summaryrefslogtreecommitdiffstats
path: root/sw/source/core
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core')
-rw-r--r--sw/source/core/SwNumberTree/SwNodeNum.cxx369
-rw-r--r--sw/source/core/SwNumberTree/SwNumberTree.cxx1181
-rw-r--r--sw/source/core/access/AccessibilityCheck.cxx969
-rw-r--r--sw/source/core/access/AccessibilityIssue.cxx89
-rw-r--r--sw/source/core/access/acccell.cxx472
-rw-r--r--sw/source/core/access/acccell.hxx133
-rw-r--r--sw/source/core/access/acccontext.cxx1519
-rw-r--r--sw/source/core/access/acccontext.hxx362
-rw-r--r--sw/source/core/access/accdoc.cxx716
-rw-r--r--sw/source/core/access/accdoc.hxx173
-rw-r--r--sw/source/core/access/accembedded.cxx121
-rw-r--r--sw/source/core/access/accembedded.hxx72
-rw-r--r--sw/source/core/access/accfootnote.cxx120
-rw-r--r--sw/source/core/access/accfootnote.hxx65
-rw-r--r--sw/source/core/access/accframe.cxx479
-rw-r--r--sw/source/core/access/accframe.hxx163
-rw-r--r--sw/source/core/access/accframebase.cxx373
-rw-r--r--sw/source/core/access/accframebase.hxx65
-rw-r--r--sw/source/core/access/accfrmobj.cxx408
-rw-r--r--sw/source/core/access/accfrmobj.hxx89
-rw-r--r--sw/source/core/access/accfrmobjmap.cxx149
-rw-r--r--sw/source/core/access/accfrmobjmap.hxx126
-rw-r--r--sw/source/core/access/accfrmobjslist.cxx163
-rw-r--r--sw/source/core/access/accfrmobjslist.hxx131
-rw-r--r--sw/source/core/access/accgraphic.cxx75
-rw-r--r--sw/source/core/access/accgraphic.hxx57
-rw-r--r--sw/source/core/access/accheaderfooter.cxx112
-rw-r--r--sw/source/core/access/accheaderfooter.hxx64
-rw-r--r--sw/source/core/access/acchyperlink.cxx236
-rw-r--r--sw/source/core/access/acchyperlink.hxx75
-rw-r--r--sw/source/core/access/acchypertextdata.cxx46
-rw-r--r--sw/source/core/access/acchypertextdata.hxx54
-rw-r--r--sw/source/core/access/accmap.cxx3440
-rw-r--r--sw/source/core/access/accnotextframe.cxx315
-rw-r--r--sw/source/core/access/accnotextframe.hxx125
-rw-r--r--sw/source/core/access/accnotexthyperlink.cxx234
-rw-r--r--sw/source/core/access/accnotexthyperlink.hxx65
-rw-r--r--sw/source/core/access/accpage.cxx160
-rw-r--r--sw/source/core/access/accpage.hxx77
-rw-r--r--sw/source/core/access/accpara.cxx3546
-rw-r--r--sw/source/core/access/accpara.hxx399
-rw-r--r--sw/source/core/access/accportions.cxx752
-rw-r--r--sw/source/core/access/accportions.hxx171
-rw-r--r--sw/source/core/access/accpreview.cxx76
-rw-r--r--sw/source/core/access/accpreview.hxx68
-rw-r--r--sw/source/core/access/accselectionhelper.cxx341
-rw-r--r--sw/source/core/access/accselectionhelper.hxx73
-rw-r--r--sw/source/core/access/acctable.cxx1739
-rw-r--r--sw/source/core/access/acctable.hxx277
-rw-r--r--sw/source/core/access/acctextframe.cxx319
-rw-r--r--sw/source/core/access/acctextframe.hxx116
-rw-r--r--sw/source/core/access/parachangetrackinginfo.cxx205
-rw-r--r--sw/source/core/access/parachangetrackinginfo.hxx51
-rw-r--r--sw/source/core/access/textmarkuphelper.cxx221
-rw-r--r--sw/source/core/access/textmarkuphelper.hxx70
-rw-r--r--sw/source/core/attr/BorderCacheOwner.cxx45
-rw-r--r--sw/source/core/attr/calbck.cxx338
-rw-r--r--sw/source/core/attr/cellatr.cxx217
-rw-r--r--sw/source/core/attr/fmtfollowtextflow.cxx27
-rw-r--r--sw/source/core/attr/fmtwrapinfluenceonobjpos.cxx174
-rw-r--r--sw/source/core/attr/format.cxx782
-rw-r--r--sw/source/core/attr/hints.cxx268
-rw-r--r--sw/source/core/attr/swatrset.cxx461
-rw-r--r--sw/source/core/bastyp/SwSmartTagMgr.cxx68
-rw-r--r--sw/source/core/bastyp/bparr.cxx505
-rw-r--r--sw/source/core/bastyp/breakit.cxx183
-rw-r--r--sw/source/core/bastyp/calc.cxx1541
-rw-r--r--sw/source/core/bastyp/checkit.cxx33
-rw-r--r--sw/source/core/bastyp/index.cxx396
-rw-r--r--sw/source/core/bastyp/init.cxx792
-rw-r--r--sw/source/core/bastyp/proofreadingiterator.cxx67
-rw-r--r--sw/source/core/bastyp/swcache.cxx509
-rw-r--r--sw/source/core/bastyp/swrect.cxx202
-rw-r--r--sw/source/core/bastyp/swregion.cxx229
-rw-r--r--sw/source/core/bastyp/swtypes.cxx66
-rw-r--r--sw/source/core/bastyp/tabcol.cxx85
-rw-r--r--sw/source/core/crsr/BlockCursor.cxx27
-rw-r--r--sw/source/core/crsr/BlockCursor.hxx91
-rw-r--r--sw/source/core/crsr/DateFormFieldButton.cxx64
-rw-r--r--sw/source/core/crsr/DropDownFormFieldButton.cxx117
-rw-r--r--sw/source/core/crsr/FormFieldButton.cxx160
-rw-r--r--sw/source/core/crsr/annotationmark.cxx95
-rw-r--r--sw/source/core/crsr/bookmark.cxx1037
-rw-r--r--sw/source/core/crsr/callnk.cxx266
-rw-r--r--sw/source/core/crsr/callnk.hxx49
-rw-r--r--sw/source/core/crsr/contentcontrolbutton.cxx160
-rw-r--r--sw/source/core/crsr/crbm.cxx321
-rw-r--r--sw/source/core/crsr/crossrefbookmark.cxx94
-rw-r--r--sw/source/core/crsr/crsrsh.cxx3886
-rw-r--r--sw/source/core/crsr/crstrvl.cxx2743
-rw-r--r--sw/source/core/crsr/crstrvl1.cxx126
-rw-r--r--sw/source/core/crsr/datecontentcontrolbutton.cxx71
-rw-r--r--sw/source/core/crsr/dropdowncontentcontrolbutton.cxx96
-rw-r--r--sw/source/core/crsr/findattr.cxx1445
-rw-r--r--sw/source/core/crsr/findcoll.cxx113
-rw-r--r--sw/source/core/crsr/findfmt.cxx97
-rw-r--r--sw/source/core/crsr/findtxt.cxx1177
-rw-r--r--sw/source/core/crsr/overlayrangesoutline.cxx104
-rw-r--r--sw/source/core/crsr/overlayrangesoutline.hxx58
-rw-r--r--sw/source/core/crsr/pam.cxx1210
-rw-r--r--sw/source/core/crsr/paminit.cxx61
-rw-r--r--sw/source/core/crsr/swcrsr.cxx2637
-rw-r--r--sw/source/core/crsr/trvlcol.cxx103
-rw-r--r--sw/source/core/crsr/trvlfnfl.cxx388
-rw-r--r--sw/source/core/crsr/trvlreg.cxx280
-rw-r--r--sw/source/core/crsr/trvltbl.cxx929
-rw-r--r--sw/source/core/crsr/viscrs.cxx1216
-rw-r--r--sw/source/core/doc/CntntIdxStore.cxx479
-rw-r--r--sw/source/core/doc/DocumentChartDataProviderManager.cxx105
-rw-r--r--sw/source/core/doc/DocumentContentOperationsManager.cxx5400
-rw-r--r--sw/source/core/doc/DocumentDeviceManager.cxx370
-rw-r--r--sw/source/core/doc/DocumentDrawModelManager.cxx357
-rw-r--r--sw/source/core/doc/DocumentExternalDataManager.cxx34
-rw-r--r--sw/source/core/doc/DocumentFieldsManager.cxx1856
-rw-r--r--sw/source/core/doc/DocumentLayoutManager.cxx500
-rw-r--r--sw/source/core/doc/DocumentLinksAdministrationManager.cxx581
-rw-r--r--sw/source/core/doc/DocumentListItemsManager.cxx105
-rw-r--r--sw/source/core/doc/DocumentListsManager.cxx214
-rw-r--r--sw/source/core/doc/DocumentOutlineNodesManager.cxx131
-rw-r--r--sw/source/core/doc/DocumentRedlineManager.cxx3518
-rw-r--r--sw/source/core/doc/DocumentSettingManager.cxx1039
-rw-r--r--sw/source/core/doc/DocumentStateManager.cxx118
-rw-r--r--sw/source/core/doc/DocumentStatisticsManager.cxx217
-rw-r--r--sw/source/core/doc/DocumentStylePoolManager.cxx2748
-rw-r--r--sw/source/core/doc/DocumentTimerManager.cxx235
-rw-r--r--sw/source/core/doc/SwDocIdle.cxx62
-rw-r--r--sw/source/core/doc/SwStyleNameMapper.cxx784
-rw-r--r--sw/source/core/doc/acmplwrd.cxx401
-rw-r--r--sw/source/core/doc/dbgoutsw.cxx837
-rw-r--r--sw/source/core/doc/doc.cxx1830
-rw-r--r--sw/source/core/doc/docbasic.cxx233
-rw-r--r--sw/source/core/doc/docbm.cxx2023
-rw-r--r--sw/source/core/doc/docchart.cxx200
-rw-r--r--sw/source/core/doc/doccomp.cxx2695
-rw-r--r--sw/source/core/doc/doccorr.cxx366
-rw-r--r--sw/source/core/doc/docdesc.cxx1050
-rw-r--r--sw/source/core/doc/docdraw.cxx699
-rw-r--r--sw/source/core/doc/docedt.cxx882
-rw-r--r--sw/source/core/doc/docfld.cxx1259
-rw-r--r--sw/source/core/doc/docfly.cxx1161
-rw-r--r--sw/source/core/doc/docfmt.cxx2176
-rw-r--r--sw/source/core/doc/docftn.cxx547
-rw-r--r--sw/source/core/doc/docglbl.cxx518
-rw-r--r--sw/source/core/doc/docglos.cxx212
-rw-r--r--sw/source/core/doc/doclay.cxx1706
-rw-r--r--sw/source/core/doc/docnew.cxx1317
-rw-r--r--sw/source/core/doc/docnum.cxx2682
-rw-r--r--sw/source/core/doc/docredln.cxx2136
-rw-r--r--sw/source/core/doc/docruby.cxx322
-rw-r--r--sw/source/core/doc/docsort.cxx941
-rw-r--r--sw/source/core/doc/docstat.cxx51
-rw-r--r--sw/source/core/doc/doctxm.cxx2133
-rw-r--r--sw/source/core/doc/docxforms.cxx129
-rw-r--r--sw/source/core/doc/extinput.cxx309
-rw-r--r--sw/source/core/doc/fmtcol.cxx657
-rw-r--r--sw/source/core/doc/ftnidx.cxx520
-rw-r--r--sw/source/core/doc/gctable.cxx463
-rw-r--r--sw/source/core/doc/htmltbl.cxx1772
-rw-r--r--sw/source/core/doc/lineinfo.cxx132
-rw-r--r--sw/source/core/doc/list.cxx180
-rw-r--r--sw/source/core/doc/notxtfrm.cxx1477
-rw-r--r--sw/source/core/doc/number.cxx1589
-rw-r--r--sw/source/core/doc/poolfmt.cxx308
-rw-r--r--sw/source/core/doc/rdfhelper.cxx264
-rw-r--r--sw/source/core/doc/sortopt.cxx61
-rw-r--r--sw/source/core/doc/swserv.cxx320
-rw-r--r--sw/source/core/doc/swstylemanager.cxx161
-rw-r--r--sw/source/core/doc/swstylemanager.hxx32
-rw-r--r--sw/source/core/doc/tblafmt.cxx1237
-rw-r--r--sw/source/core/doc/tblcpy.cxx1042
-rw-r--r--sw/source/core/doc/tblrwcl.cxx3387
-rw-r--r--sw/source/core/doc/textboxhelper.cxx1970
-rw-r--r--sw/source/core/doc/visiturl.cxx125
-rw-r--r--sw/source/core/docnode/cancellablejob.cxx33
-rw-r--r--sw/source/core/docnode/cancellablejob.hxx47
-rw-r--r--sw/source/core/docnode/finalthreadmanager.cxx424
-rw-r--r--sw/source/core/docnode/ndcopy.cxx378
-rw-r--r--sw/source/core/docnode/ndnotxt.cxx294
-rw-r--r--sw/source/core/docnode/ndnum.cxx98
-rw-r--r--sw/source/core/docnode/ndsect.cxx1475
-rw-r--r--sw/source/core/docnode/ndsect.hxx31
-rw-r--r--sw/source/core/docnode/ndtbl.cxx4684
-rw-r--r--sw/source/core/docnode/ndtbl1.cxx1774
-rw-r--r--sw/source/core/docnode/node.cxx2138
-rw-r--r--sw/source/core/docnode/node2lay.cxx517
-rw-r--r--sw/source/core/docnode/nodes.cxx2392
-rw-r--r--sw/source/core/docnode/observablethread.cxx63
-rw-r--r--sw/source/core/docnode/pausethreadstarting.cxx48
-rw-r--r--sw/source/core/docnode/retrievedinputstreamdata.cxx143
-rw-r--r--sw/source/core/docnode/retrieveinputstream.cxx83
-rw-r--r--sw/source/core/docnode/retrieveinputstreamconsumer.cxx63
-rw-r--r--sw/source/core/docnode/section.cxx1512
-rw-r--r--sw/source/core/docnode/swbaslnk.cxx326
-rw-r--r--sw/source/core/docnode/swthreadjoiner.cxx49
-rw-r--r--sw/source/core/docnode/swthreadmanager.cxx78
-rw-r--r--sw/source/core/docnode/threadlistener.cxx47
-rw-r--r--sw/source/core/docnode/threadmanager.cxx250
-rw-r--r--sw/source/core/docnode/threadmanager.hxx147
-rw-r--r--sw/source/core/draw/dcontact.cxx2632
-rw-r--r--sw/source/core/draw/dflyobj.cxx1295
-rw-r--r--sw/source/core/draw/dobjfac.cxx39
-rw-r--r--sw/source/core/draw/dpage.cxx262
-rw-r--r--sw/source/core/draw/drawdoc.cxx137
-rw-r--r--sw/source/core/draw/dview.cxx1019
-rw-r--r--sw/source/core/edit/acorrect.cxx716
-rw-r--r--sw/source/core/edit/autofmt.cxx2831
-rw-r--r--sw/source/core/edit/edatmisc.cxx160
-rw-r--r--sw/source/core/edit/edattr.cxx847
-rw-r--r--sw/source/core/edit/eddel.cxx361
-rw-r--r--sw/source/core/edit/edfcol.cxx2329
-rw-r--r--sw/source/core/edit/edfld.cxx410
-rw-r--r--sw/source/core/edit/edfldexp.cxx58
-rw-r--r--sw/source/core/edit/edfmt.cxx157
-rw-r--r--sw/source/core/edit/edglbldc.cxx383
-rw-r--r--sw/source/core/edit/edglss.cxx340
-rw-r--r--sw/source/core/edit/editsh.cxx1100
-rw-r--r--sw/source/core/edit/edlingu.cxx1729
-rw-r--r--sw/source/core/edit/ednumber.cxx905
-rw-r--r--sw/source/core/edit/edredln.cxx196
-rw-r--r--sw/source/core/edit/edsect.cxx422
-rw-r--r--sw/source/core/edit/edtab.cxx532
-rw-r--r--sw/source/core/edit/edtox.cxx396
-rw-r--r--sw/source/core/edit/edundo.cxx257
-rw-r--r--sw/source/core/edit/edws.cxx327
-rw-r--r--sw/source/core/fields/authfld.cxx782
-rw-r--r--sw/source/core/fields/cellfml.cxx1253
-rw-r--r--sw/source/core/fields/chpfld.cxx309
-rw-r--r--sw/source/core/fields/dbfld.cxx869
-rw-r--r--sw/source/core/fields/ddefld.cxx389
-rw-r--r--sw/source/core/fields/ddetbl.cxx216
-rw-r--r--sw/source/core/fields/docufld.cxx2660
-rw-r--r--sw/source/core/fields/expfld.cxx1445
-rw-r--r--sw/source/core/fields/fldbas.cxx865
-rw-r--r--sw/source/core/fields/flddat.cxx226
-rw-r--r--sw/source/core/fields/flddropdown.cxx217
-rw-r--r--sw/source/core/fields/fldlst.cxx151
-rw-r--r--sw/source/core/fields/macrofld.cxx222
-rw-r--r--sw/source/core/fields/postithelper.cxx279
-rw-r--r--sw/source/core/fields/reffld.cxx1500
-rw-r--r--sw/source/core/fields/scrptfld.cxx119
-rw-r--r--sw/source/core/fields/tblcalc.cxx211
-rw-r--r--sw/source/core/fields/textapi.cxx193
-rw-r--r--sw/source/core/fields/usrfld.cxx384
-rw-r--r--sw/source/core/frmedt/fecopy.cxx1629
-rw-r--r--sw/source/core/frmedt/fedesc.cxx245
-rw-r--r--sw/source/core/frmedt/fefly1.cxx2134
-rw-r--r--sw/source/core/frmedt/feflyole.cxx123
-rw-r--r--sw/source/core/frmedt/feshview.cxx3360
-rw-r--r--sw/source/core/frmedt/fetab.cxx2438
-rw-r--r--sw/source/core/frmedt/fews.cxx1332
-rw-r--r--sw/source/core/frmedt/tblsel.cxx2614
-rw-r--r--sw/source/core/graphic/GraphicSizeCheck.cxx163
-rw-r--r--sw/source/core/graphic/grfatr.cxx341
-rw-r--r--sw/source/core/graphic/ndgrf.cxx905
-rw-r--r--sw/source/core/inc/AccessibilityCheck.hxx38
-rw-r--r--sw/source/core/inc/AccessibilityIssue.hxx67
-rw-r--r--sw/source/core/inc/DateFormFieldButton.hxx47
-rw-r--r--sw/source/core/inc/DocumentChartDataProviderManager.hxx65
-rw-r--r--sw/source/core/inc/DocumentContentOperationsManager.hxx191
-rw-r--r--sw/source/core/inc/DocumentDeviceManager.hxx81
-rw-r--r--sw/source/core/inc/DocumentDrawModelManager.hxx86
-rw-r--r--sw/source/core/inc/DocumentExternalDataManager.hxx44
-rw-r--r--sw/source/core/inc/DocumentFieldsManager.hxx111
-rw-r--r--sw/source/core/inc/DocumentLayoutManager.hxx76
-rw-r--r--sw/source/core/inc/DocumentLinksAdministrationManager.hxx85
-rw-r--r--sw/source/core/inc/DocumentListItemsManager.hxx71
-rw-r--r--sw/source/core/inc/DocumentListsManager.hxx74
-rw-r--r--sw/source/core/inc/DocumentOutlineNodesManager.hxx65
-rw-r--r--sw/source/core/inc/DocumentRedlineManager.hxx163
-rw-r--r--sw/source/core/inc/DocumentSettingManager.hxx223
-rw-r--r--sw/source/core/inc/DocumentStateManager.hxx62
-rw-r--r--sw/source/core/inc/DocumentStatisticsManager.hxx69
-rw-r--r--sw/source/core/inc/DocumentStylePoolManager.hxx61
-rw-r--r--sw/source/core/inc/DocumentTimerManager.hxx87
-rw-r--r--sw/source/core/inc/DropDownFormFieldButton.hxx41
-rw-r--r--sw/source/core/inc/FormFieldButton.hxx52
-rw-r--r--sw/source/core/inc/GraphicSizeCheck.hxx111
-rw-r--r--sw/source/core/inc/MarkManager.hxx161
-rw-r--r--sw/source/core/inc/ModelTraverser.hxx51
-rw-r--r--sw/source/core/inc/SearchResultLocator.hxx71
-rw-r--r--sw/source/core/inc/SwGrammarMarkUp.hxx71
-rw-r--r--sw/source/core/inc/SwPortionHandler.hxx103
-rw-r--r--sw/source/core/inc/SwUndoFmt.hxx257
-rw-r--r--sw/source/core/inc/SwUndoPageDesc.hxx88
-rw-r--r--sw/source/core/inc/SwUndoTOXChange.hxx45
-rw-r--r--sw/source/core/inc/SwXMLBlockExport.hxx63
-rw-r--r--sw/source/core/inc/SwXMLBlockImport.hxx126
-rw-r--r--sw/source/core/inc/SwXMLTextBlocks.hxx98
-rw-r--r--sw/source/core/inc/SwXTextDefaults.hxx65
-rw-r--r--sw/source/core/inc/TextFrameIndex.hxx26
-rw-r--r--sw/source/core/inc/UndoAttribute.hxx258
-rw-r--r--sw/source/core/inc/UndoBookmark.hxx157
-rw-r--r--sw/source/core/inc/UndoCore.hxx293
-rw-r--r--sw/source/core/inc/UndoDelete.hxx111
-rw-r--r--sw/source/core/inc/UndoDraw.hxx134
-rw-r--r--sw/source/core/inc/UndoInsert.hxx226
-rw-r--r--sw/source/core/inc/UndoManager.hxx138
-rw-r--r--sw/source/core/inc/UndoNumbering.hxx142
-rw-r--r--sw/source/core/inc/UndoOverwrite.hxx93
-rw-r--r--sw/source/core/inc/UndoRedline.hxx143
-rw-r--r--sw/source/core/inc/UndoSection.hxx100
-rw-r--r--sw/source/core/inc/UndoSort.hxx87
-rw-r--r--sw/source/core/inc/UndoSplitMove.hxx85
-rw-r--r--sw/source/core/inc/UndoTable.hxx429
-rw-r--r--sw/source/core/inc/acorrect.hxx120
-rw-r--r--sw/source/core/inc/anchoredobjectposition.hxx447
-rw-r--r--sw/source/core/inc/annotationmark.hxx48
-rw-r--r--sw/source/core/inc/ascharanchoredobjectposition.hxx160
-rw-r--r--sw/source/core/inc/attrhint.hxx31
-rw-r--r--sw/source/core/inc/bodyfrm.hxx40
-rw-r--r--sw/source/core/inc/bookmark.hxx347
-rw-r--r--sw/source/core/inc/cellfrm.hxx76
-rw-r--r--sw/source/core/inc/cntfrm.hxx146
-rw-r--r--sw/source/core/inc/colfrm.hxx40
-rw-r--r--sw/source/core/inc/contentcontrolbutton.hxx52
-rw-r--r--sw/source/core/inc/crossrefbookmark.hxx88
-rw-r--r--sw/source/core/inc/datecontentcontrolbutton.hxx41
-rw-r--r--sw/source/core/inc/dbg_lay.hxx105
-rw-r--r--sw/source/core/inc/dflyobj.hxx146
-rw-r--r--sw/source/core/inc/dialoghelp.hxx30
-rw-r--r--sw/source/core/inc/docedt.hxx31
-rw-r--r--sw/source/core/inc/docfld.hxx192
-rw-r--r--sw/source/core/inc/docredln.hxx35
-rw-r--r--sw/source/core/inc/docsort.hxx152
-rw-r--r--sw/source/core/inc/doctxm.hxx115
-rw-r--r--sw/source/core/inc/drawfont.hxx622
-rw-r--r--sw/source/core/inc/dropdowncontentcontrolbutton.hxx39
-rw-r--r--sw/source/core/inc/dumpfilter.hxx59
-rw-r--r--sw/source/core/inc/dview.hxx125
-rw-r--r--sw/source/core/inc/environmentofanchoredobject.hxx94
-rw-r--r--sw/source/core/inc/fefly.hxx30
-rw-r--r--sw/source/core/inc/fieldhint.hxx43
-rw-r--r--sw/source/core/inc/flowfrm.hxx279
-rw-r--r--sw/source/core/inc/flyfrm.hxx313
-rw-r--r--sw/source/core/inc/flyfrms.hxx240
-rw-r--r--sw/source/core/inc/fntcache.hxx146
-rw-r--r--sw/source/core/inc/fntcap.hxx37
-rw-r--r--sw/source/core/inc/frame.hxx1438
-rw-r--r--sw/source/core/inc/frminf.hxx75
-rw-r--r--sw/source/core/inc/frmtool.hxx624
-rw-r--r--sw/source/core/inc/ftnboss.hxx131
-rw-r--r--sw/source/core/inc/ftnfrm.hxx165
-rw-r--r--sw/source/core/inc/hffrm.hxx58
-rw-r--r--sw/source/core/inc/ifinishedthreadlistener.hxx46
-rw-r--r--sw/source/core/inc/layact.hxx221
-rw-r--r--sw/source/core/inc/laycache.hxx69
-rw-r--r--sw/source/core/inc/layfrm.hxx227
-rw-r--r--sw/source/core/inc/layouter.hxx146
-rw-r--r--sw/source/core/inc/movedfwdfrmsbyobjpos.hxx58
-rw-r--r--sw/source/core/inc/mvsave.hxx201
-rw-r--r--sw/source/core/inc/node2lay.hxx90
-rw-r--r--sw/source/core/inc/noteurl.hxx32
-rw-r--r--sw/source/core/inc/notxtfrm.hxx103
-rw-r--r--sw/source/core/inc/objectformatter.hxx174
-rw-r--r--sw/source/core/inc/observablethread.hxx87
-rw-r--r--sw/source/core/inc/pagedeschint.hxx40
-rw-r--r--sw/source/core/inc/pagefrm.hxx460
-rw-r--r--sw/source/core/inc/paintfrm.hxx37
-rw-r--r--sw/source/core/inc/pamtyp.hxx114
-rw-r--r--sw/source/core/inc/prevwpage.hxx53
-rw-r--r--sw/source/core/inc/ptqueue.hxx57
-rw-r--r--sw/source/core/inc/retrievedinputstreamdata.hxx89
-rw-r--r--sw/source/core/inc/retrieveinputstream.hxx55
-rw-r--r--sw/source/core/inc/retrieveinputstreamconsumer.hxx59
-rw-r--r--sw/source/core/inc/rolbck.hxx440
-rw-r--r--sw/source/core/inc/rootfrm.hxx472
-rw-r--r--sw/source/core/inc/rowfrm.hxx129
-rw-r--r--sw/source/core/inc/scriptinfo.hxx396
-rw-r--r--sw/source/core/inc/sectfrm.hxx188
-rw-r--r--sw/source/core/inc/sortedobjs.hxx100
-rw-r--r--sw/source/core/inc/swblocks.hxx129
-rw-r--r--sw/source/core/inc/swcache.hxx266
-rw-r--r--sw/source/core/inc/swfntcch.hxx76
-rw-r--r--sw/source/core/inc/swfont.hxx1007
-rw-r--r--sw/source/core/inc/swselectionlist.hxx91
-rw-r--r--sw/source/core/inc/swthreadjoiner.hxx42
-rw-r--r--sw/source/core/inc/swthreadmanager.hxx78
-rw-r--r--sw/source/core/inc/tabfrm.hxx261
-rw-r--r--sw/source/core/inc/tblrwcl.hxx197
-rw-r--r--sw/source/core/inc/textapi.hxx65
-rw-r--r--sw/source/core/inc/threadlistener.hxx55
-rw-r--r--sw/source/core/inc/tocntntanchoredobjectposition.hxx93
-rw-r--r--sw/source/core/inc/tolayoutanchoredobjectposition.hxx52
-rw-r--r--sw/source/core/inc/txmsrt.hxx308
-rw-r--r--sw/source/core/inc/txtfly.hxx382
-rw-r--r--sw/source/core/inc/txtfrm.hxx1032
-rw-r--r--sw/source/core/inc/txttypes.hxx98
-rw-r--r--sw/source/core/inc/undoflystrattr.hxx49
-rw-r--r--sw/source/core/inc/unobookmark.hxx247
-rw-r--r--sw/source/core/inc/unocontentcontrol.hxx156
-rw-r--r--sw/source/core/inc/unoevent.hxx95
-rw-r--r--sw/source/core/inc/unofield.hxx244
-rw-r--r--sw/source/core/inc/unoflatpara.hxx147
-rw-r--r--sw/source/core/inc/unofldmid.h54
-rw-r--r--sw/source/core/inc/unofootnote.hxx149
-rw-r--r--sw/source/core/inc/unoidx.hxx229
-rw-r--r--sw/source/core/inc/unolinebreak.hxx93
-rw-r--r--sw/source/core/inc/unometa.hxx270
-rw-r--r--sw/source/core/inc/unoparaframeenum.hxx77
-rw-r--r--sw/source/core/inc/unoport.hxx324
-rw-r--r--sw/source/core/inc/unorefmark.hxx116
-rw-r--r--sw/source/core/inc/unosection.hxx163
-rw-r--r--sw/source/core/inc/unotextmarkup.hxx103
-rw-r--r--sw/source/core/inc/viewimp.hxx314
-rw-r--r--sw/source/core/inc/visiturl.hxx40
-rw-r--r--sw/source/core/inc/wrong.hxx413
-rw-r--r--sw/source/core/layout/anchoreddrawobject.cxx939
-rw-r--r--sw/source/core/layout/anchoredobject.cxx916
-rw-r--r--sw/source/core/layout/atrfrm.cxx3711
-rw-r--r--sw/source/core/layout/calcmove.cxx2215
-rw-r--r--sw/source/core/layout/colfrm.cxx446
-rw-r--r--sw/source/core/layout/dbg_lay.cxx953
-rw-r--r--sw/source/core/layout/dumpfilter.cxx164
-rw-r--r--sw/source/core/layout/findfrm.cxx1905
-rw-r--r--sw/source/core/layout/flowfrm.cxx2736
-rw-r--r--sw/source/core/layout/fly.cxx2997
-rw-r--r--sw/source/core/layout/flycnt.cxx1522
-rw-r--r--sw/source/core/layout/flyincnt.cxx285
-rw-r--r--sw/source/core/layout/flylay.cxx1505
-rw-r--r--sw/source/core/layout/flypos.cxx65
-rw-r--r--sw/source/core/layout/frmtool.cxx4040
-rw-r--r--sw/source/core/layout/ftnfrm.cxx2987
-rw-r--r--sw/source/core/layout/hffrm.cxx768
-rw-r--r--sw/source/core/layout/layact.cxx2419
-rw-r--r--sw/source/core/layout/laycache.cxx1200
-rw-r--r--sw/source/core/layout/layhelp.hxx224
-rw-r--r--sw/source/core/layout/layouter.cxx483
-rw-r--r--sw/source/core/layout/legacyitem.cxx78
-rw-r--r--sw/source/core/layout/movedfwdfrmsbyobjpos.cxx90
-rw-r--r--sw/source/core/layout/newfrm.cxx618
-rw-r--r--sw/source/core/layout/objectformatter.cxx478
-rw-r--r--sw/source/core/layout/objectformatterlayfrm.cxx188
-rw-r--r--sw/source/core/layout/objectformatterlayfrm.hxx70
-rw-r--r--sw/source/core/layout/objectformattertxtfrm.cxx971
-rw-r--r--sw/source/core/layout/objectformattertxtfrm.hxx190
-rw-r--r--sw/source/core/layout/objstmpconsiderwrapinfl.cxx60
-rw-r--r--sw/source/core/layout/objstmpconsiderwrapinfl.hxx42
-rw-r--r--sw/source/core/layout/pagechg.cxx2614
-rw-r--r--sw/source/core/layout/pagedesc.cxx803
-rw-r--r--sw/source/core/layout/paintfrm.cxx7721
-rw-r--r--sw/source/core/layout/sectfrm.cxx2941
-rw-r--r--sw/source/core/layout/softpagebreak.cxx154
-rw-r--r--sw/source/core/layout/sortedobjs.cxx293
-rw-r--r--sw/source/core/layout/ssfrm.cxx752
-rw-r--r--sw/source/core/layout/swselectionlist.cxx83
-rw-r--r--sw/source/core/layout/tabfrm.cxx6170
-rw-r--r--sw/source/core/layout/trvlfrm.cxx2654
-rw-r--r--sw/source/core/layout/unusedf.cxx79
-rw-r--r--sw/source/core/layout/virtoutp.cxx189
-rw-r--r--sw/source/core/layout/virtoutp.hxx61
-rw-r--r--sw/source/core/layout/wsfrm.cxx4743
-rw-r--r--sw/source/core/model/ModelTraverser.cxx61
-rw-r--r--sw/source/core/model/SearchResultLocator.cxx204
-rw-r--r--sw/source/core/objectpositioning/anchoredobjectposition.cxx1146
-rw-r--r--sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx398
-rw-r--r--sw/source/core/objectpositioning/environmentofanchoredobject.cxx99
-rw-r--r--sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx1217
-rw-r--r--sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx226
-rw-r--r--sw/source/core/ole/ndole.cxx1337
-rw-r--r--sw/source/core/para/paratr.cxx221
-rw-r--r--sw/source/core/sw3io/swacorr.cxx103
-rw-r--r--sw/source/core/swg/BlockListTokens.txt7
-rw-r--r--sw/source/core/swg/SwXMLBlockExport.cxx138
-rw-r--r--sw/source/core/swg/SwXMLBlockImport.cxx337
-rw-r--r--sw/source/core/swg/SwXMLSectionList.cxx131
-rw-r--r--sw/source/core/swg/SwXMLTextBlocks.cxx572
-rw-r--r--sw/source/core/swg/SwXMLTextBlocks1.cxx570
-rw-r--r--sw/source/core/swg/TextBlockTokens.txt5
-rw-r--r--sw/source/core/swg/swblocks.cxx581
-rw-r--r--sw/source/core/table/swnewtable.cxx2512
-rw-r--r--sw/source/core/table/swtable.cxx2960
-rw-r--r--sw/source/core/text/EnhancedPDFExportHelper.cxx2417
-rw-r--r--sw/source/core/text/SwGrammarMarkUp.cxx145
-rw-r--r--sw/source/core/text/atrhndl.hxx117
-rw-r--r--sw/source/core/text/atrstck.cxx851
-rw-r--r--sw/source/core/text/frmcrsr.cxx1689
-rw-r--r--sw/source/core/text/frmform.cxx2104
-rw-r--r--sw/source/core/text/frminf.cxx291
-rw-r--r--sw/source/core/text/frmpaint.cxx782
-rw-r--r--sw/source/core/text/guess.cxx659
-rw-r--r--sw/source/core/text/guess.hxx63
-rw-r--r--sw/source/core/text/inftxt.cxx2013
-rw-r--r--sw/source/core/text/inftxt.hxx786
-rw-r--r--sw/source/core/text/itradj.cxx841
-rw-r--r--sw/source/core/text/itratr.cxx1494
-rw-r--r--sw/source/core/text/itratr.hxx112
-rw-r--r--sw/source/core/text/itrcrsr.cxx1950
-rw-r--r--sw/source/core/text/itrform2.cxx3060
-rw-r--r--sw/source/core/text/itrform2.hxx245
-rw-r--r--sw/source/core/text/itrpaint.cxx713
-rw-r--r--sw/source/core/text/itrpaint.hxx65
-rw-r--r--sw/source/core/text/itrtxt.cxx465
-rw-r--r--sw/source/core/text/itrtxt.hxx340
-rw-r--r--sw/source/core/text/noteurl.cxx25
-rw-r--r--sw/source/core/text/pordrop.hxx107
-rw-r--r--sw/source/core/text/porexp.cxx275
-rw-r--r--sw/source/core/text/porexp.hxx78
-rw-r--r--sw/source/core/text/porfld.cxx1380
-rw-r--r--sw/source/core/text/porfld.hxx266
-rw-r--r--sw/source/core/text/porfly.cxx413
-rw-r--r--sw/source/core/text/porfly.hxx95
-rw-r--r--sw/source/core/text/porftn.hxx104
-rw-r--r--sw/source/core/text/porglue.cxx285
-rw-r--r--sw/source/core/text/porglue.hxx90
-rw-r--r--sw/source/core/text/porhyph.hxx88
-rw-r--r--sw/source/core/text/porlay.cxx2842
-rw-r--r--sw/source/core/text/porlay.hxx349
-rw-r--r--sw/source/core/text/porlin.cxx353
-rw-r--r--sw/source/core/text/porlin.hxx215
-rw-r--r--sw/source/core/text/pormulti.cxx2593
-rw-r--r--sw/source/core/text/pormulti.hxx256
-rw-r--r--sw/source/core/text/porref.cxx75
-rw-r--r--sw/source/core/text/porref.hxx45
-rw-r--r--sw/source/core/text/porrst.cxx760
-rw-r--r--sw/source/core/text/porrst.hxx199
-rw-r--r--sw/source/core/text/portab.hxx111
-rw-r--r--sw/source/core/text/portox.cxx77
-rw-r--r--sw/source/core/text/portox.hxx46
-rw-r--r--sw/source/core/text/portxt.cxx868
-rw-r--r--sw/source/core/text/portxt.hxx103
-rw-r--r--sw/source/core/text/possiz.hxx72
-rw-r--r--sw/source/core/text/redlnitr.cxx1137
-rw-r--r--sw/source/core/text/redlnitr.hxx135
-rw-r--r--sw/source/core/text/txtcache.cxx193
-rw-r--r--sw/source/core/text/txtcache.hxx62
-rw-r--r--sw/source/core/text/txtdrop.cxx1079
-rw-r--r--sw/source/core/text/txtfld.cxx836
-rw-r--r--sw/source/core/text/txtfly.cxx1473
-rw-r--r--sw/source/core/text/txtfrm.cxx4110
-rw-r--r--sw/source/core/text/txtftn.cxx1590
-rw-r--r--sw/source/core/text/txthyph.cxx582
-rw-r--r--sw/source/core/text/txtinit.cxx60
-rw-r--r--sw/source/core/text/txtpaint.cxx113
-rw-r--r--sw/source/core/text/txtpaint.hxx126
-rw-r--r--sw/source/core/text/txttab.cxx661
-rw-r--r--sw/source/core/text/widorp.cxx609
-rw-r--r--sw/source/core/text/widorp.hxx82
-rw-r--r--sw/source/core/text/wrong.cxx937
-rw-r--r--sw/source/core/text/xmldump.cxx678
-rw-r--r--sw/source/core/tox/ToxLinkProcessor.cxx90
-rw-r--r--sw/source/core/tox/ToxTabStopTokenHandler.cxx128
-rw-r--r--sw/source/core/tox/ToxTextGenerator.cxx468
-rw-r--r--sw/source/core/tox/ToxWhitespaceStripper.cxx63
-rw-r--r--sw/source/core/tox/tox.cxx949
-rw-r--r--sw/source/core/tox/toxhlp.cxx119
-rw-r--r--sw/source/core/tox/txmsrt.cxx989
-rw-r--r--sw/source/core/txtnode/SwGrammarContact.cxx190
-rw-r--r--sw/source/core/txtnode/atrfld.cxx756
-rw-r--r--sw/source/core/txtnode/atrflyin.cxx307
-rw-r--r--sw/source/core/txtnode/atrftn.cxx608
-rw-r--r--sw/source/core/txtnode/atrref.cxx109
-rw-r--r--sw/source/core/txtnode/atrtox.cxx90
-rw-r--r--sw/source/core/txtnode/attrcontentcontrol.cxx506
-rw-r--r--sw/source/core/txtnode/attrlinebreak.cxx136
-rw-r--r--sw/source/core/txtnode/chrfmt.cxx143
-rw-r--r--sw/source/core/txtnode/fmtatr2.cxx849
-rw-r--r--sw/source/core/txtnode/fntcache.cxx2226
-rw-r--r--sw/source/core/txtnode/fntcap.cxx782
-rw-r--r--sw/source/core/txtnode/justify.cxx234
-rw-r--r--sw/source/core/txtnode/justify.hxx65
-rw-r--r--sw/source/core/txtnode/modeltoviewhelper.cxx468
-rw-r--r--sw/source/core/txtnode/ndhints.cxx492
-rw-r--r--sw/source/core/txtnode/ndtxt.cxx5436
-rw-r--r--sw/source/core/txtnode/swfntcch.cxx75
-rw-r--r--sw/source/core/txtnode/swfont.cxx1469
-rw-r--r--sw/source/core/txtnode/thints.cxx3538
-rw-r--r--sw/source/core/txtnode/txatbase.cxx181
-rw-r--r--sw/source/core/txtnode/txatritr.cxx218
-rw-r--r--sw/source/core/txtnode/txtatr2.cxx302
-rw-r--r--sw/source/core/txtnode/txtedt.cxx2373
-rw-r--r--sw/source/core/undo/SwRewriter.cxx67
-rw-r--r--sw/source/core/undo/SwUndoField.cxx150
-rw-r--r--sw/source/core/undo/SwUndoFmt.cxx466
-rw-r--r--sw/source/core/undo/SwUndoPageDesc.cxx352
-rw-r--r--sw/source/core/undo/SwUndoTOXChange.cxx85
-rw-r--r--sw/source/core/undo/docundo.cxx839
-rw-r--r--sw/source/core/undo/rolbck.cxx1537
-rw-r--r--sw/source/core/undo/unattr.cxx1067
-rw-r--r--sw/source/core/undo/unbkmk.cxx222
-rw-r--r--sw/source/core/undo/undel.cxx1348
-rw-r--r--sw/source/core/undo/undobj.cxx1718
-rw-r--r--sw/source/core/undo/undobj1.cxx728
-rw-r--r--sw/source/core/undo/undoflystrattr.cxx90
-rw-r--r--sw/source/core/undo/undraw.cxx682
-rw-r--r--sw/source/core/undo/unfmco.cxx87
-rw-r--r--sw/source/core/undo/unins.cxx1049
-rw-r--r--sw/source/core/undo/unmove.cxx309
-rw-r--r--sw/source/core/undo/unnum.cxx396
-rw-r--r--sw/source/core/undo/unoutl.cxx73
-rw-r--r--sw/source/core/undo/unovwr.cxx475
-rw-r--r--sw/source/core/undo/unredln.cxx602
-rw-r--r--sw/source/core/undo/unsect.cxx610
-rw-r--r--sw/source/core/undo/unsort.cxx253
-rw-r--r--sw/source/core/undo/unspnd.cxx197
-rw-r--r--sw/source/core/undo/untbl.cxx3225
-rw-r--r--sw/source/core/undo/untblk.cxx487
-rw-r--r--sw/source/core/unocore/SwXTextDefaults.cxx235
-rw-r--r--sw/source/core/unocore/TextCursorHelper.cxx38
-rw-r--r--sw/source/core/unocore/XMLRangeHelper.cxx387
-rw-r--r--sw/source/core/unocore/XMLRangeHelper.hxx68
-rw-r--r--sw/source/core/unocore/swunohelper.cxx337
-rw-r--r--sw/source/core/unocore/unobkm.cxx878
-rw-r--r--sw/source/core/unocore/unochart.cxx2722
-rw-r--r--sw/source/core/unocore/unocoll.cxx1947
-rw-r--r--sw/source/core/unocore/unocontentcontrol.cxx1149
-rw-r--r--sw/source/core/unocore/unocrsr.cxx214
-rw-r--r--sw/source/core/unocore/unocrsrhelper.cxx1551
-rw-r--r--sw/source/core/unocore/unodraw.cxx2887
-rw-r--r--sw/source/core/unocore/unoevent.cxx233
-rw-r--r--sw/source/core/unocore/unofield.cxx3019
-rw-r--r--sw/source/core/unocore/unoflatpara.cxx588
-rw-r--r--sw/source/core/unocore/unoframe.cxx3701
-rw-r--r--sw/source/core/unocore/unoftn.cxx569
-rw-r--r--sw/source/core/unocore/unoidx.cxx3094
-rw-r--r--sw/source/core/unocore/unolinebreak.cxx301
-rw-r--r--sw/source/core/unocore/unomap.cxx1575
-rw-r--r--sw/source/core/unocore/unomap1.cxx1717
-rw-r--r--sw/source/core/unocore/unomapproperties.hxx539
-rw-r--r--sw/source/core/unocore/unoobj.cxx3018
-rw-r--r--sw/source/core/unocore/unoobj2.cxx1853
-rw-r--r--sw/source/core/unocore/unoparagraph.cxx1419
-rw-r--r--sw/source/core/unocore/unoport.cxx871
-rw-r--r--sw/source/core/unocore/unoportenum.cxx1548
-rw-r--r--sw/source/core/unocore/unoredline.cxx593
-rw-r--r--sw/source/core/unocore/unoredlines.cxx169
-rw-r--r--sw/source/core/unocore/unorefmk.cxx1528
-rw-r--r--sw/source/core/unocore/unosect.cxx1742
-rw-r--r--sw/source/core/unocore/unosett.cxx2143
-rw-r--r--sw/source/core/unocore/unosrch.cxx581
-rw-r--r--sw/source/core/unocore/unostyle.cxx5645
-rw-r--r--sw/source/core/unocore/unotbl.cxx4167
-rw-r--r--sw/source/core/unocore/unotext.cxx2837
-rw-r--r--sw/source/core/unocore/unotextmarkup.cxx520
-rw-r--r--sw/source/core/view/dialoghelp.cxx47
-rw-r--r--sw/source/core/view/pagepreviewlayout.cxx1465
-rw-r--r--sw/source/core/view/printdata.cxx447
-rw-r--r--sw/source/core/view/vdraw.cxx282
-rw-r--r--sw/source/core/view/viewimp.cxx501
-rw-r--r--sw/source/core/view/viewpg.cxx216
-rw-r--r--sw/source/core/view/viewsh.cxx2809
-rw-r--r--sw/source/core/view/vnew.cxx385
-rw-r--r--sw/source/core/view/vprint.cxx690
-rw-r--r--sw/source/core/view/vprint.hxx27
643 files changed, 430264 insertions, 0 deletions
diff --git a/sw/source/core/SwNumberTree/SwNodeNum.cxx b/sw/source/core/SwNumberTree/SwNodeNum.cxx
new file mode 100644
index 000000000..9c160d3c0
--- /dev/null
+++ b/sw/source/core/SwNumberTree/SwNodeNum.cxx
@@ -0,0 +1,369 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <osl/diagnose.h>
+#include <numrule.hxx>
+#include <SwNodeNum.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <IDocumentListItems.hxx>
+#include <doc.hxx>
+
+SwNodeNum::SwNodeNum(SwTextNode* pTextNode, bool const isHiddenRedlines)
+ : mpTextNode( pTextNode )
+ , mpNumRule( nullptr )
+ , m_isHiddenRedlines(isHiddenRedlines)
+{
+}
+
+SwNodeNum::SwNodeNum( SwNumRule* pNumRule )
+ : mpTextNode( nullptr )
+ , mpNumRule( pNumRule )
+ , m_isHiddenRedlines(false)
+{
+}
+
+SwNodeNum::~SwNodeNum()
+{
+}
+
+
+void SwNodeNum::ChangeNumRule( SwNumRule& rNumRule )
+{
+ OSL_ENSURE( GetNumRule() && GetTextNode(),
+ "<SwNodeNum::ChangeNumRule(..)> - missing list style and/or text node. Serious defect -> please inform OD." );
+ if ( GetNumRule() && GetTextNode() )
+ {
+ GetNumRule()->RemoveTextNode( *(GetTextNode()) );
+ }
+
+ mpNumRule = &rNumRule;
+
+ if ( GetNumRule() && GetTextNode() )
+ {
+ GetNumRule()->AddTextNode( *(GetTextNode()) );
+ }
+}
+
+SwPosition SwNodeNum::GetPosition() const
+{
+ OSL_ENSURE( GetTextNode(),
+ "<SwNodeNum::GetPosition()> - no text node set at <SwNodeNum> instance" );
+ return SwPosition(*mpTextNode);
+}
+
+SwNumberTreeNode * SwNodeNum::Create() const
+{
+ SwNodeNum * pResult = new SwNodeNum( GetNumRule() );
+
+ return pResult;
+}
+
+void SwNodeNum::PreAdd()
+{
+ OSL_ENSURE( GetTextNode(),
+ "<SwNodeNum::PreAdd()> - no text node set at <SwNodeNum> instance" );
+ if ( !GetNumRule() && GetTextNode() )
+ {
+ mpNumRule = GetTextNode()->GetNumRule();
+ }
+ OSL_ENSURE( GetNumRule(),
+ "<SwNodeNum::PreAdd()> - no list style set at <SwNodeNum> instance" );
+ if (!m_isHiddenRedlines && GetNumRule() && GetTextNode())
+ {
+ GetNumRule()->AddTextNode( *(GetTextNode()) );
+ }
+
+ if (!m_isHiddenRedlines)
+ {
+ if ( GetTextNode() &&
+ GetTextNode()->GetNodes().IsDocNodes() )
+ {
+ GetTextNode()->getIDocumentListItems().addListItem( *this );
+ }
+ }
+}
+
+void SwNodeNum::PostRemove()
+{
+ OSL_ENSURE( GetTextNode(),
+ "<SwNodeNum::PostRemove()> - no text node set at <SwNodeNum> instance" );
+ OSL_ENSURE( GetNumRule(),
+ "<SwNodeNum::PostRemove()> - no list style set at <SwNodeNum> instance" );
+
+ if (!m_isHiddenRedlines && GetTextNode())
+ {
+ GetTextNode()->getIDocumentListItems().removeListItem( *this );
+ }
+
+ if ( GetNumRule() )
+ {
+ if (!m_isHiddenRedlines && GetTextNode())
+ {
+ GetNumRule()->RemoveTextNode( *(GetTextNode()) );
+ }
+ mpNumRule = nullptr;
+ }
+}
+
+bool SwNodeNum::IsNotifiable(const SwDoc& rDoc) const
+{
+ bool aResult;
+
+ if (const SwTextNode* pTextNode = GetTextNode())
+ aResult = pTextNode->IsNotifiable();
+ else
+ aResult = IsNotificationEnabled(rDoc);
+
+ return aResult;
+}
+
+bool SwNodeNum::IsNotificationEnabled(const SwDoc& rDoc) const
+{
+ bool aResult;
+
+ if (const SwTextNode* pTextNode = GetTextNode())
+ aResult = pTextNode->IsNotificationEnabled();
+ else
+ aResult = !rDoc.IsInReading() && !rDoc.IsInDtor();
+
+ return aResult;
+}
+
+bool SwNodeNum::IsContinuous() const
+{
+ bool aResult = false;
+
+ // #i64311#
+ if ( GetNumRule() )
+ {
+ aResult = mpNumRule->IsContinusNum();
+ }
+ else if ( GetParent() )
+ {
+ aResult = GetParent()->IsContinuous();
+ }
+ else
+ {
+ OSL_FAIL( "<SwNodeNum::IsContinuous()> - OD debug" );
+ }
+
+ return aResult;
+}
+
+bool SwNodeNum::IsCounted() const
+{
+ bool aResult = false;
+
+ if ( GetTextNode() )
+ {
+ // #i59559#
+ // <SwTextNode::IsCounted()> determines, if a text node is counted for numbering
+ aResult = GetTextNode()->IsCountedInList();
+ }
+ else
+ aResult = SwNumberTreeNode::IsCounted();
+
+ return aResult;
+}
+
+// #i64010#
+bool SwNodeNum::HasCountedChildren() const
+{
+ return std::any_of(mChildren.begin(), mChildren.end(),
+ [](SwNumberTreeNode* pNode) {
+ SwNodeNum* pChild( dynamic_cast<SwNodeNum*>(pNode) );
+ OSL_ENSURE( pChild, "<SwNodeNum::HasCountedChildren()> - unexpected type of child" );
+ return pChild && (pChild->IsCountedForNumbering() || pChild->HasCountedChildren());
+ });
+}
+// #i64010#
+bool SwNodeNum::IsCountedForNumbering() const
+{
+ return IsCounted() &&
+ ( IsPhantom() || // phantoms
+ !GetTextNode() || // root node
+ GetTextNode()->HasNumber() || // text node
+ GetTextNode()->HasBullet() ); // text node
+}
+
+void SwNodeNum::NotifyNode()
+{
+ ValidateMe();
+
+ if (mpTextNode)
+ {
+ mpTextNode->NumRuleChgd();
+ }
+}
+
+bool SwNodeNum::LessThan(const SwNumberTreeNode & rNode) const
+{
+ bool bResult = false;
+ const SwNodeNum & rTmpNode = static_cast<const SwNodeNum &>(rNode);
+
+ if (mpTextNode == nullptr && rTmpNode.mpTextNode != nullptr)
+ bResult = true;
+ else if (mpTextNode != nullptr && rTmpNode.mpTextNode != nullptr)
+ {
+ // #i83479# - refactoring
+ // simplify comparison by comparing the indexes of the text nodes
+ bResult = ( mpTextNode->GetIndex() < rTmpNode.mpTextNode->GetIndex() );
+ }
+
+ return bResult;
+}
+
+bool SwNodeNum::IsRestart() const
+{
+ bool bIsRestart = false;
+
+ if ( GetTextNode() )
+ {
+ bIsRestart = GetTextNode()->IsListRestart();
+ }
+
+ return bIsRestart;
+}
+
+bool SwNodeNum::IsCountPhantoms() const
+{
+ bool bResult = true;
+
+ // #i64311#
+ // phantoms aren't counted in consecutive numbering rules
+ if ( mpNumRule )
+ bResult = !mpNumRule->IsContinusNum() &&
+ mpNumRule->IsCountPhantoms();
+ else
+ {
+ OSL_FAIL( "<SwNodeNum::IsCountPhantoms(): missing numbering rule" );
+ }
+
+ return bResult;
+}
+
+SwNumberTree::tSwNumTreeNumber SwNodeNum::GetStartValue() const
+{
+ SwNumberTree::tSwNumTreeNumber aResult = 1;
+
+ if ( IsRestart() && GetTextNode() )
+ {
+ aResult = GetTextNode()->GetActualListStartValue();
+ }
+ else
+ {
+ SwNumRule * pRule = GetNumRule();
+
+ if (pRule)
+ {
+ int nLevel = GetParent() ? GetLevelInListTree() : 0;
+
+ if (nLevel >= 0 && nLevel < MAXLEVEL)
+ {
+ const SwNumFormat * pFormat = pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(nLevel));
+
+ if (pFormat)
+ aResult = pFormat->GetStart();
+ }
+ }
+ }
+
+ return aResult;
+}
+
+void SwNodeNum::HandleNumberTreeRootNodeDelete( SwNodeNum& rNodeNum )
+{
+ SwNodeNum* pRootNode = rNodeNum.GetParent()
+ ? dynamic_cast<SwNodeNum*>(rNodeNum.GetRoot())
+ : &rNodeNum;
+ if ( !pRootNode )
+ {
+ // no root node -> nothing do.
+ return;
+ }
+
+ // unregister all number tree node entries, which correspond to a text node,
+ // about the deletion of the number tree root node.
+ UnregisterMeAndChildrenDueToRootDelete( *pRootNode );
+}
+
+void SwNodeNum::UnregisterMeAndChildrenDueToRootDelete( SwNodeNum& rNodeNum )
+{
+ const bool bIsPhantom( rNodeNum.IsPhantom() );
+ tSwNumberTreeChildren::size_type nAllowedChildCount( 0 );
+ bool bDone( false );
+ while ( !bDone &&
+ rNodeNum.GetChildCount() > nAllowedChildCount )
+ {
+ SwNodeNum* pChildNode( dynamic_cast<SwNodeNum*>((*rNodeNum.mChildren.begin())) );
+ if ( !pChildNode )
+ {
+ OSL_FAIL( "<SwNodeNum::UnregisterMeAndChildrenDueToRootDelete(..)> - unknown number tree node child" );
+ ++nAllowedChildCount;
+ continue;
+ }
+
+ // Unregistering the last child of a phantom will destroy the phantom.
+ // Thus <rNodeNum> will be destroyed and access on <rNodeNum> has to
+ // be suppressed.
+ if ( bIsPhantom && rNodeNum.GetChildCount() == 1 )
+ {
+ bDone = true;
+ }
+
+ UnregisterMeAndChildrenDueToRootDelete( *pChildNode );
+ }
+
+ if ( bIsPhantom )
+ return;
+
+ SwTextNode* pTextNode( rNodeNum.GetTextNode() );
+ if ( !pTextNode )
+ return;
+
+ pTextNode->RemoveFromList();
+ // --> clear all list attributes and the list style
+ const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray{
+ RES_PARATR_LIST_ID, RES_PARATR_LIST_LEVEL, RES_PARATR_LIST_ISRESTART,
+ RES_PARATR_LIST_RESTARTVALUE, RES_PARATR_LIST_ISCOUNTED, RES_PARATR_NUMRULE
+ };
+ SwPaM aPam( *pTextNode );
+ pTextNode->GetDoc().ResetAttrs( aPam, false,
+ aResetAttrsArray,
+ false );
+}
+
+// #i81002#
+const SwNodeNum* SwNodeNum::GetPrecedingNodeNumOf( const SwTextNode& rTextNode ) const
+{
+ const SwNodeNum* pPrecedingNodeNum( nullptr );
+
+ // #i83479#
+ SwNodeNum aNodeNumForTextNode( const_cast<SwTextNode*>(&rTextNode), false/*doesn't matter*/ );
+
+ pPrecedingNodeNum = dynamic_cast<const SwNodeNum*>(
+ GetRoot()
+ ? GetRoot()->GetPrecedingNodeOf( aNodeNumForTextNode )
+ : GetPrecedingNodeOf( aNodeNumForTextNode ) );
+
+ return pPrecedingNodeNum;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/SwNumberTree/SwNumberTree.cxx b/sw/source/core/SwNumberTree/SwNumberTree.cxx
new file mode 100644
index 000000000..30cdff34b
--- /dev/null
+++ b/sw/source/core/SwNumberTree/SwNumberTree.cxx
@@ -0,0 +1,1181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <algorithm>
+#include <SwNumberTree.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <cassert>
+
+using std::vector;
+using std::find;
+
+SwNumberTreeNode::SwNumberTreeNode()
+ : mpParent( nullptr ),
+ mnNumber( 0 ),
+ mbContinueingPreviousSubTree( false ),
+ mbPhantom( false )
+{
+ mItLastValid = mChildren.end();
+}
+
+SwNumberTreeNode::~SwNumberTreeNode()
+{
+ if (GetChildCount() > 0)
+ {
+ if (HasOnlyPhantoms())
+ {
+ delete *mChildren.begin();
+
+ mChildren.clear();
+ mItLastValid = mChildren.end();
+ }
+ else
+ {
+ OSL_FAIL("lost children!");
+ }
+ }
+
+ OSL_ENSURE( IsPhantom() || mpParent == nullptr, ": I'm not supposed to have a parent.");
+
+ mpParent = reinterpret_cast<SwNumberTreeNode *>(0xdeadbeef);
+
+ OSL_ENSURE(mChildren.empty(), "children left!");
+}
+
+SwNumberTreeNode * SwNumberTreeNode::CreatePhantom()
+{
+ SwNumberTreeNode * pNew = nullptr;
+
+ if (! mChildren.empty() &&
+ (*mChildren.begin())->IsPhantom())
+ {
+ OSL_FAIL("phantom already present");
+ }
+ else
+ {
+ pNew = Create();
+ pNew->mbPhantom = true;
+ pNew->mpParent = this;
+
+ std::pair<tSwNumberTreeChildren::iterator, bool> aInsert =
+ mChildren.insert(pNew);
+
+ if (! aInsert.second)
+ {
+ OSL_FAIL("insert of phantom failed!");
+
+ delete pNew;
+ pNew = nullptr;
+ }
+ }
+
+ return pNew;
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetRoot() const
+{
+ SwNumberTreeNode * pResult = mpParent;
+
+ if (pResult)
+ while (pResult->mpParent)
+ pResult = pResult->mpParent;
+
+ return pResult;
+}
+
+void SwNumberTreeNode::ClearObsoletePhantoms()
+{
+ tSwNumberTreeChildren::iterator aIt = mChildren.begin();
+
+ if (!(aIt != mChildren.end() && (*aIt)->IsPhantom()))
+ return;
+
+ (*aIt)->ClearObsoletePhantoms();
+
+ if ((*aIt)->mChildren.empty())
+ {
+ // #i60652#
+ // Because <mChildren.erase(aIt)> could destroy the element, which
+ // is referenced by <mItLastValid>, it's needed to adjust
+ // <mItLastValid> before erasing <aIt>.
+ SetLastValid(mChildren.end());
+
+ delete *aIt;
+ mChildren.erase(aIt);
+ }
+}
+
+void SwNumberTreeNode::ValidateHierarchical(const SwNumberTreeNode * pNode) const
+{
+ tSwNumberTreeChildren::const_iterator aValidateIt =
+ GetIterator(pNode);
+
+ if (aValidateIt == mChildren.end())
+ return;
+
+ OSL_ENSURE((*aValidateIt)->mpParent == this, "wrong parent");
+
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ // -->
+ // improvement:
+ // - Only one time checked for <mChildren.end()>.
+ // - Less checks for each loop run.
+ // correction:
+ // - consider case that current node isn't counted and isn't the first
+ // child of its parent. In this case the number of last counted child
+ // of the previous node determines the start value for the following
+ // children loop, if all children have to be validated and the first
+ // one doesn't restart the counting.
+ SwNumberTree::tSwNumTreeNumber nTmpNumber( 0 );
+ if (aIt != mChildren.end())
+ nTmpNumber = (*aIt)->mnNumber;
+ else
+ {
+ aIt = mChildren.begin();
+ (*aIt)->mbContinueingPreviousSubTree = false;
+
+ // determine default start value
+ // consider the case that the first child isn't counted.
+ nTmpNumber = (*aIt)->GetStartValue();
+ if ( !(*aIt)->IsCounted() &&
+ ( !(*aIt)->HasCountedChildren() || (*aIt)->IsPhantom() ) )
+ {
+ --nTmpNumber;
+ }
+
+ // determine special start value for the case that first child
+ // doesn't restart the numbering and the parent node isn't counted
+ // and isn't the first child.
+ const bool bParentCounted( IsCounted() &&
+ ( !IsPhantom() ||
+ HasPhantomCountedParent() ) );
+ if ( !(*aIt)->IsRestart() &&
+ GetParent() && !bParentCounted )
+ {
+ tSwNumberTreeChildren::const_iterator aParentChildIt =
+ GetParent()->GetIterator( this );
+ while ( aParentChildIt != GetParent()->mChildren.begin() )
+ {
+ --aParentChildIt;
+ SwNumberTreeNode* pPrevNode( *aParentChildIt );
+ if ( pPrevNode->GetChildCount() > 0 )
+ {
+ (*aIt)->mbContinueingPreviousSubTree = true;
+ nTmpNumber = (*(pPrevNode->mChildren.rbegin()))->GetNumber();
+ if ( (*aIt)->IsCounted() &&
+ ( !(*aIt)->IsPhantom() ||
+ (*aIt)->HasPhantomCountedParent() ) )
+ {
+ ++nTmpNumber;
+ }
+ break;
+ }
+ else if ( pPrevNode->IsCounted() )
+ {
+ break;
+ }
+ else
+ {
+ // Previous node has no children and is not counted.
+ // Thus, next turn and check for the previous node.
+ }
+ }
+ }
+
+ (*aIt)->mnNumber = nTmpNumber;
+ }
+
+ while (aIt != aValidateIt)
+ {
+ ++aIt;
+ (*aIt)->mbContinueingPreviousSubTree = false;
+
+ // --> only for counted nodes the number
+ // has to be adjusted, compared to the previous node.
+ // this condition is hold also for nodes, which restart the numbering.
+ if ( (*aIt)->IsCounted() )
+ {
+ if ((*aIt)->IsRestart())
+ nTmpNumber = (*aIt)->GetStartValue();
+ else
+ ++nTmpNumber;
+ }
+
+ (*aIt)->mnNumber = nTmpNumber;
+ }
+
+ SetLastValid(aIt, true);
+}
+
+void SwNumberTreeNode::ValidateContinuous(const SwNumberTreeNode * pNode) const
+{
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ do
+ {
+ if (aIt == mChildren.end())
+ {
+ aIt = mChildren.begin();
+ }
+ else
+ ++aIt;
+
+ if (aIt != mChildren.end())
+ {
+ SwNumberTree::tSwNumTreeNumber nTmpNumber = 0;
+ SwNumberTreeNode * pPred = (*aIt)->GetPred();
+
+ // #i64311#
+ // correct consideration of phantoms
+ // correct consideration of restart at a number tree node
+ if ( pPred )
+ {
+ if ( !(*aIt)->IsCounted() )
+ // #i65284#
+ nTmpNumber = pPred->GetNumber( pPred->GetParent() != (*aIt)->GetParent() );
+ else
+ {
+ if ( (*aIt)->IsRestart() )
+ nTmpNumber = (*aIt)->GetStartValue();
+ else
+ nTmpNumber = pPred->GetNumber( pPred->GetParent() != (*aIt)->GetParent() ) + 1;
+ }
+ }
+ else
+ {
+ if ( !(*aIt)->IsCounted() )
+ nTmpNumber = GetStartValue() - 1;
+ else
+ {
+ if ( (*aIt)->IsRestart() )
+ nTmpNumber = (*aIt)->GetStartValue();
+ else
+ nTmpNumber = GetStartValue();
+ }
+ }
+
+ (*aIt)->mnNumber = nTmpNumber;
+ }
+ }
+ while (aIt != mChildren.end() && *aIt != pNode);
+
+ // #i74748# - applied patch from garnier_romain
+ // number tree node has to be validated.
+ SetLastValid( aIt, true );
+}
+
+void SwNumberTreeNode::Validate(const SwNumberTreeNode * pNode) const
+{
+ if (! IsValid(pNode))
+ {
+ if (IsContinuous())
+ ValidateContinuous(pNode);
+ else
+ ValidateHierarchical(pNode);
+ }
+}
+
+void SwNumberTreeNode::GetNumberVector_(SwNumberTree::tNumberVector & rVector,
+ bool bValidate) const
+{
+ if (mpParent)
+ {
+ mpParent->GetNumberVector_(rVector, bValidate);
+ rVector.push_back(GetNumber(bValidate));
+ }
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetFirstNonPhantomChild()
+{
+ if (IsPhantom())
+ return (*mChildren.begin())->GetFirstNonPhantomChild();
+
+ return this;
+}
+
+/** Moves all children of this node that are greater than a given node
+ to the destination node.
+*/
+void SwNumberTreeNode::MoveGreaterChildren( SwNumberTreeNode& _rCompareNode,
+ SwNumberTreeNode& _rDestNode )
+{
+ if ( mChildren.empty() )
+ return;
+
+ // determine first child, which has to move to <_rDestNode>
+ tSwNumberTreeChildren::iterator aItUpper( mChildren.end() );
+ if ((*mChildren.begin())->IsPhantom() &&
+ _rCompareNode.LessThan(*(*mChildren.begin())->GetFirstNonPhantomChild()))
+ {
+ aItUpper = mChildren.begin();
+ }
+ else
+ {
+ aItUpper = mChildren.upper_bound(&_rCompareNode);
+ }
+
+ // move children
+ if (aItUpper != mChildren.end())
+ {
+ tSwNumberTreeChildren::iterator aIt;
+ for (aIt = aItUpper; aIt != mChildren.end(); ++aIt)
+ (*aIt)->mpParent = &_rDestNode;
+
+ _rDestNode.mChildren.insert(aItUpper, mChildren.end());
+
+ // #i60652#
+ // Because <mChildren.erase(aItUpper, mChildren.end())> could destroy
+ // the element, which is referenced by <mItLastValid>, it's needed to
+ // adjust <mItLastValid> before erasing <aIt>.
+ SetLastValid( mChildren.end() );
+
+ mChildren.erase(aItUpper, mChildren.end());
+
+ // #i60652#
+ if ( !mChildren.empty() )
+ {
+ SetLastValid( --(mChildren.end()) );
+ }
+ }
+
+#ifdef DBG_UTIL
+ IsSane(false);
+ _rDestNode.IsSane(true);
+#endif
+}
+
+void SwNumberTreeNode::MoveChildren(SwNumberTreeNode * pDest)
+{
+ if (! mChildren.empty())
+ {
+ tSwNumberTreeChildren::iterator aItBegin = mChildren.begin();
+ SwNumberTreeNode * pMyFirst = *mChildren.begin();
+
+ // #i60652#
+ // Because <mChildren.erase(aItBegin)> could destroy the element,
+ // which is referenced by <mItLastValid>, it's needed to adjust
+ // <mItLastValid> before erasing <aItBegin>.
+ SetLastValid(mChildren.end());
+
+ if (pMyFirst->IsPhantom())
+ {
+ SwNumberTreeNode * pDestLast = nullptr;
+
+ if (pDest->mChildren.empty())
+ pDestLast = pDest->CreatePhantom();
+ else
+ pDestLast = *pDest->mChildren.rbegin();
+
+ pMyFirst->MoveChildren(pDestLast);
+
+ delete pMyFirst;
+ mChildren.erase(aItBegin);
+
+ aItBegin = mChildren.begin();
+ }
+
+ for (auto& rpChild : mChildren)
+ rpChild->mpParent = pDest;
+
+ pDest->mChildren.insert(mChildren.begin(), mChildren.end());
+ mChildren.clear();
+ // <stl::set.clear()> destroys all existing iterators.
+ // Thus, <mItLastValid> is also destroyed and reset becomes necessary
+ mItLastValid = mChildren.end();
+ }
+
+ OSL_ENSURE(mChildren.empty(), "MoveChildren failed!");
+
+#ifdef DBG_UTIL
+ IsSane(false);
+ pDest->IsSane(false);
+#endif
+}
+
+void SwNumberTreeNode::AddChild(SwNumberTreeNode* pChild,
+ const int nDepth,
+ const SwDoc& rDoc)
+{
+ /*
+ Algorithm:
+
+ Search first child A that is greater than pChild,
+ A may be the end of children.
+ If nDepth > 0 then
+ {
+ if A is first child then
+ create new phantom child B at beginning of child list
+ else
+ B is A
+
+ Add child to B with depth nDepth - 1.
+ }
+ else
+ {
+ Insert pNode before A.
+
+ if A has predecessor B then
+ remove children of B that are greater as A and insert them as
+ children of A.
+ }
+
+*/
+
+ if ( nDepth < 0 )
+ {
+ OSL_FAIL( "<SwNumberTreeNode::AddChild(..)> - parameter <nDepth> out of valid range. Serious defect." );
+ return;
+ }
+
+ if ( pChild->GetParent() != nullptr || pChild->GetChildCount() > 0 )
+ {
+ OSL_FAIL("only orphans allowed.");
+ return;
+ }
+
+ if (nDepth > 0)
+ {
+ tSwNumberTreeChildren::iterator aInsertDeepIt =
+ mChildren.upper_bound(pChild);
+
+ OSL_ENSURE(! (aInsertDeepIt != mChildren.end() &&
+ (*aInsertDeepIt)->IsPhantom()), " unexpected phantom");
+
+ if (aInsertDeepIt == mChildren.begin())
+ {
+ SwNumberTreeNode * pNew = CreatePhantom();
+
+ SetLastValid(mChildren.end());
+
+ if (pNew)
+ pNew->AddChild(pChild, nDepth - 1, rDoc);
+ }
+ else
+ {
+ --aInsertDeepIt;
+ (*aInsertDeepIt)->AddChild(pChild, nDepth - 1, rDoc);
+ }
+
+ }
+ else
+ {
+ pChild->PreAdd();
+ std::pair<tSwNumberTreeChildren::iterator, bool> aResult =
+ mChildren.insert(pChild);
+
+ if (aResult.second)
+ {
+ pChild->mpParent = this;
+ bool bNotification = pChild->IsNotificationEnabled(rDoc);
+ tSwNumberTreeChildren::iterator aInsertedIt = aResult.first;
+
+ if (aInsertedIt != mChildren.begin())
+ {
+ tSwNumberTreeChildren::iterator aPredIt = aInsertedIt;
+ --aPredIt;
+
+ // -->
+ // Move greater children of previous node to new child.
+ // This has to be done recursively on the children levels.
+ // Initialize loop variables <pPrevChildNode> and <pDestNode>
+ // for loop on children levels.
+ SwNumberTreeNode* pPrevChildNode( *aPredIt );
+ SwNumberTreeNode* pDestNode( pChild );
+ while ( pDestNode && pPrevChildNode &&
+ pPrevChildNode->GetChildCount() > 0 )
+ {
+ // move children
+ pPrevChildNode->MoveGreaterChildren( *pChild, *pDestNode );
+
+ // prepare next loop:
+ // - search of last child of <pPrevChildNode
+ // - If found, determine destination node
+ if ( pPrevChildNode->GetChildCount() > 0 )
+ {
+ tSwNumberTreeChildren::reverse_iterator aIt =
+ pPrevChildNode->mChildren.rbegin();
+ pPrevChildNode = *aIt;
+ // determine new destination node
+ if ( pDestNode->GetChildCount() > 0 )
+ {
+ pDestNode = *(pDestNode->mChildren.begin());
+ if ( !pDestNode->IsPhantom() )
+ {
+ pDestNode = pDestNode->mpParent->CreatePhantom();
+ }
+ }
+ else
+ {
+ pDestNode = pDestNode->CreatePhantom();
+ }
+ }
+ else
+ {
+ // ready -> break loop.
+ break;
+ }
+ }
+ // assure that unnecessary created phantoms at <pChild> are deleted.
+ pChild->ClearObsoletePhantoms();
+
+ if ((*aPredIt)->IsValid())
+ SetLastValid(aPredIt);
+ }
+ else
+ SetLastValid(mChildren.end());
+
+ ClearObsoletePhantoms();
+
+ if( bNotification )
+ {
+ // invalidation of not counted parent
+ // and notification of its siblings.
+ if ( !IsCounted() )
+ {
+ InvalidateMe();
+ NotifyInvalidSiblings(rDoc);
+ }
+ NotifyInvalidChildren(rDoc);
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ IsSane(false);
+#endif
+}
+
+void SwNumberTreeNode::RemoveChild(SwNumberTreeNode* pChild, const SwDoc& rDoc)
+{
+ /*
+ Algorithm:
+
+ if pChild has predecessor A then
+ B is A
+ else
+ create phantom child B at beginning of child list
+
+ Move children of pChild to B.
+ */
+
+ if (pChild->IsPhantom())
+ {
+ OSL_FAIL("not applicable to phantoms!");
+
+ return;
+ }
+
+ tSwNumberTreeChildren::const_iterator aRemoveIt = GetIterator(pChild);
+
+ if (aRemoveIt != mChildren.end())
+ {
+ SwNumberTreeNode * pRemove = *aRemoveIt;
+
+ pRemove->mpParent = nullptr;
+
+ tSwNumberTreeChildren::const_iterator aItPred = mChildren.end();
+
+ if (aRemoveIt == mChildren.begin())
+ {
+ if (! pRemove->mChildren.empty())
+ {
+ CreatePhantom();
+
+ aItPred = mChildren.begin();
+ }
+ }
+ else
+ {
+ aItPred = aRemoveIt;
+ --aItPred;
+ }
+
+ if (! pRemove->mChildren.empty())
+ {
+ pRemove->MoveChildren(*aItPred);
+ (*aItPred)->InvalidateTree();
+ (*aItPred)->NotifyInvalidChildren(rDoc);
+ }
+
+ // #i60652#
+ // Because <mChildren.erase(aRemoveIt)> could destroy the element,
+ // which is referenced by <mItLastValid>, it's needed to adjust
+ // <mItLastValid> before erasing <aRemoveIt>.
+ if (aItPred != mChildren.end() && (*aItPred)->IsPhantom())
+ SetLastValid(mChildren.end());
+ else
+ SetLastValid(aItPred);
+
+ mChildren.erase(aRemoveIt);
+
+ NotifyInvalidChildren(rDoc);
+ }
+ else
+ {
+ OSL_FAIL("RemoveChild: failed!");
+ }
+
+ pChild->PostRemove();
+}
+
+void SwNumberTreeNode::RemoveMe(const SwDoc& rDoc)
+{
+ if (!mpParent)
+ return;
+
+ SwNumberTreeNode * pSavedParent = mpParent;
+
+ pSavedParent->RemoveChild(this, rDoc);
+
+ while (pSavedParent && pSavedParent->IsPhantom() &&
+ pSavedParent->HasOnlyPhantoms())
+ pSavedParent = pSavedParent->GetParent();
+
+ if (pSavedParent)
+ pSavedParent->ClearObsoletePhantoms();
+
+#ifdef DBG_UTIL
+ IsSane(false);
+#endif
+}
+
+bool SwNumberTreeNode::IsValid() const
+{
+ return mpParent && mpParent->IsValid(this);
+}
+
+SwNumberTree::tSwNumTreeNumber SwNumberTreeNode::GetNumber(bool bValidate)
+ const
+{
+ if (bValidate && mpParent)
+ mpParent->Validate(this);
+
+ return mnNumber;
+}
+
+
+SwNumberTree::tNumberVector SwNumberTreeNode::GetNumberVector() const
+{
+ vector<SwNumberTree::tSwNumTreeNumber> aResult;
+
+ GetNumberVector_(aResult);
+
+ return aResult;
+}
+
+bool SwNumberTreeNode::IsValid(const SwNumberTreeNode * pChild) const
+{
+ bool bResult = false;
+
+ if (mItLastValid != mChildren.end())
+ {
+ if (pChild && pChild->mpParent == this)
+ {
+ bResult = ! (*mItLastValid)->LessThan(*pChild);
+ }
+ }
+
+ return bResult;
+}
+
+
+bool SwNumberTreeNode::HasOnlyPhantoms() const
+{
+ bool bResult = false;
+
+ if (GetChildCount() == 1)
+ {
+ tSwNumberTreeChildren::const_iterator aIt = mChildren.begin();
+
+ bResult = (*aIt)->IsPhantom() && (*aIt)->HasOnlyPhantoms();
+ }
+ else if (GetChildCount() == 0)
+ bResult = true;
+
+ return bResult;
+}
+
+bool SwNumberTreeNode::IsCounted() const
+{
+ return !IsPhantom() ||
+ ( IsCountPhantoms() && HasCountedChildren() );
+}
+
+bool SwNumberTreeNode::HasPhantomCountedParent() const
+{
+ bool bRet( false );
+
+ OSL_ENSURE( IsPhantom(),
+ "<SwNumberTreeNode::HasPhantomCountedParent()> - wrong usage of method - it's only for phantoms" );
+ if ( IsPhantom() && mpParent )
+ {
+ if ( mpParent == GetRoot() )
+ {
+ bRet = true;
+ }
+ else if ( !mpParent->IsPhantom() )
+ {
+ bRet = mpParent->IsCounted();
+ }
+ else
+ {
+ bRet = mpParent->IsCounted() && mpParent->HasPhantomCountedParent();
+ }
+ }
+
+ return bRet;
+}
+
+bool SwNumberTreeNode::IsFirst(const SwNumberTreeNode * pNode) const
+{
+ tSwNumberTreeChildren::const_iterator aIt = mChildren.begin();
+
+ if ((*aIt)->IsPhantom())
+ ++aIt;
+
+ return *aIt == pNode;
+}
+
+bool SwNumberTreeNode::IsFirst() const
+{
+ bool bResult = true;
+
+ if (GetParent())
+ {
+ if (GetParent()->IsFirst(this))
+ {
+ SwNumberTreeNode * pNode = GetParent();
+
+ while (pNode)
+ {
+ if (!pNode->IsPhantom() && pNode->GetParent())
+ {
+ bResult = false;
+ break;
+ }
+
+ pNode = pNode->GetParent();
+ }
+
+ // If node isn't the first child, it is the second child and the
+ // first child is a phantom. In this case check, if the first phantom
+ // child have only phantom children
+ if ( bResult &&
+ this != *(GetParent()->mChildren.begin()) &&
+ !(*(GetParent()->mChildren.begin()))->HasOnlyPhantoms() )
+ {
+ bResult = false;
+ }
+ }
+ else
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+void SwNumberTreeNode::SetLevelInListTree(const int nLevel, const SwDoc& rDoc)
+{
+ if ( nLevel < 0 )
+ {
+ OSL_FAIL( "<SwNumberTreeNode::SetLevelInListTree(..)> - parameter <nLevel> out of valid range. Serious defect." );
+ return;
+ }
+
+ OSL_ENSURE( GetParent(),
+ "<SwNumberTreeNode::SetLevelInListTree(..)> - can only be called for number tree nodes in a list tree" );
+ if ( GetParent() )
+ {
+ if ( nLevel != GetLevelInListTree() )
+ {
+ SwNumberTreeNode* pRootTreeNode = GetRoot();
+ OSL_ENSURE( pRootTreeNode,
+ "<SwNumberTreeNode::SetLevelInListTree(..)> - no root tree node found. Serious defect." );
+
+ RemoveMe(rDoc);
+ pRootTreeNode->AddChild(this, nLevel, rDoc);
+ }
+ }
+}
+
+int SwNumberTreeNode::GetLevelInListTree() const
+{
+ if (mpParent)
+ return mpParent->GetLevelInListTree() + 1;
+
+ return -1;
+}
+
+SwNumberTreeNode::tSwNumberTreeChildren::size_type
+SwNumberTreeNode::GetChildCount() const
+{
+ return mChildren.size();
+}
+
+#ifdef DBG_UTIL
+void SwNumberTreeNode::IsSane(bool bRecursive) const
+{
+ vector<const SwNumberTreeNode*> aParents;
+
+ return IsSane(bRecursive, std::move(aParents));
+}
+
+void SwNumberTreeNode::IsSane(bool bRecursive,
+ vector<const SwNumberTreeNode *> rParents)
+ const
+{
+ assert(find(rParents.begin(), rParents.end(), this) == rParents.end());
+
+ assert(rParents.empty() || rParents.back() == mpParent);
+
+ rParents.push_back(this);
+
+ bool bFirst = true;
+ for (const auto& rpChild : mChildren)
+ {
+ if (rpChild)
+ {
+ if (rpChild->IsPhantom())
+ {
+ SAL_WARN_IF(rpChild->HasOnlyPhantoms(), "sw.core",
+ "HasOnlyPhantoms: is this an error?");
+
+ assert(bFirst && "found phantom not at first position.");
+ }
+
+ assert(rpChild->mpParent == this);
+
+ if (mpParent)
+ {
+ assert(rpChild->IsPhantom() || !rpChild->LessThan(*this));
+ }
+ }
+ else
+ {
+ assert(!"found child that is NULL");
+ }
+
+ if (bRecursive)
+ {
+ rpChild->IsSane(bRecursive, rParents);
+ }
+
+ bFirst = false;
+ }
+
+ rParents.pop_back();
+}
+#endif // DBG_UTIL
+
+SwNumberTreeNode::tSwNumberTreeChildren::const_iterator
+SwNumberTreeNode::GetIterator(const SwNumberTreeNode * pChild) const
+{
+ tSwNumberTreeChildren::const_iterator aItResult =
+ mChildren.find(const_cast<SwNumberTreeNode *>(pChild));
+
+ OSL_ENSURE( aItResult != mChildren.end(),
+ "something went wrong getting the iterator for a child");
+
+ return aItResult;
+}
+
+bool SwNumberTreeNodeLessThan(const SwNumberTreeNode * pA,
+ const SwNumberTreeNode * pB)
+{
+ bool bResult = false;
+
+ if (pA == nullptr && pB != nullptr)
+ bResult = true;
+ else if (pA != nullptr && pB != nullptr)
+ bResult = pA->LessThan(*pB);
+
+ return bResult;
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetLastDescendant() const
+{
+ SwNumberTreeNode * pResult = nullptr;
+ tSwNumberTreeChildren::const_reverse_iterator aIt = mChildren.rbegin();
+
+ if (aIt != mChildren.rend())
+ {
+ pResult = (*aIt)->GetLastDescendant();
+
+ if (! pResult)
+ pResult = *aIt;
+ }
+
+ return pResult;
+}
+
+bool SwNumberTreeNode::LessThan(const SwNumberTreeNode & rTreeNode) const
+{
+ return this < &rTreeNode;
+}
+
+SwNumberTreeNode * SwNumberTreeNode::GetPred(bool bSibling) const
+{
+ SwNumberTreeNode * pResult = nullptr;
+
+ if (mpParent)
+ {
+ tSwNumberTreeChildren::const_iterator aIt =
+ mpParent->GetIterator(this);
+
+ if ( aIt == mpParent->mChildren.begin() )
+ {
+ // #i64311#
+ // root node is no valid predecessor
+ pResult = mpParent->GetParent() ? mpParent : nullptr;
+ }
+ else
+ {
+ --aIt;
+
+ if ( !bSibling )
+ pResult = (*aIt)->GetLastDescendant();
+ else
+ pResult = (*aIt);
+
+ if (! pResult)
+ pResult = (*aIt);
+ }
+ }
+
+ return pResult;
+}
+
+void SwNumberTreeNode::SetLastValid
+ ( const SwNumberTreeNode::tSwNumberTreeChildren::const_iterator& aItValid,
+ bool bValidating ) const
+{
+ OSL_ENSURE( (aItValid == mChildren.end() || GetIterator(*aItValid) != mChildren.end()),
+ "last-valid iterator");
+
+ if (
+ bValidating ||
+ aItValid == mChildren.end() ||
+ (mItLastValid != mChildren.end() &&
+ (*aItValid)->LessThan(**mItLastValid))
+ )
+ {
+ mItLastValid = aItValid;
+ // invalidation of children of next not counted is needed
+ if ( GetParent() )
+ {
+ tSwNumberTreeChildren::const_iterator aParentChildIt =
+ GetParent()->GetIterator( this );
+ ++aParentChildIt;
+ if ( aParentChildIt != GetParent()->mChildren.end() )
+ {
+ SwNumberTreeNode* pNextNode( *aParentChildIt );
+ if ( !pNextNode->IsCounted() )
+ {
+ pNextNode->InvalidateChildren();
+ }
+ }
+ }
+ }
+
+ {
+ if (IsContinuous())
+ {
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ if (aIt != mChildren.end())
+ ++aIt;
+ else
+ aIt = mChildren.begin();
+
+ while (aIt != mChildren.end())
+ {
+ (*aIt)->InvalidateTree();
+
+ ++aIt;
+ }
+
+ if (mpParent)
+ {
+ mpParent->SetLastValid(mpParent->GetIterator(this), bValidating);
+ }
+ }
+ }
+}
+
+void SwNumberTreeNode::InvalidateTree() const
+{
+ // do not call SetInvalid, would cause loop !!!
+ mItLastValid = mChildren.end();
+
+ for (const auto& rpChild : mChildren)
+ rpChild->InvalidateTree();
+}
+
+void SwNumberTreeNode::Invalidate(SwNumberTreeNode const * pChild)
+{
+ if (pChild->IsValid())
+ {
+ tSwNumberTreeChildren::const_iterator aIt = GetIterator(pChild);
+
+ if (aIt != mChildren.begin())
+ --aIt;
+ else
+ aIt = mChildren.end();
+
+ SetLastValid(aIt);
+
+ }
+}
+
+void SwNumberTreeNode::InvalidateMe()
+{
+ if (mpParent)
+ mpParent->Invalidate(this);
+}
+
+void SwNumberTreeNode::ValidateMe()
+{
+ if (mpParent)
+ mpParent->Validate(this);
+}
+
+void SwNumberTreeNode::Notify(const SwDoc& rDoc)
+{
+ if (IsNotifiable(rDoc))
+ {
+ if (! IsPhantom())
+ NotifyNode();
+
+ for (auto& rpChild : mChildren)
+ rpChild->Notify(rDoc);
+ }
+}
+
+void SwNumberTreeNode::NotifyInvalidChildren(const SwDoc& rDoc)
+{
+ if (IsNotifiable(rDoc))
+ {
+ tSwNumberTreeChildren::const_iterator aIt = mItLastValid;
+
+ if (aIt == mChildren.end())
+ aIt = mChildren.begin();
+ else
+ ++aIt;
+
+ while (aIt != mChildren.end())
+ {
+ (*aIt)->Notify(rDoc);
+
+ ++aIt;
+ }
+ // notification of next not counted node is also needed.
+ if ( GetParent() )
+ {
+ tSwNumberTreeChildren::const_iterator aParentChildIt =
+ GetParent()->GetIterator( this );
+ ++aParentChildIt;
+ if ( aParentChildIt != GetParent()->mChildren.end() )
+ {
+ SwNumberTreeNode* pNextNode( *aParentChildIt );
+ if ( !pNextNode->IsCounted() )
+ {
+ pNextNode->NotifyInvalidChildren(rDoc);
+ }
+ }
+ }
+
+ }
+
+ if (IsContinuous() && mpParent)
+ mpParent->NotifyInvalidChildren(rDoc);
+}
+
+void SwNumberTreeNode::NotifyInvalidSiblings(const SwDoc& rDoc)
+{
+ if (mpParent != nullptr)
+ mpParent->NotifyInvalidChildren(rDoc);
+}
+
+// #i81002#
+const SwNumberTreeNode* SwNumberTreeNode::GetPrecedingNodeOf(
+ const SwNumberTreeNode& rNode ) const
+{
+ const SwNumberTreeNode* pPrecedingNode( nullptr );
+
+ if ( GetChildCount() > 0 )
+ {
+ tSwNumberTreeChildren::const_iterator aUpperBoundIt =
+ mChildren.upper_bound( const_cast<SwNumberTreeNode*>(&rNode) );
+ if ( aUpperBoundIt != mChildren.begin() )
+ {
+ --aUpperBoundIt;
+ pPrecedingNode = (*aUpperBoundIt)->GetPrecedingNodeOf( rNode );
+ }
+ }
+
+ if ( pPrecedingNode == nullptr && GetRoot() )
+ {
+ // <this> node has no children or the given node precedes all its children
+ // and the <this> node isn't the root node.
+ // Thus, compare the given node with the <this> node in order to check,
+ // if the <this> node precedes the given node.
+ if ( !(rNode.LessThan( *this )) )
+ {
+ pPrecedingNode = this;
+ }
+ }
+
+ return pPrecedingNode;
+}
+
+void SwNumberTreeNode::NotifyNodesOnListLevel( const int nListLevel )
+{
+ if ( nListLevel < 0 )
+ {
+ OSL_FAIL( "<SwNumberTreeNode::NotifyNodesOnListLevel(..)> - invalid list level provided" );
+ return;
+ }
+
+ SwNumberTreeNode* pRootNode = GetParent() ? GetRoot() : this;
+
+ pRootNode->NotifyChildrenOnDepth( nListLevel );
+}
+
+void SwNumberTreeNode::NotifyChildrenOnDepth( const int nDepth )
+{
+ OSL_ENSURE( nDepth >= 0,
+ "<SwNumberTreeNode::NotifyChildrenOnDepth(..)> - misusage" );
+
+ for ( const auto& rpChild : mChildren )
+ {
+ if ( nDepth == 0 )
+ {
+ rpChild->NotifyNode();
+ }
+ else
+ {
+ rpChild->NotifyChildrenOnDepth( nDepth - 1 );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/AccessibilityCheck.cxx b/sw/source/core/access/AccessibilityCheck.cxx
new file mode 100644
index 000000000..50a37dc00
--- /dev/null
+++ b/sw/source/core/access/AccessibilityCheck.cxx
@@ -0,0 +1,969 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <AccessibilityCheck.hxx>
+#include <AccessibilityIssue.hxx>
+#include <AccessibilityCheckStrings.hrc>
+#include <ndnotxt.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <drawdoc.hxx>
+#include <svx/svdpage.hxx>
+#include <swtable.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <unoparagraph.hxx>
+#include <tools/urlobj.hxx>
+#include <editeng/langitem.hxx>
+#include <charatr.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <svl/itemiter.hxx>
+#include <o3tl/vector_utils.hxx>
+#include <svx/swframetypes.hxx>
+#include <fmtanchr.hxx>
+#include <dcontact.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdasitm.hxx>
+
+namespace sw
+{
+namespace
+{
+std::shared_ptr<sw::AccessibilityIssue>
+lclAddIssue(sfx::AccessibilityIssueCollection& rIssueCollection, OUString const& rText,
+ sfx::AccessibilityIssueID eIssue = sfx::AccessibilityIssueID::UNSPECIFIED)
+{
+ auto pIssue = std::make_shared<sw::AccessibilityIssue>(eIssue);
+ pIssue->m_aIssueText = rText;
+ rIssueCollection.getIssues().push_back(pIssue);
+ return pIssue;
+}
+
+class BaseCheck
+{
+protected:
+ sfx::AccessibilityIssueCollection& m_rIssueCollection;
+
+public:
+ BaseCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : m_rIssueCollection(rIssueCollection)
+ {
+ }
+ virtual ~BaseCheck() {}
+};
+
+class NodeCheck : public BaseCheck
+{
+public:
+ NodeCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : BaseCheck(rIssueCollection)
+ {
+ }
+
+ virtual void check(SwNode* pCurrent) = 0;
+};
+
+// Check NoTextNodes: Graphic, OLE for alt (title) text
+class NoTextNodeAltTextCheck : public NodeCheck
+{
+ void checkNoTextNode(SwNoTextNode* pNoTextNode)
+ {
+ if (!pNoTextNode)
+ return;
+
+ OUString sAlternative = pNoTextNode->GetTitle();
+ if (!sAlternative.isEmpty())
+ return;
+
+ OUString sName = pNoTextNode->GetFlyFormat()->GetName();
+
+ OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
+
+ if (pNoTextNode->IsOLENode())
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::NO_ALT_OLE);
+ pIssue->setDoc(pNoTextNode->GetDoc());
+ pIssue->setIssueObject(IssueObject::OLE);
+ pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
+ }
+ else if (pNoTextNode->IsGrfNode())
+ {
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::NO_ALT_GRAPHIC);
+ pIssue->setDoc(pNoTextNode->GetDoc());
+ pIssue->setIssueObject(IssueObject::GRAPHIC);
+ pIssue->setObjectID(pNoTextNode->GetFlyFormat()->GetName());
+ }
+ }
+
+public:
+ NoTextNodeAltTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (pCurrent->GetNodeType() & SwNodeType::NoTextMask)
+ {
+ SwNoTextNode* pNoTextNode = pCurrent->GetNoTextNode();
+ if (pNoTextNode)
+ checkNoTextNode(pNoTextNode);
+ }
+ }
+};
+
+// Check Table node if the table is merged and split.
+class TableNodeMergeSplitCheck : public NodeCheck
+{
+private:
+ void addTableIssue(SwTable const& rTable, SwDoc& rDoc)
+ {
+ const SwTableFormat* pFormat = rTable.GetFrameFormat();
+ OUString sName = pFormat->GetName();
+ OUString sIssueText = SwResId(STR_TABLE_MERGE_SPLIT).replaceAll("%OBJECT_NAME%", sName);
+ auto pIssue = lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::TABLE_MERGE_SPLIT);
+ pIssue->setDoc(rDoc);
+ pIssue->setIssueObject(IssueObject::TABLE);
+ pIssue->setObjectID(sName);
+ }
+
+ void checkTableNode(SwTableNode* pTableNode)
+ {
+ if (!pTableNode)
+ return;
+
+ SwTable const& rTable = pTableNode->GetTable();
+ SwDoc& rDoc = pTableNode->GetDoc();
+ if (rTable.IsTableComplex())
+ {
+ addTableIssue(rTable, rDoc);
+ }
+ else
+ {
+ if (rTable.GetTabLines().size() > 1)
+ {
+ int i = 0;
+ size_t nFirstLineSize = 0;
+ bool bAllColumnsSameSize = true;
+ bool bCellSpansOverMoreRows = false;
+
+ for (SwTableLine const* pTableLine : rTable.GetTabLines())
+ {
+ if (i == 0)
+ {
+ nFirstLineSize = pTableLine->GetTabBoxes().size();
+ }
+ else
+ {
+ size_t nLineSize = pTableLine->GetTabBoxes().size();
+ if (nFirstLineSize != nLineSize)
+ {
+ bAllColumnsSameSize = false;
+ }
+ }
+ i++;
+
+ // Check for row span in each table box (cell)
+ for (SwTableBox const* pBox : pTableLine->GetTabBoxes())
+ {
+ if (pBox->getRowSpan() > 1)
+ bCellSpansOverMoreRows = true;
+ }
+ }
+ if (!bAllColumnsSameSize || bCellSpansOverMoreRows)
+ {
+ addTableIssue(rTable, rDoc);
+ }
+ }
+ }
+ }
+
+public:
+ TableNodeMergeSplitCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (pCurrent->GetNodeType() & SwNodeType::Table)
+ {
+ SwTableNode* pTableNode = pCurrent->GetTableNode();
+ if (pTableNode)
+ checkTableNode(pTableNode);
+ }
+ }
+};
+
+class NumberingCheck : public NodeCheck
+{
+private:
+ SwTextNode* m_pPreviousTextNode;
+
+ const std::vector<std::pair<OUString, OUString>> m_aNumberingCombinations{
+ { "1.", "2." }, { "(1)", "(2)" }, { "1)", "2)" }, { "a.", "b." }, { "(a)", "(b)" },
+ { "a)", "b)" }, { "A.", "B." }, { "(A)", "(B)" }, { "A)", "B)" }
+ };
+
+public:
+ NumberingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ , m_pPreviousTextNode(nullptr)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ if (m_pPreviousTextNode)
+ {
+ for (auto& rPair : m_aNumberingCombinations)
+ {
+ if (pCurrent->GetTextNode()->GetText().startsWith(rPair.second)
+ && m_pPreviousTextNode->GetText().startsWith(rPair.first))
+ {
+ OUString sNumbering = rPair.first + " " + rPair.second + "...";
+ OUString sIssueText
+ = SwResId(STR_FAKE_NUMBERING).replaceAll("%NUMBERING%", sNumbering);
+ lclAddIssue(m_rIssueCollection, sIssueText);
+ }
+ }
+ }
+ m_pPreviousTextNode = pCurrent->GetTextNode();
+ }
+};
+
+class HyperlinkCheck : public NodeCheck
+{
+private:
+ void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
+ {
+ uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
+ if (!xProperties->getPropertySetInfo()->hasPropertyByName("HyperLinkURL"))
+ return;
+
+ OUString sHyperlink;
+ xProperties->getPropertyValue("HyperLinkURL") >>= sHyperlink;
+ if (!sHyperlink.isEmpty())
+ {
+ OUString sText = xTextRange->getString();
+ if (INetURLObject(sText) == INetURLObject(sHyperlink))
+ {
+ OUString sIssueText
+ = SwResId(STR_HYPERLINK_TEXT_IS_LINK).replaceFirst("%LINK%", sHyperlink);
+ lclAddIssue(m_rIssueCollection, sIssueText);
+ }
+ }
+ }
+
+public:
+ HyperlinkCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ uno::Reference<text::XTextContent> xParagraph
+ = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
+ if (!xParagraph.is())
+ return;
+
+ uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun.is())
+ {
+ checkTextRange(xRun);
+ }
+ }
+ }
+};
+
+// Based on https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
+double calculateRelativeLuminance(Color const& rColor)
+{
+ // Convert to BColor which has R, G, B colors components
+ // represented by a floating point number from [0.0, 1.0]
+ const basegfx::BColor aBColor = rColor.getBColor();
+
+ double r = aBColor.getRed();
+ double g = aBColor.getGreen();
+ double b = aBColor.getBlue();
+
+ // Calculate the values according to the described algorithm
+ r = (r <= 0.03928) ? r / 12.92 : std::pow((r + 0.055) / 1.055, 2.4);
+ g = (g <= 0.03928) ? g / 12.92 : std::pow((g + 0.055) / 1.055, 2.4);
+ b = (b <= 0.03928) ? b / 12.92 : std::pow((b + 0.055) / 1.055, 2.4);
+
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
+}
+
+// TODO move to common color tools (BColorTools maybe)
+// Based on https://www.w3.org/TR/WCAG21/#dfn-contrast-ratio
+double calculateContrastRatio(Color const& rColor1, Color const& rColor2)
+{
+ const double fLuminance1 = calculateRelativeLuminance(rColor1);
+ const double fLuminance2 = calculateRelativeLuminance(rColor2);
+ const std::pair<const double, const double> aMinMax = std::minmax(fLuminance1, fLuminance2);
+
+ // (L1 + 0.05) / (L2 + 0.05)
+ // L1 is the lighter color (greater luminance value)
+ // L2 is the darker color (smaller luminance value)
+ return (aMinMax.second + 0.05) / (aMinMax.first + 0.05);
+}
+
+class TextContrastCheck : public NodeCheck
+{
+private:
+ void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange,
+ uno::Reference<text::XTextContent> const& xParagraph,
+ const SwTextNode* pTextNode)
+ {
+ Color nParaBackColor(COL_AUTO);
+ uno::Reference<beans::XPropertySet> xParagraphProperties(xParagraph, uno::UNO_QUERY);
+ if (!(xParagraphProperties->getPropertyValue("ParaBackColor") >>= nParaBackColor))
+ {
+ SAL_WARN("sw.a11y", "ParaBackColor void");
+ return;
+ }
+
+ uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
+ if (!xProperties.is())
+ return;
+
+ // Foreground color
+ sal_Int32 nCharColor = {}; // spurious -Werror=maybe-uninitialized
+ if (!(xProperties->getPropertyValue("CharColor") >>= nCharColor))
+ { // not sure this is impossible, can the default be void?
+ SAL_WARN("sw.a11y", "CharColor void");
+ return;
+ }
+ Color aForegroundColor(ColorTransparency, nCharColor);
+ if (aForegroundColor == COL_AUTO)
+ return;
+
+ const SwPageDesc* pPageDescription = pTextNode->FindPageDesc();
+ const SwFrameFormat& rPageFormat = pPageDescription->GetMaster();
+ const SwAttrSet& rPageSet = rPageFormat.GetAttrSet();
+
+ const XFillStyleItem* pXFillStyleItem(
+ rPageSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
+ Color aPageBackground(COL_AUTO);
+
+ if (pXFillStyleItem && pXFillStyleItem->GetValue() == css::drawing::FillStyle_SOLID)
+ {
+ const XFillColorItem* rXFillColorItem
+ = rPageSet.GetItem<XFillColorItem>(XATTR_FILLCOLOR, false);
+ aPageBackground = rXFillColorItem->GetColorValue();
+ }
+
+ Color nCharBackColor(COL_AUTO);
+
+ if (!(xProperties->getPropertyValue("CharBackColor") >>= nCharBackColor))
+ {
+ SAL_WARN("sw.a11y", "CharBackColor void");
+ return;
+ }
+ // Determine the background color
+ // Try Character background (highlight)
+ Color aBackgroundColor(nCharBackColor);
+
+ // If not character background color, try paragraph background color
+ if (aBackgroundColor == COL_AUTO)
+ aBackgroundColor = nParaBackColor;
+
+ // If not paragraph background color, try page color
+ if (aBackgroundColor == COL_AUTO)
+ aBackgroundColor = aPageBackground;
+
+ // If not page color, assume white background color
+ if (aBackgroundColor == COL_AUTO)
+ aBackgroundColor = COL_WHITE;
+
+ double fContrastRatio = calculateContrastRatio(aForegroundColor, aBackgroundColor);
+ if (fContrastRatio < 4.5)
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_CONTRAST));
+ }
+ }
+
+public:
+ TextContrastCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ uno::Reference<text::XTextContent> xParagraph;
+ xParagraph = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
+ if (!xParagraph.is())
+ return;
+
+ uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun.is())
+ checkTextRange(xRun, xParagraph, pTextNode);
+ }
+ }
+};
+
+class TextFormattingCheck : public NodeCheck
+{
+private:
+public:
+ TextFormattingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void checkAutoFormat(SwTextNode* pTextNode, const SwTextAttr* pTextAttr)
+ {
+ const SwFormatAutoFormat& rAutoFormat = pTextAttr->GetAutoFormat();
+ SfxItemIter aItemIter(*rAutoFormat.GetStyleHandle());
+ const SfxPoolItem* pItem = aItemIter.GetCurItem();
+ std::vector<OUString> aFormattings;
+ while (pItem)
+ {
+ OUString sFormattingType;
+ switch (pItem->Which())
+ {
+ case RES_CHRATR_WEIGHT:
+ case RES_CHRATR_CJK_WEIGHT:
+ case RES_CHRATR_CTL_WEIGHT:
+ sFormattingType = "Weight";
+ break;
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CTL_POSTURE:
+ sFormattingType = "Posture";
+ break;
+
+ case RES_CHRATR_SHADOWED:
+ sFormattingType = "Shadowed";
+ break;
+
+ case RES_CHRATR_COLOR:
+ sFormattingType = "Font Color";
+ break;
+
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CTL_FONTSIZE:
+ sFormattingType = "Font Size";
+ break;
+
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CTL_FONT:
+ sFormattingType = "Font";
+ break;
+
+ case RES_CHRATR_EMPHASIS_MARK:
+ sFormattingType = "Emphasis Mark";
+ break;
+
+ case RES_CHRATR_UNDERLINE:
+ sFormattingType = "Underline";
+ break;
+
+ case RES_CHRATR_OVERLINE:
+ sFormattingType = "Overline";
+ break;
+
+ case RES_CHRATR_CROSSEDOUT:
+ sFormattingType = "Strikethrough";
+ break;
+
+ case RES_CHRATR_RELIEF:
+ sFormattingType = "Relief";
+ break;
+
+ case RES_CHRATR_CONTOUR:
+ sFormattingType = "Outline";
+ break;
+ default:
+ break;
+ }
+ if (!sFormattingType.isEmpty())
+ aFormattings.push_back(sFormattingType);
+ pItem = aItemIter.NextItem();
+ }
+ if (aFormattings.empty())
+ return;
+
+ o3tl::remove_duplicates(aFormattings);
+ auto pIssue = lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_FORMATTING_CONVEYS_MEANING),
+ sfx::AccessibilityIssueID::TEXT_FORMATTING);
+ pIssue->setIssueObject(IssueObject::TEXT);
+ pIssue->setNode(pTextNode);
+ SwDoc& rDocument = pTextNode->GetDoc();
+ pIssue->setDoc(rDocument);
+ pIssue->setStart(pTextAttr->GetStart());
+ pIssue->setEnd(pTextAttr->GetAnyEnd());
+ }
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (pTextNode->HasHints())
+ {
+ SwpHints& rHints = pTextNode->GetSwpHints();
+ for (size_t i = 0; i < rHints.Count(); ++i)
+ {
+ const SwTextAttr* pTextAttr = rHints.Get(i);
+ if (pTextAttr->Which() == RES_TXTATR_AUTOFMT)
+ {
+ checkAutoFormat(pTextNode, pTextAttr);
+ }
+ }
+ }
+ }
+};
+
+class BlinkingTextCheck : public NodeCheck
+{
+private:
+ void checkTextRange(uno::Reference<text::XTextRange> const& xTextRange)
+ {
+ uno::Reference<beans::XPropertySet> xProperties(xTextRange, uno::UNO_QUERY);
+ if (xProperties.is() && xProperties->getPropertySetInfo()->hasPropertyByName("CharFlash"))
+ {
+ bool bBlinking = false;
+ xProperties->getPropertyValue("CharFlash") >>= bBlinking;
+
+ if (bBlinking)
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_TEXT_BLINKING));
+ }
+ }
+ }
+
+public:
+ BlinkingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ uno::Reference<text::XTextContent> xParagraph;
+ xParagraph = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
+ if (!xParagraph.is())
+ return;
+
+ uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
+ while (xRunEnum->hasMoreElements())
+ {
+ uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
+ if (xRun.is())
+ checkTextRange(xRun);
+ }
+ }
+};
+
+class HeaderCheck : public NodeCheck
+{
+private:
+ int m_nPreviousLevel;
+
+public:
+ HeaderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ , m_nPreviousLevel(0)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ SwTextNode* pTextNode = pCurrent->GetTextNode();
+ SwTextFormatColl* pCollection = pTextNode->GetTextColl();
+ if (!pCollection->IsAssignedToListLevelOfOutlineStyle())
+ return;
+
+ int nLevel = pCollection->GetAssignedOutlineStyleLevel();
+ assert(nLevel >= 0);
+ if (nLevel > m_nPreviousLevel && std::abs(nLevel - m_nPreviousLevel) > 1)
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_HEADINGS_NOT_IN_ORDER));
+ }
+ m_nPreviousLevel = nLevel;
+ }
+};
+
+// ISO 142891-1 : 7.14
+class NonInteractiveFormCheck : public NodeCheck
+{
+public:
+ NonInteractiveFormCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!pCurrent->IsTextNode())
+ return;
+
+ const auto& text = pCurrent->GetTextNode()->GetText();
+
+ // Series of tests to detect if there are fake forms in the text.
+
+ bool bCheck = text.indexOf("___") == -1; // Repeated underscores.
+
+ if (bCheck)
+ bCheck = text.indexOf("....") == -1; // Repeated dots.
+
+ if (bCheck)
+ bCheck = text.indexOf(u"……") == -1; // Repeated ellipsis.
+
+ if (bCheck)
+ bCheck = text.indexOf(u"….") == -1; // A dot after an ellipsis.
+
+ if (bCheck)
+ bCheck = text.indexOf(u".…") == -1; // An ellipsis after a dot.
+
+ // Checking if all the tests are passed successfully. If not, adding a warning.
+ if (!bCheck)
+ lclAddIssue(m_rIssueCollection, SwResId(STR_NON_INTERACTIVE_FORMS));
+ }
+};
+
+/// Check for floating text frames, as it causes problems with reading order.
+class FloatingTextCheck : public NodeCheck
+{
+public:
+ FloatingTextCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ // if node is a text-node and if it has text, we proceed. Otherwise - return.
+ const SwTextNode* textNode = pCurrent->GetTextNode();
+ if (!textNode || textNode->GetText().isEmpty())
+ return;
+
+ // If a node is in fly and if it is not anchored as char, throw warning.
+ const SwNode* startFly = pCurrent->FindFlyStartNode();
+ if (startFly
+ && startFly->GetFlyFormat()->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ lclAddIssue(m_rIssueCollection, SwResId(STR_FLOATING_TEXT));
+ }
+};
+
+/// Heading paragraphs (with outline levels > 0) are not allowed in tables
+class TableHeadingCheck : public NodeCheck
+{
+private:
+ // Boolean indicating if heading-in-table warning is already triggered.
+ bool m_bPrevPassed;
+
+public:
+ TableHeadingCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ , m_bPrevPassed(true)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ if (!m_bPrevPassed)
+ return;
+
+ const SwTextNode* textNode = pCurrent->GetTextNode();
+
+ if (textNode && textNode->GetAttrOutlineLevel() != 0)
+ {
+ const SwTableNode* parentTable = pCurrent->FindTableNode();
+
+ if (parentTable)
+ {
+ m_bPrevPassed = false;
+ lclAddIssue(m_rIssueCollection, SwResId(STR_HEADING_IN_TABLE));
+ }
+ }
+ }
+};
+
+/// Checking if headings are ordered correctly.
+class HeadingOrderCheck : public NodeCheck
+{
+public:
+ HeadingOrderCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : NodeCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwNode* pCurrent) override
+ {
+ const SwTextNode* pTextNode = pCurrent->GetTextNode();
+ if (!pTextNode)
+ return;
+
+ // If outline level stands for heading level...
+ const int currentLevel = pTextNode->GetAttrOutlineLevel();
+ if (!currentLevel)
+ return;
+
+ // ... and if is bigger than previous by more than 1, warn.
+ if (currentLevel - m_prevLevel > 1)
+ {
+ // Preparing and posting a warning.
+ OUString resultString = SwResId(STR_HEADING_ORDER);
+ resultString
+ = resultString.replaceAll("%LEVEL_CURRENT%", OUString::number(currentLevel));
+ resultString = resultString.replaceAll("%LEVEL_PREV%", OUString::number(m_prevLevel));
+
+ lclAddIssue(m_rIssueCollection, resultString);
+ }
+
+ // Updating previous level.
+ m_prevLevel = currentLevel;
+ }
+
+private:
+ // Previous heading level to compare with.
+ int m_prevLevel = 0;
+};
+
+class DocumentCheck : public BaseCheck
+{
+public:
+ DocumentCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : BaseCheck(rIssueCollection)
+ {
+ }
+
+ virtual void check(SwDoc* pDoc) = 0;
+};
+
+// Check default language
+class DocumentDefaultLanguageCheck : public DocumentCheck
+{
+public:
+ DocumentDefaultLanguageCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwDoc* pDoc) override
+ {
+ // TODO maybe - also check RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE if CJK or CTL are enabled
+ const SvxLanguageItem& rLang = pDoc->GetDefault(RES_CHRATR_LANGUAGE);
+ LanguageType eLanguage = rLang.GetLanguage();
+ if (eLanguage == LANGUAGE_NONE)
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_DEFAULT_LANGUAGE),
+ sfx::AccessibilityIssueID::DOCUMENT_LANGUAGE);
+ }
+ else
+ {
+ for (SwTextFormatColl* pTextFormatCollection : *pDoc->GetTextFormatColls())
+ {
+ const SwAttrSet& rAttrSet = pTextFormatCollection->GetAttrSet();
+ if (rAttrSet.GetLanguage(false).GetLanguage() == LANGUAGE_NONE)
+ {
+ OUString sName = pTextFormatCollection->GetName();
+ OUString sIssueText
+ = SwResId(STR_STYLE_NO_LANGUAGE).replaceAll("%STYLE_NAME%", sName);
+ lclAddIssue(m_rIssueCollection, sIssueText,
+ sfx::AccessibilityIssueID::STYLE_LANGUAGE);
+ }
+ }
+ }
+ }
+};
+
+class DocumentTitleCheck : public DocumentCheck
+{
+public:
+ DocumentTitleCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwDoc* pDoc) override
+ {
+ SwDocShell* pShell = pDoc->GetDocShell();
+ if (!pShell)
+ return;
+
+ const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(pShell->GetModel(),
+ uno::UNO_QUERY_THROW);
+ const uno::Reference<document::XDocumentProperties> xDocumentProperties(
+ xDPS->getDocumentProperties());
+ OUString sTitle = xDocumentProperties->getTitle();
+ if (sTitle.trim().isEmpty())
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_DOCUMENT_TITLE),
+ sfx::AccessibilityIssueID::DOCUMENT_TITLE);
+ }
+ }
+};
+
+class FootnoteEndnoteCheck : public DocumentCheck
+{
+public:
+ FootnoteEndnoteCheck(sfx::AccessibilityIssueCollection& rIssueCollection)
+ : DocumentCheck(rIssueCollection)
+ {
+ }
+
+ void check(SwDoc* pDoc) override
+ {
+ for (SwTextFootnote const* pTextFootnote : pDoc->GetFootnoteIdxs())
+ {
+ SwFormatFootnote const& rFootnote = pTextFootnote->GetFootnote();
+ if (rFootnote.IsEndNote())
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_ENDNOTES));
+ }
+ else
+ {
+ lclAddIssue(m_rIssueCollection, SwResId(STR_AVOID_FOOTNOTES));
+ }
+ }
+ }
+};
+
+} // end anonymous namespace
+
+// Check Shapes, TextBox
+void AccessibilityCheck::checkObject(SdrObject* pObject)
+{
+ if (!pObject)
+ return;
+
+ // Check for fontworks.
+ if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>(pObject))
+ {
+ const SdrCustomShapeGeometryItem& rGeometryItem
+ = pCustomShape->GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY);
+
+ if (const uno::Any* pAny = rGeometryItem.GetPropertyValueByName("Type"))
+ if (pAny->get<OUString>().startsWith("fontwork-"))
+ lclAddIssue(m_aIssueCollection, SwResId(STR_FONTWORKS));
+ }
+
+ // Checking if there is floating Writer text draw object and if so, throwing a warning.
+ // (Floating objects with text create problems with reading order)
+ if (pObject->HasText()
+ && FindFrameFormat(pObject)->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ lclAddIssue(m_aIssueCollection, SwResId(STR_FLOATING_TEXT));
+
+ if (pObject->GetObjIdentifier() == SdrObjKind::CustomShape
+ || pObject->GetObjIdentifier() == SdrObjKind::Text)
+ {
+ OUString sAlternative = pObject->GetTitle();
+ if (sAlternative.isEmpty())
+ {
+ OUString sName = pObject->GetName();
+ OUString sIssueText = SwResId(STR_NO_ALT).replaceAll("%OBJECT_NAME%", sName);
+ lclAddIssue(m_aIssueCollection, sIssueText, sfx::AccessibilityIssueID::NO_ALT_SHAPE);
+ }
+ }
+}
+
+void AccessibilityCheck::check()
+{
+ if (m_pDoc == nullptr)
+ return;
+
+ std::vector<std::unique_ptr<DocumentCheck>> aDocumentChecks;
+ aDocumentChecks.push_back(std::make_unique<DocumentDefaultLanguageCheck>(m_aIssueCollection));
+ aDocumentChecks.push_back(std::make_unique<DocumentTitleCheck>(m_aIssueCollection));
+ aDocumentChecks.push_back(std::make_unique<FootnoteEndnoteCheck>(m_aIssueCollection));
+
+ for (std::unique_ptr<DocumentCheck>& rpDocumentCheck : aDocumentChecks)
+ {
+ rpDocumentCheck->check(m_pDoc);
+ }
+
+ std::vector<std::unique_ptr<NodeCheck>> aNodeChecks;
+ aNodeChecks.push_back(std::make_unique<NoTextNodeAltTextCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<TableNodeMergeSplitCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<NumberingCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<HyperlinkCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<TextContrastCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<BlinkingTextCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<HeaderCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<TextFormattingCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<NonInteractiveFormCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<FloatingTextCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<TableHeadingCheck>(m_aIssueCollection));
+ aNodeChecks.push_back(std::make_unique<HeadingOrderCheck>(m_aIssueCollection));
+
+ auto const& pNodes = m_pDoc->GetNodes();
+ SwNode* pNode = nullptr;
+ for (SwNodeOffset n(0); n < pNodes.Count(); ++n)
+ {
+ pNode = pNodes[n];
+ if (pNode)
+ {
+ for (std::unique_ptr<NodeCheck>& rpNodeCheck : aNodeChecks)
+ {
+ rpNodeCheck->check(pNode);
+ }
+ }
+ }
+
+ IDocumentDrawModelAccess& rDrawModelAccess = m_pDoc->getIDocumentDrawModelAccess();
+ auto* pModel = rDrawModelAccess.GetDrawModel();
+ for (sal_uInt16 nPage = 0; nPage < pModel->GetPageCount(); ++nPage)
+ {
+ SdrPage* pPage = pModel->GetPage(nPage);
+ for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
+ {
+ SdrObject* pObject = pPage->GetObj(nObject);
+ if (pObject)
+ checkObject(pObject);
+ }
+ }
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/AccessibilityIssue.cxx b/sw/source/core/access/AccessibilityIssue.cxx
new file mode 100644
index 000000000..f8b8e3858
--- /dev/null
+++ b/sw/source/core/access/AccessibilityIssue.cxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <AccessibilityIssue.hxx>
+#include <wrtsh.hxx>
+#include <docsh.hxx>
+#include <comphelper/lok.hxx>
+
+namespace sw
+{
+AccessibilityIssue::AccessibilityIssue(sfx::AccessibilityIssueID eIssueID)
+ : sfx::AccessibilityIssue(eIssueID)
+ , m_eIssueObject(IssueObject::UNKNOWN)
+ , m_pDoc(nullptr)
+ , m_pNode(nullptr)
+ , m_nStart(0)
+ , m_nEnd(0)
+{
+}
+
+void AccessibilityIssue::setIssueObject(IssueObject eIssueObject) { m_eIssueObject = eIssueObject; }
+
+void AccessibilityIssue::setDoc(SwDoc& rDoc) { m_pDoc = &rDoc; }
+
+void AccessibilityIssue::setObjectID(OUString const& rID) { m_sObjectID = rID; }
+
+bool AccessibilityIssue::canGotoIssue() const
+{
+ if (m_eIssueObject != IssueObject::UNKNOWN)
+ return true;
+ return false;
+}
+
+void AccessibilityIssue::gotoIssue() const
+{
+ if (!m_pDoc)
+ return;
+
+ switch (m_eIssueObject)
+ {
+ case IssueObject::GRAPHIC:
+ case IssueObject::OLE:
+ {
+ SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoFly(m_sObjectID, FLYCNTTYPE_ALL, true);
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ case IssueObject::TABLE:
+ {
+ SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoTable(m_sObjectID);
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ case IssueObject::TEXT:
+ {
+ SwWrtShell* pWrtShell = m_pDoc->GetDocShell()->GetWrtShell();
+ SwContentNode* pContentNode = m_pNode->GetContentNode();
+ SwPosition aPoint(*pContentNode, m_nStart);
+ SwPosition aMark(*pContentNode, m_nEnd);
+ pWrtShell->EnterStdMode();
+ pWrtShell->StartAllAction();
+ SwPaM* pPaM = pWrtShell->GetCursor();
+ *pPaM->GetPoint() = aPoint;
+ pPaM->SetMark();
+ *pPaM->GetMark() = aMark;
+ pWrtShell->EndAllAction();
+ if (comphelper::LibreOfficeKit::isActive())
+ pWrtShell->ShowCursor();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccell.cxx b/sw/source/core/access/acccell.cxx
new file mode 100644
index 000000000..546c0457c
--- /dev/null
+++ b/sw/source/core/access/acccell.cxx
@@ -0,0 +1,472 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/log.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <swtable.hxx>
+#include <crsrsh.hxx>
+#include <viscrs.hxx>
+#include "accfrmobj.hxx"
+#include "accfrmobjslist.hxx"
+#include <frmfmt.hxx>
+#include <cellatr.hxx>
+#include <accmap.hxx>
+#include "acccell.hxx"
+
+#include <cfloat>
+#include <string_view>
+
+#include <editeng/brushitem.hxx>
+#include <swatrset.hxx>
+#include <frmatr.hxx>
+#include "acctable.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace sw::access;
+
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleCellView";
+
+bool SwAccessibleCell::IsSelected()
+{
+ bool bRet = false;
+
+ assert(GetMap());
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+ if( auto pCSh = dynamic_cast<const SwCursorShell*>(pVSh) )
+ {
+ if( pCSh->IsTableMode() )
+ {
+ const SwCellFrame *pCFrame =
+ static_cast< const SwCellFrame * >( GetFrame() );
+ SwTableBox *pBox =
+ const_cast< SwTableBox *>( pCFrame->GetTabBox() );
+ SwSelBoxes const& rBoxes(pCSh->GetTableCursor()->GetSelectedBoxes());
+ bRet = rBoxes.find(pBox) != rBoxes.end();
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleCell::GetStates( ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // SELECTABLE
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+ if( dynamic_cast<const SwCursorShell*>( pVSh) != nullptr )
+ rStateSet.AddState( AccessibleStateType::SELECTABLE );
+ //Add resizable state to table cell.
+ rStateSet.AddState( AccessibleStateType::RESIZABLE );
+
+ if (IsDisposing()) // tdf#135098
+ return;
+
+ // SELECTED
+ if( IsSelected() )
+ {
+ rStateSet.AddState( AccessibleStateType::SELECTED );
+ SAL_WARN_IF(!m_bIsSelected, "sw.a11y", "bSelected out of sync");
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+}
+
+SwAccessibleCell::SwAccessibleCell(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwCellFrame *pCellFrame )
+ : SwAccessibleContext( pInitMap, AccessibleRole::TABLE_CELL, pCellFrame )
+ , m_aSelectionHelper( *this )
+ , m_bIsSelected( false )
+{
+ OUString sBoxName( pCellFrame->GetTabBox()->GetName() );
+ SetName( sBoxName );
+
+ m_bIsSelected = IsSelected();
+
+ css::uno::Reference<css::accessibility::XAccessible> xTableReference(
+ getAccessibleParentImpl());
+ css::uno::Reference<css::accessibility::XAccessibleContext> xContextTable(
+ xTableReference, css::uno::UNO_QUERY);
+ SAL_WARN_IF(
+ (!xContextTable.is()
+ || xContextTable->getAccessibleRole() != AccessibleRole::TABLE),
+ "sw.a11y", "bad accessible context");
+ m_pAccTable = static_cast<SwAccessibleTable *>(xTableReference.get());
+}
+
+bool SwAccessibleCell::InvalidateMyCursorPos()
+{
+ bool bNew = IsSelected();
+ bool bOld;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOld = m_bIsSelected;
+ m_bIsSelected = bNew;
+ }
+ if( bNew )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ bool bChanged = bOld != bNew;
+ if( bChanged )
+ {
+ FireStateChangedEvent( AccessibleStateType::SELECTED, bNew );
+ if (m_pAccTable.is())
+ {
+ m_pAccTable->AddSelectionCell(this,bNew);
+ }
+ }
+ return bChanged;
+}
+
+bool SwAccessibleCell::InvalidateChildrenCursorPos( const SwFrame *pFrame )
+{
+ bool bChanged = false;
+
+ const SwAccessibleChildSList aVisList( GetVisArea(), *pFrame, *GetMap() );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ if( rLower.IsAccessible( GetMap()->GetShell()->IsPreview() ) )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl( pLower, false ) );
+ if( xAccImpl.is() )
+ {
+ assert(xAccImpl->GetFrame()->IsCellFrame());
+ bChanged = static_cast< SwAccessibleCell *>(
+ xAccImpl.get() )->InvalidateMyCursorPos();
+ }
+ else
+ bChanged = true; // If the context is not know we
+ // don't know whether the selection
+ // changed or not.
+ }
+ else
+ {
+ // This is a box with sub rows.
+ bChanged |= InvalidateChildrenCursorPos( pLower );
+ }
+ }
+ ++aIter;
+ }
+
+ return bChanged;
+}
+
+void SwAccessibleCell::InvalidateCursorPos_()
+{
+ if (IsSelected())
+ {
+ const SwAccessibleChild aChild( GetChild( *(GetMap()), 0 ) );
+ if( aChild.IsValid() && aChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl( GetMap()->GetContextImpl( aChild.GetSwFrame()) );
+ if (xChildImpl.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aEvent.NewValue <<= AccessibleStateType::FOCUSED;
+ xChildImpl->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+
+ const SwFrame *pParent = GetParent( SwAccessibleChild(GetFrame()), IsInPagePreview() );
+ assert(pParent->IsTabFrame());
+ const SwTabFrame *pTabFrame = static_cast< const SwTabFrame * >( pParent );
+ if( pTabFrame->IsFollow() )
+ pTabFrame = pTabFrame->FindMaster();
+
+ while( pTabFrame )
+ {
+ InvalidateChildrenCursorPos( pTabFrame );
+ pTabFrame = pTabFrame->GetFollow();
+ }
+ if (m_pAccTable.is())
+ {
+ m_pAccTable->FireSelectionEvent();
+ }
+}
+
+bool SwAccessibleCell::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_bIsSelected;
+}
+
+SwAccessibleCell::~SwAccessibleCell()
+{
+}
+
+OUString SAL_CALL SwAccessibleCell::getAccessibleDescription()
+{
+ return GetName();
+}
+
+OUString SAL_CALL SwAccessibleCell::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleCell::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleCell::getSupportedServiceNames()
+{
+ return { "com.sun.star.table.AccessibleCellView", sAccessibleServiceName };
+}
+
+void SwAccessibleCell::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ const SwFrame *pParent = GetParent( SwAccessibleChild(GetFrame()), IsInPagePreview() );
+ ::rtl::Reference< SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl( pParent, false ) );
+ if( xAccImpl.is() )
+ xAccImpl->DisposeChild(SwAccessibleChild(GetFrame()), bRecursive, bCanSkipInvisible);
+ SwAccessibleContext::Dispose( bRecursive );
+}
+
+void SwAccessibleCell::InvalidatePosOrSize( const SwRect& rOldBox )
+{
+ const SwFrame *pParent = GetParent( SwAccessibleChild(GetFrame()), IsInPagePreview() );
+ ::rtl::Reference< SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl( pParent, false ) );
+ if( xAccImpl.is() )
+ xAccImpl->InvalidateChildPosOrSize( SwAccessibleChild(GetFrame()), rOldBox );
+ SwAccessibleContext::InvalidatePosOrSize( rOldBox );
+}
+
+// XAccessibleInterface
+
+uno::Any SwAccessibleCell::queryInterface( const uno::Type& rType )
+{
+ if (rType == cppu::UnoType<XAccessibleExtendedAttributes>::get())
+ {
+ uno::Any aR;
+ aR <<= uno::Reference<XAccessibleExtendedAttributes>(this);
+ return aR;
+ }
+
+ if (rType == cppu::UnoType<XAccessibleSelection>::get())
+ {
+ uno::Any aR;
+ aR <<= uno::Reference<XAccessibleSelection>(this);
+ return aR;
+ }
+ if ( rType == ::cppu::UnoType<XAccessibleValue>::get() )
+ {
+ uno::Reference<XAccessibleValue> xValue = this;
+ uno::Any aRet;
+ aRet <<= xValue;
+ return aRet;
+ }
+ else
+ {
+ return SwAccessibleContext::queryInterface( rType );
+ }
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleCell::getTypes()
+{
+ return cppu::OTypeCollection(
+ ::cppu::UnoType<XAccessibleValue>::get(),
+ SwAccessibleContext::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleCell::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleValue
+
+SwFrameFormat* SwAccessibleCell::GetTableBoxFormat() const
+{
+ assert(GetFrame());
+ assert(GetFrame()->IsCellFrame());
+
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>( GetFrame() );
+ return pCellFrame->GetTabBox()->GetFrameFormat();
+}
+
+//Implement TableCell currentValue
+uno::Any SwAccessibleCell::getCurrentValue( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return uno::Any( GetTableBoxFormat()->GetTableBoxValue().GetValue() );
+}
+
+sal_Bool SwAccessibleCell::setCurrentValue( const uno::Any& aNumber )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ double fValue = 0;
+ bool bValid = (aNumber >>= fValue);
+ if( bValid )
+ {
+ SwTableBoxValue aValue( fValue );
+ GetTableBoxFormat()->SetFormatAttr( aValue );
+ }
+ return bValid;
+}
+
+uno::Any SwAccessibleCell::getMaximumValue( )
+{
+ return uno::Any(DBL_MAX);
+}
+
+uno::Any SwAccessibleCell::getMinimumValue( )
+{
+ return uno::Any(-DBL_MAX);
+}
+
+uno::Any SwAccessibleCell::getMinimumIncrement( )
+{
+ return uno::Any();
+}
+
+static OUString ReplaceOneChar(const OUString& oldOUString, std::u16string_view replacedChar, std::u16string_view replaceStr)
+{
+ int iReplace = oldOUString.lastIndexOf(replacedChar);
+ OUString aRet = oldOUString;
+ while(iReplace > -1)
+ {
+ aRet = aRet.replaceAt(iReplace,1, replaceStr);
+ iReplace = aRet.lastIndexOf(replacedChar,iReplace);
+ }
+ return aRet;
+}
+
+static OUString ReplaceFourChar(const OUString& oldOUString)
+{
+ OUString aRet = ReplaceOneChar(oldOUString, u"\\", u"\\\\");
+ aRet = ReplaceOneChar(aRet, u";", u"\\;");
+ aRet = ReplaceOneChar(aRet, u"=", u"\\=");
+ aRet = ReplaceOneChar(aRet, u",", u"\\,");
+ aRet = ReplaceOneChar(aRet, u":", u"\\:");
+ return aRet;
+}
+
+css::uno::Any SAL_CALL SwAccessibleCell::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ css::uno::Any strRet;
+ SwFrameFormat *pFrameFormat = GetTableBoxFormat();
+ assert(pFrameFormat);
+
+ const SwTableBoxFormula& tbl_formula = pFrameFormat->GetTableBoxFormula();
+
+ OUString strFormula = ReplaceFourChar(tbl_formula.GetFormula());
+ OUString strFor = "Formula:" + strFormula + ";";
+ strRet <<= strFor;
+
+ return strRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleCell::getBackground()
+{
+ SolarMutexGuard g;
+
+ const SvxBrushItem &rBack = GetFrame()->GetAttrSet()->GetBackground();
+ Color crBack = rBack.GetColor();
+
+ if (COL_AUTO == crBack)
+ {
+ uno::Reference<XAccessible> xAccDoc = getAccessibleParent();
+ if (xAccDoc.is())
+ {
+ uno::Reference<XAccessibleComponent> xComponentDoc(xAccDoc, uno::UNO_QUERY);
+ if (xComponentDoc.is())
+ {
+ crBack = Color(ColorTransparency, xComponentDoc->getBackground());
+ }
+ }
+ }
+ return sal_Int32(crBack);
+}
+
+// XAccessibleSelection
+void SwAccessibleCell::selectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ m_aSelectionHelper.selectAccessibleChild(nChildIndex);
+}
+
+sal_Bool SwAccessibleCell::isAccessibleChildSelected(
+ sal_Int32 nChildIndex )
+{
+ return m_aSelectionHelper.isAccessibleChildSelected(nChildIndex);
+}
+
+void SwAccessibleCell::clearAccessibleSelection( )
+{
+}
+
+void SwAccessibleCell::selectAllAccessibleChildren( )
+{
+ m_aSelectionHelper.selectAllAccessibleChildren();
+}
+
+sal_Int32 SwAccessibleCell::getSelectedAccessibleChildCount( )
+{
+ return m_aSelectionHelper.getSelectedAccessibleChildCount();
+}
+
+uno::Reference<XAccessible> SwAccessibleCell::getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex )
+{
+ return m_aSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
+}
+
+void SwAccessibleCell::deselectAccessibleChild(
+ sal_Int32 nSelectedChildIndex )
+{
+ m_aSelectionHelper.deselectAccessibleChild(nSelectedChildIndex);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccell.hxx b/sw/source/core/access/acccell.hxx
new file mode 100644
index 000000000..b000d4958
--- /dev/null
+++ b/sw/source/core/access/acccell.hxx
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "acccontext.hxx"
+#include <com/sun/star/accessibility/XAccessibleValue.hpp>
+#include "accselectionhelper.hxx"
+
+class SwCellFrame;
+class SwAccessibleTable;
+class SwFrameFormat;
+
+class SwAccessibleCell : public SwAccessibleContext,
+ public css::accessibility::XAccessibleValue,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleExtendedAttributes
+{
+ // Implementation for XAccessibleSelection interface
+ SwAccessibleSelectionHelper m_aSelectionHelper;
+ bool m_bIsSelected; // protected by base class mutex
+
+ bool IsSelected();
+
+ bool InvalidateMyCursorPos();
+ bool InvalidateChildrenCursorPos( const SwFrame *pFrame );
+
+ rtl::Reference<SwAccessibleTable> m_pAccTable;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets SELECTABLE(1) and SELECTED(+)
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet ) override;
+
+ virtual void InvalidateCursorPos_() override;
+
+ virtual ~SwAccessibleCell() override;
+
+public:
+ SwAccessibleCell(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwCellFrame *pCellFrame);
+
+ virtual bool HasCursor() override; // required by map to remember that object
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ virtual void InvalidatePosOrSize( const SwRect& rFrame ) override;
+
+ // XInterface
+
+ // (XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleValue).
+
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleExtendedAttributes
+ css::uno::Any SAL_CALL getExtendedAttributes() override ;
+private:
+ SwFrameFormat* GetTableBoxFormat() const;
+
+public:
+ // XAccessibleValue
+ virtual css::uno::Any SAL_CALL getCurrentValue( ) override;
+ virtual sal_Bool SAL_CALL setCurrentValue( const css::uno::Any& aNumber ) override;
+ virtual css::uno::Any SAL_CALL getMaximumValue( ) override;
+ virtual css::uno::Any SAL_CALL getMinimumValue( ) override;
+ virtual css::uno::Any SAL_CALL getMinimumIncrement( ) override;
+
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild( sal_Int32 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected( sal_Int32 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int32 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccontext.cxx b/sw/source/core/access/acccontext.cxx
new file mode 100644
index 000000000..601e05a98
--- /dev/null
+++ b/sw/source/core/access/acccontext.cxx
@@ -0,0 +1,1519 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/window.hxx>
+#include <swtypes.hxx>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <viewsh.hxx>
+#include <crsrsh.hxx>
+#include <fesh.hxx>
+#include <wrtsh.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <pagefrm.hxx>
+#include <flyfrm.hxx>
+#include <dflyobj.hxx>
+#include <pam.hxx>
+#include <accmap.hxx>
+#include "accfrmobjslist.hxx"
+#include "acccontext.hxx"
+#include <svx/AccessibleShape.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <PostItMgr.hxx>
+
+using namespace sw::access;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+void SwAccessibleContext::InitStates()
+{
+ m_isShowingState = GetMap() && IsShowing( *(GetMap()) );
+
+ SwViewShell *pVSh = GetMap()->GetShell();
+ m_isEditableState = pVSh && IsEditable( pVSh );
+ m_isOpaqueState = pVSh && IsOpaque( pVSh );
+ m_isDefuncState = false;
+}
+
+void SwAccessibleContext::SetParent( SwAccessibleContext *pParent )
+{
+ std::scoped_lock aGuard( m_Mutex );
+
+ uno::Reference < XAccessible > xParent( pParent );
+ m_xWeakParent = xParent;
+}
+
+uno::Reference< XAccessible > SwAccessibleContext::GetWeakParent() const
+{
+ std::scoped_lock aGuard( m_Mutex );
+
+ uno::Reference< XAccessible > xParent( m_xWeakParent );
+ return xParent;
+}
+
+vcl::Window *SwAccessibleContext::GetWindow()
+{
+ vcl::Window *pWin = nullptr;
+
+ if( GetMap() )
+ {
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ OSL_ENSURE( pVSh, "no view shell" );
+ if( pVSh )
+ pWin = pVSh->GetWin();
+
+ OSL_ENSURE( pWin, "no window" );
+ }
+
+ return pWin;
+}
+
+// get SwViewShell from accessibility map, and cast to cursor shell
+SwCursorShell* SwAccessibleContext::GetCursorShell()
+{
+ SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
+ OSL_ENSURE( pViewShell, "no view shell" );
+ return dynamic_cast<SwCursorShell*>( pViewShell);
+}
+
+const SwCursorShell* SwAccessibleContext::GetCursorShell() const
+{
+ // just like non-const GetCursorShell
+ const SwViewShell* pViewShell = GetMap() ? GetMap()->GetShell() : nullptr;
+ OSL_ENSURE( pViewShell, "no view shell" );
+ return dynamic_cast<const SwCursorShell*>( pViewShell);
+}
+
+namespace {
+
+enum class Action { NONE, SCROLLED, SCROLLED_WITHIN,
+ SCROLLED_IN, SCROLLED_OUT };
+
+}
+
+void SwAccessibleContext::ChildrenScrolled( const SwFrame *pFrame,
+ const SwRect& rOldVisArea )
+{
+ const SwRect& rNewVisArea = GetVisArea();
+ const bool bVisibleChildrenOnly = SwAccessibleChild( pFrame ).IsVisibleChildrenOnly();
+
+ const SwAccessibleChildSList aList( *pFrame, *(GetMap()) );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ while( aIter != aList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwRect aBox( rLower.GetBox( *(GetMap()) ) );
+ if( rLower.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ Action eAction = Action::NONE;
+ if( aBox.Overlaps( rNewVisArea ) )
+ {
+ if( aBox.Overlaps( rOldVisArea ) )
+ {
+ eAction = Action::SCROLLED_WITHIN;
+ }
+ else
+ {
+ if ( bVisibleChildrenOnly &&
+ !rLower.AlwaysIncludeAsChild() )
+ {
+ eAction = Action::SCROLLED_IN;
+ }
+ else
+ {
+ eAction = Action::SCROLLED;
+ }
+ }
+ }
+ else if( aBox.Overlaps( rOldVisArea ) )
+ {
+ if ( bVisibleChildrenOnly &&
+ !rLower.AlwaysIncludeAsChild() )
+ {
+ eAction = Action::SCROLLED_OUT;
+ }
+ else
+ {
+ eAction = Action::SCROLLED;
+ }
+ }
+ else if( !bVisibleChildrenOnly ||
+ rLower.AlwaysIncludeAsChild() )
+ {
+ // This wouldn't be required if the SwAccessibleFrame,
+ // wouldn't know about the visible area.
+ eAction = Action::SCROLLED;
+ }
+ if( Action::NONE != eAction )
+ {
+ if ( rLower.GetSwFrame() )
+ {
+ OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
+ "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
+ const SwFrame* pLower( rLower.GetSwFrame() );
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( pLower );
+ if( xAccImpl.is() )
+ {
+ switch( eAction )
+ {
+ case Action::SCROLLED:
+ xAccImpl->Scrolled( rOldVisArea );
+ break;
+ case Action::SCROLLED_WITHIN:
+ xAccImpl->ScrolledWithin( rOldVisArea );
+ break;
+ case Action::SCROLLED_IN:
+ xAccImpl->ScrolledIn();
+ break;
+ case Action::SCROLLED_OUT:
+ xAccImpl->ScrolledOut( rOldVisArea );
+ break;
+ case Action::NONE:
+ break;
+ }
+ }
+ else
+ {
+ ChildrenScrolled( pLower, rOldVisArea );
+ }
+ }
+ else if ( rLower.GetDrawObject() )
+ {
+ OSL_ENSURE( !rLower.AlwaysIncludeAsChild(),
+ "<SwAccessibleContext::ChildrenScrolled(..)> - always included child not considered!" );
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rLower.GetDrawObject(),
+ this );
+ if( xAccImpl.is() )
+ {
+ switch( eAction )
+ {
+ case Action::SCROLLED:
+ case Action::SCROLLED_WITHIN:
+ xAccImpl->ViewForwarderChanged();
+ break;
+ case Action::SCROLLED_IN:
+ ScrolledInShape( xAccImpl.get() );
+ break;
+ case Action::SCROLLED_OUT:
+ {
+ xAccImpl->ViewForwarderChanged();
+ // this DisposeShape call was removed by
+ // IAccessibility2 implementation
+ // without giving any reason why
+ DisposeShape( rLower.GetDrawObject(),
+ xAccImpl.get() );
+ }
+ break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case Action::NONE:
+ break;
+ }
+ }
+ }
+ else if ( rLower.GetWindow() )
+ {
+ // nothing to do - as such children are always included as children.
+ OSL_ENSURE( rLower.AlwaysIncludeAsChild(),
+ "<SwAccessibleContext::ChildrenScrolled(..)> - not always included child not considered!" );
+ }
+ }
+ }
+ else if ( rLower.GetSwFrame() &&
+ ( !bVisibleChildrenOnly ||
+ aBox.Overlaps( rOldVisArea ) ||
+ aBox.Overlaps( rNewVisArea ) ) )
+ {
+ // There are no unaccessible SdrObjects that need to be notified
+ ChildrenScrolled( rLower.GetSwFrame(), rOldVisArea );
+ }
+ ++aIter;
+ }
+}
+
+void SwAccessibleContext::Scrolled( const SwRect& rOldVisArea )
+{
+ SetVisArea( GetMap()->GetVisArea() );
+
+ ChildrenScrolled( GetFrame(), rOldVisArea );
+
+ bool bIsOldShowingState;
+ bool bIsNewShowingState = IsShowing( *(GetMap()) );
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bIsOldShowingState = m_isShowingState;
+ m_isShowingState = bIsNewShowingState;
+ }
+
+ if( bIsOldShowingState != bIsNewShowingState )
+ FireStateChangedEvent( AccessibleStateType::SHOWING,
+ bIsNewShowingState );
+}
+
+void SwAccessibleContext::ScrolledWithin( const SwRect& rOldVisArea )
+{
+ SetVisArea( GetMap()->GetVisArea() );
+
+ ChildrenScrolled( GetFrame(), rOldVisArea );
+
+ FireVisibleDataEvent();
+}
+
+void SwAccessibleContext::ScrolledIn()
+{
+ // This accessible should be freshly created, because it
+ // was not visible before. Therefore, its visible area must already
+ // reflect the scrolling.
+ OSL_ENSURE( GetVisArea() == GetMap()->GetVisArea(),
+ "Visible area of child is wrong. Did it exist already?" );
+
+ // Send child event at parent. That's all we have to do here.
+ const SwFrame* pParent = GetParent();
+ ::rtl::Reference< SwAccessibleContext > xParentImpl(
+ GetMap()->GetContextImpl( pParent, false ) );
+ uno::Reference < XAccessibleContext > xThis( this );
+ if( !xParentImpl.is() )
+ return;
+
+ SetParent( xParentImpl.get() );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xThis;
+
+ xParentImpl->FireAccessibleEvent( aEvent );
+
+ if( HasCursor() )
+ {
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ {
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, true );
+ }
+ }
+}
+
+void SwAccessibleContext::ScrolledOut( const SwRect& rOldVisArea )
+{
+ SetVisArea( GetMap()->GetVisArea() );
+
+ // First of all, update the children. That's required to dispose
+ // all children that are existing only if they are visible. They
+ // are not disposed by the recursive Dispose call that follows later on,
+ // because this call will only dispose children that are in the
+ // new visible area. The children we want to dispose however are in the
+ // old visible area all.
+ ChildrenScrolled( GetFrame(), rOldVisArea );
+
+ // Broadcast a state changed event for the showing state.
+ // It might be that the child is freshly created just to send
+ // the child event. In this case no listener will exist.
+ FireStateChangedEvent( AccessibleStateType::SHOWING, false );
+
+ // this Dispose call was removed by IAccessibility2 implementation
+ // without giving any reason why - without it we get stale
+ // entries in SwAccessibleMap::mpFrameMap.
+ Dispose(true);
+}
+
+// #i27301# - use new type definition for <_nStates>
+void SwAccessibleContext::InvalidateChildrenStates( const SwFrame* _pFrame,
+ AccessibleStates _nStates )
+{
+ const SwAccessibleChildSList aVisList( GetVisArea(), *_pFrame, *(GetMap()) );
+
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame* pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ if( rLower.IsAccessible( GetShell()->IsPreview() ) )
+ xAccImpl = GetMap()->GetContextImpl( pLower, false );
+ if( xAccImpl.is() )
+ xAccImpl->InvalidateStates( _nStates );
+ else
+ InvalidateChildrenStates( pLower, _nStates );
+ }
+ else if ( rLower.GetDrawObject() )
+ {
+ // TODO: SdrObjects
+ }
+ else if ( rLower.GetWindow() )
+ {
+ // nothing to do ?
+ }
+
+ ++aIter;
+ }
+}
+
+void SwAccessibleContext::DisposeChildren(const SwFrame *pFrame,
+ bool bRecursive,
+ bool bCanSkipInvisible)
+{
+ const SwAccessibleChildSList aVisList( GetVisArea(), *pFrame, *(GetMap()) );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame* pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ // tdf#117601 dispose the darn thing if it ever was accessible
+ ::rtl::Reference<SwAccessibleContext> xAccImpl = GetMap()->GetContextImpl(pLower, false);
+ if( xAccImpl.is() )
+ xAccImpl->Dispose( bRecursive );
+ else
+ {
+ // it's possible that the xAccImpl *does* exist with a
+ // ref-count of 0 and blocked in its dtor in another thread -
+ // this call here could be from SwAccessibleMap dtor so
+ // remove it from any maps now!
+ GetMap()->RemoveContext(pLower);
+ // in this case the context will check with a weak_ptr
+ // that the map is still alive so it's not necessary
+ // to clear its m_pMap here.
+ if (bRecursive)
+ {
+ DisposeChildren(pLower, bRecursive, bCanSkipInvisible);
+ }
+ }
+ }
+ else if ( rLower.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl(
+ GetMap()->GetContextImpl( rLower.GetDrawObject(),
+ this, false ) );
+ if( xAccImpl.is() )
+ DisposeShape( rLower.GetDrawObject(), xAccImpl.get() );
+ }
+ else if ( rLower.GetWindow() )
+ {
+ DisposeChild(rLower, false, bCanSkipInvisible);
+ }
+ ++aIter;
+ }
+}
+
+void SwAccessibleContext::InvalidateContent_( bool )
+{
+}
+
+void SwAccessibleContext::InvalidateCursorPos_()
+{
+}
+
+void SwAccessibleContext::InvalidateFocus_()
+{
+}
+
+void SwAccessibleContext::FireAccessibleEvent( AccessibleEventObject& rEvent )
+{
+ OSL_ENSURE( GetFrame(), "fire event for disposed frame?" );
+ if( !GetFrame() )
+ return;
+
+ if( !rEvent.Source.is() )
+ {
+ uno::Reference < XAccessibleContext > xThis( this );
+ rEvent.Source = xThis;
+ }
+
+ if (m_nClientId)
+ comphelper::AccessibleEventNotifier::addEvent( m_nClientId, rEvent );
+}
+
+void SwAccessibleContext::FireVisibleDataEvent()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::VISIBLE_DATA_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+}
+
+void SwAccessibleContext::FireStateChangedEvent( sal_Int16 nState,
+ bool bNewState )
+{
+ AccessibleEventObject aEvent;
+
+ aEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ if( bNewState )
+ aEvent.NewValue <<= nState;
+ else
+ aEvent.OldValue <<= nState;
+
+ FireAccessibleEvent( aEvent );
+}
+
+void SwAccessibleContext::GetStates(
+ ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SolarMutexGuard aGuard;
+
+ // SHOWING
+ if (m_isShowingState)
+ rStateSet.AddState( AccessibleStateType::SHOWING );
+
+ // EDITABLE
+ if (m_isEditableState)
+ //Set editable state to graphic and other object when the document is editable
+ {
+ rStateSet.AddState( AccessibleStateType::EDITABLE );
+ rStateSet.AddState( AccessibleStateType::RESIZABLE );
+ rStateSet.AddState( AccessibleStateType::MOVEABLE );
+ }
+ // ENABLED
+ rStateSet.AddState( AccessibleStateType::ENABLED );
+
+ // OPAQUE
+ if (m_isOpaqueState)
+ rStateSet.AddState( AccessibleStateType::OPAQUE );
+
+ // VISIBLE
+ rStateSet.AddState( AccessibleStateType::VISIBLE );
+
+ if (m_isDefuncState)
+ rStateSet.AddState( AccessibleStateType::DEFUNC );
+}
+
+bool SwAccessibleContext::IsEditableState()
+{
+ bool bRet;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bRet = m_isEditableState;
+ }
+
+ return bRet;
+}
+
+void SwAccessibleContext::ThrowIfDisposed()
+{
+ if (!(GetFrame() && GetMap()))
+ {
+ throw lang::DisposedException("object is nonfunctional",
+ static_cast<cppu::OWeakObject*>(this));
+ }
+}
+
+SwAccessibleContext::SwAccessibleContext(std::shared_ptr<SwAccessibleMap> const& pMap,
+ sal_Int16 const nRole,
+ const SwFrame *pF )
+ : SwAccessibleFrame( pMap->GetVisArea(), pF,
+ pMap->GetShell()->IsPreview() )
+ , m_pMap(pMap.get())
+ , m_wMap(pMap)
+ , m_nClientId(0)
+ , m_nRole(nRole)
+ , m_isDisposing( false )
+ , m_isRegisteredAtAccessibleMap( true )
+ , m_isSelectedInDoc(false)
+{
+ InitStates();
+}
+
+SwAccessibleContext::~SwAccessibleContext()
+{
+ // must have for 2 reasons: 2. as long as this thread has SolarMutex
+ // another thread cannot destroy the SwAccessibleMap so our temporary
+ // taking a hard ref to SwAccessibleMap won't delay its destruction
+ SolarMutexGuard aGuard;
+ // must check with weak_ptr that m_pMap is still alive
+ std::shared_ptr<SwAccessibleMap> pMap(m_wMap.lock());
+ if (m_isRegisteredAtAccessibleMap && GetFrame() && pMap)
+ {
+ pMap->RemoveContext( GetFrame() );
+ }
+}
+
+uno::Reference< XAccessibleContext > SAL_CALL
+ SwAccessibleContext::getAccessibleContext()
+{
+ uno::Reference < XAccessibleContext > xRet( this );
+ return xRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleContext::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return m_isDisposing ? 0 : GetChildCount( *(GetMap()) );
+}
+
+uno::Reference< XAccessible> SAL_CALL
+ SwAccessibleContext::getAccessibleChild( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwAccessibleChild aChild( GetChild( *(GetMap()), nIndex ) );
+ if( !aChild.IsValid() )
+ {
+ uno::Reference < XAccessibleContext > xThis( this );
+ lang::IndexOutOfBoundsException aExcept(
+ "index out of bounds",
+ xThis );
+ throw aExcept;
+ }
+
+ uno::Reference< XAccessible > xChild;
+ if( aChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl(
+ GetMap()->GetContextImpl( aChild.GetSwFrame(), !m_isDisposing ) );
+ if( xChildImpl.is() )
+ {
+ xChildImpl->SetParent( this );
+ xChild = xChildImpl.get();
+ }
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
+ GetMap()->GetContextImpl( aChild.GetDrawObject(),
+ this, !m_isDisposing) );
+ if( xChildImpl.is() )
+ xChild = xChildImpl.get();
+ }
+ else if ( aChild.GetWindow() )
+ {
+ xChild = aChild.GetWindow()->GetAccessible();
+ }
+
+ return xChild;
+}
+
+css::uno::Sequence<uno::Reference<XAccessible>> SAL_CALL
+ SwAccessibleContext::getAccessibleChildren()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ std::list< sw::access::SwAccessibleChild > aChildren;
+ GetChildren( *GetMap(), aChildren );
+
+ std::vector<uno::Reference<XAccessible>> aRet;
+ aRet.reserve(aChildren.size());
+ for (const auto & rSwChild : aChildren)
+ {
+ uno::Reference< XAccessible > xChild;
+ if( rSwChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl(
+ GetMap()->GetContextImpl( rSwChild.GetSwFrame(), !m_isDisposing ) );
+ if( xChildImpl.is() )
+ {
+ xChildImpl->SetParent( this );
+ xChild = xChildImpl.get();
+ }
+ }
+ else if ( rSwChild.GetDrawObject() )
+ {
+ ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
+ GetMap()->GetContextImpl( rSwChild.GetDrawObject(),
+ this, !m_isDisposing) );
+ if( xChildImpl.is() )
+ xChild = xChildImpl.get();
+ }
+ else if ( rSwChild.GetWindow() )
+ {
+ xChild = rSwChild.GetWindow()->GetAccessible();
+ }
+ aRet.push_back(xChild);
+ }
+ return comphelper::containerToSequence(aRet);
+}
+
+uno::Reference< XAccessible> SwAccessibleContext::getAccessibleParentImpl()
+{
+ SolarMutexGuard aGuard;
+
+ const SwFrame *pUpper = GetParent();
+ OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );
+
+ uno::Reference< XAccessible > xAcc;
+ if( pUpper )
+ xAcc = GetMap()->GetContext( pUpper, !m_isDisposing );
+
+ OSL_ENSURE( xAcc.is() || m_isDisposing, "no parent found" );
+
+ // Remember the parent as weak ref.
+ {
+ std::scoped_lock aWeakParentGuard( m_Mutex );
+ m_xWeakParent = xAcc;
+ }
+
+ return xAcc;
+}
+
+uno::Reference< XAccessible> SAL_CALL SwAccessibleContext::getAccessibleParent()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return getAccessibleParentImpl();
+}
+
+sal_Int32 SAL_CALL SwAccessibleContext::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pUpper = GetParent();
+ OSL_ENSURE( pUpper != nullptr || m_isDisposing, "no upper found" );
+
+ sal_Int32 nIndex = -1;
+ if( pUpper )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ GetMap()->GetContextImpl(pUpper, !m_isDisposing) );
+ OSL_ENSURE( xAccImpl.is() || m_isDisposing, "no parent found" );
+ if( xAccImpl.is() )
+ nIndex = xAccImpl->GetChildIndex( *(GetMap()), SwAccessibleChild(GetFrame()) );
+ }
+
+ return nIndex;
+}
+
+sal_Int16 SAL_CALL SwAccessibleContext::getAccessibleRole()
+{
+ return m_nRole;
+}
+
+OUString SAL_CALL SwAccessibleContext::getAccessibleName()
+{
+ return m_sName;
+}
+
+uno::Reference< XAccessibleRelationSet> SAL_CALL
+ SwAccessibleContext::getAccessibleRelationSet()
+{
+ // by default there are no relations
+ uno::Reference< XAccessibleRelationSet> xRet( new utl::AccessibleRelationSetHelper() );
+ return xRet;
+}
+
+uno::Reference<XAccessibleStateSet> SAL_CALL
+ SwAccessibleContext::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet =
+ new ::utl::AccessibleStateSetHelper;
+
+ if (m_isSelectedInDoc)
+ pStateSet->AddState( AccessibleStateType::SELECTED );
+
+ GetStates( *pStateSet );
+
+ return pStateSet;
+}
+
+lang::Locale SAL_CALL SwAccessibleContext::getLocale()
+{
+ SolarMutexGuard aGuard;
+
+ lang::Locale aLoc( Application::GetSettings().GetLanguageTag().getLocale() );
+ return aLoc;
+}
+
+void SAL_CALL SwAccessibleContext::addAccessibleEventListener(
+ const uno::Reference< XAccessibleEventListener >& xListener )
+{
+ if (xListener.is())
+ {
+ SolarMutexGuard aGuard;
+ if (!m_nClientId)
+ m_nClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( m_nClientId, xListener );
+ }
+}
+
+void SAL_CALL SwAccessibleContext::removeAccessibleEventListener(
+ const uno::Reference< XAccessibleEventListener >& xListener )
+{
+ if (!(xListener.is() && m_nClientId))
+ return;
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( m_nClientId, xListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ comphelper::AccessibleEventNotifier::revokeClient( m_nClientId );
+ m_nClientId = 0;
+ }
+}
+
+static bool lcl_PointInRectangle(const awt::Point & aPoint,
+ const awt::Rectangle & aRect)
+{
+ tools::Long nDiffX = aPoint.X - aRect.X;
+ tools::Long nDiffY = aPoint.Y - aRect.Y;
+
+ return
+ nDiffX >= 0 && nDiffX < aRect.Width && nDiffY >= 0 &&
+ nDiffY < aRect.Height;
+
+}
+
+sal_Bool SAL_CALL SwAccessibleContext::containsPoint(
+ const awt::Point& aPoint )
+{
+ awt::Rectangle aPixBounds = getBoundsImpl(true);
+ aPixBounds.X = 0;
+ aPixBounds.Y = 0;
+
+ return lcl_PointInRectangle(aPoint, aPixBounds);
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleContext::getAccessibleAtPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ uno::Reference< XAccessible > xAcc;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to parent
+ if( !GetFrame()->IsRootFrame() )
+ {
+ SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
+ Point aPixPos( GetMap()->CoreToPixel( aLogBounds ).TopLeft() );
+ aPixPoint.setX(aPixPoint.getX() + aPixPos.getX());
+ aPixPoint.setY(aPixPoint.getY() + aPixPos.getY());
+ }
+
+ const SwAccessibleChild aChild( GetChildAtPixel( aPixPoint, *(GetMap()) ) );
+ if( aChild.GetSwFrame() )
+ {
+ xAcc = GetMap()->GetContext( aChild.GetSwFrame() );
+ }
+ else if( aChild.GetDrawObject() )
+ {
+ xAcc = GetMap()->GetContext( aChild.GetDrawObject(), this );
+ }
+ else if ( aChild.GetWindow() )
+ {
+ xAcc = aChild.GetWindow()->GetAccessible();
+ }
+
+ return xAcc;
+}
+
+/**
+ Get bounding box.
+
+ There are two modes.
+
+ - relative
+
+ Return bounding box relative to parent if parent is no root
+ frame. Otherwise return the absolute bounding box.
+
+ - absolute
+
+ Return the absolute bounding box.
+
+ @param bRelative
+ true: Use relative mode.
+ false: Use absolute mode.
+*/
+awt::Rectangle SwAccessibleContext::getBoundsImpl(bool bRelative)
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pParent = GetParent();
+ OSL_ENSURE( pParent, "no Parent found" );
+ vcl::Window *pWin = GetWindow();
+
+ if (!pParent)
+ {
+ throw uno::RuntimeException("no Parent", static_cast<cppu::OWeakObject*>(this));
+ }
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip relative to document root
+ tools::Rectangle aPixBounds( 0, 0, 0, 0 );
+ if( GetFrame()->IsPageFrame() &&
+ static_cast < const SwPageFrame * >( GetFrame() )->IsEmptyPage() )
+ {
+ OSL_ENSURE( GetShell()->IsPreview(), "empty page accessible?" );
+ if( GetShell()->IsPreview() )
+ {
+ // adjust method call <GetMap()->GetPreviewPageSize()>
+ sal_uInt16 nPageNum =
+ static_cast < const SwPageFrame * >( GetFrame() )->GetPhyPageNum();
+ aLogBounds.SSize( GetMap()->GetPreviewPageSize( nPageNum ) );
+ }
+ }
+ if( !aLogBounds.IsEmpty() )
+ {
+ aPixBounds = GetMap()->CoreToPixel( aLogBounds );
+ if( !pParent->IsRootFrame() && bRelative)
+ {
+ SwRect aParentLogBounds( GetBounds( *(GetMap()), pParent ) ); // twip rel to doc root
+ Point aParentPixPos( GetMap()->CoreToPixel( aParentLogBounds ).TopLeft() );
+ aPixBounds.Move( -aParentPixPos.getX(), -aParentPixPos.getY() );
+ }
+ }
+
+ awt::Rectangle aBox( aPixBounds.Left(), aPixBounds.Top(),
+ aPixBounds.GetWidth(), aPixBounds.GetHeight() );
+
+ return aBox;
+}
+
+awt::Rectangle SAL_CALL SwAccessibleContext::getBounds()
+{
+ return getBoundsImpl(true);
+}
+
+awt::Point SAL_CALL SwAccessibleContext::getLocation()
+{
+ awt::Rectangle aRect = getBoundsImpl(true);
+ awt::Point aPoint(aRect.X, aRect.Y);
+
+ return aPoint;
+}
+
+awt::Point SAL_CALL SwAccessibleContext::getLocationOnScreen()
+{
+ awt::Rectangle aRect = getBoundsImpl(false);
+
+ Point aPixPos(aRect.X, aRect.Y);
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ aPixPos = pWin->OutputToAbsoluteScreenPixel(aPixPos);
+ awt::Point aPoint(aPixPos.getX(), aPixPos.getY());
+
+ return aPoint;
+}
+
+awt::Size SAL_CALL SwAccessibleContext::getSize()
+{
+ awt::Rectangle aRect = getBoundsImpl(false);
+ awt::Size aSize( aRect.Width, aRect.Height );
+
+ return aSize;
+}
+
+void SAL_CALL SwAccessibleContext::grabFocus()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( GetFrame()->IsFlyFrame() )
+ {
+ const SdrObject *pObj =
+ static_cast < const SwFlyFrame * >( GetFrame() )->GetVirtDrawObj();
+ if( pObj )
+ Select( const_cast < SdrObject * >( pObj ), false );
+ }
+ else
+ {
+ const SwContentFrame *pCFrame = nullptr;
+ if( GetFrame()->IsContentFrame() )
+ pCFrame = static_cast< const SwContentFrame * >( GetFrame() );
+ else if( GetFrame()->IsLayoutFrame() )
+ pCFrame = static_cast< const SwLayoutFrame * >( GetFrame() )->ContainsContent();
+
+ if( pCFrame && pCFrame->IsTextFrame() )
+ {
+ const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pCFrame );
+ const SwTextNode *pTextNd = pTextFrame->GetTextNodeFirst();
+ assert(pTextNd); // can it actually be null? probably not=>simplify
+ if( pTextNd )
+ {
+ // create pam for selection
+ SwPosition const aStartPos(pTextFrame->MapViewToModelPos(pTextFrame->GetOffset()));
+ SwPaM aPaM( aStartPos );
+
+ // set PaM at cursor shell
+ Select( aPaM );
+ }
+ }
+ }
+}
+
+sal_Int32 SAL_CALL SwAccessibleContext::getForeground()
+{
+ return sal_Int32(COL_BLACK);
+}
+
+sal_Int32 SAL_CALL SwAccessibleContext::getBackground()
+{
+ return sal_Int32(COL_WHITE);
+}
+
+sal_Bool SAL_CALL SwAccessibleContext::supportsService (const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+void SwAccessibleContext::DisposeShape( const SdrObject *pObj,
+ ::accessibility::AccessibleShape *pAccImpl )
+{
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl( pAccImpl );
+ if( !xAccImpl.is() )
+ xAccImpl = GetMap()->GetContextImpl( pObj, this );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ uno::Reference< XAccessible > xAcc( xAccImpl );
+ aEvent.OldValue <<= xAcc;
+ FireAccessibleEvent( aEvent );
+
+ GetMap()->RemoveContext( pObj );
+ xAccImpl->dispose();
+}
+
+void SwAccessibleContext::ScrolledInShape( ::accessibility::AccessibleShape *pAccImpl )
+{
+ if(nullptr == pAccImpl)
+ {
+ return ;
+ }
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ uno::Reference< XAccessible > xAcc( pAccImpl );
+ aEvent.NewValue <<= xAcc;
+ FireAccessibleEvent( aEvent );
+
+ if( !pAccImpl->GetState( AccessibleStateType::FOCUSED ) )
+ return;
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ {
+ AccessibleEventObject aStateChangedEvent;
+ aStateChangedEvent.EventId = AccessibleEventId::STATE_CHANGED;
+ aStateChangedEvent.NewValue <<= AccessibleStateType::FOCUSED;
+ aStateChangedEvent.Source = xAcc;
+
+ FireAccessibleEvent( aStateChangedEvent );
+ }
+}
+
+void SwAccessibleContext::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
+ OSL_ENSURE( GetMap()->GetVisArea() == GetVisArea(),
+ "invalid visible area for dispose" );
+
+ m_isDisposing = true;
+
+ // dispose children
+ if( bRecursive )
+ DisposeChildren(GetFrame(), bRecursive, bCanSkipInvisible);
+
+ // get parent
+ uno::Reference< XAccessible > xParent( GetWeakParent() );
+ uno::Reference < XAccessibleContext > xThis( this );
+
+ // send child event at parent
+ if( xParent.is() )
+ {
+ SwAccessibleContext *pAcc = static_cast<SwAccessibleContext *>(xParent.get());
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= xThis;
+ pAcc->FireAccessibleEvent( aEvent );
+ }
+
+ // set defunc state (it's not required to broadcast a state changed
+ // event if the object is disposed afterwards)
+ {
+ std::scoped_lock aDefuncStateGuard( m_Mutex );
+ m_isDefuncState = true;
+ }
+
+ // broadcast dispose event
+ if (m_nClientId)
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( m_nClientId, *this );
+ m_nClientId = 0;
+ }
+
+ RemoveFrameFromAccessibleMap();
+ ClearFrame();
+ m_pMap = nullptr;
+ m_wMap.reset();
+
+ m_isDisposing = false;
+}
+
+void SwAccessibleContext::DisposeChild( const SwAccessibleChild& rChildFrameOrObj,
+ bool bRecursive, bool bCanSkipInvisible )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !bCanSkipInvisible ||
+ rChildFrameOrObj.AlwaysIncludeAsChild() ||
+ IsShowing( *(GetMap()), rChildFrameOrObj ) ||
+ !SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly() )
+ {
+ // If the object could have existed before, then there is nothing to do,
+ // because no wrapper exists now and therefore no one is interested to
+ // get notified of the movement.
+ if( rChildFrameOrObj.GetSwFrame() )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame(), false );
+ if (xAccImpl)
+ xAccImpl->Dispose( bRecursive );
+ }
+ else if ( rChildFrameOrObj.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
+ this, false );
+ if (xAccImpl)
+ DisposeShape( rChildFrameOrObj.GetDrawObject(),
+ xAccImpl.get() );
+ }
+ else if ( rChildFrameOrObj.GetWindow() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ uno::Reference< XAccessible > xAcc =
+ rChildFrameOrObj.GetWindow()->GetAccessible();
+ aEvent.OldValue <<= xAcc;
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ else if( bRecursive && rChildFrameOrObj.GetSwFrame() )
+ DisposeChildren(rChildFrameOrObj.GetSwFrame(), bRecursive, bCanSkipInvisible);
+}
+
+void SwAccessibleContext::InvalidatePosOrSize( const SwRect& )
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( GetFrame() && !GetFrame()->getFrameArea().IsEmpty(), "context should have a size" );
+
+ bool bIsOldShowingState;
+ bool bIsNewShowingState = IsShowing( *(GetMap()) );
+ {
+ std::scoped_lock aShowingStateGuard( m_Mutex );
+ bIsOldShowingState = m_isShowingState;
+ m_isShowingState = bIsNewShowingState;
+ }
+
+ if( bIsOldShowingState != bIsNewShowingState )
+ {
+ FireStateChangedEvent( AccessibleStateType::SHOWING,
+ bIsNewShowingState );
+ }
+ else if( bIsNewShowingState )
+ {
+ // The frame stays visible -> broadcast event
+ FireVisibleDataEvent();
+ }
+
+ // note: InvalidatePosOrSize must call InvalidateContent_ so that
+ // SwAccessibleParagraph updates its portions, or dispose it
+ // (see accmap.cxx: INVALID_CONTENT is contained in POS_CHANGED)
+ if( !bIsNewShowingState &&
+ SwAccessibleChild( GetParent() ).IsVisibleChildrenOnly() )
+ {
+ // this Dispose call was removed by IAccessibility2 implementation
+ // without giving any reason why - without it we get stale
+ // entries in SwAccessibleMap::mpFrameMap.
+ Dispose(true);
+ }
+ else
+ {
+ InvalidateContent_( true );
+ }
+}
+
+void SwAccessibleContext::InvalidateChildPosOrSize(
+ const SwAccessibleChild& rChildFrameOrObj,
+ const SwRect& rOldFrame )
+{
+ SolarMutexGuard aGuard;
+
+ // this happens during layout, e.g. when a page is deleted and next page's
+ // header/footer moves backward such an event is generated
+ SAL_INFO_IF(rChildFrameOrObj.GetSwFrame() &&
+ rChildFrameOrObj.GetSwFrame()->getFrameArea().IsEmpty(),
+ "sw.a11y", "child context should have a size");
+
+ if ( rChildFrameOrObj.AlwaysIncludeAsChild() )
+ {
+ // nothing to do;
+ return;
+ }
+
+ const bool bVisibleChildrenOnly = SwAccessibleChild( GetFrame() ).IsVisibleChildrenOnly();
+ const bool bNew = rOldFrame.IsEmpty() ||
+ ( rOldFrame.Left() == 0 && rOldFrame.Top() == 0 );
+ if( IsShowing( *(GetMap()), rChildFrameOrObj ) )
+ {
+ // If the object could have existed before, then there is nothing to do,
+ // because no wrapper exists now and therefore no one is interested to
+ // get notified of the movement.
+ if( bNew || (bVisibleChildrenOnly && !IsShowing( rOldFrame )) )
+ {
+ if( rChildFrameOrObj.GetSwFrame() )
+ {
+ // The frame becomes visible. A child event must be send.
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
+ xAccImpl->ScrolledIn();
+ }
+ else if ( rChildFrameOrObj.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
+ this );
+ // #i37790#
+ if ( xAccImpl.is() )
+ {
+ ScrolledInShape( xAccImpl.get() );
+ }
+ else
+ {
+ OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - no accessible shape found." );
+ }
+ }
+ else if ( rChildFrameOrObj.GetWindow() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= rChildFrameOrObj.GetWindow()->GetAccessible();
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ else
+ {
+ // If the frame was visible before, then a child event for the parent
+ // needs to be send. However, there is no wrapper existing, and so
+ // no notifications for grandchildren are required. If the are
+ // grandgrandchildren, they would be notified by the layout.
+ if( bVisibleChildrenOnly &&
+ !bNew && IsShowing( rOldFrame ) )
+ {
+ if( rChildFrameOrObj.GetSwFrame() )
+ {
+ ::rtl::Reference< SwAccessibleContext > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetSwFrame() );
+ xAccImpl->SetParent( this );
+ xAccImpl->Dispose( true );
+ }
+ else if ( rChildFrameOrObj.GetDrawObject() )
+ {
+ ::rtl::Reference< ::accessibility::AccessibleShape > xAccImpl =
+ GetMap()->GetContextImpl( rChildFrameOrObj.GetDrawObject(),
+ this );
+ DisposeShape( rChildFrameOrObj.GetDrawObject(),
+ xAccImpl.get() );
+ }
+ else if ( rChildFrameOrObj.GetWindow() )
+ {
+ OSL_FAIL( "<SwAccessibleContext::InvalidateChildPosOrSize(..)> - not expected to handle dispose of child of type <vcl::Window>." );
+ }
+ }
+ }
+}
+
+void SwAccessibleContext::InvalidateContent()
+{
+ SolarMutexGuard aGuard;
+
+ InvalidateContent_( false );
+}
+
+void SwAccessibleContext::InvalidateCursorPos()
+{
+ SolarMutexGuard aGuard;
+
+ InvalidateCursorPos_();
+}
+
+void SwAccessibleContext::InvalidateFocus()
+{
+ SolarMutexGuard aGuard;
+
+ InvalidateFocus_();
+}
+
+// #i27301# - use new type definition for <_nStates>
+void SwAccessibleContext::InvalidateStates( AccessibleStates _nStates )
+{
+ if( !GetMap() )
+ return;
+
+ SwViewShell *pVSh = GetMap()->GetShell();
+ if( pVSh )
+ {
+ if( _nStates & AccessibleStates::EDITABLE )
+ {
+ bool bIsOldEditableState;
+ bool bIsNewEditableState = IsEditable( pVSh );
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bIsOldEditableState = m_isEditableState;
+ m_isEditableState = bIsNewEditableState;
+ }
+
+ if( bIsOldEditableState != bIsNewEditableState )
+ FireStateChangedEvent( AccessibleStateType::EDITABLE,
+ bIsNewEditableState );
+ }
+ if( _nStates & AccessibleStates::OPAQUE )
+ {
+ bool bIsOldOpaqueState;
+ bool bIsNewOpaqueState = IsOpaque( pVSh );
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bIsOldOpaqueState = m_isOpaqueState;
+ m_isOpaqueState = bIsNewOpaqueState;
+ }
+
+ if( bIsOldOpaqueState != bIsNewOpaqueState )
+ FireStateChangedEvent( AccessibleStateType::OPAQUE,
+ bIsNewOpaqueState );
+ }
+ }
+
+ InvalidateChildrenStates( GetFrame(), _nStates );
+}
+
+void SwAccessibleContext::InvalidateRelation( sal_uInt16 nType )
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = nType;
+
+ FireAccessibleEvent( aEvent );
+}
+
+/** #i27301# - text selection has changed */
+void SwAccessibleContext::InvalidateTextSelection()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TEXT_SELECTION_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+}
+
+/** #i88069# - attributes has changed */
+void SwAccessibleContext::InvalidateAttr()
+{
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TEXT_ATTRIBUTE_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+}
+
+bool SwAccessibleContext::HasCursor()
+{
+ return false;
+}
+
+bool SwAccessibleContext::Select( SwPaM *pPaM, SdrObject *pObj,
+ bool bAdd )
+{
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( !pCursorShell )
+ return false;
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ // Get rid of activated OLE object
+ if( pFEShell )
+ pFEShell->FinishOLEObj();
+
+ SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(pCursorShell);
+
+ bool bRet = false;
+ if( pObj )
+ {
+ if( pFEShell )
+ {
+ sal_uInt8 nFlags = bAdd ? SW_ADD_SELECT : 0;
+ pFEShell->SelectObj( Point(), nFlags, pObj );
+ bRet = true;
+ }
+ }
+ else if( pPaM )
+ {
+ // Get rid of frame selection. If there is one, make text cursor
+ // visible again.
+ bool bCallShowCursor = false;
+ if( pFEShell && (pFEShell->IsFrameSelected() ||
+ pFEShell->IsObjSelected()) )
+ {
+ Point aPt( LONG_MIN, LONG_MIN );
+ pFEShell->SelectObj( aPt );
+ bCallShowCursor = true;
+ }
+ pCursorShell->KillPams();
+ if( pWrtShell && pPaM->HasMark() )
+ // We have to do this or SwWrtShell can't figure out that it needs
+ // to kill the selection later, when the user moves the cursor.
+ pWrtShell->SttSelect();
+ pCursorShell->SetSelection( *pPaM );
+ if( pPaM->HasMark() && *pPaM->GetPoint() == *pPaM->GetMark())
+ // Setting a "Selection" that starts and ends at the same spot
+ // should remove the selection rather than create an empty one, so
+ // that we get defined behavior if accessibility sets the cursor
+ // later.
+ pCursorShell->ClearMark();
+ if( bCallShowCursor )
+ pCursorShell->ShowCursor();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+OUString SwAccessibleContext::GetResource(TranslateId pResId,
+ const OUString *pArg1,
+ const OUString *pArg2)
+{
+ OUString sStr = SwResId(pResId);
+
+ if( pArg1 )
+ {
+ sStr = sStr.replaceFirst( "$(ARG1)", *pArg1 );
+ }
+ if( pArg2 )
+ {
+ sStr = sStr.replaceFirst( "$(ARG2)", *pArg2 );
+ }
+
+ return sStr;
+}
+
+void SwAccessibleContext::RemoveFrameFromAccessibleMap()
+{
+ assert(m_refCount > 0); // must be alive to do this without using m_wMap
+ if (m_isRegisteredAtAccessibleMap && GetFrame() && GetMap())
+ GetMap()->RemoveContext( GetFrame() );
+}
+
+bool SwAccessibleContext::HasAdditionalAccessibleChildren()
+{
+ bool bRet( false );
+
+ if ( GetFrame()->IsTextFrame() )
+ {
+ SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ bRet = pPostItMgr->HasFrameConnectedSidebarWins( *(GetFrame()) );
+ }
+ }
+
+ return bRet;
+}
+
+/** #i88070# - get additional accessible child by index */
+vcl::Window* SwAccessibleContext::GetAdditionalAccessibleChild( const sal_Int32 nIndex )
+{
+ vcl::Window* pAdditionalAccessibleChild( nullptr );
+
+ if ( GetFrame()->IsTextFrame() )
+ {
+ SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ pAdditionalAccessibleChild =
+ pPostItMgr->GetSidebarWinForFrameByIndex( *(GetFrame()), nIndex );
+ }
+ }
+
+ return pAdditionalAccessibleChild;
+}
+
+/** #i88070# - get all additional accessible children */
+void SwAccessibleContext::GetAdditionalAccessibleChildren( std::vector< vcl::Window* >* pChildren )
+{
+ if ( GetFrame()->IsTextFrame() )
+ {
+ SwPostItMgr* pPostItMgr = GetMap()->GetShell()->GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ pPostItMgr->GetAllSidebarWinForFrame( *(GetFrame()), pChildren );
+ }
+ }
+}
+
+bool SwAccessibleContext::SetSelectedState(bool const bSelected)
+{
+ if (m_isSelectedInDoc != bSelected)
+ {
+ m_isSelectedInDoc = bSelected;
+ FireStateChangedEvent( AccessibleStateType::SELECTED, bSelected );
+ return true;
+ }
+ return false;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acccontext.hxx b/sw/source/core/access/acccontext.hxx
new file mode 100644
index 000000000..4faf8e237
--- /dev/null
+++ b/sw/source/core/access/acccontext.hxx
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "accframe.hxx"
+#include <accmap.hxx>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext3.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <unotools/resmgr.hxx>
+
+#include <memory>
+#include <mutex>
+
+namespace vcl { class Window; }
+class SwCursorShell;
+class SdrObject;
+class SwPaM;
+namespace utl {
+ class AccessibleStateSetHelper;
+}
+namespace accessibility {
+ class AccessibleShape;
+}
+
+inline constexpr OUStringLiteral sAccessibleServiceName = u"com.sun.star.accessibility.Accessible";
+
+class SwAccessibleContext :
+ public ::cppu::WeakImplHelper<
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleContext3,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::lang::XServiceInfo
+ >,
+ public SwAccessibleFrame
+{
+ // The implements for the XAccessibleSelection interface has been
+ // 'externalized' and wants access to the protected members like
+ // GetMap, GetChild, GetParent, and GetFrame.
+ friend class SwAccessibleSelectionHelper;
+#if OSL_DEBUG_LEVEL > 0
+ friend class SwAccessibleMap;
+#endif
+
+protected:
+ mutable std::mutex m_Mutex;
+
+private:
+ OUString m_sName; // immutable outside constructor
+
+ // The parent if it has been retrieved. This is always an
+ // SwAccessibleContext. (protected by Mutex)
+ css::uno::WeakReference <
+ css::accessibility::XAccessible > m_xWeakParent;
+
+ SwAccessibleMap *m_pMap; // must be protected by solar mutex
+ /// note: the m_pMap is guaranteed to be valid until we hit the
+ /// dtor ~SwAccessibleContext, then m_wMap must be checked if it's still
+ /// alive, after locking SolarMutex (alternatively, Dispose clears m_pMap)
+ std::weak_ptr<SwAccessibleMap> m_wMap;
+
+ sal_uInt32 m_nClientId; // client id in the AccessibleEventNotifier queue
+ sal_Int16 m_nRole; // immutable outside constructor
+
+ // The current states (protected by mutex)
+ bool m_isShowingState : 1;
+ bool m_isEditableState : 1;
+ bool m_isOpaqueState : 1;
+ bool m_isDefuncState : 1;
+
+ // Are we currently disposing that object (protected by solar mutex)?
+ bool m_isDisposing : 1;
+
+ // #i85634# - boolean, indicating if the accessible context is
+ // in general registered at the accessible map.
+ bool m_isRegisteredAtAccessibleMap;
+
+ void InitStates();
+
+protected:
+ void SetName( const OUString& rName ) { m_sName = rName; }
+ sal_Int16 GetRole() const
+ {
+ return m_nRole;
+ }
+ //This flag is used to mark the object's selected state.
+ bool m_isSelectedInDoc;
+ void SetParent( SwAccessibleContext *pParent );
+ css::uno::Reference< css::accessibility::XAccessible> GetWeakParent() const;
+
+ bool IsDisposing() const { return m_isDisposing; }
+
+ vcl::Window *GetWindow();
+ SwAccessibleMap *GetMap() { return m_pMap; }
+ const SwAccessibleMap *GetMap() const { return m_pMap; }
+
+ /** convenience method to get the SwViewShell through accessibility map */
+ SwViewShell* GetShell()
+ {
+ return GetMap()->GetShell();
+ }
+ const SwViewShell* GetShell() const
+ {
+ return GetMap()->GetShell();
+ }
+
+ /** convenience method to get SwCursorShell through accessibility map
+ * @returns SwCursorShell, or NULL if none is found */
+ SwCursorShell* GetCursorShell();
+ const SwCursorShell* GetCursorShell() const;
+
+ // Notify all children that the visible area has changed.
+ // The SwFrame might belong to the current object or to any other child or
+ // grandchild.
+ void ChildrenScrolled( const SwFrame *pFrame, const SwRect& rOldVisArea );
+
+ // The context's showing state changed. May only be called for context that
+ // exist even if they aren't visible.
+ void Scrolled( const SwRect& rOldVisArea );
+
+ // A child has been moved while setting the visible area
+ void ScrolledWithin( const SwRect& rOldVisArea );
+
+ // The has been added while setting the visible area
+ void ScrolledIn();
+
+ // The context has to be removed while setting the visible area
+ void ScrolledOut( const SwRect& rOldVisArea );
+
+ // Invalidate the states of all children of the specified SwFrame. The
+ // SwFrame might belong the current object or to any child or grandchild!
+ // #i27301# - use new type definition for <_nStates>
+ void InvalidateChildrenStates( const SwFrame* _pFrame,
+ AccessibleStates _nStates );
+
+ // Dispose children of the specified SwFrame. The SwFrame might belong to
+ // the current object or to any other child or grandchild.
+ void DisposeChildren(const SwFrame *pFrame,
+ bool bRecursive, bool bCanSkipInvisible);
+
+ void DisposeShape( const SdrObject *pObj,
+ ::accessibility::AccessibleShape *pAccImpl );
+ void ScrolledInShape( ::accessibility::AccessibleShape *pAccImpl );
+
+ virtual void InvalidateContent_( bool bVisibleDataFired );
+
+ virtual void InvalidateCursorPos_();
+ virtual void InvalidateFocus_();
+
+public:
+ void FireAccessibleEvent( css::accessibility::AccessibleEventObject& rEvent );
+
+protected:
+ // broadcast visual data event
+ void FireVisibleDataEvent();
+
+ // broadcast state change event
+ void FireStateChangedEvent( sal_Int16 nState, bool bNewState );
+
+ // Set states for getAccessibleStateSet.
+ // This base class sets DEFUNC(0/1), EDITABLE(0/1), ENABLED(1),
+ // SHOWING(0/1), OPAQUE(0/1) and VISIBLE(1).
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet );
+
+ bool IsEditableState();
+
+ /// @throws css::uno::RuntimeException
+ css::awt::Rectangle
+ getBoundsImpl(bool bRelative);
+
+ // #i85634#
+ void NotRegisteredAtAccessibleMap()
+ {
+ m_isRegisteredAtAccessibleMap = false;
+ }
+ void RemoveFrameFromAccessibleMap();
+
+ void ThrowIfDisposed();
+
+ virtual ~SwAccessibleContext() override;
+
+ // Return a reference to the parent.
+ css::uno::Reference< css::accessibility::XAccessible>
+ getAccessibleParentImpl();
+
+public:
+ SwAccessibleContext( std::shared_ptr<SwAccessibleMap> const& pMap,
+ sal_Int16 nRole, const SwFrame *pFrame );
+
+ // XAccessible
+
+ // Return the XAccessibleContext.
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext> SAL_CALL
+ getAccessibleContext() override;
+
+ // XAccessibleContext
+
+ // Return the number of currently visible children.
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+
+ // Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int32 nIndex) override;
+
+ virtual css::uno::Sequence<css::uno::Reference< css::accessibility::XAccessible>> SAL_CALL
+ getAccessibleChildren() override;
+
+ // Return a reference to the parent.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleParent() override;
+
+ // Return this objects index among the parents children.
+ virtual sal_Int32 SAL_CALL
+ getAccessibleIndexInParent() override;
+
+ // Return this object's role.
+ virtual sal_Int16 SAL_CALL
+ getAccessibleRole() override;
+
+ // getAccessibleDescription() is abstract
+
+ // Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+
+ // Return NULL to indicate that an empty relation set.
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ // Return the set of current states.
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleStateSet> SAL_CALL
+ getAccessibleStateSet() override;
+
+ /** Return the parents locale or throw exception if this object has no
+ parent yet/anymore. */
+ virtual css::lang::Locale SAL_CALL
+ getLocale() override;
+
+ // XAccessibleEventBroadcaster
+
+ virtual void SAL_CALL addAccessibleEventListener(
+ const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener(
+ const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::awt::Rectangle SAL_CALL getBounds() override;
+
+ virtual css::awt::Point SAL_CALL getLocation() override;
+
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override;
+
+ virtual css::awt::Size SAL_CALL getSize() override;
+
+ virtual void SAL_CALL grabFocus() override;
+
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ // XServiceInfo
+
+ // getImplementationName() and getSupportedServiceNames are abstract
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // thread safe C++ interface
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true);
+
+ // The child object is not visible any longer and should be destroyed
+ virtual void DisposeChild(const sw::access::SwAccessibleChild& rFrameOrObj, bool bRecursive, bool bCanSkipInvisible);
+
+ // The object has been moved by the layout
+ virtual void InvalidatePosOrSize( const SwRect& rFrame );
+
+ // The child object has been moved by the layout
+ virtual void InvalidateChildPosOrSize( const sw::access::SwAccessibleChild& rFrameOrObj,
+ const SwRect& rFrame );
+
+ // The content may have changed (but it hasn't to have changed)
+ void InvalidateContent();
+
+ // The caretPos has changed
+ void InvalidateCursorPos();
+
+ // The Focus state has changed
+ void InvalidateFocus();
+
+ // Check states
+ // #i27301# - use new type definition for <_nStates>
+ void InvalidateStates( AccessibleStates _nStates );
+
+ // the XAccessibleRelationSet may have changed
+ void InvalidateRelation( sal_uInt16 nType );
+
+ void InvalidateTextSelection(); // #i27301# - text selection has changed
+ void InvalidateAttr(); // #i88069# - attributes has changed
+
+ bool HasAdditionalAccessibleChildren();
+
+ // #i88070# - get additional child by index
+ vcl::Window* GetAdditionalAccessibleChild( const sal_Int32 nIndex );
+
+ // #i88070# - get all additional accessible children
+ void GetAdditionalAccessibleChildren( std::vector< vcl::Window* >* pChildren );
+
+ const OUString& GetName() const { return m_sName; }
+
+ virtual bool HasCursor(); // required by map to remember that object
+
+ bool Select( SwPaM *pPaM, SdrObject *pObj, bool bAdd );
+ bool Select( SwPaM& rPaM )
+ {
+ return Select( &rPaM, nullptr, false );
+ }
+ bool Select( SdrObject *pObj, bool bAdd )
+ {
+ return Select( nullptr, pObj, bAdd );
+ }
+
+ //This method is used to update the selected state and fire the selected state changed event.
+ virtual bool SetSelectedState(bool bSelected);
+ bool IsSelectedInDoc() const { return m_isSelectedInDoc; }
+
+ static OUString GetResource(TranslateId pResId,
+ const OUString *pArg1 = nullptr,
+ const OUString *pArg2 = nullptr);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accdoc.cxx b/sw/source/core/access/accdoc.cxx
new file mode 100644
index 000000000..4eb719242
--- /dev/null
+++ b/sw/source/core/access/accdoc.cxx
@@ -0,0 +1,716 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/window.hxx>
+#include <rootfrm.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <accmap.hxx>
+#include "accdoc.hxx"
+#include <strings.hrc>
+#include <pagefrm.hxx>
+
+#include <swatrset.hxx>
+#include <docsh.hxx>
+#include <crsrsh.hxx>
+#include <fesh.hxx>
+#include <fmtclds.hxx>
+#include <flyfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <section.hxx>
+#include <swmodule.hxx>
+#include <svtools/colorcfg.hxx>
+
+#include <fmtanchr.hxx>
+#include <viewimp.hxx>
+#include <dview.hxx>
+#include <dcontact.hxx>
+#include <svx/svdmark.hxx>
+constexpr OUStringLiteral sServiceName = u"com.sun.star.text.AccessibleTextDocumentView";
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleDocumentView";
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+using lang::IndexOutOfBoundsException;
+
+// SwAccessibleDocumentBase: base class for SwAccessibleDocument and
+// SwAccessiblePreview
+
+SwAccessibleDocumentBase::SwAccessibleDocumentBase(
+ std::shared_ptr<SwAccessibleMap> const& pMap)
+ : SwAccessibleContext(pMap, AccessibleRole::DOCUMENT_TEXT,
+ pMap->GetShell()->GetLayout())
+ , mxParent(pMap->GetShell()->GetWin()->GetAccessibleParentWindow()->GetAccessible())
+ , mpChildWin(nullptr)
+{
+}
+
+SwAccessibleDocumentBase::~SwAccessibleDocumentBase()
+{
+}
+
+void SwAccessibleDocumentBase::SetVisArea()
+{
+ SolarMutexGuard aGuard;
+
+ SwRect aOldVisArea( GetVisArea() );
+ const SwRect& rNewVisArea = GetMap()->GetVisArea();
+ if( aOldVisArea != rNewVisArea )
+ {
+ SwAccessibleFrame::SetVisArea( GetMap()->GetVisArea() );
+ // #i58139# - showing state of document view needs also be updated.
+ // Thus, call method <Scrolled(..)> instead of <ChildrenScrolled(..)>
+ // ChildrenScrolled( GetFrame(), aOldVisArea );
+ Scrolled( aOldVisArea );
+ }
+}
+
+void SwAccessibleDocumentBase::AddChild( vcl::Window *pWin, bool bFireEvent )
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( !mpChildWin, "only one child window is supported" );
+ if( !mpChildWin )
+ {
+ mpChildWin = pWin;
+
+ if( bFireEvent )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= mpChildWin->GetAccessible();
+ FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+void SwAccessibleDocumentBase::RemoveChild( vcl::Window *pWin )
+{
+ SolarMutexGuard aGuard;
+
+ OSL_ENSURE( !mpChildWin || pWin == mpChildWin, "invalid child window to remove" );
+ if( mpChildWin && pWin == mpChildWin )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.OldValue <<= mpChildWin->GetAccessible();
+ FireAccessibleEvent( aEvent );
+
+ mpChildWin = nullptr;
+ }
+}
+
+sal_Int32 SAL_CALL SwAccessibleDocumentBase::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+
+ // ThrowIfDisposed is called by parent
+
+ sal_Int32 nChildren = SwAccessibleContext::getAccessibleChildCount();
+ if( !IsDisposing() && mpChildWin )
+ nChildren++;
+
+ return nChildren;
+}
+
+uno::Reference< XAccessible> SAL_CALL
+ SwAccessibleDocumentBase::getAccessibleChild( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpChildWin )
+ {
+ ThrowIfDisposed();
+
+ if ( nIndex == GetChildCount( *(GetMap()) ) )
+ {
+ return mpChildWin->GetAccessible();
+ }
+ }
+
+ return SwAccessibleContext::getAccessibleChild( nIndex );
+}
+
+uno::Reference< XAccessible> SAL_CALL SwAccessibleDocumentBase::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int32 SAL_CALL SwAccessibleDocumentBase::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference < XAccessibleContext > xAcc( mxParent->getAccessibleContext() );
+ uno::Reference < XAccessible > xThis( this );
+ sal_Int32 nCount = xAcc->getAccessibleChildCount();
+
+ for( sal_Int32 i=0; i < nCount; i++ )
+ {
+ try
+ {
+ if( xAcc->getAccessibleChild( i ) == xThis )
+ return i;
+ }
+ catch(const css::lang::IndexOutOfBoundsException &)
+ {
+ return -1;
+ }
+ }
+ return -1;
+}
+
+OUString SAL_CALL SwAccessibleDocumentBase::getAccessibleDescription()
+{
+ return GetResource( STR_ACCESS_DOC_DESC );
+}
+
+OUString SAL_CALL SwAccessibleDocumentBase::getAccessibleName()
+{
+ SolarMutexGuard g;
+
+ OUString sAccName = GetResource( STR_ACCESS_DOC_WORDPROCESSING );
+ SwDoc *pDoc = GetMap() ? GetShell()->GetDoc() : nullptr;
+ if ( pDoc )
+ {
+ OUString sFileName = pDoc->getDocAccTitle();
+ if ( sFileName.isEmpty() )
+ {
+ SwDocShell* pDocSh = pDoc->GetDocShell();
+ if ( pDocSh )
+ {
+ sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME );
+ }
+ }
+
+ if ( !sFileName.isEmpty() )
+ {
+ sAccName = sFileName + " - " + sAccName;
+ }
+ }
+
+ return sAccName;
+}
+
+awt::Rectangle SAL_CALL SwAccessibleDocumentBase::getBounds()
+{
+ try
+ {
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ tools::Rectangle aPixBounds( pWin->GetWindowExtentsRelative( pWin->GetAccessibleParentWindow() ) );
+ awt::Rectangle aBox( aPixBounds.Left(), aPixBounds.Top(),
+ aPixBounds.GetWidth(), aPixBounds.GetHeight() );
+
+ return aBox;
+ }
+ catch(const css::lang::IndexOutOfBoundsException &)
+ {
+ return awt::Rectangle();
+ }
+}
+
+awt::Point SAL_CALL SwAccessibleDocumentBase::getLocation()
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ Point aPixPos( pWin->GetWindowExtentsRelative( pWin->GetAccessibleParentWindow() ).TopLeft() );
+ awt::Point aLoc( aPixPos.getX(), aPixPos.getY() );
+
+ return aLoc;
+}
+
+css::awt::Point SAL_CALL SwAccessibleDocumentBase::getLocationOnScreen()
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ Point aPixPos( pWin->GetWindowExtentsRelative( nullptr ).TopLeft() );
+ awt::Point aLoc( aPixPos.getX(), aPixPos.getY() );
+
+ return aLoc;
+}
+
+css::awt::Size SAL_CALL SwAccessibleDocumentBase::getSize()
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ Size aPixSize( pWin->GetWindowExtentsRelative( nullptr ).GetSize() );
+ awt::Size aSize( aPixSize.Width(), aPixSize.Height() );
+
+ return aSize;
+}
+
+sal_Bool SAL_CALL SwAccessibleDocumentBase::containsPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ tools::Rectangle aPixBounds( pWin->GetWindowExtentsRelative( nullptr ) );
+ aPixBounds.Move(-aPixBounds.Left(), -aPixBounds.Top());
+
+ Point aPixPoint( aPoint.X, aPoint.Y );
+ return aPixBounds.Contains( aPixPoint );
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleDocumentBase::getAccessibleAtPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpChildWin )
+ {
+ ThrowIfDisposed();
+
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+ if (pWin->isDisposed()) // tdf#147967
+ return nullptr;
+
+ Point aPixPoint( aPoint.X, aPoint.Y ); // px rel to window
+ if( mpChildWin->GetWindowExtentsRelative( pWin ).Contains( aPixPoint ) )
+ return mpChildWin->GetAccessible();
+ }
+
+ return SwAccessibleContext::getAccessibleAtPoint( aPoint );
+}
+
+// SwAccessibleDocument
+
+void SwAccessibleDocument::GetStates(
+ ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // MULTISELECTABLE
+ rStateSet.AddState( AccessibleStateType::MULTI_SELECTABLE );
+ rStateSet.AddState( AccessibleStateType::MANAGES_DESCENDANTS );
+}
+
+SwAccessibleDocument::SwAccessibleDocument(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap)
+ : SwAccessibleDocumentBase(pInitMap)
+ , maSelectionHelper(*this)
+{
+ SetName(pInitMap->GetDocName());
+ vcl::Window *pWin = pInitMap->GetShell()->GetWin();
+ if( pWin )
+ {
+ pWin->AddChildEventListener( LINK( this, SwAccessibleDocument, WindowChildEventListener ));
+ sal_uInt16 nCount = pWin->GetChildCount();
+ for( sal_uInt16 i=0; i < nCount; i++ )
+ {
+ vcl::Window* pChildWin = pWin->GetChild( i );
+ if( pChildWin &&
+ AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ AddChild( pChildWin, false );
+ }
+ }
+}
+
+SwAccessibleDocument::~SwAccessibleDocument()
+{
+ vcl::Window *pWin = GetMap() ? GetMap()->GetShell()->GetWin() : nullptr;
+ if( pWin )
+ pWin->RemoveChildEventListener( LINK( this, SwAccessibleDocument, WindowChildEventListener ));
+}
+
+void SwAccessibleDocument::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ OSL_ENSURE( GetFrame() && GetMap(), "already disposed" );
+
+ vcl::Window *pWin = GetMap() ? GetMap()->GetShell()->GetWin() : nullptr;
+ if( pWin )
+ pWin->RemoveChildEventListener( LINK( this, SwAccessibleDocument, WindowChildEventListener ));
+ SwAccessibleContext::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+IMPL_LINK( SwAccessibleDocument, WindowChildEventListener, VclWindowEvent&, rEvent, void )
+{
+ OSL_ENSURE( rEvent.GetWindow(), "Window???" );
+ switch ( rEvent.GetId() )
+ {
+ case VclEventId::WindowShow: // send create on show for direct accessible children
+ {
+ vcl::Window* pChildWin = static_cast< vcl::Window* >( rEvent.GetData() );
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ AddChild( pChildWin );
+ }
+ }
+ break;
+ case VclEventId::WindowHide: // send destroy on hide for direct accessible children
+ {
+ vcl::Window* pChildWin = static_cast< vcl::Window* >( rEvent.GetData() );
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ RemoveChild( pChildWin );
+ }
+ }
+ break;
+ case VclEventId::ObjectDying: // send destroy on hide for direct accessible children
+ {
+ vcl::Window* pChildWin = rEvent.GetWindow();
+ if( pChildWin && AccessibleRole::EMBEDDED_OBJECT == pChildWin->GetAccessibleRole() )
+ {
+ RemoveChild( pChildWin );
+ }
+ }
+ break;
+ default: break;
+ }
+}
+
+OUString SAL_CALL SwAccessibleDocument::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleDocument::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleDocument::getSupportedServiceNames()
+{
+ return { sServiceName, sAccessibleServiceName };
+}
+
+// XInterface
+
+uno::Any SwAccessibleDocument::queryInterface(
+ const uno::Type& rType )
+{
+ uno::Any aRet;
+ if ( rType == cppu::UnoType<XAccessibleSelection>::get() )
+ {
+ uno::Reference<XAccessibleSelection> aSelect = this;
+ aRet <<= aSelect;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleExtendedAttributes>::get())
+ {
+ uno::Reference<XAccessibleExtendedAttributes> aAttribute = this;
+ aRet <<= aAttribute;
+ }
+ else
+ aRet = SwAccessibleContext::queryInterface( rType );
+ return aRet;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleDocument::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<XAccessibleSelection>::get(),
+ SwAccessibleDocumentBase::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleDocument::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleSelection
+
+void SwAccessibleDocument::selectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ maSelectionHelper.selectAccessibleChild(nChildIndex);
+}
+
+sal_Bool SwAccessibleDocument::isAccessibleChildSelected(
+ sal_Int32 nChildIndex )
+{
+ return maSelectionHelper.isAccessibleChildSelected(nChildIndex);
+}
+
+void SwAccessibleDocument::clearAccessibleSelection( )
+{
+}
+
+void SwAccessibleDocument::selectAllAccessibleChildren( )
+{
+ maSelectionHelper.selectAllAccessibleChildren();
+}
+
+sal_Int32 SwAccessibleDocument::getSelectedAccessibleChildCount( )
+{
+ return maSelectionHelper.getSelectedAccessibleChildCount();
+}
+
+uno::Reference<XAccessible> SwAccessibleDocument::getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex )
+{
+ return maSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
+}
+
+// index has to be treated as global child index.
+void SwAccessibleDocument::deselectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ maSelectionHelper.deselectAccessibleChild( nChildIndex );
+}
+
+uno::Any SAL_CALL SwAccessibleDocument::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ uno::Any anyAttribute;
+ SwDoc *pDoc = GetMap() ? GetShell()->GetDoc() : nullptr;
+
+ if (!pDoc)
+ return anyAttribute;
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( !pCursorShell )
+ return anyAttribute;
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ if( pFEShell )
+ {
+ OUString sDisplay;
+ sal_uInt16 nPage, nLogPage;
+ pFEShell->GetPageNumber(-1,true,nPage,nLogPage,sDisplay);
+
+ OUString sValue = "page-name:" + sDisplay +
+ ";page-number:" +
+ OUString::number( nPage ) +
+ ";total-pages:" +
+ OUString::number( pCursorShell->GetPageCnt() ) + ";";
+
+ SwContentFrame* pCurrFrame = pCursorShell->GetCurrFrame();
+ SwPageFrame* pCurrPage=static_cast<SwFrame*>(pCurrFrame)->FindPageFrame();
+ sal_uLong nLineNum = 0;
+ SwTextFrame* pTextFrame = nullptr;
+ SwTextFrame* pCurrTextFrame = nullptr;
+ pTextFrame = static_cast< SwTextFrame* >(pCurrPage->ContainsContent());
+ if (pCurrFrame->IsInFly())//such as, graphic,chart
+ {
+ SwFlyFrame *pFlyFrame = pCurrFrame->FindFlyFrame();
+ const SwFormatAnchor& rAnchor = pFlyFrame->GetFormat()->GetAnchor();
+ RndStdIds eAnchorId = rAnchor.GetAnchorId();
+ if(eAnchorId == RndStdIds::FLY_AS_CHAR)
+ {
+ const SwFrame *pSwFrame = pFlyFrame->GetAnchorFrame();
+ if(pSwFrame->IsTextFrame())
+ pCurrTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pSwFrame));
+ }
+ }
+ else
+ {
+ assert(dynamic_cast<SwTextFrame*>(pCurrFrame));
+ pCurrTextFrame = static_cast<SwTextFrame* >(pCurrFrame);
+ }
+ //check whether the text frame where the Graph/OLE/Frame anchored is in the Header/Footer
+ SwFrame* pFrame = pCurrTextFrame;
+ while ( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() )
+ pFrame = pFrame->GetUpper();
+ if ( pFrame )
+ pCurrTextFrame = nullptr;
+ //check shape
+ if(pCursorShell->Imp()->GetDrawView())
+ {
+ const SdrMarkList &rMrkList = pCursorShell->Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark(i)->GetMarkedSdrObj();
+ SwFrameFormat* pFormat = static_cast<SwDrawContact*>(pObj->GetUserCall())->GetFormat();
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if( RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId() )
+ pCurrTextFrame = nullptr;
+ }
+ }
+ //calculate line number
+ if (pCurrTextFrame && pTextFrame)
+ {
+ if (!(pCurrTextFrame->IsInTab() || pCurrTextFrame->IsInFootnote()))
+ {
+ while( pTextFrame && pTextFrame != pCurrTextFrame )
+ {
+ //check header/footer
+ pFrame = pTextFrame;
+ while ( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() )
+ pFrame = pFrame->GetUpper();
+ if ( pFrame )
+ {
+ pTextFrame = static_cast< SwTextFrame*>(pTextFrame->GetNextContentFrame());
+ continue;
+ }
+ if (!(pTextFrame->IsInTab() || pTextFrame->IsInFootnote() || pTextFrame->IsInFly()))
+ nLineNum += pTextFrame->GetThisLines();
+ pTextFrame = static_cast< SwTextFrame* >(pTextFrame ->GetNextContentFrame());
+ }
+ SwPaM* pCaret = pCursorShell->GetCursor();
+ if (!pCurrTextFrame->IsEmpty() && pCaret)
+ {
+ assert(pCurrTextFrame->IsTextFrame());
+ const SwPosition* pPoint = nullptr;
+ if (pCurrTextFrame->IsInFly())
+ {
+ SwFlyFrame *pFlyFrame = pCurrTextFrame->FindFlyFrame();
+ const SwFormatAnchor& rAnchor = pFlyFrame->GetFormat()->GetAnchor();
+ pPoint = rAnchor.GetContentAnchor();
+ SwContentNode *const pNode(pPoint->nNode.GetNode().GetContentNode());
+ pCurrTextFrame = pNode
+ ? static_cast<SwTextFrame*>(pNode->getLayoutFrame(
+ pCurrTextFrame->getRootFrame(), pPoint))
+ : nullptr;
+ }
+ else
+ pPoint = pCaret->GetPoint();
+ if (pCurrTextFrame)
+ {
+ TextFrameIndex const nActPos(pCurrTextFrame->MapModelToViewPos(*pPoint));
+ nLineNum += pCurrTextFrame->GetLineCount( nActPos );
+ }
+ }
+ else
+ ++nLineNum;
+ }
+ }
+
+ sValue += "line-number:" + OUString::number( nLineNum ) + ";";
+
+ SwFrame* pCurrCol=static_cast<SwFrame*>(pCurrFrame)->FindColFrame();
+
+ sValue += "column-number:";
+
+ int nCurrCol = 1;
+ if(pCurrCol!=nullptr)
+ {
+ //SwLayoutFrame* pParent = pCurrCol->GetUpper();
+ SwFrame* pCurrPageCol=static_cast<SwFrame*>(pCurrFrame)->FindColFrame();
+ while(pCurrPageCol && pCurrPageCol->GetUpper() && pCurrPageCol->GetUpper()->IsPageFrame())
+ {
+ pCurrPageCol = pCurrPageCol->GetUpper();
+ }
+
+ SwLayoutFrame* pParent = pCurrPageCol->GetUpper();
+
+ if(pParent!=nullptr)
+ {
+ SwFrame* pCol = pParent->Lower();
+ while(pCol&&(pCol!=pCurrPageCol))
+ {
+ pCol = pCol->GetNext();
+ ++nCurrCol;
+ }
+ }
+ }
+ sValue += OUString::number( nCurrCol ) + ";";
+
+ const SwFormatCol &rFormatCol=pCurrPage->GetAttrSet()->GetCol();
+ sal_uInt16 nColCount=rFormatCol.GetNumCols();
+ nColCount = nColCount>0?nColCount:1;
+ sValue += "total-columns:" + OUString::number( nColCount ) + ";";
+
+ SwSectionFrame* pCurrSctFrame=static_cast<SwFrame*>(pCurrFrame)->FindSctFrame();
+ if(pCurrSctFrame!=nullptr && pCurrSctFrame->GetSection()!=nullptr )
+ {
+ OUString sectionName = pCurrSctFrame->GetSection()->GetSectionName();
+
+ sectionName = sectionName.replaceFirst( "\\" , "\\\\" );
+ sectionName = sectionName.replaceFirst( "=" , "\\=" );
+ sectionName = sectionName.replaceFirst( ";" , "\\;" );
+ sectionName = sectionName.replaceFirst( "," , "\\," );
+ sectionName = sectionName.replaceFirst( ":" , "\\:" );
+
+ sValue += "section-name:" + sectionName + ";";
+
+ //section-columns-number
+
+ nCurrCol = 1;
+
+ if(pCurrCol!=nullptr)
+ {
+ SwLayoutFrame* pParent = pCurrCol->GetUpper();
+ if(pParent!=nullptr)
+ {
+ SwFrame* pCol = pParent->Lower();
+ while(pCol&&(pCol!=pCurrCol))
+ {
+ pCol = pCol->GetNext();
+ nCurrCol +=1;
+ }
+ }
+ }
+ sValue += "section-columns-number:" +
+ OUString::number( nCurrCol ) + ";";
+
+ //section-total-columns
+ const SwFormatCol &rFormatSctCol=pCurrSctFrame->GetAttrSet()->GetCol();
+ sal_uInt16 nSctColCount=rFormatSctCol.GetNumCols();
+ nSctColCount = nSctColCount>0?nSctColCount:1;
+ sValue += "section-total-columns:" +
+ OUString::number( nSctColCount ) + ";";
+ }
+
+ anyAttribute <<= sValue;
+ }
+ return anyAttribute;
+}
+
+sal_Int32 SAL_CALL SwAccessibleDocument::getBackground()
+{
+ SolarMutexGuard aGuard;
+ return sal_Int32(SW_MOD()->GetColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accdoc.hxx b/sw/source/core/access/accdoc.hxx
new file mode 100644
index 000000000..6f16cc585
--- /dev/null
+++ b/sw/source/core/access/accdoc.hxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "acccontext.hxx"
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+#include "accselectionhelper.hxx"
+#include <vcl/window.hxx>
+
+// base class for SwAccessibleDocument (in this same header file) and
+// SwAccessiblePreview
+class SwAccessibleDocumentBase : public SwAccessibleContext
+{
+ css::uno::Reference< css::accessibility::XAccessible> mxParent;
+
+ VclPtr<vcl::Window> mpChildWin; // protected by solar mutex
+
+ using SwAccessibleFrame::SetVisArea;
+
+protected:
+ virtual ~SwAccessibleDocumentBase() override;
+
+public:
+ SwAccessibleDocumentBase(std::shared_ptr<SwAccessibleMap> const& pInitMap);
+
+ void SetVisArea();
+
+ void AddChild( vcl::Window *pWin, bool bFireEvent = true );
+ void RemoveChild( vcl::Window *pWin );
+
+ // XAccessibleContext
+
+ // Return the number of currently visible children.
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+
+ // Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int32 nIndex) override;
+
+ // Return a reference to the parent.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleParent() override;
+
+ // Return this objects index among the parents children.
+ virtual sal_Int32 SAL_CALL
+ getAccessibleIndexInParent() override;
+
+ // Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ virtual OUString SAL_CALL getAccessibleName() override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(
+ const css::awt::Point& aPoint ) override;
+
+ virtual css::awt::Rectangle SAL_CALL getBounds() override;
+
+ virtual css::awt::Point SAL_CALL getLocation() override;
+
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override;
+
+ virtual css::awt::Size SAL_CALL getSize() override;
+};
+
+/**
+ * access to an accessible Writer document
+ */
+class SwAccessibleDocument : public SwAccessibleDocumentBase,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleExtendedAttributes
+{
+ // Implementation for XAccessibleSelection interface
+ SwAccessibleSelectionHelper maSelectionHelper;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets MULTISELECTABLE(1)
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet ) override;
+
+ virtual ~SwAccessibleDocument() override;
+
+public:
+ SwAccessibleDocument(std::shared_ptr<SwAccessibleMap> const& pInitMap);
+
+ DECL_LINK( WindowChildEventListener, VclWindowEvent&, void );
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XInterface
+
+ // XInterface is inherited through SwAccessibleContext and
+ // XAccessibleSelection. These methods are needed to avoid
+ // ambiguities.
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleSelection
+
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int32 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int32 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex ) override;
+
+ // index has to be treated as global child index.
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override;
+
+ // thread safe C++ interface
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accembedded.cxx b/sw/source/core/access/accembedded.cxx
new file mode 100644
index 000000000..1eb54cf25
--- /dev/null
+++ b/sw/source/core/access/accembedded.cxx
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <flyfrm.hxx>
+#include "accembedded.hxx"
+#include <cntfrm.hxx>
+#include <notxtfrm.hxx>
+#include <ndole.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleEmbeddedObject";
+
+SwAccessibleEmbeddedObject::SwAccessibleEmbeddedObject(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleNoTextFrame( pInitMap, AccessibleRole::EMBEDDED_OBJECT, pFlyFrame )
+{
+}
+
+SwAccessibleEmbeddedObject::~SwAccessibleEmbeddedObject()
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ SwAccessibleEmbeddedObject::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = SwAccessibleNoTextFrame::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast< css::accessibility::XAccessibleExtendedAttributes* >(this) );
+ return aReturn;
+}
+
+void SAL_CALL
+ SwAccessibleEmbeddedObject::acquire()
+ noexcept
+{
+ SwAccessibleNoTextFrame::acquire ();
+}
+
+void SAL_CALL
+ SwAccessibleEmbeddedObject::release()
+ noexcept
+{
+ SwAccessibleNoTextFrame::release ();
+}
+
+OUString SAL_CALL SwAccessibleEmbeddedObject::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleEmbeddedObject::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleEmbeddedObject::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.AccessibleTextEmbeddedObject", sAccessibleServiceName };
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleEmbeddedObject::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleExtendedAttributes
+css::uno::Any SAL_CALL SwAccessibleEmbeddedObject::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ css::uno::Any strRet;
+ OUString style;
+ SwFlyFrame* pFFrame = getFlyFrame();
+
+ if( pFFrame )
+ {
+ style = "style:";
+ SwContentFrame* pCFrame;
+ pCFrame = pFFrame->ContainsContent();
+ if( pCFrame )
+ {
+ assert(pCFrame->IsNoTextFrame());
+ SwContentNode *const pCNode = static_cast<SwNoTextFrame*>(pCFrame)->GetNode();
+ if( pCNode )
+ {
+ style += static_cast<SwOLENode*>(pCNode)->GetOLEObj().GetStyleString();
+ }
+ }
+ style += ";";
+ }
+ strRet <<= style;
+ return strRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accembedded.hxx b/sw/source/core/access/accembedded.hxx
new file mode 100644
index 000000000..ce82af9e2
--- /dev/null
+++ b/sw/source/core/access/accembedded.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "accnotextframe.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+
+class SwAccessibleEmbeddedObject : public SwAccessibleNoTextFrame
+ , public css::accessibility::XAccessibleExtendedAttributes
+
+{
+protected:
+ virtual ~SwAccessibleEmbeddedObject() override;
+
+public:
+ SwAccessibleEmbeddedObject(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame* pFlyFrame );
+
+ // XInterface
+
+ virtual css::uno::Any SAL_CALL
+ queryInterface (const css::uno::Type & rType) override;
+
+ virtual void SAL_CALL
+ acquire()
+ noexcept override;
+
+ virtual void SAL_CALL
+ release()
+ noexcept override;
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleExtendedAttributes
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override ;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfootnote.cxx b/sw/source/core/access/accfootnote.cxx
new file mode 100644
index 000000000..6fb7ebca8
--- /dev/null
+++ b/sw/source/core/access/accfootnote.cxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <ftnfrm.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <viewsh.hxx>
+#include <accmap.hxx>
+#include "accfootnote.hxx"
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+
+constexpr OUStringLiteral sImplementationNameFootnote
+ = u"com.sun.star.comp.Writer.SwAccessibleFootnoteView";
+constexpr OUStringLiteral sImplementationNameEndnote
+ = u"com.sun.star.comp.Writer.SwAccessibleEndnoteView";
+
+SwAccessibleFootnote::SwAccessibleFootnote(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ bool bIsEndnote,
+ const SwFootnoteFrame *pFootnoteFrame ) :
+ SwAccessibleContext( pInitMap,
+ bIsEndnote ? AccessibleRole::END_NOTE : AccessibleRole::FOOTNOTE,
+ pFootnoteFrame )
+{
+ TranslateId pResId = bIsEndnote ? STR_ACCESS_ENDNOTE_NAME
+ : STR_ACCESS_FOOTNOTE_NAME;
+
+ OUString sArg;
+ const SwTextFootnote *pTextFootnote =
+ static_cast< const SwFootnoteFrame *>( GetFrame() )->GetAttr();
+ if( pTextFootnote )
+ {
+ const SwDoc *pDoc = GetShell()->GetDoc();
+ sArg = pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, pFootnoteFrame->getRootFrame());
+ }
+
+ SetName(GetResource(pResId, &sArg));
+}
+
+SwAccessibleFootnote::~SwAccessibleFootnote()
+{
+}
+
+OUString SAL_CALL SwAccessibleFootnote::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ TranslateId pResId = AccessibleRole::END_NOTE == GetRole()
+ ? STR_ACCESS_ENDNOTE_DESC
+ : STR_ACCESS_FOOTNOTE_DESC ;
+
+ OUString sArg;
+ const SwTextFootnote *pTextFootnote =
+ static_cast< const SwFootnoteFrame *>( GetFrame() )->GetAttr();
+ if( pTextFootnote )
+ {
+ const SwDoc *pDoc = GetMap()->GetShell()->GetDoc();
+ sArg = pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, GetFrame()->getRootFrame());
+ }
+
+ return GetResource(pResId, &sArg);
+}
+
+OUString SAL_CALL SwAccessibleFootnote::getImplementationName()
+{
+ if( AccessibleRole::END_NOTE == GetRole() )
+ return sImplementationNameEndnote;
+ else
+ return sImplementationNameFootnote;
+}
+
+sal_Bool SAL_CALL SwAccessibleFootnote::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+Sequence< OUString > SAL_CALL SwAccessibleFootnote::getSupportedServiceNames()
+{
+ return { (AccessibleRole::END_NOTE == GetRole())?OUString("com.sun.star.text.AccessibleEndnoteView"):OUString("com.sun.star.text.AccessibleFootnoteView"),
+ sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessibleFootnote::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+bool SwAccessibleFootnote::IsEndnote( const SwFootnoteFrame *pFootnoteFrame )
+{
+ const SwTextFootnote *pTextFootnote = pFootnoteFrame ->GetAttr();
+ return pTextFootnote && pTextFootnote->GetFootnote().IsEndNote() ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfootnote.hxx b/sw/source/core/access/accfootnote.hxx
new file mode 100644
index 000000000..ecc852cd5
--- /dev/null
+++ b/sw/source/core/access/accfootnote.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include "acccontext.hxx"
+
+class SwAccessibleMap;
+class SwFootnoteFrame;
+
+class SwAccessibleFootnote : public SwAccessibleContext
+{
+protected:
+ virtual ~SwAccessibleFootnote() override;
+
+public:
+ SwAccessibleFootnote( std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ bool bIsEndnote,
+ const SwFootnoteFrame *pFootnoteFrame );
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object. */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service. */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ static bool IsEndnote( const SwFootnoteFrame *pFrame );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframe.cxx b/sw/source/core/access/accframe.cxx
new file mode 100644
index 000000000..684953c6d
--- /dev/null
+++ b/sw/source/core/access/accframe.cxx
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/brushitem.hxx>
+#include <flyfrm.hxx>
+#include <sectfrm.hxx>
+#include <section.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <pagefrm.hxx>
+#include <pagedesc.hxx>
+#include <fldbas.hxx>
+#include <accmap.hxx>
+#include "accfrmobjslist.hxx"
+#include "accfrmobjmap.hxx"
+#include "accframe.hxx"
+
+using namespace sw::access;
+
+// Regarding visibility (or in terms of accessibility: regarding the showing
+// state): A frame is visible and therefore contained in the tree if its frame
+// size overlaps with the visible area. The bounding box however is the
+// frame's paint area.
+sal_Int32 SwAccessibleFrame::GetChildCount( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame *pFrame,
+ bool bInPagePreview )
+{
+ sal_Int32 nCount = 0;
+
+ const SwAccessibleChildSList aVisList( rVisArea, *pFrame, rAccMap );
+
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ nCount++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ nCount += GetChildCount( rAccMap,
+ rVisArea, rLower.GetSwFrame(),
+ bInPagePreview );
+ }
+ ++aIter;
+ }
+
+ return nCount;
+}
+
+SwAccessibleChild SwAccessibleFrame::GetChild(
+ SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ sal_Int32& rPos,
+ bool bInPagePreview )
+{
+ SwAccessibleChild aRet;
+
+ if( rPos >= 0 )
+ {
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_iterator aIter( aVisMap.cbegin() );
+ while( aIter != aVisMap.cend() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = (*aIter).second;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( 0 == rPos )
+ aRet = rLower;
+ else
+ rPos--;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChild( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rPos,
+ bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order.
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( 0 == rPos )
+ aRet = rLower;
+ else
+ rPos--;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChild( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rPos,
+ bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ }
+
+ return aRet;
+}
+
+bool SwAccessibleFrame::GetChildIndex(
+ SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const SwAccessibleChild& rChild,
+ sal_Int32& rPos,
+ bool bInPagePreview )
+{
+ bool bFound = false;
+
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_iterator aIter( aVisMap.cbegin() );
+ while( aIter != aVisMap.cend() && !bFound )
+ {
+ const SwAccessibleChild& rLower = (*aIter).second;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( rChild == rLower )
+ bFound = true;
+ else
+ rPos++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ bFound = GetChildIndex( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rChild,
+ rPos, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order.
+
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() && !bFound )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ if( rChild == rLower )
+ bFound = true;
+ else
+ rPos++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ bFound = GetChildIndex( rAccMap,
+ rVisArea, *(rLower.GetSwFrame()), rChild,
+ rPos, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+
+ return bFound;
+}
+
+SwAccessibleChild SwAccessibleFrame::GetChildAtPixel( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const Point& rPixPos,
+ bool bInPagePreview,
+ SwAccessibleMap& rAccMap )
+{
+ SwAccessibleChild aRet;
+
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here, and we have to reverse iterate,
+ // because objects in front should be returned.
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_reverse_iterator aRIter( aVisMap.crbegin() );
+ while( aRIter != aVisMap.crend() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = (*aRIter).second;
+ // A frame is returned if it's frame size is inside the visarea
+ // and the position is inside the frame's paint area.
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ SwRect aLogBounds( rLower.GetBounds( rAccMap ) );
+ if( !aLogBounds.IsEmpty() )
+ {
+ tools::Rectangle aPixBounds( rAccMap.CoreToPixel( aLogBounds ) );
+ if( aPixBounds.Contains( rPixPos ) )
+ aRet = rLower;
+ }
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChildAtPixel( rVisArea, *(rLower.GetSwFrame()), rPixPos,
+ bInPagePreview, rAccMap );
+ }
+ ++aRIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order. Moreover, we can iterate forward,
+ // because the lowers don't overlap!
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() && !aRet.IsValid() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ // A frame is returned if it's frame size is inside the visarea
+ // and the position is inside the frame's paint area.
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ SwRect aLogBounds( rLower.GetBounds( rAccMap ) );
+ if( !aLogBounds.IsEmpty() )
+ {
+ tools::Rectangle aPixBounds( rAccMap.CoreToPixel( aLogBounds ) );
+ if( aPixBounds.Contains( rPixPos ) )
+ aRet = rLower;
+ }
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ aRet = GetChildAtPixel( rVisArea, *(rLower.GetSwFrame()), rPixPos,
+ bInPagePreview, rAccMap );
+ }
+ ++aIter;
+ }
+ }
+
+ return aRet;
+}
+
+void SwAccessibleFrame::GetChildren( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ std::list< SwAccessibleChild >& rChildren,
+ bool bInPagePreview )
+{
+ if( SwAccessibleChildMap::IsSortingRequired( rFrame ) )
+ {
+ // We need a sorted list here
+ const SwAccessibleChildMap aVisMap( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildMap::const_iterator aIter( aVisMap.cbegin() );
+ while( aIter != aVisMap.cend() )
+ {
+ const SwAccessibleChild& rLower = (*aIter).second;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ rChildren.push_back( rLower );
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ GetChildren( rAccMap, rVisArea, *(rLower.GetSwFrame()),
+ rChildren, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+ else
+ {
+ // The unsorted list is sorted enough, because it returns lower
+ // frames in the correct order.
+ const SwAccessibleChildSList aVisList( rVisArea, rFrame, rAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( bInPagePreview ) )
+ {
+ rChildren.push_back( rLower );
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ GetChildren( rAccMap, rVisArea, *(rLower.GetSwFrame()),
+ rChildren, bInPagePreview );
+ }
+ ++aIter;
+ }
+ }
+}
+
+SwRect SwAccessibleFrame::GetBounds( const SwAccessibleMap& rAccMap,
+ const SwFrame *pFrame )
+{
+ if( !pFrame )
+ pFrame = GetFrame();
+
+ SwAccessibleChild aFrame( pFrame );
+ SwRect aBounds( aFrame.GetBounds( rAccMap ).Intersection( maVisArea ) );
+ return aBounds;
+}
+
+bool SwAccessibleFrame::IsEditable( SwViewShell const *pVSh ) const
+{
+ const SwFrame *pFrame = GetFrame();
+ if( !pFrame )
+ return false;
+
+ OSL_ENSURE( pVSh, "no view shell" );
+ if( pVSh && (pVSh->GetViewOptions()->IsReadonly() ||
+ pVSh->IsPreview()) )
+ return false;
+
+ if( !pFrame->IsRootFrame() && pFrame->IsProtected() )
+ return false;
+
+ return true;
+}
+
+bool SwAccessibleFrame::IsOpaque( SwViewShell const *pVSh ) const
+{
+ SwAccessibleChild aFrame( GetFrame() );
+ if( !aFrame.GetSwFrame() )
+ return false;
+
+ OSL_ENSURE( pVSh, "no view shell" );
+ if( !pVSh )
+ return false;
+
+ const SwViewOption *pVOpt = pVSh->GetViewOptions();
+ do
+ {
+ const SwFrame *pFrame = aFrame.GetSwFrame();
+ if( pFrame->IsRootFrame() )
+ return true;
+
+ if( pFrame->IsPageFrame() && !pVOpt->IsPageBack() )
+ return false;
+
+ const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();
+ if( !rBack.GetColor().IsTransparent() ||
+ rBack.GetGraphicPos() != GPOS_NONE )
+ return true;
+
+ // If a fly frame has a transparent background color, we have to consider the background.
+ // But a background color "no fill"/"auto fill" should *not* be considered.
+ if( pFrame->IsFlyFrame() &&
+ rBack.GetColor().IsTransparent() &&
+ rBack.GetColor() != COL_TRANSPARENT
+ )
+ return true;
+
+ if( pFrame->IsSctFrame() )
+ {
+ const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
+ if( pSection && ( SectionType::ToxHeader == pSection->GetType() ||
+ SectionType::ToxContent == pSection->GetType() ) &&
+ !pVOpt->IsReadonly() &&
+ SwViewOption::IsIndexShadings() )
+ return true;
+ }
+ if( pFrame->IsFlyFrame() )
+ aFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ else
+ aFrame = pFrame->GetUpper();
+ } while( aFrame.GetSwFrame() && !aFrame.IsAccessible( IsInPagePreview() ) );
+
+ return false;
+}
+
+SwAccessibleFrame::SwAccessibleFrame( const SwRect& rVisArea,
+ const SwFrame *pF,
+ bool bIsPagePreview ) :
+ maVisArea( rVisArea ),
+ mpFrame( pF ),
+ mbIsInPagePreview( bIsPagePreview )
+{
+ assert(mpFrame);
+}
+
+SwAccessibleFrame::~SwAccessibleFrame()
+{
+}
+
+const SwFrame* SwAccessibleFrame::GetParent( const SwAccessibleChild& rFrameOrObj,
+ bool bInPagePreview )
+{
+ return rFrameOrObj.GetParent( bInPagePreview );
+}
+
+OUString SwAccessibleFrame::GetFormattedPageNumber() const
+{
+ sal_uInt16 nPageNum = GetFrame()->GetVirtPageNum();
+ SvxNumType nFormat = GetFrame()->FindPageFrame()->GetPageDesc()
+ ->GetNumType().GetNumberingType();
+ if( SVX_NUM_NUMBER_NONE == nFormat )
+ nFormat = SVX_NUM_ARABIC;
+
+ OUString sRet( FormatNumber( nPageNum, nFormat ) );
+ return sRet;
+}
+
+sal_Int32 SwAccessibleFrame::GetChildCount( SwAccessibleMap& rAccMap ) const
+{
+ return GetChildCount( rAccMap, maVisArea, mpFrame, IsInPagePreview() );
+}
+
+sw::access::SwAccessibleChild SwAccessibleFrame::GetChild(
+ SwAccessibleMap& rAccMap,
+ sal_Int32 nPos ) const
+{
+ return SwAccessibleFrame::GetChild( rAccMap, maVisArea, *mpFrame, nPos, IsInPagePreview() );
+}
+
+sal_Int32 SwAccessibleFrame::GetChildIndex( SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rChild ) const
+{
+ sal_Int32 nPos = 0;
+ return GetChildIndex( rAccMap, maVisArea, *mpFrame, rChild, nPos, IsInPagePreview() )
+ ? nPos
+ : -1;
+}
+
+sw::access::SwAccessibleChild SwAccessibleFrame::GetChildAtPixel(
+ const Point& rPos,
+ SwAccessibleMap& rAccMap ) const
+{
+ return GetChildAtPixel( maVisArea, *mpFrame, rPos, IsInPagePreview(), rAccMap );
+}
+
+void SwAccessibleFrame::GetChildren( SwAccessibleMap& rAccMap,
+ std::list< sw::access::SwAccessibleChild >& rChildren ) const
+{
+ GetChildren( rAccMap, maVisArea, *mpFrame, rChildren, IsInPagePreview() );
+}
+
+bool SwAccessibleFrame::IsShowing( const SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rFrameOrObj ) const
+{
+ return IsShowing( rFrameOrObj.GetBox( rAccMap ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframe.hxx b/sw/source/core/access/accframe.hxx
new file mode 100644
index 000000000..6f2dff0ae
--- /dev/null
+++ b/sw/source/core/access/accframe.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRAME_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRAME_HXX
+
+#include <swrect.hxx>
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#include <list>
+#include "accfrmobj.hxx"
+
+class SwAccessibleMap;
+class SwFrame;
+class SwViewShell;
+
+// Any method of this class must be called with an acquired solar mutex!
+
+class SwAccessibleFrame
+{
+ SwRect maVisArea;
+ const SwFrame* mpFrame;
+ const bool mbIsInPagePreview;
+
+protected:
+ // #i77106# - method needs to be called by new class <SwAccessibleTableColHeaders>
+ static sal_Int32 GetChildCount( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame *pFrame,
+ bool bInPagePreviewr );
+
+// private:
+ static sw::access::SwAccessibleChild GetChild( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ sal_Int32& rPos,
+ bool bInPagePreview);
+
+ static bool GetChildIndex( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const sw::access::SwAccessibleChild& rChild,
+ sal_Int32& rPos,
+ bool bInPagePreview );
+
+ static sw::access::SwAccessibleChild GetChildAtPixel( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ const Point& rPos,
+ bool bInPagePreview,
+ SwAccessibleMap& rAccMap );
+
+ static void GetChildren( SwAccessibleMap& rAccMap,
+ const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ std::list< sw::access::SwAccessibleChild >& rChildren,
+ bool bInPagePreview );
+
+ bool IsEditable( SwViewShell const *pVSh ) const;
+
+ bool IsOpaque( SwViewShell const *pVSh ) const;
+
+public:
+ bool IsShowing( const SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rFrameOrObj ) const;
+ inline bool IsShowing( const SwRect& rFrame ) const;
+ inline bool IsShowing( const SwAccessibleMap& rAccMap ) const;
+
+protected:
+ bool IsInPagePreview() const
+ {
+ return mbIsInPagePreview;
+ }
+
+ void ClearFrame()
+ {
+ mpFrame = nullptr;
+ }
+
+ SwAccessibleFrame( const SwRect& rVisArea,
+ const SwFrame *pFrame,
+ bool bIsPagePreview );
+ virtual ~SwAccessibleFrame();
+public:
+ // Return the SwFrame this context is attached to.
+ const SwFrame* GetFrame() const { return mpFrame; };
+
+ static const SwFrame* GetParent( const sw::access::SwAccessibleChild& rFrameOrObj,
+ bool bInPagePreview );
+
+ sal_Int32 GetChildIndex( SwAccessibleMap& rAccMap,
+ const sw::access::SwAccessibleChild& rChild ) const;
+
+protected:
+ // Return the bounding box of the frame clipped to the visible area.
+ // If no frame is specified, use this' frame.
+ SwRect GetBounds( const SwAccessibleMap& rAccMap,
+ const SwFrame *pFrame = nullptr );
+
+ // Return the upper that has a context attached. This might be
+ // another one than the immediate upper.
+ inline const SwFrame *GetParent() const;
+
+ // Return the lower count or the nth lower, there the lowers have a
+ // not be same one as the SwFrame's lowers
+ sal_Int32 GetChildCount( SwAccessibleMap& rAccMap ) const;
+ sw::access::SwAccessibleChild GetChild( SwAccessibleMap& rAccMap,
+ sal_Int32 nPos ) const;
+ sw::access::SwAccessibleChild GetChildAtPixel( const Point& rPos,
+ SwAccessibleMap& rAccMap ) const;
+ void GetChildren( SwAccessibleMap& rAccMap,
+ std::list< sw::access::SwAccessibleChild >& rChildren ) const;
+
+ void SetVisArea( const SwRect& rNewVisArea )
+ {
+ maVisArea = rNewVisArea;
+ }
+
+ const SwRect& GetVisArea() const
+ {
+ return maVisArea;
+ }
+
+ OUString GetFormattedPageNumber() const;
+};
+
+inline bool SwAccessibleFrame::IsShowing( const SwRect& rFrame ) const
+{
+ return !rFrame.IsEmpty() && rFrame.Overlaps( maVisArea );
+}
+
+inline bool SwAccessibleFrame::IsShowing( const SwAccessibleMap& rAccMap ) const
+{
+ sw::access::SwAccessibleChild aFrameOrObj( GetFrame() );
+ return IsShowing( rAccMap, aFrameOrObj );
+}
+
+inline const SwFrame *SwAccessibleFrame::GetParent() const
+{
+ sw::access::SwAccessibleChild aFrameOrObj( GetFrame() );
+ return GetParent( aFrameOrObj, IsInPagePreview() );
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframebase.cxx b/sw/source/core/access/accframebase.cxx
new file mode 100644
index 000000000..c9a1ea73e
--- /dev/null
+++ b/sw/source/core/access/accframebase.cxx
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <frmfmt.hxx>
+#include <flyfrm.hxx>
+#include <fmtcntnt.hxx>
+#include <ndindex.hxx>
+#include <fesh.hxx>
+#include <hints.hxx>
+#include <accmap.hxx>
+#include "accframebase.hxx"
+
+#include <crsrsh.hxx>
+#include <notxtfrm.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <fmtanchr.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+bool SwAccessibleFrameBase::IsSelected()
+{
+ bool bRet = false;
+
+ assert(GetMap());
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+ if( auto pFESh = dynamic_cast<const SwFEShell*>(pVSh) )
+ {
+ const SwFrame *pFlyFrame = pFESh->GetSelectedFlyFrame();
+ if( pFlyFrame == GetFrame() )
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void SwAccessibleFrameBase::GetStates(
+ ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ const SwViewShell *pVSh = GetMap()->GetShell();
+ assert(pVSh);
+
+ if (dynamic_cast<const SwFEShell*>(pVSh))
+ {
+ // SELECTABLE
+ rStateSet.AddState(AccessibleStateType::SELECTABLE);
+ // FOCUSABLE
+ rStateSet.AddState(AccessibleStateType::FOCUSABLE);
+ }
+
+ // SELECTED and FOCUSED
+ if( IsSelected() )
+ {
+ rStateSet.AddState( AccessibleStateType::SELECTED );
+ SAL_WARN_IF(!m_bIsSelected, "sw.a11y", "bSelected out of sync");
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ }
+ if( GetSelectedState() )
+ rStateSet.AddState( AccessibleStateType::SELECTED );
+}
+
+SwNodeType SwAccessibleFrameBase::GetNodeType( const SwFlyFrame *pFlyFrame )
+{
+ SwNodeType nType = SwNodeType::Text;
+ if( pFlyFrame->Lower() )
+ {
+ if( pFlyFrame->Lower()->IsNoTextFrame() )
+ {
+ const SwNoTextFrame *const pContentFrame =
+ static_cast<const SwNoTextFrame *>(pFlyFrame->Lower());
+ nType = pContentFrame->GetNode()->GetNodeType();
+ }
+ }
+ else
+ {
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ const SwNodeIndex *pNdIdx = rContent.GetContentIdx();
+ if( pNdIdx )
+ {
+ const SwContentNode *pCNd =
+ (pNdIdx->GetNodes())[pNdIdx->GetIndex()+1]->GetContentNode();
+ if( pCNd )
+ nType = pCNd->GetNodeType();
+ }
+ }
+
+ return nType;
+}
+
+SwAccessibleFrameBase::SwAccessibleFrameBase(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleContext( pInitMap, nInitRole, pFlyFrame ),
+ m_bIsSelected( false )
+{
+ const SwFrameFormat* pFrameFormat = pFlyFrame->GetFormat();
+ if(pFrameFormat)
+ StartListening(const_cast<SwFrameFormat*>(pFrameFormat)->GetNotifier());
+
+ SetName( pFrameFormat->GetName() );
+
+ m_bIsSelected = IsSelected();
+}
+
+void SwAccessibleFrameBase::InvalidateCursorPos_()
+{
+ bool bNewSelected = IsSelected();
+ bool bOldSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOldSelected = m_bIsSelected;
+ m_bIsSelected = bNewSelected;
+ }
+
+ if( bNewSelected )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ if( bOldSelected == bNewSelected )
+ return;
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() && bNewSelected )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected );
+ if( pWin && pWin->HasFocus() && !bNewSelected )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected );
+ if(!bNewSelected)
+ return;
+
+ uno::Reference< XAccessible > xParent( GetWeakParent() );
+ if( xParent.is() )
+ {
+ SwAccessibleContext *pAcc =
+ static_cast <SwAccessibleContext *>( xParent.get() );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ uno::Reference< XAccessible > xChild(this);
+ aEvent.NewValue <<= xChild;
+ pAcc->FireAccessibleEvent( aEvent );
+ }
+}
+
+void SwAccessibleFrameBase::InvalidateFocus_()
+{
+ vcl::Window *pWin = GetWindow();
+ if( !pWin )
+ return;
+
+ bool bSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bSelected = m_bIsSelected;
+ }
+ assert(bSelected && "focus object should be selected");
+
+ FireStateChangedEvent( AccessibleStateType::FOCUSED,
+ pWin->HasFocus() && bSelected );
+}
+
+bool SwAccessibleFrameBase::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_bIsSelected;
+}
+
+SwAccessibleFrameBase::~SwAccessibleFrameBase()
+{
+}
+
+void SwAccessibleFrameBase::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListeningAll();
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyModifyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyModifyHint->GetWhich();
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(GetFrame());
+ if(nWhich == RES_NAME_CHANGED && pFlyFrame)
+ {
+ const SwFrameFormat* pFrameFormat = pFlyFrame->GetFormat();
+
+ const OUString sOldName( GetName() );
+ assert( !pLegacyModifyHint->m_pOld ||
+ static_cast<const SwStringMsgPoolItem *>(pLegacyModifyHint->m_pOld)->GetString() == GetName());
+
+ SetName( pFrameFormat->GetName() );
+ assert( !pLegacyModifyHint->m_pNew ||
+ static_cast<const SwStringMsgPoolItem *>(pLegacyModifyHint->m_pNew)->GetString() == GetName());
+
+ if( sOldName != GetName() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= sOldName;
+ aEvent.NewValue <<= GetName();
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+}
+
+void SwAccessibleFrameBase::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ SwAccessibleContext::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+//Get the selection cursor of the document.
+SwPaM* SwAccessibleFrameBase::GetCursor()
+{
+ // get the cursor shell; if we don't have any, we don't have a
+ // cursor/selection either
+ SwPaM* pCursor = nullptr;
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr && !pCursorShell->IsTableMode() )
+ {
+ SwFEShell *pFESh = dynamic_cast<SwFEShell*>( pCursorShell);
+ if( !pFESh ||
+ !(pFESh->IsFrameSelected() || pFESh->IsObjSelected() > 0) )
+ {
+ // get the selection, and test whether it affects our text node
+ pCursor = pCursorShell->GetCursor( false /* ??? */ );
+ }
+ }
+
+ return pCursor;
+}
+
+//Return the selected state of the object.
+//when the object's anchor are in the selection cursor, we should return true.
+bool SwAccessibleFrameBase::GetSelectedState( )
+{
+ SolarMutexGuard aGuard;
+
+ if(GetMap()->IsDocumentSelAll())
+ {
+ return true;
+ }
+
+ // SELECTED.
+ SwFlyFrame* pFlyFrame = getFlyFrame();
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ const SwPosition *pPos = rAnchor.GetContentAnchor();
+ if( !pPos )
+ return false;
+ int nIndex = pPos->nContent.GetIndex();
+ if( pPos->nNode.GetNode().GetTextNode() )
+ {
+ SwPaM* pCursor = GetCursor();
+ if( pCursor != nullptr )
+ {
+ const SwTextNode* pNode = pPos->nNode.GetNode().GetTextNode();
+ SwNodeOffset nHere = pNode->GetIndex();
+
+ // iterate over ring
+ SwPaM* pRingStart = pCursor;
+ do
+ {
+ // ignore, if no mark
+ if( pCursor->HasMark() )
+ {
+ // check whether nHere is 'inside' pCursor
+ SwPosition* pStart = pCursor->Start();
+ SwNodeOffset nStartIndex = pStart->nNode.GetIndex();
+ SwPosition* pEnd = pCursor->End();
+ SwNodeOffset nEndIndex = pEnd->nNode.GetIndex();
+ if( ( nHere >= nStartIndex ) && (nHere <= nEndIndex) )
+ {
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ if( ((nHere == nStartIndex) && (nIndex >= pStart->nContent.GetIndex())) || (nHere > nStartIndex) )
+ if( ((nHere == nEndIndex) && (nIndex < pEnd->nContent.GetIndex())) || (nHere < nEndIndex) )
+ return true;
+ }
+ else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
+ return true;
+ }
+ else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ if (IsDestroyFrameAnchoredAtChar(*pPos, *pStart, *pEnd))
+ {
+ return true;
+ }
+ }
+ break;
+ }
+ // else: this PaM doesn't point to this paragraph
+ }
+ // else: this PaM is collapsed and doesn't select anything
+
+ // next PaM in ring
+ pCursor = pCursor->GetNext();
+ }
+ while( pCursor != pRingStart );
+ }
+ }
+ return false;
+}
+
+SwFlyFrame* SwAccessibleFrameBase::getFlyFrame() const
+{
+ SwFlyFrame* pFlyFrame = nullptr;
+
+ const SwFrame* pFrame = GetFrame();
+ assert(pFrame);
+ if( pFrame->IsFlyFrame() )
+ {
+ pFlyFrame = static_cast<SwFlyFrame*>( const_cast<SwFrame*>( pFrame ) );
+ }
+
+ return pFlyFrame;
+}
+
+bool SwAccessibleFrameBase::SetSelectedState( bool )
+{
+ bool bParaSelected = GetSelectedState() || IsSelected();
+
+ if (m_isSelectedInDoc != bParaSelected)
+ {
+ m_isSelectedInDoc = bParaSelected;
+ FireStateChangedEvent( AccessibleStateType::SELECTED, bParaSelected );
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accframebase.hxx b/sw/source/core/access/accframebase.hxx
new file mode 100644
index 000000000..12af1f52d
--- /dev/null
+++ b/sw/source/core/access/accframebase.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRAMEBASE_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRAMEBASE_HXX
+
+#include "acccontext.hxx"
+#include <svl/listener.hxx>
+#include <ndtyp.hxx>
+
+class SwFlyFrame;
+
+class SwAccessibleFrameBase : public SwAccessibleContext, public SvtListener
+{
+ bool m_bIsSelected; // protected by base class mutex
+ bool IsSelected();
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets SELECTABLE(1), SELECTED(+),
+ // FOCUSABLE(1) and FOCUSED(+)
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet ) override;
+ SwFlyFrame* getFlyFrame() const;
+ bool GetSelectedState( );
+ SwPaM* GetCursor();
+
+ virtual void InvalidateCursorPos_() override;
+ virtual void InvalidateFocus_() override;
+
+ virtual ~SwAccessibleFrameBase() override;
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleFrameBase(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame *pFlyFrame );
+
+ virtual bool HasCursor() override; // required by map to remember that object
+
+ static SwNodeType GetNodeType( const SwFlyFrame *pFlyFrame );
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+ virtual bool SetSelectedState( bool bSelected ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobj.cxx b/sw/source/core/access/accfrmobj.cxx
new file mode 100644
index 000000000..c09c0215e
--- /dev/null
+++ b/sw/source/core/access/accfrmobj.cxx
@@ -0,0 +1,408 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "accfrmobj.hxx"
+
+#include <accmap.hxx>
+#include "acccontext.hxx"
+
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <flyfrm.hxx>
+#include <pagefrm.hxx>
+#include <cellfrm.hxx>
+#include <swtable.hxx>
+#include <dflyobj.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <dcontact.hxx>
+
+#include <vcl/window.hxx>
+
+namespace sw::access {
+
+SwAccessibleChild::SwAccessibleChild()
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{}
+
+SwAccessibleChild::SwAccessibleChild( const SdrObject* pDrawObj )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ Init( pDrawObj );
+}
+
+SwAccessibleChild::SwAccessibleChild( const SwFrame* pFrame )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ Init( pFrame );
+}
+
+SwAccessibleChild::SwAccessibleChild( vcl::Window* pWindow )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ Init( pWindow );
+}
+
+SwAccessibleChild::SwAccessibleChild( const SwFrame* pFrame,
+ const SdrObject* pDrawObj,
+ vcl::Window* pWindow )
+ : mpFrame( nullptr )
+ , mpDrawObj( nullptr )
+ , mpWindow( nullptr )
+{
+ if ( pFrame )
+ {
+ Init( pFrame );
+ }
+ else if ( pDrawObj )
+ {
+ Init( pDrawObj );
+ }
+ else if ( pWindow )
+ {
+ Init( pWindow );
+ }
+ OSL_ENSURE( (!pFrame || pFrame == mpFrame) &&
+ (!pDrawObj || pDrawObj == mpDrawObj) &&
+ (!pWindow || pWindow == mpWindow),
+ "invalid frame/object/window combination" );
+
+}
+
+SwAccessibleChild::~SwAccessibleChild() = default;
+
+void SwAccessibleChild::Init( const SdrObject* pDrawObj )
+{
+ mpDrawObj = pDrawObj;
+ const SwVirtFlyDrawObj* pFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(mpDrawObj);
+ mpFrame = pFlyDrawObj ? pFlyDrawObj->GetFlyFrame() : nullptr;
+ mpWindow = nullptr;
+}
+
+void SwAccessibleChild::Init( const SwFrame* pFrame )
+{
+ mpFrame = pFrame;
+ mpDrawObj = mpFrame && mpFrame->IsFlyFrame()
+ ? static_cast < const SwFlyFrame * >( mpFrame )->GetVirtDrawObj()
+ : nullptr;
+ mpWindow = nullptr;
+}
+
+void SwAccessibleChild::Init( vcl::Window* pWindow )
+{
+ mpWindow = pWindow;
+ mpFrame = nullptr;
+ mpDrawObj = nullptr;
+}
+
+bool SwAccessibleChild::IsAccessible( bool bPagePreview ) const
+{
+ bool bRet( false );
+
+ if ( mpFrame )
+ {
+ bRet = mpFrame->IsAccessibleFrame() &&
+ ( !mpFrame->IsCellFrame() ||
+ static_cast<const SwCellFrame *>( mpFrame )->GetTabBox()->GetSttNd() != nullptr ) &&
+ !mpFrame->IsInCoveredCell() &&
+ ( bPagePreview ||
+ !mpFrame->IsPageFrame() );
+ }
+ else if ( mpDrawObj )
+ {
+ bRet = true;
+ }
+ else if ( mpWindow )
+ {
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool SwAccessibleChild::IsBoundAsChar() const
+{
+ bool bRet( false );
+
+ if ( mpFrame )
+ {
+ bRet = mpFrame->IsFlyFrame() &&
+ static_cast< const SwFlyFrame *>(mpFrame)->IsFlyInContentFrame();
+ }
+ else if ( mpDrawObj )
+ {
+ const SwFrameFormat* pFrameFormat = ::FindFrameFormat( mpDrawObj );
+ bRet = pFrameFormat
+ && (RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId());
+ }
+ else if ( mpWindow )
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+SwAccessibleChild& SwAccessibleChild::operator=( const SdrObject* pDrawObj )
+{
+ Init( pDrawObj );
+ return *this;
+}
+
+SwAccessibleChild& SwAccessibleChild::operator=( const SwFrame* pFrame )
+{
+ Init( pFrame );
+ return *this;
+}
+
+SwAccessibleChild& SwAccessibleChild::operator=( vcl::Window* pWindow )
+{
+ Init( pWindow );
+ return *this;
+}
+
+bool SwAccessibleChild::operator==( const SwAccessibleChild& r ) const
+{
+ return mpFrame == r.mpFrame &&
+ mpDrawObj == r.mpDrawObj &&
+ mpWindow == r.mpWindow;
+}
+
+bool SwAccessibleChild::IsValid() const
+{
+ return mpFrame != nullptr ||
+ mpDrawObj != nullptr ||
+ mpWindow != nullptr;
+}
+
+bool SwAccessibleChild::IsVisibleChildrenOnly() const
+{
+ bool bRet( false );
+
+ if ( !mpFrame )
+ {
+ bRet = true;
+ }
+ else
+ {
+ bRet = mpFrame->IsRootFrame() ||
+ !( mpFrame->IsTabFrame() ||
+ mpFrame->IsInTab() ||
+ ( IsBoundAsChar() &&
+ static_cast<const SwFlyFrame*>(mpFrame)->GetAnchorFrame()->IsInTab() ) );
+ }
+
+ return bRet;
+}
+
+SwRect SwAccessibleChild::GetBox( const SwAccessibleMap& rAccMap ) const
+{
+ SwRect aBox;
+
+ if ( mpFrame )
+ {
+ if ( mpFrame->IsPageFrame() &&
+ static_cast< const SwPageFrame * >( mpFrame )->IsEmptyPage() )
+ {
+ aBox = SwRect( mpFrame->getFrameArea().Left(), mpFrame->getFrameArea().Top()-1, 1, 1 );
+ }
+ else if ( mpFrame->IsTabFrame() )
+ {
+ aBox = mpFrame->getFrameArea();
+ aBox.Intersection( mpFrame->GetUpper()->getFrameArea() );
+ }
+ else
+ {
+ aBox = mpFrame->getFrameArea();
+ }
+ }
+ else if( mpDrawObj )
+ {
+ const SwContact* const pContact = ::GetUserCall(mpDrawObj);
+ // assume that a) the SwVirt* objects that don't have this are handled
+ // by the mpFrame case above b) for genuine SdrObject this must be set
+ // if it's connected to layout
+ assert(dynamic_cast<SwDrawContact const*>(pContact));
+ SwPageFrame const*const pPage(const_cast<SwAnchoredObject *>(
+ pContact->GetAnchoredObj(mpDrawObj))->FindPageFrameOfAnchor());
+ if (pPage) // may end up here with partial layout -> not visible
+ {
+ aBox = SwRect( mpDrawObj->GetCurrentBoundRect() );
+ // tdf#91260 drawing object may be partially off-page
+ aBox.Intersection(pPage->getFrameArea());
+ }
+ }
+ else if ( mpWindow )
+ {
+ vcl::Window *pWin = rAccMap.GetShell()->GetWin();
+ if (pWin)
+ {
+ aBox = SwRect( pWin->PixelToLogic(
+ tools::Rectangle( mpWindow->GetPosPixel(),
+ mpWindow->GetSizePixel() ) ) );
+ }
+ }
+
+ return aBox;
+}
+
+SwRect SwAccessibleChild::GetBounds( const SwAccessibleMap& rAccMap ) const
+{
+ SwRect aBound;
+
+ if( mpFrame )
+ {
+ if( mpFrame->IsPageFrame() &&
+ static_cast< const SwPageFrame * >( mpFrame )->IsEmptyPage() )
+ {
+ aBound = SwRect( mpFrame->getFrameArea().Left(), mpFrame->getFrameArea().Top()-1, 0, 0 );
+ }
+ else
+ aBound = mpFrame->GetPaintArea();
+ }
+ else if( mpDrawObj )
+ {
+ aBound = GetBox( rAccMap );
+ }
+ else if ( mpWindow )
+ {
+ aBound = GetBox( rAccMap );
+ }
+
+ return aBound;
+}
+
+bool SwAccessibleChild::AlwaysIncludeAsChild() const
+{
+ bool bAlwaysIncludedAsChild( false );
+
+ if ( mpWindow )
+ {
+ bAlwaysIncludedAsChild = true;
+ }
+
+ return bAlwaysIncludedAsChild;
+}
+
+const SwFrame* SwAccessibleChild::GetParent( const bool bInPagePreview ) const
+{
+ const SwFrame* pParent( nullptr );
+
+ if ( mpFrame )
+ {
+ if( mpFrame->IsFlyFrame() )
+ {
+ const SwFlyFrame* pFly = static_cast< const SwFlyFrame *>( mpFrame );
+ if( pFly->IsFlyInContentFrame() )
+ {
+ // For RndStdIds::FLY_AS_CHAR the parent is the anchor
+ pParent = pFly->GetAnchorFrame();
+ OSL_ENSURE( SwAccessibleChild( pParent ).IsAccessible( bInPagePreview ),
+ "parent is not accessible" );
+ }
+ else
+ {
+ // In any other case the parent is the root frm
+ // (in page preview, the page frame)
+ if( bInPagePreview )
+ pParent = pFly->FindPageFrame();
+ else
+ pParent = pFly->getRootFrame();
+ }
+ }
+ else
+ {
+ SwAccessibleChild aUpper( mpFrame->GetUpper() );
+ while( aUpper.GetSwFrame() && !aUpper.IsAccessible(bInPagePreview) )
+ {
+ aUpper = aUpper.GetSwFrame()->GetUpper();
+ }
+ pParent = aUpper.GetSwFrame();
+ }
+ }
+ else if( mpDrawObj )
+ {
+ const SwDrawContact *pContact =
+ static_cast< const SwDrawContact* >( GetUserCall( mpDrawObj ) );
+ OSL_ENSURE( pContact, "sdr contact is missing" );
+ if( pContact )
+ {
+ const SwFrameFormat *pFrameFormat = pContact->GetFormat();
+ OSL_ENSURE( pFrameFormat, "frame format is missing" );
+ if( pFrameFormat && RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId() )
+ {
+ // For RndStdIds::FLY_AS_CHAR the parent is the anchor
+ pParent = pContact->GetAnchorFrame();
+ OSL_ENSURE( SwAccessibleChild( pParent ).IsAccessible( bInPagePreview ),
+ "parent is not accessible" );
+
+ }
+ else
+ {
+ // In any other case the parent is the root frm
+ SwFrame const*const pAnchor(pContact->GetAnchorFrame());
+ if (pAnchor) // null if object removed from layout
+ {
+ if (bInPagePreview)
+ pParent = pAnchor->FindPageFrame();
+ else
+ pParent = pAnchor->getRootFrame();
+ }
+ }
+ }
+ }
+ else if ( mpWindow )
+ {
+ css::uno::Reference < css::accessibility::XAccessible > xAcc =
+ mpWindow->GetAccessible();
+ if ( xAcc.is() )
+ {
+ css::uno::Reference < css::accessibility::XAccessibleContext > xAccContext =
+ xAcc->getAccessibleContext();
+ if ( xAccContext.is() )
+ {
+ css::uno::Reference < css::accessibility::XAccessible > xAccParent =
+ xAccContext->getAccessibleParent();
+ if ( xAccParent.is() )
+ {
+ SwAccessibleContext* pAccParentImpl =
+ dynamic_cast< SwAccessibleContext *>( xAccParent.get() );
+ if ( pAccParentImpl )
+ {
+ pParent = pAccParentImpl->GetFrame();
+ }
+ }
+ }
+ }
+ }
+
+ return pParent;
+}
+
+} // namespace sw::access
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobj.hxx b/sw/source/core/access/accfrmobj.hxx
new file mode 100644
index 000000000..cb5fdfb7b
--- /dev/null
+++ b/sw/source/core/access/accfrmobj.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJ_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJ_HXX
+
+#include <vcl/vclptr.hxx>
+
+class SwAccessibleMap;
+class SwFrame;
+class SdrObject;
+namespace vcl { class Window; }
+class SwRect;
+
+namespace sw::access {
+
+class SwAccessibleChild
+{
+ public:
+ SwAccessibleChild();
+ ~SwAccessibleChild();
+ explicit SwAccessibleChild( const SdrObject* pDrawObj );
+ explicit SwAccessibleChild( const SwFrame* pFrame );
+ explicit SwAccessibleChild( vcl::Window* pWindow );
+ SwAccessibleChild( const SwFrame* pFrame,
+ const SdrObject* pDrawObj,
+ vcl::Window* pWindow );
+
+ SwAccessibleChild(SwAccessibleChild const &) = default;
+ SwAccessibleChild(SwAccessibleChild &&) = default;
+ SwAccessibleChild & operator =(SwAccessibleChild const &) = default;
+ SwAccessibleChild & operator =(SwAccessibleChild &&) = default;
+
+ SwAccessibleChild& operator=( const SdrObject* pDrawObj );
+ SwAccessibleChild& operator=( const SwFrame* pFrame );
+ SwAccessibleChild& operator=( vcl::Window* pWindow );
+
+ bool operator==( const SwAccessibleChild& r ) const;
+
+ bool IsValid() const;
+
+ const SwFrame* GetSwFrame() const { return mpFrame; }
+ const SdrObject* GetDrawObject() const { return mpDrawObj; }
+ vcl::Window* GetWindow() const { return mpWindow; }
+
+ const SwFrame* GetParent( const bool bInPagePreview ) const;
+
+ bool IsAccessible( bool bPagePreview ) const;
+ bool IsBoundAsChar() const;
+
+ bool IsVisibleChildrenOnly() const;
+ SwRect GetBox( const SwAccessibleMap& rAccMap ) const;
+ SwRect GetBounds( const SwAccessibleMap& rAccMap ) const;
+
+ /** indicating, if accessible child is included even, if the corresponding
+ object is not visible. */
+ bool AlwaysIncludeAsChild() const;
+
+ private:
+ const SwFrame* mpFrame;
+ const SdrObject* mpDrawObj;
+ VclPtr<vcl::Window> mpWindow;
+
+ void Init( const SdrObject* pDrawObj );
+ void Init( const SwFrame* pFrame );
+ void Init( vcl::Window* pWindow );
+};
+
+} // eof of namespace sw::access
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjmap.cxx b/sw/source/core/access/accfrmobjmap.cxx
new file mode 100644
index 000000000..5da8dad1a
--- /dev/null
+++ b/sw/source/core/access/accfrmobjmap.cxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "accfrmobjmap.hxx"
+#include <accmap.hxx>
+#include "acccontext.hxx"
+
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pagefrm.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+
+#include <svx/svdobj.hxx>
+
+using namespace sw::access;
+
+SwAccessibleChildMap::SwAccessibleChildMap( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap )
+ : mnHellId( rAccMap.GetShell()->GetDoc()->getIDocumentDrawModelAccess().GetHellId() )
+ , mnControlsId( rAccMap.GetShell()->GetDoc()->getIDocumentDrawModelAccess().GetControlsId() )
+{
+ const bool bVisibleChildrenOnly = SwAccessibleChild( &rFrame ).IsVisibleChildrenOnly();
+
+ sal_uInt32 nPos = 0;
+ SwAccessibleChild aLower( rFrame.GetLower() );
+ while( aLower.GetSwFrame() )
+ {
+ if ( !bVisibleChildrenOnly ||
+ aLower.AlwaysIncludeAsChild() ||
+ aLower.GetBox( rAccMap ).Overlaps( rVisArea ) )
+ {
+ insert( nPos++, SwAccessibleChildMapKey::TEXT, aLower );
+ }
+
+ aLower = aLower.GetSwFrame()->GetNext();
+ }
+
+ if ( rFrame.IsPageFrame() )
+ {
+ OSL_ENSURE( bVisibleChildrenOnly, "page frame within tab frame???" );
+ const SwPageFrame *pPgFrame =
+ static_cast< const SwPageFrame * >( &rFrame );
+ const SwSortedObjs *pObjs = pPgFrame->GetSortedObjs();
+ if ( pObjs )
+ {
+ for(const SwAnchoredObject* pObj : *pObjs)
+ {
+ aLower = pObj->GetDrawObj();
+ if ( aLower.GetBox( rAccMap ).Overlaps( rVisArea ) )
+ {
+ insert( aLower.GetDrawObject(), aLower );
+ }
+ }
+ }
+ }
+ else if( rFrame.IsTextFrame() )
+ {
+ const SwSortedObjs *pObjs = rFrame.GetDrawObjs();
+ if ( pObjs )
+ {
+ for(const SwAnchoredObject* pObj : *pObjs)
+ {
+ aLower = pObj->GetDrawObj();
+ if ( aLower.IsBoundAsChar() &&
+ ( !bVisibleChildrenOnly ||
+ aLower.AlwaysIncludeAsChild() ||
+ aLower.GetBox( rAccMap ).Overlaps( rVisArea ) ) )
+ {
+ insert( aLower.GetDrawObject(), aLower );
+ }
+ }
+ }
+
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl =
+ rAccMap.GetContextImpl( &rFrame, false );
+ if( xAccImpl.is() )
+ {
+ SwAccessibleContext* pAccImpl = xAccImpl.get();
+ if ( pAccImpl &&
+ pAccImpl->HasAdditionalAccessibleChildren() )
+ {
+ std::vector< vcl::Window* > aAdditionalChildren;
+ pAccImpl->GetAdditionalAccessibleChildren( &aAdditionalChildren );
+
+ sal_Int32 nCounter( 0 );
+ for ( const auto& rpChild : aAdditionalChildren )
+ {
+ aLower = rpChild;
+ insert( ++nCounter, SwAccessibleChildMapKey::XWINDOW, aLower );
+ }
+ }
+ }
+ }
+ }
+}
+
+std::pair< SwAccessibleChildMap::iterator, bool > SwAccessibleChildMap::insert(
+ const sal_uInt32 nPos,
+ const SwAccessibleChildMapKey::LayerId eLayerId,
+ const SwAccessibleChild& rLower )
+{
+ SwAccessibleChildMapKey aKey( eLayerId, nPos );
+ return emplace( aKey, rLower );
+}
+
+std::pair< SwAccessibleChildMap::iterator, bool > SwAccessibleChildMap::insert(
+ const SdrObject *pObj,
+ const SwAccessibleChild& rLower )
+{
+ const SdrLayerID nLayer = pObj->GetLayer();
+ SwAccessibleChildMapKey::LayerId eLayerId =
+ (mnHellId == nLayer)
+ ? SwAccessibleChildMapKey::HELL
+ : ( (mnControlsId == nLayer)
+ ? SwAccessibleChildMapKey::CONTROLS
+ : SwAccessibleChildMapKey::HEAVEN );
+ SwAccessibleChildMapKey aKey( eLayerId, pObj->GetOrdNum() );
+ return emplace( aKey, rLower );
+}
+
+bool SwAccessibleChildMap::IsSortingRequired( const SwFrame& rFrame )
+{
+ return ( rFrame.IsPageFrame() &&
+ static_cast< const SwPageFrame& >( rFrame ).GetSortedObjs() ) ||
+ ( rFrame.IsTextFrame() &&
+ rFrame.GetDrawObjs() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjmap.hxx b/sw/source/core/access/accfrmobjmap.hxx
new file mode 100644
index 000000000..ed4ecafec
--- /dev/null
+++ b/sw/source/core/access/accfrmobjmap.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJMAP_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJMAP_HXX
+
+#include <tools/gen.hxx>
+#include <svx/svdtypes.hxx>
+#include "accfrmobj.hxx"
+#include <map>
+
+class SwAccessibleMap;
+class SwRect;
+class SwFrame;
+class SdrObject;
+
+class SwAccessibleChildMapKey
+{
+public:
+ enum LayerId { INVALID, HELL, TEXT, HEAVEN, CONTROLS, XWINDOW };
+
+ SwAccessibleChildMapKey()
+ : m_eLayerId( INVALID )
+ , m_nOrdNum( 0 )
+ , m_nPosNum( 0, 0 )
+ {}
+
+ SwAccessibleChildMapKey( LayerId eId, sal_uInt32 nOrd )
+ : m_eLayerId( eId )
+ , m_nOrdNum( nOrd )
+ , m_nPosNum( 0, 0 )
+ {}
+
+ bool operator()( const SwAccessibleChildMapKey& r1,
+ const SwAccessibleChildMapKey& r2 ) const
+ {
+ if(r1.m_eLayerId == r2.m_eLayerId)
+ {
+ if(r1.m_nPosNum == r2.m_nPosNum)
+ return r1.m_nOrdNum < r2.m_nOrdNum;
+ else
+ {
+ if(r1.m_nPosNum.getY() == r2.m_nPosNum.getY())
+ return r1.m_nPosNum.getX() < r2.m_nPosNum.getX();
+ else
+ return r1.m_nPosNum.getY() < r2.m_nPosNum.getY();
+ }
+ }
+ else
+ return r1.m_eLayerId < r2.m_eLayerId;
+ }
+
+ /* MT: Need to get this position parameter stuff in dev300 somehow...
+ //This methods are used to insert an object to the map, adding a position parameter.
+ std::pair< iterator, bool > insert( sal_uInt32 nOrd, Point nPos,
+ const SwFrameOrObj& rLower );
+ std::pair< iterator, bool > insert( const SdrObject *pObj,
+ const SwFrameOrObj& rLower,
+ const SwDoc *pDoc,
+ Point nPos);
+ */
+
+private:
+ LayerId m_eLayerId;
+ sal_uInt32 m_nOrdNum;
+ Point m_nPosNum;
+};
+
+
+class SwAccessibleChildMap
+{
+public:
+ typedef SwAccessibleChildMapKey key_type;
+ typedef sw::access::SwAccessibleChild mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwAccessibleChildMapKey key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_reverse_iterator const_reverse_iterator;
+
+private:
+ const SdrLayerID mnHellId;
+ const SdrLayerID mnControlsId;
+ std::map<key_type,mapped_type,key_compare> maMap;
+
+ std::pair< iterator, bool > insert( const sal_uInt32 nPos,
+ const SwAccessibleChildMapKey::LayerId eLayerId,
+ const sw::access::SwAccessibleChild& rLower );
+ std::pair< iterator, bool > insert( const SdrObject* pObj,
+ const sw::access::SwAccessibleChild& rLower );
+
+public:
+ SwAccessibleChildMap( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap );
+
+ static bool IsSortingRequired( const SwFrame& rFrame );
+
+ const_iterator cbegin() const { return maMap.cbegin(); }
+ const_iterator cend() const { return maMap.cend(); }
+ const_reverse_iterator crbegin() const { return maMap.crbegin(); }
+ const_reverse_iterator crend() const { return maMap.crend(); }
+
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjslist.cxx b/sw/source/core/access/accfrmobjslist.cxx
new file mode 100644
index 000000000..c62987798
--- /dev/null
+++ b/sw/source/core/access/accfrmobjslist.cxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "accfrmobjslist.hxx"
+#include <accmap.hxx>
+#include "acccontext.hxx"
+
+#include <pagefrm.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+
+using namespace ::sw::access;
+
+SwAccessibleChildSList_const_iterator::SwAccessibleChildSList_const_iterator(
+ const SwAccessibleChildSList& rLst,
+ SwAccessibleMap& rAccMap )
+ : m_rList( rLst ),
+ m_aCurr( m_rList.GetFrame().GetLower() ),
+ m_nNextObj( 0 )
+{
+ if( !m_aCurr.GetSwFrame() )
+ {
+ const SwFrame& rFrame = m_rList.GetFrame();
+ if( rFrame.IsPageFrame() )
+ {
+ const SwPageFrame& rPgFrame = static_cast< const SwPageFrame& >( rFrame );
+ const SwSortedObjs *pObjs = rPgFrame.GetSortedObjs();
+ if( pObjs && pObjs->size() )
+ {
+ m_aCurr = (*pObjs)[m_nNextObj++]->GetDrawObj();
+ }
+ }
+ else if( rFrame.IsTextFrame() )
+ {
+ const SwSortedObjs *pObjs = rFrame.GetDrawObjs();
+ if ( pObjs && pObjs->size() )
+ {
+ m_aCurr = (*pObjs)[m_nNextObj++]->GetDrawObj();
+ while( m_aCurr.IsValid() && !m_aCurr.IsBoundAsChar() )
+ {
+ m_aCurr = (m_nNextObj < pObjs->size())
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ }
+ }
+ if ( !m_aCurr.IsValid() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl =
+ rAccMap.GetContextImpl( &rFrame, false );
+ if( xAccImpl.is() )
+ {
+ SwAccessibleContext* pAccImpl = xAccImpl.get();
+ m_aCurr = SwAccessibleChild( pAccImpl->GetAdditionalAccessibleChild( 0 ) );
+ ++m_nNextObj;
+ }
+ }
+ }
+ }
+
+ if( m_rList.IsVisibleChildrenOnly() )
+ {
+ // Find the first visible
+ while( m_aCurr.IsValid() &&
+ !m_aCurr.AlwaysIncludeAsChild() &&
+ !m_aCurr.GetBox( rAccMap ).Overlaps( m_rList.GetVisArea() ) )
+ {
+ next();
+ }
+ }
+}
+
+SwAccessibleChildSList_const_iterator& SwAccessibleChildSList_const_iterator::next()
+{
+ bool bNextTaken( true );
+ if( m_aCurr.GetDrawObject() || m_aCurr.GetWindow() )
+ {
+ bNextTaken = false;
+ }
+ else if( m_aCurr.GetSwFrame() )
+ {
+ m_aCurr = m_aCurr.GetSwFrame()->GetNext();
+ if( !m_aCurr.GetSwFrame() )
+ {
+ bNextTaken = false;
+ }
+ }
+
+ if( !bNextTaken )
+ {
+ const SwFrame& rFrame = m_rList.GetFrame();
+ if( rFrame.IsPageFrame() )
+ {
+ const SwPageFrame& rPgFrame = static_cast< const SwPageFrame& >( rFrame );
+ const SwSortedObjs *pObjs = rPgFrame.GetSortedObjs();
+ m_aCurr = ( pObjs && m_nNextObj < pObjs->size() )
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ }
+ else if( rFrame.IsTextFrame() )
+ {
+ const SwSortedObjs* pObjs = rFrame.GetDrawObjs();
+ const size_t nObjsCount = pObjs ? pObjs->size() : 0;
+ m_aCurr = ( pObjs && m_nNextObj < nObjsCount )
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ while( m_aCurr.IsValid() && !m_aCurr.IsBoundAsChar() )
+ {
+ m_aCurr = ( m_nNextObj < nObjsCount )
+ ? (*pObjs)[m_nNextObj++]->GetDrawObj()
+ : static_cast< const SdrObject *>( nullptr );
+ }
+ if ( !m_aCurr.IsValid() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl =
+ m_rList.GetAccMap().GetContextImpl( &rFrame, false );
+ if( xAccImpl.is() )
+ {
+ SwAccessibleContext* pAccImpl = xAccImpl.get();
+ m_aCurr = SwAccessibleChild( pAccImpl->GetAdditionalAccessibleChild( m_nNextObj - nObjsCount ) );
+ ++m_nNextObj;
+ }
+ }
+ }
+ }
+
+ return *this;
+}
+
+SwAccessibleChildSList_const_iterator& SwAccessibleChildSList_const_iterator::next_visible()
+{
+ next();
+ while( m_aCurr.IsValid() &&
+ !m_aCurr.AlwaysIncludeAsChild() &&
+ !m_aCurr.GetBox( m_rList.GetAccMap() ).Overlaps( m_rList.GetVisArea() ) )
+ {
+ next();
+ }
+
+ return *this;
+}
+
+SwAccessibleChildSList_const_iterator& SwAccessibleChildSList_const_iterator::operator++()
+{
+ return m_rList.IsVisibleChildrenOnly() ? next_visible() : next();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accfrmobjslist.hxx b/sw/source/core/access/accfrmobjslist.hxx
new file mode 100644
index 000000000..9df293d71
--- /dev/null
+++ b/sw/source/core/access/accfrmobjslist.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJSLIST_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCFRMOBJSLIST_HXX
+
+#include "accfrmobj.hxx"
+#include <swrect.hxx>
+
+class SwAccessibleMap;
+class SwAccessibleChildSList;
+
+class SwAccessibleChildSList_const_iterator
+{
+private:
+ friend class SwAccessibleChildSList;
+
+ const SwAccessibleChildSList& m_rList; // The frame we are iterating over
+ sw::access::SwAccessibleChild m_aCurr; // The current object
+ size_t m_nNextObj; // The index of the current sdr object
+
+ SwAccessibleChildSList_const_iterator( const SwAccessibleChildSList& rLst )
+ : m_rList( rLst )
+ , m_nNextObj( 0 )
+ {}
+
+ SwAccessibleChildSList_const_iterator( const SwAccessibleChildSList& rLst,
+ SwAccessibleMap& rAccMap );
+
+ SwAccessibleChildSList_const_iterator& next();
+ SwAccessibleChildSList_const_iterator& next_visible();
+
+public:
+ bool operator==( const SwAccessibleChildSList_const_iterator& r ) const
+ {
+ return m_aCurr == r.m_aCurr;
+ }
+
+ bool operator!=(
+ const SwAccessibleChildSList_const_iterator& r ) const
+ {
+ return !(*this == r);
+ }
+
+ SwAccessibleChildSList_const_iterator& operator++();
+
+ const sw::access::SwAccessibleChild& operator*() const
+ {
+ return m_aCurr;
+ }
+};
+
+// An iterator to iterate over a frame's child in any order
+class SwAccessibleChildSList
+{
+ const SwRect maVisArea;
+ const SwFrame& mrFrame;
+ const bool mbVisibleChildrenOnly;
+ SwAccessibleMap& mrAccMap;
+
+public:
+ typedef SwAccessibleChildSList_const_iterator const_iterator;
+
+ SwAccessibleChildSList( const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap )
+ : maVisArea()
+ , mrFrame( rFrame )
+ , mbVisibleChildrenOnly( false )
+ , mrAccMap( rAccMap )
+ {}
+
+ SwAccessibleChildSList( const SwRect& rVisArea,
+ const SwFrame& rFrame,
+ SwAccessibleMap& rAccMap )
+ : maVisArea( rVisArea )
+ , mrFrame( rFrame )
+ , mbVisibleChildrenOnly( sw::access::SwAccessibleChild( &rFrame ).IsVisibleChildrenOnly() )
+ , mrAccMap( rAccMap )
+ {
+ }
+
+ const_iterator begin() const
+ {
+ return SwAccessibleChildSList_const_iterator( *this, mrAccMap );
+ }
+
+ const_iterator end() const
+ {
+ return SwAccessibleChildSList_const_iterator( *this );
+ }
+
+ const SwFrame& GetFrame() const
+ {
+ return mrFrame;
+ }
+
+ bool IsVisibleChildrenOnly() const
+ {
+ return mbVisibleChildrenOnly;
+ }
+
+ const SwRect& GetVisArea() const
+ {
+ return maVisArea;
+ }
+
+ SwAccessibleMap& GetAccMap() const
+ {
+ return mrAccMap;
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accgraphic.cxx b/sw/source/core/access/accgraphic.cxx
new file mode 100644
index 000000000..d6d115d78
--- /dev/null
+++ b/sw/source/core/access/accgraphic.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <flyfrm.hxx>
+#include <fmturl.hxx>
+#include "accgraphic.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+SwAccessibleGraphic::SwAccessibleGraphic(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleNoTextFrame( pInitMap, AccessibleRole::GRAPHIC, pFlyFrame )
+{
+}
+
+SwAccessibleGraphic::~SwAccessibleGraphic()
+{
+}
+
+OUString SAL_CALL SwAccessibleGraphic::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.SwAccessibleGraphic";
+}
+
+sal_Bool SAL_CALL SwAccessibleGraphic::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+Sequence< OUString > SAL_CALL SwAccessibleGraphic::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.AccessibleTextGraphicObject", sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessibleGraphic::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// Return this object's role.
+sal_Int16 SAL_CALL SwAccessibleGraphic::getAccessibleRole()
+{
+ SolarMutexGuard g;
+
+ SwFormatURL aURL( static_cast<const SwLayoutFrame*>(GetFrame())->GetFormat()->GetURL() );
+
+ if (aURL.GetMap())
+ return AccessibleRole::IMAGE_MAP;
+ return AccessibleRole::GRAPHIC;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accgraphic.hxx b/sw/source/core/access/accgraphic.hxx
new file mode 100644
index 000000000..dc21201bc
--- /dev/null
+++ b/sw/source/core/access/accgraphic.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCGRAPHIC_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCGRAPHIC_HXX
+
+#include "accnotextframe.hxx"
+
+class SwAccessibleGraphic : public SwAccessibleNoTextFrame
+{
+protected:
+ virtual ~SwAccessibleGraphic() override;
+
+public:
+ SwAccessibleGraphic(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame *pFlyFrame );
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object. */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service. */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+ /// Return this object's role.
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accheaderfooter.cxx b/sw/source/core/access/accheaderfooter.cxx
new file mode 100644
index 000000000..d07b2c802
--- /dev/null
+++ b/sw/source/core/access/accheaderfooter.cxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <hffrm.hxx>
+#include "accheaderfooter.hxx"
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+constexpr OUStringLiteral sImplementationNameHeader
+ = u"com.sun.star.comp.Writer.SwAccessibleHeaderView";
+constexpr OUStringLiteral sImplementationNameFooter
+ = u"com.sun.star.comp.Writer.SwAccessibleFooterView";
+
+SwAccessibleHeaderFooter::SwAccessibleHeaderFooter(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwHeaderFrame* pHdFrame ) :
+ SwAccessibleContext( pInitMap, AccessibleRole::HEADER, pHdFrame )
+{
+ OUString sArg( OUString::number( pHdFrame->GetPhyPageNum() ) );
+ SetName( GetResource( STR_ACCESS_HEADER_NAME, &sArg ) );
+}
+
+SwAccessibleHeaderFooter::SwAccessibleHeaderFooter(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFooterFrame* pFtFrame ) :
+ SwAccessibleContext( pInitMap, AccessibleRole::FOOTER, pFtFrame )
+{
+ OUString sArg( OUString::number( pFtFrame->GetPhyPageNum() ) );
+ SetName( GetResource( STR_ACCESS_FOOTER_NAME, &sArg ) );
+}
+
+SwAccessibleHeaderFooter::~SwAccessibleHeaderFooter()
+{
+}
+
+OUString SAL_CALL SwAccessibleHeaderFooter::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ TranslateId pResId = AccessibleRole::HEADER == GetRole()
+ ? STR_ACCESS_HEADER_DESC
+ : STR_ACCESS_FOOTER_DESC ;
+
+ OUString sArg( GetFormattedPageNumber() );
+
+ return GetResource(pResId, &sArg);
+}
+
+OUString SAL_CALL SwAccessibleHeaderFooter::getImplementationName()
+{
+ if( AccessibleRole::HEADER == GetRole() )
+ return sImplementationNameHeader;
+ else
+ return sImplementationNameFooter;
+}
+
+sal_Bool SAL_CALL SwAccessibleHeaderFooter::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+Sequence< OUString > SAL_CALL SwAccessibleHeaderFooter::getSupportedServiceNames()
+{
+ return { (AccessibleRole::HEADER == GetRole())?OUString("com.sun.star.text.AccessibleHeaderView"):OUString("com.sun.star.text.AccessibleFooterView"),
+ sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessibleHeaderFooter::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Int32 SAL_CALL SwAccessibleHeaderFooter::getBackground()
+{
+ Reference< XAccessible > xParent = getAccessibleParent();
+ if (xParent.is())
+ {
+ Reference< XAccessibleComponent > xAccContext (xParent,UNO_QUERY);
+ if(xAccContext.is())
+ {
+ return xAccContext->getBackground();
+ }
+ }
+ return SwAccessibleContext::getBackground();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accheaderfooter.hxx b/sw/source/core/access/accheaderfooter.hxx
new file mode 100644
index 000000000..821ed9883
--- /dev/null
+++ b/sw/source/core/access/accheaderfooter.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHEADERFOOTER_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHEADERFOOTER_HXX
+
+#include "acccontext.hxx"
+
+class SwHeaderFrame;
+class SwFooterFrame;
+
+class SwAccessibleHeaderFooter : public SwAccessibleContext
+{
+protected:
+ virtual ~SwAccessibleHeaderFooter() override;
+
+public:
+ SwAccessibleHeaderFooter(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwHeaderFrame* pHdFrame);
+ SwAccessibleHeaderFooter(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFooterFrame* pFtFrame);
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object. */
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class. */
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service. */
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchyperlink.cxx b/sw/source/core/access/acchyperlink.cxx
new file mode 100644
index 000000000..76a2721ec
--- /dev/null
+++ b/sw/source/core/access/acchyperlink.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/accessiblekeybindinghelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/frame/XDesktop.hpp>
+#include <com/sun/star/document/XLinkTargetSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <swurl.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/svapp.hxx>
+#include <txtinet.hxx>
+#include "accpara.hxx"
+#include "acchyperlink.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+
+SwAccessibleHyperlink::SwAccessibleHyperlink(const SwTextAttr & rTextAttr,
+ SwAccessibleParagraph & rAccPara,
+ sal_Int32 const nStt, sal_Int32 const nEnd)
+ : m_pHyperlink(const_cast<SwFormatINetFormat*>(&rTextAttr.GetINetFormat()))
+ , m_xParagraph(&rAccPara)
+ , m_nStartIndex( nStt )
+ , m_nEndIndex( nEnd )
+{
+ StartListening(m_pHyperlink->GetNotifier());
+}
+
+SwAccessibleHyperlink::~SwAccessibleHyperlink()
+{
+ Invalidate(); // with SolarMutex!
+}
+
+// when the pool item dies, invalidate! this is the only reason for Listener...
+void SwAccessibleHyperlink::Notify(SfxHint const& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ Invalidate();
+ }
+}
+
+// both the parent SwAccessibleParagraph and the pool-item must be valid
+const SwFormatINetFormat *SwAccessibleHyperlink::GetTextAttr() const
+{
+ return (m_xParagraph.is() && m_xParagraph->GetMap())
+ ? m_pHyperlink
+ : nullptr;
+}
+
+// XAccessibleAction
+sal_Int32 SAL_CALL SwAccessibleHyperlink::getAccessibleActionCount()
+{
+ return isValid() ? 1 : 0;
+}
+
+sal_Bool SAL_CALL SwAccessibleHyperlink::doAccessibleAction( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ bool bRet = false;
+
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+ SwFormatINetFormat const*const pINetFormat = GetTextAttr();
+ if (pINetFormat && !pINetFormat->GetValue().isEmpty())
+ {
+ SwViewShell *pVSh = m_xParagraph->GetShell();
+ if (pVSh)
+ {
+ LoadURL(*pVSh, pINetFormat->GetValue(), LoadUrlFlags::NONE,
+ pINetFormat->GetTargetFrame());
+ const SwTextINetFormat *const pTextAttr = pINetFormat->GetTextINetFormat();
+ if (pTextAttr)
+ {
+ const_cast<SwTextINetFormat*>(pTextAttr)->SetVisited(true);
+ const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid(true);
+ }
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+OUString SAL_CALL SwAccessibleHyperlink::getAccessibleActionDescription(
+ sal_Int32 nIndex )
+{
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+
+ SolarMutexGuard g;
+ if (SwFormatINetFormat const*const pINetFormat = GetTextAttr())
+ {
+ return pINetFormat->GetValue();
+ }
+
+ return OUString();
+}
+
+uno::Reference< XAccessibleKeyBinding > SAL_CALL
+ SwAccessibleHyperlink::getAccessibleActionKeyBinding( sal_Int32 )
+{
+ uno::Reference< XAccessibleKeyBinding > xKeyBinding;
+
+ if( isValid() )
+ {
+ rtl::Reference<::comphelper::OAccessibleKeyBindingHelper> pKeyBindingHelper =
+ new ::comphelper::OAccessibleKeyBindingHelper();
+ xKeyBinding = pKeyBindingHelper;
+
+ awt::KeyStroke aKeyStroke;
+ aKeyStroke.Modifiers = 0;
+ aKeyStroke.KeyCode = KEY_RETURN;
+ aKeyStroke.KeyChar = 0;
+ aKeyStroke.KeyFunc = 0;
+ pKeyBindingHelper->AddKeyBinding( aKeyStroke );
+ }
+
+ return xKeyBinding;
+}
+
+// XAccessibleHyperlink
+uno::Any SAL_CALL SwAccessibleHyperlink::getAccessibleActionAnchor(
+ sal_Int32 nIndex)
+{
+ SolarMutexGuard g;
+
+ uno::Any aRet;
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+ OUString text( m_xParagraph->GetString() );
+ OUString retText = text.copy(m_nStartIndex, m_nEndIndex - m_nStartIndex);
+ aRet <<= retText;
+ return aRet;
+}
+
+uno::Any SAL_CALL SwAccessibleHyperlink::getAccessibleActionObject(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if(nIndex != 0)
+ throw lang::IndexOutOfBoundsException();
+ OUString retText;
+ if (SwFormatINetFormat const*const pINetFormat = GetTextAttr())
+ {
+ retText = pINetFormat->GetValue();
+ }
+ uno::Any aRet;
+ aRet <<= retText;
+ return aRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleHyperlink::getStartIndex()
+{
+ return m_nStartIndex;
+}
+
+sal_Int32 SAL_CALL SwAccessibleHyperlink::getEndIndex()
+{
+ return m_nEndIndex;
+}
+
+sal_Bool SAL_CALL SwAccessibleHyperlink::isValid( )
+{
+ SolarMutexGuard aGuard;
+ if (m_xParagraph.is())
+ {
+ if (SwFormatINetFormat const*const pINetFormat = GetTextAttr())
+ {
+ OUString const sText(pINetFormat->GetValue());
+ sal_Int32 nPos = sText.indexOf("#");
+ if (nPos==0)//document link
+ {
+ uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
+ if( ! xFactory.is() )
+ return false;
+ uno::Reference< css::frame::XDesktop > xDesktop( xFactory->createInstance( "com.sun.star.frame.Desktop" ),
+ uno::UNO_QUERY );
+ if( !xDesktop.is() )
+ return false;
+ uno::Reference< lang::XComponent > xComp = xDesktop->getCurrentComponent();
+ if( !xComp.is() )
+ return false;
+ uno::Reference< css::document::XLinkTargetSupplier > xLTS(xComp, uno::UNO_QUERY);
+ if ( !xLTS.is())
+ return false;
+
+ uno::Reference< css::container::XNameAccess > xLinks = xLTS->getLinks();
+ uno::Reference< css::container::XNameAccess > xSubLinks;
+ const uno::Sequence< OUString > aNames( xLinks->getElementNames() );
+
+ for( const OUString& aLink : aNames )
+ {
+ uno::Any aAny = xLinks->getByName( aLink );
+ aAny >>= xSubLinks;
+ if (xSubLinks->hasByName(sText.copy(1)) )
+ return true;
+ }
+ }
+ else//internet
+ return true;
+ }
+ }//xpara valid
+ return false;
+}
+
+void SwAccessibleHyperlink::Invalidate()
+{
+ SolarMutexGuard aGuard;
+ m_xParagraph = nullptr;
+ m_pHyperlink = nullptr;
+ EndListeningAll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchyperlink.hxx b/sw/source/core/access/acchyperlink.hxx
new file mode 100644
index 000000000..68116d99d
--- /dev/null
+++ b/sw/source/core/access/acchyperlink.hxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERLINK_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERLINK_HXX
+
+#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp>
+#include <rtl/ref.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svl/listener.hxx>
+
+class SwFormatINetFormat;
+class SwAccessibleParagraph;
+class SwTextAttr;
+
+class SwAccessibleHyperlink
+ : public ::cppu::WeakImplHelper<css::accessibility::XAccessibleHyperlink>
+ , public SvtListener
+{
+ friend class SwAccessibleParagraph;
+ friend class SwAccessibleHyperTextData;
+ SwFormatINetFormat * m_pHyperlink;
+ ::rtl::Reference< SwAccessibleParagraph > m_xParagraph;
+ sal_Int32 m_nStartIndex;
+ sal_Int32 m_nEndIndex;
+
+ SwAccessibleHyperlink(const SwTextAttr &,
+ SwAccessibleParagraph &,
+ sal_Int32 nStt, sal_Int32 nEnd );
+ virtual ~SwAccessibleHyperlink() override;
+
+ const SwFormatINetFormat* GetTextAttr() const;
+ void Invalidate();
+
+ virtual void Notify(SfxHint const& rHint) override;
+
+public:
+ // XAccessibleAction
+ virtual sal_Int32 SAL_CALL getAccessibleActionCount() override;
+ virtual sal_Bool SAL_CALL doAccessibleAction( sal_Int32 nIndex ) override;
+ virtual OUString SAL_CALL getAccessibleActionDescription(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleKeyBinding > SAL_CALL
+ getAccessibleActionKeyBinding( sal_Int32 nIndex ) override;
+
+ // XAccessibleHyperlink
+ virtual css::uno::Any SAL_CALL getAccessibleActionAnchor(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Any SAL_CALL getAccessibleActionObject(
+ sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getStartIndex() override;
+ virtual sal_Int32 SAL_CALL getEndIndex() override;
+ virtual sal_Bool SAL_CALL isValid( ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchypertextdata.cxx b/sw/source/core/access/acchypertextdata.cxx
new file mode 100644
index 000000000..918bcc631
--- /dev/null
+++ b/sw/source/core/access/acchypertextdata.cxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "acchyperlink.hxx"
+#include "acchypertextdata.hxx"
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+SwAccessibleHyperTextData::SwAccessibleHyperTextData()
+{
+}
+
+SwAccessibleHyperTextData::~SwAccessibleHyperTextData()
+{
+ iterator aIter = begin();
+ while( aIter != end() )
+ {
+ Reference < XAccessibleHyperlink > xTmp = (*aIter).second;
+ if( xTmp.is() )
+ {
+ SwAccessibleHyperlink *pTmp =
+ static_cast< SwAccessibleHyperlink * >( xTmp.get() );
+ pTmp->Invalidate();
+ }
+ ++aIter;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acchypertextdata.hxx b/sw/source/core/access/acchypertextdata.hxx
new file mode 100644
index 000000000..01833885a
--- /dev/null
+++ b/sw/source/core/access/acchypertextdata.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERTEXTDATA_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCHYPERTEXTDATA_HXX
+
+#include <cppuhelper/weakref.hxx>
+#include <map>
+
+class SwTextAttr;
+namespace com::sun::star::accessibility {
+ class XAccessibleHyperlink;
+}
+
+class SwAccessibleHyperTextData
+{
+public:
+ typedef const SwTextAttr * key_type;
+ typedef css::uno::WeakReference< css::accessibility::XAccessibleHyperlink > mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef std::less< const SwTextAttr * > key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+private:
+ std::map<key_type,mapped_type,key_compare> maMap;
+public:
+ SwAccessibleHyperTextData();
+ ~SwAccessibleHyperTextData();
+
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accmap.cxx b/sw/source/core/access/accmap.cxx
new file mode 100644
index 000000000..d5c30b9d9
--- /dev/null
+++ b/sw/source/core/access/accmap.cxx
@@ -0,0 +1,3440 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <rtl/ref.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <vcl/window.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/unomod.hxx>
+#include <algorithm>
+#include <map>
+#include <unordered_map>
+#include <list>
+#include <vector>
+#include <accmap.hxx>
+#include "acccontext.hxx"
+#include "accdoc.hxx"
+#include <strings.hrc>
+#include "accpreview.hxx"
+#include "accpage.hxx"
+#include "accpara.hxx"
+#include "accheaderfooter.hxx"
+#include "accfootnote.hxx"
+#include "acctextframe.hxx"
+#include "accgraphic.hxx"
+#include "accembedded.hxx"
+#include "acccell.hxx"
+#include "acctable.hxx"
+#include <fesh.hxx>
+#include <istype.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <hffrm.hxx>
+#include <ftnfrm.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <pagefrm.hxx>
+#include <flyfrm.hxx>
+#include <ndtyp.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdpage.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <pagepreviewlayout.hxx>
+#include <dcontact.hxx>
+#include <svx/svdmark.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <dflyobj.hxx>
+#include <prevwpage.hxx>
+#include <calbck.hxx>
+#include <undobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::sw::access;
+
+class SwAccessibleContextMap_Impl
+{
+public:
+ typedef const SwFrame * key_type;
+ typedef uno::WeakReference < XAccessible > mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef std::map<key_type, mapped_type>::iterator iterator;
+ typedef std::map<key_type, mapped_type>::const_iterator const_iterator;
+private:
+ std::map <key_type, mapped_type> maMap;
+public:
+
+#if OSL_DEBUG_LEVEL > 0
+ bool mbLocked;
+#endif
+
+ SwAccessibleContextMap_Impl()
+#if OSL_DEBUG_LEVEL > 0
+ : mbLocked( false )
+#endif
+ {}
+
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ bool empty() const { return maMap.empty(); }
+ void clear() { maMap.clear(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+namespace {
+
+class SwDrawModellListener_Impl : public SfxListener,
+ public ::cppu::WeakImplHelper< document::XShapeEventBroadcaster >
+{
+ mutable ::osl::Mutex maListenerMutex;
+ ::comphelper::OInterfaceContainerHelper3<css::document::XEventListener> maEventListeners;
+ std::unordered_multimap<css::uno::Reference< css::drawing::XShape >, css::uno::Reference< css::document::XShapeEventListener >> maShapeListeners;
+ SdrModel *mpDrawModel;
+protected:
+ virtual ~SwDrawModellListener_Impl() override;
+
+public:
+ explicit SwDrawModellListener_Impl( SdrModel *pDrawModel );
+
+ // css::document::XEventBroadcaster
+ virtual void SAL_CALL addEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const uno::Reference< document::XEventListener >& xListener ) override;
+ // css::document::XShapeEventBroadcaster
+ virtual void SAL_CALL addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+ virtual void SAL_CALL removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::document::XShapeEventListener >& xListener ) override;
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ void Dispose();
+};
+
+}
+
+SwDrawModellListener_Impl::SwDrawModellListener_Impl( SdrModel *pDrawModel ) :
+ maEventListeners( maListenerMutex ),
+ mpDrawModel( pDrawModel )
+{
+ StartListening( *mpDrawModel );
+}
+
+SwDrawModellListener_Impl::~SwDrawModellListener_Impl()
+{
+ Dispose();
+}
+
+void SAL_CALL SwDrawModellListener_Impl::addEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ maEventListeners.addInterface( xListener );
+}
+
+void SAL_CALL SwDrawModellListener_Impl::removeEventListener( const uno::Reference< document::XEventListener >& xListener )
+{
+ maEventListeners.removeInterface( xListener );
+}
+
+void SAL_CALL SwDrawModellListener_Impl::addShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ assert(xShape.is() && "no shape?");
+ osl::MutexGuard aGuard(maListenerMutex);
+ maShapeListeners.emplace(xShape, xListener);
+}
+
+void SAL_CALL SwDrawModellListener_Impl::removeShapeEventListener(
+ const css::uno::Reference< css::drawing::XShape >& xShape,
+ const uno::Reference< document::XShapeEventListener >& xListener )
+{
+ osl::MutexGuard aGuard(maListenerMutex);
+ auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
+ for (auto it = itBegin; it != itEnd; ++it)
+ if (it->second == xListener)
+ {
+ maShapeListeners.erase(it);
+ return;
+ }
+}
+
+void SwDrawModellListener_Impl::Notify( SfxBroadcaster& /*rBC*/,
+ const SfxHint& rHint )
+{
+ // do not broadcast notifications for writer fly frames, because there
+ // are no shapes that need to know about them.
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint *pSdrHint = static_cast<const SdrHint*>( &rHint );
+ if (pSdrHint->GetObject() &&
+ ( dynamic_cast< const SwFlyDrawObj* >(pSdrHint->GetObject()) != nullptr ||
+ dynamic_cast< const SwVirtFlyDrawObj* >(pSdrHint->GetObject()) != nullptr ||
+ isType<SdrObject>(pSdrHint->GetObject()) ) )
+ {
+ return;
+ }
+
+ OSL_ENSURE( mpDrawModel, "draw model listener is disposed" );
+ if( !mpDrawModel )
+ return;
+
+ document::EventObject aEvent;
+ if( !SvxUnoDrawMSFactory::createEvent( mpDrawModel, pSdrHint, aEvent ) )
+ return;
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter( maEventListeners );
+ while( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->notifyEvent( aEvent );
+ }
+ catch( uno::RuntimeException const & )
+ {
+ TOOLS_WARN_EXCEPTION("sw.a11y", "Runtime exception caught while notifying shape");
+ }
+ }
+
+ // right now, we're only handling the specific event necessary to fix this performance problem
+ if (pSdrHint->GetKind() == SdrHintKind::ObjectChange)
+ {
+ auto pSdrObject = const_cast<SdrObject*>(pSdrHint->GetObject());
+ uno::Reference<drawing::XShape> xShape(pSdrObject->getUnoShape(), uno::UNO_QUERY);
+ osl::MutexGuard aGuard(maListenerMutex);
+ auto [itBegin, itEnd] = maShapeListeners.equal_range(xShape);
+ for (auto it = itBegin; it != itEnd; ++it)
+ it->second->notifyShapeEvent(aEvent);
+ }
+}
+
+void SwDrawModellListener_Impl::Dispose()
+{
+ if (mpDrawModel != nullptr) {
+ EndListening( *mpDrawModel );
+ }
+ mpDrawModel = nullptr;
+}
+
+typedef std::pair < const SdrObject *, ::rtl::Reference < ::accessibility::AccessibleShape > > SwAccessibleObjShape_Impl;
+
+class SwAccessibleShapeMap_Impl
+{
+public:
+
+ typedef const SdrObject * key_type;
+ typedef uno::WeakReference<XAccessible> mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef std::map<key_type, mapped_type>::iterator iterator;
+ typedef std::map<key_type, mapped_type>::const_iterator const_iterator;
+
+private:
+
+ ::accessibility::AccessibleShapeTreeInfo maInfo;
+ std::map<key_type, mapped_type> maMap;
+
+public:
+
+ explicit SwAccessibleShapeMap_Impl( SwAccessibleMap const *pMap )
+ {
+ maInfo.SetSdrView( pMap->GetShell()->GetDrawView() );
+ maInfo.SetWindow( pMap->GetShell()->GetWin() );
+ maInfo.SetViewForwarder( pMap );
+ uno::Reference < document::XShapeEventBroadcaster > xModelBroadcaster =
+ new SwDrawModellListener_Impl(
+ pMap->GetShell()->getIDocumentDrawModelAccess().GetOrCreateDrawModel() );
+ maInfo.SetModelBroadcaster( xModelBroadcaster );
+ }
+
+ ~SwAccessibleShapeMap_Impl();
+
+ const ::accessibility::AccessibleShapeTreeInfo& GetInfo() const { return maInfo; }
+
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> Copy( size_t& rSize,
+ const SwFEShell *pFESh,
+ SwAccessibleObjShape_Impl **pSelShape ) const;
+
+ iterator end() { return maMap.end(); }
+ const_iterator cbegin() const { return maMap.cbegin(); }
+ const_iterator cend() const { return maMap.cend(); }
+ bool empty() const { return maMap.empty(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+SwAccessibleShapeMap_Impl::~SwAccessibleShapeMap_Impl()
+{
+ uno::Reference < document::XEventBroadcaster > xBrd( maInfo.GetModelBroadcaster() );
+ if( xBrd.is() )
+ static_cast < SwDrawModellListener_Impl * >( xBrd.get() )->Dispose();
+}
+
+std::unique_ptr<SwAccessibleObjShape_Impl[]>
+ SwAccessibleShapeMap_Impl::Copy(
+ size_t& rSize, const SwFEShell *pFESh,
+ SwAccessibleObjShape_Impl **pSelStart ) const
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+
+ size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+ rSize = maMap.size();
+
+ if( rSize > 0 )
+ {
+ pShapes.reset(new SwAccessibleObjShape_Impl[rSize]);
+
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ pSelShape = &(pShapes[rSize]);
+ for( const auto& rEntry : maMap )
+ {
+ const SdrObject *pObj = rEntry.first;
+ uno::Reference < XAccessible > xAcc( rEntry.second );
+ if( nSelShapes && pFESh && pFESh->IsObjSelected( *pObj ) )
+ {
+ // selected objects are inserted from the back
+ --pSelShape;
+ pSelShape->first = pObj;
+ pSelShape->second =
+ static_cast < ::accessibility::AccessibleShape* >(
+ xAcc.get() );
+ --nSelShapes;
+ }
+ else
+ {
+ pShape->first = pObj;
+ pShape->second =
+ static_cast < ::accessibility::AccessibleShape* >(
+ xAcc.get() );
+ ++pShape;
+ }
+ }
+ assert(pSelShape == pShape);
+ }
+
+ if( pSelStart )
+ *pSelStart = pSelShape;
+
+ return pShapes;
+}
+
+struct SwAccessibleEvent_Impl
+{
+public:
+ enum EventType { CARET_OR_STATES,
+ INVALID_CONTENT,
+ POS_CHANGED,
+ CHILD_POS_CHANGED,
+ SHAPE_SELECTION,
+ DISPOSE,
+ INVALID_ATTR };
+
+private:
+ SwRect maOldBox; // the old bounds for CHILD_POS_CHANGED
+ // and POS_CHANGED
+ uno::WeakReference < XAccessible > mxAcc; // The object that fires the event
+ SwAccessibleChild maFrameOrObj; // the child for CHILD_POS_CHANGED and
+ // the same as xAcc for any other
+ // event type
+ EventType meType; // The event type
+ AccessibleStates mnStates; // check states or update caret pos
+
+public:
+ const SwFrame* mpParentFrame; // The object that fires the event
+ bool IsNoXaccParentFrame() const
+ {
+ return CHILD_POS_CHANGED == meType && mpParentFrame != nullptr;
+ }
+
+public:
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ const SwAccessibleChild& rFrameOrObj )
+ : mxAcc( pA ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {}
+
+ SwAccessibleEvent_Impl( EventType eT,
+ const SwAccessibleChild& rFrameOrObj )
+ : maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert(SwAccessibleEvent_Impl::DISPOSE == meType &&
+ "wrong event constructor, DISPOSE only");
+ }
+
+ explicit SwAccessibleEvent_Impl( EventType eT )
+ : meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert(SwAccessibleEvent_Impl::SHAPE_SELECTION == meType &&
+ "wrong event constructor, SHAPE_SELECTION only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ const SwAccessibleChild& rFrameOrObj,
+ const SwRect& rR )
+ : maOldBox( rR ),
+ mxAcc( pA ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( nullptr )
+ {
+ assert((SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType ||
+ SwAccessibleEvent_Impl::POS_CHANGED == meType) &&
+ "wrong event constructor, (CHILD_)POS_CHANGED only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT,
+ SwAccessibleContext *pA,
+ const SwAccessibleChild& rFrameOrObj,
+ const AccessibleStates _nStates )
+ : mxAcc( pA ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( _nStates ),
+ mpParentFrame( nullptr )
+ {
+ assert( SwAccessibleEvent_Impl::CARET_OR_STATES == meType &&
+ "wrong event constructor, CARET_OR_STATES only" );
+ }
+
+ SwAccessibleEvent_Impl( EventType eT, const SwFrame *pParentFrame,
+ const SwAccessibleChild& rFrameOrObj, const SwRect& rR ) :
+ maOldBox( rR ),
+ maFrameOrObj( rFrameOrObj ),
+ meType( eT ),
+ mnStates( AccessibleStates::NONE ),
+ mpParentFrame( pParentFrame )
+ {
+ assert( SwAccessibleEvent_Impl::CHILD_POS_CHANGED == meType &&
+ "wrong event constructor, CHILD_POS_CHANGED only" );
+ }
+
+ // <SetType(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetType( EventType eT )
+ {
+ meType = eT;
+ }
+ EventType GetType() const
+ {
+ return meType;
+ }
+
+ ::rtl::Reference < SwAccessibleContext > GetContext() const
+ {
+ uno::Reference < XAccessible > xTmp( mxAcc );
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xTmp.get() ) );
+
+ return xAccImpl;
+ }
+
+ const SwRect& GetOldBox() const
+ {
+ return maOldBox;
+ }
+ // <SetOldBox(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetOldBox( const SwRect& rOldBox )
+ {
+ maOldBox = rOldBox;
+ }
+
+ const SwAccessibleChild& GetFrameOrObj() const
+ {
+ return maFrameOrObj;
+ }
+
+ // <SetStates(..)> only used in method <SwAccessibleMap::AppendEvent(..)>
+ void SetStates( AccessibleStates _nStates )
+ {
+ mnStates |= _nStates;
+ }
+
+ bool IsUpdateCursorPos() const
+ {
+ return bool(mnStates & AccessibleStates::CARET);
+ }
+ bool IsInvalidateStates() const
+ {
+ return bool(mnStates & (AccessibleStates::EDITABLE | AccessibleStates::OPAQUE));
+ }
+ bool IsInvalidateRelation() const
+ {
+ return bool(mnStates & (AccessibleStates::RELATION_FROM | AccessibleStates::RELATION_TO));
+ }
+ bool IsInvalidateTextSelection() const
+ {
+ return bool( mnStates & AccessibleStates::TEXT_SELECTION_CHANGED );
+ }
+
+ bool IsInvalidateTextAttrs() const
+ {
+ return bool( mnStates & AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
+ }
+
+ AccessibleStates GetStates() const
+ {
+ return mnStates;
+ }
+
+ AccessibleStates GetAllStates() const
+ {
+ return mnStates;
+ }
+};
+
+class SwAccessibleEventList_Impl
+{
+ std::list<SwAccessibleEvent_Impl> maEvents;
+ bool mbFiring;
+
+public:
+ SwAccessibleEventList_Impl()
+ : mbFiring( false )
+ {}
+
+ void SetFiring()
+ {
+ mbFiring = true;
+ }
+ bool IsFiring() const
+ {
+ return mbFiring;
+ }
+
+ void MoveMissingXAccToEnd();
+
+ size_t size() const { return maEvents.size(); }
+ std::list<SwAccessibleEvent_Impl>::iterator begin() { return maEvents.begin(); }
+ std::list<SwAccessibleEvent_Impl>::iterator end() { return maEvents.end(); }
+ std::list<SwAccessibleEvent_Impl>::iterator insert( const std::list<SwAccessibleEvent_Impl>::iterator& aIter,
+ const SwAccessibleEvent_Impl& rEvent )
+ {
+ return maEvents.insert( aIter, rEvent );
+ }
+ std::list<SwAccessibleEvent_Impl>::iterator erase( const std::list<SwAccessibleEvent_Impl>::iterator& aPos )
+ {
+ return maEvents.erase( aPos );
+ }
+};
+
+// see comment in SwAccessibleMap::InvalidatePosOrSize()
+// last case "else if(pParent)" for why this surprising hack exists
+void SwAccessibleEventList_Impl::MoveMissingXAccToEnd()
+{
+ size_t nSize = size();
+ if (nSize < 2 )
+ {
+ return;
+ }
+ SwAccessibleEventList_Impl lstEvent;
+ for (auto li = begin(); li != end(); )
+ {
+ if (li->IsNoXaccParentFrame())
+ {
+ lstEvent.insert(lstEvent.end(), *li);
+ li = erase(li);
+ }
+ else
+ ++li;
+ }
+ assert(size() + lstEvent.size() == nSize);
+ maEvents.insert(end(),lstEvent.begin(),lstEvent.end());
+ assert(size() == nSize);
+}
+
+namespace {
+
+struct SwAccessibleChildFunc
+{
+ bool operator()( const SwAccessibleChild& r1,
+ const SwAccessibleChild& r2 ) const
+ {
+ const void *p1 = r1.GetSwFrame()
+ ? static_cast < const void * >( r1.GetSwFrame())
+ : ( r1.GetDrawObject()
+ ? static_cast < const void * >( r1.GetDrawObject() )
+ : static_cast < const void * >( r1.GetWindow() ) );
+ const void *p2 = r2.GetSwFrame()
+ ? static_cast < const void * >( r2.GetSwFrame())
+ : ( r2.GetDrawObject()
+ ? static_cast < const void * >( r2.GetDrawObject() )
+ : static_cast < const void * >( r2.GetWindow() ) );
+ return p1 < p2;
+ }
+};
+
+}
+
+class SwAccessibleEventMap_Impl
+{
+public:
+ typedef SwAccessibleChild key_type;
+ typedef std::list<SwAccessibleEvent_Impl>::iterator mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwAccessibleChildFunc key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map <key_type,mapped_type,key_compare> maMap;
+public:
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+namespace {
+
+struct SwAccessibleParaSelection
+{
+ TextFrameIndex nStartOfSelection;
+ TextFrameIndex nEndOfSelection;
+
+ SwAccessibleParaSelection(const TextFrameIndex nStartOfSelection_,
+ const TextFrameIndex nEndOfSelection_)
+ : nStartOfSelection(nStartOfSelection_)
+ , nEndOfSelection(nEndOfSelection_)
+ {}
+};
+
+struct SwXAccWeakRefComp
+{
+ bool operator()( const uno::WeakReference<XAccessible>& _rXAccWeakRef1,
+ const uno::WeakReference<XAccessible>& _rXAccWeakRef2 ) const
+ {
+ return _rXAccWeakRef1.get() < _rXAccWeakRef2.get();
+ }
+};
+
+}
+
+class SwAccessibleSelectedParas_Impl
+{
+public:
+ typedef uno::WeakReference < XAccessible > key_type;
+ typedef SwAccessibleParaSelection mapped_type;
+ typedef std::pair<const key_type,mapped_type> value_type;
+ typedef SwXAccWeakRefComp key_compare;
+ typedef std::map<key_type,mapped_type,key_compare>::iterator iterator;
+ typedef std::map<key_type,mapped_type,key_compare>::const_iterator const_iterator;
+private:
+ std::map<key_type,mapped_type,key_compare> maMap;
+public:
+ iterator begin() { return maMap.begin(); }
+ iterator end() { return maMap.end(); }
+ iterator find(const key_type& key) { return maMap.find(key); }
+ template<class... Args>
+ std::pair<iterator,bool> emplace(Args&&... args) { return maMap.emplace(std::forward<Args>(args)...); }
+ iterator erase(const_iterator const & pos) { return maMap.erase(pos); }
+};
+
+// helper class that stores preview data
+class SwAccPreviewData
+{
+ typedef std::vector<tools::Rectangle> Rectangles;
+ Rectangles maPreviewRects;
+ Rectangles maLogicRects;
+
+ SwRect maVisArea;
+ Fraction maScale;
+
+ const SwPageFrame *mpSelPage;
+
+ /** adjust logic page rectangle to its visible part
+
+ @param _iorLogicPgSwRect
+ input/output parameter - reference to the logic page rectangle, which
+ has to be adjusted.
+
+ @param _rPreviewPgSwRect
+ input parameter - constant reference to the corresponding preview page
+ rectangle; needed to determine the visible part of the logic page rectangle.
+
+ @param _rPreviewWinSize
+ input parameter - constant reference to the preview window size in TWIP;
+ needed to determine the visible part of the logic page rectangle
+ */
+ static void AdjustLogicPgRectToVisibleArea( SwRect& _iorLogicPgSwRect,
+ const SwRect& _rPreviewPgSwRect,
+ const Size& _rPreviewWinSize );
+
+public:
+ SwAccPreviewData();
+
+ void Update( const SwAccessibleMap& rAccMap,
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize );
+
+ void InvalidateSelection( const SwPageFrame* _pSelectedPageFrame );
+
+ const SwRect& GetVisArea() const { return maVisArea;}
+
+ /** Adjust the MapMode so that the preview page appears at the
+ * proper position. rPoint identifies the page for which the
+ * MapMode should be adjusted. If bFromPreview is true, rPoint is
+ * a preview coordinate; else it's a document coordinate. */
+ void AdjustMapMode( MapMode& rMapMode,
+ const Point& rPoint ) const;
+
+ const SwPageFrame *GetSelPage() const { return mpSelPage; }
+
+ void DisposePage(const SwPageFrame *pPageFrame );
+};
+
+SwAccPreviewData::SwAccPreviewData() :
+ mpSelPage( nullptr )
+{
+}
+
+void SwAccPreviewData::Update( const SwAccessibleMap& rAccMap,
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ // store preview scaling, maximal preview page size and selected page
+ maScale = _rScale;
+ mpSelPage = _pSelectedPageFrame;
+
+ // prepare loop on preview pages
+ maPreviewRects.clear();
+ maLogicRects.clear();
+ SwAccessibleChild aPage;
+ maVisArea.Clear();
+
+ // loop on preview pages to calculate <maPreviewRects>, <maLogicRects> and
+ // <maVisArea>
+ for ( auto & rpPreviewPage : _rPreviewPages )
+ {
+ aPage = rpPreviewPage->pPage;
+
+ // add preview page rectangle to <maPreviewRects>
+ tools::Rectangle aPreviewPgRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize );
+ maPreviewRects.push_back( aPreviewPgRect );
+
+ // add logic page rectangle to <maLogicRects>
+ SwRect aLogicPgSwRect( aPage.GetBox( rAccMap ) );
+ tools::Rectangle aLogicPgRect( aLogicPgSwRect.SVRect() );
+ maLogicRects.push_back( aLogicPgRect );
+ // union visible area with visible part of logic page rectangle
+ if ( rpPreviewPage->bVisible )
+ {
+ if ( !rpPreviewPage->pPage->IsEmptyPage() )
+ {
+ AdjustLogicPgRectToVisibleArea( aLogicPgSwRect,
+ SwRect( aPreviewPgRect ),
+ _rPreviewWinSize );
+ }
+ if ( maVisArea.IsEmpty() )
+ maVisArea = aLogicPgSwRect;
+ else
+ maVisArea.Union( aLogicPgSwRect );
+ }
+ }
+}
+
+void SwAccPreviewData::InvalidateSelection( const SwPageFrame* _pSelectedPageFrame )
+{
+ mpSelPage = _pSelectedPageFrame;
+ assert(mpSelPage);
+}
+
+namespace {
+
+struct ContainsPredicate
+{
+ const Point& mrPoint;
+ explicit ContainsPredicate( const Point& rPoint ) : mrPoint(rPoint) {}
+ bool operator() ( const tools::Rectangle& rRect ) const
+ {
+ return rRect.Contains( mrPoint );
+ }
+};
+
+}
+
+void SwAccPreviewData::AdjustMapMode( MapMode& rMapMode,
+ const Point& rPoint ) const
+{
+ // adjust scale
+ rMapMode.SetScaleX( maScale );
+ rMapMode.SetScaleY( maScale );
+
+ // find proper rectangle
+ Rectangles::const_iterator aBegin = maLogicRects.begin();
+ Rectangles::const_iterator aEnd = maLogicRects.end();
+ Rectangles::const_iterator aFound = std::find_if( aBegin, aEnd,
+ ContainsPredicate( rPoint ) );
+
+ if( aFound != aEnd )
+ {
+ // found! set new origin
+ Point aPoint = (maPreviewRects.begin() + (aFound - aBegin))->TopLeft();
+ aPoint -= (maLogicRects.begin() + (aFound-aBegin))->TopLeft();
+ rMapMode.SetOrigin( aPoint );
+ }
+ // else: don't adjust MapMode
+}
+
+void SwAccPreviewData::DisposePage(const SwPageFrame *pPageFrame )
+{
+ if( mpSelPage == pPageFrame )
+ mpSelPage = nullptr;
+}
+
+// adjust logic page rectangle to its visible part
+void SwAccPreviewData::AdjustLogicPgRectToVisibleArea(
+ SwRect& _iorLogicPgSwRect,
+ const SwRect& _rPreviewPgSwRect,
+ const Size& _rPreviewWinSize )
+{
+ // determine preview window rectangle
+ const SwRect aPreviewWinSwRect( Point( 0, 0 ), _rPreviewWinSize );
+ // calculate visible preview page rectangle
+ SwRect aVisPreviewPgSwRect( _rPreviewPgSwRect );
+ aVisPreviewPgSwRect.Intersection( aPreviewWinSwRect );
+ // adjust logic page rectangle
+ SwTwips nTmpDiff;
+ // left
+ nTmpDiff = aVisPreviewPgSwRect.Left() - _rPreviewPgSwRect.Left();
+ _iorLogicPgSwRect.AddLeft( nTmpDiff );
+ // top
+ nTmpDiff = aVisPreviewPgSwRect.Top() - _rPreviewPgSwRect.Top();
+ _iorLogicPgSwRect.AddTop( nTmpDiff );
+ // right
+ nTmpDiff = _rPreviewPgSwRect.Right() - aVisPreviewPgSwRect.Right();
+ _iorLogicPgSwRect.AddRight( - nTmpDiff );
+ // bottom
+ nTmpDiff = _rPreviewPgSwRect.Bottom() - aVisPreviewPgSwRect.Bottom();
+ _iorLogicPgSwRect.AddBottom( - nTmpDiff );
+}
+
+static bool AreInSameTable( const uno::Reference< XAccessible >& rAcc,
+ const SwFrame *pFrame )
+{
+ bool bRet = false;
+
+ if( pFrame && pFrame->IsCellFrame() && rAcc.is() )
+ {
+ // Is it in the same table? We check that
+ // by comparing the last table frame in the
+ // follow chain, because that's cheaper than
+ // searching the first one.
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( rAcc.get() );
+ if( pAccImpl->GetFrame()->IsCellFrame() )
+ {
+ const SwTabFrame *pTabFrame1 = pAccImpl->GetFrame()->FindTabFrame();
+ if (pTabFrame1)
+ {
+ while (pTabFrame1->GetFollow())
+ pTabFrame1 = pTabFrame1->GetFollow();
+ }
+
+ const SwTabFrame *pTabFrame2 = pFrame->FindTabFrame();
+ if (pTabFrame2)
+ {
+ while (pTabFrame2->GetFollow())
+ pTabFrame2 = pTabFrame2->GetFollow();
+ }
+
+ bRet = (pTabFrame1 == pTabFrame2);
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleMap::FireEvent( const SwAccessibleEvent_Impl& rEvent )
+{
+ ::rtl::Reference < SwAccessibleContext > xAccImpl( rEvent.GetContext() );
+ if (!xAccImpl.is() && rEvent.mpParentFrame != nullptr)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( rEvent.mpParentFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if (xAcc.is())
+ {
+ uno::Reference < XAccessibleContext > xContext(xAcc,uno::UNO_QUERY);
+ if (xContext.is() && xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ xAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ }
+ }
+ if( SwAccessibleEvent_Impl::SHAPE_SELECTION == rEvent.GetType() )
+ {
+ DoInvalidateShapeSelection();
+ }
+ else if( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ if ( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
+ rEvent.IsInvalidateTextAttrs() )
+ {
+ xAccImpl->InvalidateAttr();
+ }
+ switch( rEvent.GetType() )
+ {
+ case SwAccessibleEvent_Impl::INVALID_CONTENT:
+ xAccImpl->InvalidateContent();
+ break;
+ case SwAccessibleEvent_Impl::POS_CHANGED:
+ xAccImpl->InvalidatePosOrSize( rEvent.GetOldBox() );
+ break;
+ case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
+ xAccImpl->InvalidateChildPosOrSize( rEvent.GetFrameOrObj(),
+ rEvent.GetOldBox() );
+ break;
+ case SwAccessibleEvent_Impl::DISPOSE:
+ assert(!"dispose event has been stored");
+ break;
+ case SwAccessibleEvent_Impl::INVALID_ATTR:
+ // nothing to do here - handled above
+ break;
+ default:
+ break;
+ }
+ if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
+ {
+ if( rEvent.IsUpdateCursorPos() )
+ xAccImpl->InvalidateCursorPos();
+ if( rEvent.IsInvalidateStates() )
+ xAccImpl->InvalidateStates( rEvent.GetStates() );
+ if( rEvent.IsInvalidateRelation() )
+ {
+ // both events CONTENT_FLOWS_FROM_RELATION_CHANGED and
+ // CONTENT_FLOWS_TO_RELATION_CHANGED are possible
+ if ( rEvent.GetAllStates() & AccessibleStates::RELATION_FROM )
+ {
+ xAccImpl->InvalidateRelation(
+ AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED );
+ }
+ if ( rEvent.GetAllStates() & AccessibleStates::RELATION_TO )
+ {
+ xAccImpl->InvalidateRelation(
+ AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
+ }
+ }
+
+ if ( rEvent.IsInvalidateTextSelection() )
+ {
+ xAccImpl->InvalidateTextSelection();
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::AppendEvent( const SwAccessibleEvent_Impl& rEvent )
+{
+ osl::MutexGuard aGuard( maEventMutex );
+
+ if( !mpEvents )
+ mpEvents.reset(new SwAccessibleEventList_Impl);
+ if( !mpEventMap )
+ mpEventMap.reset(new SwAccessibleEventMap_Impl);
+
+ if( mpEvents->IsFiring() )
+ {
+ // While events are fired new ones are generated. They have to be fired
+ // now. This does not work for DISPOSE events!
+ OSL_ENSURE( rEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE,
+ "dispose event while firing events" );
+ FireEvent( rEvent );
+ }
+ else
+ {
+
+ SwAccessibleEventMap_Impl::iterator aIter =
+ mpEventMap->find( rEvent.GetFrameOrObj() );
+ if( aIter != mpEventMap->end() )
+ {
+ SwAccessibleEvent_Impl aEvent( *(*aIter).second );
+ assert( aEvent.GetType() != SwAccessibleEvent_Impl::DISPOSE &&
+ "dispose events should not be stored" );
+ bool bAppendEvent = true;
+ switch( rEvent.GetType() )
+ {
+ case SwAccessibleEvent_Impl::CARET_OR_STATES:
+ // A CARET_OR_STATES event is added to any other
+ // event only. It is broadcasted after any other event, so the
+ // event should be put to the back.
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ aEvent.SetStates( rEvent.GetAllStates() );
+ break;
+ case SwAccessibleEvent_Impl::INVALID_CONTENT:
+ // An INVALID_CONTENT event overwrites a CARET_OR_STATES
+ // event (but keeps its flags) and it is contained in a
+ // POS_CHANGED event.
+ // Therefore, the event's type has to be adapted and the event
+ // has to be put at the end.
+ //
+ // fdo#56031 An INVALID_CONTENT event overwrites a INVALID_ATTR
+ // event and overwrites its flags
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ if( aEvent.GetType() == SwAccessibleEvent_Impl::CARET_OR_STATES )
+ aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
+ else if ( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR )
+ {
+ aEvent.SetType( SwAccessibleEvent_Impl::INVALID_CONTENT );
+ aEvent.SetStates( rEvent.GetAllStates() );
+ }
+
+ break;
+ case SwAccessibleEvent_Impl::POS_CHANGED:
+ // A pos changed event overwrites CARET_STATES (keeping its
+ // flags) as well as INVALID_CONTENT. The old box position
+ // has to be stored however if the old event is not a
+ // POS_CHANGED itself.
+ OSL_ENSURE( aEvent.GetType() != SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ if( aEvent.GetType() != SwAccessibleEvent_Impl::POS_CHANGED )
+ aEvent.SetOldBox( rEvent.GetOldBox() );
+ aEvent.SetType( SwAccessibleEvent_Impl::POS_CHANGED );
+ break;
+ case SwAccessibleEvent_Impl::CHILD_POS_CHANGED:
+ // CHILD_POS_CHANGED events can only follow CHILD_POS_CHANGED
+ // events. The only action that needs to be done again is
+ // to put the old event to the back. The new one cannot be used,
+ // because we are interested in the old frame bounds.
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ "invalid event combination" );
+ break;
+ case SwAccessibleEvent_Impl::SHAPE_SELECTION:
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::SHAPE_SELECTION,
+ "invalid event combination" );
+ break;
+ case SwAccessibleEvent_Impl::DISPOSE:
+ // DISPOSE events overwrite all others. They are not stored
+ // but executed immediately to avoid broadcasting of
+ // nonfunctional objects. So what needs to be done here is to
+ // remove all events for the frame in question.
+ bAppendEvent = false;
+ break;
+ case SwAccessibleEvent_Impl::INVALID_ATTR:
+ OSL_ENSURE( aEvent.GetType() == SwAccessibleEvent_Impl::INVALID_ATTR,
+ "invalid event combination" );
+ break;
+ }
+ if( bAppendEvent )
+ {
+ mpEvents->erase( (*aIter).second );
+ (*aIter).second = mpEvents->insert( mpEvents->end(), aEvent );
+ }
+ else
+ {
+ mpEvents->erase( (*aIter).second );
+ mpEventMap->erase( aIter );
+ }
+ }
+ else if( SwAccessibleEvent_Impl::DISPOSE != rEvent.GetType() )
+ {
+ mpEventMap->emplace( rEvent.GetFrameOrObj(),
+ mpEvents->insert( mpEvents->end(), rEvent ) );
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateCursorPosition(
+ const uno::Reference< XAccessible >& rAcc )
+{
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( rAcc.get() );
+ assert(pAccImpl);
+ assert(pAccImpl->GetFrame());
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild(pAccImpl->GetFrame()),
+ AccessibleStates::CARET );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ // While firing events the current frame might have
+ // been disposed because it moved out of the visible area.
+ // Setting the cursor for such frames is useless and even
+ // causes asserts.
+ if( pAccImpl->GetFrame() )
+ pAccImpl->InvalidateCursorPos();
+ }
+}
+
+void SwAccessibleMap::InvalidateShapeSelection()
+{
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::SHAPE_SELECTION );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ DoInvalidateShapeSelection();
+ }
+}
+
+//This method should implement the following functions:
+//1.find the shape objects and set the selected state.
+//2.find the Swframe objects and set the selected state.
+//3.find the paragraph objects and set the selected state.
+void SwAccessibleMap::InvalidateShapeInParaSelection()
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>(pVSh);
+ SwPaM* pCursor = pFESh ? pFESh->GetCursor( false /* ??? */ ) : nullptr;
+
+ //const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+ }
+
+ bool bIsSelAll =IsDocumentSelAll();
+
+ if( mpShapeMap )
+ {
+ //Checked for shapes.
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+
+ if( bIsSelAll)
+ {
+ while( aIter != aEndIter )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+
+ ++aIter;
+ }
+ }
+ else
+ {
+ while( aIter != aEndIter )
+ {
+ const SwFrameFormat *pFrameFormat = (*aIter).first ? ::FindFrameFormat( (*aIter).first ) : nullptr;
+ if( !pFrameFormat )
+ {
+ ++aIter;
+ continue;
+ }
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ const SwPosition *pPos = rAnchor.GetContentAnchor();
+
+ if(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if(xAcc.is())
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+
+ ++aIter;
+ continue;
+ }
+
+ if( !pPos )
+ {
+ ++aIter;
+ continue;
+ }
+ if( pPos->nNode.GetNode().GetTextNode() )
+ {
+ int nIndex = pPos->nContent.GetIndex();
+ bool bMarked = false;
+ if( pCursor != nullptr )
+ {
+ const SwTextNode* pNode = pPos->nNode.GetNode().GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pVSh->GetLayout())));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ SwNodeOffset nHere = pNode->GetIndex();
+
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ bMarked = true;
+ // check whether nHere is 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ SwNodeOffset nStartIndex = pStart->nNode.GetIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ SwNodeOffset nEndIndex = pEnd->nNode.GetIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ if( ( ((nHere == nStartIndex) && (nIndex >= pStart->nContent.GetIndex())) || (nHere > nStartIndex) )
+ &&( ((nHere == nEndIndex) && (nIndex < pEnd->nContent.GetIndex())) || (nHere < nEndIndex) ) )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if( xAcc.is() )
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ else if( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ uno::Reference<XAccessible> const xAcc((*aIter).second);
+ if (xAcc.is())
+ {
+ if (IsSelectFrameAnchoredAtPara(*pPos, *pStart, *pEnd))
+ {
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ }
+ else if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ uno::Reference<XAccessible> const xAcc((*aIter).second);
+ if (xAcc.is())
+ {
+ if (IsDestroyFrameAnchoredAtChar(*pPos, *pStart, *pEnd))
+ {
+ static_cast<::accessibility::AccessibleShape*>(xAcc.get())->SetState( AccessibleStateType::SELECTED );
+ }
+ else
+ {
+ static_cast<::accessibility::AccessibleShape*>(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !bMarked )
+ {
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ size_t nNumShapes = nShapes;
+ while( nNumShapes )
+ {
+ if( pShape < pSelShape && (pShape->first==(*aIter).first) )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ if(xAcc.is())
+ static_cast < ::accessibility::AccessibleShape* >(xAcc.get())->ResetState( AccessibleStateType::SELECTED );
+ }
+ --nNumShapes;
+ ++pShape;
+ }
+ }
+ }
+
+ ++aIter;
+ }//while( aIter != aEndIter )
+ }//else
+ }
+
+ pShapes.reset();
+
+ //Checked for FlyFrame
+ if (mpFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
+ while( aIter != mpFrameMap->end() )
+ {
+ const SwFrame *pFrame = (*aIter).first;
+ if(pFrame->IsFlyFrame())
+ {
+ uno::Reference < XAccessible > xAcc = (*aIter).second;
+
+ if(xAcc.is())
+ {
+ SwAccessibleFrameBase *pAccFrame = static_cast< SwAccessibleFrameBase * >(xAcc.get());
+ bool bFrameChanged = pAccFrame->SetSelectedState( true );
+ if (bFrameChanged)
+ {
+ const SwFlyFrame *pFlyFrame = static_cast< const SwFlyFrame * >( pFrame );
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ uno::Reference< XAccessible > xAccParent = pAccFrame->getAccessibleParent();
+ if (xAccParent.is())
+ {
+ uno::Reference< XAccessibleContext > xAccContext = xAccParent->getAccessibleContext();
+ if(xAccContext.is() && xAccContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xAccContext.get());
+ if(pAccFrame->IsSelectedInDoc())
+ {
+ m_setParaAdd.insert(pAccPara);
+ }
+ else if(m_setParaAdd.count(pAccPara) == 0)
+ {
+ m_setParaRemove.insert(pAccPara);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ++aIter;
+ }
+ }
+
+ typedef std::vector< SwAccessibleContext* > VEC_PARA;
+ VEC_PARA vecAdd;
+ VEC_PARA vecRemove;
+ //Checked for Paras.
+ bool bMarkChanged = false;
+ SwAccessibleContextMap_Impl mapTemp;
+ if( pCursor != nullptr )
+ {
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ if( rTmpCursor.HasMark() )
+ {
+ SwNodeIndex nStartIndex( rTmpCursor.Start()->nNode );
+ SwNodeIndex nEndIndex( rTmpCursor.End()->nNode );
+ for (; nStartIndex <= nEndIndex; ++nStartIndex)
+ {
+ SwFrame *pFrame = nullptr;
+ if(nStartIndex.GetNode().IsContentNode())
+ {
+ SwContentNode* pCNd = static_cast<SwContentNode*>(&(nStartIndex.GetNode()));
+ pFrame = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First();
+ if (mapTemp.find(pFrame) != mapTemp.end())
+ {
+ continue; // sw_redlinehide: once is enough
+ }
+ }
+ else if( nStartIndex.GetNode().IsTableNode() )
+ {
+ SwTableNode * pTable = static_cast<SwTableNode *>(&(nStartIndex.GetNode()));
+ SwTableFormat* pFormat = pTable->GetTable().GetFrameFormat();
+ pFrame = SwIterator<SwFrame, SwTableFormat>(*pFormat).First();
+ }
+
+ if( pFrame && mpFrameMap)
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc = (*aIter).second;
+ bool isChanged = false;
+ if( xAcc.is() )
+ {
+ isChanged = static_cast< SwAccessibleContext * >(xAcc.get())->SetSelectedState( true );
+ }
+ if(!isChanged)
+ {
+ SwAccessibleContextMap_Impl::iterator aEraseIter = mpSelectedFrameMap->find( pFrame );
+ if(aEraseIter != mpSelectedFrameMap->end())
+ mpSelectedFrameMap->erase(aEraseIter);
+ }
+ else
+ {
+ bMarkChanged = true;
+ vecAdd.push_back(static_cast< SwAccessibleContext * >(xAcc.get()));
+ }
+
+ mapTemp.emplace( pFrame, xAcc );
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !mpSelectedFrameMap )
+ mpSelectedFrameMap.reset( new SwAccessibleContextMap_Impl );
+ if( !mpSelectedFrameMap->empty() )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpSelectedFrameMap->begin();
+ while( aIter != mpSelectedFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc = (*aIter).second;
+ if(xAcc.is())
+ static_cast< SwAccessibleContext * >(xAcc.get())->SetSelectedState( false );
+ ++aIter;
+ vecRemove.push_back(static_cast< SwAccessibleContext * >(xAcc.get()));
+ }
+ bMarkChanged = true;
+ mpSelectedFrameMap->clear();
+ }
+
+ SwAccessibleContextMap_Impl::iterator aIter = mapTemp.begin();
+ while( aIter != mapTemp.end() )
+ {
+ mpSelectedFrameMap->emplace( (*aIter).first, (*aIter).second );
+ ++aIter;
+ }
+ mapTemp.clear();
+
+ if( !(bMarkChanged && mpFrameMap))
+ return;
+
+ for (SwAccessibleContext* pAccPara : vecAdd)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ if (pAccPara)
+ {
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ for (SwAccessibleContext* pAccPara : vecRemove)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ if (pAccPara)
+ {
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+//Merge with DoInvalidateShapeFocus
+void SwAccessibleMap::DoInvalidateShapeSelection(bool bInvalidateFocusMode /*=false*/)
+{
+ std::unique_ptr<SwAccessibleObjShape_Impl[]> pShapes;
+ SwAccessibleObjShape_Impl *pSelShape = nullptr;
+ size_t nShapes = 0;
+
+ const SwViewShell *pVSh = GetShell();
+ const SwFEShell *pFESh = dynamic_cast<const SwFEShell*>(pVSh);
+ const size_t nSelShapes = pFESh ? pFESh->IsObjSelected() : 0;
+
+ //when InvalidateFocus Call this function ,and the current selected shape count is not 1 ,
+ //return
+ if (bInvalidateFocusMode && nSelShapes != 1)
+ {
+ return;
+ }
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ pShapes = mpShapeMap->Copy( nShapes, pFESh, &pSelShape );
+ }
+
+ if( !pShapes )
+ return;
+
+ typedef std::vector< ::rtl::Reference < ::accessibility::AccessibleShape > > VEC_SHAPE;
+ VEC_SHAPE vecxShapeAdd;
+ VEC_SHAPE vecxShapeRemove;
+ int nCountSelectedShape=0;
+
+ vcl::Window *pWin = GetShell()->GetWin();
+ bool bFocused = pWin && pWin->HasFocus();
+ SwAccessibleObjShape_Impl *pShape = pShapes.get();
+ int nShapeCount = nShapes;
+ while( nShapeCount )
+ {
+ if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
+ {
+ if( pShape < pSelShape )
+ {
+ if(pShape->second->ResetState( AccessibleStateType::SELECTED ))
+ {
+ vecxShapeRemove.push_back(pShape->second);
+ }
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+ }
+ }
+ --nShapeCount;
+ ++pShape;
+ }
+
+ for (const auto& rpShape : vecxShapeRemove)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ pAccShape->CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE, uno::Any(), uno::Any());
+ }
+ }
+
+ pShape = pShapes.get();
+
+ while( nShapes )
+ {
+ if (pShape->second.is() && IsInSameLevel(pShape->first, pFESh))
+ {
+ if( pShape >= pSelShape )
+ {
+ //first fire focus event
+ if( bFocused && 1 == nSelShapes )
+ pShape->second->SetState( AccessibleStateType::FOCUSED );
+ else
+ pShape->second->ResetState( AccessibleStateType::FOCUSED );
+
+ if(pShape->second->SetState( AccessibleStateType::SELECTED ))
+ {
+ vecxShapeAdd.push_back(pShape->second);
+ }
+ ++nCountSelectedShape;
+ }
+ }
+
+ --nShapes;
+ ++pShape;
+ }
+
+ const unsigned int SELECTION_WITH_NUM = 10;
+ if (vecxShapeAdd.size() > SELECTION_WITH_NUM )
+ {
+ uno::Reference< XAccessible > xDoc = GetDocumentView( );
+ SwAccessibleContext * pCont = static_cast<SwAccessibleContext *>(xDoc.get());
+ if (pCont)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ pCont->FireAccessibleEvent(aEvent);
+ }
+ }
+ else
+ {
+ short nEventID = AccessibleEventId::SELECTION_CHANGED_ADD;
+ if (nCountSelectedShape <= 1 && vecxShapeAdd.size() == 1 )
+ {
+ nEventID = AccessibleEventId::SELECTION_CHANGED;
+ }
+ for (const auto& rpShape : vecxShapeAdd)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ pAccShape->CommitChange(nEventID, uno::Any(), uno::Any());
+ }
+ }
+ }
+
+ for (const auto& rpShape : vecxShapeAdd)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape)
+ {
+ SdrObject *pObj = SdrObject::getSdrObjectFromXShape(pAccShape->GetXShape());
+ SwFrameFormat *pFrameFormat = pObj ? FindFrameFormat( pObj ) : nullptr;
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
+ if (xPara.is())
+ {
+ uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
+ if (xParaContext.is() && xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
+ if (pAccPara)
+ {
+ m_setParaAdd.insert(pAccPara);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (const auto& rpShape : vecxShapeRemove)
+ {
+ ::accessibility::AccessibleShape *pAccShape = rpShape.get();
+ if (pAccShape && !pAccShape->IsDisposed())
+ {
+ uno::Reference< XAccessible > xPara = pAccShape->getAccessibleParent();
+ uno::Reference< XAccessibleContext > xParaContext = xPara->getAccessibleContext();
+ if (xParaContext.is() && xParaContext->getAccessibleRole() == AccessibleRole::PARAGRAPH)
+ {
+ SwAccessibleParagraph* pAccPara = static_cast< SwAccessibleParagraph *>(xPara.get());
+ if (m_setParaAdd.count(pAccPara) == 0 )
+ {
+ m_setParaRemove.insert(pAccPara);
+ }
+ }
+ }
+ }
+}
+
+SwAccessibleMap::SwAccessibleMap( SwViewShell *pSh ) :
+ mpVSh( pSh ),
+ mbShapeSelected( false ),
+ maDocName(SwAccessibleContext::GetResource(STR_ACCESS_DOC_NAME))
+{
+ pSh->GetLayout()->AddAccessibleShell();
+}
+
+SwAccessibleMap::~SwAccessibleMap()
+{
+ DBG_TESTSOLARMUTEX();
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpFrameMap )
+ {
+ const SwRootFrame *pRootFrame = GetShell()->GetLayout();
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ if( !xAcc.is() )
+ assert(false); // let's hope this can't happen? the vcl::Window apparently owns the top-level
+ //xAcc = new SwAccessibleDocument(shared_from_this());
+ }
+ }
+
+ if(xAcc.is())
+ {
+ SwAccessibleDocumentBase *const pAcc =
+ static_cast<SwAccessibleDocumentBase *>(xAcc.get());
+ pAcc->Dispose( true );
+ }
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->begin();
+ while( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xTmp = (*aIter).second;
+ if( xTmp.is() )
+ {
+ SwAccessibleContext *pTmp = static_cast< SwAccessibleContext * >( xTmp.get() );
+ assert(pTmp->GetMap() == nullptr); // must be disposed
+ }
+ ++aIter;
+ }
+ }
+#endif
+ {
+ osl::MutexGuard aGuard( maMutex );
+ assert((!mpFrameMap || mpFrameMap->empty()) &&
+ "Frame map should be empty after disposing the root frame");
+ assert((!mpShapeMap || mpShapeMap->empty()) &&
+ "Object map should be empty after disposing the root frame");
+ mpFrameMap.reset();
+ mpShapeMap.reset();
+ mvShapes.clear();
+ mpSelectedParas.reset();
+ }
+
+ mpPreview.reset();
+
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ assert(!mpEvents);
+ assert(!mpEventMap);
+ mpEventMap.reset();
+ mpEvents.reset();
+ }
+ mpVSh->GetLayout()->RemoveAccessibleShell();
+}
+
+uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView_(
+ bool bPagePreview )
+{
+ uno::Reference < XAccessible > xAcc;
+ bool bSetVisArea = false;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpFrameMap )
+ {
+ mpFrameMap.reset(new SwAccessibleContextMap_Impl);
+#if OSL_DEBUG_LEVEL > 0
+ mpFrameMap->mbLocked = false;
+#endif
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ assert(!mpFrameMap->mbLocked);
+ mpFrameMap->mbLocked = true;
+#endif
+
+ const SwRootFrame *pRootFrame = GetShell()->GetLayout();
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pRootFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ if( xAcc.is() )
+ {
+ bSetVisArea = true; // Set VisArea when map mutex is not locked
+ }
+ else
+ {
+ if( bPagePreview )
+ xAcc = new SwAccessiblePreview(shared_from_this());
+ else
+ xAcc = new SwAccessibleDocument(shared_from_this());
+
+ if( aIter != mpFrameMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpFrameMap->emplace( pRootFrame, xAcc );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ mpFrameMap->mbLocked = false;
+#endif
+ }
+
+ if( bSetVisArea )
+ {
+ SwAccessibleDocumentBase *pAcc =
+ static_cast< SwAccessibleDocumentBase * >( xAcc.get() );
+ pAcc->SetVisArea();
+ }
+
+ return xAcc;
+}
+
+uno::Reference< XAccessible > SwAccessibleMap::GetDocumentView( )
+{
+ return GetDocumentView_( false );
+}
+
+uno::Reference<XAccessible> SwAccessibleMap::GetDocumentPreview(
+ const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ // create & update preview data object
+ if( mpPreview == nullptr )
+ mpPreview.reset( new SwAccPreviewData() );
+ mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );
+
+ uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
+ return xAcc;
+}
+
+uno::Reference< XAccessible> SwAccessibleMap::GetContext( const SwFrame *pFrame,
+ bool bCreate )
+{
+ DBG_TESTSOLARMUTEX();
+ uno::Reference < XAccessible > xAcc;
+ uno::Reference < XAccessible > xOldCursorAcc;
+ bool bOldShapeSelected = false;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpFrameMap && bCreate )
+ mpFrameMap.reset(new SwAccessibleContextMap_Impl);
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pFrame );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() && bCreate )
+ {
+ rtl::Reference<SwAccessibleContext> pAcc;
+ switch( pFrame->GetType() )
+ {
+ case SwFrameType::Txt:
+ pAcc = new SwAccessibleParagraph(shared_from_this(),
+ static_cast< const SwTextFrame& >( *pFrame ) );
+ break;
+ case SwFrameType::Header:
+ pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
+ static_cast< const SwHeaderFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Footer:
+ pAcc = new SwAccessibleHeaderFooter(shared_from_this(),
+ static_cast< const SwFooterFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Ftn:
+ {
+ const SwFootnoteFrame *pFootnoteFrame =
+ static_cast < const SwFootnoteFrame * >( pFrame );
+ bool bIsEndnote =
+ SwAccessibleFootnote::IsEndnote( pFootnoteFrame );
+ pAcc = new SwAccessibleFootnote(shared_from_this(), bIsEndnote,
+ /*(bIsEndnote ? mnEndnote++ : mnFootnote++),*/
+ pFootnoteFrame );
+ }
+ break;
+ case SwFrameType::Fly:
+ {
+ const SwFlyFrame *pFlyFrame =
+ static_cast < const SwFlyFrame * >( pFrame );
+ switch( SwAccessibleFrameBase::GetNodeType( pFlyFrame ) )
+ {
+ case SwNodeType::Grf:
+ pAcc = new SwAccessibleGraphic(shared_from_this(), pFlyFrame );
+ break;
+ case SwNodeType::Ole:
+ pAcc = new SwAccessibleEmbeddedObject(shared_from_this(), pFlyFrame );
+ break;
+ default:
+ pAcc = new SwAccessibleTextFrame(shared_from_this(), *pFlyFrame );
+ break;
+ }
+ }
+ break;
+ case SwFrameType::Cell:
+ pAcc = new SwAccessibleCell(shared_from_this(),
+ static_cast< const SwCellFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Tab:
+ pAcc = new SwAccessibleTable(shared_from_this(),
+ static_cast< const SwTabFrame *>( pFrame ) );
+ break;
+ case SwFrameType::Page:
+ OSL_ENSURE( GetShell()->IsPreview(),
+ "accessible page frames only in PagePreview" );
+ pAcc = new SwAccessiblePage(shared_from_this(), pFrame);
+ break;
+ default: break;
+ }
+ xAcc = pAcc;
+ assert(xAcc.is());
+
+ if( aIter != mpFrameMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpFrameMap->emplace( pFrame, xAcc );
+ }
+
+ if( pAcc->HasCursor() &&
+ !AreInSameTable( mxCursorContext, pFrame ) )
+ {
+ // If the new context has the focus, and if we know
+ // another context that had the focus, then the focus
+ // just moves from the old context to the new one. We
+ // then have to send a focus event and a caret event for
+ // the old context. We have to do that now,
+ // because after we have left this method, anyone might
+ // call getStates for the new context and will get a
+ // focused state then. Sending the focus changes event
+ // after that seems to be strange. However, we cannot
+ // send a focus event for the new context now, because
+ // no one except us knows it. In any case, we remember
+ // the new context as the one that has the focus
+ // currently.
+
+ xOldCursorAcc = mxCursorContext;
+ mxCursorContext = xAcc;
+
+ bOldShapeSelected = mbShapeSelected;
+ mbShapeSelected = false;
+ }
+ }
+ }
+ }
+
+ // Invalidate focus for old object when map is not locked
+ if( xOldCursorAcc.is() )
+ InvalidateCursorPosition( xOldCursorAcc );
+ if( bOldShapeSelected )
+ InvalidateShapeSelection();
+
+ return xAcc;
+}
+
+::rtl::Reference < SwAccessibleContext > SwAccessibleMap::GetContextImpl(
+ const SwFrame *pFrame,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc( GetContext( pFrame, bCreate ) );
+
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast< SwAccessibleContext * >( xAcc.get() ) );
+
+ return xAccImpl;
+}
+
+uno::Reference< XAccessible> SwAccessibleMap::GetContext(
+ const SdrObject *pObj,
+ SwAccessibleContext *pParentImpl,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc;
+ uno::Reference < XAccessible > xOldCursorAcc;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpShapeMap && bCreate )
+ mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ xAcc = (*aIter).second;
+
+ if( !xAcc.is() && bCreate )
+ {
+ rtl::Reference< ::accessibility::AccessibleShape> pAcc;
+ uno::Reference < drawing::XShape > xShape(
+ const_cast< SdrObject * >( pObj )->getUnoShape(),
+ uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
+ ::accessibility::ShapeTypeHandler::Instance();
+ uno::Reference < XAccessible > xParent( pParentImpl );
+ ::accessibility::AccessibleShapeInfo aShapeInfo(
+ xShape, xParent, this );
+
+ pAcc = rShapeTypeHandler.CreateAccessibleObject(
+ aShapeInfo, mpShapeMap->GetInfo() );
+ }
+ xAcc = pAcc.get();
+ assert(xAcc.is());
+ pAcc->Init();
+ if( aIter != mpShapeMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpShapeMap->emplace( pObj, xAcc );
+ }
+ // TODO: focus!!!
+ AddGroupContext(pObj, xAcc);
+ }
+ }
+ }
+
+ // Invalidate focus for old object when map is not locked
+ if( xOldCursorAcc.is() )
+ InvalidateCursorPosition( xOldCursorAcc );
+
+ return xAcc;
+}
+
+bool SwAccessibleMap::IsInSameLevel(const SdrObject* pObj, const SwFEShell* pFESh)
+{
+ if (pFESh)
+ return pFESh->IsObjSameLevelWithMarked(pObj);
+ return false;
+}
+
+void SwAccessibleMap::AddShapeContext(const SdrObject *pObj, uno::Reference < XAccessible > const & xAccShape)
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpShapeMap )
+ {
+ mpShapeMap->emplace( pObj, xAccShape );
+ }
+
+}
+
+//Added by yanjun for sym2_6407
+void SwAccessibleMap::RemoveGroupContext(const SdrObject *pParentObj)
+{
+ osl::MutexGuard aGuard( maMutex );
+ // TODO: Why are sub-shapes of group shapes even added to our map?
+ // Doesn't the AccessibleShape of the top-level shape create them
+ // on demand anyway? Why does SwAccessibleMap need to know them?
+ // We cannot rely on getAccessibleChild here to remove the sub-shapes
+ // from mpShapes because the top-level shape may not only be disposed here
+ // but also by visibility checks in svx, then it doesn't return children.
+ if (mpShapeMap && pParentObj && pParentObj->IsGroupObject())
+ {
+ SdrObjList *const pChildren(pParentObj->GetSubList());
+ for (size_t i = 0; pChildren && i < pChildren->GetObjCount(); ++i)
+ {
+ SdrObject *const pChild(pChildren->GetObj(i));
+ assert(pChild);
+ RemoveContext(pChild);
+ }
+ }
+}
+//End
+
+void SwAccessibleMap::AddGroupContext(const SdrObject *pParentObj, uno::Reference < XAccessible > const & xAccParent)
+{
+ osl::MutexGuard aGuard( maMutex );
+ if( !mpShapeMap )
+ return;
+
+ //here get all the sub list.
+ if (!pParentObj->IsGroupObject())
+ return;
+
+ if (!xAccParent.is())
+ return;
+
+ uno::Reference < XAccessibleContext > xContext = xAccParent->getAccessibleContext();
+ if (!xContext.is())
+ return;
+
+ sal_Int32 nChildren = xContext->getAccessibleChildCount();
+ for(sal_Int32 i = 0; i<nChildren; i++)
+ {
+ uno::Reference < XAccessible > xChild = xContext->getAccessibleChild(i);
+ if (xChild.is())
+ {
+ uno::Reference < XAccessibleContext > xChildContext = xChild->getAccessibleContext();
+ if (xChildContext.is())
+ {
+ short nRole = xChildContext->getAccessibleRole();
+ if (nRole == AccessibleRole::SHAPE)
+ {
+ ::accessibility::AccessibleShape* pAccShape = static_cast < ::accessibility::AccessibleShape* >( xChild.get());
+ uno::Reference < drawing::XShape > xShape = pAccShape->GetXShape();
+ if (xShape.is())
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
+ AddShapeContext(pObj, xChild);
+ AddGroupContext(pObj,xChild);
+ }
+ }
+ }
+ }
+ }
+}
+
+::rtl::Reference < ::accessibility::AccessibleShape > SwAccessibleMap::GetContextImpl(
+ const SdrObject *pObj,
+ SwAccessibleContext *pParentImpl,
+ bool bCreate )
+{
+ uno::Reference < XAccessible > xAcc( GetContext( pObj, pParentImpl, bCreate ) );
+
+ ::rtl::Reference < ::accessibility::AccessibleShape > xAccImpl(
+ static_cast< ::accessibility::AccessibleShape* >( xAcc.get() ) );
+
+ return xAccImpl;
+}
+
+void SwAccessibleMap::RemoveContext( const SwFrame *pFrame )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpFrameMap )
+ return;
+
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pFrame );
+ if( aIter == mpFrameMap->end() )
+ return;
+
+ mpFrameMap->erase( aIter );
+
+ // Remove reference to old caret object. Though mxCursorContext
+ // is a weak reference and cleared automatically, clearing it
+ // directly makes sure to not keep a non-functional object.
+ uno::Reference < XAccessible > xOldAcc( mxCursorContext );
+ if( xOldAcc.is() )
+ {
+ SwAccessibleContext *pOldAccImpl =
+ static_cast< SwAccessibleContext *>( xOldAcc.get() );
+ OSL_ENSURE( pOldAccImpl->GetFrame(), "old caret context is disposed" );
+ if( pOldAccImpl->GetFrame() == pFrame )
+ {
+ xOldAcc.clear(); // get an empty ref
+ mxCursorContext = xOldAcc;
+ }
+ }
+
+ if( mpFrameMap->empty() )
+ {
+ mpFrameMap.reset();
+ }
+}
+
+void SwAccessibleMap::RemoveContext( const SdrObject *pObj )
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpShapeMap )
+ return;
+
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter == mpShapeMap->end() )
+ return;
+
+ uno::Reference < XAccessible > xTempHold( (*aIter).second );
+ mpShapeMap->erase( aIter );
+ RemoveGroupContext(pObj);
+ // The shape selection flag is not cleared, but one might do
+ // so but has to make sure that the removed context is the one
+ // that is selected.
+
+ if( mpShapeMap && mpShapeMap->empty() )
+ {
+ mpShapeMap.reset();
+ }
+}
+
+bool SwAccessibleMap::Contains(const SwFrame *pFrame) const
+{
+ return (pFrame && mpFrameMap && mpFrameMap->find(pFrame) != mpFrameMap->end());
+}
+
+void SwAccessibleMap::A11yDispose( const SwFrame *pFrame,
+ const SdrObject *pObj,
+ vcl::Window* pWindow,
+ bool bRecursive,
+ bool bCanSkipInvisible )
+{
+ SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
+
+ // Indeed, the following assert checks the frame's accessible flag,
+ // because that's the one that is evaluated in the layout. The frame
+ // might not be accessible anyway. That's the case for cell frames that
+ // contain further cells.
+ OSL_ENSURE( !aFrameOrObj.GetSwFrame() || aFrameOrObj.GetSwFrame()->IsAccessibleFrame(),
+ "non accessible frame should be disposed" );
+
+ if (!(aFrameOrObj.IsAccessible(GetShell()->IsPreview())
+ // fdo#87199 dispose the darn thing if it ever was accessible
+ || Contains(pFrame)))
+ return;
+
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
+ ::rtl::Reference< ::accessibility::AccessibleShape > xShapeAccImpl;
+ // get accessible context for frame
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ // First of all look for an accessible context for a frame
+ if( aFrameOrObj.GetSwFrame() && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ if( !xAccImpl.is() && mpFrameMap )
+ {
+ // If there is none, look if the parent is accessible.
+ const SwFrame *pParent =
+ SwAccessibleFrame::GetParent( aFrameOrObj,
+ GetShell()->IsPreview());
+
+ if( pParent )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pParent );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xParentAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ }
+ if( !xParentAccImpl.is() && !aFrameOrObj.GetSwFrame() && mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::iterator aIter =
+ mpShapeMap->find( aFrameOrObj.GetDrawObject() );
+ if( aIter != mpShapeMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xShapeAccImpl =
+ static_cast< ::accessibility::AccessibleShape *>( xAcc.get() );
+ }
+ }
+ if( pObj && GetShell()->ActionPend() &&
+ (xParentAccImpl.is() || xShapeAccImpl.is()) )
+ {
+ // Keep a reference to the XShape to avoid that it
+ // is deleted with a SwFrameFormat::SwClientNotify.
+ uno::Reference < drawing::XShape > xShape(
+ const_cast< SdrObject * >( pObj )->getUnoShape(),
+ uno::UNO_QUERY );
+ if( xShape.is() )
+ {
+ mvShapes.push_back( xShape );
+ }
+ }
+ }
+
+ // remove events stored for the frame
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ if( mpEvents )
+ {
+ SwAccessibleEventMap_Impl::iterator aIter =
+ mpEventMap->find( aFrameOrObj );
+ if( aIter != mpEventMap->end() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::DISPOSE, aFrameOrObj );
+ AppendEvent( aEvent );
+ }
+ }
+ }
+
+ // If the frame is accessible and there is a context for it, dispose
+ // the frame. If the frame is no context for it but disposing should
+ // take place recursive, the frame's children have to be disposed
+ // anyway, so we have to create the context then.
+ if( xAccImpl.is() )
+ {
+ xAccImpl->Dispose( bRecursive );
+ }
+ else if( xParentAccImpl.is() )
+ {
+ // If the frame is a cell frame, the table must be notified.
+ // If we are in an action, a table model change event will
+ // be broadcasted at the end of the action to give the table
+ // a chance to generate a single table change event.
+
+ xParentAccImpl->DisposeChild( aFrameOrObj, bRecursive, bCanSkipInvisible );
+ }
+ else if( xShapeAccImpl.is() )
+ {
+ RemoveContext( aFrameOrObj.GetDrawObject() );
+ xShapeAccImpl->dispose();
+ }
+
+ if( mpPreview && pFrame && pFrame->IsPageFrame() )
+ mpPreview->DisposePage( static_cast< const SwPageFrame *>( pFrame ) );
+}
+
+void SwAccessibleMap::InvalidatePosOrSize( const SwFrame *pFrame,
+ const SdrObject *pObj,
+ vcl::Window* pWindow,
+ const SwRect& rOldBox )
+{
+ SwAccessibleChild aFrameOrObj( pFrame, pObj, pWindow );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ ::rtl::Reference< SwAccessibleContext > xAccImpl;
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl;
+ const SwFrame *pParent =nullptr;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ if( aFrameOrObj.GetSwFrame() )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ // If there is an accessible object already it is
+ // notified directly.
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ if( !xAccImpl.is() )
+ {
+ // Otherwise we look if the parent is accessible.
+ // If not, there is nothing to do.
+ pParent =
+ SwAccessibleFrame::GetParent( aFrameOrObj,
+ GetShell()->IsPreview());
+
+ if( pParent )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pParent );
+ if( aIter != mpFrameMap->end() )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ xParentAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ }
+ }
+ }
+ }
+ }
+
+ if( xAccImpl.is() )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::POS_CHANGED, xAccImpl.get(),
+ aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ if (xAccImpl->GetFrame()) // not if disposed by FireEvents()
+ {
+ xAccImpl->InvalidatePosOrSize(rOldBox);
+ }
+ }
+ }
+ else if( xParentAccImpl.is() )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ assert(pParent);
+ // tdf#99722 faster not to buffer events that won't be sent
+ if (!SwAccessibleChild(pParent).IsVisibleChildrenOnly()
+ || xParentAccImpl->IsShowing(rOldBox)
+ || xParentAccImpl->IsShowing(*this, aFrameOrObj))
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ xParentAccImpl.get(), aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ }
+ else
+ {
+ FireEvents();
+ xParentAccImpl->InvalidateChildPosOrSize( aFrameOrObj,
+ rOldBox );
+ }
+ }
+ else if(pParent)
+ {
+/*
+For child graphic and its parent paragraph,if split 2 graphic to 2 paragraph,
+will delete one graphic swfrm and new create 1 graphic swfrm ,
+then the new paragraph and the new graphic SwFrame will add .
+but when add graphic SwFrame ,the accessible of the new Paragraph is not created yet.
+so the new graphic accessible 'parent is NULL,
+so run here: save the parent's SwFrame not the accessible object parent,
+*/
+ bool bIsValidFrame = false;
+ bool bIsTextParent = false;
+ if (aFrameOrObj.GetSwFrame())
+ {
+ if (SwFrameType::Fly == pFrame->GetType())
+ {
+ bIsValidFrame =true;
+ }
+ }
+ else if(pObj)
+ {
+ if (SwFrameType::Txt == pParent->GetType())
+ {
+ bIsTextParent =true;
+ }
+ }
+ if( bIsValidFrame || bIsTextParent )
+ {
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CHILD_POS_CHANGED,
+ pParent, aFrameOrObj, rOldBox );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ OSL_ENSURE(false,"");
+ }
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateContent( const SwFrame *pFrame )
+{
+ SwAccessibleChild aFrameOrObj( pFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( !xAcc.is() )
+ return;
+
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::INVALID_CONTENT, pAccImpl,
+ aFrameOrObj );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateContent();
+ }
+}
+
+void SwAccessibleMap::InvalidateAttr( const SwTextFrame& rTextFrame )
+{
+ SwAccessibleChild aFrameOrObj( &rTextFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( !xAcc.is() )
+ return;
+
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::INVALID_ATTR,
+ pAccImpl, aFrameOrObj );
+ aEvent.SetStates( AccessibleStates::TEXT_ATTRIBUTE_CHANGED );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateAttr();
+ }
+}
+
+void SwAccessibleMap::InvalidateCursorPosition( const SwFrame *pFrame )
+{
+ SwAccessibleChild aFrameOrObj( pFrame );
+ bool bShapeSelected = false;
+ const SwViewShell *pVSh = GetShell();
+ if( auto pCSh = dynamic_cast<const SwCursorShell*>(pVSh) )
+ {
+ if( pCSh->IsTableMode() )
+ {
+ while( aFrameOrObj.GetSwFrame() && !aFrameOrObj.GetSwFrame()->IsCellFrame() )
+ aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
+ }
+ else if( auto pFESh = dynamic_cast<const SwFEShell*>(pVSh) )
+ {
+ const SwFrame *pFlyFrame = pFESh->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ OSL_ENSURE( !pFrame || pFrame->FindFlyFrame() == pFlyFrame,
+ "cursor is not contained in fly frame" );
+ aFrameOrObj = pFlyFrame;
+ }
+ else if( pFESh->IsObjSelected() > 0 )
+ {
+ bShapeSelected = true;
+ aFrameOrObj = static_cast<const SwFrame *>( nullptr );
+ }
+ }
+ }
+
+ OSL_ENSURE( bShapeSelected || aFrameOrObj.IsAccessible(GetShell()->IsPreview()),
+ "frame is not accessible" );
+
+ uno::Reference < XAccessible > xOldAcc;
+ uno::Reference < XAccessible > xAcc;
+ bool bOldShapeSelected = false;
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xOldAcc = mxCursorContext;
+ mxCursorContext = xAcc; // clear reference
+
+ bOldShapeSelected = mbShapeSelected;
+ mbShapeSelected = bShapeSelected;
+
+ if( aFrameOrObj.GetSwFrame() && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ else
+ {
+ SwRect rcEmpty;
+ const SwTabFrame* pTabFrame = aFrameOrObj.GetSwFrame()->FindTabFrame();
+ if (pTabFrame)
+ {
+ InvalidatePosOrSize(pTabFrame, nullptr, nullptr, rcEmpty);
+ }
+ else
+ {
+ InvalidatePosOrSize(aFrameOrObj.GetSwFrame(), nullptr, nullptr, rcEmpty);
+ }
+
+ aIter = mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+
+ // For cells, some extra thoughts are necessary,
+ // because invalidating the cursor for one cell
+ // invalidates the cursor for all cells of the same
+ // table. For this reason, we don't want to
+ // invalidate the cursor for the old cursor object
+ // and the new one if they are within the same table,
+ // because this would result in doing the work twice.
+ // Moreover, we have to make sure to invalidate the
+ // cursor even if the current cell has no accessible object.
+ // If the old cursor objects exists and is in the same
+ // table, it's the best choice, because using it avoids
+ // an unnecessary cursor invalidation cycle when creating
+ // a new object for the current cell.
+ if( aFrameOrObj.GetSwFrame()->IsCellFrame() )
+ {
+ if( xOldAcc.is() &&
+ AreInSameTable( xOldAcc, aFrameOrObj.GetSwFrame() ) )
+ {
+ if( xAcc.is() )
+ xOldAcc = xAcc; // avoid extra invalidation
+ else
+ xAcc = xOldAcc; // make sure at least one
+ }
+ if( !xAcc.is() )
+ xAcc = GetContext( aFrameOrObj.GetSwFrame() );
+ }
+ }
+ else if (bShapeSelected)
+ {
+ const SwFEShell *pFESh = static_cast< const SwFEShell * >( pVSh );
+ const SdrMarkList *pMarkList = pFESh->GetMarkList();
+ if (pMarkList != nullptr && pMarkList->GetMarkCount() == 1)
+ {
+ SdrObject *pObj = pMarkList->GetMark( 0 )->GetMarkedSdrObj();
+ ::rtl::Reference < ::accessibility::AccessibleShape > pAccShapeImpl = GetContextImpl(pObj,nullptr,false);
+ if (!pAccShapeImpl.is())
+ {
+ while (pObj && pObj->getParentSdrObjectFromSdrObject())
+ {
+ pObj = pObj->getParentSdrObjectFromSdrObject();
+ }
+ if (pObj != nullptr)
+ {
+ const SwFrame *pParent = SwAccessibleFrame::GetParent( SwAccessibleChild(pObj), GetShell()->IsPreview() );
+ if( pParent )
+ {
+ ::rtl::Reference< SwAccessibleContext > xParentAccImpl = GetContextImpl(pParent,false);
+ if (!xParentAccImpl.is())
+ {
+ const SwTabFrame* pTabFrame = pParent->FindTabFrame();
+ if (pTabFrame)
+ {
+ //The Table should not add in acc.because the "pParent" is not add to acc .
+ uno::Reference< XAccessible> xAccParentTab = GetContext(pTabFrame);//Should Create.
+
+ const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pTabFrame), GetShell()->IsPreview() );
+ if (pParentRoot)
+ {
+ ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
+ if(xParentAccImplRoot.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAccParentTab;
+ xParentAccImplRoot->FireAccessibleEvent( aEvent );
+ }
+ }
+
+ //Get "pParent" acc again.
+ xParentAccImpl = GetContextImpl(pParent,false);
+ }
+ else
+ {
+ //directly create this acc para .
+ xParentAccImpl = GetContextImpl(pParent);//Should Create.
+
+ const SwFrame *pParentRoot = SwAccessibleFrame::GetParent( SwAccessibleChild(pParent), GetShell()->IsPreview() );
+
+ ::rtl::Reference< SwAccessibleContext > xParentAccImplRoot = GetContextImpl(pParentRoot,false);
+ if(xParentAccImplRoot.is())
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= uno::Reference< XAccessible>(xParentAccImpl);
+ xParentAccImplRoot->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ if (xParentAccImpl.is())
+ {
+ uno::Reference< XAccessible> xAccShape =
+ GetContext(pObj,xParentAccImpl.get());
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CHILD;
+ aEvent.NewValue <<= xAccShape;
+ xParentAccImpl->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ m_setParaAdd.clear();
+ m_setParaRemove.clear();
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( bOldShapeSelected || bShapeSelected )
+ InvalidateShapeSelection();
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+
+ InvalidateShapeInParaSelection();
+
+ for (SwAccessibleParagraph* pAccPara : m_setParaRemove)
+ {
+ if(pAccPara && pAccPara->getSelectedAccessibleChildCount() == 0 && pAccPara->getSelectedText().getLength() == 0)
+ {
+ if(pAccPara->SetSelectedState(false))
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+ for (SwAccessibleParagraph* pAccPara : m_setParaAdd)
+ {
+ if(pAccPara && pAccPara->SetSelectedState(true))
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED;
+ pAccPara->FireAccessibleEvent( aEvent );
+ }
+ }
+}
+
+void SwAccessibleMap::InvalidateFocus()
+{
+ if(GetShell()->IsPreview())
+ {
+ uno::Reference<XAccessible> xAcc = GetDocumentView_( true );
+ if (xAcc)
+ {
+ SwAccessiblePreview *pAccPreview = static_cast<SwAccessiblePreview *>(xAcc.get());
+ if (pAccPreview)
+ {
+ pAccPreview->InvalidateFocus();
+ return ;
+ }
+ }
+ }
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xAcc = mxCursorContext;
+ }
+
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ pAccImpl->InvalidateFocus();
+ }
+ else
+ {
+ DoInvalidateShapeSelection(true);
+ }
+}
+
+void SwAccessibleMap::SetCursorContext(
+ const ::rtl::Reference < SwAccessibleContext >& rCursorContext )
+{
+ osl::MutexGuard aGuard( maMutex );
+ uno::Reference < XAccessible > xAcc( rCursorContext );
+ mxCursorContext = xAcc;
+}
+
+void SwAccessibleMap::InvalidateEditableStates( const SwFrame* _pFrame )
+{
+ // Start with the frame or the first upper that is accessible
+ SwAccessibleChild aFrameOrObj( _pFrame );
+ while( aFrameOrObj.GetSwFrame() &&
+ !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ aFrameOrObj = aFrameOrObj.GetSwFrame()->GetUpper();
+ if( !aFrameOrObj.GetSwFrame() )
+ aFrameOrObj = GetShell()->GetLayout();
+
+ uno::Reference< XAccessible > xAcc( GetContext( aFrameOrObj.GetSwFrame() ) );
+ SwAccessibleContext *pAccImpl = static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild(pAccImpl->GetFrame()),
+ AccessibleStates::EDITABLE );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateStates( AccessibleStates::EDITABLE );
+ }
+}
+
+void SwAccessibleMap::InvalidateRelationSet_( const SwFrame* pFrame,
+ bool bFrom )
+{
+ // first, see if this frame is accessible, and if so, get the respective
+ SwAccessibleChild aFrameOrObj( pFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+ }
+
+ // deliver event directly, or queue event
+ if( !xAcc.is() )
+ return;
+
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent( SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl, SwAccessibleChild(pFrame),
+ ( bFrom
+ ? AccessibleStates::RELATION_FROM
+ : AccessibleStates::RELATION_TO ) );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateRelation( bFrom
+ ? AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
+ : AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED );
+ }
+}
+
+void SwAccessibleMap::InvalidateRelationSet( const SwFrame* pMaster,
+ const SwFrame* pFollow )
+{
+ InvalidateRelationSet_( pMaster, false );
+ InvalidateRelationSet_( pFollow, true );
+}
+
+// invalidation of CONTENT_FLOW_FROM/_TO relation of a paragraph
+void SwAccessibleMap::InvalidateParaFlowRelation( const SwTextFrame& _rTextFrame,
+ const bool _bFrom )
+{
+ InvalidateRelationSet_( &_rTextFrame, _bFrom );
+}
+
+// invalidation of text selection of a paragraph
+void SwAccessibleMap::InvalidateParaTextSelection( const SwTextFrame& _rTextFrame )
+{
+ // first, see if this frame is accessible, and if so, get the respective
+ SwAccessibleChild aFrameOrObj( &_rTextFrame );
+ if( !aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ return;
+
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+ }
+
+ // deliver event directly, or queue event
+ if( !xAcc.is() )
+ return;
+
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+ if( GetShell()->ActionPend() )
+ {
+ SwAccessibleEvent_Impl aEvent(
+ SwAccessibleEvent_Impl::CARET_OR_STATES,
+ pAccImpl,
+ SwAccessibleChild( &_rTextFrame ),
+ AccessibleStates::TEXT_SELECTION_CHANGED );
+ AppendEvent( aEvent );
+ }
+ else
+ {
+ FireEvents();
+ pAccImpl->InvalidateTextSelection();
+ }
+}
+
+sal_Int32 SwAccessibleMap::GetChildIndex( const SwFrame& rParentFrame,
+ vcl::Window& rChild ) const
+{
+ sal_Int32 nIndex( -1 );
+
+ SwAccessibleChild aFrameOrObj( &rParentFrame );
+ if( aFrameOrObj.IsAccessible( GetShell()->IsPreview() ) )
+ {
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( aFrameOrObj.GetSwFrame() );
+ if( aIter != mpFrameMap->end() )
+ {
+ xAcc = (*aIter).second;
+ }
+ }
+ }
+
+ if( xAcc.is() )
+ {
+ SwAccessibleContext *pAccImpl =
+ static_cast< SwAccessibleContext *>( xAcc.get() );
+
+ nIndex = pAccImpl->GetChildIndex( const_cast<SwAccessibleMap&>(*this),
+ SwAccessibleChild( &rChild ) );
+ }
+ }
+
+ return nIndex;
+}
+
+void SwAccessibleMap::UpdatePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ assert(GetShell()->IsPreview() && "no preview?");
+ assert(mpPreview != nullptr && "no preview data?");
+
+ mpPreview->Update( *this, _rPreviewPages, _rScale, _pSelectedPageFrame, _rPreviewWinSize );
+
+ // propagate change of VisArea through the document's
+ // accessibility tree; this will also send appropriate scroll
+ // events
+ SwAccessibleContext* pDoc =
+ GetContextImpl( GetShell()->GetLayout() ).get();
+ static_cast<SwAccessibleDocumentBase*>( pDoc )->SetVisArea();
+
+ uno::Reference < XAccessible > xOldAcc;
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xOldAcc = mxCursorContext;
+
+ const SwPageFrame *pSelPage = mpPreview->GetSelPage();
+ if( pSelPage && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter =
+ mpFrameMap->find( pSelPage );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+}
+
+void SwAccessibleMap::InvalidatePreviewSelection( sal_uInt16 nSelPage )
+{
+ assert(GetShell()->IsPreview());
+ assert(mpPreview != nullptr);
+
+ mpPreview->InvalidateSelection( GetShell()->GetLayout()->GetPageByPageNum( nSelPage ) );
+
+ uno::Reference < XAccessible > xOldAcc;
+ uno::Reference < XAccessible > xAcc;
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ xOldAcc = mxCursorContext;
+
+ const SwPageFrame *pSelPage = mpPreview->GetSelPage();
+ if( pSelPage && mpFrameMap )
+ {
+ SwAccessibleContextMap_Impl::iterator aIter = mpFrameMap->find( pSelPage );
+ if( aIter != mpFrameMap->end() )
+ xAcc = (*aIter).second;
+ }
+ }
+
+ if( xOldAcc.is() && xOldAcc != xAcc )
+ InvalidateCursorPosition( xOldAcc );
+ if( xAcc.is() )
+ InvalidateCursorPosition( xAcc );
+}
+
+bool SwAccessibleMap::IsPageSelected( const SwPageFrame *pPageFrame ) const
+{
+ return mpPreview && mpPreview->GetSelPage() == pPageFrame;
+}
+
+void SwAccessibleMap::FireEvents()
+{
+ {
+ osl::MutexGuard aGuard( maEventMutex );
+ if( mpEvents )
+ {
+ if (mpEvents->IsFiring())
+ {
+ return; // prevent recursive FireEvents()
+ }
+
+ mpEvents->SetFiring();
+ mpEvents->MoveMissingXAccToEnd();
+ for( auto const& aEvent : *mpEvents )
+ FireEvent(aEvent);
+
+ mpEventMap.reset();
+ mpEvents.reset();
+ }
+ }
+ {
+ osl::MutexGuard aGuard( maMutex );
+ mvShapes.clear();
+ }
+
+}
+
+tools::Rectangle SwAccessibleMap::GetVisibleArea() const
+{
+ return o3tl::convert( GetVisArea().SVRect(), o3tl::Length::twip, o3tl::Length::mm100 );
+}
+
+// Convert a MM100 value relative to the document root into a pixel value
+// relative to the screen!
+Point SwAccessibleMap::LogicToPixel( const Point& rPoint ) const
+{
+ Point aPoint = o3tl::toTwips( rPoint, o3tl::Length::mm100 );
+ if (const vcl::Window* pWin = GetShell()->GetWin())
+ {
+ MapMode aMapMode;
+ GetMapMode( aPoint, aMapMode );
+ aPoint = pWin->LogicToPixel( aPoint, aMapMode );
+ aPoint = pWin->OutputToAbsoluteScreenPixel( aPoint );
+ }
+
+ return aPoint;
+}
+
+Size SwAccessibleMap::LogicToPixel( const Size& rSize ) const
+{
+ Size aSize( o3tl::toTwips( rSize, o3tl::Length::mm100 ) );
+ if (const OutputDevice* pWin = GetShell()->GetWin()->GetOutDev())
+ {
+ MapMode aMapMode;
+ GetMapMode( Point(0,0), aMapMode );
+ aSize = pWin->LogicToPixel( aSize, aMapMode );
+ }
+
+ return aSize;
+}
+
+bool SwAccessibleMap::ReplaceChild (
+ ::accessibility::AccessibleShape* pCurrentChild,
+ const uno::Reference< drawing::XShape >& _rxShape,
+ const tools::Long /*_nIndex*/,
+ const ::accessibility::AccessibleShapeTreeInfo& /*_rShapeTreeInfo*/
+ )
+{
+ const SdrObject *pObj = nullptr;
+ {
+ osl::MutexGuard aGuard( maMutex );
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+ while( aIter != aEndIter && !pObj )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ ::accessibility::AccessibleShape *pAccShape =
+ static_cast < ::accessibility::AccessibleShape* >( xAcc.get() );
+ if( pAccShape == pCurrentChild )
+ {
+ pObj = (*aIter).first;
+ }
+ ++aIter;
+ }
+ }
+ }
+ if( !pObj )
+ return false;
+
+ uno::Reference < drawing::XShape > xShape( _rxShape ); // keep reference to shape, because
+ // we might be the only one that
+ // holds it.
+ // Also get keep parent.
+ uno::Reference < XAccessible > xParent( pCurrentChild->getAccessibleParent() );
+ pCurrentChild = nullptr; // will be released by dispose
+ A11yDispose( nullptr, pObj, nullptr );
+
+ {
+ osl::MutexGuard aGuard( maMutex );
+
+ if( !mpShapeMap )
+ mpShapeMap.reset(new SwAccessibleShapeMap_Impl( this ));
+
+ // create the new child
+ ::accessibility::ShapeTypeHandler& rShapeTypeHandler =
+ ::accessibility::ShapeTypeHandler::Instance();
+ ::accessibility::AccessibleShapeInfo aShapeInfo(
+ xShape, xParent, this );
+ rtl::Reference< ::accessibility::AccessibleShape> pReplacement(
+ rShapeTypeHandler.CreateAccessibleObject (
+ aShapeInfo, mpShapeMap->GetInfo() ));
+
+ uno::Reference < XAccessible > xAcc( pReplacement );
+ if( xAcc.is() )
+ {
+ pReplacement->Init();
+
+ SwAccessibleShapeMap_Impl::iterator aIter = mpShapeMap->find( pObj );
+ if( aIter != mpShapeMap->end() )
+ {
+ (*aIter).second = xAcc;
+ }
+ else
+ {
+ mpShapeMap->emplace( pObj, xAcc );
+ }
+ }
+ }
+
+ SwRect aEmptyRect;
+ InvalidatePosOrSize( nullptr, pObj, nullptr, aEmptyRect );
+
+ return true;
+}
+
+//Get the accessible control shape from the model object, here model object is with XPropertySet type
+::accessibility::AccessibleControlShape * SwAccessibleMap::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ if( mpShapeMap )
+ {
+ SwAccessibleShapeMap_Impl::const_iterator aIter = mpShapeMap->cbegin();
+ SwAccessibleShapeMap_Impl::const_iterator aEndIter = mpShapeMap->cend();
+ while( aIter != aEndIter)
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).second );
+ ::accessibility::AccessibleShape *pAccShape =
+ static_cast < ::accessibility::AccessibleShape* >( xAcc.get() );
+ if(pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId (pAccShape->GetXShape()) == ::accessibility::DRAWING_CONTROL)
+ {
+ ::accessibility::AccessibleControlShape *pCtlAccShape = static_cast < ::accessibility::AccessibleControlShape* >(pAccShape);
+ if (pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ ++aIter;
+ }
+ }
+ return nullptr;
+}
+
+css::uno::Reference< XAccessible >
+ SwAccessibleMap::GetAccessibleCaption (const css::uno::Reference< css::drawing::XShape >&)
+{
+ return nullptr;
+}
+
+Point SwAccessibleMap::PixelToCore( const Point& rPoint ) const
+{
+ Point aPoint;
+ if (const OutputDevice* pWin = GetShell()->GetWin()->GetOutDev())
+ {
+ MapMode aMapMode;
+ GetMapMode( rPoint, aMapMode );
+ aPoint = pWin->PixelToLogic( rPoint, aMapMode );
+ }
+ return aPoint;
+}
+
+static tools::Long lcl_CorrectCoarseValue(tools::Long aCoarseValue, tools::Long aFineValue,
+ tools::Long aRefValue, bool bToLower)
+{
+ tools::Long aResult = aCoarseValue;
+
+ if (bToLower)
+ {
+ if (aFineValue < aRefValue)
+ aResult -= 1;
+ }
+ else
+ {
+ if (aFineValue > aRefValue)
+ aResult += 1;
+ }
+
+ return aResult;
+}
+
+static void lcl_CorrectRectangle(tools::Rectangle & rRect,
+ const tools::Rectangle & rSource,
+ const tools::Rectangle & rInGrid)
+{
+ rRect.SetLeft( lcl_CorrectCoarseValue(rRect.Left(), rSource.Left(),
+ rInGrid.Left(), false) );
+ rRect.SetTop( lcl_CorrectCoarseValue(rRect.Top(), rSource.Top(),
+ rInGrid.Top(), false) );
+ rRect.SetRight( lcl_CorrectCoarseValue(rRect.Right(), rSource.Right(),
+ rInGrid.Right(), true) );
+ rRect.SetBottom( lcl_CorrectCoarseValue(rRect.Bottom(), rSource.Bottom(),
+ rInGrid.Bottom(), true) );
+}
+
+tools::Rectangle SwAccessibleMap::CoreToPixel( const SwRect& rRect ) const
+{
+ tools::Rectangle aRect;
+ if (const OutputDevice* pWin = GetShell()->GetWin()->GetOutDev())
+ {
+ MapMode aMapMode;
+ GetMapMode( rRect.TopLeft(), aMapMode );
+ aRect = pWin->LogicToPixel( rRect.SVRect(), aMapMode );
+
+ tools::Rectangle aTmpRect = pWin->PixelToLogic( aRect, aMapMode );
+ lcl_CorrectRectangle(aRect, rRect.SVRect(), aTmpRect);
+ }
+
+ return aRect;
+}
+
+/** get mapping mode for LogicToPixel and PixelToLogic conversions
+
+ Method returns mapping mode of current output device and adjusts it,
+ if the shell is in page/print preview.
+ Necessary, because <PreviewAdjust(..)> changes mapping mode at current
+ output device for mapping logic document positions to page preview window
+ positions and vice versa and doesn't take care to recover its changes.
+*/
+void SwAccessibleMap::GetMapMode( const Point& _rPoint,
+ MapMode& _orMapMode ) const
+{
+ MapMode aMapMode = GetShell()->GetWin()->GetMapMode();
+ if( GetShell()->IsPreview() )
+ {
+ assert(mpPreview != nullptr);
+ mpPreview->AdjustMapMode( aMapMode, _rPoint );
+ }
+ _orMapMode = aMapMode;
+}
+
+Size SwAccessibleMap::GetPreviewPageSize(sal_uInt16 const nPreviewPageNum) const
+{
+ assert(mpVSh->IsPreview());
+ assert(mpPreview != nullptr);
+ return mpVSh->PagePreviewLayout()->GetPreviewPageSizeByPageNum(nPreviewPageNum);
+}
+
+/** method to build up a new data structure of the accessible paragraphs,
+ which have a selection
+ Important note: method has to be used inside a mutual exclusive section
+*/
+std::unique_ptr<SwAccessibleSelectedParas_Impl> SwAccessibleMap::BuildSelectedParas()
+{
+ // no accessible contexts, no selection
+ if ( !mpFrameMap )
+ {
+ return nullptr;
+ }
+
+ // get cursor as an instance of its base class <SwPaM>
+ SwPaM* pCursor( nullptr );
+ {
+ SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>(GetShell());
+ if ( pCursorShell )
+ {
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>(pCursorShell);
+ if ( !pFEShell ||
+ ( !pFEShell->IsFrameSelected() &&
+ pFEShell->IsObjSelected() == 0 ) )
+ {
+ // get cursor without updating an existing table cursor.
+ pCursor = pCursorShell->GetCursor( false );
+ }
+ }
+ }
+ // no cursor, no selection
+ if ( !pCursor )
+ {
+ return nullptr;
+ }
+
+ std::unique_ptr<SwAccessibleSelectedParas_Impl> pRetSelectedParas;
+
+ // loop on all cursors
+ SwPaM* pRingStart = pCursor;
+ do {
+
+ // for a selection the cursor has to have a mark.
+ // for safety reasons assure that point and mark are in text nodes
+ if ( pCursor->HasMark() &&
+ pCursor->GetPoint()->nNode.GetNode().IsTextNode() &&
+ pCursor->GetMark()->nNode.GetNode().IsTextNode() )
+ {
+ SwPosition* pStartPos = pCursor->Start();
+ SwPosition* pEndPos = pCursor->End();
+ // loop on all text nodes inside the selection
+ SwNodeIndex aIdx( pStartPos->nNode );
+ for ( ; aIdx.GetIndex() <= pEndPos->nNode.GetIndex(); ++aIdx )
+ {
+ SwTextNode* pTextNode( aIdx.GetNode().GetTextNode() );
+ if ( pTextNode )
+ {
+ // loop on all text frames registered at the text node.
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
+ for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame; pTextFrame = aIter.Next() )
+ {
+ uno::WeakReference < XAccessible > xWeakAcc;
+ SwAccessibleContextMap_Impl::iterator aMapIter =
+ mpFrameMap->find( pTextFrame );
+ if( aMapIter != mpFrameMap->end() )
+ {
+ xWeakAcc = (*aMapIter).second;
+ SwAccessibleParaSelection aDataEntry(
+ sw::FrameContainsNode(*pTextFrame, pStartPos->nNode.GetIndex())
+ ? pTextFrame->MapModelToViewPos(*pStartPos)
+ : TextFrameIndex(0),
+
+ sw::FrameContainsNode(*pTextFrame, pEndPos->nNode.GetIndex())
+ ? pTextFrame->MapModelToViewPos(*pEndPos)
+ : TextFrameIndex(COMPLETE_STRING));
+ if ( !pRetSelectedParas )
+ {
+ pRetSelectedParas.reset(
+ new SwAccessibleSelectedParas_Impl);
+ }
+ // sw_redlinehide: should be idempotent for multiple nodes in a merged para
+ pRetSelectedParas->emplace( xWeakAcc, aDataEntry );
+ }
+ }
+ }
+ }
+ }
+
+ // prepare next turn: get next cursor in ring
+ pCursor = pCursor->GetNext();
+ } while ( pCursor != pRingStart );
+
+ return pRetSelectedParas;
+}
+
+void SwAccessibleMap::InvalidateTextSelectionOfAllParas()
+{
+ osl::MutexGuard aGuard( maMutex );
+
+ // keep previously known selected paragraphs
+ std::unique_ptr<SwAccessibleSelectedParas_Impl> pPrevSelectedParas( std::move(mpSelectedParas) );
+
+ // determine currently selected paragraphs
+ mpSelectedParas = BuildSelectedParas();
+
+ // compare currently selected paragraphs with the previously selected
+ // paragraphs and submit corresponding TEXT_SELECTION_CHANGED events.
+ // first, search for new and changed selections.
+ // on the run remove selections from previously known ones, if they are
+ // also in the current ones.
+ if ( mpSelectedParas )
+ {
+ SwAccessibleSelectedParas_Impl::iterator aIter = mpSelectedParas->begin();
+ for ( ; aIter != mpSelectedParas->end(); ++aIter )
+ {
+ bool bSubmitEvent( false );
+ if ( !pPrevSelectedParas )
+ {
+ // new selection
+ bSubmitEvent = true;
+ }
+ else
+ {
+ SwAccessibleSelectedParas_Impl::iterator aPrevSelected =
+ pPrevSelectedParas->find( (*aIter).first );
+ if ( aPrevSelected != pPrevSelectedParas->end() )
+ {
+ // check, if selection has changed
+ if ( (*aIter).second.nStartOfSelection !=
+ (*aPrevSelected).second.nStartOfSelection ||
+ (*aIter).second.nEndOfSelection !=
+ (*aPrevSelected).second.nEndOfSelection )
+ {
+ // changed selection
+ bSubmitEvent = true;
+ }
+ pPrevSelectedParas->erase( aPrevSelected );
+ }
+ else
+ {
+ // new selection
+ bSubmitEvent = true;
+ }
+ }
+
+ if ( bSubmitEvent )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).first );
+ if ( xAcc.is() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xAcc.get() ) );
+ if ( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ const SwTextFrame* pTextFrame = xAccImpl->GetFrame()->DynCastTextFrame();
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
+ if ( pTextFrame )
+ {
+ InvalidateParaTextSelection( *pTextFrame );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // second, handle previous selections - after the first step the data
+ // structure of the previously known only contains the 'old' selections
+ if ( !pPrevSelectedParas )
+ return;
+
+ SwAccessibleSelectedParas_Impl::iterator aIter = pPrevSelectedParas->begin();
+ for ( ; aIter != pPrevSelectedParas->end(); ++aIter )
+ {
+ uno::Reference < XAccessible > xAcc( (*aIter).first );
+ if ( xAcc.is() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xAccImpl(
+ static_cast<SwAccessibleContext*>( xAcc.get() ) );
+ if ( xAccImpl.is() && xAccImpl->GetFrame() )
+ {
+ const SwTextFrame* pTextFrame = xAccImpl->GetFrame()->DynCastTextFrame();
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleMap::_SubmitTextSelectionChangedEvents()> - unexpected type of frame" );
+ if ( pTextFrame )
+ {
+ InvalidateParaTextSelection( *pTextFrame );
+ }
+ }
+ }
+ }
+}
+
+const SwRect& SwAccessibleMap::GetVisArea() const
+{
+ assert(!GetShell()->IsPreview() || (mpPreview != nullptr));
+
+ return GetShell()->IsPreview()
+ ? mpPreview->GetVisArea()
+ : GetShell()->VisArea();
+}
+
+bool SwAccessibleMap::IsDocumentSelAll()
+{
+ return GetShell()->GetDoc()->IsPrepareSelAll();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotextframe.cxx b/sw/source/core/access/accnotextframe.cxx
new file mode 100644
index 000000000..0f1454602
--- /dev/null
+++ b/sw/source/core/access/accnotextframe.cxx
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <frmfmt.hxx>
+#include <ndnotxt.hxx>
+#include <flyfrm.hxx>
+#include <notxtfrm.hxx>
+#include <hints.hxx>
+#include "accnotextframe.hxx"
+#include <fmturl.hxx>
+#include "accnotexthyperlink.hxx"
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using utl::AccessibleRelationSetHelper;
+
+const SwNoTextNode *SwAccessibleNoTextFrame::GetNoTextNode() const
+{
+ const SwNoTextNode *pNd = nullptr;
+ const SwFlyFrame *pFlyFrame = static_cast< const SwFlyFrame *>( GetFrame() );
+ if( pFlyFrame->Lower() && pFlyFrame->Lower()->IsNoTextFrame() )
+ {
+ const SwNoTextFrame *pContentFrame =
+ static_cast<const SwNoTextFrame *>(pFlyFrame->Lower());
+ const SwContentNode* pSwContentNode = pContentFrame->GetNode();
+ if(pSwContentNode != nullptr)
+ {
+ pNd = pSwContentNode->GetNoTextNode();
+ }
+ }
+
+ return pNd;
+}
+
+SwAccessibleNoTextFrame::SwAccessibleNoTextFrame(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame* pFlyFrame ) :
+ SwAccessibleFrameBase( pInitMap, nInitRole, pFlyFrame )
+{
+ const SwNoTextNode* pNd = GetNoTextNode();
+ // #i73249#
+ // consider new attributes Title and Description
+ if( pNd )
+ {
+ StartListening(const_cast<SwNoTextNode*>(pNd)->GetNotifier());
+ msTitle = pNd->GetTitle();
+
+ msDesc = pNd->GetDescription();
+ if ( msDesc.isEmpty() &&
+ msTitle != GetName() )
+ {
+ msDesc = msTitle;
+ }
+ }
+}
+
+SwAccessibleNoTextFrame::~SwAccessibleNoTextFrame()
+{
+}
+
+void SwAccessibleNoTextFrame::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ EndListeningAll();
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyModifyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyModifyHint->GetWhich();
+ if (nWhich != RES_TITLE_CHANGED && nWhich != RES_DESCRIPTION_CHANGED)
+ return;
+ const SwNoTextNode* pNd = GetNoTextNode();
+ switch(nWhich)
+ {
+ // #i73249#
+ case RES_TITLE_CHANGED:
+ {
+ OUString sOldTitle, sNewTitle;
+ const SwStringMsgPoolItem* pOldItem = dynamic_cast<const SwStringMsgPoolItem*>(pLegacyModifyHint->m_pOld);
+ if(pOldItem)
+ sOldTitle = pOldItem->GetString();
+ const SwStringMsgPoolItem* pNewItem = dynamic_cast<const SwStringMsgPoolItem*>(pLegacyModifyHint->m_pNew);
+ if(pNewItem)
+ sNewTitle = pNewItem->GetString();
+ if(sOldTitle == sNewTitle)
+ break;
+ msTitle = sNewTitle;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= sOldTitle;
+ aEvent.NewValue <<= msTitle;
+ FireAccessibleEvent(aEvent);
+
+ if(!pNd->GetDescription().isEmpty())
+ break;
+ [[fallthrough]];
+ }
+ case RES_DESCRIPTION_CHANGED:
+ {
+ if(pNd && GetFrame())
+ {
+ const OUString sOldDesc(msDesc);
+
+ const OUString& rDesc = pNd->GetDescription();
+ msDesc = rDesc;
+ if(msDesc.isEmpty() && msTitle != GetName())
+ msDesc = msTitle;
+
+ if(msDesc != sOldDesc)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= msDesc;
+ FireAccessibleEvent(aEvent);
+ }
+ }
+ }
+ }
+ }
+}
+
+void SwAccessibleNoTextFrame::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ SwAccessibleFrameBase::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+// #i73249#
+OUString SAL_CALL SwAccessibleNoTextFrame::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if ( !msTitle.isEmpty() )
+ {
+ return msTitle;
+ }
+
+ return SwAccessibleFrameBase::getAccessibleName();
+}
+
+OUString SAL_CALL SwAccessibleNoTextFrame::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return msDesc;
+}
+
+// XInterface
+
+uno::Any SAL_CALL SwAccessibleNoTextFrame::queryInterface( const uno::Type& aType )
+{
+ if( aType ==
+ ::cppu::UnoType<XAccessibleImage>::get() )
+ {
+ uno::Reference<XAccessibleImage> xImage = this;
+ return uno::Any(xImage);
+ }
+ else if ( aType == cppu::UnoType<XAccessibleHypertext>::get())
+ {
+ uno::Reference<XAccessibleHypertext> aAccHypertext = this;
+ return uno::Any( aAccHypertext );
+ }
+ else
+ return SwAccessibleContext::queryInterface( aType );
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleNoTextFrame::getTypes()
+{
+ return cppu::OTypeCollection(
+ ::cppu::UnoType<XAccessibleImage>::get(),
+ SwAccessibleFrameBase::getTypes() ).getTypes();
+}
+
+/// XAccessibleImage
+/** implementation of the XAccessibleImage methods is a no-brainer, as
+ all relevant information is already accessible through other
+ methods. So we just delegate to those. */
+
+OUString SAL_CALL SwAccessibleNoTextFrame::getAccessibleImageDescription()
+{
+ return getAccessibleDescription();
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getAccessibleImageHeight( )
+{
+ return getSize().Height;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getAccessibleImageWidth( )
+{
+ return getSize().Width;
+}
+
+// XAccessibleText
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getCaretPosition( ){return 0;}
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::setCaretPosition( sal_Int32 ){return false;}
+sal_Unicode SAL_CALL SwAccessibleNoTextFrame::getCharacter( sal_Int32 ){return 0;}
+css::uno::Sequence< css::beans::PropertyValue > SAL_CALL SwAccessibleNoTextFrame::getCharacterAttributes( sal_Int32 , const css::uno::Sequence< OUString >& )
+{
+ return uno::Sequence<beans::PropertyValue>();
+}
+css::awt::Rectangle SAL_CALL SwAccessibleNoTextFrame::getCharacterBounds( sal_Int32 )
+{
+ return css::awt::Rectangle(0, 0, 0, 0 );
+}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getCharacterCount( ){return 0;}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getIndexAtPoint( const css::awt::Point& ){return 0;}
+OUString SAL_CALL SwAccessibleNoTextFrame::getSelectedText( ){return OUString();}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getSelectionStart( ){return 0;}
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getSelectionEnd( ){return 0;}
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::setSelection( sal_Int32 , sal_Int32 ){return true;}
+OUString SAL_CALL SwAccessibleNoTextFrame::getText( ){return OUString();}
+OUString SAL_CALL SwAccessibleNoTextFrame::getTextRange( sal_Int32 , sal_Int32 ){return OUString();}
+css::accessibility::TextSegment SAL_CALL SwAccessibleNoTextFrame::getTextAtIndex( sal_Int32 , sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL SwAccessibleNoTextFrame::getTextBeforeIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL SwAccessibleNoTextFrame::getTextBehindIndex( sal_Int32 , sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::copyText( sal_Int32, sal_Int32 ){return true;}
+sal_Bool SAL_CALL SwAccessibleNoTextFrame::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ){return false;}
+
+// XAccessibleHyperText
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getHyperLinkCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = 0;
+ SwFormatURL aURL( static_cast<const SwLayoutFrame*>(GetFrame())->GetFormat()->GetURL() );
+
+ if(aURL.GetMap() || !aURL.GetURL().isEmpty())
+ nCount = 1;
+
+ return nCount;
+}
+
+uno::Reference< XAccessibleHyperlink > SAL_CALL
+ SwAccessibleNoTextFrame::getHyperLink( sal_Int32 nLinkIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwFormatURL aURL( static_cast<const SwLayoutFrame*>(GetFrame())->GetFormat()->GetURL() );
+
+ if( nLinkIndex > 0 )
+ throw lang::IndexOutOfBoundsException();
+
+ if( aURL.GetMap() || !aURL.GetURL().isEmpty() )
+ {
+ if ( !m_xHyperlink.is() )
+ {
+ m_xHyperlink = new SwAccessibleNoTextHyperlink( this, GetFrame() );
+ }
+
+ return m_xHyperlink;
+ }
+
+ return nullptr;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextFrame::getHyperLinkIndex( sal_Int32 )
+{
+ return 0;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL SwAccessibleNoTextFrame::getAccessibleRelationSet( )
+{
+ return new AccessibleRelationSetHelper();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotextframe.hxx b/sw/source/core/access/accnotextframe.hxx
new file mode 100644
index 000000000..13680ffa5
--- /dev/null
+++ b/sw/source/core/access/accnotextframe.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTFRAME_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTFRAME_HXX
+
+#include "accframebase.hxx"
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessibleImage.hpp>
+#include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
+
+class SwFlyFrame;
+class SwNoTextNode;
+
+class SwAccessibleNoTextFrame : public SwAccessibleFrameBase,
+ public css::accessibility::XAccessibleImage,
+ public css::accessibility::XAccessibleHypertext//Added by yangzhh for HyperLink
+{
+ friend class SwAccessibleNoTextHyperlink;
+ css::uno::Reference< css::accessibility::XAccessibleHyperlink > m_xHyperlink;
+ OUString msTitle;
+ OUString msDesc;
+
+protected:
+ virtual ~SwAccessibleNoTextFrame() override;
+
+ const SwNoTextNode *GetNoTextNode() const;
+
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleNoTextFrame( std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ sal_Int16 nInitRole,
+ const SwFlyFrame *pFlyFrame );
+
+ // XAccessibleContext
+
+ // #i73249# - Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleImage.
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XAccessibleImage
+ virtual OUString SAL_CALL
+ getAccessibleImageDescription( ) override;
+
+ virtual sal_Int32 SAL_CALL
+ getAccessibleImageHeight( ) override;
+
+ virtual sal_Int32 SAL_CALL
+ getAccessibleImageWidth( ) override;
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ virtual sal_Int32 SAL_CALL getCaretPosition( ) override;
+ virtual sal_Bool SAL_CALL setCaretPosition( sal_Int32 nIndex ) override;
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;//Shen Zhen Jie changed sal_Unicode to sal_uInt32
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getCharacterCount( ) override;
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& aPoint ) override;
+ virtual OUString SAL_CALL getSelectedText( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionStart( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionEnd( ) override;
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual OUString SAL_CALL getText( ) override;
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // XAccessibleHypertext
+ virtual sal_Int32 SAL_CALL getHyperLinkCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleHyperlink >
+ SAL_CALL getHyperLink( sal_Int32 nLinkIndex ) override;
+ virtual sal_Int32 SAL_CALL getHyperLinkIndex( sal_Int32 nCharIndex ) override;
+
+ SwAccessibleMap *GetAccessibleMap(){ return GetMap();}
+
+public:
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotexthyperlink.cxx b/sw/source/core/access/accnotexthyperlink.cxx
new file mode 100644
index 000000000..36ded7390
--- /dev/null
+++ b/sw/source/core/access/accnotexthyperlink.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <comphelper/accessiblekeybindinghelper.hxx>
+#include <swurl.hxx>
+#include <vcl/svapp.hxx>
+#include <frmfmt.hxx>
+
+#include "accnotexthyperlink.hxx"
+
+#include <fmturl.hxx>
+
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/keycodes.hxx>
+
+#include <accmap.hxx>
+
+using namespace css;
+using namespace css::lang;
+using namespace css::uno;
+using namespace css::accessibility;
+
+SwAccessibleNoTextHyperlink::SwAccessibleNoTextHyperlink( SwAccessibleNoTextFrame *p, const SwFrame *aFrame ) :
+ mxFrame( p ),
+ mpFrame( aFrame )
+{
+}
+
+// XAccessibleAction
+sal_Int32 SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionCount()
+{
+ SolarMutexGuard g;
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ return pMap->GetIMapObjectCount();
+ }
+ else if( !aURL.GetURL().isEmpty() )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+sal_Bool SAL_CALL SwAccessibleNoTextHyperlink::doAccessibleAction( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ bool bRet = false;
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetURL().isEmpty())
+ {
+ SwViewShell *pVSh = mxFrame->GetShell();
+ if( pVSh )
+ {
+ LoadURL( *pVSh, pMapObj->GetURL(), LoadUrlFlags::NONE,
+ pMapObj->GetTarget() );
+ bRet = true;
+ }
+ }
+ }
+ else if (!aURL.GetURL().isEmpty())
+ {
+ SwViewShell *pVSh = mxFrame->GetShell();
+ if( pVSh )
+ {
+ LoadURL( *pVSh, aURL.GetURL(), LoadUrlFlags::NONE,
+ aURL.GetTargetFrameName() );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+OUString SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionDescription(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ OUString sDesc;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetDesc().isEmpty())
+ sDesc = pMapObj->GetDesc();
+ else if (!pMapObj->GetURL().isEmpty())
+ sDesc = pMapObj->GetURL();
+ }
+ else if( !aURL.GetURL().isEmpty() )
+ sDesc = aURL.GetName();
+
+ return sDesc;
+}
+
+Reference< XAccessibleKeyBinding > SAL_CALL
+ SwAccessibleNoTextHyperlink::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ Reference< XAccessibleKeyBinding > xKeyBinding;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ bool bIsValid = false;
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetURL().isEmpty())
+ bIsValid = true;
+ }
+ else if (!aURL.GetURL().isEmpty())
+ bIsValid = true;
+
+ if(bIsValid)
+ {
+ rtl::Reference<::comphelper::OAccessibleKeyBindingHelper> pKeyBindingHelper =
+ new ::comphelper::OAccessibleKeyBindingHelper();
+ xKeyBinding = pKeyBindingHelper;
+
+ css::awt::KeyStroke aKeyStroke;
+ aKeyStroke.Modifiers = 0;
+ aKeyStroke.KeyCode = KEY_RETURN;
+ aKeyStroke.KeyChar = 0;
+ aKeyStroke.KeyFunc = 0;
+ pKeyBindingHelper->AddKeyBinding( aKeyStroke );
+ }
+
+ return xKeyBinding;
+}
+
+// XAccessibleHyperlink
+Any SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionAnchor(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ Any aRet;
+ //SwFrame* pAnchor = static_cast<SwFlyFrame*>(mpFrame)->GetAnchor();
+ Reference< XAccessible > xAnchor = mxFrame->GetAccessibleMap()->GetContext(mpFrame);
+ //SwAccessibleNoTextFrame* pFrame = xFrame.get();
+ //Reference< XAccessible > xAnchor = (XAccessible*)pFrame;
+ aRet <<= xAnchor;
+ return aRet;
+}
+
+Any SAL_CALL SwAccessibleNoTextHyperlink::getAccessibleActionObject(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ if(nIndex < 0 || nIndex >= getAccessibleActionCount())
+ throw lang::IndexOutOfBoundsException();
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ OUString retText;
+ ImageMap* pMap = aURL.GetMap();
+ if( pMap != nullptr )
+ {
+ IMapObject* pMapObj = pMap->GetIMapObject(nIndex);
+ if (!pMapObj->GetURL().isEmpty())
+ retText = pMapObj->GetURL();
+ }
+ else if ( !aURL.GetURL().isEmpty() )
+ retText = aURL.GetURL();
+
+ Any aRet;
+ aRet <<= retText;
+ return aRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextHyperlink::getStartIndex()
+{
+ return 0;
+}
+
+sal_Int32 SAL_CALL SwAccessibleNoTextHyperlink::getEndIndex()
+{
+ return 0;
+}
+
+sal_Bool SAL_CALL SwAccessibleNoTextHyperlink::isValid( )
+{
+ SolarMutexGuard g;
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+
+ if( aURL.GetMap() || !aURL.GetURL().isEmpty() )
+ return true;
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accnotexthyperlink.hxx b/sw/source/core/access/accnotexthyperlink.hxx
new file mode 100644
index 000000000..b986abc87
--- /dev/null
+++ b/sw/source/core/access/accnotexthyperlink.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTHYPERLINK_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCNOTEXTHYPERLINK_HXX
+
+#include <com/sun/star/accessibility/XAccessibleHyperlink.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <layfrm.hxx>
+
+#include "accnotextframe.hxx"
+
+class SwAccessibleNoTextHyperlink :
+ public ::cppu::WeakImplHelper<
+ css::accessibility::XAccessibleHyperlink >
+{
+ friend class SwAccessibleNoTextFrame;
+
+ ::rtl::Reference< SwAccessibleNoTextFrame > mxFrame;
+ const SwFrame *mpFrame;
+
+ SwFrameFormat *GetFormat()
+ {
+ return const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(mpFrame))->GetFormat();
+ }
+public:
+
+ SwAccessibleNoTextHyperlink( SwAccessibleNoTextFrame *p, const SwFrame* aFrame );
+
+ // XAccessibleAction
+ virtual sal_Int32 SAL_CALL getAccessibleActionCount() override;
+ virtual sal_Bool SAL_CALL doAccessibleAction( sal_Int32 nIndex ) override;
+ virtual OUString SAL_CALL getAccessibleActionDescription(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL
+ getAccessibleActionKeyBinding( sal_Int32 nIndex ) override;
+
+ // XAccessibleHyperlink
+ virtual css::uno::Any SAL_CALL getAccessibleActionAnchor(
+ sal_Int32 nIndex ) override;
+ virtual css::uno::Any SAL_CALL getAccessibleActionObject(
+ sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getStartIndex() override;
+ virtual sal_Int32 SAL_CALL getEndIndex() override;
+ virtual sal_Bool SAL_CALL isValid( ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpage.cxx b/sw/source/core/access/accpage.cxx
new file mode 100644
index 000000000..a22b01970
--- /dev/null
+++ b/sw/source/core/access/accpage.cxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/window.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include "accpage.hxx"
+
+#include <strings.hrc>
+#include <pagefrm.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+using uno::Sequence;
+
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessiblePageView";
+
+bool SwAccessiblePage::IsSelected()
+{
+ return GetMap()->IsPageSelected( static_cast < const SwPageFrame * >( GetFrame() ) );
+}
+
+void SwAccessiblePage::GetStates(
+ ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // FOCUSABLE
+ rStateSet.AddState( AccessibleStateType::FOCUSABLE );
+
+ // FOCUSED
+ if( IsSelected() )
+ {
+ OSL_ENSURE( m_bIsSelected, "bSelected out of sync" );
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ }
+}
+
+void SwAccessiblePage::InvalidateCursorPos_()
+{
+ bool bNewSelected = IsSelected();
+ bool bOldSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOldSelected = m_bIsSelected;
+ m_bIsSelected = bNewSelected;
+ }
+
+ if( bNewSelected )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ if( bOldSelected != bNewSelected )
+ {
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, bNewSelected );
+ }
+}
+
+void SwAccessiblePage::InvalidateFocus_()
+{
+ vcl::Window *pWin = GetWindow();
+ if( !pWin )
+ return;
+
+ bool bSelected;
+
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bSelected = m_bIsSelected;
+ }
+ OSL_ENSURE( bSelected, "focus object should be selected" );
+
+ FireStateChangedEvent( AccessibleStateType::FOCUSED,
+ pWin->HasFocus() && bSelected );
+}
+
+SwAccessiblePage::SwAccessiblePage(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFrame* pFrame )
+ : SwAccessibleContext( pInitMap, AccessibleRole::PANEL, pFrame )
+ , m_bIsSelected( false )
+{
+ assert(pFrame != nullptr);
+ assert(pInitMap != nullptr);
+ assert(pFrame->IsPageFrame());
+
+ OUString sPage = OUString::number(
+ static_cast<const SwPageFrame*>( GetFrame() )->GetPhyPageNum() );
+ SetName( GetResource( STR_ACCESS_PAGE_NAME, &sPage ) );
+}
+
+SwAccessiblePage::~SwAccessiblePage()
+{
+}
+
+bool SwAccessiblePage::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_bIsSelected;
+}
+
+OUString SwAccessiblePage::getImplementationName( )
+{
+ return sImplementationName;
+}
+
+sal_Bool SwAccessiblePage::supportsService( const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence<OUString> SwAccessiblePage::getSupportedServiceNames( )
+{
+ return { "com.sun.star.text.AccessiblePageView", sAccessibleServiceName };
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessiblePage::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString SwAccessiblePage::getAccessibleDescription( )
+{
+ ThrowIfDisposed();
+
+ OUString sArg( GetFormattedPageNumber() );
+ return GetResource( STR_ACCESS_PAGE_DESC, &sArg );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpage.hxx b/sw/source/core/access/accpage.hxx
new file mode 100644
index 000000000..006eda746
--- /dev/null
+++ b/sw/source/core/access/accpage.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPAGE_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPAGE_HXX
+
+#include "acccontext.hxx"
+
+/**
+ * accessibility implementation for the page (SwPageFrame)
+ * The page is _only_ visible in the page preview. For the regular
+ * document view, it doesn't make sense to add this additional element
+ * into the hierarchy. For the page preview, however, the page is the
+ * important.
+ */
+class SwAccessiblePage : public SwAccessibleContext
+{
+ bool m_bIsSelected; // protected by base class mutex
+
+ bool IsSelected();
+
+ using SwAccessibleFrame::GetBounds;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets
+ // FOCUSABLE(1) and FOCUSED(+)
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet ) override;
+
+ virtual void InvalidateCursorPos_() override;
+ virtual void InvalidateFocus_() override;
+
+ virtual ~SwAccessiblePage() override;
+
+public:
+ // convenience constructor to avoid typecast;
+ // may only be called with SwPageFrame argument
+ SwAccessiblePage(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFrame* pFrame);
+
+ // XAccessibleContext methods that need to be overridden
+
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService (
+ const OUString& sServiceName) override;
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ virtual bool HasCursor() override; // required by map to remember that object
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpara.cxx b/sw/source/core/access/accpara.cxx
new file mode 100644
index 000000000..f3b0d9070
--- /dev/null
+++ b/sw/source/core/access/accpara.cxx
@@ -0,0 +1,3546 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <numeric>
+#include <txtfrm.hxx>
+#include <flyfrm.hxx>
+#include <mdiexp.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <unotextrange.hxx>
+#include <unocrsrhelper.hxx>
+#include <crstate.hxx>
+#include <accmap.hxx>
+#include <fesh.hxx>
+#include <viewopt.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <breakit.hxx>
+#include "accpara.hxx"
+#include "accportions.hxx"
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <unocrsr.hxx>
+#include <unoport.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include "acchyperlink.hxx"
+#include "acchypertextdata.hxx"
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <comphelper/accessibletexthelper.hxx>
+#include <algorithm>
+#include <docufld.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <modcfg.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <swmodule.hxx>
+#include <redline.hxx>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/awt/FontStrikeout.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <wrong.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <swatrset.hxx>
+#include <unosett.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <viewimp.hxx>
+#include "textmarkuphelper.hxx"
+#include "parachangetrackinginfo.hxx"
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <svx/colorwindow.hxx>
+#include <o3tl/string_view.hxx>
+#include <editeng/editids.hrc>
+
+#include <reffld.hxx>
+#include <flddat.hxx>
+#include "../../uibase/inc/fldmgr.hxx"
+#include <fldbas.hxx> // SwField
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::container;
+
+using beans::PropertyValue;
+using beans::XMultiPropertySet;
+using beans::UnknownPropertyException;
+using beans::PropertyState_DIRECT_VALUE;
+
+using std::max;
+using std::min;
+using std::sort;
+
+namespace com::sun::star::text {
+ class XText;
+}
+
+constexpr OUStringLiteral sServiceName = u"com.sun.star.text.AccessibleParagraphView";
+constexpr OUStringLiteral sImplementationName = u"com.sun.star.comp.Writer.SwAccessibleParagraphView";
+
+OUString const & SwAccessibleParagraph::GetString()
+{
+ return GetPortionData().GetAccessibleString();
+}
+
+OUString SwAccessibleParagraph::GetDescription()
+{
+ return OUString(); // provide empty description for paragraphs
+}
+
+sal_Int32 SwAccessibleParagraph::GetCaretPos()
+{
+ sal_Int32 nRet = -1;
+
+ // get the selection's point, and test whether it's in our node
+ // #i27301# - consider adjusted method signature
+ SwPaM* pCaret = GetCursor( false ); // caret is first PaM in PaM-ring
+
+ if( pCaret != nullptr )
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ assert(pTextFrame);
+
+ // check whether the point points into 'our' node
+ SwPosition* pPoint = pCaret->GetPoint();
+ if (sw::FrameContainsNode(*pTextFrame, pPoint->nNode.GetIndex()))
+ {
+ // same node? Then check whether it's also within 'our' part
+ // of the paragraph
+ const TextFrameIndex nIndex = pTextFrame->MapModelToViewPos(*pPoint);
+ if(!GetPortionData().IsValidCorePosition( nIndex ) ||
+ (GetPortionData().IsZeroCorePositionData()
+ && nIndex == TextFrameIndex(0)))
+ {
+ bool bFormat = pTextFrame->HasPara();
+ if(bFormat)
+ {
+ ClearPortionData();
+ UpdatePortionData();
+ }
+ }
+ if( GetPortionData().IsValidCorePosition( nIndex ) )
+ {
+ // Yes, it's us!
+ // consider that cursor/caret is in front of the list label
+ if ( pCaret->IsInFrontOfLabel() )
+ {
+ nRet = 0;
+ }
+ else
+ {
+ nRet = GetPortionData().GetAccessiblePosition( nIndex );
+ }
+
+ OSL_ENSURE( nRet >= 0, "invalid cursor?" );
+ OSL_ENSURE( nRet <= GetPortionData().GetAccessibleString().
+ getLength(), "invalid cursor?" );
+ }
+ // else: in this paragraph, but in different frame
+ }
+ // else: not in this paragraph
+ }
+ // else: no cursor -> no caret
+
+ return nRet;
+}
+
+// #i27301# - new parameter <_bForSelection>
+SwPaM* SwAccessibleParagraph::GetCursor( const bool _bForSelection )
+{
+ // get the cursor shell; if we don't have any, we don't have a
+ // cursor/selection either
+ SwPaM* pCursor = nullptr;
+ SwCursorShell* pCursorShell = SwAccessibleParagraph::GetCursorShell();
+ // #i27301# - if cursor is retrieved for selection, the cursors for
+ // a table selection has to be returned.
+ if ( pCursorShell != nullptr &&
+ ( _bForSelection || !pCursorShell->IsTableMode() ) )
+ {
+ SwFEShell *pFESh = dynamic_cast<SwFEShell*>(pCursorShell);
+ if( !pFESh ||
+ !(pFESh->IsFrameSelected() || pFESh->IsObjSelected() > 0) )
+ {
+ // get the selection, and test whether it affects our text node
+ pCursor = pCursorShell->GetCursor( false /* ??? */ );
+ }
+ }
+
+ return pCursor;
+}
+
+bool SwAccessibleParagraph::IsHeading() const
+{
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const SwTextNode *pTextNd = pFrame->GetTextNodeForParaProps();
+ return pTextNd->IsOutline();
+}
+
+void SwAccessibleParagraph::GetStates(
+ ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+
+ // MULTILINE
+ rStateSet.AddState( AccessibleStateType::MULTI_LINE );
+
+ if (GetCursorShell())
+ {
+ // MULTISELECTABLE
+ rStateSet.AddState(AccessibleStateType::MULTI_SELECTABLE);
+ // FOCUSABLE
+ rStateSet.AddState(AccessibleStateType::FOCUSABLE);
+ }
+
+ // FOCUSED (simulates node index of cursor)
+ SwPaM* pCaret = GetCursor( false ); // #i27301# - consider adjusted method signature
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ assert(pFrame);
+ if (pCaret != nullptr &&
+ sw::FrameContainsNode(*pFrame, pCaret->GetPoint()->nNode.GetIndex()) &&
+ m_nOldCaretPos != -1)
+ {
+ vcl::Window *pWin = GetWindow();
+ if( pWin && pWin->HasFocus() )
+ rStateSet.AddState( AccessibleStateType::FOCUSED );
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+}
+
+void SwAccessibleParagraph::InvalidateContent_( bool bVisibleDataFired )
+{
+ OUString sOldText( GetString() );
+
+ ClearPortionData();
+
+ const OUString& rText = GetString();
+
+ if( rText != sOldText )
+ {
+ // The text is changed
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TEXT_CHANGED;
+
+ // determine exact changes between sOldText and rText
+ (void)comphelper::OCommonAccessibleText::implInitTextChangedEvent(sOldText, rText,
+ aEvent.OldValue,
+ aEvent.NewValue);
+
+ FireAccessibleEvent( aEvent );
+ uno::Reference< XAccessible > xparent = getAccessibleParent();
+ uno::Reference< XAccessibleContext > xAccContext(xparent,uno::UNO_QUERY);
+ if (xAccContext.is() && xAccContext->getAccessibleRole() == AccessibleRole::TABLE_CELL)
+ {
+ SwAccessibleContext* pPara = static_cast< SwAccessibleContext* >(xparent.get());
+ if(pPara)
+ {
+ AccessibleEventObject aParaEvent;
+ aParaEvent.EventId = AccessibleEventId::VALUE_CHANGED;
+ pPara->FireAccessibleEvent(aParaEvent);
+ }
+ }
+ }
+ else if( !bVisibleDataFired )
+ {
+ FireVisibleDataEvent();
+ }
+
+ bool bNewIsHeading = IsHeading();
+ //Get the real heading level, Heading1 ~ Heading10
+ m_nHeadingLevel = GetRealHeadingLevel();
+ bool bOldIsHeading;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ bOldIsHeading = m_bIsHeading;
+ if( m_bIsHeading != bNewIsHeading )
+ m_bIsHeading = bNewIsHeading;
+ }
+
+ if( bNewIsHeading != bOldIsHeading )
+ {
+ // The role has changed
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::ROLE_CHANGED;
+
+ FireAccessibleEvent( aEvent );
+ }
+
+ if( rText == sOldText )
+ return;
+
+ OUString sNewDesc( GetDescription() );
+ OUString sOldDesc;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ sOldDesc = m_sDesc;
+ if( m_sDesc != sNewDesc )
+ m_sDesc = sNewDesc;
+ }
+
+ if( sNewDesc != sOldDesc )
+ {
+ // The text is changed
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= sNewDesc;
+
+ FireAccessibleEvent( aEvent );
+ }
+}
+
+void SwAccessibleParagraph::InvalidateCursorPos_()
+{
+ // The text is changed
+ sal_Int32 nNew = GetCaretPos();
+ sal_Int32 nOld;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ nOld = m_nOldCaretPos;
+ m_nOldCaretPos = nNew;
+ }
+ if( -1 != nNew )
+ {
+ // remember that object as the one that has the caret. This is
+ // necessary to notify that object if the cursor leaves it.
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ vcl::Window *pWin = GetWindow();
+ if( nOld == nNew )
+ return;
+
+ // The cursor's node position is simulated by the focus!
+ if( pWin && pWin->HasFocus() && -1 == nOld )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, true );
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::CARET_CHANGED;
+ aEvent.OldValue <<= nOld;
+ aEvent.NewValue <<= nNew;
+
+ FireAccessibleEvent( aEvent );
+
+ if( pWin && pWin->HasFocus() && -1 == nNew )
+ FireStateChangedEvent( AccessibleStateType::FOCUSED, false );
+ //To send TEXT_SELECTION_CHANGED event
+ sal_Int32 nStart=0;
+ sal_Int32 nEnd =0;
+ bool bCurSelection = GetSelection(nStart,nEnd);
+ if(m_bLastHasSelection || bCurSelection )
+ {
+ aEvent.EventId = AccessibleEventId::TEXT_SELECTION_CHANGED;
+ aEvent.OldValue.clear();
+ aEvent.NewValue.clear();
+ FireAccessibleEvent(aEvent);
+ }
+ m_bLastHasSelection =bCurSelection;
+
+}
+
+void SwAccessibleParagraph::InvalidateFocus_()
+{
+ vcl::Window *pWin = GetWindow();
+ if( pWin )
+ {
+ sal_Int32 nPos;
+ {
+ std::scoped_lock aGuard( m_Mutex );
+ nPos = m_nOldCaretPos;
+ }
+ OSL_ENSURE( nPos != -1, "focus object should be selected" );
+
+ FireStateChangedEvent( AccessibleStateType::FOCUSED,
+ pWin->HasFocus() && nPos != -1 );
+ }
+}
+
+SwAccessibleParagraph::SwAccessibleParagraph(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTextFrame& rTextFrame )
+ : SwAccessibleContext( pInitMap, AccessibleRole::PARAGRAPH, &rTextFrame )
+ , m_nOldCaretPos( -1 )
+ , m_bIsHeading( false )
+ //Get the real heading level, Heading1 ~ Heading10
+ , m_nHeadingLevel (-1)
+ , m_aSelectionHelper( *this )
+ , mpParaChangeTrackInfo( new SwParaChangeTrackingInfo( rTextFrame ) ) // #i108125#
+ , m_bLastHasSelection(false) //To add TEXT_SELECTION_CHANGED event
+{
+ StartListening(const_cast<SwTextFrame&>(rTextFrame));
+ m_bIsHeading = IsHeading();
+ //Get the real heading level, Heading1 ~ Heading10
+ m_nHeadingLevel = GetRealHeadingLevel();
+ SetName( OUString() ); // set an empty accessibility name for paragraphs
+}
+
+SwAccessibleParagraph::~SwAccessibleParagraph()
+{
+ SolarMutexGuard aGuard;
+
+ m_pPortionData.reset();
+ m_pHyperTextData.reset();
+ mpParaChangeTrackInfo.reset(); // #i108125#
+ EndListeningAll();
+}
+
+bool SwAccessibleParagraph::HasCursor()
+{
+ std::scoped_lock aGuard( m_Mutex );
+ return m_nOldCaretPos != -1;
+}
+
+void SwAccessibleParagraph::UpdatePortionData()
+{
+ // obtain the text frame
+ const SwTextFrame* pFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ OSL_ENSURE( pFrame != nullptr, "The text frame has vanished!" );
+ if (!pFrame)
+ ClearPortionData();
+ else
+ {
+ OSL_ENSURE( pFrame->IsTextFrame(), "The text frame has mutated!" );
+ // build new portion data
+ m_pPortionData.reset( new SwAccessiblePortionData(
+ pFrame, GetMap()->GetShell()->GetViewOptions()) );
+ pFrame->VisitPortions( *m_pPortionData );
+ }
+ OSL_ENSURE( m_pPortionData != nullptr, "UpdatePortionData() failed" );
+}
+
+void SwAccessibleParagraph::ClearPortionData()
+{
+ m_pPortionData.reset();
+ m_pHyperTextData.reset();
+}
+
+void SwAccessibleParagraph::ExecuteAtViewShell( sal_uInt16 nSlot )
+{
+ OSL_ENSURE( GetMap() != nullptr, "no map?" );
+ SwViewShell* pViewShell = GetMap()->GetShell();
+
+ OSL_ENSURE( pViewShell != nullptr, "View shell expected!" );
+ SfxViewShell* pSfxShell = pViewShell->GetSfxViewShell();
+
+ OSL_ENSURE( pSfxShell != nullptr, "SfxViewShell shell expected!" );
+ if( !pSfxShell )
+ return;
+
+ SfxViewFrame *pFrame = pSfxShell->GetViewFrame();
+ OSL_ENSURE( pFrame != nullptr, "View frame expected!" );
+ if( !pFrame )
+ return;
+
+ SfxDispatcher *pDispatcher = pFrame->GetDispatcher();
+ OSL_ENSURE( pDispatcher != nullptr, "Dispatcher expected!" );
+ if( !pDispatcher )
+ return;
+
+ pDispatcher->Execute( nSlot );
+}
+
+rtl::Reference<SwXTextPortion> SwAccessibleParagraph::CreateUnoPortion(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ OSL_ENSURE( (IsValidChar(nStartIndex, GetString().getLength()) &&
+ (nEndIndex == -1)) ||
+ IsValidRange(nStartIndex, nEndIndex, GetString().getLength()),
+ "please check parameters before calling this method" );
+
+ const TextFrameIndex nStart = GetPortionData().GetCoreViewPosition(nStartIndex);
+ const TextFrameIndex nEnd = (nEndIndex == -1)
+ ? (nStart + TextFrameIndex(1))
+ : GetPortionData().GetCoreViewPosition(nEndIndex);
+
+ // create UNO cursor
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwPosition aStartPos(pFrame->MapViewToModelPos(nStart));
+ auto pUnoCursor(const_cast<SwDoc&>(pFrame->GetDoc()).CreateUnoCursor(aStartPos));
+ pUnoCursor->SetMark();
+ *pUnoCursor->GetMark() = pFrame->MapViewToModelPos(nEnd);
+
+ // create a (dummy) text portion to be returned
+ uno::Reference<text::XText> aEmpty;
+ return new SwXTextPortion ( pUnoCursor.get(), aEmpty, PORTION_TEXT);
+}
+
+// range checking for parameter
+
+bool SwAccessibleParagraph::IsValidChar(
+ sal_Int32 nPos, sal_Int32 nLength)
+{
+ return (nPos >= 0) && (nPos < nLength);
+}
+
+bool SwAccessibleParagraph::IsValidPosition(
+ sal_Int32 nPos, sal_Int32 nLength)
+{
+ return (nPos >= 0) && (nPos <= nLength);
+}
+
+bool SwAccessibleParagraph::IsValidRange(
+ sal_Int32 nBegin, sal_Int32 nEnd, sal_Int32 nLength)
+{
+ return IsValidPosition(nBegin, nLength) && IsValidPosition(nEnd, nLength);
+}
+
+//the function is to check whether the position is in a redline range.
+const SwRangeRedline* SwAccessibleParagraph::GetRedlineAtIndex()
+{
+ const SwRangeRedline* pRedline = nullptr;
+ SwPaM* pCrSr = GetCursor( true );
+ if ( pCrSr )
+ {
+ SwPosition* pStart = pCrSr->Start();
+ pRedline = pStart->GetDoc().getIDocumentRedlineAccess().GetRedline(*pStart, nullptr);
+ }
+
+ return pRedline;
+}
+
+// text boundaries
+
+bool SwAccessibleParagraph::GetCharBoundary(
+ i18n::Boundary& rBound,
+ sal_Int32 nPos )
+{
+ if( GetPortionData().FillBoundaryIFDateField( rBound, nPos) )
+ return true;
+
+ rBound.startPos = nPos;
+ rBound.endPos = nPos+1;
+ return true;
+}
+
+bool SwAccessibleParagraph::GetWordBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ // now ask the Break-Iterator for the word
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ // get locale for this position
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const TextFrameIndex nCorePos = GetPortionData().GetCoreViewPosition(nPos);
+ lang::Locale aLocale = g_pBreakIt->GetLocale(pFrame->GetLangOfChar(nCorePos, 0, true));
+
+ // which type of word are we interested in?
+ // (DICTIONARY_WORD includes punctuation, ANY_WORD doesn't.)
+ const sal_Int16 nWordType = i18n::WordType::ANY_WORD;
+
+ // get word boundary, as the Break-Iterator sees fit.
+ rBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rText, nPos, aLocale, nWordType, true );
+
+ return true;
+}
+
+bool SwAccessibleParagraph::GetSentenceBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ const sal_Unicode* pStr = rText.getStr();
+ while( nPos < rText.getLength() && pStr[nPos] == u' ' )
+ nPos++;
+
+ GetPortionData().GetSentenceBoundary( rBound, nPos );
+ return true;
+}
+
+bool SwAccessibleParagraph::GetLineBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ if( rText.getLength() == nPos )
+ GetPortionData().GetLastLineBoundary( rBound );
+ else
+ GetPortionData().GetLineBoundary( rBound, nPos );
+ return true;
+}
+
+bool SwAccessibleParagraph::GetParagraphBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText )
+{
+ rBound.startPos = 0;
+ rBound.endPos = rText.getLength();
+ return true;
+}
+
+bool SwAccessibleParagraph::GetAttributeBoundary(
+ i18n::Boundary& rBound,
+ sal_Int32 nPos )
+{
+ GetPortionData().GetAttributeBoundary( rBound, nPos );
+ return true;
+}
+
+bool SwAccessibleParagraph::GetGlyphBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos )
+{
+ // ask the Break-Iterator for the glyph by moving one cell
+ // forward, and then one cell back
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ // get locale for this position
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const TextFrameIndex nCorePos = GetPortionData().GetCoreViewPosition(nPos);
+ lang::Locale aLocale = g_pBreakIt->GetLocale(pFrame->GetLangOfChar(nCorePos, 0, true));
+
+ // get word boundary, as the Break-Iterator sees fit.
+ const sal_Int16 nIterMode = i18n::CharacterIteratorMode::SKIPCELL;
+ sal_Int32 nDone = 0;
+ rBound.endPos = g_pBreakIt->GetBreakIter()->nextCharacters(
+ rText, nPos, aLocale, nIterMode, 1, nDone );
+ rBound.startPos = g_pBreakIt->GetBreakIter()->previousCharacters(
+ rText, rBound.endPos, aLocale, nIterMode, 1, nDone );
+ bool bRet = ((rBound.startPos <= nPos) && (nPos <= rBound.endPos));
+ OSL_ENSURE( rBound.startPos <= nPos, "start pos too high" );
+ OSL_ENSURE( rBound.endPos >= nPos, "end pos too low" );
+
+ return bRet;
+}
+
+bool SwAccessibleParagraph::GetTextBoundary(
+ i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos,
+ sal_Int16 nTextType )
+{
+ // error checking
+ if( !( AccessibleTextType::LINE == nTextType
+ ? IsValidPosition( nPos, rText.getLength() )
+ : IsValidChar( nPos, rText.getLength() ) ) )
+ throw lang::IndexOutOfBoundsException();
+
+ bool bRet;
+
+ switch( nTextType )
+ {
+ case AccessibleTextType::WORD:
+ bRet = GetWordBoundary(rBound, rText, nPos);
+ break;
+
+ case AccessibleTextType::SENTENCE:
+ bRet = GetSentenceBoundary( rBound, rText, nPos );
+ break;
+
+ case AccessibleTextType::PARAGRAPH:
+ bRet = GetParagraphBoundary( rBound, rText );
+ break;
+
+ case AccessibleTextType::CHARACTER:
+ bRet = GetCharBoundary( rBound, nPos );
+ break;
+
+ case AccessibleTextType::LINE:
+ //Solve the problem of returning wrong LINE and PARAGRAPH
+ if((nPos == rText.getLength()) && nPos > 0)
+ bRet = GetLineBoundary( rBound, rText, nPos - 1);
+ else
+ bRet = GetLineBoundary( rBound, rText, nPos );
+ break;
+
+ case AccessibleTextType::ATTRIBUTE_RUN:
+ bRet = GetAttributeBoundary( rBound, nPos );
+ break;
+
+ case AccessibleTextType::GLYPH:
+ bRet = GetGlyphBoundary( rBound, rText, nPos );
+ break;
+
+ default:
+ throw lang::IllegalArgumentException( );
+ }
+
+ return bRet;
+}
+
+OUString SAL_CALL SwAccessibleParagraph::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ std::scoped_lock aGuard2( m_Mutex );
+ if( m_sDesc.isEmpty() )
+ m_sDesc = GetDescription();
+
+ return m_sDesc;
+}
+
+lang::Locale SAL_CALL SwAccessibleParagraph::getLocale()
+{
+ SolarMutexGuard aGuard;
+
+ const SwTextFrame *pTextFrame = GetFrame()->DynCastTextFrame();
+ if( !pTextFrame )
+ {
+ throw uno::RuntimeException("no SwTextFrame", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ lang::Locale aLoc(g_pBreakIt->GetLocale(pTextFrame->GetLangOfChar(TextFrameIndex(0), 0, true)));
+
+ return aLoc;
+}
+
+// #i27138# - paragraphs are in relation CONTENT_FLOWS_FROM and/or CONTENT_FLOWS_TO
+uno::Reference<XAccessibleRelationSet> SAL_CALL SwAccessibleParagraph::getAccessibleRelationSet()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ rtl::Reference<utl::AccessibleRelationSetHelper> pHelper = new utl::AccessibleRelationSetHelper();
+
+ const SwTextFrame* pTextFrame = GetFrame()->DynCastTextFrame();
+ OSL_ENSURE( pTextFrame,
+ "<SwAccessibleParagraph::getAccessibleRelationSet()> - missing text frame");
+ if ( pTextFrame )
+ {
+ const SwContentFrame* pPrevContentFrame( pTextFrame->FindPrevCnt() );
+ if ( pPrevContentFrame )
+ {
+ uno::Sequence< uno::Reference<XInterface> > aSequence { GetMap()->GetContext( pPrevContentFrame ) };
+ AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_FROM,
+ aSequence );
+ pHelper->AddRelation( aAccRel );
+ }
+
+ const SwContentFrame* pNextContentFrame( pTextFrame->FindNextCnt( true ) );
+ if ( pNextContentFrame )
+ {
+ uno::Sequence< uno::Reference<XInterface> > aSequence { GetMap()->GetContext( pNextContentFrame ) };
+ AccessibleRelation aAccRel( AccessibleRelationType::CONTENT_FLOWS_TO,
+ aSequence );
+ pHelper->AddRelation( aAccRel );
+ }
+ }
+
+ return pHelper;
+}
+
+void SAL_CALL SwAccessibleParagraph::grabFocus()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // get cursor shell
+ SwCursorShell *pCursorSh = GetCursorShell();
+ SwPaM *pCursor = GetCursor( false ); // #i27301# - consider new method signature
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+
+ if (pCursorSh != nullptr &&
+ ( pCursor == nullptr ||
+ !sw::FrameContainsNode(*pTextFrame, pCursor->GetPoint()->nNode.GetIndex()) ||
+ !pTextFrame->IsInside(pTextFrame->MapModelToViewPos(*pCursor->GetPoint()))))
+ {
+ // create pam for selection
+ SwPosition const aStartPos(pTextFrame->MapViewToModelPos(pTextFrame->GetOffset()));
+ SwPaM aPaM( aStartPos );
+
+ // set PaM at cursor shell
+ Select( aPaM );
+
+ }
+
+ // ->#i13955#
+ vcl::Window * pWindow = GetWindow();
+
+ if (pWindow != nullptr)
+ pWindow->GrabFocus();
+ // <-#i13955#
+}
+
+// #i71385#
+static bool lcl_GetBackgroundColor( Color & rColor,
+ const SwFrame* pFrame,
+ SwCursorShell* pCursorSh )
+{
+ const SvxBrushItem* pBackgroundBrush = nullptr;
+ std::optional<Color> xSectionTOXColor;
+ SwRect aDummyRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( pFrame &&
+ pFrame->GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false ) )
+ {
+ if ( xSectionTOXColor )
+ {
+ rColor = *xSectionTOXColor;
+ return true;
+ }
+ else
+ {
+ rColor = pBackgroundBrush->GetColor();
+ return true;
+ }
+ }
+ else if ( pCursorSh )
+ {
+ rColor = pCursorSh->Imp()->GetRetoucheColor();
+ return true;
+ }
+
+ return false;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getForeground()
+{
+ SolarMutexGuard g;
+
+ Color aBackgroundCol;
+
+ if ( lcl_GetBackgroundColor( aBackgroundCol, GetFrame(), GetCursorShell() ) )
+ {
+ if ( aBackgroundCol.IsDark() )
+ {
+ return sal_Int32(COL_WHITE);
+ }
+ else
+ {
+ return sal_Int32(COL_BLACK);
+ }
+ }
+
+ return SwAccessibleContext::getForeground();
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getBackground()
+{
+ SolarMutexGuard g;
+
+ Color aBackgroundCol;
+
+ if ( lcl_GetBackgroundColor( aBackgroundCol, GetFrame(), GetCursorShell() ) )
+ {
+ return sal_Int32(aBackgroundCol);
+ }
+
+ return SwAccessibleContext::getBackground();
+}
+
+OUString SAL_CALL SwAccessibleParagraph::getImplementationName()
+{
+ return sImplementationName;
+}
+
+sal_Bool SAL_CALL SwAccessibleParagraph::supportsService(
+ const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleParagraph::getSupportedServiceNames()
+{
+ return { sServiceName, sAccessibleServiceName };
+}
+
+static uno::Sequence< OUString > const & getAttributeNames()
+{
+ static uno::Sequence< OUString > const aNames
+ {
+ // Add the font name to attribute list
+ // sorted list of strings
+ UNO_NAME_CHAR_BACK_COLOR,
+ UNO_NAME_CHAR_COLOR,
+ UNO_NAME_CHAR_CONTOURED,
+ UNO_NAME_CHAR_EMPHASIS,
+ UNO_NAME_CHAR_ESCAPEMENT,
+ UNO_NAME_CHAR_FONT_NAME,
+ UNO_NAME_CHAR_HEIGHT,
+ UNO_NAME_CHAR_POSTURE,
+ UNO_NAME_CHAR_SHADOWED,
+ UNO_NAME_CHAR_STRIKEOUT,
+ UNO_NAME_CHAR_UNDERLINE,
+ UNO_NAME_CHAR_UNDERLINE_COLOR,
+ UNO_NAME_CHAR_WEIGHT,
+ };
+ return aNames;
+}
+
+static uno::Sequence< OUString > const & getSupplementalAttributeNames()
+{
+ static uno::Sequence< OUString > const aNames
+ {
+ // sorted list of strings
+ UNO_NAME_NUMBERING_LEVEL,
+ UNO_NAME_NUMBERING_RULES,
+ UNO_NAME_PARA_ADJUST,
+ UNO_NAME_PARA_BOTTOM_MARGIN,
+ UNO_NAME_PARA_FIRST_LINE_INDENT,
+ UNO_NAME_PARA_LEFT_MARGIN,
+ UNO_NAME_PARA_LINE_SPACING,
+ UNO_NAME_PARA_RIGHT_MARGIN,
+ UNO_NAME_TABSTOPS,
+ };
+ return aNames;
+}
+
+// XInterface
+
+uno::Any SwAccessibleParagraph::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet;
+ if ( rType == cppu::UnoType<XAccessibleText>::get())
+ {
+ uno::Reference<XAccessibleText> aAccText = static_cast<XAccessibleText *>(*this); // resolve ambiguity
+ aRet <<= aAccText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleEditableText>::get())
+ {
+ uno::Reference<XAccessibleEditableText> aAccEditText = this;
+ aRet <<= aAccEditText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleSelection>::get())
+ {
+ uno::Reference<XAccessibleSelection> aAccSel = this;
+ aRet <<= aAccSel;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleHypertext>::get())
+ {
+ uno::Reference<XAccessibleHypertext> aAccHyp = this;
+ aRet <<= aAccHyp;
+ }
+ // #i63870#
+ // add interface com::sun:star:accessibility::XAccessibleTextAttributes
+ else if ( rType == cppu::UnoType<XAccessibleTextAttributes>::get())
+ {
+ uno::Reference<XAccessibleTextAttributes> aAccTextAttr = this;
+ aRet <<= aAccTextAttr;
+ }
+ // #i89175#
+ // add interface com::sun:star:accessibility::XAccessibleTextMarkup
+ else if ( rType == cppu::UnoType<XAccessibleTextMarkup>::get())
+ {
+ uno::Reference<XAccessibleTextMarkup> aAccTextMarkup = this;
+ aRet <<= aAccTextMarkup;
+ }
+ // add interface com::sun:star:accessibility::XAccessibleMultiLineText
+ else if ( rType == cppu::UnoType<XAccessibleMultiLineText>::get())
+ {
+ uno::Reference<XAccessibleMultiLineText> aAccMultiLineText = this;
+ aRet <<= aAccMultiLineText;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleTextSelection>::get())
+ {
+ uno::Reference< css::accessibility::XAccessibleTextSelection > aTextExtension = this;
+ aRet <<= aTextExtension;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleExtendedAttributes>::get())
+ {
+ uno::Reference<XAccessibleExtendedAttributes> xAttr = this;
+ aRet <<= xAttr;
+ }
+ else
+ {
+ aRet = SwAccessibleContext::queryInterface(rType);
+ }
+
+ return aRet;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleParagraph::getTypes()
+{
+ // #i63870# - add type accessibility::XAccessibleTextAttributes
+ // #i89175# - add type accessibility::XAccessibleTextMarkup and
+ return cppu::OTypeCollection(
+ cppu::UnoType<XAccessibleEditableText>::get(),
+ cppu::UnoType<XAccessibleTextAttributes>::get(),
+ ::cppu::UnoType<XAccessibleSelection>::get(),
+ cppu::UnoType<XAccessibleTextMarkup>::get(),
+ cppu::UnoType<XAccessibleMultiLineText>::get(),
+ cppu::UnoType<XAccessibleHypertext>::get(),
+ SwAccessibleContext::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleParagraph::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleText
+
+sal_Int32 SwAccessibleParagraph::getCaretPosition()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nRet = GetCaretPos();
+ {
+ std::scoped_lock aOldCaretPosGuard( m_Mutex );
+ OSL_ENSURE( nRet == m_nOldCaretPos, "caret pos out of sync" );
+ m_nOldCaretPos = nRet;
+ }
+ if( -1 != nRet )
+ {
+ ::rtl::Reference < SwAccessibleContext > xThis( this );
+ GetMap()->SetCursorContext( xThis );
+ }
+
+ return nRet;
+}
+
+sal_Bool SAL_CALL SwAccessibleParagraph::setCaretPosition( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ bool bRet = false;
+
+ // get cursor shell
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ // create pam for selection
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ TextFrameIndex const nFrameIndex(GetPortionData().GetCoreViewPosition(nIndex));
+ SwPosition aStartPos(pFrame->MapViewToModelPos(nFrameIndex));
+ SwPaM aPaM( aStartPos );
+
+ // set PaM at cursor shell
+ bRet = Select( aPaM );
+ }
+
+ return bRet;
+}
+
+sal_Unicode SwAccessibleParagraph::getCharacter( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ OUString sText( GetString() );
+
+ // return character (if valid)
+ if( !IsValidChar(nIndex, sText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ return sText[nIndex];
+}
+
+css::uno::Sequence< css::style::TabStop > SwAccessibleParagraph::GetCurrentTabStop( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ /* #i12332# The position after the string needs special treatment.
+ IsValidChar -> IsValidPosition
+ */
+ if( ! (IsValidPosition( nIndex, GetString().getLength() ) ) )
+ throw lang::IndexOutOfBoundsException();
+
+ /* #i12332# */
+ bool bBehindText = false;
+ if ( nIndex == GetString().getLength() )
+ bBehindText = true;
+
+ // get model position & prepare GetCharRect() arguments
+ SwCursorMoveState aMoveState;
+ aMoveState.m_bRealHeight = true;
+ aMoveState.m_bRealWidth = true;
+ SwSpecialPos aSpecialPos;
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+
+ /* #i12332# FillSpecialPos does not accept nIndex ==
+ GetString().getLength(). In that case nPos is set to the
+ length of the string in the core. This way GetCharRect
+ returns the rectangle for a cursor at the end of the
+ paragraph. */
+ const TextFrameIndex nPos = bBehindText
+ ? TextFrameIndex(pFrame->GetText().getLength())
+ : GetPortionData().FillSpecialPos(nIndex, aSpecialPos, aMoveState.m_pSpecialPos );
+
+ // call GetCharRect
+ SwRect aCoreRect;
+ SwPosition aPosition(pFrame->MapViewToModelPos(nPos));
+ GetFrame()->GetCharRect( aCoreRect, aPosition, &aMoveState );
+
+ // already get the caret position
+ css::uno::Sequence< css::style::TabStop > tabs;
+ const sal_Int32 nStrLen = pFrame->GetText().getLength();
+ if( nStrLen > 0 )
+ {
+ SwFrame* pTFrame = const_cast<SwFrame*>(GetFrame());
+ tabs = pTFrame->GetTabStopInfo(aCoreRect.Left());
+ }
+
+ if( tabs.hasElements() )
+ {
+ // translate core coordinates into accessibility coordinates
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ SwRect aTmpRect(0, 0, tabs[0].Position, 0);
+
+ tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aTmpRect ));
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ aScreenRect.Move( -aFramePixPos.X(), -aFramePixPos.Y() );
+
+ tabs.getArray()[0].Position = aScreenRect.GetWidth();
+ }
+
+ return tabs;
+}
+
+namespace {
+
+struct IndexCompare
+{
+ const PropertyValue* pValues;
+ explicit IndexCompare( const PropertyValue* pVals ) : pValues(pVals) {}
+ bool operator() ( sal_Int32 a, sal_Int32 b ) const
+ {
+ return (pValues[a].Name < pValues[b].Name);
+ }
+};
+
+}
+
+OUString SwAccessibleParagraph::GetFieldTypeNameAtIndex(sal_Int32 nIndex)
+{
+ OUString strTypeName;
+ SwFieldMgr aMgr;
+ SwTextField* pTextField = nullptr;
+ sal_Int32 nFieldIndex = GetPortionData().GetFieldIndex(nIndex);
+ if (nFieldIndex >= 0)
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ sw::MergedAttrIter iter(*pFrame);
+ while (SwTextAttr const*const pHt = iter.NextAttr())
+ {
+ if ((pHt->Which() == RES_TXTATR_FIELD
+ || pHt->Which() == RES_TXTATR_ANNOTATION
+ || pHt->Which() == RES_TXTATR_INPUTFIELD)
+ && (nFieldIndex-- == 0))
+ {
+ pTextField = const_cast<SwTextField*>(
+ static_txtattr_cast<SwTextField const*>(pHt));
+ break;
+ }
+ else if (pHt->Which() == RES_TXTATR_REFMARK
+ && (nFieldIndex-- == 0))
+ {
+ strTypeName = "set reference";
+ }
+ }
+ }
+ if (pTextField)
+ {
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ if (pField)
+ {
+ strTypeName = SwFieldType::GetTypeStr(pField->GetTypeId());
+ const SwFieldIds nWhich = pField->GetTyp()->Which();
+ OUString sEntry;
+ sal_uInt32 subType = 0;
+ switch (nWhich)
+ {
+ case SwFieldIds::DocStat:
+ subType = static_cast<const SwDocStatField*>(pField)->GetSubType();
+ break;
+ case SwFieldIds::GetRef:
+ {
+ switch( pField->GetSubType() )
+ {
+ case REF_BOOKMARK:
+ {
+ const SwGetRefField* pRefField = dynamic_cast<const SwGetRefField*>(pField);
+ if ( pRefField && pRefField->IsRefToHeadingCrossRefBookmark() )
+ sEntry = "Headings";
+ else if ( pRefField && pRefField->IsRefToNumItemCrossRefBookmark() )
+ sEntry = "Numbered Paragraphs";
+ else
+ sEntry = "Bookmarks";
+ }
+ break;
+ case REF_FOOTNOTE:
+ sEntry = "Footnotes";
+ break;
+ case REF_ENDNOTE:
+ sEntry = "Endnotes";
+ break;
+ case REF_SETREFATTR:
+ sEntry = "Insert Reference";
+ break;
+ case REF_SEQUENCEFLD:
+ sEntry = static_cast<const SwGetRefField*>(pField)->GetSetRefName();
+ break;
+ }
+ //Get format string
+ strTypeName = sEntry;
+ // <pField->GetFormat() >= 0> is always true as <pField->GetFormat()> is unsigned
+// if (pField->GetFormat() >= 0)
+ {
+ sEntry = aMgr.GetFormatStr( pField->GetTypeId(), pField->GetFormat() );
+ if (sEntry.getLength() > 0)
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ }
+ break;
+ case SwFieldIds::DateTime:
+ subType = static_cast<const SwDateTimeField*>(pField)->GetSubType();
+ break;
+ case SwFieldIds::JumpEdit:
+ {
+ const sal_uInt32 nFormat= pField->GetFormat();
+ const sal_uInt16 nSize = aMgr.GetFormatCount(pField->GetTypeId(), false);
+ if (nFormat < nSize)
+ {
+ sEntry = aMgr.GetFormatStr(pField->GetTypeId(), nFormat);
+ if (sEntry.getLength() > 0)
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ }
+ break;
+ case SwFieldIds::ExtUser:
+ subType = static_cast<const SwExtUserField*>(pField)->GetSubType();
+ break;
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::SetExp:
+ {
+ sEntry = pField->GetTyp()->GetName();
+ if (sEntry.getLength() > 0)
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ break;
+ case SwFieldIds::DocInfo:
+ subType = pField->GetSubType();
+ subType &= 0x00ff;
+ break;
+ case SwFieldIds::RefPageSet:
+ {
+ const SwRefPageSetField* pRPld = static_cast<const SwRefPageSetField*>(pField);
+ bool bOn = pRPld->IsOn();
+ strTypeName += "-";
+ if (bOn)
+ strTypeName += "on";
+ else
+ strTypeName += "off";
+ }
+ break;
+ case SwFieldIds::Author:
+ {
+ strTypeName += "-" + aMgr.GetFormatStr(pField->GetTypeId(), pField->GetFormat() & 0xff);
+ }
+ break;
+ default: break;
+ }
+ if (subType > 0 || nWhich == SwFieldIds::DocInfo || nWhich == SwFieldIds::ExtUser || nWhich == SwFieldIds::DocStat)
+ {
+ std::vector<OUString> aLst;
+ aMgr.GetSubTypes(pField->GetTypeId(), aLst);
+ if (subType < aLst.size())
+ sEntry = aLst[subType];
+ if (sEntry.getLength() > 0)
+ {
+ if (nWhich == SwFieldIds::DocInfo)
+ {
+ strTypeName = sEntry;
+ sal_uInt16 nSize = aMgr.GetFormatCount(pField->GetTypeId(), false);
+ const sal_uInt16 nExSub = pField->GetSubType() & 0xff00;
+ if (nSize > 0 && nExSub > 0)
+ {
+ //Get extra subtype string
+ strTypeName += "-";
+ sEntry = aMgr.GetFormatStr(pField->GetTypeId(), nExSub/0x0100-1);
+ strTypeName += sEntry;
+ }
+ }
+ else
+ {
+ strTypeName += "-" + sEntry;
+ }
+ }
+ }
+ }
+ }
+ return strTypeName;
+}
+
+// #i63870# - re-implement method on behalf of methods
+// <_getDefaultAttributesImpl(..)> and <_getRunAttributesImpl(..)>
+uno::Sequence<PropertyValue> SwAccessibleParagraph::getCharacterAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString >& aRequestedAttributes )
+{
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString& rText = GetString();
+
+ if (!IsValidPosition(nIndex, rText.getLength()))
+ throw lang::IndexOutOfBoundsException();
+
+ bool bSupplementalMode = false;
+ uno::Sequence< OUString > aNames = aRequestedAttributes;
+ if (!aNames.hasElements())
+ {
+ bSupplementalMode = true;
+ aNames = getAttributeNames();
+ }
+ // retrieve default character attributes
+ tAccParaPropValMap aDefAttrSeq;
+ _getDefaultAttributesImpl( aNames, aDefAttrSeq, true );
+
+ // retrieved run character attributes
+ tAccParaPropValMap aRunAttrSeq;
+ _getRunAttributesImpl( nIndex, aNames, aRunAttrSeq );
+
+ // merge default and run attributes
+ std::vector< PropertyValue > aValues( aDefAttrSeq.size() );
+ sal_Int32 i = 0;
+ for ( const auto& rDefEntry : aDefAttrSeq )
+ {
+ tAccParaPropValMap::const_iterator aRunIter =
+ aRunAttrSeq.find( rDefEntry.first );
+ if ( aRunIter != aRunAttrSeq.end() )
+ {
+ aValues[i] = aRunIter->second;
+ }
+ else
+ {
+ aValues[i] = rDefEntry.second;
+ }
+ ++i;
+ }
+ if( bSupplementalMode )
+ {
+ uno::Sequence< OUString > aSupplementalNames = aRequestedAttributes;
+ if (!aSupplementalNames.hasElements())
+ aSupplementalNames = getSupplementalAttributeNames();
+
+ tAccParaPropValMap aSupplementalAttrSeq;
+ _getSupplementalAttributesImpl( aSupplementalNames, aSupplementalAttrSeq );
+
+ aValues.resize( aValues.size() + aSupplementalAttrSeq.size() );
+
+ for ( const auto& rSupplementalEntry : aSupplementalAttrSeq )
+ {
+ aValues[i] = rSupplementalEntry.second;
+ ++i;
+ }
+
+ _correctValues( nIndex, aValues );
+
+ aValues.emplace_back();
+
+ OUString strTypeName = GetFieldTypeNameAtIndex(nIndex);
+ if (!strTypeName.isEmpty())
+ {
+ aValues.emplace_back();
+ PropertyValue& rValueFT = aValues.back();
+ rValueFT.Name = "FieldType";
+ rValueFT.Value <<= strTypeName.toAsciiLowerCase();
+ rValueFT.Handle = -1;
+ rValueFT.State = PropertyState_DIRECT_VALUE;
+ }
+
+ //sort property values
+ // build sorted index array
+ sal_Int32 nLength = aValues.size();
+ std::vector<sal_Int32> aIndices;
+ aIndices.reserve(nLength);
+ for (i = 0; i < nLength; ++i)
+ aIndices.push_back(i);
+ std::sort(aIndices.begin(), aIndices.end(), IndexCompare(aValues.data()));
+ // create sorted sequences according to index array
+ uno::Sequence<PropertyValue> aNewValues( nLength );
+ PropertyValue* pNewValues = aNewValues.getArray();
+ for (i = 0; i < nLength; ++i)
+ {
+ pNewValues[i] = aValues[aIndices[i]];
+ }
+ return aNewValues;
+ }
+
+ return comphelper::containerToSequence(aValues);
+}
+
+static void SetPutRecursive(SfxItemSet &targetSet, const SfxItemSet &sourceSet)
+{
+ const SfxItemSet *const pParentSet = sourceSet.GetParent();
+ if (pParentSet)
+ SetPutRecursive(targetSet, *pParentSet);
+ targetSet.Put(sourceSet);
+}
+
+// #i63870#
+void SwAccessibleParagraph::_getDefaultAttributesImpl(
+ const uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rDefAttrSeq,
+ const bool bOnlyCharAttrs )
+{
+ // retrieve default attributes
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const SwTextNode *const pTextNode(pFrame->GetTextNodeForParaProps());
+ std::optional<SfxItemSet> pSet;
+ if ( !bOnlyCharAttrs )
+ {
+ pSet.emplace( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()),
+ svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_PARATR_BEGIN, RES_PARATR_END - 1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END - 1> );
+ }
+ else
+ {
+ pSet.emplace( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()),
+ svl::Items<RES_CHRATR_BEGIN, RES_CHRATR_END - 1> );
+ }
+ // #i82637# - From the perspective of the a11y API the default character
+ // attributes are the character attributes, which are set at the paragraph style
+ // of the paragraph. The character attributes set at the automatic paragraph
+ // style of the paragraph are treated as run attributes.
+ // pTextNode->SwContentNode::GetAttr( *pSet );
+ // get default paragraph attributes, if needed, and merge these into <pSet>
+ if ( !bOnlyCharAttrs )
+ {
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END - 1>
+ aParaSet( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()) );
+ pTextNode->SwContentNode::GetAttr( aParaSet );
+ pSet->Put( aParaSet );
+ }
+ // get default character attributes and merge these into <pSet>
+ OSL_ENSURE( pTextNode->GetTextColl(),
+ "<SwAccessibleParagraph::_getDefaultAttributesImpl(..)> - missing paragraph style. Serious defect!" );
+ if ( pTextNode->GetTextColl() )
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1>
+ aCharSet( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()) );
+ SetPutRecursive( aCharSet, pTextNode->GetTextColl()->GetAttrSet() );
+ pSet->Put( aCharSet );
+ }
+
+ // build-up sequence containing the run attributes <rDefAttrSeq>
+ tAccParaPropValMap aDefAttrSeq;
+ {
+ const SfxItemPropertyMap& rPropMap =
+ aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR )->getPropertyMap();
+ for ( const auto pEntry : rPropMap.getPropertyEntries() )
+ {
+ const SfxPoolItem* pItem = pSet->GetItem( pEntry->nWID );
+ if ( pItem )
+ {
+ uno::Any aVal;
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+
+ PropertyValue rPropVal;
+ rPropVal.Name = pEntry->aName;
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+
+ aDefAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+
+ // #i72800#
+ // add property value entry for the paragraph style
+ if ( !bOnlyCharAttrs && pTextNode->GetTextColl() )
+ {
+ if ( aDefAttrSeq.find( UNO_NAME_PARA_STYLE_NAME ) == aDefAttrSeq.end() )
+ {
+ PropertyValue rPropVal;
+ rPropVal.Name = UNO_NAME_PARA_STYLE_NAME;
+ uno::Any aVal( uno::Any( pTextNode->GetTextColl()->GetName() ) );
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+
+ aDefAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+
+ // #i73371#
+ // resolve value text::WritingMode2::PAGE of property value entry WritingMode
+ if ( !bOnlyCharAttrs && GetFrame() )
+ {
+ tAccParaPropValMap::iterator aIter = aDefAttrSeq.find( UNO_NAME_WRITING_MODE );
+ if ( aIter != aDefAttrSeq.end() )
+ {
+ PropertyValue rPropVal( aIter->second );
+ sal_Int16 nVal = rPropVal.Value.get<sal_Int16>();
+ if ( nVal == text::WritingMode2::PAGE )
+ {
+ const SwFrame* pUpperFrame( GetFrame()->GetUpper() );
+ while ( pUpperFrame )
+ {
+ if ( pUpperFrame->GetType() &
+ ( SwFrameType::Page | SwFrameType::Fly | SwFrameType::Section | SwFrameType::Tab | SwFrameType::Cell ) )
+ {
+ if ( pUpperFrame->IsVertical() )
+ {
+ nVal = text::WritingMode2::TB_RL;
+ }
+ else if ( pUpperFrame->IsRightToLeft() )
+ {
+ nVal = text::WritingMode2::RL_TB;
+ }
+ else
+ {
+ nVal = text::WritingMode2::LR_TB;
+ }
+ rPropVal.Value <<= nVal;
+ aDefAttrSeq[rPropVal.Name] = rPropVal;
+ break;
+ }
+
+ if ( pUpperFrame->IsFlyFrame() )
+ {
+ pUpperFrame = static_cast<const SwFlyFrame*>(pUpperFrame)->GetAnchorFrame();
+ }
+ else
+ {
+ pUpperFrame = pUpperFrame->GetUpper();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !aRequestedAttributes.hasElements() )
+ {
+ rDefAttrSeq = aDefAttrSeq;
+ }
+ else
+ {
+ for( const OUString& rReqAttr : aRequestedAttributes )
+ {
+ tAccParaPropValMap::const_iterator const aIter = aDefAttrSeq.find( rReqAttr );
+ if ( aIter != aDefAttrSeq.end() )
+ {
+ rDefAttrSeq[ aIter->first ] = aIter->second;
+ }
+ }
+ }
+}
+
+uno::Sequence< PropertyValue > SwAccessibleParagraph::getDefaultAttributes(
+ const uno::Sequence< OUString >& aRequestedAttributes )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ tAccParaPropValMap aDefAttrSeq;
+ _getDefaultAttributesImpl( aRequestedAttributes, aDefAttrSeq );
+
+ // #i92233#
+ constexpr OUStringLiteral sMMToPixelRatio = u"MMToPixelRatio";
+ bool bProvideMMToPixelRatio( !aRequestedAttributes.hasElements() ||
+ (comphelper::findValue(aRequestedAttributes, sMMToPixelRatio) != -1) );
+
+ uno::Sequence< PropertyValue > aValues( aDefAttrSeq.size() +
+ ( bProvideMMToPixelRatio ? 1 : 0 ) );
+ auto pValues = aValues.getArray();
+ std::transform(aDefAttrSeq.begin(), aDefAttrSeq.end(), pValues,
+ [](const auto& rEntry) -> PropertyValue { return rEntry.second; });
+
+ // #i92233#
+ if ( bProvideMMToPixelRatio )
+ {
+ PropertyValue rPropVal;
+ rPropVal.Name = sMMToPixelRatio;
+ const Size a100thMMSize( 1000, 1000 );
+ const Size aPixelSize = GetMap()->LogicToPixel( a100thMMSize );
+ const float fRatio = (static_cast<float>(a100thMMSize.Width())/100)/aPixelSize.Width();
+ rPropVal.Value <<= fRatio;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+ pValues[ aValues.getLength() - 1 ] = rPropVal;
+ }
+
+ return aValues;
+}
+
+void SwAccessibleParagraph::_getRunAttributesImpl(
+ const sal_Int32 nIndex,
+ const uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rRunAttrSeq )
+{
+ // create PaM for character at position <nIndex>
+ std::optional<SwPaM> pPaM;
+ const TextFrameIndex nCorePos(GetPortionData().GetCoreViewPosition(nIndex));
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwPosition const aModelPos(pFrame->MapViewToModelPos(nCorePos));
+ SwTextNode *const pTextNode(aModelPos.nNode.GetNode().GetTextNode());
+ {
+ SwPosition const aEndPos(*pTextNode,
+ aModelPos.nContent.GetIndex() == pTextNode->Len()
+ ? pTextNode->Len() // ???
+ : aModelPos.nContent.GetIndex() + 1);
+ pPaM.emplace(aModelPos, aEndPos);
+ }
+
+ // retrieve character attributes for the created PaM <pPaM>
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END -1> aSet( pPaM->GetDoc().GetAttrPool() );
+ // #i82637#
+ // From the perspective of the a11y API the character attributes, which
+ // are set at the automatic paragraph style of the paragraph, are treated
+ // as run attributes.
+ // SwXTextCursor::GetCursorAttr( *pPaM, aSet, sal_True, sal_True );
+ // get character attributes from automatic paragraph style and merge these into <aSet>
+ {
+ if ( pTextNode->HasSwAttrSet() )
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END -1> aAutomaticParaStyleCharAttrs( pPaM->GetDoc().GetAttrPool());
+ aAutomaticParaStyleCharAttrs.Put( *(pTextNode->GetpSwAttrSet()), false );
+ aSet.Put( aAutomaticParaStyleCharAttrs );
+ }
+ }
+ // get character attributes at <pPaM> and merge these into <aSet>
+ {
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END -1> aCharAttrsAtPaM( pPaM->GetDoc().GetAttrPool() );
+ SwUnoCursorHelper::GetCursorAttr(*pPaM, aCharAttrsAtPaM, true);
+ aSet.Put( aCharAttrsAtPaM );
+ }
+
+ // build-up sequence containing the run attributes <rRunAttrSeq>
+ {
+ tAccParaPropValMap aRunAttrSeq;
+ {
+ tAccParaPropValMap aDefAttrSeq;
+ uno::Sequence< OUString > aDummy;
+ _getDefaultAttributesImpl( aDummy, aDefAttrSeq, true ); // #i82637#
+
+ const SfxItemPropertyMap& rPropMap =
+ aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_CURSOR )->getPropertyMap();
+ for ( const auto pEntry : rPropMap.getPropertyEntries() )
+ {
+ const SfxPoolItem* pItem( nullptr );
+ // #i82637# - Found character attributes, whose value equals the value of
+ // the corresponding default character attributes, are excluded.
+ if ( aSet.GetItemState( pEntry->nWID, true, &pItem ) == SfxItemState::SET )
+ {
+ uno::Any aVal;
+ pItem->QueryValue( aVal, pEntry->nMemberId );
+
+ PropertyValue rPropVal;
+ rPropVal.Name = pEntry->aName;
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = PropertyState_DIRECT_VALUE;
+
+ tAccParaPropValMap::const_iterator aDefIter =
+ aDefAttrSeq.find( rPropVal.Name );
+ if ( aDefIter == aDefAttrSeq.end() ||
+ rPropVal.Value != aDefIter->second.Value )
+ {
+ aRunAttrSeq[rPropVal.Name] = rPropVal;
+ }
+ }
+ }
+ }
+
+ if ( !aRequestedAttributes.hasElements() )
+ {
+ rRunAttrSeq = aRunAttrSeq;
+ }
+ else
+ {
+ for( const OUString& rReqAttr : aRequestedAttributes )
+ {
+ tAccParaPropValMap::iterator aIter = aRunAttrSeq.find( rReqAttr );
+ if ( aIter != aRunAttrSeq.end() )
+ {
+ rRunAttrSeq[ (*aIter).first ] = (*aIter).second;
+ }
+ }
+ }
+ }
+}
+
+uno::Sequence< PropertyValue > SwAccessibleParagraph::getRunAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString >& aRequestedAttributes )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ {
+ const OUString& rText = GetString();
+ if (!IsValidPosition(nIndex, rText.getLength()))
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+ }
+
+ tAccParaPropValMap aRunAttrSeq;
+ _getRunAttributesImpl( nIndex, aRequestedAttributes, aRunAttrSeq );
+
+ return comphelper::mapValuesToSequence( aRunAttrSeq );
+}
+
+void SwAccessibleParagraph::_getSupplementalAttributesImpl(
+ const uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rSupplementalAttrSeq )
+{
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ const SwTextNode *const pTextNode(pFrame->GetTextNodeForParaProps());
+ SfxItemSetFixed<
+ RES_PARATR_LINESPACING, RES_PARATR_ADJUST,
+ RES_PARATR_TABSTOP, RES_PARATR_TABSTOP,
+ RES_PARATR_NUMRULE, RES_PARATR_NUMRULE,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1,
+ RES_LR_SPACE, RES_UL_SPACE>
+ aSet( const_cast<SwAttrPool&>(pTextNode->GetDoc().GetAttrPool()) );
+
+ if ( pTextNode->HasBullet() || pTextNode->HasNumber() )
+ {
+ aSet.Put( pTextNode->GetAttr(RES_PARATR_LIST_LEVEL) );
+ }
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_UL_SPACE) );
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_LR_SPACE) );
+ aSet.Put( pTextNode->SwContentNode::GetAttr(RES_PARATR_ADJUST) );
+
+ tAccParaPropValMap aSupplementalAttrSeq;
+ {
+ const SfxItemPropertyMapEntry* pPropMap(
+ aSwMapProvider.GetPropertyMapEntries( PROPERTY_MAP_ACCESSIBILITY_TEXT_ATTRIBUTE ) );
+ while ( !pPropMap->aName.isEmpty() )
+ {
+ const SfxPoolItem* pItem = aSet.GetItem( pPropMap->nWID );
+ if ( pItem )
+ {
+ uno::Any aVal;
+ pItem->QueryValue( aVal, pPropMap->nMemberId );
+
+ PropertyValue rPropVal;
+ rPropVal.Name = pPropMap->aName;
+ rPropVal.Value = aVal;
+ rPropVal.Handle = -1;
+ rPropVal.State = beans::PropertyState_DEFAULT_VALUE;
+
+ aSupplementalAttrSeq[rPropVal.Name] = rPropVal;
+ }
+
+ ++pPropMap;
+ }
+ }
+
+ for( const OUString& rSupplementalAttr : aRequestedAttributes )
+ {
+ tAccParaPropValMap::const_iterator const aIter = aSupplementalAttrSeq.find( rSupplementalAttr );
+ if ( aIter != aSupplementalAttrSeq.end() )
+ {
+ rSupplementalAttrSeq[ aIter->first ] = aIter->second;
+ }
+ }
+}
+
+void SwAccessibleParagraph::_correctValues( const sal_Int32 nIndex,
+ std::vector< PropertyValue >& rValues)
+{
+ PropertyValue ChangeAttr, ChangeAttrColor;
+
+ const SwRangeRedline* pRedline = GetRedlineAtIndex();
+ if ( pRedline )
+ {
+
+ const SwModuleOptions *pOpt = SW_MOD()->GetModuleConfig();
+ AuthorCharAttr aChangeAttr;
+ if ( pOpt )
+ {
+ switch( pRedline->GetType())
+ {
+ case RedlineType::Insert:
+ aChangeAttr = pOpt->GetInsertAuthorAttr();
+ break;
+ case RedlineType::Delete:
+ aChangeAttr = pOpt->GetDeletedAuthorAttr();
+ break;
+ case RedlineType::Format:
+ aChangeAttr = pOpt->GetFormatAuthorAttr();
+ break;
+ default: break;
+ }
+ }
+ switch( aChangeAttr.m_nItemId )
+ {
+ case SID_ATTR_CHAR_WEIGHT:
+ ChangeAttr.Name = UNO_NAME_CHAR_WEIGHT;
+ ChangeAttr.Value <<= awt::FontWeight::BOLD;
+ break;
+ case SID_ATTR_CHAR_POSTURE:
+ ChangeAttr.Name = UNO_NAME_CHAR_POSTURE;
+ ChangeAttr.Value <<= awt::FontSlant_ITALIC; //char posture
+ break;
+ case SID_ATTR_CHAR_STRIKEOUT:
+ ChangeAttr.Name = UNO_NAME_CHAR_STRIKEOUT;
+ ChangeAttr.Value <<= awt::FontStrikeout::SINGLE; //char strikeout
+ break;
+ case SID_ATTR_CHAR_UNDERLINE:
+ ChangeAttr.Name = UNO_NAME_CHAR_UNDERLINE;
+ ChangeAttr.Value <<= aChangeAttr.m_nAttr; //underline line
+ break;
+ }
+ if( aChangeAttr.m_nColor != COL_NONE_COLOR )
+ {
+ if( aChangeAttr.m_nItemId == SID_ATTR_BRUSH )
+ {
+ ChangeAttrColor.Name = UNO_NAME_CHAR_BACK_COLOR;
+ if( aChangeAttr.m_nColor == COL_TRANSPARENT )//char backcolor
+ ChangeAttrColor.Value <<= COL_BLUE;
+ else
+ ChangeAttrColor.Value <<= aChangeAttr.m_nColor;
+ }
+ else
+ {
+ ChangeAttrColor.Name = UNO_NAME_CHAR_COLOR;
+ if( aChangeAttr.m_nColor == COL_TRANSPARENT )//char color
+ ChangeAttrColor.Value <<= COL_BLUE;
+ else
+ ChangeAttrColor.Value <<= aChangeAttr.m_nColor;
+ }
+ }
+ }
+
+ // sw_redlinehide: this function only needs SwWrongList for 1 character,
+ // and the end is excluded by InWrongWord(),
+ // so it ought to work to just pick the wrong-list/node that contains
+ // the character following the given nIndex
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ TextFrameIndex const nCorePos(GetPortionData().GetCoreViewPosition(nIndex));
+ std::pair<SwTextNode*, sal_Int32> pos(pFrame->MapViewToModel(nCorePos));
+ if (pos.first->Len() == pos.second
+ && nCorePos != TextFrameIndex(pFrame->GetText().getLength()))
+ {
+ pos = pFrame->MapViewToModel(nCorePos + TextFrameIndex(1)); // try this one instead
+ assert(pos.first->Len() != pos.second);
+ }
+ const SwTextNode *const pTextNode(pos.first);
+
+ sal_Int32 nValues = rValues.size();
+ for (sal_Int32 i = 0; i < nValues; ++i)
+ {
+ PropertyValue& rValue = rValues[i];
+
+ if (rValue.Name == ChangeAttr.Name )
+ {
+ rValue.Value = ChangeAttr.Value;
+ continue;
+ }
+
+ if (rValue.Name == ChangeAttrColor.Name )
+ {
+ rValue.Value = ChangeAttrColor.Value;
+ continue;
+ }
+
+ //back color
+ if (rValue.Name == UNO_NAME_CHAR_BACK_COLOR)
+ {
+ uno::Any &anyChar = rValue.Value;
+ sal_uInt32 crBack = static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved));
+ if (COL_AUTO == Color(ColorTransparency, crBack))
+ {
+ uno::Reference<XAccessibleComponent> xComponent(this);
+ if (xComponent.is())
+ {
+ crBack = static_cast<sal_uInt32>(xComponent->getBackground());
+ }
+ rValue.Value <<= crBack;
+ }
+ continue;
+ }
+
+ //char color
+ if (rValue.Name == UNO_NAME_CHAR_COLOR)
+ {
+ if( GetPortionData().IsInGrayPortion( nIndex ) )
+ rValue.Value <<= SwViewOption::GetFieldShadingsColor();
+ uno::Any &anyChar = rValue.Value;
+ sal_uInt32 crChar = static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved));
+
+ if( COL_AUTO == Color(ColorTransparency, crChar) )
+ {
+ uno::Reference<XAccessibleComponent> xComponent(this);
+ if (xComponent.is())
+ {
+ Color cr(ColorTransparency, xComponent->getBackground());
+ crChar = sal_uInt32(cr.IsDark() ? COL_WHITE : COL_BLACK);
+ rValue.Value <<= crChar;
+ }
+ }
+ continue;
+ }
+
+ // UnderLine
+ if (rValue.Name == UNO_NAME_CHAR_UNDERLINE)
+ {
+ //misspelled word
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr && pCursorShell->GetViewOptions() && pCursorShell->GetViewOptions()->IsOnlineSpell())
+ {
+ const SwWrongList* pWrongList = pTextNode->GetWrong();
+ if( nullptr != pWrongList )
+ {
+ sal_Int32 nBegin = pos.second;
+ sal_Int32 nLen = 1;
+ if (pWrongList->InWrongWord(nBegin, nLen) && !pTextNode->IsSymbolAt(nBegin))
+ {
+ rValue.Value <<= sal_uInt16(LINESTYLE_WAVE);
+ }
+ }
+ }
+ continue;
+ }
+
+ // UnderLineColor
+ if (rValue.Name == UNO_NAME_CHAR_UNDERLINE_COLOR)
+ {
+ //misspelled word
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr && pCursorShell->GetViewOptions() && pCursorShell->GetViewOptions()->IsOnlineSpell())
+ {
+ const SwWrongList* pWrongList = pTextNode->GetWrong();
+ if( nullptr != pWrongList )
+ {
+ sal_Int32 nBegin = pos.second;
+ sal_Int32 nLen = 1;
+ if (pWrongList->InWrongWord(nBegin, nLen) && !pTextNode->IsSymbolAt(nBegin))
+ {
+ rValue.Value <<= sal_Int32(0x00ff0000);
+ continue;
+ }
+ }
+ }
+
+ uno::Any &anyChar = rValue.Value;
+ sal_uInt32 crUnderline = static_cast<sal_uInt32>( reinterpret_cast<sal_uIntPtr>(anyChar.pReserved));
+ if ( COL_AUTO == Color(ColorTransparency, crUnderline) )
+ {
+ uno::Reference<XAccessibleComponent> xComponent(this);
+ if (xComponent.is())
+ {
+ Color cr(ColorTransparency, xComponent->getBackground());
+ crUnderline = sal_uInt32(cr.IsDark() ? COL_WHITE : COL_BLACK);
+ rValue.Value <<= crUnderline;
+ }
+ }
+
+ continue;
+ }
+
+ //tab stop
+ if (rValue.Name == UNO_NAME_TABSTOPS)
+ {
+ css::uno::Sequence< css::style::TabStop > tabs = GetCurrentTabStop( nIndex );
+ if( !tabs.hasElements() )
+ {
+ css::style::TabStop ts;
+ css::awt::Rectangle rc0 = getCharacterBounds(0);
+ css::awt::Rectangle rc1 = getCharacterBounds(nIndex);
+ if( rc1.X - rc0.X >= 48 )
+ ts.Position = (rc1.X - rc0.X) - (rc1.X - rc0.X - 48)% 47 + 47;
+ else
+ ts.Position = 48;
+ ts.DecimalChar = ' ';
+ ts.FillChar = ' ';
+ ts.Alignment = css::style::TabAlign_LEFT;
+ tabs = { ts };
+ }
+ rValue.Value <<= tabs;
+ continue;
+ }
+
+ //footnote & endnote
+ if (rValue.Name == UNO_NAME_CHAR_ESCAPEMENT)
+ {
+ if ( GetPortionData().IsIndexInFootnode(nIndex) )
+ {
+ rValue.Value <<= sal_Int32(101);
+ }
+ continue;
+ }
+ }
+}
+
+awt::Rectangle SwAccessibleParagraph::getCharacterBounds(
+ sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // #i12332# The position after the string needs special treatment.
+ // IsValidChar -> IsValidPosition
+ if( ! (IsValidPosition( nIndex, GetString().getLength() ) ) )
+ throw lang::IndexOutOfBoundsException();
+
+ // #i12332#
+ bool bBehindText = false;
+ if ( nIndex == GetString().getLength() )
+ bBehindText = true;
+
+ // get model position & prepare GetCharRect() arguments
+ SwCursorMoveState aMoveState;
+ aMoveState.m_bRealHeight = true;
+ aMoveState.m_bRealWidth = true;
+ SwSpecialPos aSpecialPos;
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+
+ /** #i12332# FillSpecialPos does not accept nIndex ==
+ GetString().getLength(). In that case nPos is set to the
+ length of the string in the core. This way GetCharRect
+ returns the rectangle for a cursor at the end of the
+ paragraph. */
+ const TextFrameIndex nPos = bBehindText
+ ? TextFrameIndex(pFrame->GetText().getLength())
+ : GetPortionData().FillSpecialPos(nIndex, aSpecialPos, aMoveState.m_pSpecialPos );
+
+ // call GetCharRect
+ SwRect aCoreRect;
+ SwPosition aPosition(pFrame->MapViewToModelPos(nPos));
+ GetFrame()->GetCharRect( aCoreRect, aPosition, &aMoveState );
+
+ // translate core coordinates into accessibility coordinates
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aCoreRect ));
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ aScreenRect.Move( -aFramePixPos.getX(), -aFramePixPos.getY() );
+
+ // convert into AWT Rectangle
+ return awt::Rectangle(
+ aScreenRect.Left(), aScreenRect.Top(),
+ aScreenRect.GetWidth(), aScreenRect.GetHeight() );
+}
+
+sal_Int32 SwAccessibleParagraph::getCharacterCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetString().getLength();
+}
+
+sal_Int32 SwAccessibleParagraph::getIndexAtPoint( const awt::Point& rPoint )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // construct Point (translate into layout coordinates)
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+ Point aPoint( rPoint.X, rPoint.Y );
+ SwRect aLogBounds( GetBounds( *(GetMap()), GetFrame() ) ); // twip rel to doc root
+ Point aPixPos( GetMap()->CoreToPixel( aLogBounds ).TopLeft() );
+ aPoint.setX(aPoint.getX() + aPixPos.getX());
+ aPoint.setY(aPoint.getY() + aPixPos.getY());
+ Point aCorePoint( GetMap()->PixelToCore( aPoint ) );
+ if( !aLogBounds.Contains( aCorePoint ) )
+ {
+ // #i12332# rPoint is may also be in rectangle returned by
+ // getCharacterBounds(getCharacterCount()
+
+ awt::Rectangle aRectEndPos =
+ getCharacterBounds(getCharacterCount());
+
+ if (rPoint.X - aRectEndPos.X >= 0 &&
+ rPoint.X - aRectEndPos.X < aRectEndPos.Width &&
+ rPoint.Y - aRectEndPos.Y >= 0 &&
+ rPoint.Y - aRectEndPos.Y < aRectEndPos.Height)
+ return getCharacterCount();
+
+ return -1;
+ }
+
+ // ask core for position
+ OSL_ENSURE( GetFrame() != nullptr, "The text frame has vanished!" );
+ OSL_ENSURE( GetFrame()->IsTextFrame(), "The text frame has mutated!" );
+ const SwTextFrame* pFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ // construct SwPosition (where GetModelPositionForViewPoint() will put the result into)
+ SwTextNode* pNode = const_cast<SwTextNode*>(pFrame->GetTextNodeFirst());
+ SwPosition aPos(*pNode, 0);
+ SwCursorMoveState aMoveState;
+ aMoveState.m_bPosMatchesBounds = true;
+ const bool bSuccess = pFrame->GetModelPositionForViewPoint( &aPos, aCorePoint, &aMoveState );
+
+ TextFrameIndex nIndex = pFrame->MapModelToViewPos(aPos);
+ if (TextFrameIndex(0) < nIndex)
+ {
+ assert(bSuccess);
+ SwRect aResultRect;
+ pFrame->GetCharRect( aResultRect, aPos );
+ bool bVert = pFrame->IsVertical();
+ bool bR2L = pFrame->IsRightToLeft();
+
+ if ( (!bVert && aResultRect.Pos().getX() > aCorePoint.getX()) ||
+ ( bVert && aResultRect.Pos().getY() > aCorePoint.getY()) ||
+ ( bR2L && aResultRect.Right() < aCorePoint.getX()) )
+ {
+ SwPosition aPosPrev(pFrame->MapViewToModelPos(nIndex - TextFrameIndex(1)));
+ SwRect aResultRectPrev;
+ pFrame->GetCharRect( aResultRectPrev, aPosPrev );
+ if ( (!bVert && aResultRectPrev.Pos().getX() < aCorePoint.getX() && aResultRect.Pos().getY() == aResultRectPrev.Pos().getY()) ||
+ ( bVert && aResultRectPrev.Pos().getY() < aCorePoint.getY() && aResultRect.Pos().getX() == aResultRectPrev.Pos().getX()) ||
+ ( bR2L && aResultRectPrev.Right() > aCorePoint.getX() && aResultRect.Pos().getY() == aResultRectPrev.Pos().getY()) )
+ {
+ --nIndex;
+ }
+ }
+ }
+
+ return bSuccess
+ ? GetPortionData().GetAccessiblePosition(nIndex)
+ : -1;
+}
+
+OUString SwAccessibleParagraph::getSelectedText()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart, nEnd;
+ bool bSelected = GetSelection( nStart, nEnd );
+ return bSelected
+ ? GetString().copy( nStart, nEnd - nStart )
+ : OUString();
+}
+
+sal_Int32 SwAccessibleParagraph::getSelectionStart()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart, nEnd;
+ GetSelection( nStart, nEnd );
+ return nStart;
+}
+
+sal_Int32 SwAccessibleParagraph::getSelectionEnd()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart, nEnd;
+ GetSelection( nStart, nEnd );
+ return nEnd;
+}
+
+sal_Bool SwAccessibleParagraph::setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidRange( nStartIndex, nEndIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ bool bRet = false;
+
+ // get cursor shell
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ // create pam for selection
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ TextFrameIndex const nStart(GetPortionData().GetCoreViewPosition(nStartIndex));
+ TextFrameIndex const nEnd(GetPortionData().GetCoreViewPosition(nEndIndex));
+ SwPaM aPaM(pFrame->MapViewToModelPos(nStart));
+ aPaM.SetMark();
+ *aPaM.GetPoint() = pFrame->MapViewToModelPos(nEnd);
+
+ // set PaM at cursor shell
+ bRet = Select( aPaM );
+ }
+
+ return bRet;
+}
+
+OUString SwAccessibleParagraph::getText()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetString();
+}
+
+OUString SwAccessibleParagraph::getTextRange(
+ sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ OUString sText( GetString() );
+
+ if ( !IsValidRange( nStartIndex, nEndIndex, sText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ OrderRange( nStartIndex, nEndIndex );
+ return sText.copy(nStartIndex, nEndIndex-nStartIndex );
+}
+
+/*accessibility::*/TextSegment SwAccessibleParagraph::getTextAtIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ /*accessibility::*/TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ const OUString rText = GetString();
+ // implement the silly specification that first position after
+ // text must return an empty string, rather than throwing an
+ // IndexOutOfBoundsException, except for LINE, where the last
+ // line is returned
+ if( nIndex == rText.getLength() && AccessibleTextType::LINE != nTextType )
+ return aResult;
+
+ // with error checking
+ i18n::Boundary aBound;
+ bool bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+
+ OSL_ENSURE( aBound.startPos >= 0, "illegal boundary" );
+ OSL_ENSURE( aBound.startPos <= aBound.endPos, "illegal boundary" );
+
+ // return word (if present)
+ if ( bWord )
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+
+ return aResult;
+}
+
+/*accessibility::*/TextSegment SwAccessibleParagraph::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString rText = GetString();
+
+ /*accessibility::*/TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ //If nIndex = 0, then nobefore text so return -1 directly.
+ if( nIndex == 0 )
+ return aResult;
+ //Tab will be return when call WORDTYPE
+
+ // get starting pos
+ i18n::Boundary aBound;
+ if (nIndex == rText.getLength())
+ aBound.startPos = aBound.endPos = nIndex;
+ else
+ {
+ bool bTmp = GetTextBoundary( aBound, rText, nIndex, nTextType );
+
+ if ( ! bTmp )
+ aBound.startPos = aBound.endPos = nIndex;
+ }
+
+ // now skip to previous word
+ if (nTextType==2 || nTextType == 3)
+ {
+ i18n::Boundary preBound = aBound;
+ while(preBound.startPos==aBound.startPos && nIndex > 0)
+ {
+ nIndex = min( nIndex, preBound.startPos ) - 1;
+ if( nIndex < 0 ) break;
+ GetTextBoundary( preBound, rText, nIndex, nTextType );
+ }
+ //if (nIndex>0)
+ if (nIndex>=0)
+ //Tab will be return when call WORDTYPE
+ {
+ aResult.SegmentText = rText.copy( preBound.startPos, preBound.endPos - preBound.startPos );
+ aResult.SegmentStart = preBound.startPos;
+ aResult.SegmentEnd = preBound.endPos;
+ }
+ }
+ else
+ {
+ bool bWord = false;
+ while( !bWord )
+ {
+ nIndex = min( nIndex, aBound.startPos ) - 1;
+ if( nIndex >= 0 )
+ {
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+ }
+ else
+ break; // exit if beginning of string is reached
+ }
+
+ if (bWord && nIndex<rText.getLength())
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+ }
+ return aResult;
+}
+
+/*accessibility::*/TextSegment SwAccessibleParagraph::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 nTextType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ /*accessibility::*/TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ const OUString rText = GetString();
+
+ // implement the silly specification that first position after
+ // text must return an empty string, rather than throwing an
+ // IndexOutOfBoundsException
+ if( nIndex == rText.getLength() )
+ return aResult;
+
+ // get first word, then skip to next word
+ i18n::Boundary aBound;
+ GetTextBoundary( aBound, rText, nIndex, nTextType );
+ bool bWord = false;
+ while( !bWord )
+ {
+ nIndex = max( sal_Int32(nIndex+1), aBound.endPos );
+ if( nIndex < rText.getLength() )
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+ else
+ break; // exit if end of string is reached
+ }
+
+ if ( bWord )
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+
+/*
+ sal_Bool bWord = sal_False;
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+
+ if (nTextType==2)
+ {
+ Boundary nexBound=aBound;
+
+ // real current word
+ if( nIndex <= aBound.endPos && nIndex >= aBound.startPos )
+ {
+ while(nexBound.endPos==aBound.endPos&&nIndex<rText.getLength())
+ {
+ // nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) + 1;
+ nIndex = max( (sal_Int32)(nIndex), nexBound.endPos) ;
+ const sal_Unicode* pStr = rText.getStr();
+ if (pStr)
+ {
+ if( pStr[nIndex] == sal_Unicode(' ') )
+ nIndex++;
+ }
+ if( nIndex < rText.getLength() )
+ {
+ bWord = GetTextBoundary( nexBound, rText, nIndex, nTextType );
+ }
+ }
+ }
+
+ if (bWord && nIndex<rText.getLength())
+ {
+ aResult.SegmentText = rText.copy( nexBound.startPos, nexBound.endPos - nexBound.startPos );
+ aResult.SegmentStart = nexBound.startPos;
+ aResult.SegmentEnd = nexBound.endPos;
+ }
+
+ }
+ else
+ {
+ bWord = sal_False;
+ while( !bWord )
+ {
+ nIndex = max( (sal_Int32)(nIndex+1), aBound.endPos );
+ if( nIndex < rText.getLength() )
+ {
+ bWord = GetTextBoundary( aBound, rText, nIndex, nTextType );
+ }
+ else
+ break; // exit if end of string is reached
+ }
+ if (bWord && nIndex<rText.getLength())
+ {
+ aResult.SegmentText = rText.copy( aBound.startPos, aBound.endPos - aBound.startPos );
+ aResult.SegmentStart = aBound.startPos;
+ aResult.SegmentEnd = aBound.endPos;
+ }
+ }
+*/
+ return aResult;
+}
+
+sal_Bool SwAccessibleParagraph::copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // select and copy (through dispatch mechanism)
+ setSelection( nStartIndex, nEndIndex );
+ ExecuteAtViewShell( SID_COPY );
+ return true;
+}
+
+sal_Bool SwAccessibleParagraph::scrollSubstringTo( sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex, AccessibleScrollType aScrollType )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidRange( nStartIndex, nEndIndex, nLength ) )
+ throw lang::IndexOutOfBoundsException();
+
+ vcl::Window *pWin = GetWindow();
+ if ( ! pWin )
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+
+ /* Start and end character bounds, in pixels, relative to the paragraph */
+ awt::Rectangle startR, endR;
+ startR = getCharacterBounds(nStartIndex);
+ endR = getCharacterBounds(nEndIndex);
+
+ /* Adjust points to fit the bounding box of both bounds. */
+ Point sP(std::min(startR.X, endR.X), startR.Y);
+ Point eP(std::max(startR.X + startR.Width, endR.X + endR.Width), endR.Y + endR.Height);
+
+ /* Offset the values relative to the view shell frame */
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ sP += aFramePixPos;
+ eP += aFramePixPos;
+
+ Point startPoint(GetMap()->PixelToCore(sP));
+ Point endPoint(GetMap()->PixelToCore(eP));
+
+ switch (aScrollType)
+ {
+#ifdef notyet
+ case AccessibleScrollType_SCROLL_TOP_LEFT:
+ break;
+ case AccessibleScrollType_SCROLL_BOTTOM_RIGHT:
+ break;
+ case AccessibleScrollType_SCROLL_TOP_EDGE:
+ break;
+ case AccessibleScrollType_SCROLL_BOTTOM_EDGE:
+ break;
+ case AccessibleScrollType_SCROLL_LEFT_EDGE:
+ break;
+ case AccessibleScrollType_SCROLL_RIGHT_EDGE:
+ break;
+#endif
+ case AccessibleScrollType_SCROLL_ANYWHERE:
+ break;
+ default:
+ return false;
+ }
+
+ const SwRect aRect(startPoint, endPoint);
+ SwViewShell* pViewShell = GetMap()->GetShell();
+ OSL_ENSURE( pViewShell != nullptr, "View shell expected!" );
+
+ ScrollMDI(pViewShell, aRect, USHRT_MAX, USHRT_MAX);
+
+ return true;
+}
+
+// XAccessibleEditableText
+
+sal_Bool SwAccessibleParagraph::cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( !IsEditableState() )
+ return false;
+
+ // select and cut (through dispatch mechanism)
+ setSelection( nStartIndex, nEndIndex );
+ ExecuteAtViewShell( SID_CUT );
+ return true;
+}
+
+sal_Bool SwAccessibleParagraph::pasteText( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( !IsEditableState() )
+ return false;
+
+ // select and paste (through dispatch mechanism)
+ setSelection( nIndex, nIndex );
+ ExecuteAtViewShell( SID_PASTE );
+ return true;
+}
+
+sal_Bool SwAccessibleParagraph::deleteText( sal_Int32 nStartIndex, sal_Int32 nEndIndex )
+{
+ return replaceText( nStartIndex, nEndIndex, OUString() );
+}
+
+sal_Bool SwAccessibleParagraph::insertText( const OUString& sText, sal_Int32 nIndex )
+{
+ return replaceText( nIndex, nIndex, sText );
+}
+
+sal_Bool SwAccessibleParagraph::replaceText(
+ sal_Int32 nStartIndex, sal_Int32 nEndIndex,
+ const OUString& sReplacement )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString& rText = GetString();
+
+ if( !IsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ if( !IsEditableState() )
+ return false;
+
+ // translate positions
+ TextFrameIndex nStart;
+ TextFrameIndex nEnd;
+ bool bSuccess = GetPortionData().GetEditableRange(
+ nStartIndex, nEndIndex, nStart, nEnd );
+
+ // edit only if the range is editable
+ if( bSuccess )
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ // create SwPosition for nStartIndex
+ SwPosition aStartPos(pFrame->MapViewToModelPos(nStart));
+
+ // create SwPosition for nEndIndex
+ SwPosition aEndPos(pFrame->MapViewToModelPos(nEnd));
+
+ // now create XTextRange as helper and set string
+ const uno::Reference<text::XTextRange> xRange(
+ SwXTextRange::CreateXTextRange(
+ const_cast<SwDoc&>(pFrame->GetDoc()), aStartPos, &aEndPos));
+ xRange->setString(sReplacement);
+
+ // delete portion data
+ ClearPortionData();
+ }
+
+ return bSuccess;
+
+}
+
+sal_Bool SwAccessibleParagraph::setAttributes(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex,
+ const uno::Sequence<PropertyValue>& rAttributeSet )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const OUString& rText = GetString();
+
+ if( ! IsValidRange( nStartIndex, nEndIndex, rText.getLength() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ if( !IsEditableState() )
+ return false;
+
+ // create a (dummy) text portion for the sole purpose of calling
+ // setPropertyValue on it
+ rtl::Reference<SwXTextPortion> xPortion = CreateUnoPortion( nStartIndex,
+ nEndIndex );
+
+ // build sorted index array
+ sal_Int32 nLength = rAttributeSet.getLength();
+ const PropertyValue* pPairs = rAttributeSet.getConstArray();
+ std::vector<sal_Int32> aIndices(nLength);
+ std::iota(aIndices.begin(), aIndices.end(), 0);
+ std::sort(aIndices.begin(), aIndices.end(), IndexCompare(pPairs));
+
+ // create sorted sequences according to index array
+ uno::Sequence< OUString > aNames( nLength );
+ OUString* pNames = aNames.getArray();
+ uno::Sequence< uno::Any > aValues( nLength );
+ uno::Any* pValues = aValues.getArray();
+ for (sal_Int32 i = 0; i < nLength; ++i)
+ {
+ const PropertyValue& rVal = pPairs[aIndices[i]];
+ pNames[i] = rVal.Name;
+ pValues[i] = rVal.Value;
+ }
+ aIndices.clear();
+
+ // now set the values
+ bool bRet = true;
+ try
+ {
+ xPortion->setPropertyValues( aNames, aValues );
+ }
+ catch (const UnknownPropertyException&)
+ {
+ // error handling through return code!
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+sal_Bool SwAccessibleParagraph::setText( const OUString& sText )
+{
+ return replaceText(0, GetString().getLength(), sText);
+}
+
+// XAccessibleSelection
+
+void SwAccessibleParagraph::selectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ ThrowIfDisposed();
+
+ m_aSelectionHelper.selectAccessibleChild(nChildIndex);
+}
+
+sal_Bool SwAccessibleParagraph::isAccessibleChildSelected(
+ sal_Int32 nChildIndex )
+{
+ ThrowIfDisposed();
+
+ return m_aSelectionHelper.isAccessibleChildSelected(nChildIndex);
+}
+
+void SwAccessibleParagraph::clearAccessibleSelection( )
+{
+ ThrowIfDisposed();
+}
+
+void SwAccessibleParagraph::selectAllAccessibleChildren( )
+{
+ ThrowIfDisposed();
+
+ m_aSelectionHelper.selectAllAccessibleChildren();
+}
+
+sal_Int32 SwAccessibleParagraph::getSelectedAccessibleChildCount( )
+{
+ ThrowIfDisposed();
+
+ return m_aSelectionHelper.getSelectedAccessibleChildCount();
+}
+
+uno::Reference<XAccessible> SwAccessibleParagraph::getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex )
+{
+ ThrowIfDisposed();
+
+ return m_aSelectionHelper.getSelectedAccessibleChild(nSelectedChildIndex);
+}
+
+// index has to be treated as global child index.
+void SwAccessibleParagraph::deselectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ ThrowIfDisposed();
+
+ m_aSelectionHelper.deselectAccessibleChild( nChildIndex );
+}
+
+// XAccessibleHypertext
+
+namespace {
+
+class SwHyperlinkIter_Impl
+{
+ SwTextFrame const& m_rFrame;
+ sw::MergedAttrIter m_Iter;
+ TextFrameIndex m_nStt;
+ TextFrameIndex m_nEnd;
+
+public:
+ explicit SwHyperlinkIter_Impl(const SwTextFrame & rTextFrame);
+ const SwTextAttr *next(SwTextNode const** ppNode = nullptr);
+
+ TextFrameIndex startIdx() const { return m_nStt; }
+ TextFrameIndex endIdx() const { return m_nEnd; }
+};
+
+}
+
+SwHyperlinkIter_Impl::SwHyperlinkIter_Impl(const SwTextFrame & rTextFrame)
+ : m_rFrame(rTextFrame)
+ , m_Iter(rTextFrame)
+ , m_nStt(rTextFrame.GetOffset())
+{
+ const SwTextFrame *const pFollFrame = rTextFrame.GetFollow();
+ m_nEnd = pFollFrame ? pFollFrame->GetOffset() : TextFrameIndex(rTextFrame.GetText().getLength());
+}
+
+const SwTextAttr *SwHyperlinkIter_Impl::next(SwTextNode const** ppNode)
+{
+ const SwTextAttr *pAttr = nullptr;
+ if (ppNode)
+ {
+ *ppNode = nullptr;
+ }
+
+ SwTextNode const* pNode(nullptr);
+ while (SwTextAttr const*const pHt = m_Iter.NextAttr(&pNode))
+ {
+ if (RES_TXTATR_INETFMT == pHt->Which())
+ {
+ const TextFrameIndex nHtStt(m_rFrame.MapModelToView(pNode, pHt->GetStart()));
+ const TextFrameIndex nHtEnd(m_rFrame.MapModelToView(pNode, pHt->GetAnyEnd()));
+ if (nHtEnd > nHtStt &&
+ ((nHtStt >= m_nStt && nHtStt < m_nEnd) ||
+ (nHtEnd > m_nStt && nHtEnd <= m_nEnd)))
+ {
+ pAttr = pHt;
+ if (ppNode)
+ {
+ *ppNode = pNode;
+ }
+ break;
+ }
+ }
+ }
+
+ return pAttr;
+};
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getHyperLinkCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = 0;
+ // #i77108# - provide hyperlinks also in editable documents.
+
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ SwHyperlinkIter_Impl aIter(*pTextFrame);
+ while( aIter.next() )
+ nCount++;
+
+ return nCount;
+}
+
+uno::Reference< XAccessibleHyperlink > SAL_CALL
+ SwAccessibleParagraph::getHyperLink( sal_Int32 nLinkIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ uno::Reference< XAccessibleHyperlink > xRet;
+
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ SwHyperlinkIter_Impl aHIter(*pTextFrame);
+ SwTextNode const* pNode(nullptr);
+ SwTextAttr* pHt = const_cast<SwTextAttr*>(aHIter.next(&pNode));
+ for (sal_Int32 nTIndex = 0; pHt && nTIndex <= nLinkIndex; ++nTIndex)
+ {
+ if( nTIndex == nLinkIndex )
+ { // found
+ if (!m_pHyperTextData)
+ m_pHyperTextData.reset( new SwAccessibleHyperTextData );
+ SwAccessibleHyperTextData::iterator aIter =
+ m_pHyperTextData ->find( pHt );
+ if (aIter != m_pHyperTextData->end())
+ {
+ xRet = (*aIter).second;
+ }
+ if (!xRet.is())
+ {
+ TextFrameIndex const nHintStart(pTextFrame->MapModelToView(pNode, pHt->GetStart()));
+ TextFrameIndex const nHintEnd(pTextFrame->MapModelToView(pNode, pHt->GetAnyEnd()));
+ const sal_Int32 nTmpHStt = GetPortionData().GetAccessiblePosition(
+ max(aHIter.startIdx(), nHintStart));
+ const sal_Int32 nTmpHEnd = GetPortionData().GetAccessiblePosition(
+ min(aHIter.endIdx(), nHintEnd));
+ xRet = new SwAccessibleHyperlink(*pHt,
+ *this, nTmpHStt, nTmpHEnd );
+ if (aIter != m_pHyperTextData->end())
+ {
+ (*aIter).second = xRet;
+ }
+ else
+ {
+ m_pHyperTextData->emplace( pHt, xRet );
+ }
+ }
+ break;
+ }
+
+ // iterate next hyperlink
+ pHt = const_cast<SwTextAttr*>(aHIter.next(&pNode));
+ }
+ if( !xRet.is() )
+ throw lang::IndexOutOfBoundsException();
+
+ return xRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getHyperLinkIndex( sal_Int32 nCharIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nCharIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ sal_Int32 nRet = -1;
+ // #i77108#
+ {
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>( GetFrame() );
+ SwHyperlinkIter_Impl aHIter(*pTextFrame);
+
+ const TextFrameIndex nIdx = GetPortionData().GetCoreViewPosition(nCharIndex);
+ sal_Int32 nPos = 0;
+ SwTextNode const* pNode(nullptr);
+ const SwTextAttr *pHt = aHIter.next(&pNode);
+ while (pHt && (nIdx < pTextFrame->MapModelToView(pNode, pHt->GetStart())
+ || nIdx >= pTextFrame->MapModelToView(pNode, pHt->GetAnyEnd())))
+ {
+ pHt = aHIter.next(&pNode);
+ nPos++;
+ }
+
+ if( pHt )
+ nRet = nPos;
+ }
+
+ if (nRet == -1)
+ throw lang::IndexOutOfBoundsException();
+ return nRet;
+}
+
+// #i71360#, #i108125# - adjustments for change tracking text markup
+sal_Int32 SAL_CALL SwAccessibleParagraph::getTextMarkupCount( sal_Int32 nTextMarkupType )
+{
+ SolarMutexGuard g;
+
+ std::unique_ptr<SwTextMarkupHelper> pTextMarkupHelper;
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ case text::TextMarkupType::TRACK_CHANGE_DELETION:
+ case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pTextMarkupHelper.reset( new SwTextMarkupHelper(
+ GetPortionData(),
+ *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
+ }
+ break;
+ default:
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
+ }
+ }
+
+ return pTextMarkupHelper->getTextMarkupCount( nTextMarkupType );
+}
+
+//MSAA Extension Implementation in app module
+sal_Bool SAL_CALL SwAccessibleParagraph::scrollToPosition( const css::awt::Point&, sal_Bool )
+{
+ return false;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getSelectedPortionCount( )
+{
+ SolarMutexGuard g;
+
+ sal_Int32 nSelected = 0;
+ SwPaM* pCursor = GetCursor( true );
+ if( pCursor != nullptr )
+ {
+ // get SwPosition for my node
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ // iterate over ring
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ // check whether frame's node(s) are 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ SwNodeOffset nStartIndex = pStart->nNode.GetIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ SwNodeOffset nEndIndex = pEnd->nNode.GetIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ nSelected++;
+ }
+ // else: this PaM doesn't point to this paragraph
+ }
+ // else: this PaM is collapsed and doesn't select anything
+ }
+ }
+ return nSelected;
+
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionStart( sal_Int32 nSelectedPortionIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart=-1, nEnd=-1;
+ /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd );
+ return nStart;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nStart=-1, nEnd=-1;
+ /*sal_Bool bSelected = */GetSelectionAtIndex(&nSelectedPortionIndex, nStart, nEnd );
+ return nEnd;
+}
+
+sal_Bool SAL_CALL SwAccessibleParagraph::removeSelection( sal_Int32 selectionIndex )
+{
+ SolarMutexGuard g;
+
+ if(selectionIndex < 0) return false;
+
+ sal_Int32 nSelected = selectionIndex;
+
+ // get the selection, and test whether it affects our text node
+ SwPaM* pCursor = GetCursor( true );
+
+ if( pCursor != nullptr )
+ {
+ bool bRet = false;
+
+ // get SwPosition for my node
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ // iterate over ring
+ SwPaM* pRingStart = pCursor;
+ do
+ {
+ // ignore, if no mark
+ if( pCursor->HasMark() )
+ {
+ // check whether frame's node(s) are 'inside' pCursor
+ SwPosition* pStart = pCursor->Start();
+ SwNodeOffset nStartIndex = pStart->nNode.GetIndex();
+ SwPosition* pEnd = pCursor->End();
+ SwNodeOffset nEndIndex = pEnd->nNode.GetIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if( nSelected == 0 )
+ {
+ pCursor->MoveTo(nullptr);
+ delete pCursor;
+ bRet = true;
+ }
+ else
+ {
+ nSelected--;
+ }
+ }
+ }
+ // else: this PaM is collapsed and doesn't select anything
+ if(!bRet)
+ pCursor = pCursor->GetNext();
+ }
+ while( !bRet && (pCursor != pRingStart) );
+ }
+ return true;
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::addSelection( sal_Int32, sal_Int32 startOffset, sal_Int32 endOffset)
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking
+ sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidRange( startOffset, endOffset, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ sal_Int32 nSelectedCount = getSelectedPortionCount();
+ for ( sal_Int32 i = nSelectedCount ; i >= 0 ; i--)
+ {
+ sal_Int32 nStart, nEnd;
+ bool bSelected = GetSelectionAtIndex(&i, nStart, nEnd );
+ if(bSelected)
+ {
+ if(nStart <= nEnd )
+ {
+ if (( startOffset>=nStart && startOffset <=nEnd ) || //startOffset in a selection
+ ( endOffset>=nStart && endOffset <=nEnd ) || //endOffset in a selection
+ ( startOffset <= nStart && endOffset >=nEnd) || //start and end include the old selection
+ ( startOffset >= nStart && endOffset <=nEnd) )
+ {
+ removeSelection(i);
+ }
+
+ }
+ else
+ {
+ if (( startOffset>=nEnd && startOffset <=nStart ) || //startOffset in a selection
+ ( endOffset>=nEnd && endOffset <=nStart ) || //endOffset in a selection
+ ( startOffset <= nStart && endOffset >=nEnd) || //start and end include the old selection
+ ( startOffset >= nStart && endOffset <=nEnd) )
+
+ {
+ removeSelection(i);
+ }
+ }
+ }
+
+ }
+
+ // get cursor shell
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ // create pam for selection
+ pCursorShell->StartAction();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwPaM* aPaM = pCursorShell->CreateCursor();
+ aPaM->SetMark();
+ *aPaM->GetPoint() = pFrame->MapViewToModelPos(GetPortionData().GetCoreViewPosition(startOffset));
+ *aPaM->GetMark() = pFrame->MapViewToModelPos(GetPortionData().GetCoreViewPosition(endOffset));
+ pCursorShell->EndAction();
+ }
+
+ return 0;
+}
+
+/*accessibility::*/TextSegment SAL_CALL
+ SwAccessibleParagraph::getTextMarkup( sal_Int32 nTextMarkupIndex,
+ sal_Int32 nTextMarkupType )
+{
+ SolarMutexGuard g;
+
+ std::unique_ptr<SwTextMarkupHelper> pTextMarkupHelper;
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ case text::TextMarkupType::TRACK_CHANGE_DELETION:
+ case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pTextMarkupHelper.reset( new SwTextMarkupHelper(
+ GetPortionData(),
+ *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
+ }
+ break;
+ default:
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
+ }
+ }
+
+ return pTextMarkupHelper->getTextMarkup( nTextMarkupIndex, nTextMarkupType );
+}
+
+uno::Sequence< /*accessibility::*/TextSegment > SAL_CALL
+ SwAccessibleParagraph::getTextMarkupAtIndex( sal_Int32 nCharIndex,
+ sal_Int32 nTextMarkupType )
+{
+ SolarMutexGuard g;
+
+ // parameter checking
+ const sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nCharIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ std::unique_ptr<SwTextMarkupHelper> pTextMarkupHelper;
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ case text::TextMarkupType::TRACK_CHANGE_DELETION:
+ case text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pTextMarkupHelper.reset( new SwTextMarkupHelper(
+ GetPortionData(),
+ *(mpParaChangeTrackInfo->getChangeTrackingTextMarkupList( nTextMarkupType ) )) );
+ }
+ break;
+ default:
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ pTextMarkupHelper.reset(new SwTextMarkupHelper(GetPortionData(), *pFrame));
+ }
+ }
+
+ return pTextMarkupHelper->getTextMarkupAtIndex( nCharIndex, nTextMarkupType );
+}
+
+// #i89175#
+sal_Int32 SAL_CALL SwAccessibleParagraph::getLineNumberAtIndex( sal_Int32 nIndex )
+{
+ SolarMutexGuard g;
+
+ // parameter checking
+ const sal_Int32 nLength = GetString().getLength();
+ if ( ! IsValidPosition( nIndex, nLength ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ const sal_Int32 nLineNo = GetPortionData().GetLineNo( nIndex );
+ return nLineNo;
+}
+
+/*accessibility::*/TextSegment SAL_CALL
+ SwAccessibleParagraph::getTextAtLineNumber( sal_Int32 nLineNo )
+{
+ SolarMutexGuard g;
+
+ // parameter checking
+ if ( nLineNo < 0 ||
+ nLineNo >= GetPortionData().GetLineCount() )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ i18n::Boundary aLineBound;
+ GetPortionData().GetBoundaryOfLine( nLineNo, aLineBound );
+
+ /*accessibility::*/TextSegment aTextAtLine;
+ const OUString rText = GetString();
+ aTextAtLine.SegmentText = rText.copy( aLineBound.startPos,
+ aLineBound.endPos - aLineBound.startPos );
+ aTextAtLine.SegmentStart = aLineBound.startPos;
+ aTextAtLine.SegmentEnd = aLineBound.endPos;
+
+ return aTextAtLine;
+}
+
+/*accessibility::*/TextSegment SAL_CALL SwAccessibleParagraph::getTextAtLineWithCaret()
+{
+ SolarMutexGuard g;
+
+ const sal_Int32 nLineNoOfCaret = getNumberOfLineWithCaret();
+
+ if ( nLineNoOfCaret >= 0 &&
+ nLineNoOfCaret < GetPortionData().GetLineCount() )
+ {
+ return getTextAtLineNumber( nLineNoOfCaret );
+ }
+
+ return /*accessibility::*/TextSegment();
+}
+
+sal_Int32 SAL_CALL SwAccessibleParagraph::getNumberOfLineWithCaret()
+{
+ SolarMutexGuard g;
+
+ const sal_Int32 nCaretPos = getCaretPosition();
+ const sal_Int32 nLength = GetString().getLength();
+ if ( !IsValidPosition( nCaretPos, nLength ) )
+ {
+ return -1;
+ }
+
+ sal_Int32 nLineNo = GetPortionData().GetLineNo( nCaretPos );
+
+ // special handling for cursor positioned at end of text line via End key
+ if ( nCaretPos != 0 )
+ {
+ i18n::Boundary aLineBound;
+ GetPortionData().GetBoundaryOfLine( nLineNo, aLineBound );
+ if ( nCaretPos == aLineBound.startPos )
+ {
+ SwCursorShell* pCursorShell = SwAccessibleParagraph::GetCursorShell();
+ if ( pCursorShell != nullptr )
+ {
+ const awt::Rectangle aCharRect = getCharacterBounds( nCaretPos );
+
+ const SwRect& aCursorCoreRect = pCursorShell->GetCharRect();
+ // translate core coordinates into accessibility coordinates
+ vcl::Window *pWin = GetWindow();
+ if (!pWin)
+ {
+ throw uno::RuntimeException("no Window", static_cast<cppu::OWeakObject*>(this));
+ }
+
+ tools::Rectangle aScreenRect( GetMap()->CoreToPixel( aCursorCoreRect ));
+
+ SwRect aFrameLogBounds( GetBounds( *(GetMap()) ) ); // twip rel to doc root
+ Point aFramePixPos( GetMap()->CoreToPixel( aFrameLogBounds ).TopLeft() );
+ aScreenRect.Move( -aFramePixPos.getX(), -aFramePixPos.getY() );
+
+ // convert into AWT Rectangle
+ const awt::Rectangle aCursorRect( aScreenRect.Left(),
+ aScreenRect.Top(),
+ aScreenRect.GetWidth(),
+ aScreenRect.GetHeight() );
+
+ if ( aCharRect.X != aCursorRect.X ||
+ aCharRect.Y != aCursorRect.Y )
+ {
+ --nLineNo;
+ }
+ }
+ }
+ }
+
+ return nLineNo;
+}
+
+// #i108125#
+void SwAccessibleParagraph::Notify(SfxBroadcaster&, const SfxHint&)
+{
+ mpParaChangeTrackInfo->reset();
+}
+
+bool SwAccessibleParagraph::GetSelectionAtIndex(
+ sal_Int32 * pSelection, sal_Int32& nStart, sal_Int32& nEnd)
+{
+ if (pSelection && *pSelection < 0) return false;
+
+ bool bRet = false;
+ nStart = -1;
+ nEnd = -1;
+
+ // get the selection, and test whether it affects our text node
+ SwPaM* pCursor = GetCursor( true );
+ if( pCursor != nullptr )
+ {
+ // get SwPosition for my node
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(GetFrame()));
+ SwNodeOffset nFirstNode(pFrame->GetTextNodeFirst()->GetIndex());
+ SwNodeOffset nLastNode;
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ nLastNode = pMerged->pLastNode->GetIndex();
+ }
+ else
+ {
+ nLastNode = nFirstNode;
+ }
+
+ // iterate over ring
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ // ignore, if no mark
+ if( rTmpCursor.HasMark() )
+ {
+ // check whether frame's node(s) are 'inside' pCursor
+ SwPosition* pStart = rTmpCursor.Start();
+ SwNodeOffset nStartIndex = pStart->nNode.GetIndex();
+ SwPosition* pEnd = rTmpCursor.End();
+ SwNodeOffset nEndIndex = pEnd->nNode.GetIndex();
+ if ((nStartIndex <= nLastNode) && (nFirstNode <= nEndIndex))
+ {
+ if (!pSelection || *pSelection == 0)
+ {
+ // translate start and end positions
+
+ // start position
+ sal_Int32 nLocalStart = -1;
+ if (nStartIndex < nFirstNode)
+ {
+ // selection starts in previous node:
+ // then our local selection starts with the paragraph
+ nLocalStart = 0;
+ }
+ else
+ {
+ assert(FrameContainsNode(*pFrame, nStartIndex));
+
+ // selection starts in this node:
+ // then check whether it's before or inside our part of
+ // the paragraph, and if so, get the proper position
+ const TextFrameIndex nCoreStart =
+ pFrame->MapModelToViewPos(*pStart);
+ if( nCoreStart <
+ GetPortionData().GetFirstValidCorePosition() )
+ {
+ nLocalStart = 0;
+ }
+ else if( nCoreStart <=
+ GetPortionData().GetLastValidCorePosition() )
+ {
+ SAL_WARN_IF(
+ !GetPortionData().IsValidCorePosition(
+ nCoreStart),
+ "sw.a11y",
+ "problem determining valid core position");
+
+ nLocalStart =
+ GetPortionData().GetAccessiblePosition(
+ nCoreStart );
+ }
+ }
+
+ // end position
+ sal_Int32 nLocalEnd = -1;
+ if (nLastNode < nEndIndex)
+ {
+ // selection ends in following node:
+ // then our local selection extends to the end
+ nLocalEnd = GetPortionData().GetAccessibleString().
+ getLength();
+ }
+ else
+ {
+ assert(FrameContainsNode(*pFrame, nEndIndex));
+
+ // selection ends in this node: then select everything
+ // before our part of the node
+ const TextFrameIndex nCoreEnd =
+ pFrame->MapModelToViewPos(*pEnd);
+ if( nCoreEnd >
+ GetPortionData().GetLastValidCorePosition() )
+ {
+ // selection extends beyond out part of this para
+ nLocalEnd = GetPortionData().GetAccessibleString().
+ getLength();
+ }
+ else if( nCoreEnd >=
+ GetPortionData().GetFirstValidCorePosition() )
+ {
+ // selection is inside our part of this para
+ SAL_WARN_IF(
+ !GetPortionData().IsValidCorePosition(
+ nCoreEnd),
+ "sw.a11y",
+ "problem determining valid core position");
+
+ nLocalEnd = GetPortionData().GetAccessiblePosition(
+ nCoreEnd );
+ }
+ }
+
+ if( ( nLocalStart != -1 ) && ( nLocalEnd != -1 ) )
+ {
+ nStart = nLocalStart;
+ nEnd = nLocalEnd;
+ bRet = true;
+ }
+ } // if hit the index
+ else
+ {
+ --*pSelection;
+ }
+ }
+ // else: this PaM doesn't point to this paragraph
+ }
+ // else: this PaM is collapsed and doesn't select anything
+ if(bRet)
+ break;
+ }
+ }
+ // else: nocursor -> no selection
+
+ if (pSelection && bRet)
+ {
+ sal_Int32 nCaretPos = GetCaretPos();
+ if( nStart == nCaretPos )
+ {
+ sal_Int32 tmp = nStart;
+ nStart = nEnd;
+ nEnd = tmp;
+ }
+ }
+ return bRet;
+}
+
+sal_Int16 SAL_CALL SwAccessibleParagraph::getAccessibleRole()
+{
+ SolarMutexGuard g;
+
+ //Get the real heading level, Heading1 ~ Heading10
+ if (m_nHeadingLevel > 0)
+ {
+ return AccessibleRole::HEADING;
+ }
+ else
+ {
+ return AccessibleRole::PARAGRAPH;
+ }
+}
+
+//Get the real heading level, Heading1 ~ Heading10
+sal_Int32 SwAccessibleParagraph::GetRealHeadingLevel()
+{
+ uno::Reference< css::beans::XPropertySet > xPortion = CreateUnoPortion( 0, 0 );
+ uno::Any styleAny = xPortion->getPropertyValue( "ParaStyleName" );
+ OUString sValue;
+ if (styleAny >>= sValue)
+ {
+ sal_Int32 length = sValue.getLength();
+ if (length == 9 || length == 10)
+ {
+ if (sValue.startsWith("Heading"))
+ {
+ std::u16string_view intStr = sValue.subView(8);
+ sal_Int32 headingLevel = o3tl::toInt32(intStr);
+ return headingLevel;
+ }
+ }
+ }
+ return -1;
+}
+
+uno::Any SAL_CALL SwAccessibleParagraph::getExtendedAttributes()
+{
+ SolarMutexGuard g;
+
+ uno::Any Ret;
+ OUString strHeading("heading-level:");
+ if( m_nHeadingLevel >= 0 )
+ strHeading += OUString::number(m_nHeadingLevel);
+ // tdf#84102: expose the same attribute with the name "level"
+ strHeading += ";level:";
+ if( m_nHeadingLevel >= 0 )
+ strHeading += OUString::number(m_nHeadingLevel);
+ strHeading += ";";
+
+ Ret <<= strHeading;
+
+ return Ret;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpara.hxx b/sw/source/core/access/accpara.hxx
new file mode 100644
index 000000000..ade2df47d
--- /dev/null
+++ b/sw/source/core/access/accpara.hxx
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPARA_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPARA_HXX
+
+#include "acccontext.hxx"
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleHypertext.hpp>
+#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
+#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp>
+#include <com/sun/star/accessibility/XAccessibleTextSelection.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedAttributes.hpp>
+#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
+#include "accselectionhelper.hxx"
+#include <unordered_map>
+#include <svl/lstner.hxx>
+
+class SwTextFrame;
+class SwPaM;
+class SwAccessiblePortionData;
+class SwAccessibleHyperTextData;
+class SwRangeRedline;
+class SwXTextPortion;
+class SwParaChangeTrackingInfo; //#i108125#
+
+namespace com::sun::star {
+ namespace i18n { struct Boundary; }
+ namespace accessibility { class XAccessibleHyperlink; }
+ namespace style { struct TabStop; }
+}
+
+typedef std::unordered_map< OUString,
+ css::beans::PropertyValue > tAccParaPropValMap;
+
+class SwAccessibleParagraph :
+ public SfxListener,
+ public SwAccessibleContext,
+ public css::accessibility::XAccessibleEditableText,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleHypertext,
+ public css::accessibility::XAccessibleTextMarkup,
+ public css::accessibility::XAccessibleMultiLineText,
+ public css::accessibility::XAccessibleTextAttributes,
+ public css::accessibility::XAccessibleTextSelection,
+ public css::accessibility::XAccessibleExtendedAttributes
+{
+ friend class SwAccessibleHyperlink;
+
+ OUString m_sDesc; // protected by base classes mutex
+
+ // data for this paragraph's text portions; this contains the
+ // mapping from the core 'model string' to the accessible text
+ // string.
+ // pPortionData may be NULL; it should only be accessed through the
+ // Get/Clear/Has/UpdatePortionData() methods
+ std::unique_ptr<SwAccessiblePortionData> m_pPortionData;
+ std::unique_ptr<SwAccessibleHyperTextData> m_pHyperTextData;
+
+ sal_Int32 m_nOldCaretPos; // The 'old' caret pos. It's only valid as long
+ // as the cursor is inside this object (protected by
+ // mutex)
+
+ bool m_bIsHeading; // protected by base classes mutex
+ sal_Int32 m_nHeadingLevel;
+
+ // implementation for XAccessibleSelection
+ SwAccessibleSelectionHelper m_aSelectionHelper;
+
+ std::unique_ptr<SwParaChangeTrackingInfo> mpParaChangeTrackInfo; // #i108125#
+
+ // XAccessibleComponent
+ bool m_bLastHasSelection;
+
+ /// get the (accessible) text string (requires frame; check before)
+ OUString const & GetString();
+
+ static OUString GetDescription();
+
+ // get the current care position
+ sal_Int32 GetCaretPos();
+
+ // determine the current selection. Fill the values with
+ // -1 if there is no selection in the this paragraph
+ // @param pSelection (optional) check only Nth selection in ring
+ bool GetSelectionAtIndex(sal_Int32 * pSelection, sal_Int32& nStart, sal_Int32& nEnd);
+ bool GetSelection(sal_Int32& nStart, sal_Int32& nEnd) {
+ return GetSelectionAtIndex(nullptr, nStart, nEnd);
+ }
+
+ // helper for GetSelection and getCaretPosition
+ // #i27301# - add parameter <_bForSelection>, which indicates,
+ // if the cursor is retrieved for selection or for caret position.
+ SwPaM* GetCursor( const bool _bForSelection );
+
+ // for cut/copy/paste: execute a particular slot at the view shell
+ void ExecuteAtViewShell( sal_uInt16 nSlot );
+
+ // helper method for get/setAttributes
+ // (for the special case of (nEndIndex==-1) a single character will
+ // be selected)
+ rtl::Reference<SwXTextPortion> CreateUnoPortion( sal_Int32 nStart, sal_Int32 nEnd );
+
+ // methods for checking the parameter range:
+
+ // does nPos point to a char?
+ static bool IsValidChar(sal_Int32 nPos, sal_Int32 nLength);
+
+ // does nPos point to a position? (may be behind the last character)
+ static bool IsValidPosition(sal_Int32 nPos, sal_Int32 nLength);
+
+ // is nBegin...nEnd a valid range? (nEnd points past the last character)
+ static bool IsValidRange(sal_Int32 nBegin, sal_Int32 nEnd, sal_Int32 nLength);
+
+ // Ensure ordered range (i.e. nBegin is smaller then nEnd)
+ static void OrderRange(sal_Int32& nBegin, sal_Int32& nEnd)
+ {
+ if( nBegin > nEnd )
+ {
+ sal_Int32 nTmp = nBegin; nBegin = nEnd; nEnd = nTmp;
+ }
+ }
+
+ const SwRangeRedline* GetRedlineAtIndex();
+ OUString GetFieldTypeNameAtIndex(sal_Int32 nIndex);
+
+ // #i63870#
+ void _getDefaultAttributesImpl(
+ const css::uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rDefAttrSeq,
+ const bool bOnlyCharAttrs = false );
+ void _getRunAttributesImpl(
+ const sal_Int32 nIndex,
+ const css::uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rRunAttrSeq );
+
+ void _getSupplementalAttributesImpl(
+ const css::uno::Sequence< OUString >& aRequestedAttributes,
+ tAccParaPropValMap& rSupplementalAttrSeq );
+
+ void _correctValues(
+ const sal_Int32 nIndex,
+ std::vector< css::beans::PropertyValue >& rValues );
+
+public:
+ bool IsHeading() const;
+
+protected:
+
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets MULTILINE(1), MULTISELECTABLE(+),
+ // FOCUSABLE(+) and FOCUSED(+)
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet ) override;
+
+ virtual void InvalidateContent_( bool bVisibleDataFired ) override;
+
+ virtual void InvalidateCursorPos_() override;
+ virtual void InvalidateFocus_() override;
+
+ virtual ~SwAccessibleParagraph() override;
+
+ // handling of data for the text portions
+
+ // force update of new portion data
+ /// @throws css::uno::RuntimeException
+ void UpdatePortionData();
+
+ // remove the current portion data
+ void ClearPortionData();
+
+ // get portion data; update if necessary
+ /// @throws css::uno::RuntimeException
+ SwAccessiblePortionData& GetPortionData()
+ {
+ if( m_pPortionData == nullptr )
+ UpdatePortionData();
+ return *m_pPortionData;
+ }
+
+ //helpers for word boundaries
+
+ bool GetCharBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos );
+ bool GetWordBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+ bool GetSentenceBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+ bool GetLineBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+ static bool GetParagraphBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText );
+ bool GetAttributeBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos );
+ bool GetGlyphBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos );
+
+ // get boundaries of word/sentence/etc. for specified text type
+ // Does all argument checking, and then delegates to helper methods above.
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool GetTextBoundary( css::i18n::Boundary& rBound,
+ const OUString& rText,
+ sal_Int32 nPos,
+ sal_Int16 aTextType );
+
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+
+public:
+
+ SwAccessibleParagraph( std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTextFrame& rTextFrame );
+
+ inline operator css::accessibility::XAccessibleText *();
+
+ virtual bool HasCursor() override; // required by map to remember that object
+
+ css::uno::Sequence< css::style::TabStop > GetCurrentTabStop( sal_Int32 nIndex );
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+
+ // XAccessibleContext
+
+ // Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // Return the parents locale or throw exception if this object has no
+ // parent yet/anymore.
+ virtual css::lang::Locale SAL_CALL
+ getLocale() override;
+
+ // #i27138# - paragraphs are in relation CONTENT_FLOWS_FROM and/or CONTENT_FLOWS_TO
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleRelationSet> SAL_CALL
+ getAccessibleRelationSet() override;
+
+ // XAccessibleComponent
+
+ virtual void SAL_CALL grabFocus() override;
+ // #i71385#
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ // XServiceInfo
+
+ // Returns an identifier for the implementation of this object.
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ // Return whether the specified service is supported by this class.
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ // Returns a list of all supported services. In this case that is just
+ // the AccessibleContext service.
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XInterface
+
+ // (XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleEditableText).
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleText
+ virtual sal_Int32 SAL_CALL getCaretPosition() override;
+ virtual sal_Bool SAL_CALL setCaretPosition( sal_Int32 nIndex ) override;
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getCharacterCount( ) override;
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& aPoint ) override;
+ virtual OUString SAL_CALL getSelectedText( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionStart() override;
+ virtual sal_Int32 SAL_CALL getSelectionEnd() override;
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual OUString SAL_CALL getText( ) override;
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // XAccessibleEditableText
+ virtual sal_Bool SAL_CALL cutText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL pasteText( sal_Int32 nIndex ) override;
+ virtual sal_Bool SAL_CALL deleteText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL insertText( const OUString& sText, sal_Int32 nIndex ) override;
+ virtual sal_Bool SAL_CALL replaceText( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const OUString& sReplacement ) override;
+ virtual sal_Bool SAL_CALL setAttributes( sal_Int32 nStartIndex, sal_Int32 nEndIndex, const css::uno::Sequence< css::beans::PropertyValue >& aAttributeSet ) override;
+ virtual sal_Bool SAL_CALL setText( const OUString& sText ) override;
+
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int32 nChildIndex ) override;
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+ virtual sal_Int32 SAL_CALL getSelectedAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex ) override;
+
+ // index has to be treated as global child index.
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ // XAccessibleHypertext
+ virtual sal_Int32 SAL_CALL getHyperLinkCount() override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleHyperlink >
+ SAL_CALL getHyperLink( sal_Int32 nLinkIndex ) override;
+ virtual sal_Int32 SAL_CALL getHyperLinkIndex( sal_Int32 nCharIndex ) override;
+
+ // #i71360#
+ // XAccessibleTextMarkup
+ virtual sal_Int32 SAL_CALL getTextMarkupCount( sal_Int32 nTextMarkupType ) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL
+ getTextMarkup( sal_Int32 nTextMarkupIndex,
+ sal_Int32 nTextMarkupType ) override;
+
+ virtual css::uno::Sequence< css::accessibility::TextSegment > SAL_CALL
+ getTextMarkupAtIndex( sal_Int32 nCharIndex,
+ sal_Int32 nTextMarkupType ) override;
+
+ // XAccessibleTextSelection
+ virtual sal_Bool SAL_CALL scrollToPosition( const css::awt::Point& aPoint, sal_Bool isLeftTop ) override;
+ virtual sal_Int32 SAL_CALL getSelectedPortionCount( ) override;
+ virtual sal_Int32 SAL_CALL getSeletedPositionStart( sal_Int32 nSelectedPortionIndex ) override;
+ virtual sal_Int32 SAL_CALL getSeletedPositionEnd( sal_Int32 nSelectedPortionIndex ) override;
+ virtual sal_Bool SAL_CALL removeSelection( sal_Int32 selectionIndex ) override;
+ virtual sal_Int32 SAL_CALL addSelection( sal_Int32 selectionIndex, sal_Int32 startOffset, sal_Int32 endOffset) override;
+ // XAccessibleExtendedAttributes
+ virtual css::uno::Any SAL_CALL getExtendedAttributes() override ;
+ sal_Int32 GetRealHeadingLevel();
+
+ // #i89175#
+ // XAccessibleMultiLineText
+ virtual sal_Int32 SAL_CALL getLineNumberAtIndex( sal_Int32 nIndex ) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL
+ getTextAtLineNumber( sal_Int32 nLineNo ) override;
+
+ virtual css::accessibility::TextSegment SAL_CALL
+ getTextAtLineWithCaret() override;
+
+ virtual sal_Int32 SAL_CALL getNumberOfLineWithCaret() override;
+
+ // #i63870#
+ // XAccessibleTextAttributes
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getDefaultAttributes( const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getRunAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+};
+
+inline SwAccessibleParagraph::operator css::accessibility::XAccessibleText *()
+{
+ return static_cast< css::accessibility::XAccessibleEditableText * >( this );
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accportions.cxx b/sw/source/core/access/accportions.cxx
new file mode 100644
index 000000000..186e61124
--- /dev/null
+++ b/sw/source/core/access/accportions.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include "accportions.hxx"
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <txttypes.hxx>
+
+// for portion replacement in Special()
+#include <viewopt.hxx>
+
+// for GetWordBoundary(...), GetSentenceBoundary(...):
+#include <breakit.hxx>
+#include <txtfrm.hxx>
+
+// for FillSpecialPos(...)
+#include <crstate.hxx>
+
+using namespace ::com::sun::star;
+
+using i18n::Boundary;
+
+// 'portion type' for terminating portions
+#define POR_TERMINATE PortionType::NONE
+
+// portion attributes
+#define PORATTR_SPECIAL 1
+#define PORATTR_READONLY 2
+#define PORATTR_GRAY 4
+#define PORATTR_TERM 128
+
+/// returns the index of the first position whose value is smaller
+/// or equal, and whose following value is equal or larger
+template<typename T>
+static size_t FindBreak(const std::vector<T>& rPositions, T nValue);
+
+/// like FindBreak, but finds the last equal or larger position
+template<typename T>
+static size_t FindLastBreak(const std::vector<T>& rPositions, T nValue);
+
+
+SwAccessiblePortionData::SwAccessiblePortionData(
+ const SwTextFrame *const pTextFrame,
+ const SwViewOption* pViewOpt ) :
+ m_pTextFrame(pTextFrame),
+ m_nViewPosition( 0 ),
+ m_pViewOptions( pViewOpt ),
+ m_nBeforePortions( 0 ),
+ m_bFinished( false )
+{
+ OSL_ENSURE( m_pTextFrame != nullptr, "Need SwTextFrame!" );
+
+ // reserve some space to reduce memory allocations
+ m_aLineBreaks.reserve( 5 );
+ m_ViewPositions.reserve( 10 );
+ m_aAccessiblePositions.reserve( 10 );
+
+ // always include 'first' line-break position
+ m_aLineBreaks.push_back( 0 );
+}
+
+SwAccessiblePortionData::~SwAccessiblePortionData()
+{
+}
+
+void SwAccessiblePortionData::Text(TextFrameIndex const nLength,
+ PortionType nType, sal_Int32 /*nHeight*/, sal_Int32 /*nWidth*/)
+{
+ OSL_ENSURE((m_nViewPosition + nLength) <= TextFrameIndex(m_pTextFrame->GetText().getLength()),
+ "portion exceeds model string!" );
+
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ // ignore zero-length portions
+ if (nLength == TextFrameIndex(0))
+ return;
+
+ // store 'old' positions
+ m_ViewPositions.push_back( m_nViewPosition );
+ m_aAccessiblePositions.push_back( m_aBuffer.getLength() );
+
+ // store portion attributes
+ sal_uInt8 nAttr = IsGrayPortionType(nType) ? PORATTR_GRAY : 0;
+ m_aPortionAttrs.push_back( nAttr );
+
+ // update buffer + nViewPosition
+ m_aBuffer.append(m_pTextFrame->GetText().subView(sal_Int32(m_nViewPosition), sal_Int32(nLength)));
+ m_nViewPosition += nLength;
+}
+
+void SwAccessiblePortionData::Special(
+ TextFrameIndex const nLength, const OUString& rText, PortionType nType,
+ sal_Int32 /*nHeight*/, sal_Int32 /*nWidth*/, const SwFont* /*pFont*/)
+{
+ OSL_ENSURE(m_nViewPosition >= TextFrameIndex(0), "illegal position");
+ OSL_ENSURE((m_nViewPosition + nLength) <= TextFrameIndex(m_pTextFrame->GetText().getLength()),
+ "portion exceeds model string!" );
+
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ // construct string with representation; either directly from
+ // rText, or use resources for special case portions
+ OUString sDisplay;
+ switch( nType )
+ {
+ case PortionType::PostIts:
+ case PortionType::FlyCnt:
+ sDisplay = OUString(u'\xfffc');
+ break;
+ case PortionType::Field:
+ case PortionType::Hidden:
+ case PortionType::Combined:
+ case PortionType::IsoRef:
+ // When the field content is empty, input a special character.
+ if (rText.isEmpty())
+ sDisplay = OUString(u'\xfffc');
+ else
+ sDisplay = rText;
+ m_aFieldPosition.push_back(m_aBuffer.getLength());
+ m_aFieldPosition.push_back(m_aBuffer.getLength() + rText.getLength());
+ break;
+ case PortionType::FootnoteNum:
+ break;
+ case PortionType::Footnote:
+ {
+ sDisplay = rText;
+ sal_Int32 nStart=m_aBuffer.getLength();
+ sal_Int32 nEnd=nStart + rText.getLength();
+ m_vecPairPos.emplace_back(nStart,nEnd);
+ break;
+ }
+ break;
+ case PortionType::Number:
+ case PortionType::Bullet:
+ sDisplay = rText + " ";
+ break;
+ // There should probably be some special treatment to graphical bullets
+ case PortionType::GrfNum:
+ break;
+ // #i111768# - apply patch from kstribley:
+ // Include the control characters.
+ case PortionType::ControlChar:
+ sDisplay = rText + OUStringChar(m_pTextFrame->GetText()[sal_Int32(m_nViewPosition)]);
+ break;
+ case PortionType::Bookmark:
+ // TODO
+ break;
+ default:
+ sDisplay = rText;
+ break;
+ }
+
+ // ignore zero/zero portions (except for terminators)
+ if ((nLength == TextFrameIndex(0)) && (sDisplay.getLength() == 0) && (nType != POR_TERMINATE))
+ return;
+
+ // special treatment for zero length portion at the beginning:
+ // count as 'before' portion
+ if ((nLength == TextFrameIndex(0)) && (m_nViewPosition == TextFrameIndex(0)))
+ m_nBeforePortions++;
+
+ // store the 'old' positions
+ m_ViewPositions.push_back( m_nViewPosition );
+ m_aAccessiblePositions.push_back( m_aBuffer.getLength() );
+
+ // store portion attributes
+ sal_uInt8 nAttr = PORATTR_SPECIAL;
+ if( IsGrayPortionType(nType) ) nAttr |= PORATTR_GRAY;
+ if (nLength == TextFrameIndex(0)) nAttr |= PORATTR_READONLY;
+ if( nType == POR_TERMINATE ) nAttr |= PORATTR_TERM;
+ m_aPortionAttrs.push_back( nAttr );
+
+ // update buffer + nViewPosition
+ m_aBuffer.append( sDisplay );
+ m_nViewPosition += nLength;
+}
+
+void SwAccessiblePortionData::LineBreak(sal_Int32 /*nWidth*/)
+{
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ m_aLineBreaks.push_back( m_aBuffer.getLength() );
+}
+
+void SwAccessiblePortionData::Skip(TextFrameIndex const nLength)
+{
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+ OSL_ENSURE( m_ViewPositions.empty(), "Never Skip() after portions" );
+ OSL_ENSURE(nLength <= TextFrameIndex(m_pTextFrame->GetText().getLength()),
+ "skip exceeds model string!" );
+
+ m_nViewPosition += nLength;
+}
+
+void SwAccessiblePortionData::Finish()
+{
+ OSL_ENSURE( !m_bFinished, "We are already done!" );
+
+ // include terminator values: always include two 'last character'
+ // markers in the position arrays to make sure we always find one
+ // position before the end
+ Special( TextFrameIndex(0), OUString(), POR_TERMINATE );
+ Special( TextFrameIndex(0), OUString(), POR_TERMINATE );
+ LineBreak(0);
+ LineBreak(0);
+
+ m_sAccessibleString = m_aBuffer.makeStringAndClear();
+ m_bFinished = true;
+}
+
+bool SwAccessiblePortionData::IsPortionAttrSet(
+ size_t nPortionNo, sal_uInt8 nAttr ) const
+{
+ OSL_ENSURE( nPortionNo < m_aPortionAttrs.size(),
+ "Illegal portion number" );
+ return (m_aPortionAttrs[nPortionNo] & nAttr) != 0;
+}
+
+bool SwAccessiblePortionData::IsSpecialPortion( size_t nPortionNo ) const
+{
+ return IsPortionAttrSet(nPortionNo, PORATTR_SPECIAL);
+}
+
+bool SwAccessiblePortionData::IsGrayPortionType( PortionType nType ) const
+{
+ // gray portions?
+ // Compare with: inftxt.cxx, SwTextPaintInfo::DrawViewOpt(...)
+ bool bGray = false;
+ switch( nType )
+ {
+ case PortionType::Footnote:
+ case PortionType::IsoRef:
+ case PortionType::Ref:
+ case PortionType::QuoVadis:
+ case PortionType::Number:
+ case PortionType::Field:
+ case PortionType::InputField:
+ case PortionType::IsoTox:
+ case PortionType::Tox:
+ case PortionType::Hidden:
+ bGray = !m_pViewOptions->IsPagePreview() &&
+ !m_pViewOptions->IsReadonly() && SwViewOption::IsFieldShadings();
+ break;
+ case PortionType::Tab: bGray = m_pViewOptions->IsTab(); break;
+ case PortionType::SoftHyphen: bGray = m_pViewOptions->IsSoftHyph(); break;
+ case PortionType::Blank: bGray = m_pViewOptions->IsHardBlank(); break;
+ default:
+ break; // bGray is false
+ }
+ return bGray;
+}
+
+const OUString& SwAccessiblePortionData::GetAccessibleString() const
+{
+ OSL_ENSURE( m_bFinished, "Shouldn't call this before we are done!" );
+
+ return m_sAccessibleString;
+}
+
+void SwAccessiblePortionData::GetLineBoundary(
+ Boundary& rBound,
+ sal_Int32 nPos ) const
+{
+ FillBoundary( rBound, m_aLineBreaks,
+ FindBreak( m_aLineBreaks, nPos ) );
+}
+
+// #i89175#
+sal_Int32 SwAccessiblePortionData::GetLineCount() const
+{
+ size_t nBreaks = m_aLineBreaks.size();
+ // A non-empty paragraph has at least 4 breaks: one for each line3 and
+ // 3 additional ones.
+ // An empty paragraph has 3 breaks.
+ // Less than 3 breaks is an error case.
+ sal_Int32 nLineCount = ( nBreaks > 3 )
+ ? nBreaks - 3
+ : ( ( nBreaks == 3 ) ? 1 : 0 );
+ return nLineCount;
+}
+
+sal_Int32 SwAccessiblePortionData::GetLineNo( const sal_Int32 nPos ) const
+{
+ sal_Int32 nLineNo = FindBreak( m_aLineBreaks, nPos );
+
+ // handling of position after last character
+ const sal_Int32 nLineCount( GetLineCount() );
+ if ( nLineNo >= nLineCount )
+ {
+ nLineNo = nLineCount - 1;
+ }
+
+ return nLineNo;
+}
+
+void SwAccessiblePortionData::GetBoundaryOfLine( const sal_Int32 nLineNo,
+ i18n::Boundary& rLineBound )
+{
+ FillBoundary( rLineBound, m_aLineBreaks, nLineNo );
+}
+
+void SwAccessiblePortionData::GetLastLineBoundary(
+ Boundary& rBound ) const
+{
+ OSL_ENSURE( m_aLineBreaks.size() >= 2, "need min + max value" );
+
+ // The last two positions except the two delimiters are the ones
+ // we are looking for, except for empty paragraphs (nBreaks==3)
+ size_t nBreaks = m_aLineBreaks.size();
+ FillBoundary( rBound, m_aLineBreaks, nBreaks <= 3 ? 0 : nBreaks-4 );
+}
+
+TextFrameIndex SwAccessiblePortionData::GetCoreViewPosition(sal_Int32 const nPos) const
+{
+ OSL_ENSURE( nPos >= 0, "illegal position" );
+ OSL_ENSURE( nPos <= m_sAccessibleString.getLength(), "illegal position" );
+
+ // find the portion number
+ size_t nPortionNo = FindBreak( m_aAccessiblePositions, nPos );
+
+ // get core view portion size
+ TextFrameIndex nStartPos = m_ViewPositions[nPortionNo];
+
+ // if it's a non-special portion, move into the portion, else
+ // return the portion start
+ if( ! IsSpecialPortion( nPortionNo ) )
+ {
+ // text portions have to be of the same width
+ OSL_ENSURE( sal_Int32(m_ViewPositions[nPortionNo+1] - nStartPos) ==
+ ( m_aAccessiblePositions[nPortionNo+1] -
+ m_aAccessiblePositions[nPortionNo] ),
+ "accessibility portion disagrees with text model" );
+
+ nStartPos += TextFrameIndex(nPos - m_aAccessiblePositions[nPortionNo]);
+ }
+ // else: return nStartPos unmodified
+
+ OSL_ENSURE(nStartPos >= TextFrameIndex(0), "There's something weird in number of characters of SwTextFrame");
+ return nStartPos;
+}
+
+void SwAccessiblePortionData::FillBoundary(
+ Boundary& rBound,
+ const AccessiblePositions& rPositions,
+ size_t nPos )
+{
+ rBound.startPos = rPositions[nPos];
+ rBound.endPos = rPositions[nPos+1];
+}
+
+template<typename T>
+static size_t FindBreak(const std::vector<T>& rPositions, T const nValue)
+{
+ OSL_ENSURE( rPositions.size() >= 2, "need min + max value" );
+ OSL_ENSURE( rPositions[0] <= nValue, "need min value" );
+ OSL_ENSURE( rPositions[rPositions.size()-1] >= nValue,
+ "need first terminator value" );
+ OSL_ENSURE( rPositions[rPositions.size()-2] >= nValue,
+ "need second terminator value" );
+
+ size_t nMin = 0;
+ size_t nMax = rPositions.size()-2;
+
+ // loop until no more than two candidates are left
+ while( nMin+1 < nMax )
+ {
+ // check loop invariants
+ OSL_ENSURE( ( (nMin == 0) && (rPositions[nMin] <= nValue) ) ||
+ ( (nMin != 0) && (rPositions[nMin] < nValue) ),
+ "minvalue not minimal" );
+ OSL_ENSURE( nValue <= rPositions[nMax], "max value not maximal" );
+
+ // get middle (and ensure progress)
+ size_t nMiddle = (nMin + nMax)/2;
+ OSL_ENSURE( nMin < nMiddle, "progress?" );
+ OSL_ENSURE( nMiddle < nMax, "progress?" );
+
+ // check array
+ OSL_ENSURE( rPositions[nMin] <= rPositions[nMiddle],
+ "garbled positions array" );
+ OSL_ENSURE( rPositions[nMiddle] <= rPositions[nMax],
+ "garbled positions array" );
+
+ if( nValue > rPositions[nMiddle] )
+ nMin = nMiddle;
+ else
+ nMax = nMiddle;
+ }
+
+ // only two are left; we only need to check which one is the winner
+ OSL_ENSURE( (nMax == nMin) || (nMax == nMin+1), "only two left" );
+ if( (rPositions[nMin] < nValue) && (rPositions[nMin+1] <= nValue) )
+ nMin = nMin+1;
+
+ // finally, check to see whether the returned value is the 'right' position
+ OSL_ENSURE( rPositions[nMin] <= nValue, "not smaller or equal" );
+ OSL_ENSURE( nValue <= rPositions[nMin+1], "not equal or larger" );
+ OSL_ENSURE( (nMin == 0) || (rPositions[nMin-1] <= nValue),
+ "earlier value should have been returned" );
+
+ OSL_ENSURE( nMin < rPositions.size()-1,
+ "shouldn't return last position (due to terminator values)" );
+
+ return nMin;
+}
+
+template<typename T>
+static size_t FindLastBreak(const std::vector<T>& rPositions, T const nValue)
+{
+ size_t nResult = FindBreak( rPositions, nValue );
+
+ // skip 'zero-length' portions
+ // #i70538# consider size of <rPosition> and ignore last entry
+ while ( nResult < rPositions.size() - 2 &&
+ rPositions[nResult+1] <= nValue )
+ {
+ nResult++;
+ }
+
+ return nResult;
+}
+
+void SwAccessiblePortionData::GetSentenceBoundary(
+ Boundary& rBound,
+ sal_Int32 nPos )
+{
+ OSL_ENSURE( nPos >= 0, "illegal position; check before" );
+ OSL_ENSURE( nPos < m_sAccessibleString.getLength(), "illegal position" );
+
+ if( m_pSentences == nullptr )
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ m_pSentences.reset(new AccessiblePositions);
+ m_pSentences->reserve(10);
+
+ // use xBreak->endOfSentence to iterate over all words; store
+ // positions in pSentences
+ sal_Int32 nCurrent = 0;
+ sal_Int32 nLength = m_sAccessibleString.getLength();
+ do
+ {
+ m_pSentences->push_back( nCurrent );
+
+ const TextFrameIndex nFramePos = GetCoreViewPosition(nCurrent);
+
+ sal_Int32 nNew = g_pBreakIt->GetBreakIter()->endOfSentence(
+ m_sAccessibleString, nCurrent,
+ g_pBreakIt->GetLocale(m_pTextFrame->GetLangOfChar(nFramePos, 0, true))) + 1;
+
+ if( (nNew < 0) && (nNew > nLength) )
+ nNew = nLength;
+ else if (nNew <= nCurrent)
+ nNew = nCurrent + 1; // ensure forward progress
+
+ nCurrent = nNew;
+ }
+ while (nCurrent < nLength);
+
+ // finish with two terminators
+ m_pSentences->push_back( nLength );
+ m_pSentences->push_back( nLength );
+ }
+
+ FillBoundary( rBound, *m_pSentences, FindBreak( *m_pSentences, nPos ) );
+}
+
+void SwAccessiblePortionData::GetAttributeBoundary(
+ Boundary& rBound,
+ sal_Int32 nPos) const
+{
+ OSL_ENSURE( m_pTextFrame != nullptr, "Need SwTextNode!" );
+
+ // attribute boundaries can only occur on portion boundaries
+ FillBoundary( rBound, m_aAccessiblePositions,
+ FindBreak( m_aAccessiblePositions, nPos ) );
+}
+
+sal_Int32 SwAccessiblePortionData::GetAccessiblePosition(TextFrameIndex const nPos) const
+{
+ OSL_ENSURE(nPos <= TextFrameIndex(m_pTextFrame->GetText().getLength()), "illegal position");
+
+ // find the portion number
+ // #i70538# - consider "empty" model portions - e.g. number portion
+ size_t nPortionNo = FindLastBreak( m_ViewPositions, nPos );
+
+ sal_Int32 nRet = m_aAccessiblePositions[nPortionNo];
+
+ // if the view portion has more than one position, go into it;
+ // else return that position
+ TextFrameIndex nStartPos = m_ViewPositions[nPortionNo];
+ TextFrameIndex nEndPos = m_ViewPositions[nPortionNo+1];
+ if (!IsSpecialPortion(nPortionNo))
+ {
+ // text portions have to be of the same width
+ OSL_ENSURE( sal_Int32(nEndPos - nStartPos) ==
+ ( m_aAccessiblePositions[nPortionNo+1] -
+ m_aAccessiblePositions[nPortionNo] ),
+ "accessibility portion disagrees with text model" );
+
+ TextFrameIndex nWithinPortion = nPos - m_ViewPositions[nPortionNo];
+ nRet += sal_Int32(nWithinPortion);
+ }
+ // else: return nRet unmodified
+
+ OSL_ENSURE( (nRet >= 0) && (nRet <= m_sAccessibleString.getLength()),
+ "too long!" );
+ return nRet;
+}
+
+TextFrameIndex SwAccessiblePortionData::FillSpecialPos(
+ sal_Int32 nPos,
+ SwSpecialPos& rPos,
+ SwSpecialPos*& rpPos ) const
+{
+ size_t nPortionNo = FindLastBreak( m_aAccessiblePositions, nPos );
+
+ SwSPExtendRange nExtend(SwSPExtendRange::NONE);
+ sal_Int32 nRefPos(0);
+ TextFrameIndex nCorePos(0);
+
+ if( nPortionNo < m_nBeforePortions )
+ {
+ nExtend = SwSPExtendRange::BEFORE;
+ rpPos = &rPos;
+ }
+ else
+ {
+ TextFrameIndex nCoreEndPos = m_ViewPositions[nPortionNo+1];
+ nCorePos = m_ViewPositions[nPortionNo];
+
+ // skip backwards over zero-length portions, since GetCharRect()
+ // counts all model-zero-length portions as belonging to the
+ // previous portion
+ size_t nCorePortionNo = nPortionNo;
+ while (nCorePos == nCoreEndPos)
+ {
+ nCorePortionNo--;
+ nCoreEndPos = nCorePos;
+ nCorePos = m_ViewPositions[nCorePortionNo];
+
+ OSL_ENSURE( nCorePos >= TextFrameIndex(0), "Can't happen." );
+ OSL_ENSURE( nCorePortionNo >= m_nBeforePortions, "Can't happen." );
+ }
+ OSL_ENSURE( nCorePos != nCoreEndPos,
+ "portion with core-representation expected" );
+
+ // if we have anything except plain text, compute nExtend + nRefPos
+ if (IsSpecialPortion(nCorePortionNo))
+ {
+ // case 1: a non-text portion
+ // reference position is the first accessibility for our
+ // core portion
+ nRefPos = m_aAccessiblePositions[ nCorePortionNo ];
+ nExtend = SwSPExtendRange::NONE;
+ rpPos = &rPos;
+ }
+ else if(nPortionNo != nCorePortionNo)
+ {
+ // case 2: a multi-character (text!) portion, followed by
+ // zero-length portions
+ // reference position is the first character of the next
+ // portion, and we are 'behind'
+ nRefPos = m_aAccessiblePositions[ nCorePortionNo+1 ];
+ nExtend = SwSPExtendRange::BEHIND;
+ rpPos = &rPos;
+ }
+ else
+ {
+ // case 3: regular text portion
+ OSL_ENSURE( sal_Int32(nCoreEndPos - nCorePos) ==
+ ( m_aAccessiblePositions[nPortionNo+1] -
+ m_aAccessiblePositions[nPortionNo] ),
+ "text portion expected" );
+
+ nCorePos += TextFrameIndex(nPos - m_aAccessiblePositions[nPortionNo]);
+ rpPos = nullptr;
+ }
+ }
+ if( rpPos != nullptr )
+ {
+ OSL_ENSURE( rpPos == &rPos, "Yes!" );
+ OSL_ENSURE( nRefPos <= nPos, "wrong reference" );
+
+ // get the line number, and adjust nRefPos for the line
+ // (if necessary)
+ size_t nRefLine = FindBreak( m_aLineBreaks, nRefPos );
+ size_t nMyLine = FindBreak( m_aLineBreaks, nPos );
+ sal_uInt16 nLineOffset = o3tl::narrowing<sal_uInt16>( nMyLine - nRefLine );
+ if( nLineOffset != 0 )
+ nRefPos = m_aLineBreaks[ nMyLine ];
+
+ // fill char offset and 'special position'
+ rPos.nCharOfst = nPos - nRefPos;
+ rPos.nExtendRange = nExtend;
+ rPos.nLineOfst = nLineOffset;
+ }
+
+ return nCorePos;
+}
+
+bool SwAccessiblePortionData::FillBoundaryIFDateField( css::i18n::Boundary& rBound, const sal_Int32 nPos )
+{
+ if( m_aFieldPosition.size() < 2 )
+ return false;
+ for( size_t i = 0; i < m_aFieldPosition.size() - 1; i += 2 )
+ {
+ if( nPos < m_aFieldPosition[ i + 1 ] && nPos >= m_aFieldPosition[ i ] )
+ {
+ rBound.startPos = m_aFieldPosition[i];
+ rBound.endPos = m_aFieldPosition[i + 1];
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwAccessiblePortionData::AdjustAndCheck(
+ sal_Int32 nPos,
+ size_t& nPortionNo,
+ TextFrameIndex& rCorePos,
+ bool& bEdit) const
+{
+ // find portion and get mode position
+ nPortionNo = FindBreak( m_aAccessiblePositions, nPos );
+ rCorePos = m_ViewPositions[ nPortionNo ];
+
+ // for special portions, make sure we're on a portion boundary
+ // for text portions, add the in-portion offset
+ if( IsSpecialPortion( nPortionNo ) )
+ bEdit &= nPos == m_aAccessiblePositions[nPortionNo];
+ else
+ rCorePos += TextFrameIndex(nPos - m_aAccessiblePositions[nPortionNo]);
+}
+
+bool SwAccessiblePortionData::GetEditableRange(
+ sal_Int32 nStart, sal_Int32 nEnd,
+ TextFrameIndex& rCoreStart, TextFrameIndex& rCoreEnd) const
+{
+ bool bIsEditable = true;
+
+ // get start and end portions
+ size_t nStartPortion, nEndPortion;
+ AdjustAndCheck( nStart, nStartPortion, rCoreStart, bIsEditable );
+ AdjustAndCheck( nEnd, nEndPortion, rCoreEnd, bIsEditable );
+
+ // iterate over portions, and make sure there is no read-only portion
+ // in-between
+ size_t nLastPortion = nEndPortion;
+
+ // don't count last portion if we're in front of a special portion
+ if( IsSpecialPortion(nLastPortion) )
+ {
+ if (nLastPortion > 0)
+ nLastPortion--;
+ else
+ // special case: because size_t is usually unsigned, we can't just
+ // decrease nLastPortion to -1 (which would normally do the job, so
+ // this whole if wouldn't be needed). Instead, we'll do this
+ // special case and just increase the start portion beyond the last
+ // portion to make sure the loop below will have zero iteration.
+ nStartPortion = nLastPortion + 1;
+ }
+
+ for( size_t nPor = nStartPortion; nPor <= nLastPortion; nPor++ )
+ {
+ bIsEditable &= ! IsPortionAttrSet(nPor, PORATTR_READONLY);
+ }
+
+ return bIsEditable;
+}
+
+bool SwAccessiblePortionData::IsValidCorePosition(TextFrameIndex const nPos) const
+{
+ // a position is valid if it's within the core view positions that we know
+ return (m_ViewPositions[0] <= nPos) && (nPos <= m_ViewPositions.back());
+}
+
+bool SwAccessiblePortionData::IsZeroCorePositionData()
+{
+ if (m_ViewPositions.empty()) return true;
+ return m_ViewPositions[0] == TextFrameIndex(0)
+ && m_ViewPositions.back() == TextFrameIndex(0);
+}
+
+bool SwAccessiblePortionData::IsIndexInFootnode(sal_Int32 nIndex)
+{
+ for (const auto & pairPos : m_vecPairPos)
+ {
+ if(nIndex >= pairPos.first && nIndex < pairPos.second )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwAccessiblePortionData::IsInGrayPortion( sal_Int32 nPos )
+{
+// return IsGrayPortion( FindBreak( aAccessiblePositions, nPos ) );
+ return IsPortionAttrSet( FindBreak( m_aAccessiblePositions, nPos ),
+ PORATTR_GRAY );
+}
+
+sal_Int32 SwAccessiblePortionData::GetFieldIndex(sal_Int32 nPos) const
+{
+ sal_Int32 nIndex = -1;
+ if( m_aFieldPosition.size() >= 2 )
+ {
+ for( size_t i = 0; i < m_aFieldPosition.size() - 1; i += 2 )
+ {
+ if( nPos <= m_aFieldPosition[ i + 1 ] && nPos >= m_aFieldPosition[ i ] )
+ {
+ nIndex = i/2;
+ break;
+ }
+ }
+ }
+ return nIndex;
+}
+
+TextFrameIndex SwAccessiblePortionData::GetFirstValidCorePosition() const
+{
+ return m_ViewPositions[0];
+}
+
+TextFrameIndex SwAccessiblePortionData::GetLastValidCorePosition() const
+{
+ return m_ViewPositions.back();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accportions.hxx b/sw/source/core/access/accportions.hxx
new file mode 100644
index 000000000..5598699a6
--- /dev/null
+++ b/sw/source/core/access/accportions.hxx
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPORTIONS_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPORTIONS_HXX
+
+#include <SwPortionHandler.hxx>
+#include <sal/types.h>
+#include <rtl/ustrbuf.hxx>
+#include <memory>
+#include <vector>
+
+class SwTextFrame;
+struct SwSpecialPos;
+class SwViewOption;
+namespace com::sun::star {
+ namespace i18n { struct Boundary; }
+}
+
+/**
+ * collect text portion data from the layout through SwPortionHandler interface
+ */
+class SwAccessiblePortionData : public SwPortionHandler
+{
+ // the frame this portion is referring to
+ SwTextFrame const* m_pTextFrame;
+
+ // variables used while collecting the data
+ OUStringBuffer m_aBuffer;
+ TextFrameIndex m_nViewPosition;
+ const SwViewOption* m_pViewOptions;
+
+ /// the accessible string
+ /// note that the content is different both from the string in the text
+ /// node(s) as well as the string in the text frame, so there are 3
+ /// different index spaces involved.
+ OUString m_sAccessibleString;
+
+ // positions array
+ // instances of Position_t must always include the minimum and
+ // maximum positions as first/last elements (to simplify the
+ // algorithms)
+ typedef std::vector<sal_Int32> AccessiblePositions;
+ typedef std::vector<TextFrameIndex> FramePositions;
+
+ AccessiblePositions m_aLineBreaks; /// position of line breaks
+ FramePositions m_ViewPositions; /// position of portion breaks in the core view
+ AccessiblePositions m_aAccessiblePositions; /// portion breaks in m_sAccessibleString
+ AccessiblePositions m_aFieldPosition;
+
+ std::vector<sal_uInt8> m_aPortionAttrs; /// additional portion attributes
+
+ std::unique_ptr<AccessiblePositions> m_pSentences; /// positions of sentence breaks
+
+ size_t m_nBeforePortions; /// # of portions before first core character
+ bool m_bFinished;
+
+ /// fill the boundary with the values from rPositions[nPos]
+ static void FillBoundary(css::i18n::Boundary& rBound,
+ const AccessiblePositions& rPositions,
+ size_t nPos );
+
+ /// Access to portion attributes
+ bool IsPortionAttrSet( size_t nPortionNo, sal_uInt8 nAttr ) const;
+ bool IsSpecialPortion( size_t nPortionNo ) const;
+ bool IsGrayPortionType( PortionType nType ) const;
+
+ // helper method for GetEditableRange(...):
+ void AdjustAndCheck( sal_Int32 nPos, size_t& nPortionNo,
+ TextFrameIndex& rCorePos, bool& bEdit) const;
+
+public:
+ SwAccessiblePortionData( const SwTextFrame* pTextFrame,
+ const SwViewOption* pViewOpt );
+ virtual ~SwAccessiblePortionData() override;
+
+ // SwPortionHandler methods
+ virtual void Text(TextFrameIndex nLength, PortionType nType, sal_Int32 nHeight = 0, sal_Int32 nWidth = 0) override;
+ virtual void Special(TextFrameIndex nLength, const OUString& rText, PortionType nType, sal_Int32 nHeight = 0, sal_Int32 nWidth = 0, const SwFont* pFont = nullptr) override;
+ virtual void LineBreak(sal_Int32 nWidth) override;
+ virtual void Skip(TextFrameIndex nLength) override;
+ virtual void Finish() override;
+
+ bool FillBoundaryIFDateField( css::i18n::Boundary& rBound, const sal_Int32 nPos );
+ bool IsIndexInFootnode(sal_Int32 nIndex);
+ bool IsInGrayPortion( sal_Int32 nPos );
+ sal_Int32 GetFieldIndex(sal_Int32 nPos) const;
+
+ bool IsZeroCorePositionData();
+
+ // access to the portion data
+
+ /// get the text string, as presented by the layout
+ const OUString& GetAccessibleString() const;
+
+ /// get the start & end positions of the sentence
+ void GetLineBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos ) const;
+
+ // get start and end position of the last line
+ void GetLastLineBoundary( css::i18n::Boundary& rBound ) const;
+
+ /// Determine whether this core position is valid for these portions.
+ /// (A paragraph may be split into several frames, e.g. at page
+ /// boundaries. In this case, only part of a paragraph is represented
+ /// through this object. This method determines whether one particular
+ /// position is valid for this object or not.)
+ bool IsValidCorePosition(TextFrameIndex nPos) const;
+ TextFrameIndex GetFirstValidCorePosition() const;
+ TextFrameIndex GetLastValidCorePosition() const;
+
+ /// get the position in the accessibility string for a given view position
+ sal_Int32 GetAccessiblePosition(TextFrameIndex nPos) const;
+
+ // #i89175#
+ sal_Int32 GetLineCount() const;
+ sal_Int32 GetLineNo( const sal_Int32 nPos ) const;
+ void GetBoundaryOfLine( const sal_Int32 nLineNo,
+ css::i18n::Boundary& rLineBound );
+
+ /// get the position in the core view string for a given
+ /// (accessibility) position
+ TextFrameIndex GetCoreViewPosition(sal_Int32 nPos) const;
+
+ /// fill a SwSpecialPos structure, suitable for calling
+ /// SwTextFrame->GetCharRect
+ /// Returns the core position, and fills rpPos either with NULL or
+ /// with the &rPos, after putting the appropriate data into it.
+ TextFrameIndex FillSpecialPos(sal_Int32 nPos,
+ SwSpecialPos& rPos,
+ SwSpecialPos*& rpPos ) const;
+
+ // get boundaries of words/sentences. The data structures are
+ // created on-demand.
+ void GetSentenceBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos );
+
+ // get (a) boundary for attribute change
+ void GetAttributeBoundary( css::i18n::Boundary& rBound,
+ sal_Int32 nPos ) const;
+
+ /// Convert start and end positions into core positions.
+ /// @returns true if 'special' portions are included either completely
+ /// or not at all. This can be used to test whether editing
+ /// that range would be legal
+ bool GetEditableRange( sal_Int32 nStart, sal_Int32 nEnd,
+ TextFrameIndex& rCoreStart, TextFrameIndex& rCoreEnd) const;
+
+private:
+ std::vector< std::pair<sal_Int32,sal_Int32> > m_vecPairPos;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpreview.cxx b/sw/source/core/access/accpreview.cxx
new file mode 100644
index 000000000..503cc543e
--- /dev/null
+++ b/sw/source/core/access/accpreview.cxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <strings.hrc>
+#include "accpreview.hxx"
+
+constexpr OUStringLiteral sImplementationName
+ = u"com.sun.star.comp.Writer.SwAccessibleDocumentPageView";
+
+using ::com::sun::star::uno::Sequence;
+
+SwAccessiblePreview::SwAccessiblePreview(std::shared_ptr<SwAccessibleMap> const& pMap)
+ : SwAccessibleDocumentBase(pMap)
+{
+ SetName( GetResource( STR_ACCESS_PREVIEW_DOC_NAME ) );
+}
+
+SwAccessiblePreview::~SwAccessiblePreview()
+{
+}
+
+OUString SwAccessiblePreview::getImplementationName( )
+{
+ return sImplementationName;
+}
+
+sal_Bool SwAccessiblePreview::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence<OUString> SwAccessiblePreview::getSupportedServiceNames( )
+{
+ return {"com.sun.star.text.AccessibleTextDocumentPageView",
+ sAccessibleServiceName};
+}
+
+Sequence< sal_Int8 > SAL_CALL SwAccessiblePreview::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+OUString SAL_CALL SwAccessiblePreview::getAccessibleDescription()
+{
+ return GetResource( STR_ACCESS_PREVIEW_DOC_NAME );
+}
+
+OUString SAL_CALL SwAccessiblePreview::getAccessibleName()
+{
+ return SwAccessibleDocumentBase::getAccessibleName() + " " + GetResource( STR_ACCESS_PREVIEW_DOC_SUFFIX );
+}
+
+void SwAccessiblePreview::InvalidateFocus_()
+{
+ FireStateChangedEvent( css::accessibility::AccessibleStateType::FOCUSED, true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accpreview.hxx b/sw/source/core/access/accpreview.hxx
new file mode 100644
index 000000000..a60254f6a
--- /dev/null
+++ b/sw/source/core/access/accpreview.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPREVIEW_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCPREVIEW_HXX
+
+#include "accdoc.hxx"
+
+/**
+ * accessibility implementation for the page preview.
+ * The children of the page preview are the pages that are visible in the
+ * preview.
+ *
+ * The vast majority of the implementation logic is inherited from
+ * SwAccessibleDocumentBase.
+ */
+class SwAccessiblePreview : public SwAccessibleDocumentBase
+{
+ virtual ~SwAccessiblePreview() override;
+
+public:
+ SwAccessiblePreview(std::shared_ptr<SwAccessibleMap> const& pMap);
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ OUString SAL_CALL getAccessibleDescription() override;
+ OUString SAL_CALL getAccessibleName() override;
+ virtual void InvalidateFocus_() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accselectionhelper.cxx b/sw/source/core/access/accselectionhelper.cxx
new file mode 100644
index 000000000..96a622ece
--- /dev/null
+++ b/sw/source/core/access/accselectionhelper.cxx
@@ -0,0 +1,341 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include "accselectionhelper.hxx"
+
+#include "acccontext.hxx"
+#include <accmap.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <viewsh.hxx>
+#include <fesh.hxx>
+#include <vcl/svapp.hxx>
+#include <flyfrm.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <fmtanchr.hxx>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+using ::com::sun::star::accessibility::XAccessible;
+using ::com::sun::star::accessibility::XAccessibleContext;
+using ::com::sun::star::accessibility::XAccessibleSelection;
+
+using namespace ::sw::access;
+
+SwAccessibleSelectionHelper::SwAccessibleSelectionHelper(
+ SwAccessibleContext& rContext ) :
+ m_rContext( rContext )
+{
+}
+
+SwFEShell* SwAccessibleSelectionHelper::GetFEShell()
+{
+ OSL_ENSURE( m_rContext.GetMap() != nullptr, "no map?" );
+ SwViewShell* pViewShell = m_rContext.GetMap()->GetShell();
+ OSL_ENSURE( pViewShell != nullptr,
+ "No view shell? Then what are you looking at?" );
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>( pViewShell );
+
+ return pFEShell;
+}
+
+void SwAccessibleSelectionHelper::throwIndexOutOfBoundsException()
+{
+ Reference < XAccessibleContext > xThis( &m_rContext );
+ Reference < XAccessibleSelection >xSelThis( xThis, UNO_QUERY );
+ lang::IndexOutOfBoundsException aExcept(
+ "index out of bounds",
+ xSelThis );
+ throw aExcept;
+}
+
+// XAccessibleSelection
+void SwAccessibleSelectionHelper::selectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ // Get the respective child as SwFrame (also do index checking), ...
+ const SwAccessibleChild aChild = m_rContext.GetChild( *(m_rContext.GetMap()),
+ nChildIndex );
+ if( !aChild.IsValid() )
+ throwIndexOutOfBoundsException();
+
+ // we can only select fly frames, so we ignore (should: return
+ // false) all other attempts at child selection
+ if (GetFEShell())
+ {
+ const SdrObject *pObj = aChild.GetDrawObject();
+ if( pObj )
+ m_rContext.Select( const_cast< SdrObject *>( pObj ), nullptr==aChild.GetSwFrame());
+ }
+ // no frame shell, or no frame, or no fly frame -> can't select
+}
+
+//When the selected state of the SwFrameOrObj is set, return true.
+static bool lcl_getSelectedState(const SwAccessibleChild& aChild,
+ SwAccessibleContext* pContext,
+ SwAccessibleMap* pMap)
+{
+ Reference< XAccessible > xAcc;
+ if ( aChild.GetSwFrame() )
+ {
+ xAcc = pMap->GetContext( aChild.GetSwFrame(), false );
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ xAcc = pMap->GetContext( aChild.GetDrawObject(), pContext, false );
+ }
+
+ if( xAcc.is() )
+ {
+ Reference< XAccessibleContext > pRContext = xAcc->getAccessibleContext();
+ if(!pRContext.is())
+ return false;
+ Reference<XAccessibleStateSet> pRStateSet = pRContext->getAccessibleStateSet();
+ if( pRStateSet.is() )
+ {
+ const Sequence<short> aStates = pRStateSet->getStates();
+ if (std::find(aStates.begin(), aStates.end(), AccessibleStateType::SELECTED) != aStates.end())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwAccessibleSelectionHelper::isAccessibleChildSelected(
+ sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ // Get the respective child as SwFrame (also do index checking), ...
+ const SwAccessibleChild aChild = m_rContext.GetChild( *(m_rContext.GetMap()),
+ nChildIndex );
+ if( !aChild.IsValid() )
+ throwIndexOutOfBoundsException();
+
+ // ... and compare to the currently selected frame
+ bool bRet = false;
+ if (const SwFEShell* pFEShell = GetFEShell())
+ {
+ if ( aChild.GetSwFrame() != nullptr )
+ {
+ bRet = (pFEShell->GetSelectedFlyFrame() == aChild.GetSwFrame());
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ bRet = pFEShell->IsObjSelected( *aChild.GetDrawObject() );
+ }
+ //If the SwFrameOrObj is not selected directly in the UI, we should check whether it is selected in the selection cursor.
+ if( !bRet )
+ {
+ if( lcl_getSelectedState( aChild, &m_rContext, m_rContext.GetMap() ) )
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+void SwAccessibleSelectionHelper::selectAllAccessibleChildren( )
+{
+ SolarMutexGuard aGuard;
+
+ // We can select only one. So iterate over the children to find
+ // the first we can select, and select it.
+
+ SwFEShell* pFEShell = GetFEShell();
+ if (!pFEShell)
+ return;
+
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+
+ for( const SwAccessibleChild& rChild : aChildren )
+ {
+ const SdrObject* pObj = rChild.GetDrawObject();
+ const SwFrame* pFrame = rChild.GetSwFrame();
+ if( pObj && !(pFrame != nullptr && pFEShell->IsObjSelected()) )
+ {
+ m_rContext.Select( const_cast< SdrObject *>( pObj ), nullptr==pFrame );
+ if( pFrame )
+ break;
+ }
+ }
+}
+
+sal_Int32 SwAccessibleSelectionHelper::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nCount = 0;
+ // Only one frame can be selected at a time, and we only frames
+ // for selectable children.
+ if (const SwFEShell* pFEShell = GetFEShell())
+ {
+ const SwFlyFrame* pFlyFrame = pFEShell->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ nCount = 1;
+ }
+ else
+ {
+ const size_t nSelObjs = pFEShell->IsObjSelected();
+ if( nSelObjs > 0 )
+ {
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+
+ for( const SwAccessibleChild& rChild : aChildren )
+ {
+ if( rChild.GetDrawObject() && !rChild.GetSwFrame() &&
+ SwAccessibleFrame::GetParent(rChild, m_rContext.IsInPagePreview())
+ == m_rContext.GetFrame() &&
+ pFEShell->IsObjSelected( *rChild.GetDrawObject() ) )
+ {
+ nCount++;
+ }
+ if (o3tl::make_unsigned(nCount) >= nSelObjs)
+ break;
+ }
+ }
+ }
+ //If the SwFrameOrObj is not selected directly in the UI,
+ //we should check whether it is selected in the selection cursor.
+ if( nCount == 0 )
+ {
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+ nCount = static_cast<sal_Int32>(std::count_if(aChildren.begin(), aChildren.end(),
+ [this](const SwAccessibleChild& aChild) { return lcl_getSelectedState(aChild, &m_rContext, m_rContext.GetMap()); }));
+ }
+ }
+ return nCount;
+}
+
+Reference<XAccessible> SwAccessibleSelectionHelper::getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ // Since the index is relative to the selected children, and since
+ // there can be at most one selected frame child, the index must
+ // be 0, and a selection must exist, otherwise we have to throw an
+ // lang::IndexOutOfBoundsException
+ SwFEShell* pFEShell = GetFEShell();
+ if (!pFEShell)
+ throwIndexOutOfBoundsException();
+
+ SwAccessibleChild aChild;
+ const SwFlyFrame *pFlyFrame = pFEShell->GetSelectedFlyFrame();
+ if( pFlyFrame )
+ {
+ if( 0 == nSelectedChildIndex )
+ {
+ if(SwAccessibleFrame::GetParent( SwAccessibleChild(pFlyFrame), m_rContext.IsInPagePreview()) == m_rContext.GetFrame() )
+ {
+ aChild = pFlyFrame;
+ }
+ else
+ {
+ const SwFrameFormat *pFrameFormat = pFlyFrame->GetFormat();
+ if (pFrameFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ if( rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ const SwFrame *pParaFrame = SwAccessibleFrame::GetParent( SwAccessibleChild(pFlyFrame), m_rContext.IsInPagePreview() );
+ aChild = pParaFrame;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ const size_t nSelObjs = pFEShell->IsObjSelected();
+ if( 0 == nSelObjs || o3tl::make_unsigned(nSelectedChildIndex) >= nSelObjs )
+ throwIndexOutOfBoundsException();
+
+ std::list< SwAccessibleChild > aChildren;
+ m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren );
+
+ for( const SwAccessibleChild& rChild : aChildren )
+ {
+ if( rChild.GetDrawObject() && !rChild.GetSwFrame() &&
+ SwAccessibleFrame::GetParent(rChild, m_rContext.IsInPagePreview()) ==
+ m_rContext.GetFrame() &&
+ pFEShell->IsObjSelected( *rChild.GetDrawObject() ) )
+ {
+ if( 0 == nSelectedChildIndex )
+ aChild = rChild;
+ else
+ --nSelectedChildIndex;
+ }
+ if (aChild.IsValid())
+ break;
+ }
+ }
+
+ if( !aChild.IsValid() )
+ throwIndexOutOfBoundsException();
+
+ OSL_ENSURE( m_rContext.GetMap() != nullptr, "We need the map." );
+ Reference< XAccessible > xChild;
+ if( aChild.GetSwFrame() )
+ {
+ ::rtl::Reference < SwAccessibleContext > xChildImpl(
+ m_rContext.GetMap()->GetContextImpl( aChild.GetSwFrame() ) );
+ if( xChildImpl.is() )
+ {
+ xChildImpl->SetParent( &m_rContext );
+ xChild = xChildImpl.get();
+ }
+ }
+ else if ( aChild.GetDrawObject() )
+ {
+ ::rtl::Reference < ::accessibility::AccessibleShape > xChildImpl(
+ m_rContext.GetMap()->GetContextImpl( aChild.GetDrawObject(),
+ &m_rContext ) );
+ if( xChildImpl.is() )
+ xChild = xChildImpl.get();
+ }
+ return xChild;
+}
+
+// index has to be treated as global child index.
+void SwAccessibleSelectionHelper::deselectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ SolarMutexGuard g;
+
+ if( nChildIndex < 0 ||
+ nChildIndex >= m_rContext.GetChildCount( *(m_rContext.GetMap()) ) )
+ throwIndexOutOfBoundsException();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/accselectionhelper.hxx b/sw/source/core/access/accselectionhelper.hxx
new file mode 100644
index 000000000..ceaa2a784
--- /dev/null
+++ b/sw/source/core/access/accselectionhelper.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCSELECTIONHELPER_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCSELECTIONHELPER_HXX
+
+#include <sal/types.h>
+#include <com/sun/star/uno/Reference.h>
+
+namespace com::sun::star::accessibility { class XAccessible; }
+
+class SwAccessibleContext;
+class SwFEShell;
+
+class SwAccessibleSelectionHelper
+{
+ /// the context on which this helper works
+ SwAccessibleContext& m_rContext;
+
+ /// get FE-Shell
+ SwFEShell* GetFEShell();
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ void throwIndexOutOfBoundsException();
+
+public:
+ SwAccessibleSelectionHelper( SwAccessibleContext& rContext );
+
+ // XAccessibleSelection
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ void selectAccessibleChild(
+ sal_Int32 nChildIndex );
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ bool isAccessibleChildSelected(
+ sal_Int32 nChildIndex );
+ /// @throws css::uno::RuntimeException
+ void selectAllAccessibleChildren( );
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getSelectedAccessibleChildCount( );
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference< css::accessibility::XAccessible > getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex );
+ // index has to be treated as global child index.
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::uno::RuntimeException
+ void deselectAccessibleChild(
+ sal_Int32 nChildIndex );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctable.cxx b/sw/source/core/access/acctable.cxx
new file mode 100644
index 000000000..0fd373754
--- /dev/null
+++ b/sw/source/core/access/acctable.cxx
@@ -0,0 +1,1739 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <vector>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
+#include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <vcl/svapp.hxx>
+#include <frmfmt.hxx>
+#include <tabfrm.hxx>
+#include <cellfrm.hxx>
+#include <swtable.hxx>
+#include <crsrsh.hxx>
+#include <viscrs.hxx>
+#include "accfrmobjslist.hxx"
+#include <accmap.hxx>
+#include <strings.hrc>
+#include "acctable.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+
+#include <editeng/brushitem.hxx>
+#include <swatrset.hxx>
+#include <frmatr.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::sw::access;
+
+typedef o3tl::sorted_vector< sal_Int32 > Int32Set_Impl;
+
+const unsigned int SELECTION_WITH_NUM = 10;
+
+namespace {
+
+class SwAccTableSelHandler_Impl
+{
+public:
+ virtual void Unselect( sal_Int32 nRowOrCol, sal_Int32 nExt ) = 0;
+
+protected:
+ ~SwAccTableSelHandler_Impl() {}
+};
+
+}
+
+class SwAccessibleTableData_Impl
+{
+ SwAccessibleMap& mrAccMap;
+ Int32Set_Impl maRows;
+ Int32Set_Impl maColumns;
+ Point maTabFramePos;
+ const SwTabFrame *mpTabFrame;
+ bool mbIsInPagePreview;
+ bool mbOnlyTableColumnHeader;
+
+ void CollectData( const SwFrame *pFrame );
+
+ bool FindCell( const Point& rPos, const SwFrame *pFrame ,
+ bool bExact, const SwFrame *& rFrame ) const;
+
+ void GetSelection( const Point& rTabPos, const SwRect& rArea,
+ const SwSelBoxes& rSelBoxes, const SwFrame *pFrame,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const;
+
+ // #i77106#
+ bool IncludeRow( const SwFrame& rFrame ) const
+ {
+ return !mbOnlyTableColumnHeader ||
+ mpTabFrame->IsInHeadline( rFrame );
+ }
+public:
+ // #i77106# - add third optional parameter <bOnlyTableColumnHeader>, default value <false>
+ SwAccessibleTableData_Impl( SwAccessibleMap& rAccMap,
+ const SwTabFrame *pTabFrame,
+ bool bIsInPagePreview,
+ bool bOnlyTableColumnHeader = false );
+
+ const Int32Set_Impl& GetRows() const { return maRows; }
+ const Int32Set_Impl& GetColumns() const { return maColumns; }
+
+ inline Int32Set_Impl::const_iterator GetRowIter( sal_Int32 nRow ) const;
+ inline Int32Set_Impl::const_iterator GetColumnIter( sal_Int32 nCol ) const;
+
+ /// @throws lang::IndexOutOfBoundsException
+ /// @throws uno::RuntimeException
+ const SwFrame *GetCell( sal_Int32 nRow, sal_Int32 nColumn, SwAccessibleTable *pThis ) const;
+ const SwFrame *GetCellAtPos( sal_Int32 nLeft, sal_Int32 nTop ) const;
+ inline sal_Int32 GetRowCount() const;
+ inline sal_Int32 GetColumnCount() const;
+ bool CompareExtents( const SwAccessibleTableData_Impl& r ) const;
+
+ void GetSelection( sal_Int32 nStart, sal_Int32 nEnd,
+ const SwSelBoxes& rSelBoxes,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const;
+
+ /// @throws lang::IndexOutOfBoundsException
+ void CheckRowAndCol( sal_Int32 nRow, sal_Int32 nCol,
+ SwAccessibleTable *pThis ) const;
+
+ const Point& GetTablePos() const { return maTabFramePos; }
+ void SetTablePos( const Point& rPos ) { maTabFramePos = rPos; }
+};
+
+void SwAccessibleTableData_Impl::CollectData( const SwFrame *pFrame )
+{
+ const SwAccessibleChildSList aList( *pFrame, mrAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ SwAccessibleChildSList::const_iterator aEndIter( aList.end() );
+ while( aIter != aEndIter )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ if( pLower )
+ {
+ if( pLower->IsRowFrame() )
+ {
+ // #i77106#
+ if ( IncludeRow( *pLower ) )
+ {
+ maRows.insert( pLower->getFrameArea().Top() - maTabFramePos.getY() );
+ CollectData( pLower );
+ }
+ }
+ else if( pLower->IsCellFrame() &&
+ rLower.IsAccessible( mbIsInPagePreview ) )
+ {
+ maColumns.insert( pLower->getFrameArea().Left() - maTabFramePos.getX() );
+ }
+ else
+ {
+ CollectData( pLower );
+ }
+ }
+ ++aIter;
+ }
+}
+
+bool SwAccessibleTableData_Impl::FindCell(
+ const Point& rPos, const SwFrame *pFrame, bool bExact,
+ const SwFrame *& rRet ) const
+{
+ bool bFound = false;
+
+ const SwAccessibleChildSList aList( *pFrame, mrAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ SwAccessibleChildSList::const_iterator aEndIter( aList.end() );
+ while( !bFound && aIter != aEndIter )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ OSL_ENSURE( pLower, "child should be a frame" );
+ if( pLower )
+ {
+ if( rLower.IsAccessible( mbIsInPagePreview ) )
+ {
+ OSL_ENSURE( pLower->IsCellFrame(), "lower is not a cell frame" );
+ const SwRect& rFrame = pLower->getFrameArea();
+ if( rFrame.Right() >= rPos.X() && rFrame.Bottom() >= rPos.Y() )
+ {
+ // We have found the cell
+ OSL_ENSURE( rFrame.Left() <= rPos.X() && rFrame.Top() <= rPos.Y(),
+ "find frame moved to far!" );
+ bFound = true;
+ if( !bExact ||
+ (rFrame.Top() == rPos.Y() && rFrame.Left() == rPos.Y() ) )
+ {
+ rRet = pLower;
+ }
+ }
+ }
+ else
+ {
+ // #i77106#
+ if ( !pLower->IsRowFrame() ||
+ IncludeRow( *pLower ) )
+ {
+ bFound = FindCell( rPos, pLower, bExact, rRet );
+ }
+ }
+ }
+ ++aIter;
+ }
+
+ return bFound;
+}
+
+void SwAccessibleTableData_Impl::GetSelection(
+ const Point& rTabPos,
+ const SwRect& rArea,
+ const SwSelBoxes& rSelBoxes,
+ const SwFrame *pFrame,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const
+{
+ const SwAccessibleChildSList aList( *pFrame, mrAccMap );
+ SwAccessibleChildSList::const_iterator aIter( aList.begin() );
+ SwAccessibleChildSList::const_iterator aEndIter( aList.end() );
+ while( aIter != aEndIter )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ const SwFrame *pLower = rLower.GetSwFrame();
+ OSL_ENSURE( pLower, "child should be a frame" );
+ const SwRect& rBox = rLower.GetBox( mrAccMap );
+ if( pLower && rBox.Overlaps( rArea ) )
+ {
+ if( rLower.IsAccessible( mbIsInPagePreview ) )
+ {
+ OSL_ENSURE( pLower->IsCellFrame(), "lower is not a cell frame" );
+ const SwCellFrame *pCFrame =
+ static_cast < const SwCellFrame * >( pLower );
+ SwTableBox *pBox =
+ const_cast< SwTableBox *>( pCFrame->GetTabBox() );
+ if( rSelBoxes.find( pBox ) == rSelBoxes.end() )
+ {
+ const Int32Set_Impl rRowsOrCols =
+ bColumns ? maColumns : maRows;
+
+ sal_Int32 nPos = bColumns ? (rBox.Left() - rTabPos.X())
+ : (rBox.Top() - rTabPos.Y());
+ Int32Set_Impl::const_iterator aSttRowOrCol(
+ rRowsOrCols.lower_bound( nPos ) );
+ sal_Int32 nRowOrCol =
+ static_cast< sal_Int32 >( std::distance(
+ rRowsOrCols.begin(), aSttRowOrCol ) );
+
+ nPos = bColumns ? (rBox.Right() - rTabPos.X())
+ : (rBox.Bottom() - rTabPos.Y());
+ Int32Set_Impl::const_iterator aEndRowOrCol(
+ rRowsOrCols.upper_bound( nPos ) );
+ sal_Int32 nExt =
+ static_cast< sal_Int32 >( std::distance(
+ aSttRowOrCol, aEndRowOrCol ) );
+
+ rSelHdl.Unselect( nRowOrCol, nExt );
+ }
+ }
+ else
+ {
+ // #i77106#
+ if ( !pLower->IsRowFrame() ||
+ IncludeRow( *pLower ) )
+ {
+ GetSelection( rTabPos, rArea, rSelBoxes, pLower, rSelHdl,
+ bColumns );
+ }
+ }
+ }
+ ++aIter;
+ }
+}
+
+const SwFrame *SwAccessibleTableData_Impl::GetCell(
+ sal_Int32 nRow, sal_Int32 nColumn,
+ SwAccessibleTable *pThis ) const
+{
+ CheckRowAndCol( nRow, nColumn, pThis );
+
+ Int32Set_Impl::const_iterator aSttCol( GetColumnIter( nColumn ) );
+ Int32Set_Impl::const_iterator aSttRow( GetRowIter( nRow ) );
+ const SwFrame *pCellFrame = GetCellAtPos( *aSttCol, *aSttRow );
+
+ return pCellFrame;
+}
+
+void SwAccessibleTableData_Impl::GetSelection(
+ sal_Int32 nStart, sal_Int32 nEnd,
+ const SwSelBoxes& rSelBoxes,
+ SwAccTableSelHandler_Impl& rSelHdl,
+ bool bColumns ) const
+{
+ SwRect aArea( mpTabFrame->getFrameArea() );
+ Point aPos( aArea.Pos() );
+
+ const Int32Set_Impl& rRowsOrColumns = bColumns ? maColumns : maRows;
+ if( nStart > 0 )
+ {
+ Int32Set_Impl::const_iterator aStt( rRowsOrColumns.begin() );
+ std::advance( aStt,
+ static_cast< Int32Set_Impl::difference_type >( nStart ) );
+ if( bColumns )
+ aArea.Left( *aStt + aPos.getX() );
+ else
+ aArea.Top( *aStt + aPos.getY() );
+ }
+ if( nEnd < static_cast< sal_Int32 >( rRowsOrColumns.size() ) )
+ {
+ Int32Set_Impl::const_iterator aEnd( rRowsOrColumns.begin() );
+ std::advance( aEnd,
+ static_cast< Int32Set_Impl::difference_type >( nEnd ) );
+ if( bColumns )
+ aArea.Right( *aEnd + aPos.getX() - 1 );
+ else
+ aArea.Bottom( *aEnd + aPos.getY() - 1 );
+ }
+
+ GetSelection( aPos, aArea, rSelBoxes, mpTabFrame, rSelHdl, bColumns );
+}
+
+const SwFrame *SwAccessibleTableData_Impl::GetCellAtPos(
+ sal_Int32 nLeft, sal_Int32 nTop ) const
+{
+ Point aPos( mpTabFrame->getFrameArea().Pos() );
+ aPos.Move( nLeft, nTop );
+ const SwFrame *pRet = nullptr;
+ FindCell( aPos, mpTabFrame, false/*bExact*/, pRet );
+
+ return pRet;
+}
+
+inline sal_Int32 SwAccessibleTableData_Impl::GetRowCount() const
+{
+ sal_Int32 count = static_cast< sal_Int32 >( maRows.size() ) ;
+ count = (count <=0)? 1:count;
+ return count;
+}
+
+inline sal_Int32 SwAccessibleTableData_Impl::GetColumnCount() const
+{
+ return static_cast< sal_Int32 >( maColumns.size() );
+}
+
+bool SwAccessibleTableData_Impl::CompareExtents(
+ const SwAccessibleTableData_Impl& rCmp ) const
+{
+ return maRows == rCmp.maRows
+ && maColumns == rCmp.maColumns;
+}
+
+SwAccessibleTableData_Impl::SwAccessibleTableData_Impl( SwAccessibleMap& rAccMap,
+ const SwTabFrame *pTabFrame,
+ bool bIsInPagePreview,
+ bool bOnlyTableColumnHeader )
+ : mrAccMap( rAccMap )
+ , maTabFramePos( pTabFrame->getFrameArea().Pos() )
+ , mpTabFrame( pTabFrame )
+ , mbIsInPagePreview( bIsInPagePreview )
+ , mbOnlyTableColumnHeader( bOnlyTableColumnHeader )
+{
+ CollectData( mpTabFrame );
+}
+
+inline Int32Set_Impl::const_iterator SwAccessibleTableData_Impl::GetRowIter(
+ sal_Int32 nRow ) const
+{
+ Int32Set_Impl::const_iterator aCol( GetRows().begin() );
+ if( nRow > 0 )
+ {
+ std::advance( aCol,
+ static_cast< Int32Set_Impl::difference_type >( nRow ) );
+ }
+ return aCol;
+}
+
+inline Int32Set_Impl::const_iterator SwAccessibleTableData_Impl::GetColumnIter(
+ sal_Int32 nColumn ) const
+{
+ Int32Set_Impl::const_iterator aCol = GetColumns().begin();
+ if( nColumn > 0 )
+ {
+ std::advance( aCol,
+ static_cast< Int32Set_Impl::difference_type >( nColumn ) );
+ }
+ return aCol;
+}
+
+void SwAccessibleTableData_Impl::CheckRowAndCol(
+ sal_Int32 nRow, sal_Int32 nCol, SwAccessibleTable *pThis ) const
+{
+ if( ( nRow < 0 || o3tl::make_unsigned(nRow) >= maRows.size() ) ||
+ ( nCol < 0 || o3tl::make_unsigned(nCol) >= maColumns.size() ) )
+ {
+ uno::Reference < XAccessibleTable > xThis( pThis );
+ lang::IndexOutOfBoundsException aExcept(
+ "row or column index out of range",
+ xThis );
+ throw aExcept;
+ }
+}
+
+namespace {
+
+class SwAccSingleTableSelHandler_Impl : public SwAccTableSelHandler_Impl
+{
+ bool m_bSelected;
+
+public:
+
+ inline SwAccSingleTableSelHandler_Impl();
+
+ virtual ~SwAccSingleTableSelHandler_Impl() {}
+
+ bool IsSelected() const { return m_bSelected; }
+
+ virtual void Unselect( sal_Int32, sal_Int32 ) override;
+};
+
+}
+
+inline SwAccSingleTableSelHandler_Impl::SwAccSingleTableSelHandler_Impl() :
+ m_bSelected( true )
+{
+}
+
+void SwAccSingleTableSelHandler_Impl::Unselect( sal_Int32, sal_Int32 )
+{
+ m_bSelected = false;
+}
+
+namespace {
+
+class SwAccAllTableSelHandler_Impl : public SwAccTableSelHandler_Impl
+
+{
+ std::vector< bool > m_aSelected;
+ sal_Int32 m_nCount;
+
+public:
+ explicit SwAccAllTableSelHandler_Impl(sal_Int32 nSize)
+ : m_aSelected(nSize, true)
+ , m_nCount(nSize)
+ {
+ }
+
+ uno::Sequence < sal_Int32 > GetSelSequence();
+
+ virtual void Unselect( sal_Int32 nRowOrCol, sal_Int32 nExt ) override;
+ virtual ~SwAccAllTableSelHandler_Impl();
+};
+
+}
+
+SwAccAllTableSelHandler_Impl::~SwAccAllTableSelHandler_Impl()
+{
+}
+
+uno::Sequence < sal_Int32 > SwAccAllTableSelHandler_Impl::GetSelSequence()
+{
+ OSL_ENSURE( m_nCount >= 0, "underflow" );
+ uno::Sequence < sal_Int32 > aRet( m_nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = m_aSelected.size();
+ for( size_t i=0; i < nSize && nPos < m_nCount; i++ )
+ {
+ if( m_aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ OSL_ENSURE( nPos == m_nCount, "count is wrong" );
+
+ return aRet;
+}
+
+void SwAccAllTableSelHandler_Impl::Unselect( sal_Int32 nRowOrCol,
+ sal_Int32 nExt )
+{
+ OSL_ENSURE( o3tl::make_unsigned( nRowOrCol ) < m_aSelected.size(),
+ "index too large" );
+ OSL_ENSURE( o3tl::make_unsigned( nRowOrCol+nExt ) <= m_aSelected.size(),
+ "extent too large" );
+ while( nExt )
+ {
+ if( m_aSelected[static_cast< size_t >( nRowOrCol )] )
+ {
+ m_aSelected[static_cast< size_t >( nRowOrCol )] = false;
+ m_nCount--;
+ }
+ nExt--;
+ nRowOrCol++;
+ }
+}
+
+const SwSelBoxes *SwAccessibleTable::GetSelBoxes() const
+{
+ const SwSelBoxes *pSelBoxes = nullptr;
+ const SwCursorShell *pCSh = GetCursorShell();
+ if( (pCSh != nullptr) && pCSh->IsTableMode() )
+ {
+ pSelBoxes = &pCSh->GetTableCursor()->GetSelectedBoxes();
+ }
+
+ return pSelBoxes;
+}
+
+void SwAccessibleTable::FireTableChangeEvent(
+ const SwAccessibleTableData_Impl& rTableData )
+{
+ AccessibleTableModelChange aModelChange;
+ aModelChange.Type = AccessibleTableModelChangeType::UPDATE;
+ aModelChange.FirstRow = 0;
+ aModelChange.LastRow = rTableData.GetRowCount() - 1;
+ aModelChange.FirstColumn = 0;
+ aModelChange.LastColumn = rTableData.GetColumnCount() - 1;
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TABLE_MODEL_CHANGED;
+ aEvent.NewValue <<= aModelChange;
+
+ FireAccessibleEvent( aEvent );
+}
+
+const SwTableBox* SwAccessibleTable::GetTableBox( sal_Int32 nChildIndex ) const
+{
+ OSL_ENSURE( nChildIndex >= 0, "Illegal child index." );
+ OSL_ENSURE( nChildIndex < const_cast<SwAccessibleTable*>(this)->getAccessibleChildCount(), "Illegal child index." ); // #i77106#
+
+ const SwTableBox* pBox = nullptr;
+
+ // get table box for 'our' table cell
+ SwAccessibleChild aCell( GetChild( *const_cast<SwAccessibleMap*>(GetMap()), nChildIndex ) );
+ if( aCell.GetSwFrame() )
+ {
+ const SwFrame* pChildFrame = aCell.GetSwFrame();
+ if( (pChildFrame != nullptr) && pChildFrame->IsCellFrame() )
+ {
+ const SwCellFrame* pCellFrame =
+ static_cast<const SwCellFrame*>( pChildFrame );
+ pBox = pCellFrame->GetTabBox();
+ }
+ }
+
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+ return pBox;
+}
+
+bool SwAccessibleTable::IsChildSelected( sal_Int32 nChildIndex ) const
+{
+ bool bRet = false;
+ const SwSelBoxes* pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ const SwTableBox* pBox = GetTableBox( nChildIndex );
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+ bRet = pSelBoxes->find( const_cast<SwTableBox*>( pBox ) ) != pSelBoxes->end();
+ }
+
+ return bRet;
+}
+
+sal_Int32 SwAccessibleTable::GetIndexOfSelectedChild(
+ sal_Int32 nSelectedChildIndex ) const
+{
+ // iterate over all children to n-th isAccessibleChildSelected()
+ sal_Int32 nChildren = const_cast<SwAccessibleTable*>(this)->getAccessibleChildCount(); // #i77106#
+ if( nSelectedChildIndex >= nChildren )
+ return -1;
+
+ sal_Int32 n = 0;
+ while( n < nChildren )
+ {
+ if( IsChildSelected( n ) )
+ {
+ if( 0 == nSelectedChildIndex )
+ break;
+ else
+ --nSelectedChildIndex;
+ }
+ ++n;
+ }
+
+ return n < nChildren ? n : -1;
+}
+
+void SwAccessibleTable::GetStates(
+ ::utl::AccessibleStateSetHelper& rStateSet )
+{
+ SwAccessibleContext::GetStates( rStateSet );
+ //Add resizable state to table
+ rStateSet.AddState( AccessibleStateType::RESIZABLE );
+ // MULTISELECTABLE
+ rStateSet.AddState( AccessibleStateType::MULTI_SELECTABLE );
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell )
+ rStateSet.AddState( AccessibleStateType::MULTI_SELECTABLE );
+}
+
+SwAccessibleTable::SwAccessibleTable(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTabFrame* pTabFrame ) :
+ SwAccessibleContext( pInitMap, AccessibleRole::TABLE, pTabFrame )
+{
+ const SwFrameFormat* pFrameFormat = pTabFrame->GetFormat();
+ if(pFrameFormat)
+ StartListening(const_cast<SwFrameFormat*>(pFrameFormat)->GetNotifier());
+
+ SetName( pFrameFormat->GetName() + "-" + OUString::number( pTabFrame->GetPhyPageNum() ) );
+
+ const OUString sArg1( static_cast< const SwTabFrame * >( GetFrame() )->GetFormat()->GetName() );
+ const OUString sArg2( GetFormattedPageNumber() );
+
+ m_sDesc = GetResource( STR_ACCESS_TABLE_DESC, &sArg1, &sArg2 );
+ UpdateTableData();
+}
+
+SwAccessibleTable::~SwAccessibleTable()
+{
+ SolarMutexGuard aGuard;
+
+ mpTableData.reset();
+}
+
+void SwAccessibleTable::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListeningAll();
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyHint->GetWhich();
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(GetFrame());
+ if(nWhich == RES_NAME_CHANGED && pTabFrame)
+ {
+ const SwFrameFormat *pFrameFormat = pTabFrame->GetFormat();
+
+ const OUString sOldName( GetName() );
+ const OUString sNewTabName = pFrameFormat->GetName();
+
+ SetName( sNewTabName + "-" + OUString::number( pTabFrame->GetPhyPageNum() ) );
+
+ if( sOldName != GetName() )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= sOldName;
+ aEvent.NewValue <<= GetName();
+ FireAccessibleEvent( aEvent );
+ }
+
+ const OUString sOldDesc( m_sDesc );
+ const OUString sArg2( GetFormattedPageNumber() );
+
+ m_sDesc = GetResource( STR_ACCESS_TABLE_DESC, &sNewTabName, &sArg2 );
+ if( m_sDesc != sOldDesc )
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= m_sDesc;
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ }
+}
+
+uno::Any SwAccessibleTable::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet;
+ if ( rType == cppu::UnoType<XAccessibleTable>::get() )
+ {
+ uno::Reference<XAccessibleTable> xThis( this );
+ aRet <<= xThis;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleSelection>::get() )
+ {
+ uno::Reference<XAccessibleSelection> xSelection( this );
+ aRet <<= xSelection;
+ }
+ else if ( rType == cppu::UnoType<XAccessibleTableSelection>::get() )
+ {
+ uno::Reference<XAccessibleTableSelection> xTableExtent( this );
+ aRet <<= xTableExtent;
+ }
+ else
+ {
+ aRet = SwAccessibleContext::queryInterface(rType);
+ }
+
+ return aRet;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SwAccessibleTable::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<XAccessibleSelection>::get(),
+ cppu::UnoType<XAccessibleTable>::get(),
+ SwAccessibleContext::getTypes() ).getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleTable::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// #i77106#
+std::unique_ptr<SwAccessibleTableData_Impl> SwAccessibleTable::CreateNewTableData()
+{
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>( GetFrame() );
+ return std::unique_ptr<SwAccessibleTableData_Impl>(new SwAccessibleTableData_Impl( *GetMap(), pTabFrame, IsInPagePreview() ));
+}
+
+void SwAccessibleTable::UpdateTableData()
+{
+ // #i77106# - usage of new method <CreateNewTableData()>
+ mpTableData = CreateNewTableData();
+}
+
+void SwAccessibleTable::ClearTableData()
+{
+ mpTableData.reset();
+}
+
+OUString SAL_CALL SwAccessibleTable::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return m_sDesc;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleRowCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetTableData().GetRowCount();
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleColumnCount( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return GetTableData().GetColumnCount();
+}
+
+OUString SAL_CALL SwAccessibleTable::getAccessibleRowDescription(
+ sal_Int32 nRow )
+{
+ // #i87532# - determine table cell in <nRow>th row and
+ // in first column of row header table and return its text content.
+ OUString sRowDesc;
+
+ GetTableData().CheckRowAndCol(nRow, 0, this);
+
+ uno::Reference< XAccessibleTable > xTableRowHeader = getAccessibleRowHeaders();
+ if ( xTableRowHeader.is() )
+ {
+ uno::Reference< XAccessible > xRowHeaderCell =
+ xTableRowHeader->getAccessibleCellAt( nRow, 0 );
+ OSL_ENSURE( xRowHeaderCell.is(),
+ "<SwAccessibleTable::getAccessibleRowDescription(..)> - missing row header cell -> serious issue." );
+ uno::Reference< XAccessibleContext > xRowHeaderCellContext =
+ xRowHeaderCell->getAccessibleContext();
+ const sal_Int32 nCellChildCount( xRowHeaderCellContext->getAccessibleChildCount() );
+ for ( sal_Int32 nChildIndex = 0; nChildIndex < nCellChildCount; ++nChildIndex )
+ {
+ uno::Reference< XAccessible > xChild = xRowHeaderCellContext->getAccessibleChild( nChildIndex );
+ uno::Reference< XAccessibleText > xChildText( xChild, uno::UNO_QUERY );
+ if ( xChildText.is() )
+ {
+ sRowDesc += xChildText->getText();
+ }
+ }
+ }
+
+ return sRowDesc;
+}
+
+OUString SAL_CALL SwAccessibleTable::getAccessibleColumnDescription(
+ sal_Int32 nColumn )
+{
+ // #i87532# - determine table cell in first row and
+ // in <nColumn>th column of column header table and return its text content.
+ OUString sColumnDesc;
+
+ GetTableData().CheckRowAndCol(0, nColumn, this);
+
+ uno::Reference< XAccessibleTable > xTableColumnHeader = getAccessibleColumnHeaders();
+ if ( xTableColumnHeader.is() )
+ {
+ uno::Reference< XAccessible > xColumnHeaderCell =
+ xTableColumnHeader->getAccessibleCellAt( 0, nColumn );
+ OSL_ENSURE( xColumnHeaderCell.is(),
+ "<SwAccessibleTable::getAccessibleColumnDescription(..)> - missing column header cell -> serious issue." );
+ uno::Reference< XAccessibleContext > xColumnHeaderCellContext =
+ xColumnHeaderCell->getAccessibleContext();
+ const sal_Int32 nCellChildCount( xColumnHeaderCellContext->getAccessibleChildCount() );
+ for ( sal_Int32 nChildIndex = 0; nChildIndex < nCellChildCount; ++nChildIndex )
+ {
+ uno::Reference< XAccessible > xChild = xColumnHeaderCellContext->getAccessibleChild( nChildIndex );
+ uno::Reference< XAccessibleText > xChildText( xChild, uno::UNO_QUERY );
+ if ( xChildText.is() )
+ {
+ sColumnDesc += xChildText->getText();
+ }
+ }
+ }
+
+ return sColumnDesc;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleRowExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_Int32 nExtend = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ UpdateTableData();
+ GetTableData().CheckRowAndCol( nRow, nColumn, this );
+
+ Int32Set_Impl::const_iterator aSttCol(
+ GetTableData().GetColumnIter( nColumn ) );
+ Int32Set_Impl::const_iterator aSttRow(
+ GetTableData().GetRowIter( nRow ) );
+ const SwFrame *pCellFrame = GetTableData().GetCellAtPos( *aSttCol, *aSttRow );
+ if( pCellFrame )
+ {
+ sal_Int32 nBottom = pCellFrame->getFrameArea().Bottom();
+ nBottom -= GetFrame()->getFrameArea().Top();
+ Int32Set_Impl::const_iterator aEndRow(
+ GetTableData().GetRows().upper_bound( nBottom ) );
+ nExtend =
+ static_cast< sal_Int32 >( std::distance( aSttRow, aEndRow ) );
+ }
+
+ return nExtend;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleColumnExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_Int32 nExtend = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+ UpdateTableData();
+
+ GetTableData().CheckRowAndCol( nRow, nColumn, this );
+
+ Int32Set_Impl::const_iterator aSttCol(
+ GetTableData().GetColumnIter( nColumn ) );
+ Int32Set_Impl::const_iterator aSttRow(
+ GetTableData().GetRowIter( nRow ) );
+ const SwFrame *pCellFrame = GetTableData().GetCellAtPos( *aSttCol, *aSttRow );
+ if( pCellFrame )
+ {
+ sal_Int32 nRight = pCellFrame->getFrameArea().Right();
+ nRight -= GetFrame()->getFrameArea().Left();
+ Int32Set_Impl::const_iterator aEndCol(
+ GetTableData().GetColumns().upper_bound( nRight ) );
+ nExtend =
+ static_cast< sal_Int32 >( std::distance( aSttCol, aEndCol ) );
+ }
+
+ return nExtend;
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL
+ SwAccessibleTable::getAccessibleRowHeaders( )
+{
+ // Row headers aren't supported
+ return uno::Reference< XAccessibleTable >();
+}
+
+uno::Reference< XAccessibleTable > SAL_CALL
+ SwAccessibleTable::getAccessibleColumnHeaders( )
+{
+ SolarMutexGuard aGuard;
+
+ // #i87532# - assure that return accessible object is empty,
+ // if no column header exists.
+ rtl::Reference<SwAccessibleTableColHeaders> pTableColHeaders =
+ new SwAccessibleTableColHeaders(GetMap()->shared_from_this(),
+ static_cast<const SwTabFrame *>(GetFrame()));
+ if ( pTableColHeaders->getAccessibleChildCount() <= 0 )
+ {
+ return uno::Reference< XAccessibleTable >();
+ }
+
+ return pTableColHeaders;
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL SwAccessibleTable::getSelectedAccessibleRows()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ sal_Int32 nRows = GetTableData().GetRowCount();
+ SwAccAllTableSelHandler_Impl aSelRows( nRows );
+
+ GetTableData().GetSelection( 0, nRows, *pSelBoxes, aSelRows,
+ false );
+
+ return aSelRows.GetSelSequence();
+ }
+ else
+ {
+ return uno::Sequence< sal_Int32 >( 0 );
+ }
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL SwAccessibleTable::getSelectedAccessibleColumns()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ sal_Int32 nCols = GetTableData().GetColumnCount();
+ SwAccAllTableSelHandler_Impl aSelCols( nCols );
+
+ GetTableData().GetSelection( 0, nCols, *pSelBoxes, aSelCols, true );
+
+ return aSelCols.GetSelSequence();
+ }
+ else
+ {
+ return uno::Sequence< sal_Int32 >( 0 );
+ }
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ GetTableData().CheckRowAndCol( nRow, 0, this );
+
+ bool bRet;
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ SwAccSingleTableSelHandler_Impl aSelRow;
+ GetTableData().GetSelection( nRow, nRow+1, *pSelBoxes, aSelRow,
+ false );
+ bRet = aSelRow.IsSelected();
+ }
+ else
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleColumnSelected(
+ sal_Int32 nColumn )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ GetTableData().CheckRowAndCol( 0, nColumn, this );
+
+ bool bRet;
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ SwAccSingleTableSelHandler_Impl aSelCol;
+
+ GetTableData().GetSelection( nColumn, nColumn+1, *pSelBoxes, aSelCol,
+ true );
+ bRet = aSelCol.IsSelected();
+ }
+ else
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleTable::getAccessibleCellAt(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ uno::Reference< XAccessible > xRet;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pCellFrame =
+ GetTableData().GetCell( nRow, nColumn, this );
+ if( pCellFrame )
+ xRet = GetMap()->GetContext( pCellFrame );
+
+ return xRet;
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleTable::getAccessibleCaption()
+{
+ // captions aren't supported
+ return uno::Reference< XAccessible >();
+}
+
+uno::Reference< XAccessible > SAL_CALL SwAccessibleTable::getAccessibleSummary()
+{
+ // summaries aren't supported
+ return uno::Reference< XAccessible >();
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleSelected(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ bool bRet = false;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ const SwFrame *pFrame =
+ GetTableData().GetCell( nRow, nColumn, this );
+ if( pFrame && pFrame->IsCellFrame() )
+ {
+ const SwSelBoxes *pSelBoxes = GetSelBoxes();
+ if( pSelBoxes )
+ {
+ const SwCellFrame *pCFrame = static_cast < const SwCellFrame * >( pFrame );
+ SwTableBox *pBox =
+ const_cast< SwTableBox *>( pCFrame->GetTabBox() );
+ bRet = pSelBoxes->find( pBox ) != pSelBoxes->end();
+ }
+ }
+
+ return bRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleIndex(
+ sal_Int32 nRow, sal_Int32 nColumn )
+{
+ sal_Int32 nRet = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwAccessibleChild aCell( GetTableData().GetCell( nRow, nColumn, this ));
+ if ( aCell.IsValid() )
+ {
+ nRet = GetChildIndex( *(GetMap()), aCell );
+ }
+
+ return nRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleRow( sal_Int32 nChildIndex )
+{
+ sal_Int32 nRet = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // #i77106#
+ if ( ( nChildIndex < 0 ) ||
+ ( nChildIndex >= getAccessibleChildCount() ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ SwAccessibleChild aCell( GetChild( *(GetMap()), nChildIndex ) );
+ if ( aCell.GetSwFrame() )
+ {
+ sal_Int32 nTop = aCell.GetSwFrame()->getFrameArea().Top();
+ nTop -= GetFrame()->getFrameArea().Top();
+ Int32Set_Impl::const_iterator aRow(
+ GetTableData().GetRows().lower_bound( nTop ) );
+ nRet = static_cast< sal_Int32 >( std::distance(
+ GetTableData().GetRows().begin(), aRow ) );
+ }
+ else
+ {
+ OSL_ENSURE( !aCell.IsValid(), "SwAccessibleTable::getAccessibleColumn:"
+ "aCell not expected to be valid.");
+
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return nRet;
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getAccessibleColumn(
+ sal_Int32 nChildIndex )
+{
+ sal_Int32 nRet = -1;
+
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // #i77106#
+ if ( ( nChildIndex < 0 ) ||
+ ( nChildIndex >= getAccessibleChildCount() ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ SwAccessibleChild aCell( GetChild( *(GetMap()), nChildIndex ) );
+ if ( aCell.GetSwFrame() )
+ {
+ sal_Int32 nLeft = aCell.GetSwFrame()->getFrameArea().Left();
+ nLeft -= GetFrame()->getFrameArea().Left();
+ Int32Set_Impl::const_iterator aCol(
+ GetTableData().GetColumns().lower_bound( nLeft ) );
+ nRet = static_cast< sal_Int32 >( std::distance(
+ GetTableData().GetColumns().begin(), aCol ) );
+ }
+ else
+ {
+ OSL_ENSURE( !aCell.IsValid(), "SwAccessibleTable::getAccessibleColumn:"
+ "aCell not expected to be valid.");
+
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return nRet;
+}
+
+OUString SAL_CALL SwAccessibleTable::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.SwAccessibleTableView";
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::supportsService(
+ const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleTable::getSupportedServiceNames()
+{
+ return { "com.sun.star.table.AccessibleTableView", sAccessibleServiceName };
+}
+
+void SwAccessibleTable::InvalidatePosOrSize( const SwRect& rOldBox )
+{
+ SolarMutexGuard aGuard;
+
+ //need to update children
+ std::unique_ptr<SwAccessibleTableData_Impl> pNewTableData = CreateNewTableData();
+ if( !pNewTableData->CompareExtents( GetTableData() ) )
+ {
+ mpTableData = std::move(pNewTableData);
+ FireTableChangeEvent(*mpTableData);
+ }
+ if( HasTableData() )
+ GetTableData().SetTablePos( GetFrame()->getFrameArea().Pos() );
+
+ SwAccessibleContext::InvalidatePosOrSize( rOldBox );
+}
+
+void SwAccessibleTable::Dispose(bool bRecursive, bool bCanSkipInvisible)
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+ SwAccessibleContext::Dispose(bRecursive, bCanSkipInvisible);
+}
+
+void SwAccessibleTable::DisposeChild( const SwAccessibleChild& rChildFrameOrObj,
+ bool bRecursive, bool bCanSkipInvisible )
+{
+ SolarMutexGuard aGuard;
+
+ const SwFrame *pFrame = rChildFrameOrObj.GetSwFrame();
+ OSL_ENSURE( pFrame, "frame expected" );
+ if( HasTableData() )
+ {
+ FireTableChangeEvent( GetTableData() );
+ ClearTableData();
+ }
+
+ // There are two reason why this method has been called. The first one
+ // is there is no context for pFrame. The method is then called by
+ // the map, and we have to call our superclass.
+ // The other situation is that we have been call by a call to get notified
+ // about its change. We then must not call the superclass
+ uno::Reference< XAccessible > xAcc( GetMap()->GetContext( pFrame, false ) );
+ if( !xAcc.is() )
+ SwAccessibleContext::DisposeChild( rChildFrameOrObj, bRecursive, bCanSkipInvisible );
+}
+
+void SwAccessibleTable::InvalidateChildPosOrSize( const SwAccessibleChild& rChildFrameOrObj,
+ const SwRect& rOldBox )
+{
+ SolarMutexGuard aGuard;
+
+ if( HasTableData() )
+ {
+ SAL_WARN_IF( HasTableData() &&
+ GetFrame()->getFrameArea().Pos() != GetTableData().GetTablePos(),
+ "sw.a11y", "table has invalid position" );
+ if( HasTableData() )
+ {
+ std::unique_ptr<SwAccessibleTableData_Impl> pNewTableData = CreateNewTableData(); // #i77106#
+ if( !pNewTableData->CompareExtents( GetTableData() ) )
+ {
+ if (pNewTableData->GetRowCount() != mpTableData->GetRowCount()
+ && 1 < GetTableData().GetRowCount())
+ {
+ Int32Set_Impl::const_iterator aSttCol( GetTableData().GetColumnIter( 0 ) );
+ Int32Set_Impl::const_iterator aSttRow( GetTableData().GetRowIter( 1 ) );
+ const SwFrame *pCellFrame = GetTableData().GetCellAtPos( *aSttCol, *aSttRow );
+ Int32Set_Impl::const_iterator aSttCol2( pNewTableData->GetColumnIter( 0 ) );
+ Int32Set_Impl::const_iterator aSttRow2( pNewTableData->GetRowIter( 0 ) );
+ const SwFrame *pCellFrame2 = pNewTableData->GetCellAtPos( *aSttCol2, *aSttRow2 );
+
+ if(pCellFrame == pCellFrame2)
+ {
+ AccessibleTableModelChange aModelChange;
+ aModelChange.Type = AccessibleTableModelChangeType::UPDATE;
+ aModelChange.FirstRow = 0;
+ aModelChange.LastRow = mpTableData->GetRowCount() - 1;
+ aModelChange.FirstColumn = 0;
+ aModelChange.LastColumn = mpTableData->GetColumnCount() - 1;
+
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED;
+ aEvent.NewValue <<= aModelChange;
+
+ FireAccessibleEvent( aEvent );
+ }
+ }
+ else
+ FireTableChangeEvent( GetTableData() );
+ ClearTableData();
+ mpTableData = std::move(pNewTableData);
+ }
+ }
+ }
+
+ // #i013961# - always call super class method
+ SwAccessibleContext::InvalidateChildPosOrSize( rChildFrameOrObj, rOldBox );
+}
+
+// XAccessibleSelection
+
+void SAL_CALL SwAccessibleTable::selectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( (nChildIndex < 0) || (nChildIndex >= getAccessibleChildCount()) ) // #i77106#
+ throw lang::IndexOutOfBoundsException();
+
+ // preliminaries: get 'our' table box, and get the cursor shell
+ const SwTableBox* pBox = GetTableBox( nChildIndex );
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell == nullptr )
+ return;
+
+ // assure, that child, identified by the given index, isn't already selected.
+ if ( IsChildSelected( nChildIndex ) )
+ {
+ return;
+ }
+
+ // now we can start to do the work: check whether we already have
+ // a table selection (in 'our' table). If so, extend the
+ // selection, else select the current cell.
+
+ // if we have a selection in a table, check if it's in the
+ // same table that we're trying to select in
+ const SwTableNode* pSelectedTable = pCursorShell->IsCursorInTable();
+ if( pSelectedTable != nullptr )
+ {
+ // get top-most table line
+ const SwTableLine* pUpper = pBox->GetUpper();
+ while( pUpper->GetUpper() != nullptr )
+ pUpper = pUpper->GetUpper()->GetUpper();
+ sal_uInt16 nPos =
+ pSelectedTable->GetTable().GetTabLines().GetPos( pUpper );
+ if( nPos == USHRT_MAX )
+ pSelectedTable = nullptr;
+ }
+
+ // create the new selection
+ const SwStartNode* pStartNode = pBox->GetSttNd();
+ if( pSelectedTable == nullptr || !pCursorShell->GetTableCrs() )
+ {
+ pCursorShell->StartAction();
+ // Set cursor into current cell. This deletes any table cursor.
+ SwPaM aPaM( *pStartNode );
+ aPaM.Move( fnMoveForward, GoInNode );
+ Select( aPaM );
+ // Move cursor to the end of the table creating a selection and a table
+ // cursor.
+ pCursorShell->SetMark();
+ pCursorShell->MoveTable( GotoCurrTable, fnTableEnd );
+ // now set the cursor into the cell again.
+ SwPaM *pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ *pPaM->GetPoint() = *pPaM->GetMark();
+ pCursorShell->EndAction();
+ // we now have one cell selected!
+ }
+ else
+ {
+ // if the cursor is already in this table,
+ // expand the current selection (i.e., set
+ // point to new position; keep mark)
+ SwPaM aPaM( *pStartNode );
+ aPaM.Move( fnMoveForward, GoInNode );
+ aPaM.SetMark();
+ const SwPaM *pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ *(aPaM.GetMark()) = *pPaM->GetMark();
+ Select( aPaM );
+
+ }
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::isAccessibleChildSelected(
+ sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if( (nChildIndex < 0) || (nChildIndex >= getAccessibleChildCount()) ) // #i77106#
+ throw lang::IndexOutOfBoundsException();
+
+ return IsChildSelected( nChildIndex );
+}
+
+void SAL_CALL SwAccessibleTable::clearAccessibleSelection( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ pCursorShell->StartAction();
+ pCursorShell->ClearMark();
+ pCursorShell->EndAction();
+ }
+}
+
+void SAL_CALL SwAccessibleTable::selectAllAccessibleChildren( )
+{
+ // first clear selection, then select first and last child
+ clearAccessibleSelection();
+ selectAccessibleChild( 0 );
+ selectAccessibleChild( getAccessibleChildCount()-1 ); // #i77106#
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getSelectedAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // iterate over all children and count isAccessibleChildSelected()
+ sal_Int32 nCount = 0;
+
+ sal_Int32 nChildren = getAccessibleChildCount(); // #i71106#
+ for( sal_Int32 n = 0; n < nChildren; n++ )
+ if( IsChildSelected( n ) )
+ nCount++;
+
+ return nCount;
+}
+
+uno::Reference<XAccessible> SAL_CALL SwAccessibleTable::getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // parameter checking (part 1): index lower 0
+ if( nSelectedChildIndex < 0 )
+ throw lang::IndexOutOfBoundsException();
+
+ sal_Int32 nChildIndex = GetIndexOfSelectedChild( nSelectedChildIndex );
+
+ // parameter checking (part 2): index higher than selected children?
+ if( nChildIndex < 0 )
+ throw lang::IndexOutOfBoundsException();
+
+ // #i77106#
+ if ( nChildIndex >= getAccessibleChildCount() )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return getAccessibleChild( nChildIndex );
+}
+
+// index has to be treated as global child index.
+void SAL_CALL SwAccessibleTable::deselectAccessibleChild(
+ sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ SwCursorShell* pCursorShell = GetCursorShell();
+
+ // index has to be treated as global child index
+ if ( !pCursorShell )
+ throw lang::IndexOutOfBoundsException();
+
+ // assure, that given child index is in bounds.
+ if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() ) // #i77106#
+ throw lang::IndexOutOfBoundsException();
+
+ // assure, that child, identified by the given index, is selected.
+ if ( !IsChildSelected( nChildIndex ) )
+ return;
+
+ const SwTableBox* pBox = GetTableBox( nChildIndex );
+ OSL_ENSURE( pBox != nullptr, "We need the table box." );
+
+ // If we unselect point, then set cursor to mark. If we clear another
+ // selected box, then set cursor to point.
+ // reduce selection to mark.
+ SwPaM *pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ bool bDeselectPoint =
+ pBox->GetSttNd() ==
+ pPaM->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+
+ SwPaM aPaM( bDeselectPoint ? *pPaM->GetMark() : *pPaM->GetPoint() );
+
+ pCursorShell->StartAction();
+
+ // Set cursor into either point or mark
+ Select( aPaM );
+ // Move cursor to the end of the table creating a selection and a table
+ // cursor.
+ pCursorShell->SetMark();
+ pCursorShell->MoveTable( GotoCurrTable, fnTableEnd );
+ // now set the cursor into the cell again.
+ pPaM = pCursorShell->GetTableCrs() ? pCursorShell->GetTableCrs()
+ : pCursorShell->GetCursor();
+ *pPaM->GetPoint() = *pPaM->GetMark();
+ pCursorShell->EndAction();
+}
+
+sal_Int32 SAL_CALL SwAccessibleTable::getBackground()
+{
+ const SvxBrushItem &rBack = GetFrame()->GetAttrSet()->GetBackground();
+ Color crBack = rBack.GetColor();
+
+ if (COL_AUTO == crBack)
+ {
+ uno::Reference<XAccessible> xAccDoc = getAccessibleParent();
+ if (xAccDoc.is())
+ {
+ uno::Reference<XAccessibleComponent> xComponentDoc(xAccDoc,uno::UNO_QUERY);
+ if (xComponentDoc.is())
+ {
+ crBack = Color(ColorTransparency, xComponentDoc->getBackground());
+ }
+ }
+ }
+ return sal_Int32(crBack);
+}
+
+void SwAccessibleTable::FireSelectionEvent( )
+{
+ AccessibleEventObject aEvent;
+
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_REMOVE;
+
+ for (const auto& rCell : m_vecCellRemove)
+ {
+ // fdo#57197: check if the object is still alive
+ uno::Reference<XAccessible> const xAcc(rCell.second);
+ if (xAcc.is())
+ {
+ SwAccessibleContext *const pAccCell(rCell.first);
+ assert(pAccCell);
+ pAccCell->FireAccessibleEvent(aEvent);
+ }
+ }
+
+ if (m_vecCellAdd.size() <= SELECTION_WITH_NUM)
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_ADD;
+ for (const auto& rCell : m_vecCellAdd)
+ {
+ // fdo#57197: check if the object is still alive
+ uno::Reference<XAccessible> const xAcc(rCell.second);
+ if (xAcc.is())
+ {
+ SwAccessibleContext *const pAccCell(rCell.first);
+ assert(pAccCell);
+ pAccCell->FireAccessibleEvent(aEvent);
+ }
+ }
+ return ;
+ }
+ else
+ {
+ aEvent.EventId = AccessibleEventId::SELECTION_CHANGED_WITHIN;
+ FireAccessibleEvent(aEvent);
+ }
+}
+
+void SwAccessibleTable::AddSelectionCell(
+ SwAccessibleContext *const pAccCell, bool const bAddOrRemove)
+{
+ uno::Reference<XAccessible> const xTmp(pAccCell);
+ if (bAddOrRemove)
+ {
+ m_vecCellAdd.emplace_back(pAccCell, xTmp);
+ }
+ else
+ {
+ m_vecCellRemove.emplace_back(pAccCell, xTmp);
+ }
+}
+
+// XAccessibleTableSelection
+sal_Bool SAL_CALL SwAccessibleTable::selectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleColumnSelected( row ) )
+ return true;
+
+ tools::Long lColumnCount = getAccessibleColumnCount();
+ for(tools::Long lCol = 0; lCol < lColumnCount; lCol ++)
+ {
+ tools::Long lChildIndex = getAccessibleIndex(row, lCol);
+ selectAccessibleChild(lChildIndex);
+ }
+
+ return true;
+}
+sal_Bool SAL_CALL SwAccessibleTable::selectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleColumnSelected( column ) )
+ return true;
+
+ sal_Int32 lRowCount = getAccessibleRowCount();
+
+ for(sal_Int32 lRow = 0; lRow < lRowCount; lRow ++)
+ {
+ sal_Int32 lChildIndex = getAccessibleIndex(lRow, column);
+ selectAccessibleChild(lChildIndex);
+ }
+ return true;
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::unselectRow( sal_Int32 row )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleSelected( row , 0 ) && isAccessibleSelected( row , getAccessibleColumnCount()-1 ) )
+ {
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ pCursorShell->StartAction();
+ pCursorShell->ClearMark();
+ pCursorShell->EndAction();
+ return true;
+ }
+ }
+ return true;
+}
+
+sal_Bool SAL_CALL SwAccessibleTable::unselectColumn( sal_Int32 column )
+{
+ SolarMutexGuard g;
+
+ if( isAccessibleSelected( 0 , column ) && isAccessibleSelected( getAccessibleRowCount()-1,column))
+ {
+ SwCursorShell* pCursorShell = GetCursorShell();
+ if( pCursorShell != nullptr )
+ {
+ pCursorShell->StartAction();
+ pCursorShell->ClearMark();
+ pCursorShell->EndAction();
+ return true;
+ }
+ }
+ return true;
+}
+
+// #i77106# - implementation of class <SwAccessibleTableColHeaders>
+SwAccessibleTableColHeaders::SwAccessibleTableColHeaders(
+ std::shared_ptr<SwAccessibleMap> const& pMap,
+ const SwTabFrame *const pTabFrame)
+ : SwAccessibleTable(pMap, pTabFrame)
+{
+ SolarMutexGuard aGuard;
+
+ const SwFrameFormat* pFrameFormat = pTabFrame->GetFormat();
+ if(pFrameFormat)
+ StartListening(const_cast<SwFrameFormat*>(pFrameFormat)->GetNotifier());
+ const OUString aName = pFrameFormat->GetName() + "-ColumnHeaders";
+
+ SetName( aName + "-" + OUString::number( pTabFrame->GetPhyPageNum() ) );
+
+ const OUString sArg2( GetFormattedPageNumber() );
+
+ SetDesc( GetResource( STR_ACCESS_TABLE_DESC, &aName, &sArg2 ) );
+
+ NotRegisteredAtAccessibleMap(); // #i85634#
+}
+
+std::unique_ptr<SwAccessibleTableData_Impl> SwAccessibleTableColHeaders::CreateNewTableData()
+{
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>( GetFrame() );
+ return std::unique_ptr<SwAccessibleTableData_Impl>(new SwAccessibleTableData_Impl( *(GetMap()), pTabFrame, IsInPagePreview(), true ));
+}
+
+void SwAccessibleTableColHeaders::Notify(const SfxHint& )
+{
+}
+
+// XInterface
+uno::Any SAL_CALL SwAccessibleTableColHeaders::queryInterface( const uno::Type& aType )
+{
+ return SwAccessibleTable::queryInterface( aType );
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL SwAccessibleTableColHeaders::getAccessibleChildCount()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ sal_Int32 nCount = 0;
+
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>( GetFrame() );
+ const SwAccessibleChildSList aVisList( GetVisArea(), *pTabFrame, *(GetMap()) );
+ SwAccessibleChildSList::const_iterator aIter( aVisList.begin() );
+ while( aIter != aVisList.end() )
+ {
+ const SwAccessibleChild& rLower = *aIter;
+ if( rLower.IsAccessible( IsInPagePreview() ) )
+ {
+ nCount++;
+ }
+ else if( rLower.GetSwFrame() )
+ {
+ // There are no unaccessible SdrObjects that count
+ if ( !rLower.GetSwFrame()->IsRowFrame() ||
+ pTabFrame->IsInHeadline( *(rLower.GetSwFrame()) ) )
+ {
+ nCount += SwAccessibleFrame::GetChildCount( *(GetMap()),
+ GetVisArea(),
+ rLower.GetSwFrame(),
+ IsInPagePreview() );
+ }
+ }
+ ++aIter;
+ }
+
+ return nCount;
+}
+
+uno::Reference< XAccessible> SAL_CALL
+ SwAccessibleTableColHeaders::getAccessibleChild (sal_Int32 nIndex)
+{
+ if ( nIndex < 0 || nIndex >= getAccessibleChildCount() )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ return SwAccessibleTable::getAccessibleChild( nIndex );
+}
+
+// XAccessibleTable
+uno::Reference< XAccessibleTable >
+ SAL_CALL SwAccessibleTableColHeaders::getAccessibleRowHeaders()
+{
+ return uno::Reference< XAccessibleTable >();
+}
+
+uno::Reference< XAccessibleTable >
+ SAL_CALL SwAccessibleTableColHeaders::getAccessibleColumnHeaders()
+{
+ return uno::Reference< XAccessibleTable >();
+}
+
+// XServiceInfo
+
+OUString SAL_CALL SwAccessibleTableColHeaders::getImplementationName()
+{
+ static constexpr OUStringLiteral sImplName
+ = u"com.sun.star.comp.Writer.SwAccessibleTableColumnHeadersView";
+ return sImplName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctable.hxx b/sw/source/core/access/acctable.hxx
new file mode 100644
index 000000000..c31ecca95
--- /dev/null
+++ b/sw/source/core/access/acctable.hxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <com/sun/star/accessibility/XAccessibleTableSelection.hpp>
+#include <vector>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+
+#include <svl/listener.hxx>
+
+#include "acccontext.hxx"
+
+class SwTabFrame;
+class SwAccessibleTableData_Impl;
+class SwTableBox;
+class SwSelBoxes;
+
+namespace sw::access {
+ class SwAccessibleChild;
+}
+
+class SwAccessibleTable :
+ public SwAccessibleContext,
+ public css::accessibility::XAccessibleTable,
+ public css::accessibility::XAccessibleSelection,
+ public css::accessibility::XAccessibleTableSelection,
+ public SvtListener
+{
+ std::unique_ptr<SwAccessibleTableData_Impl> mpTableData; // the table's data, protected by SolarMutex
+ OUString m_sDesc;
+ const SwSelBoxes *GetSelBoxes() const;
+
+ void FireTableChangeEvent( const SwAccessibleTableData_Impl& rTableData );
+
+ /** get the SwTableBox* for the given child */
+ const SwTableBox* GetTableBox( sal_Int32 ) const;
+
+ bool IsChildSelected( sal_Int32 nChildIndex ) const;
+
+ sal_Int32 GetIndexOfSelectedChild( sal_Int32 nSelectedChildIndex ) const;
+
+protected:
+ // Set states for getAccessibleStateSet.
+ // This derived class additionally sets MULTISELECTABLE(+)
+ virtual void GetStates( ::utl::AccessibleStateSetHelper& rStateSet ) override;
+
+ virtual ~SwAccessibleTable() override;
+
+ // #i77106#
+ void SetDesc( const OUString& sNewDesc )
+ {
+ m_sDesc = sNewDesc;
+ }
+
+ virtual std::unique_ptr<SwAccessibleTableData_Impl> CreateNewTableData(); // #i77106#
+
+ // force update of table data
+ void UpdateTableData();
+
+ // remove the current table data
+ void ClearTableData();
+
+ // get table data, update if necessary
+ inline SwAccessibleTableData_Impl& GetTableData();
+
+ // Is table data evailable?
+ bool HasTableData() const { return (mpTableData != nullptr); }
+
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleTable(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwTabFrame* pTableFrame);
+
+ // XInterface
+
+ // (XInterface methods need to be implemented to disambiguate
+ // between those inherited through SwAccessibleContext and
+ // XAccessibleTable).
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ virtual void SAL_CALL acquire( ) noexcept override
+ { SwAccessibleContext::acquire(); };
+
+ virtual void SAL_CALL release( ) noexcept override
+ { SwAccessibleContext::release(); };
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleContext
+
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XAccessibleTable
+
+ virtual sal_Int32 SAL_CALL getAccessibleRowCount() override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumnCount( ) override;
+ virtual OUString SAL_CALL getAccessibleRowDescription(
+ sal_Int32 nRow ) override;
+ virtual OUString SAL_CALL getAccessibleColumnDescription(
+ sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleRowExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumnExtentAt(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleRowHeaders( ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleColumnHeaders( ) override;
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleRows( ) override;
+ virtual css::uno::Sequence< sal_Int32 > SAL_CALL
+ getSelectedAccessibleColumns( ) override;
+ virtual sal_Bool SAL_CALL isAccessibleRowSelected( sal_Int32 nRow ) override;
+ virtual sal_Bool SAL_CALL isAccessibleColumnSelected( sal_Int32 nColumn ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getAccessibleCaption( ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessible > SAL_CALL
+ getAccessibleSummary( ) override;
+ virtual sal_Bool SAL_CALL isAccessibleSelected(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndex(
+ sal_Int32 nRow, sal_Int32 nColumn ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleRow( sal_Int32 nChildIndex ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleColumn( sal_Int32 nChildIndex ) override;
+ // XAccessibleTableSelection
+ virtual sal_Bool SAL_CALL selectRow( sal_Int32 row ) override ;
+ virtual sal_Bool SAL_CALL selectColumn( sal_Int32 column ) override ;
+ virtual sal_Bool SAL_CALL unselectRow( sal_Int32 row ) override;
+ virtual sal_Bool SAL_CALL unselectColumn( sal_Int32 column ) override;
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // C++ interface
+
+ // The object has been moved by the layout
+ virtual void InvalidatePosOrSize( const SwRect& rOldBox ) override;
+
+ // The object is not visible any longer and should be destroyed
+ virtual void Dispose(bool bRecursive, bool bCanSkipInvisible = true) override;
+
+ virtual void DisposeChild( const sw::access::SwAccessibleChild& rFrameOrObj,
+ bool bRecursive, bool bCanSkipInvisible ) override;
+ virtual void InvalidateChildPosOrSize( const sw::access::SwAccessibleChild& rFrameOrObj,
+ const SwRect& rFrame ) override;
+
+ // XAccessibleSelection
+
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int32 nChildIndex ) override;
+
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int32 SAL_CALL getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex ) override;
+
+ // index has to be treated as global child index.
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ // XAccessibleComponent
+ sal_Int32 SAL_CALL getBackground() override;
+ typedef std::vector< std::pair<SwAccessibleContext*,
+ css::uno::WeakReference<css::accessibility::XAccessible> > > Cells_t;
+ Cells_t m_vecCellAdd;
+ Cells_t m_vecCellRemove;
+ void FireSelectionEvent( );
+ void AddSelectionCell(SwAccessibleContext*, bool bAddOrRemove);
+};
+
+inline SwAccessibleTableData_Impl& SwAccessibleTable::GetTableData()
+{
+ if( !mpTableData )
+ UpdateTableData();
+ return *mpTableData;
+}
+
+// #i77106# - subclass to represent table column headers
+class SwAccessibleTableColHeaders : public SwAccessibleTable
+{
+protected:
+ virtual ~SwAccessibleTableColHeaders() override
+ {}
+
+ virtual std::unique_ptr<SwAccessibleTableData_Impl> CreateNewTableData() override;
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleTableColHeaders(std::shared_ptr<SwAccessibleMap> const& pMap,
+ const SwTabFrame *pTabFrame);
+
+ // XInterface
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& aType ) override;
+
+ // XAccessibleContext
+
+ /// Return the number of currently visible children.
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+
+ /// Return the specified child or NULL if index is invalid.
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL
+ getAccessibleChild (sal_Int32 nIndex) override;
+
+ // XAccessibleTable
+
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleRowHeaders( ) override;
+ virtual css::uno::Reference<
+ css::accessibility::XAccessibleTable >
+ SAL_CALL getAccessibleColumnHeaders( ) override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctextframe.cxx b/sw/source/core/access/acctextframe.cxx
new file mode 100644
index 000000000..a5d6d43df
--- /dev/null
+++ b/sw/source/core/access/acctextframe.cxx
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleRelation.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <frmfmt.hxx>
+#include <flyfrm.hxx>
+#include <accmap.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <hints.hxx>
+#include "acctextframe.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+using utl::AccessibleRelationSetHelper;
+using ::com::sun::star::accessibility::XAccessibleContext;
+
+SwAccessibleTextFrame::SwAccessibleTextFrame(
+ std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame& rFlyFrame ) :
+ SwAccessibleFrameBase( pInitMap, AccessibleRole::TEXT_FRAME, &rFlyFrame )
+{
+ const SwFlyFrameFormat* pFlyFrameFormat = rFlyFrame.GetFormat();
+ msTitle = pFlyFrameFormat->GetObjTitle();
+
+ msDesc = pFlyFrameFormat->GetObjDescription();
+ if ( msDesc.isEmpty() &&
+ msTitle != GetName() )
+ {
+ msDesc = msTitle;
+ }
+}
+
+SwAccessibleTextFrame::~SwAccessibleTextFrame()
+{
+}
+
+void SwAccessibleTextFrame::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ EndListeningAll();
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyModifyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyModifyHint->GetWhich();
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(GetFrame());
+ switch(nWhich)
+ {
+ // #i73249#
+ case RES_TITLE_CHANGED:
+ {
+ OUString sOldTitle, sNewTitle;
+ const SwStringMsgPoolItem *pOldItem = dynamic_cast<const SwStringMsgPoolItem*>(pLegacyModifyHint->m_pOld);
+ if(pOldItem)
+ sOldTitle = pOldItem->GetString();
+ const SwStringMsgPoolItem *pNewItem = dynamic_cast<const SwStringMsgPoolItem*>(pLegacyModifyHint->m_pNew);
+ if(pNewItem)
+ sNewTitle = pNewItem->GetString();
+ if(sOldTitle == sNewTitle)
+ break;
+ msTitle = sNewTitle;
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::NAME_CHANGED;
+ aEvent.OldValue <<= sOldTitle;
+ aEvent.NewValue <<= msTitle;
+ FireAccessibleEvent( aEvent );
+
+ const SwFlyFrameFormat* pFlyFrameFormat = pFlyFrame->GetFormat();
+ if(!pFlyFrameFormat || !pFlyFrameFormat->GetObjDescription().isEmpty())
+ break;
+ [[fallthrough]];
+ }
+ case RES_DESCRIPTION_CHANGED:
+ {
+ if(pFlyFrame)
+ {
+ const OUString sOldDesc(msDesc);
+
+ const SwFlyFrameFormat* pFlyFrameFormat = pFlyFrame->GetFormat();
+ const OUString& rDesc = pFlyFrameFormat->GetObjDescription();
+ msDesc = rDesc;
+ if(msDesc.isEmpty() && msTitle != GetName())
+ msDesc = msTitle;
+
+ if(msDesc != sOldDesc)
+ {
+ AccessibleEventObject aEvent;
+ aEvent.EventId = AccessibleEventId::DESCRIPTION_CHANGED;
+ aEvent.OldValue <<= sOldDesc;
+ aEvent.NewValue <<= msDesc;
+ FireAccessibleEvent(aEvent);
+ }
+ }
+ }
+ }
+ }
+}
+
+// XInterface
+
+css::uno::Any SAL_CALL
+ SwAccessibleTextFrame::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = SwAccessibleContext::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast< css::accessibility::XAccessibleSelection* >(this)
+ );
+ return aReturn;
+}
+
+void SAL_CALL
+ SwAccessibleTextFrame::acquire()
+ noexcept
+{
+ SwAccessibleContext::acquire ();
+}
+
+void SAL_CALL
+ SwAccessibleTextFrame::release()
+ noexcept
+{
+ SwAccessibleContext::release ();
+}
+
+// XAccessibleSelection
+
+void SAL_CALL SwAccessibleTextFrame::selectAccessibleChild( sal_Int32 )
+{
+ SAL_WARN("sw.a11y", "<SwAccessibleTextFrame::selectAccessibleChild( sal_Int32 )> - missing implementation");
+}
+
+sal_Bool SAL_CALL SwAccessibleTextFrame::isAccessibleChildSelected( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard g;
+
+ uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
+ uno::Reference<XAccessibleContext> xContext;
+ if( xAcc.is() )
+ xContext = xAcc->getAccessibleContext();
+
+ if( xContext.is() )
+ {
+ if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
+ {
+ uno::Reference< css::accessibility::XAccessibleText >
+ xText(xAcc, uno::UNO_QUERY);
+ if( xText.is() )
+ {
+ if( xText->getSelectionStart() >= 0 ) return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void SAL_CALL SwAccessibleTextFrame::clearAccessibleSelection( )
+{
+ SAL_WARN("sw.a11y", "<SwAccessibleTextFrame::clearAccessibleSelection()> - missing implementation");
+}
+
+void SAL_CALL SwAccessibleTextFrame::selectAllAccessibleChildren( )
+{
+ SAL_WARN("sw.a11y", "<SwAccessibleTextFrame::selectAllAccessibleChildren()> - missing implementation");
+}
+
+sal_Int32 SAL_CALL SwAccessibleTextFrame::getSelectedAccessibleChildCount()
+{
+ sal_Int32 nCount = 0;
+ sal_Int32 TotalCount = getAccessibleChildCount();
+ for( sal_Int32 i = 0; i < TotalCount; i++ )
+ if( isAccessibleChildSelected(i) ) nCount++;
+
+ return nCount;
+}
+
+uno::Reference<XAccessible> SAL_CALL SwAccessibleTextFrame::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+{
+ SolarMutexGuard g;
+
+ if ( nSelectedChildIndex > getSelectedAccessibleChildCount() )
+ throw lang::IndexOutOfBoundsException();
+ sal_Int32 i1, i2;
+ for( i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ )
+ if( isAccessibleChildSelected(i1) )
+ {
+ if( i2 == nSelectedChildIndex )
+ return getAccessibleChild( i1 );
+ i2++;
+ }
+ return uno::Reference<XAccessible>();
+}
+
+void SAL_CALL SwAccessibleTextFrame::deselectAccessibleChild( sal_Int32 )
+{
+ SAL_WARN("sw.a11y", "<SwAccessibleTextFrame::selectAllAccessibleChildren( sal_Int32 )> - missing implementation");
+}
+
+// #i73249#
+OUString SAL_CALL SwAccessibleTextFrame::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ if ( !msTitle.isEmpty() )
+ {
+ return msTitle;
+ }
+
+ return SwAccessibleFrameBase::getAccessibleName();
+}
+
+OUString SAL_CALL SwAccessibleTextFrame::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ return msDesc;
+
+}
+
+OUString SAL_CALL SwAccessibleTextFrame::getImplementationName()
+{
+ return "com.sun.star.comp.Writer.SwAccessibleTextFrameView";
+}
+
+sal_Bool SAL_CALL SwAccessibleTextFrame::supportsService(const OUString& sTestServiceName)
+{
+ return cppu::supportsService(this, sTestServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwAccessibleTextFrame::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.AccessibleTextFrameView", sAccessibleServiceName };
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwAccessibleTextFrame::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XAccessibleRelationSet
+
+SwFlyFrame* SwAccessibleTextFrame::getFlyFrame() const
+{
+ SwFlyFrame* pFlyFrame = nullptr;
+
+ const SwFrame* pFrame = GetFrame();
+ assert(pFrame);
+ if( pFrame->IsFlyFrame() )
+ {
+ pFlyFrame = static_cast<SwFlyFrame*>( const_cast<SwFrame*>( pFrame ) );
+ }
+
+ return pFlyFrame;
+}
+
+AccessibleRelation SwAccessibleTextFrame::makeRelation( sal_Int16 nType, const SwFlyFrame* pFrame )
+{
+ uno::Sequence<uno::Reference<XInterface> > aSequence { GetMap()->GetContext( pFrame ) };
+ return AccessibleRelation( nType, aSequence );
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL SwAccessibleTextFrame::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+
+ ThrowIfDisposed();
+
+ // get the frame, and insert prev/next relations into helper
+
+ rtl::Reference<AccessibleRelationSetHelper> pHelper = new AccessibleRelationSetHelper();
+
+ SwFlyFrame* pFlyFrame = getFlyFrame();
+ assert(pFlyFrame);
+
+ const SwFlyFrame* pPrevFrame = pFlyFrame->GetPrevLink();
+ if( pPrevFrame != nullptr )
+ pHelper->AddRelation( makeRelation(
+ AccessibleRelationType::CONTENT_FLOWS_FROM, pPrevFrame ) );
+
+ const SwFlyFrame* pNextFrame = pFlyFrame->GetNextLink();
+ if( pNextFrame != nullptr )
+ pHelper->AddRelation( makeRelation(
+ AccessibleRelationType::CONTENT_FLOWS_TO, pNextFrame ) );
+
+ return pHelper;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/acctextframe.hxx b/sw/source/core/access/acctextframe.hxx
new file mode 100644
index 000000000..050970f62
--- /dev/null
+++ b/sw/source/core/access/acctextframe.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_ACCTEXTFRAME_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_ACCTEXTFRAME_HXX
+
+#include "accframebase.hxx"
+
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+
+class SwFlyFrame;
+
+class SwAccessibleTextFrame : public SwAccessibleFrameBase,
+ public css::accessibility::XAccessibleSelection
+{
+private:
+ // #i73249#
+ OUString msTitle;
+ OUString msDesc;
+
+protected:
+ virtual ~SwAccessibleTextFrame() override;
+ virtual void Notify(const SfxHint&) override;
+
+public:
+ SwAccessibleTextFrame(std::shared_ptr<SwAccessibleMap> const& pInitMap,
+ const SwFlyFrame& rFlyFrame);
+
+ virtual css::uno::Any SAL_CALL queryInterface(
+ css::uno::Type const & rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ // XAccessibleSelection
+ virtual void SAL_CALL selectAccessibleChild(
+ sal_Int32 nChildIndex ) override;
+
+ virtual sal_Bool SAL_CALL isAccessibleChildSelected(
+ sal_Int32 nChildIndex ) override;
+
+ virtual void SAL_CALL clearAccessibleSelection( ) override;
+
+ virtual void SAL_CALL selectAllAccessibleChildren( ) override;
+
+ virtual sal_Int32 SAL_CALL getSelectedAccessibleChildCount( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(
+ sal_Int32 nSelectedChildIndex ) override;
+
+ virtual void SAL_CALL deselectAccessibleChild(
+ sal_Int32 nSelectedChildIndex ) override;
+
+ // XAccessibleContext
+
+ // #i73249# - Return the object's current name.
+ virtual OUString SAL_CALL
+ getAccessibleName() override;
+ /// Return this object's description.
+ virtual OUString SAL_CALL
+ getAccessibleDescription() override;
+
+ // XServiceInfo
+
+ /** Returns an identifier for the implementation of this object.
+ */
+ virtual OUString SAL_CALL
+ getImplementationName() override;
+
+ /** Return whether the specified service is supported by this class.
+ */
+ virtual sal_Bool SAL_CALL
+ supportsService (const OUString& sServiceName) override;
+
+ /** Returns a list of all supported services. In this case that is just
+ the AccessibleContext service.
+ */
+ virtual css::uno::Sequence< OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XAccessibleContext::getAccessibleRelationSet
+
+ // text frame may have accessible relations to their
+ // predecessor/successor frames
+
+private:
+ // helper methods for getAccessibleRelationSet:
+ SwFlyFrame* getFlyFrame() const;
+
+ css::accessibility::AccessibleRelation makeRelation(
+ sal_Int16 nType, const SwFlyFrame* pFrame );
+
+public:
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL getAccessibleRelationSet() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/parachangetrackinginfo.cxx b/sw/source/core/access/parachangetrackinginfo.cxx
new file mode 100644
index 000000000..0943dc15e
--- /dev/null
+++ b/sw/source/core/access/parachangetrackinginfo.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "parachangetrackinginfo.hxx"
+
+#include <wrong.hxx>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <osl/diagnose.h>
+
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <ndtxt.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+
+#include <algorithm>
+
+namespace {
+ void initChangeTrackTextMarkupLists( const SwTextFrame& rTextFrame,
+ std::unique_ptr<SwWrongList>& opChangeTrackInsertionTextMarkupList,
+ std::unique_ptr<SwWrongList>& opChangeTrackDeletionTextMarkupList,
+ std::unique_ptr<SwWrongList>& opChangeTrackFormatChangeTextMarkupList )
+ {
+ opChangeTrackInsertionTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) );
+ opChangeTrackDeletionTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) );
+ opChangeTrackFormatChangeTextMarkupList.reset( new SwWrongList( WRONGLIST_CHANGETRACKING ) );
+
+ if (!rTextFrame.GetTextNodeFirst())
+ {
+ OSL_FAIL( "<initChangeTrackTextMarkupLists(..) - missing <SwTextNode> instance!" );
+ return;
+ }
+ // sw_redlinehide: the first node is sufficient - there are only
+ // multiple ones in Hide case and the code below returns early then
+ const SwTextNode& rTextNode(*(rTextFrame.GetTextNodeFirst()));
+
+ const IDocumentRedlineAccess& rIDocChangeTrack( rTextNode.getIDocumentRedlineAccess() );
+
+ if (!IDocumentRedlineAccess::IsShowChanges(rIDocChangeTrack.GetRedlineFlags())
+ || rTextFrame.getRootFrame()->IsHideRedlines()
+ || rIDocChangeTrack.GetRedlineTable().empty())
+ {
+ // nothing to do --> empty change track text markup lists.
+ return;
+ }
+
+ const SwRedlineTable::size_type nIdxOfFirstRedlineForTextNode =
+ rIDocChangeTrack.GetRedlinePos( rTextNode, RedlineType::Any );
+ if ( nIdxOfFirstRedlineForTextNode == SwRedlineTable::npos )
+ {
+ // nothing to do --> empty change track text markup lists.
+ return;
+ }
+
+ // sw_redlinehide: rely on the Hide early return above & cast
+ // TextFrameIndex to SwIndex directly
+ const sal_Int32 nTextFrameTextStartPos = rTextFrame.IsFollow()
+ ? sal_Int32(rTextFrame.GetOffset())
+ : 0;
+ const sal_Int32 nTextFrameTextEndPos = rTextFrame.HasFollow()
+ ? sal_Int32(rTextFrame.GetFollow()->GetOffset())
+ : rTextFrame.GetText().getLength();
+
+ // iteration over the redlines which overlap with the text node.
+ const SwRedlineTable& rRedlineTable = rIDocChangeTrack.GetRedlineTable();
+ const SwRedlineTable::size_type nRedlineCount( rRedlineTable.size() );
+ for ( SwRedlineTable::size_type nActRedline = nIdxOfFirstRedlineForTextNode;
+ nActRedline < nRedlineCount;
+ ++nActRedline)
+ {
+ const SwRangeRedline* pActRedline = rRedlineTable[ nActRedline ];
+ if ( pActRedline->Start()->nNode > rTextNode.GetIndex() )
+ {
+ break;
+ }
+
+ sal_Int32 nTextNodeChangeTrackStart(COMPLETE_STRING);
+ sal_Int32 nTextNodeChangeTrackEnd(COMPLETE_STRING);
+ pActRedline->CalcStartEnd( rTextNode.GetIndex(),
+ nTextNodeChangeTrackStart,
+ nTextNodeChangeTrackEnd );
+ if ( nTextNodeChangeTrackStart > nTextFrameTextEndPos ||
+ nTextNodeChangeTrackEnd < nTextFrameTextStartPos )
+ {
+ // Consider only redlines which overlap with the text frame's text.
+ continue;
+ }
+
+ SwWrongList* pMarkupList( nullptr );
+ switch ( pActRedline->GetType() )
+ {
+ case RedlineType::Insert:
+ {
+ pMarkupList = opChangeTrackInsertionTextMarkupList.get();
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ pMarkupList = opChangeTrackDeletionTextMarkupList.get();
+ }
+ break;
+ case RedlineType::Format:
+ {
+ pMarkupList = opChangeTrackFormatChangeTextMarkupList.get();
+ }
+ break;
+ default:
+ {
+ // other types are not considered
+ }
+ }
+ if ( pMarkupList )
+ {
+ const sal_Int32 nTextFrameChangeTrackStart =
+ std::max(nTextNodeChangeTrackStart, nTextFrameTextStartPos);
+
+ const sal_Int32 nTextFrameChangeTrackEnd =
+ std::min(nTextNodeChangeTrackEnd, nTextFrameTextEndPos);
+
+ pMarkupList->Insert( OUString(), nullptr,
+ nTextFrameChangeTrackStart,
+ nTextFrameChangeTrackEnd - nTextFrameChangeTrackStart,
+ pMarkupList->Count() );
+ }
+ } // eof iteration over the redlines which overlap with the text node
+ }
+} // eof anonymous namespace
+
+SwParaChangeTrackingInfo::SwParaChangeTrackingInfo( const SwTextFrame& rTextFrame )
+ : mrTextFrame( rTextFrame )
+{
+}
+
+SwParaChangeTrackingInfo::~SwParaChangeTrackingInfo()
+{
+ reset();
+}
+
+void SwParaChangeTrackingInfo::reset()
+{
+ mpChangeTrackInsertionTextMarkupList.reset();
+ mpChangeTrackDeletionTextMarkupList.reset();
+ mpChangeTrackFormatChangeTextMarkupList.reset();
+}
+
+const SwWrongList* SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList( const sal_Int32 nTextMarkupType )
+{
+ SwWrongList* pChangeTrackingTextMarkupList = nullptr;
+
+ if ( mpChangeTrackInsertionTextMarkupList == nullptr )
+ {
+ OSL_ENSURE( mpChangeTrackDeletionTextMarkupList == nullptr,
+ "<SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList(..) - <mpChangeTrackDeletionTextMarkupList> expected to be NULL." );
+ OSL_ENSURE( mpChangeTrackFormatChangeTextMarkupList == nullptr,
+ "<SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList(..) - <mpChangeTrackFormatChangeTextMarkupList> expected to be NULL." );
+ initChangeTrackTextMarkupLists( mrTextFrame,
+ mpChangeTrackInsertionTextMarkupList,
+ mpChangeTrackDeletionTextMarkupList,
+ mpChangeTrackFormatChangeTextMarkupList );
+ }
+
+ switch ( nTextMarkupType )
+ {
+ case css::text::TextMarkupType::TRACK_CHANGE_INSERTION:
+ {
+ pChangeTrackingTextMarkupList = mpChangeTrackInsertionTextMarkupList.get();
+ }
+ break;
+ case css::text::TextMarkupType::TRACK_CHANGE_DELETION:
+ {
+ pChangeTrackingTextMarkupList = mpChangeTrackDeletionTextMarkupList.get();
+ }
+ break;
+ case css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
+ {
+ pChangeTrackingTextMarkupList = mpChangeTrackFormatChangeTextMarkupList.get();
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwParaChangeTrackingInfo::getChangeTrackingTextMarkupList(..)> - misusage - unexpected text markup type for change tracking." );
+ }
+ }
+
+ return pChangeTrackingTextMarkupList;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/parachangetrackinginfo.hxx b/sw/source/core/access/parachangetrackinginfo.hxx
new file mode 100644
index 000000000..30f020125
--- /dev/null
+++ b/sw/source/core/access/parachangetrackinginfo.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_PARACHANGETRACKINGINFO_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_PARACHANGETRACKINGINFO_HXX
+
+#include <sal/types.h>
+#include <memory>
+
+class SwTextFrame;
+class SwWrongList;
+
+class SwParaChangeTrackingInfo
+{
+ public:
+ explicit SwParaChangeTrackingInfo( const SwTextFrame& rTextFrame );
+ ~SwParaChangeTrackingInfo();
+
+ void reset();
+
+ const SwWrongList* getChangeTrackingTextMarkupList( const sal_Int32 nTextMarkupType );
+
+ private:
+ SwParaChangeTrackingInfo( const SwParaChangeTrackingInfo& ) = delete;
+ SwParaChangeTrackingInfo& operator=( const SwParaChangeTrackingInfo& ) = delete;
+
+ const SwTextFrame& mrTextFrame;
+
+ std::unique_ptr<SwWrongList> mpChangeTrackInsertionTextMarkupList;
+ std::unique_ptr<SwWrongList> mpChangeTrackDeletionTextMarkupList;
+ std::unique_ptr<SwWrongList> mpChangeTrackFormatChangeTextMarkupList;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/textmarkuphelper.cxx b/sw/source/core/access/textmarkuphelper.cxx
new file mode 100644
index 000000000..5329241c6
--- /dev/null
+++ b/sw/source/core/access/textmarkuphelper.cxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "textmarkuphelper.hxx"
+#include "accportions.hxx"
+
+#include <vector>
+
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <com/sun/star/accessibility/TextSegment.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <ndtxt.hxx>
+#include <wrong.hxx>
+
+using namespace com::sun::star;
+
+// helper functions
+namespace {
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ SwWrongList const* (SwTextNode::*
+ getTextMarkupFunc(const sal_Int32 nTextMarkupType))() const
+ {
+ switch ( nTextMarkupType )
+ {
+ case text::TextMarkupType::SPELLCHECK:
+ {
+ return &SwTextNode::GetWrong;
+ }
+ break;
+ case text::TextMarkupType::PROOFREADING:
+ {
+ // support not implemented yet
+ return nullptr;
+ }
+ break;
+ case text::TextMarkupType::SMARTTAG:
+ {
+ // support not implemented yet
+ return nullptr;
+ }
+ break;
+ default:
+ {
+ throw lang::IllegalArgumentException();
+ }
+ }
+ }
+}
+
+// implementation of class <SwTextMarkupoHelper>
+SwTextMarkupHelper::SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwTextFrame& rTextFrame)
+ : mrPortionData( rPortionData )
+ , m_pTextFrame(&rTextFrame)
+ , mpTextMarkupList( nullptr )
+{
+}
+
+// #i108125#
+SwTextMarkupHelper::SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwWrongList& rTextMarkupList )
+ : mrPortionData( rPortionData )
+ , m_pTextFrame( nullptr )
+ , mpTextMarkupList( &rTextMarkupList )
+{
+}
+
+sal_Int32 SwTextMarkupHelper::getTextMarkupCount( const sal_Int32 nTextMarkupType )
+{
+ sal_Int32 nTextMarkupCount( 0 );
+
+ if (mpTextMarkupList)
+ {
+ nTextMarkupCount = mpTextMarkupList->Count();
+ }
+ else
+ {
+ assert(m_pTextFrame);
+ SwWrongList const* (SwTextNode::*const pGetWrongList)() const = getTextMarkupFunc(nTextMarkupType);
+ if (pGetWrongList)
+ {
+ sw::WrongListIteratorCounter iter(*m_pTextFrame, pGetWrongList);
+ nTextMarkupCount = iter.GetElementCount();
+ }
+ }
+
+ return nTextMarkupCount;
+}
+
+css::accessibility::TextSegment
+ SwTextMarkupHelper::getTextMarkup( const sal_Int32 nTextMarkupIndex,
+ const sal_Int32 nTextMarkupType )
+{
+ if ( nTextMarkupIndex >= getTextMarkupCount( nTextMarkupType ) ||
+ nTextMarkupIndex < 0 )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ css::accessibility::TextSegment aTextMarkupSegment;
+ aTextMarkupSegment.SegmentStart = -1;
+ aTextMarkupSegment.SegmentEnd = -1;
+
+ std::unique_ptr<sw::WrongListIteratorCounter> pIter;
+ if (mpTextMarkupList)
+ {
+ pIter.reset(new sw::WrongListIteratorCounter(*mpTextMarkupList));
+ }
+ else
+ {
+ assert(m_pTextFrame);
+ SwWrongList const* (SwTextNode::*const pGetWrongList)() const = getTextMarkupFunc(nTextMarkupType);
+ if (pGetWrongList)
+ {
+ pIter.reset(new sw::WrongListIteratorCounter(*m_pTextFrame, pGetWrongList));
+ }
+ }
+
+ if (pIter)
+ {
+ auto const oElement(pIter->GetElementAt(nTextMarkupIndex));
+ if (oElement)
+ {
+ const OUString& rText = mrPortionData.GetAccessibleString();
+ const sal_Int32 nStartPos =
+ mrPortionData.GetAccessiblePosition(oElement->first);
+ const sal_Int32 nEndPos =
+ mrPortionData.GetAccessiblePosition(oElement->second);
+ aTextMarkupSegment.SegmentText = rText.copy( nStartPos, nEndPos - nStartPos );
+ aTextMarkupSegment.SegmentStart = nStartPos;
+ aTextMarkupSegment.SegmentEnd = nEndPos;
+ }
+ else
+ {
+ OSL_FAIL( "<SwTextMarkupHelper::getTextMarkup(..)> - missing <SwWrongArea> instance" );
+ }
+ }
+
+ return aTextMarkupSegment;
+}
+
+css::uno::Sequence< css::accessibility::TextSegment >
+ SwTextMarkupHelper::getTextMarkupAtIndex( const sal_Int32 nCharIndex,
+ const sal_Int32 nTextMarkupType )
+{
+ // assumption:
+ // value of <nCharIndex> is in range [0..length of accessible text)
+
+ const TextFrameIndex nCoreCharIndex = mrPortionData.GetCoreViewPosition(nCharIndex);
+ // Handling of portions with core length == 0 at the beginning of the
+ // paragraph - e.g. numbering portion.
+ if ( mrPortionData.GetAccessiblePosition( nCoreCharIndex ) > nCharIndex )
+ {
+ return uno::Sequence< css::accessibility::TextSegment >();
+ }
+
+ std::unique_ptr<sw::WrongListIteratorCounter> pIter;
+ if (mpTextMarkupList)
+ {
+ pIter.reset(new sw::WrongListIteratorCounter(*mpTextMarkupList));
+ }
+ else
+ {
+ assert(m_pTextFrame);
+ SwWrongList const* (SwTextNode::*const pGetWrongList)() const = getTextMarkupFunc(nTextMarkupType);
+ if (pGetWrongList)
+ {
+ pIter.reset(new sw::WrongListIteratorCounter(*m_pTextFrame, pGetWrongList));
+ }
+ }
+
+ std::vector< css::accessibility::TextSegment > aTmpTextMarkups;
+ if (pIter)
+ {
+ const OUString& rText = mrPortionData.GetAccessibleString();
+ sal_uInt16 count(pIter->GetElementCount());
+ for (sal_uInt16 i = 0; i < count; ++i)
+ {
+ auto const oElement(pIter->GetElementAt(i));
+ if (oElement &&
+ oElement->first <= nCoreCharIndex &&
+ nCoreCharIndex < oElement->second)
+ {
+ const sal_Int32 nStartPos =
+ mrPortionData.GetAccessiblePosition(oElement->first);
+ const sal_Int32 nEndPos =
+ mrPortionData.GetAccessiblePosition(oElement->second);
+ css::accessibility::TextSegment aTextMarkupSegment;
+ aTextMarkupSegment.SegmentText = rText.copy( nStartPos, nEndPos - nStartPos );
+ aTextMarkupSegment.SegmentStart = nStartPos;
+ aTextMarkupSegment.SegmentEnd = nEndPos;
+ aTmpTextMarkups.push_back( aTextMarkupSegment );
+ }
+ }
+ }
+
+ return comphelper::containerToSequence(aTmpTextMarkups );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/access/textmarkuphelper.hxx b/sw/source/core/access/textmarkuphelper.hxx
new file mode 100644
index 000000000..6db3f9bc0
--- /dev/null
+++ b/sw/source/core/access/textmarkuphelper.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESS_TEXTMARKUPHELPER_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESS_TEXTMARKUPHELPER_HXX
+
+#include <sal/types.h>
+#include <com/sun/star/uno/Sequence.h>
+
+namespace com::sun::star::accessibility {
+ struct TextSegment;
+}
+
+class SwAccessiblePortionData;
+class SwTextFrame;
+class SwWrongList; // #i108125#
+
+class SwTextMarkupHelper
+{
+ public:
+ SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwTextFrame& rTextFrame);
+ SwTextMarkupHelper( const SwAccessiblePortionData& rPortionData,
+ const SwWrongList& rTextMarkupList ); // #i108125#
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ sal_Int32 getTextMarkupCount( const sal_Int32 nTextMarkupType );
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::accessibility::TextSegment getTextMarkup(
+ const sal_Int32 nTextMarkupIndex,
+ const sal_Int32 nTextMarkupType );
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< css::accessibility::TextSegment >
+ getTextMarkupAtIndex( const sal_Int32 nCharIndex,
+ const sal_Int32 nTextMarkupType );
+
+ private:
+ SwTextMarkupHelper( const SwTextMarkupHelper& ) = delete;
+ SwTextMarkupHelper& operator=( const SwTextMarkupHelper& ) = delete;
+
+ const SwAccessiblePortionData& mrPortionData;
+
+ SwTextFrame const* m_pTextFrame;
+ const SwWrongList* mpTextMarkupList;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/BorderCacheOwner.cxx b/sw/source/core/attr/BorderCacheOwner.cxx
new file mode 100644
index 000000000..af13686c5
--- /dev/null
+++ b/sw/source/core/attr/BorderCacheOwner.cxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <BorderCacheOwner.hxx>
+
+#include <hintids.hxx>
+#include <frame.hxx>
+#include <swcache.hxx>
+
+using namespace sw;
+
+BorderCacheOwner::~BorderCacheOwner()
+{
+ if (m_bInCache)
+ SwFrame::GetCache().Delete(this);
+}
+
+void BorderCacheOwner::InvalidateInSwCache(const sal_uInt16 nWhich)
+{
+ switch (nWhich)
+ {
+ case RES_OBJECTDYING:
+ case RES_FMT_CHG:
+ case RES_ATTRSET_CHG:
+ case RES_UL_SPACE:
+ case RES_LR_SPACE:
+ case RES_BOX:
+ case RES_SHADOW:
+ case RES_FRM_SIZE:
+ case RES_KEEP:
+ case RES_BREAK:
+ if (m_bInCache)
+ {
+ SwFrame::GetCache().Delete(this);
+ m_bInCache = false;
+ }
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/calbck.cxx b/sw/source/core/attr/calbck.cxx
new file mode 100644
index 000000000..81a249203
--- /dev/null
+++ b/sw/source/core/attr/calbck.cxx
@@ -0,0 +1,338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include <algorithm>
+#include <format.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+
+#ifdef DBG_UTIL
+#include <sal/backtrace.hxx>
+#endif
+
+namespace sw
+{
+ bool ListenerEntry::GetInfo(SfxPoolItem& rInfo) const
+ { return m_pToTell == nullptr || m_pToTell->GetInfo( rInfo ); }
+ void ListenerEntry::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+ {
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if (pLegacyHint->m_pNew && pLegacyHint->m_pNew->Which() == RES_OBJECTDYING)
+ {
+ auto pModifyChanged = CheckRegistration(pLegacyHint->m_pOld);
+ if (pModifyChanged)
+ m_pToTell->SwClientNotify(rModify, *pModifyChanged);
+ }
+ else if (m_pToTell)
+ m_pToTell->SwClientNotify(rModify, rHint);
+ }
+ else if (m_pToTell)
+ m_pToTell->SwClientNotify(rModify, rHint);
+ }
+}
+
+sw::LegacyModifyHint::~LegacyModifyHint() {}
+
+SwClient::SwClient(SwClient&& o) noexcept
+ : m_pRegisteredIn(nullptr)
+{
+ if(o.m_pRegisteredIn)
+ {
+ o.m_pRegisteredIn->Add(this);
+ o.EndListeningAll();
+ }
+}
+
+SwClient::~SwClient()
+{
+ if(GetRegisteredIn())
+ DBG_TESTSOLARMUTEX();
+ OSL_ENSURE( !m_pRegisteredIn || m_pRegisteredIn->HasWriterListeners(), "SwModify still known, but Client already disconnected!" );
+ if( m_pRegisteredIn && m_pRegisteredIn->HasWriterListeners() )
+ m_pRegisteredIn->Remove( this );
+}
+
+std::optional<sw::ModifyChangedHint> SwClient::CheckRegistration( const SfxPoolItem* pOld )
+{
+ DBG_TESTSOLARMUTEX();
+ // this method only handles notification about dying SwModify objects
+ if( !pOld || pOld->Which() != RES_OBJECTDYING )
+ return {};
+
+ assert(dynamic_cast<const SwPtrMsgPoolItem*>(pOld));
+ const SwPtrMsgPoolItem* pDead = static_cast<const SwPtrMsgPoolItem*>(pOld);
+ if(pDead->pObject != m_pRegisteredIn)
+ {
+ // we should only care received death notes from objects we are following
+ return {};
+ }
+ // I've got a notification from the object I know
+ SwModify* pAbove = m_pRegisteredIn->GetRegisteredIn();
+ if(pAbove)
+ {
+ // if the dying object itself was listening at an SwModify, I take over
+ // adding myself to pAbove will automatically remove me from my current pRegisteredIn
+ pAbove->Add(this);
+ }
+ else
+ {
+ // destroy connection
+ EndListeningAll();
+ }
+ return sw::ModifyChangedHint(pAbove);
+}
+
+void SwClient::CheckRegistrationFormat(SwFormat& rOld)
+{
+ assert(GetRegisteredIn() == &rOld);
+ auto pNew = rOld.DerivedFrom();
+ SAL_INFO("sw.core", "reparenting " << typeid(*this).name() << " at " << this << " from " << typeid(rOld).name() << " at " << &rOld << " to " << typeid(*pNew).name() << " at " << pNew);
+ assert(pNew);
+ pNew->Add(this);
+ const SwFormatChg aOldFormat(&rOld);
+ const SwFormatChg aNewFormat(pNew);
+ const sw::LegacyModifyHint aHint(&aOldFormat, &aNewFormat);
+ SwClientNotify(rOld, aHint);
+}
+
+void SwClient::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CheckRegistration(pLegacyHint->m_pOld);
+};
+
+void SwClient::StartListeningToSameModifyAs(const SwClient& other)
+{
+ if(other.m_pRegisteredIn)
+ other.m_pRegisteredIn->Add(this);
+ else
+ EndListeningAll();
+}
+
+void SwClient::EndListeningAll()
+{
+ if(m_pRegisteredIn)
+ m_pRegisteredIn->Remove(this);
+}
+
+SwModify::~SwModify()
+{
+ DBG_TESTSOLARMUTEX();
+ OSL_ENSURE( !IsModifyLocked(), "Modify destroyed but locked." );
+
+ // notify all clients that they shall remove themselves
+ SwPtrMsgPoolItem aDyObject( RES_OBJECTDYING, this );
+ SwModify::SwClientNotify(*this, sw::LegacyModifyHint(&aDyObject, &aDyObject));
+
+ const bool hasListenersOnDeath = m_pWriterListeners;
+ (void)hasListenersOnDeath;
+ while(m_pWriterListeners)
+ {
+ SAL_WARN("sw.core", "lost a client of type: " << typeid(*m_pWriterListeners).name() << " at " << m_pWriterListeners << " still registered on type: " << typeid(*this).name() << " at " << this << ".");
+ static_cast<SwClient*>(m_pWriterListeners)->CheckRegistration(&aDyObject);
+ }
+ assert(!hasListenersOnDeath);
+}
+
+bool SwModify::GetInfo( SfxPoolItem& rInfo ) const
+{
+ if(!m_pWriterListeners)
+ return true;
+ SwIterator<SwClient,SwModify> aIter(*this);
+ for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
+ if(!pClient->GetInfo( rInfo ))
+ return false;
+ return true;
+}
+
+void SwModify::Add( SwClient* pDepend )
+{
+ DBG_TESTSOLARMUTEX();
+#ifdef DBG_UTIL
+ // You should not EVER use SwModify directly in new code:
+ // - Preexisting SwModifys should only ever be used via sw::BroadcastingModify.
+ // This includes sw::BroadcastMixin, which is the long-term target (without
+ // SwModify).
+ // - New classes should use sw::BroadcastMixin alone.
+ if(!dynamic_cast<sw::BroadcastingModify*>(this))
+ {
+ auto pBT = sal::backtrace_get(20);
+ SAL_WARN("sw.core", "Modify that is not broadcasting used!\n" << sal::backtrace_to_string(pBT.get()));
+ }
+#endif
+
+ if(pDepend->m_pRegisteredIn == this)
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ if(sw::ClientIteratorBase::s_pClientIters)
+ {
+ for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
+ {
+ SAL_WARN_IF(&rIter.m_rRoot == m_pWriterListeners, "sw.core", "a " << typeid(*pDepend).name() << " client added as listener to a " << typeid(*this).name() << " during client iteration.");
+ }
+ }
+#endif
+ // deregister new client in case it is already registered elsewhere
+ if( pDepend->m_pRegisteredIn != nullptr )
+ pDepend->m_pRegisteredIn->Remove( pDepend );
+
+ if( !m_pWriterListeners )
+ {
+ // first client added
+ m_pWriterListeners = pDepend;
+ m_pWriterListeners->m_pLeft = nullptr;
+ m_pWriterListeners->m_pRight = nullptr;
+ }
+ else
+ {
+ // append client
+ pDepend->m_pRight = m_pWriterListeners->m_pRight;
+ m_pWriterListeners->m_pRight = pDepend;
+ pDepend->m_pLeft = m_pWriterListeners;
+ if( pDepend->m_pRight )
+ pDepend->m_pRight->m_pLeft = pDepend;
+ }
+
+ // connect client to me
+ pDepend->m_pRegisteredIn = this;
+}
+
+SwClient* SwModify::Remove( SwClient* pDepend )
+{
+ DBG_TESTSOLARMUTEX();
+ assert(pDepend->m_pRegisteredIn == this);
+
+ // SwClient is my listener
+ // remove it from my list
+ ::sw::WriterListener* pR = pDepend->m_pRight;
+ ::sw::WriterListener* pL = pDepend->m_pLeft;
+ if( m_pWriterListeners == pDepend )
+ m_pWriterListeners = pL ? pL : pR;
+
+ if( pL )
+ pL->m_pRight = pR;
+ if( pR )
+ pR->m_pLeft = pL;
+
+ // update ClientIterators
+ if(sw::ClientIteratorBase::s_pClientIters)
+ {
+ for(auto& rIter : sw::ClientIteratorBase::s_pClientIters->GetRingContainer())
+ {
+ if (&rIter.m_rRoot == this &&
+ (rIter.m_pCurrent == pDepend || rIter.m_pPosition == pDepend))
+ {
+ // if object being removed is the current or next object in an
+ // iterator, advance this iterator
+ rIter.m_pPosition = pR;
+ }
+ }
+ }
+ pDepend->m_pLeft = nullptr;
+ pDepend->m_pRight = nullptr;
+ pDepend->m_pRegisteredIn = nullptr;
+ return pDepend;
+}
+
+sw::WriterMultiListener::WriterMultiListener(SwClient& rToTell)
+ : m_rToTell(rToTell)
+{}
+
+sw::WriterMultiListener::~WriterMultiListener()
+{}
+
+void sw::WriterMultiListener::StartListening(SwModify* pDepend)
+{
+ EndListening(nullptr);
+ m_vDepends.emplace_back(&m_rToTell, pDepend);
+}
+
+
+bool sw::WriterMultiListener::IsListeningTo(const SwModify* const pBroadcaster) const
+{
+ return std::any_of(m_vDepends.begin(), m_vDepends.end(),
+ [&pBroadcaster](const ListenerEntry& aListener)
+ {
+ return aListener.GetRegisteredIn() == pBroadcaster;
+ });
+}
+
+void sw::WriterMultiListener::EndListening(SwModify* pBroadcaster)
+{
+ m_vDepends.erase(
+ std::remove_if( m_vDepends.begin(), m_vDepends.end(),
+ [&pBroadcaster](const ListenerEntry& aListener)
+ {
+ return aListener.GetRegisteredIn() == nullptr || aListener.GetRegisteredIn() == pBroadcaster;
+ }),
+ m_vDepends.end());
+}
+
+void sw::WriterMultiListener::EndListeningAll()
+{
+ m_vDepends.clear();
+}
+
+sw::ClientIteratorBase* sw::ClientIteratorBase::s_pClientIters = nullptr;
+
+void SwModify::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+
+ DBG_TESTSOLARMUTEX();
+ if(IsModifyLocked())
+ return;
+
+ LockModify();
+ CallSwClientNotify(rHint);
+ UnlockModify();
+}
+
+void SwModify::CallSwClientNotify( const SfxHint& rHint ) const
+{
+ DBG_TESTSOLARMUTEX();
+ SwIterator<SwClient,SwModify> aIter(*this);
+ for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
+ pClient->SwClientNotify( *this, rHint );
+}
+
+void sw::BroadcastingModify::CallSwClientNotify(const SfxHint& rHint) const
+{
+ SwModify::CallSwClientNotify(rHint);
+ const_cast<BroadcastingModify*>(this)->GetNotifier().Broadcast(rHint);
+}
+
+void sw::ClientNotifyAttrChg(SwModify& rModify, const SwAttrSet& aSet, SwAttrSet& aOld, SwAttrSet& aNew)
+{
+ const SwAttrSetChg aChgOld(aSet, aOld);
+ const SwAttrSetChg aChgNew(aSet, aNew);
+ const sw::LegacyModifyHint aHint(&aChgOld, &aChgNew);
+ rModify.SwClientNotify(rModify, aHint);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/cellatr.cxx b/sw/source/core/attr/cellatr.cxx
new file mode 100644
index 000000000..3fdfb0772
--- /dev/null
+++ b/sw/source/core/attr/cellatr.cxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <calc.hxx>
+#include <cellatr.hxx>
+#include <doc.hxx>
+#include <float.h>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <node.hxx>
+#include <rolbck.hxx>
+#include <rtl/ustring.hxx>
+#include <calbck.hxx>
+#include <swtable.hxx>
+
+// The % SV_COUNTRY_LANGUAGE_OFFSET result checks if nFormat is a mere built-in
+// @ Text format of *any* locale and if so uses the default text format. Text
+// is text, the locale doesn't matter for Writer's number formatting purposes.
+// The advantage is that this is the pool's default item value and some places
+// benefit from this special treatment in that they don't have to handle/store
+// attribute specifics, especially when writing a document.
+SwTableBoxNumFormat::SwTableBoxNumFormat( sal_uInt32 nFormat )
+ : SfxUInt32Item( RES_BOXATR_FORMAT,
+ (((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == getSwDefaultTextFormat()) ?
+ getSwDefaultTextFormat() : nFormat))
+{
+}
+
+bool SwTableBoxNumFormat::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return GetValue() == static_cast<const SwTableBoxNumFormat&>(rAttr).GetValue();
+}
+
+SwTableBoxNumFormat* SwTableBoxNumFormat::Clone( SfxItemPool* ) const
+{
+ return new SwTableBoxNumFormat( GetValue() );
+}
+
+SwTableBoxFormula::SwTableBoxFormula( const OUString& rFormula )
+ : SfxPoolItem( RES_BOXATR_FORMULA ),
+ SwTableFormula( rFormula ),
+ m_pDefinedIn( nullptr )
+{
+}
+
+bool SwTableBoxFormula::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return GetFormula() == static_cast<const SwTableBoxFormula&>(rAttr).GetFormula() &&
+ m_pDefinedIn == static_cast<const SwTableBoxFormula&>(rAttr).m_pDefinedIn;
+}
+
+SwTableBoxFormula* SwTableBoxFormula::Clone( SfxItemPool* ) const
+{
+ // switch to external rendering
+ SwTableBoxFormula* pNew = new SwTableBoxFormula( GetFormula() );
+ pNew->SwTableFormula::operator=( *this );
+ return pNew;
+}
+
+/** Get node type of the node containing this formula
+
+ E.g. TextField -> TextNode, or
+ BoxAttribute -> BoxStartNode
+
+ Caution: Must override when inheriting.
+*/
+const SwNode* SwTableBoxFormula::GetNodeOfFormula() const
+{
+ auto pTableBox = GetTableBox();
+ return pTableBox ? pTableBox->GetSttNd() : nullptr;
+}
+
+SwTableBox* SwTableBoxFormula::GetTableBox()
+{
+ assert(!m_pDefinedIn || dynamic_cast<SwTableBoxFormat*>(m_pDefinedIn));
+ return m_pDefinedIn ? static_cast<SwTableBoxFormat*>(m_pDefinedIn)->GetTableBox() : nullptr;
+}
+
+void SwTableBoxFormula::ChangeState( const SfxPoolItem* pItem )
+{
+ if( !m_pDefinedIn )
+ return ;
+
+ SwTableFormulaUpdate* pUpdateField;
+ if( !pItem || RES_TABLEFML_UPDATE != pItem->Which() )
+ {
+ // reset value flag
+ ChgValid( false );
+ return ;
+ }
+
+ pUpdateField = const_cast<SwTableFormulaUpdate*>(static_cast<const SwTableFormulaUpdate*>(pItem));
+
+ // detect table that contains this attribute
+ const SwTableNode* pTableNd;
+ const SwNode* pNd = GetNodeOfFormula();
+ if (!pNd || &pNd->GetNodes() != &pNd->GetDoc().GetNodes())
+ return;
+ pTableNd = pNd->FindTableNode();
+ if( pTableNd == nullptr )
+ return;
+
+ switch( pUpdateField->m_eFlags )
+ {
+ case TBL_CALC:
+ // reset value flag
+ ChgValid( false );
+ break;
+ case TBL_BOXNAME:
+ if( &pTableNd->GetTable() == pUpdateField->m_pTable )
+ // use external rendering
+ PtrToBoxNm( pUpdateField->m_pTable );
+ break;
+ case TBL_BOXPTR:
+ // internal rendering
+ BoxNmToPtr( &pTableNd->GetTable() );
+ break;
+ case TBL_RELBOXNAME:
+ if( &pTableNd->GetTable() == pUpdateField->m_pTable )
+ // relative rendering
+ ToRelBoxNm( pUpdateField->m_pTable );
+ break;
+
+ case TBL_SPLITTBL:
+ if( &pTableNd->GetTable() == pUpdateField->m_pTable )
+ {
+ sal_uInt16 nLnPos = SwTableFormula::GetLnPosInTable(
+ pTableNd->GetTable(), GetTableBox() );
+ pUpdateField->m_bBehindSplitLine = USHRT_MAX != nLnPos &&
+ pUpdateField->m_nSplitLine <= nLnPos;
+ }
+ else
+ pUpdateField->m_bBehindSplitLine = false;
+ [[fallthrough]];
+ case TBL_MERGETBL:
+ if( pUpdateField->m_pHistory )
+ {
+ // for a history record the unchanged formula is needed
+ SwTableBoxFormula aCopy( *this );
+ pUpdateField->m_bModified = false;
+ ToSplitMergeBoxNm( *pUpdateField );
+
+ if( pUpdateField->m_bModified )
+ {
+ // external rendering
+ aCopy.PtrToBoxNm( &pTableNd->GetTable() );
+ pUpdateField->m_pHistory->Add(
+ &aCopy,
+ &aCopy,
+ pNd->FindTableBoxStartNode()->GetIndex());
+ }
+ }
+ else
+ ToSplitMergeBoxNm( *pUpdateField );
+ break;
+ }
+}
+
+void SwTableBoxFormula::Calc( SwTableCalcPara& rCalcPara, double& rValue )
+{
+ if( !rCalcPara.m_rCalc.IsCalcError() )
+ {
+ // create pointers from box names
+ BoxNmToPtr( rCalcPara.m_pTable );
+ const OUString sFormula( MakeFormula( rCalcPara ));
+ if( !rCalcPara.m_rCalc.IsCalcError() )
+ rValue = rCalcPara.m_rCalc.Calculate( sFormula ).GetDouble();
+ else
+ rValue = DBL_MAX;
+ ChgValid( !rCalcPara.IsStackOverflow() ); // value is now valid again
+ }
+}
+
+SwTableBoxValue::SwTableBoxValue()
+ : SfxPoolItem( RES_BOXATR_VALUE ), m_nValue( 0 )
+{
+}
+
+SwTableBoxValue::SwTableBoxValue( const double nVal )
+ : SfxPoolItem( RES_BOXATR_VALUE ), m_nValue( nVal )
+{
+}
+
+bool SwTableBoxValue::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ SwTableBoxValue const& rOther( static_cast<SwTableBoxValue const&>(rAttr) );
+ // items with NaN should be equal to enable pooling
+ return std::isnan( m_nValue )
+ ? std::isnan( rOther.m_nValue )
+ : ( m_nValue == rOther.m_nValue );
+}
+
+SwTableBoxValue* SwTableBoxValue::Clone( SfxItemPool* ) const
+{
+ return new SwTableBoxValue( m_nValue );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/fmtfollowtextflow.cxx b/sw/source/core/attr/fmtfollowtextflow.cxx
new file mode 100644
index 000000000..5b4240537
--- /dev/null
+++ b/sw/source/core/attr/fmtfollowtextflow.cxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fmtfollowtextflow.hxx>
+
+SwFormatFollowTextFlow* SwFormatFollowTextFlow::Clone(SfxItemPool*) const
+{
+ return new SwFormatFollowTextFlow(*this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/fmtwrapinfluenceonobjpos.cxx b/sw/source/core/attr/fmtwrapinfluenceonobjpos.cxx
new file mode 100644
index 000000000..8f9abd96c
--- /dev/null
+++ b/sw/source/core/attr/fmtwrapinfluenceonobjpos.cxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <unomid.h>
+#include <osl/diagnose.h>
+#include <libxml/xmlwriter.h>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+SwFormatWrapInfluenceOnObjPos::SwFormatWrapInfluenceOnObjPos( sal_Int16 _nWrapInfluenceOnPosition )
+ : SfxPoolItem( RES_WRAP_INFLUENCE_ON_OBJPOS ),
+ mnWrapInfluenceOnPosition( _nWrapInfluenceOnPosition )
+{
+}
+
+SwFormatWrapInfluenceOnObjPos::~SwFormatWrapInfluenceOnObjPos()
+{
+}
+
+bool SwFormatWrapInfluenceOnObjPos::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SwFormatWrapInfluenceOnObjPos& rAttribute
+ = static_cast<const SwFormatWrapInfluenceOnObjPos&>(rAttr);
+ return (mnWrapInfluenceOnPosition == rAttribute.GetWrapInfluenceOnObjPos()
+ && mbAllowOverlap == rAttribute.mbAllowOverlap
+ && mnOverlapVertOffset == rAttribute.mnOverlapVertOffset);
+}
+
+SwFormatWrapInfluenceOnObjPos* SwFormatWrapInfluenceOnObjPos::Clone( SfxItemPool * ) const
+{
+ return new SwFormatWrapInfluenceOnObjPos(*this);
+}
+
+bool SwFormatWrapInfluenceOnObjPos::QueryValue( Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ if( nMemberId == MID_WRAP_INFLUENCE )
+ {
+ rVal <<= GetWrapInfluenceOnObjPos();
+ }
+ else if( nMemberId == MID_ALLOW_OVERLAP )
+ {
+ rVal <<= GetAllowOverlap();
+ }
+ else
+ {
+ OSL_FAIL( "<SwFormatWrapInfluenceOnObjPos::QueryValue()> - unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatWrapInfluenceOnObjPos::PutValue( const Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = false;
+
+ if( nMemberId == MID_WRAP_INFLUENCE )
+ {
+ sal_Int16 nNewWrapInfluence = 0;
+ rVal >>= nNewWrapInfluence;
+ // #i35017# - constant names have changed and <ITERATIVE> has been added
+ if ( nNewWrapInfluence == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ||
+ nNewWrapInfluence == text::WrapInfluenceOnPosition::ONCE_CONCURRENT ||
+ nNewWrapInfluence == text::WrapInfluenceOnPosition::ITERATIVE )
+ {
+ SetWrapInfluenceOnObjPos( nNewWrapInfluence );
+ bRet = true;
+ }
+ else
+ {
+ OSL_FAIL( "<SwFormatWrapInfluenceOnObjPos::PutValue(..)> - invalid attribute value" );
+ }
+ }
+ else if( nMemberId == MID_ALLOW_OVERLAP )
+ {
+ bool bAllowOverlap = true;
+ if (rVal >>= bAllowOverlap)
+ {
+ SetAllowOverlap(bAllowOverlap);
+ bRet = true;
+ }
+ else
+ {
+ SAL_WARN("sw.core", "SwFormatWrapInfluenceOnObjPos::PutValue: invalid AllowOverlap type");
+ }
+ }
+ else
+ {
+ OSL_FAIL( "<SwFormatWrapInfluenceOnObjPos::PutValue(..)> - unknown MemberId" );
+ }
+ return bRet;
+}
+
+void SwFormatWrapInfluenceOnObjPos::SetWrapInfluenceOnObjPos( sal_Int16 _nWrapInfluenceOnPosition )
+{
+ // #i35017# - constant names have changed and consider new value <ITERATIVE>
+ if ( _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ||
+ _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_CONCURRENT ||
+ _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ITERATIVE )
+ {
+ mnWrapInfluenceOnPosition = _nWrapInfluenceOnPosition;
+ }
+ else
+ {
+ OSL_FAIL( "<SwFormatWrapInfluenceOnObjPos::SetWrapInfluenceOnObjPos(..)> - invalid attribute value" );
+ }
+}
+
+// #i35017# - add parameter <_bIterativeAsOnceConcurrent> to control, if
+// value <ITERATIVE> has to be treated as <ONCE_CONCURRENT>
+sal_Int16 SwFormatWrapInfluenceOnObjPos::GetWrapInfluenceOnObjPos(
+ const bool _bIterativeAsOnceConcurrent ) const
+{
+ sal_Int16 nWrapInfluenceOnPosition( mnWrapInfluenceOnPosition );
+
+ if ( _bIterativeAsOnceConcurrent &&
+ nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ITERATIVE )
+ {
+ nWrapInfluenceOnPosition = text::WrapInfluenceOnPosition::ONCE_CONCURRENT;
+ }
+
+ return nWrapInfluenceOnPosition;
+}
+
+void SwFormatWrapInfluenceOnObjPos::SetAllowOverlap(bool bAllowOverlap)
+{
+ mbAllowOverlap = bAllowOverlap;
+}
+
+bool SwFormatWrapInfluenceOnObjPos::GetAllowOverlap() const
+{
+ return mbAllowOverlap;
+}
+
+void SwFormatWrapInfluenceOnObjPos::SetOverlapVertOffset(SwTwips nOverlapVertOffset)
+{
+ mnOverlapVertOffset = nOverlapVertOffset;
+}
+
+SwTwips SwFormatWrapInfluenceOnObjPos::GetOverlapVertOffset() const { return mnOverlapVertOffset; }
+
+void SwFormatWrapInfluenceOnObjPos::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatWrapInfluenceOnObjPos"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWrapInfluenceOnPosition"), BAD_CAST(OString::number(mnWrapInfluenceOnPosition).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mbAllowOverlap"), BAD_CAST(OString::boolean(mbAllowOverlap).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/format.cxx b/sw/source/core/attr/format.cxx
new file mode 100644
index 000000000..94b6af99d
--- /dev/null
+++ b/sw/source/core/attr/format.cxx
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <DocumentSettingManager.hxx> //For SwFmt::getIDocumentSettingAccess()
+#include <IDocumentTimerAccess.hxx>
+#include <doc.hxx>
+#include <fmtcolfunc.hxx>
+#include <format.hxx>
+#include <frmatr.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <svl/grabbagitem.hxx>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <svx/xdef.hxx>
+
+using namespace com::sun::star;
+
+SwFormat::SwFormat( SwAttrPool& rPool, const char* pFormatNm,
+ const WhichRangesContainer& pWhichRanges, SwFormat *pDrvdFrame,
+ sal_uInt16 nFormatWhich ) :
+ m_aFormatName( OUString::createFromAscii(pFormatNm) ),
+ m_aSet( rPool, pWhichRanges ),
+ m_nWhichId( nFormatWhich ),
+ m_nPoolFormatId( USHRT_MAX ),
+ m_nPoolHelpId( USHRT_MAX ),
+ m_nPoolHlpFileId( UCHAR_MAX )
+{
+ m_bAutoUpdateFormat = false; // LAYER_IMPL
+ m_bAutoFormat = true;
+ m_bFormatInDTOR = m_bHidden = false;
+
+ if( pDrvdFrame )
+ {
+ pDrvdFrame->Add(this);
+ m_aSet.SetParent( &pDrvdFrame->m_aSet );
+ }
+}
+
+SwFormat::SwFormat( SwAttrPool& rPool, const OUString& rFormatNm,
+ const WhichRangesContainer& pWhichRanges, SwFormat* pDrvdFrame,
+ sal_uInt16 nFormatWhich ) :
+ m_aFormatName( rFormatNm ),
+ m_aSet( rPool, pWhichRanges ),
+ m_nWhichId( nFormatWhich ),
+ m_nPoolFormatId( USHRT_MAX ),
+ m_nPoolHelpId( USHRT_MAX ),
+ m_nPoolHlpFileId( UCHAR_MAX )
+{
+ m_bAutoUpdateFormat = false; // LAYER_IMPL
+ m_bAutoFormat = true;
+ m_bFormatInDTOR = m_bHidden = false;
+
+ if( pDrvdFrame )
+ {
+ pDrvdFrame->Add(this);
+ m_aSet.SetParent( &pDrvdFrame->m_aSet );
+ }
+}
+
+SwFormat::SwFormat( const SwFormat& rFormat ) :
+ sw::BorderCacheOwner(),
+ m_aFormatName( rFormat.m_aFormatName ),
+ m_aSet( rFormat.m_aSet ),
+ m_nWhichId( rFormat.m_nWhichId ),
+ m_nPoolFormatId( rFormat.GetPoolFormatId() ),
+ m_nPoolHelpId( rFormat.GetPoolHelpId() ),
+ m_nPoolHlpFileId( rFormat.GetPoolHlpFileId() )
+{
+ m_bFormatInDTOR = false; // LAYER_IMPL
+ m_bAutoFormat = rFormat.m_bAutoFormat;
+ m_bHidden = rFormat.m_bHidden;
+ m_bAutoUpdateFormat = rFormat.m_bAutoUpdateFormat;
+
+ if( auto pDerived = rFormat.DerivedFrom() )
+ {
+ pDerived->Add(this);
+ m_aSet.SetParent( &pDerived->m_aSet );
+ }
+ // a few special treatments for attributes
+ m_aSet.SetModifyAtAttr( this );
+}
+
+SwFormat &SwFormat::operator=(const SwFormat& rFormat)
+{
+ if(this == &rFormat)
+ return *this;
+
+ m_nWhichId = rFormat.m_nWhichId;
+ m_nPoolFormatId = rFormat.GetPoolFormatId();
+ m_nPoolHelpId = rFormat.GetPoolHelpId();
+ m_nPoolHlpFileId = rFormat.GetPoolHlpFileId();
+
+ InvalidateInSwCache(RES_OBJECTDYING);
+
+ // copy only array with attributes delta
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+ m_aSet.Intersect_BC( rFormat.m_aSet, &aOld, &aNew );
+ (void)m_aSet.Put_BC( rFormat.m_aSet, &aOld, &aNew );
+
+ // a few special treatments for attributes
+ m_aSet.SetModifyAtAttr( this );
+
+ // create PoolItem attribute for Modify
+ if( aOld.Count() )
+ {
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ }
+
+ if(GetRegisteredIn() != rFormat.GetRegisteredIn())
+ {
+ StartListeningToSameModifyAs(rFormat);
+ m_aSet.SetParent( GetRegisteredIn()
+ ? &rFormat.m_aSet
+ : nullptr);
+ }
+
+ m_bAutoFormat = rFormat.m_bAutoFormat;
+ m_bHidden = rFormat.m_bHidden;
+ m_bAutoUpdateFormat = rFormat.m_bAutoUpdateFormat;
+ return *this;
+}
+
+void SwFormat::SetName( const OUString& rNewName, bool bBroadcast )
+{
+ OSL_ENSURE( !IsDefault(), "SetName: Defaultformat" );
+ if( bBroadcast )
+ {
+ SwStringMsgPoolItem aOld( RES_NAME_CHANGED, m_aFormatName );
+ SwStringMsgPoolItem aNew( RES_NAME_CHANGED, rNewName );
+ m_aFormatName = rNewName;
+ const sw::LegacyModifyHint aHint(&aOld, &aNew);
+ SwClientNotify(*this, aHint);
+ }
+ else
+ {
+ m_aFormatName = rNewName;
+ }
+}
+
+/** Copy attributes
+
+ This function is called in every Copy-Ctor for copying the attributes.
+ The latter can be only copied as soon as the derived class exists since
+ for setting them the Which() function is called and that has the default
+ value of 0 in the base class and is then overridden by the derived class.
+
+ If we copy over multiple documents then the new document has to be provided
+ in which <this> is defined. Currently this is important for DropCaps
+ because that contains data that needs to be copied deeply.
+*/
+void SwFormat::CopyAttrs( const SwFormat& rFormat )
+{
+ // copy only array with attributes delta
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ InvalidateInSwFntCache(RES_ATTRSET_CHG);
+
+ // special treatments for some attributes
+ SwAttrSet* pChgSet = const_cast<SwAttrSet*>(&rFormat.m_aSet);
+
+ // copy only array with attributes delta
+ if( pChgSet->GetPool() != m_aSet.GetPool() )
+ pChgSet->CopyToModify( *this );
+ else
+ {
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+
+ if ( m_aSet.Put_BC( *pChgSet, &aOld, &aNew ) )
+ {
+ // a few special treatments for attributes
+ m_aSet.SetModifyAtAttr( this );
+
+ SwAttrSetChg aChgOld( m_aSet, aOld );
+ SwAttrSetChg aChgNew( m_aSet, aNew );
+ SwClientNotify(*this, sw::LegacyModifyHint(&aChgOld, &aChgNew)); // send all modified ones
+ }
+ }
+
+ if( pChgSet != &rFormat.m_aSet ) // was a Set created?
+ delete pChgSet;
+}
+
+SwFormat::~SwFormat()
+{
+ // This happens at an ObjectDying message. Thus put all dependent
+ // ones on DerivedFrom.
+ if(!HasWriterListeners())
+ return;
+
+ m_bFormatInDTOR = true;
+
+ if(!DerivedFrom())
+ {
+ SwFormat::ResetFormatAttr(RES_PAGEDESC);
+ SAL_WARN("sw.core", "~SwFormat: format still has clients on death, but parent format is missing: " << GetName());
+ return;
+ }
+ SwIterator<SwClient,SwFormat> aIter(*this);
+ for(SwClient* pClient = aIter.First(); pClient; pClient = aIter.Next())
+ pClient->CheckRegistrationFormat(*this);
+ assert(!HasWriterListeners());
+}
+
+void SwFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+
+ std::unique_ptr<SwAttrSetChg> pOldClientChg, pNewClientChg;
+ auto pDependsHint = std::make_unique<sw::LegacyModifyHint>(pLegacy->m_pOld, pLegacy->m_pNew);
+ const sal_uInt16 nWhich = pLegacy->GetWhich();
+ InvalidateInSwCache(nWhich);
+ switch(nWhich)
+ {
+ case 0:
+ break;
+ case RES_OBJECTDYING:
+ {
+ // NB: this still notifies depends even if pLegacy->m_pNew is nullptr, which seems non-obvious
+ if(!pLegacy->m_pNew)
+ break;
+ // If the dying object is the parent format of this format so
+ // attach this to the parent of the parent
+ SwFormat* pFormat = static_cast<SwFormat*>(pLegacy->m_pNew->StaticWhichCast(RES_OBJECTDYING).pObject);
+
+ // do not move if this is the topmost format
+ if(GetRegisteredIn() && GetRegisteredIn() == pFormat)
+ {
+ if(pFormat->GetRegisteredIn())
+ {
+ // if parent so register in new parent
+ pFormat->DerivedFrom()->Add(this);
+ m_aSet.SetParent(&DerivedFrom()->m_aSet);
+ }
+ else
+ {
+ // otherwise de-register at least from dying one
+ EndListeningAll();
+ m_aSet.SetParent(nullptr);
+ }
+ }
+ break;
+ }
+ case RES_ATTRSET_CHG:
+ {
+ // NB: this still notifies depends even if this condition is not met, which seems non-obvious
+ auto pOldAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto pNewAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ if (pOldAttrSetChg && pNewAttrSetChg && pOldAttrSetChg->GetTheChgdSet() != &m_aSet)
+ {
+ // pass only those that are not set...
+ pNewClientChg.reset(new SwAttrSetChg(*pNewAttrSetChg));
+ pNewClientChg->GetChgSet()->Differentiate(m_aSet);
+ if(pNewClientChg->Count()) // ... if any
+ {
+ pOldClientChg.reset(new SwAttrSetChg(*pOldAttrSetChg));
+ pOldClientChg->GetChgSet()->Differentiate(m_aSet);
+ pDependsHint.reset(new sw::LegacyModifyHint(pOldClientChg.get(), pNewClientChg.get()));
+ }
+ else
+ pDependsHint.reset(nullptr);
+ }
+ break;
+ }
+ case RES_FMT_CHG:
+ {
+ // if the format parent will be moved so register my attribute set at
+ // the new one
+
+ // skip my own Modify
+ // NB: this still notifies depends even if this condition is not met, which seems non-obvious
+ auto pOldFormatChg = static_cast<const SwFormatChg*>(pLegacy->m_pOld);
+ auto pNewFormatChg = static_cast<const SwFormatChg*>(pLegacy->m_pNew);
+ if(pOldFormatChg && pNewFormatChg && pOldFormatChg->pChangedFormat != this && pNewFormatChg->pChangedFormat == GetRegisteredIn())
+ {
+ // attach Set to new parent
+ m_aSet.SetParent(DerivedFrom() ? &DerivedFrom()->m_aSet : nullptr);
+ }
+ break;
+ }
+ default:
+ // attribute is defined in this format
+ if(SfxItemState::SET == m_aSet.GetItemState(nWhich, false))
+ {
+ // DropCaps might come into this block
+ SAL_WARN_IF(RES_PARATR_DROP != nWhich, "sw.core", "Hint was sent without sender");
+ pDependsHint.reset(nullptr);
+ }
+ }
+ if(pDependsHint)
+ {
+ InvalidateInSwFntCache(pDependsHint->GetWhich());
+ SwModify::SwClientNotify(*this, *pDependsHint);
+ }
+}
+
+bool SwFormat::SetDerivedFrom(SwFormat *pDerFrom)
+{
+ if ( pDerFrom )
+ {
+ const SwFormat* pFormat = pDerFrom;
+ while ( pFormat != nullptr )
+ {
+ if ( pFormat == this )
+ return false;
+
+ pFormat=pFormat->DerivedFrom();
+ }
+ }
+ else
+ {
+ // nothing provided, search for Dflt format
+ pDerFrom = this;
+ while ( pDerFrom->DerivedFrom() )
+ pDerFrom = pDerFrom->DerivedFrom();
+ }
+ if ( (pDerFrom == DerivedFrom()) || (pDerFrom == this) )
+ return false;
+
+ assert( Which()==pDerFrom->Which()
+ || (Which()==RES_CONDTXTFMTCOLL && pDerFrom->Which()==RES_TXTFMTCOLL)
+ || (Which()==RES_TXTFMTCOLL && pDerFrom->Which()==RES_CONDTXTFMTCOLL)
+ || (Which()==RES_FLYFRMFMT && pDerFrom->Which()==RES_FRMFMT)
+ );
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ InvalidateInSwFntCache(RES_ATTRSET_CHG);
+
+ pDerFrom->Add( this );
+ m_aSet.SetParent( &pDerFrom->m_aSet );
+
+ SwFormatChg aOldFormat( this );
+ SwFormatChg aNewFormat( this );
+ const sw::LegacyModifyHint aHint(&aOldFormat, &aNewFormat);
+ SwClientNotify(*this, aHint);
+
+ return true;
+}
+
+bool SwFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return false;
+}
+
+const SfxPoolItem& SwFormat::GetFormatAttr( sal_uInt16 nWhich, bool bInParents ) const
+{
+ if (RES_BACKGROUND == nWhich && supportsFullDrawingLayerFillAttributeSet())
+ {
+ // FALLBACKBREAKHERE should not be used; instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ SAL_INFO("sw.core", "Do no longer use SvxBrushItem, instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST] FillAttributes or makeBackgroundBrushItem (simple fallback is in place and used)");
+ static std::unique_ptr<SvxBrushItem> aSvxBrushItem; //(std::make_shared<SvxBrushItem>(RES_BACKGROUND));
+
+ // fill the local static SvxBrushItem from the current ItemSet so that
+ // the fill attributes [XATTR_FILL_FIRST .. XATTR_FILL_LAST] are used
+ // as good as possible to create a fallback representation and return that
+ aSvxBrushItem = getSvxBrushItemFromSourceSet(m_aSet, RES_BACKGROUND, bInParents);
+
+ return *aSvxBrushItem;
+ }
+
+ return m_aSet.Get( nWhich, bInParents );
+}
+
+SfxItemState SwFormat::GetItemState( sal_uInt16 nWhich, bool bSrchInParent, const SfxPoolItem **ppItem ) const
+{
+ if (RES_BACKGROUND == nWhich && supportsFullDrawingLayerFillAttributeSet())
+ {
+ // FALLBACKBREAKHERE should not be used; instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ SAL_INFO("sw.core", "Do no longer use SvxBrushItem, instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST] FillAttributes or SwFormat::GetBackgroundStat (simple fallback is in place and used)");
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFill = getSdrAllFillAttributesHelper();
+
+ // check if the new fill attributes are used
+ if(aFill && aFill->isUsed())
+ {
+ // if yes, fill the local SvxBrushItem using the new fill attributes
+ // as good as possible to have an instance for the pointer to point
+ // to and return as state that it is set
+ static std::unique_ptr<SvxBrushItem> aSvxBrushItem; //(RES_BACKGROUND);
+
+ aSvxBrushItem = getSvxBrushItemFromSourceSet(m_aSet, RES_BACKGROUND, bSrchInParent);
+ if( ppItem )
+ *ppItem = aSvxBrushItem.get();
+
+ return SfxItemState::SET;
+ }
+
+ // if not, reset pointer and return SfxItemState::DEFAULT to signal that
+ // the item is not set
+ if( ppItem )
+ *ppItem = nullptr;
+
+ return SfxItemState::DEFAULT;
+ }
+
+ return m_aSet.GetItemState( nWhich, bSrchInParent, ppItem );
+}
+
+SfxItemState SwFormat::GetBackgroundState(std::unique_ptr<SvxBrushItem>& rItem) const
+{
+ if (supportsFullDrawingLayerFillAttributeSet())
+ {
+ // FALLBACKBREAKHERE should not be used; instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFill = getSdrAllFillAttributesHelper();
+
+ // check if the new fill attributes are used
+ if(aFill && aFill->isUsed())
+ {
+ // if yes, fill the local SvxBrushItem using the new fill attributes
+ // as good as possible to have an instance for the pointer to point
+ // to and return as state that it is set
+ rItem = getSvxBrushItemFromSourceSet(m_aSet, RES_BACKGROUND);
+ return SfxItemState::SET;
+ }
+
+ // if not return SfxItemState::DEFAULT to signal that the item is not set
+ return SfxItemState::DEFAULT;
+ }
+
+ const SvxBrushItem* pItem = nullptr;
+ SfxItemState eRet = m_aSet.GetItemState(RES_BACKGROUND, true, &pItem);
+ if (pItem)
+ rItem.reset(pItem->Clone());
+ return eRet;
+}
+
+bool SwFormat::SetFormatAttr( const SfxPoolItem& rAttr )
+{
+ const sal_uInt16 nWhich = rAttr.Which();
+ InvalidateInSwFntCache( nWhich );
+ InvalidateInSwCache( nWhich );
+
+ bool bRet = false;
+
+ if (RES_BACKGROUND == rAttr.Which() && supportsFullDrawingLayerFillAttributeSet())
+ {
+ // FALLBACKBREAKHERE should not be used; instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ SAL_INFO("sw.core", "Do no longer use SvxBrushItem, instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST] FillAttributes (simple fallback is in place and used)");
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*m_aSet.GetPool());
+ const SvxBrushItem& rSource = rAttr.StaticWhichCast(RES_BACKGROUND);
+
+ // fill a local ItemSet with the attributes corresponding as good as possible
+ // to the new fill properties [XATTR_FILL_FIRST .. XATTR_FILL_LAST] and set these
+ // as ItemSet
+ setSvxBrushItemAsFillAttributesToTargetSet(rSource, aTempSet);
+
+ if(IsModifyLocked())
+ {
+ bRet = m_aSet.Put( aTempSet );
+ if( bRet )
+ {
+ m_aSet.SetModifyAtAttr( this );
+ }
+ }
+ else
+ {
+ SwAttrSet aOld(*m_aSet.GetPool(), m_aSet.GetRanges()), aNew(*m_aSet.GetPool(), m_aSet.GetRanges());
+
+ bRet = m_aSet.Put_BC(aTempSet, &aOld, &aNew);
+
+ if(bRet)
+ {
+ m_aSet.SetModifyAtAttr(this);
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ }
+ }
+
+ return bRet;
+ }
+
+ // if Modify is locked then no modifications will be sent;
+ // but call Modify always for FrameFormats
+ const sal_uInt16 nFormatWhich = Which();
+ if( IsModifyLocked() ||
+ ( !HasWriterListeners() &&
+ (RES_GRFFMTCOLL == nFormatWhich ||
+ RES_TXTFMTCOLL == nFormatWhich ) ) )
+ {
+ bRet = nullptr != m_aSet.Put( rAttr );
+ if( bRet )
+ m_aSet.SetModifyAtAttr( this );
+ // #i71574#
+ if ( nFormatWhich == RES_TXTFMTCOLL && rAttr.Which() == RES_PARATR_NUMRULE )
+ {
+ TextFormatCollFunc::CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle( this );
+ }
+ }
+ else
+ {
+ // copy only array with attributes delta
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+
+ bRet = m_aSet.Put_BC( rAttr, &aOld, &aNew );
+ if( bRet )
+ {
+ // some special treatments for attributes
+ m_aSet.SetModifyAtAttr( this );
+
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ }
+ }
+ return bRet;
+}
+
+bool SwFormat::SetFormatAttr( const SfxItemSet& rSet )
+{
+ if( !rSet.Count() )
+ return false;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ InvalidateInSwFntCache(RES_ATTRSET_CHG);
+
+ bool bRet = false;
+
+ // Use local copy to be able to apply needed changes, e.g. call
+ // CheckForUniqueItemForLineFillNameOrIndex which is needed for NameOrIndex stuff
+ SfxItemSet aTempSet(rSet);
+
+ // Need to check for unique item for DrawingLayer items of type NameOrIndex
+ // and evtl. correct that item to ensure unique names for that type. This call may
+ // modify/correct entries inside of the given SfxItemSet
+ if(GetDoc())
+ {
+ GetDoc()->CheckForUniqueItemForLineFillNameOrIndex(aTempSet);
+ }
+
+ if (supportsFullDrawingLayerFillAttributeSet())
+ {
+ if(const SvxBrushItem* pSource = aTempSet.GetItemIfSet(RES_BACKGROUND, false))
+ {
+ // FALLBACKBREAKHERE should not be used; instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ SAL_INFO("sw.core", "Do no longer use SvxBrushItem, instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST] FillAttributes (simple fallback is in place and used)");
+
+ // copy all items to be set anyways to a local ItemSet with is also prepared for the new
+ // fill attribute ranges [XATTR_FILL_FIRST .. XATTR_FILL_LAST]. Add the attributes
+ // corresponding as good as possible to the new fill properties and set the whole ItemSet
+ setSvxBrushItemAsFillAttributesToTargetSet(*pSource, aTempSet);
+
+ if(IsModifyLocked())
+ {
+ bRet = m_aSet.Put( aTempSet );
+ if( bRet )
+ {
+ m_aSet.SetModifyAtAttr( this );
+ }
+ }
+ else
+ {
+ SwAttrSet aOld(*m_aSet.GetPool(), m_aSet.GetRanges()), aNew(*m_aSet.GetPool(), m_aSet.GetRanges());
+
+ bRet = m_aSet.Put_BC(aTempSet, &aOld, &aNew);
+
+ if(bRet)
+ {
+ m_aSet.SetModifyAtAttr(this);
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ }
+ }
+
+ return bRet;
+ }
+ }
+
+ // if Modify is locked then no modifications will be sent;
+ // but call Modify always for FrameFormats
+ const sal_uInt16 nFormatWhich = Which();
+ if ( IsModifyLocked() ||
+ ( !HasWriterListeners() &&
+ ( RES_GRFFMTCOLL == nFormatWhich ||
+ RES_TXTFMTCOLL == nFormatWhich ) ) )
+ {
+ bRet = m_aSet.Put( aTempSet );
+ if( bRet )
+ m_aSet.SetModifyAtAttr( this );
+ // #i71574#
+ if ( nFormatWhich == RES_TXTFMTCOLL )
+ {
+ TextFormatCollFunc::CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle( this );
+ }
+ }
+ else
+ {
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+ bRet = m_aSet.Put_BC( aTempSet, &aOld, &aNew );
+ if( bRet )
+ {
+ // some special treatments for attributes
+ m_aSet.SetModifyAtAttr( this );
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ }
+ }
+ return bRet;
+}
+
+// remove Hint using nWhich from array with delta
+bool SwFormat::ResetFormatAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
+{
+ if( !m_aSet.Count() )
+ return false;
+
+ if( !nWhich2 || nWhich2 < nWhich1 )
+ nWhich2 = nWhich1; // then set to 1st ID, only this item
+
+ for( sal_uInt16 n = nWhich1; n < nWhich2; ++n )
+ InvalidateInSwFntCache( n );
+ for( sal_uInt16 n = nWhich1; n < nWhich2 && IsInCache(); ++n )
+ InvalidateInSwCache( n );
+
+ // if Modify is locked then no modifications will be sent
+ if( IsModifyLocked() )
+ return 0 != (( nWhich2 == nWhich1 )
+ ? m_aSet.ClearItem( nWhich1 )
+ : m_aSet.ClearItem_BC( nWhich1, nWhich2 ));
+
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+ bool bRet = 0 != m_aSet.ClearItem_BC( nWhich1, nWhich2, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ return bRet;
+}
+
+// #i73790#
+sal_uInt16 SwFormat::ResetAllFormatAttr()
+{
+ if( !m_aSet.Count() )
+ return 0;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ InvalidateInSwFntCache(RES_ATTRSET_CHG);
+
+ // if Modify is locked then no modifications will be sent
+ if( IsModifyLocked() )
+ return m_aSet.ClearItem();
+
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+ bool bRet = 0 != m_aSet.ClearItem_BC( 0, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+ return aNew.Count();
+}
+
+void SwFormat::DelDiffs( const SfxItemSet& rSet )
+{
+ if( !m_aSet.Count() )
+ return;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ InvalidateInSwFntCache(RES_ATTRSET_CHG);
+
+ // if Modify is locked then no modifications will be sent
+ if( IsModifyLocked() )
+ {
+ m_aSet.Intersect( rSet );
+ return;
+ }
+
+ SwAttrSet aOld( *m_aSet.GetPool(), m_aSet.GetRanges() ),
+ aNew( *m_aSet.GetPool(), m_aSet.GetRanges() );
+ bool bRet = 0 != m_aSet.Intersect_BC( rSet, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, m_aSet, aOld, aNew);
+}
+
+void SwFormat::SetPageFormatToDefault()
+{
+ const sal_Int32 nSize = o3tl::convert(2, o3tl::Length::cm, o3tl::Length::twip);
+ SetFormatAttr(SvxLRSpaceItem(nSize, nSize, nSize, 0, RES_LR_SPACE));
+ SetFormatAttr(SvxULSpaceItem(nSize, nSize, RES_UL_SPACE));
+}
+
+/** SwFormat::IsBackgroundTransparent
+
+ Virtual method to determine, if background of format is transparent.
+ Default implementation returns false. Thus, subclasses have to override
+ method, if the specific subclass can have a transparent background.
+
+ @return false, default implementation
+*/
+bool SwFormat::IsBackgroundTransparent() const
+{
+ return false;
+}
+
+/*
+ * Document Interface Access
+ */
+const IDocumentSettingAccess& SwFormat::getIDocumentSettingAccess() const { return GetDoc()->GetDocumentSettingManager(); }
+const IDocumentDrawModelAccess& SwFormat::getIDocumentDrawModelAccess() const { return GetDoc()->getIDocumentDrawModelAccess(); }
+IDocumentDrawModelAccess& SwFormat::getIDocumentDrawModelAccess() { return GetDoc()->getIDocumentDrawModelAccess(); }
+const IDocumentLayoutAccess& SwFormat::getIDocumentLayoutAccess() const { return GetDoc()->getIDocumentLayoutAccess(); }
+IDocumentLayoutAccess& SwFormat::getIDocumentLayoutAccess() { return GetDoc()->getIDocumentLayoutAccess(); }
+IDocumentTimerAccess& SwFormat::getIDocumentTimerAccess() { return GetDoc()->getIDocumentTimerAccess(); }
+IDocumentFieldsAccess& SwFormat::getIDocumentFieldsAccess() { return GetDoc()->getIDocumentFieldsAccess(); }
+IDocumentChartDataProviderAccess& SwFormat::getIDocumentChartDataProviderAccess() { return GetDoc()->getIDocumentChartDataProviderAccess(); }
+
+void SwFormat::GetGrabBagItem(uno::Any& rVal) const
+{
+ if (m_pGrabBagItem)
+ m_pGrabBagItem->QueryValue(rVal);
+ else
+ rVal <<= uno::Sequence<beans::PropertyValue>();
+}
+
+void SwFormat::SetGrabBagItem(const uno::Any& rVal)
+{
+ if (!m_pGrabBagItem)
+ m_pGrabBagItem = std::make_shared<SfxGrabBagItem>();
+
+ m_pGrabBagItem->PutValue(rVal, 0);
+}
+
+std::unique_ptr<SvxBrushItem> SwFormat::makeBackgroundBrushItem(bool bInP) const
+{
+ if (supportsFullDrawingLayerFillAttributeSet())
+ {
+ // FALLBACKBREAKHERE should not be used; instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ SAL_INFO("sw.core", "Do no longer use SvxBrushItem, instead use [XATTR_FILL_FIRST .. XATTR_FILL_LAST] FillAttributes (simple fallback is in place and used)");
+
+ // fill the local static SvxBrushItem from the current ItemSet so that
+ // the fill attributes [XATTR_FILL_FIRST .. XATTR_FILL_LAST] are used
+ // as good as possible to create a fallback representation and return that
+ return getSvxBrushItemFromSourceSet(m_aSet, RES_BACKGROUND, bInP);
+ }
+
+ return std::unique_ptr<SvxBrushItem>(m_aSet.GetBackground(bInP).Clone());
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwFormat::getSdrAllFillAttributesHelper() const
+{
+ return drawinglayer::attribute::SdrAllFillAttributesHelperPtr();
+}
+
+void SwFormat::RemoveAllUnos()
+{
+ SwPtrMsgPoolItem aMsgHint(RES_REMOVE_UNO_OBJECT, this);
+ SwClientNotify(*this, sw::LegacyModifyHint(&aMsgHint, &aMsgHint));
+}
+
+SwFormatsBase::~SwFormatsBase()
+{}
+
+SwFormat* SwFormatsBase::FindFormatByName( const OUString& rName ) const
+{
+ SwFormat* pFnd = nullptr;
+ for( size_t n = 0; n < GetFormatCount(); ++n )
+ {
+ // Does the Doc already contain the template?
+ if( GetFormat(n)->HasName( rName ) )
+ {
+ pFnd = GetFormat(n);
+ break;
+ }
+ }
+ return pFnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/hints.cxx b/sw/source/core/attr/hints.cxx
new file mode 100644
index 000000000..8016037bf
--- /dev/null
+++ b/sw/source/core/attr/hints.cxx
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <swtypes.hxx>
+#include <svl/languageoptions.hxx>
+#include <vcl/outdev.hxx>
+#include <osl/diagnose.h>
+
+SwFormatChg::SwFormatChg( SwFormat* pFormat )
+ : SwMsgPoolItem( RES_FMT_CHG ), pChangedFormat( pFormat )
+{
+}
+
+SwInsText::SwInsText(sal_Int32 const nP, sal_Int32 const nL, bool const isInFMCommand, bool const isInFMResult)
+ : SwMsgPoolItem( RES_INS_TXT )
+ , nPos( nP ), nLen( nL )
+ , isInsideFieldmarkCommand(isInFMCommand)
+ , isInsideFieldmarkResult(isInFMResult)
+{
+}
+
+SwDelChr::SwDelChr( sal_Int32 nP )
+ : SwMsgPoolItem( RES_DEL_CHR ), nPos( nP )
+{
+}
+
+SwDelText::SwDelText( sal_Int32 nS, sal_Int32 nL )
+ : SwMsgPoolItem( RES_DEL_TXT ), nStart( nS ), nLen( nL )
+{
+}
+
+namespace sw {
+
+MoveText::MoveText(SwTextNode *const pD, sal_Int32 const nD, sal_Int32 const nS, sal_Int32 const nL)
+ : pDestNode(pD), nDestStart(nD), nSourceStart(nS), nLen(nL)
+{
+}
+
+RedlineDelText::RedlineDelText(sal_Int32 const nS, sal_Int32 const nL)
+ : nStart(nS), nLen(nL)
+{
+}
+
+RedlineUnDelText::RedlineUnDelText(sal_Int32 const nS, sal_Int32 const nL)
+ : nStart(nS), nLen(nL)
+{
+}
+
+} // namespace sw
+
+SwUpdateAttr::SwUpdateAttr( sal_Int32 nS, sal_Int32 nE, sal_uInt16 nW )
+ : SwMsgPoolItem( RES_UPDATE_ATTR ), m_nStart( nS ), m_nEnd( nE ), m_nWhichAttr( nW )
+{
+}
+
+SwUpdateAttr::SwUpdateAttr( sal_Int32 nS, sal_Int32 nE, sal_uInt16 nW, std::vector<sal_uInt16> aW )
+ : SwMsgPoolItem( RES_UPDATE_ATTR ), m_nStart( nS ), m_nEnd( nE ), m_nWhichAttr( nW ), m_aWhichFmtAttrs( aW )
+{
+}
+
+SwRefMarkFieldUpdate::SwRefMarkFieldUpdate( OutputDevice* pOutput )
+ : SwMsgPoolItem( RES_REFMARKFLD_UPDATE ),
+ pOut( pOutput )
+{
+ OSL_ENSURE( pOut, "No OutputDevice pointer" );
+}
+
+SwDocPosUpdate::SwDocPosUpdate( const SwTwips nDcPos )
+ : SwMsgPoolItem( RES_DOCPOS_UPDATE ), nDocPos(nDcPos)
+{
+}
+
+SwTableFormulaUpdate::SwTableFormulaUpdate( const SwTable* pNewTable )
+ : SwMsgPoolItem( RES_TABLEFML_UPDATE ),
+ m_pTable( pNewTable ), m_pHistory( nullptr ), m_nSplitLine( USHRT_MAX ),
+ m_eFlags( TBL_CALC )
+{
+ m_aData.pDelTable = nullptr;
+ m_bModified = m_bBehindSplitLine = false;
+ OSL_ENSURE( m_pTable, "No Table pointer" );
+}
+
+SwAutoFormatGetDocNode::SwAutoFormatGetDocNode( const SwNodes* pNds )
+ : SwMsgPoolItem( RES_AUTOFMT_DOCNODE ), pNodes( pNds )
+{
+}
+
+SwAttrSetChg::SwAttrSetChg( const SwAttrSet& rTheSet, SwAttrSet& rSet )
+ : SwMsgPoolItem( RES_ATTRSET_CHG ),
+ m_bDelSet( false ),
+ m_pChgSet( &rSet ),
+ m_pTheChgdSet( &rTheSet )
+{
+}
+
+SwAttrSetChg::SwAttrSetChg( const SwAttrSetChg& rChgSet )
+ : SwMsgPoolItem( RES_ATTRSET_CHG ),
+ m_bDelSet( true ),
+ m_pTheChgdSet( rChgSet.m_pTheChgdSet )
+{
+ m_pChgSet = new SwAttrSet( *rChgSet.m_pChgSet );
+}
+
+SwAttrSetChg::~SwAttrSetChg()
+{
+ if( m_bDelSet )
+ delete m_pChgSet;
+}
+
+#ifdef DBG_UTIL
+void SwAttrSetChg::ClearItem( sal_uInt16 nWhch )
+{
+ OSL_ENSURE( m_bDelSet, "The Set may not be changed!" );
+ m_pChgSet->ClearItem( nWhch );
+}
+#endif
+
+SwMsgPoolItem::SwMsgPoolItem( sal_uInt16 nWhch )
+ : SfxPoolItem( nWhch )
+{
+}
+
+bool SwMsgPoolItem::operator==( const SfxPoolItem& ) const
+{
+ assert( false && "SwMsgPoolItem knows no ==" );
+ return false;
+}
+
+SwMsgPoolItem* SwMsgPoolItem::Clone( SfxItemPool* ) const
+{
+ OSL_FAIL( "SwMsgPoolItem knows no Clone" );
+ return nullptr;
+}
+
+#if OSL_DEBUG_LEVEL > 0
+const SfxPoolItem* GetDfltAttr( sal_uInt16 nWhich )
+{
+ OSL_ASSERT( nWhich < POOLATTR_END && nWhich >= POOLATTR_BEGIN );
+
+ SfxPoolItem *pHt = aAttrTab[ nWhich - POOLATTR_BEGIN ];
+ OSL_ENSURE( pHt, "GetDfltFormatAttr(): Dflt == 0" );
+ return pHt;
+}
+#else
+const SfxPoolItem* GetDfltAttr( sal_uInt16 nWhich )
+{
+ return aAttrTab[ nWhich - POOLATTR_BEGIN ];
+}
+#endif
+
+SwVirtPageNumInfo::SwVirtPageNumInfo( const SwPageFrame *pPg ) :
+ SwMsgPoolItem( RES_VIRTPAGENUM_INFO ), m_pPage( nullptr ), m_pOrigPage( pPg ), m_pFrame( nullptr )
+{
+}
+
+SwFindNearestNode::SwFindNearestNode( const SwNode& rNd )
+ : SwMsgPoolItem( RES_FINDNEARESTNODE ), m_pNode( &rNd ), m_pFound( nullptr )
+{
+}
+
+void SwFindNearestNode::CheckNode( const SwNode& rNd )
+{
+ if( &m_pNode->GetNodes() == &rNd.GetNodes() )
+ {
+ SwNodeOffset nIdx = rNd.GetIndex();
+ if( nIdx < m_pNode->GetIndex() &&
+ ( !m_pFound || nIdx > m_pFound->GetIndex() ) &&
+ nIdx > rNd.GetNodes().GetEndOfExtras().GetIndex() )
+ m_pFound = &rNd;
+ }
+}
+
+sal_uInt16 GetWhichOfScript( sal_uInt16 nWhich, sal_uInt16 nScript )
+{
+ static const sal_uInt16 aLangMap[3] =
+ { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE };
+ static const sal_uInt16 aFontMap[3] =
+ { RES_CHRATR_FONT, RES_CHRATR_CJK_FONT, RES_CHRATR_CTL_FONT};
+ static const sal_uInt16 aFontSizeMap[3] =
+ { RES_CHRATR_FONTSIZE, RES_CHRATR_CJK_FONTSIZE, RES_CHRATR_CTL_FONTSIZE };
+ static const sal_uInt16 aWeightMap[3] =
+ { RES_CHRATR_WEIGHT, RES_CHRATR_CJK_WEIGHT, RES_CHRATR_CTL_WEIGHT};
+ static const sal_uInt16 aPostureMap[3] =
+ { RES_CHRATR_POSTURE, RES_CHRATR_CJK_POSTURE, RES_CHRATR_CTL_POSTURE};
+
+ const sal_uInt16* pM;
+ switch( nWhich )
+ {
+ case RES_CHRATR_LANGUAGE:
+ case RES_CHRATR_CJK_LANGUAGE:
+ case RES_CHRATR_CTL_LANGUAGE:
+ pM = aLangMap;
+ break;
+
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_CJK_FONT:
+ case RES_CHRATR_CTL_FONT:
+ pM = aFontMap;
+ break;
+
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_CJK_FONTSIZE:
+ case RES_CHRATR_CTL_FONTSIZE:
+ pM = aFontSizeMap;
+ break;
+
+ case RES_CHRATR_WEIGHT:
+ case RES_CHRATR_CJK_WEIGHT:
+ case RES_CHRATR_CTL_WEIGHT:
+ pM = aWeightMap;
+ break;
+
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CTL_POSTURE:
+ pM = aPostureMap;
+ break;
+
+ default:
+ pM = nullptr;
+ }
+
+ sal_uInt16 nRet;
+ if( pM )
+ {
+ using namespace ::com::sun::star;
+ {
+ if( i18n::ScriptType::WEAK == nScript )
+ nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+ switch( nScript)
+ {
+ case i18n::ScriptType::COMPLEX:
+ ++pM;
+ [[fallthrough]];
+ case i18n::ScriptType::ASIAN:
+ ++pM;
+ [[fallthrough]];
+ default:
+ nRet = *pM;
+ }
+ }
+ }
+ else
+ nRet = nWhich;
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/attr/swatrset.cxx b/sw/source/core/attr/swatrset.cxx
new file mode 100644
index 000000000..de014a521
--- /dev/null
+++ b/sw/source/core/attr/swatrset.cxx
@@ -0,0 +1,461 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+
+#include <cellatr.hxx>
+#include <charfmt.hxx>
+#include <fchrfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <editeng/editeng.hxx>
+#include <fmtanchr.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtautofmt.hxx>
+#include <hintids.hxx>
+#include <list.hxx>
+#include <node.hxx>
+#include <numrule.hxx>
+#include <pagedesc.hxx>
+#include <paratr.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+#include <svl/whiter.hxx>
+
+#include <svx/svdpool.hxx>
+#include <svx/sxenditm.hxx>
+#include <svx/sdsxyitm.hxx>
+
+SwAttrPool::SwAttrPool( SwDoc* pD )
+ : SfxItemPool( "SWG",
+ POOLATTR_BEGIN, POOLATTR_END-1,
+ aSlotTab, &aAttrTab ),
+ m_pDoc( pD )
+{
+ // create SfxItemPool and EditEngine pool and add these in a chain. These
+ // belong us and will be removed/destroyed in removeAndDeleteSecondaryPools() used from
+ // the destructor
+ rtl::Reference<SfxItemPool> pSdrPool = new SdrItemPool(this);
+
+ // #75371# change DefaultItems for the SdrEdgeObj distance items
+ // to TWIPS.
+ constexpr tools::Long nDefEdgeDist
+ = o3tl::convert(500, o3tl::Length::mm100, o3tl::Length::twip);
+
+ pSdrPool->SetPoolDefaultItem(SdrEdgeNode1HorzDistItem(nDefEdgeDist));
+ pSdrPool->SetPoolDefaultItem(SdrEdgeNode1VertDistItem(nDefEdgeDist));
+ pSdrPool->SetPoolDefaultItem(SdrEdgeNode2HorzDistItem(nDefEdgeDist));
+ pSdrPool->SetPoolDefaultItem(SdrEdgeNode2VertDistItem(nDefEdgeDist));
+
+ // #i33700# // Set shadow distance defaults as PoolDefaultItems
+ constexpr tools::Long nDefShadowDist
+ = o3tl::convert(300, o3tl::Length::mm100, o3tl::Length::twip);
+ pSdrPool->SetPoolDefaultItem(makeSdrShadowXDistItem(nDefShadowDist));
+ pSdrPool->SetPoolDefaultItem(makeSdrShadowYDistItem(nDefShadowDist));
+
+ rtl::Reference<SfxItemPool> pEEgPool = EditEngine::CreatePool();
+
+ pSdrPool->SetSecondaryPool(pEEgPool.get());
+
+ if(GetFrozenIdRanges().empty())
+ {
+ FreezeIdRanges();
+ }
+ else
+ {
+ pSdrPool->FreezeIdRanges();
+ }
+}
+
+SwAttrPool::~SwAttrPool()
+{
+ // cleanup secondary pools
+ SfxItemPool *pSdrPool = GetSecondaryPool();
+ // first delete the items, then break the linking
+ pSdrPool->Delete();
+ SetSecondaryPool(nullptr);
+}
+
+SwAttrSet::SwAttrSet( SwAttrPool& rPool, sal_uInt16 nWh1, sal_uInt16 nWh2 )
+ : SfxItemSet( rPool, nWh1, nWh2 ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
+{
+}
+
+SwAttrSet::SwAttrSet( SwAttrPool& rPool, const WhichRangesContainer& nWhichPairTable )
+ : SfxItemSet( rPool, nWhichPairTable )
+ , m_pOldSet( nullptr ), m_pNewSet( nullptr )
+{
+}
+
+SwAttrSet::SwAttrSet( const SwAttrSet& rSet )
+ : SfxItemSet( rSet ), m_pOldSet( nullptr ), m_pNewSet( nullptr )
+{
+}
+
+std::unique_ptr<SfxItemSet> SwAttrSet::Clone( bool bItems, SfxItemPool *pToPool ) const
+{
+ if ( pToPool && pToPool != GetPool() )
+ {
+ SwAttrPool* pAttrPool = dynamic_cast< SwAttrPool* >(pToPool);
+ std::unique_ptr<SfxItemSet> pTmpSet;
+ if ( !pAttrPool )
+ pTmpSet = SfxItemSet::Clone( bItems, pToPool );
+ else
+ {
+ pTmpSet.reset(new SwAttrSet( *pAttrPool, GetRanges() ));
+ if ( bItems )
+ {
+ SfxWhichIter aIter(*pTmpSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ const SfxPoolItem* pItem;
+ if ( SfxItemState::SET == GetItemState( nWhich, false, &pItem ) )
+ pTmpSet->Put( *pItem );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ }
+ return pTmpSet;
+ }
+ else
+ return std::unique_ptr<SfxItemSet>(
+ bItems
+ ? new SwAttrSet( *this )
+ : new SwAttrSet( *GetPool(), GetRanges() ));
+}
+
+bool SwAttrSet::Put_BC( const SfxPoolItem& rAttr,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ m_pNewSet = pNew;
+ m_pOldSet = pOld;
+ bool bRet = nullptr != SfxItemSet::Put( rAttr );
+ m_pOldSet = m_pNewSet = nullptr;
+ return bRet;
+}
+
+bool SwAttrSet::Put_BC( const SfxItemSet& rSet,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ m_pNewSet = pNew;
+ m_pOldSet = pOld;
+ bool bRet = SfxItemSet::Put( rSet );
+ m_pOldSet = m_pNewSet = nullptr;
+ return bRet;
+}
+
+sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ m_pNewSet = pNew;
+ m_pOldSet = pOld;
+ sal_uInt16 nRet = SfxItemSet::ClearItem( nWhich );
+ m_pOldSet = m_pNewSet = nullptr;
+ return nRet;
+}
+
+sal_uInt16 SwAttrSet::ClearItem_BC( sal_uInt16 nWhich1, sal_uInt16 nWhich2,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ OSL_ENSURE( nWhich1 <= nWhich2, "no valid range" );
+ m_pNewSet = pNew;
+ m_pOldSet = pOld;
+ sal_uInt16 nRet = 0;
+ for( ; nWhich1 <= nWhich2; ++nWhich1 )
+ nRet = nRet + SfxItemSet::ClearItem( nWhich1 );
+ m_pOldSet = m_pNewSet = nullptr;
+ return nRet;
+}
+
+int SwAttrSet::Intersect_BC( const SfxItemSet& rSet,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ m_pNewSet = pNew;
+ m_pOldSet = pOld;
+ SfxItemSet::Intersect( rSet );
+ m_pOldSet = m_pNewSet = nullptr;
+ return pNew ? pNew->Count() : ( pOld ? pOld->Count() : 0 );
+}
+
+/// Notification callback
+void SwAttrSet::Changed( const SfxPoolItem& rOld, const SfxPoolItem& rNew )
+{
+ if( m_pOldSet )
+ m_pOldSet->PutChgd( rOld );
+ if( m_pNewSet )
+ m_pNewSet->PutChgd( rNew );
+}
+
+/** special treatment for some attributes
+
+ Set the Modify pointer (old pDefinedIn) for the following attributes:
+ - SwFormatDropCaps
+ - SwFormatPageDesc
+
+ (Is called at inserts into formats/nodes)
+*/
+bool SwAttrSet::SetModifyAtAttr( const sw::BroadcastingModify* pModify )
+{
+ bool bSet = false;
+
+ const SwFormatPageDesc* pPageDescItem = GetItemIfSet( RES_PAGEDESC, false );
+ if( pPageDescItem &&
+ pPageDescItem->GetDefinedIn() != pModify )
+ {
+ const_cast<SwFormatPageDesc&>(*pPageDescItem).ChgDefinedIn( pModify );
+ bSet = true;
+ }
+
+ if(SwFormatDrop* pFormatDrop = const_cast<SwFormatDrop*>(GetItemIfSet( RES_PARATR_DROP, false )))
+ {
+ auto pDropDefiner = dynamic_cast<const sw::FormatDropDefiner*>(pModify);
+ // If CharFormat is set and it is set in different attribute pools then
+ // the CharFormat has to be copied.
+ SwCharFormat* pCharFormat = pFormatDrop->GetCharFormat();
+ if(pCharFormat && GetPool() != pCharFormat->GetAttrSet().GetPool())
+ {
+ pCharFormat = GetDoc()->CopyCharFormat(*pCharFormat);
+ pFormatDrop->SetCharFormat(pCharFormat);
+ }
+ pFormatDrop->ChgDefinedIn(pDropDefiner);
+ bSet = true;
+ }
+
+ const SwTableBoxFormula* pBoxFormula = GetItemIfSet( RES_BOXATR_FORMULA, false );
+ if( pBoxFormula && pBoxFormula->GetDefinedIn() != pModify )
+ {
+ const_cast<SwTableBoxFormula&>(*pBoxFormula).ChgDefinedIn( pModify );
+ bSet = true;
+ }
+
+ return bSet;
+}
+
+void SwAttrSet::CopyToModify( sw::BroadcastingModify& rMod ) const
+{
+ // copy attributes across multiple documents if needed
+ SwContentNode* pCNd = dynamic_cast<SwContentNode*>( &rMod );
+ SwFormat* pFormat = dynamic_cast<SwFormat*>( &rMod );
+
+ if( pCNd || pFormat )
+ {
+ if( Count() )
+ {
+ // #i92811#
+ std::unique_ptr<SfxStringItem> pNewListIdItem;
+
+ const SwDoc *pSrcDoc = GetDoc();
+ SwDoc *pDstDoc = pCNd ? &pCNd->GetDoc() : pFormat->GetDoc();
+
+ // Does the NumRule has to be copied?
+ const SwNumRuleItem* pNumRuleItem;
+ if( pSrcDoc != pDstDoc &&
+ (pNumRuleItem = GetItemIfSet( RES_PARATR_NUMRULE, false )) )
+ {
+ const OUString& rNm = pNumRuleItem->GetValue();
+ if( !rNm.isEmpty() )
+ {
+ SwNumRule* pDestRule = pDstDoc->FindNumRulePtr( rNm );
+ if( pDestRule )
+ pDestRule->SetInvalidRule( true );
+ else
+ pDstDoc->MakeNumRule( rNm, pSrcDoc->FindNumRulePtr( rNm ) );
+ }
+ }
+
+ // copy list and if needed also the corresponding list style
+ // for text nodes
+ const SfxStringItem* pStrItem;
+ if ( pSrcDoc != pDstDoc &&
+ pCNd && pCNd->IsTextNode() &&
+ (pStrItem = GetItemIfSet( RES_PARATR_LIST_ID, false )) )
+ {
+ const OUString& sListId = pStrItem->GetValue();
+ if ( !sListId.isEmpty() &&
+ !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) )
+ {
+ const SwList* pList = pSrcDoc->getIDocumentListsAccess().getListByName( sListId );
+ // copy list style, if needed
+ const OUString& sDefaultListStyleName =
+ pList->GetDefaultListStyleName();
+ // #i92811#
+ const SwNumRule* pDstDocNumRule =
+ pDstDoc->FindNumRulePtr( sDefaultListStyleName );
+ if ( !pDstDocNumRule )
+ {
+ pDstDoc->MakeNumRule( sDefaultListStyleName,
+ pSrcDoc->FindNumRulePtr( sDefaultListStyleName ) );
+ }
+ else
+ {
+ const SwNumRule* pSrcDocNumRule =
+ pSrcDoc->FindNumRulePtr( sDefaultListStyleName );
+ // If list id of text node equals the list style's
+ // default list id in the source document, the same
+ // should be hold in the destination document.
+ // Thus, create new list id item.
+ if (pSrcDocNumRule && sListId == pSrcDocNumRule->GetDefaultListId())
+ {
+ pNewListIdItem.reset(new SfxStringItem (
+ RES_PARATR_LIST_ID,
+ pDstDocNumRule->GetDefaultListId() ));
+ }
+ }
+ // check again, if list exist, because <SwDoc::MakeNumRule(..)>
+ // could have also created it.
+ if ( pNewListIdItem == nullptr &&
+ !pDstDoc->getIDocumentListsAccess().getListByName( sListId ) )
+ {
+ // copy list
+ pDstDoc->getIDocumentListsAccess().createList( sListId, sDefaultListStyleName );
+ }
+ }
+ }
+
+ std::optional< SfxItemSet > tmpSet;
+ const SwFormatPageDesc* pPageDescItem;
+ if( pSrcDoc != pDstDoc && (pPageDescItem = GetItemIfSet(
+ RES_PAGEDESC, false )))
+ {
+ const SwPageDesc* pPgDesc = pPageDescItem->GetPageDesc();
+ if( pPgDesc )
+ {
+ tmpSet.emplace(*this);
+
+ SwPageDesc* pDstPgDesc = pDstDoc->FindPageDesc(pPgDesc->GetName());
+ if( !pDstPgDesc )
+ {
+ pDstPgDesc = pDstDoc->MakePageDesc(pPgDesc->GetName());
+ pDstDoc->CopyPageDesc( *pPgDesc, *pDstPgDesc );
+ }
+ SwFormatPageDesc aDesc( pDstPgDesc );
+ aDesc.SetNumOffset( pPageDescItem->GetNumOffset() );
+ tmpSet->Put( aDesc );
+ }
+ }
+
+ const SwFormatAnchor* pAnchorItem;
+ if( pSrcDoc != pDstDoc && (pAnchorItem = GetItemIfSet( RES_ANCHOR, false ))
+ && pAnchorItem->GetContentAnchor() != nullptr )
+ {
+ if( !tmpSet )
+ tmpSet.emplace( *this );
+ // Anchors at any node position cannot be copied to another document, because the SwPosition
+ // would still point to the old document. It needs to be fixed up explicitly.
+ tmpSet->ClearItem( RES_ANCHOR );
+ }
+
+ const SwFormatAutoFormat* pAutoFormatItem;
+ if (pSrcDoc != pDstDoc &&
+ (pAutoFormatItem = GetItemIfSet(RES_PARATR_LIST_AUTOFMT, false)) &&
+ pAutoFormatItem->GetStyleHandle())
+ {
+ SfxItemSet const& rAutoStyle(*pAutoFormatItem->GetStyleHandle());
+ std::shared_ptr<SfxItemSet> const pNewSet(
+ rAutoStyle.SfxItemSet::Clone(true, &pDstDoc->GetAttrPool()));
+
+ // fix up character style, it contains pointers to pSrcDoc
+ if (const SwFormatCharFormat* pCharFormatItem = pNewSet->GetItemIfSet(RES_TXTATR_CHARFMT, false))
+ {
+ SwCharFormat *const pCopy(pDstDoc->CopyCharFormat(*pCharFormatItem->GetCharFormat()));
+ const_cast<SwFormatCharFormat&>(*pCharFormatItem).SetCharFormat(pCopy);
+ }
+
+ SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
+ // TODO: for ODF export we'd need to add it to the autostyle pool
+ item.SetStyleHandle(pNewSet);
+ if (!tmpSet)
+ {
+ tmpSet.emplace(*this);
+ }
+ tmpSet->Put(item);
+ }
+
+ if( tmpSet )
+ {
+ if( pCNd )
+ {
+ // #i92811#
+ if ( pNewListIdItem != nullptr )
+ {
+ tmpSet->Put( std::move(pNewListIdItem) );
+ }
+ pCNd->SetAttr( *tmpSet );
+ }
+ else
+ {
+ pFormat->SetFormatAttr( *tmpSet );
+ }
+ }
+ else if( pCNd )
+ {
+ // #i92811#
+ if ( pNewListIdItem != nullptr )
+ {
+ SfxItemSet aTmpSet( *this );
+ aTmpSet.Put( std::move(pNewListIdItem) );
+ pCNd->SetAttr( aTmpSet );
+ }
+ else
+ {
+ pCNd->SetAttr( *this );
+ }
+ }
+ else
+ {
+ pFormat->SetFormatAttr( *this );
+ }
+ }
+ if (pCNd && pCNd->HasSwAttrSet())
+ {
+ SfxWhichIter it(*this);
+ std::vector<sal_uInt16> toClear;
+ for (sal_uInt16 nWhich = it.FirstWhich(); nWhich != 0; nWhich = it.NextWhich())
+ {
+ if (GetItemState(nWhich, false) != SfxItemState::SET
+ && pCNd->GetSwAttrSet().GetItemState(nWhich, false) == SfxItemState::SET)
+ {
+ toClear.emplace_back(nWhich);
+ }
+ }
+ if (!toClear.empty())
+ {
+ pCNd->ResetAttr(toClear);
+ }
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_FAIL("neither Format nor ContentNode - no Attributes copied");
+#endif
+}
+
+/// check if ID is in range of attribute set IDs
+bool IsInRange( const WhichRangesContainer& pRange, const sal_uInt16 nId )
+{
+ for(const auto& rPair : pRange)
+ {
+ if( rPair.first <= nId && nId <= rPair.second )
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/SwSmartTagMgr.cxx b/sw/source/core/bastyp/SwSmartTagMgr.cxx
new file mode 100644
index 000000000..cc769d870
--- /dev/null
+++ b/sw/source/core/bastyp/SwSmartTagMgr.cxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwSmartTagMgr.hxx>
+
+#include <docsh.hxx>
+#include <swmodule.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+rtl::Reference<SwSmartTagMgr> SwSmartTagMgr::spTheSwSmartTagMgr;
+
+SwSmartTagMgr& SwSmartTagMgr::Get()
+{
+ if (!spTheSwSmartTagMgr)
+ {
+ spTheSwSmartTagMgr = new SwSmartTagMgr(SwDocShell::Factory().GetModuleName());
+ spTheSwSmartTagMgr->Init(u"Writer");
+ }
+ return *spTheSwSmartTagMgr;
+}
+
+SwSmartTagMgr::SwSmartTagMgr(const OUString& rModuleName)
+ : SmartTagMgr(rModuleName)
+{
+}
+
+SwSmartTagMgr::~SwSmartTagMgr() {}
+
+void SwSmartTagMgr::modified(const lang::EventObject& rEO)
+{
+ SolarMutexGuard aGuard;
+
+ // Installed recognizers have changed. We remove all existing smart tags:
+ SwModule::CheckSpellChanges(false, true, true, true);
+
+ SmartTagMgr::modified(rEO);
+}
+
+void SwSmartTagMgr::changesOccurred(const util::ChangesEvent& rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ // Configuration has changed. We remove all existing smart tags:
+ SwModule::CheckSpellChanges(false, true, true, true);
+
+ SmartTagMgr::changesOccurred(rEvent);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/bparr.cxx b/sw/source/core/bastyp/bparr.cxx
new file mode 100644
index 000000000..b99385bfd
--- /dev/null
+++ b/sw/source/core/bastyp/bparr.cxx
@@ -0,0 +1,505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <bparr.hxx>
+#include <tools/long.hxx>
+#include <limits.h>
+#include <string.h>
+
+/** Resize block management by this constant.
+ As a result there are approx. 20 * MAXENTRY == 20000 entries available */
+const sal_uInt16 nBlockGrowSize = 20;
+
+#if OSL_DEBUG_LEVEL > 2
+#define CHECKIDX( p, n, i, c ) CheckIdx( p, n, i, c );
+void CheckIdx( BlockInfo** ppInf, sal_uInt16 nBlock, sal_Int32 nSize, sal_uInt16 nCur )
+{
+ assert( !nSize || nCur < nBlock ); // BigPtrArray: CurIndex invalid
+
+ sal_Int32 nIdx = 0;
+ for( sal_uInt16 nCnt = 0; nCnt < nBlock; ++nCnt, ++ppInf )
+ {
+ nIdx += (*ppInf)->nElem;
+ // Array with holes is not allowed
+ assert( !nCnt || (*(ppInf-1))->nEnd + 1 == (*ppInf)->nStart );
+ }
+ assert(nIdx == nSize); // invalid count in nSize
+}
+#else
+#define CHECKIDX( p, n, i, c )
+#endif
+
+BigPtrArray::BigPtrArray()
+{
+ m_nBlock = m_nCur = 0;
+ m_nSize = 0;
+ m_nMaxBlock = nBlockGrowSize;
+ m_ppInf.reset( new BlockInfo* [ m_nMaxBlock ] );
+}
+
+BigPtrArray::~BigPtrArray()
+{
+ if( m_nBlock )
+ {
+ BlockInfo** pp = m_ppInf.get();
+ for( sal_uInt16 n = 0; n < m_nBlock; ++n, ++pp )
+ {
+ delete *pp;
+ }
+ }
+}
+
+// Also moving is done simply here. Optimization is useless because of the
+// division of this field into multiple parts.
+void BigPtrArray::Move( sal_Int32 from, sal_Int32 to )
+{
+ if (from != to)
+ {
+ sal_uInt16 cur = Index2Block( from );
+ BlockInfo* p = m_ppInf[ cur ];
+ BigPtrEntry* pElem = p->mvData[ from - p->nStart ];
+ Insert( pElem, to ); // insert first, then delete!
+ Remove( ( to < from ) ? ( from + 1 ) : from );
+ }
+}
+
+BigPtrEntry* BigPtrArray::operator[]( sal_Int32 idx ) const
+{
+ assert(idx < m_nSize); // operator[]: Index out of bounds
+ m_nCur = Index2Block( idx );
+ BlockInfo* p = m_ppInf[ m_nCur ];
+ return p->mvData[ idx - p->nStart ];
+}
+
+/** Search a block at a given position */
+sal_uInt16 BigPtrArray::Index2Block( sal_Int32 pos ) const
+{
+ // last used block?
+ BlockInfo* p = m_ppInf[ m_nCur ];
+ if( p->nStart <= pos && p->nEnd >= pos )
+ return m_nCur;
+ // Index = 0?
+ if( !pos )
+ return 0;
+
+ // following one?
+ if( m_nCur < ( m_nBlock - 1 ) )
+ {
+ p = m_ppInf[ m_nCur+1 ];
+ if( p->nStart <= pos && p->nEnd >= pos )
+ return m_nCur+1;
+ }
+ // previous one?
+ else if( pos < p->nStart && m_nCur > 0 )
+ {
+ p = m_ppInf[ m_nCur-1 ];
+ if( p->nStart <= pos && p->nEnd >= pos )
+ return m_nCur-1;
+ }
+
+ // binary search: always successful
+ sal_uInt16 lower = 0, upper = m_nBlock - 1;
+ sal_uInt16 cur = 0;
+ for(;;)
+ {
+ sal_uInt16 n = lower + ( upper - lower ) / 2;
+ cur = ( n == cur ) ? n+1 : n;
+ p = m_ppInf[ cur ];
+ if( p->nStart <= pos && p->nEnd >= pos )
+ return cur;
+
+ if( p->nStart > pos )
+ upper = cur;
+ else
+ lower = cur;
+ }
+}
+
+/** Update all index areas
+
+ @param pos last correct block (starting point)
+*/
+void BigPtrArray::UpdIndex( sal_uInt16 pos )
+{
+ BlockInfo** pp = m_ppInf.get() + pos;
+ sal_Int32 idx = (*pp)->nEnd + 1;
+ while( ++pos < m_nBlock )
+ {
+ BlockInfo* p = *++pp;
+ p->nStart = idx;
+ idx += p->nElem;
+ p->nEnd = idx - 1;
+ }
+}
+
+/** Create and insert new block
+
+ Existing blocks will be moved rearward.
+
+ @param pos Position at which the new block should be created.
+*/
+BlockInfo* BigPtrArray::InsBlock( sal_uInt16 pos )
+{
+ if( m_nBlock == m_nMaxBlock )
+ {
+ // than extend the array first
+ BlockInfo** ppNew = new BlockInfo* [ m_nMaxBlock + nBlockGrowSize ];
+ memcpy( ppNew, m_ppInf.get(), m_nMaxBlock * sizeof( BlockInfo* ));
+ m_nMaxBlock += nBlockGrowSize;
+ m_ppInf.reset( ppNew );
+ }
+ if( pos != m_nBlock )
+ {
+ memmove( m_ppInf.get() + pos+1, m_ppInf.get() + pos,
+ ( m_nBlock - pos ) * sizeof( BlockInfo* ));
+ }
+ ++m_nBlock;
+ BlockInfo* p = new BlockInfo;
+ m_ppInf[ pos ] = p;
+
+ if( pos )
+ p->nStart = p->nEnd = m_ppInf[ pos-1 ]->nEnd + 1;
+ else
+ p->nStart = p->nEnd = 0;
+
+ p->nEnd--; // no elements
+ p->nElem = 0;
+ p->pBigArr = this;
+ return p;
+}
+
+void BigPtrArray::BlockDel( sal_uInt16 nDel )
+{
+ m_nBlock = m_nBlock - nDel;
+ if( m_nMaxBlock - m_nBlock > nBlockGrowSize )
+ {
+ // than shrink array
+ nDel = (( m_nBlock / nBlockGrowSize ) + 1 ) * nBlockGrowSize;
+ BlockInfo** ppNew = new BlockInfo* [ nDel ];
+ memcpy( ppNew, m_ppInf.get(), m_nBlock * sizeof( BlockInfo* ));
+ m_ppInf.reset( ppNew );
+ m_nMaxBlock = nDel;
+ }
+}
+
+void BigPtrArray::Insert( BigPtrEntry* pElem, sal_Int32 pos )
+{
+ CHECKIDX( m_ppInf.get(), m_nBlock, m_nSize, m_nCur );
+
+ BlockInfo* p;
+ sal_uInt16 cur;
+ if( !m_nSize )
+ {
+ // special case: insert first element
+ cur = 0;
+ p = InsBlock( cur );
+ }
+ else if( pos == m_nSize )
+ {
+ // special case: insert at end
+ cur = m_nBlock - 1;
+ p = m_ppInf[ cur ];
+ if( p->nElem == MAXENTRY )
+ // the last block is full, create a new one
+ p = InsBlock( ++cur );
+ }
+ else
+ {
+ // standard case:
+ cur = Index2Block( pos );
+ p = m_ppInf[ cur ];
+ }
+
+ if( p->nElem == MAXENTRY )
+ {
+ // does the last entry fit into the next block?
+ BlockInfo* q;
+ if( cur < ( m_nBlock - 1 ) && m_ppInf[ cur+1 ]->nElem < MAXENTRY )
+ {
+ q = m_ppInf[ cur+1 ];
+ if( q->nElem )
+ {
+ int nCount = q->nElem;
+ auto pFrom = q->mvData.begin() + nCount;
+ auto pTo = pFrom + 1;
+ while( nCount-- )
+ {
+ *--pTo = *--pFrom;
+ ++((*pTo)->m_nOffset);
+ }
+ }
+ q->nStart--;
+ q->nEnd--;
+ }
+ else
+ {
+ // If it does not fit, then insert a new block. But if there is more
+ // than 50% space in the array then compress first.
+ if( /*nBlock == nMaxBlock &&*/
+ m_nBlock > ( m_nSize / ( MAXENTRY / 2 ) ) &&
+ cur >= Compress() )
+ {
+ // Something was moved before the current position and all
+ // pointer might be invalid. Thus restart Insert.
+ Insert( pElem, pos );
+ return ;
+ }
+
+ q = InsBlock( cur+1 );
+ }
+
+ // entry does not fit anymore - clear space
+ BigPtrEntry* pLast = p->mvData[ MAXENTRY-1 ];
+ pLast->m_nOffset = 0;
+ pLast->m_pBlock = q;
+
+ q->mvData[ 0 ] = pLast;
+ q->nElem++;
+ q->nEnd++;
+
+ p->nEnd--;
+ p->nElem--;
+ }
+ // now we have free space - insert
+ pos -= p->nStart;
+ assert(pos < MAXENTRY);
+ if( pos != p->nElem )
+ {
+ int nCount = p->nElem - sal_uInt16(pos);
+ auto pFrom = p->mvData.begin() + p->nElem;
+ auto pTo = pFrom + 1;
+ while( nCount-- )
+ {
+ *--pTo = *--pFrom;
+ ++( *pTo )->m_nOffset;
+ }
+ }
+ // insert element and update indices
+ pElem->m_nOffset = sal_uInt16(pos);
+ pElem->m_pBlock = p;
+ p->mvData[ pos ] = pElem;
+ p->nEnd++;
+ p->nElem++;
+ m_nSize++;
+ if( cur != ( m_nBlock - 1 ) ) UpdIndex( cur );
+ m_nCur = cur;
+
+ CHECKIDX( m_ppInf.get(), m_nBlock, m_nSize, m_nCur );
+}
+
+void BigPtrArray::Remove( sal_Int32 pos, sal_Int32 n )
+{
+ CHECKIDX( m_ppInf.get(), m_nBlock, m_nSize, m_nCur );
+
+ sal_uInt16 nBlkdel = 0; // deleted blocks
+ sal_uInt16 cur = Index2Block( pos ); // current block number
+ sal_uInt16 nBlk1 = cur; // 1st treated block
+ sal_uInt16 nBlk1del = USHRT_MAX; // 1st deleted block
+ BlockInfo* p = m_ppInf[ cur ];
+ pos -= p->nStart;
+
+ sal_Int32 nElem = n;
+ while( nElem )
+ {
+ sal_uInt16 nel = p->nElem - sal_uInt16(pos);
+ if( sal_Int32(nel) > nElem )
+ nel = sal_uInt16(nElem);
+ // move elements if needed
+ if( ( pos + nel ) < sal_Int32(p->nElem) )
+ {
+ auto pTo = p->mvData.begin() + pos;
+ auto pFrom = pTo + nel;
+ int nCount = p->nElem - nel - sal_uInt16(pos);
+ while( nCount-- )
+ {
+ *pTo = *pFrom++;
+ (*pTo)->m_nOffset = (*pTo)->m_nOffset - nel;
+ ++pTo;
+ }
+ }
+ p->nEnd -= nel;
+ p->nElem = p->nElem - nel;
+ // possibly delete block completely
+ if( !p->nElem )
+ {
+ nBlkdel++;
+ if( USHRT_MAX == nBlk1del )
+ nBlk1del = cur;
+ }
+ nElem -= nel;
+ if( !nElem )
+ break;
+ p = m_ppInf[ ++cur ];
+ pos = 0;
+ }
+
+ // update table if blocks were removed
+ if( nBlkdel )
+ {
+ for( sal_uInt16 i = nBlk1del; i < ( nBlk1del + nBlkdel ); i++ )
+ delete m_ppInf[ i ];
+
+ if( ( nBlk1del + nBlkdel ) < m_nBlock )
+ {
+ memmove( m_ppInf.get() + nBlk1del, m_ppInf.get() + nBlk1del + nBlkdel,
+ ( m_nBlock - nBlkdel - nBlk1del ) * sizeof( BlockInfo* ) );
+
+ // UpdateIdx updates the successor thus start before first elem
+ if( !nBlk1 )
+ {
+ p = m_ppInf[ 0 ];
+ p->nStart = 0;
+ p->nEnd = p->nElem-1;
+ }
+ else
+ {
+ --nBlk1;
+ }
+ }
+ BlockDel( nBlkdel ); // blocks were deleted
+ }
+
+ m_nSize -= n;
+ if( nBlk1 != ( m_nBlock - 1 ) && m_nSize )
+ UpdIndex( nBlk1 );
+ m_nCur = nBlk1;
+
+ // call Compress() if there is more than 50% space in the array
+ if( m_nBlock > ( m_nSize / ( MAXENTRY / 2 ) ) )
+ Compress();
+
+ CHECKIDX( m_ppInf.get(), m_nBlock, m_nSize, m_nCur );
+}
+
+void BigPtrArray::Replace( sal_Int32 idx, BigPtrEntry* pElem)
+{
+ assert(idx < m_nSize); // Index out of bounds
+ m_nCur = Index2Block( idx );
+ BlockInfo* p = m_ppInf[ m_nCur ];
+ pElem->m_nOffset = sal_uInt16(idx - p->nStart);
+ pElem->m_pBlock = p;
+ p->mvData[ idx - p->nStart ] = pElem;
+}
+
+/** Compress the array */
+sal_uInt16 BigPtrArray::Compress()
+{
+ CHECKIDX( m_ppInf.get(), m_nBlock, m_nSize, m_nCur );
+
+ // Iterate over InfoBlock array from beginning to end. If there is a deleted
+ // block in between so move all following ones accordingly. The pointer <pp>
+ // represents the "old" and <qq> the "new" array.
+ BlockInfo** pp = m_ppInf.get(), **qq = pp;
+ BlockInfo* p;
+ BlockInfo* pLast(nullptr); // last empty block
+ sal_uInt16 nLast = 0; // missing elements
+ sal_uInt16 nBlkdel = 0; // number of deleted blocks
+ sal_uInt16 nFirstChgPos = USHRT_MAX; // at which position was the 1st change?
+
+ // convert fill percentage into number of remaining elements
+ short const nMax = MAXENTRY - tools::Long(MAXENTRY) * COMPRESSLVL / 100;
+
+ for( sal_uInt16 cur = 0; cur < m_nBlock; ++cur )
+ {
+ p = *pp++;
+ sal_uInt16 n = p->nElem;
+ // Check if a not completely full block will be ignored. This happens if
+ // the current block would have to be split but the filling of the
+ // inspected block is already over its threshold value. In this case we
+ // do not fill more (it's expensive because of a double memmove() call)
+ if( nLast && ( n > nLast ) && ( nLast < nMax ) )
+ nLast = 0;
+ if( nLast )
+ {
+ if( USHRT_MAX == nFirstChgPos )
+ nFirstChgPos = cur;
+
+ // Not full yet? Then fill up.
+ if( n > nLast )
+ n = nLast;
+
+ // move elements from current to last block
+ auto pElem = pLast->mvData.begin() + pLast->nElem;
+ auto pFrom = p->mvData.begin();
+ for( sal_uInt16 nCount = n, nOff = pLast->nElem;
+ nCount; --nCount, ++pElem )
+ {
+ *pElem = *pFrom++;
+ (*pElem)->m_pBlock = pLast;
+ (*pElem)->m_nOffset = nOff++;
+ }
+
+ // adjustment
+ pLast->nElem = pLast->nElem + n;
+ nLast = nLast - n;
+ p->nElem = p->nElem - n;
+
+ // Is the current block now empty as a result?
+ if( !p->nElem )
+ {
+ // then remove
+ delete p;
+ p = nullptr;
+ ++nBlkdel;
+ }
+ else
+ {
+ pElem = p->mvData.begin();
+ pFrom = pElem + n;
+ int nCount = p->nElem;
+ while( nCount-- )
+ {
+ *pElem = *pFrom++;
+ (*pElem)->m_nOffset = (*pElem)->m_nOffset - n;
+ ++pElem;
+ }
+ }
+ }
+
+ if( p ) // BlockInfo was not deleted
+ {
+ *qq++ = p; // adjust to correct position
+
+ // keep the potentially existing last half-full block
+ if( !nLast && p->nElem < MAXENTRY )
+ {
+ pLast = p;
+ nLast = MAXENTRY - p->nElem;
+ }
+ }
+ }
+
+ // if blocks were deleted shrink BlockInfo array if needed
+ if( nBlkdel )
+ BlockDel( nBlkdel );
+
+ // and re-index
+ p = m_ppInf[ 0 ];
+ p->nEnd = p->nElem - 1;
+ UpdIndex( 0 );
+
+ if( m_nCur >= nFirstChgPos )
+ m_nCur = 0;
+
+ CHECKIDX( m_ppInf.get(), m_nBlock, m_nSize, m_nCur );
+
+ return nFirstChgPos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/breakit.cxx b/sw/source/core/bastyp/breakit.cxx
new file mode 100644
index 000000000..af77672f7
--- /dev/null
+++ b/sw/source/core/bastyp/breakit.cxx
@@ -0,0 +1,183 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <breakit.hxx>
+#include <swtypes.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <svl/languageoptions.hxx>
+#include <unicode/uchar.h>
+#include <unotools/localedatawrapper.hxx>
+#include <algorithm>
+
+using namespace com::sun::star;
+
+SwBreakIt* g_pBreakIt = nullptr;
+
+void SwBreakIt::Create_( const uno::Reference<uno::XComponentContext> & rxContext )
+{
+ delete g_pBreakIt;
+ g_pBreakIt = new SwBreakIt( rxContext );
+}
+
+void SwBreakIt::Delete_()
+{
+ delete g_pBreakIt;
+ g_pBreakIt = nullptr;
+}
+
+SwBreakIt * SwBreakIt::Get()
+{
+ return g_pBreakIt;
+}
+
+SwBreakIt::SwBreakIt( const uno::Reference<uno::XComponentContext> & rxContext )
+ : m_xContext(rxContext)
+ , m_xBreak(i18n::BreakIterator::create(m_xContext))
+ , m_aForbiddenLang(LANGUAGE_DONTKNOW)
+{
+}
+
+void SwBreakIt::GetLocale_( const LanguageType aLang )
+{
+ if (m_xLanguageTag)
+ m_xLanguageTag->reset(aLang);
+ else
+ m_xLanguageTag.reset(new LanguageTag(aLang));
+}
+
+void SwBreakIt::GetLocale_( const LanguageTag& rLanguageTag )
+{
+ if (m_xLanguageTag)
+ *m_xLanguageTag = rLanguageTag;
+ else
+ m_xLanguageTag.reset(new LanguageTag(rLanguageTag));
+}
+
+void SwBreakIt::GetForbidden_( const LanguageType aLang )
+{
+ LocaleDataWrapper aWrap(m_xContext, GetLanguageTag(aLang));
+
+ m_aForbiddenLang = aLang;
+ m_xForbidden.reset(new i18n::ForbiddenCharacters(aWrap.getForbiddenCharacters()));
+}
+
+sal_uInt16 SwBreakIt::GetRealScriptOfText( const OUString& rText, sal_Int32 nPos ) const
+{
+ sal_uInt16 nScript = i18n::ScriptType::WEAK;
+ if (!rText.isEmpty())
+ {
+ if( nPos && nPos == rText.getLength() )
+ --nPos;
+ else if( nPos < 0)
+ nPos = 0;
+
+ nScript = m_xBreak->getScriptType(rText, nPos);
+ sal_Int32 nChgPos = 0;
+ if (i18n::ScriptType::WEAK == nScript && nPos >= 0 && nPos + 1 < rText.getLength())
+ {
+ // A weak character followed by a mark may be meant to combine with
+ // the mark, so prefer the following character's script
+ switch (u_charType(rText[nPos + 1]))
+ {
+ case U_NON_SPACING_MARK:
+ case U_ENCLOSING_MARK:
+ case U_COMBINING_SPACING_MARK:
+ nScript = m_xBreak->getScriptType(rText, nPos+1);
+ break;
+ }
+ }
+ if( i18n::ScriptType::WEAK == nScript && nPos )
+ {
+ nChgPos = m_xBreak->beginOfScript(rText, nPos, nScript);
+ if( 0 < nChgPos )
+ nScript = m_xBreak->getScriptType(rText, nChgPos-1);
+ }
+
+ if( i18n::ScriptType::WEAK == nScript )
+ {
+ nChgPos = m_xBreak->endOfScript(rText, nPos, nScript);
+ if( rText.getLength() > nChgPos && 0 <= nChgPos )
+ nScript = m_xBreak->getScriptType(rText, nChgPos);
+ }
+ }
+ if( i18n::ScriptType::WEAK == nScript )
+ nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+ return nScript;
+}
+
+SvtScriptType SwBreakIt::GetAllScriptsOfText( const OUString& rText ) const
+{
+ const SvtScriptType coAllScripts = SvtScriptType::LATIN |
+ SvtScriptType::ASIAN |
+ SvtScriptType::COMPLEX;
+ SvtScriptType nRet = SvtScriptType::NONE;
+ sal_uInt16 nScript = 0;
+ if (!rText.isEmpty())
+ {
+ for( sal_Int32 n = 0, nEnd = rText.getLength(); n < nEnd;
+ n = m_xBreak->endOfScript(rText, n, nScript) )
+ {
+ nScript = m_xBreak->getScriptType(rText, n);
+ switch( nScript )
+ {
+ case i18n::ScriptType::LATIN: nRet |= SvtScriptType::LATIN; break;
+ case i18n::ScriptType::ASIAN: nRet |= SvtScriptType::ASIAN; break;
+ case i18n::ScriptType::COMPLEX: nRet |= SvtScriptType::COMPLEX; break;
+ case i18n::ScriptType::WEAK:
+ if( nRet == SvtScriptType::NONE )
+ nRet |= coAllScripts;
+ break;
+ }
+ if( coAllScripts == nRet )
+ break;
+ }
+ }
+ return nRet;
+}
+
+sal_Int32 SwBreakIt::getGraphemeCount(const OUString& rText,
+ sal_Int32 nStart, sal_Int32 nEnd) const
+{
+ sal_Int32 nGraphemeCount = 0;
+
+ sal_Int32 nCurPos = std::max(static_cast<sal_Int32>(0), nStart);
+ while (nCurPos < nEnd)
+ {
+ // fdo#49208 cheat and assume that nothing can combine with a space
+ // to form a single grapheme
+ if (rText[nCurPos] == ' ')
+ {
+ ++nCurPos;
+ }
+ else
+ {
+ sal_Int32 nCount2 = 1;
+ nCurPos = m_xBreak->nextCharacters(rText, nCurPos, lang::Locale(),
+ i18n::CharacterIteratorMode::SKIPCELL, nCount2, nCount2);
+ }
+ ++nGraphemeCount;
+ }
+
+ return nGraphemeCount;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/calc.cxx b/sw/source/core/bastyp/calc.cxx
new file mode 100644
index 000000000..685e7adac
--- /dev/null
+++ b/sw/source/core/bastyp/calc.cxx
@@ -0,0 +1,1541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <calc.hxx>
+#include <cfloat>
+#include <climits>
+#include <memory>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <cstdlib>
+#include <dbmgr.hxx>
+#include <docfld.hxx>
+#include <docstat.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <editeng/langitem.hxx>
+#include <expfld.hxx>
+#include <hintids.hxx>
+#include <o3tl/temporary.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <shellres.hxx>
+#include <svl/numformat.hxx>
+#include <svl/languageoptions.hxx>
+#include <swmodule.hxx>
+#include <swtypes.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/useroptions.hxx>
+#include <usrfld.hxx>
+#include <viewsh.hxx>
+#include <com/sun/star/i18n/KParseTokens.hpp>
+#include <com/sun/star/i18n/KParseType.hpp>
+
+using namespace ::com::sun::star;
+
+const char sCalc_Add[] = "add";
+const char sCalc_Sub[] = "sub";
+const char sCalc_Mul[] = "mul";
+const char sCalc_Div[] = "div";
+const char sCalc_Phd[] = "phd";
+const char sCalc_Sqrt[] = "sqrt";
+const char sCalc_Pow[] = "pow";
+const char sCalc_Or[] = "or";
+const char sCalc_Xor[] = "xor";
+const char sCalc_And[] = "and";
+const char sCalc_Not[] = "not";
+const char sCalc_Eq[] = "eq";
+const char sCalc_Neq[] = "neq";
+const char sCalc_Leq[] = "leq";
+const char sCalc_Geq[] = "geq";
+const char sCalc_L[] = "l";
+const char sCalc_G[] = "g";
+const char sCalc_Sum[] = "sum";
+const char sCalc_Mean[] = "mean";
+const char sCalc_Min[] = "min";
+const char sCalc_Max[] = "max";
+const char sCalc_Sin[] = "sin";
+const char sCalc_Cos[] = "cos";
+const char sCalc_Tan[] = "tan";
+const char sCalc_Asin[] = "asin";
+const char sCalc_Acos[] = "acos";
+const char sCalc_Atan[] = "atan";
+const char sCalc_Round[]= "round";
+const char sCalc_Date[] = "date";
+const char sCalc_Product[] = "product";
+const char sCalc_Average[] = "average";
+const char sCalc_Count[]= "count";
+const char sCalc_Sign[] = "sign";
+const char sCalc_Abs[] = "abs";
+const char sCalc_Int[] = "int";
+
+// ATTENTION: sorted list of all operators
+struct CalcOp
+{
+ union{
+ const char* pName;
+ const OUString* pUName;
+ };
+ SwCalcOper eOp;
+};
+
+CalcOp const aOpTable[] = {
+/* ABS */ {{sCalc_Abs}, CALC_ABS}, // Abs (since LibreOffice 7.1)
+/* ACOS */ {{sCalc_Acos}, CALC_ACOS}, // Arc cosine
+/* ADD */ {{sCalc_Add}, CALC_PLUS}, // Addition
+/* AND */ {{sCalc_And}, CALC_AND}, // log. AND
+/* ASIN */ {{sCalc_Asin}, CALC_ASIN}, // Arc sine
+/* ATAN */ {{sCalc_Atan}, CALC_ATAN}, // Arc tangent
+/* AVERAGE */ {{sCalc_Average}, CALC_AVERAGE}, // Average (since LibreOffice 7.1)
+/* COS */ {{sCalc_Cos}, CALC_COS}, // Cosine
+/* COUNT */ {{sCalc_Count}, CALC_COUNT}, // Count (since LibreOffice 7.1)
+/* DATE */ {{sCalc_Date}, CALC_DATE}, // Date
+/* DIV */ {{sCalc_Div}, CALC_DIV}, // Division
+/* EQ */ {{sCalc_Eq}, CALC_EQ}, // Equality
+/* G */ {{sCalc_G}, CALC_GRE}, // Greater than
+/* GEQ */ {{sCalc_Geq}, CALC_GEQ}, // Greater or equal
+/* INT */ {{sCalc_Int}, CALC_INT}, // Int (since LibreOffice 7.4)
+/* L */ {{sCalc_L}, CALC_LES}, // Less than
+/* LEQ */ {{sCalc_Leq}, CALC_LEQ}, // Less or equal
+/* MAX */ {{sCalc_Max}, CALC_MAX}, // Maximum value
+/* MEAN */ {{sCalc_Mean}, CALC_MEAN}, // Mean
+/* MIN */ {{sCalc_Min}, CALC_MIN}, // Minimum value
+/* MUL */ {{sCalc_Mul}, CALC_MUL}, // Multiplication
+/* NEQ */ {{sCalc_Neq}, CALC_NEQ}, // Not equal
+/* NOT */ {{sCalc_Not}, CALC_NOT}, // log. NOT
+/* OR */ {{sCalc_Or}, CALC_OR}, // log. OR
+/* PHD */ {{sCalc_Phd}, CALC_PHD}, // Percentage
+/* POW */ {{sCalc_Pow}, CALC_POW}, // Exponentiation
+/* PRODUCT */ {{sCalc_Product}, CALC_PRODUCT}, // Product (since LibreOffice 7.1)
+/* ROUND */ {{sCalc_Round}, CALC_ROUND}, // Rounding
+/* SIGN */ {{sCalc_Sign}, CALC_SIGN}, // Sign (since LibreOffice 7.1)
+/* SIN */ {{sCalc_Sin}, CALC_SIN}, // Sine
+/* SQRT */ {{sCalc_Sqrt}, CALC_SQRT}, // Square root
+/* SUB */ {{sCalc_Sub}, CALC_MINUS}, // Subtraction
+/* SUM */ {{sCalc_Sum}, CALC_SUM}, // Sum
+/* TAN */ {{sCalc_Tan}, CALC_TAN}, // Tangent
+/* XOR */ {{sCalc_Xor}, CALC_XOR} // log. XOR
+};
+
+double const nRoundVal[] = {
+ 5.0e+0, 0.5e+0, 0.5e-1, 0.5e-2, 0.5e-3, 0.5e-4, 0.5e-5, 0.5e-6,
+ 0.5e-7, 0.5e-8, 0.5e-9, 0.5e-10,0.5e-11,0.5e-12,0.5e-13,0.5e-14,
+ 0.5e-15,0.5e-16
+};
+
+// First character may be any alphabetic or underscore.
+const sal_Int32 coStartFlags =
+ i18n::KParseTokens::ANY_LETTER_OR_NUMBER |
+ i18n::KParseTokens::ASC_UNDERSCORE |
+ i18n::KParseTokens::IGNORE_LEADING_WS;
+
+// Continuing characters may be any alphanumeric, underscore, or dot.
+const sal_Int32 coContFlags =
+ ( coStartFlags | i18n::KParseTokens::ASC_DOT )
+ & ~i18n::KParseTokens::IGNORE_LEADING_WS;
+
+extern "C" {
+static int OperatorCompare( const void *pFirst, const void *pSecond)
+{
+ int nRet = 0;
+ if( CALC_NAME == static_cast<const CalcOp*>(pFirst)->eOp )
+ {
+ if( CALC_NAME == static_cast<const CalcOp*>(pSecond)->eOp )
+ nRet = static_cast<const CalcOp*>(pFirst)->pUName->compareTo(
+ *static_cast<const CalcOp*>(pSecond)->pUName );
+ else
+ nRet = static_cast<const CalcOp*>(pFirst)->pUName->compareToAscii(
+ static_cast<const CalcOp*>(pSecond)->pName );
+ }
+ else
+ {
+ if( CALC_NAME == static_cast<const CalcOp*>(pSecond)->eOp )
+ nRet = -1 * static_cast<const CalcOp*>(pSecond)->pUName->compareToAscii(
+ static_cast<const CalcOp*>(pFirst)->pName );
+ else
+ nRet = strcmp( static_cast<const CalcOp*>(pFirst)->pName,
+ static_cast<const CalcOp*>(pSecond)->pName );
+ }
+ return nRet;
+}
+}// extern "C"
+
+CalcOp* FindOperator( const OUString& rSrch )
+{
+ CalcOp aSrch;
+ aSrch.pUName = &rSrch;
+ aSrch.eOp = CALC_NAME;
+
+ return static_cast<CalcOp*>(bsearch( static_cast<void*>(&aSrch),
+ static_cast<void const *>(aOpTable),
+ SAL_N_ELEMENTS( aOpTable ),
+ sizeof( CalcOp ),
+ OperatorCompare ));
+}
+
+static LanguageType GetDocAppScriptLang( SwDoc const & rDoc )
+{
+ TypedWhichId<SvxLanguageItem> nWhich =
+ GetWhichOfScript( RES_CHRATR_LANGUAGE,
+ SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() ));
+ return rDoc.GetDefault(nWhich).GetLanguage();
+}
+
+static double lcl_ConvertToDateValue( SwDoc& rDoc, sal_Int32 nDate )
+{
+ double nRet = 0;
+ SvNumberFormatter* pFormatter = rDoc.GetNumberFormatter();
+ if( pFormatter )
+ {
+ const Date& rNull = pFormatter->GetNullDate();
+ Date aDate( nDate >> 24, (nDate& 0x00FF0000) >> 16, nDate& 0x0000FFFF );
+ nRet = aDate - rNull;
+ }
+ return nRet;
+}
+
+SwCalc::SwCalc( SwDoc& rD )
+ : m_aVarTable(TBLSZ)
+ , m_aErrExpr( OUString(), SwSbxValue(), nullptr )
+ , m_nCommandPos(0)
+ , m_rDoc( rD )
+ , m_pCharClass( &GetAppCharClass() )
+ , m_nListPor( 0 )
+ , m_bHasNumber( false )
+ , m_eCurrOper( CALC_NAME )
+ , m_eCurrListOper( CALC_NAME )
+ , m_eError( SwCalcError::NONE )
+{
+ m_aErrExpr.aStr = "~C_ERR~";
+ LanguageType eLang = GetDocAppScriptLang( m_rDoc );
+ LanguageTag aLanguageTag( eLang );
+
+ if( eLang != m_pCharClass->getLanguageTag().getLanguageType() )
+ {
+ m_pCharClass = new CharClass( ::comphelper::getProcessComponentContext(), aLanguageTag );
+ }
+ m_xLocaleDataWrapper.reset(new LocaleDataWrapper( std::move(aLanguageTag) ));
+
+ m_sCurrSym = comphelper::string::strip(m_xLocaleDataWrapper->getCurrSymbol(), ' ');
+ m_sCurrSym = m_pCharClass->lowercase( m_sCurrSym );
+
+ static char const
+ sNType0[] = "false",
+ sNType1[] = "true",
+ sNType2[] = "pi",
+ sNType3[] = "e",
+ sNType4[] = "tables",
+ sNType5[] = "graf",
+ sNType6[] = "ole",
+ sNType7[] = "page",
+ sNType8[] = "para",
+ sNType9[] = "word",
+ sNType10[]= "char",
+
+ sNType11[] = "user_firstname" ,
+ sNType12[] = "user_lastname" ,
+ sNType13[] = "user_initials" ,
+ sNType14[] = "user_company" ,
+ sNType15[] = "user_street" ,
+ sNType16[] = "user_country" ,
+ sNType17[] = "user_zipcode" ,
+ sNType18[] = "user_city" ,
+ sNType19[] = "user_title" ,
+ sNType20[] = "user_position" ,
+ sNType21[] = "user_tel_work" ,
+ sNType22[] = "user_tel_home" ,
+ sNType23[] = "user_fax" ,
+ sNType24[] = "user_email" ,
+ sNType25[] = "user_state" ,
+ sNType26[] = "graph"
+ ;
+ static const char* const sNTypeTab[ 27 ] =
+ {
+ sNType0, sNType1, sNType2, sNType3, sNType4, sNType5,
+ sNType6, sNType7, sNType8, sNType9, sNType10, sNType11,
+ sNType12, sNType13, sNType14, sNType15, sNType16, sNType17,
+ sNType18, sNType19, sNType20, sNType21, sNType22, sNType23,
+ sNType24,
+
+ // those have two HashIds
+ sNType25, sNType26
+ };
+ static sal_uInt16 const aHashValue[ 27 ] =
+ {
+ 34, 38, 43, 7, 18, 32, 22, 29, 30, 33, 3,
+ 28, 24, 40, 9, 11, 26, 45, 4, 23, 36, 44, 19, 5, 1,
+ // those have two HashIds
+ 11, 38
+ };
+ static UserOptToken const aAdrToken[ 12 ] =
+ {
+ UserOptToken::Company, UserOptToken::Street, UserOptToken::Country, UserOptToken::Zip,
+ UserOptToken::City, UserOptToken::Title, UserOptToken::Position, UserOptToken::TelephoneWork,
+ UserOptToken::TelephoneHome, UserOptToken::Fax, UserOptToken::Email, UserOptToken::State
+ };
+
+ static sal_uInt16 SwDocStat::* const aDocStat1[ 3 ] =
+ {
+ &SwDocStat::nTable, &SwDocStat::nGrf, &SwDocStat::nOLE
+ };
+ static sal_uLong SwDocStat::* const aDocStat2[ 4 ] =
+ {
+ &SwDocStat::nPage, &SwDocStat::nPara,
+ &SwDocStat::nWord, &SwDocStat::nChar
+ };
+
+#if TBLSZ != 47
+#error Did you adjust all hash values?
+#endif
+
+ const SwDocStat& rDocStat = m_rDoc.getIDocumentStatistics().GetDocStat();
+
+ SwSbxValue nVal;
+ OUString sTmpStr;
+ sal_uInt16 n;
+
+ for( n = 0; n < 25; ++n )
+ {
+ sTmpStr = OUString::createFromAscii(sNTypeTab[n]);
+ m_aVarTable[ aHashValue[ n ] ].reset( new SwCalcExp( sTmpStr, nVal, nullptr ) );
+ }
+
+ m_aVarTable[ aHashValue[ 0 ] ]->nValue.PutBool( false );
+ m_aVarTable[ aHashValue[ 1 ] ]->nValue.PutBool( true );
+ m_aVarTable[ aHashValue[ 2 ] ]->nValue.PutDouble( M_PI );
+ m_aVarTable[ aHashValue[ 3 ] ]->nValue.PutDouble( M_E );
+
+ for( n = 0; n < 3; ++n )
+ m_aVarTable[ aHashValue[ n + 4 ] ]->nValue.PutLong( rDocStat.*aDocStat1[ n ] );
+ for( n = 0; n < 4; ++n )
+ m_aVarTable[ aHashValue[ n + 7 ] ]->nValue.PutLong( rDocStat.*aDocStat2[ n ] );
+
+ SvtUserOptions& rUserOptions = SW_MOD()->GetUserOptions();
+
+ m_aVarTable[ aHashValue[ 11 ] ]->nValue.PutString( rUserOptions.GetFirstName() );
+ m_aVarTable[ aHashValue[ 12 ] ]->nValue.PutString( rUserOptions.GetLastName() );
+ m_aVarTable[ aHashValue[ 13 ] ]->nValue.PutString( rUserOptions.GetID() );
+
+ for( n = 0; n < 11; ++n )
+ m_aVarTable[ aHashValue[ n + 14 ] ]->nValue.PutString(
+ rUserOptions.GetToken( aAdrToken[ n ] ));
+
+ nVal.PutString( rUserOptions.GetToken( aAdrToken[ 11 ] ));
+ sTmpStr = OUString::createFromAscii(sNTypeTab[25]);
+ m_aVarTable[ aHashValue[ 25 ] ]->pNext.reset( new SwCalcExp( sTmpStr, nVal, nullptr ) );
+
+} // SwCalc::SwCalc
+
+SwCalc::~SwCalc() COVERITY_NOEXCEPT_FALSE
+{
+ if( m_pCharClass != &GetAppCharClass() )
+ delete m_pCharClass;
+}
+
+SwSbxValue SwCalc::Calculate( const OUString& rStr )
+{
+ m_eError = SwCalcError::NONE;
+ SwSbxValue nResult;
+
+ if( rStr.isEmpty() )
+ return nResult;
+
+ m_nListPor = 0;
+ m_eCurrListOper = CALC_PLUS; // default: sum
+
+ m_sCommand = rStr;
+ m_nCommandPos = 0;
+
+ for (;;)
+ {
+ m_eCurrOper = GetToken();
+ if (m_eCurrOper == CALC_ENDCALC || m_eError != SwCalcError::NONE )
+ break;
+ nResult = Expr();
+ }
+
+ if( m_eError != SwCalcError::NONE)
+ nResult.PutDouble( DBL_MAX );
+
+ return nResult;
+}
+
+OUString SwCalc::GetStrResult( const SwSbxValue& rVal )
+{
+ if( !rVal.IsDouble() )
+ {
+ return rVal.GetOUString();
+ }
+ return GetStrResult( rVal.GetDouble() );
+}
+
+OUString SwCalc::GetStrResult( double nValue )
+{
+ if( nValue >= DBL_MAX )
+ switch( m_eError )
+ {
+ case SwCalcError::Syntax : return SwViewShell::GetShellRes()->aCalc_Syntax;
+ case SwCalcError::DivByZero : return SwViewShell::GetShellRes()->aCalc_ZeroDiv;
+ case SwCalcError::FaultyBrackets : return SwViewShell::GetShellRes()->aCalc_Brack;
+ case SwCalcError::OverflowInPower : return SwViewShell::GetShellRes()->aCalc_Pow;
+ case SwCalcError::Overflow : return SwViewShell::GetShellRes()->aCalc_Overflow;
+ default : return SwViewShell::GetShellRes()->aCalc_Default;
+ }
+
+ const sal_Int32 nDecPlaces = 15;
+ OUString aRetStr( ::rtl::math::doubleToUString(
+ nValue,
+ rtl_math_StringFormat_Automatic,
+ nDecPlaces,
+ m_xLocaleDataWrapper->getNumDecimalSep()[0],
+ true ));
+ return aRetStr;
+}
+
+SwCalcExp* SwCalc::VarInsert( const OUString &rStr )
+{
+ OUString aStr = m_pCharClass->lowercase( rStr );
+ return VarLook( aStr, true );
+}
+
+SwCalcExp* SwCalc::VarLook( const OUString& rStr, bool bIns )
+{
+ m_aErrExpr.nValue.SetVoidValue(false);
+
+ sal_uInt16 ii = 0;
+ OUString aStr = m_pCharClass->lowercase( rStr );
+
+ SwCalcExp* pFnd = m_aVarTable.Find(aStr, &ii);
+
+ if( !pFnd )
+ {
+ // then check doc
+ SwHashTable<SwCalcFieldType> const & rDocTable = m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().GetFieldTypeTable();
+ for( SwHash* pEntry = rDocTable[ii].get(); pEntry; pEntry = pEntry->pNext.get() )
+ {
+ if( aStr == pEntry->aStr )
+ {
+ // then insert here
+ pFnd = new SwCalcExp( aStr, SwSbxValue(),
+ static_cast<SwCalcFieldType*>(pEntry)->pFieldType );
+ pFnd->pNext = std::move( m_aVarTable[ii] );
+ m_aVarTable[ii].reset(pFnd);
+ break;
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ if( pFnd->pFieldType && pFnd->pFieldType->Which() == SwFieldIds::User )
+ {
+ SwUserFieldType* pUField = const_cast<SwUserFieldType*>(static_cast<const SwUserFieldType*>(pFnd->pFieldType));
+ if( nsSwGetSetExpType::GSE_STRING & pUField->GetType() )
+ {
+ pFnd->nValue.PutString( pUField->GetContent() );
+ }
+ else if( !pUField->IsValid() )
+ {
+ // Save the current values...
+ sal_uInt16 nListPor = m_nListPor;
+ bool bHasNumber = m_bHasNumber;
+ SwSbxValue nLastLeft = m_nLastLeft;
+ SwSbxValue nNumberValue = m_nNumberValue;
+ sal_Int32 nCommandPos = m_nCommandPos;
+ SwCalcOper eCurrOper = m_eCurrOper;
+ SwCalcOper eCurrListOper = m_eCurrListOper;
+ OUString sCurrCommand = m_sCommand;
+
+ pFnd->nValue.PutDouble( pUField->GetValue( *this ) );
+
+ // ...and write them back.
+ m_nListPor = nListPor;
+ m_bHasNumber = bHasNumber;
+ m_nLastLeft = nLastLeft;
+ m_nNumberValue = nNumberValue;
+ m_nCommandPos = nCommandPos;
+ m_eCurrOper = eCurrOper;
+ m_eCurrListOper = eCurrListOper;
+ m_sCommand = sCurrCommand;
+ }
+ else
+ {
+ pFnd->nValue.PutDouble( pUField->GetValue() );
+ }
+ }
+ else if ( !pFnd->pFieldType && pFnd->nValue.IsDBvalue() )
+ {
+ if ( pFnd->nValue.IsString() )
+ m_aErrExpr.nValue.PutString( pFnd->nValue.GetOUString() );
+ else if ( pFnd->nValue.IsDouble() )
+ m_aErrExpr.nValue.PutDouble( pFnd->nValue.GetDouble() );
+ pFnd = &m_aErrExpr;
+ }
+ return pFnd;
+ }
+
+ // At this point the "real" case variable has to be used
+ OUString const sTmpName( ::ReplacePoint(rStr) );
+
+ if( !bIns )
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ SwDBManager *pMgr = m_rDoc.GetDBManager();
+ OUString sDBName(GetDBName( sTmpName ));
+ OUString sSourceName(sDBName.getToken(0, DB_DELIM));
+ OUString sTableName(sDBName.getToken(0, ';').getToken(1, DB_DELIM));
+ if( pMgr && !sSourceName.isEmpty() && !sTableName.isEmpty() &&
+ pMgr->OpenDataSource(sSourceName, sTableName))
+ {
+ OUString sColumnName( GetColumnName( sTmpName ));
+ OSL_ENSURE(!sColumnName.isEmpty(), "Missing DB column name");
+
+ OUString sDBNum( SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber) );
+ sDBNum = m_pCharClass->lowercase(sDBNum);
+
+ // Initialize again because this doesn't happen in docfld anymore for
+ // elements != SwFieldIds::Database. E.g. if there is an expression field before
+ // a DB_Field in a document.
+ const sal_uInt32 nTmpRec = pMgr->GetSelectedRecordId(sSourceName, sTableName);
+ VarChange(sDBNum, nTmpRec);
+
+ if( sDBNum.equalsIgnoreAsciiCase(sColumnName) )
+ {
+ m_aErrExpr.nValue.PutULong(nTmpRec);
+ return &m_aErrExpr;
+ }
+
+ OUString sResult;
+ double nNumber = DBL_MAX;
+
+ LanguageType nLang = m_xLocaleDataWrapper->getLanguageTag().getLanguageType();
+ if(pMgr->GetColumnCnt( sSourceName, sTableName, sColumnName,
+ nTmpRec, nLang, sResult, &nNumber ))
+ {
+ if (nNumber != DBL_MAX)
+ m_aErrExpr.nValue.PutDouble( nNumber );
+ else
+ m_aErrExpr.nValue.PutString( sResult );
+
+ return &m_aErrExpr;
+ }
+ }
+ else
+#endif
+ {
+ //data source was not available - set return to "NoValue"
+ m_aErrExpr.nValue.SetVoidValue(true);
+ }
+ // NEVER save!
+ return &m_aErrExpr;
+ }
+
+ SwCalcExp* pNewExp = new SwCalcExp( aStr, SwSbxValue(), nullptr );
+ pNewExp->pNext = std::move( m_aVarTable[ ii ] );
+ m_aVarTable[ ii ].reset( pNewExp );
+
+ OUString sColumnName( GetColumnName( sTmpName ));
+ OSL_ENSURE( !sColumnName.isEmpty(), "Missing DB column name" );
+ if( sColumnName.equalsIgnoreAsciiCase(
+ SwFieldType::GetTypeStr( SwFieldTypesEnum::DatabaseSetNumber ) ))
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ SwDBManager *pMgr = m_rDoc.GetDBManager();
+ OUString sDBName(GetDBName( sTmpName ));
+ OUString sSourceName(sDBName.getToken(0, DB_DELIM));
+ OUString sTableName(sDBName.getToken(0, ';').getToken(1, DB_DELIM));
+ if( pMgr && !sSourceName.isEmpty() && !sTableName.isEmpty() &&
+ pMgr->OpenDataSource(sSourceName, sTableName) &&
+ !pMgr->IsInMerge())
+ {
+ pNewExp->nValue.PutULong( pMgr->GetSelectedRecordId(sSourceName, sTableName));
+ }
+ else
+#endif
+ {
+ pNewExp->nValue.SetVoidValue(true);
+ }
+ }
+
+ return pNewExp;
+}
+
+void SwCalc::VarChange( const OUString& rStr, double nValue )
+{
+ SwSbxValue aVal( nValue );
+ VarChange( rStr, aVal );
+}
+
+void SwCalc::VarChange( const OUString& rStr, const SwSbxValue& rValue )
+{
+ OUString aStr = m_pCharClass->lowercase( rStr );
+
+ sal_uInt16 nPos = 0;
+ SwCalcExp* pFnd = m_aVarTable.Find( aStr, &nPos );
+
+ if( !pFnd )
+ {
+ pFnd = new SwCalcExp( aStr, rValue, nullptr );
+ pFnd->pNext = std::move( m_aVarTable[ nPos ] );
+ m_aVarTable[ nPos ].reset( pFnd );
+ }
+ else
+ {
+ pFnd->nValue = rValue;
+ }
+}
+
+bool SwCalc::Push( const SwUserFieldType* pUserFieldType )
+{
+ if( m_aRekurStack.end() != std::find(m_aRekurStack.begin(), m_aRekurStack.end(), pUserFieldType ) )
+ return false;
+
+ m_aRekurStack.push_back( pUserFieldType );
+ return true;
+}
+
+void SwCalc::Pop()
+{
+ OSL_ENSURE( m_aRekurStack.size(), "SwCalc: Pop on an invalid pointer" );
+
+ m_aRekurStack.pop_back();
+}
+
+const CharClass* SwCalc::GetCharClass() const
+{
+ return m_pCharClass;
+}
+
+void SwCalc::SetCharClass(const LanguageTag& rLanguageTag)
+{
+ m_pCharClass = new CharClass( ::comphelper::getProcessComponentContext(), rLanguageTag );
+}
+
+SwCalcOper SwCalc::GetToken()
+{
+ if( m_nCommandPos >= m_sCommand.getLength() )
+ {
+ m_eCurrOper = CALC_ENDCALC;
+ return m_eCurrOper;
+ }
+
+ using namespace ::com::sun::star::i18n;
+ {
+ // Parse any token.
+ ParseResult aRes = m_pCharClass->parseAnyToken( m_sCommand, m_nCommandPos,
+ coStartFlags, OUString(),
+ coContFlags, OUString());
+
+ bool bSetError = true;
+ sal_Int32 nRealStt = m_nCommandPos + aRes.LeadingWhiteSpace;
+ if( aRes.TokenType & (KParseType::ASC_NUMBER | KParseType::UNI_NUMBER) )
+ {
+ m_nNumberValue.PutDouble( aRes.Value );
+ m_eCurrOper = CALC_NUMBER;
+ bSetError = false;
+ }
+ else if( aRes.TokenType & KParseType::IDENTNAME )
+ {
+ OUString aName( m_sCommand.copy( nRealStt,
+ aRes.EndPos - nRealStt ) );
+ //#101436#: The variable may contain a database name. It must not be
+ // converted to lower case! Instead all further comparisons must be
+ // done case-insensitive
+ OUString sLowerCaseName = m_pCharClass->lowercase( aName );
+ // catch currency symbol
+ if( sLowerCaseName == m_sCurrSym )
+ {
+ m_nCommandPos = aRes.EndPos;
+ return GetToken(); // call again
+ }
+
+ // catch operators
+ CalcOp* pFnd = ::FindOperator( sLowerCaseName );
+ if( pFnd )
+ {
+ m_eCurrOper = pFnd->eOp;
+ switch( m_eCurrOper )
+ {
+ case CALC_SUM:
+ case CALC_MEAN:
+ case CALC_AVERAGE:
+ case CALC_COUNT:
+ m_eCurrListOper = CALC_PLUS;
+ break;
+ case CALC_MIN:
+ m_eCurrListOper = CALC_MIN_IN;
+ break;
+ case CALC_MAX:
+ m_eCurrListOper = CALC_MAX_IN;
+ break;
+ case CALC_DATE:
+ m_eCurrListOper = CALC_MONTH;
+ break;
+ case CALC_PRODUCT:
+ m_eCurrListOper = CALC_MUL;
+ break;
+ default:
+ break;
+ }
+ m_nCommandPos = aRes.EndPos;
+ return m_eCurrOper;
+ }
+ m_aVarName = aName;
+ m_eCurrOper = CALC_NAME;
+ bSetError = false;
+ }
+ else if ( aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING )
+ {
+ m_nNumberValue.PutString( aRes.DequotedNameOrString );
+ m_eCurrOper = CALC_NUMBER;
+ bSetError = false;
+ }
+ else if( aRes.TokenType & KParseType::ONE_SINGLE_CHAR )
+ {
+ std::u16string_view aName( m_sCommand.subView( nRealStt,
+ aRes.EndPos - nRealStt ));
+ if( 1 == aName.size() )
+ {
+ bSetError = false;
+ sal_Unicode ch = aName[0];
+ switch( ch )
+ {
+ case ';':
+ if( CALC_MONTH == m_eCurrListOper || CALC_DAY == m_eCurrListOper )
+ {
+ m_eCurrOper = m_eCurrListOper;
+ break;
+ }
+ [[fallthrough]];
+ case '\n':
+ m_eCurrOper = CALC_PRINT;
+ break;
+
+ case '%':
+ case '^':
+ case '*':
+ case '/':
+ case '+':
+ case '-':
+ case '(':
+ case ')':
+ m_eCurrOper = SwCalcOper(ch);
+ break;
+
+ case '=':
+ case '!':
+ {
+ SwCalcOper eTmp2;
+ if( '=' == ch )
+ {
+ m_eCurrOper = SwCalcOper('=');
+ eTmp2 = CALC_EQ;
+ }
+ else
+ {
+ m_eCurrOper = CALC_NOT;
+ eTmp2 = CALC_NEQ;
+ }
+
+ if( aRes.EndPos < m_sCommand.getLength() &&
+ '=' == m_sCommand[aRes.EndPos] )
+ {
+ m_eCurrOper = eTmp2;
+ ++aRes.EndPos;
+ }
+ }
+ break;
+
+ case cListDelim:
+ m_eCurrOper = m_eCurrListOper;
+ break;
+
+ case '[':
+ if( aRes.EndPos < m_sCommand.getLength() )
+ {
+ m_aVarName.setLength(0);
+ sal_Int32 nFndPos = aRes.EndPos,
+ nSttPos = nFndPos;
+
+ do {
+ nFndPos = m_sCommand.indexOf( ']', nFndPos );
+ if( -1 != nFndPos )
+ {
+ // ignore the ]
+ if ('\\' == m_sCommand[nFndPos-1])
+ {
+ m_aVarName.append(m_sCommand.subView(nSttPos,
+ nFndPos - nSttPos - 1) );
+ nSttPos = ++nFndPos;
+ }
+ else
+ break;
+ }
+ } while( nFndPos != -1 );
+
+ if( nFndPos != -1 )
+ {
+ if( nSttPos != nFndPos )
+ m_aVarName.append(m_sCommand.subView(nSttPos,
+ nFndPos - nSttPos) );
+ aRes.EndPos = nFndPos + 1;
+ m_eCurrOper = CALC_NAME;
+ }
+ else
+ bSetError = true;
+ }
+ else
+ {
+ bSetError = true;
+ }
+ break;
+
+ default:
+ bSetError = true;
+ break;
+ }
+ }
+ }
+ else if( aRes.TokenType & KParseType::BOOLEAN )
+ {
+ std::u16string_view aName( m_sCommand.subView( nRealStt,
+ aRes.EndPos - nRealStt ));
+ if( !aName.empty() )
+ {
+ sal_Unicode ch = aName[0];
+
+ bSetError = true;
+ if ('<' == ch || '>' == ch)
+ {
+ bSetError = false;
+
+ SwCalcOper eTmp2 = ('<' == ch) ? CALC_LEQ : CALC_GEQ;
+ m_eCurrOper = ('<' == ch) ? CALC_LES : CALC_GRE;
+
+ if( 2 == aName.size() && '=' == aName[1] )
+ m_eCurrOper = eTmp2;
+ else if( 1 != aName.size() )
+ bSetError = true;
+ }
+ }
+ }
+ else if( nRealStt == m_sCommand.getLength() )
+ {
+ m_eCurrOper = CALC_ENDCALC;
+ bSetError = false;
+ }
+
+ if( bSetError )
+ {
+ m_eError = SwCalcError::Syntax;
+ m_eCurrOper = CALC_PRINT;
+ }
+ m_nCommandPos = aRes.EndPos;
+ };
+
+ return m_eCurrOper;
+}
+
+SwSbxValue SwCalc::Term()
+{
+ SwSbxValue left( Prim() );
+ m_nLastLeft = left;
+ for(;;)
+ {
+ sal_uInt16 nSbxOper = USHRT_MAX;
+
+ switch( m_eCurrOper )
+ {
+ case CALC_AND:
+ {
+ GetToken();
+ bool bB = Prim().GetBool();
+ left.PutBool( left.GetBool() && bB );
+ }
+ break;
+ case CALC_OR:
+ {
+ GetToken();
+ bool bB = Prim().GetBool();
+ left.PutBool( left.GetBool() || bB );
+ }
+ break;
+ case CALC_XOR:
+ {
+ GetToken();
+ bool bR = Prim().GetBool();
+ bool bL = left.GetBool();
+ left.PutBool(bL != bR);
+ }
+ break;
+
+ case CALC_EQ: nSbxOper = SbxEQ; break;
+ case CALC_NEQ: nSbxOper = SbxNE; break;
+ case CALC_LEQ: nSbxOper = SbxLE; break;
+ case CALC_GEQ: nSbxOper = SbxGE; break;
+ case CALC_GRE: nSbxOper = SbxGT; break;
+ case CALC_LES: nSbxOper = SbxLT; break;
+
+ case CALC_MUL: nSbxOper = SbxMUL; break;
+ case CALC_DIV: nSbxOper = SbxDIV; break;
+
+ case CALC_MIN_IN:
+ {
+ GetToken();
+ SwSbxValue e = Prim();
+ left = left.GetDouble() < e.GetDouble() ? left : e;
+ }
+ break;
+ case CALC_MAX_IN:
+ {
+ GetToken();
+ SwSbxValue e = Prim();
+ left = left.GetDouble() > e.GetDouble() ? left : e;
+ }
+ break;
+ case CALC_MONTH:
+ {
+ GetToken();
+ SwSbxValue e = Prim();
+ sal_Int32 nYear = static_cast<sal_Int32>(floor( left.GetDouble() ));
+ nYear = nYear & 0x0000FFFF;
+ sal_Int32 nMonth = static_cast<sal_Int32>(floor( e.GetDouble() ));
+ nMonth = ( nMonth & 0x000000FF ) << 16;
+ left.PutLong( nMonth + nYear );
+ m_eCurrOper = CALC_DAY;
+ }
+ break;
+ case CALC_DAY:
+ {
+ GetToken();
+ SwSbxValue e = Prim();
+ sal_Int32 nYearMonth = static_cast<sal_Int32>(floor( left.GetDouble() ));
+ nYearMonth = nYearMonth & 0x00FFFFFF;
+ sal_Int32 nDay = static_cast<sal_Int32>(floor( e.GetDouble() ));
+ nDay = ( nDay & 0x000000FF ) << 24;
+ left = lcl_ConvertToDateValue( m_rDoc, nDay + nYearMonth );
+ }
+ break;
+ case CALC_ROUND:
+ {
+ GetToken();
+ SwSbxValue e = Prim();
+
+ double fVal = 0;
+ double fFac = 1;
+ sal_Int32 nDec = static_cast<sal_Int32>(floor( e.GetDouble() ));
+ if( nDec < -20 || nDec > 20 )
+ {
+ m_eError = SwCalcError::Overflow;
+ left.Clear();
+ return left;
+ }
+ fVal = left.GetDouble();
+ if( nDec >= 0)
+ {
+ for (sal_Int32 i = 0; i < nDec; ++i )
+ fFac *= 10.0;
+ }
+ else
+ {
+ for (sal_Int32 i = 0; i < -nDec; ++i )
+ fFac /= 10.0;
+ }
+
+ fVal *= fFac;
+ bool bSign;
+ if (fVal < 0.0)
+ {
+ fVal *= -1.0;
+ bSign = true;
+ }
+ else
+ {
+ bSign = false;
+ }
+
+ // rounding
+ double fNum = fVal; // find the exponent
+ int nExp = 0;
+ if( fNum > 0 )
+ {
+ while( fNum < 1.0 )
+ {
+ fNum *= 10.0;
+ --nExp;
+ }
+ while( fNum >= 10.0 )
+ {
+ fNum /= 10.0;
+ ++nExp;
+ }
+ }
+ nExp = 15 - nExp;
+ if( nExp > 15 )
+ nExp = 15;
+ else if( nExp <= 1 )
+ nExp = 0;
+ fVal = floor( fVal+ 0.5 + nRoundVal[ nExp ] );
+
+ if (bSign)
+ fVal *= -1.0;
+
+ fVal /= fFac;
+
+ left.PutDouble( fVal );
+ }
+ break;
+
+//#77448# (=2*3^2 != 18)
+
+ default:
+ return left;
+ }
+
+ if( USHRT_MAX != nSbxOper )
+ {
+ // #i47706: cast to SbxOperator AFTER comparing to USHRT_MAX
+ SbxOperator eSbxOper = static_cast<SbxOperator>(nSbxOper);
+
+ GetToken();
+ if( SbxEQ <= eSbxOper && eSbxOper <= SbxGE )
+ {
+ left.PutBool( left.Compare( eSbxOper, Prim() ));
+ }
+ else
+ {
+ SwSbxValue aRight( Prim() );
+ aRight.MakeDouble();
+ left.MakeDouble();
+
+ if( SbxDIV == eSbxOper && !aRight.GetDouble() )
+ m_eError = SwCalcError::DivByZero;
+ else
+ left.Compute( eSbxOper, aRight );
+ }
+ }
+ }
+}
+
+SwSbxValue SwCalc::StdFunc(pfCalc pFnc, bool bChkTrig)
+{
+ SwSbxValue nErg;
+ GetToken();
+ double nVal = Prim().GetDouble();
+ if( !bChkTrig || ( nVal > -1 && nVal < 1 ) )
+ nErg.PutDouble( (*pFnc)( nVal ) );
+ else
+ m_eError = SwCalcError::Overflow;
+ return nErg;
+}
+
+SwSbxValue SwCalc::PrimFunc(bool &rChkPow)
+{
+ rChkPow = false;
+
+ switch (m_eCurrOper)
+ {
+ case CALC_SIN:
+ SAL_INFO("sw.calc", "sin");
+ return StdFunc(&sin, false);
+ case CALC_COS:
+ SAL_INFO("sw.calc", "cos");
+ return StdFunc(&cos, false);
+ case CALC_TAN:
+ SAL_INFO("sw.calc", "tan");
+ return StdFunc(&tan, false);
+ case CALC_ATAN:
+ SAL_INFO("sw.calc", "atan");
+ return StdFunc(&atan, false);
+ case CALC_ASIN:
+ SAL_INFO("sw.calc", "asin");
+ return StdFunc(&asin, true);
+ case CALC_ACOS:
+ SAL_INFO("sw.calc", "acos");
+ return StdFunc(&acos, true);
+ case CALC_ABS:
+ SAL_INFO("sw.calc", "abs");
+ return StdFunc(&abs, false);
+ case CALC_SIGN:
+ {
+ SAL_INFO("sw.calc", "sign");
+ SwSbxValue nErg;
+ GetToken();
+ double nVal = Prim().GetDouble();
+ nErg.PutDouble( int(0 < nVal) - int(nVal < 0) );
+ return nErg;
+ }
+ case CALC_INT:
+ {
+ SAL_INFO("sw.calc", "int");
+ SwSbxValue nErg;
+ GetToken();
+ sal_Int32 nVal = static_cast<sal_Int32>( Prim().GetDouble() );
+ nErg.PutDouble( nVal );
+ return nErg;
+ }
+ case CALC_NOT:
+ {
+ SAL_INFO("sw.calc", "not");
+ GetToken();
+ SwSbxValue nErg = Prim();
+ if( SbxSTRING == nErg.GetType() )
+ {
+ nErg.PutBool( nErg.GetOUString().isEmpty() );
+ }
+ else if(SbxBOOL == nErg.GetType() )
+ {
+ nErg.PutBool(!nErg.GetBool());
+ }
+ // Evaluate arguments manually so that the binary NOT below does not
+ // get called. We want a BOOLEAN NOT.
+ else if (nErg.IsNumeric())
+ {
+ nErg.PutLong( nErg.GetDouble() == 0.0 ? 1 : 0 );
+ }
+ else
+ {
+ OSL_FAIL( "unexpected case. computing binary NOT" );
+ //!! computes a binary NOT
+ nErg.Compute( SbxNOT, nErg );
+ }
+ return nErg;
+ }
+ case CALC_NUMBER:
+ {
+ SAL_INFO("sw.calc", "number: " << m_nNumberValue.GetDouble());
+ SwSbxValue nErg;
+ m_bHasNumber = true;
+ if( GetToken() == CALC_PHD )
+ {
+ double aTmp = m_nNumberValue.GetDouble();
+ aTmp *= 0.01;
+ nErg.PutDouble( aTmp );
+ GetToken();
+ }
+ else if( m_eCurrOper == CALC_NAME )
+ {
+ m_eError = SwCalcError::Syntax;
+ }
+ else
+ {
+ nErg = m_nNumberValue;
+ rChkPow = true;
+ }
+ return nErg;
+ }
+ case CALC_NAME:
+ {
+ SAL_INFO("sw.calc", "name");
+ SwSbxValue nErg;
+ switch(SwCalcOper eOper = GetToken())
+ {
+ case CALC_ASSIGN:
+ {
+ SwCalcExp* n = VarInsert(m_aVarName.toString());
+ GetToken();
+ nErg = n->nValue = Expr();
+ break;
+ }
+ default:
+ nErg = VarLook(m_aVarName.toString())->nValue;
+ // Explicitly disallow unknown function names (followed by "("),
+ // allow unknown variable names (equal to zero)
+ if (nErg.IsVoidValue() && (eOper == CALC_LP))
+ m_eError = SwCalcError::Syntax;
+ else
+ rChkPow = true;
+ break;
+ }
+ return nErg;
+ }
+ case CALC_MINUS:
+ {
+ SAL_INFO("sw.calc", "-");
+ SwSbxValue nErg;
+ GetToken();
+ nErg.PutDouble( -(Prim().GetDouble()) );
+ return nErg;
+ }
+ case CALC_LP:
+ {
+ SAL_INFO("sw.calc", "(");
+ GetToken();
+ SwSbxValue nErg = Expr();
+ if( m_eCurrOper != CALC_RP )
+ {
+ m_eError = SwCalcError::FaultyBrackets;
+ }
+ else
+ {
+ GetToken();
+ rChkPow = true; // in order for =(7)^2 to work
+ }
+ return nErg;
+ }
+ case CALC_RP:
+ // ignore, see tdf#121962
+ SAL_INFO("sw.calc", ")");
+ break;
+ case CALC_MEAN:
+ case CALC_AVERAGE:
+ {
+ SAL_INFO("sw.calc", "mean");
+ m_nListPor = 1;
+ m_bHasNumber = CALC_MEAN == m_eCurrOper;
+ GetToken();
+ SwSbxValue nErg = Expr();
+ double aTmp = nErg.GetDouble();
+ aTmp /= m_nListPor;
+ if ( !m_bHasNumber )
+ m_eError = SwCalcError::DivByZero;
+ else
+ nErg.PutDouble( aTmp );
+ return nErg;
+ }
+ case CALC_COUNT:
+ {
+ SAL_INFO("sw.calc", "count");
+ m_nListPor = 1;
+ m_bHasNumber = false;
+ GetToken();
+ SwSbxValue nErg = Expr();
+ nErg.PutDouble( m_bHasNumber ? m_nListPor : 0 );
+ return nErg;
+ }
+ case CALC_SQRT:
+ {
+ SAL_INFO("sw.calc", "sqrt");
+ GetToken();
+ SwSbxValue nErg = Prim();
+ if( nErg.GetDouble() < 0 )
+ m_eError = SwCalcError::Overflow;
+ else
+ nErg.PutDouble( sqrt( nErg.GetDouble() ));
+ return nErg;
+ }
+ case CALC_SUM:
+ case CALC_PRODUCT:
+ case CALC_DATE:
+ case CALC_MIN:
+ case CALC_MAX:
+ {
+ SAL_INFO("sw.calc", "sum/product/date/min/max");
+ GetToken();
+ SwSbxValue nErg = Expr();
+ return nErg;
+ }
+ case CALC_ENDCALC:
+ {
+ SAL_INFO("sw.calc", "endcalc");
+ SwSbxValue nErg;
+ nErg.Clear();
+ return nErg;
+ }
+ default:
+ SAL_INFO("sw.calc", "syntax error");
+ m_eError = SwCalcError::Syntax;
+ break;
+ }
+
+ return SwSbxValue();
+}
+
+SwSbxValue SwCalc::Prim()
+{
+ bool bChkPow;
+ SwSbxValue nErg = PrimFunc(bChkPow);
+
+ if (bChkPow && m_eCurrOper == CALC_POW)
+ {
+ double dleft = nErg.GetDouble();
+ GetToken();
+ double right = Prim().GetDouble();
+
+ double fraction;
+ fraction = modf( right, &o3tl::temporary(double()) );
+ if( ( dleft < 0.0 && 0.0 != fraction ) ||
+ ( 0.0 == dleft && right < 0.0 ) )
+ {
+ m_eError = SwCalcError::Overflow;
+ nErg.Clear();
+ }
+ else
+ {
+ dleft = pow(dleft, right );
+ if( dleft == HUGE_VAL )
+ {
+ m_eError = SwCalcError::OverflowInPower;
+ nErg.Clear();
+ }
+ else
+ {
+ nErg.PutDouble( dleft );
+ }
+ }
+ }
+
+ return nErg;
+}
+
+SwSbxValue SwCalc::Expr()
+{
+ SwSbxValue left = Term();
+ m_nLastLeft = left;
+ for(;;)
+ {
+ switch(m_eCurrOper)
+ {
+ case CALC_PLUS:
+ {
+ GetToken();
+ left.MakeDouble();
+ SwSbxValue right(Term());
+ right.MakeDouble();
+ left.Compute(SbxPLUS, right);
+ m_nListPor++;
+ break;
+ }
+ case CALC_MINUS:
+ {
+ GetToken();
+ left.MakeDouble();
+ SwSbxValue right(Term());
+ right.MakeDouble();
+ left.Compute(SbxMINUS, right);
+ break;
+ }
+ default:
+ {
+ return left;
+ }
+ }
+ }
+}
+
+OUString SwCalc::GetColumnName(const OUString& rName)
+{
+ sal_Int32 nPos = rName.indexOf(DB_DELIM);
+ if( -1 != nPos )
+ {
+ nPos = rName.indexOf(DB_DELIM, nPos + 1);
+
+ if( -1 != nPos )
+ return rName.copy(nPos + 1);
+ }
+ return rName;
+}
+
+OUString SwCalc::GetDBName(std::u16string_view rName)
+{
+ size_t nPos = rName.find(DB_DELIM);
+ if( std::u16string_view::npos != nPos )
+ {
+ nPos = rName.find(DB_DELIM, nPos + 1);
+
+ if( std::u16string_view::npos != nPos )
+ return OUString(rName.substr( 0, nPos ));
+ }
+ SwDBData aData = m_rDoc.GetDBData();
+ return aData.sDataSource + OUStringChar(DB_DELIM) + aData.sCommand;
+}
+
+namespace
+{
+ bool lcl_Str2Double( const OUString& rCommand, sal_Int32& rCommandPos,
+ double& rVal,
+ const LocaleDataWrapper* const pLclData )
+ {
+ assert(pLclData);
+ const sal_Unicode nCurrCmdPos = rCommandPos;
+ rtl_math_ConversionStatus eStatus;
+ const sal_Unicode* pEnd;
+ rVal = pLclData->stringToDouble( rCommand.getStr() + rCommandPos,
+ rCommand.getStr() + rCommand.getLength(),
+ true,
+ &eStatus,
+ &pEnd );
+ rCommandPos = static_cast<sal_Int32>(pEnd - rCommand.getStr());
+
+ return rtl_math_ConversionStatus_Ok == eStatus &&
+ nCurrCmdPos != rCommandPos;
+ }
+}
+
+bool SwCalc::Str2Double( const OUString& rCommand, sal_Int32& rCommandPos,
+ double& rVal )
+{
+ const SvtSysLocale aSysLocale;
+ return lcl_Str2Double( rCommand, rCommandPos, rVal, &aSysLocale.GetLocaleData() );
+}
+
+bool SwCalc::Str2Double( const OUString& rCommand, sal_Int32& rCommandPos,
+ double& rVal, SwDoc const * const pDoc )
+{
+ const SvtSysLocale aSysLocale;
+ std::unique_ptr<const LocaleDataWrapper> pLclD;
+ if( pDoc )
+ {
+ LanguageType eLang = GetDocAppScriptLang( *pDoc );
+ if (eLang != aSysLocale.GetLanguageTag().getLanguageType())
+ {
+ pLclD.reset( new LocaleDataWrapper( LanguageTag( eLang )) );
+ }
+ }
+
+ bool const bRet = lcl_Str2Double(rCommand, rCommandPos, rVal,
+ pLclD ? pLclD.get() : &aSysLocale.GetLocaleData());
+
+ return bRet;
+}
+
+bool SwCalc::IsValidVarName( const OUString& rStr, OUString* pValidName )
+{
+ bool bRet = false;
+ using namespace ::com::sun::star::i18n;
+ {
+ // Parse any token.
+ ParseResult aRes = GetAppCharClass().parseAnyToken( rStr, 0,
+ coStartFlags, OUString(),
+ coContFlags, OUString() );
+
+ if( aRes.TokenType & KParseType::IDENTNAME )
+ {
+ bRet = aRes.EndPos == rStr.getLength();
+ if( pValidName )
+ {
+ *pValidName = rStr.copy( aRes.LeadingWhiteSpace,
+ aRes.EndPos - aRes.LeadingWhiteSpace );
+ }
+ }
+ else if( pValidName )
+ pValidName->clear();
+ }
+ return bRet;
+}
+
+SwHash::SwHash(const OUString& rStr)
+ : aStr(rStr)
+{
+}
+
+SwHash::~SwHash()
+{
+}
+
+SwCalcExp::SwCalcExp(const OUString& rStr, const SwSbxValue& rVal,
+ const SwFieldType* pType)
+ : SwHash(rStr)
+ , nValue(rVal)
+ , pFieldType(pType)
+{
+}
+
+bool SwSbxValue::GetBool() const
+{
+ return SbxSTRING == GetType() ? !GetOUString().isEmpty()
+ : SbxValue::GetBool();
+}
+
+double SwSbxValue::GetDouble() const
+{
+ double nRet;
+ if( SbxSTRING == GetType() )
+ {
+ sal_Int32 nStt = 0;
+ SwCalc::Str2Double( GetOUString(), nStt, nRet );
+ }
+ else if (IsBool())
+ {
+ nRet = GetBool() ? 1.0 : 0.0;
+ }
+ else
+ {
+ nRet = SbxValue::GetDouble();
+ }
+ return nRet;
+}
+
+SwSbxValue& SwSbxValue::MakeDouble()
+{
+ if( GetType() == SbxSTRING || GetType() == SbxBOOL )
+ PutDouble( GetDouble() );
+ return *this;
+}
+
+#ifdef STANDALONE_HASHCALC
+
+// this is example code how to create hash values in the CTOR:
+
+#include <stdio.h>
+void main()
+{
+ static char
+ sNType0[] = "false", sNType1[] = "true", sNType2[] = "pi",
+ sNType3[] = "e", sNType4[] = "tables", sNType5[] = "graf",
+ sNType6[] = "ole", sNType7[] = "page", sNType8[] = "para",
+ sNType9[] = "word", sNType10[]= "char",
+ sNType11[] = "user_company" , sNType12[] = "user_firstname" ,
+ sNType13[] = "user_lastname" , sNType14[] = "user_initials",
+ sNType15[] = "user_street" , sNType16[] = "user_country" ,
+ sNType17[] = "user_zipcode" , sNType18[] = "user_city" ,
+ sNType19[] = "user_title" , sNType20[] = "user_position" ,
+ sNType21[] = "user_tel_home", sNType22[] = "user_tel_work",
+ sNType23[] = "user_fax" , sNType24[] = "user_email" ,
+ sNType25[] = "user_state", sNType26[] = "graph"
+ ;
+
+ static const char* sNTypeTab[ 27 ] =
+ {
+ sNType0, sNType1, sNType2, sNType3, sNType4, sNType5,
+ sNType6, sNType7, sNType8, sNType9, sNType10, sNType11,
+ sNType12, sNType13, sNType14, sNType15, sNType16, sNType17,
+ sNType18, sNType19, sNType20, sNType21, sNType22, sNType23,
+ sNType24, sNType25, sNType26
+ };
+
+ const unsigned short nTableSize = 47;
+ int aArr[ nTableSize ] = { 0 };
+ char ch;
+
+ for( int n = 0; n < 27; ++n )
+ {
+ unsigned int ii = 0;
+ const char* pp = sNTypeTab[ n ];
+
+ while( *pp )
+ {
+ ii = ii << 1 ^ *pp++;
+ }
+ ii %= nTableSize;
+
+ ch = aArr[ ii ] ? 'X' : ' ';
+ aArr[ ii ] = 1;
+ printf( "%-20s -> %3u [%c]\n", sNTypeTab[ n ], ii, ch );
+ }
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/checkit.cxx b/sw/source/core/bastyp/checkit.cxx
new file mode 100644
index 000000000..f6e00314d
--- /dev/null
+++ b/sw/source/core/bastyp/checkit.cxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <checkit.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/InputSequenceChecker.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::i18n;
+
+SwCheckIt::SwCheckIt()
+{
+ Reference<XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ xCheck = InputSequenceChecker::create(xContext);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/index.cxx b/sw/source/core/bastyp/index.cxx
new file mode 100644
index 000000000..3fa1eaec1
--- /dev/null
+++ b/sw/source/core/bastyp/index.cxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <index.hxx>
+
+#include <assert.h>
+#include <sal/log.hxx>
+
+#include <crossrefbookmark.hxx>
+
+SwIndex::SwIndex(SwIndexReg *const pReg, sal_Int32 const nIdx)
+ : m_nIndex( nIdx )
+ , m_pIndexReg( pReg )
+ , m_pNext( nullptr )
+ , m_pPrev( nullptr )
+ , m_pMark( nullptr )
+{
+ Init(m_nIndex);
+}
+
+SwIndex::SwIndex( const SwIndex& rIdx, short nDiff )
+ : m_pIndexReg( rIdx.m_pIndexReg )
+ , m_pNext( nullptr )
+ , m_pPrev( nullptr )
+ , m_pMark( nullptr )
+{
+ ChgValue( rIdx, rIdx.m_nIndex + nDiff );
+}
+
+SwIndex::SwIndex( const SwIndex& rIdx )
+ : m_nIndex( rIdx.m_nIndex )
+ , m_pIndexReg( rIdx.m_pIndexReg )
+ , m_pNext( nullptr )
+ , m_pPrev( nullptr )
+ , m_pMark( nullptr )
+{
+ ChgValue( rIdx, rIdx.m_nIndex );
+}
+
+void SwIndex::Init(sal_Int32 const nIdx)
+{
+ if (!m_pIndexReg)
+ {
+ m_nIndex = 0; // always 0 if no IndexReg
+ }
+ else if (!m_pIndexReg->m_pFirst) // first Index?
+ {
+ assert(!m_pIndexReg->m_pLast);
+ m_pIndexReg->m_pFirst = m_pIndexReg->m_pLast = this;
+ m_nIndex = nIdx;
+ }
+ else if (nIdx > ((m_pIndexReg->m_pLast->m_nIndex
+ - m_pIndexReg->m_pFirst->m_nIndex) / 2))
+ {
+ ChgValue( *m_pIndexReg->m_pLast, nIdx );
+ }
+ else
+ {
+ ChgValue( *m_pIndexReg->m_pFirst, nIdx );
+ }
+}
+
+SwIndex& SwIndex::ChgValue( const SwIndex& rIdx, sal_Int32 nNewValue )
+{
+ assert(m_pIndexReg == rIdx.m_pIndexReg);
+ if (!m_pIndexReg)
+ {
+ m_nIndex = 0;
+ return *this; // no IndexReg => no list to sort into; m_nIndex is 0
+ }
+ SwIndex* pFnd = const_cast<SwIndex*>(&rIdx);
+ if (rIdx.m_nIndex > nNewValue) // move forwards
+ {
+ for (;;)
+ {
+ SwIndex* pPrv = pFnd->m_pPrev;
+ if (!pPrv || pPrv->m_nIndex <= nNewValue)
+ break;
+ pFnd = pPrv;
+ }
+
+ if( pFnd != this )
+ {
+ // remove from list at old position
+ Remove();
+
+ m_pNext = pFnd;
+ m_pPrev = pFnd->m_pPrev;
+ if (m_pPrev)
+ m_pPrev->m_pNext = this;
+ else
+ m_pIndexReg->m_pFirst = this;
+ pFnd->m_pPrev = this;
+ }
+ }
+ else if (rIdx.m_nIndex < nNewValue)
+ {
+ for (;;)
+ {
+ SwIndex* pNxt = pFnd->m_pNext;
+ if (!pNxt || pNxt->m_nIndex >= nNewValue)
+ break;
+ pFnd = pNxt;
+ }
+
+ if( pFnd != this )
+ {
+ // remove from list at old position
+ Remove();
+
+ m_pPrev = pFnd;
+ m_pNext = pFnd->m_pNext;
+ if (m_pNext)
+ m_pNext->m_pPrev = this;
+ else
+ m_pIndexReg->m_pLast = this;
+ pFnd->m_pNext = this;
+ }
+ }
+ else if( pFnd != this )
+ {
+ // remove from list at old position
+ Remove();
+
+ m_pPrev = pFnd; // == &rIdx here
+ m_pNext = rIdx.m_pNext;
+ m_pPrev->m_pNext = this;
+
+ if (!m_pNext) // last in the list
+ m_pIndexReg->m_pLast = this;
+ else
+ m_pNext->m_pPrev = this;
+ }
+
+ if (m_pIndexReg->m_pFirst == m_pNext)
+ m_pIndexReg->m_pFirst = this;
+ if (m_pIndexReg->m_pLast == m_pPrev)
+ m_pIndexReg->m_pLast = this;
+
+ m_nIndex = nNewValue;
+
+ return *this;
+}
+
+void SwIndex::Remove()
+{
+ if (!m_pIndexReg)
+ {
+ assert(!m_pPrev && !m_pNext);
+ return;
+ }
+
+ if (m_pPrev)
+ {
+ m_pPrev->m_pNext = m_pNext;
+ }
+ else if (m_pIndexReg->m_pFirst == this)
+ {
+ m_pIndexReg->m_pFirst = m_pNext;
+ }
+
+ if (m_pNext)
+ {
+ m_pNext->m_pPrev = m_pPrev;
+ }
+ else if (m_pIndexReg->m_pLast == this)
+ {
+ m_pIndexReg->m_pLast = m_pPrev;
+ }
+}
+
+SwIndex& SwIndex::operator=( const SwIndex& rIdx )
+{
+ bool bEqual;
+ if (rIdx.m_pIndexReg != m_pIndexReg) // unregister!
+ {
+ Remove();
+ m_pIndexReg = rIdx.m_pIndexReg;
+ m_pNext = m_pPrev = nullptr;
+ bEqual = false;
+ }
+ else
+ bEqual = rIdx.m_nIndex == m_nIndex;
+
+ if( !bEqual )
+ ChgValue( rIdx, rIdx.m_nIndex );
+ return *this;
+}
+
+SwIndex& SwIndex::Assign( SwIndexReg* pArr, sal_Int32 nIdx )
+{
+ if (pArr != m_pIndexReg) // unregister!
+ {
+ Remove();
+ m_pIndexReg = pArr;
+ m_pNext = m_pPrev = nullptr;
+ Init(nIdx);
+ }
+ else if (m_nIndex != nIdx)
+ {
+ ChgValue( *this, nIdx );
+ }
+ return *this;
+}
+
+void SwIndex::SetMark(const sw::mark::IMark* pMark)
+{
+ m_pMark = pMark;
+}
+
+SwIndexReg::SwIndexReg()
+ : m_pFirst( nullptr ), m_pLast( nullptr )
+{
+}
+
+SwIndexReg::~SwIndexReg()
+{
+ assert(!m_pFirst && !m_pLast && "There are still indices registered");
+}
+
+void SwIndexReg::Update(
+ SwIndex const & rIdx,
+ const sal_Int32 nDiff,
+ const bool bNeg,
+ const bool /* argument is only used in derived class*/ )
+{
+ SwIndex* pStt = const_cast<SwIndex*>(&rIdx);
+ const sal_Int32 nNewVal = rIdx.m_nIndex;
+ if( bNeg )
+ {
+ const sal_Int32 nLast = rIdx.GetIndex() + nDiff;
+ while (pStt && pStt->m_nIndex == nNewVal)
+ {
+ pStt->m_nIndex = nNewVal;
+ pStt = pStt->m_pPrev;
+ }
+ pStt = rIdx.m_pNext;
+ while (pStt && pStt->m_nIndex >= nNewVal
+ && pStt->m_nIndex <= nLast)
+ {
+ pStt->m_nIndex = nNewVal;
+ pStt = pStt->m_pNext;
+ }
+ while( pStt )
+ {
+ pStt->m_nIndex = pStt->m_nIndex - nDiff;
+ pStt = pStt->m_pNext;
+ }
+ }
+ else
+ {
+ while (pStt && pStt->m_nIndex == nNewVal)
+ {
+ pStt->m_nIndex = pStt->m_nIndex + nDiff;
+ pStt = pStt->m_pPrev;
+ }
+ pStt = rIdx.m_pNext;
+ while( pStt )
+ {
+ // HACK: avoid updating position of cross-ref bookmarks
+ if (!pStt->m_pMark || nullptr == dynamic_cast<
+ ::sw::mark::CrossRefBookmark const*>(pStt->m_pMark))
+ {
+ pStt->m_nIndex = pStt->m_nIndex + nDiff;
+ }
+ pStt = pStt->m_pNext;
+ }
+ }
+}
+
+void SwIndexReg::MoveTo( SwIndexReg& rArr )
+{
+ if (!(this != &rArr && m_pFirst))
+ return;
+
+ SwIndex * pIdx = const_cast<SwIndex*>(m_pFirst);
+ SwIndex * pNext;
+ while( pIdx )
+ {
+ pNext = pIdx->m_pNext;
+ pIdx->Assign( &rArr, pIdx->GetIndex() );
+ pIdx = pNext;
+ }
+ m_pFirst = nullptr;
+ m_pLast = nullptr;
+}
+
+#ifdef DBG_UTIL
+
+// SwIndex
+
+sal_Int32 SwIndex::operator++()
+{
+ SAL_WARN_IF( !(m_nIndex < SAL_MAX_INT32), "sw.core",
+ "SwIndex::operator++() wraps around" );
+
+ ChgValue( *this, m_nIndex+1 );
+ return m_nIndex;
+}
+
+sal_Int32 SwIndex::operator--(int)
+{
+ SAL_WARN_IF( !(m_nIndex > 0), "sw.core",
+ "SwIndex::operator--(int) wraps around" );
+
+ const sal_Int32 nOldIndex = m_nIndex;
+ ChgValue( *this, m_nIndex-1 );
+ return nOldIndex;
+}
+
+sal_Int32 SwIndex::operator--()
+{
+ SAL_WARN_IF( !( m_nIndex > 0), "sw.core",
+ "SwIndex::operator--() wraps around" );
+ return ChgValue( *this, m_nIndex-1 ).m_nIndex;
+}
+
+sal_Int32 SwIndex::operator+=( sal_Int32 const nVal )
+{
+ SAL_WARN_IF( !(m_nIndex <= SAL_MAX_INT32 - nVal), "sw.core",
+ "SwIndex SwIndex::operator+=(sal_Int32) wraps around" );
+ return ChgValue( *this, m_nIndex + nVal ).m_nIndex;
+}
+
+sal_Int32 SwIndex::operator-=( sal_Int32 const nVal )
+{
+ SAL_WARN_IF( !(m_nIndex >= nVal), "sw.core",
+ "SwIndex::operator-=(sal_Int32) wraps around" );
+ return ChgValue( *this, m_nIndex - nVal ).m_nIndex;
+}
+
+bool SwIndex::operator< ( const SwIndex & rIndex ) const
+{
+ // Attempt to compare indices into different arrays
+ assert(m_pIndexReg == rIndex.m_pIndexReg);
+ return m_nIndex < rIndex.m_nIndex;
+}
+
+bool SwIndex::operator<=( const SwIndex & rIndex ) const
+{
+ // Attempt to compare indices into different arrays
+ assert(m_pIndexReg == rIndex.m_pIndexReg);
+ return m_nIndex <= rIndex.m_nIndex;
+}
+
+bool SwIndex::operator> ( const SwIndex & rIndex ) const
+{
+ // Attempt to compare indices into different arrays
+ assert(m_pIndexReg == rIndex.m_pIndexReg);
+ return m_nIndex > rIndex.m_nIndex;
+}
+
+bool SwIndex::operator>=( const SwIndex & rIndex ) const
+{
+ // Attempt to compare indices into different arrays
+ assert(m_pIndexReg == rIndex.m_pIndexReg);
+ return m_nIndex >= rIndex.m_nIndex;
+}
+
+SwIndex& SwIndex::operator= ( sal_Int32 const nVal )
+{
+ if (m_nIndex != nVal)
+ ChgValue( *this, nVal );
+
+ return *this;
+}
+
+#endif
+
+std::ostream& operator <<(std::ostream& s, const SwIndex& index)
+{
+ return s << "SwIndex offset (" << index.GetIndex() << ")";
+}
+
+std::ostream& operator <<(std::ostream& s, const SwNodeOffset& index)
+{
+ return s << sal_Int32(index);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx
new file mode 100644
index 000000000..461d7bc2d
--- /dev/null
+++ b/sw/source/core/bastyp/init.cxx
@@ -0,0 +1,792 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <acmplwrd.hxx>
+#include <breakit.hxx>
+#include <cellatr.hxx>
+#include <checkit.hxx>
+#include <cmdid.h>
+#include <comphelper/processfactory.hxx>
+#include <doc.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/blinkitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/nhypitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/paravertalignitem.hxx>
+#include <editeng/pbinitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <editeng/prntitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/rsiditem.hxx>
+#include <svl/grabbagitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/spltitem.hxx>
+#include <editeng/svxacorr.hxx>
+#include <editeng/swafopt.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <editsh.hxx>
+#include <fchrfmt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtautofmt.hxx>
+#include <fmtclbl.hxx>
+#include <fmtclds.hxx>
+#include <fmtcnct.hxx>
+#include <fmtcntnt.hxx>
+#include <fmteiro.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtfld.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <fmtfordr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtftn.hxx>
+#include <fmtftntx.hxx>
+#include <formatlinebreak.hxx>
+#include <fmthdft.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtline.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtmeta.hxx>
+#include <formatcontentcontrol.hxx>
+#include <fmtornt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtruby.hxx>
+#include <fmtsrnd.hxx>
+#include <fmturl.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <fntcache.hxx>
+#include <grfatr.hxx>
+#include <hfspacingitem.hxx>
+#include <hintids.hxx>
+#include <init.hxx>
+#include <paratr.hxx>
+#include <proofreadingiterator.hxx>
+#include <editeng/editids.hrc>
+#include <svl/macitem.hxx>
+#include <svx/sdtaitm.hxx>
+#include <swcalwrp.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <tblafmt.hxx>
+#include <tgrditem.hxx>
+#include <tools/globname.hxx>
+#include <tox.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <salhelper/singletonref.hxx>
+#include <viscrs.hxx>
+
+using namespace ::com::sun::star;
+
+// some ranges for sets in collections/ nodes
+
+// AttrSet range for the 2 break attributes
+WhichRangesContainer const aBreakSetRange(svl::Items<
+ RES_PAGEDESC, RES_BREAK
+>);
+
+// AttrSet range for TextFormatColl
+// list attributes ( RES_PARATR_LIST_BEGIN - RES_PARATR_LIST_END ) are not
+// included in the paragraph style's itemset.
+WhichRangesContainer const aTextFormatCollSetRange(svl::Items<
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_PARATR_BEGIN, RES_PARATR_END-1,
+ RES_PARATR_LIST_LEVEL, RES_PARATR_LIST_LEVEL,
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+
+ // FillAttribute support
+ XATTR_FILL_FIRST, XATTR_FILL_LAST
+
+>);
+
+// AttrSet range for GrfFormatColl
+WhichRangesContainer const aGrfFormatCollSetRange(svl::Items<
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_GRFATR_BEGIN, RES_GRFATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+// AttrSet range for TextNode
+WhichRangesContainer const aTextNodeSetRange(svl::Items<
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_PARATR_BEGIN, RES_PARATR_END-1,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+
+ // FillAttribute support (paragraph FillStyle)
+ XATTR_FILL_FIRST, XATTR_FILL_LAST
+
+>);
+
+// AttrSet range for NoTextNode
+WhichRangesContainer const aNoTextNodeSetRange(svl::Items<
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_GRFATR_BEGIN, RES_GRFATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+WhichRangesContainer const aTableSetRange(svl::Items<
+ RES_FILL_ORDER, RES_FRM_SIZE,
+ RES_LR_SPACE, RES_BREAK,
+ RES_HORI_ORIENT, RES_HORI_ORIENT,
+ RES_BACKGROUND, RES_SHADOW,
+ RES_KEEP, RES_KEEP,
+ RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT,
+ RES_FRAMEDIR, RES_FRAMEDIR,
+ // #i29550#
+ RES_COLLAPSING_BORDERS, RES_COLLAPSING_BORDERS,
+ // <-- collapsing
+ RES_FRMATR_GRABBAG, RES_FRMATR_GRABBAG,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+WhichRangesContainer const aTableLineSetRange(svl::Items<
+ RES_FILL_ORDER, RES_FRM_SIZE,
+ // HasTextChangesOnly
+ RES_LR_SPACE, RES_UL_SPACE,
+ RES_PRINT, RES_PRINT,
+ RES_PROTECT, RES_PROTECT,
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BACKGROUND, RES_SHADOW,
+ RES_ROW_SPLIT, RES_ROW_SPLIT,
+ RES_FRMATR_GRABBAG, RES_FRMATR_GRABBAG,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+WhichRangesContainer const aTableBoxSetRange(svl::Items<
+ RES_FILL_ORDER, RES_FRM_SIZE,
+ RES_LR_SPACE, RES_UL_SPACE,
+ RES_PROTECT, RES_PROTECT,
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BACKGROUND, RES_SHADOW,
+ RES_FRAMEDIR, RES_FRAMEDIR,
+ RES_FRMATR_GRABBAG, RES_FRMATR_GRABBAG,
+ RES_BOXATR_BEGIN, RES_BOXATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+// AttrSet range for SwFrameFormat
+WhichRangesContainer const aFrameFormatSetRange(svl::Items<
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+
+ // FillAttribute support (TextFrame, OLE, Writer GraphicObject)
+ XATTR_FILL_FIRST, XATTR_FILL_LAST
+
+>);
+
+// AttrSet range for SwCharFormat
+WhichRangesContainer const aCharFormatSetRange(svl::Items<
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+// AttrSet range for character autostyles
+WhichRangesContainer const aCharAutoFormatSetRange(svl::Items<
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+// AttrSet range for SwPageDescFormat
+WhichRangesContainer const aPgFrameFormatSetRange(svl::Items<
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1
+>);
+
+// create table for accessing default format attributes
+SwDfltAttrTab aAttrTab( POOLATTR_END - POOLATTR_BEGIN, nullptr );
+
+SfxItemInfo aSlotTab[] =
+{
+ { SID_ATTR_CHAR_CASEMAP, true }, // RES_CHRATR_CASEMAP
+ { SID_ATTR_CHAR_CHARSETCOLOR, true }, // RES_CHRATR_CHARSETCOLOR
+ { SID_ATTR_CHAR_COLOR, true }, // RES_CHRATR_COLOR
+ { SID_ATTR_CHAR_CONTOUR, true }, // RES_CHRATR_CONTOUR
+ { SID_ATTR_CHAR_STRIKEOUT, true }, // RES_CHRATR_CROSSEDOUT
+ { SID_ATTR_CHAR_ESCAPEMENT, true }, // RES_CHRATR_ESCAPEMENT
+ { SID_ATTR_CHAR_FONT, true }, // RES_CHRATR_FONT
+ { SID_ATTR_CHAR_FONTHEIGHT, true }, // RES_CHRATR_FONTSIZE
+ { SID_ATTR_CHAR_KERNING, true }, // RES_CHRATR_KERNING
+ { SID_ATTR_CHAR_LANGUAGE, true }, // RES_CHRATR_LANGUAGE
+ { SID_ATTR_CHAR_POSTURE, true }, // RES_CHRATR_POSTURE
+ { 0, true }, // RES_CHRATR_UNUSED1
+ { SID_ATTR_CHAR_SHADOWED, true }, // RES_CHRATR_SHADOWED
+ { SID_ATTR_CHAR_UNDERLINE, true }, // RES_CHRATR_UNDERLINE
+ { SID_ATTR_CHAR_WEIGHT, true }, // RES_CHRATR_WEIGHT
+ { SID_ATTR_CHAR_WORDLINEMODE, true }, // RES_CHRATR_WORDLINEMODE
+ { SID_ATTR_CHAR_AUTOKERN, true }, // RES_CHRATR_AUTOKERN
+ { SID_ATTR_FLASH, true }, // RES_CHRATR_BLINK
+ { 0, true }, // RES_CHRATR_UNUSED2
+ { 0, true }, // RES_CHRATR_NOHYPHEN
+ { SID_ATTR_BRUSH_CHAR, true }, // RES_CHRATR_BACKGROUND
+ { SID_ATTR_CHAR_CJK_FONT, true }, // RES_CHRATR_CJK_FONT
+ { SID_ATTR_CHAR_CJK_FONTHEIGHT, true },// RES_CHRATR_CJK_FONTSIZE
+ { SID_ATTR_CHAR_CJK_LANGUAGE, true }, // RES_CHRATR_CJK_LANGUAGE
+ { SID_ATTR_CHAR_CJK_POSTURE, true }, // RES_CHRATR_CJK_POSTURE
+ { SID_ATTR_CHAR_CJK_WEIGHT, true }, // RES_CHRATR_CJK_WEIGHT
+ { SID_ATTR_CHAR_CTL_FONT, true }, // RES_CHRATR_CTL_FONT
+ { SID_ATTR_CHAR_CTL_FONTHEIGHT, true },// RES_CHRATR_CTL_FONTSIZE
+ { SID_ATTR_CHAR_CTL_LANGUAGE, true }, // RES_CHRATR_CTL_LANGUAGE
+ { SID_ATTR_CHAR_CTL_POSTURE, true }, // RES_CHRATR_CTL_POSTURE
+ { SID_ATTR_CHAR_CTL_WEIGHT, true }, // RES_CHRATR_CTL_WEIGHT
+ { SID_ATTR_CHAR_ROTATED, true }, // RES_CHRATR_ROTATE
+ { SID_ATTR_CHAR_EMPHASISMARK, true }, // RES_CHRATR_EMPHASIS_MARK
+ { SID_ATTR_CHAR_TWO_LINES, true }, // RES_CHRATR_TWO_LINES
+ { SID_ATTR_CHAR_SCALEWIDTH, true }, // RES_CHRATR_SCALEW
+ { SID_ATTR_CHAR_RELIEF, true }, // RES_CHRATR_RELIEF
+ { SID_ATTR_CHAR_HIDDEN, true }, // RES_CHRATR_HIDDEN
+ { SID_ATTR_CHAR_OVERLINE, true }, // RES_CHRATR_OVERLINE
+ { 0, true }, // RES_CHRATR_RSID
+ { SID_ATTR_CHAR_BOX, true }, // RES_CHRATR_BOX
+ { SID_ATTR_CHAR_SHADOW, true }, // RES_CHRATR_SHADOW
+ { 0, true }, // RES_CHRATR_HIGHLIGHT
+ { SID_ATTR_CHAR_GRABBAG, true }, // RES_CHRATR_GRABBAG
+ { 0, true }, // RES_CHRATR_BIDIRTL
+ { 0, true }, // RES_CHRATR_IDCTHINT
+
+ { 0, false }, // RES_TXTATR_REFMARK
+ { 0, false }, // RES_TXTATR_TOXMARK
+ { 0, false }, // RES_TXTATR_META
+ { 0, false }, // RES_TXTATR_METAFIELD
+ { 0, true }, // RES_TXTATR_AUTOFMT
+ { FN_TXTATR_INET, false }, // RES_TXTATR_INETFMT
+ { 0, false }, // RES_TXTATR_CHARFMT
+ { SID_ATTR_CHAR_CJK_RUBY, false }, // RES_TXTATR_CJK_RUBY
+ { 0, true }, // RES_TXTATR_UNKNOWN_CONTAINER
+ { 0, false }, // RES_TXTATR_INPUTFIELD
+ { 0, false }, // RES_TXTATR_CONTENTCONTROL
+
+ { 0, false }, // RES_TXTATR_FIELD
+ { 0, false }, // RES_TXTATR_FLYCNT
+ { 0, false }, // RES_TXTATR_FTN
+ { 0, false }, // RES_TXTATR_ANNOTATION
+ { 0, false }, // RES_TXTATR_LINEBREAK
+ { 0, true }, // RES_TXTATR_DUMMY1
+
+ { SID_ATTR_PARA_LINESPACE, true }, // RES_PARATR_LINESPACING
+ { SID_ATTR_PARA_ADJUST, true }, // RES_PARATR_ADJUST
+ { SID_ATTR_PARA_SPLIT, true }, // RES_PARATR_SPLIT
+ { SID_ATTR_PARA_ORPHANS, true }, // RES_PARATR_ORPHANS
+ { SID_ATTR_PARA_WIDOWS, true }, // RES_PARATR_WIDOWS
+ { SID_ATTR_TABSTOP, true }, // RES_PARATR_TABSTOP
+ { SID_ATTR_PARA_HYPHENZONE, true }, // RES_PARATR_HYPHENZONE
+ { FN_FORMAT_DROPCAPS, false }, // RES_PARATR_DROP
+ { SID_ATTR_PARA_REGISTER, true }, // RES_PARATR_REGISTER
+ { SID_ATTR_PARA_NUMRULE, true }, // RES_PARATR_NUMRULE
+ { SID_ATTR_PARA_SCRIPTSPACE, true }, // RES_PARATR_SCRIPTSPACE
+ { SID_ATTR_PARA_HANGPUNCTUATION, true },// RES_PARATR_HANGINGPUNCTUATION
+
+ { SID_ATTR_PARA_FORBIDDEN_RULES, true },// RES_PARATR_FORBIDDEN_RULES
+ { SID_PARA_VERTALIGN, true }, // RES_PARATR_VERTALIGN
+ { SID_ATTR_PARA_SNAPTOGRID, true }, // RES_PARATR_SNAPTOGRID
+ { SID_ATTR_BORDER_CONNECT, true }, // RES_PARATR_CONNECT_BORDER
+
+ { SID_ATTR_PARA_OUTLINE_LEVEL, true }, // RES_PARATR_OUTLINELEVEL //#outline level
+ { 0, true }, // RES_PARATR_RSID
+ { SID_ATTR_PARA_GRABBAG, true }, // RES_PARATR_GRABBAG
+ { 0, true }, // RES_PARATR_LIST_ID
+ { 0, true }, // RES_PARATR_LIST_LEVEL
+ { 0, true }, // RES_PARATR_LIST_ISRESTART
+ { 0, true }, // RES_PARATR_LIST_RESTARTVALUE
+ { 0, true }, // RES_PARATR_LIST_ISCOUNTED
+ { 0, true }, // RES_PARATR_LIST_AUTOFMT
+
+ { 0, true }, // RES_FILL_ORDER
+ { 0, true }, // RES_FRM_SIZE
+ { SID_ATTR_PAGE_PAPERBIN, true }, // RES_PAPER_BIN
+ { SID_ATTR_LRSPACE, true }, // RES_LR_SPACE
+ { SID_ATTR_ULSPACE, true }, // RES_UL_SPACE
+ { 0, false }, // RES_PAGEDESC
+ { SID_ATTR_PARA_PAGEBREAK, true }, // RES_BREAK
+ { 0, false }, // RES_CNTNT
+ { 0, true }, // RES_HEADER
+ { 0, true }, // RES_FOOTER
+ { 0, true }, // RES_PRINT
+ { FN_OPAQUE, true }, // RES_OPAQUE
+ { FN_SET_PROTECT, true }, // RES_PROTECT
+ { FN_SURROUND, true }, // RES_SURROUND
+ { FN_VERT_ORIENT, true }, // RES_VERT_ORIENT
+ { FN_HORI_ORIENT, true }, // RES_HORI_ORIENT
+ { 0, false }, // RES_ANCHOR
+ { SID_ATTR_BRUSH, true }, // RES_BACKGROUND
+ { SID_ATTR_BORDER_OUTER, true }, // RES_BOX
+ { SID_ATTR_BORDER_SHADOW, true }, // RES_SHADOW
+ { SID_ATTR_MACROITEM, true }, // RES_FRMMACRO
+ { FN_ATTR_COLUMNS, true }, // RES_COL
+ { SID_ATTR_PARA_KEEP, true }, // RES_KEEP
+ { 0, true }, // RES_URL
+ { 0, true }, // RES_EDIT_IN_READONLY
+
+ { 0, true }, // RES_LAYOUT_SPLIT
+ { 0, false }, // RES_CHAIN
+ { 0, true }, // RES_TEXTGRID
+ { FN_FORMAT_LINENUMBER, true }, // RES_LINENUMBER
+ { 0, true }, // RES_FTN_AT_TXTEND
+ { 0, true }, // RES_END_AT_TXTEND
+ { 0, true }, // RES_COLUMNBALANCE
+
+ { SID_ATTR_FRAMEDIRECTION, true }, // RES_FRAMEDIR
+
+ { SID_ATTR_HDFT_DYNAMIC_SPACING, true },// RES_HEADER_FOOTER_EAT_SPACING
+ { FN_TABLE_ROW_SPLIT, true }, // RES_ROW_SPLIT
+ // #i18732# - use slot-id define in svx
+ { SID_SW_FOLLOW_TEXT_FLOW, true }, // RES_FOLLOW_TEXT_FLOW
+ // #i29550#
+ { SID_SW_COLLAPSING_BORDERS, true }, // RES_COLLAPSING_BORDERS
+ // #i28701#
+ { SID_SW_WRAP_INFLUENCE_ON_OBJPOS, true },// RES_WRAP_INFLUENCE_ON_OBJPOS
+ { 0, false }, // RES_AUTO_STYLE
+ { 0, true }, // RES_FRMATR_STYLE_NAME
+ { 0, true }, // RES_FRMATR_CONDITIONAL_STYLE_NAME
+ { 0, true }, // RES_FRMATR_GRABBAG
+ { 0, true }, // RES_TEXT_VERT_ADJUST
+ { 0, true }, // RES_BACKGROUND_FULL_SIZE
+ { 0, true }, // RES_RTL_GUTTER
+
+ { 0, true }, // RES_GRFATR_MIRRORGRF
+ { SID_ATTR_GRAF_CROP, true }, // RES_GRFATR_CROPGRF
+ { 0, true }, // RES_GRFATR_ROTATION,
+ { 0, true }, // RES_GRFATR_LUMINANCE,
+ { 0, true }, // RES_GRFATR_CONTRAST,
+ { 0, true }, // RES_GRFATR_CHANNELR,
+ { 0, true }, // RES_GRFATR_CHANNELG,
+ { 0, true }, // RES_GRFATR_CHANNELB,
+ { 0, true }, // RES_GRFATR_GAMMA,
+ { 0, true }, // RES_GRFATR_INVERT,
+ { 0, true }, // RES_GRFATR_TRANSPARENCY,
+ { 0, true }, // RES_GRFATR_DUMMY1,
+ { 0, true }, // RES_GRFATR_DUMMY2,
+ { 0, true }, // RES_GRFATR_DUMMY3,
+ { 0, true }, // RES_GRFATR_DUMMY4,
+ { 0, true }, // RES_GRFATR_DUMMY5,
+ { 0, true }, // RES_GRFATR_DUMMY6,
+
+ { 0, true }, // RES_BOXATR_FORMAT
+ { 0, false }, // RES_BOXATR_FORMULA,
+ { 0, true }, // RES_BOXATR_VALUE
+
+ { 0, true } // RES_UNKNOWNATR_CONTAINER
+};
+
+std::vector<SvGlobalName> *pGlobalOLEExcludeList = nullptr;
+
+SwAutoCompleteWord* SwDoc::s_pAutoCompleteWords = nullptr;
+
+SwCheckIt* pCheckIt = nullptr;
+static CharClass* pAppCharClass = nullptr;
+
+static CollatorWrapper* pCollator = nullptr,
+ *pCaseCollator = nullptr;
+
+salhelper::SingletonRef<SwCalendarWrapper>* s_getCalendarWrapper()
+{
+ static salhelper::SingletonRef<SwCalendarWrapper> aCalendarWrapper;
+ return &aCalendarWrapper;
+}
+
+void InitCore()
+{
+ SfxPoolItem* pItem;
+
+ aAttrTab[ RES_CHRATR_CASEMAP- POOLATTR_BEGIN ] = new SvxCaseMapItem( SvxCaseMap::NotMapped, RES_CHRATR_CASEMAP);
+ aAttrTab[ RES_CHRATR_CHARSETCOLOR- POOLATTR_BEGIN ] = new SvxColorItem(RES_CHRATR_CHARSETCOLOR);
+ aAttrTab[ RES_CHRATR_COLOR- POOLATTR_BEGIN ] = new SvxColorItem(RES_CHRATR_COLOR);
+ aAttrTab[ RES_CHRATR_CONTOUR- POOLATTR_BEGIN ] = new SvxContourItem( false, RES_CHRATR_CONTOUR );
+ aAttrTab[ RES_CHRATR_CROSSEDOUT- POOLATTR_BEGIN ] = new SvxCrossedOutItem( STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT );
+ aAttrTab[ RES_CHRATR_ESCAPEMENT- POOLATTR_BEGIN ] = new SvxEscapementItem( RES_CHRATR_ESCAPEMENT );
+ aAttrTab[ RES_CHRATR_FONT- POOLATTR_BEGIN ] = new SvxFontItem( RES_CHRATR_FONT );
+
+ aAttrTab[ RES_CHRATR_FONTSIZE- POOLATTR_BEGIN ] = new SvxFontHeightItem( 240, 100, RES_CHRATR_FONTSIZE );
+ aAttrTab[ RES_CHRATR_KERNING- POOLATTR_BEGIN ] = new SvxKerningItem( 0, RES_CHRATR_KERNING );
+ aAttrTab[ RES_CHRATR_LANGUAGE- POOLATTR_BEGIN ] = new SvxLanguageItem(LANGUAGE_DONTKNOW, RES_CHRATR_LANGUAGE );
+ aAttrTab[ RES_CHRATR_POSTURE- POOLATTR_BEGIN ] = new SvxPostureItem( ITALIC_NONE, RES_CHRATR_POSTURE );
+ aAttrTab[ RES_CHRATR_UNUSED1- POOLATTR_BEGIN ] = new SfxVoidItem( RES_CHRATR_UNUSED1 );
+ aAttrTab[ RES_CHRATR_SHADOWED- POOLATTR_BEGIN ] = new SvxShadowedItem( false, RES_CHRATR_SHADOWED );
+ aAttrTab[ RES_CHRATR_UNDERLINE- POOLATTR_BEGIN ] = new SvxUnderlineItem( LINESTYLE_NONE, RES_CHRATR_UNDERLINE );
+ aAttrTab[ RES_CHRATR_WEIGHT- POOLATTR_BEGIN ] = new SvxWeightItem( WEIGHT_NORMAL, RES_CHRATR_WEIGHT );
+ aAttrTab[ RES_CHRATR_RSID - POOLATTR_BEGIN ] = new SvxRsidItem( 0, RES_CHRATR_RSID );
+ aAttrTab[ RES_CHRATR_WORDLINEMODE- POOLATTR_BEGIN ] = new SvxWordLineModeItem( false, RES_CHRATR_WORDLINEMODE );
+ aAttrTab[ RES_CHRATR_AUTOKERN- POOLATTR_BEGIN ] = new SvxAutoKernItem( false, RES_CHRATR_AUTOKERN );
+ aAttrTab[ RES_CHRATR_BLINK - POOLATTR_BEGIN ] = new SvxBlinkItem( false, RES_CHRATR_BLINK );
+ aAttrTab[ RES_CHRATR_NOHYPHEN - POOLATTR_BEGIN ] = new SvxNoHyphenItem( RES_CHRATR_NOHYPHEN );
+ aAttrTab[ RES_CHRATR_UNUSED2- POOLATTR_BEGIN ] = new SfxVoidItem( RES_CHRATR_UNUSED2 );
+ aAttrTab[ RES_CHRATR_BACKGROUND - POOLATTR_BEGIN ] = new SvxBrushItem( RES_CHRATR_BACKGROUND );
+
+ // CJK-Attributes
+ aAttrTab[ RES_CHRATR_CJK_FONT - POOLATTR_BEGIN ] = new SvxFontItem( RES_CHRATR_CJK_FONT );
+ aAttrTab[ RES_CHRATR_CJK_FONTSIZE - POOLATTR_BEGIN ] = new SvxFontHeightItem( 240, 100, RES_CHRATR_CJK_FONTSIZE );
+ aAttrTab[ RES_CHRATR_CJK_LANGUAGE - POOLATTR_BEGIN ] = new SvxLanguageItem(LANGUAGE_DONTKNOW, RES_CHRATR_CJK_LANGUAGE);
+ aAttrTab[ RES_CHRATR_CJK_POSTURE - POOLATTR_BEGIN ] = new SvxPostureItem(ITALIC_NONE, RES_CHRATR_CJK_POSTURE );
+ aAttrTab[ RES_CHRATR_CJK_WEIGHT - POOLATTR_BEGIN ] = new SvxWeightItem( WEIGHT_NORMAL, RES_CHRATR_CJK_WEIGHT );
+
+ // CTL-Attributes
+ aAttrTab[ RES_CHRATR_CTL_FONT - POOLATTR_BEGIN ] = new SvxFontItem( RES_CHRATR_CTL_FONT );
+ aAttrTab[ RES_CHRATR_CTL_FONTSIZE - POOLATTR_BEGIN ] = new SvxFontHeightItem( 240, 100, RES_CHRATR_CTL_FONTSIZE );
+ aAttrTab[ RES_CHRATR_CTL_LANGUAGE - POOLATTR_BEGIN ] = new SvxLanguageItem(LANGUAGE_DONTKNOW, RES_CHRATR_CTL_LANGUAGE);
+ aAttrTab[ RES_CHRATR_CTL_POSTURE - POOLATTR_BEGIN ] = new SvxPostureItem(ITALIC_NONE, RES_CHRATR_CTL_POSTURE );
+ aAttrTab[ RES_CHRATR_CTL_WEIGHT - POOLATTR_BEGIN ] = new SvxWeightItem( WEIGHT_NORMAL, RES_CHRATR_CTL_WEIGHT );
+
+ aAttrTab[ RES_CHRATR_ROTATE - POOLATTR_BEGIN ] = new SvxCharRotateItem( 0_deg10, false, RES_CHRATR_ROTATE );
+ aAttrTab[ RES_CHRATR_EMPHASIS_MARK - POOLATTR_BEGIN ] = new SvxEmphasisMarkItem( FontEmphasisMark::NONE, RES_CHRATR_EMPHASIS_MARK );
+ aAttrTab[ RES_CHRATR_TWO_LINES - POOLATTR_BEGIN ] = new SvxTwoLinesItem( false, 0, 0, RES_CHRATR_TWO_LINES );
+ aAttrTab[ RES_CHRATR_SCALEW - POOLATTR_BEGIN ] = new SvxCharScaleWidthItem( 100, RES_CHRATR_SCALEW );
+ aAttrTab[ RES_CHRATR_RELIEF - POOLATTR_BEGIN ] = new SvxCharReliefItem( FontRelief::NONE, RES_CHRATR_RELIEF );
+ aAttrTab[ RES_CHRATR_HIDDEN - POOLATTR_BEGIN ] = new SvxCharHiddenItem( false, RES_CHRATR_HIDDEN );
+ aAttrTab[ RES_CHRATR_OVERLINE- POOLATTR_BEGIN ] = new SvxOverlineItem( LINESTYLE_NONE, RES_CHRATR_OVERLINE );
+ aAttrTab[ RES_CHRATR_BOX - POOLATTR_BEGIN ] = new SvxBoxItem( RES_CHRATR_BOX );
+ aAttrTab[ RES_CHRATR_SHADOW - POOLATTR_BEGIN ] = new SvxShadowItem( RES_CHRATR_SHADOW );
+ aAttrTab[ RES_CHRATR_HIGHLIGHT - POOLATTR_BEGIN ] = new SvxBrushItem( RES_CHRATR_HIGHLIGHT );
+ aAttrTab[ RES_CHRATR_GRABBAG - POOLATTR_BEGIN ] = new SfxGrabBagItem( RES_CHRATR_GRABBAG );
+
+// CharacterAttr - MSWord weak char direction/script override emulation
+ aAttrTab[ RES_CHRATR_BIDIRTL - POOLATTR_BEGIN ] = new SfxInt16Item( RES_CHRATR_BIDIRTL, sal_Int16(-1) );
+ aAttrTab[ RES_CHRATR_IDCTHINT - POOLATTR_BEGIN ] = new SfxInt16Item( RES_CHRATR_IDCTHINT, sal_Int16(-1) );
+
+ aAttrTab[ RES_TXTATR_REFMARK - POOLATTR_BEGIN ] = new SwFormatRefMark( OUString() );
+ aAttrTab[ RES_TXTATR_TOXMARK - POOLATTR_BEGIN ] = new SwTOXMark;
+ aAttrTab[ RES_TXTATR_META - POOLATTR_BEGIN ] = SwFormatMeta::CreatePoolDefault(RES_TXTATR_META);
+ aAttrTab[ RES_TXTATR_METAFIELD - POOLATTR_BEGIN ] = SwFormatMeta::CreatePoolDefault(RES_TXTATR_METAFIELD);
+ aAttrTab[ RES_TXTATR_AUTOFMT- POOLATTR_BEGIN ] = new SwFormatAutoFormat;
+ aAttrTab[ RES_TXTATR_INETFMT - POOLATTR_BEGIN ] = new SwFormatINetFormat( OUString(), OUString() );
+ aAttrTab[ RES_TXTATR_CHARFMT- POOLATTR_BEGIN ] = new SwFormatCharFormat( nullptr );
+ aAttrTab[ RES_TXTATR_CJK_RUBY - POOLATTR_BEGIN ] = new SwFormatRuby( OUString() );
+ aAttrTab[ RES_TXTATR_UNKNOWN_CONTAINER - POOLATTR_BEGIN ] = new SvXMLAttrContainerItem( RES_TXTATR_UNKNOWN_CONTAINER );
+ aAttrTab[ RES_TXTATR_INPUTFIELD - POOLATTR_BEGIN ] = new SwFormatField( RES_TXTATR_INPUTFIELD );
+ aAttrTab[ RES_TXTATR_CONTENTCONTROL - POOLATTR_BEGIN ] = new SwFormatContentControl( RES_TXTATR_CONTENTCONTROL );
+
+ aAttrTab[ RES_TXTATR_FIELD- POOLATTR_BEGIN ] = new SwFormatField( RES_TXTATR_FIELD );
+ aAttrTab[ RES_TXTATR_FLYCNT - POOLATTR_BEGIN ] = new SwFormatFlyCnt( nullptr );
+ aAttrTab[ RES_TXTATR_FTN - POOLATTR_BEGIN ] = new SwFormatFootnote;
+ aAttrTab[ RES_TXTATR_ANNOTATION - POOLATTR_BEGIN ] = new SwFormatField( RES_TXTATR_ANNOTATION );
+ aAttrTab[RES_TXTATR_LINEBREAK - POOLATTR_BEGIN] = new SwFormatLineBreak(SwLineBreakClear::NONE);
+
+// TextAttr - Dummies
+ aAttrTab[ RES_TXTATR_DUMMY1 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_TXTATR_DUMMY1 );
+
+ aAttrTab[ RES_PARATR_LINESPACING- POOLATTR_BEGIN ] = new SvxLineSpacingItem( LINE_SPACE_DEFAULT_HEIGHT, RES_PARATR_LINESPACING );
+ aAttrTab[ RES_PARATR_ADJUST- POOLATTR_BEGIN ] = new SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST );
+ aAttrTab[ RES_PARATR_SPLIT- POOLATTR_BEGIN ] = new SvxFormatSplitItem( true, RES_PARATR_SPLIT );
+ aAttrTab[ RES_PARATR_WIDOWS- POOLATTR_BEGIN ] = new SvxWidowsItem( 0, RES_PARATR_WIDOWS );
+ aAttrTab[ RES_PARATR_ORPHANS- POOLATTR_BEGIN ] = new SvxOrphansItem( 0, RES_PARATR_ORPHANS );
+ aAttrTab[ RES_PARATR_TABSTOP- POOLATTR_BEGIN ] = new SvxTabStopItem( 1, SVX_TAB_DEFDIST, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+
+ pItem = new SvxHyphenZoneItem( false, RES_PARATR_HYPHENZONE );
+ static_cast<SvxHyphenZoneItem*>(pItem)->GetMaxHyphens() = 0; // Default: 0
+ aAttrTab[ RES_PARATR_HYPHENZONE- POOLATTR_BEGIN ] = pItem;
+
+ aAttrTab[ RES_PARATR_DROP- POOLATTR_BEGIN ] = new SwFormatDrop;
+ aAttrTab[ RES_PARATR_REGISTER - POOLATTR_BEGIN ] = new SwRegisterItem( false );
+ aAttrTab[ RES_PARATR_NUMRULE - POOLATTR_BEGIN ] = new SwNumRuleItem( OUString() );
+
+ aAttrTab[ RES_PARATR_SCRIPTSPACE - POOLATTR_BEGIN ] = new SvxScriptSpaceItem( true, RES_PARATR_SCRIPTSPACE );
+ aAttrTab[ RES_PARATR_HANGINGPUNCTUATION - POOLATTR_BEGIN ] = new SvxHangingPunctuationItem( true, RES_PARATR_HANGINGPUNCTUATION );
+ aAttrTab[ RES_PARATR_FORBIDDEN_RULES - POOLATTR_BEGIN ] = new SvxForbiddenRuleItem( true, RES_PARATR_FORBIDDEN_RULES );
+ aAttrTab[ RES_PARATR_VERTALIGN - POOLATTR_BEGIN ] = new SvxParaVertAlignItem( SvxParaVertAlignItem::Align::Automatic, RES_PARATR_VERTALIGN );
+ aAttrTab[ RES_PARATR_SNAPTOGRID - POOLATTR_BEGIN ] = new SvxParaGridItem( true, RES_PARATR_SNAPTOGRID );
+ aAttrTab[ RES_PARATR_CONNECT_BORDER - POOLATTR_BEGIN ] = new SwParaConnectBorderItem;
+
+ aAttrTab[ RES_PARATR_OUTLINELEVEL - POOLATTR_BEGIN ] = new SfxUInt16Item( RES_PARATR_OUTLINELEVEL, 0 );
+ aAttrTab[ RES_PARATR_RSID - POOLATTR_BEGIN ] = new SvxRsidItem( 0, RES_PARATR_RSID );
+ aAttrTab[ RES_PARATR_GRABBAG - POOLATTR_BEGIN ] = new SfxGrabBagItem( RES_PARATR_GRABBAG );
+
+ aAttrTab[ RES_PARATR_LIST_ID - POOLATTR_BEGIN ] = new SfxStringItem( RES_PARATR_LIST_ID, OUString() );
+ aAttrTab[ RES_PARATR_LIST_LEVEL - POOLATTR_BEGIN ] = new SfxInt16Item( RES_PARATR_LIST_LEVEL, 0 );
+ aAttrTab[ RES_PARATR_LIST_ISRESTART - POOLATTR_BEGIN ] = new SfxBoolItem( RES_PARATR_LIST_ISRESTART, false );
+ aAttrTab[ RES_PARATR_LIST_RESTARTVALUE - POOLATTR_BEGIN ] = new SfxInt16Item( RES_PARATR_LIST_RESTARTVALUE, 1 );
+ aAttrTab[ RES_PARATR_LIST_ISCOUNTED - POOLATTR_BEGIN ] = new SfxBoolItem( RES_PARATR_LIST_ISCOUNTED, true );
+ aAttrTab[ RES_PARATR_LIST_AUTOFMT - POOLATTR_BEGIN ] = new SwFormatAutoFormat(RES_PARATR_LIST_AUTOFMT);//new SfxSetItem(RES_PARATR_LIST_AUTOFMT, std::make_unique<SfxItemSet>(aCharAutoFormatSetRange));
+
+ aAttrTab[ RES_FILL_ORDER- POOLATTR_BEGIN ] = new SwFormatFillOrder;
+ aAttrTab[ RES_FRM_SIZE- POOLATTR_BEGIN ] = new SwFormatFrameSize;
+ aAttrTab[ RES_PAPER_BIN- POOLATTR_BEGIN ] = new SvxPaperBinItem( RES_PAPER_BIN );
+ aAttrTab[ RES_LR_SPACE- POOLATTR_BEGIN ] = new SvxLRSpaceItem( RES_LR_SPACE );
+ aAttrTab[ RES_UL_SPACE- POOLATTR_BEGIN ] = new SvxULSpaceItem( RES_UL_SPACE );
+ aAttrTab[ RES_PAGEDESC- POOLATTR_BEGIN ] = new SwFormatPageDesc;
+ aAttrTab[ RES_BREAK- POOLATTR_BEGIN ] = new SvxFormatBreakItem( SvxBreak::NONE, RES_BREAK);
+ aAttrTab[ RES_CNTNT- POOLATTR_BEGIN ] = new SwFormatContent;
+ aAttrTab[ RES_HEADER- POOLATTR_BEGIN ] = new SwFormatHeader;
+ aAttrTab[ RES_FOOTER- POOLATTR_BEGIN ] = new SwFormatFooter;
+ aAttrTab[ RES_PRINT- POOLATTR_BEGIN ] = new SvxPrintItem( RES_PRINT );
+ aAttrTab[ RES_OPAQUE- POOLATTR_BEGIN ] = new SvxOpaqueItem( RES_OPAQUE );
+ aAttrTab[ RES_PROTECT- POOLATTR_BEGIN ] = new SvxProtectItem( RES_PROTECT );
+ aAttrTab[ RES_SURROUND- POOLATTR_BEGIN ] = new SwFormatSurround;
+ aAttrTab[ RES_VERT_ORIENT- POOLATTR_BEGIN ] = new SwFormatVertOrient;
+ aAttrTab[ RES_HORI_ORIENT- POOLATTR_BEGIN ] = new SwFormatHoriOrient;
+ aAttrTab[ RES_ANCHOR- POOLATTR_BEGIN ] = new SwFormatAnchor;
+ aAttrTab[ RES_BACKGROUND- POOLATTR_BEGIN ] = new SvxBrushItem( RES_BACKGROUND );
+ aAttrTab[ RES_BOX- POOLATTR_BEGIN ] = new SvxBoxItem( RES_BOX );
+ aAttrTab[ RES_SHADOW- POOLATTR_BEGIN ] = new SvxShadowItem( RES_SHADOW );
+ aAttrTab[ RES_FRMMACRO- POOLATTR_BEGIN ] = new SvxMacroItem( RES_FRMMACRO );
+ aAttrTab[ RES_COL- POOLATTR_BEGIN ] = new SwFormatCol;
+ aAttrTab[ RES_KEEP - POOLATTR_BEGIN ] = new SvxFormatKeepItem( false, RES_KEEP );
+ aAttrTab[ RES_URL - POOLATTR_BEGIN ] = new SwFormatURL();
+ aAttrTab[ RES_EDIT_IN_READONLY - POOLATTR_BEGIN ] = new SwFormatEditInReadonly;
+ aAttrTab[ RES_LAYOUT_SPLIT - POOLATTR_BEGIN ] = new SwFormatLayoutSplit;
+ aAttrTab[ RES_CHAIN - POOLATTR_BEGIN ] = new SwFormatChain;
+ aAttrTab[ RES_TEXTGRID - POOLATTR_BEGIN ] = new SwTextGridItem;
+ aAttrTab[ RES_HEADER_FOOTER_EAT_SPACING - POOLATTR_BEGIN ] = new SwHeaderAndFooterEatSpacingItem;
+ aAttrTab[ RES_LINENUMBER - POOLATTR_BEGIN ] = new SwFormatLineNumber;
+ aAttrTab[ RES_FTN_AT_TXTEND - POOLATTR_BEGIN ] = new SwFormatFootnoteAtTextEnd;
+ aAttrTab[ RES_END_AT_TXTEND - POOLATTR_BEGIN ] = new SwFormatEndAtTextEnd;
+ aAttrTab[ RES_COLUMNBALANCE - POOLATTR_BEGIN ] = new SwFormatNoBalancedColumns;
+ aAttrTab[ RES_FRAMEDIR - POOLATTR_BEGIN ] = new SvxFrameDirectionItem( SvxFrameDirection::Environment, RES_FRAMEDIR );
+ aAttrTab[ RES_ROW_SPLIT - POOLATTR_BEGIN ] = new SwFormatRowSplit;
+
+ // #i18732#
+ aAttrTab[ RES_FOLLOW_TEXT_FLOW - POOLATTR_BEGIN ] = new SwFormatFollowTextFlow(false);
+ // collapsing borders #i29550#
+ aAttrTab[ RES_COLLAPSING_BORDERS - POOLATTR_BEGIN ] = new SfxBoolItem( RES_COLLAPSING_BORDERS, false );
+ // #i28701#
+ // #i35017# - constant name has changed
+ aAttrTab[ RES_WRAP_INFLUENCE_ON_OBJPOS - POOLATTR_BEGIN ] = new SwFormatWrapInfluenceOnObjPos( text::WrapInfluenceOnPosition::ONCE_CONCURRENT );
+
+ aAttrTab[ RES_AUTO_STYLE - POOLATTR_BEGIN ] = new SwFormatAutoFormat( RES_AUTO_STYLE );
+ aAttrTab[ RES_FRMATR_STYLE_NAME - POOLATTR_BEGIN ] = new SfxStringItem( RES_FRMATR_STYLE_NAME, OUString());
+ aAttrTab[ RES_FRMATR_CONDITIONAL_STYLE_NAME - POOLATTR_BEGIN ] = new SfxStringItem( RES_FRMATR_CONDITIONAL_STYLE_NAME, OUString() );
+ aAttrTab[ RES_FRMATR_GRABBAG - POOLATTR_BEGIN ] = new SfxGrabBagItem(RES_FRMATR_GRABBAG);
+ aAttrTab[ RES_TEXT_VERT_ADJUST - POOLATTR_BEGIN ] = new SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP,RES_TEXT_VERT_ADJUST);
+ aAttrTab[ RES_BACKGROUND_FULL_SIZE - POOLATTR_BEGIN ] = new SfxBoolItem(RES_BACKGROUND_FULL_SIZE, true);
+ aAttrTab[ RES_RTL_GUTTER - POOLATTR_BEGIN ] = new SfxBoolItem(RES_RTL_GUTTER, false);
+
+ aAttrTab[ RES_GRFATR_MIRRORGRF- POOLATTR_BEGIN ] = new SwMirrorGrf;
+ aAttrTab[ RES_GRFATR_CROPGRF- POOLATTR_BEGIN ] = new SwCropGrf;
+ aAttrTab[ RES_GRFATR_ROTATION - POOLATTR_BEGIN ] = new SwRotationGrf;
+ aAttrTab[ RES_GRFATR_LUMINANCE - POOLATTR_BEGIN ] = new SwLuminanceGrf;
+ aAttrTab[ RES_GRFATR_CONTRAST - POOLATTR_BEGIN ] = new SwContrastGrf;
+ aAttrTab[ RES_GRFATR_CHANNELR - POOLATTR_BEGIN ] = new SwChannelRGrf;
+ aAttrTab[ RES_GRFATR_CHANNELG - POOLATTR_BEGIN ] = new SwChannelGGrf;
+ aAttrTab[ RES_GRFATR_CHANNELB - POOLATTR_BEGIN ] = new SwChannelBGrf;
+ aAttrTab[ RES_GRFATR_GAMMA - POOLATTR_BEGIN ] = new SwGammaGrf;
+ aAttrTab[ RES_GRFATR_INVERT - POOLATTR_BEGIN ] = new SwInvertGrf;
+ aAttrTab[ RES_GRFATR_TRANSPARENCY - POOLATTR_BEGIN ] = new SwTransparencyGrf;
+ aAttrTab[ RES_GRFATR_DRAWMODE - POOLATTR_BEGIN ] = new SwDrawModeGrf;
+
+// GraphicAttr - Dummies
+ aAttrTab[ RES_GRFATR_DUMMY1 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_GRFATR_DUMMY1 );
+ aAttrTab[ RES_GRFATR_DUMMY2 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_GRFATR_DUMMY2 );
+ aAttrTab[ RES_GRFATR_DUMMY3 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_GRFATR_DUMMY3 );
+ aAttrTab[ RES_GRFATR_DUMMY4 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_GRFATR_DUMMY4 );
+ aAttrTab[ RES_GRFATR_DUMMY5 - POOLATTR_BEGIN ] = new SfxBoolItem( RES_GRFATR_DUMMY5 );
+
+ aAttrTab[ RES_BOXATR_FORMAT- POOLATTR_BEGIN ] = new SwTableBoxNumFormat;
+ aAttrTab[ RES_BOXATR_FORMULA- POOLATTR_BEGIN ] = new SwTableBoxFormula( OUString() );
+ aAttrTab[ RES_BOXATR_VALUE- POOLATTR_BEGIN ] = new SwTableBoxValue;
+
+ aAttrTab[ RES_UNKNOWNATR_CONTAINER- POOLATTR_BEGIN ] =
+ new SvXMLAttrContainerItem( RES_UNKNOWNATR_CONTAINER );
+
+ // get the correct fonts:
+ ::GetDefaultFonts( (aAttrTab[ RES_CHRATR_FONT- POOLATTR_BEGIN ])->StaticWhichCast(RES_CHRATR_FONT),
+ (aAttrTab[ RES_CHRATR_CJK_FONT - POOLATTR_BEGIN ])->StaticWhichCast(RES_CHRATR_CJK_FONT),
+ (aAttrTab[ RES_CHRATR_CTL_FONT - POOLATTR_BEGIN ])->StaticWhichCast(RES_CHRATR_CTL_FONT) );
+
+ SwBreakIt::Create_( ::comphelper::getProcessComponentContext() );
+ pCheckIt = nullptr;
+
+ FrameInit();
+ TextInit_();
+
+ SwSelPaintRects::s_pMapMode = new MapMode;
+ SwFntObj::s_pPixMap = new MapMode;
+
+ pGlobalOLEExcludeList = new std::vector<SvGlobalName>;
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ const SvxSwAutoFormatFlags& rAFlags = SvxAutoCorrCfg::Get().GetAutoCorrect()->GetSwFlags();
+ SwDoc::s_pAutoCompleteWords = new SwAutoCompleteWord( rAFlags.nAutoCmpltListLen,
+ rAFlags.nAutoCmpltWordLen );
+ }
+ else
+ {
+ SwDoc::s_pAutoCompleteWords = new SwAutoCompleteWord( 0, 0 );
+ }
+}
+
+void FinitCore()
+{
+ FrameFinit();
+ TextFinit();
+
+ sw::proofreadingiterator::dispose();
+ SwBreakIt::Delete_();
+ delete pCheckIt;
+ delete pAppCharClass;
+ delete pCollator;
+ delete pCaseCollator;
+
+ // destroy default TableAutoFormat
+ delete SwTableAutoFormat::s_pDefaultBoxAutoFormat;
+
+ delete SwSelPaintRects::s_pMapMode;
+ delete SwFntObj::s_pPixMap;
+
+ delete SwEditShell::s_pAutoFormatFlags;
+
+#if OSL_DEBUG_LEVEL > 0
+ // free defaults to prevent assertions
+ if ( aAttrTab[0]->GetRefCount() )
+ SfxItemPool::ReleaseDefaults( &aAttrTab );
+#endif
+ delete SwDoc::s_pAutoCompleteWords;
+
+ // delete all default attributes
+ for(SfxPoolItem* pHt : aAttrTab)
+ {
+ delete pHt;
+ }
+
+ delete pGlobalOLEExcludeList;
+}
+
+// returns the APP - CharClass instance - used for all ToUpper/ToLower/...
+CharClass& GetAppCharClass()
+{
+ if ( !pAppCharClass )
+ {
+ pAppCharClass = new CharClass(
+ ::comphelper::getProcessComponentContext(),
+ SwBreakIt::Get()->GetLanguageTag( GetAppLanguageTag() ));
+ }
+ return *pAppCharClass;
+}
+
+void SwCalendarWrapper::LoadDefaultCalendar( LanguageType eLang )
+{
+ if( eLang != m_nLang )
+ {
+ m_nLang = eLang;
+ loadDefaultCalendar( LanguageTag::convertToLocale( m_nLang ));
+ }
+}
+
+LanguageType GetAppLanguage()
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ return Application::GetSettings().GetLanguageTag().getLanguageType();
+ return LANGUAGE_ENGLISH_US;
+}
+
+const LanguageTag& GetAppLanguageTag()
+{
+ return Application::GetSettings().GetLanguageTag();
+}
+
+CollatorWrapper& GetAppCollator()
+{
+ if( !pCollator )
+ {
+ const lang::Locale& rLcl = g_pBreakIt->GetLocale( GetAppLanguage() );
+
+ pCollator = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
+ pCollator->loadDefaultCollator( rLcl, SW_COLLATOR_IGNORES );
+ }
+ return *pCollator;
+}
+
+CollatorWrapper& GetAppCaseCollator()
+{
+ if( !pCaseCollator )
+ {
+ const lang::Locale& rLcl = g_pBreakIt->GetLocale( GetAppLanguage() );
+
+ pCaseCollator = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
+ pCaseCollator->loadDefaultCollator( rLcl, 0 );
+ }
+ return *pCaseCollator;
+}
+
+namespace
+{
+ class TransWrp
+ {
+ private:
+ std::unique_ptr<utl::TransliterationWrapper> m_xTransWrp;
+ public:
+ TransWrp()
+ {
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ m_xTransWrp.reset(new ::utl::TransliterationWrapper( xContext,
+ TransliterationFlags::IGNORE_CASE |
+ TransliterationFlags::IGNORE_KANA |
+ TransliterationFlags::IGNORE_WIDTH ));
+
+ m_xTransWrp->loadModuleIfNeeded( GetAppLanguage() );
+ }
+ const ::utl::TransliterationWrapper& getTransliterationWrapper() const
+ {
+ return *m_xTransWrp;
+ }
+ };
+}
+
+const ::utl::TransliterationWrapper& GetAppCmpStrIgnore()
+{
+ static TransWrp theTransWrp;
+ return theTransWrp.getTransliterationWrapper();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/proofreadingiterator.cxx b/sw/source/core/bastyp/proofreadingiterator.cxx
new file mode 100644
index 000000000..64c0ac2b1
--- /dev/null
+++ b/sw/source/core/bastyp/proofreadingiterator.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/linguistic2/ProofreadingIterator.hpp>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <vcl/svapp.hxx>
+
+#include <proofreadingiterator.hxx>
+
+namespace
+{
+css::uno::Reference<css::linguistic2::XProofreadingIterator> instance;
+bool disposed = false;
+
+void doDispose(css::uno::Reference<css::linguistic2::XProofreadingIterator> const& inst)
+{
+ css::uno::Reference<css::lang::XComponent> comp(inst, css::uno::UNO_QUERY);
+ if (comp.is())
+ {
+ SolarMutexReleaser r;
+ comp->dispose();
+ }
+}
+}
+
+css::uno::Reference<css::linguistic2::XProofreadingIterator>
+sw::proofreadingiterator::get(css::uno::Reference<css::uno::XComponentContext> const& context)
+{
+ css::uno::Reference<css::linguistic2::XProofreadingIterator> inst(
+ css::linguistic2::ProofreadingIterator::create(context));
+ bool disp;
+ {
+ SolarMutexGuard g;
+ instance = inst;
+ disp = disposed;
+ }
+ if (disp)
+ {
+ doDispose(inst);
+ }
+ return inst;
+}
+
+void sw::proofreadingiterator::dispose()
+{
+ css::uno::Reference<css::linguistic2::XProofreadingIterator> inst;
+ {
+ SolarMutexGuard g;
+ inst = instance;
+ instance.clear();
+ disposed = true;
+ }
+ doDispose(inst);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/swcache.cxx b/sw/source/core/bastyp/swcache.cxx
new file mode 100644
index 000000000..ccf7522a2
--- /dev/null
+++ b/sw/source/core/bastyp/swcache.cxx
@@ -0,0 +1,509 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swcache.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <limits.h>
+
+#ifdef DBG_UTIL
+void SwCache::Check()
+{
+ if ( !m_pRealFirst )
+ {
+ assert(m_pFirst == nullptr && m_pLast == nullptr);
+ return;
+ }
+
+ // consistency check
+ assert(m_pLast->GetNext() == nullptr);
+ assert(m_pRealFirst->GetPrev() == nullptr);
+ sal_uInt16 nCnt = 0;
+ bool bFirstFound = false;
+ SwCacheObj *pObj = m_pRealFirst;
+ SwCacheObj *const pOldRealFirst = m_pRealFirst;
+ while ( pObj )
+ {
+ // the object must be found also when moving backwards
+ SwCacheObj *pTmp = m_pLast;
+ while ( pTmp && pTmp != pObj )
+ pTmp = pTmp->GetPrev();
+ assert(pTmp && "Object not found.");
+
+ ++nCnt;
+ if ( pObj == m_pFirst )
+ bFirstFound = true;
+ if ( !pObj->GetNext() )
+ {
+ assert(pObj == m_pLast);
+ }
+ pObj = pObj->GetNext();
+ assert(pObj != pOldRealFirst); (void) pOldRealFirst;
+ }
+ assert(bFirstFound);
+ SAL_WARN_IF( nCnt + m_aFreePositions.size() != size(), "sw.core", "Lost Chain." );
+ SAL_WARN_IF(
+ size() == m_nCurMax && m_nCurMax != m_aFreePositions.size() + nCnt, "sw.core",
+ "Lost FreePositions." );
+}
+
+#define INCREMENT( nVar ) ++nVar
+#define CHECK Check();
+
+#else
+#define INCREMENT( nVar )
+#define CHECK
+#endif
+
+SwCache::SwCache( const sal_uInt16 nInitSize
+#ifdef DBG_UTIL
+ , const OString &rNm
+#endif
+ ) :
+ m_pRealFirst( nullptr ),
+ m_pFirst( nullptr ),
+ m_pLast( nullptr ),
+ m_nCurMax( nInitSize )
+#ifdef DBG_UTIL
+ , m_aName( rNm )
+ , m_nAppend( 0 )
+ , m_nInsertFree( 0 )
+ , m_nReplace( 0 )
+ , m_nGetSuccess( 0 )
+ , m_nGetFail( 0 )
+ , m_nToTop( 0 )
+ , m_nDelete( 0 )
+ , m_nGetSeek( 0 )
+ , m_nAverageSeekCnt( 0 )
+ , m_nFlushCnt( 0 )
+ , m_nFlushedObjects( 0 )
+ , m_nIncreaseMax( 0 )
+ , m_nDecreaseMax( 0 )
+#endif
+{
+ m_aCacheObjects.reserve( nInitSize );
+}
+
+SwCache::~SwCache()
+{
+#ifdef DBG_UTIL
+ SAL_INFO(
+ "sw.core",
+ m_aName << "; number of new entries: " << m_nAppend
+ << "; number of insert on free places: " << m_nInsertFree
+ << "; number of replacements: " << m_nReplace
+ << "; number of successful Gets: " << m_nGetSuccess
+ << "; number of failed Gets: " << m_nGetFail
+ << "; number or reordering (LRU): " << m_nToTop
+ << "; number of suppressions: " << m_nDelete
+ << "; number of Gets without Index: " << m_nGetSeek
+ << "; number of Seek for Get without Index: " << m_nAverageSeekCnt
+ << "; number of Flush calls: " << m_nFlushCnt
+ << "; number of flushed objects: " << m_nFlushedObjects
+ << "; number of Cache expansions: " << m_nIncreaseMax
+ << "; number of Cache reductions: " << m_nDecreaseMax);
+ Check();
+#endif
+}
+
+void SwCache::IncreaseMax( const sal_uInt16 nAdd )
+{
+ if (o3tl::checked_add(m_nCurMax, nAdd, m_nCurMax))
+ {
+ std::abort();
+ }
+#ifdef DBG_UTIL
+ ++m_nIncreaseMax;
+#endif
+}
+
+void SwCache::DecreaseMax( const sal_uInt16 nSub )
+{
+ if ( m_nCurMax > nSub )
+ m_nCurMax = m_nCurMax - sal::static_int_cast< sal_uInt16 >(nSub);
+#ifdef DBG_UTIL
+ ++m_nDecreaseMax;
+#endif
+}
+
+void SwCache::Flush()
+{
+ INCREMENT( m_nFlushCnt );
+ SwCacheObj *pObj = m_pRealFirst;
+ m_pRealFirst = m_pFirst = m_pLast = nullptr;
+ SwCacheObj *pTmp;
+ while ( pObj )
+ {
+ assert(!pObj->IsLocked());
+ {
+ pTmp = pObj;
+ pObj = pTmp->GetNext();
+ m_aFreePositions.push_back( pTmp->GetCachePos() );
+ m_aCacheObjects[pTmp->GetCachePos()].reset(); // deletes pTmp
+ INCREMENT( m_nFlushedObjects );
+ }
+ }
+}
+
+void SwCache::ToTop( SwCacheObj *pObj )
+{
+ INCREMENT( m_nToTop );
+
+ // cut object out of chain and insert at beginning
+ if ( m_pRealFirst == pObj ) // pFirst was checked by caller
+ {
+ CHECK;
+ return;
+ }
+
+ if ( !m_pRealFirst )
+ {
+ // the first will be inserted
+ assert(!m_pFirst && !m_pLast);
+ m_pRealFirst = m_pFirst = m_pLast = pObj;
+ CHECK;
+ return;
+ }
+
+ assert(m_pFirst && m_pLast);
+
+ // cut
+ if ( pObj == m_pLast )
+ {
+ assert(pObj->GetPrev());
+ m_pLast = pObj->GetPrev();
+ m_pLast->SetNext( nullptr );
+ }
+ else
+ {
+ if ( pObj->GetNext() )
+ pObj->GetNext()->SetPrev( pObj->GetPrev() );
+ if ( pObj->GetPrev() )
+ pObj->GetPrev()->SetNext( pObj->GetNext() );
+ }
+
+ // paste at the (virtual) beginning
+ if ( m_pRealFirst == m_pFirst )
+ {
+ m_pRealFirst->SetPrev( pObj );
+ pObj->SetNext( m_pRealFirst );
+ pObj->SetPrev( nullptr );
+ m_pRealFirst = m_pFirst = pObj;
+ CHECK;
+ }
+ else
+ {
+ if ( m_pFirst->GetPrev() )
+ {
+ m_pFirst->GetPrev()->SetNext( pObj );
+ pObj->SetPrev( m_pFirst->GetPrev() );
+ }
+ else
+ pObj->SetPrev( nullptr );
+ m_pFirst->SetPrev( pObj );
+ pObj->SetNext( m_pFirst );
+ m_pFirst = pObj;
+ CHECK;
+ }
+}
+
+SwCacheObj *SwCache::Get( const void *pOwner, const sal_uInt16 nIndex,
+ const bool bToTop )
+{
+ SwCacheObj *pRet = (nIndex < m_aCacheObjects.size()) ? m_aCacheObjects[ nIndex ].get() : nullptr;
+ if ( pRet )
+ {
+ if ( !pRet->IsOwner( pOwner ) )
+ pRet = nullptr;
+ else if ( bToTop && pRet != m_pFirst )
+ ToTop( pRet );
+ }
+
+#ifdef DBG_UTIL
+ if ( pRet )
+ ++m_nGetSuccess;
+ else
+ ++m_nGetFail;
+#endif
+
+ return pRet;
+}
+
+SwCacheObj *SwCache::Get( const void *pOwner, const bool bToTop )
+{
+ SwCacheObj *pRet = m_pRealFirst;
+ while ( pRet && !pRet->IsOwner( pOwner ) )
+ {
+ INCREMENT( m_nAverageSeekCnt );
+ pRet = pRet->GetNext();
+ }
+
+ if ( bToTop && pRet && pRet != m_pFirst )
+ ToTop( pRet );
+
+#ifdef DBG_UTIL
+ if ( pRet )
+ ++m_nGetSuccess;
+ else
+ ++m_nGetFail;
+ ++m_nGetSeek;
+#endif
+ return pRet;
+}
+
+void SwCache::DeleteObj( SwCacheObj *pObj )
+{
+ CHECK;
+ OSL_ENSURE( !pObj->IsLocked(), "SwCache::Delete: object is locked." );
+ if ( pObj->IsLocked() )
+ return;
+
+ if ( m_pFirst == pObj )
+ {
+ if ( m_pFirst->GetNext() )
+ m_pFirst = m_pFirst->GetNext();
+ else
+ m_pFirst = m_pFirst->GetPrev();
+ }
+ if ( m_pRealFirst == pObj )
+ m_pRealFirst = m_pRealFirst->GetNext();
+ if ( m_pLast == pObj )
+ m_pLast = m_pLast->GetPrev();
+ if ( pObj->GetPrev() )
+ pObj->GetPrev()->SetNext( pObj->GetNext() );
+ if ( pObj->GetNext() )
+ pObj->GetNext()->SetPrev( pObj->GetPrev() );
+
+ m_aFreePositions.push_back( pObj->GetCachePos() );
+ assert(m_aCacheObjects[pObj->GetCachePos()].get() == pObj);
+ m_aCacheObjects[pObj->GetCachePos()] = nullptr; // deletes pObj
+
+ CHECK;
+ if ( m_aCacheObjects.size() > m_nCurMax &&
+ (m_nCurMax <= (m_aCacheObjects.size() - m_aFreePositions.size())) )
+ {
+ // Shrink if possible.To do so we need enough free positions.
+ // Unpleasant side effect: positions will be moved and the owner of
+ // these might not find them afterwards
+ for ( size_t i = 0; i < m_aCacheObjects.size(); ++i )
+ {
+ SwCacheObj *pTmpObj = m_aCacheObjects[i].get();
+ if ( !pTmpObj )
+ {
+ m_aCacheObjects.erase( m_aCacheObjects.begin() + i );
+ --i;
+ }
+ else
+ {
+ pTmpObj->SetCachePos( i );
+ }
+ }
+ m_aFreePositions.clear();
+ }
+ CHECK;
+}
+
+void SwCache::Delete(void const*const pOwner, sal_uInt16 const nIndex)
+{
+ INCREMENT( m_nDelete );
+ if (SwCacheObj *const pObj = Get(pOwner, nIndex, false))
+ DeleteObj(pObj);
+}
+
+void SwCache::Delete( const void *pOwner )
+{
+ INCREMENT( m_nDelete );
+ SwCacheObj *pObj = Get( pOwner, false );
+ if ( pObj )
+ DeleteObj( pObj );
+}
+
+bool SwCache::Insert(SwCacheObj *const pNew, bool const isDuplicateOwnerAllowed)
+{
+ CHECK;
+ OSL_ENSURE( !pNew->GetPrev() && !pNew->GetNext(), "New but not new." );
+ if (!isDuplicateOwnerAllowed)
+ {
+ for (auto const & rpObj : m_aCacheObjects)
+ { // check owner doesn't have a cache object yet; required for SwTextLine
+ assert(!rpObj || rpObj->GetOwner() != pNew->GetOwner());
+ (void) rpObj;
+ }
+ }
+
+ sal_uInt16 nPos;
+ if ( m_aCacheObjects.size() < m_nCurMax )
+ {
+ // there is still space; insert directly
+ INCREMENT( m_nAppend );
+ nPos = m_aCacheObjects.size();
+ m_aCacheObjects.emplace_back(pNew);
+ }
+ else if ( !m_aFreePositions.empty() )
+ {
+ // there are placeholders; use the last of those
+ INCREMENT( m_nInsertFree );
+ const sal_uInt16 nFreePos = m_aFreePositions.size() - 1;
+ nPos = m_aFreePositions[ nFreePos ];
+ m_aCacheObjects[nPos].reset(pNew);
+ m_aFreePositions.erase( m_aFreePositions.begin() + nFreePos );
+ }
+ else
+ {
+ INCREMENT( m_nReplace );
+ // the last of the LRU has to go
+ SwCacheObj *pObj = m_pLast;
+
+ while ( pObj && pObj->IsLocked() )
+ pObj = pObj->GetPrev();
+ if ( !pObj )
+ {
+ SAL_WARN("sw.core", "SwCache overflow.");
+ IncreaseMax(100); // embiggen & try again
+ return Insert(pNew, isDuplicateOwnerAllowed);
+ }
+
+ nPos = pObj->GetCachePos();
+ if ( pObj == m_pLast )
+ {
+ m_pLast = pObj->GetPrev();
+ assert(m_pLast); // must have capacity > 1
+ }
+ if (pObj == m_pFirst)
+ {
+ if (pObj->GetNext())
+ {
+ m_pFirst = pObj->GetNext();
+ }
+ else
+ {
+ m_pFirst = pObj->GetPrev();
+ }
+ assert(m_pFirst); // must have capacity > 1
+ }
+ if (pObj == m_pRealFirst)
+ {
+ m_pRealFirst = pObj->GetNext();
+ assert(m_pRealFirst); // must have capacity > 1
+ }
+ if (pObj->GetPrev())
+ {
+ pObj->GetPrev()->SetNext( pObj->GetNext() );
+ }
+ if (pObj->GetNext())
+ {
+ pObj->GetNext()->SetPrev( pObj->GetPrev() );
+ }
+ m_aCacheObjects[nPos].reset(pNew);
+ }
+ pNew->SetCachePos( nPos );
+
+ if ( m_pFirst )
+ {
+ if ( m_pFirst->GetPrev() )
+ { m_pFirst->GetPrev()->SetNext( pNew );
+ pNew->SetPrev( m_pFirst->GetPrev() );
+ }
+ m_pFirst->SetPrev( pNew );
+ pNew->SetNext( m_pFirst );
+ }
+ else
+ {
+ assert(!m_pLast);
+ m_pLast = pNew;
+ }
+ if ( m_pFirst == m_pRealFirst )
+ m_pRealFirst = pNew;
+ m_pFirst = pNew;
+
+ CHECK;
+ return true;
+}
+
+void SwCache::SetLRUOfst( const sal_uInt16 nOfst )
+{
+ assert(nOfst < m_nCurMax);
+ if ( !m_pRealFirst || ((m_aCacheObjects.size() - m_aFreePositions.size()) < nOfst) )
+ return;
+
+ CHECK;
+ m_pFirst = m_pRealFirst;
+ for ( sal_uInt16 i = 0; i < m_aCacheObjects.size() && i < nOfst; ++i )
+ {
+ if ( m_pFirst->GetNext() && m_pFirst->GetNext()->GetNext() )
+ m_pFirst = m_pFirst->GetNext();
+ else
+ break;
+ }
+ CHECK;
+}
+
+SwCacheObj::SwCacheObj( const void *pOwn ) :
+ m_pNext( nullptr ),
+ m_pPrev( nullptr ),
+ m_nCachePos( USHRT_MAX ),
+ m_nLock( 0 ),
+ m_pOwner( pOwn )
+{
+}
+
+SwCacheObj::~SwCacheObj()
+{
+}
+
+#ifdef DBG_UTIL
+void SwCacheObj::Lock()
+{
+ OSL_ENSURE( m_nLock < UCHAR_MAX, "Too many Locks for CacheObject." );
+ ++m_nLock;
+}
+
+void SwCacheObj::Unlock()
+{
+ OSL_ENSURE( m_nLock, "No more Locks available." );
+ --m_nLock;
+}
+#endif
+
+SwCacheAccess::~SwCacheAccess()
+{
+ if ( m_pObj )
+ m_pObj->Unlock();
+}
+
+void SwCacheAccess::Get_(bool const isDuplicateOwnerAllowed)
+{
+ OSL_ENSURE( !m_pObj, "SwCacheAcces Obj already available." );
+
+ m_pObj = NewObj();
+ if (!m_rCache.Insert(m_pObj, isDuplicateOwnerAllowed))
+ {
+ delete m_pObj;
+ m_pObj = nullptr;
+ }
+ else
+ {
+ m_pObj->Lock();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/swrect.cxx b/sw/source/core/bastyp/swrect.cxx
new file mode 100644
index 000000000..4a25b8b22
--- /dev/null
+++ b/sw/source/core/bastyp/swrect.cxx
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swrect.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#ifdef DBG_UTIL
+#include <tools/stream.hxx>
+#endif
+
+SwRect::SwRect( const tools::Rectangle &rRect ) :
+ m_Point( rRect.Left(), rRect.Top() )
+{
+ m_Size.setWidth( rRect.IsWidthEmpty() ? 0 : rRect.Right() - rRect.Left() + 1);
+ m_Size.setHeight(rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top() + 1);
+}
+
+SwRect& SwRect::Union( const SwRect& rRect )
+{
+ if( rRect.IsEmpty())
+ return *this;
+ if( IsEmpty())
+ {
+ *this = rRect;
+ return *this;
+ }
+ if ( Top() > rRect.Top() )
+ Top( rRect.Top() );
+ if ( Left() > rRect.Left() )
+ Left( rRect.Left() );
+ tools::Long n = rRect.Right();
+ if ( Right() < n )
+ Right( n );
+ n = rRect.Bottom();
+ if ( Bottom() < n )
+ Bottom( n );
+ return *this;
+}
+
+SwRect& SwRect::Intersection( const SwRect& rRect )
+{
+ // any similarity between me and given element?
+ if ( Overlaps( rRect ) )
+ {
+ // get smaller right and lower, and greater left and upper edge
+ if ( Left() < rRect.Left() )
+ Left( rRect.Left() );
+ if ( Top() < rRect.Top() )
+ Top( rRect.Top() );
+ tools::Long n = rRect.Right();
+ if ( Right() > n )
+ Right( n );
+ n = rRect.Bottom();
+ if ( Bottom() > n )
+ Bottom( n );
+ }
+ else
+ // Def.: if intersection is empty, set only SSize to 0
+ SSize(0, 0);
+
+ return *this;
+}
+
+SwRect& SwRect::Intersection_( const SwRect& rOther )
+{
+ // get smaller right and lower, and greater left and upper edge
+ auto left = std::max( m_Point.X(), rOther.m_Point.X() );
+ auto top = std::max( m_Point.Y(), rOther.m_Point.Y() );
+ tools::Long right = std::min( m_Point.X() + m_Size.Width(), rOther.m_Point.X() + rOther.m_Size.Width() );
+ auto bottom = std::min( m_Point.Y() + m_Size.Height(), rOther.m_Point.Y() + rOther.m_Size.Height() );
+
+ *this = SwRect( left, top, right - left, bottom - top );
+
+ return *this;
+}
+
+void SwRect::Justify()
+{
+ if ( m_Size.getHeight() < 0 )
+ {
+ m_Point.setY(m_Point.getY() + m_Size.getHeight() + 1);
+ m_Size.setHeight(-m_Size.getHeight());
+ }
+ if ( m_Size.getWidth() < 0 )
+ {
+ m_Point.setX(m_Point.getX() + m_Size.getWidth() + 1);
+ m_Size.setWidth(-m_Size.getWidth());
+ }
+}
+
+// Similar to the inline methods, but we need the function pointers
+void SwRect::Width_( const tools::Long nNew ) { m_Size.setWidth(nNew); }
+void SwRect::Height_( const tools::Long nNew ) { m_Size.setHeight(nNew); }
+void SwRect::Left_( const tools::Long nLeft ){ m_Size.AdjustWidth(m_Point.getX() - nLeft ); m_Point.setX(nLeft); }
+void SwRect::Right_( const tools::Long nRight ){ m_Size.setWidth(nRight - m_Point.getX()); }
+void SwRect::Top_( const tools::Long nTop ){ m_Size.AdjustHeight(m_Point.getY() - nTop ); m_Point.setY(nTop); }
+void SwRect::Bottom_( const tools::Long nBottom ){ m_Size.setHeight(nBottom - m_Point.getY()); }
+
+tools::Long SwRect::Width_() const{ return m_Size.getWidth(); }
+tools::Long SwRect::Height_() const{ return m_Size.getHeight(); }
+tools::Long SwRect::Left_() const{ return m_Point.getX(); }
+tools::Long SwRect::Right_() const{ return m_Point.getX() + m_Size.getWidth(); }
+tools::Long SwRect::Top_() const{ return m_Point.getY(); }
+tools::Long SwRect::Bottom_() const{ return m_Point.getY() + m_Size.getHeight(); }
+
+void SwRect::AddWidth( const tools::Long nAdd ) { m_Size.AdjustWidth(nAdd ); }
+void SwRect::AddHeight( const tools::Long nAdd ) { m_Size.AdjustHeight(nAdd ); }
+void SwRect::AddLeft( const tools::Long nAdd ){ m_Size.AdjustWidth(-nAdd ); m_Point.setX(m_Point.getX() + nAdd); }
+void SwRect::SubLeft( const tools::Long nSub ){ m_Size.AdjustWidth(nSub ); m_Point.setX(m_Point.getX() - nSub); }
+void SwRect::AddRight( const tools::Long nAdd ){ m_Size.AdjustWidth(nAdd ); }
+void SwRect::AddTop( const tools::Long nAdd ){ m_Size.AdjustHeight(-nAdd ); m_Point.setY(m_Point.getY() + nAdd); }
+void SwRect::SubTop( const tools::Long nSub ){ m_Size.AdjustHeight(nSub ); m_Point.setY(m_Point.getY() - nSub); }
+void SwRect::AddBottom( const tools::Long nAdd ){ m_Size.AdjustHeight(nAdd ); }
+void SwRect::SetPosX( const tools::Long nNew ){ m_Point.setX(nNew); }
+void SwRect::SetPosY( const tools::Long nNew ){ m_Point.setY(nNew); }
+
+Size SwRect::Size_() const { return SSize(); }
+Size SwRect::SwappedSize() const { return Size( m_Size.getHeight(), m_Size.getWidth() ); }
+
+tools::Long SwRect::GetLeftDistance( tools::Long nLimit ) const { return m_Point.getX() - nLimit; }
+tools::Long SwRect::GetBottomDistance( tools::Long nLim ) const { return nLim - m_Point.getY() - m_Size.getHeight();}
+tools::Long SwRect::GetTopDistance( tools::Long nLimit ) const { return m_Point.getY() - nLimit; }
+tools::Long SwRect::GetRightDistance( tools::Long nLim ) const { return nLim - m_Point.getX() - m_Size.getWidth(); }
+
+bool SwRect::OverStepLeft( tools::Long nLimit ) const
+ { return nLimit > m_Point.getX() && m_Point.getX() + m_Size.getWidth() > nLimit; }
+bool SwRect::OverStepBottom( tools::Long nLimit ) const
+ { return nLimit > m_Point.getY() && m_Point.getY() + m_Size.getHeight() > nLimit; }
+bool SwRect::OverStepTop( tools::Long nLimit ) const
+ { return nLimit > m_Point.getY() && m_Point.getY() + m_Size.getHeight() > nLimit; }
+bool SwRect::OverStepRight( tools::Long nLimit ) const
+ { return nLimit > m_Point.getX() && m_Point.getX() + m_Size.getWidth() > nLimit; }
+
+void SwRect::SetLeftAndWidth( tools::Long nLeft, tools::Long nNew )
+{
+ m_Point.setX(nLeft);
+ m_Size.setWidth(nNew);
+}
+void SwRect::SetTopAndHeight( tools::Long nTop, tools::Long nNew )
+{
+ m_Point.setY(nTop);
+ m_Size.setHeight(nNew);
+}
+void SwRect::SetRightAndWidth( tools::Long nRight, tools::Long nNew )
+{
+ m_Point.setX(nRight - nNew);
+ m_Size.setWidth(nNew);
+}
+void SwRect::SetBottomAndHeight( tools::Long nBottom, tools::Long nNew )
+{
+ m_Point.setY(nBottom - nNew);
+ m_Size.setHeight(nNew);
+}
+void SwRect::SetUpperLeftCorner( const Point& rNew )
+ { m_Point = rNew; }
+void SwRect::SetUpperRightCorner( const Point& rNew )
+ { m_Point = Point(rNew.X() - m_Size.getWidth(), rNew.Y()); }
+void SwRect::SetLowerLeftCorner( const Point& rNew )
+ { m_Point = Point(rNew.X(), rNew.Y() - m_Size.getHeight()); }
+
+void SwRect::dumpAsXmlAttributes(xmlTextWriterPtr writer) const
+{
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("left"), "%li", Left());
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("top"), "%li", Top());
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("width"), "%li", Width());
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("height"), "%li", Height());
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("bottom"), "%li", Bottom());
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("right"), "%li", Right());
+}
+
+#ifdef DBG_UTIL
+SvStream& WriteSwRect(SvStream &rStream, const SwRect &rRect)
+{
+ rStream.WriteChar('[').WriteInt32(rRect.Top()).
+ WriteChar('/').WriteInt32(rRect.Left()).
+ WriteChar(',').WriteInt32(rRect.Width()).
+ WriteChar('x').WriteInt32(rRect.Height()).
+ WriteCharPtr("] ");
+ return rStream;
+}
+#endif
+
+static_assert( std::is_trivially_copyable< SwRect >::value );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/swregion.cxx b/sw/source/core/bastyp/swregion.cxx
new file mode 100644
index 000000000..b790bf147
--- /dev/null
+++ b/sw/source/core/bastyp/swregion.cxx
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swrect.hxx>
+#include <swregion.hxx>
+#include <swtypes.hxx>
+
+SwRegionRects::SwRegionRects( const SwRect &rStartRect, sal_uInt16 nInit ) :
+ m_aOrigin( rStartRect )
+{
+ reserve(nInit);
+ push_back( m_aOrigin );
+}
+
+SwRegionRects::SwRegionRects( sal_uInt16 nInit ) :
+ m_aOrigin()
+{
+ reserve(nInit);
+}
+
+// If <rDel> is true then this Rect will be overwritten by <rRect> at
+// position <nPos>. Otherwise <rRect> is attached at the end.
+inline void SwRegionRects::InsertRect( const SwRect &rRect,
+ const sal_uInt16 nPos, bool &rDel )
+{
+ if( rDel )
+ {
+ (*this)[nPos] = rRect;
+ rDel = false;
+ }
+ else
+ {
+ push_back( rRect );
+ }
+}
+
+void SwRegionRects::operator+=( const SwRect &rRect )
+{
+ push_back( rRect );
+}
+
+/** Delete all overlaps of the Rects in array with the given <rRect>
+
+ To do so, all existing rectangles have to be either split or deleted.
+
+ @param rRect rectangle with the area that should be deleted
+*/
+void SwRegionRects::operator-=( const SwRect &rRect )
+{
+ sal_uInt16 nMax = size();
+ for ( sal_uInt16 i = 0; i < nMax; ++i )
+ {
+ if ( rRect.Overlaps( (*this)[i] ) )
+ {
+ SwRect aTmp( (*this)[i] );
+ SwRect aInter( aTmp );
+ aInter.Intersection_( rRect );
+
+ // The first Rect that should be inserted takes position of i.
+ // This avoids one Delete() call.
+ bool bDel = true;
+
+ // now split; only those rectangles should be left over that are in
+ // the "old" but not in the "new" area; hence, not in intersection.
+ tools::Long nTmp = aInter.Top() - aTmp.Top();
+ if ( 0 < nTmp )
+ {
+ const tools::Long nOldVal = aTmp.Height();
+ aTmp.Height(nTmp);
+ InsertRect( aTmp, i, bDel );
+ aTmp.Height( nOldVal );
+ }
+
+ aTmp.Top( aInter.Top() + aInter.Height() );
+ if ( aTmp.Height() > 0 )
+ InsertRect( aTmp, i, bDel );
+
+ aTmp.Top( aInter.Top() );
+ aTmp.Bottom( aInter.Bottom() );
+ nTmp = aInter.Left() - aTmp.Left();
+ if ( 0 < nTmp )
+ {
+ const tools::Long nOldVal = aTmp.Width();
+ aTmp.Width( nTmp );
+ InsertRect( aTmp, i, bDel );
+ aTmp.Width( nOldVal );
+ }
+
+ aTmp.Left( aInter.Left() + aInter.Width() ); //+1?
+ if ( aTmp.Width() > 0 )
+ InsertRect( aTmp, i, bDel );
+
+ if( bDel )
+ {
+ erase( begin() + i );
+ --i; // so that we don't forget any
+ --nMax; // so that we don't check too much
+ }
+ }
+ }
+}
+
+/** invert current rectangle
+
+ Change the shape, such that holes with be areas and areas are holes now.
+
+ Note: If no rects were removed, then the shape is identical to the original
+ shape. As a result, it will be a NULL-SRectangle after inverting.
+*/
+void SwRegionRects::Invert()
+{
+ // not very elegant and fast, but efficient:
+ // Create a new region and remove all areas that are left over. Afterwards
+ // copy all values.
+
+ // To avoid unnecessary memory requirements, create a "useful" initial size:
+ // Number of rectangles in this area * 2 + 2 for the special case of a
+ // single hole (so four Rects in the inverse case).
+ SwRegionRects aInvRegion( m_aOrigin, size()*2+2 );
+ for( const_iterator it = begin(); it != end(); ++it )
+ aInvRegion -= *it;
+
+ // overwrite all existing
+ swap( aInvRegion );
+}
+
+static inline SwTwips CalcArea( const SwRect &rRect )
+{
+ return rRect.Width() * rRect.Height();
+}
+
+void SwRegionRects::LimitToOrigin()
+{
+ for (size_type i = 0; i < size(); ++i )
+ (*this)[ i ].Intersection( m_aOrigin );
+}
+
+// combine all adjacent rectangles
+void SwRegionRects::Compress( CompressType type )
+{
+ bool bAgain;
+ do
+ {
+ sort( begin(), end(), []( const SwRect& l, const SwRect& r ) { return l.Top() < r.Top(); } );
+ bAgain = false;
+ bool bRemoved = false;
+ for (size_type i = 0; i < size(); ++i )
+ {
+ if( (*this)[i].IsEmpty())
+ continue;
+ // Rectangles are sorted by Y axis, so check only pairs of rectangles
+ // that are possibly overlapping or adjacent or close enough to be grouped by the fuzzy
+ // code below.
+ const tools::Long nFuzzy = type == CompressFuzzy ? 1361513 : 0;
+ const tools::Long yMax = (*this)[i].Top() + (*this)[i].Height() + nFuzzy
+ / std::max<tools::Long>( 1, (*this)[i].Width());
+ for(size_type j = i+1; j < size(); ++j)
+ {
+ if( (*this)[j].IsEmpty())
+ continue;
+ if( (*this)[j].Top() > yMax )
+ break;
+ // If one rectangle contains a second completely than the latter
+ // does not need to be stored and can be deleted
+ else if ( (*this)[i].Contains( (*this)[j] ) )
+ {
+ (*this)[j].Width(0); // = erase(), see below
+ bRemoved = true;
+ }
+ else if ( (*this)[j].Contains( (*this)[i] ) )
+ {
+ (*this)[i] = (*this)[j];
+ (*this)[j].Width(0);
+ bRemoved = true;
+ bAgain = true;
+ }
+ else
+ {
+ // Merge adjacent rectangles (possibly overlapping), such rectangles can be
+ // detected by their merged areas being equal to the area of the union
+ // (which is obviously the case if they share one side, and using
+ // the nFuzzy extra allows merging also rectangles that do not quite cover
+ // the entire union but it's close enough).
+
+ // For combining as much as possible (and for having less single
+ // paints), the area of the union can be a little bit larger:
+ // ( 9622 * 141.5 = 1361513 ~= a quarter (1/4) centimeter wider
+ // than the width of an A4 page
+ SwRect aUnion = (*this)[i].GetUnion( (*this)[j] );
+ SwRect aInter = (*this)[i].GetIntersection( (*this)[j] );
+ if ( CalcArea( (*this)[i] ) + CalcArea( (*this)[j] ) - CalcArea( aInter )
+ + nFuzzy >= CalcArea( aUnion ) )
+ {
+ (*this)[i] = aUnion;
+ (*this)[j].Width(0);
+ bRemoved = true;
+ bAgain = true;
+ }
+ }
+ }
+ }
+ // Instead of repeated erase() we Width(0) the elements, and now erase
+ // all empty elements just once.
+ if( bRemoved )
+ resize( std::remove_if(begin(), end(), [](const SwRect& rect) { return rect.IsEmpty(); }) - begin());
+ // Code paths setting bAgain alter elements of the vector, possibly breaking
+ // the Y-axis optimization, so run another pass just to make sure. The adjacent-rects
+ // merging code may possibly benefit from a repeated pass also if two pairs of merged
+ // rects might get merged again and this pass skipped that.
+ } while(bAgain);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/swtypes.cxx b/sw/source/core/bastyp/swtypes.cxx
new file mode 100644
index 000000000..507804363
--- /dev/null
+++ b/sw/source/core/bastyp/swtypes.cxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swtypes.hxx>
+
+#include <editeng/unolingu.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/graph.hxx>
+
+using namespace com::sun::star;
+
+Size GetGraphicSizeTwip(const Graphic& rGraphic, vcl::RenderContext* pOutDev)
+{
+ const MapMode aMapTwip(MapUnit::MapTwip);
+ Size aSize(rGraphic.GetPrefSize());
+
+ if (!aSize.getWidth() && !aSize.getHeight())
+ {
+ const_cast<Graphic&>(rGraphic).makeAvailable();
+ aSize = rGraphic.GetPrefSize();
+ }
+
+ if (MapUnit::MapPixel == rGraphic.GetPrefMapMode().GetMapUnit())
+ {
+ if (!pOutDev)
+ pOutDev = Application::GetDefaultDevice();
+ aSize = pOutDev->PixelToLogic(aSize, aMapTwip);
+ }
+ else
+ {
+ aSize = OutputDevice::LogicToLogic(aSize, rGraphic.GetPrefMapMode(), aMapTwip);
+ }
+ return aSize;
+}
+
+uno::Reference<linguistic2::XSpellChecker1> GetSpellChecker()
+{
+ return LinguMgr::GetSpellChecker();
+}
+
+uno::Reference<linguistic2::XHyphenator> GetHyphenator() { return LinguMgr::GetHyphenator(); }
+
+uno::Reference<linguistic2::XThesaurus> GetThesaurus() { return LinguMgr::GetThesaurus(); }
+
+uno::Reference<linguistic2::XLinguProperties> GetLinguPropertySet()
+{
+ return LinguMgr::GetLinguPropertySet();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/bastyp/tabcol.cxx b/sw/source/core/bastyp/tabcol.cxx
new file mode 100644
index 000000000..4efc9d8ef
--- /dev/null
+++ b/sw/source/core/bastyp/tabcol.cxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tabcol.hxx>
+#include <limits.h>
+
+SwTabCols::SwTabCols( sal_uInt16 nSize ) :
+ m_nLeftMin( 0 ),
+ m_nLeft( 0 ),
+ m_nRight( 0 ),
+ m_nRightMax( 0 ),
+ m_bLastRowAllowedToChange( true )
+{
+ if ( nSize )
+ m_aData.reserve( nSize );
+}
+
+SwTabCols::SwTabCols( const SwTabCols& rCpy ) :
+ m_nLeftMin( rCpy.GetLeftMin() ),
+ m_nLeft( rCpy.GetLeft() ),
+ m_nRight( rCpy.GetRight() ),
+ m_nRightMax( rCpy.GetRightMax() ),
+ m_bLastRowAllowedToChange( rCpy.IsLastRowAllowedToChange() ),
+ m_aData( rCpy.GetData() )
+{
+}
+
+SwTabCols &SwTabCols::operator=( const SwTabCols& rCpy )
+{
+ m_nLeftMin = rCpy.GetLeftMin();
+ m_nLeft = rCpy.GetLeft();
+ m_nRight = rCpy.GetRight();
+ m_nRightMax= rCpy.GetRightMax();
+ m_bLastRowAllowedToChange = rCpy.IsLastRowAllowedToChange();
+
+ Remove( 0, Count() );
+ m_aData = rCpy.GetData();
+
+ return *this;
+}
+
+void SwTabCols::Insert( tools::Long nValue, tools::Long nMin, tools::Long nMax,
+ bool bValue, size_t nPos )
+{
+ SwTabColsEntry aEntry;
+ aEntry.nPos = nValue;
+ aEntry.nMin = nMin;
+ aEntry.nMax = nMax;
+ aEntry.bHidden = bValue;
+ m_aData.insert( m_aData.begin() + nPos, aEntry );
+}
+
+void SwTabCols::Insert( tools::Long nValue, bool bValue, size_t nPos )
+{
+ SwTabColsEntry aEntry;
+ aEntry.nPos = nValue;
+ aEntry.nMin = 0;
+ aEntry.nMax = LONG_MAX;
+ aEntry.bHidden = bValue;
+ m_aData.insert( m_aData.begin() + nPos, aEntry );
+}
+
+void SwTabCols::Remove( size_t nPos, size_t nCount )
+{
+ SwTabColsEntries::iterator aStart = m_aData.begin() + nPos;
+ m_aData.erase( aStart, aStart + nCount );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/BlockCursor.cxx b/sw/source/core/crsr/BlockCursor.cxx
new file mode 100644
index 000000000..0dd0f5dff
--- /dev/null
+++ b/sw/source/core/crsr/BlockCursor.cxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <viscrs.hxx>
+#include "BlockCursor.hxx"
+
+SwBlockCursor::~SwBlockCursor() {}
+
+SwShellCursor& SwBlockCursor::getShellCursor() { return maCursor; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/BlockCursor.hxx b/sw/source/core/crsr/BlockCursor.hxx
new file mode 100644
index 000000000..28b09e0e8
--- /dev/null
+++ b/sw/source/core/crsr/BlockCursor.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_CRSR_BLOCKCURSOR_HXX
+#define INCLUDED_SW_SOURCE_CORE_CRSR_BLOCKCURSOR_HXX
+
+#include <optional>
+#include <tools/gen.hxx>
+
+#include <viscrs.hxx>
+
+class SwCursorShell;
+struct SwPosition;
+
+ /** Access to the block cursor
+
+ A block cursor contains a SwShellCursor and additional information about
+ the rectangle which has been created by pressing the mouse button and
+ moving the mouse.
+
+ It's simply an aggregation of a SwShellCursor and a rectangle defined by
+ a start and an end point.
+*/
+class SwBlockCursor
+{
+ SwShellCursor maCursor;
+ std::optional<Point> maStartPt;
+ std::optional<Point> maEndPt;
+
+public:
+ SwBlockCursor( const SwCursorShell& rCursorSh, const SwPosition &rPos ) :
+ maCursor( rCursorSh, rPos ) {}
+ /** Access to the shell cursor
+
+ @return SwShellCursor& which represents the start and end position of the
+ current block selection
+ */
+ SwShellCursor& getShellCursor();
+ /** Defines the starting vertex of the block selection
+
+ @param rPt
+ rPt should contain the document coordinates of the mouse cursor when
+ the block selection starts (MouseButtonDown)
+ */
+ void setStartPoint( const Point &rPt ) { maStartPt = rPt; }
+ /** Defines the ending vertex of the block selection
+
+ @param rPt
+ rPt should contain the document coordinates of the mouse cursor when
+ the block selection has started and the mouse has been moved (MouseMove)
+ */
+ void setEndPoint( const Point &rPt ) { maEndPt = rPt; }
+ /** The document coordinates where the block selection has been started
+
+ @return 0, if no start point has been set
+ */
+ std::optional<Point> const & getStartPoint() const { return maStartPt; }
+ /** The document coordinates where the block selection ends (at the moment)
+
+ @return 0, if no end point has been set
+ */
+ std::optional<Point> const & getEndPoint() const { return maEndPt; }
+ /** Deletion of the mouse created rectangle
+
+ When start and end points exist, the block cursor depends on this. If the
+ cursor is moved by cursor keys (e.g. up/down, home/end) the mouse rectangle
+ is obsolete and has to be deleted.
+ */
+ void clearPoints() { maStartPt.reset(); maEndPt.reset(); }
+ ~SwBlockCursor();
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/DateFormFieldButton.cxx b/sw/source/core/crsr/DateFormFieldButton.cxx
new file mode 100644
index 000000000..b16845a63
--- /dev/null
+++ b/sw/source/core/crsr/DateFormFieldButton.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <DateFormFieldButton.hxx>
+#include <edtwin.hxx>
+#include <bookmark.hxx>
+#include <tools/date.hxx>
+#include <svl/numformat.hxx>
+#include <vcl/svapp.hxx>
+
+IMPL_LINK(DateFormFieldButton, ImplSelectHdl, weld::Calendar&, rCalendar, void)
+{
+ if (m_pDateFieldmark)
+ {
+ const Date& rNullDate = m_pNumberFormatter->GetNullDate();
+ double dDate = rCalendar.get_date() - rNullDate;
+ m_pDateFieldmark->SetCurrentDate(dDate);
+ }
+ m_xFieldPopup->popdown();
+}
+
+DateFormFieldButton::DateFormFieldButton(SwEditWin* pEditWin, sw::mark::DateFieldmark& rFieldmark,
+ SvNumberFormatter* pNumberFormatter)
+ : FormFieldButton(pEditWin, rFieldmark)
+ , m_pNumberFormatter(pNumberFormatter)
+ , m_pDateFieldmark(dynamic_cast<sw::mark::DateFieldmark*>(&m_rFieldmark))
+{
+}
+
+DateFormFieldButton::~DateFormFieldButton() { disposeOnce(); }
+
+void DateFormFieldButton::LaunchPopup()
+{
+ m_xFieldPopupBuilder
+ = Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/calendar.ui");
+ m_xFieldPopup = m_xFieldPopupBuilder->weld_popover("Calendar");
+ m_xCalendar = m_xFieldPopupBuilder->weld_calendar("date");
+ if (m_pDateFieldmark)
+ {
+ std::pair<bool, double> aResult = m_pDateFieldmark->GetCurrentDate();
+ if (aResult.first)
+ {
+ const Date& rNullDate = m_pNumberFormatter->GetNullDate();
+ m_xCalendar->set_date(rNullDate + sal_Int32(aResult.second));
+ }
+ }
+ m_xCalendar->connect_activated(LINK(this, DateFormFieldButton, ImplSelectHdl));
+ FormFieldButton::LaunchPopup();
+ m_xCalendar->grab_focus();
+}
+
+void DateFormFieldButton::DestroyPopup()
+{
+ m_xCalendar.reset();
+ FormFieldButton::DestroyPopup();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/crsr/DropDownFormFieldButton.cxx b/sw/source/core/crsr/DropDownFormFieldButton.cxx
new file mode 100644
index 000000000..eb06bcc5a
--- /dev/null
+++ b/sw/source/core/crsr/DropDownFormFieldButton.cxx
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <DropDownFormFieldButton.hxx>
+#include <edtwin.hxx>
+#include <bookmark.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <xmloff/odffields.hxx>
+#include <IMark.hxx>
+#include <view.hxx>
+#include <docsh.hxx>
+#include <strings.hrc>
+
+/**
+ * Popup dialog for drop-down form field showing the list items of the field.
+ * The user can select the item using this popup while filling in a form.
+ */
+
+void DropDownFormFieldButton::InitDropdown()
+{
+ const sw::mark::IFieldmark::parameter_map_t* const pParameters = m_rFieldmark.GetParameters();
+
+ sw::mark::IFieldmark::parameter_map_t::const_iterator pListEntries
+ = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
+ css::uno::Sequence<OUString> vListEntries;
+ if (pListEntries != pParameters->end())
+ {
+ pListEntries->second >>= vListEntries;
+ for (OUString const& i : std::as_const(vListEntries))
+ m_xTreeView->append_text(i);
+ }
+
+ if (!vListEntries.hasElements())
+ {
+ m_xTreeView->append_text(SwResId(STR_DROP_DOWN_EMPTY_LIST));
+ }
+
+ // Select the current one
+ sw::mark::IFieldmark::parameter_map_t::const_iterator pResult
+ = pParameters->find(ODF_FORMDROPDOWN_RESULT);
+ if (pResult != pParameters->end())
+ {
+ sal_Int32 nSelection = -1;
+ pResult->second >>= nSelection;
+ m_xTreeView->set_cursor(nSelection);
+ m_xTreeView->select(nSelection);
+ }
+
+ auto nHeight = m_xTreeView->get_height_rows(
+ std::min<int>(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount(),
+ m_xTreeView->n_children()));
+ m_xTreeView->set_size_request(-1, nHeight);
+ Size lbSize(m_xTreeView->get_preferred_size());
+ lbSize.AdjustWidth(4);
+ lbSize.AdjustHeight(4);
+ auto nMinListWidth = GetSizePixel().Width();
+ lbSize.setWidth(std::max(lbSize.Width(), nMinListWidth));
+ m_xTreeView->set_size_request(lbSize.Width(), lbSize.Height());
+}
+
+IMPL_LINK(DropDownFormFieldButton, MyListBoxHandler, weld::TreeView&, rBox, bool)
+{
+ OUString sSelection = rBox.get_selected_text();
+ if (sSelection == SwResId(STR_DROP_DOWN_EMPTY_LIST))
+ {
+ m_xFieldPopup->popdown();
+ return true;
+ }
+
+ sal_Int32 nSelection = rBox.get_selected_index();
+ if (nSelection >= 0)
+ {
+ (*m_rFieldmark.GetParameters())[ODF_FORMDROPDOWN_RESULT] <<= nSelection;
+ m_rFieldmark.Invalidate();
+ SwView& rView = static_cast<SwEditWin*>(GetParent())->GetView();
+ rView.GetDocShell()->SetModified();
+ }
+
+ m_xFieldPopup->popdown();
+
+ return true;
+}
+
+DropDownFormFieldButton::DropDownFormFieldButton(SwEditWin* pEditWin,
+ sw::mark::DropDownFieldmark& rFieldmark)
+ : FormFieldButton(pEditWin, rFieldmark)
+{
+}
+
+DropDownFormFieldButton::~DropDownFormFieldButton() { disposeOnce(); }
+
+void DropDownFormFieldButton::LaunchPopup()
+{
+ m_xFieldPopupBuilder
+ = Application::CreateBuilder(GetFrameWeld(), "modules/swriter/ui/formdropdown.ui");
+ m_xFieldPopup = m_xFieldPopupBuilder->weld_popover("FormDropDown");
+ m_xTreeView = m_xFieldPopupBuilder->weld_tree_view("list");
+ InitDropdown();
+ m_xTreeView->connect_row_activated(LINK(this, DropDownFormFieldButton, MyListBoxHandler));
+ FormFieldButton::LaunchPopup();
+ m_xTreeView->grab_focus();
+}
+
+void DropDownFormFieldButton::DestroyPopup()
+{
+ m_xTreeView.reset();
+ FormFieldButton::DestroyPopup();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/crsr/FormFieldButton.cxx b/sw/source/core/crsr/FormFieldButton.cxx
new file mode 100644
index 000000000..43534de20
--- /dev/null
+++ b/sw/source/core/crsr/FormFieldButton.cxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <DropDownFormFieldButton.hxx>
+#include <edtwin.hxx>
+#include <basegfx/color/bcolortools.hxx>
+#include <bookmark.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/event.hxx>
+
+FormFieldButton::FormFieldButton(SwEditWin* pEditWin, sw::mark::Fieldmark& rFieldmark)
+ : Control(pEditWin, WB_DIALOGCONTROL)
+ , m_rFieldmark(rFieldmark)
+{
+ assert(GetParent());
+ assert(dynamic_cast<SwEditWin*>(GetParent()));
+
+ SetBackground();
+ EnableChildTransparentMode();
+ SetParentClipMode(ParentClipMode::NoClip);
+ SetPaintTransparent(true);
+}
+
+FormFieldButton::~FormFieldButton() { disposeOnce(); }
+
+void FormFieldButton::LaunchPopup()
+{
+ m_xFieldPopup->connect_closed(LINK(this, DropDownFormFieldButton, FieldPopupModeEndHdl));
+
+ tools::Rectangle aRect(Point(0, 0), GetSizePixel());
+ weld::Window* pParent = weld::GetPopupParent(*this, aRect);
+ m_xFieldPopup->popup_at_rect(pParent, aRect);
+}
+
+void FormFieldButton::DestroyPopup()
+{
+ m_xFieldPopup.reset();
+ m_xFieldPopupBuilder.reset();
+}
+
+void FormFieldButton::dispose()
+{
+ DestroyPopup();
+ Control::dispose();
+}
+
+void FormFieldButton::CalcPosAndSize(const SwRect& rPortionPaintArea)
+{
+ assert(GetParent());
+
+ Point aBoxPos = GetParent()->LogicToPixel(rPortionPaintArea.Pos());
+ Size aBoxSize = GetParent()->LogicToPixel(rPortionPaintArea.SSize());
+
+ // First calculate the size of the frame around the field
+ int nPadding = aBoxSize.Height() / 4;
+ aBoxPos.AdjustX(-nPadding);
+ aBoxPos.AdjustY(-nPadding);
+ aBoxSize.AdjustWidth(2 * nPadding);
+ aBoxSize.AdjustHeight(2 * nPadding);
+
+ m_aFieldFramePixel = tools::Rectangle(aBoxPos, aBoxSize);
+
+ // Then extend the size with the button area
+ aBoxSize.AdjustWidth(GetParent()->LogicToPixel(rPortionPaintArea.SSize()).Height());
+
+ if (aBoxPos != GetPosPixel() || aBoxSize != GetSizePixel())
+ {
+ SetPosSizePixel(aBoxPos, aBoxSize);
+ Invalidate();
+ }
+}
+
+void FormFieldButton::MouseButtonDown(const MouseEvent&)
+{
+ LaunchPopup();
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(FormFieldButton, FieldPopupModeEndHdl, weld::Popover&, void)
+{
+ DestroyPopup();
+ m_rFieldmark.Invalidate();
+ // Hide the button here and make it visible later, to make transparent background work with SAL_USE_VCLPLUGIN=gen
+ Show(false);
+ Invalidate();
+}
+
+static basegfx::BColor lcl_GetFillColor(const basegfx::BColor& rLineColor, double aLuminance)
+{
+ basegfx::BColor aHslLine = basegfx::utils::rgb2hsl(rLineColor);
+ aHslLine.setZ(aLuminance);
+ return basegfx::utils::hsl2rgb(aHslLine);
+}
+
+void FormFieldButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ SetMapMode(MapMode(MapUnit::MapPixel));
+
+ //const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ Color aLineColor = COL_BLACK;
+ Color aFillColor(lcl_GetFillColor(aLineColor.getBColor(), (m_xFieldPopup ? 0.5 : 0.75)));
+
+ // Draw the frame around the field
+ // GTK3 backend cuts down the frame's top and left border, to avoid that add a padding around the frame
+ int nPadding = 1;
+ Point aPos(nPadding, nPadding);
+ Size aSize(m_aFieldFramePixel.GetSize().Width() - nPadding,
+ m_aFieldFramePixel.GetSize().Height() - nPadding);
+ const tools::Rectangle aFrameRect(tools::Rectangle(aPos, aSize));
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(COL_TRANSPARENT);
+ rRenderContext.DrawRect(aFrameRect);
+
+ // Draw the button next to the frame
+ Point aButtonPos(aFrameRect.TopLeft());
+ aButtonPos.AdjustX(aFrameRect.GetSize().getWidth() - 1);
+ Size aButtonSize(aFrameRect.GetSize());
+ aButtonSize.setWidth(GetSizePixel().getWidth() - aFrameRect.getWidth() - nPadding);
+ const tools::Rectangle aButtonRect(tools::Rectangle(aButtonPos, aButtonSize));
+
+ // Background & border
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(aFillColor);
+ rRenderContext.DrawRect(aButtonRect);
+
+ // the arrowhead
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(aLineColor);
+
+ Point aCenter(aButtonPos.X() + (aButtonSize.Width() / 2),
+ aButtonPos.Y() + (aButtonSize.Height() / 2));
+ Size aArrowSize(aButtonSize.Width() / 4, aButtonSize.Height() / 10);
+
+ tools::Polygon aPoly(3);
+ aPoly.SetPoint(Point(aCenter.X() - aArrowSize.Width(), aCenter.Y() - aArrowSize.Height()), 0);
+ aPoly.SetPoint(Point(aCenter.X() + aArrowSize.Width(), aCenter.Y() - aArrowSize.Height()), 1);
+ aPoly.SetPoint(Point(aCenter.X(), aCenter.Y() + aArrowSize.Height()), 2);
+ rRenderContext.DrawPolygon(aPoly);
+}
+
+WindowHitTest FormFieldButton::ImplHitTest(const Point& rFramePos)
+{
+ // We need to check whether the position hits the button (the frame should be mouse transparent)
+ WindowHitTest aResult = Control::ImplHitTest(rFramePos);
+ if (aResult != WindowHitTest::Inside)
+ return aResult;
+ else
+ {
+ return rFramePos.X() >= m_aFieldFramePixel.Right() ? WindowHitTest::Inside
+ : WindowHitTest::Transparent;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/crsr/annotationmark.cxx b/sw/source/core/crsr/annotationmark.cxx
new file mode 100644
index 000000000..ce7c03675
--- /dev/null
+++ b/sw/source/core/crsr/annotationmark.cxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <algorithm>
+#include <annotationmark.hxx>
+
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <docufld.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <UndoBookmark.hxx>
+#include <ndtxt.hxx>
+#include <txtfld.hxx>
+
+namespace sw::mark
+{
+ AnnotationMark::AnnotationMark(
+ const SwPaM& rPaM,
+ const OUString& rName )
+ : MarkBase( rPaM, rName )
+ {
+ if ( rName.getLength() == 0 )
+ {
+ SetName( MarkBase::GenerateNewName(u"__Annotation__") );
+ }
+ }
+
+ AnnotationMark::~AnnotationMark()
+ {
+ }
+
+ void AnnotationMark::InitDoc(SwDoc& io_rDoc,
+ sw::mark::InsertMode const, SwPosition const*const)
+ {
+ SwTextNode *pTextNode = GetMarkEnd().nNode.GetNode().GetTextNode();
+ assert(pTextNode);
+ SwTextField *const pTextField = pTextNode->GetFieldTextAttrAt(
+ GetMarkEnd().nContent.GetIndex()-1, true);
+ assert(pTextField != nullptr);
+ auto pPostItField
+ = dynamic_cast<const SwPostItField*>(pTextField->GetFormatField().GetField());
+ assert(pPostItField);
+ // use the annotation mark's name as the annotation name, if
+ // - the annotation field has an empty annotation name or
+ // - the annotation mark's name differs (on mark creation a name clash had been detected)
+ if ( pPostItField->GetName().isEmpty()
+ || pPostItField->GetName() != GetName() )
+ {
+ const_cast<SwPostItField*>(pPostItField)->SetName( GetName() );
+ }
+
+ if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ io_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsBookmark>(*this) );
+ }
+ io_rDoc.getIDocumentState().SetModified();
+ }
+
+ const SwFormatField* AnnotationMark::GetAnnotationFormatField() const
+ {
+ SwDoc& rDoc = GetMarkPos().GetDoc();
+
+ const auto sName = GetName();
+ SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::Postit, OUString(), false );
+ std::vector<SwFormatField*> vFields;
+ pType->GatherFields(vFields);
+ auto ppFound = std::find_if(vFields.begin(), vFields.end(), [&sName](SwFormatField* pF)
+ {
+ auto pPF = dynamic_cast<const SwPostItField*>(pF->GetField());
+ return pPF && pPF->GetName() == sName;
+ });
+ return ppFound != vFields.end() ? *ppFound : nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/bookmark.cxx b/sw/source/core/crsr/bookmark.cxx
new file mode 100644
index 000000000..af2dc2bad
--- /dev/null
+++ b/sw/source/core/crsr/bookmark.cxx
@@ -0,0 +1,1037 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <bookmark.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentState.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <swserv.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/viewsh.hxx>
+#include <UndoBookmark.hxx>
+#include <unobookmark.hxx>
+#include <xmloff/odffields.hxx>
+#include <libxml/xmlwriter.h>
+#include <comphelper/random.hxx>
+#include <comphelper/anytostring.hxx>
+#include <sal/log.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <edtwin.hxx>
+#include <DateFormFieldButton.hxx>
+#include <DropDownFormFieldButton.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <comphelper/lok.hxx>
+#include <txtfrm.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <rtl/strbuf.hxx>
+#include <strings.hrc>
+
+using namespace ::sw::mark;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace sw::mark
+{
+
+ SwPosition FindFieldSep(IFieldmark const& rMark)
+ {
+ SwPosition const& rStartPos(rMark.GetMarkStart());
+ SwPosition const& rEndPos(rMark.GetMarkEnd());
+ SwNodes const& rNodes(rStartPos.nNode.GetNodes());
+ SwNodeOffset const nStartNode(rStartPos.nNode.GetIndex());
+ SwNodeOffset const nEndNode(rEndPos.nNode.GetIndex());
+ int nFields(0);
+ std::optional<SwPosition> ret;
+ for (SwNodeOffset n = nEndNode; nStartNode <= n; --n)
+ {
+ SwNode *const pNode(rNodes[n]);
+ if (pNode->IsTextNode())
+ {
+ SwTextNode & rTextNode(*pNode->GetTextNode());
+ sal_Int32 const nStart(n == nStartNode
+ ? rStartPos.nContent.GetIndex() + 1
+ : 0);
+ sal_Int32 const nEnd(n == nEndNode
+ // subtract 1 to ignore the end char
+ ? rEndPos.nContent.GetIndex() - 1
+ : rTextNode.Len());
+ for (sal_Int32 i = nEnd; nStart < i; --i)
+ {
+ const sal_Unicode c(rTextNode.GetText()[i - 1]);
+ switch (c)
+ {
+ case CH_TXT_ATR_FIELDSTART:
+ --nFields;
+ assert(0 <= nFields);
+ break;
+ case CH_TXT_ATR_FIELDEND:
+ ++nFields;
+ // fields in field result could happen by manual
+ // editing, although the field update deletes them
+ break;
+ case CH_TXT_ATR_FIELDSEP:
+ if (nFields == 0)
+ {
+ assert(!ret); // one per field
+ ret = SwPosition(rTextNode, i - 1);
+#ifndef DBG_UTIL
+ return *ret;
+#endif
+ }
+ break;
+ }
+ }
+ }
+ else if (pNode->IsEndNode() && !pNode->StartOfSectionNode()->IsSectionNode())
+ {
+ assert(nStartNode <= pNode->StartOfSectionIndex());
+ // fieldmark cannot overlap node section, unless it's a section
+ n = pNode->StartOfSectionIndex();
+ }
+ else
+ {
+ assert(pNode->IsNoTextNode() || pNode->IsSectionNode()
+ || (pNode->IsEndNode() && pNode->StartOfSectionNode()->IsSectionNode()));
+ }
+ }
+ assert(ret); // must have found it
+ return *ret;
+ }
+} // namespace sw::mark
+
+namespace
+{
+ void lcl_FixPosition(SwPosition& rPos)
+ {
+ // make sure the position has 1) the proper node, and 2) a proper index
+ SwTextNode* pTextNode = rPos.nNode.GetNode().GetTextNode();
+ if(pTextNode == nullptr && rPos.nContent.GetIndex() > 0)
+ {
+ SAL_INFO(
+ "sw.core",
+ "illegal position: " << rPos.nContent.GetIndex()
+ << " without proper TextNode");
+ rPos.nContent.Assign(nullptr, 0);
+ }
+ else if(pTextNode != nullptr && rPos.nContent.GetIndex() > pTextNode->Len())
+ {
+ SAL_INFO(
+ "sw.core",
+ "illegal position: " << rPos.nContent.GetIndex()
+ << " is beyond " << pTextNode->Len());
+ rPos.nContent.Assign(pTextNode, pTextNode->Len());
+ }
+ }
+
+ void lcl_AssertFieldMarksSet(const Fieldmark& rField,
+ const sal_Unicode aStartMark,
+ const sal_Unicode aEndMark)
+ {
+ if (aEndMark != CH_TXT_ATR_FORMELEMENT)
+ {
+ SwPosition const& rStart(rField.GetMarkStart());
+ assert(rStart.nNode.GetNode().GetTextNode()->GetText()[rStart.nContent.GetIndex()] == aStartMark); (void) rStart; (void) aStartMark;
+ SwPosition const sepPos(sw::mark::FindFieldSep(rField));
+ assert(sepPos.nNode.GetNode().GetTextNode()->GetText()[sepPos.nContent.GetIndex()] == CH_TXT_ATR_FIELDSEP); (void) sepPos;
+ }
+ else
+ { // must be m_pPos1 < m_pPos2 because of asymmetric SplitNode update
+ assert(rField.GetMarkPos().nContent.GetIndex() + 1 == rField.GetOtherMarkPos().nContent.GetIndex());
+ }
+ SwPosition const& rEnd(rField.GetMarkEnd());
+ assert(rEnd.nNode.GetNode().GetTextNode()->GetText()[rEnd.nContent.GetIndex() - 1] == aEndMark); (void) rEnd;
+ }
+
+ void lcl_SetFieldMarks(Fieldmark& rField,
+ SwDoc& io_rDoc,
+ const sal_Unicode aStartMark,
+ const sal_Unicode aEndMark,
+ SwPosition const*const pSepPos)
+ {
+ io_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr);
+ OUString startChar(aStartMark);
+ if (aEndMark != CH_TXT_ATR_FORMELEMENT
+ && rField.GetMarkStart() == rField.GetMarkEnd())
+ {
+ // do only 1 InsertString call - to expand existing bookmarks at the
+ // position over the whole field instead of just aStartMark
+ startChar += OUStringChar(CH_TXT_ATR_FIELDSEP) + OUStringChar(aEndMark);
+ }
+
+ SwPosition start = rField.GetMarkStart();
+ if (aEndMark != CH_TXT_ATR_FORMELEMENT)
+ {
+ SwPaM aStartPaM(start);
+ io_rDoc.getIDocumentContentOperations().InsertString(aStartPaM, startChar);
+ start.nContent -= startChar.getLength(); // restore, it was moved by InsertString
+ // do not manipulate via reference directly but call SetMarkStartPos
+ // which works even if start and end pos were the same
+ rField.SetMarkStartPos( start );
+ SwPosition& rEnd = rField.GetMarkEnd(); // note: retrieve after
+ // setting start, because if start==end it can go stale, see SetMarkPos()
+ assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd));
+ if (startChar.getLength() == 1)
+ {
+ *aStartPaM.GetPoint() = pSepPos ? *pSepPos : rEnd;
+ io_rDoc.getIDocumentContentOperations().InsertString(aStartPaM, OUString(CH_TXT_ATR_FIELDSEP));
+ if (!pSepPos || rEnd < *pSepPos)
+ { // rEnd is not moved automatically if it's same as insert pos
+ ++rEnd.nContent;
+ }
+ }
+ assert(pSepPos == nullptr || (start < *pSepPos && *pSepPos <= rEnd));
+ }
+ else
+ {
+ assert(pSepPos == nullptr);
+ }
+
+ SwPosition& rEnd = rField.GetMarkEnd();
+ if (aEndMark && startChar.getLength() == 1)
+ {
+ SwPaM aEndPaM(rEnd);
+ io_rDoc.getIDocumentContentOperations().InsertString(aEndPaM, OUString(aEndMark));
+ if (aEndMark != CH_TXT_ATR_FORMELEMENT)
+ {
+ ++rEnd.nContent; // InsertString didn't move non-empty mark
+ }
+ else
+ { // InsertString moved the mark's end, not its start
+ assert(rField.GetMarkPos().nContent.GetIndex() + 1 == rField.GetOtherMarkPos().nContent.GetIndex());
+ }
+ }
+ lcl_AssertFieldMarksSet(rField, aStartMark, aEndMark);
+
+ io_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr);
+ }
+
+ void lcl_RemoveFieldMarks(const Fieldmark& rField,
+ SwDoc& io_rDoc,
+ const sal_Unicode aStartMark,
+ const sal_Unicode aEndMark)
+ {
+ io_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::UI_REPLACE, nullptr);
+
+ const SwPosition& rStart = rField.GetMarkStart();
+ SwTextNode const*const pStartTextNode = rStart.nNode.GetNode().GetTextNode();
+ assert(pStartTextNode);
+ if (aEndMark != CH_TXT_ATR_FORMELEMENT)
+ {
+ (void) pStartTextNode;
+ // check this before start / end because of the +1 / -1 ...
+ SwPosition const sepPos(sw::mark::FindFieldSep(rField));
+ io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(rStart, aStartMark);
+ io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(sepPos, CH_TXT_ATR_FIELDSEP);
+ }
+
+ const SwPosition& rEnd = rField.GetMarkEnd();
+ SwTextNode *const pEndTextNode = rEnd.nNode.GetNode().GetTextNode();
+ assert(pEndTextNode);
+ const sal_Int32 nEndPos = (rEnd == rStart)
+ ? rEnd.nContent.GetIndex()
+ : rEnd.nContent.GetIndex() - 1;
+ assert(pEndTextNode->GetText()[nEndPos] == aEndMark);
+ SwPosition const aEnd(*pEndTextNode, nEndPos);
+ io_rDoc.GetDocumentContentOperationsManager().DeleteDummyChar(aEnd, aEndMark);
+
+ io_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::UI_REPLACE, nullptr);
+ }
+
+ auto InvalidatePosition(SwPosition const& rPos) -> void
+ {
+ SwUpdateAttr const aHint(rPos.nContent.GetIndex(), rPos.nContent.GetIndex(), 0);
+ rPos.nNode.GetNode().GetTextNode()->CallSwClientNotify(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+}
+
+namespace sw::mark
+{
+ MarkBase::MarkBase(const SwPaM& aPaM,
+ const OUString& rName)
+ : m_pPos1(new SwPosition(*(aPaM.GetPoint())))
+ , m_aName(rName)
+ {
+ m_pPos1->nContent.SetMark(this);
+ lcl_FixPosition(*m_pPos1);
+ if (aPaM.HasMark() && (*aPaM.GetMark() != *aPaM.GetPoint()))
+ {
+ MarkBase::SetOtherMarkPos(*(aPaM.GetMark()));
+ lcl_FixPosition(*m_pPos2);
+ }
+ }
+
+ // For fieldmarks, the CH_TXT_ATR_FIELDSTART and CH_TXT_ATR_FIELDEND
+ // themselves are part of the covered range. This is guaranteed by
+ // TextFieldmark::InitDoc/lcl_AssureFieldMarksSet.
+ bool MarkBase::IsCoveringPosition(const SwPosition& rPos) const
+ {
+ return GetMarkStart() <= rPos && rPos < GetMarkEnd();
+ }
+
+ void MarkBase::SetMarkPos(const SwPosition& rNewPos)
+ {
+ std::make_unique<SwPosition>(rNewPos).swap(m_pPos1);
+ m_pPos1->nContent.SetMark(this);
+ }
+
+ void MarkBase::SetOtherMarkPos(const SwPosition& rNewPos)
+ {
+ std::make_unique<SwPosition>(rNewPos).swap(m_pPos2);
+ m_pPos2->nContent.SetMark(this);
+ }
+
+ OUString MarkBase::ToString( ) const
+ {
+ return "Mark: ( Name, [ Node1, Index1 ] ): ( " + m_aName + ", [ "
+ + OUString::number( sal_Int32(GetMarkPos().nNode.GetIndex()) ) + ", "
+ + OUString::number( GetMarkPos().nContent.GetIndex( ) ) + " ] )";
+ }
+
+ void MarkBase::dumpAsXml(xmlTextWriterPtr pWriter) const
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkBase"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(m_aName.toUtf8().getStr()));
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("markPos"));
+ GetMarkPos().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ if (IsExpanded())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("otherMarkPos"));
+ GetOtherMarkPos().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ MarkBase::~MarkBase()
+ { }
+
+ OUString MarkBase::GenerateNewName(std::u16string_view rPrefix)
+ {
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ if (bHack)
+ {
+ static sal_Int64 nIdCounter = SAL_CONST_INT64(6000000000);
+ return rPrefix + OUString::number(nIdCounter++);
+ }
+ else
+ {
+ static OUString sUniquePostfix;
+ static sal_Int32 nCount = SAL_MAX_INT32;
+ if(nCount == SAL_MAX_INT32)
+ {
+ unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
+ std::numeric_limits<unsigned int>::max()));
+ sUniquePostfix = "_" + OUString::number(n);
+ nCount = 0;
+ }
+ // putting the counter in front of the random parts will speed up string comparisons
+ return rPrefix + OUString::number(nCount++) + sUniquePostfix;
+ }
+ }
+
+ void MarkBase::SwClientNotify(const SwModify&, const SfxHint& rHint)
+ {
+ CallSwClientNotify(rHint);
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
+ { // invalidate cached uno object
+ SetXBookmark(uno::Reference<text::XTextContent>(nullptr));
+ }
+ }
+
+ auto MarkBase::InvalidateFrames() -> void
+ {
+ }
+
+ NavigatorReminder::NavigatorReminder(const SwPaM& rPaM)
+ : MarkBase(rPaM, MarkBase::GenerateNewName(u"__NavigatorReminder__"))
+ { }
+
+ UnoMark::UnoMark(const SwPaM& aPaM)
+ : MarkBase(aPaM, MarkBase::GenerateNewName(u"__UnoMark__"))
+ { }
+
+ DdeBookmark::DdeBookmark(const SwPaM& aPaM)
+ : MarkBase(aPaM, MarkBase::GenerateNewName(u"__DdeLink__"))
+ { }
+
+ void DdeBookmark::SetRefObject(SwServerObject* pObj)
+ {
+ m_aRefObj = pObj;
+ }
+
+ void DdeBookmark::DeregisterFromDoc(SwDoc& rDoc)
+ {
+ if(m_aRefObj.is())
+ rDoc.getIDocumentLinksAdministration().GetLinkManager().RemoveServer(m_aRefObj.get());
+ }
+
+ DdeBookmark::~DdeBookmark()
+ {
+ if( m_aRefObj.is() )
+ {
+ if(m_aRefObj->HasDataLinks())
+ {
+ ::sfx2::SvLinkSource* p = m_aRefObj.get();
+ p->SendDataChanged();
+ }
+ m_aRefObj->SetNoServer();
+ }
+ }
+
+ Bookmark::Bookmark(const SwPaM& aPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName)
+ : DdeBookmark(aPaM)
+ , m_aCode(rCode)
+ , m_bHidden(false)
+ {
+ m_aName = rName;
+ }
+
+ void Bookmark::InitDoc(SwDoc& io_rDoc,
+ sw::mark::InsertMode const, SwPosition const*const)
+ {
+ if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ io_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsBookmark>(*this));
+ }
+ io_rDoc.getIDocumentState().SetModified();
+ InvalidateFrames();
+ }
+
+ void Bookmark::DeregisterFromDoc(SwDoc& io_rDoc)
+ {
+ DdeBookmark::DeregisterFromDoc(io_rDoc);
+
+ if (io_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ io_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoDeleteBookmark>(*this));
+ }
+ io_rDoc.getIDocumentState().SetModified();
+ InvalidateFrames();
+ }
+
+ // invalidate text frames in case it's hidden or Formatting Marks enabled
+ auto Bookmark::InvalidateFrames() -> void
+ {
+ InvalidatePosition(GetMarkPos());
+ if (IsExpanded())
+ {
+ InvalidatePosition(GetOtherMarkPos());
+ }
+ }
+
+ void Bookmark::Hide(bool const isHide)
+ {
+ if (isHide != m_bHidden)
+ {
+ m_bHidden = isHide;
+ InvalidateFrames();
+ }
+ }
+
+ void Bookmark::SetHideCondition(OUString const& rHideCondition)
+ {
+ if (m_sHideCondition != rHideCondition)
+ {
+ m_sHideCondition = rHideCondition;
+ // don't eval condition here yet - probably only needed for
+ // UI editing condition and that doesn't exist yet
+ }
+ }
+
+ ::sfx2::IXmlIdRegistry& Bookmark::GetRegistry()
+ {
+ SwDoc& rDoc( GetMarkPos().GetDoc() );
+ return rDoc.GetXmlIdRegistry();
+ }
+
+ bool Bookmark::IsInClipboard() const
+ {
+ SwDoc& rDoc( GetMarkPos().GetDoc() );
+ return rDoc.IsClipBoard();
+ }
+
+ bool Bookmark::IsInUndo() const
+ {
+ return false;
+ }
+
+ bool Bookmark::IsInContent() const
+ {
+ SwDoc& rDoc( GetMarkPos().GetDoc() );
+ return !rDoc.IsInHeaderFooter( GetMarkPos().nNode );
+ }
+
+ uno::Reference< rdf::XMetadatable > Bookmark::MakeUnoObject()
+ {
+ SwDoc& rDoc( GetMarkPos().GetDoc() );
+ const uno::Reference< rdf::XMetadatable> xMeta(
+ SwXBookmark::CreateXBookmark(rDoc, this), uno::UNO_QUERY);
+ return xMeta;
+ }
+
+ Fieldmark::Fieldmark(const SwPaM& rPaM)
+ : MarkBase(rPaM, MarkBase::GenerateNewName(u"__Fieldmark__"))
+ {
+ if(!IsExpanded())
+ SetOtherMarkPos(GetMarkPos());
+ }
+
+ void Fieldmark::SetMarkStartPos( const SwPosition& rNewStartPos )
+ {
+ if ( GetMarkPos( ) <= GetOtherMarkPos( ) )
+ return SetMarkPos( rNewStartPos );
+ else
+ return SetOtherMarkPos( rNewStartPos );
+ }
+
+ OUString Fieldmark::ToString( ) const
+ {
+ return "Fieldmark: ( Name, Type, [ Nd1, Id1 ], [ Nd2, Id2 ] ): ( " + m_aName + ", "
+ + m_aFieldname + ", [ " + OUString::number( sal_Int32(GetMarkPos().nNode.GetIndex( )) )
+ + ", " + OUString::number( GetMarkPos( ).nContent.GetIndex( ) ) + " ], ["
+ + OUString::number( sal_Int32(GetOtherMarkPos().nNode.GetIndex( )) ) + ", "
+ + OUString::number( GetOtherMarkPos( ).nContent.GetIndex( ) ) + " ] ) ";
+ }
+
+ void Fieldmark::Invalidate( )
+ {
+ // TODO: Does exist a better solution to trigger a format of the
+ // fieldmark portion? If yes, please use it.
+ SwPaM aPaM( GetMarkPos(), GetOtherMarkPos() );
+ aPaM.InvalidatePaM();
+ }
+
+ void Fieldmark::dumpAsXml(xmlTextWriterPtr pWriter) const
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Fieldmark"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldname"), BAD_CAST(m_aFieldname.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fieldHelptext"), BAD_CAST(m_aFieldHelptext.toUtf8().getStr()));
+ MarkBase::dumpAsXml(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("parameters"));
+ for (auto& rParam : m_vParams)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("parameter"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(rParam.first.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(comphelper::anyToString(rParam.second).toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ TextFieldmark::TextFieldmark(const SwPaM& rPaM, const OUString& rName)
+ : Fieldmark(rPaM)
+ {
+ if ( !rName.isEmpty() )
+ m_aName = rName;
+ }
+
+ void TextFieldmark::InitDoc(SwDoc& io_rDoc,
+ sw::mark::InsertMode const eMode, SwPosition const*const pSepPos)
+ {
+ if (eMode == sw::mark::InsertMode::New)
+ {
+ lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos);
+ }
+ else
+ {
+ lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
+ }
+ }
+
+ void TextFieldmark::ReleaseDoc(SwDoc& rDoc)
+ {
+ IDocumentUndoRedo & rIDUR(rDoc.GetIDocumentUndoRedo());
+ if (rIDUR.DoesUndo())
+ {
+ rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this));
+ }
+ ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
+ lcl_RemoveFieldMarks(*this, rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
+ // notify layouts to unhide - for the entire fieldmark, as in InitDoc()
+ SwPaM const tmp(GetMarkPos(), GetOtherMarkPos());
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, tmp);
+ }
+
+ NonTextFieldmark::NonTextFieldmark(const SwPaM& rPaM)
+ : Fieldmark(rPaM)
+ { }
+
+ void NonTextFieldmark::InitDoc(SwDoc& io_rDoc,
+ sw::mark::InsertMode const eMode, SwPosition const*const pSepPos)
+ {
+ assert(pSepPos == nullptr);
+ if (eMode == sw::mark::InsertMode::New)
+ {
+ lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT, pSepPos);
+ }
+ else
+ {
+ lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT);
+ }
+ }
+
+ void NonTextFieldmark::ReleaseDoc(SwDoc& rDoc)
+ {
+ IDocumentUndoRedo & rIDUR(rDoc.GetIDocumentUndoRedo());
+ if (rIDUR.DoesUndo())
+ {
+ rIDUR.AppendUndo(std::make_unique<SwUndoDelNoTextFieldmark>(*this));
+ }
+ ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
+ lcl_RemoveFieldMarks(*this, rDoc,
+ CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FORMELEMENT);
+ }
+
+
+ CheckboxFieldmark::CheckboxFieldmark(const SwPaM& rPaM)
+ : NonTextFieldmark(rPaM)
+ { }
+
+ void CheckboxFieldmark::SetChecked(bool checked)
+ {
+ if ( IsChecked() != checked )
+ {
+ (*GetParameters())[OUString(ODF_FORMCHECKBOX_RESULT)] <<= checked;
+ // mark document as modified
+ SwDoc& rDoc( GetMarkPos().GetDoc() );
+ rDoc.getIDocumentState().SetModified();
+ }
+ }
+
+ bool CheckboxFieldmark::IsChecked() const
+ {
+ bool bResult = false;
+ parameter_map_t::const_iterator pResult = GetParameters()->find(OUString(ODF_FORMCHECKBOX_RESULT));
+ if(pResult != GetParameters()->end())
+ pResult->second >>= bResult;
+ return bResult;
+ }
+
+ FieldmarkWithDropDownButton::FieldmarkWithDropDownButton(const SwPaM& rPaM)
+ : NonTextFieldmark(rPaM)
+ , m_pButton(nullptr)
+ {
+ }
+
+ FieldmarkWithDropDownButton::~FieldmarkWithDropDownButton()
+ {
+ m_pButton.disposeAndClear();
+ }
+
+ void FieldmarkWithDropDownButton::RemoveButton()
+ {
+ if(m_pButton)
+ m_pButton.disposeAndClear();
+ }
+
+ DropDownFieldmark::DropDownFieldmark(const SwPaM& rPaM)
+ : FieldmarkWithDropDownButton(rPaM)
+ {
+ }
+
+ DropDownFieldmark::~DropDownFieldmark()
+ {
+ }
+
+ void DropDownFieldmark::ShowButton(SwEditWin* pEditWin)
+ {
+ if(pEditWin)
+ {
+ if(!m_pButton)
+ m_pButton = VclPtr<DropDownFormFieldButton>::Create(pEditWin, *this);
+ m_pButton->CalcPosAndSize(m_aPortionPaintArea);
+ m_pButton->Show();
+ }
+ }
+
+ void DropDownFieldmark::RemoveButton()
+ {
+ FieldmarkWithDropDownButton::RemoveButton();
+ }
+
+ void DropDownFieldmark::SetPortionPaintArea(const SwRect& rPortionPaintArea)
+ {
+ m_aPortionPaintArea = rPortionPaintArea;
+ if(m_pButton)
+ {
+ m_pButton->Show();
+ m_pButton->CalcPosAndSize(m_aPortionPaintArea);
+ }
+ }
+
+ void DropDownFieldmark::SendLOKShowMessage(const SfxViewShell* pViewShell)
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (!pViewShell || pViewShell->isLOKMobilePhone())
+ return;
+
+ if (m_aPortionPaintArea.IsEmpty())
+ return;
+
+ OStringBuffer sPayload;
+ sPayload = OString::Concat("{\"action\": \"show\","
+ " \"type\": \"drop-down\", \"textArea\": \"") +
+ m_aPortionPaintArea.SVRect().toString() + "\",";
+ // Add field params to the message
+ sPayload.append(" \"params\": { \"items\": [");
+
+ // List items
+ auto pParameters = this->GetParameters();
+ auto pListEntriesIter = pParameters->find(ODF_FORMDROPDOWN_LISTENTRY);
+ css::uno::Sequence<OUString> vListEntries;
+ if (pListEntriesIter != pParameters->end())
+ {
+ pListEntriesIter->second >>= vListEntries;
+ for (const OUString& sItem : std::as_const(vListEntries))
+ sPayload.append("\"" + OUStringToOString(sItem, RTL_TEXTENCODING_UTF8) + "\", ");
+ sPayload.setLength(sPayload.getLength() - 2);
+ }
+ sPayload.append("], ");
+
+ // Selected item
+ auto pSelectedItemIter = pParameters->find(ODF_FORMDROPDOWN_RESULT);
+ sal_Int32 nSelection = -1;
+ if (pSelectedItemIter != pParameters->end())
+ {
+ pSelectedItemIter->second >>= nSelection;
+ }
+ sPayload.append("\"selected\": \"" + OString::number(nSelection) + "\", ");
+
+ // Placeholder text
+ sPayload.append("\"placeholderText\": \"" + OUStringToOString(SwResId(STR_DROP_DOWN_EMPTY_LIST), RTL_TEXTENCODING_UTF8) + "\"}}");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON, sPayload.toString().getStr());
+ }
+
+ void DropDownFieldmark::SendLOKHideMessage(const SfxViewShell* pViewShell)
+ {
+ OString sPayload = "{\"action\": \"hide\", \"type\": \"drop-down\"}";
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_FORM_FIELD_BUTTON, sPayload.getStr());
+ }
+
+ DateFieldmark::DateFieldmark(const SwPaM& rPaM)
+ : FieldmarkWithDropDownButton(rPaM)
+ , m_pNumberFormatter(nullptr)
+ , m_pDocumentContentOperationsManager(nullptr)
+ {
+ }
+
+ DateFieldmark::~DateFieldmark()
+ {
+ }
+
+ void DateFieldmark::InitDoc(SwDoc& io_rDoc,
+ sw::mark::InsertMode eMode, SwPosition const*const pSepPos)
+ {
+ m_pNumberFormatter = io_rDoc.GetNumberFormatter();
+ m_pDocumentContentOperationsManager = &io_rDoc.GetDocumentContentOperationsManager();
+ if (eMode == sw::mark::InsertMode::New)
+ {
+ lcl_SetFieldMarks(*this, io_rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND, pSepPos);
+ }
+ else
+ {
+ lcl_AssertFieldMarksSet(*this, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
+ }
+ }
+
+ void DateFieldmark::ReleaseDoc(SwDoc& rDoc)
+ {
+ IDocumentUndoRedo & rIDUR(rDoc.GetIDocumentUndoRedo());
+ if (rIDUR.DoesUndo())
+ {
+ // TODO does this need a 3rd Undo class?
+ rIDUR.AppendUndo(std::make_unique<SwUndoDelTextFieldmark>(*this));
+ }
+ ::sw::UndoGuard const ug(rIDUR); // prevent SwUndoDeletes
+ lcl_RemoveFieldMarks(*this, rDoc, CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDEND);
+ // notify layouts to unhide - for the entire fieldmark, as in InitDoc()
+ SwPaM const tmp(GetMarkPos(), GetOtherMarkPos());
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, tmp);
+ }
+
+ void DateFieldmark::ShowButton(SwEditWin* pEditWin)
+ {
+ if(pEditWin)
+ {
+ if(!m_pButton)
+ m_pButton = VclPtr<DateFormFieldButton>::Create(pEditWin, *this, m_pNumberFormatter);
+ SwRect aPaintArea(m_aPaintAreaStart.TopLeft(), m_aPaintAreaEnd.BottomRight());
+ m_pButton->CalcPosAndSize(aPaintArea);
+ m_pButton->Show();
+ }
+ }
+
+ void DateFieldmark::SetPortionPaintAreaStart(const SwRect& rPortionPaintArea)
+ {
+ if (rPortionPaintArea.IsEmpty())
+ return;
+
+ m_aPaintAreaStart = rPortionPaintArea;
+ InvalidateCurrentDateParam();
+ }
+
+ void DateFieldmark::SetPortionPaintAreaEnd(const SwRect& rPortionPaintArea)
+ {
+ if (rPortionPaintArea.IsEmpty())
+ return;
+
+ if(m_aPaintAreaEnd == rPortionPaintArea &&
+ m_pButton && m_pButton->IsVisible())
+ return;
+
+ m_aPaintAreaEnd = rPortionPaintArea;
+ if(m_pButton)
+ {
+ m_pButton->Show();
+ SwRect aPaintArea(m_aPaintAreaStart.TopLeft(), m_aPaintAreaEnd.BottomRight());
+ m_pButton->CalcPosAndSize(aPaintArea);
+ m_pButton->Invalidate();
+ }
+ InvalidateCurrentDateParam();
+ }
+
+ OUString DateFieldmark::GetContent() const
+ {
+ const SwTextNode* const pTextNode = GetMarkEnd().nNode.GetNode().GetTextNode();
+ SwPosition const sepPos(sw::mark::FindFieldSep(*this));
+ const sal_Int32 nStart(sepPos.nContent.GetIndex());
+ const sal_Int32 nEnd (GetMarkEnd().nContent.GetIndex());
+
+ OUString sContent;
+ if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() &&
+ nEnd > nStart + 2)
+ sContent = pTextNode->GetText().copy(nStart + 1, nEnd - nStart - 2);
+ return sContent;
+ }
+
+ void DateFieldmark::ReplaceContent(const OUString& sNewContent)
+ {
+ if(!m_pDocumentContentOperationsManager)
+ return;
+
+ const SwTextNode* const pTextNode = GetMarkEnd().nNode.GetNode().GetTextNode();
+ SwPosition const sepPos(sw::mark::FindFieldSep(*this));
+ const sal_Int32 nStart(sepPos.nContent.GetIndex());
+ const sal_Int32 nEnd (GetMarkEnd().nContent.GetIndex());
+
+ if(nStart + 1 < pTextNode->GetText().getLength() && nEnd <= pTextNode->GetText().getLength() &&
+ nEnd > nStart + 2)
+ {
+ SwPaM aFieldPam(GetMarkStart().nNode, nStart + 1,
+ GetMarkStart().nNode, nEnd - 1);
+ m_pDocumentContentOperationsManager->ReplaceRange(aFieldPam, sNewContent, false);
+ }
+ else
+ {
+ SwPaM aFieldStartPam(GetMarkStart().nNode, nStart + 1);
+ m_pDocumentContentOperationsManager->InsertString(aFieldStartPam, sNewContent);
+ }
+
+ }
+
+ std::pair<bool, double> DateFieldmark::GetCurrentDate() const
+ {
+ // Check current date param first
+ std::pair<bool, double> aResult = ParseCurrentDateParam();
+ if(aResult.first)
+ return aResult;
+
+ const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+ bool bFoundValidDate = false;
+ double dCurrentDate = 0;
+ OUString sDateFormat;
+ auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sDateFormat;
+ }
+
+ OUString sLang;
+ pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sLang;
+ }
+
+ // Get current content of the field
+ OUString sContent = GetContent();
+
+ sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType());
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ m_pNumberFormatter->PutEntry(sDateFormat,
+ nCheckPos,
+ nType,
+ nFormat,
+ LanguageTag(sLang).getLanguageType());
+ }
+
+ if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sContent, nFormat, dCurrentDate);
+ }
+ return std::pair<bool, double>(bFoundValidDate, dCurrentDate);
+ }
+
+ void DateFieldmark::SetCurrentDate(double fDate)
+ {
+ // Replace current content with the selected date
+ ReplaceContent(GetDateInCurrentDateFormat(fDate));
+
+ // Also save the current date in a standard format
+ sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+ (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= GetDateInStandardDateFormat(fDate);
+ }
+
+ OUString DateFieldmark::GetDateInStandardDateFormat(double fDate) const
+ {
+ OUString sCurrentDate;
+ sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(ODF_FORMDATE_CURRENTDATE_FORMAT, ODF_FORMDATE_CURRENTDATE_LANGUAGE);
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT;
+ m_pNumberFormatter->PutEntry(sFormat,
+ nCheckPos,
+ nType,
+ nFormat,
+ ODF_FORMDATE_CURRENTDATE_LANGUAGE);
+ }
+
+ if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ const Color* pCol = nullptr;
+ m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentDate, &pCol, false);
+ }
+ return sCurrentDate;
+ }
+
+ std::pair<bool, double> DateFieldmark::ParseCurrentDateParam() const
+ {
+ bool bFoundValidDate = false;
+ double dCurrentDate = 0;
+
+ const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+ auto pResult = pParameters->find(ODF_FORMDATE_CURRENTDATE);
+ OUString sCurrentDate;
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sCurrentDate;
+ }
+ if(!sCurrentDate.isEmpty())
+ {
+ sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(ODF_FORMDATE_CURRENTDATE_FORMAT, ODF_FORMDATE_CURRENTDATE_LANGUAGE);
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString sFormat = ODF_FORMDATE_CURRENTDATE_FORMAT;
+ m_pNumberFormatter->PutEntry(sFormat,
+ nCheckPos,
+ nType,
+ nFormat,
+ ODF_FORMDATE_CURRENTDATE_LANGUAGE);
+ }
+
+ if(nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ bFoundValidDate = m_pNumberFormatter->IsNumberFormat(sCurrentDate, nFormat, dCurrentDate);
+ }
+ }
+ return std::pair<bool, double>(bFoundValidDate, dCurrentDate);
+ }
+
+
+ OUString DateFieldmark::GetDateInCurrentDateFormat(double fDate) const
+ {
+ // Get current date format and language
+ OUString sDateFormat;
+ const sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+ auto pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sDateFormat;
+ }
+
+ OUString sLang;
+ pResult = pParameters->find(ODF_FORMDATE_DATEFORMAT_LANGUAGE);
+ if (pResult != pParameters->end())
+ {
+ pResult->second >>= sLang;
+ }
+
+ // Fill the content with the specified format
+ OUString sCurrentContent;
+ sal_uInt32 nFormat = m_pNumberFormatter->GetEntryKey(sDateFormat, LanguageTag(sLang).getLanguageType());
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString sFormat = sDateFormat;
+ m_pNumberFormatter->PutEntry(sFormat,
+ nCheckPos,
+ nType,
+ nFormat,
+ LanguageTag(sLang).getLanguageType());
+ }
+
+ if (nFormat != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ const Color* pCol = nullptr;
+ m_pNumberFormatter->GetOutputString(fDate, nFormat, sCurrentContent, &pCol, false);
+ }
+ return sCurrentContent;
+ }
+
+ void DateFieldmark::InvalidateCurrentDateParam()
+ {
+ std::pair<bool, double> aResult = ParseCurrentDateParam();
+ if(!aResult.first)
+ return;
+
+ // Current date became invalid
+ if(GetDateInCurrentDateFormat(aResult.second) != GetContent())
+ {
+ sw::mark::IFieldmark::parameter_map_t* pParameters = GetParameters();
+ (*pParameters)[ODF_FORMDATE_CURRENTDATE] <<= OUString();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/callnk.cxx b/sw/source/core/crsr/callnk.cxx
new file mode 100644
index 000000000..c4f2ccdff
--- /dev/null
+++ b/sw/source/core/crsr/callnk.cxx
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <osl/diagnose.h>
+#include <fmtcntnt.hxx>
+#include <txatbase.hxx>
+#include "callnk.hxx"
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <frmfmt.hxx>
+#include <txtfrm.hxx>
+#include <rowfrm.hxx>
+#include <fmtfsize.hxx>
+#include <ndtxt.hxx>
+#include <flyfrm.hxx>
+#include <breakit.hxx>
+#include <UndoTable.hxx>
+
+SwCallLink::SwCallLink( SwCursorShell & rSh )
+ : m_rShell( rSh )
+{
+ // remember SPoint-values of current cursor
+ SwPaM* pCursor = m_rShell.IsTableMode() ? m_rShell.GetTableCrs() : m_rShell.GetCursor();
+ SwNode& rNd = pCursor->GetPoint()->nNode.GetNode();
+ m_nNode = rNd.GetIndex();
+ m_nContent = pCursor->GetPoint()->nContent.GetIndex();
+ m_nNodeType = rNd.GetNodeType();
+ m_bHasSelection = ( *pCursor->GetPoint() != *pCursor->GetMark() );
+
+ if( rNd.IsTextNode() )
+ m_nLeftFramePos = SwCallLink::getLayoutFrame( m_rShell.GetLayout(), *rNd.GetTextNode(), m_nContent,
+ !m_rShell.ActionPend() );
+ else
+ {
+ m_nLeftFramePos = 0;
+
+ // A special treatment for SwFeShell:
+ // When deleting the header/footer, footnotes SwFeShell sets the
+ // Cursor to NULL (Node + Content).
+ // If the Cursor is not on a ContentNode (ContentNode) this fact gets
+ // saved in nNdType.
+ if( SwNodeType::ContentMask & m_nNodeType )
+ m_nNodeType = SwNodeType::NONE;
+ }
+}
+
+namespace sw {
+
+/**
+ An empty paragraph inside a table with a nested table preceding it
+ should be hidden, unless the cursor is positioned in the paragraph.
+
+ If the cursor is now (or was previously) inside such a paragraph,
+ send a size change notification on the row frame to force reformatting.
+ */
+void NotifyTableCollapsedParagraph(const SwContentNode *const pNode, SwCursorShell *const pShell)
+{
+ if ( !pNode )
+ return;
+
+ SwFrame *const pMyFrame = pNode->getLayoutFrame(pShell ? pShell->GetLayout() : nullptr);
+ if ( !pMyFrame )
+ return;
+
+ // important: only invalidate layout if something is actually hidden or
+ // shown! Otherwise performance is going to suffer with "difficult" tables.
+ if (!pMyFrame->IsCollapse())
+ return;
+
+ SwRowFrame *const pRow = pMyFrame->FindRowFrame();
+ if ( !pRow )
+ return;
+
+ const SwTableLine* pLine = pRow->GetTabLine( );
+
+ if (pShell && (pShell->IsTableMode() || (pShell->StartsWithTable() && pShell->ExtendedSelectedAll())))
+ {
+ // If we have a table selection, then avoid the notification: it's not necessary (the text
+ // cursor needs no updating) and the notification may kill the selection overlay, leading to
+ // flicker.
+ // Same for whole-document selection when it starts with a table.
+ return;
+ }
+
+ // notify a change in frame size to force reformatting of the row
+ const SwFormatFrameSize aSize = pLine->GetFrameFormat()->GetFrameSize();
+ pRow->OnFrameSize(aSize);
+}
+
+} // namespace sw
+
+SwCallLink::~SwCallLink() COVERITY_NOEXCEPT_FALSE
+{
+ if( m_nNodeType == SwNodeType::NONE || !m_rShell.m_bCallChgLnk ) // see ctor
+ return ;
+
+ // If travelling over Nodes check formats and register them anew at the
+ // new Node.
+ SwPaM* pCurrentCursor = m_rShell.IsTableMode() ? m_rShell.GetTableCrs() : m_rShell.GetCursor();
+ SwContentNode * pCNd = pCurrentCursor->GetContentNode();
+ if( !pCNd )
+ return;
+
+ if (pCNd->GetIndex() != m_nNode) // only if moved to different node
+ {
+ ::sw::NotifyTableCollapsedParagraph(pCNd, &m_rShell);
+
+ const SwDoc *pDoc=m_rShell.GetDoc();
+ if (sal_Int32(m_nNode) < sal_Int32(pDoc->GetNodes().Count()))
+ {
+ const SwContentNode *const pNode = pDoc->GetNodes()[m_nNode]->GetContentNode();
+ ::sw::NotifyTableCollapsedParagraph(pNode, &m_rShell);
+ }
+ }
+
+ sal_Int32 nCmp, nCurrentContent = pCurrentCursor->GetPoint()->nContent.GetIndex();
+ SwNodeType nNdWhich = pCNd->GetNodeType();
+ SwNodeOffset nCurrentNode = pCurrentCursor->GetPoint()->nNode.GetIndex();
+
+ // Register the Shell as dependent at the current Node. By doing this all
+ // attribute changes can be signaled over the link.
+ pCNd->Add( &m_rShell );
+
+ const bool bCurrentHasSelection = (*pCurrentCursor->GetPoint() != *pCurrentCursor->GetMark());
+
+ if( m_nNodeType != nNdWhich || m_nNode != nCurrentNode )
+ {
+ // Every time a switch between nodes occurs, there is a chance that
+ // new attributes do apply - meaning text-attributes.
+ // So the currently applying attributes would have to be determined.
+ // That can be done in one go by the handler.
+ m_rShell.CallChgLnk();
+ }
+ else if (m_bHasSelection != bCurrentHasSelection)
+ {
+ // always call change link when selection changes
+ m_rShell.CallChgLnk();
+ }
+ else if( m_rShell.m_aChgLnk.IsSet() && SwNodeType::Text == nNdWhich &&
+ m_nContent != nCurrentContent )
+ {
+ // If travelling with left/right only and the frame is
+ // unchanged (columns!) then check text hints.
+ if( m_nLeftFramePos == SwCallLink::getLayoutFrame( m_rShell.GetLayout(), *pCNd->GetTextNode(), nCurrentContent,
+ !m_rShell.ActionPend() ) &&
+ (( nCmp = m_nContent ) + 1 == nCurrentContent || // Right
+ m_nContent -1 == ( nCmp = nCurrentContent )) ) // Left
+ {
+ if( nCmp == nCurrentContent && pCurrentCursor->HasMark() ) // left & select
+ ++nCmp;
+
+ if ( pCNd->GetTextNode()->HasHints() )
+ {
+ const SwpHints &rHts = pCNd->GetTextNode()->GetSwpHints();
+
+ for( size_t n = 0; n < rHts.Count(); ++n )
+ {
+ const SwTextAttr* pHt = rHts.Get( n );
+ const sal_Int32 *pEnd = pHt->End();
+ const sal_Int32 nStart = pHt->GetStart();
+
+ // If "only start" or "start and end equal" then call on
+ // every overflow of start.
+ if( ( !pEnd || ( nStart == *pEnd ) ) &&
+ ( nStart == m_nContent || nStart == nCurrentContent) )
+ {
+ m_rShell.CallChgLnk();
+ return;
+ }
+
+ // If the attribute has an area and that area is not empty ...
+ else if( pEnd && nStart < *pEnd &&
+ // ... then test if travelling occurred via start/end.
+ ( nStart == nCmp ||
+ ( pHt->DontExpand() ? nCmp == *pEnd-1
+ : nCmp == *pEnd ) ))
+ {
+ m_rShell.CallChgLnk();
+ return;
+ }
+ }
+ }
+
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ const OUString rText = pCNd->GetTextNode()->GetText();
+ if( !nCmp ||
+ g_pBreakIt->GetBreakIter()->getScriptType( rText, m_nContent )
+ != g_pBreakIt->GetBreakIter()->getScriptType(rText, nCurrentContent))
+ {
+ m_rShell.CallChgLnk();
+ return;
+ }
+ }
+ else
+ // If travelling more than one character with home/end/.. then
+ // always call ChgLnk, because it can not be determined here what
+ // has changed. Something may have changed.
+ m_rShell.CallChgLnk();
+ }
+
+ const SwFrame* pFrame;
+ const SwFlyFrame *pFlyFrame;
+ if (m_rShell.ActionPend())
+ return;
+ pFrame = pCNd->getLayoutFrame(m_rShell.GetLayout(), nullptr, nullptr);
+ if (!pFrame)
+ return;
+ pFlyFrame = pFrame->FindFlyFrame();
+ if ( !pFlyFrame || m_rShell.IsTableMode() )
+ return;
+
+ const SwNodeIndex* pIndex = pFlyFrame->GetFormat()->GetContent().GetContentIdx();
+ OSL_ENSURE( pIndex, "Fly without Content" );
+
+ if (!pIndex)
+ return;
+
+ const SwNode& rStNd = pIndex->GetNode();
+
+ if( rStNd.EndOfSectionNode()->StartOfSectionIndex() > m_nNode ||
+ m_nNode > rStNd.EndOfSectionIndex() )
+ m_rShell.GetFlyMacroLnk().Call( pFlyFrame->GetFormat() );
+}
+
+tools::Long SwCallLink::getLayoutFrame(const SwRootFrame* pRoot,
+ SwTextNode const & rNd, sal_Int32 nCntPos, bool /*bCalcFrame*/)
+{
+ SwTextFrame* pFrame = static_cast<SwTextFrame*>(rNd.getLayoutFrame(pRoot, nullptr, nullptr));
+ SwTextFrame* pNext;
+ if ( pFrame && !pFrame->IsHiddenNow() )
+ {
+ if( pFrame->HasFollow() )
+ {
+ TextFrameIndex const nPos(pFrame->MapModelToView(&rNd, nCntPos));
+ for (;;)
+ {
+ pNext = pFrame->GetFollow();
+ if(!pNext || nPos < pNext->GetOffset())
+ break;
+ pFrame = pNext;
+ }
+ }
+
+ return pFrame->getFrameArea().Left();
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/callnk.hxx b/sw/source/core/crsr/callnk.hxx
new file mode 100644
index 000000000..ca7db0c8f
--- /dev/null
+++ b/sw/source/core/crsr/callnk.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_CRSR_CALLNK_HXX
+#define INCLUDED_SW_SOURCE_CORE_CRSR_CALLNK_HXX
+
+#include <tools/long.hxx>
+#include <ndtyp.hxx>
+#include <nodeoffset.hxx>
+
+class SwCursorShell;
+class SwTextNode;
+class SwRootFrame;
+
+class SwCallLink
+{
+public:
+ SwCursorShell & m_rShell;
+ SwNodeOffset m_nNode;
+ tools::Long m_nLeftFramePos;
+ sal_Int32 m_nContent;
+ SwNodeType m_nNodeType;
+ bool m_bHasSelection;
+
+ explicit SwCallLink( SwCursorShell & rSh );
+ ~SwCallLink() COVERITY_NOEXCEPT_FALSE;
+
+ static tools::Long getLayoutFrame( const SwRootFrame*, SwTextNode const & rNd, sal_Int32 nCntPos, bool bCalcFrame );
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_CRSR_CALLNK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/contentcontrolbutton.cxx b/sw/source/core/crsr/contentcontrolbutton.cxx
new file mode 100644
index 000000000..108a6fe7e
--- /dev/null
+++ b/sw/source/core/crsr/contentcontrolbutton.cxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <contentcontrolbutton.hxx>
+
+#include <vcl/weldutils.hxx>
+#include <vcl/event.hxx>
+#include <vcl/decoview.hxx>
+
+#include <edtwin.hxx>
+#include <dview.hxx>
+
+SwContentControlButton::SwContentControlButton(
+ SwEditWin* pEditWin, const std::shared_ptr<SwContentControl>& pContentControl)
+ : Control(pEditWin, WB_DIALOGCONTROL)
+ , m_pContentControl(pContentControl)
+{
+ assert(GetParent());
+ assert(dynamic_cast<SwEditWin*>(GetParent()));
+
+ SetBackground();
+ EnableChildTransparentMode();
+ SetParentClipMode(ParentClipMode::NoClip);
+ SetPaintTransparent(true);
+}
+
+SwContentControlButton::~SwContentControlButton() { disposeOnce(); }
+
+void SwContentControlButton::LaunchPopup()
+{
+ m_xPopup->connect_closed(LINK(this, SwContentControlButton, PopupModeEndHdl));
+
+ tools::Rectangle aRect(Point(0, 0), GetSizePixel());
+ weld::Window* pParent = weld::GetPopupParent(*this, aRect);
+ m_xPopup->popup_at_rect(pParent, aRect);
+}
+
+void SwContentControlButton::DestroyPopup()
+{
+ m_xPopup.reset();
+ m_xPopupBuilder.reset();
+}
+
+void SwContentControlButton::dispose()
+{
+ DestroyPopup();
+ Control::dispose();
+}
+
+void SwContentControlButton::CalcPosAndSize(const SwRect& rPortionPaintArea)
+{
+ assert(GetParent());
+
+ Point aBoxPos = GetParent()->LogicToPixel(rPortionPaintArea.Pos());
+ Size aBoxSize = GetParent()->LogicToPixel(rPortionPaintArea.SSize());
+
+ // First calculate the size of the frame around the content control's last portion
+ int nPadding = aBoxSize.Height() / 4;
+ aBoxPos.AdjustX(-nPadding / 2);
+ aBoxPos.AdjustY(-1);
+ aBoxSize.AdjustWidth(nPadding);
+ aBoxSize.AdjustHeight(2);
+
+ m_aFramePixel = tools::Rectangle(aBoxPos, aBoxSize);
+
+ // Then extend the size with the button area
+ aBoxSize.AdjustWidth(GetParent()->LogicToPixel(rPortionPaintArea.SSize()).Height());
+
+ if (aBoxPos != GetPosPixel() || aBoxSize != GetSizePixel())
+ {
+ SetPosSizePixel(aBoxPos, aBoxSize);
+ Invalidate();
+ }
+}
+
+void SwContentControlButton::MouseButtonDown(const MouseEvent&) { StartPopup(); }
+
+void SwContentControlButton::StartPopup()
+{
+ LaunchPopup();
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(SwContentControlButton, PopupModeEndHdl, weld::Popover&, void)
+{
+ DestroyPopup();
+ Show(false);
+ Invalidate();
+}
+
+void SwContentControlButton::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ SetMapMode(MapMode(MapUnit::MapPixel));
+
+ Color aLineColor = COL_BLACK;
+ Color aFillColor = aLineColor;
+ aFillColor.IncreaseLuminance(255 * (m_xPopup ? 0.5 : 0.75));
+
+ // Calc the frame around the content control's last portion
+ int nPadding = 1;
+ Point aPos(nPadding, nPadding);
+ Size aSize(m_aFramePixel.GetSize().Width() - nPadding,
+ m_aFramePixel.GetSize().Height() - nPadding);
+ const tools::Rectangle aFrameRect(tools::Rectangle(aPos, aSize));
+
+ // Draw the button next to the frame
+ Point aButtonPos(aFrameRect.TopLeft());
+ aButtonPos.AdjustX(aFrameRect.GetSize().getWidth() - nPadding * 2);
+ Size aButtonSize(aFrameRect.GetSize());
+ aButtonSize.setWidth(GetSizePixel().getWidth() - aFrameRect.getWidth() - nPadding);
+ const tools::Rectangle aButtonRect(tools::Rectangle(aButtonPos, aButtonSize));
+
+ // Background & border
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(aFillColor);
+ rRenderContext.DrawRect(aButtonRect);
+
+ // the arrowhead
+ DecorationView aDecoView(&rRenderContext);
+ tools::Rectangle aSymbolRect(aButtonRect);
+ // 20% distance to the left and right button border
+ const tools::Long nBorderDistanceLeftAndRight = aSymbolRect.GetWidth() / 4;
+ aSymbolRect.AdjustLeft(nBorderDistanceLeftAndRight);
+ aSymbolRect.AdjustRight(-nBorderDistanceLeftAndRight);
+ // 20% distance to the top and bottom button border
+ const tools::Long nBorderDistanceTopAndBottom = aSymbolRect.GetHeight() / 4;
+ aSymbolRect.AdjustTop(nBorderDistanceTopAndBottom);
+ aSymbolRect.AdjustBottom(-nBorderDistanceTopAndBottom);
+ AntialiasingFlags eAntialiasing = rRenderContext.GetAntialiasing();
+ if (SwDrawView::IsAntiAliasing())
+ {
+ rRenderContext.SetAntialiasing(eAntialiasing | AntialiasingFlags::Enable);
+ }
+ aDecoView.DrawSymbol(aSymbolRect, SymbolType::SPIN_DOWN, GetTextColor(), DrawSymbolFlags::NONE);
+ if (SwDrawView::IsAntiAliasing())
+ {
+ rRenderContext.SetAntialiasing(eAntialiasing);
+ }
+}
+
+WindowHitTest SwContentControlButton::ImplHitTest(const Point& rFramePos)
+{
+ // We need to check whether the position hits the button (the frame should be mouse transparent)
+ WindowHitTest aResult = Control::ImplHitTest(rFramePos);
+ if (aResult != WindowHitTest::Inside)
+ return aResult;
+ else
+ {
+ return rFramePos.X() >= m_aFramePixel.Right() ? WindowHitTest::Inside
+ : WindowHitTest::Transparent;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/crbm.cxx b/sw/source/core/crsr/crbm.cxx
new file mode 100644
index 000000000..02f554014
--- /dev/null
+++ b/sw/source/core/crsr/crbm.cxx
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <crsrsh.hxx>
+#include <ndtxt.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <IMark.hxx>
+#include <swcrsr.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+namespace
+{
+ struct CursorStateHelper
+ {
+ explicit CursorStateHelper(SwCursorShell const & rShell)
+ : m_pCursor(rShell.GetCursor())
+ , m_aSaveState(*m_pCursor)
+ { }
+
+ void SetCursorToMark(::sw::mark::IMark const * const pMark)
+ {
+ *(m_pCursor->GetPoint()) = pMark->GetMarkStart();
+ if(pMark->IsExpanded())
+ {
+ m_pCursor->SetMark();
+ *(m_pCursor->GetMark()) = pMark->GetMarkEnd();
+ }
+ }
+
+ /// returns true if the Cursor had been rolled back
+ bool RollbackIfIllegal()
+ {
+ if(m_pCursor->IsSelOvr(SwCursorSelOverFlags::CheckNodeSection
+ | SwCursorSelOverFlags::Toggle))
+ {
+ m_pCursor->DeleteMark();
+ m_pCursor->RestoreSavePos();
+ return true;
+ }
+ return false;
+ }
+
+ SwCursor* m_pCursor;
+ SwCursorSaveState m_aSaveState;
+ };
+
+ bool lcl_ReverseMarkOrderingByEnd(const ::sw::mark::IMark* pFirst,
+ const ::sw::mark::IMark* pSecond)
+ {
+ return pFirst->GetMarkEnd() > pSecond->GetMarkEnd();
+ }
+
+ bool lcl_IsInvisibleBookmark(const ::sw::mark::IMark* pMark)
+ {
+ return IDocumentMarkAccess::GetType(*pMark) != IDocumentMarkAccess::MarkType::BOOKMARK;
+ }
+}
+
+// at CurrentCursor.SPoint
+::sw::mark::IMark* SwCursorShell::SetBookmark(
+ const vcl::KeyCode& rCode,
+ const OUString& rName,
+ IDocumentMarkAccess::MarkType eMark)
+{
+ StartAction();
+ ::sw::mark::IMark* pMark = getIDocumentMarkAccess()->makeMark(
+ *GetCursor(),
+ rName,
+ eMark, sw::mark::InsertMode::New);
+ ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
+ if(pBookmark)
+ {
+ pBookmark->SetKeyCode(rCode);
+ pBookmark->SetShortName(OUString());
+ }
+ EndAction();
+ return pMark;
+}
+// set CurrentCursor.SPoint
+
+// at CurrentCursor.SPoint
+::sw::mark::IMark* SwCursorShell::SetBookmark2(
+ const vcl::KeyCode& rCode,
+ const OUString& rName,
+ bool bHide,
+ const OUString& rCondition)
+{
+ StartAction();
+ ::sw::mark::IMark* pMark = getIDocumentMarkAccess()->makeMark(
+ *GetCursor(),
+ rName,
+ IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
+ ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
+ if (pBookmark)
+ {
+ pBookmark->SetKeyCode(rCode);
+ pBookmark->SetShortName(OUString());
+ pBookmark->Hide(bHide);
+ pBookmark->SetHideCondition(rCondition);
+ }
+ EndAction();
+ return pMark;
+}
+
+namespace sw {
+
+bool IsMarkHidden(SwRootFrame const& rLayout, ::sw::mark::IMark const& rMark)
+{
+ if (!rLayout.HasMergedParas())
+ {
+ return false;
+ }
+ SwNode const& rNode(rMark.GetMarkPos().nNode.GetNode());
+ SwTextNode const*const pTextNode(rNode.GetTextNode());
+ if (pTextNode == nullptr)
+ { // UNO_BOOKMARK may point to table node
+ return rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden;
+ }
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNode->getLayoutFrame(&rLayout)));
+ if (!pFrame)
+ {
+ return true;
+ }
+ if (rMark.IsExpanded())
+ {
+ SwTextFrame const*const pOtherFrame(static_cast<SwTextFrame const*>(
+ rMark.GetOtherMarkPos().nNode.GetNode().GetTextNode()->getLayoutFrame(&rLayout)));
+ return pFrame == pOtherFrame
+ && pFrame->MapModelToViewPos(rMark.GetMarkPos())
+ == pFrame->MapModelToViewPos(rMark.GetOtherMarkPos());
+ }
+ else
+ {
+ if (rMark.GetMarkPos().nContent.GetIndex() == pTextNode->Len())
+ { // at end of node: never deleted (except if node deleted)
+ return pTextNode->GetRedlineMergeFlag() == SwNode::Merge::Hidden;
+ }
+ else
+ { // check character following mark pos
+ return pFrame->MapModelToViewPos(rMark.GetMarkPos())
+ == pFrame->MapModelToView(pTextNode, rMark.GetMarkPos().nContent.GetIndex() + 1);
+ }
+ }
+}
+
+} // namespace sw
+
+// set CurrentCursor.SPoint
+bool SwCursorShell::GotoMark(const ::sw::mark::IMark* const pMark, bool bAtStart)
+{
+ if (sw::IsMarkHidden(*GetLayout(), *pMark))
+ {
+ return false;
+ }
+ // watch Cursor-Moves
+ CursorStateHelper aCursorSt(*this);
+ if ( bAtStart )
+ *aCursorSt.m_pCursor->GetPoint() = pMark->GetMarkStart();
+ else
+ *aCursorSt.m_pCursor->GetPoint() = pMark->GetMarkEnd();
+
+ if(aCursorSt.RollbackIfIllegal()) return false;
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return true;
+}
+
+bool SwCursorShell::GotoMark(const ::sw::mark::IMark* const pMark)
+{
+ if (sw::IsMarkHidden(*GetLayout(), *pMark))
+ {
+ return false;
+ }
+ // watch Cursor-Moves
+ CursorStateHelper aCursorSt(*this);
+ aCursorSt.SetCursorToMark(pMark);
+
+ if(aCursorSt.RollbackIfIllegal()) return false;
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return true;
+}
+
+bool SwCursorShell::GoNextBookmark()
+{
+ IDocumentMarkAccess* pMarkAccess = getIDocumentMarkAccess();
+ std::vector<::sw::mark::IMark*> vCandidates;
+ remove_copy_if(
+ pMarkAccess->findFirstBookmarkStartsAfter(*GetCursor()->GetPoint()),
+ pMarkAccess->getBookmarksEnd(),
+ back_inserter(vCandidates),
+ &lcl_IsInvisibleBookmark);
+
+ // watch Cursor-Moves
+ CursorStateHelper aCursorSt(*this);
+ auto ppMark = vCandidates.begin();
+ for(; ppMark!=vCandidates.end(); ++ppMark)
+ {
+ if (sw::IsMarkHidden(*GetLayout(), **ppMark))
+ {
+ continue;
+ }
+ aCursorSt.SetCursorToMark(*ppMark);
+ if(!aCursorSt.RollbackIfIllegal())
+ break; // found legal move
+ }
+ if(ppMark==vCandidates.end())
+ {
+ SttEndDoc(false);
+ return false;
+ }
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return true;
+}
+
+bool SwCursorShell::GoPrevBookmark()
+{
+ IDocumentMarkAccess* pMarkAccess = getIDocumentMarkAccess();
+ // candidates from which to choose the mark before
+ // no need to consider marks starting after rPos
+ std::vector<::sw::mark::IMark*> vCandidates;
+ remove_copy_if(
+ pMarkAccess->getBookmarksBegin(),
+ pMarkAccess->findFirstBookmarkStartsAfter(*GetCursor()->GetPoint()),
+ back_inserter(vCandidates),
+ &lcl_IsInvisibleBookmark);
+ sort(
+ vCandidates.begin(),
+ vCandidates.end(),
+ &lcl_ReverseMarkOrderingByEnd);
+
+ // watch Cursor-Moves
+ CursorStateHelper aCursorSt(*this);
+ auto ppMark = vCandidates.begin();
+ for(; ppMark!=vCandidates.end(); ++ppMark)
+ {
+ // ignoring those not ending before the Cursor
+ // (we were only able to eliminate those starting
+ // behind the Cursor by the upper_bound(..)
+ // above)
+ if(!((**ppMark).GetMarkEnd() < *GetCursor()->GetPoint()))
+ continue;
+ if (sw::IsMarkHidden(*GetLayout(), **ppMark))
+ {
+ continue;
+ }
+ aCursorSt.SetCursorToMark(*ppMark);
+ if(!aCursorSt.RollbackIfIllegal())
+ break; // found legal move
+ }
+ if(ppMark==vCandidates.end())
+ {
+ SttEndDoc(true);
+ return false;
+ }
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return true;
+}
+
+bool SwCursorShell::IsFormProtected()
+{
+ return getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FORM);
+}
+
+::sw::mark::IFieldmark* SwCursorShell::GetCurrentFieldmark()
+{
+ // TODO: Refactor
+ SwPosition pos(*GetCursor()->GetPoint());
+ return getIDocumentMarkAccess()->getFieldmarkFor(pos);
+}
+
+::sw::mark::IFieldmark* SwCursorShell::GetFieldmarkAfter()
+{
+ SwPosition pos(*GetCursor()->GetPoint());
+ return getIDocumentMarkAccess()->getFieldmarkAfter(pos);
+}
+
+::sw::mark::IFieldmark* SwCursorShell::GetFieldmarkBefore()
+{
+ SwPosition pos(*GetCursor()->GetPoint());
+ return getIDocumentMarkAccess()->getFieldmarkBefore(pos);
+}
+
+bool SwCursorShell::GotoFieldmark(::sw::mark::IFieldmark const * const pMark)
+{
+ if(pMark==nullptr) return false;
+
+ // watch Cursor-Moves
+ CursorStateHelper aCursorSt(*this);
+ aCursorSt.SetCursorToMark(pMark);
+ ++aCursorSt.m_pCursor->GetPoint()->nContent;
+ --aCursorSt.m_pCursor->GetMark()->nContent;
+
+ if(aCursorSt.RollbackIfIllegal()) return false;
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/crossrefbookmark.cxx b/sw/source/core/crsr/crossrefbookmark.cxx
new file mode 100644
index 000000000..f5754ee19
--- /dev/null
+++ b/sw/source/core/crsr/crossrefbookmark.cxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+
+#include <IDocumentMarkAccess.hxx>
+#include <crossrefbookmark.hxx>
+#include <ndtxt.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace
+{
+ constexpr OUStringLiteral CrossRefNumItemBookmark_NamePrefix = u"__RefNumPara__";
+}
+
+namespace sw::mark
+{
+ CrossRefBookmark::CrossRefBookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName,
+ std::u16string_view rPrefix)
+ : Bookmark(
+ // ensure that m_pPos2 is null by only passing start to super
+ SwPaM(*rPaM.Start()), rCode, rName)
+ {
+ assert( IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark(rPaM)
+ && "<CrossRefBookmark::CrossRefBookmark(..)>"
+ "- creation of cross-reference bookmark with an illegal PaM that does not expand over exactly one whole paragraph.");
+ if(rName.isEmpty())
+ m_aName = MarkBase::GenerateNewName(rPrefix);
+ assert(!m_pPos2);
+ }
+
+ void CrossRefBookmark::SetMarkPos(const SwPosition& rNewPos)
+ {
+ assert(rNewPos.nNode.GetNode().GetTextNode() &&
+ "<sw::mark::CrossRefBookmark::SetMarkPos(..)>"
+ " - new bookmark position for cross-reference bookmark doesn't mark text node");
+ assert(rNewPos.nContent.GetIndex() == 0 &&
+ "<sw::mark::CrossRefBookmark::SetMarkPos(..)>"
+ " - new bookmark position for cross-reference bookmark doesn't mark start of text node");
+ MarkBase::SetMarkPos(rNewPos);
+ }
+
+ SwPosition& CrossRefBookmark::GetOtherMarkPos() const
+ {
+ assert(false &&
+ "<sw::mark::CrossRefBookmark::GetOtherMarkPos(..)>"
+ " - this should never be called!");
+ for (;;) { std::abort(); } // avoid "must return a value" warnings
+ }
+
+ CrossRefHeadingBookmark::CrossRefHeadingBookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName)
+ : CrossRefBookmark(rPaM, rCode, rName, OUStringConcatenation(IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()+"_Toc"))
+ { }
+
+ bool CrossRefHeadingBookmark::IsLegalName(std::u16string_view rName)
+ {
+ return o3tl::starts_with(rName, IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix());
+ }
+
+ CrossRefNumItemBookmark::CrossRefNumItemBookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName)
+ : CrossRefBookmark(rPaM, rCode, rName, CrossRefNumItemBookmark_NamePrefix)
+ { }
+
+ bool CrossRefNumItemBookmark::IsLegalName(std::u16string_view rName)
+ {
+ return o3tl::starts_with(rName, CrossRefNumItemBookmark_NamePrefix);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx
new file mode 100644
index 000000000..4817c422a
--- /dev/null
+++ b/sw/source/core/crsr/crsrsh.cxx
@@ -0,0 +1,3886 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <com/sun/star/text/XTextRange.hpp>
+
+#include <hintids.hxx>
+#include <svx/srchdlg.hxx>
+#include <sfx2/viewsh.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <doc.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <viewimp.hxx>
+#include <pam.hxx>
+#include <swselectionlist.hxx>
+#include "BlockCursor.hxx"
+#include <ndtxt.hxx>
+#include <flyfrm.hxx>
+#include <dview.hxx>
+#include <viewopt.hxx>
+#include <crsrsh.hxx>
+#include <tabfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <swtable.hxx>
+#include "callnk.hxx"
+#include <viscrs.hxx>
+#include <section.hxx>
+#include <docsh.hxx>
+#include <scriptinfo.hxx>
+#include <globdoc.hxx>
+#include <pamtyp.hxx>
+#include <mdiexp.hxx>
+#include <fmteiro.hxx>
+#include <wrong.hxx>
+#include <unotextrange.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <IGrammarContact.hxx>
+#include <comphelper/flagguard.hxx>
+#include <strings.hrc>
+#include <IDocumentLayoutAccess.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <comphelper/sequence.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/frmdir.hxx>
+#include <sal/log.hxx>
+#include <PostItMgr.hxx>
+#include <DocumentSettingManager.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <tabcol.hxx>
+#include <wrtsh.hxx>
+#include <undobj.hxx>
+#include <view.hxx>
+#include <hints.hxx>
+#include <tools/json_writer.hxx>
+
+using namespace com::sun::star;
+using namespace util;
+
+/**
+ * Check if pCurrentCursor points into already existing ranges and delete those.
+ * @param Pointer to SwCursor object
+ */
+static void CheckRange( SwCursor* pCurrentCursor )
+{
+ const SwPosition *pStt = pCurrentCursor->Start(),
+ *pEnd = pCurrentCursor->End();
+
+ SwPaM *pTmpDel = nullptr,
+ *pTmp = pCurrentCursor->GetNext();
+
+ // Search the complete ring
+ while( pTmp != pCurrentCursor )
+ {
+ const SwPosition *pTmpStt = pTmp->Start(),
+ *pTmpEnd = pTmp->End();
+ if( *pStt <= *pTmpStt )
+ {
+ if( *pEnd > *pTmpStt ||
+ ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
+ pTmpDel = pTmp;
+ }
+ else
+ if( *pStt < *pTmpEnd )
+ pTmpDel = pTmp;
+
+ // If Point or Mark is within the Cursor range, we need to remove the old
+ // range. Take note that Point does not belong to the range anymore.
+ pTmp = pTmp->GetNext();
+ delete pTmpDel; // Remove old range
+ pTmpDel = nullptr;
+ }
+}
+
+// SwCursorShell
+
+/**
+ * Add a copy of current cursor, append it after current, and collapse current cursor.
+ * @return - Returns a newly created copy of current cursor.
+ */
+SwPaM * SwCursorShell::CreateCursor()
+{
+ // don't create new Cursor with active table Selection
+ assert(!IsTableMode());
+
+ // ensure that m_pCurrentCursor is valid; if it's invalid it would be
+ // copied to pNew and then pNew would be deleted in UpdateCursor() below
+ ClearUpCursors();
+
+ // New cursor as copy of current one. Add to the ring.
+ // Links point to previously created one, ie forward.
+ SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor );
+
+ // Hide PaM logically, to avoid undoing the inverting from
+ // copied PaM (#i75172#)
+ pNew->swapContent(*m_pCurrentCursor);
+
+ m_pCurrentCursor->DeleteMark();
+
+ UpdateCursor( SwCursorShell::SCROLLWIN );
+ return pNew;
+}
+
+/**
+ * Delete current Cursor, making the following one the current.
+ * Note, this function does not delete anything if there is no other cursor.
+ * @return - returns true if there was another cursor and we deleted one.
+ */
+void SwCursorShell::DestroyCursor()
+{
+ // don't delete Cursor with active table Selection
+ assert(!IsTableMode());
+
+ // Is there a next one? Don't do anything if not.
+ if(!m_pCurrentCursor->IsMultiSelection())
+ return;
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursor* pNextCursor = static_cast<SwCursor*>(m_pCurrentCursor->GetNext());
+ delete m_pCurrentCursor;
+ m_pCurrentCursor = dynamic_cast<SwShellCursor*>(pNextCursor);
+ UpdateCursor();
+}
+
+/**
+ * Create and return a new shell cursor.
+ * Simply returns the current shell cursor if there is no selection
+ * (HasSelection()).
+ */
+SwCursor & SwCursorShell::CreateNewShellCursor()
+{
+ if (HasSelection())
+ {
+ (void) CreateCursor(); // n.b. returns old cursor
+ }
+ return *GetCursor();
+}
+
+/**
+ * Return the current shell cursor
+ * @return - returns current `SwPaM` shell cursor
+ */
+SwCursor & SwCursorShell::GetCurrentShellCursor()
+{
+ return *GetCursor();
+}
+
+/**
+ * Return pointer to the current shell cursor
+ * @return - returns pointer to current `SwCursor` shell cursor
+ */
+SwCursor* SwCursorShell::GetCursor( bool bMakeTableCursor ) const
+{
+ if( m_pTableCursor )
+ {
+ if( bMakeTableCursor && m_pTableCursor->IsCursorMovedUpdate() )
+ {
+ //don't re-create 'parked' cursors
+ if( m_pTableCursor->GetPoint()->nNode.GetIndex() &&
+ m_pTableCursor->GetMark()->nNode.GetIndex() )
+ {
+ const SwContentNode* pCNd = m_pTableCursor->GetContentNode();
+ if( pCNd && pCNd->getLayoutFrame( GetLayout() ) )
+ {
+ pCNd = m_pTableCursor->GetContentNode(false);
+ if( pCNd && pCNd->getLayoutFrame( GetLayout() ) )
+ {
+ SwShellTableCursor* pTC = m_pTableCursor;
+ GetLayout()->MakeTableCursors( *pTC );
+ }
+ }
+ }
+ }
+
+ if( m_pTableCursor->IsChgd() )
+ {
+ const_cast<SwCursorShell*>(this)->m_pCurrentCursor =
+ dynamic_cast<SwShellCursor*>(m_pTableCursor->MakeBoxSels( m_pCurrentCursor ));
+ }
+ }
+ return m_pCurrentCursor;
+}
+
+void SwCursorShell::StartAction()
+{
+ if( !ActionPend() )
+ {
+ // save for update of the ribbon bar
+ const SwNode& rNd = m_pCurrentCursor->GetPoint()->nNode.GetNode();
+ m_nCurrentNode = rNd.GetIndex();
+ m_nCurrentContent = m_pCurrentCursor->GetPoint()->nContent.GetIndex();
+ m_nCurrentNdTyp = rNd.GetNodeType();
+ if( rNd.IsTextNode() )
+ m_nLeftFramePos = SwCallLink::getLayoutFrame( GetLayout(), *rNd.GetTextNode(), m_nCurrentContent, true );
+ else
+ m_nLeftFramePos = 0;
+ }
+ SwViewShell::StartAction(); // to the SwViewShell
+}
+
+void SwCursorShell::EndAction( const bool bIdleEnd )
+{
+ comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll());
+ bool bVis = m_bSVCursorVis;
+
+ // Idle-formatting?
+ if( bIdleEnd && Imp()->HasPaintRegion() )
+ {
+ m_pCurrentCursor->Hide();
+ }
+
+ // Update all invalid numberings before the last action
+ if( 1 == mnStartAction )
+ GetDoc()->UpdateNumRule();
+
+ // #i76923#: Don't show the cursor in the SwViewShell::EndAction() - call.
+ // Only the UpdateCursor shows the cursor.
+ bool bSavSVCursorVis = m_bSVCursorVis;
+ m_bSVCursorVis = false;
+
+ SwViewShell::EndAction( bIdleEnd ); // have SwViewShell go first
+
+ m_bSVCursorVis = bSavSVCursorVis;
+
+ if( ActionPend() )
+ {
+ if( bVis ) // display SV-Cursor again
+ m_pVisibleCursor->Show();
+
+ return;
+ }
+
+ sal_uInt16 eFlags = SwCursorShell::CHKRANGE;
+ if ( !bIdleEnd )
+ eFlags |= SwCursorShell::SCROLLWIN;
+
+ UpdateCursor( eFlags, bIdleEnd ); // Show Cursor changes
+
+ {
+ SwCallLink aLk( *this ); // Watch cursor moves,
+ aLk.m_nNode = m_nCurrentNode; // possibly call the link
+ aLk.m_nNodeType = m_nCurrentNdTyp;
+ aLk.m_nContent = m_nCurrentContent;
+ aLk.m_nLeftFramePos = m_nLeftFramePos;
+
+ if( !m_nCursorMove ||
+ ( 1 == m_nCursorMove && m_bInCMvVisportChgd ) )
+ // display Cursor & Selections again
+ ShowCursors( m_bSVCursorVis );
+ }
+ // call ChgCall if there is still one
+ if( m_bCallChgLnk && m_bChgCallFlag && m_aChgLnk.IsSet() )
+ {
+ m_aChgLnk.Call(nullptr);
+ m_bChgCallFlag = false; // reset flag
+ }
+}
+
+void SwCursorShell::SttCursorMove()
+{
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_nCursorMove < USHRT_MAX, "Too many nested CursorMoves." );
+#endif
+ ++m_nCursorMove;
+ StartAction();
+}
+
+void SwCursorShell::EndCursorMove( const bool bIdleEnd )
+{
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_nCursorMove, "EndCursorMove() without SttCursorMove()." );
+#endif
+ EndAction( bIdleEnd );
+ --m_nCursorMove;
+#ifdef DBG_UTIL
+ if( !m_nCursorMove )
+ m_bInCMvVisportChgd = false;
+#endif
+}
+
+bool SwCursorShell::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode,
+ bool bVisualAllowed )
+{
+ if( IsTableMode() )
+ return bLeft ? GoPrevCell() : GoNextCell();
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ bool bRet = false;
+
+ // #i27615# Handle cursor in front of label.
+ const SwTextNode* pTextNd = nullptr;
+
+ if( m_pBlockCursor )
+ m_pBlockCursor->clearPoints();
+
+ // 1. CASE: Cursor is in front of label. A move to the right
+ // will simply reset the bInFrontOfLabel flag:
+ SwShellCursor* pShellCursor = getShellCursor( true );
+ if ( !bLeft && pShellCursor->IsInFrontOfLabel() )
+ {
+ SetInFrontOfLabel( false );
+ bRet = true;
+ }
+ // 2. CASE: Cursor is at beginning of numbered paragraph. A move
+ // to the left will simply set the bInFrontOfLabel flag:
+ else if (bLeft
+ && pShellCursor->GetPoint()->nNode.GetNode().IsTextNode()
+ && static_cast<SwTextFrame const*>(
+ pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->getLayoutFrame(GetLayout())
+ )->MapModelToViewPos(*pShellCursor->GetPoint()) == TextFrameIndex(0)
+ && !pShellCursor->IsInFrontOfLabel()
+ && !pShellCursor->HasMark()
+ && nullptr != (pTextNd = sw::GetParaPropsNode(*GetLayout(), pShellCursor->GetPoint()->nNode))
+ && pTextNd->HasVisibleNumberingOrBullet())
+ {
+ SetInFrontOfLabel( true );
+ bRet = true;
+ }
+ // 3. CASE: Regular cursor move. Reset the bInFrontOfLabel flag:
+ else
+ {
+ const bool bSkipHidden = !GetViewOptions()->IsShowHiddenChar();
+ // #i107447#
+ // To avoid loop the reset of <bInFrontOfLabel> flag is no longer
+ // reflected in the return value <bRet>.
+ const bool bResetOfInFrontOfLabel = SetInFrontOfLabel( false );
+ bRet = pShellCursor->LeftRight( bLeft, nCnt, nMode, bVisualAllowed,
+ bSkipHidden, !IsOverwriteCursor(),
+ GetLayout(),
+ GetViewOptions()->IsFieldName());
+ if ( !bRet && bLeft && bResetOfInFrontOfLabel )
+ {
+ // undo reset of <bInFrontOfLabel> flag
+ SetInFrontOfLabel( true );
+ }
+ }
+
+ if( bRet )
+ {
+ UpdateCursor();
+ }
+
+ return bRet;
+}
+
+void SwCursorShell::MarkListLevel( const OUString& sListId,
+ const int nListLevel )
+{
+ if (sListId == m_sMarkedListId && nListLevel == m_nMarkedListLevel)
+ return;
+
+ if ( !m_sMarkedListId.isEmpty() )
+ mxDoc->MarkListLevel( m_sMarkedListId, m_nMarkedListLevel, false );
+
+ if ( !sListId.isEmpty() )
+ {
+ mxDoc->MarkListLevel( sListId, nListLevel, true );
+ }
+
+ m_sMarkedListId = sListId;
+ m_nMarkedListLevel = nListLevel;
+}
+
+void SwCursorShell::UpdateMarkedListLevel()
+{
+ SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(),
+ GetCursor_()->GetPoint()->nNode);
+
+ if ( !pTextNd )
+ return;
+
+ if (!pTextNd->IsNumbered(GetLayout()))
+ {
+ m_pCurrentCursor->SetInFrontOfLabel_( false );
+ MarkListLevel( OUString(), 0 );
+ }
+ else if ( m_pCurrentCursor->IsInFrontOfLabel() )
+ {
+ if ( pTextNd->IsInList() )
+ {
+ assert(pTextNd->GetActualListLevel() >= 0 &&
+ pTextNd->GetActualListLevel() < MAXLEVEL);
+ MarkListLevel( pTextNd->GetListId(),
+ pTextNd->GetActualListLevel() );
+ }
+ }
+ else
+ {
+ MarkListLevel( OUString(), 0 );
+ }
+}
+
+void SwCursorShell::FirePageChangeEvent(sal_uInt16 nOldPage, sal_uInt16 nNewPage)
+{
+#ifdef ACCESSIBLE_LAYOUT
+ if( Imp()->IsAccessible() )
+ Imp()->FirePageChangeEvent( nOldPage, nNewPage );
+#else
+ (void)nOldPage;
+ (void)nNewPage;
+#endif
+}
+
+void SwCursorShell::FireColumnChangeEvent(sal_uInt16 nOldColumn, sal_uInt16 nNewColumn)
+{
+#ifdef ACCESSIBLE_LAYOUT
+ if( Imp()->IsAccessible() )
+ Imp()->FireColumnChangeEvent( nOldColumn, nNewColumn);
+#else
+ (void)nOldColumn;
+ (void)nNewColumn;
+#endif
+}
+
+void SwCursorShell::FireSectionChangeEvent(sal_uInt16 nOldSection, sal_uInt16 nNewSection)
+{
+#ifdef ACCESSIBLE_LAYOUT
+ if( Imp()->IsAccessible() )
+ Imp()->FireSectionChangeEvent( nOldSection, nNewSection );
+#else
+ (void)nOldSection;
+ (void)nNewSection;
+#endif
+}
+
+bool SwCursorShell::bColumnChange()
+{
+ SwFrame* pCurrFrame = GetCurrFrame(false);
+
+ if (pCurrFrame == nullptr)
+ {
+ return false;
+ }
+
+ SwFrame* pCurrCol=pCurrFrame->FindColFrame();
+
+ while(pCurrCol== nullptr && pCurrFrame!=nullptr )
+ {
+ SwLayoutFrame* pParent = pCurrFrame->GetUpper();
+ if(pParent!=nullptr)
+ {
+ pCurrCol=static_cast<SwFrame*>(pParent)->FindColFrame();
+ pCurrFrame = pParent;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if(m_oldColFrame == pCurrCol)
+ return false;
+ else
+ {
+ m_oldColFrame = pCurrCol;
+ return true;
+ }
+}
+
+bool SwCursorShell::UpDown( bool bUp, sal_uInt16 nCnt )
+{
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+
+ bool bTableMode = IsTableMode();
+ SwShellCursor* pTmpCursor = getShellCursor( true );
+
+ bool bRet = pTmpCursor->UpDown( bUp, nCnt );
+ // #i40019# UpDown should always reset the bInFrontOfLabel flag:
+ bRet |= SetInFrontOfLabel(false);
+
+ if( m_pBlockCursor )
+ m_pBlockCursor->clearPoints();
+
+ if( bRet )
+ {
+ m_eMvState = CursorMoveState::UpDown; // status for Cursor travelling - GetModelPositionForViewPoint
+ if( !ActionPend() )
+ {
+ CursorFlag eUpdateMode = SwCursorShell::SCROLLWIN;
+ if( !bTableMode )
+ eUpdateMode = static_cast<CursorFlag>(eUpdateMode
+ | SwCursorShell::UPDOWN | SwCursorShell::CHKRANGE);
+ UpdateCursor( o3tl::narrowing<sal_uInt16>(eUpdateMode) );
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::LRMargin( bool bLeft, bool bAPI)
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ CurrShell aCurr( this );
+ m_eMvState = CursorMoveState::LeftMargin; // status for Cursor travelling - GetModelPositionForViewPoint
+
+ const bool bTableMode = IsTableMode();
+ SwShellCursor* pTmpCursor = getShellCursor( true );
+
+ if( m_pBlockCursor )
+ m_pBlockCursor->clearPoints();
+
+ const bool bWasAtLM = GetCursor_()->IsAtLeftRightMargin(*GetLayout(), true, bAPI);
+
+ bool bRet = pTmpCursor->LeftRightMargin(*GetLayout(), bLeft, bAPI);
+
+ if ( bLeft && !bTableMode && bRet && bWasAtLM && !GetCursor_()->HasMark() )
+ {
+ const SwTextNode * pTextNd = GetCursor_()->GetNode().GetTextNode();
+ assert(sw::GetParaPropsNode(*GetLayout(), GetCursor_()->GetPoint()->nNode) == pTextNd);
+ if ( pTextNd && pTextNd->HasVisibleNumberingOrBullet() )
+ SetInFrontOfLabel( true );
+ }
+ else if ( !bLeft )
+ {
+ bRet = SetInFrontOfLabel( false ) || bRet;
+ }
+
+ if( bRet )
+ {
+ UpdateCursor();
+ }
+ return bRet;
+}
+
+bool SwCursorShell::IsAtLRMargin( bool bLeft, bool bAPI ) const
+{
+ const SwShellCursor* pTmpCursor = getShellCursor( true );
+ return pTmpCursor->IsAtLeftRightMargin(*GetLayout(), bLeft, bAPI);
+}
+
+bool SwCursorShell::SttEndDoc( bool bStt )
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+
+ SwShellCursor* pTmpCursor = m_pBlockCursor ? &m_pBlockCursor->getShellCursor() : m_pCurrentCursor;
+ bool bRet = pTmpCursor->SttEndDoc( bStt );
+ if( bRet )
+ {
+ if( bStt )
+ pTmpCursor->GetPtPos().setY( 0 ); // set to 0 explicitly (table header)
+ if( m_pBlockCursor )
+ {
+ m_pBlockCursor->clearPoints();
+ RefreshBlockCursor();
+ }
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ }
+ return bRet;
+}
+
+void SwCursorShell::ExtendedSelectAll(bool bFootnotes)
+{
+ SwNodes& rNodes = GetDoc()->GetNodes();
+ SwPosition* pPos = m_pCurrentCursor->GetPoint();
+ pPos->nNode = bFootnotes ? rNodes.GetEndOfPostIts() : rNodes.GetEndOfAutotext();
+ pPos->nContent.Assign( rNodes.GoNext( &pPos->nNode ), 0 );
+ pPos = m_pCurrentCursor->GetMark();
+ pPos->nNode = rNodes.GetEndOfContent();
+ SwContentNode* pCNd = SwNodes::GoPrevious( &pPos->nNode );
+ pPos->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
+}
+
+bool SwCursorShell::ExtendedSelectedAll()
+{
+ SwNodes& rNodes = GetDoc()->GetNodes();
+ SwNodeIndex nNode = rNodes.GetEndOfAutotext();
+ SwContentNode* pStart = rNodes.GoNext(&nNode);
+ if (!pStart)
+ return false;
+
+ nNode = rNodes.GetEndOfContent();
+ SwContentNode* pEnd = SwNodes::GoPrevious(&nNode);
+ if (!pEnd)
+ return false;
+
+ SwPosition aStart(*pStart, 0);
+ SwPosition aEnd(*pEnd, pEnd->Len());
+ SwShellCursor* pShellCursor = getShellCursor(false);
+ return aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End();
+}
+
+bool SwCursorShell::StartsWithTable()
+{
+ SwNodes& rNodes = GetDoc()->GetNodes();
+ SwNodeIndex nNode(rNodes.GetEndOfExtras());
+ SwContentNode* pContentNode = rNodes.GoNext(&nNode);
+ return pContentNode->FindTableNode();
+}
+
+bool SwCursorShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage )
+{
+ bool bRet = false;
+
+ // never jump of section borders at selection
+ if( !m_pCurrentCursor->HasMark() || !m_pCurrentCursor->IsNoContent() )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ CurrShell aCurr( this );
+
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ Point& rPt = m_pCurrentCursor->GetPtPos();
+ std::pair<Point, bool> tmp(rPt, false);
+ SwContentFrame * pFrame = m_pCurrentCursor->GetContentNode()->
+ getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
+ if( pFrame && GetFrameInPage( pFrame, fnWhichPage, fnPosPage, m_pCurrentCursor ) &&
+ !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos ))
+ {
+ UpdateCursor();
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::isInHiddenTextFrame(SwShellCursor* pShellCursor)
+{
+ SwContentNode *pCNode = pShellCursor->GetContentNode();
+ std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false);
+ SwContentFrame *const pFrame = pCNode
+ ? pCNode->getLayoutFrame(GetLayout(), pShellCursor->GetPoint(), &tmp)
+ : nullptr;
+ return !pFrame || (pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow());
+}
+
+// sw_redlinehide: this should work for all cases: GoCurrPara, GoNextPara, GoPrevPara
+static bool IsAtStartOrEndOfFrame(SwCursorShell const*const pShell,
+ SwShellCursor const*const pShellCursor, SwMoveFnCollection const& fnPosPara)
+{
+ SwContentNode *const pCNode = pShellCursor->GetContentNode();
+ assert(pCNode); // surely can't have moved otherwise?
+ std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false);
+ SwContentFrame const*const pFrame = pCNode->getLayoutFrame(
+ pShell->GetLayout(), pShellCursor->GetPoint(), &tmp);
+ if (!pFrame || !pFrame->IsTextFrame())
+ {
+ return false;
+ }
+ SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(*pFrame));
+ TextFrameIndex const ix(rTextFrame.MapModelToViewPos(*pShellCursor->GetPoint()));
+ if (&fnParaStart == &fnPosPara)
+ {
+ return ix == TextFrameIndex(0);
+ }
+ else
+ {
+ assert(&fnParaEnd == &fnPosPara);
+ return ix == TextFrameIndex(rTextFrame.GetText().getLength());
+ }
+}
+
+bool SwCursorShell::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & fnPosPara )
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ SwShellCursor* pTmpCursor = getShellCursor( true );
+ bool bRet = pTmpCursor->MovePara( fnWhichPara, fnPosPara );
+ if( bRet )
+ {
+ //keep going until we get something visible, i.e. skip
+ //over hidden paragraphs, don't get stuck at the start
+ //which is what SwCursorShell::UpdateCursorPos will reset
+ //the position to if we pass it a position in an
+ //invisible hidden paragraph field
+ while (isInHiddenTextFrame(pTmpCursor)
+ || !IsAtStartOrEndOfFrame(this, pTmpCursor, fnPosPara))
+ {
+ if (!pTmpCursor->MovePara(fnWhichPara, fnPosPara))
+ break;
+ }
+
+ UpdateCursor();
+ }
+ return bRet;
+}
+
+bool SwCursorShell::MoveSection( SwWhichSection fnWhichSect,
+ SwMoveFnCollection const & fnPosSect)
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ SwCursor* pTmpCursor = getShellCursor( true );
+ bool bRet = pTmpCursor->MoveSection( fnWhichSect, fnPosSect );
+ if( bRet )
+ UpdateCursor();
+ return bRet;
+
+}
+
+// position cursor
+
+static SwFrame* lcl_IsInHeaderFooter( const SwNodeIndex& rIdx, Point& rPt )
+{
+ SwFrame* pFrame = nullptr;
+ SwContentNode* pCNd = rIdx.GetNode().GetContentNode();
+ if( pCNd )
+ {
+ std::pair<Point, bool> tmp(rPt, false);
+ SwContentFrame *pContentFrame = pCNd->getLayoutFrame(
+ pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ pFrame = pContentFrame ? pContentFrame->GetUpper() : nullptr;
+ while( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() )
+ pFrame = pFrame->IsFlyFrame() ? static_cast<SwFlyFrame*>(pFrame)->AnchorFrame()
+ : pFrame->GetUpper();
+ }
+ return pFrame;
+}
+
+bool SwCursorShell::IsInHeaderFooter( bool* pbInHeader ) const
+{
+ Point aPt;
+ SwFrame* pFrame = ::lcl_IsInHeaderFooter( m_pCurrentCursor->GetPoint()->nNode, aPt );
+ if( pFrame && pbInHeader )
+ *pbInHeader = pFrame->IsHeaderFrame();
+ return nullptr != pFrame;
+}
+
+int SwCursorShell::SetCursor( const Point &rLPt, bool bOnlyText, bool bBlock )
+{
+ CurrShell aCurr( this );
+
+ SwShellCursor* pCursor = getShellCursor( bBlock );
+ SwPosition aPos( *pCursor->GetPoint() );
+ Point aPt( rLPt );
+ Point & rCurrentCursorPt = pCursor->GetPtPos();
+ SwCursorMoveState aTmpState( IsTableMode() ? CursorMoveState::TableSel :
+ bOnlyText ? CursorMoveState::SetOnlyText : CursorMoveState::NONE );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+
+ SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
+
+ if ( pTextNd && !IsTableMode() &&
+ // #i37515# No bInFrontOfLabel during selection
+ !pCursor->HasMark() &&
+ pTextNd->HasVisibleNumberingOrBullet() )
+ {
+ aTmpState.m_bInFrontOfLabel = true; // #i27615#
+ }
+ else
+ {
+ aTmpState.m_bInFrontOfLabel = false;
+ }
+
+ int bRet = CRSR_POSOLD |
+ ( GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState )
+ ? 0 : CRSR_POSCHG );
+
+ const bool bOldInFrontOfLabel = IsInFrontOfLabel();
+ const bool bNewInFrontOfLabel = aTmpState.m_bInFrontOfLabel;
+
+ pCursor->SetCursorBidiLevel( aTmpState.m_nCursorBidiLevel );
+
+ if( CursorMoveState::RightMargin == aTmpState.m_eState )
+ m_eMvState = CursorMoveState::RightMargin;
+ // is the new position in header or footer?
+ SwFrame* pFrame = lcl_IsInHeaderFooter( aPos.nNode, aPt );
+ if( IsTableMode() && !pFrame && aPos.nNode.GetNode().StartOfSectionNode() ==
+ pCursor->GetPoint()->nNode.GetNode().StartOfSectionNode() )
+ // same table column and not in header/footer -> back
+ return bRet;
+
+ if( m_pBlockCursor && bBlock )
+ {
+ m_pBlockCursor->setEndPoint( rLPt );
+ if( !pCursor->HasMark() )
+ m_pBlockCursor->setStartPoint( rLPt );
+ else if( !m_pBlockCursor->getStartPoint() )
+ m_pBlockCursor->setStartPoint( pCursor->GetMkPos() );
+ }
+ if( !pCursor->HasMark() )
+ {
+ // is at the same position and if in header/footer -> in the same
+ if( aPos == *pCursor->GetPoint() &&
+ bOldInFrontOfLabel == bNewInFrontOfLabel )
+ {
+ if( pFrame )
+ {
+ if( pFrame->getFrameArea().Contains( rCurrentCursorPt ))
+ return bRet;
+ }
+ else if( aPos.nNode.GetNode().IsContentNode() )
+ {
+ // in the same frame?
+ std::pair<Point, bool> tmp(m_aCharRect.Pos(), false);
+ SwFrame* pOld = static_cast<SwContentNode&>(aPos.nNode.GetNode()).getLayoutFrame(
+ GetLayout(), nullptr, &tmp);
+ tmp.first = aPt;
+ SwFrame* pNew = static_cast<SwContentNode&>(aPos.nNode.GetNode()).getLayoutFrame(
+ GetLayout(), nullptr, &tmp);
+ if( pNew == pOld )
+ return bRet;
+ }
+ }
+ }
+ else
+ {
+ // SSelection over not allowed sections or if in header/footer -> different
+ if( !CheckNodesRange( aPos.nNode, pCursor->GetMark()->nNode, true )
+ || ( pFrame && !pFrame->getFrameArea().Contains( pCursor->GetMkPos() ) ))
+ return bRet;
+
+ // is at same position but not in header/footer
+ if( aPos == *pCursor->GetPoint() )
+ return bRet;
+ }
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ SwCursorSaveState aSaveState( *pCursor );
+
+ *pCursor->GetPoint() = aPos;
+ rCurrentCursorPt = aPt;
+
+ // #i41424# Only update the marked number levels if necessary
+ // Force update of marked number levels if necessary.
+ if ( bNewInFrontOfLabel || bOldInFrontOfLabel )
+ m_pCurrentCursor->SetInFrontOfLabel_( !bNewInFrontOfLabel );
+ SetInFrontOfLabel( bNewInFrontOfLabel );
+
+ if( !pCursor->IsSelOvr( SwCursorSelOverFlags::ChangePos ) )
+ {
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE );
+ bRet &= ~CRSR_POSOLD;
+ }
+ else if( bOnlyText && !m_pCurrentCursor->HasMark() )
+ {
+ if( FindValidContentNode( bOnlyText ) )
+ {
+ // position cursor in a valid content
+ if( aPos == *pCursor->GetPoint() )
+ bRet = CRSR_POSOLD;
+ else
+ {
+ UpdateCursor();
+ bRet &= ~CRSR_POSOLD;
+ }
+ }
+ else
+ {
+ // there is no valid content -> hide cursor
+ m_pVisibleCursor->Hide(); // always hide visible cursor
+ m_eMvState = CursorMoveState::NONE; // status for Cursor travelling
+ m_bAllProtect = true;
+ if( GetDoc()->GetDocShell() )
+ {
+ GetDoc()->GetDocShell()->SetReadOnlyUI();
+ CallChgLnk(); // notify UI
+ }
+ }
+ }
+ return bRet;
+}
+
+void SwCursorShell::TableCursorToCursor()
+{
+ assert(m_pTableCursor);
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+}
+
+void SwCursorShell::BlockCursorToCursor()
+{
+ assert(m_pBlockCursor);
+ if( m_pBlockCursor && !HasSelection() )
+ {
+ SwPaM& rPam = m_pBlockCursor->getShellCursor();
+ m_pCurrentCursor->SetMark();
+ *m_pCurrentCursor->GetPoint() = *rPam.GetPoint();
+ if( rPam.HasMark() )
+ *m_pCurrentCursor->GetMark() = *rPam.GetMark();
+ else
+ m_pCurrentCursor->DeleteMark();
+ }
+ delete m_pBlockCursor;
+ m_pBlockCursor = nullptr;
+}
+
+void SwCursorShell::CursorToBlockCursor()
+{
+ if( !m_pBlockCursor )
+ {
+ SwPosition aPos( *m_pCurrentCursor->GetPoint() );
+ m_pBlockCursor = new SwBlockCursor( *this, aPos );
+ SwShellCursor &rBlock = m_pBlockCursor->getShellCursor();
+ rBlock.GetPtPos() = m_pCurrentCursor->GetPtPos();
+ if( m_pCurrentCursor->HasMark() )
+ {
+ rBlock.SetMark();
+ *rBlock.GetMark() = *m_pCurrentCursor->GetMark();
+ rBlock.GetMkPos() = m_pCurrentCursor->GetMkPos();
+ }
+ }
+ m_pBlockCursor->clearPoints();
+ RefreshBlockCursor();
+}
+
+void SwCursorShell::ClearMark()
+{
+ // is there any GetMark?
+ if( m_pTableCursor )
+ {
+ std::vector<SwPaM*> vCursors;
+ for(auto& rCursor : m_pCurrentCursor->GetRingContainer())
+ if(&rCursor != m_pCurrentCursor)
+ vCursors.push_back(&rCursor);
+ for(auto pCursor : vCursors)
+ delete pCursor;
+ m_pTableCursor->DeleteMark();
+
+ m_pCurrentCursor->DeleteMark();
+
+ *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
+ m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos();
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+ m_pCurrentCursor->SwSelPaintRects::Show();
+ }
+ else
+ {
+ if( !m_pCurrentCursor->HasMark() )
+ return;
+ m_pCurrentCursor->DeleteMark();
+ if( !m_nCursorMove )
+ m_pCurrentCursor->SwSelPaintRects::Show();
+ }
+}
+
+void SwCursorShell::NormalizePam(bool bPointFirst)
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ m_pCurrentCursor->Normalize(bPointFirst);
+}
+
+void SwCursorShell::SwapPam()
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ m_pCurrentCursor->Exchange();
+}
+
+//TODO: provide documentation
+/** Search in the selected area for a Selection that covers the given point.
+
+ It checks if a Selection exists but does
+ not move the current cursor.
+
+ @param rPt The point to search at.
+ @param bTstHit ???
+*/
+bool SwCursorShell::TestCurrPam(
+ const Point & rPt,
+ bool bTstHit )
+{
+ CurrShell aCurr( this );
+
+ // check if the SPoint is in a table selection
+ if( m_pTableCursor )
+ return m_pTableCursor->Contains( rPt );
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ // search position <rPt> in document
+ SwPosition aPtPos( *m_pCurrentCursor->GetPoint() );
+ Point aPt( rPt );
+
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+ if ( !GetLayout()->GetModelPositionForViewPoint( &aPtPos, aPt, &aTmpState ) && bTstHit )
+ return false;
+
+ // search in all selections for this position
+ SwShellCursor* pCmp = m_pCurrentCursor; // keep the pointer on cursor
+ do
+ {
+ if (pCmp->HasMark() && *pCmp->Start() <= aPtPos && *pCmp->End() > aPtPos)
+ return true; // return without update
+ pCmp = pCmp->GetNext();
+ } while (m_pCurrentCursor != pCmp);
+ return false;
+}
+
+void SwCursorShell::KillPams()
+{
+ // Does any exist for deletion?
+ if( !m_pTableCursor && !m_pBlockCursor && !m_pCurrentCursor->IsMultiSelection() )
+ return;
+
+ while( m_pCurrentCursor->GetNext() != m_pCurrentCursor )
+ delete m_pCurrentCursor->GetNext();
+ m_pCurrentCursor->SetColumnSelection( false );
+
+ if( m_pTableCursor )
+ {
+ // delete the ring of cursors
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
+ m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos();
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+ }
+ else if( m_pBlockCursor )
+ {
+ // delete the ring of cursors
+ m_pCurrentCursor->DeleteMark();
+ SwShellCursor &rBlock = m_pBlockCursor->getShellCursor();
+ *m_pCurrentCursor->GetPoint() = *rBlock.GetPoint();
+ m_pCurrentCursor->GetPtPos() = rBlock.GetPtPos();
+ rBlock.DeleteMark();
+ m_pBlockCursor->clearPoints();
+ }
+ UpdateCursor( SwCursorShell::SCROLLWIN );
+}
+
+int SwCursorShell::CompareCursorStackMkCurrPt() const
+{
+ int nRet = 0;
+ const SwPosition *pFirst = nullptr, *pSecond = nullptr;
+ const SwCursor *pCur = GetCursor(), *pStack = m_pStackCursor;
+ // cursor on stack is needed if we compare against stack
+ if( pStack )
+ {
+ pFirst = pStack->GetMark();
+ pSecond = pCur->GetPoint();
+ }
+ if( !pFirst || !pSecond )
+ nRet = INT_MAX;
+ else if( *pFirst < *pSecond )
+ nRet = -1;
+ else if( *pFirst == *pSecond )
+ nRet = 0;
+ else
+ nRet = 1;
+ return nRet;
+}
+
+bool SwCursorShell::IsSelOnePara() const
+{
+ if (m_pCurrentCursor->IsMultiSelection())
+ {
+ return false;
+ }
+ if (m_pCurrentCursor->GetPoint()->nNode == m_pCurrentCursor->GetMark()->nNode)
+ {
+ return true;
+ }
+ if (GetLayout()->HasMergedParas())
+ {
+ SwContentFrame const*const pFrame(GetCurrFrame(false));
+ auto const n(m_pCurrentCursor->GetMark()->nNode.GetIndex());
+ return FrameContainsNode(*pFrame, n);
+ }
+ return false;
+}
+
+bool SwCursorShell::IsSttPara() const
+{
+ if (GetLayout()->HasMergedParas())
+ {
+ SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode());
+ if (pNode)
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(
+ pNode->getLayoutFrame(GetLayout())));
+ if (pFrame)
+ {
+ return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint())
+ == TextFrameIndex(0);
+ }
+ }
+ }
+ return m_pCurrentCursor->GetPoint()->nContent == 0;
+}
+
+bool SwCursorShell::IsEndPara() const
+{
+ if (GetLayout()->HasMergedParas())
+ {
+ SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode());
+ if (pNode)
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(
+ pNode->getLayoutFrame(GetLayout())));
+ if (pFrame)
+ {
+ return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint())
+ == TextFrameIndex(pFrame->GetText().getLength());
+ }
+ }
+ }
+ return m_pCurrentCursor->GetPoint()->nContent == m_pCurrentCursor->GetContentNode()->Len();
+}
+
+bool SwCursorShell::IsEndOfTable() const
+{
+ if (IsTableMode() || IsBlockMode() || !IsEndPara())
+ {
+ return false;
+ }
+ SwTableNode const*const pTableNode( IsCursorInTable() );
+ if (!pTableNode)
+ {
+ return false;
+ }
+ SwEndNode const*const pEndTableNode(pTableNode->EndOfSectionNode());
+ SwNodeIndex const lastNode(*pEndTableNode, -2);
+ SAL_WARN_IF(!lastNode.GetNode().GetTextNode(), "sw.core",
+ "text node expected");
+ return (lastNode == m_pCurrentCursor->GetPoint()->nNode);
+}
+
+bool SwCursorShell::IsCursorInFootnote() const
+{
+ SwStartNodeType aStartNodeType = m_pCurrentCursor->GetNode().StartOfSectionNode()->GetStartNodeType();
+ return aStartNodeType == SwStartNodeType::SwFootnoteStartNode;
+}
+
+bool SwCursorShell::IsInFrontOfLabel() const
+{
+ return m_pCurrentCursor->IsInFrontOfLabel();
+}
+
+bool SwCursorShell::SetInFrontOfLabel( bool bNew )
+{
+ if ( bNew != IsInFrontOfLabel() )
+ {
+ m_pCurrentCursor->SetInFrontOfLabel_( bNew );
+ UpdateMarkedListLevel();
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+void collectUIInformation(const OUString& aPage)
+{
+ EventDescription aDescription;
+ aDescription.aAction = "GOTO";
+ aDescription.aParameters = {{"PAGE", aPage}};
+ aDescription.aID = "writer_edit";
+ aDescription.aKeyWord = "SwEditWinUIObject";
+ aDescription.aParent = "MainWindow";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+bool SwCursorShell::GotoPage( sal_uInt16 nPage )
+{
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ bool bRet = GetLayout()->SetCurrPage( m_pCurrentCursor, nPage ) &&
+ !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos );
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+
+ collectUIInformation(OUString::number(nPage));
+ return bRet;
+}
+
+void SwCursorShell::GetCharRectAt(SwRect& rRect, const SwPosition* pPos)
+{
+ SwContentFrame* pFrame = GetCurrFrame();
+ pFrame->GetCharRect( rRect, *pPos );
+}
+
+void SwCursorShell::GetPageNum( sal_uInt16 &rnPhyNum, sal_uInt16 &rnVirtNum,
+ bool bAtCursorPos, const bool bCalcFrame )
+{
+ CurrShell aCurr( this );
+ // page number: first visible page or the one at the cursor
+ const SwContentFrame* pCFrame;
+ const SwPageFrame *pPg = nullptr;
+
+ if( !bAtCursorPos || nullptr == (pCFrame = GetCurrFrame( bCalcFrame )) ||
+ nullptr == (pPg = pCFrame->FindPageFrame()) )
+ {
+ pPg = Imp()->GetFirstVisPage(GetOut());
+ while( pPg && pPg->IsEmptyPage() )
+ pPg = static_cast<const SwPageFrame *>(pPg->GetNext());
+ }
+ // pPg has to exist with a default of 1 for the special case "Writerstart"
+ rnPhyNum = pPg? pPg->GetPhyPageNum() : 1;
+ rnVirtNum = pPg? pPg->GetVirtPageNum() : 1;
+}
+
+sal_uInt16 SwCursorShell::GetPageNumSeqNonEmpty()
+{
+ CurrShell aCurr(this);
+ // page number: first visible page or the one at the cursor
+ const SwContentFrame* pCFrame = GetCurrFrame(/*bCalcFrame*/true);
+ const SwPageFrame* pPg = nullptr;
+
+ if (pCFrame == nullptr || nullptr == (pPg = pCFrame->FindPageFrame()))
+ {
+ pPg = Imp()->GetFirstVisPage(GetOut());
+ while (pPg && pPg->IsEmptyPage())
+ pPg = static_cast<const SwPageFrame*>(pPg->GetNext());
+ }
+
+ sal_uInt16 nPageNo = 0;
+ while (pPg)
+ {
+ if (!pPg->IsEmptyPage())
+ ++nPageNo;
+ pPg = static_cast<const SwPageFrame*>(pPg->GetPrev());
+ }
+ return nPageNo;
+}
+
+sal_uInt16 SwCursorShell::GetNextPrevPageNum( bool bNext )
+{
+ CurrShell aCurr( this );
+ // page number: first visible page or the one at the cursor
+ const SwPageFrame *pPg = Imp()->GetFirstVisPage(GetOut());
+ if( pPg )
+ {
+ const SwTwips nPageTop = pPg->getFrameArea().Top();
+
+ if( bNext )
+ {
+ // go to next view layout row:
+ do
+ {
+ pPg = static_cast<const SwPageFrame *>(pPg->GetNext());
+ }
+ while( pPg && pPg->getFrameArea().Top() == nPageTop );
+
+ while( pPg && pPg->IsEmptyPage() )
+ pPg = static_cast<const SwPageFrame *>(pPg->GetNext());
+ }
+ else
+ {
+ // go to previous view layout row:
+ do
+ {
+ pPg = static_cast<const SwPageFrame *>(pPg->GetPrev());
+ }
+ while( pPg && pPg->getFrameArea().Top() == nPageTop );
+
+ while( pPg && pPg->IsEmptyPage() )
+ pPg = static_cast<const SwPageFrame *>(pPg->GetPrev());
+ }
+ }
+ // pPg has to exist with a default of 1 for the special case "Writerstart"
+ return pPg ? pPg->GetPhyPageNum() : USHRT_MAX;
+}
+
+sal_uInt16 SwCursorShell::GetPageCnt()
+{
+ CurrShell aCurr( this );
+ // return number of pages
+ return GetLayout()->GetPageNum();
+}
+
+OUString SwCursorShell::getPageRectangles()
+{
+ CurrShell aCurr(this);
+ SwRootFrame* pLayout = GetLayout();
+ OUStringBuffer aBuf;
+ for (const SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ aBuf.append(pFrame->getFrameArea().Left());
+ aBuf.append(", ");
+ aBuf.append(pFrame->getFrameArea().Top());
+ aBuf.append(", ");
+ aBuf.append(pFrame->getFrameArea().Width());
+ aBuf.append(", ");
+ aBuf.append(pFrame->getFrameArea().Height());
+ aBuf.append("; ");
+ }
+ if (!aBuf.isEmpty())
+ aBuf.setLength( aBuf.getLength() - 2); // remove the last "; "
+ return aBuf.makeStringAndClear();
+}
+
+void SwCursorShell::NotifyCursor(SfxViewShell* pOtherShell) const
+{
+ auto pView = const_cast<SdrView*>(GetDrawView());
+ if (pView->GetTextEditObject())
+ {
+ // Blinking cursor.
+ EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
+ rEditView.RegisterOtherShell(pOtherShell);
+ rEditView.ShowCursor();
+ rEditView.RegisterOtherShell(nullptr);
+ // Text selection, if any.
+ rEditView.DrawSelectionXOR(pOtherShell);
+
+ // Shape text lock.
+ if (OutlinerView* pOutlinerView = pView->GetTextEditOutlinerView())
+ {
+ OString sRect = pOutlinerView->GetOutputArea().toString();
+ SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRect);
+ }
+ }
+ else
+ {
+ // Cursor position.
+ m_pVisibleCursor->SetPosAndShow(pOtherShell);
+ // Cursor visibility.
+ if (GetSfxViewShell() != pOtherShell)
+ {
+ OString aPayload = OString::boolean(m_bSVCursorVis);
+ SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
+ }
+ // Text selection.
+ m_pCurrentCursor->Show(pOtherShell);
+ // Graphic selection.
+ pView->AdjustMarkHdl(pOtherShell);
+ }
+}
+
+/// go to the next SSelection
+bool SwCursorShell::GoNextCursor()
+{
+ if( !m_pCurrentCursor->IsMultiSelection() )
+ return false;
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ m_pCurrentCursor = m_pCurrentCursor->GetNext();
+
+ // #i24086#: show also all others
+ if( !ActionPend() )
+ {
+ UpdateCursor();
+ m_pCurrentCursor->Show(nullptr);
+ }
+ return true;
+}
+
+/// go to the previous SSelection
+bool SwCursorShell::GoPrevCursor()
+{
+ if( !m_pCurrentCursor->IsMultiSelection() )
+ return false;
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ m_pCurrentCursor = m_pCurrentCursor->GetPrev();
+
+ // #i24086#: show also all others
+ if( !ActionPend() )
+ {
+ UpdateCursor();
+ m_pCurrentCursor->Show(nullptr);
+ }
+ return true;
+}
+
+void SwCursorShell::GoNextPrevCursorSetSearchLabel(const bool bNext)
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ if( !m_pCurrentCursor->IsMultiSelection() )
+ {
+ if( !m_pCurrentCursor->HasMark() )
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return;
+ }
+
+ if (bNext)
+ GoNextCursor();
+ else
+ GoPrevCursor();
+}
+
+void SwCursorShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect)
+{
+ comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll());
+ CurrShell aCurr( this );
+
+ // always switch off all cursors when painting
+ SwRect aRect( rRect );
+
+ bool bVis = false;
+ // if a cursor is visible then hide the SV cursor
+ if( m_pVisibleCursor->IsVisible() && !aRect.Overlaps( m_aCharRect ) )
+ {
+ bVis = true;
+ m_pVisibleCursor->Hide();
+ }
+
+ // re-paint area
+ SwViewShell::Paint(rRenderContext, rRect);
+
+ if( m_bHasFocus && !m_bBasicHideCursor )
+ {
+ SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
+
+ if( !ActionPend() )
+ {
+ // so that right/bottom borders will not be cropped
+ pCurrentCursor->Invalidate( VisArea() );
+ pCurrentCursor->Show(nullptr);
+ }
+ else
+ pCurrentCursor->Invalidate( aRect );
+
+ }
+
+ if (SwPostItMgr* pPostItMgr = GetPostItMgr())
+ {
+ // No point in showing the cursor for Writer text when there is an
+ // active annotation edit.
+ if (bVis)
+ bVis = !pPostItMgr->HasActiveSidebarWin();
+ }
+
+ if( m_bSVCursorVis && bVis ) // also show SV cursor again
+ m_pVisibleCursor->Show();
+}
+
+void SwCursorShell::VisPortChgd( const SwRect & rRect )
+{
+ CurrShell aCurr( this );
+ bool bVis; // switch off all cursors when scrolling
+
+ // if a cursor is visible then hide the SV cursor
+ bVis = m_pVisibleCursor->IsVisible();
+ if( bVis )
+ m_pVisibleCursor->Hide();
+
+ m_bVisPortChgd = true;
+ m_aOldRBPos.setX(VisArea().Right());
+ m_aOldRBPos.setY(VisArea().Bottom());
+
+ // For not having problems with the SV cursor, Update() is called for the
+ // Window in SwViewShell::VisPo...
+ // During painting no selections should be shown, thus the call is encapsulated. <- TODO: old artefact?
+ SwViewShell::VisPortChgd( rRect ); // move area
+
+ if( m_bSVCursorVis && bVis ) // show SV cursor again
+ m_pVisibleCursor->Show();
+
+ if( m_nCursorMove )
+ m_bInCMvVisportChgd = true;
+
+ m_bVisPortChgd = false;
+}
+
+/** Set the cursor back into content.
+
+ This should only be called if the cursor was move somewhere else (e.g. when
+ deleting a border). The new position is calculated from its current position
+ in the layout.
+*/
+void SwCursorShell::UpdateCursorPos()
+{
+ CurrShell aCurr( this );
+ ++mnStartAction;
+ SwShellCursor* pShellCursor = getShellCursor( true );
+ Size aOldSz( GetDocSize() );
+
+ if( isInHiddenTextFrame(pShellCursor) )
+ {
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+ GetLayout()->GetModelPositionForViewPoint( pShellCursor->GetPoint(), pShellCursor->GetPtPos(),
+ &aTmpState );
+ pShellCursor->DeleteMark();
+ }
+ IGrammarContact *pGrammarContact = GetDoc() ? GetDoc()->getGrammarContact() : nullptr;
+ if( pGrammarContact )
+ pGrammarContact->updateCursorPosition( *m_pCurrentCursor->GetPoint() );
+ --mnStartAction;
+ if( aOldSz != GetDocSize() )
+ SizeChgNotify();
+}
+
+// #i65475# - if Point/Mark in hidden sections, move them out
+static bool lcl_CheckHiddenSection( SwNodeIndex& rIdx )
+{
+ bool bOk = true;
+ const SwSectionNode* pSectNd = rIdx.GetNode().FindSectionNode();
+ if( pSectNd && pSectNd->GetSection().IsHiddenFlag() )
+ {
+ SwNodeIndex aTmp( *pSectNd );
+ const SwNode* pFrameNd =
+ rIdx.GetNodes().FindPrvNxtFrameNode( aTmp, pSectNd->EndOfSectionNode() );
+ bOk = pFrameNd != nullptr;
+ SAL_WARN_IF(!bOk, "sw.core", "found no Node with Frames");
+ rIdx = aTmp;
+ }
+ return bOk;
+}
+
+/// Try to set the cursor to the next visible content node.
+static void lcl_CheckHiddenPara( SwPosition& rPos )
+{
+ SwNodeIndex aTmp( rPos.nNode );
+ SwTextNode* pTextNd = aTmp.GetNode().GetTextNode();
+ while( pTextNd && pTextNd->HasHiddenCharAttribute( true ) )
+ {
+ SwContentNode* pContent = aTmp.GetNodes().GoNext( &aTmp );
+ if ( pContent && pContent->IsTextNode() )
+ pTextNd = pContent->GetTextNode();
+ else
+ pTextNd = nullptr;
+ }
+
+ if ( pTextNd )
+ rPos = SwPosition( aTmp, SwIndex( pTextNd, 0 ) );
+}
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+namespace {
+
+// #i27301# - helper class that notifies the accessibility about invalid text
+// selections in its destructor
+class SwNotifyAccAboutInvalidTextSelections
+{
+ private:
+ SwCursorShell& mrCursorSh;
+
+ public:
+ explicit SwNotifyAccAboutInvalidTextSelections( SwCursorShell& _rCursorSh )
+ : mrCursorSh( _rCursorSh )
+ {}
+
+ ~SwNotifyAccAboutInvalidTextSelections() COVERITY_NOEXCEPT_FALSE
+ {
+ mrCursorSh.InvalidateAccessibleParaTextSelection();
+ }
+};
+
+}
+#endif
+
+void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd )
+{
+ CurrShell aCurr( this );
+ ClearUpCursors();
+
+ if (ActionPend())
+ {
+ if ( eFlags & SwCursorShell::READONLY )
+ m_bIgnoreReadonly = true;
+ return; // if not then no update
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwNotifyAccAboutInvalidTextSelections aInvalidateTextSelections( *this );
+#endif
+
+ if ( m_bIgnoreReadonly )
+ {
+ m_bIgnoreReadonly = false;
+ eFlags |= SwCursorShell::READONLY;
+ }
+
+ if( eFlags & SwCursorShell::CHKRANGE ) // check all cursor moves for
+ CheckRange( m_pCurrentCursor ); // overlapping ranges
+
+ if( !bIdleEnd )
+ CheckTableBoxContent();
+
+ // If the current cursor is in a table and point/mark in different boxes,
+ // then the table mode is active (also if it is already active: m_pTableCursor)
+ SwPaM* pTstCursor = getShellCursor( true );
+ if( pTstCursor->HasMark() && !m_pBlockCursor &&
+ mxDoc->IsIdxInTable( pTstCursor->GetPoint()->nNode ) &&
+ ( m_pTableCursor ||
+ pTstCursor->GetNode().StartOfSectionNode() !=
+ pTstCursor->GetNode( false ).StartOfSectionNode() ) && !mbSelectAll)
+ {
+ SwShellCursor* pITmpCursor = getShellCursor( true );
+ Point aTmpPt( pITmpCursor->GetPtPos() );
+ Point aTmpMk( pITmpCursor->GetMkPos() );
+ SwPosition* pPos = pITmpCursor->GetPoint();
+
+ // Bug 65475 (1999) - if Point/Mark in hidden sections, move them out
+ lcl_CheckHiddenSection( pPos->nNode );
+ lcl_CheckHiddenSection( pITmpCursor->GetMark()->nNode );
+
+ // Move cursor out of hidden paragraphs
+ if ( !GetViewOptions()->IsShowHiddenChar() )
+ {
+ lcl_CheckHiddenPara( *pPos );
+ lcl_CheckHiddenPara( *pITmpCursor->GetMark() );
+ }
+
+ std::pair<Point, bool> const tmp(aTmpPt, false);
+ SwContentFrame *pTableFrame = pPos->nNode.GetNode().GetContentNode()->
+ getLayoutFrame( GetLayout(), pPos, &tmp);
+
+ OSL_ENSURE( pTableFrame, "Table Cursor not in Content ??" );
+
+ // --> Make code robust. The table cursor may point
+ // to a table in a currently inactive header.
+ SwTabFrame *pTab = pTableFrame ? pTableFrame->FindTabFrame() : nullptr;
+
+ if ( pTab && pTab->GetTable()->GetRowsToRepeat() > 0 )
+ {
+ // First check if point is in repeated headline:
+ bool bInRepeatedHeadline = pTab->IsFollow() && pTab->IsInHeadline( *pTableFrame );
+
+ // Second check if mark is in repeated headline:
+ if ( !bInRepeatedHeadline )
+ {
+ std::pair<Point, bool> const tmp1(aTmpMk, false);
+ SwContentFrame* pMarkTableFrame = pITmpCursor->GetContentNode( false )->
+ getLayoutFrame(GetLayout(), pITmpCursor->GetMark(), &tmp1);
+ OSL_ENSURE( pMarkTableFrame, "Table Cursor not in Content ??" );
+
+ if ( pMarkTableFrame )
+ {
+ SwTabFrame* pMarkTab = pMarkTableFrame->FindTabFrame();
+ OSL_ENSURE( pMarkTab, "Table Cursor not in Content ??" );
+
+ // Make code robust:
+ if ( pMarkTab )
+ {
+ bInRepeatedHeadline = pMarkTab->IsFollow() && pMarkTab->IsInHeadline( *pMarkTableFrame );
+ }
+ }
+ }
+
+ // No table cursor in repeated headlines:
+ if ( bInRepeatedHeadline )
+ {
+ pTableFrame = nullptr;
+
+ SwMoveFnCollection const & fnPosSect = *pPos < *pITmpCursor->GetMark()
+ ? fnSectionStart
+ : fnSectionEnd;
+
+ // then only select inside the Box
+ if( m_pTableCursor )
+ {
+ m_pCurrentCursor->SetMark();
+ *m_pCurrentCursor->GetMark() = *m_pTableCursor->GetMark();
+ m_pCurrentCursor->GetMkPos() = m_pTableCursor->GetMkPos();
+ m_pTableCursor->DeleteMark();
+ m_pTableCursor->SwSelPaintRects::Hide();
+ }
+
+ *m_pCurrentCursor->GetPoint() = *m_pCurrentCursor->GetMark();
+ GoCurrSection( *m_pCurrentCursor, fnPosSect );
+ }
+ }
+
+ // we really want a table selection
+ if( pTab && pTableFrame )
+ {
+ if( !m_pTableCursor )
+ {
+ m_pTableCursor = new SwShellTableCursor( *this,
+ *m_pCurrentCursor->GetMark(), m_pCurrentCursor->GetMkPos(),
+ *pPos, aTmpPt );
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SwSelPaintRects::Hide();
+
+ CheckTableBoxContent();
+ if(!m_pTableCursor)
+ {
+ SAL_WARN("sw.core", "fdo#74854: "
+ "this should not happen, but better lose the selection "
+ "rather than crashing");
+ return;
+ }
+ }
+
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_bRealHeight = true;
+ {
+ DisableCallbackAction a(*GetLayout());
+ if (!pTableFrame->GetCharRect( m_aCharRect, *m_pTableCursor->GetPoint(), &aTmpState))
+ {
+ Point aCentrPt( m_aCharRect.Center() );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+ pTableFrame->GetModelPositionForViewPoint(m_pTableCursor->GetPoint(), aCentrPt, &aTmpState);
+ bool const bResult =
+ pTableFrame->GetCharRect(m_aCharRect, *m_pTableCursor->GetPoint());
+ OSL_ENSURE( bResult, "GetCharRect failed." );
+ }
+ }
+
+ m_pVisibleCursor->Hide(); // always hide visible Cursor
+ // scroll Cursor to visible area
+ if( eFlags & SwCursorShell::SCROLLWIN &&
+ (HasSelection() || eFlags & SwCursorShell::READONLY ||
+ !IsCursorReadonly()) )
+ {
+ SwFrame* pBoxFrame = pTableFrame;
+ while( pBoxFrame && !pBoxFrame->IsCellFrame() )
+ pBoxFrame = pBoxFrame->GetUpper();
+ if( pBoxFrame && pBoxFrame->getFrameArea().HasArea() )
+ MakeVisible( pBoxFrame->getFrameArea() );
+ else
+ MakeVisible( m_aCharRect );
+ }
+
+ // let Layout create the Cursors in the Boxes
+ if( m_pTableCursor->IsCursorMovedUpdate() )
+ GetLayout()->MakeTableCursors( *m_pTableCursor );
+ if( m_bHasFocus && !m_bBasicHideCursor )
+ m_pTableCursor->Show(nullptr);
+
+ // set Cursor-Points to the new Positions
+ m_pTableCursor->GetPtPos().setX(m_aCharRect.Left());
+ m_pTableCursor->GetPtPos().setY(m_aCharRect.Top());
+
+ if( m_bSVCursorVis )
+ {
+ m_aCursorHeight.setX(0);
+ m_aCursorHeight.setY(aTmpState.m_aRealHeight.getY() < 0 ?
+ -m_aCharRect.Width() : m_aCharRect.Height());
+ m_pVisibleCursor->Show(); // show again
+ }
+ m_eMvState = CursorMoveState::NONE; // state for cursor travelling - GetModelPositionForViewPoint
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (Imp()->IsAccessible() && m_bSendAccessibleCursorEvents)
+ Imp()->InvalidateAccessibleCursorPosition( pTableFrame );
+#endif
+ return;
+ }
+ }
+
+ if( m_pTableCursor )
+ {
+ // delete Ring
+ while( m_pCurrentCursor->GetNext() != m_pCurrentCursor )
+ delete m_pCurrentCursor->GetNext();
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
+ m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos();
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+ }
+
+ m_pVisibleCursor->Hide(); // always hide visible Cursor
+
+ // are we perhaps in a protected / hidden Section ?
+ {
+ SwShellCursor* pShellCursor = getShellCursor( true );
+ bool bChgState = true;
+ const SwSectionNode* pSectNd = pShellCursor->GetNode().FindSectionNode();
+ if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() ||
+ ( !IsReadOnlyAvailable() &&
+ pSectNd->GetSection().IsProtectFlag() &&
+ ( !mxDoc->GetDocShell() ||
+ !mxDoc->GetDocShell()->IsReadOnly() || m_bAllProtect )) ) )
+ {
+ if( !FindValidContentNode( !HasDrawView() ||
+ 0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
+ {
+ // everything protected/hidden -> special mode
+ if( m_bAllProtect && !IsReadOnlyAvailable() &&
+ pSectNd->GetSection().IsProtectFlag() )
+ bChgState = false;
+ else
+ {
+ m_eMvState = CursorMoveState::NONE; // state for cursor travelling
+ m_bAllProtect = true;
+ if( GetDoc()->GetDocShell() )
+ {
+ GetDoc()->GetDocShell()->SetReadOnlyUI();
+ CallChgLnk(); // notify UI!
+ }
+ return;
+ }
+ }
+ }
+ if( bChgState )
+ {
+ bool bWasAllProtect = m_bAllProtect;
+ m_bAllProtect = false;
+ if( bWasAllProtect && GetDoc()->GetDocShell() &&
+ GetDoc()->GetDocShell()->IsReadOnlyUI() )
+ {
+ GetDoc()->GetDocShell()->SetReadOnlyUI( false );
+ CallChgLnk(); // notify UI!
+ }
+ }
+ }
+
+ UpdateCursorPos();
+
+ // The cursor must always point into content; there's some code
+ // that relies on this. (E.g. in SwEditShell::GetScriptType, which always
+ // loops _behind_ the last node in the selection, which always works if you
+ // are in content.) To achieve this, we'll force cursor(s) to point into
+ // content, if UpdateCursorPos() hasn't already done so.
+ for(SwPaM& rCmp : m_pCurrentCursor->GetRingContainer())
+ {
+ // start will move forwards, end will move backwards
+ bool bPointIsStart = ( rCmp.Start() == rCmp.GetPoint() );
+
+ // move point; forward if it's the start, backwards if it's the end
+ if( ! rCmp.GetPoint()->nNode.GetNode().IsContentNode() )
+ rCmp.Move( bPointIsStart ? fnMoveForward : fnMoveBackward,
+ GoInContent );
+
+ // move mark (if exists); forward if it's the start, else backwards
+ if( rCmp.HasMark() )
+ {
+ if( ! rCmp.GetMark()->nNode.GetNode().IsContentNode() )
+ {
+ rCmp.Exchange();
+ rCmp.Move( !bPointIsStart ? fnMoveForward : fnMoveBackward,
+ GoInContent );
+ rCmp.Exchange();
+ }
+ }
+ }
+
+ SwRect aOld( m_aCharRect );
+ bool bFirst = true;
+ SwContentFrame *pFrame;
+ int nLoopCnt = 100;
+ SwShellCursor* pShellCursor = getShellCursor( true );
+
+ do {
+ bool bAgainst;
+ do {
+ bAgainst = false;
+ std::pair<Point, bool> const tmp1(pShellCursor->GetPtPos(), false);
+ pFrame = pShellCursor->GetContentNode()->getLayoutFrame(GetLayout(),
+ pShellCursor->GetPoint(), &tmp1);
+ // if the Frame doesn't exist anymore, the complete Layout has to be
+ // created, because there used to be a Frame here!
+ if ( !pFrame )
+ {
+ do
+ {
+ CalcLayout();
+ std::pair<Point, bool> const tmp(pShellCursor->GetPtPos(), false);
+ pFrame = pShellCursor->GetContentNode()->getLayoutFrame(
+ GetLayout(), pShellCursor->GetPoint(), &tmp);
+ } while( !pFrame );
+ }
+ else if ( Imp()->IsIdleAction() )
+ // Guarantee everything's properly formatted
+ pFrame->PrepareCursor();
+
+ // In protected Fly? but ignore in case of frame selection
+ if( !IsReadOnlyAvailable() && pFrame->IsProtected() &&
+ ( !Imp()->GetDrawView() ||
+ !Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ) &&
+ (!mxDoc->GetDocShell() ||
+ !mxDoc->GetDocShell()->IsReadOnly() || m_bAllProtect ) )
+ {
+ // look for a valid position
+ bool bChgState = true;
+ if( !FindValidContentNode(!HasDrawView() ||
+ 0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
+ {
+ // everything is protected / hidden -> special Mode
+ if( m_bAllProtect )
+ bChgState = false;
+ else
+ {
+ m_eMvState = CursorMoveState::NONE; // state for cursor travelling
+ m_bAllProtect = true;
+ if( GetDoc()->GetDocShell() )
+ {
+ GetDoc()->GetDocShell()->SetReadOnlyUI();
+ CallChgLnk(); // notify UI!
+ }
+ return;
+ }
+ }
+
+ if( bChgState )
+ {
+ bool bWasAllProtect = m_bAllProtect;
+ m_bAllProtect = false;
+ if( bWasAllProtect && GetDoc()->GetDocShell() &&
+ GetDoc()->GetDocShell()->IsReadOnlyUI() )
+ {
+ GetDoc()->GetDocShell()->SetReadOnlyUI( false );
+ CallChgLnk(); // notify UI!
+ }
+ m_bAllProtect = false;
+ bAgainst = true; // look for the right Frame again
+ }
+ }
+ } while( bAgainst );
+
+ SwCursorMoveState aTmpState( m_eMvState );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+ aTmpState.m_bRealHeight = true;
+ aTmpState.m_bRealWidth = IsOverwriteCursor();
+ aTmpState.m_nCursorBidiLevel = pShellCursor->GetCursorBidiLevel();
+
+ // #i27615#,#i30453#
+ SwSpecialPos aSpecialPos;
+ aSpecialPos.nExtendRange = SwSPExtendRange::BEFORE;
+ if (pShellCursor->IsInFrontOfLabel())
+ {
+ aTmpState.m_pSpecialPos = &aSpecialPos;
+ }
+
+ {
+ DisableCallbackAction a(*GetLayout()); // tdf#91602 prevent recursive Action
+ if (!pFrame->GetCharRect(m_aCharRect, *pShellCursor->GetPoint(), &aTmpState))
+ {
+ Point& rPt = pShellCursor->GetPtPos();
+ rPt = m_aCharRect.Center();
+ pFrame->GetModelPositionForViewPoint( pShellCursor->GetPoint(), rPt, &aTmpState );
+ }
+ }
+ UISizeNotify(); // tdf#96256 update view size
+
+ if( !pShellCursor->HasMark() )
+ m_aCursorHeight = aTmpState.m_aRealHeight;
+ else
+ {
+ m_aCursorHeight.setX(0);
+ m_aCursorHeight.setY(aTmpState.m_aRealHeight.getY() < 0 ?
+ -m_aCharRect.Width() : m_aCharRect.Height());
+ }
+
+ if( !bFirst && aOld == m_aCharRect )
+ break;
+
+ // if the layout says that we are after the 100th iteration still in
+ // flow then we should always take the current position for granted.
+ // (see bug: 29658)
+ if( !--nLoopCnt )
+ {
+ OSL_ENSURE( false, "endless loop? CharRect != OldCharRect ");
+ break;
+ }
+ aOld = m_aCharRect;
+ bFirst = false;
+
+ // update cursor Points to the new Positions
+ pShellCursor->GetPtPos().setX(m_aCharRect.Left());
+ pShellCursor->GetPtPos().setY(m_aCharRect.Top());
+
+ if( !(eFlags & SwCursorShell::UPDOWN )) // delete old Pos. of Up/Down
+ {
+ DisableCallbackAction a(*GetLayout());
+ pFrame->Calc(GetOut());
+ m_nUpDownX = pFrame->IsVertical() ?
+ m_aCharRect.Top() - pFrame->getFrameArea().Top() :
+ m_aCharRect.Left() - pFrame->getFrameArea().Left();
+ }
+
+ // scroll Cursor to visible area
+ if( m_bHasFocus && eFlags & SwCursorShell::SCROLLWIN &&
+ (HasSelection() || eFlags & SwCursorShell::READONLY ||
+ !IsCursorReadonly() || GetViewOptions()->IsSelectionInReadonly()) )
+ {
+ // in case of scrolling this EndAction doesn't show the SV cursor
+ // again, thus save and reset the flag here
+ bool bSav = m_bSVCursorVis;
+ m_bSVCursorVis = false;
+ MakeSelVisible();
+ m_bSVCursorVis = bSav;
+ }
+
+ } while( eFlags & SwCursorShell::SCROLLWIN );
+
+ assert(pFrame);
+
+ if( m_pBlockCursor )
+ RefreshBlockCursor();
+
+ // We should not restrict cursor update to the active view when using LOK
+ bool bCheckFocus = m_bHasFocus || comphelper::LibreOfficeKit::isActive();
+
+ if( !bIdleEnd && bCheckFocus && !m_bBasicHideCursor )
+ {
+ if( m_pTableCursor )
+ m_pTableCursor->SwSelPaintRects::Show();
+ else
+ {
+ m_pCurrentCursor->SwSelPaintRects::Show();
+ if( m_pBlockCursor )
+ {
+ SwShellCursor* pNxt = m_pCurrentCursor->GetNext();
+ while( pNxt && pNxt != m_pCurrentCursor )
+ {
+ pNxt->SwSelPaintRects::Show();
+ pNxt = pNxt->GetNext();
+ }
+ }
+ }
+ }
+
+ m_eMvState = CursorMoveState::NONE; // state for cursor travelling - GetModelPositionForViewPoint
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (Imp()->IsAccessible() && m_bSendAccessibleCursorEvents)
+ Imp()->InvalidateAccessibleCursorPosition( pFrame );
+#endif
+
+ // switch from blinking cursor to read-only-text-selection cursor
+ const sal_uInt64 nBlinkTime = GetOut()->GetSettings().GetStyleSettings().
+ GetCursorBlinkTime();
+
+ if ( (IsCursorReadonly() && GetViewOptions()->IsSelectionInReadonly()) ==
+ ( nBlinkTime != STYLE_CURSOR_NOBLINKTIME ) )
+ {
+ // non blinking cursor in read only - text selection mode
+ AllSettings aSettings = GetOut()->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ const sal_uInt64 nNewBlinkTime = nBlinkTime == STYLE_CURSOR_NOBLINKTIME ?
+ Application::GetSettings().GetStyleSettings().GetCursorBlinkTime() :
+ STYLE_CURSOR_NOBLINKTIME;
+ aStyleSettings.SetCursorBlinkTime( nNewBlinkTime );
+ aSettings.SetStyleSettings( aStyleSettings );
+ GetOut()->SetSettings( aSettings );
+ }
+
+ if( m_bSVCursorVis )
+ m_pVisibleCursor->Show(); // show again
+
+ if (comphelper::LibreOfficeKit::isActive())
+ sendLOKCursorUpdates();
+
+ getIDocumentMarkAccess()->NotifyCursorUpdate(*this);
+}
+
+void SwCursorShell::sendLOKCursorUpdates()
+{
+ SwView* pView = static_cast<SwView*>(GetSfxViewShell());
+ if (!pView || !pView->GetWrtShellPtr())
+ return;
+
+ SwWrtShell* pShell = &pView->GetWrtShell();
+
+ SwFrame* pCurrentFrame = GetCurrFrame();
+ SelectionType eType = pShell->GetSelectionType();
+
+ tools::JsonWriter aJsonWriter;
+
+ if (pCurrentFrame && (eType & SelectionType::Table) && pCurrentFrame->IsInTab())
+ {
+ const SwRect& rPageRect = pShell->GetAnyCurRect(CurRectType::Page, nullptr);
+
+ {
+ auto columnsNode = aJsonWriter.startNode("columns");
+ SwTabCols aTabCols;
+ pShell->GetTabCols(aTabCols);
+
+ const int nColumnOffset = aTabCols.GetLeftMin() + rPageRect.Left();
+
+ aJsonWriter.put("left", aTabCols.GetLeft());
+ aJsonWriter.put("right", aTabCols.GetRight());
+ aJsonWriter.put("tableOffset", static_cast<sal_Int64>(nColumnOffset));
+
+ {
+ auto entriesNode = aJsonWriter.startArray("entries");
+ for (size_t i = 0; i < aTabCols.Count(); ++i)
+ {
+ auto entryNode = aJsonWriter.startStruct();
+ auto const & rEntry = aTabCols.GetEntry(i);
+ aJsonWriter.put("position", rEntry.nPos);
+ aJsonWriter.put("min", rEntry.nMin);
+ aJsonWriter.put("max", rEntry.nMax);
+ aJsonWriter.put("hidden", rEntry.bHidden);
+ }
+ }
+ }
+
+ {
+ auto rowsNode = aJsonWriter.startNode("rows");
+ SwTabCols aTabRows;
+ pShell->GetTabRows(aTabRows);
+
+ const int nRowOffset = aTabRows.GetLeftMin() + rPageRect.Top();
+
+ aJsonWriter.put("left", aTabRows.GetLeft());
+ aJsonWriter.put("right", aTabRows.GetRight());
+ aJsonWriter.put("tableOffset", static_cast<sal_Int64>(nRowOffset));
+
+ {
+ auto entriesNode = aJsonWriter.startArray("entries");
+ for (size_t i = 0; i < aTabRows.Count(); ++i)
+ {
+ auto entryNode = aJsonWriter.startStruct();
+ auto const & rEntry = aTabRows.GetEntry(i);
+ aJsonWriter.put("position", rEntry.nPos);
+ aJsonWriter.put("min", rEntry.nMin);
+ aJsonWriter.put("max", rEntry.nMax);
+ aJsonWriter.put("hidden", rEntry.bHidden);
+ }
+ }
+ }
+ }
+
+ char* pChar = aJsonWriter.extractData();
+ GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, pChar);
+ free(pChar);
+}
+
+void SwCursorShell::RefreshBlockCursor()
+{
+ assert(m_pBlockCursor);
+ SwShellCursor &rBlock = m_pBlockCursor->getShellCursor();
+ Point aPt = rBlock.GetPtPos();
+ std::pair<Point, bool> const tmp(aPt, false);
+ SwContentFrame* pFrame = rBlock.GetContentNode()->getLayoutFrame(
+ GetLayout(), rBlock.GetPoint(), &tmp);
+ Point aMk;
+ if( m_pBlockCursor->getEndPoint() && m_pBlockCursor->getStartPoint() )
+ {
+ aPt = *m_pBlockCursor->getStartPoint();
+ aMk = *m_pBlockCursor->getEndPoint();
+ }
+ else
+ {
+ aPt = rBlock.GetPtPos();
+ if( pFrame )
+ {
+ if( pFrame->IsVertical() )
+ aPt.setY(pFrame->getFrameArea().Top() + GetUpDownX());
+ else
+ aPt.setX(pFrame->getFrameArea().Left() + GetUpDownX());
+ }
+ aMk = rBlock.GetMkPos();
+ }
+ SwRect aRect( aMk, aPt );
+ aRect.Justify();
+ SwSelectionList aSelList( pFrame );
+
+ if( !GetLayout()->FillSelection( aSelList, aRect ) )
+ return;
+
+ SwCursor* pNxt = static_cast<SwCursor*>(m_pCurrentCursor->GetNext());
+ while( pNxt != m_pCurrentCursor )
+ {
+ delete pNxt;
+ pNxt = static_cast<SwCursor*>(m_pCurrentCursor->GetNext());
+ }
+
+ std::list<SwPaM*>::iterator pStart = aSelList.getStart();
+ std::list<SwPaM*>::iterator pPam = aSelList.getEnd();
+ OSL_ENSURE( pPam != pStart, "FillSelection should deliver at least one PaM" );
+ m_pCurrentCursor->SetMark();
+ --pPam;
+ // If there is only one text portion inside the rectangle, a simple
+ // selection is created
+ if( pPam == pStart )
+ {
+ *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint();
+ if( (*pPam)->HasMark() )
+ *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
+ else
+ m_pCurrentCursor->DeleteMark();
+ delete *pPam;
+ m_pCurrentCursor->SetColumnSelection( false );
+ }
+ else
+ {
+ // The order of the SwSelectionList has to be preserved but
+ // the order inside the ring created by CreateCursor() is not like
+ // expected => First create the selections before the last one
+ // downto the first selection.
+ // At least create the cursor for the last selection
+ --pPam;
+ *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n-1 (if n == number of selections)
+ if( (*pPam)->HasMark() )
+ *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
+ else
+ m_pCurrentCursor->DeleteMark();
+ delete *pPam;
+ m_pCurrentCursor->SetColumnSelection( true );
+ while( pPam != pStart )
+ {
+ --pPam;
+
+ SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor );
+ pNew->insert( pNew->begin(), m_pCurrentCursor->begin(), m_pCurrentCursor->end());
+ m_pCurrentCursor->clear();
+ m_pCurrentCursor->DeleteMark();
+
+ *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n-2, n-3, .., 2, 1
+ if( (*pPam)->HasMark() )
+ {
+ m_pCurrentCursor->SetMark();
+ *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
+ }
+ else
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SetColumnSelection( true );
+ delete *pPam;
+ }
+ {
+ SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor );
+ pNew->insert( pNew->begin(), m_pCurrentCursor->begin(), m_pCurrentCursor->end() );
+ m_pCurrentCursor->clear();
+ m_pCurrentCursor->DeleteMark();
+ }
+ pPam = aSelList.getEnd();
+ --pPam;
+ *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n, the last selection
+ if( (*pPam)->HasMark() )
+ {
+ m_pCurrentCursor->SetMark();
+ *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark();
+ }
+ else
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SetColumnSelection( true );
+ delete *pPam;
+ }
+}
+
+/// create a copy of the cursor and save it in the stack
+void SwCursorShell::Push()
+{
+ // fdo#60513: if we have a table cursor, copy that; else copy current.
+ // This seems to work because UpdateCursor() will fix this up on Pop(),
+ // then MakeBoxSels() will re-create the current m_pCurrentCursor cell ring.
+ SwShellCursor *const pCurrent(m_pTableCursor ? m_pTableCursor : m_pCurrentCursor);
+ m_pStackCursor = new SwShellCursor( *this, *pCurrent->GetPoint(),
+ pCurrent->GetPtPos(), m_pStackCursor );
+
+ if (pCurrent->HasMark())
+ {
+ m_pStackCursor->SetMark();
+ *m_pStackCursor->GetMark() = *pCurrent->GetMark();
+ }
+}
+
+/** delete cursor
+
+ @param eDelete delete from stack, or delete current
+ and assign the one from stack as the new current cursor.
+ @return <true> if there was one on the stack, <false> otherwise
+*/
+bool SwCursorShell::Pop(PopMode const eDelete)
+{
+ ::std::unique_ptr<SwCallLink> pLink(::std::make_unique<SwCallLink>(*this)); // watch Cursor-Moves; call Link if needed
+ return Pop(eDelete, ::std::move(pLink));
+}
+
+bool SwCursorShell::Pop(PopMode const eDelete,
+ [[maybe_unused]] ::std::unique_ptr<SwCallLink> const pLink)
+{
+ assert(pLink); // parameter exists only to be deleted before return
+
+ // are there any left?
+ if (nullptr == m_pStackCursor)
+ return false;
+
+ SwShellCursor *pTmp = nullptr, *pOldStack = m_pStackCursor;
+
+ // the successor becomes the current one
+ if (m_pStackCursor->GetNext() != m_pStackCursor)
+ {
+ pTmp = m_pStackCursor->GetNext();
+ }
+
+ if (PopMode::DeleteStack == eDelete)
+ delete m_pStackCursor;
+
+ m_pStackCursor = pTmp; // assign new one
+
+ if (PopMode::DeleteCurrent == eDelete)
+ {
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ // If the visible SSelection was not changed
+ const Point& rPoint = pOldStack->GetPtPos();
+ if (rPoint == m_pCurrentCursor->GetPtPos() || rPoint == m_pCurrentCursor->GetMkPos())
+ {
+ // move "Selections Rectangles"
+ m_pCurrentCursor->insert( m_pCurrentCursor->begin(), pOldStack->begin(), pOldStack->end() );
+ pOldStack->clear();
+ }
+
+ if( pOldStack->HasMark() )
+ {
+ m_pCurrentCursor->SetMark();
+ *m_pCurrentCursor->GetMark() = *pOldStack->GetMark();
+ m_pCurrentCursor->GetMkPos() = pOldStack->GetMkPos();
+ }
+ else
+ // no selection so revoke old one and set to old position
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = *pOldStack->GetPoint();
+ m_pCurrentCursor->GetPtPos() = pOldStack->GetPtPos();
+ delete pOldStack;
+
+ if( !m_pCurrentCursor->IsInProtectTable( true ) &&
+ !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos ) )
+ {
+ UpdateCursor(); // update current cursor
+ if (m_pTableCursor)
+ { // tdf#106929 ensure m_pCurrentCursor ring is recreated from table
+ m_pTableCursor->SetChgd();
+ }
+ }
+ }
+ return true;
+}
+
+/** Combine two cursors
+
+ Delete topmost from stack and use its GetMark in the current.
+*/
+void SwCursorShell::Combine()
+{
+ // any others left?
+ if (nullptr == m_pStackCursor)
+ return;
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ // rhbz#689053: IsSelOvr must restore the saved stack position, not the
+ // current one, because current point + stack mark may be invalid PaM
+ SwCursorSaveState aSaveState(*m_pStackCursor);
+ // stack cursor & current cursor in same Section?
+ assert(!m_pStackCursor->HasMark() ||
+ CheckNodesRange(m_pStackCursor->GetMark()->nNode,
+ m_pCurrentCursor->GetPoint()->nNode, true));
+ *m_pStackCursor->GetPoint() = *m_pCurrentCursor->GetPoint();
+ m_pStackCursor->GetPtPos() = m_pCurrentCursor->GetPtPos();
+
+ SwShellCursor * pTmp = nullptr;
+ if (m_pStackCursor->GetNext() != m_pStackCursor)
+ {
+ pTmp = m_pStackCursor->GetNext();
+ }
+ delete m_pCurrentCursor;
+ m_pCurrentCursor = m_pStackCursor;
+ m_pStackCursor->MoveTo(nullptr); // remove from ring
+ m_pStackCursor = pTmp;
+ if( !m_pCurrentCursor->IsInProtectTable( true ) &&
+ !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos ) )
+ {
+ UpdateCursor(); // update current cursor
+ }
+}
+
+void SwCursorShell::HideCursors()
+{
+ if( !m_bHasFocus || m_bBasicHideCursor )
+ return;
+
+ // if cursor is visible then hide SV cursor
+ if( m_pVisibleCursor->IsVisible() )
+ {
+ CurrShell aCurr( this );
+ m_pVisibleCursor->Hide();
+ }
+ // revoke inversion of SSelection
+ SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
+ pCurrentCursor->Hide();
+}
+
+void SwCursorShell::ShowCursors( bool bCursorVis )
+{
+ if( !m_bHasFocus || m_bAllProtect || m_bBasicHideCursor )
+ return;
+
+ CurrShell aCurr( this );
+ SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
+ pCurrentCursor->Show(nullptr);
+
+ if( m_bSVCursorVis && bCursorVis ) // also show SV cursor again
+ m_pVisibleCursor->Show();
+}
+
+void SwCursorShell::ShowCursor()
+{
+ if( m_bBasicHideCursor )
+ return;
+
+ m_bSVCursorVis = true;
+ m_pCurrentCursor->SetShowTextInputFieldOverlay( true );
+ m_pCurrentCursor->SetShowContentControlOverlay(true);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ const OString aPayload = OString::boolean(m_bSVCursorVis);
+ GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr());
+ SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
+ }
+
+ UpdateCursor();
+}
+
+void SwCursorShell::HideCursor()
+{
+ if( m_bBasicHideCursor )
+ return;
+
+ m_bSVCursorVis = false;
+ // possibly reverse selected areas!!
+ CurrShell aCurr( this );
+ m_pCurrentCursor->SetShowTextInputFieldOverlay( false );
+ m_pCurrentCursor->SetShowContentControlOverlay(false);
+ m_pVisibleCursor->Hide();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = OString::boolean(m_bSVCursorVis);
+ GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr());
+ SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload);
+ }
+}
+
+void SwCursorShell::ShellLoseFocus()
+{
+ if( !m_bBasicHideCursor )
+ HideCursors();
+ m_bHasFocus = false;
+}
+
+void SwCursorShell::ShellGetFocus()
+{
+ comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll());
+
+ m_bHasFocus = true;
+ if( !m_bBasicHideCursor && VisArea().Width() )
+ {
+ UpdateCursor( o3tl::narrowing<sal_uInt16>( SwCursorShell::CHKRANGE ) );
+ ShowCursors( m_bSVCursorVis );
+ }
+}
+
+/** Get current frame in which the cursor is positioned. */
+SwContentFrame *SwCursorShell::GetCurrFrame( const bool bCalcFrame ) const
+{
+ CurrShell aCurr( const_cast<SwCursorShell*>(this) );
+ SwContentFrame *pRet = nullptr;
+ SwContentNode *pNd = m_pCurrentCursor->GetContentNode();
+ if ( pNd )
+ {
+ if ( bCalcFrame )
+ {
+ sal_uInt16* pST = const_cast<sal_uInt16*>(&mnStartAction);
+ ++(*pST);
+ const Size aOldSz( GetDocSize() );
+ std::pair<Point, bool> const tmp(m_pCurrentCursor->GetPtPos(), true);
+ pRet = pNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
+ --(*pST);
+ if( aOldSz != GetDocSize() )
+ const_cast<SwCursorShell*>(this)->SizeChgNotify();
+ }
+ else
+ {
+ std::pair<Point, bool> const tmp(m_pCurrentCursor->GetPtPos(), false);
+ pRet = pNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
+ }
+ }
+ return pRet;
+}
+
+//TODO: provide documentation
+/** forward all attribute/format changes at the current node to the Link
+
+ @param pOld ???
+ @param pNew ???
+*/
+void SwCursorShell::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if(dynamic_cast<const sw::PostGraphicArrivedHint*>(&rHint) && m_aGrfArrivedLnk.IsSet())
+ {
+ m_aGrfArrivedLnk.Call(*this);
+ return;
+ }
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ auto nWhich = pLegacy->GetWhich();
+ if(!nWhich)
+ nWhich = sal::static_int_cast<sal_uInt16>(RES_MSG_BEGIN);
+ if( m_bCallChgLnk &&
+ ( nWhich < RES_MSG_BEGIN || nWhich >= RES_MSG_END ||
+ nWhich == RES_FMT_CHG || nWhich == RES_UPDATE_ATTR ||
+ nWhich == RES_ATTRSET_CHG ))
+ // messages are not forwarded
+ // #i6681#: RES_UPDATE_ATTR is implicitly unset in
+ // SwTextNode::Insert(SwTextHint*, sal_uInt16); we react here and thus do
+ // not need to send the expensive RES_FMT_CHG in Insert.
+ CallChgLnk();
+ switch(nWhich)
+ {
+ case RES_OBJECTDYING:
+ EndListeningAll();
+ break;
+ case RES_GRAPHIC_SWAPIN:
+ if(m_aGrfArrivedLnk.IsSet())
+ m_aGrfArrivedLnk.Call(*this);
+ }
+
+}
+
+/** Does the current cursor create a selection?
+
+ This means checking if GetMark is set and if SPoint and GetMark differ.
+*/
+bool SwCursorShell::HasSelection() const
+{
+ const SwPaM* pCursor = getShellCursor( true );
+ return IsTableMode()
+ || (pCursor->HasMark() &&
+ (*pCursor->GetPoint() != *pCursor->GetMark()
+ || IsFlySelectedByCursor(*GetDoc(), *pCursor->Start(), *pCursor->End())));
+}
+
+void SwCursorShell::CallChgLnk()
+{
+ // Do not make any call in StartAction/EndAction but just set the flag.
+ // This will be handled in EndAction.
+ if (ActionPend())
+ m_bChgCallFlag = true; // remember change
+ else if( m_aChgLnk.IsSet() )
+ {
+ if( m_bCallChgLnk )
+ m_aChgLnk.Call(nullptr);
+ m_bChgCallFlag = false; // reset flag
+ }
+}
+
+/// get selected text of a node at current cursor
+OUString SwCursorShell::GetSelText() const
+{
+ OUString aText;
+ if (GetLayout()->HasMergedParas())
+ {
+ SwContentFrame const*const pFrame(GetCurrFrame(false));
+ if (pFrame && FrameContainsNode(*pFrame, m_pCurrentCursor->GetMark()->nNode.GetIndex()))
+ {
+ OUStringBuffer buf;
+ SwPosition const*const pStart(m_pCurrentCursor->Start());
+ SwPosition const*const pEnd(m_pCurrentCursor->End());
+ for (SwNodeOffset i = pStart->nNode.GetIndex(); i <= pEnd->nNode.GetIndex(); ++i)
+ {
+ SwNode const& rNode(*pStart->nNode.GetNodes()[i]);
+ assert(!rNode.IsEndNode());
+ if (rNode.IsStartNode())
+ {
+ i = rNode.EndOfSectionIndex();
+ }
+ else if (rNode.IsTextNode())
+ {
+ sal_Int32 const nStart(i == pStart->nNode.GetIndex()
+ ? pStart->nContent.GetIndex()
+ : 0);
+ sal_Int32 const nEnd(i == pEnd->nNode.GetIndex()
+ ? pEnd->nContent.GetIndex()
+ : rNode.GetTextNode()->Len());
+ buf.append(rNode.GetTextNode()->GetExpandText(
+ GetLayout(),
+ nStart, nEnd - nStart, false, false, false,
+ ExpandMode::HideDeletions));
+
+ }
+ }
+ aText = buf.makeStringAndClear();
+ }
+ }
+ else if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() ==
+ m_pCurrentCursor->GetMark()->nNode.GetIndex() )
+ {
+ SwTextNode* pTextNd = m_pCurrentCursor->GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ const sal_Int32 nStt = m_pCurrentCursor->Start()->nContent.GetIndex();
+ aText = pTextNd->GetExpandText(GetLayout(), nStt,
+ m_pCurrentCursor->End()->nContent.GetIndex() - nStt );
+ }
+ }
+ return aText;
+}
+
+/** get the nth character of the current SSelection
+
+ @param bEnd Start counting from the end? From start otherwise.
+ @param nOffset position of the character
+*/
+sal_Unicode SwCursorShell::GetChar( bool bEnd, tools::Long nOffset )
+{
+ if( IsTableMode() ) // not possible in table mode
+ return 0;
+
+ const SwPosition* pPos = !m_pCurrentCursor->HasMark() ? m_pCurrentCursor->GetPoint()
+ : bEnd ? m_pCurrentCursor->End() : m_pCurrentCursor->Start();
+ SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
+ if( !pTextNd )
+ return 0;
+
+ const sal_Int32 nPos = pPos->nContent.GetIndex();
+ const OUString& rStr = pTextNd->GetText();
+ sal_Unicode cCh = 0;
+
+ if (((nPos+nOffset) >= 0 ) && (nPos+nOffset) < rStr.getLength())
+ cCh = rStr[nPos + nOffset];
+
+ return cCh;
+}
+
+/** extend current SSelection by n characters
+
+ @param bEnd Start counting from the end? From start otherwise.
+ @param nCount Number of characters.
+*/
+bool SwCursorShell::ExtendSelection( bool bEnd, sal_Int32 nCount )
+{
+ if( !m_pCurrentCursor->HasMark() || IsTableMode() )
+ return false; // no selection
+
+ SwPosition* pPos = bEnd ? m_pCurrentCursor->End() : m_pCurrentCursor->Start();
+ SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
+ assert(pTextNd);
+
+ sal_Int32 nPos = pPos->nContent.GetIndex();
+ if( bEnd )
+ {
+ if ((nPos + nCount) <= pTextNd->GetText().getLength())
+ nPos = nPos + nCount;
+ else
+ return false; // not possible
+ }
+ else if( nPos >= nCount )
+ nPos = nPos - nCount;
+ else
+ return false; // not possible anymore
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+
+ pPos->nContent = nPos;
+ UpdateCursor();
+
+ return true;
+}
+
+/** Move visible cursor to given position in document.
+
+ @param rPt The position to move the visible cursor to.
+ @return <false> if SPoint was corrected by the layout.
+*/
+bool SwCursorShell::SetVisibleCursor( const Point &rPt )
+{
+ CurrShell aCurr( this );
+ Point aPt( rPt );
+ SwPosition aPos( *m_pCurrentCursor->GetPoint() );
+ SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+ aTmpState.m_bRealHeight = true;
+
+ const bool bRet = GetLayout()->GetModelPositionForViewPoint( &aPos, aPt /*, &aTmpState*/ );
+
+ SetInFrontOfLabel( false ); // #i27615#
+
+ // show only in TextNodes
+ SwTextNode* pTextNd = aPos.nNode.GetNode().GetTextNode();
+ if( !pTextNd )
+ return false;
+
+ const SwSectionNode* pSectNd = pTextNd->FindSectionNode();
+ if( pSectNd && (pSectNd->GetSection().IsHiddenFlag() ||
+ ( !IsReadOnlyAvailable() &&
+ pSectNd->GetSection().IsProtectFlag())) )
+ return false;
+
+ std::pair<Point, bool> const tmp(aPt, true);
+ SwContentFrame *pFrame = pTextNd->getLayoutFrame(GetLayout(), &aPos, &tmp);
+ if ( Imp()->IsIdleAction() )
+ pFrame->PrepareCursor();
+ SwRect aTmp( m_aCharRect );
+
+ pFrame->GetCharRect( m_aCharRect, aPos, &aTmpState );
+
+ // #i10137#
+ if( aTmp == m_aCharRect && m_pVisibleCursor->IsVisible() )
+ return true;
+
+ m_pVisibleCursor->Hide(); // always hide visible cursor
+ if( IsScrollMDI( this, m_aCharRect ))
+ {
+ MakeVisible( m_aCharRect );
+ m_pCurrentCursor->Show(nullptr);
+ }
+
+ {
+ if( aTmpState.m_bRealHeight )
+ m_aCursorHeight = aTmpState.m_aRealHeight;
+ else
+ {
+ m_aCursorHeight.setX(0);
+ m_aCursorHeight.setY(m_aCharRect.Height());
+ }
+
+ m_pVisibleCursor->SetDragCursor();
+ m_pVisibleCursor->Show(); // show again
+ }
+ return bRet;
+}
+
+SwVisibleCursor* SwCursorShell::GetVisibleCursor() const
+{
+ return m_pVisibleCursor;
+}
+
+bool SwCursorShell::IsOverReadOnlyPos( const Point& rPt ) const
+{
+ Point aPt( rPt );
+ SwPaM aPam( *m_pCurrentCursor->GetPoint() );
+ GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aPt );
+ // form view
+ return aPam.HasReadonlySel( GetViewOptions()->IsFormView() );
+}
+
+/** Get the number of elements in the ring of cursors
+
+ @param bAll If <false> get only spanned ones (= with selections) (Basic).
+*/
+sal_uInt16 SwCursorShell::GetCursorCnt( bool bAll ) const
+{
+ SwPaM* pTmp = GetCursor()->GetNext();
+ sal_uInt16 n = (bAll || ( m_pCurrentCursor->HasMark() &&
+ *m_pCurrentCursor->GetPoint() != *m_pCurrentCursor->GetMark())) ? 1 : 0;
+ while( pTmp != m_pCurrentCursor )
+ {
+ if( bAll || ( pTmp->HasMark() &&
+ *pTmp->GetPoint() != *pTmp->GetMark()))
+ ++n;
+ pTmp = pTmp->GetNext();
+ }
+ return n;
+}
+
+bool SwCursorShell::IsStartOfDoc() const
+{
+ if( m_pCurrentCursor->GetPoint()->nContent.GetIndex() )
+ return false;
+
+ // after EndOfIcons comes the content selection (EndNd+StNd+ContentNd)
+ SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfExtras(), 2 );
+ if( !aIdx.GetNode().IsContentNode() )
+ GetDoc()->GetNodes().GoNext( &aIdx );
+ return aIdx == m_pCurrentCursor->GetPoint()->nNode;
+}
+
+bool SwCursorShell::IsEndOfDoc() const
+{
+ SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfContent(), -1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = SwNodes::GoPrevious( &aIdx );
+
+ return aIdx == m_pCurrentCursor->GetPoint()->nNode &&
+ pCNd->Len() == m_pCurrentCursor->GetPoint()->nContent.GetIndex();
+}
+
+/** Invalidate cursors
+
+ Delete all created cursors, set table crsr and last crsr to their TextNode
+ (or StartNode?). They will then all re-created at the next ::GetCursor() call.
+
+ This is needed for Drag&Drop/ Clipboard-paste in tables.
+*/
+bool SwCursorShell::ParkTableCursor()
+{
+ if( !m_pTableCursor )
+ return false;
+
+ m_pTableCursor->ParkCursor();
+
+ while( m_pCurrentCursor->GetNext() != m_pCurrentCursor )
+ delete m_pCurrentCursor->GetNext();
+
+ // *always* move cursor's Point and Mark
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint();
+
+ return true;
+}
+
+void SwCursorShell::ParkPams( SwPaM* pDelRg, SwShellCursor** ppDelRing )
+{
+ const SwPosition *pStt = pDelRg->Start(),
+ *pEnd = pDelRg->End();
+
+ SwPaM *pTmpDel = nullptr, *pTmp = *ppDelRing;
+
+ // search over the whole ring
+ bool bGoNext;
+ do {
+
+ if (!pTmp)
+ break;
+
+ const SwPosition *pTmpStt = pTmp->Start(),
+ *pTmpEnd = pTmp->End();
+ // If a SPoint or GetMark are in a cursor area then cancel the old area.
+ // During comparison keep in mind that End() is outside the area.
+ if( *pStt <= *pTmpStt )
+ {
+ if( *pEnd > *pTmpStt ||
+ ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd ))
+ pTmpDel = pTmp;
+ }
+ else
+ if( *pStt < *pTmpEnd )
+ pTmpDel = pTmp;
+
+ bGoNext = true;
+ if (pTmpDel) // is the pam in the range -> delete
+ {
+ bool bDelete = true;
+ if( *ppDelRing == pTmpDel )
+ {
+ if( *ppDelRing == m_pCurrentCursor )
+ {
+ bDelete = GoNextCursor();
+ if( bDelete )
+ {
+ bGoNext = false;
+ pTmp = pTmp->GetNext();
+ }
+ }
+ else
+ bDelete = false; // never delete the StackCursor
+ }
+
+ if( bDelete )
+ {
+ if (pTmp == pTmpDel)
+ pTmp = nullptr;
+ delete pTmpDel; // invalidate old area
+ }
+ else
+ {
+ pTmpDel->GetPoint()->nContent.Assign(nullptr, 0);
+ pTmpDel->GetPoint()->nNode = SwNodeOffset(0);
+ pTmpDel->DeleteMark();
+ }
+ pTmpDel = nullptr;
+ }
+ if( bGoNext && pTmp )
+ pTmp = pTmp->GetNext();
+
+ } while( !bGoNext || *ppDelRing != pTmp );
+}
+
+//TODO: provide documentation
+/** Remove selections and additional cursors of all shells.
+
+ The remaining cursor of the shell is parked.
+
+ @param rIdx ???
+*/
+void SwCursorShell::ParkCursor( const SwNodeIndex &rIdx )
+{
+ SwNode *pNode = &rIdx.GetNode();
+
+ // create a new PaM
+ SwPaM aNew( *GetCursor()->GetPoint() );
+ if( pNode->GetStartNode() )
+ {
+ pNode = pNode->StartOfSectionNode();
+ if( pNode->IsTableNode() )
+ {
+ // the given node is in a table, thus park cursor to table node
+ // (outside of the table)
+ aNew.GetPoint()->nNode = *pNode->StartOfSectionNode();
+ }
+ else
+ // Also on the start node itself. Then we need to request the start
+ // node always via its end node! (StartOfSelection of StartNode is
+ // the parent)
+ aNew.GetPoint()->nNode = *pNode->EndOfSectionNode()->StartOfSectionNode();
+ }
+ else
+ aNew.GetPoint()->nNode = *pNode->StartOfSectionNode();
+ aNew.SetMark();
+ aNew.GetPoint()->nNode = *pNode->EndOfSectionNode();
+
+ // take care of all shells
+ for(SwViewShell& rTmp : GetRingContainer())
+ {
+ if( auto pSh = dynamic_cast<SwCursorShell *>(&rTmp))
+ {
+ if (pSh->m_pStackCursor)
+ pSh->ParkPams(&aNew, &pSh->m_pStackCursor);
+
+ pSh->ParkPams( &aNew, &pSh->m_pCurrentCursor );
+ if( pSh->m_pTableCursor )
+ {
+ // set table cursor always to 0 and the current one always to
+ // the beginning of the table
+ SwPaM* pTCursor = pSh->GetTableCrs();
+ SwNode* pTableNd = pTCursor->GetPoint()->nNode.GetNode().FindTableNode();
+ if ( pTableNd )
+ {
+ pTCursor->GetPoint()->nContent.Assign(nullptr, 0);
+ pTCursor->GetPoint()->nNode = SwNodeOffset(0);
+ pTCursor->DeleteMark();
+ pSh->m_pCurrentCursor->GetPoint()->nNode = *pTableNd;
+ }
+ }
+ }
+ }
+}
+
+/** Copy constructor
+
+ Copy cursor position and add it to the ring.
+ All views of a document are in the ring of the shell.
+*/
+SwCursorShell::SwCursorShell( SwCursorShell& rShell, vcl::Window *pInitWin )
+ : SwViewShell( rShell, pInitWin )
+ , sw::BroadcastingModify()
+ , m_pStackCursor( nullptr )
+ , m_pBlockCursor( nullptr )
+ , m_pTableCursor( nullptr )
+ , m_pBoxIdx( nullptr )
+ , m_pBoxPtr( nullptr )
+ , m_nUpDownX(0)
+ , m_nLeftFramePos(0)
+ , m_nCurrentNode(0)
+ , m_nCurrentContent(0)
+ , m_nCurrentNdTyp(SwNodeType::NONE)
+ , m_nCursorMove( 0 )
+ , m_eMvState( CursorMoveState::NONE )
+ , m_eEnhancedTableSel(SwTable::SEARCH_NONE)
+ , m_nMarkedListLevel( 0 )
+ , m_oldColFrame(nullptr)
+{
+ CurrShell aCurr( this );
+ // only keep the position of the current cursor of the copy shell
+ m_pCurrentCursor = new SwShellCursor( *this, *(rShell.m_pCurrentCursor->GetPoint()) );
+ m_pCurrentCursor->GetContentNode()->Add( this );
+
+ m_bAllProtect = m_bVisPortChgd = m_bChgCallFlag = m_bInCMvVisportChgd =
+ m_bGCAttr = m_bIgnoreReadonly = m_bSelTableCells = m_bBasicHideCursor =
+ m_bOverwriteCursor = false;
+ m_bSendAccessibleCursorEvents = true;
+ m_bCallChgLnk = m_bHasFocus = m_bAutoUpdateCells = true;
+ m_bSVCursorVis = true;
+ m_bSetCursorInReadOnly = true;
+ m_pVisibleCursor = new SwVisibleCursor( this );
+ m_bMacroExecAllowed = rShell.IsMacroExecAllowed();
+}
+
+/// default constructor
+SwCursorShell::SwCursorShell( SwDoc& rDoc, vcl::Window *pInitWin,
+ const SwViewOption *pInitOpt )
+ : SwViewShell( rDoc, pInitWin, pInitOpt )
+ , sw::BroadcastingModify()
+ , m_pStackCursor( nullptr )
+ , m_pBlockCursor( nullptr )
+ , m_pTableCursor( nullptr )
+ , m_pBoxIdx( nullptr )
+ , m_pBoxPtr( nullptr )
+ , m_nUpDownX(0)
+ , m_nLeftFramePos(0)
+ , m_nCurrentNode(0)
+ , m_nCurrentContent(0)
+ , m_nCurrentNdTyp(SwNodeType::NONE)
+ , m_nCursorMove( 0 )
+ , m_eMvState( CursorMoveState::NONE ) // state for crsr-travelling - GetModelPositionForViewPoint
+ , m_eEnhancedTableSel(SwTable::SEARCH_NONE)
+ , m_nMarkedListLevel( 0 )
+ , m_oldColFrame(nullptr)
+{
+ CurrShell aCurr( this );
+ // create initial cursor and set it to first content position
+ SwNodes& rNds = rDoc.GetNodes();
+
+ SwNodeIndex aNodeIdx( *rNds.GetEndOfContent().StartOfSectionNode() );
+ SwContentNode* pCNd = rNds.GoNext( &aNodeIdx ); // go to the first ContentNode
+
+ m_pCurrentCursor = new SwShellCursor( *this, SwPosition( aNodeIdx, SwIndex( pCNd, 0 )));
+
+ // Register shell as dependent at current node. As a result all attribute
+ // changes can be forwarded via the Link.
+ pCNd->Add( this );
+
+ m_bAllProtect = m_bVisPortChgd = m_bChgCallFlag = m_bInCMvVisportChgd =
+ m_bGCAttr = m_bIgnoreReadonly = m_bSelTableCells = m_bBasicHideCursor =
+ m_bOverwriteCursor = false;
+ m_bSendAccessibleCursorEvents = true;
+ m_bCallChgLnk = m_bHasFocus = m_bAutoUpdateCells = true;
+ m_bSVCursorVis = true;
+ m_bSetCursorInReadOnly = true;
+
+ m_pVisibleCursor = new SwVisibleCursor( this );
+ m_bMacroExecAllowed = true;
+}
+
+SwCursorShell::~SwCursorShell()
+{
+ // if it is not the last view then at least the field should be updated
+ if( !unique() )
+ CheckTableBoxContent( m_pCurrentCursor->GetPoint() );
+ else
+ ClearTableBoxContent();
+
+ delete m_pVisibleCursor;
+ delete m_pBlockCursor;
+ delete m_pTableCursor;
+
+ // release cursors
+ while(m_pCurrentCursor->GetNext() != m_pCurrentCursor)
+ delete m_pCurrentCursor->GetNext();
+ delete m_pCurrentCursor;
+
+ // free stack
+ if (m_pStackCursor)
+ {
+ while (m_pStackCursor->GetNext() != m_pStackCursor)
+ delete m_pStackCursor->GetNext();
+ delete m_pStackCursor;
+ }
+
+ // #i54025# - do not give a HTML parser that might potentially hang as
+ // a client at the cursor shell the chance to hang itself on a TextNode
+ EndListeningAll();
+}
+
+SwShellCursor* SwCursorShell::getShellCursor( bool bBlock )
+{
+ if( m_pTableCursor )
+ return m_pTableCursor;
+ if( m_pBlockCursor && bBlock )
+ return &m_pBlockCursor->getShellCursor();
+ return m_pCurrentCursor;
+}
+
+/** Should WaitPtr be switched on for the clipboard?
+
+ Wait for TableMode, multiple selections and more than x selected paragraphs.
+*/
+bool SwCursorShell::ShouldWait() const
+{
+ if ( IsTableMode() || GetCursorCnt() > 1 )
+ return true;
+
+ if( HasDrawView() && GetDrawView()->GetMarkedObjectList().GetMarkCount() )
+ return true;
+
+ SwPaM* pPam = GetCursor();
+ return pPam->Start()->nNode.GetIndex() + SwNodeOffset(10) <
+ pPam->End()->nNode.GetIndex();
+}
+
+size_t SwCursorShell::UpdateTableSelBoxes()
+{
+ if (m_pTableCursor && (m_pTableCursor->IsChgd() || !m_pTableCursor->GetSelectedBoxesCount()))
+ {
+ GetLayout()->MakeTableCursors( *m_pTableCursor );
+ }
+ return m_pTableCursor ? m_pTableCursor->GetSelectedBoxesCount() : 0;
+}
+
+/// show the current selected "object"
+void SwCursorShell::MakeSelVisible()
+{
+ OSL_ENSURE( m_bHasFocus, "no focus but cursor should be made visible?" );
+ if( m_aCursorHeight.Y() < m_aCharRect.Height() && m_aCharRect.Height() > VisArea().Height() )
+ {
+ SwRect aTmp( m_aCharRect );
+ tools::Long nDiff = m_aCharRect.Height() - VisArea().Height();
+ if( nDiff < m_aCursorHeight.getX() )
+ aTmp.Top( nDiff + m_aCharRect.Top() );
+ else
+ {
+ aTmp.Top( m_aCursorHeight.getX() + m_aCharRect.Top() );
+ aTmp.Height( m_aCursorHeight.getY() );
+ }
+ if( !aTmp.HasArea() )
+ {
+ aTmp.AddHeight(1 );
+ aTmp.AddWidth(1 );
+ }
+ MakeVisible( aTmp );
+ }
+ else
+ {
+ if( m_aCharRect.HasArea() )
+ MakeVisible( m_aCharRect );
+ else
+ {
+ SwRect aTmp( m_aCharRect );
+ aTmp.AddHeight(1 );
+ aTmp.AddWidth(1 );
+ MakeVisible( aTmp );
+ }
+ }
+}
+
+/// search a valid content position (not protected/hidden)
+bool SwCursorShell::FindValidContentNode( bool bOnlyText )
+{
+ if( m_pTableCursor )
+ {
+ assert(!"Did not remove table selection!");
+ return false;
+ }
+
+ // #i45129# - everything is allowed in UI-readonly
+ if( !m_bAllProtect && GetDoc()->GetDocShell() &&
+ GetDoc()->GetDocShell()->IsReadOnlyUI() )
+ return true;
+
+ if( m_pCurrentCursor->HasMark() )
+ ClearMark();
+
+ // first check for frames
+ SwNodeIndex& rNdIdx = m_pCurrentCursor->GetPoint()->nNode;
+ SwNodeOffset nNdIdx = rNdIdx.GetIndex(); // keep backup
+ SwNodes& rNds = mxDoc->GetNodes();
+ SwContentNode* pCNd = rNdIdx.GetNode().GetContentNode();
+ const SwContentFrame * pFrame;
+
+ if (pCNd && nullptr != (pFrame = pCNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint())) &&
+ !IsReadOnlyAvailable() && pFrame->IsProtected() &&
+ nNdIdx < rNds.GetEndOfExtras().GetIndex() )
+ {
+ // skip protected frame
+ SwPaM aPam( *m_pCurrentCursor->GetPoint() );
+ aPam.SetMark();
+ aPam.GetMark()->nNode = rNds.GetEndOfContent();
+ aPam.GetPoint()->nNode = *pCNd->EndOfSectionNode();
+
+ bool bFirst = false;
+ if( nullptr == (pCNd = ::GetNode( aPam, bFirst, fnMoveForward )))
+ {
+ aPam.GetMark()->nNode = *rNds.GetEndOfPostIts().StartOfSectionNode();
+ pCNd = ::GetNode( aPam, bFirst, fnMoveBackward );
+ }
+
+ if( !pCNd ) // should *never* happen
+ {
+ rNdIdx = nNdIdx; // back to old node
+ return false;
+ }
+ *m_pCurrentCursor->GetPoint() = *aPam.GetPoint();
+ }
+ else if( bOnlyText && pCNd && pCNd->IsNoTextNode() )
+ {
+ // set to beginning of document
+ rNdIdx = mxDoc->GetNodes().GetEndOfExtras();
+ m_pCurrentCursor->GetPoint()->nContent.Assign( mxDoc->GetNodes().GoNext(
+ &rNdIdx ), 0 );
+ nNdIdx = rNdIdx.GetIndex();
+ }
+
+ bool bOk = true;
+
+ // #i9059# cursor may not stand in protected cells
+ // (unless cursor in protected areas is OK.)
+ const SwTableNode* pTableNode = rNdIdx.GetNode().FindTableNode();
+ if( !IsReadOnlyAvailable() &&
+ pTableNode != nullptr && rNdIdx.GetNode().IsProtect() )
+ {
+ // we're in a table, and we're in a protected area, so we're
+ // probably in a protected cell.
+
+ // move forward into non-protected area.
+ SwPaM aPam( rNdIdx.GetNode(), 0 );
+ while( aPam.GetNode().IsProtect() &&
+ aPam.Move( fnMoveForward, GoInContent ) )
+ ; // nothing to do in the loop; the aPam.Move does the moving!
+
+ // didn't work? then go backwards!
+ if( aPam.GetNode().IsProtect() )
+ {
+ SwPaM aTmpPaM( rNdIdx.GetNode(), 0 );
+ aPam = aTmpPaM;
+ while( aPam.GetNode().IsProtect() &&
+ aPam.Move( fnMoveBackward, GoInContent ) )
+ ; // nothing to do in the loop; the aPam.Move does the moving!
+ }
+
+ // if we're successful, set the new position
+ if( ! aPam.GetNode().IsProtect() )
+ {
+ *m_pCurrentCursor->GetPoint() = *aPam.GetPoint();
+ }
+ }
+
+ // in a protected frame
+ const SwSectionNode* pSectNd = rNdIdx.GetNode().FindSectionNode();
+ if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() ||
+ ( !IsReadOnlyAvailable() &&
+ pSectNd->GetSection().IsProtectFlag() )) )
+ {
+ bOk = false;
+ bool bGoNextSection = true;
+ for( int nLoopCnt = 0; !bOk && nLoopCnt < 2; ++nLoopCnt )
+ {
+ bool bContinue;
+ do {
+ bContinue = false;
+ for (;;)
+ {
+ if (bGoNextSection)
+ pCNd = rNds.GoNextSection( &rNdIdx,
+ true, !IsReadOnlyAvailable() );
+ else
+ pCNd = SwNodes::GoPrevSection( &rNdIdx,
+ true, !IsReadOnlyAvailable() );
+ if ( pCNd == nullptr) break;
+ // moved inside a table -> check if it is protected
+ if( pCNd->FindTableNode() )
+ {
+ SwCallLink aTmp( *this );
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ aTmp.m_nNodeType = SwNodeType::NONE; // don't do anything in DTOR
+ if( !m_pCurrentCursor->IsInProtectTable( true ) )
+ {
+ const SwSectionNode* pSNd = pCNd->FindSectionNode();
+ if( !pSNd || !pSNd->GetSection().IsHiddenFlag()
+ || (!IsReadOnlyAvailable() &&
+ pSNd->GetSection().IsProtectFlag() ))
+ {
+ bOk = true;
+ break; // found non-protected cell
+ }
+ continue; // continue search
+ }
+ }
+ else
+ {
+ bOk = true;
+ break; // found non-protected cell
+ }
+ }
+
+ if( bOk && rNdIdx.GetIndex() < rNds.GetEndOfExtras().GetIndex() )
+ {
+ // also check for Fly - might be protected as well
+ pFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, nullptr);
+ if (nullptr == pFrame ||
+ ( !IsReadOnlyAvailable() && pFrame->IsProtected() ) ||
+ ( bOnlyText && pCNd->IsNoTextNode() ) )
+ {
+ // continue search
+ bOk = false;
+ bContinue = true;
+ }
+ }
+ } while( bContinue );
+
+ if( !bOk )
+ {
+ if( !nLoopCnt )
+ bGoNextSection = false;
+ rNdIdx = nNdIdx;
+ }
+ }
+ }
+ if( bOk )
+ {
+ pCNd = rNdIdx.GetNode().GetContentNode();
+ const sal_Int32 nContent = rNdIdx.GetIndex() < nNdIdx ? pCNd->Len() : 0;
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, nContent );
+ }
+ else
+ {
+ pCNd = rNdIdx.GetNode().GetContentNode();
+ // if cursor in hidden frame, always move it
+ if (!pCNd || !pCNd->getLayoutFrame(GetLayout(), nullptr, nullptr))
+ {
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+ GetLayout()->GetModelPositionForViewPoint( m_pCurrentCursor->GetPoint(), m_pCurrentCursor->GetPtPos(),
+ &aTmpState );
+ }
+ }
+ return bOk;
+}
+
+bool SwCursorShell::IsCursorReadonly() const
+{
+ if ( GetViewOptions()->IsReadonly() ||
+ GetViewOptions()->IsFormView() /* Formula view */ )
+ {
+ SwFrame *pFrame = GetCurrFrame( false );
+ const SwFlyFrame* pFly;
+ const SwSection* pSection;
+
+ if( pFrame && pFrame->IsInFly() &&
+ (pFly = pFrame->FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() &&
+ pFly->Lower() &&
+ !pFly->Lower()->IsNoTextFrame() &&
+ !GetDrawView()->GetMarkedObjectList().GetMarkCount() )
+ {
+ return false;
+ }
+ // edit in readonly sections
+ else if ( pFrame && pFrame->IsInSct() &&
+ nullptr != ( pSection = pFrame->FindSctFrame()->GetSection() ) &&
+ pSection->IsEditInReadonlyFlag() )
+ {
+ return false;
+ }
+ else if ( !IsMultiSelection() && CursorInsideInputField() )
+ {
+ return false;
+ }
+
+ return true;
+ }
+ return false;
+}
+
+/// is the cursor allowed to enter ReadOnly sections?
+void SwCursorShell::SetReadOnlyAvailable( bool bFlag )
+{
+ // *never* switch in GlobalDoc
+ if( (!GetDoc()->GetDocShell() ||
+ dynamic_cast<const SwGlobalDocShell*>(GetDoc()->GetDocShell()) == nullptr ) &&
+ bFlag != m_bSetCursorInReadOnly )
+ {
+ // If the flag is switched off then all selections need to be
+ // invalidated. Otherwise we would trust that nothing protected is selected.
+ if( !bFlag )
+ {
+ ClearMark();
+ }
+ m_bSetCursorInReadOnly = bFlag;
+ UpdateCursor();
+ }
+}
+
+bool SwCursorShell::HasReadonlySel() const
+{
+ if (GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ {
+ // Treat selections that span over start or end of paragraph of an outline node
+ // with folded outline content as read-only.
+ SwWrtShell* pWrtSh = GetDoc()->GetDocShell()->GetWrtShell();
+ if (pWrtSh)
+ {
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ SwPaM aPaM(*rPaM.GetMark(), *rPaM.GetPoint());
+ aPaM.Normalize();
+ SwNodeIndex aPointIdx(aPaM.GetPoint()->nNode.GetNode());
+ SwNodeIndex aMarkIdx(aPaM.GetMark()->nNode.GetNode());
+ if (aPointIdx == aMarkIdx)
+ continue;
+ // If any nodes in PaM are folded outline content nodes, then set read-only.
+ SwOutlineNodes::size_type nPos;
+ for (SwNodeIndex aIdx = aPointIdx; aIdx <= aMarkIdx; aIdx++)
+ {
+ if (GetDoc()->GetNodes().GetOutLineNds().Seek_Entry(&(aIdx.GetNode()), &nPos) &&
+ !pWrtSh->GetAttrOutlineContentVisible(nPos))
+ return true;
+ }
+ }
+ }
+ }
+ bool bRet = false;
+ // If protected area is to be ignored, then selections are never read-only.
+ if ((IsReadOnlyAvailable() || GetViewOptions()->IsFormView() ||
+ GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM )) &&
+ !SwViewOption::IsIgnoreProtectedArea())
+ {
+ if ( m_pTableCursor != nullptr )
+ {
+ bRet = m_pTableCursor->HasReadOnlyBoxSel()
+ || m_pTableCursor->HasReadonlySel( GetViewOptions()->IsFormView() );
+ }
+ else
+ {
+ for(const SwPaM& rCursor : m_pCurrentCursor->GetRingContainer())
+ {
+ if( rCursor.HasReadonlySel( GetViewOptions()->IsFormView() ) )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::IsSelFullPara() const
+{
+ bool bRet = false;
+
+ if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() ==
+ m_pCurrentCursor->GetMark()->nNode.GetIndex() && !m_pCurrentCursor->IsMultiSelection() )
+ {
+ sal_Int32 nStt = m_pCurrentCursor->GetPoint()->nContent.GetIndex();
+ sal_Int32 nEnd = m_pCurrentCursor->GetMark()->nContent.GetIndex();
+ if( nStt > nEnd )
+ {
+ sal_Int32 nTmp = nStt;
+ nStt = nEnd;
+ nEnd = nTmp;
+ }
+ const SwContentNode* pCNd = m_pCurrentCursor->GetContentNode();
+ bRet = pCNd && !nStt && nEnd == pCNd->Len();
+ }
+ return bRet;
+}
+
+SvxFrameDirection SwCursorShell::GetTextDirection( const Point* pPt ) const
+{
+ SwPosition aPos( *m_pCurrentCursor->GetPoint() );
+ Point aPt( pPt ? *pPt : m_pCurrentCursor->GetPtPos() );
+ if( pPt )
+ {
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState );
+ }
+
+ return mxDoc->GetTextDirection( aPos, &aPt );
+}
+
+bool SwCursorShell::IsInVerticalText( const Point* pPt ) const
+{
+ const SvxFrameDirection nDir = GetTextDirection( pPt );
+ return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir
+ || nDir == SvxFrameDirection::Vertical_LR_BT;
+}
+
+bool SwCursorShell::IsInRightToLeftText() const
+{
+ const SvxFrameDirection nDir = GetTextDirection();
+ // GetTextDirection uses SvxFrameDirection::Vertical_LR_TB to indicate RTL in
+ // vertical environment
+ return SvxFrameDirection::Vertical_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir;
+}
+
+/// If the current cursor position is inside a hidden range, the hidden range
+/// is selected.
+bool SwCursorShell::SelectHiddenRange()
+{
+ bool bRet = false;
+ if ( !GetViewOptions()->IsShowHiddenChar() && !m_pCurrentCursor->HasMark() )
+ {
+ SwPosition& rPt = *m_pCurrentCursor->GetPoint();
+ const SwTextNode* pNode = rPt.nNode.GetNode().GetTextNode();
+ if ( pNode )
+ {
+ const sal_Int32 nPos = rPt.nContent.GetIndex();
+
+ // check if nPos is in hidden range
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ SwScriptInfo::GetBoundsOfHiddenRange( *pNode, nPos, nHiddenStart, nHiddenEnd );
+ if ( COMPLETE_STRING != nHiddenStart )
+ {
+ // make selection:
+ m_pCurrentCursor->SetMark();
+ m_pCurrentCursor->GetMark()->nContent = nHiddenEnd;
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+sal_uLong SwCursorShell::Find_Text( const i18nutil::SearchOptions2& rSearchOpt,
+ bool bSearchInNotes,
+ SwDocPositions eStart, SwDocPositions eEnd,
+ bool& bCancel,
+ FindRanges eRng,
+ bool bReplace )
+{
+ if( m_pTableCursor )
+ GetCursor();
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ sal_uLong nRet = m_pCurrentCursor->Find_Text(rSearchOpt, bSearchInNotes, eStart, eEnd,
+ bCancel, eRng, bReplace, GetLayout());
+ if( nRet || bCancel )
+ UpdateCursor();
+ return nRet;
+}
+
+sal_uLong SwCursorShell::FindFormat( const SwTextFormatColl& rFormatColl,
+ SwDocPositions eStart, SwDocPositions eEnd,
+ bool& bCancel,
+ FindRanges eRng,
+ const SwTextFormatColl* pReplFormat )
+{
+ if( m_pTableCursor )
+ GetCursor();
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ sal_uLong nRet = m_pCurrentCursor->FindFormat(rFormatColl, eStart, eEnd, bCancel, eRng,
+ pReplFormat );
+ if( nRet )
+ UpdateCursor();
+ return nRet;
+}
+
+sal_uLong SwCursorShell::FindAttrs( const SfxItemSet& rSet,
+ bool bNoCollections,
+ SwDocPositions eStart, SwDocPositions eEnd,
+ bool& bCancel,
+ FindRanges eRng,
+ const i18nutil::SearchOptions2* pSearchOpt,
+ const SfxItemSet* rReplSet )
+{
+ if( m_pTableCursor )
+ GetCursor();
+ delete m_pTableCursor;
+ m_pTableCursor = nullptr;
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ sal_uLong nRet = m_pCurrentCursor->FindAttrs(rSet, bNoCollections, eStart, eEnd,
+ bCancel, eRng, pSearchOpt, rReplSet, GetLayout());
+ if( nRet )
+ UpdateCursor();
+ return nRet;
+}
+
+void SwCursorShell::SetSelection( const SwPaM& rCursor )
+{
+ StartAction();
+ SwCursor* pCursor = GetCursor();
+ *pCursor->GetPoint() = *rCursor.GetPoint();
+ if(rCursor.GetNext() != &rCursor)
+ {
+ const SwPaM *_pStartCursor = rCursor.GetNext();
+ do
+ {
+ SwPaM* pCurrentCursor = CreateCursor();
+ *pCurrentCursor->GetPoint() = *_pStartCursor->GetPoint();
+ if(_pStartCursor->HasMark())
+ {
+ pCurrentCursor->SetMark();
+ *pCurrentCursor->GetMark() = *_pStartCursor->GetMark();
+ }
+ } while( (_pStartCursor = _pStartCursor->GetNext()) != &rCursor );
+ }
+ // CreateCursor() adds a copy of current cursor after current, and then deletes mark of current
+ // cursor; therefore set current cursor's mark only after creating all other cursors
+ if (rCursor.HasMark())
+ {
+ pCursor->SetMark();
+ *pCursor->GetMark() = *rCursor.GetMark();
+ }
+ EndAction();
+}
+
+static const SwStartNode* lcl_NodeContext( const SwNode& rNode )
+{
+ const SwStartNode *pRet = rNode.StartOfSectionNode();
+ while( pRet->IsSectionNode() || pRet->IsTableNode() ||
+ pRet->GetStartNodeType() == SwTableBoxStartNode )
+ {
+ pRet = pRet->StartOfSectionNode();
+ }
+ return pRet;
+}
+
+/**
+ Checks if a position is valid. To be valid the position's node must
+ be a content node and the content must not be unregistered.
+
+ @param aPos the position to check.
+*/
+bool sw_PosOk(const SwPosition & aPos)
+{
+ return nullptr != aPos.nNode.GetNode().GetContentNode() &&
+ aPos.nContent.GetIdxReg();
+}
+
+/**
+ Checks if a PaM is valid. For a PaM to be valid its point must be
+ valid. Additionally if the PaM has a mark this has to be valid, too.
+
+ @param aPam the PaM to check
+*/
+static bool lcl_CursorOk(SwPaM & aPam)
+{
+ return sw_PosOk(*aPam.GetPoint()) && (! aPam.HasMark()
+ || sw_PosOk(*aPam.GetMark()));
+}
+
+void SwCursorShell::ClearUpCursors()
+{
+ // start of the ring
+ SwPaM * pStartCursor = GetCursor();
+ // start loop with second entry of the ring
+ SwPaM * pCursor = pStartCursor->GetNext();
+ SwPaM * pTmpCursor;
+ bool bChanged = false;
+
+ // For all entries in the ring except the start entry delete the entry if
+ // it is invalid.
+ while (pCursor != pStartCursor)
+ {
+ pTmpCursor = pCursor->GetNext();
+ if ( ! lcl_CursorOk(*pCursor))
+ {
+ delete pCursor;
+ bChanged = true;
+ }
+ pCursor = pTmpCursor;
+ }
+
+ if( pStartCursor->HasMark() && !sw_PosOk( *pStartCursor->GetMark() ) )
+ {
+ pStartCursor->DeleteMark();
+ bChanged = true;
+ }
+ if (pStartCursor->GetPoint()->nNode.GetNode().IsTableNode())
+ {
+ // tdf#106959: When cursor points to start of a table, the proper content
+ // node is the first one inside the table, not the previous one
+ SwNodes& aNodes = GetDoc()->GetNodes();
+ SwNodeIndex aIdx(pStartCursor->GetPoint()->nNode);
+ if (SwNode* pNode = aNodes.GoNext(&aIdx))
+ {
+ SwPaM aTmpPam(*pNode);
+ *pStartCursor = aTmpPam;
+ bChanged = true;
+ }
+ }
+ if( !sw_PosOk( *pStartCursor->GetPoint() ) )
+ {
+ SwNodes & aNodes = GetDoc()->GetNodes();
+ const SwNode* pStart = lcl_NodeContext( pStartCursor->GetPoint()->nNode.GetNode() );
+ SwNodeIndex aIdx( pStartCursor->GetPoint()->nNode );
+ SwNode * pNode = SwNodes::GoPrevious(&aIdx);
+ if( pNode == nullptr || lcl_NodeContext( *pNode ) != pStart )
+ aNodes.GoNext( &aIdx );
+ if( pNode == nullptr || lcl_NodeContext( *pNode ) != pStart )
+ {
+ // If the start entry of the ring is invalid replace it with a
+ // cursor pointing to the beginning of the first content node in the
+ // document.
+ aIdx = *(aNodes.GetEndOfContent().StartOfSectionNode());
+ pNode = aNodes.GoNext( &aIdx );
+ }
+ bool bFound = (pNode != nullptr);
+
+ assert(bFound);
+
+ if (bFound)
+ {
+ SwPaM aTmpPam(*pNode);
+ *pStartCursor = aTmpPam;
+ }
+
+ bChanged = true;
+ }
+
+ // If at least one of the cursors in the ring have been deleted or replaced,
+ // remove the table cursor.
+ if (m_pTableCursor != nullptr && bChanged)
+ TableCursorToCursor();
+}
+
+OUString SwCursorShell::GetCursorDescr() const
+{
+ OUString aResult;
+
+ if (IsMultiSelection())
+ aResult += SwResId(STR_MULTISEL);
+ else
+ aResult = SwDoc::GetPaMDescr(*GetCursor());
+
+ return aResult;
+}
+
+void SwCursorShell::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwCursorShell"));
+
+ SwViewShell::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pCurrentCursor"));
+ for (const SwPaM& rPaM : m_pCurrentCursor->GetRingContainer())
+ rPaM.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+static void lcl_FillRecognizerData( std::vector< OUString >& rSmartTagTypes,
+ uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
+ const SwWrongList& rSmartTagList, sal_Int32 nCurrent )
+{
+ // Insert smart tag information
+ std::vector< uno::Reference< container::XStringKeyMap > > aStringKeyMaps;
+
+ for ( sal_uInt16 i = 0; i < rSmartTagList.Count(); ++i )
+ {
+ const sal_Int32 nSTPos = rSmartTagList.Pos( i );
+ const sal_Int32 nSTLen = rSmartTagList.Len( i );
+
+ if ( nSTPos <= nCurrent && nCurrent < nSTPos + nSTLen )
+ {
+ const SwWrongArea* pArea = rSmartTagList.GetElement( i );
+ if ( pArea )
+ {
+ rSmartTagTypes.push_back( pArea->maType );
+ aStringKeyMaps.push_back( pArea->mxPropertyBag );
+ }
+ }
+ }
+
+ if ( !rSmartTagTypes.empty() )
+ {
+ rStringKeyMaps = comphelper::containerToSequence(aStringKeyMaps);
+ }
+}
+
+static void lcl_FillTextRange( uno::Reference<text::XTextRange>& rRange,
+ SwTextNode& rNode, sal_Int32 nBegin, sal_Int32 nLen )
+{
+ // create SwPosition for nStartIndex
+ SwIndex aIndex( &rNode, nBegin );
+ SwPosition aStartPos( rNode, aIndex );
+
+ // create SwPosition for nEndIndex
+ SwPosition aEndPos( aStartPos );
+ aEndPos.nContent = nBegin + nLen;
+
+ const uno::Reference<text::XTextRange> xRange =
+ SwXTextRange::CreateXTextRange(rNode.GetDoc(), aStartPos, &aEndPos);
+
+ rRange = xRange;
+}
+
+void SwCursorShell::GetSmartTagTerm( std::vector< OUString >& rSmartTagTypes,
+ uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps,
+ uno::Reference< text::XTextRange>& rRange ) const
+{
+ if ( !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
+ return;
+
+ SwPaM* pCursor = GetCursor();
+ SwPosition aPos( *pCursor->GetPoint() );
+ SwTextNode *pNode = aPos.nNode.GetNode().GetTextNode();
+ if ( !pNode || pNode->IsInProtectSect() )
+ return;
+
+ const SwWrongList *pSmartTagList = pNode->GetSmartTags();
+ if ( !pSmartTagList )
+ return;
+
+ sal_Int32 nCurrent = aPos.nContent.GetIndex();
+ sal_Int32 nBegin = nCurrent;
+ sal_Int32 nLen = 1;
+
+ if (!pSmartTagList->InWrongWord(nBegin, nLen) || pNode->IsSymbolAt(nBegin))
+ return;
+
+ const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
+ const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
+ if ( pSubList )
+ {
+ pSmartTagList = pSubList;
+ nCurrent = 0;
+ }
+
+ lcl_FillRecognizerData( rSmartTagTypes, rStringKeyMaps, *pSmartTagList, nCurrent );
+ lcl_FillTextRange( rRange, *pNode, nBegin, nLen );
+}
+
+// see also SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect )
+void SwCursorShell::GetSmartTagRect( const Point& rPt, SwRect& rSelectRect )
+{
+ SwPaM* pCursor = GetCursor();
+ SwPosition aPos( *pCursor->GetPoint() );
+ Point aPt( rPt );
+ SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
+ SwSpecialPos aSpecialPos;
+ eTmpState.m_pSpecialPos = &aSpecialPos;
+ SwTextNode *pNode;
+ const SwWrongList *pSmartTagList;
+
+ if( !GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &eTmpState ) )
+ return;
+ pNode = aPos.nNode.GetNode().GetTextNode();
+ if( !pNode )
+ return;
+ pSmartTagList = pNode->GetSmartTags();
+ if( !pSmartTagList )
+ return;
+ if( pNode->IsInProtectSect() )
+ return;
+
+ sal_Int32 nBegin = aPos.nContent.GetIndex();
+ sal_Int32 nLen = 1;
+
+ if (!pSmartTagList->InWrongWord(nBegin, nLen) || pNode->IsSymbolAt(nBegin))
+ return;
+
+ // get smarttag word
+ OUString aText( pNode->GetText().copy(nBegin, nLen) );
+
+ //save the start and end positions of the line and the starting point
+ Push();
+ LeftMargin();
+ const sal_Int32 nLineStart = GetCursor()->GetPoint()->nContent.GetIndex();
+ RightMargin();
+ const sal_Int32 nLineEnd = GetCursor()->GetPoint()->nContent.GetIndex();
+ Pop(PopMode::DeleteCurrent);
+
+ // make sure the selection build later from the data below does not
+ // include "in word" character to the left and right in order to
+ // preserve those. Therefore count those "in words" in order to
+ // modify the selection accordingly.
+ const sal_Unicode* pChar = aText.getStr();
+ sal_Int32 nLeft = 0;
+ while (*pChar++ == CH_TXTATR_INWORD)
+ ++nLeft;
+ pChar = aText.getLength() ? aText.getStr() + aText.getLength() - 1 : nullptr;
+ sal_Int32 nRight = 0;
+ while (pChar && *pChar-- == CH_TXTATR_INWORD)
+ ++nRight;
+
+ aPos.nContent = nBegin + nLeft;
+ pCursor = GetCursor();
+ *pCursor->GetPoint() = aPos;
+ pCursor->SetMark();
+ ExtendSelection( true, nLen - nLeft - nRight );
+ // do not determine the rectangle in the current line
+ const sal_Int32 nWordStart = (nBegin + nLeft) < nLineStart ? nLineStart : nBegin + nLeft;
+ // take one less than the line end - otherwise the next line would
+ // be calculated
+ const sal_Int32 nWordEnd = std::min(nBegin + nLen - nLeft - nRight, nLineEnd);
+ Push();
+ pCursor->DeleteMark();
+ SwIndex& rContent = GetCursor()->GetPoint()->nContent;
+ rContent = nWordStart;
+ SwRect aStartRect;
+ SwCursorMoveState aState;
+ aState.m_bRealWidth = true;
+ SwContentNode* pContentNode = pCursor->GetContentNode();
+ std::pair<Point, bool> const tmp(rPt, false);
+ SwContentFrame *pContentFrame = pContentNode->getLayoutFrame(
+ GetLayout(), pCursor->GetPoint(), &tmp);
+
+ pContentFrame->GetCharRect( aStartRect, *pCursor->GetPoint(), &aState );
+ rContent = nWordEnd - 1;
+ SwRect aEndRect;
+ pContentFrame->GetCharRect( aEndRect, *pCursor->GetPoint(),&aState );
+ rSelectRect = aStartRect.Union( aEndRect );
+ Pop(PopMode::DeleteCurrent);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx
new file mode 100644
index 000000000..adf761742
--- /dev/null
+++ b/sw/source/core/crsr/crstrvl.cxx
@@ -0,0 +1,2743 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <utility>
+#include <hintids.hxx>
+#include <comphelper/string.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <svx/svdobj.hxx>
+#include <osl/diagnose.h>
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <fldbas.hxx>
+#include <swtable.hxx>
+#include <docary.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <txtftn.hxx>
+#include <txtinet.hxx>
+#include <fmtinfmt.hxx>
+#include <txttxmrk.hxx>
+#include <frmfmt.hxx>
+#include <flyfrm.hxx>
+#include <viscrs.hxx>
+#include "callnk.hxx"
+#include <doctxm.hxx>
+#include <docfld.hxx>
+#include <expfld.hxx>
+#include <reffld.hxx>
+#include <flddat.hxx>
+#include <cellatr.hxx>
+#include <swundo.hxx>
+#include <redline.hxx>
+#include <fmtcntnt.hxx>
+#include <fmthdft.hxx>
+#include <pagedesc.hxx>
+#include <fesh.hxx>
+#include <charfmt.hxx>
+#include <fmturl.hxx>
+#include <txtfrm.hxx>
+#include <wrong.hxx>
+#include <calbck.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <docufld.hxx>
+#include <svx/srchdlg.hxx>
+#include <frameformats.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <textcontentcontrol.hxx>
+
+using namespace ::com::sun::star;
+
+void SwCursorShell::MoveCursorToNum()
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ if( ActionPend() )
+ return;
+ CurrShell aCurr( this );
+ // try to set cursor onto this position, at half of the char-
+ // SRectangle's height
+ Point aPt( m_pCurrentCursor->GetPtPos() );
+ std::pair<Point, bool> const tmp(aPt, true);
+ SwContentFrame * pFrame = m_pCurrentCursor->GetContentNode()->getLayoutFrame(
+ GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
+ pFrame->GetCharRect( m_aCharRect, *m_pCurrentCursor->GetPoint() );
+ pFrame->Calc(GetOut());
+ if( pFrame->IsVertical() )
+ {
+ aPt.setX(m_aCharRect.Center().getX());
+ aPt.setY(pFrame->getFrameArea().Top() + GetUpDownX());
+ }
+ else
+ {
+ aPt.setY(m_aCharRect.Center().getY());
+ aPt.setX(pFrame->getFrameArea().Left() + GetUpDownX());
+ }
+ pFrame->GetModelPositionForViewPoint( m_pCurrentCursor->GetPoint(), aPt );
+ if ( !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos ))
+ {
+ UpdateCursor(SwCursorShell::UPDOWN |
+ SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ }
+}
+
+/// go to next/previous point on the same level
+void SwCursorShell::GotoNextNum()
+{
+ if (!SwDoc::GotoNextNum(*m_pCurrentCursor->GetPoint(), GetLayout()))
+ return;
+ MoveCursorToNum();
+}
+
+void SwCursorShell::GotoPrevNum()
+{
+ if (!SwDoc::GotoPrevNum(*m_pCurrentCursor->GetPoint(), GetLayout()))
+ return;
+ MoveCursorToNum();
+}
+
+/// jump from content to header
+bool SwCursorShell::GotoHeaderText()
+{
+ const SwFrame* pFrame = GetCurrFrame()->FindPageFrame();
+ while( pFrame && !pFrame->IsHeaderFrame() )
+ pFrame = pFrame->GetLower();
+ // found header, search 1. content frame
+ while( pFrame && !pFrame->IsContentFrame() )
+ pFrame = pFrame->GetLower();
+
+ if( pFrame )
+ {
+ CurrShell aCurr( this );
+ // get header frame
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursor *pTmpCursor = getShellCursor( true );
+ SwCursorSaveState aSaveState( *pTmpCursor );
+ pFrame->Calc(GetOut());
+ Point aPt( pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos() );
+ pFrame->GetModelPositionForViewPoint( pTmpCursor->GetPoint(), aPt );
+ if( !pTmpCursor->IsSelOvr() )
+ UpdateCursor();
+ else
+ pFrame = nullptr;
+ }
+ return nullptr != pFrame;
+}
+
+/// jump from content to footer
+bool SwCursorShell::GotoFooterText()
+{
+ const SwPageFrame* pFrame = GetCurrFrame()->FindPageFrame();
+ if( pFrame )
+ {
+ const SwFrame* pLower = pFrame->GetLastLower();
+
+ while( pLower && !pLower->IsFooterFrame() )
+ pLower = pLower->GetLower();
+ // found footer, search 1. content frame
+ while( pLower && !pLower->IsContentFrame() )
+ pLower = pLower->GetLower();
+
+ if( pLower )
+ {
+ SwCursor *pTmpCursor = getShellCursor( true );
+ CurrShell aCurr( this );
+ // get position in footer
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pTmpCursor );
+ pLower->Calc(GetOut());
+ Point aPt( pLower->getFrameArea().Pos() + pLower->getFramePrintArea().Pos() );
+ pLower->GetModelPositionForViewPoint( pTmpCursor->GetPoint(), aPt );
+ if( !pTmpCursor->IsSelOvr() )
+ UpdateCursor();
+ else
+ pFrame = nullptr;
+ }
+ else
+ pFrame = nullptr;
+ }
+ else
+ pFrame = nullptr;
+ return nullptr != pFrame;
+}
+
+bool SwCursorShell::SetCursorInHdFt( size_t nDescNo, bool bInHeader )
+{
+ bool bRet = false;
+ SwDoc *pMyDoc = GetDoc();
+ const SwPageDesc* pDesc = nullptr;
+
+ CurrShell aCurr( this );
+
+ if( SIZE_MAX == nDescNo )
+ {
+ // take the current one
+ const SwContentFrame *pCurrFrame = GetCurrFrame();
+ const SwPageFrame* pPage = (pCurrFrame == nullptr) ? nullptr : pCurrFrame->FindPageFrame();
+ if( pPage && pMyDoc->ContainsPageDesc(
+ pPage->GetPageDesc(), &nDescNo) )
+ pDesc = pPage->GetPageDesc();
+ }
+ else
+ if (nDescNo < pMyDoc->GetPageDescCnt())
+ pDesc = &pMyDoc->GetPageDesc( nDescNo );
+
+ if( pDesc )
+ {
+ // check if the attribute exists
+ const SwFormatContent* pCnt = nullptr;
+ if( bInHeader )
+ {
+ // mirrored pages? ignore for now
+ const SwFormatHeader& rHd = pDesc->GetMaster().GetHeader();
+ if( rHd.GetHeaderFormat() )
+ pCnt = &rHd.GetHeaderFormat()->GetContent();
+ }
+ else
+ {
+ const SwFormatFooter& rFt = pDesc->GetMaster().GetFooter();
+ if( rFt.GetFooterFormat() )
+ pCnt = &rFt.GetFooterFormat()->GetContent();
+ }
+
+ if( pCnt && pCnt->GetContentIdx() )
+ {
+ SwNodeIndex aIdx( *pCnt->GetContentIdx(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = pMyDoc->GetNodes().GoNext( &aIdx );
+
+ Point aPt( m_pCurrentCursor->GetPtPos() );
+
+ std::pair<Point, bool> const tmp(aPt, false);
+ if (pCNd && nullptr != pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp))
+ {
+ // then we can set the cursor in here
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ ClearMark();
+
+ SwPosition& rPos = *m_pCurrentCursor->GetPoint();
+ rPos.nNode = *pCNd;
+ rPos.nContent.Assign( pCNd, 0 );
+
+ bRet = !m_pCurrentCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ }
+ }
+ }
+ return bRet;
+}
+
+/// jump to the next index
+bool SwCursorShell::GotoNextTOXBase( const OUString* pName )
+{
+ bool bRet = false;
+
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ SwContentNode* pFnd = nullptr;
+ for( SwSectionFormats::size_type n = rFormats.size(); n; )
+ {
+ const SwSection* pSect = rFormats[ --n ]->GetSection();
+ if (SectionType::ToxContent == pSect->GetType())
+ {
+ SwSectionNode const*const pSectNd(
+ pSect->GetFormat()->GetSectionNode());
+ if ( pSectNd
+ && m_pCurrentCursor->GetPoint()->nNode < pSectNd->GetIndex()
+ && (!pFnd || pFnd->GetIndex() > pSectNd->GetIndex())
+ && (!pName || *pName ==
+ static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()))
+ {
+ SwNodeIndex aIdx(*pSectNd, 1);
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if (!pCNd)
+ pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
+ if (pCNd &&
+ pCNd->EndOfSectionIndex() <= pSectNd->EndOfSectionIndex())
+ {
+ SwContentFrame const*const pCFrame(
+ pCNd->getLayoutFrame(GetLayout()));
+ if (pCFrame &&
+ (IsReadOnlyAvailable() || !pCFrame->IsProtected()))
+ {
+ pFnd = pCNd;
+ }
+ }
+ }
+ }
+ }
+ if( pFnd )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ m_pCurrentCursor->GetPoint()->nNode = *pFnd;
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pFnd, 0 );
+ bRet = !m_pCurrentCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ }
+ return bRet;
+}
+
+/// jump to previous index
+bool SwCursorShell::GotoPrevTOXBase( const OUString* pName )
+{
+ bool bRet = false;
+
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ SwContentNode* pFnd = nullptr;
+ for( SwSectionFormats::size_type n = rFormats.size(); n; )
+ {
+ const SwSection* pSect = rFormats[ --n ]->GetSection();
+ if (SectionType::ToxContent == pSect->GetType())
+ {
+ SwSectionNode const*const pSectNd(
+ pSect->GetFormat()->GetSectionNode());
+ if ( pSectNd
+ && m_pCurrentCursor->GetPoint()->nNode > pSectNd->EndOfSectionIndex()
+ && (!pFnd || pFnd->GetIndex() < pSectNd->GetIndex())
+ && (!pName || *pName ==
+ static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()))
+ {
+ SwNodeIndex aIdx(*pSectNd, 1);
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if (!pCNd)
+ pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
+ if (pCNd &&
+ pCNd->EndOfSectionIndex() <= pSectNd->EndOfSectionIndex())
+ {
+ SwContentFrame const*const pCFrame(
+ pCNd->getLayoutFrame(GetLayout()));
+ if (pCFrame &&
+ (IsReadOnlyAvailable() || !pCFrame->IsProtected()))
+ {
+ pFnd = pCNd;
+ }
+ }
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ m_pCurrentCursor->GetPoint()->nNode = *pFnd;
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pFnd, 0 );
+ bRet = !m_pCurrentCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ }
+ return bRet;
+}
+
+/// jump to index of TOXMark
+void SwCursorShell::GotoTOXMarkBase()
+{
+ SwTOXMarks aMarks;
+ sal_uInt16 nCnt = SwDoc::GetCurTOXMark(*m_pCurrentCursor->GetPoint(), aMarks);
+ if(!nCnt)
+ return;
+ // Take the 1. and get the index type. Ask it for the actual index.
+ const SwTOXType* pType = aMarks[0]->GetTOXType();
+ auto pContentFrame = pType->FindContentFrame(*GetDoc(), *GetLayout());
+ SwCallLink aLk(*this); // watch Cursor-Moves
+ SwCursorSaveState aSaveState(*m_pCurrentCursor);
+ assert(pContentFrame->IsTextFrame());
+ *m_pCurrentCursor->GetPoint() = static_cast<SwTextFrame const*>(pContentFrame)->MapViewToModelPos(TextFrameIndex(0));
+ if(!m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr())
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+}
+
+/// Jump to next/previous table formula
+/// Optionally it is possible to also jump to broken formulas
+bool SwCursorShell::GotoNxtPrvTableFormula( bool bNext, bool bOnlyErrors )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ if( IsTableMode() )
+ return false;
+
+ bool bFnd = false;
+ SwPosition aOldPos = *m_pCurrentCursor->GetPoint();
+ SwPosition& rPos = *m_pCurrentCursor->GetPoint();
+
+ Point aPt;
+ SwPosition aFndPos( GetDoc()->GetNodes().GetEndOfContent() );
+ if( !bNext )
+ aFndPos.nNode = SwNodeOffset(0);
+ SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos );
+
+ {
+ const SwNode* pSttNd = rPos.nNode.GetNode().FindTableBoxStartNode();
+ if( pSttNd )
+ {
+ const SwTableBox* pTBox = pSttNd->FindTableNode()->GetTable().
+ GetTableBox( pSttNd->GetIndex() );
+ if( pTBox )
+ aCurGEF = SetGetExpField( *pTBox );
+ }
+ }
+
+ if( rPos.nNode < GetDoc()->GetNodes().GetEndOfExtras() )
+ {
+ // also at collection use only the first frame
+ std::pair<Point, bool> const tmp(aPt, false);
+ aCurGEF.SetBodyPos( *rPos.nNode.GetNode().GetContentNode()->getLayoutFrame( GetLayout(),
+ &rPos, &tmp) );
+ }
+ {
+ sal_uInt32 nMaxItems = GetDoc()->GetAttrPool().GetItemCount2( RES_BOXATR_FORMULA );
+
+ if( nMaxItems > 0 )
+ {
+ sal_uInt8 nMaxDo = 2;
+ do {
+ for (const SfxPoolItem* pItem : GetDoc()->GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
+ {
+ const SwTableBox* pTBox;
+ auto pFormulaItem = dynamic_cast<const SwTableBoxFormula*>(pItem);
+ if( !pFormulaItem )
+ continue;
+ pTBox = pFormulaItem->GetTableBox();
+ if( pTBox &&
+ pTBox->GetSttNd() &&
+ pTBox->GetSttNd()->GetNodes().IsDocNodes() &&
+ ( !bOnlyErrors ||
+ !pFormulaItem->HasValidBoxes() ) )
+ {
+ SwNodeIndex aIdx( *pTBox->GetSttNd() );
+ const SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
+ std::pair<Point, bool> const tmp(aPt, false);
+ if (pCNd)
+ {
+ const SwContentFrame* pCFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pCFrame && (IsReadOnlyAvailable() || !pCFrame->IsProtected() ))
+ {
+ SetGetExpField aCmp( *pTBox );
+ aCmp.SetBodyPos( *pCFrame );
+
+ if( bNext ? ( aCurGEF < aCmp && aCmp < aFndGEF )
+ : ( aCmp < aCurGEF && aFndGEF < aCmp ))
+ {
+ aFndGEF = aCmp;
+ bFnd = true;
+ }
+ }
+ }
+ }
+ }
+ if( !bFnd )
+ {
+ if( bNext )
+ {
+ rPos.nNode = SwNodeOffset(0);
+ rPos.nContent = 0;
+ aCurGEF = SetGetExpField( rPos );
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
+ }
+ else
+ {
+ aCurGEF = SetGetExpField( SwPosition( GetDoc()->GetNodes().GetEndOfContent() ) );
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
+ }
+ }
+ } while( !bFnd && --nMaxDo );
+ }
+ }
+
+ if( bFnd )
+ {
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ aFndGEF.GetPosOfContent( rPos );
+ m_pCurrentCursor->DeleteMark();
+
+ bFnd = !m_pCurrentCursor->IsSelOvr();
+ if( bFnd )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ }
+ else
+ {
+ rPos = aOldPos;
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ }
+
+ return bFnd;
+}
+
+/// jump to next/previous index marker
+bool SwCursorShell::GotoNxtPrvTOXMark( bool bNext )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ if( IsTableMode() )
+ return false;
+
+ bool bFnd = false;
+ SwPosition& rPos = *m_pCurrentCursor->GetPoint();
+
+ Point aPt;
+ SwPosition aFndPos( GetDoc()->GetNodes().GetEndOfContent() );
+ if( !bNext )
+ aFndPos.nNode = SwNodeOffset(0);
+ SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos );
+
+ if( rPos.nNode.GetIndex() < GetDoc()->GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // also at collection use only the first frame
+ std::pair<Point, bool> const tmp(aPt, false);
+ aCurGEF.SetBodyPos( *rPos.nNode.GetNode().
+ GetContentNode()->getLayoutFrame(GetLayout(), &rPos, &tmp));
+ }
+
+ {
+ const SwTextNode* pTextNd;
+ const SwTextTOXMark* pTextTOX;
+ sal_uInt32 nMaxItems = GetDoc()->GetAttrPool().GetItemCount2( RES_TXTATR_TOXMARK );
+
+ if( nMaxItems > 0 )
+ {
+ do {
+ for (const SfxPoolItem* pItem : GetDoc()->GetAttrPool().GetItemSurrogates(RES_TXTATR_TOXMARK))
+ {
+ auto pToxMarkItem = dynamic_cast<const SwTOXMark*>(pItem);
+ if( !pToxMarkItem )
+ continue;
+ pTextTOX = pToxMarkItem->GetTextTOXMark();
+ if( !pTextTOX )
+ continue;
+ pTextNd = &pTextTOX->GetTextNode();
+ if( !pTextNd->GetNodes().IsDocNodes() )
+ continue;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame* pCFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if( pCFrame && ( IsReadOnlyAvailable() || !pCFrame->IsProtected() ))
+ {
+ SwNodeIndex aNdIndex( *pTextNd ); // UNIX needs this object
+ SetGetExpField aCmp( aNdIndex, *pTextTOX );
+ aCmp.SetBodyPos( *pCFrame );
+
+ if( bNext ? ( aCurGEF < aCmp && aCmp < aFndGEF )
+ : ( aCmp < aCurGEF && aFndGEF < aCmp ))
+ {
+ aFndGEF = aCmp;
+ bFnd = true;
+ }
+ }
+ }
+ if( !bFnd )
+ {
+ if ( bNext )
+ {
+ rPos.nNode = SwNodeOffset(0);
+ rPos.nContent = 0;
+ aCurGEF = SetGetExpField( rPos );
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
+ }
+ else
+ {
+ aCurGEF = SetGetExpField( SwPosition( GetDoc()->GetNodes().GetEndOfContent() ) );
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
+ }
+ }
+ } while ( !bFnd );
+ }
+ else
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ }
+
+ if( bFnd )
+ {
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ aFndGEF.GetPosOfContent( rPos );
+
+ bFnd = !m_pCurrentCursor->IsSelOvr();
+ if( bFnd )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ }
+ return bFnd;
+}
+
+/// traveling between marks
+const SwTOXMark& SwCursorShell::GotoTOXMark( const SwTOXMark& rStart,
+ SwTOXSearch eDir )
+{
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ const SwTOXMark& rNewMark = GetDoc()->GotoTOXMark( rStart, eDir,
+ IsReadOnlyAvailable() );
+ // set position
+ SwPosition& rPos = *GetCursor()->GetPoint();
+ rPos.nNode = rNewMark.GetTextTOXMark()->GetTextNode();
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
+ rNewMark.GetTextTOXMark()->GetStart() );
+
+ if( !m_pCurrentCursor->IsSelOvr() )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+
+ return rNewMark;
+}
+
+/// jump to next/previous field type
+static void lcl_MakeFieldLst(
+ SetGetExpFields& rLst,
+ const SwFieldType& rFieldType,
+ const bool bInReadOnly,
+ const bool bChkInpFlag = false )
+{
+ // always search the 1. frame
+ Point aPt;
+ std::vector<SwFormatField*> vFields;
+ rFieldType.GatherFields(vFields, false);
+ for(SwFormatField* pFormatField: vFields)
+ {
+ SwTextField* pTextField = pFormatField->GetTextField();
+ if ( pTextField != nullptr
+ && ( !bChkInpFlag
+ || static_cast<const SwSetExpField*>(pTextField->GetFormatField().GetField())->GetInputFlag() ) )
+ {
+ const SwTextNode& rTextNode = pTextField->GetTextNode();
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame* pCFrame =
+ rTextNode.getLayoutFrame(
+ rTextNode.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ if ( pCFrame != nullptr
+ && ( bInReadOnly || !pCFrame->IsProtected() ) )
+ {
+ std::unique_ptr<SetGetExpField> pNew(new SetGetExpField( SwNodeIndex( rTextNode ), pTextField ));
+ pNew->SetBodyPos( *pCFrame );
+ rLst.insert( std::move(pNew) );
+ }
+ }
+ }
+}
+
+static SetGetExpFields::const_iterator
+lcl_FindField(bool & o_rFound, SetGetExpFields const& rSrtLst,
+ SwRootFrame const *const pLayout, SwTextNode *const pTextNode,
+ SwTextField const *const pTextField, SwPosition const& rPos,
+ sal_Int32 const nContentOffset)
+{
+ std::unique_ptr<SetGetExpField> pSrch;
+ std::unique_ptr<SwIndex> pIndex;
+ if (-1 == nContentOffset)
+ {
+ pSrch.reset(new SetGetExpField(rPos.nNode, pTextField, &rPos.nContent));
+ }
+ else
+ {
+ pIndex.reset(new SwIndex(rPos.nNode.GetNode().GetContentNode(), nContentOffset));
+ pSrch.reset(new SetGetExpField(rPos.nNode, pTextField, pIndex.get()));
+ }
+
+ if (rPos.nNode.GetIndex() < pTextNode->GetNodes().GetEndOfExtras().GetIndex())
+ {
+ // also at collection use only the first frame
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ pSrch->SetBodyPos(*pTextNode->getLayoutFrame(pLayout, &rPos, &tmp));
+ }
+
+ SetGetExpFields::const_iterator it = rSrtLst.lower_bound(pSrch.get());
+
+ o_rFound = (it != rSrtLst.end()) && (**it == *pSrch);
+ return it;
+}
+
+bool SwCursorShell::MoveFieldType(
+ const SwFieldType* pFieldType,
+ const bool bNext,
+ const SwFieldIds nResType,
+ const bool bAddSetExpressionFieldsToInputFields )
+{
+ // sorted list of all fields
+ SetGetExpFields aSrtLst;
+
+ if ( pFieldType )
+ {
+ if( SwFieldIds::Input != pFieldType->Which() && !pFieldType->HasWriterListeners() )
+ {
+ return false;
+ }
+
+ // found Modify object, add all fields to array
+ ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable() );
+
+ if( SwFieldIds::Input == pFieldType->Which() && bAddSetExpressionFieldsToInputFields )
+ {
+ // there are hidden input fields in the set exp. fields
+ const SwFieldTypes& rFieldTypes = *mxDoc->getIDocumentFieldsAccess().GetFieldTypes();
+ const size_t nSize = rFieldTypes.size();
+ for( size_t i=0; i < nSize; ++i )
+ {
+ pFieldType = rFieldTypes[ i ].get();
+ if ( SwFieldIds::SetExp == pFieldType->Which() )
+ {
+ ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable(), true );
+ }
+ }
+ }
+ }
+ else
+ {
+ const SwFieldTypes& rFieldTypes = *mxDoc->getIDocumentFieldsAccess().GetFieldTypes();
+ const size_t nSize = rFieldTypes.size();
+ const bool bAllFieldTypes = nResType == SwFieldIds::Unknown;
+ for( size_t i=0; i < nSize; ++i )
+ {
+ pFieldType = rFieldTypes[ i ].get();
+ if (bAllFieldTypes || nResType == pFieldType->Which())
+ {
+ ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable() );
+ }
+ }
+ }
+
+ // found no fields?
+ if( aSrtLst.empty() )
+ return false;
+
+ SetGetExpFields::const_iterator it;
+ SwCursor* pCursor = getShellCursor( true );
+ {
+ // (1998): Always use field for search so that the right one is found as
+ // well some are in frames that are anchored to a paragraph that has a
+ // field
+ const SwPosition& rPos = *pCursor->GetPoint();
+
+ SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTNd, "No ContentNode" );
+
+ SwTextField * pTextField = pTNd->GetFieldTextAttrAt( rPos.nContent.GetIndex(), true );
+ const bool bDelField = ( pTextField == nullptr );
+ sal_Int32 nContentOffset = -1;
+
+ if( bDelField )
+ {
+ // create dummy for the search
+ SwFormatField* pFormatField = new SwFormatField( SwDateTimeField(
+ static_cast<SwDateTimeFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DateTime ) ) ) );
+
+ pTextField = new SwTextField( *pFormatField, rPos.nContent.GetIndex(),
+ mxDoc->IsClipBoard() );
+ pTextField->ChgTextNode( pTNd );
+ }
+ else
+ {
+ // the cursor might be anywhere inside the input field,
+ // but we will be searching for the field start
+ if (pTextField->Which() == RES_TXTATR_INPUTFIELD
+ && rPos.nContent.GetIndex() != pTextField->GetStart())
+ nContentOffset = pTextField->GetStart();
+ }
+ bool isSrch;
+ it = lcl_FindField(isSrch, aSrtLst,
+ GetLayout(), pTNd, pTextField, rPos, nContentOffset);
+
+ if( bDelField )
+ {
+ auto const pFormat(static_cast<SwFormatField*>(&pTextField->GetAttr()));
+ delete pTextField;
+ delete pFormat;
+ }
+
+ if( it != aSrtLst.end() && isSrch ) // found
+ {
+ if( bNext )
+ {
+ if( ++it == aSrtLst.end() )
+ return false; // already at the end
+ }
+ else
+ {
+ if( it == aSrtLst.begin() )
+ return false; // no more steps backward possible
+ --it;
+ }
+ }
+ else // not found
+ {
+ if( bNext )
+ {
+ if( it == aSrtLst.end() )
+ return false;
+ }
+ else
+ {
+ if( it == aSrtLst.begin() )
+ return false; // no more steps backward possible
+ --it;
+ }
+ }
+ }
+ const SetGetExpField& rFnd = **it;
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pCursor );
+
+ rFnd.GetPosOfContent( *pCursor->GetPoint() );
+ bool bRet = !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle );
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return bRet;
+}
+
+bool SwCursorShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote)
+{
+ if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
+ pWrtSh->addCurrentPosition();
+
+ bool bRet = false;
+ SwCursor* pCursor = getShellCursor(true);
+
+ CurrShell aCurr(this);
+ SwCallLink aLk(*this); // watch Cursor-Moves
+ SwCursorSaveState aSaveState(*pCursor);
+
+ pCursor->GetPoint()->nNode = rTextFootnote.GetTextNode();
+ pCursor->GetPoint()->nContent.Assign(const_cast<SwTextNode*>(&rTextFootnote.GetTextNode()),
+ rTextFootnote.GetStart());
+ bRet = !pCursor->IsSelOvr();
+ if (bRet)
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return bRet;
+}
+
+bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rContentControl)
+{
+ bool bRet = false;
+ std::shared_ptr<SwContentControl> pContentControl = rContentControl.GetContentControl();
+ if (!pContentControl->GetShowingPlaceHolder() && !pContentControl->GetCheckbox()
+ && !pContentControl->GetSelectedListItem() && !pContentControl->GetSelectedDate())
+ {
+ return bRet;
+ }
+
+ const SwTextContentControl* pTextContentControl = pContentControl->GetTextAttr();
+ if (pTextContentControl)
+ {
+ CurrShell aCurr(this);
+ SwCallLink aLink(*this);
+
+ SwCursor* pCursor = getShellCursor(true);
+ SwCursorSaveState aSaveState(*pCursor);
+
+ pCursor->SetMark();
+ SwTextNode* pTextNode = pContentControl->GetTextNode();
+ pCursor->GetPoint()->nNode = *pTextNode;
+ // Don't select the text attribute itself at the start.
+ sal_Int32 nStart = pTextContentControl->GetStart() + 1;
+ pCursor->GetPoint()->nContent.Assign(pTextNode, nStart);
+ pCursor->GetMark()->nNode = *pTextNode;
+ // Don't select the CH_TXTATR_BREAKWORD itself at the end.
+ sal_Int32 nEnd = *pTextContentControl->End() - 1;
+ pCursor->GetMark()->nContent.Assign(pTextNode, nEnd);
+
+ // Assume that once the placeholder is selected, the content is no longer the placeholder.
+ pContentControl->SetShowingPlaceHolder(false);
+
+ bRet = !pCursor->IsSelOvr();
+ if (bRet)
+ {
+ UpdateCursor(SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE
+ | SwCursorShell::READONLY);
+ }
+ }
+
+ return bRet;
+}
+
+bool SwCursorShell::GotoFormatField( const SwFormatField& rField )
+{
+ bool bRet = false;
+ SwTextField const*const pTextField(rField.GetTextField());
+ if (pTextField
+ && (!GetLayout()->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(
+ GetDoc()->getIDocumentRedlineAccess(), *pTextField)))
+ {
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+
+ SwCursor* pCursor = getShellCursor( true );
+ SwCursorSaveState aSaveState( *pCursor );
+
+ SwTextNode* pTNd = pTextField->GetpTextNode();
+ pCursor->GetPoint()->nNode = *pTNd;
+ pCursor->GetPoint()->nContent.Assign( pTNd, pTextField->GetStart() );
+
+ bRet = !pCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ }
+ return bRet;
+}
+
+SwTextField * SwCursorShell::GetTextFieldAtPos(
+ const SwPosition* pPos,
+ const bool bIncludeInputFieldAtStart )
+{
+ SwTextField* pTextField = nullptr;
+
+ SwTextNode * const pNode = pPos->nNode.GetNode().GetTextNode();
+ if ( pNode != nullptr )
+ {
+ pTextField = pNode->GetFieldTextAttrAt( pPos->nContent.GetIndex(), bIncludeInputFieldAtStart );
+ }
+
+ return pTextField;
+}
+
+SwTextField* SwCursorShell::GetTextFieldAtCursor(
+ const SwPaM* pCursor,
+ const bool bIncludeInputFieldAtStart )
+{
+ SwTextField* pFieldAtCursor = nullptr;
+
+ SwTextField* pTextField = GetTextFieldAtPos( pCursor->Start(), bIncludeInputFieldAtStart );
+ if ( pTextField != nullptr
+ && pCursor->Start()->nNode == pCursor->End()->nNode )
+ {
+ const sal_Int32 nTextFieldLength =
+ pTextField->End() != nullptr
+ ? *(pTextField->End()) - pTextField->GetStart()
+ : 1;
+ if ( ( pCursor->End()->nContent.GetIndex() - pCursor->Start()->nContent.GetIndex() ) <= nTextFieldLength )
+ {
+ 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();
+ if ( pCursor->IsMultiSelection() )
+ {
+ // multi selection not handled.
+ return nullptr;
+ }
+
+ SwField* pCurField = GetFieldAtCursor( pCursor, bIncludeInputFieldAtStart );
+ if ( pCurField != nullptr
+ && SwFieldIds::Table == pCurField->GetTyp()->Which() )
+ {
+ // table formula? convert internal name into external
+ const SwTableNode* pTableNd = IsCursorInTable();
+ static_cast<SwTableField*>(pCurField)->PtrToBoxNm( pTableNd ? &pTableNd->GetTable() : nullptr );
+ }
+
+ return pCurField;
+}
+
+bool SwCursorShell::CursorInsideInputField() const
+{
+ for(SwPaM& rCursor : GetCursor()->GetRingContainer())
+ {
+ if (dynamic_cast<const SwTextInputField*>(GetTextFieldAtCursor(&rCursor, true)))
+ return true;
+ }
+ return false;
+}
+
+SwTextContentControl* SwCursorShell::CursorInsideContentControl() const
+{
+ for (SwPaM& rCursor : GetCursor()->GetRingContainer())
+ {
+ const SwPosition* pStart = rCursor.Start();
+ SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ continue;
+ }
+
+ sal_Int32 nIndex = pStart->nContent.GetIndex();
+ if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT))
+ {
+ return static_txtattr_cast<SwTextContentControl*>(pAttr);
+ }
+ }
+
+ return nullptr;
+}
+
+bool SwCursorShell::PosInsideInputField( const SwPosition& rPos )
+{
+ return dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos( &rPos, false )) != nullptr;
+}
+
+bool SwCursorShell::DocPtInsideInputField( const Point& rDocPt ) const
+{
+ SwPosition aPos( *(GetCursor()->Start()) );
+ Point aDocPt( rDocPt );
+ if ( GetLayout()->GetModelPositionForViewPoint( &aPos, aDocPt ) )
+ {
+ return PosInsideInputField( aPos );
+ }
+ return false;
+}
+
+sal_Int32 SwCursorShell::StartOfInputFieldAtPos( const SwPosition& rPos )
+{
+ const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos( &rPos, true ));
+ assert(pTextInputField != nullptr
+ && "<SwEditShell::StartOfInputFieldAtPos(..)> - no Input Field at given position");
+ return pTextInputField->GetStart();
+}
+
+sal_Int32 SwCursorShell::EndOfInputFieldAtPos( const SwPosition& rPos )
+{
+ const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos( &rPos, true ));
+ assert(pTextInputField != nullptr
+ && "<SwEditShell::EndOfInputFieldAtPos(..)> - no Input Field at given position");
+ return *(pTextInputField->End());
+}
+
+void SwCursorShell::GotoOutline( SwOutlineNodes::size_type nIdx )
+{
+ SwCursor* pCursor = getShellCursor( true );
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pCursor );
+
+ const SwNodes& rNds = GetDoc()->GetNodes();
+ SwTextNode* pTextNd = rNds.GetOutLineNds()[ nIdx ]->GetTextNode();
+ pCursor->GetPoint()->nNode = *pTextNd;
+ pCursor->GetPoint()->nContent.Assign( pTextNd, 0 );
+
+ if( !pCursor->IsSelOvr() )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+}
+
+bool SwCursorShell::GotoOutline( const OUString& rName )
+{
+ SwCursor* pCursor = getShellCursor( true );
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pCursor );
+
+ bool bRet = false;
+ if (mxDoc->GotoOutline(*pCursor->GetPoint(), rName, GetLayout())
+ && !pCursor->IsSelOvr())
+ {
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ bRet = true;
+ }
+ return bRet;
+}
+
+/// jump to next node with outline num.
+bool SwCursorShell::GotoNextOutline()
+{
+ const SwNodes& rNds = GetDoc()->GetNodes();
+
+ if ( rNds.GetOutLineNds().empty() )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+
+ SwCursor* pCursor = getShellCursor( true );
+ SwNode* pNd = &(pCursor->GetNode());
+ SwOutlineNodes::size_type nPos;
+ bool bUseFirst = !rNds.GetOutLineNds().Seek_Entry( pNd, &nPos );
+ SwOutlineNodes::size_type const nStartPos(nPos);
+
+ do
+ {
+ if (!bUseFirst)
+ {
+ ++nPos;
+ }
+ if (rNds.GetOutLineNds().size() <= nPos)
+ {
+ nPos = 0;
+ }
+
+ if (bUseFirst)
+ {
+ bUseFirst = false;
+ }
+ else
+ {
+ if (nPos == nStartPos)
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+ }
+
+ pNd = rNds.GetOutLineNds()[ nPos ];
+ }
+ while (!sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode()));
+
+ if (nPos < nStartPos)
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
+ }
+ else
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+ }
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pCursor );
+ pCursor->GetPoint()->nNode = *pNd;
+ pCursor->GetPoint()->nContent.Assign( pNd->GetTextNode(), 0 );
+
+ bool bRet = !pCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return bRet;
+}
+
+/// jump to previous node with outline num.
+bool SwCursorShell::GotoPrevOutline()
+{
+ const SwNodes& rNds = GetDoc()->GetNodes();
+
+ if ( rNds.GetOutLineNds().empty() )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+
+ SwCursor* pCursor = getShellCursor( true );
+ SwNode* pNd = &(pCursor->GetNode());
+ SwOutlineNodes::size_type nPos;
+ bool bRet = false;
+ (void)rNds.GetOutLineNds().Seek_Entry(pNd, &nPos);
+ SwOutlineNodes::size_type const nStartPos(nPos);
+
+ do
+ {
+ if (nPos == 0)
+ {
+ nPos = rNds.GetOutLineNds().size() - 1;
+ }
+ else
+ {
+ --nPos; // before
+ }
+ if (nPos == nStartPos)
+ {
+ pNd = nullptr;
+ break;
+ }
+
+ pNd = rNds.GetOutLineNds()[ nPos ];
+ }
+ while (!sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode()));
+
+ if (pNd)
+ {
+ if (nStartPos < nPos)
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
+ }
+ else
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+ }
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pCursor );
+ pCursor->GetPoint()->nNode = *pNd;
+ pCursor->GetPoint()->nContent.Assign( pNd->GetTextNode(), 0 );
+
+ bRet = !pCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ }
+ else
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ }
+ return bRet;
+}
+
+/// search "outline position" before previous outline node at given level
+SwOutlineNodes::size_type SwCursorShell::GetOutlinePos(sal_uInt8 nLevel, SwPaM* pPaM)
+{
+ SwPaM* pCursor = pPaM ? pPaM : getShellCursor(true);
+ const SwNodes& rNds = GetDoc()->GetNodes();
+
+ SwNode* pNd = &(pCursor->GetNode());
+ SwOutlineNodes::size_type nPos;
+ if( rNds.GetOutLineNds().Seek_Entry( pNd, &nPos ))
+ nPos++; // is at correct position; take next for while
+
+ while( nPos-- ) // check the one in front of the current
+ {
+ pNd = rNds.GetOutLineNds()[ nPos ];
+
+ if (sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode())
+ && pNd->GetTextNode()->GetAttrOutlineLevel()-1 <= nLevel)
+ {
+ if (pNd->GetIndex() < rNds.GetEndOfExtras().GetIndex()
+ && pCursor->GetNode().GetIndex() > rNds.GetEndOfExtras().GetIndex())
+ {
+ // node found in extras but cursor position is not in extras
+ return SwOutlineNodes::npos;
+ }
+ return nPos;
+ }
+ }
+ return SwOutlineNodes::npos; // no more left
+}
+
+void SwCursorShell::MakeOutlineSel(SwOutlineNodes::size_type nSttPos, SwOutlineNodes::size_type nEndPos,
+ bool bWithChildren , bool bKillPams)
+{
+ const SwNodes& rNds = GetDoc()->GetNodes();
+ const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
+ if( rOutlNds.empty() )
+ return;
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+
+ if( nSttPos > nEndPos ) // parameters switched?
+ {
+ OSL_ENSURE( false, "Start > End for array access" );
+ std::swap(nSttPos, nEndPos);
+ }
+
+ SwNode* pSttNd = rOutlNds[ nSttPos ];
+ SwNode* pEndNd = rOutlNds[ nEndPos ];
+
+ if( bWithChildren )
+ {
+ const int nLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
+ for( ++nEndPos; nEndPos < rOutlNds.size(); ++nEndPos )
+ {
+ pEndNd = rOutlNds[ nEndPos ];
+ const int nNxtLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
+ if( nNxtLevel <= nLevel )
+ break; // EndPos is now on the next one
+ }
+ }
+ // if without children then set onto next one
+ else if( ++nEndPos < rOutlNds.size() )
+ pEndNd = rOutlNds[ nEndPos ];
+
+ if( nEndPos == rOutlNds.size() ) // no end found
+ pEndNd = &rNds.GetEndOfContent();
+
+ if( bKillPams )
+ KillPams();
+
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ // set end to the end of the previous content node
+ m_pCurrentCursor->GetPoint()->nNode = *pSttNd;
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pSttNd->GetContentNode(), 0 );
+ m_pCurrentCursor->SetMark();
+ m_pCurrentCursor->GetPoint()->nNode = *pEndNd;
+ m_pCurrentCursor->Move( fnMoveBackward, GoInNode ); // end of predecessor
+
+ // and everything is already selected
+ bool bRet = !m_pCurrentCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+}
+
+/// jump to reference marker
+bool SwCursorShell::GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType,
+ sal_uInt16 nSeqNo )
+{
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ sal_Int32 nPos = -1;
+ SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( GetDoc(), rRefMark,
+ nSubType, nSeqNo, &nPos, nullptr, GetLayout());
+ if( pTextNd && pTextNd->GetNodes().IsDocNodes() )
+ {
+ m_pCurrentCursor->GetPoint()->nNode = *pTextNd;
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pTextNd, nPos );
+
+ if( !m_pCurrentCursor->IsSelOvr() )
+ {
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwCursorShell::IsPageAtPos( const Point &rPt ) const
+{
+ if( GetLayout() )
+ return nullptr != GetLayout()->GetPageAtPos( rPt );
+ return false;
+}
+
+bool SwCursorShell::GetContentAtPos( const Point& rPt,
+ SwContentAtPos& rContentAtPos,
+ bool bSetCursor,
+ SwRect* pFieldRect )
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ if( !IsTableMode() )
+ {
+ Point aPt( rPt );
+ SwPosition aPos( *m_pCurrentCursor->GetPoint() );
+
+ SwTextNode* pTextNd;
+ SwCursorMoveState aTmpState;
+ aTmpState.m_bFieldInfo = true;
+ aTmpState.m_bExactOnly = !( IsAttrAtPos::Outline & rContentAtPos.eContentAtPos );
+ aTmpState.m_bContentCheck = bool(IsAttrAtPos::ContentCheck & rContentAtPos.eContentAtPos);
+ aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
+
+ SwSpecialPos aSpecialPos;
+ aTmpState.m_pSpecialPos = ( IsAttrAtPos::SmartTag & rContentAtPos.eContentAtPos ) ?
+ &aSpecialPos : nullptr;
+
+ const bool bCursorFoundExact = GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState );
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+
+ const SwNodes& rNds = GetDoc()->GetNodes();
+ if( pTextNd
+ && IsAttrAtPos::Outline & rContentAtPos.eContentAtPos
+ && !rNds.GetOutLineNds().empty() )
+ {
+ // only for nodes in outline nodes
+ SwOutlineNodes::size_type nPos;
+ if(rNds.GetOutLineNds().Seek_Entry(pTextNd, &nPos))
+ {
+ rContentAtPos.eContentAtPos = IsAttrAtPos::Outline;
+ rContentAtPos.sStr = sw::GetExpandTextMerged(GetLayout(), *pTextNd, true, false, ExpandMode::ExpandFootnote);
+ rContentAtPos.aFnd.pNode = pTextNd;
+ bRet = true;
+ }
+ }
+ else if ( IsAttrAtPos::ContentCheck & rContentAtPos.eContentAtPos
+ && bCursorFoundExact )
+ {
+ bRet = true;
+ }
+ else if( pTextNd
+ && IsAttrAtPos::NumLabel & rContentAtPos.eContentAtPos)
+ {
+ bRet = aTmpState.m_bInNumPortion;
+ rContentAtPos.aFnd.pNode = sw::GetParaPropsNode(*GetLayout(), aPos.nNode);
+
+ Size aSizeLogic(aTmpState.m_nInNumPortionOffset, 0);
+ Size aSizePixel = GetWin()->LogicToPixel(aSizeLogic);
+ rContentAtPos.nDist = aSizePixel.Width();
+ }
+ else if( bCursorFoundExact && pTextNd )
+ {
+ SwContentFrame *pFrame(nullptr);
+ if( !aTmpState.m_bPosCorr )
+ {
+ SwTextAttr* pTextAttr;
+ if ( IsAttrAtPos::SmartTag & rContentAtPos.eContentAtPos
+ && !aTmpState.m_bFootnoteNoInfo )
+ {
+ const SwWrongList* pSmartTagList = pTextNd->GetSmartTags();
+ sal_Int32 nCurrent = aPos.nContent.GetIndex();
+ const sal_Int32 nBegin = nCurrent;
+ sal_Int32 nLen = 1;
+
+ if (pSmartTagList && pSmartTagList->InWrongWord(nCurrent, nLen) && !pTextNd->IsSymbolAt(nBegin))
+ {
+ const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
+ const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
+ if ( pSubList )
+ {
+ nCurrent = aTmpState.m_pSpecialPos->nCharOfst;
+
+ if ( pSubList->InWrongWord( nCurrent, nLen ) )
+ bRet = true;
+ }
+ else
+ bRet = true;
+
+ if( bRet && bSetCursor )
+ {
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = aPos;
+ if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | SwCursorSelOverFlags::Toggle) )
+ bRet = false;
+ else
+ UpdateCursor();
+ }
+ if( bRet )
+ {
+ rContentAtPos.eContentAtPos = IsAttrAtPos::SmartTag;
+
+ std::pair<Point, bool> tmp(aPt, true);
+ if (pFieldRect)
+ {
+ pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pFrame)
+ pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
+ }
+ }
+ }
+ }
+
+ if ( !bRet
+ && ( IsAttrAtPos::Field | IsAttrAtPos::ClickField ) & rContentAtPos.eContentAtPos
+ && !aTmpState.m_bFootnoteNoInfo )
+ {
+ pTextAttr = pTextNd->GetFieldTextAttrAt( aPos.nContent.GetIndex() );
+ const SwField* pField = pTextAttr != nullptr
+ ? pTextAttr->GetFormatField().GetField()
+ : nullptr;
+ if ( IsAttrAtPos::ClickField & rContentAtPos.eContentAtPos
+ && pField && !pField->HasClickHdl() )
+ {
+ pField = nullptr;
+ }
+
+ if ( pField )
+ {
+ if (pFieldRect)
+ {
+ std::pair<Point, bool> tmp(aPt, true);
+ pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pFrame)
+ {
+ //tdf#116397 now that we looking for the bounds of the field drop the SmartTag
+ //index within field setting so we don't the bounds of the char within the field
+ SwSpecialPos* pSpecialPos = aTmpState.m_pSpecialPos;
+ aTmpState.m_pSpecialPos = nullptr;
+ pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
+ aTmpState.m_pSpecialPos = pSpecialPos;
+ }
+ }
+
+ if( bSetCursor )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = aPos;
+ if( m_pCurrentCursor->IsSelOvr() )
+ {
+ // allow click fields in protected sections
+ // only placeholder is not possible
+ if( IsAttrAtPos::Field & rContentAtPos.eContentAtPos
+ || SwFieldIds::JumpEdit == pField->Which() )
+ pField = nullptr;
+ }
+ else
+ UpdateCursor();
+ }
+ else if( SwFieldIds::Table == pField->Which() &&
+ static_cast<const SwTableField*>(pField)->IsIntrnlName() )
+ {
+ // create from internal (for CORE) the external
+ // (for UI) formula
+ const SwTableNode* pTableNd = pTextNd->FindTableNode();
+ if( pTableNd ) // is in a table
+ const_cast<SwTableField*>(static_cast<const SwTableField*>(pField))->PtrToBoxNm( &pTableNd->GetTable() );
+ }
+ }
+
+ if( pField )
+ {
+ rContentAtPos.aFnd.pField = pField;
+ rContentAtPos.pFndTextAttr = pTextAttr;
+ rContentAtPos.eContentAtPos = IsAttrAtPos::Field;
+ bRet = true;
+ }
+ }
+
+ if( !bRet && IsAttrAtPos::FormControl & rContentAtPos.eContentAtPos )
+ {
+ IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess( );
+ sw::mark::IFieldmark* pFieldBookmark = pMarksAccess->getFieldmarkFor( aPos );
+ if (bCursorFoundExact && pFieldBookmark)
+ {
+ rContentAtPos.eContentAtPos = IsAttrAtPos::FormControl;
+ rContentAtPos.aFnd.pFieldmark = pFieldBookmark;
+ bRet=true;
+ }
+ }
+
+ if (!bRet && rContentAtPos.eContentAtPos & IsAttrAtPos::ContentControl)
+ {
+ SwTextAttr* pAttr = pTextNd->GetTextAttrAt(
+ aPos.nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+ if (pAttr)
+ {
+ rContentAtPos.eContentAtPos = IsAttrAtPos::ContentControl;
+ rContentAtPos.pFndTextAttr = pAttr;
+ bRet = true;
+ }
+ }
+
+ if( !bRet && IsAttrAtPos::Ftn & rContentAtPos.eContentAtPos )
+ {
+ if( aTmpState.m_bFootnoteNoInfo )
+ {
+ // over the footnote's char
+ bRet = true;
+ if( bSetCursor )
+ {
+ *m_pCurrentCursor->GetPoint() = aPos;
+ if( !GotoFootnoteAnchor() )
+ bRet = false;
+ }
+ if( bRet )
+ rContentAtPos.eContentAtPos = IsAttrAtPos::Ftn;
+ }
+ else if ( nullptr != ( pTextAttr = pTextNd->GetTextAttrForCharAt(
+ aPos.nContent.GetIndex(), RES_TXTATR_FTN )) )
+ {
+ bRet = true;
+ if( bSetCursor )
+ {
+ if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
+ pWrtSh->addCurrentPosition();
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ m_pCurrentCursor->GetPoint()->nNode = *static_cast<SwTextFootnote*>(pTextAttr)->GetStartNode();
+ SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection(
+ &m_pCurrentCursor->GetPoint()->nNode,
+ true, !IsReadOnlyAvailable() );
+
+ if( pCNd )
+ {
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, 0 );
+ if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle ))
+ bRet = false;
+ else
+ UpdateCursor();
+ }
+ else
+ bRet = false;
+ }
+
+ if( bRet )
+ {
+ rContentAtPos.eContentAtPos = IsAttrAtPos::Ftn;
+ rContentAtPos.pFndTextAttr = pTextAttr;
+ rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
+
+ if (pFieldRect)
+ {
+ std::pair<Point, bool> tmp(aPt, true);
+ pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pFrame)
+ pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
+ }
+ }
+ }
+ }
+
+ if( !bRet
+ && ( IsAttrAtPos::ToxMark | IsAttrAtPos::RefMark ) & rContentAtPos.eContentAtPos
+ && !aTmpState.m_bFootnoteNoInfo )
+ {
+ pTextAttr = nullptr;
+ if( IsAttrAtPos::ToxMark & rContentAtPos.eContentAtPos )
+ {
+ std::vector<SwTextAttr *> const marks(
+ pTextNd->GetTextAttrsAt(
+ aPos.nContent.GetIndex(), RES_TXTATR_TOXMARK));
+ if (!marks.empty())
+ { // hmm... can only return 1 here
+ pTextAttr = *marks.begin();
+ }
+ }
+
+ if( !pTextAttr &&
+ IsAttrAtPos::RefMark & rContentAtPos.eContentAtPos )
+ {
+ std::vector<SwTextAttr *> const marks(
+ pTextNd->GetTextAttrsAt(
+ aPos.nContent.GetIndex(), RES_TXTATR_REFMARK));
+ if (!marks.empty())
+ { // hmm... can only return 1 here
+ pTextAttr = *marks.begin();
+ }
+ }
+
+ if( pTextAttr )
+ {
+ bRet = true;
+ if( bSetCursor )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = aPos;
+ if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | SwCursorSelOverFlags::Toggle ) )
+ bRet = false;
+ else
+ UpdateCursor();
+ }
+
+ if( bRet )
+ {
+ const sal_Int32* pEnd = pTextAttr->GetEnd();
+ if( pEnd )
+ rContentAtPos.sStr =
+ pTextNd->GetExpandText(GetLayout(), pTextAttr->GetStart(), *pEnd - pTextAttr->GetStart());
+ else if( RES_TXTATR_TOXMARK == pTextAttr->Which())
+ rContentAtPos.sStr =
+ pTextAttr->GetTOXMark().GetAlternativeText();
+
+ rContentAtPos.eContentAtPos =
+ RES_TXTATR_TOXMARK == pTextAttr->Which()
+ ? IsAttrAtPos::ToxMark
+ : IsAttrAtPos::RefMark;
+ rContentAtPos.pFndTextAttr = pTextAttr;
+ rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
+
+ std::pair<Point, bool> tmp(aPt, true);
+ if (pFieldRect)
+ {
+ pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pFrame)
+ pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
+ }
+ }
+ }
+ }
+
+ if ( !bRet
+ && IsAttrAtPos::InetAttr & rContentAtPos.eContentAtPos
+ && !aTmpState.m_bFootnoteNoInfo )
+ {
+ sal_Int32 index = aPos.nContent.GetIndex();
+ pTextAttr = pTextNd->GetTextAttrAt(index, RES_TXTATR_INETFMT);
+
+ if(!pTextAttr && index > 0)
+ pTextAttr = pTextNd->GetTextAttrAt(index - 1, RES_TXTATR_INETFMT);
+ // "detect" only INetAttrs with URLs
+ if( pTextAttr && !pTextAttr->GetINetFormat().GetValue().isEmpty() )
+ {
+ bRet = true;
+ if( bSetCursor )
+ {
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ m_pCurrentCursor->DeleteMark();
+ *m_pCurrentCursor->GetPoint() = aPos;
+ if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle) )
+ bRet = false;
+ else
+ UpdateCursor();
+ }
+ if( bRet )
+ {
+ const sal_Int32 nSt = pTextAttr->GetStart();
+ const sal_Int32 nEnd = *pTextAttr->End();
+
+ rContentAtPos.sStr = pTextNd->GetExpandText(GetLayout(), nSt, nEnd-nSt);
+
+ rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
+ rContentAtPos.eContentAtPos = IsAttrAtPos::InetAttr;
+ rContentAtPos.pFndTextAttr = pTextAttr;
+
+ if (pFieldRect)
+ {
+ std::pair<Point, bool> tmp(aPt, true);
+ pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pFrame)
+ {
+ //get bounding box of range
+ SwRect aStart;
+ SwPosition aStartPos(*pTextNd, nSt);
+ pFrame->GetCharRect(aStart, aStartPos, &aTmpState);
+ SwRect aEnd;
+ SwPosition aEndPos(*pTextNd, nEnd);
+ pFrame->GetCharRect(aEnd, aEndPos, &aTmpState);
+ if (aStart.Top() != aEnd.Top() || aStart.Bottom() != aEnd.Bottom())
+ {
+ aStart.Left(pFrame->getFrameArea().Left());
+ aEnd.Right(pFrame->getFrameArea().Right());
+ }
+ *pFieldRect = aStart.Union(aEnd);
+ }
+ }
+ }
+ }
+ }
+
+ if( !bRet && IsAttrAtPos::Redline & rContentAtPos.eContentAtPos )
+ {
+ const SwRangeRedline* pRedl = GetDoc()->getIDocumentRedlineAccess().GetRedline(aPos, nullptr);
+
+ if( pRedl )
+ {
+ rContentAtPos.aFnd.pRedl = pRedl;
+ rContentAtPos.eContentAtPos = IsAttrAtPos::Redline;
+ rContentAtPos.pFndTextAttr = nullptr;
+ bRet = true;
+
+ if (pFieldRect)
+ {
+ std::pair<Point, bool> tmp(aPt, true);
+ pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if( pFrame )
+ {
+ // not sure if this should be limited to one
+ // paragraph, or mark the entire redline; let's
+ // leave it limited to one for now...
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ pRedl->CalcStartEnd(pTextNd->GetIndex(), nStart, nEnd);
+ if (nStart == COMPLETE_STRING)
+ {
+ // consistency: found pRedl, so there must be
+ // something in pTextNd
+ assert(nEnd != COMPLETE_STRING);
+ nStart = 0;
+ }
+ if (nEnd == COMPLETE_STRING)
+ {
+ nEnd = pTextNd->Len();
+ }
+ //get bounding box of range
+ SwRect aStart;
+ pFrame->GetCharRect(aStart, SwPosition(*pTextNd, nStart), &aTmpState);
+ SwRect aEnd;
+ pFrame->GetCharRect(aEnd, SwPosition(*pTextNd, nEnd), &aTmpState);
+ if (aStart.Top() != aEnd.Top() || aStart.Bottom() != aEnd.Bottom())
+ {
+ aStart.Left(pFrame->getFrameArea().Left());
+ aEnd.Right(pFrame->getFrameArea().Right());
+ }
+ *pFieldRect = aStart.Union(aEnd);
+ }
+ }
+ }
+ }
+ }
+
+ if( !bRet && ( IsAttrAtPos::TableRedline & rContentAtPos.eContentAtPos ) )
+ {
+ const SwTableNode* pTableNd;
+ const SwTableBox* pBox;
+ const SwTableLine* pTableLine;
+ const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode();
+ if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) &&
+ nullptr != ( pBox = pTableNd->GetTable().GetTableBox(
+ pSttNd->GetIndex() )) &&
+ nullptr != ( pTableLine = pBox->GetUpper() ) &&
+ RedlineType::None != pTableLine->GetRedlineType() )
+ {
+ SwRedlineTable::size_type nPos = 0;
+ nPos = pTableLine->UpdateTextChangesOnly(nPos);
+ if ( nPos != SwRedlineTable::npos )
+ {
+ rContentAtPos.aFnd.pRedl = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable()[nPos];
+ rContentAtPos.eContentAtPos = IsAttrAtPos::TableRedline;
+ bRet = true;
+ }
+
+ }
+ }
+
+ if( !bRet
+ && ( IsAttrAtPos::TableBoxFml & rContentAtPos.eContentAtPos
+#ifdef DBG_UTIL
+ || IsAttrAtPos::TableBoxValue & rContentAtPos.eContentAtPos
+#endif
+ ) )
+ {
+ const SwTableNode* pTableNd;
+ const SwTableBox* pBox;
+ const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode();
+ const SwTableBoxFormula* pItem;
+#ifdef DBG_UTIL
+ const SwTableBoxValue* pItem2 = nullptr;
+#endif
+ if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) &&
+ nullptr != ( pBox = pTableNd->GetTable().GetTableBox(
+ pSttNd->GetIndex() )) &&
+#ifdef DBG_UTIL
+ ( (pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMULA, false )) ||
+ (pItem2 = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_VALUE, false )) )
+#else
+ (pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMULA, false ))
+#endif
+ )
+ {
+ std::pair<Point, bool> tmp(aPt, true);
+ SwFrame* pF = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if( pF )
+ {
+ // then the CellFrame
+ pFrame = static_cast<SwContentFrame*>(pF);
+ while( pF && !pF->IsCellFrame() )
+ pF = pF->GetUpper();
+ }
+
+ if( aTmpState.m_bPosCorr )
+ {
+ if( pF && !pF->getFrameArea().Contains( aPt ))
+ pF = nullptr;
+ }
+ else if( !pF )
+ pF = pFrame;
+
+ if( pF ) // only then it is valid
+ {
+ // create from internal (for CORE) the external
+ // (for UI) formula
+ rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxFml;
+#ifdef DBG_UTIL
+ if( pItem2 )
+ rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxValue;
+ else
+#endif
+ const_cast<SwTableBoxFormula&>(*pItem).PtrToBoxNm( &pTableNd->GetTable() );
+
+ bRet = true;
+ if( bSetCursor )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+ *m_pCurrentCursor->GetPoint() = aPos;
+ if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle) )
+ bRet = false;
+ else
+ UpdateCursor();
+ }
+
+ if( bRet )
+ {
+ if( pFieldRect )
+ {
+ *pFieldRect = pF->getFramePrintArea();
+ *pFieldRect += pF->getFrameArea().Pos();
+ }
+ rContentAtPos.pFndTextAttr = nullptr;
+ rContentAtPos.aFnd.pAttr = pItem;
+ }
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ if( !bRet && IsAttrAtPos::CurrAttrs & rContentAtPos.eContentAtPos )
+ {
+ const sal_Int32 n = aPos.nContent.GetIndex();
+ SfxItemSetFixed<POOLATTR_BEGIN, POOLATTR_END - 1> aSet( GetDoc()->GetAttrPool() );
+ if( pTextNd->GetpSwpHints() )
+ {
+ for( size_t i = 0; i < pTextNd->GetSwpHints().Count(); ++i )
+ {
+ const SwTextAttr* pHt = pTextNd->GetSwpHints().Get(i);
+ const sal_Int32 nAttrStart = pHt->GetStart();
+ if( nAttrStart > n ) // over the section
+ break;
+
+ if( nullptr != pHt->End() && (
+ ( nAttrStart < n &&
+ ( pHt->DontExpand() ? n < *pHt->End()
+ : n <= *pHt->End() )) ||
+ ( n == nAttrStart &&
+ ( nAttrStart == *pHt->End() || !n ))) )
+ {
+ aSet.Put( pHt->GetAttr() );
+ }
+ }
+ if( pTextNd->HasSwAttrSet() &&
+ pTextNd->GetpSwAttrSet()->Count() )
+ {
+ SfxItemSet aFormatSet( pTextNd->GetSwAttrSet() );
+ // remove all from format set that are also in TextSet
+ aFormatSet.Differentiate( aSet );
+ // now merge all together
+ aSet.Put( aFormatSet );
+ }
+ }
+ else
+ pTextNd->SwContentNode::GetAttr( aSet );
+
+ rContentAtPos.sStr = "Pos: (";
+ rContentAtPos.sStr += OUString::number( sal_Int32(aPos.nNode.GetIndex()));
+ rContentAtPos.sStr += ":";
+ rContentAtPos.sStr += OUString::number( aPos.nContent.GetIndex());
+ rContentAtPos.sStr += ")";
+ rContentAtPos.sStr += "\nParagraph Style: ";
+ rContentAtPos.sStr += pTextNd->GetFormatColl()->GetName();
+ if( pTextNd->GetCondFormatColl() )
+ {
+ rContentAtPos.sStr += "\nConditional Style: " + pTextNd->GetCondFormatColl()->GetName();
+ }
+
+ if( aSet.Count() )
+ {
+ OUStringBuffer sAttrs;
+ SfxItemIter aIter( aSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ const IntlWrapper aInt(SvtSysLocale().GetUILanguageTag());
+ do
+ {
+ if( !IsInvalidItem( pItem ))
+ {
+ OUString aStr;
+ GetDoc()->GetAttrPool().GetPresentation(*pItem,
+ MapUnit::MapCM, aStr, aInt);
+ if (!sAttrs.isEmpty())
+ sAttrs.append(", ");
+ sAttrs.append(aStr);
+ }
+ pItem = aIter.NextItem();
+ } while (pItem);
+ if (!sAttrs.isEmpty())
+ {
+ if( !rContentAtPos.sStr.isEmpty() )
+ rContentAtPos.sStr += "\n";
+ rContentAtPos.sStr += "Attr: " + sAttrs.makeStringAndClear();
+ }
+ }
+ bRet = true;
+ rContentAtPos.eContentAtPos = IsAttrAtPos::CurrAttrs;
+ }
+#endif
+ }
+ }
+
+ if( !bRet )
+ {
+ rContentAtPos.eContentAtPos = IsAttrAtPos::NONE;
+ rContentAtPos.aFnd.pField = nullptr;
+ }
+ return bRet;
+}
+
+// #i90516#
+const SwPostItField* SwCursorShell::GetPostItFieldAtCursor() const
+{
+ const SwPostItField* pPostItField = nullptr;
+
+ if ( !IsTableMode() )
+ {
+ const SwPosition* pCursorPos = GetCursor_()->GetPoint();
+ const SwTextNode* pTextNd = pCursorPos->nNode.GetNode().GetTextNode();
+ if ( pTextNd )
+ {
+ SwTextAttr* pTextAttr = pTextNd->GetFieldTextAttrAt( pCursorPos->nContent.GetIndex() );
+ const SwField* pField = pTextAttr != nullptr ? pTextAttr->GetFormatField().GetField() : nullptr;
+ if ( pField && pField->Which()== SwFieldIds::Postit )
+ {
+ pPostItField = static_cast<const SwPostItField*>(pField);
+ }
+ }
+ }
+
+ return pPostItField;
+}
+
+/// is the node in a protected section?
+bool SwContentAtPos::IsInProtectSect() const
+{
+ const SwTextNode* pNd = nullptr;
+ if( pFndTextAttr )
+ {
+ switch( eContentAtPos )
+ {
+ case IsAttrAtPos::Field:
+ case IsAttrAtPos::ClickField:
+ pNd = static_txtattr_cast<SwTextField const*>(pFndTextAttr)->GetpTextNode();
+ break;
+
+ case IsAttrAtPos::Ftn:
+ pNd = &static_cast<const SwTextFootnote*>(pFndTextAttr)->GetTextNode();
+ break;
+
+ case IsAttrAtPos::InetAttr:
+ pNd = static_txtattr_cast<SwTextINetFormat const*>(pFndTextAttr)->GetpTextNode();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( !pNd )
+ return false;
+ if( pNd->IsInProtectSect() )
+ return true;
+
+ const SwContentFrame* pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr);
+ return pFrame && pFrame->IsProtected() ;
+}
+
+bool SwContentAtPos::IsInRTLText()const
+{
+ bool bRet = false;
+ const SwTextNode* pNd = nullptr;
+ if (pFndTextAttr && (eContentAtPos == IsAttrAtPos::Ftn))
+ {
+ const SwTextFootnote* pTextFootnote = static_cast<const SwTextFootnote*>(pFndTextAttr);
+ if(pTextFootnote->GetStartNode())
+ {
+ SwStartNode* pSttNd = pTextFootnote->GetStartNode()->GetNode().GetStartNode();
+ SwPaM aTemp( *pSttNd );
+ aTemp.Move(fnMoveForward, GoInNode);
+ SwContentNode* pContentNode = aTemp.GetContentNode();
+ if(pContentNode && pContentNode->IsTextNode())
+ pNd = pContentNode->GetTextNode();
+ }
+ }
+ if(pNd)
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
+ SwTextFrame* pTmpFrame = aIter.First();
+ while( pTmpFrame )
+ {
+ if ( !pTmpFrame->IsFollow())
+ {
+ bRet = pTmpFrame->IsRightToLeft();
+ break;
+ }
+ pTmpFrame = aIter.Next();
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::SelectTextModel( const sal_Int32 nStart,
+ const sal_Int32 nEnd )
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ SwCallLink aLk( *this );
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ SwPosition& rPos = *m_pCurrentCursor->GetPoint();
+ m_pCurrentCursor->DeleteMark();
+ rPos.nContent = nStart;
+ m_pCurrentCursor->SetMark();
+ rPos.nContent = nEnd;
+
+ if( !m_pCurrentCursor->IsSelOvr() )
+ {
+ UpdateCursor();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+TextFrameIndex SwCursorShell::GetCursorPointAsViewIndex() const
+{
+ SwPosition const*const pPos(GetCursor()->GetPoint());
+ SwTextNode const*const pTextNode(pPos->nNode.GetNode().GetTextNode());
+ assert(pTextNode);
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTextNode->getLayoutFrame(GetLayout())));
+ assert(pFrame);
+ return pFrame->MapModelToViewPos(*pPos);
+}
+
+bool SwCursorShell::SelectTextView(TextFrameIndex const nStart,
+ TextFrameIndex const nEnd)
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ SwCallLink aLk( *this );
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ SwPosition& rPos = *m_pCurrentCursor->GetPoint();
+ m_pCurrentCursor->DeleteMark();
+ // indexes must correspond to cursor point!
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode()->getLayoutFrame(GetLayout())));
+ assert(pFrame);
+ rPos = pFrame->MapViewToModelPos(nStart);
+ m_pCurrentCursor->SetMark();
+ rPos = pFrame->MapViewToModelPos(nEnd);
+
+ if (!m_pCurrentCursor->IsSelOvr())
+ {
+ UpdateCursor();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich,
+ bool bExpand,
+ const SwTextAttr* pTextAttr )
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ if( !IsTableMode() )
+ {
+ if( !pTextAttr )
+ {
+ SwPosition& rPos = *m_pCurrentCursor->GetPoint();
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ pTextAttr = pTextNd
+ ? pTextNd->GetTextAttrAt(rPos.nContent.GetIndex(),
+ nWhich,
+ bExpand ? SwTextNode::EXPAND : SwTextNode::DEFAULT)
+ : nullptr;
+ }
+
+ if( pTextAttr )
+ {
+ const sal_Int32* pEnd = pTextAttr->End();
+ bRet = SelectTextModel(pTextAttr->GetStart(), (pEnd ? *pEnd : pTextAttr->GetStart() + 1));
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::GotoINetAttr( const SwTextINetFormat& rAttr )
+{
+ bool bRet = false;
+ if( rAttr.GetpTextNode() )
+ {
+ SwCursor* pCursor = getShellCursor( true );
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *pCursor );
+
+ pCursor->GetPoint()->nNode = *rAttr.GetpTextNode();
+ pCursor->GetPoint()->nContent.Assign( const_cast<SwTextNode*>(rAttr.GetpTextNode()),
+ rAttr.GetStart() );
+ bRet = !pCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ }
+ return bRet;
+}
+
+const SwFormatINetFormat* SwCursorShell::FindINetAttr( std::u16string_view rName ) const
+{
+ return mxDoc->FindINetAttr( rName );
+}
+
+bool SwCursorShell::GetShadowCursorPos( const Point& rPt, SwFillMode eFillMode,
+ SwRect& rRect, sal_Int16& rOrient )
+{
+
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ if (!IsTableMode() && !HasSelection()
+ && GetDoc()->GetIDocumentUndoRedo().DoesUndo())
+ {
+ Point aPt( rPt );
+ SwPosition aPos( *m_pCurrentCursor->GetPoint() );
+
+ SwFillCursorPos aFPos( eFillMode );
+ SwCursorMoveState aTmpState( &aFPos );
+
+ if( GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState ) &&
+ !aPos.nNode.GetNode().IsProtect())
+ {
+ // start position in protected section?
+ rRect = aFPos.aCursor;
+ rOrient = aFPos.eOrient;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::SetShadowCursorPos( const Point& rPt, SwFillMode eFillMode )
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ if (!IsTableMode() && !HasSelection()
+ && GetDoc()->GetIDocumentUndoRedo().DoesUndo())
+ {
+ Point aPt( rPt );
+ SwPosition aPos( *m_pCurrentCursor->GetPoint() );
+
+ SwFillCursorPos aFPos( eFillMode );
+ SwCursorMoveState aTmpState( &aFPos );
+
+ if( GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState ) )
+ {
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ StartAction();
+
+ SwContentNode* pCNd = aPos.nNode.GetNode().GetContentNode();
+ SwUndoId nUndoId = SwUndoId::INS_FROM_SHADOWCRSR;
+ // If only the paragraph attributes "Adjust" or "LRSpace" are set,
+ // then the following should not delete those again.
+ if( 0 == aFPos.nParaCnt + aFPos.nColumnCnt &&
+ ( SwFillMode::Indent == aFPos.eMode ||
+ ( text::HoriOrientation::NONE != aFPos.eOrient &&
+ 0 == aFPos.nTabCnt + aFPos.nSpaceCnt )) &&
+ pCNd && pCNd->Len() )
+ nUndoId = SwUndoId::EMPTY;
+
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( nUndoId, nullptr );
+
+ SwTextFormatColl* pNextFormat = nullptr;
+ SwTextNode* pTNd = pCNd ? pCNd->GetTextNode() : nullptr;
+ if( pTNd )
+ pNextFormat = &pTNd->GetTextColl()->GetNextTextFormatColl();
+
+ const SwSectionNode* pSectNd = pCNd ? pCNd->FindSectionNode() : nullptr;
+ if( pSectNd && aFPos.nParaCnt )
+ {
+ SwNodeIndex aEnd( aPos.nNode, 1 );
+ while( aEnd.GetNode().IsEndNode() &&
+ &aEnd.GetNode() !=
+ pSectNd->EndOfSectionNode() )
+ ++aEnd;
+
+ if( aEnd.GetNode().IsEndNode() &&
+ pCNd->Len() == aPos.nContent.GetIndex() )
+ aPos.nNode = *pSectNd->EndOfSectionNode();
+ }
+
+ for( sal_uInt16 n = 0; n < aFPos.nParaCnt + aFPos.nColumnCnt; ++n )
+ {
+ GetDoc()->getIDocumentContentOperations().AppendTextNode( aPos );
+ if( !n && pNextFormat )
+ {
+ *m_pCurrentCursor->GetPoint() = aPos;
+ GetDoc()->SetTextFormatColl( *m_pCurrentCursor, pNextFormat, false );
+ }
+ if( n < aFPos.nColumnCnt )
+ {
+ *m_pCurrentCursor->GetPoint() = aPos;
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem( *m_pCurrentCursor,
+ SvxFormatBreakItem( SvxBreak::ColumnBefore, RES_BREAK ) );
+ }
+ }
+
+ *m_pCurrentCursor->GetPoint() = aPos;
+ switch( aFPos.eMode )
+ {
+ case SwFillMode::Indent:
+ if( nullptr != (pCNd = aPos.nNode.GetNode().GetContentNode() ))
+ {
+ SfxItemSetFixed<
+ RES_PARATR_ADJUST, RES_PARATR_ADJUST,
+ RES_LR_SPACE, RES_LR_SPACE> aSet( GetDoc()->GetAttrPool() );
+ SvxLRSpaceItem aLR(pCNd->GetAttr(RES_LR_SPACE));
+ aLR.SetTextLeft( aFPos.nTabCnt );
+ aLR.SetTextFirstLineOffset( 0 );
+ aSet.Put( aLR );
+
+ const SvxAdjustItem& rAdj = pCNd->GetAttr(RES_PARATR_ADJUST);
+ if( SvxAdjust::Left != rAdj.GetAdjust() )
+ aSet.Put( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
+
+ GetDoc()->getIDocumentContentOperations().InsertItemSet( *m_pCurrentCursor, aSet );
+ }
+ else {
+ OSL_ENSURE( false, "No ContentNode" );
+ }
+ break;
+
+ case SwFillMode::Tab:
+ case SwFillMode::TabSpace:
+ case SwFillMode::Space:
+ {
+ OUStringBuffer sInsert;
+ if (aFPos.eMode == SwFillMode::Space)
+ {
+ comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceOnlyCnt, ' ');
+ }
+ else
+ {
+ if (aFPos.nTabCnt)
+ comphelper::string::padToLength(sInsert, aFPos.nTabCnt, '\t');
+ if (aFPos.nSpaceCnt)
+ comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceCnt, ' ');
+ }
+ if (!sInsert.isEmpty())
+ GetDoc()->getIDocumentContentOperations().InsertString( *m_pCurrentCursor, sInsert.makeStringAndClear());
+ }
+ [[fallthrough]]; // still need to set orientation
+ case SwFillMode::Margin:
+ if( text::HoriOrientation::NONE != aFPos.eOrient )
+ {
+ SvxAdjustItem aAdj( SvxAdjust::Left, RES_PARATR_ADJUST );
+ switch( aFPos.eOrient )
+ {
+ case text::HoriOrientation::CENTER:
+ aAdj.SetAdjust( SvxAdjust::Center );
+ break;
+ case text::HoriOrientation::RIGHT:
+ aAdj.SetAdjust( SvxAdjust::Right );
+ break;
+ default:
+ break;
+ }
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem( *m_pCurrentCursor, aAdj );
+ }
+ break;
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( nUndoId, nullptr );
+ EndAction();
+
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+const SwRangeRedline* SwCursorShell::SelNextRedline()
+{
+ const SwRangeRedline* pFnd = nullptr;
+ if( !IsTableMode() )
+ {
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ // ensure point is at the end so alternating SelNext/SelPrev works
+ NormalizePam(false);
+ pFnd = GetDoc()->getIDocumentRedlineAccess().SelNextRedline( *m_pCurrentCursor );
+
+ // at the end of the document, go to the start of the document, and try again
+ if ( !pFnd )
+ {
+ GetDoc()->GetDocShell()->GetWrtShell()->StartOfSection();
+ pFnd = GetDoc()->getIDocumentRedlineAccess().SelNextRedline( *m_pCurrentCursor );
+ }
+
+ if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
+ UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ else
+ pFnd = nullptr;
+ }
+ return pFnd;
+}
+
+const SwRangeRedline* SwCursorShell::SelPrevRedline()
+{
+ const SwRangeRedline* pFnd = nullptr;
+ if( !IsTableMode() )
+ {
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ // ensure point is at the start so alternating SelNext/SelPrev works
+ NormalizePam(true);
+ pFnd = GetDoc()->getIDocumentRedlineAccess().SelPrevRedline( *m_pCurrentCursor );
+
+ // at the start of the document, go to the end of the document, and try again
+ if ( !pFnd )
+ {
+ GetDoc()->GetDocShell()->GetWrtShell()->EndOfSection();
+ pFnd = GetDoc()->getIDocumentRedlineAccess().SelPrevRedline( *m_pCurrentCursor );
+ }
+
+ if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
+ UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+ else
+ pFnd = nullptr;
+ }
+ return pFnd;
+}
+
+const SwRangeRedline* SwCursorShell::GotoRedline_( SwRedlineTable::size_type nArrPos, bool bSelect )
+{
+ const SwRangeRedline* pFnd = nullptr;
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ pFnd = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable()[ nArrPos ];
+ if( pFnd )
+ {
+ *m_pCurrentCursor->GetPoint() = *pFnd->Start();
+
+ SwNodeIndex* pIdx = &m_pCurrentCursor->GetPoint()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection( pIdx,
+ true, IsReadOnlyAvailable() );
+ if( pCNd )
+ {
+ if( *pIdx <= pFnd->End()->nNode )
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd && bSelect )
+ {
+ m_pCurrentCursor->SetMark();
+ if( RedlineType::FmtColl == pFnd->GetType() )
+ {
+ SwContentNode* pCNd = pIdx->GetNode().GetContentNode();
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ m_pCurrentCursor->GetMark()->nContent.Assign( pCNd, 0 );
+ }
+ else
+ *m_pCurrentCursor->GetPoint() = *pFnd->End();
+
+ pIdx = &m_pCurrentCursor->GetPoint()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ SwContentNode* pCNd = SwNodes::GoPrevSection( pIdx,
+ true, IsReadOnlyAvailable() );
+ if( pCNd )
+ {
+ if( *pIdx >= m_pCurrentCursor->GetMark()->nNode )
+ m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd )
+ {
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->RestoreSavePos();
+ }
+ else if( bSelect && *m_pCurrentCursor->GetMark() == *m_pCurrentCursor->GetPoint() )
+ m_pCurrentCursor->DeleteMark();
+
+ if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE
+ | SwCursorShell::READONLY );
+ else
+ {
+ pFnd = nullptr;
+ if( bSelect )
+ m_pCurrentCursor->DeleteMark();
+ }
+ }
+ return pFnd;
+}
+
+const SwRangeRedline* SwCursorShell::GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect )
+{
+ const SwRangeRedline* pFnd = nullptr;
+ if( !IsTableMode() )
+ {
+ CurrShell aCurr( this );
+
+ const SwRedlineTable& rTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ const SwRangeRedline* pTmp = rTable[ nArrPos ];
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+ if( nSeqNo && bSelect )
+ {
+ bool bCheck = false;
+ int nLoopCnt = 2;
+ SwRedlineTable::size_type nArrSavPos = nArrPos;
+
+ do {
+ pTmp = GotoRedline_( nArrPos, true );
+
+ if( !pFnd )
+ pFnd = pTmp;
+
+ if( pTmp && bCheck )
+ {
+ // Check for overlaps. These can happen when FormatColl-
+ // Redlines were stretched over a whole paragraph
+ SwPaM* pCur = m_pCurrentCursor;
+ SwPaM* pNextPam = pCur->GetNext();
+ SwPosition* pCStt = pCur->Start(), *pCEnd = pCur->End();
+ while( pCur != pNextPam )
+ {
+ const SwPosition *pNStt = pNextPam->Start(),
+ *pNEnd = pNextPam->End();
+
+ bool bDel = true;
+ switch( ::ComparePosition( *pCStt, *pCEnd,
+ *pNStt, *pNEnd ))
+ {
+ case SwComparePosition::Inside: // Pos1 is completely in Pos2
+ if( !pCur->HasMark() )
+ {
+ pCur->SetMark();
+ *pCur->GetMark() = *pNStt;
+ }
+ else
+ *pCStt = *pNStt;
+ *pCEnd = *pNEnd;
+ break;
+
+ case SwComparePosition::Outside: // Pos2 is completely in Pos1
+ case SwComparePosition::Equal: // Pos1 has same size as Pos2
+ break;
+
+ case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at beginning
+ if( !pCur->HasMark() )
+ pCur->SetMark();
+ *pCEnd = *pNEnd;
+ break;
+ case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at end
+ if( !pCur->HasMark() )
+ {
+ pCur->SetMark();
+ *pCur->GetMark() = *pNStt;
+ }
+ else
+ *pCStt = *pNStt;
+ break;
+
+ default:
+ bDel = false;
+ }
+
+ if( bDel )
+ {
+ // not needed anymore
+ SwPaM* pPrevPam = pNextPam->GetPrev();
+ delete pNextPam;
+ pNextPam = pPrevPam;
+ }
+ pNextPam = pNextPam->GetNext();
+ }
+ }
+
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? rTable.FindNextOfSeqNo( nArrPos )
+ : rTable.FindPrevOfSeqNo( nArrPos );
+ if( SwRedlineTable::npos != nFndPos ||
+ ( 0 != ( --nLoopCnt ) && SwRedlineTable::npos != (
+ nFndPos = rTable.FindPrevOfSeqNo( nArrSavPos ))) )
+ {
+ if( pTmp )
+ {
+ // create new cursor
+ CreateCursor();
+ bCheck = true;
+ }
+ nArrPos = nFndPos;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while( nLoopCnt );
+ }
+ else
+ pFnd = GotoRedline_( nArrPos, bSelect );
+ }
+ return pFnd;
+}
+
+bool SwCursorShell::SelectNxtPrvHyperlink( bool bNext )
+{
+ SwNodes& rNds = GetDoc()->GetNodes();
+ const SwNode* pBodyEndNd = &rNds.GetEndOfContent();
+ const SwNode* pBodySttNd = pBodyEndNd->StartOfSectionNode();
+ SwNodeOffset nBodySttNdIdx = pBodySttNd->GetIndex();
+ Point aPt;
+
+ SetGetExpField aCmpPos( SwPosition( bNext ? *pBodyEndNd : *pBodySttNd ) );
+ SetGetExpField aCurPos( bNext ? *m_pCurrentCursor->End() : *m_pCurrentCursor->Start() );
+ if( aCurPos.GetNode() < nBodySttNdIdx )
+ {
+ const SwContentNode* pCNd = aCurPos.GetNodeFromContent()->GetContentNode();
+ std::pair<Point, bool> tmp(aPt, true);
+ if (pCNd)
+ {
+ SwContentFrame* pFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if( pFrame )
+ aCurPos.SetBodyPos( *pFrame );
+ }
+ }
+
+ // check first all the hyperlink fields
+ {
+ const SwTextNode* pTextNd;
+ const SwCharFormats* pFormats = GetDoc()->GetCharFormats();
+ for( SwCharFormats::size_type n = pFormats->size(); 1 < n; )
+ {
+ SwIterator<SwTextINetFormat,SwCharFormat> aIter(*(*pFormats)[--n]);
+
+ for( SwTextINetFormat* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
+ {
+ pTextNd = pFnd->GetpTextNode();
+ if( pTextNd && pTextNd->GetNodes().IsDocNodes() )
+ {
+ SwTextINetFormat& rAttr = *pFnd;
+ SwPosition aTmpPos( *pTextNd );
+ SetGetExpField aPos( aTmpPos.nNode, rAttr );
+ if (pTextNd->GetIndex() < nBodySttNdIdx)
+ {
+ std::pair<Point, bool> tmp(aPt, true);
+ SwContentFrame* pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ if (pFrame)
+ {
+ aPos.SetBodyPos( *pFrame );
+ }
+ }
+
+ if( bNext
+ ? ( aPos < aCmpPos && aCurPos < aPos )
+ : ( aCmpPos < aPos && aPos < aCurPos ))
+ {
+ OUString sText(pTextNd->GetExpandText(GetLayout(),
+ rAttr.GetStart(),
+ *rAttr.GetEnd() - rAttr.GetStart() ) );
+
+ sText = sText.replaceAll("\x0a", "");
+ sText = comphelper::string::strip(sText, ' ');
+
+ if( !sText.isEmpty() )
+ aCmpPos = aPos;
+ }
+ }
+ }
+ }
+ }
+
+ // then check all the Flys with a URL or image map
+ {
+ const SwFrameFormats* pFormats = GetDoc()->GetSpzFrameFormats();
+ for( SwFrameFormats::size_type n = 0, nEnd = pFormats->size(); n < nEnd; ++n )
+ {
+ SwFlyFrameFormat* pFormat = static_cast<SwFlyFrameFormat*>((*pFormats)[ n ]);
+ const SwFormatURL& rURLItem = pFormat->GetURL();
+ if( rURLItem.GetMap() || !rURLItem.GetURL().isEmpty() )
+ {
+ SwFlyFrame* pFly = pFormat->GetFrame( &aPt );
+ SwPosition aTmpPos( *pBodySttNd );
+ if( pFly &&
+ GetBodyTextNode( *GetDoc(), aTmpPos, *pFly->GetLower() ) )
+ {
+ SetGetExpField aPos( *pFormat, &aTmpPos );
+
+ if( bNext
+ ? ( aPos < aCmpPos && aCurPos < aPos )
+ : ( aCmpPos < aPos && aPos < aCurPos ))
+ aCmpPos = aPos;
+ }
+ }
+ }
+ }
+
+ // found any URL ?
+ bool bRet = false;
+ const SwTextINetFormat* pFndAttr = aCmpPos.GetINetFormat();
+ const SwFlyFrameFormat* pFndFormat = aCmpPos.GetFlyFormat();
+ if( pFndAttr || pFndFormat )
+ {
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this );
+
+ // found a text attribute ?
+ if( pFndAttr )
+ {
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ aCmpPos.GetPosOfContent( *m_pCurrentCursor->GetPoint() );
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SetMark();
+ m_pCurrentCursor->GetPoint()->nContent = *pFndAttr->End();
+
+ if( !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
+ {
+ UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|
+ SwCursorShell::READONLY );
+ bRet = true;
+ }
+ }
+ // found a draw object ?
+ else if( RES_DRAWFRMFMT == pFndFormat->Which() )
+ {
+ const SdrObject* pSObj = pFndFormat->FindSdrObject();
+ if (pSObj)
+ {
+ static_cast<SwFEShell*>(this)->SelectObj( pSObj->GetCurrentBoundRect().Center() );
+ MakeSelVisible();
+ bRet = true;
+ }
+ }
+ else // then is it a fly
+ {
+ SwFlyFrame* pFly = pFndFormat->GetFrame(&aPt);
+ if( pFly )
+ {
+ static_cast<SwFEShell*>(this)->SelectFlyFrame( *pFly );
+ MakeSelVisible();
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/crstrvl1.cxx b/sw/source/core/crsr/crstrvl1.cxx
new file mode 100644
index 000000000..565456676
--- /dev/null
+++ b/sw/source/core/crsr/crstrvl1.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <crsrsh.hxx>
+#include <viscrs.hxx>
+
+#include <com/sun/star/i18n/WordType.hpp>
+
+using namespace ::com::sun::star::i18n;
+
+bool SwCursorShell::IsStartWord( sal_Int16 nWordType ) const
+{
+ return m_pCurrentCursor->IsStartWordWT(nWordType, GetLayout());
+}
+bool SwCursorShell::IsEndWord( sal_Int16 nWordType ) const
+{
+ return m_pCurrentCursor->IsEndWordWT(nWordType, GetLayout());
+}
+
+bool SwCursorShell::IsInWord( sal_Int16 nWordType ) const
+{
+ return m_pCurrentCursor->IsInWordWT(nWordType, GetLayout());
+}
+
+bool SwCursorShell::IsStartSentence() const
+{
+ return m_pCurrentCursor->IsStartEndSentence(false, GetLayout());
+}
+bool SwCursorShell::IsEndSentence() const
+{
+ return m_pCurrentCursor->IsStartEndSentence(true, GetLayout());
+}
+
+bool SwCursorShell::GoStartWord()
+{
+ return CallCursorShellFN( &SwCursorShell::GoStartWordImpl );
+}
+bool SwCursorShell::GoEndWord()
+{
+ return CallCursorShellFN( &SwCursorShell::GoEndWordImpl );
+}
+
+bool SwCursorShell::GoNextWord()
+{
+ return CallCursorShellFN( &SwCursorShell::GoNextWordImpl );
+}
+bool SwCursorShell::GoPrevWord()
+{
+ return CallCursorShellFN( &SwCursorShell::GoPrevWordImpl );
+}
+
+bool SwCursorShell::GoNextSentence()
+{
+ return CallCursorShellFN( &SwCursorShell::GoNextSentenceImpl );
+}
+
+bool SwCursorShell::GoEndSentence()
+{
+ return CallCursorShellFN( &SwCursorShell::GoEndSentenceImpl );
+}
+
+bool SwCursorShell::GoStartSentence()
+{
+ return CallCursorShellFN( &SwCursorShell::GoStartSentenceImpl );
+}
+
+bool SwCursorShell::SelectWord( const Point* pPt )
+{
+ return m_pCurrentCursor->SelectWord( this, pPt );
+}
+
+void SwCursorShell::ExpandToSentenceBorders()
+{
+ m_pCurrentCursor->ExpandToSentenceBorders(GetLayout());
+}
+
+bool SwCursorShell::GoStartWordImpl()
+{
+ return getShellCursor(true)->GoStartWordWT(WordType::ANYWORD_IGNOREWHITESPACES, GetLayout());
+}
+
+bool SwCursorShell::GoEndWordImpl()
+{
+ return getShellCursor(true)->GoEndWordWT(WordType::ANYWORD_IGNOREWHITESPACES, GetLayout());
+}
+
+bool SwCursorShell::GoNextWordImpl()
+{
+ return getShellCursor(true)->GoNextWordWT(WordType::ANYWORD_IGNOREWHITESPACES, GetLayout());
+}
+
+bool SwCursorShell::GoPrevWordImpl()
+{
+ return getShellCursor(true)->GoPrevWordWT(WordType::ANYWORD_IGNOREWHITESPACES, GetLayout());
+}
+
+bool SwCursorShell::GoNextSentenceImpl()
+{
+ return getShellCursor(true)->GoSentence(SwCursor::NEXT_SENT, GetLayout());
+}
+bool SwCursorShell::GoEndSentenceImpl()
+{
+ return getShellCursor(true)->GoSentence(SwCursor::END_SENT, GetLayout());
+}
+bool SwCursorShell::GoStartSentenceImpl()
+{
+ return getShellCursor(true)->GoSentence(SwCursor::START_SENT, GetLayout());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/datecontentcontrolbutton.cxx b/sw/source/core/crsr/datecontentcontrolbutton.cxx
new file mode 100644
index 000000000..9de971b6f
--- /dev/null
+++ b/sw/source/core/crsr/datecontentcontrolbutton.cxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <datecontentcontrolbutton.hxx>
+
+#include <svl/numformat.hxx>
+#include <tools/date.hxx>
+#include <vcl/svapp.hxx>
+
+#include <edtwin.hxx>
+#include <formatcontentcontrol.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+
+IMPL_LINK(SwDateContentControlButton, SelectHandler, weld::Calendar&, rCalendar, void)
+{
+ const Date& rNullDate = m_pNumberFormatter->GetNullDate();
+ double fDate = rCalendar.get_date() - rNullDate;
+ m_xPopup->popdown();
+ m_pContentControl->SetSelectedDate(fDate);
+ SwView& rView = static_cast<SwEditWin*>(GetParent())->GetView();
+ SwWrtShell& rWrtShell = rView.GetWrtShell();
+ rWrtShell.GotoContentControl(*m_pContentControl->GetFormatContentControl());
+}
+
+SwDateContentControlButton::SwDateContentControlButton(
+ SwEditWin* pEditWin, const std::shared_ptr<SwContentControl>& pContentControl,
+ SvNumberFormatter* pNumberFormatter)
+ : SwContentControlButton(pEditWin, pContentControl)
+ , m_pNumberFormatter(pNumberFormatter)
+{
+}
+
+SwDateContentControlButton::~SwDateContentControlButton() { disposeOnce(); }
+
+void SwDateContentControlButton::LaunchPopup()
+{
+ m_xPopupBuilder = Application::CreateBuilder(GetFrameWeld(),
+ "modules/swriter/ui/contentcontrolcalendar.ui");
+ m_xPopup = m_xPopupBuilder->weld_popover("Calendar");
+ m_xCalendar = m_xPopupBuilder->weld_calendar("date");
+
+ // Read the doc model.
+ if (m_pContentControl)
+ {
+ const Date& rNullDate = m_pNumberFormatter->GetNullDate();
+ double fCurrentDate = m_pContentControl->GetCurrentDateValue();
+ if (fCurrentDate != 0)
+ {
+ m_xCalendar->set_date(rNullDate + sal_Int32(fCurrentDate));
+ }
+ }
+
+ m_xCalendar->connect_activated(LINK(this, SwDateContentControlButton, SelectHandler));
+ SwContentControlButton::LaunchPopup();
+ m_xCalendar->grab_focus();
+}
+
+void SwDateContentControlButton::DestroyPopup()
+{
+ m_xCalendar.reset();
+ SwContentControlButton::DestroyPopup();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/dropdowncontentcontrolbutton.cxx b/sw/source/core/crsr/dropdowncontentcontrolbutton.cxx
new file mode 100644
index 000000000..2fa456e41
--- /dev/null
+++ b/sw/source/core/crsr/dropdowncontentcontrolbutton.cxx
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <dropdowncontentcontrolbutton.hxx>
+
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <docsh.hxx>
+#include <strings.hrc>
+#include <formatcontentcontrol.hxx>
+#include <wrtsh.hxx>
+
+void SwDropDownContentControlButton::InitDropdown()
+{
+ std::vector<SwContentControlListItem> aListItems = m_pContentControl->GetListItems();
+
+ for (const auto& rListItem : aListItems)
+ {
+ m_xTreeView->append_text(rListItem.ToString());
+ }
+
+ if (aListItems.empty())
+ {
+ m_xTreeView->append_text(SwResId(STR_DROP_DOWN_EMPTY_LIST));
+ }
+
+ int nHeight = m_xTreeView->get_height_rows(
+ std::min<int>(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount(),
+ m_xTreeView->n_children()));
+ m_xTreeView->set_size_request(-1, nHeight);
+ Size aSize(m_xTreeView->get_preferred_size());
+ aSize.AdjustWidth(4);
+ aSize.AdjustHeight(4);
+ tools::Long nMinListWidth = GetSizePixel().Width();
+ aSize.setWidth(std::max(aSize.Width(), nMinListWidth));
+ m_xTreeView->set_size_request(aSize.Width(), aSize.Height());
+}
+
+IMPL_LINK(SwDropDownContentControlButton, ListBoxHandler, weld::TreeView&, rBox, bool)
+{
+ OUString sSelection = rBox.get_selected_text();
+ if (sSelection == SwResId(STR_DROP_DOWN_EMPTY_LIST))
+ {
+ m_xPopup->popdown();
+ return true;
+ }
+
+ sal_Int32 nSelection = rBox.get_selected_index();
+ m_xPopup->popdown();
+ if (nSelection >= 0)
+ {
+ SwView& rView = static_cast<SwEditWin*>(GetParent())->GetView();
+ SwWrtShell& rWrtShell = rView.GetWrtShell();
+ m_pContentControl->SetSelectedListItem(nSelection);
+ rWrtShell.GotoContentControl(*m_pContentControl->GetFormatContentControl());
+ }
+
+ return true;
+}
+
+SwDropDownContentControlButton::SwDropDownContentControlButton(
+ SwEditWin* pEditWin, const std::shared_ptr<SwContentControl>& pContentControl)
+ : SwContentControlButton(pEditWin, pContentControl)
+{
+}
+
+SwDropDownContentControlButton::~SwDropDownContentControlButton() { disposeOnce(); }
+
+void SwDropDownContentControlButton::LaunchPopup()
+{
+ m_xPopupBuilder = Application::CreateBuilder(GetFrameWeld(),
+ "modules/swriter/ui/contentcontroldropdown.ui");
+ m_xPopup = m_xPopupBuilder->weld_popover("ContentControlDropDown");
+ m_xTreeView = m_xPopupBuilder->weld_tree_view("list");
+ InitDropdown();
+ m_xTreeView->connect_row_activated(LINK(this, SwDropDownContentControlButton, ListBoxHandler));
+ SwContentControlButton::LaunchPopup();
+ m_xTreeView->grab_focus();
+}
+
+void SwDropDownContentControlButton::DestroyPopup()
+{
+ m_xTreeView.reset();
+ SwContentControlButton::DestroyPopup();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/findattr.cxx b/sw/source/core/crsr/findattr.cxx
new file mode 100644
index 000000000..51728f49b
--- /dev/null
+++ b/sw/source/core/crsr/findattr.cxx
@@ -0,0 +1,1445 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/searchopt.hxx>
+#include <osl/diagnose.h>
+#include <unotools/syslocale.hxx>
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/srchitem.hxx>
+#include <svl/whiter.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <fmtpdsc.hxx>
+#include <txatbase.hxx>
+#include <charfmt.hxx>
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <swcrsr.hxx>
+#include <ndtxt.hxx>
+#include <pamtyp.hxx>
+#include <txtfrm.hxx>
+#include <swundo.hxx>
+#include <optional>
+
+#include <algorithm>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+// Special case for SvxFontItem: only compare the name
+static bool CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 )
+{
+ switch( rItem1.Which() )
+ {
+ case RES_CHRATR_FONT:
+ return rItem1.StaticWhichCast(RES_CHRATR_FONT).GetFamilyName() == rItem2.StaticWhichCast(RES_CHRATR_FONT).GetFamilyName();
+
+ case RES_CHRATR_COLOR:
+ return rItem1.StaticWhichCast(RES_CHRATR_COLOR).GetValue().IsRGBEqual(rItem2.StaticWhichCast(RES_CHRATR_COLOR).GetValue());
+ case RES_PAGEDESC:
+ ::std::optional<sal_uInt16> const oNumOffset1 = rItem1.StaticWhichCast(RES_PAGEDESC).GetNumOffset();
+ ::std::optional<sal_uInt16> const oNumOffset2 = rItem2.StaticWhichCast(RES_PAGEDESC).GetNumOffset();
+
+ if (oNumOffset1 != oNumOffset2)
+ return false;
+
+ return rItem1.StaticWhichCast(RES_PAGEDESC).GetPageDesc() == rItem2.StaticWhichCast(RES_PAGEDESC).GetPageDesc();
+ }
+ return rItem1 == rItem2;
+}
+
+const SwTextAttr* GetFrwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
+ sal_Int32 nContentPos )
+{
+ while( rPos < rHtsArr.Count() )
+ {
+ const SwTextAttr *pTextHt = rHtsArr.Get( rPos++ );
+ // the start of an attribute has to be in the section
+ if( pTextHt->GetStart() >= nContentPos )
+ return pTextHt; // valid text attribute
+ }
+ return nullptr; // invalid text attribute
+}
+
+const SwTextAttr* GetBkwrdTextHint( const SwpHints& rHtsArr, size_t& rPos,
+ sal_Int32 nContentPos )
+{
+ while( rPos > 0 )
+ {
+ const SwTextAttr *pTextHt = rHtsArr.Get( --rPos );
+ // the start of an attribute has to be in the section
+ if( pTextHt->GetStart() < nContentPos )
+ return pTextHt; // valid text attribute
+ }
+ return nullptr; // invalid text attribute
+}
+
+static void lcl_SetAttrPam( SwPaM& rPam, sal_Int32 nStart, const sal_Int32* pEnd,
+ const bool bSaveMark )
+{
+ sal_Int32 nContentPos;
+ if( bSaveMark )
+ nContentPos = rPam.GetMark()->nContent.GetIndex();
+ else
+ nContentPos = rPam.GetPoint()->nContent.GetIndex();
+ bool bTstEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode;
+
+ SwContentNode* pCNd = rPam.GetContentNode();
+ rPam.GetPoint()->nContent.Assign( pCNd, nStart );
+ rPam.SetMark(); // Point == GetMark
+
+ // Point points to end of search area or end of attribute
+ if( pEnd )
+ {
+ if( bTstEnd && *pEnd > nContentPos )
+ rPam.GetPoint()->nContent = nContentPos;
+ else
+ rPam.GetPoint()->nContent = *pEnd;
+ }
+}
+
+// TODO: provide documentation
+/** search for a text attribute
+
+ This function searches in a text node for a given attribute.
+ If that is found then the SwPaM contains the section that surrounds the
+ attribute (w.r.t. the search area).
+
+ @param rTextNd Text node to search in.
+ @param rPam ???
+ @param rCmpItem ???
+ @param fnMove ???
+ @return Returns <true> if found, <false> otherwise.
+*/
+static bool lcl_SearchAttr( const SwTextNode& rTextNd, SwPaM& rPam,
+ const SfxPoolItem& rCmpItem,
+ SwMoveFnCollection const & fnMove)
+{
+ if ( !rTextNd.HasHints() )
+ return false;
+
+ const SwTextAttr *pTextHt = nullptr;
+ bool bForward = &fnMove == &fnMoveForward;
+ size_t nPos = bForward ? 0 : rTextNd.GetSwpHints().Count();
+ sal_Int32 nContentPos = rPam.GetPoint()->nContent.GetIndex();
+
+ while( nullptr != ( pTextHt=(*fnMove.fnGetHint)(rTextNd.GetSwpHints(),nPos,nContentPos)))
+ if (pTextHt->Which() == rCmpItem.Which())
+ {
+ lcl_SetAttrPam( rPam, pTextHt->GetStart(), pTextHt->End(), bForward );
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+/// search for multiple text attributes
+struct SwSrchChrAttr
+{
+ sal_uInt16 nWhich;
+ sal_Int32 nStt;
+ sal_Int32 nEnd;
+
+ SwSrchChrAttr(): nWhich(0), nStt(0), nEnd(0) {}
+
+ SwSrchChrAttr( const SfxPoolItem& rItem,
+ sal_Int32 nStart, sal_Int32 nAnyEnd )
+ : nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd )
+ {}
+};
+
+class SwAttrCheckArr
+{
+ SwSrchChrAttr *m_pFindArr, *m_pStackArr;
+ sal_Int32 m_nNodeStart;
+ sal_Int32 m_nNodeEnd;
+ sal_uInt16 m_nArrStart, m_nArrLen;
+ sal_uInt16 m_nFound, m_nStackCount;
+ SfxItemSet m_aComapeSet;
+ bool m_bNoColls;
+ bool m_bForward;
+
+public:
+ SwAttrCheckArr( const SfxItemSet& rSet, bool bForward, bool bNoCollections );
+ ~SwAttrCheckArr();
+
+ void SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam );
+
+ /// how many attributes are there in total?
+ sal_uInt16 Count() const { return m_aComapeSet.Count(); }
+ bool Found() const { return m_nFound == m_aComapeSet.Count(); }
+ bool CheckStack();
+
+ sal_Int32 Start() const;
+ sal_Int32 End() const;
+
+ sal_Int32 GetNdStt() const { return m_nNodeStart; }
+ sal_Int32 GetNdEnd() const { return m_nNodeEnd; }
+
+ bool SetAttrFwd( const SwTextAttr& rAttr );
+ bool SetAttrBwd( const SwTextAttr& rAttr );
+};
+
+}
+
+SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, bool bFwd,
+ bool bNoCollections )
+ : m_nNodeStart(0)
+ , m_nNodeEnd(0)
+ , m_nFound(0)
+ , m_nStackCount(0)
+ , m_aComapeSet( *rSet.GetPool(), svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END-1> )
+ , m_bNoColls(bNoCollections)
+ , m_bForward(bFwd)
+{
+ m_aComapeSet.Put( rSet, false );
+
+ // determine area of Fnd/Stack array (Min/Max)
+ SfxItemIter aIter( m_aComapeSet );
+ m_nArrStart = m_aComapeSet.GetWhichByPos( aIter.GetFirstPos() );
+ m_nArrLen = m_aComapeSet.GetWhichByPos( aIter.GetLastPos() ) - m_nArrStart+1;
+
+ char* pFndChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
+ char* pStackChar = new char[ m_nArrLen * sizeof(SwSrchChrAttr) ];
+
+ m_pFindArr = reinterpret_cast<SwSrchChrAttr*>(pFndChar);
+ m_pStackArr = reinterpret_cast<SwSrchChrAttr*>(pStackChar);
+}
+
+SwAttrCheckArr::~SwAttrCheckArr()
+{
+ delete[] reinterpret_cast<char*>(m_pFindArr);
+ delete[] reinterpret_cast<char*>(m_pStackArr);
+}
+
+void SwAttrCheckArr::SetNewSet( const SwTextNode& rTextNd, const SwPaM& rPam )
+{
+ std::fill(m_pFindArr, m_pFindArr + m_nArrLen, SwSrchChrAttr());
+ std::fill(m_pStackArr, m_pStackArr + m_nArrLen, SwSrchChrAttr());
+ m_nFound = 0;
+ m_nStackCount = 0;
+
+ if( m_bForward )
+ {
+ m_nNodeStart = rPam.GetPoint()->nContent.GetIndex();
+ m_nNodeEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
+ ? rPam.GetMark()->nContent.GetIndex()
+ : rTextNd.GetText().getLength();
+ }
+ else
+ {
+ m_nNodeEnd = rPam.GetPoint()->nContent.GetIndex();
+ m_nNodeStart = rPam.GetPoint()->nNode == rPam.GetMark()->nNode
+ ? rPam.GetMark()->nContent.GetIndex()
+ : 0;
+ }
+
+ if( m_bNoColls && !rTextNd.HasSwAttrSet() )
+ return ;
+
+ const SfxItemSet& rSet = rTextNd.GetSwAttrSet();
+
+ SfxItemIter aIter( m_aComapeSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ const SfxPoolItem* pFndItem;
+ sal_uInt16 nWhich;
+
+ do
+ {
+ if( IsInvalidItem( pItem ) )
+ {
+ nWhich = m_aComapeSet.GetWhichByPos( aIter.GetCurPos() );
+ if( RES_TXTATR_END <= nWhich )
+ break; // end of text attributes
+
+ if( SfxItemState::SET == rSet.GetItemState( nWhich, !m_bNoColls, &pFndItem )
+ && !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) ))
+ {
+ m_pFindArr[ nWhich - m_nArrStart ] =
+ SwSrchChrAttr( *pFndItem, m_nNodeStart, m_nNodeEnd );
+ m_nFound++;
+ }
+ }
+ else
+ {
+ nWhich = pItem->Which();
+ if( RES_TXTATR_END <= nWhich )
+ break; // end of text attributes
+
+ if( CmpAttr( rSet.Get( nWhich, !m_bNoColls ), *pItem ) )
+ {
+ m_pFindArr[ nWhich - m_nArrStart ] =
+ SwSrchChrAttr( *pItem, m_nNodeStart, m_nNodeEnd );
+ m_nFound++;
+ }
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+}
+
+static bool
+lcl_IsAttributeIgnorable(sal_Int32 const nNdStart, sal_Int32 const nNdEnd,
+ SwSrchChrAttr const& rTmp)
+{
+ // #i115528#: if there is a paragraph attribute, it has been added by the
+ // SwAttrCheckArr ctor, and nFound is 1.
+ // if the paragraph is entirely covered by hints that override the paragraph
+ // attribute, then this function must find an attribute to decrement nFound!
+ // so check for an empty search range, let attributes that start/end there
+ // cover it, and hope for the best...
+ return ((nNdEnd == nNdStart)
+ ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt))
+ : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt)));
+}
+
+bool SwAttrCheckArr::SetAttrFwd( const SwTextAttr& rAttr )
+{
+ SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
+
+ // ignore all attributes not in search range
+ if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
+ {
+ return Found();
+ }
+
+ const SfxPoolItem* pItem;
+ // here we explicitly also search in character templates
+ sal_uInt16 nWhch = rAttr.Which();
+ std::optional<SfxWhichIter> oIter;
+ const SfxPoolItem* pTmpItem = nullptr;
+ const SfxItemSet* pSet = nullptr;
+ if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
+ {
+ if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
+ return Found();
+ pTmpItem = nullptr;
+ pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
+ if ( pSet )
+ {
+ oIter.emplace( *pSet );
+ nWhch = oIter->FirstWhich();
+ while( nWhch &&
+ SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
+ nWhch = oIter->NextWhich();
+ if( !nWhch )
+ pTmpItem = nullptr;
+ }
+ }
+ else
+ pTmpItem = &rAttr.GetAttr();
+
+ while( pTmpItem )
+ {
+ SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
+ if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
+ {
+ sal_uInt16 n;
+ SwSrchChrAttr* pCmp;
+
+ // first delete all up to start position that are already invalid
+ SwSrchChrAttr* pArrPtr;
+ if( m_nFound )
+ for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen;
+ ++n, ++pArrPtr )
+ if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt )
+ {
+ pArrPtr->nWhich = 0; // deleted
+ m_nFound--;
+ }
+
+ // delete all up to start position that are already invalid and
+ // move all "open" ones (= stick out over start position) from stack
+ // into FndSet
+ if( m_nStackCount )
+ for( pArrPtr = m_pStackArr, n=0; n < m_nArrLen; ++n, ++pArrPtr )
+ {
+ if( !pArrPtr->nWhich )
+ continue;
+
+ if( pArrPtr->nEnd <= aTmp.nStt )
+ {
+ pArrPtr->nWhich = 0; // deleted
+ if( !--m_nStackCount )
+ break;
+ }
+ else if( pArrPtr->nStt <= aTmp.nStt )
+ {
+ pCmp = &m_pFindArr[ n ];
+ if( pCmp->nWhich )
+ {
+ if( pCmp->nEnd < pArrPtr->nEnd ) // extend
+ pCmp->nEnd = pArrPtr->nEnd;
+ }
+ else
+ {
+ *pCmp = *pArrPtr;
+ m_nFound++;
+ }
+ pArrPtr->nWhich = 0;
+ if( !--m_nStackCount )
+ break;
+ }
+ }
+
+ bool bContinue = false;
+
+ if( SfxItemState::DONTCARE == eState )
+ {
+ // Will the attribute become valid?
+ if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
+ *pTmpItem ))
+ {
+ // search attribute and extend if needed
+ pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
+ if( !pCmp->nWhich )
+ {
+ *pCmp = aTmp; // not found, insert
+ m_nFound++;
+ }
+ else if( pCmp->nEnd < aTmp.nEnd ) // extend?
+ pCmp->nEnd = aTmp.nEnd;
+
+ bContinue = true;
+ }
+ }
+ // Will the attribute become valid?
+ else if( CmpAttr( *pItem, *pTmpItem ) )
+ {
+ m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
+ ++m_nFound;
+ bContinue = true;
+ }
+
+ // then is has to go on the stack
+ if( !bContinue )
+ {
+ pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
+ if (pCmp->nWhich )
+ {
+ // exists on stack, only if it is even bigger
+ if( pCmp->nEnd > aTmp.nEnd )
+ {
+ OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
+ "slot on stack is still in use" );
+
+ if( aTmp.nStt <= pCmp->nStt )
+ pCmp->nStt = aTmp.nEnd;
+ else
+ pCmp->nEnd = aTmp.nStt;
+
+ m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
+ m_nStackCount++;
+ }
+ pCmp->nWhich = 0;
+ m_nFound--;
+ }
+ }
+ }
+ if( oIter )
+ {
+ assert(pSet && "otherwise no oIter");
+ nWhch = oIter->NextWhich();
+ while( nWhch &&
+ SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
+ nWhch = oIter->NextWhich();
+ if( !nWhch )
+ break;
+ }
+ else
+ break;
+ }
+ oIter.reset();
+ return Found();
+}
+
+bool SwAttrCheckArr::SetAttrBwd( const SwTextAttr& rAttr )
+{
+ SwSrchChrAttr aTmp( rAttr.GetAttr(), rAttr.GetStart(), rAttr.GetAnyEnd() );
+
+ // ignore all attributes not in search range
+ if (lcl_IsAttributeIgnorable(m_nNodeStart, m_nNodeEnd, aTmp))
+ {
+ return Found();
+ }
+
+ const SfxPoolItem* pItem;
+ // here we explicitly also search in character templates
+ sal_uInt16 nWhch = rAttr.Which();
+ std::optional<SfxWhichIter> oIter;
+ const SfxPoolItem* pTmpItem = nullptr;
+ const SfxItemSet* pSet = nullptr;
+ if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch )
+ {
+ if( m_bNoColls && RES_TXTATR_CHARFMT == nWhch )
+ return Found();
+
+ pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
+ if ( pSet )
+ {
+ oIter.emplace( *pSet );
+ nWhch = oIter->FirstWhich();
+ while( nWhch &&
+ SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
+ nWhch = oIter->NextWhich();
+ if( !nWhch )
+ pTmpItem = nullptr;
+ }
+ }
+ else
+ pTmpItem = &rAttr.GetAttr();
+
+ while( pTmpItem )
+ {
+ SfxItemState eState = m_aComapeSet.GetItemState( nWhch, false, &pItem );
+ if( SfxItemState::DONTCARE == eState || SfxItemState::SET == eState )
+ {
+ sal_uInt16 n;
+ SwSrchChrAttr* pCmp;
+
+ // first delete all up to start position that are already invalid
+ SwSrchChrAttr* pArrPtr;
+ if( m_nFound )
+ for( pArrPtr = m_pFindArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
+ if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd )
+ {
+ pArrPtr->nWhich = 0; // deleted
+ m_nFound--;
+ }
+
+ // delete all up to start position that are already invalid and
+ // move all "open" ones (= stick out over start position) from stack
+ // into FndSet
+ if( m_nStackCount )
+ for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
+ {
+ if( !pArrPtr->nWhich )
+ continue;
+
+ if( pArrPtr->nStt >= aTmp.nEnd )
+ {
+ pArrPtr->nWhich = 0; // deleted
+ if( !--m_nStackCount )
+ break;
+ }
+ else if( pArrPtr->nEnd >= aTmp.nEnd )
+ {
+ pCmp = &m_pFindArr[ n ];
+ if( pCmp->nWhich )
+ {
+ if( pCmp->nStt > pArrPtr->nStt ) // extend
+ pCmp->nStt = pArrPtr->nStt;
+ }
+ else
+ {
+ *pCmp = *pArrPtr;
+ m_nFound++;
+ }
+ pArrPtr->nWhich = 0;
+ if( !--m_nStackCount )
+ break;
+ }
+ }
+
+ bool bContinue = false;
+ if( SfxItemState::DONTCARE == eState )
+ {
+ // Will the attribute become valid?
+ if( !CmpAttr( m_aComapeSet.GetPool()->GetDefaultItem( nWhch ),
+ *pTmpItem ) )
+ {
+ // search attribute and extend if needed
+ pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
+ if( !pCmp->nWhich )
+ {
+ *pCmp = aTmp; // not found, insert
+ m_nFound++;
+ }
+ else if( pCmp->nStt > aTmp.nStt ) // extend?
+ pCmp->nStt = aTmp.nStt;
+
+ bContinue = true;
+ }
+ }
+ // Will the attribute become valid?
+ else if( CmpAttr( *pItem, *pTmpItem ))
+ {
+ m_pFindArr[ nWhch - m_nArrStart ] = aTmp;
+ ++m_nFound;
+ bContinue = true;
+ }
+
+ // then is has to go on the stack
+ if( !bContinue )
+ {
+ pCmp = &m_pFindArr[ nWhch - m_nArrStart ];
+ if( pCmp->nWhich )
+ {
+ // exists on stack, only if it is even bigger
+ if( pCmp->nStt < aTmp.nStt )
+ {
+ OSL_ENSURE( !m_pStackArr[ nWhch - m_nArrStart ].nWhich,
+ "slot on stack is still in use" );
+
+ if( aTmp.nEnd <= pCmp->nEnd )
+ pCmp->nEnd = aTmp.nStt;
+ else
+ pCmp->nStt = aTmp.nEnd;
+
+ m_pStackArr[ nWhch - m_nArrStart ] = *pCmp;
+ m_nStackCount++;
+ }
+ pCmp->nWhich = 0;
+ m_nFound--;
+ }
+ }
+ }
+ if( oIter )
+ {
+ assert(pSet && "otherwise no oIter");
+ nWhch = oIter->NextWhich();
+ while( nWhch &&
+ SfxItemState::SET != oIter->GetItemState( true, &pTmpItem ) )
+ nWhch = oIter->NextWhich();
+ if( !nWhch )
+ break;
+ }
+ else
+ break;
+ }
+ oIter.reset();
+ return Found();
+}
+
+sal_Int32 SwAttrCheckArr::Start() const
+{
+ sal_Int32 nStart = m_nNodeStart;
+ SwSrchChrAttr* pArrPtr = m_pFindArr;
+ for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
+ if( pArrPtr->nWhich && pArrPtr->nStt > nStart )
+ nStart = pArrPtr->nStt;
+
+ return nStart;
+}
+
+sal_Int32 SwAttrCheckArr::End() const
+{
+ SwSrchChrAttr* pArrPtr = m_pFindArr;
+ sal_Int32 nEnd = m_nNodeEnd;
+ for( sal_uInt16 n = 0; n < m_nArrLen; ++n, ++pArrPtr )
+ if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd )
+ nEnd = pArrPtr->nEnd;
+
+ return nEnd;
+}
+
+bool SwAttrCheckArr::CheckStack()
+{
+ if( !m_nStackCount )
+ return false;
+
+ sal_uInt16 n;
+ const sal_Int32 nSttPos = Start();
+ const sal_Int32 nEndPos = End();
+ SwSrchChrAttr* pArrPtr;
+ for( pArrPtr = m_pStackArr, n = 0; n < m_nArrLen; ++n, ++pArrPtr )
+ {
+ if( !pArrPtr->nWhich )
+ continue;
+
+ if( m_bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos )
+ {
+ pArrPtr->nWhich = 0; // deleted
+ if( !--m_nStackCount )
+ return m_nFound == m_aComapeSet.Count();
+ }
+ else if( m_bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos )
+ {
+ // move all "open" ones (= stick out over start position) into FndSet
+ OSL_ENSURE( !m_pFindArr[ n ].nWhich, "slot in array is already in use" );
+ m_pFindArr[ n ] = *pArrPtr;
+ pArrPtr->nWhich = 0;
+ m_nFound++;
+ if( !--m_nStackCount )
+ return m_nFound == m_aComapeSet.Count();
+ }
+ }
+ return m_nFound == m_aComapeSet.Count();
+}
+
+static bool lcl_SearchForward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
+ SwPaM& rPam )
+{
+ sal_Int32 nEndPos;
+ rCmpArr.SetNewSet( rTextNd, rPam );
+ if( !rTextNd.HasHints() )
+ {
+ if( !rCmpArr.Found() )
+ return false;
+ nEndPos = rCmpArr.GetNdEnd();
+ lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
+ return true;
+ }
+
+ const SwpHints& rHtArr = rTextNd.GetSwpHints();
+ const SwTextAttr* pAttr;
+ size_t nPos = 0;
+
+ // if everything is already there then check with which it will be ended
+ if( rCmpArr.Found() )
+ {
+ for( ; nPos < rHtArr.Count(); ++nPos )
+ {
+ pAttr = rHtArr.Get( nPos );
+ if( !rCmpArr.SetAttrFwd( *pAttr ) )
+ {
+ if( rCmpArr.GetNdStt() < pAttr->GetStart() )
+ {
+ // found end
+ auto nTmpStart = pAttr->GetStart();
+ lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(),
+ &nTmpStart, true );
+ return true;
+ }
+ // continue search
+ break;
+ }
+ }
+
+ if( nPos == rHtArr.Count() && rCmpArr.Found() )
+ {
+ // found
+ nEndPos = rCmpArr.GetNdEnd();
+ lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, true );
+ return true;
+ }
+ }
+
+ sal_Int32 nSttPos;
+ for( ; nPos < rHtArr.Count(); ++nPos )
+ {
+ pAttr = rHtArr.Get( nPos );
+ if( rCmpArr.SetAttrFwd( *pAttr ) )
+ {
+ // Do multiple start at that position? Do also check those:
+ nSttPos = pAttr->GetStart();
+ while( ++nPos < rHtArr.Count() )
+ {
+ pAttr = rHtArr.Get( nPos );
+ if( nSttPos != pAttr->GetStart() || !rCmpArr.SetAttrFwd( *pAttr ) )
+ break;
+ }
+
+ if( !rCmpArr.Found() )
+ continue;
+
+ // then we have our search area
+ nSttPos = rCmpArr.Start();
+ nEndPos = rCmpArr.End();
+ if( nSttPos > nEndPos )
+ return false;
+
+ lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
+ return true;
+ }
+ }
+
+ if( !rCmpArr.CheckStack() )
+ return false;
+ nSttPos = rCmpArr.Start();
+ nEndPos = rCmpArr.End();
+ if( nSttPos > nEndPos )
+ return false;
+
+ lcl_SetAttrPam( rPam, nSttPos, &nEndPos, true );
+ return true;
+}
+
+static bool lcl_SearchBackward( const SwTextNode& rTextNd, SwAttrCheckArr& rCmpArr,
+ SwPaM& rPam )
+{
+ sal_Int32 nEndPos;
+ rCmpArr.SetNewSet( rTextNd, rPam );
+ if( !rTextNd.HasHints() )
+ {
+ if( !rCmpArr.Found() )
+ return false;
+ nEndPos = rCmpArr.GetNdEnd();
+ lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
+ return true;
+ }
+
+ const SwpHints& rHtArr = rTextNd.GetSwpHints();
+ const SwTextAttr* pAttr;
+ size_t nPos = rHtArr.Count();
+ sal_Int32 nSttPos;
+
+ // if everything is already there then check with which it will be ended
+ if( rCmpArr.Found() )
+ {
+ while( nPos )
+ {
+ pAttr = rHtArr.GetSortedByEnd( --nPos );
+ if( !rCmpArr.SetAttrBwd( *pAttr ) )
+ {
+ nSttPos = pAttr->GetAnyEnd();
+ if( nSttPos < rCmpArr.GetNdEnd() )
+ {
+ // found end
+ nEndPos = rCmpArr.GetNdEnd();
+ lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
+ return true;
+ }
+
+ // continue search
+ break;
+ }
+ }
+
+ if( !nPos && rCmpArr.Found() )
+ {
+ // found
+ nEndPos = rCmpArr.GetNdEnd();
+ lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, false );
+ return true;
+ }
+ }
+
+ while( nPos )
+ {
+ pAttr = rHtArr.GetSortedByEnd( --nPos );
+ if( rCmpArr.SetAttrBwd( *pAttr ) )
+ {
+ // Do multiple start at that position? Do also check those:
+ if( nPos )
+ {
+ nEndPos = pAttr->GetAnyEnd();
+ while( --nPos )
+ {
+ pAttr = rHtArr.GetSortedByEnd( nPos );
+ if( nEndPos != pAttr->GetAnyEnd() || !rCmpArr.SetAttrBwd( *pAttr ) )
+ break;
+ }
+ }
+ if( !rCmpArr.Found() )
+ continue;
+
+ // then we have our search area
+ nSttPos = rCmpArr.Start();
+ nEndPos = rCmpArr.End();
+ if( nSttPos > nEndPos )
+ return false;
+
+ lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
+ return true;
+ }
+ }
+
+ if( !rCmpArr.CheckStack() )
+ return false;
+ nSttPos = rCmpArr.Start();
+ nEndPos = rCmpArr.End();
+ if( nSttPos > nEndPos )
+ return false;
+
+ lcl_SetAttrPam( rPam, nSttPos, &nEndPos, false );
+ return true;
+}
+
+static bool lcl_Search( const SwContentNode& rCNd, const SfxItemSet& rCmpSet, bool bNoColls )
+{
+ // search only hard attribution?
+ if( bNoColls && !rCNd.HasSwAttrSet() )
+ return false;
+
+ const SfxItemSet& rNdSet = rCNd.GetSwAttrSet();
+ SfxItemIter aIter( rCmpSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ const SfxPoolItem* pNdItem;
+ sal_uInt16 nWhich;
+
+ do
+ {
+ if( IsInvalidItem( pItem ))
+ {
+ nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() );
+ if( SfxItemState::SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem )
+ || CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) ))
+ return false;
+ }
+ else
+ {
+ nWhich = pItem->Which();
+
+ if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem ))
+ return false;
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+ return true; // found
+}
+
+namespace sw {
+
+bool FindAttrImpl(SwPaM & rSearchPam,
+ const SfxPoolItem& rAttr, SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly,
+ SwRootFrame const*const pLayout)
+{
+ // determine which attribute is searched:
+ const sal_uInt16 nWhich = rAttr.Which();
+ bool bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich);
+ assert(isTXTATR(nWhich)); // sw_redlinehide: only works for non-formatting hints such as needed in UpdateFields; use FindAttrsImpl for others
+
+ std::unique_ptr<SwPaM> pPam(sw::MakeRegion(fnMove, rRegion));
+
+ bool bFound = false;
+ bool bFirst = true;
+ const bool bSrchForward = &fnMove == &fnMoveForward;
+ SwContentNode * pNode;
+
+ // if at beginning/end then move it out of the node
+ if( bSrchForward
+ ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetContentNode()->Len()
+ : !pPam->GetPoint()->nContent.GetIndex() )
+ {
+ if( !(*fnMove.fnNds)( &pPam->GetPoint()->nNode, false ))
+ {
+ return false;
+ }
+ SwContentNode *pNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pNd, bSrchForward ? 0 : pNd->Len() );
+ }
+
+ while (nullptr != (pNode = ::GetNode(*pPam, bFirst, fnMove, bInReadOnly, pLayout)))
+ {
+ if( bCharAttr )
+ {
+ if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
+ continue;
+
+ SwTextFrame const*const pFrame(pLayout
+ ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
+ : nullptr);
+ if (pFrame)
+ {
+ SwTextNode const* pAttrNode(nullptr);
+ SwTextAttr const* pAttr(nullptr);
+ if (bSrchForward)
+ {
+ sw::MergedAttrIter iter(*pFrame);
+ do
+ {
+ pAttr = iter.NextAttr(&pAttrNode);
+ }
+ while (pAttr
+ && (pAttrNode->GetIndex() < pPam->GetPoint()->nNode.GetIndex()
+ || (pAttrNode->GetIndex() == pPam->GetPoint()->nNode.GetIndex()
+ && pAttr->GetStart() < pPam->GetPoint()->nContent.GetIndex())
+ || pAttr->Which() != nWhich));
+ }
+ else
+ {
+ sw::MergedAttrIterReverse iter(*pFrame);
+ do
+ {
+ pAttr = iter.PrevAttr(&pAttrNode);
+ }
+ while (pAttr
+ && (pPam->GetPoint()->nNode.GetIndex() < pAttrNode->GetIndex()
+ || (pPam->GetPoint()->nNode.GetIndex() == pAttrNode->GetIndex()
+ && pPam->GetPoint()->nContent.GetIndex() <= pAttr->GetStart())
+ || pAttr->Which() != nWhich));
+ }
+ if (pAttr)
+ {
+ assert(pAttrNode);
+ pPam->GetPoint()->nNode = *pAttrNode;
+ lcl_SetAttrPam(*pPam, pAttr->GetStart(), pAttr->End(), bSrchForward);
+ bFound = true;
+ break;
+ }
+ }
+ else if (!pLayout && pNode->GetTextNode()->HasHints() &&
+ lcl_SearchAttr(*pNode->GetTextNode(), *pPam, rAttr, fnMove))
+ {
+ bFound = true;
+ }
+ if (bFound)
+ {
+ // set to the values of the attribute
+ rSearchPam.SetMark();
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ *rSearchPam.GetMark() = *pPam->GetMark();
+ break;
+ }
+ else if (isTXTATR(nWhich))
+ continue;
+ }
+
+#if 0
+ // no hard attribution, so check if node was asked for this attr before
+ if( !pNode->HasSwAttrSet() )
+ {
+ SwFormat* pTmpFormat = pNode->GetFormatColl();
+ if( !aFormatArr.insert( pTmpFormat ).second )
+ continue; // collection was requested earlier
+ }
+
+ if( SfxItemState::SET == pNode->GetSwAttrSet().GetItemState( nWhich,
+ true, &pItem ))
+ {
+ // FORWARD: SPoint at the end, GetMark at the beginning of the node
+ // BACKWARD: SPoint at the beginning, GetMark at the end of the node
+ // always: incl. start and incl. end
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ rSearchPam.SetMark();
+ pNode->MakeEndIndex( &rSearchPam.GetPoint()->nContent );
+ bFound = true;
+ break;
+ }
+#endif
+ }
+
+ // if backward search, switch point and mark
+ if( bFound && !bSrchForward )
+ rSearchPam.Exchange();
+
+ return bFound;
+}
+
+} // namespace sw
+
+typedef bool (*FnSearchAttr)( const SwTextNode&, SwAttrCheckArr&, SwPaM& );
+
+static bool FindAttrsImpl(SwPaM & rSearchPam,
+ const SfxItemSet& rSet, bool bNoColls, SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly, bool bMoveFirst,
+ SwRootFrame const*const pLayout)
+{
+ std::unique_ptr<SwPaM> pPam(sw::MakeRegion(fnMove, rRegion));
+
+ bool bFound = false;
+ bool bFirst = true;
+ const bool bSrchForward = &fnMove == &fnMoveForward;
+ SwContentNode * pNode;
+ o3tl::sorted_vector<SwFormat*> aFormatArr;
+
+ // check which text/char attributes are searched
+ SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls );
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_GRFATR_END-1> aOtherSet( rSearchPam.GetDoc().GetAttrPool() );
+ aOtherSet.Put( rSet, false ); // got all invalid items
+
+ FnSearchAttr fnSearch = bSrchForward
+ ? (&::lcl_SearchForward)
+ : (&::lcl_SearchBackward);
+
+ // if at beginning/end then move it out of the node
+ if( bMoveFirst &&
+ ( bSrchForward
+ ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetContentNode()->Len()
+ : !pPam->GetPoint()->nContent.GetIndex() ) )
+ {
+ if( !(*fnMove.fnNds)( &pPam->GetPoint()->nNode, false ))
+ {
+ return false;
+ }
+ SwContentNode *pNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pNd, bSrchForward ? 0 : pNd->Len() );
+ }
+
+ while (nullptr != (pNode = ::GetNode(*pPam, bFirst, fnMove, bInReadOnly, pLayout)))
+ {
+ SwTextFrame const*const pFrame(pLayout && pNode->IsTextNode()
+ ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
+ : nullptr);
+ assert(!pLayout || !pNode->IsTextNode() || pFrame);
+ // sw_redlinehide: it's apparently not possible to find break items
+ // with the UI, so checking one node is enough
+ SwContentNode const& rPropsNode(*(pFrame
+ ? pFrame->GetTextNodeForParaProps()
+ : pNode));
+
+ if( aCmpArr.Count() )
+ {
+ if( !pNode->IsTextNode() ) // CharAttr are only in text nodes
+ continue;
+
+ if (aOtherSet.Count() &&
+ !lcl_Search(rPropsNode, aOtherSet, bNoColls))
+ {
+ continue;
+ }
+ sw::MergedPara const*const pMergedPara(pFrame ? pFrame->GetMergedPara() : nullptr);
+ if (pMergedPara)
+ {
+ SwPosition const& rStart(*pPam->Start());
+ SwPosition const& rEnd(*pPam->End());
+ // no extents? fall back to searching index 0 of propsnode
+ // to find its node items
+ if (pMergedPara->extents.empty())
+ {
+ if (rStart.nNode.GetIndex() <= rPropsNode.GetIndex()
+ && rPropsNode.GetIndex() <= rEnd.nNode.GetIndex())
+ {
+ SwPaM tmp(rPropsNode, 0, rPropsNode, 0);
+ bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, tmp);
+ if (bFound)
+ {
+ *pPam = tmp;
+ }
+ }
+ }
+ else
+ {
+ // iterate the extents, and intersect with input pPam:
+ // the found ranges should never include delete redlines
+ // so that subsequent Replace will not affect them
+ for (size_t i = 0; i < pMergedPara->extents.size(); ++i)
+ {
+ auto const rExtent(pMergedPara->extents[bSrchForward
+ ? i
+ : pMergedPara->extents.size() - i - 1]);
+ if (rExtent.pNode->GetIndex() < rStart.nNode.GetIndex()
+ || rEnd.nNode.GetIndex() < rExtent.pNode->GetIndex())
+ {
+ continue;
+ }
+ sal_Int32 const nStart(rExtent.pNode == &rStart.nNode.GetNode()
+ ? rStart.nContent.GetIndex()
+ : 0);
+ if (rExtent.nEnd <= nStart)
+ {
+ continue;
+ }
+ sal_Int32 const nEnd(rExtent.pNode == &rEnd.nNode.GetNode()
+ ? rEnd.nContent.GetIndex()
+ : rExtent.pNode->Len());
+ if (nEnd < rExtent.nStart
+ || (nStart != nEnd && nEnd == rExtent.nStart))
+ {
+ continue;
+ }
+ SwPaM tmp(*rExtent.pNode, std::max(nStart, rExtent.nStart),
+ *rExtent.pNode, std::min(nEnd, rExtent.nEnd));
+ tmp.Normalize(bSrchForward);
+ bFound = (*fnSearch)(*rExtent.pNode, aCmpArr, tmp);
+ if (bFound)
+ {
+ *pPam = tmp;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ bFound = (*fnSearch)(*pNode->GetTextNode(), aCmpArr, *pPam);
+ }
+ if (bFound)
+ {
+ // set to the values of the attribute
+ rSearchPam.SetMark();
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ *rSearchPam.GetMark() = *pPam->GetMark();
+ break;
+ }
+ continue; // text attribute
+ }
+
+ if( !aOtherSet.Count() )
+ continue;
+
+ // no hard attribution, so check if node was asked for this attr before
+ // (as an optimisation)
+ if (!rPropsNode.HasSwAttrSet())
+ {
+ SwFormat* pTmpFormat = rPropsNode.GetFormatColl();
+ if( !aFormatArr.insert( pTmpFormat ).second )
+ continue; // collection was requested earlier
+ }
+
+ if (lcl_Search(rPropsNode, aOtherSet, bNoColls))
+ {
+ // FORWARD: SPoint at the end, GetMark at the beginning of the node
+ // BACKWARD: SPoint at the beginning, GetMark at the end of the node
+ if (pFrame)
+ {
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ rSearchPam.SetMark();
+ *rSearchPam.GetMark() = pFrame->MapViewToModelPos(
+ TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
+ }
+ else
+ {
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ rSearchPam.SetMark();
+ if (bSrchForward)
+ {
+ pNode->MakeEndIndex( &rSearchPam.GetPoint()->nContent );
+ }
+ else
+ {
+ pNode->MakeStartIndex( &rSearchPam.GetPoint()->nContent );
+ }
+ }
+ bFound = true;
+ break;
+ }
+ }
+
+ // in search direction, mark precedes point, because the next iteration
+ // starts at point
+ if (bFound)
+ {
+ rSearchPam.Normalize(!bSrchForward);
+ }
+
+ return bFound;
+}
+
+namespace {
+
+/// parameters for search for attributes
+struct SwFindParaAttr : public SwFindParas
+{
+ bool m_bNoCollection;
+ const SfxItemSet *pSet, *pReplSet;
+ const i18nutil::SearchOptions2 *pSearchOpt;
+ SwCursor& m_rCursor;
+ SwRootFrame const* m_pLayout;
+ std::unique_ptr<utl::TextSearch> pSText;
+
+ SwFindParaAttr( const SfxItemSet& rSet, bool bNoCollection,
+ const i18nutil::SearchOptions2* pOpt, const SfxItemSet* pRSet,
+ SwCursor& rCursor, SwRootFrame const*const pLayout)
+ : m_bNoCollection(bNoCollection)
+ , pSet( &rSet )
+ , pReplSet( pRSet )
+ , pSearchOpt( pOpt )
+ , m_rCursor(rCursor)
+ , m_pLayout(pLayout)
+ {}
+
+ virtual ~SwFindParaAttr() {}
+
+ virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly,
+ std::unique_ptr<SvxSearchItem>& xSearchItem) override;
+ virtual bool IsReplaceMode() const override;
+};
+
+}
+
+int SwFindParaAttr::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly,
+ std::unique_ptr<SvxSearchItem>& xSearchItem)
+{
+ // replace string (only if text given and search is not parameterized)?
+ bool bReplaceText = pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
+ !pSet->Count() );
+ bool bReplaceAttr = pReplSet && pReplSet->Count();
+ bool bMoveFirst = !bReplaceAttr;
+ if( bInReadOnly && (bReplaceAttr || bReplaceText ))
+ bInReadOnly = false;
+
+ // We search for attributes, should we search for text as well?
+ {
+ SwPaM aRegion( *rRegion.GetMark(), *rRegion.GetPoint() );
+ SwPaM* pTextRegion = &aRegion;
+ SwPaM aSrchPam( *rCursor.GetPoint() );
+
+ while( true )
+ {
+ if( pSet->Count() ) // any attributes?
+ {
+ // first attributes
+ if (!FindAttrsImpl(aSrchPam, *pSet, m_bNoCollection, fnMove, aRegion, bInReadOnly, bMoveFirst, m_pLayout))
+ return FIND_NOT_FOUND;
+ bMoveFirst = true;
+
+ if( !pSearchOpt )
+ break; // ok, only attributes, so found
+
+ pTextRegion = &aSrchPam;
+ }
+ else if( !pSearchOpt )
+ return FIND_NOT_FOUND;
+
+ // then search in text of it
+ if( !pSText )
+ {
+ i18nutil::SearchOptions2 aTmp( *pSearchOpt );
+
+ // search in selection
+ aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE |
+ SearchFlags::REG_NOT_ENDOFLINE);
+
+ aTmp.Locale = SvtSysLocale().GetLanguageTag().getLocale();
+
+ pSText.reset( new utl::TextSearch( aTmp ) );
+ }
+
+ // TODO: searching for attributes in Outliner text?!
+
+ // continue search in correct section (pTextRegion)
+ if (sw::FindTextImpl(aSrchPam, *pSearchOpt, false/*bSearchInNotes*/, *pSText, fnMove, *pTextRegion, bInReadOnly, m_pLayout, xSearchItem) &&
+ *aSrchPam.GetMark() != *aSrchPam.GetPoint() )
+ break; // found
+ else if( !pSet->Count() )
+ return FIND_NOT_FOUND; // only text and nothing found
+
+ *aRegion.GetMark() = *aSrchPam.GetPoint();
+ }
+
+ *rCursor.GetPoint() = *aSrchPam.GetPoint();
+ rCursor.SetMark();
+ *rCursor.GetMark() = *aSrchPam.GetMark();
+ }
+
+ if( bReplaceText )
+ {
+ const bool bRegExp(
+ SearchAlgorithms2::REGEXP == pSearchOpt->AlgorithmType2);
+ SwIndex& rSttCntIdx = rCursor.Start()->nContent;
+ const sal_Int32 nSttCnt = rSttCntIdx.GetIndex();
+
+ // add to shell-cursor-ring so that the regions will be moved eventually
+ SwPaM* pPrevRing(nullptr);
+ if( bRegExp )
+ {
+ pPrevRing = const_cast<SwPaM &>(rRegion).GetPrev();
+ const_cast<SwPaM &>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
+ }
+
+ std::optional<OUString> xRepl;
+ if (bRegExp)
+ xRepl = sw::ReplaceBackReferences(*pSearchOpt, &rCursor, m_pLayout);
+ sw::ReplaceImpl(rCursor,
+ xRepl ? *xRepl : pSearchOpt->replaceString, bRegExp,
+ m_rCursor.GetDoc(), m_pLayout);
+
+ m_rCursor.SaveTableBoxContent( rCursor.GetPoint() );
+
+ if( bRegExp )
+ {
+ // and remove region again
+ SwPaM* p;
+ SwPaM* pNext = const_cast<SwPaM*>(&rRegion);
+ do {
+ p = pNext;
+ pNext = p->GetNext();
+ p->MoveTo(const_cast<SwPaM*>(&rRegion));
+ } while( p != pPrevRing );
+ }
+ rSttCntIdx = nSttCnt;
+ }
+
+ if( bReplaceAttr )
+ {
+ // is the selection still existent?
+ // all searched attributes are reset to default if
+ // they are not in ReplaceSet
+ if( !pSet->Count() )
+ {
+ rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
+ rCursor, *pReplSet, SetAttrMode::DEFAULT, m_pLayout);
+ }
+ else
+ {
+ SfxItemPool* pPool = pReplSet->GetPool();
+ SfxItemSet aSet( *pPool, pReplSet->GetRanges() );
+
+ SfxItemIter aIter( *pSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ // reset all that are not set with pool defaults
+ if( !IsInvalidItem( pItem ) && SfxItemState::SET !=
+ pReplSet->GetItemState( pItem->Which(), false ))
+ aSet.Put( pPool->GetDefaultItem( pItem->Which() ));
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+ aSet.Put( *pReplSet );
+ rCursor.GetDoc().getIDocumentContentOperations().InsertItemSet(
+ rCursor, aSet, SetAttrMode::DEFAULT, m_pLayout);
+ }
+
+ return FIND_NO_RING;
+ }
+ else
+ return FIND_FOUND;
+}
+
+bool SwFindParaAttr::IsReplaceMode() const
+{
+ return ( pSearchOpt && !pSearchOpt->replaceString.isEmpty() ) ||
+ ( pReplSet && pReplSet->Count() );
+}
+
+/// search for attributes
+sal_uLong SwCursor::FindAttrs( const SfxItemSet& rSet, bool bNoCollections,
+ SwDocPositions nStart, SwDocPositions nEnd,
+ bool& bCancel, FindRanges eFndRngs,
+ const i18nutil::SearchOptions2* pSearchOpt,
+ const SfxItemSet* pReplSet,
+ SwRootFrame const*const pLayout)
+{
+ // switch off OLE-notifications
+ SwDoc& rDoc = GetDoc();
+ Link<bool,void> aLnk( rDoc.GetOle2Link() );
+ rDoc.SetOle2Link( Link<bool,void>() );
+
+ bool bReplace = ( pSearchOpt && ( !pSearchOpt->replaceString.isEmpty() ||
+ !rSet.Count() ) ) ||
+ (pReplSet && pReplSet->Count());
+ bool const bStartUndo = rDoc.GetIDocumentUndoRedo().DoesUndo() && bReplace;
+ if (bStartUndo)
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr );
+ }
+
+ SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt,
+ pReplSet, *this, pLayout );
+
+ sal_uLong nRet = FindAll( aSwFindParaAttr, nStart, nEnd, eFndRngs, bCancel );
+ rDoc.SetOle2Link( aLnk );
+ if( nRet && bReplace )
+ rDoc.getIDocumentState().SetModified();
+
+ if (bStartUndo)
+ {
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REPLACE, nullptr );
+ }
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/findcoll.cxx b/sw/source/core/crsr/findcoll.cxx
new file mode 100644
index 000000000..ad43b7afe
--- /dev/null
+++ b/sw/source/core/crsr/findcoll.cxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swcrsr.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <pamtyp.hxx>
+#include <swundo.hxx>
+#include <SwRewriter.hxx>
+#include <strings.hrc>
+
+namespace {
+
+/// parameters for a search for FormatCollections
+struct SwFindParaFormatColl : public SwFindParas
+{
+ const SwTextFormatColl *pFormatColl, *pReplColl;
+ SwRootFrame const* m_pLayout;
+ SwFindParaFormatColl(const SwTextFormatColl& rFormatColl,
+ const SwTextFormatColl *const pRpColl,
+ SwRootFrame const*const pLayout)
+ : pFormatColl( &rFormatColl )
+ , pReplColl( pRpColl )
+ , m_pLayout(pLayout)
+ {}
+ virtual ~SwFindParaFormatColl() {}
+ virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly, std::unique_ptr<SvxSearchItem>& xSearchItem) override;
+ virtual bool IsReplaceMode() const override;
+};
+
+}
+
+int SwFindParaFormatColl::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly,
+ std::unique_ptr<SvxSearchItem>& /*xSearchItem*/)
+{
+ int nRet = FIND_FOUND;
+ if( bInReadOnly && pReplColl )
+ bInReadOnly = false;
+
+ if (!sw::FindFormatImpl(rCursor, *pFormatColl, fnMove, rRegion, bInReadOnly, m_pLayout))
+ nRet = FIND_NOT_FOUND;
+ else if( pReplColl )
+ {
+ rCursor.GetDoc().SetTextFormatColl(rCursor,
+ const_cast<SwTextFormatColl*>(pReplColl), true, false, m_pLayout);
+ nRet = FIND_NO_RING;
+ }
+ return nRet;
+}
+
+bool SwFindParaFormatColl::IsReplaceMode() const
+{
+ return nullptr != pReplColl;
+}
+
+/// search for Format-Collections
+sal_uLong SwCursor::FindFormat( const SwTextFormatColl& rFormatColl, SwDocPositions nStart,
+ SwDocPositions nEnd, bool& bCancel,
+ FindRanges eFndRngs, const SwTextFormatColl* pReplFormatColl,
+ SwRootFrame const*const pLayout)
+{
+ // switch off OLE-notifications
+ SwDoc& rDoc = GetDoc();
+ Link<bool,void> aLnk( rDoc.GetOle2Link() );
+ rDoc.SetOle2Link( Link<bool,void>() );
+
+ bool const bStartUndo =
+ rDoc.GetIDocumentUndoRedo().DoesUndo() && pReplFormatColl;
+ if (bStartUndo)
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, rFormatColl.GetName());
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, pReplFormatColl->GetName());
+
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE_STYLE,
+ &aRewriter );
+ }
+
+ SwFindParaFormatColl aSwFindParaFormatColl(rFormatColl, pReplFormatColl, pLayout);
+
+ sal_uLong nRet = FindAll( aSwFindParaFormatColl, nStart, nEnd, eFndRngs, bCancel );
+ rDoc.SetOle2Link( aLnk );
+
+ if( nRet && pReplFormatColl )
+ rDoc.getIDocumentState().SetModified();
+
+ if (bStartUndo)
+ {
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/findfmt.cxx b/sw/source/core/crsr/findfmt.cxx
new file mode 100644
index 000000000..a04850284
--- /dev/null
+++ b/sw/source/core/crsr/findfmt.cxx
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <pamtyp.hxx>
+#include <pam.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <memory>
+
+namespace sw {
+
+bool FindFormatImpl(SwPaM & rSearchPam,
+ const SwFormat& rFormat, SwMoveFnCollection const & fnMove,
+ const SwPaM &rRegion, bool bInReadOnly,
+ SwRootFrame const*const pLayout)
+{
+ bool bFound = false;
+ const bool bSrchForward = &fnMove == &fnMoveForward;
+ std::unique_ptr<SwPaM> pPam(MakeRegion( fnMove, rRegion ));
+
+ // if at beginning/end then move it out of the node
+ if( bSrchForward
+ ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetContentNode()->Len()
+ : !pPam->GetPoint()->nContent.GetIndex() )
+ {
+ if( !(*fnMove.fnNds)( &pPam->GetPoint()->nNode, false ))
+ {
+ return false;
+ }
+ SwContentNode *pNd = pPam->GetPoint()->nNode.GetNode().GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pNd, bSrchForward ? 0 : pNd->Len() );
+ }
+
+ bool bFirst = true;
+ SwContentNode* pNode;
+ while (nullptr != (pNode = ::GetNode(*pPam, bFirst, fnMove, bInReadOnly, pLayout)))
+ {
+ SwTextFrame const*const pFrame(pLayout && pNode->IsTextNode()
+ ? static_cast<SwTextFrame const*>(pNode->getLayoutFrame(pLayout))
+ : nullptr);
+ assert(!pLayout || !pNode->IsTextNode() || pFrame);
+ SwContentNode const& rPropsNode(*(pFrame
+ ? pFrame->GetTextNodeForParaProps()
+ : pNode));
+
+ if (rPropsNode.GetFormatColl() == &rFormat)
+ {
+ // if a FormatCollection is found then it is definitely a SwContentNode
+
+ // FORWARD: SPoint at the end, GetMark at the beginning of the node
+ // BACKWARD: SPoint at the beginning, GetMark at the end of the node
+ // always: incl. start and incl. end
+ if (pFrame)
+ {
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ rSearchPam.SetMark();
+ *rSearchPam.GetMark() = pFrame->MapViewToModelPos(
+ TextFrameIndex(bSrchForward ? pFrame->GetText().getLength() : 0));
+ }
+ else
+ {
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ rSearchPam.SetMark();
+ pNode->MakeEndIndex( &rSearchPam.GetPoint()->nContent );
+ rSearchPam.GetMark()->nContent = 0;
+ }
+
+ // if backward search, switch point and mark
+ if( !bSrchForward )
+ rSearchPam.Exchange();
+
+ bFound = true;
+ break;
+ }
+ }
+ return bFound;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/findtxt.cxx b/sw/source/core/crsr/findtxt.cxx
new file mode 100644
index 000000000..6e29c3614
--- /dev/null
+++ b/sw/source/core/crsr/findtxt.cxx
@@ -0,0 +1,1177 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/util/SearchResult.hpp>
+#include <comphelper/lok.hxx>
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <svx/svdview.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <editeng/outliner.hxx>
+#include <osl/diagnose.h>
+
+#include <wrtsh.hxx>
+#include <txatritr.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <swcrsr.hxx>
+#include <redline.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <dcontact.hxx>
+#include <pamtyp.hxx>
+#include <ndtxt.hxx>
+#include <swundo.hxx>
+#include <UndoInsert.hxx>
+#include <breakit.hxx>
+#include <docsh.hxx>
+#include <PostItMgr.hxx>
+#include <view.hxx>
+
+using namespace ::com::sun::star;
+using namespace util;
+
+namespace {
+
+/// because the Find may be called on the View or the Model, we need an index
+/// afflicted by multiple personality disorder
+struct AmbiguousIndex
+{
+private:
+ sal_Int32 m_value;
+
+#ifndef NDEBUG
+ enum class tags : char { Any, Frame, Model };
+ tags m_tag;
+#endif
+
+public:
+ AmbiguousIndex() : m_value(-1)
+#ifndef NDEBUG
+ , m_tag(tags::Any)
+#endif
+ {}
+ explicit AmbiguousIndex(sal_Int32 const value
+#ifndef NDEBUG
+ , tags const tag
+#endif
+ ) : m_value(value)
+#ifndef NDEBUG
+ , m_tag(tag)
+#endif
+ {}
+
+ sal_Int32 & GetAnyIndex() { return m_value; } ///< for arithmetic
+ sal_Int32 const& GetAnyIndex() const { return m_value; } ///< for arithmetic
+ TextFrameIndex GetFrameIndex() const
+ {
+ assert(m_tag != tags::Model);
+ return TextFrameIndex(m_value);
+ }
+ sal_Int32 GetModelIndex() const
+ {
+ assert(m_tag != tags::Frame);
+ return m_value;
+ }
+ void SetFrameIndex(TextFrameIndex const value)
+ {
+#ifndef NDEBUG
+ m_tag = tags::Frame;
+#endif
+ m_value = sal_Int32(value);
+ }
+ void SetModelIndex(sal_Int32 const value)
+ {
+#ifndef NDEBUG
+ m_tag = tags::Model;
+#endif
+ m_value = value;
+ }
+
+ bool operator ==(AmbiguousIndex const& rOther) const
+ {
+ assert(m_tag == tags::Any || rOther.m_tag == tags::Any || m_tag == rOther.m_tag);
+ return m_value == rOther.m_value;
+ }
+ bool operator <=(AmbiguousIndex const& rOther) const
+ {
+ assert(m_tag == tags::Any || rOther.m_tag == tags::Any || m_tag == rOther.m_tag);
+ return m_value <= rOther.m_value;
+ }
+ bool operator < (AmbiguousIndex const& rOther) const
+ {
+ assert(m_tag == tags::Any || rOther.m_tag == tags::Any || m_tag == rOther.m_tag);
+ return m_value < rOther.m_value;
+ }
+ AmbiguousIndex operator - (AmbiguousIndex const& rOther) const
+ {
+ assert(m_tag == tags::Any || rOther.m_tag == tags::Any || m_tag == rOther.m_tag);
+ return AmbiguousIndex(m_value - rOther.m_value
+#ifndef NDEBUG
+ , std::max(m_tag, rOther.m_tag)
+#endif
+ );
+ }
+};
+
+class MaybeMergedIter
+{
+ std::optional<sw::MergedAttrIter> m_oMergedIter;
+ SwTextNode const*const m_pNode;
+ size_t m_HintIndex;
+
+public:
+ MaybeMergedIter(SwTextFrame const*const pFrame, SwTextNode const*const pNode)
+ : m_pNode(pNode)
+ , m_HintIndex(0)
+ {
+ if (pFrame)
+ {
+ m_oMergedIter.emplace(*pFrame);
+ }
+ }
+
+ SwTextAttr const* NextAttr(SwTextNode const*& rpNode)
+ {
+ if (m_oMergedIter)
+ {
+ return m_oMergedIter->NextAttr(&rpNode);
+ }
+ if (SwpHints const*const pHints = m_pNode->GetpSwpHints())
+ {
+ if (m_HintIndex < pHints->Count())
+ {
+ rpNode = m_pNode;
+ return pHints->Get(m_HintIndex++);
+ }
+ }
+ return nullptr;
+ }
+};
+
+}
+
+static OUString
+lcl_CleanStr(const SwTextNode& rNd,
+ SwTextFrame const*const pFrame,
+ SwRootFrame const*const pLayout,
+ AmbiguousIndex const nStart, AmbiguousIndex & rEnd,
+ std::vector<AmbiguousIndex> &rArr,
+ bool const bRemoveSoftHyphen, bool const bRemoveCommentAnchors)
+{
+ OUStringBuffer buf(pLayout ? pFrame->GetText() : rNd.GetText());
+ rArr.clear();
+
+ MaybeMergedIter iter(pLayout ? pFrame : nullptr, pLayout ? nullptr : &rNd);
+
+ AmbiguousIndex nSoftHyphen = nStart;
+ AmbiguousIndex nHintStart;
+ bool bNewHint = true;
+ bool bNewSoftHyphen = true;
+ const AmbiguousIndex nEnd = rEnd;
+ std::vector<AmbiguousIndex> aReplaced;
+ SwTextNode const* pNextHintNode(nullptr);
+ SwTextAttr const* pNextHint(iter.NextAttr(pNextHintNode));
+
+ do
+ {
+ if ( bNewHint )
+ {
+ if (pLayout)
+ {
+ nHintStart.SetFrameIndex(pNextHint
+ ? pFrame->MapModelToView(pNextHintNode, pNextHint->GetStart())
+ : TextFrameIndex(-1));
+ }
+ else
+ {
+ nHintStart.SetModelIndex(pNextHint ? pNextHint->GetStart() : -1);
+ }
+ }
+
+ if ( bNewSoftHyphen )
+ {
+ if (pLayout)
+ {
+ nSoftHyphen.SetFrameIndex(TextFrameIndex(bRemoveSoftHyphen
+ ? pFrame->GetText().indexOf(CHAR_SOFTHYPHEN, nSoftHyphen.GetAnyIndex())
+ : -1));
+ }
+ else
+ {
+ nSoftHyphen.SetModelIndex(bRemoveSoftHyphen
+ ? rNd.GetText().indexOf(CHAR_SOFTHYPHEN, nSoftHyphen.GetAnyIndex())
+ : -1);
+ }
+ }
+
+ bNewHint = false;
+ bNewSoftHyphen = false;
+ AmbiguousIndex nStt;
+
+ // Check if next stop is a hint.
+ if (0 <= nHintStart.GetAnyIndex()
+ && (-1 == nSoftHyphen.GetAnyIndex() || nHintStart < nSoftHyphen)
+ && nHintStart < nEnd )
+ {
+ nStt = nHintStart;
+ bNewHint = true;
+ }
+ // Check if next stop is a soft hyphen.
+ else if ( -1 != nSoftHyphen.GetAnyIndex()
+ && (-1 == nHintStart.GetAnyIndex() || nSoftHyphen < nHintStart)
+ && nSoftHyphen < nEnd)
+ {
+ nStt = nSoftHyphen;
+ bNewSoftHyphen = true;
+ }
+ // If nSoftHyphen == nHintStart, the current hint *must* be a hint with an end.
+ else if (-1 != nSoftHyphen.GetAnyIndex() && nSoftHyphen == nHintStart)
+ {
+ nStt = nSoftHyphen;
+ bNewHint = true;
+ bNewSoftHyphen = true;
+ }
+ else
+ break;
+
+ AmbiguousIndex nCurrent(nStt);
+ nCurrent.GetAnyIndex() -= rArr.size();
+
+ if ( bNewHint )
+ {
+ if (pNextHint && pNextHint->HasDummyChar() && (nStart <= nStt))
+ {
+ switch (pNextHint->Which())
+ {
+ case RES_TXTATR_FLYCNT:
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_REFMARK:
+ case RES_TXTATR_TOXMARK:
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ {
+ // (1998) they are desired as separators and
+ // belong not any longer to a word.
+ // they should also be ignored at a
+ // beginning/end of a sentence if blank. Those are
+ // simply removed if first. If at the end, we keep the
+ // replacement and remove afterwards all at a string's
+ // end (might be normal 0x7f).
+ const bool bEmpty = pNextHint->Which() != RES_TXTATR_FIELD
+ || (static_txtattr_cast<SwTextField const*>(pNextHint)->GetFormatField().GetField()->ExpandField(true, pLayout).isEmpty());
+ if ( bEmpty && nStart == nCurrent )
+ {
+ rArr.push_back( nCurrent );
+ if (rEnd.GetAnyIndex() > nCurrent.GetAnyIndex())
+ {
+ --rEnd.GetAnyIndex();
+ }
+ buf.remove(nCurrent.GetAnyIndex(), 1);
+ }
+ else
+ {
+ if ( bEmpty )
+ aReplaced.push_back( nCurrent );
+ buf[nCurrent.GetAnyIndex()] = '\x7f';
+ }
+ }
+ break;
+ case RES_TXTATR_ANNOTATION:
+ {
+ if( bRemoveCommentAnchors )
+ {
+ rArr.push_back( nCurrent );
+ if (rEnd.GetAnyIndex() > nCurrent.GetAnyIndex())
+ {
+ --rEnd.GetAnyIndex();
+ }
+ buf.remove( nCurrent.GetAnyIndex(), 1 );
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "unknown case in lcl_CleanStr" );
+ break;
+ }
+ }
+ pNextHint = iter.NextAttr(pNextHintNode);
+ }
+
+ if ( bNewSoftHyphen )
+ {
+ rArr.push_back( nCurrent );
+
+ // If the soft hyphen to be removed is past the end of the range we're searching in,
+ // don't adjust the end.
+ if (rEnd.GetAnyIndex() > nCurrent.GetAnyIndex())
+ {
+ --rEnd.GetAnyIndex();
+ }
+
+ buf.remove(nCurrent.GetAnyIndex(), 1);
+ ++nSoftHyphen.GetAnyIndex();
+ }
+ }
+ while ( true );
+
+ for (auto i = aReplaced.size(); i; )
+ {
+ const AmbiguousIndex nTmp = aReplaced[ --i ];
+ if (nTmp.GetAnyIndex() == buf.getLength() - 1)
+ {
+ buf.truncate(nTmp.GetAnyIndex());
+ rArr.push_back( nTmp );
+ --rEnd.GetAnyIndex();
+ }
+ }
+
+ return buf.makeStringAndClear();
+}
+
+static bool DoSearch(SwPaM & rSearchPam,
+ const i18nutil::SearchOptions2& rSearchOpt, utl::TextSearch& rSText,
+ SwMoveFnCollection const & fnMove,
+ bool bSrchForward, bool bRegSearch, bool bChkEmptyPara, bool bChkParaEnd,
+ AmbiguousIndex & nStart, AmbiguousIndex & nEnd, AmbiguousIndex nTextLen,
+ SwTextNode const* pNode, SwTextFrame const* pTextFrame,
+ SwRootFrame const* pLayout, SwPaM* pPam);
+
+namespace sw {
+
+// @param xSearchItem allocate in parent so we can do so outside the calling loop
+bool FindTextImpl(SwPaM & rSearchPam,
+ const i18nutil::SearchOptions2& rSearchOpt, bool bSearchInNotes,
+ utl::TextSearch& rSText,
+ SwMoveFnCollection const & fnMove, const SwPaM & rRegion,
+ bool bInReadOnly, SwRootFrame const*const pLayout,
+ std::unique_ptr<SvxSearchItem>& xSearchItem)
+{
+ if( rSearchOpt.searchString.isEmpty() )
+ return false;
+
+ std::unique_ptr<SwPaM> pPam = sw::MakeRegion(fnMove, rRegion);
+ const bool bSrchForward = &fnMove == &fnMoveForward;
+ SwNodeIndex& rNdIdx = pPam->GetPoint()->nNode;
+ SwIndex& rContentIdx = pPam->GetPoint()->nContent;
+
+ // If bFound is true then the string was found and is between nStart and nEnd
+ bool bFound = false;
+ // start position in text or initial position
+ bool bFirst = true;
+ SwContentNode * pNode;
+
+ const bool bRegSearch = SearchAlgorithms2::REGEXP == rSearchOpt.AlgorithmType2;
+ const bool bChkEmptyPara = bRegSearch && 2 == rSearchOpt.searchString.getLength() &&
+ ( rSearchOpt.searchString == "^$" ||
+ rSearchOpt.searchString == "$^" );
+ const bool bChkParaEnd = bRegSearch && rSearchOpt.searchString == "$";
+
+ if (!xSearchItem)
+ {
+ xSearchItem.reset(new SvxSearchItem(SID_SEARCH_ITEM)); // this is a very expensive operation (calling configmgr etc.)
+ xSearchItem->SetSearchOptions(rSearchOpt);
+ xSearchItem->SetBackward(!bSrchForward);
+ }
+
+ // LanguageType eLastLang = 0;
+ while (nullptr != (pNode = ::GetNode(*pPam, bFirst, fnMove, bInReadOnly, pLayout)))
+ {
+ if( pNode->IsTextNode() )
+ {
+ SwTextNode& rTextNode = *pNode->GetTextNode();
+ SwTextFrame const*const pFrame(pLayout
+ ? static_cast<SwTextFrame const*>(rTextNode.getLayoutFrame(pLayout))
+ : nullptr);
+ assert(!pLayout || pFrame);
+ AmbiguousIndex nTextLen;
+ if (pLayout)
+ {
+ nTextLen.SetFrameIndex(TextFrameIndex(pFrame->GetText().getLength()));
+ }
+ else
+ {
+ nTextLen.SetModelIndex(rTextNode.GetText().getLength());
+ }
+ AmbiguousIndex nEnd;
+ if (pLayout
+ ? FrameContainsNode(*pFrame, pPam->GetMark()->nNode.GetIndex())
+ : rNdIdx == pPam->GetMark()->nNode)
+ {
+ if (pLayout)
+ {
+ nEnd.SetFrameIndex(pFrame->MapModelToViewPos(*pPam->GetMark()));
+ }
+ else
+ {
+ nEnd.SetModelIndex(pPam->GetMark()->nContent.GetIndex());
+ }
+ }
+ else
+ {
+ if (bSrchForward)
+ {
+ nEnd = nTextLen;
+ }
+ else
+ {
+ if (pLayout)
+ {
+ nEnd.SetFrameIndex(TextFrameIndex(0));
+ }
+ else
+ {
+ nEnd.SetModelIndex(0);
+ }
+ }
+ }
+ AmbiguousIndex nStart;
+ if (pLayout)
+ {
+ nStart.SetFrameIndex(pFrame->MapModelToViewPos(*pPam->GetPoint()));
+ }
+ else
+ {
+ nStart.SetModelIndex(rContentIdx.GetIndex());
+ }
+
+ /* #i80135# */
+ // if there are SwPostItFields inside our current node text, we
+ // split the text into separate pieces and search for text inside
+ // the pieces as well as inside the fields
+ MaybeMergedIter iter(pLayout ? pFrame : nullptr, pLayout ? nullptr : &rTextNode);
+
+ // count PostItFields by looping over all fields
+ std::vector<std::pair<SwTextAttr const*, AmbiguousIndex>> postits;
+ if (bSearchInNotes)
+ {
+ if (!bSrchForward)
+ {
+ std::swap(nStart, nEnd);
+ }
+
+ SwTextNode const* pTemp(nullptr);
+ while (SwTextAttr const*const pTextAttr = iter.NextAttr(pTemp))
+ {
+ if ( pTextAttr->Which()==RES_TXTATR_ANNOTATION )
+ {
+ AmbiguousIndex aPos;
+ aPos.SetModelIndex(pTextAttr->GetStart());
+ if (pLayout)
+ {
+ aPos.SetFrameIndex(pFrame->MapModelToView(pTemp, aPos.GetModelIndex()));
+ }
+ if ((nStart <= aPos) && (aPos <= nEnd))
+ {
+ postits.emplace_back(pTextAttr, aPos);
+ }
+ }
+ }
+
+ if (!bSrchForward)
+ {
+ std::swap(nStart, nEnd);
+ }
+
+ }
+
+ SwDocShell *const pDocShell = pNode->GetDoc().GetDocShell();
+ SwWrtShell *const pWrtShell = pDocShell ? pDocShell->GetWrtShell() : nullptr;
+ SwPostItMgr *const pPostItMgr = pWrtShell ? pWrtShell->GetPostItMgr() : nullptr;
+
+ // If there is an active text edit, then search there.
+ bool bEndedTextEdit = false;
+ SdrView* pSdrView = pWrtShell ? pWrtShell->GetDrawView() : nullptr;
+ if (pSdrView)
+ {
+ // If the edited object is not anchored to this node, then ignore it.
+ SdrObject* pObject = pSdrView->GetTextEditObject();
+ if (pObject)
+ {
+ if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
+ {
+ const SwPosition* pPosition = pFrameFormat->GetAnchor().GetContentAnchor();
+ if (!pPosition || (pLayout
+ ? !FrameContainsNode(*pFrame, pPosition->nNode.GetIndex())
+ : pPosition->nNode.GetIndex() != pNode->GetIndex()))
+ pObject = nullptr;
+ }
+ }
+
+ if (pObject)
+ {
+ sal_uInt16 nResult = pSdrView->GetTextEditOutlinerView()->StartSearchAndReplace(*xSearchItem);
+ if (!nResult)
+ {
+ // If not found, end the text edit.
+ pSdrView->SdrEndTextEdit();
+ const Point aPoint(pSdrView->GetAllMarkedRect().TopLeft());
+ pSdrView->UnmarkAll();
+ pWrtShell->CallSetCursor(&aPoint, true);
+ pWrtShell->Edit();
+ bEndedTextEdit = true;
+ }
+ else
+ {
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Writer and editeng selections are not supported in parallel.
+ SvxSearchItem* pSearchItem = SwView::GetSearchItem();
+ // If we just finished search in shape text, don't attempt to do that again.
+ if (!bEndedTextEdit && !(pSearchItem && pSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL))
+ {
+ // If there are any shapes anchored to this node, search there.
+ SwPaM aPaM(pNode->GetDoc().GetNodes().GetEndOfContent());
+ if (pLayout)
+ {
+ *aPaM.GetPoint() = pFrame->MapViewToModelPos(nStart.GetFrameIndex());
+ }
+ else
+ {
+ aPaM.GetPoint()->nNode = rTextNode;
+ aPaM.GetPoint()->nContent.Assign(
+ aPaM.GetPoint()->nNode.GetNode().GetTextNode(),
+ nStart.GetModelIndex());
+ }
+ aPaM.SetMark();
+ if (pLayout)
+ {
+ aPaM.GetMark()->nNode = (pFrame->GetMergedPara()
+ ? *pFrame->GetMergedPara()->pLastNode
+ : rTextNode)
+ .GetIndex() + 1;
+ }
+ else
+ {
+ aPaM.GetMark()->nNode = rTextNode.GetIndex() + 1;
+ }
+ aPaM.GetMark()->nContent.Assign(aPaM.GetMark()->nNode.GetNode().GetTextNode(), 0);
+ if (pNode->GetDoc().getIDocumentDrawModelAccess().Search(aPaM, *xSearchItem) && pSdrView)
+ {
+ if (SdrObject* pObject = pSdrView->GetTextEditObject())
+ {
+ if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
+ {
+ const SwPosition* pPosition = pFrameFormat->GetAnchor().GetContentAnchor();
+ if (pPosition)
+ {
+ // Set search position to the shape's anchor point.
+ *rSearchPam.GetPoint() = *pPosition;
+ rSearchPam.GetPoint()->nContent.Assign(pPosition->nNode.GetNode().GetContentNode(), 0);
+ rSearchPam.SetMark();
+ bFound = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // do we need to finish a note?
+ if (pPostItMgr && pPostItMgr->HasActiveSidebarWin())
+ {
+ if (bSearchInNotes)
+ {
+ if (!postits.empty())
+ {
+ if (bSrchForward)
+ {
+ postits.erase(postits.begin());
+ }
+ else
+ {
+ postits.pop_back(); // hope that's the right one?
+ }
+ }
+ //search inside, finish and put focus back into the doc
+ if (pPostItMgr->FinishSearchReplace(rSearchOpt,bSrchForward))
+ {
+ bFound = true ;
+ break;
+ }
+ }
+ else
+ {
+ pPostItMgr->SetActiveSidebarWin(nullptr);
+ }
+ }
+
+ if (!postits.empty())
+ {
+ // now we have to split
+ AmbiguousIndex nStartInside;
+ AmbiguousIndex nEndInside;
+ sal_Int32 aLoop = bSrchForward ? 0 : postits.size();
+
+ while ((0 <= aLoop) && (o3tl::make_unsigned(aLoop) <= postits.size()))
+ {
+ if (bSrchForward)
+ {
+ if (aLoop == 0)
+ {
+ nStartInside = nStart;
+ }
+ else if (pLayout)
+ {
+ nStartInside.SetFrameIndex(postits[aLoop - 1].second.GetFrameIndex() + TextFrameIndex(1));
+ }
+ else
+ {
+ nStartInside.SetModelIndex(postits[aLoop - 1].second.GetModelIndex() + 1);
+ }
+ nEndInside = static_cast<size_t>(aLoop) == postits.size()
+ ? nEnd
+ : postits[aLoop].second;
+ nTextLen = nEndInside - nStartInside;
+ }
+ else
+ {
+ nStartInside = static_cast<size_t>(aLoop) == postits.size()
+ ? nStart
+ : postits[aLoop].second;
+ if (aLoop == 0)
+ {
+ nEndInside = nEnd;
+ }
+ else if (pLayout)
+ {
+ nEndInside.SetFrameIndex(postits[aLoop - 1].second.GetFrameIndex() + TextFrameIndex(1));
+ }
+ else
+ {
+ nEndInside.SetModelIndex(postits[aLoop - 1].second.GetModelIndex() + 1);
+ }
+ nTextLen = nStartInside - nEndInside;
+ }
+ // search inside the text between a note
+ bFound = DoSearch( rSearchPam,
+ rSearchOpt, rSText, fnMove, bSrchForward,
+ bRegSearch, bChkEmptyPara, bChkParaEnd,
+ nStartInside, nEndInside, nTextLen,
+ pNode->GetTextNode(), pFrame, pLayout,
+ pPam.get() );
+ if ( bFound )
+ break;
+ else
+ {
+ // we should now be right in front of a note, search inside
+ if (bSrchForward
+ ? (static_cast<size_t>(aLoop) != postits.size())
+ : (aLoop != 0))
+ {
+ const SwTextAttr *const pTextAttr = bSrchForward
+ ? postits[aLoop].first
+ : postits[aLoop - 1].first;
+ if (pPostItMgr && pPostItMgr->SearchReplace(
+ static_txtattr_cast<SwTextField const*>(pTextAttr)->GetFormatField(),rSearchOpt,bSrchForward))
+ {
+ bFound = true ;
+ break;
+ }
+ }
+ }
+ aLoop = bSrchForward ? aLoop+1 : aLoop-1;
+ }
+ }
+ else
+ {
+ // if there is no SwPostItField inside or searching inside notes
+ // is disabled, we search the whole length just like before
+ bFound = DoSearch( rSearchPam,
+ rSearchOpt, rSText, fnMove, bSrchForward,
+ bRegSearch, bChkEmptyPara, bChkParaEnd,
+ nStart, nEnd, nTextLen,
+ pNode->GetTextNode(), pFrame, pLayout,
+ pPam.get() );
+ }
+ if (bFound)
+ break;
+ }
+ }
+ return bFound;
+}
+
+} // namespace sw
+
+bool DoSearch(SwPaM & rSearchPam,
+ const i18nutil::SearchOptions2& rSearchOpt, utl::TextSearch& rSText,
+ SwMoveFnCollection const & fnMove, bool bSrchForward, bool bRegSearch,
+ bool bChkEmptyPara, bool bChkParaEnd,
+ AmbiguousIndex & nStart, AmbiguousIndex & nEnd, AmbiguousIndex const nTextLen,
+ SwTextNode const*const pNode, SwTextFrame const*const pFrame,
+ SwRootFrame const*const pLayout, SwPaM* pPam)
+{
+ bool bFound = false;
+ SwNodeIndex& rNdIdx = pPam->GetPoint()->nNode;
+ OUString sCleanStr;
+ std::vector<AmbiguousIndex> aFltArr;
+ LanguageType eLastLang = LANGUAGE_SYSTEM;
+ // if the search string contains a soft hyphen,
+ // we don't strip them from the text:
+ bool bRemoveSoftHyphens = true;
+ // if the search string contains a comment, we don't strip them from the text
+ const bool bRemoveCommentAnchors = rSearchOpt.searchString.indexOf( CH_TXTATR_INWORD ) == -1;
+
+ if ( bRegSearch )
+ {
+ if ( -1 != rSearchOpt.searchString.indexOf("\\xAD")
+ || -1 != rSearchOpt.searchString.indexOf("\\x{00AD}")
+ || -1 != rSearchOpt.searchString.indexOf("\\u00AD")
+ || -1 != rSearchOpt.searchString.indexOf("\\U000000AD")
+ || -1 != rSearchOpt.searchString.indexOf("\\N{SOFT HYPHEN}"))
+ {
+ bRemoveSoftHyphens = false;
+ }
+ }
+ else
+ {
+ if ( 1 == rSearchOpt.searchString.getLength() &&
+ CHAR_SOFTHYPHEN == rSearchOpt.searchString.toChar() )
+ bRemoveSoftHyphens = false;
+ }
+
+ if( bSrchForward )
+ sCleanStr = lcl_CleanStr(*pNode, pFrame, pLayout, nStart, nEnd,
+ aFltArr, bRemoveSoftHyphens, bRemoveCommentAnchors);
+ else
+ sCleanStr = lcl_CleanStr(*pNode, pFrame, pLayout, nEnd, nStart,
+ aFltArr, bRemoveSoftHyphens, bRemoveCommentAnchors);
+
+ std::unique_ptr<SwScriptIterator> pScriptIter;
+ sal_uInt16 nSearchScript = 0;
+ sal_uInt16 nCurrScript = 0;
+
+ if (SearchAlgorithms2::APPROXIMATE == rSearchOpt.AlgorithmType2)
+ {
+ pScriptIter.reset(new SwScriptIterator(sCleanStr, nStart.GetAnyIndex(), bSrchForward));
+ nSearchScript = g_pBreakIt->GetRealScriptOfText( rSearchOpt.searchString, 0 );
+ }
+
+ const AmbiguousIndex nStringEnd = nEnd;
+ bool bZeroMatch = false; // zero-length match, i.e. only $ anchor as regex
+ while ( ((bSrchForward && nStart < nStringEnd) ||
+ (!bSrchForward && nStringEnd < nStart)) && !bZeroMatch )
+ {
+ // SearchAlgorithms_APPROXIMATE works on a per word base so we have to
+ // provide the text searcher with the correct locale, because it uses
+ // the break-iterator
+ if ( pScriptIter )
+ {
+ nEnd.GetAnyIndex() = pScriptIter->GetScriptChgPos();
+ nCurrScript = pScriptIter->GetCurrScript();
+ if ( nSearchScript == nCurrScript )
+ {
+ const LanguageType eCurrLang = pLayout
+ ? pFrame->GetLangOfChar(bSrchForward
+ ? nStart.GetFrameIndex()
+ : nEnd.GetFrameIndex(),
+ 0, true)
+ : pNode->GetLang(bSrchForward
+ ? nStart.GetModelIndex()
+ : nEnd.GetModelIndex());
+
+ if ( eCurrLang != eLastLang )
+ {
+ const lang::Locale aLocale(
+ g_pBreakIt->GetLocale( eCurrLang ) );
+ rSText.SetLocale( utl::TextSearch::UpgradeToSearchOptions2( rSearchOpt), aLocale );
+ eLastLang = eCurrLang;
+ }
+ }
+ pScriptIter->Next();
+ }
+ AmbiguousIndex nProxyStart = nStart;
+ AmbiguousIndex nProxyEnd = nEnd;
+ if( nSearchScript == nCurrScript &&
+ (rSText.*fnMove.fnSearch)( sCleanStr, &nProxyStart.GetAnyIndex(), &nProxyEnd.GetAnyIndex(), nullptr) &&
+ !(bZeroMatch = (nProxyStart == nProxyEnd)))
+ {
+ nStart = nProxyStart;
+ nEnd = nProxyEnd;
+ // set section correctly
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ rSearchPam.SetMark();
+
+ // adjust start and end
+ if( !aFltArr.empty() )
+ {
+ // if backward search, switch positions temporarily
+ if (!bSrchForward) { std::swap(nStart, nEnd); }
+
+ AmbiguousIndex nNew = nStart;
+ for (size_t n = 0; n < aFltArr.size() && aFltArr[ n ] <= nStart; ++n )
+ {
+ ++nNew.GetAnyIndex();
+ }
+
+ nStart = nNew;
+ nNew = nEnd;
+ for( size_t n = 0; n < aFltArr.size() && aFltArr[ n ] < nEnd; ++n )
+ {
+ ++nNew.GetAnyIndex();
+ }
+
+ nEnd = nNew;
+ // if backward search, switch positions temporarily
+ if( !bSrchForward ) { std::swap(nStart, nEnd); }
+ }
+ if (pLayout)
+ {
+ *rSearchPam.GetMark() = pFrame->MapViewToModelPos(nStart.GetFrameIndex());
+ *rSearchPam.GetPoint() = pFrame->MapViewToModelPos(nEnd.GetFrameIndex());
+ }
+ else
+ {
+ rSearchPam.GetMark()->nContent = nStart.GetModelIndex();
+ rSearchPam.GetPoint()->nContent = nEnd.GetModelIndex();
+ }
+
+ // if backward search, switch point and mark
+ if( !bSrchForward )
+ rSearchPam.Exchange();
+ bFound = true;
+ break;
+ }
+ else
+ {
+ nEnd = nProxyEnd;
+ }
+ nStart = nEnd;
+ }
+
+ pScriptIter.reset();
+
+ if ( bFound )
+ return true;
+ else if ((bChkEmptyPara && !nStart.GetAnyIndex() && !nTextLen.GetAnyIndex())
+ || bChkParaEnd)
+ {
+ *rSearchPam.GetPoint() = *pPam->GetPoint();
+ if (pLayout)
+ {
+ *rSearchPam.GetPoint() = pFrame->MapViewToModelPos(
+ bChkParaEnd ? nTextLen.GetFrameIndex() : TextFrameIndex(0));
+ }
+ else
+ {
+ rSearchPam.GetPoint()->nContent = bChkParaEnd ? nTextLen.GetModelIndex() : 0;
+ }
+ rSearchPam.SetMark();
+ const SwNode *const pSttNd = bSrchForward
+ ? &rSearchPam.GetPoint()->nNode.GetNode() // end of the frame
+ : &rNdIdx.GetNode(); // keep the bug as-is for now...
+ /* FIXME: this condition does not work for !bSrchForward backward
+ * search, it probably never did. (pSttNd != &rNdIdx.GetNode())
+ * is never true in this case. */
+ if( (bSrchForward || pSttNd != &rNdIdx.GetNode()) &&
+ rSearchPam.Move(fnMoveForward, GoInContent) &&
+ (!bSrchForward || pSttNd != &rSearchPam.GetPoint()->nNode.GetNode()) &&
+ SwNodeOffset(1) == abs(rSearchPam.GetPoint()->nNode.GetIndex() -
+ rSearchPam.GetMark()->nNode.GetIndex()))
+ {
+ // if backward search, switch point and mark
+ if( !bSrchForward )
+ rSearchPam.Exchange();
+ return true;
+ }
+ }
+ return bFound;
+}
+
+namespace {
+
+/// parameters for search and replace in text
+struct SwFindParaText : public SwFindParas
+{
+ const i18nutil::SearchOptions2& m_rSearchOpt;
+ SwCursor& m_rCursor;
+ SwRootFrame const* m_pLayout;
+ utl::TextSearch m_aSText;
+ bool m_bReplace;
+ bool m_bSearchInNotes;
+
+ SwFindParaText(const i18nutil::SearchOptions2& rOpt, bool bSearchInNotes,
+ bool bRepl, SwCursor& rCursor, SwRootFrame const*const pLayout)
+ : m_rSearchOpt( rOpt )
+ , m_rCursor( rCursor )
+ , m_pLayout(pLayout)
+ , m_aSText( utl::TextSearch::UpgradeToSearchOptions2(rOpt) )
+ , m_bReplace( bRepl )
+ , m_bSearchInNotes( bSearchInNotes )
+ {}
+ virtual int DoFind(SwPaM &, SwMoveFnCollection const &, const SwPaM &, bool bInReadOnly, std::unique_ptr<SvxSearchItem>& xSearchItem) override;
+ virtual bool IsReplaceMode() const override;
+ virtual ~SwFindParaText();
+};
+
+}
+
+SwFindParaText::~SwFindParaText()
+{
+}
+
+int SwFindParaText::DoFind(SwPaM & rCursor, SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly,
+ std::unique_ptr<SvxSearchItem>& xSearchItem)
+{
+ if( bInReadOnly && m_bReplace )
+ bInReadOnly = false;
+
+ const bool bFnd = sw::FindTextImpl(rCursor, m_rSearchOpt, m_bSearchInNotes,
+ m_aSText, fnMove, rRegion, bInReadOnly, m_pLayout, xSearchItem);
+
+ if( bFnd && m_bReplace ) // replace string
+ {
+ // use replace method in SwDoc
+ const bool bRegExp(SearchAlgorithms2::REGEXP == m_rSearchOpt.AlgorithmType2);
+ SwIndex& rSttCntIdx = rCursor.Start()->nContent;
+ const sal_Int32 nSttCnt = rSttCntIdx.GetIndex();
+ // add to shell-cursor-ring so that the regions will be moved eventually
+ SwPaM* pPrev(nullptr);
+ if( bRegExp )
+ {
+ pPrev = const_cast<SwPaM&>(rRegion).GetPrev();
+ const_cast<SwPaM&>(rRegion).GetRingContainer().merge( m_rCursor.GetRingContainer() );
+ }
+
+ std::optional<OUString> xRepl;
+ if (bRegExp)
+ xRepl = sw::ReplaceBackReferences(m_rSearchOpt, &rCursor, m_pLayout);
+ bool const bReplaced = sw::ReplaceImpl(rCursor,
+ xRepl ? *xRepl : m_rSearchOpt.replaceString,
+ bRegExp, m_rCursor.GetDoc(), m_pLayout);
+
+ m_rCursor.SaveTableBoxContent( rCursor.GetPoint() );
+
+ if( bRegExp )
+ {
+ // and remove region again
+ SwPaM* p;
+ SwPaM* pNext(const_cast<SwPaM*>(&rRegion));
+ do {
+ p = pNext;
+ pNext = p->GetNext();
+ p->MoveTo(const_cast<SwPaM*>(&rRegion));
+ } while( p != pPrev );
+ }
+ if (bRegExp && !bReplaced)
+ { // fdo#80715 avoid infinite loop if join failed
+ bool bRet = ((&fnMoveForward == &fnMove) ? &GoNextPara : &GoPrevPara)
+ (rCursor, fnMove);
+ (void) bRet;
+ assert(bRet); // if join failed, next node must be SwTextNode
+ }
+ else
+ rCursor.Start()->nContent = nSttCnt;
+ return FIND_NO_RING;
+ }
+ return bFnd ? FIND_FOUND : FIND_NOT_FOUND;
+}
+
+bool SwFindParaText::IsReplaceMode() const
+{
+ return m_bReplace;
+}
+
+sal_uLong SwCursor::Find_Text( const i18nutil::SearchOptions2& rSearchOpt, bool bSearchInNotes,
+ SwDocPositions nStart, SwDocPositions nEnd,
+ bool& bCancel, FindRanges eFndRngs, bool bReplace,
+ SwRootFrame const*const pLayout)
+{
+ // switch off OLE-notifications
+ SwDoc& rDoc = GetDoc();
+ Link<bool,void> aLnk( rDoc.GetOle2Link() );
+ rDoc.SetOle2Link( Link<bool,void>() );
+
+ bool const bStartUndo = rDoc.GetIDocumentUndoRedo().DoesUndo() && bReplace;
+ if (bStartUndo)
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REPLACE, nullptr );
+ }
+
+ bool bSearchSel = 0 != (rSearchOpt.searchFlag & SearchFlags::REG_NOT_BEGINOFLINE);
+ if( bSearchSel )
+ eFndRngs = static_cast<FindRanges>(eFndRngs | FindRanges::InSel);
+ SwFindParaText aSwFindParaText(rSearchOpt, bSearchInNotes, bReplace, *this, pLayout);
+ sal_uLong nRet = FindAll( aSwFindParaText, nStart, nEnd, eFndRngs, bCancel );
+ rDoc.SetOle2Link( aLnk );
+ if( nRet && bReplace )
+ rDoc.getIDocumentState().SetModified();
+
+ if (bStartUndo)
+ {
+ SwRewriter rewriter(MakeUndoReplaceRewriter(
+ nRet, rSearchOpt.searchString, rSearchOpt.replaceString));
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REPLACE, & rewriter );
+ }
+ return nRet;
+}
+
+namespace sw {
+
+bool ReplaceImpl(
+ SwPaM & rCursor,
+ OUString const& rReplacement,
+ bool const bRegExp,
+ SwDoc & rDoc,
+ SwRootFrame const*const pLayout)
+{
+ bool bReplaced(true);
+ IDocumentContentOperations & rIDCO(rDoc.getIDocumentContentOperations());
+#if 0
+ // FIXME there's some problem with multiple redlines here on Undo
+ std::vector<std::shared_ptr<SwUnoCursor>> ranges;
+ if (rDoc.getIDocumentRedlineAccess().IsRedlineOn()
+ || !pLayout
+ || !pLayout->IsHideRedlines()
+ || sw::GetRanges(ranges, rDoc, rCursor))
+ {
+ bReplaced = rIDCO.ReplaceRange(rCursor, rReplacement, bRegExp);
+ }
+ else
+ {
+ assert(!ranges.empty());
+ assert(ranges.front()->GetPoint()->nNode == ranges.front()->GetMark()->nNode);
+ bReplaced = rIDCO.ReplaceRange(*ranges.front(), rReplacement, bRegExp);
+ for (auto it = ranges.begin() + 1; it != ranges.end(); ++it)
+ {
+ bReplaced &= rIDCO.DeleteAndJoin(**it);
+ }
+ }
+#else
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+ if (pLayout && pLayout->IsHideRedlines()
+ && !rIDRA.IsRedlineOn() // otherwise: ReplaceRange will handle it
+ && (rIDRA.GetRedlineFlags() & RedlineFlags::ShowDelete)) // otherwise: ReplaceRange will DeleteRedline()
+ {
+ SwRedlineTable::size_type tmp;
+ rIDRA.GetRedline(*rCursor.Start(), &tmp);
+ while (tmp < rIDRA.GetRedlineTable().size())
+ {
+ SwRangeRedline const*const pRedline(rIDRA.GetRedlineTable()[tmp]);
+ if (*rCursor.End() <= *pRedline->Start())
+ {
+ break;
+ }
+ if (*pRedline->End() <= *rCursor.Start())
+ {
+ ++tmp;
+ continue;
+ }
+ if (pRedline->GetType() == RedlineType::Delete)
+ {
+ assert(*pRedline->Start() != *pRedline->End());
+ // search in hidden layout can't overlap redlines
+ assert(*rCursor.Start() <= *pRedline->Start() && *pRedline->End() <= *rCursor.End());
+ SwPaM pam(*pRedline, nullptr);
+ bReplaced &= rIDCO.DeleteAndJoin(pam);
+ }
+ else
+ {
+ ++tmp;
+ }
+ }
+ }
+ bReplaced &= rIDCO.ReplaceRange(rCursor, rReplacement, bRegExp);
+#endif
+ return bReplaced;
+}
+
+std::optional<OUString> ReplaceBackReferences(const i18nutil::SearchOptions2& rSearchOpt,
+ SwPaM *const pPam, SwRootFrame const*const pLayout)
+{
+ std::optional<OUString> xRet;
+ if( pPam && pPam->HasMark() &&
+ SearchAlgorithms2::REGEXP == rSearchOpt.AlgorithmType2 )
+ {
+ SwContentNode const*const pTextNode = pPam->GetContentNode();
+ SwContentNode const*const pMarkTextNode = pPam->GetContentNode(false);
+ if (!pTextNode || !pTextNode->IsTextNode()
+ || !pMarkTextNode || !pMarkTextNode->IsTextNode())
+ {
+ return xRet;
+ }
+ SwTextFrame const*const pFrame(pLayout
+ ? static_cast<SwTextFrame const*>(pTextNode->getLayoutFrame(pLayout))
+ : nullptr);
+ const bool bParaEnd = rSearchOpt.searchString == "$" || rSearchOpt.searchString == "^$" || rSearchOpt.searchString == "$^";
+ if (bParaEnd || (pLayout
+ ? sw::FrameContainsNode(*pFrame, pPam->GetMark()->nNode.GetIndex())
+ : pTextNode == pMarkTextNode))
+ {
+ utl::TextSearch aSText( utl::TextSearch::UpgradeToSearchOptions2( rSearchOpt) );
+ SearchResult aResult;
+ OUString aReplaceStr( rSearchOpt.replaceString );
+ if (bParaEnd)
+ {
+ OUString const aStr("\\n");
+ aResult.subRegExpressions = 1;
+ aResult.startOffset = { 0 };
+ aResult.endOffset = { aStr.getLength() };
+ aSText.ReplaceBackReferences( aReplaceStr, aStr, aResult );
+ xRet = aReplaceStr;
+ }
+ else
+ {
+ AmbiguousIndex nStart;
+ AmbiguousIndex nEnd;
+ if (pLayout)
+ {
+ nStart.SetFrameIndex(pFrame->MapModelToViewPos(*pPam->Start()));
+ nEnd.SetFrameIndex(pFrame->MapModelToViewPos(*pPam->End()));
+ }
+ else
+ {
+ nStart.SetModelIndex(pPam->Start()->nContent.GetIndex());
+ nEnd.SetModelIndex(pPam->End()->nContent.GetIndex());
+ }
+ std::vector<AmbiguousIndex> aFltArr;
+ OUString const aStr = lcl_CleanStr(*pTextNode->GetTextNode(), pFrame, pLayout,
+ nStart, nEnd, aFltArr, false, false);
+ if (aSText.SearchForward(aStr, &nStart.GetAnyIndex(), &nEnd.GetAnyIndex(), &aResult))
+ {
+ aSText.ReplaceBackReferences( aReplaceStr, aStr, aResult );
+ xRet = aReplaceStr;
+ }
+ }
+ }
+ }
+ return xRet;
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/overlayrangesoutline.cxx b/sw/source/core/crsr/overlayrangesoutline.cxx
new file mode 100644
index 000000000..aff0f9d8b
--- /dev/null
+++ b/sw/source/core/crsr/overlayrangesoutline.cxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include "overlayrangesoutline.hxx"
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+
+namespace
+{
+ // combine ranges geometrically to a single, ORed polygon
+ basegfx::B2DPolyPolygon impCombineRangesToPolyPolygon(const std::vector< basegfx::B2DRange >& rRanges)
+ {
+ const sal_uInt32 nCount(rRanges.size());
+ basegfx::B2DPolyPolygon aRetval;
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const basegfx::B2DPolygon aDiscretePolygon(basegfx::utils::createPolygonFromRect(rRanges[a]));
+
+ if(0 == a)
+ {
+ aRetval.append(aDiscretePolygon);
+ }
+ else
+ {
+ aRetval = basegfx::utils::solvePolygonOperationOr(aRetval, basegfx::B2DPolyPolygon(aDiscretePolygon));
+ }
+ }
+
+ return aRetval;
+ }
+}
+
+namespace sw::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayRangesOutline::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ const sal_uInt32 nCount(getRanges().size());
+
+ if( nCount )
+ {
+ const basegfx::BColor aRGBColor(getBaseColor().getBColor());
+ const basegfx::B2DPolyPolygon aPolyPolygon(impCombineRangesToPolyPolygon(getRanges()));
+ const drawinglayer::primitive2d::Primitive2DReference aOutline(
+ new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
+ aPolyPolygon,
+ aRGBColor));
+
+ aRetval.resize(1);
+ aRetval[0] = aOutline;
+ }
+
+ return aRetval;
+ }
+
+ OverlayRangesOutline::OverlayRangesOutline(
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges )
+ : sdr::overlay::OverlayObject(rColor)
+ , maRanges(std::move(rRanges))
+ {
+ // no AA for highlight overlays
+ allowAntiAliase(false);
+ }
+
+ OverlayRangesOutline::~OverlayRangesOutline()
+ {
+ if( getOverlayManager() )
+ {
+ getOverlayManager()->remove(*this);
+ }
+ }
+
+ void OverlayRangesOutline::setRanges(std::vector< basegfx::B2DRange >&& rNew)
+ {
+ if(rNew != maRanges)
+ {
+ maRanges = std::move(rNew);
+ objectChange();
+ }
+ }
+} // end of namespace sw::overlay
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/overlayrangesoutline.hxx b/sw/source/core/crsr/overlayrangesoutline.hxx
new file mode 100644
index 000000000..2eb053c72
--- /dev/null
+++ b/sw/source/core/crsr/overlayrangesoutline.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_CRSR_OVERLAYRANGESOUTLINE_HXX
+#define INCLUDED_SW_SOURCE_CORE_CRSR_OVERLAYRANGESOUTLINE_HXX
+
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+#include <vector>
+
+namespace sw::overlay
+ {
+ class OverlayRangesOutline : public sdr::overlay::OverlayObject
+ {
+ // geometry of overlay
+ std::vector< basegfx::B2DRange > maRanges;
+
+ // geometry creation for OverlayObject
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+ public:
+ OverlayRangesOutline(
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges );
+
+ virtual ~OverlayRangesOutline() override;
+
+ // data read access
+ const std::vector< basegfx::B2DRange >& getRanges() const
+ {
+ return maRanges;
+ }
+
+ // data write access
+ void setRanges(std::vector< basegfx::B2DRange >&& rNew);
+ };
+
+} // end of namespace sw::overlay
+
+#endif // INCLUDED_SW_SOURCE_CORE_CRSR_OVERLAYRANGESOUTLINE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/pam.cxx b/sw/source/core/crsr/pam.cxx
new file mode 100644
index 000000000..78bdbe365
--- /dev/null
+++ b/sw/source/core/crsr/pam.cxx
@@ -0,0 +1,1210 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/gen.hxx>
+#include <editeng/protitem.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <cntfrm.hxx>
+#include <pagefrm.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <pamtyp.hxx>
+#include <txtfrm.hxx>
+#include <fmtcntnt.hxx>
+#include <frmatr.hxx>
+#include <flyfrm.hxx>
+#include <fmteiro.hxx>
+#include <section.hxx>
+#include <sectfrm.hxx>
+#include <ndtxt.hxx>
+#include <swcrsr.hxx>
+
+#include <IMark.hxx>
+#include <DocumentSettingManager.hxx>
+#include <hints.hxx>
+#include <txatbase.hxx>
+#include <osl/diagnose.h>
+#include <xmloff/odffields.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <editsh.hxx>
+#include <textcontentcontrol.hxx>
+
+// for the dump "MSC-" compiler
+static sal_Int32 GetSttOrEnd( bool bCondition, const SwContentNode& rNd )
+{
+ return bCondition ? 0 : rNd.Len();
+}
+
+SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwIndex & rContent )
+ : nNode( rNodeIndex ), nContent( rContent )
+{
+}
+
+SwPosition::SwPosition( const SwNodeIndex & rNodeIndex )
+ : nNode( rNodeIndex ), nContent( nNode.GetNode().GetContentNode() )
+{
+}
+
+SwPosition::SwPosition( const SwNode& rNode )
+ : nNode( rNode ), nContent( nNode.GetNode().GetContentNode() )
+{
+}
+
+SwPosition::SwPosition( SwContentNode & rNode, const sal_Int32 nOffset )
+ : nNode( rNode ), nContent( &rNode, nOffset )
+{
+}
+
+bool SwPosition::operator<(const SwPosition &rPos) const
+{
+ if( nNode < rPos.nNode )
+ return true;
+ if( nNode == rPos.nNode )
+ {
+ // note that positions with text node but no SwIndex registered are
+ // created for text frames anchored at para (see SwXFrame::getAnchor())
+ SwIndexReg const*const pThisReg(nContent.GetIdxReg());
+ SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
+ if (pThisReg && pOtherReg)
+ {
+ return (nContent < rPos.nContent);
+ }
+ else // by convention position with no index is smaller
+ {
+ return pOtherReg != nullptr;
+ }
+ }
+ return false;
+}
+
+bool SwPosition::operator>(const SwPosition &rPos) const
+{
+ if(nNode > rPos.nNode )
+ return true;
+ if( nNode == rPos.nNode )
+ {
+ // note that positions with text node but no SwIndex registered are
+ // created for text frames anchored at para (see SwXFrame::getAnchor())
+ SwIndexReg const*const pThisReg(nContent.GetIdxReg());
+ SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
+ if (pThisReg && pOtherReg)
+ {
+ return (nContent > rPos.nContent);
+ }
+ else // by convention position with no index is smaller
+ {
+ return pThisReg != nullptr;
+ }
+ }
+ return false;
+}
+
+bool SwPosition::operator<=(const SwPosition &rPos) const
+{
+ if(nNode < rPos.nNode )
+ return true;
+ if( nNode == rPos.nNode )
+ {
+ // note that positions with text node but no SwIndex registered are
+ // created for text frames anchored at para (see SwXFrame::getAnchor())
+ SwIndexReg const*const pThisReg(nContent.GetIdxReg());
+ SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
+ if (pThisReg && pOtherReg)
+ {
+ return (nContent <= rPos.nContent);
+ }
+ else // by convention position with no index is smaller
+ {
+ return pThisReg == nullptr;
+ }
+ }
+ return false;
+}
+
+bool SwPosition::operator>=(const SwPosition &rPos) const
+{
+ if(nNode > rPos.nNode )
+ return true;
+ if( nNode == rPos.nNode )
+ {
+ // note that positions with text node but no SwIndex registered are
+ // created for text frames anchored at para (see SwXFrame::getAnchor())
+ SwIndexReg const*const pThisReg(nContent.GetIdxReg());
+ SwIndexReg const*const pOtherReg(rPos.nContent.GetIdxReg());
+ if (pThisReg && pOtherReg)
+ {
+ return (nContent >= rPos.nContent);
+ }
+ else // by convention position with no index is smaller
+ {
+ return pOtherReg == nullptr;
+ }
+ }
+ return false;
+}
+
+bool SwPosition::operator==(const SwPosition &rPos) const
+{
+ return (nNode == rPos.nNode)
+ && (nContent == rPos.nContent);
+}
+
+bool SwPosition::operator!=(const SwPosition &rPos) const
+{
+ return (nNode != rPos.nNode)
+ || (nContent != rPos.nContent);
+}
+
+SwDoc& SwPosition::GetDoc() const
+{
+ return nNode.GetNode().GetDoc();
+}
+
+void SwPosition::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPosition"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nNode"), BAD_CAST(OString::number(sal_Int32(nNode.GetIndex())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nContent"), BAD_CAST(OString::number(nContent.GetIndex()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+std::ostream &operator <<(std::ostream& s, const SwPosition& position)
+{
+ return s << "SwPosition (node " << position.nNode.GetIndex() << ", offset " << position.nContent.GetIndex() << ")";
+}
+
+namespace {
+
+enum CHKSECTION { Chk_Both, Chk_One, Chk_None };
+
+}
+
+static CHKSECTION lcl_TstIdx( SwNodeOffset nSttIdx, SwNodeOffset nEndIdx, const SwNode& rEndNd )
+{
+ SwNodeOffset nStt = rEndNd.StartOfSectionIndex(), nEnd = rEndNd.GetIndex();
+ CHKSECTION eSec = nStt < nSttIdx && nEnd >= nSttIdx ? Chk_One : Chk_None;
+ if( nStt < nEndIdx && nEnd >= nEndIdx )
+ return( eSec == Chk_One ? Chk_Both : Chk_One );
+ return eSec;
+}
+
+static bool lcl_ChkOneRange( CHKSECTION eSec, bool bChkSections,
+ const SwNode& rBaseEnd, SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ if( eSec != Chk_Both )
+ return false;
+
+ if( !bChkSections )
+ return true;
+
+ // search the surrounding section
+ const SwNodes& rNds = rBaseEnd.GetNodes();
+ const SwNode *pTmp, *pNd = rNds[ nStt ];
+ if( !pNd->IsStartNode() )
+ pNd = pNd->StartOfSectionNode();
+
+ if( pNd == rNds[ nEnd ]->StartOfSectionNode() )
+ return true; // same StartNode, same section
+
+ // already on a base node => error
+ if( !pNd->StartOfSectionIndex() )
+ return false;
+
+ for (;;)
+ {
+ pTmp = pNd->StartOfSectionNode();
+ if (pTmp->EndOfSectionNode() == &rBaseEnd )
+ break;
+ pNd = pTmp;
+ }
+
+ SwNodeOffset nSttIdx = pNd->GetIndex(), nEndIdx = pNd->EndOfSectionIndex();
+ return nSttIdx <= nStt && nStt <= nEndIdx &&
+ nSttIdx <= nEnd && nEnd <= nEndIdx;
+}
+
+/** Check if the given range is inside one of the defined top-level sections.
+ *
+ * The top-level sections are Content, AutoText, PostIts, Inserts, and Redlines.
+ *
+ * @param bChkSection if true, also check that the given range is inside
+ * a single second-level section inside any of the
+ * top-level sections, except for the Content section.
+ *
+ * @return <true> if valid range
+ */
+bool CheckNodesRange( const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd, bool bChkSection )
+{
+ const SwNodes& rNds = rStt.GetNodes();
+ SwNodeOffset nStt = rStt.GetIndex(), nEnd = rEnd.GetIndex();
+ CHKSECTION eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfContent() );
+ if( Chk_None != eSec )
+ return eSec == Chk_Both;
+
+ eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfAutotext() );
+ if( Chk_None != eSec )
+ return lcl_ChkOneRange( eSec, bChkSection,
+ rNds.GetEndOfAutotext(), nStt, nEnd );
+
+ eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfPostIts() );
+ if( Chk_None != eSec )
+ return lcl_ChkOneRange( eSec, bChkSection,
+ rNds.GetEndOfPostIts(), nStt, nEnd );
+
+ eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfInserts() );
+ if( Chk_None != eSec )
+ return lcl_ChkOneRange( eSec, bChkSection,
+ rNds.GetEndOfInserts(), nStt, nEnd );
+
+ eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfRedlines() );
+ if( Chk_None != eSec )
+ return lcl_ChkOneRange( eSec, bChkSection,
+ rNds.GetEndOfRedlines(), nStt, nEnd );
+
+ return false; // somewhere in between => error
+}
+
+bool GoNext(SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode )
+{
+ if( pNd->IsContentNode() )
+ return static_cast<SwContentNode*>(pNd)->GoNext( pIdx, nMode );
+ return false;
+}
+
+bool GoPrevious( SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode )
+{
+ if( pNd->IsContentNode() )
+ return static_cast<SwContentNode*>(pNd)->GoPrevious( pIdx, nMode );
+ return false;
+}
+
+SwContentNode* GoNextNds( SwNodeIndex* pIdx, bool bChk )
+{
+ SwNodeIndex aIdx( *pIdx );
+ SwContentNode* pNd = aIdx.GetNodes().GoNext( &aIdx );
+ if( pNd )
+ {
+ if( bChk && SwNodeOffset(1) != aIdx.GetIndex() - pIdx->GetIndex() &&
+ !CheckNodesRange( *pIdx, aIdx, true ) )
+ pNd = nullptr;
+ else
+ *pIdx = aIdx;
+ }
+ return pNd;
+}
+
+SwContentNode* GoPreviousNds( SwNodeIndex * pIdx, bool bChk )
+{
+ SwNodeIndex aIdx( *pIdx );
+ SwContentNode* pNd = SwNodes::GoPrevious( &aIdx );
+ if( pNd )
+ {
+ if( bChk && SwNodeOffset(1) != pIdx->GetIndex() - aIdx.GetIndex() &&
+ !CheckNodesRange( *pIdx, aIdx, true ) )
+ pNd = nullptr;
+ else
+ *pIdx = aIdx;
+ }
+ return pNd;
+}
+
+SwPaM::SwPaM( const SwPosition& rPos, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rPos )
+ , m_Bound2( rPos.nNode.GetNode().GetNodes() ) // default initialize
+ , m_pPoint( &m_Bound1 )
+ , m_pMark( m_pPoint )
+ , m_bIsInFrontOfLabel( false )
+{
+}
+
+SwPaM::SwPaM( const SwPosition& rMark, const SwPosition& rPoint, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rMark )
+ , m_Bound2( rPoint )
+ , m_pPoint( &m_Bound2 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+}
+
+SwPaM::SwPaM( const SwNodeIndex& rMark, const SwNodeIndex& rPoint,
+ SwNodeOffset nMarkOffset, SwNodeOffset nPointOffset, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rMark )
+ , m_Bound2( rPoint )
+ , m_pPoint( &m_Bound2 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+ if ( nMarkOffset )
+ {
+ m_pMark->nNode += nMarkOffset;
+ }
+ if ( nPointOffset )
+ {
+ m_pPoint->nNode += nPointOffset;
+ }
+ m_Bound1.nContent.Assign( m_Bound1.nNode.GetNode().GetContentNode(), 0 );
+ m_Bound2.nContent.Assign( m_Bound2.nNode.GetNode().GetContentNode(), 0 );
+}
+
+SwPaM::SwPaM( const SwNode& rMark, const SwNode& rPoint,
+ SwNodeOffset nMarkOffset, SwNodeOffset nPointOffset, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rMark )
+ , m_Bound2( rPoint )
+ , m_pPoint( &m_Bound2 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+ if ( nMarkOffset )
+ {
+ m_pMark->nNode += nMarkOffset;
+ }
+ if ( nPointOffset )
+ {
+ m_pPoint->nNode += nPointOffset;
+ }
+ m_Bound1.nContent.Assign( m_Bound1.nNode.GetNode().GetContentNode(), 0 );
+ m_Bound2.nContent.Assign( m_Bound2.nNode.GetNode().GetContentNode(), 0 );
+}
+
+SwPaM::SwPaM( const SwNodeIndex& rMark, sal_Int32 nMarkContent,
+ const SwNodeIndex& rPoint, sal_Int32 nPointContent, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rMark )
+ , m_Bound2( rPoint )
+ , m_pPoint( &m_Bound2 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+ m_pPoint->nContent.Assign( rPoint.GetNode().GetContentNode(), nPointContent);
+ m_pMark ->nContent.Assign( rMark .GetNode().GetContentNode(), nMarkContent );
+}
+
+SwPaM::SwPaM( const SwNode& rMark, sal_Int32 nMarkContent,
+ const SwNode& rPoint, sal_Int32 nPointContent, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rMark )
+ , m_Bound2( rPoint )
+ , m_pPoint( &m_Bound2 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+ m_pPoint->nContent.Assign( m_pPoint->nNode.GetNode().GetContentNode(),
+ nPointContent);
+ m_pMark ->nContent.Assign( m_pMark ->nNode.GetNode().GetContentNode(),
+ nMarkContent );
+}
+
+SwPaM::SwPaM( const SwNode& rNode, sal_Int32 nContent, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rNode )
+ , m_Bound2( m_Bound1.nNode.GetNode().GetNodes() ) // default initialize
+ , m_pPoint( &m_Bound1 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+ m_pPoint->nContent.Assign( m_pPoint->nNode.GetNode().GetContentNode(),
+ nContent );
+}
+
+SwPaM::SwPaM( const SwNodeIndex& rNodeIdx, sal_Int32 nContent, SwPaM* pRing )
+ : Ring( pRing )
+ , m_Bound1( rNodeIdx )
+ , m_Bound2( rNodeIdx.GetNode().GetNodes() ) // default initialize
+ , m_pPoint( &m_Bound1 )
+ , m_pMark( &m_Bound1 )
+ , m_bIsInFrontOfLabel( false )
+{
+ m_pPoint->nContent.Assign( rNodeIdx.GetNode().GetContentNode(), nContent );
+}
+
+SwPaM::~SwPaM() {}
+
+SwPaM::SwPaM(SwPaM const& rPam, SwPaM *const pRing)
+ : Ring(pRing)
+ , m_Bound1( *(rPam.m_pPoint) )
+ , m_Bound2( *(rPam.m_pMark) )
+ , m_pPoint( &m_Bound1 ), m_pMark( rPam.HasMark() ? &m_Bound2 : m_pPoint )
+ , m_bIsInFrontOfLabel( false )
+{
+}
+
+// @@@ semantic: no copy assignment for super class Ring.
+SwPaM &SwPaM::operator=( const SwPaM &rPam )
+{
+ if(this == &rPam)
+ return *this;
+
+ *m_pPoint = *( rPam.m_pPoint );
+ if ( rPam.HasMark() )
+ {
+ SetMark();
+ *m_pMark = *( rPam.m_pMark );
+ }
+ else
+ {
+ DeleteMark();
+ }
+ return *this;
+}
+
+void SwPaM::SetMark()
+{
+ if (m_pPoint == &m_Bound1)
+ {
+ m_pMark = &m_Bound2;
+ }
+ else
+ {
+ m_pMark = &m_Bound1;
+ }
+ (*m_pMark) = *m_pPoint;
+}
+
+#ifdef DBG_UTIL
+void SwPaM::Exchange()
+{
+ if (m_pPoint != m_pMark)
+ {
+ SwPosition *pTmp = m_pPoint;
+ m_pPoint = m_pMark;
+ m_pMark = pTmp;
+ }
+}
+#endif
+
+/// movement of cursor
+bool SwPaM::Move( SwMoveFnCollection const & fnMove, SwGoInDoc fnGo )
+{
+ const bool bRet = (*fnGo)( *this, fnMove );
+
+ m_bIsInFrontOfLabel = false;
+ return bRet;
+}
+
+namespace sw {
+
+/** make a new region
+
+ Sets the first SwPaM onto the given SwPaM, or to the beginning or end of a
+ document. SPoint stays at its position, GetMark will be changed respectively.
+
+ @param fnMove Contains information if beginning or end of document.
+ @param pOrigRg The given region.
+
+ @return Newly created range, in Ring with parameter pOrigRg.
+*/
+std::unique_ptr<SwPaM> MakeRegion(SwMoveFnCollection const & fnMove,
+ const SwPaM & rOrigRg)
+{
+ std::unique_ptr<SwPaM> pPam;
+ {
+ pPam.reset(new SwPaM(rOrigRg, const_cast<SwPaM*>(&rOrigRg))); // given search range
+ // make sure that SPoint is on the "real" start position
+ // FORWARD: SPoint always smaller than GetMark
+ // BACKWARD: SPoint always bigger than GetMark
+ if( (pPam->GetMark()->*fnMove.fnCmpOp)( *pPam->GetPoint() ) )
+ pPam->Exchange();
+ }
+ return pPam;
+}
+
+} // namespace sw
+
+void SwPaM::Normalize(bool bPointFirst)
+{
+ if (HasMark())
+ if ( ( bPointFirst && *m_pPoint > *m_pMark) ||
+ (!bPointFirst && *m_pPoint < *m_pMark) )
+ {
+ Exchange();
+ }
+}
+
+/// return page number at cursor (for reader and page bound frames)
+sal_uInt16 SwPaM::GetPageNum( bool bAtPoint, const Point* pLayPos )
+{
+ const SwContentFrame* pCFrame;
+ const SwPageFrame *pPg;
+ const SwContentNode *pNd ;
+ const SwPosition* pPos = bAtPoint ? m_pPoint : m_pMark;
+
+ std::pair<Point, bool> tmp;
+ if (pLayPos)
+ {
+ tmp.first = *pLayPos;
+ tmp.second = false;
+ }
+ if( nullptr != ( pNd = pPos->nNode.GetNode().GetContentNode() ) &&
+ nullptr != (pCFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), pPos, pLayPos ? &tmp : nullptr)) &&
+ nullptr != ( pPg = pCFrame->FindPageFrame() ))
+ return pPg->GetPhyPageNum();
+ return 0;
+}
+
+// form view - see also SwCursorShell::IsCursorReadonly()
+static const SwFrame* lcl_FindEditInReadonlyFrame( const SwFrame& rFrame )
+{
+ const SwFrame* pRet = nullptr;
+
+ const SwFlyFrame* pFly;
+ const SwSectionFrame* pSectionFrame;
+
+ if( rFrame.IsInFly() &&
+ (pFly = rFrame.FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() &&
+ pFly->Lower() &&
+ !pFly->Lower()->IsNoTextFrame() )
+ {
+ pRet = pFly;
+ }
+ else if ( rFrame.IsInSct() &&
+ nullptr != ( pSectionFrame = rFrame.FindSctFrame() )->GetSection() &&
+ pSectionFrame->GetSection()->IsEditInReadonlyFlag() )
+ {
+ pRet = pSectionFrame;
+ }
+
+ return pRet;
+}
+
+/// is in protected section or selection surrounds something protected
+bool SwPaM::HasReadonlySel( bool bFormView ) const
+{
+ bool bRet = false;
+
+ const SwContentNode* pNd = GetPoint()->nNode.GetNode().GetContentNode();
+ const SwContentFrame *pFrame = nullptr;
+ if ( pNd != nullptr )
+ {
+ Point aTmpPt;
+ std::pair<Point, bool> const tmp(aTmpPt, false);
+ pFrame = pNd->getLayoutFrame(
+ pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ GetPoint(), &tmp);
+ }
+
+ // Will be set if point are inside edit-in-readonly environment
+ const SwFrame* pPointEditInReadonlyFrame = nullptr;
+ if ( pFrame != nullptr
+ && ( pFrame->IsProtected()
+ || ( bFormView
+ && nullptr == ( pPointEditInReadonlyFrame = lcl_FindEditInReadonlyFrame( *pFrame ) ) ) ) )
+ {
+ bRet = true;
+ }
+ else if( pNd != nullptr )
+ {
+ const SwSectionNode* pSNd = pNd->GetSectionNode();
+ if ( pSNd != nullptr
+ && ( pSNd->GetSection().IsProtectFlag()
+ || ( bFormView
+ && !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
+ {
+ bRet = true;
+ }
+ else
+ {
+ const SwSectionNode* pParentSectionNd = pNd->FindSectionNode();
+ if ( pParentSectionNd != nullptr
+ && ( pParentSectionNd->GetSection().IsProtectFlag()
+ || ( bFormView && !pParentSectionNd->GetSection().IsEditInReadonlyFlag()) ) )
+ {
+ bRet = true;
+ }
+ }
+ }
+
+ if ( !bRet
+ && HasMark()
+ && GetPoint()->nNode != GetMark()->nNode )
+ {
+ pNd = GetMark()->nNode.GetNode().GetContentNode();
+ pFrame = nullptr;
+ if ( pNd != nullptr )
+ {
+ Point aTmpPt;
+ std::pair<Point, bool> const tmp(aTmpPt, false);
+ pFrame = pNd->getLayoutFrame(
+ pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ GetMark(), &tmp);
+ }
+
+ const SwFrame* pMarkEditInReadonlyFrame = nullptr;
+ if ( pFrame != nullptr
+ && ( pFrame->IsProtected()
+ || ( bFormView
+ && nullptr == ( pMarkEditInReadonlyFrame = lcl_FindEditInReadonlyFrame( *pFrame ) ) ) ) )
+ {
+ bRet = true;
+ }
+ else if( pNd != nullptr )
+ {
+ const SwSectionNode* pSNd = pNd->GetSectionNode();
+ if ( pSNd != nullptr
+ && ( pSNd->GetSection().IsProtectFlag()
+ || ( bFormView
+ && !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
+ {
+ bRet = true;
+ }
+ }
+
+ if ( !bRet && bFormView )
+ {
+ // Check if start and end frame are inside the _same_
+ // edit-in-readonly-environment. Otherwise we better return 'true'
+ if ( pPointEditInReadonlyFrame != pMarkEditInReadonlyFrame )
+ bRet = true;
+ }
+
+ // check for protected section inside the selection
+ if( !bRet )
+ {
+ SwNodeOffset nSttIdx = GetMark()->nNode.GetIndex(),
+ nEndIdx = GetPoint()->nNode.GetIndex();
+ if( nEndIdx <= nSttIdx )
+ {
+ SwNodeOffset nTmp = nSttIdx;
+ nSttIdx = nEndIdx;
+ nEndIdx = nTmp;
+ }
+
+ // If a protected section should be between nodes, then the
+ // selection needs to contain already x nodes.
+ // (TextNd, SectNd, TextNd, EndNd, TextNd )
+ if( nSttIdx + SwNodeOffset(3) < nEndIdx )
+ {
+ const SwSectionFormats& rFormats = GetDoc().GetSections();
+ for( SwSectionFormats::size_type n = rFormats.size(); n; )
+ {
+ const SwSectionFormat* pFormat = rFormats[ --n ];
+ if( pFormat->GetProtect().IsContentProtected() )
+ {
+ const SwFormatContent& rContent = pFormat->GetContent(false);
+ OSL_ENSURE( rContent.GetContentIdx(), "where is the SectionNode?" );
+ SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex();
+ if( nSttIdx <= nIdx && nEndIdx >= nIdx &&
+ rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ const SwDoc& rDoc = GetDoc();
+ // Legacy text/combo/checkbox: never return read-only when inside these form fields.
+ const IDocumentMarkAccess* pMarksAccess = rDoc.getIDocumentMarkAccess();
+ sw::mark::IFieldmark* pA = GetPoint() ? pMarksAccess->getFieldmarkFor( *GetPoint( ) ) : nullptr;
+ sw::mark::IFieldmark* pB = GetMark() ? pMarksAccess->getFieldmarkFor( *GetMark( ) ) : pA;
+ // prevent the user from accidentally deleting the field itself when modifying the text.
+ const bool bAtStartA = (pA != nullptr) && (pA->GetMarkStart() == *GetPoint());
+ const bool bAtStartB = (pB != nullptr) && (pB->GetMarkStart() == *GetMark());
+
+ if (officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get())
+ {
+ ; // allow editing all fields in generic mode
+ }
+ else if (!bRet)
+ {
+ bool bUnhandledMark = pA && pA->GetFieldname( ) == ODF_UNHANDLED;
+ // Unhandled fieldmarks case shouldn't be edited manually to avoid breaking anything
+ if ( ( pA == pB ) && bUnhandledMark )
+ bRet = true;
+ else
+ {
+ if ((pA == pB) && (bAtStartA != bAtStartB))
+ bRet = true;
+ else if (pA != pB)
+ {
+ // If both points are either outside or at marks edges (i.e. selection either
+ // touches fields, or fully encloses it), then don't disable editing
+ bRet = !( ( !pA || bAtStartA ) && ( !pB || bAtStartB ) );
+ }
+ if( !bRet && rDoc.GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM ) && (pA || pB) )
+ {
+ // Form protection case
+ bRet = ( pA == nullptr ) || ( pB == nullptr ) || bAtStartA || bAtStartB;
+ }
+ }
+ }
+ else
+ {
+ // Allow editing when the cursor/selection is fully inside of a legacy form field.
+ bRet = !( pA != nullptr && !bAtStartA && !bAtStartB && pA == pB );
+
+ if (bRet)
+ {
+ // Also allow editing inside content controls in general, similar to form fields.
+ // Specific types will be disabled below.
+ if (const SwEditShell* pEditShell = rDoc.GetEditShell())
+ bRet = !pEditShell->CursorInsideContentControl();
+ }
+ }
+
+ if (!bRet)
+ {
+ // Paragraph Signatures and Classification fields are read-only.
+ if (const SwEditShell* pEditShell = rDoc.GetEditShell())
+ bRet = pEditShell->IsCursorInParagraphMetadataField();
+ }
+
+ if (!bRet &&
+ rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS))
+ {
+ if (rDoc.getIDocumentMarkAccess()->isBookmarkDeleted(*this))
+ {
+ return true;
+ }
+ }
+ if (!bRet &&
+ rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FIELDS))
+ {
+ SwPosition const& rStart(*Start());
+ SwPosition const& rEnd(*End());
+ for (SwNodeIndex n = rStart.nNode; n <= rEnd.nNode; ++n)
+ {
+ if (SwTextNode const*const pNode = n.GetNode().GetTextNode())
+ {
+ if (SwpHints const*const pHints = pNode->GetpSwpHints())
+ {
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ SwTextAttr const*const pHint(pHints->Get(i));
+ if (n == rStart.nNode && pHint->GetStart() < rStart.nContent.GetIndex())
+ {
+ continue; // before selection
+ }
+ if (n == rEnd.nNode && rEnd.nContent.GetIndex() <= pHint->GetStart())
+ {
+ break; // after selection
+ }
+ if (pHint->Which() == RES_TXTATR_FIELD
+ // placeholders don't work if you can't delete them
+ && pHint->GetFormatField().GetField()->GetTyp()->Which() != SwFieldIds::JumpEdit)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!bRet)
+ {
+ // See if we're inside a read-only content control.
+ const SwPosition* pStart = Start();
+ SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ sal_Int32 nIndex = pStart->nContent.GetIndex();
+ SwTextAttr* pAttr
+ = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+ auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ if (pTextContentControl)
+ {
+ const SwFormatContentControl& rFormatContentControl
+ = pTextContentControl->GetContentControl();
+ std::shared_ptr<SwContentControl> pContentControl
+ = rFormatContentControl.GetContentControl();
+ if (pContentControl && !pContentControl->GetReadWrite())
+ {
+ bRet = pContentControl->GetCheckbox() || pContentControl->GetPicture();
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+/// This function returns the next node in direction of search. If there is no
+/// left or the next is out of the area, then a null-pointer is returned.
+/// @param rbFirst If <true> then first time request. If so than the position of
+/// the PaM must not be changed!
+SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & fnMove,
+ bool const bInReadOnly, SwRootFrame const*const i_pLayout)
+{
+ SwRootFrame const*const pLayout(i_pLayout ? i_pLayout :
+ rPam.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout());
+ SwContentNode * pNd = nullptr;
+ if( ((*rPam.GetPoint()).*fnMove.fnCmpOp)( *rPam.GetMark() ) ||
+ ( *rPam.GetPoint() == *rPam.GetMark() && rbFirst ) )
+ {
+ if( rbFirst )
+ {
+ rbFirst = false;
+ pNd = rPam.GetContentNode();
+ if( pNd )
+ {
+ SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
+ if(
+ (
+ nullptr == pFrame ||
+ ( !bInReadOnly && pFrame->IsProtected() ) ||
+ (pFrame->IsTextFrame() && static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow())
+ ) ||
+ ( !bInReadOnly && pNd->FindSectionNode() &&
+ pNd->FindSectionNode()->GetSection().IsProtect()
+ )
+ )
+ {
+ pNd = nullptr;
+ }
+ }
+ }
+
+ if( !pNd ) // is the cursor not on a ContentNode?
+ {
+ SwPosition aPos( *rPam.GetPoint() );
+ bool bSrchForward = &fnMove == &fnMoveForward;
+ SwNodes& rNodes = aPos.nNode.GetNodes();
+
+ // go to next/previous ContentNode
+ while( true )
+ {
+ if (i_pLayout && aPos.nNode.GetNode().IsTextNode())
+ {
+ auto const fal(sw::GetFirstAndLastNode(*pLayout, aPos.nNode));
+ aPos.nNode = bSrchForward ? *fal.second : *fal.first;
+ }
+
+ pNd = bSrchForward
+ ? rNodes.GoNextSection( &aPos.nNode, true, !bInReadOnly )
+ : SwNodes::GoPrevSection( &aPos.nNode, true, !bInReadOnly );
+ if( pNd )
+ {
+ aPos.nContent.Assign( pNd, ::GetSttOrEnd( bSrchForward,*pNd ));
+ // is the position still in the area
+ if( (aPos.*fnMove.fnCmpOp)( *rPam.GetMark() ) )
+ {
+ // only in AutoTextSection can be nodes that are hidden
+ SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
+ if (nullptr == pFrame ||
+ ( !bInReadOnly && pFrame->IsProtected() ) ||
+ ( pFrame->IsTextFrame() &&
+ static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow()))
+ {
+ pNd = nullptr;
+ continue;
+ }
+ *rPam.GetPoint() = aPos;
+ }
+ else
+ pNd = nullptr; // no valid node
+ break;
+ }
+ break;
+ }
+ }
+ }
+ return pNd;
+}
+
+void GoStartDoc( SwPosition * pPos )
+{
+ SwNodes& rNodes = pPos->nNode.GetNodes();
+ pPos->nNode = *rNodes.GetEndOfContent().StartOfSectionNode();
+ // we always need to find a ContentNode!
+ SwContentNode* pCNd = rNodes.GoNext( &pPos->nNode );
+ if( pCNd )
+ pCNd->MakeStartIndex( &pPos->nContent );
+}
+
+void GoEndDoc( SwPosition * pPos )
+{
+ SwNodes& rNodes = pPos->nNode.GetNodes();
+ pPos->nNode = rNodes.GetEndOfContent();
+ SwContentNode* pCNd = GoPreviousNds( &pPos->nNode, true );
+ if( pCNd )
+ pCNd->MakeEndIndex( &pPos->nContent );
+}
+
+void GoStartSection( SwPosition * pPos )
+{
+ // jump to section's beginning
+ SwNodes& rNodes = pPos->nNode.GetNodes();
+ sal_uInt16 nLevel = SwNodes::GetSectionLevel( pPos->nNode );
+ if( pPos->nNode < rNodes.GetEndOfContent().StartOfSectionIndex() )
+ nLevel--;
+ do { SwNodes::GoStartOfSection( &pPos->nNode ); } while( nLevel-- );
+
+ // already on a ContentNode
+ pPos->nNode.GetNode().GetContentNode()->MakeStartIndex( &pPos->nContent );
+}
+
+/// go to the end of the current base section
+void GoEndSection( SwPosition * pPos )
+{
+ // jump to section's beginning/end
+ SwNodes& rNodes = pPos->nNode.GetNodes();
+ sal_uInt16 nLevel = SwNodes::GetSectionLevel( pPos->nNode );
+ if( pPos->nNode < rNodes.GetEndOfContent().StartOfSectionIndex() )
+ nLevel--;
+ do { SwNodes::GoEndOfSection( &pPos->nNode ); } while( nLevel-- );
+
+ // now on an EndNode, thus to the previous ContentNode
+ if( GoPreviousNds( &pPos->nNode, true ) )
+ pPos->nNode.GetNode().GetContentNode()->MakeEndIndex( &pPos->nContent );
+}
+
+bool GoInDoc( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ (*fnMove.fnDoc)( rPam.GetPoint() );
+ return true;
+}
+
+bool GoInSection( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ (*fnMove.fnSections)( rPam.GetPoint() );
+ return true;
+}
+
+bool GoInNode( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ SwContentNode *pNd = (*fnMove.fnNds)( &rPam.GetPoint()->nNode, true );
+ if( pNd )
+ rPam.GetPoint()->nContent.Assign( pNd,
+ ::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
+ return pNd;
+}
+
+bool GoInContent( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
+ &rPam.GetPoint()->nContent, CRSR_SKIP_CHARS ))
+ return true;
+ return GoInNode( rPam, fnMove );
+}
+
+bool GoInContentCells( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
+ &rPam.GetPoint()->nContent, CRSR_SKIP_CELLS ))
+ return true;
+ return GoInNode( rPam, fnMove );
+}
+
+bool GoInContentSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
+ &rPam.GetPoint()->nContent, CRSR_SKIP_CHARS | CRSR_SKIP_HIDDEN ) )
+ return true;
+ return GoInNode( rPam, fnMove );
+}
+
+bool GoInContentCellsSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ if( (*fnMove.fnNd)( &rPam.GetPoint()->nNode.GetNode(),
+ &rPam.GetPoint()->nContent, CRSR_SKIP_CELLS | CRSR_SKIP_HIDDEN ) )
+ return true;
+ return GoInNode( rPam, fnMove );
+}
+
+bool GoPrevPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
+{
+ if( rPam.Move( fnMoveBackward, GoInNode ) )
+ {
+ // always on a ContentNode
+ SwPosition& rPos = *rPam.GetPoint();
+ SwContentNode * pNd = rPos.nNode.GetNode().GetContentNode();
+ rPos.nContent.Assign( pNd,
+ ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
+ return true;
+ }
+ return false;
+}
+
+bool GoCurrPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
+{
+ SwPosition& rPos = *rPam.GetPoint();
+ SwContentNode * pNd = rPos.nNode.GetNode().GetContentNode();
+ if( pNd )
+ {
+ const sal_Int32 nOld = rPos.nContent.GetIndex();
+ const sal_Int32 nNew = &aPosPara == &fnMoveForward ? 0 : pNd->Len();
+ // if already at beginning/end then to the next/previous
+ if( nOld != nNew )
+ {
+ rPos.nContent.Assign( pNd, nNew );
+ return true;
+ }
+ }
+ // move node to next/previous ContentNode
+ if( ( &aPosPara==&fnParaStart && nullptr != ( pNd =
+ GoPreviousNds( &rPos.nNode, true ))) ||
+ ( &aPosPara==&fnParaEnd && nullptr != ( pNd =
+ GoNextNds( &rPos.nNode, true ))) )
+ {
+ rPos.nContent.Assign( pNd,
+ ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ));
+ return true;
+ }
+ return false;
+}
+
+bool GoNextPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
+{
+ if( rPam.Move( fnMoveForward, GoInNode ) )
+ {
+ // always on a ContentNode
+ SwPosition& rPos = *rPam.GetPoint();
+ SwContentNode * pNd = rPos.nNode.GetNode().GetContentNode();
+ rPos.nContent.Assign( pNd,
+ ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
+ return true;
+ }
+ return false;
+}
+
+bool GoCurrSection( SwPaM & rPam, SwMoveFnCollection const & fnMove )
+{
+ SwPosition& rPos = *rPam.GetPoint();
+ SwPosition aSavePos( rPos ); // position for comparison
+ (fnMove.fnSection)( &rPos.nNode );
+ SwContentNode *pNd;
+ if( nullptr == ( pNd = rPos.nNode.GetNode().GetContentNode()) &&
+ nullptr == ( pNd = (*fnMove.fnNds)( &rPos.nNode, true )) )
+ {
+ rPos = aSavePos; // do not change cursor
+ return false;
+ }
+
+ rPos.nContent.Assign( pNd,
+ ::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
+ return aSavePos != rPos;
+}
+
+OUString SwPaM::GetText() const
+{
+ OUStringBuffer aResult;
+
+ SwNodeIndex aNodeIndex = Start()->nNode;
+
+ // The first node can be already the end node.
+ // Use a "forever" loop with an exit condition in the middle
+ // of its body, in order to correctly handle all cases.
+ bool bIsStartNode = true;
+ for (;;)
+ {
+ const bool bIsEndNode = aNodeIndex == End()->nNode;
+ SwTextNode * pTextNode = aNodeIndex.GetNode().GetTextNode();
+
+ if (pTextNode != nullptr)
+ {
+ if (!bIsStartNode)
+ {
+ aResult.append(CH_TXTATR_NEWLINE); // use newline for para break
+ }
+ const OUString& aTmpStr = pTextNode->GetText();
+
+ if (bIsStartNode || bIsEndNode)
+ {
+ // Handle corner cases of start/end node(s)
+ const sal_Int32 nStart = bIsStartNode
+ ? Start()->nContent.GetIndex()
+ : 0;
+ const sal_Int32 nEnd = bIsEndNode
+ ? End()->nContent.GetIndex()
+ : aTmpStr.getLength();
+
+ aResult.append(aTmpStr.subView(nStart, nEnd-nStart));
+ }
+ else
+ {
+ aResult.append(aTmpStr);
+ }
+ }
+
+ if (bIsEndNode)
+ {
+ break;
+ }
+
+ ++aNodeIndex;
+ bIsStartNode = false;
+ }
+
+ return aResult.makeStringAndClear();
+}
+
+void SwPaM::InvalidatePaM()
+{
+ for (SwNodeIndex index = Start()->nNode; index <= End()->nNode; ++index)
+ {
+ if (SwTextNode *const pTextNode = index.GetNode().GetTextNode())
+ {
+ // pretend that the PaM marks changed formatting to reformat...
+ sal_Int32 const nStart(
+ index == Start()->nNode ? Start()->nContent.GetIndex() : 0);
+ // this should work even for length of 0
+ SwUpdateAttr const aHint(
+ nStart,
+ index == End()->nNode
+ ? End()->nContent.GetIndex() - nStart
+ : pTextNode->Len() - nStart,
+ 0);
+ pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+ // other node types not invalidated
+ }
+}
+
+void SwPaM::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPaM"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
+ GetPoint()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (HasMark())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mark"));
+ GetMark()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+std::ostream &operator <<(std::ostream& s, const SwPaM& pam)
+{
+ if( pam.HasMark())
+ return s << "SwPaM (point " << *pam.GetPoint() << ", mark " << *pam.GetMark() << ")";
+ else
+ return s << "SwPaM (point " << *pam.GetPoint() << ")";
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/paminit.cxx b/sw/source/core/crsr/paminit.cxx
new file mode 100644
index 000000000..367be6e02
--- /dev/null
+++ b/sw/source/core/crsr/paminit.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <pam.hxx>
+#include <pamtyp.hxx>
+#include <cshtyp.hxx>
+
+const SwMoveFnCollection aFwrd = {
+ /* fnNd */ &GoNext,
+ /* fnNds */ &GoNextNds,
+ /* fnDoc */ &GoEndDoc,
+ /* fnSections */ &GoEndSection,
+ /* fnCmpOp */ &SwPosition::operator<,
+ /* fnGetHint */ &GetFrwrdTextHint,
+ /* fnSearch */ &utl::TextSearch::SearchForward,
+ /* fnSection */ &SwNodes::GoStartOfSection
+};
+
+const SwMoveFnCollection aBwrd = {
+ /* fnNd */ &GoPrevious,
+ /* fnNds */ &GoPreviousNds,
+ /* fnDoc */ &GoStartDoc,
+ /* fnSections */ &GoStartSection,
+ /* fnCmpOp */ &SwPosition::operator>,
+ /* fnGetHint */ &GetBkwrdTextHint,
+ /* fnSearch */ &utl::TextSearch::SearchBackward,
+ /* fnSection */ &SwNodes::GoEndOfSection
+};
+
+SwMoveFnCollection const & fnParaStart = aFwrd;
+SwMoveFnCollection const & fnParaEnd = aBwrd;
+
+SwMoveFnCollection const & fnSectionStart = aFwrd;
+SwMoveFnCollection const & fnSectionEnd = aBwrd;
+
+SwMoveFnCollection const & fnTableStart = aFwrd;
+SwMoveFnCollection const & fnTableEnd = aBwrd;
+
+SwMoveFnCollection const & fnRegionStart = aFwrd;
+SwMoveFnCollection const & fnRegionEnd = aBwrd;
+
+SwMoveFnCollection const & fnMoveBackward = aBwrd;
+SwMoveFnCollection const & fnMoveForward = aFwrd;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/swcrsr.cxx b/sw/source/core/crsr/swcrsr.cxx
new file mode 100644
index 000000000..6a3c8aa21
--- /dev/null
+++ b/sw/source/core/crsr/swcrsr.cxx
@@ -0,0 +1,2637 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <editeng/protitem.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <unotools/charclass.hxx>
+#include <svl/ctloptions.hxx>
+#include <svl/srchitem.hxx>
+#include <swmodule.hxx>
+#include <fmtcntnt.hxx>
+#include <swtblfmt.hxx>
+#include <swcrsr.hxx>
+#include <unocrsr.hxx>
+#include <bookmark.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <section.hxx>
+#include <swtable.hxx>
+#include <cntfrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <scriptinfo.hxx>
+#include <crstate.hxx>
+#include <docsh.hxx>
+#include <viewsh.hxx>
+#include <frmatr.hxx>
+#include <breakit.hxx>
+#include <mdiexp.hxx>
+#include <strings.hrc>
+#include <redline.hxx>
+#include <txatbase.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <memory>
+#include <comphelper/lok.hxx>
+#include <editsh.hxx>
+
+#include <viewopt.hxx>
+
+using namespace ::com::sun::star::i18n;
+
+const sal_uInt16 coSrchRplcThreshold = 60000;
+
+namespace {
+
+struct PercentHdl
+{
+ SwDocShell* pDSh;
+ sal_uLong nActPos;
+ bool bBack, bNodeIdx;
+
+ PercentHdl( sal_uLong nStt, sal_uLong nEnd, SwDocShell* pSh )
+ : pDSh(pSh), nActPos(nStt), bBack(false), bNodeIdx(false)
+ {
+ bBack = (nStt > nEnd);
+ if( bBack )
+ {
+ sal_uLong n = nStt; nStt = nEnd; nEnd = n;
+ }
+ ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd );
+ }
+
+ explicit PercentHdl( const SwPaM& rPam )
+ : pDSh( rPam.GetDoc().GetDocShell() )
+ {
+ sal_Int32 nStt, nEnd;
+ if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
+ {
+ bNodeIdx = false;
+ nStt = rPam.GetMark()->nContent.GetIndex();
+ nEnd = rPam.GetPoint()->nContent.GetIndex();
+ }
+ else
+ {
+ bNodeIdx = true;
+ nStt = sal_Int32(rPam.GetMark()->nNode.GetIndex());
+ nEnd = sal_Int32(rPam.GetPoint()->nNode.GetIndex());
+ }
+ nActPos = nStt;
+ bBack = (nStt > nEnd );
+ if( bBack )
+ {
+ sal_uLong n = nStt; nStt = nEnd; nEnd = n;
+ }
+ ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd, pDSh );
+ }
+
+ ~PercentHdl() { ::EndProgress( pDSh ); }
+
+ void NextPos( sal_uLong nPos ) const
+ { ::SetProgressState( bBack ? nActPos - nPos : nPos, pDSh ); }
+
+ void NextPos( SwPosition const & rPos ) const
+ {
+ sal_Int32 nPos;
+ if( bNodeIdx )
+ nPos = sal_Int32(rPos.nNode.GetIndex());
+ else
+ nPos = rPos.nContent.GetIndex();
+ ::SetProgressState( bBack ? nActPos - nPos : nPos, pDSh );
+ }
+};
+
+}
+
+SwCursor::SwCursor( const SwPosition &rPos, SwPaM* pRing )
+ : SwPaM( rPos, pRing )
+ , m_nRowSpanOffset(0)
+ , m_nCursorBidiLevel(0)
+ , m_bColumnSelection(false)
+{
+}
+
+// @@@ semantic: no copy ctor.
+SwCursor::SwCursor(SwCursor const& rCpy, SwPaM *const pRing)
+ : SwPaM( rCpy, pRing )
+ , m_nRowSpanOffset(rCpy.m_nRowSpanOffset)
+ , m_nCursorBidiLevel(rCpy.m_nCursorBidiLevel)
+ , m_bColumnSelection(rCpy.m_bColumnSelection)
+{
+}
+
+SwCursor::~SwCursor()
+{
+}
+
+SwCursor* SwCursor::Create( SwPaM* pRing ) const
+{
+ return new SwCursor( *GetPoint(), pRing );
+}
+
+bool SwCursor::IsReadOnlyAvailable() const
+{
+ return false;
+}
+
+bool SwCursor::IsSkipOverHiddenSections() const
+{
+ return true;
+}
+
+bool SwCursor::IsSkipOverProtectSections() const
+{
+ return !IsReadOnlyAvailable();
+}
+
+// CreateNewSavePos is virtual so that derived classes of cursor can implement
+// own SaveObjects if needed and validate them in the virtual check routines.
+void SwCursor::SaveState()
+{
+ m_vSavePos.emplace_back( *this );
+}
+
+void SwCursor::RestoreState()
+{
+ if (!m_vSavePos.empty()) // Robust
+ {
+ m_vSavePos.pop_back();
+ }
+}
+
+/// determine if point is outside of the node-array's content area
+bool SwCursor::IsNoContent() const
+{
+ return GetPoint()->nNode.GetIndex() <
+ GetDoc().GetNodes().GetEndOfExtras().GetIndex();
+}
+
+bool SwCursor::IsSelOvrCheck(SwCursorSelOverFlags)
+{
+ return false;
+}
+
+// extracted from IsSelOvr()
+bool SwTableCursor::IsSelOvrCheck(SwCursorSelOverFlags eFlags)
+{
+ SwNodes& rNds = GetDoc().GetNodes();
+ // check sections of nodes array
+ if( (SwCursorSelOverFlags::CheckNodeSection & eFlags)
+ && HasMark() )
+ {
+ SwNodeIndex aOldPos( rNds, GetSavePos()->nNode );
+ if( !CheckNodesRange( aOldPos, GetPoint()->nNode, true ))
+ {
+ GetPoint()->nNode = aOldPos;
+ GetPoint()->nContent.Assign( GetContentNode(), GetSavePos()->nContent );
+ return true;
+ }
+ }
+ return SwCursor::IsSelOvrCheck(eFlags);
+}
+
+namespace
+{
+ const SwTextAttr* InputFieldAtPos(SwPosition const *pPos)
+ {
+ SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
+ if (!pTextNd)
+ return nullptr;
+ return pTextNd->GetTextAttrAt(pPos->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT);
+ }
+}
+
+bool SwCursor::IsSelOvr( SwCursorSelOverFlags eFlags )
+{
+ SwDoc& rDoc = GetDoc();
+ SwNodes& rNds = rDoc.GetNodes();
+
+ bool bSkipOverHiddenSections = IsSkipOverHiddenSections();
+ bool bSkipOverProtectSections = IsSkipOverProtectSections();
+
+ if ( IsSelOvrCheck( eFlags ) )
+ {
+ return true;
+ }
+
+ if (m_vSavePos.back().nNode != GetPoint()->nNode.GetIndex() &&
+ // (1997) in UI-ReadOnly everything is allowed
+ ( !rDoc.GetDocShell() || !rDoc.GetDocShell()->IsReadOnlyUI() ))
+ {
+ // check new sections
+ SwNodeIndex& rPtIdx = GetPoint()->nNode;
+ const SwSectionNode* pSectNd = rPtIdx.GetNode().FindSectionNode();
+ if( pSectNd &&
+ ((bSkipOverHiddenSections && pSectNd->GetSection().IsHiddenFlag() ) ||
+ (bSkipOverProtectSections && pSectNd->GetSection().IsProtectFlag() )))
+ {
+ if( !( SwCursorSelOverFlags::ChangePos & eFlags ) )
+ {
+ // then we're already done
+ RestoreSavePos();
+ return true;
+ }
+
+ // set cursor to new position:
+ SwNodeIndex aIdx( rPtIdx );
+ sal_Int32 nContentPos = m_vSavePos.back().nContent;
+ bool bGoNxt = m_vSavePos.back().nNode < rPtIdx.GetIndex();
+ SwContentNode* pCNd = bGoNxt
+ ? rNds.GoNextSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections)
+ : SwNodes::GoPrevSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections);
+ if( !pCNd && ( SwCursorSelOverFlags::EnableRevDirection & eFlags ))
+ {
+ bGoNxt = !bGoNxt;
+ pCNd = bGoNxt ? rNds.GoNextSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections)
+ : SwNodes::GoPrevSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections);
+ }
+
+ bool bIsValidPos = nullptr != pCNd;
+ const bool bValidNodesRange = bIsValidPos &&
+ ::CheckNodesRange( rPtIdx, aIdx, true );
+ if( !bValidNodesRange )
+ {
+ rPtIdx = m_vSavePos.back().nNode;
+ pCNd = rPtIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ bIsValidPos = false;
+ nContentPos = 0;
+ rPtIdx = aIdx;
+ pCNd = rPtIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ // then to the beginning of the document
+ rPtIdx = rNds.GetEndOfExtras();
+ pCNd = rNds.GoNext( &rPtIdx );
+ }
+ }
+ }
+
+ // register ContentIndex:
+ const sal_Int32 nTmpPos = bIsValidPos ? (bGoNxt ? 0 : pCNd->Len()) : nContentPos;
+ GetPoint()->nContent.Assign( pCNd, nTmpPos );
+ if( !bIsValidPos || !bValidNodesRange ||
+ IsInProtectTable( true ) )
+ return true;
+ }
+
+ // is there a protected section in the section?
+ if( HasMark() && bSkipOverProtectSections)
+ {
+ SwNodeOffset nSttIdx = GetMark()->nNode.GetIndex(),
+ nEndIdx = GetPoint()->nNode.GetIndex();
+ if( nEndIdx <= nSttIdx )
+ {
+ SwNodeOffset nTmp = nSttIdx;
+ nSttIdx = nEndIdx;
+ nEndIdx = nTmp;
+ }
+
+ const SwSectionFormats& rFormats = rDoc.GetSections();
+ for( SwSectionFormats::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ const SwSectionFormat* pFormat = rFormats[n];
+ const SvxProtectItem& rProtect = pFormat->GetProtect();
+ if( rProtect.IsContentProtected() )
+ {
+ const SwFormatContent& rContent = pFormat->GetContent(false);
+ OSL_ENSURE( rContent.GetContentIdx(), "No SectionNode?" );
+ SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex();
+ if( nSttIdx <= nIdx && nEndIdx >= nIdx )
+ {
+ // if it is no linked section then we cannot select it
+ const SwSection& rSect = *pFormat->GetSection();
+ if( SectionType::Content == rSect.GetType() )
+ {
+ RestoreSavePos();
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ const SwNode* pNd = &GetPoint()->nNode.GetNode();
+ if( pNd->IsContentNode() && !dynamic_cast<SwUnoCursor*>(this) )
+ {
+ const SwContentFrame* pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ if ( (SwCursorSelOverFlags::ChangePos & eFlags) //allowed to change position if it's a bad one
+ && pFrame && pFrame->isFrameAreaDefinitionValid()
+ && !pFrame->getFrameArea().Height() //a bad zero height position
+ && !InputFieldAtPos(GetPoint()) ) //unless it's a (vertical) input field
+ {
+ // skip to the next/prev valid paragraph with a layout
+ SwNodeIndex& rPtIdx = GetPoint()->nNode;
+ bool bGoNxt = m_vSavePos.back().nNode < rPtIdx.GetIndex();
+ for (;;)
+ {
+ pFrame = bGoNxt ? pFrame->GetNextContentFrame() : pFrame->GetPrevContentFrame();
+ if (!pFrame || 0 != pFrame->getFrameArea().Height() )
+ break;
+ }
+
+ // #i72394# skip to prev/next valid paragraph with a layout in case
+ // the first search did not succeed:
+ if( !pFrame )
+ {
+ bGoNxt = !bGoNxt;
+ pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ while ( pFrame && 0 == pFrame->getFrameArea().Height() )
+ {
+ pFrame = bGoNxt ? pFrame->GetNextContentFrame()
+ : pFrame->GetPrevContentFrame();
+ }
+ }
+
+ if (pFrame != nullptr)
+ {
+ if (pFrame->IsTextFrame())
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame));
+ *GetPoint() = pTextFrame->MapViewToModelPos(TextFrameIndex(
+ bGoNxt ? 0 : pTextFrame->GetText().getLength()));
+ }
+ else
+ {
+ assert(pFrame->IsNoTextFrame());
+ SwContentNode *const pCNd = const_cast<SwContentNode*>(
+ static_cast<SwNoTextFrame const*>(pFrame)->GetNode());
+ assert(pCNd);
+
+ // set this ContentNode as new position
+ rPtIdx = *pCNd;
+ // assign corresponding ContentIndex
+ const sal_Int32 nTmpPos = bGoNxt ? 0 : pCNd->Len();
+ GetPoint()->nContent.Assign( pCNd, nTmpPos );
+ }
+
+
+ if (rPtIdx.GetIndex() == m_vSavePos.back().nNode
+ && GetPoint()->nContent.GetIndex() == m_vSavePos.back().nContent)
+ {
+ // new position equals saved one
+ // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
+ pFrame = nullptr;
+ }
+
+ if ( IsInProtectTable( true ) )
+ {
+ // new position in protected table
+ // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
+ pFrame = nullptr;
+ }
+ }
+ }
+
+ if( !pFrame )
+ {
+ DeleteMark();
+ RestoreSavePos();
+ return true; // we need a frame
+ }
+ }
+
+ // is the cursor allowed to be in a protected node?
+ if( !( SwCursorSelOverFlags::ChangePos & eFlags ) && !IsAtValidPos() )
+ {
+ DeleteMark();
+ RestoreSavePos();
+ return true;
+ }
+
+ if( !HasMark() )
+ return false;
+
+ // check for invalid sections
+ if( !::CheckNodesRange( GetMark()->nNode, GetPoint()->nNode, true ))
+ {
+ DeleteMark();
+ RestoreSavePos();
+ return true; // we need a frame
+ }
+
+ pNd = &GetMark()->nNode.GetNode();
+ if( pNd->IsContentNode()
+ && !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() )
+ && !dynamic_cast<SwUnoCursor*>(this) )
+ {
+ DeleteMark();
+ RestoreSavePos();
+ return true; // we need a frame
+ }
+
+ // ensure that selection is only inside an InputField or contains the InputField completely
+ {
+ const SwTextAttr* pInputFieldTextAttrAtPoint = InputFieldAtPos(GetPoint());
+ const SwTextAttr* pInputFieldTextAttrAtMark = InputFieldAtPos(GetMark());
+
+ if ( pInputFieldTextAttrAtPoint != pInputFieldTextAttrAtMark )
+ {
+ const SwNodeOffset nRefNodeIdx =
+ ( SwCursorSelOverFlags::Toggle & eFlags )
+ ? m_vSavePos.back().nNode
+ : GetMark()->nNode.GetIndex();
+ const sal_Int32 nRefContentIdx =
+ ( SwCursorSelOverFlags::Toggle & eFlags )
+ ? m_vSavePos.back().nContent
+ : GetMark()->nContent.GetIndex();
+ const bool bIsForwardSelection =
+ nRefNodeIdx < GetPoint()->nNode.GetIndex()
+ || ( nRefNodeIdx == GetPoint()->nNode.GetIndex()
+ && nRefContentIdx < GetPoint()->nContent.GetIndex() );
+
+ if ( pInputFieldTextAttrAtPoint != nullptr )
+ {
+ const sal_Int32 nNewPointPos =
+ bIsForwardSelection ? *(pInputFieldTextAttrAtPoint->End()) : pInputFieldTextAttrAtPoint->GetStart();
+ SwTextNode* pTextNdAtPoint = GetPoint()->nNode.GetNode().GetTextNode();
+ GetPoint()->nContent.Assign( pTextNdAtPoint, nNewPointPos );
+ }
+
+ if ( pInputFieldTextAttrAtMark != nullptr )
+ {
+ const sal_Int32 nNewMarkPos =
+ bIsForwardSelection ? pInputFieldTextAttrAtMark->GetStart() : *(pInputFieldTextAttrAtMark->End());
+ SwTextNode* pTextNdAtMark = GetMark()->nNode.GetNode().GetTextNode();
+ GetMark()->nContent.Assign( pTextNdAtMark, nNewMarkPos );
+ }
+ }
+ }
+
+ const SwTableNode* pPtNd = GetPoint()->nNode.GetNode().FindTableNode();
+ const SwTableNode* pMrkNd = GetMark()->nNode.GetNode().FindTableNode();
+ // both in no or in same table node
+ if( ( !pMrkNd && !pPtNd ) || pPtNd == pMrkNd )
+ return false;
+
+ // in different tables or only mark in table
+ if( pMrkNd )
+ {
+ // not allowed, so go back to old position
+ RestoreSavePos();
+ // Cursor stays at old position
+ return true;
+ }
+
+ // Note: this cannot happen in TableMode
+ // Only Point in Table then go behind/in front of table
+ if (SwCursorSelOverFlags::ChangePos & eFlags)
+ {
+ bool bSelTop = GetPoint()->nNode.GetIndex() <
+ ((SwCursorSelOverFlags::Toggle & eFlags)
+ ? m_vSavePos.back().nNode : GetMark()->nNode.GetIndex());
+
+ do { // loop for table after table
+ SwNodeOffset nSEIdx = pPtNd->EndOfSectionIndex();
+ SwNodeOffset nSttEndTable = nSEIdx + 1;
+
+ if( bSelTop )
+ nSttEndTable = rNds[ nSEIdx ]->StartOfSectionIndex() - 1;
+
+ GetPoint()->nNode = nSttEndTable;
+ const SwNode* pMyNd = &(GetNode());
+
+ if( pMyNd->IsSectionNode() || ( pMyNd->IsEndNode() &&
+ pMyNd->StartOfSectionNode()->IsSectionNode() ) )
+ {
+ pMyNd = bSelTop
+ ? SwNodes::GoPrevSection( &GetPoint()->nNode,true,false )
+ : rNds.GoNextSection( &GetPoint()->nNode,true,false );
+
+ /* #i12312# Handle failure of Go{Prev|Next}Section */
+ if ( nullptr == pMyNd)
+ break;
+
+ pPtNd = pMyNd->FindTableNode();
+ if( pPtNd )
+ continue;
+ }
+
+ // we permit these
+ if( pMyNd->IsContentNode() &&
+ ::CheckNodesRange( GetMark()->nNode,
+ GetPoint()->nNode, true ))
+ {
+ // table in table
+ const SwTableNode* pOuterTableNd = pMyNd->FindTableNode();
+ if ( pOuterTableNd )
+ pMyNd = pOuterTableNd;
+ else
+ {
+ SwContentNode* pCNd = const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pMyNd));
+ GetPoint()->nContent.Assign( pCNd, bSelTop ? pCNd->Len() : 0 );
+ return false;
+ }
+ }
+ if( bSelTop )
+ {
+ if ( !pMyNd->IsEndNode() )
+ break;
+ pPtNd = pMyNd->FindTableNode();
+ }
+ else
+ pPtNd = pMyNd->GetTableNode();
+ if (!pPtNd)
+ break;
+ } while( true );
+ }
+
+ // stay on old position
+ RestoreSavePos();
+ return true;
+}
+
+bool SwCursor::IsInProtectTable( bool bMove, bool bChgCursor )
+{
+ SwContentNode* pCNd = GetContentNode();
+ if( !pCNd )
+ return false;
+
+ // No table, no protected cell:
+ const SwTableNode* pTableNode = pCNd->FindTableNode();
+ if ( !pTableNode )
+ return false;
+
+ // Current position == last save position?
+ if (m_vSavePos.back().nNode == GetPoint()->nNode.GetIndex())
+ return false;
+
+ // Check for covered cell:
+ bool bInCoveredCell = false;
+ const SwStartNode* pTmpSttNode = pCNd->FindTableBoxStartNode();
+ OSL_ENSURE( pTmpSttNode, "In table, therefore I expect to get a SwTableBoxStartNode" );
+ const SwTableBox* pBox = pTmpSttNode ? pTableNode->GetTable().GetTableBox( pTmpSttNode->GetIndex() ) : nullptr; //Robust #151355
+ if ( pBox && pBox->getRowSpan() < 1 ) // Robust #151270
+ bInCoveredCell = true;
+
+ // Positions of covered cells are not acceptable:
+ if ( !bInCoveredCell )
+ {
+ // Position not protected?
+ if ( !pCNd->IsProtect() )
+ return false;
+
+ // Cursor in protected cells allowed?
+ if ( IsReadOnlyAvailable() )
+ return false;
+ }
+
+ // If we reach this point, we are in a protected or covered table cell!
+
+ if( !bMove )
+ {
+ if( bChgCursor )
+ // restore the last save position
+ RestoreSavePos();
+
+ return true; // Cursor stays at old position
+ }
+
+ // We are in a protected table cell. Traverse top to bottom?
+ if (m_vSavePos.back().nNode < GetPoint()->nNode.GetIndex())
+ {
+ // search next valid box
+ // if there is another StartNode after the EndNode of a cell then
+ // there is another cell
+ SwNodeIndex aCellStt( *GetNode().FindTableBoxStartNode()->EndOfSectionNode(), 1 );
+ bool bProt = true;
+GoNextCell:
+ for (;;) {
+ if( !aCellStt.GetNode().IsStartNode() )
+ break;
+ ++aCellStt;
+ pCNd = aCellStt.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aCellStt.GetNodes().GoNext( &aCellStt );
+ bProt = pCNd->IsProtect();
+ if( !bProt )
+ break;
+ aCellStt.Assign( *pCNd->FindTableBoxStartNode()->EndOfSectionNode(), 1 );
+ }
+
+SetNextCursor:
+ if( !bProt ) // found free cell
+ {
+ GetPoint()->nNode = aCellStt;
+ SwContentNode* pTmpCNd = GetContentNode();
+ if( pTmpCNd )
+ {
+ GetPoint()->nContent.Assign( pTmpCNd, 0 );
+ return false;
+ }
+ return IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos );
+ }
+ // end of table, so go to next node
+ ++aCellStt;
+ SwNode* pNd = &aCellStt.GetNode();
+ if( pNd->IsEndNode() || HasMark())
+ {
+ // if only table in FlyFrame or SSelection then stay on old position
+ if( bChgCursor )
+ RestoreSavePos();
+ return true;
+ }
+ else if( pNd->IsTableNode() && aCellStt++ )
+ goto GoNextCell;
+
+ bProt = false; // index is now on a content node
+ goto SetNextCursor;
+ }
+
+ // search for the previous valid box
+ {
+ // if there is another EndNode in front of the StartNode than there
+ // exists a previous cell
+ SwNodeIndex aCellStt( *GetNode().FindTableBoxStartNode(), -1 );
+ SwNode* pNd;
+ bool bProt = true;
+GoPrevCell:
+ for (;;) {
+ pNd = &aCellStt.GetNode();
+ if( !pNd->IsEndNode() )
+ break;
+ aCellStt.Assign( *pNd->StartOfSectionNode(), +1 );
+ pCNd = aCellStt.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = pNd->GetNodes().GoNext( &aCellStt );
+ bProt = pCNd->IsProtect();
+ if( !bProt )
+ break;
+ aCellStt.Assign( *pNd->FindTableBoxStartNode(), -1 );
+ }
+
+SetPrevCursor:
+ if( !bProt ) // found free cell
+ {
+ GetPoint()->nNode = aCellStt;
+ SwContentNode* pTmpCNd = GetContentNode();
+ if( pTmpCNd )
+ {
+ GetPoint()->nContent.Assign( pTmpCNd, 0 );
+ return false;
+ }
+ return IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos );
+ }
+ // at the beginning of a table, so go to next node
+ --aCellStt;
+ pNd = &aCellStt.GetNode();
+ if( pNd->IsStartNode() || HasMark() )
+ {
+ // if only table in FlyFrame or SSelection then stay on old position
+ if( bChgCursor )
+ RestoreSavePos();
+ return true;
+ }
+ else if( pNd->StartOfSectionNode()->IsTableNode() && aCellStt-- )
+ goto GoPrevCell;
+
+ bProt = false; // index is now on a content node
+ goto SetPrevCursor;
+ }
+}
+
+/// Return <true> if cursor can be set to this position
+bool SwCursor::IsAtValidPos( bool bPoint ) const
+{
+ const SwDoc& rDoc = GetDoc();
+ const SwPosition* pPos = bPoint ? GetPoint() : GetMark();
+ const SwNode* pNd = &pPos->nNode.GetNode();
+
+ if( pNd->IsContentNode() && !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) &&
+ !dynamic_cast<const SwUnoCursor*>(this) )
+ {
+ return false;
+ }
+
+ // #i45129# - in UI-ReadOnly everything is allowed
+ if( !rDoc.GetDocShell() || !rDoc.GetDocShell()->IsReadOnlyUI() )
+ return true;
+
+ const bool bCursorInReadOnly = IsReadOnlyAvailable();
+ if( !bCursorInReadOnly && pNd->IsProtect() )
+ return false;
+
+ const SwSectionNode* pSectNd = pNd->FindSectionNode();
+ return !pSectNd
+ || !(pSectNd->GetSection().IsHiddenFlag() ||
+ ( !bCursorInReadOnly && pSectNd->GetSection().IsProtectFlag() ));
+}
+
+void SwCursor::SaveTableBoxContent( const SwPosition* ) {}
+
+/// set range for search in document
+SwMoveFnCollection const & SwCursor::MakeFindRange( SwDocPositions nStart,
+ SwDocPositions nEnd, SwPaM* pRange ) const
+{
+ pRange->SetMark();
+ FillFindPos( nStart, *pRange->GetMark() );
+ FillFindPos( nEnd, *pRange->GetPoint() );
+
+ // determine direction of search
+ return ( SwDocPositions::Start == nStart || SwDocPositions::OtherStart == nStart ||
+ (SwDocPositions::Curr == nStart &&
+ (SwDocPositions::End == nEnd || SwDocPositions::OtherEnd == nEnd ) ))
+ ? fnMoveForward : fnMoveBackward;
+}
+
+static sal_uLong lcl_FindSelection( SwFindParas& rParas, SwCursor* pCurrentCursor,
+ SwMoveFnCollection const & fnMove, SwCursor*& pFndRing,
+ SwPaM& aRegion, FindRanges eFndRngs,
+ bool bInReadOnly, bool& bCancel )
+{
+ SwDoc& rDoc = pCurrentCursor->GetDoc();
+ bool const bDoesUndo = rDoc.GetIDocumentUndoRedo().DoesUndo();
+ int nFndRet = 0;
+ sal_uLong nFound = 0;
+ const bool bSrchBkwrd = &fnMove == &fnMoveBackward;
+ SwPaM *pTmpCursor = pCurrentCursor, *pSaveCursor = pCurrentCursor;
+ std::unique_ptr<SvxSearchItem> xSearchItem;
+
+ // only create progress bar for ShellCursor
+ bool bIsUnoCursor = dynamic_cast<SwUnoCursor*>(pCurrentCursor) != nullptr;
+ std::unique_ptr<PercentHdl> pPHdl;
+ sal_uInt16 nCursorCnt = 0;
+ if( FindRanges::InSel & eFndRngs )
+ {
+ while( pCurrentCursor != ( pTmpCursor = pTmpCursor->GetNext() ))
+ ++nCursorCnt;
+ if( nCursorCnt && !bIsUnoCursor )
+ pPHdl.reset(new PercentHdl( 0, nCursorCnt, rDoc.GetDocShell() ));
+ }
+ else
+ pSaveCursor = pSaveCursor->GetPrev();
+
+ bool bEnd = false;
+ do {
+ aRegion.SetMark();
+ // independent from search direction: SPoint is always bigger than mark
+ // if the search area is valid
+ SwPosition *pSttPos = aRegion.GetMark(),
+ *pEndPos = aRegion.GetPoint();
+ *pSttPos = *pTmpCursor->Start();
+ *pEndPos = *pTmpCursor->End();
+ if( bSrchBkwrd )
+ aRegion.Exchange();
+
+ if( !nCursorCnt && !pPHdl && !bIsUnoCursor )
+ pPHdl.reset(new PercentHdl( aRegion ));
+
+ // as long as found and not at same position
+ while( *pSttPos <= *pEndPos )
+ {
+ nFndRet = rParas.DoFind(*pCurrentCursor, fnMove, aRegion, bInReadOnly, xSearchItem);
+ if( 0 == nFndRet ||
+ ( pFndRing &&
+ *pFndRing->GetPoint() == *pCurrentCursor->GetPoint() &&
+ *pFndRing->GetMark() == *pCurrentCursor->GetMark() ))
+ break;
+ if( !( FIND_NO_RING & nFndRet ))
+ {
+ // #i24084# - create ring similar to the one in CreateCursor
+ SwCursor* pNew = pCurrentCursor->Create( pFndRing );
+ if( !pFndRing )
+ pFndRing = pNew;
+
+ pNew->SetMark();
+ *pNew->GetMark() = *pCurrentCursor->GetMark();
+ }
+
+ ++nFound;
+
+ if( !( eFndRngs & FindRanges::InSelAll) )
+ {
+ bEnd = true;
+ break;
+ }
+
+ if ((coSrchRplcThreshold == nFound)
+ && rDoc.GetIDocumentUndoRedo().DoesUndo()
+ && rParas.IsReplaceMode())
+ {
+ short nRet = pCurrentCursor->MaxReplaceArived();
+ if( RET_YES == nRet )
+ {
+ rDoc.GetIDocumentUndoRedo().DelAllUndoObj();
+ rDoc.GetIDocumentUndoRedo().DoUndo(false);
+ }
+ else
+ {
+ bEnd = true;
+ if(RET_CANCEL == nRet)
+ {
+ bCancel = true;
+ }
+ break;
+ }
+ }
+
+ if( bSrchBkwrd )
+ // move pEndPos in front of the found area
+ *pEndPos = *pCurrentCursor->Start();
+ else
+ // move pSttPos behind the found area
+ *pSttPos = *pCurrentCursor->End();
+
+ if( *pSttPos == *pEndPos )
+ // in area but at the end => done
+ break;
+
+ if( !nCursorCnt && pPHdl )
+ {
+ pPHdl->NextPos( *aRegion.GetMark() );
+ }
+ }
+
+ if( bEnd || !( eFndRngs & ( FindRanges::InSelAll | FindRanges::InSel )) )
+ break;
+
+ pTmpCursor = pTmpCursor->GetNext();
+ if( nCursorCnt && pPHdl )
+ {
+ pPHdl->NextPos( ++pPHdl->nActPos );
+ }
+
+ } while( pTmpCursor != pSaveCursor && pTmpCursor->GetNext() != pTmpCursor);
+
+ if( nFound && !pFndRing ) // if no ring should be created
+ pFndRing = pCurrentCursor->Create();
+
+ rDoc.GetIDocumentUndoRedo().DoUndo(bDoesUndo);
+ return nFound;
+}
+
+static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd,
+ SwPaM& rPam, bool bFirst )
+{
+ if( rSttNd.GetIndex() + 1 == rEndNd.GetIndex() )
+ return false;
+
+ SwNodes& rNds = rPam.GetDoc().GetNodes();
+ rPam.DeleteMark();
+ SwContentNode* pCNd;
+ if( !bFirst )
+ {
+ rPam.GetPoint()->nNode = rSttNd;
+ pCNd = rNds.GoNext( &rPam.GetPoint()->nNode );
+ if( !pCNd )
+ return false;
+ pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
+ }
+ else if( rSttNd.GetIndex() > rPam.GetPoint()->nNode.GetIndex() ||
+ rPam.GetPoint()->nNode.GetIndex() >= rEndNd.GetIndex() )
+ // not in this section
+ return false;
+
+ rPam.SetMark();
+ rPam.GetPoint()->nNode = rEndNd;
+ pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode );
+ if( !pCNd )
+ return false;
+ pCNd->MakeEndIndex( &rPam.GetPoint()->nContent );
+
+ return *rPam.GetMark() < *rPam.GetPoint();
+}
+
+static bool lcl_MakeSelBkwrd( const SwNode& rSttNd, const SwNode& rEndNd,
+ SwPaM& rPam, bool bFirst )
+{
+ if( rEndNd.GetIndex() + 1 == rSttNd.GetIndex() )
+ return false;
+
+ SwNodes& rNds = rPam.GetDoc().GetNodes();
+ rPam.DeleteMark();
+ SwContentNode* pCNd;
+ if( !bFirst )
+ {
+ rPam.GetPoint()->nNode = rSttNd;
+ pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode );
+ if( !pCNd )
+ return false;
+ pCNd->MakeEndIndex( &rPam.GetPoint()->nContent );
+ }
+ else if( rEndNd.GetIndex() > rPam.GetPoint()->nNode.GetIndex() ||
+ rPam.GetPoint()->nNode.GetIndex() >= rSttNd.GetIndex() )
+ return false; // not in this section
+
+ rPam.SetMark();
+ rPam.GetPoint()->nNode = rEndNd;
+ pCNd = rNds.GoNext( &rPam.GetPoint()->nNode );
+ if( !pCNd )
+ return false;
+ pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
+
+ return *rPam.GetPoint() < *rPam.GetMark();
+}
+
+// this method "searches" for all use cases because in SwFindParas is always the
+// correct parameters and respective search method
+sal_uLong SwCursor::FindAll( SwFindParas& rParas,
+ SwDocPositions nStart, SwDocPositions nEnd,
+ FindRanges eFndRngs, bool& bCancel )
+{
+ bCancel = false;
+ SwCursorSaveState aSaveState( *this );
+
+ // create region without adding it to the ring
+ SwPaM aRegion( *GetPoint() );
+ SwMoveFnCollection const & fnMove = MakeFindRange( nStart, nEnd, &aRegion );
+
+ sal_uLong nFound = 0;
+ const bool bMvBkwrd = &fnMove == &fnMoveBackward;
+ bool bInReadOnly = IsReadOnlyAvailable();
+ std::unique_ptr<SvxSearchItem> xSearchItem;
+
+ SwCursor* pFndRing = nullptr;
+ SwNodes& rNds = GetDoc().GetNodes();
+
+ // search in sections?
+ if( FindRanges::InSel & eFndRngs )
+ {
+ // if string was not found in region then get all sections (cursors
+ // stays unchanged)
+ nFound = lcl_FindSelection( rParas, this, fnMove,
+ pFndRing, aRegion, eFndRngs,
+ bInReadOnly, bCancel );
+ if( 0 == nFound )
+ return nFound;
+
+ // found string at least once; it's all in new Cursor ring thus delete old one
+ while( GetNext() != this )
+ delete GetNext();
+
+ *GetPoint() = *pFndRing->GetPoint();
+ SetMark();
+ *GetMark() = *pFndRing->GetMark();
+ pFndRing->GetRingContainer().merge( GetRingContainer() );
+ delete pFndRing;
+ }
+ else if( FindRanges::InOther & eFndRngs )
+ {
+ // put cursor as copy of current into ring
+ // chaining points always to first created, so forward
+ SwCursor* pSav = Create( this ); // save the current cursor
+
+ // if already outside of body text search from this position or start at
+ // 1. base section
+ if( bMvBkwrd
+ ? lcl_MakeSelBkwrd( rNds.GetEndOfExtras(),
+ *rNds.GetEndOfPostIts().StartOfSectionNode(),
+ *this, rNds.GetEndOfExtras().GetIndex() >=
+ GetPoint()->nNode.GetIndex() )
+ : lcl_MakeSelFwrd( *rNds.GetEndOfPostIts().StartOfSectionNode(),
+ rNds.GetEndOfExtras(), *this,
+ rNds.GetEndOfExtras().GetIndex() >=
+ GetPoint()->nNode.GetIndex() ))
+ {
+ nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing,
+ aRegion, eFndRngs, bInReadOnly, bCancel );
+ }
+
+ if( !nFound )
+ {
+ // put back the old one
+ *GetPoint() = *pSav->GetPoint();
+ if( pSav->HasMark() )
+ {
+ SetMark();
+ *GetMark() = *pSav->GetMark();
+ }
+ else
+ DeleteMark();
+ return 0;
+ }
+
+ if( !( FindRanges::InSelAll & eFndRngs ))
+ {
+ // there should only be a single one, thus add it
+ // independent from search direction: SPoint is always bigger than
+ // mark if the search area is valid
+ *GetPoint() = *pFndRing->GetPoint();
+ SetMark();
+ *GetMark() = *pFndRing->GetMark();
+ }
+ else
+ {
+ // found string at least once; it's all in new Cursor ring thus delete old one
+ while( GetNext() != this )
+ delete GetNext();
+
+ *GetPoint() = *pFndRing->GetPoint();
+ SetMark();
+ *GetMark() = *pFndRing->GetMark();
+ pFndRing->GetRingContainer().merge( GetRingContainer() );
+ }
+ delete pFndRing;
+ }
+ else if( FindRanges::InSelAll & eFndRngs )
+ {
+ SwCursor* pSav = Create( this ); // save the current cursor
+
+ const SwNode* pSttNd = ( FindRanges::InBodyOnly & eFndRngs )
+ ? rNds.GetEndOfContent().StartOfSectionNode()
+ : rNds.GetEndOfPostIts().StartOfSectionNode();
+
+ if( bMvBkwrd
+ ? lcl_MakeSelBkwrd( rNds.GetEndOfContent(), *pSttNd, *this, false )
+ : lcl_MakeSelFwrd( *pSttNd, rNds.GetEndOfContent(), *this, false ))
+ {
+ nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing,
+ aRegion, eFndRngs, bInReadOnly, bCancel );
+ }
+
+ if( !nFound )
+ {
+ // put back the old one
+ *GetPoint() = *pSav->GetPoint();
+ if( pSav->HasMark() )
+ {
+ SetMark();
+ *GetMark() = *pSav->GetMark();
+ }
+ else
+ DeleteMark();
+ return 0;
+ }
+ while( GetNext() != this )
+ delete GetNext();
+
+ *GetPoint() = *pFndRing->GetPoint();
+ SetMark();
+ *GetMark() = *pFndRing->GetMark();
+ pFndRing->GetRingContainer().merge( GetRingContainer() );
+ delete pFndRing;
+ }
+ else
+ {
+ // if a GetMark is set then keep the GetMark of the found object
+ // This allows spanning an area with this search.
+ SwPosition aMarkPos( *GetMark() );
+ const bool bMarkPos = HasMark() && (eFndRngs == FindRanges::InBody);
+
+ nFound = rParas.DoFind(*this, fnMove, aRegion, bInReadOnly, xSearchItem) ? 1 : 0;
+ if (0 != nFound && bMarkPos)
+ *GetMark() = aMarkPos;
+ }
+
+ if( nFound && SwCursor::IsSelOvr( SwCursorSelOverFlags::Toggle ) )
+ nFound = 0;
+ return nFound;
+}
+
+void SwCursor::FillFindPos( SwDocPositions ePos, SwPosition& rPos ) const
+{
+ bool bIsStart = true;
+ SwContentNode* pCNd = nullptr;
+ SwNodes& rNds = GetDoc().GetNodes();
+
+ switch( ePos )
+ {
+ case SwDocPositions::Start:
+ rPos.nNode = *rNds.GetEndOfContent().StartOfSectionNode();
+ pCNd = rNds.GoNext( &rPos.nNode );
+ break;
+ case SwDocPositions::End:
+ rPos.nNode = rNds.GetEndOfContent();
+ pCNd = SwNodes::GoPrevious( &rPos.nNode );
+ bIsStart = false;
+ break;
+ case SwDocPositions::OtherStart:
+ rPos.nNode = *rNds[ SwNodeOffset(0) ];
+ pCNd = rNds.GoNext( &rPos.nNode );
+ break;
+ case SwDocPositions::OtherEnd:
+ rPos.nNode = *rNds.GetEndOfContent().StartOfSectionNode();
+ pCNd = SwNodes::GoPrevious( &rPos.nNode );
+ bIsStart = false;
+ break;
+ default:
+ rPos = *GetPoint();
+ }
+
+ if( pCNd )
+ {
+ rPos.nContent.Assign( pCNd, bIsStart ? 0 : pCNd->Len() );
+ }
+}
+
+short SwCursor::MaxReplaceArived()
+{
+ return RET_YES;
+}
+
+namespace {
+
+struct HideWrapper
+{
+ // either the frame's text or the node's text (possibly pre-filtered)
+ OUString const* m_pText;
+ // this is actually a TextFrameIndex but all of the i18n code uses sal_Int32
+ sal_Int32 m_nPtIndex;
+ // if mapping is needed, use this frame
+ SwTextFrame * m_pFrame;
+ // input in the constructor, output (via mapping) in the destructor
+ SwTextNode *& m_rpTextNode;
+ sal_Int32 & m_rPtPos;
+
+ HideWrapper(SwRootFrame const*const pLayout,
+ SwTextNode *& rpTextNode, sal_Int32 & rPtPos,
+ OUString const*const pFilteredNodeText = nullptr)
+ : m_pText(pFilteredNodeText)
+ , m_pFrame(nullptr)
+ , m_rpTextNode(rpTextNode)
+ , m_rPtPos(rPtPos)
+ {
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ m_pFrame = static_cast<SwTextFrame*>(rpTextNode->getLayoutFrame(pLayout));
+ m_pText = &m_pFrame->GetText();
+ m_nPtIndex = sal_Int32(m_pFrame->MapModelToView(rpTextNode, rPtPos));
+ }
+ else
+ {
+ if (!m_pText)
+ {
+ m_pText = &rpTextNode->GetText();
+ }
+ m_nPtIndex = rPtPos;
+ }
+ }
+ ~HideWrapper()
+ {
+ AssignBack(m_rpTextNode, m_rPtPos);
+ }
+ void AssignBack(SwTextNode *& rpTextNode, sal_Int32 & rPtPos)
+ {
+ if (0 <= m_nPtIndex && m_pFrame)
+ {
+ std::pair<SwTextNode*, sal_Int32> const pos(
+ m_pFrame->MapViewToModel(TextFrameIndex(m_nPtIndex)));
+ rpTextNode = pos.first;
+ rPtPos = pos.second;
+ }
+ else
+ {
+ rPtPos = m_nPtIndex;
+ }
+ }
+};
+
+} // namespace
+
+bool SwCursor::SelectWord( SwViewShell const * pViewShell, const Point* pPt )
+{
+ return SelectWordWT( pViewShell, WordType::ANYWORD_IGNOREWHITESPACES, pPt );
+}
+
+bool SwCursor::IsStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ bRet = g_pBreakIt->GetBreakIter()->isBeginWord(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos )),
+ nWordType );
+ }
+ return bRet;
+}
+
+bool SwCursor::IsEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ bRet = g_pBreakIt->GetBreakIter()->isEndWord(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ nWordType );
+
+ }
+ return bRet;
+}
+
+bool SwCursor::IsInWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ Boundary aBoundary = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ nWordType,
+ true );
+
+ bRet = aBoundary.startPos != aBoundary.endPos &&
+ aBoundary.startPos <= w.m_nPtIndex &&
+ w.m_nPtIndex <= aBoundary.endPos;
+ w.m_nPtIndex = aBoundary.startPos; // hack: convert startPos back...
+ }
+ if(bRet)
+ {
+ const CharClass& rCC = GetAppCharClass();
+ bRet = rCC.isLetterNumeric(pTextNd->GetText(), nPtPos);
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::IsStartEndSentence(bool bEnd, SwRootFrame const*const pLayout) const
+{
+ bool bRet = bEnd ?
+ GetContentNode() && GetPoint()->nContent == GetContentNode()->Len() :
+ GetPoint()->nContent.GetIndex() == 0;
+
+ if ((pLayout != nullptr && pLayout->HasMergedParas()) || !bRet)
+ {
+ SwCursor aCursor(*GetPoint(), nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence(bEnd ? SwCursor::END_SENT : SwCursor::START_SENT, pLayout);
+ bRet = aOrigPos == *aCursor.GetPoint();
+ }
+ return bRet;
+}
+
+bool SwCursor::GoStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ SwCursorSaveState aSave( *this );
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ nWordType,
+ false ).startPos;
+ }
+
+ if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0)
+ {
+ *GetPoint() = SwPosition(*pTextNd, nPtPos);
+ if( !IsSelOvr() )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::GoEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ SwCursorSaveState aSave( *this );
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ nWordType,
+ true ).endPos;
+ }
+
+ if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0 &&
+ GetPoint()->nContent.GetIndex() != nPtPos )
+ {
+ *GetPoint() = SwPosition(*pTextNd, nPtPos);
+ if( !IsSelOvr() )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::GoNextWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ SwCursorSaveState aSave( *this );
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->nextWord(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang(nPtPos, 1) ),
+ nWordType ).startPos;
+ }
+
+ if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0)
+ {
+ *GetPoint() = SwPosition(*pTextNd, nPtPos);
+ if( !IsSelOvr() )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::GoPrevWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ SwCursorSaveState aSave( *this );
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pTextNd, nPtPos);
+
+ const sal_Int32 nPtStart = w.m_nPtIndex;
+ if (w.m_nPtIndex)
+ {
+ --w.m_nPtIndex;
+ w.AssignBack(pTextNd, nPtPos);
+ }
+
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->previousWord(
+ *w.m_pText, nPtStart,
+ g_pBreakIt->GetLocale( pTextNd->GetLang(nPtPos, 1) ),
+ nWordType ).startPos;
+ }
+
+ if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0)
+ {
+ *GetPoint() = SwPosition(*pTextNd, nPtPos);
+ if( !IsSelOvr() )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::SelectWordWT( SwViewShell const * pViewShell, sal_Int16 nWordType, const Point* pPt )
+{
+ SwCursorSaveState aSave( *this );
+
+ bool bRet = false;
+ DeleteMark();
+ const SwRootFrame* pLayout = pViewShell->GetLayout();
+ if( pPt && nullptr != pLayout )
+ {
+ // set the cursor to the layout position
+ Point aPt( *pPt );
+ pLayout->GetModelPositionForViewPoint( GetPoint(), aPt );
+ }
+
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ // Should we select the whole fieldmark?
+ const IDocumentMarkAccess* pMarksAccess = GetDoc().getIDocumentMarkAccess( );
+ sw::mark::IFieldmark const*const pMark(pMarksAccess->getFieldmarkFor(*GetPoint()));
+ if (pMark && (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
+ {
+ *GetPoint() = sw::mark::FindFieldSep(*pMark);
+ ++GetPoint()->nContent; // Don't select the separator
+
+ const SwPosition& rEnd = pMark->GetMarkEnd();
+
+ assert(pMark->GetMarkEnd() != *GetPoint());
+ SetMark();
+ GetMark()->nNode = rEnd.nNode;
+ GetMark()->nContent = rEnd.nContent;
+ --GetMark()->nContent; // Don't select the end delimiter
+
+ bRet = true;
+ }
+ else
+ {
+ bool bForward = true;
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ HideWrapper w(pViewShell->GetLayout(), pTextNd, nPtPos);
+
+ Boundary aBndry( g_pBreakIt->GetBreakIter()->getWordBoundary(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ nWordType,
+ bForward ));
+
+ if (comphelper::LibreOfficeKit::isActive() && aBndry.startPos == aBndry.endPos && w.m_nPtIndex > 0)
+ {
+ // nPtPos is the end of the paragraph, select the last word then.
+ --w.m_nPtIndex;
+ w.AssignBack(pTextNd, nPtPos);
+
+ aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ nWordType,
+ bForward );
+
+ }
+
+ SwTextNode * pStartNode(pTextNd);
+ sal_Int32 nStartIndex;
+ w.m_nPtIndex = aBndry.startPos;
+ w.AssignBack(pStartNode, nStartIndex);
+
+ SwTextNode * pEndNode(pTextNd);
+ sal_Int32 nEndIndex;
+ w.m_nPtIndex = aBndry.endPos;
+ w.AssignBack(pEndNode, nEndIndex);
+
+ if( aBndry.startPos != aBndry.endPos )
+ {
+ *GetPoint() = SwPosition(*pEndNode, nEndIndex);
+ if( !IsSelOvr() )
+ {
+ SetMark();
+ *GetMark() = SwPosition(*pStartNode, nStartIndex);
+ if (sw::mark::IMark* pAnnotationMark = pMarksAccess->getAnnotationMarkFor(*GetPoint()))
+ {
+ // An annotation mark covers the selected word. Check
+ // if it covers only the word: in that case we select
+ // the comment anchor as well.
+ bool bStartMatch = GetMark()->nNode == pAnnotationMark->GetMarkStart().nNode &&
+ GetMark()->nContent == pAnnotationMark->GetMarkStart().nContent;
+ bool bEndMatch = GetPoint()->nNode == pAnnotationMark->GetMarkEnd().nNode &&
+ GetPoint()->nContent.GetIndex() + 1 == pAnnotationMark->GetMarkEnd().nContent.GetIndex();
+ if (bStartMatch && bEndMatch)
+ ++GetPoint()->nContent;
+ }
+ if( !IsSelOvr() )
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ if( !bRet )
+ {
+ DeleteMark();
+ RestoreSavePos();
+ }
+ return bRet;
+}
+
+static OUString lcl_MaskDeletedRedlines( const SwTextNode* pTextNd )
+{
+ OUString aRes;
+ if (pTextNd)
+ {
+ //mask deleted redlines
+ OUString sNodeText(pTextNd->GetText());
+ const SwDoc& rDoc = pTextNd->GetDoc();
+ const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ if ( bShowChg )
+ {
+ SwRedlineTable::size_type nAct = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *pTextNd, RedlineType::Any );
+ for ( ; nAct < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); nAct++ )
+ {
+ const SwRangeRedline* pRed = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nAct ];
+ if ( pRed->Start()->nNode > pTextNd->GetIndex() )
+ break;
+
+ if( RedlineType::Delete == pRed->GetType() )
+ {
+ sal_Int32 nStart, nEnd;
+ pRed->CalcStartEnd( pTextNd->GetIndex(), nStart, nEnd );
+
+ while ( nStart < nEnd && nStart < sNodeText.getLength() )
+ sNodeText = sNodeText.replaceAt( nStart++, 1, rtl::OUStringChar(CH_TXTATR_INWORD) );
+ }
+ }
+ }
+ aRes = sNodeText;
+ }
+ return aRes;
+}
+
+bool SwCursor::GoSentence(SentenceMoveType eMoveType, SwRootFrame const*const pLayout)
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = GetNode().GetTextNode();
+ if (pTextNd)
+ {
+ OUString const sNodeText(lcl_MaskDeletedRedlines(pTextNd));
+
+ SwCursorSaveState aSave( *this );
+ sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pTextNd, nPtPos, &sNodeText);
+
+ switch ( eMoveType )
+ {
+ case START_SENT: /* when modifying: see also ExpandToSentenceBorders below! */
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
+ break;
+ case END_SENT: /* when modifying: see also ExpandToSentenceBorders below! */
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
+ break;
+ case NEXT_SENT:
+ {
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
+ if (w.m_nPtIndex >= 0 && w.m_nPtIndex < w.m_pText->getLength())
+ {
+ do
+ {
+ ++w.m_nPtIndex;
+ }
+ while (w.m_nPtIndex < w.m_pText->getLength()
+ && (*w.m_pText)[w.m_nPtIndex] == ' ');
+ }
+ break;
+ }
+ case PREV_SENT:
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
+
+ if (w.m_nPtIndex == 0)
+ return false; // the previous sentence is not in this paragraph
+ if (w.m_nPtIndex > 0)
+ {
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ *w.m_pText, w.m_nPtIndex - 1,
+ g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
+ }
+ break;
+ }
+ }
+
+ // it is allowed to place the PaM just behind the last
+ // character in the text thus <= ...Len
+ if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0)
+ {
+ *GetPoint() = SwPosition(*pTextNd, nPtPos);
+ if( !IsSelOvr() )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SwCursor::ExpandToSentenceBorders(SwRootFrame const*const pLayout)
+{
+ SwTextNode* pStartNd = Start()->nNode.GetNode().GetTextNode();
+ SwTextNode* pEndNd = End()->nNode.GetNode().GetTextNode();
+ if (!pStartNd || !pEndNd)
+ return;
+
+ if (!HasMark())
+ SetMark();
+
+ OUString sStartText( lcl_MaskDeletedRedlines( pStartNd ) );
+ OUString sEndText( pStartNd == pEndNd? sStartText : lcl_MaskDeletedRedlines( pEndNd ) );
+
+ SwCursorSaveState aSave( *this );
+ sal_Int32 nStartPos = Start()->nContent.GetIndex();
+ sal_Int32 nEndPos = End()->nContent.GetIndex();
+
+ {
+ HideWrapper w(pLayout, pStartNd, nStartPos, &sStartText);
+
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pStartNd->GetLang( nStartPos ) ) );
+ }
+ {
+ HideWrapper w(pLayout, pEndNd, nEndPos, &sEndText);
+
+ w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
+ *w.m_pText, w.m_nPtIndex,
+ g_pBreakIt->GetLocale( pEndNd->GetLang( nEndPos ) ) );
+ }
+
+ // it is allowed to place the PaM just behind the last
+ // character in the text thus <= ...Len
+ if (nStartPos <= pStartNd->GetText().getLength() && nStartPos >= 0)
+ {
+ *GetMark() = SwPosition(*pStartNd, nStartPos);
+ }
+ if (nEndPos <= pEndNd->GetText().getLength() && nEndPos >= 0)
+ {
+ *GetPoint() = SwPosition(*pEndNd, nEndPos);
+ }
+}
+
+bool SwTableCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 /*nMode*/,
+ bool /*bVisualAllowed*/, bool /*bSkipHidden*/, bool /*bInsertCursor*/,
+ SwRootFrame const*, bool /*isFieldNames*/)
+{
+ return bLeft ? GoPrevCell( nCnt )
+ : GoNextCell( nCnt );
+}
+
+// calculate cursor bidi level: extracted from LeftRight()
+const SwContentFrame*
+SwCursor::DoSetBidiLevelLeftRight(
+ bool & io_rbLeft, bool bVisualAllowed, bool bInsertCursor)
+{
+ // calculate cursor bidi level
+ const SwContentFrame* pSttFrame = nullptr;
+ SwNode& rNode = GetPoint()->nNode.GetNode();
+
+ if( rNode.IsTextNode() )
+ {
+ const SwTextNode& rTNd = *rNode.GetTextNode();
+ SwIndex& rIdx = GetPoint()->nContent;
+ sal_Int32 nPos = rIdx.GetIndex();
+
+ const SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions();
+ if ( bVisualAllowed && rCTLOptions.IsCTLFontEnabled() &&
+ SvtCTLOptions::MOVEMENT_VISUAL ==
+ rCTLOptions.GetCTLCursorMovement() )
+ {
+ // for visual cursor travelling (used in bidi layout)
+ // we first have to convert the logic to a visual position
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, true);
+ pSttFrame = rTNd.getLayoutFrame(
+ GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ GetPoint(), &tmp);
+ if( pSttFrame )
+ {
+ sal_uInt8 nCursorLevel = GetCursorBidiLevel();
+ bool bForward = ! io_rbLeft;
+ SwTextFrame *const pTF(const_cast<SwTextFrame*>(
+ static_cast<const SwTextFrame*>(pSttFrame)));
+ TextFrameIndex nTFIndex(pTF->MapModelToViewPos(*GetPoint()));
+ pTF->PrepareVisualMove( nTFIndex, nCursorLevel,
+ bForward, bInsertCursor );
+ *GetPoint() = pTF->MapViewToModelPos(nTFIndex);
+ SetCursorBidiLevel( nCursorLevel );
+ io_rbLeft = ! bForward;
+ }
+ }
+ else
+ {
+ SwTextFrame const* pFrame;
+ const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo(rTNd, &pFrame);
+ if ( pSI )
+ {
+ const sal_Int32 nMoveOverPos = io_rbLeft ?
+ ( nPos ? nPos - 1 : 0 ) :
+ nPos;
+ TextFrameIndex nIndex(pFrame->MapModelToView(&rTNd, nMoveOverPos));
+ SetCursorBidiLevel( pSI->DirType(nIndex) );
+ }
+ }
+ }
+ return pSttFrame;
+}
+
+bool SwCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode,
+ bool bVisualAllowed,bool bSkipHidden, bool bInsertCursor,
+ SwRootFrame const*const pLayout, bool isFieldNames)
+{
+ // calculate cursor bidi level
+ SwNode& rNode = GetPoint()->nNode.GetNode();
+ const SwContentFrame* pSttFrame = // may side-effect bLeft!
+ DoSetBidiLevelLeftRight(bLeft, bVisualAllowed, bInsertCursor);
+
+ // can the cursor be moved n times?
+ SwCursorSaveState aSave( *this );
+ SwMoveFnCollection const & fnMove = bLeft ? fnMoveBackward : fnMoveForward;
+
+ SwGoInDoc fnGo;
+ if ( bSkipHidden )
+ fnGo = CRSR_SKIP_CELLS == nMode ? GoInContentCellsSkipHidden : GoInContentSkipHidden;
+ else
+ fnGo = CRSR_SKIP_CELLS == nMode ? GoInContentCells : GoInContent;
+
+ SwTextFrame const* pFrame(nullptr);
+ if (pLayout)
+ {
+ pFrame = static_cast<SwTextFrame*>(rNode.GetContentNode()->getLayoutFrame(pLayout));
+ if (pFrame)
+ {
+ while (pFrame->GetPrecede())
+ {
+ pFrame = static_cast<SwTextFrame const*>(pFrame->GetPrecede());
+ }
+ }
+ }
+
+ while( nCnt )
+ {
+ SwNodeIndex aOldNodeIdx( GetPoint()->nNode );
+
+ TextFrameIndex beforeIndex(-1);
+ if (pFrame)
+ {
+ beforeIndex = pFrame->MapModelToViewPos(*GetPoint());
+ }
+
+ if (!bLeft && pLayout && pLayout->GetFieldmarkMode() == sw::FieldmarkMode::ShowResult)
+ {
+ SwTextNode const*const pNode(GetPoint()->nNode.GetNode().GetTextNode());
+ assert(pNode);
+ if (pNode->Len() != GetPoint()->nContent.GetIndex()
+ && pNode->GetText()[GetPoint()->nContent.GetIndex()] == CH_TXT_ATR_FIELDSTART)
+ {
+ IDocumentMarkAccess const& rIDMA(*GetDoc().getIDocumentMarkAccess());
+ sw::mark::IFieldmark const*const pMark(rIDMA.getFieldmarkAt(*GetPoint()));
+ assert(pMark);
+ *GetPoint() = sw::mark::FindFieldSep(*pMark);
+ }
+ }
+
+ if ( !Move( fnMove, fnGo ) )
+ {
+ const SwEditShell* pSh = GetDoc().GetEditShell();
+ const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr;
+ if (pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton())
+ {
+ // Fixes crash that occurs in documents with outline content folded at the end of
+ // the document. When the cursor is at the end of the visible document and
+ // right arrow key is pressed Move fails after moving the cursor to the
+ // end of the document model, which doesn't have a node frame and causes
+ // weird numbers to be displayed in the statusbar page number count. Left
+ // arrow, when in this state, causes a crash without RestoredSavePos() added here.
+ RestoreSavePos();
+ }
+ break;
+ }
+
+ if (pFrame)
+ {
+ SwTextFrame const* pNewFrame(static_cast<SwTextFrame const*>(
+ GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame(pLayout)));
+ if (pNewFrame)
+ {
+ while (pNewFrame->GetPrecede())
+ {
+ pNewFrame = static_cast<SwTextFrame const*>(pNewFrame->GetPrecede());
+ }
+ }
+ // sw_redlinehide: fully redline-deleted nodes don't have frames...
+ if (pFrame == pNewFrame || !pNewFrame)
+ {
+ if (!pNewFrame || beforeIndex == pFrame->MapModelToViewPos(*GetPoint()))
+ {
+ continue; // moving inside delete redline, doesn't count...
+ }
+ }
+ else
+ {
+ // assume iteration is stable & returns the same frame
+ assert(!pFrame->IsAnFollow(pNewFrame) && !pNewFrame->IsAnFollow(pFrame));
+ pFrame = pNewFrame;
+ }
+ }
+
+ if (bLeft && pLayout && pLayout->GetFieldmarkMode() == sw::FieldmarkMode::ShowCommand)
+ {
+ SwTextNode const*const pNode(GetPoint()->nNode.GetNode().GetTextNode());
+ assert(pNode);
+ if (pNode->Len() != GetPoint()->nContent.GetIndex()
+ && pNode->GetText()[GetPoint()->nContent.GetIndex()] == CH_TXT_ATR_FIELDEND)
+ {
+ IDocumentMarkAccess const& rIDMA(*GetDoc().getIDocumentMarkAccess());
+ sw::mark::IFieldmark const*const pMark(rIDMA.getFieldmarkAt(*GetPoint()));
+ assert(pMark);
+ *GetPoint() = sw::mark::FindFieldSep(*pMark);
+ }
+ }
+
+ if (isFieldNames)
+ {
+ SwTextNode const*const pNode(GetPoint()->nNode.GetNode().GetTextNode());
+ assert(pNode);
+ SwTextAttr const*const pInputField(pNode->GetTextAttrAt(
+ GetPoint()->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT));
+ if (pInputField)
+ {
+ continue; // skip over input fields
+ }
+ }
+
+ // If we were located inside a covered cell but our position has been
+ // corrected, we check if the last move has moved the cursor to a
+ // different table cell. In this case we set the cursor to the stored
+ // covered position and redo the move:
+ if (m_nRowSpanOffset)
+ {
+ const SwNode* pOldTabBoxSttNode = aOldNodeIdx.GetNode().FindTableBoxStartNode();
+ const SwTableNode* pOldTabSttNode = pOldTabBoxSttNode ? pOldTabBoxSttNode->FindTableNode() : nullptr;
+ const SwNode* pNewTabBoxSttNode = GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ const SwTableNode* pNewTabSttNode = pNewTabBoxSttNode ? pNewTabBoxSttNode->FindTableNode() : nullptr;
+
+ const bool bCellChanged = pOldTabSttNode && pNewTabSttNode &&
+ pOldTabSttNode == pNewTabSttNode &&
+ pOldTabBoxSttNode && pNewTabBoxSttNode &&
+ pOldTabBoxSttNode != pNewTabBoxSttNode;
+
+ if ( bCellChanged )
+ {
+ // Set cursor to start/end of covered cell:
+ SwTableBox* pTableBox = pOldTabBoxSttNode->GetTableBox();
+ if ( pTableBox && pTableBox->getRowSpan() > 1 )
+ {
+ pTableBox = & pTableBox->FindEndOfRowSpan(
+ pOldTabSttNode->GetTable(),
+ o3tl::narrowing<sal_uInt16>(pTableBox->getRowSpan() + m_nRowSpanOffset));
+ SwNodeIndex& rPtIdx = GetPoint()->nNode;
+ SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
+ rPtIdx = aNewIdx;
+
+ GetDoc().GetNodes().GoNextSection( &rPtIdx, false, false );
+ SwContentNode* pContentNode = GetContentNode();
+ if ( pContentNode )
+ {
+ GetPoint()->nContent.Assign( pContentNode, bLeft ? pContentNode->Len() : 0 );
+
+ // Redo the move:
+ if ( !Move( fnMove, fnGo ) )
+ break;
+ }
+ }
+ m_nRowSpanOffset = 0;
+ }
+ }
+
+ // Check if I'm inside a covered cell. Correct cursor if necessary and
+ // store covered cell:
+ const SwNode* pTableBoxStartNode = GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ if ( pTableBoxStartNode )
+ {
+ const SwTableBox* pTableBox = pTableBoxStartNode->GetTableBox();
+ if ( pTableBox && pTableBox->getRowSpan() < 1 )
+ {
+ // Store the row span offset:
+ m_nRowSpanOffset = pTableBox->getRowSpan();
+
+ // Move cursor to non-covered cell:
+ const SwTableNode* pTableNd = pTableBoxStartNode->FindTableNode();
+ pTableBox = & pTableBox->FindStartOfRowSpan( pTableNd->GetTable() );
+ SwNodeIndex& rPtIdx = GetPoint()->nNode;
+ SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
+ rPtIdx = aNewIdx;
+
+ GetDoc().GetNodes().GoNextSection( &rPtIdx, false, false );
+ SwContentNode* pContentNode = GetContentNode();
+ if ( pContentNode )
+ {
+ GetPoint()->nContent.Assign( pContentNode, bLeft ? pContentNode->Len() : 0 );
+ }
+ }
+ }
+ --nCnt;
+ }
+
+ // here come some special rules for visual cursor travelling
+ if ( pSttFrame )
+ {
+ SwNode& rTmpNode = GetPoint()->nNode.GetNode();
+ if ( &rTmpNode != &rNode && rTmpNode.IsTextNode() )
+ {
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, true);
+ const SwContentFrame* pEndFrame = rTmpNode.GetTextNode()->getLayoutFrame(
+ GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ GetPoint(), &tmp);
+ if ( pEndFrame )
+ {
+ if ( ! pEndFrame->IsRightToLeft() != ! pSttFrame->IsRightToLeft() )
+ {
+ if ( ! bLeft )
+ pEndFrame->RightMargin( this );
+ else
+ pEndFrame->LeftMargin( this );
+ }
+ }
+ }
+ }
+
+ return 0 == nCnt && !IsInProtectTable( true ) &&
+ !IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos );
+}
+
+// calculate cursor bidi level: extracted from UpDown()
+void SwCursor::DoSetBidiLevelUpDown()
+{
+ SwNode& rNode = GetPoint()->nNode.GetNode();
+ if ( !rNode.IsTextNode() )
+ return;
+
+ SwTextFrame const* pFrame;
+ const SwScriptInfo* pSI =
+ SwScriptInfo::GetScriptInfo( *rNode.GetTextNode(), &pFrame );
+ if ( !pSI )
+ return;
+
+ SwIndex& rIdx = GetPoint()->nContent;
+ const sal_Int32 nPos = rIdx.GetIndex();
+
+ if (!(nPos && nPos < rNode.GetTextNode()->GetText().getLength()))
+ return;
+
+ TextFrameIndex const nIndex(pFrame->MapModelToView(rNode.GetTextNode(), nPos));
+ const sal_uInt8 nCurrLevel = pSI->DirType( nIndex );
+ const sal_uInt8 nPrevLevel = pSI->DirType( nIndex - TextFrameIndex(1) );
+
+ if ( nCurrLevel % 2 != nPrevLevel % 2 )
+ {
+ // set cursor level to the lower of the two levels
+ SetCursorBidiLevel( std::min( nCurrLevel, nPrevLevel ) );
+ }
+ else
+ SetCursorBidiLevel( nCurrLevel );
+}
+
+bool SwCursor::UpDown( bool bUp, sal_uInt16 nCnt,
+ Point const * pPt, tools::Long nUpDownX,
+ SwRootFrame & rLayout)
+{
+ SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
+ bool bAdjustTableCursor = false;
+
+ // If the point/mark of the table cursor in the same box then set cursor to
+ // beginning of the box
+ if( pTableCursor && GetNode().StartOfSectionNode() ==
+ GetNode( false ).StartOfSectionNode() )
+ {
+ if ( End() != GetPoint() )
+ Exchange();
+ bAdjustTableCursor = true;
+ }
+
+ bool bRet = false;
+ Point aPt;
+ if( pPt )
+ aPt = *pPt;
+ std::pair<Point, bool> const temp(aPt, true);
+ SwContentFrame* pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &temp);
+
+ if( pFrame )
+ {
+ SwCursorSaveState aSave( *this );
+
+ if( !pPt )
+ {
+ SwRect aTmpRect;
+ pFrame->GetCharRect( aTmpRect, *GetPoint() );
+ aPt = aTmpRect.Pos();
+
+ nUpDownX = pFrame->IsVertical() ?
+ aPt.getY() - pFrame->getFrameArea().Top() :
+ aPt.getX() - pFrame->getFrameArea().Left();
+ }
+
+ // It is allowed to move footnotes in other footnotes but not sections
+ const bool bChkRange = !pFrame->IsInFootnote() || HasMark();
+ const SwPosition aOldPos( *GetPoint() );
+ const bool bInReadOnly = IsReadOnlyAvailable();
+
+ if ( bAdjustTableCursor && !bUp )
+ {
+ // Special case: We have a table cursor but the start box has more
+ // than one paragraph. If we want to go down, we have to set the
+ // point to the last frame in the table box. This is only necessary
+ // if we do not already have a table selection
+ const SwStartNode* pTableNd = GetNode().FindTableBoxStartNode();
+ OSL_ENSURE( pTableNd, "pTableCursor without SwTableNode?" );
+
+ if ( pTableNd ) // safety first
+ {
+ const SwNode* pEndNd = pTableNd->EndOfSectionNode();
+ GetPoint()->nNode = *pEndNd;
+ pTableCursor->Move( fnMoveBackward, GoInNode );
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp);
+ }
+ }
+
+ while( nCnt &&
+ (bUp ? pFrame->UnitUp( this, nUpDownX, bInReadOnly )
+ : pFrame->UnitDown( this, nUpDownX, bInReadOnly ) ) &&
+ CheckNodesRange( aOldPos.nNode, GetPoint()->nNode, bChkRange ))
+ {
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp);
+ --nCnt;
+ }
+
+ // iterate over whole number of items?
+ if( !nCnt && !IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos ) )
+ {
+ if( !pTableCursor )
+ {
+ // try to position the cursor at half of the char-rect's height
+ DisableCallbackAction a(rLayout);
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp);
+ SwCursorMoveState eTmpState( CursorMoveState::UpDown );
+ eTmpState.m_bSetInReadOnly = bInReadOnly;
+ SwRect aTmpRect;
+ pFrame->GetCharRect( aTmpRect, *GetPoint(), &eTmpState );
+ if ( pFrame->IsVertical() )
+ {
+ aPt.setX(aTmpRect.Center().getX());
+ pFrame->Calc(rLayout.GetCurrShell()->GetOut());
+ aPt.setY(pFrame->getFrameArea().Top() + nUpDownX);
+ }
+ else
+ {
+ aPt.setY(aTmpRect.Center().getY());
+ pFrame->Calc(rLayout.GetCurrShell()->GetOut());
+ aPt.setX(pFrame->getFrameArea().Left() + nUpDownX);
+ }
+ pFrame->GetModelPositionForViewPoint( GetPoint(), aPt, &eTmpState );
+ }
+ bRet = !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos );
+ }
+ else if (!pFrame->IsInFootnote()) // tdf#150457 Jump to the begin/end
+ // of the first/last line only if the
+ // cursor is not inside a footenote
+ {
+ sal_Int32 nOffset = 0;
+
+ // Jump to beginning or end of line when the cursor at first or last line.
+ if(!bUp)
+ {
+ SwTextNode* pTextNd = GetPoint()->nNode.GetNode().GetTextNode();
+ if (pTextNd)
+ nOffset = pTextNd->GetText().getLength();
+ }
+ const SwPosition aPos(*GetContentNode(), nOffset);
+
+ //if cursor has already been at start or end of file,
+ //Update cursor to change nUpDownX.
+ if ( aOldPos.nContent.GetIndex() == nOffset )
+ {
+ if (SwEditShell* pSh = GetDoc().GetEditShell())
+ pSh->UpdateCursor();
+ bRet = false;
+ }
+ else{
+ *GetPoint() = aPos; // just give a new position
+ bRet = true;
+ }
+
+ }
+ else
+ *GetPoint() = aOldPos;
+
+ DoSetBidiLevelUpDown(); // calculate cursor bidi level
+ }
+ return bRet;
+}
+
+bool SwCursor::LeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI)
+{
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, true);
+ SwContentFrame const*const pFrame = GetContentNode()->getLayoutFrame(
+ &rLayout, GetPoint(), &tmp);
+
+ // calculate cursor bidi level
+ if ( pFrame )
+ SetCursorBidiLevel( pFrame->IsRightToLeft() ? 1 : 0 );
+
+ SwCursorSaveState aSave( *this );
+ return pFrame
+ && (bLeft ? pFrame->LeftMargin( this ) : pFrame->RightMargin( this, bAPI ) )
+ && !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos );
+}
+
+bool SwCursor::IsAtLeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI) const
+{
+ bool bRet = false;
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, true);
+ SwContentFrame const*const pFrame = GetContentNode()->getLayoutFrame(
+ &rLayout, GetPoint(), &tmp);
+ if( pFrame )
+ {
+ SwPaM aPam( *GetPoint() );
+ if( !bLeft && aPam.GetPoint()->nContent.GetIndex() )
+ --aPam.GetPoint()->nContent;
+ bRet = (bLeft ? pFrame->LeftMargin( &aPam )
+ : pFrame->RightMargin( &aPam, bAPI ))
+ && (!pFrame->IsTextFrame()
+ || static_cast<SwTextFrame const*>(pFrame)->MapModelToViewPos(*aPam.GetPoint())
+ == static_cast<SwTextFrame const*>(pFrame)->MapModelToViewPos(*GetPoint()));
+ }
+ return bRet;
+}
+
+bool SwCursor::SttEndDoc( bool bStt )
+{
+ SwCursorSaveState aSave( *this );
+ // Never jump over section boundaries during selection!
+ // Can the cursor still moved on?
+ SwMoveFnCollection const & fnMove = bStt ? fnMoveBackward : fnMoveForward;
+ bool bRet = (!HasMark() || !IsNoContent() ) &&
+ Move( fnMove, GoInDoc ) &&
+ !IsInProtectTable( true ) &&
+ !IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos |
+ SwCursorSelOverFlags::EnableRevDirection );
+ return bRet;
+}
+
+bool SwCursor::GoPrevNextCell( bool bNext, sal_uInt16 nCnt )
+{
+ const SwTableNode* pTableNd = GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return false;
+
+ // If there is another EndNode in front of the cell's StartNode then there
+ // exists a previous cell
+ SwCursorSaveState aSave( *this );
+ SwNodeIndex& rPtIdx = GetPoint()->nNode;
+
+ while( nCnt-- )
+ {
+ const SwNode* pTableBoxStartNode = rPtIdx.GetNode().FindTableBoxStartNode();
+ const SwTableBox* pTableBox = pTableBoxStartNode->GetTableBox();
+
+ // Check if we have to move the cursor to a covered cell before
+ // proceeding:
+ if (m_nRowSpanOffset)
+ {
+ if ( pTableBox && pTableBox->getRowSpan() > 1 )
+ {
+ pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(),
+ o3tl::narrowing<sal_uInt16>(pTableBox->getRowSpan() + m_nRowSpanOffset));
+ SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
+ rPtIdx = aNewIdx;
+ pTableBoxStartNode = rPtIdx.GetNode().FindTableBoxStartNode();
+ }
+ m_nRowSpanOffset = 0;
+ }
+
+ const SwNode* pTmpNode = bNext ?
+ pTableBoxStartNode->EndOfSectionNode() :
+ pTableBoxStartNode;
+
+ SwNodeIndex aCellIdx( *pTmpNode, bNext ? 1 : -1 );
+ if( (bNext && !aCellIdx.GetNode().IsStartNode()) ||
+ (!bNext && !aCellIdx.GetNode().IsEndNode()) )
+ return false;
+
+ if (bNext)
+ rPtIdx = aCellIdx;
+ else
+ rPtIdx.Assign(*aCellIdx.GetNode().StartOfSectionNode());
+
+ pTableBoxStartNode = rPtIdx.GetNode().FindTableBoxStartNode();
+ pTableBox = pTableBoxStartNode->GetTableBox();
+ if ( pTableBox && pTableBox->getRowSpan() < 1 )
+ {
+ m_nRowSpanOffset = pTableBox->getRowSpan();
+ // move cursor to non-covered cell:
+ pTableBox = & pTableBox->FindStartOfRowSpan( pTableNd->GetTable() );
+ SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
+ rPtIdx = aNewIdx;
+ }
+ }
+
+ ++rPtIdx;
+ if( !rPtIdx.GetNode().IsContentNode() )
+ GetDoc().GetNodes().GoNextSection( &rPtIdx, true, false );
+ GetPoint()->nContent.Assign( GetContentNode(), 0 );
+
+ return !IsInProtectTable( true );
+}
+
+bool SwTableCursor::GotoTable( const OUString& )
+{
+ return false; // invalid action
+}
+
+bool SwCursor::GotoTable( const OUString& rName )
+{
+ bool bRet = false;
+ if ( !HasMark() )
+ {
+ SwTable* pTmpTable = SwTable::FindTable( GetDoc().FindTableFormatByName( rName ) );
+ if( pTmpTable )
+ {
+ // a table in a normal nodes array
+ SwCursorSaveState aSave( *this );
+ GetPoint()->nNode = *pTmpTable->GetTabSortBoxes()[ 0 ]->
+ GetSttNd()->FindTableNode();
+ Move( fnMoveForward, GoInContent );
+ bRet = !IsSelOvr();
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::GotoTableBox( const OUString& rName )
+{
+ bool bRet = false;
+ const SwTableNode* pTableNd = GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ // retrieve box by name
+ const SwTableBox* pTableBox = pTableNd->GetTable().GetTableBox( rName );
+ if( pTableBox && pTableBox->GetSttNd() &&
+ ( !pTableBox->GetFrameFormat()->GetProtect().IsContentProtected() ||
+ IsReadOnlyAvailable() ) )
+ {
+ SwCursorSaveState aSave( *this );
+ GetPoint()->nNode = *pTableBox->GetSttNd();
+ Move( fnMoveForward, GoInContent );
+ bRet = !IsSelOvr();
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & fnPosPara )
+{
+ // for optimization test something before
+ const SwNode* pNd = &GetPoint()->nNode.GetNode();
+ bool bShortCut = false;
+ if ( fnWhichPara == GoCurrPara )
+ {
+ // #i41048#
+ // If fnWhichPara == GoCurrPara then (*fnWhichPara)( *this, fnPosPara )
+ // can already move the cursor to a different text node. In this case
+ // we better check if IsSelOvr().
+ const SwContentNode* pContentNd = pNd->GetContentNode();
+ if ( pContentNd )
+ {
+ const sal_Int32 nSttEnd = &fnPosPara == &fnMoveForward ? 0 : pContentNd->Len();
+ if ( GetPoint()->nContent.GetIndex() != nSttEnd )
+ bShortCut = true;
+ }
+ }
+ else
+ {
+ if ( pNd->IsTextNode() &&
+ pNd->GetNodes()[ pNd->GetIndex() +
+ SwNodeOffset(fnWhichPara == GoNextPara ? 1 : -1 ) ]->IsTextNode() )
+ bShortCut = true;
+ }
+
+ if ( bShortCut )
+ return (*fnWhichPara)( *this, fnPosPara );
+
+ // else we must use the SaveStructure, because the next/prev is not
+ // a same node type.
+ SwCursorSaveState aSave( *this );
+ return (*fnWhichPara)( *this, fnPosPara ) &&
+ !IsInProtectTable( true ) &&
+ !IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos );
+}
+
+bool SwCursor::MoveSection( SwWhichSection fnWhichSect,
+ SwMoveFnCollection const & fnPosSect)
+{
+ SwCursorSaveState aSave( *this );
+ return (*fnWhichSect)( *this, fnPosSect ) &&
+ !IsInProtectTable( true ) &&
+ !IsSelOvr( SwCursorSelOverFlags::Toggle |
+ SwCursorSelOverFlags::ChangePos );
+}
+
+void SwCursor::RestoreSavePos()
+{
+ // This method is not supposed to be used in cases when nodes may be
+ // deleted; detect such cases, but do not crash (example: fdo#40831).
+ SwNodeOffset uNodeCount(GetPoint()->nNode.GetNodes().Count());
+ OSL_ENSURE(m_vSavePos.empty() || m_vSavePos.back().nNode < uNodeCount,
+ "SwCursor::RestoreSavePos: invalid node: "
+ "probably something was deleted; consider using SwUnoCursor instead");
+ if (m_vSavePos.empty() || m_vSavePos.back().nNode >= uNodeCount)
+ return;
+
+ GetPoint()->nNode = m_vSavePos.back().nNode;
+
+ sal_Int32 nIdx = 0;
+ if ( GetContentNode() )
+ {
+ if (m_vSavePos.back().nContent <= GetContentNode()->Len())
+ nIdx = m_vSavePos.back().nContent;
+ else
+ {
+ nIdx = GetContentNode()->Len();
+ OSL_FAIL("SwCursor::RestoreSavePos: invalid content index");
+ }
+ }
+ GetPoint()->nContent.Assign( GetContentNode(), nIdx );
+}
+
+SwTableCursor::SwTableCursor( const SwPosition &rPos )
+ : SwCursor( rPos, nullptr )
+{
+ m_bParked = false;
+ m_bChanged = false;
+ m_nTablePtNd = SwNodeOffset(0);
+ m_nTableMkNd = SwNodeOffset(0);
+ m_nTablePtCnt = 0;
+ m_nTableMkCnt = 0;
+}
+
+SwTableCursor::~SwTableCursor() {}
+
+static bool
+lcl_SeekEntry(const SwSelBoxes& rTmp, SwStartNode const*const pSrch,
+ size_t & o_rFndPos)
+{
+ SwNodeOffset nIdx = pSrch->GetIndex();
+
+ size_t nO = rTmp.size();
+ if( nO > 0 )
+ {
+ nO--;
+ size_t nU = 0;
+ while( nU <= nO )
+ {
+ size_t nM = nU + ( nO - nU ) / 2;
+ if( rTmp[ nM ]->GetSttNd() == pSrch )
+ {
+ o_rFndPos = nM;
+ return true;
+ }
+ else if( rTmp[ nM ]->GetSttIdx() < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ return false;
+ else
+ nO = nM - 1;
+ }
+ }
+ return false;
+}
+
+SwCursor* SwTableCursor::MakeBoxSels( SwCursor* pCurrentCursor )
+{
+ if (m_bChanged)
+ {
+ if (m_bParked)
+ {
+ // move back into content
+ Exchange();
+ Move( fnMoveForward );
+ Exchange();
+ Move( fnMoveForward );
+ m_bParked = false;
+ }
+
+ m_bChanged = false;
+
+ // create temporary copies so that all boxes that
+ // have already cursors can be removed
+ SwSelBoxes aTmp(m_SelectedBoxes);
+
+ // compare old and new ones
+ SwNodes& rNds = pCurrentCursor->GetDoc().GetNodes();
+ const SwStartNode* pSttNd;
+ SwPaM* pCur = pCurrentCursor;
+ do {
+ size_t nPos;
+ bool bDel = false;
+ pSttNd = pCur->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ if( !pCur->HasMark() || !pSttNd ||
+ pSttNd != pCur->GetMark()->nNode.GetNode().FindTableBoxStartNode() )
+ bDel = true;
+
+ else if( lcl_SeekEntry( aTmp, pSttNd, nPos ))
+ {
+ SwNodeIndex aIdx( *pSttNd, 1 );
+ const SwNode* pNd = &aIdx.GetNode();
+ if( !pNd->IsContentNode() )
+ pNd = rNds.GoNextSection( &aIdx, true, false );
+
+ SwPosition* pPos = pCur->GetMark();
+ if( pNd != &pPos->nNode.GetNode() )
+ pPos->nNode = *pNd;
+ pPos->nContent.Assign( const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd)), 0 );
+
+ aIdx.Assign( *pSttNd->EndOfSectionNode(), - 1 );
+ pNd = &aIdx.GetNode();
+ if( !pNd->IsContentNode() )
+ pNd = SwNodes::GoPrevSection( &aIdx, true, false );
+
+ pPos = pCur->GetPoint();
+ if (pNd && pNd != &pPos->nNode.GetNode())
+ pPos->nNode = *pNd;
+ pPos->nContent.Assign(const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd)), pNd ? static_cast<const SwContentNode*>(pNd)->Len() : 0);
+
+ aTmp.erase( aTmp.begin() + nPos );
+ }
+ else
+ bDel = true;
+
+ pCur = pCur->GetNext();
+ if( bDel )
+ {
+ SwPaM* pDel = pCur->GetPrev();
+
+ if( pDel == pCurrentCursor )
+ pCurrentCursor->DeleteMark();
+ else
+ delete pDel;
+ }
+ } while ( pCurrentCursor != pCur );
+
+ for (size_t nPos = 0; nPos < aTmp.size(); ++nPos)
+ {
+ pSttNd = aTmp[ nPos ]->GetSttNd();
+
+ SwNodeIndex aIdx( *pSttNd, 1 );
+ if( &aIdx.GetNodes() != &rNds )
+ break;
+ SwNode* pNd = &aIdx.GetNode();
+ if( !pNd->IsContentNode() )
+ pNd = rNds.GoNextSection( &aIdx, true, false );
+
+ SwPaM *const pNew = (!pCurrentCursor->IsMultiSelection() && !pCurrentCursor->HasMark())
+ ? pCurrentCursor
+ : pCurrentCursor->Create( pCurrentCursor );
+ pNew->GetPoint()->nNode = *pNd;
+ pNew->GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pNd), 0 );
+ pNew->SetMark();
+
+ SwPosition* pPos = pNew->GetPoint();
+ pPos->nNode.Assign( *pSttNd->EndOfSectionNode(), - 1 );
+ pNd = &pPos->nNode.GetNode();
+ if( !pNd->IsContentNode() )
+ pNd = SwNodes::GoPrevSection( &pPos->nNode, true, false );
+
+ pPos->nContent.Assign(static_cast<SwContentNode*>(pNd), pNd ? static_cast<SwContentNode*>(pNd)->Len() : 0);
+ }
+ }
+ return pCurrentCursor;
+}
+
+void SwTableCursor::InsertBox( const SwTableBox& rTableBox )
+{
+ SwTableBox* pBox = const_cast<SwTableBox*>(&rTableBox);
+ m_SelectedBoxes.insert(pBox);
+ m_bChanged = true;
+}
+
+void SwTableCursor::DeleteBox(size_t const nPos)
+{
+ m_SelectedBoxes.erase(m_SelectedBoxes.begin() + nPos);
+ m_bChanged = true;
+}
+
+bool SwTableCursor::NewTableSelection()
+{
+ bool bRet = false;
+ const SwNode *pStart = GetNode().FindTableBoxStartNode();
+ const SwNode *pEnd = GetNode(false).FindTableBoxStartNode();
+ if( pStart && pEnd )
+ {
+ const SwTableNode *pTableNode = pStart->FindTableNode();
+ if( pTableNode == pEnd->FindTableNode() &&
+ pTableNode->GetTable().IsNewModel() )
+ {
+ bRet = true;
+ SwSelBoxes aNew(m_SelectedBoxes);
+ pTableNode->GetTable().CreateSelection( pStart, pEnd, aNew,
+ SwTable::SEARCH_NONE, false );
+ ActualizeSelection( aNew );
+ }
+ }
+ return bRet;
+}
+
+void SwTableCursor::ActualizeSelection( const SwSelBoxes &rNew )
+{
+ size_t nOld = 0, nNew = 0;
+ while (nOld < m_SelectedBoxes.size() && nNew < rNew.size())
+ {
+ SwTableBox const*const pPOld = m_SelectedBoxes[ nOld ];
+ const SwTableBox* pPNew = rNew[ nNew ];
+ if( pPOld == pPNew )
+ { // this box will stay
+ ++nOld;
+ ++nNew;
+ }
+ else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
+ {
+ DeleteBox( nOld ); // this box has to go
+ }
+ else
+ {
+ InsertBox( *pPNew ); // this is a new one
+ ++nOld;
+ ++nNew;
+ }
+ }
+
+ while (nOld < m_SelectedBoxes.size())
+ {
+ DeleteBox( nOld ); // some more to delete
+ }
+
+ for ( ; nNew < rNew.size(); ++nNew ) // some more to insert
+ {
+ InsertBox( *rNew[ nNew ] );
+ }
+}
+
+bool SwTableCursor::IsCursorMovedUpdate()
+{
+ if( !IsCursorMoved() )
+ return false;
+
+ m_nTableMkNd = GetMark()->nNode.GetIndex();
+ m_nTablePtNd = GetPoint()->nNode.GetIndex();
+ m_nTableMkCnt = GetMark()->nContent.GetIndex();
+ m_nTablePtCnt = GetPoint()->nContent.GetIndex();
+ return true;
+}
+
+/// park table cursor on the boxes' start node
+void SwTableCursor::ParkCursor()
+{
+ // de-register index from text node
+ SwNode* pNd = &GetPoint()->nNode.GetNode();
+ if( !pNd->IsStartNode() )
+ pNd = pNd->StartOfSectionNode();
+ GetPoint()->nNode = *pNd;
+ GetPoint()->nContent.Assign( nullptr, 0 );
+
+ pNd = &GetMark()->nNode.GetNode();
+ if( !pNd->IsStartNode() )
+ pNd = pNd->StartOfSectionNode();
+ GetMark()->nNode = *pNd;
+ GetMark()->nContent.Assign( nullptr, 0 );
+
+ m_bChanged = true;
+ m_bParked = true;
+}
+
+bool SwTableCursor::HasReadOnlyBoxSel() const
+{
+ bool bRet = false;
+ for (size_t n = m_SelectedBoxes.size(); n; )
+ {
+ if (m_SelectedBoxes[--n]->GetFrameFormat()->GetProtect().IsContentProtected())
+ {
+ bRet = true;
+ break;
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/trvlcol.cxx b/sw/source/core/crsr/trvlcol.cxx
new file mode 100644
index 000000000..c680e5a87
--- /dev/null
+++ b/sw/source/core/crsr/trvlcol.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <crsrsh.hxx>
+#include <layfrm.hxx>
+#include <cntfrm.hxx>
+#include <swcrsr.hxx>
+#include <viscrs.hxx>
+#include "callnk.hxx"
+
+SwLayoutFrame* GetCurrColumn( const SwLayoutFrame* pLayFrame )
+{
+ while( pLayFrame && !pLayFrame->IsColumnFrame() )
+ pLayFrame = pLayFrame->GetUpper();
+ return const_cast<SwLayoutFrame*>(pLayFrame);
+}
+
+SwLayoutFrame* GetNextColumn( const SwLayoutFrame* pLayFrame )
+{
+ SwLayoutFrame* pActCol = GetCurrColumn( pLayFrame );
+ return pActCol ? static_cast<SwLayoutFrame*>(pActCol->GetNext()) : nullptr;
+}
+
+SwLayoutFrame* GetPrevColumn( const SwLayoutFrame* pLayFrame )
+{
+ SwLayoutFrame* pActCol = GetCurrColumn( pLayFrame );
+ return pActCol ? static_cast<SwLayoutFrame*>(pActCol->GetPrev()) : nullptr;
+}
+
+SwContentFrame* GetColumnStt( const SwLayoutFrame* pColFrame )
+{
+ return pColFrame ? const_cast<SwContentFrame*>(pColFrame->ContainsContent()) : nullptr;
+}
+
+SwContentFrame* GetColumnEnd( const SwLayoutFrame* pColFrame )
+{
+ SwContentFrame *pRet = GetColumnStt( pColFrame );
+ if( !pRet )
+ return nullptr;
+
+ SwContentFrame *pNxt = pRet->GetNextContentFrame();
+ while( pNxt && pColFrame->IsAnLower( pNxt ) )
+ {
+ pRet = pNxt;
+ pNxt = pNxt->GetNextContentFrame();
+ }
+ return pRet;
+}
+
+void SwCursorShell::MoveColumn( SwWhichColumn fnWhichCol, SwPosColumn fnPosCol )
+{
+ if( m_pTableCursor )
+ return;
+ SwLayoutFrame* pLayFrame = GetCurrFrame()->GetUpper();
+ if( !pLayFrame )
+ return;
+ pLayFrame = (*fnWhichCol)( pLayFrame );
+ if( !pLayFrame )
+ return;
+
+ SwContentFrame* pCnt = (*fnPosCol)( pLayFrame );
+ if( !pCnt )
+ return;
+
+ CurrShell aCurr( this );
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ pCnt->Calc(GetOut());
+
+ Point aPt( pCnt->getFrameArea().Pos() + pCnt->getFramePrintArea().Pos() );
+ if( fnPosCol == GetColumnEnd )
+ {
+ aPt.setX(aPt.getX() + pCnt->getFramePrintArea().Width());
+ aPt.setY(aPt.getY() + pCnt->getFramePrintArea().Height());
+ }
+
+ pCnt->GetModelPositionForViewPoint( m_pCurrentCursor->GetPoint(), aPt );
+
+ if( !m_pCurrentCursor->IsInProtectTable( true ) &&
+ !m_pCurrentCursor->IsSelOvr() )
+ {
+ UpdateCursor();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/trvlfnfl.cxx b/sw/source/core/crsr/trvlfnfl.cxx
new file mode 100644
index 000000000..b6477cc60
--- /dev/null
+++ b/sw/source/core/crsr/trvlfnfl.cxx
@@ -0,0 +1,388 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <ftnfrm.hxx>
+#include <swcrsr.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <txtftn.hxx>
+#include <ftnidx.hxx>
+#include <viscrs.hxx>
+#include "callnk.hxx"
+#include <svx/srchdlg.hxx>
+#include <wrtsh.hxx>
+
+bool SwCursorShell::CallCursorShellFN( FNCursorShell fnCursor )
+{
+ if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
+ pWrtSh->addCurrentPosition();
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ bool bRet = (this->*fnCursor)();
+ if( bRet )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ return bRet;
+}
+
+bool SwCursorShell::CallCursorFN( FNCursor fnCursor )
+{
+ if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
+ pWrtSh->addCurrentPosition();
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursor* pCursor = getShellCursor( true );
+ bool bRet = (pCursor->*fnCursor)();
+ if( bRet )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ return bRet;
+}
+
+bool SwCursor::GotoFootnoteText()
+{
+ // jump from content to footnote
+ bool bRet = false;
+ SwTextNode* pTextNd = GetPoint()->nNode.GetNode().GetTextNode();
+
+ SwTextAttr *const pFootnote( pTextNd
+ ? pTextNd->GetTextAttrForCharAt(
+ GetPoint()->nContent.GetIndex(), RES_TXTATR_FTN)
+ : nullptr);
+ if (pFootnote)
+ {
+ SwCursorSaveState aSaveState( *this );
+ GetPoint()->nNode = *static_cast<SwTextFootnote*>(pFootnote)->GetStartNode();
+
+ SwContentNode* pCNd = GetDoc().GetNodes().GoNextSection(
+ &GetPoint()->nNode,
+ true, !IsReadOnlyAvailable() );
+ if( pCNd )
+ {
+ GetPoint()->nContent.Assign( pCNd, 0 );
+ bRet = !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle );
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::GotoFootnoteText()
+{
+ bool bRet = CallCursorFN( &SwCursor::GotoFootnoteText );
+ if( !bRet )
+ {
+ SwTextNode* pTextNd = GetCursor_() ?
+ GetCursor_()->GetPoint()->nNode.GetNode().GetTextNode() : nullptr;
+ if( pTextNd )
+ {
+ std::pair<Point, bool> const tmp(GetCursor_()->GetSttPos(), true);
+ const SwFrame *pFrame = pTextNd->getLayoutFrame( GetLayout(),
+ GetCursor_()->Start(), &tmp);
+ const SwFootnoteBossFrame* pFootnoteBoss;
+ bool bSkip = pFrame && pFrame->IsInFootnote();
+ while( pFrame )
+ {
+ pFootnoteBoss = pFrame->FindFootnoteBossFrame();
+ if (!pFootnoteBoss)
+ break;
+ pFrame = pFootnoteBoss->FindFootnoteCont();
+ if( pFrame )
+ {
+ if( bSkip )
+ bSkip = false;
+ else
+ {
+ const SwContentFrame* pCnt = static_cast<const SwLayoutFrame*>
+ (pFrame)->ContainsContent();
+ if( pCnt )
+ {
+ SwTextFrame const*const pTF(
+ static_cast<const SwTextFrame*>(pCnt));
+ *GetCursor_()->GetPoint() =
+ pTF->MapViewToModelPos(pTF->GetOffset());
+ UpdateCursor( SwCursorShell::SCROLLWIN |
+ SwCursorShell::CHKRANGE | SwCursorShell::READONLY );
+ bRet = true;
+ break;
+ }
+ }
+ }
+ if( pFootnoteBoss->GetNext() && !pFootnoteBoss->IsPageFrame() )
+ pFrame = pFootnoteBoss->GetNext();
+ else
+ pFrame = pFootnoteBoss->GetUpper();
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwCursor::GotoFootnoteAnchor()
+{
+ // jump from footnote to anchor
+ const SwNode* pSttNd = GetNode().FindFootnoteStartNode();
+ if( pSttNd )
+ {
+ // search in all footnotes in document for this StartIndex
+ const SwFootnoteIdxs& rFootnoteArr = pSttNd->GetDoc().GetFootnoteIdxs();
+ for( size_t n = 0; n < rFootnoteArr.size(); ++n )
+ {
+ const SwTextFootnote* pTextFootnote = rFootnoteArr[ n ];
+ if( nullptr != pTextFootnote->GetStartNode() &&
+ pSttNd == &pTextFootnote->GetStartNode()->GetNode() )
+ {
+ SwCursorSaveState aSaveState( *this );
+
+ SwTextNode& rTNd = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
+ GetPoint()->nNode = rTNd;
+ GetPoint()->nContent.Assign( &rTNd, pTextFootnote->GetStart() );
+
+ return !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle );
+ }
+ }
+ }
+ return false;
+}
+
+bool SwCursorShell::GotoFootnoteAnchor()
+{
+ if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
+ pWrtSh->addCurrentPosition();
+
+ // jump from footnote to anchor
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ bool bRet = m_pCurrentCursor->GotoFootnoteAnchor();
+ if( bRet )
+ {
+ // special treatment for table header row
+ m_pCurrentCursor->GetPtPos() = Point();
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ }
+ return bRet;
+}
+
+static bool CmpLE( const SwTextFootnote& rFootnote, SwNodeOffset nNd, sal_Int32 nCnt )
+{
+ const SwNodeOffset nTNd = rFootnote.GetTextNode().GetIndex();
+ return nTNd < nNd || ( nTNd == nNd && rFootnote.GetStart() <= nCnt );
+}
+
+static bool CmpL( const SwTextFootnote& rFootnote, SwNodeOffset nNd, sal_Int32 nCnt )
+{
+ const SwNodeOffset nTNd = rFootnote.GetTextNode().GetIndex();
+ return nTNd < nNd || ( nTNd == nNd && rFootnote.GetStart() < nCnt );
+}
+
+bool SwCursor::GotoNextFootnoteAnchor()
+{
+ const SwFootnoteIdxs& rFootnoteArr = GetDoc().GetFootnoteIdxs();
+ const SwTextFootnote* pTextFootnote = nullptr;
+ size_t nPos = 0;
+
+ if( rFootnoteArr.empty() )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+
+ if( rFootnoteArr.SeekEntry( GetPoint()->nNode, &nPos ))
+ {
+ // there is a footnote with this index, so search also for the next one
+ if( nPos < rFootnoteArr.size() )
+ {
+ SwNodeOffset nNdPos = GetPoint()->nNode.GetIndex();
+ const sal_Int32 nCntPos = GetPoint()->nContent.GetIndex();
+
+ pTextFootnote = rFootnoteArr[ nPos ];
+ // search forwards
+ if( CmpLE( *pTextFootnote, nNdPos, nCntPos ) )
+ {
+ pTextFootnote = nullptr;
+ for( ++nPos; nPos < rFootnoteArr.size(); ++nPos )
+ {
+ pTextFootnote = rFootnoteArr[ nPos ];
+ if( !CmpLE( *pTextFootnote, nNdPos, nCntPos ) )
+ break; // found
+ pTextFootnote = nullptr;
+ }
+ }
+ else if( nPos )
+ {
+ // search backwards
+ pTextFootnote = nullptr;
+ while( nPos )
+ {
+ pTextFootnote = rFootnoteArr[ --nPos ];
+ if( CmpLE( *pTextFootnote, nNdPos, nCntPos ) )
+ {
+ pTextFootnote = rFootnoteArr[ ++nPos ];
+ break; // found
+ }
+ }
+ }
+ }
+ }
+ else if( nPos < rFootnoteArr.size() )
+ pTextFootnote = rFootnoteArr[ nPos ];
+
+ if (pTextFootnote == nullptr)
+ {
+ pTextFootnote = rFootnoteArr[ 0 ];
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
+ }
+ else
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ bool bRet = nullptr != pTextFootnote;
+ if( bRet )
+ {
+ SwCursorSaveState aSaveState( *this );
+
+ SwTextNode& rTNd = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
+ GetPoint()->nNode = rTNd;
+ GetPoint()->nContent.Assign( &rTNd, pTextFootnote->GetStart() );
+ bRet = !IsSelOvr();
+ }
+ return bRet;
+}
+
+bool SwCursor::GotoPrevFootnoteAnchor()
+{
+ const SwFootnoteIdxs& rFootnoteArr = GetDoc().GetFootnoteIdxs();
+ const SwTextFootnote* pTextFootnote = nullptr;
+ size_t nPos = 0;
+
+ if( rFootnoteArr.empty() )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+
+ if( rFootnoteArr.SeekEntry( GetPoint()->nNode, &nPos ) )
+ {
+ // there is a footnote with this index, so search also for the next one
+ SwNodeOffset nNdPos = GetPoint()->nNode.GetIndex();
+ const sal_Int32 nCntPos = GetPoint()->nContent.GetIndex();
+
+ pTextFootnote = rFootnoteArr[ nPos ];
+ // search forwards
+ if( CmpL( *pTextFootnote, nNdPos, nCntPos ))
+ {
+ for( ++nPos; nPos < rFootnoteArr.size(); ++nPos )
+ {
+ pTextFootnote = rFootnoteArr[ nPos ];
+ if( !CmpL( *pTextFootnote, nNdPos, nCntPos ) )
+ {
+ pTextFootnote = rFootnoteArr[ nPos-1 ];
+ break;
+ }
+ }
+ }
+ else if( nPos )
+ {
+ // search backwards
+ pTextFootnote = nullptr;
+ while( nPos )
+ {
+ pTextFootnote = rFootnoteArr[ --nPos ];
+ if( CmpL( *pTextFootnote, nNdPos, nCntPos ))
+ break; // found
+ pTextFootnote = nullptr;
+ }
+ }
+ else
+ pTextFootnote = nullptr;
+ }
+ else if( nPos )
+ pTextFootnote = rFootnoteArr[ nPos-1 ];
+
+ if( pTextFootnote == nullptr )
+ {
+ pTextFootnote = rFootnoteArr[ rFootnoteArr.size() - 1 ];
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
+ }
+ else
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ bool bRet = nullptr != pTextFootnote;
+ if( bRet )
+ {
+ SwCursorSaveState aSaveState( *this );
+
+ SwTextNode& rTNd = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
+ GetPoint()->nNode = rTNd;
+ GetPoint()->nContent.Assign( &rTNd, pTextFootnote->GetStart() );
+ bRet = !IsSelOvr();
+ }
+ return bRet;
+}
+
+bool SwCursorShell::GotoNextFootnoteAnchor()
+{
+ return CallCursorFN( &SwCursor::GotoNextFootnoteAnchor );
+}
+
+bool SwCursorShell::GotoPrevFootnoteAnchor()
+{
+ return CallCursorFN( &SwCursor::GotoPrevFootnoteAnchor );
+}
+
+/// jump from border to anchor
+void SwCursorShell::GotoFlyAnchor()
+{
+ CurrShell aCurr( this );
+ const SwFrame* pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while( pFrame && !pFrame->IsFlyFrame() );
+
+ if( !pFrame ) // no FlyFrame
+ return;
+
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ SwCursorSaveState aSaveState( *m_pCurrentCursor );
+
+ // jump in BodyFrame closest to FlyFrame
+ SwRect aTmpRect( m_aCharRect );
+ if( !pFrame->getFrameArea().Contains( aTmpRect ))
+ aTmpRect = pFrame->getFrameArea();
+ Point aPt( aTmpRect.Left(), aTmpRect.Top() +
+ ( aTmpRect.Bottom() - aTmpRect.Top() ) / 2 );
+ aPt.setX(aPt.getX() > (pFrame->getFrameArea().Left() + (pFrame->getFrameArea().SSize().Width() / 2 ))
+ ? pFrame->getFrameArea().Right()
+ : pFrame->getFrameArea().Left());
+
+ const SwPageFrame* pPageFrame = pFrame->FindPageFrame();
+ const SwContentFrame* pFndFrame = pPageFrame->GetContentPos( aPt, false, true );
+ pFndFrame->GetModelPositionForViewPoint( m_pCurrentCursor->GetPoint(), aPt );
+
+ bool bRet = !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr();
+ if( bRet )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/trvlreg.cxx b/sw/source/core/crsr/trvlreg.cxx
new file mode 100644
index 000000000..7d5617886
--- /dev/null
+++ b/sw/source/core/crsr/trvlreg.cxx
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <swcrsr.hxx>
+#include <docary.hxx>
+#include <fmtcntnt.hxx>
+#include <viscrs.hxx>
+#include "callnk.hxx"
+#include <pamtyp.hxx>
+#include <section.hxx>
+#include <svx/srchdlg.hxx>
+
+bool GotoPrevRegion( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosRegion,
+ bool bInReadOnly )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+ SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
+ SwSectionNode* pNd = aIdx.GetNode().FindSectionNode();
+ if( pNd )
+ aIdx.Assign( *pNd, -1 );
+
+ SwNodeIndex aOldIdx = aIdx;
+ SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
+ do {
+ while( aIdx.GetIndex() )
+ {
+ pNd = aIdx.GetNode().StartOfSectionNode()->GetSectionNode();
+ if (pNd)
+ break;
+ --aIdx;
+ if ( aIdx == aOldIdx )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+ }
+
+ if ( !aIdx.GetIndex() )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
+ aIdx = nLastNd;
+ continue;
+ }
+
+ assert( pNd ); // coverity, should never be nullptr
+ {
+ if( pNd->GetSection().IsHiddenFlag() ||
+ ( !bInReadOnly &&
+ pNd->GetSection().IsProtectFlag() ))
+ {
+ // skip protected or hidden ones
+ aIdx.Assign( *pNd, - 1 );
+ continue;
+ }
+ else if( &fnPosRegion == &fnMoveForward )
+ {
+ aIdx = *pNd;
+ SwContentNode* pCNd = pNd->GetNodes().GoNextSection( &aIdx,
+ true, !bInReadOnly );
+ if( !pCNd )
+ {
+ --aIdx;
+ continue;
+ }
+ rCurrentCursor.GetPoint()->nContent.Assign( pCNd, 0 );
+ }
+ else
+ {
+ aIdx = *pNd->EndOfSectionNode();
+ SwContentNode* pCNd = SwNodes::GoPrevSection( &aIdx,
+ true, !bInReadOnly );
+ if( !pCNd )
+ {
+ aIdx.Assign( *pNd, - 1 );
+ continue;
+ }
+ rCurrentCursor.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ }
+ rCurrentCursor.GetPoint()->nNode = aIdx;
+ return true;
+ }
+ } while( true );
+
+ // the flow is such that it is not possible to get here
+ return false;
+}
+
+bool GotoNextRegion( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosRegion,
+ bool bInReadOnly )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+ SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
+ SwSectionNode* pNd = aIdx.GetNode().FindSectionNode();
+ if( pNd )
+ aIdx.Assign( *pNd->EndOfSectionNode(), - 1 );
+
+ SwNodeIndex aOldIdx = aIdx;
+ SwNodeOffset nEndCount = aIdx.GetNode().GetNodes().Count() - 1;
+ do {
+ while( aIdx.GetIndex() < nEndCount )
+ {
+ pNd = aIdx.GetNode().GetSectionNode();
+ if (pNd)
+ break;
+ ++aIdx;
+ if ( aIdx == aOldIdx )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+ }
+
+ if ( aIdx.GetIndex() == nEndCount )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
+ aIdx = SwNodeOffset(0);
+ continue;
+ }
+
+ assert( pNd ); // coverity, should never be nullptr
+ {
+ if( pNd->GetSection().IsHiddenFlag() ||
+ ( !bInReadOnly &&
+ pNd->GetSection().IsProtectFlag() ))
+ {
+ // skip protected or hidden ones
+ aIdx.Assign( *pNd->EndOfSectionNode(), +1 );
+ continue;
+ }
+ else if( &fnPosRegion == &fnMoveForward )
+ {
+ aIdx = *pNd;
+ SwContentNode* pCNd = pNd->GetNodes().GoNextSection( &aIdx,
+ true, !bInReadOnly );
+ if( !pCNd )
+ {
+ aIdx.Assign( *pNd->EndOfSectionNode(), +1 );
+ continue;
+ }
+ rCurrentCursor.GetPoint()->nContent.Assign( pCNd, 0 );
+ }
+ else
+ {
+ aIdx = *pNd->EndOfSectionNode();
+ SwContentNode* pCNd = SwNodes::GoPrevSection( &aIdx,
+ true, !bInReadOnly );
+ if( !pCNd )
+ {
+ ++aIdx;
+ continue;
+ }
+ rCurrentCursor.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ }
+ rCurrentCursor.GetPoint()->nNode = aIdx;
+ return true;
+ }
+ } while( true );
+
+ // the flow is such that it is not possible to get here
+ return false;
+}
+
+bool GotoCurrRegionAndSkip( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosRegion,
+ bool bInReadOnly )
+{
+ SwNode& rCurrNd = rCurrentCursor.GetNode();
+ SwSectionNode* pNd = rCurrNd.FindSectionNode();
+ if( !pNd )
+ return false;
+
+ SwPosition* pPos = rCurrentCursor.GetPoint();
+ const sal_Int32 nCurrCnt = pPos->nContent.GetIndex();
+ bool bMoveBackward = &fnPosRegion == &fnMoveBackward;
+
+ do {
+ SwContentNode* pCNd;
+ if( bMoveBackward ) // to the end of the section
+ {
+ SwNodeIndex aIdx( *pNd->EndOfSectionNode() );
+ pCNd = SwNodes::GoPrevSection( &aIdx, true, !bInReadOnly );
+ if( !pCNd )
+ return false;
+ pPos->nNode = aIdx;
+ }
+ else
+ {
+ SwNodeIndex aIdx( *pNd );
+ pCNd = pNd->GetNodes().GoNextSection( &aIdx, true, !bInReadOnly );
+ if( !pCNd )
+ return false;
+ pPos->nNode = aIdx;
+ }
+
+ pPos->nContent.Assign( pCNd, bMoveBackward ? pCNd->Len() : 0 );
+
+ if( &pPos->nNode.GetNode() != &rCurrNd ||
+ pPos->nContent.GetIndex() != nCurrCnt )
+ // there was a change
+ return true;
+
+ // try also the parent of this section
+ SwSection* pParent = pNd->GetSection().GetParent();
+ pNd = pParent ? pParent->GetFormat()->GetSectionNode() : nullptr;
+ } while( pNd );
+ return false;
+}
+
+bool SwCursor::MoveRegion( SwWhichRegion fnWhichRegion, SwMoveFnCollection const & fnPosRegion )
+{
+ SwCursorSaveState aSaveState( *this );
+ return !dynamic_cast<SwTableCursor*>(this) &&
+ (*fnWhichRegion)( *this, fnPosRegion, IsReadOnlyAvailable() ) &&
+ !IsSelOvr() &&
+ (GetPoint()->nNode.GetIndex() != m_vSavePos.back().nNode ||
+ GetPoint()->nContent.GetIndex() != m_vSavePos.back().nContent);
+}
+
+bool SwCursorShell::MoveRegion( SwWhichRegion fnWhichRegion, SwMoveFnCollection const & fnPosRegion )
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves;call Link if needed
+ bool bRet = !m_pTableCursor && m_pCurrentCursor->MoveRegion( fnWhichRegion, fnPosRegion );
+ if( bRet )
+ UpdateCursor();
+ return bRet;
+}
+
+bool SwCursor::GotoRegion( std::u16string_view rName )
+{
+ bool bRet = false;
+ const SwSectionFormats& rFormats = GetDoc().GetSections();
+ for( SwSectionFormats::size_type n = rFormats.size(); n; )
+ {
+ const SwSectionFormat* pFormat = rFormats[ --n ];
+ const SwSection* pSect = pFormat->GetSection();
+ if( pSect && pSect->GetSectionName() == rName )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ // area in normal nodes array
+ SwCursorSaveState aSaveState( *this );
+
+ GetPoint()->nNode = *pIdx;
+ Move( fnMoveForward, GoInContent );
+ bRet = !IsSelOvr();
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwCursorShell::GotoRegion( std::u16string_view rName )
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves;call Link if needed
+ bool bRet = !m_pTableCursor && m_pCurrentCursor->GotoRegion( rName );
+ if( bRet )
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/trvltbl.cxx b/sw/source/core/crsr/trvltbl.cxx
new file mode 100644
index 000000000..12b8eae08
--- /dev/null
+++ b/sw/source/core/crsr/trvltbl.cxx
@@ -0,0 +1,929 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <cntfrm.hxx>
+#include <editsh.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <viscrs.hxx>
+#include "callnk.hxx"
+#include <tabfrm.hxx>
+#include <ndtxt.hxx>
+#include <shellres.hxx>
+#include <cellfrm.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <osl/diagnose.h>
+#include <svx/srchdlg.hxx>
+
+/// set cursor into next/previous cell
+bool SwCursorShell::GoNextCell( bool bAppendLine )
+{
+ bool bRet = false;
+ const SwTableNode* pTableNd = nullptr;
+
+ if( IsTableMode() || nullptr != ( pTableNd = IsCursorInTable() ))
+ {
+ SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ bRet = true;
+
+ // Check if we have to move the cursor to a covered cell before
+ // proceeding:
+ const SwNode* pTableBoxStartNode = pCursor->GetNode().FindTableBoxStartNode();
+ const SwTableBox* pTableBox = nullptr;
+
+ if ( pCursor->GetCursorRowSpanOffset() )
+ {
+ pTableBox = pTableBoxStartNode->GetTableBox();
+ if (pTableBox && pTableBox->getRowSpan() > 1)
+ {
+ if ( !pTableNd )
+ pTableNd = IsCursorInTable();
+ assert (pTableNd);
+ pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(),
+ o3tl::narrowing<sal_uInt16>(pTableBox->getRowSpan() + pCursor->GetCursorRowSpanOffset() ) );
+ pTableBoxStartNode = pTableBox->GetSttNd();
+ }
+ }
+
+ SwNodeIndex aCellStt( *pTableBoxStartNode->EndOfSectionNode(), 1 );
+
+ // if there is another StartNode after the EndNode of a cell then
+ // there is another cell
+ if( !aCellStt.GetNode().IsStartNode() )
+ {
+ if( pCursor->HasMark() || !bAppendLine )
+ bRet = false;
+ else if (pTableNd)
+ {
+ // if there is no list anymore then create new one
+ if ( !pTableBox )
+ pTableBox = pTableNd->GetTable().GetTableBox(
+ pCursor->GetPoint()->nNode.GetNode().
+ StartOfSectionIndex() );
+
+ OSL_ENSURE( pTableBox, "Box is not in this table" );
+ SwSelBoxes aBoxes;
+
+ // the document might change; w/o Action views would not be notified
+ static_cast<SwEditShell*>(this)->StartAllAction();
+ bRet = mxDoc->InsertRow( SwTable::SelLineFromBox( pTableBox, aBoxes, false ));
+ static_cast<SwEditShell*>(this)->EndAllAction();
+ }
+ }
+ bRet = bRet && pCursor->GoNextCell();
+ if( bRet )
+ UpdateCursor();
+ }
+ return bRet;
+}
+
+bool SwCursorShell::GoPrevCell()
+{
+ bool bRet = false;
+ if( IsTableMode() || IsCursorInTable() )
+ {
+ SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ bRet = pCursor->GoPrevCell();
+ if( bRet )
+ UpdateCursor(); // update current cursor
+ }
+ return bRet;
+}
+
+static const SwFrame* lcl_FindMostUpperCellFrame( const SwFrame* pFrame )
+{
+ while ( pFrame &&
+ ( !pFrame->IsCellFrame() ||
+ !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
+ pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ return pFrame;
+}
+
+bool SwCursorShell::SelTableRowOrCol( bool bRow, bool bRowSimple )
+{
+ // check if the current cursor's SPoint/Mark are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame->IsInTab() )
+ return false;
+
+ const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
+ const SwTabFrame* pMasterTabFrame = pTabFrame->IsFollow() ? pTabFrame->FindMaster( true ) : pTabFrame;
+ const SwTable* pTable = pTabFrame->GetTable();
+
+ CurrShell aCurr( this );
+
+ const SwTableBox* pStt = nullptr;
+ const SwTableBox* pEnd = nullptr;
+
+ // search box based on layout
+ SwSelBoxes aBoxes;
+ SwTableSearchType eType = bRow ? SwTableSearchType::Row : SwTableSearchType::Col;
+ const bool bCheckProtected = !IsReadOnlyAvailable();
+
+ if( bCheckProtected )
+ eType = static_cast<SwTableSearchType>(eType | SwTableSearchType::Protect);
+
+ if ( !bRowSimple )
+ {
+ GetTableSel( *this, aBoxes, eType );
+
+ if( aBoxes.empty() )
+ return false;
+
+ pStt = aBoxes[0];
+ pEnd = aBoxes.back();
+ }
+ // #i32329# Enhanced table selection
+ else if ( pTable->IsNewModel() )
+ {
+ const SwShellCursor *pCursor = GetCursor_();
+ SwTable::SearchType eSearchType = bRow ? SwTable::SEARCH_ROW : SwTable::SEARCH_COL;
+ pTable->CreateSelection( *pCursor, aBoxes, eSearchType, bCheckProtected );
+ if( aBoxes.empty() )
+ return false;
+
+ pStt = aBoxes[0];
+ pEnd = aBoxes.back();
+
+ m_eEnhancedTableSel = eSearchType;
+ }
+ else
+ {
+ const SwShellCursor *pCursor = GetCursor_();
+ const SwFrame* pStartFrame = pFrame;
+ const SwContentNode *pCNd = pCursor->GetContentNode( false );
+ std::pair<Point, bool> const tmp(pCursor->GetMkPos(), true);
+ const SwFrame* pEndFrame = pCNd
+ ? pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp)
+ : nullptr;
+
+ if ( bRow )
+ {
+ pStartFrame = lcl_FindMostUpperCellFrame( pStartFrame );
+ pEndFrame = lcl_FindMostUpperCellFrame( pEndFrame );
+ }
+
+ if ( !pStartFrame || !pEndFrame )
+ return false;
+
+ const bool bVert = pFrame->ImplFindTabFrame()->IsVertical();
+
+ // If we select upwards it is sufficient to set pStt and pEnd
+ // to the first resp. last box of the selection obtained from
+ // GetTableSel. However, selecting downwards requires the frames
+ // located at the corners of the selection. This does not work
+ // for column selections in vertical tables:
+ const bool bSelectUp = ( bVert && !bRow ) ||
+ *pCursor->GetPoint() <= *pCursor->GetMark();
+ SwCellFrames aCells;
+ GetTableSel( static_cast<const SwCellFrame*>(pStartFrame),
+ static_cast<const SwCellFrame*>(pEndFrame),
+ aBoxes, bSelectUp ? nullptr : &aCells, eType );
+
+ if( aBoxes.empty() || ( !bSelectUp && 4 != aCells.size() ) )
+ return false;
+
+ if ( bSelectUp )
+ {
+ pStt = aBoxes[0];
+ pEnd = aBoxes.back();
+ }
+ else
+ {
+ // will become point of table cursor
+ pStt = aCells[bVert ? 0 : (bRow ? 2 : 1)]->GetTabBox();
+ // will become mark of table cursor
+ pEnd = aCells[bVert ? 3 : (bRow ? 1 : 2)]->GetTabBox();
+ }
+ }
+
+ // if no table cursor exists, create one
+ if( !m_pTableCursor )
+ {
+ m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SwSelPaintRects::Hide();
+ }
+
+ m_pTableCursor->DeleteMark();
+
+ // set start and end of a column
+ m_pTableCursor->GetPoint()->nNode = *pEnd->GetSttNd();
+ m_pTableCursor->Move( fnMoveForward, GoInContent );
+ m_pTableCursor->SetMark();
+ m_pTableCursor->GetPoint()->nNode = *pStt->GetSttNd()->EndOfSectionNode();
+ m_pTableCursor->Move( fnMoveBackward, GoInContent );
+
+ // set PtPos 'close' to the reference table, otherwise we might get problems
+ // with the repeated headlines check in UpdateCursor():
+ if ( !bRow )
+ m_pTableCursor->GetPtPos() = pMasterTabFrame->IsVertical()
+ ? pMasterTabFrame->getFrameArea().TopRight()
+ : pMasterTabFrame->getFrameArea().TopLeft();
+
+ UpdateCursor();
+ return true;
+}
+
+bool SwCursorShell::SelTable()
+{
+ // check if the current cursor's SPoint/Mark are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame->IsInTab() )
+ return false;
+
+ const SwTabFrame *pTableFrame = pFrame->ImplFindTabFrame();
+ const SwTabFrame* pMasterTabFrame = pTableFrame->IsFollow() ? pTableFrame->FindMaster( true ) : pTableFrame;
+ const SwTableNode* pTableNd = pTableFrame->GetTable()->GetTableNode();
+
+ CurrShell aCurr( this );
+
+ if( !m_pTableCursor )
+ {
+ m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SwSelPaintRects::Hide();
+ }
+
+ m_pTableCursor->DeleteMark();
+ m_pTableCursor->GetPoint()->nNode = *pTableNd;
+ m_pTableCursor->Move( fnMoveForward, GoInContent );
+ m_pTableCursor->SetMark();
+ // set MkPos 'close' to the master table, otherwise we might get problems
+ // with the repeated headlines check in UpdateCursor():
+ m_pTableCursor->GetMkPos() = pMasterTabFrame->IsVertical() ? pMasterTabFrame->getFrameArea().TopRight() : pMasterTabFrame->getFrameArea().TopLeft();
+ m_pTableCursor->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
+ m_pTableCursor->Move( fnMoveBackward, GoInContent );
+ UpdateCursor();
+ return true;
+}
+
+bool SwCursorShell::SelTableBox()
+{
+ // if we're in a table, create a table cursor, and select the cell
+ // that the current cursor's point resides in
+
+ // search for start node of our table box. If not found, exit really
+ const SwStartNode* pStartNode =
+ m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+
+#if OSL_DEBUG_LEVEL > 0
+ // the old code checks whether we're in a table by asking the
+ // frame. This should yield the same result as searching for the
+ // table box start node, right?
+ SwFrame *pFrame = GetCurrFrame();
+ OSL_ENSURE( !pFrame->IsInTab() == !(pStartNode != nullptr),
+ "Schroedinger's table: We're in a box, and also we aren't." );
+#endif
+ if( pStartNode == nullptr )
+ return false;
+
+ CurrShell aCurr( this );
+
+ // create a table cursor, if there isn't one already
+ if( !m_pTableCursor )
+ {
+ m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SwSelPaintRects::Hide();
+ }
+
+ // select the complete box with our shiny new m_pTableCursor
+ // 1. delete mark, and move point to first content node in box
+ m_pTableCursor->DeleteMark();
+ *(m_pTableCursor->GetPoint()) = SwPosition( *pStartNode );
+ m_pTableCursor->Move( fnMoveForward, GoInNode );
+
+ // 2. set mark, and move point to last content node in box
+ m_pTableCursor->SetMark();
+ *(m_pTableCursor->GetPoint()) = SwPosition( *(pStartNode->EndOfSectionNode()) );
+ m_pTableCursor->Move( fnMoveBackward, GoInNode );
+
+ // 3. exchange
+ m_pTableCursor->Exchange();
+
+ // with some luck, UpdateCursor() will now update everything that
+ // needs updating
+ UpdateCursor();
+
+ return true;
+}
+
+// TODO: provide documentation
+/** get the next non-protected cell inside a table
+
+ @param[in,out] rIdx is on a table node
+ @param bInReadOnly ???
+
+ @return <false> if no suitable cell could be found, otherwise <rIdx> points
+ to content in a suitable cell and <true> is returned.
+*/
+static bool lcl_FindNextCell( SwNodeIndex& rIdx, bool bInReadOnly )
+{
+ // check protected cells
+ SwNodeIndex aTmp( rIdx, 2 ); // TableNode + StartNode
+
+ // the resulting cell should be in that table:
+ const SwTableNode* pTableNd = rIdx.GetNode().GetTableNode();
+
+ if ( !pTableNd )
+ {
+ OSL_FAIL( "lcl_FindNextCell not celled with table start node!" );
+ return false;
+ }
+
+ const SwNode* pTableEndNode = pTableNd->EndOfSectionNode();
+
+ SwNodes& rNds = aTmp.GetNode().GetNodes();
+ SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
+
+ // no content node => go to next content node
+ if( !pCNd )
+ pCNd = rNds.GoNext( &aTmp );
+
+ // robust
+ if ( !pCNd )
+ return false;
+
+ SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+
+ if ( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
+ (!bInReadOnly && pFrame->IsProtected() ) )
+ {
+ // we are not located inside a 'valid' cell. We have to continue searching...
+
+ // skip behind current section. This might be the end of the table cell
+ // or behind an inner section or...
+ aTmp.Assign( *pCNd->EndOfSectionNode(), 1 );
+
+ // loop to find a suitable cell...
+ for( ;; )
+ {
+ SwNode* pNd = &aTmp.GetNode();
+
+ // we break this loop if we reached the end of the table.
+ // to make this code even more robust, we also break if we are
+ // already behind the table end node:
+ if( pNd == pTableEndNode || /*robust: */ pNd->GetIndex() > pTableEndNode->GetIndex() )
+ return false;
+
+ // ok, get the next content node:
+ pCNd = aTmp.GetNode().GetContentNode();
+ if( nullptr == pCNd )
+ pCNd = rNds.GoNext( &aTmp );
+
+ // robust:
+ if ( !pCNd )
+ return false;
+
+ // check if we have found a suitable table cell:
+ pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+
+ if ( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
+ (bInReadOnly || !pFrame->IsProtected() ) )
+ {
+ // finally, we have found a suitable table cell => set index and return
+ rIdx = *pCNd;
+ return true;
+ }
+
+ // continue behind the current section:
+ aTmp.Assign( *pCNd->EndOfSectionNode(), +1 );
+ }
+ }
+ rIdx = *pCNd;
+ return true;
+}
+
+/// see lcl_FindNextCell()
+static bool lcl_FindPrevCell( SwNodeIndex& rIdx, bool bInReadOnly )
+{
+ SwNodeIndex aTmp( rIdx, -2 ); // TableNode + EndNode
+
+ const SwNode* pTableEndNode = &rIdx.GetNode();
+ const SwTableNode* pTableNd = pTableEndNode->StartOfSectionNode()->GetTableNode();
+
+ if ( !pTableNd )
+ {
+ OSL_FAIL( "lcl_FindPrevCell not celled with table start node!" );
+ return false;
+ }
+
+ SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
+
+ if( !pCNd )
+ pCNd = SwNodes::GoPrevious( &aTmp );
+
+ if ( !pCNd )
+ return false;
+
+ SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+
+ if( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
+ (!bInReadOnly && pFrame->IsProtected() ))
+ {
+ // skip before current section
+ aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
+ for( ;; )
+ {
+ SwNode* pNd = &aTmp.GetNode();
+
+ if( pNd == pTableNd || pNd->GetIndex() < pTableNd->GetIndex() )
+ return false;
+
+ pCNd = aTmp.GetNode().GetContentNode();
+ if( nullptr == pCNd )
+ pCNd = SwNodes::GoPrevious( &aTmp );
+
+ if ( !pCNd )
+ return false;
+
+ pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+
+ if( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
+ (bInReadOnly || !pFrame->IsProtected() ) )
+ {
+ rIdx = *pCNd;
+ return true; // ok, not protected
+ }
+ aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
+ }
+ }
+ rIdx = *pCNd;
+ return true;
+}
+
+bool GotoPrevTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
+ bool bInReadOnly )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
+
+ SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ // #i26532#: If we are inside a table, we may not go backward to the
+ // table start node, because we would miss any tables inside this table.
+ SwTableNode* pInnerTableNd = nullptr;
+ SwNodeIndex aTmpIdx( aIdx );
+ while( aTmpIdx.GetIndex() &&
+ nullptr == ( pInnerTableNd = aTmpIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
+ --aTmpIdx;
+
+ if( pInnerTableNd == pTableNd )
+ aIdx.Assign( *pTableNd, -1 );
+ }
+
+ SwNodeIndex aOldIdx = aIdx;
+ SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
+ do {
+ while( aIdx.GetIndex() &&
+ nullptr == ( pTableNd = aIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
+ {
+ --aIdx;
+ if ( aIdx == aOldIdx )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+ }
+
+ if ( !aIdx.GetIndex() )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
+ aIdx = nLastNd;
+ continue;
+ }
+
+ {
+ if( &fnPosTable == &fnMoveForward ) // at the beginning?
+ {
+ aIdx = *aIdx.GetNode().StartOfSectionNode();
+ if( !lcl_FindNextCell( aIdx, bInReadOnly ))
+ {
+ // skip table
+ aIdx.Assign( *pTableNd, -1 );
+ continue;
+ }
+ }
+ else
+ {
+ // check protected cells
+ if( !lcl_FindNextCell( aIdx, bInReadOnly ))
+ {
+ // skip table
+ aIdx.Assign( *pTableNd, -1 );
+ continue;
+ }
+ }
+
+ SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
+ if ( pTextNode )
+ {
+ rCurrentCursor.GetPoint()->nNode = *pTextNode;
+ rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
+ pTextNode->Len() :
+ 0 );
+ }
+ return true;
+ }
+ } while( true );
+
+ return false;
+}
+
+bool GotoNextTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
+ bool bInReadOnly )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
+ SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
+
+ if( pTableNd )
+ aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
+
+ SwNodeIndex aOldIdx = aIdx;
+ SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
+ do {
+ while( aIdx.GetIndex() < nLastNd &&
+ nullptr == ( pTableNd = aIdx.GetNode().GetTableNode()) )
+ {
+ ++aIdx;
+ if ( aIdx == aOldIdx )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+ }
+
+ if ( aIdx.GetIndex() == nLastNd )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
+ aIdx = SwNodeOffset(0);
+ continue;
+ }
+
+ assert( pTableNd ); // coverity, should never be nullptr
+
+ if( &fnPosTable == &fnMoveForward ) // at the beginning?
+ {
+ if( !lcl_FindNextCell( aIdx, bInReadOnly ))
+ {
+ // skip table
+ aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
+ continue;
+ }
+ }
+ else
+ {
+ aIdx = *aIdx.GetNode().EndOfSectionNode();
+ // check protected cells
+ if( !lcl_FindNextCell( aIdx, bInReadOnly ))
+ {
+ // skip table
+ aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
+ continue;
+ }
+ }
+
+ SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
+ if ( pTextNode )
+ {
+ rCurrentCursor.GetPoint()->nNode = *pTextNode;
+ rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
+ pTextNode->Len() :
+ 0 );
+ }
+ return true;
+
+ } while( true );
+
+ // the flow is such that it is not possible to get there
+
+ return false;
+}
+
+bool GotoCurrTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
+ bool bInReadOnly )
+{
+ SwTableNode* pTableNd = rCurrentCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return false;
+
+ SwTextNode* pTextNode = nullptr;
+ if( &fnPosTable == &fnMoveBackward ) // to the end of the table
+ {
+ SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
+ if( !lcl_FindPrevCell( aIdx, bInReadOnly ))
+ return false;
+ pTextNode = aIdx.GetNode().GetTextNode();
+ }
+ else
+ {
+ SwNodeIndex aIdx( *pTableNd );
+ if( !lcl_FindNextCell( aIdx, bInReadOnly ))
+ return false;
+ pTextNode = aIdx.GetNode().GetTextNode();
+ }
+
+ if ( pTextNode )
+ {
+ rCurrentCursor.GetPoint()->nNode = *pTextNode;
+ rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
+ pTextNode->Len() :
+ 0 );
+ }
+
+ return true;
+}
+
+bool SwCursor::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
+{
+ bool bRet = false;
+ SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
+
+ if( pTableCursor || !HasMark() )
+ {
+ SwCursorSaveState aSaveState( *this );
+ bRet = (*fnWhichTable)( *this, fnPosTable, IsReadOnlyAvailable() ) &&
+ !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
+ SwCursorSelOverFlags::Toggle );
+ }
+ return bRet;
+}
+
+bool SwCursorShell::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
+
+ SwShellCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
+ bool bCheckPos;
+ bool bRet;
+ SwNodeOffset nPtNd(0);
+ sal_Int32 nPtCnt = 0;
+
+ if ( !m_pTableCursor && m_pCurrentCursor->HasMark() )
+ {
+ // switch to table mode
+ m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
+ m_pCurrentCursor->DeleteMark();
+ m_pCurrentCursor->SwSelPaintRects::Hide();
+ m_pTableCursor->SetMark();
+ pCursor = m_pTableCursor;
+ bCheckPos = false;
+ }
+ else
+ {
+ bCheckPos = true;
+ nPtNd = pCursor->GetPoint()->nNode.GetIndex();
+ nPtCnt = pCursor->GetPoint()->nContent.GetIndex();
+ }
+
+ bRet = pCursor->MoveTable( fnWhichTable, fnPosTable );
+
+ if( bRet )
+ {
+ // #i45028# - set "top" position for repeated headline rows
+ pCursor->GetPtPos() = Point();
+
+ UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
+
+ if( bCheckPos &&
+ pCursor->GetPoint()->nNode.GetIndex() == nPtNd &&
+ pCursor->GetPoint()->nContent.GetIndex() == nPtCnt )
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwCursorShell::IsTableComplexForChart()
+{
+ bool bRet = false;
+
+ // Here we may trigger table formatting so we better do that inside an action
+ StartAction();
+ const SwTableNode* pTNd = m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTNd )
+ {
+ // in a table; check if table or section is balanced
+ OUString sSel;
+ if( m_pTableCursor )
+ sSel = GetBoxNms();
+ bRet = pTNd->GetTable().IsTableComplexForChart( sSel );
+ }
+ EndAction();
+
+ return bRet;
+}
+
+OUString SwCursorShell::GetBoxNms() const
+{
+ OUString sNm;
+ const SwPosition* pPos;
+ SwFrame* pFrame;
+
+ if( IsTableMode() )
+ {
+ SwContentNode *pCNd = m_pTableCursor->Start()->nNode.GetNode().GetContentNode();
+ pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
+ if( !pFrame )
+ return sNm;
+
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+
+ OSL_ENSURE( pFrame, "no frame for this box" );
+
+ if( !pFrame )
+ return sNm;
+
+ sNm = static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName() + ":";
+ pPos = m_pTableCursor->End();
+ }
+ else
+ {
+ const SwTableNode* pTableNd = IsCursorInTable();
+ if( !pTableNd )
+ return sNm;
+ pPos = GetCursor()->GetPoint();
+ }
+
+ SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
+ pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
+
+ if( pFrame )
+ {
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+
+ if( pFrame )
+ sNm += static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName();
+ }
+ return sNm;
+}
+
+bool SwCursorShell::GotoTable( const OUString& rName )
+{
+ SwCallLink aLk( *this ); // watch Cursor-Moves
+ bool bRet = !m_pTableCursor && m_pCurrentCursor->GotoTable( rName );
+ if( bRet )
+ {
+ m_pCurrentCursor->GetPtPos() = Point();
+ UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
+ SwCursorShell::READONLY );
+ }
+ return bRet;
+}
+
+bool SwCursorShell::CheckTableBoxContent( const SwPosition* pPos )
+{
+ if( !m_pBoxIdx || !m_pBoxPtr || IsSelTableCells() || !IsAutoUpdateCells() )
+ return false;
+
+ // check if box content is consistent with given box format, reset if not
+ SwTableBox* pChkBox = nullptr;
+ SwStartNode* pSttNd = nullptr;
+ if( !pPos )
+ {
+ // get stored position
+ if (nullptr != (pSttNd = m_pBoxIdx->GetNode().GetStartNode()) &&
+ SwTableBoxStartNode == pSttNd->GetStartNodeType() &&
+ m_pBoxPtr == pSttNd->FindTableNode()->GetTable().
+ GetTableBox( m_pBoxIdx->GetIndex() ) )
+ pChkBox = m_pBoxPtr;
+ }
+ else
+ {
+ pSttNd = pPos->nNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
+ if( pSttNd)
+ pChkBox = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
+ }
+
+ // box has more than one paragraph
+ if( pChkBox && pSttNd->GetIndex() + SwNodeOffset(2) != pSttNd->EndOfSectionIndex() )
+ pChkBox = nullptr;
+
+ // destroy pointer before next action starts
+ if( !pPos && !pChkBox )
+ ClearTableBoxContent();
+
+ // cursor not anymore in this section?
+ if( pChkBox && !pPos &&
+ ( m_pCurrentCursor->HasMark() || m_pCurrentCursor->GetNext() != m_pCurrentCursor ||
+ pSttNd->GetIndex() + 1 == m_pCurrentCursor->GetPoint()->nNode.GetIndex() ))
+ pChkBox = nullptr;
+
+ // Did the content of a box change at all? This is important if e.g. Undo
+ // could not restore the content properly.
+ if( pChkBox )
+ {
+ const SwTextNode* pNd = GetDoc()->GetNodes()[
+ pSttNd->GetIndex() + 1 ]->GetTextNode();
+ if( !pNd ||
+ ( pNd->GetText() == SwViewShell::GetShellRes()->aCalc_Error &&
+ SfxItemState::SET == pChkBox->GetFrameFormat()->
+ GetItemState( RES_BOXATR_FORMULA )) )
+ pChkBox = nullptr;
+ }
+
+ if( pChkBox )
+ {
+ // destroy pointer before next action starts
+ ClearTableBoxContent();
+ StartAction();
+ GetDoc()->ChkBoxNumFormat( *pChkBox, true );
+ EndAction();
+ }
+
+ return nullptr != pChkBox;
+}
+
+void SwCursorShell::SaveTableBoxContent( const SwPosition* pPos )
+{
+ if( IsSelTableCells() || !IsAutoUpdateCells() )
+ return ;
+
+ if( !pPos )
+ pPos = m_pCurrentCursor->GetPoint();
+
+ SwStartNode* pSttNd = pPos->nNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
+
+ bool bCheckBox = false;
+ if( pSttNd && m_pBoxIdx )
+ {
+ if( pSttNd == &m_pBoxIdx->GetNode() )
+ pSttNd = nullptr;
+ else
+ bCheckBox = true;
+ }
+ else
+ bCheckBox = nullptr != m_pBoxIdx;
+
+ if( bCheckBox )
+ {
+ // check m_pBoxIdx
+ SwPosition aPos( *m_pBoxIdx );
+ CheckTableBoxContent( &aPos );
+ }
+
+ if( pSttNd )
+ {
+ m_pBoxPtr = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
+
+ if( m_pBoxIdx )
+ *m_pBoxIdx = *pSttNd;
+ else
+ m_pBoxIdx = new SwNodeIndex( *pSttNd );
+ }
+}
+
+void SwCursorShell::ClearTableBoxContent()
+{
+ delete m_pBoxIdx;
+ m_pBoxIdx = nullptr;
+ m_pBoxPtr = nullptr;
+}
+
+bool SwCursorShell::EndAllTableBoxEdit()
+{
+ bool bRet = false;
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rSh) )
+ bRet |= pCursorShell->CheckTableBoxContent(
+ pCursorShell->m_pCurrentCursor->GetPoint() );
+
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx
new file mode 100644
index 000000000..9e41acb4a
--- /dev/null
+++ b/sw/source/core/crsr/viscrs.cxx
@@ -0,0 +1,1216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_feature_desktop.h>
+
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <viewopt.hxx>
+#include <frmtool.hxx>
+#include <viscrs.hxx>
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <swtable.hxx>
+#include <viewimp.hxx>
+#include <dview.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <txtfld.hxx>
+#include <scriptinfo.hxx>
+#include <view.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/srchdlg.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include "overlayrangesoutline.hxx"
+
+#include <memory>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <boost/property_tree/json_parser.hpp>
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <paintfrm.hxx>
+#include <PostItMgr.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <docsh.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <o3tl/string_view.hxx>
+#include <tools/json_writer.hxx>
+#include <cellfrm.hxx>
+#include <wrtsh.hxx>
+#include <textcontentcontrol.hxx>
+#include <dropdowncontentcontrolbutton.hxx>
+#include <datecontentcontrolbutton.hxx>
+
+// Here static members are defined. They will get changed on alteration of the
+// MapMode. This is done so that on ShowCursor the same size does not have to be
+// expensively determined again and again.
+
+tools::Long SwSelPaintRects::s_nPixPtX = 0;
+tools::Long SwSelPaintRects::s_nPixPtY = 0;
+MapMode* SwSelPaintRects::s_pMapMode = nullptr;
+
+// Starting from here: classes / methods for the non-text-cursor
+SwVisibleCursor::SwVisibleCursor( const SwCursorShell * pCShell )
+ : m_pCursorShell( pCShell )
+ , m_nPageLastTime(0)
+{
+ pCShell->GetWin()->SetCursor( &m_aTextCursor );
+ m_bIsVisible = m_aTextCursor.IsVisible();
+ m_bIsDragCursor = false;
+ m_aTextCursor.SetWidth( 0 );
+}
+
+SwVisibleCursor::~SwVisibleCursor()
+{
+ if( m_bIsVisible && m_aTextCursor.IsVisible() )
+ m_aTextCursor.Hide();
+
+ m_pCursorShell->GetWin()->SetCursor( nullptr );
+}
+
+void SwVisibleCursor::Show()
+{
+ if( !m_bIsVisible )
+ {
+ m_bIsVisible = true;
+
+ // display at all?
+ if( m_pCursorShell->VisArea().Overlaps( m_pCursorShell->m_aCharRect ) || comphelper::LibreOfficeKit::isActive() )
+ SetPosAndShow(nullptr);
+ }
+}
+
+void SwVisibleCursor::Hide()
+{
+ if( m_bIsVisible )
+ {
+ m_bIsVisible = false;
+
+ if( m_aTextCursor.IsVisible() ) // Shouldn't the flags be in effect?
+ m_aTextCursor.Hide();
+ }
+}
+
+namespace
+{
+
+// Build JSON message to be sent to Online
+OString buildHyperlinkJSON(const OUString& sText, const OUString& sLink)
+{
+ boost::property_tree::ptree aTree;
+ aTree.put("text", sText);
+ aTree.put("link", sLink);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree, false);
+
+ return OString(o3tl::trim(aStream.str()));
+}
+
+}
+
+void SwVisibleCursor::SetPosAndShow(SfxViewShell const * pViewShell)
+{
+ SwRect aRect;
+ tools::Long nTmpY = m_pCursorShell->m_aCursorHeight.getY();
+ if( 0 > nTmpY )
+ {
+ nTmpY = -nTmpY;
+ m_aTextCursor.SetOrientation( 900_deg10 );
+ aRect = SwRect( m_pCursorShell->m_aCharRect.Pos(),
+ Size( m_pCursorShell->m_aCharRect.Height(), nTmpY ) );
+ aRect.Pos().setX(aRect.Pos().getX() + m_pCursorShell->m_aCursorHeight.getX());
+ if( m_pCursorShell->IsOverwriteCursor() )
+ aRect.Pos().setY(aRect.Pos().getY() + aRect.Width());
+ }
+ else
+ {
+ m_aTextCursor.SetOrientation();
+ aRect = SwRect( m_pCursorShell->m_aCharRect.Pos(),
+ Size( m_pCursorShell->m_aCharRect.Width(), nTmpY ) );
+ aRect.Pos().setY(aRect.Pos().getY() + m_pCursorShell->m_aCursorHeight.getX());
+ }
+
+ // check if cursor should show the current cursor bidi level
+ m_aTextCursor.SetDirection();
+ const SwCursor* pTmpCursor = m_pCursorShell->GetCursor_();
+
+ if ( pTmpCursor && !m_pCursorShell->IsOverwriteCursor() )
+ {
+ SwNode& rNode = pTmpCursor->GetPoint()->nNode.GetNode();
+ if( rNode.IsTextNode() )
+ {
+ const SwTextNode& rTNd = *rNode.GetTextNode();
+ const SwFrame* pFrame = rTNd.getLayoutFrame(m_pCursorShell->GetLayout(), nullptr, nullptr);
+ if ( pFrame )
+ {
+ const SwScriptInfo* pSI = static_cast<const SwTextFrame*>(pFrame)->GetScriptInfo();
+ // cursor level has to be shown
+ if ( pSI && pSI->CountDirChg() > 1 )
+ {
+ m_aTextCursor.SetDirection(
+ ( pTmpCursor->GetCursorBidiLevel() % 2 ) ?
+ CursorDirection::RTL :
+ CursorDirection::LTR );
+ }
+ if ( pFrame->IsRightToLeft() )
+ {
+ const OutputDevice *pOut = m_pCursorShell->GetOut();
+ if ( pOut )
+ {
+ tools::Long nSize = pOut->GetSettings().GetStyleSettings().GetCursorSize();
+ Size aSize( nSize, nSize );
+ aSize = pOut->PixelToLogic( aSize );
+ aRect.Left( aRect.Left() - aSize.Width() );
+ }
+ }
+ }
+ }
+ }
+
+ if( aRect.Height())
+ {
+ ::SwCalcPixStatics( m_pCursorShell->GetOut() );
+
+ // Disable pixel alignment when tiled rendering, so that twip values of
+ // the cursor don't depend on statics.
+ if (!comphelper::LibreOfficeKit::isActive())
+ ::SwAlignRect( aRect, static_cast<SwViewShell const *>(m_pCursorShell), m_pCursorShell->GetOut() );
+ }
+ if( !m_pCursorShell->IsOverwriteCursor() || m_bIsDragCursor ||
+ m_pCursorShell->IsSelection() )
+ aRect.Width( 0 );
+
+ m_aTextCursor.SetSize( aRect.SSize() );
+
+ m_aTextCursor.SetPos( aRect.Pos() );
+
+ bool bPostItActive = false;
+ SwView* pView = dynamic_cast<SwView*>(m_pCursorShell->GetSfxViewShell());
+ if (pView)
+ {
+ if (SwPostItMgr* pPostItMgr = pView->GetPostItMgr())
+ bPostItActive = pPostItMgr->GetActiveSidebarWin() != nullptr;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && !bPostItActive)
+ {
+ // notify about page number change (if that happened)
+ sal_uInt16 nPage, nVirtPage;
+ // bCalcFrame=false is important to avoid calculating the layout when
+ // we're in the middle of doing that already.
+ const_cast<SwCursorShell*>(m_pCursorShell)->GetPageNum(nPage, nVirtPage, /*bAtCursorPos=*/true, /*bCalcFrame=*/false);
+ if (nPage != m_nPageLastTime)
+ {
+ m_nPageLastTime = nPage;
+ OString aPayload = OString::number(nPage - 1);
+ m_pCursorShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
+ }
+
+ // This may get called often, so instead of sending data on each update, just notify
+ // that there's been an update, and the other side will pull the data using
+ // getLOKPayload() when it decides to.
+ m_aLastLOKRect = aRect;
+ if (pViewShell)
+ {
+ if (pViewShell == m_pCursorShell->GetSfxViewShell())
+ {
+ SfxLokHelper::notifyUpdatePerViewId(pViewShell, LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR);
+ }
+ else
+ {
+ SfxLokHelper::notifyUpdatePerViewId(pViewShell, m_pCursorShell->GetSfxViewShell(), pViewShell,
+ LOK_CALLBACK_INVALIDATE_VIEW_CURSOR);
+ }
+ }
+ else
+ {
+ SfxLokHelper::notifyUpdatePerViewId(m_pCursorShell->GetSfxViewShell(), SfxViewShell::Current(),
+ m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR);
+ SfxLokHelper::notifyOtherViewsUpdatePerViewId(m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR);
+ }
+ }
+
+ if ( m_pCursorShell->IsCursorReadonly() && !m_pCursorShell->GetViewOptions()->IsSelectionInReadonly() )
+ return;
+
+ if ( m_pCursorShell->GetDrawView() )
+ const_cast<SwDrawView*>(static_cast<const SwDrawView*>(m_pCursorShell->GetDrawView()))->SetAnimationEnabled(
+ !m_pCursorShell->IsSelection() );
+
+ sal_uInt16 nStyle = m_bIsDragCursor ? CURSOR_SHADOW : 0;
+ if( nStyle != m_aTextCursor.GetStyle() )
+ {
+ m_aTextCursor.SetStyle( nStyle );
+ m_aTextCursor.SetWindow( m_bIsDragCursor ? m_pCursorShell->GetWin() : nullptr );
+ }
+
+ m_aTextCursor.Show();
+}
+
+OString SwVisibleCursor::getLOKPayload(int nType, int nViewId, bool*) const
+{
+ assert(nType == LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR || nType == LOK_CALLBACK_INVALIDATE_VIEW_CURSOR);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SwRect aRect = m_aLastLOKRect;
+
+ // notify about the cursor position & size
+ tools::Rectangle aSVRect(aRect.Pos().getX(), aRect.Pos().getY(), aRect.Pos().getX() + aRect.SSize().Width(), aRect.Pos().getY() + aRect.SSize().Height());
+ OString sRect = aSVRect.toString();
+
+ if(nType == LOK_CALLBACK_INVALIDATE_VIEW_CURSOR)
+ return SfxLokHelper::makePayloadJSON(m_pCursorShell->GetSfxViewShell(), nViewId, "rectangle", sRect);
+
+ // is cursor at a misspelled word ?
+ bool bIsWrong = false;
+ SwView* pView = dynamic_cast<SwView*>(m_pCursorShell->GetSfxViewShell());
+ if (pView && pView->GetWrtShellPtr())
+ {
+ const SwViewOption* pVOpt = pView->GetWrtShell().GetViewOptions();
+ if(pVOpt && pVOpt->IsOnlineSpell())
+ {
+ SwPaM* pCursor = m_pCursorShell->GetCursor();
+ SwPosition aPos(*pCursor->GetPoint());
+ Point aPt = aRect.Pos();
+ SwCursorMoveState eTmpState(CursorMoveState::SetOnlyText);
+ SwTextNode *pNode = nullptr;
+ if (m_pCursorShell->GetLayout()->GetModelPositionForViewPoint(&aPos, aPt, &eTmpState))
+ pNode = aPos.nNode.GetNode().GetTextNode();
+ if (pNode && !pNode->IsInProtectSect())
+ {
+ sal_Int32 nBegin = aPos.nContent.GetIndex();
+ sal_Int32 nLen = 1;
+
+ SwWrongList *pWrong = pNode->GetWrong();
+ if (!pWrong)
+ pWrong = pNode->GetGrammarCheck();
+ if (pWrong)
+ bIsWrong = pWrong->InWrongWord(nBegin,nLen) && !pNode->IsSymbolAt(nBegin);
+ }
+ }
+ }
+
+ OString sHyperlink;
+ SwContentAtPos aContentAtPos(IsAttrAtPos::InetAttr);
+ bool bIsSelection = m_pCursorShell->IsSelection();
+
+ if (const_cast<SwCursorShell*>(m_pCursorShell)->GetContentAtPos(aRect.Pos(), aContentAtPos))
+ {
+ const SwFormatINetFormat* pItem = static_cast<const SwFormatINetFormat*>(aContentAtPos.aFnd.pAttr);
+ sHyperlink = buildHyperlinkJSON(aContentAtPos.sStr, pItem->GetValue());
+ }
+ else if (bIsSelection)
+ {
+ SwWrtShell* pShell = m_pCursorShell->GetDoc()->GetDocShell()->GetWrtShell();
+
+ if (pShell)
+ {
+ SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>
+ aSet(m_pCursorShell->GetSfxViewShell()->GetPool());
+ pShell->GetCurAttr(aSet);
+ if(SfxItemState::SET <= aSet.GetItemState( RES_TXTATR_INETFMT ))
+ {
+ sHyperlink = buildHyperlinkJSON(m_pCursorShell->GetSelText(),
+ aSet.GetItem(RES_TXTATR_INETFMT)->GetValue());
+ }
+ }
+ }
+
+ return SfxLokHelper::makeVisCursorInvalidation(nViewId, sRect, bIsWrong, sHyperlink);
+ }
+ else
+ abort();
+}
+
+const vcl::Cursor& SwVisibleCursor::GetTextCursor() const
+{
+ return m_aTextCursor;
+}
+
+SwSelPaintRects::SwSelPaintRects( const SwCursorShell& rCSh )
+ : m_pCursorShell( &rCSh )
+#if HAVE_FEATURE_DESKTOP
+ , m_bShowTextInputFieldOverlay(true)
+ , m_bShowContentControlOverlay(true)
+#endif
+{
+}
+
+SwSelPaintRects::~SwSelPaintRects()
+{
+ Hide();
+ m_pContentControlButton.disposeAndClear();
+}
+
+void SwSelPaintRects::swapContent(SwSelPaintRects& rSwap)
+{
+ SwRects::swap(rSwap);
+
+#if HAVE_FEATURE_DESKTOP
+ // #i75172# also swap m_pCursorOverlay
+ std::swap(m_pCursorOverlay, rSwap.m_pCursorOverlay);
+ std::swap(m_bShowTextInputFieldOverlay, rSwap.m_bShowTextInputFieldOverlay);
+ std::swap(m_pTextInputFieldOverlay, rSwap.m_pTextInputFieldOverlay);
+ std::swap(m_bShowContentControlOverlay, rSwap.m_bShowContentControlOverlay);
+ std::swap(m_pContentControlOverlay, rSwap.m_pContentControlOverlay);
+#endif
+}
+
+void SwSelPaintRects::Hide()
+{
+#if HAVE_FEATURE_DESKTOP
+ m_pCursorOverlay.reset();
+ m_pTextInputFieldOverlay.reset();
+ m_pContentControlOverlay.reset();
+#endif
+
+ SwRects::clear();
+}
+
+/**
+ * Return a layout rectangle (typically with minimal width) that represents a
+ * cursor at rPosition.
+ *
+ * @param rPoint layout position as a hint about what layout frame contains
+ * rPosition (there might be multiple frames for a single node)
+ * @param rPosition the doc model position (paragraph / character index)
+ */
+static SwRect lcl_getLayoutRect(const Point& rPoint, const SwPosition& rPosition)
+{
+ const SwContentNode* pNode = rPosition.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(rPoint, true);
+ const SwContentFrame* pFrame = pNode->getLayoutFrame(
+ pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ &rPosition, &tmp);
+ SwRect aRect;
+ pFrame->GetCharRect(aRect, rPosition);
+ return aRect;
+}
+
+void SwShellCursor::FillStartEnd(SwRect& rStart, SwRect& rEnd) const
+{
+ const SwShellCursor* pCursor = GetShell()->getShellCursor(false);
+ rStart = lcl_getLayoutRect(pCursor->GetSttPos(), *pCursor->Start());
+ rEnd = lcl_getLayoutRect(pCursor->GetEndPos(), *pCursor->End());
+}
+
+void SwSelPaintRects::Show(std::vector<OString>* pSelectionRectangles)
+{
+ SdrView *const pView = const_cast<SdrView*>(m_pCursorShell->GetDrawView());
+
+ if(!(pView && pView->PaintWindowCount()))
+ return;
+
+ // reset rects
+ SwRects::clear();
+ FillRects();
+
+#if HAVE_FEATURE_DESKTOP
+ // get new rects
+ std::vector< basegfx::B2DRange > aNewRanges;
+ aNewRanges.reserve(size());
+ for(size_type a = 0; a < size(); ++a)
+ {
+ const SwRect aNextRect((*this)[a]);
+ const tools::Rectangle aPntRect(aNextRect.SVRect());
+
+ aNewRanges.emplace_back(
+ aPntRect.Left(), aPntRect.Top(),
+ aPntRect.Right() + 1, aPntRect.Bottom() + 1);
+ }
+
+ if (m_pCursorOverlay)
+ {
+ if(!aNewRanges.empty())
+ {
+ static_cast<sdr::overlay::OverlaySelection*>(m_pCursorOverlay.get())->setRanges(std::move(aNewRanges));
+ }
+ else
+ {
+ m_pCursorOverlay.reset();
+ }
+ }
+ else if(!empty())
+ {
+ SdrPaintWindow* pCandidate = pView->GetPaintWindow(0);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ // get the system's highlight color
+ const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+
+ // create correct selection
+ m_pCursorOverlay.reset( new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlight,
+ std::move(aNewRanges),
+ true) );
+
+ xTargetOverlay->add(*m_pCursorOverlay);
+ }
+ }
+
+ HighlightInputField();
+ HighlightContentControl();
+#endif
+
+ // Tiled editing does not expose the draw and writer cursor, it just
+ // talks about "the" cursor at the moment. As long as that's true,
+ // don't say anything about the Writer cursor till a draw object is
+ // being edited.
+ if (!comphelper::LibreOfficeKit::isActive() || pView->GetTextEditObject())
+ return;
+
+ // If pSelectionRectangles is set, we're just collecting the text selections -> don't emit start/end.
+ if (!empty() && !pSelectionRectangles)
+ {
+ SwRect aStartRect;
+ SwRect aEndRect;
+ FillStartEnd(aStartRect, aEndRect);
+
+ if (aStartRect.HasArea())
+ SfxLokHelper::notifyUpdate(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_SELECTION_START);
+ if (aEndRect.HasArea())
+ SfxLokHelper::notifyUpdate(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_SELECTION_END);
+ }
+
+ std::vector<OString> aRect;
+ aRect.reserve(size());
+ for (size_type i = 0; i < size(); ++i)
+ {
+ const SwRect& rRect = (*this)[i];
+ aRect.push_back(rRect.SVRect().toString());
+ }
+ OString sRect = comphelper::string::join("; ", aRect);
+ if (!pSelectionRectangles)
+ {
+ SfxLokHelper::notifyUpdate(GetShell()->GetSfxViewShell(),LOK_CALLBACK_TEXT_SELECTION);
+ SfxLokHelper::notifyOtherViewsUpdatePerViewId(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_VIEW_SELECTION);
+ }
+ else
+ pSelectionRectangles->push_back(sRect);
+}
+
+OString SwSelPaintRects::getLOKPayload( int nType, int nViewId, bool* ignore ) const
+{
+ switch( nType )
+ {
+ case LOK_CALLBACK_TEXT_SELECTION_START:
+ case LOK_CALLBACK_TEXT_SELECTION_END:
+ {
+ // The selection may be a complex polygon, emit the logical
+ // start/end cursor rectangle of the selection as separate
+ // events, if there is a real selection.
+ // This can be used to easily show selection handles on the
+ // client side.
+ SwRect aStartRect;
+ SwRect aEndRect;
+ FillStartEnd(aStartRect, aEndRect);
+
+ // no selection rect
+ if (!size())
+ {
+ *ignore = true;
+ return OString();
+ }
+
+ if( nType == LOK_CALLBACK_TEXT_SELECTION_START )
+ {
+ if (aStartRect.HasArea())
+ return aStartRect.SVRect().toString();
+ *ignore = true;
+ return OString();
+ }
+ else // LOK_CALLBACK_TEXT_SELECTION_END
+ {
+ if (aEndRect.HasArea())
+ return aEndRect.SVRect().toString();
+ *ignore = true;
+ return OString();
+ }
+ }
+ break;
+ case LOK_CALLBACK_TEXT_SELECTION:
+ case LOK_CALLBACK_TEXT_VIEW_SELECTION:
+ {
+ std::vector<OString> aRect;
+ aRect.reserve(size());
+ for (size_type i = 0; i < size(); ++i)
+ {
+ const SwRect& rRect = (*this)[i];
+ aRect.push_back(rRect.SVRect().toString());
+ }
+ OString sRect = comphelper::string::join("; ", aRect);
+ if( nType == LOK_CALLBACK_TEXT_SELECTION )
+ return sRect;
+ else // LOK_CALLBACK_TEXT_VIEW_SELECTION
+ return SfxLokHelper::makePayloadJSON(GetShell()->GetSfxViewShell(), nViewId, "selection", sRect);
+ }
+ break;
+ }
+ abort();
+}
+
+void SwSelPaintRects::HighlightInputField()
+{
+ std::vector< basegfx::B2DRange > aInputFieldRanges;
+
+ if (m_bShowTextInputFieldOverlay)
+ {
+ SwTextInputField* pCurTextInputFieldAtCursor =
+ dynamic_cast<SwTextInputField*>(SwCursorShell::GetTextFieldAtPos( GetShell()->GetCursor()->Start(), false ));
+ if ( pCurTextInputFieldAtCursor != nullptr )
+ {
+ SwTextNode* pTextNode = pCurTextInputFieldAtCursor->GetpTextNode();
+ std::unique_ptr<SwShellCursor> pCursorForInputTextField(
+ new SwShellCursor( *GetShell(), SwPosition( *pTextNode, pCurTextInputFieldAtCursor->GetStart() ) ) );
+ pCursorForInputTextField->SetMark();
+ pCursorForInputTextField->GetMark()->nNode = *pTextNode;
+ pCursorForInputTextField->GetMark()->nContent.Assign( pTextNode, *(pCurTextInputFieldAtCursor->End()) );
+
+ pCursorForInputTextField->FillRects();
+ SwRects* pRects = pCursorForInputTextField.get();
+ for (const SwRect & rNextRect : *pRects)
+ {
+ const tools::Rectangle aPntRect(rNextRect.SVRect());
+
+ aInputFieldRanges.emplace_back(
+ aPntRect.Left(), aPntRect.Top(),
+ aPntRect.Right() + 1, aPntRect.Bottom() + 1);
+ }
+ }
+ }
+
+ if ( !aInputFieldRanges.empty() )
+ {
+ if (m_pTextInputFieldOverlay != nullptr)
+ {
+ m_pTextInputFieldOverlay->setRanges( std::move(aInputFieldRanges) );
+ }
+ else
+ {
+ SdrView* pView = const_cast<SdrView*>(GetShell()->GetDrawView());
+ SdrPaintWindow* pCandidate = pView->GetPaintWindow(0);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ // use system's highlight color with decreased luminance as highlight color
+ Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+ aHighlight.DecreaseLuminance( 128 );
+
+ m_pTextInputFieldOverlay.reset( new sw::overlay::OverlayRangesOutline(
+ aHighlight, std::move(aInputFieldRanges) ) );
+ xTargetOverlay->add( *m_pTextInputFieldOverlay );
+ }
+ }
+ }
+ else
+ {
+ m_pTextInputFieldOverlay.reset();
+ }
+}
+
+void SwSelPaintRects::HighlightContentControl()
+{
+ std::vector<basegfx::B2DRange> aContentControlRanges;
+ std::vector<OString> aLOKRectangles;
+ SwRect aLastPortionPaintArea;
+ std::shared_ptr<SwContentControl> pContentControl;
+
+ if (m_bShowContentControlOverlay)
+ {
+ const SwPosition* pStart = GetShell()->GetCursor()->Start();
+ SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode();
+ SwTextContentControl* pCurContentControlAtCursor = nullptr;
+ if (pTextNode)
+ {
+ // SwTextNode::PARENT because this way we highlight when the user will type inside the
+ // content control, not outside of it.
+ SwTextAttr* pAttr = pTextNode->GetTextAttrAt(
+ pStart->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT);
+ if (pAttr)
+ {
+ pCurContentControlAtCursor = static_txtattr_cast<SwTextContentControl*>(pAttr);
+ }
+ }
+ if (pCurContentControlAtCursor)
+ {
+ auto pCursorForContentControl = std::make_unique<SwShellCursor>(
+ *GetShell(), SwPosition(*pTextNode, pCurContentControlAtCursor->GetStart()));
+ pCursorForContentControl->SetMark();
+ pCursorForContentControl->GetMark()->nNode = *pTextNode;
+ pCursorForContentControl->GetMark()->nContent.Assign(
+ pTextNode, *(pCurContentControlAtCursor->End()));
+
+ pCursorForContentControl->FillRects();
+ SwRects* pRects = pCursorForContentControl.get();
+ for (const auto& rRect : *pRects)
+ {
+ tools::Rectangle aRect(rRect.SVRect());
+
+ aContentControlRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right() + 1,
+ aRect.Bottom() + 1);
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ aLOKRectangles.push_back(aRect.toString());
+ }
+ }
+
+ if (!pRects->empty())
+ {
+ aLastPortionPaintArea = (*pRects)[pRects->size() - 1];
+ }
+ pContentControl = pCurContentControlAtCursor->GetContentControl().GetContentControl();
+ }
+ }
+
+ if (!aContentControlRanges.empty())
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = comphelper::string::join("; ", aLOKRectangles);
+ tools::JsonWriter aJson;
+ aJson.put("action", "show");
+ aJson.put("rectangles", aPayload);
+
+ if (pContentControl && pContentControl->HasListItems())
+ {
+ tools::ScopedJsonWriterArray aItems = aJson.startArray("items");
+ for (const auto& rItem : pContentControl->GetListItems())
+ {
+ aJson.putSimpleValue(rItem.ToString());
+ }
+ }
+
+ if (pContentControl && pContentControl->GetDate())
+ {
+ aJson.put("date", "true");
+ }
+
+ std::unique_ptr<char, o3tl::free_delete> pJson(aJson.extractData());
+ GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL, pJson.get());
+ }
+ if (m_pContentControlOverlay)
+ {
+ m_pContentControlOverlay->setRanges(std::move(aContentControlRanges));
+ }
+ else
+ {
+ SdrView* pView = const_cast<SdrView*>(GetShell()->GetDrawView());
+ SdrPaintWindow* pCandidate = pView->GetPaintWindow(0);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay
+ = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ // Use the system's highlight color with decreased luminance as highlight color.
+ Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+ aHighlight.DecreaseLuminance(128);
+
+ m_pContentControlOverlay.reset(new sw::overlay::OverlayRangesOutline(
+ aHighlight, std::move(aContentControlRanges)));
+ xTargetOverlay->add(*m_pContentControlOverlay);
+ }
+ }
+
+ if (pContentControl && pContentControl->HasListItems())
+ {
+ auto pWrtShell = dynamic_cast<const SwWrtShell*>(GetShell());
+ if (pWrtShell)
+ {
+ auto& rEditWin = const_cast<SwEditWin&>(pWrtShell->GetView().GetEditWin());
+ if (m_pContentControlButton
+ && m_pContentControlButton->GetContentControl() != pContentControl)
+ {
+ m_pContentControlButton.disposeAndClear();
+ }
+ if (!m_pContentControlButton)
+ {
+ m_pContentControlButton = VclPtr<SwDropDownContentControlButton>::Create(
+ &rEditWin, pContentControl);
+ }
+ m_pContentControlButton->CalcPosAndSize(aLastPortionPaintArea);
+ m_pContentControlButton->Show();
+ }
+ }
+ if (pContentControl && pContentControl->GetDate())
+ {
+ auto pWrtShell = dynamic_cast<const SwWrtShell*>(GetShell());
+ if (pWrtShell)
+ {
+ auto& rEditWin = const_cast<SwEditWin&>(pWrtShell->GetView().GetEditWin());
+ if (m_pContentControlButton
+ && m_pContentControlButton->GetContentControl() != pContentControl)
+ {
+ m_pContentControlButton.disposeAndClear();
+ }
+ if (!m_pContentControlButton)
+ {
+ m_pContentControlButton = VclPtr<SwDateContentControlButton>::Create(
+ &rEditWin, pContentControl, pWrtShell->GetDoc()->GetNumberFormatter());
+ }
+ m_pContentControlButton->CalcPosAndSize(aLastPortionPaintArea);
+ m_pContentControlButton->Show();
+ }
+ }
+ }
+ else
+ {
+ if (comphelper::LibreOfficeKit::isActive() && m_pContentControlOverlay)
+ {
+ tools::JsonWriter aJson;
+ aJson.put("action", "hide");
+ std::unique_ptr<char, o3tl::free_delete> pJson(aJson.extractData());
+ GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL, pJson.get());
+ }
+ m_pContentControlOverlay.reset();
+
+ if (m_pContentControlButton)
+ {
+ m_pContentControlButton.disposeAndClear();
+ }
+ }
+}
+
+VclPtr<SwContentControlButton> SwSelPaintRects::GetContentControlButton() const
+{
+ return m_pContentControlButton;
+}
+
+void SwSelPaintRects::Invalidate( const SwRect& rRect )
+{
+ size_type nSz = size();
+ if( !nSz )
+ return;
+
+ SwRegionRects aReg( GetShell()->VisArea() );
+ aReg.assign( begin(), end() );
+ aReg -= rRect;
+ SwRects::erase( begin(), begin() + nSz );
+ SwRects::insert( begin(), aReg.begin(), aReg.end() );
+
+ // If the selection is to the right or at the bottom, outside the
+ // visible area, it is never aligned on one pixel at the right/bottom.
+ // This has to be determined here and if that is the case the
+ // rectangle has to be expanded.
+ if( !(GetShell()->m_bVisPortChgd && 0 != ( nSz = size())) )
+ return;
+
+ SwSelPaintRects::Get1PixelInLogic( *GetShell() );
+ iterator it = begin();
+ for( ; nSz--; ++it )
+ {
+ SwRect& rRectIt = *it;
+ if( rRectIt.Right() == GetShell()->m_aOldRBPos.X() )
+ rRectIt.AddRight( s_nPixPtX );
+ if( rRectIt.Bottom() == GetShell()->m_aOldRBPos.Y() )
+ rRectIt.AddBottom( s_nPixPtY );
+ }
+}
+
+// check current MapMode of the shell and set possibly the static members.
+// Optional set the parameters pX, pY
+void SwSelPaintRects::Get1PixelInLogic( const SwViewShell& rSh,
+ tools::Long* pX, tools::Long* pY )
+{
+ const OutputDevice* pOut = rSh.GetWin()->GetOutDev();
+ if ( ! pOut )
+ pOut = rSh.GetOut();
+
+ const MapMode& rMM = pOut->GetMapMode();
+ if (s_pMapMode->GetMapUnit() != rMM.GetMapUnit() ||
+ s_pMapMode->GetScaleX() != rMM.GetScaleX() ||
+ s_pMapMode->GetScaleY() != rMM.GetScaleY())
+ {
+ *s_pMapMode = rMM;
+ Size aTmp( 1, 1 );
+ aTmp = pOut->PixelToLogic( aTmp );
+ s_nPixPtX = aTmp.Width();
+ s_nPixPtY = aTmp.Height();
+ }
+ if( pX )
+ *pX = s_nPixPtX;
+ if( pY )
+ *pY = s_nPixPtY;
+}
+
+SwShellCursor::SwShellCursor(
+ const SwCursorShell& rCShell,
+ const SwPosition &rPos )
+ : SwCursor(rPos,nullptr)
+ , SwSelPaintRects(rCShell)
+ , m_pInitialPoint(SwPaM::GetPoint())
+{}
+
+SwShellCursor::SwShellCursor(
+ const SwCursorShell& rCShell,
+ const SwPosition &rPos,
+ const Point& rPtPos,
+ SwPaM* pRing )
+ : SwCursor(rPos, pRing)
+ , SwSelPaintRects(rCShell)
+ , m_MarkPt(rPtPos)
+ , m_PointPt(rPtPos)
+ , m_pInitialPoint(SwPaM::GetPoint())
+{}
+
+SwShellCursor::SwShellCursor( SwShellCursor& rICursor )
+ : SwCursor(rICursor, &rICursor)
+ , SwSelPaintRects(*rICursor.GetShell())
+ , m_MarkPt(rICursor.GetMkPos())
+ , m_PointPt(rICursor.GetPtPos())
+ , m_pInitialPoint(SwPaM::GetPoint())
+{}
+
+SwShellCursor::~SwShellCursor()
+{}
+
+bool SwShellCursor::IsReadOnlyAvailable() const
+{
+ return GetShell()->IsReadOnlyAvailable();
+}
+
+void SwShellCursor::SetMark()
+{
+ if (SwPaM::GetPoint() == m_pInitialPoint)
+ m_MarkPt = m_PointPt;
+ else
+ m_PointPt = m_MarkPt;
+ SwPaM::SetMark();
+}
+
+void SwShellCursor::FillRects()
+{
+ // calculate the new rectangles
+ if( HasMark() &&
+ GetPoint()->nNode.GetNode().IsContentNode() &&
+ GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame( GetShell()->GetLayout() ) &&
+ (GetMark()->nNode == GetPoint()->nNode ||
+ (GetMark()->nNode.GetNode().IsContentNode() &&
+ GetMark()->nNode.GetNode().GetContentNode()->getLayoutFrame( GetShell()->GetLayout() ) ) ))
+ GetShell()->GetLayout()->CalcFrameRects( *this );
+}
+
+void SwShellCursor::Show(SfxViewShell const * pViewShell)
+{
+ std::vector<OString> aSelectionRectangles;
+ for(SwPaM& rPaM : GetRingContainer())
+ {
+ SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rPaM);
+ if(pShCursor)
+ pShCursor->SwSelPaintRects::Show(&aSelectionRectangles);
+ }
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ std::vector<OString> aRect;
+ for (const OString & rSelectionRectangle : aSelectionRectangles)
+ {
+ if (rSelectionRectangle.isEmpty())
+ continue;
+ aRect.push_back(rSelectionRectangle);
+ }
+ OString sRect = comphelper::string::join("; ", aRect);
+ if (pViewShell)
+ {
+ // Just notify pViewShell about our existing selection.
+ if (pViewShell != GetShell()->GetSfxViewShell())
+ SfxLokHelper::notifyOtherView(GetShell()->GetSfxViewShell(), pViewShell, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", sRect);
+ }
+ else
+ {
+ GetShell()->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRect.getStr());
+ SfxLokHelper::notifyOtherViews(GetShell()->GetSfxViewShell(), LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", sRect);
+ }
+}
+
+// This rectangle gets painted anew, therefore the SSelection in this
+// area is invalid.
+void SwShellCursor::Invalidate( const SwRect& rRect )
+{
+ for(SwPaM& rPaM : GetRingContainer())
+ {
+ SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rPaM);
+ // skip any non SwShellCursor objects in the ring
+ // see also: SwAutoFormat::DeleteSel()
+ if(pShCursor)
+ pShCursor->SwSelPaintRects::Invalidate(rRect);
+ }
+}
+
+void SwShellCursor::Hide()
+{
+ for(SwPaM& rPaM : GetRingContainer())
+ {
+ SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rPaM);
+ if(pShCursor)
+ pShCursor->SwSelPaintRects::Hide();
+ }
+}
+
+SwCursor* SwShellCursor::Create( SwPaM* pRing ) const
+{
+ return new SwShellCursor( *GetShell(), *GetPoint(), GetPtPos(), pRing );
+}
+
+short SwShellCursor::MaxReplaceArived()
+{
+ short nRet = RET_YES;
+ SvxSearchDialog* pDlg = SwView::GetSearchDialog();
+ if( pDlg )
+ {
+ // Terminate old actions. The table-frames get constructed and
+ // a SSelection can be created.
+ std::vector<sal_uInt16> vActionCounts;
+ for(SwViewShell& rShell : const_cast< SwCursorShell* >( GetShell() )->GetRingContainer())
+ {
+ sal_uInt16 nActCnt = 0;
+ while(rShell.ActionPend())
+ {
+ rShell.EndAction();
+ ++nActCnt;
+ }
+ vActionCounts.push_back(nActCnt);
+ }
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pDlg->getDialog(), "modules/swriter/ui/asksearchdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xDialog(xBuilder->weld_message_dialog("AskSearchDialog"));
+ nRet = xDialog->run();
+ auto pActionCount = vActionCounts.begin();
+ for(SwViewShell& rShell : const_cast< SwCursorShell* >( GetShell() )->GetRingContainer())
+ {
+ while(*pActionCount)
+ {
+ rShell.StartAction();
+ --(*pActionCount);
+ }
+ ++pActionCount;
+ }
+ }
+ else
+ // otherwise from the Basic, and then switch to RET_YES
+ nRet = RET_YES;
+
+ return nRet;
+}
+
+void SwShellCursor::SaveTableBoxContent( const SwPosition* pPos )
+{
+ const_cast<SwCursorShell*>(GetShell())->SaveTableBoxContent( pPos );
+}
+
+bool SwShellCursor::UpDown( bool bUp, sal_uInt16 nCnt )
+{
+ // tdf#124603 trigger pending spell checking of the node
+ if ( nCnt == 1 )
+ {
+ SwTextNode* pNode = GetPoint()->nNode.GetNode().GetTextNode();
+ if( pNode && SwTextNode::WrongState::PENDING == pNode->GetWrongDirty() )
+ {
+ SwWrtShell* pShell = pNode->GetDoc().GetDocShell()->GetWrtShell();
+ if ( pShell && !pShell->IsSelection() && !pShell->IsSelFrameMode() )
+ {
+ const SwViewOption* pVOpt = pShell->GetViewOptions();
+ if ( pVOpt && pVOpt->IsOnlineSpell() )
+ {
+ const bool bOldViewLock = pShell->IsViewLocked();
+ pShell->LockView( true );
+
+ SwTextFrame* pFrame(
+ static_cast<SwTextFrame*>(pNode->getLayoutFrame(GetShell()->GetLayout())));
+ SwRect aRepaint(pFrame->AutoSpell_(*pNode, 0));
+ if (aRepaint.HasArea())
+ pShell->InvalidateWindows(aRepaint);
+
+ pShell->LockView( bOldViewLock );
+ }
+ }
+ }
+ }
+
+ return SwCursor::UpDown( bUp, nCnt,
+ &GetPtPos(), GetShell()->GetUpDownX(),
+ *GetShell()->GetLayout());
+}
+
+// if <true> than the cursor can be set to the position.
+bool SwShellCursor::IsAtValidPos( bool bPoint ) const
+{
+ if( GetShell() && ( GetShell()->IsAllProtect() ||
+ GetShell()->GetViewOptions()->IsReadonly() ||
+ ( GetShell()->Imp()->GetDrawView() &&
+ GetShell()->Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() )))
+ return true;
+
+ return SwCursor::IsAtValidPos( bPoint );
+}
+
+SwShellTableCursor::SwShellTableCursor( const SwCursorShell& rCursorSh,
+ const SwPosition& rPos )
+ : SwCursor(rPos,nullptr), SwShellCursor(rCursorSh, rPos), SwTableCursor(rPos)
+{
+}
+
+SwShellTableCursor::SwShellTableCursor( const SwCursorShell& rCursorSh,
+ const SwPosition& rMkPos, const Point& rMkPt,
+ const SwPosition& rPtPos, const Point& rPtPt )
+ : SwCursor(rPtPos,nullptr), SwShellCursor(rCursorSh, rPtPos), SwTableCursor(rPtPos)
+{
+ SetMark();
+ *GetMark() = rMkPos;
+ GetMkPos() = rMkPt;
+ GetPtPos() = rPtPt;
+}
+
+SwShellTableCursor::~SwShellTableCursor() {}
+
+void SwShellTableCursor::SetMark() { SwShellCursor::SetMark(); }
+
+SwCursor* SwShellTableCursor::Create( SwPaM* pRing ) const
+{
+ return SwShellCursor::Create( pRing );
+}
+
+short SwShellTableCursor::MaxReplaceArived()
+{
+ return SwShellCursor::MaxReplaceArived();
+}
+
+void SwShellTableCursor::SaveTableBoxContent( const SwPosition* pPos )
+{
+ SwShellCursor::SaveTableBoxContent( pPos );
+}
+
+void SwShellTableCursor::FillRects()
+{
+ // Calculate the new rectangles. If the cursor is still "parked" do nothing
+ if (m_SelectedBoxes.empty() || m_bParked || !GetPoint()->nNode.GetIndex())
+ return;
+
+ bool bStart = true;
+ SwRegionRects aReg( comphelper::LibreOfficeKit::isActive()
+ ? GetShell()->getIDocumentLayoutAccess().GetCurrentLayout()->getFrameArea()
+ : GetShell()->VisArea() );
+ SwNodes& rNds = GetDoc().GetNodes();
+ SwFrame* pEndFrame = nullptr;
+ for (size_t n = 0; n < m_SelectedBoxes.size(); ++n)
+ {
+ const SwStartNode* pSttNd = m_SelectedBoxes[n]->GetSttNd();
+ const SwTableNode* pSelTableNd = pSttNd->FindTableNode();
+
+ SwNodeIndex aIdx( *pSttNd );
+ SwContentNode* pCNd = rNds.GoNextSection( &aIdx, true, false );
+
+ // table in table
+ // (see also lcl_FindTopLevelTable in unoobj2.cxx for a different
+ // version to do this)
+ const SwTableNode* pCurTableNd = pCNd ? pCNd->FindTableNode() : nullptr;
+ while ( pSelTableNd != pCurTableNd && pCurTableNd )
+ {
+ aIdx = pCurTableNd->EndOfSectionIndex();
+ pCNd = rNds.GoNextSection( &aIdx, true, false );
+ pCurTableNd = pCNd->FindTableNode();
+ }
+
+ if( !pCNd )
+ continue;
+
+ std::pair<Point, bool> const tmp(GetSttPos(), false);
+ SwFrame* pFrame = pCNd->getLayoutFrame(GetShell()->GetLayout(), nullptr, &tmp);
+ while( pFrame && !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+
+ OSL_ENSURE( pFrame, "Node not in a table" );
+
+ while ( pFrame )
+ {
+ if( aReg.GetOrigin().Overlaps( pFrame->getFrameArea() ) )
+ {
+ aReg -= pFrame->getFrameArea();
+ if (bStart)
+ {
+ bStart = false;
+ m_aStart = SwRect(pFrame->getFrameArea().Left(), pFrame->getFrameArea().Top(), 1, pFrame->getFrameArea().Height());
+ }
+ }
+
+ pEndFrame = pFrame;
+ pFrame = pFrame->GetNextCellLeaf();
+ }
+ }
+ if (pEndFrame)
+ m_aEnd = SwRect(pEndFrame->getFrameArea().Right(), pEndFrame->getFrameArea().Top(), 1, pEndFrame->getFrameArea().Height());
+ aReg.Invert();
+ insert( begin(), aReg.begin(), aReg.end() );
+}
+
+void SwShellTableCursor::FillStartEnd(SwRect& rStart, SwRect& rEnd) const
+{
+ rStart = m_aStart;
+ rEnd = m_aEnd;
+}
+
+// Check if the SPoint is within the Table-SSelection.
+bool SwShellTableCursor::Contains( const Point& rPt ) const
+{
+ // Calculate the new rectangles. If the cursor is still "parked" do nothing
+ if (m_SelectedBoxes.empty() || m_bParked || !GetPoint()->nNode.GetIndex())
+ return false;
+
+ SwNodes& rNds = GetDoc().GetNodes();
+ for (size_t n = 0; n < m_SelectedBoxes.size(); ++n)
+ {
+ SwNodeIndex aIdx( *m_SelectedBoxes[n]->GetSttNd() );
+ SwContentNode* pCNd = rNds.GoNextSection( &aIdx, true, false );
+ if( !pCNd )
+ continue;
+
+ std::pair<Point, bool> const tmp(GetPtPos(), true);
+ SwFrame* pFrame = pCNd->getLayoutFrame(GetShell()->GetLayout(), nullptr, &tmp);
+ while( pFrame && !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+ OSL_ENSURE( pFrame, "Node not in a table" );
+ if( pFrame && pFrame->getFrameArea().Contains( rPt ) )
+ return true;
+
+ for ( SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame); pCellFrame; pCellFrame = pCellFrame->GetFollowCell() )
+ {
+ if( pCellFrame->getFrameArea().Contains( rPt ) )
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwShellTableCursor::IsAtValidPos( bool bPoint ) const
+{
+ return SwShellCursor::IsAtValidPos( bPoint );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/CntntIdxStore.cxx b/sw/source/core/doc/CntntIdxStore.cxx
new file mode 100644
index 000000000..38875b774
--- /dev/null
+++ b/sw/source/core/doc/CntntIdxStore.cxx
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <bookmark.hxx>
+#include <cntfrm.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <editsh.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <functional>
+#include <mvsave.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <redline.hxx>
+#include <sal/types.h>
+#include <unocrsr.hxx>
+#include <txtfrm.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+using namespace ::boost;
+using namespace ::sw::mark;
+
+namespace
+{
+ // #i59534: If a paragraph will be split we have to restore some redline positions
+ // This help function checks a position compared with a node and a content index
+
+ const int BEFORE_NODE = 0; // Position before the given node index
+ const int BEFORE_SAME_NODE = 1; // Same node index but content index before given content index
+ const int SAME_POSITION = 2; // Same node index and samecontent index
+ const int BEHIND_SAME_NODE = 3; // Same node index but content index behind given content index
+ const int BEHIND_NODE = 4; // Position behind the given node index
+
+ int lcl_RelativePosition( const SwPosition& rPos, SwNodeOffset nNode, sal_Int32 nContent )
+ {
+ SwNodeOffset nIndex = rPos.nNode.GetIndex();
+ int nReturn = BEFORE_NODE;
+ if( nIndex == nNode )
+ {
+ const sal_Int32 nCntIdx = rPos.nContent.GetIndex();
+ if( nCntIdx < nContent )
+ nReturn = BEFORE_SAME_NODE;
+ else if( nCntIdx == nContent )
+ nReturn = SAME_POSITION;
+ else
+ nReturn = BEHIND_SAME_NODE;
+ }
+ else if( nIndex > nNode )
+ nReturn = BEHIND_NODE;
+ return nReturn;
+ }
+ struct MarkEntry
+ {
+ tools::Long m_nIdx;
+ bool m_bOther;
+ sal_Int32 m_nContent;
+#if 0
+#include <sal/log.hxx>
+ void Dump()
+ {
+ SAL_INFO("sw.core", "Index: " << m_nIdx << "\tOther: " << m_bOther << "\tContent: " << m_nContent);
+ }
+#endif
+ };
+ struct PaMEntry
+ {
+ SwPaM* m_pPaM;
+ bool m_isMark;
+ sal_Int32 m_nContent;
+ };
+ struct OffsetUpdater
+ {
+ const SwContentNode* m_pNewContentNode;
+ const sal_Int32 m_nOffset;
+ OffsetUpdater(SwContentNode const * pNewContentNode, sal_Int32 nOffset)
+ : m_pNewContentNode(pNewContentNode), m_nOffset(nOffset) {};
+ void operator()(SwPosition& rPos, sal_Int32 nContent) const
+ {
+ rPos.nNode = *m_pNewContentNode;
+ rPos.nContent.Assign(const_cast<SwContentNode*>(m_pNewContentNode), nContent + m_nOffset);
+ };
+ };
+ struct LimitUpdater
+ {
+ const SwContentNode* m_pNewContentNode;
+ const sal_uLong m_nLen;
+ const sal_Int32 m_nCorrLen;
+ LimitUpdater(SwContentNode const * pNewContentNode, sal_uLong nLen, sal_Int32 nCorrLen)
+ : m_pNewContentNode(pNewContentNode), m_nLen(nLen), m_nCorrLen(nCorrLen) {};
+ void operator()(SwPosition& rPos, sal_Int32 nContent) const
+ {
+ rPos.nNode = *m_pNewContentNode;
+ if( nContent < m_nCorrLen )
+ {
+ rPos.nContent.Assign(const_cast<SwContentNode*>(m_pNewContentNode), std::min( nContent, static_cast<sal_Int32>(m_nLen) ) );
+ }
+ else
+ {
+ rPos.nContent -= m_nCorrLen;
+ }
+ };
+ };
+ struct ContentIdxStoreImpl : sw::mark::ContentIdxStore
+ {
+ std::vector<MarkEntry> m_aBkmkEntries;
+ std::vector<MarkEntry> m_aRedlineEntries;
+ std::vector<MarkEntry> m_aFlyEntries;
+ std::vector<PaMEntry> m_aUnoCursorEntries;
+ std::vector<PaMEntry> m_aShellCursorEntries;
+ typedef std::function<void (SwPosition& rPos, sal_Int32 nContent)> updater_t;
+ virtual void Clear() override
+ {
+ m_aBkmkEntries.clear();
+ m_aRedlineEntries.clear();
+ m_aFlyEntries.clear();
+ m_aUnoCursorEntries.clear();
+ m_aShellCursorEntries.clear();
+ }
+ virtual bool Empty() override
+ {
+ return m_aBkmkEntries.empty() && m_aRedlineEntries.empty() && m_aFlyEntries.empty() && m_aUnoCursorEntries.empty() && m_aShellCursorEntries.empty();
+ }
+ virtual void Save(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit=false) override
+ {
+ SaveBkmks(rDoc, nNode, nContent);
+ SaveRedlines(rDoc, nNode, nContent);
+ SaveFlys(rDoc, nNode, nContent, bSaveFlySplit);
+ SaveUnoCursors(rDoc, nNode, nContent);
+ SaveShellCursors(rDoc, nNode, nContent);
+ }
+ virtual void Restore(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nOffset=0, bool bAuto = false, bool bAtStart = false, RestoreMode eMode = RestoreMode::All) override
+ {
+ SwContentNode* pCNd = rDoc.GetNodes()[ nNode ]->GetContentNode();
+ updater_t aUpdater = OffsetUpdater(pCNd, nOffset);
+ if (eMode & RestoreMode::NonFlys)
+ {
+ RestoreBkmks(rDoc, aUpdater);
+ RestoreRedlines(rDoc, aUpdater);
+ RestoreUnoCursors(aUpdater);
+ RestoreShellCursors(aUpdater);
+ }
+ if (eMode & RestoreMode::Flys)
+ {
+ RestoreFlys(rDoc, aUpdater, bAuto, bAtStart);
+ }
+ }
+ virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode eMode = RestoreMode::All) override
+ {
+ SwContentNode* pCNd = rNd.GetContentNode();
+ SwDoc& rDoc = rNd.GetDoc();
+ updater_t aUpdater = LimitUpdater(pCNd, nLen, nCorrLen);
+ if (eMode & RestoreMode::NonFlys)
+ {
+ RestoreBkmks(rDoc, aUpdater);
+ RestoreRedlines(rDoc, aUpdater);
+ RestoreUnoCursors(aUpdater);
+ RestoreShellCursors(aUpdater);
+ }
+ if (eMode & RestoreMode::Flys)
+ {
+ RestoreFlys(rDoc, aUpdater, false, false);
+ }
+ }
+
+ private:
+ void SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater);
+ void SaveRedlines(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreRedlines(SwDoc& rDoc, updater_t const & rUpdater);
+ void SaveFlys(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit);
+ void RestoreFlys(SwDoc& rDoc, updater_t const & rUpdater, bool bAuto, bool bAtStart);
+ void SaveUnoCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreUnoCursors(updater_t const & rUpdater);
+ void SaveShellCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent);
+ void RestoreShellCursors(updater_t const & rUpdater);
+ static const SwPosition& GetRightMarkPos(::sw::mark::IMark const * pMark, bool bOther)
+ { return bOther ? pMark->GetOtherMarkPos() : pMark->GetMarkPos(); };
+ static void SetRightMarkPos(MarkBase* pMark, bool bOther, const SwPosition* const pPos)
+ { bOther ? pMark->SetOtherMarkPos(*pPos) : pMark->SetMarkPos(*pPos); };
+ };
+ void lcl_ChkPaM( std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM, const bool bGetPoint, bool bSetMark)
+ {
+ const SwPosition* pPos = &rPaM.GetBound(bGetPoint);
+ if( pPos->nNode.GetIndex() == nNode && pPos->nContent.GetIndex() < nContent )
+ {
+ const PaMEntry aEntry = { &rPaM, bSetMark, pPos->nContent.GetIndex() };
+ rPaMEntries.push_back(aEntry);
+ }
+ }
+ void lcl_ChkPaMBoth( std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM)
+ {
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, true, true);
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, false, false);
+ }
+ void lcl_ChkUnoCrsrPaMBoth(std::vector<PaMEntry>& rPaMEntries, const SwNodeOffset nNode, const sal_Int32 nContent, SwPaM& rPaM)
+ {
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, true, false);
+ lcl_ChkPaM(rPaMEntries, nNode, nContent, rPaM, false, true);
+ }
+
+#if 0
+ static void DumpEntries(std::vector<MarkEntry>* pEntries)
+ {
+ for (MarkEntry& aEntry : *pEntries)
+ aEntry.Dump();
+ }
+#endif
+}
+
+void ContentIdxStoreImpl::SaveBkmks(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ const IDocumentMarkAccess::const_iterator_t ppBkmkEnd = pMarkAccess->getAllMarksEnd();
+ for(
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin();
+ ppBkmk != ppBkmkEnd;
+ ++ppBkmk)
+ {
+ const ::sw::mark::IMark* pBkmk = *ppBkmk;
+ bool bMarkPosEqual = false;
+ if(pBkmk->GetMarkPos().nNode.GetIndex() == nNode
+ && pBkmk->GetMarkPos().nContent.GetIndex() <= nContent)
+ {
+ if(pBkmk->GetMarkPos().nContent.GetIndex() < nContent)
+ {
+ const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().nContent.GetIndex() };
+ m_aBkmkEntries.push_back(aEntry);
+ }
+ else // if a bookmark position is equal nContent, the other position
+ bMarkPosEqual = true; // has to decide if it is added to the array
+ }
+ if(pBkmk->IsExpanded()
+ && pBkmk->GetOtherMarkPos().nNode.GetIndex() == nNode
+ && pBkmk->GetOtherMarkPos().nContent.GetIndex() <= nContent)
+ {
+ if(bMarkPosEqual)
+ { // the other position is before, the (main) position is equal
+ const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), false, pBkmk->GetMarkPos().nContent.GetIndex() };
+ m_aBkmkEntries.push_back(aEntry);
+ }
+ const MarkEntry aEntry = { static_cast<tools::Long>(ppBkmk - pMarkAccess->getAllMarksBegin()), true, pBkmk->GetOtherMarkPos().nContent.GetIndex() };
+ m_aBkmkEntries.push_back(aEntry);
+ }
+ }
+}
+
+void ContentIdxStoreImpl::RestoreBkmks(SwDoc& rDoc, updater_t const & rUpdater)
+{
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ for (const MarkEntry& aEntry : m_aBkmkEntries)
+ {
+ if (MarkBase *const pMark = pMarkAccess->getAllMarksBegin().get()[aEntry.m_nIdx])
+ {
+ SwPosition aNewPos(GetRightMarkPos(pMark, aEntry.m_bOther));
+ rUpdater(aNewPos, aEntry.m_nContent);
+ SetRightMarkPos(pMark, aEntry.m_bOther, &aNewPos);
+ }
+ }
+ if (!m_aBkmkEntries.empty())
+ { // tdf#105705 sort bookmarks because SaveBkmks special handling of
+ // "bMarkPosEqual" may destroy sort order
+ pMarkAccess->assureSortedMarkContainers();
+ }
+}
+
+void ContentIdxStoreImpl::SaveRedlines(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ SwRedlineTable const & rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ tools::Long nIdx = 0;
+ for (const SwRangeRedline* pRdl : rRedlineTable)
+ {
+ int nPointPos = lcl_RelativePosition( *pRdl->GetPoint(), nNode, nContent );
+ int nMarkPos = pRdl->HasMark() ? lcl_RelativePosition( *pRdl->GetMark(), nNode, nContent ) :
+ nPointPos;
+ // #i59534: We have to store the positions inside the same node before the insert position
+ // and the one at the insert position if the corresponding Point/Mark position is before
+ // the insert position.
+ if( nPointPos == BEFORE_SAME_NODE ||
+ ( nPointPos == SAME_POSITION && nMarkPos < SAME_POSITION ) )
+ {
+ const MarkEntry aEntry = { nIdx, false, pRdl->GetPoint()->nContent.GetIndex() };
+ m_aRedlineEntries.push_back(aEntry);
+ }
+ if( pRdl->HasMark() && ( nMarkPos == BEFORE_SAME_NODE ||
+ ( nMarkPos == SAME_POSITION && nPointPos < SAME_POSITION ) ) )
+ {
+ const MarkEntry aEntry = { nIdx, true, pRdl->GetMark()->nContent.GetIndex() };
+ m_aRedlineEntries.push_back(aEntry);
+ }
+ ++nIdx;
+ }
+}
+
+void ContentIdxStoreImpl::RestoreRedlines(SwDoc& rDoc, updater_t const & rUpdater)
+{
+ const SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for (const MarkEntry& aEntry : m_aRedlineEntries)
+ {
+ SwPosition* const pPos = aEntry.m_bOther
+ ? rRedlTable[ aEntry.m_nIdx ]->GetMark()
+ : rRedlTable[ aEntry.m_nIdx ]->GetPoint();
+ rUpdater(*pPos, aEntry.m_nContent);
+ }
+}
+
+void ContentIdxStoreImpl::SaveFlys(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit)
+{
+ SwContentNode *pNode = rDoc.GetNodes()[nNode]->GetContentNode();
+ if( !pNode )
+ return;
+ SwFrame* pFrame = pNode->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pFrame )
+ {
+ // sw_redlinehide: this looks like an invalid optimisation if merged,
+ // assuming that flys in deleted redlines should be saved here too.
+ if ((!pFrame->IsTextFrame() || !static_cast<SwTextFrame const*>(pFrame)->GetMergedPara())
+ && !pFrame->GetDrawObjs())
+ return; // if we have a layout and no DrawObjs, we can skip this
+ }
+ MarkEntry aSave = { 0, false, 0 };
+ for (const SwFrameFormat* pFrameFormat : *rDoc.GetSpzFrameFormats())
+ {
+ if ( RES_FLYFRMFMT == pFrameFormat->Which() || RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnchor.GetContentAnchor();
+ if ( pAPos && ( nNode == pAPos->nNode.GetIndex() ) &&
+ ( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() ||
+ RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() ) )
+ {
+ bool bSkip = false;
+ aSave.m_bOther = false;
+ aSave.m_nContent = pAPos->nContent.GetIndex();
+ if ( RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() )
+ {
+ if( nContent <= aSave.m_nContent )
+ {
+ if( bSaveFlySplit )
+ aSave.m_bOther = true;
+ else
+ bSkip = true;
+ }
+ }
+ if(!bSkip)
+ m_aFlyEntries.push_back(aSave);
+ }
+ }
+ ++aSave.m_nIdx;
+ }
+}
+
+void ContentIdxStoreImpl::RestoreFlys(SwDoc& rDoc, updater_t const & rUpdater, bool bAuto, bool bAtStart)
+{
+ SwFrameFormats* pSpz = rDoc.GetSpzFrameFormats();
+ for (const MarkEntry& aEntry : m_aFlyEntries)
+ {
+ if(!aEntry.m_bOther)
+ {
+ SwFrameFormat *pFrameFormat = (*pSpz)[ aEntry.m_nIdx ];
+ const SwFormatAnchor& rFlyAnchor = pFrameFormat->GetAnchor();
+ if( rFlyAnchor.GetContentAnchor() )
+ {
+ if(bAtStart && RndStdIds::FLY_AT_PARA == rFlyAnchor.GetAnchorId())
+ continue;
+ SwFormatAnchor aNew( rFlyAnchor );
+ SwPosition aNewPos( *rFlyAnchor.GetContentAnchor() );
+ rUpdater(aNewPos, aEntry.m_nContent);
+ if ( RndStdIds::FLY_AT_CHAR != rFlyAnchor.GetAnchorId() )
+ {
+ aNewPos.nContent.Assign( nullptr, 0 );
+ }
+ aNew.SetAnchor( &aNewPos );
+ pFrameFormat->SetFormatAttr( aNew );
+ }
+ }
+ else if( bAuto )
+ {
+ SwFrameFormat* pFrameFormat = (*pSpz)[ aEntry.m_nIdx ];
+ const SfxPoolItem* pAnchor = &pFrameFormat->GetAnchor();
+ pFrameFormat->CallSwClientNotify(sw::LegacyModifyHint(pAnchor, pAnchor));
+ }
+ }
+}
+
+void ContentIdxStoreImpl::SaveUnoCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ rDoc.cleanupUnoCursorTable();
+ for (const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
+ {
+ auto pUnoCursor(pWeakUnoCursor.lock());
+ if(!pUnoCursor)
+ continue;
+ for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
+ {
+ lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries, nNode, nContent, rPaM);
+ }
+ const SwUnoTableCursor* pUnoTableCursor = dynamic_cast<const SwUnoTableCursor*>(pUnoCursor.get());
+ if( pUnoTableCursor )
+ {
+ for(SwPaM& rPaM : const_cast<SwUnoTableCursor*>(pUnoTableCursor)->GetSelRing().GetRingContainer())
+ {
+ lcl_ChkUnoCrsrPaMBoth(m_aUnoCursorEntries, nNode, nContent, rPaM);
+ }
+ }
+ }
+}
+
+void ContentIdxStoreImpl::RestoreUnoCursors(updater_t const & rUpdater)
+{
+ for (const PaMEntry& aEntry : m_aUnoCursorEntries)
+ {
+ rUpdater(aEntry.m_pPaM->GetBound(!aEntry.m_isMark), aEntry.m_nContent);
+ }
+}
+
+void ContentIdxStoreImpl::SaveShellCursors(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent)
+{
+ SwCursorShell* pShell = rDoc.GetEditShell();
+ if (!pShell)
+ return;
+ for(SwViewShell& rCurShell : pShell->GetRingContainer())
+ {
+ if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rCurShell) )
+ {
+ SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
+ if( _pStackCursor )
+ for (;;)
+ {
+ lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, *_pStackCursor);
+ if (!_pStackCursor)
+ break;
+ _pStackCursor = _pStackCursor->GetNext();
+ if (_pStackCursor == pCursorShell->GetStackCursor())
+ break;
+ }
+
+ for(SwPaM& rPaM : pCursorShell->GetCursor_()->GetRingContainer())
+ {
+ lcl_ChkPaMBoth( m_aShellCursorEntries, nNode, nContent, rPaM);
+ }
+ }
+ }
+}
+
+void ContentIdxStoreImpl::RestoreShellCursors(updater_t const & rUpdater)
+{
+ for (const PaMEntry& aEntry : m_aShellCursorEntries)
+ {
+ rUpdater(aEntry.m_pPaM->GetBound(aEntry.m_isMark), aEntry.m_nContent);
+ }
+}
+
+namespace sw::mark {
+ std::shared_ptr<ContentIdxStore> ContentIdxStore::Create()
+ {
+ return std::make_shared<ContentIdxStoreImpl>();
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentChartDataProviderManager.cxx b/sw/source/core/doc/DocumentChartDataProviderManager.cxx
new file mode 100644
index 000000000..e559892ab
--- /dev/null
+++ b/sw/source/core/doc/DocumentChartDataProviderManager.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+*/
+
+#include <DocumentChartDataProviderManager.hxx>
+
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <vcl/svapp.hxx>
+#include <swtable.hxx>
+#include <unochart.hxx>
+#include <ndole.hxx>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+namespace sw {
+
+DocumentChartDataProviderManager::DocumentChartDataProviderManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+
+}
+
+SwChartDataProvider * DocumentChartDataProviderManager::GetChartDataProvider( bool bCreate ) const
+{
+ // since there must be only one instance of this object per document
+ // we need a mutex here
+ SolarMutexGuard aGuard;
+
+ if (bCreate && !maChartDataProviderImplRef.is())
+ {
+ maChartDataProviderImplRef = new SwChartDataProvider(m_rDoc);
+ }
+ return maChartDataProviderImplRef.get();
+}
+
+void DocumentChartDataProviderManager::CreateChartInternalDataProviders( const SwTable *pTable )
+{
+ if (!pTable)
+ return;
+
+ OUString aName( pTable->GetFrameFormat()->GetName() );
+ SwOLENode *pONd;
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *m_rDoc.GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while (nullptr != (pStNd = aIdx.GetNode().GetStartNode()))
+ {
+ ++aIdx;
+ pONd = aIdx.GetNode().GetOLENode();
+ if( pONd &&
+ aName == pONd->GetChartTableName() /* OLE node is chart? */ &&
+ nullptr != (pONd->getLayoutFrame( m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout() )) /* chart frame is not hidden */ )
+ {
+ uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
+ if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
+ {
+ uno::Reference< chart2::XChartDocument > xChart( xIP->getComponent(), UNO_QUERY );
+ if (xChart.is())
+ xChart->createInternalDataProvider( true );
+
+ // there may be more than one chart for each table thus we need to continue the loop...
+ }
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+}
+
+SwChartLockController_Helper & DocumentChartDataProviderManager::GetChartControllerHelper()
+{
+ if (!mpChartControllerHelper)
+ {
+ mpChartControllerHelper.reset(new SwChartLockController_Helper( & m_rDoc ));
+ }
+ return *mpChartControllerHelper;
+}
+
+DocumentChartDataProviderManager::~DocumentChartDataProviderManager()
+{
+ // clean up chart related structures...
+ // Note: the chart data provider gets already disposed in ~SwDocShell
+ // since all UNO API related functionality requires an existing SwDocShell
+ // this assures that dispose gets called if there is need for it.
+ maChartDataProviderImplRef.clear();
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentContentOperationsManager.cxx b/sw/source/core/doc/DocumentContentOperationsManager.cxx
new file mode 100644
index 000000000..4b4404f45
--- /dev/null
+++ b/sw/source/core/doc/DocumentContentOperationsManager.cxx
@@ -0,0 +1,5400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentContentOperationsManager.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <UndoManager.hxx>
+#include <docary.hxx>
+#include <textboxhelper.hxx>
+#include <dcontact.hxx>
+#include <grfatr.hxx>
+#include <numrule.hxx>
+#include <charfmt.hxx>
+#include <ndgrf.hxx>
+#include <ndnotxt.hxx>
+#include <ndole.hxx>
+#include <breakit.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtcnct.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <redline.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <frmtool.hxx>
+#include <unocrsr.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <paratr.hxx>
+#include <txatbase.hxx>
+#include <UndoRedline.hxx>
+#include <undobj.hxx>
+#include <UndoBookmark.hxx>
+#include <UndoDelete.hxx>
+#include <UndoSplitMove.hxx>
+#include <UndoOverwrite.hxx>
+#include <UndoInsert.hxx>
+#include <UndoAttribute.hxx>
+#include <rolbck.hxx>
+#include <acorrect.hxx>
+#include <bookmark.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <hints.hxx>
+#include <fmtflcnt.hxx>
+#include <docedt.hxx>
+#include <frameformats.hxx>
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <sfx2/Metadatable.hxx>
+#include <sot/exchange.hxx>
+#include <svl/stritem.hxx>
+#include <svl/itemiter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdouno.hxx>
+#include <tools/globname.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <com/sun/star/i18n/Boundary.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <tuple>
+#include <memory>
+
+using namespace ::com::sun::star::i18n;
+
+namespace
+{
+ // Copy method from SwDoc
+ // Prevent copying into Flys that are anchored in the range
+ bool lcl_ChkFlyFly( SwDoc& rDoc, SwNodeOffset nSttNd, SwNodeOffset nEndNd,
+ SwNodeOffset nInsNd )
+ {
+ const SwFrameFormats& rFrameFormatTable = *rDoc.GetSpzFrameFormats();
+
+ for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
+ {
+ SwFrameFormat const*const pFormat = rFrameFormatTable[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
+ nSttNd <= pAPos->nNode.GetIndex() &&
+ pAPos->nNode.GetIndex() < nEndNd )
+ {
+ const SwFormatContent& rContent = pFormat->GetContent();
+ SwStartNode* pSNd;
+ if( !rContent.GetContentIdx() ||
+ nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ))
+ continue;
+
+ if( pSNd->GetIndex() < nInsNd &&
+ nInsNd < pSNd->EndOfSectionIndex() )
+ // Do not copy !
+ return true;
+
+ if( lcl_ChkFlyFly( rDoc, pSNd->GetIndex(),
+ pSNd->EndOfSectionIndex(), nInsNd ) )
+ // Do not copy !
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, SwNodeOffset & rDelCount)
+ {
+ SwNodeIndex const& rStart(rSourcePaM.Start()->nNode);
+ // Special handling for SwDoc::AppendDoc
+ if (rSourcePaM.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1
+ == rStart.GetIndex())
+ {
+ rDelCount = SwNodeOffset(1);
+ return SwNodeIndex(rStart, +1);
+ }
+ else
+ {
+ rDelCount = SwNodeOffset(0);
+ return rStart;
+ }
+ }
+
+ /*
+ The CopyBookmarks function has to copy bookmarks from the source to the destination nodes
+ array. It is called after a call of the CopyNodes(..) function. But this function does not copy
+ every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied
+ if the corresponding end/start node is outside the copied pam.
+ The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node
+ index inside the pam.
+ rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number
+ of "non-copy" nodes between rPam.Start() and rLastIdx.
+ nNewIdx is the new position of interest.
+ */
+ void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const SwNodeOffset nNewIdx, SwNodeOffset& rDelCount )
+ {
+ SwNodeOffset nStart = rPam.Start()->nNode.GetIndex();
+ SwNodeOffset nEnd = rPam.End()->nNode.GetIndex();
+ if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward?
+ {
+ // We never copy the StartOfContent node
+ do // count "non-copy" nodes
+ {
+ SwNode& rNode = rLastIdx.GetNode();
+ if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
+ || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
+ {
+ ++rDelCount;
+ }
+ ++rLastIdx;
+ }
+ while( rLastIdx.GetIndex() < nNewIdx );
+ }
+ else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now,
+ // no move backward needed
+ {
+ while( rLastIdx.GetIndex() > nNewIdx )
+ {
+ SwNode& rNode = rLastIdx.GetNode();
+ if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd )
+ || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) )
+ {
+ --rDelCount;
+ }
+ rLastIdx--;
+ }
+ }
+ }
+
+ void lcl_SetCpyPos( const SwPosition& rOrigPos,
+ const SwPosition& rOrigStt,
+ const SwPosition& rCpyStt,
+ SwPosition& rChgPos,
+ SwNodeOffset nDelCount )
+ {
+ SwNodeOffset nNdOff = rOrigPos.nNode.GetIndex();
+ nNdOff -= rOrigStt.nNode.GetIndex();
+ nNdOff -= nDelCount;
+ sal_Int32 nContentPos = rOrigPos.nContent.GetIndex();
+
+ // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos>
+ rChgPos.nNode = nNdOff + rCpyStt.nNode.GetIndex();
+ if( !nNdOff )
+ {
+ // just adapt the content index
+ if( nContentPos > rOrigStt.nContent.GetIndex() )
+ nContentPos -= rOrigStt.nContent.GetIndex();
+ else
+ nContentPos = 0;
+ nContentPos += rCpyStt.nContent.GetIndex();
+ }
+ rChgPos.nContent.Assign( rChgPos.nNode.GetNode().GetContentNode(), nContentPos );
+ }
+
+}
+
+namespace sw
+{
+ // TODO: use SaveBookmark (from DelBookmarks)
+ void CopyBookmarks(const SwPaM& rPam, const SwPosition& rCpyPam)
+ {
+ const SwDoc& rSrcDoc = rPam.GetDoc();
+ SwDoc& rDestDoc = rCpyPam.GetDoc();
+ const IDocumentMarkAccess* const pSrcMarkAccess = rSrcDoc.getIDocumentMarkAccess();
+ ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
+
+ const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
+ SwPosition const*const pCpyStt = &rCpyPam;
+
+ std::vector< const ::sw::mark::IMark* > vMarksToCopy;
+ for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin();
+ ppMark != pSrcMarkAccess->getAllMarksEnd();
+ ++ppMark )
+ {
+ const ::sw::mark::IMark* const pMark = *ppMark;
+
+ const SwPosition& rMarkStart = pMark->GetMarkStart();
+ const SwPosition& rMarkEnd = pMark->GetMarkEnd();
+ // only include marks that are in the range and not touching both start and end
+ // - not for annotation or checkbox marks.
+ bool const isIncludeStart(
+ (rStt.nContent.GetIndex() == 0 // paragraph start selected?
+ // also: only if inserting at the start - cross reference
+ // marks require index to be 0, and there could be one
+ // on the target node already
+ && rCpyPam.nContent.GetIndex() == 0)
+ || rMarkStart != rStt);
+ bool const isIncludeEnd(
+ (rEnd.nNode.GetNode().IsTextNode() // paragraph end selected?
+ && rEnd.nContent.GetIndex() == rEnd.nNode.GetNode().GetTextNode()->Len())
+ || rMarkEnd != rEnd);
+ const bool bIsNotOnBoundary =
+ pMark->IsExpanded()
+ ? (isIncludeStart || isIncludeEnd) // rMarkStart != rMarkEnd
+ : (isIncludeStart && isIncludeEnd); // rMarkStart == rMarkEnd
+ const IDocumentMarkAccess::MarkType aMarkType = IDocumentMarkAccess::GetType(*pMark);
+ if ( rMarkStart >= rStt && rMarkEnd <= rEnd
+ && ( bIsNotOnBoundary
+ || aMarkType == IDocumentMarkAccess::MarkType::ANNOTATIONMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
+ || aMarkType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
+ {
+ vMarksToCopy.push_back(pMark);
+ }
+ }
+ // We have to count the "non-copied" nodes...
+ SwNodeOffset nDelCount;
+ SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
+ for(const sw::mark::IMark* const pMark : vMarksToCopy)
+ {
+ SwPaM aTmpPam(*pCpyStt);
+ lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().nNode.GetIndex(), nDelCount);
+ lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount);
+ if(pMark->IsExpanded())
+ {
+ aTmpPam.SetMark();
+ lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().nNode.GetIndex(), nDelCount);
+ lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount);
+ }
+
+ ::sw::mark::IMark* const pNewMark = rDestDoc.getIDocumentMarkAccess()->makeMark(
+ aTmpPam,
+ pMark->GetName(),
+ IDocumentMarkAccess::GetType(*pMark),
+ ::sw::mark::InsertMode::CopyText);
+ // Explicitly try to get exactly the same name as in the source
+ // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name
+ if (pNewMark == nullptr)
+ {
+ assert(IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK
+ || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
+ continue; // can't insert duplicate cross reference mark
+ }
+ rDestDoc.getIDocumentMarkAccess()->renameMark(pNewMark, pMark->GetName());
+
+ // copying additional attributes for bookmarks or fieldmarks
+ ::sw::mark::IBookmark* const pNewBookmark =
+ dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark);
+ const ::sw::mark::IBookmark* const pOldBookmark =
+ dynamic_cast< const ::sw::mark::IBookmark* >(pMark);
+ if (pNewBookmark && pOldBookmark)
+ {
+ pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode());
+ pNewBookmark->SetShortName(pOldBookmark->GetShortName());
+ pNewBookmark->Hide(pOldBookmark->IsHidden());
+ pNewBookmark->SetHideCondition(pOldBookmark->GetHideCondition());
+ }
+ ::sw::mark::IFieldmark* const pNewFieldmark =
+ dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark);
+ const ::sw::mark::IFieldmark* const pOldFieldmark =
+ dynamic_cast< const ::sw::mark::IFieldmark* >(pMark);
+ if (pNewFieldmark && pOldFieldmark)
+ {
+ pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname());
+ pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext());
+ ::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters();
+ const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters();
+ for (const auto& rEntry : *pOldParams )
+ {
+ pNewParams->insert( rEntry );
+ }
+ }
+
+ ::sfx2::Metadatable const*const pMetadatable(
+ dynamic_cast< ::sfx2::Metadatable const* >(pMark));
+ ::sfx2::Metadatable *const pNewMetadatable(
+ dynamic_cast< ::sfx2::Metadatable * >(pNewMark));
+ if (pMetadatable && pNewMetadatable)
+ {
+ pNewMetadatable->RegisterAsCopyOf(*pMetadatable);
+ }
+ }
+ }
+} // namespace sw
+
+namespace
+{
+ void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam )
+ {
+ const SwDoc& rSrcDoc = rPam.GetDoc();
+ const SwRedlineTable& rTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ if( rTable.empty() )
+ return;
+
+ SwDoc& rDestDoc = rCpyPam.GetDoc();
+ SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End();
+ std::unique_ptr<SwPaM> pDelPam;
+ const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
+ // We have to count the "non-copied" nodes
+ SwNodeOffset nDelCount;
+ SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount));
+
+ SwRedlineTable::size_type n = 0;
+ rSrcDoc.getIDocumentRedlineAccess().GetRedline( *pStt, &n );
+ for( ; n < rTable.size(); ++n )
+ {
+ const SwRangeRedline* pRedl = rTable[ n ];
+ if( RedlineType::Delete == pRedl->GetType() && pRedl->IsVisible() )
+ {
+ const SwPosition *pRStt = pRedl->Start(), *pREnd = pRedl->End();
+
+ SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
+ switch( eCmpPos )
+ {
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before:
+ // Pos1 is before Pos2
+ break;
+
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Behind:
+ // Pos1 is after Pos2
+ n = rTable.size();
+ break;
+
+ default:
+ {
+ pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() ));
+ if( *pStt < *pRStt )
+ {
+ lcl_NonCopyCount( rPam, aCorrIdx, pRStt->nNode.GetIndex(), nDelCount );
+ lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt,
+ *pDelPam->GetPoint(), nDelCount );
+ }
+ pDelPam->SetMark();
+
+ if( *pEnd < *pREnd )
+ *pDelPam->GetPoint() = *pCpyEnd;
+ else
+ {
+ lcl_NonCopyCount( rPam, aCorrIdx, pREnd->nNode.GetIndex(), nDelCount );
+ lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt,
+ *pDelPam->GetPoint(), nDelCount );
+ }
+
+ if (pDelPam->GetNext() != pDelPam.get()
+ && *pDelPam->GetNext()->End() == *pDelPam->Start())
+ {
+ *pDelPam->GetNext()->End() = *pDelPam->End();
+ pDelPam.reset(pDelPam->GetNext());
+ }
+ }
+ }
+ }
+ }
+
+ if( !pDelPam )
+ return;
+
+ RedlineFlags eOld = rDestDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
+
+ ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo());
+
+ do {
+ rDestDoc.getIDocumentContentOperations().DeleteAndJoin( *pDelPam->GetNext() );
+ if( !pDelPam->IsMultiSelection() )
+ break;
+ delete pDelPam->GetNext();
+ } while( true );
+
+ rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg )
+ {
+ SwDoc& rSrcDoc = rRg.aStart.GetNode().GetDoc();
+ if( !rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
+ SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd );
+ lcl_DeleteRedlines( aRgTmp, aCpyTmp );
+ }
+ }
+
+ void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest )
+ {
+ SwFormatChain aSrc( pSrc->GetChain() );
+ if ( !aSrc.GetNext() )
+ {
+ aSrc.SetNext( pDest );
+ pSrc->SetFormatAttr( aSrc );
+ }
+ SwFormatChain aDest( pDest->GetChain() );
+ if ( !aDest.GetPrev() )
+ {
+ aDest.SetPrev( pSrc );
+ pDest->SetFormatAttr( aDest );
+ }
+ }
+
+ // #i86492#
+ bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam )
+ {
+ bool bRet = false;
+
+ const SwTextNode* pTextNd = rPam.Start()->nNode.GetNode().GetTextNode();
+ const SwTextNode* pEndTextNd = rPam.End()->nNode.GetNode().GetTextNode();
+ if ( pTextNd && pTextNd->IsInList() &&
+ pEndTextNd && pEndTextNd->IsInList() )
+ {
+ bRet = true;
+ SwNodeIndex aIdx(rPam.Start()->nNode);
+
+ do
+ {
+ ++aIdx;
+ pTextNd = aIdx.GetNode().GetTextNode();
+
+ if ( !pTextNd || !pTextNd->IsInList() )
+ {
+ bRet = false;
+ break;
+ }
+ } while (pTextNd != pEndTextNd);
+ }
+
+ return bRet;
+ }
+
+ bool lcl_MarksWholeNode(const SwPaM & rPam)
+ {
+ bool bResult = false;
+ const SwPosition* pStt = rPam.Start();
+ const SwPosition* pEnd = rPam.End();
+
+ if (nullptr != pStt && nullptr != pEnd)
+ {
+ const SwTextNode* pSttNd = pStt->nNode.GetNode().GetTextNode();
+ const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
+
+ if (nullptr != pSttNd && nullptr != pEndNd &&
+ pStt->nContent.GetIndex() == 0 &&
+ pEnd->nContent.GetIndex() == pEndNd->Len())
+ {
+ bResult = true;
+ }
+ }
+
+ return bResult;
+ }
+}
+
+//local functions originally from sw/source/core/doc/docedt.cxx
+namespace sw
+{
+ void CalcBreaks(std::vector<std::pair<SwNodeOffset, sal_Int32>> & rBreaks,
+ SwPaM const & rPam, bool const isOnlyFieldmarks)
+ {
+ SwNodeOffset const nStartNode(rPam.Start()->nNode.GetIndex());
+ SwNodeOffset const nEndNode(rPam.End()->nNode.GetIndex());
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
+ IDocumentMarkAccess const& rIDMA(*rPam.GetDoc().getIDocumentMarkAccess());
+
+ std::stack<std::tuple<sw::mark::IFieldmark const*, bool, SwNodeOffset, sal_Int32>> startedFields;
+
+ for (SwNodeOffset n = nStartNode; n <= nEndNode; ++n)
+ {
+ SwNode *const pNode(rNodes[n]);
+ if (pNode->IsTextNode())
+ {
+ SwTextNode & rTextNode(*pNode->GetTextNode());
+ sal_Int32 const nStart(n == nStartNode
+ ? rPam.Start()->nContent.GetIndex()
+ : 0);
+ sal_Int32 const nEnd(n == nEndNode
+ ? rPam.End()->nContent.GetIndex()
+ : rTextNode.Len());
+ for (sal_Int32 i = nStart; i < nEnd; ++i)
+ {
+ const sal_Unicode c(rTextNode.GetText()[i]);
+ switch (c)
+ {
+ // note: CH_TXT_ATR_FORMELEMENT does not need handling
+ // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_BREAKWORD:
+ {
+ // META hints only have dummy char at the start, not
+ // at the end, so no need to check in nStartNode
+ if (n == nEndNode && !isOnlyFieldmarks)
+ {
+ SwTextAttr const* pAttr(rTextNode.GetTextAttrForCharAt(i));
+ if (pAttr && pAttr->End() && (nEnd < *pAttr->End()))
+ {
+ assert(pAttr->HasDummyChar());
+ rBreaks.emplace_back(n, i);
+ }
+
+ if (!pAttr)
+ {
+ // See if this is an end dummy character for a content control.
+ pAttr = rTextNode.GetTextAttrForEndCharAt(i, RES_TXTATR_CONTENTCONTROL);
+ if (pAttr && (nStart > pAttr->GetStart()))
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ }
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDSTART:
+ {
+ auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
+ startedFields.emplace(pFieldMark, false, 0, 0);
+ break;
+ }
+ case CH_TXT_ATR_FIELDSEP:
+ {
+ if (startedFields.empty())
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ else
+ { // no way to find the field via MarkManager...
+ assert(std::get<0>(startedFields.top())->IsCoveringPosition(SwPosition(rTextNode, i)));
+ std::get<1>(startedFields.top()) = true;
+ std::get<2>(startedFields.top()) = n;
+ std::get<3>(startedFields.top()) = i;
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDEND:
+ {
+ if (startedFields.empty())
+ {
+ rBreaks.emplace_back(n, i);
+ }
+ else
+ { // fieldmarks must not overlap => stack
+ assert(std::get<0>(startedFields.top()) == rIDMA.getFieldmarkAt(SwPosition(rTextNode, i)));
+ startedFields.pop();
+ }
+ break;
+ }
+ }
+ }
+ }
+ else if (pNode->IsStartNode())
+ {
+ if (pNode->EndOfSectionIndex() <= nEndNode)
+ { // fieldmark cannot overlap node section
+ n = pNode->EndOfSectionIndex();
+ }
+ }
+ else
+ { // EndNode can actually happen with sections :(
+ assert(pNode->IsEndNode() || pNode->IsNoTextNode());
+ }
+ }
+ while (!startedFields.empty())
+ {
+ SwPosition const& rStart(std::get<0>(startedFields.top())->GetMarkStart());
+ std::pair<SwNodeOffset, sal_Int32> const pos(
+ rStart.nNode.GetIndex(), rStart.nContent.GetIndex());
+ auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), pos);
+ assert(it == rBreaks.end() || *it != pos);
+ rBreaks.insert(it, pos);
+ if (std::get<1>(startedFields.top()))
+ {
+ std::pair<SwNodeOffset, sal_Int32> const posSep(
+ std::get<2>(startedFields.top()),
+ std::get<3>(startedFields.top()));
+ it = std::lower_bound(rBreaks.begin(), rBreaks.end(), posSep);
+ assert(it == rBreaks.end() || *it != posSep);
+ rBreaks.insert(it, posSep);
+ }
+ startedFields.pop();
+ }
+ }
+}
+
+namespace
+{
+
+ bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations,
+ SwPaM & rPam, SwDeleteFlags const flags,
+ bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, SwDeleteFlags))
+ {
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+
+ sw::CalcBreaks(Breaks, rPam);
+
+ if (Breaks.empty())
+ {
+ return (rDocumentContentOperations.*pFunc)(rPam, flags);
+ }
+
+ // Deletion must be split into several parts if the text node
+ // contains a text attribute with end and with dummy character
+ // and the selection does not contain the text attribute completely,
+ // but overlaps its start (left), where the dummy character is.
+
+ SwPosition const & rSelectionEnd( *rPam.End() );
+
+ bool bRet( true );
+ // iterate from end to start, to avoid invalidating the offsets!
+ auto iter( Breaks.rbegin() );
+ SwNodeOffset nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
+ SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
+ SwPosition & rEnd( *aPam.End() );
+ SwPosition & rStart( *aPam.Start() );
+
+ while (iter != Breaks.rend())
+ {
+ rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags);
+ nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
+ }
+ rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
+ ++iter;
+ }
+
+ rStart = *rPam.Start(); // set to original start
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags);
+ }
+
+ return bRet;
+ }
+
+ bool lcl_StrLenOverflow( const SwPaM& rPam )
+ {
+ // If we try to merge two paragraphs we have to test if afterwards
+ // the string doesn't exceed the allowed string length
+ if( rPam.GetPoint()->nNode != rPam.GetMark()->nNode )
+ {
+ const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
+ const SwTextNode* pEndNd = pEnd->nNode.GetNode().GetTextNode();
+ if( (nullptr != pEndNd) && pStt->nNode.GetNode().IsTextNode() )
+ {
+ const sal_uInt64 nSum = pStt->nContent.GetIndex() +
+ pEndNd->GetText().getLength() - pEnd->nContent.GetIndex();
+ return nSum > o3tl::make_unsigned(SAL_MAX_INT32);
+ }
+ }
+ return false;
+ }
+
+ struct SaveRedline
+ {
+ SwRangeRedline* pRedl;
+ SwNodeOffset nStt, nEnd;
+ sal_Int32 nSttCnt;
+ sal_Int32 nEndCnt;
+
+ SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx )
+ : pRedl(pR)
+ , nEnd(0)
+ , nEndCnt(0)
+ {
+ const SwPosition* pStt = pR->Start(),
+ * pEnd = pR->End();
+ SwNodeOffset nSttIdx = rSttIdx.GetIndex();
+ nStt = pStt->nNode.GetIndex() - nSttIdx;
+ nSttCnt = pStt->nContent.GetIndex();
+ if( pR->HasMark() )
+ {
+ nEnd = pEnd->nNode.GetIndex() - nSttIdx;
+ nEndCnt = pEnd->nContent.GetIndex();
+ }
+
+ pRedl->GetPoint()->nNode = SwNodeOffset(0);
+ pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
+ pRedl->GetMark()->nNode = SwNodeOffset(0);
+ pRedl->GetMark()->nContent.Assign( nullptr, 0 );
+ }
+
+ SaveRedline( SwRangeRedline* pR, const SwPosition& rPos )
+ : pRedl(pR)
+ , nEnd(0)
+ , nEndCnt(0)
+ {
+ const SwPosition* pStt = pR->Start(),
+ * pEnd = pR->End();
+ SwNodeOffset nSttIdx = rPos.nNode.GetIndex();
+ nStt = pStt->nNode.GetIndex() - nSttIdx;
+ nSttCnt = pStt->nContent.GetIndex();
+ if( nStt == SwNodeOffset(0) )
+ nSttCnt = nSttCnt - rPos.nContent.GetIndex();
+ if( pR->HasMark() )
+ {
+ nEnd = pEnd->nNode.GetIndex() - nSttIdx;
+ nEndCnt = pEnd->nContent.GetIndex();
+ if( nEnd == SwNodeOffset(0) )
+ nEndCnt = nEndCnt - rPos.nContent.GetIndex();
+ }
+
+ pRedl->GetPoint()->nNode = SwNodeOffset(0);
+ pRedl->GetPoint()->nContent.Assign( nullptr, 0 );
+ pRedl->GetMark()->nNode = SwNodeOffset(0);
+ pRedl->GetMark()->nContent.Assign( nullptr, 0 );
+ }
+
+ void SetPos( SwNodeOffset nInsPos )
+ {
+ pRedl->GetPoint()->nNode = nInsPos + nStt;
+ pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt );
+ if( pRedl->HasMark() )
+ {
+ pRedl->GetMark()->nNode = nInsPos + nEnd;
+ pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt );
+ }
+ }
+
+ void SetPos( const SwPosition& aPos )
+ {
+ pRedl->GetPoint()->nNode = aPos.nNode.GetIndex() + nStt;
+ pRedl->GetPoint()->nContent.Assign( pRedl->GetContentNode(), nSttCnt + ( nStt == SwNodeOffset(0) ? aPos.nContent.GetIndex() : 0 ) );
+ if( pRedl->HasMark() )
+ {
+ pRedl->GetMark()->nNode = aPos.nNode.GetIndex() + nEnd;
+ pRedl->GetMark()->nContent.Assign( pRedl->GetContentNode(false), nEndCnt + ( nEnd == SwNodeOffset(0) ? aPos.nContent.GetIndex() : 0 ) );
+ }
+ }
+ };
+
+ typedef std::vector< SaveRedline > SaveRedlines_t;
+
+ void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr)
+ {
+ SwDoc& rDoc = aPam.GetNode().GetDoc();
+
+ const SwPosition* pStart = aPam.Start();
+ const SwPosition* pEnd = aPam.End();
+
+ // get first relevant redline
+ SwRedlineTable::size_type nCurrentRedline;
+ rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline );
+ if( nCurrentRedline > 0)
+ nCurrentRedline--;
+
+ // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ // iterate over relevant redlines and decide for each whether it should
+ // be saved, or split + saved
+ SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ )
+ {
+ SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ];
+ SwComparePosition eCompare =
+ ComparePosition( *pCurrent->Start(), *pCurrent->End(),
+ *pStart, *pEnd);
+
+ // we must save this redline if it overlaps aPam
+ // (we may have to split it, too)
+ if( eCompare == SwComparePosition::OverlapBehind ||
+ eCompare == SwComparePosition::OverlapBefore ||
+ eCompare == SwComparePosition::Outside ||
+ eCompare == SwComparePosition::Inside ||
+ eCompare == SwComparePosition::Equal )
+ {
+ rRedlineTable.Remove( nCurrentRedline-- );
+
+ // split beginning, if necessary
+ if( eCompare == SwComparePosition::OverlapBefore ||
+ eCompare == SwComparePosition::Outside )
+ {
+ SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
+ *pNewRedline->End() = *pStart;
+ *pCurrent->Start() = *pStart;
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
+ }
+
+ // split end, if necessary
+ if( eCompare == SwComparePosition::OverlapBehind ||
+ eCompare == SwComparePosition::Outside )
+ {
+ SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent );
+ *pNewRedline->Start() = *pEnd;
+ *pCurrent->End() = *pEnd;
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
+ }
+
+ // save the current redline
+ rArr.emplace_back( pCurrent, *pStart );
+ }
+ }
+
+ // restore old redline mode
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_RestoreRedlines(SwDoc& rDoc, const SwPosition& rPos, SaveRedlines_t& rArr)
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ for(SaveRedline & rSvRedLine : rArr)
+ {
+ rSvRedLine.SetPos( rPos );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr)
+ {
+ SwDoc& rDoc = rRg.aStart.GetNode().GetDoc();
+ SwRedlineTable::size_type nRedlPos;
+ SwPosition aSrchPos( rRg.aStart ); aSrchPos.nNode--;
+ aSrchPos.nContent.Assign( aSrchPos.nNode.GetNode().GetContentNode(), 0 );
+ if( rDoc.getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos )
+ --nRedlPos;
+ else if( nRedlPos >= rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() )
+ return ;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+ SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+
+ do {
+ SwRangeRedline* pTmp = rRedlTable[ nRedlPos ];
+
+ const SwPosition* pRStt = pTmp->Start(),
+ * pREnd = pTmp->End();
+
+ if( pRStt->nNode < rRg.aStart )
+ {
+ if( pREnd->nNode > rRg.aStart && pREnd->nNode < rRg.aEnd )
+ {
+ // Create a copy and set the end of the original to the end of the MoveArea.
+ // The copy is moved too.
+ SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
+ SwPosition* pTmpPos = pNewRedl->Start();
+ pTmpPos->nNode = rRg.aStart;
+ pTmpPos->nContent.Assign(
+ pTmpPos->nNode.GetNode().GetContentNode(), 0 );
+
+ rArr.emplace_back(pNewRedl, rRg.aStart);
+
+ pTmpPos = pTmp->End();
+ pTmpPos->nNode = rRg.aEnd;
+ pTmpPos->nContent.Assign(
+ pTmpPos->nNode.GetNode().GetContentNode(), 0 );
+ }
+ else if( pREnd->nNode == rRg.aStart )
+ {
+ SwPosition* pTmpPos = pTmp->End();
+ pTmpPos->nNode = rRg.aEnd;
+ pTmpPos->nContent.Assign(
+ pTmpPos->nNode.GetNode().GetContentNode(), 0 );
+ }
+ }
+ else if( pRStt->nNode < rRg.aEnd )
+ {
+ rRedlTable.Remove( nRedlPos-- );
+ if( pREnd->nNode < rRg.aEnd ||
+ ( pREnd->nNode == rRg.aEnd && !pREnd->nContent.GetIndex()) )
+ {
+ // move everything
+ rArr.emplace_back( pTmp, rRg.aStart );
+ }
+ else
+ {
+ // split
+ SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp );
+ SwPosition* pTmpPos = pNewRedl->End();
+ pTmpPos->nNode = rRg.aEnd;
+ pTmpPos->nContent.Assign(
+ pTmpPos->nNode.GetNode().GetContentNode(), 0 );
+
+ rArr.emplace_back( pNewRedl, rRg.aStart );
+
+ pTmpPos = pTmp->Start();
+ pTmpPos->nNode = rRg.aEnd;
+ pTmpPos->nContent.Assign(
+ pTmpPos->nNode.GetNode().GetContentNode(), 0 );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pTmp, true );
+ }
+ }
+ else
+ break;
+
+ } while( ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() );
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ void lcl_RestoreRedlines(SwDoc& rDoc, SwNodeOffset const nInsPos, SaveRedlines_t& rArr)
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ for(SaveRedline & rSvRedLine : rArr)
+ {
+ rSvRedLine.SetPos( nInsPos );
+ IDocumentRedlineAccess::AppendResult const result(
+ rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ));
+ if ( IDocumentRedlineAccess::AppendResult::APPENDED == result &&
+ rSvRedLine.pRedl->GetType() == RedlineType::Delete )
+ {
+ UpdateFramesForAddDeleteRedline(rDoc, *rSvRedLine.pRedl);
+ }
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+
+ bool lcl_SaveFootnote( const SwNodeIndex& rSttNd, const SwNodeIndex& rEndNd,
+ const SwNodeIndex& rInsPos,
+ SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr,
+ const SwIndex* pSttCnt = nullptr, const SwIndex* pEndCnt = nullptr )
+ {
+ bool bUpdateFootnote = false;
+ const SwNodes& rNds = rInsPos.GetNodes();
+ const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() &&
+ rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex();
+ const bool bSaveFootnote = !bDelFootnote &&
+ rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex();
+ if( !rFootnoteArr.empty() )
+ {
+
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( rSttNd, &nPos );
+ SwTextFootnote* pSrch;
+ const SwNode* pFootnoteNd;
+
+ // Delete/save all that come after it
+ while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
+ &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
+ <= rEndNd.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( ( pEndCnt && pSttCnt )
+ ? (( &rSttNd.GetNode() == pFootnoteNd &&
+ pSttCnt->GetIndex() > nFootnoteSttIdx) ||
+ ( &rEndNd.GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEndCnt->GetIndex() ))
+ : ( &rEndNd.GetNode() == pFootnoteNd ))
+ {
+ ++nPos; // continue searching
+ }
+ else
+ {
+ // delete it
+ if( bDelFootnote )
+ {
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
+ SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
+ rTextNd.EraseText( aIdx, 1 );
+ }
+ else
+ {
+ pSrch->DelFrames(nullptr);
+ rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
+ if( bSaveFootnote )
+ rSaveArr.insert( pSrch );
+ }
+ bUpdateFootnote = true;
+ }
+ }
+
+ while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
+ GetTextNode())->GetIndex() >= rSttNd.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( !pEndCnt || !pSttCnt ||
+ ! (( &rSttNd.GetNode() == pFootnoteNd &&
+ pSttCnt->GetIndex() > nFootnoteSttIdx ) ||
+ ( &rEndNd.GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEndCnt->GetIndex() )) )
+ {
+ if( bDelFootnote )
+ {
+ // delete it
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode());
+ SwIndex aIdx( &rTextNd, nFootnoteSttIdx );
+ rTextNd.EraseText( aIdx, 1 );
+ }
+ else
+ {
+ pSrch->DelFrames(nullptr);
+ rFootnoteArr.erase( rFootnoteArr.begin() + nPos );
+ if( bSaveFootnote )
+ rSaveArr.insert( pSrch );
+ }
+ bUpdateFootnote = true;
+ }
+ }
+ }
+ // When moving from redline section into document content section, e.g.
+ // after loading a document with (delete-)redlines, the footnote array
+ // has to be adjusted... (#i70572)
+ if( bSaveFootnote )
+ {
+ SwNodeIndex aIdx( rSttNd );
+ while( aIdx < rEndNd ) // Check the moved section
+ {
+ SwNode* pNode = &aIdx.GetNode();
+ if( pNode->IsTextNode() ) // Looking for text nodes...
+ {
+ SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints();
+ if( pHints && pHints->HasFootnote() ) //...with footnotes
+ {
+ bUpdateFootnote = true; // Heureka
+ const size_t nCount = pHints->Count();
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwTextAttr *pAttr = pHints->Get( i );
+ if ( pAttr->Which() == RES_TXTATR_FTN )
+ {
+ rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) );
+ }
+ }
+ }
+ }
+ ++aIdx;
+ }
+ }
+ return bUpdateFootnote;
+ }
+
+ bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos )
+ {
+ sal_Unicode const cChr = pNode->GetText()[nPos];
+ switch (cChr)
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ return !pNode->GetTextAttrForCharAt(nPos);// how could there be none?
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ void lcl_SkipAttr( const SwTextNode *pNode, SwIndex &rIdx, sal_Int32 &rStart )
+ {
+ if( !lcl_MayOverwrite( pNode, rStart ) )
+ {
+ // skip all special attributes
+ do {
+ ++rIdx;
+ rStart = rIdx.GetIndex();
+ } while (rStart < pNode->GetText().getLength()
+ && !lcl_MayOverwrite(pNode, rStart) );
+ }
+ }
+
+ bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc )
+ {
+ if( bRegExpRplc )
+ {
+ sal_Int32 nPos = 0;
+ static const OUStringLiteral sPara(u"\\n");
+ for (;;)
+ {
+ nPos = rStr.indexOf( sPara, nPos );
+ if (nPos<0)
+ {
+ break;
+ }
+ // Has this been escaped?
+ if( nPos && '\\' == rStr[nPos-1])
+ {
+ ++nPos;
+ if( nPos >= rStr.getLength() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ rRet = rStr.copy( 0, nPos );
+ rStr = rStr.copy( nPos + sPara.getLength() );
+ return true;
+ }
+ }
+ }
+ rRet = rStr;
+ rStr.clear();
+ return false;
+ }
+}
+
+namespace //local functions originally from docfmt.cxx
+{
+
+ bool lcl_ApplyOtherSet(
+ SwContentNode & rNode,
+ SwHistory *const pHistory,
+ SfxItemSet const& rOtherSet,
+ SfxItemSet const& rFirstSet,
+ SfxItemSet const& rPropsSet,
+ SwRootFrame const*const pLayout,
+ SwNodeIndex *const o_pIndex = nullptr)
+ {
+ assert(rOtherSet.Count());
+
+ bool ret(false);
+ SwTextNode *const pTNd = rNode.GetTextNode();
+ sw::MergedPara const* pMerged(nullptr);
+ if (pLayout && pLayout->HasMergedParas() && pTNd)
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(
+ pTNd->getLayoutFrame(pLayout)));
+ if (pTextFrame)
+ {
+ pMerged = pTextFrame->GetMergedPara();
+ }
+ if (pMerged)
+ {
+ if (rFirstSet.Count())
+ {
+ if (pHistory)
+ {
+ SwRegHistory aRegH(pMerged->pFirstNode, *pMerged->pFirstNode, pHistory);
+ ret = pMerged->pFirstNode->SetAttr(rFirstSet);
+ }
+ else
+ {
+ ret = pMerged->pFirstNode->SetAttr(rFirstSet);
+ }
+ }
+ if (rPropsSet.Count())
+ {
+ if (pHistory)
+ {
+ SwRegHistory aRegH(pMerged->pParaPropsNode, *pMerged->pParaPropsNode, pHistory);
+ ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
+ }
+ else
+ {
+ ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret;
+ }
+ }
+ if (o_pIndex)
+ {
+ *o_pIndex = *pMerged->pLastNode; // skip hidden
+ }
+ }
+ }
+
+ // input cursor can't be on hidden node, and iteration skips them
+ assert(!pLayout || !pLayout->HasMergedParas()
+ || rNode.GetRedlineMergeFlag() != SwNode::Merge::Hidden);
+
+ if (!pMerged)
+ {
+ if (pHistory)
+ {
+ SwRegHistory aRegH(&rNode, rNode, pHistory);
+ ret = rNode.SetAttr( rOtherSet );
+ }
+ else
+ {
+ ret = rNode.SetAttr( rOtherSet );
+ }
+ }
+ return ret;
+ }
+
+ #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; }
+
+ // set format redline with extra data for lcl_InsAttr()
+ void lcl_SetRedline(
+ SwDoc& rDoc,
+ const SwPaM &rRg)
+ {
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra;
+
+ // check existing redline on the same range, and use its extra data, if it exists
+ SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos(
+ rRg.Start()->nNode.GetNode(), RedlineType::Format );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ const SwPosition *pRStt, *pREnd;
+ do {
+ SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ pRStt = pTmp->Start();
+ pREnd = pTmp->End();
+ SwComparePosition eCompare = ComparePosition( *rRg.Start(), *rRg.End(), *pRStt, *pREnd );
+ if ( eCompare == SwComparePosition::Inside || eCompare == SwComparePosition::Equal )
+ {
+ if (pTmp->GetExtraData())
+ {
+ const SwRedlineExtraData* pExtraData = pTmp->GetExtraData();
+ const SwRedlineExtraData_FormatColl* pFormattingChanges =
+ dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData);
+ // Check if the extra data is of type 'formatting changes'
+ if (pFormattingChanges)
+ {
+ // Get the item set that holds all the changes properties
+ const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet();
+ xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, pChangesSet));
+ break;
+ }
+ }
+ }
+ } while( pRStt <= rRg.Start() && ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
+ }
+
+ SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::Format, rRg );
+ auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline( pRedline, true));
+ // store original text attributes to reject formatting change
+ if (IDocumentRedlineAccess::AppendResult::IGNORED == result)
+ return;
+
+ // no existing format redline in the range
+ if (!xExtra)
+ {
+ // Apply the first character's attributes to the ReplaceText
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( rDoc.GetAttrPool() );
+ SwTextNode * pNode = rRg.Start()->nNode.GetNode().GetTextNode();
+ pNode->GetParaAttr( aSet, rRg.Start()->nContent.GetIndex() + 1, rRg.End()->nContent.GetIndex() );
+
+ aSet.ClearItem( RES_TXTATR_REFMARK );
+ aSet.ClearItem( RES_TXTATR_TOXMARK );
+ aSet.ClearItem( RES_TXTATR_CJK_RUBY );
+ aSet.ClearItem( RES_TXTATR_INETFMT );
+ aSet.ClearItem( RES_TXTATR_META );
+ aSet.ClearItem( RES_TXTATR_METAFIELD );
+
+ // After GetParaAttr aSet can contain INVALID_POOL_ITEM items, e.g. RES_TXTATR_CHARFMT
+ // and (a copy of) this SfxItemSet can be passed to MSWordExportBase::OutputItemSet
+ // which doesn't handle INVALID_POOL_ITEM items so clear InvalidItems here
+ aSet.ClearInvalidItems();
+
+ xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, &aSet));
+ }
+
+ if (xExtra)
+ {
+ pRedline->SetExtraData(xExtra.get() );
+ }
+ }
+
+ // create format redline(s) for the given range:
+ // to track the original formatting stored in the
+ // hints, create redlines for all parts of the
+ // range partitioned by boundaries of the hints.
+ void lcl_SetRedlines(
+ SwDoc& rDoc,
+ const SwPaM &rRg)
+ {
+ SwNodeIndex aIdx( rRg.Start()->nNode );
+ const SwNodeIndex aEndNd( rRg.End()->nNode );
+ while( aIdx <= aEndNd )
+ {
+ SwTextNode *pNode = aIdx.GetNode().GetTextNode();
+ if( pNode )
+ {
+ const sal_Int32 nStart = aIdx == rRg.Start()->nNode
+ ? rRg.Start()->nContent.GetIndex()
+ : 0;
+ const sal_Int32 nEnd = aIdx < aEndNd
+ ? pNode->GetText().getLength()
+ : rRg.End()->nContent.GetIndex();
+
+ if( SwpHints *pHints = pNode->GetpSwpHints() )
+ {
+ const size_t nCount = pHints->Count();
+ sal_Int32 nRedEnd = nStart;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwTextAttr *pAttr = pHints->Get( i );
+
+ if ( pAttr->GetStart() > nEnd )
+ {
+ break; // after the range
+ }
+
+ if ( !pAttr->GetEnd() || *pAttr->GetEnd() < nStart )
+ {
+ continue; // before the range
+ }
+
+ // range part before the hint
+ if ( nRedEnd < pAttr->GetStart() )
+ {
+ SwPaM aPam( *pNode, nRedEnd, *pNode, pAttr->GetStart() );
+ lcl_SetRedline(rDoc, aPam);
+ }
+
+ // range part at the hint
+ sal_Int32 nRedStart = std::max(pAttr->GetStart(), nStart);
+ nRedEnd = std::min(*pAttr->GetEnd(), nEnd);
+ SwPaM aPam2( *pNode, nRedStart, *pNode, nRedEnd );
+ lcl_SetRedline(rDoc, aPam2);
+ }
+
+ // range part after the last hint
+ if ( nRedEnd < nEnd )
+ {
+ SwPaM aPam( *pNode, nRedEnd, *pNode, nEnd );
+ lcl_SetRedline(rDoc, aPam);
+ }
+ }
+ else
+ {
+ SwPaM aPam( *pNode, nStart, *pNode, nEnd );
+ lcl_SetRedline(rDoc, aPam);
+ }
+ }
+ ++aIdx;
+ }
+ }
+
+ /// Insert Hints according to content types;
+ // Is used in SwDoc::Insert(..., SwFormatHint &rHt)
+
+ bool lcl_InsAttr(
+ SwDoc& rDoc,
+ const SwPaM &rRg,
+ const SfxItemSet& rChgSet,
+ const SetAttrMode nFlags,
+ SwUndoAttr *const pUndo,
+ SwRootFrame const*const pLayout,
+ SwTextAttr **ppNewTextAttr)
+ {
+ // Divide the Sets (for selections in Nodes)
+ const SfxItemSet* pCharSet = nullptr;
+ const SfxItemSet* pOtherSet = nullptr;
+ bool bDelete = false;
+ bool bCharAttr = false;
+ bool bOtherAttr = false;
+
+ // Check, if we can work with rChgSet or if we have to create additional SfxItemSets
+ if ( 1 == rChgSet.Count() )
+ {
+ SfxItemIter aIter( rChgSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ if (pItem && !IsInvalidItem(pItem))
+ {
+ const sal_uInt16 nWhich = pItem->Which();
+
+ if ( isCHRATR(nWhich) ||
+ (RES_TXTATR_CHARFMT == nWhich) ||
+ (RES_TXTATR_INETFMT == nWhich) ||
+ (RES_TXTATR_AUTOFMT == nWhich) ||
+ (RES_TXTATR_UNKNOWN_CONTAINER == nWhich) )
+ {
+ pCharSet = &rChgSet;
+ bCharAttr = true;
+ }
+
+ if ( isPARATR(nWhich)
+ || isPARATR_LIST(nWhich)
+ || isFRMATR(nWhich)
+ || isGRFATR(nWhich)
+ || isUNKNOWNATR(nWhich)
+ || isDrawingLayerAttribute(nWhich) )
+ {
+ pOtherSet = &rChgSet;
+ bOtherAttr = true;
+ }
+ }
+ }
+
+ // Build new itemset if either
+ // - rChgSet.Count() > 1 or
+ // - The attribute in rChgSet does not belong to one of the above categories
+ if ( !bCharAttr && !bOtherAttr )
+ {
+ SfxItemSet* pTmpCharItemSet = new SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT,
+ RES_TXTATR_UNKNOWN_CONTAINER,
+ RES_TXTATR_UNKNOWN_CONTAINER>( rDoc.GetAttrPool() );
+
+ SfxItemSet* pTmpOtherItemSet = new SfxItemSetFixed<
+ RES_PARATR_BEGIN, RES_GRFATR_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1,
+ // FillAttribute support:
+ XATTR_FILL_FIRST, XATTR_FILL_LAST>( rDoc.GetAttrPool() );
+
+ pTmpCharItemSet->Put( rChgSet );
+ pTmpOtherItemSet->Put( rChgSet );
+
+ pCharSet = pTmpCharItemSet;
+ pOtherSet = pTmpOtherItemSet;
+
+ bDelete = true;
+ }
+
+ SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
+ bool bRet = false;
+ const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
+ SwContentNode* pNode = pStt->nNode.GetNode().GetContentNode();
+
+ if( pNode && pNode->IsTextNode() )
+ {
+ // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol
+ if (pLayout && pNode->GetTextNode()->getIDocumentSettingAccess()->
+ get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ))
+ {
+ SwContentNode* pEndNode = pEnd->nNode.GetNode().GetContentNode();
+ SwContentNode* pCurrentNode = pEndNode;
+ auto nStartIndex = pNode->GetIndex();
+ auto nEndIndex = pEndNode->GetIndex();
+ SwNodeIndex aIdx( pEnd->nNode.GetNode() );
+ while ( pCurrentNode != nullptr && nStartIndex <= pCurrentNode->GetIndex() )
+ {
+ if (pCurrentNode->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT) &&
+ // remove character formatting only on wholly selected paragraphs
+ (nStartIndex < pCurrentNode->GetIndex() || pStt->nContent.GetIndex() == 0) &&
+ (pCurrentNode->GetIndex() < nEndIndex || pEnd->nContent.GetIndex() == pEndNode->Len()))
+ {
+ pCurrentNode->ResetAttr(RES_PARATR_LIST_AUTOFMT);
+ // reset also paragraph marker
+ SwIndex nIdx( pCurrentNode, pCurrentNode->Len() );
+ pCurrentNode->GetTextNode()->RstTextAttr(nIdx, 1);
+ }
+ pCurrentNode = SwNodes::GoPrevious( &aIdx );
+ }
+ }
+ // #i27615#
+ if (rRg.IsInFrontOfLabel())
+ {
+ SwTextNode * pTextNd = pNode->GetTextNode();
+ if (pLayout)
+ {
+ pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
+ }
+ SwNumRule * pNumRule = pTextNd->GetNumRule();
+
+ if ( !pNumRule )
+ {
+ OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." );
+ DELETECHARSETS
+ return false;
+ }
+
+ int nLevel = pTextNd->GetActualListLevel();
+
+ if (nLevel < 0)
+ nLevel = 0;
+
+ if (nLevel >= MAXLEVEL)
+ nLevel = MAXLEVEL - 1;
+
+ SwNumFormat aNumFormat = pNumRule->Get(o3tl::narrowing<sal_uInt16>(nLevel));
+ SwCharFormat * pCharFormat =
+ rDoc.FindCharFormatByName(aNumFormat.GetCharFormatName());
+
+ if (pCharFormat)
+ {
+ if (pHistory)
+ pHistory->Add(pCharFormat->GetAttrSet(), *pCharFormat);
+
+ if ( pCharSet )
+ pCharFormat->SetFormatAttr(*pCharSet);
+ }
+
+ DELETECHARSETS
+ return true;
+ }
+
+ const SwIndex& rSt = pStt->nContent;
+
+ // Attributes without an end do not have a range
+ if ( !bCharAttr && !bOtherAttr )
+ {
+ SfxItemSetFixed<RES_TXTATR_NOEND_BEGIN, RES_TXTATR_NOEND_END-1>
+ aTextSet( rDoc.GetAttrPool() );
+ aTextSet.Put( rChgSet );
+ if( aTextSet.Count() )
+ {
+ SwRegHistory history( pNode, *pNode, pHistory );
+ bRet = history.InsertItems(
+ aTextSet, rSt.GetIndex(), rSt.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr ) || bRet;
+
+ if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())))
+ {
+ SwPaM aPam( pStt->nNode, pStt->nContent.GetIndex()-1,
+ pStt->nNode, pStt->nContent.GetIndex() );
+
+ if( pUndo )
+ pUndo->SaveRedlineData( aPam, true );
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+ }
+
+ // TextAttributes with an end never expand their range
+ if ( !bCharAttr && !bOtherAttr )
+ {
+ // CharFormat and URL attributes are treated separately!
+ // TEST_TEMP ToDo: AutoFormat!
+ SfxItemSetFixed<
+ RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD,
+ RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
+ RES_TXTATR_INPUTFIELD, RES_TXTATR_CONTENTCONTROL>
+ aTextSet(rDoc.GetAttrPool());
+
+ aTextSet.Put( rChgSet );
+ if( aTextSet.Count() )
+ {
+ const sal_Int32 nInsCnt = rSt.GetIndex();
+ const sal_Int32 nEnd = pStt->nNode == pEnd->nNode
+ ? pEnd->nContent.GetIndex()
+ : pNode->Len();
+ SwRegHistory history( pNode, *pNode, pHistory );
+ bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags, ppNewTextAttr )
+ || bRet;
+
+ if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())))
+ {
+ // Was text content inserted? (RefMark/TOXMarks without an end)
+ bool bTextIns = nInsCnt != rSt.GetIndex();
+ // Was content inserted or set over the selection?
+ SwPaM aPam( pStt->nNode, bTextIns ? nInsCnt + 1 : nEnd,
+ pStt->nNode, nInsCnt );
+ if( pUndo )
+ pUndo->SaveRedlineData( aPam, bTextIns );
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(
+ bTextIns ? RedlineType::Insert : RedlineType::Format, aPam ),
+ true);
+ else if( bTextIns )
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+ }
+ }
+
+ // We always have to set the auto flag for PageDescs that are set at the Node!
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ SwTableNode* pTableNd;
+ const SwFormatPageDesc* pDesc = pOtherSet->GetItemIfSet( RES_PAGEDESC, false );
+ if( pDesc )
+ {
+ if( pNode )
+ {
+ // Set auto flag. Only in the template it's without auto!
+ SwFormatPageDesc aNew( *pDesc );
+
+ // Tables now also know line breaks
+ if( !(nFlags & SetAttrMode::APICALL) &&
+ nullptr != ( pTableNd = pNode->FindTableNode() ) )
+ {
+ SwTableNode* pCurTableNd = pTableNd;
+ while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
+ pTableNd = pCurTableNd;
+
+ // set the table format
+ SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
+ SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
+ pFormat->SetFormatAttr( aNew );
+ bRet = true;
+ }
+ else
+ {
+ SwContentNode * pFirstNode(pNode);
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ pFirstNode = sw::GetFirstAndLastNode(*pLayout, pStt->nNode).first;
+ }
+ SwRegHistory aRegH( pFirstNode, *pFirstNode, pHistory );
+ bRet = pFirstNode->SetAttr( aNew ) || bRet;
+ }
+ }
+
+ // bOtherAttr = true means that pOtherSet == rChgSet. In this case
+ // we know, that there is only one attribute in pOtherSet. We cannot
+ // perform the following operations, instead we return:
+ if ( bOtherAttr )
+ return bRet;
+
+ const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC );
+ if( !pOtherSet->Count() )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+ }
+
+ // Tables now also know line breaks
+ const SvxFormatBreakItem* pBreak;
+ if( pNode && !(nFlags & SetAttrMode::APICALL) &&
+ nullptr != (pTableNd = pNode->FindTableNode() ) &&
+ (pBreak = pOtherSet->GetItemIfSet( RES_BREAK, false )) )
+ {
+ SwTableNode* pCurTableNd = pTableNd;
+ while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) )
+ pTableNd = pCurTableNd;
+
+ // set the table format
+ SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat();
+ SwRegHistory aRegH( pFormat, *pTableNd, pHistory );
+ pFormat->SetFormatAttr( *pBreak );
+ bRet = true;
+
+ // bOtherAttr = true means that pOtherSet == rChgSet. In this case
+ // we know, that there is only one attribute in pOtherSet. We cannot
+ // perform the following operations, instead we return:
+ if ( bOtherAttr )
+ return bRet;
+
+ const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK );
+ if( !pOtherSet->Count() )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+ }
+
+ {
+ // If we have a PoolNumRule, create it if needed
+ sal_uInt16 nPoolId=0;
+ const SwNumRuleItem* pRule = pOtherSet->GetItemIfSet( RES_PARATR_NUMRULE, false );
+ if( pRule &&
+ !rDoc.FindNumRulePtr( pRule->GetValue() ) &&
+ USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(),
+ SwGetPoolIdFromName::NumRule )) )
+ rDoc.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId );
+ }
+ }
+
+ SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(rDoc.GetAttrPool());
+ if (pOtherSet && pOtherSet->Count())
+ { // actually only RES_BREAK is possible here...
+ firstSet.Put(*pOtherSet);
+ }
+ SfxItemSetFixed
+ <RES_PARATR_BEGIN, RES_PAGEDESC,
+ RES_BREAK+1, RES_FRMATR_END,
+ XATTR_FILL_FIRST, XATTR_FILL_LAST+1> propsSet(rDoc.GetAttrPool());
+ if (pOtherSet && pOtherSet->Count())
+ {
+ propsSet.Put(*pOtherSet);
+ }
+
+ if( !rRg.HasMark() ) // no range
+ {
+ if( !pNode )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+
+ if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
+ {
+ SwTextNode* pTextNd = pNode->GetTextNode();
+ const SwIndex& rSt = pStt->nContent;
+ sal_Int32 nMkPos, nPtPos = rSt.GetIndex();
+ const OUString& rStr = pTextNd->GetText();
+
+ // Special case: if the Cursor is located within a URL attribute, we take over it's area
+ SwTextAttr const*const pURLAttr(
+ pTextNd->GetTextAttrAt(rSt.GetIndex(), RES_TXTATR_INETFMT));
+ if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
+ {
+ nMkPos = pURLAttr->GetStart();
+ nPtPos = *pURLAttr->End();
+ }
+ else
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ pTextNd->GetText(), nPtPos,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
+ true);
+
+ if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
+ {
+ nMkPos = aBndry.startPos;
+ nPtPos = aBndry.endPos;
+ }
+ else
+ nPtPos = nMkPos = rSt.GetIndex();
+ }
+
+ // Remove the overriding attributes from the SwpHintsArray,
+ // if the selection spans across the whole paragraph.
+ // These attributes are inserted as FormatAttributes and
+ // never override the TextAttributes!
+ if( !(nFlags & SetAttrMode::DONTREPLACE ) &&
+ pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength())
+ {
+ SwIndex aSt( pTextNd );
+ if( pHistory )
+ {
+ // Save all attributes for the Undo.
+ SwRegHistory aRHst( *pTextNd, pHistory );
+ pTextNd->GetpSwpHints()->Register( &aRHst );
+ pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->GetpSwpHints()->DeRegister();
+ }
+ else
+ pTextNd->RstTextAttr( aSt, nPtPos, 0, pCharSet );
+ }
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos );
+
+ if( pUndo )
+ pUndo->SaveRedlineData( aPam, false );
+
+ lcl_SetRedlines(rDoc, aPam);
+ }
+
+ // the SwRegHistory inserts the attribute into the TextNode!
+ SwRegHistory history( pNode, *pNode, pHistory );
+
+ bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags, /*ppNewTextAttr*/nullptr )
+ || bRet;
+
+ }
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ // Need to check for unique item for DrawingLayer items of type NameOrIndex
+ // and evtl. correct that item to ensure unique names for that type. This call may
+ // modify/correct entries inside of the given SfxItemSet
+ SfxItemSet aTempLocalCopy(*pOtherSet);
+
+ rDoc.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy);
+ bRet = lcl_ApplyOtherSet(*pNode, pHistory, aTempLocalCopy, firstSet, propsSet, pLayout) || bRet;
+ }
+
+ DELETECHARSETS
+ return bRet;
+ }
+
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() )
+ {
+ if( pUndo )
+ pUndo->SaveRedlineData( rRg, false );
+
+ lcl_SetRedlines(rDoc, rRg);
+ }
+
+ /* now if range */
+ sal_uLong nNodes = 0;
+
+ SwNodeIndex aSt( rDoc.GetNodes() );
+ SwNodeIndex aEnd( rDoc.GetNodes() );
+ SwIndex aCntEnd( pEnd->nContent );
+
+ if( pNode )
+ {
+ const sal_Int32 nLen = pNode->Len();
+ if( pStt->nNode != pEnd->nNode )
+ aCntEnd.Assign( pNode, nLen );
+
+ if( pStt->nContent.GetIndex() != 0 || aCntEnd.GetIndex() != nLen )
+ {
+ // the SwRegHistory inserts the attribute into the TextNode!
+ if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
+ {
+ SwRegHistory history( pNode, *pNode, pHistory );
+ bRet = history.InsertItems(*pCharSet,
+ pStt->nContent.GetIndex(), aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr)
+ || bRet;
+ }
+
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ bRet = lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout) || bRet;
+ }
+
+ // Only selection in a Node.
+ if( pStt->nNode == pEnd->nNode )
+ {
+ DELETECHARSETS
+ return bRet;
+ }
+ ++nNodes;
+ aSt.Assign( pStt->nNode.GetNode(), +1 );
+ }
+ else
+ aSt = pStt->nNode;
+ aCntEnd = pEnd->nContent; // aEnd was changed!
+ }
+ else
+ aSt.Assign( pStt->nNode.GetNode(), +1 );
+
+ // aSt points to the first full Node now
+
+ /*
+ * The selection spans more than one Node.
+ */
+ if( pStt->nNode < pEnd->nNode )
+ {
+ pNode = pEnd->nNode.GetNode().GetContentNode();
+ if(pNode)
+ {
+ if( aCntEnd.GetIndex() != pNode->Len() )
+ {
+ // the SwRegHistory inserts the attribute into the TextNode!
+ if( pNode->IsTextNode() && pCharSet && pCharSet->Count() )
+ {
+ SwRegHistory history( pNode, *pNode, pHistory );
+ (void)history.InsertItems(*pCharSet,
+ 0, aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr);
+ }
+
+ if( pOtherSet && pOtherSet->Count() )
+ {
+ lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout);
+ }
+
+ ++nNodes;
+ aEnd = pEnd->nNode;
+ }
+ else
+ aEnd.Assign( pEnd->nNode.GetNode(), +1 );
+ }
+ else
+ aEnd = pEnd->nNode;
+ }
+ else
+ aEnd.Assign( pEnd->nNode.GetNode(), +1 );
+
+ // aEnd points BEHIND the last full node now
+
+ /* Edit the fully selected Nodes. */
+ // Reset all attributes from the set!
+ if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) )
+ {
+ ::sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHistory, pCharSet, pLayout);
+ rDoc.GetNodes().ForEach( aSt, aEnd, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
+ }
+
+ bool bCreateSwpHints = pCharSet && (
+ SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) ||
+ SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) );
+
+ for (SwNodeIndex current = aSt; current < aEnd; ++current)
+ {
+ SwTextNode *const pTNd = current.GetNode().GetTextNode();
+ if (!pTNd)
+ continue;
+
+ if (pLayout && pLayout->HasMergedParas()
+ && pTNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ { // not really sure what to do here, but applying to hidden
+ continue; // nodes doesn't make sense...
+ }
+
+ if( pHistory )
+ {
+ SwRegHistory aRegH( pTNd, *pTNd, pHistory );
+
+ if (pCharSet && pCharSet->Count())
+ {
+ if (SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints()
+ : pTNd->GetpSwpHints())
+ {
+ pSwpHints->Register( &aRegH );
+ }
+
+ pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
+
+ // re-fetch as it may be deleted by SetAttr
+ if (SwpHints *pSwpHints = pTNd->GetpSwpHints())
+ pSwpHints->DeRegister();
+ }
+ }
+ else
+ {
+ if (pCharSet && pCharSet->Count())
+ pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags);
+ }
+ ++nNodes;
+ }
+
+ if (pOtherSet && pOtherSet->Count())
+ {
+ for (; aSt < aEnd; ++aSt)
+ {
+ pNode = aSt.GetNode().GetContentNode();
+ if (!pNode)
+ continue;
+
+ lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout, &aSt);
+ ++nNodes;
+ }
+ }
+
+ DELETECHARSETS
+ return (nNodes != 0) || bRet;
+ }
+}
+
+namespace sw
+{
+
+namespace mark
+{
+ bool IsFieldmarkOverlap(SwPaM const& rPaM)
+ {
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+ sw::CalcBreaks(Breaks, rPaM);
+ return !Breaks.empty();
+ }
+}
+
+DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+/**
+ * Checks if rStart..rEnd mark a range that makes sense to copy.
+ *
+ * IsMoveToFly means the copy is a move to create a fly
+ * and so existing flys at the edge must not be copied.
+ */
+static bool IsEmptyRange(const SwPosition& rStart, const SwPosition& rEnd,
+ SwCopyFlags const flags)
+{
+ if (rStart == rEnd)
+ { // check if a fly anchored there would be copied - then copy...
+ return !IsDestroyFrameAnchoredAtChar(rStart, rStart, rEnd,
+ (flags & SwCopyFlags::IsMoveToFly)
+ ? DelContentType::WriterfilterHack|DelContentType::AllMask
+ : DelContentType::AllMask);
+ }
+ else
+ {
+ return rEnd < rStart;
+ }
+}
+
+// Copy an area into this document or into another document
+bool
+DocumentContentOperationsManager::CopyRange( SwPaM& rPam, SwPosition& rPos,
+ SwCopyFlags const flags) const
+{
+ const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
+
+ SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
+ bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
+
+ // Catch if there's no copy to do
+ if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel))
+ return false;
+
+ // Prevent copying into Flys that are anchored in the source range
+ if (&rDoc == &m_rDoc && (flags & SwCopyFlags::CheckPosInFly))
+ {
+ // Correct the Start-/EndNode
+ SwNodeOffset nStt = pStt->nNode.GetIndex(),
+ nEnd = pEnd->nNode.GetIndex(),
+ nDiff = nEnd - nStt +1;
+ SwNode* pNd = m_rDoc.GetNodes()[ nStt ];
+ if( pNd->IsContentNode() && pStt->nContent.GetIndex() )
+ {
+ ++nStt;
+ --nDiff;
+ }
+ if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() &&
+ static_cast<SwContentNode*>(pNd)->Len() != pEnd->nContent.GetIndex() )
+ {
+ --nEnd;
+ --nDiff;
+ }
+ if( nDiff &&
+ lcl_ChkFlyFly( rDoc, nStt, nEnd, rPos.nNode.GetIndex() ) )
+ {
+ return false;
+ }
+ }
+
+ SwPaM* pRedlineRange = nullptr;
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
+ (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) )
+ pRedlineRange = new SwPaM( rPos );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+
+ bool bRet = false;
+
+ if( &rDoc != &m_rDoc )
+ { // ordinary copy
+ bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
+ }
+ else if( ! ( *pStt <= rPos && rPos < *pEnd &&
+ ( pStt->nNode != pEnd->nNode ||
+ !pStt->nNode.GetNode().IsTextNode() )) )
+ {
+ // Copy to a position outside of the area, or copy a single TextNode
+ // Do an ordinary copy
+ bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange);
+ }
+ else
+ {
+ // Copy the range in itself
+ assert(!"mst: this is assumed to be dead code");
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ if( pRedlineRange )
+ {
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlineRange ), true);
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange );
+ delete pRedlineRange;
+ }
+
+ return bRet;
+}
+
+static auto GetCorrPosition(SwPaM const& rPam) -> SwPosition
+{
+ // tdf#152710 target position must be on node that survives deletion
+ // so that PaMCorrAbs can invalidate SwUnoCursors properly
+ return rPam.GetPoint()->nNode.GetNode().IsContentNode()
+ ? *rPam.GetPoint()
+ : rPam.GetMark()->nNode.GetNode().IsContentNode()
+ ? *rPam.GetMark()
+ // this would be the result in SwNodes::RemoveNode()
+ : SwPosition(SwNodeIndex(rPam.End()->nNode.GetNode(), SwNodeOffset(+1)));
+}
+
+/// Delete a full Section of the NodeArray.
+/// The passed Node is located somewhere in the designated Section.
+void DocumentContentOperationsManager::DeleteSection( SwNode *pNode )
+{
+ assert(pNode && "Didn't pass a Node.");
+
+ SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode)
+ : pNode->StartOfSectionNode();
+ SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() );
+
+ // delete all Flys, Bookmarks, ...
+ DelFlyInRange( aSttIdx, aEndIdx );
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, RedlineType::Any );
+ DelBookmarks(aSttIdx, aEndIdx);
+
+ {
+ // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area
+ SwPaM const range(aSttIdx, aEndIdx);
+ SwPosition const pos(GetCorrPosition(range));
+ ::PaMCorrAbs(range, pos);
+ }
+
+ m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 );
+}
+
+void DocumentContentOperationsManager::DeleteDummyChar(
+ SwPosition const& rPos, sal_Unicode const cDummy)
+{
+ SwPaM aPam(rPos, rPos);
+ ++aPam.GetPoint()->nContent;
+ assert(aPam.GetText().getLength() == 1 && aPam.GetText()[0] == cDummy);
+ (void) cDummy;
+
+ DeleteRangeImpl(aPam, SwDeleteFlags::Default);
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+}
+
+void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam )
+{
+ lcl_DoWithBreaks(*this, rPam, SwDeleteFlags::Default, &DocumentContentOperationsManager::DeleteRangeImpl);
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+}
+
+bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam )
+{
+ const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
+ const SwNode* pNd = &rStt.nNode.GetNode();
+ SwNodeOffset nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() -
+ pNd->StartOfSectionIndex();
+ SwNodeOffset nNodeDiff = rEnd.nNode.GetIndex() - rStt.nNode.GetIndex();
+
+ if ( nSectDiff-SwNodeOffset(2) <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
+ /* #i9185# Prevent getting the node after the end node (see below) */
+ rEnd.nNode.GetIndex() + 1 == m_rDoc.GetNodes().Count() )
+ {
+ return false;
+ }
+
+ {
+ SwPaM temp(rPam, nullptr);
+ if (!temp.HasMark())
+ {
+ temp.SetMark();
+ }
+ if (SwTextNode *const pNode = temp.Start()->nNode.GetNode().GetTextNode())
+ { // rPam may not have nContent set but IsFieldmarkOverlap requires it
+ pNode->MakeStartIndex(&temp.Start()->nContent);
+ }
+ if (SwTextNode *const pNode = temp.End()->nNode.GetNode().GetTextNode())
+ {
+ pNode->MakeEndIndex(&temp.End()->nContent);
+ }
+ if (sw::mark::IsFieldmarkOverlap(temp))
+ { // a bit of a problem: we want to completely remove the nodes
+ // but then how can the CH_TXT_ATR survive?
+ return false;
+ }
+ }
+
+ // Move hard page breaks to the following Node.
+ bool bSavePageBreak = false, bSavePageDesc = false;
+
+ /* #i9185# This would lead to a segmentation fault if not caught above. */
+ SwNodeOffset nNextNd = rEnd.nNode.GetIndex() + 1;
+ SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode();
+
+ if( pTableNd && pNd->IsContentNode() )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ {
+ const SfxPoolItem *pItem;
+ const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet();
+ if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ bSavePageDesc = true;
+ }
+
+ if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ bSavePageBreak = true;
+ }
+ }
+ }
+
+ bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ if( bDoesUndo )
+ {
+ if( !rPam.HasMark() )
+ rPam.SetMark();
+ else if( rPam.GetPoint() == &rStt )
+ rPam.Exchange();
+ rPam.GetPoint()->nNode++;
+
+ SwContentNode *pTmpNode = rPam.GetPoint()->nNode.GetNode().GetContentNode();
+ rPam.GetPoint()->nContent.Assign( pTmpNode, 0 );
+ bool bGoNext = (nullptr == pTmpNode);
+ pTmpNode = rPam.GetMark()->nNode.GetNode().GetContentNode();
+ rPam.GetMark()->nContent.Assign( pTmpNode, 0 );
+
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+
+ SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
+ {
+ SwPosition aTmpPos( *aDelPam.GetPoint() );
+ if( bGoNext )
+ {
+ pTmpNode = m_rDoc.GetNodes().GoNext( &aTmpPos.nNode );
+ aTmpPos.nContent.Assign( pTmpNode, 0 );
+ }
+ ::PaMCorrAbs( aDelPam, aTmpPos );
+ }
+
+ std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aDelPam, SwDeleteFlags::Default, true));
+
+ *rPam.GetPoint() = *aDelPam.GetPoint();
+ pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ rPam.DeleteMark();
+ }
+ else
+ {
+ SwNodeRange aRg( rStt.nNode, rEnd.nNode );
+ rPam.Normalize(false);
+
+ // Try to move past the End
+ if( !rPam.Move( fnMoveForward, GoInNode ) )
+ {
+ // Fair enough, at the Beginning then
+ rPam.Exchange();
+ if( !rPam.Move( fnMoveBackward, GoInNode ))
+ {
+ SAL_WARN("sw.core", "DelFullPara: no more Nodes");
+ return false;
+ }
+ }
+
+ // must delete all fieldmarks before CorrAbs(), or they'll remain
+ // moved to wrong node without their CH_TXT_ATR_FIELD*
+ // (note: deleteMarks() doesn't help here, in case of partially
+ // selected fieldmarks; let's delete these as re-inserting their chars
+ // elsewhere looks difficult)
+ ::std::set<::sw::mark::IFieldmark*> fieldmarks;
+ for (SwNodeIndex i = aRg.aStart; i <= aRg.aEnd; ++i)
+ {
+ if (SwTextNode *const pTextNode = i.GetNode().GetTextNode())
+ {
+ for (sal_Int32 j = 0; j < pTextNode->GetText().getLength(); ++j)
+ {
+ switch (pTextNode->GetText()[j])
+ {
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDEND:
+ fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getFieldmarkAt(SwPosition(*pTextNode, j)));
+ break;
+ case CH_TXT_ATR_FIELDSEP:
+ fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getFieldmarkFor(SwPosition(*pTextNode, j)));
+ break;
+ }
+ }
+ }
+ }
+ for (auto const pFieldMark : fieldmarks)
+ {
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(pFieldMark);
+ }
+
+ // move bookmarks, redlines etc.
+ if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this
+ {
+ m_rDoc.CorrAbs( aRg.aStart, *rPam.GetPoint(), 0, true );
+ }
+ else
+ {
+ SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true );
+ }
+
+ // What's with Flys?
+ {
+ // If there are FlyFrames left, delete these too
+ for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n )
+ {
+ SwFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n];
+ const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ // note: here use <= not < like in
+ // IsDestroyFrameAnchoredAtChar() because of the increment
+ // of rPam in the bDoesUndo path above!
+ aRg.aStart <= pAPos->nNode && pAPos->nNode <= aRg.aEnd )
+ {
+ m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ --n;
+ }
+ }
+ }
+
+ rPam.DeleteMark();
+ m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 );
+ }
+ m_rDoc.getIDocumentState().SetModified();
+
+ return true;
+}
+
+bool DocumentContentOperationsManager::DeleteAndJoin(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ if ( lcl_StrLenOverflow( rPam ) )
+ return false;
+
+ bool const ret = lcl_DoWithBreaks( *this, rPam, flags, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
+ ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl
+ : &DocumentContentOperationsManager::DeleteAndJoinImpl );
+
+ return ret;
+}
+
+// It seems that this is mostly used by SwDoc internals; the only
+// way to call this from the outside seems to be the special case in
+// SwDoc::CopyRange (but I have not managed to actually hit that case).
+bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags )
+{
+ // nothing moved: return
+ const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End();
+ if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd))
+ return false;
+
+ assert(!sw::mark::IsFieldmarkOverlap(rPaM)); // probably an invalid redline was created?
+
+ // Save the paragraph anchored Flys, so that they can be moved.
+ SaveFlyArr aSaveFlyArr;
+ SaveFlyInRange( rPaM, rPos, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) );
+
+ // save redlines (if DOC_MOVEREDLINES is used)
+ SaveRedlines_t aSaveRedl;
+ if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ lcl_SaveRedlines( rPaM, aSaveRedl );
+
+ // #i17764# unfortunately, code below relies on undos being
+ // in a particular order, and presence of bookmarks
+ // will change this order. Hence, we delete bookmarks
+ // here without undo.
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ DelBookmarks(
+ pStt->nNode,
+ pEnd->nNode,
+ nullptr,
+ &pStt->nContent,
+ &pEnd->nContent);
+ }
+
+ bool bUpdateFootnote = false;
+ SwFootnoteIdxs aTmpFntIdx;
+
+ std::unique_ptr<SwUndoMove> pUndoMove;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ pUndoMove.reset(new SwUndoMove( rPaM, rPos ));
+ pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES );
+ }
+ else
+ {
+ bUpdateFootnote = lcl_SaveFootnote( pStt->nNode, pEnd->nNode, rPos.nNode,
+ m_rDoc.GetFootnoteIdxs(), aTmpFntIdx,
+ &pStt->nContent, &pEnd->nContent );
+ }
+
+ bool bSplit = false;
+ SwPaM aSavePam( rPos, rPos );
+
+ // Move the SPoint to the beginning of the range
+ if( rPaM.GetPoint() == pEnd )
+ rPaM.Exchange();
+
+ // If there is a TextNode before and after the Move, create a JoinNext in the EditShell.
+ SwTextNode* pSrcNd = rPaM.GetPoint()->nNode.GetNode().GetTextNode();
+ bool bCorrSavePam = pSrcNd && pStt->nNode != pEnd->nNode;
+
+ // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode.
+ // However, this does not update the cursor. So we create a TextNode to keep
+ // updating the indices. After the Move the Node is optionally deleted.
+ SwTextNode * pTNd = rPos.nNode.GetNode().GetTextNode();
+ if( pTNd && rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode &&
+ ( rPos.nContent.GetIndex() || ( pTNd->Len() && bCorrSavePam )) )
+ {
+ bSplit = true;
+ const sal_Int32 nMkContent = rPaM.GetMark()->nContent.GetIndex();
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save( m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
+
+ SwTextNode * pOrigNode = pTNd;
+ assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
+ *aSavePam.GetPoint() == rPos);
+ assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
+ assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
+ assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
+
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(m_rDoc, pOrigNode->GetIndex()-SwNodeOffset(1), 0, true, false, eMode);
+ }
+ });
+ pTNd = pTNd->SplitContentNode(rPos, &restoreFunc)->GetTextNode();
+
+ //A new node was inserted before the orig pTNd and the content up to
+ //rPos moved into it. The old node is returned with the remainder
+ //of the content in it.
+ //
+ //aSavePam was created with rPos, it continues to point to the
+ //old node, but with the *original* content index into the node.
+ //Seeing as all the orignode content before that index has
+ //been removed, the new index into the original node should now be set
+ //to 0 and the content index of rPos should also be adapted to the
+ //truncated node
+ assert(*aSavePam.GetPoint() == *aSavePam.GetMark() &&
+ *aSavePam.GetPoint() == rPos);
+ assert(aSavePam.GetPoint()->nContent.GetIdxReg() == pOrigNode);
+ assert(aSavePam.GetPoint()->nNode == rPos.nNode.GetIndex());
+ assert(rPos.nNode.GetIndex() == pOrigNode->GetIndex());
+ aSavePam.GetPoint()->nContent.Assign(pOrigNode, 0);
+ rPos = *aSavePam.GetMark() = *aSavePam.GetPoint();
+
+ // correct the PaM!
+ if( rPos.nNode == rPaM.GetMark()->nNode )
+ {
+ rPaM.GetMark()->nNode = rPos.nNode.GetIndex()-SwNodeOffset(1);
+ rPaM.GetMark()->nContent.Assign( pTNd, nMkContent );
+ }
+ }
+
+ // Put back the Pam by one "content"; so that it's always outside of
+ // the manipulated range.
+ // tdf#99692 don't Move() back if that would end up in another node
+ // because moving backward is not necessarily the inverse of forward then.
+ // (but do Move() back if we have split the node)
+ const bool bNullContent = !bSplit && aSavePam.GetPoint()->nContent == 0;
+ if( bNullContent )
+ {
+ aSavePam.GetPoint()->nNode--;
+ aSavePam.GetPoint()->nContent.Assign(aSavePam.GetContentNode(), 0);
+ }
+ else
+ {
+ bool const success(aSavePam.Move(fnMoveBackward, GoInContent));
+ assert(success);
+ (void) success;
+ }
+
+ // Copy all Bookmarks that are within the Move range into an array,
+ // that saves the position as an offset.
+ std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
+ DelBookmarks(
+ pStt->nNode,
+ pEnd->nNode,
+ &aSaveBkmks,
+ &pStt->nContent,
+ &pEnd->nContent);
+
+ // If there is no range anymore due to the above deletions (e.g. the
+ // footnotes got deleted), it's still a valid Move!
+ if( *rPaM.GetPoint() != *rPaM.GetMark() )
+ {
+ // now do the actual move
+ m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() );
+
+ // after a MoveRange() the Mark is deleted
+ if ( rPaM.HasMark() ) // => no Move occurred!
+ {
+ return false;
+ }
+ }
+ else
+ rPaM.DeleteMark();
+
+ OSL_ENSURE( *aSavePam.GetMark() == rPos ||
+ ( aSavePam.GetMark()->nNode.GetNode().GetContentNode() == nullptr ),
+ "PaM was not moved. Aren't there ContentNodes at the beginning/end?" );
+ *aSavePam.GetMark() = rPos;
+
+ rPaM.SetMark(); // create a Sel. around the new range
+ pTNd = aSavePam.GetNode().GetTextNode();
+ assert(!m_rDoc.GetIDocumentUndoRedo().DoesUndo());
+ bool bRemove = true;
+ // Do two Nodes have to be joined at the SavePam?
+ if (bSplit && pTNd)
+ {
+ if (pTNd->CanJoinNext())
+ {
+ // Always join next, because <pTNd> has to stay as it is.
+ // A join previous from its next would more or less delete <pTNd>
+ pTNd->JoinNext();
+ bRemove = false;
+ }
+ }
+ if (bNullContent)
+ {
+ aSavePam.GetPoint()->nNode++;
+ aSavePam.GetPoint()->nContent.Assign( aSavePam.GetContentNode(), 0 );
+ }
+ else if (bRemove) // No move forward after joining with next paragraph
+ {
+ aSavePam.Move( fnMoveForward, GoInContent );
+ }
+
+ // Insert the Bookmarks back into the Document.
+ *rPaM.GetMark() = *aSavePam.Start();
+ for(auto& rBkmk : aSaveBkmks)
+ rBkmk.SetInDoc(
+ &m_rDoc,
+ rPaM.GetMark()->nNode,
+ &rPaM.GetMark()->nContent);
+ *rPaM.GetPoint() = *aSavePam.End();
+
+ // Move the Flys to the new position.
+ // note: rPos is at the end here; can't really tell flys that used to be
+ // at the start of rPam from flys that used to be at the end of rPam
+ // unfortunately, so some of them are going to end up with wrong anchor...
+ RestFlyInRange( aSaveFlyArr, *rPaM.Start(), &(rPos.nNode) );
+
+ // restore redlines (if DOC_MOVEREDLINES is used)
+ if( !aSaveRedl.empty() )
+ {
+ lcl_RestoreRedlines( m_rDoc, *aSavePam.Start(), aSaveRedl );
+ }
+
+ if( bUpdateFootnote )
+ {
+ if( !aTmpFntIdx.empty() )
+ {
+ m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
+ aTmpFntIdx.clear();
+ }
+
+ m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNodeIndex& rPos,
+ SwMoveFlags eMvFlags )
+{
+ // Moves all Nodes to the new position.
+ // Bookmarks are moved too (currently without Undo support).
+
+ // If footnotes are being moved to the special section, remove them now.
+
+ // Or else delete the Frames for all footnotes that are being moved
+ // and have it rebuild after the Move (footnotes can change pages).
+ // Additionally we have to correct the FootnoteIdx array's sorting.
+ bool bUpdateFootnote = false;
+ SwFootnoteIdxs aTmpFntIdx;
+
+ std::unique_ptr<SwUndoMove> pUndo;
+ if ((SwMoveFlags::CREATEUNDOOBJ & eMvFlags ) && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoMove( m_rDoc, rRange, rPos ));
+ }
+ else
+ {
+ bUpdateFootnote = lcl_SaveFootnote( rRange.aStart, rRange.aEnd, rPos,
+ m_rDoc.GetFootnoteIdxs(), aTmpFntIdx );
+ }
+
+ SaveRedlines_t aSaveRedl;
+ std::vector<SwRangeRedline*> aSavRedlInsPosArr;
+ if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ lcl_SaveRedlines( rRange, aSaveRedl );
+
+ // Find all RedLines that end at the InsPos.
+ // These have to be moved back to the "old" position after the Move.
+ SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rPos.GetNode(), RedlineType::Any );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ const SwPosition *pRStt, *pREnd;
+ do {
+ SwRangeRedline* pTmp = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ pRStt = pTmp->Start();
+ pREnd = pTmp->End();
+ if( pREnd->nNode == rPos && pRStt->nNode < rPos )
+ {
+ aSavRedlInsPosArr.push_back( pTmp );
+ }
+ } while( pRStt->nNode < rPos && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size());
+ }
+ }
+
+ // Copy all Bookmarks that are within the Move range into an array
+ // that stores all references to positions as an offset.
+ // The final mapping happens after the Move.
+ std::vector< ::sw::mark::SaveBookmark> aSaveBkmks;
+ DelBookmarks(rRange.aStart, rRange.aEnd, &aSaveBkmks);
+
+ // Save the paragraph-bound Flys, so that they can be moved.
+ SaveFlyArr aSaveFlyArr;
+ if( !m_rDoc.GetSpzFrameFormats()->empty() )
+ SaveFlyInRange( rRange, aSaveFlyArr );
+
+ // Set it to before the Position, so that it cannot be moved further.
+ SwNodeIndex aIdx( rPos, -1 );
+
+ std::unique_ptr<SwNodeIndex> pSaveInsPos;
+ if( pUndo )
+ pSaveInsPos.reset(new SwNodeIndex( rRange.aStart, -1 ));
+
+ // move the Nodes
+ bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags);
+ if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rPos, !bNoDelFrames ) )
+ {
+ ++aIdx; // again back to old position
+ if( pSaveInsPos )
+ ++(*pSaveInsPos);
+ }
+ else
+ {
+ aIdx = rRange.aStart;
+ pUndo.reset();
+ }
+
+ // move the Flys to the new position
+ if( !aSaveFlyArr.empty() )
+ {
+ SwPosition const tmp(aIdx);
+ RestFlyInRange(aSaveFlyArr, tmp, nullptr);
+ }
+
+ // Add the Bookmarks back to the Document
+ for(auto& rBkmk : aSaveBkmks)
+ rBkmk.SetInDoc(&m_rDoc, aIdx);
+
+ if( !aSavRedlInsPosArr.empty() )
+ {
+ SwNode* pNewNd = &aIdx.GetNode();
+ for(SwRangeRedline* pTmp : aSavRedlInsPosArr)
+ {
+ if( m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp ) )
+ {
+ SwPosition* pEnd = pTmp->End();
+ pEnd->nNode = aIdx;
+ pEnd->nContent.Assign( pNewNd->GetContentNode(), 0 );
+ }
+ }
+ }
+
+ if( !aSaveRedl.empty() )
+ lcl_RestoreRedlines( m_rDoc, aIdx.GetIndex(), aSaveRedl );
+
+ if( pUndo )
+ {
+ pUndo->SetDestRange( aIdx, rPos, *pSaveInsPos );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ pSaveInsPos.reset();
+
+ if( bUpdateFootnote )
+ {
+ if( !aTmpFntIdx.empty() )
+ {
+ m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx );
+ aTmpFntIdx.clear();
+ }
+
+ m_rDoc.GetFootnoteIdxs().UpdateAllFootnote();
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+void DocumentContentOperationsManager::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos )
+{
+ SwNodeIndex aIdx( rPaM.Start()->nNode );
+ bool bJoinText = aIdx.GetNode().IsTextNode();
+ bool bOneNode = rPaM.GetPoint()->nNode == rPaM.GetMark()->nNode;
+ aIdx--; // in front of the move area!
+
+ bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT );
+ if( !bRet || bOneNode )
+ return;
+
+ if( bJoinText )
+ ++aIdx;
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+ SwNodeIndex aNxtIdx( aIdx );
+ if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) )
+ {
+ { // Block so SwIndex into node is deleted before Join
+ m_rDoc.CorrRel( aNxtIdx, SwPosition( aIdx, SwIndex(pTextNd,
+ pTextNd->GetText().getLength()) ), 0, true );
+ }
+ pTextNd->JoinNext();
+ }
+}
+
+// Overwrite only uses the point of the PaM, the mark is ignored; characters
+// are replaced from point until the end of the node; at the end of the node,
+// characters are inserted.
+bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr )
+{
+ assert(rStr.getLength());
+ SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint());
+ if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect
+ {
+ if( 1 == rStr.getLength() )
+ m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] );
+ m_rDoc.DeleteAutoCorrExceptWord();
+ }
+
+ SwTextNode *pNode = rPt.nNode.GetNode().GetTextNode();
+ if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase
+ {
+ return false;
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called
+ }
+
+ const size_t nOldAttrCnt = pNode->GetpSwpHints()
+ ? pNode->GetpSwpHints()->Count() : 0;
+ SwDataChanged aTmp( rRg );
+ SwIndex& rIdx = rPt.nContent;
+ sal_Int32 const nActualStart(rIdx.GetIndex());
+ sal_Int32 nStart = 0;
+
+ bool bOldExpFlg = pNode->IsIgnoreDontExpand();
+ pNode->SetIgnoreDontExpand( true );
+
+ for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt )
+ {
+ // start behind the characters (to fix the attributes!)
+ nStart = rIdx.GetIndex();
+ if (nStart < pNode->GetText().getLength())
+ {
+ lcl_SkipAttr( pNode, rIdx, nStart );
+ }
+ sal_Unicode c = rStr[ nCnt ];
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ bool bMerged(false);
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo();
+ SwUndoOverwrite *const pUndoOW(
+ dynamic_cast<SwUndoOverwrite *>(pUndo) );
+ if (pUndoOW)
+ {
+ // if CanGrouping() returns true it's already merged
+ bMerged = pUndoOW->CanGrouping(m_rDoc, rPt, c);
+ }
+ }
+ if (!bMerged)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoOverwrite>(m_rDoc, rPt, c) );
+ }
+ }
+ else
+ {
+ // start behind the characters (to fix the attributes!)
+ if (nStart < pNode->GetText().getLength())
+ ++rIdx;
+ pNode->InsertText( OUString(c), rIdx, SwInsertFlags::EMPTYEXPAND );
+ if( nStart+1 < rIdx.GetIndex() )
+ {
+ rIdx = nStart;
+ pNode->EraseText( rIdx, 1 );
+ ++rIdx;
+ }
+ }
+ }
+ pNode->SetIgnoreDontExpand( bOldExpFlg );
+
+ const size_t nNewAttrCnt = pNode->GetpSwpHints()
+ ? pNode->GetpSwpHints()->Count() : 0;
+ if( nOldAttrCnt != nNewAttrCnt )
+ {
+ const SwUpdateAttr aHint(0,0,0);
+ pNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+
+ if (!m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
+ !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ SwPaM aPam(rPt.nNode, nActualStart, rPt.nNode, rPt.nContent.GetIndex());
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any );
+ }
+ else if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ // FIXME: this redline is WRONG: there is no DELETE, and the skipped
+ // characters are also included in aPam
+ SwPaM aPam(rPt.nNode, nActualStart, rPt.nNode, rPt.nContent.GetIndex());
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr,
+ const SwInsertFlags nInsertMode )
+{
+ // tdf#119019 accept tracked paragraph formatting to do not hide new insertions
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg );
+ if (eOld != m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags())
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ }
+
+ // fetching DoesUndo is surprisingly expensive
+ bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ if (bDoesUndo)
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called!
+
+ const SwPosition& rPos = *rRg.GetPoint();
+
+ if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction
+ {
+ if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
+ {
+ m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] );
+ }
+ m_rDoc.DeleteAutoCorrExceptWord();
+ }
+
+ SwTextNode *const pNode = rPos.nNode.GetNode().GetTextNode();
+ if(!pNode)
+ return false;
+
+ SwDataChanged aTmp( rRg );
+
+ if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
+ if (bDoesUndo)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsert>(rPos.nNode,
+ rPos.nContent.GetIndex(), ins.getLength(), nInsertMode));
+ }
+ }
+ else
+ { // if Undo and grouping is enabled, everything changes!
+ SwUndoInsert * pUndo = nullptr;
+
+ // don't group the start if hints at the start should be expanded
+ if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND))
+ {
+ SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo();
+ SwUndoInsert *const pUndoInsert(
+ dynamic_cast<SwUndoInsert *>(pLastUndo) );
+ if (pUndoInsert && pUndoInsert->CanGrouping(rPos))
+ {
+ pUndo = pUndoInsert;
+ }
+ }
+
+ CharClass const& rCC = GetAppCharClass();
+ sal_Int32 nInsPos = rPos.nContent.GetIndex();
+
+ if (!pUndo)
+ {
+ pUndo = new SwUndoInsert( rPos.nNode, nInsPos, 0, nInsertMode,
+ !rCC.isLetterNumeric( rStr, 0 ) );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+
+ OUString const ins(pNode->InsertText(rStr, rPos.nContent, nInsertMode));
+
+ for (sal_Int32 i = 0; i < ins.getLength(); ++i)
+ {
+ nInsPos++;
+ // if CanGrouping() returns true, everything has already been done
+ if (!pUndo->CanGrouping(ins[i]))
+ {
+ pUndo = new SwUndoInsert(rPos.nNode, nInsPos, 1, nInsertMode,
+ !rCC.isLetterNumeric(ins, i));
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ }
+ }
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( rPos.nNode, aTmp.GetContent(),
+ rPos.nNode, rPos.nContent.GetIndex());
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ }
+ else
+ {
+ m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+void DocumentContentOperationsManager::SetIME(bool bIME)
+{
+ m_bIME = bIME;
+}
+
+bool DocumentContentOperationsManager::GetIME() const
+{
+ return m_bIME;
+}
+
+void DocumentContentOperationsManager::TransliterateText(
+ const SwPaM& rPaM,
+ utl::TransliterationWrapper& rTrans )
+{
+ std::unique_ptr<SwUndoTransliterate> pUndo;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoTransliterate( rPaM, rTrans ));
+
+ const SwPosition* pStt = rPaM.Start(),
+ * pEnd = rPaM.End();
+ SwNodeOffset nSttNd = pStt->nNode.GetIndex(),
+ nEndNd = pEnd->nNode.GetIndex();
+ sal_Int32 nSttCnt = pStt->nContent.GetIndex();
+ sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
+
+ SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ if( (pStt == pEnd) && pTNd ) // no selection?
+ {
+ /* Check if cursor is inside of a word */
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ pTNd->GetText(), nSttCnt,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ),
+ WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
+ true);
+
+ if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos )
+ {
+ /* Cursor is inside of a word */
+ if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE) {
+ /* set current sentence as 'area of effect' */
+ nSttCnt = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ pTNd->GetText(), nSttCnt,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ) );
+ nEndCnt = g_pBreakIt->GetBreakIter()->endOfSentence(
+ pTNd->GetText(), nEndCnt,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nEndCnt ) ) );
+ } else {
+ /* Set current word as 'area of effect' */
+ nSttCnt = aBndry.startPos;
+ nEndCnt = aBndry.endPos;
+ }
+ } else {
+ /* Cursor is not inside of a word. Nothing should happen. */
+ return;
+ }
+ }
+
+ bool bUseRedlining = m_rDoc.getIDocumentRedlineAccess().IsRedlineOn();
+ // as a workaround for a known performance problem, switch off redlining
+ // to avoid freezing, if transliteration could result too many redlines
+ if ( bUseRedlining )
+ {
+ const sal_uLong nMaxRedlines = 500;
+ const bool bIsTitleCase = rTrans.getType() == TransliterationFlags::TITLE_CASE;
+ sal_uLong nAffectedNodes = 0;
+ sal_uLong nAffectedChars = nEndCnt;
+ SwNodeIndex aIdx( pStt->nNode );
+ for( ; aIdx.GetIndex() <= nEndNd; ++aIdx )
+ {
+ SwTextNode* pAffectedNode = aIdx.GetNode().GetTextNode();
+
+ // don't count not text nodes or empty text nodes
+ if( !pAffectedNode || pAffectedNode->GetText().isEmpty() )
+ continue;
+
+ nAffectedNodes++;
+
+ // count characters of the node (the last - maybe partially
+ // selected - node was counted at initialization of nAffectedChars)
+ if( aIdx.GetIndex() < nEndNd )
+ nAffectedChars += pAffectedNode->GetText().getLength();
+
+ // transliteration creates n redlines for n nodes, except in the
+ // case of title case, where it creates n redlines for n words
+ if( nAffectedNodes > nMaxRedlines ||
+ // estimate word count based on the character count, where
+ // 6 = average English word length is ~5 letters + space
+ ( bIsTitleCase && (nAffectedChars - nSttCnt)/6 > nMaxRedlines ) )
+ {
+ bUseRedlining = false;
+ break;
+ }
+ }
+ }
+
+ if( nSttNd != nEndNd ) // is more than one text node involved?
+ {
+ // iterate over all affected text nodes, the first and the last one
+ // may be incomplete because the selection starts and/or ends there
+
+ SwNodeIndex aIdx( pStt->nNode );
+ if( nSttCnt )
+ {
+ ++aIdx;
+ if( pTNd )
+ {
+ pTNd->TransliterateText(
+ rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
+ }
+ }
+
+ for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
+ {
+ pTNd = aIdx.GetNode().GetTextNode();
+ if (pTNd)
+ {
+ pTNd->TransliterateText(
+ rTrans, 0, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining);
+ }
+ }
+
+ if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
+ {
+ pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get(), bUseRedlining );
+ }
+ }
+ else if( pTNd && nSttCnt < nEndCnt )
+ {
+ pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get(), bUseRedlining );
+ }
+ if( pUndo && pUndo->HasData() )
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphic(
+ const SwPaM &rRg,
+ const OUString& rGrfName,
+ const OUString& rFltName,
+ const Graphic* pGraphic,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet,
+ SwFrameFormat* pFrameFormat )
+{
+ if( !pFrameFormat )
+ pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
+ SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
+ SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
+ rGrfName, rFltName, pGraphic,
+ m_rDoc.GetDfltGrfFormatColl() );
+ SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
+ pFlyAttrSet, pGrfAttrSet, pFrameFormat );
+ return pSwFlyFrameFormat;
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphicObject(
+ const SwPaM &rRg, const GraphicObject& rGrfObj,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet )
+{
+ SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC );
+ SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode(
+ SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
+ rGrfObj, m_rDoc.GetDfltGrfFormatColl() );
+ SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode,
+ pFlyAttrSet, pGrfAttrSet, pFrameFormat );
+ return pSwFlyFrameFormat;
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertEmbObject(
+ const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj,
+ SfxItemSet* pFlyAttrSet)
+{
+ sal_uInt16 nId = RES_POOLFRM_OLE;
+ if (xObj.is())
+ {
+ SvGlobalName aClassName( xObj->getClassID() );
+ if (SotExchange::IsMath(aClassName))
+ {
+ nId = RES_POOLFRM_FORMEL;
+ }
+ }
+
+ SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId );
+
+ return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode(
+ SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
+ xObj,
+ m_rDoc.GetDfltGrfFormatColl() ),
+ pFlyAttrSet, nullptr,
+ pFrameFormat );
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsertOLE(const SwPaM &rRg, const OUString& rObjName,
+ sal_Int64 nAspect,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet)
+{
+ SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE );
+
+ return InsNoTextNode( *rRg.GetPoint(),
+ m_rDoc.GetNodes().MakeOLENode(
+ SwNodeIndex( m_rDoc.GetNodes().GetEndOfAutotext() ),
+ rObjName,
+ nAspect,
+ m_rDoc.GetDfltGrfFormatColl(),
+ nullptr ),
+ pFlyAttrSet, pGrfAttrSet,
+ pFrameFormat );
+}
+
+void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName,
+ const OUString& rFltName, const Graphic* pGraphic )
+{
+ SwGrfNode *pGrfNd;
+ if( !(( !rPam.HasMark()
+ || rPam.GetPoint()->nNode.GetIndex() == rPam.GetMark()->nNode.GetIndex() )
+ && nullptr != ( pGrfNd = rPam.GetPoint()->nNode.GetNode().GetGrfNode() )) )
+ return;
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoReRead>(rPam, *pGrfNd));
+ }
+
+ // Because we don't know if we can mirror the graphic, the mirror attribute is always reset
+ if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet().
+ GetMirrorGrf().GetValue() )
+ pGrfNd->SetAttr( SwMirrorGrf() );
+
+ pGrfNd->ReRead( rGrfName, rFltName, pGraphic );
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+// Insert drawing object, which has to be already inserted in the DrawModel
+SwDrawFrameFormat* DocumentContentOperationsManager::InsertDrawObj(
+ const SwPaM &rRg,
+ SdrObject& rDrawObj,
+ const SfxItemSet& rFlyAttrSet )
+{
+ SwDrawFrameFormat* pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
+
+ const SwFormatAnchor* pAnchor = rFlyAttrSet.GetItemIfSet( RES_ANCHOR, false );
+ pFormat->SetFormatAttr( rFlyAttrSet );
+
+ // Didn't set the Anchor yet?
+ // DrawObjecte must never end up in the Header/Footer!
+ RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId();
+ const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId);
+
+ const SwNodeIndex* pChkIdx = nullptr;
+ if ( pAnchor == nullptr )
+ {
+ pChkIdx = &rRg.GetPoint()->nNode;
+ }
+ else if ( bIsAtContent )
+ {
+ pChkIdx =
+ pAnchor->GetContentAnchor() ? &pAnchor->GetContentAnchor()->nNode : &rRg.GetPoint()->nNode;
+ }
+
+ // allow drawing objects in header/footer, but control objects aren't allowed in header/footer.
+ if( pChkIdx != nullptr
+ && ::CheckControlLayer( &rDrawObj )
+ && m_rDoc.IsInHeaderFooter( *pChkIdx ) )
+ {
+ // apply at-page anchor format
+ eAnchorId = RndStdIds::FLY_AT_PAGE;
+ pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) );
+ }
+ else if( pAnchor == nullptr
+ || ( bIsAtContent
+ && pAnchor->GetContentAnchor() == nullptr ) )
+ {
+ // apply anchor format
+ SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() );
+ eAnchorId = aAnch.GetAnchorId();
+ if ( eAnchorId == RndStdIds::FLY_AT_FLY )
+ {
+ const SwStartNode* pStartNode = rRg.GetNode().FindFlyStartNode();
+ assert(pStartNode);
+ SwPosition aPos(*pStartNode);
+ aAnch.SetAnchor( &aPos );
+ }
+ else
+ {
+ aAnch.SetAnchor( rRg.GetPoint() );
+ if ( eAnchorId == RndStdIds::FLY_AT_PAGE )
+ {
+ eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA;
+ aAnch.SetType( eAnchorId );
+ }
+ }
+ pFormat->SetFormatAttr( aAnch );
+ }
+
+ // insert text attribute for as-character anchored drawing object
+ if ( eAnchorId == RndStdIds::FLY_AS_CHAR )
+ {
+ bool bAnchorAtPageAsFallback = true;
+ const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor();
+ if ( rDrawObjAnchorFormat.GetContentAnchor() != nullptr )
+ {
+ SwTextNode* pAnchorTextNode =
+ rDrawObjAnchorFormat.GetContentAnchor()->nNode.GetNode().GetTextNode();
+ if ( pAnchorTextNode != nullptr )
+ {
+ const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->nContent.GetIndex();
+ SwFormatFlyCnt aFormat( pFormat );
+ pAnchorTextNode->InsertItem( aFormat, nStt, nStt );
+ bAnchorAtPageAsFallback = false;
+ }
+ }
+
+ if ( bAnchorAtPageAsFallback )
+ {
+ OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" );
+ pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) );
+ }
+ }
+
+ SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj );
+
+ // Create Frames if necessary
+ if( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ // create layout representation
+ pFormat->MakeFrames();
+ // #i42319# - follow-up of #i35635#
+ // move object to visible layer
+ // #i79391#
+ if ( pContact->GetAnchorFrame() )
+ {
+ pContact->MoveObjToVisibleLayer( &rDrawObj );
+ }
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0) );
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return pFormat;
+}
+
+bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart )
+{
+ SwContentNode *pNode = rPos.nNode.GetNode().GetContentNode();
+ if(nullptr == pNode)
+ return false;
+
+ {
+ // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope.
+ // After that they can be before/after the position.
+ SwDataChanged aTmp( m_rDoc, rPos );
+ }
+
+ SwUndoSplitNode* pUndo = nullptr;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ // insert the Undo object (currently only for TextNode)
+ if( pNode->IsTextNode() )
+ {
+ pUndo = new SwUndoSplitNode( m_rDoc, rPos, bChkTableStart );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+ }
+
+ // Update the rsid of the old and the new node unless
+ // the old node is split at the beginning or at the end
+ SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
+ const sal_Int32 nPos = rPos.nContent.GetIndex();
+ if( pTextNode && nPos && nPos != pTextNode->Len() )
+ {
+ m_rDoc.UpdateParRsid( pTextNode );
+ }
+
+ //JP 28.01.97: Special case for SplitNode at table start:
+ // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table
+ // then insert a paragraph before it.
+ if( bChkTableStart && !rPos.nContent.GetIndex() && pNode->IsTextNode() )
+ {
+ SwNodeOffset nPrevPos = rPos.nNode.GetIndex() - 1;
+ const SwTableNode* pTableNd;
+ const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ];
+ if( pNd->IsStartNode() &&
+ SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() &&
+ nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) &&
+ ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() &&
+ SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() )
+ || ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() )
+ || pNd->IsContentNode() ))
+ {
+ if( pNd->IsContentNode() )
+ {
+ //JP 30.04.99 Bug 65660:
+ // There are no page breaks outside of the normal body area,
+ // so this is not a valid condition to insert a paragraph.
+ if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ pNd = nullptr;
+ else
+ {
+ // Only if the table has page breaks!
+ const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
+ if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) &&
+ SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) )
+ pNd = nullptr;
+ }
+ }
+
+ if( pNd )
+ {
+ SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode(
+ SwNodeIndex( *pTableNd ),
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ if( pTextNd )
+ {
+ const_cast<SwPosition&>(rPos).nNode = pTableNd->GetIndex()-SwNodeOffset(1);
+ const_cast<SwPosition&>(rPos).nContent.Assign( pTextNd, 0 );
+
+ // only add page breaks/styles to the body area
+ if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ {
+ pTextNd->SetAttr( *pItem );
+ pFrameFormat->ResetFormatAttr( RES_PAGEDESC );
+ }
+ if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ {
+ pTextNd->SetAttr( *pItem );
+ pFrameFormat->ResetFormatAttr( RES_BREAK );
+ }
+ }
+
+ if( pUndo )
+ pUndo->SetTableFlag();
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+ }
+ }
+ }
+ }
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save( m_rDoc, rPos.nNode.GetIndex(), rPos.nContent.GetIndex(), true );
+ assert(pNode->IsTextNode());
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool bAtStart)> restoreFunc(
+ [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool const bAtStart)
+ {
+ if (!pContentStore->Empty())
+ { // move all bookmarks, TOXMarks, FlyAtCnt
+ pContentStore->Restore(m_rDoc, rPos.nNode.GetIndex()-SwNodeOffset(1), 0, true, bAtStart && (eMode & sw::mark::RestoreMode::Flys), eMode);
+ }
+ if (eMode & sw::mark::RestoreMode::NonFlys)
+ {
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ||
+ (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() &&
+ !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()))
+ {
+ SwPaM aPam( rPos );
+ aPam.SetMark();
+ aPam.Move( fnMoveBackward );
+ if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
+ {
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(RedlineType::Insert, aPam), true);
+ }
+ else
+ {
+ m_rDoc.getIDocumentRedlineAccess().SplitRedline(aPam);
+ }
+ }
+ }
+ });
+ pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc);
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::AppendTextNode( SwPosition& rPos )
+{
+ // create new node before EndOfContent
+ SwTextNode * pCurNode = rPos.nNode.GetNode().GetTextNode();
+ if( !pCurNode )
+ {
+ // so then one can be created!
+ SwNodeIndex aIdx( rPos.nNode, 1 );
+ pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx,
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+ }
+ else
+ pCurNode = pCurNode->AppendNode( rPos )->GetTextNode();
+
+ rPos.nNode++;
+ rPos.nContent.Assign( pCurNode, 0 );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsert>( rPos.nNode ) );
+ }
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( rPos );
+ aPam.SetMark();
+ aPam.Move( fnMoveBackward );
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ else
+ m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return true;
+}
+
+bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr,
+ const bool bRegExReplace )
+{
+ // unfortunately replace works slightly differently from delete,
+ // so we cannot use lcl_DoWithBreaks here...
+
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ aPam.Normalize(false);
+ if (aPam.GetPoint()->nNode != aPam.GetMark()->nNode)
+ {
+ aPam.Move(fnMoveBackward);
+ }
+ OSL_ENSURE((aPam.GetPoint()->nNode == aPam.GetMark()->nNode), "invalid pam?");
+
+ sw::CalcBreaks(Breaks, aPam);
+
+ while (!Breaks.empty() // skip over prefix of dummy chars
+ && (aPam.GetMark()->nNode.GetIndex() == Breaks.begin()->first)
+ && (aPam.GetMark()->nContent.GetIndex() == Breaks.begin()->second))
+ {
+ // skip!
+ ++aPam.GetMark()->nContent; // always in bounds if Breaks valid
+ Breaks.erase(Breaks.begin());
+ }
+ *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix
+
+ if (Breaks.empty())
+ {
+ // park aPam somewhere so it does not point to node that is deleted
+ aPam.DeleteMark();
+ *aPam.GetPoint() = SwPosition(m_rDoc.GetNodes().GetEndOfContent());
+ return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam!
+ }
+
+ // Deletion must be split into several parts if the text node
+ // contains a text attribute with end and with dummy character
+ // and the selection does not contain the text attribute completely,
+ // but overlaps its start (left), where the dummy character is.
+
+ bool bRet( true );
+ // iterate from end to start, to avoid invalidating the offsets!
+ auto iter( Breaks.rbegin() );
+ SwNodeOffset nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
+ OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!");
+ SwPosition & rEnd( *aPam.End() );
+ SwPosition & rStart( *aPam.Start() );
+
+ // set end of temp pam to original end (undo Move backward above)
+ rEnd = *rPam.End();
+ // after first deletion, rEnd will point into the original text node again!
+
+ while (iter != Breaks.rend())
+ {
+ rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn())
+ ? DeleteAndJoinWithRedlineImpl(aPam, SwDeleteFlags::Default)
+ : DeleteAndJoinImpl(aPam, SwDeleteFlags::Default);
+ nOffset = iter->first - rStart.nNode.GetIndex(); // deleted fly nodes...
+ }
+ rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
+ ++iter;
+ }
+
+ rStart = *rPam.Start(); // set to original start
+ assert(rStart < rEnd && "replace part empty!");
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace);
+ }
+
+ rPam = aPam; // update original pam (is this required?)
+
+ return bRet;
+}
+
+bool DocumentContentOperationsManager::InsertPoolItem(
+ const SwPaM &rRg,
+ const SfxPoolItem &rHt,
+ const SetAttrMode nFlags,
+ SwRootFrame const*const pLayout,
+ SwTextAttr **ppNewTextAttr)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+
+ SwDataChanged aTmp( rRg );
+ std::unique_ptr<SwUndoAttr> pUndoAttr;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ pUndoAttr.reset(new SwUndoAttr( rRg, rHt, nFlags ));
+ }
+
+ SfxItemSet aSet( m_rDoc.GetAttrPool(), rHt.Which(), rHt.Which() );
+ aSet.Put( rHt );
+ const bool bRet = lcl_InsAttr(m_rDoc, rRg, aSet, nFlags, pUndoAttr.get(), pLayout, ppNewTextAttr);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
+ }
+
+ if( bRet )
+ {
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ return bRet;
+}
+
+void DocumentContentOperationsManager::InsertItemSet ( const SwPaM &rRg, const SfxItemSet &rSet,
+ const SetAttrMode nFlags, SwRootFrame const*const pLayout)
+{
+ SwDataChanged aTmp( rRg );
+ std::unique_ptr<SwUndoAttr> pUndoAttr;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ pUndoAttr.reset(new SwUndoAttr( rRg, rSet, nFlags ));
+ }
+
+ bool bRet = lcl_InsAttr(m_rDoc, rRg, rSet, nFlags, pUndoAttr.get(), pLayout, /*ppNewTextAttr*/nullptr );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) );
+ }
+
+ if( bRet )
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition & rPos )
+{
+ const SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
+ if ( !pTNd )
+ return;
+
+ const OUString& rText = pTNd->GetText();
+ sal_Int32 nIdx = 0;
+ while (nIdx < rText.getLength())
+ {
+ sal_Unicode const cCh = rText[nIdx];
+ if (('\t' != cCh) && (' ' != cCh))
+ {
+ break;
+ }
+ ++nIdx;
+ }
+
+ if ( nIdx > 0 )
+ {
+ SwPaM aPam(rPos);
+ aPam.GetPoint()->nContent = 0;
+ aPam.SetMark();
+ aPam.GetMark()->nContent = nIdx;
+ DeleteRange( aPam );
+ }
+}
+
+// Copy method from SwDoc - "copy Flys in Flys"
+/// note: rRg/rInsPos *exclude* a partially selected start text node;
+/// pCopiedPaM *includes* a partially selected start text node
+void DocumentContentOperationsManager::CopyWithFlyInFly(
+ const SwNodeRange& rRg,
+ const SwNodeIndex& rInsPos,
+ const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/,
+ const bool bMakeNewFrames,
+ const bool bDelRedlines,
+ const bool bCopyFlyAtFly,
+ SwCopyFlags const flags) const
+{
+ assert(!pCopiedPaM || pCopiedPaM->first.End()->nNode == rRg.aEnd);
+ assert(!pCopiedPaM || pCopiedPaM->second.nNode <= rInsPos);
+
+ SwDoc& rDest = rInsPos.GetNode().GetDoc();
+ SwNodeIndex aSavePos( rInsPos );
+
+ if (rRg.aStart != rRg.aEnd)
+ {
+ bool bEndIsEqualEndPos = rInsPos == rRg.aEnd;
+ --aSavePos;
+ SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 );
+
+ // insert behind the already copied start node
+ m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, false, true );
+ aRedlRest.Restore();
+
+ if (bEndIsEqualEndPos)
+ {
+ const_cast<SwNodeIndex&>(rRg.aEnd).Assign(aSavePos.GetNode(), +1);
+ }
+ }
+
+ // Also copy all bookmarks
+ // guess this must be done before the DelDummyNodes below as that
+ // deletes nodes so would mess up the index arithmetic
+ // sw_fieldmarkhide: also needs to be done before making frames
+ if (m_rDoc.getIDocumentMarkAccess()->getAllMarksCount())
+ {
+ SwPaM aRgTmp( rRg.aStart, rRg.aEnd );
+ SwPosition targetPos(SwNodeIndex(aSavePos,
+ SwNodeOffset(rRg.aStart != rRg.aEnd ? +1 : 0)));
+ if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
+ {
+ // there is 1 (partially selected, maybe) paragraph before
+ assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->nNode);
+ // only use the passed in target SwPosition if the source PaM point
+ // is on a different node; if it was the same node then the target
+ // position was likely moved along by the copy operation and now
+ // points to the end of the range!
+ targetPos = pCopiedPaM->second;
+ }
+
+ sw::CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, targetPos);
+ }
+
+ if (rRg.aStart != rRg.aEnd)
+ {
+ bool isRecreateEndNode(false);
+ if (bMakeNewFrames) // tdf#130685 only after aRedlRest
+ { // recreate from previous node (could be merged now)
+ o3tl::sorted_vector<SwTextFrame*> frames;
+ SwTextNode * pNode = aSavePos.GetNode().GetTextNode();
+ SwTextNode *const pEndNode = rInsPos.GetNode().GetTextNode();
+ if (pEndNode)
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.insert(pFrame);
+ // tdf#135061 check if end node is merged to a preceding node
+ if (pNode == nullptr && pFrame->GetMergedPara()
+ && pFrame->GetMergedPara()->pFirstNode->GetIndex() < aSavePos.GetIndex())
+ {
+ pNode = pFrame->GetMergedPara()->pFirstNode;
+ }
+ }
+ }
+ }
+ if (pNode != nullptr)
+ {
+ sw::RecreateStartTextFrames(*pNode);
+ if (!frames.empty())
+ { // tdf#132187 check if the end node needs new frames
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ auto const it = frames.find(pFrame);
+ if (it != frames.end())
+ {
+ frames.erase(it);
+ }
+ }
+ }
+ if (!frames.empty()) // existing frame was deleted
+ { // all layouts because MakeFrames recreates all layouts
+ pEndNode->DelFrames(nullptr);
+ isRecreateEndNode = true;
+ }
+ }
+ }
+ }
+ bool const isAtStartOfSection(aSavePos.GetNode().IsStartNode());
+ ++aSavePos;
+ if (bMakeNewFrames)
+ {
+ // it's possible that CheckParaRedlineMerge() deleted frames
+ // on rInsPos so have to include it, but it must not be included
+ // if it was the first node in the document so that MakeFrames()
+ // will find the existing (wasn't deleted) frame on it
+ SwNodeIndex const end(rInsPos,
+ SwNodeOffset((!isRecreateEndNode || isAtStartOfSection)
+ ? 0 : +1));
+ ::MakeFrames(&rDest, aSavePos, end);
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ {
+ //JP 17.06.99: Bug 66973 - check count only if the selection is in
+ // the same section or there's no section, because sections that are
+ // not fully selected are not copied.
+ const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode();
+ SwNodeIndex aTmpI( rRg.aEnd, -1 );
+ const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode();
+ if( pSSectNd == pESectNd &&
+ !rRg.aStart.GetNode().IsSectionNode() &&
+ !aTmpI.GetNode().IsEndNode() )
+ {
+ // If the range starts with a SwStartNode, it isn't copied
+ SwNodeOffset offset( (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0 );
+ OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() ==
+ rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset,
+ "An insufficient number of nodes were copied!" );
+ }
+ }
+#endif
+
+ {
+ ::sw::UndoGuard const undoGuard(rDest.GetIDocumentUndoRedo());
+ CopyFlyInFlyImpl(rRg, pCopiedPaM ? &pCopiedPaM->first : nullptr,
+ // see comment below regarding use of pCopiedPaM->second
+ (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->nNode)
+ ? pCopiedPaM->second.nNode
+ : aSavePos,
+ bCopyFlyAtFly,
+ flags);
+ }
+
+ SwNodeRange aCpyRange( aSavePos, rInsPos );
+
+ if( bDelRedlines && ( RedlineFlags::DeleteRedlines & rDest.getIDocumentRedlineAccess().GetRedlineFlags() ))
+ lcl_DeleteRedlines( rRg, aCpyRange );
+
+ rDest.GetNodes().DelDummyNodes( aCpyRange );
+}
+
+// note: for the redline Show/Hide this must be in sync with
+// SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection()
+void DocumentContentOperationsManager::CopyFlyInFlyImpl(
+ const SwNodeRange& rRg,
+ SwPaM const*const pCopiedPaM,
+ const SwNodeIndex& rStartIdx,
+ const bool bCopyFlyAtFly,
+ SwCopyFlags const flags) const
+{
+ assert(!pCopiedPaM || pCopiedPaM->End()->nNode == rRg.aEnd);
+
+ // First collect all Flys, sort them according to their ordering number,
+ // and then only copy them. This maintains the ordering numbers (which are only
+ // managed in the DrawModel).
+ SwDoc& rDest = rStartIdx.GetNode().GetDoc();
+ std::set< ZSortFly > aSet;
+ const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size();
+
+ SwTextBoxHelper::SavedLink aOldTextBoxes;
+ SwTextBoxHelper::saveLinks(*m_rDoc.GetSpzFrameFormats(), aOldTextBoxes);
+
+ for ( size_t n = 0; n < nArrLen; ++n )
+ {
+ SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if ( !pAPos )
+ continue;
+ bool bAdd = false;
+ SwNodeOffset nSkipAfter = pAPos->nNode.GetIndex();
+ SwNodeOffset nStart = rRg.aStart.GetIndex();
+ switch ( pAnchor->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_FLY:
+ if(bCopyFlyAtFly)
+ ++nSkipAfter;
+ else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove())
+ ++nStart;
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ {
+ bAdd = IsSelectFrameAnchoredAtPara(*pAPos,
+ pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
+ pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
+ (flags & SwCopyFlags::IsMoveToFly)
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask);
+ }
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ bAdd = IsDestroyFrameAnchoredAtChar(*pAPos,
+ pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart),
+ pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd),
+ (flags & SwCopyFlags::IsMoveToFly)
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask);
+ }
+ break;
+ default:
+ continue;
+ }
+ if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId())
+ {
+ if (nStart > nSkipAfter)
+ continue;
+ if (pAPos->nNode > rRg.aEnd)
+ continue;
+ //frames at the last source node are not always copied:
+ //- if the node is empty and is the last node of the document or a table cell
+ // or a text frame then they have to be copied
+ //- if the content index in this node is > 0 then paragraph and frame bound objects are copied
+ //- to-character bound objects are copied if their index is <= nEndContentIndex
+ if (pAPos->nNode < rRg.aEnd)
+ bAdd = true;
+ if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move
+ {
+ if (!bAdd)
+ {
+ // technically old code checked nContent of AT_FLY which is pointless
+ bAdd = pCopiedPaM && 0 < pCopiedPaM->End()->nContent.GetIndex();
+ }
+ }
+ }
+ if( bAdd )
+ {
+ aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() ));
+ }
+ }
+
+ // Store all copied (and also the newly created) frames in another array.
+ // They are stored as matching the originals, so that we will be later
+ // able to build the chains accordingly.
+ std::vector< SwFrameFormat* > aVecSwFrameFormat;
+ std::set< ZSortFly >::const_iterator it=aSet.begin();
+
+ while (it != aSet.end())
+ {
+ // #i59964#
+ // correct determination of new anchor position
+ SwFormatAnchor aAnchor( *(*it).GetAnchor() );
+ assert( aAnchor.GetContentAnchor() != nullptr );
+ SwPosition newPos = *aAnchor.GetContentAnchor();
+ // for at-paragraph and at-character anchored objects the new anchor
+ // position can *not* be determined by the difference of the current
+ // anchor position to the start of the copied range, because not
+ // complete selected sections in the copied range aren't copied - see
+ // method <SwNodes::CopyNodes(..)>.
+ // Thus, the new anchor position in the destination document is found
+ // by counting the text nodes.
+ if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
+ (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) )
+ {
+ // First, determine number of anchor text node in the copied range.
+ // Note: The anchor text node *have* to be inside the copied range.
+ sal_uLong nAnchorTextNdNumInRange( 0 );
+ bool bAnchorTextNdFound( false );
+ // start at the first node for which flys are copied
+ SwNodeIndex aIdx(pCopiedPaM ? pCopiedPaM->Start()->nNode : rRg.aStart);
+ while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd )
+ {
+ if ( aIdx.GetNode().IsTextNode() )
+ {
+ ++nAnchorTextNdNumInRange;
+ bAnchorTextNdFound = aAnchor.GetContentAnchor()->nNode == aIdx;
+ }
+
+ ++aIdx;
+ }
+
+ if ( !bAnchorTextNdFound )
+ {
+ // This case can *not* happen, but to be robust take the first
+ // text node in the destination document.
+ OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" );
+ nAnchorTextNdNumInRange = 1;
+ }
+ // Second, search corresponding text node in destination document
+ // by counting forward from start insert position <rStartIdx> the
+ // determined number of text nodes.
+ aIdx = rStartIdx;
+ SwNodeIndex aAnchorNdIdx( rStartIdx );
+ const SwNode& aEndOfContentNd =
+ aIdx.GetNode().GetNodes().GetEndOfContent();
+ while ( nAnchorTextNdNumInRange > 0 &&
+ &(aIdx.GetNode()) != &aEndOfContentNd )
+ {
+ if ( aIdx.GetNode().IsTextNode() )
+ {
+ --nAnchorTextNdNumInRange;
+ aAnchorNdIdx = aIdx;
+ }
+
+ ++aIdx;
+ }
+ if ( !aAnchorNdIdx.GetNode().IsTextNode() )
+ {
+ // This case can *not* happen, but to be robust take the first
+ // text node in the destination document.
+ OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" );
+ aAnchorNdIdx = rStartIdx;
+ while ( !aAnchorNdIdx.GetNode().IsTextNode() )
+ {
+ ++aAnchorNdIdx;
+ }
+ }
+ // apply found anchor text node as new anchor position
+ newPos.nNode = aAnchorNdIdx;
+ }
+ else
+ {
+ SwNodeOffset nOffset = newPos.nNode.GetIndex() - rRg.aStart.GetIndex();
+ SwNodeIndex aIdx( rStartIdx, nOffset );
+ newPos.nNode = aIdx;
+ }
+ // Set the character bound Flys back at the original character
+ if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) &&
+ newPos.nNode.GetNode().IsTextNode() )
+ {
+ // only if pCopiedPaM: care about partially selected start node
+ sal_Int32 const nContent = pCopiedPaM && pCopiedPaM->Start()->nNode == aAnchor.GetContentAnchor()->nNode
+ ? newPos.nContent.GetIndex() - pCopiedPaM->Start()->nContent.GetIndex()
+ : newPos.nContent.GetIndex();
+ newPos.nContent.Assign(newPos.nNode.GetNode().GetTextNode(), nContent);
+ }
+ else
+ {
+ newPos.nContent.Assign( nullptr, 0 );
+ }
+ aAnchor.SetAnchor( &newPos );
+
+ // Check recursion: if copying content inside the same frame, then don't copy the format.
+ if( &rDest == &m_rDoc )
+ {
+ const SwFormatContent& rContent = (*it).GetFormat()->GetContent();
+ const SwStartNode* pSNd;
+ if( rContent.GetContentIdx() &&
+ nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) &&
+ pSNd->GetIndex() < rStartIdx.GetIndex() &&
+ rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() )
+ {
+ it = aSet.erase(it);
+ continue;
+ }
+ }
+
+ // Ignore TextBoxes, they are already handled in
+ // sw::DocumentLayoutManager::CopyLayoutFormat().
+ if (SwTextBoxHelper::isTextBox(it->GetFormat(), RES_FLYFRMFMT))
+ {
+ it = aSet.erase(it);
+ continue;
+ }
+
+ // Copy the format and set the new anchor
+ aVecSwFrameFormat.push_back( rDest.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(),
+ aAnchor, false, true ) );
+ ++it;
+ }
+
+ // Rebuild as much as possible of all chains that are available in the original,
+ OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" );
+ if ( aSet.size() != aVecSwFrameFormat.size() )
+ return;
+
+ size_t n = 0;
+ for (const auto& rFlyN : aSet)
+ {
+ const SwFrameFormat *pFormatN = rFlyN.GetFormat();
+ const SwFormatChain &rChain = pFormatN->GetChain();
+ size_t k = 0;
+ for (const auto& rFlyK : aSet)
+ {
+ const SwFrameFormat *pFormatK = rFlyK.GetFormat();
+ if ( rChain.GetPrev() == pFormatK )
+ {
+ ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]),
+ static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) );
+ }
+ else if ( rChain.GetNext() == pFormatK )
+ {
+ ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]),
+ static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) );
+ }
+ ++k;
+ }
+ ++n;
+ }
+
+ // Re-create content property of draw formats, knowing how old shapes
+ // were paired with old fly formats (aOldTextBoxes) and that aSet is
+ // parallel with aVecSwFrameFormat.
+ SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes);
+}
+
+/*
+ * Reset the text's hard formatting
+ */
+/** @params pArgs contains the document's ChrFormatTable
+ * Is need for selections at the beginning/end and with no SSelection.
+ */
+bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode* pNd, void* pArgs )
+{
+ ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs);
+ if (pPara->pLayout && pPara->pLayout->HasMergedParas()
+ && pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return true; // skip hidden, since new items aren't applied
+ }
+ SwTextNode * pTextNode = pNd->GetTextNode();
+ if( pTextNode && pTextNode->GetpSwpHints() )
+ {
+ SwIndex aSt( pTextNode, 0 );
+ sal_Int32 nEnd = pTextNode->Len();
+
+ if( &pPara->pSttNd->nNode.GetNode() == pTextNode &&
+ pPara->pSttNd->nContent.GetIndex() )
+ aSt = pPara->pSttNd->nContent.GetIndex();
+
+ if( &pPara->pEndNd->nNode.GetNode() == pNd )
+ nEnd = pPara->pEndNd->nContent.GetIndex();
+
+ if( pPara->pHistory )
+ {
+ // Save all attributes for the Undo.
+ SwRegHistory aRHst( *pTextNode, pPara->pHistory );
+ pTextNode->GetpSwpHints()->Register( &aRHst );
+ pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
+ pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
+ if( pTextNode->GetpSwpHints() )
+ pTextNode->GetpSwpHints()->DeRegister();
+ }
+ else
+ pTextNode->RstTextAttr( aSt, nEnd - aSt.GetIndex(), pPara->nWhich,
+ pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange );
+ }
+ return true;
+}
+
+DocumentContentOperationsManager::~DocumentContentOperationsManager()
+{
+}
+//Private methods
+
+bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ assert(m_rDoc.getIDocumentRedlineAccess().IsRedlineOn());
+
+ RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+
+ if (*rPam.GetPoint() == *rPam.GetMark())
+ {
+ return false; // do not add empty redlines
+ }
+
+ std::vector<SwRangeRedline*> redlines;
+ {
+ auto pRedline(std::make_unique<SwRangeRedline>(RedlineType::Delete, rPam));
+ if (pRedline->HasValidRange())
+ {
+ redlines.push_back(pRedline.release());
+ }
+ else // sigh ... why is such a selection even possible...
+ { // split it up so we get one SwUndoRedlineDelete per inserted RL
+ redlines = GetAllValidRanges(std::move(pRedline));
+ }
+ }
+
+ if (redlines.empty())
+ {
+ return false;
+ }
+
+ // tdf#54819 current redlining needs also modification of paragraph style and
+ // attributes added to the same grouped Undo
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ auto & rDMA(*m_rDoc.getIDocumentMarkAccess());
+ std::vector<std::unique_ptr<SwUndo>> MarkUndos;
+ for (auto iter = rDMA.getAnnotationMarksBegin();
+ iter != rDMA.getAnnotationMarksEnd(); )
+ {
+ // tdf#111524 remove annotation marks that have their field
+ // characters deleted
+ SwPosition const& rEndPos((**iter).GetMarkEnd());
+ if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End())
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ MarkUndos.emplace_back(std::make_unique<SwUndoDeleteBookmark>(**iter));
+ }
+ // iter is into annotation mark vector so must be dereferenced!
+ rDMA.deleteMark(&**iter);
+ // this invalidates iter, have to start over...
+ iter = rDMA.getAnnotationMarksBegin();
+ }
+ else
+ { // marks are sorted by start
+ if (*rPam.End() < (**iter).GetMarkStart())
+ {
+ break;
+ }
+ ++iter;
+ }
+ }
+
+ // tdf#119019 accept tracked paragraph formatting to do not hide new deletions
+ if (*rPam.GetPoint() != *rPam.GetMark())
+ m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam);
+
+ std::vector<std::unique_ptr<SwUndoRedlineDelete>> undos;
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // this should no longer happen in calls from the UI but maybe via API
+ // (randomTest and testTdf54819 triggers it)
+ SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
+ "sw.core", "redlines will be moved in DeleteAndJoin");
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ for (SwRangeRedline * pRedline : redlines)
+ {
+ assert(pRedline->HasValidRange());
+ undos.emplace_back(std::make_unique<SwUndoRedlineDelete>(
+ *pRedline, SwUndoId::DELETE, flags));
+ }
+ const SwRewriter aRewriter = undos.front()->GetRewriter();
+ // can only group a single undo action
+ if (MarkUndos.empty() && undos.size() == 1
+ && m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
+ SwUndoRedlineDelete *const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete*>(pLastUndo));
+ bool const bMerged = pUndoRedlineDel
+ && pUndoRedlineDel->CanGrouping(*undos.front());
+ if (!bMerged)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(undos.front()));
+ }
+ undos.clear(); // prevent unmatched EndUndo
+ }
+ else
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE, &aRewriter);
+ for (auto& it : MarkUndos)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
+ }
+ for (auto & it : undos)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it));
+ }
+ }
+ }
+
+ for (SwRangeRedline *const pRedline : redlines)
+ {
+ // note: 1. the pRedline can still be merged & deleted
+ // 2. the impl. can even DeleteAndJoin the range => no plain PaM
+ std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*pRedline->GetMark()));
+ pCursor->SetMark();
+ *pCursor->GetPoint() = *pRedline->GetPoint();
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(pRedline, true);
+ // sw_redlinehide: 2 reasons why this is needed:
+ // 1. it's the first redline in node => RedlineDelText was sent but ignored
+ // 2. redline spans multiple nodes => must merge text frames
+ sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor);
+ }
+ m_rDoc.getIDocumentState().SetModified();
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ if (!undos.empty())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ return true;
+}
+
+bool DocumentContentOperationsManager::DeleteAndJoinImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ bool bJoinText, bJoinPrev;
+ ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
+
+ bool const bSuccess( DeleteRangeImpl(rPam, flags) );
+ if (!bSuccess)
+ return false;
+
+ if( bJoinText )
+ {
+ ::sw_JoinText( rPam, bJoinPrev );
+ }
+
+ if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline()
+ && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ m_rDoc.getIDocumentRedlineAccess().CompressRedlines();
+ }
+
+ return true;
+}
+
+bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ // Move all cursors out of the deleted range, but first copy the
+ // passed PaM, because it could be a cursor that would be moved!
+ SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
+ {
+ SwPosition const pos(GetCorrPosition(aDelPam));
+ ::PaMCorrAbs(aDelPam, pos);
+ }
+
+ bool const bSuccess( DeleteRangeImplImpl(aDelPam, flags) );
+ if (bSuccess)
+ { // now copy position from temp copy to given PaM
+ *rPam.GetPoint() = *aDelPam.GetPoint();
+ }
+
+ return bSuccess;
+}
+
+bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam, SwDeleteFlags const flags)
+{
+ SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
+
+ if (!rPam.HasMark()
+ || (*pStt == *pEnd && !IsFlySelectedByCursor(m_rDoc, *pStt, *pEnd)))
+ {
+ return false;
+ }
+
+ if( m_rDoc.GetAutoCorrExceptWord() )
+ {
+ // if necessary the saved Word for the exception
+ if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->nNode != pEnd->nNode ||
+ pStt->nContent.GetIndex() + 1 != pEnd->nContent.GetIndex() ||
+ !m_rDoc.GetAutoCorrExceptWord()->CheckDelChar( *pStt ))
+ { m_rDoc.DeleteAutoCorrExceptWord(); }
+ }
+
+ {
+ // Delete all empty TextHints at the Mark's position
+ SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
+ SwpHints* pHts;
+ if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() )
+ {
+ const sal_Int32 nMkCntPos = rPam.GetMark()->nContent.GetIndex();
+ for( size_t n = pHts->Count(); n; )
+ {
+ const SwTextAttr* pAttr = pHts->Get( --n );
+ if( nMkCntPos > pAttr->GetStart() )
+ break;
+
+ const sal_Int32 *pEndIdx;
+ if( nMkCntPos == pAttr->GetStart() &&
+ nullptr != (pEndIdx = pAttr->End()) &&
+ *pEndIdx == pAttr->GetStart() )
+ pTextNd->DestroyAttr( pHts->Cut( n ) );
+ }
+ }
+ }
+
+ {
+ // Send DataChanged before deletion, so that we still know
+ // which objects are in the range.
+ // Afterwards they could be before/after the Position.
+ SwDataChanged aTmp( rPam );
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().ClearRedo();
+ bool bMerged(false);
+ if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo())
+ {
+ SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() );
+ SwUndoDelete *const pUndoDelete(
+ dynamic_cast<SwUndoDelete *>(pLastUndo) );
+ if (pUndoDelete)
+ {
+ bMerged = pUndoDelete->CanGrouping(m_rDoc, rPam);
+ // if CanGrouping() returns true it's already merged
+ }
+ }
+ if (!bMerged)
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(rPam, flags));
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+
+ return true;
+ }
+
+ if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
+
+ // Delete and move all "Flys at the paragraph", which are within the Selection
+ if (!(flags & SwDeleteFlags::ArtificialSelection))
+ {
+ DelFlyInRange(rPam.GetMark()->nNode, rPam.GetPoint()->nNode,
+ &rPam.GetMark()->nContent, &rPam.GetPoint()->nContent);
+ }
+ DelBookmarks(
+ pStt->nNode,
+ pEnd->nNode,
+ nullptr,
+ &pStt->nContent,
+ &pEnd->nContent);
+
+ SwNodeIndex aSttIdx( pStt->nNode );
+ SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode();
+
+ do { // middle checked loop!
+ if( pCNd )
+ {
+ SwTextNode * pStartTextNode( pCNd->GetTextNode() );
+ if ( pStartTextNode )
+ {
+ // now move the Content to the new Node
+ bool bOneNd = pStt->nNode == pEnd->nNode;
+ const sal_Int32 nLen = ( bOneNd ? pEnd->nContent.GetIndex()
+ : pCNd->Len() )
+ - pStt->nContent.GetIndex();
+
+ // Don't call again, if already empty
+ if( nLen )
+ {
+ pStartTextNode->EraseText( pStt->nContent, nLen );
+
+ if( !pStartTextNode->Len() )
+ {
+ // METADATA: remove reference if empty (consider node deleted)
+ pStartTextNode->RemoveMetadataReference();
+ }
+ }
+
+ if( bOneNd ) // that's it
+ break;
+
+ ++aSttIdx;
+ }
+ else
+ {
+ // So that there are no indices left registered when deleted,
+ // we remove a SwPaM from the Content here.
+ pStt->nContent.Assign( nullptr, 0 );
+ }
+ }
+
+ pCNd = pEnd->nNode.GetNode().GetContentNode();
+ if( pCNd )
+ {
+ SwTextNode * pEndTextNode( pCNd->GetTextNode() );
+ if( pEndTextNode )
+ {
+ // if already empty, don't call again
+ if( pEnd->nContent.GetIndex() )
+ {
+ SwIndex aIdx( pCNd, 0 );
+ pEndTextNode->EraseText( aIdx, pEnd->nContent.GetIndex() );
+
+ if( !pEndTextNode->Len() )
+ {
+ // METADATA: remove reference if empty (consider node deleted)
+ pEndTextNode->RemoveMetadataReference();
+ }
+ }
+ }
+ else
+ {
+ // So that there are no indices left registered when deleted,
+ // we remove a SwPaM from the Content here.
+ pEnd->nContent.Assign( nullptr, 0 );
+ }
+ }
+
+ // if the end is not a content node, delete it as well
+ SwNodeOffset nEnd = pEnd->nNode.GetIndex();
+ if( pCNd == nullptr )
+ nEnd++;
+
+ if( aSttIdx != nEnd )
+ {
+ // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete
+ SwNode *pTmpNd;
+ while (pEnd == rPam.GetPoint()
+ && nEnd + SwNodeOffset(2) < m_rDoc.GetNodes().Count()
+ && (pTmpNd = m_rDoc.GetNodes()[nEnd + 1])->IsEndNode()
+ && pTmpNd->StartOfSectionNode()->IsSectionNode()
+ && aSttIdx <= pTmpNd->StartOfSectionNode()->GetIndex())
+ {
+ SwNodeRange range(*pTmpNd->StartOfSectionNode(), *pTmpNd);
+ m_rDoc.GetNodes().SectionUp(&range);
+ --nEnd; // account for deleted start node
+ }
+
+ // delete the Nodes from the NodesArray
+ m_rDoc.GetNodes().Delete( aSttIdx, nEnd - aSttIdx.GetIndex() );
+ }
+
+ // If the Node that contained the Cursor has been deleted,
+ // the Content has to be assigned to the current Content.
+ pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
+ pStt->nContent.GetIndex() );
+
+ // If we deleted across Node boundaries we have to correct the PaM,
+ // because they are in different Nodes now.
+ // Also, the Selection is revoked.
+ *pEnd = *pStt;
+ rPam.DeleteMark();
+
+ } while( false );
+
+ m_rDoc.getIDocumentState().SetModified();
+
+ return true;
+}
+
+// It's possible to call Replace with a PaM that spans 2 paragraphs:
+// search with regex for "$", then replace _all_
+bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr,
+ const bool bRegExReplace )
+{
+ if (!rPam.HasMark())
+ return false;
+
+ bool bJoinText, bJoinPrev;
+ ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev );
+
+ {
+ // Create a copy of the Cursor in order to move all Pams from
+ // the other views out of the deletion range.
+ // Except for itself!
+ SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() );
+ ::PaMCorrAbs( aDelPam, *aDelPam.GetPoint() );
+
+ SwPosition *pStt = aDelPam.Start(),
+ *pEnd = aDelPam.End();
+ bool bOneNode = pStt->nNode == pEnd->nNode;
+
+ // Own Undo?
+ OUString sRepl( rStr );
+ SwTextNode* pTextNd = pStt->nNode.GetNode().GetTextNode();
+ sal_Int32 nStt = pStt->nContent.GetIndex();
+ sal_Int32 nEnd;
+
+ SwDataChanged aTmp( aDelPam );
+
+ if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // this should no longer happen in calls from the UI but maybe via API
+ SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
+ "sw.core", "redlines will be moved in ReplaceRange");
+
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ // If any Redline will change (split!) the node
+ const ::sw::mark::IMark* pBkmk =
+ m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
+ OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
+ ::sw::mark::InsertMode::New);
+
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
+
+ *aDelPam.GetPoint() = pBkmk->GetMarkPos();
+ if(pBkmk->IsExpanded())
+ *aDelPam.GetMark() = pBkmk->GetOtherMarkPos();
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
+ pStt = aDelPam.Start();
+ pTextNd = pStt->nNode.GetNode().GetTextNode();
+ nStt = pStt->nContent.GetIndex();
+ }
+
+ if( !sRepl.isEmpty() )
+ {
+ // Apply the first character's attributes to the ReplaceText
+ SfxItemSetFixed
+ <RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( m_rDoc.GetAttrPool() );
+ pTextNd->GetParaAttr( aSet, nStt+1, nStt+1 );
+
+ aSet.ClearItem( RES_TXTATR_REFMARK );
+ aSet.ClearItem( RES_TXTATR_TOXMARK );
+ aSet.ClearItem( RES_TXTATR_CJK_RUBY );
+ aSet.ClearItem( RES_TXTATR_INETFMT );
+ aSet.ClearItem( RES_TXTATR_META );
+ aSet.ClearItem( RES_TXTATR_METAFIELD );
+
+ if( aDelPam.GetPoint() != aDelPam.End() )
+ aDelPam.Exchange();
+
+ // Remember the End
+ SwNodeIndex aPtNd( aDelPam.GetPoint()->nNode, -1 );
+ const sal_Int32 nPtCnt = aDelPam.GetPoint()->nContent.GetIndex();
+
+ bool bFirst = true;
+ OUString sIns;
+ while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
+ {
+ InsertString( aDelPam, sIns );
+ if( bFirst )
+ {
+ SwNodeIndex aMkNd( aDelPam.GetMark()->nNode, -1 );
+ const sal_Int32 nMkCnt = aDelPam.GetMark()->nContent.GetIndex();
+
+ SplitNode( *aDelPam.GetPoint(), false );
+
+ ++aMkNd;
+ aDelPam.GetMark()->nNode = aMkNd;
+ aDelPam.GetMark()->nContent.Assign(
+ aMkNd.GetNode().GetContentNode(), nMkCnt );
+ bFirst = false;
+ }
+ else
+ SplitNode( *aDelPam.GetPoint(), false );
+ }
+ if( !sIns.isEmpty() )
+ {
+ InsertString( aDelPam, sIns );
+ }
+
+ SwPaM aTmpRange( *aDelPam.GetPoint() );
+ aTmpRange.SetMark();
+
+ ++aPtNd;
+ aDelPam.GetPoint()->nNode = aPtNd;
+ aDelPam.GetPoint()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
+ nPtCnt);
+ *aTmpRange.GetMark() = *aDelPam.GetPoint();
+
+ m_rDoc.RstTextAttrs( aTmpRange );
+ InsertItemSet( aTmpRange, aSet );
+ }
+
+ // tdf#139982: Appending the redline may immediately delete flys
+ // anchored in the previous text if it's inside an insert redline.
+ // Also flys will be deleted if the redline is accepted. Move them
+ // to the position between the previous text and the new text,
+ // there the chance of surviving both accept and reject is best.
+ SaveFlyArr flys;
+ SaveFlyInRange(aDelPam, *aDelPam.End(), flys, false);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRedlineDelete>( aDelPam, SwUndoId::REPLACE ));
+ }
+ // add redline similar to DeleteAndJoinWithRedlineImpl()
+ std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*aDelPam.GetMark()));
+ pCursor->SetMark();
+ *pCursor->GetPoint() = *aDelPam.GetPoint();
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true);
+ RestFlyInRange(flys, *aDelPam.End(), &aDelPam.End()->nNode, true);
+ sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor);
+
+ *rPam.GetMark() = *aDelPam.GetMark();
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ *aDelPam.GetPoint() = *rPam.GetPoint();
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ // If any Redline will change (split!) the node
+ const ::sw::mark::IMark* pBkmk =
+ m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam,
+ OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK,
+ ::sw::mark::InsertMode::New);
+
+ SwIndex& rIdx = aDelPam.GetPoint()->nContent;
+ rIdx.Assign( nullptr, 0 );
+ aDelPam.GetMark()->nContent = rIdx;
+ rPam.GetPoint()->nNode = SwNodeOffset(0);
+ rPam.GetPoint()->nContent = rIdx;
+ *rPam.GetMark() = *rPam.GetPoint();
+ m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+
+ *rPam.GetPoint() = pBkmk->GetMarkPos();
+ *rPam.GetMark() = pBkmk->IsExpanded() ? pBkmk->GetOtherMarkPos() : pBkmk->GetMarkPos();
+
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk);
+ }
+ bJoinText = false;
+ }
+ else
+ {
+ assert((pStt->nNode == pEnd->nNode ||
+ ( pStt->nNode.GetIndex() + 1 == pEnd->nNode.GetIndex() &&
+ !pEnd->nContent.GetIndex() )) &&
+ "invalid range: Point and Mark on different nodes" );
+
+ if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, RedlineType::Any );
+
+ SwUndoReplace* pUndoRpl = nullptr;
+ bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ if (bDoesUndo)
+ {
+ pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace);
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoRpl));
+ }
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+
+ if( aDelPam.GetPoint() != pStt )
+ aDelPam.Exchange();
+
+ SwNodeIndex aPtNd( pStt->nNode, -1 );
+ const sal_Int32 nPtCnt = pStt->nContent.GetIndex();
+
+ // Set the values again, if Frames or footnotes on the Text have been removed.
+ nStt = nPtCnt;
+ nEnd = bOneNode ? pEnd->nContent.GetIndex()
+ : pTextNd->GetText().getLength();
+
+ bool bFirst = true;
+ OUString sIns;
+ while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) )
+ {
+ if (!bFirst || nStt == pTextNd->GetText().getLength())
+ {
+ InsertString( aDelPam, sIns );
+ }
+ else if( nStt < nEnd || !sIns.isEmpty() )
+ {
+ pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
+ }
+ SplitNode( *pStt, false);
+ bFirst = false;
+ }
+
+ if( bFirst || !sIns.isEmpty() )
+ {
+ if (!bFirst || nStt == pTextNd->GetText().getLength())
+ {
+ InsertString( aDelPam, sIns );
+ }
+ else if( nStt < nEnd || !sIns.isEmpty() )
+ {
+ pTextNd->ReplaceText( pStt->nContent, nEnd - nStt, sIns );
+ }
+ }
+
+ *rPam.GetPoint() = *aDelPam.GetMark();
+ ++aPtNd;
+ rPam.GetMark()->nNode = aPtNd;
+ rPam.GetMark()->nContent.Assign( aPtNd.GetNode().GetContentNode(),
+ nPtCnt );
+
+ if (bJoinText)
+ {
+ assert(rPam.GetPoint() == rPam.End());
+ // move so that SetEnd remembers position after sw_JoinText
+ rPam.Move(fnMoveBackward);
+ }
+ else if (aDelPam.GetPoint() == pStt) // backward selection?
+ {
+ assert(*rPam.GetMark() <= *rPam.GetPoint());
+ rPam.Exchange(); // swap so that rPam is backwards
+ }
+
+ if( pUndoRpl )
+ {
+ pUndoRpl->SetEnd(rPam);
+ }
+ }
+ }
+
+ bool bRet(true);
+ if (bJoinText)
+ {
+ bRet = ::sw_JoinText(rPam, bJoinPrev);
+ }
+
+ m_rDoc.getIDocumentState().SetModified();
+ return bRet;
+}
+
+SwFlyFrameFormat* DocumentContentOperationsManager::InsNoTextNode( const SwPosition& rPos, SwNoTextNode* pNode,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet,
+ SwFrameFormat* pFrameFormat)
+{
+ SwFlyFrameFormat *pFormat = nullptr;
+ if( pNode )
+ {
+ pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA,
+ pFlyAttrSet, pFrameFormat );
+ if( pGrfAttrSet )
+ pNode->SetAttr( *pGrfAttrSet );
+ }
+ return pFormat;
+}
+
+#define NUMRULE_STATE \
+ std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \
+ std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \
+
+#define PUSH_NUMRULE_STATE \
+ lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd );
+
+#define POP_NUMRULE_STATE \
+ lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam );
+
+static void lcl_PushNumruleState(
+ std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet,
+ std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet,
+ const SwTextNode *pDestTextNd )
+{
+ // Safe numrule item at destination.
+ // #i86492# - Safe also <ListId> item of destination.
+ const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet();
+ if (pAttrSet == nullptr)
+ return;
+
+ if (const SwNumRuleItem* pItem = pAttrSet->GetItemIfSet(RES_PARATR_NUMRULE, false))
+ {
+ aNumRuleItemHolderIfSet.reset(pItem->Clone());
+ }
+
+ if (const SfxStringItem* pItem = pAttrSet->GetItemIfSet(RES_PARATR_LIST_ID, false))
+ {
+ aListIdItemHolderIfSet.reset(pItem->Clone());
+ }
+}
+
+static void lcl_PopNumruleState(
+ const std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet,
+ const std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet,
+ SwTextNode *pDestTextNd, const SwPaM& rPam )
+{
+ /* If only a part of one paragraph is copied
+ restore the numrule at the destination. */
+ // #i86492# - restore also <ListId> item
+ if ( lcl_MarksWholeNode(rPam) )
+ return;
+
+ if (aNumRuleItemHolderIfSet)
+ {
+ pDestTextNd->SetAttr(*aNumRuleItemHolderIfSet);
+ }
+ else
+ {
+ pDestTextNd->ResetAttr(RES_PARATR_NUMRULE);
+ }
+
+ if (aListIdItemHolderIfSet)
+ {
+ pDestTextNd->SetAttr(*aListIdItemHolderIfSet);
+ }
+ else
+ {
+ pDestTextNd->ResetAttr(RES_PARATR_LIST_ID);
+ }
+}
+
+bool DocumentContentOperationsManager::CopyImpl(SwPaM& rPam, SwPosition& rPos,
+ SwCopyFlags const flags,
+ SwPaM *const pCopyRange) const
+{
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+
+ sw::CalcBreaks(Breaks, rPam, true);
+
+ if (Breaks.empty())
+ {
+ return CopyImplImpl(rPam, rPos, flags, pCopyRange);
+ }
+
+ SwPosition const & rSelectionEnd( *rPam.End() );
+
+ bool bRet(true);
+ bool bFirst(true);
+ // iterate from end to start, ... don't think it's necessary here?
+ auto iter( Breaks.rbegin() );
+ SwNodeOffset nOffset(0);
+ SwNodes const& rNodes(rPam.GetPoint()->nNode.GetNodes());
+ SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node!
+ SwPosition & rEnd( *aPam.End() );
+ SwPosition & rStart( *aPam.Start() );
+ SwPaM copyRange(rPos, rPos);
+
+ while (iter != Breaks.rend())
+ {
+ rStart = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1);
+ if (rStart < rEnd) // check if part is empty
+ {
+ // pass in copyRange member as rPos; should work ...
+ bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
+ nOffset = iter->first - rStart.nNode.GetIndex(); // fly nodes...
+ if (pCopyRange)
+ {
+ if (bFirst)
+ {
+ pCopyRange->SetMark();
+ *pCopyRange->GetMark() = *copyRange.End();
+ }
+ *pCopyRange->GetPoint() = *copyRange.Start();
+ }
+ bFirst = false;
+ }
+ rEnd = SwPosition(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second);
+ ++iter;
+ }
+
+ rStart = *rPam.Start(); // set to original start
+ if (rStart < rEnd) // check if part is empty
+ {
+ bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, &copyRange);
+ if (pCopyRange)
+ {
+ if (bFirst)
+ {
+ pCopyRange->SetMark();
+ *pCopyRange->GetMark() = *copyRange.End();
+ }
+ *pCopyRange->GetPoint() = *copyRange.Start();
+ }
+ }
+
+ return bRet;
+}
+
+bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPos,
+ SwCopyFlags const flags,
+ SwPaM *const pCpyRange) const
+{
+ SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
+ const bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection();
+
+ SwPosition const*const pStt = rPam.Start();
+ SwPosition *const pEnd = rPam.End();
+
+ // Catch when there's no copy to do.
+ if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel) ||
+ //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end
+ //JP 15.11.2001: don't test inclusive the end, ever exclusive
+ ( &rDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd ))
+ {
+ return false;
+ }
+
+ const bool bEndEqualIns = &rDoc == &m_rDoc && rPos == *pEnd;
+
+ // If Undo is enabled, create the UndoCopy object
+ SwUndoCpyDoc* pUndo = nullptr;
+ // lcl_DeleteRedlines may delete the start or end node of the cursor when
+ // removing the redlines so use cursor that is corrected by PaMCorrAbs
+ std::shared_ptr<SwUnoCursor> const pCopyPam(rDoc.CreateUnoCursor(rPos));
+
+ SwTableNumFormatMerge aTNFM( m_rDoc, rDoc );
+ std::optional<std::vector<SwFrameFormat*>> pFlys;
+ std::vector<SwFrameFormat*> const* pFlysAtInsPos;
+
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoCpyDoc(*pCopyPam);
+ pFlysAtInsPos = pUndo->GetFlysAnchoredAt();
+ }
+ else
+ {
+ pFlys = sw::GetFlysAnchoredAt(rDoc, rPos.nNode.GetIndex());
+ pFlysAtInsPos = pFlys ? &*pFlys : nullptr;
+ }
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+
+ // Move the PaM one node back from the insert position, so that
+ // the position doesn't get moved
+ pCopyPam->SetMark();
+ bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent);
+ // If the position was shifted from more than one node, an end node has been skipped
+ bool bAfterTable = false;
+ if ((rPos.nNode.GetIndex() - pCopyPam->GetPoint()->nNode.GetIndex()) > SwNodeOffset(1))
+ {
+ // First go back to the original place
+ pCopyPam->GetPoint()->nNode = rPos.nNode;
+ pCopyPam->GetPoint()->nContent = rPos.nContent;
+
+ bCanMoveBack = false;
+ bAfterTable = true;
+ }
+ if( !bCanMoveBack )
+ {
+ pCopyPam->GetPoint()->nNode--;
+ assert(pCopyPam->GetPoint()->nContent.GetIndex() == 0);
+ }
+
+ SwNodeRange aRg( pStt->nNode, pEnd->nNode );
+ SwNodeIndex aInsPos( rPos.nNode );
+ const bool bOneNode = pStt->nNode == pEnd->nNode;
+ SwTextNode* pSttTextNd = pStt->nNode.GetNode().GetTextNode();
+ SwTextNode* pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
+ SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode();
+ bool bCopyCollFormat = !rDoc.IsInsOnlyTextGlossary() &&
+ ( (pDestTextNd && !pDestTextNd->GetText().getLength()) ||
+ ( !bOneNode && !rPos.nContent.GetIndex() ) );
+ bool bCopyBookmarks = true;
+ bool bCopyPageSource = false;
+ SwNodeOffset nDeleteTextNodes(0);
+
+ // #i104585# copy outline num rule to clipboard (for ASCII filter)
+ if (rDoc.IsClipBoard() && m_rDoc.GetOutlineNumRule())
+ {
+ rDoc.SetOutlineNumRule(*m_rDoc.GetOutlineNumRule());
+ }
+
+ // #i86492#
+ // Correct the search for a previous list:
+ // First search for non-outline numbering list. Then search for non-outline
+ // bullet list.
+ // Keep also the <ListId> value for possible propagation.
+ OUString aListIdToPropagate;
+ const SwNumRule* pNumRuleToPropagate =
+ rDoc.SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true );
+ if ( !pNumRuleToPropagate )
+ {
+ pNumRuleToPropagate =
+ rDoc.SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, nullptr, true );
+ }
+ // #i86492#
+ // Do not propagate previous found list, if
+ // - destination is an empty paragraph which is not in a list and
+ // - source contains at least one paragraph which is not in a list
+ if ( pNumRuleToPropagate &&
+ pDestTextNd && !pDestTextNd->GetText().getLength() &&
+ !pDestTextNd->IsInList() &&
+ !lcl_ContainsOnlyParagraphsInList( rPam ) )
+ {
+ pNumRuleToPropagate = nullptr;
+ }
+
+ // This do/while block is only there so that we can break out of it!
+ do {
+ if( pSttTextNd )
+ {
+ ++nDeleteTextNodes; // must be joined in Undo
+ // Don't copy the beginning completely?
+ if( !bCopyCollFormat || bColumnSel || pStt->nContent.GetIndex() )
+ {
+ SwIndex aDestIdx( rPos.nContent );
+ bool bCopyOk = false;
+ if( !pDestTextNd )
+ {
+ if( pStt->nContent.GetIndex() || bOneNode )
+ pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos,
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
+ else
+ {
+ pDestTextNd = pSttTextNd->MakeCopy(rDoc, aInsPos, true)->GetTextNode();
+ bCopyOk = true;
+ }
+ aDestIdx.Assign( pDestTextNd, 0 );
+ bCopyCollFormat = true;
+ }
+ else if( !bOneNode || bColumnSel )
+ {
+ const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
+ {
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+
+ if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
+ {
+ // after the SplitNode, span the CpyPam correctly again
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ }
+
+ pDestTextNd = rDoc.GetNodes()[ aInsPos.GetIndex()-SwNodeOffset(1) ]->GetTextNode();
+ aDestIdx.Assign(
+ pDestTextNd, pDestTextNd->GetText().getLength());
+
+ // Correct the area again
+ if( bEndEqualIns )
+ {
+ bool bChg = pEnd != rPam.GetPoint();
+ if( bChg )
+ rPam.Exchange();
+ rPam.Move( fnMoveBackward, GoInContent );
+ if( bChg )
+ rPam.Exchange();
+ }
+ else if( rPos == *pEnd )
+ {
+ // The end was also moved
+ pEnd->nNode--;
+ pEnd->nContent.Assign( pDestTextNd, nContentEnd );
+ }
+ // tdf#63022 always reset pEndTextNd after SplitNode
+ aRg.aEnd = pEnd->nNode;
+ pEndTextNd = pEnd->nNode.GetNode().GetTextNode();
+ }
+
+ NUMRULE_STATE
+ if( bCopyCollFormat && bOneNode )
+ {
+ PUSH_NUMRULE_STATE
+ }
+
+ if( !bCopyOk )
+ {
+ const sal_Int32 nCpyLen = ( bOneNode
+ ? pEnd->nContent.GetIndex()
+ : pSttTextNd->GetText().getLength())
+ - pStt->nContent.GetIndex();
+ pSttTextNd->CopyText( pDestTextNd, aDestIdx,
+ pStt->nContent, nCpyLen );
+ if( bEndEqualIns )
+ pEnd->nContent -= nCpyLen;
+ }
+
+ aRg.aStart++;
+
+ if( bOneNode )
+ {
+ if (bCopyCollFormat)
+ {
+ // tdf#138897 no Undo for applying style, SwUndoInserts does it
+ pSttTextNd->CopyCollFormat(*pDestTextNd, false);
+ POP_NUMRULE_STATE
+ }
+
+ // copy at-char flys in rPam
+ SwNodeIndex temp(*pDestTextNd); // update to new (start) node for flys
+ // tdf#126626 prevent duplicate Undos
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ CopyFlyInFlyImpl(aRg, &rPam, temp, false);
+
+ break;
+ }
+ }
+ }
+ else if( pDestTextNd )
+ {
+ // Problems with insertion of table selections into "normal" text solved.
+ // We have to set the correct PaM for Undo, if this PaM starts in a textnode,
+ // the undo operation will try to merge this node after removing the table.
+ // If we didn't split a textnode, the PaM should start at the inserted table node
+ if( rPos.nContent.GetIndex() == pDestTextNd->Len() )
+ { // Insertion at the last position of a textnode (empty or not)
+ ++aInsPos; // The table will be inserted behind the text node
+ }
+ else if( rPos.nContent.GetIndex() )
+ { // Insertion in the middle of a text node, it has to be split
+ // (and joined from undo)
+ ++nDeleteTextNodes;
+
+ const sal_Int32 nContentEnd = pEnd->nContent.GetIndex();
+ {
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+
+ if (bCanMoveBack && rPos == *pCopyPam->GetPoint())
+ {
+ // after the SplitNode, span the CpyPam correctly again
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ pCopyPam->Move( fnMoveBackward, GoInContent );
+ }
+
+ // Correct the area again
+ if( bEndEqualIns )
+ aRg.aEnd--;
+ // The end would also be moved
+ else if( rPos == *pEnd )
+ {
+ rPos.nNode-=SwNodeOffset(2);
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
+ nContentEnd );
+ rPos.nNode++;
+ aRg.aEnd--;
+ }
+ }
+ else if( bCanMoveBack )
+ { // Insertion at the first position of a text node. It will not be split, the table
+ // will be inserted before the text node.
+ // See below, before the SetInsertRange function of the undo object will be called,
+ // the CpyPam would be moved to the next content position. This has to be avoided
+ // We want to be moved to the table node itself thus we have to set bCanMoveBack
+ // and to manipulate pCopyPam.
+ bCanMoveBack = false;
+ pCopyPam->GetPoint()->nNode--;
+ }
+ }
+
+ pDestTextNd = aInsPos.GetNode().GetTextNode();
+ if (pEndTextNd)
+ {
+ SwIndex aDestIdx( rPos.nContent );
+ if( !pDestTextNd )
+ {
+ pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos,
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD));
+ aDestIdx.Assign( pDestTextNd, 0 );
+ aInsPos--;
+
+ // if we have to insert an extra text node
+ // at the destination, this node will be our new destination
+ // (text) node, and thus we increment nDeleteTextNodes. This
+ // will ensure that this node will be deleted during Undo.
+ ++nDeleteTextNodes; // must be deleted
+ }
+
+ const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty();
+
+ NUMRULE_STATE
+ if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
+ {
+ PUSH_NUMRULE_STATE
+ }
+
+ pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwIndex( pEndTextNd ),
+ pEnd->nContent.GetIndex() );
+
+ // Also copy all format templates
+ if( bCopyCollFormat && ( bOneNode || bEmptyDestNd ))
+ {
+ // tdf#138897 no Undo for applying style, SwUndoInserts does it
+ pEndTextNd->CopyCollFormat(*pDestTextNd, false);
+ if ( bOneNode )
+ {
+ POP_NUMRULE_STATE
+ }
+ }
+ }
+
+ SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
+ if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
+ {
+ if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet())
+ {
+ aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() );
+ if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) )
+ pDestTextNd->ResetAttr( RES_BREAK );
+ if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) )
+ pDestTextNd->ResetAttr( RES_PAGEDESC );
+ }
+ }
+
+ {
+ SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
+ SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
+ if (bCanMoveBack)
+ { // pCopyPam is actually 1 before the copy range so move it fwd
+ SwPaM temp(*pCopyPam->GetPoint());
+ temp.Move(fnMoveForward, GoInContent);
+ startPos = *temp.GetPoint();
+ }
+ assert(startPos.nNode.GetNode().IsContentNode());
+ std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos);
+ if( aInsPos == pEnd->nNode )
+ {
+ SwNodeIndex aSaveIdx( aInsPos, -1 );
+ assert(pStt->nNode != pEnd->nNode);
+ pEnd->nContent = 0; // TODO why this?
+ CopyWithFlyInFly(aRg, aInsPos, &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
+ ++aSaveIdx;
+ pEnd->nNode = aSaveIdx;
+ pEnd->nContent.Assign( aSaveIdx.GetNode().GetTextNode(), 0 );
+ }
+ else
+ CopyWithFlyInFly(aRg, aInsPos, &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags);
+
+ bCopyBookmarks = false;
+ }
+
+ // at-char anchors post SplitNode are on index 0 of 2nd node and will
+ // remain there - move them back to the start (end would also work?)
+ // ... also for at-para anchors; here start is preferable because
+ // it's consistent with SplitNode from SwUndoInserts::RedoImpl()
+ if (pFlysAtInsPos)
+ {
+ // init *again* - because CopyWithFlyInFly moved startPos
+ SwPosition startPos(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1),
+ SwIndex(SwNodeIndex(pCopyPam->GetPoint()->nNode, +1).GetNode().GetContentNode()));
+ if (bCanMoveBack)
+ { // pCopyPam is actually 1 before the copy range so move it fwd
+ SwPaM temp(*pCopyPam->GetPoint());
+ temp.Move(fnMoveForward, GoInContent);
+ startPos = *temp.GetPoint();
+ }
+ assert(startPos.nNode.GetNode().IsContentNode());
+ SwPosition startPosAtPara(startPos);
+ startPosAtPara.nContent.Assign(nullptr, 0);
+
+ for (SwFrameFormat * pFly : *pFlysAtInsPos)
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ SwFormatAnchor anchor(*pAnchor);
+ anchor.SetAnchor( &startPos );
+ pFly->SetFormatAttr(anchor);
+ }
+ else if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ SwFormatAnchor anchor(*pAnchor);
+ anchor.SetAnchor( &startPosAtPara );
+ pFly->SetFormatAttr(anchor);
+ }
+ }
+ }
+
+ if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd)
+ {
+ // Put the breaks back into the first node
+ if( aBrkSet.Count() && nullptr != ( pDestTextNd = rDoc.GetNodes()[
+ pCopyPam->GetPoint()->nNode.GetIndex()+1 ]->GetTextNode()))
+ {
+ pDestTextNd->SetAttr( aBrkSet );
+ bCopyPageSource = true;
+ }
+ }
+ } while( false );
+
+
+ // it is not possible to make this test when copy from the clipBoard to document
+ // in this case the PageNum not exist anymore
+ // tdf#39400 and tdf#97526
+ // when copy from document to ClipBoard, and it is from the first page
+ // and not the source has the page break
+ if (rDoc.IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource)
+ {
+ if (pDestTextNd)
+ {
+ pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break
+ pDestTextNd->ResetAttr(RES_PAGEDESC);
+ }
+ }
+
+
+ // Adjust position (in case it was moved / in another node)
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(),
+ rPos.nContent.GetIndex() );
+
+ if( rPos.nNode != aInsPos )
+ {
+ pCopyPam->GetMark()->nNode = aInsPos;
+ if (aInsPos < rPos.nNode)
+ { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above
+ pCopyPam->GetContentNode(false)->MakeEndIndex(&pCopyPam->GetMark()->nContent);
+ }
+ else // incremented in (!pSttTextNd && pDestTextNd) above
+ {
+ pCopyPam->GetMark()->nContent.Assign(pCopyPam->GetContentNode(false), 0);
+ }
+ rPos = *pCopyPam->GetMark();
+ }
+ else
+ *pCopyPam->GetMark() = rPos;
+
+ if ( !bAfterTable )
+ pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode );
+ else
+ {
+ pCopyPam->GetPoint()->nNode++;
+
+ // Reset the offset to 0 as it was before the insertion
+ pCopyPam->GetPoint()->nContent.Assign(pCopyPam->GetPoint()->nNode.GetNode().GetContentNode(), 0);
+ // If the next node is a start node, then step back: the start node
+ // has been copied and needs to be in the selection for the undo
+ if (pCopyPam->GetPoint()->nNode.GetNode().IsStartNode())
+ pCopyPam->GetPoint()->nNode--;
+
+ }
+ pCopyPam->Exchange();
+
+ // Also copy all bookmarks
+ if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() )
+ {
+ sw::CopyBookmarks(rPam, *pCopyPam->Start());
+ }
+
+ if( RedlineFlags::DeleteRedlines & eOld )
+ {
+ assert(*pCopyPam->GetPoint() == rPos);
+ // the Node rPos points to may be deleted so unregister ...
+ rPos.nContent.Assign(nullptr, 0);
+ lcl_DeleteRedlines(rPam, *pCopyPam);
+ rPos = *pCopyPam->GetPoint(); // ... and restore.
+ }
+
+ // If Undo is enabled, store the inserted area
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // append it after styles have been copied when copying nodes
+ rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ pUndo->SetInsertRange(*pCopyPam, true, nDeleteTextNodes);
+ }
+
+ if( pCpyRange )
+ {
+ pCpyRange->SetMark();
+ *pCpyRange->GetPoint() = *pCopyPam->GetPoint();
+ *pCpyRange->GetMark() = *pCopyPam->GetMark();
+ }
+
+ if ( pNumRuleToPropagate != nullptr )
+ {
+ // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId>
+ // Don't reset indent attributes, that would mean loss of direct
+ // formatting.
+ rDoc.SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr,
+ aListIdToPropagate, true, /*bResetIndentAttrs=*/false );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ rDoc.getIDocumentState().SetModified();
+
+ return true;
+}
+
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentDeviceManager.cxx b/sw/source/core/doc/DocumentDeviceManager.cxx
new file mode 100644
index 000000000..0aa41350c
--- /dev/null
+++ b/sw/source/core/doc/DocumentDeviceManager.cxx
@@ -0,0 +1,370 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <DocumentDeviceManager.hxx>
+
+#include <memory>
+#include <utility>
+
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/printer.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/jobset.hxx>
+#include <printdata.hxx>
+#include <vcl/mapmod.hxx>
+#include <svl/itemset.hxx>
+#include <cfgitems.hxx>
+#include <cmdid.h>
+#include <drawdoc.hxx>
+#include <wdocsh.hxx>
+#include <prtopt.hxx>
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <viewopt.hxx>
+#include <swwait.hxx>
+#include <fntcache.hxx>
+
+class SwDocShell;
+class SwWait;
+
+namespace sw {
+
+DocumentDeviceManager::DocumentDeviceManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ), mpPrt(nullptr), mpVirDev(nullptr) {}
+
+SfxPrinter* DocumentDeviceManager::getPrinter(/*[in]*/ bool bCreate ) const
+{
+ SfxPrinter* pRet = nullptr;
+ if ( !bCreate || mpPrt )
+ pRet = mpPrt;
+ else
+ pRet = &CreatePrinter_();
+
+ return pRet;
+}
+
+void DocumentDeviceManager::setPrinter(/*[in]*/ SfxPrinter *pP,/*[in]*/ bool bDeleteOld,/*[in]*/ bool bCallPrtDataChanged )
+{
+ assert ( !pP || !pP->isDisposed() );
+ if ( pP != mpPrt )
+ {
+ if ( bDeleteOld )
+ mpPrt.disposeAndClear();
+ mpPrt = pP;
+
+ // our printer should always use TWIP. Don't rely on this being set in SwViewShell::InitPrt, there
+ // are situations where this isn't called. #i108712#
+ if ( mpPrt )
+ {
+ MapMode aMapMode( mpPrt->GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::MapTwip );
+ mpPrt->SetMapMode( aMapMode );
+ }
+
+ if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() && !m_rDoc.GetDocumentSettingManager().get( DocumentSettingId::USE_VIRTUAL_DEVICE ) )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( mpPrt );
+ }
+
+ if ( bCallPrtDataChanged &&
+ // #i41075# Do not call PrtDataChanged() if we do not
+ // use the printer for formatting:
+ !m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) )
+ PrtDataChanged();
+}
+
+VirtualDevice* DocumentDeviceManager::getVirtualDevice(/*[in]*/ bool bCreate ) const
+{
+ VirtualDevice* pRet = nullptr;
+ if ( !bCreate || mpVirDev )
+ pRet = mpVirDev;
+ else
+ pRet = &CreateVirtualDevice_();
+
+ assert ( !pRet || !pRet->isDisposed() );
+
+ return pRet;
+}
+
+void DocumentDeviceManager::setVirtualDevice(/*[in]*/ VirtualDevice* pVd )
+{
+ assert ( !pVd->isDisposed() );
+
+ if ( mpVirDev.get() != pVd )
+ {
+ mpVirDev.disposeAndClear();
+ mpVirDev = pVd;
+
+ if ( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() && m_rDoc.GetDocumentSettingManager().get( DocumentSettingId::USE_VIRTUAL_DEVICE ) )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( mpVirDev );
+ }
+}
+
+OutputDevice* DocumentDeviceManager::getReferenceDevice(/*[in]*/ bool bCreate ) const
+{
+ OutputDevice* pRet = nullptr;
+ if ( !m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) )
+ {
+ pRet = getPrinter( bCreate );
+
+ if ( bCreate && !mpPrt->IsValid() )
+ {
+ pRet = getVirtualDevice( true );
+ }
+ }
+ else
+ {
+ pRet = getVirtualDevice( bCreate );
+ }
+
+ assert ( !pRet || !pRet->isDisposed() );
+
+ return pRet;
+}
+
+void DocumentDeviceManager::setReferenceDeviceType(/*[in]*/ bool bNewVirtual, /*[in]*/ bool bNewHiRes )
+{
+ if ( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) == bNewVirtual &&
+ m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE) == bNewHiRes )
+ return;
+
+ if ( bNewVirtual )
+ {
+ VirtualDevice* pMyVirDev = getVirtualDevice( true );
+ if ( !bNewHiRes )
+ pMyVirDev->SetReferenceDevice( VirtualDevice::RefDevMode::Dpi600 );
+ else
+ pMyVirDev->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 );
+
+ if( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( pMyVirDev );
+ }
+ else
+ {
+ // #i41075#
+ // We have to take care that a printer exists before calling
+ // PrtDataChanged() in order to prevent that PrtDataChanged()
+ // triggers this funny situation:
+ // getReferenceDevice()->getPrinter()->CreatePrinter_()
+ // ->setPrinter()-> PrtDataChanged()
+ SfxPrinter* pPrinter = getPrinter( true );
+ if( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( pPrinter );
+ }
+
+ m_rDoc.GetDocumentSettingManager().set(DocumentSettingId::USE_VIRTUAL_DEVICE, bNewVirtual );
+ m_rDoc.GetDocumentSettingManager().set(DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE, bNewHiRes );
+ PrtDataChanged();
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+const JobSetup* DocumentDeviceManager::getJobsetup() const
+{
+ return mpPrt ? &mpPrt->GetJobSetup() : nullptr;
+}
+
+void DocumentDeviceManager::setJobsetup(/*[in]*/ const JobSetup &rJobSetup )
+{
+ bool bCheckPageDescs = !mpPrt;
+ bool bDataChanged = false;
+
+ if ( mpPrt )
+ {
+ if ( mpPrt->GetName() == rJobSetup.GetPrinterName() )
+ {
+ if ( mpPrt->GetJobSetup() != rJobSetup )
+ {
+ mpPrt->SetJobSetup( rJobSetup );
+ bDataChanged = true;
+ }
+ }
+ else
+ mpPrt.disposeAndClear();
+ }
+
+ if( !mpPrt )
+ {
+ //The ItemSet is deleted by Sfx!
+ auto pSet = std::make_unique<SfxItemSetFixed<
+ SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
+ SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
+ SID_HTML_MODE, SID_HTML_MODE,
+ FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER>>(m_rDoc.GetAttrPool());
+ VclPtr<SfxPrinter> p = VclPtr<SfxPrinter>::Create( std::move(pSet), rJobSetup );
+ if ( bCheckPageDescs )
+ setPrinter( p, true, true );
+ else
+ {
+ mpPrt = p;
+ bDataChanged = true;
+ }
+ }
+ if ( bDataChanged && !m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::USE_VIRTUAL_DEVICE) )
+ PrtDataChanged();
+}
+
+const SwPrintData & DocumentDeviceManager::getPrintData() const
+{
+ if(!mpPrtData)
+ {
+ DocumentDeviceManager * pThis = const_cast< DocumentDeviceManager * >(this);
+ pThis->mpPrtData.reset(new SwPrintData);
+
+ // SwPrintData should be initialized from the configuration,
+ // the respective config item is implemented by SwPrintOptions which
+ // is also derived from SwPrintData
+ const SwDocShell *pDocSh = m_rDoc.GetDocShell();
+ OSL_ENSURE( pDocSh, "pDocSh is 0, can't determine if this is a WebDoc or not" );
+ bool bWeb = dynamic_cast< const SwWebDocShell * >(pDocSh) != nullptr;
+ SwPrintOptions aPrintOptions( bWeb );
+ *pThis->mpPrtData = aPrintOptions;
+ }
+ assert(mpPrtData && "this will always be set by now");
+ return *mpPrtData;
+}
+
+void DocumentDeviceManager::setPrintData(/*[in]*/ const SwPrintData& rPrtData )
+{
+ if(!mpPrtData)
+ mpPrtData.reset(new SwPrintData);
+ *mpPrtData = rPrtData;
+}
+
+DocumentDeviceManager::~DocumentDeviceManager()
+{
+ mpPrtData.reset();
+ mpVirDev.disposeAndClear();
+ mpPrt.disposeAndClear();
+}
+
+VirtualDevice& DocumentDeviceManager::CreateVirtualDevice_() const
+{
+#ifdef IOS
+ VclPtr<VirtualDevice> pNewVir = VclPtr<VirtualDevice>::Create(DeviceFormat::GRAYSCALE);
+#else
+ VclPtr<VirtualDevice> pNewVir = VclPtr<VirtualDevice>::Create(DeviceFormat::DEFAULT);
+#endif
+
+ pNewVir->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 );
+
+ // #i60945# External leading compatibility for unix systems.
+ if ( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING ) )
+ pNewVir->Compat_ZeroExtleadBug();
+
+ MapMode aMapMode( pNewVir->GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::MapTwip );
+ pNewVir->SetMapMode( aMapMode );
+
+ const_cast<DocumentDeviceManager*>(this)->setVirtualDevice( pNewVir );
+ return *mpVirDev;
+}
+
+SfxPrinter& DocumentDeviceManager::CreatePrinter_() const
+{
+ OSL_ENSURE( ! mpPrt, "Do not call CreatePrinter_(), call getPrinter() instead" );
+
+ // We create a default SfxPrinter.
+ // The ItemSet is deleted by Sfx!
+ auto pSet = std::make_unique<SfxItemSetFixed<
+ SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN,
+ SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC,
+ SID_HTML_MODE, SID_HTML_MODE,
+ FN_PARAM_ADDPRINTER, FN_PARAM_ADDPRINTER>>(m_rDoc.GetAttrPool());
+
+ VclPtr<SfxPrinter> pNewPrt = VclPtr<SfxPrinter>::Create( std::move(pSet) );
+
+ // assign PrintData to newly created printer
+ const SwPrintData& rPrtData = getPrintData();
+ SwAddPrinterItem aAddPrinterItem(rPrtData);
+ SfxItemSet aOptions(pNewPrt->GetOptions());
+ aOptions.Put(aAddPrinterItem);
+ pNewPrt->SetOptions(aOptions);
+
+ const_cast<DocumentDeviceManager*>(this)->setPrinter( pNewPrt, true, true );
+ return *mpPrt;
+}
+
+void DocumentDeviceManager::PrtDataChanged()
+{
+// If you change this, also modify InJobSetup in Sw3io if appropriate.
+
+ // #i41075#
+ OSL_ENSURE( m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::USE_VIRTUAL_DEVICE) ||
+ nullptr != getPrinter( false ), "PrtDataChanged will be called recursively!" );
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ std::unique_ptr<SwWait> pWait;
+ bool bEndAction = false;
+
+ if( m_rDoc.GetDocShell() )
+ m_rDoc.GetDocShell()->UpdateFontList();
+
+ bool bDraw = true;
+ if ( pTmpRoot )
+ {
+ SwViewShell *pSh = m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh &&
+ (!pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsPrtFormat()) )
+ {
+ if ( m_rDoc.GetDocShell() )
+ pWait.reset(new SwWait( *m_rDoc.GetDocShell(), true ));
+
+ pTmpRoot->StartAllAction();
+ bEndAction = true;
+
+ bDraw = false;
+ if( m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetAddExtLeading( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::ADD_EXT_LEADING) );
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( getReferenceDevice( false ) );
+ }
+
+ pFntCache->Flush();
+
+ for(SwRootFrame* aLayout : m_rDoc.GetAllLayouts())
+ aLayout->InvalidateAllContent(SwInvalidateFlags::Size);
+
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ rShell.InitPrt(getPrinter(false));
+ }
+ }
+ if ( bDraw && m_rDoc.getIDocumentDrawModelAccess().GetDrawModel() )
+ {
+ const bool bTmpAddExtLeading = m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::ADD_EXT_LEADING);
+ if ( bTmpAddExtLeading != m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->IsAddExtLeading() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetAddExtLeading( bTmpAddExtLeading );
+
+ OutputDevice* pOutDev = getReferenceDevice( false );
+ if ( pOutDev != m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetRefDevice() )
+ m_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->SetRefDevice( pOutDev );
+ }
+
+ m_rDoc.PrtOLENotify( true );
+
+ if ( bEndAction )
+ pTmpRoot->EndAllAction();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentDrawModelManager.cxx b/sw/source/core/doc/DocumentDrawModelManager.cxx
new file mode 100644
index 000000000..21bde0ebf
--- /dev/null
+++ b/sw/source/core/doc/DocumentDrawModelManager.cxx
@@ -0,0 +1,357 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <DocumentDrawModelManager.hxx>
+
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <swtypes.hxx>
+#include <svl/hint.hxx>
+#include <viewsh.hxx>
+#include <view.hxx>
+#include <drawdoc.hxx>
+#include <rootfrm.hxx>
+#include <fmtanchr.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdview.hxx>
+#include <svl/srchitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+class SdrOutliner;
+
+namespace sw
+{
+
+DocumentDrawModelManager::DocumentDrawModelManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , mnHeaven(0)
+ , mnHell(0)
+ , mnControls(0)
+ , mnInvisibleHeaven(0)
+ , mnInvisibleHell(0)
+ , mnInvisibleControls(0)
+{
+}
+
+// Is also called by the Sw3 Reader, if there was an error when reading the
+// drawing layer. If it is called by the Sw3 Reader the layer is rebuilt
+// from scratch.
+void DocumentDrawModelManager::InitDrawModel()
+{
+ // !! Attention: there is similar code in the Sw3 Reader (sw3imp.cxx) that
+ // also has to be maintained!!
+ if ( mpDrawModel )
+ ReleaseDrawModel();
+
+ // set FontHeight pool defaults without changing static SdrEngineDefaults
+ m_rDoc.GetAttrPool().SetPoolDefaultItem(SvxFontHeightItem( 240, 100, EE_CHAR_FONTHEIGHT ));
+
+ SAL_INFO( "sw.doc", "before create DrawDocument" );
+ // The document owns the SwDrawModel. We always have two layers and one page.
+ mpDrawModel.reset(new SwDrawModel(m_rDoc));
+
+ mpDrawModel->EnableUndo( m_rDoc.GetIDocumentUndoRedo().DoesUndo() );
+
+ OUString sLayerNm;
+ sLayerNm = "Hell";
+ mnHell = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "Heaven";
+ mnHeaven = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "Controls";
+ mnControls = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+ mpDrawModel->GetLayerAdmin().SetControlLayerName(sLayerNm);
+
+ // add invisible layers corresponding to the visible ones.
+ {
+ sLayerNm = "InvisibleHell";
+ mnInvisibleHell = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "InvisibleHeaven";
+ mnInvisibleHeaven = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+
+ sLayerNm = "InvisibleControls";
+ mnInvisibleControls = mpDrawModel->GetLayerAdmin().NewLayer( sLayerNm )->GetID();
+ }
+
+ rtl::Reference<SdrPage> pMasterPage = mpDrawModel->AllocPage( false );
+ mpDrawModel->InsertPage( pMasterPage.get() );
+ SAL_INFO( "sw.doc", "after create DrawDocument" );
+ SdrOutliner& rOutliner = mpDrawModel->GetDrawOutliner();
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ SAL_INFO( "sw.doc", "before create Spellchecker/Hyphenator" );
+ css::uno::Reference< css::linguistic2::XSpellChecker1 > xSpell = ::GetSpellChecker();
+ rOutliner.SetSpeller( xSpell );
+ css::uno::Reference< css::linguistic2::XHyphenator > xHyphenator( ::GetHyphenator() );
+ rOutliner.SetHyphenator( xHyphenator );
+ SAL_INFO( "sw.doc", "after create Spellchecker/Hyphenator" );
+ }
+ m_rDoc.SetCalcFieldValueHdl(&rOutliner);
+ m_rDoc.SetCalcFieldValueHdl(&mpDrawModel->GetHitTestOutliner());
+
+ // Set the LinkManager in the model so that linked graphics can be inserted.
+ // The WinWord import needs it too.
+ mpDrawModel->SetLinkManager( & m_rDoc.getIDocumentLinksAdministration().GetLinkManager() );
+ mpDrawModel->SetAddExtLeading( m_rDoc.getIDocumentSettingAccess().get(DocumentSettingId::ADD_EXT_LEADING) );
+
+ OutputDevice* pRefDev = m_rDoc.getIDocumentDeviceAccess().getReferenceDevice( false );
+ if ( pRefDev )
+ mpDrawModel->SetRefDevice( pRefDev );
+
+ mpDrawModel->SetNotifyUndoActionHdl( std::bind( &SwDoc::AddDrawUndo, &m_rDoc, std::placeholders::_1 ));
+ SwViewShell* const pSh = m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( !pSh )
+ return;
+
+ for(const SwViewShell& rViewSh : pSh->GetRingContainer())
+ {
+ SwRootFrame* pRoot = rViewSh.GetLayout();
+ if( pRoot && !pRoot->GetDrawPage() )
+ {
+ // Disable "multiple layout" for the moment:
+ // use pMasterPage instead of a new created SdrPage
+ // mpDrawModel->AllocPage( FALSE );
+ // mpDrawModel->InsertPage( pDrawPage );
+ SdrPage* pDrawPage = pMasterPage.get();
+ pRoot->SetDrawPage( pDrawPage );
+ pDrawPage->SetSize( pRoot->getFrameArea().SSize() );
+ }
+ }
+}
+
+
+void DocumentDrawModelManager::ReleaseDrawModel()
+{
+ // !! Also maintain the code in the sw3io for inserting documents!!
+ mpDrawModel.reset();
+}
+
+
+const SwDrawModel* DocumentDrawModelManager::GetDrawModel() const
+{
+ return mpDrawModel.get();
+}
+
+SwDrawModel* DocumentDrawModelManager::GetDrawModel()
+{
+ return mpDrawModel.get();
+}
+
+SwDrawModel* DocumentDrawModelManager::MakeDrawModel_()
+{
+ OSL_ENSURE( !mpDrawModel, "MakeDrawModel_: Why?" );
+ InitDrawModel();
+ SwViewShell* const pSh = m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pSh )
+ {
+ for(SwViewShell& rViewSh : pSh->GetRingContainer())
+ rViewSh.MakeDrawView();
+
+ // Broadcast, so that the FormShell can be connected to the DrawView
+ if( m_rDoc.GetDocShell() )
+ {
+ SfxHint aHint( SfxHintId::SwDrawViewsCreated );
+ m_rDoc.GetDocShell()->Broadcast( aHint );
+ }
+ }
+ return mpDrawModel.get();
+}
+
+SwDrawModel* DocumentDrawModelManager::GetOrCreateDrawModel()
+{
+ return GetDrawModel() ? GetDrawModel() : MakeDrawModel_();
+}
+
+SdrLayerID DocumentDrawModelManager::GetHeavenId() const
+{
+ return mnHeaven;
+}
+
+SdrLayerID DocumentDrawModelManager::GetHellId() const
+{
+ return mnHell;
+}
+
+SdrLayerID DocumentDrawModelManager::GetControlsId() const
+{
+ return mnControls;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleHeavenId() const
+{
+ return mnInvisibleHeaven;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleHellId() const
+{
+ return mnInvisibleHell;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleControlsId() const
+{
+ return mnInvisibleControls;
+}
+
+void DocumentDrawModelManager::NotifyInvisibleLayers( SdrPageView& _rSdrPageView )
+{
+ OUString sLayerNm;
+ sLayerNm = "InvisibleHell";
+ _rSdrPageView.SetLayerVisible( sLayerNm, false );
+
+ sLayerNm = "InvisibleHeaven";
+ _rSdrPageView.SetLayerVisible( sLayerNm, false );
+
+ sLayerNm = "InvisibleControls";
+ _rSdrPageView.SetLayerVisible( sLayerNm, false );
+}
+
+bool DocumentDrawModelManager::IsVisibleLayerId( SdrLayerID _nLayerId ) const
+{
+ bool bRetVal;
+
+ if ( _nLayerId == GetHeavenId() ||
+ _nLayerId == GetHellId() ||
+ _nLayerId == GetControlsId() )
+ {
+ bRetVal = true;
+ }
+ else if ( _nLayerId == GetInvisibleHeavenId() ||
+ _nLayerId == GetInvisibleHellId() ||
+ _nLayerId == GetInvisibleControlsId() )
+ {
+ bRetVal = false;
+ }
+ else
+ {
+ OSL_FAIL( "<SwDoc::IsVisibleLayerId(..)> - unknown layer ID." );
+ bRetVal = false;
+ }
+
+ return bRetVal;
+}
+
+SdrLayerID DocumentDrawModelManager::GetInvisibleLayerIdByVisibleOne( SdrLayerID _nVisibleLayerId )
+{
+ SdrLayerID nInvisibleLayerId;
+
+ if ( _nVisibleLayerId == GetHeavenId() )
+ {
+ nInvisibleLayerId = GetInvisibleHeavenId();
+ }
+ else if ( _nVisibleLayerId == GetHellId() )
+ {
+ nInvisibleLayerId = GetInvisibleHellId();
+ }
+ else if ( _nVisibleLayerId == GetControlsId() )
+ {
+ nInvisibleLayerId = GetInvisibleControlsId();
+ }
+ else if ( _nVisibleLayerId == GetInvisibleHeavenId() ||
+ _nVisibleLayerId == GetInvisibleHellId() ||
+ _nVisibleLayerId == GetInvisibleControlsId() )
+ {
+ OSL_FAIL( "<SwDoc::GetInvisibleLayerIdByVisibleOne(..)> - given layer ID already an invisible one." );
+ nInvisibleLayerId = _nVisibleLayerId;
+ }
+ else
+ {
+ OSL_FAIL( "<SwDoc::GetInvisibleLayerIdByVisibleOne(..)> - given layer ID is unknown." );
+ nInvisibleLayerId = _nVisibleLayerId;
+ }
+
+ return nInvisibleLayerId;
+}
+
+bool DocumentDrawModelManager::Search(const SwPaM& rPaM, const SvxSearchItem& rSearchItem)
+{
+ SwPosFlyFrames aFrames = m_rDoc.GetAllFlyFormats(&rPaM, /*bDrawAlso=*/true);
+
+ for (const SwPosFlyFramePtr& pPosFlyFrame : aFrames)
+ {
+ // Filter for at-paragraph anchored draw frames.
+ const SwFrameFormat& rFrameFormat = pPosFlyFrame->GetFormat();
+ const SwFormatAnchor& rAnchor = rFrameFormat.GetAnchor();
+ if (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA || rFrameFormat.Which() != RES_DRAWFRMFMT)
+ continue;
+
+ // Does the shape have matching text?
+ SdrOutliner& rOutliner = GetDrawModel()->GetDrawOutliner();
+ SdrObject* pObject = const_cast<SdrObject*>(rFrameFormat.FindSdrObject());
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObject);
+ if (!pTextObj)
+ continue;
+ const OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
+ if (!pParaObj)
+ continue;
+ rOutliner.SetText(*pParaObj);
+ SwDocShell* pDocShell = m_rDoc.GetDocShell();
+ if (!pDocShell)
+ return false;
+ SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+ if (!pWrtShell)
+ return false;
+ if (!rOutliner.HasText(rSearchItem))
+ continue;
+
+ // If so, then select highlight the search result.
+ pWrtShell->SelectObj(Point(), 0, pObject);
+ SwView* pView = pDocShell->GetView();
+ if (!pView)
+ return false;
+ if (!pView->EnterShapeDrawTextMode(pObject))
+ continue;
+ SdrView* pSdrView = pWrtShell->GetDrawView();
+ if (!pSdrView)
+ return false;
+ OutlinerView* pOutlinerView = pSdrView->GetTextEditOutlinerView();
+ if (!rSearchItem.GetBackward())
+ pOutlinerView->SetSelection(ESelection(0, 0, 0, 0));
+ else
+ pOutlinerView->SetSelection(ESelection(EE_PARA_MAX_COUNT, EE_TEXTPOS_MAX_COUNT, EE_PARA_MAX_COUNT, EE_TEXTPOS_MAX_COUNT));
+ pOutlinerView->StartSearchAndReplace(rSearchItem);
+ return true;
+ }
+
+ return false;
+}
+
+void DocumentDrawModelManager::DrawNotifyUndoHdl()
+{
+ mpDrawModel->SetNotifyUndoActionHdl( nullptr );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentExternalDataManager.cxx b/sw/source/core/doc/DocumentExternalDataManager.cxx
new file mode 100644
index 000000000..3e751a316
--- /dev/null
+++ b/sw/source/core/doc/DocumentExternalDataManager.cxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentExternalDataManager.hxx>
+
+namespace sw
+{
+
+void DocumentExternalDataManager::setExternalData(::sw::tExternalDataType eType, ::sw::tExternalDataPointer pPayload)
+{
+ m_externalData[eType] = pPayload;
+}
+
+::sw::tExternalDataPointer DocumentExternalDataManager::getExternalData(::sw::tExternalDataType eType)
+{
+ return m_externalData[eType];
+}
+
+}
diff --git a/sw/source/core/doc/DocumentFieldsManager.cxx b/sw/source/core/doc/DocumentFieldsManager.cxx
new file mode 100644
index 000000000..bc4920465
--- /dev/null
+++ b/sw/source/core/doc/DocumentFieldsManager.cxx
@@ -0,0 +1,1856 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentFieldsManager.hxx>
+#include <config_features.h>
+#include <config_fuzzers.h>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <rootfrm.hxx>
+#include <dbmgr.hxx>
+#include <chpfld.hxx>
+#include <dbfld.hxx>
+#include <reffld.hxx>
+#include <flddropdown.hxx>
+#include <strings.hrc>
+#include <SwUndoField.hxx>
+#include <flddat.hxx>
+#include <cntfrm.hxx>
+#include <node2lay.hxx>
+#include <section.hxx>
+#include <docufld.hxx>
+#include <calbck.hxx>
+#include <cellatr.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <fmtfld.hxx>
+#include <ndtxt.hxx>
+#include <txtfld.hxx>
+#include <docfld.hxx>
+#include <hints.hxx>
+#include <docary.hxx>
+#include <fldbas.hxx>
+#include <expfld.hxx>
+#include <ddefld.hxx>
+#include <authfld.hxx>
+#include <usrfld.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <o3tl/deleter.hxx>
+#include <osl/diagnose.h>
+#include <unotools/transliterationwrapper.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace sw
+{
+ bool IsFieldDeletedInModel(IDocumentRedlineAccess const& rIDRA,
+ SwTextField const& rTextField)
+ {
+ SwRedlineTable::size_type tmp;
+ SwPosition const pos(rTextField.GetTextNode(),
+ rTextField.GetStart());
+ SwRangeRedline const*const pRedline(rIDRA.GetRedline(pos, &tmp));
+ return (pRedline
+ && pRedline->GetType() == RedlineType::Delete
+ && *pRedline->GetPoint() != *pRedline->GetMark());
+ }
+}
+
+namespace
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+
+ OUString lcl_GetDBVarName( SwDoc& rDoc, SwDBNameInfField& rDBField )
+ {
+ SwDBData aDBData( rDBField.GetDBData( &rDoc ));
+ OUString sDBNumNm;
+ SwDBData aDocData = rDoc.GetDBData();
+
+ if( aDBData != aDocData )
+ {
+ sDBNumNm = aDBData.sDataSource + OUStringChar(DB_DELIM)
+ + aDBData.sCommand + OUStringChar(DB_DELIM);
+ }
+ sDBNumNm += SwFieldType::GetTypeStr(SwFieldTypesEnum::DatabaseSetNumber);
+
+ return sDBNumNm;
+ }
+
+#endif
+
+ bool IsFieldDeleted(IDocumentRedlineAccess const& rIDRA,
+ SwRootFrame const& rLayout, SwTextField const& rTextField)
+ {
+ SwTextNode const& rNode(rTextField.GetTextNode());
+ bool const isInBody(
+ rNode.GetNodes().GetEndOfExtras().GetIndex() < rNode.GetIndex());
+ if (!isInBody && nullptr == rNode.getLayoutFrame(&rLayout))
+ { // see SwDocUpdateField::GetBodyNode() - fields in hidden sections
+ // don't have layout frames but must be updated, so use the same
+ // check as there, but do it again because GetBodyNode() checks
+ // for *any* layout...
+ return true;
+ }
+ return sw::IsFieldDeletedInModel(rIDRA, rTextField);
+ }
+
+ void lcl_CalcField( SwDoc& rDoc, SwCalc& rCalc, const SetGetExpField& rSGEField,
+ SwDBManager* pMgr, SwRootFrame const*const pLayout)
+ {
+ const SwTextField* pTextField = rSGEField.GetTextField();
+ if( !pTextField )
+ return ;
+
+ if (pLayout && pLayout->IsHideRedlines()
+ && IsFieldDeleted(rDoc.getIDocumentRedlineAccess(), *pLayout, *pTextField))
+ {
+ return;
+ }
+
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ const SwFieldIds nFieldWhich = pField->GetTyp()->Which();
+
+ if( SwFieldIds::SetExp == nFieldWhich )
+ {
+ SwSbxValue aValue;
+ if( nsSwGetSetExpType::GSE_EXPR & pField->GetSubType() )
+ aValue.PutDouble( static_cast<const SwSetExpField*>(pField)->GetValue(pLayout) );
+ else
+ // Extension to calculate with Strings
+ aValue.PutString( static_cast<const SwSetExpField*>(pField)->GetExpStr(pLayout) );
+
+ // set the new value in Calculator
+ rCalc.VarChange( pField->GetTyp()->GetName(), aValue );
+ }
+ else if( pMgr )
+ {
+ #if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDoc;
+ #else
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::DbNumSet:
+ {
+ SwDBNumSetField* pDBField = const_cast<SwDBNumSetField*>(static_cast<const SwDBNumSetField*>(pField));
+
+ SwDBData aDBData(pDBField->GetDBData(&rDoc));
+
+ if( pDBField->IsCondValid() &&
+ pMgr->OpenDataSource( aDBData.sDataSource, aDBData.sCommand ))
+ rCalc.VarChange( lcl_GetDBVarName( rDoc, *pDBField),
+ pDBField->GetFormat() );
+ }
+ break;
+ case SwFieldIds::DbNextSet:
+ {
+ SwDBNextSetField* pDBField = const_cast<SwDBNextSetField*>(static_cast<const SwDBNextSetField*>(pField));
+ SwDBData aDBData(pDBField->GetDBData(&rDoc));
+ if( !pDBField->IsCondValid() ||
+ !pMgr->OpenDataSource( aDBData.sDataSource, aDBData.sCommand ))
+ break;
+
+ OUString sDBNumNm(lcl_GetDBVarName( rDoc, *pDBField));
+ SwCalcExp* pExp = rCalc.VarLook( sDBNumNm );
+ if( pExp )
+ rCalc.VarChange( sDBNumNm, pExp->nValue.GetLong() + 1 );
+ }
+ break;
+
+ default: break;
+ }
+ #endif
+ }
+ }
+}
+
+namespace sw
+{
+
+DocumentFieldsManager::DocumentFieldsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ),
+ mbNewFieldLst(true),
+ mpUpdateFields(new SwDocUpdateField(m_rDoc)),
+ mpFieldTypes( new SwFieldTypes ),
+ mnLockExpField( 0 )
+{
+}
+
+const SwFieldTypes* DocumentFieldsManager::GetFieldTypes() const
+{
+ return mpFieldTypes.get();
+}
+
+/** Insert field types
+ *
+ * @param rFieldTyp ???
+ * @return Always returns a pointer to the type, if it's new or already added.
+ */
+SwFieldType* DocumentFieldsManager::InsertFieldType(const SwFieldType &rFieldTyp)
+{
+ const SwFieldTypes::size_type nSize = mpFieldTypes->size();
+ const SwFieldIds nFieldWhich = rFieldTyp.Which();
+
+ SwFieldTypes::size_type i = INIT_FLDTYPES;
+
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::SetExp:
+ //JP 29.01.96: SequenceFields start at INIT_FLDTYPES - 3!!
+ // Or we get doubble number circles!!
+ //MIB 14.03.95: From now on also the SW3-Reader relies on this, when
+ //constructing string pools and when reading SetExp fields
+ if( nsSwGetSetExpType::GSE_SEQ & static_cast<const SwSetExpFieldType&>(rFieldTyp).GetType() )
+ i -= INIT_SEQ_FLDTYPES;
+ [[fallthrough]];
+ case SwFieldIds::Database:
+ case SwFieldIds::User:
+ case SwFieldIds::Dde:
+ {
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ OUString sFieldNm( rFieldTyp.GetName() );
+ for( ; i < nSize; ++i )
+ if( nFieldWhich == (*mpFieldTypes)[i]->Which() &&
+ rSCmp.isEqual( sFieldNm, (*mpFieldTypes)[i]->GetName() ))
+ return (*mpFieldTypes)[i].get();
+ }
+ break;
+
+ case SwFieldIds::TableOfAuthorities:
+ for( ; i < nSize; ++i )
+ if( nFieldWhich == (*mpFieldTypes)[i]->Which() )
+ return (*mpFieldTypes)[i].get();
+ break;
+
+ default:
+ for( i = 0; i < nSize; ++i )
+ if( nFieldWhich == (*mpFieldTypes)[i]->Which() )
+ return (*mpFieldTypes)[i].get();
+ }
+
+ std::unique_ptr<SwFieldType> pNew = rFieldTyp.Copy();
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::Dde:
+ static_cast<SwDDEFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ break;
+
+ case SwFieldIds::Database:
+ case SwFieldIds::Table:
+ case SwFieldIds::DateTime:
+ case SwFieldIds::GetExp:
+ static_cast<SwValueFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ break;
+
+ case SwFieldIds::User:
+ case SwFieldIds::SetExp:
+ static_cast<SwValueFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ // JP 29.07.96: Optionally prepare FieldList for Calculator:
+ mpUpdateFields->InsertFieldType( *pNew );
+ break;
+ case SwFieldIds::TableOfAuthorities :
+ static_cast<SwAuthorityFieldType*>(pNew.get())->SetDoc( &m_rDoc );
+ break;
+ default: break;
+ }
+
+ mpFieldTypes->insert( mpFieldTypes->begin() + nSize, std::move(pNew) );
+ m_rDoc.getIDocumentState().SetModified();
+
+ return (*mpFieldTypes)[ nSize ].get();
+}
+
+/// @returns the field type of the Doc
+SwFieldType *DocumentFieldsManager::GetSysFieldType( const SwFieldIds eWhich ) const
+{
+ for( SwFieldTypes::size_type i = 0; i < INIT_FLDTYPES; ++i )
+ if( eWhich == (*mpFieldTypes)[i]->Which() )
+ return (*mpFieldTypes)[i].get();
+ return nullptr;
+}
+
+/// Find first type with ResId and name
+SwFieldType* DocumentFieldsManager::GetFieldType(
+ SwFieldIds nResId,
+ const OUString& rName,
+ bool bDbFieldMatching // used in some UNO calls for SwFieldIds::Database to use different string matching code #i51815#
+ ) const
+{
+ const SwFieldTypes::size_type nSize = mpFieldTypes->size();
+ SwFieldTypes::size_type i {0};
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+
+ switch( nResId )
+ {
+ case SwFieldIds::SetExp:
+ //JP 29.01.96: SequenceFields start at INIT_FLDTYPES - 3!!
+ // Or we get doubble number circles!!
+ //MIB 14.03.95: From now on also the SW3-Reader relies on this, when
+ //constructing string pools and when reading SetExp fields
+ i = INIT_FLDTYPES - INIT_SEQ_FLDTYPES;
+ break;
+
+ case SwFieldIds::Database:
+ case SwFieldIds::User:
+ case SwFieldIds::Dde:
+ case SwFieldIds::TableOfAuthorities:
+ i = INIT_FLDTYPES;
+ break;
+ default: break;
+ }
+
+ SwFieldType* pRet = nullptr;
+ for( ; i < nSize; ++i )
+ {
+ SwFieldType* pFieldType = (*mpFieldTypes)[i].get();
+
+ if (nResId == pFieldType->Which())
+ {
+ OUString aFieldName( pFieldType->GetName() );
+ if (bDbFieldMatching && nResId == SwFieldIds::Database) // #i51815#
+ aFieldName = aFieldName.replace(DB_DELIM, '.');
+
+ if (rSCmp.isEqual( rName, aFieldName ))
+ {
+ pRet = pFieldType;
+ break;
+ }
+ }
+ }
+ return pRet;
+}
+
+/// Remove field type
+void DocumentFieldsManager::RemoveFieldType(size_t nField)
+{
+ OSL_ENSURE( INIT_FLDTYPES <= nField, "don't remove InitFields" );
+ /*
+ * Dependent fields present -> ErrRaise
+ */
+ if(nField >= mpFieldTypes->size())
+ return;
+
+ SwFieldType* pTmp = (*mpFieldTypes)[nField].get();
+
+ // JP 29.07.96: Optionally prepare FieldList for Calculator
+ SwFieldIds nWhich = pTmp->Which();
+ switch( nWhich )
+ {
+ case SwFieldIds::SetExp:
+ case SwFieldIds::User:
+ mpUpdateFields->RemoveFieldType( *pTmp );
+ [[fallthrough]];
+ case SwFieldIds::Dde:
+ if( pTmp->HasWriterListeners() && !m_rDoc.IsUsed( *pTmp ) )
+ {
+ if( SwFieldIds::SetExp == nWhich )
+ static_cast<SwSetExpFieldType*>(pTmp)->SetDeleted( true );
+ else if( SwFieldIds::User == nWhich )
+ static_cast<SwUserFieldType*>(pTmp)->SetDeleted( true );
+ else
+ static_cast<SwDDEFieldType*>(pTmp)->SetDeleted( true );
+ nWhich = SwFieldIds::Database;
+ }
+ break;
+ default: break;
+ }
+
+ if( nWhich != SwFieldIds::Database )
+ {
+ OSL_ENSURE( !pTmp->HasWriterListeners(), "Dependent fields present!" );
+ }
+ else
+ (*mpFieldTypes)[nField].release(); // DB fields are ref-counted and delete themselves
+
+ mpFieldTypes->erase( mpFieldTypes->begin() + nField );
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+// All have to be re-evaluated.
+void DocumentFieldsManager::UpdateFields(bool bCloseDB)
+{
+ // Tell all types to update their fields
+ for(auto const& pFieldType: *mpFieldTypes)
+ pFieldType->UpdateFields();
+
+ if(!IsExpFieldsLocked())
+ UpdateExpFields(nullptr, false); // update expression fields
+
+ // Tables
+ UpdateTableFields(nullptr);
+
+ // References
+ UpdateRefFields();
+ if(bCloseDB)
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ m_rDoc.GetDBManager()->CloseAll();
+#endif
+ }
+ // Only evaluate on full update
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+void DocumentFieldsManager::InsDeletedFieldType( SwFieldType& rFieldTyp )
+{
+ // The FieldType was marked as deleted and removed from the array.
+ // One has to look this up again, now.
+ // - If it's not present, it can be re-inserted.
+ // - If the same type is found, the deleted one has to be renamed.
+
+ const SwFieldTypes::size_type nSize = mpFieldTypes->size();
+ const SwFieldIds nFieldWhich = rFieldTyp.Which();
+
+ OSL_ENSURE( SwFieldIds::SetExp == nFieldWhich ||
+ SwFieldIds::User == nFieldWhich ||
+ SwFieldIds::Dde == nFieldWhich, "Wrong FieldType" );
+
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ const OUString& rFieldNm = rFieldTyp.GetName();
+
+ for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i )
+ {
+ SwFieldType* pFnd = (*mpFieldTypes)[i].get();
+ if( nFieldWhich == pFnd->Which() &&
+ rSCmp.isEqual( rFieldNm, pFnd->GetName() ) )
+ {
+ // find new name
+ SwFieldTypes::size_type nNum = 1;
+ do {
+ OUString sSrch = rFieldNm + OUString::number( nNum );
+ for( i = INIT_FLDTYPES; i < nSize; ++i )
+ {
+ pFnd = (*mpFieldTypes)[i].get();
+ if( nFieldWhich == pFnd->Which() &&
+ rSCmp.isEqual( sSrch, pFnd->GetName() ) )
+ break;
+ }
+ if( i >= nSize ) // not found
+ {
+ const_cast<OUString&>(rFieldNm) = sSrch;
+ break; // exit while loop
+ }
+ ++nNum;
+ } while( true );
+ break;
+ }
+ }
+
+ // not found, so insert, and updated deleted flag
+ mpFieldTypes->insert( mpFieldTypes->begin() + nSize, std::unique_ptr<SwFieldType>(&rFieldTyp) );
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::SetExp:
+ static_cast<SwSetExpFieldType&>(rFieldTyp).SetDeleted( false );
+ break;
+ case SwFieldIds::User:
+ static_cast<SwUserFieldType&>(rFieldTyp).SetDeleted( false );
+ break;
+ case SwFieldIds::Dde:
+ static_cast<SwDDEFieldType&>(rFieldTyp).SetDeleted( false );
+ break;
+ default: break;
+ }
+}
+
+void DocumentFieldsManager::PutValueToField(const SwPosition & rPos,
+ const Any& rVal, sal_uInt16 nWhich)
+{
+ Any aOldVal;
+ SwField * pField = GetFieldAtPos(rPos);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
+ pField->QueryValue(aOldVal, nWhich))
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFieldFromAPI>(rPos, aOldVal, rVal, nWhich));
+ }
+
+ pField->PutValue(rVal, nWhich);
+}
+
+bool DocumentFieldsManager::UpdateField(SwTextField * pDstTextField, SwField & rSrcField,
+ SwMsgPoolItem * pMsgHint,
+ bool bUpdateFields)
+{
+ OSL_ENSURE(pDstTextField, "no field to update!");
+
+ bool bTableSelBreak = false;
+
+ SwFormatField * pDstFormatField = const_cast<SwFormatField*>(&pDstTextField->GetFormatField());
+ SwField * pDstField = pDstFormatField->GetField();
+ SwFieldIds nFieldWhich = rSrcField.GetTyp()->Which();
+ SwNodeIndex aTableNdIdx(pDstTextField->GetTextNode());
+
+ if (pDstField->GetTyp()->Which() ==
+ rSrcField.GetTyp()->Which())
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwPosition aPosition( pDstTextField->GetTextNode() );
+ aPosition.nContent = pDstTextField->GetStart();
+
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFieldFromDoc>( aPosition, *pDstField, rSrcField, pMsgHint, bUpdateFields) );
+ }
+
+ pDstFormatField->SetField(rSrcField.CopyField());
+ SwField* pNewField = pDstFormatField->GetField();
+
+ switch( nFieldWhich )
+ {
+ case SwFieldIds::SetExp:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::HiddenPara:
+ UpdateExpFields( pDstTextField, true );
+ break;
+
+ case SwFieldIds::Table:
+ {
+ const SwTableNode* pTableNd =
+ m_rDoc.IsIdxInTable(aTableNdIdx);
+ if( pTableNd )
+ {
+ SwTableFormulaUpdate aTableUpdate( &pTableNd->
+ GetTable() );
+ if (bUpdateFields)
+ UpdateTableFields( &aTableUpdate );
+ else
+ pNewField->GetTyp()->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aTableUpdate));
+
+ if (! bUpdateFields)
+ bTableSelBreak = true;
+ }
+ }
+ break;
+
+ case SwFieldIds::Macro:
+ if( bUpdateFields && pDstTextField->GetpTextNode() )
+ pDstTextField->GetpTextNode()->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, pDstFormatField));
+ break;
+
+ case SwFieldIds::DatabaseName:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbSetNumber:
+ m_rDoc.ChgDBData(static_cast<SwDBNameInfField*>( pNewField)->GetRealDBData());
+ pNewField->GetTyp()->UpdateFields();
+
+ break;
+
+ case SwFieldIds::Database:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ // JP 10.02.96: call ChgValue, so that the style change sets the
+ // ContentString correctly
+ SwDBField* pDBField = static_cast<SwDBField*>(pNewField);
+ if (pDBField->IsInitialized())
+ pDBField->ChgValue( pDBField->GetValue(), true );
+
+ pDBField->ClearInitialized();
+ pDBField->InitContent();
+ }
+#endif
+ [[fallthrough]];
+
+ default:
+ pDstFormatField->UpdateTextNode(nullptr, pMsgHint);
+ }
+
+ // The fields we can calculate here are being triggered for an update
+ // here explicitly.
+ if( nFieldWhich == SwFieldIds::User )
+ UpdateUsrFields();
+ }
+
+ return bTableSelBreak;
+}
+
+/// Update reference and table fields
+void DocumentFieldsManager::UpdateRefFields()
+{
+ for(auto const& pFieldType: *mpFieldTypes)
+ if(SwFieldIds::GetRef == pFieldType->Which())
+ static_cast<SwGetRefFieldType*>(pFieldType.get())->UpdateGetReferences();
+}
+
+void DocumentFieldsManager::UpdateTableFields( SfxPoolItem* pHt )
+{
+ OSL_ENSURE( !pHt || RES_TABLEFML_UPDATE == pHt->Which(),
+ "What MessageItem is this?" );
+
+ auto pFieldType = GetFieldType( SwFieldIds::Table, OUString(), false );
+ if(pFieldType)
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ SwTableFormulaUpdate* pUpdateField = nullptr;
+ if( pHt && RES_TABLEFML_UPDATE == pHt->Which() )
+ pUpdateField = static_cast<SwTableFormulaUpdate*>(pHt);
+ for(auto pFormatField : vFields)
+ {
+ SwTableField* pField = static_cast<SwTableField*>(pFormatField->GetField());
+ if( pUpdateField )
+ {
+ // table where this field is located
+ const SwTableNode* pTableNd;
+ const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
+ pTableNd = rTextNd.FindTableNode();
+ if (pTableNd == nullptr)
+ continue;
+
+ switch( pUpdateField->m_eFlags )
+ {
+ case TBL_CALC:
+ // re-set the value flag
+ // JP 17.06.96: internal representation of all formulas
+ // (reference to other table!!!)
+ if( nsSwExtendedSubType::SUB_CMD & pField->GetSubType() )
+ pField->PtrToBoxNm( pUpdateField->m_pTable );
+ else
+ pField->ChgValid( false );
+ break;
+ case TBL_BOXNAME:
+ // is this the wanted table?
+ if( &pTableNd->GetTable() == pUpdateField->m_pTable )
+ // to the external representation
+ pField->PtrToBoxNm( pUpdateField->m_pTable );
+ break;
+ case TBL_BOXPTR:
+ // to the internal representation
+ // JP 17.06.96: internal representation on all formulas
+ // (reference to other table!!!)
+ pField->BoxNmToPtr( &pTableNd->GetTable() );
+ break;
+ case TBL_RELBOXNAME:
+ // is this the wanted table?
+ if( &pTableNd->GetTable() == pUpdateField->m_pTable )
+ // to the relative representation
+ pField->ToRelBoxNm( pUpdateField->m_pTable );
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ // reset the value flag for all
+ pField->ChgValid( false );
+ }
+ }
+ // process all table box formulas
+ for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
+ {
+ auto pBoxFormula = dynamic_cast<const SwTableBoxFormula*>(pItem);
+ if( pBoxFormula && pBoxFormula->GetDefinedIn() )
+ {
+ const_cast<SwTableBoxFormula*>(pBoxFormula)->ChangeState( pHt );
+ }
+ }
+
+ SwRootFrame const* pLayout(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ assert(!pLayout || pLay->IsHideRedlines() == pLayout->IsHideRedlines()); // TODO
+ pLayout = pLay;
+ }
+
+ // all fields/boxes are now invalid, so we can start to calculate
+ if( pHt && ( RES_TABLEFML_UPDATE != pHt->Which() ||
+ TBL_CALC != static_cast<SwTableFormulaUpdate*>(pHt)->m_eFlags ))
+ return ;
+
+ std::unique_ptr<SwCalc, o3tl::default_delete<SwCalc>> pCalc;
+
+ if( pFieldType )
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ for(SwFormatField* pFormatField: vFields)
+ {
+ // start calculation at the end
+ // new fields are inserted at the beginning of the modify chain
+ // that gives faster calculation on import
+ // mba: do we really need this "optimization"? Is it still valid?
+ SwTableField *const pField(static_cast<SwTableField*>(pFormatField->GetField()));
+ if (nsSwExtendedSubType::SUB_CMD & pField->GetSubType())
+ continue;
+
+ // needs to be recalculated
+ if( !pField->IsValid() )
+ {
+ // table where this field is located
+ const SwTextNode& rTextNd = pFormatField->GetTextField()->GetTextNode();
+ const SwTableNode* pTableNd = rTextNd.FindTableNode();
+ if( !pTableNd )
+ continue;
+
+ // if this field is not in the to-be-updated table, skip it
+ if( pHt && &pTableNd->GetTable() !=
+ static_cast<SwTableFormulaUpdate*>(pHt)->m_pTable )
+ continue;
+
+ if( !pCalc )
+ pCalc.reset(new SwCalc( m_rDoc ));
+
+ // get the values of all SetExpression fields that are valid
+ // until the table
+ SwFrame* pFrame = nullptr;
+ if( pTableNd->GetIndex() < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // is in the special section, that's expensive!
+ Point aPt; // return the first frame of the layout - Tab.Headline!!
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = rTextNd.getLayoutFrame(pLayout, nullptr, &tmp);
+ if( pFrame )
+ {
+ SwPosition aPos( *pTableNd );
+ if( GetBodyTextNode( m_rDoc, aPos, *pFrame ) )
+ {
+ FieldsToCalc( *pCalc, SetGetExpField(
+ aPos.nNode, pFormatField->GetTextField(),
+ &aPos.nContent, pFrame->GetPhyPageNum()),
+ pLayout);
+ }
+ else
+ pFrame = nullptr;
+ }
+ }
+ if( !pFrame )
+ {
+ // create index to determine the TextNode
+ SwNodeIndex aIdx( rTextNd );
+ SwFrame const*const pFrame2 = ::sw::FindNeighbourFrameForNode(rTextNd);
+ FieldsToCalc( *pCalc,
+ SetGetExpField(aIdx, pFormatField->GetTextField(),
+ nullptr,
+ pFrame2 ? pFrame2->GetPhyPageNum() : 0),
+ pLayout);
+ }
+
+ SwTableCalcPara aPara(*pCalc, pTableNd->GetTable(), pLayout);
+ pField->CalcField( aPara );
+ if( aPara.IsStackOverflow() )
+ {
+ bool const bResult = aPara.CalcWithStackOverflow();
+ if (bResult)
+ {
+ pField->CalcField( aPara );
+ }
+ OSL_ENSURE(bResult,
+ "the chained formula could no be calculated");
+ }
+ pCalc->SetCalcError( SwCalcError::NONE );
+ }
+ pFormatField->UpdateTextNode(nullptr, pHt);
+ }
+ }
+
+ // calculate the formula at the boxes
+ for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
+ {
+ auto pFormula = const_cast<SwTableBoxFormula*>(dynamic_cast<const SwTableBoxFormula*>(pItem));
+ if( pFormula && pFormula->GetDefinedIn() && !pFormula->IsValid() )
+ {
+ SwTableBox* pBox = pFormula->GetTableBox();
+ if( pBox && pBox->GetSttNd() &&
+ pBox->GetSttNd()->GetNodes().IsDocNodes() )
+ {
+ const SwTableNode* pTableNd = pBox->GetSttNd()->FindTableNode();
+ if( !pHt || &pTableNd->GetTable() ==
+ static_cast<SwTableFormulaUpdate*>(pHt)->m_pTable )
+ {
+ double nValue;
+ if( !pCalc )
+ pCalc.reset(new SwCalc( m_rDoc ));
+
+ // get the values of all SetExpression fields that are valid
+ // until the table
+ SwFrame* pFrame = nullptr;
+ if( pTableNd->GetIndex() < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // is in the special section, that's expensive!
+ SwNodeIndex aCNdIdx( *pTableNd, +2 );
+ SwContentNode* pCNd = aCNdIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = m_rDoc.GetNodes().GoNext( &aCNdIdx );
+
+ if (pCNd)
+ {
+ Point aPt; // return the first frame of the layout - Tab.Headline!!
+ std::pair<Point, bool> const tmp(aPt, true);
+ pFrame = pCNd->getLayoutFrame(pLayout, nullptr, &tmp);
+ if( pFrame )
+ {
+ SwPosition aPos( *pCNd );
+ if( GetBodyTextNode( m_rDoc, aPos, *pFrame ) )
+ {
+ FieldsToCalc(*pCalc, SetGetExpField(aPos.nNode,
+ nullptr, nullptr, pFrame->GetPhyPageNum()),
+ pLayout);
+ }
+ else
+ pFrame = nullptr;
+ }
+ }
+ }
+ if( !pFrame )
+ {
+ // create index to determine the TextNode
+ SwNodeIndex aIdx( *pTableNd );
+ SwFrame const*const pFrame2 = ::sw::FindNeighbourFrameForNode(*pTableNd);
+ FieldsToCalc(*pCalc, SetGetExpField(aIdx, nullptr, nullptr,
+ pFrame2 ? pFrame2->GetPhyPageNum() : 0),
+ pLayout);
+ }
+
+ SwTableCalcPara aPara(*pCalc, pTableNd->GetTable(), pLayout);
+ pFormula->Calc( aPara, nValue );
+
+ if( aPara.IsStackOverflow() )
+ {
+ bool const bResult = aPara.CalcWithStackOverflow();
+ if (bResult)
+ {
+ pFormula->Calc( aPara, nValue );
+ }
+ OSL_ENSURE(bResult,
+ "the chained formula could no be calculated");
+ }
+
+ SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
+ SfxItemSetFixed<RES_BOXATR_BEGIN,RES_BOXATR_END-1> aTmp( m_rDoc.GetAttrPool() );
+
+ if( pCalc->IsCalcError() )
+ nValue = DBL_MAX;
+ aTmp.Put( SwTableBoxValue( nValue ));
+ if( SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_FORMAT ))
+ aTmp.Put( SwTableBoxNumFormat( 0 ));
+ pFormat->SetFormatAttr( aTmp );
+
+ pCalc->SetCalcError( SwCalcError::NONE );
+ }
+ }
+ }
+ }
+}
+
+void DocumentFieldsManager::UpdateExpFields( SwTextField* pUpdateField, bool bUpdRefFields )
+{
+ if( IsExpFieldsLocked() || m_rDoc.IsInReading() )
+ return;
+
+ bool bOldInUpdateFields = mpUpdateFields->IsInUpdateFields();
+ mpUpdateFields->SetInUpdateFields( true );
+
+ mpUpdateFields->MakeFieldList( m_rDoc, true, GETFLD_ALL );
+ mbNewFieldLst = false;
+
+ if (mpUpdateFields->GetSortList()->empty())
+ {
+ if( bUpdRefFields )
+ UpdateRefFields();
+
+ mpUpdateFields->SetInUpdateFields( bOldInUpdateFields );
+ mpUpdateFields->SetFieldsDirty( false );
+ return ;
+ }
+
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame const* pLayoutRLHidden(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ pLayoutRLHidden = pLay;
+ }
+ else
+ {
+ pLayout = pLay;
+ }
+ }
+ if (pLayout || !pLayoutRLHidden) // always calc *something*...
+ {
+ UpdateExpFieldsImpl(pUpdateField, pLayout);
+ }
+ if (pLayoutRLHidden)
+ {
+ UpdateExpFieldsImpl(pUpdateField, pLayoutRLHidden);
+ }
+
+ // update reference fields
+ if( bUpdRefFields )
+ UpdateRefFields();
+
+ mpUpdateFields->SetInUpdateFields( bOldInUpdateFields );
+ mpUpdateFields->SetFieldsDirty( false );
+}
+
+void DocumentFieldsManager::UpdateExpFieldsImpl(
+ SwTextField * pUpdateField, SwRootFrame const*const pLayout)
+{
+ SwFieldIds nWhich;
+
+ // Hash table for all string replacements is filled on-the-fly.
+ // Try to fabricate an uneven number.
+ const SwFieldTypes::size_type nHashSize {(( mpFieldTypes->size() / 7 ) + 1 ) * 7};
+ const sal_uInt16 nStrFormatCnt = o3tl::narrowing<sal_uInt16>(nHashSize);
+ OSL_ENSURE( nStrFormatCnt == nHashSize, "Downcasting to sal_uInt16 lost information!" );
+ SwHashTable<HashStr> aHashStrTable(nStrFormatCnt);
+
+ {
+ const SwFieldType* pFieldType;
+ // process separately:
+ for( auto n = mpFieldTypes->size(); n; )
+ {
+ pFieldType = (*mpFieldTypes)[ --n ].get();
+ switch( pFieldType->Which() )
+ {
+ case SwFieldIds::User:
+ {
+ // Entry present?
+ sal_uInt16 nPos;
+ const OUString& rNm = pFieldType->GetName();
+ OUString sExpand(const_cast<SwUserFieldType*>(static_cast<const SwUserFieldType*>(pFieldType))->Expand(nsSwGetSetExpType::GSE_STRING, 0, LANGUAGE_SYSTEM));
+ SwHash* pFnd = aHashStrTable.Find( rNm, &nPos );
+ if( pFnd )
+ // modify entry in the hash table
+ static_cast<HashStr*>(pFnd)->aSetStr = sExpand;
+ else
+ // insert the new entry
+ aHashStrTable[nPos].reset( new HashStr( rNm, sExpand,
+ aHashStrTable[nPos].release() ) );
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+
+ // The array is filled with all fields; start calculation.
+ SwCalc aCalc( m_rDoc );
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ OUString sDBNumNm( SwFieldType::GetTypeStr( SwFieldTypesEnum::DatabaseSetNumber ) );
+
+ // already set the current record number
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+ pMgr->CloseAll( false );
+
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper* pLclData = &aSysLocale.GetLocaleData();
+ const LanguageType nLang = pLclData->getLanguageTag().getLanguageType();
+ bool bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc );
+#endif
+
+ // Make sure we don't hide all content, which would lead to a crash. First, count how many visible sections we have.
+ int nShownSections = 0;
+ SwNodeOffset nContentStart = m_rDoc.GetNodes().GetEndOfContent().StartOfSectionIndex() + 1;
+ SwNodeOffset nContentEnd = m_rDoc.GetNodes().GetEndOfContent().GetIndex();
+ SwSectionFormats& rSectFormats = m_rDoc.GetSections();
+ for( SwSectionFormats::size_type n = 0; n<rSectFormats.size(); ++n )
+ {
+ SwSectionFormat& rSectFormat = *rSectFormats[ n ];
+ SwSectionNode* pSectionNode = rSectFormat.GetSectionNode();
+ SwSection* pSect = rSectFormat.GetSection();
+
+ // Usually some of the content is not in a section: count that as a virtual section, so that all real sections can be hidden.
+ // Only look for section gaps at the lowest level, ignoring sub-sections.
+ if ( pSectionNode && !rSectFormat.GetParent() )
+ {
+ SwNodeIndex aNextIdx( *pSectionNode->EndOfSectionNode(), 1 );
+ if ( n == 0 && pSectionNode->GetIndex() != nContentStart )
+ nShownSections++; //document does not start with a section
+ if ( n == rSectFormats.size() - 1 )
+ {
+ if ( aNextIdx.GetIndex() != nContentEnd )
+ nShownSections++; //document does not end in a section
+ }
+ else if ( !aNextIdx.GetNode().IsSectionNode() )
+ nShownSections++; //section is not immediately followed by another section
+ }
+
+ // count only visible sections
+ if ( pSect && !pSect->CalcHiddenFlag())
+ nShownSections++;
+ }
+
+ IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess());
+ std::unordered_map<SwSetExpFieldType const*, SwTextNode const*> SetExpOutlineNodeMap;
+
+ for (std::unique_ptr<SetGetExpField> const& it : *mpUpdateFields->GetSortList())
+ {
+ SwSection* pSect = const_cast<SwSection*>(it->GetSection());
+ if( pSect )
+ {
+ SwSbxValue aValue = aCalc.Calculate(
+ pSect->GetCondition() );
+ if(!aValue.IsVoidValue())
+ {
+ // Do we want to hide this one?
+ bool bHide = aValue.GetBool();
+ if (bHide && !pSect->IsCondHidden())
+ {
+ // This section will be hidden, but it wasn't before
+ if (nShownSections == 1)
+ {
+ // This would be the last section, so set its condition to false, and avoid hiding it.
+ pSect->SetCondition("0");
+ bHide = false;
+ }
+ nShownSections--;
+ }
+ pSect->SetCondHidden( bHide );
+ }
+ continue;
+ }
+ ::sw::mark::IBookmark *const pBookmark(
+ const_cast<::sw::mark::IBookmark *>(it->GetBookmark()));
+ if (pBookmark)
+ {
+ SwSbxValue const aValue(aCalc.Calculate(pBookmark->GetHideCondition()));
+ if (!aValue.IsVoidValue())
+ {
+ pBookmark->Hide(aValue.GetBool());
+ }
+ continue;
+ }
+
+ SwTextField* pTextField = const_cast<SwTextField*>(it->GetTextField());
+ if( !pTextField )
+ {
+ OSL_ENSURE( false, "what's wrong now'" );
+ continue;
+ }
+
+ if (pLayout && pLayout->IsHideRedlines()
+ && IsFieldDeleted(rIDRA, *pLayout, *pTextField))
+ {
+ continue;
+ }
+
+ SwFormatField* pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+ const SwField* pField = pFormatField->GetField();
+
+ nWhich = pField->GetTyp()->Which();
+ switch( nWhich )
+ {
+ case SwFieldIds::HiddenText:
+ {
+ SwHiddenTextField* pHField = const_cast<SwHiddenTextField*>(static_cast<const SwHiddenTextField*>(pField));
+ SwSbxValue aValue = aCalc.Calculate( pHField->GetPar1() );
+ bool bValue = !aValue.GetBool();
+ if(!aValue.IsVoidValue())
+ {
+ pHField->SetValue( bValue );
+ // evaluate field
+ pHField->Evaluate(m_rDoc);
+ }
+ }
+ break;
+ case SwFieldIds::HiddenPara:
+ {
+ SwHiddenParaField* pHPField = const_cast<SwHiddenParaField*>(static_cast<const SwHiddenParaField*>(pField));
+ SwSbxValue aValue = aCalc.Calculate( pHPField->GetPar1() );
+ bool bValue = aValue.GetBool();
+ if(!aValue.IsVoidValue())
+ pHPField->SetHidden( bValue );
+ }
+ break;
+ case SwFieldIds::DbSetNumber:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ const_cast<SwDBSetNumberField*>(static_cast<const SwDBSetNumberField*>(pField))->Evaluate(m_rDoc);
+ aCalc.VarChange( sDBNumNm, static_cast<const SwDBSetNumberField*>(pField)->GetSetNumber());
+ pField->ExpandField(m_rDoc.IsClipBoard(), nullptr);
+ }
+#endif
+ break;
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ {
+ UpdateDBNumFields( *const_cast<SwDBNameInfField*>(static_cast<const SwDBNameInfField*>(pField)), aCalc );
+ if( bCanFill )
+ bCanFill = pMgr->FillCalcWithMergeData( m_rDoc.GetNumberFormatter(), nLang, aCalc );
+ }
+#endif
+ break;
+ case SwFieldIds::Database:
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ // evaluate field
+ const_cast<SwDBField*>(static_cast<const SwDBField*>(pField))->Evaluate();
+
+ SwDBData aTmpDBData(static_cast<const SwDBField*>(pField)->GetDBData());
+
+ if( pMgr->IsDataSourceOpen(aTmpDBData.sDataSource, aTmpDBData.sCommand, false))
+ aCalc.VarChange( sDBNumNm, pMgr->GetSelectedRecordId(aTmpDBData.sDataSource, aTmpDBData.sCommand, aTmpDBData.nCommandType));
+
+ const OUString& rName = pField->GetTyp()->GetName();
+
+ // Add entry to hash table
+ // Entry present?
+ sal_uInt16 nPos;
+ HashStr* pFnd = aHashStrTable.Find( rName, &nPos );
+ OUString const value(pField->ExpandField(m_rDoc.IsClipBoard(), nullptr));
+ if( pFnd )
+ {
+ // Modify entry in the hash table
+ pFnd->aSetStr = value;
+ }
+ else
+ {
+ // insert new entry
+ aHashStrTable[nPos].reset( new HashStr( rName,
+ value, aHashStrTable[nPos].release()) );
+ }
+#endif
+ }
+ break;
+ case SwFieldIds::GetExp:
+ case SwFieldIds::SetExp:
+ {
+ if( nsSwGetSetExpType::GSE_STRING & pField->GetSubType() ) // replace String
+ {
+ if( SwFieldIds::GetExp == nWhich )
+ {
+ SwGetExpField* pGField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(pField));
+
+ if( (!pUpdateField || pUpdateField == pTextField )
+ && pGField->IsInBodyText() )
+ {
+ OUString aNew = LookString( aHashStrTable, pGField->GetFormula() );
+ pGField->ChgExpStr( aNew, pLayout );
+ }
+ }
+ else
+ {
+ SwSetExpField* pSField = const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField));
+ // is the "formula" a field?
+ OUString aNew = LookString( aHashStrTable, pSField->GetFormula() );
+
+ if( aNew.isEmpty() ) // nothing found then the formula is the new value
+ aNew = pSField->GetFormula();
+
+ // only update one field
+ if( !pUpdateField || pUpdateField == pTextField )
+ pSField->ChgExpStr( aNew, pLayout );
+
+ // lookup the field's name
+ aNew = static_cast<SwSetExpFieldType*>(pSField->GetTyp())->GetSetRefName();
+ // Entry present?
+ sal_uInt16 nPos;
+ HashStr* pFnd = aHashStrTable.Find( aNew, &nPos );
+ if( pFnd )
+ // Modify entry in the hash table
+ pFnd->aSetStr = pSField->GetExpStr(pLayout);
+ else
+ {
+ // insert new entry
+ aHashStrTable[nPos].reset( new HashStr( aNew,
+ pSField->GetExpStr(pLayout),
+ aHashStrTable[nPos].release() ) );
+ pFnd = aHashStrTable[nPos].get();
+ }
+
+ // Extension for calculation with Strings
+ SwSbxValue aValue;
+ aValue.PutString( pFnd->aSetStr );
+ aCalc.VarChange( aNew, aValue );
+ }
+ }
+ else // recalculate formula
+ {
+ if( SwFieldIds::GetExp == nWhich )
+ {
+ SwGetExpField* pGField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(pField));
+
+ if( (!pUpdateField || pUpdateField == pTextField )
+ && pGField->IsInBodyText() )
+ {
+ SwSbxValue aValue = aCalc.Calculate(
+ pGField->GetFormula());
+ if(!aValue.IsVoidValue())
+ pGField->SetValue(aValue.GetDouble(), pLayout);
+ }
+ }
+ else
+ {
+ SwSetExpField* pSField = const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField));
+ SwSetExpFieldType* pSFieldTyp = static_cast<SwSetExpFieldType*>(pField->GetTyp());
+ OUString aNew = pSFieldTyp->GetName();
+
+ SwNode* pSeqNd = nullptr;
+
+ if( pSField->IsSequenceField() )
+ {
+ const sal_uInt8 nLvl = pSFieldTyp->GetOutlineLvl();
+ if( MAXLEVEL > nLvl )
+ {
+ // test if the Number needs to be updated
+ pSeqNd = m_rDoc.GetNodes()[ it->GetNode() ];
+
+ const SwTextNode* pOutlNd = pSeqNd->
+ FindOutlineNodeOfLevel(nLvl, pLayout);
+ auto const iter(SetExpOutlineNodeMap.find(pSFieldTyp));
+ if (iter == SetExpOutlineNodeMap.end()
+ || iter->second != pOutlNd)
+ {
+ SetExpOutlineNodeMap[pSFieldTyp] = pOutlNd;
+ aCalc.VarChange( aNew, 0 );
+ }
+ }
+ }
+
+ aNew += "=" + pSField->GetFormula();
+
+ SwSbxValue aValue = aCalc.Calculate( aNew );
+ if (!aCalc.IsCalcError())
+ {
+ double nErg = aValue.GetDouble();
+ // only update one field
+ if( !aValue.IsVoidValue() && (!pUpdateField || pUpdateField == pTextField) )
+ {
+ pSField->SetValue(nErg, pLayout);
+
+ if( pSeqNd )
+ pSFieldTyp->SetChapter(*pSField, *pSeqNd, pLayout);
+ }
+ }
+ }
+ }
+ }
+ break;
+ default: break;
+ } // switch
+
+ {
+ // 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)
+ {
+ bool const tmp = pInputField->LockNotifyContentChange();
+ (void) tmp;
+ assert(tmp && "should not be locked here?");
+ }
+ ::comphelper::ScopeGuard g([pInputField]()
+ {
+ if (pInputField)
+ {
+ pInputField->UnlockNotifyContentChange();
+ }
+ });
+ pFormatField->UpdateTextNode(nullptr, nullptr); // trigger formatting
+ }
+
+ if (pUpdateField == pTextField) // if only this one is updated
+ {
+ if( SwFieldIds::GetExp == nWhich || // only GetField or
+ SwFieldIds::HiddenText == nWhich || // HiddenText?
+ SwFieldIds::HiddenPara == nWhich) // HiddenParaField?
+ break; // quit
+ pUpdateField = nullptr; // update all from here on
+ }
+ }
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ pMgr->CloseAll(false);
+#endif
+}
+
+/// Insert field type that was marked as deleted
+void DocumentFieldsManager::UpdateUsrFields()
+{
+ SwCalc* pCalc = nullptr;
+ for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < mpFieldTypes->size(); ++i )
+ {
+ const SwFieldType* pFieldType = (*mpFieldTypes)[i].get();
+ if( SwFieldIds::User == pFieldType->Which() )
+ {
+ if( !pCalc )
+ pCalc = new SwCalc( m_rDoc );
+ const_cast<SwUserFieldType*>(static_cast<const SwUserFieldType*>(pFieldType))->GetValue( *pCalc );
+ }
+ }
+
+ if( pCalc )
+ {
+ delete pCalc;
+ m_rDoc.getIDocumentState().SetModified();
+ }
+}
+
+sal_Int32 DocumentFieldsManager::GetRecordsPerDocument() const
+{
+ sal_Int32 nRecords = 1;
+
+ mpUpdateFields->MakeFieldList( m_rDoc, true, GETFLD_ALL );
+ if (mpUpdateFields->GetSortList()->empty())
+ return nRecords;
+
+ for (std::unique_ptr<SetGetExpField> const& it : *mpUpdateFields->GetSortList())
+ {
+ const SwTextField *pTextField = it->GetTextField();
+ if( !pTextField )
+ continue;
+
+ const SwFormatField &pFormatField = pTextField->GetFormatField();
+ const SwField* pField = pFormatField.GetField();
+
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ nRecords++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return nRecords;
+}
+
+void DocumentFieldsManager::UpdatePageFields( SfxPoolItem* pMsgHint )
+{
+ for( SwFieldTypes::size_type i = 0; i < INIT_FLDTYPES; ++i )
+ {
+ SwFieldType* pFieldType = (*mpFieldTypes)[ i ].get();
+ switch( pFieldType->Which() )
+ {
+ case SwFieldIds::PageNumber:
+ case SwFieldIds::Chapter:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::RefPageGet:
+ pFieldType->CallSwClientNotify(sw::LegacyModifyHint(nullptr, pMsgHint));
+ break;
+ case SwFieldIds::DocStat:
+ pFieldType->CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+ break;
+ default: break;
+ }
+ }
+ SetNewFieldLst(true);
+}
+
+void DocumentFieldsManager::LockExpFields()
+{
+ ++mnLockExpField;
+}
+
+void DocumentFieldsManager::UnlockExpFields()
+{
+ assert(mnLockExpField != 0);
+ if( mnLockExpField )
+ --mnLockExpField;
+}
+
+bool DocumentFieldsManager::IsExpFieldsLocked() const
+{
+ return 0 != mnLockExpField;
+}
+
+SwDocUpdateField& DocumentFieldsManager::GetUpdateFields() const
+{
+ return *mpUpdateFields;
+}
+
+bool DocumentFieldsManager::SetFieldsDirty( bool b, const SwNode* pChk, SwNodeOffset nLen )
+{
+ // See if the supplied nodes actually contain fields.
+ // If they don't, the flag doesn't need to be changed.
+ bool bFieldsFnd = false;
+ if( b && pChk && !GetUpdateFields().IsFieldsDirty() && !m_rDoc.IsInDtor()
+ // ?? what's up with Undo, this is also wanted there!
+ /*&& &pChk->GetNodes() == &GetNodes()*/ )
+ {
+ b = false;
+ if( !nLen )
+ ++nLen;
+ SwNodeOffset nStt = pChk->GetIndex();
+ const SwNodes& rNds = pChk->GetNodes();
+ while( nLen-- )
+ {
+ const SwTextNode* pTNd = rNds[ nStt++ ]->GetTextNode();
+ if( pTNd )
+ {
+ if( pTNd->GetAttrOutlineLevel() != 0 )
+ // update chapter fields
+ b = true;
+ else if( pTNd->GetpSwpHints() && pTNd->GetSwpHints().Count() )
+ {
+ const size_t nEnd = pTNd->GetSwpHints().Count();
+ for( size_t n = 0 ; n < nEnd; ++n )
+ {
+ const SwTextAttr* pAttr = pTNd->GetSwpHints().Get(n);
+ if ( pAttr->Which() == RES_TXTATR_FIELD
+ || pAttr->Which() == RES_TXTATR_INPUTFIELD)
+ {
+ b = true;
+ break;
+ }
+ }
+ }
+
+ if( b )
+ break;
+ }
+ }
+ bFieldsFnd = b;
+ }
+ GetUpdateFields().SetFieldsDirty( b );
+ return bFieldsFnd;
+}
+
+void DocumentFieldsManager::SetFixFields( const DateTime* pNewDateTime )
+{
+ bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ sal_Int32 nDate;
+ sal_Int64 nTime;
+ if( pNewDateTime )
+ {
+ nDate = pNewDateTime->GetDate();
+ nTime = pNewDateTime->GetTime();
+ }
+ else
+ {
+ DateTime aDateTime( DateTime::SYSTEM );
+ nDate = aDateTime.GetDate();
+ nTime = aDateTime.GetTime();
+ }
+
+ SwFieldIds const aTypes[] {
+ /*0*/ SwFieldIds::DocInfo,
+ /*1*/ SwFieldIds::Author,
+ /*2*/ SwFieldIds::ExtUser,
+ /*3*/ SwFieldIds::Filename,
+ /*4*/ SwFieldIds::DateTime }; // MUST be at the end!
+
+ for(SwFieldIds aType : aTypes)
+ {
+ std::vector<SwFormatField*> vFields;
+ GetSysFieldType(aType)->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ if (pFormatField->GetTextField())
+ {
+ bool bChgd = false;
+ switch( aType )
+ {
+ case SwFieldIds::DocInfo:
+ if( static_cast<SwDocInfoField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwDocInfoField* pDocInfField = static_cast<SwDocInfoField*>(pFormatField->GetField());
+ pDocInfField->SetExpansion( static_cast<SwDocInfoFieldType*>(
+ pDocInfField->GetTyp())->Expand(
+ pDocInfField->GetSubType(),
+ pDocInfField->GetFormat(),
+ pDocInfField->GetLanguage(),
+ pDocInfField->GetName() ) );
+ }
+ break;
+
+ case SwFieldIds::Author:
+ if( static_cast<SwAuthorField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwAuthorField* pAuthorField = static_cast<SwAuthorField*>(pFormatField->GetField());
+ pAuthorField->SetExpansion( SwAuthorFieldType::Expand( pAuthorField->GetFormat() ) );
+ }
+ break;
+
+ case SwFieldIds::ExtUser:
+ if( static_cast<SwExtUserField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwExtUserField* pExtUserField = static_cast<SwExtUserField*>(pFormatField->GetField());
+ pExtUserField->SetExpansion( SwExtUserFieldType::Expand(pExtUserField->GetSubType()) );
+ }
+ break;
+
+ case SwFieldIds::DateTime:
+ if( static_cast<SwDateTimeField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ static_cast<SwDateTimeField*>(pFormatField->GetField())->SetDateTime(
+ DateTime(Date(nDate), tools::Time(nTime)) );
+ }
+ break;
+
+ case SwFieldIds::Filename:
+ if( static_cast<SwFileNameField*>(pFormatField->GetField())->IsFixed() )
+ {
+ bChgd = true;
+ SwFileNameField* pFileNameField =
+ static_cast<SwFileNameField*>(pFormatField->GetField());
+ pFileNameField->SetExpansion( static_cast<SwFileNameFieldType*>(
+ pFileNameField->GetTyp())->Expand(
+ pFileNameField->GetFormat() ) );
+ }
+ break;
+ default: break;
+ }
+
+ // Trigger formatting
+ if( bChgd )
+ pFormatField->UpdateTextNode(nullptr, nullptr);
+ }
+ }
+ }
+
+ if( !bIsModified )
+ m_rDoc.getIDocumentState().ResetModified();
+}
+
+void DocumentFieldsManager::FieldsToCalc(SwCalc& rCalc,
+ const SetGetExpField& rToThisField, SwRootFrame const*const pLayout)
+{
+ // create the sorted list of all SetFields
+ mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC );
+ mbNewFieldLst = false;
+
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ SwDBManager* pMgr = NULL;
+#else
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+ pMgr->CloseAll(false);
+#endif
+
+ if (!mpUpdateFields->GetSortList()->empty())
+ {
+ SetGetExpFields::const_iterator const itLast =
+ mpUpdateFields->GetSortList()->upper_bound(
+ &rToThisField);
+ for (auto it = mpUpdateFields->GetSortList()->begin(); it != itLast; ++it)
+ {
+ lcl_CalcField(m_rDoc, rCalc, **it, pMgr, pLayout);
+ }
+ }
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ pMgr->CloseAll(false);
+#endif
+}
+
+void DocumentFieldsManager::FieldsToCalc(SwCalc& rCalc,
+ SwNodeOffset const nLastNd, sal_Int32 const nLastCnt)
+{
+ // create the sorted list of all SetFields
+ mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_CALC );
+ mbNewFieldLst = false;
+
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ SwDBManager* pMgr = NULL;
+#else
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+ pMgr->CloseAll(false);
+#endif
+
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame const* pLayoutRLHidden(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ pLayoutRLHidden = pLay;
+ }
+ else
+ {
+ pLayout = pLay;
+ }
+ }
+
+ // note this is not duplicate of the other FieldsToCalc because there is
+ // (currently) no SetGetExpField that compares only a position
+ for(auto it = mpUpdateFields->GetSortList()->begin();
+ it != mpUpdateFields->GetSortList()->end() &&
+ ( (*it)->GetNode() < nLastNd ||
+ ( (*it)->GetNode() == nLastNd && (*it)->GetContent() <= nLastCnt )
+ );
+ ++it )
+ {
+ if (pLayout || !pLayoutRLHidden) // always calc *something*...
+ {
+ lcl_CalcField( m_rDoc, rCalc, **it, pMgr, pLayout );
+ }
+ if (pLayoutRLHidden)
+ {
+ lcl_CalcField( m_rDoc, rCalc, **it, pMgr, pLayoutRLHidden );
+ }
+ }
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ pMgr->CloseAll(false);
+#endif
+}
+
+void DocumentFieldsManager::FieldsToExpand( SwHashTable<HashStr> & rHashTable,
+ const SetGetExpField& rToThisField, SwRootFrame const& rLayout)
+{
+ // create the sorted list of all SetFields
+ mpUpdateFields->MakeFieldList( m_rDoc, mbNewFieldLst, GETFLD_EXPAND );
+ mbNewFieldLst = false;
+
+ IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess());
+
+ // Hash table for all string replacements is filled on-the-fly.
+ // Try to fabricate an uneven number.
+ sal_uInt16 nTableSize = ((mpUpdateFields->GetSortList()->size() / 7) + 1) * 7;
+ rHashTable.resize(nTableSize);
+
+ SetGetExpFields::const_iterator const itLast =
+ mpUpdateFields->GetSortList()->upper_bound(&rToThisField);
+
+ for (auto it = mpUpdateFields->GetSortList()->begin(); it != itLast; ++it)
+ {
+ const SwTextField* pTextField = (*it)->GetTextField();
+ if( !pTextField )
+ continue;
+
+ if (rLayout.IsHideRedlines()
+ && IsFieldDeleted(rIDRA, rLayout, *pTextField))
+ {
+ continue;
+ }
+
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::SetExp:
+ if( nsSwGetSetExpType::GSE_STRING & pField->GetSubType() )
+ {
+ // set the new value in the hash table
+ // is the formula a field?
+ SwSetExpField* pSField = const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField));
+ OUString aNew = LookString( rHashTable, pSField->GetFormula() );
+
+ if( aNew.isEmpty() ) // nothing found, then the formula is
+ aNew = pSField->GetFormula(); // the new value
+
+ // #i3141# - update expression of field as in method
+ // <SwDoc::UpdateExpFields(..)> for string/text fields
+ pSField->ChgExpStr(aNew, &rLayout);
+
+ // look up the field's name
+ aNew = static_cast<SwSetExpFieldType*>(pSField->GetTyp())->GetSetRefName();
+ // Entry present?
+ sal_uInt16 nPos;
+ SwHash* pFnd = rHashTable.Find( aNew, &nPos );
+ if( pFnd )
+ // modify entry in the hash table
+ static_cast<HashStr*>(pFnd)->aSetStr = pSField->GetExpStr(&rLayout);
+ else
+ // insert the new entry
+ rHashTable[nPos].reset( new HashStr( aNew,
+ pSField->GetExpStr(&rLayout), rHashTable[nPos].release()));
+ }
+ break;
+ case SwFieldIds::Database:
+ {
+ const OUString& rName = pField->GetTyp()->GetName();
+
+ // Insert entry in the hash table
+ // Entry present?
+ sal_uInt16 nPos;
+ HashStr* pFnd = rHashTable.Find( rName, &nPos );
+ OUString const value(pField->ExpandField(m_rDoc.IsClipBoard(), nullptr));
+ if( pFnd )
+ {
+ // modify entry in the hash table
+ pFnd->aSetStr = value;
+ }
+ else
+ {
+ // insert the new entry
+ rHashTable[nPos].reset( new HashStr( rName,
+ value, rHashTable[nPos].release()) );
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+
+bool DocumentFieldsManager::IsNewFieldLst() const
+{
+ return mbNewFieldLst;
+}
+
+void DocumentFieldsManager::SetNewFieldLst(bool bFlag)
+{
+ mbNewFieldLst = bFlag;
+}
+
+void DocumentFieldsManager::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField )
+{
+ if (!mbNewFieldLst && !m_rDoc.IsInDtor())
+ mpUpdateFields->InsDelFieldInFieldLst( bIns, rField );
+}
+
+SwField * DocumentFieldsManager::GetFieldAtPos(const SwPosition & rPos)
+{
+ SwTextField * const pAttr = GetTextFieldAtPos(rPos);
+
+ return pAttr ? const_cast<SwField *>( pAttr->GetFormatField().GetField() ) : nullptr;
+}
+
+SwTextField * DocumentFieldsManager::GetTextFieldAtPos(const SwPosition & rPos)
+{
+ SwTextNode * const pNode = rPos.nNode.GetNode().GetTextNode();
+
+ return (pNode != nullptr)
+ ? pNode->GetFieldTextAttrAt( rPos.nContent.GetIndex(), true )
+ : nullptr;
+}
+
+/// @note For simplicity assume that all field types have updatable contents so
+/// optimization currently only available when no fields exist.
+bool DocumentFieldsManager::containsUpdatableFields()
+{
+ std::vector<SwFormatField*> vFields;
+ for (auto const& pFieldType: *mpFieldTypes)
+ {
+ pFieldType->GatherFields(vFields);
+ if(vFields.size()>0)
+ return true;
+ }
+ return false;
+}
+
+/// Remove all unreferenced field types of a document
+void DocumentFieldsManager::GCFieldTypes()
+{
+ for( auto n = mpFieldTypes->size(); n > INIT_FLDTYPES; )
+ if( !(*mpFieldTypes)[ --n ]->HasWriterListeners() )
+ RemoveFieldType( n );
+}
+
+void DocumentFieldsManager::InitFieldTypes() // is being called by the CTOR
+{
+ // Field types
+ mpFieldTypes->emplace_back( new SwDateTimeFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwChapterFieldType );
+ mpFieldTypes->emplace_back( new SwPageNumberFieldType );
+ mpFieldTypes->emplace_back( new SwAuthorFieldType );
+ mpFieldTypes->emplace_back( new SwFileNameFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwDBNameFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwGetExpFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwGetRefFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwHiddenTextFieldType );
+ mpFieldTypes->emplace_back( new SwPostItFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwDocStatFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwDocInfoFieldType(&m_rDoc) );
+ mpFieldTypes->emplace_back( new SwInputFieldType( &m_rDoc ) );
+ mpFieldTypes->emplace_back( new SwTableFieldType( &m_rDoc ) );
+ mpFieldTypes->emplace_back( new SwMacroFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwHiddenParaFieldType );
+ mpFieldTypes->emplace_back( new SwDBNextSetFieldType );
+ mpFieldTypes->emplace_back( new SwDBNumSetFieldType );
+ mpFieldTypes->emplace_back( new SwDBSetNumberFieldType );
+ mpFieldTypes->emplace_back( new SwTemplNameFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwTemplNameFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwExtUserFieldType );
+ mpFieldTypes->emplace_back( new SwRefPageSetFieldType );
+ mpFieldTypes->emplace_back( new SwRefPageGetFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwJumpEditFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwScriptFieldType(m_rDoc) );
+ mpFieldTypes->emplace_back( new SwCombinedCharFieldType );
+ mpFieldTypes->emplace_back( new SwDropDownFieldType );
+
+ // Types have to be at the end!
+ // We expect this in the InsertFieldType!
+ // MIB 14.04.95: In Sw3StringPool::Setup (sw3imp.cxx) and
+ // lcl_sw3io_InSetExpField (sw3field.cxx) now also
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_ABB), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_TABLE), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_FRAME), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_DRAWING), nsSwGetSetExpType::GSE_SEQ) );
+ mpFieldTypes->emplace_back( new SwSetExpFieldType(&m_rDoc,
+ SwResId(STR_POOLCOLL_LABEL_FIGURE), nsSwGetSetExpType::GSE_SEQ) );
+
+ assert( mpFieldTypes->size() == INIT_FLDTYPES );
+}
+
+void DocumentFieldsManager::ClearFieldTypes()
+{
+ mpFieldTypes->erase( mpFieldTypes->begin() + INIT_FLDTYPES, mpFieldTypes->end() );
+}
+
+void DocumentFieldsManager::UpdateDBNumFields( SwDBNameInfField& rDBField, SwCalc& rCalc )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDBField;
+ (void) rCalc;
+#else
+ SwDBManager* pMgr = m_rDoc.GetDBManager();
+
+ SwFieldIds nFieldType = rDBField.Which();
+
+ bool bPar1 = rCalc.Calculate( rDBField.GetPar1() ).GetBool();
+
+ if( SwFieldIds::DbNextSet == nFieldType )
+ static_cast<SwDBNextSetField&>(rDBField).SetCondValid( bPar1 );
+ else
+ static_cast<SwDBNumSetField&>(rDBField).SetCondValid( bPar1 );
+
+ if( !rDBField.GetRealDBData().sDataSource.isEmpty() )
+ {
+ // Edit a certain database
+ if( SwFieldIds::DbNextSet == nFieldType )
+ static_cast<SwDBNextSetField&>(rDBField).Evaluate(m_rDoc);
+ else
+ static_cast<SwDBNumSetField&>(rDBField).Evaluate(m_rDoc);
+
+ SwDBData aTmpDBData( rDBField.GetDBData(&m_rDoc) );
+
+ if( pMgr->OpenDataSource( aTmpDBData.sDataSource, aTmpDBData.sCommand ))
+ rCalc.VarChange( lcl_GetDBVarName( m_rDoc, rDBField),
+ pMgr->GetSelectedRecordId(aTmpDBData.sDataSource, aTmpDBData.sCommand, aTmpDBData.nCommandType) );
+ }
+ else
+ {
+ OSL_FAIL("TODO: what should happen with unnamed DBFields?");
+ }
+#endif
+}
+
+DocumentFieldsManager::~DocumentFieldsManager()
+{
+ mpUpdateFields.reset();
+ mpFieldTypes.reset();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx b/sw/source/core/doc/DocumentLayoutManager.cxx
new file mode 100644
index 000000000..87c75b3e2
--- /dev/null
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -0,0 +1,500 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentLayoutManager.hxx>
+#include <doc.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <undobj.hxx>
+#include <viewsh.hxx>
+#include <layouter.hxx>
+#include <poolfmt.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtcnct.hxx>
+#include <ndole.hxx>
+#include <fmtanchr.hxx>
+#include <txtflcnt.hxx>
+#include <fmtflcnt.hxx>
+#include <ndtxt.hxx>
+#include <unoframe.hxx>
+#include <textboxhelper.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <frameformats.hxx>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace sw
+{
+
+DocumentLayoutManager::DocumentLayoutManager( SwDoc& i_rSwdoc ) :
+ m_rDoc( i_rSwdoc ),
+ mpCurrentView( nullptr )
+{
+}
+
+const SwViewShell *DocumentLayoutManager::GetCurrentViewShell() const
+{
+ return mpCurrentView;
+}
+
+SwViewShell *DocumentLayoutManager::GetCurrentViewShell()
+{
+ return mpCurrentView;
+}
+
+void DocumentLayoutManager::SetCurrentViewShell( SwViewShell* pNew )
+{
+ mpCurrentView = pNew;
+}
+
+// It must be able to communicate to a SwViewShell. This is going to be removed later.
+const SwRootFrame *DocumentLayoutManager::GetCurrentLayout() const
+{
+ if(GetCurrentViewShell())
+ return GetCurrentViewShell()->GetLayout();
+ return nullptr;
+}
+
+SwRootFrame *DocumentLayoutManager::GetCurrentLayout()
+{
+ if(GetCurrentViewShell())
+ return GetCurrentViewShell()->GetLayout();
+ return nullptr;
+}
+
+bool DocumentLayoutManager::HasLayout() const
+{
+ // if there is a view, there is always a layout
+ return (mpCurrentView != nullptr);
+}
+
+SwLayouter* DocumentLayoutManager::GetLayouter()
+{
+ return mpLayouter.get();
+}
+
+const SwLayouter* DocumentLayoutManager::GetLayouter() const
+{
+ return mpLayouter.get();
+}
+
+void DocumentLayoutManager::SetLayouter( SwLayouter* pNew )
+{
+ mpLayouter.reset( pNew );
+}
+
+/** Create a new format whose settings fit to the Request by default.
+
+ The format is put into the respective format array.
+ If there already is a fitting format, it is returned instead. */
+SwFrameFormat *DocumentLayoutManager::MakeLayoutFormat( RndStdIds eRequest, const SfxItemSet* pSet )
+{
+ SwFrameFormat *pFormat = nullptr;
+ const bool bMod = m_rDoc.getIDocumentState().IsModified();
+ bool bHeader = false;
+
+ switch ( eRequest )
+ {
+ case RndStdIds::HEADER:
+ case RndStdIds::HEADERL:
+ case RndStdIds::HEADERR:
+ {
+ bHeader = true;
+ [[fallthrough]];
+ }
+ case RndStdIds::FOOTER:
+ {
+ pFormat = new SwFrameFormat( m_rDoc.GetAttrPool(),
+ (bHeader ? "Right header" : "Right footer"),
+ m_rDoc.GetDfltFrameFormat() );
+
+ SwNodeIndex aTmpIdx( m_rDoc.GetNodes().GetEndOfAutotext() );
+ SwStartNode* pSttNd =
+ m_rDoc.GetNodes().MakeTextSection
+ ( aTmpIdx,
+ bHeader ? SwHeaderStartNode : SwFooterStartNode,
+ m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>( bHeader
+ ? ( eRequest == RndStdIds::HEADERL
+ ? RES_POOLCOLL_HEADERL
+ : eRequest == RndStdIds::HEADERR
+ ? RES_POOLCOLL_HEADERR
+ : RES_POOLCOLL_HEADER )
+ : RES_POOLCOLL_FOOTER
+ ) ) );
+ pFormat->SetFormatAttr( SwFormatContent( pSttNd ));
+
+ if( pSet ) // Set a few more attributes
+ pFormat->SetFormatAttr( *pSet );
+
+ // Why set it back? Doc has changed, or not?
+ // In any case, wrong for the FlyFrames!
+ if ( !bMod )
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+ break;
+
+ case RndStdIds::DRAW_OBJECT:
+ {
+ pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() );
+ if( pSet ) // Set a few more attributes
+ pFormat->SetFormatAttr( *pSet );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0));
+ }
+ }
+ break;
+
+#if OSL_DEBUG_LEVEL > 0
+ case RndStdIds::FLY_AT_PAGE:
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AT_FLY:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AS_CHAR:
+ OSL_FAIL( "use new interface instead: SwDoc::MakeFlySection!" );
+ break;
+#endif
+
+ default:
+ OSL_ENSURE( false,
+ "LayoutFormat was requested with an invalid Request." );
+
+ }
+ return pFormat;
+}
+
+/// Deletes the denoted format and its content.
+void DocumentLayoutManager::DelLayoutFormat( SwFrameFormat *pFormat )
+{
+ // A chain of frames needs to be merged, if necessary,
+ // so that the Frame's contents are adjusted accordingly before we destroy the Frames.
+ const SwFormatChain &rChain = pFormat->GetChain();
+ if ( rChain.GetPrev() )
+ {
+ SwFormatChain aChain( rChain.GetPrev()->GetChain() );
+ aChain.SetNext( rChain.GetNext() );
+ m_rDoc.SetAttr( aChain, *rChain.GetPrev() );
+ }
+ if ( rChain.GetNext() )
+ {
+ SwFormatChain aChain( rChain.GetNext()->GetChain() );
+ aChain.SetPrev( rChain.GetPrev() );
+ m_rDoc.SetAttr( aChain, *rChain.GetNext() );
+ }
+
+ const SwNodeIndex* pCntIdx = nullptr;
+ // The draw format doesn't own its content, it just has a pointer to it.
+ if (pFormat->Which() != RES_DRAWFRMFMT)
+ pCntIdx = pFormat->GetContent().GetContentIdx();
+ if (pCntIdx && !m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Disconnect if it's an OLE object
+ SwOLENode* pOLENd = m_rDoc.GetNodes()[ pCntIdx->GetIndex()+1 ]->GetOLENode();
+ if( pOLENd && pOLENd->GetOLEObj().IsOleRef() )
+ {
+ try
+ {
+ pOLENd->GetOLEObj().GetOleRef()->changeState( embed::EmbedStates::LOADED );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ // Destroy Frames
+ pFormat->DelFrames();
+
+ // Only FlyFrames are undoable at first
+ const sal_uInt16 nWh = pFormat->Which();
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo() &&
+ (RES_FLYFRMFMT == nWh || RES_DRAWFRMFMT == nWh))
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDelLayFormat>( pFormat ));
+ }
+ else
+ {
+ // #i32089# - delete at-frame anchored objects
+ if ( nWh == RES_FLYFRMFMT )
+ {
+ // determine frame formats of at-frame anchored objects
+ const SwNodeIndex* pContentIdx = nullptr;
+ if (pFormat->Which() != RES_DRAWFRMFMT)
+ pContentIdx = pFormat->GetContent().GetContentIdx();
+ if (pContentIdx)
+ {
+ const SwFrameFormats* pTable = pFormat->GetDoc()->GetSpzFrameFormats();
+ if ( pTable )
+ {
+ std::vector<SwFrameFormat*> aToDeleteFrameFormats;
+ const SwNodeOffset nNodeIdxOfFlyFormat( pContentIdx->GetIndex() );
+
+ for ( size_t i = 0; i < pTable->size(); ++i )
+ {
+ SwFrameFormat* pTmpFormat = (*pTable)[i];
+ const SwFormatAnchor &rAnch = pTmpFormat->GetAnchor();
+ if ( rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY &&
+ rAnch.GetContentAnchor()->nNode.GetIndex() == nNodeIdxOfFlyFormat )
+ {
+ aToDeleteFrameFormats.push_back( pTmpFormat );
+ }
+ }
+
+ // delete found frame formats
+ while ( !aToDeleteFrameFormats.empty() )
+ {
+ SwFrameFormat* pTmpFormat = aToDeleteFrameFormats.back();
+ pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pTmpFormat );
+
+ aToDeleteFrameFormats.pop_back();
+ }
+ }
+ }
+ }
+
+ // Delete content
+ if( pCntIdx )
+ {
+ SwNode *pNode = &pCntIdx->GetNode();
+ const_cast<SwFormatContent&>(pFormat->GetFormatAttr( RES_CNTNT )).SetNewContentIdx( nullptr );
+ m_rDoc.getIDocumentContentOperations().DeleteSection( pNode );
+ }
+
+ // Delete the character for FlyFrames anchored as char (if necessary)
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && rAnchor.GetContentAnchor())
+ {
+ const SwPosition* pPos = rAnchor.GetContentAnchor();
+ SwTextNode *pTextNd = pPos->nNode.GetNode().GetTextNode();
+
+ // attribute is still in text node, delete it
+ if ( pTextNd )
+ {
+ SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>(
+ pTextNd->GetTextAttrForCharAt( pPos->nContent.GetIndex(),
+ RES_TXTATR_FLYCNT ));
+ if ( pAttr && (pAttr->GetFlyCnt().GetFrameFormat() == pFormat) )
+ {
+ // don't delete, set pointer to 0
+ const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
+ SwIndex aIdx( pPos->nContent );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ }
+
+ m_rDoc.DelFrameFormat( pFormat );
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+/** Copies the stated format (pSrc) to pDest and returns pDest.
+
+ If there's no pDest, it is created.
+ If the source format is located in another document, also copy correctly
+ in this case.
+ The Anchor attribute's position is always set to 0! */
+SwFrameFormat *DocumentLayoutManager::CopyLayoutFormat(
+ const SwFrameFormat& rSource,
+ const SwFormatAnchor& rNewAnchor,
+ bool bSetTextFlyAtt,
+ bool bMakeFrames )
+{
+ const bool bFly = RES_FLYFRMFMT == rSource.Which();
+ const bool bDraw = RES_DRAWFRMFMT == rSource.Which();
+ OSL_ENSURE( bFly || bDraw, "this method only works for fly or draw" );
+
+ SwDoc* pSrcDoc = const_cast<SwDoc*>(rSource.GetDoc());
+
+ // May we copy this object?
+ // We may, unless it's 1) it's a control (and therefore a draw)
+ // 2) anchored in a header/footer
+ // 3) anchored (to paragraph?)
+ bool bMayNotCopy = false;
+ const auto pCAnchor = rNewAnchor.GetContentAnchor();
+ bool bInHeaderFooter = pCAnchor && m_rDoc.IsInHeaderFooter(pCAnchor->nNode);
+ if(bDraw)
+ {
+ bool bCheckControlLayer = false;
+ rSource.CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer));
+ bMayNotCopy =
+ bCheckControlLayer &&
+ ((RndStdIds::FLY_AT_PARA == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_FLY == rNewAnchor.GetAnchorId()) || (RndStdIds::FLY_AT_CHAR == rNewAnchor.GetAnchorId())) &&
+ bInHeaderFooter;
+ }
+
+ // just return if we can't copy this
+ if( bMayNotCopy )
+ return nullptr;
+
+ SwFrameFormat* pDest = m_rDoc.GetDfltFrameFormat();
+ if( rSource.GetRegisteredIn() != pSrcDoc->GetDfltFrameFormat() )
+ pDest = m_rDoc.CopyFrameFormat( *static_cast<const SwFrameFormat*>(rSource.GetRegisteredIn()) );
+ if( bFly )
+ {
+ // #i11176#
+ // To do a correct cloning concerning the ZOrder for all objects
+ // it is necessary to actually create a draw object for fly frames, too.
+ // These are then added to the DrawingLayer (which needs to exist).
+ // Together with correct sorting of all drawinglayer based objects
+ // before cloning ZOrder transfer works correctly then.
+ SwFlyFrameFormat *pFormat = m_rDoc.MakeFlyFrameFormat( rSource.GetName(), pDest );
+ pDest = pFormat;
+
+ SwXFrame::GetOrCreateSdrObject(*pFormat);
+ }
+ else
+ pDest = m_rDoc.MakeDrawFrameFormat( OUString(), pDest );
+
+ // Copy all other or new attributes
+ pDest->CopyAttrs( rSource );
+
+ // Do not copy chains
+ pDest->ResetFormatAttr( RES_CHAIN );
+
+ if( bFly )
+ {
+ // Duplicate the content.
+ const SwNode& rCSttNd = rSource.GetContent().GetContentIdx()->GetNode();
+ SwNodeRange aRg( rCSttNd, SwNodeOffset(1), *rCSttNd.EndOfSectionNode() );
+
+ SwNodeIndex aIdx( m_rDoc.GetNodes().GetEndOfAutotext() );
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( aIdx, SwFlyStartNode );
+
+ // Set the Anchor/ContentIndex first.
+ // Within the copying part, we can access the values (DrawFormat in Headers and Footers)
+ aIdx = *pSttNd;
+ SwFormatContent aAttr( rSource.GetContent() );
+ aAttr.SetNewContentIdx( &aIdx );
+ pDest->SetFormatAttr( aAttr );
+ pDest->SetFormatAttr( rNewAnchor );
+
+ if( !m_rDoc.IsCopyIsMove() || &m_rDoc != pSrcDoc )
+ {
+ if( (m_rDoc.IsInReading() && !bInHeaderFooter) || m_rDoc.IsInMailMerge() )
+ pDest->SetName( OUString() );
+ else
+ {
+ // Test first if the name is already taken, if so generate a new one.
+ SwNodeType nNdTyp = aRg.aStart.GetNode().GetNodeType();
+
+ OUString sOld( pDest->GetName() );
+ pDest->SetName( OUString() );
+ if( m_rDoc.FindFlyByName( sOld, nNdTyp ) ) // found one
+ switch( nNdTyp )
+ {
+ case SwNodeType::Grf: sOld = m_rDoc.GetUniqueGrfName(sOld); break;
+ case SwNodeType::Ole: sOld = m_rDoc.GetUniqueOLEName(); break;
+ default: sOld = m_rDoc.GetUniqueFrameName(); break;
+ }
+
+ pDest->SetName( sOld );
+ }
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0));
+ }
+
+ // Make sure that FlyFrames in FlyFrames are copied
+ aIdx = *pSttNd->EndOfSectionNode();
+
+ //fdo#36631 disable (scoped) any undo operations associated with the
+ //contact object itself. They should be managed by SwUndoInsLayFormat.
+ const ::sw::DrawUndoGuard drawUndoGuard(m_rDoc.GetIDocumentUndoRedo());
+
+ pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aIdx, nullptr, false, true, true);
+ }
+ else
+ {
+ OSL_ENSURE( RES_DRAWFRMFMT == rSource.Which(), "Neither Fly nor Draw." );
+ // #i52780# - Note: moving object to visible layer not needed.
+ rSource.CallSwClientNotify(sw::DrawFormatLayoutCopyHint(static_cast<SwDrawFrameFormat&>(*pDest), m_rDoc));
+
+ if(pDest->GetAnchor() == rNewAnchor)
+ {
+ // Do *not* connect to layout, if a <MakeFrames> will not be called.
+ if(bMakeFrames)
+ pDest->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES));
+
+ }
+ else
+ pDest->SetFormatAttr( rNewAnchor );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsLayFormat>(pDest,SwNodeOffset(0),0));
+ }
+ }
+
+ if (bSetTextFlyAtt && (RndStdIds::FLY_AS_CHAR == rNewAnchor.GetAnchorId()))
+ {
+ const SwPosition* pPos = rNewAnchor.GetContentAnchor();
+ SwFormatFlyCnt aFormat( pDest );
+ pPos->nNode.GetNode().GetTextNode()->InsertItem(
+ aFormat, pPos->nContent.GetIndex(), 0 );
+ }
+
+ if( bMakeFrames )
+ pDest->MakeFrames();
+
+ if (pDest->GetName().isEmpty())
+ {
+ // Format name should have unique name. Let's use object name as a fallback
+ SdrObject *pObj = pDest->FindSdrObject();
+ if (pObj)
+ pDest->SetName(pObj->GetName());
+ }
+
+ // If the draw format has a TextBox, then copy its fly format as well.
+ if (const auto& pTextBoxes = rSource.GetOtherTextBoxFormats())
+ pTextBoxes->Clone(&m_rDoc, rNewAnchor, pDest, bSetTextFlyAtt, bMakeFrames);
+
+ return pDest;
+}
+
+//Load document from fdo#42534 under valgrind, drag the scrollbar down so full
+//document layout is triggered. Close document before layout has completed, and
+//SwAnchoredObject objects deleted by the deletion of layout remain referenced
+//by the SwLayouter
+void DocumentLayoutManager::ClearSwLayouterEntries()
+{
+ SwLayouter::ClearMovedFwdFrames( m_rDoc );
+ SwLayouter::ClearObjsTmpConsiderWrapInfluence( m_rDoc );
+ // #i65250#
+ SwLayouter::ClearMoveBwdLayoutInfo( m_rDoc );
+}
+
+DocumentLayoutManager::~DocumentLayoutManager()
+{
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sw/source/core/doc/DocumentLinksAdministrationManager.cxx b/sw/source/core/doc/DocumentLinksAdministrationManager.cxx
new file mode 100644
index 000000000..175b50cab
--- /dev/null
+++ b/sw/source/core/doc/DocumentLinksAdministrationManager.cxx
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <DocumentLinksAdministrationManager.hxx>
+
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/docfile.hxx>
+#include <dialoghelp.hxx>
+#include <linkenum.hxx>
+#include <com/sun/star/document/UpdateDocMode.hpp>
+#include <swtypes.hxx>
+#include <docsh.hxx>
+#include <bookmark.hxx>
+#include <swserv.hxx>
+#include <swbaslnk.hxx>
+#include <section.hxx>
+#include <docary.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/securityoptions.hxx>
+
+using namespace ::com::sun::star;
+
+//Helper functions for this file
+namespace
+{
+ struct FindItem
+ {
+ const OUString m_Item;
+ SwTableNode* pTableNd;
+ SwSectionNode* pSectNd;
+
+ explicit FindItem(const OUString& rS)
+ : m_Item(rS), pTableNd(nullptr), pSectNd(nullptr)
+ {}
+ };
+
+ ::sfx2::SvBaseLink* lcl_FindNextRemovableLink( const ::sfx2::SvBaseLinks& rLinks )
+ {
+ for (const auto& rLinkIter : rLinks)
+ {
+ ::sfx2::SvBaseLink& rLnk = *rLinkIter;
+ if ((sfx2::SvBaseLinkObjectType::ClientGraphic == rLnk.GetObjType() || sfx2::SvBaseLinkObjectType::ClientFile == rLnk.GetObjType())
+ && dynamic_cast<const SwBaseLink*>(&rLnk) != nullptr)
+ {
+ tools::SvRef<sfx2::SvBaseLink> xLink(&rLnk);
+
+ OUString sFName;
+ sfx2::LinkManager::GetDisplayNames( xLink.get(), nullptr, &sFName );
+
+ INetURLObject aURL( sFName );
+ if( INetProtocol::File == aURL.GetProtocol() ||
+ INetProtocol::Cid == aURL.GetProtocol() )
+ return &rLnk;
+ }
+ }
+ return nullptr;
+ }
+
+
+ ::sw::mark::DdeBookmark* lcl_FindDdeBookmark( const IDocumentMarkAccess& rMarkAccess, const OUString& rName, const bool bCaseSensitive )
+ {
+ //Iterating over all bookmarks, checking DdeBookmarks
+ const OUString sNameLc = bCaseSensitive ? rName : GetAppCharClass().lowercase(rName);
+ for(IDocumentMarkAccess::const_iterator_t ppMark = rMarkAccess.getAllMarksBegin();
+ ppMark != rMarkAccess.getAllMarksEnd();
+ ++ppMark)
+ {
+ if (::sw::mark::DdeBookmark* const pBkmk = dynamic_cast< ::sw::mark::DdeBookmark*>(*ppMark))
+ {
+ if (
+ (bCaseSensitive && (pBkmk->GetName() == sNameLc)) ||
+ (!bCaseSensitive && GetAppCharClass().lowercase(pBkmk->GetName()) == sNameLc)
+ )
+ {
+ return pBkmk;
+ }
+ }
+ }
+ return nullptr;
+ }
+
+
+ bool lcl_FindSection( const SwSectionFormat* pSectFormat, FindItem * const pItem, bool bCaseSensitive )
+ {
+ SwSection* pSect = pSectFormat->GetSection();
+ if( pSect )
+ {
+ OUString sNm( bCaseSensitive
+ ? pSect->GetSectionName()
+ : GetAppCharClass().lowercase( pSect->GetSectionName() ));
+ OUString sCompare( bCaseSensitive
+ ? pItem->m_Item
+ : GetAppCharClass().lowercase( pItem->m_Item ) );
+ if( sNm == sCompare )
+ {
+ // found, so get the data
+ const SwNodeIndex* pIdx = pSectFormat->GetContent().GetContentIdx();
+ if( pIdx && &pSectFormat->GetDoc()->GetNodes() == &pIdx->GetNodes() )
+ {
+ // a table in the normal NodesArr
+ pItem->pSectNd = pIdx->GetNode().GetSectionNode();
+ return false;
+ }
+ // If the name is already correct, but not the rest then we don't have them.
+ // The names are always unique.
+ }
+ }
+ return true;
+ }
+
+ bool lcl_FindTable( const SwFrameFormat* pTableFormat, FindItem * const pItem )
+ {
+ OUString sNm( GetAppCharClass().lowercase( pTableFormat->GetName() ));
+ if ( sNm == pItem->m_Item )
+ {
+ SwTable* pTmpTable = SwTable::FindTable( pTableFormat );
+ if( pTmpTable )
+ {
+ SwTableBox* pFBox = pTmpTable->GetTabSortBoxes()[0];
+ if( pFBox && pFBox->GetSttNd() &&
+ &pTableFormat->GetDoc()->GetNodes() == &pFBox->GetSttNd()->GetNodes() )
+ {
+ // a table in the normal NodesArr
+ pItem->pTableNd = const_cast<SwTableNode*>(
+ pFBox->GetSttNd()->FindTableNode());
+ return false;
+ }
+ }
+ // If the name is already correct, but not the rest then we don't have them.
+ // The names are always unique.
+ }
+ return true;
+ }
+
+}
+
+
+namespace sw
+{
+
+DocumentLinksAdministrationManager::DocumentLinksAdministrationManager( SwDoc& i_rSwdoc )
+ : mbVisibleLinks(true)
+ , mbLinksUpdated( false ) //#i38810#
+ , m_pLinkMgr( new sfx2::LinkManager(nullptr) )
+ , m_rDoc( i_rSwdoc )
+{
+}
+
+bool DocumentLinksAdministrationManager::IsVisibleLinks() const
+{
+ return mbVisibleLinks;
+}
+
+void DocumentLinksAdministrationManager::SetVisibleLinks(bool bFlag)
+{
+ mbVisibleLinks = bFlag;
+}
+
+sfx2::LinkManager& DocumentLinksAdministrationManager::GetLinkManager()
+{
+ return *m_pLinkMgr;
+}
+
+const sfx2::LinkManager& DocumentLinksAdministrationManager::GetLinkManager() const
+{
+ return *m_pLinkMgr;
+}
+
+// #i42634# Moved common code of SwReader::Read() and SwDocShell::UpdateLinks()
+// to new SwDoc::UpdateLinks():
+void DocumentLinksAdministrationManager::UpdateLinks()
+{
+ if (!m_rDoc.GetDocShell())
+ return;
+ SfxObjectCreateMode eMode = m_rDoc.GetDocShell()->GetCreateMode();
+ if (eMode == SfxObjectCreateMode::INTERNAL)
+ return;
+ if (eMode == SfxObjectCreateMode::ORGANIZER)
+ return;
+ if (m_rDoc.GetDocShell()->IsPreview())
+ return;
+ if (GetLinkManager().GetLinks().empty())
+ return;
+ sal_uInt16 nLinkMode = m_rDoc.GetDocumentSettingManager().getLinkUpdateMode(true);
+ sal_uInt16 nUpdateDocMode = m_rDoc.GetDocShell()->GetUpdateDocMode();
+ if (nLinkMode == NEVER && nUpdateDocMode != document::UpdateDocMode::FULL_UPDATE)
+ return;
+
+ bool bAskUpdate = nLinkMode == MANUAL;
+ bool bUpdate = true;
+ switch(nUpdateDocMode)
+ {
+ case document::UpdateDocMode::NO_UPDATE: bUpdate = false;break;
+ case document::UpdateDocMode::QUIET_UPDATE:bAskUpdate = false; break;
+ case document::UpdateDocMode::FULL_UPDATE: bAskUpdate = true; break;
+ }
+ if (nLinkMode == AUTOMATIC && !bAskUpdate)
+ {
+ SfxMedium * medium = m_rDoc.GetDocShell()->GetMedium();
+ if (!SvtSecurityOptions::isTrustedLocationUriForUpdatingLinks(
+ medium == nullptr ? OUString() : medium->GetName()))
+ {
+ bAskUpdate = true;
+ }
+ }
+ comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = m_rDoc.GetDocShell()->getEmbeddedObjectContainer();
+ if (bUpdate)
+ {
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true);
+
+ weld::Window* pDlgParent = GetFrameWeld(m_rDoc.GetDocShell());
+ GetLinkManager().UpdateAllLinks(bAskUpdate, false, pDlgParent);
+ }
+ else
+ {
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false);
+ }
+}
+
+bool DocumentLinksAdministrationManager::GetData( const OUString& rItem, const OUString& rMimeType,
+ uno::Any & rValue ) const
+{
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), rItem, bCaseSensitive);
+ if(pBkmk)
+ return SwServerObject(*pBkmk).GetData(rValue, rMimeType);
+
+ // Do we already have the Item?
+ OUString sItem( bCaseSensitive ? rItem : GetAppCharClass().lowercase(rItem));
+ FindItem aPara( sItem );
+ for( const SwSectionFormat* pFormat : m_rDoc.GetSections() )
+ {
+ if (!(lcl_FindSection(pFormat, &aPara, bCaseSensitive)))
+ break;
+ }
+ if( aPara.pSectNd )
+ {
+ // found, so get the data
+ return SwServerObject( *aPara.pSectNd ).GetData( rValue, rMimeType );
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+
+ FindItem aPara( GetAppCharClass().lowercase( rItem ));
+ for( const SwFrameFormat* pFormat : *m_rDoc.GetTableFrameFormats() )
+ {
+ if (!(lcl_FindTable(pFormat, &aPara)))
+ break;
+ }
+ if( aPara.pTableNd )
+ {
+ return SwServerObject( *aPara.pTableNd ).GetData( rValue, rMimeType );
+ }
+
+ return false;
+}
+
+void DocumentLinksAdministrationManager::SetData( const OUString& rItem )
+{
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), rItem, bCaseSensitive);
+ if(pBkmk)
+ {
+ return;
+ }
+
+ // Do we already have the Item?
+ OUString sItem( bCaseSensitive ? rItem : GetAppCharClass().lowercase(rItem));
+ FindItem aPara( sItem );
+ for( const SwSectionFormat* pFormat : m_rDoc.GetSections() )
+ {
+ if (!(lcl_FindSection(pFormat, &aPara, bCaseSensitive)))
+ break;
+ }
+ if( aPara.pSectNd )
+ {
+ // found, so get the data
+ return;
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+
+ OUString sItem(GetAppCharClass().lowercase(rItem));
+ FindItem aPara( sItem );
+ for( const SwFrameFormat* pFormat : *m_rDoc.GetTableFrameFormats() )
+ {
+ if (!(lcl_FindTable(pFormat, &aPara)))
+ break;
+ }
+}
+
+::sfx2::SvLinkSource* DocumentLinksAdministrationManager::CreateLinkSource(const OUString& rItem)
+{
+ SwServerObject* pObj = nullptr;
+
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ // bookmarks
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), rItem, bCaseSensitive);
+ if(pBkmk && pBkmk->IsExpanded())
+ {
+ pObj = pBkmk->GetRefObject();
+ if( !pObj )
+ {
+ // mark found, but no link yet -> create hotlink
+ pObj = new SwServerObject(*pBkmk);
+ pBkmk->SetRefObject(pObj);
+ GetLinkManager().InsertServer(pObj);
+ }
+ }
+ if(pObj)
+ return pObj;
+
+ FindItem aPara(bCaseSensitive ? rItem : GetAppCharClass().lowercase(rItem));
+ // sections
+ for( const SwSectionFormat* pFormat : m_rDoc.GetSections() )
+ {
+ if (!(lcl_FindSection(pFormat, &aPara, bCaseSensitive)))
+ break;
+ }
+
+ if(aPara.pSectNd)
+ {
+ pObj = aPara.pSectNd->GetSection().GetObject();
+ if( !pObj )
+ {
+ // section found, but no link yet -> create hotlink
+ pObj = new SwServerObject( *aPara.pSectNd );
+ aPara.pSectNd->GetSection().SetRefObject( pObj );
+ GetLinkManager().InsertServer(pObj);
+ }
+ }
+ if(pObj)
+ return pObj;
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+
+ FindItem aPara( GetAppCharClass().lowercase(rItem) );
+ // tables
+ for( const SwFrameFormat* pFormat : *m_rDoc.GetTableFrameFormats() )
+ {
+ if (!(lcl_FindTable(pFormat, &aPara)))
+ break;
+ }
+ if(aPara.pTableNd)
+ {
+ pObj = aPara.pTableNd->GetTable().GetObject();
+ if( !pObj )
+ {
+ // table found, but no link yet -> create hotlink
+ pObj = new SwServerObject(*aPara.pTableNd);
+ aPara.pTableNd->GetTable().SetRefObject(pObj);
+ GetLinkManager().InsertServer(pObj);
+ }
+ }
+ return pObj;
+}
+
+/// embedded all local links (Areas/Graphics)
+bool DocumentLinksAdministrationManager::EmbedAllLinks()
+{
+ bool bRet = false;
+ sfx2::LinkManager& rLnkMgr = GetLinkManager();
+ const ::sfx2::SvBaseLinks& rLinks = rLnkMgr.GetLinks();
+ if( !rLinks.empty() )
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+
+ ::sfx2::SvBaseLink* pLnk = nullptr;
+ while( nullptr != (pLnk = lcl_FindNextRemovableLink( rLinks ) ) )
+ {
+ tools::SvRef<sfx2::SvBaseLink> xLink = pLnk;
+ // Tell the link that it's being destroyed!
+ xLink->Closed();
+
+ // if one forgot to remove itself
+ if( xLink.is() )
+ rLnkMgr.Remove( xLink.get() );
+
+ bRet = true;
+ }
+
+ m_rDoc.GetIDocumentUndoRedo().DelAllUndoObj();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ return bRet;
+}
+
+void DocumentLinksAdministrationManager::SetLinksUpdated(const bool bNewLinksUpdated)
+{
+ mbLinksUpdated = bNewLinksUpdated;
+}
+
+bool DocumentLinksAdministrationManager::LinksUpdated() const
+{
+ return mbLinksUpdated;
+}
+
+DocumentLinksAdministrationManager::~DocumentLinksAdministrationManager()
+{
+}
+
+bool DocumentLinksAdministrationManager::SelectServerObj( std::u16string_view rStr, SwPaM*& rpPam, std::unique_ptr<SwNodeRange>& rpRange ) const
+{
+ // Do we actually have the Item?
+ rpPam = nullptr;
+ rpRange = nullptr;
+
+ OUString sItem( INetURLObject::decode( rStr,
+ INetURLObject::DecodeMechanism::WithCharset ));
+
+ sal_Int32 nPos = sItem.indexOf( cMarkSeparator );
+
+ const CharClass& rCC = GetAppCharClass();
+
+ // Extension for sections: not only link bookmarks/sections
+ // but also frames (text!), tables, outlines:
+ if( -1 != nPos )
+ {
+ bool bContinue = false;
+ OUString sName( sItem.copy( 0, nPos ) );
+ std::u16string_view sCmp( sItem.subView( nPos + 1 ));
+ sItem = rCC.lowercase( sItem );
+
+ FindItem aPara( sName );
+
+ if( sCmp == u"table" )
+ {
+ sName = rCC.lowercase( sName );
+ for( const SwFrameFormat* pFormat : *m_rDoc.GetTableFrameFormats() )
+ {
+ if (!(lcl_FindTable(pFormat, &aPara)))
+ break;
+ }
+ if( aPara.pTableNd )
+ {
+ rpRange.reset(new SwNodeRange( *aPara.pTableNd, SwNodeOffset(0),
+ *aPara.pTableNd->EndOfSectionNode(), SwNodeOffset(1) ));
+ return true;
+ }
+ }
+ else if( sCmp == u"frame" )
+ {
+ const SwFlyFrameFormat* pFlyFormat = m_rDoc.FindFlyByName( sName );
+ if( pFlyFormat )
+ {
+ SwNodeIndex* pIdx = const_cast<SwNodeIndex*>(pFlyFormat->GetContent().GetContentIdx());
+ if( pIdx )
+ {
+ SwNode* pNd = &pIdx->GetNode();
+ if( !pNd->IsNoTextNode() )
+ {
+ rpRange.reset(new SwNodeRange( *pNd, SwNodeOffset(1), *pNd->EndOfSectionNode() ));
+ return true;
+ }
+ }
+ }
+ }
+ else if( sCmp == u"region" )
+ {
+ sItem = sName; // Is being dealt with further down!
+ bContinue = true;
+ }
+ else if( sCmp == u"outline" )
+ {
+ SwPosition aPos( SwNodeIndex( m_rDoc.GetNodes() ));
+ if (m_rDoc.GotoOutline(aPos, sName, nullptr))
+ {
+ SwNode* pNd = &aPos.nNode.GetNode();
+ const int nLvl = pNd->GetTextNode()->GetAttrOutlineLevel()-1;
+
+ const SwOutlineNodes& rOutlNds = m_rDoc.GetNodes().GetOutLineNds();
+ SwOutlineNodes::size_type nTmpPos;
+ (void)rOutlNds.Seek_Entry( pNd, &nTmpPos );
+ rpRange.reset(new SwNodeRange( aPos.nNode, SwNodeOffset(0), aPos.nNode ));
+
+ // look for the section's end, now
+ for( ++nTmpPos;
+ nTmpPos < rOutlNds.size() &&
+ nLvl < rOutlNds[ nTmpPos ]->GetTextNode()->
+ GetAttrOutlineLevel()-1;
+ ++nTmpPos )
+ ; // there is no block
+
+ if( nTmpPos < rOutlNds.size() )
+ rpRange->aEnd = *rOutlNds[ nTmpPos ];
+ else
+ rpRange->aEnd = m_rDoc.GetNodes().GetEndOfContent();
+ return true;
+ }
+ }
+
+ if( !bContinue )
+ return false;
+ }
+
+ // search for bookmarks and sections case sensitive at first. If nothing is found then try again case insensitive
+ bool bCaseSensitive = true;
+ while( true )
+ {
+ ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*m_rDoc.getIDocumentMarkAccess(), sItem, bCaseSensitive);
+ if(pBkmk)
+ {
+ if(pBkmk->IsExpanded())
+ rpPam = new SwPaM(
+ pBkmk->GetMarkPos(),
+ pBkmk->GetOtherMarkPos());
+ return static_cast<bool>(rpPam);
+ }
+
+ FindItem aPara( bCaseSensitive ? sItem : rCC.lowercase( sItem ) );
+
+ if( !m_rDoc.GetSections().empty() )
+ {
+ for( const SwSectionFormat* pFormat : m_rDoc.GetSections() )
+ {
+ if (!(lcl_FindSection(pFormat, &aPara, bCaseSensitive)))
+ break;
+ }
+ if( aPara.pSectNd )
+ {
+ rpRange.reset(new SwNodeRange( *aPara.pSectNd, SwNodeOffset(1),
+ *aPara.pSectNd->EndOfSectionNode() ));
+ return true;
+
+ }
+ }
+ if( !bCaseSensitive )
+ break;
+ bCaseSensitive = false;
+ }
+ return false;
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentListItemsManager.cxx b/sw/source/core/doc/DocumentListItemsManager.cxx
new file mode 100644
index 000000000..2a8f0691d
--- /dev/null
+++ b/sw/source/core/doc/DocumentListItemsManager.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <DocumentListItemsManager.hxx>
+
+#include <SwNodeNum.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+
+namespace sw
+{
+
+DocumentListItemsManager::DocumentListItemsManager() : mpListItemsList( new tImplSortedNodeNumList ) // #i83479#
+{
+}
+
+bool DocumentListItemsManager::lessThanNodeNum::operator()( const SwNodeNum* pNodeNumOne,
+ const SwNodeNum* pNodeNumTwo ) const
+{
+ return pNodeNumOne->LessThan( *pNodeNumTwo );
+}
+
+void DocumentListItemsManager::addListItem( const SwNodeNum& rNodeNum )
+{
+ if ( mpListItemsList == nullptr )
+ {
+ return;
+ }
+
+ const bool bAlreadyInserted(
+ mpListItemsList->insert( &rNodeNum ).second );
+ OSL_ENSURE( bAlreadyInserted,
+ "<DocumentListItemsManager::addListItem(..)> - <SwNodeNum> instance already registered as numbered item!" );
+}
+
+void DocumentListItemsManager::removeListItem( const SwNodeNum& rNodeNum )
+{
+ if ( mpListItemsList == nullptr )
+ {
+ return;
+ }
+
+ const tImplSortedNodeNumList::size_type nDeleted = mpListItemsList->erase( &rNodeNum );
+ if ( nDeleted > 1 )
+ {
+ OSL_FAIL( "<DocumentListItemsManager::removeListItem(..)> - <SwNodeNum> was registered more than once as numbered item!" );
+ }
+}
+
+OUString DocumentListItemsManager::getListItemText(const SwNodeNum& rNodeNum,
+ SwRootFrame const& rLayout) const
+{
+ SwTextNode const*const pNode(rNodeNum.GetTextNode());
+ assert(pNode);
+ return sw::GetExpandTextMerged(&rLayout, *pNode, true, true, ExpandMode::ExpandFootnote);
+}
+
+bool DocumentListItemsManager::isNumberedInLayout(
+ SwNodeNum const& rNodeNum, // note: this is the non-hidden Num ...
+ SwRootFrame const& rLayout) const
+{
+ return sw::IsParaPropsNode(rLayout, *rNodeNum.GetTextNode());
+}
+
+void DocumentListItemsManager::getNumItems( tSortedNodeNumList& orNodeNumList ) const
+{
+ orNodeNumList.clear();
+ orNodeNumList.reserve( mpListItemsList->size() );
+
+ for ( const SwNodeNum* pNodeNum : *mpListItemsList )
+ {
+ if ( pNodeNum->IsCounted() &&
+ pNodeNum->GetTextNode() && pNodeNum->GetTextNode()->HasNumber() )
+ {
+ orNodeNumList.push_back( pNodeNum );
+ }
+ }
+}
+
+DocumentListItemsManager::~DocumentListItemsManager()
+{
+}
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentListsManager.cxx b/sw/source/core/doc/DocumentListsManager.cxx
new file mode 100644
index 000000000..16cb54071
--- /dev/null
+++ b/sw/source/core/doc/DocumentListsManager.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentListsManager.hxx>
+#include <doc.hxx>
+#include <list.hxx>
+#include <numrule.hxx>
+
+#include <comphelper/random.hxx>
+#include <osl/diagnose.h>
+
+
+namespace sw
+{
+
+DocumentListsManager::DocumentListsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+SwList* DocumentListsManager::createList( const OUString& rListId,
+ const OUString& sDefaultListStyleName )
+{
+ OUString sListId = rListId;
+ if ( sListId.isEmpty() )
+ {
+ sListId = CreateUniqueListId();
+ }
+
+ if ( getListByName( sListId ) )
+ {
+ OSL_FAIL( "<DocumentListsManager::createList(..)> - provided list id already used. Serious defect." );
+ return nullptr;
+ }
+
+ SwNumRule* pDefaultNumRuleForNewList = m_rDoc.FindNumRulePtr( sDefaultListStyleName );
+ if ( !pDefaultNumRuleForNewList )
+ {
+ OSL_FAIL( "<DocumentListsManager::createList(..)> - for provided default list style name no list style is found. Serious defect." );
+ return nullptr;
+ }
+
+ SwList* pNewList = new SwList( sListId, *pDefaultNumRuleForNewList, m_rDoc.GetNodes() );
+ maLists[sListId].reset(pNewList);
+
+ return pNewList;
+}
+
+SwList* DocumentListsManager::getListByName( const OUString& sListId ) const
+{
+ SwList* pList = nullptr;
+
+ auto aListIter = maLists.find( sListId );
+ if ( aListIter != maLists.end() )
+ {
+ pList = (*aListIter).second.get();
+ }
+
+ return pList;
+}
+
+void DocumentListsManager::createListForListStyle( const OUString& sListStyleName )
+{
+ if ( sListStyleName.isEmpty() )
+ {
+ OSL_FAIL( "<DocumentListsManager::createListForListStyle(..)> - no list style name provided. Serious defect." );
+ return;
+ }
+
+ if ( getListForListStyle( sListStyleName ) )
+ {
+ OSL_FAIL( "<DocumentListsManager::createListForListStyle(..)> - a list for the provided list style name already exists. Serious defect." );
+ return;
+ }
+
+ SwNumRule* pNumRule = m_rDoc.FindNumRulePtr( sListStyleName );
+ if ( !pNumRule )
+ {
+ OSL_FAIL( "<DocumentListsManager::createListForListStyle(..)> - for provided list style name no list style is found. Serious defect." );
+ return;
+ }
+
+ OUString sListId( pNumRule->GetDefaultListId() ); // can be empty String
+ if ( getListByName( sListId ) )
+ {
+ sListId.clear();
+ }
+ SwList* pNewList = createList( sListId, sListStyleName );
+ maListStyleLists[sListStyleName] = pNewList;
+ pNumRule->SetDefaultListId( pNewList->GetListId() );
+}
+
+SwList* DocumentListsManager::getListForListStyle( const OUString& sListStyleName ) const
+{
+ SwList* pList = nullptr;
+
+ std::unordered_map< OUString, SwList* >::const_iterator
+ aListIter = maListStyleLists.find( sListStyleName );
+ if ( aListIter != maListStyleLists.end() )
+ {
+ pList = (*aListIter).second;
+ }
+
+ return pList;
+}
+
+void DocumentListsManager::deleteListForListStyle( const OUString& sListStyleName )
+{
+ OUString sListId;
+ {
+ SwList* pList = getListForListStyle( sListStyleName );
+ OSL_ENSURE( pList,
+ "<DocumentListsManager::deleteListForListStyle(..)> - misusage of method: no list found for given list style name" );
+ if ( pList )
+ {
+ sListId = pList->GetListId();
+ }
+ }
+ if ( !sListId.isEmpty() )
+ {
+ maListStyleLists.erase( sListStyleName );
+ maLists.erase( sListId );
+ }
+}
+
+void DocumentListsManager::deleteListsByDefaultListStyle( const OUString& rListStyleName )
+{
+ auto aListIter = maLists.begin();
+ while ( aListIter != maLists.end() )
+ {
+ if ( (*aListIter).second->GetDefaultListStyleName() == rListStyleName )
+ {
+ aListIter = maLists.erase(aListIter);
+ }
+ else
+ ++aListIter;
+ }
+}
+
+void DocumentListsManager::trackChangeOfListStyleName( const OUString& sListStyleName,
+ const OUString& sNewListStyleName )
+{
+ SwList* pList = getListForListStyle( sListStyleName );
+ OSL_ENSURE( pList,
+ "<DocumentListsManager::changeOfListStyleName(..)> - misusage of method: no list found for given list style name" );
+
+ if ( pList != nullptr )
+ {
+ maListStyleLists.erase( sListStyleName );
+ maListStyleLists[sNewListStyleName] = pList;
+ }
+ for (auto & it : maLists) // tdf#91131 update these references too
+ {
+ if (it.second->GetDefaultListStyleName() == sListStyleName)
+ {
+ it.second->SetDefaultListStyleName(sNewListStyleName);
+ }
+ }
+}
+
+
+DocumentListsManager::~DocumentListsManager()
+{
+}
+
+
+OUString DocumentListsManager::MakeListIdUnique( const OUString& aSuggestedUniqueListId )
+{
+ tools::Long nHitCount = 0;
+ OUString aTmpStr = aSuggestedUniqueListId;
+ while ( getListByName( aTmpStr ) )
+ {
+ ++nHitCount;
+ aTmpStr = aSuggestedUniqueListId + OUString::number( nHitCount );
+ }
+
+ return aTmpStr;
+}
+
+OUString DocumentListsManager::CreateUniqueListId()
+{
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+ if (bHack)
+ {
+ static sal_Int64 nIdCounter = SAL_CONST_INT64(7000000000);
+ return MakeListIdUnique( OUString( "list" + OUString::number(nIdCounter++) ) );
+ }
+ else
+ {
+ // #i92478#
+ unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
+ std::numeric_limits<unsigned int>::max()));
+ OUString const aNewListId = "list" + OUString::number(n);
+ return MakeListIdUnique( aNewListId );
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentOutlineNodesManager.cxx b/sw/source/core/doc/DocumentOutlineNodesManager.cxx
new file mode 100644
index 000000000..8d0237727
--- /dev/null
+++ b/sw/source/core/doc/DocumentOutlineNodesManager.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentOutlineNodesManager.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <modeltoviewhelper.hxx>
+#include <rtl/ustrbuf.hxx>
+
+namespace sw
+{
+
+DocumentOutlineNodesManager::DocumentOutlineNodesManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+IDocumentOutlineNodes::tSortedOutlineNodeList::size_type DocumentOutlineNodesManager::getOutlineNodesCount() const
+{
+ return m_rDoc.GetNodes().GetOutLineNds().size();
+}
+
+int DocumentOutlineNodesManager::getOutlineLevel( const tSortedOutlineNodeList::size_type nIdx ) const
+{
+ return m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->
+ GetTextNode()->GetAttrOutlineLevel()-1;
+}
+
+OUString GetExpandTextMerged(SwRootFrame const*const pLayout,
+ SwTextNode const& rNode, bool const bWithNumber,
+ bool const bWithSpacesForLevel, ExpandMode const i_mode)
+{
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(rNode.getLayoutFrame(pLayout)));
+ if (pFrame)
+ {
+ sw::MergedPara const*const pMerged = pFrame->GetMergedPara();
+ if (pMerged)
+ {
+ if (&rNode != pMerged->pParaPropsNode)
+ {
+ return OUString();
+ }
+ else
+ {
+ ExpandMode const mode(ExpandMode::HideDeletions | i_mode);
+ OUStringBuffer ret(rNode.GetExpandText(pLayout, 0, -1,
+ bWithNumber, bWithNumber, bWithSpacesForLevel, mode));
+ for (SwNodeOffset i = rNode.GetIndex() + 1;
+ i <= pMerged->pLastNode->GetIndex(); ++i)
+ {
+ SwNode *const pTmp(rNode.GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+ ret.append(pTmp->GetTextNode()->GetExpandText(
+ pLayout, 0, -1, false, false, false, mode));
+ }
+ }
+ return ret.makeStringAndClear();
+ }
+ }
+ }
+ }
+ return rNode.GetExpandText(pLayout, 0, -1, bWithNumber,
+ bWithNumber, bWithSpacesForLevel, i_mode);
+}
+
+OUString DocumentOutlineNodesManager::getOutlineText(
+ const tSortedOutlineNodeList::size_type nIdx,
+ SwRootFrame const*const pLayout,
+ const bool bWithNumber,
+ const bool bWithSpacesForLevel,
+ const bool bWithFootnote ) const
+{
+ SwTextNode const*const pNode(m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->GetTextNode());
+ return GetExpandTextMerged(pLayout, *pNode,
+ bWithNumber, bWithSpacesForLevel,
+ (bWithFootnote ? ExpandMode::ExpandFootnote : ExpandMode(0)));
+}
+
+SwTextNode* DocumentOutlineNodesManager::getOutlineNode( const tSortedOutlineNodeList::size_type nIdx ) const
+{
+ return m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->GetTextNode();
+}
+
+bool DocumentOutlineNodesManager::isOutlineInLayout(
+ const tSortedOutlineNodeList::size_type nIdx,
+ SwRootFrame const& rLayout) const
+{
+ auto const pNode(m_rDoc.GetNodes().GetOutLineNds()[ nIdx ]->GetTextNode());
+ return sw::IsParaPropsNode(rLayout, *pNode);
+}
+
+void DocumentOutlineNodesManager::getOutlineNodes( IDocumentOutlineNodes::tSortedOutlineNodeList& orOutlineNodeList ) const
+{
+ orOutlineNodeList.clear();
+ orOutlineNodeList.reserve( getOutlineNodesCount() );
+
+ const tSortedOutlineNodeList::size_type nOutlCount = getOutlineNodesCount();
+ for ( tSortedOutlineNodeList::size_type i = 0; i < nOutlCount; ++i )
+ {
+ orOutlineNodeList.push_back(
+ m_rDoc.GetNodes().GetOutLineNds()[i]->GetTextNode() );
+ }
+}
+
+DocumentOutlineNodesManager::~DocumentOutlineNodesManager()
+{
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx b/sw/source/core/doc/DocumentRedlineManager.cxx
new file mode 100644
index 000000000..150d3e090
--- /dev/null
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -0,0 +1,3518 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+*/
+#include <DocumentRedlineManager.hxx>
+#include <frmfmt.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <fmtfld.hxx>
+#include <frmtool.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <redline.hxx>
+#include <UndoRedline.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <ftnidx.hxx>
+#include <authfld.hxx>
+#include <strings.hrc>
+#include <swmodule.hxx>
+#include <osl/diagnose.h>
+#include <editeng/prntitem.hxx>
+
+using namespace com::sun::star;
+
+#ifdef DBG_UTIL
+
+ #define ERROR_PREFIX "redline table corrupted: "
+
+ namespace
+ {
+ // helper function for lcl_CheckRedline
+ // 1. make sure that pPos->nContent points into pPos->nNode
+ // 2. check that position is valid and doesn't point after text
+ void lcl_CheckPosition( const SwPosition* pPos )
+ {
+ assert(dynamic_cast<SwIndexReg*>(&pPos->nNode.GetNode())
+ == pPos->nContent.GetIdxReg());
+
+ SwTextNode* pTextNode = pPos->nNode.GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ assert(pPos->nContent == 0);
+ }
+ else
+ {
+ assert(pPos->nContent >= 0 && pPos->nContent <= pTextNode->Len());
+ }
+ }
+
+ void lcl_CheckPam( const SwPaM* pPam )
+ {
+ assert(pPam);
+ lcl_CheckPosition( pPam->GetPoint() );
+ lcl_CheckPosition( pPam->GetMark() );
+ }
+
+ // check validity of the redline table. Checks redline bounds, and make
+ // sure the redlines are sorted and non-overlapping.
+ void lcl_CheckRedline( const IDocumentRedlineAccess& redlineAccess )
+ {
+ const SwRedlineTable& rTable = redlineAccess.GetRedlineTable();
+
+ // verify valid redline positions
+ for(SwRangeRedline* i : rTable)
+ lcl_CheckPam( i );
+
+ for(SwRangeRedline* j : rTable)
+ {
+ // check for empty redlines
+ // note: these can destroy sorting in SwTextNode::Update()
+ // if there's another one without mark on the same pos.
+ OSL_ENSURE( ( *(j->GetPoint()) != *(j->GetMark()) ) ||
+ ( j->GetContentIdx() != nullptr ),
+ ERROR_PREFIX "empty redline" );
+ }
+
+ // verify proper redline sorting
+ for( size_t n = 1; n < rTable.size(); ++n )
+ {
+ const SwRangeRedline* pPrev = rTable[ n-1 ];
+ const SwRangeRedline* pCurrent = rTable[ n ];
+
+ // check redline sorting
+ SAL_WARN_IF( *pPrev->Start() > *pCurrent->Start(), "sw",
+ ERROR_PREFIX "not sorted correctly" );
+
+ // check for overlapping redlines
+ SAL_WARN_IF( *pPrev->End() > *pCurrent->Start(), "sw",
+ ERROR_PREFIX "overlapping redlines" );
+ }
+
+ assert(std::is_sorted(rTable.begin(), rTable.end(), CompareSwRedlineTable()));
+ }
+ }
+
+ #define CHECK_REDLINE( pDoc ) lcl_CheckRedline( pDoc );
+
+#else
+
+ #define CHECK_REDLINE( pDoc )
+
+#endif
+
+namespace sw {
+
+static void UpdateFieldsForRedline(IDocumentFieldsAccess & rIDFA)
+{
+ auto const pAuthType(static_cast<SwAuthorityFieldType*>(rIDFA.GetFieldType(
+ SwFieldIds::TableOfAuthorities, OUString(), false)));
+ if (pAuthType) // created on demand...
+ {
+ pAuthType->DelSequenceArray();
+ }
+ rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
+ rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
+ rIDFA.UpdateExpFields(nullptr, false);
+ rIDFA.UpdateRefFields();
+}
+
+void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
+{
+ if (rDoc.IsClipBoard())
+ {
+ return;
+ }
+ // no need to call UpdateFootnoteNums for FTNNUM_PAGE:
+ // the AppendFootnote/RemoveFootnote will do it by itself!
+ rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
+ SwPosition currentStart(*rPam.Start());
+ SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
+ while (!pStartNode)
+ {
+ // note: branch only taken for redlines, not fieldmarks
+ SwStartNode *const pTableOrSectionNode(
+ currentStart.nNode.GetNode().IsTableNode()
+ ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
+ : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
+ if ( !pTableOrSectionNode )
+ {
+ SAL_WARN("sw.core", "UpdateFramesForAddDeleteRedline:: known pathology (or ChangesInRedline mode)");
+ return;
+ }
+ for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
+ {
+ pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ }
+ for (SwRootFrame const*const pLayout : rDoc.GetAllLayouts())
+ {
+ if (pLayout->HasMergedParas())
+ {
+ if (pTableOrSectionNode->IsTableNode())
+ {
+ static_cast<SwTableNode*>(pTableOrSectionNode)->DelFrames(pLayout);
+ }
+ else
+ {
+ static_cast<SwSectionNode*>(pTableOrSectionNode)->DelFrames(pLayout);
+ }
+ }
+ }
+ currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
+ currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
+ pStartNode = currentStart.nNode.GetNode().GetTextNode();
+ }
+ if (currentStart < *rPam.End())
+ {
+ SwTextNode * pNode(pStartNode);
+ do
+ {
+ // deleted text node: remove it from "hidden" list
+ // to update numbering in Show Changes mode
+ SwPosition aPos( *pNode, pNode->Len() );
+ if ( pNode->GetNumRule() && aPos < *rPam.End() )
+ pNode->RemoveFromListRLHidden();
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ // set anchored objects as deleted
+ pFrame->SetDrawObjsAsDeleted(true);
+ }
+ if (frames.empty())
+ {
+ auto const& layouts(rDoc.GetAllLayouts());
+ assert(std::none_of(layouts.begin(), layouts.end(),
+ [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
+ (void) layouts;
+ break;
+ }
+ auto eMode(sw::FrameMode::Existing);
+ SwTextNode * pLast(pNode);
+ for (SwTextFrame * pFrame : frames)
+ {
+ SwTextNode & rFirstNode(pFrame->GetMergedPara()
+ ? *pFrame->GetMergedPara()->pFirstNode
+ : *pNode);
+ assert(pNode == pStartNode
+ ? rFirstNode.GetIndex() <= pNode->GetIndex()
+ : &rFirstNode == pNode);
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ // the first node of the new redline is not necessarily the first
+ // node of the merged frame, there could be another redline nearby
+ sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, *pNode, nullptr);
+ // if redline is split across table and table cell is empty, there's no redline in the cell and so no merged para
+ if (pFrame->GetMergedPara())
+ {
+ pLast = const_cast<SwTextNode*>(pFrame->GetMergedPara()->pLastNode);
+ }
+ }
+ SwNodeIndex tmp(*pLast);
+ // skip over hidden sections!
+ pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
+ }
+ while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
+ }
+ // fields last - SwGetRefField::UpdateField requires up-to-date frames
+ UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
+
+ // update SwPostItMgr / notes in the margin
+ rDoc.GetDocShell()->Broadcast(
+ SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::REMOVED) );
+}
+
+void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam)
+{
+ // tdf#147006 fieldmark command may be empty => do not call AppendAllObjs()
+ if (rDoc.IsClipBoard() || *rPam.GetPoint() == *rPam.GetMark())
+ {
+ return;
+ }
+ bool isAppendObjsCalled(false);
+ rDoc.GetFootnoteIdxs().UpdateFootnote(rPam.Start()->nNode);
+ SwPosition currentStart(*rPam.Start());
+ SwTextNode * pStartNode(rPam.Start()->nNode.GetNode().GetTextNode());
+ while (!pStartNode)
+ {
+ // note: branch only taken for redlines, not fieldmarks
+ SwStartNode const*const pTableOrSectionNode(
+ currentStart.nNode.GetNode().IsTableNode()
+ ? static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetTableNode())
+ : static_cast<SwStartNode*>(currentStart.nNode.GetNode().GetSectionNode()));
+ assert(pTableOrSectionNode); // known pathology
+ for (SwNodeOffset j = pTableOrSectionNode->GetIndex(); j <= pTableOrSectionNode->EndOfSectionIndex(); ++j)
+ {
+ pTableOrSectionNode->GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::None);
+ }
+ if (rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())
+ {
+ // note: this will also create frames for all currently hidden flys
+ // because it calls AppendAllObjs
+ SwNodeIndex const end(*pTableOrSectionNode->EndOfSectionNode());
+ ::MakeFrames(&rDoc, currentStart.nNode, end);
+ isAppendObjsCalled = true;
+ }
+ currentStart.nNode = pTableOrSectionNode->EndOfSectionIndex() + 1;
+ currentStart.nContent.Assign(currentStart.nNode.GetNode().GetContentNode(), 0);
+ pStartNode = currentStart.nNode.GetNode().GetTextNode();
+ }
+ if (currentStart < *rPam.End())
+ {
+ SwTextNode * pNode(pStartNode);
+ do
+ {
+ // undeleted text node: add it to the "hidden" list
+ // to update numbering in Show Changes mode
+ SwPosition aPos( *pNode, pNode->Len() );
+ if ( pNode->GetNumRule() && aPos < *rPam.End() )
+ pNode->AddToListRLHidden();
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ // set anchored objects as not deleted
+ pFrame->SetDrawObjsAsDeleted(false);
+ }
+ if (frames.empty())
+ {
+ // in SwUndoSaveSection::SaveSection(), DelFrames() preceded this call
+ if (!pNode->FindTableBoxStartNode() && !pNode->FindFlyStartNode())
+ {
+ auto const& layouts(rDoc.GetAllLayouts());
+ assert(std::none_of(layouts.begin(), layouts.end(),
+ [](SwRootFrame const*const pLayout) { return pLayout->IsHideRedlines(); }));
+ (void) layouts;
+ }
+ isAppendObjsCalled = true; // skip that!
+ break;
+ }
+
+ // no nodes can be unmerged by this - skip MakeFrames() etc.
+ if (rPam.GetPoint()->nNode == rPam.GetMark()->nNode)
+ {
+ break; // continue with AppendAllObjs()
+ }
+
+ // first, call CheckParaRedlineMerge on the first paragraph,
+ // to init flag on new merge range (if any) + 1st node post the merge
+ auto eMode(sw::FrameMode::Existing);
+ SwTextNode * pLast(pNode);
+ for (SwTextFrame * pFrame : frames)
+ {
+ if (auto const pMergedPara = pFrame->GetMergedPara())
+ {
+ pLast = const_cast<SwTextNode*>(pMergedPara->pLastNode);
+ assert(pNode == pStartNode
+ ? pMergedPara->pFirstNode->GetIndex() <= pNode->GetIndex()
+ : pMergedPara->pFirstNode == pNode);
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ SwTextNode & rFirstNode(*pMergedPara->pFirstNode);
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ // update pNode so MakeFrames starts on 2nd node
+ pNode = &rFirstNode;
+ }
+ }
+ if (pLast != pNode)
+ {
+ // now start node until end of merge + 1 has proper flags; MakeFrames
+ // should pick up from the next node in need of frames by checking flags
+ SwNodeIndex const start(*pNode, +1);
+ SwNodeIndex const end(*pLast, +1); // end is exclusive
+ // note: this will also create frames for all currently hidden flys
+ // both on first and non-first nodes because it calls AppendAllObjs
+ ::MakeFrames(&rDoc, start, end);
+ isAppendObjsCalled = true;
+ // re-use this to move flys that are now on the wrong frame, with end
+ // of redline as "second" node; the nodes between start and end should
+ // be complete with MakeFrames already
+ sw::MoveMergedFlysAndFootnotes(frames, *pNode, *pLast, false);
+ }
+ SwNodeIndex tmp(*pLast);
+ // skip over hidden sections!
+ pNode = static_cast<SwTextNode*>(pLast->GetNodes().GoNextSection(&tmp, /*bSkipHidden=*/true, /*bSkipProtect=*/false));
+ }
+ while (pNode && pNode->GetIndex() <= rPam.End()->nNode.GetIndex());
+ }
+
+ if (!isAppendObjsCalled)
+ { // recreate flys in the one node the hard way...
+ for (auto const& pLayout : rDoc.GetAllLayouts())
+ {
+ if (pLayout->HasMergedParas())
+ {
+ AppendAllObjs(rDoc.GetSpzFrameFormats(), pLayout);
+ break;
+ }
+ }
+ }
+ // fields last - SwGetRefField::UpdateField requires up-to-date frames
+ UpdateFieldsForRedline(rDoc.getIDocumentFieldsAccess()); // after footnotes
+
+ // update SwPostItMgr / notes in the margin
+ rDoc.GetDocShell()->Broadcast(
+ SwFormatFieldHint(nullptr, SwFormatFieldHintWhich::INSERTED) );
+}
+
+} // namespace sw
+
+namespace
+{
+ bool IsPrevPos( const SwPosition & rPos1, const SwPosition & rPos2 )
+ {
+ const SwContentNode* pCNd;
+ if( 0 != rPos2.nContent.GetIndex() )
+ return false;
+ if( rPos2.nNode.GetIndex() - 1 != rPos1.nNode.GetIndex() )
+ return false;
+ pCNd = rPos1.nNode.GetNode().GetContentNode();
+ return pCNd && rPos1.nContent.GetIndex() == pCNd->Len();
+ }
+
+ // copy style or return with SwRedlineExtra_FormatColl with reject data of the upcoming copy
+ SwRedlineExtraData_FormatColl* lcl_CopyStyle( const SwPosition & rFrom, const SwPosition & rTo, bool bCopy = true )
+ {
+ SwTextNode* pToNode = rTo.nNode.GetNode().GetTextNode();
+ SwTextNode* pFromNode = rFrom.nNode.GetNode().GetTextNode();
+ if (pToNode != nullptr && pFromNode != nullptr && pToNode != pFromNode)
+ {
+ const SwPaM aPam(*pToNode);
+ SwDoc& rDoc = aPam.GetDoc();
+ // using Undo, copy paragraph style
+ SwTextFormatColl* pFromColl = pFromNode->GetTextColl();
+ SwTextFormatColl* pToColl = pToNode->GetTextColl();
+ if (bCopy && pFromColl != pToColl)
+ rDoc.SetTextFormatColl(aPam, pFromColl);
+
+ // using Undo, remove direct paragraph formatting of the "To" paragraph,
+ // and apply here direct paragraph formatting of the "From" paragraph
+ SfxItemSetFixed<
+ RES_PARATR_BEGIN, RES_PARATR_END - 3, // skip RSID and GRABBAG
+ RES_PARATR_LIST_BEGIN, RES_UL_SPACE, // skip PAGEDESC and BREAK
+ RES_CNTNT, RES_FRMATR_END - 1>
+ aTmp(rDoc.GetAttrPool());
+ SfxItemSet aTmp2(aTmp);
+
+ pToNode->GetParaAttr(aTmp, 0, 0);
+ pFromNode->GetParaAttr(aTmp2, 0, 0);
+
+ bool bSameSet = aTmp == aTmp2;
+
+ if (!bSameSet)
+ {
+ for( sal_uInt16 nItem = 0; nItem < aTmp.TotalCount(); ++nItem)
+ {
+ sal_uInt16 nWhich = aTmp.GetWhichByPos(nItem);
+ if( SfxItemState::SET == aTmp.GetItemState( nWhich, false ) &&
+ SfxItemState::SET != aTmp2.GetItemState( nWhich, false ) )
+ aTmp2.Put( aTmp.GetPool()->GetDefaultItem(nWhich), nWhich );
+ }
+ }
+
+ if (bCopy && !bSameSet)
+ rDoc.getIDocumentContentOperations().InsertItemSet(aPam, aTmp2);
+ else if (!bCopy && (!bSameSet || pFromColl != pToColl))
+ return new SwRedlineExtraData_FormatColl( pFromColl->GetName(), USHRT_MAX, &aTmp2 );
+ }
+ return nullptr;
+ }
+
+ // delete the empty tracked table row (i.e. if it's last tracked deletion was accepted)
+ void lcl_DeleteTrackedTableRow ( const SwPosition* pPos )
+ {
+ const SwTableBox* pBox = pPos->nNode.GetNode().GetTableBox();
+ if ( !pBox )
+ return;
+
+ const SwTableLine* pLine = pBox->GetUpper();
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ // empty table row with property "HasTextChangesOnly" = false
+ if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
+ {
+ if ( pLine->IsEmpty() )
+ {
+ SwCursor aCursor( *pPos, nullptr );
+ pPos->GetDoc().DeleteRow( aCursor );
+ }
+ else
+ {
+ // update property "HasTextChangesOnly"
+ SwRedlineTable::size_type nPos = 0;
+ (void)pLine->UpdateTextChangesOnly(nPos);
+ }
+ }
+ }
+
+ // at rejection of a deletion in a table, remove the tracking of the table row
+ // (also at accepting the last redline insertion of a tracked table row insertion)
+ void lcl_RemoveTrackingOfTableRow( const SwPosition* pPos, bool bRejectDeletion )
+ {
+ const SwTableBox* pBox = pPos->nNode.GetNode().GetTableBox();
+ if ( !pBox )
+ return;
+
+ const SwTableLine* pLine = pBox->GetUpper();
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ pLine->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ // table row property "HasTextChangesOnly" is set and its value is false
+ if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
+ {
+ bool bNoMoreInsertion = false;
+ if ( !bRejectDeletion )
+ {
+ SwRedlineTable::size_type nPos = 0;
+ SwRedlineTable::size_type nInsert = pLine->UpdateTextChangesOnly(nPos, /*bUpdateProperty=*/false);
+
+ if ( SwRedlineTable::npos == nInsert )
+ bNoMoreInsertion = true;
+ }
+ if ( bRejectDeletion || bNoMoreInsertion )
+ {
+ SvxPrintItem aUnsetTracking(RES_PRINT, true);
+ SwCursor aCursor( *pPos, nullptr );
+ pPos->GetDoc().SetRowNotTracked( aCursor, aUnsetTracking );
+ }
+ }
+ }
+
+ bool lcl_AcceptRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng = nullptr,
+ const SwPosition* pEndRng = nullptr )
+ {
+ bool bRet = true;
+ SwRangeRedline* pRedl = rArr[ rPos ];
+ SwPosition *pRStt = nullptr, *pREnd = nullptr;
+ SwComparePosition eCmp = SwComparePosition::Outside;
+ if( pSttRng && pEndRng )
+ {
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
+ }
+
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Format:
+ {
+ bool bCheck = false, bReplace = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( *pSttRng == *pRStt )
+ pRedl->SetStart( *pEndRng, pRStt );
+ else
+ {
+ if( *pEndRng != *pREnd )
+ {
+ // split up
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEndRng );
+ rArr.Insert( pNew ); ++rPos;
+ }
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ bool bInsert = RedlineType::Insert == pRedl->GetType();
+ SwPosition aPos(pRedl->Start()->nNode);
+ rArr.DeleteAndDestroy( rPos-- );
+
+ // remove tracking of the table row, if needed
+ if ( bInsert )
+ lcl_RemoveTrackingOfTableRow( &aPos, /*bRejectDelete=*/false );
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+
+ if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
+ {
+ // re-insert
+ rArr.Remove( pRedl );
+ rArr.Insert( pRedl );
+ }
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ SwDoc& rDoc = pRedl->GetDoc();
+ const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
+ bool bDelRedl = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ pDelStt = pSttRng;
+ pDelEnd = pEndRng;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( bCallDelete )
+ {
+ pDelStt = pRStt;
+ pDelEnd = pEndRng;
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ if( bCallDelete )
+ {
+ pDelStt = pREnd;
+ pDelEnd = pSttRng;
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ rArr.Remove( rPos-- );
+ bDelRedl = true;
+ if( bCallDelete )
+ {
+ pDelStt = pRedl->Start();
+ pDelEnd = pRedl->End();
+ }
+ }
+ break;
+ default:
+ bRet = false;
+ }
+
+ if( pDelStt && pDelEnd )
+ {
+ SwPaM aPam( *pDelStt, *pDelEnd );
+ SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+
+ // keep style of the empty paragraph after deletion of wholly paragraphs
+ if( pCSttNd && pCEndNd && pRStt && pREnd && pRStt->nContent == 0 )
+ lcl_CopyStyle(*pREnd, *pRStt);
+
+ if( bDelRedl )
+ delete pRedl;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ if( pCSttNd && pCEndNd )
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ lcl_DeleteTrackedTableRow( aPam.End() );
+ }
+ else if (pCSttNd && !pCEndNd)
+ {
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange(aPam);
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( bDelRedl )
+ delete pRedl;
+ }
+ break;
+
+ case RedlineType::FmtColl:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ case RedlineType::ParagraphFormat:
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ bool lcl_RejectRedline( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng = nullptr,
+ const SwPosition* pEndRng = nullptr )
+ {
+ bool bRet = true;
+ SwRangeRedline* pRedl = rArr[ rPos ];
+ SwDoc& rDoc = pRedl->GetDoc();
+ SwPosition *pRStt = nullptr, *pREnd = nullptr;
+ SwComparePosition eCmp = SwComparePosition::Outside;
+ if( pSttRng && pEndRng )
+ {
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ eCmp = ComparePosition( *pSttRng, *pEndRng, *pRStt, *pREnd );
+ }
+
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ {
+ const SwPosition *pDelStt = nullptr, *pDelEnd = nullptr;
+ bool bDelRedl = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ pDelStt = pSttRng;
+ pDelEnd = pEndRng;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( bCallDelete )
+ {
+ pDelStt = pRStt;
+ pDelEnd = pEndRng;
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ if( bCallDelete )
+ {
+ pDelStt = pREnd;
+ pDelEnd = pSttRng;
+ }
+ break;
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ // delete the range again
+ rArr.Remove( rPos-- );
+ bDelRedl = true;
+ if( bCallDelete )
+ {
+ pDelStt = pRedl->Start();
+ pDelEnd = pRedl->End();
+ }
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+ if( pDelStt && pDelEnd )
+ {
+ SwPaM aPam( *pDelStt, *pDelEnd );
+
+ SwContentNode* pCSttNd = pDelStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pDelEnd->nNode.GetNode().GetContentNode();
+
+ if( bDelRedl )
+ delete pRedl;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ if( pCSttNd && pCEndNd )
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ lcl_DeleteTrackedTableRow( aPam.End() );
+ }
+ else if (pCSttNd && !pCEndNd)
+ {
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ if (aPam.End()->nNode.GetNode().IsStartNode())
+ { // end node will be deleted too! see nNodeDiff+1
+ --aPam.End()->nNode;
+ }
+ assert(!aPam.End()->nNode.GetNode().IsStartNode());
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange(aPam);
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( bDelRedl )
+ delete pRedl;
+ }
+ break;
+ case RedlineType::Delete:
+ {
+ SwRangeRedline* pNew = nullptr;
+ bool bCheck = false, bReplace = false;
+ SwPaM const updatePaM(pSttRng ? *pSttRng : *pRedl->Start(),
+ pEndRng ? *pEndRng : *pRedl->End());
+
+ if( pRedl->GetExtraData() )
+ pRedl->GetExtraData()->Reject( *pRedl );
+
+ // remove tracking of the table row, if needed
+ lcl_RemoveTrackingOfTableRow( updatePaM.End(), /*bRejectDelete=*/true );
+
+ switch( eCmp )
+ {
+ case SwComparePosition::Inside:
+ {
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ if( *pSttRng == *pRStt )
+ {
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ }
+ else
+ {
+ if( *pEndRng != *pREnd )
+ {
+ // split up
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEndRng );
+ rArr.Insert( pCpy ); ++rPos;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ }
+
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ if( pNew )
+ pNew->SetStart( *pSttRng );
+ }
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ pRedl->SetStart( *pEndRng, pRStt );
+ bReplace = true;
+ if( pNew )
+ pNew->SetEnd( *pEndRng );
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ if( 1 < pRedl->GetStackCount() )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PopData();
+ }
+ pRedl->SetEnd( *pSttRng, pREnd );
+ bCheck = true;
+ if( pNew )
+ pNew->SetStart( *pSttRng );
+ break;
+
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ if( !pRedl->PopData() )
+ // deleting the RedlineObject is enough
+ rArr.DeleteAndDestroy( rPos-- );
+ break;
+
+ default:
+ bRet = false;
+ }
+
+ if( pNew )
+ {
+ rArr.Insert( pNew ); ++rPos;
+ }
+
+ if( bReplace || ( bCheck && !pRedl->HasValidRange() ))
+ {
+ // re-insert
+ rArr.Remove( pRedl );
+ rArr.Insert( pRedl );
+ }
+
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, updatePaM);
+ }
+ break;
+
+ case RedlineType::Format:
+ case RedlineType::FmtColl:
+ case RedlineType::ParagraphFormat:
+ {
+ // tdf#52391 instead of hidden acception at the requested
+ // rejection, remove direct text formatting to get the potential
+ // original state of the text (FIXME if the original text
+ // has already contained direct text formatting: unfortunately
+ // ODF 1.2 doesn't support rejection of format-only changes)
+ if ( pRedl->GetType() == RedlineType::Format )
+ {
+ SwPaM aPam( *(pRedl->Start()), *(pRedl->End()) );
+ rDoc.ResetAttrs(aPam);
+ }
+ else if ( pRedl->GetType() == RedlineType::ParagraphFormat )
+ {
+ // handle paragraph formatting changes
+ // (range is only a full paragraph or a part of it)
+ const SwPosition* pStt = pRedl->Start();
+ SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ // expand range to the whole paragraph
+ // and reset only the paragraph attributes
+ SwPaM aPam( *pTNd, pTNd->GetText().getLength() );
+ o3tl::sorted_vector<sal_uInt16> aResetAttrsArray;
+
+ constexpr std::pair<sal_uInt16, sal_uInt16> aResetableSetRange[] = {
+ { RES_PARATR_BEGIN, RES_PARATR_END - 1 },
+ { RES_PARATR_LIST_BEGIN, RES_FRMATR_END - 1 },
+ };
+
+ for (const auto& [nBegin, nEnd] : aResetableSetRange)
+ {
+ for (sal_uInt16 i = nBegin; i <= nEnd; ++i)
+ aResetAttrsArray.insert( i );
+ }
+
+ rDoc.ResetAttrs(aPam, false, aResetAttrsArray);
+
+ // remove numbering
+ if ( pTNd->GetNumRule() )
+ rDoc.DelNumRules(aPam);
+ }
+ }
+
+ if( pRedl->GetExtraData() )
+ pRedl->GetExtraData()->Reject( *pRedl );
+
+ rArr.DeleteAndDestroy( rPos-- );
+ }
+ break;
+
+ default:
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ typedef bool (*Fn_AcceptReject)( SwRedlineTable& rArr, SwRedlineTable::size_type& rPos,
+ bool bCallDelete,
+ const SwPosition* pSttRng,
+ const SwPosition* pEndRng);
+
+
+ int lcl_AcceptRejectRedl( Fn_AcceptReject fn_AcceptReject,
+ SwRedlineTable& rArr, bool bCallDelete,
+ const SwPaM& rPam)
+ {
+ SwRedlineTable::size_type n = 0;
+ int nCount = 0;
+
+ const SwPosition* pStt = rPam.Start(),
+ * pEnd = rPam.End();
+ const SwRangeRedline* pFnd = rArr.FindAtPosition( *pStt, n );
+ if( pFnd && // Is new a part of it?
+ ( *pFnd->Start() != *pStt || *pFnd->End() > *pEnd ))
+ {
+ // Only revoke the partial selection
+ if( (*fn_AcceptReject)( rArr, n, bCallDelete, pStt, pEnd ))
+ nCount++;
+ ++n;
+ }
+
+ // tdf#119824 first we will accept only overlapping paragraph format changes
+ // in the first loop to avoid potential content changes during Redo
+ bool bHasParagraphFormatChange = false;
+ for( int m = 0 ; m < 2 && !bHasParagraphFormatChange; ++m )
+ {
+ for(SwRedlineTable::size_type o = n ; o < rArr.size(); ++o )
+ {
+ SwRangeRedline* pTmp = rArr[ o ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if( *pTmp->End() <= *pEnd )
+ {
+ if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
+ (*fn_AcceptReject)( rArr, o, bCallDelete, nullptr, nullptr ))
+ {
+ bHasParagraphFormatChange = true;
+ nCount++;
+ }
+ }
+ else
+ {
+ if( *pTmp->Start() < *pEnd )
+ {
+ // Only revoke the partial selection
+ if( (m > 0 || RedlineType::ParagraphFormat == pTmp->GetType()) &&
+ (*fn_AcceptReject)( rArr, o, bCallDelete, pStt, pEnd ))
+ {
+ bHasParagraphFormatChange = true;
+ nCount++;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return nCount;
+ }
+
+ void lcl_AdjustRedlineRange( SwPaM& rPam )
+ {
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPosition* pStt = rPam.Start(),
+ * pEnd = rPam.End();
+ SwDoc& rDoc = rPam.GetDoc();
+ if( !pStt->nContent.GetIndex() &&
+ !rDoc.GetNodes()[ pStt->nNode.GetIndex() - 1 ]->IsContentNode() )
+ {
+ const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pStt, nullptr );
+ if( pRedl )
+ {
+ const SwPosition* pRStt = pRedl->Start();
+ if( !pRStt->nContent.GetIndex() && pRStt->nNode.GetIndex() ==
+ pStt->nNode.GetIndex() - 1 )
+ *pStt = *pRStt;
+ }
+ }
+ if( pEnd->nNode.GetNode().IsContentNode() &&
+ !rDoc.GetNodes()[ pEnd->nNode.GetIndex() + 1 ]->IsContentNode() &&
+ pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() )
+ {
+ const SwRangeRedline* pRedl = rDoc.getIDocumentRedlineAccess().GetRedline( *pEnd, nullptr );
+ if( pRedl )
+ {
+ const SwPosition* pREnd = pRedl->End();
+ if( !pREnd->nContent.GetIndex() && pREnd->nNode.GetIndex() ==
+ pEnd->nNode.GetIndex() + 1 )
+ *pEnd = *pREnd;
+ }
+ }
+ }
+
+ /// in case some text is deleted, ensure that the not-yet-inserted
+ /// SwRangeRedline has its positions corrected not to point to deleted node
+ class TemporaryRedlineUpdater
+ {
+ private:
+ SwRangeRedline & m_rRedline;
+ std::shared_ptr<SwUnoCursor> m_pCursor;
+ public:
+ TemporaryRedlineUpdater(SwDoc & rDoc, SwRangeRedline & rRedline)
+ : m_rRedline(rRedline)
+ , m_pCursor(rDoc.CreateUnoCursor(*rRedline.GetPoint(), false))
+ {
+ if (m_rRedline.HasMark())
+ {
+ m_pCursor->SetMark();
+ *m_pCursor->GetMark() = *m_rRedline.GetMark();
+ *m_rRedline.GetMark() = SwPosition(rDoc.GetNodes().GetEndOfContent());
+ }
+ *m_rRedline.GetPoint() = SwPosition(rDoc.GetNodes().GetEndOfContent());
+ }
+ ~TemporaryRedlineUpdater()
+ {
+ static_cast<SwPaM&>(m_rRedline) = *m_pCursor;
+ }
+ };
+}
+
+namespace sw
+{
+
+DocumentRedlineManager::DocumentRedlineManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , meRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete)
+ , mbIsRedlineMove(false)
+ , mnAutoFormatRedlnCommentNo(0)
+{
+}
+
+RedlineFlags DocumentRedlineManager::GetRedlineFlags() const
+{
+ return meRedlineFlags;
+}
+
+void DocumentRedlineManager::SetRedlineFlags( RedlineFlags eMode )
+{
+ if( meRedlineFlags == eMode )
+ return;
+
+ if( (RedlineFlags::ShowMask & meRedlineFlags) != (RedlineFlags::ShowMask & eMode)
+ || !(RedlineFlags::ShowMask & eMode) )
+ {
+ bool bSaveInXMLImportFlag = m_rDoc.IsInXMLImport();
+ m_rDoc.SetInXMLImport( false );
+ // and then hide/display everything
+ void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool); // Allow compiler warn if use of
+ // uninitialized ptr is possible
+
+ RedlineFlags eShowMode = RedlineFlags::ShowMask & eMode;
+ if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ pFnc = &SwRangeRedline::Show;
+ else if (eShowMode == RedlineFlags::ShowInsert)
+ pFnc = &SwRangeRedline::Hide;
+ else if (eShowMode == RedlineFlags::ShowDelete)
+ pFnc = &SwRangeRedline::ShowOriginal;
+ else
+ {
+ pFnc = &SwRangeRedline::Hide;
+ eMode |= RedlineFlags::ShowInsert;
+ }
+
+ CheckAnchoredFlyConsistency(m_rDoc);
+ CHECK_REDLINE( *this )
+
+ o3tl::sorted_vector<SwRootFrame *> hiddenLayouts;
+ if (eShowMode == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ {
+ // sw_redlinehide: the problem here is that MoveFromSection
+ // creates the frames wrongly (non-merged), because its own
+ // SwRangeRedline has wrong positions until after the nodes
+ // are all moved, so fix things up by force by re-creating
+ // all merged frames from scratch.
+ o3tl::sorted_vector<SwRootFrame *> const layouts(m_rDoc.GetAllLayouts());
+ for (SwRootFrame *const pLayout : layouts)
+ {
+ if (pLayout->IsHideRedlines())
+ {
+ pLayout->SetHideRedlines(false);
+ hiddenLayouts.insert(pLayout);
+ }
+ }
+ }
+
+ for (sal_uInt16 nLoop = 1; nLoop <= 2; ++nLoop)
+ for (size_t i = 0; i < maRedlineTable.size(); ++i)
+ {
+ SwRangeRedline *const pRedline = maRedlineTable[i];
+ (pRedline->*pFnc)(nLoop, i, false);
+ while (maRedlineTable.size() <= i
+ || maRedlineTable[i] != pRedline)
+ { // ensure current position
+ --i; // a previous redline may have been deleted
+ }
+ }
+
+ //SwRangeRedline::MoveFromSection routinely changes
+ //the keys that mpRedlineTable is sorted by
+ maRedlineTable.Resort();
+
+ CheckAnchoredFlyConsistency(m_rDoc);
+ CHECK_REDLINE( *this )
+
+ for (SwRootFrame *const pLayout : hiddenLayouts)
+ {
+ pLayout->SetHideRedlines(true);
+ }
+
+ m_rDoc.SetInXMLImport( bSaveInXMLImportFlag );
+ }
+ meRedlineFlags = eMode;
+ m_rDoc.getIDocumentState().SetModified();
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::IsRedlineOn() const
+{
+ return IDocumentRedlineAccess::IsRedlineOn(meRedlineFlags);
+}
+
+bool DocumentRedlineManager::IsIgnoreRedline() const
+{
+ return bool(RedlineFlags::Ignore & meRedlineFlags);
+}
+
+void DocumentRedlineManager::SetRedlineFlags_intern(RedlineFlags eMode)
+{
+ meRedlineFlags = eMode;
+}
+
+const SwRedlineTable& DocumentRedlineManager::GetRedlineTable() const
+{
+ return maRedlineTable;
+}
+
+SwRedlineTable& DocumentRedlineManager::GetRedlineTable()
+{
+ return maRedlineTable;
+}
+
+const SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable() const
+{
+ return maExtraRedlineTable;
+}
+
+SwExtraRedlineTable& DocumentRedlineManager::GetExtraRedlineTable()
+{
+ return maExtraRedlineTable;
+}
+
+bool DocumentRedlineManager::IsInRedlines(const SwNode & rNode) const
+{
+ SwPosition aPos(rNode);
+ SwNode & rEndOfRedlines = m_rDoc.GetNodes().GetEndOfRedlines();
+ SwPaM aPam(SwPosition(*rEndOfRedlines.StartOfSectionNode()),
+ SwPosition(rEndOfRedlines));
+
+ return aPam.ContainsPosition(aPos);
+}
+
+bool DocumentRedlineManager::IsRedlineMove() const
+{
+ return mbIsRedlineMove;
+}
+
+void DocumentRedlineManager::SetRedlineMove(bool bFlag)
+{
+ mbIsRedlineMove = bFlag;
+}
+
+/*
+Text means Text not "polluted" by Redlines.
+
+Behaviour of Insert-Redline:
+ - in the Text - insert Redline Object
+ - in InsertRedline (own) - ignore, existing is extended
+ - in InsertRedline (others) - split up InsertRedline and
+ insert Redline Object
+ - in DeleteRedline - split up DeleteRedline or
+ move at the end/beginning
+
+Behaviour of Delete-Redline:
+ - in the Text - insert Redline Object
+ - in DeleteRedline (own/others) - ignore
+ - in InsertRedline (own) - ignore, but delete character
+ - in InsertRedline (others) - split up InsertRedline and
+ insert Redline Object
+ - Text and own Insert overlap - delete Text in the own Insert,
+ extend in the other Text
+ (up to the Insert!)
+ - Text and other Insert overlap - insert Redline Object, the
+ other Insert is overlapped by
+ the Delete
+*/
+IDocumentRedlineAccess::AppendResult
+DocumentRedlineManager::AppendRedline(SwRangeRedline* pNewRedl, bool const bCallDelete)
+{
+ CHECK_REDLINE( *this )
+
+ if (!IsRedlineOn() || IsShowOriginal(meRedlineFlags))
+ {
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ CHECK_REDLINE( *this )
+ return AppendResult::IGNORED;
+ }
+
+
+ bool bMerged = false;
+
+ pNewRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+
+ if( m_rDoc.IsAutoFormatRedline() )
+ {
+ pNewRedl->SetAutoFormat();
+ if( moAutoFormatRedlnComment && !moAutoFormatRedlnComment->isEmpty() )
+ {
+ pNewRedl->SetComment( *moAutoFormatRedlnComment );
+ pNewRedl->SetSeqNo( mnAutoFormatRedlnCommentNo );
+ }
+ }
+
+ SwPosition* pStt = pNewRedl->Start(),
+ * pEnd = pNewRedl->End();
+ {
+ SwTextNode* pTextNode = pStt->nNode.GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ if( pStt->nContent > 0 )
+ {
+ OSL_ENSURE( false, "Redline start: non-text-node with content" );
+ pStt->nContent = 0;
+ }
+ }
+ else
+ {
+ if( pStt->nContent > pTextNode->Len() )
+ {
+ OSL_ENSURE( false, "Redline start: index after text" );
+ pStt->nContent = pTextNode->Len();
+ }
+ }
+ pTextNode = pEnd->nNode.GetNode().GetTextNode();
+ if( pTextNode == nullptr )
+ {
+ if( pEnd->nContent > 0 )
+ {
+ OSL_ENSURE( false, "Redline end: non-text-node with content" );
+ pEnd->nContent = 0;
+ }
+ }
+ else
+ {
+ if( pEnd->nContent > pTextNode->Len() )
+ {
+ OSL_ENSURE( false, "Redline end: index after text" );
+ pEnd->nContent = pTextNode->Len();
+ }
+ }
+ }
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ { // Do not insert empty redlines
+ delete pNewRedl;
+ return AppendResult::IGNORED;
+ }
+ bool bCompress = false;
+ SwRedlineTable::size_type n = 0;
+ // look up the first Redline for the starting position
+ if( !GetRedline( *pStt, &n ) && n )
+ --n;
+ bool bDec = false;
+
+ for( ; pNewRedl && n < maRedlineTable.size(); bDec ? n : ++n )
+ {
+ bDec = false;
+
+ SwRangeRedline* pRedl = maRedlineTable[ n ];
+ SwPosition* pRStt = pRedl->Start(),
+ * pREnd = pRedl->End();
+
+ // #i8518# remove empty redlines while we're at it
+ if( ( *pRStt == *pREnd ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ maRedlineTable.DeleteAndDestroy(n);
+ continue;
+ }
+
+ SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd );
+
+ switch( pNewRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ // don't join inserted characters with moved text
+ !pRedl->IsMoved() )
+ {
+ bool bDelete = false;
+
+ // Merge if applicable?
+ if( (( SwComparePosition::Behind == eCmpPos &&
+ IsPrevPos( *pREnd, *pStt ) ) ||
+ ( SwComparePosition::CollideStart == eCmpPos ) ||
+ ( SwComparePosition::OverlapBehind == eCmpPos ) ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ ( n+1 >= maRedlineTable.size() ||
+ ( *maRedlineTable[ n+1 ]->Start() >= *pEnd &&
+ *maRedlineTable[ n+1 ]->Start() != *pREnd ) ) )
+ {
+ pRedl->SetEnd( *pEnd, pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ }
+
+ bMerged = true;
+ bDelete = true;
+ }
+ else if( (( SwComparePosition::Before == eCmpPos &&
+ IsPrevPos( *pEnd, *pRStt ) ) ||
+ ( SwComparePosition::CollideEnd == eCmpPos ) ||
+ ( SwComparePosition::OverlapBefore == eCmpPos ) ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ ( !n ||
+ *maRedlineTable[ n-1 ]->End() != *pRStt ))
+ {
+ pRedl->SetStart( *pStt, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+
+ bMerged = true;
+ bDelete = true;
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // own insert-over-insert redlines:
+ // just scrap the inside ones
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ {
+ *pStt = *pREnd;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ bDelete = true;
+ }
+ else if( SwComparePosition::OverlapBefore == eCmpPos )
+ {
+ *pEnd = *pRStt;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ bDelete = true;
+ }
+ else if( SwComparePosition::Inside == eCmpPos )
+ {
+ bDelete = true;
+ bMerged = true;
+ }
+ else if( SwComparePosition::Equal == eCmpPos )
+ bDelete = true;
+
+ if( bDelete )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+
+ // set IsMoved checking nearby redlines
+ if (n < maRedlineTable.size()) // in case above 're-insert' failed
+ maRedlineTable.isMoved(n);
+ }
+ }
+ else if( SwComparePosition::Inside == eCmpPos )
+ {
+ // split up
+ if( *pEnd != *pREnd )
+ {
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ maRedlineTable.Insert( pCpy );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ }
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+
+ // split up the new redline, since it covers the
+ // existing redline. Insert the first part, and
+ // progress with the remainder as usual
+ SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
+ pSplit->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd );
+ maRedlineTable.Insert( pSplit );
+ if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if ( SwComparePosition::OverlapBehind == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+ pNewRedl->SetStart( *pREnd );
+ }
+ else if ( SwComparePosition::OverlapBefore == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+ *pEnd = *pRStt;
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ break;
+ case RedlineType::Delete:
+ if( SwComparePosition::Inside == eCmpPos )
+ {
+ // split up
+ if( *pEnd != *pREnd )
+ {
+ SwRangeRedline* pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ maRedlineTable.Insert( pCpy );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ }
+ }
+ else if ( SwComparePosition::Outside == eCmpPos )
+ {
+ // handle overlapping redlines in broken documents
+
+ // split up the new redline, since it covers the
+ // existing redline. Insert the first part, and
+ // progress with the remainder as usual
+ SwRangeRedline* pSplit = new SwRangeRedline( *pNewRedl );
+ pSplit->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd );
+ maRedlineTable.Insert( pSplit );
+ if( *pStt == *pEnd && pNewRedl->GetContentIdx() == nullptr )
+ {
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ }
+ }
+ else if ( SwComparePosition::Equal == eCmpPos )
+ {
+ // handle identical redlines in broken documents
+ // delete old (delete) redline
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if ( SwComparePosition::OverlapBehind == eCmpPos )
+ { // Another workaround for broken redlines
+ pNewRedl->SetStart( *pREnd );
+ }
+ break;
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pStt, pREnd );
+ if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::Inside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if( *pEnd != *pREnd )
+ {
+ if( *pEnd != *pRStt )
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ if( *pStt == *pRStt && pRedl->GetContentIdx() == nullptr )
+ maRedlineTable.DeleteAndDestroy( n );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Delete:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Delete:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Outside:
+ {
+ // Overlaps the current one completely,
+ // split the new one
+ if (*pEnd == *pREnd)
+ {
+ pNewRedl->SetEnd(*pRStt, pEnd);
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetStart( *pREnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ case SwComparePosition::Equal:
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ bCompress = true;
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::OverlapBehind:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pREnd, pStt );
+ else
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::CollideEnd:
+ if (pRStt->nContent != 0
+ && pRStt->nNode != pREnd->nNode)
+ { // tdf#147466 HACK: don't combine in this case to avoid the tdf#119571 code from *undeleting* section nodes
+ break;
+ }
+ [[fallthrough]];
+ case SwComparePosition::CollideStart:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) )
+ {
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ // Before we can merge, we make it visible!
+ // We insert temporarily so that pNew is
+ // also dealt with when moving the indices.
+ maRedlineTable.Insert(pNewRedl);
+ pRedl->Show(0, maRedlineTable.GetPos(pRedl));
+ maRedlineTable.Remove( pNewRedl );
+ pRStt = pRedl->Start();
+ pREnd = pRedl->End();
+ }
+
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::CollideStart == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+
+ // delete current (below), and restart process with
+ // previous
+ SwRedlineTable::size_type nToBeDeleted = n;
+ bDec = true;
+
+ if( *(pNewRedl->Start()) <= *pREnd )
+ {
+ // Whoooah, we just extended the new 'redline'
+ // beyond previous redlines, so better start
+ // again. Of course this is not supposed to
+ // happen, and in an ideal world it doesn't,
+ // but unfortunately this code is buggy and
+ // totally rotten so it does happen and we
+ // better fix it.
+ n = 0;
+ bDec = true;
+ }
+
+ maRedlineTable.DeleteAndDestroy( nToBeDeleted );
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Insert:
+ {
+ // b62341295: Do not throw away redlines
+ // even if they are not allowed to be combined
+ RedlineFlags eOld = meRedlineFlags;
+ if( !( eOld & RedlineFlags::DontCombineRedlines ) &&
+ pRedl->IsOwnRedline( *pNewRedl ) &&
+ // tdf#116084 tdf#121176 don't combine anonymized deletion
+ // and anonymized insertion, i.e. with the same dummy timestamp
+ !pRedl->GetRedlineData(0).IsAnonymized() )
+ {
+
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore);
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Equal:
+ bCompress = true;
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ [[fallthrough]];
+
+ case SwComparePosition::Inside:
+ if( bCallDelete )
+ {
+ // DeleteAndJoin does not yield the
+ // desired result if there is no paragraph to
+ // join with, i.e. at the end of the document.
+ // For this case, we completely delete the
+ // paragraphs (if, of course, we also start on
+ // a paragraph boundary).
+ if( (pStt->nContent == 0) &&
+ pEnd->nNode.GetNode().IsEndNode() )
+ {
+ pEnd->nNode--;
+ pEnd->nContent.Assign(
+ pEnd->nNode.GetNode().GetTextNode(), 0);
+ m_rDoc.getIDocumentContentOperations().DelFullPara( *pNewRedl );
+ }
+ else
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pNewRedl );
+
+ bCompress = true;
+ }
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ break;
+
+ case SwComparePosition::Outside:
+ {
+ maRedlineTable.Remove( n );
+ bDec = true;
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( *pRedl );
+ n = 0; // re-initialize
+ }
+ delete pRedl;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ {
+ SwPaM aPam( *pRStt, *pEnd );
+
+ if( *pEnd == *pREnd )
+ maRedlineTable.DeleteAndDestroy( n );
+ else
+ {
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ }
+
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ n = 0; // re-initialize
+ }
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ {
+ SwPaM aPam( *pStt, *pREnd );
+
+ if( *pStt == *pRStt )
+ {
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+
+ if( bCallDelete )
+ {
+ TemporaryRedlineUpdater const u(m_rDoc, *pNewRedl);
+ m_rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ meRedlineFlags = eOld;
+ }
+ else
+ {
+ // it may be necessary to split the existing redline in
+ // two. In this case, pRedl will be changed to cover
+ // only part of its former range, and pNew will cover
+ // the remainder.
+ SwRangeRedline* pNew = nullptr;
+
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Equal:
+ {
+ pRedl->PushData( *pNewRedl );
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
+ }
+ bCompress = true;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ {
+ if( *pRStt == *pStt )
+ {
+ // #i97421#
+ // redline w/out extent loops
+ if (*pStt != *pEnd)
+ {
+ pNewRedl->PushData( *pRedl, false );
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ }
+ }
+ else
+ {
+ pNewRedl->PushData( *pRedl, false );
+ if( *pREnd != *pEnd )
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ }
+ pRedl->SetEnd( *pStt, pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ }
+ }
+ }
+ break;
+
+ case SwComparePosition::Outside:
+ {
+ pRedl->PushData( *pNewRedl );
+ if( *pEnd == *pREnd )
+ {
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetEnd( *pRStt );
+ pNewRedl->SetStart( *pREnd, pStt );
+ }
+ bCompress = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ {
+ if( *pEnd == *pREnd )
+ {
+ pRedl->PushData( *pNewRedl );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ maRedlineTable.Insert(pNewRedl);
+ pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
+ maRedlineTable.Remove( pNewRedl );
+ }
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PushData( *pNewRedl );
+ pNew->SetEnd( *pEnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ pRedl->SetStart( *pNew->End(), pRStt ) ;
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ bDec = true;
+ }
+ }
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ {
+ if( *pStt == *pRStt )
+ {
+ pRedl->PushData( *pNewRedl );
+ pNewRedl->SetStart( *pREnd, pStt );
+ if( IsHideChanges( meRedlineFlags ))
+ {
+ maRedlineTable.Insert( pNewRedl );
+ pRedl->Hide(0, maRedlineTable.GetPos(pRedl));
+ maRedlineTable.Remove( pNewRedl );
+ }
+ }
+ else
+ {
+ pNew = new SwRangeRedline( *pRedl );
+ pNew->PushData( *pNewRedl );
+ pNew->SetStart( *pStt );
+ pNewRedl->SetStart( *pREnd, pStt );
+ pRedl->SetEnd( *pNew->Start(), pREnd );
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ // insert the pNew part (if it exists)
+ if( pNew )
+ {
+ maRedlineTable.Insert( pNew );
+
+ // pNew must be deleted if Insert() wasn't
+ // successful. But that can't happen, since pNew is
+ // part of the original pRedl redline.
+ // OSL_ENSURE( bRet, "Can't insert existing redline?" );
+
+ // restart (now with pRedl being split up)
+ n = 0;
+ bDec = true;
+ }
+ }
+ }
+ break;
+
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ break;
+
+ case SwComparePosition::Inside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if( *pEnd != *pREnd )
+ {
+ if( *pEnd != *pRStt )
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ if( ( *pStt == *pRStt ) &&
+ ( pRedl->GetContentIdx() == nullptr ) )
+ maRedlineTable.DeleteAndDestroy( n );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ }
+ else
+ pRedl->SetEnd( *pStt, pREnd );
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::Format:
+ switch( pRedl->GetType() )
+ {
+ case RedlineType::Insert:
+ case RedlineType::Delete:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::OverlapBefore:
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pNewRedl->SetStart( *pREnd, pStt );
+ break;
+
+ case SwComparePosition::Equal:
+ case SwComparePosition::Inside:
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ break;
+
+ case SwComparePosition::Outside:
+ // Overlaps the current one completely,
+ // split or shorten the new one
+ if (*pEnd == *pREnd)
+ {
+ pNewRedl->SetEnd(*pRStt, pEnd);
+ }
+ else if (*pStt == *pRStt)
+ {
+ pNewRedl->SetStart(*pREnd, pStt);
+ }
+ else
+ {
+ SwRangeRedline* pNew = new SwRangeRedline( *pNewRedl );
+ pNew->SetStart( *pREnd );
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case RedlineType::Format:
+ switch( eCmpPos )
+ {
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ // Overlaps the current one completely or has the
+ // same dimension, delete the old one
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // own one can be ignored completely
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ else if( *pREnd == *pEnd )
+ // or else only shorten the current one
+ pRedl->SetEnd( *pStt, pREnd );
+ else if( *pRStt == *pStt )
+ {
+ // or else only shorten the current one
+ pRedl->SetStart( *pEnd, pRStt );
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl, n );
+ bDec = true;
+ }
+ else
+ {
+ // If it lies completely within the current one
+ // we need to split it
+ SwRangeRedline* pNew = new SwRangeRedline( *pRedl );
+ pNew->SetStart( *pEnd );
+ pRedl->SetEnd( *pStt, pREnd );
+ AppendRedline( pNew, bCallDelete );
+ n = 0; // re-initialize
+ bDec = true;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::OverlapBehind:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pRStt, pStt );
+ else
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = false;
+ }
+ else if( SwComparePosition::OverlapBehind == eCmpPos )
+ pNewRedl->SetStart( *pREnd, pStt );
+ else
+ pNewRedl->SetEnd( *pRStt, pEnd );
+ break;
+
+ case SwComparePosition::CollideEnd:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ (n == 0 || *maRedlineTable[ n-1 ]->End() < *pStt))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ pNewRedl->SetEnd( *pREnd, pEnd );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+ case SwComparePosition::CollideStart:
+ if( pRedl->IsOwnRedline( *pNewRedl ) &&
+ pRedl->CanCombine( *pNewRedl ) &&
+ (n+1 >= maRedlineTable.size() ||
+ (*maRedlineTable[ n+1 ]->Start() >= *pEnd &&
+ *maRedlineTable[ n+1 ]->Start() != *pREnd)))
+ {
+ // If that's the case we can merge it, meaning
+ // the new one covers this well
+ pNewRedl->SetStart( *pRStt, pStt );
+ maRedlineTable.DeleteAndDestroy( n );
+ bDec = true;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case RedlineType::FmtColl:
+ // How should we behave here?
+ // insert as is
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( pNewRedl )
+ {
+ if( ( *pStt == *pEnd ) &&
+ ( pNewRedl->GetContentIdx() == nullptr ) )
+ { // Do not insert empty redlines
+ delete pNewRedl;
+ pNewRedl = nullptr;
+ }
+ else
+ {
+ if ( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ if ( pStt->nContent != 0 )
+ {
+ // tdf#119571 update the style of the joined paragraph
+ // after a partially deleted paragraph to show its correct style
+ // in "Show changes" mode, too. All removed paragraphs
+ // get the style of the first (partially deleted) paragraph
+ // to avoid text insertion with bad style in the deleted
+ // area later (except paragraphs of the removed tables).
+
+ SwContentNode* pDelNd = pStt->nNode.GetNode().GetContentNode();
+ // start copying the style of the first paragraph from the end of the range
+ SwContentNode* pTextNd = pEnd->nNode.GetNode().GetContentNode();
+ SwNodeIndex aIdx( pEnd->nNode.GetNode() );
+ bool bFirst = true;
+
+ while (pTextNd != nullptr && pDelNd->GetIndex() < pTextNd->GetIndex())
+ {
+ if( pTextNd->IsTextNode() )
+ {
+ SwPosition aPos(aIdx);
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ bCompress = true;
+
+ // split redline to store ExtraData per paragraphs
+ SwRangeRedline* pPar = new SwRangeRedline( *pNewRedl );
+ pPar->SetStart( aPos );
+ pNewRedl->SetEnd( aPos );
+
+ // get extradata for reset formatting of the modified paragraph
+ SwRedlineExtraData_FormatColl* pExtraData = lcl_CopyStyle(aPos, *pStt, false);
+ if (pExtraData)
+ {
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
+ if (!bFirst)
+ pExtraData->SetFormatAll(false);
+ xRedlineExtraData.reset(pExtraData);
+ pPar->SetExtraData( xRedlineExtraData.get() );
+ }
+
+ // skip empty redlines without ExtraData
+ // FIXME: maybe checking pExtraData is redundant here
+ if ( pExtraData || *pPar->Start() != *pPar->End() )
+ maRedlineTable.Insert( pPar );
+ else
+ delete pPar;
+ }
+
+ // modify paragraph formatting
+ lcl_CopyStyle(*pStt, aPos);
+ }
+
+ if (bFirst)
+ bFirst = false;
+
+ // Jump to the previous paragraph and if needed, skip paragraphs of
+ // the removed table(s) in the range to avoid leaving empty tables
+ // because of the non-continuous redline range over the table.
+ // FIXME: this is not enough for tables with inner redlines, where
+ // tracked deletion of the text containing such a table leaves an
+ // empty table at the place of the table (a problem inherited from OOo).
+ pTextNd = nullptr;
+ while( --aIdx && pDelNd->GetIndex() < aIdx.GetIndex() &&
+ !aIdx.GetNode().IsContentNode() )
+ {
+ // possible table end
+ if( aIdx.GetNode().IsEndNode() && aIdx.GetNode().FindTableNode() )
+ {
+ SwNodeIndex aIdx2 = aIdx;
+ // search table start and skip table paragraphs
+ while ( pDelNd->GetIndex() < aIdx2.GetIndex() )
+ {
+ SwTableNode* pTable = aIdx2.GetNode().GetTableNode();
+ if( pTable &&
+ pTable->EndOfSectionNode()->GetIndex() == aIdx.GetIndex() )
+ {
+ aIdx = aIdx2;
+ break;
+ }
+ --aIdx2;
+ }
+ }
+ }
+
+ if (aIdx.GetNode().IsContentNode())
+ pTextNd = aIdx.GetNode().GetContentNode();
+ }
+ }
+
+ // delete tables of the deletion explicitly, to avoid
+ // remaining empty tables after accepting the rejection
+ // and visible empty tables in Hide Changes mode
+ // (this was the case, if tables have already contained
+ // other tracked changes)
+ // FIXME: because of recursive nature of AppendRedline,
+ // this doesn't work for selections with multiple tables
+ if ( m_rDoc.GetIDocumentUndoRedo().DoesUndo() )
+ {
+ SwNodeIndex aSttIdx( pStt->nNode.GetNode() );
+ SwNodeIndex aEndIdx( pEnd->nNode.GetNode() );
+ while ( aSttIdx < aEndIdx )
+ {
+ if ( aSttIdx.GetNode().IsTableNode() )
+ {
+ SvxPrintItem aNotTracked(RES_PRINT, false);
+ SwCursor aCursor( SwPosition(aSttIdx), nullptr );
+ m_rDoc.SetRowNotTracked( aCursor, aNotTracked, /*bAll=*/true );
+ }
+ ++aSttIdx;
+ }
+ }
+ }
+ bool const ret = maRedlineTable.Insert( pNewRedl );
+ assert(ret || !pNewRedl);
+ if (ret && !pNewRedl)
+ {
+ bMerged = true; // treat InsertWithValidRanges as "merge"
+ }
+ }
+ }
+
+ if( bCompress )
+ CompressRedlines();
+
+ CHECK_REDLINE( *this )
+
+ return (nullptr != pNewRedl)
+ ? AppendResult::APPENDED
+ : (bMerged ? AppendResult::MERGED : AppendResult::IGNORED);
+}
+
+bool DocumentRedlineManager::AppendTableRowRedline( SwTableRowRedline* pNewRedl )
+{
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ pNewRedl->InvalidateRange();
+ */
+
+ // Make equivalent of 'AppendRedline' checks inside here too
+
+ maExtraRedlineTable.Insert( pNewRedl );
+ }
+ else
+ {
+ // TO DO - equivalent for 'SwTableRowRedline'
+ /*
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
+ DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl, pNewRedl = 0;
+ */
+ }
+ // #TODO - equivalent for 'SwTableRowRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ return nullptr != pNewRedl;
+}
+
+bool DocumentRedlineManager::AppendTableCellRedline( SwTableCellRedline* pNewRedl )
+{
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ if (IsRedlineOn() && !IsShowOriginal(meRedlineFlags))
+ {
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ pNewRedl->InvalidateRange();
+ */
+
+ // Make equivalent of 'AppendRedline' checks inside here too
+
+ maExtraRedlineTable.Insert( pNewRedl );
+ }
+ else
+ {
+ // TO DO - equivalent for 'SwTableCellRedline'
+ /*
+ if( bCallDelete && RedlineType::Delete == pNewRedl->GetType() )
+ {
+ RedlineFlags eOld = meRedlineFlags;
+ // Set to NONE, so that the Delete::Redo merges the Redline data correctly!
+ // The ShowMode needs to be retained!
+ meRedlineFlags = eOld & ~(RedlineFlags::On | RedlineFlags::Ignore);
+ DeleteAndJoin( *pNewRedl );
+ meRedlineFlags = eOld;
+ }
+ delete pNewRedl, pNewRedl = 0;
+ */
+ }
+ // #TODO - equivalent for 'SwTableCellRedline'
+ /*
+ CHECK_REDLINE( this )
+ */
+
+ return nullptr != pNewRedl;
+}
+
+void DocumentRedlineManager::CompressRedlines()
+{
+ CHECK_REDLINE( *this )
+
+ void (SwRangeRedline::*pFnc)(sal_uInt16, size_t, bool) = nullptr;
+ RedlineFlags eShow = RedlineFlags::ShowMask & meRedlineFlags;
+ if( eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ pFnc = &SwRangeRedline::Show;
+ else if (eShow == RedlineFlags::ShowInsert)
+ pFnc = &SwRangeRedline::Hide;
+
+ // Try to merge identical ones
+ for( SwRedlineTable::size_type n = 1; n < maRedlineTable.size(); ++n )
+ {
+ SwRangeRedline* pPrev = maRedlineTable[ n-1 ],
+ * pCur = maRedlineTable[ n ];
+ const SwPosition* pPrevStt = pPrev->Start(),
+ * pPrevEnd = pPrev->End();
+ const SwPosition* pCurStt = pCur->Start(),
+ * pCurEnd = pCur->End();
+ if( *pPrevEnd == *pCurStt && pPrev->CanCombine( *pCur ) &&
+ pPrevStt->nNode.GetNode().StartOfSectionNode() ==
+ pCurEnd->nNode.GetNode().StartOfSectionNode() &&
+ !pCurEnd->nNode.GetNode().StartOfSectionNode()->IsTableNode() )
+ {
+ // we then can merge them
+ SwRedlineTable::size_type nPrevIndex = n-1;
+ pPrev->Show(0, nPrevIndex);
+ pCur->Show(0, n);
+
+ pPrev->SetEnd( *pCur->End() );
+ maRedlineTable.DeleteAndDestroy( n );
+ --n;
+ if( pFnc )
+ (pPrev->*pFnc)(0, nPrevIndex, false);
+ }
+ }
+ CHECK_REDLINE( *this )
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::SplitRedline( const SwPaM& rRange )
+{
+ bool bChg = false;
+ SwRedlineTable::size_type n = 0;
+ const SwPosition* pStt = rRange.Start();
+ const SwPosition* pEnd = rRange.End();
+ //FIXME overlapping problem GetRedline( *pStt, &n );
+ for ( ; n < maRedlineTable.size(); ++n)
+ {
+ SwRangeRedline * pRedline = maRedlineTable[ n ];
+ SwPosition *const pRedlineStart = pRedline->Start();
+ SwPosition *const pRedlineEnd = pRedline->End();
+ if (*pRedlineStart <= *pStt && *pStt <= *pRedlineEnd &&
+ *pRedlineStart <= *pEnd && *pEnd <= *pRedlineEnd)
+ {
+ bChg = true;
+ int nn = 0;
+ if (*pStt == *pRedlineStart)
+ nn += 1;
+ if (*pEnd == *pRedlineEnd)
+ nn += 2;
+
+ SwRangeRedline* pNew = nullptr;
+ switch( nn )
+ {
+ case 0:
+ pNew = new SwRangeRedline( *pRedline );
+ pRedline->SetEnd( *pStt, pRedlineEnd );
+ pNew->SetStart( *pEnd );
+ break;
+
+ case 1:
+ *pRedlineStart = *pEnd;
+ break;
+
+ case 2:
+ *pRedlineEnd = *pStt;
+ break;
+
+ case 3:
+ pRedline->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ maRedlineTable.DeleteAndDestroy( n-- );
+ pRedline = nullptr;
+ break;
+ }
+ if (pRedline && !pRedline->HasValidRange())
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedline, n );
+ }
+ if( pNew )
+ maRedlineTable.Insert( pNew, n );
+ }
+ else if (*pEnd < *pRedlineStart)
+ break;
+ }
+ return bChg;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::DeleteRedline( const SwPaM& rRange, bool bSaveInUndo,
+ RedlineType nDelType )
+{
+ if( !rRange.HasMark() || *rRange.GetMark() == *rRange.GetPoint() )
+ return false;
+
+ bool bChg = false;
+
+ if (bSaveInUndo && m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoRedline> pUndo(new SwUndoRedline( SwUndoId::REDLINE, rRange ));
+ if( pUndo->GetRedlSaveCount() )
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ const SwPosition* pStt = rRange.Start(),
+ * pEnd = rRange.End();
+ SwRedlineTable::size_type n = 0;
+ GetRedline( *pStt, &n );
+ for( ; n < maRedlineTable.size() ; ++n )
+ {
+ SwRangeRedline* pRedl = maRedlineTable[ n ];
+ if( RedlineType::Any != nDelType && nDelType != pRedl->GetType() )
+ continue;
+
+ SwPosition* pRStt = pRedl->Start(),
+ * pREnd = pRedl->End();
+ switch( ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ) )
+ {
+ case SwComparePosition::Equal:
+ case SwComparePosition::Outside:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ maRedlineTable.DeleteAndDestroy( n-- );
+ bChg = true;
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ pRedl->SetStart( *pEnd, pRStt );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ pRedl->SetEnd( *pStt, pREnd );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ }
+ break;
+
+ case SwComparePosition::Inside:
+ {
+ // this one needs to be split
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ if( *pRStt == *pStt )
+ {
+ pRedl->SetStart( *pEnd, pRStt );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ }
+ else
+ {
+ SwRangeRedline* pCpy;
+ if( *pREnd != *pEnd )
+ {
+ pCpy = new SwRangeRedline( *pRedl );
+ pCpy->SetStart( *pEnd );
+ pCpy->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ else
+ pCpy = nullptr;
+ pRedl->SetEnd( *pStt, pREnd );
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ if( !pRedl->HasValidRange() )
+ {
+ // re-insert
+ maRedlineTable.Remove( n );
+ maRedlineTable.Insert( pRedl );
+ --n;
+ }
+ if( pCpy )
+ maRedlineTable.Insert( pCpy );
+ }
+ }
+ break;
+
+ case SwComparePosition::CollideEnd:
+ // remove (not hidden) empty redlines created for fixing tdf#119571
+ // (Note: hidden redlines are all empty, i.e. start and end are equal.)
+ if ( pRedl->HasMark() && *pRedl->GetMark() == *pRedl->GetPoint() )
+ {
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Remove);
+ maRedlineTable.DeleteAndDestroy( n-- );
+ bChg = true;
+ break;
+ }
+ [[fallthrough]];
+
+ case SwComparePosition::Before:
+ n = maRedlineTable.size();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( bChg )
+ m_rDoc.getIDocumentState().SetModified();
+
+ return bChg;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::DeleteRedline( const SwStartNode& rNode, bool bSaveInUndo,
+ RedlineType nDelType )
+{
+ SwPaM aTemp(*rNode.EndOfSectionNode(), rNode);
+ return DeleteRedline(aTemp, bSaveInUndo, nDelType);
+}
+
+SwRedlineTable::size_type DocumentRedlineManager::GetRedlinePos( const SwNode& rNd, RedlineType nType ) const
+{
+ const SwNodeOffset nNdIdx = rNd.GetIndex();
+ // if the table only contains good (i.e. non-overlapping) data, we can do a binary search
+ if (!maRedlineTable.HasOverlappingElements())
+ {
+ // binary search to the first redline with end >= the needle
+ auto it = std::lower_bound(maRedlineTable.begin(), maRedlineTable.end(), rNd,
+ [&nNdIdx](const SwRangeRedline* lhs, const SwNode& /*rhs*/)
+ {
+ return lhs->End()->nNode.GetIndex() < nNdIdx;
+ });
+ for( ; it != maRedlineTable.end(); ++it)
+ {
+ const SwRangeRedline* pTmp = *it;
+ SwNodeOffset nStart = pTmp->Start()->nNode.GetIndex(),
+ nEnd = pTmp->End()->nNode.GetIndex();
+
+ if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
+ nStart <= nNdIdx && nNdIdx <= nEnd )
+ return std::distance(maRedlineTable.begin(), it);
+
+ if( nStart > nNdIdx )
+ break;
+ }
+ }
+ else
+ {
+ for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+ SwNodeOffset nPt = pTmp->GetPoint()->nNode.GetIndex(),
+ nMk = pTmp->GetMark()->nNode.GetIndex();
+ if( nPt < nMk ) { SwNodeOffset nTmp = nMk; nMk = nPt; nPt = nTmp; }
+
+ if( ( RedlineType::Any == nType || nType == pTmp->GetType()) &&
+ nMk <= nNdIdx && nNdIdx <= nPt )
+ return n;
+
+ if( nMk > nNdIdx )
+ break;
+ }
+ }
+ return SwRedlineTable::npos;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::HasRedline( const SwPaM& rPam, RedlineType nType, bool bStartOrEndInRange ) const
+{
+ SwPosition currentStart(*rPam.Start());
+ SwPosition currentEnd(*rPam.End());
+ SwNodeIndex pEndNodeIndex(currentEnd.nNode.GetNode());
+
+ for( SwRedlineTable::size_type n = GetRedlinePos( rPam.Start()->nNode.GetNode(), nType );
+ n < maRedlineTable.size(); ++n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+
+ if ( pTmp->Start()->nNode > pEndNodeIndex )
+ break;
+
+ if( RedlineType::Any != nType && nType != pTmp->GetType() )
+ continue;
+
+ // redline over the range
+ if ( currentStart < *pTmp->End() && *pTmp->Start() <= currentEnd &&
+ // starting or ending within the range
+ ( !bStartOrEndInRange ||
+ ( currentStart < *pTmp->Start() || *pTmp->End() < currentEnd ) ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& rPos,
+ SwRedlineTable::size_type* pFndPos ) const
+{
+ SwRedlineTable::size_type nO = maRedlineTable.size(), nM, nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ nM = nU + ( nO - nU ) / 2;
+ const SwRangeRedline* pRedl = maRedlineTable[ nM ];
+ const SwPosition* pStt = pRedl->Start();
+ const SwPosition* pEnd = pRedl->End();
+ if( pEnd == pStt
+ ? *pStt == rPos
+ : ( *pStt <= rPos && rPos < *pEnd ) )
+ {
+ while( nM && rPos == *maRedlineTable[ nM - 1 ]->End() &&
+ rPos == *maRedlineTable[ nM - 1 ]->Start() )
+ {
+ --nM;
+ pRedl = maRedlineTable[ nM ];
+ }
+ // if there are format and insert changes in the same position
+ // show insert change first.
+ // since the redlines are sorted by position, only check the redline
+ // before and after the current redline
+ if( RedlineType::Format == pRedl->GetType() )
+ {
+ if( nM && rPos >= *maRedlineTable[ nM - 1 ]->Start() &&
+ rPos <= *maRedlineTable[ nM - 1 ]->End() &&
+ ( RedlineType::Insert == maRedlineTable[ nM - 1 ]->GetType() ) )
+ {
+ --nM;
+ pRedl = maRedlineTable[ nM ];
+ }
+ else if( ( nM + 1 ) <= nO && rPos >= *maRedlineTable[ nM + 1 ]->Start() &&
+ rPos <= *maRedlineTable[ nM + 1 ]->End() &&
+ ( RedlineType::Insert == maRedlineTable[ nM + 1 ]->GetType() ) )
+ {
+ ++nM;
+ pRedl = maRedlineTable[ nM ];
+ }
+ }
+
+ if( pFndPos )
+ *pFndPos = nM;
+ return pRedl;
+ }
+ else if( *pEnd <= rPos )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::AcceptRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
+{
+ bool bRet = false;
+
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ SwRangeRedline* pTmp = maRedlineTable[ nPos ];
+ pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::ACCEPT_REDLINE, &aRewriter);
+ }
+
+ int nLoopCnt = 2;
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+
+ do {
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAcceptRedline>(*pTmp) );
+ }
+
+ bRet |= lcl_AcceptRedline( maRedlineTable, nPos, bCallDelete );
+
+ if( nSeqNo )
+ {
+ if( SwRedlineTable::npos == nPos )
+ nPos = 0;
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
+ : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
+ if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
+ SwRedlineTable::npos != ( nFndPos =
+ maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
+ {
+ nPos = nFndPos;
+ pTmp = maRedlineTable[ nPos ];
+ }
+ else
+ nLoopCnt = 0;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while( nLoopCnt );
+
+ if( bRet )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ }
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::AcceptRedline( const SwPaM& rPam, bool bCallDelete )
+{
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ lcl_AdjustRedlineRange( aPam );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::ACCEPT_REDLINE, nullptr );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAcceptRedline>( aPam ));
+ }
+
+ int nRet = lcl_AcceptRejectRedl( lcl_AcceptRedline, maRedlineTable,
+ bCallDelete, aPam );
+ if( nRet > 0 )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ OUString aTmpStr;
+
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nRet));
+ aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::ACCEPT_REDLINE, &aRewriter );
+ }
+ return nRet != 0;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+void DocumentRedlineManager::AcceptRedlineParagraphFormatting( const SwPaM &rPam )
+{
+ const SwPosition* pStt = rPam.Start(),
+ * pEnd = rPam.End();
+
+ const SwNodeOffset nSttIdx = pStt->nNode.GetIndex();
+ const SwNodeOffset nEndIdx = pEnd->nNode.GetIndex();
+
+ for( SwRedlineTable::size_type n = 0; n < maRedlineTable.size() ; ++n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+ SwNodeOffset nPt = pTmp->GetPoint()->nNode.GetIndex(),
+ nMk = pTmp->GetMark()->nNode.GetIndex();
+ if( nPt < nMk ) { SwNodeOffset nTmp = nMk; nMk = nPt; nPt = nTmp; }
+
+ if( RedlineType::ParagraphFormat == pTmp->GetType() &&
+ ( (nSttIdx <= nMk && nMk <= nEndIdx) || (nSttIdx <= nPt && nPt <= nEndIdx) ) )
+ AcceptRedline( n, false );
+
+ if( nMk > nEndIdx )
+ break;
+ }
+}
+
+bool DocumentRedlineManager::RejectRedline( SwRedlineTable::size_type nPos, bool bCallDelete )
+{
+ bool bRet = false;
+
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ SwRangeRedline* pTmp = maRedlineTable[ nPos ];
+ pTmp->Show(0, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ pTmp->Show(1, maRedlineTable.GetPos(pTmp), /*bForced=*/true);
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pTmp->GetDescr());
+ m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::REJECT_REDLINE, &aRewriter);
+ }
+
+ int nLoopCnt = 2;
+ sal_uInt16 nSeqNo = pTmp->GetSeqNo();
+
+ do {
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRejectRedline>( *pTmp ) );
+ }
+
+ bRet |= lcl_RejectRedline( maRedlineTable, nPos, bCallDelete );
+
+ if( nSeqNo )
+ {
+ if( SwRedlineTable::npos == nPos )
+ nPos = 0;
+ SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
+ ? maRedlineTable.FindNextSeqNo( nSeqNo, nPos )
+ : maRedlineTable.FindPrevSeqNo( nSeqNo, nPos );
+ if( SwRedlineTable::npos != nFndPos || ( 0 != ( --nLoopCnt ) &&
+ SwRedlineTable::npos != ( nFndPos =
+ maRedlineTable.FindPrevSeqNo( nSeqNo, nPos ))) )
+ {
+ nPos = nFndPos;
+ pTmp = maRedlineTable[ nPos ];
+ }
+ else
+ nLoopCnt = 0;
+ }
+ else
+ nLoopCnt = 0;
+
+ } while( nLoopCnt );
+
+ if( bRet )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ }
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+bool DocumentRedlineManager::RejectRedline( const SwPaM& rPam, bool bCallDelete )
+{
+ // Switch to visible in any case
+ if( (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete) !=
+ (RedlineFlags::ShowMask & meRedlineFlags) )
+ SetRedlineFlags( RedlineFlags::ShowInsert | RedlineFlags::ShowDelete | meRedlineFlags );
+
+ // The Selection is only in the ContentSection. If there are Redlines
+ // to Non-ContentNodes before or after that, then the Selections
+ // expand to them.
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+ lcl_AdjustRedlineRange( aPam );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::REJECT_REDLINE, nullptr );
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoRejectRedline>(aPam) );
+ }
+
+ int nRet = lcl_AcceptRejectRedl( lcl_RejectRedline, maRedlineTable,
+ bCallDelete, aPam );
+ if( nRet > 0 )
+ {
+ CompressRedlines();
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ OUString aTmpStr;
+
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nRet));
+ aTmpStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ m_rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::REJECT_REDLINE, &aRewriter );
+ }
+
+ return nRet != 0;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+void DocumentRedlineManager::AcceptAllRedline(bool bAccept)
+{
+ bool bSuccess = true;
+ OUString sUndoStr;
+ IDocumentUndoRedo& rUndoMgr = m_rDoc.GetIDocumentUndoRedo();
+
+ if (maRedlineTable.size() > 1)
+ {
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(maRedlineTable.size()));
+ sUndoStr = aRewriter.Apply(SwResId(STR_N_REDLINES));
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, sUndoStr);
+ rUndoMgr.StartUndo(bAccept ? SwUndoId::ACCEPT_REDLINE : SwUndoId::REJECT_REDLINE, &aRewriter);
+ }
+
+ while (!maRedlineTable.empty() && bSuccess)
+ {
+ if (bAccept)
+ bSuccess = AcceptRedline(maRedlineTable.size() - 1, true);
+ else
+ bSuccess = RejectRedline(maRedlineTable.size() - 1, true);
+ }
+
+ if (!sUndoStr.isEmpty())
+ {
+ rUndoMgr.EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+}
+
+const SwRangeRedline* DocumentRedlineManager::SelNextRedline( SwPaM& rPam ) const
+{
+ rPam.DeleteMark();
+ rPam.SetMark();
+
+ SwPosition& rSttPos = *rPam.GetPoint();
+ SwPosition aSavePos( rSttPos );
+ bool bRestart;
+
+ // If the starting position points to the last valid ContentNode,
+ // we take the next Redline in any case.
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n );
+ if( pFnd )
+ {
+ const SwPosition* pEnd = pFnd->End();
+ if( !pEnd->nNode.GetNode().IsContentNode() )
+ {
+ SwNodeIndex aTmp( pEnd->nNode );
+ SwContentNode* pCNd = SwNodes::GoPrevSection( &aTmp );
+ if( !pCNd || ( aTmp == rSttPos.nNode &&
+ pCNd->Len() == rSttPos.nContent.GetIndex() ))
+ pFnd = nullptr;
+ }
+ if( pFnd )
+ rSttPos = *pFnd->End();
+ }
+
+ do {
+ bRestart = false;
+
+ for( ; !pFnd && n < maRedlineTable.size(); ++n )
+ {
+ pFnd = maRedlineTable[ n ];
+ if( pFnd->HasMark() && pFnd->IsVisible() )
+ {
+ *rPam.GetMark() = *pFnd->Start();
+ rSttPos = *pFnd->End();
+ break;
+ }
+ else
+ pFnd = nullptr;
+ }
+
+ if( pFnd )
+ {
+ // Merge all of the same type and author that are
+ // consecutive into one Selection.
+ const SwPosition* pPrevEnd = pFnd->End();
+ while( ++n < maRedlineTable.size() )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ n ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition *pRStt;
+ if( pFnd->GetType() != pTmp->GetType() ||
+ pFnd->GetAuthor() != pTmp->GetAuthor() )
+ break;
+ pRStt = pTmp->Start();
+ if( *pPrevEnd == *pRStt || IsPrevPos( *pPrevEnd, *pRStt ) )
+ {
+ pPrevEnd = pTmp->End();
+ rSttPos = *pPrevEnd;
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ const SwRangeRedline* pSaveFnd = pFnd;
+
+ SwContentNode* pCNd;
+ SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx <= rPam.GetPoint()->nNode )
+ rPam.GetMark()->nContent.Assign( pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd )
+ {
+ pIdx = &rPam.GetPoint()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = SwNodes::GoPrevSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx >= rPam.GetMark()->nNode )
+ rPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
+ {
+ if( n < maRedlineTable.size() )
+ {
+ bRestart = true;
+ *rPam.GetPoint() = *pSaveFnd->End();
+ }
+ else
+ {
+ rPam.DeleteMark();
+ *rPam.GetPoint() = aSavePos;
+ }
+ pFnd = nullptr;
+ }
+ }
+ } while( bRestart );
+
+ return pFnd;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const SwRangeRedline* DocumentRedlineManager::SelPrevRedline( SwPaM& rPam ) const
+{
+ rPam.DeleteMark();
+ rPam.SetMark();
+
+ SwPosition& rSttPos = *rPam.GetPoint();
+ SwPosition aSavePos( rSttPos );
+ bool bRestart;
+
+ // If the starting position points to the last valid ContentNode,
+ // we take the previous Redline in any case.
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd = GetRedlineTable().FindAtPosition( rSttPos, n, false );
+ if( pFnd )
+ {
+ const SwPosition* pStt = pFnd->Start();
+ if( !pStt->nNode.GetNode().IsContentNode() )
+ {
+ SwNodeIndex aTmp( pStt->nNode );
+ SwContentNode* pCNd = m_rDoc.GetNodes().GoNextSection( &aTmp );
+ if( !pCNd || ( aTmp == rSttPos.nNode &&
+ !rSttPos.nContent.GetIndex() ))
+ pFnd = nullptr;
+ }
+ if( pFnd )
+ rSttPos = *pFnd->Start();
+ }
+
+ do {
+ bRestart = false;
+
+ while( !pFnd && 0 < n )
+ {
+ pFnd = maRedlineTable[ --n ];
+ if( pFnd->HasMark() && pFnd->IsVisible() )
+ {
+ *rPam.GetMark() = *pFnd->End();
+ rSttPos = *pFnd->Start();
+ }
+ else
+ pFnd = nullptr;
+ }
+
+ if( pFnd )
+ {
+ // Merge all of the same type and author that are
+ // consecutive into one Selection.
+ const SwPosition* pNextStt = pFnd->Start();
+ while( 0 < n )
+ {
+ const SwRangeRedline* pTmp = maRedlineTable[ --n ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition *pREnd;
+ if( pFnd->GetType() == pTmp->GetType() &&
+ pFnd->GetAuthor() == pTmp->GetAuthor() &&
+ ( *pNextStt == *( pREnd = pTmp->End() ) ||
+ IsPrevPos( *pREnd, *pNextStt )) )
+ {
+ pNextStt = pTmp->Start();
+ rSttPos = *pNextStt;
+ }
+ else
+ {
+ ++n;
+ break;
+ }
+ }
+ }
+ }
+
+ if( pFnd )
+ {
+ const SwRangeRedline* pSaveFnd = pFnd;
+
+ SwContentNode* pCNd;
+ SwNodeIndex* pIdx = &rPam.GetMark()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = SwNodes::GoPrevSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx >= rPam.GetPoint()->nNode )
+ rPam.GetMark()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ pFnd = nullptr;
+ }
+ }
+
+ if( pFnd )
+ {
+ pIdx = &rPam.GetPoint()->nNode;
+ if( !pIdx->GetNode().IsContentNode() )
+ {
+ pCNd = m_rDoc.GetNodes().GoNextSection( pIdx );
+ if( pCNd )
+ {
+ if( *pIdx <= rPam.GetMark()->nNode )
+ rPam.GetPoint()->nContent.Assign( pCNd, 0 );
+ else
+ pFnd = nullptr;
+ }
+ }
+ }
+
+ if( !pFnd || *rPam.GetMark() == *rPam.GetPoint() )
+ {
+ if( n )
+ {
+ bRestart = true;
+ *rPam.GetPoint() = *pSaveFnd->Start();
+ }
+ else
+ {
+ rPam.DeleteMark();
+ *rPam.GetPoint() = aSavePos;
+ }
+ pFnd = nullptr;
+ }
+ }
+ } while( bRestart );
+
+ return pFnd;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+// Set comment at the Redline
+bool DocumentRedlineManager::SetRedlineComment( const SwPaM& rPaM, const OUString& rS )
+{
+ bool bRet = false;
+ const SwPosition* pStt = rPaM.Start(),
+ * pEnd = rPaM.End();
+ SwRedlineTable::size_type n = 0;
+ if( GetRedlineTable().FindAtPosition( *pStt, n ) )
+ {
+ for( ; n < maRedlineTable.size(); ++n )
+ {
+ bRet = true;
+ SwRangeRedline* pTmp = maRedlineTable[ n ];
+ if( pStt != pEnd && *pTmp->Start() > *pEnd )
+ break;
+
+ pTmp->SetComment( rS );
+ if( *pTmp->End() >= *pEnd )
+ break;
+ }
+ }
+ if( bRet )
+ m_rDoc.getIDocumentState().SetModified();
+
+ return bRet;
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+// Create a new author if necessary
+std::size_t DocumentRedlineManager::GetRedlineAuthor()
+{
+ return SW_MOD()->GetRedlineAuthor();
+}
+
+/// Insert new author into the Table for the Readers etc.
+std::size_t DocumentRedlineManager::InsertRedlineAuthor( const OUString& rNew )
+{
+ return SW_MOD()->InsertRedlineAuthor(rNew);
+}
+
+void DocumentRedlineManager::UpdateRedlineAttr()
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->IsVisible() )
+ pRedl->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+
+ // #TODO - add 'SwExtraRedlineTable' also ?
+}
+
+const uno::Sequence <sal_Int8>& DocumentRedlineManager::GetRedlinePassword() const
+{
+ return maRedlinePasswd;
+}
+
+void DocumentRedlineManager::SetRedlinePassword(
+ /*[in]*/const uno::Sequence <sal_Int8>& rNewPassword)
+{
+ maRedlinePasswd = rNewPassword;
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+/// Set comment text for the Redline, which is inserted later on via
+/// AppendRedline. Is used by Autoformat.
+/// A null pointer resets the mode. The pointer is not copied, so it
+/// needs to stay valid!
+void DocumentRedlineManager::SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo )
+{
+ m_rDoc.SetAutoFormatRedline( nullptr != pText );
+ if( pText )
+ {
+ moAutoFormatRedlnComment = *pText;
+ }
+ else
+ {
+ moAutoFormatRedlnComment.reset();
+ }
+
+ mnAutoFormatRedlnCommentNo = nSeqNo;
+}
+
+void DocumentRedlineManager::HideAll( bool bDeletion )
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
+ {
+ SwRangeRedline* pRedline = rTable[i-1];
+ if ( pRedline->GetType() == RedlineType::Delete )
+ {
+ if ( bDeletion && pRedline->IsVisible() )
+ {
+ pRedline->Hide(0, rTable.GetPos(pRedline), false);
+ pRedline->Hide(1, rTable.GetPos(pRedline), false);
+ }
+ else if ( !bDeletion && !pRedline->IsVisible() )
+ {
+ pRedline->Show(0, rTable.GetPos(pRedline), true);
+ pRedline->Show(1, rTable.GetPos(pRedline), true);
+ }
+ }
+ else if ( pRedline->GetType() == RedlineType::Insert )
+ {
+ if ( !bDeletion && pRedline->IsVisible() )
+ {
+ pRedline->ShowOriginal(0, rTable.GetPos(pRedline), false);
+ pRedline->ShowOriginal(1, rTable.GetPos(pRedline), false);
+ }
+ else if ( bDeletion && !pRedline->IsVisible() )
+ {
+ pRedline->Show(0, rTable.GetPos(pRedline), true);
+ pRedline->Show(1, rTable.GetPos(pRedline), true);
+ }
+ }
+ }
+}
+
+void DocumentRedlineManager::ShowAll()
+{
+ const SwRedlineTable& rTable = GetRedlineTable();
+ for (SwRedlineTable::size_type i = rTable.size(); i > 0; --i)
+ {
+ SwRangeRedline* pRedline = rTable[i-1];
+ if ( !pRedline->IsVisible() )
+ {
+ pRedline->Show(0, rTable.GetPos(pRedline), true);
+ pRedline->Show(1, rTable.GetPos(pRedline), true);
+ }
+ }
+}
+
+DocumentRedlineManager::~DocumentRedlineManager()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentSettingManager.cxx b/sw/source/core/doc/DocumentSettingManager.cxx
new file mode 100644
index 000000000..5f2a57099
--- /dev/null
+++ b/sw/source/core/doc/DocumentSettingManager.cxx
@@ -0,0 +1,1039 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <DocumentSettingManager.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdmodel.hxx>
+#include <svl/asiancfg.hxx>
+#include <unotools/compatibility.hxx>
+#include <unotools/configmgr.hxx>
+#include <drawdoc.hxx>
+#include <swmodule.hxx>
+#include <linkenum.hxx>
+#include <rootfrm.hxx>
+#include <breakit.hxx>
+#include <docary.hxx>
+
+/* IDocumentSettingAccess */
+
+sw::DocumentSettingManager::DocumentSettingManager(SwDoc &rDoc)
+ :m_rDoc(rDoc),
+ mnLinkUpdMode( GLOBALSETTING ),
+ meFieldUpdMode( AUTOUPD_GLOBALSETTING ),
+ meChrCmprType( CharCompressType::NONE ),
+ mn32DummyCompatibilityOptions1(0),
+ mn32DummyCompatibilityOptions2(0),
+ mbHTMLMode(false),
+ mbIsGlobalDoc(false),
+ mbGlblDocSaveLinks(false),
+ mbIsLabelDoc(false),
+ mbPurgeOLE(true),
+ mbKernAsianPunctuation(false),
+
+ // COMPATIBILITY FLAGS START
+
+ mbAddFlyOffsets(false),
+ mbAddVerticalFlyOffsets(false),
+ mbUseHiResolutionVirtualDevice(true),
+ mbMathBaselineAlignment(false), // default for *old* documents is 'off'
+ mbStylesNoDefault(false),
+ mbFloattableNomargins(false),
+ mEmbedFonts(false),
+ mEmbedUsedFonts(false),
+ mEmbedLatinScriptFonts(true),
+ mEmbedAsianScriptFonts(true),
+ mEmbedComplexScriptFonts(true),
+ mEmbedSystemFonts(false),
+ mbOldNumbering(false),
+ mbIgnoreFirstLineIndentInNumbering(false),
+ mbDoNotResetParaAttrsForNumFont(false),
+ mbTableRowKeep(false),
+ mbIgnoreTabsAndBlanksForLineCalculation(false),
+ mbDoNotCaptureDrawObjsOnPage(false),
+ mbClipAsCharacterAnchoredWriterFlyFrames(false),
+ mbUnixForceZeroExtLeading(false),
+ mbTabRelativeToIndent(true),
+ mbProtectForm(false), // i#78591#
+ mbMsWordCompTrailingBlanks(false), // tdf#104349 tdf#104668
+ mbMsWordCompMinLineHeightByFly(false),
+ mbInvertBorderSpacing (false),
+ mbCollapseEmptyCellPara(true),
+ mbTabAtLeftIndentForParagraphsInList(false), //#i89181#
+ mbSmallCapsPercentage66(false),
+ mbTabOverflow(true),
+ mbUnbreakableNumberings(false),
+ mbClippedPictures(false),
+ mbBackgroundParaOverDrawings(false),
+ mbTabOverMargin(false),
+ mbTabOverSpacing(false),
+ mbTreatSingleColumnBreakAsPageBreak(false),
+ mbSurroundTextWrapSmall(false),
+ mbPropLineSpacingShrinksFirstLine(true),
+ mbSubtractFlys(false),
+ mApplyParagraphMarkFormatToNumbering(false),
+ mbLastBrowseMode( false ),
+ mbDisableOffPagePositioning ( false ),
+ mbProtectBookmarks(false),
+ mbProtectFields(false),
+ mbHeaderSpacingBelowLastPara(false),
+ mbFrameAutowidthWithMorePara(false),
+ mbGutterAtTop(false),
+ mbFootnoteInColumnToPageEnd(false),
+ mnImagePreferredDPI(0),
+ mbAutoFirstLineIndentDisregardLineSpace(true),
+ mbWrapAsCharFlysLikeInOOXML(false),
+ mbNoNumberingShowFollowBy(false)
+ // COMPATIBILITY FLAGS END
+{
+ // COMPATIBILITY FLAGS START
+
+ // Note: Any non-hidden compatibility flag should obtain its default
+ // by asking SvtCompatibilityOptions, see below.
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ const SvtCompatibilityOptions aOptions;
+ mbParaSpaceMax = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddSpacing );
+ mbParaSpaceMaxAtPages = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddSpacingAtPages );
+ mbTabCompat = !aOptions.GetDefault( SvtCompatibilityEntry::Index::UseOurTabStops );
+ mbUseVirtualDevice = !aOptions.GetDefault( SvtCompatibilityEntry::Index::UsePrtMetrics );
+ mbAddExternalLeading = !aOptions.GetDefault( SvtCompatibilityEntry::Index::NoExtLeading );
+ mbOldLineSpacing = aOptions.GetDefault( SvtCompatibilityEntry::Index::UseLineSpacing );
+ mbAddParaSpacingToTableCells = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddTableSpacing );
+ mbAddParaLineSpacingToTableCells = aOptions.GetDefault( SvtCompatibilityEntry::Index::AddTableLineSpacing );
+ mbUseFormerObjectPos = aOptions.GetDefault( SvtCompatibilityEntry::Index::UseObjectPositioning );
+ mbUseFormerTextWrapping = aOptions.GetDefault( SvtCompatibilityEntry::Index::UseOurTextWrapping );
+ mbConsiderWrapOnObjPos = aOptions.GetDefault( SvtCompatibilityEntry::Index::ConsiderWrappingStyle );
+ mbDoNotJustifyLinesWithManualBreak = !aOptions.GetDefault( SvtCompatibilityEntry::Index::ExpandWordSpace );
+ mbProtectForm = aOptions.GetDefault( SvtCompatibilityEntry::Index::ProtectForm );
+ mbMsWordCompTrailingBlanks = aOptions.GetDefault( SvtCompatibilityEntry::Index::MsWordTrailingBlanks );
+ mbSubtractFlys = aOptions.GetDefault( SvtCompatibilityEntry::Index::SubtractFlysAnchoredAtFlys );
+ mbEmptyDbFieldHidesPara
+ = aOptions.GetDefault(SvtCompatibilityEntry::Index::EmptyDbFieldHidesPara);
+ }
+ else
+ {
+ mbParaSpaceMax = false;
+ mbParaSpaceMaxAtPages = false;
+ mbTabCompat = true;
+ mbUseVirtualDevice = true;
+ mbAddExternalLeading = true;
+ mbOldLineSpacing = false;
+ mbAddParaSpacingToTableCells = false;
+ mbAddParaLineSpacingToTableCells = false;
+ mbUseFormerObjectPos = false;
+ mbUseFormerTextWrapping = false;
+ mbConsiderWrapOnObjPos = false;
+ mbDoNotJustifyLinesWithManualBreak = true;
+ mbProtectForm = false;
+ mbMsWordCompTrailingBlanks = false;
+ mbSubtractFlys = false;
+ mbEmptyDbFieldHidesPara = true;
+ }
+
+ // COMPATIBILITY FLAGS END
+
+}
+
+
+sw::DocumentSettingManager::~DocumentSettingManager()
+{
+}
+
+/* IDocumentSettingAccess */
+bool sw::DocumentSettingManager::get(/*[in]*/ DocumentSettingId id) const
+{
+ switch (id)
+ {
+ // COMPATIBILITY FLAGS START
+ case DocumentSettingId::PARA_SPACE_MAX: return mbParaSpaceMax; //(n8Dummy1 & DUMMY_PARASPACEMAX);
+ case DocumentSettingId::PARA_SPACE_MAX_AT_PAGES: return mbParaSpaceMaxAtPages; //(n8Dummy1 & DUMMY_PARASPACEMAX_AT_PAGES);
+ case DocumentSettingId::TAB_COMPAT: return mbTabCompat; //(n8Dummy1 & DUMMY_TAB_COMPAT);
+ case DocumentSettingId::ADD_FLY_OFFSETS: return mbAddFlyOffsets; //(n8Dummy2 & DUMMY_ADD_FLY_OFFSETS);
+ case DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS: return mbAddVerticalFlyOffsets;
+ case DocumentSettingId::ADD_EXT_LEADING: return mbAddExternalLeading; //(n8Dummy2 & DUMMY_ADD_EXTERNAL_LEADING);
+ case DocumentSettingId::USE_VIRTUAL_DEVICE: return mbUseVirtualDevice; //(n8Dummy1 & DUMMY_USE_VIRTUAL_DEVICE);
+ case DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE: return mbUseHiResolutionVirtualDevice; //(n8Dummy2 & DUMMY_USE_HIRES_VIR_DEV);
+ case DocumentSettingId::OLD_NUMBERING: return mbOldNumbering;
+ case DocumentSettingId::OLD_LINE_SPACING: return mbOldLineSpacing;
+ case DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS: return mbAddParaSpacingToTableCells;
+ case DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS: return mbAddParaLineSpacingToTableCells;
+ case DocumentSettingId::USE_FORMER_OBJECT_POS: return mbUseFormerObjectPos;
+ case DocumentSettingId::USE_FORMER_TEXT_WRAPPING: return mbUseFormerTextWrapping;
+ case DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION: return mbConsiderWrapOnObjPos;
+ case DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK: return mbDoNotJustifyLinesWithManualBreak;
+ case DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING: return mbIgnoreFirstLineIndentInNumbering;
+ case DocumentSettingId::TABLE_ROW_KEEP: return mbTableRowKeep;
+ case DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION: return mbIgnoreTabsAndBlanksForLineCalculation;
+ case DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE: return mbDoNotCaptureDrawObjsOnPage;
+ // #i68949#
+ case DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME: return mbClipAsCharacterAnchoredWriterFlyFrames;
+ case DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING: return mbUnixForceZeroExtLeading;
+ case DocumentSettingId::TABS_RELATIVE_TO_INDENT : return mbTabRelativeToIndent;
+ case DocumentSettingId::PROTECT_FORM: return mbProtectForm;
+ // tdf#104349 tdf#104668
+ case DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS: return mbMsWordCompTrailingBlanks;
+ case DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY: return mbMsWordCompMinLineHeightByFly;
+ // #i89181#
+ case DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST: return mbTabAtLeftIndentForParagraphsInList;
+ case DocumentSettingId::INVERT_BORDER_SPACING: return mbInvertBorderSpacing;
+ case DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA: return mbCollapseEmptyCellPara;
+ case DocumentSettingId::SMALL_CAPS_PERCENTAGE_66: return mbSmallCapsPercentage66;
+ case DocumentSettingId::TAB_OVERFLOW: return mbTabOverflow;
+ case DocumentSettingId::UNBREAKABLE_NUMBERINGS: return mbUnbreakableNumberings;
+ case DocumentSettingId::CLIPPED_PICTURES: return mbClippedPictures;
+ case DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS: return mbBackgroundParaOverDrawings;
+ case DocumentSettingId::TAB_OVER_MARGIN: return mbTabOverMargin;
+ case DocumentSettingId::TAB_OVER_SPACING: return mbTabOverSpacing;
+ case DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK: return mbTreatSingleColumnBreakAsPageBreak;
+ case DocumentSettingId::SURROUND_TEXT_WRAP_SMALL: return mbSurroundTextWrapSmall;
+ case DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE: return mbPropLineSpacingShrinksFirstLine;
+ case DocumentSettingId::SUBTRACT_FLYS: return mbSubtractFlys;
+
+ case DocumentSettingId::BROWSE_MODE: return mbLastBrowseMode; // Attention: normally the SwViewShell has to be asked!
+ case DocumentSettingId::HTML_MODE: return mbHTMLMode;
+ case DocumentSettingId::GLOBAL_DOCUMENT: return mbIsGlobalDoc;
+ case DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS: return mbGlblDocSaveLinks;
+ case DocumentSettingId::LABEL_DOCUMENT: return mbIsLabelDoc;
+ case DocumentSettingId::PURGE_OLE: return mbPurgeOLE;
+ case DocumentSettingId::KERN_ASIAN_PUNCTUATION: return mbKernAsianPunctuation;
+ case DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT: return mbDoNotResetParaAttrsForNumFont;
+ case DocumentSettingId::MATH_BASELINE_ALIGNMENT: return mbMathBaselineAlignment;
+ case DocumentSettingId::STYLES_NODEFAULT: return mbStylesNoDefault;
+ case DocumentSettingId::FLOATTABLE_NOMARGINS: return mbFloattableNomargins;
+ case DocumentSettingId::EMBED_FONTS: return mEmbedFonts;
+ case DocumentSettingId::EMBED_USED_FONTS: return mEmbedUsedFonts;
+ case DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS: return mEmbedLatinScriptFonts;
+ case DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS: return mEmbedAsianScriptFonts;
+ case DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS: return mEmbedComplexScriptFonts;
+ case DocumentSettingId::EMBED_SYSTEM_FONTS: return mEmbedSystemFonts;
+ case DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING: return mApplyParagraphMarkFormatToNumbering;
+ case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING: return mbDisableOffPagePositioning;
+ case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA: return mbEmptyDbFieldHidesPara;
+ case DocumentSettingId::CONTINUOUS_ENDNOTES: return mbContinuousEndnotes;
+ case DocumentSettingId::PROTECT_BOOKMARKS: return mbProtectBookmarks;
+ case DocumentSettingId::PROTECT_FIELDS: return mbProtectFields;
+ case DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA: return mbHeaderSpacingBelowLastPara;
+ case DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA: return mbFrameAutowidthWithMorePara;
+ case DocumentSettingId::GUTTER_AT_TOP:
+ return mbGutterAtTop;
+ case DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND: return mbFootnoteInColumnToPageEnd;
+ case DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE:
+ return mbAutoFirstLineIndentDisregardLineSpace;
+ case DocumentSettingId::WRAP_AS_CHAR_FLYS_LIKE_IN_OOXML: return mbWrapAsCharFlysLikeInOOXML;
+ case DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY: return mbNoNumberingShowFollowBy;
+ default:
+ OSL_FAIL("Invalid setting id");
+ }
+ return false;
+}
+
+void sw::DocumentSettingManager::set(/*[in]*/ DocumentSettingId id, /*[in]*/ bool value)
+{
+ switch (id)
+ {
+ // COMPATIBILITY FLAGS START
+ case DocumentSettingId::PARA_SPACE_MAX:
+ mbParaSpaceMax = value;
+ break;
+ case DocumentSettingId::PARA_SPACE_MAX_AT_PAGES:
+ mbParaSpaceMaxAtPages = value;
+ break;
+ case DocumentSettingId::TAB_COMPAT:
+ mbTabCompat = value;
+ break;
+ case DocumentSettingId::ADD_FLY_OFFSETS:
+ mbAddFlyOffsets = value;
+ break;
+ case DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS:
+ mbAddVerticalFlyOffsets = value;
+ break;
+ case DocumentSettingId::ADD_EXT_LEADING:
+ mbAddExternalLeading = value;
+ break;
+ case DocumentSettingId::USE_VIRTUAL_DEVICE:
+ mbUseVirtualDevice = value;
+ break;
+ case DocumentSettingId::USE_HIRES_VIRTUAL_DEVICE:
+ mbUseHiResolutionVirtualDevice = value;
+ break;
+ case DocumentSettingId::OLD_NUMBERING:
+ if (mbOldNumbering != value)
+ {
+ mbOldNumbering = value;
+
+ const SwNumRuleTable& rNmTable = m_rDoc.GetNumRuleTable();
+ for( SwNumRuleTable::size_type n = 0; n < rNmTable.size(); ++n )
+ rNmTable[n]->SetInvalidRule(true);
+
+ m_rDoc.UpdateNumRule();
+
+ SwNumRule *pOutlineRule = m_rDoc.GetOutlineNumRule();
+ if (pOutlineRule)
+ {
+ pOutlineRule->Validate(m_rDoc);
+ // counting of phantoms depends on <IsOldNumbering()>
+ pOutlineRule->SetCountPhantoms( !mbOldNumbering );
+ }
+ }
+ break;
+ case DocumentSettingId::OLD_LINE_SPACING:
+ mbOldLineSpacing = value;
+ break;
+ case DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS:
+ mbAddParaSpacingToTableCells = value;
+ break;
+ case DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS:
+ mbAddParaLineSpacingToTableCells = value;
+ break;
+ case DocumentSettingId::USE_FORMER_OBJECT_POS:
+ mbUseFormerObjectPos = value;
+ break;
+ case DocumentSettingId::USE_FORMER_TEXT_WRAPPING:
+ mbUseFormerTextWrapping = value;
+ break;
+ case DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION:
+ mbConsiderWrapOnObjPos = value;
+ break;
+ case DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK:
+ mbDoNotJustifyLinesWithManualBreak = value;
+ break;
+ case DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING:
+ mbIgnoreFirstLineIndentInNumbering = value;
+ break;
+
+ case DocumentSettingId::TABLE_ROW_KEEP:
+ mbTableRowKeep = value;
+ break;
+
+ case DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION:
+ mbIgnoreTabsAndBlanksForLineCalculation = value;
+ break;
+
+ case DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE:
+ mbDoNotCaptureDrawObjsOnPage = value;
+ break;
+
+ // #i68949#
+ case DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME:
+ mbClipAsCharacterAnchoredWriterFlyFrames = value;
+ break;
+
+ case DocumentSettingId::UNIX_FORCE_ZERO_EXT_LEADING:
+ mbUnixForceZeroExtLeading = value;
+ break;
+
+ case DocumentSettingId::PROTECT_FORM:
+ mbProtectForm = value;
+ break;
+
+ // tdf#140349
+ case DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS:
+ mbMsWordCompTrailingBlanks = value;
+ break;
+
+ case DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY:
+ mbMsWordCompMinLineHeightByFly = value;
+ break;
+
+ case DocumentSettingId::TABS_RELATIVE_TO_INDENT:
+ mbTabRelativeToIndent = value;
+ break;
+ // #i89181#
+ case DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST:
+ mbTabAtLeftIndentForParagraphsInList = value;
+ break;
+
+ case DocumentSettingId::INVERT_BORDER_SPACING:
+ mbInvertBorderSpacing = value;
+ break;
+
+ case DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA:
+ mbCollapseEmptyCellPara = value;
+ break;
+
+ case DocumentSettingId::SMALL_CAPS_PERCENTAGE_66:
+ mbSmallCapsPercentage66 = value;
+ break;
+
+ case DocumentSettingId::TAB_OVERFLOW:
+ mbTabOverflow = value;
+ break;
+
+ case DocumentSettingId::UNBREAKABLE_NUMBERINGS:
+ mbUnbreakableNumberings = value;
+ break;
+
+ case DocumentSettingId::CLIPPED_PICTURES:
+ mbClippedPictures = value;
+ break;
+
+ case DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS:
+ mbBackgroundParaOverDrawings = value;
+ break;
+
+ case DocumentSettingId::TAB_OVER_MARGIN:
+ mbTabOverMargin = value;
+ break;
+
+ case DocumentSettingId::TAB_OVER_SPACING:
+ mbTabOverSpacing = value;
+ break;
+
+ case DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK:
+ mbTreatSingleColumnBreakAsPageBreak = value;
+ break;
+
+ case DocumentSettingId::SURROUND_TEXT_WRAP_SMALL:
+ mbSurroundTextWrapSmall = value;
+ break;
+
+ case DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE:
+ mbPropLineSpacingShrinksFirstLine = value;
+ break;
+
+ case DocumentSettingId::SUBTRACT_FLYS:
+ mbSubtractFlys = value;
+ break;
+
+ case DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE:
+ mbAutoFirstLineIndentDisregardLineSpace = value;
+ break;
+
+ case DocumentSettingId::WRAP_AS_CHAR_FLYS_LIKE_IN_OOXML:
+ mbWrapAsCharFlysLikeInOOXML = value;
+ break;
+
+ case DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY:
+ mbNoNumberingShowFollowBy = value;
+ break;
+
+ // COMPATIBILITY FLAGS END
+
+ case DocumentSettingId::BROWSE_MODE: //can be used temporary (load/save) when no SwViewShell is available
+ // Can't render in webview successfully.
+ if (comphelper::LibreOfficeKit::isActive())
+ mbLastBrowseMode = false;
+ else
+ mbLastBrowseMode = value;
+ break;
+
+ case DocumentSettingId::HTML_MODE:
+ mbHTMLMode = value;
+ break;
+
+ case DocumentSettingId::GLOBAL_DOCUMENT:
+ mbIsGlobalDoc = value;
+ break;
+
+ case DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS:
+ mbGlblDocSaveLinks = value;
+ break;
+
+ case DocumentSettingId::LABEL_DOCUMENT:
+ mbIsLabelDoc = value;
+ break;
+
+ case DocumentSettingId::PURGE_OLE:
+ mbPurgeOLE = value;
+ break;
+
+ case DocumentSettingId::KERN_ASIAN_PUNCTUATION:
+ mbKernAsianPunctuation = value;
+ break;
+
+ case DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT:
+ mbDoNotResetParaAttrsForNumFont = value;
+ break;
+ case DocumentSettingId::MATH_BASELINE_ALIGNMENT:
+ mbMathBaselineAlignment = value;
+ break;
+ case DocumentSettingId::STYLES_NODEFAULT:
+ mbStylesNoDefault = value;
+ break;
+ case DocumentSettingId::FLOATTABLE_NOMARGINS:
+ mbFloattableNomargins = value;
+ break;
+ case DocumentSettingId::EMBED_FONTS:
+ mEmbedFonts = value;
+ break;
+ case DocumentSettingId::EMBED_USED_FONTS:
+ mEmbedUsedFonts = value;
+ break;
+ case DocumentSettingId::EMBED_LATIN_SCRIPT_FONTS:
+ mEmbedLatinScriptFonts = value;
+ break;
+ case DocumentSettingId::EMBED_ASIAN_SCRIPT_FONTS:
+ mEmbedAsianScriptFonts = value;
+ break;
+ case DocumentSettingId::EMBED_COMPLEX_SCRIPT_FONTS:
+ mEmbedComplexScriptFonts = value;
+ break;
+ case DocumentSettingId::EMBED_SYSTEM_FONTS:
+ mEmbedSystemFonts = value;
+ break;
+ case DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING:
+ mApplyParagraphMarkFormatToNumbering = value;
+ break;
+ case DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING:
+ mbDisableOffPagePositioning = value;
+ break;
+ case DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA:
+ mbEmptyDbFieldHidesPara = value;
+ break;
+ case DocumentSettingId::CONTINUOUS_ENDNOTES:
+ mbContinuousEndnotes = value;
+ break;
+ case DocumentSettingId::PROTECT_BOOKMARKS:
+ mbProtectBookmarks = value;
+ break;
+ case DocumentSettingId::PROTECT_FIELDS:
+ mbProtectFields = value;
+ break;
+ case DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA:
+ mbHeaderSpacingBelowLastPara = value;
+ break;
+ case DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA:
+ mbFrameAutowidthWithMorePara = value;
+ break;
+ case DocumentSettingId::GUTTER_AT_TOP:
+ mbGutterAtTop = value;
+ break;
+ case DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND:
+ mbFootnoteInColumnToPageEnd = value;
+ break;
+ default:
+ OSL_FAIL("Invalid setting id");
+ }
+}
+
+const css::i18n::ForbiddenCharacters*
+ sw::DocumentSettingManager::getForbiddenCharacters(/*[in]*/ LanguageType nLang, /*[in]*/ bool bLocaleData ) const
+{
+ const css::i18n::ForbiddenCharacters* pRet = nullptr;
+ if (mxForbiddenCharsTable)
+ pRet = mxForbiddenCharsTable->GetForbiddenCharacters( nLang, false );
+ if( bLocaleData && !pRet && g_pBreakIt )
+ pRet = &g_pBreakIt->GetForbidden( nLang );
+ return pRet;
+}
+
+void sw::DocumentSettingManager::setForbiddenCharacters(/*[in]*/ LanguageType nLang,
+ /*[in]*/ const css::i18n::ForbiddenCharacters& rFChars )
+{
+ if (!mxForbiddenCharsTable)
+ mxForbiddenCharsTable = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext());
+ mxForbiddenCharsTable->SetForbiddenCharacters( nLang, rFChars );
+
+ SdrModel *pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ if( pDrawModel )
+ {
+ pDrawModel->SetForbiddenCharsTable(mxForbiddenCharsTable);
+ if( !m_rDoc.IsInReading() )
+ pDrawModel->ReformatAllTextObjects();
+ }
+
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pTmpRoot && !m_rDoc.IsInReading() )
+ {
+ pTmpRoot->StartAllAction();
+ for(SwRootFrame* aLayout : m_rDoc.GetAllLayouts())
+ aLayout->InvalidateAllContent(SwInvalidateFlags::Size);
+ pTmpRoot->EndAllAction();
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+std::shared_ptr<SvxForbiddenCharactersTable>& sw::DocumentSettingManager::getForbiddenCharacterTable()
+{
+ if (!mxForbiddenCharsTable)
+ mxForbiddenCharsTable = SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext());
+ return mxForbiddenCharsTable;
+}
+
+const std::shared_ptr<SvxForbiddenCharactersTable>& sw::DocumentSettingManager::getForbiddenCharacterTable() const
+{
+ return mxForbiddenCharsTable;
+}
+
+sal_uInt16 sw::DocumentSettingManager::getLinkUpdateMode( /*[in]*/bool bGlobalSettings ) const
+{
+ sal_uInt16 nRet = mnLinkUpdMode;
+ if( bGlobalSettings && GLOBALSETTING == nRet )
+ nRet = SW_MOD()->GetLinkUpdMode();
+ return nRet;
+}
+
+void sw::DocumentSettingManager::setLinkUpdateMode( /*[in]*/sal_uInt16 eMode )
+{
+ mnLinkUpdMode = eMode;
+}
+
+SwFieldUpdateFlags sw::DocumentSettingManager::getFieldUpdateFlags( /*[in]*/bool bGlobalSettings ) const
+{
+ SwFieldUpdateFlags eRet = meFieldUpdMode;
+ if( bGlobalSettings && AUTOUPD_GLOBALSETTING == eRet )
+ eRet = SW_MOD()->GetFieldUpdateFlags();
+ return eRet;
+}
+
+void sw::DocumentSettingManager::setFieldUpdateFlags(/*[in]*/SwFieldUpdateFlags eMode )
+{
+ meFieldUpdMode = eMode;
+}
+
+CharCompressType sw::DocumentSettingManager::getCharacterCompressionType() const
+{
+ return meChrCmprType;
+}
+
+void sw::DocumentSettingManager::setCharacterCompressionType( /*[in]*/CharCompressType n )
+{
+ if( meChrCmprType == n )
+ return;
+
+ meChrCmprType = n;
+
+ SdrModel *pDrawModel = m_rDoc.getIDocumentDrawModelAccess().GetDrawModel();
+ if( pDrawModel )
+ {
+ pDrawModel->SetCharCompressType( n );
+ if( !m_rDoc.IsInReading() )
+ pDrawModel->ReformatAllTextObjects();
+ }
+
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pTmpRoot && !m_rDoc.IsInReading() )
+ {
+ pTmpRoot->StartAllAction();
+ for( auto aLayout : m_rDoc.GetAllLayouts() )
+ aLayout->InvalidateAllContent(SwInvalidateFlags::Size);
+ pTmpRoot->EndAllAction();
+ }
+ m_rDoc.getIDocumentState().SetModified();
+}
+
+
+void sw::DocumentSettingManager::ReplaceCompatibilityOptions(const DocumentSettingManager& rSource)
+{
+ Setn32DummyCompatibilityOptions1( rSource.Getn32DummyCompatibilityOptions1() );
+ Setn32DummyCompatibilityOptions2( rSource.Getn32DummyCompatibilityOptions2() );
+
+ // No mbHTMLMode: this is the subset of mbLastBrowseMode, which can be temporarily enabled even
+ // for non-SwWebDocShells.
+ // No mbIsGlobalDoc: this is true for SwGlobalDocShells.
+ mbGlblDocSaveLinks = rSource.mbGlblDocSaveLinks;
+ mbIsLabelDoc = rSource.mbIsLabelDoc;
+ mbPurgeOLE = rSource.mbPurgeOLE;
+ mbKernAsianPunctuation = rSource.mbKernAsianPunctuation;
+ mbParaSpaceMax = rSource.mbParaSpaceMax;
+ mbParaSpaceMaxAtPages = rSource.mbParaSpaceMaxAtPages;
+ mbTabCompat = rSource.mbTabCompat;
+ mbUseVirtualDevice = rSource.mbUseVirtualDevice;
+ mbAddFlyOffsets = rSource.mbAddFlyOffsets;
+ mbAddVerticalFlyOffsets = rSource.mbAddVerticalFlyOffsets;
+ mbAddExternalLeading = rSource.mbAddExternalLeading;
+ mbUseHiResolutionVirtualDevice = rSource.mbUseHiResolutionVirtualDevice;
+ mbOldLineSpacing = rSource.mbOldLineSpacing;
+ mbAddParaSpacingToTableCells = rSource.mbAddParaSpacingToTableCells;
+ mbAddParaLineSpacingToTableCells = rSource.mbAddParaLineSpacingToTableCells;
+ mbUseFormerObjectPos = rSource.mbUseFormerObjectPos;
+ mbUseFormerTextWrapping = rSource.mbUseFormerTextWrapping;
+ mbConsiderWrapOnObjPos = rSource.mbConsiderWrapOnObjPos;
+ mbMathBaselineAlignment = rSource.mbMathBaselineAlignment;
+ mbStylesNoDefault = rSource.mbStylesNoDefault;
+ mbFloattableNomargins = rSource.mbFloattableNomargins;
+ mbOldNumbering = rSource.mbOldNumbering;
+ mbIgnoreFirstLineIndentInNumbering = rSource.mbIgnoreFirstLineIndentInNumbering;
+ mbDoNotJustifyLinesWithManualBreak = rSource.mbDoNotJustifyLinesWithManualBreak;
+ mbDoNotResetParaAttrsForNumFont = rSource.mbDoNotResetParaAttrsForNumFont;
+ mbTableRowKeep = rSource.mbTableRowKeep;
+ mbIgnoreTabsAndBlanksForLineCalculation = rSource.mbIgnoreTabsAndBlanksForLineCalculation;
+ mbDoNotCaptureDrawObjsOnPage = rSource.mbDoNotCaptureDrawObjsOnPage;
+ mbClipAsCharacterAnchoredWriterFlyFrames = rSource.mbClipAsCharacterAnchoredWriterFlyFrames;
+ mbUnixForceZeroExtLeading = rSource.mbUnixForceZeroExtLeading;
+ mbTabRelativeToIndent = rSource.mbTabRelativeToIndent;
+ mbProtectForm = rSource.mbProtectForm;
+ mbMsWordCompTrailingBlanks = rSource.mbMsWordCompTrailingBlanks;
+ mbMsWordCompMinLineHeightByFly = rSource.mbMsWordCompMinLineHeightByFly;
+ mbInvertBorderSpacing = rSource.mbInvertBorderSpacing;
+ mbCollapseEmptyCellPara = rSource.mbCollapseEmptyCellPara;
+ mbTabAtLeftIndentForParagraphsInList = rSource.mbTabAtLeftIndentForParagraphsInList;
+ mbSmallCapsPercentage66 = rSource.mbSmallCapsPercentage66;
+ mbTabOverflow = rSource.mbTabOverflow;
+ mbUnbreakableNumberings = rSource.mbUnbreakableNumberings;
+ mbClippedPictures = rSource.mbClippedPictures;
+ mbBackgroundParaOverDrawings = rSource.mbBackgroundParaOverDrawings;
+ mbTabOverMargin = rSource.mbTabOverMargin;
+ mbTabOverSpacing = rSource.mbTabOverSpacing;
+ mbTreatSingleColumnBreakAsPageBreak = rSource.mbTreatSingleColumnBreakAsPageBreak;
+ mbSurroundTextWrapSmall = rSource.mbSurroundTextWrapSmall;
+ mbPropLineSpacingShrinksFirstLine = rSource.mbPropLineSpacingShrinksFirstLine;
+ mbSubtractFlys = rSource.mbSubtractFlys;
+ // No mbLastBrowseMode: this is false by default everywhere
+ mbDisableOffPagePositioning = rSource.mbDisableOffPagePositioning;
+ mbEmptyDbFieldHidesPara = rSource.mbEmptyDbFieldHidesPara;
+ mbContinuousEndnotes = rSource.mbContinuousEndnotes;
+ // No mbProtectBookmarks: this is false by default everywhere
+ // No mbProtectFields: this is false by default everywhere
+ mbHeaderSpacingBelowLastPara = rSource.mbHeaderSpacingBelowLastPara;
+ mbFrameAutowidthWithMorePara = rSource.mbFrameAutowidthWithMorePara;
+ mbFootnoteInColumnToPageEnd = rSource.mbFootnoteInColumnToPageEnd;
+}
+
+sal_uInt32 sw::DocumentSettingManager::Getn32DummyCompatibilityOptions1() const
+{
+ return mn32DummyCompatibilityOptions1;
+}
+
+void sw::DocumentSettingManager::Setn32DummyCompatibilityOptions1( const sal_uInt32 CompatibilityOptions1 )
+{
+ mn32DummyCompatibilityOptions1 = CompatibilityOptions1;
+}
+
+sal_uInt32 sw::DocumentSettingManager::Getn32DummyCompatibilityOptions2() const
+{
+ return mn32DummyCompatibilityOptions2;
+}
+
+void sw::DocumentSettingManager::Setn32DummyCompatibilityOptions2( const sal_uInt32 CompatibilityOptions2 )
+{
+ mn32DummyCompatibilityOptions2 = CompatibilityOptions2;
+}
+
+void sw::DocumentSettingManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("DocumentSettingManager"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbHTMLMode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbHTMLMode).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIsGlobalDoc"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIsGlobalDoc).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbGlblDocSaveLinks"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbGlblDocSaveLinks).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIsLabelDoc"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIsLabelDoc).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbPurgeOLE"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbPurgeOLE).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbKernAsianPunctuation"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbKernAsianPunctuation).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbParaSpaceMax"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbParaSpaceMax).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbParaSpaceMaxAtPages"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbParaSpaceMaxAtPages).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabCompat"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabCompat).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseVirtualDevice"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseVirtualDevice).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddFlyOffsets"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddFlyOffsets).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddVerticalFlyOffsets"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddVerticalFlyOffsets).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddExternalLeading"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddExternalLeading).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseHiResolutionVirtualDevice"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseHiResolutionVirtualDevice).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbOldLineSpacing"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbOldLineSpacing).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddParaSpacingToTableCells"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddParaSpacingToTableCells).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbAddParaLineSpacingToTableCells"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbAddParaLineSpacingToTableCells).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseFormerObjectPos"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseFormerObjectPos).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUseFormerTextWrapping"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUseFormerTextWrapping).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbConsiderWrapOnObjPos"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbConsiderWrapOnObjPos).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbMathBaselineAlignment"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbMathBaselineAlignment).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbStylesNoDefault"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbStylesNoDefault).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbFloattableNomargins"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbFloattableNomargins).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbOldNumbering"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbOldNumbering).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIgnoreFirstLineIndentInNumbering"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIgnoreFirstLineIndentInNumbering).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotJustifyLinesWithManualBreak"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotJustifyLinesWithManualBreak).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotResetParaAttrsForNumFont"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotResetParaAttrsForNumFont).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTableRowKeep"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTableRowKeep).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbIgnoreTabsAndBlanksForLineCalculation"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbIgnoreTabsAndBlanksForLineCalculation).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDoNotCaptureDrawObjsOnPage"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDoNotCaptureDrawObjsOnPage).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbClipAsCharacterAnchoredWriterFlyFrames"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbClipAsCharacterAnchoredWriterFlyFrames).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUnixForceZeroExtLeading"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUnixForceZeroExtLeading).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabRelativeToIndent"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabRelativeToIndent).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbProtectForm"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbProtectForm).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbMsWordCompTrailingBlanks"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbMsWordCompTrailingBlanks).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbMsWordCompMinLineHeightByFly"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbMsWordCompMinLineHeightByFly).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbInvertBorderSpacing"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbInvertBorderSpacing).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbCollapseEmptyCellPara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbCollapseEmptyCellPara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabAtLeftIndentForParagraphsInList"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabAtLeftIndentForParagraphsInList).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbSmallCapsPercentage66"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbSmallCapsPercentage66).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverflow"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabOverflow).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbUnbreakableNumberings"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbUnbreakableNumberings).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbClippedPictures"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbClippedPictures).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbBackgroundParaOverDrawings"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbBackgroundParaOverDrawings).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverMargin"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabOverMargin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTabOverSpacing"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTabOverSpacing).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbTreatSingleColumnBreakAsPageBreak"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbTreatSingleColumnBreakAsPageBreak).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbSurroundTextWrapSmall"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbSurroundTextWrapSmall).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbPropLineSpacingShrinksFirstLine"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbPropLineSpacingShrinksFirstLine).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbSubtractFlys"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbSubtractFlys).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbLastBrowseMode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbLastBrowseMode).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbDisableOffPagePositioning"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbDisableOffPagePositioning).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbEmptyDbFieldHidesPara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbEmptyDbFieldHidesPara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbContinuousEndnotes"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbContinuousEndnotes).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbHeaderSpacingBelowLastPara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbHeaderSpacingBelowLastPara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbFrameAutowidthWithMorePara"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbFrameAutowidthWithMorePara).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbGutterAtTop"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbGutterAtTop).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbFootnoteInColumnToPageEnd"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(mbFootnoteInColumnToPageEnd).getStr()));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mnImagePreferredDPI"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(mnImagePreferredDPI).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentStateManager.cxx b/sw/source/core/doc/DocumentStateManager.cxx
new file mode 100644
index 000000000..bf965d54b
--- /dev/null
+++ b/sw/source/core/doc/DocumentStateManager.cxx
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentStateManager.hxx>
+#include <doc.hxx>
+#include <DocumentStatisticsManager.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <acorrect.hxx>
+
+namespace sw
+{
+
+DocumentStateManager::DocumentStateManager( SwDoc& i_rSwdoc ) :
+ m_rDoc( i_rSwdoc ),
+ mbEnableSetModified(true),
+ mbModified(false),
+ mbUpdateExpField(false),
+ mbNewDoc(false),
+ mbInCallModified(false)
+{
+}
+
+void DocumentStateManager::SetModified()
+{
+ if (!IsEnableSetModified())
+ return;
+
+ m_rDoc.GetDocumentLayoutManager().ClearSwLayouterEntries();
+ mbModified = true;
+ m_rDoc.GetDocumentStatisticsManager().SetDocStatModified( true );
+ if( m_rDoc.GetOle2Link().IsSet() )
+ {
+ mbInCallModified = true;
+ m_rDoc.GetOle2Link().Call( true );
+ mbInCallModified = false;
+ }
+
+ if( m_rDoc.GetAutoCorrExceptWord() && !m_rDoc.GetAutoCorrExceptWord()->IsDeleted() )
+ m_rDoc.DeleteAutoCorrExceptWord();
+}
+
+void DocumentStateManager::ResetModified()
+{
+ // give the old and new modified state to the link
+ // Bit 0: -> old state
+ // Bit 1: -> new state
+ bool bOldModified = mbModified;
+ mbModified = false;
+ m_rDoc.GetDocumentStatisticsManager().SetDocStatModified( false );
+ m_rDoc.GetIDocumentUndoRedo().SetUndoNoModifiedPosition();
+ if( bOldModified && m_rDoc.GetOle2Link().IsSet() )
+ {
+ mbInCallModified = true;
+ m_rDoc.GetOle2Link().Call( false );
+ mbInCallModified = false;
+ }
+}
+
+bool DocumentStateManager::IsModified() const
+{
+ return mbModified;
+}
+
+bool DocumentStateManager::IsEnableSetModified() const
+{
+ return mbEnableSetModified;
+}
+
+void DocumentStateManager::SetEnableSetModified(bool bEnableSetModified)
+{
+ mbEnableSetModified = bEnableSetModified;
+}
+
+bool DocumentStateManager::IsInCallModified() const
+{
+ return mbInCallModified;
+}
+
+bool DocumentStateManager::IsUpdateExpField() const
+{
+ return mbUpdateExpField;
+}
+
+bool DocumentStateManager::IsNewDoc() const
+{
+ return mbNewDoc;
+}
+
+void DocumentStateManager::SetNewDoc(bool b)
+{
+ mbNewDoc = b;
+}
+
+void DocumentStateManager::SetUpdateExpFieldStat(bool b)
+{
+ mbUpdateExpField = b;
+}
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentStatisticsManager.cxx b/sw/source/core/doc/DocumentStatisticsManager.cxx
new file mode 100644
index 000000000..79df62ff1
--- /dev/null
+++ b/sw/source/core/doc/DocumentStatisticsManager.cxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentStatisticsManager.hxx>
+#include <doc.hxx>
+#include <fldbas.hxx>
+#include <docsh.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <view.hxx>
+#include <ndtxt.hxx>
+#include <fmtfld.hxx>
+#include <rootfrm.hxx>
+#include <docufld.hxx>
+#include <docstat.hxx>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace ::com::sun::star;
+
+namespace sw
+{
+
+DocumentStatisticsManager::DocumentStatisticsManager( SwDoc& i_rSwdoc )
+ : m_rDoc( i_rSwdoc ),
+ mpDocStat( new SwDocStat ),
+ mbInitialized( false ),
+ maStatsUpdateIdle( i_rSwdoc, "sw::DocumentStatisticsManager maStatsUpdateIdle" )
+{
+ maStatsUpdateIdle.SetPriority( TaskPriority::LOWEST );
+ maStatsUpdateIdle.SetInvokeHandler( LINK( this, DocumentStatisticsManager, DoIdleStatsUpdate ) );
+}
+
+void DocumentStatisticsManager::DocInfoChgd(bool const isEnableSetModified)
+{
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DocInfo )->UpdateFields();
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::TemplateName )->UpdateFields();
+ if (isEnableSetModified)
+ {
+ m_rDoc.getIDocumentState().SetModified();
+ }
+}
+
+const SwDocStat& DocumentStatisticsManager::GetDocStat() const
+{
+ return *mpDocStat;
+}
+
+void DocumentStatisticsManager::SetDocStatModified(bool bSet)
+{
+ mpDocStat->bModified = bSet;
+}
+
+const SwDocStat& DocumentStatisticsManager::GetUpdatedDocStat( bool bCompleteAsync, bool bFields )
+{
+ if( mpDocStat->bModified || !mbInitialized)
+ {
+ UpdateDocStat( bCompleteAsync, bFields );
+ }
+ return *mpDocStat;
+}
+
+void DocumentStatisticsManager::SetDocStat( const SwDocStat& rStat )
+{
+ *mpDocStat = rStat;
+ mbInitialized = true;
+}
+
+void DocumentStatisticsManager::UpdateDocStat( bool bCompleteAsync, bool bFields )
+{
+ if( !mpDocStat->bModified && mbInitialized)
+ return;
+
+ if (!bCompleteAsync)
+ {
+ maStatsUpdateIdle.Stop();
+ while (IncrementalDocStatCalculate(
+ std::numeric_limits<tools::Long>::max(), bFields)) {}
+ }
+ else if (IncrementalDocStatCalculate(5000, bFields))
+ maStatsUpdateIdle.Start();
+ else
+ maStatsUpdateIdle.Stop();
+}
+
+// returns true while there is more to do
+bool DocumentStatisticsManager::IncrementalDocStatCalculate(tools::Long nChars, bool bFields)
+{
+ mbInitialized = true;
+ mpDocStat->Reset();
+ mpDocStat->nPara = 0; // default is 1!
+
+ // This is the inner loop - at least while the paras are dirty.
+ for( SwNodeOffset i = m_rDoc.GetNodes().Count(); i > SwNodeOffset(0) && nChars > 0; )
+ {
+ SwNode* pNd = m_rDoc.GetNodes()[ --i ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ {
+ tools::Long const nOldChars(mpDocStat->nChar);
+ SwTextNode *pText = static_cast< SwTextNode * >( pNd );
+ if (pText->CountWords(*mpDocStat, 0, pText->GetText().getLength()))
+ {
+ nChars -= (mpDocStat->nChar - nOldChars);
+ }
+ break;
+ }
+ case SwNodeType::Table: ++mpDocStat->nTable; break;
+ case SwNodeType::Grf: ++mpDocStat->nGrf; break;
+ case SwNodeType::Ole: ++mpDocStat->nOLE; break;
+ case SwNodeType::Section: break;
+ default: break;
+ }
+ }
+
+ // #i93174#: notes contain paragraphs that are not nodes
+ {
+ SwFieldType * const pPostits( m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit) );
+ std::vector<SwFormatField*> vFields;
+ pPostits->GatherFields(vFields);
+ for(auto pFormatField : vFields)
+ {
+ const auto pField = static_cast<SwPostItField const*>(pFormatField->GetField());
+ mpDocStat->nAllPara += pField->GetNumberOfParagraphs();
+ }
+ }
+
+ mpDocStat->nPage = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ? m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetPageNum() : 0;
+ SetDocStatModified( false );
+
+ css::uno::Sequence < css::beans::NamedValue > aStat( mpDocStat->nPage ? 8 : 7);
+ auto pStat = aStat.getArray();
+ sal_Int32 n=0;
+ pStat[n].Name = "TableCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nTable);
+ pStat[n].Name = "ImageCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nGrf);
+ pStat[n].Name = "ObjectCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nOLE);
+ if ( mpDocStat->nPage )
+ {
+ pStat[n].Name = "PageCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nPage);
+ }
+ pStat[n].Name = "ParagraphCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nPara);
+ pStat[n].Name = "WordCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nWord);
+ pStat[n].Name = "CharacterCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nChar);
+ pStat[n].Name = "NonWhitespaceCharacterCount";
+ pStat[n++].Value <<= static_cast<sal_Int32>(mpDocStat->nCharExcludingSpaces);
+
+ // For e.g. autotext documents there is no pSwgInfo (#i79945)
+ SwDocShell* pObjShell(m_rDoc.GetDocShell());
+ if (pObjShell)
+ {
+ const uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pObjShell->GetModel(), uno::UNO_QUERY_THROW);
+ const uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ // #i96786#: do not set modified flag when updating statistics
+ const bool bDocWasModified( m_rDoc.getIDocumentState().IsModified() );
+ const ModifyBlocker_Impl b(pObjShell);
+ // rhbz#1081176: don't jump to cursor pos because of (temporary)
+ // activation of modified flag triggering move to input position
+ auto aViewGuard(pObjShell->LockAllViews());
+ xDocProps->setDocumentStatistics(aStat);
+ if (!bDocWasModified)
+ {
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+ }
+
+ // optionally update stat. fields
+ if (bFields)
+ {
+ SwFieldType *pType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocStat);
+ pType->UpdateFields();
+ }
+
+ return nChars < 0;
+}
+
+IMPL_LINK( DocumentStatisticsManager, DoIdleStatsUpdate, Timer *, pIdle, void )
+{
+ if (IncrementalDocStatCalculate(32000))
+ pIdle->Start();
+ SwView* pView = m_rDoc.GetDocShell() ? m_rDoc.GetDocShell()->GetView() : nullptr;
+ if( pView )
+ pView->UpdateDocStats();
+}
+
+DocumentStatisticsManager::~DocumentStatisticsManager()
+{
+ maStatsUpdateIdle.Stop();
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentStylePoolManager.cxx b/sw/source/core/doc/DocumentStylePoolManager.cxx
new file mode 100644
index 000000000..cba46902b
--- /dev/null
+++ b/sw/source/core/doc/DocumentStylePoolManager.cxx
@@ -0,0 +1,2748 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentStylePoolManager.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <paratr.hxx>
+#include <poolfmt.hxx>
+#include <fmtornt.hxx>
+#include <charfmt.hxx>
+#include <fmtsrnd.hxx>
+#include <docary.hxx>
+#include <pagedesc.hxx>
+#include <frmfmt.hxx>
+#include <fmtline.hxx>
+#include <numrule.hxx>
+#include <hints.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <strings.hrc>
+#include <frmatr.hxx>
+#include <frameformats.hxx>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+
+bool IsConditionalByPoolId(sal_uInt16 nId)
+{
+ // TODO: why is this style conditional?
+ // If it is changed to no longer be conditional, then a style "Text Body"
+ // will be imported without its conditions from ODF.
+ return RES_POOLCOLL_TEXT == nId;
+}
+
+namespace
+{
+ const sal_uInt16 PT_3 = 3 * 20; // 3 pt
+ const sal_uInt16 PT_6 = 6 * 20; // 6 pt
+ const sal_uInt16 PT_7 = 7 * 20; // 7 pt
+ const sal_uInt16 PT_9 = 9 * 20; // 9 pt
+ const sal_uInt16 PT_10 = 10 * 20; // 10 pt
+ const sal_uInt16 PT_12 = 12 * 20; // 12 pt
+ const sal_uInt16 PT_13 = 13 * 20; // 13 pt
+ const sal_uInt16 PT_14 = 14 * 20; // 14 pt
+ const sal_uInt16 PT_16 = 16 * 20; // 16 pt
+ const sal_uInt16 PT_18 = 18 * 20; // 18 pt
+ const sal_uInt16 PT_24 = 24 * 20; // 24 pt
+ const sal_uInt16 PT_28 = 28 * 20; // 28 pt
+
+ const sal_uInt16 HTML_PARSPACE = o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip);
+
+ const sal_uInt16 aHeadlineSizes[ 2 * MAXLEVEL ] = {
+ // we do everything percentual now:
+ PT_18, PT_16, PT_14, PT_13, PT_12,
+ PT_12, PT_10, PT_10, PT_9, PT_9, // normal
+
+ PT_24, PT_18, PT_14, PT_12, PT_10,
+ PT_7, PT_7, PT_7, PT_7, PT_7 // HTML mode
+ };
+
+ tools::Long lcl_GetRightMargin( SwDoc& rDoc )
+ {
+ // Make sure that the printer settings are taken over to the standard
+ // page style
+ const SwFrameFormat& rPgDscFormat = rDoc.GetPageDesc( 0 ).GetMaster();
+ const SvxLRSpaceItem& rLR = rPgDscFormat.GetLRSpace();
+ const tools::Long nLeft = rLR.GetLeft();
+ const tools::Long nRight = rLR.GetRight();
+ const tools::Long nWidth = rPgDscFormat.GetFrameSize().GetWidth();
+ return nWidth - nLeft - nRight;
+ }
+
+ void lcl_SetDfltFont( DefaultFontType nFntType, SfxItemSet& rSet )
+ {
+ static struct {
+ sal_uInt16 nResLngId;
+ sal_uInt16 nResFntId;
+ } aArr[ 3 ] = {
+ { RES_CHRATR_LANGUAGE, RES_CHRATR_FONT },
+ { RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_FONT },
+ { RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_FONT }
+ };
+ for(const auto & n : aArr)
+ {
+ LanguageType nLng = static_cast<const SvxLanguageItem&>(rSet.GetPool()->GetDefaultItem(
+ n.nResLngId )).GetLanguage();
+ vcl::Font aFnt( OutputDevice::GetDefaultFont( nFntType,
+ nLng, GetDefaultFontFlags::OnlyOne ) );
+
+ rSet.Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(),
+ OUString(), aFnt.GetPitch(),
+ aFnt.GetCharSet(), n.nResFntId ));
+ }
+ }
+
+ void lcl_SetDfltFont( DefaultFontType nLatinFntType, DefaultFontType nCJKFntType,
+ DefaultFontType nCTLFntType, SfxItemSet& rSet )
+ {
+ static struct {
+ sal_uInt16 nResLngId;
+ sal_uInt16 nResFntId;
+ DefaultFontType nFntType;
+ } aArr[ 3 ] = {
+ { RES_CHRATR_LANGUAGE, RES_CHRATR_FONT, static_cast<DefaultFontType>(0) },
+ { RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_FONT, static_cast<DefaultFontType>(0) },
+ { RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_FONT, static_cast<DefaultFontType>(0) }
+ };
+ aArr[0].nFntType = nLatinFntType;
+ aArr[1].nFntType = nCJKFntType;
+ aArr[2].nFntType = nCTLFntType;
+
+ for(const auto & n : aArr)
+ {
+ LanguageType nLng = static_cast<const SvxLanguageItem&>(rSet.GetPool()->GetDefaultItem(
+ n.nResLngId )).GetLanguage();
+ vcl::Font aFnt( OutputDevice::GetDefaultFont( n.nFntType,
+ nLng, GetDefaultFontFlags::OnlyOne ) );
+
+ rSet.Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(),
+ OUString(), aFnt.GetPitch(),
+ aFnt.GetCharSet(), n.nResFntId ));
+ }
+ }
+
+ void lcl_SetHeadline( SwDoc& rDoc, SwTextFormatColl* pColl,
+ SfxItemSet& rSet,
+ sal_uInt16 nOutLvlBits, sal_uInt8 nLevel, bool bItalic )
+ {
+ SetAllScriptItem( rSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SvxFontHeightItem aHItem(240, 100, RES_CHRATR_FONTSIZE);
+ const bool bHTMLMode = rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE);
+ if( bHTMLMode )
+ aHItem.SetHeight( aHeadlineSizes[ MAXLEVEL + nLevel ] );
+ else
+ aHItem.SetHeight( aHeadlineSizes[ nLevel ] );
+ SetAllScriptItem( rSet, aHItem );
+
+ if( bItalic && !bHTMLMode )
+ SetAllScriptItem( rSet, SvxPostureItem( ITALIC_NORMAL, RES_CHRATR_POSTURE ) );
+
+ if( bHTMLMode )
+ {
+ lcl_SetDfltFont( DefaultFontType::LATIN_TEXT, DefaultFontType::CJK_TEXT,
+ DefaultFontType::CTL_TEXT, rSet );
+ }
+
+ if( !pColl )
+ return;
+
+ if( !( nOutLvlBits & ( 1 << nLevel )) )
+ {
+ pColl->AssignToListLevelOfOutlineStyle(nLevel);
+ if( !bHTMLMode )
+ {
+ SwNumRule * pOutlineRule = rDoc.GetOutlineNumRule();
+ const SwNumFormat& rNFormat = pOutlineRule->Get( nLevel );
+
+ if ( rNFormat.GetPositionAndSpaceMode() ==
+ SvxNumberFormat::LABEL_WIDTH_AND_POSITION &&
+ ( rNFormat.GetAbsLSpace() || rNFormat.GetFirstLineOffset() ) )
+ {
+ SvxLRSpaceItem aLR( pColl->GetFormatAttr( RES_LR_SPACE ) );
+ aLR.SetTextFirstLineOffsetValue( rNFormat.GetFirstLineOffset() );
+ //TODO: overflow
+ aLR.SetTextLeft( rNFormat.GetAbsLSpace() );
+ pColl->SetFormatAttr( aLR );
+ }
+
+ // All paragraph styles, which are assigned to a level of the
+ // outline style has to have the outline style set as its list style.
+ SwNumRuleItem aItem(pOutlineRule->GetName());
+ pColl->SetFormatAttr(aItem);
+ }
+ }
+ pColl->SetNextTextFormatColl( *rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+
+ void lcl_SetRegister( SwDoc& rDoc, SfxItemSet& rSet, sal_uInt16 nFact,
+ bool bHeader, bool bTab )
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ sal_uInt16 nLeft = o3tl::convert(5 * nFact, o3tl::Length::mm, o3tl::Length::twip);
+ aLR.SetTextLeft( nLeft );
+
+ rSet.Put( aLR );
+ if( bHeader )
+ {
+ SetAllScriptItem( rSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SetAllScriptItem( rSet, SvxFontHeightItem( PT_16, 100, RES_CHRATR_FONTSIZE ) );
+ }
+ if( bTab )
+ {
+ tools::Long nRightMargin = lcl_GetRightMargin( rDoc );
+ SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( nRightMargin - nLeft,
+ SvxTabAdjust::Right,
+ cDfltDecimalChar, '.' ));
+ rSet.Put( aTStops );
+ }
+ }
+
+ void lcl_SetNumBul( SwDoc& rDoc, SwTextFormatColl* pColl,
+ SfxItemSet& rSet,
+ sal_uInt16 nNxt, SwTwips nEZ, SwTwips nLeft,
+ SwTwips nUpper, SwTwips nLower )
+ {
+
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ aLR.SetTextFirstLineOffset( sal_uInt16(nEZ) );
+ aLR.SetTextLeft( sal_uInt16(nLeft) );
+ aUL.SetUpper( sal_uInt16(nUpper) );
+ aUL.SetLower( sal_uInt16(nLower) );
+ rSet.Put( aLR );
+ rSet.Put( aUL );
+
+ if( pColl )
+ pColl->SetNextTextFormatColl( *rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nNxt ));
+ }
+
+ void lcl_PutStdPageSizeIntoItemSet( SwDoc& rDoc, SfxItemSet& rSet )
+ {
+ SwPageDesc* pStdPgDsc = rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ SwFormatFrameSize aFrameSz( pStdPgDsc->GetMaster().GetFrameSize() );
+ if( pStdPgDsc->GetLandscape() )
+ {
+ SwTwips nTmp = aFrameSz.GetHeight();
+ aFrameSz.SetHeight( aFrameSz.GetWidth() );
+ aFrameSz.SetWidth( nTmp );
+ }
+ rSet.Put( aFrameSz );
+ }
+}
+
+const TranslateId STR_POOLCOLL_TEXT_ARY[] =
+{
+ // Category Text
+ STR_POOLCOLL_STANDARD,
+ STR_POOLCOLL_TEXT,
+ STR_POOLCOLL_TEXT_IDENT,
+ STR_POOLCOLL_TEXT_NEGIDENT,
+ STR_POOLCOLL_TEXT_MOVE,
+ STR_POOLCOLL_GREETING,
+ STR_POOLCOLL_SIGNATURE,
+ STR_POOLCOLL_CONFRONTATION,
+ STR_POOLCOLL_MARGINAL,
+ // Subcategory Headlines
+ STR_POOLCOLL_HEADLINE_BASE,
+ STR_POOLCOLL_HEADLINE1,
+ STR_POOLCOLL_HEADLINE2,
+ STR_POOLCOLL_HEADLINE3,
+ STR_POOLCOLL_HEADLINE4,
+ STR_POOLCOLL_HEADLINE5,
+ STR_POOLCOLL_HEADLINE6,
+ STR_POOLCOLL_HEADLINE7,
+ STR_POOLCOLL_HEADLINE8,
+ STR_POOLCOLL_HEADLINE9,
+ STR_POOLCOLL_HEADLINE10
+};
+
+const TranslateId STR_POOLCOLL_LISTS_ARY[]
+{
+ // Category Lists
+ STR_POOLCOLL_NUMBER_BULLET_BASE,
+ // Subcategory Numbering
+ STR_POOLCOLL_NUM_LEVEL1S,
+ STR_POOLCOLL_NUM_LEVEL1,
+ STR_POOLCOLL_NUM_LEVEL1E,
+ STR_POOLCOLL_NUM_NONUM1,
+ STR_POOLCOLL_NUM_LEVEL2S,
+ STR_POOLCOLL_NUM_LEVEL2,
+ STR_POOLCOLL_NUM_LEVEL2E,
+ STR_POOLCOLL_NUM_NONUM2,
+ STR_POOLCOLL_NUM_LEVEL3S,
+ STR_POOLCOLL_NUM_LEVEL3,
+ STR_POOLCOLL_NUM_LEVEL3E,
+ STR_POOLCOLL_NUM_NONUM3,
+ STR_POOLCOLL_NUM_LEVEL4S,
+ STR_POOLCOLL_NUM_LEVEL4,
+ STR_POOLCOLL_NUM_LEVEL4E,
+ STR_POOLCOLL_NUM_NONUM4,
+ STR_POOLCOLL_NUM_LEVEL5S,
+ STR_POOLCOLL_NUM_LEVEL5,
+ STR_POOLCOLL_NUM_LEVEL5E,
+ STR_POOLCOLL_NUM_NONUM5,
+
+ // Subcategory Enumeration
+ STR_POOLCOLL_BULLET_LEVEL1S,
+ STR_POOLCOLL_BULLET_LEVEL1,
+ STR_POOLCOLL_BULLET_LEVEL1E,
+ STR_POOLCOLL_BULLET_NONUM1,
+ STR_POOLCOLL_BULLET_LEVEL2S,
+ STR_POOLCOLL_BULLET_LEVEL2,
+ STR_POOLCOLL_BULLET_LEVEL2E,
+ STR_POOLCOLL_BULLET_NONUM2,
+ STR_POOLCOLL_BULLET_LEVEL3S,
+ STR_POOLCOLL_BULLET_LEVEL3,
+ STR_POOLCOLL_BULLET_LEVEL3E,
+ STR_POOLCOLL_BULLET_NONUM3,
+ STR_POOLCOLL_BULLET_LEVEL4S,
+ STR_POOLCOLL_BULLET_LEVEL4,
+ STR_POOLCOLL_BULLET_LEVEL4E,
+ STR_POOLCOLL_BULLET_NONUM4,
+ STR_POOLCOLL_BULLET_LEVEL5S,
+ STR_POOLCOLL_BULLET_LEVEL5,
+ STR_POOLCOLL_BULLET_LEVEL5E,
+ STR_POOLCOLL_BULLET_NONUM5
+};
+
+// Special Areas
+const TranslateId STR_POOLCOLL_EXTRA_ARY[]
+{
+ // Subcategory Header
+ STR_POOLCOLL_HEADERFOOTER,
+ STR_POOLCOLL_HEADER,
+ STR_POOLCOLL_HEADERL,
+ STR_POOLCOLL_HEADERR,
+ // Subcategory Footer
+ STR_POOLCOLL_FOOTER,
+ STR_POOLCOLL_FOOTERL,
+ STR_POOLCOLL_FOOTERR,
+ // Subcategory Table
+ STR_POOLCOLL_TABLE,
+ STR_POOLCOLL_TABLE_HDLN,
+ // Subcategory Labels
+ STR_POOLCOLL_LABEL,
+ STR_POOLCOLL_LABEL_ABB,
+ STR_POOLCOLL_LABEL_TABLE,
+ STR_POOLCOLL_LABEL_FRAME,
+ STR_POOLCOLL_LABEL_FIGURE,
+ // Miscellaneous
+ STR_POOLCOLL_FRAME,
+ STR_POOLCOLL_FOOTNOTE,
+ STR_POOLCOLL_ENVELOPE_ADDRESS,
+ STR_POOLCOLL_SEND_ADDRESS,
+ STR_POOLCOLL_ENDNOTE,
+ STR_POOLCOLL_LABEL_DRAWING
+};
+
+const TranslateId STR_POOLCOLL_REGISTER_ARY[] =
+{
+ // Category Directories
+ STR_POOLCOLL_REGISTER_BASE,
+ // Subcategory Index-Directories
+ STR_POOLCOLL_TOX_IDXH,
+ STR_POOLCOLL_TOX_IDX1,
+ STR_POOLCOLL_TOX_IDX2,
+ STR_POOLCOLL_TOX_IDX3,
+ STR_POOLCOLL_TOX_IDXBREAK,
+ // Subcategory Tables of Contents
+ STR_POOLCOLL_TOX_CNTNTH,
+ STR_POOLCOLL_TOX_CNTNT1,
+ STR_POOLCOLL_TOX_CNTNT2,
+ STR_POOLCOLL_TOX_CNTNT3,
+ STR_POOLCOLL_TOX_CNTNT4,
+ STR_POOLCOLL_TOX_CNTNT5,
+ // Subcategory User-Directories:
+ STR_POOLCOLL_TOX_USERH,
+ STR_POOLCOLL_TOX_USER1,
+ STR_POOLCOLL_TOX_USER2,
+ STR_POOLCOLL_TOX_USER3,
+ STR_POOLCOLL_TOX_USER4,
+ STR_POOLCOLL_TOX_USER5,
+ // Subcategory Table of Contents more Levels 5 - 10
+ STR_POOLCOLL_TOX_CNTNT6,
+ STR_POOLCOLL_TOX_CNTNT7,
+ STR_POOLCOLL_TOX_CNTNT8,
+ STR_POOLCOLL_TOX_CNTNT9,
+ STR_POOLCOLL_TOX_CNTNT10,
+ // Illustrations Index
+ STR_POOLCOLL_TOX_ILLUSH,
+ STR_POOLCOLL_TOX_ILLUS1,
+ // Object Index
+ STR_POOLCOLL_TOX_OBJECTH,
+ STR_POOLCOLL_TOX_OBJECT1,
+ // Tables Index
+ STR_POOLCOLL_TOX_TABLESH,
+ STR_POOLCOLL_TOX_TABLES1,
+ // Index of Authorities
+ STR_POOLCOLL_TOX_AUTHORITIESH,
+ STR_POOLCOLL_TOX_AUTHORITIES1,
+ // Subcategory User-Directories more Levels 5 - 10
+ STR_POOLCOLL_TOX_USER6,
+ STR_POOLCOLL_TOX_USER7,
+ STR_POOLCOLL_TOX_USER8,
+ STR_POOLCOLL_TOX_USER9,
+ STR_POOLCOLL_TOX_USER10
+};
+
+const TranslateId STR_POOLCOLL_DOC_ARY[] =
+{
+ // Category Chapter/Document
+ STR_POOLCOLL_DOC_TITLE,
+ STR_POOLCOLL_DOC_SUBTITLE,
+ STR_POOLCOLL_DOC_APPENDIX
+};
+
+const TranslateId STR_POOLCOLL_HTML_ARY[] =
+{
+ // Category HTML-Templates
+ STR_POOLCOLL_HTML_BLOCKQUOTE,
+ STR_POOLCOLL_HTML_PRE,
+ STR_POOLCOLL_HTML_HR,
+ STR_POOLCOLL_HTML_DD,
+ STR_POOLCOLL_HTML_DT
+};
+
+const TranslateId STR_POOLCHR_ARY[] =
+{
+ STR_POOLCHR_FOOTNOTE,
+ STR_POOLCHR_PAGENO,
+ STR_POOLCHR_LABEL,
+ STR_POOLCHR_DROPCAPS,
+ STR_POOLCHR_NUM_LEVEL,
+ STR_POOLCHR_BULLET_LEVEL,
+ STR_POOLCHR_INET_NORMAL,
+ STR_POOLCHR_INET_VISIT,
+ STR_POOLCHR_JUMPEDIT,
+ STR_POOLCHR_TOXJUMP,
+ STR_POOLCHR_ENDNOTE,
+ STR_POOLCHR_LINENUM,
+ STR_POOLCHR_IDX_MAIN_ENTRY,
+ STR_POOLCHR_FOOTNOTE_ANCHOR,
+ STR_POOLCHR_ENDNOTE_ANCHOR,
+ STR_POOLCHR_RUBYTEXT,
+ STR_POOLCHR_VERT_NUM
+};
+
+const TranslateId STR_POOLCHR_HTML_ARY[] =
+{
+ STR_POOLCHR_HTML_EMPHASIS,
+ STR_POOLCHR_HTML_CITATION,
+ STR_POOLCHR_HTML_STRONG,
+ STR_POOLCHR_HTML_CODE,
+ STR_POOLCHR_HTML_SAMPLE,
+ STR_POOLCHR_HTML_KEYBOARD,
+ STR_POOLCHR_HTML_VARIABLE,
+ STR_POOLCHR_HTML_DEFINSTANCE,
+ STR_POOLCHR_HTML_TELETYPE
+};
+
+const TranslateId STR_POOLFRM_ARY[] =
+{
+ STR_POOLFRM_FRAME,
+ STR_POOLFRM_GRAPHIC,
+ STR_POOLFRM_OLE,
+ STR_POOLFRM_FORMEL,
+ STR_POOLFRM_MARGINAL,
+ STR_POOLFRM_WATERSIGN,
+ STR_POOLFRM_LABEL
+};
+
+const TranslateId STR_POOLPAGE_ARY[] =
+{
+ // Page styles
+ STR_POOLPAGE_STANDARD,
+ STR_POOLPAGE_FIRST,
+ STR_POOLPAGE_LEFT,
+ STR_POOLPAGE_RIGHT,
+ STR_POOLPAGE_ENVELOPE,
+ STR_POOLPAGE_REGISTER,
+ STR_POOLPAGE_HTML,
+ STR_POOLPAGE_FOOTNOTE,
+ STR_POOLPAGE_ENDNOTE,
+ STR_POOLPAGE_LANDSCAPE
+};
+
+const TranslateId STR_POOLNUMRULE_NUM_ARY[] =
+{
+ // Numbering styles
+ STR_POOLNUMRULE_NOLIST,
+ STR_POOLNUMRULE_NUM1,
+ STR_POOLNUMRULE_NUM2,
+ STR_POOLNUMRULE_NUM3,
+ STR_POOLNUMRULE_NUM4,
+ STR_POOLNUMRULE_NUM5,
+ STR_POOLNUMRULE_BUL1,
+ STR_POOLNUMRULE_BUL2,
+ STR_POOLNUMRULE_BUL3,
+ STR_POOLNUMRULE_BUL4,
+ STR_POOLNUMRULE_BUL5
+};
+
+// XXX MUST match the entries of TableStyleProgNameTable in
+// sw/source/core/doc/SwStyleNameMapper.cxx and MUST match the order of
+// RES_POOL_TABLESTYLE_TYPE in sw/inc/poolfmt.hxx
+const TranslateId STR_TABSTYLE_ARY[] =
+{
+ // XXX MUST be in order, Writer first, then Svx old, then Svx new
+ // 1 Writer resource string
+ STR_TABSTYLE_DEFAULT,
+ // 16 old styles Svx resource strings
+ RID_SVXSTR_TBLAFMT_3D,
+ RID_SVXSTR_TBLAFMT_BLACK1,
+ RID_SVXSTR_TBLAFMT_BLACK2,
+ RID_SVXSTR_TBLAFMT_BLUE,
+ RID_SVXSTR_TBLAFMT_BROWN,
+ RID_SVXSTR_TBLAFMT_CURRENCY,
+ RID_SVXSTR_TBLAFMT_CURRENCY_3D,
+ RID_SVXSTR_TBLAFMT_CURRENCY_GRAY,
+ RID_SVXSTR_TBLAFMT_CURRENCY_LAVENDER,
+ RID_SVXSTR_TBLAFMT_CURRENCY_TURQUOISE,
+ RID_SVXSTR_TBLAFMT_GRAY,
+ RID_SVXSTR_TBLAFMT_GREEN,
+ RID_SVXSTR_TBLAFMT_LAVENDER,
+ RID_SVXSTR_TBLAFMT_RED,
+ RID_SVXSTR_TBLAFMT_TURQUOISE,
+ RID_SVXSTR_TBLAFMT_YELLOW,
+ // 10 new styles since LibreOffice 6.0 Svx resource strings
+ RID_SVXSTR_TBLAFMT_LO6_ACADEMIC,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_BLUE,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_GREEN,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_RED,
+ RID_SVXSTR_TBLAFMT_LO6_BOX_LIST_YELLOW,
+ RID_SVXSTR_TBLAFMT_LO6_ELEGANT,
+ RID_SVXSTR_TBLAFMT_LO6_FINANCIAL,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_GRID_COLUMNS,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_GRID_ROWS,
+ RID_SVXSTR_TBLAFMT_LO6_SIMPLE_LIST_SHADED
+};
+
+namespace sw
+{
+
+DocumentStylePoolManager::DocumentStylePoolManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc )
+{
+}
+
+SwTextFormatColl* DocumentStylePoolManager::GetTextCollFromPool( sal_uInt16 nId, bool bRegardLanguage )
+{
+ OSL_ENSURE(
+ (RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END) ||
+ (RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END) ||
+ (RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END) ||
+ (RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END) ||
+ (RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END) ||
+ (RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END),
+ "Wrong AutoFormat Id" );
+
+ SwTextFormatColl* pNewColl;
+ sal_uInt16 nOutLvlBits = 0;
+
+ for (size_t n = 0, nSize = m_rDoc.GetTextFormatColls()->size(); n < nSize; ++n)
+ {
+ pNewColl = (*m_rDoc.GetTextFormatColls())[ n ];
+ if( nId == pNewColl->GetPoolFormatId() )
+ {
+ // in online we can have multiple languages, use translated name
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString aName;
+ SwStyleNameMapper::GetUIName(nId, aName);
+ if (!aName.isEmpty())
+ pNewColl->SetName(aName);
+ }
+
+ return pNewColl;
+ }
+
+ if( pNewColl->IsAssignedToListLevelOfOutlineStyle())
+ nOutLvlBits |= ( 1 << pNewColl->GetAssignedOutlineStyleLevel() );
+ }
+
+ // Didn't find it until here -> create anew
+ TranslateId pResId;
+ if (RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_TEXT_ARY) == RES_POOLCOLL_TEXT_END - RES_POOLCOLL_TEXT_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_TEXT_ARY[nId - RES_POOLCOLL_TEXT_BEGIN];
+ }
+ else if (RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_LISTS_ARY) == RES_POOLCOLL_LISTS_END - RES_POOLCOLL_LISTS_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_LISTS_ARY[nId - RES_POOLCOLL_LISTS_BEGIN];
+ }
+ else if (RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_EXTRA_ARY) == RES_POOLCOLL_EXTRA_END - RES_POOLCOLL_EXTRA_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_EXTRA_ARY[nId - RES_POOLCOLL_EXTRA_BEGIN];
+ }
+ else if (RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_REGISTER_ARY) == RES_POOLCOLL_REGISTER_END - RES_POOLCOLL_REGISTER_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_REGISTER_ARY[nId - RES_POOLCOLL_REGISTER_BEGIN];
+ }
+ else if (RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_DOC_ARY) == RES_POOLCOLL_DOC_END - RES_POOLCOLL_DOC_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_DOC_ARY[nId - RES_POOLCOLL_DOC_BEGIN];
+ }
+ else if (RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END)
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLCOLL_HTML_ARY) == RES_POOLCOLL_HTML_END - RES_POOLCOLL_HTML_BEGIN, "### unexpected size!");
+ pResId = STR_POOLCOLL_HTML_ARY[nId - RES_POOLCOLL_HTML_BEGIN];
+ }
+
+ OSL_ENSURE(pResId, "Invalid Pool ID");
+ if (!pResId)
+ return GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ OUString aNm(SwResId(pResId));
+
+ // A Set for all to-be-set Attributes
+ SwAttrSet aSet( m_rDoc.GetAttrPool(), aTextFormatCollSetRange );
+ sal_uInt16 nParent = GetPoolParent( nId );
+
+ {
+
+//FEATURE::CONDCOLL
+ if(::IsConditionalByPoolId( nId ))
+ pNewColl = new SwConditionTextFormatColl( m_rDoc.GetAttrPool(), aNm, !nParent
+ ? m_rDoc.GetDfltTextFormatColl()
+ : GetTextCollFromPool( nParent ));
+ else
+//FEATURE::CONDCOLL
+ pNewColl = new SwTextFormatColl( m_rDoc.GetAttrPool(), aNm, !nParent
+ ? m_rDoc.GetDfltTextFormatColl()
+ : GetTextCollFromPool( nParent ));
+ pNewColl->SetPoolFormatId( nId );
+ m_rDoc.GetTextFormatColls()->push_back( pNewColl );
+ }
+
+ bool bNoDefault = m_rDoc.GetDocumentSettingManager().get( DocumentSettingId::STYLES_NODEFAULT );
+ if ( !bNoDefault )
+ {
+ switch( nId )
+ {
+ // General content forms
+ case RES_POOLCOLL_STANDARD:
+ /* koreans do not like SvxScriptItem(TRUE) */
+ if (bRegardLanguage)
+ {
+ LanguageType nAppLanguage = GetAppLanguage();
+ if (GetDefaultFrameDirection(nAppLanguage) ==
+ SvxFrameDirection::Horizontal_RL_TB)
+ {
+ SvxAdjustItem aAdjust(SvxAdjust::Right, RES_PARATR_ADJUST );
+ aSet.Put(aAdjust);
+ }
+ if (nAppLanguage == LANGUAGE_KOREAN)
+ {
+ SvxScriptSpaceItem aScriptSpace(false, RES_PARATR_SCRIPTSPACE);
+ aSet.Put(aScriptSpace);
+ }
+ }
+ break;
+
+ case RES_POOLCOLL_TEXT: // Text body
+ {
+ SvxLineSpacingItem aLSpc( LINE_SPACE_DEFAULT_HEIGHT, RES_PARATR_LINESPACING );
+ SvxULSpaceItem aUL( 0, PT_7, RES_UL_SPACE );
+ aLSpc.SetPropLineSpace( 115 );
+ if( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) ) aUL.SetLower( HTML_PARSPACE );
+ aSet.Put( aUL );
+ aSet.Put( aLSpc );
+ }
+ break;
+ case RES_POOLCOLL_TEXT_IDENT: // Text body indentation
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextFirstLineOffset(o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip));
+ aSet.Put( aLR );
+ }
+ break;
+ case RES_POOLCOLL_TEXT_NEGIDENT: // Text body neg. indentation
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextFirstLineOffset(-o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip));
+ aLR.SetTextLeft(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ SvxTabStopItem aTStops(RES_PARATR_TABSTOP);
+ aTStops.Insert( SvxTabStop( 0 ));
+
+ aSet.Put( aLR );
+ aSet.Put( aTStops );
+ }
+ break;
+ case RES_POOLCOLL_TEXT_MOVE: // Text body move
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextLeft(o3tl::convert(5, o3tl::Length::mm, o3tl::Length::twip));
+ aSet.Put( aLR );
+ }
+ break;
+
+ case RES_POOLCOLL_CONFRONTATION: // Text body confrontation
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextFirstLineOffset(
+ -o3tl::convert(45, o3tl::Length::mm, o3tl::Length::twip));
+ aLR.SetTextLeft(o3tl::convert(5, o3tl::Length::cm, o3tl::Length::twip));
+ SvxTabStopItem aTStops( RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( 0 ));
+
+ aSet.Put( aLR );
+ aSet.Put( aTStops );
+ }
+ break;
+ case RES_POOLCOLL_MARGINAL: // Text body marginal
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextLeft(o3tl::convert(4, o3tl::Length::cm, o3tl::Length::twip));
+ aSet.Put( aLR );
+ }
+ break;
+
+ case RES_POOLCOLL_HEADLINE_BASE: // Base headline
+ {
+ static const sal_uInt16 aFontWhich[] =
+ { RES_CHRATR_FONT,
+ RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CTL_FONT
+ };
+ static const sal_uInt16 aLangTypes[] =
+ {
+ RES_CHRATR_LANGUAGE,
+ RES_CHRATR_CJK_LANGUAGE,
+ RES_CHRATR_CTL_LANGUAGE
+ };
+ static const LanguageType aLangs[] =
+ {
+ LANGUAGE_ENGLISH_US,
+ LANGUAGE_ENGLISH_US,
+ LANGUAGE_ARABIC_SAUDI_ARABIA
+ };
+ static const DefaultFontType nFontTypes[] =
+ {
+ DefaultFontType::LATIN_HEADING,
+ DefaultFontType::CJK_HEADING,
+ DefaultFontType::CTL_HEADING
+ };
+
+ for( int i = 0; i < 3; ++i )
+ {
+ LanguageType nLng = static_cast<const SvxLanguageItem&>(m_rDoc.GetDefault( aLangTypes[i] )).GetLanguage();
+ if( LANGUAGE_DONTKNOW == nLng )
+ nLng = aLangs[i];
+
+ vcl::Font aFnt( OutputDevice::GetDefaultFont( nFontTypes[i],
+ nLng, GetDefaultFontFlags::OnlyOne ) );
+
+ aSet.Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(),
+ OUString(), aFnt.GetPitch(),
+ aFnt.GetCharSet(), aFontWhich[i] ));
+ }
+
+ SvxFontHeightItem aFntSize( PT_14, 100, RES_CHRATR_FONTSIZE );
+ SvxULSpaceItem aUL( PT_12, PT_6, RES_UL_SPACE );
+ if( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) )
+ aUL.SetLower( HTML_PARSPACE );
+ aSet.Put( SvxFormatKeepItem( true, RES_KEEP ));
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+
+ aSet.Put( aUL );
+ SetAllScriptItem( aSet, aFntSize );
+ }
+ break;
+
+ case RES_POOLCOLL_NUMBER_BULLET_BASE: // Base Numbering
+ break;
+
+ case RES_POOLCOLL_GREETING: // Greeting
+ case RES_POOLCOLL_REGISTER_BASE: // Base indexes
+ case RES_POOLCOLL_SIGNATURE: // Signatures
+ case RES_POOLCOLL_TABLE: // Table content
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ if (nId == RES_POOLCOLL_TABLE)
+ {
+ aSet.Put( SvxWidowsItem( 0, RES_PARATR_WIDOWS ) );
+ aSet.Put( SvxOrphansItem( 0, RES_PARATR_ORPHANS ) );
+ }
+ }
+ break;
+
+ case RES_POOLCOLL_HEADLINE1: // Heading 1
+ {
+ SvxULSpaceItem aUL( PT_12, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 0, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE2: // Heading 2
+ {
+ SvxULSpaceItem aUL( PT_10, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 1, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE3: // Heading 3
+ {
+ SvxULSpaceItem aUL( PT_7, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 2, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE4: // Heading 4
+ {
+ SvxULSpaceItem aUL( PT_6, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 3, true );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE5: // Heading 5
+ {
+ SvxULSpaceItem aUL( PT_6, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 4, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE6: // Heading 6
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 5, true );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE7: // Heading 7
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 6, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE8: // Heading 8
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 7, true );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE9: // Heading 9
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 8, false );
+ }
+ break;
+ case RES_POOLCOLL_HEADLINE10: // Heading 10
+ {
+ SvxULSpaceItem aUL( PT_3, PT_3, RES_UL_SPACE );
+ aSet.Put( aUL );
+ lcl_SetHeadline( m_rDoc, pNewColl, aSet, nOutLvlBits, 9, false );
+ }
+ break;
+
+ // Special sections:
+ // Header
+ case RES_POOLCOLL_HEADERFOOTER:
+ case RES_POOLCOLL_HEADER:
+ case RES_POOLCOLL_HEADERL:
+ case RES_POOLCOLL_HEADERR:
+ // Footer
+ case RES_POOLCOLL_FOOTER:
+ case RES_POOLCOLL_FOOTERL:
+ case RES_POOLCOLL_FOOTERR:
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+
+ tools::Long nRightMargin = lcl_GetRightMargin( m_rDoc );
+
+ SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( nRightMargin / 2, SvxTabAdjust::Center ) );
+ aTStops.Insert( SvxTabStop( nRightMargin, SvxTabAdjust::Right ) );
+
+ aSet.Put( aTStops );
+
+ if ( (nId==RES_POOLCOLL_HEADERR) || (nId==RES_POOLCOLL_FOOTERR) ) {
+ SvxAdjustItem aAdjust(SvxAdjust::Right, RES_PARATR_ADJUST );
+ aSet.Put(aAdjust);
+ }
+ }
+ break;
+
+ case RES_POOLCOLL_TABLE_HDLN:
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_FOOTNOTE: // paragraph style Footnote
+ case RES_POOLCOLL_ENDNOTE: // paragraph style Endnote
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextFirstLineOffset(-o3tl::convert(6, o3tl::Length::mm, o3tl::Length::twip));
+ aLR.SetTextLeft(o3tl::convert(6, o3tl::Length::mm, o3tl::Length::twip));
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_10, 100, RES_CHRATR_FONTSIZE ) );
+ aSet.Put( aLR );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_LABEL: // basic caption
+ {
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ aUL.SetUpper( PT_6 );
+ aUL.SetLower( PT_6 );
+ aSet.Put( aUL );
+ SetAllScriptItem( aSet, SvxPostureItem( ITALIC_NORMAL, RES_CHRATR_POSTURE ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_10, 100, RES_CHRATR_FONTSIZE ) );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_FRAME: // Frame content
+ case RES_POOLCOLL_LABEL_ABB: // caption image
+ case RES_POOLCOLL_LABEL_TABLE: // caption table
+ case RES_POOLCOLL_LABEL_FRAME: // caption frame
+ case RES_POOLCOLL_LABEL_DRAWING: // caption drawing
+ case RES_POOLCOLL_LABEL_FIGURE:
+ break;
+
+ case RES_POOLCOLL_ENVELOPE_ADDRESS: // envelope address
+ {
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ aUL.SetLower( PT_3 );
+ aSet.Put( aUL );
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_SEND_ADDRESS: // Sender address
+ {
+ if( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) )
+ SetAllScriptItem( aSet, SvxPostureItem(ITALIC_NORMAL, RES_CHRATR_POSTURE) );
+ else
+ {
+ SvxULSpaceItem aUL( RES_UL_SPACE ); aUL.SetLower( PT_3 );
+ aSet.Put( aUL );
+ }
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ // User defined indexes:
+ case RES_POOLCOLL_TOX_USERH: // Header
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_USER1: // 1st level
+ lcl_SetRegister( m_rDoc, aSet, 0, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER2: // 2nd level
+ lcl_SetRegister( m_rDoc, aSet, 1, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER3: // 3rd level
+ lcl_SetRegister( m_rDoc, aSet, 2, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER4: // 4th level
+ lcl_SetRegister( m_rDoc, aSet, 3, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER5: // 5th level
+ lcl_SetRegister( m_rDoc, aSet, 4, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER6: // 6th level
+ lcl_SetRegister( m_rDoc, aSet, 5, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER7: // 7th level
+ lcl_SetRegister( m_rDoc, aSet, 6, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER8: // 8th level
+ lcl_SetRegister( m_rDoc, aSet, 7, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER9: // 9th level
+ lcl_SetRegister( m_rDoc, aSet, 8, false, true );
+ break;
+ case RES_POOLCOLL_TOX_USER10: // 10th level
+ lcl_SetRegister( m_rDoc, aSet, 9, false, true );
+ break;
+
+ // Index
+ case RES_POOLCOLL_TOX_IDXH: // Header
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_IDX1: // 1st level
+ lcl_SetRegister( m_rDoc, aSet, 0, false, false );
+ break;
+ case RES_POOLCOLL_TOX_IDX2: // 2nd level
+ lcl_SetRegister( m_rDoc, aSet, 1, false, false );
+ break;
+ case RES_POOLCOLL_TOX_IDX3: // 3rd level
+ lcl_SetRegister( m_rDoc, aSet, 2, false, false );
+ break;
+ case RES_POOLCOLL_TOX_IDXBREAK: // Separator
+ lcl_SetRegister( m_rDoc, aSet, 0, false, false );
+ break;
+
+ // Table of Content
+ case RES_POOLCOLL_TOX_CNTNTH: // Header
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_CNTNT1: // 1st level
+ lcl_SetRegister( m_rDoc, aSet, 0, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT2: // 2nd level
+ lcl_SetRegister( m_rDoc, aSet, 1, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT3: // 3rd level
+ lcl_SetRegister( m_rDoc, aSet, 2, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT4: // 4th level
+ lcl_SetRegister( m_rDoc, aSet, 3, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT5: // 5th level
+ lcl_SetRegister( m_rDoc, aSet, 4, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT6: // 6th level
+ lcl_SetRegister( m_rDoc, aSet, 5, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT7: // 7th level
+ lcl_SetRegister( m_rDoc, aSet, 6, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT8: // 8th level
+ lcl_SetRegister( m_rDoc, aSet, 7, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT9: // 9th level
+ lcl_SetRegister( m_rDoc, aSet, 8, false, true );
+ break;
+ case RES_POOLCOLL_TOX_CNTNT10: // 10th level
+ lcl_SetRegister( m_rDoc, aSet, 9, false, true );
+ break;
+
+ case RES_POOLCOLL_TOX_ILLUSH:
+ case RES_POOLCOLL_TOX_OBJECTH:
+ case RES_POOLCOLL_TOX_TABLESH:
+ case RES_POOLCOLL_TOX_AUTHORITIESH:
+ lcl_SetRegister( m_rDoc, aSet, 0, true, false );
+ {
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+ case RES_POOLCOLL_TOX_ILLUS1:
+ case RES_POOLCOLL_TOX_OBJECT1:
+ case RES_POOLCOLL_TOX_TABLES1:
+ case RES_POOLCOLL_TOX_AUTHORITIES1:
+ lcl_SetRegister( m_rDoc, aSet, 0, false, true );
+ break;
+
+ case RES_POOLCOLL_NUM_LEVEL1S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL1,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 0 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL1,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 0 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL1E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL1,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 0 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM1,
+ 0, SwNumRule::GetNumIndent( 0 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL2S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL2,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 1 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL2,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 1 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL2E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL2,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 1 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM2,
+ 0, SwNumRule::GetNumIndent( 1 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL3S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL3,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 2 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL3,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 2 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL3E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL3,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 2 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM3,
+ 0, SwNumRule::GetNumIndent( 2 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL4S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL4,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 3 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL4,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 3 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL4E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL4,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 3 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM4,
+ 0, SwNumRule::GetNumIndent( 3 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL5S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL5,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 4 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL5,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 4 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_NUM_LEVEL5E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_LEVEL5,
+ lNumberFirstLineOffset, SwNumRule::GetNumIndent( 4 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_NUM_NONUM5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_NUM_NONUM5,
+ 0, SwNumRule::GetNumIndent( 4 ), 0, PT_6 );
+ break;
+
+ case RES_POOLCOLL_BULLET_LEVEL1S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL1,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 0 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL1,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 0 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL1E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL1,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 0 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM1:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM1,
+ 0, SwNumRule::GetBullIndent( 0 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL2S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL2,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 1 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL2,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 1 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL2E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL2,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 1 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM2:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM2,
+ 0, SwNumRule::GetBullIndent( 1 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL3S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL3,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 2 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL3,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 2 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL3E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL3,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 2 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM3:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM3,
+ 0, SwNumRule::GetBullIndent( 2 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL4S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL4,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 3 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL4,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 3 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL4E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL4,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 3 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM4:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM4,
+ 0, SwNumRule::GetBullIndent( 3 ), 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL5S:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL5,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 4 ),
+ PT_12, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL5,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 4 ),
+ 0, PT_6 );
+ break;
+ case RES_POOLCOLL_BULLET_LEVEL5E:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_LEVEL5,
+ lBulletFirstLineOffset, SwNumRule::GetBullIndent( 4 ),
+ 0, PT_12 );
+ break;
+ case RES_POOLCOLL_BULLET_NONUM5:
+ lcl_SetNumBul( m_rDoc, pNewColl, aSet, RES_POOLCOLL_BULLET_NONUM5,
+ 0, SwNumRule::GetBullIndent( 4 ), 0, PT_6 );
+ break;
+
+ case RES_POOLCOLL_DOC_TITLE: // Document Title
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_28, 100, RES_CHRATR_FONTSIZE ) );
+
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) );
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+ break;
+
+ case RES_POOLCOLL_DOC_SUBTITLE: // Document subtitle
+ {
+ SvxULSpaceItem aUL( PT_3, PT_6, RES_UL_SPACE );
+ aSet.Put( aUL );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_18, 100, RES_CHRATR_FONTSIZE ));
+
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ));
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+ break;
+
+ case RES_POOLCOLL_DOC_APPENDIX: // Document Appendix tdf#114090
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem( PT_16, 100, RES_CHRATR_FONTSIZE ) );
+
+ aSet.Put( SvxAdjustItem( SvxAdjust::Center, RES_PARATR_ADJUST ) );
+
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_BLOCKQUOTE:
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetLeft(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ aLR.SetRight(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ aSet.Put( aLR );
+ std::unique_ptr<SvxULSpaceItem> aUL(pNewColl->GetULSpace().Clone());
+ aUL->SetLower( HTML_PARSPACE );
+ aSet.Put(std::move(aUL));
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_PRE:
+ {
+ ::lcl_SetDfltFont( DefaultFontType::FIXED, aSet );
+
+ // WORKAROUND: Set PRE to 10pt
+ SetAllScriptItem( aSet, SvxFontHeightItem(PT_10, 100, RES_CHRATR_FONTSIZE) );
+
+ // The lower paragraph distance is set explicitly (makes
+ // assigning hard attributes easier)
+ std::unique_ptr<SvxULSpaceItem> aULSpaceItem(pNewColl->GetULSpace().Clone());
+ aULSpaceItem->SetLower( 0 );
+ aSet.Put(std::move(aULSpaceItem));
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_HR:
+ {
+ SvxBoxItem aBox( RES_BOX );
+ Color aColor( COL_GRAY );
+ SvxBorderLine aNew(&aColor, 3, SvxBorderLineStyle::DOUBLE);
+ aBox.SetLine( &aNew, SvxBoxItemLine::BOTTOM );
+
+ aSet.Put( aBox );
+ aSet.Put( SwParaConnectBorderItem( false ) );
+ SetAllScriptItem( aSet, SvxFontHeightItem(120, 100, RES_CHRATR_FONTSIZE) );
+
+ std::unique_ptr<SvxULSpaceItem> aUL;
+ {
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_TEXT ));
+ aUL.reset(pNewColl->GetULSpace().Clone());
+ }
+ aUL->SetLower( HTML_PARSPACE );
+ aSet.Put(std::move(aUL));
+ SwFormatLineNumber aLN;
+ aLN.SetCountLines( false );
+ aSet.Put( aLN );
+ }
+ break;
+
+ case RES_POOLCOLL_HTML_DD:
+ {
+ std::unique_ptr<SvxLRSpaceItem> aLR(pNewColl->GetLRSpace().Clone());
+ // We indent by 1 cm. The IDs are always 2 away from each other!
+ aLR->SetLeft(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ aSet.Put(std::move(aLR));
+ }
+ break;
+ case RES_POOLCOLL_HTML_DT:
+ {
+ std::unique_ptr<SvxLRSpaceItem> aLR;
+ {
+ pNewColl->SetNextTextFormatColl( *GetTextCollFromPool( RES_POOLCOLL_HTML_DD ));
+ aLR.reset(pNewColl->GetLRSpace().Clone());
+ }
+ // We indent by 0 cm. The IDs are always 2 away from each other!
+ aLR->SetLeft( 0 );
+ aSet.Put( std::move(aLR) );
+ }
+ break;
+ }
+ }
+
+ if( aSet.Count() )
+ pNewColl->SetFormatAttr( aSet );
+ return pNewColl;
+}
+
+/// Return the AutomaticFormat with the supplied Id. If it doesn't
+/// exist, create it.
+SwFormat* DocumentStylePoolManager::GetFormatFromPool( sal_uInt16 nId )
+{
+ SwFormat *pNewFormat = nullptr;
+ SwFormat *pDeriveFormat = nullptr;
+
+ SwFormatsBase* pArray[ 2 ];
+ sal_uInt16 nArrCnt = 1;
+ TranslateId pRCId;
+ WhichRangesContainer const* pWhichRange;
+
+ switch( nId & (COLL_GET_RANGE_BITS + POOLGRP_NOCOLLID) )
+ {
+ case POOLGRP_CHARFMT:
+ {
+ pArray[0] = m_rDoc.GetCharFormats();
+ pDeriveFormat = m_rDoc.GetDfltCharFormat();
+ pWhichRange = &aCharFormatSetRange;
+
+ if (nId >= RES_POOLCHR_HTML_BEGIN && nId < RES_POOLCHR_HTML_END)
+ pRCId = STR_POOLCHR_HTML_ARY[nId - RES_POOLCHR_HTML_BEGIN];
+ else if (nId >= RES_POOLCHR_NORMAL_BEGIN && nId < RES_POOLCHR_NORMAL_END)
+ pRCId = STR_POOLCHR_ARY[nId - RES_POOLCHR_BEGIN];
+ else
+ {
+ // Fault: unknown Format, but a CharFormat -> return the first one
+ OSL_ENSURE( false, "invalid Id" );
+ pRCId = STR_POOLCHR_ARY[0];
+ }
+ }
+ break;
+ case POOLGRP_FRAMEFMT:
+ {
+ pArray[0] = m_rDoc.GetFrameFormats();
+ pArray[1] = m_rDoc.GetSpzFrameFormats();
+ pDeriveFormat = m_rDoc.GetDfltFrameFormat();
+ nArrCnt = 2;
+ pWhichRange = &aFrameFormatSetRange;
+
+ // Fault: unknown Format, but a FrameFormat
+ // -> return the first one
+ if( RES_POOLFRM_BEGIN > nId || nId >= RES_POOLFRM_END )
+ {
+ OSL_ENSURE( false, "invalid Id" );
+ nId = RES_POOLFRM_BEGIN;
+ }
+
+ pRCId = STR_POOLFRM_ARY[nId - RES_POOLFRM_BEGIN];
+ }
+ break;
+
+ default:
+ // Fault, unknown Format
+ OSL_ENSURE( nId, "invalid Id" );
+ return nullptr;
+ }
+ OSL_ENSURE(pRCId, "invalid Id");
+
+ while( nArrCnt-- )
+ for( size_t n = 0; n < (*pArray[nArrCnt]).GetFormatCount(); ++n )
+ {
+ pNewFormat = (*pArray[ nArrCnt ] ).GetFormat( n );
+ if( nId == pNewFormat->GetPoolFormatId() )
+ {
+ return pNewFormat;
+ }
+ }
+
+ OUString aNm(SwResId(pRCId));
+ SwAttrSet aSet(m_rDoc.GetAttrPool(), *pWhichRange);
+
+ {
+ bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ switch (nId & (COLL_GET_RANGE_BITS + POOLGRP_NOCOLLID) )
+ {
+ case POOLGRP_CHARFMT:
+ pNewFormat = m_rDoc.MakeCharFormat_(aNm, pDeriveFormat, false, true);
+ break;
+ case POOLGRP_FRAMEFMT:
+ pNewFormat = m_rDoc.MakeFrameFormat_(aNm, pDeriveFormat, false, true);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( !bIsModified )
+ m_rDoc.getIDocumentState().ResetModified();
+ pNewFormat->SetPoolFormatId( nId );
+ pNewFormat->SetAuto(false); // no AutoFormat
+ }
+
+ switch( nId )
+ {
+ case RES_POOLCHR_FOOTNOTE: // Footnote
+ case RES_POOLCHR_PAGENO: // Page/Field
+ case RES_POOLCHR_LABEL: // Label
+ case RES_POOLCHR_DROPCAPS: // Dropcaps
+ case RES_POOLCHR_NUM_LEVEL: // Numbering level
+ case RES_POOLCHR_TOXJUMP: // Table of contents jump
+ case RES_POOLCHR_ENDNOTE: // Endnote
+ case RES_POOLCHR_LINENUM: // Line numbering
+ break;
+
+ case RES_POOLCHR_ENDNOTE_ANCHOR: // Endnote anchor
+ case RES_POOLCHR_FOOTNOTE_ANCHOR: // Footnote anchor
+ {
+ aSet.Put( SvxEscapementItem( DFLT_ESC_AUTO_SUPER, DFLT_ESC_PROP, RES_CHRATR_ESCAPEMENT ) );
+ }
+ break;
+
+ case RES_POOLCHR_BULLET_LEVEL: // Bullet character
+ {
+ const vcl::Font& rBulletFont = numfunc::GetDefBulletFont();
+ SetAllScriptItem( aSet, SvxFontItem( rBulletFont.GetFamilyType(),
+ rBulletFont.GetFamilyName(), rBulletFont.GetStyleName(),
+ rBulletFont.GetPitch(), rBulletFont.GetCharSet(), RES_CHRATR_FONT ));
+ }
+ break;
+
+ case RES_POOLCHR_INET_NORMAL:
+ {
+ aSet.Put( SvxColorItem( COL_BLUE, RES_CHRATR_COLOR ) );
+ aSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE ) );
+ }
+ break;
+ case RES_POOLCHR_INET_VISIT:
+ {
+ aSet.Put( SvxColorItem( COL_RED, RES_CHRATR_COLOR ) );
+ aSet.Put( SvxUnderlineItem( LINESTYLE_SINGLE, RES_CHRATR_UNDERLINE ) );
+ }
+ break;
+ case RES_POOLCHR_JUMPEDIT:
+ {
+ aSet.Put( SvxColorItem( COL_CYAN, RES_CHRATR_COLOR ) );
+ aSet.Put( SvxUnderlineItem( LINESTYLE_DOTTED, RES_CHRATR_UNDERLINE ) );
+ aSet.Put( SvxCaseMapItem( SvxCaseMap::SmallCaps, RES_CHRATR_CASEMAP ) );
+ }
+ break;
+
+ case RES_POOLCHR_RUBYTEXT:
+ {
+ tools::Long nH = GetDfltAttr( RES_CHRATR_CJK_FONTSIZE )->GetHeight() / 2;
+ SetAllScriptItem( aSet, SvxFontHeightItem( nH, 100, RES_CHRATR_FONTSIZE));
+ aSet.Put(SvxUnderlineItem( LINESTYLE_NONE, RES_CHRATR_UNDERLINE ));
+ aSet.Put(SvxEmphasisMarkItem( FontEmphasisMark::NONE, RES_CHRATR_EMPHASIS_MARK) );
+ }
+ break;
+
+ case RES_POOLCHR_HTML_EMPHASIS:
+ case RES_POOLCHR_HTML_CITATION:
+ case RES_POOLCHR_HTML_VARIABLE:
+ {
+ SetAllScriptItem( aSet, SvxPostureItem( ITALIC_NORMAL, RES_CHRATR_POSTURE) );
+ }
+ break;
+
+ case RES_POOLCHR_IDX_MAIN_ENTRY:
+ case RES_POOLCHR_HTML_STRONG:
+ {
+ SetAllScriptItem( aSet, SvxWeightItem( WEIGHT_BOLD, RES_CHRATR_WEIGHT ));
+ }
+ break;
+
+ case RES_POOLCHR_HTML_CODE:
+ case RES_POOLCHR_HTML_SAMPLE:
+ case RES_POOLCHR_HTML_KEYBOARD:
+ case RES_POOLCHR_HTML_TELETYPE:
+ {
+ ::lcl_SetDfltFont( DefaultFontType::FIXED, aSet );
+ }
+ break;
+ case RES_POOLCHR_VERT_NUM:
+ aSet.Put( SvxCharRotateItem( 900_deg10, false, RES_CHRATR_ROTATE ) );
+ break;
+
+ case RES_POOLFRM_FRAME:
+ {
+ if ( m_rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) )
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::LINE_CENTER, text::RelOrientation::PRINT_AREA ) );
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ }
+ else
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ) );
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::PRINT_AREA ) );
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::PRINT_AREA ) );
+ Color aCol( COL_BLACK );
+ SvxBorderLine aLine( &aCol, SvxBorderLineWidth::Hairline );
+ SvxBoxItem aBox( RES_BOX );
+ aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
+ aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ aBox.SetAllDistances( 85 );
+ aSet.Put( aBox );
+ aSet.Put( SvxLRSpaceItem( 114, 114, 0, 0, RES_LR_SPACE ) );
+ aSet.Put( SvxULSpaceItem( 114, 114, RES_UL_SPACE ) );
+ }
+
+ // for styles of FlyFrames do not set the FillStyle to make it a derived attribute
+ aSet.ClearItem(XATTR_FILLSTYLE);
+ }
+ break;
+ case RES_POOLFRM_GRAPHIC:
+ case RES_POOLFRM_OLE:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_DYNAMIC ));
+ }
+ break;
+ case RES_POOLFRM_FORMEL:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::CHAR_CENTER, text::RelOrientation::FRAME ) );
+ aSet.Put( SvxLRSpaceItem( 0, 0, 0, 0, RES_LR_SPACE ) );
+ }
+ break;
+ case RES_POOLFRM_MARGINAL:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ));
+ // Set the default width to 3.5 cm, use the minimum value for the height
+ aSet.Put(SwFormatFrameSize(SwFrameSize::Minimum,
+ o3tl::toTwips(35, o3tl::Length::mm),
+ o3tl::toTwips(5, o3tl::Length::mm)));
+ }
+ break;
+ case RES_POOLFRM_WATERSIGN:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ));
+ aSet.Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ));
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ));
+ aSet.Put( SvxOpaqueItem( RES_OPAQUE, false ));
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ));
+ }
+ break;
+ case RES_POOLFRM_LABEL:
+ {
+ aSet.Put( SwFormatAnchor( RndStdIds::FLY_AS_CHAR ) );
+ aSet.Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ) );
+ aSet.Put( SvxLRSpaceItem( 114, 114, 0, 0, RES_LR_SPACE ) );
+
+ SvxProtectItem aProtect( RES_PROTECT );
+ aProtect.SetSizeProtect( true );
+ aProtect.SetPosProtect( true );
+ aSet.Put( aProtect );
+
+ pNewFormat->SetAutoUpdateFormat();
+ }
+ break;
+ }
+ if( aSet.Count() )
+ {
+ pNewFormat->SetFormatAttr( aSet );
+ }
+ return pNewFormat;
+}
+
+SwFrameFormat* DocumentStylePoolManager::GetFrameFormatFromPool( sal_uInt16 nId )
+{
+ return static_cast<SwFrameFormat*>(GetFormatFromPool( nId ));
+}
+
+SwCharFormat* DocumentStylePoolManager::GetCharFormatFromPool( sal_uInt16 nId )
+{
+ return static_cast<SwCharFormat*>(GetFormatFromPool( nId ));
+}
+
+SwPageDesc* DocumentStylePoolManager::GetPageDescFromPool( sal_uInt16 nId, bool bRegardLanguage )
+{
+ OSL_ENSURE( RES_POOLPAGE_BEGIN <= nId && nId < RES_POOLPAGE_END,
+ "Wrong AutoFormat Id" );
+
+ for( size_t n = 0; n < m_rDoc.GetPageDescCnt(); ++n )
+ {
+ if ( nId == m_rDoc.GetPageDesc(n).GetPoolFormatId() )
+ {
+ return &m_rDoc.GetPageDesc(n);
+ }
+ }
+
+ if( RES_POOLPAGE_BEGIN > nId || nId >= RES_POOLPAGE_END )
+ {
+ // unknown page pool ID
+ OSL_ENSURE( false, "<SwDoc::GetPageDescFromPool(..)> - unknown page pool ID" );
+ nId = RES_POOLPAGE_BEGIN;
+ }
+
+ SwPageDesc* pNewPgDsc = nullptr;
+ {
+ static_assert(SAL_N_ELEMENTS(STR_POOLPAGE_ARY) == RES_POOLPAGE_END - RES_POOLPAGE_BEGIN, "### unexpected size!");
+ const OUString aNm(SwResId(STR_POOLPAGE_ARY[nId - RES_POOLPAGE_BEGIN]));
+ const bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ pNewPgDsc = m_rDoc.MakePageDesc(aNm, nullptr, bRegardLanguage);
+ }
+
+ pNewPgDsc->SetPoolFormatId( nId );
+ if ( !bIsModified )
+ {
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+ }
+
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ {
+ aLR.SetLeft(o3tl::convert(2, o3tl::Length::cm, o3tl::Length::twip));
+ aLR.SetRight( aLR.GetLeft() );
+ }
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+ {
+ aUL.SetUpper( o3tl::narrowing<sal_uInt16>(aLR.GetLeft()) );
+ aUL.SetLower( o3tl::narrowing<sal_uInt16>(aLR.GetLeft()) );
+ }
+
+ SwAttrSet aSet( m_rDoc.GetAttrPool(), aPgFrameFormatSetRange );
+ bool bSetLeft = true;
+
+ switch( nId )
+ {
+ case RES_POOLPAGE_STANDARD: // "Default"
+ {
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All | UseOnPage::FirstShare );
+ }
+ break;
+
+ case RES_POOLPAGE_FIRST: // "First Page"
+ case RES_POOLPAGE_REGISTER: // "Index"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ if( RES_POOLPAGE_FIRST == nId )
+ pNewPgDsc->SetFollow( GetPageDescFromPool( RES_POOLPAGE_STANDARD ));
+ }
+ break;
+
+ case RES_POOLPAGE_LEFT: // "Left Page"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ bSetLeft = false;
+ pNewPgDsc->SetUseOn( UseOnPage::Left );
+ // this relies on GetPageDescFromPool() not going into infinite recursion
+ // (by this point RES_POOLPAGE_LEFT will not reach this place again)
+ pNewPgDsc->SetFollow( GetPageDescFromPool( RES_POOLPAGE_RIGHT ));
+ }
+ break;
+ case RES_POOLPAGE_RIGHT: // "Right Page"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ bSetLeft = false;
+ pNewPgDsc->SetUseOn( UseOnPage::Right );
+ pNewPgDsc->SetFollow( GetPageDescFromPool( RES_POOLPAGE_LEFT ));
+ }
+ break;
+
+ case RES_POOLPAGE_ENVELOPE: // "Envelope"
+ {
+ Size aPSize( SvxPaperInfo::GetPaperSize( PAPER_ENV_C65 ) );
+ LandscapeSwap( aPSize );
+ aSet.Put( SwFormatFrameSize( SwFrameSize::Fixed, aPSize.Width(), aPSize.Height() ));
+ aLR.SetLeft( 0 ); aLR.SetRight( 0 );
+ aUL.SetUpper( 0 ); aUL.SetLower( 0 );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ pNewPgDsc->SetLandscape( true );
+ }
+ break;
+
+ case RES_POOLPAGE_HTML: // "HTML"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aLR.SetRight(o3tl::convert(1, o3tl::Length::cm, o3tl::Length::twip));
+ aUL.SetUpper( o3tl::narrowing<sal_uInt16>(aLR.GetRight()) );
+ aUL.SetLower( o3tl::narrowing<sal_uInt16>(aLR.GetRight()) );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ }
+ break;
+
+ case RES_POOLPAGE_FOOTNOTE: // "Footnote"
+ case RES_POOLPAGE_ENDNOTE: // "Endnote"
+ {
+ lcl_PutStdPageSizeIntoItemSet( m_rDoc, aSet );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ SwPageFootnoteInfo aInf( pNewPgDsc->GetFootnoteInfo() );
+ aInf.SetLineWidth( 0 );
+ aInf.SetTopDist( 0 );
+ aInf.SetBottomDist( 0 );
+ pNewPgDsc->SetFootnoteInfo( aInf );
+ }
+ break;
+
+ case RES_POOLPAGE_LANDSCAPE: // "Landscape"
+ {
+ SwPageDesc* pStdPgDsc = GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ SwFormatFrameSize aFrameSz( pStdPgDsc->GetMaster().GetFrameSize() );
+ if ( !pStdPgDsc->GetLandscape() )
+ {
+ const SwTwips nTmp = aFrameSz.GetHeight();
+ aFrameSz.SetHeight( aFrameSz.GetWidth() );
+ aFrameSz.SetWidth( nTmp );
+ }
+ aSet.Put( aFrameSz );
+ aSet.Put( aLR );
+ aSet.Put( aUL );
+ pNewPgDsc->SetUseOn( UseOnPage::All );
+ pNewPgDsc->SetLandscape( true );
+ }
+ break;
+
+ }
+
+ if( aSet.Count() )
+ {
+ if( bSetLeft )
+ {
+ pNewPgDsc->GetLeft().SetFormatAttr( aSet );
+ pNewPgDsc->GetFirstLeft().SetFormatAttr( aSet );
+ }
+ pNewPgDsc->GetMaster().SetFormatAttr( aSet );
+ pNewPgDsc->GetFirstMaster().SetFormatAttr( aSet );
+ }
+ return pNewPgDsc;
+}
+
+SwNumRule* DocumentStylePoolManager::GetNumRuleFromPool( sal_uInt16 nId )
+{
+ OSL_ENSURE( RES_POOLNUMRULE_BEGIN <= nId && nId < RES_POOLNUMRULE_END,
+ "Wrong AutoFormat Id" );
+
+ SwNumRule* pNewRule;
+
+ for (size_t n = 0; n < m_rDoc.GetNumRuleTable().size(); ++n )
+ {
+ pNewRule = m_rDoc.GetNumRuleTable()[ n ];
+ if (nId == pNewRule->GetPoolFormatId())
+ {
+ return pNewRule;
+ }
+ }
+
+ // error: unknown Pool style
+ if( RES_POOLNUMRULE_BEGIN > nId || nId >= RES_POOLNUMRULE_END )
+ {
+ OSL_ENSURE( false, "invalid Id" );
+ nId = RES_POOLNUMRULE_BEGIN;
+ }
+
+ static_assert(SAL_N_ELEMENTS(STR_POOLNUMRULE_NUM_ARY) == RES_POOLNUMRULE_END - RES_POOLNUMRULE_BEGIN, "### unexpected size!");
+ OUString aNm(SwResId(STR_POOLNUMRULE_NUM_ARY[nId - RES_POOLNUMRULE_BEGIN]));
+
+ SwCharFormat *pNumCFormat = nullptr, *pBullCFormat = nullptr;
+
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode eNumberFormatPositionAndSpaceMode
+ = numfunc::GetDefaultPositionAndSpaceMode(); //#i89178#
+ {
+ bool bIsModified = m_rDoc.getIDocumentState().IsModified();
+
+ sal_uInt16 n = m_rDoc.MakeNumRule( aNm, nullptr, false, eNumberFormatPositionAndSpaceMode );
+
+ pNewRule = m_rDoc.GetNumRuleTable()[ n ];
+ pNewRule->SetPoolFormatId( nId );
+ pNewRule->SetAutoRule( false );
+
+ if( RES_POOLNUMRULE_NUM1 <= nId && nId <= RES_POOLNUMRULE_NUM5 )
+ pNumCFormat = GetCharFormatFromPool( RES_POOLCHR_NUM_LEVEL );
+
+ if( ( RES_POOLNUMRULE_BUL1 <= nId && nId <= RES_POOLNUMRULE_BUL5 ) ||
+ RES_POOLNUMRULE_NUM5 == nId )
+ pBullCFormat = GetCharFormatFromPool( RES_POOLCHR_NUM_LEVEL );
+
+ if( !bIsModified )
+ m_rDoc.getIDocumentState().ResetModified();
+ }
+
+ switch( nId )
+ {
+ case RES_POOLNUMRULE_NUM1:
+ {
+ SwNumFormat aFormat;
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_ARABIC);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0.7 cm intervals, with 1 cm = 567
+ 397, 794, 1191, 1588, 1985, 2381, 2778, 3175, 3572, 3969
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr + 357 ); // 357 is indent of 0.63 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr + 357 );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+
+ case RES_POOLNUMRULE_NUM2:
+ {
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+ 397, 397, 397, 397, // 0.70 cm intervals
+ 397, 397, 397, 397,
+ 397, 397
+ };
+
+ const sal_uInt16* pArr = aAbsSpace;
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHARS_UPPER_LETTER);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ }
+
+ sal_uInt16 nSpace = 357; // indent of 0.63 cm
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ nSpace += pArr[ n ];
+ aFormat.SetAbsLSpace( nSpace );
+ aFormat.SetFirstLineOffset( - pArr[ n ] );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ nSpace += pArr[ n ];
+ aFormat.SetListtabPos( nSpace );
+ aFormat.SetIndentAt( nSpace );
+ aFormat.SetFirstLineIndent( - pArr[ n ] );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_NUM3:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHARS_LOWER_LETTER);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+
+ tools::Long const nOffs = 397; // 0.70 cm
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - nOffs );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( (n+1) * nOffs + 357 ); // 357 is indent of 0.63 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ tools::Long nPos = (n+1) * nOffs;
+ aFormat.SetListtabPos(nPos + 357);
+ aFormat.SetIndentAt(nPos + 357);
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_NUM4:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_ROMAN_UPPER);
+ aFormat.SetCharFormat( pNumCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 1.33 cm intervals
+ 754, 1508, 1191, 2262, 3016, 3771, 4525, 5279, 6033, 6787
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( 580 - (*pArr) ); // 1 cm space
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( 580 - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ aFormat.SetListFormat("", ".", n);
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_NUM5:
+ {
+ // [ First, LSpace ]
+ static const sal_uInt16 aAbsSpace0to2[] =
+ {
+ 174, 754, // 0.33, 1.33,
+ 567, 1151, // 1.03, 2.03,
+ 397, 1548 // 2.03, 2.73
+ };
+
+ const sal_uInt16* pArr0to2 = aAbsSpace0to2;
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_ROMAN_LOWER);
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+ aFormat.SetListFormat("", ".", 0);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ }
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( -pArr0to2[0] ); // == 0.33 cm
+ aFormat.SetAbsLSpace( pArr0to2[1] ); // == 1.33 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( -pArr0to2[0] );
+ aFormat.SetListtabPos( pArr0to2[1] );
+ aFormat.SetIndentAt( pArr0to2[1] );
+ }
+
+ aFormat.SetCharFormat( pNumCFormat );
+ pNewRule->Set( 0, aFormat );
+
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetStart( 1 );
+ aFormat.SetListFormat("", ".", 1);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( -pArr0to2[2] ); // == 1.03 cm
+ aFormat.SetAbsLSpace( pArr0to2[3] ); // == 2.03 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( -pArr0to2[2] );
+ aFormat.SetListtabPos( pArr0to2[3] );
+ aFormat.SetIndentAt( pArr0to2[3] );
+ }
+
+ pNewRule->Set( 1, aFormat );
+
+ aFormat.SetNumberingType(SVX_NUM_CHARS_LOWER_LETTER);
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetStart( 3 );
+ aFormat.SetListFormat("", u")", 2);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - pArr0to2[4] ); // == 2.03 cm
+ aFormat.SetAbsLSpace( pArr0to2[5] ); // == 2.73 cm
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( -pArr0to2[4] );
+ aFormat.SetListtabPos( pArr0to2[5] );
+ aFormat.SetIndentAt( pArr0to2[5] );
+ }
+
+ pNewRule->Set( 2, aFormat );
+
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ aFormat.SetBulletChar( cBulletChar );
+ sal_Int16 nOffs = o3tl::convert(4, o3tl::Length::mm, o3tl::Length::twip),
+ nOffs2 = o3tl::convert(2, o3tl::Length::cm, o3tl::Length::twip);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetFirstLineIndent( - nOffs );
+ }
+
+ for (sal_uInt16 n = 3; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetStart( n+1 );
+ aFormat.SetListFormat("", "", n);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( nOffs2 + ((n-3) * nOffs) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ tools::Long nPos = nOffs2 + ((n-3) * static_cast<tools::Long>(nOffs));
+ aFormat.SetListtabPos(nPos);
+ aFormat.SetIndentAt(nPos);
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+
+ case RES_POOLNUMRULE_BUL1:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ aFormat.SetBulletChar( cBulletChar );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,4 0,8 1,2 1,6 2,0 2,4 2,8 3,2 3,6 4,0
+ 227, 454, 680, 907, 1134, 1361, 1587, 1814, 2041, 2268
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL2:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+ aFormat.SetBulletChar( 0x2013 );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,3 0,6 0,9 1,2 1,5 1,8 2,1 2,4 2,7 3,0
+ 170, 340, 510, 680, 850, 1020, 1191, 1361, 1531, 1701
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL3:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+
+ sal_uInt16 nOffs = o3tl::convert(4, o3tl::Length::mm, o3tl::Length::twip);
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - nOffs );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n)
+ {
+ aFormat.SetBulletChar( (n & 1) ? 0x25a1 : 0x2611 );
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( ((n & 1) +1) * nOffs );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ tools::Long nPos = ((n & 1) +1) * static_cast<tools::Long>(nOffs);
+ aFormat.SetListtabPos(nPos);
+ aFormat.SetIndentAt(nPos);
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL4:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,4 0,8 1,2 1,6 2,0 2,4 2,8 3,2 3,6 4,0
+ 227, 454, 680, 907, 1134, 1361, 1587, 1814, 2041, 2268
+ };
+
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::SPACE );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ switch( n )
+ {
+ case 0: aFormat.SetBulletChar( 0x27a2 ); break;
+ case 1: aFormat.SetBulletChar( 0xE006 ); break;
+ default: aFormat.SetBulletChar( 0xE004 ); break;
+ }
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ case RES_POOLNUMRULE_BUL5:
+ {
+ SwNumFormat aFormat;
+
+ aFormat.SetPositionAndSpaceMode( eNumberFormatPositionAndSpaceMode );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ aFormat.SetCharFormat( pBullCFormat );
+ aFormat.SetStart( 1 );
+ aFormat.SetIncludeUpperLevels( 1 );
+ aFormat.SetBulletChar( 0x2717 );
+ aFormat.SetBulletFont( &numfunc::GetDefBulletFont() );
+
+ static const sal_uInt16 aAbsSpace[ MAXLEVEL ] =
+ {
+// cm: 0,4 0,8 1,2 1,6 2,0 2,4 2,8 3,2 3,6 4,0
+ 227, 454, 680, 907, 1134, 1361, 1587, 1814, 2041, 2268
+ };
+ const sal_uInt16* pArr = aAbsSpace;
+
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetFirstLineOffset( - (*pArr) );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ aFormat.SetFirstLineIndent( - (*pArr) );
+ }
+
+ for (sal_uInt16 n = 0; n < MAXLEVEL; ++n, ++pArr)
+ {
+ if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aFormat.SetAbsLSpace( *pArr );
+ }
+ else if ( eNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aFormat.SetListtabPos( *pArr );
+ aFormat.SetIndentAt( *pArr );
+ }
+
+ pNewRule->Set( n, aFormat );
+ }
+ }
+ break;
+ }
+
+ return pNewRule;
+}
+
+/// Check if this AutoCollection is already/still in use in this Document
+bool DocumentStylePoolManager::IsPoolTextCollUsed( sal_uInt16 nId ) const
+{
+ OSL_ENSURE(
+ (RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END) ||
+ (RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END) ||
+ (RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END) ||
+ (RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END) ||
+ (RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END) ||
+ (RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END),
+ "Wrong AutoFormat Id" );
+
+ SwTextFormatColl* pNewColl = nullptr;
+ bool bFnd = false;
+ for( SwTextFormatColls::size_type n = 0; !bFnd && n < m_rDoc.GetTextFormatColls()->size(); ++n )
+ {
+ pNewColl = (*m_rDoc.GetTextFormatColls())[ n ];
+ if( nId == pNewColl->GetPoolFormatId() )
+ bFnd = true;
+ }
+
+ if( !bFnd || !pNewColl->HasWriterListeners() )
+ return false;
+
+ SwAutoFormatGetDocNode aGetHt( &m_rDoc.GetNodes() );
+ return !pNewColl->GetInfo( aGetHt );
+}
+
+/// Check if this AutoCollection is already/still in use
+bool DocumentStylePoolManager::IsPoolFormatUsed( sal_uInt16 nId ) const
+{
+ const SwFormat *pNewFormat = nullptr;
+ const SwFormatsBase* pArray[ 2 ];
+ sal_uInt16 nArrCnt = 1;
+ bool bFnd = true;
+
+ if (RES_POOLCHR_BEGIN <= nId && nId < RES_POOLCHR_END)
+ {
+ pArray[0] = m_rDoc.GetCharFormats();
+ }
+ else if (RES_POOLFRM_BEGIN <= nId && nId < RES_POOLFRM_END)
+ {
+ pArray[0] = m_rDoc.GetFrameFormats();
+ pArray[1] = m_rDoc.GetSpzFrameFormats();
+ nArrCnt = 2;
+ }
+ else
+ {
+ SAL_WARN("sw.core", "Invalid Pool Id: " << nId << " should be within "
+ "[" << int(RES_POOLCHR_BEGIN) << "," << int(RES_POOLCHR_END) << ") or "
+ "[" << int(RES_POOLFRM_BEGIN) << "," << int(RES_POOLFRM_END) << ")");
+ bFnd = false;
+ }
+
+ if( bFnd )
+ {
+ bFnd = false;
+ while( nArrCnt-- && !bFnd )
+ for( size_t n = 0; !bFnd && n < (*pArray[nArrCnt]).GetFormatCount(); ++n )
+ {
+ pNewFormat = (*pArray[ nArrCnt ] ).GetFormat( n );
+ if( nId == pNewFormat->GetPoolFormatId() )
+ bFnd = true;
+ }
+ }
+
+ // Not found or no dependencies?
+ if( bFnd && pNewFormat->HasWriterListeners() )
+ {
+ // Check if we have dependent ContentNodes in the Nodes array
+ // (also indirect ones for derived Formats)
+ SwAutoFormatGetDocNode aGetHt( &m_rDoc.GetNodes() );
+ bFnd = !pNewFormat->GetInfo( aGetHt );
+ }
+ else
+ bFnd = false;
+
+ return bFnd;
+}
+
+/// Check if this AutoCollection is already/still in use in this Document
+bool DocumentStylePoolManager::IsPoolPageDescUsed( sal_uInt16 nId ) const
+{
+ OSL_ENSURE( RES_POOLPAGE_BEGIN <= nId && nId < RES_POOLPAGE_END,
+ "Wrong AutoFormat Id" );
+ SwPageDesc *pNewPgDsc = nullptr;
+ bool bFnd = false;
+ for( size_t n = 0; !bFnd && n < m_rDoc.GetPageDescCnt(); ++n )
+ {
+ pNewPgDsc = &m_rDoc.GetPageDesc(n);
+ if( nId == pNewPgDsc->GetPoolFormatId() )
+ bFnd = true;
+ }
+
+ // Not found or no dependencies?
+ if( !bFnd || !pNewPgDsc->HasWriterListeners() ) // ??????
+ return false;
+
+ // Check if we have dependent ContentNodes in the Nodes array
+ // (also indirect ones for derived Formats)
+ SwAutoFormatGetDocNode aGetHt( &m_rDoc.GetNodes() );
+ return !pNewPgDsc->GetInfo( aGetHt );
+}
+
+DocumentStylePoolManager::~DocumentStylePoolManager()
+{
+}
+
+}
+
+static std::vector<OUString>
+lcl_NewUINameArray(const TranslateId* pIds, const size_t nLen, const size_t nSvxIds = 0)
+{
+ assert(nSvxIds <= nLen);
+ const size_t nWriterIds = nLen - nSvxIds;
+ std::vector<OUString> aNameArray;
+ aNameArray.reserve(nLen);
+ for (size_t i = 0; i < nWriterIds; ++i)
+ aNameArray.push_back(SwResId(pIds[i]));
+ for (size_t i = nWriterIds; i < nLen; ++i)
+ aNameArray.push_back(SvxResId(pIds[i]));
+ return aNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTextUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aTextUINameArray;
+
+ auto it = s_aTextUINameArray.find(rCurrentLanguage);
+ if (it == s_aTextUINameArray.end())
+ it = s_aTextUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_TEXT_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_TEXT_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetListsUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aListsUINameArray;
+
+ auto it = s_aListsUINameArray.find(rCurrentLanguage);
+ if (it == s_aListsUINameArray.end())
+ it = s_aListsUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_LISTS_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_LISTS_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetExtraUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aExtraUINameArray;
+
+ auto it = s_aExtraUINameArray.find(rCurrentLanguage);
+ if (it == s_aExtraUINameArray.end())
+ it = s_aExtraUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_EXTRA_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_EXTRA_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetRegisterUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aRegisterUINameArray;
+
+ auto it = s_aRegisterUINameArray.find(rCurrentLanguage);
+ if (it == s_aRegisterUINameArray.end())
+ it = s_aRegisterUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_REGISTER_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_REGISTER_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetDocUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aDocUINameArray;
+
+ auto it = s_aDocUINameArray.find(rCurrentLanguage);
+ if (it == s_aDocUINameArray.end())
+ it = s_aDocUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_DOC_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_DOC_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aHTMLUINameArray;
+
+ auto it = s_aHTMLUINameArray.find(rCurrentLanguage);
+ if (it == s_aHTMLUINameArray.end())
+ it = s_aHTMLUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCOLL_HTML_ARY, SAL_N_ELEMENTS(STR_POOLCOLL_HTML_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetFrameFormatUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aFrameFormatUINameArray;
+
+ auto it = s_aFrameFormatUINameArray.find(rCurrentLanguage);
+ if (it == s_aFrameFormatUINameArray.end())
+ it = s_aFrameFormatUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLFRM_ARY, SAL_N_ELEMENTS(STR_POOLFRM_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetChrFormatUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aChrFormatUINameArray;
+
+ auto it = s_aChrFormatUINameArray.find(rCurrentLanguage);
+ if (it == s_aChrFormatUINameArray.end())
+ it = s_aChrFormatUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCHR_ARY, SAL_N_ELEMENTS(STR_POOLCHR_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLChrFormatUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aHTMLChrFormatUINameArray;
+
+ auto it = s_aHTMLChrFormatUINameArray.find(rCurrentLanguage);
+ if (it == s_aHTMLChrFormatUINameArray.end())
+ it = s_aHTMLChrFormatUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLCHR_HTML_ARY, SAL_N_ELEMENTS(STR_POOLCHR_HTML_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetPageDescUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aPageDescUINameArray;
+
+ auto it = s_aPageDescUINameArray.find(rCurrentLanguage);
+ if (it == s_aPageDescUINameArray.end())
+ it = s_aPageDescUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLPAGE_ARY, SAL_N_ELEMENTS(STR_POOLPAGE_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetNumRuleUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aNumRuleUINameArray;
+
+ auto it = s_aNumRuleUINameArray.find(rCurrentLanguage);
+ if (it == s_aNumRuleUINameArray.end())
+ it = s_aNumRuleUINameArray.emplace(rCurrentLanguage,
+ lcl_NewUINameArray(STR_POOLNUMRULE_NUM_ARY, SAL_N_ELEMENTS(STR_POOLNUMRULE_NUM_ARY))).first;
+
+ return it->second;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTableStyleUINameArray()
+{
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, std::vector<OUString>> s_aTableStyleUINameArray;
+
+ auto it = s_aTableStyleUINameArray.find(rCurrentLanguage);
+ if (it == s_aTableStyleUINameArray.end())
+ it = s_aTableStyleUINameArray.emplace(rCurrentLanguage,
+ // 1 Writer resource string (XXX if this ever changes rather use offset math)
+ lcl_NewUINameArray(STR_TABSTYLE_ARY, SAL_N_ELEMENTS(STR_TABSTYLE_ARY),
+ static_cast<size_t>(SAL_N_ELEMENTS(STR_TABSTYLE_ARY) - 1))).first;
+
+ return it->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentTimerManager.cxx b/sw/source/core/doc/DocumentTimerManager.cxx
new file mode 100644
index 000000000..13f85a202
--- /dev/null
+++ b/sw/source/core/doc/DocumentTimerManager.cxx
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <DocumentTimerManager.hxx>
+
+#include <doc.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <viewsh.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/linguprops.hxx>
+#include <fldupde.hxx>
+#include <sfx2/progress.hxx>
+#include <viewopt.hxx>
+#include <docsh.hxx>
+#include <docfld.hxx>
+#include <fldbas.hxx>
+#include <vcl/scheduler.hxx>
+#include <comphelper/lok.hxx>
+#include <editsh.hxx>
+
+namespace sw
+{
+DocumentTimerManager::DocumentTimerManager(SwDoc& i_rSwdoc)
+ : m_rDoc(i_rSwdoc)
+ , m_nIdleBlockCount(0)
+ , m_bStartOnUnblock(false)
+ , m_aDocIdle(i_rSwdoc, "sw::DocumentTimerManager m_aDocIdle")
+ , m_aFireIdleJobsTimer("sw::DocumentTimerManager m_aFireIdleJobsTimer")
+ , m_bWaitForLokInit(true)
+{
+ m_aDocIdle.SetPriority(TaskPriority::LOWEST);
+ m_aDocIdle.SetInvokeHandler(LINK(this, DocumentTimerManager, DoIdleJobs));
+
+ m_aFireIdleJobsTimer.SetInvokeHandler(LINK(this, DocumentTimerManager, FireIdleJobsTimeout));
+ m_aFireIdleJobsTimer.SetTimeout(1000); // Enough time for LOK to render the first tiles.
+}
+
+void DocumentTimerManager::StartIdling()
+{
+ if (m_bWaitForLokInit && comphelper::LibreOfficeKit::isActive())
+ {
+ // Start the idle jobs only after a certain delay.
+ m_bWaitForLokInit = false;
+ StopIdling();
+ m_aFireIdleJobsTimer.Start();
+ return;
+ }
+
+ m_bWaitForLokInit = false;
+ m_bStartOnUnblock = true;
+ if (0 == m_nIdleBlockCount)
+ {
+ if (!m_aDocIdle.IsActive())
+ m_aDocIdle.Start();
+ else
+ Scheduler::Wakeup();
+ }
+}
+
+void DocumentTimerManager::StopIdling()
+{
+ m_bStartOnUnblock = false;
+ m_aDocIdle.Stop();
+}
+
+void DocumentTimerManager::BlockIdling()
+{
+ assert(SAL_MAX_UINT32 != m_nIdleBlockCount);
+ ++m_nIdleBlockCount;
+}
+
+void DocumentTimerManager::UnblockIdling()
+{
+ assert(0 != m_nIdleBlockCount);
+ --m_nIdleBlockCount;
+
+ if ((0 == m_nIdleBlockCount) && m_bStartOnUnblock)
+ {
+ if (!m_aDocIdle.IsActive())
+ m_aDocIdle.Start();
+ else
+ Scheduler::Wakeup();
+ }
+}
+
+IMPL_LINK(DocumentTimerManager, FireIdleJobsTimeout, Timer*, , void)
+{
+ // Now we can run the idle jobs, assuming we finished LOK initialization.
+ StartIdling();
+}
+
+DocumentTimerManager::IdleJob DocumentTimerManager::GetNextIdleJob() const
+{
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pTmpRoot &&
+ !SfxProgress::GetActiveProgress( m_rDoc.GetDocShell() ) )
+ {
+ SwViewShell* pShell(m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ for(const SwViewShell& rSh : pShell->GetRingContainer())
+ if( rSh.ActionPend() )
+ return IdleJob::Busy;
+
+ if( pTmpRoot->IsNeedGrammarCheck() )
+ {
+ bool bIsOnlineSpell = pShell->GetViewOptions()->IsOnlineSpell();
+ bool bIsAutoGrammar = false;
+ SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bIsAutoGrammar;
+
+ if( bIsOnlineSpell && bIsAutoGrammar && m_rDoc.StartGrammarChecking( true ) )
+ return IdleJob::Grammar;
+ }
+
+ // If we're dragging re-layout doesn't occur so avoid a busy loop.
+ if (!pShell->HasDrawViewDrag())
+ {
+ for ( auto pLayout : m_rDoc.GetAllLayouts() )
+ {
+ if( pLayout->IsIdleFormat() )
+ return IdleJob::Layout;
+ }
+ }
+
+ SwFieldUpdateFlags nFieldUpdFlag = m_rDoc.GetDocumentSettingManager().getFieldUpdateFlags(true);
+ if( ( AUTOUPD_FIELD_ONLY == nFieldUpdFlag
+ || AUTOUPD_FIELD_AND_CHARTS == nFieldUpdFlag )
+ && m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().IsFieldsDirty() )
+ {
+ if( m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().IsInUpdateFields()
+ || m_rDoc.getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ return IdleJob::Busy;
+ return IdleJob::Fields;
+ }
+ }
+
+ return IdleJob::None;
+}
+
+IMPL_LINK_NOARG( DocumentTimerManager, DoIdleJobs, Timer*, void )
+{
+#ifdef TIMELOG
+ static ::rtl::Logfile* pModLogFile = new ::rtl::Logfile( "First DoIdleJobs" );
+#endif
+ BlockIdling();
+ StopIdling();
+
+ IdleJob eJob = GetNextIdleJob();
+
+ switch ( eJob )
+ {
+ case IdleJob::Grammar:
+ m_rDoc.StartGrammarChecking();
+ break;
+
+ case IdleJob::Layout:
+ for ( auto pLayout : m_rDoc.GetAllLayouts() )
+ if( pLayout->IsIdleFormat() )
+ {
+ pLayout->GetCurrShell()->LayoutIdle();
+ break;
+ }
+ break;
+
+ case IdleJob::Fields:
+ {
+ SwViewShell* pShell( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() );
+ SwRootFrame* pTmpRoot = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+
+ // Action brackets!
+ m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().SetInUpdateFields( true );
+
+ pTmpRoot->StartAllAction();
+
+ // no jump on update of fields #i85168#
+ const bool bOldLockView = pShell->IsViewLocked();
+ pShell->LockView( true );
+
+ auto pChapterFieldType = m_rDoc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter );
+ pChapterFieldType->CallSwClientNotify(sw::LegacyModifyHint( nullptr, nullptr )); // ChapterField
+ m_rDoc.getIDocumentFieldsAccess().UpdateExpFields( nullptr, false ); // Updates ExpressionFields
+ m_rDoc.getIDocumentFieldsAccess().UpdateTableFields(nullptr); // Tables
+ m_rDoc.getIDocumentFieldsAccess().UpdateRefFields(); // References
+
+ // Validate and update the paragraph signatures.
+ if (SwEditShell* pSh = m_rDoc.GetEditShell())
+ pSh->ValidateAllParagraphSignatures(true);
+
+ pTmpRoot->EndAllAction();
+
+ pShell->LockView( bOldLockView );
+
+ m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().SetInUpdateFields( false );
+ m_rDoc.getIDocumentFieldsAccess().GetUpdateFields().SetFieldsDirty( false );
+ break;
+ }
+
+ case IdleJob::Busy:
+ break;
+ case IdleJob::None:
+ break;
+ }
+
+ if ( IdleJob::None != eJob )
+ StartIdling();
+ UnblockIdling();
+
+#ifdef TIMELOG
+ if( pModLogFile && 1 != (long)pModLogFile )
+ delete pModLogFile, static_cast<long&>(pModLogFile) = 1;
+#endif
+}
+
+DocumentTimerManager::~DocumentTimerManager() {}
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/SwDocIdle.cxx b/sw/source/core/doc/SwDocIdle.cxx
new file mode 100644
index 000000000..8c3764289
--- /dev/null
+++ b/sw/source/core/doc/SwDocIdle.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <viewopt.hxx>
+#include <vcl/scheduler.hxx>
+
+#include <SwDocIdle.hxx>
+#include <IDocumentTimerAccess.hxx>
+
+namespace sw
+{
+
+sal_uInt64 SwDocIdle::UpdateMinPeriod( sal_uInt64 /* nTimeNow */ ) const
+{
+ bool bReadyForSchedule = true;
+
+ SwView* pView = m_rDoc.GetDocShell() ? m_rDoc.GetDocShell()->GetView() : nullptr;
+ if( pView )
+ {
+ SwWrtShell& rWrtShell = pView->GetWrtShell();
+ bReadyForSchedule = rWrtShell.GetViewOptions()->IsIdle();
+ }
+
+ if( bReadyForSchedule && !m_rDoc.getIDocumentTimerAccess().IsDocIdle() )
+ bReadyForSchedule = false;
+
+ return bReadyForSchedule
+ ? Scheduler::ImmediateTimeoutMs : Scheduler::InfiniteTimeoutMs;
+}
+
+SwDocIdle::SwDocIdle( SwDoc &doc, const char * pDebugIdleName )
+ : Idle(pDebugIdleName), m_rDoc( doc )
+{
+}
+
+SwDocIdle::~SwDocIdle()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/SwStyleNameMapper.cxx b/sw/source/core/doc/SwStyleNameMapper.cxx
new file mode 100644
index 000000000..fcb39150e
--- /dev/null
+++ b/sw/source/core/doc/SwStyleNameMapper.cxx
@@ -0,0 +1,784 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwStyleNameMapper.hxx>
+#include <poolfmt.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <map>
+
+#ifdef _NEED_TO_DEBUG_MAPPING
+#include <stdlib.h>
+#endif
+
+namespace
+{
+
+const OUString &
+lcl_GetSpecialExtraName(const OUString& rExtraName, const bool bIsUIName )
+{
+ const std::vector<OUString>& rExtraArr = bIsUIName
+ ? SwStyleNameMapper::GetExtraUINameArray()
+ : SwStyleNameMapper::GetExtraProgNameArray();
+ static const sal_uInt16 nIds[] =
+ {
+ RES_POOLCOLL_LABEL_DRAWING - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_ABB - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_TABLE - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_FRAME- RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_FIGURE-RES_POOLCOLL_EXTRA_BEGIN,
+ 0
+ };
+ const sal_uInt16 * pIds;
+ for ( pIds = nIds; *pIds; ++pIds)
+ {
+ if (rExtraName == rExtraArr[ *pIds ])
+ {
+ return bIsUIName
+ ? SwStyleNameMapper::GetExtraProgNameArray()[*pIds]
+ : SwStyleNameMapper::GetExtraUINameArray()[*pIds];
+ }
+ }
+ return rExtraName;
+}
+
+bool lcl_SuffixIsUser(const OUString & rString)
+{
+ const sal_Unicode *pChar = rString.getStr();
+ sal_Int32 nLen = rString.getLength();
+ bool bRet = false;
+ if( nLen > 8 &&
+ pChar[nLen-7] == ' ' &&
+ pChar[nLen-6] == '(' &&
+ pChar[nLen-5] == 'u' &&
+ pChar[nLen-4] == 's' &&
+ pChar[nLen-3] == 'e' &&
+ pChar[nLen-2] == 'r' &&
+ pChar[nLen-1] == ')' )
+ bRet = true;
+ return bRet;
+}
+
+void lcl_CheckSuffixAndDelete(OUString & rString)
+{
+ if (lcl_SuffixIsUser(rString))
+ {
+ rString = rString.copy(0, rString.getLength() - 7);
+ }
+}
+
+NameToIdHash HashFromRange(sal_uInt16 nAcc) { return NameToIdHash(nAcc); }
+template <typename... Rest>
+NameToIdHash HashFromRange(sal_uInt16 nAcc, sal_uInt16 nBegin, sal_uInt16 nEnd,
+ const std::vector<OUString>& (*pFunc)(), Rest... rest)
+{
+ NameToIdHash hash(HashFromRange(nAcc + nEnd - nBegin, rest...));
+ sal_uInt16 nIndex, nId;
+ const std::vector<OUString>& rStrings = pFunc();
+ for (nIndex = 0, nId = nBegin; nId < nEnd; nId++, nIndex++)
+ hash[rStrings[nIndex]] = nId;
+ return hash;
+}
+
+template <auto initFunc> struct TablePair
+{
+ static const NameToIdHash& getMap(bool bProgName)
+ {
+ if (bProgName)
+ {
+ static const NameToIdHash s_aProgMap(initFunc(true));
+ return s_aProgMap;
+ }
+
+ SvtSysLocale aSysLocale;
+ const LanguageTag& rCurrentLanguage = aSysLocale.GetUILanguageTag();
+ static std::map<LanguageTag, NameToIdHash> s_aUIMap;
+
+ auto it = s_aUIMap.find(rCurrentLanguage);
+ if (it == s_aUIMap.end())
+ it = s_aUIMap.emplace(rCurrentLanguage, initFunc(false)).first;
+
+ return it->second;
+ }
+};
+
+NameToIdHash GetParaMap (bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLCOLL_TEXT_BEGIN, RES_POOLCOLL_TEXT_END, bProgName ? &SwStyleNameMapper::GetTextProgNameArray : &SwStyleNameMapper::GetTextUINameArray,
+ RES_POOLCOLL_LISTS_BEGIN, RES_POOLCOLL_LISTS_END, bProgName ? &SwStyleNameMapper::GetListsProgNameArray : &SwStyleNameMapper::GetListsUINameArray,
+ RES_POOLCOLL_EXTRA_BEGIN, RES_POOLCOLL_EXTRA_END, bProgName ? &SwStyleNameMapper::GetExtraProgNameArray : &SwStyleNameMapper::GetExtraUINameArray,
+ RES_POOLCOLL_REGISTER_BEGIN, RES_POOLCOLL_REGISTER_END, bProgName ? &SwStyleNameMapper::GetRegisterProgNameArray : &SwStyleNameMapper::GetRegisterUINameArray,
+ RES_POOLCOLL_DOC_BEGIN, RES_POOLCOLL_DOC_END, bProgName ? &SwStyleNameMapper::GetDocProgNameArray : &SwStyleNameMapper::GetDocUINameArray,
+ RES_POOLCOLL_HTML_BEGIN, RES_POOLCOLL_HTML_END, bProgName ? &SwStyleNameMapper::GetHTMLProgNameArray : &SwStyleNameMapper::GetHTMLUINameArray
+ );
+};
+
+NameToIdHash GetCharMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLCHR_NORMAL_BEGIN, RES_POOLCHR_NORMAL_END, bProgName ? &SwStyleNameMapper::GetChrFormatProgNameArray : &SwStyleNameMapper::GetChrFormatUINameArray,
+ RES_POOLCHR_HTML_BEGIN, RES_POOLCHR_HTML_END, bProgName ? &SwStyleNameMapper::GetHTMLChrFormatProgNameArray : &SwStyleNameMapper::GetHTMLChrFormatUINameArray
+ );
+};
+
+NameToIdHash GetFrameMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLFRM_BEGIN, RES_POOLFRM_END, bProgName ? &SwStyleNameMapper::GetFrameFormatProgNameArray : &SwStyleNameMapper::GetFrameFormatUINameArray
+ );
+};
+
+NameToIdHash GetPageMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLPAGE_BEGIN, RES_POOLPAGE_END, bProgName ? &SwStyleNameMapper::GetPageDescProgNameArray : &SwStyleNameMapper::GetPageDescUINameArray
+ );
+};
+
+NameToIdHash GetNumRuleMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLNUMRULE_BEGIN, RES_POOLNUMRULE_END, bProgName ? &SwStyleNameMapper::GetNumRuleProgNameArray : &SwStyleNameMapper::GetNumRuleUINameArray
+ );
+};
+
+NameToIdHash GetTableStyleMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLTABLESTYLE_BEGIN, RES_POOLTABLESTYLE_END, bProgName ? &SwStyleNameMapper::GetTableStyleProgNameArray : &SwStyleNameMapper::GetTableStyleUINameArray
+ );
+};
+
+NameToIdHash GetCellStyleMap(bool bProgName)
+{
+ return HashFromRange(0,
+ RES_POOLCELLSTYLE_BEGIN, RES_POOLCELLSTYLE_END, bProgName ? &SwStyleNameMapper::GetCellStyleProgNameArray : &SwStyleNameMapper::GetCellStyleUINameArray
+ );
+};
+
+} // namespace
+
+#ifdef _NEED_TO_DEBUG_MAPPING
+void SwStyleNameMapper::testNameTable( SwGetPoolIdFromName const nFamily, sal_uInt16 const nStartIndex, sal_uInt16 const nEndIndex )
+{
+ sal_uInt16 nIndex;
+ sal_uInt16 nId;
+
+ for ( nIndex = 0, nId = nStartIndex ; nId < nEndIndex ; nId++,nIndex++ )
+ {
+ OUString aString, bString;
+ FillUIName ( nId, aString );
+ bString = GetProgName ( nFamily, aString );
+ sal_uInt16 nNewId = GetPoolIdFromProgName ( bString, nFamily );
+ FillProgName ( nNewId, aString );
+ bString = GetUIName ( aString, nFamily );
+ nNewId = GetPoolIdFromUIName ( aString, nFamily );
+ if ( nNewId != nId )
+ abort();
+ }
+}
+#endif
+
+const NameToIdHash & SwStyleNameMapper::getHashTable ( SwGetPoolIdFromName eFlags, bool bProgName )
+{
+#ifdef _NEED_TO_DEBUG_MAPPING
+ static bool bTested = false;
+ if ( !bTested )
+ {
+ bTested = true;
+
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_TEXT_BEGIN, RES_POOLCOLL_TEXT_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_LISTS_BEGIN, RES_POOLCOLL_LISTS_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_EXTRA_BEGIN, RES_POOLCOLL_EXTRA_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_REGISTER_BEGIN, RES_POOLCOLL_REGISTER_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_DOC_BEGIN, RES_POOLCOLL_DOC_END );
+ testNameTable( SwGetPoolIdFromName::TxtColl, RES_POOLCOLL_HTML_BEGIN, RES_POOLCOLL_HTML_END );
+ testNameTable( SwGetPoolIdFromName::ChrFmt, RES_POOLCHR_NORMAL_BEGIN, RES_POOLCHR_NORMAL_END );
+ testNameTable( SwGetPoolIdFromName::ChrFmt, RES_POOLCHR_HTML_BEGIN, RES_POOLCHR_HTML_END );
+ testNameTable( SwGetPoolIdFromName::FrmFmt, RES_POOLFRM_BEGIN, RES_POOLFRM_END );
+ testNameTable( SwGetPoolIdFromName::PageDesc, RES_POOLPAGE_BEGIN, RES_POOLPAGE_END );
+ testNameTable( SwGetPoolIdFromName::NumRule, RES_POOLNUMRULE_BEGIN, RES_POOLNUMRULE_END );
+ }
+#endif
+
+ switch ( eFlags )
+ {
+ case SwGetPoolIdFromName::TxtColl:
+ return TablePair<GetParaMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::ChrFmt:
+ return TablePair<GetCharMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::FrmFmt:
+ return TablePair<GetFrameMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::PageDesc:
+ return TablePair<GetPageMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::NumRule:
+ return TablePair<GetNumRuleMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::TabStyle:
+ return TablePair<GetTableStyleMap>::getMap(bProgName);
+ case SwGetPoolIdFromName::CellStyle:
+ return TablePair<GetCellStyleMap>::getMap(bProgName);
+ }
+
+ assert(false); // must not reach here
+ abort();
+}
+
+// This gets the UI name from the programmatic name
+const OUString& SwStyleNameMapper::GetUIName(const OUString& rName,
+ SwGetPoolIdFromName const eFlags)
+{
+ sal_uInt16 nId = GetPoolIdFromProgName ( rName, eFlags );
+ return nId != USHRT_MAX ? GetUIName( nId, rName ) : rName;
+}
+
+// Get the programmatic name from the UI name
+const OUString& SwStyleNameMapper::GetProgName(
+ const OUString& rName, SwGetPoolIdFromName const eFlags)
+{
+ sal_uInt16 nId = GetPoolIdFromUIName ( rName, eFlags );
+ return nId != USHRT_MAX ? GetProgName( nId, rName ) : rName;
+}
+
+// Get the programmatic name from the UI name in rName and put it into rFillName
+void SwStyleNameMapper::FillProgName(
+ const OUString& rName, OUString& rFillName,
+ SwGetPoolIdFromName const eFlags)
+{
+ sal_uInt16 nId = GetPoolIdFromUIName ( rName, eFlags );
+ if ( nId == USHRT_MAX )
+ {
+ // rName isn't in our UI name table...check if it's in the programmatic one
+ nId = GetPoolIdFromProgName ( rName, eFlags );
+
+ rFillName = rName;
+ if (nId == USHRT_MAX )
+ {
+ // It isn't ...make sure the suffix isn't already " (user)"...if it is,
+ // we need to add another one
+ if (lcl_SuffixIsUser(rFillName))
+ rFillName += " (user)";
+ }
+ else
+ {
+ // It's in the programmatic name table...append suffix
+ rFillName += " (user)";
+ }
+ }
+ else
+ {
+ // If we aren't trying to disambiguate, then just do a normal fill
+ fillNameFromId(nId, rFillName, true);
+ }
+
+ if (eFlags == SwGetPoolIdFromName::ChrFmt && rName == SwResId(STR_POOLCHR_STANDARD))
+ rFillName = "Standard";
+}
+
+// Get the UI name from the programmatic name in rName and put it into rFillName
+void SwStyleNameMapper::FillUIName(
+ const OUString& rName, OUString& rFillName,
+ SwGetPoolIdFromName const eFlags)
+{
+ OUString aName = rName;
+ if (eFlags == SwGetPoolIdFromName::ChrFmt && rName == "Standard")
+ aName = SwResId(STR_POOLCHR_STANDARD);
+
+ sal_uInt16 nId = GetPoolIdFromProgName ( aName, eFlags );
+ if ( nId == USHRT_MAX )
+ {
+ rFillName = aName;
+ // aName isn't in our Prog name table...check if it has a " (user)" suffix, if so remove it
+ lcl_CheckSuffixAndDelete ( rFillName );
+ }
+ else
+ {
+ // If we aren't trying to disambiguate, then just do a normal fill
+ fillNameFromId(nId, rFillName, false);
+ }
+}
+
+const OUString& SwStyleNameMapper::getNameFromId(
+ sal_uInt16 const nId, const OUString& rFillName, bool const bProgName)
+{
+ sal_uInt16 nStt = 0;
+ const std::vector<OUString>* pStrArr = nullptr;
+
+ switch( (USER_FMT | COLL_GET_RANGE_BITS | POOLGRP_NOCOLLID) & nId )
+ {
+ case COLL_TEXT_BITS:
+ if( RES_POOLCOLL_TEXT_BEGIN <= nId && nId < RES_POOLCOLL_TEXT_END )
+ {
+ pStrArr = bProgName ? &GetTextProgNameArray() : &GetTextUINameArray();
+ nStt = RES_POOLCOLL_TEXT_BEGIN;
+ }
+ break;
+ case COLL_LISTS_BITS:
+ if( RES_POOLCOLL_LISTS_BEGIN <= nId && nId < RES_POOLCOLL_LISTS_END )
+ {
+ pStrArr = bProgName ? &GetListsProgNameArray() : &GetListsUINameArray();
+ nStt = RES_POOLCOLL_LISTS_BEGIN;
+ }
+ break;
+ case COLL_EXTRA_BITS:
+ if( RES_POOLCOLL_EXTRA_BEGIN <= nId && nId < RES_POOLCOLL_EXTRA_END )
+ {
+ pStrArr = bProgName ? &GetExtraProgNameArray() : &GetExtraUINameArray();
+ nStt = RES_POOLCOLL_EXTRA_BEGIN;
+ }
+ break;
+ case COLL_REGISTER_BITS:
+ if( RES_POOLCOLL_REGISTER_BEGIN <= nId && nId < RES_POOLCOLL_REGISTER_END )
+ {
+ pStrArr = bProgName ? &GetRegisterProgNameArray() : &GetRegisterUINameArray();
+ nStt = RES_POOLCOLL_REGISTER_BEGIN;
+ }
+ break;
+ case COLL_DOC_BITS:
+ if( RES_POOLCOLL_DOC_BEGIN <= nId && nId < RES_POOLCOLL_DOC_END )
+ {
+ pStrArr = bProgName ? &GetDocProgNameArray() : &GetDocUINameArray();
+ nStt = RES_POOLCOLL_DOC_BEGIN;
+ }
+ break;
+ case COLL_HTML_BITS:
+ if( RES_POOLCOLL_HTML_BEGIN <= nId && nId < RES_POOLCOLL_HTML_END )
+ {
+ pStrArr = bProgName ? &GetHTMLProgNameArray() : &GetHTMLUINameArray();
+ nStt = RES_POOLCOLL_HTML_BEGIN;
+ }
+ break;
+ case POOLGRP_CHARFMT:
+ if( RES_POOLCHR_NORMAL_BEGIN <= nId && nId < RES_POOLCHR_NORMAL_END )
+ {
+ pStrArr = bProgName ? &GetChrFormatProgNameArray() : &GetChrFormatUINameArray();
+ nStt = RES_POOLCHR_NORMAL_BEGIN;
+ }
+ else if( RES_POOLCHR_HTML_BEGIN <= nId && nId < RES_POOLCHR_HTML_END )
+ {
+ pStrArr = bProgName ? &GetHTMLChrFormatProgNameArray() : &GetHTMLChrFormatUINameArray();
+ nStt = RES_POOLCHR_HTML_BEGIN;
+ }
+ break;
+ case POOLGRP_FRAMEFMT:
+ if( RES_POOLFRM_BEGIN <= nId && nId < RES_POOLFRM_END )
+ {
+ pStrArr = bProgName ? &GetFrameFormatProgNameArray() : &GetFrameFormatUINameArray();
+ nStt = RES_POOLFRM_BEGIN;
+ }
+ break;
+ case POOLGRP_PAGEDESC:
+ if( RES_POOLPAGE_BEGIN <= nId && nId < RES_POOLPAGE_END )
+ {
+ pStrArr = bProgName ? &GetPageDescProgNameArray() : &GetPageDescUINameArray();
+ nStt = RES_POOLPAGE_BEGIN;
+ }
+ break;
+ case POOLGRP_NUMRULE:
+ if( RES_POOLNUMRULE_BEGIN <= nId && nId < RES_POOLNUMRULE_END )
+ {
+ pStrArr = bProgName ? &GetNumRuleProgNameArray() : &GetNumRuleUINameArray();
+ nStt = RES_POOLNUMRULE_BEGIN;
+ }
+ break;
+ case POOLGRP_TABSTYLE:
+ if( RES_POOLTABLESTYLE_BEGIN <= nId && nId < RES_POOLTABLESTYLE_END )
+ {
+ pStrArr = bProgName ? &GetTableStyleProgNameArray() : &GetTableStyleUINameArray();
+ nStt = RES_POOLTABLESTYLE_BEGIN;
+ }
+ break;
+ }
+ return pStrArr ? pStrArr->operator[](nId - nStt) : rFillName;
+}
+
+void SwStyleNameMapper::fillNameFromId(
+ sal_uInt16 const nId, OUString& rFillName, bool bProgName)
+{
+ rFillName = getNameFromId(nId, rFillName, bProgName);
+}
+
+// Get the UI name from the pool ID
+void SwStyleNameMapper::FillUIName(sal_uInt16 const nId, OUString& rFillName)
+{
+ fillNameFromId(nId, rFillName, false);
+}
+
+// Get the UI name from the pool ID
+const OUString& SwStyleNameMapper::GetUIName(
+ sal_uInt16 const nId, const OUString& rName)
+{
+ return getNameFromId(nId, rName, false);
+}
+
+// Get the programmatic name from the pool ID
+void SwStyleNameMapper::FillProgName(sal_uInt16 nId, OUString& rFillName)
+{
+ fillNameFromId(nId, rFillName, true);
+}
+
+// Get the programmatic name from the pool ID
+const OUString&
+SwStyleNameMapper::GetProgName(sal_uInt16 const nId, const OUString& rName)
+{
+ return getNameFromId(nId, rName, true);
+}
+
+// This gets the PoolId from the UI Name
+sal_uInt16 SwStyleNameMapper::GetPoolIdFromUIName(
+ const OUString& rName, SwGetPoolIdFromName const eFlags)
+{
+ const NameToIdHash & rHashMap = getHashTable ( eFlags, false );
+ NameToIdHash::const_iterator aIter = rHashMap.find(rName);
+ return aIter != rHashMap.end() ? (*aIter).second : USHRT_MAX;
+}
+
+// Get the Pool ID from the programmatic name
+sal_uInt16 SwStyleNameMapper::GetPoolIdFromProgName(
+ const OUString& rName, SwGetPoolIdFromName const eFlags)
+{
+ const NameToIdHash & rHashMap = getHashTable ( eFlags, true );
+ NameToIdHash::const_iterator aIter = rHashMap.find(rName);
+ return aIter != rHashMap.end() ? (*aIter).second : USHRT_MAX;
+}
+
+// Hard coded Programmatic Name tables
+
+/// returns an empty array because Cell Names aren't translated
+const std::vector<OUString>& SwStyleNameMapper::GetCellStyleUINameArray()
+{
+ static const std::vector<OUString> s_aCellStyleUINameArray;
+ return s_aCellStyleUINameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTextProgNameArray()
+{
+ static const std::vector<OUString> s_aTextProgNameArray = {
+ "Standard", // RES_POOLCOLL_STANDARD
+ "Text body",
+ "First line indent",
+ "Hanging indent",
+ "Text body indent",
+ "Salutation",
+ "Signature",
+ "List Indent", // RES_POOLCOLL_CONFRONTATION
+ "Marginalia",
+ "Heading",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3",
+ "Heading 4",
+ "Heading 5",
+ "Heading 6",
+ "Heading 7",
+ "Heading 8",
+ "Heading 9",
+ "Heading 10", // RES_POOLCOLL_TEXT_END
+ };
+ return s_aTextProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetListsProgNameArray()
+{
+ static const std::vector<OUString> s_aListsProgNameArray = {
+ "List", // STR_POCO_PRGM_NUMBER_BULLET_BASE
+ "Numbering 1 Start", // STR_POCO_PRGM_NUM_LEVEL1S
+ "Numbering 1",
+ "Numbering 1 End",
+ "Numbering 1 Cont.",
+ "Numbering 2 Start",
+ "Numbering 2",
+ "Numbering 2 End",
+ "Numbering 2 Cont.",
+ "Numbering 3 Start",
+ "Numbering 3",
+ "Numbering 3 End",
+ "Numbering 3 Cont.",
+ "Numbering 4 Start",
+ "Numbering 4",
+ "Numbering 4 End",
+ "Numbering 4 Cont.",
+ "Numbering 5 Start",
+ "Numbering 5",
+ "Numbering 5 End",
+ "Numbering 5 Cont.",
+ "List 1 Start",
+ "List 1",
+ "List 1 End",
+ "List 1 Cont.",
+ "List 2 Start",
+ "List 2",
+ "List 2 End",
+ "List 2 Cont.",
+ "List 3 Start",
+ "List 3",
+ "List 3 End",
+ "List 3 Cont.",
+ "List 4 Start",
+ "List 4",
+ "List 4 End",
+ "List 4 Cont.",
+ "List 5 Start",
+ "List 5",
+ "List 5 End",
+ "List 5 Cont.", // STR_POCO_PRGM_BULLET_NONUM5
+ };
+ return s_aListsProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetExtraProgNameArray()
+{
+ static const std::vector<OUString> s_aExtraProgNameArray = {
+ "Header and Footer", // RES_POOLCOLL_EXTRA_BEGIN
+ "Header",
+ "Header left",
+ "Header right",
+ "Footer",
+ "Footer left",
+ "Footer right",
+ "Table Contents",
+ "Table Heading",
+ "Caption",
+ "Illustration",
+ "Table",
+ "Text",
+ "Figure", // RES_POOLCOLL_LABEL_FIGURE
+ "Frame contents",
+ "Footnote",
+ "Addressee",
+ "Sender",
+ "Endnote",
+ "Drawing", // RES_POOLCOLL_LABEL_DRAWING
+ };
+ return s_aExtraProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetRegisterProgNameArray()
+{
+ static const std::vector<OUString> s_aRegisterProgNameArray = {
+ "Index", // STR_POCO_PRGM_REGISTER_BASE
+ "Index Heading", // STR_POCO_PRGM_TOX_IDXH
+ "Index 1",
+ "Index 2",
+ "Index 3",
+ "Index Separator",
+ "Contents Heading",
+ "Contents 1",
+ "Contents 2",
+ "Contents 3",
+ "Contents 4",
+ "Contents 5",
+ "User Index Heading",
+ "User Index 1",
+ "User Index 2",
+ "User Index 3",
+ "User Index 4",
+ "User Index 5",
+ "Contents 6",
+ "Contents 7",
+ "Contents 8",
+ "Contents 9",
+ "Contents 10",
+ "Figure Index Heading",
+ "Figure Index 1",
+ "Object index heading",
+ "Object index 1",
+ "Table index heading",
+ "Table index 1",
+ "Bibliography Heading",
+ "Bibliography 1",
+ "User Index 6",
+ "User Index 7",
+ "User Index 8",
+ "User Index 9",
+ "User Index 10", // STR_POCO_PRGM_TOX_USER10
+ };
+ return s_aRegisterProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetDocProgNameArray()
+{
+ static const std::vector<OUString> s_aDocProgNameArray = {
+ "Title", // STR_POCO_PRGM_DOC_TITLE
+ "Subtitle",
+ "Appendix",
+ };
+ return s_aDocProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLProgNameArray()
+{
+ static const std::vector<OUString> s_aHTMLProgNameArray = {
+ "Quotations",
+ "Preformatted Text",
+ "Horizontal Line",
+ "List Contents",
+ "List Heading", // STR_POCO_PRGM_HTML_DT
+ };
+ return s_aHTMLProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetFrameFormatProgNameArray()
+{
+ static const std::vector<OUString> s_aFrameFormatProgNameArray = {
+ "Frame", // RES_POOLFRM_FRAME
+ "Graphics",
+ "OLE",
+ "Formula",
+ "Marginalia",
+ "Watermark",
+ "Labels", // RES_POOLFRM_LABEL
+ };
+ return s_aFrameFormatProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetChrFormatProgNameArray()
+{
+ static const std::vector<OUString> s_aChrFormatProgNameArray = {
+ "Footnote Symbol", // RES_POOLCHR_FOOTNOTE
+ "Page Number",
+ "Caption characters",
+ "Drop Caps",
+ "Numbering Symbols",
+ "Bullet Symbols",
+ "Internet link",
+ "Visited Internet Link",
+ "Placeholder",
+ "Index Link",
+ "Endnote Symbol",
+ "Line numbering",
+ "Main index entry",
+ "Footnote anchor",
+ "Endnote anchor",
+ "Rubies", // RES_POOLCHR_RUBYTEXT
+ "Vertical Numbering Symbols", // RES_POOLCHR_VERT_NUMBER
+ };
+ return s_aChrFormatProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetHTMLChrFormatProgNameArray()
+{
+ static const std::vector<OUString> s_aHTMLChrFormatProgNameArray = {
+ "Emphasis", // RES_POOLCHR_HTML_EMPHASIS
+ "Citation",
+ "Strong Emphasis",
+ "Source Text",
+ "Example",
+ "User Entry",
+ "Variable",
+ "Definition",
+ "Teletype", // RES_POOLCHR_HTML_TELETYPE
+ };
+ return s_aHTMLChrFormatProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetPageDescProgNameArray()
+{
+ static const std::vector<OUString> s_aPageDescProgNameArray = {
+ "Standard", // STR_POOLPAGE_PRGM_STANDARD
+ "First Page",
+ "Left Page",
+ "Right Page",
+ "Envelope",
+ "Index",
+ "HTML",
+ "Footnote",
+ "Endnote", // STR_POOLPAGE_PRGM_ENDNOTE
+ "Landscape",
+ };
+ return s_aPageDescProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetNumRuleProgNameArray()
+{
+ static const std::vector<OUString> s_aNumRuleProgNameArray = {
+ "No List",
+ "Numbering 123", // STR_POOLNUMRULE_PRGM_NUM1
+ "Numbering ABC",
+ "Numbering abc",
+ "Numbering IVX",
+ "Numbering ivx",
+ "List 1",
+ "List 2",
+ "List 3",
+ "List 4",
+ "List 5", // STR_POOLNUMRULE_PRGM_BUL5
+ };
+ return s_aNumRuleProgNameArray;
+}
+
+const std::vector<OUString>& SwStyleNameMapper::GetTableStyleProgNameArray()
+{
+ // XXX MUST match the entries of STR_TABSTYLE_ARY in
+ // sw/source/core/doc/DocumentStylePoolManager.cxx and MUST match the order of
+ // RES_POOL_TABLESTYLE_TYPE in sw/inc/poolfmt.hxx
+ static const std::vector<OUString> s_aTableStyleProgNameArray = {
+ "Default Style", // RES_POOLTABLESTYLE_DEFAULT
+ "3D", // RES_POOLTABLESTYLE_3D
+ "Black 1", // RES_POOLTABLESTYLE_BLACK1
+ "Black 2", // RES_POOLTABLESTYLE_BLACK2
+ "Blue", // RES_POOLTABLESTYLE_BLUE
+ "Brown", // RES_POOLTABLESTYLE_BROWN
+ "Currency", // RES_POOLTABLESTYLE_CURRENCY
+ "Currency 3D", // RES_POOLTABLESTYLE_CURRENCY_3D
+ "Currency Gray", // RES_POOLTABLESTYLE_CURRENCY_GRAY
+ "Currency Lavender", // RES_POOLTABLESTYLE_CURRENCY_LAVENDER
+ "Currency Turquoise", // RES_POOLTABLESTYLE_CURRENCY_TURQUOISE
+ "Gray", // RES_POOLTABLESTYLE_GRAY
+ "Green", // RES_POOLTABLESTYLE_GREEN
+ "Lavender", // RES_POOLTABLESTYLE_LAVENDER
+ "Red", // RES_POOLTABLESTYLE_RED
+ "Turquoise", // RES_POOLTABLESTYLE_TURQUOISE
+ "Yellow", // RES_POOLTABLESTYLE_YELLOW
+ "Academic", // RES_POOLTABLESTYLE_LO6_ACADEMIC
+ "Box List Blue", // RES_POOLTABLESTYLE_LO6_BOX_LIST_BLUE
+ "Box List Green", // RES_POOLTABLESTYLE_LO6_BOX_LIST_GREEN
+ "Box List Red", // RES_POOLTABLESTYLE_LO6_BOX_LIST_RED
+ "Box List Yellow", // RES_POOLTABLESTYLE_LO6_BOX_LIST_YELLOW
+ "Elegant", // RES_POOLTABLESTYLE_LO6_ELEGANT
+ "Financial", // RES_POOLTABLESTYLE_LO6_FINANCIAL
+ "Simple Grid Columns", // RES_POOLTABLESTYLE_LO6_SIMPLE_GRID_COLUMNS
+ "Simple Grid Rows", // RES_POOLTABLESTYLE_LO6_SIMPLE_GRID_ROWS
+ "Simple List Shaded", // RES_POOLTABLESTYLE_LO6_SIMPLE_LIST_SHADED
+ };
+ return s_aTableStyleProgNameArray;
+}
+
+/// returns an empty array because Cell Names aren't translated
+const std::vector<OUString>& SwStyleNameMapper::GetCellStyleProgNameArray()
+{
+ static const std::vector<OUString> s_aCellStyleProgNameArray;
+ return s_aCellStyleProgNameArray;
+}
+
+const OUString &
+SwStyleNameMapper::GetSpecialExtraProgName(const OUString& rExtraUIName)
+{
+ return lcl_GetSpecialExtraName( rExtraUIName, true );
+}
+
+const OUString &
+SwStyleNameMapper::GetSpecialExtraUIName(const OUString& rExtraProgName)
+{
+ return lcl_GetSpecialExtraName( rExtraProgName, false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/acmplwrd.cxx b/sw/source/core/doc/acmplwrd.cxx
new file mode 100644
index 000000000..65eb9193b
--- /dev/null
+++ b/sw/source/core/doc/acmplwrd.cxx
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tools/urlobj.hxx>
+#include <hintids.hxx>
+#include <acmplwrd.hxx>
+#include <doc.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <calbck.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <editeng/svxacorr.hxx>
+#include <osl/diagnose.h>
+
+#include <editeng/acorrcfg.hxx>
+#include <sfx2/docfile.hxx>
+#include <docsh.hxx>
+
+#include <cassert>
+#include <vector>
+
+class SwAutoCompleteClient : public SwClient
+{
+ SwAutoCompleteWord* m_pAutoCompleteWord;
+ SwDoc* m_pDoc;
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong s_nSwAutoCompleteClientCount;
+#endif
+public:
+ SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc);
+ SwAutoCompleteClient(const SwAutoCompleteClient& rClient);
+ virtual ~SwAutoCompleteClient() override;
+
+ SwAutoCompleteClient& operator=(const SwAutoCompleteClient& rClient);
+
+ const SwDoc& GetDoc() const {return *m_pDoc;}
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong GetElementCount() {return s_nSwAutoCompleteClientCount;}
+#endif
+protected:
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+};
+
+class SwAutoCompleteWord_Impl
+{
+ std::vector<SwAutoCompleteClient>
+ m_aClientVector;
+ SwAutoCompleteWord& m_rAutoCompleteWord;
+public:
+ explicit SwAutoCompleteWord_Impl(SwAutoCompleteWord& rParent) :
+ m_rAutoCompleteWord(rParent){}
+ void AddDocument(SwDoc& rDoc);
+ void RemoveDocument(const SwDoc& rDoc);
+};
+
+class SwAutoCompleteString
+ : public editeng::IAutoCompleteString
+{
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong s_nSwAutoCompleteStringCount;
+#endif
+ std::vector<const SwDoc*> m_aSourceDocs;
+ public:
+ SwAutoCompleteString(const OUString& rStr, sal_Int32 nLen);
+
+ virtual ~SwAutoCompleteString() override;
+ void AddDocument(const SwDoc& rDoc);
+ //returns true if last document reference has been removed
+ bool RemoveDocument(const SwDoc& rDoc);
+#if OSL_DEBUG_LEVEL > 0
+ static sal_uLong GetElementCount() {return s_nSwAutoCompleteStringCount;}
+#endif
+};
+#if OSL_DEBUG_LEVEL > 0
+ sal_uLong SwAutoCompleteClient::s_nSwAutoCompleteClientCount = 0;
+ sal_uLong SwAutoCompleteString::s_nSwAutoCompleteStringCount = 0;
+#endif
+
+SwAutoCompleteClient::SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc) :
+ m_pAutoCompleteWord(&rToTell),
+ m_pDoc(&rSwDoc)
+{
+ m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this);
+#if OSL_DEBUG_LEVEL > 0
+ ++s_nSwAutoCompleteClientCount;
+#endif
+}
+
+SwAutoCompleteClient::SwAutoCompleteClient(const SwAutoCompleteClient& rClient) :
+ SwClient(),
+ m_pAutoCompleteWord(rClient.m_pAutoCompleteWord),
+ m_pDoc(rClient.m_pDoc)
+{
+ m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this);
+#if OSL_DEBUG_LEVEL > 0
+ ++s_nSwAutoCompleteClientCount;
+#endif
+}
+
+SwAutoCompleteClient::~SwAutoCompleteClient()
+{
+#if OSL_DEBUG_LEVEL > 0
+ --s_nSwAutoCompleteClientCount;
+#else
+ (void) this;
+#endif
+}
+
+SwAutoCompleteClient& SwAutoCompleteClient::operator=(const SwAutoCompleteClient& rClient)
+{
+ m_pAutoCompleteWord = rClient.m_pAutoCompleteWord;
+ m_pDoc = rClient.m_pDoc;
+ StartListeningToSameModifyAs(rClient);
+ return *this;
+}
+
+void SwAutoCompleteClient::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ switch(pLegacy->GetWhich())
+ {
+ case RES_REMOVE_UNO_OBJECT:
+ case RES_OBJECTDYING:
+ EndListeningAll();
+ m_pAutoCompleteWord->DocumentDying(*m_pDoc);
+ }
+}
+
+void SwAutoCompleteWord_Impl::AddDocument(SwDoc& rDoc)
+{
+ if (std::any_of(m_aClientVector.begin(), m_aClientVector.end(),
+ [&rDoc](SwAutoCompleteClient& rClient) { return &rClient.GetDoc() == &rDoc; }))
+ return;
+ m_aClientVector.emplace_back(m_rAutoCompleteWord, rDoc);
+}
+
+void SwAutoCompleteWord_Impl::RemoveDocument(const SwDoc& rDoc)
+{
+ auto aIt = std::find_if(m_aClientVector.begin(), m_aClientVector.end(),
+ [&rDoc](SwAutoCompleteClient& rClient) { return &rClient.GetDoc() == &rDoc; });
+ if (aIt != m_aClientVector.end())
+ m_aClientVector.erase(aIt);
+}
+
+SwAutoCompleteString::SwAutoCompleteString(
+ const OUString& rStr, sal_Int32 const nLen)
+ : editeng::IAutoCompleteString(rStr.copy(0, nLen))
+{
+#if OSL_DEBUG_LEVEL > 0
+ ++s_nSwAutoCompleteStringCount;
+#endif
+}
+
+SwAutoCompleteString::~SwAutoCompleteString()
+{
+#if OSL_DEBUG_LEVEL > 0
+ --s_nSwAutoCompleteStringCount;
+#else
+ (void) this;
+#endif
+}
+
+void SwAutoCompleteString::AddDocument(const SwDoc& rDoc)
+{
+ auto aIt = std::find(m_aSourceDocs.begin(), m_aSourceDocs.end(), &rDoc);
+ if (aIt != m_aSourceDocs.end())
+ return;
+ m_aSourceDocs.push_back(&rDoc);
+}
+
+bool SwAutoCompleteString::RemoveDocument(const SwDoc& rDoc)
+{
+ auto aIt = std::find(m_aSourceDocs.begin(), m_aSourceDocs.end(), &rDoc);
+ if (aIt != m_aSourceDocs.end())
+ {
+ m_aSourceDocs.erase(aIt);
+ return m_aSourceDocs.empty();
+ }
+ return false;
+}
+
+SwAutoCompleteWord::SwAutoCompleteWord(
+ editeng::SortedAutoCompleteStrings::size_type nWords, sal_uInt16 nMWrdLen ):
+ m_pImpl(new SwAutoCompleteWord_Impl(*this)),
+ m_nMaxCount( nWords ),
+ m_nMinWordLen( nMWrdLen ),
+ m_bLockWordList( false )
+{
+}
+
+SwAutoCompleteWord::~SwAutoCompleteWord()
+{
+ m_WordList.DeleteAndDestroyAll(); // so the assertion below works
+#if OSL_DEBUG_LEVEL > 0
+ sal_uLong nStrings = SwAutoCompleteString::GetElementCount();
+ sal_uLong nClients = SwAutoCompleteClient::GetElementCount();
+ OSL_ENSURE(!nStrings && !nClients, "AutoComplete: clients or string count mismatch");
+#endif
+}
+
+bool SwAutoCompleteWord::InsertWord( const OUString& rWord, SwDoc& rDoc )
+{
+ SwDocShell* pDocShell = rDoc.GetDocShell();
+ SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : nullptr;
+ // strings from help module should not be added
+ if( pMedium )
+ {
+ const INetURLObject& rURL = pMedium->GetURLObject();
+ if ( rURL.GetProtocol() == INetProtocol::VndSunStarHelp )
+ return false;
+ }
+
+ OUString aNewWord = rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), "")
+ .replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "");
+
+ m_pImpl->AddDocument(rDoc);
+ bool bRet = false;
+ sal_Int32 nWrdLen = aNewWord.getLength();
+ while( nWrdLen && '.' == aNewWord[ nWrdLen-1 ])
+ --nWrdLen;
+
+ if( !m_bLockWordList && nWrdLen >= m_nMinWordLen )
+ {
+ SwAutoCompleteString* pNew = new SwAutoCompleteString( aNewWord, nWrdLen );
+ pNew->AddDocument(rDoc);
+ std::pair<editeng::SortedAutoCompleteStrings::const_iterator, bool>
+ aInsPair = m_WordList.insert(pNew);
+
+ m_LookupTree.insert( aNewWord.copy(0, nWrdLen) );
+
+ if (aInsPair.second)
+ {
+ bRet = true;
+ if (m_aLRUList.size() >= m_nMaxCount)
+ {
+ // the last one needs to be removed
+ // so that there is space for the first one
+ SwAutoCompleteString* pDel = m_aLRUList.back();
+ m_aLRUList.pop_back();
+ m_WordList.erase(pDel);
+ delete pDel;
+ }
+ m_aLRUList.push_front(pNew);
+ }
+ else
+ {
+ delete pNew;
+ // then move "up"
+ pNew = static_cast<SwAutoCompleteString*>(*aInsPair.first);
+
+ // add the document to the already inserted string
+ pNew->AddDocument(rDoc);
+
+ // move pNew to the front of the LRU list
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pNew );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ if ( m_aLRUList.begin() != it && m_aLRUList.end() != it )
+ {
+ m_aLRUList.erase( it );
+ m_aLRUList.push_front( pNew );
+ }
+ }
+ }
+ return bRet;
+}
+
+void SwAutoCompleteWord::SetMaxCount(
+ editeng::SortedAutoCompleteStrings::size_type nNewMax )
+{
+ if( nNewMax < m_nMaxCount && m_aLRUList.size() > nNewMax )
+ {
+ // remove the trailing ones
+ SwAutoCompleteStringPtrDeque::size_type nLRUIndex = nNewMax-1;
+ while (nNewMax < m_WordList.size() && nLRUIndex < m_aLRUList.size())
+ {
+ auto it = m_WordList.find(m_aLRUList[nLRUIndex++]);
+ OSL_ENSURE( m_WordList.end() != it, "String not found" );
+ editeng::IAutoCompleteString *const pDel = *it;
+ m_WordList.erase(it);
+ delete pDel;
+ }
+ m_aLRUList.erase( m_aLRUList.begin() + nNewMax - 1, m_aLRUList.end() );
+ }
+ m_nMaxCount = nNewMax;
+}
+
+void SwAutoCompleteWord::SetMinWordLen( sal_uInt16 n )
+{
+ // Do you really want to remove all words that are less than the minWrdLen?
+ if( n < m_nMinWordLen )
+ {
+ for (size_t nPos = 0; nPos < m_WordList.size(); ++nPos)
+ if (m_WordList[ nPos ]->GetAutoCompleteString().getLength() < n)
+ {
+ SwAutoCompleteString *const pDel =
+ dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos]);
+ m_WordList.erase_at(nPos);
+
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ m_aLRUList.erase( it );
+ --nPos;
+ delete pDel;
+ }
+ }
+
+ m_nMinWordLen = n;
+}
+
+/** Return all words matching a given prefix
+ *
+ * @param aMatch the prefix to search for
+ * @param rWords the words found matching
+ */
+bool SwAutoCompleteWord::GetWordsMatching(const OUString& aMatch, std::vector<OUString>& rWords) const
+{
+ assert(rWords.empty());
+ m_LookupTree.findSuggestions(aMatch, rWords);
+ return !rWords.empty();
+}
+
+void SwAutoCompleteWord::CheckChangedList(
+ const editeng::SortedAutoCompleteStrings& rNewLst)
+{
+ size_t nMyLen = m_WordList.size(), nNewLen = rNewLst.size();
+ size_t nMyPos = 0, nNewPos = 0;
+
+ for( ; nMyPos < nMyLen && nNewPos < nNewLen; ++nMyPos, ++nNewPos )
+ {
+ const editeng::IAutoCompleteString * pStr = rNewLst[ nNewPos ];
+ while (m_WordList[nMyPos] != pStr)
+ {
+ SwAutoCompleteString *const pDel =
+ dynamic_cast<SwAutoCompleteString*>(m_WordList[nMyPos]);
+ m_WordList.erase_at(nMyPos);
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ m_aLRUList.erase( it );
+ delete pDel;
+ if( nMyPos >= --nMyLen )
+ break;
+ }
+ }
+ // remove the elements at the end of the array
+ if( nMyPos >= nMyLen )
+ return;
+
+ // clear LRU array first then delete the string object
+ for( ; nNewPos < nMyLen; ++nNewPos )
+ {
+ SwAutoCompleteString *const pDel =
+ dynamic_cast<SwAutoCompleteString*>(m_WordList[nNewPos]);
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
+ OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
+ m_aLRUList.erase( it );
+ delete pDel;
+ }
+ // remove from array
+ m_WordList.erase(m_WordList.begin() + nMyPos,
+ m_WordList.begin() + nMyLen);
+}
+
+void SwAutoCompleteWord::DocumentDying(const SwDoc& rDoc)
+{
+ m_pImpl->RemoveDocument(rDoc);
+
+ SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ const bool bDelete = !pACorr->GetSwFlags().bAutoCmpltKeepList;
+ for (size_t nPos = m_WordList.size(); nPos; nPos--)
+ {
+ SwAutoCompleteString *const pCurrent = dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos - 1]);
+ if(pCurrent && pCurrent->RemoveDocument(rDoc) && bDelete)
+ {
+ m_WordList.erase_at(nPos - 1);
+ SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pCurrent );
+ OSL_ENSURE( m_aLRUList.end() != it, "word not found in LRU list" );
+ m_aLRUList.erase( it );
+ delete pCurrent;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/dbgoutsw.cxx b/sw/source/core/doc/dbgoutsw.cxx
new file mode 100644
index 000000000..20d6828f8
--- /dev/null
+++ b/sw/source/core/doc/dbgoutsw.cxx
@@ -0,0 +1,837 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifdef DBG_UTIL
+
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/itemiter.hxx>
+#include <map>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <ndhints.hxx>
+#include <txatbase.hxx>
+#include <pam.hxx>
+#include <docary.hxx>
+#include <undobj.hxx>
+#include <numrule.hxx>
+#include <doc.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <swrect.hxx>
+#include <ndarr.hxx>
+#include <paratr.hxx>
+#include <SwNodeNum.hxx>
+#include <dbgoutsw.hxx>
+#include <frameformats.hxx>
+#include <cstdio>
+
+static OString aDbgOutResult;
+bool bDbgOutStdErr = false;
+bool bDbgOutPrintAttrSet = false;
+
+template<class T>
+static OUString lcl_dbg_out_SvPtrArr(const T & rArr)
+{
+ OUStringBuffer aStr("[ ");
+
+ for (typename T::const_iterator i(rArr.begin()); i != rArr.end(); ++i)
+ {
+ if (i != rArr.begin())
+ aStr.append(", ");
+
+ if (*i)
+ aStr.append(lcl_dbg_out(**i));
+ else
+ aStr.append("(null)");
+ }
+
+ aStr.append(" ]");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const void * pVoid)
+{
+ char sBuffer[1024];
+
+ sprintf(sBuffer, "%p", pVoid);
+
+ OUString aTmpStr(sBuffer, strlen(sBuffer), RTL_TEXTENCODING_ASCII_US);
+
+ return dbg_out(aTmpStr);
+}
+
+const char * dbg_out(std::u16string_view aStr)
+{
+ aDbgOutResult = OUStringToOString(aStr, RTL_TEXTENCODING_ASCII_US);
+
+ if (bDbgOutStdErr)
+ fprintf(stderr, "%s", aDbgOutResult.getStr());
+
+ return aDbgOutResult.getStr();
+}
+
+static std::map<sal_uInt16,OUString> & GetItemWhichMap()
+{
+ static std::map<sal_uInt16,OUString> aItemWhichMap
+ {
+ { RES_CHRATR_CASEMAP , "CHRATR_CASEMAP" },
+ { RES_CHRATR_CHARSETCOLOR , "CHRATR_CHARSETCOLOR" },
+ { RES_CHRATR_COLOR , "CHRATR_COLOR" },
+ { RES_CHRATR_CONTOUR , "CHRATR_CONTOUR" },
+ { RES_CHRATR_CROSSEDOUT , "CHRATR_CROSSEDOUT" },
+ { RES_CHRATR_ESCAPEMENT , "CHRATR_ESCAPEMENT" },
+ { RES_CHRATR_FONT , "CHRATR_FONT" },
+ { RES_CHRATR_FONTSIZE , "CHRATR_FONTSIZE" },
+ { RES_CHRATR_KERNING , "CHRATR_KERNING" },
+ { RES_CHRATR_LANGUAGE , "CHRATR_LANGUAGE" },
+ { RES_CHRATR_POSTURE , "CHRATR_POSTURE" },
+ { RES_CHRATR_SHADOWED , "CHRATR_SHADOWED" },
+ { RES_CHRATR_UNDERLINE , "CHRATR_UNDERLINE" },
+ { RES_CHRATR_OVERLINE , "CHRATR_OVERLINE" },
+ { RES_CHRATR_WEIGHT , "CHRATR_WEIGHT" },
+ { RES_CHRATR_WORDLINEMODE , "CHRATR_WORDLINEMODE" },
+ { RES_CHRATR_AUTOKERN , "CHRATR_AUTOKERN" },
+ { RES_CHRATR_BLINK , "CHRATR_BLINK" },
+ { RES_CHRATR_NOHYPHEN , "CHRATR_NOHYPHEN" },
+ { RES_CHRATR_BACKGROUND , "CHRATR_BACKGROUND" },
+ { RES_CHRATR_HIGHLIGHT , "CHRATR_HIGHLIGHT" },
+ { RES_CHRATR_CJK_FONT , "CHRATR_CJK_FONT" },
+ { RES_CHRATR_CJK_FONTSIZE , "CHRATR_CJK_FONTSIZE" },
+ { RES_CHRATR_CJK_LANGUAGE , "CHRATR_CJK_LANGUAGE" },
+ { RES_CHRATR_CJK_POSTURE , "CHRATR_CJK_POSTURE" },
+ { RES_CHRATR_CJK_WEIGHT , "CHRATR_CJK_WEIGHT" },
+ { RES_CHRATR_CTL_FONT , "CHRATR_CTL_FONT" },
+ { RES_CHRATR_CTL_FONTSIZE , "CHRATR_CTL_FONTSIZE" },
+ { RES_CHRATR_CTL_LANGUAGE , "CHRATR_CTL_LANGUAGE" },
+ { RES_CHRATR_CTL_POSTURE , "CHRATR_CTL_POSTURE" },
+ { RES_CHRATR_CTL_WEIGHT , "CHRATR_CTL_WEIGHT" },
+ { RES_CHRATR_ROTATE , "CHRATR_ROTATE" },
+ { RES_CHRATR_EMPHASIS_MARK , "CHRATR_EMPHASIS_MARK" },
+ { RES_CHRATR_TWO_LINES , "CHRATR_TWO_LINES" },
+ { RES_CHRATR_SCALEW , "CHRATR_SCALEW" },
+ { RES_CHRATR_RELIEF , "CHRATR_RELIEF" },
+ { RES_CHRATR_HIDDEN , "CHRATR_HIDDEN" },
+ { RES_CHRATR_BOX , "CHRATR_BOX" },
+ { RES_CHRATR_SHADOW , "CHRATR_SHADOW" },
+ { RES_TXTATR_AUTOFMT , "TXTATR_AUTOFMT" },
+ { RES_TXTATR_INETFMT , "TXTATR_INETFMT" },
+ { RES_TXTATR_REFMARK , "TXTATR_REFMARK" },
+ { RES_TXTATR_TOXMARK , "TXTATR_TOXMARK" },
+ { RES_TXTATR_CHARFMT , "TXTATR_CHARFMT" },
+ { RES_TXTATR_INPUTFIELD , "RES_TXTATR_INPUTFIELD" },
+ { RES_TXTATR_CONTENTCONTROL , "RES_TXTATR_CONTENTCONTROL" },
+ { RES_TXTATR_CJK_RUBY , "TXTATR_CJK_RUBY" },
+ { RES_TXTATR_UNKNOWN_CONTAINER , "TXTATR_UNKNOWN_CONTAINER" },
+ { RES_TXTATR_META , "TXTATR_META" },
+ { RES_TXTATR_METAFIELD , "TXTATR_METAFIELD" },
+ { RES_TXTATR_FIELD , "TXTATR_FIELD" },
+ { RES_TXTATR_FLYCNT , "TXTATR_FLYCNT" },
+ { RES_TXTATR_FTN , "TXTATR_FTN" },
+ { RES_TXTATR_ANNOTATION , "TXTATR_ANNOTATION" },
+ { RES_TXTATR_LINEBREAK , "RES_TXTATR_LINEBREAK" },
+ { RES_TXTATR_DUMMY1 , "TXTATR_DUMMY1" },
+ { RES_PARATR_LINESPACING , "PARATR_LINESPACING" },
+ { RES_PARATR_ADJUST , "PARATR_ADJUST" },
+ { RES_PARATR_SPLIT , "PARATR_SPLIT" },
+ { RES_PARATR_ORPHANS , "PARATR_ORPHANS" },
+ { RES_PARATR_WIDOWS , "PARATR_WIDOWS" },
+ { RES_PARATR_TABSTOP , "PARATR_TABSTOP" },
+ { RES_PARATR_HYPHENZONE , "PARATR_HYPHENZONE" },
+ { RES_PARATR_DROP , "PARATR_DROP" },
+ { RES_PARATR_REGISTER , "PARATR_REGISTER" },
+ { RES_PARATR_NUMRULE , "PARATR_NUMRULE" },
+ { RES_PARATR_SCRIPTSPACE , "PARATR_SCRIPTSPACE" },
+ { RES_PARATR_HANGINGPUNCTUATION , "PARATR_HANGINGPUNCTUATION" },
+ { RES_PARATR_FORBIDDEN_RULES , "PARATR_FORBIDDEN_RULES" },
+ { RES_PARATR_VERTALIGN , "PARATR_VERTALIGN" },
+ { RES_PARATR_SNAPTOGRID , "PARATR_SNAPTOGRID" },
+ { RES_PARATR_CONNECT_BORDER , "PARATR_CONNECT_BORDER" },
+ { RES_FILL_ORDER , "FILL_ORDER" },
+ { RES_FRM_SIZE , "FRM_SIZE" },
+ { RES_PAPER_BIN , "PAPER_BIN" },
+ { RES_LR_SPACE , "LR_SPACE" },
+ { RES_UL_SPACE , "UL_SPACE" },
+ { RES_PAGEDESC , "PAGEDESC" },
+ { RES_BREAK , "BREAK" },
+ { RES_CNTNT , "CNTNT" },
+ { RES_HEADER , "HEADER" },
+ { RES_FOOTER , "FOOTER" },
+ { RES_PRINT , "PRINT" },
+ { RES_OPAQUE , "OPAQUE" },
+ { RES_PROTECT , "PROTECT" },
+ { RES_SURROUND , "SURROUND" },
+ { RES_VERT_ORIENT , "VERT_ORIENT" },
+ { RES_HORI_ORIENT , "HORI_ORIENT" },
+ { RES_ANCHOR , "ANCHOR" },
+ { RES_BACKGROUND , "BACKGROUND" },
+ { RES_BOX , "BOX" },
+ { RES_SHADOW , "SHADOW" },
+ { RES_FRMMACRO , "FRMMACRO" },
+ { RES_COL , "COL" },
+ { RES_KEEP , "KEEP" },
+ { RES_URL , "URL" },
+ { RES_EDIT_IN_READONLY , "EDIT_IN_READONLY" },
+ { RES_LAYOUT_SPLIT , "LAYOUT_SPLIT" },
+ { RES_CHAIN , "CHAIN" },
+ { RES_TEXTGRID , "TEXTGRID" },
+ { RES_LINENUMBER , "LINENUMBER" },
+ { RES_FTN_AT_TXTEND , "FTN_AT_TXTEND" },
+ { RES_END_AT_TXTEND , "END_AT_TXTEND" },
+ { RES_COLUMNBALANCE , "COLUMNBALANCE" },
+ { RES_FRAMEDIR , "FRAMEDIR" },
+ { RES_HEADER_FOOTER_EAT_SPACING , "HEADER_FOOTER_EAT_SPACING" },
+ { RES_ROW_SPLIT , "ROW_SPLIT" },
+ { RES_GRFATR_MIRRORGRF , "GRFATR_MIRRORGRF" },
+ { RES_GRFATR_CROPGRF , "GRFATR_CROPGRF" },
+ { RES_GRFATR_ROTATION , "GRFATR_ROTATION" },
+ { RES_GRFATR_LUMINANCE , "GRFATR_LUMINANCE" },
+ { RES_GRFATR_CONTRAST , "GRFATR_CONTRAST" },
+ { RES_GRFATR_CHANNELR , "GRFATR_CHANNELR" },
+ { RES_GRFATR_CHANNELG , "GRFATR_CHANNELG" },
+ { RES_GRFATR_CHANNELB , "GRFATR_CHANNELB" },
+ { RES_GRFATR_GAMMA , "GRFATR_GAMMA" },
+ { RES_GRFATR_INVERT , "GRFATR_INVERT" },
+ { RES_GRFATR_TRANSPARENCY , "GRFATR_TRANSPARENCY" },
+ { RES_GRFATR_DRAWMODE , "GRFATR_DRAWMODE" },
+ { RES_BOXATR_FORMAT , "BOXATR_FORMAT" },
+ { RES_BOXATR_FORMULA , "BOXATR_FORMULA" },
+ { RES_BOXATR_VALUE , "BOXATR_VALUE" },
+ };
+
+ return aItemWhichMap;
+}
+
+static OUString lcl_dbg_out(const SfxPoolItem & rItem)
+{
+ OUString aStr("[ ");
+
+ if (GetItemWhichMap().find(rItem.Which()) != GetItemWhichMap().end())
+ aStr += GetItemWhichMap()[rItem.Which()];
+ else
+ aStr += OUString::number(rItem.Which());
+
+ aStr += " ]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SfxPoolItem & rItem)
+{
+ return dbg_out(lcl_dbg_out(rItem));
+}
+
+const char * dbg_out(const SfxPoolItem * pItem)
+{
+ return dbg_out(pItem ? lcl_dbg_out(*pItem) : OUString("(nil)"));
+}
+
+static OUString lcl_dbg_out(const SfxItemSet & rSet)
+{
+ SfxItemIter aIter(rSet);
+ bool bFirst = true;
+ OUStringBuffer aStr = "[ ";
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (!bFirst)
+ aStr.append(", ");
+
+ if (reinterpret_cast<sal_uIntPtr>(pItem) != SAL_MAX_SIZE)
+ aStr.append(lcl_dbg_out(*pItem));
+ else
+ aStr.append("invalid");
+
+ bFirst = false;
+ }
+
+ aStr.append(" ]");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const SfxItemSet & rSet)
+{
+ return dbg_out(lcl_dbg_out(rSet));
+}
+
+static OUString lcl_dbg_out(const SwTextAttr & rAttr)
+{
+ OUString aStr =
+ "[ " +
+ OUString::number(rAttr.GetStart()) +
+ "->" +
+ OUString::number(*rAttr.End()) +
+ " " +
+ lcl_dbg_out(rAttr.GetAttr()) +
+ " ]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwTextAttr & rAttr)
+{
+ return dbg_out(lcl_dbg_out(rAttr));
+}
+
+static OUString lcl_dbg_out(const SwpHints & rHints)
+{
+ OUStringBuffer aStr("[ SwpHints\n");
+
+ for (size_t i = 0; i < rHints.Count(); ++i)
+ {
+ aStr.append(" ");
+ aStr.append(lcl_dbg_out(*rHints.Get(i)));
+ aStr.append("\n");
+ }
+
+ aStr.append("]\n");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const SwpHints &rHints)
+{
+ return dbg_out(lcl_dbg_out(rHints));
+}
+
+static OUString lcl_dbg_out(const SwPosition & rPos)
+{
+ OUString aStr =
+ "( " +
+ OUString::number(sal_Int32(rPos.nNode.GetIndex())) +
+ ", " +
+ OUString::number(rPos.nContent.GetIndex()) +
+ ": " +
+ OUString::number(reinterpret_cast<sal_IntPtr>(rPos.nContent.GetIdxReg()), 16) +
+ " )";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwPosition & rPos)
+{
+ return dbg_out(lcl_dbg_out(rPos));
+}
+
+static OUString lcl_dbg_out(const SwPaM & rPam)
+{
+ OUString aStr =
+ "[ Pt: " +
+ lcl_dbg_out(*rPam.GetPoint());
+
+ if (rPam.HasMark())
+ {
+ aStr += ", Mk: " + lcl_dbg_out(*rPam.GetMark());
+ }
+
+ aStr += " ]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwPaM & rPam)
+{
+ return dbg_out(lcl_dbg_out(rPam));
+}
+
+static OUString lcl_dbg_out(const SwNodeNum & )
+{
+ return OUString();/*rNum.ToString();*/
+}
+
+const char * dbg_out(const SwNodeNum & rNum)
+{
+ return dbg_out(lcl_dbg_out(rNum));
+}
+
+static OUString lcl_dbg_out(const SwRect & rRect)
+{
+ OUString aResult =
+ "[ [" +
+ OUString::number(rRect.Left()) +
+ ", " +
+ OUString::number(rRect.Top()) +
+ "], [" +
+ OUString::number(rRect.Right()) +
+ ", " +
+ OUString::number(rRect.Bottom()) +
+ "] ]";
+
+ return aResult;
+}
+
+const char * dbg_out(const SwRect & rRect)
+{
+ return dbg_out(lcl_dbg_out(rRect));
+}
+
+static OUString lcl_dbg_out(const SwFrameFormat & rFrameFormat)
+{
+ char sBuffer[256];
+ sprintf(sBuffer, "%p", &rFrameFormat);
+
+ OUString aResult = "[ " +
+ OUString(sBuffer, strlen(sBuffer), RTL_TEXTENCODING_ASCII_US) +
+ "(" +
+ rFrameFormat.GetName() + ")";
+
+ if (rFrameFormat.IsAuto())
+ aResult += "*";
+
+ aResult += " ," + lcl_dbg_out(rFrameFormat.FindLayoutRect()) + " ]";
+
+ return aResult;
+}
+
+const char * dbg_out(const SwFrameFormat & rFrameFormat)
+{
+ return dbg_out(lcl_dbg_out(rFrameFormat));
+}
+
+static OUString lcl_AnchoredFrames(const SwNode & rNode)
+{
+ OUStringBuffer aResult("[");
+
+ const SwDoc& rDoc = rNode.GetDoc();
+ const SwFrameFormats * pFrameFormats = rDoc.GetSpzFrameFormats();
+
+ if (pFrameFormats)
+ {
+ bool bFirst = true;
+ for (SwFrameFormats::const_iterator i(pFrameFormats->begin());
+ i != pFrameFormats->end(); ++i)
+ {
+ const SwFormatAnchor & rAnchor = (*i)->GetAnchor();
+ const SwPosition * pPos = rAnchor.GetContentAnchor();
+
+ if (pPos && &pPos->nNode.GetNode() == &rNode)
+ {
+ if (! bFirst)
+ aResult.append(", ");
+
+ if (*i)
+ aResult.append(lcl_dbg_out(**i));
+ bFirst = false;
+ }
+ }
+ }
+
+ aResult.append("]");
+
+ return aResult.makeStringAndClear();
+}
+
+static OUString lcl_dbg_out_NumType(sal_Int16 nType)
+{
+ OUString aTmpStr;
+
+ switch (nType)
+ {
+ case SVX_NUM_NUMBER_NONE:
+ aTmpStr += " NONE";
+
+ break;
+ case SVX_NUM_CHARS_UPPER_LETTER:
+ aTmpStr += " CHARS_UPPER_LETTER";
+
+ break;
+ case SVX_NUM_CHARS_LOWER_LETTER:
+ aTmpStr += " CHARS_LOWER_LETTER";
+
+ break;
+ case SVX_NUM_ROMAN_UPPER:
+ aTmpStr += " ROMAN_UPPER";
+
+ break;
+ case SVX_NUM_ROMAN_LOWER:
+ aTmpStr += " ROMAN_LOWER";
+
+ break;
+ case SVX_NUM_ARABIC:
+ aTmpStr += " ARABIC";
+
+ break;
+ default:
+ aTmpStr += " ??";
+
+ break;
+ }
+
+ return aTmpStr;
+}
+
+static OUString lcl_dbg_out(const SwNode & rNode)
+{
+ char aBuffer[128];
+ sprintf(aBuffer, "%p", &rNode);
+
+ OUString aTmpStr = "<node "
+ "index=\"" +
+ OUString::number(sal_Int32(rNode.GetIndex())) +
+ "\""
+ " serial=\"" +
+ OUString::number(rNode.GetSerial()) +
+ "\""
+ " type=\"" +
+ OUString::number(sal_Int32( rNode.GetNodeType() ) ) +
+ "\""
+ " pointer=\"" +
+ OUString(aBuffer, strlen(aBuffer), RTL_TEXTENCODING_ASCII_US) +
+ "\">";
+
+ const SwTextNode * pTextNode = rNode.GetTextNode();
+
+ if (rNode.IsTextNode())
+ {
+ const SfxItemSet * pAttrSet = pTextNode->GetpSwAttrSet();
+
+ aTmpStr += "<txt>" + (pTextNode->GetText().getLength() > 10 ? pTextNode->GetText().copy(0, 10) : pTextNode->GetText()) + "</txt>";
+
+ if (rNode.IsTableNode())
+ aTmpStr += "<tbl/>";
+
+ aTmpStr += "<outlinelevel>" + OUString::number(pTextNode->GetAttrOutlineLevel()-1) + "</outlinelevel>";
+
+ const SwNumRule * pNumRule = pTextNode->GetNumRule();
+
+ if (pNumRule != nullptr)
+ {
+ aTmpStr += "<number>";
+ if ( pTextNode->GetNum() )
+ {
+ aTmpStr += lcl_dbg_out(*(pTextNode->GetNum()));
+ }
+ aTmpStr += "</number><rule>" +
+ pNumRule->GetName();
+
+ const SwNumRuleItem * pItem = nullptr;
+
+ if (pAttrSet &&
+ (pItem = pAttrSet->GetItemIfSet(RES_PARATR_NUMRULE, false)))
+ {
+ aTmpStr += "(" + pItem->GetValue() + ")*";
+ }
+
+ const SwNumFormat * pNumFormat = nullptr;
+ aTmpStr += "</rule>";
+
+ if (pTextNode->GetActualListLevel() > 0)
+ pNumFormat = pNumRule->GetNumFormat( static_cast< sal_uInt16 >(pTextNode->GetActualListLevel()) );
+
+ if (pNumFormat)
+ {
+ aTmpStr += "<numformat>" +
+ lcl_dbg_out_NumType(pNumFormat->GetNumberingType()) + "</numformat>";
+ }
+ }
+
+ if (pTextNode->IsCountedInList())
+ aTmpStr += "<counted/>";
+
+ SwFormatColl * pColl = pTextNode->GetFormatColl();
+
+ if (pColl)
+ {
+ aTmpStr += "<coll>" + pColl->GetName() + "(";
+
+ SwTextFormatColl *pTextColl = static_cast<SwTextFormatColl*>(pColl);
+ if (pTextColl->IsAssignedToListLevelOfOutlineStyle())
+ {
+ aTmpStr += OUString::number(pTextColl->GetAssignedOutlineStyleLevel());
+ }
+ else
+ {
+ aTmpStr += OUString::number(-1);
+ }
+
+ const SwNumRuleItem & rItem =
+ pColl->GetFormatAttr(RES_PARATR_NUMRULE);
+ const OUString& sNumruleName = rItem.GetValue();
+
+ if (!sNumruleName.isEmpty())
+ {
+ aTmpStr += ", " + sNumruleName;
+ }
+ aTmpStr += ")"
+ "</coll>";
+ }
+
+ SwFormatColl * pCColl = pTextNode->GetCondFormatColl();
+
+ if (pCColl)
+ {
+ aTmpStr += "<ccoll>" + pCColl->GetName() + "</ccoll>";
+ }
+
+ aTmpStr += "<frms>" + lcl_AnchoredFrames(rNode) + "</frms>";
+
+ if (bDbgOutPrintAttrSet)
+ {
+ aTmpStr += "<attrs>" + lcl_dbg_out(pTextNode->GetSwAttrSet()) + "</attrs>";
+ }
+ }
+ else if (rNode.IsStartNode())
+ {
+ aTmpStr += "<start end=\"";
+
+ const SwStartNode * pStartNode = dynamic_cast<const SwStartNode *> (&rNode);
+ if (pStartNode != nullptr)
+ aTmpStr += OUString::number(sal_Int32(pStartNode->EndOfSectionNode()->GetIndex()));
+
+ aTmpStr += "\"/>";
+ }
+ else if (rNode.IsEndNode())
+ aTmpStr += "<end/>";
+
+ aTmpStr += "</node>";
+
+ return aTmpStr;
+}
+
+const char * dbg_out(const SwNode & rNode)
+{
+ return dbg_out(lcl_dbg_out(rNode));
+}
+
+const char * dbg_out(const SwNode * pNode)
+{
+ if (nullptr != pNode)
+ return dbg_out(*pNode);
+ else
+ return nullptr;
+}
+
+const char * dbg_out(const SwContentNode * pNode)
+{
+ if (nullptr != pNode)
+ return dbg_out(*pNode);
+ else
+ return nullptr;
+}
+
+const char * dbg_out(const SwTextNode * pNode)
+{
+ if (nullptr != pNode)
+ return dbg_out(*pNode);
+ else
+ return nullptr;
+}
+
+static OUString lcl_dbg_out(const SwUndo & rUndo)
+{
+ return "[ " + OUString::number(static_cast<int>(rUndo.GetId()))
+ + ": " + rUndo.GetComment() + " ]";
+}
+
+const char * dbg_out(const SwUndo & rUndo)
+{
+ return dbg_out(lcl_dbg_out(rUndo));
+}
+
+static OUString lcl_dbg_out(SwOutlineNodes const & rNodes)
+{
+ OUStringBuffer aStr("[\n");
+
+ for (size_t i = 0; i < rNodes.size(); i++)
+ {
+ aStr.append(lcl_dbg_out(*rNodes[i]));
+ aStr.append("\n");
+ }
+
+ aStr.append("]\n");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out( SwOutlineNodes const & rNodes)
+{
+ return dbg_out(lcl_dbg_out(rNodes));
+}
+
+static OUString lcl_dbg_out(const SvxNumberFormat & rFormat)
+{
+ OUString aResult = lcl_dbg_out_NumType(rFormat.GetNumberingType());
+ return aResult;
+}
+
+static OUString lcl_dbg_out(const SwNumRule & rRule)
+{
+ OUStringBuffer aResult("[ ");
+
+ aResult.append(rRule.GetName());
+ aResult.append(" [");
+
+ for (sal_uInt8 n = 0; n < MAXLEVEL; n++)
+ {
+ if (n > 0)
+ aResult.append(", ");
+
+ aResult.append(lcl_dbg_out(rRule.Get(n)));
+ }
+
+ aResult.append("]");
+
+ aResult.append("]");
+
+ return aResult.makeStringAndClear();
+}
+
+const char * dbg_out(const SwNumRule & rRule)
+{
+ return dbg_out(lcl_dbg_out(rRule));
+}
+
+static OUString lcl_dbg_out(const SwTextFormatColl & rFormat)
+{
+ return rFormat.GetName() + "(" +
+ OUString::number(rFormat.GetAttrOutlineLevel()) + ")";
+}
+
+const char * dbg_out(const SwTextFormatColl & rFormat)
+{
+ return dbg_out(lcl_dbg_out(rFormat));
+}
+
+static OUString lcl_dbg_out(const SwFrameFormats & rFrameFormats)
+{
+ return lcl_dbg_out_SvPtrArr<SwFrameFormats>(rFrameFormats);
+}
+
+const char * dbg_out(const SwFrameFormats & rFrameFormats)
+{
+ return dbg_out(lcl_dbg_out(rFrameFormats));
+}
+
+static OUString lcl_dbg_out(const SwNumRuleTable & rTable)
+{
+ OUStringBuffer aResult("[");
+
+ for (size_t n = 0; n < rTable.size(); n++)
+ {
+ if (n > 0)
+ aResult.append(", ");
+
+ aResult.append(rTable[n]->GetName());
+
+ char sBuffer[256];
+ sprintf(sBuffer, "(%p)", rTable[n]);
+ aResult.appendAscii(sBuffer);
+ }
+
+ aResult.append("]");
+
+ return aResult.makeStringAndClear();
+}
+
+const char * dbg_out(const SwNumRuleTable & rTable)
+{
+ return dbg_out(lcl_dbg_out(rTable));
+}
+
+static OUString lcl_TokenType2Str(FormTokenType nType)
+{
+ switch(nType)
+ {
+ case TOKEN_ENTRY_NO:
+ return "NO";
+ case TOKEN_ENTRY_TEXT:
+ return "ENTRY_TEXT";
+ case TOKEN_ENTRY:
+ return "ENTRY";
+ case TOKEN_TAB_STOP:
+ return "TAB_STOP";
+ case TOKEN_TEXT:
+ return "TOKEN_TEXT";
+ case TOKEN_PAGE_NUMS:
+ return "NUMS";
+ case TOKEN_CHAPTER_INFO:
+ return "CHAPTER_INFO";
+ case TOKEN_LINK_START:
+ return "LINK_START";
+ case TOKEN_LINK_END:
+ return "LINK_END";
+ case TOKEN_AUTHORITY:
+ return "AUTHORITY";
+ case TOKEN_END:
+ return "END";
+ default:
+ OSL_FAIL("should not be reached");
+ return "??";
+ }
+}
+
+static OUString lcl_dbg_out(const SwFormToken & rToken)
+{
+ return rToken.GetString();
+}
+
+const char * dbg_out(const SwFormToken & rToken)
+{
+ return dbg_out(lcl_dbg_out(rToken));
+}
+
+static OUString lcl_dbg_out(const SwFormTokens & rTokens)
+{
+ OUStringBuffer aStr("[");
+
+ SwFormTokens::const_iterator aIt;
+
+ for (aIt = rTokens.begin(); aIt != rTokens.end(); ++aIt)
+ {
+ if (aIt != rTokens.begin())
+ aStr.append(", ");
+
+ aStr.append(lcl_TokenType2Str(aIt->eTokenType));
+ aStr.append(": ");
+ aStr.append(lcl_dbg_out(*aIt));
+ }
+
+ aStr.append("]");
+
+ return aStr.makeStringAndClear();
+}
+
+const char * dbg_out(const SwFormTokens & rTokens)
+{
+ return dbg_out(lcl_dbg_out(rTokens));
+}
+
+static OUString lcl_dbg_out(const SwNodeRange & rRange)
+{
+ OUString aStr =
+ "[" +
+ lcl_dbg_out(SwPosition(rRange.aStart)) +
+ ", " +
+ lcl_dbg_out(SwPosition(rRange.aEnd)) +
+ "]";
+
+ return aStr;
+}
+
+const char * dbg_out(const SwNodeRange & rRange)
+{
+ return dbg_out(lcl_dbg_out(rRange));
+}
+
+#endif // DEBUG
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doc.cxx b/sw/source/core/doc/doc.cxx
new file mode 100644
index 000000000..800c26458
--- /dev/null
+++ b/sw/source/core/doc/doc.cxx
@@ -0,0 +1,1830 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+
+#include <doc.hxx>
+#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <DocumentFieldsManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentDrawModelManager.hxx>
+#include <DocumentTimerManager.hxx>
+#include <DocumentDeviceManager.hxx>
+#include <DocumentChartDataProviderManager.hxx>
+#include <DocumentLinksAdministrationManager.hxx>
+#include <DocumentListItemsManager.hxx>
+#include <DocumentListsManager.hxx>
+#include <DocumentOutlineNodesManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <DocumentStatisticsManager.hxx>
+#include <DocumentStateManager.hxx>
+#include <DocumentStylePoolManager.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <DocumentExternalDataManager.hxx>
+#include <UndoManager.hxx>
+#include <dbmgr.hxx>
+#include <hintids.hxx>
+
+#include <comphelper/random.hxx>
+#include <tools/multisel.hxx>
+#include <rtl/ustring.hxx>
+#include <svl/poolitem.hxx>
+#include <unotools/syslocale.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/pbinitem.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <swatrset.hxx>
+#include <swmodule.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <dbfld.hxx>
+#include <txtinet.hxx>
+#include <txtrfmrk.hxx>
+#include <frmatr.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
+#include <UndoTable.hxx>
+#include <pagedesc.hxx>
+#include <doctxm.hxx>
+#include <poolfmt.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <scriptinfo.hxx>
+#include <mdiexp.hxx>
+#include <docary.hxx>
+#include <printdata.hxx>
+#include <strings.hrc>
+#include <SwUndoTOXChange.hxx>
+#include <unocrsr.hxx>
+#include <docfld.hxx>
+#include <docufld.hxx>
+#include <viewsh.hxx>
+#include <shellres.hxx>
+#include <txtfrm.hxx>
+#include <attrhint.hxx>
+
+#include <vector>
+#include <map>
+#include <osl/diagnose.h>
+#include <osl/interlck.h>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <editeng/langitem.hxx>
+#include <calbck.hxx>
+#include <crsrsh.hxx>
+
+/* @@@MAINTAINABILITY-HORROR@@@
+ Probably unwanted dependency on SwDocShell
+*/
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+
+sal_Int32 SwDoc::acquire()
+{
+ assert(mReferenceCount >= 0);
+ return osl_atomic_increment(&mReferenceCount);
+}
+
+sal_Int32 SwDoc::release()
+{
+ assert(mReferenceCount >= 1);
+ auto x = osl_atomic_decrement(&mReferenceCount);
+ if (x == 0)
+ delete this;
+ return x;
+}
+
+sal_Int32 SwDoc::getReferenceCount() const
+{
+ assert(mReferenceCount >= 0);
+ return mReferenceCount;
+}
+
+::sw::MetaFieldManager & SwDoc::GetMetaFieldManager()
+{
+ return *m_pMetaFieldManager;
+}
+
+::sw::UndoManager & SwDoc::GetUndoManager()
+{
+ return *m_pUndoManager;
+}
+
+::sw::UndoManager const & SwDoc::GetUndoManager() const
+{
+ return *m_pUndoManager;
+}
+
+
+IDocumentUndoRedo & SwDoc::GetIDocumentUndoRedo()
+{
+ return *m_pUndoManager;
+}
+
+IDocumentUndoRedo const & SwDoc::GetIDocumentUndoRedo() const
+{
+ return *m_pUndoManager;
+}
+
+/* IDocumentDrawModelAccess */
+IDocumentDrawModelAccess const & SwDoc::getIDocumentDrawModelAccess() const
+{
+ return GetDocumentDrawModelManager();
+}
+
+IDocumentDrawModelAccess & SwDoc::getIDocumentDrawModelAccess()
+{
+ return GetDocumentDrawModelManager();
+}
+
+::sw::DocumentDrawModelManager const & SwDoc::GetDocumentDrawModelManager() const
+{
+ return *m_pDocumentDrawModelManager;
+}
+
+::sw::DocumentDrawModelManager & SwDoc::GetDocumentDrawModelManager()
+{
+ return *m_pDocumentDrawModelManager;
+}
+
+/* IDocumentSettingAccess */
+IDocumentSettingAccess const & SwDoc::getIDocumentSettingAccess() const
+{
+ return GetDocumentSettingManager();
+}
+
+IDocumentSettingAccess & SwDoc::getIDocumentSettingAccess()
+{
+ return GetDocumentSettingManager();
+}
+
+::sw::DocumentSettingManager & SwDoc::GetDocumentSettingManager()
+{
+ return *m_pDocumentSettingManager;
+}
+
+::sw::DocumentSettingManager const & SwDoc::GetDocumentSettingManager() const
+{
+ return *m_pDocumentSettingManager;
+}
+
+sal_uInt32 SwDoc::getRsid() const
+{
+ return mnRsid;
+}
+
+void SwDoc::setRsid( sal_uInt32 nVal )
+{
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ sal_uInt32 nIncrease = 0;
+ if (!bHack)
+ {
+ // Increase the rsid with a random number smaller than 2^17. This way we
+ // expect to be able to edit a document 2^12 times before rsid overflows.
+ // start from 1 to ensure the new rsid is not the same
+ nIncrease = comphelper::rng::uniform_uint_distribution(1, (1 << 17) - 1);
+ }
+ mnRsid = nVal + nIncrease;
+}
+
+sal_uInt32 SwDoc::getRsidRoot() const
+{
+ return mnRsidRoot;
+}
+
+void SwDoc::setRsidRoot( sal_uInt32 nVal )
+{
+ mnRsidRoot = nVal;
+}
+
+/* IDocumentChartDataProviderAccess */
+IDocumentChartDataProviderAccess const & SwDoc::getIDocumentChartDataProviderAccess() const
+{
+ return *m_pDocumentChartDataProviderManager;
+}
+
+IDocumentChartDataProviderAccess & SwDoc::getIDocumentChartDataProviderAccess()
+{
+ return *m_pDocumentChartDataProviderManager;
+}
+
+// IDocumentDeviceAccess
+IDocumentDeviceAccess const & SwDoc::getIDocumentDeviceAccess() const
+{
+ return *m_pDeviceAccess;
+}
+
+IDocumentDeviceAccess & SwDoc::getIDocumentDeviceAccess()
+{
+ return *m_pDeviceAccess;
+}
+
+//IDocumentTimerAccess
+IDocumentTimerAccess const & SwDoc::getIDocumentTimerAccess() const
+{
+ return *m_pDocumentTimerManager;
+}
+
+IDocumentTimerAccess & SwDoc::getIDocumentTimerAccess()
+{
+ return *m_pDocumentTimerManager;
+}
+
+// IDocumentLinksAdministration
+IDocumentLinksAdministration const & SwDoc::getIDocumentLinksAdministration() const
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+IDocumentLinksAdministration & SwDoc::getIDocumentLinksAdministration()
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+::sw::DocumentLinksAdministrationManager const & SwDoc::GetDocumentLinksAdministrationManager() const
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+::sw::DocumentLinksAdministrationManager & SwDoc::GetDocumentLinksAdministrationManager()
+{
+ return *m_pDocumentLinksAdministrationManager;
+}
+
+//IDocumentListItems
+IDocumentListItems const & SwDoc::getIDocumentListItems() const
+{
+ return *m_pDocumentListItemsManager;
+}
+
+//IDocumentListItems
+IDocumentListItems & SwDoc::getIDocumentListItems()
+{
+ return *m_pDocumentListItemsManager;
+}
+
+//IDocumentListsAccess
+IDocumentListsAccess const & SwDoc::getIDocumentListsAccess() const
+{
+ return *m_pDocumentListsManager;
+}
+
+IDocumentListsAccess & SwDoc::getIDocumentListsAccess()
+{
+ return *m_pDocumentListsManager;
+}
+
+//IDocumentOutlinesNodes
+IDocumentOutlineNodes const & SwDoc::getIDocumentOutlineNodes() const
+{
+ return *m_pDocumentOutlineNodesManager;
+}
+
+IDocumentOutlineNodes & SwDoc::getIDocumentOutlineNodes()
+{
+ return *m_pDocumentOutlineNodesManager;
+}
+
+//IDocumentContentOperations
+IDocumentContentOperations const & SwDoc::getIDocumentContentOperations() const
+{
+ return *m_pDocumentContentOperationsManager;
+}
+
+IDocumentContentOperations & SwDoc::getIDocumentContentOperations()
+{
+ return *m_pDocumentContentOperationsManager;
+}
+
+::sw::DocumentContentOperationsManager const & SwDoc::GetDocumentContentOperationsManager() const
+{
+ return *m_pDocumentContentOperationsManager;
+}
+::sw::DocumentContentOperationsManager & SwDoc::GetDocumentContentOperationsManager()
+{
+ return *m_pDocumentContentOperationsManager;
+}
+
+//IDocumentRedlineAccess
+IDocumentRedlineAccess const & SwDoc::getIDocumentRedlineAccess() const
+{
+ return *m_pDocumentRedlineManager;
+}
+
+IDocumentRedlineAccess& SwDoc::getIDocumentRedlineAccess()
+{
+ return *m_pDocumentRedlineManager;
+}
+
+::sw::DocumentRedlineManager const & SwDoc::GetDocumentRedlineManager() const
+{
+ return *m_pDocumentRedlineManager;
+}
+
+::sw::DocumentRedlineManager& SwDoc::GetDocumentRedlineManager()
+{
+ return *m_pDocumentRedlineManager;
+}
+
+//IDocumentFieldsAccess
+
+IDocumentFieldsAccess const & SwDoc::getIDocumentFieldsAccess() const
+{
+ return *m_pDocumentFieldsManager;
+}
+
+IDocumentFieldsAccess & SwDoc::getIDocumentFieldsAccess()
+{
+ return *m_pDocumentFieldsManager;
+}
+
+::sw::DocumentFieldsManager & SwDoc::GetDocumentFieldsManager()
+{
+ return *m_pDocumentFieldsManager;
+}
+
+//IDocumentStatistics
+IDocumentStatistics const & SwDoc::getIDocumentStatistics() const
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+IDocumentStatistics & SwDoc::getIDocumentStatistics()
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+::sw::DocumentStatisticsManager const & SwDoc::GetDocumentStatisticsManager() const
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+::sw::DocumentStatisticsManager & SwDoc::GetDocumentStatisticsManager()
+{
+ return *m_pDocumentStatisticsManager;
+}
+
+//IDocumentState
+IDocumentState const & SwDoc::getIDocumentState() const
+{
+ return *m_pDocumentStateManager;
+}
+
+IDocumentState & SwDoc::getIDocumentState()
+{
+ return *m_pDocumentStateManager;
+}
+
+//IDocumentLayoutAccess
+IDocumentLayoutAccess const & SwDoc::getIDocumentLayoutAccess() const
+{
+ return *m_pDocumentLayoutManager;
+}
+
+IDocumentLayoutAccess & SwDoc::getIDocumentLayoutAccess()
+{
+ return *m_pDocumentLayoutManager;
+}
+
+::sw::DocumentLayoutManager const & SwDoc::GetDocumentLayoutManager() const
+{
+ return *m_pDocumentLayoutManager;
+}
+
+::sw::DocumentLayoutManager & SwDoc::GetDocumentLayoutManager()
+{
+ return *m_pDocumentLayoutManager;
+}
+
+//IDocumentStylePoolAccess
+IDocumentStylePoolAccess const & SwDoc::getIDocumentStylePoolAccess() const
+{
+ return *m_pDocumentStylePoolManager;
+}
+
+IDocumentStylePoolAccess & SwDoc::getIDocumentStylePoolAccess()
+{
+ return *m_pDocumentStylePoolManager;
+}
+
+//IDocumentExternalData
+IDocumentExternalData const & SwDoc::getIDocumentExternalData() const
+{
+ return *m_pDocumentExternalDataManager;
+}
+
+IDocumentExternalData & SwDoc::getIDocumentExternalData()
+{
+ return *m_pDocumentExternalDataManager;
+}
+
+/* Implementations the next Interface here */
+
+/*
+ * Document editing (Doc-SS) to fill the document
+ * by the RTF parser and for the EditShell.
+ */
+void SwDoc::ChgDBData(const SwDBData& rNewData)
+{
+ if( rNewData != maDBData )
+ {
+ maDBData = rNewData;
+ getIDocumentState().SetModified();
+ if (m_pDBManager)
+ m_pDBManager->CommitLastRegistrations();
+ }
+ getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DatabaseName)->UpdateFields();
+}
+
+namespace {
+
+struct PostItField_ : public SetGetExpField
+{
+ PostItField_( const SwNodeIndex& rNdIdx, const SwTextField* pField )
+ : SetGetExpField( rNdIdx, pField, nullptr ) {}
+
+ sal_uInt16 GetPageNo( const StringRangeEnumerator &rRangeEnum,
+ const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
+ sal_uInt16& rVirtPgNo, sal_uInt16& rLineNo );
+
+ const SwPostItField* GetPostIt() const
+ {
+ return static_cast<const SwPostItField*>( GetTextField()->GetFormatField().GetField() );
+ }
+};
+
+}
+
+sal_uInt16 PostItField_::GetPageNo(
+ const StringRangeEnumerator &rRangeEnum,
+ const o3tl::sorted_vector< sal_Int32 > &rPossiblePages,
+ /* out */ sal_uInt16& rVirtPgNo, /* out */ sal_uInt16& rLineNo )
+{
+ //Problem: If a PostItField is contained in a Node that is represented
+ //by more than one layout instance,
+ //we have to decide whether it should be printed once or n-times.
+ //Probably only once. For the page number we don't select a random one,
+ //but the PostIt's first occurrence in the selected area.
+ rVirtPgNo = 0;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(GetTextField()->GetTextNode());
+ for( SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ TextFrameIndex const nPos = pFrame->MapModelToView(
+ &GetTextField()->GetTextNode(), GetContent());
+ if( pFrame->GetOffset() > nPos ||
+ (pFrame->HasFollow() && pFrame->GetFollow()->GetOffset() <= nPos) )
+ continue;
+ sal_uInt16 nPgNo = pFrame->GetPhyPageNum();
+ if( rRangeEnum.hasValue( nPgNo, &rPossiblePages ))
+ {
+ rLineNo = o3tl::narrowing<sal_uInt16>(pFrame->GetLineCount( nPos ) +
+ pFrame->GetAllLines() - pFrame->GetThisLines());
+ rVirtPgNo = pFrame->GetVirtPageNum();
+ return nPgNo;
+ }
+ }
+ return 0;
+}
+
+bool sw_GetPostIts(const IDocumentFieldsAccess& rIDFA, SetGetExpFields* pSrtLst)
+{
+ SwFieldType* pFieldType = rIDFA.GetSysFieldType(SwFieldIds::Postit);
+ assert(pFieldType);
+
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ if(pSrtLst)
+ for(auto pField: vFields)
+ {
+ auto pTextField = pField->GetTextField();
+ SwNodeIndex aIdx(pTextField->GetTextNode());
+ std::unique_ptr<PostItField_> pNew(new PostItField_(aIdx, pTextField));
+ pSrtLst->insert(std::move(pNew));
+
+ }
+ return vFields.size()>0;
+}
+
+static void lcl_FormatPostIt(
+ IDocumentContentOperations* pIDCO,
+ SwPaM& aPam,
+ const SwPostItField* pField,
+ bool bNewPage, bool bIsFirstPostIt,
+ sal_uInt16 nPageNo, sal_uInt16 nLineNo )
+{
+ static char const sTmp[] = " : ";
+
+ assert(SwViewShell::GetShellRes());
+
+ if (bNewPage)
+ {
+ pIDCO->InsertPoolItem( aPam, SvxFormatBreakItem( SvxBreak::PageAfter, RES_BREAK ) );
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ }
+ else if (!bIsFirstPostIt)
+ {
+ // add an empty line between different notes
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ }
+
+ OUString aStr( SwViewShell::GetShellRes()->aPostItPage );
+ aStr += sTmp +
+ OUString::number( nPageNo ) +
+ " ";
+ if( nLineNo )
+ {
+ aStr += SwViewShell::GetShellRes()->aPostItLine;
+ aStr += sTmp +
+ OUString::number( nLineNo ) +
+ " ";
+ }
+ aStr += SwViewShell::GetShellRes()->aPostItAuthor;
+ aStr += sTmp + pField->GetPar1() + " ";
+ SvtSysLocale aSysLocale;
+ aStr += /*(LocaleDataWrapper&)*/aSysLocale.GetLocaleData().getDate( pField->GetDate() );
+ if(pField->GetResolved())
+ aStr += " " + SwResId(STR_RESOLVED);
+ pIDCO->InsertString( aPam, aStr );
+
+ pIDCO->SplitNode( *aPam.GetPoint(), false );
+ aStr = pField->GetPar2();
+#if defined(_WIN32)
+ // Throw out all CR in Windows
+ aStr = aStr.replaceAll("\r", "");
+#endif
+ pIDCO->InsertString( aPam, aStr );
+}
+
+/// provide the paper tray to use according to the page style in use,
+/// but do that only if the respective item is NOT just the default item
+static sal_Int32 lcl_GetPaperBin( const SwPageFrame *pStartFrame )
+{
+ sal_Int32 nRes = -1;
+
+ const SwFrameFormat &rFormat = pStartFrame->GetPageDesc()->GetMaster();
+ const SfxPoolItem *pItem = nullptr;
+ SfxItemState eState = rFormat.GetItemState( RES_PAPER_BIN, false, &pItem );
+ const SvxPaperBinItem *pPaperBinItem = dynamic_cast< const SvxPaperBinItem * >(pItem);
+ if (eState > SfxItemState::DEFAULT && pPaperBinItem)
+ nRes = pPaperBinItem->GetValue();
+
+ return nRes;
+}
+
+namespace
+{
+// tdf#:114663 Translates a range string from user input (with page numbering possibly not
+// taking blank pages into account) to equivalent string which references physical page numbers.
+// rUIPages2PhyPagesMap must contain a contiguous sequence of UI page numbers
+OUString UIPages2PhyPages(const OUString& rUIPageRange, const std::map< sal_Int32, sal_Int32 >& rUIPages2PhyPagesMap)
+{
+ if (rUIPages2PhyPagesMap.empty())
+ return OUString();
+ auto iMin = rUIPages2PhyPagesMap.begin();
+ const sal_Int32 nUIPageMin = iMin->first, nPhyPageMin = iMin->second;
+ auto iMax = rUIPages2PhyPagesMap.rbegin();
+ const sal_Int32 nUIPageMax = iMax->first, nPhyPageMax = iMax->second;
+ OUStringBuffer aOut(rUIPageRange.getLength());
+ OUStringBuffer aNumber(16);
+ const sal_Unicode* pInput = rUIPageRange.getStr();
+ while (*pInput)
+ {
+ while (*pInput >= '0' && *pInput <= '9')
+ aNumber.append(*pInput++);
+ if (!aNumber.isEmpty())
+ {
+ sal_Int32 nNumber = aNumber.makeStringAndClear().toInt32();
+ if (nNumber < nUIPageMin)
+ nNumber = nPhyPageMin-1;
+ else if (nNumber > nUIPageMax)
+ nNumber = nPhyPageMax+1;
+ else
+ nNumber = rUIPages2PhyPagesMap.at(nNumber);
+ aOut.append(nNumber);
+ }
+
+ while (*pInput && (*pInput < '0' || *pInput > '9'))
+ aOut.append(*pInput++);
+ }
+
+ return aOut.makeStringAndClear();
+}
+}
+
+// tdf#52316 remove blank pages from page count and actual page number
+void SwDoc::CalculateNonBlankPages(
+ const SwRootFrame& rLayout,
+ sal_uInt16& nDocPageCount,
+ sal_uInt16& nActualPage)
+{
+ sal_uInt16 nDocPageCountWithBlank = nDocPageCount;
+ sal_uInt16 nActualPageWithBlank = nActualPage;
+ sal_uInt16 nPageNum = 1;
+ const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ while (pStPage && nPageNum <= nDocPageCountWithBlank)
+ {
+ if ( pStPage->getFrameArea().Height() == 0 )
+ {
+ --nDocPageCount;
+ if (nPageNum <= nActualPageWithBlank)
+ --nActualPage;
+ }
+ ++nPageNum;
+ pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
+ }
+}
+
+void SwDoc::CalculatePagesForPrinting(
+ const SwRootFrame& rLayout,
+ /* out */ SwRenderData &rData,
+ const SwPrintUIOptions &rOptions,
+ bool bIsPDFExport,
+ sal_Int32 nDocPageCount )
+{
+ const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
+ const bool bPrintSelection = nContent == 2;
+
+ // properties to take into account when calculating the set of pages
+ // (PDF export UI does not allow for selecting left or right pages only)
+ bool bPrintLeftPages = bIsPDFExport || rOptions.IsPrintLeftPages();
+ bool bPrintRightPages = bIsPDFExport || rOptions.IsPrintRightPages();
+ // #i103700# printing selections should not allow for automatic inserting empty pages
+ bool bPrintEmptyPages = !bPrintSelection && rOptions.IsPrintEmptyPages( bIsPDFExport );
+
+ std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
+ o3tl::sorted_vector< sal_Int32 > &rValidPages = rData.GetValidPagesSet();
+ // Map page numbers from user input (possibly ignoring blanks) to physical page numbers
+ std::map< sal_Int32, sal_Int32 > aUIPages2PhyPagesMap;
+ rValidPages.clear();
+
+ sal_Int32 nPageNum = 1, nUIPageNum = 1;
+ const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ while (pStPage && nPageNum <= nDocPageCount)
+ {
+ const bool bNonEmptyPage = pStPage->getFrameArea().Height() != 0;
+ const bool bPrintThisPage =
+ ( (bPrintRightPages && pStPage->OnRightPage()) ||
+ (bPrintLeftPages && !pStPage->OnRightPage()) ) &&
+ ( bPrintEmptyPages || bNonEmptyPage );
+
+ if (bPrintThisPage)
+ {
+ rValidPages.insert( nPageNum );
+ rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
+ }
+
+ if ( bPrintEmptyPages || bNonEmptyPage )
+ {
+ aUIPages2PhyPagesMap[nUIPageNum++] = nPageNum;
+ }
+ ++nPageNum;
+ pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
+ }
+
+ // now that we have identified the valid pages for printing according
+ // to the print settings we need to get the PageRange to use and
+ // use both results to get the actual pages to be printed
+ // (post-it settings need to be taken into account later on!)
+
+ // get PageRange value to use
+ OUString aPageRange;
+ // #i116085# - adjusting fix for i113919
+ if ( !bIsPDFExport )
+ {
+ // PageContent :
+ // 0 -> print all pages (default if aPageRange is empty)
+ // 1 -> print range according to PageRange
+ // 2 -> print selection
+ if (1 == nContent)
+ aPageRange = rOptions.getStringValue( "PageRange" );
+
+ if (2 == nContent)
+ {
+ // note that printing selections is actually implemented by copying
+ // the selection to a new temporary document and printing all of that one.
+ // Thus for Writer "PrintContent" must never be 2.
+ // See SwXTextDocument::GetRenderDoc for evaluating if a selection is to be
+ // printed and for creating the temporary document.
+ }
+
+ // please note
+ }
+ if (aPageRange.isEmpty()) // empty string -> print all
+ {
+ // set page range to print to 'all pages'
+ aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
+ }
+ else
+ {
+ // Convert page numbers from user input to physical page numbers
+ aPageRange = UIPages2PhyPages(aPageRange, aUIPages2PhyPagesMap);
+ }
+ rData.SetPageRange( aPageRange );
+
+ // get vector of pages to print according to PageRange and valid pages set from above
+ // (result may be an empty vector, for example if the range string is not correct)
+ // If excluding empty pages, allow range to specify range of printable pages
+ StringRangeEnumerator::getRangesFromString( aPageRange, rData.GetPagesToPrint(),
+ 1, nDocPageCount, 0, &rData.GetValidPagesSet() );
+}
+
+void SwDoc::UpdatePagesForPrintingWithPostItData(
+ /* out */ SwRenderData &rData,
+ const SwPrintUIOptions &rOptions,
+ sal_Int32 nDocPageCount )
+{
+
+ SwPostItMode nPostItMode = static_cast<SwPostItMode>( rOptions.getIntValue( "PrintAnnotationMode", 0 ) );
+ assert((nPostItMode == SwPostItMode::NONE || rData.HasPostItData())
+ && "print post-its without post-it data?");
+ const SetGetExpFields::size_type nPostItCount =
+ rData.HasPostItData() ? rData.m_pPostItFields->size() : 0;
+ if (nPostItMode == SwPostItMode::NONE || nPostItCount <= 0)
+ return;
+
+ CurrShell aCurr( rData.m_pPostItShell.get() );
+
+ // clear document and move to end of it
+ SwDoc & rPostItDoc(*rData.m_pPostItShell->GetDoc());
+ SwPaM aPam(rPostItDoc.GetNodes().GetEndOfContent());
+ aPam.Move( fnMoveBackward, GoInDoc );
+ aPam.SetMark();
+ aPam.Move( fnMoveForward, GoInDoc );
+ rPostItDoc.getIDocumentContentOperations().DeleteRange( aPam );
+
+ const StringRangeEnumerator aRangeEnum( rData.GetPageRange(), 1, nDocPageCount, 0 );
+
+ // For mode SwPostItMode::EndPage:
+ // maps a physical page number to the page number in post-it document that holds
+ // the first post-it for that physical page . Needed to relate the correct start frames
+ // from the post-it doc to the physical page of the document
+ std::map< sal_Int32, sal_Int32 > aPostItLastStartPageNum;
+
+ // add all post-its on valid pages within the page range to the
+ // temporary post-it document.
+ // Since the array of post-it fields is sorted by page and line number we will
+ // already get them in the correct order
+ sal_uInt16 nVirtPg = 0, nLineNo = 0, nLastPageNum = 0, nPhyPageNum = 0;
+ bool bIsFirstPostIt = true;
+ for (SetGetExpFields::size_type i = 0; i < nPostItCount; ++i)
+ {
+ PostItField_& rPostIt = static_cast<PostItField_&>(*(*rData.m_pPostItFields)[ i ]);
+ nLastPageNum = nPhyPageNum;
+ nPhyPageNum = rPostIt.GetPageNo(
+ aRangeEnum, rData.GetValidPagesSet(), nVirtPg, nLineNo );
+ if (nPhyPageNum)
+ {
+ // need to insert a page break?
+ // In SwPostItMode::EndPage mode for each document page the following
+ // post-it page needs to start on a new page
+ const bool bNewPage = nPostItMode == SwPostItMode::EndPage &&
+ !bIsFirstPostIt && nPhyPageNum != nLastPageNum;
+
+ lcl_FormatPostIt( &rData.m_pPostItShell->GetDoc()->getIDocumentContentOperations(), aPam,
+ rPostIt.GetPostIt(), bNewPage, bIsFirstPostIt, nVirtPg, nLineNo );
+ bIsFirstPostIt = false;
+
+ if (nPostItMode == SwPostItMode::EndPage)
+ {
+ // get the correct number of current pages for the post-it document
+ rData.m_pPostItShell->CalcLayout();
+ const sal_Int32 nPages = rData.m_pPostItShell->GetPageCount();
+ aPostItLastStartPageNum[ nPhyPageNum ] = nPages;
+ }
+ }
+ }
+
+ // format post-it doc to get correct number of pages
+ rData.m_pPostItShell->CalcLayout();
+
+ SwRootFrame* pPostItRoot = rData.m_pPostItShell->GetLayout();
+ //tdf#103313 print dialog maxes out cpu as Idles never get to
+ //complete this postitshell's desire to complete formatting
+ pPostItRoot->ResetIdleFormat();
+
+ const sal_Int32 nPostItDocPageCount = rData.m_pPostItShell->GetPageCount();
+
+ if (nPostItMode == SwPostItMode::Only || nPostItMode == SwPostItMode::EndDoc)
+ {
+ // now add those post-it pages to the vector of pages to print
+ // or replace them if only post-its should be printed
+
+ if (nPostItMode == SwPostItMode::Only)
+ {
+ // no document page to be printed
+ rData.GetPagesToPrint().clear();
+ }
+
+ // now we just need to add the post-it pages to be printed to the
+ // end of the vector of pages to print
+ sal_Int32 nPageNum = 0;
+ const SwPageFrame * pPageFrame = static_cast<SwPageFrame*>(pPostItRoot->Lower());
+ while( pPageFrame && nPageNum < nPostItDocPageCount )
+ {
+ ++nPageNum;
+ // negative page number indicates page is from the post-it doc
+ rData.GetPagesToPrint().push_back( -nPageNum );
+ pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
+ }
+ OSL_ENSURE( nPageNum == nPostItDocPageCount, "unexpected number of pages" );
+ }
+ else if (nPostItMode == SwPostItMode::EndPage)
+ {
+ // the next step is to find all the pages from the post-it
+ // document that should be printed for a given physical page
+ // of the document
+
+ std::vector< sal_Int32 > aTmpPagesToPrint;
+ sal_Int32 nLastPostItPage(0);
+ const size_t nNum = rData.GetPagesToPrint().size();
+ for (size_t i = 0 ; i < nNum; ++i)
+ {
+ // add the physical page to print from the document
+ const sal_Int32 nPhysPage = rData.GetPagesToPrint()[i];
+ aTmpPagesToPrint.push_back( nPhysPage );
+
+ // add the post-it document pages to print, i.e those
+ // post-it pages that have the data for the above physical page
+ std::map<sal_Int32, sal_Int32>::const_iterator const iter(
+ aPostItLastStartPageNum.find(nPhysPage));
+ if (iter != aPostItLastStartPageNum.end())
+ {
+ for (sal_Int32 j = nLastPostItPage + 1;
+ j <= iter->second; ++j)
+ {
+ // negative page number indicates page is from the
+ aTmpPagesToPrint.push_back(-j); // post-it document
+ }
+ nLastPostItPage = iter->second;
+ }
+ }
+
+ // finally we need to assign those vectors to the resulting ones.
+ // swapping the data should be more efficient than assigning since
+ // we won't need the temporary vectors anymore
+ rData.GetPagesToPrint().swap( aTmpPagesToPrint );
+ }
+
+}
+
+void SwDoc::CalculatePagePairsForProspectPrinting(
+ const SwRootFrame& rLayout,
+ /* out */ SwRenderData &rData,
+ const SwPrintUIOptions &rOptions,
+ sal_Int32 nDocPageCount )
+{
+ std::map< sal_Int32, sal_Int32 > &rPrinterPaperTrays = rData.GetPrinterPaperTrays();
+ o3tl::sorted_vector< sal_Int32 > &rValidPagesSet = rData.GetValidPagesSet();
+ std::vector< std::pair< sal_Int32, sal_Int32 > > &rPagePairs = rData.GetPagePairsForProspectPrinting();
+ std::map< sal_Int32, const SwPageFrame * > validStartFrames;
+
+ rPagePairs.clear();
+ rValidPagesSet.clear();
+
+ OUString aPageRange;
+ // PageContent :
+ // 0 -> print all pages (default if aPageRange is empty)
+ // 1 -> print range according to PageRange
+ // 2 -> print selection
+ const sal_Int64 nContent = rOptions.getIntValue( "PrintContent", 0 );
+ if (nContent == 1)
+ aPageRange = rOptions.getStringValue( "PageRange" );
+ if (aPageRange.isEmpty()) // empty string -> print all
+ {
+ // set page range to print to 'all pages'
+ aPageRange = OUString::number( 1 ) + "-" + OUString::number( nDocPageCount );
+ }
+ StringRangeEnumerator aRange( aPageRange, 1, nDocPageCount, 0 );
+
+ if ( aRange.size() <= 0)
+ return;
+
+ const SwPageFrame *pStPage = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ for ( sal_Int32 i = 1; pStPage && i < nDocPageCount; ++i )
+ pStPage = static_cast<const SwPageFrame*>(pStPage->GetNext());
+ if ( !pStPage ) // Then it was that
+ return;
+
+ // currently for prospect printing all pages are valid to be printed
+ // thus we add them all to the respective map and set for later use
+ sal_Int32 nPageNum = 0;
+ const SwPageFrame *pPageFrame = dynamic_cast<const SwPageFrame*>( rLayout.Lower() );
+ while( pPageFrame && nPageNum < nDocPageCount )
+ {
+ ++nPageNum;
+ rValidPagesSet.insert( nPageNum );
+ validStartFrames[ nPageNum ] = pPageFrame;
+ pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
+
+ rPrinterPaperTrays[ nPageNum ] = lcl_GetPaperBin( pStPage );
+ }
+ OSL_ENSURE( nPageNum == nDocPageCount, "unexpected number of pages" );
+
+ // properties to take into account when calculating the set of pages
+ // Note: here bPrintLeftPages and bPrintRightPages refer to the (virtual) resulting pages
+ // of the prospect!
+ bool bPrintLeftPages = rOptions.IsPrintLeftPages();
+ bool bPrintRightPages = rOptions.IsPrintRightPages();
+ bool bPrintProspectRTL = rOptions.getIntValue( "PrintProspectRTL", 0 ) != 0;
+
+ // get pages for prospect printing according to the 'PageRange'
+ // (duplicates and any order allowed!)
+ std::vector< sal_Int32 > aPagesToPrint;
+ StringRangeEnumerator::getRangesFromString(
+ aPageRange, aPagesToPrint, 1, nDocPageCount, 0 );
+
+ if (aPagesToPrint.empty())
+ return;
+
+ // now fill the vector for calculating the page pairs with the start frames
+ // from the above obtained vector
+ std::vector< const SwPageFrame * > aVec;
+ for (sal_Int32 nPage : aPagesToPrint)
+ {
+ const SwPageFrame *pFrame = validStartFrames[ nPage ];
+ aVec.push_back( pFrame );
+ }
+
+ // just one page is special ...
+ if ( 1 == aVec.size() )
+ aVec.insert( aVec.begin() + 1, nullptr ); // insert a second empty page
+ else
+ {
+ // now extend the number of pages to fit a multiple of 4
+ // (4 'normal' pages are needed for a single prospect paper
+ // with back and front)
+ while( aVec.size() & 3 )
+ aVec.push_back( nullptr );
+ }
+
+ // make sure that all pages are in correct order
+ std::vector< const SwPageFrame * >::size_type nSPg = 0;
+ std::vector< const SwPageFrame * >::size_type nEPg = aVec.size();
+ sal_Int32 nStep = 1;
+ if ( 0 == (nEPg & 1 )) // there are no uneven ones!
+ --nEPg;
+
+ if ( !bPrintLeftPages )
+ ++nStep;
+ else if ( !bPrintRightPages )
+ {
+ ++nStep;
+ ++nSPg;
+ --nEPg;
+ }
+
+ // the number of 'virtual' pages to be printed
+ sal_Int32 nCntPage = (( nEPg - nSPg ) / ( 2 * nStep )) + 1;
+
+ for ( sal_Int32 nPrintCount = 0; nSPg < nEPg &&
+ nPrintCount < nCntPage; ++nPrintCount )
+ {
+ pStPage = aVec[ nSPg ];
+ const SwPageFrame* pNxtPage = nEPg < aVec.size() ? aVec[ nEPg ] : nullptr;
+
+ short nRtlOfs = bPrintProspectRTL ? 1 : 0;
+ if ( 0 == (( nSPg + nRtlOfs) & 1 ) ) // switch for odd number in LTR, even number in RTL
+ {
+ const SwPageFrame* pTmp = pStPage;
+ pStPage = pNxtPage;
+ pNxtPage = pTmp;
+ }
+
+ sal_Int32 nFirst = -1, nSecond = -1;
+ for ( int nC = 0; nC < 2; ++nC )
+ {
+ sal_Int32 nPage = -1;
+ if ( pStPage )
+ nPage = pStPage->GetPhyPageNum();
+ if (nC == 0)
+ nFirst = nPage;
+ else
+ nSecond = nPage;
+
+ pStPage = pNxtPage;
+ }
+ rPagePairs.emplace_back(nFirst, nSecond );
+
+ nSPg = nSPg + nStep;
+ nEPg = nEPg - nStep;
+ }
+ OSL_ENSURE( size_t(nCntPage) == rPagePairs.size(), "size mismatch for number of page pairs" );
+
+ // luckily prospect printing does not make use of post-its so far,
+ // thus we are done here.
+}
+
+/// @return the reference in the doc for the name
+const SwFormatRefMark* SwDoc::GetRefMark( std::u16string_view rName ) const
+{
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
+ {
+ auto pFormatRef = dynamic_cast<const SwFormatRefMark*>(pItem);
+ if(!pFormatRef)
+ continue;
+
+ const SwTextRefMark* pTextRef = pFormatRef->GetTextRefMark();
+ if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() &&
+ rName == pFormatRef->GetRefName() )
+ return pFormatRef;
+ }
+ return nullptr;
+}
+
+/// @return the RefMark per index - for Uno
+const SwFormatRefMark* SwDoc::GetRefMark( sal_uInt16 nIndex ) const
+{
+ const SwFormatRefMark* pRet = nullptr;
+
+ sal_uInt32 nCount = 0;
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
+ {
+ auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem);
+ if( !pRefMark )
+ continue;
+ const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
+ if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() )
+ {
+ if(nCount == nIndex)
+ {
+ pRet = pRefMark;
+ break;
+ }
+ nCount++;
+ }
+ }
+ return pRet;
+}
+
+/// @return the names of all set references in the Doc
+//JP 24.06.96: If the array pointer is 0, then just return whether a RefMark is set in the Doc
+// OS 25.06.96: From now on we always return the reference count
+sal_uInt16 SwDoc::GetRefMarks( std::vector<OUString>* pNames ) const
+{
+ sal_uInt16 nCount = 0;
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_REFMARK))
+ {
+ auto pRefMark = dynamic_cast<const SwFormatRefMark*>(pItem);
+ if( !pRefMark )
+ continue;
+ const SwTextRefMark* pTextRef = pRefMark->GetTextRefMark();
+ if( pTextRef && &pTextRef->GetTextNode().GetNodes() == &GetNodes() )
+ {
+ if( pNames )
+ {
+ OUString aTmp(pRefMark->GetRefName());
+ pNames->insert(pNames->begin() + nCount, aTmp);
+ }
+ ++nCount;
+ }
+ }
+
+ return nCount;
+}
+
+static bool lcl_SpellAndGrammarAgain( SwNode* pNd, void* pArgs )
+{
+ SwTextNode *pTextNode = pNd->GetTextNode();
+ bool bOnlyWrong = *static_cast<sal_Bool*>(pArgs);
+ if( pTextNode )
+ {
+ if( bOnlyWrong )
+ {
+ if( pTextNode->GetWrong() &&
+ pTextNode->GetWrong()->InvalidateWrong() )
+ pTextNode->SetWrongDirty(SwTextNode::WrongState::TODO);
+ if( pTextNode->GetGrammarCheck() &&
+ pTextNode->GetGrammarCheck()->InvalidateWrong() )
+ pTextNode->SetGrammarCheckDirty( true );
+ }
+ else
+ {
+ pTextNode->SetWrongDirty(SwTextNode::WrongState::TODO);
+ if( pTextNode->GetWrong() )
+ pTextNode->GetWrong()->SetInvalid( 0, COMPLETE_STRING );
+ pTextNode->SetGrammarCheckDirty( true );
+ if( pTextNode->GetGrammarCheck() )
+ pTextNode->GetGrammarCheck()->SetInvalid( 0, COMPLETE_STRING );
+ }
+ }
+ return true;
+}
+
+static bool lcl_CheckSmartTagsAgain( SwNode* pNd, void* )
+{
+ SwTextNode *pTextNode = pNd->GetTextNode();
+ if( pTextNode )
+ {
+ pTextNode->SetSmartTagDirty( true );
+ pTextNode->ClearSmartTags();
+ }
+ return true;
+}
+
+/**
+ * Re-trigger spelling in the idle handler.
+ *
+ * @param bInvalid if <true>, the WrongLists in all nodes are invalidated
+ * and the SpellInvalid flag is set on all pages.
+ * @param bOnlyWrong controls whether only the areas with wrong words are
+ * checked or the whole area.
+ * @param bSmartTags ???
+ */
+void SwDoc::SpellItAgainSam( bool bInvalid, bool bOnlyWrong, bool bSmartTags )
+{
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
+ assert(getIDocumentLayoutAccess().GetCurrentLayout() && "SpellAgain: Where's my RootFrame?");
+ if( bInvalid )
+ {
+ for ( auto aLayout : aAllLayouts )
+ {
+ aLayout->AllInvalidateSmartTagsOrSpelling(bSmartTags);
+ aLayout->SetNeedGrammarCheck(true);
+ }
+ if ( bSmartTags )
+ GetNodes().ForEach( lcl_CheckSmartTagsAgain, &bOnlyWrong );
+ GetNodes().ForEach( lcl_SpellAndGrammarAgain, &bOnlyWrong );
+ }
+
+ for ( auto aLayout : aAllLayouts )
+ aLayout->SetIdleFlags();
+}
+
+void SwDoc::InvalidateAutoCompleteFlag()
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if( !pTmpRoot )
+ return;
+
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
+ for( auto aLayout : aAllLayouts )
+ aLayout->AllInvalidateAutoCompleteWords();
+ for( SwNodeOffset nNd(1), nCnt = GetNodes().Count(); nNd < nCnt; ++nNd )
+ {
+ SwTextNode* pTextNode = GetNodes()[ nNd ]->GetTextNode();
+ if ( pTextNode ) pTextNode->SetAutoCompleteWordDirty( true );
+ }
+
+ for( auto aLayout : aAllLayouts )
+ aLayout->SetIdleFlags();
+}
+
+const SwFormatINetFormat* SwDoc::FindINetAttr( std::u16string_view rName ) const
+{
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ auto pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if( !pFormatItem || pFormatItem->GetName() != rName )
+ continue;
+ const SwTextINetFormat* pTextAttr = pFormatItem->GetTextINetFormat();
+ if( !pTextAttr )
+ continue;
+ const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
+ if( pTextNd && &pTextNd->GetNodes() == &GetNodes() )
+ {
+ return pFormatItem;
+ }
+ }
+ return nullptr;
+}
+
+void SwDoc::Summary(SwDoc& rExtDoc, sal_uInt8 nLevel, sal_uInt8 nPara, bool bImpress)
+{
+ const SwOutlineNodes& rOutNds = GetNodes().GetOutLineNds();
+ if (rOutNds.empty())
+ return;
+
+ ::StartProgress( STR_STATSTR_SUMMARY, 0, rOutNds.size(), GetDocShell() );
+ SwNodeIndex aEndOfDoc( rExtDoc.GetNodes().GetEndOfContent(), -1 );
+ for( SwOutlineNodes::size_type i = 0; i < rOutNds.size(); ++i )
+ {
+ ::SetProgressState( static_cast<tools::Long>(i), GetDocShell() );
+ const SwNodeOffset nIndex = rOutNds[ i ]->GetIndex();
+
+ const int nLvl = GetNodes()[ nIndex ]->GetTextNode()->GetAttrOutlineLevel()-1;
+ if( nLvl > nLevel )
+ continue;
+ SwNodeOffset nEndOfs(1);
+ sal_uInt8 nWish = nPara;
+ SwNodeOffset nNextOutNd = i + 1 < rOutNds.size() ?
+ rOutNds[ i + 1 ]->GetIndex() : GetNodes().Count();
+ bool bKeep = false;
+ while( ( nWish || bKeep ) && nIndex + nEndOfs < nNextOutNd &&
+ GetNodes()[ nIndex + nEndOfs ]->IsTextNode() )
+ {
+ SwTextNode* pTextNode = GetNodes()[ nIndex+nEndOfs ]->GetTextNode();
+ if (pTextNode->GetText().getLength() && nWish)
+ --nWish;
+ bKeep = pTextNode->GetSwAttrSet().GetKeep().GetValue();
+ ++nEndOfs;
+ }
+
+ SwNodeRange aRange( *rOutNds[ i ], SwNodeOffset(0), *rOutNds[ i ], nEndOfs );
+ GetNodes().Copy_( aRange, aEndOfDoc );
+ }
+ const SwTextFormatColls *pColl = rExtDoc.GetTextFormatColls();
+ for( SwTextFormatColls::size_type i = 0; i < pColl->size(); ++i )
+ (*pColl)[ i ]->ResetFormatAttr( RES_PAGEDESC, RES_BREAK );
+ SwNodeIndex aIndx( rExtDoc.GetNodes().GetEndOfExtras() );
+ ++aEndOfDoc;
+ while( aIndx < aEndOfDoc )
+ {
+ bool bDelete = false;
+ SwNode *pNode = &aIndx.GetNode();
+ if( pNode->IsTextNode() )
+ {
+ SwTextNode *pNd = pNode->GetTextNode();
+ if( pNd->HasSwAttrSet() )
+ pNd->ResetAttr( RES_PAGEDESC, RES_BREAK );
+ if( bImpress )
+ {
+ SwTextFormatColl* pMyColl = pNd->GetTextColl();
+
+ const sal_uInt16 nHeadLine = o3tl::narrowing<sal_uInt16>(
+ !pMyColl->IsAssignedToListLevelOfOutlineStyle()
+ ? RES_POOLCOLL_HEADLINE2
+ : RES_POOLCOLL_HEADLINE1 );
+ pMyColl = rExtDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( nHeadLine );
+ pNd->ChgFormatColl( pMyColl );
+ }
+ if( !pNd->Len() &&
+ pNd->StartOfSectionIndex()+SwNodeOffset(2) < pNd->EndOfSectionIndex() )
+ {
+ bDelete = true;
+ rExtDoc.GetNodes().Delete( aIndx );
+ }
+ }
+ if( !bDelete )
+ ++aIndx;
+ }
+ ::EndProgress( GetDocShell() );
+}
+
+namespace
+{
+void RemoveOrDeleteContents(SwTextNode* pTextNd, IDocumentContentOperations& xOperations)
+{
+ SwPaM aPam(*pTextNd, 0, *pTextNd, pTextNd->GetText().getLength());
+
+ // Remove hidden paragraph or delete contents:
+ // Delete contents if
+ // 1. removing the paragraph would result in an empty section or
+ // 2. if the paragraph is the last paragraph in the section and
+ // there is no paragraph in front of the paragraph:
+ if ((SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
+ || (SwNodeOffset(1) == pTextNd->EndOfSectionIndex() - pTextNd->GetIndex()
+ && !pTextNd->GetNodes()[pTextNd->GetIndex() - 1]->GetTextNode()))
+ {
+ xOperations.DeleteRange(aPam);
+ }
+ else
+ {
+ aPam.DeleteMark();
+ xOperations.DelFullPara(aPam);
+ }
+}
+// Returns if the data was actually modified
+bool HandleHidingField(SwFormatField& rFormatField, const SwNodes& rNodes,
+ IDocumentContentOperations& xOperations)
+{
+ if( !rFormatField.GetTextField() )
+ return false;
+ SwTextNode* pTextNd = rFormatField.GetTextField()->GetpTextNode();
+ if( pTextNd
+ && pTextNd->GetpSwpHints() && pTextNd->IsHiddenByParaField()
+ && &pTextNd->GetNodes() == &rNodes)
+ {
+ RemoveOrDeleteContents(pTextNd, xOperations);
+ return true;
+ }
+ return false;
+}
+}
+
+// The greater the returned value, the more weight has this field type on deciding the final
+// paragraph state
+int SwDoc::FieldCanHideParaWeight(SwFieldIds eFieldId) const
+{
+ switch (eFieldId)
+ {
+ case SwFieldIds::HiddenPara:
+ return 20;
+ case SwFieldIds::Database:
+ return GetDocumentSettingManager().get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA)
+ ? 10
+ : 0;
+ default:
+ return 0;
+ }
+}
+
+bool SwDoc::FieldHidesPara(const SwField& rField) const
+{
+ switch (rField.GetTyp()->Which())
+ {
+ case SwFieldIds::HiddenPara:
+ return static_cast<const SwHiddenParaField&>(rField).IsHidden();
+ case SwFieldIds::Database:
+ return FieldCanHideParaWeight(SwFieldIds::Database)
+ && rField.ExpandField(true, nullptr).isEmpty();
+ default:
+ return false;
+ }
+}
+
+/// Remove the invisible content from the document e.g. hidden areas, hidden paragraphs
+// Returns if the data was actually modified
+bool SwDoc::RemoveInvisibleContent()
+{
+ bool bRet = false;
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
+
+ {
+ class FieldTypeGuard : public SwClient
+ {
+ public:
+ explicit FieldTypeGuard(SwFieldType* pType)
+ : SwClient(pType)
+ {
+ }
+ const SwFieldType* get() const
+ {
+ return static_cast<const SwFieldType*>(GetRegisteredIn());
+ }
+ };
+ // Removing some nodes for one SwFieldIds::Database type might remove the type from
+ // document's field types, invalidating iterators. So, we need to create own list of
+ // matching types prior to processing them.
+ std::vector<std::unique_ptr<FieldTypeGuard>> aHidingFieldTypes;
+ for (std::unique_ptr<SwFieldType> const & pType : *getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if (FieldCanHideParaWeight(pType->Which()))
+ aHidingFieldTypes.push_back(std::make_unique<FieldTypeGuard>(pType.get()));
+ }
+ for (const auto& pTypeGuard : aHidingFieldTypes)
+ {
+ if (const SwFieldType* pType = pTypeGuard->get())
+ {
+ std::vector<SwFormatField*> vFields;
+ pType->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ bRet |= HandleHidingField(*pFormatField, GetNodes(), getIDocumentContentOperations());
+ }
+ }
+ }
+
+ // Remove any hidden paragraph (hidden text attribute)
+ for( SwNodeOffset n = GetNodes().Count(); n; )
+ {
+ SwTextNode* pTextNd = GetNodes()[ --n ]->GetTextNode();
+ if ( pTextNd )
+ {
+ bool bRemoved = false;
+ if ( pTextNd->HasHiddenCharAttribute( true ) )
+ {
+ bRemoved = true;
+ bRet = true;
+
+ if (SwNodeOffset(2) == pTextNd->EndOfSectionIndex() - pTextNd->StartOfSectionIndex())
+ {
+ SwFrameFormat *const pFormat = pTextNd->StartOfSectionNode()->GetFlyFormat();
+ if (nullptr != pFormat)
+ {
+ // remove hidden text frame
+ getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
+ }
+ else
+ {
+ // default, remove hidden paragraph
+ RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
+ }
+ }
+ else
+ {
+ // default, remove hidden paragraph
+ RemoveOrDeleteContents(pTextNd, getIDocumentContentOperations());
+ }
+ }
+ else if ( pTextNd->HasHiddenCharAttribute( false ) )
+ {
+ bRemoved = true;
+ bRet = true;
+ SwScriptInfo::DeleteHiddenRanges( *pTextNd );
+ }
+
+ // Footnotes/Frames may have been removed, therefore we have
+ // to reset n:
+ if ( bRemoved )
+ {
+ // [n] has to be inside [0 .. GetNodes().Count()] range
+ if (n > GetNodes().Count())
+ n = GetNodes().Count();
+ }
+ }
+ }
+
+ {
+ // Delete/empty all hidden areas
+ o3tl::sorted_vector<SwSectionFormat*> aSectFormats;
+ SwSectionFormats& rSectFormats = GetSections();
+
+ for( SwSectionFormats::size_type n = rSectFormats.size(); n; )
+ {
+ SwSectionFormat* pSectFormat = rSectFormats[ --n ];
+ // don't add sections in Undo/Redo
+ if( !pSectFormat->IsInNodesArr())
+ continue;
+ SwSection* pSect = pSectFormat->GetSection();
+ if( pSect->CalcHiddenFlag() )
+ {
+ SwSection* pParent = pSect, *pTmp;
+ while( nullptr != (pTmp = pParent->GetParent() ))
+ {
+ if( pTmp->IsHiddenFlag() )
+ pSect = pTmp;
+ pParent = pTmp;
+ }
+
+ aSectFormats.insert( pSect->GetFormat() );
+ }
+ if( !pSect->GetCondition().isEmpty() )
+ {
+ SwSectionData aSectionData( *pSect );
+ aSectionData.SetCondition( OUString() );
+ aSectionData.SetHidden( false );
+ UpdateSection( n, aSectionData );
+ }
+ }
+
+ auto n = aSectFormats.size();
+
+ if( 0 != n )
+ {
+ while( n )
+ {
+ SwSectionFormat* pSectFormat = aSectFormats[ --n ];
+ SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
+ if( pSectNd )
+ {
+ bRet = true;
+ SwPaM aPam( *pSectNd );
+
+ if( pSectNd->StartOfSectionNode()->StartOfSectionIndex() ==
+ pSectNd->GetIndex() - 1 &&
+ pSectNd->StartOfSectionNode()->EndOfSectionIndex() ==
+ pSectNd->EndOfSectionIndex() + 1 )
+ {
+ // only delete the content
+ SwContentNode* pCNd = GetNodes().GoNext(
+ &aPam.GetPoint()->nNode );
+ aPam.GetPoint()->nContent.Assign( pCNd, 0 );
+ aPam.SetMark();
+ aPam.GetPoint()->nNode = *pSectNd->EndOfSectionNode();
+ pCNd = SwNodes::GoPrevious(
+ &aPam.GetPoint()->nNode );
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ getIDocumentContentOperations().DeleteRange( aPam );
+ }
+ else
+ {
+ // delete the whole section
+ aPam.SetMark();
+ aPam.GetPoint()->nNode = *pSectNd->EndOfSectionNode();
+ getIDocumentContentOperations().DelFullPara( aPam );
+ }
+
+ }
+ }
+ }
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_DELETE_INVISIBLECNTNT, nullptr );
+ return bRet;
+}
+
+bool SwDoc::HasInvisibleContent() const
+{
+ std::vector<SwFormatField*> vFields;
+ getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenPara)->GatherFields(vFields);
+ if(vFields.size())
+ return true;
+
+ // Search for any hidden paragraph (hidden text attribute)
+ for( SwNodeOffset n = GetNodes().Count()-SwNodeOffset(1); n; --n)
+ {
+ SwTextNode* pTextNd = GetNodes()[ n ]->GetTextNode();
+ if ( pTextNd &&
+ ( pTextNd->HasHiddenCharAttribute( true ) || pTextNd->HasHiddenCharAttribute( false ) ) )
+ return true;
+ }
+
+ for(auto pSectFormat : GetSections())
+ {
+ // don't add sections in Undo/Redo
+ if( !pSectFormat->IsInNodesArr())
+ continue;
+ SwSection* pSect = pSectFormat->GetSection();
+ if( pSect->IsHidden() )
+ return true;
+ }
+ return false;
+}
+
+bool SwDoc::RestoreInvisibleContent()
+{
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
+ && (SwUndoId::UI_DELETE_INVISIBLECNTNT == nLastUndoId))
+ {
+ GetIDocumentUndoRedo().Undo();
+ GetIDocumentUndoRedo().ClearRedo();
+ return true;
+ }
+ return false;
+}
+
+bool SwDoc::ConvertFieldsToText(SwRootFrame const& rLayout)
+{
+ bool bRet = false;
+ getIDocumentFieldsAccess().LockExpFields();
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_REPLACE, nullptr );
+
+ const SwFieldTypes* pMyFieldTypes = getIDocumentFieldsAccess().GetFieldTypes();
+ const SwFieldTypes::size_type nCount = pMyFieldTypes->size();
+ //go backward, field types are removed
+ for(SwFieldTypes::size_type nType = nCount; nType > 0; --nType)
+ {
+ const SwFieldType *pCurType = (*pMyFieldTypes)[nType - 1].get();
+
+ if ( SwFieldIds::Postit == pCurType->Which() )
+ continue;
+
+ std::vector<SwFormatField*> vFieldFormats;
+ pCurType->GatherFields(vFieldFormats, false);
+ for(const auto& rpFieldFormat : vFieldFormats)
+ {
+ const SwTextField *pTextField = rpFieldFormat->GetTextField();
+ // skip fields that are currently not in the document
+ // e.g. fields in undo or redo array
+
+ bool bSkip = !pTextField ||
+ !pTextField->GetpTextNode()->GetNodes().IsDocNodes();
+
+ if (!bSkip)
+ {
+ bool bInHeaderFooter = IsInHeaderFooter(SwNodeIndex(*pTextField->GetpTextNode()));
+ const SwFormatField& rFormatField = pTextField->GetFormatField();
+ const SwField* pField = rFormatField.GetField();
+
+ //#i55595# some fields have to be excluded in headers/footers
+ SwFieldIds nWhich = pField->GetTyp()->Which();
+ if(!bInHeaderFooter ||
+ (nWhich != SwFieldIds::PageNumber &&
+ nWhich != SwFieldIds::Chapter &&
+ nWhich != SwFieldIds::GetExp&&
+ nWhich != SwFieldIds::SetExp&&
+ nWhich != SwFieldIds::Input&&
+ nWhich != SwFieldIds::RefPageGet&&
+ nWhich != SwFieldIds::RefPageSet))
+ {
+ OUString sText = pField->ExpandField(true, &rLayout);
+
+ // database fields should not convert their command into text
+ if( SwFieldIds::Database == pCurType->Which() && !static_cast<const SwDBField*>(pField)->IsInitialized())
+ sText.clear();
+
+ SwPaM aInsertPam(*pTextField->GetpTextNode(), pTextField->GetStart());
+ aInsertPam.SetMark();
+
+ // go to the end of the field
+ const SwTextField *pFieldAtEnd = sw::DocumentFieldsManager::GetTextFieldAtPos(*aInsertPam.End());
+ if (pFieldAtEnd && pFieldAtEnd->Which() == RES_TXTATR_INPUTFIELD)
+ {
+ SwPosition &rEndPos = *aInsertPam.GetPoint();
+ rEndPos.nContent = SwCursorShell::EndOfInputFieldAtPos( *aInsertPam.End() );
+ }
+ else
+ {
+ aInsertPam.Move();
+ }
+
+ // first insert the text after field to keep the field's attributes,
+ // then delete the field
+ if (!sText.isEmpty())
+ {
+ // to keep the position after insert
+ SwPaM aDelPam( *aInsertPam.GetMark(), *aInsertPam.GetPoint() );
+ aDelPam.Move( fnMoveBackward );
+ aInsertPam.DeleteMark();
+
+ getIDocumentContentOperations().InsertString( aInsertPam, sText );
+
+ aDelPam.Move();
+ // finally remove the field
+ getIDocumentContentOperations().DeleteAndJoin( aDelPam );
+ }
+ else
+ {
+ getIDocumentContentOperations().DeleteAndJoin( aInsertPam );
+ }
+
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_REPLACE, nullptr );
+ getIDocumentFieldsAccess().UnlockExpFields();
+ return bRet;
+
+}
+
+bool SwDoc::IsInsTableFormatNum() const
+{
+ return SW_MOD()->IsInsTableFormatNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+bool SwDoc::IsInsTableChangeNumFormat() const
+{
+ return SW_MOD()->IsInsTableChangeNumFormat(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+bool SwDoc::IsInsTableAlignNum() const
+{
+ return SW_MOD()->IsInsTableAlignNum(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+bool SwDoc::IsSplitVerticalByDefault() const
+{
+ return SW_MOD()->IsSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE));
+}
+
+void SwDoc::SetSplitVerticalByDefault(bool value)
+{
+ SW_MOD()->SetSplitVerticalByDefault(GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE), value);
+}
+
+/// Set up the InsertDB as Undo table
+void SwDoc::AppendUndoForInsertFromDB( const SwPaM& rPam, bool bIsTable )
+{
+ if( bIsTable )
+ {
+ const SwTableNode* pTableNd = rPam.GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ std::unique_ptr<SwUndoCpyTable> pUndo(new SwUndoCpyTable(*this));
+ pUndo->SetTableSttIdx( pTableNd->GetIndex() );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else if( rPam.HasMark() )
+ {
+ std::unique_ptr<SwUndoCpyDoc> pUndo(new SwUndoCpyDoc( rPam ));
+ pUndo->SetInsertRange( rPam, false );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+}
+
+void SwDoc::ChangeTOX(SwTOXBase & rTOX, const SwTOXBase & rNew)
+{
+ assert(dynamic_cast<const SwTOXBaseSection*>(&rTOX));
+ SwTOXBaseSection& rTOXSect(static_cast<SwTOXBaseSection&>(rTOX));
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTOXChange>(*this, rTOXSect, rNew));
+ }
+
+ rTOX = rNew;
+
+ // note: do not Update the ToX here - the caller will do it, with a ViewShell!
+}
+
+OUString SwDoc::GetPaMDescr(const SwPaM & rPam)
+{
+ if (&rPam.GetNode() == &rPam.GetNode(false))
+ {
+ SwTextNode * pTextNode = rPam.GetNode().GetTextNode();
+
+ if (nullptr != pTextNode)
+ {
+ const sal_Int32 nStart = rPam.Start()->nContent.GetIndex();
+ const sal_Int32 nEnd = rPam.End()->nContent.GetIndex();
+
+ return SwResId(STR_START_QUOTE)
+ + ShortenString(pTextNode->GetText().copy(nStart, nEnd - nStart),
+ nUndoStringLength,
+ SwResId(STR_LDOTS))
+ + SwResId(STR_END_QUOTE);
+ }
+ }
+ else
+ {
+ return SwResId(STR_PARAGRAPHS);
+ }
+
+ return "??";
+}
+
+bool SwDoc::ContainsHiddenChars() const
+{
+ for( SwNodeOffset n = GetNodes().Count(); n; )
+ {
+ SwNode* pNd = GetNodes()[ --n ];
+ if ( pNd->IsTextNode() && pNd->GetTextNode()->HasHiddenCharAttribute( false ) )
+ return true;
+ }
+
+ return false;
+}
+
+std::shared_ptr<SwUnoCursor> SwDoc::CreateUnoCursor( const SwPosition& rPos, bool bTableCursor )
+{
+ std::shared_ptr<SwUnoCursor> pNew;
+ if( bTableCursor )
+ pNew = std::make_shared<SwUnoTableCursor>(rPos);
+ else
+ pNew = std::make_shared<SwUnoCursor>(rPos);
+
+ mvUnoCursorTable.push_back( pNew );
+ return pNew;
+}
+
+void SwDoc::ChkCondColls()
+{
+ for (SwTextFormatColls::size_type n = 0; n < mpTextFormatCollTable->size(); ++n)
+ {
+ SwTextFormatColl *pColl = (*mpTextFormatCollTable)[n];
+ if (RES_CONDTXTFMTCOLL == pColl->Which())
+ pColl->CallSwClientNotify( SwAttrHint() );
+ }
+}
+
+uno::Reference< script::vba::XVBAEventProcessor > const &
+SwDoc::GetVbaEventProcessor()
+{
+#if HAVE_FEATURE_SCRIPTING
+ if( !mxVbaEvents.is() && mpDocShell && ooo::vba::isAlienWordDoc( *mpDocShell ) )
+ {
+ try
+ {
+ uno::Reference< frame::XModel > xModel( mpDocShell->GetModel(), uno::UNO_SET_THROW );
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xModel) };
+ mxVbaEvents.set( ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "com.sun.star.script.vba.VBATextEventProcessor" , aArgs ), uno::UNO_QUERY_THROW );
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+#endif
+ return mxVbaEvents;
+}
+
+void SwDoc::SetMissingDictionaries( bool bIsMissing )
+{
+ if (!bIsMissing)
+ meDictionaryMissing = MissingDictionary::False;
+ else if (meDictionaryMissing == MissingDictionary::Undefined)
+ meDictionaryMissing = MissingDictionary::True;
+};
+
+void SwDoc::SetLanguage(const LanguageType eLang, const sal_uInt16 nId)
+{
+ mpAttrPool->SetPoolDefaultItem(SvxLanguageItem(eLang, nId));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docbasic.cxx b/sw/source/core/doc/docbasic.cxx
new file mode 100644
index 000000000..6bf54c6b6
--- /dev/null
+++ b/sw/source/core/doc/docbasic.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <basic/sbx.hxx>
+#include <frmfmt.hxx>
+#include <fmtinfmt.hxx>
+#include <fmturl.hxx>
+#include <frmatr.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <swevent.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+using namespace ::com::sun::star::uno;
+
+static Sequence<Any> *lcl_docbasic_convertArgs( SbxArray& rArgs )
+{
+ Sequence<Any> *pRet = nullptr;
+
+ sal_uInt32 nCount = rArgs.Count();
+ if( nCount > 1 )
+ {
+ nCount--;
+ pRet = new Sequence<Any>( nCount );
+ Any *pUnoArgs = pRet->getArray();
+ for( sal_uInt32 i=0; i<nCount; i++ )
+ {
+ SbxVariable* pVar = rArgs.Get(i + 1);
+ switch( pVar->GetType() )
+ {
+ case SbxSTRING:
+ pUnoArgs[i] <<= pVar->GetOUString();
+ break;
+ case SbxCHAR:
+ pUnoArgs[i] <<= static_cast<sal_Int16>(pVar->GetChar()) ;
+ break;
+ case SbxUSHORT:
+ pUnoArgs[i] <<= static_cast<sal_Int16>(pVar->GetUShort());
+ break;
+ case SbxLONG:
+ pUnoArgs[i] <<= pVar->GetLong();
+ break;
+ default:
+ pUnoArgs[i].clear();
+ break;
+ }
+ }
+ }
+
+ return pRet;
+}
+
+void SwDoc::ExecMacro( const SvxMacro& rMacro, OUString* pRet, SbxArray* pArgs )
+{
+ switch( rMacro.GetScriptType() )
+ {
+ case STARBASIC:
+ {
+ SbxBaseRef aRef;
+ SbxValue* pRetValue = new SbxValue;
+ aRef = pRetValue;
+ mpDocShell->CallBasic( rMacro.GetMacName(),
+ rMacro.GetLibName(),
+ pArgs, pRet ? pRetValue : nullptr );
+
+ if( pRet && SbxNULL < pRetValue->GetType() &&
+ SbxVOID != pRetValue->GetType() )
+ {
+ // valid value, so set it
+ *pRet = pRetValue->GetOUString();
+ }
+ }
+ break;
+ case JAVASCRIPT:
+ // ignore JavaScript calls
+ break;
+ case EXTENDED_STYPE:
+ {
+ std::unique_ptr<Sequence<Any> > pUnoArgs;
+ if( pArgs )
+ {
+ // better to rename the local function to lcl_translateBasic2Uno and
+ // a much shorter routine can be found in sfx2/source/doc/objmisc.cxx
+ pUnoArgs.reset(lcl_docbasic_convertArgs( *pArgs ));
+ }
+
+ if (!pUnoArgs)
+ {
+ pUnoArgs.reset(new Sequence< Any > (0));
+ }
+
+ // TODO - return value is not handled
+ Any aRet;
+ Sequence< sal_Int16 > aOutArgsIndex;
+ Sequence< Any > aOutArgs;
+
+ SAL_INFO("sw", "SwDoc::ExecMacro URL is " << rMacro.GetMacName() );
+
+ mpDocShell->CallXScript(
+ rMacro.GetMacName(), *pUnoArgs, aRet, aOutArgsIndex, aOutArgs);
+
+ break;
+ }
+ }
+}
+
+sal_uInt16 SwDoc::CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent,
+ bool bCheckPtr )
+{
+ if( !mpDocShell ) // we can't do that without a DocShell!
+ return 0;
+
+ sal_uInt16 nRet = 0;
+ const SvxMacroTableDtor* pTable = nullptr;
+ switch( rCallEvent.eType )
+ {
+ case EVENT_OBJECT_INETATTR:
+ if( bCheckPtr )
+ {
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ auto pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if( pFormatItem && rCallEvent.PTR.pINetAttr == pFormatItem )
+ {
+ bCheckPtr = false; // misuse as a flag
+ break;
+ }
+ }
+ }
+ if( !bCheckPtr )
+ pTable = rCallEvent.PTR.pINetAttr->GetMacroTable();
+ break;
+
+ case EVENT_OBJECT_URLITEM:
+ case EVENT_OBJECT_IMAGE:
+ {
+ const SwFrameFormat* pFormat = rCallEvent.PTR.pFormat;
+ if( bCheckPtr )
+ {
+ if (GetSpzFrameFormats()->IsAlive(pFormat))
+ bCheckPtr = false; // misuse as a flag
+ else
+ // this shouldn't be possible now that SwCallMouseEvent
+ // listens for dying format?
+ assert(false);
+ }
+ if( !bCheckPtr )
+ pTable = &pFormat->GetMacro().GetMacroTable();
+ }
+ break;
+
+ case EVENT_OBJECT_IMAGEMAP:
+ {
+ const IMapObject* pIMapObj = rCallEvent.PTR.IMAP.pIMapObj;
+ if( bCheckPtr )
+ {
+ const SwFrameFormat* pFormat = rCallEvent.PTR.IMAP.pFormat;
+ if (GetSpzFrameFormats()->IsAlive(pFormat))
+ {
+ const ImageMap* pIMap = pFormat->GetURL().GetMap();
+ if (pIMap)
+ {
+ for( size_t nPos = pIMap->GetIMapObjectCount(); nPos; )
+ if( pIMapObj == pIMap->GetIMapObject( --nPos ))
+ {
+ bCheckPtr = false; // misuse as a flag
+ break;
+ }
+ }
+ }
+ }
+ if( !bCheckPtr )
+ pTable = &pIMapObj->GetMacroTable();
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( pTable )
+ {
+ nRet = 0x1;
+ if( pTable->IsKeyValid( nEvent ) )
+ {
+ const SvxMacro& rMacro = *pTable->Get( nEvent );
+ if( STARBASIC == rMacro.GetScriptType() )
+ {
+ nRet += ERRCODE_NONE == mpDocShell->CallBasic( rMacro.GetMacName(),
+ rMacro.GetLibName(), nullptr ) ? 1 : 0;
+ }
+ else if( EXTENDED_STYPE == rMacro.GetScriptType() )
+ {
+ Sequence<Any> aUnoArgs;
+
+ Any aRet;
+ Sequence< sal_Int16 > aOutArgsIndex;
+ Sequence< Any > aOutArgs;
+
+ SAL_INFO("sw", "SwDoc::CallEvent URL is " << rMacro.GetMacName() );
+
+ nRet += ERRCODE_NONE == mpDocShell->CallXScript(
+ rMacro.GetMacName(), aUnoArgs, aRet, aOutArgsIndex, aOutArgs) ? 1 : 0;
+ }
+ // JavaScript calls are ignored
+ }
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docbm.cxx b/sw/source/core/doc/docbm.cxx
new file mode 100644
index 000000000..d2ad4e111
--- /dev/null
+++ b/sw/source/core/doc/docbm.cxx
@@ -0,0 +1,2023 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <utility>
+
+#include <MarkManager.hxx>
+#include <bookmark.hxx>
+#include <crossrefbookmark.hxx>
+#include <crsrsh.hxx>
+#include <annotationmark.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <docary.hxx>
+#include <xmloff/odffields.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <redline.hxx>
+#include <rolbck.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <sal/log.hxx>
+#include <UndoBookmark.hxx>
+#include <tools/datetimeutils.hxx>
+#include <txtfrm.hxx>
+#include <view.hxx>
+
+#include <libxml/xmlstring.h>
+#include <libxml/xmlwriter.h>
+#include <comphelper/lok.hxx>
+#include <strings.hrc>
+
+constexpr OUStringLiteral S_ANNOTATION_BOOKMARK = u"____";
+
+using namespace ::sw::mark;
+
+std::vector<::sw::mark::MarkBase*>::const_iterator const&
+IDocumentMarkAccess::iterator::get() const
+{
+ return *m_pIter;
+}
+
+IDocumentMarkAccess::iterator::iterator(std::vector<::sw::mark::MarkBase*>::const_iterator const& rIter)
+ : m_pIter(rIter)
+{
+}
+
+IDocumentMarkAccess::iterator::iterator(iterator const& rOther)
+ : m_pIter(rOther.m_pIter)
+{
+}
+
+auto IDocumentMarkAccess::iterator::operator=(iterator const& rOther) -> iterator&
+{
+ m_pIter = rOther.m_pIter;
+ return *this;
+}
+
+IDocumentMarkAccess::iterator::iterator(iterator && rOther) noexcept
+ : m_pIter(std::move(rOther.m_pIter))
+{
+}
+
+auto IDocumentMarkAccess::iterator::operator=(iterator && rOther) noexcept -> iterator&
+{
+ m_pIter = std::move(rOther.m_pIter);
+ return *this;
+}
+
+// ARGH why does it *need* to return const& ?
+::sw::mark::IMark* /*const&*/
+IDocumentMarkAccess::iterator::operator*() const
+{
+ return static_cast<sw::mark::IMark*>(**m_pIter);
+}
+
+auto IDocumentMarkAccess::iterator::operator++() -> iterator&
+{
+ ++(*m_pIter);
+ return *this;
+}
+auto IDocumentMarkAccess::iterator::operator++(int) -> iterator
+{
+ iterator tmp(*this);
+ ++(*m_pIter);
+ return tmp;
+}
+
+bool IDocumentMarkAccess::iterator::operator==(iterator const& rOther) const
+{
+ return *m_pIter == *rOther.m_pIter;
+}
+
+bool IDocumentMarkAccess::iterator::operator!=(iterator const& rOther) const
+{
+ return *m_pIter != *rOther.m_pIter;
+}
+
+IDocumentMarkAccess::iterator::iterator()
+ : m_pIter(std::in_place)
+{
+}
+
+auto IDocumentMarkAccess::iterator::operator--() -> iterator&
+{
+ --(*m_pIter);
+ return *this;
+}
+
+auto IDocumentMarkAccess::iterator::operator--(int) -> iterator
+{
+ iterator tmp(*this);
+ --(*m_pIter);
+ return tmp;
+}
+
+auto IDocumentMarkAccess::iterator::operator+=(difference_type const n) -> iterator&
+{
+ (*m_pIter) += n;
+ return *this;
+}
+
+auto IDocumentMarkAccess::iterator::operator+(difference_type const n) const -> iterator
+{
+ return iterator(*m_pIter + n);
+}
+
+auto IDocumentMarkAccess::iterator::operator-=(difference_type const n) -> iterator&
+{
+ (*m_pIter) -= n;
+ return *this;
+}
+
+auto IDocumentMarkAccess::iterator::operator-(difference_type const n) const -> iterator
+{
+ return iterator(*m_pIter - n);
+}
+
+auto IDocumentMarkAccess::iterator::operator-(iterator const& rOther) const -> difference_type
+{
+ return *m_pIter - *rOther.m_pIter;
+}
+
+auto IDocumentMarkAccess::iterator::operator[](difference_type const n) const -> value_type
+{
+ return static_cast<sw::mark::IMark*>((*m_pIter)[n]);
+}
+
+bool IDocumentMarkAccess::iterator::operator<(iterator const& rOther) const
+{
+ return *m_pIter < *rOther.m_pIter;
+}
+bool IDocumentMarkAccess::iterator::operator>(iterator const& rOther) const
+{
+ return *m_pIter > *rOther.m_pIter;
+}
+bool IDocumentMarkAccess::iterator::operator<=(iterator const& rOther) const
+{
+ return *m_pIter <= *rOther.m_pIter;
+}
+bool IDocumentMarkAccess::iterator::operator>=(iterator const& rOther) const
+{
+ return *m_pIter >= *rOther.m_pIter;
+}
+
+
+namespace
+{
+ bool lcl_GreaterThan( const SwPosition& rPos, const SwNodeIndex& rNdIdx, const SwIndex* pIdx )
+ {
+ return pIdx != nullptr
+ ? ( rPos.nNode > rNdIdx
+ || ( rPos.nNode == rNdIdx
+ && rPos.nContent >= pIdx->GetIndex() ) )
+ : rPos.nNode >= rNdIdx;
+ }
+
+ bool lcl_Lower( const SwPosition& rPos, const SwNodeIndex& rNdIdx, const SwIndex* pIdx )
+ {
+ return rPos.nNode < rNdIdx
+ || ( pIdx != nullptr
+ && rPos.nNode == rNdIdx
+ && rPos.nContent < pIdx->GetIndex() );
+ }
+
+ bool lcl_MarkOrderingByStart(const ::sw::mark::MarkBase *const pFirst,
+ const ::sw::mark::MarkBase *const pSecond)
+ {
+ auto const& rFirstStart(pFirst->GetMarkStart());
+ auto const& rSecondStart(pSecond->GetMarkStart());
+ if (rFirstStart.nNode != rSecondStart.nNode)
+ {
+ return rFirstStart.nNode < rSecondStart.nNode;
+ }
+ const sal_Int32 nFirstContent = rFirstStart.nContent.GetIndex();
+ const sal_Int32 nSecondContent = rSecondStart.nContent.GetIndex();
+ if (nFirstContent != 0 || nSecondContent != 0)
+ {
+ return nFirstContent < nSecondContent;
+ }
+ auto *const pCRFirst (dynamic_cast<::sw::mark::CrossRefBookmark const*>(pFirst));
+ auto *const pCRSecond(dynamic_cast<::sw::mark::CrossRefBookmark const*>(pSecond));
+ if ((pCRFirst == nullptr) == (pCRSecond == nullptr))
+ {
+ return false; // equal
+ }
+ return pCRFirst != nullptr; // cross-ref sorts *before*
+ }
+
+ bool lcl_MarkOrderingByEnd(const ::sw::mark::MarkBase *const pFirst,
+ const ::sw::mark::MarkBase *const pSecond)
+ {
+ return pFirst->GetMarkEnd() < pSecond->GetMarkEnd();
+ }
+
+ void lcl_InsertMarkSorted(MarkManager::container_t& io_vMarks,
+ ::sw::mark::MarkBase *const pMark)
+ {
+ io_vMarks.insert(
+ lower_bound(
+ io_vMarks.begin(),
+ io_vMarks.end(),
+ pMark,
+ &lcl_MarkOrderingByStart),
+ pMark);
+ }
+
+ std::unique_ptr<SwPosition> lcl_PositionFromContentNode(
+ SwContentNode * const pContentNode,
+ const bool bAtEnd)
+ {
+ std::unique_ptr<SwPosition> pResult(new SwPosition(*pContentNode));
+ pResult->nContent.Assign(pContentNode, bAtEnd ? pContentNode->Len() : 0);
+ return pResult;
+ }
+
+ // return a position at the begin of rEnd, if it is a ContentNode
+ // else set it to the begin of the Node after rEnd, if there is one
+ // else set it to the end of the node before rStt
+ // else set it to the ContentNode of the Pos outside the Range
+ std::unique_ptr<SwPosition> lcl_FindExpelPosition(
+ const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd,
+ const SwPosition& rOtherPosition)
+ {
+ SwContentNode * pNode = rEnd.GetNode().GetContentNode();
+ bool bPosAtEndOfNode = false;
+ if ( pNode == nullptr)
+ {
+ SwNodeIndex aEnd = rEnd;
+ pNode = rEnd.GetNodes().GoNext( &aEnd );
+ bPosAtEndOfNode = false;
+ }
+ if ( pNode == nullptr )
+ {
+ SwNodeIndex aStt = rStt;
+ pNode = SwNodes::GoPrevious(&aStt);
+ bPosAtEndOfNode = true;
+ }
+ if ( pNode != nullptr )
+ {
+ return lcl_PositionFromContentNode( pNode, bPosAtEndOfNode );
+ }
+
+ return std::make_unique<SwPosition>(rOtherPosition);
+ }
+
+ struct CompareIMarkStartsBefore
+ {
+ bool operator()(SwPosition const& rPos,
+ const sw::mark::IMark* pMark)
+ {
+ return rPos < pMark->GetMarkStart();
+ }
+ bool operator()(const sw::mark::IMark* pMark,
+ SwPosition const& rPos)
+ {
+ return pMark->GetMarkStart() < rPos;
+ }
+ };
+
+ // Apple llvm-g++ 4.2.1 with _GLIBCXX_DEBUG won't eat boost::bind for this
+ // Neither will MSVC 2008 with _DEBUG
+ struct CompareIMarkStartsAfter
+ {
+ bool operator()(SwPosition const& rPos,
+ const sw::mark::IMark* pMark)
+ {
+ return pMark->GetMarkStart() > rPos;
+ }
+ };
+
+
+ IMark* lcl_getMarkAfter(const MarkManager::container_t& rMarks, const SwPosition& rPos)
+ {
+ auto const pMarkAfter = upper_bound(
+ rMarks.begin(),
+ rMarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ if(pMarkAfter == rMarks.end())
+ return nullptr;
+ return *pMarkAfter;
+ };
+
+ IMark* lcl_getMarkBefore(const MarkManager::container_t& rMarks, const SwPosition& rPos)
+ {
+ // candidates from which to choose the mark before
+ MarkManager::container_t vCandidates;
+ // no need to consider marks starting after rPos
+ auto const pCandidatesEnd = upper_bound(
+ rMarks.begin(),
+ rMarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ vCandidates.reserve(pCandidatesEnd - rMarks.begin());
+ // only marks ending before are candidates
+ remove_copy_if(
+ rMarks.begin(),
+ pCandidatesEnd,
+ back_inserter(vCandidates),
+ [&rPos] (const ::sw::mark::MarkBase *const pMark) { return !(pMark->GetMarkEnd() < rPos); } );
+ // no candidate left => we are in front of the first mark or there are none
+ if(vCandidates.empty()) return nullptr;
+ // return the highest (last) candidate using mark end ordering
+ return *max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd);
+ }
+
+ bool lcl_FixCorrectedMark(
+ const bool bChangedPos,
+ const bool bChangedOPos,
+ MarkBase* io_pMark )
+ {
+ if ( IDocumentMarkAccess::GetType(*io_pMark) == IDocumentMarkAccess::MarkType::ANNOTATIONMARK )
+ {
+ // annotation marks are allowed to span a table cell range.
+ // but trigger sorting to be save
+ return true;
+ }
+
+ if ( ( bChangedPos || bChangedOPos )
+ && io_pMark->IsExpanded()
+ && io_pMark->GetOtherMarkPos().nNode.GetNode().FindTableBoxStartNode() !=
+ io_pMark->GetMarkPos().nNode.GetNode().FindTableBoxStartNode() )
+ {
+ if ( !bChangedOPos )
+ {
+ io_pMark->SetMarkPos( io_pMark->GetOtherMarkPos() );
+ }
+ io_pMark->ClearOtherMarkPos();
+ DdeBookmark * const pDdeBkmk = dynamic_cast< DdeBookmark*>(io_pMark);
+ if ( pDdeBkmk != nullptr
+ && pDdeBkmk->IsServer() )
+ {
+ pDdeBkmk->SetRefObject(nullptr);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool lcl_MarkEqualByStart(const ::sw::mark::MarkBase *const pFirst,
+ const ::sw::mark::MarkBase *const pSecond)
+ {
+ return !lcl_MarkOrderingByStart(pFirst, pSecond) &&
+ !lcl_MarkOrderingByStart(pSecond, pFirst);
+ }
+
+ MarkManager::container_t::const_iterator lcl_FindMark(
+ MarkManager::container_t& rMarks,
+ const ::sw::mark::MarkBase *const pMarkToFind)
+ {
+ auto ppCurrentMark = lower_bound(
+ rMarks.begin(), rMarks.end(),
+ pMarkToFind, &lcl_MarkOrderingByStart);
+ // since there are usually not too many marks on the same start
+ // position, we are not doing a bisect search for the upper bound
+ // but instead start to iterate from pMarkLow directly
+ while (ppCurrentMark != rMarks.end() && lcl_MarkEqualByStart(*ppCurrentMark, pMarkToFind))
+ {
+ if(*ppCurrentMark == pMarkToFind)
+ {
+ return MarkManager::container_t::const_iterator(std::move(ppCurrentMark));
+ }
+ ++ppCurrentMark;
+ }
+ // reached a mark starting on a later start pos or the end of the
+ // vector => not found
+ return rMarks.end();
+ };
+
+ MarkManager::container_t::const_iterator lcl_FindMarkAtPos(
+ MarkManager::container_t& rMarks,
+ const SwPosition& rPos,
+ const IDocumentMarkAccess::MarkType eType)
+ {
+ for (auto ppCurrentMark = lower_bound(
+ rMarks.begin(), rMarks.end(),
+ rPos,
+ CompareIMarkStartsBefore());
+ ppCurrentMark != rMarks.end();
+ ++ppCurrentMark)
+ {
+ // Once we reach a mark starting after the target pos
+ // we do not need to continue
+ if((*ppCurrentMark)->GetMarkStart() > rPos)
+ break;
+ if(IDocumentMarkAccess::GetType(**ppCurrentMark) == eType)
+ {
+ return MarkManager::container_t::const_iterator(std::move(ppCurrentMark));
+ }
+ }
+ // reached a mark starting on a later start pos or the end of the
+ // vector => not found
+ return rMarks.end();
+ };
+
+ MarkManager::container_t::const_iterator lcl_FindMarkByName(
+ const OUString& rName,
+ const MarkManager::container_t::const_iterator& ppMarksBegin,
+ const MarkManager::container_t::const_iterator& ppMarksEnd)
+ {
+ return find_if(
+ ppMarksBegin,
+ ppMarksEnd,
+ [&rName] (::sw::mark::MarkBase const*const pMark) { return pMark->GetName() == rName; } );
+ }
+
+ void lcl_DebugMarks(MarkManager::container_t const& rMarks)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("sw.core", rMarks.size() << " Marks");
+ for (auto ppMark = rMarks.begin();
+ ppMark != rMarks.end();
+ ++ppMark)
+ {
+ IMark* pMark = *ppMark;
+ const SwPosition* const pStPos = &pMark->GetMarkStart();
+ const SwPosition* const pEndPos = &pMark->GetMarkEnd();
+ SAL_INFO("sw.core",
+ sal_Int32(pStPos->nNode.GetIndex()) << "," <<
+ pStPos->nContent.GetIndex() << " " <<
+ sal_Int32(pEndPos->nNode.GetIndex()) << "," <<
+ pEndPos->nContent.GetIndex() << " " <<
+ typeid(*pMark).name() << " " <<
+ pMark->GetName());
+ }
+#else
+ (void) rMarks;
+#endif
+ assert(std::is_sorted(rMarks.begin(), rMarks.end(), lcl_MarkOrderingByStart));
+ };
+}
+
+IDocumentMarkAccess::MarkType IDocumentMarkAccess::GetType(const IMark& rBkmk)
+{
+ const std::type_info* const pMarkTypeInfo = &typeid(rBkmk);
+ // not using dynamic_cast<> here for performance
+ if(*pMarkTypeInfo == typeid(UnoMark))
+ return MarkType::UNO_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(DdeBookmark))
+ return MarkType::DDE_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(Bookmark))
+ return MarkType::BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(CrossRefHeadingBookmark))
+ return MarkType::CROSSREF_HEADING_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(CrossRefNumItemBookmark))
+ return MarkType::CROSSREF_NUMITEM_BOOKMARK;
+ else if(*pMarkTypeInfo == typeid(AnnotationMark))
+ return MarkType::ANNOTATIONMARK;
+ else if(*pMarkTypeInfo == typeid(TextFieldmark))
+ return MarkType::TEXT_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(CheckboxFieldmark))
+ return MarkType::CHECKBOX_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(DropDownFieldmark))
+ return MarkType::DROPDOWN_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(DateFieldmark))
+ return MarkType::DATE_FIELDMARK;
+ else if(*pMarkTypeInfo == typeid(NavigatorReminder))
+ return MarkType::NAVIGATOR_REMINDER;
+ else
+ {
+ assert(false && "IDocumentMarkAccess::GetType(..)"
+ " - unknown MarkType. This needs to be fixed!");
+ return MarkType::UNO_BOOKMARK;
+ }
+}
+
+OUString IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix()
+{
+ return "__RefHeading__";
+}
+
+bool IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( const SwPaM& rPaM )
+{
+ return rPaM.Start()->nNode.GetNode().IsTextNode() &&
+ rPaM.Start()->nContent.GetIndex() == 0 &&
+ ( !rPaM.HasMark() ||
+ ( rPaM.GetMark()->nNode == rPaM.GetPoint()->nNode &&
+ rPaM.End()->nContent.GetIndex() == rPaM.End()->nNode.GetNode().GetTextNode()->Len() ) );
+}
+
+void IDocumentMarkAccess::DeleteFieldmarkCommand(::sw::mark::IFieldmark const& rMark)
+{
+ if (GetType(rMark) != MarkType::TEXT_FIELDMARK)
+ {
+ return; // TODO FORMDATE has no command?
+ }
+ SwPaM pam(sw::mark::FindFieldSep(rMark), rMark.GetMarkStart());
+ ++pam.GetPoint()->nContent; // skip CH_TXT_ATR_FIELDSTART
+ pam.GetDoc().getIDocumentContentOperations().DeleteAndJoin(pam);
+}
+
+namespace sw::mark
+{
+ MarkManager::MarkManager(SwDoc& rDoc)
+ : m_rDoc(rDoc)
+ , m_pLastActiveFieldmark(nullptr)
+ { }
+
+ ::sw::mark::IMark* MarkManager::makeMark(const SwPaM& rPaM,
+ const OUString& rName,
+ const IDocumentMarkAccess::MarkType eType,
+ sw::mark::InsertMode const eMode,
+ SwPosition const*const pSepPos)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ {
+ const SwPosition* const pPos1 = rPaM.GetPoint();
+ const SwPosition* pPos2 = pPos1;
+ if(rPaM.HasMark())
+ pPos2 = rPaM.GetMark();
+ SAL_INFO("sw.core",
+ rName << " " <<
+ sal_Int32(pPos1->nNode.GetIndex() )<< "," <<
+ pPos1->nContent.GetIndex() << " " <<
+ sal_Int32(pPos2->nNode.GetIndex()) << "," <<
+ pPos2->nContent.GetIndex());
+ }
+#endif
+ if ( (!rPaM.GetPoint()->nNode.GetNode().IsTextNode()
+ && (eType != MarkType::UNO_BOOKMARK
+ // SwXTextRange can be on table node or plain start node (FLY_AT_FLY)
+ || !rPaM.GetPoint()->nNode.GetNode().IsStartNode()))
+ || (!rPaM.GetMark()->nNode.GetNode().IsTextNode()
+ && (eType != MarkType::UNO_BOOKMARK
+ || !rPaM.GetMark()->nNode.GetNode().IsStartNode())))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - refusing to create mark on non-textnode");
+ return nullptr;
+ }
+ // There should only be one CrossRefBookmark per Textnode per Type
+ if ((eType == MarkType::CROSSREF_NUMITEM_BOOKMARK || eType == MarkType::CROSSREF_HEADING_BOOKMARK)
+ && (lcl_FindMarkAtPos(m_vBookmarks, *rPaM.Start(), eType) != m_vBookmarks.end()))
+ { // this can happen via UNO API
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - refusing to create duplicate CrossRefBookmark");
+ return nullptr;
+ }
+
+ if ((eType == MarkType::CHECKBOX_FIELDMARK || eType == MarkType::DROPDOWN_FIELDMARK)
+ && (eMode == InsertMode::New
+ ? *rPaM.GetPoint() != *rPaM.GetMark()
+ // CopyText: pam covers CH_TXT_ATR_FORMELEMENT
+ : (rPaM.GetPoint()->nNode != rPaM.GetMark()->nNode
+ || rPaM.Start()->nContent.GetIndex() + 1 != rPaM.End()->nContent.GetIndex())))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - invalid range on point fieldmark");
+ return nullptr;
+ }
+
+ if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK)
+ && (rPaM.GetPoint()->nNode.GetNode().StartOfSectionNode() != rPaM.GetMark()->nNode.GetNode().StartOfSectionNode()
+ || (pSepPos && rPaM.GetPoint()->nNode.GetNode().StartOfSectionNode() != pSepPos->nNode.GetNode().StartOfSectionNode())))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - invalid range on fieldmark, different nodes array sections");
+ return nullptr;
+ }
+
+ if ((eType == MarkType::TEXT_FIELDMARK || eType == MarkType::DATE_FIELDMARK)
+ // can't check for Copy - it asserts - but it's also obviously unnecessary
+ && eMode == InsertMode::New
+ && sw::mark::IsFieldmarkOverlap(rPaM))
+ {
+ SAL_WARN("sw.core", "MarkManager::makeMark(..)"
+ " - invalid range on fieldmark, overlaps existing fieldmark or meta-field");
+ return nullptr;
+ }
+
+ // create mark
+ std::unique_ptr<::sw::mark::MarkBase> pMark;
+ switch(eType)
+ {
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ pMark = std::make_unique<TextFieldmark>(rPaM, rName);
+ break;
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ pMark = std::make_unique<CheckboxFieldmark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ pMark = std::make_unique<DropDownFieldmark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ pMark = std::make_unique<DateFieldmark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ pMark = std::make_unique<NavigatorReminder>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ pMark = std::make_unique<Bookmark>(rPaM, vcl::KeyCode(), rName);
+ break;
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ pMark = std::make_unique<DdeBookmark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ pMark = std::make_unique<CrossRefHeadingBookmark>(rPaM, vcl::KeyCode(), rName);
+ break;
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ pMark = std::make_unique<CrossRefNumItemBookmark>(rPaM, vcl::KeyCode(), rName);
+ break;
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ pMark = std::make_unique<UnoMark>(rPaM);
+ break;
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ pMark = std::make_unique<AnnotationMark>( rPaM, rName );
+ break;
+ }
+ assert(pMark && "MarkManager::makeMark(..) - Mark was not created.");
+
+ if(pMark->GetMarkPos() != pMark->GetMarkStart())
+ pMark->Swap();
+
+ // for performance reasons, we trust UnoMarks to have a (generated) unique name
+ if ( eType != IDocumentMarkAccess::MarkType::UNO_BOOKMARK )
+ pMark->SetName( getUniqueMarkName( pMark->GetName() ) );
+
+ // insert any dummy chars before inserting into sorted vectors
+ pMark->InitDoc(m_rDoc, eMode, pSepPos);
+
+ // register mark
+ lcl_InsertMarkSorted(m_vAllMarks, pMark.get());
+ switch(eType)
+ {
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ lcl_InsertMarkSorted(m_vBookmarks, pMark.get());
+ break;
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ lcl_InsertMarkSorted(m_vFieldmarks, pMark.get());
+ break;
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ lcl_InsertMarkSorted( m_vAnnotationMarks, pMark.get() );
+ break;
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ // no special array for these
+ break;
+ }
+ if (eMode == InsertMode::New
+ && (eType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || eType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
+ {
+ // due to SwInsText notifications everything is visible now - tell
+ // layout to hide as appropriate
+ // note: we don't know how many layouts there are and which
+ // parts they hide, so just notify the entire fieldmark, it
+ // should give the right result if not in the most efficient way
+ // note2: can't be done in InitDoc() because it requires the mark
+ // to be inserted in the vectors.
+ SwPaM const tmp(pMark->GetMarkPos(), pMark->GetOtherMarkPos());
+ sw::UpdateFramesForAddDeleteRedline(m_rDoc, tmp);
+ }
+
+ SAL_INFO("sw.core", "--- makeType ---");
+ SAL_INFO("sw.core", "Marks");
+ lcl_DebugMarks(m_vAllMarks);
+ SAL_INFO("sw.core", "Bookmarks");
+ lcl_DebugMarks(m_vBookmarks);
+ SAL_INFO("sw.core", "Fieldmarks");
+ lcl_DebugMarks(m_vFieldmarks);
+
+ return pMark.release();
+ }
+
+ ::sw::mark::IFieldmark* MarkManager::makeFieldBookmark(
+ const SwPaM& rPaM,
+ const OUString& rName,
+ const OUString& rType,
+ SwPosition const*const pSepPos)
+ {
+
+ // Disable undo, because we handle it using SwUndoInsTextFieldmark
+ bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
+
+ sw::mark::IMark* pMark = nullptr;
+ if(rType == ODF_FORMDATE)
+ {
+ pMark = makeMark(rPaM, rName,
+ IDocumentMarkAccess::MarkType::DATE_FIELDMARK,
+ sw::mark::InsertMode::New,
+ pSepPos);
+ }
+ else
+ {
+ pMark = makeMark(rPaM, rName,
+ IDocumentMarkAccess::MarkType::TEXT_FIELDMARK,
+ sw::mark::InsertMode::New,
+ pSepPos);
+ }
+ sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
+ if (pFieldMark)
+ pFieldMark->SetFieldname( rType );
+
+ if (bUndoIsEnabled)
+ {
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled);
+ if (pFieldMark)
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsTextFieldmark>(*pFieldMark));
+ }
+
+ return pFieldMark;
+ }
+
+ ::sw::mark::IFieldmark* MarkManager::makeNoTextFieldBookmark(
+ const SwPaM& rPaM,
+ const OUString& rName,
+ const OUString& rType)
+ {
+ // Disable undo, because we handle it using SwUndoInsNoTextFieldmark
+ bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
+
+ bool bEnableSetModified = m_rDoc.getIDocumentState().IsEnableSetModified();
+ m_rDoc.getIDocumentState().SetEnableSetModified(false);
+
+ sw::mark::IMark* pMark = nullptr;
+ if(rType == ODF_FORMCHECKBOX)
+ {
+ pMark = makeMark( rPaM, rName,
+ IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK,
+ sw::mark::InsertMode::New);
+ }
+ else if(rType == ODF_FORMDROPDOWN)
+ {
+ pMark = makeMark( rPaM, rName,
+ IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK,
+ sw::mark::InsertMode::New);
+ }
+ else if(rType == ODF_FORMDATE)
+ {
+ pMark = makeMark( rPaM, rName,
+ IDocumentMarkAccess::MarkType::DATE_FIELDMARK,
+ sw::mark::InsertMode::New);
+ }
+
+ sw::mark::IFieldmark* pFieldMark = dynamic_cast<sw::mark::IFieldmark*>( pMark );
+ if (pFieldMark)
+ pFieldMark->SetFieldname( rType );
+
+ if (bUndoIsEnabled)
+ {
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled);
+ if (pFieldMark)
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsNoTextFieldmark>(*pFieldMark));
+ }
+
+ m_rDoc.getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ m_rDoc.getIDocumentState().SetModified();
+
+ return pFieldMark;
+ }
+
+ ::sw::mark::IMark* MarkManager::getMarkForTextNode(
+ const SwTextNode& rTextNode,
+ const IDocumentMarkAccess::MarkType eType )
+ {
+ SwPosition aPos(rTextNode);
+ aPos.nContent.Assign(&const_cast<SwTextNode&>(rTextNode), 0);
+ auto const ppExistingMark = lcl_FindMarkAtPos(m_vBookmarks, aPos, eType);
+ if(ppExistingMark != m_vBookmarks.end())
+ return *ppExistingMark;
+ const SwPaM aPaM(aPos);
+ return makeMark(aPaM, OUString(), eType, sw::mark::InsertMode::New);
+ }
+
+ sw::mark::IMark* MarkManager::makeAnnotationMark(
+ const SwPaM& rPaM,
+ const OUString& rName )
+ {
+ return makeMark(rPaM, rName, IDocumentMarkAccess::MarkType::ANNOTATIONMARK,
+ sw::mark::InsertMode::New);
+ }
+
+ void MarkManager::repositionMark(
+ ::sw::mark::IMark* const io_pMark,
+ const SwPaM& rPaM)
+ {
+ assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc &&
+ "<MarkManager::repositionMark(..)>"
+ " - Mark is not in my doc.");
+ MarkBase* const pMarkBase = dynamic_cast< MarkBase* >(io_pMark);
+ if (!pMarkBase)
+ return;
+
+ pMarkBase->InvalidateFrames();
+
+ pMarkBase->SetMarkPos(*(rPaM.GetPoint()));
+ if(rPaM.HasMark())
+ pMarkBase->SetOtherMarkPos(*(rPaM.GetMark()));
+ else
+ pMarkBase->ClearOtherMarkPos();
+
+ if(pMarkBase->GetMarkPos() != pMarkBase->GetMarkStart())
+ pMarkBase->Swap();
+
+ pMarkBase->InvalidateFrames();
+
+ sortMarks();
+ }
+
+ bool MarkManager::renameMark(
+ ::sw::mark::IMark* io_pMark,
+ const OUString& rNewName )
+ {
+ assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc &&
+ "<MarkManager::renameMark(..)>"
+ " - Mark is not in my doc.");
+ if ( io_pMark->GetName() == rNewName )
+ return true;
+ if (lcl_FindMarkByName(rNewName, m_vAllMarks.begin(), m_vAllMarks.end()) != m_vAllMarks.end())
+ return false;
+ if (::sw::mark::MarkBase* pMarkBase = dynamic_cast< ::sw::mark::MarkBase* >(io_pMark))
+ {
+ const OUString sOldName(pMarkBase->GetName());
+ pMarkBase->SetName(rNewName);
+
+ if (dynamic_cast< ::sw::mark::Bookmark* >(io_pMark))
+ {
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRenameBookmark>(sOldName, rNewName, m_rDoc));
+ }
+ m_rDoc.getIDocumentState().SetModified();
+ }
+ }
+ return true;
+ }
+
+ void MarkManager::correctMarksAbsolute(
+ const SwNodeIndex& rOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nOffset)
+ {
+ const SwNode* const pOldNode = &rOldNode.GetNode();
+ SwPosition aNewPos(rNewPos);
+ aNewPos.nContent += nOffset;
+ bool isSortingNeeded = false;
+
+ for (auto ppMark = m_vAllMarks.begin();
+ ppMark != m_vAllMarks.end();
+ ++ppMark)
+ {
+ ::sw::mark::MarkBase *const pMark = *ppMark;
+ // correction of non-existent non-MarkBase instances cannot be done
+ assert(pMark);
+ // is on position ??
+ bool bChangedPos = false;
+ if(&pMark->GetMarkPos().nNode.GetNode() == pOldNode)
+ {
+ pMark->SetMarkPos(aNewPos);
+ bChangedPos = true;
+ isSortingNeeded = true;
+ }
+ bool bChangedOPos = false;
+ if (pMark->IsExpanded() &&
+ &pMark->GetOtherMarkPos().nNode.GetNode() == pOldNode)
+ {
+ // shift the OtherMark to aNewPos
+ pMark->SetOtherMarkPos(aNewPos);
+ bChangedOPos= true;
+ isSortingNeeded = true;
+ }
+ // illegal selection? collapse the mark and restore sorting later
+ isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
+ }
+
+ // restore sorting if needed
+ if(isSortingNeeded)
+ sortMarks();
+
+ SAL_INFO("sw.core", "correctMarksAbsolute");
+ lcl_DebugMarks(m_vAllMarks);
+ }
+
+ void MarkManager::correctMarksRelative(const SwNodeIndex& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset)
+ {
+ const SwNode* const pOldNode = &rOldNode.GetNode();
+ SwPosition aNewPos(rNewPos);
+ aNewPos.nContent += nOffset;
+ bool isSortingNeeded = false;
+
+ for (auto ppMark = m_vAllMarks.begin();
+ ppMark != m_vAllMarks.end();
+ ++ppMark)
+ {
+ // is on position ??
+ bool bChangedPos = false, bChangedOPos = false;
+ ::sw::mark::MarkBase* const pMark = *ppMark;
+ // correction of non-existent non-MarkBase instances cannot be done
+ assert(pMark);
+ if(&pMark->GetMarkPos().nNode.GetNode() == pOldNode)
+ {
+ SwPosition aNewPosRel(aNewPos);
+ if (dynamic_cast< ::sw::mark::CrossRefBookmark *>(pMark))
+ {
+ // ensure that cross ref bookmark always starts at 0
+ aNewPosRel.nContent = 0; // HACK for WW8 import
+ isSortingNeeded = true; // and sort them to be safe...
+ }
+ aNewPosRel.nContent += pMark->GetMarkPos().nContent.GetIndex();
+ pMark->SetMarkPos(aNewPosRel);
+ bChangedPos = true;
+ }
+ if(pMark->IsExpanded() &&
+ &pMark->GetOtherMarkPos().nNode.GetNode() == pOldNode)
+ {
+ SwPosition aNewPosRel(aNewPos);
+ aNewPosRel.nContent += pMark->GetOtherMarkPos().nContent.GetIndex();
+ pMark->SetOtherMarkPos(aNewPosRel);
+ bChangedOPos = true;
+ }
+ // illegal selection? collapse the mark and restore sorting later
+ isSortingNeeded |= lcl_FixCorrectedMark(bChangedPos, bChangedOPos, pMark);
+ }
+
+ // restore sorting if needed
+ if(isSortingNeeded)
+ sortMarks();
+
+ SAL_INFO("sw.core", "correctMarksRelative");
+ lcl_DebugMarks(m_vAllMarks);
+ }
+
+ static bool isDeleteMark(
+ ::sw::mark::MarkBase const*const pMark,
+ SwNodeIndex const& rStt,
+ SwNodeIndex const& rEnd,
+ SwIndex const*const pSttIdx,
+ SwIndex const*const pEndIdx,
+ bool & rbIsPosInRange,
+ bool & rbIsOtherPosInRange)
+ {
+ assert(pMark);
+ // navigator marks should not be moved
+ // TODO: Check if this might make them invalid
+ if (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER)
+ {
+ return false;
+ }
+
+ // on position ??
+ rbIsPosInRange = lcl_GreaterThan(pMark->GetMarkPos(), rStt, pSttIdx)
+ && lcl_Lower(pMark->GetMarkPos(), rEnd, pEndIdx);
+ rbIsOtherPosInRange = pMark->IsExpanded()
+ && lcl_GreaterThan(pMark->GetOtherMarkPos(), rStt, pSttIdx)
+ && lcl_Lower(pMark->GetOtherMarkPos(), rEnd, pEndIdx);
+ // special case: completely in range, touching the end?
+ if ( pEndIdx != nullptr
+ && ( ( rbIsOtherPosInRange
+ && pMark->GetMarkPos().nNode == rEnd
+ && pMark->GetMarkPos().nContent == *pEndIdx )
+ || ( rbIsPosInRange
+ && pMark->IsExpanded()
+ && pMark->GetOtherMarkPos().nNode == rEnd
+ && pMark->GetOtherMarkPos().nContent == *pEndIdx ) ) )
+ {
+ rbIsPosInRange = true;
+ rbIsOtherPosInRange = true;
+ }
+
+ if (rbIsPosInRange
+ && (rbIsOtherPosInRange
+ || !pMark->IsExpanded()))
+ {
+ // completely in range
+
+ bool bDeleteMark = true;
+ {
+ switch ( IDocumentMarkAccess::GetType( *pMark ) )
+ {
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ // no delete of cross-reference bookmarks, if range is inside one paragraph
+ bDeleteMark = rStt != rEnd;
+ break;
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ // no delete of UNO mark, if it is not expanded and only touches the start of the range
+ bDeleteMark = rbIsOtherPosInRange
+ || pMark->IsExpanded()
+ || pSttIdx == nullptr
+ || pMark->GetMarkPos().nNode != rStt
+ || pMark->GetMarkPos().nContent != *pSttIdx;
+ break;
+ default:
+ bDeleteMark = true;
+ break;
+ }
+ }
+ return bDeleteMark;
+ }
+ return false;
+ }
+
+ bool MarkManager::isBookmarkDeleted(SwPaM const& rPaM) const
+ {
+ SwPosition const& rStart(*rPaM.Start());
+ SwPosition const& rEnd(*rPaM.End());
+ for (auto ppMark = m_vBookmarks.begin();
+ ppMark != m_vBookmarks.end();
+ ++ppMark)
+ {
+ bool bIsPosInRange(false);
+ bool bIsOtherPosInRange(false);
+ bool const bDeleteMark = isDeleteMark(*ppMark,
+ rStart.nNode, rEnd.nNode, &rStart.nContent, &rEnd.nContent,
+ bIsPosInRange, bIsOtherPosInRange);
+ if (bDeleteMark
+ && IDocumentMarkAccess::GetType(**ppMark) == MarkType::BOOKMARK)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void MarkManager::deleteMarks(
+ const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd,
+ std::vector<SaveBookmark>* pSaveBkmk,
+ const SwIndex* pSttIdx,
+ const SwIndex* pEndIdx )
+ {
+ std::vector<const_iterator_t> vMarksToDelete;
+ bool bIsSortingNeeded = false;
+
+ // boolean indicating, if at least one mark has been moved while collecting marks for deletion
+ bool bMarksMoved = false;
+ // have marks in the range been skipped instead of deleted
+ bool bMarksSkipDeletion = false;
+
+ // copy all bookmarks in the move area to a vector storing all position data as offset
+ // reassignment is performed after the move
+ for (auto ppMark = m_vAllMarks.begin();
+ ppMark != m_vAllMarks.end();
+ ++ppMark)
+ {
+ ::sw::mark::MarkBase *const pMark = *ppMark;
+ bool bIsPosInRange(false);
+ bool bIsOtherPosInRange(false);
+ bool const bDeleteMark = isDeleteMark(pMark, rStt, rEnd, pSttIdx, pEndIdx, bIsPosInRange, bIsOtherPosInRange);
+
+ if ( bIsPosInRange
+ && ( bIsOtherPosInRange
+ || !pMark->IsExpanded() ) )
+ {
+ if ( bDeleteMark )
+ {
+ if ( pSaveBkmk )
+ {
+ pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, pSttIdx ) );
+ }
+ vMarksToDelete.emplace_back(ppMark);
+ }
+ else
+ {
+ bMarksSkipDeletion = true;
+ }
+ }
+ else if ( bIsPosInRange != bIsOtherPosInRange )
+ {
+ // the bookmark is partially in the range
+ // move position of that is in the range out of it
+
+ std::unique_ptr< SwPosition > pNewPos;
+ {
+ if ( pEndIdx != nullptr )
+ {
+ pNewPos = std::make_unique< SwPosition >( rEnd, *pEndIdx );
+ }
+ else
+ {
+ pNewPos =
+ lcl_FindExpelPosition( rStt, rEnd, bIsPosInRange ? pMark->GetOtherMarkPos() : pMark->GetMarkPos() );
+ }
+ }
+
+ bool bMoveMark = true;
+ {
+ switch ( IDocumentMarkAccess::GetType( *pMark ) )
+ {
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ // no move of cross-reference bookmarks, if move occurs inside a certain node
+ bMoveMark = pMark->GetMarkPos().nNode != pNewPos->nNode;
+ break;
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ // no move of annotation marks, if method is called to collect deleted marks
+ bMoveMark = pSaveBkmk == nullptr;
+ break;
+ default:
+ bMoveMark = true;
+ break;
+ }
+ }
+ if ( bMoveMark )
+ {
+ if ( bIsPosInRange )
+ pMark->SetMarkPos(*pNewPos);
+ else
+ pMark->SetOtherMarkPos(*pNewPos);
+ bMarksMoved = true;
+
+ // illegal selection? collapse the mark and restore sorting later
+ bIsSortingNeeded |= lcl_FixCorrectedMark( bIsPosInRange, bIsOtherPosInRange, pMark );
+ }
+ }
+ }
+
+ {
+ // fdo#61016 delay the deletion of the fieldmark characters
+ // to prevent that from deleting the marks on that position
+ // which would invalidate the iterators in vMarksToDelete
+ std::vector< std::unique_ptr<ILazyDeleter> > vDelay;
+ vDelay.reserve(vMarksToDelete.size());
+
+ // If needed, sort mark containers containing subsets of the marks
+ // in order to assure sorting. The sorting is critical for the
+ // deletion of a mark as it is searched in these container for
+ // deletion.
+ if ( !vMarksToDelete.empty() && bMarksMoved )
+ {
+ sortSubsetMarks();
+ }
+ // we just remembered the iterators to delete, so we do not need to search
+ // for the shared_ptr<> (the entry in m_vAllMarks) again
+ // reverse iteration, since erasing an entry invalidates iterators
+ // behind it (the iterators in vMarksToDelete are sorted)
+ for ( std::vector< const_iterator_t >::reverse_iterator pppMark = vMarksToDelete.rbegin();
+ pppMark != vMarksToDelete.rend();
+ ++pppMark )
+ {
+ vDelay.push_back(deleteMark(*pppMark, pSaveBkmk != nullptr));
+ }
+ } // scope to kill vDelay
+
+ // also need to sort if both marks were moved and not-deleted because
+ // the not-deleted marks could be in wrong order vs. the moved ones
+ if (bIsSortingNeeded || (bMarksMoved && bMarksSkipDeletion))
+ {
+ sortMarks();
+ }
+
+ SAL_INFO("sw.core", "deleteMarks");
+ lcl_DebugMarks(m_vAllMarks);
+ }
+
+ namespace {
+
+ struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
+ {
+ std::unique_ptr<Fieldmark> m_pFieldmark;
+ SwDoc& m_rDoc;
+ bool const m_isMoveNodes;
+ LazyFieldmarkDeleter(Fieldmark *const pMark, SwDoc& rDoc, bool const isMoveNodes)
+ : m_pFieldmark(pMark), m_rDoc(rDoc), m_isMoveNodes(isMoveNodes)
+ {
+ assert(m_pFieldmark);
+ }
+ virtual ~LazyFieldmarkDeleter() override
+ {
+ // note: because of the call chain from SwUndoDelete, the field
+ // command *cannot* be deleted here as it would create a separate
+ // SwUndoDelete that's interleaved with the SwHistory of the outer
+ // one - only delete the CH_TXT_ATR_FIELD*!
+ if (!m_isMoveNodes)
+ {
+ m_pFieldmark->ReleaseDoc(m_rDoc);
+ }
+ }
+ };
+
+ }
+
+ std::unique_ptr<IDocumentMarkAccess::ILazyDeleter>
+ MarkManager::deleteMark(const const_iterator_t& ppMark, bool const isMoveNodes)
+ {
+ std::unique_ptr<ILazyDeleter> ret;
+ if (ppMark.get() == m_vAllMarks.end())
+ return ret;
+ IMark* pMark = *ppMark;
+
+ switch(IDocumentMarkAccess::GetType(*pMark))
+ {
+ case IDocumentMarkAccess::MarkType::BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
+ {
+ auto const ppBookmark = lcl_FindMark(m_vBookmarks, *ppMark.get());
+ if ( ppBookmark != m_vBookmarks.end() )
+ {
+ m_vBookmarks.erase(ppBookmark);
+ }
+ else
+ {
+ assert(false &&
+ "<MarkManager::deleteMark(..)> - Bookmark not found in Bookmark container.");
+ }
+ }
+ break;
+
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ {
+ auto const ppFieldmark = lcl_FindMark(m_vFieldmarks, *ppMark.get());
+ if ( ppFieldmark != m_vFieldmarks.end() )
+ {
+ if(m_pLastActiveFieldmark == *ppFieldmark)
+ ClearFieldActivation();
+
+ m_vFieldmarks.erase(ppFieldmark);
+ ret.reset(new LazyFieldmarkDeleter(dynamic_cast<Fieldmark*>(pMark), m_rDoc, isMoveNodes));
+ }
+ else
+ {
+ assert(false &&
+ "<MarkManager::deleteMark(..)> - Fieldmark not found in Fieldmark container.");
+ }
+ }
+ break;
+
+ case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
+ {
+ auto const ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, *ppMark.get());
+ assert(ppAnnotationMark != m_vAnnotationMarks.end() &&
+ "<MarkManager::deleteMark(..)> - Annotation Mark not found in Annotation Mark container.");
+ m_vAnnotationMarks.erase(ppAnnotationMark);
+ }
+ break;
+
+ case IDocumentMarkAccess::MarkType::DDE_BOOKMARK:
+ case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER:
+ case IDocumentMarkAccess::MarkType::UNO_BOOKMARK:
+ // no special marks container
+ break;
+ }
+ //Effective STL Item 27, get a non-const iterator aI at the same
+ //position as const iterator ppMark was
+ auto aI = m_vAllMarks.begin();
+ std::advance(aI, std::distance<container_t::const_iterator>(aI, ppMark.get()));
+ DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(pMark);
+ if (pDdeBookmark)
+ {
+ pDdeBookmark->DeregisterFromDoc(m_rDoc);
+
+ // Update aI, possibly a selection listener invalidated the iterators of m_vAllMarks.
+ auto [it, endIt] = equal_range(m_vAllMarks.begin(), m_vAllMarks.end(),
+ pMark->GetMarkStart(), CompareIMarkStartsBefore());
+ for (; it != endIt; ++it)
+ {
+ if (*it == pMark)
+ {
+ aI = m_vAllMarks.begin();
+ std::advance(aI, std::distance<container_t::const_iterator>(aI, it));
+ break;
+ }
+ }
+ }
+
+ m_vAllMarks.erase(aI);
+ // If we don't have a lazy deleter
+ if (!ret)
+ // delete after we remove from the list, because the destructor can
+ // recursively call into this method.
+ delete pMark;
+ return ret;
+ }
+
+ void MarkManager::deleteMark(const IMark* const pMark)
+ {
+ assert(&pMark->GetMarkPos().GetDoc() == &m_rDoc &&
+ "<MarkManager::deleteMark(..)>"
+ " - Mark is not in my doc.");
+ // finds the last Mark that is starting before pMark
+ // (pMarkLow < pMark)
+ auto [it, endIt] = equal_range(
+ m_vAllMarks.begin(),
+ m_vAllMarks.end(),
+ pMark->GetMarkStart(),
+ CompareIMarkStartsBefore());
+ for ( ; it != endIt; ++it)
+ if (*it == pMark)
+ {
+ deleteMark(iterator(it), false);
+ break;
+ }
+ }
+
+ void MarkManager::clearAllMarks()
+ {
+ ClearFieldActivation();
+ m_vFieldmarks.clear();
+ m_vBookmarks.clear();
+ m_vAnnotationMarks.clear();
+ for (const auto & p : m_vAllMarks)
+ delete p;
+ m_vAllMarks.clear();
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::findMark(const OUString& rName) const
+ {
+ auto const ret = lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end());
+ return IDocumentMarkAccess::iterator(ret);
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::findBookmark(const OUString& rName) const
+ {
+ auto const ret = lcl_FindMarkByName(rName, m_vBookmarks.begin(), m_vBookmarks.end());
+ return IDocumentMarkAccess::iterator(ret);
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksBegin() const
+ { return m_vAllMarks.begin(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAllMarksEnd() const
+ { return m_vAllMarks.end(); }
+
+ sal_Int32 MarkManager::getAllMarksCount() const
+ { return m_vAllMarks.size(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksBegin() const
+ { return m_vBookmarks.begin(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getBookmarksEnd() const
+ { return m_vBookmarks.end(); }
+
+ sal_Int32 MarkManager::getBookmarksCount() const
+ { return m_vBookmarks.size(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksBegin() const
+ { return m_vFieldmarks.begin(); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getFieldmarksEnd() const
+ { return m_vFieldmarks.end(); }
+
+
+ // finds the first that is starting after
+ IDocumentMarkAccess::const_iterator_t MarkManager::findFirstBookmarkStartsAfter(const SwPosition& rPos) const
+ {
+ return std::upper_bound(
+ m_vBookmarks.begin(),
+ m_vBookmarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ }
+
+ IFieldmark* MarkManager::getFieldmarkAt(const SwPosition& rPos) const
+ {
+ auto const pFieldmark = find_if(
+ m_vFieldmarks.begin(),
+ m_vFieldmarks.end(),
+ [&rPos] (::sw::mark::MarkBase const*const pMark) {
+ return pMark->GetMarkStart() == rPos
+ // end position includes the CH_TXT_ATR_FIELDEND
+ || (pMark->GetMarkEnd().nContent.GetIndex() == rPos.nContent.GetIndex() + 1
+ && pMark->GetMarkEnd().nNode == rPos.nNode);
+ } );
+ return (pFieldmark == m_vFieldmarks.end())
+ ? nullptr
+ : dynamic_cast<IFieldmark*>(*pFieldmark);
+ }
+
+ IFieldmark* MarkManager::getFieldmarkFor(const SwPosition& rPos) const
+ {
+ auto itFieldmark = find_if(
+ m_vFieldmarks.begin(),
+ m_vFieldmarks.end(),
+ [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
+ if (itFieldmark == m_vFieldmarks.end())
+ return nullptr;
+ auto pFieldmark(*itFieldmark);
+ for ( ; itFieldmark != m_vFieldmarks.end()
+ && (**itFieldmark).GetMarkStart() <= rPos; ++itFieldmark)
+ { // find the innermost fieldmark
+ if (rPos < (**itFieldmark).GetMarkEnd()
+ && (pFieldmark->GetMarkStart() < (**itFieldmark).GetMarkStart()
+ || (**itFieldmark).GetMarkEnd() < pFieldmark->GetMarkEnd()))
+ {
+ pFieldmark = *itFieldmark;
+ }
+ }
+ return dynamic_cast<IFieldmark*>(pFieldmark);
+ }
+
+ void MarkManager::deleteFieldmarkAt(const SwPosition& rPos)
+ {
+ auto const pFieldmark = dynamic_cast<Fieldmark*>(getFieldmarkAt(rPos));
+ assert(pFieldmark); // currently all callers require it to be there
+
+ deleteMark(lcl_FindMark(m_vAllMarks, pFieldmark), false);
+ }
+
+ ::sw::mark::IFieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType)
+ {
+ bool bActualChange = false;
+ if(rNewType == ODF_FORMDROPDOWN)
+ {
+ if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark))
+ bActualChange = true;
+ if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
+ return nullptr;
+ }
+ else if(rNewType == ODF_FORMCHECKBOX)
+ {
+ if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark))
+ bActualChange = true;
+ if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown
+ return nullptr;
+ }
+ else if(rNewType == ODF_FORMDATE)
+ {
+ if (!dynamic_cast<::sw::mark::DateFieldmark*>(pFieldmark))
+ bActualChange = true;
+ if (!dynamic_cast<::sw::mark::TextFieldmark*>(pFieldmark)) // only allowed converting between date field <-> text field
+ return nullptr;
+ }
+
+ if (!bActualChange)
+ return nullptr;
+
+ // Store attributes needed to create the new fieldmark
+ OUString sName = pFieldmark->GetName();
+ SwPaM const aPaM(pFieldmark->GetMarkStart());
+
+ // Remove the old fieldmark and create a new one with the new type
+ if (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX)
+ {
+ SwPosition aNewPos (aPaM.GetPoint()->nNode, aPaM.GetPoint()->nContent);
+ deleteFieldmarkAt(aNewPos);
+ return makeNoTextFieldBookmark(aPaM, sName, rNewType);
+ }
+ else if(rNewType == ODF_FORMDATE)
+ {
+ SwPosition aPos (aPaM.GetPoint()->nNode, aPaM.GetPoint()->nContent);
+ SwPaM aNewPaM(pFieldmark->GetMarkStart(), pFieldmark->GetMarkEnd());
+ deleteFieldmarkAt(aPos);
+ // HACK: hard-code the separator position here at the start because
+ // writerfilter put it in the wrong place (at the end) on attach()
+ SwPosition const sepPos(*aNewPaM.Start());
+ return makeFieldBookmark(aNewPaM, sName, rNewType, &sepPos);
+ }
+ return nullptr;
+ }
+
+ void MarkManager::NotifyCursorUpdate(const SwCursorShell& rCursorShell)
+ {
+ SwView* pSwView = dynamic_cast<SwView *>(rCursorShell.GetSfxViewShell());
+ if(!pSwView)
+ return;
+
+ SwEditWin& rEditWin = pSwView->GetEditWin();
+ SwPosition aPos(*rCursorShell.GetCursor()->GetPoint());
+ IFieldmark* pFieldBM = getFieldmarkFor(aPos);
+ FieldmarkWithDropDownButton* pNewActiveFieldmark = nullptr;
+ if ((!pFieldBM || (pFieldBM->GetFieldname() != ODF_FORMDROPDOWN && pFieldBM->GetFieldname() != ODF_FORMDATE))
+ && aPos.nContent.GetIndex() > 0 )
+ {
+ --aPos.nContent;
+ pFieldBM = getFieldmarkFor(aPos);
+ }
+
+ if ( pFieldBM && (pFieldBM->GetFieldname() == ODF_FORMDROPDOWN ||
+ pFieldBM->GetFieldname() == ODF_FORMDATE))
+ {
+ if (m_pLastActiveFieldmark != pFieldBM)
+ {
+ FieldmarkWithDropDownButton& rFormField = dynamic_cast<FieldmarkWithDropDownButton&>(*pFieldBM);
+ pNewActiveFieldmark = &rFormField;
+ }
+ else
+ {
+ pNewActiveFieldmark = m_pLastActiveFieldmark;
+ }
+ }
+
+ if(pNewActiveFieldmark != m_pLastActiveFieldmark)
+ {
+ ClearFieldActivation();
+ m_pLastActiveFieldmark = pNewActiveFieldmark;
+ if(pNewActiveFieldmark)
+ pNewActiveFieldmark->ShowButton(&rEditWin);
+ }
+
+ LOKUpdateActiveField(pSwView);
+ }
+
+ void MarkManager::ClearFieldActivation()
+ {
+ if(m_pLastActiveFieldmark)
+ m_pLastActiveFieldmark->RemoveButton();
+
+ m_pLastActiveFieldmark = nullptr;
+ }
+
+ void MarkManager::LOKUpdateActiveField(const SfxViewShell* pViewShell)
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (m_pLastActiveFieldmark)
+ {
+ if (auto pDrowDown = m_pLastActiveFieldmark->GetFieldname() == ODF_FORMDROPDOWN ?
+ dynamic_cast<::sw::mark::DropDownFieldmark*>(m_pLastActiveFieldmark) :
+ nullptr)
+ {
+ pDrowDown->SendLOKShowMessage(pViewShell);
+ }
+ }
+ else
+ {
+ // Check whether we have any drop down fieldmark at all.
+ bool bDropDownFieldExist = false;
+ for (auto aIter = m_vFieldmarks.begin(); aIter != m_vFieldmarks.end(); ++aIter)
+ {
+ IFieldmark *pMark = dynamic_cast<IFieldmark*>(*aIter);
+ if (pMark && pMark->GetFieldname() == ODF_FORMDROPDOWN)
+ {
+ bDropDownFieldExist = true;
+ break;
+ }
+ }
+
+ if (bDropDownFieldExist)
+ ::sw::mark::DropDownFieldmark::SendLOKHideMessage(pViewShell);
+ }
+ }
+
+ IFieldmark* MarkManager::getDropDownFor(const SwPosition& rPos) const
+ {
+ IFieldmark *pMark = getFieldmarkAt(rPos);
+ if (!pMark || pMark->GetFieldname() != ODF_FORMDROPDOWN)
+ return nullptr;
+ return pMark;
+ }
+
+ std::vector<IFieldmark*> MarkManager::getNoTextFieldmarksIn(const SwPaM &rPaM) const
+ {
+ std::vector<IFieldmark*> aRet;
+
+ for (auto aI = m_vFieldmarks.begin(),
+ aEnd = m_vFieldmarks.end(); aI != aEnd; ++aI)
+ {
+ ::sw::mark::IMark* pI = *aI;
+ const SwPosition &rStart = pI->GetMarkPos();
+ if (!rPaM.ContainsPosition(rStart))
+ continue;
+
+ IFieldmark *pMark = dynamic_cast<IFieldmark*>(pI);
+ if (!pMark || (pMark->GetFieldname() != ODF_FORMDROPDOWN
+ && pMark->GetFieldname() != ODF_FORMCHECKBOX))
+ {
+ continue;
+ }
+
+ aRet.push_back(pMark);
+ }
+
+ return aRet;
+ }
+
+ IFieldmark* MarkManager::getFieldmarkAfter(const SwPosition& rPos) const
+ { return dynamic_cast<IFieldmark*>(lcl_getMarkAfter(m_vFieldmarks, rPos)); }
+
+ IFieldmark* MarkManager::getFieldmarkBefore(const SwPosition& rPos) const
+ { return dynamic_cast<IFieldmark*>(lcl_getMarkBefore(m_vFieldmarks, rPos)); }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksBegin() const
+ {
+ return m_vAnnotationMarks.begin();
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::getAnnotationMarksEnd() const
+ {
+ return m_vAnnotationMarks.end();
+ }
+
+ sal_Int32 MarkManager::getAnnotationMarksCount() const
+ {
+ return m_vAnnotationMarks.size();
+ }
+
+ IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationMark( const OUString& rName ) const
+ {
+ auto const ret = lcl_FindMarkByName( rName, m_vAnnotationMarks.begin(), m_vAnnotationMarks.end() );
+ return IDocumentMarkAccess::iterator(ret);
+ }
+
+ IMark* MarkManager::getAnnotationMarkFor(const SwPosition& rPos) const
+ {
+ auto const pAnnotationMark = find_if(
+ m_vAnnotationMarks.begin(),
+ m_vAnnotationMarks.end(),
+ [&rPos] (const ::sw::mark::MarkBase *const pMark) { return pMark->IsCoveringPosition(rPos); } );
+ if (pAnnotationMark == m_vAnnotationMarks.end())
+ return nullptr;
+ return *pAnnotationMark;
+ }
+
+ // finds the first that is starting after
+ IDocumentMarkAccess::const_iterator_t MarkManager::findFirstAnnotationStartsAfter(const SwPosition& rPos) const
+ {
+ return std::upper_bound(
+ m_vAnnotationMarks.begin(),
+ m_vAnnotationMarks.end(),
+ rPos,
+ CompareIMarkStartsAfter());
+ }
+
+ // create helper bookmark for annotations on tracked deletions
+ ::sw::mark::IMark* MarkManager::makeAnnotationBookmark(const SwPaM& rPaM,
+ const OUString& rName,
+ const IDocumentMarkAccess::MarkType eType,
+ sw::mark::InsertMode const eMode,
+ SwPosition const*const pSepPos)
+ {
+ OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
+ return makeMark( rPaM, sAnnotationBookmarkName, eType, eMode, pSepPos);
+ }
+
+ // find helper bookmark of annotations on tracked deletions
+ IDocumentMarkAccess::const_iterator_t MarkManager::findAnnotationBookmark(const OUString& rName) const
+ {
+ OUString sAnnotationBookmarkName(rName + S_ANNOTATION_BOOKMARK);
+ return findBookmark(sAnnotationBookmarkName);
+ }
+
+ // restore text ranges of annotations on tracked deletions
+ // based on the helper bookmarks (which can survive I/O and hiding redlines)
+ void MarkManager::restoreAnnotationMarks(bool bDelete)
+ {
+ for (auto iter = getBookmarksBegin();
+ iter != getBookmarksEnd(); )
+ {
+ const OUString & rBookmarkName = (**iter).GetName();
+ sal_Int32 nPos;
+ if ( rBookmarkName.startsWith("__Annotation__") &&
+ (nPos = rBookmarkName.indexOf(S_ANNOTATION_BOOKMARK)) > -1 )
+ {
+ ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo());
+ IDocumentMarkAccess::const_iterator_t pMark = findAnnotationMark(rBookmarkName.copy(0, nPos));
+ if ( pMark != getAnnotationMarksEnd() )
+ {
+ const SwPaM aPam((**iter).GetMarkStart(), (**pMark).GetMarkEnd());
+ repositionMark(*pMark, aPam);
+ }
+ if (bDelete)
+ {
+ deleteMark(&**iter);
+ // this invalidates iter, have to start over...
+ iter = getBookmarksBegin();
+ }
+ else
+ ++iter;
+ }
+ else
+ ++iter;
+ }
+ }
+
+ OUString MarkManager::getUniqueMarkName(const OUString& rName) const
+ {
+ OSL_ENSURE(rName.getLength(),
+ "<MarkManager::getUniqueMarkName(..)> - a name should be proposed");
+ if( m_rDoc.IsInMailMerge())
+ {
+ OUString newName = rName + "MailMergeMark"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( m_vAllMarks.size() + 1 );
+ return newName;
+ }
+
+ if (lcl_FindMarkByName(rName, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
+ {
+ return rName;
+ }
+ OUString sTmp;
+
+ // try the name "<rName>XXX" (where XXX is a number starting from 1) unless there is
+ // an unused name. Due to performance-reasons (especially in mailmerge-scenarios) there
+ // is a map m_aMarkBasenameMapUniqueOffset which holds the next possible offset (XXX) for
+ // rName (so there is no need to test for nCnt-values smaller than the offset).
+ sal_Int32 nCnt = 1;
+ MarkBasenameMapUniqueOffset_t::const_iterator aIter = m_aMarkBasenameMapUniqueOffset.find(rName);
+ if(aIter != m_aMarkBasenameMapUniqueOffset.end()) nCnt = aIter->second;
+ OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rName);
+ while(nCnt < SAL_MAX_INT32)
+ {
+ sTmp = aPrefix + OUString::number(nCnt);
+ nCnt++;
+ if (lcl_FindMarkByName(sTmp, m_vAllMarks.begin(), m_vAllMarks.end()) == m_vAllMarks.end())
+ {
+ break;
+ }
+ }
+ m_aMarkBasenameMapUniqueOffset[rName] = nCnt;
+
+ return sTmp;
+ }
+
+ void MarkManager::assureSortedMarkContainers() const
+ {
+ const_cast< MarkManager* >(this)->sortMarks();
+ }
+
+ void MarkManager::sortSubsetMarks()
+ {
+ sort(m_vBookmarks.begin(), m_vBookmarks.end(), &lcl_MarkOrderingByStart);
+ sort(m_vFieldmarks.begin(), m_vFieldmarks.end(), &lcl_MarkOrderingByStart);
+ sort(m_vAnnotationMarks.begin(), m_vAnnotationMarks.end(), &lcl_MarkOrderingByStart);
+ }
+
+ void MarkManager::sortMarks()
+ {
+ sort(m_vAllMarks.begin(), m_vAllMarks.end(), &lcl_MarkOrderingByStart);
+ sortSubsetMarks();
+ }
+
+void MarkManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ struct
+ {
+ const char* pName;
+ const container_t* pContainer;
+ } aContainers[] =
+ {
+ // UNO marks are only part of all marks.
+ {"allmarks", &m_vAllMarks},
+ {"bookmarks", &m_vBookmarks},
+ {"fieldmarks", &m_vFieldmarks},
+ {"annotationmarks", &m_vAnnotationMarks}
+ };
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("MarkManager"));
+ for (const auto & rContainer : aContainers)
+ {
+ if (!rContainer.pContainer->empty())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(rContainer.pName));
+ for (auto it = rContainer.pContainer->begin(); it != rContainer.pContainer->end(); ++it)
+ (*it)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+} // namespace ::sw::mark
+
+namespace
+{
+ bool lcl_Greater( const SwPosition& rPos, const SwNodeIndex& rNdIdx, const SwIndex* pIdx )
+ {
+ return rPos.nNode > rNdIdx || ( pIdx && rPos.nNode == rNdIdx && rPos.nContent > pIdx->GetIndex() );
+ }
+}
+
+// IDocumentMarkAccess for SwDoc
+IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess()
+ { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
+
+const IDocumentMarkAccess* SwDoc::getIDocumentMarkAccess() const
+ { return static_cast< IDocumentMarkAccess* >(mpMarkManager.get()); }
+
+SaveBookmark::SaveBookmark(
+ const IMark& rBkmk,
+ const SwNodeIndex & rMvPos,
+ const SwIndex* pIdx)
+ : m_aName(rBkmk.GetName())
+ , m_bHidden(false)
+ , m_eOrigBkmType(IDocumentMarkAccess::GetType(rBkmk))
+{
+ const IBookmark* const pBookmark = dynamic_cast< const IBookmark* >(&rBkmk);
+ if(pBookmark)
+ {
+ m_aShortName = pBookmark->GetShortName();
+ m_aCode = pBookmark->GetKeyCode();
+ m_bHidden = pBookmark->IsHidden();
+ m_aHideCondition = pBookmark->GetHideCondition();
+
+ ::sfx2::Metadatable const*const pMetadatable(
+ dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
+ if (pMetadatable)
+ {
+ m_pMetadataUndo = pMetadatable->CreateUndo();
+ }
+ }
+ m_nNode1 = rBkmk.GetMarkPos().nNode.GetIndex();
+ m_nContent1 = rBkmk.GetMarkPos().nContent.GetIndex();
+
+ m_nNode1 -= rMvPos.GetIndex();
+ if(pIdx && !m_nNode1)
+ m_nContent1 -= pIdx->GetIndex();
+
+ if(rBkmk.IsExpanded())
+ {
+ m_nNode2 = rBkmk.GetOtherMarkPos().nNode.GetIndex();
+ m_nContent2 = rBkmk.GetOtherMarkPos().nContent.GetIndex();
+
+ m_nNode2 -= rMvPos.GetIndex();
+ if(pIdx && !m_nNode2)
+ m_nContent2 -= pIdx->GetIndex();
+ }
+ else
+ {
+ m_nNode2 = NODE_OFFSET_MAX;
+ m_nContent2 = -1;
+ }
+}
+
+void SaveBookmark::SetInDoc(
+ SwDoc* pDoc,
+ const SwNodeIndex& rNewPos,
+ const SwIndex* pIdx)
+{
+ SwPaM aPam(rNewPos.GetNode());
+ if(pIdx)
+ aPam.GetPoint()->nContent = *pIdx;
+
+ if(NODE_OFFSET_MAX != m_nNode2)
+ {
+ aPam.SetMark();
+
+ aPam.GetMark()->nNode += m_nNode2;
+ if(pIdx && !m_nNode2)
+ aPam.GetMark()->nContent += m_nContent2;
+ else
+ aPam.GetMark()->nContent.Assign(aPam.GetContentNode(false), m_nContent2);
+ }
+
+ aPam.GetPoint()->nNode += m_nNode1;
+
+ if(pIdx && !m_nNode1)
+ aPam.GetPoint()->nContent += m_nContent1;
+ else
+ aPam.GetPoint()->nContent.Assign(aPam.GetContentNode(), m_nContent1);
+
+ if(aPam.HasMark()
+ && !CheckNodesRange(aPam.GetPoint()->nNode, aPam.GetMark()->nNode, true))
+ return;
+
+ ::sw::mark::IBookmark* const pBookmark = dynamic_cast<::sw::mark::IBookmark*>(
+ pDoc->getIDocumentMarkAccess()->makeMark(aPam, m_aName,
+ m_eOrigBkmType, sw::mark::InsertMode::CopyText));
+ if(!pBookmark)
+ return;
+
+ pBookmark->SetKeyCode(m_aCode);
+ pBookmark->SetShortName(m_aShortName);
+ pBookmark->Hide(m_bHidden);
+ pBookmark->SetHideCondition(m_aHideCondition);
+
+ if (m_pMetadataUndo)
+ {
+ ::sfx2::Metadatable * const pMeta(
+ dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
+ assert(pMeta && "metadata undo, but not metadatable?");
+ if (pMeta)
+ {
+ pMeta->RestoreMetadata(m_pMetadataUndo);
+ }
+ }
+}
+
+// DelBookmarks
+
+void DelBookmarks(
+ const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd,
+ std::vector<SaveBookmark> * pSaveBkmk,
+ const SwIndex* pSttIdx,
+ const SwIndex* pEndIdx)
+{
+ // illegal range ??
+ if(rStt.GetIndex() > rEnd.GetIndex()
+ || (rStt == rEnd && (!pSttIdx || !pEndIdx || pSttIdx->GetIndex() >= pEndIdx->GetIndex())))
+ return;
+ SwDoc& rDoc = rStt.GetNode().GetDoc();
+
+ rDoc.getIDocumentMarkAccess()->deleteMarks(rStt, rEnd, pSaveBkmk, pSttIdx, pEndIdx);
+
+ // Copy all Redlines which are in the move area into an array
+ // which holds all position information as offset.
+ // Assignment happens after moving.
+ SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ // Is at position?
+ SwPosition *const pRStt = pRedl->Start();
+ SwPosition *const pREnd = pRedl->End();
+
+ if( lcl_Greater( *pRStt, rStt, pSttIdx ) && lcl_Lower( *pRStt, rEnd, pEndIdx ))
+ {
+ pRStt->nNode = rEnd;
+ if( pEndIdx )
+ pRStt->nContent = *pEndIdx;
+ else
+ {
+ bool bStt = true;
+ SwContentNode* pCNd = pRStt->nNode.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = rDoc.GetNodes().GoNext( &pRStt->nNode );
+ if (!pCNd)
+ {
+ bStt = false;
+ pRStt->nNode = rStt;
+ pCNd = SwNodes::GoPrevious( &pRStt->nNode );
+ if( !pCNd )
+ {
+ pRStt->nNode = pREnd->nNode;
+ pCNd = pRStt->nNode.GetNode().GetContentNode();
+ }
+ }
+ pRStt->nContent.Assign( pCNd, bStt ? 0 : pCNd->Len() );
+ }
+ }
+ if( lcl_Greater( *pREnd, rStt, pSttIdx ) && lcl_Lower( *pREnd, rEnd, pEndIdx ))
+ {
+ pREnd->nNode = rStt;
+ if( pSttIdx )
+ pREnd->nContent = *pSttIdx;
+ else
+ {
+ bool bStt = false;
+ SwContentNode* pCNd = pREnd->nNode.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = SwNodes::GoPrevious( &pREnd->nNode );
+ if( !pCNd )
+ {
+ bStt = true;
+ pREnd->nNode = rEnd;
+ pCNd = rDoc.GetNodes().GoNext( &pREnd->nNode );
+ if( !pCNd )
+ {
+ pREnd->nNode = pRStt->nNode;
+ pCNd = pREnd->nNode.GetNode().GetContentNode();
+ }
+ }
+ pREnd->nContent.Assign( pCNd, bStt ? 0 : pCNd->Len() );
+ }
+ }
+ }
+}
+
+namespace sw {
+
+SwInsText MakeSwInsText(SwTextNode & rNode, sal_Int32 const nPos, sal_Int32 const nLen)
+{
+ SwCursor cursor(SwPosition(rNode, nPos), nullptr);
+ bool isInsideFieldmarkCommand(false);
+ bool isInsideFieldmarkResult(false);
+ while (auto const*const pMark = rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(*cursor.GetPoint()))
+ {
+ if (sw::mark::FindFieldSep(*pMark) < *cursor.GetPoint())
+ {
+ isInsideFieldmarkResult = true;
+ }
+ else
+ {
+ isInsideFieldmarkCommand = true;
+ }
+ *cursor.GetPoint() = pMark->GetMarkStart();
+ if (!cursor.Left(1))
+ {
+ break;
+ }
+ }
+ return SwInsText(nPos, nLen, isInsideFieldmarkCommand, isInsideFieldmarkResult);
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docchart.cxx b/sw/source/core/doc/docchart.cxx
new file mode 100644
index 000000000..27d94c09a
--- /dev/null
+++ b/sw/source/core/doc/docchart.cxx
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <ndindex.hxx>
+#include <swtable.hxx>
+#include <viewsh.hxx>
+#include <ndole.hxx>
+#include <swtblfmt.hxx>
+#include <tblsel.hxx>
+#include <frameformats.hxx>
+#include <unochart.hxx>
+#include <osl/diagnose.h>
+
+void SwTable::UpdateCharts() const
+{
+ GetFrameFormat()->GetDoc()->UpdateCharts( GetFrameFormat()->GetName() );
+}
+
+bool SwTable::IsTableComplexForChart( const OUString& rSelection ) const
+{
+ const SwTableBox* pSttBox, *pEndBox;
+ if( 2 < rSelection.getLength() )
+ {
+ const sal_Int32 nSeparator {rSelection.indexOf( ':' )};
+ OSL_ENSURE( -1 != nSeparator, "no valid selection" );
+
+ // Remove brackets at the beginning and from the end
+ const sal_Int32 nOffset {'<' == rSelection[0] ? 1 : 0};
+ const sal_Int32 nLength {'>' == rSelection[ rSelection.getLength()-1 ]
+ ? rSelection.getLength()-1 : rSelection.getLength()};
+
+ pSttBox = GetTableBox(rSelection.copy( nOffset, nSeparator - nOffset ));
+ pEndBox = GetTableBox(rSelection.copy( nSeparator+1, nLength - (nSeparator+1) ));
+ }
+ else
+ {
+ const SwTableLines* pLns = &GetTabLines();
+ pSttBox = (*pLns)[ 0 ]->GetTabBoxes().front();
+ while( !pSttBox->GetSttNd() )
+ // Until the Content Box!
+ pSttBox = pSttBox->GetTabLines().front()->GetTabBoxes().front();
+
+ const SwTableBoxes* pBoxes = &pLns->back()->GetTabBoxes();
+ pEndBox = pBoxes->back();
+ while( !pEndBox->GetSttNd() )
+ {
+ // Until the Content Box!
+ pLns = &pEndBox->GetTabLines();
+ pBoxes = &pLns->back()->GetTabBoxes();
+ pEndBox = pBoxes->back();
+ }
+ }
+
+ return !pSttBox || !pEndBox || !::ChkChartSel( *pSttBox->GetSttNd(),
+ *pEndBox->GetSttNd() );
+}
+
+void SwDoc::DoUpdateAllCharts()
+{
+ SwViewShell* pVSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( !pVSh )
+ return;
+
+ const SwFrameFormats& rTableFormats = *GetTableFrameFormats();
+ for( size_t n = 0; n < rTableFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFormat = rTableFormats[ n ];
+ if( SwTable* pTmpTable = SwTable::FindTable( pFormat ) )
+ if( const SwTableNode* pTableNd = pTmpTable->GetTableNode() )
+ if( pTableNd->GetNodes().IsDocNodes() )
+ {
+ UpdateCharts_( *pTmpTable, *pVSh );
+ }
+ }
+}
+
+void SwDoc::UpdateCharts_( const SwTable& rTable, SwViewShell const & rVSh ) const
+{
+ OUString aName( rTable.GetFrameFormat()->GetName() );
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwOLENode *pONd = aIdx.GetNode().GetOLENode();
+ if( pONd &&
+ aName == pONd->GetChartTableName() &&
+ pONd->getLayoutFrame( rVSh.GetLayout() ) )
+ {
+ // tdf#122995 for OLE/Charts in SW we do not (yet) have a refresh
+ // mechanism or embedding of the primitive representation, so this
+ // needs to be done locally here (simplest solution).
+ bool bImmediateMode(false);
+
+ if(pONd->IsChart())
+ {
+ // refresh to trigger repaint
+ const SwRect aChartRect(pONd->FindLayoutRect());
+ if(!aChartRect.IsEmpty())
+ const_cast<SwViewShell &>(rVSh).InvalidateWindows(aChartRect);
+
+ // forced refresh of the chart's primitive representation
+ pONd->GetOLEObj().resetBufferedData();
+
+ // InvalidateTable using the Immediate-Mode, else the chart will
+ // not yet know that it is invalidated at the next repaint and create
+ // the same graphical representation again
+ bImmediateMode = true;
+ }
+
+ SwChartDataProvider *pPCD = getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD)
+ pPCD->InvalidateTable( &rTable, bImmediateMode );
+ // following this the framework will now take care of repainting
+ // the chart or it's replacement image...
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+}
+
+void SwDoc::UpdateCharts( const OUString& rName ) const
+{
+ SwTable* pTmpTable = SwTable::FindTable( FindTableFormatByName( rName ) );
+ if( pTmpTable )
+ {
+ SwViewShell const * pVSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if( pVSh )
+ UpdateCharts_( *pTmpTable, *pVSh );
+ }
+}
+
+void SwDoc::SetTableName( SwFrameFormat& rTableFormat, const OUString &rNewName )
+{
+ const OUString aOldName( rTableFormat.GetName() );
+
+ bool bNameFound = rNewName.isEmpty();
+ if( !bNameFound )
+ {
+ const SwFrameFormats& rTable = *GetTableFrameFormats();
+ for( size_t i = rTable.size(); i; )
+ {
+ const SwFrameFormat* pFormat = rTable[ --i ];
+ if( !pFormat->IsDefault() &&
+ pFormat->GetName() == rNewName && IsUsed( *pFormat ) )
+ {
+ bNameFound = true;
+ break;
+ }
+ }
+ }
+
+ if( !bNameFound )
+ rTableFormat.SetName( rNewName, true );
+ else
+ rTableFormat.SetName( GetUniqueTableName(), true );
+
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwOLENode *pNd = aIdx.GetNode().GetOLENode();
+ if( pNd && aOldName == pNd->GetChartTableName() )
+ {
+ pNd->SetChartTableName( rNewName );
+
+ SwTable* pTable = SwTable::FindTable( &rTableFormat );
+ SwChartDataProvider *pPCD = getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD)
+ pPCD->InvalidateTable( pTable );
+ // following this the framework will now take care of repainting
+ // the chart or it's replacement image...
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+ getIDocumentState().SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doccomp.cxx b/sw/source/core/doc/doccomp.cxx
new file mode 100644
index 000000000..bc6e3fc0c
--- /dev/null
+++ b/sw/source/core/doc/doccomp.cxx
@@ -0,0 +1,2695 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <swmodule.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <redline.hxx>
+#include <UndoRedline.hxx>
+#include <section.hxx>
+#include <tox.hxx>
+#include <docsh.hxx>
+#include <fmtcntnt.hxx>
+#include <modcfg.hxx>
+#include <frameformats.hxx>
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+using namespace ::com::sun::star;
+
+using std::vector;
+
+namespace {
+
+class SwCompareLine final
+{
+ const SwNode* m_pNode;
+public:
+ explicit SwCompareLine( const SwNode& rNd ) : m_pNode( &rNd ) {}
+ SwCompareLine() : m_pNode( nullptr ) {}
+
+ sal_uLong GetHashValue() const;
+ bool Compare( const SwCompareLine& rLine ) const;
+
+ static sal_uLong GetTextNodeHashValue( const SwTextNode& rNd, sal_uLong nVal );
+ static bool CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd );
+ static bool CompareTextNd( const SwTextNode& rDstNd,
+ const SwTextNode& rSrcNd );
+
+ bool ChangesInLine( const SwCompareLine& rLine,
+ std::unique_ptr<SwPaM>& rpInsRing, std::unique_ptr<SwPaM>& rpDelRing ) const;
+
+ const SwNode& GetNode() const { return *m_pNode; }
+
+ const SwNode& GetEndNode() const;
+
+ // for debugging
+ OUString GetText() const;
+};
+
+
+class CompareData
+{
+protected:
+ SwDoc& m_rDoc;
+private:
+ std::unique_ptr<size_t[]> m_pIndex;
+ std::unique_ptr<bool[]> m_pChangedFlag;
+
+ std::unique_ptr<SwPaM> m_pInsertRing, m_pDelRing;
+
+ static SwNodeOffset PrevIdx( const SwNode* pNd );
+ static SwNodeOffset NextIdx( const SwNode* pNd );
+
+ vector<SwCompareLine> m_aLines;
+ bool m_bRecordDiff;
+
+ // Truncate beginning and end and add all others to the LinesArray
+ void CheckRanges( CompareData& );
+
+ virtual const SwNode& GetEndOfContent() = 0;
+
+public:
+ CompareData(SwDoc& rD, bool bRecordDiff)
+ : m_rDoc( rD )
+ , m_bRecordDiff(bRecordDiff)
+ {
+ }
+ virtual ~CompareData();
+
+ // Are there differences?
+ bool HasDiffs( const CompareData& rData ) const;
+
+ // Triggers the comparison and creation of two documents
+ void CompareLines( CompareData& rData );
+ // Display the differences - calls the methods ShowInsert and ShowDelete.
+ // These are passed the start and end line number.
+ // Displaying the actually content is to be handled by the subclass!
+ sal_uLong ShowDiffs( const CompareData& rData );
+
+ void ShowInsert( sal_uLong nStt, sal_uLong nEnd );
+ void ShowDelete( const CompareData& rData, sal_uLong nStt,
+ sal_uLong nEnd, sal_uLong nInsPos );
+ void CheckForChangesInLine( const CompareData& rData,
+ sal_uLong nStt, sal_uLong nEnd,
+ sal_uLong nThisStt, sal_uLong nThisEnd );
+
+ // Set non-ambiguous index for a line. Same lines have the same index, even in the other CompareData!
+ void SetIndex( size_t nLine, size_t nIndex );
+ size_t GetIndex( size_t nLine ) const
+ { return nLine < m_aLines.size() ? m_pIndex[ nLine ] : 0; }
+
+ // Set/get of a line has changed
+ void SetChanged( size_t nLine, bool bFlag = true );
+ bool GetChanged( size_t nLine ) const
+ {
+ return (m_pChangedFlag && nLine < m_aLines.size())
+ && m_pChangedFlag[ nLine ];
+ }
+
+ size_t GetLineCount() const { return m_aLines.size(); }
+ const SwCompareLine& GetLine( size_t nLine ) const
+ { return m_aLines[ nLine ]; }
+ void InsertLine( SwCompareLine aLine )
+ { m_aLines.push_back( aLine ); }
+
+ void SetRedlinesToDoc( bool bUseDocInfo );
+};
+
+class CompareMainText : public CompareData
+{
+public:
+ CompareMainText(SwDoc &rD, bool bRecordDiff)
+ : CompareData(rD, bRecordDiff)
+ {
+ }
+
+ virtual const SwNode& GetEndOfContent() override
+ {
+ return m_rDoc.GetNodes().GetEndOfContent();
+ }
+};
+
+class CompareFrameFormatText : public CompareData
+{
+ const SwNodeIndex &m_rIndex;
+public:
+ CompareFrameFormatText(SwDoc &rD, const SwNodeIndex &rIndex)
+ : CompareData(rD, true/*bRecordDiff*/)
+ , m_rIndex(rIndex)
+ {
+ }
+
+ virtual const SwNode& GetEndOfContent() override
+ {
+ return *m_rIndex.GetNode().EndOfSectionNode();
+ }
+};
+
+class Hash
+{
+ struct HashData
+ {
+ sal_uLong nNext, nHash;
+ SwCompareLine aLine;
+
+ HashData()
+ : nNext( 0 ), nHash( 0 ) {}
+ };
+
+ std::unique_ptr<sal_uLong[]> m_pHashArr;
+ std::unique_ptr<HashData[]> m_pDataArr;
+ sal_uLong m_nCount, m_nPrime;
+
+public:
+ explicit Hash( sal_uLong nSize );
+
+ void CalcHashValue( CompareData& rData );
+
+ sal_uLong GetCount() const { return m_nCount; }
+};
+
+class Compare
+{
+public:
+ class MovedData
+ {
+ std::unique_ptr<sal_uLong[]> m_pIndex;
+ std::unique_ptr<sal_uLong[]> m_pLineNum;
+ sal_uLong m_nCount;
+
+ public:
+ MovedData( CompareData& rData, const char* pDiscard );
+
+ sal_uLong GetIndex( sal_uLong n ) const { return m_pIndex[ n ]; }
+ sal_uLong GetLineNum( sal_uLong n ) const { return m_pLineNum[ n ]; }
+ sal_uLong GetCount() const { return m_nCount; }
+ };
+
+private:
+ /// Look for the moved lines
+ class CompareSequence
+ {
+ CompareData &m_rData1, &m_rData2;
+ const MovedData &m_rMoved1, &m_rMoved2;
+ std::unique_ptr<tools::Long[]> m_pMemory;
+ tools::Long *m_pFDiag, *m_pBDiag;
+
+ void Compare( sal_uLong nStt1, sal_uLong nEnd1, sal_uLong nStt2, sal_uLong nEnd2 );
+ sal_uLong CheckDiag( sal_uLong nStt1, sal_uLong nEnd1,
+ sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost );
+ public:
+ CompareSequence( CompareData& rD1, CompareData& rD2,
+ const MovedData& rMD1, const MovedData& rMD2 );
+ };
+
+ static void CountDifference( const CompareData& rData, sal_uLong* pCounts );
+ static void SetDiscard( const CompareData& rData,
+ char* pDiscard, const sal_uLong* pCounts );
+ static void CheckDiscard( sal_uLong nLen, char* pDiscard );
+ static void ShiftBoundaries( CompareData& rData1, CompareData& rData2 );
+
+public:
+ Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 );
+};
+
+class ArrayComparator
+{
+public:
+ virtual bool Compare( int nIdx1, int nIdx2 ) const = 0;
+ virtual int GetLen1() const = 0;
+ virtual int GetLen2() const = 0;
+ virtual ~ArrayComparator() {}
+};
+
+/// Consider two lines equal if similar enough (e.g. look like different
+/// versions of the same paragraph)
+class LineArrayComparator : public ArrayComparator
+{
+private:
+ int m_nLen1, m_nLen2;
+ const CompareData &m_rData1, &m_rData2;
+ int m_nFirst1, m_nFirst2;
+
+public:
+ LineArrayComparator( const CompareData &rD1, const CompareData &rD2,
+ int nStt1, int nEnd1, int nStt2, int nEnd2 );
+
+ virtual bool Compare( int nIdx1, int nIdx2 ) const override;
+ virtual int GetLen1() const override { return m_nLen1; }
+ virtual int GetLen2() const override { return m_nLen2; }
+};
+
+class WordArrayComparator : public ArrayComparator
+{
+private:
+ const SwTextNode *m_pTextNode1, *m_pTextNode2;
+ std::unique_ptr<int[]> m_pPos1, m_pPos2;
+ int m_nCount1, m_nCount2; // number of words
+
+ static void CalcPositions( int *pPos, const SwTextNode *pTextNd, int &nCnt );
+
+public:
+ WordArrayComparator( const SwTextNode *pNode1, const SwTextNode *pNode2 );
+
+ virtual bool Compare( int nIdx1, int nIdx2 ) const override;
+ virtual int GetLen1() const override { return m_nCount1; }
+ virtual int GetLen2() const override { return m_nCount2; }
+ int GetCharSequence( const int *pWordLcs1, const int *pWordLcs2,
+ int *pSubseq1, int *pSubseq2, int nLcsLen );
+};
+
+class CharArrayComparator : public ArrayComparator
+{
+private:
+ const SwTextNode *m_pTextNode1, *m_pTextNode2;
+
+public:
+ CharArrayComparator( const SwTextNode *pNode1, const SwTextNode *pNode2 )
+ : m_pTextNode1( pNode1 ), m_pTextNode2( pNode2 )
+ {
+ }
+
+ virtual bool Compare( int nIdx1, int nIdx2 ) const override;
+ virtual int GetLen1() const override { return m_pTextNode1->GetText().getLength(); }
+ virtual int GetLen2() const override { return m_pTextNode2->GetText().getLength(); }
+};
+
+/// Options set in Tools->Options->Writer->Comparison
+struct CmpOptionsContainer
+{
+ SwCompareMode eCmpMode;
+ int nIgnoreLen;
+ bool bUseRsid;
+};
+
+}
+
+static CmpOptionsContainer CmpOptions;
+
+namespace {
+
+class CommonSubseq
+{
+private:
+ std::unique_ptr<int[]> m_pData;
+
+protected:
+ ArrayComparator &m_rComparator;
+
+ CommonSubseq( ArrayComparator &rComparator, int nMaxSize )
+ : m_rComparator( rComparator )
+ {
+ m_pData.reset( new int[ nMaxSize ] );
+ }
+
+ int FindLCS( int *pLcs1, int *pLcs2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 );
+
+public:
+ static int IgnoreIsolatedPieces( int *pLcs1, int *pLcs2, int nLen1, int nLen2,
+ int nLcsLen, int nPieceLen );
+};
+
+/// Use Hirschberg's algorithm to find LCS in linear space
+class LgstCommonSubseq: public CommonSubseq
+{
+private:
+ static const int CUTOFF = 1<<20; // Stop recursion at this value
+
+ std::unique_ptr<int[]> m_pL1, m_pL2;
+ std::unique_ptr<int[]> m_pBuff1, m_pBuff2;
+
+ void FindL( int *pL, int nStt1, int nEnd1, int nStt2, int nEnd2 );
+ int HirschbergLCS( int *pLcs1, int *pLcs2, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 );
+
+public:
+ explicit LgstCommonSubseq( ArrayComparator &rComparator );
+
+ int Find( int *pSubseq1, int *pSubseq2 );
+};
+
+/// Find a common subsequence in linear time
+class FastCommonSubseq: private CommonSubseq
+{
+private:
+ static const int CUTOFF = 2056;
+
+ int FindFastCS( int *pSeq1, int *pSeq2, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 );
+
+public:
+ explicit FastCommonSubseq( ArrayComparator &rComparator )
+ : CommonSubseq( rComparator, CUTOFF )
+ {
+ }
+
+ int Find( int *pSubseq1, int *pSubseq2 )
+ {
+ return FindFastCS( pSubseq1, pSubseq2, 0, m_rComparator.GetLen1(),
+ 0, m_rComparator.GetLen2() );
+ }
+};
+
+}
+
+CompareData::~CompareData()
+{
+ if( m_pDelRing )
+ {
+ while( m_pDelRing->GetNext() != m_pDelRing.get() )
+ delete m_pDelRing->GetNext();
+ m_pDelRing.reset();
+ }
+ if( m_pInsertRing )
+ {
+ while( m_pInsertRing->GetNext() != m_pInsertRing.get() )
+ delete m_pInsertRing->GetNext();
+ m_pInsertRing.reset();
+ }
+}
+
+void CompareData::SetIndex( size_t nLine, size_t nIndex )
+{
+ if( !m_pIndex )
+ {
+ m_pIndex.reset( new size_t[ m_aLines.size() ] );
+ memset( m_pIndex.get(), 0, m_aLines.size() * sizeof( size_t ) );
+ }
+ if( nLine < m_aLines.size() )
+ m_pIndex[ nLine ] = nIndex;
+}
+
+void CompareData::SetChanged( size_t nLine, bool bFlag )
+{
+ if( !m_pChangedFlag )
+ {
+ m_pChangedFlag.reset( new bool[ m_aLines.size() +1 ] );
+ memset( m_pChangedFlag.get(), 0, (m_aLines.size() +1) * sizeof( bool ) );
+ }
+ if( nLine < m_aLines.size() )
+ m_pChangedFlag[ nLine ] = bFlag;
+}
+
+void CompareData::CompareLines( CompareData& rData )
+{
+ CheckRanges( rData );
+
+ sal_uLong nDifferent;
+ {
+ Hash aH( GetLineCount() + rData.GetLineCount() + 1 );
+ aH.CalcHashValue( *this );
+ aH.CalcHashValue( rData );
+ nDifferent = aH.GetCount();
+ }
+ {
+ Compare aComp( nDifferent, *this, rData );
+ }
+}
+
+sal_uLong CompareData::ShowDiffs( const CompareData& rData )
+{
+ sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount();
+ sal_uLong nStt1 = 0, nStt2 = 0;
+ sal_uLong nCnt = 0;
+
+ while( nStt1 < nLen1 || nStt2 < nLen2 )
+ {
+ if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) )
+ {
+ // Find a region of different lines between two pairs of identical
+ // lines.
+ sal_uLong nSav1 = nStt1, nSav2 = nStt2;
+ while( nStt1 < nLen1 && rData.GetChanged( nStt1 )) ++nStt1;
+ while( nStt2 < nLen2 && GetChanged( nStt2 )) ++nStt2;
+
+ if (m_bRecordDiff)
+ {
+ // Check if there are changed lines (only slightly different) and
+ // compare them in detail.
+ CheckForChangesInLine( rData, nSav1, nStt1, nSav2, nStt2 );
+ }
+
+ ++nCnt;
+ }
+ ++nStt1;
+ ++nStt2;
+ }
+ return nCnt;
+}
+
+bool CompareData::HasDiffs( const CompareData& rData ) const
+{
+ bool bRet = false;
+ sal_uLong nLen1 = rData.GetLineCount(), nLen2 = GetLineCount();
+ sal_uLong nStt1 = 0, nStt2 = 0;
+
+ while( nStt1 < nLen1 || nStt2 < nLen2 )
+ {
+ if( rData.GetChanged( nStt1 ) || GetChanged( nStt2 ) )
+ {
+ bRet = true;
+ break;
+ }
+ ++nStt1;
+ ++nStt2;
+ }
+ return bRet;
+}
+
+Hash::Hash( sal_uLong nSize )
+ : m_nCount(1)
+{
+
+ static const sal_uLong primes[] =
+ {
+ 509,
+ 1021,
+ 2039,
+ 4093,
+ 8191,
+ 16381,
+ 32749,
+ 65521,
+ 131071,
+ 262139,
+ 524287,
+ 1048573,
+ 2097143,
+ 4194301,
+ 8388593,
+ 16777213,
+ 33554393,
+ 67108859, /* Preposterously large . . . */
+ 134217689,
+ 268435399,
+ 536870909,
+ 1073741789,
+ 2147483647,
+ 0
+ };
+ int i;
+
+ m_pDataArr.reset( new HashData[ nSize ] );
+ m_pDataArr[0].nNext = 0;
+ m_pDataArr[0].nHash = 0;
+ m_nPrime = primes[0];
+
+ for( i = 0; primes[i] < nSize / 3; i++)
+ if( !primes[i] )
+ {
+ m_pHashArr = nullptr;
+ return;
+ }
+ m_nPrime = primes[ i ];
+ m_pHashArr.reset( new sal_uLong[ m_nPrime ] );
+ memset( m_pHashArr.get(), 0, m_nPrime * sizeof( sal_uLong ) );
+}
+
+void Hash::CalcHashValue( CompareData& rData )
+{
+ if( !m_pHashArr )
+ return;
+
+ for( size_t n = 0; n < rData.GetLineCount(); ++n )
+ {
+ const SwCompareLine aLine = rData.GetLine( n );
+ sal_uLong nH = aLine.GetHashValue();
+
+ sal_uLong* pFound = &m_pHashArr[ nH % m_nPrime ];
+ size_t i;
+ for( i = *pFound; ; i = m_pDataArr[i].nNext )
+ if( !i )
+ {
+ i = m_nCount++;
+ m_pDataArr[i].nNext = *pFound;
+ m_pDataArr[i].nHash = nH;
+ m_pDataArr[i].aLine = aLine;
+ *pFound = i;
+ break;
+ }
+ else if( m_pDataArr[i].nHash == nH &&
+ m_pDataArr[i].aLine.Compare( aLine ))
+ break;
+
+ rData.SetIndex( n, i );
+ }
+}
+
+Compare::Compare( sal_uLong nDiff, CompareData& rData1, CompareData& rData2 )
+{
+ std::unique_ptr<MovedData> pMD1, pMD2;
+ // Look for the differing lines
+ {
+ std::unique_ptr<char[]> pDiscard1( new char[ rData1.GetLineCount() ] );
+ std::unique_ptr<char[]> pDiscard2( new char[ rData2.GetLineCount() ] );
+
+ std::unique_ptr<sal_uLong[]> pCount1(new sal_uLong[ nDiff ]);
+ std::unique_ptr<sal_uLong[]> pCount2(new sal_uLong[ nDiff ]);
+ memset( pCount1.get(), 0, nDiff * sizeof( sal_uLong ));
+ memset( pCount2.get(), 0, nDiff * sizeof( sal_uLong ));
+
+ // find indices in CompareData which have been assigned multiple times
+ CountDifference( rData1, pCount1.get() );
+ CountDifference( rData2, pCount2.get() );
+
+ // All which occur only once now have either been inserted or deleted.
+ // All which are also contained in the other one have been moved.
+ SetDiscard( rData1, pDiscard1.get(), pCount2.get() );
+ SetDiscard( rData2, pDiscard2.get(), pCount1.get() );
+
+ CheckDiscard( rData1.GetLineCount(), pDiscard1.get() );
+ CheckDiscard( rData2.GetLineCount(), pDiscard2.get() );
+
+ pMD1.reset(new MovedData( rData1, pDiscard1.get() ));
+ pMD2.reset(new MovedData( rData2, pDiscard2.get() ));
+ }
+
+ {
+ CompareSequence aTmp( rData1, rData2, *pMD1, *pMD2 );
+ }
+
+ ShiftBoundaries( rData1, rData2 );
+}
+
+void Compare::CountDifference( const CompareData& rData, sal_uLong* pCounts )
+{
+ sal_uLong nLen = rData.GetLineCount();
+ for( sal_uLong n = 0; n < nLen; ++n )
+ {
+ sal_uLong nIdx = rData.GetIndex( n );
+ ++pCounts[ nIdx ];
+ }
+}
+
+void Compare::SetDiscard( const CompareData& rData,
+ char* pDiscard, const sal_uLong* pCounts )
+{
+ const sal_uLong nLen = rData.GetLineCount();
+
+ // calculate Max with respect to the line count
+ sal_uLong nMax = 5;
+
+ for( sal_uLong n = nLen / 64; ( n = n >> 2 ) > 0; )
+ nMax <<= 1;
+
+ for( sal_uLong n = 0; n < nLen; ++n )
+ {
+ sal_uLong nIdx = rData.GetIndex( n );
+ if( nIdx )
+ {
+ nIdx = pCounts[ nIdx ];
+ pDiscard[ n ] = !nIdx ? 1 : nIdx > nMax ? 2 : 0;
+ }
+ else
+ pDiscard[ n ] = 0;
+ }
+}
+
+void Compare::CheckDiscard( sal_uLong nLen, char* pDiscard )
+{
+ for( sal_uLong n = 0; n < nLen; ++n )
+ {
+ if( 2 == pDiscard[ n ] )
+ pDiscard[n] = 0;
+ else if( pDiscard[ n ] )
+ {
+ sal_uLong j;
+ sal_uLong length;
+ sal_uLong provisional = 0;
+
+ /* Find end of this run of discardable lines.
+ Count how many are provisionally discardable. */
+ for (j = n; j < nLen; j++)
+ {
+ if( !pDiscard[j] )
+ break;
+ if( 2 == pDiscard[j] )
+ ++provisional;
+ }
+
+ /* Cancel provisional discards at end, and shrink the run. */
+ while( j > n && 2 == pDiscard[j - 1] )
+ {
+ pDiscard[ --j ] = 0;
+ --provisional;
+ }
+
+ /* Now we have the length of a run of discardable lines
+ whose first and last are not provisional. */
+ length = j - n;
+
+ /* If 1/4 of the lines in the run are provisional,
+ cancel discarding of all provisional lines in the run. */
+ if (provisional * 4 > length)
+ {
+ while (j > n)
+ if (pDiscard[--j] == 2)
+ pDiscard[j] = 0;
+ }
+ else
+ {
+ sal_uLong consec;
+ sal_uLong minimum = 1;
+ sal_uLong tem = length / 4;
+
+ /* MINIMUM is approximate square root of LENGTH/4.
+ A subrun of two or more provisionals can stand
+ when LENGTH is at least 16.
+ A subrun of 4 or more can stand when LENGTH >= 64. */
+ while ((tem = tem >> 2) > 0)
+ minimum *= 2;
+ minimum++;
+
+ /* Cancel any subrun of MINIMUM or more provisionals
+ within the larger run. */
+ for (j = 0, consec = 0; j < length; j++)
+ if (pDiscard[n + j] != 2)
+ consec = 0;
+ else if (minimum == ++consec)
+ /* Back up to start of subrun, to cancel it all. */
+ j -= consec;
+ else if (minimum < consec)
+ pDiscard[n + j] = 0;
+
+ /* Scan from beginning of run
+ until we find 3 or more nonprovisionals in a row
+ or until the first nonprovisional at least 8 lines in.
+ Until that point, cancel any provisionals. */
+ for (j = 0, consec = 0; j < length; j++)
+ {
+ if (j >= 8 && pDiscard[n + j] == 1)
+ break;
+ if (pDiscard[n + j] == 2)
+ {
+ consec = 0;
+ pDiscard[n + j] = 0;
+ }
+ else if (pDiscard[n + j] == 0)
+ consec = 0;
+ else
+ consec++;
+ if (consec == 3)
+ break;
+ }
+
+ /* I advances to the last line of the run. */
+ n += length - 1;
+
+ /* Same thing, from end. */
+ for (j = 0, consec = 0; j < length; j++)
+ {
+ if (j >= 8 && pDiscard[n - j] == 1)
+ break;
+ if (pDiscard[n - j] == 2)
+ {
+ consec = 0;
+ pDiscard[n - j] = 0;
+ }
+ else if (pDiscard[n - j] == 0)
+ consec = 0;
+ else
+ consec++;
+ if (consec == 3)
+ break;
+ }
+ }
+ }
+ }
+}
+
+Compare::MovedData::MovedData( CompareData& rData, const char* pDiscard )
+ : m_nCount( 0 )
+{
+ sal_uLong nLen = rData.GetLineCount();
+ sal_uLong n;
+
+ for( n = 0; n < nLen; ++n )
+ if( pDiscard[ n ] )
+ rData.SetChanged( n );
+ else
+ ++m_nCount;
+
+ if( m_nCount )
+ {
+ m_pIndex.reset( new sal_uLong[ m_nCount ] );
+ m_pLineNum.reset( new sal_uLong[ m_nCount ] );
+
+ for( n = 0, m_nCount = 0; n < nLen; ++n )
+ if( !pDiscard[ n ] )
+ {
+ m_pIndex[ m_nCount ] = rData.GetIndex( n );
+ m_pLineNum[ m_nCount++ ] = n;
+ }
+ }
+}
+
+/// Find the differing lines
+Compare::CompareSequence::CompareSequence(
+ CompareData& rD1, CompareData& rD2,
+ const MovedData& rMD1, const MovedData& rMD2 )
+ : m_rData1( rD1 ), m_rData2( rD2 ), m_rMoved1( rMD1 ), m_rMoved2( rMD2 )
+{
+ sal_uLong nSize = rMD1.GetCount() + rMD2.GetCount() + 3;
+ m_pMemory.reset( new tools::Long[ nSize * 2 ] );
+ m_pFDiag = m_pMemory.get() + ( rMD2.GetCount() + 1 );
+ m_pBDiag = m_pMemory.get() + ( nSize + rMD2.GetCount() + 1 );
+
+ Compare( 0, rMD1.GetCount(), 0, rMD2.GetCount() );
+}
+
+void Compare::CompareSequence::Compare( sal_uLong nStt1, sal_uLong nEnd1,
+ sal_uLong nStt2, sal_uLong nEnd2 )
+{
+ /* Slide down the bottom initial diagonal. */
+ while( nStt1 < nEnd1 && nStt2 < nEnd2 &&
+ m_rMoved1.GetIndex( nStt1 ) == m_rMoved2.GetIndex( nStt2 ))
+ {
+ ++nStt1;
+ ++nStt2;
+ }
+
+ /* Slide up the top initial diagonal. */
+ while( nEnd1 > nStt1 && nEnd2 > nStt2 &&
+ m_rMoved1.GetIndex( nEnd1 - 1 ) == m_rMoved2.GetIndex( nEnd2 - 1 ))
+ {
+ --nEnd1;
+ --nEnd2;
+ }
+
+ /* Handle simple cases. */
+ if( nStt1 == nEnd1 )
+ while( nStt2 < nEnd2 )
+ m_rData2.SetChanged( m_rMoved2.GetLineNum( nStt2++ ));
+
+ else if (nStt2 == nEnd2)
+ while (nStt1 < nEnd1)
+ m_rData1.SetChanged( m_rMoved1.GetLineNum( nStt1++ ));
+
+ else
+ {
+ sal_uLong c, d, b;
+
+ /* Find a point of correspondence in the middle of the files. */
+
+ d = CheckDiag( nStt1, nEnd1, nStt2, nEnd2, &c );
+ b = m_pBDiag[ std::make_signed_t<decltype(d)>(d) ];
+
+ if( 1 != c )
+ {
+ /* Use that point to split this problem into two subproblems. */
+ Compare( nStt1, b, nStt2, b - d );
+ /* This used to use f instead of b,
+ but that is incorrect!
+ It is not necessarily the case that diagonal d
+ has a snake from b to f. */
+ Compare( b, nEnd1, b - d, nEnd2 );
+ }
+ }
+}
+
+sal_uLong Compare::CompareSequence::CheckDiag( sal_uLong nStt1, sal_uLong nEnd1,
+ sal_uLong nStt2, sal_uLong nEnd2, sal_uLong* pCost )
+{
+ const tools::Long dmin = nStt1 - nEnd2; /* Minimum valid diagonal. */
+ const tools::Long dmax = nEnd1 - nStt2; /* Maximum valid diagonal. */
+ const tools::Long fmid = nStt1 - nStt2; /* Center diagonal of top-down search. */
+ const tools::Long bmid = nEnd1 - nEnd2; /* Center diagonal of bottom-up search. */
+
+ tools::Long fmin = fmid, fmax = fmid; /* Limits of top-down search. */
+ tools::Long bmin = bmid, bmax = bmid; /* Limits of bottom-up search. */
+
+ tools::Long c; /* Cost. */
+ tools::Long odd = (fmid - bmid) & 1; /* True if southeast corner is on an odd
+ diagonal with respect to the northwest. */
+
+ m_pFDiag[fmid] = nStt1;
+ m_pBDiag[bmid] = nEnd1;
+
+ for (c = 1;; ++c)
+ {
+ tools::Long d; /* Active diagonal. */
+
+ /* Extend the top-down search by an edit step in each diagonal. */
+ if (fmin > dmin)
+ m_pFDiag[--fmin - 1] = -1;
+ else
+ ++fmin;
+ if (fmax < dmax)
+ m_pFDiag[++fmax + 1] = -1;
+ else
+ --fmax;
+ for (d = fmax; d >= fmin; d -= 2)
+ {
+ tools::Long x, y, tlo = m_pFDiag[d - 1], thi = m_pFDiag[d + 1];
+
+ if (tlo >= thi)
+ x = tlo + 1;
+ else
+ x = thi;
+ y = x - d;
+ while( o3tl::make_unsigned(x) < nEnd1 && o3tl::make_unsigned(y) < nEnd2 &&
+ m_rMoved1.GetIndex( x ) == m_rMoved2.GetIndex( y ))
+ {
+ ++x;
+ ++y;
+ }
+ m_pFDiag[d] = x;
+ if( odd && bmin <= d && d <= bmax && m_pBDiag[d] <= m_pFDiag[d] )
+ {
+ *pCost = 2 * c - 1;
+ return d;
+ }
+ }
+
+ /* Similar extend the bottom-up search. */
+ if (bmin > dmin)
+ m_pBDiag[--bmin - 1] = INT_MAX;
+ else
+ ++bmin;
+ if (bmax < dmax)
+ m_pBDiag[++bmax + 1] = INT_MAX;
+ else
+ --bmax;
+ for (d = bmax; d >= bmin; d -= 2)
+ {
+ tools::Long x, y, tlo = m_pBDiag[d - 1], thi = m_pBDiag[d + 1];
+
+ if (tlo < thi)
+ x = tlo;
+ else
+ x = thi - 1;
+ y = x - d;
+ while( o3tl::make_unsigned(x) > nStt1 && o3tl::make_unsigned(y) > nStt2 &&
+ m_rMoved1.GetIndex( x - 1 ) == m_rMoved2.GetIndex( y - 1 ))
+ {
+ --x;
+ --y;
+ }
+ m_pBDiag[d] = x;
+ if (!odd && fmin <= d && d <= fmax && m_pBDiag[d] <= m_pFDiag[d])
+ {
+ *pCost = 2 * c;
+ return d;
+ }
+ }
+ }
+}
+
+namespace
+{
+ void lcl_ShiftBoundariesOneway( CompareData* const pData, CompareData const * const pOtherData)
+ {
+ sal_uLong i = 0;
+ sal_uLong j = 0;
+ sal_uLong i_end = pData->GetLineCount();
+ sal_uLong preceding = ULONG_MAX;
+ sal_uLong other_preceding = ULONG_MAX;
+
+ while (true)
+ {
+ sal_uLong start, other_start;
+
+ /* Scan forwards to find beginning of another run of changes.
+ Also keep track of the corresponding point in the other file. */
+
+ while( i < i_end && !pData->GetChanged( i ) )
+ {
+ while( pOtherData->GetChanged( j++ ))
+ /* Non-corresponding lines in the other file
+ will count as the preceding batch of changes. */
+ other_preceding = j;
+ i++;
+ }
+
+ if (i == i_end)
+ break;
+
+ start = i;
+ other_start = j;
+
+ while (true)
+ {
+ /* Now find the end of this run of changes. */
+
+ while( pData->GetChanged( ++i ))
+ ;
+
+ /* If the first changed line matches the following unchanged one,
+ and this run does not follow right after a previous run,
+ and there are no lines deleted from the other file here,
+ then classify the first changed line as unchanged
+ and the following line as changed in its place. */
+
+ /* You might ask, how could this run follow right after another?
+ Only because the previous run was shifted here. */
+
+ if( i != i_end &&
+ pData->GetIndex( start ) == pData->GetIndex( i ) &&
+ !pOtherData->GetChanged( j ) &&
+ start != preceding && other_start != other_preceding )
+ {
+ pData->SetChanged( start++, false );
+ pData->SetChanged( i );
+ /* Since one line-that-matches is now before this run
+ instead of after, we must advance in the other file
+ to keep in sync. */
+ ++j;
+ }
+ else
+ break;
+ }
+
+ preceding = i;
+ other_preceding = j;
+ }
+ }
+}
+
+void Compare::ShiftBoundaries( CompareData& rData1, CompareData& rData2 )
+{
+ lcl_ShiftBoundariesOneway(&rData1, &rData2);
+ lcl_ShiftBoundariesOneway(&rData2, &rData1);
+}
+
+sal_uLong SwCompareLine::GetHashValue() const
+{
+ sal_uLong nRet = 0;
+ switch( m_pNode->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ nRet = GetTextNodeHashValue( *m_pNode->GetTextNode(), nRet );
+ break;
+
+ case SwNodeType::Table:
+ {
+ const SwNode* pEndNd = m_pNode->EndOfSectionNode();
+ SwNodeIndex aIdx( *m_pNode );
+ while( &aIdx.GetNode() != pEndNd )
+ {
+ if( aIdx.GetNode().IsTextNode() )
+ nRet = GetTextNodeHashValue( *aIdx.GetNode().GetTextNode(), nRet );
+ ++aIdx;
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ {
+ OUString sStr( GetText() );
+ for( sal_Int32 n = 0; n < sStr.getLength(); ++n )
+ nRet = (nRet << 1) + sStr[ n ];
+ }
+ break;
+
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ // Fixed ID? Should never occur ...
+ break;
+ default: break;
+ }
+ return nRet;
+}
+
+const SwNode& SwCompareLine::GetEndNode() const
+{
+ const SwNode* pNd = m_pNode;
+ switch( m_pNode->GetNodeType() )
+ {
+ case SwNodeType::Table:
+ pNd = m_pNode->EndOfSectionNode();
+ break;
+
+ case SwNodeType::Section:
+ {
+ const SwSectionNode& rSNd = static_cast<const SwSectionNode&>(*m_pNode);
+ const SwSection& rSect = rSNd.GetSection();
+ if( SectionType::Content != rSect.GetType() || rSect.IsProtect() )
+ pNd = m_pNode->EndOfSectionNode();
+ }
+ break;
+ default: break;
+ }
+ return *pNd;
+}
+
+bool SwCompareLine::Compare( const SwCompareLine& rLine ) const
+{
+ return CompareNode( *m_pNode, *rLine.m_pNode );
+}
+
+namespace
+{
+ OUString SimpleTableToText(const SwNode &rNode)
+ {
+ OUStringBuffer sRet;
+ const SwNode* pEndNd = rNode.EndOfSectionNode();
+ SwNodeIndex aIdx( rNode );
+ while (&aIdx.GetNode() != pEndNd)
+ {
+ if (aIdx.GetNode().IsTextNode())
+ {
+ if (sRet.getLength())
+ {
+ sRet.append( '\n' );
+ }
+ sRet.append( aIdx.GetNode().GetTextNode()->GetExpandText(nullptr) );
+ }
+ ++aIdx;
+ }
+ return sRet.makeStringAndClear();
+ }
+}
+
+bool SwCompareLine::CompareNode( const SwNode& rDstNd, const SwNode& rSrcNd )
+{
+ if( rSrcNd.GetNodeType() != rDstNd.GetNodeType() )
+ return false;
+
+ bool bRet = false;
+
+ switch( rDstNd.GetNodeType() )
+ {
+ case SwNodeType::Text:
+ bRet = CompareTextNd( *rDstNd.GetTextNode(), *rSrcNd.GetTextNode() )
+ && ( !CmpOptions.bUseRsid || rDstNd.GetTextNode()->CompareParRsid( *rSrcNd.GetTextNode() ) );
+ break;
+
+ case SwNodeType::Table:
+ {
+ const SwTableNode& rTSrcNd = static_cast<const SwTableNode&>(rSrcNd);
+ const SwTableNode& rTDstNd = static_cast<const SwTableNode&>(rDstNd);
+
+ bRet = ( rTSrcNd.EndOfSectionIndex() - rTSrcNd.GetIndex() ) ==
+ ( rTDstNd.EndOfSectionIndex() - rTDstNd.GetIndex() );
+
+ // --> #i107826#: compare actual table content
+ if (bRet)
+ {
+ bRet = (SimpleTableToText(rSrcNd) == SimpleTableToText(rDstNd));
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ {
+ const SwSectionNode& rSSrcNd = static_cast<const SwSectionNode&>(rSrcNd),
+ & rSDstNd = static_cast<const SwSectionNode&>(rDstNd);
+ const SwSection& rSrcSect = rSSrcNd.GetSection(),
+ & rDstSect = rSDstNd.GetSection();
+ SectionType eSrcSectType = rSrcSect.GetType(),
+ eDstSectType = rDstSect.GetType();
+ switch( eSrcSectType )
+ {
+ case SectionType::Content:
+ bRet = SectionType::Content == eDstSectType &&
+ rSrcSect.IsProtect() == rDstSect.IsProtect();
+ if( bRet && rSrcSect.IsProtect() )
+ {
+ // the only have they both the same size
+ bRet = ( rSSrcNd.EndOfSectionIndex() - rSSrcNd.GetIndex() ) ==
+ ( rSDstNd.EndOfSectionIndex() - rSDstNd.GetIndex() );
+ }
+ break;
+
+ case SectionType::ToxHeader:
+ case SectionType::ToxContent:
+ if( SectionType::ToxHeader == eDstSectType ||
+ SectionType::ToxContent == eDstSectType )
+ {
+ // the same type of TOX?
+ const SwTOXBase* pSrcTOX = rSrcSect.GetTOXBase();
+ const SwTOXBase* pDstTOX = rDstSect.GetTOXBase();
+ bRet = pSrcTOX && pDstTOX
+ && pSrcTOX->GetType() == pDstTOX->GetType()
+ && pSrcTOX->GetTitle() == pDstTOX->GetTitle()
+ && pSrcTOX->GetTypeName() == pDstTOX->GetTypeName()
+ ;
+ }
+ break;
+
+ case SectionType::DdeLink:
+ case SectionType::FileLink:
+ bRet = eSrcSectType == eDstSectType &&
+ rSrcSect.GetLinkFileName() ==
+ rDstSect.GetLinkFileName();
+ break;
+ }
+ }
+ break;
+
+ case SwNodeType::End:
+ bRet = rSrcNd.StartOfSectionNode()->GetNodeType() ==
+ rDstNd.StartOfSectionNode()->GetNodeType();
+
+ // --> #i107826#: compare actual table content
+ if (bRet && rSrcNd.StartOfSectionNode()->GetNodeType() == SwNodeType::Table)
+ {
+ bRet = CompareNode(
+ *rSrcNd.StartOfSectionNode(), *rDstNd.StartOfSectionNode());
+ }
+
+ break;
+
+ default: break;
+ }
+ return bRet;
+}
+
+OUString SwCompareLine::GetText() const
+{
+ OUString sRet;
+ switch( m_pNode->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ sRet = m_pNode->GetTextNode()->GetExpandText(nullptr);
+ break;
+
+ case SwNodeType::Table:
+ {
+ sRet = "Tabelle: " + SimpleTableToText(*m_pNode);
+ }
+ break;
+
+ case SwNodeType::Section:
+ {
+ sRet = "Section - Node:";
+
+ const SwSectionNode& rSNd = static_cast<const SwSectionNode&>(*m_pNode);
+ const SwSection& rSect = rSNd.GetSection();
+ switch( rSect.GetType() )
+ {
+ case SectionType::Content:
+ if( rSect.IsProtect() )
+ sRet += OUString::number(
+ sal_Int32(rSNd.EndOfSectionIndex() - rSNd.GetIndex()) );
+ break;
+
+ case SectionType::ToxHeader:
+ case SectionType::ToxContent:
+ {
+ const SwTOXBase* pTOX = rSect.GetTOXBase();
+ if( pTOX )
+ sRet += pTOX->GetTitle() + pTOX->GetTypeName() +
+ OUString::number(pTOX->GetType());
+ }
+ break;
+
+ case SectionType::DdeLink:
+ case SectionType::FileLink:
+ sRet += rSect.GetLinkFileName();
+ break;
+ }
+ }
+ break;
+
+ case SwNodeType::Grf:
+ sRet = "Grafik - Node:";
+ break;
+ case SwNodeType::Ole:
+ sRet = "OLE - Node:";
+ break;
+ default: break;
+ }
+ return sRet;
+}
+
+sal_uLong SwCompareLine::GetTextNodeHashValue( const SwTextNode& rNd, sal_uLong nVal )
+{
+ OUString sStr( rNd.GetExpandText(nullptr) );
+ for( sal_Int32 n = 0; n < sStr.getLength(); ++n )
+ nVal = (nVal << 1 ) + sStr[ n ];
+ return nVal;
+}
+
+bool SwCompareLine::CompareTextNd( const SwTextNode& rDstNd,
+ const SwTextNode& rSrcNd )
+{
+ bool bRet = false;
+ // Very simple at first
+ if( rDstNd.GetText() == rSrcNd.GetText() )
+ {
+ // The text is the same, but are the "special attributes" (0xFF) also the same?
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool SwCompareLine::ChangesInLine( const SwCompareLine& rLine,
+ std::unique_ptr<SwPaM>& rpInsRing, std::unique_ptr<SwPaM>& rpDelRing ) const
+{
+ bool bRet = false;
+
+ // Only compare textnodes
+ if( SwNodeType::Text == m_pNode->GetNodeType() &&
+ SwNodeType::Text == rLine.GetNode().GetNodeType() )
+ {
+ SwTextNode& rDstNd = *const_cast<SwTextNode*>(m_pNode->GetTextNode());
+ const SwTextNode& rSrcNd = *rLine.GetNode().GetTextNode();
+ SwDoc& rDstDoc = rDstNd.GetDoc();
+
+ int nLcsLen = 0;
+
+ int nDstLen = rDstNd.GetText().getLength();
+ int nSrcLen = rSrcNd.GetText().getLength();
+
+ int nMinLen = std::min( nDstLen , nSrcLen );
+ int nAvgLen = ( nDstLen + nSrcLen )/2;
+
+ std::vector<int> aLcsDst( nMinLen + 1 );
+ std::vector<int> aLcsSrc( nMinLen + 1 );
+
+ if( CmpOptions.eCmpMode == SwCompareMode::ByWord )
+ {
+ std::vector<int> aTmpLcsDst( nMinLen + 1 );
+ std::vector<int> aTmpLcsSrc( nMinLen + 1 );
+
+ WordArrayComparator aCmp( &rDstNd, &rSrcNd );
+
+ LgstCommonSubseq aSeq( aCmp );
+
+ nLcsLen = aSeq.Find( aTmpLcsDst.data(), aTmpLcsSrc.data() );
+
+ if( CmpOptions.nIgnoreLen )
+ {
+ nLcsLen = CommonSubseq::IgnoreIsolatedPieces( aTmpLcsDst.data(), aTmpLcsSrc.data(),
+ aCmp.GetLen1(), aCmp.GetLen2(),
+ nLcsLen, CmpOptions.nIgnoreLen );
+ }
+
+ nLcsLen = aCmp.GetCharSequence( aTmpLcsDst.data(), aTmpLcsSrc.data(),
+ aLcsDst.data(), aLcsSrc.data(), nLcsLen );
+ }
+ else
+ {
+ CharArrayComparator aCmp( &rDstNd, &rSrcNd );
+ LgstCommonSubseq aSeq( aCmp );
+
+ nLcsLen = aSeq.Find( aLcsDst.data(), aLcsSrc.data() );
+
+ if( CmpOptions.nIgnoreLen )
+ {
+ nLcsLen = CommonSubseq::IgnoreIsolatedPieces( aLcsDst.data(), aLcsSrc.data(), nDstLen,
+ nSrcLen, nLcsLen,
+ CmpOptions.nIgnoreLen );
+ }
+ }
+
+ // find the sum of the squares of the continuous substrings
+ int nSqSum = 0;
+ int nCnt = 1;
+ for( int i = 0; i < nLcsLen; i++ )
+ {
+ if( i != nLcsLen - 1 && aLcsDst[i] + 1 == aLcsDst[i + 1]
+ && aLcsSrc[i] + 1 == aLcsSrc[i + 1] )
+ {
+ nCnt++;
+ }
+ else
+ {
+ nSqSum += nCnt*nCnt;
+ nCnt = 1;
+ }
+ }
+
+ // Don't compare if there aren't enough similarities
+ if ( nAvgLen >= 8 && nSqSum*32 < nAvgLen*nAvgLen )
+ {
+ return false;
+ }
+
+ // Show the differences
+ int nSkip = 0;
+ for( int i = 0; i <= nLcsLen; i++ )
+ {
+ int nDstFrom = i ? (aLcsDst[i - 1] + 1) : 0;
+ int nDstTo = ( i == nLcsLen ) ? nDstLen : aLcsDst[i];
+ int nSrcFrom = i ? (aLcsSrc[i - 1] + 1) : 0;
+ int nSrcTo = ( i == nLcsLen ) ? nSrcLen : aLcsSrc[i];
+
+ SwPaM aPam( rDstNd, nDstTo + nSkip );
+
+ if ( nDstFrom < nDstTo )
+ {
+ SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpInsRing.get() );
+ if( !rpInsRing )
+ rpInsRing.reset(pTmp);
+ pTmp->SetMark();
+ pTmp->GetMark()->nContent = nDstFrom + nSkip;
+ }
+
+ if ( nSrcFrom < nSrcTo )
+ {
+ bool bUndo = rDstDoc.GetIDocumentUndoRedo().DoesUndo();
+ rDstDoc.GetIDocumentUndoRedo().DoUndo( false );
+ SwPaM aCpyPam( rSrcNd, nSrcFrom );
+ aCpyPam.SetMark();
+ aCpyPam.GetPoint()->nContent = nSrcTo;
+ aCpyPam.GetDoc().getIDocumentContentOperations().CopyRange( aCpyPam, *aPam.GetPoint(),
+ SwCopyFlags::CheckPosInFly);
+ rDstDoc.GetIDocumentUndoRedo().DoUndo( bUndo );
+
+ SwPaM* pTmp = new SwPaM( *aPam.GetPoint(), rpDelRing.get() );
+ if( !rpDelRing )
+ rpDelRing.reset(pTmp);
+
+ pTmp->SetMark();
+ pTmp->GetMark()->nContent = nDstTo + nSkip;
+ nSkip += nSrcTo - nSrcFrom;
+
+ if( rpInsRing )
+ {
+ SwPaM* pCorr = rpInsRing->GetPrev();
+ if( *pCorr->GetPoint() == *pTmp->GetPoint() )
+ *pCorr->GetPoint() = *pTmp->GetMark();
+ }
+ }
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+SwNodeOffset CompareData::NextIdx( const SwNode* pNd )
+{
+ if( pNd->IsStartNode() )
+ {
+ if( pNd->IsTableNode() )
+ pNd = pNd->EndOfSectionNode();
+ else
+ {
+ const SwSectionNode* pSNd = pNd->GetSectionNode();
+ if( pSNd &&
+ ( SectionType::Content != pSNd->GetSection().GetType() ||
+ pSNd->GetSection().IsProtect() ) )
+ pNd = pNd->EndOfSectionNode();
+ }
+ }
+ return pNd->GetIndex() + 1;
+}
+
+SwNodeOffset CompareData::PrevIdx( const SwNode* pNd )
+{
+ if( pNd->IsEndNode() )
+ {
+ if( pNd->StartOfSectionNode()->IsTableNode() )
+ pNd = pNd->StartOfSectionNode();
+ else
+ {
+ const SwSectionNode* pSNd = pNd->StartOfSectionNode()->GetSectionNode();
+ if( pSNd &&
+ ( SectionType::Content != pSNd->GetSection().GetType() ||
+ pSNd->GetSection().IsProtect() ) )
+ pNd = pNd->StartOfSectionNode();
+ }
+ }
+ return pNd->GetIndex() - 1;
+}
+
+void CompareData::CheckRanges( CompareData& rData )
+{
+ const SwNodes& rSrcNds = rData.m_rDoc.GetNodes();
+ const SwNodes& rDstNds = m_rDoc.GetNodes();
+
+ const SwNode& rSrcEndNd = rData.GetEndOfContent();
+ const SwNode& rDstEndNd = GetEndOfContent();
+
+ SwNodeOffset nSrcSttIdx = NextIdx( rSrcEndNd.StartOfSectionNode() );
+ SwNodeOffset nSrcEndIdx = rSrcEndNd.GetIndex();
+
+ SwNodeOffset nDstSttIdx = NextIdx( rDstEndNd.StartOfSectionNode() );
+ SwNodeOffset nDstEndIdx = rDstEndNd.GetIndex();
+
+ while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx )
+ {
+ const SwNode* pSrcNd = rSrcNds[ nSrcSttIdx ];
+ const SwNode* pDstNd = rDstNds[ nDstSttIdx ];
+ if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd ))
+ break;
+
+ nSrcSttIdx = NextIdx( pSrcNd );
+ nDstSttIdx = NextIdx( pDstNd );
+ }
+
+ nSrcEndIdx = PrevIdx( &rSrcEndNd );
+ nDstEndIdx = PrevIdx( &rDstEndNd );
+ while( nSrcSttIdx < nSrcEndIdx && nDstSttIdx < nDstEndIdx )
+ {
+ const SwNode* pSrcNd = rSrcNds[ nSrcEndIdx ];
+ const SwNode* pDstNd = rDstNds[ nDstEndIdx ];
+ if( !SwCompareLine::CompareNode( *pSrcNd, *pDstNd ))
+ break;
+
+ nSrcEndIdx = PrevIdx( pSrcNd );
+ nDstEndIdx = PrevIdx( pDstNd );
+ }
+
+ while( nSrcSttIdx <= nSrcEndIdx )
+ {
+ const SwNode* pNd = rSrcNds[ nSrcSttIdx ];
+ rData.InsertLine( SwCompareLine( *pNd ) );
+ nSrcSttIdx = NextIdx( pNd );
+ }
+
+ while( nDstSttIdx <= nDstEndIdx )
+ {
+ const SwNode* pNd = rDstNds[ nDstSttIdx ];
+ InsertLine( SwCompareLine( *pNd ) );
+ nDstSttIdx = NextIdx( pNd );
+ }
+}
+
+void CompareData::ShowInsert( sal_uLong nStt, sal_uLong nEnd )
+{
+ SwPaM* pTmp = new SwPaM( GetLine( nStt ).GetNode(), 0,
+ GetLine( nEnd-1 ).GetEndNode(), 0,
+ m_pInsertRing.get() );
+ if( !m_pInsertRing )
+ m_pInsertRing.reset( pTmp );
+
+ // #i65201#: These SwPaMs are calculated smaller than needed, see comment below
+}
+
+void CompareData::ShowDelete(
+ const CompareData& rData,
+ sal_uLong nStt,
+ sal_uLong nEnd,
+ sal_uLong nInsPos )
+{
+ SwNodeRange aRg(
+ rData.GetLine( nStt ).GetNode(), SwNodeOffset(0),
+ rData.GetLine( nEnd-1 ).GetEndNode(), SwNodeOffset(1) );
+
+ SwNodeOffset nOffset(0);
+ std::optional<SwCompareLine> xLine;
+ if( nInsPos >= 1 )
+ {
+ if( GetLineCount() == nInsPos )
+ {
+ xLine = GetLine( nInsPos-1 );
+ nOffset = SwNodeOffset(1);
+ }
+ else
+ xLine = GetLine( nInsPos );
+ }
+
+ const SwNode* pLineNd;
+ if( xLine )
+ {
+ if( nOffset )
+ pLineNd = &xLine->GetEndNode();
+ else
+ pLineNd = &xLine->GetNode();
+ }
+ else
+ {
+ pLineNd = &GetEndOfContent();
+ nOffset = SwNodeOffset(0);
+ }
+
+ SwNodeIndex aInsPos( *pLineNd, nOffset );
+ SwNodeIndex aSavePos( aInsPos, -1 );
+
+ rData.m_rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos);
+ m_rDoc.getIDocumentState().SetModified();
+ ++aSavePos;
+
+ // #i65201#: These SwPaMs are calculated when the (old) delete-redlines are hidden,
+ // they will be inserted when the delete-redlines are shown again.
+ // To avoid unwanted insertions of delete-redlines into these new redlines, what happens
+ // especially at the end of the document, I reduce the SwPaM by one node.
+ // Before the new redlines are inserted, they have to expand again.
+ SwPaM* pTmp = new SwPaM( aSavePos.GetNode(), aInsPos.GetNode(), SwNodeOffset(0), SwNodeOffset(-1), m_pDelRing.get() );
+ if( !m_pDelRing )
+ m_pDelRing.reset(pTmp);
+
+ if( m_pInsertRing )
+ {
+ SwPaM* pCorr = m_pInsertRing->GetPrev();
+ if( *pCorr->GetPoint() == *pTmp->GetPoint() )
+ {
+ SwNodeIndex aTmpPos( pTmp->GetMark()->nNode, -1 );
+ *pCorr->GetPoint() = SwPosition( aTmpPos );
+ }
+ }
+}
+
+void CompareData::CheckForChangesInLine( const CompareData& rData,
+ sal_uLong nStt, sal_uLong nEnd,
+ sal_uLong nThisStt, sal_uLong nThisEnd )
+{
+ LineArrayComparator aCmp( *this, rData, nThisStt, nThisEnd,
+ nStt, nEnd );
+
+ int nMinLen = std::min( aCmp.GetLen1(), aCmp.GetLen2() );
+ std::unique_ptr<int[]> pLcsDst(new int[ nMinLen ]);
+ std::unique_ptr<int[]> pLcsSrc(new int[ nMinLen ]);
+
+ FastCommonSubseq subseq( aCmp );
+ int nLcsLen = subseq.Find( pLcsDst.get(), pLcsSrc.get() );
+ for (int i = 0; i <= nLcsLen; i++)
+ {
+ // Beginning of inserted lines (inclusive)
+ int nDstFrom = i ? pLcsDst[i - 1] + 1 : 0;
+ // End of inserted lines (exclusive)
+ int nDstTo = ( i == nLcsLen ) ? aCmp.GetLen1() : pLcsDst[i];
+ // Beginning of deleted lines (inclusive)
+ int nSrcFrom = i ? pLcsSrc[i - 1] + 1 : 0;
+ // End of deleted lines (exclusive)
+ int nSrcTo = ( i == nLcsLen ) ? aCmp.GetLen2() : pLcsSrc[i];
+
+ if( i )
+ {
+ const SwCompareLine aDstLn = GetLine( nThisStt + nDstFrom - 1 );
+ const SwCompareLine aSrcLn = rData.GetLine( nStt + nSrcFrom - 1 );
+
+ // Show differences in detail for lines that
+ // were matched as only slightly different
+ if( !aDstLn.ChangesInLine( aSrcLn, m_pInsertRing, m_pDelRing ) )
+ {
+ ShowInsert( nThisStt + nDstFrom - 1, nThisStt + nDstFrom );
+ ShowDelete( rData, nStt + nSrcFrom - 1, nStt + nSrcFrom,
+ nThisStt + nDstFrom );
+ }
+ }
+
+ // Lines missing from source are inserted
+ if( nDstFrom != nDstTo )
+ {
+ ShowInsert( nThisStt + nDstFrom, nThisStt + nDstTo );
+ }
+
+ // Lines missing from destination are deleted
+ if( nSrcFrom != nSrcTo )
+ {
+ ShowDelete( rData, nStt + nSrcFrom, nStt + nSrcTo, nThisStt + nDstTo );
+ }
+ }
+}
+
+void CompareData::SetRedlinesToDoc( bool bUseDocInfo )
+{
+ SwPaM* pTmp = m_pDelRing.get();
+
+ // get the Author / TimeStamp from the "other" document info
+ std::size_t nAuthor = m_rDoc.getIDocumentRedlineAccess().GetRedlineAuthor();
+ DateTime aTimeStamp( DateTime::SYSTEM );
+ SwDocShell *pDocShell(m_rDoc.GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell) {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+
+ if( bUseDocInfo && xDocProps.is() ) {
+ OUString aTmp( 1 == xDocProps->getEditingCycles()
+ ? xDocProps->getAuthor()
+ : xDocProps->getModifiedBy() );
+ util::DateTime uDT( 1 == xDocProps->getEditingCycles()
+ ? xDocProps->getCreationDate()
+ : xDocProps->getModificationDate() );
+
+ if( !aTmp.isEmpty() )
+ {
+ nAuthor = m_rDoc.getIDocumentRedlineAccess().InsertRedlineAuthor( aTmp );
+ aTimeStamp = DateTime(uDT);
+ }
+ }
+ }
+
+ if( pTmp )
+ {
+ SwRedlineData aRedlnData( RedlineType::Delete, nAuthor, aTimeStamp,
+ OUString(), nullptr );
+ do {
+ // #i65201#: Expand again, see comment above.
+ if( pTmp->GetPoint()->nContent == 0 )
+ {
+ ++pTmp->GetPoint()->nNode;
+ pTmp->GetPoint()->nContent.Assign( pTmp->GetContentNode(), 0 );
+ }
+ // #i101009#
+ // prevent redlines that end on structural end node
+ if (& GetEndOfContent() ==
+ & pTmp->GetPoint()->nNode.GetNode())
+ {
+ --pTmp->GetPoint()->nNode;
+ SwContentNode *const pContentNode( pTmp->GetContentNode() );
+ pTmp->GetPoint()->nContent.Assign( pContentNode,
+ pContentNode ? pContentNode->Len() : 0 );
+ // tdf#106218 try to avoid losing a paragraph break here:
+ if (pTmp->GetMark()->nContent == 0)
+ {
+ SwNodeIndex const prev(pTmp->GetMark()->nNode, -1);
+ if (prev.GetNode().IsTextNode())
+ {
+ *pTmp->GetMark() = SwPosition(
+ *prev.GetNode().GetTextNode(),
+ prev.GetNode().GetTextNode()->Len());
+ }
+ }
+ }
+
+ m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pTmp, false, RedlineType::Any );
+
+ if (m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCompDoc>( *pTmp, false ));
+ }
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( aRedlnData, *pTmp ), true );
+
+ } while( m_pDelRing.get() != ( pTmp = pTmp->GetNext()) );
+ }
+
+ pTmp = m_pInsertRing.get();
+ if( !pTmp )
+ return;
+
+ do {
+ if( pTmp->GetPoint()->nContent == 0 )
+ {
+ ++pTmp->GetPoint()->nNode;
+ pTmp->GetPoint()->nContent.Assign( pTmp->GetContentNode(), 0 );
+ }
+ // #i101009#
+ // prevent redlines that end on structural end node
+ if (& GetEndOfContent() ==
+ & pTmp->GetPoint()->nNode.GetNode())
+ {
+ --pTmp->GetPoint()->nNode;
+ SwContentNode *const pContentNode( pTmp->GetContentNode() );
+ pTmp->GetPoint()->nContent.Assign( pContentNode,
+ pContentNode ? pContentNode->Len() : 0 );
+ // tdf#106218 try to avoid losing a paragraph break here:
+ if (pTmp->GetMark()->nContent == 0)
+ {
+ SwNodeIndex const prev(pTmp->GetMark()->nNode, -1);
+ if (prev.GetNode().IsTextNode())
+ {
+ *pTmp->GetMark() = SwPosition(
+ *prev.GetNode().GetTextNode(),
+ prev.GetNode().GetTextNode()->Len());
+ }
+ }
+ }
+ } while( m_pInsertRing.get() != ( pTmp = pTmp->GetNext()) );
+ SwRedlineData aRedlnData( RedlineType::Insert, nAuthor, aTimeStamp,
+ OUString(), nullptr );
+
+ // combine consecutive
+ if( pTmp->GetNext() != m_pInsertRing.get() )
+ {
+ do {
+ SwPosition& rSttEnd = *pTmp->End(),
+ & rEndStt = *pTmp->GetNext()->Start();
+ const SwContentNode* pCNd;
+ if( rSttEnd == rEndStt ||
+ (!rEndStt.nContent.GetIndex() &&
+ rEndStt.nNode.GetIndex() - 1 == rSttEnd.nNode.GetIndex() &&
+ nullptr != ( pCNd = rSttEnd.nNode.GetNode().GetContentNode() ) &&
+ rSttEnd.nContent.GetIndex() == pCNd->Len()))
+ {
+ if( pTmp->GetNext() == m_pInsertRing.get() )
+ {
+ // are consecutive, so combine
+ rEndStt = *pTmp->Start();
+ delete pTmp;
+ pTmp = m_pInsertRing.get();
+ }
+ else
+ {
+ // are consecutive, so combine
+ rSttEnd = *pTmp->GetNext()->End();
+ delete pTmp->GetNext();
+ }
+ }
+ else
+ pTmp = pTmp->GetNext();
+ } while( m_pInsertRing.get() != pTmp );
+ }
+
+ do {
+ if (IDocumentRedlineAccess::AppendResult::APPENDED ==
+ m_rDoc.getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline(aRedlnData, *pTmp), true) &&
+ m_rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ m_rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCompDoc>( *pTmp, true ));
+ }
+ } while( m_pInsertRing.get() != ( pTmp = pTmp->GetNext()) );
+}
+
+typedef std::shared_ptr<CompareData> CompareDataPtr;
+typedef std::pair<CompareDataPtr, CompareDataPtr> CompareDataPtrPair;
+typedef std::vector<CompareDataPtrPair> Comparators;
+
+namespace
+{
+ Comparators buildComparators(SwDoc &rSrcDoc, SwDoc &rDestDoc)
+ {
+ Comparators aComparisons;
+ //compare main text
+ aComparisons.emplace_back(std::make_shared<CompareMainText>(rSrcDoc, true),
+ std::make_shared<CompareMainText>(rDestDoc, true));
+
+ //if we have the same number of frames then try to compare within them
+ const SwFrameFormats *pSrcFrameFormats = rSrcDoc.GetSpzFrameFormats();
+ const SwFrameFormats *pDestFrameFormats = rDestDoc.GetSpzFrameFormats();
+ if (pSrcFrameFormats->size() == pDestFrameFormats->size())
+ {
+ for (size_t i = 0; i < pSrcFrameFormats->size(); ++i)
+ {
+ const SwFrameFormat& rSrcFormat = *(*pSrcFrameFormats)[i];
+ const SwFrameFormat& rDestFormat = *(*pDestFrameFormats)[i];
+ const SwNodeIndex* pSrcIdx = rSrcFormat.GetContent().GetContentIdx();
+ const SwNodeIndex* pDestIdx = rDestFormat.GetContent().GetContentIdx();
+ if (!pSrcIdx && !pDestIdx)
+ continue;
+ if (!pSrcIdx || !pDestIdx)
+ break;
+ const SwNode* pSrcNode = pSrcIdx->GetNode().EndOfSectionNode();
+ const SwNode* pDestNode = pDestIdx->GetNode().EndOfSectionNode();
+ if (!pSrcNode && !pDestNode)
+ continue;
+ if (!pSrcNode || !pDestNode)
+ break;
+ if (pSrcIdx->GetNodes()[pSrcIdx->GetIndex() + 1]->IsNoTextNode()
+ || pDestIdx->GetNodes()[pDestIdx->GetIndex() + 1]->IsNoTextNode())
+ {
+ continue; // tdf#125660 don't redline GrfNode/OLENode
+ }
+ aComparisons.emplace_back(std::make_shared<CompareFrameFormatText>(rSrcDoc, *pSrcIdx),
+ std::make_shared<CompareFrameFormatText>(rDestDoc, *pDestIdx));
+ }
+ }
+ return aComparisons;
+ }
+}
+
+// Returns (the difference count?) if something is different
+tools::Long SwDoc::CompareDoc( const SwDoc& rDoc )
+{
+ if( &rDoc == this )
+ return 0;
+
+ tools::Long nRet = 0;
+
+ // Get comparison options
+ CmpOptions.eCmpMode = SW_MOD()->GetCompareMode();
+ if( CmpOptions.eCmpMode == SwCompareMode::Auto )
+ {
+ if( getRsidRoot() == rDoc.getRsidRoot() )
+ {
+ CmpOptions.eCmpMode = SwCompareMode::ByChar;
+ CmpOptions.bUseRsid = true;
+ CmpOptions.nIgnoreLen = 2;
+ }
+ else
+ {
+ CmpOptions.eCmpMode = SwCompareMode::ByWord;
+ CmpOptions.bUseRsid = false;
+ CmpOptions.nIgnoreLen = 3;
+ }
+ }
+ else
+ {
+ CmpOptions.bUseRsid = getRsidRoot() == rDoc.getRsidRoot() && SW_MOD()->IsUseRsid();
+ CmpOptions.nIgnoreLen = SW_MOD()->IsIgnorePieces() ? SW_MOD()->GetPieceLen() : 0;
+ }
+
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ bool bDocWasModified = getIDocumentState().IsModified();
+ SwDoc& rSrcDoc = const_cast<SwDoc&>(rDoc);
+ bool bSrcModified = rSrcDoc.getIDocumentState().IsModified();
+
+ RedlineFlags eSrcRedlMode = rSrcDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowInsert );
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On | RedlineFlags::ShowInsert);
+
+ Comparators aComparisons(buildComparators(rSrcDoc, *this));
+
+ for (auto& a : aComparisons)
+ {
+ CompareData& rD0 = *a.first;
+ CompareData& rD1 = *a.second;
+ rD1.CompareLines( rD0 );
+ nRet |= rD1.ShowDiffs( rD0 );
+ }
+
+ if( nRet )
+ {
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::On |
+ RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ for (auto& a : aComparisons)
+ {
+ CompareData& rD1 = *a.second;
+ rD1.SetRedlinesToDoc( !bDocWasModified );
+ }
+ getIDocumentState().SetModified();
+ }
+
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( eSrcRedlMode );
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ if( !bSrcModified )
+ rSrcDoc.getIDocumentState().ResetModified();
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ return nRet;
+}
+
+namespace
+{
+ struct SaveMergeRedline
+ {
+ const SwRangeRedline* pSrcRedl;
+ SwRangeRedline* pDestRedl;
+ SaveMergeRedline( const SwNode& rDstNd, const SwRangeRedline& rSrcRedl);
+ sal_uInt16 InsertRedline(SwPaM* pLastDestRedline);
+ };
+}
+
+SaveMergeRedline::SaveMergeRedline( const SwNode& rDstNd,
+ const SwRangeRedline& rSrcRedl)
+ : pSrcRedl( &rSrcRedl )
+{
+ SwPosition aPos( rDstNd );
+
+ const SwPosition* pStt = rSrcRedl.Start();
+ if( rDstNd.IsContentNode() )
+ aPos.nContent.Assign( const_cast<SwContentNode*>(static_cast<const SwContentNode*>(&rDstNd)), pStt->nContent.GetIndex() );
+ pDestRedl = new SwRangeRedline( rSrcRedl.GetRedlineData(), aPos );
+
+ if( RedlineType::Delete != pDestRedl->GetType() )
+ return;
+
+ // mark the area as deleted
+ const SwPosition* pEnd = rSrcRedl.End();
+
+ pDestRedl->SetMark();
+ pDestRedl->GetPoint()->nNode += pEnd->nNode.GetIndex() -
+ pStt->nNode.GetIndex();
+ pDestRedl->GetPoint()->nContent.Assign( pDestRedl->GetContentNode(),
+ pEnd->nContent.GetIndex() );
+}
+
+sal_uInt16 SaveMergeRedline::InsertRedline(SwPaM* pLastDestRedline)
+{
+ sal_uInt16 nIns = 0;
+ SwDoc& rDoc = pDestRedl->GetDoc();
+
+ if( RedlineType::Insert == pDestRedl->GetType() )
+ {
+ // the part was inserted so copy it from the SourceDoc
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNodeIndex aSaveNd( pDestRedl->GetPoint()->nNode, -1 );
+ const sal_Int32 nSaveCnt = pDestRedl->GetPoint()->nContent.GetIndex();
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+
+ pSrcRedl->GetDoc().getIDocumentContentOperations().CopyRange(
+ *const_cast<SwPaM*>(static_cast<const SwPaM*>(pSrcRedl)),
+ *pDestRedl->GetPoint(), SwCopyFlags::CheckPosInFly);
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ pDestRedl->SetMark();
+ ++aSaveNd;
+ pDestRedl->GetMark()->nNode = aSaveNd;
+ pDestRedl->GetMark()->nContent.Assign( aSaveNd.GetNode().GetContentNode(),
+ nSaveCnt );
+
+ if( pLastDestRedline && *pLastDestRedline->GetPoint() == *pDestRedl->GetPoint() )
+ *pLastDestRedline->GetPoint() = *pDestRedl->GetMark();
+ }
+ else
+ {
+ //JP 21.09.98: Bug 55909
+ // If there already is a deleted or inserted one at the same position, we have to split it!
+ SwPosition* pDStt = pDestRedl->GetMark(),
+ * pDEnd = pDestRedl->GetPoint();
+ SwRedlineTable::size_type n = 0;
+
+ // find the first redline for StartPos
+ if( !rDoc.getIDocumentRedlineAccess().GetRedline( *pDStt, &n ) && n )
+ --n;
+
+ const SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; n < rRedlineTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rRedlineTable[ n ];
+ SwPosition* pRStt = pRedl->Start(),
+ * pREnd = pRedl->End();
+ if( RedlineType::Delete == pRedl->GetType() ||
+ RedlineType::Insert == pRedl->GetType() )
+ {
+ SwComparePosition eCmpPos = ComparePosition( *pDStt, *pDEnd, *pRStt, *pREnd );
+ switch( eCmpPos )
+ {
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Behind:
+ break;
+
+ case SwComparePosition::Inside:
+ case SwComparePosition::Equal:
+ delete pDestRedl;
+ pDestRedl = nullptr;
+ [[fallthrough]];
+
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before:
+ n = rRedlineTable.size();
+ break;
+
+ case SwComparePosition::Outside:
+ assert(pDestRedl && "is this actually impossible");
+ if (pDestRedl)
+ {
+ SwRangeRedline* pCpyRedl = new SwRangeRedline(
+ pDestRedl->GetRedlineData(), *pDStt );
+ pCpyRedl->SetMark();
+ *pCpyRedl->GetPoint() = *pRStt;
+
+ std::unique_ptr<SwUndoCompDoc> pUndo;
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoCompDoc( *pCpyRedl ));
+
+ // now modify doc: append redline, undo (and count)
+ rDoc.getIDocumentRedlineAccess().AppendRedline( pCpyRedl, true );
+ if( pUndo )
+ {
+ rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ ++nIns;
+
+ *pDStt = *pREnd;
+
+ // we should start over now
+ n = SwRedlineTable::npos;
+ }
+ break;
+
+ case SwComparePosition::OverlapBefore:
+ *pDEnd = *pRStt;
+ break;
+
+ case SwComparePosition::OverlapBehind:
+ *pDStt = *pREnd;
+ break;
+ }
+ }
+ else if( *pDEnd <= *pRStt )
+ break;
+ }
+
+ }
+
+ if( pDestRedl )
+ {
+ std::unique_ptr<SwUndoCompDoc> pUndo;
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoCompDoc( *pDestRedl ));
+
+ // now modify doc: append redline, undo (and count)
+ IDocumentRedlineAccess::AppendResult const result(
+ rDoc.getIDocumentRedlineAccess().AppendRedline(pDestRedl, true));
+ if( pUndo )
+ {
+ rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ ++nIns;
+
+ // if AppendRedline has deleted our redline, we may not keep a
+ // reference to it
+ if (IDocumentRedlineAccess::AppendResult::APPENDED != result)
+ pDestRedl = nullptr;
+ }
+ return nIns;
+}
+
+/// Merge two documents
+tools::Long SwDoc::MergeDoc( const SwDoc& rDoc )
+{
+ if( &rDoc == this )
+ return 0;
+
+ tools::Long nRet = 0;
+
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ SwDoc& rSrcDoc = const_cast<SwDoc&>(rDoc);
+ bool bSrcModified = rSrcDoc.getIDocumentState().IsModified();
+
+ RedlineFlags eSrcRedlMode = rSrcDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowDelete );
+ getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowDelete );
+
+ CompareMainText aD0(rSrcDoc, false);
+ CompareMainText aD1(*this, false);
+ aD1.CompareLines( aD0 );
+ if( !aD1.HasDiffs( aD0 ) )
+ {
+ // we want to get all redlines from the SourceDoc
+
+ // look for all insert redlines from the SourceDoc and determine their position in the DestDoc
+ std::vector<SaveMergeRedline> vRedlines;
+ const SwRedlineTable& rSrcRedlTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ SwNodeOffset nEndOfExtra = rSrcDoc.GetNodes().GetEndOfExtras().GetIndex();
+ SwNodeOffset nMyEndOfExtra = GetNodes().GetEndOfExtras().GetIndex();
+ for(const SwRangeRedline* pRedl : rSrcRedlTable)
+ {
+ SwNodeOffset nNd = pRedl->GetPoint()->nNode.GetIndex();
+ RedlineType eType = pRedl->GetType();
+ if( nEndOfExtra < nNd &&
+ ( RedlineType::Insert == eType || RedlineType::Delete == eType ))
+ {
+ const SwNode* pDstNd = GetNodes()[
+ nMyEndOfExtra + nNd - nEndOfExtra ];
+
+ // Found the position.
+ // Then we also have to insert the redline to the line in the DestDoc.
+ vRedlines.emplace_back(*pDstNd, *pRedl);
+ }
+ }
+
+ if( !vRedlines.empty() )
+ {
+ // Carry over all into DestDoc
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On |
+ RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete);
+
+ SwPaM* pLastDestRedline(nullptr);
+ for(SaveMergeRedline& rRedline: vRedlines)
+ {
+ nRet += rRedline.InsertRedline(pLastDestRedline);
+ pLastDestRedline = rRedline.pDestRedl;
+ }
+ }
+ }
+
+ rSrcDoc.getIDocumentRedlineAccess().SetRedlineFlags( eSrcRedlMode );
+ if( !bSrcModified )
+ rSrcDoc.getIDocumentState().ResetModified();
+
+ getIDocumentRedlineAccess().SetRedlineFlags(RedlineFlags::ShowInsert | RedlineFlags::ShowDelete);
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ return nRet;
+}
+
+LineArrayComparator::LineArrayComparator( const CompareData &rD1,
+ const CompareData &rD2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 )
+ : m_rData1( rD1 ), m_rData2( rD2 ), m_nFirst1( nStt1 ), m_nFirst2( nStt2 )
+{
+ m_nLen1 = nEnd1 - nStt1;
+ m_nLen2 = nEnd2 - nStt2;
+}
+
+bool LineArrayComparator::Compare( int nIdx1, int nIdx2 ) const
+{
+ if( nIdx1 < 0 || nIdx2 < 0 || nIdx1 >= m_nLen1 || nIdx2 >= m_nLen2 )
+ {
+ OSL_ENSURE( false, "Index out of range!" );
+ return false;
+ }
+
+ const SwTextNode *pTextNd1 = m_rData1.GetLine( m_nFirst1 + nIdx1 ).GetNode().GetTextNode();
+ const SwTextNode *pTextNd2 = m_rData2.GetLine( m_nFirst2 + nIdx2 ).GetNode().GetTextNode();
+
+ if( !pTextNd1 || !pTextNd2
+ || ( CmpOptions.bUseRsid && !pTextNd1->CompareParRsid( *pTextNd2 ) ) )
+ {
+ return false;
+ }
+
+ const sal_Int32 nPar1Len = pTextNd1->Len();
+ const sal_Int32 nPar2Len = pTextNd2->Len();
+
+ if( std::min( nPar1Len, nPar2Len ) * 3 < std::max( nPar1Len, nPar2Len ) )
+ {
+ return false;
+ }
+
+ sal_Int32 nBorderLen = ( nPar1Len + nPar2Len )/16;
+
+ if( nBorderLen < 3 )
+ {
+ nBorderLen = std::min<sal_Int32>( 3, std::min( nPar1Len, nPar2Len ) );
+ }
+
+ std::set<unsigned> aHashes;
+ unsigned nHash = 0;
+ unsigned nMul = 251;
+ unsigned nPow = 1;
+ sal_Int32 i;
+
+ for( i = 0; i < nBorderLen - 1; i++ )
+ {
+ nPow *= nMul;
+ }
+ for( i = 0; i < nBorderLen; i++ )
+ {
+ nHash = nHash*nMul + pTextNd1->GetText()[i];
+ }
+ aHashes.insert( nHash );
+ for( ; i < nPar1Len; i++ )
+ {
+ nHash = nHash - nPow*pTextNd1->GetText()[ i - nBorderLen ];
+ nHash = nHash*nMul + pTextNd1->GetText()[ i ];
+
+ aHashes.insert( nHash );
+ }
+
+ nHash = 0;
+ for( i = 0; i < nBorderLen; i++ )
+ {
+ nHash = nHash*nMul + pTextNd2->GetText()[ i ];
+ }
+
+ if( aHashes.find( nHash ) != aHashes.end() )
+ {
+ return true;
+ }
+
+ for( ; i < nPar2Len; i++ )
+ {
+ nHash = nHash - nPow*pTextNd2->GetText()[ i - nBorderLen ];
+ nHash = nHash*nMul + pTextNd2->GetText()[ i ];
+ if( aHashes.find( nHash ) != aHashes.end() )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CharArrayComparator::Compare( int nIdx1, int nIdx2 ) const
+{
+ if( nIdx1 < 0 || nIdx2 < 0 || nIdx1 >= GetLen1() || nIdx2 >= GetLen2() )
+ {
+ OSL_ENSURE( false, "Index out of range!" );
+ return false;
+ }
+
+ return ( !CmpOptions.bUseRsid
+ || m_pTextNode1->CompareRsid( *m_pTextNode2, nIdx1 + 1, nIdx2 + 1 ) )
+ && m_pTextNode1->GetText()[ nIdx1 ] == m_pTextNode2->GetText()[ nIdx2 ];
+}
+
+WordArrayComparator::WordArrayComparator( const SwTextNode *pNode1,
+ const SwTextNode *pNode2 )
+ : m_pTextNode1( pNode1 ), m_pTextNode2( pNode2 )
+{
+ m_pPos1.reset( new int[ m_pTextNode1->GetText().getLength() + 1 ] );
+ m_pPos2.reset( new int[ m_pTextNode2->GetText().getLength() + 1 ] );
+
+ CalcPositions( m_pPos1.get(), m_pTextNode1, m_nCount1 );
+ CalcPositions( m_pPos2.get(), m_pTextNode2, m_nCount2 );
+}
+
+bool WordArrayComparator::Compare( int nIdx1, int nIdx2 ) const
+{
+ int nLen = m_pPos1[ nIdx1 + 1 ] - m_pPos1[ nIdx1 ];
+ if( nLen != m_pPos2[ nIdx2 + 1 ] - m_pPos2[ nIdx2 ] )
+ {
+ return false;
+ }
+ for( int i = 0; i < nLen; i++)
+ {
+ if( m_pTextNode1->GetText()[ m_pPos1[ nIdx1 ] + i ]
+ != m_pTextNode2->GetText()[ m_pPos2[ nIdx2 ] + i ]
+ || ( CmpOptions.bUseRsid && !m_pTextNode1->CompareRsid( *m_pTextNode2,
+ m_pPos1[ nIdx1 ] + i, m_pPos2[ nIdx2 ] + i ) ) )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+int WordArrayComparator::GetCharSequence( const int *pWordLcs1,
+ const int *pWordLcs2, int *pSubseq1, int *pSubseq2, int nLcsLen )
+{
+ int nLen = 0;
+ for( int i = 0; i < nLcsLen; i++ )
+ {
+ // Check for hash collisions
+ if( m_pPos1[ pWordLcs1[i] + 1 ] - m_pPos1[ pWordLcs1[i] ]
+ != m_pPos2[ pWordLcs2[i] + 1 ] - m_pPos2[ pWordLcs2[i] ] )
+ {
+ continue;
+ }
+ for( int j = 0; j < m_pPos1[pWordLcs1[i]+1] - m_pPos1[pWordLcs1[i]]; j++)
+ {
+ pSubseq1[ nLen ] = m_pPos1[ pWordLcs1[i] ] + j;
+ pSubseq2[ nLen ] = m_pPos2[ pWordLcs2[i] ] + j;
+
+ if( m_pTextNode1->GetText()[ m_pPos1[ pWordLcs1[i] ] + j ]
+ != m_pTextNode2->GetText()[ m_pPos2[ pWordLcs2[i] ] + j ] )
+ {
+ nLen -= j;
+ break;
+ }
+
+ nLen++;
+ }
+ }
+ return nLen;
+}
+
+void WordArrayComparator::CalcPositions( int *pPos, const SwTextNode *pTextNd,
+ int &nCnt )
+{
+ nCnt = -1;
+ for (int i = 0; i <= pTextNd->GetText().getLength(); ++i)
+ {
+ if (i == 0 || i == pTextNd->GetText().getLength()
+ || !rtl::isAsciiAlphanumeric( pTextNd->GetText()[ i - 1 ])
+ || !rtl::isAsciiAlphanumeric( pTextNd->GetText()[ i ]))
+ { // Begin new word
+ nCnt++;
+ pPos[ nCnt ] = i;
+ }
+ }
+}
+
+int CommonSubseq::FindLCS( int *pLcs1, int *pLcs2, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 )
+{
+ int nLen1 = nEnd1 ? nEnd1 - nStt1 : m_rComparator.GetLen1();
+ int nLen2 = nEnd2 ? nEnd2 - nStt2 : m_rComparator.GetLen2();
+
+ assert( nLen1 >= 0 );
+ assert( nLen2 >= 0 );
+
+ std::unique_ptr<int*[]> pLcs( new int*[ nLen1 + 1 ] );
+ pLcs[ 0 ] = m_pData.get();
+
+ for( int i = 1; i < nLen1 + 1; i++ )
+ pLcs[ i ] = pLcs[ i - 1 ] + nLen2 + 1;
+
+ for( int i = 0; i <= nLen1; i++ )
+ pLcs[i][0] = 0;
+
+ for( int j = 0; j <= nLen2; j++ )
+ pLcs[0][j] = 0;
+
+ // Find lcs
+ for( int i = 1; i <= nLen1; i++ )
+ {
+ for( int j = 1; j <= nLen2; j++ )
+ {
+ if( m_rComparator.Compare( nStt1 + i - 1, nStt2 + j - 1 ) )
+ pLcs[i][j] = pLcs[i - 1][j - 1] + 1;
+ else
+ pLcs[i][j] = std::max( pLcs[i][j - 1], pLcs[i - 1][j] );
+ }
+ }
+
+ int nLcsLen = pLcs[ nLen1 ][ nLen2 ];
+
+ // Recover the lcs in the two sequences
+ if( pLcs1 && pLcs2 )
+ {
+ int nIdx1 = nLen1;
+ int nIdx2 = nLen2;
+ int nIdx = nLcsLen - 1;
+
+ while( nIdx1 > 0 && nIdx2 > 0 )
+ {
+ if( pLcs[ nIdx1 ][ nIdx2 ] == pLcs[ nIdx1 - 1 ][ nIdx2 ] )
+ nIdx1--;
+ else if( pLcs[ nIdx1 ][ nIdx2 ] == pLcs[ nIdx1 ][ nIdx2 - 1 ] )
+ nIdx2--;
+ else
+ {
+ nIdx1--;
+ nIdx2--;
+ pLcs1[ nIdx ] = nIdx1 + nStt1;
+ pLcs2[ nIdx ] = nIdx2 + nStt2;
+ nIdx--;
+ }
+ }
+ }
+
+ return nLcsLen;
+}
+
+int CommonSubseq::IgnoreIsolatedPieces( int *pLcs1, int *pLcs2, int nLen1,
+ int nLen2, int nLcsLen, int nPieceLen )
+{
+ if( !nLcsLen )
+ {
+ return 0;
+ }
+
+ int nNext = 0;
+
+ // Don't ignore text at the beginning of the paragraphs
+ if( pLcs1[ 0 ] == 0 && pLcs2[ 0 ] == 0 )
+ {
+ while( nNext < nLcsLen - 1 && pLcs1[ nNext ] + 1 == pLcs1[ nNext + 1 ]
+ && pLcs2[ nNext ] + 1 == pLcs2[ nNext + 1 ] )
+ {
+ nNext++;
+ }
+ nNext++;
+ }
+
+ int nCnt = 1;
+
+ for( int i = nNext; i < nLcsLen; i++ )
+ {
+ if( i != nLcsLen - 1 && pLcs1[ i ] + 1 == pLcs1[ i + 1 ]
+ && pLcs2[ i ] + 1 == pLcs2[ i + 1 ] )
+ {
+ nCnt++;
+ }
+ else
+ {
+ if( nCnt > nPieceLen
+ // Don't ignore text at the end of the paragraphs
+ || ( i == nLcsLen - 1
+ && pLcs1[i] == nLen1 - 1 && pLcs2[i] == nLen2 - 1 ))
+ {
+ for( int j = i + 1 - nCnt; j <= i; j++ )
+ {
+ pLcs2[ nNext ] = pLcs2[ j ];
+ pLcs1[ nNext ] = pLcs1[ j ];
+ nNext++;
+ }
+ }
+ nCnt = 1;
+ }
+ }
+
+ return nNext;
+}
+
+LgstCommonSubseq::LgstCommonSubseq( ArrayComparator &rComparator )
+ : CommonSubseq( rComparator, CUTOFF )
+{
+ m_pBuff1.reset( new int[ rComparator.GetLen2() + 1 ] );
+ m_pBuff2.reset( new int[ rComparator.GetLen2() + 1 ] );
+
+ m_pL1.reset( new int[ rComparator.GetLen2() + 1 ] );
+ m_pL2.reset( new int[ rComparator.GetLen2() + 1 ] );
+}
+
+void LgstCommonSubseq::FindL( int *pL, int nStt1, int nEnd1,
+ int nStt2, int nEnd2 )
+{
+ int nLen1 = nEnd1 ? nEnd1 - nStt1 : m_rComparator.GetLen1();
+ int nLen2 = nEnd2 ? nEnd2 - nStt2 : m_rComparator.GetLen2();
+
+ int *currL = m_pBuff1.get();
+ int *prevL = m_pBuff2.get();
+
+ // Avoid memory corruption
+ if( nLen2 > m_rComparator.GetLen2() )
+ {
+ assert( false );
+ return;
+ }
+
+ memset( m_pBuff1.get(), 0, sizeof( m_pBuff1[0] ) * ( nLen2 + 1 ) );
+ memset( m_pBuff2.get(), 0, sizeof( m_pBuff2[0] ) * ( nLen2 + 1 ) );
+
+ // Find lcs
+ for( int i = 1; i <= nLen1; i++ )
+ {
+ for( int j = 1; j <= nLen2; j++ )
+ {
+ if( m_rComparator.Compare( nStt1 + i - 1, nStt2 + j - 1 ) )
+ currL[j] = prevL[j - 1] + 1;
+ else
+ currL[j] = std::max( currL[j - 1], prevL[j] );
+ }
+ int *tmp = currL;
+ currL = prevL;
+ prevL = tmp;
+ }
+ memcpy( pL, prevL, ( nLen2 + 1 ) * sizeof( *prevL ) );
+}
+
+int LgstCommonSubseq::HirschbergLCS( int *pLcs1, int *pLcs2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 )
+{
+ static int nLen1;
+ static int nLen2;
+ nLen1 = nEnd1 - nStt1;
+ nLen2 = nEnd2 - nStt2;
+
+ if( ( nLen1 + 1 ) * ( nLen2 + 1 ) <= CUTOFF )
+ {
+ if( !nLen1 || !nLen2 )
+ {
+ return 0;
+ }
+ return FindLCS(pLcs1, pLcs2, nStt1, nEnd1, nStt2, nEnd2);
+ }
+
+ int nMid = nLen1/2;
+
+ FindL( m_pL1.get(), nStt1, nStt1 + nMid, nStt2, nEnd2 );
+ FindL( m_pL2.get(), nStt1 + nMid, nEnd1, nStt2, nEnd2 );
+
+ int nMaxPos = 0;
+ static int nMaxVal;
+ nMaxVal = -1;
+
+ static int i;
+ for( i = 0; i <= nLen2; i++ )
+ {
+ if( m_pL1[i] + ( m_pL2[nLen2] - m_pL2[i] ) > nMaxVal )
+ {
+ nMaxPos = i;
+ nMaxVal = m_pL1[i]+( m_pL2[nLen2] - m_pL2[i] );
+ }
+ }
+
+ int nRet = HirschbergLCS( pLcs1, pLcs2, nStt1, nStt1 + nMid,
+ nStt2, nStt2 + nMaxPos );
+ nRet += HirschbergLCS( pLcs1 + nRet, pLcs2 + nRet, nStt1 + nMid, nEnd1,
+ nStt2 + nMaxPos, nEnd2 );
+
+ return nRet;
+}
+
+int LgstCommonSubseq::Find( int *pSubseq1, int *pSubseq2 )
+{
+ int nStt = 0;
+ int nCutEnd = 0;
+ int nEnd1 = m_rComparator.GetLen1();
+ int nEnd2 = m_rComparator.GetLen2();
+
+ // Check for corresponding lines in the beginning of the sequences
+ while( nStt < nEnd1 && nStt < nEnd2 && m_rComparator.Compare( nStt, nStt ) )
+ {
+ pSubseq1[ nStt ] = nStt;
+ pSubseq2[ nStt ] = nStt;
+ nStt++;
+ }
+
+ pSubseq1 += nStt;
+ pSubseq2 += nStt;
+
+ // Check for corresponding lines in the end of the sequences
+ while( nStt < nEnd1 && nStt < nEnd2
+ && m_rComparator.Compare( nEnd1 - 1, nEnd2 - 1 ) )
+ {
+ nCutEnd++;
+ nEnd1--;
+ nEnd2--;
+ }
+
+ int nLen = HirschbergLCS( pSubseq1, pSubseq2, nStt, nEnd1, nStt, nEnd2 );
+
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSubseq1[ nLen + i ] = nEnd1 + i;
+ pSubseq2[ nLen + i ] = nEnd2 + i;
+ }
+
+ return nStt + nLen + nCutEnd;
+}
+
+int FastCommonSubseq::FindFastCS( int *pSeq1, int *pSeq2, int nStt1,
+ int nEnd1, int nStt2, int nEnd2 )
+{
+ int nCutBeg = 0;
+ int nCutEnd = 0;
+
+ // Check for corresponding lines in the beginning of the sequences
+ while( nStt1 < nEnd1 && nStt2 < nEnd2 && m_rComparator.Compare( nStt1, nStt2 ) )
+ {
+ pSeq1[ nCutBeg ] = nStt1++;
+ pSeq2[ nCutBeg ] = nStt2++;
+ nCutBeg++;
+ }
+
+ pSeq1 += nCutBeg;
+ pSeq2 += nCutBeg;
+
+ // Check for corresponding lines in the end of the sequences
+ while( nStt1 < nEnd1 && nStt2 < nEnd2
+ && m_rComparator.Compare( nEnd1 - 1, nEnd2 - 1 ) )
+ {
+ nCutEnd++;
+ nEnd1--;
+ nEnd2--;
+ }
+
+ int nLen1 = nEnd1 - nStt1;
+ int nLen2 = nEnd2 - nStt2;
+
+ // Return if a sequence is empty
+ if( nLen1 <= 0 || nLen2 <= 0 )
+ {
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ i ] = nEnd1 + i;
+ pSeq2[ i ] = nEnd2 + i;
+ }
+ return nCutBeg + nCutEnd;
+ }
+
+ // Cut to LCS for small values
+ if( nLen1 < 3 || nLen2 < 3 || ( nLen1 + 1 ) * ( nLen2 + 1 ) <= CUTOFF )
+ {
+ int nLcsLen = FindLCS( pSeq1, pSeq2, nStt1, nEnd1, nStt2, nEnd2);
+
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ nLcsLen + i ] = nEnd1 + i;
+ pSeq2[ nLcsLen + i ] = nEnd2 + i;
+ }
+ return nCutBeg + nLcsLen + nCutEnd;
+ }
+
+ int nMid1 = nLen1/2;
+ int nMid2 = nLen2/2;
+
+ int nRad;
+ int nPos1 = -1, nPos2 = -1;
+
+ // Find a point of correspondence in the middle of the sequences
+ for( nRad = 0; nRad*nRad < std::min( nMid1, nMid2 ); nRad++ )
+ {
+ // Search to the left and to the right of the middle of the first sequence
+ for( int i = nMid1 - nRad; i <= nMid1 + nRad; i++ )
+ {
+ if( m_rComparator.Compare( nStt1 + i, nStt2 + nMid2 - nRad ) )
+ {
+ nPos1 = nStt1 + i;
+ nPos2 = nStt2 + nMid2 - nRad;
+ break;
+ }
+ if( m_rComparator.Compare( nStt1 + i, nStt2 + nMid2 + nRad ) )
+ {
+ nPos1 = nStt1 + i;
+ nPos2 = nStt2 + nMid2 - nRad;
+ break;
+ }
+ }
+ // Search to the left and to the right of the middle of the second sequence
+ for( int i = nMid2 - nRad; i <= nMid2 + nRad; i++ )
+ {
+ if( m_rComparator.Compare( nStt2 + nMid2 - nRad, nStt2 + i ) )
+ {
+ nPos2 = nStt2 + i;
+ nPos1 = nStt1 + nMid1 - nRad;
+ break;
+ }
+ if( m_rComparator.Compare( nStt2 + nMid2 - nRad, nStt2 + i ) )
+ {
+ nPos2 = nStt2 + i;
+ nPos1 = nStt1 + nMid1 - nRad;
+ break;
+ }
+ }
+ }
+
+ // return if no point of correspondence found
+ if( nPos1 == -1 )
+ {
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ i ] = nEnd1 + i;
+ pSeq2[ i ] = nEnd2 + i;
+ }
+ return nCutBeg + nCutEnd;
+ }
+
+ // Run the same on the sequences to the left of the correspondence point
+ int nLen = FindFastCS( pSeq1, pSeq2, nStt1, nPos1, nStt2, nPos2 );
+
+ pSeq1[ nLen ] = nPos1;
+ pSeq2[ nLen ] = nPos2;
+
+ // Run the same on the sequences to the right of the correspondence point
+ nLen += FindFastCS( pSeq1 + nLen + 1, pSeq2 + nLen + 1,
+ nPos1 + 1, nEnd1, nPos2 + 1, nEnd2 ) + 1;
+
+ for( int i = 0; i < nCutEnd; i++ )
+ {
+ pSeq1[ nLen + i ] = nEnd1 + i;
+ pSeq2[ nLen + i ] = nEnd2 + i;
+ }
+
+ return nLen + nCutBeg + nCutEnd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doccorr.cxx b/sw/source/core/doc/doccorr.cxx
new file mode 100644
index 000000000..3c3d8d7b5
--- /dev/null
+++ b/sw/source/core/doc/doccorr.cxx
@@ -0,0 +1,366 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <node.hxx>
+#include <editsh.hxx>
+#include <viscrs.hxx>
+#include <redline.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <unocrsr.hxx>
+
+namespace
+{
+ /// find the relevant section in which the SwUnoCursor may wander.
+ /// returns NULL if no restrictions apply
+ const SwStartNode* lcl_FindUnoCursorSection( const SwNode& rNode )
+ {
+ const SwStartNode* pStartNode = rNode.IsStartNode() ? rNode.GetStartNode() : rNode.StartOfSectionNode();
+ while( ( pStartNode != nullptr ) &&
+ ( pStartNode->StartOfSectionNode() != pStartNode ) &&
+ // section node is only start node allowing overlapped delete
+ pStartNode->IsSectionNode() )
+ {
+ pStartNode = pStartNode->StartOfSectionNode();
+ }
+
+ return pStartNode;
+ }
+
+ bool lcl_PosCorrAbs(SwPosition & rPos,
+ const SwPosition& rStart,
+ const SwPosition& rEnd,
+ const SwPosition& rNewPos)
+ {
+ if ((rStart <= rPos) && (rPos <= rEnd))
+ {
+ rPos = rNewPos;
+ return true;
+ }
+ return false;
+ };
+
+ bool lcl_PaMCorrAbs(SwPaM & rPam,
+ const SwPosition& rStart,
+ const SwPosition& rEnd,
+ const SwPosition& rNewPos)
+ {
+ bool bRet = false;
+ bRet |= lcl_PosCorrAbs(rPam.GetBound(), rStart, rEnd, rNewPos);
+ bRet |= lcl_PosCorrAbs(rPam.GetBound(false), rStart, rEnd, rNewPos);
+ return bRet;
+ };
+
+ void lcl_PaMCorrRel1(SwPaM * pPam,
+ SwNode const * const pOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nCntIdx)
+ {
+ for(int nb = 0; nb < 2; ++nb)
+ if(&(pPam->GetBound(bool(nb)).nNode.GetNode()) == pOldNode)
+ {
+ pPam->GetBound(bool(nb)).nNode = rNewPos.nNode;
+ pPam->GetBound(bool(nb)).nContent.Assign(
+ const_cast<SwIndexReg*>(rNewPos.nContent.GetIdxReg()),
+ nCntIdx + pPam->GetBound(bool(nb)).nContent.GetIndex());
+ }
+ }
+}
+
+void PaMCorrAbs( const SwPaM& rRange,
+ const SwPosition& rNewPos )
+{
+ SwPosition const aStart( *rRange.Start() );
+ SwPosition const aEnd( *rRange.End() );
+ SwPosition const aNewPos( rNewPos );
+ SwDoc& rDoc = aStart.nNode.GetNode().GetDoc();
+
+ if (SwCursorShell *const pShell = rDoc.GetEditShell())
+ {
+ for(const SwViewShell& rShell : pShell->GetRingContainer())
+ {
+ const SwCursorShell* pCursorShell = dynamic_cast<const SwCursorShell*>(&rShell);
+ if(!pCursorShell)
+ continue;
+ SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
+ if( _pStackCursor )
+ for (;;)
+ {
+ lcl_PaMCorrAbs( *_pStackCursor, aStart, aEnd, aNewPos );
+ if( !_pStackCursor )
+ break;
+ _pStackCursor = _pStackCursor->GetNext();
+ if( _pStackCursor == pCursorShell->GetStackCursor() )
+ break;
+ }
+
+ for(SwPaM& rPaM : const_cast<SwShellCursor*>(pCursorShell->GetCursor_())->GetRingContainer())
+ {
+ lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos );
+ }
+
+ if( pCursorShell->IsTableMode() )
+ lcl_PaMCorrAbs( const_cast<SwPaM &>(*pCursorShell->GetTableCrs()), aStart, aEnd, aNewPos );
+ }
+ }
+
+ rDoc.cleanupUnoCursorTable();
+ for(const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
+ {
+ auto pUnoCursor(pWeakUnoCursor.lock());
+ if(!pUnoCursor)
+ continue;
+
+ bool bChange = false; // has the UNO cursor been corrected?
+
+ // determine whether the UNO cursor will leave it's designated
+ // section
+ bool const bLeaveSection =
+ pUnoCursor->IsRemainInSection() &&
+ ( lcl_FindUnoCursorSection( aNewPos.nNode.GetNode() ) !=
+ lcl_FindUnoCursorSection(
+ pUnoCursor->GetPoint()->nNode.GetNode() ) );
+
+ for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
+ {
+ bChange |= lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos );
+ }
+
+ SwUnoTableCursor *const pUnoTableCursor =
+ dynamic_cast<SwUnoTableCursor *>(pUnoCursor.get());
+ if( pUnoTableCursor )
+ {
+ for(SwPaM& rPaM : pUnoTableCursor->GetSelRing().GetRingContainer())
+ {
+ bChange |=
+ lcl_PaMCorrAbs( rPaM, aStart, aEnd, aNewPos );
+ }
+ }
+
+ // if a UNO cursor leaves its designated section, we must inform
+ // (and invalidate) said cursor
+ if (bChange && bLeaveSection)
+ {
+ // the UNO cursor has left its section. We need to notify it!
+ sw::UnoCursorHint aHint;
+ pUnoCursor->m_aNotifier.Broadcast(aHint);
+ }
+ }
+}
+
+void SwDoc::CorrAbs(const SwNodeIndex& rOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nOffset,
+ bool bMoveCursor)
+{
+ SwContentNode *const pContentNode( rOldNode.GetNode().GetContentNode() );
+ SwPaM const aPam(rOldNode, 0,
+ rOldNode, pContentNode ? pContentNode->Len() : 0);
+ SwPosition aNewPos(rNewPos);
+ aNewPos.nContent += nOffset;
+
+ getIDocumentMarkAccess()->correctMarksAbsolute(rOldNode, rNewPos, nOffset);
+ // fix redlines
+ {
+ SwRedlineTable& rTable = getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type n = 0; n < rTable.size(); )
+ {
+ // is on position ??
+ SwRangeRedline *const pRedline( rTable[ n ] );
+ bool const bChanged =
+ lcl_PaMCorrAbs(*pRedline, *aPam.Start(), *aPam.End(), aNewPos);
+ // clean up empty redlines: docredln.cxx asserts these as invalid
+ if (bChanged && (*pRedline->GetPoint() == *pRedline->GetMark())
+ && (pRedline->GetContentIdx() == nullptr))
+ {
+ rTable.DeleteAndDestroy(n);
+ }
+ else
+ {
+ ++n;
+ }
+ }
+
+ // To-Do - need to add here 'SwExtraRedlineTable' also ?
+ }
+
+ if(bMoveCursor)
+ {
+ ::PaMCorrAbs(aPam, aNewPos);
+ }
+}
+
+void SwDoc::CorrAbs(
+ const SwPaM& rRange,
+ const SwPosition& rNewPos,
+ bool bMoveCursor )
+{
+ SwPosition aStart(*rRange.Start());
+ SwPosition aEnd(*rRange.End());
+
+ DelBookmarks( aStart.nNode, aEnd.nNode, nullptr, &aStart.nContent, &aEnd.nContent );
+
+ if(bMoveCursor)
+ ::PaMCorrAbs(rRange, rNewPos);
+}
+
+void SwDoc::CorrAbs(
+ const SwNodeIndex& rStartNode,
+ const SwNodeIndex& rEndNode,
+ const SwPosition& rNewPos,
+ bool bMoveCursor )
+{
+ DelBookmarks( rStartNode, rEndNode );
+
+ if(bMoveCursor)
+ {
+ SwContentNode *const pContentNode( rEndNode.GetNode().GetContentNode() );
+ SwPaM const aPam(rStartNode, 0,
+ rEndNode, pContentNode ? pContentNode->Len() : 0);
+ ::PaMCorrAbs(aPam, rNewPos);
+ }
+}
+
+void PaMCorrRel( const SwNodeIndex &rOldNode,
+ const SwPosition &rNewPos,
+ const sal_Int32 nOffset )
+{
+ const SwNode* pOldNode = &rOldNode.GetNode();
+ SwPosition aNewPos( rNewPos );
+ const SwDoc& rDoc = pOldNode->GetDoc();
+
+ const sal_Int32 nCntIdx = rNewPos.nContent.GetIndex() + nOffset;
+
+ if (SwCursorShell const* pShell = rDoc.GetEditShell())
+ {
+ for(const SwViewShell& rShell : pShell->GetRingContainer())
+ {
+ SwCursorShell* pCursorShell = const_cast<SwCursorShell*>(dynamic_cast<const SwCursorShell*>(&rShell));
+ if(!pCursorShell)
+ continue;
+ SwPaM *_pStackCursor = pCursorShell->GetStackCursor();
+ if( _pStackCursor )
+ for (;;)
+ {
+ lcl_PaMCorrRel1( _pStackCursor, pOldNode, aNewPos, nCntIdx );
+ if( !_pStackCursor )
+ break;
+ _pStackCursor = _pStackCursor->GetNext();
+ if( _pStackCursor == pCursorShell->GetStackCursor() )
+ break;
+ }
+
+ SwPaM* pStartPaM = pCursorShell->GetCursor_();
+ for(SwPaM& rPaM : pStartPaM->GetRingContainer())
+ {
+ lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx);
+ }
+
+ if( pCursorShell->IsTableMode() )
+ lcl_PaMCorrRel1( pCursorShell->GetTableCrs(), pOldNode, aNewPos, nCntIdx );
+ }
+ }
+
+ rDoc.cleanupUnoCursorTable();
+ for(const auto& pWeakUnoCursor : rDoc.mvUnoCursorTable)
+ {
+ auto pUnoCursor(pWeakUnoCursor.lock());
+ if(!pUnoCursor)
+ continue;
+ for(SwPaM& rPaM : pUnoCursor->GetRingContainer())
+ {
+ lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx );
+ }
+
+ SwUnoTableCursor* pUnoTableCursor =
+ dynamic_cast<SwUnoTableCursor*>(pUnoCursor.get());
+ if( pUnoTableCursor )
+ {
+ for(SwPaM& rPaM : pUnoTableCursor->GetSelRing().GetRingContainer())
+ {
+ lcl_PaMCorrRel1( &rPaM, pOldNode, aNewPos, nCntIdx );
+ }
+ }
+ }
+}
+
+void SwDoc::CorrRel(const SwNodeIndex& rOldNode,
+ const SwPosition& rNewPos,
+ const sal_Int32 nOffset,
+ bool bMoveCursor)
+{
+ getIDocumentMarkAccess()->correctMarksRelative(rOldNode, rNewPos, nOffset);
+
+ { // fix the Redlines
+ SwRedlineTable& rTable = getIDocumentRedlineAccess().GetRedlineTable();
+ SwPosition aNewPos(rNewPos);
+ for(SwRangeRedline* p : rTable)
+ {
+ // lies on the position ??
+ lcl_PaMCorrRel1( p, &rOldNode.GetNode(), aNewPos, aNewPos.nContent.GetIndex() + nOffset );
+ }
+
+ // To-Do - need to add here 'SwExtraRedlineTable' also ?
+ }
+
+ if(bMoveCursor)
+ ::PaMCorrRel(rOldNode, rNewPos, nOffset);
+}
+
+SwEditShell const * SwDoc::GetEditShell() const
+{
+ SwViewShell const *pCurrentView = getIDocumentLayoutAccess().GetCurrentViewShell();
+ // Layout and OLE shells should be available
+ if( pCurrentView )
+ {
+ for(const SwViewShell& rCurrentSh : pCurrentView->GetRingContainer())
+ {
+ // look for an EditShell (if it exists)
+ if( auto pEditShell = dynamic_cast<const SwEditShell *>(&rCurrentSh) )
+ {
+ return pEditShell;
+ }
+ }
+ }
+ return nullptr;
+}
+
+SwEditShell* SwDoc::GetEditShell()
+{
+ return const_cast<SwEditShell*>( const_cast<SwDoc const *>( this )->GetEditShell() );
+}
+
+::sw::IShellCursorSupplier * SwDoc::GetIShellCursorSupplier()
+{
+ return GetEditShell();
+}
+
+//bool foo()
+//{
+// bool b1 = true ? true : false;
+// bool b2 = (true ? true : false) ? true : false;
+// bool b3 = true ? (true ? true : false) : false;
+// bool b4 = true ? true : (true ? true : false);
+// return false;
+//}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docdesc.cxx b/sw/source/core/doc/docdesc.cxx
new file mode 100644
index 000000000..de64f1ae7
--- /dev/null
+++ b/sw/source/core/doc/docdesc.cxx
@@ -0,0 +1,1050 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <cmdid.h>
+#include <init.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <tools/globname.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <unotools/localedatawrapper.hxx>
+#include <fmtfsize.hxx>
+#include <fmthdft.hxx>
+#include <fmtcntnt.hxx>
+#include <ftninfo.hxx>
+#include <fesh.hxx>
+#include <ndole.hxx>
+#include <mdiexp.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <poolfmt.hxx>
+#include <docsh.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <fldbas.hxx>
+#include <strings.hrc>
+#include <hints.hxx>
+#include <SwUndoPageDesc.hxx>
+#include <pagedeschint.hxx>
+#include <tgrditem.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/syslocale.hxx>
+#include <svx/swframetypes.hxx>
+#include <o3tl/unit_conversion.hxx>
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+using namespace com::sun::star;
+
+static void lcl_DefaultPageFormat( sal_uInt16 nPoolFormatId,
+ SwFrameFormat &rFormat1,
+ SwFrameFormat &rFormat2,
+ SwFrameFormat &rFormat3,
+ SwFrameFormat &rFormat4)
+{
+ // --> #i41075# Printer on demand
+ // This function does not require a printer anymore.
+ // The default page size is obtained from the application
+ //locale
+
+ SwFormatFrameSize aFrameSize( SwFrameSize::Fixed );
+ const Size aPhysSize = SvxPaperInfo::GetDefaultPaperSize();
+ aFrameSize.SetSize( aPhysSize );
+
+ // Prepare for default margins.
+ // Margins have a default minimum size.
+ // If the printer forces a larger margins, that's ok too.
+ // The HTML page desc had A4 as page size always.
+ // This has been changed to take the page size from the printer.
+ // Unfortunately, the margins of the HTML page desc are smaller than
+ // the margins used here in general, so one extra case is required.
+ // In the long term, this needs to be changed to always keep the
+ // margins from the page desc.
+ sal_Int32 nMinTop, nMinBottom, nMinLeft, nMinRight;
+ if( RES_POOLPAGE_HTML == nPoolFormatId )
+ {
+ nMinRight = nMinTop = nMinBottom = o3tl::toTwips(1, o3tl::Length::cm);
+ nMinLeft = o3tl::toTwips(2, o3tl::Length::cm);
+ }
+ else if (!utl::ConfigManager::IsFuzzing() && MeasurementSystem::Metric == SvtSysLocale().GetLocaleData().getMeasurementSystemEnum() )
+ {
+ nMinTop = nMinBottom = nMinLeft = nMinRight = o3tl::toTwips(2, o3tl::Length::cm);
+ }
+ else
+ {
+ nMinTop = nMinBottom = o3tl::toTwips(1, o3tl::Length::in); // as in MS Word
+ nMinLeft = nMinRight = o3tl::toTwips(1.25, o3tl::Length::in);
+ }
+
+ // set margins
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ SvxULSpaceItem aUL( RES_UL_SPACE );
+
+ aUL.SetUpper( o3tl::narrowing<sal_uInt16>(nMinTop) );
+ aUL.SetLower( o3tl::narrowing<sal_uInt16>(nMinBottom) );
+ aLR.SetRight( nMinRight );
+ aLR.SetLeft( nMinLeft );
+
+ rFormat1.SetFormatAttr( aFrameSize );
+ rFormat1.SetFormatAttr( aLR );
+ rFormat1.SetFormatAttr( aUL );
+
+ rFormat2.SetFormatAttr( aFrameSize );
+ rFormat2.SetFormatAttr( aLR );
+ rFormat2.SetFormatAttr( aUL );
+
+ rFormat3.SetFormatAttr( aFrameSize );
+ rFormat3.SetFormatAttr( aLR );
+ rFormat3.SetFormatAttr( aUL );
+
+ rFormat4.SetFormatAttr( aFrameSize );
+ rFormat4.SetFormatAttr( aLR );
+ rFormat4.SetFormatAttr( aUL );
+}
+
+static void lcl_DescSetAttr( const SwFrameFormat &rSource, SwFrameFormat &rDest,
+ const bool bPage = true )
+{
+ // We should actually use ItemSet's Intersect here, but that doesn't work
+ // correctly if we have different WhichRanges.
+
+ // Take over the attributes which are of interest.
+ sal_uInt16 const aIdArr[] = {
+ RES_FRM_SIZE, RES_UL_SPACE, // [83..86
+ RES_BACKGROUND, RES_SHADOW, // [99..101
+ RES_COL, RES_COL, // [103
+ RES_TEXTGRID, RES_TEXTGRID, // [109
+ RES_FRAMEDIR, RES_FRAMEDIR, // [114
+ RES_HEADER_FOOTER_EAT_SPACING, RES_HEADER_FOOTER_EAT_SPACING, // [115
+ RES_BACKGROUND_FULL_SIZE, RES_BACKGROUND_FULL_SIZE, // [131
+ RES_RTL_GUTTER, RES_RTL_GUTTER, // [132
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER, // [143
+
+ // take over DrawingLayer FillStyles
+ XATTR_FILL_FIRST, XATTR_FILL_LAST, // [1014
+
+ 0};
+
+ const SfxPoolItem* pItem;
+ for( sal_uInt16 n = 0; aIdArr[ n ]; n += 2 )
+ {
+ for( sal_uInt16 nId = aIdArr[ n ]; nId <= aIdArr[ n+1]; ++nId )
+ {
+ // #i45539#
+ // bPage == true:
+ // All in aIdArr except from RES_HEADER_FOOTER_EAT_SPACING
+ // bPage == false:
+ // All in aIdArr except from RES_COL and RES_PAPER_BIN:
+ bool bExecuteId(true);
+
+ if(bPage)
+ {
+ // When Page
+ switch(nId)
+ {
+ // All in aIdArr except from RES_HEADER_FOOTER_EAT_SPACING
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ // take out SvxBrushItem; it's the result of the fallback
+ // at SwFormat::GetItemState and not really in state SfxItemState::SET
+ case RES_BACKGROUND:
+ bExecuteId = false;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // When not Page
+ switch(nId)
+ {
+ // When not Page: All in aIdArr except these:
+ case RES_COL:
+ case RES_PAPER_BIN:
+ case RES_BACKGROUND_FULL_SIZE:
+ case RES_RTL_GUTTER:
+ bExecuteId = false;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(bExecuteId)
+ {
+ if (SfxItemState::SET == rSource.GetItemState(nId, false, &pItem))
+ {
+ rDest.SetFormatAttr(*pItem);
+ }
+ else
+ {
+ rDest.ResetFormatAttr(nId);
+ }
+ }
+ }
+ }
+
+ // Transmit pool and help IDs too
+ rDest.SetPoolFormatId( rSource.GetPoolFormatId() );
+ rDest.SetPoolHelpId( rSource.GetPoolHelpId() );
+ rDest.SetPoolHlpFileId( rSource.GetPoolHlpFileId() );
+}
+
+namespace
+{
+ SwFrameFormat& getFrameFormat(SwPageDesc &rDesc, bool bLeft, bool bFirst)
+ {
+ if (bFirst)
+ {
+ if (bLeft)
+ return rDesc.GetFirstLeft();
+ return rDesc.GetFirstMaster();
+ }
+ return rDesc.GetLeft();
+ }
+
+ const SwFrameFormat& getConstFrameFormat(const SwPageDesc &rDesc, bool bLeft, bool bFirst)
+ {
+ return getFrameFormat(const_cast<SwPageDesc&>(rDesc), bLeft, bFirst);
+ }
+}
+
+void SwDoc::CopyMasterHeader(const SwPageDesc &rChged, const SwFormatHeader &rHead, SwPageDesc &rDesc, bool bLeft, bool bFirst)
+{
+ assert(bLeft || bFirst);
+ SwFrameFormat& rDescFrameFormat = getFrameFormat(rDesc, bLeft, bFirst);
+ if (bFirst && bLeft)
+ {
+ // special case: always shared with something
+ rDescFrameFormat.SetFormatAttr( rChged.IsFirstShared()
+ ? rDesc.GetLeft().GetHeader()
+ : rDesc.GetFirstMaster().GetHeader());
+ }
+ else if ((bFirst ? rChged.IsFirstShared() : rChged.IsHeaderShared())
+ || !rHead.IsActive())
+ {
+ // Left or first shares the header with the Master.
+ rDescFrameFormat.SetFormatAttr( rDesc.GetMaster().GetHeader() );
+ }
+ else if ( rHead.IsActive() )
+ { // Left or first gets its own header if the Format doesn't already have one.
+ // If it already has one and it points to the same Section as the
+ // Right one, it needs to get an own Header.
+ // The content is evidently copied.
+ const SwFormatHeader &rFormatHead = rDescFrameFormat.GetHeader();
+ if ( !rFormatHead.IsActive() )
+ {
+ SwFormatHeader aHead( getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::HEADERL, nullptr ) );
+ rDescFrameFormat.SetFormatAttr( aHead );
+ // take over additional attributes (margins, borders ...)
+ ::lcl_DescSetAttr( *rHead.GetHeaderFormat(), *aHead.GetHeaderFormat(), false);
+ }
+ else
+ {
+ const SwFormatContent &aCnt = rFormatHead.GetHeaderFormat()->GetContent();
+
+ if (!aCnt.GetContentIdx())
+ {
+ const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst);
+ rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetHeader() );
+ }
+ else
+ {
+ const SwFrameFormat *pRight = rHead.GetHeaderFormat();
+ if (!pRight)
+ return;
+ const SwFormatContent &aRCnt = pRight->GetContent();
+
+ if ((*aRCnt.GetContentIdx() == *aCnt.GetContentIdx()) ||
+ // The ContentIdx is _always_ different when called from
+ // SwDocStyleSheet::SetItemSet, because it deep-copies the
+ // PageDesc. So check if it was previously shared.
+ (bFirst ? rDesc.IsFirstShared() : rDesc.IsHeaderShared()))
+ {
+ SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(),
+ bFirst ? "First header" : "Left header",
+ GetDfltFrameFormat() );
+ ::lcl_DescSetAttr( *pRight, *pFormat, false );
+ // The section which the right header attribute is pointing
+ // is copied, and the Index to the StartNode is set to
+ // the left or first header attribute.
+ SwNodeIndex aTmp( GetNodes().GetEndOfAutotext() );
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( aTmp, SwHeaderStartNode );
+ SwNodeRange aRange( aRCnt.GetContentIdx()->GetNode(), SwNodeOffset(0),
+ *aRCnt.GetContentIdx()->GetNode().EndOfSectionNode() );
+ aTmp = *pSttNd->EndOfSectionNode();
+ GetNodes().Copy_( aRange, aTmp, false );
+ aTmp = *pSttNd;
+ GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, nullptr, aTmp);
+ SwPaM const source(aRange.aStart, aRange.aEnd);
+ SwPosition dest(aTmp);
+ sw::CopyBookmarks(source, dest);
+ pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
+ rDescFrameFormat.SetFormatAttr( SwFormatHeader( pFormat ) );
+ }
+ else
+ ::lcl_DescSetAttr( *pRight,
+ *const_cast<SwFrameFormat*>(rFormatHead.GetHeaderFormat()), false );
+ }
+ }
+ }
+}
+
+void SwDoc::CopyMasterFooter(const SwPageDesc &rChged, const SwFormatFooter &rFoot, SwPageDesc &rDesc, bool bLeft, bool bFirst)
+{
+ assert(bLeft || bFirst);
+ SwFrameFormat& rDescFrameFormat = getFrameFormat(rDesc, bLeft, bFirst);
+ if (bFirst && bLeft)
+ {
+ // special case: always shared with something
+ rDescFrameFormat.SetFormatAttr( rChged.IsFirstShared()
+ ? rDesc.GetLeft().GetFooter()
+ : rDesc.GetFirstMaster().GetFooter());
+ }
+ else if ((bFirst ? rChged.IsFirstShared() : rChged.IsFooterShared())
+ || !rFoot.IsActive())
+ {
+ // Left or first shares the Header with the Master.
+ rDescFrameFormat.SetFormatAttr( rDesc.GetMaster().GetFooter() );
+ }
+ else if ( rFoot.IsActive() )
+ { // Left or first gets its own Footer if the Format does not already have one.
+ // If the Format already has a Footer and it points to the same section as the Right one,
+ // it needs to get an own one.
+ // The content is evidently copied.
+ const SwFormatFooter &rFormatFoot = rDescFrameFormat.GetFooter();
+ if ( !rFormatFoot.IsActive() )
+ {
+ SwFormatFooter aFoot( getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::FOOTER, nullptr ) );
+ rDescFrameFormat.SetFormatAttr( aFoot );
+ // Take over additional attributes (margins, borders ...).
+ ::lcl_DescSetAttr( *rFoot.GetFooterFormat(), *aFoot.GetFooterFormat(), false);
+ }
+ else
+ {
+ const SwFormatContent &aLCnt = rFormatFoot.GetFooterFormat()->GetContent();
+ if( !aLCnt.GetContentIdx() )
+ {
+ const SwFrameFormat& rChgedFrameFormat = getConstFrameFormat(rChged, bLeft, bFirst);
+ rDescFrameFormat.SetFormatAttr( rChgedFrameFormat.GetFooter() );
+ }
+ else
+ {
+ const SwFrameFormat *pRight = rFoot.GetFooterFormat();
+ if (!pRight)
+ return;
+ const SwFormatContent &aRCnt = pRight->GetContent();
+
+ if ((*aRCnt.GetContentIdx() == *aLCnt.GetContentIdx()) ||
+ // The ContentIdx is _always_ different when called from
+ // SwDocStyleSheet::SetItemSet, because it deep-copies the
+ // PageDesc. So check if it was previously shared.
+ (bFirst ? rDesc.IsFirstShared() : rDesc.IsFooterShared()))
+ {
+ SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(),
+ bFirst ? "First footer" : "Left footer",
+ GetDfltFrameFormat() );
+ ::lcl_DescSetAttr( *pRight, *pFormat, false );
+ // The section to which the right footer attribute is pointing
+ // is copied, and the Index to the StartNode is set to
+ // the left footer attribute.
+ SwNodeIndex aTmp( GetNodes().GetEndOfAutotext() );
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( aTmp, SwFooterStartNode );
+ SwNodeRange aRange( aRCnt.GetContentIdx()->GetNode(), SwNodeOffset(0),
+ *aRCnt.GetContentIdx()->GetNode().EndOfSectionNode() );
+ aTmp = *pSttNd->EndOfSectionNode();
+ GetNodes().Copy_( aRange, aTmp, false );
+ aTmp = *pSttNd;
+ GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRange, nullptr, aTmp);
+ SwPaM const source(aRange.aStart, aRange.aEnd);
+ SwPosition dest(aTmp);
+ sw::CopyBookmarks(source, dest);
+ pFormat->SetFormatAttr( SwFormatContent( pSttNd ) );
+ rDescFrameFormat.SetFormatAttr( SwFormatFooter( pFormat ) );
+ }
+ else
+ ::lcl_DescSetAttr( *pRight,
+ *const_cast<SwFrameFormat*>(rFormatFoot.GetFooterFormat()), false );
+ }
+ }
+ }
+}
+
+void SwDoc::ChgPageDesc( size_t i, const SwPageDesc &rChged )
+{
+ assert(i < m_PageDescs.size() && "PageDescs is out of range.");
+
+ SwPageDesc& rDesc = *m_PageDescs[i];
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Stash header formats as needed.
+ const SwFormatHeader& rLeftHead = rChged.GetLeft().GetHeader();
+ const SwFormatHeader& rFirstMasterHead = rChged.GetFirstMaster().GetHeader();
+ const SwFormatHeader& rFirstLeftHead = rChged.GetFirstLeft().GetHeader();
+ const bool bStashLeftHead = !rDesc.IsHeaderShared() && rChged.IsHeaderShared();
+ const bool bStashFirstMasterHead = !rDesc.IsFirstShared() && rChged.IsFirstShared();
+ const bool bStashFirstLeftHead = (!rDesc.IsHeaderShared() && rChged.IsHeaderShared()) || (!rDesc.IsFirstShared() && rChged.IsFirstShared());
+ if (bStashLeftHead && rLeftHead.GetRegisteredIn() && !rDesc.HasStashedFormat(true, true, false))
+ rDesc.StashFrameFormat(rChged.GetLeft(), true, true, false);
+ if (bStashFirstMasterHead && rFirstMasterHead.GetRegisteredIn() && !rDesc.HasStashedFormat(true, false, true))
+ rDesc.StashFrameFormat(rChged.GetFirstMaster(), true, false, true);
+ if (bStashFirstLeftHead && rFirstLeftHead.GetRegisteredIn() && !rDesc.HasStashedFormat(true, true, true))
+ rDesc.StashFrameFormat(rChged.GetFirstLeft(), true, true, true);
+
+ // Stash footer formats as needed.
+ const SwFormatFooter& rLeftFoot = rChged.GetLeft().GetFooter();
+ const SwFormatFooter& rFirstMasterFoot = rChged.GetFirstMaster().GetFooter();
+ const SwFormatFooter& rFirstLeftFoot = rChged.GetFirstLeft().GetFooter();
+ const bool bStashLeftFoot = !rDesc.IsFooterShared() && rChged.IsFooterShared();
+ const bool bStashFirstMasterFoot = !rDesc.IsFirstShared() && rChged.IsFirstShared();
+ const bool bStashFirstLeftFoot = (!rDesc.IsFooterShared() && rChged.IsFooterShared()) || (!rDesc.IsFirstShared() && rChged.IsFirstShared());
+ if (bStashLeftFoot && rLeftFoot.GetRegisteredIn() && !rDesc.HasStashedFormat(false, true, false))
+ rDesc.StashFrameFormat(rChged.GetLeft(), false, true, false);
+ if (bStashFirstMasterFoot && rFirstMasterFoot.GetRegisteredIn() && !rDesc.HasStashedFormat(false, false, true))
+ rDesc.StashFrameFormat(rChged.GetFirstMaster(), false, false, true);
+ if (bStashFirstLeftFoot && rFirstLeftFoot.GetRegisteredIn() && !rDesc.HasStashedFormat(false, true, true))
+ rDesc.StashFrameFormat(rChged.GetFirstLeft(), false, true, true);
+
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoPageDesc>(rDesc, rChged, this));
+ }
+ else
+ {
+ SwUndoId nBeingUndone(SwUndoId::EMPTY);
+ GetIDocumentUndoRedo().GetFirstRedoInfo(nullptr, &nBeingUndone);
+ if (SwUndoId::HEADER_FOOTER == nBeingUndone)
+ {
+ // The last format change is currently being undone. Remove header/footer and corresponding nodes.
+ auto rDescMasterHeaderFormat = rDesc.GetMaster().GetFormatAttr(RES_HEADER);
+ auto rDescLeftHeaderFormat = rDesc.GetLeft().GetFormatAttr(RES_HEADER);
+ auto rDescFirstLeftHeaderFormat = rDesc.GetFirstLeft().GetFormatAttr(RES_HEADER);
+ auto rDescMasterFooterFormat = rDesc.GetMaster().GetFormatAttr(RES_FOOTER);
+ auto rDescLeftFooterFormat = rDesc.GetLeft().GetFormatAttr(RES_FOOTER);
+ auto rDescFirstLeftFooterFormat = rDesc.GetFirstLeft().GetFormatAttr(RES_FOOTER);
+
+ auto rChgedMasterHeaderFormat = rChged.GetMaster().GetFormatAttr(RES_HEADER);
+ auto rChgedLeftHeaderFormat = rChged.GetLeft().GetFormatAttr(RES_HEADER);
+ auto rChgedFirstLeftHeaderFormat = rChged.GetFirstLeft().GetFormatAttr(RES_HEADER);
+ auto rChgedMasterFooterFormat = rChged.GetMaster().GetFormatAttr(RES_FOOTER);
+ auto rChgedLeftFooterFormat = rChged.GetLeft().GetFormatAttr(RES_FOOTER);
+ auto rChgedFirstLeftFooterFormat = rChged.GetFirstLeft().GetFormatAttr(RES_FOOTER);
+
+ rDesc.GetMaster().ResetFormatAttr(RES_HEADER);
+ rDesc.GetLeft().ResetFormatAttr(RES_HEADER);
+ rDesc.GetFirstLeft().ResetFormatAttr(RES_HEADER);
+ rDesc.GetMaster().ResetFormatAttr(RES_FOOTER);
+ rDesc.GetLeft().ResetFormatAttr(RES_FOOTER);
+ rDesc.GetFirstLeft().ResetFormatAttr(RES_FOOTER);
+
+ auto lDelHFFormat = [this](SwClient* pToRemove, SwFrameFormat* pFormat)
+ {
+ // Code taken from lcl_DelHFFormat
+ pFormat->Remove(pToRemove);
+ SwFormatContent& rCnt = const_cast<SwFormatContent&>(pFormat->GetContent());
+ if (rCnt.GetContentIdx())
+ {
+ SwNode* pNode = nullptr;
+ {
+ SwNodeIndex aIdx(*rCnt.GetContentIdx(), 0);
+ pNode = &aIdx.GetNode();
+ SwNodeOffset nEnd = pNode->EndOfSectionIndex();
+ while (aIdx < nEnd)
+ {
+ if (pNode->IsContentNode() &&
+ static_cast<SwContentNode*>(pNode)->HasWriterListeners())
+ {
+ SwCursorShell* pShell = SwIterator<SwCursorShell, SwContentNode>(*static_cast<SwContentNode*>(pNode)).First();
+ if (pShell)
+ {
+ pShell->ParkCursor(aIdx);
+ aIdx = nEnd - 1;
+ }
+ }
+ ++aIdx;
+ pNode = &aIdx.GetNode();
+ }
+ }
+ rCnt.SetNewContentIdx(nullptr);
+
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ assert(pNode);
+ getIDocumentContentOperations().DeleteSection(pNode);
+ }
+ delete pFormat;
+ };
+
+ if (rDescMasterHeaderFormat.GetHeaderFormat() && rDescMasterHeaderFormat != rChgedMasterHeaderFormat)
+ lDelHFFormat(&rDescMasterHeaderFormat, rDescMasterHeaderFormat.GetHeaderFormat());
+ else if (rDescLeftHeaderFormat.GetHeaderFormat() && rDescLeftHeaderFormat != rChgedLeftHeaderFormat)
+ lDelHFFormat(&rDescLeftHeaderFormat, rDescLeftHeaderFormat.GetHeaderFormat());
+ else if (rDescFirstLeftHeaderFormat.GetHeaderFormat() && rDescFirstLeftHeaderFormat != rChgedFirstLeftHeaderFormat)
+ lDelHFFormat(&rDescFirstLeftHeaderFormat, rDescFirstLeftHeaderFormat.GetHeaderFormat());
+
+ else if (rDescMasterFooterFormat.GetFooterFormat() && rDescMasterFooterFormat != rChgedMasterFooterFormat)
+ lDelHFFormat(&rDescMasterFooterFormat, rDescMasterFooterFormat.GetFooterFormat());
+ else if (rDescLeftFooterFormat.GetFooterFormat() && rDescLeftFooterFormat != rChgedLeftFooterFormat)
+ lDelHFFormat(&rDescLeftFooterFormat, rDescLeftFooterFormat.GetFooterFormat());
+ else if (rDescFirstLeftFooterFormat.GetFooterFormat() && rDescFirstLeftFooterFormat != rChgedFirstLeftFooterFormat)
+ lDelHFFormat(&rDescFirstLeftFooterFormat, rDescFirstLeftFooterFormat.GetFooterFormat());
+ }
+ }
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Mirror at first if needed.
+ if ( rChged.GetUseOn() == UseOnPage::Mirror )
+ const_cast<SwPageDesc&>(rChged).Mirror();
+ else
+ {
+ // Or else transfer values from Master to Left
+ ::lcl_DescSetAttr(rChged.GetMaster(),
+ const_cast<SwPageDesc&>(rChged).GetLeft());
+ }
+ ::lcl_DescSetAttr(rChged.GetMaster(),
+ const_cast<SwPageDesc&>(rChged).GetFirstMaster());
+ ::lcl_DescSetAttr(rChged.GetLeft(),
+ const_cast<SwPageDesc&>(rChged).GetFirstLeft());
+
+ // Take over NumType.
+ if( rChged.GetNumType().GetNumberingType() != rDesc.GetNumType().GetNumberingType() )
+ {
+ rDesc.SetNumType( rChged.GetNumType() );
+ // Notify page number fields that NumFormat has changed
+ getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::PageNumber )->UpdateFields();
+ getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::RefPageGet )->UpdateFields();
+
+ // If the numbering scheme has changed we could have QuoVadis/ErgoSum texts
+ // that refer to a changed page, so we invalidate foot notes.
+ SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
+ for( SwFootnoteIdxs::size_type nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
+ {
+ SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+ }
+
+ // Take over orientation
+ rDesc.SetLandscape( rChged.GetLandscape() );
+
+ // Synch header.
+ const SwFormatHeader& rMasterHead = rChged.GetMaster().GetHeader();
+ rDesc.GetMaster().SetFormatAttr( rMasterHead );
+ const bool bRestoreStashedLeftHead = rDesc.IsHeaderShared() && !rChged.IsHeaderShared();
+ const bool bRestoreStashedFirstMasterHead = rDesc.IsFirstShared() && !rChged.IsFirstShared();
+ const bool bRestoreStashedFirstLeftHead = (rDesc.IsHeaderShared() && !rChged.IsHeaderShared()) || (rDesc.IsFirstShared() && !rChged.IsFirstShared());
+ const SwFrameFormat* pStashedLeftFormat = bRestoreStashedLeftHead ? rChged.GetStashedFrameFormat(true, true, false) : nullptr;
+ const SwFrameFormat* pStashedFirstMasterFormat = bRestoreStashedFirstMasterHead ? rChged.GetStashedFrameFormat(true, false, true) : nullptr;
+ const SwFrameFormat* pStashedFirstLeftFormat = bRestoreStashedFirstLeftHead ? rChged.GetStashedFrameFormat(true, true, true) : nullptr;
+ CopyMasterHeader(rChged, pStashedLeftFormat ? pStashedLeftFormat->GetHeader() : rMasterHead, rDesc, true, false); // Copy left header
+ CopyMasterHeader(rChged, pStashedFirstMasterFormat ? pStashedFirstMasterFormat->GetHeader() : rMasterHead, rDesc, false, true); // Copy first master
+ CopyMasterHeader(rChged, pStashedFirstLeftFormat ? pStashedFirstLeftFormat->GetHeader() : rMasterHead, rDesc, true, true); // Copy first left
+
+ if (pStashedLeftFormat)
+ rDesc.RemoveStashedFormat(true, true, false);
+
+ if (pStashedFirstMasterFormat)
+ rDesc.RemoveStashedFormat(true, false, true);
+
+ if (pStashedFirstLeftFormat)
+ rDesc.RemoveStashedFormat(true, true, true);
+
+ rDesc.ChgHeaderShare( rChged.IsHeaderShared() );
+
+ // Synch Footer.
+ const SwFormatFooter& rMasterFoot = rChged.GetMaster().GetFooter();
+ rDesc.GetMaster().SetFormatAttr( rMasterFoot );
+ const bool bRestoreStashedLeftFoot = rDesc.IsFooterShared() && !rChged.IsFooterShared();
+ const bool bRestoreStashedFirstMasterFoot = rDesc.IsFirstShared() && !rChged.IsFirstShared();
+ const bool bRestoreStashedFirstLeftFoot = (rDesc.IsFooterShared() && !rChged.IsFooterShared()) || (rDesc.IsFirstShared() && !rChged.IsFirstShared());
+ const SwFrameFormat* pStashedLeftFoot = bRestoreStashedLeftFoot ? rChged.GetStashedFrameFormat(false, true, false) : nullptr;
+ const SwFrameFormat* pStashedFirstMasterFoot = bRestoreStashedFirstMasterFoot ? rChged.GetStashedFrameFormat(false, false, true) : nullptr;
+ const SwFrameFormat* pStashedFirstLeftFoot = bRestoreStashedFirstLeftFoot ? rChged.GetStashedFrameFormat(false, true, true) : nullptr;
+ CopyMasterFooter(rChged, pStashedLeftFoot ? pStashedLeftFoot->GetFooter() : rMasterFoot, rDesc, true, false); // Copy left footer
+ CopyMasterFooter(rChged, pStashedFirstMasterFoot ? pStashedFirstMasterFoot->GetFooter() : rMasterFoot, rDesc, false, true); // Copy first master
+ CopyMasterFooter(rChged, pStashedFirstLeftFoot ? pStashedFirstLeftFoot->GetFooter() : rMasterFoot, rDesc, true, true); // Copy first left
+
+ if (pStashedLeftFormat)
+ rDesc.RemoveStashedFormat(false, true, false);
+
+ if (pStashedFirstMasterFoot)
+ rDesc.RemoveStashedFormat(false, false, true);
+
+ if (pStashedFirstLeftFoot)
+ rDesc.RemoveStashedFormat(false, true, true);
+
+ rDesc.ChgFooterShare( rChged.IsFooterShared() );
+ // there is just one first shared flag for both header and footer?
+ rDesc.ChgFirstShare( rChged.IsFirstShared() );
+
+ if ( rDesc.GetName() != rChged.GetName() )
+ rDesc.SetName( rChged.GetName() );
+
+ // A RegisterChange is triggered, if necessary
+ rDesc.SetRegisterFormatColl( rChged.GetRegisterFormatColl() );
+
+ // If UseOn or the Follow change, the paragraphs need to know about it.
+ bool bUseOn = false;
+ bool bFollow = false;
+ if (rDesc.GetUseOn() != rChged.GetUseOn())
+ {
+ rDesc.SetUseOn( rChged.GetUseOn() );
+ bUseOn = true;
+ }
+ if (rDesc.GetFollow() != rChged.GetFollow())
+ {
+ if (rChged.GetFollow() == &rChged)
+ {
+ if (rDesc.GetFollow() != &rDesc)
+ {
+ rDesc.SetFollow( &rDesc );
+ bFollow = true;
+ }
+ }
+ else
+ {
+ rDesc.SetFollow( rChged.m_pFollow );
+ bFollow = true;
+ }
+ }
+
+ if ( (bUseOn || bFollow) && pTmpRoot)
+ // Inform layout!
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->AllCheckPageDescs();
+ }
+
+ // Take over the page attributes.
+ ::lcl_DescSetAttr( rChged.GetMaster(), rDesc.GetMaster() );
+ ::lcl_DescSetAttr( rChged.GetLeft(), rDesc.GetLeft() );
+ ::lcl_DescSetAttr( rChged.GetFirstMaster(), rDesc.GetFirstMaster() );
+ ::lcl_DescSetAttr( rChged.GetFirstLeft(), rDesc.GetFirstLeft() );
+
+ // If the FootnoteInfo changes, the pages are triggered.
+ if( !(rDesc.GetFootnoteInfo() == rChged.GetFootnoteInfo()) )
+ {
+ rDesc.SetFootnoteInfo( rChged.GetFootnoteInfo() );
+ sw::PageFootnoteHint aHint;
+ rDesc.GetMaster().CallSwClientNotify(aHint);
+ rDesc.GetLeft().CallSwClientNotify(aHint);
+ rDesc.GetFirstMaster().CallSwClientNotify(aHint);
+ rDesc.GetFirstLeft().CallSwClientNotify(aHint);
+ }
+ getIDocumentState().SetModified();
+
+ SfxBindings* pBindings =
+ ( GetDocShell() && GetDocShell()->GetDispatcher() ) ? GetDocShell()->GetDispatcher()->GetBindings() : nullptr;
+ if ( pBindings )
+ {
+ pBindings->Invalidate( SID_ATTR_PAGE_COLUMN );
+ pBindings->Invalidate( SID_ATTR_PAGE );
+ pBindings->Invalidate( SID_ATTR_PAGE_SIZE );
+ pBindings->Invalidate( SID_ATTR_PAGE_ULSPACE );
+ pBindings->Invalidate( SID_ATTR_PAGE_LRSPACE );
+ }
+
+ //h/f of first-left page must not be unique but same as first master or left
+ assert((rDesc.IsFirstShared())
+ ? rDesc.GetFirstLeft().GetHeader().GetHeaderFormat() == rDesc.GetLeft().GetHeader().GetHeaderFormat()
+ : rDesc.GetFirstLeft().GetHeader().GetHeaderFormat() == rDesc.GetFirstMaster().GetHeader().GetHeaderFormat());
+ assert((rDesc.IsFirstShared())
+ ? rDesc.GetFirstLeft().GetFooter().GetFooterFormat() == rDesc.GetLeft().GetFooter().GetFooterFormat()
+ : rDesc.GetFirstLeft().GetFooter().GetFooterFormat() == rDesc.GetFirstMaster().GetFooter().GetFooterFormat());
+}
+
+/// All descriptors whose Follow point to the to-be-deleted have to be adapted.
+// #i7983#
+void SwDoc::PreDelPageDesc(SwPageDesc const * pDel)
+{
+ if (nullptr == pDel)
+ return;
+
+ // mba: test iteration as clients are removed while iteration
+ SwPageDescHint aHint( m_PageDescs[0] );
+ pDel->CallSwClientNotify( aHint );
+
+ bool bHasLayout = getIDocumentLayoutAccess().HasLayout();
+ if ( mpFootnoteInfo->DependsOn( pDel ) )
+ {
+ mpFootnoteInfo->ChgPageDesc( m_PageDescs[0] );
+ if ( bHasLayout )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->CheckFootnotePageDescs(false);
+ }
+ }
+ else if ( mpEndNoteInfo->DependsOn( pDel ) )
+ {
+ mpEndNoteInfo->ChgPageDesc( m_PageDescs[0] );
+ if ( bHasLayout )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->CheckFootnotePageDescs(true);
+ }
+ }
+
+ for (SwPageDesc* pPageDesc : m_PageDescs)
+ {
+ if (pPageDesc->GetFollow() == pDel)
+ {
+ pPageDesc->SetFollow(nullptr);
+ if( bHasLayout )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->AllCheckPageDescs();
+ }
+ }
+ }
+}
+
+void SwDoc::BroadcastStyleOperation(const OUString& rName, SfxStyleFamily eFamily,
+ SfxHintId nOp)
+{
+ if (mpDocShell)
+ {
+ SfxStyleSheetBasePool * pPool = mpDocShell->GetStyleSheetPool();
+
+ if (pPool)
+ {
+ SfxStyleSheetBase* pBase = pPool->Find(rName, eFamily);
+
+ if (pBase != nullptr)
+ pPool->Broadcast(SfxStyleSheetHint( nOp, *pBase ));
+ }
+ }
+}
+
+void SwDoc::DelPageDesc( size_t i, bool bBroadcast )
+{
+ OSL_ENSURE(i < m_PageDescs.size(), "PageDescs is out of range.");
+ OSL_ENSURE( i != 0, "You cannot delete the default Pagedesc.");
+ if ( i == 0 )
+ return;
+
+ SwPageDesc &rDel = *m_PageDescs[i];
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rDel.GetName(), SfxStyleFamily::Page,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoPageDescDelete>(rDel, this));
+ }
+
+ PreDelPageDesc(&rDel); // #i7983#
+
+ m_PageDescs.erase(m_PageDescs.begin() + i);
+ getIDocumentState().SetModified();
+}
+
+SwPageDesc* SwDoc::MakePageDesc(const OUString &rName, const SwPageDesc *pCpy,
+ bool bRegardLanguage, bool bBroadcast)
+{
+ SwPageDesc *pNew;
+ if( pCpy )
+ {
+ pNew = new SwPageDesc( *pCpy );
+ pNew->SetName( rName );
+ if( rName != pCpy->GetName() )
+ {
+ pNew->SetPoolFormatId( USHRT_MAX );
+ pNew->SetPoolHelpId( USHRT_MAX );
+ pNew->SetPoolHlpFileId( UCHAR_MAX );
+ }
+ }
+ else
+ {
+ pNew = new SwPageDesc( rName, GetDfltFrameFormat(), this );
+ // Set the default page format.
+ lcl_DefaultPageFormat( USHRT_MAX, pNew->GetMaster(), pNew->GetLeft(), pNew->GetFirstMaster(), pNew->GetFirstLeft() );
+
+ SvxFrameDirection aFrameDirection = bRegardLanguage ?
+ GetDefaultFrameDirection(GetAppLanguage())
+ : SvxFrameDirection::Horizontal_LR_TB;
+
+ pNew->GetMaster().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ pNew->GetLeft().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ pNew->GetFirstMaster().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ pNew->GetFirstLeft().SetFormatAttr( SvxFrameDirectionItem(aFrameDirection, RES_FRAMEDIR) );
+ }
+
+ std::pair<SwPageDescs::const_iterator, bool> res = m_PageDescs.push_back( pNew );
+ SAL_WARN_IF(!res.second, "sw", "MakePageDesc called with existing name" );
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Page,
+ SfxHintId::StyleSheetCreated);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoPageDescCreate>(pNew, this));
+ }
+
+ getIDocumentState().SetModified();
+ return pNew;
+}
+
+void SwDoc::PrtOLENotify( bool bAll )
+{
+ SwFEShell *pShell = nullptr;
+ {
+ SwViewShell *pSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pSh )
+ {
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ {
+ if(auto pFEShell = dynamic_cast<SwFEShell*>( &rShell))
+ {
+ pShell = pFEShell;
+ break;
+ }
+ }
+ }
+ }
+ if ( !pShell )
+ {
+ // This doesn't make sense without a Shell and thus without a client, because
+ // the communication about size changes is implemented by these components.
+ // Because we don't have a Shell we remember this unfortunate situation
+ // in the document,
+ // which is made up for later on when creating the first Shell.
+ mbOLEPrtNotifyPending = true;
+ if ( bAll )
+ mbAllOLENotify = true;
+ }
+ else
+ {
+ if ( mbAllOLENotify )
+ bAll = true;
+
+ mbOLEPrtNotifyPending = mbAllOLENotify = false;
+
+ std::unique_ptr<SwOLENodes> pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), !bAll );
+ if ( pNodes )
+ {
+ ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
+ 0, pNodes->size(), GetDocShell());
+ getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction();
+
+ for( SwOLENodes::size_type i = 0; i < pNodes->size(); ++i )
+ {
+ ::SetProgressState( i, GetDocShell() );
+
+ SwOLENode* pOLENd = (*pNodes)[i];
+ pOLENd->SetOLESizeInvalid( false );
+
+ // At first load the Infos and see if it's not already in the exclude list.
+ SvGlobalName aName;
+
+ svt::EmbeddedObjectRef& xObj = pOLENd->GetOLEObj().GetObject();
+ if ( xObj.is() )
+ aName = SvGlobalName( xObj->getClassID() );
+ else // Not yet loaded
+ {
+ // TODO/LATER: retrieve ClassID of an unloaded object
+ // aName = ????
+ }
+
+ bool bFound = false;
+ for ( std::vector<SvGlobalName>::size_type j = 0;
+ j < pGlobalOLEExcludeList->size() && !bFound;
+ ++j )
+ {
+ bFound = (*pGlobalOLEExcludeList)[j] == aName;
+ }
+ if ( bFound )
+ continue;
+
+ // We don't know it, so the object has to be loaded.
+ // If it doesn't want to be informed
+ if ( xObj.is() )
+ {
+ pGlobalOLEExcludeList->push_back( aName );
+ }
+ }
+ pNodes.reset();
+ getIDocumentLayoutAccess().GetCurrentLayout()->EndAllAction();
+ ::EndProgress( GetDocShell() );
+ }
+ }
+}
+
+IMPL_LINK_NOARG( SwDoc, DoUpdateModifiedOLE, Timer *, void )
+{
+ SwFEShell* pSh = static_cast<SwFEShell*>(GetEditShell());
+ if (!pSh)
+ return;
+
+ mbOLEPrtNotifyPending = mbAllOLENotify = false;
+
+ std::unique_ptr<SwOLENodes> pNodes = SwContentNode::CreateOLENodesArray( *GetDfltGrfFormatColl(), true );
+ if( !pNodes )
+ return;
+
+ ::StartProgress( STR_STATSTR_SWGPRTOLENOTIFY,
+ 0, pNodes->size(), GetDocShell());
+ getIDocumentLayoutAccess().GetCurrentLayout()->StartAllAction();
+ SwUpdateAttr aHint(0,0,0);
+ for( SwOLENodes::size_type i = 0; i < pNodes->size(); ++i )
+ {
+ ::SetProgressState( i, GetDocShell() );
+
+ SwOLENode* pOLENd = (*pNodes)[i];
+ pOLENd->SetOLESizeInvalid( false );
+
+ // We don't know it, so the object has to be loaded.
+ // If it doesn't want to be informed
+ if( pOLENd->GetOLEObj().GetOleRef().is() ) // Broken?
+ {
+ pOLENd->UpdateAttr(aHint);
+ }
+ }
+ getIDocumentLayoutAccess().GetCurrentLayout()->EndAllAction();
+ ::EndProgress( GetDocShell() );
+}
+
+static SwPageDesc* lcl_FindPageDesc( const SwPageDescs *pPageDescs,
+ size_t *pPos, const OUString &rName )
+{
+ SwPageDesc* res = nullptr;
+ SwPageDescs::const_iterator it = pPageDescs->find( rName );
+ if( it != pPageDescs->end() )
+ {
+ res = *it;
+ if( pPos )
+ *pPos = std::distance( pPageDescs->begin(), it );
+ }
+ else if( pPos )
+ *pPos = SIZE_MAX;
+ return res;
+}
+
+SwPageDesc* SwDoc::FindPageDesc( const OUString & rName, size_t* pPos ) const
+{
+ return lcl_FindPageDesc( &m_PageDescs, pPos, rName );
+}
+
+bool SwDoc::ContainsPageDesc( const SwPageDesc *pDesc, size_t* pPos ) const
+{
+ if( pDesc == nullptr )
+ return false;
+ if( !m_PageDescs.contains( const_cast <SwPageDesc*>( pDesc ) ) ) {
+ if( pPos )
+ *pPos = SIZE_MAX;
+ return false;
+ }
+ if( ! pPos )
+ return true;
+
+ SwPageDesc* desc = lcl_FindPageDesc(
+ &m_PageDescs, pPos, pDesc->GetName() );
+ SAL_WARN_IF( desc != pDesc, "sw", "SwPageDescs container is broken!" );
+ return true;
+}
+
+void SwDoc::DelPageDesc( const OUString & rName, bool bBroadcast )
+{
+ size_t nI;
+
+ if (FindPageDesc(rName, &nI))
+ DelPageDesc(nI, bBroadcast);
+}
+
+void SwDoc::ChgPageDesc( const OUString & rName, const SwPageDesc & rDesc)
+{
+ size_t nI;
+
+ if (FindPageDesc(rName, &nI))
+ ChgPageDesc(nI, rDesc);
+}
+
+/*
+ * The HTML import cannot resist changing the page descriptions, I don't
+ * know why. This function is meant to check the page descriptors for invalid
+ * values.
+ */
+void SwDoc::CheckDefaultPageFormat()
+{
+ for ( size_t i = 0; i < GetPageDescCnt(); ++i )
+ {
+ SwPageDesc& rDesc = GetPageDesc( i );
+
+ SwFrameFormat& rMaster = rDesc.GetMaster();
+ SwFrameFormat& rLeft = rDesc.GetLeft();
+
+ const SwFormatFrameSize& rMasterSize = rMaster.GetFrameSize();
+ const SwFormatFrameSize& rLeftSize = rLeft.GetFrameSize();
+
+ const bool bSetSize = INVALID_TWIPS == rMasterSize.GetWidth() ||
+ INVALID_TWIPS == rMasterSize.GetHeight() ||
+ INVALID_TWIPS == rLeftSize.GetWidth() ||
+ INVALID_TWIPS == rLeftSize.GetHeight();
+
+ if ( bSetSize )
+ lcl_DefaultPageFormat( rDesc.GetPoolFormatId(), rDesc.GetMaster(), rDesc.GetLeft(), rDesc.GetFirstMaster(), rDesc.GetFirstLeft() );
+ }
+}
+
+void SwDoc::SetDefaultPageMode(bool bSquaredPageMode)
+{
+ if( !bSquaredPageMode == !IsSquaredPageMode() )
+ return;
+
+ const SwTextGridItem& rGrid = GetDefault( RES_TEXTGRID );
+ SwTextGridItem aNewGrid = rGrid;
+ aNewGrid.SetSquaredMode(bSquaredPageMode);
+ aNewGrid.Init();
+ SetDefault(aNewGrid);
+
+ for ( size_t i = 0; i < GetPageDescCnt(); ++i )
+ {
+ SwPageDesc& rDesc = GetPageDesc( i );
+
+ SwFrameFormat& rMaster = rDesc.GetMaster();
+ SwFrameFormat& rLeft = rDesc.GetLeft();
+
+ SwTextGridItem aGrid(rMaster.GetFormatAttr(RES_TEXTGRID));
+ aGrid.SwitchPaperMode( bSquaredPageMode );
+ rMaster.SetFormatAttr(aGrid);
+ rLeft.SetFormatAttr(aGrid);
+ }
+}
+
+bool SwDoc::IsSquaredPageMode() const
+{
+ const SwTextGridItem& rGrid = GetDefault( RES_TEXTGRID );
+ return rGrid.IsSquaredMode();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docdraw.cxx b/sw/source/core/doc/docdraw.cxx
new file mode 100644
index 000000000..cd1883ee3
--- /dev/null
+++ b/sw/source/core/doc/docdraw.cxx
@@ -0,0 +1,699 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/colritem.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdogrp.hxx>
+#include <editeng/measfld.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <fmtanchr.hxx>
+#include <charatr.hxx>
+#include <frmfmt.hxx>
+#include <charfmt.hxx>
+#include <viewimp.hxx>
+#include <doc.hxx>
+#include <docfunc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
+#include <drawdoc.hxx>
+#include <UndoDraw.hxx>
+#include <swundo.hxx>
+#include <dcontact.hxx>
+#include <dview.hxx>
+#include <mvsave.hxx>
+#include <flyfrm.hxx>
+#include <dflyobj.hxx>
+#include <txtfrm.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fmtornt.hxx>
+#include <svx/svditer.hxx>
+
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::linguistic2;
+
+/** local method to determine positioning and alignment attributes for a drawing
+ * object, which is newly connected to the layout.
+ *
+ * Used for a newly formed group object <SwDoc::GroupSelection(..)>
+ * and the members of a destroyed group <SwDoc::UnGroupSelection(..)>
+ */
+static void lcl_AdjustPositioningAttr( SwDrawFrameFormat* _pFrameFormat,
+ const SdrObject& _rSdrObj )
+{
+ const SwContact* pContact = GetUserCall( &_rSdrObj );
+ OSL_ENSURE( pContact, "<lcl_AdjustPositioningAttr(..)> - missing contact object." );
+
+ // determine position of new group object relative to its anchor frame position
+ SwTwips nHoriRelPos = 0;
+ SwTwips nVertRelPos = 0;
+ {
+ const SwFrame* pAnchorFrame = pContact->GetAnchoredObj( &_rSdrObj )->GetAnchorFrame();
+ OSL_ENSURE( !pAnchorFrame ||
+ !pAnchorFrame->IsTextFrame() ||
+ !static_cast<const SwTextFrame*>(pAnchorFrame)->IsFollow(),
+ "<lcl_AdjustPositioningAttr(..)> - anchor frame is a follow." );
+ bool bVert = false;
+ bool bR2L = false;
+ // #i45952# - use anchor position of anchor frame, if it exist.
+ Point aAnchorPos;
+ if ( pAnchorFrame )
+ {
+ // #i45952#
+ aAnchorPos = pAnchorFrame->GetFrameAnchorPos( ::HasWrap( &_rSdrObj ) );
+ bVert = pAnchorFrame->IsVertical();
+ bR2L = pAnchorFrame->IsRightToLeft();
+ }
+ else
+ {
+ // #i45952#
+ aAnchorPos = _rSdrObj.GetAnchorPos();
+ // If no anchor frame exist - e.g. because no layout exists - the
+ // default layout direction is taken.
+ const SvxFrameDirectionItem& rDirItem =
+ _pFrameFormat->GetAttrSet().GetPool()->GetDefaultItem( RES_FRAMEDIR );
+ switch ( rDirItem.GetValue() )
+ {
+ case SvxFrameDirection::Vertical_LR_TB:
+ {
+ // vertical from left-to-right
+ bVert = true;
+ bR2L = true;
+ OSL_FAIL( "<lcl_AdjustPositioningAttr(..)> - vertical from left-to-right not supported." );
+ }
+ break;
+ case SvxFrameDirection::Vertical_RL_TB:
+ {
+ // vertical from right-to-left
+ bVert = true;
+ bR2L = false;
+ }
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ {
+ // horizontal from right-to-left
+ bVert = false;
+ bR2L = true;
+ }
+ break;
+ case SvxFrameDirection::Horizontal_LR_TB:
+ {
+ // horizontal from left-to-right
+ bVert = false;
+ bR2L = false;
+ }
+ break;
+ case SvxFrameDirection::Environment:
+ SAL_WARN("sw.core", "lcl_AdjustPositioningAttr(..) SvxFrameDirection::Environment not supported");
+ break;
+ default: break;
+ }
+
+ }
+ // use geometry of drawing object
+ const tools::Rectangle aObjRect = _rSdrObj.GetSnapRect();
+
+ if ( bVert )
+ {
+ if ( bR2L ) {
+ //SvxFrameDirection::Vertical_LR_TB
+ nHoriRelPos = aObjRect.Left() - aAnchorPos.getX();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.getY();
+ } else {
+ //SvxFrameDirection::Vertical_RL_TB
+ nHoriRelPos = aObjRect.Top() - aAnchorPos.getY();
+ nVertRelPos = aAnchorPos.getX() - aObjRect.Right();
+ }
+ }
+ else if ( bR2L )
+ {
+ nHoriRelPos = aAnchorPos.getX() - aObjRect.Right();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.getY();
+ }
+ else
+ {
+ nHoriRelPos = aObjRect.Left() - aAnchorPos.getX();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.getY();
+ }
+ }
+
+ _pFrameFormat->SetFormatAttr( SwFormatHoriOrient( nHoriRelPos, text::HoriOrientation::NONE, text::RelOrientation::FRAME ) );
+ _pFrameFormat->SetFormatAttr( SwFormatVertOrient( nVertRelPos, text::VertOrientation::NONE, text::RelOrientation::FRAME ) );
+ // #i44334#, #i44681# - positioning attributes already set
+ _pFrameFormat->PosAttrSet();
+ // #i34750# - keep current object rectangle for drawing
+ // objects. The object rectangle is used on events from the drawing layer
+ // to adjust the positioning attributes - see <SwDrawContact::Changed_(..)>.
+ {
+ const SwAnchoredObject* pAnchoredObj = pContact->GetAnchoredObj( &_rSdrObj );
+ if ( auto pAnchoredDrawObj = dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) )
+ {
+ const tools::Rectangle aObjRect = _rSdrObj.GetSnapRect();
+ const_cast<SwAnchoredDrawObject*>(pAnchoredDrawObj)
+ ->SetLastObjRect( aObjRect );
+ }
+ }
+}
+
+SwDrawContact* SwDoc::GroupSelection( SdrView& rDrawView )
+{
+ // replace marked 'virtual' drawing objects by the corresponding 'master'
+ // drawing objects.
+ SwDrawView::ReplaceMarkedDrawVirtObjs( rDrawView );
+
+ const SdrMarkList &rMrkList = rDrawView.GetMarkedObjectList();
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ bool bNoGroup = ( nullptr == pObj->getParentSdrObjectFromSdrObject() );
+ SwDrawContact* pNewContact = nullptr;
+ if( bNoGroup )
+ {
+ SwDrawFrameFormat *pFormat = nullptr;
+
+ // Revoke anchor attribute.
+ SwDrawContact *pMyContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ const SwFormatAnchor aAnch( pMyContact->GetFormat()->GetAnchor() );
+
+ std::unique_ptr<SwUndoDrawGroup> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoDrawGroup( o3tl::narrowing<sal_uInt16>(rMrkList.GetMarkCount()), *this));
+
+ // #i53320#
+ bool bGroupMembersNotPositioned( false );
+ {
+ SwAnchoredDrawObject* pAnchoredDrawObj =
+ static_cast<SwAnchoredDrawObject*>(pMyContact->GetAnchoredObj( pObj ));
+ bGroupMembersNotPositioned = pAnchoredDrawObj->NotYetPositioned();
+ }
+
+ std::map<const SdrObject*, SwFrameFormat*> vSavedTextBoxes;
+ // Destroy ContactObjects and formats.
+ for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ // #i53320#
+#if OSL_DEBUG_LEVEL > 0
+ SwAnchoredDrawObject* pAnchoredDrawObj =
+ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ));
+ OSL_ENSURE( bGroupMembersNotPositioned == pAnchoredDrawObj->NotYetPositioned(),
+ "<SwDoc::GroupSelection(..)> - group members have different positioning status!" );
+#endif
+ // Before the format will be killed, save its textbox for later use.
+ if (auto pShapeFormat = pContact->GetFormat())
+ if (auto pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
+ for (const auto& rTextBoxElement : pTextBoxNode->GetAllTextBoxes())
+ vSavedTextBoxes.emplace(rTextBoxElement);
+
+ pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+ // Deletes itself!
+ pContact->Changed(*pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ if( pUndo )
+ pUndo->AddObj( i, pFormat, pObj );
+ else
+ DelFrameFormat( pFormat );
+
+ // #i45952# - re-introduce position normalization of group member
+ // objects, because its anchor position is cleared, when they are
+ // grouped.
+ Point aAnchorPos( pObj->GetAnchorPos() );
+ pObj->NbcSetAnchorPos( Point( 0, 0 ) );
+ pObj->NbcMove( Size( aAnchorPos.getX(), aAnchorPos.getY() ) );
+ }
+
+ pFormat = MakeDrawFrameFormat( GetUniqueDrawObjectName(),
+ GetDfltFrameFormat() );
+ pFormat->SetFormatAttr( aAnch );
+ // #i36010# - set layout direction of the position
+ pFormat->SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+
+ // Add the saved textboxes to the new format.
+ auto pTextBoxNode = std::make_shared<SwTextBoxNode>(
+ SwTextBoxNode(static_cast<SwFrameFormat*>(pFormat)));
+ for (const auto& pTextBoxEntry : vSavedTextBoxes)
+ {
+ pTextBoxNode->AddTextBox(const_cast<SdrObject*>(pTextBoxEntry.first),
+ pTextBoxEntry.second);
+ pTextBoxEntry.second->SetOtherTextBoxFormats(pTextBoxNode);
+ }
+ pFormat->SetOtherTextBoxFormats(pTextBoxNode);
+ vSavedTextBoxes.clear();
+
+ rDrawView.GroupMarked();
+ OSL_ENSURE( rMrkList.GetMarkCount() == 1, "GroupMarked more or none groups." );
+
+ SdrObject* pNewGroupObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ pNewGroupObj->SetName(pFormat->GetName());
+ pNewContact = new SwDrawContact( pFormat, pNewGroupObj );
+ // #i35635#
+ pNewContact->MoveObjToVisibleLayer( pNewGroupObj );
+ pNewContact->ConnectToLayout();
+ // #i53320# - No adjustment of the positioning and alignment
+ // attributes, if group members aren't positioned yet.
+ if ( !bGroupMembersNotPositioned )
+ {
+ // #i26791# - Adjust positioning and alignment attributes.
+ lcl_AdjustPositioningAttr( pFormat, *pNewGroupObj );
+ }
+
+ if( pUndo )
+ {
+ pUndo->SetGroupFormat( pFormat );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ }
+
+ rDrawView.GroupMarked();
+ OSL_ENSURE( rMrkList.GetMarkCount() == 1, "GroupMarked more or none groups." );
+ }
+
+ return pNewContact;
+}
+
+static void lcl_CollectTextBoxesForSubGroupObj(SwFrameFormat* pTargetFormat, std::shared_ptr<SwTextBoxNode> pTextBoxNode,
+ SdrObject* pSourceObjs)
+{
+ if (auto pChildrenObjs = pSourceObjs->getChildrenOfSdrObject())
+ for (size_t i = 0; i < pChildrenObjs->GetObjCount(); ++i)
+ lcl_CollectTextBoxesForSubGroupObj(pTargetFormat, pTextBoxNode, pChildrenObjs->GetObj(i));
+ else
+ {
+ if (auto pTextBox = pTextBoxNode->GetTextBox(pSourceObjs))
+ {
+ if (!pTargetFormat->GetOtherTextBoxFormats())
+ {
+ pTargetFormat->SetOtherTextBoxFormats(std::make_shared<SwTextBoxNode>(SwTextBoxNode(pTargetFormat)));
+ }
+ pTargetFormat->GetOtherTextBoxFormats()->AddTextBox(pSourceObjs, pTextBox);
+ pTextBox->SetOtherTextBoxFormats(pTargetFormat->GetOtherTextBoxFormats());
+ }
+ }
+}
+
+
+void SwDoc::UnGroupSelection( SdrView& rDrawView )
+{
+ bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
+ if( bUndo )
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ }
+
+ // replace marked 'virtual' drawing objects by the corresponding 'master'
+ // drawing objects.
+ SwDrawView::ReplaceMarkedDrawVirtObjs( rDrawView );
+
+ const SdrMarkList &rMrkList = rDrawView.GetMarkedObjectList();
+ std::unique_ptr<std::vector< std::pair< SwDrawFrameFormat*, SdrObject* > >[]> pFormatsAndObjs;
+ const size_t nMarkCount( rMrkList.GetMarkCount() );
+ if ( nMarkCount )
+ {
+ pFormatsAndObjs.reset( new std::vector< std::pair< SwDrawFrameFormat*, SdrObject* > >[nMarkCount] );
+ SdrObject *pMyObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( !pMyObj->getParentSdrObjectFromSdrObject() )
+ {
+ for ( size_t i = 0; i < nMarkCount; ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( auto pObjGroup = dynamic_cast<SdrObjGroup*>(pObj) )
+ {
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ std::shared_ptr<SwTextBoxNode> pTextBoxNode;
+ if (auto pGroupFormat = pContact->GetFormat())
+ pTextBoxNode = pGroupFormat->GetOtherTextBoxFormats();
+
+ SwFormatAnchor aAnch( pContact->GetFormat()->GetAnchor() );
+ SdrObjList *pLst = pObjGroup->GetSubList();
+
+ SwUndoDrawUnGroup* pUndo = nullptr;
+ if( bUndo )
+ {
+ pUndo = new SwUndoDrawUnGroup( pObjGroup, *this );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ for ( size_t i2 = 0; i2 < pLst->GetObjCount(); ++i2 )
+ {
+ SdrObject* pSubObj = pLst->GetObj( i2 );
+ SwDrawFrameFormat *pFormat = MakeDrawFrameFormat( GetUniqueShapeName(),
+ GetDfltFrameFormat() );
+ pFormat->SetFormatAttr( aAnch );
+
+ if (pTextBoxNode)
+ {
+ if (!pObj->getChildrenOfSdrObject())
+ {
+ if (auto pTextBoxFormat = pTextBoxNode->GetTextBox(pSubObj))
+ {
+ auto pNewTextBoxNode =std::make_shared<SwTextBoxNode>(SwTextBoxNode(pFormat));
+ pNewTextBoxNode->AddTextBox(pSubObj, pTextBoxFormat);
+ pFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ pTextBoxFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ }
+ }
+ else
+ {
+ lcl_CollectTextBoxesForSubGroupObj(pFormat, pTextBoxNode, pSubObj);
+ }
+ }
+ // #i36010# - set layout direction of the position
+ pFormat->SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+ if (pSubObj->GetName().isEmpty())
+ pSubObj->SetName(pFormat->GetName());
+ pFormatsAndObjs[i].emplace_back( pFormat, pSubObj );
+
+ if( bUndo )
+ pUndo->AddObj( o3tl::narrowing<sal_uInt16>(i2), pFormat );
+ }
+ }
+ }
+ }
+ }
+ rDrawView.UnGroupMarked();
+ // creation of <SwDrawContact> instances for the former group members and
+ // its connection to the Writer layout.
+ for ( size_t i = 0; i < nMarkCount; ++i )
+ {
+ SwUndoDrawUnGroupConnectToLayout* pUndo = nullptr;
+ if( bUndo )
+ {
+ pUndo = new SwUndoDrawUnGroupConnectToLayout(*this);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ while ( !pFormatsAndObjs[i].empty() )
+ {
+ SwDrawFrameFormat* pFormat( pFormatsAndObjs[i].back().first );
+ SdrObject* pObj( pFormatsAndObjs[i].back().second );
+ pFormatsAndObjs[i].pop_back();
+
+ SwDrawContact* pContact = new SwDrawContact( pFormat, pObj );
+ pContact->MoveObjToVisibleLayer( pObj );
+ pContact->ConnectToLayout();
+ lcl_AdjustPositioningAttr( pFormat, *pObj );
+
+ if ( bUndo )
+ {
+ pUndo->AddFormatAndObj( pFormat, pObj );
+ }
+ }
+ }
+}
+
+bool SwDoc::DeleteSelection( SwDrawView& rDrawView )
+{
+ bool bCallBase = false;
+ const SdrMarkList &rMrkList = rDrawView.GetMarkedObjectList();
+ if( rMrkList.GetMarkCount() )
+ {
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ bool bDelMarked = true;
+
+ if( 1 == rMrkList.GetMarkCount() )
+ {
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( auto pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>( pObj) )
+ {
+ SwFlyFrameFormat* pFrameFormat = pDrawObj->GetFlyFrame()->GetFormat();
+ if( pFrameFormat )
+ {
+ getIDocumentLayoutAccess().DelLayoutFormat( pFrameFormat );
+ bDelMarked = false;
+ }
+ }
+ }
+
+ for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ {
+ SwDrawContact *pC = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ SwDrawFrameFormat *pFrameFormat = static_cast<SwDrawFrameFormat*>(pC->GetFormat());
+ if( pFrameFormat &&
+ RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId() )
+ {
+ rDrawView.MarkObj( pObj, rDrawView.Imp().GetPageView(), true );
+ --i;
+ getIDocumentLayoutAccess().DelLayoutFormat( pFrameFormat );
+ }
+ }
+ }
+
+ if( rMrkList.GetMarkCount() && bDelMarked )
+ {
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if( !pObj->getParentSdrObjectFromSdrObject() )
+ {
+ std::unique_ptr<SwUndoDrawDelete> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoDrawDelete( o3tl::narrowing<sal_uInt16>(rMrkList.GetMarkCount()), *this ));
+
+ // Destroy ContactObjects, save formats.
+ for( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ const SdrMark& rMark = *rMrkList.GetMark( i );
+ pObj = rMark.GetMarkedSdrObj();
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(pObj->GetUserCall());
+ if( pContact ) // of course not for grouped objects
+ {
+ SwDrawFrameFormat *pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+ // before delete of selection is performed, marked
+ // <SwDrawVirtObj>-objects have to be replaced by its
+ // reference objects. Thus, assert, if a
+ // <SwDrawVirt>-object is found in the mark list.
+ if ( dynamic_cast<const SwDrawVirtObj*>( pObj) != nullptr )
+ {
+ OSL_FAIL( "<SwDrawVirtObj> is still marked for delete. application will crash!" );
+ }
+ // Deletes itself!
+ pContact->Changed(*pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ if( pUndo )
+ pUndo->AddObj( pFormat, rMark );
+ else
+ DelFrameFormat( pFormat );
+ }
+ }
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ bCallBase = true;
+ }
+ getIDocumentState().SetModified();
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+
+ return bCallBase;
+}
+
+ZSortFly::ZSortFly(const SwFrameFormat* pFrameFormat, const SwFormatAnchor* pFlyAn, sal_uInt32 nArrOrdNum)
+ : m_pFormat(pFrameFormat)
+ , m_pAnchor(pFlyAn)
+ , m_nOrdNum(nArrOrdNum)
+{
+ SAL_WARN_IF(m_pFormat->Which() != RES_FLYFRMFMT && m_pFormat->Which() != RES_DRAWFRMFMT, "sw.core", "What kind of format is this?");
+ m_pFormat->CallSwClientNotify(sw::GetZOrderHint(m_nOrdNum));
+}
+
+/// In the Outliner, set a link to the method for field display in edit objects.
+void SwDoc::SetCalcFieldValueHdl(Outliner* pOutliner)
+{
+ pOutliner->SetCalcFieldValueHdl(LINK(this, SwDoc, CalcFieldValueHdl));
+}
+
+/// Recognise fields/URLs in the Outliner and set how they are displayed.
+IMPL_LINK(SwDoc, CalcFieldValueHdl, EditFieldInfo*, pInfo, void)
+{
+ if (!pInfo)
+ return;
+
+ const SvxFieldItem& rField = pInfo->GetField();
+ const SvxFieldData* pField = rField.GetField();
+
+ if (auto pDateField = dynamic_cast<const SvxDateField*>( pField))
+ {
+ // Date field
+ pInfo->SetRepresentation(
+ pDateField->GetFormatted(
+ *GetNumberFormatter(), LANGUAGE_SYSTEM) );
+ }
+ else if (auto pURLField = dynamic_cast<const SvxURLField*>( pField))
+ {
+ // URL field
+ switch ( pURLField->GetFormat() )
+ {
+ case SvxURLFormat::AppDefault: //!!! Can be set in App???
+ case SvxURLFormat::Repr:
+ pInfo->SetRepresentation(pURLField->GetRepresentation());
+ break;
+
+ case SvxURLFormat::Url:
+ pInfo->SetRepresentation(pURLField->GetURL());
+ break;
+ }
+
+ sal_uInt16 nChrFormat;
+
+ if (IsVisitedURL(pURLField->GetURL()))
+ nChrFormat = RES_POOLCHR_INET_VISIT;
+ else
+ nChrFormat = RES_POOLCHR_INET_NORMAL;
+
+ SwFormat *pFormat = getIDocumentStylePoolAccess().GetCharFormatFromPool(nChrFormat);
+
+ Color aColor(COL_LIGHTBLUE);
+ if (pFormat)
+ aColor = pFormat->GetColor().GetValue();
+
+ pInfo->SetTextColor(aColor);
+ }
+ else if (dynamic_cast<const SdrMeasureField*>( pField))
+ {
+ // Clear measure field
+ pInfo->SetFieldColor(std::optional<Color>());
+ }
+ else if ( auto pTimeField = dynamic_cast<const SvxExtTimeField*>( pField) )
+ {
+ // Time field
+ pInfo->SetRepresentation(
+ pTimeField->GetFormatted(*GetNumberFormatter(), LANGUAGE_SYSTEM) );
+ }
+ else
+ {
+ OSL_FAIL("unknown field command");
+ pInfo->SetRepresentation( OUString( '?' ) );
+ }
+}
+
+// #i62875#
+namespace docfunc
+{
+ bool ExistsDrawObjs( SwDoc& p_rDoc )
+ {
+ bool bExistsDrawObjs( false );
+
+ if ( p_rDoc.getIDocumentDrawModelAccess().GetDrawModel() &&
+ p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ) )
+ {
+ const SdrPage& rSdrPage( *(p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) );
+
+ SdrObjListIter aIter( &rSdrPage, SdrIterMode::Flat );
+ while( aIter.IsMore() )
+ {
+ SdrObject* pObj( aIter.Next() );
+ if ( !dynamic_cast<SwVirtFlyDrawObj*>(pObj) &&
+ !dynamic_cast<SwFlyDrawObj*>(pObj) )
+ {
+ bExistsDrawObjs = true;
+ break;
+ }
+ }
+ }
+
+ return bExistsDrawObjs;
+ }
+
+ bool AllDrawObjsOnPage( SwDoc& p_rDoc )
+ {
+ bool bAllDrawObjsOnPage( true );
+
+ if ( p_rDoc.getIDocumentDrawModelAccess().GetDrawModel() &&
+ p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 ) )
+ {
+ const SdrPage& rSdrPage( *(p_rDoc.getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 )) );
+
+ SdrObjListIter aIter( &rSdrPage, SdrIterMode::Flat );
+ while( aIter.IsMore() )
+ {
+ SdrObject* pObj( aIter.Next() );
+ if ( !dynamic_cast<SwVirtFlyDrawObj*>(pObj) &&
+ !dynamic_cast<SwFlyDrawObj*>(pObj) )
+ {
+ SwDrawContact* pDrawContact =
+ dynamic_cast<SwDrawContact*>(::GetUserCall( pObj ));
+ if ( pDrawContact )
+ {
+ SwAnchoredDrawObject* pAnchoredDrawObj =
+ dynamic_cast<SwAnchoredDrawObject*>(pDrawContact->GetAnchoredObj( pObj ));
+
+ // error handling
+ {
+ if ( !pAnchoredDrawObj )
+ {
+ OSL_FAIL( "<docfunc::AllDrawObjsOnPage() - missing anchored draw object" );
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ }
+
+ if ( pAnchoredDrawObj->NotYetPositioned() )
+ {
+ // The drawing object isn't yet layouted.
+ // Thus, it isn't known, if all drawing objects are on page.
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ else if ( pAnchoredDrawObj->IsOutsidePage() )
+ {
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ }
+ else
+ {
+ // contact object of drawing object doesn't exists.
+ // Thus, the drawing object isn't yet positioned.
+ // Thus, it isn't known, if all drawing objects are on page.
+ bAllDrawObjsOnPage = false;
+ break;
+ }
+ }
+ }
+ }
+
+ return bAllDrawObjsOnPage;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docedt.cxx b/sw/source/core/doc/docedt.cxx
new file mode 100644
index 000000000..3751ae034
--- /dev/null
+++ b/sw/source/core/doc/docedt.cxx
@@ -0,0 +1,882 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <acorrect.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <docsh.hxx>
+#include <docary.hxx>
+#include <mdiexp.hxx>
+#include <mvsave.hxx>
+#include <redline.hxx>
+#include <rolbck.hxx>
+#include <rootfrm.hxx>
+#include <splargs.hxx>
+#include <swcrsr.hxx>
+#include <txtfrm.hxx>
+#include <unoflatpara.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <docedt.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <frameformats.hxx>
+
+#include <vector>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::i18n;
+
+
+void RestFlyInRange( SaveFlyArr & rArr, const SwPosition& rStartPos,
+ const SwNodeIndex* pInsertPos, bool const isForceToStartPos)
+{
+ SwPosition aPos(rStartPos);
+ for(const SaveFly & rSave : rArr)
+ {
+ // create new anchor
+ SwFrameFormat* pFormat = rSave.pFrameFormat;
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+
+ if (rSave.isAtInsertNode || isForceToStartPos)
+ {
+ if( pInsertPos != nullptr )
+ {
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ aPos.nNode = *pInsertPos;
+ aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()),
+ rSave.nContentIndex);
+ }
+ else
+ {
+ assert(aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR);
+ aPos = rStartPos;
+ }
+ }
+ else
+ {
+ aPos.nNode = rStartPos.nNode;
+ aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()), 0);
+ }
+ }
+ else
+ {
+ aPos.nNode = rStartPos.nNode.GetIndex() + rSave.nNdDiff;
+ aPos.nContent.Assign(dynamic_cast<SwIndexReg*>(&aPos.nNode.GetNode()),
+ rSave.nNdDiff == SwNodeOffset(0)
+ ? rStartPos.nContent.GetIndex() + rSave.nContentIndex
+ : rSave.nContentIndex);
+ }
+
+ aAnchor.SetAnchor( &aPos );
+ pFormat->GetDoc()->GetSpzFrameFormats()->push_back( pFormat );
+ // SetFormatAttr should call Modify() and add it to the node
+ pFormat->SetFormatAttr( aAnchor );
+ SwContentNode* pCNd = aPos.nNode.GetNode().GetContentNode();
+ if (pCNd && pCNd->getLayoutFrame(pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
+ pFormat->MakeFrames();
+ }
+ sw::CheckAnchoredFlyConsistency(rStartPos.nNode.GetNode().GetDoc());
+}
+
+void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr )
+{
+ SwFrameFormats& rFormats = *rRg.aStart.GetNode().GetDoc().GetSpzFrameFormats();
+ for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ SwFrameFormat *const pFormat = rFormats[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ rRg.aStart <= pAPos->nNode && pAPos->nNode < rRg.aEnd )
+ {
+ SaveFly aSave( pAPos->nNode.GetIndex() - rRg.aStart.GetIndex(),
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ ? pAPos->nContent.GetIndex()
+ : 0,
+ pFormat, false );
+ rArr.push_back( aSave );
+ pFormat->DelFrames();
+ // set a dummy anchor position to maintain anchoring invariants
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ aAnchor.SetAnchor(nullptr);
+ pFormat->SetFormatAttr(aAnchor);
+ rFormats.erase( rFormats.begin() + n-- );
+ }
+ }
+ sw::CheckAnchoredFlyConsistency(rRg.aStart.GetNode().GetDoc());
+}
+
+void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
+ SaveFlyArr& rArr, bool bMoveAllFlys, SwHistory *const pHistory)
+{
+ SwFrameFormats& rFormats = *rPam.GetPoint()->nNode.GetNode().GetDoc().GetSpzFrameFormats();
+ SwFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+
+ const SwPosition* pPos = rPam.Start();
+ const SwNodeIndex& rSttNdIdx = pPos->nNode;
+
+ SwPosition atParaEnd(*rPam.End());
+ if (bMoveAllFlys)
+ {
+ assert(!rPam.End()->nNode.GetNode().IsTextNode() // can be table end-node
+ || rPam.End()->nContent.GetIndex() == rPam.End()->nNode.GetNode().GetTextNode()->Len());
+ ++atParaEnd.nNode;
+ atParaEnd.nContent.Assign(atParaEnd.nNode.GetNode().GetContentNode(), 0);
+ }
+
+ for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ pFormat = rFormats[n];
+ pAnchor = &pFormat->GetAnchor();
+ const SwPosition* pAPos = pAnchor->GetContentAnchor();
+ const SwNodeIndex* pContentIdx;
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ // do not move if the InsPos is in the ContentArea of the Fly
+ ( nullptr == ( pContentIdx = pFormat->GetContent().GetContentIdx() ) ||
+ (*pContentIdx >= rInsPos.nNode ||
+ rInsPos.nNode >= pContentIdx->GetNode().EndOfSectionIndex())))
+ {
+ bool bInsPos = false;
+
+ if ( (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
+ && IsDestroyFrameAnchoredAtChar(*pAPos, *rPam.Start(), *rPam.End()))
+ || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
+ && IsSelectFrameAnchoredAtPara(*pAPos, *rPam.Start(), atParaEnd,
+ bMoveAllFlys
+ ? DelContentType::CheckNoCntnt|DelContentType::AllMask
+ : DelContentType::AllMask))
+ || (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()
+ && (bInsPos = (rInsPos.nNode == pAPos->nNode)))
+ || (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()
+ && (bInsPos = (rInsPos == *pAPos))))
+ {
+ if (pHistory)
+ {
+ pHistory->AddChangeFlyAnchor(*pFormat);
+ }
+ SaveFly aSave( pAPos->nNode.GetIndex() - rSttNdIdx.GetIndex(),
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())
+ ? (pAPos->nNode == rSttNdIdx)
+ ? pAPos->nContent.GetIndex() - rPam.Start()->nContent.GetIndex()
+ : pAPos->nContent.GetIndex()
+ : 0,
+ pFormat, bInsPos );
+ rArr.push_back( aSave );
+ pFormat->DelFrames();
+ // set a dummy anchor position to maintain anchoring invariants
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ aAnchor.SetAnchor(nullptr);
+ pFormat->SetFormatAttr(aAnchor);
+ rFormats.erase( rFormats.begin() + n-- );
+ }
+ }
+ }
+ sw::CheckAnchoredFlyConsistency(rPam.GetPoint()->nNode.GetNode().GetDoc());
+}
+
+/// Delete and move all Flys at the paragraph, that are within the selection.
+/// If there is a Fly at the SPoint, it is moved onto the Mark.
+void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
+ const SwNodeIndex& rPtNdIdx,
+ SwIndex const*const pMkIdx, SwIndex const*const pPtIdx)
+{
+ assert((pMkIdx == nullptr) == (pPtIdx == nullptr));
+ SwPosition const point(pPtIdx
+ ? SwPosition(rPtNdIdx, *pPtIdx)
+ : SwPosition(rPtNdIdx));
+ SwPosition const mark(pPtIdx
+ ? SwPosition(rMkNdIdx, *pMkIdx)
+ : SwPosition(rMkNdIdx));
+ SwPosition const& rStart = mark <= point ? mark : point;
+ SwPosition const& rEnd = mark <= point ? point : mark;
+
+ SwDoc& rDoc = rMkNdIdx.GetNode().GetDoc();
+ SwFrameFormats& rTable = *rDoc.GetSpzFrameFormats();
+ for ( auto i = rTable.size(); i; )
+ {
+ SwFrameFormat *pFormat = rTable[--i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnch.GetContentAnchor();
+ if (pAPos &&
+ (((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ && IsSelectFrameAnchoredAtPara(*pAPos, rStart, rEnd, pPtIdx
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask|DelContentType::WriterfilterHack|DelContentType::CheckNoCntnt))
+ || ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ && IsDestroyFrameAnchoredAtChar(*pAPos, rStart, rEnd, pPtIdx
+ ? DelContentType::AllMask|DelContentType::WriterfilterHack
+ : DelContentType::AllMask|DelContentType::WriterfilterHack|DelContentType::CheckNoCntnt))))
+ {
+ // If the Fly is deleted, all Flys in its content have to be deleted too.
+ const SwFormatContent &rContent = pFormat->GetContent();
+ // But only fly formats own their content, not draw formats.
+ if (rContent.GetContentIdx() && pFormat->Which() == RES_FLYFRMFMT)
+ {
+ DelFlyInRange( *rContent.GetContentIdx(),
+ SwNodeIndex( *rContent.GetContentIdx()->
+ GetNode().EndOfSectionNode() ));
+ // Position could have been moved!
+ if (i > rTable.size())
+ i = rTable.size();
+ else if (i == rTable.size() || pFormat != rTable[i])
+ i = std::distance(rTable.begin(), rTable.find( pFormat ));
+ }
+
+ rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+
+ // DelLayoutFormat can also trigger the deletion of objects.
+ if (i > rTable.size())
+ i = rTable.size();
+ }
+ }
+}
+
+// #i59534: Redo of insertion of multiple text nodes runs into trouble
+// because of unnecessary expanded redlines
+// From now on this class saves the redline positions of all redlines which ends exact at the
+// insert position (node _and_ content index)
+SaveRedlEndPosForRestore::SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, sal_Int32 nCnt )
+ : mnSaveContent( nCnt )
+{
+ SwNode& rNd = rInsIdx.GetNode();
+ SwDoc& rDest = rNd.GetDoc();
+ if( rDest.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ return;
+
+ SwRedlineTable::size_type nFndPos;
+ const SwPosition* pEnd;
+ SwPosition aSrcPos( rInsIdx, SwIndex( rNd.GetContentNode(), nCnt ));
+ rDest.getIDocumentRedlineAccess().GetRedline( aSrcPos, &nFndPos );
+ const SwRangeRedline* pRedl;
+ while( nFndPos--
+ && *( pEnd = ( pRedl = rDest.getIDocumentRedlineAccess().GetRedlineTable()[ nFndPos ] )->End() ) == aSrcPos
+ && *pRedl->Start() < aSrcPos )
+ {
+ if( !mpSaveIndex )
+ {
+ mpSaveIndex.reset(new SwNodeIndex( rInsIdx, -1 ));
+ }
+ mvSavArr.push_back( const_cast<SwPosition*>(pEnd) );
+ }
+}
+
+SaveRedlEndPosForRestore::~SaveRedlEndPosForRestore()
+{
+ mpSaveIndex.reset();
+}
+
+void SaveRedlEndPosForRestore::Restore()
+{
+ if (mvSavArr.empty())
+ return;
+ ++(*mpSaveIndex);
+ SwContentNode* pNode = mpSaveIndex->GetNode().GetContentNode();
+ // If there's no content node at the remembered position, we will not restore the old position
+ // This may happen if a table (or section?) will be inserted.
+ if( pNode )
+ {
+ SwPosition aPos( *mpSaveIndex, SwIndex( pNode, mnSaveContent ));
+ for( auto n = mvSavArr.size(); n; )
+ *mvSavArr[ --n ] = aPos;
+ }
+}
+
+/// Convert list of ranges of whichIds to a corresponding list of whichIds
+static std::vector<sal_uInt16> lcl_RangesToVector(const WhichRangesContainer& pRanges)
+{
+ std::vector<sal_uInt16> aResult;
+
+ for(const WhichPair& rPair : pRanges)
+ {
+ for (sal_uInt16 j = rPair.first; j <= rPair.second; j++)
+ aResult.push_back(j);
+ }
+
+ return aResult;
+}
+
+void sw_GetJoinFlags( SwPaM& rPam, bool& rJoinText, bool& rJoinPrev )
+{
+ rJoinText = false;
+ rJoinPrev = false;
+ if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
+ return;
+
+ const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
+ SwTextNode *pSttNd = pStt->nNode.GetNode().GetTextNode();
+ if( !pSttNd )
+ return;
+
+ SwTextNode *pEndNd = pEnd->nNode.GetNode().GetTextNode();
+ rJoinText = nullptr != pEndNd;
+ if( !rJoinText )
+ return;
+
+ bool bExchange = pStt == rPam.GetPoint();
+ if( !pStt->nContent.GetIndex() &&
+ pEndNd->GetText().getLength() != pEnd->nContent.GetIndex())
+ bExchange = !bExchange;
+ if( bExchange )
+ rPam.Exchange();
+ rJoinPrev = rPam.GetPoint() == pStt;
+ OSL_ENSURE( !pStt->nContent.GetIndex() &&
+ pEndNd->GetText().getLength() != pEnd->nContent.GetIndex()
+ ? (rPam.GetPoint()->nNode < rPam.GetMark()->nNode)
+ : (rPam.GetPoint()->nNode > rPam.GetMark()->nNode),
+ "sw_GetJoinFlags");
+}
+
+bool sw_JoinText( SwPaM& rPam, bool bJoinPrev )
+{
+ SwNodeIndex aIdx( rPam.GetPoint()->nNode );
+ SwTextNode *pTextNd = aIdx.GetNode().GetTextNode();
+ SwNodeIndex aOldIdx( aIdx );
+ SwTextNode *pOldTextNd = pTextNd;
+
+ if( pTextNd && pTextNd->CanJoinNext( &aIdx ) )
+ {
+ SwDoc& rDoc = rPam.GetDoc();
+ if( bJoinPrev )
+ {
+ // We do not need to handle xmlids in this case, because
+ // it is only invoked if one paragraph is/becomes completely empty
+ // (see sw_GetJoinFlags)
+ {
+ // If PageBreaks are deleted/set, it must not be added to the Undo history!
+ // Also, deleting the Node is not added to the Undo history!
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ /* PageBreaks, PageDesc, ColumnBreaks */
+ // If we need to change something about the logic to copy the PageBreaks,
+ // PageDesc, etc. we also have to change SwUndoDelete.
+ // There, we copy the AUTO PageBreak from the GetMarkNode!
+
+ /* The MarkNode */
+ pTextNd = aIdx.GetNode().GetTextNode();
+ if (pTextNd->HasSwAttrSet())
+ {
+ if( SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState( RES_BREAK, false) )
+ pTextNd->ResetAttr( RES_BREAK );
+ if( pTextNd->HasSwAttrSet() &&
+ SfxItemState::SET == pTextNd->GetpSwAttrSet()->GetItemState( RES_PAGEDESC, false ) )
+ pTextNd->ResetAttr( RES_PAGEDESC );
+ }
+
+ /* The PointNode */
+ if( pOldTextNd->HasSwAttrSet() )
+ {
+ const SfxPoolItem* pItem;
+ SfxItemSet aSet( rDoc.GetAttrPool(), aBreakSetRange );
+ const SfxItemSet* pSet = pOldTextNd->GetpSwAttrSet();
+ if( SfxItemState::SET == pSet->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ aSet.Put( *pItem );
+ if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ aSet.Put( *pItem );
+ if( aSet.Count() )
+ pTextNd->SetAttr( aSet );
+ }
+ pOldTextNd->FormatToTextAttr( pTextNd );
+
+ const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(rDoc, aOldIdx.GetIndex(), SAL_MAX_INT32);
+
+ SwIndex aAlphaIdx(pTextNd);
+ pOldTextNd->CutText( pTextNd, aAlphaIdx, SwIndex(pOldTextNd),
+ pOldTextNd->Len() );
+ SwPosition aAlphaPos( aIdx, aAlphaIdx );
+ rDoc.CorrRel( rPam.GetPoint()->nNode, aAlphaPos, 0, true );
+
+ // move all Bookmarks/TOXMarks
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( rDoc, aIdx.GetIndex() );
+
+ // If the passed PaM is not in the Cursor ring,
+ // treat it separately (e.g. when it's being called from AutoFormat)
+ if( pOldTextNd == rPam.GetBound().nContent.GetIdxReg() )
+ rPam.GetBound() = aAlphaPos;
+ if( pOldTextNd == rPam.GetBound( false ).nContent.GetIdxReg() )
+ rPam.GetBound( false ) = aAlphaPos;
+ }
+ // delete the Node, at last!
+ SwNode::Merge const eOldMergeFlag(pOldTextNd->GetRedlineMergeFlag());
+ if (eOldMergeFlag == SwNode::Merge::First
+ && !pTextNd->IsCreateFrameWhenHidingRedlines())
+ {
+ sw::MoveDeletedPrevFrames(*pOldTextNd, *pTextNd);
+ }
+ rDoc.GetNodes().Delete( aOldIdx );
+ sw::CheckResetRedlineMergeFlag(*pTextNd,
+ eOldMergeFlag == SwNode::Merge::NonFirst
+ ? sw::Recreate::Predecessor
+ : sw::Recreate::No);
+ }
+ else
+ {
+ SwTextNode* pDelNd = aIdx.GetNode().GetTextNode();
+ if( pTextNd->Len() )
+ pDelNd->FormatToTextAttr( pTextNd );
+ else
+ {
+ /* This case was missed:
+
+ <something></something> <-- pTextNd
+ <other>ccc</other> <-- pDelNd
+
+ <something> and <other> are paragraph
+ attributes. The attribute <something> stayed if not
+ overwritten by an attribute in "ccc". Fixed by
+ first resetting all character attributes in first
+ paragraph (pTextNd).
+ */
+ std::vector<sal_uInt16> aShorts =
+ lcl_RangesToVector(aCharFormatSetRange);
+ pTextNd->ResetAttr(aShorts);
+
+ if( pDelNd->HasSwAttrSet() )
+ {
+ // only copy the character attributes
+ SfxItemSet aTmpSet( rDoc.GetAttrPool(), aCharFormatSetRange );
+ aTmpSet.Put( *pDelNd->GetpSwAttrSet() );
+ pTextNd->SetAttr( aTmpSet );
+ }
+ }
+
+ rDoc.CorrRel( aIdx, *rPam.GetPoint(), 0, true );
+ // #i100466# adjust given <rPam>, if it does not belong to the cursors
+ if ( pDelNd == rPam.GetBound().nContent.GetIdxReg() )
+ {
+ rPam.GetBound() = SwPosition( SwNodeIndex( *pTextNd ), SwIndex( pTextNd ) );
+ }
+ if( pDelNd == rPam.GetBound( false ).nContent.GetIdxReg() )
+ {
+ rPam.GetBound( false ) = SwPosition( SwNodeIndex( *pTextNd ), SwIndex( pTextNd ) );
+ }
+ pTextNd->JoinNext();
+ }
+ return true;
+ }
+ else return false;
+}
+
+static void lcl_syncGrammarError( SwTextNode &rTextNode, linguistic2::ProofreadingResult& rResult,
+ const ModelToViewHelper &rConversionMap )
+{
+ if( rTextNode.IsGrammarCheckDirty() )
+ return;
+ SwGrammarMarkUp* pWrong = rTextNode.GetGrammarCheck();
+ linguistic2::SingleProofreadingError* pArray = rResult.aErrors.getArray();
+ sal_uInt16 j = 0;
+ if( pWrong )
+ {
+ for( sal_Int32 i = 0; i < rResult.aErrors.getLength(); ++i )
+ {
+ const linguistic2::SingleProofreadingError &rError = rResult.aErrors[i];
+ const sal_Int32 nStart = rConversionMap.ConvertToModelPosition( rError.nErrorStart ).mnPos;
+ const sal_Int32 nEnd = rConversionMap.ConvertToModelPosition( rError.nErrorStart + rError.nErrorLength ).mnPos;
+ if( i != j )
+ pArray[j] = pArray[i];
+ if( pWrong->LookForEntry( nStart, nEnd ) )
+ ++j;
+ }
+ }
+ if( rResult.aErrors.getLength() > j )
+ rResult.aErrors.realloc( j );
+}
+
+uno::Any SwDoc::Spell( SwPaM& rPaM,
+ uno::Reference< XSpellChecker1 > const &xSpeller,
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
+ bool bGrammarCheck,
+ SwRootFrame const*const pLayout,
+ SwConversionArgs *pConvArgs ) const
+{
+ SwPosition* pSttPos = rPaM.Start(), *pEndPos = rPaM.End();
+
+ std::unique_ptr<SwSpellArgs> pSpellArgs;
+ if (pConvArgs)
+ {
+ pConvArgs->SetStart(pSttPos->nNode.GetNode().GetTextNode(), pSttPos->nContent);
+ pConvArgs->SetEnd( pEndPos->nNode.GetNode().GetTextNode(), pEndPos->nContent );
+ }
+ else
+ pSpellArgs.reset(new SwSpellArgs( xSpeller,
+ pSttPos->nNode.GetNode().GetTextNode(), pSttPos->nContent,
+ pEndPos->nNode.GetNode().GetTextNode(), pEndPos->nContent,
+ bGrammarCheck ));
+
+ SwNodeOffset nCurrNd = pSttPos->nNode.GetIndex();
+ SwNodeOffset nEndNd = pEndPos->nNode.GetIndex();
+
+ uno::Any aRet;
+ if( nCurrNd <= nEndNd )
+ {
+ SwContentFrame* pContentFrame;
+ bool bGoOn = true;
+ while( bGoOn )
+ {
+ SwNode* pNd = GetNodes()[ nCurrNd ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ if( nullptr != ( pContentFrame = pNd->GetTextNode()->getLayoutFrame( getIDocumentLayoutAccess().GetCurrentLayout() )) )
+ {
+ // skip protected and hidden Cells and Flys
+ if( pContentFrame->IsProtected() )
+ {
+ nCurrNd = pNd->EndOfSectionIndex();
+ }
+ else if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ if( pPageCnt && *pPageCnt && pPageSt )
+ {
+ sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum();
+ if( !*pPageSt )
+ {
+ *pPageSt = nPageNr;
+ if( *pPageCnt < *pPageSt )
+ *pPageCnt = *pPageSt;
+ }
+ tools::Long nStat;
+ if( nPageNr >= *pPageSt )
+ nStat = nPageNr - *pPageSt + 1;
+ else
+ nStat = nPageNr + *pPageCnt - *pPageSt + 1;
+ ::SetProgressState( nStat, GetDocShell() );
+ }
+ //Spell() changes the pSpellArgs in case an error is found
+ sal_Int32 nBeginGrammarCheck = 0;
+ sal_Int32 nEndGrammarCheck = 0;
+ if( pSpellArgs && pSpellArgs->bIsGrammarCheck)
+ {
+ nBeginGrammarCheck = pSpellArgs->pStartNode == pNd ? pSpellArgs->pStartIdx->GetIndex() : 0;
+ // if grammar checking starts inside of a sentence the start position has to be adjusted
+ if( nBeginGrammarCheck )
+ {
+ SwIndex aStartIndex( dynamic_cast< SwTextNode* >( pNd ), nBeginGrammarCheck );
+ SwPosition aStart( *pNd, aStartIndex );
+ SwCursor aCursor(aStart, nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence( SwCursor::START_SENT );
+ if( aOrigPos != *aCursor.GetPoint() )
+ {
+ nBeginGrammarCheck = aCursor.GetPoint()->nContent.GetIndex();
+ }
+ }
+ nEndGrammarCheck = (pSpellArgs->pEndNode == pNd)
+ ? pSpellArgs->pEndIdx->GetIndex()
+ : pNd->GetTextNode()
+ ->GetText().getLength();
+ }
+
+ sal_Int32 nSpellErrorPosition = pNd->GetTextNode()->GetText().getLength();
+ if( (!pConvArgs && pNd->GetTextNode()->Spell( pSpellArgs.get() )) ||
+ ( pConvArgs && pNd->GetTextNode()->Convert( *pConvArgs )))
+ {
+ // Cancel and remember position
+ pSttPos->nNode = nCurrNd;
+ pEndPos->nNode = nCurrNd;
+ nCurrNd = nEndNd;
+ if( pSpellArgs )
+ nSpellErrorPosition = pSpellArgs->pStartIdx->GetIndex() > pSpellArgs->pEndIdx->GetIndex() ?
+ pSpellArgs->pEndIdx->GetIndex() :
+ pSpellArgs->pStartIdx->GetIndex();
+ }
+
+ if( pSpellArgs && pSpellArgs->bIsGrammarCheck )
+ {
+ uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( GetGCIterator() );
+ if (xGCIterator.is())
+ {
+ uno::Reference< lang::XComponent > xDoc = GetDocShell()->GetBaseModel();
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pNd->GetTextNode(), pLayout);
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ // get XFlatParagraph to use...
+ uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *pNd->GetTextNode(), aExpandText, aConversionMap );
+
+ // get error position of cursor in XFlatParagraph
+ linguistic2::ProofreadingResult aResult;
+ bool bGrammarErrors;
+ do
+ {
+ aConversionMap.ConvertToViewPosition( nBeginGrammarCheck );
+ aResult = xGCIterator->checkSentenceAtPosition(
+ xDoc, xFlatPara, aExpandText, lang::Locale(), nBeginGrammarCheck, -1, -1 );
+
+ lcl_syncGrammarError( *pNd->GetTextNode(), aResult, aConversionMap );
+
+ // get suggestions to use for the specific error position
+ bGrammarErrors = aResult.aErrors.hasElements();
+ // if grammar checking doesn't have any progress then quit
+ if( aResult.nStartOfNextSentencePosition <= nBeginGrammarCheck )
+ break;
+ // prepare next iteration
+ nBeginGrammarCheck = aResult.nStartOfNextSentencePosition;
+ }
+ while( nSpellErrorPosition > aResult.nBehindEndOfSentencePosition && !bGrammarErrors && aResult.nBehindEndOfSentencePosition < nEndGrammarCheck );
+
+ if( bGrammarErrors && nSpellErrorPosition >= aResult.nBehindEndOfSentencePosition )
+ {
+ aRet <<= aResult;
+ //put the cursor to the current error
+ const linguistic2::SingleProofreadingError &rError = aResult.aErrors[0];
+ nCurrNd = pNd->GetIndex();
+ pSttPos->nNode = nCurrNd;
+ pEndPos->nNode = nCurrNd;
+ pSpellArgs->pStartNode = pNd->GetTextNode();
+ pSpellArgs->pEndNode = pNd->GetTextNode();
+ pSpellArgs->pStartIdx->Assign(pNd->GetTextNode(), aConversionMap.ConvertToModelPosition( rError.nErrorStart ).mnPos );
+ pSpellArgs->pEndIdx->Assign(pNd->GetTextNode(), aConversionMap.ConvertToModelPosition( rError.nErrorStart + rError.nErrorLength ).mnPos );
+ nCurrNd = nEndNd;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case SwNodeType::Section:
+ if( static_cast<SwSectionNode*>(pNd)->GetSection().IsProtect() ||
+ static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() )
+ nCurrNd = pNd->EndOfSectionIndex();
+ break;
+ case SwNodeType::End:
+ {
+ break;
+ }
+ default: break;
+ }
+
+ bGoOn = nCurrNd < nEndNd;
+ ++nCurrNd;
+ }
+ }
+
+ if( !aRet.hasValue() )
+ {
+ if (pConvArgs)
+ aRet <<= pConvArgs->aConvText;
+ else
+ aRet <<= pSpellArgs->xSpellAlt;
+ }
+
+ return aRet;
+}
+
+namespace {
+
+class SwHyphArgs : public SwInterHyphInfo
+{
+ const SwNode *m_pStart;
+ const SwNode *m_pEnd;
+ SwNode *m_pNode;
+ sal_uInt16 *m_pPageCnt;
+ sal_uInt16 *m_pPageSt;
+
+ SwNodeOffset m_nNode;
+ sal_Int32 m_nPamStart;
+ sal_Int32 m_nPamLen;
+
+public:
+ SwHyphArgs( const SwPaM *pPam, const Point &rPoint,
+ sal_uInt16* pPageCount, sal_uInt16* pPageStart );
+ void SetPam( SwPaM *pPam ) const;
+ void SetNode( SwNode *pNew ) { m_pNode = pNew; }
+ inline void SetRange( const SwNode *pNew );
+ void NextNode() { ++m_nNode; }
+ sal_uInt16 *GetPageCnt() { return m_pPageCnt; }
+ sal_uInt16 *GetPageSt() { return m_pPageSt; }
+};
+
+}
+
+SwHyphArgs::SwHyphArgs( const SwPaM *pPam, const Point &rCursorPos,
+ sal_uInt16* pPageCount, sal_uInt16* pPageStart )
+ : SwInterHyphInfo( rCursorPos ), m_pNode(nullptr),
+ m_pPageCnt( pPageCount ), m_pPageSt( pPageStart )
+{
+ // The following constraints have to be met:
+ // 1) there is at least one Selection
+ // 2) SPoint() == Start()
+ OSL_ENSURE( pPam->HasMark(), "SwDoc::Hyphenate: blowing in the wind");
+ OSL_ENSURE( *pPam->GetPoint() <= *pPam->GetMark(),
+ "SwDoc::Hyphenate: New York, New York");
+
+ const SwPosition *pPoint = pPam->GetPoint();
+ m_nNode = pPoint->nNode.GetIndex();
+
+ // Set start
+ m_pStart = pPoint->nNode.GetNode().GetTextNode();
+ m_nPamStart = pPoint->nContent.GetIndex();
+
+ // Set End and Length
+ const SwPosition *pMark = pPam->GetMark();
+ m_pEnd = pMark->nNode.GetNode().GetTextNode();
+ m_nPamLen = pMark->nContent.GetIndex();
+ if( pPoint->nNode == pMark->nNode )
+ m_nPamLen = m_nPamLen - pPoint->nContent.GetIndex();
+}
+
+inline void SwHyphArgs::SetRange( const SwNode *pNew )
+{
+ m_nStart = m_pStart == pNew ? m_nPamStart : 0;
+ m_nEnd = m_pEnd == pNew ? m_nPamStart + m_nPamLen : SAL_MAX_INT32;
+}
+
+void SwHyphArgs::SetPam( SwPaM *pPam ) const
+{
+ if( !m_pNode )
+ *pPam->GetPoint() = *pPam->GetMark();
+ else
+ {
+ pPam->GetPoint()->nNode = m_nNode;
+ pPam->GetPoint()->nContent.Assign( m_pNode->GetContentNode(), m_nWordStart );
+ pPam->GetMark()->nNode = m_nNode;
+ pPam->GetMark()->nContent.Assign( m_pNode->GetContentNode(),
+ m_nWordStart + m_nWordLen );
+ OSL_ENSURE( m_nNode == m_pNode->GetIndex(),
+ "SwHyphArgs::SetPam: Pam disaster" );
+ }
+}
+
+// Returns true if we can proceed.
+static bool lcl_HyphenateNode( SwNode* pNd, void* pArgs )
+{
+ // Hyphenate returns true if there is a hyphenation point and sets pPam
+ SwTextNode *pNode = pNd->GetTextNode();
+ SwHyphArgs *pHyphArgs = static_cast<SwHyphArgs*>(pArgs);
+ if( pNode )
+ {
+ // sw_redlinehide: this will be called once per node for merged nodes;
+ // the fully deleted ones won't have frames so are skipped.
+ SwContentFrame* pContentFrame = pNode->getLayoutFrame( pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pContentFrame && !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ sal_uInt16 *pPageSt = pHyphArgs->GetPageSt();
+ sal_uInt16 *pPageCnt = pHyphArgs->GetPageCnt();
+ if( pPageCnt && *pPageCnt && pPageSt )
+ {
+ sal_uInt16 nPageNr = pContentFrame->GetPhyPageNum();
+ if( !*pPageSt )
+ {
+ *pPageSt = nPageNr;
+ if( *pPageCnt < *pPageSt )
+ *pPageCnt = *pPageSt;
+ }
+ tools::Long nStat = nPageNr >= *pPageSt ? nPageNr - *pPageSt + 1
+ : nPageNr + *pPageCnt - *pPageSt + 1;
+ ::SetProgressState( nStat, pNode->GetDoc().GetDocShell() );
+ }
+ pHyphArgs->SetRange( pNd );
+ if( pNode->Hyphenate( *pHyphArgs ) )
+ {
+ pHyphArgs->SetNode( pNd );
+ return false;
+ }
+ }
+ }
+ pHyphArgs->NextNode();
+ return true;
+}
+
+uno::Reference< XHyphenatedWord > SwDoc::Hyphenate(
+ SwPaM *pPam, const Point &rCursorPos,
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ OSL_ENSURE(this == &pPam->GetDoc(), "SwDoc::Hyphenate: strangers in the night");
+
+ if( *pPam->GetPoint() > *pPam->GetMark() )
+ pPam->Exchange();
+
+ SwHyphArgs aHyphArg( pPam, rCursorPos, pPageCnt, pPageSt );
+ SwNodeIndex aTmpIdx( pPam->GetMark()->nNode, 1 );
+ GetNodes().ForEach( pPam->GetPoint()->nNode, aTmpIdx,
+ lcl_HyphenateNode, &aHyphArg );
+ aHyphArg.SetPam( pPam );
+ return aHyphArg.GetHyphWord(); // will be set by lcl_HyphenateNode
+}
+
+// Save the current values to add them as automatic entries to AutoCorrect.
+void SwDoc::SetAutoCorrExceptWord( std::unique_ptr<SwAutoCorrExceptWord> pNew )
+{
+ mpACEWord = std::move(pNew);
+}
+
+void SwDoc::DeleteAutoCorrExceptWord()
+{
+ mpACEWord.reset();
+}
+
+void SwDoc::CountWords( const SwPaM& rPaM, SwDocStat& rStat )
+{
+ // This is a modified version of SwDoc::TransliterateText
+ const SwPosition* pStt = rPaM.Start();
+ const SwPosition* pEnd = rPaM.End();
+
+ const SwNodeOffset nSttNd = pStt->nNode.GetIndex();
+ const SwNodeOffset nEndNd = pEnd->nNode.GetIndex();
+
+ const sal_Int32 nSttCnt = pStt->nContent.GetIndex();
+ const sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
+
+ const SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ if( pStt == pEnd && pTNd ) // no region ?
+ {
+ // do nothing
+ return;
+ }
+
+ if( nSttNd != nEndNd )
+ {
+ SwNodeIndex aIdx( pStt->nNode );
+ if( nSttCnt )
+ {
+ ++aIdx;
+ if( pTNd )
+ pTNd->CountWords( rStat, nSttCnt, pTNd->GetText().getLength() );
+ }
+
+ for( ; aIdx.GetIndex() < nEndNd; ++aIdx )
+ if( nullptr != ( pTNd = aIdx.GetNode().GetTextNode() ))
+ pTNd->CountWords( rStat, 0, pTNd->GetText().getLength() );
+
+ if( nEndCnt && nullptr != ( pTNd = pEnd->nNode.GetNode().GetTextNode() ))
+ pTNd->CountWords( rStat, 0, nEndCnt );
+ }
+ else if( pTNd && nSttCnt < nEndCnt )
+ pTNd->CountWords( rStat, nSttCnt, nEndCnt );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docfld.cxx b/sw/source/core/doc/docfld.cxx
new file mode 100644
index 000000000..d0e489b7b
--- /dev/null
+++ b/sw/source/core/doc/docfld.cxx
@@ -0,0 +1,1259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <hintids.hxx>
+
+#include <comphelper/string.hxx>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#ifndef UNX
+#include <unotools/transliterationwrapper.hxx>
+#endif
+#include <o3tl/string_view.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <node2lay.hxx>
+#include <cntfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <calc.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <txttxmrk.hxx>
+#include <docfld.hxx>
+#include <docufld.hxx>
+#include <usrfld.hxx>
+#include <expfld.hxx>
+#include <dbfld.hxx>
+#include <reffld.hxx>
+#include <dbmgr.hxx>
+#include <section.hxx>
+#include <docary.hxx>
+#include <authfld.hxx>
+#include <txtinet.hxx>
+#include <fmtcntnt.hxx>
+
+using namespace ::com::sun::star::uno;
+
+// the StartIndex can be supplied optionally (e.g. if it was queried before - is a virtual
+// method otherwise!)
+SetGetExpField::SetGetExpField(
+ const SwNodeIndex& rNdIdx,
+ const SwTextField* pField,
+ const SwIndex* pIdx,
+ sal_uInt16 const nPageNumber)
+ : m_nPageNumber(nPageNumber)
+{
+ m_eSetGetExpFieldType = TEXTFIELD;
+ m_CNTNT.pTextField = pField;
+ m_nNode = rNdIdx.GetIndex();
+ if( pIdx )
+ m_nContent = pIdx->GetIndex();
+ else if( pField )
+ m_nContent = pField->GetStart();
+ else
+ m_nContent = 0;
+}
+
+SetGetExpField::SetGetExpField( const SwNodeIndex& rNdIdx,
+ const SwTextINetFormat& rINet )
+{
+ m_eSetGetExpFieldType = TEXTINET;
+ m_CNTNT.pTextINet = &rINet;
+ m_nNode = rNdIdx.GetIndex();
+ m_nContent = rINet.GetStart();
+}
+
+// Extension for Sections:
+// these always have content position 0xffffffff!
+// There is never a field on this, only up to COMPLETE_STRING possible
+SetGetExpField::SetGetExpField( const SwSectionNode& rSectNd,
+ const SwPosition* pPos,
+ sal_uInt16 const nPageNumber)
+ : m_nPageNumber(nPageNumber)
+{
+ m_eSetGetExpFieldType = SECTIONNODE;
+ m_CNTNT.pSection = &rSectNd.GetSection();
+
+ if( pPos )
+ {
+ m_nNode = pPos->nNode.GetIndex();
+ m_nContent = pPos->nContent.GetIndex();
+ }
+ else
+ {
+ m_nNode = rSectNd.GetIndex();
+ m_nContent = 0;
+ }
+}
+
+SetGetExpField::SetGetExpField(::sw::mark::IBookmark const& rBookmark,
+ SwPosition const*const pPos,
+ sal_uInt16 const nPageNumber)
+ : m_nPageNumber(nPageNumber)
+{
+ m_eSetGetExpFieldType = BOOKMARK;
+ m_CNTNT.pBookmark = &rBookmark;
+
+ if (pPos)
+ {
+ m_nNode = pPos->nNode.GetIndex();
+ m_nContent = pPos->nContent.GetIndex();
+ }
+ else
+ {
+ m_nNode = rBookmark.GetMarkStart().nNode.GetIndex();
+ m_nContent = rBookmark.GetMarkStart().nContent.GetIndex();;
+ }
+}
+
+SetGetExpField::SetGetExpField( const SwTableBox& rTBox )
+{
+ m_eSetGetExpFieldType = TABLEBOX;
+ m_CNTNT.pTBox = &rTBox;
+
+ m_nNode = SwNodeOffset(0);
+ m_nContent = 0;
+ if( rTBox.GetSttNd() )
+ {
+ SwNodeIndex aIdx( *rTBox.GetSttNd() );
+ const SwContentNode* pNd = aIdx.GetNode().GetNodes().GoNext( &aIdx );
+ if( pNd )
+ m_nNode = pNd->GetIndex();
+ }
+}
+
+SetGetExpField::SetGetExpField( const SwNodeIndex& rNdIdx,
+ const SwTextTOXMark& rTOX )
+{
+ m_eSetGetExpFieldType = TEXTTOXMARK;
+ m_CNTNT.pTextTOX = &rTOX;
+ m_nNode = rNdIdx.GetIndex();
+ m_nContent = rTOX.GetStart();
+}
+
+SetGetExpField::SetGetExpField( const SwPosition& rPos )
+{
+ m_eSetGetExpFieldType = CRSRPOS;
+ m_CNTNT.pPos = &rPos;
+ m_nNode = rPos.nNode.GetIndex();
+ m_nContent = rPos.nContent.GetIndex();
+}
+
+SetGetExpField::SetGetExpField( const SwFlyFrameFormat& rFlyFormat,
+ const SwPosition* pPos )
+{
+ m_eSetGetExpFieldType = FLYFRAME;
+ m_CNTNT.pFlyFormat = &rFlyFormat;
+ if( pPos )
+ {
+ m_nNode = pPos->nNode.GetIndex();
+ m_nContent = pPos->nContent.GetIndex();
+ }
+ else
+ {
+ const SwFormatContent& rContent = rFlyFormat.GetContent();
+ m_nNode = rContent.GetContentIdx()->GetIndex() + 1;
+ m_nContent = 0;
+ }
+}
+
+void SetGetExpField::GetPosOfContent( SwPosition& rPos ) const
+{
+ const SwNode* pNd = GetNodeFromContent();
+ if( pNd )
+ pNd = pNd->GetContentNode();
+
+ if( pNd )
+ {
+ rPos.nNode = *pNd;
+ rPos.nContent.Assign( const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd)), GetCntPosFromContent() );
+ }
+ else
+ {
+ rPos.nNode = m_nNode;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContent );
+ }
+}
+
+void SetGetExpField::SetBodyPos( const SwContentFrame& rFrame )
+{
+ if( !rFrame.IsInDocBody() )
+ {
+ SwNodeIndex aIdx( rFrame.IsTextFrame()
+ ? *static_cast<SwTextFrame const&>(rFrame).GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame const&>(rFrame).GetNode() );
+ SwDoc& rDoc = aIdx.GetNodes().GetDoc();
+ SwPosition aPos( aIdx );
+ bool const bResult = ::GetBodyTextNode( rDoc, aPos, rFrame );
+ OSL_ENSURE(bResult, "Where is the field?");
+ m_nNode = aPos.nNode.GetIndex();
+ m_nContent = aPos.nContent.GetIndex();
+ }
+}
+
+bool SetGetExpField::operator==( const SetGetExpField& rField ) const
+{
+ return m_nNode == rField.m_nNode
+ && m_nContent == rField.m_nContent
+ && ( !m_CNTNT.pTextField
+ || !rField.m_CNTNT.pTextField
+ || m_CNTNT.pTextField == rField.m_CNTNT.pTextField );
+}
+
+bool SetGetExpField::operator<( const SetGetExpField& rField ) const
+{
+ if (m_nPageNumber != 0 && rField.m_nPageNumber != 0 && m_nPageNumber != rField.m_nPageNumber)
+ {
+ return m_nPageNumber < rField.m_nPageNumber;
+ }
+ if( m_nNode < rField.m_nNode || ( m_nNode == rField.m_nNode && m_nContent < rField.m_nContent ))
+ return true;
+ else if( m_nNode != rField.m_nNode || m_nContent != rField.m_nContent )
+ return false;
+
+ const SwNode *pFirst = GetNodeFromContent(),
+ *pNext = rField.GetNodeFromContent();
+
+ // Position is the same: continue only if both field pointers are set!
+ if( !pFirst || !pNext )
+ return false;
+
+ // same Section?
+ if( pFirst->StartOfSectionNode() != pNext->StartOfSectionNode() )
+ {
+ // is one in the table?
+ const SwNode *pFirstStt, *pNextStt;
+ const SwTableNode* pTableNd = pFirst->FindTableNode();
+ if( pTableNd )
+ pFirstStt = pTableNd->StartOfSectionNode();
+ else
+ pFirstStt = pFirst->StartOfSectionNode();
+
+ pTableNd = pNext->FindTableNode();
+ if( pTableNd )
+ pNextStt = pTableNd->StartOfSectionNode();
+ else
+ pNextStt = pNext->StartOfSectionNode();
+
+ if( pFirstStt != pNextStt )
+ {
+ if( pFirst->IsTextNode() && pNext->IsTextNode() &&
+ ( pFirst->FindFlyStartNode() || pNext->FindFlyStartNode() ))
+ {
+ // FIXME: in NewFieldPortion(), SwGetExpField are expanded via
+ // DocumentFieldsManager::FieldsToExpand() calling
+ // std::upper_bound binary search function - the sort order
+ // depends on the fly positions in the layout, but the fly
+ // positions depend on the expansion of the SwGetExpField!
+ // This circular dep will cause trouble, it would be better to
+ // use only model positions (anchor), but then how to compare
+ // at-page anchored flys which don't have a model anchor?
+ return ::IsFrameBehind( *pNext->GetTextNode(), m_nContent, *pFirst->GetTextNode(), m_nContent );
+ }
+ return pFirstStt->GetIndex() < pNextStt->GetIndex();
+ }
+ }
+
+ // same Section: is the field in the same Node?
+ if( pFirst != pNext )
+ return pFirst->GetIndex() < pNext->GetIndex();
+
+ // same Node in the Section, check Position in the Node
+ return GetCntPosFromContent() < rField.GetCntPosFromContent();
+}
+
+const SwNode* SetGetExpField::GetNodeFromContent() const
+{
+ const SwNode* pRet = nullptr;
+ if( m_CNTNT.pTextField )
+ switch( m_eSetGetExpFieldType )
+ {
+ case TEXTFIELD:
+ pRet = &m_CNTNT.pTextField->GetTextNode();
+ break;
+
+ case TEXTINET:
+ pRet = &m_CNTNT.pTextINet->GetTextNode();
+ break;
+
+ case SECTIONNODE:
+ pRet = m_CNTNT.pSection->GetFormat()->GetSectionNode();
+ break;
+
+ case BOOKMARK:
+ pRet = &m_CNTNT.pBookmark->GetMarkStart().nNode.GetNode();
+ break;
+
+ case CRSRPOS:
+ pRet = &m_CNTNT.pPos->nNode.GetNode();
+ break;
+
+ case TEXTTOXMARK:
+ pRet = &m_CNTNT.pTextTOX->GetTextNode();
+ break;
+
+ case TABLEBOX:
+ if( m_CNTNT.pTBox->GetSttNd() )
+ {
+ SwNodeIndex aIdx( *m_CNTNT.pTBox->GetSttNd() );
+ pRet = aIdx.GetNode().GetNodes().GoNext( &aIdx );
+ }
+ break;
+
+ case FLYFRAME:
+ {
+ SwNodeIndex aIdx( *m_CNTNT.pFlyFormat->GetContent().GetContentIdx() );
+ pRet = aIdx.GetNode().GetNodes().GoNext( &aIdx );
+ }
+ break;
+ }
+ return pRet;
+}
+
+sal_Int32 SetGetExpField::GetCntPosFromContent() const
+{
+ sal_Int32 nRet = 0;
+ if( m_CNTNT.pTextField )
+ switch( m_eSetGetExpFieldType )
+ {
+ case TEXTFIELD:
+ nRet = m_CNTNT.pTextField->GetStart();
+ break;
+ case TEXTINET:
+ nRet = m_CNTNT.pTextINet->GetStart();
+ break;
+ case TEXTTOXMARK:
+ nRet = m_CNTNT.pTextTOX->GetStart();
+ break;
+ case BOOKMARK:
+ nRet = m_CNTNT.pBookmark->GetMarkStart().nContent.GetIndex();
+ break;
+ case CRSRPOS:
+ nRet = m_CNTNT.pPos->nContent.GetIndex();
+ break;
+ default:
+ break;
+ }
+ return nRet;
+}
+
+HashStr::HashStr( const OUString& rName, const OUString& rText,
+ HashStr* pNxt )
+ : SwHash( rName ), aSetStr( rText )
+{
+ pNext.reset( pNxt );
+}
+
+/// Look up the Name, if it is present, return its String, otherwise return an empty String
+OUString LookString( SwHashTable<HashStr> const & rTable, const OUString& rName )
+{
+ HashStr* pFnd = rTable.Find( comphelper::string::strip(rName, ' ') );
+ if( pFnd )
+ return pFnd->aSetStr;
+
+ return OUString();
+}
+
+SwDBData const & SwDoc::GetDBData()
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if(maDBData.sDataSource.isEmpty())
+ {
+ // Similar to: SwEditShell::IsAnyDatabaseFieldInDoc
+ for (const auto& pFieldType : *getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if (IsUsed(*pFieldType))
+ {
+ SwFieldIds nWhich = pFieldType->Which();
+ switch(nWhich)
+ {
+ case SwFieldIds::Database:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbSetNumber:
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ if(vFields.size())
+ {
+ if(SwFieldIds::Database == nWhich)
+ maDBData = static_cast<SwDBFieldType*>(vFields.front()->GetField()->GetTyp())->GetDBData();
+ else
+ maDBData = static_cast<SwDBNameInfField*> (vFields.front()->GetField())->GetRealDBData();
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ if(maDBData.sDataSource.isEmpty())
+ maDBData = SwDBManager::GetAddressDBName();
+#endif
+ return maDBData;
+}
+
+void SwDoc::SetInitDBFields( bool b )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) b;
+#else
+ GetDBManager()->SetInitDBFields( b );
+#endif
+}
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+
+/// Get all databases that are used by fields
+static OUString lcl_DBDataToString(const SwDBData& rData)
+{
+ return rData.sDataSource + OUStringChar(DB_DELIM)
+ + rData.sCommand + OUStringChar(DB_DELIM)
+ + OUString::number(rData.nCommandType);
+}
+
+#endif
+
+void SwDoc::GetAllUsedDB( std::vector<OUString>& rDBNameList,
+ const std::vector<OUString>* pAllDBNames )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDBNameList;
+ (void) pAllDBNames;
+#else
+ std::vector<OUString> aUsedDBNames;
+ std::vector<OUString> aAllDBNames;
+
+ if( !pAllDBNames )
+ {
+ GetAllDBNames( aAllDBNames );
+ pAllDBNames = &aAllDBNames;
+ }
+
+ SwSectionFormats& rArr = GetSections();
+ for (auto n = rArr.size(); n; )
+ {
+ SwSection* pSect = rArr[ --n ]->GetSection();
+
+ if( pSect )
+ {
+ AddUsedDBToList( rDBNameList, FindUsedDBs( *pAllDBNames,
+ pSect->GetCondition(), aUsedDBNames ) );
+ aUsedDBNames.clear();
+ }
+ }
+
+ for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
+ {
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(nWhichHint))
+ {
+ 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;
+
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::DatabaseName:
+ AddUsedDBToList( rDBNameList,
+ lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
+ break;
+
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ AddUsedDBToList( rDBNameList,
+ lcl_DBDataToString(static_cast<const SwDBNameInfField*>(pField)->GetRealDBData() ));
+ [[fallthrough]]; // JP: is that right like that?
+
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::HiddenPara:
+ AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
+ pField->GetPar1(), aUsedDBNames ));
+ aUsedDBNames.clear();
+ break;
+
+ case SwFieldIds::SetExp:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::Table:
+ AddUsedDBToList(rDBNameList, FindUsedDBs( *pAllDBNames,
+ pField->GetFormula(), aUsedDBNames ));
+ aUsedDBNames.clear();
+ break;
+ default: break;
+ }
+ }
+ }
+#endif
+}
+
+void SwDoc::GetAllDBNames( std::vector<OUString>& rAllDBNames )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rAllDBNames;
+#else
+ SwDBManager* pMgr = GetDBManager();
+
+ const SwDSParams_t& rArr = pMgr->GetDSParamArray();
+ for (const auto& pParam : rArr)
+ {
+ rAllDBNames.emplace_back(pParam->sDataSource + OUStringChar(DB_DELIM) + pParam->sCommand);
+ }
+#endif
+}
+
+std::vector<OUString>& SwDoc::FindUsedDBs( const std::vector<OUString>& rAllDBNames,
+ const OUString& rFormula,
+ std::vector<OUString>& rUsedDBNames )
+{
+ const CharClass& rCC = GetAppCharClass();
+#ifndef UNX
+ const OUString sFormula(rCC.uppercase( rFormula ));
+#else
+ const OUString sFormula(rFormula);
+#endif
+
+ for (const auto &sItem : rAllDBNames)
+ {
+ sal_Int32 nPos = sFormula.indexOf( sItem );
+ if( nPos>=0 &&
+ sFormula[ nPos + sItem.getLength() ] == '.' &&
+ (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 )))
+ {
+ // Look up table name
+ nPos += sItem.getLength() + 1;
+ const sal_Int32 nEndPos = sFormula.indexOf('.', nPos);
+ if( nEndPos>=0 )
+ {
+ rUsedDBNames.emplace_back(sItem + OUStringChar(DB_DELIM) + sFormula.subView( nPos, nEndPos - nPos ));
+ }
+ }
+ }
+ return rUsedDBNames;
+}
+
+void SwDoc::AddUsedDBToList( std::vector<OUString>& rDBNameList,
+ const std::vector<OUString>& rUsedDBNames )
+{
+ for ( const auto &sName : rUsedDBNames )
+ AddUsedDBToList( rDBNameList, sName );
+}
+
+void SwDoc::AddUsedDBToList( std::vector<OUString>& rDBNameList, const OUString& rDBName)
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDBNameList;
+ (void) rDBName;
+#else
+ if( rDBName.isEmpty() )
+ return;
+
+#ifdef UNX
+ for( const auto &sName : rDBNameList )
+ if( rDBName == o3tl::getToken(sName, 0, ';') )
+ return;
+#else
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ for( const auto &sName : rDBNameList )
+ if( rSCmp.isEqual( rDBName, sName.getToken(0, ';') ) )
+ return;
+#endif
+
+ SwDBData aData;
+ sal_Int32 nIdx{ 0 };
+ aData.sDataSource = rDBName.getToken(0, DB_DELIM, nIdx);
+ aData.sCommand = rDBName.getToken(0, DB_DELIM, nIdx);
+ aData.nCommandType = -1;
+ GetDBManager()->CreateDSData(aData);
+ rDBNameList.push_back(rDBName);
+#endif
+}
+
+void SwDoc::ChangeDBFields( const std::vector<OUString>& rOldNames,
+ const OUString& rNewName )
+{
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rOldNames;
+ (void) rNewName;
+#else
+ SwDBData aNewDBData;
+ sal_Int32 nIdx{ 0 };
+ aNewDBData.sDataSource = rNewName.getToken(0, DB_DELIM, nIdx);
+ aNewDBData.sCommand = rNewName.getToken(0, DB_DELIM, nIdx);
+ aNewDBData.nCommandType = o3tl::toInt32(o3tl::getToken(rNewName, 0, DB_DELIM, nIdx));
+
+ SwSectionFormats& rArr = GetSections();
+ for (auto n = rArr.size(); n; )
+ {
+ SwSection* pSect = rArr[ --n ]->GetSection();
+
+ if( pSect )
+ {
+ pSect->SetCondition(ReplaceUsedDBs(rOldNames, rNewName, pSect->GetCondition()));
+ }
+ }
+
+ for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
+ {
+ for (const SfxPoolItem* pItem : GetAttrPool().GetItemSurrogates(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() )
+ {
+ case SwFieldIds::Database:
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ 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);
+
+ static_cast<SwDBField*>(pField)->ClearInitialized();
+ static_cast<SwDBField*>(pField)->InitContent();
+
+ bExpand = true;
+ }
+#endif
+ break;
+
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::DatabaseName:
+ if (IsNameInArray(rOldNames,
+ lcl_DBDataToString(static_cast<SwDBNameInfField*>(pField)->GetRealDBData())))
+ {
+ 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;
+
+ 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 );
+ }
+ }
+ getIDocumentState().SetModified();
+#endif
+}
+
+namespace
+{
+
+OUString lcl_CutOffDBCommandType(const OUString& rName)
+{
+ return rName.replaceFirst(OUStringChar(DB_DELIM), ".").getToken(0, DB_DELIM);
+}
+
+}
+
+OUString SwDoc::ReplaceUsedDBs( const std::vector<OUString>& rUsedDBNames,
+ const OUString& rNewName, const OUString& rFormula )
+{
+ const CharClass& rCC = GetAppCharClass();
+ const OUString sNewName( lcl_CutOffDBCommandType(rNewName) );
+ OUString sFormula(rFormula);
+
+ for(const auto & rUsedDBName : rUsedDBNames)
+ {
+ const OUString sDBName( lcl_CutOffDBCommandType(rUsedDBName) );
+
+ if (sDBName!=sNewName)
+ {
+ sal_Int32 nPos = 0;
+ for (;;)
+ {
+ nPos = sFormula.indexOf(sDBName, nPos);
+ if (nPos<0)
+ {
+ break;
+ }
+
+ if( sFormula[nPos + sDBName.getLength()] == '.' &&
+ (!nPos || !rCC.isLetterNumeric( sFormula, nPos - 1 )))
+ {
+ sFormula = sFormula.replaceAt(nPos, sDBName.getLength(), sNewName);
+ //prevent re-searching - this is useless and provokes
+ //endless loops when names containing each other and numbers are exchanged
+ //e.g.: old ?12345.12345 new: i12345.12345
+ nPos += sNewName.getLength();
+ }
+ }
+ }
+ }
+ return sFormula;
+}
+
+bool SwDoc::IsNameInArray( const std::vector<OUString>& rArr, const OUString& rName )
+{
+#ifdef UNX
+ for( const auto &sName : rArr )
+ if( rName == sName )
+ return true;
+#else
+ const ::utl::TransliterationWrapper& rSCmp = GetAppCmpStrIgnore();
+ for( const auto &sName : rArr )
+ if( rSCmp.isEqual( rName, sName ))
+ return true;
+#endif
+ return false;
+}
+
+void SwDoc::ChangeAuthorityData( const SwAuthEntry* pNewData )
+{
+ const SwFieldTypes::size_type nSize = getIDocumentFieldsAccess().GetFieldTypes()->size();
+
+ for( SwFieldTypes::size_type i = INIT_FLDTYPES; i < nSize; ++i )
+ {
+ SwFieldType* pFieldType = (*getIDocumentFieldsAccess().GetFieldTypes())[i].get();
+ if( SwFieldIds::TableOfAuthorities == pFieldType->Which() )
+ {
+ SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(pFieldType);
+ pAuthType->ChangeEntryContent(pNewData);
+ break;
+ }
+ }
+
+}
+
+void SwDocUpdateField::InsDelFieldInFieldLst( bool bIns, const SwTextField& rField )
+{
+ const SwFieldIds nWhich = rField.GetFormatField().GetField()->GetTyp()->Which();
+ switch( nWhich )
+ {
+ case SwFieldIds::Database:
+ case SwFieldIds::SetExp:
+ case SwFieldIds::HiddenPara:
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::GetExp:
+ break; // these have to be added/removed!
+
+ default:
+ return;
+ }
+
+ SetFieldsDirty( true );
+ if (!m_pFieldSortList)
+ {
+ if( !bIns ) // if list is present and deleted
+ return; // don't do a thing
+ m_pFieldSortList.reset(new SetGetExpFields);
+ }
+
+ if( bIns ) // insert anew:
+ GetBodyNode( rField, nWhich );
+ else
+ {
+ // look up via the pTextField pointer. It is a sorted list, but it's sorted by node
+ // position. Until this is found, the search for the pointer is already done.
+ for (SetGetExpFields::size_type n = 0; n < m_pFieldSortList->size(); ++n)
+ {
+ if (&rField == (*m_pFieldSortList)[n]->GetPointer())
+ {
+ m_pFieldSortList->erase_at(n);
+ n--; // one field can occur multiple times
+ }
+ }
+ }
+}
+
+void SwDocUpdateField::MakeFieldList( SwDoc& rDoc, bool bAll, int eGetMode )
+{
+ if (!m_pFieldSortList || bAll
+ || ((eGetMode & m_nFieldListGetMode) != eGetMode)
+ || rDoc.GetNodes().Count() != m_nNodes)
+ {
+ MakeFieldList_( rDoc, eGetMode );
+ }
+}
+
+void SwDocUpdateField::MakeFieldList_( SwDoc& rDoc, int eGetMode )
+{
+ // new version: walk all fields of the attribute pool
+ m_pFieldSortList.reset(new SetGetExpFields);
+
+ // remembeer sections that were unhidden and need to be hidden again
+ std::vector<std::reference_wrapper<SwSection>> aUnhiddenSections;
+
+ // consider and unhide sections
+ // with hide condition, only in mode GETFLD_ALL (<eGetMode == GETFLD_ALL>)
+ // notes by OD:
+ // eGetMode == GETFLD_CALC in call from methods SwDoc::FieldsToCalc
+ // eGetMode == GETFLD_EXPAND in call from method SwDoc::FieldsToExpand
+ // eGetMode == GETFLD_ALL in call from method SwDoc::UpdateExpFields
+ // I figured out that hidden section only have to be shown,
+ // if fields have updated (call by SwDoc::UpdateExpFields) and thus
+ // the hide conditions of section have to be updated.
+ // For correct updating the hide condition of a section, its position
+ // have to be known in order to insert the hide condition as a new
+ // expression field into the sorted field list (<m_pFieldSortList>).
+ if ( eGetMode == GETFLD_ALL )
+ // Collect the sections first. Supply sections that are hidden by condition
+ // with frames so that the contained fields are sorted properly.
+ {
+ // In order for the frames to be created the right way, they have to be expanded
+ // from top to bottom
+ std::vector<SwNodeOffset> aTmpArr;
+ std::vector<SwNodeOffset>::size_type nArrStt = 0;
+ SwSectionFormats& rArr = rDoc.GetSections();
+ SwSectionNode* pSectNd = nullptr;
+ SwNodeOffset nSttContent = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+
+ for (SwSectionFormats::size_type n = rArr.size(); n; )
+ {
+ SwSection* pSect = rArr[ --n ]->GetSection();
+ if( !pSect || !pSect->IsHidden() || pSect->GetCondition().isEmpty() )
+ continue;
+ pSectNd = pSect->GetFormat()->GetSectionNode();
+ if( pSectNd )
+ {
+ SwNodeOffset nIdx = pSectNd->GetIndex();
+ aTmpArr.push_back( nIdx );
+ if( nIdx < nSttContent )
+ ++nArrStt;
+ }
+ }
+ std::sort(aTmpArr.begin(), aTmpArr.end());
+
+ // Display all first so that we have frames. The BodyAnchor is defined by that.
+ // First the ContentArea, then the special areas!
+ for (std::vector<sal_uLong>::size_type n = nArrStt; n < aTmpArr.size(); ++n)
+ {
+ pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode();
+ OSL_ENSURE( pSectNd, "Where is my SectionNode" );
+
+ auto& rSection = pSectNd->GetSection();
+ // unhide and remember the conditionally hidden sections
+ if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden())
+ {
+ aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again
+ rSection.SetCondHidden(false);
+ }
+ }
+ for (std::vector<sal_uLong>::size_type n = 0; n < nArrStt; ++n)
+ {
+ pSectNd = rDoc.GetNodes()[ aTmpArr[ n ] ]->GetSectionNode();
+ OSL_ENSURE( pSectNd, "Where is my SectionNode" );
+
+ auto& rSection = pSectNd->GetSection();
+ // unhide and remember the conditionally hidden sections
+ if (rSection.IsHidden() && !rSection.GetCondition().isEmpty() && rSection.IsCondHidden())
+ {
+ aUnhiddenSections.push_back(std::ref(rSection)); // remember to later hide again
+ rSection.SetCondHidden(false);
+ }
+ }
+
+ // add all to the list so that they are sorted
+ for (const auto &nId : aTmpArr)
+ {
+ SwSectionNode const& rSectionNode(*rDoc.GetNodes()[ nId ]->GetSectionNode());
+ GetBodyNodeGeneric(rSectionNode, rSectionNode);
+ }
+
+ // bookmarks with hide conditions, handle similar to sections
+ auto const& rIDMA(*rDoc.getIDocumentMarkAccess());
+ for (auto it = rIDMA.getBookmarksBegin(); it != rIDMA.getBookmarksEnd(); ++it)
+ {
+ auto const pBookmark(dynamic_cast<::sw::mark::IBookmark const*>(*it));
+ assert(pBookmark);
+ if (!pBookmark->GetHideCondition().isEmpty())
+ {
+ GetBodyNodeGeneric((*it)->GetMarkStart().nNode.GetNode(), *pBookmark);
+ }
+ }
+ }
+
+ static const OUStringLiteral sTrue(u"TRUE");
+ static const OUStringLiteral sFalse(u"FALSE");
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ bool bIsDBManager = nullptr != rDoc.GetDBManager();
+#endif
+
+ for (sal_uInt16 const nWhichHint : { RES_TXTATR_FIELD, RES_TXTATR_INPUTFIELD })
+ {
+ for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(nWhichHint))
+ {
+ 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)
+ {
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::GetExp:
+ if (GETFLD_ALL == eGetMode)
+ sFormula = sTrue;
+ break;
+
+ case SwFieldIds::Database:
+ if (GETFLD_EXPAND & eGetMode)
+ sFormula = sTrue;
+ break;
+
+ case SwFieldIds::SetExp:
+ if ((eGetMode != GETFLD_EXPAND) ||
+ (nsSwGetSetExpType::GSE_STRING & pField->GetSubType()))
+ {
+ 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;
+
+ sFormula.clear();
+ // trigger formatting
+ const_cast<SwFormatField*>(pFormatField)->UpdateTextNode( 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)->UpdateTextNode(nullptr, nullptr);
+ }
+ break;
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ 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()))
+ )
+ {
+ sFormula = pField->GetPar1();
+ }
+ }
+ 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;
+ }
+
+ if (!sFormula.isEmpty())
+ {
+ GetBodyNode( *pTextField, nWhich );
+ }
+ }
+ }
+ m_nFieldListGetMode = eGetMode;
+ m_nNodes = rDoc.GetNodes().Count();
+
+ // return the conditional hidden value back to the previous value
+ for (auto& rSectionWrapper : aUnhiddenSections)
+ {
+ auto& rSection = rSectionWrapper.get();
+ rSection.SetCondHidden(true);
+ }
+}
+
+void SwDocUpdateField::GetBodyNode( const SwTextField& rTField, SwFieldIds nFieldWhich )
+{
+ const SwTextNode& rTextNd = rTField.GetTextNode();
+ const SwDoc& rDoc = rTextNd.GetDoc();
+
+ // always the first! (in tab headline, header-/footer)
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ // need pos to get the frame on the correct page
+ SwPosition const pos(const_cast<SwTextNode&>(rTextNd), rTField.GetStart());
+ const SwFrame* pFrame = rTextNd.getLayoutFrame(
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), &pos, &tmp);
+
+ std::unique_ptr<SetGetExpField> pNew;
+ bool bIsInBody = false;
+
+ if( !pFrame || pFrame->IsInDocBody() )
+ {
+ // create index to determine the TextNode
+ SwNodeIndex aIdx( rTextNd );
+ bIsInBody = rDoc.GetNodes().GetEndOfExtras().GetIndex() < aIdx.GetIndex();
+
+ // We don't want to update fields in redlines, or those
+ // in frames whose anchor is in redline. However, we do want to update
+ // fields in hidden sections. So: In order to be updated, a field 1)
+ // must have a frame, or 2) it must be in the document body.
+ if (pFrame == nullptr && bIsInBody)
+ { // try harder to get a frame for the page number
+ pFrame = ::sw::FindNeighbourFrameForNode(rTextNd);
+ // possibly there is no layout at all, happens in mail merge
+ }
+ if( (pFrame != nullptr) || bIsInBody )
+ {
+ pNew.reset(new SetGetExpField(aIdx, &rTField, nullptr,
+ pFrame ? pFrame->GetPhyPageNum() : 0));
+ }
+ }
+ else
+ {
+ // create index to determine the TextNode
+ SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
+ bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the Field");
+ pNew.reset(new SetGetExpField(aPos.nNode, &rTField, &aPos.nContent,
+ pFrame->GetPhyPageNum()));
+ }
+
+ // always set the BodyTextFlag in GetExp or DB fields
+ if( SwFieldIds::GetExp == nFieldWhich )
+ {
+ SwGetExpField* pGetField = const_cast<SwGetExpField*>(static_cast<const SwGetExpField*>(rTField.GetFormatField().GetField()));
+ pGetField->ChgBodyTextFlag( bIsInBody );
+ }
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ else if( SwFieldIds::Database == nFieldWhich )
+ {
+ SwDBField* pDBField = const_cast<SwDBField*>(static_cast<const SwDBField*>(rTField.GetFormatField().GetField()));
+ pDBField->ChgBodyTextFlag( bIsInBody );
+ }
+#endif
+ if( pNew != nullptr )
+ m_pFieldSortList->insert( std::move(pNew) );
+}
+
+template<typename T>
+void SwDocUpdateField::GetBodyNodeGeneric(SwNode const& rNode, T const& rCond)
+{
+ const SwDoc& rDoc = rNode.GetDoc();
+ std::unique_ptr<SetGetExpField> pNew;
+
+ if (rNode.GetIndex() < rDoc.GetNodes().GetEndOfExtras().GetIndex())
+ {
+ do { // middle check loop
+
+ // we need to get the anchor first
+ // create index to determine the TextNode
+ SwPosition aPos(rNode);
+ SwContentNode const*const pCNd = rNode.IsSectionNode()
+ ? rDoc.GetNodes().GoNext(&aPos.nNode) // to the next ContentNode
+ : rNode.GetContentNode();
+
+ if( !pCNd || !pCNd->IsTextNode() )
+ break;
+
+ // always the first! (in tab headline, header-/footer)
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame* pFrame = pCNd->getLayoutFrame(
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ if( !pFrame )
+ break;
+
+ bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the Field");
+ pNew.reset(new SetGetExpField(rCond, &aPos, pFrame->GetPhyPageNum()));
+
+ } while( false );
+ }
+
+ if( !pNew )
+ {
+ // try harder to get a frame for the page number
+ SwFrame const*const pFrame = ::sw::FindNeighbourFrameForNode(rNode);
+ pNew.reset(new SetGetExpField(rCond, nullptr, pFrame ? pFrame->GetPhyPageNum() : 0));
+ }
+
+ m_pFieldSortList->insert( std::move(pNew) );
+}
+
+void SwDocUpdateField::InsertFieldType( const SwFieldType& rType )
+{
+ OUString sFieldName;
+ switch( rType.Which() )
+ {
+ case SwFieldIds::User :
+ sFieldName = static_cast<const SwUserFieldType&>(rType).GetName();
+ break;
+ case SwFieldIds::SetExp:
+ sFieldName = static_cast<const SwSetExpFieldType&>(rType).GetName();
+ break;
+ default:
+ OSL_ENSURE( false, "No valid field type" );
+ }
+
+ if( sFieldName.isEmpty() )
+ return;
+
+ SetFieldsDirty( true );
+ // look up and remove from the hash table
+ sFieldName = GetAppCharClass().lowercase( sFieldName );
+ sal_uInt16 n;
+
+ SwCalcFieldType* pFnd = GetFieldTypeTable().Find( sFieldName, &n );
+
+ if( !pFnd )
+ {
+ SwCalcFieldType* pNew = new SwCalcFieldType( sFieldName, &rType );
+ pNew->pNext.reset( m_FieldTypeTable[n].release() );
+ m_FieldTypeTable[n].reset(pNew);
+ }
+}
+
+void SwDocUpdateField::RemoveFieldType( const SwFieldType& rType )
+{
+ OUString sFieldName;
+ switch( rType.Which() )
+ {
+ case SwFieldIds::User :
+ sFieldName = static_cast<const SwUserFieldType&>(rType).GetName();
+ break;
+ case SwFieldIds::SetExp:
+ sFieldName = static_cast<const SwSetExpFieldType&>(rType).GetName();
+ break;
+ default: break;
+ }
+
+ if( sFieldName.isEmpty() )
+ return;
+
+ SetFieldsDirty( true );
+ // look up and remove from the hash table
+ sFieldName = GetAppCharClass().lowercase( sFieldName );
+ sal_uInt16 n;
+
+ SwCalcFieldType* pFnd = GetFieldTypeTable().Find( sFieldName, &n );
+ if( !pFnd )
+ return;
+
+ if (m_FieldTypeTable[n].get() == pFnd)
+ {
+ m_FieldTypeTable[n].reset(static_cast<SwCalcFieldType*>(pFnd->pNext.release()));
+ }
+ else
+ {
+ SwHash* pPrev = m_FieldTypeTable[n].get();
+ while( pPrev->pNext.get() != pFnd )
+ pPrev = pPrev->pNext.get();
+ pPrev->pNext = std::move(pFnd->pNext);
+ // no need to explicitly delete here, the embedded linked list uses unique_ptr
+ }
+}
+
+SwDocUpdateField::SwDocUpdateField(SwDoc& rDoc)
+ : m_FieldTypeTable(TBLSZ)
+ , m_nNodes(0)
+ , m_nFieldListGetMode(0)
+ , m_rDoc(rDoc)
+ , m_bInUpdateFields(false)
+ , m_bFieldsDirty(false)
+{
+}
+
+SwDocUpdateField::~SwDocUpdateField()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docfly.cxx b/sw/source/core/doc/docfly.cxx
new file mode 100644
index 000000000..41756ff0c
--- /dev/null
+++ b/sw/source/core/doc/docfly.cxx
@@ -0,0 +1,1161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmark.hxx>
+#include <osl/diagnose.h>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <dcontact.hxx>
+#include <ndgrf.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <ndindex.hxx>
+#include <drawdoc.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtflcnt.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <flyfrm.hxx>
+#include <textboxhelper.hxx>
+#include <txatbase.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <crstate.hxx>
+#include <UndoCore.hxx>
+#include <UndoAttribute.hxx>
+#include <fmtcnct.hxx>
+#include <dflyobj.hxx>
+#include <undoflystrattr.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+#include <memory>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xflhtit.hxx>
+
+using namespace ::com::sun::star;
+
+size_t SwDoc::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const
+{
+ const SwFrameFormats& rFormats = *GetSpzFrameFormats();
+ const size_t nSize = rFormats.size();
+ size_t nCount = 0;
+ const SwNodeIndex* pIdx;
+
+ for ( size_t i = 0; i < nSize; ++i)
+ {
+ const SwFrameFormat* pFlyFormat = rFormats[ i ];
+
+ if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ continue;
+
+ if( RES_FLYFRMFMT != pFlyFormat->Which() )
+ continue;
+ pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNodes().IsDocNodes() )
+ {
+ const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
+
+ switch( eType )
+ {
+ case FLYCNTTYPE_FRM:
+ if(!pNd->IsNoTextNode())
+ nCount++;
+ break;
+
+ case FLYCNTTYPE_GRF:
+ if( pNd->IsGrfNode() )
+ nCount++;
+ break;
+
+ case FLYCNTTYPE_OLE:
+ if(pNd->IsOLENode())
+ nCount++;
+ break;
+
+ default:
+ nCount++;
+ }
+ }
+ }
+ return nCount;
+}
+
+/// @attention If you change this, also update SwXFrameEnumeration in unocoll.
+SwFrameFormat* SwDoc::GetFlyNum( size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes )
+{
+ SwFrameFormats& rFormats = *GetSpzFrameFormats();
+ SwFrameFormat* pRetFormat = nullptr;
+ const size_t nSize = rFormats.size();
+ const SwNodeIndex* pIdx;
+ size_t nCount = 0;
+
+ for( size_t i = 0; !pRetFormat && i < nSize; ++i )
+ {
+ SwFrameFormat* pFlyFormat = rFormats[ i ];
+
+ if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ continue;
+
+ if( RES_FLYFRMFMT != pFlyFormat->Which() )
+ continue;
+ pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNodes().IsDocNodes() )
+ {
+ const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
+ switch( eType )
+ {
+ case FLYCNTTYPE_FRM:
+ if( !pNd->IsNoTextNode() && nIdx == nCount++)
+ pRetFormat = pFlyFormat;
+ break;
+ case FLYCNTTYPE_GRF:
+ if(pNd->IsGrfNode() && nIdx == nCount++ )
+ pRetFormat = pFlyFormat;
+ break;
+ case FLYCNTTYPE_OLE:
+ if(pNd->IsOLENode() && nIdx == nCount++)
+ pRetFormat = pFlyFormat;
+ break;
+ default:
+ if(nIdx == nCount++)
+ pRetFormat = pFlyFormat;
+ }
+ }
+ }
+ return pRetFormat;
+}
+
+std::vector<SwFrameFormat const*> SwDoc::GetFlyFrameFormats(
+ FlyCntType const eType, bool const bIgnoreTextBoxes)
+{
+ SwFrameFormats& rFormats = *GetSpzFrameFormats();
+ const size_t nSize = rFormats.size();
+
+ std::vector<SwFrameFormat const*> ret;
+ ret.reserve(nSize);
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SwFrameFormat const*const pFlyFormat = rFormats[ i ];
+
+ if (bIgnoreTextBoxes && SwTextBoxHelper::isTextBox(pFlyFormat, RES_FLYFRMFMT))
+ {
+ continue;
+ }
+
+ if (RES_FLYFRMFMT != pFlyFormat->Which())
+ {
+ continue;
+ }
+
+ SwNodeIndex const*const pIdx(pFlyFormat->GetContent().GetContentIdx());
+ if (pIdx && pIdx->GetNodes().IsDocNodes())
+ {
+ SwNode const*const pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
+ switch (eType)
+ {
+ case FLYCNTTYPE_FRM:
+ if (!pNd->IsNoTextNode())
+ ret.push_back(pFlyFormat);
+ break;
+ case FLYCNTTYPE_GRF:
+ if (pNd->IsGrfNode())
+ ret.push_back(pFlyFormat);
+ break;
+ case FLYCNTTYPE_OLE:
+ if (pNd->IsOLENode())
+ ret.push_back(pFlyFormat);
+ break;
+ default:
+ ret.push_back(pFlyFormat);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static Point lcl_FindAnchorLayPos( SwDoc& rDoc, const SwFormatAnchor& rAnch,
+ const SwFrameFormat* pFlyFormat )
+{
+ Point aRet;
+ if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ switch( rAnch.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ if( pFlyFormat && rAnch.GetContentAnchor() )
+ {
+ const SwFrame* pOld = static_cast<const SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aRet );
+ if( pOld )
+ aRet = pOld->getFrameArea().Pos();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
+ if( rAnch.GetContentAnchor() )
+ {
+ const SwPosition *pPos = rAnch.GetContentAnchor();
+ const SwContentNode* pNd = pPos->nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(aRet, false);
+ const SwFrame* pOld = pNd ? pNd->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ if( pOld )
+ aRet = pOld->getFrameArea().Pos();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ if( rAnch.GetContentAnchor() )
+ {
+ const SwFlyFrameFormat* pFormat = static_cast<SwFlyFrameFormat*>(rAnch.GetContentAnchor()->
+ nNode.GetNode().GetFlyFormat());
+ const SwFrame* pOld = pFormat ? pFormat->GetFrame( &aRet ) : nullptr;
+ if( pOld )
+ aRet = pOld->getFrameArea().Pos();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ sal_uInt16 nPgNum = rAnch.GetPageNum();
+ const SwPageFrame *pPage = static_cast<SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
+ for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i,
+ pPage =static_cast<const SwPageFrame*>(pPage->GetNext()) )
+ if( i == nPgNum )
+ {
+ aRet = pPage->getFrameArea().Pos();
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return aRet;
+}
+
+#define MAKEFRMS 0
+#define IGNOREANCHOR 1
+#define DONTMAKEFRMS 2
+
+sal_Int8 SwDoc::SetFlyFrameAnchor( SwFrameFormat& rFormat, SfxItemSet& rSet, bool bNewFrames )
+{
+ // Changing anchors is almost always allowed.
+ // Exception: Paragraph and character bound frames must not become
+ // page bound, if they are located in the header or footer.
+ const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
+ const RndStdIds nOld = rOldAnch.GetAnchorId();
+
+ SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
+ RndStdIds nNew = aNewAnch.GetAnchorId();
+
+ // Is the new anchor valid?
+ if( !aNewAnch.GetContentAnchor() && (RndStdIds::FLY_AT_FLY == nNew ||
+ (RndStdIds::FLY_AT_PARA == nNew) || (RndStdIds::FLY_AS_CHAR == nNew) ||
+ (RndStdIds::FLY_AT_CHAR == nNew) ))
+ {
+ return IGNOREANCHOR;
+ }
+
+ if( nOld == nNew )
+ return DONTMAKEFRMS;
+
+ Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFormat ));
+ Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, nullptr ));
+
+ // Destroy the old Frames.
+ // The Views are hidden implicitly, so hiding them another time would be
+ // kind of a show!
+ rFormat.DelFrames();
+
+ if ( RndStdIds::FLY_AS_CHAR == nOld )
+ {
+ // We need to handle InContents in a special way:
+ // The TextAttribut needs to be destroyed which, unfortunately, also
+ // destroys the format. To avoid that, we disconnect the format from
+ // the attribute.
+ const SwPosition *pPos = rOldAnch.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
+ OSL_ENSURE( pHint && pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint && pHint->GetFlyCnt().GetFrameFormat() == &rFormat,
+ "Wrong TextFlyCnt-Hint." );
+ if (pHint)
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // They are disconnected. We now have to destroy the attribute.
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+
+ // We can finally set the attribute. It needs to be the first one!
+ // Undo depends on it!
+ rFormat.SetFormatAttr( aNewAnch );
+
+ // Correct the position
+ switch( nNew )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ // If no position attributes are received, we have to make sure
+ // that no forbidden automatic alignment is left.
+ {
+ const SwPosition *pPos = aNewAnch.GetContentAnchor();
+ SwTextNode *pNd = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Cursor does not point to TextNode." );
+
+ SwFormatFlyCnt aFormat( static_cast<SwFlyFrameFormat*>(&rFormat) );
+ pNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 );
+ }
+
+ if( SfxItemState::SET != rSet.GetItemState( RES_VERT_ORIENT, false ))
+ {
+ SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
+ bool bSet = true;
+ switch( aOldV.GetVertOrient() )
+ {
+ case text::VertOrientation::LINE_TOP: aOldV.SetVertOrient( text::VertOrientation::TOP ); break;
+ case text::VertOrientation::LINE_CENTER: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
+ case text::VertOrientation::LINE_BOTTOM: aOldV.SetVertOrient( text::VertOrientation::BOTTOM); break;
+ case text::VertOrientation::NONE: aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
+ default:
+ bSet = false;
+ }
+ if( bSet )
+ rSet.Put( aOldV );
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ // If no position attributes are coming in, we correct the position in a way
+ // such that the fly's document coordinates are preserved.
+ // If only the alignment changes in the position attributes (text::RelOrientation::FRAME
+ // vs. text::RelOrientation::PRTAREA), we also correct the position.
+ const SwFormatHoriOrient* pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false );
+
+ SwFormatHoriOrient aOldH( rFormat.GetHoriOrient() );
+ bool bPutOldH(false);
+
+ if( text::HoriOrientation::NONE == aOldH.GetHoriOrient() && ( !pHoriOrientItem ||
+ aOldH.GetPos() == pHoriOrientItem->GetPos() ))
+ {
+ SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos();
+ nPos += aOldAnchorPos.getX() - aNewAnchorPos.getX();
+
+ if( pHoriOrientItem )
+ {
+ aOldH.SetHoriOrient( pHoriOrientItem->GetHoriOrient() );
+ aOldH.SetRelationOrient( pHoriOrientItem->GetRelationOrient() );
+ }
+ aOldH.SetPos( nPos );
+ bPutOldH = true;
+ }
+ if (nNew == RndStdIds::FLY_AT_PAGE)
+ {
+ sal_Int16 nRelOrient(pHoriOrientItem
+ ? pHoriOrientItem->GetRelationOrient()
+ : aOldH.GetRelationOrient());
+ if (sw::GetAtPageRelOrientation(nRelOrient, false))
+ {
+ SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
+ aOldH.SetRelationOrient(nRelOrient);
+ bPutOldH = true;
+ }
+ }
+ if (bPutOldH)
+ {
+ rSet.Put( aOldH );
+ }
+
+ const SwFormatVertOrient* pVertOrientItem = rSet.GetItemIfSet( RES_VERT_ORIENT, false );
+ SwFormatVertOrient aOldV( rFormat.GetVertOrient() );
+
+ // #i28922# - correction: compare <aOldV.GetVertOrient() with
+ // <text::VertOrientation::NONE>
+ if( text::VertOrientation::NONE == aOldV.GetVertOrient() && (!pVertOrientItem ||
+ aOldV.GetPos() == pVertOrientItem->GetPos() ) )
+ {
+ SwTwips nPos = (RndStdIds::FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos();
+ nPos += aOldAnchorPos.getY() - aNewAnchorPos.getY();
+ if( pVertOrientItem )
+ {
+ SwFormatVertOrient& rV = const_cast<SwFormatVertOrient&>(*pVertOrientItem);
+ aOldV.SetVertOrient( rV.GetVertOrient() );
+ aOldV.SetRelationOrient( rV.GetRelationOrient() );
+ }
+ aOldV.SetPos( nPos );
+ rSet.Put( aOldV );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( bNewFrames )
+ rFormat.MakeFrames();
+
+ return MAKEFRMS;
+}
+
+static bool
+lcl_SetFlyFrameAttr(SwDoc & rDoc,
+ sal_Int8 (SwDoc::*pSetFlyFrameAnchor)(SwFrameFormat &, SfxItemSet &, bool),
+ SwFrameFormat & rFlyFormat, SfxItemSet & rSet)
+{
+ // #i32968# Inserting columns in the frame causes MakeFrameFormat to put two
+ // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Is the anchor attribute included?
+ // If so, we pass it to a special method, which returns true
+ // if the Fly needs to be created anew, because we e.g change the FlyType.
+ sal_Int8 const nMakeFrames =
+ (SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
+ ? (rDoc.*pSetFlyFrameAnchor)( rFlyFormat, rSet, false )
+ : DONTMAKEFRMS;
+
+ const SfxPoolItem* pItem;
+ SfxItemIter aIter( rSet );
+ SfxItemSet aTmpSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
+ const SfxPoolItem* pItemIter = aIter.GetCurItem();
+ do {
+ switch(pItemIter->Which())
+ {
+ case RES_FILL_ORDER:
+ case RES_BREAK:
+ case RES_PAGEDESC:
+ case RES_CNTNT:
+ case RES_FOOTER:
+ OSL_FAIL( "Unknown Fly attribute." );
+ [[fallthrough]];
+ case RES_CHAIN:
+ rSet.ClearItem(pItemIter->Which());
+ break;
+ case RES_ANCHOR:
+ if( DONTMAKEFRMS != nMakeFrames )
+ break;
+ [[fallthrough]];
+ default:
+ if( !IsInvalidItem(pItemIter) && ( SfxItemState::SET !=
+ rFlyFormat.GetAttrSet().GetItemState(pItemIter->Which(), true, &pItem ) ||
+ *pItem != *pItemIter))
+ aTmpSet.Put(*pItemIter);
+ break;
+ }
+
+ pItemIter = aIter.NextItem();
+
+ } while (pItemIter && (0 != pItemIter->Which()));
+
+ if( aTmpSet.Count() )
+ rFlyFormat.SetFormatAttr( aTmpSet );
+
+ if( MAKEFRMS == nMakeFrames )
+ rFlyFormat.MakeFrames();
+
+ return aTmpSet.Count() || MAKEFRMS == nMakeFrames;
+}
+
+void SwDoc::CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet& rSet)
+{
+ SwDrawModel* pDrawModel = getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ SfxItemIter aIter(rSet);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (IsInvalidItem(pItem))
+ continue;
+ std::unique_ptr<SfxPoolItem> pResult;
+
+ switch(pItem->Which())
+ {
+ case XATTR_FILLBITMAP:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLBITMAP).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_LINEDASH:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_LINEDASH).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_LINESTART:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_LINESTART).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_LINEEND:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_LINEEND).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_FILLGRADIENT:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLGRADIENT).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLFLOATTRANSPARENCE).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ case XATTR_FILLHATCH:
+ {
+ pResult = pItem->StaticWhichCast(XATTR_FILLHATCH).checkForUniqueItem(pDrawModel);
+ break;
+ }
+ }
+
+ if(pResult)
+ {
+ rSet.Put(std::move(pResult));
+ }
+ }
+}
+
+bool SwDoc::SetFlyFrameAttr( SwFrameFormat& rFlyFormat, SfxItemSet& rSet )
+{
+ if( !rSet.Count() )
+ return false;
+
+ std::unique_ptr<SwUndoFormatAttrHelper> pSaveUndo;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
+ pSaveUndo.reset( new SwUndoFormatAttrHelper( rFlyFormat ) );
+ }
+
+ bool const bRet = lcl_SetFlyFrameAttr(*this, &SwDoc::SetFlyFrameAnchor, rFlyFormat, rSet);
+
+ if (pSaveUndo && pSaveUndo->GetUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo( pSaveUndo->ReleaseUndo() );
+ }
+
+ getIDocumentState().SetModified();
+
+ //SwTextBoxHelper::syncFlyFrameAttr(rFlyFormat, rSet);
+
+ return bRet;
+}
+
+// #i73249#
+void SwDoc::SetFlyFrameTitle( SwFlyFrameFormat& rFlyFrameFormat,
+ const OUString& sNewTitle )
+{
+ if ( rFlyFrameFormat.GetObjTitle() == sNewTitle )
+ {
+ return;
+ }
+
+ ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
+ SwUndoId::FLYFRMFMT_TITLE,
+ rFlyFrameFormat.GetObjTitle(),
+ sNewTitle ) );
+ }
+
+ rFlyFrameFormat.SetObjTitle( sNewTitle, true );
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetFlyFrameDescription( SwFlyFrameFormat& rFlyFrameFormat,
+ const OUString& sNewDescription )
+{
+ if ( rFlyFrameFormat.GetObjDescription() == sNewDescription )
+ {
+ return;
+ }
+
+ ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFlyStrAttr>( rFlyFrameFormat,
+ SwUndoId::FLYFRMFMT_DESCRIPTION,
+ rFlyFrameFormat.GetObjDescription(),
+ sNewDescription ) );
+ }
+
+ rFlyFrameFormat.SetObjDescription( sNewDescription, true );
+
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::SetFrameFormatToFly( SwFrameFormat& rFormat, SwFrameFormat& rNewFormat,
+ SfxItemSet* pSet, bool bKeepOrient )
+{
+ bool bChgAnchor = false, bFrameSz = false;
+
+ const SwFormatFrameSize aFrameSz( rFormat.GetFrameSize() );
+
+ SwUndoSetFlyFormat* pUndo = nullptr;
+ bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
+ if (bUndo)
+ {
+ pUndo = new SwUndoSetFlyFormat( rFormat, rNewFormat );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ // #i32968# Inserting columns in the section causes MakeFrameFormat to put
+ // 2 objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Set the column first, or we'll have trouble with
+ //Set/Reset/Synch. and so on
+ if( SfxItemState::SET != rNewFormat.GetAttrSet().GetItemState( RES_COL ))
+ rFormat.ResetFormatAttr( RES_COL );
+
+ if( rFormat.DerivedFrom() != &rNewFormat )
+ {
+ rFormat.SetDerivedFrom( &rNewFormat );
+
+ // 1. If not automatic = ignore; else = dispose
+ // 2. Dispose of it!
+ if( SfxItemState::SET == rNewFormat.GetAttrSet().GetItemState( RES_FRM_SIZE, false ))
+ {
+ rFormat.ResetFormatAttr( RES_FRM_SIZE );
+ bFrameSz = true;
+ }
+
+ const SfxItemSet* pAsk = pSet;
+ if( !pAsk ) pAsk = &rNewFormat.GetAttrSet();
+ const SwFormatAnchor* pFormatAnchor = pAsk->GetItemIfSet( RES_ANCHOR, false );
+ if( pFormatAnchor
+ && pFormatAnchor->GetAnchorId() !=
+ rFormat.GetAnchor().GetAnchorId() )
+ {
+ if( pSet )
+ bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, *pSet, false );
+ else
+ {
+ // Needs to have the FlyFormat range, because we set attributes in it,
+ // in SetFlyFrameAnchor.
+ SfxItemSet aFlySet( *rNewFormat.GetAttrSet().GetPool(),
+ rNewFormat.GetAttrSet().GetRanges() );
+ aFlySet.Put( *pFormatAnchor );
+ bChgAnchor = MAKEFRMS == SetFlyFrameAnchor( rFormat, aFlySet, false);
+ }
+ }
+ }
+
+ // Only reset vertical and horizontal orientation, if we have automatic alignment
+ // set in the template. Otherwise use the old value.
+ // If we update the frame template the Fly should NOT lose its orientation (which
+ // is not being updated!).
+ // text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now
+ if (!bKeepOrient)
+ {
+ rFormat.ResetFormatAttr(RES_VERT_ORIENT);
+ rFormat.ResetFormatAttr(RES_HORI_ORIENT);
+ }
+
+ rFormat.ResetFormatAttr( RES_PRINT, RES_SURROUND );
+ rFormat.ResetFormatAttr( RES_LR_SPACE, RES_UL_SPACE );
+ rFormat.ResetFormatAttr( RES_BACKGROUND, RES_COL );
+ rFormat.ResetFormatAttr( RES_EDIT_IN_READONLY );
+
+ if( !bFrameSz )
+ rFormat.SetFormatAttr( aFrameSz );
+
+ if( bChgAnchor )
+ rFormat.MakeFrames();
+
+ if( pUndo )
+ pUndo->EndListeningAll();
+
+ getIDocumentState().SetModified();
+
+ return bChgAnchor;
+}
+
+void SwDoc::GetGrfNms( const SwFlyFrameFormat& rFormat, OUString* pGrfName,
+ OUString* pFltName )
+{
+ SwNodeIndex aIdx( *rFormat.GetContent().GetContentIdx(), 1 );
+ const SwGrfNode* pGrfNd = aIdx.GetNode().GetGrfNode();
+ if( pGrfNd && pGrfNd->IsLinkedFile() )
+ pGrfNd->GetFileFilterNms( pGrfName, pFltName );
+}
+
+bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
+ RndStdIds _eAnchorType,
+ const bool _bSameOnly,
+ const bool _bPosCorr )
+{
+ OSL_ENSURE( getIDocumentLayoutAccess().GetCurrentLayout(), "No layout!" );
+
+ if ( !_rMrkList.GetMarkCount() ||
+ _rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() )
+ {
+ return false;
+ }
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSATTR, nullptr );
+
+ bool bUnmark = false;
+ for ( size_t i = 0; i < _rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ {
+ SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ // consider, that drawing object has
+ // no user call. E.g.: a 'virtual' drawing object is disconnected by
+ // the anchor type change of the 'master' drawing object.
+ // Continue with next selected object and assert, if this isn't excepted.
+ if ( !pContact )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj);
+ bool bNoUserCallExcepted = pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected();
+ OSL_ENSURE( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" );
+#endif
+ continue;
+ }
+
+ // #i26791#
+ const SwFrame* pOldAnchorFrame = pContact->GetAnchorFrame( pObj );
+ const SwFrame* pNewAnchorFrame = pOldAnchorFrame;
+
+ // #i54336#
+ // Instead of only keeping the index position for an as-character
+ // anchored object the complete <SwPosition> is kept, because the
+ // anchor index position could be moved, if the object again is
+ // anchored as character.
+ std::unique_ptr<const SwPosition> xOldAsCharAnchorPos;
+ const RndStdIds eOldAnchorType = pContact->GetAnchorId();
+ if ( !_bSameOnly && eOldAnchorType == RndStdIds::FLY_AS_CHAR )
+ {
+ xOldAsCharAnchorPos.reset(new SwPosition(pContact->GetContentAnchor()));
+ }
+
+ if ( _bSameOnly )
+ _eAnchorType = eOldAnchorType;
+
+ SwFormatAnchor aNewAnch( _eAnchorType );
+ SwAnchoredObject *pAnchoredObj = pContact->GetAnchoredObj(pObj);
+ tools::Rectangle aObjRect(pAnchoredObj->GetObjRect().SVRect());
+ const Point aPt( aObjRect.TopLeft() );
+
+ switch ( _eAnchorType )
+ {
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ const Point aNewPoint = ( pOldAnchorFrame->IsVertical() ||
+ pOldAnchorFrame->IsRightToLeft() )
+ ? aObjRect.TopRight()
+ : aPt;
+
+ // allow drawing objects in header/footer
+ pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aNewPoint );
+ if ( pNewAnchorFrame->IsTextFrame() && static_cast<const SwTextFrame*>(pNewAnchorFrame)->IsFollow() )
+ {
+ pNewAnchorFrame = static_cast<const SwTextFrame*>(pNewAnchorFrame)->FindMaster();
+ }
+ if ( pNewAnchorFrame->IsProtected() )
+ {
+ pNewAnchorFrame = nullptr;
+ }
+ else
+ {
+ SwPosition aPos( pNewAnchorFrame->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pNewAnchorFrame)->GetTextNodeForParaProps()
+ : *static_cast<SwNoTextFrame const*>(pNewAnchorFrame)->GetNode() );
+
+ aNewAnch.SetType( _eAnchorType );
+ aNewAnch.SetAnchor( &aPos );
+ }
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ {
+ // Search the closest SwFlyFrame starting from the upper left corner.
+ SwFrame *pTextFrame;
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ SwPosition aPos( GetNodes() );
+ Point aPoint( aPt );
+ aPoint.setX(aPoint.getX() - 1);
+ getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+ // consider that drawing objects can be in
+ // header/footer. Thus, <GetFrame()> by left-top-corner
+ std::pair<Point, bool> const tmp(aPt, false);
+ pTextFrame = aPos.nNode.GetNode().
+ GetContentNode()->getLayoutFrame(
+ getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ }
+ const SwFrame *pTmp = ::FindAnchor( pTextFrame, aPt );
+ pNewAnchorFrame = pTmp->FindFlyFrame();
+ if( pNewAnchorFrame && !pNewAnchorFrame->IsProtected() )
+ {
+ const SwFrameFormat *pTmpFormat = static_cast<const SwFlyFrame*>(pNewAnchorFrame)->GetFormat();
+ const SwFormatContent& rContent = pTmpFormat->GetContent();
+ SwPosition aPos( *rContent.GetContentIdx() );
+ aNewAnch.SetAnchor( &aPos );
+ break;
+ }
+
+ aNewAnch.SetType( RndStdIds::FLY_AT_PAGE );
+ [[fallthrough]];
+ }
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ pNewAnchorFrame = getIDocumentLayoutAccess().GetCurrentLayout()->Lower();
+ while ( pNewAnchorFrame && !pNewAnchorFrame->getFrameArea().Contains( aPt ) )
+ pNewAnchorFrame = pNewAnchorFrame->GetNext();
+ if ( !pNewAnchorFrame )
+ continue;
+
+ aNewAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnchorFrame)->GetPhyPageNum());
+ }
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ if( _bSameOnly ) // Change of position/size
+ {
+ if( !pOldAnchorFrame )
+ {
+ pContact->ConnectToLayout();
+ pOldAnchorFrame = pContact->GetAnchorFrame();
+ }
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pOldAnchorFrame))->Prepare();
+ }
+ else // Change of anchors
+ {
+ // allow drawing objects in header/footer
+ pNewAnchorFrame = ::FindAnchor( pOldAnchorFrame, aPt );
+ if( pNewAnchorFrame->IsProtected() )
+ {
+ pNewAnchorFrame = nullptr;
+ break;
+ }
+
+ bUnmark = ( 0 != i );
+ Point aPoint( aPt );
+ aPoint.setX(aPoint.getX() - 1); // Do not load in the DrawObj!
+ aNewAnch.SetType( RndStdIds::FLY_AS_CHAR );
+ assert(pNewAnchorFrame->IsTextFrame()); // because AS_CHAR
+ SwTextFrame const*const pFrame(
+ static_cast<SwTextFrame const*>(pNewAnchorFrame));
+ SwPosition aPos( *pFrame->GetTextNodeForParaProps() );
+ if ( pNewAnchorFrame->getFrameArea().Contains( aPoint ) )
+ {
+ // We need to find a TextNode, because only there we can anchor a
+ // content-bound DrawObject.
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+ }
+ else
+ {
+ if ( pNewAnchorFrame->getFrameArea().Bottom() < aPt.Y() )
+ {
+ aPos = pFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ aPos = pFrame->MapViewToModelPos(
+ TextFrameIndex(pFrame->GetText().getLength()));
+ }
+ }
+ aNewAnch.SetAnchor( &aPos );
+ SetAttr( aNewAnch, *pContact->GetFormat() );
+ // #i26791# - adjust vertical positioning to 'center to
+ // baseline'
+ SetAttr( SwFormatVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ), *pContact->GetFormat() );
+ SwTextNode *pNd = aPos.nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Cursor not positioned at TextNode." );
+
+ SwFormatFlyCnt aFormat( pContact->GetFormat() );
+ pNd->InsertItem( aFormat, aPos.nContent.GetIndex(), 0 );
+
+ // Has a textbox attached to the format? Sync it as well!
+ if (pContact->GetFormat() && pContact->GetFormat()->GetOtherTextBoxFormats())
+ {
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ SwTextBoxHelper::changeAnchor, pContact->GetFormat(), pObj);
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unexpected AnchorId." );
+ }
+
+ if ( (RndStdIds::FLY_AS_CHAR != _eAnchorType) &&
+ pNewAnchorFrame &&
+ ( !_bSameOnly || pNewAnchorFrame != pOldAnchorFrame ) )
+ {
+ // #i26791# - Direct object positioning no longer needed. Apply
+ // of attributes (method call <SetAttr(..)>) takes care of the
+ // invalidation of the object position.
+ if ( _bPosCorr )
+ {
+ // #i33313# - consider not connected 'virtual' drawing
+ // objects
+ auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj);
+ if ( pSwDrawVirtObj && !pSwDrawVirtObj->IsConnected() )
+ {
+ SwRect aNewObjRect( aObjRect );
+ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( nullptr ))
+ ->AdjustPositioningAttr( pNewAnchorFrame,
+ &aNewObjRect );
+ }
+ else
+ {
+ static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
+ ->AdjustPositioningAttr( pNewAnchorFrame );
+ }
+ }
+ if (aNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ SwFormatHoriOrient item(pContact->GetFormat()->GetHoriOrient());
+ sal_Int16 nRelOrient(item.GetRelationOrient());
+ if (sw::GetAtPageRelOrientation(nRelOrient, false))
+ {
+ SAL_INFO("sw.ui", "fixing horizontal RelOrientation for at-page anchor");
+ item.SetRelationOrient(nRelOrient);
+ SetAttr(item, *pContact->GetFormat());
+ }
+ }
+ // tdf#136385 set the anchor last - otherwise it messes up the
+ // position in SwDrawContact::Changed_() callback
+ SetAttr(aNewAnch, *pContact->GetFormat());
+ }
+
+ // we have changed the anchoring attributes, and those are used to
+ // order the object in its sorted list, so update its position
+ pAnchoredObj->UpdateObjInSortedList();
+
+ // #i54336#
+ if (xOldAsCharAnchorPos)
+ {
+ if ( pNewAnchorFrame)
+ {
+ // We need to handle InContents in a special way:
+ // The TextAttribut needs to be destroyed which, unfortunately, also
+ // destroys the format. To avoid that, we disconnect the format from
+ // the attribute.
+ const sal_Int32 nIndx( xOldAsCharAnchorPos->nContent.GetIndex() );
+ SwTextNode* pTextNode( xOldAsCharAnchorPos->nNode.GetNode().GetTextNode() );
+ assert(pTextNode && "<SwDoc::ChgAnchor(..)> - missing previous anchor text node for as-character anchored object");
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing FlyInCnt-Hint.");
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // They are disconnected. We now have to destroy the attribute.
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
+ }
+ }
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ getIDocumentState().SetModified();
+
+ return bUnmark;
+}
+
+SwChainRet SwDoc::Chainable( const SwFrameFormat &rSource, const SwFrameFormat &rDest )
+{
+ // The Source must not yet have a Follow.
+ const SwFormatChain &rOldChain = rSource.GetChain();
+ if ( rOldChain.GetNext() )
+ return SwChainRet::SOURCE_CHAINED;
+
+ // Target must not be equal to Source and we also must not have a closed chain.
+ const SwFrameFormat *pFormat = &rDest;
+ do {
+ if( pFormat == &rSource )
+ return SwChainRet::SELF;
+ pFormat = pFormat->GetChain().GetNext();
+ } while ( pFormat );
+
+ // There must not be a chaining from outside to inside or the other way around.
+ if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) )
+ return SwChainRet::SELF;
+
+ // The Target must not yet have a Master.
+ const SwFormatChain &rChain = rDest.GetChain();
+ if( rChain.GetPrev() )
+ return SwChainRet::IS_IN_CHAIN;
+
+ // Target must be empty.
+ const SwNodeIndex* pCntIdx = rDest.GetContent().GetContentIdx();
+ if( !pCntIdx )
+ return SwChainRet::NOT_FOUND;
+
+ SwNodeIndex aNxtIdx( *pCntIdx, 1 );
+ const SwTextNode* pTextNd = aNxtIdx.GetNode().GetTextNode();
+ if( !pTextNd )
+ return SwChainRet::NOT_FOUND;
+
+ const SwNodeOffset nFlySttNd = pCntIdx->GetIndex();
+ if( SwNodeOffset(2) != ( pCntIdx->GetNode().EndOfSectionIndex() - nFlySttNd ) ||
+ pTextNd->GetText().getLength() )
+ {
+ return SwChainRet::NOT_EMPTY;
+ }
+
+ for( auto pSpzFrameFm : *GetSpzFrameFormats() )
+ {
+ const SwFormatAnchor& rAnchor = pSpzFrameFm->GetAnchor();
+ // #i20622# - to-frame anchored objects are allowed.
+ if ( (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PARA) &&
+ (rAnchor.GetAnchorId() != RndStdIds::FLY_AT_CHAR) )
+ continue;
+ if ( nullptr == rAnchor.GetContentAnchor() )
+ continue;
+ SwNodeOffset nTstSttNd = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ if( nFlySttNd <= nTstSttNd && nTstSttNd < nFlySttNd + SwNodeOffset(2) )
+ {
+ return SwChainRet::NOT_EMPTY;
+ }
+ }
+
+ // We also need to consider the right area.
+ // Both Flys need to be located in the same area (Body, Header/Footer, Fly).
+ // If the Source is not the selected frame, it's enough to find a suitable
+ // one. e.g. if it's requested by the API.
+
+ // both in the same fly, header, footer or on the page?
+ const SwFormatAnchor &rSrcAnchor = rSource.GetAnchor(),
+ &rDstAnchor = rDest.GetAnchor();
+ SwNodeOffset nEndOfExtras = GetNodes().GetEndOfExtras().GetIndex();
+ bool bAllowed = false;
+ if ( RndStdIds::FLY_AT_PAGE == rSrcAnchor.GetAnchorId() )
+ {
+ if ( (RndStdIds::FLY_AT_PAGE == rDstAnchor.GetAnchorId()) ||
+ ( rDstAnchor.GetContentAnchor() &&
+ rDstAnchor.GetContentAnchor()->nNode.GetIndex() > nEndOfExtras ))
+ bAllowed = true;
+ }
+ else if( rSrcAnchor.GetContentAnchor() && rDstAnchor.GetContentAnchor() )
+ {
+ const SwNodeIndex &rSrcIdx = rSrcAnchor.GetContentAnchor()->nNode,
+ &rDstIdx = rDstAnchor.GetContentAnchor()->nNode;
+ const SwStartNode* pSttNd = nullptr;
+ if( rSrcIdx == rDstIdx ||
+ ( !pSttNd &&
+ nullptr != ( pSttNd = rSrcIdx.GetNode().FindFlyStartNode() ) &&
+ pSttNd == rDstIdx.GetNode().FindFlyStartNode() ) ||
+ ( !pSttNd &&
+ nullptr != ( pSttNd = rSrcIdx.GetNode().FindFooterStartNode() ) &&
+ pSttNd == rDstIdx.GetNode().FindFooterStartNode() ) ||
+ ( !pSttNd &&
+ nullptr != ( pSttNd = rSrcIdx.GetNode().FindHeaderStartNode() ) &&
+ pSttNd == rDstIdx.GetNode().FindHeaderStartNode() ) ||
+ ( !pSttNd && rDstIdx.GetIndex() > nEndOfExtras &&
+ rSrcIdx.GetIndex() > nEndOfExtras ))
+ bAllowed = true;
+ }
+
+ return bAllowed ? SwChainRet::OK : SwChainRet::WRONG_AREA;
+}
+
+SwChainRet SwDoc::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest )
+{
+ SwChainRet nErr = Chainable( rSource, rDest );
+ if ( nErr == SwChainRet::OK )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::CHAINE, nullptr );
+
+ SwFlyFrameFormat& rDestFormat = const_cast<SwFlyFrameFormat&>(static_cast<const SwFlyFrameFormat&>(rDest));
+
+ // Attach Follow to the Master.
+ SwFormatChain aChain = rDestFormat.GetChain();
+ aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
+ SetAttr( aChain, rDestFormat );
+
+ SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE,
+ RES_CHAIN, RES_CHAIN> aSet( GetAttrPool() );
+
+ // Attach Follow to the Master.
+ aChain.SetPrev( &static_cast<SwFlyFrameFormat&>(rSource) );
+ SetAttr( aChain, rDestFormat );
+
+ // Attach Master to the Follow.
+ // Make sure that the Master has a fixed height.
+ aChain = rSource.GetChain();
+ aChain.SetNext( &rDestFormat );
+ aSet.Put( aChain );
+
+ SwFormatFrameSize aSize( rSource.GetFrameSize() );
+ if ( aSize.GetHeightSizeType() != SwFrameSize::Fixed )
+ {
+ SwFlyFrame *pFly = SwIterator<SwFlyFrame,SwFormat>( rSource ).First();
+ if ( pFly )
+ aSize.SetHeight( pFly->getFrameArea().Height() );
+ aSize.SetHeightSizeType( SwFrameSize::Fixed );
+ aSet.Put( aSize );
+ }
+ SetAttr( aSet, rSource );
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::CHAINE, nullptr );
+ }
+ return nErr;
+}
+
+void SwDoc::Unchain( SwFrameFormat &rFormat )
+{
+ SwFormatChain aChain( rFormat.GetChain() );
+ if ( aChain.GetNext() )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::UNCHAIN, nullptr );
+ SwFrameFormat *pFollow = aChain.GetNext();
+ aChain.SetNext( nullptr );
+ SetAttr( aChain, rFormat );
+ aChain = pFollow->GetChain();
+ aChain.SetPrev( nullptr );
+ SetAttr( aChain, *pFollow );
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::UNCHAIN, nullptr );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docfmt.cxx b/sw/source/core/doc/docfmt.cxx
new file mode 100644
index 000000000..dbf8fefed
--- /dev/null
+++ b/sw/source/core/doc/docfmt.cxx
@@ -0,0 +1,2176 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/numformat.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/rsiditem.hxx>
+#include <editeng/colritem.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <osl/diagnose.h>
+#include <svl/zforlist.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/configmgr.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <fmtpdsc.hxx>
+#include <fmthdft.hxx>
+#include <fmtcntnt.hxx>
+#include <doc.hxx>
+#include <docfunc.hxx>
+#include <drawdoc.hxx>
+#include <MarkManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <UndoCore.hxx>
+#include <UndoAttribute.hxx>
+#include <UndoInsert.hxx>
+#include <pagedesc.hxx>
+#include <rolbck.hxx>
+#include <mvsave.hxx>
+#include <txatbase.hxx>
+#include <swtblfmt.hxx>
+#include <charfmt.hxx>
+#include <docary.hxx>
+#include <paratr.hxx>
+#include <redline.hxx>
+#include <reffld.hxx>
+#include <fmtinfmt.hxx>
+#include <breakit.hxx>
+#include <SwUndoFmt.hxx>
+#include <UndoManager.hxx>
+#include <swmodule.hxx>
+#include <modcfg.hxx>
+#include <frameformats.hxx>
+#include <textboxhelper.hxx>
+#include <memory>
+
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+
+/*
+ * Internal functions
+ */
+
+static void SetTextFormatCollNext( SwTextFormatColl* pTextColl, const SwTextFormatColl* pDel )
+{
+ if ( &pTextColl->GetNextTextFormatColl() == pDel )
+ {
+ pTextColl->SetNextTextFormatColl( *pTextColl );
+ }
+}
+
+static bool lcl_RstAttr( SwNode* pNd, void* pArgs )
+{
+ const sw::DocumentContentOperationsManager::ParaRstFormat* pPara = static_cast<sw::DocumentContentOperationsManager::ParaRstFormat*>(pArgs);
+ SwContentNode* pNode = pNd->GetContentNode();
+ if (pPara && pPara->pLayout && pPara->pLayout->HasMergedParas()
+ && pNode && pNode->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return true;
+ }
+ if( pNode && pNode->HasSwAttrSet() )
+ {
+ const bool bLocked = pNode->IsModifyLocked();
+ pNode->LockModify();
+
+ SwDoc& rDoc = pNode->GetDoc();
+
+ // remove unused attribute RES_LR_SPACE
+ // add list attributes
+ SfxItemSetFixed<
+ RES_PARATR_NUMRULE, RES_PARATR_NUMRULE,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1,
+ RES_PAGEDESC, RES_BREAK> aSavedAttrsSet(rDoc.GetAttrPool());
+ const SfxItemSet* pAttrSetOfNode = pNode->GetpSwAttrSet();
+
+ std::vector<sal_uInt16> aClearWhichIds;
+ // restoring all paragraph list attributes
+ {
+ SfxItemSetFixed<RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END - 1> aListAttrSet( rDoc.GetAttrPool() );
+ aListAttrSet.Set(*pAttrSetOfNode);
+ if ( aListAttrSet.Count() )
+ {
+ aSavedAttrsSet.Put(aListAttrSet);
+ SfxItemIter aIter( aListAttrSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ while( pItem )
+ {
+ aClearWhichIds.push_back( pItem->Which() );
+ pItem = aIter.NextItem();
+ }
+ }
+ }
+
+ const SfxPoolItem* pItem;
+
+ sal_uInt16 const aSavIds[3] = { RES_PAGEDESC, RES_BREAK, RES_PARATR_NUMRULE };
+ for (sal_uInt16 aSavId : aSavIds)
+ {
+ if (SfxItemState::SET == pAttrSetOfNode->GetItemState(aSavId, false, &pItem))
+ {
+ bool bSave = false;
+ switch( aSavId )
+ {
+ case RES_PAGEDESC:
+ bSave = nullptr != pItem->StaticWhichCast(RES_PAGEDESC).GetPageDesc();
+ break;
+ case RES_BREAK:
+ bSave = SvxBreak::NONE != pItem->StaticWhichCast(RES_BREAK).GetBreak();
+ break;
+ case RES_PARATR_NUMRULE:
+ bSave = !pItem->StaticWhichCast(RES_PARATR_NUMRULE).GetValue().isEmpty();
+ break;
+ }
+ if( bSave )
+ {
+ aSavedAttrsSet.Put(*pItem);
+ aClearWhichIds.push_back(aSavId);
+ }
+ }
+ }
+
+ // do not clear items directly from item set and only clear to be kept
+ // attributes, if no deletion item set is found.
+ const bool bKeepAttributes =
+ !pPara || !pPara->pDelSet || pPara->pDelSet->Count() == 0;
+ if ( bKeepAttributes )
+ {
+ pNode->ResetAttr( aClearWhichIds );
+ }
+
+ if( !bLocked )
+ pNode->UnlockModify();
+
+ if( pPara )
+ {
+ SwRegHistory aRegH( pNode, *pNode, pPara->pHistory );
+
+ if( pPara->pDelSet && pPara->pDelSet->Count() )
+ {
+ OSL_ENSURE( !bKeepAttributes,
+ "<lcl_RstAttr(..)> - certain attributes are kept, but not needed." );
+ SfxItemIter aIter( *pPara->pDelSet );
+ for (pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if ( ( pItem->Which() != RES_PAGEDESC &&
+ pItem->Which() != RES_BREAK &&
+ pItem->Which() != RES_PARATR_NUMRULE ) ||
+ ( aSavedAttrsSet.GetItemState( pItem->Which(), false ) != SfxItemState::SET ) )
+ {
+ pNode->ResetAttr( pItem->Which() );
+ }
+ }
+ }
+ else if( pPara->bResetAll )
+ pNode->ResetAllAttr();
+ else
+ pNode->ResetAttr( RES_PARATR_BEGIN, POOLATTR_END - 1 );
+ }
+ else
+ pNode->ResetAllAttr();
+
+ // only restore saved attributes, if needed
+ if (bKeepAttributes && aSavedAttrsSet.Count())
+ {
+ pNode->LockModify();
+
+ pNode->SetAttr(aSavedAttrsSet);
+
+ if( !bLocked )
+ pNode->UnlockModify();
+ }
+ }
+ return true;
+}
+
+void SwDoc::RstTextAttrs(const SwPaM &rRg, bool bInclRefToxMark,
+ bool bExactRange, SwRootFrame const*const pLayout)
+{
+ SwHistory* pHst = nullptr;
+ SwDataChanged aTmp( rRg );
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoResetAttr> pUndo(new SwUndoResetAttr( rRg, RES_CHRFMT ));
+ pHst = &pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
+ sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHst, nullptr, pLayout );
+ aPara.bInclRefToxMark = bInclRefToxMark;
+ aPara.bExactRange = bExactRange;
+ GetNodes().ForEach( pStt->nNode.GetIndex(), pEnd->nNode.GetIndex()+1,
+ sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::ResetAttrs( const SwPaM &rRg,
+ bool bTextAttr,
+ const o3tl::sorted_vector<sal_uInt16> &rAttrs,
+ const bool bSendDataChangedEvents,
+ SwRootFrame const*const pLayout)
+{
+ SwPaM* pPam = const_cast<SwPaM*>(&rRg);
+ if( !bTextAttr && !rAttrs.empty() && RES_TXTATR_END > *(rAttrs.begin()) )
+ bTextAttr = true;
+
+ if( !rRg.HasMark() )
+ {
+ SwTextNode* pTextNd = rRg.GetPoint()->nNode.GetNode().GetTextNode();
+ if( !pTextNd )
+ return ;
+
+ pPam = new SwPaM( *rRg.GetPoint() );
+
+ SwIndex& rSt = pPam->GetPoint()->nContent;
+ sal_Int32 nMkPos, nPtPos = rSt.GetIndex();
+
+ // Special case: if the Cursor is located within a URL attribute, we take over it's area
+ SwTextAttr const*const pURLAttr(
+ pTextNd->GetTextAttrAt(rSt.GetIndex(), RES_TXTATR_INETFMT));
+ if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty())
+ {
+ nMkPos = pURLAttr->GetStart();
+ nPtPos = *pURLAttr->End();
+ }
+ else
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ pTextNd->GetText(), nPtPos,
+ g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
+ WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/,
+ true);
+
+ if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos )
+ {
+ nMkPos = aBndry.startPos;
+ nPtPos = aBndry.endPos;
+ }
+ else
+ {
+ nPtPos = nMkPos = rSt.GetIndex();
+ if( bTextAttr )
+ pTextNd->DontExpandFormat( rSt );
+ }
+ }
+
+ rSt = nMkPos;
+ pPam->SetMark();
+ pPam->GetPoint()->nContent = nPtPos;
+ }
+
+ // #i96644#
+ std::optional< SwDataChanged > oDataChanged;
+ if ( bSendDataChangedEvents )
+ {
+ oDataChanged.emplace( *pPam );
+ }
+ SwHistory* pHst = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoResetAttr> pUndo(new SwUndoResetAttr( rRg,
+ bTextAttr ? sal_uInt16(RES_CONDTXTFMTCOLL) : sal_uInt16(RES_TXTFMTCOLL) ));
+ if( !rAttrs.empty() )
+ {
+ pUndo->SetAttrs( o3tl::sorted_vector(rAttrs) );
+ }
+ pHst = &pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ const SwPosition *pStt = pPam->Start(), *pEnd = pPam->End();
+ sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHst, nullptr, pLayout);
+
+ // mst: not including META here; it seems attrs with CH_TXTATR are omitted
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_INETFMT, RES_TXTATR_UNKNOWN_CONTAINER,
+ RES_PARATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
+ aDelSet(GetAttrPool());
+ for( auto it = rAttrs.rbegin(); it != rAttrs.rend(); ++it )
+ {
+ if( POOLATTR_END > *it )
+ aDelSet.Put( *GetDfltAttr( *it ));
+ }
+ if( aDelSet.Count() )
+ aPara.pDelSet = &aDelSet;
+
+ bool bAdd = true;
+ SwNodeIndex aTmpStt( pStt->nNode );
+ SwNodeIndex aTmpEnd( pEnd->nNode );
+ if( pStt->nContent.GetIndex() ) // just one part
+ {
+ // set up a later, and all CharFormatAttr -> TextFormatAttr
+ SwTextNode* pTNd = aTmpStt.GetNode().GetTextNode();
+ if( pTNd && pTNd->HasSwAttrSet() && pTNd->GetpSwAttrSet()->Count() )
+ {
+ if (pHst)
+ {
+ SwRegHistory history(pTNd, *pTNd, pHst);
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ else
+ {
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ }
+
+ ++aTmpStt;
+ }
+ if( pEnd->nContent.GetIndex() == pEnd->nNode.GetNode().GetContentNode()->Len() )
+ {
+ // set up a later, and all CharFormatAttr -> TextFormatAttr
+ ++aTmpEnd;
+ bAdd = false;
+ }
+ else if( pStt->nNode != pEnd->nNode || !pStt->nContent.GetIndex() )
+ {
+ SwTextNode* pTNd = aTmpEnd.GetNode().GetTextNode();
+ if( pTNd && pTNd->HasSwAttrSet() && pTNd->GetpSwAttrSet()->Count() )
+ {
+ if (pHst)
+ {
+ SwRegHistory history(pTNd, *pTNd, pHst);
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ else
+ {
+ pTNd->FormatToTextAttr(pTNd);
+ }
+ }
+ }
+
+ if( aTmpStt < aTmpEnd )
+ GetNodes().ForEach( pStt->nNode, aTmpEnd, lcl_RstAttr, &aPara );
+ else if( !rRg.HasMark() )
+ {
+ aPara.bResetAll = false ;
+ ::lcl_RstAttr( &pStt->nNode.GetNode(), &aPara );
+ aPara.bResetAll = true ;
+ }
+
+ if( bTextAttr )
+ {
+ if( bAdd )
+ ++aTmpEnd;
+ GetNodes().ForEach( pStt->nNode, aTmpEnd, sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara );
+ }
+
+ getIDocumentState().SetModified();
+
+ oDataChanged.reset(); //before delete pPam
+
+ if( pPam != &rRg )
+ delete pPam;
+}
+
+/// Set the rsid of the next nLen symbols of rRg to the current session number
+void SwDoc::UpdateRsid( const SwPaM &rRg, const sal_Int32 nLen )
+{
+ if (!SW_MOD()->GetModuleConfig()->IsStoreRsid())
+ return;
+
+ SwTextNode *pTextNode = rRg.GetPoint()->nNode.GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return;
+ }
+ const sal_Int32 nStart(rRg.GetPoint()->nContent.GetIndex() - nLen);
+ SvxRsidItem aRsid( mnRsid, RES_CHRATR_RSID );
+
+ SfxItemSetFixed<RES_CHRATR_RSID, RES_CHRATR_RSID> aSet(GetAttrPool());
+ aSet.Put(aRsid);
+ bool const bRet(pTextNode->SetAttr(aSet, nStart,
+ rRg.GetPoint()->nContent.GetIndex()));
+
+ if (bRet && GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwUndo *const pLastUndo = GetUndoManager().GetLastUndo();
+ SwUndoInsert *const pUndoInsert(dynamic_cast<SwUndoInsert*>(pLastUndo));
+ // this function is called after Insert so expects to find SwUndoInsert
+ assert(pUndoInsert);
+ if (pUndoInsert)
+ {
+ pUndoInsert->SetWithRsid();
+ }
+ }
+}
+
+bool SwDoc::UpdateParRsid( SwTextNode *pTextNode, sal_uInt32 nVal )
+{
+ if (!SW_MOD()->GetModuleConfig()->IsStoreRsid())
+ return false;
+
+ if (!pTextNode)
+ {
+ return false;
+ }
+
+ SvxRsidItem aRsid( nVal ? nVal : mnRsid, RES_PARATR_RSID );
+ return pTextNode->SetAttr( aRsid );
+}
+
+/// Set the attribute according to the stated format.
+/// If Undo is enabled, the old values is added to the Undo history.
+void SwDoc::SetAttr( const SfxPoolItem& rAttr, SwFormat& rFormat )
+{
+ SfxItemSet aSet( GetAttrPool(), rAttr.Which(), rAttr.Which() );
+ aSet.Put( rAttr );
+ SetAttr( aSet, rFormat );
+}
+
+/// Set the attribute according to the stated format.
+/// If Undo is enabled, the old values is added to the Undo history.
+void SwDoc::SetAttr( const SfxItemSet& rSet, SwFormat& rFormat )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwUndoFormatAttrHelper aTmp( rFormat );
+ rFormat.SetFormatAttr( rSet );
+ if ( aTmp.GetUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo( aTmp.ReleaseUndo() );
+ }
+ else
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ }
+ }
+ else
+ {
+ rFormat.SetFormatAttr( rSet );
+ }
+
+ // If the format is a shape, and it has a textbox, sync.
+ auto pShapeFormat = dynamic_cast<SwFrameFormat*>(&rFormat);
+ if (pShapeFormat && SwTextBoxHelper::isTextBox(pShapeFormat, RES_DRAWFRMFMT))
+ {
+ if (auto pObj = pShapeFormat->FindRealSdrObject())
+ {
+ SwTextBoxHelper::syncFlyFrameAttr(*pShapeFormat, rSet, pObj);
+ SwTextBoxHelper::changeAnchor(pShapeFormat, pObj);
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::ResetAttrAtFormat( const sal_uInt16 nWhichId,
+ SwFormat& rChangedFormat )
+{
+ std::unique_ptr<SwUndo> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoFormatResetAttr( rChangedFormat, nWhichId ));
+
+ const bool bAttrReset = rChangedFormat.ResetFormatAttr( nWhichId );
+
+ if ( bAttrReset )
+ {
+ if ( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ getIDocumentState().SetModified();
+ }
+}
+
+static bool lcl_SetNewDefTabStops( SwTwips nOldWidth, SwTwips nNewWidth,
+ SvxTabStopItem& rChgTabStop )
+{
+ // Set the default values of all TabStops to the new value.
+ // Attention: we always work with the PoolAttribute here, so that
+ // we don't calculate the same value on the same TabStop (pooled!) for all sets.
+ // We send a FormatChg to modify.
+
+ sal_uInt16 nOldCnt = rChgTabStop.Count();
+ if( !nOldCnt || nOldWidth == nNewWidth )
+ return false;
+
+ // Find the default's beginning
+ sal_uInt16 n;
+ for( n = nOldCnt; n ; --n )
+ if( SvxTabAdjust::Default != rChgTabStop[n - 1].GetAdjustment() )
+ break;
+ ++n;
+ if( n < nOldCnt ) // delete the DefTabStops
+ rChgTabStop.Remove( n, nOldCnt - n );
+ return true;
+}
+
+/// Set the attribute as new default attribute in this document.
+/// If Undo is enabled, the old value is added to the Undo history.
+void SwDoc::SetDefault( const SfxPoolItem& rAttr )
+{
+ SfxItemSet aSet( GetAttrPool(), rAttr.Which(), rAttr.Which() );
+ aSet.Put( rAttr );
+ SetDefault( aSet );
+}
+
+void SwDoc::SetDefault( const SfxItemSet& rSet )
+{
+ if( !rSet.Count() )
+ return;
+
+ sw::BroadcastingModify aCallMod;
+ SwAttrSet aOld( GetAttrPool(), rSet.GetRanges() ),
+ aNew( GetAttrPool(), rSet.GetRanges() );
+ SfxItemIter aIter( rSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ SfxItemPool* pSdrPool = GetAttrPool().GetSecondaryPool();
+ do
+ {
+ bool bCheckSdrDflt = false;
+ const sal_uInt16 nWhich = pItem->Which();
+ aOld.Put( GetAttrPool().GetDefaultItem( nWhich ) );
+ GetAttrPool().SetPoolDefaultItem( *pItem );
+ aNew.Put( GetAttrPool().GetDefaultItem( nWhich ) );
+
+ if (isCHRATR(nWhich) || isTXTATR(nWhich))
+ {
+ aCallMod.Add( mpDfltTextFormatColl.get() );
+ aCallMod.Add( mpDfltCharFormat.get() );
+ bCheckSdrDflt = nullptr != pSdrPool;
+ }
+ else if ( isPARATR(nWhich) ||
+ isPARATR_LIST(nWhich) )
+ {
+ aCallMod.Add( mpDfltTextFormatColl.get() );
+ bCheckSdrDflt = nullptr != pSdrPool;
+ }
+ else if (isGRFATR(nWhich))
+ {
+ aCallMod.Add( mpDfltGrfFormatColl.get() );
+ }
+ else if (isFRMATR(nWhich) || isDrawingLayerAttribute(nWhich) )
+ {
+ aCallMod.Add( mpDfltGrfFormatColl.get() );
+ aCallMod.Add( mpDfltTextFormatColl.get() );
+ aCallMod.Add( mpDfltFrameFormat.get() );
+ }
+ else if (isBOXATR(nWhich))
+ {
+ aCallMod.Add( mpDfltFrameFormat.get() );
+ }
+
+ // also copy the defaults
+ if( bCheckSdrDflt )
+ {
+ sal_uInt16 nSlotId = GetAttrPool().GetSlotId( nWhich );
+ if( 0 != nSlotId && nSlotId != nWhich )
+ {
+ sal_uInt16 nEdtWhich = pSdrPool->GetWhich( nSlotId );
+ if( 0 != nEdtWhich && nSlotId != nEdtWhich )
+ {
+ std::unique_ptr<SfxPoolItem> pCpy(pItem->Clone());
+ pCpy->SetWhich( nEdtWhich );
+ pSdrPool->SetPoolDefaultItem( *pCpy );
+ }
+ }
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+
+ if( aNew.Count() && aCallMod.HasWriterListeners() )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDefaultAttr>( aOld, *this ) );
+ }
+
+ const SvxTabStopItem* pTmpItem = aNew.GetItemIfSet( RES_PARATR_TABSTOP, false );
+ if( pTmpItem && pTmpItem->Count() )
+ {
+ // Set the default values of all TabStops to the new value.
+ // Attention: we always work with the PoolAttribute here, so that
+ // we don't calculate the same value on the same TabStop (pooled!) for all sets.
+ // We send a FormatChg to modify.
+ SwTwips nNewWidth = (*pTmpItem)[ 0 ].GetTabPos(),
+ nOldWidth = aOld.Get(RES_PARATR_TABSTOP)[ 0 ].GetTabPos();
+
+ bool bChg = false;
+ for (const SfxPoolItem* pItem2 : GetAttrPool().GetItemSurrogates(RES_PARATR_TABSTOP))
+ {
+ if(auto pTabStopItem = pItem2->DynamicWhichCast(RES_PARATR_TABSTOP))
+ bChg |= lcl_SetNewDefTabStops( nOldWidth, nNewWidth,
+ *const_cast<SvxTabStopItem*>(pTabStopItem) );
+ }
+
+ aNew.ClearItem( RES_PARATR_TABSTOP );
+ aOld.ClearItem( RES_PARATR_TABSTOP );
+ if( bChg )
+ {
+ SwFormatChg aChgFormat( mpDfltCharFormat.get() );
+ // notify the frames
+ aCallMod.CallSwClientNotify(sw::LegacyModifyHint( &aChgFormat, &aChgFormat ));
+ }
+ }
+ }
+
+ if( aNew.Count() && aCallMod.HasWriterListeners() )
+ {
+ SwAttrSetChg aChgOld( aOld, aOld );
+ SwAttrSetChg aChgNew( aNew, aNew );
+ aCallMod.CallSwClientNotify(sw::LegacyModifyHint( &aChgOld, &aChgNew )); // all changed are sent
+ }
+
+ // remove the default formats from the object again
+ SwIterator<SwClient, sw::BroadcastingModify> aClientIter(aCallMod);
+ for(SwClient* pClient = aClientIter.First(); pClient; pClient = aClientIter.Next())
+ aCallMod.Remove( pClient );
+
+ getIDocumentState().SetModified();
+}
+
+/// Get the default attribute in this document
+const SfxPoolItem& SwDoc::GetDefault( sal_uInt16 nFormatHint ) const
+{
+ return GetAttrPool().GetDefaultItem( nFormatHint );
+}
+
+/// Delete the formats
+void SwDoc::DelCharFormat(size_t nFormat, bool bBroadcast)
+{
+ SwCharFormat * pDel = (*mpCharFormatTable)[nFormat];
+
+ if (bBroadcast)
+ BroadcastStyleOperation(pDel->GetName(), SfxStyleFamily::Char,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCharFormatDelete>(pDel, *this));
+ }
+
+ delete (*mpCharFormatTable)[nFormat];
+ mpCharFormatTable->erase(mpCharFormatTable->begin() + nFormat);
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::DelCharFormat( SwCharFormat const *pFormat, bool bBroadcast )
+{
+ size_t nFormat = mpCharFormatTable->GetPos( pFormat );
+ OSL_ENSURE( SIZE_MAX != nFormat, "Format not found," );
+ DelCharFormat( nFormat, bBroadcast );
+}
+
+void SwDoc::DelFrameFormat( SwFrameFormat *pFormat, bool bBroadcast )
+{
+ if( dynamic_cast<const SwTableBoxFormat*>( pFormat) != nullptr || dynamic_cast<const SwTableLineFormat*>( pFormat) != nullptr )
+ {
+ OSL_ENSURE( false, "Format is not in the DocArray any more, "
+ "so it can be deleted with delete" );
+ delete pFormat;
+ }
+ else
+ {
+ // The format has to be in the one or the other, we'll see in which one.
+ if (mpFrameFormatTable->ContainsFormat(*pFormat))
+ {
+ if (bBroadcast)
+ BroadcastStyleOperation(pFormat->GetName(),
+ SfxStyleFamily::Frame,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFrameFormatDelete>(pFormat, *this));
+ }
+
+ mpFrameFormatTable->erase( pFormat );
+ delete pFormat;
+ }
+ else
+ {
+ bool contains = GetSpzFrameFormats()->ContainsFormat(*pFormat);
+ OSL_ENSURE( contains, "FrameFormat not found." );
+ if( contains )
+ {
+ GetSpzFrameFormats()->erase( pFormat );
+ delete pFormat;
+ }
+ }
+ }
+}
+
+void SwDoc::DelTableFrameFormat( SwTableFormat *pFormat )
+{
+ SwFrameFormats::const_iterator it = mpTableFrameFormatTable->find( pFormat );
+ OSL_ENSURE( it != mpTableFrameFormatTable->end(), "Format not found," );
+ mpTableFrameFormatTable->erase( it );
+ delete pFormat;
+}
+
+SwFrameFormat* SwDoc::FindFrameFormatByName( const OUString& rName ) const
+{
+ return mpFrameFormatTable->FindFormatByName( rName );
+}
+
+/// Create the formats
+SwFlyFrameFormat *SwDoc::MakeFlyFrameFormat( const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom )
+{
+ SwFlyFrameFormat *pFormat = new SwFlyFrameFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+ GetSpzFrameFormats()->push_back(pFormat);
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+SwDrawFrameFormat *SwDoc::MakeDrawFrameFormat( const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom )
+{
+ SwDrawFrameFormat *pFormat = new SwDrawFrameFormat( GetAttrPool(), rFormatName, pDerivedFrom);
+ GetSpzFrameFormats()->push_back(pFormat);
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+size_t SwDoc::GetTableFrameFormatCount(bool bUsed) const
+{
+ if (!bUsed)
+ {
+ return mpTableFrameFormatTable->size();
+ }
+
+ SwAutoFormatGetDocNode aGetHt(&GetNodes());
+ size_t nCount = 0;
+ for (SwFrameFormat* const & pFormat : *mpTableFrameFormatTable)
+ {
+ if (!pFormat->GetInfo(aGetHt))
+ nCount++;
+ }
+ return nCount;
+}
+
+SwFrameFormat& SwDoc::GetTableFrameFormat(size_t nFormat, bool bUsed) const
+{
+ if (!bUsed)
+ {
+ return *((*mpTableFrameFormatTable)[nFormat]);
+ }
+
+ SwAutoFormatGetDocNode aGetHt(&GetNodes());
+
+ size_t index = 0;
+
+ for (SwFrameFormat* const & pFormat : *mpTableFrameFormatTable)
+ {
+ if (!pFormat->GetInfo(aGetHt))
+ {
+ if (index == nFormat)
+ return *pFormat;
+ else
+ index++;
+ }
+ }
+ throw std::out_of_range("Format index out of range.");
+}
+
+SwTableFormat* SwDoc::MakeTableFrameFormat( const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom )
+{
+ SwTableFormat* pFormat = new SwTableFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+ mpTableFrameFormatTable->push_back( pFormat );
+ getIDocumentState().SetModified();
+
+ return pFormat;
+}
+
+SwFrameFormat *SwDoc::MakeFrameFormat(const OUString &rFormatName,
+ SwFrameFormat *pDerivedFrom,
+ bool bBroadcast, bool bAuto)
+{
+ SwFrameFormat *pFormat = new SwFrameFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+
+ pFormat->SetAuto(bAuto);
+ mpFrameFormatTable->push_back( pFormat );
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFrameFormatCreate>(pFormat, pDerivedFrom, *this));
+ }
+
+ if (bBroadcast)
+ {
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Frame,
+ SfxHintId::StyleSheetCreated);
+ }
+
+ return pFormat;
+}
+
+SwFormat *SwDoc::MakeFrameFormat_(const OUString &rFormatName,
+ SwFormat *pDerivedFrom,
+ bool bBroadcast, bool bAuto)
+{
+ SwFrameFormat *pFrameFormat = dynamic_cast<SwFrameFormat*>(pDerivedFrom);
+ pFrameFormat = MakeFrameFormat( rFormatName, pFrameFormat, bBroadcast, bAuto );
+ return pFrameFormat;
+}
+
+SwCharFormat *SwDoc::MakeCharFormat( const OUString &rFormatName,
+ SwCharFormat *pDerivedFrom,
+ bool bBroadcast )
+{
+ SwCharFormat *pFormat = new SwCharFormat( GetAttrPool(), rFormatName, pDerivedFrom );
+ mpCharFormatTable->insert( pFormat );
+ pFormat->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCharFormatCreate>(pFormat, pDerivedFrom, *this));
+ }
+
+ if (bBroadcast)
+ {
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Char,
+ SfxHintId::StyleSheetCreated);
+ }
+
+ return pFormat;
+}
+
+SwFormat *SwDoc::MakeCharFormat_(const OUString &rFormatName,
+ SwFormat *pDerivedFrom,
+ bool bBroadcast, bool /*bAuto*/)
+{
+ SwCharFormat *pCharFormat = dynamic_cast<SwCharFormat*>(pDerivedFrom);
+ pCharFormat = MakeCharFormat( rFormatName, pCharFormat, bBroadcast );
+ return pCharFormat;
+}
+
+/// Create the FormatCollections
+SwTextFormatColl* SwDoc::MakeTextFormatColl( const OUString &rFormatName,
+ SwTextFormatColl *pDerivedFrom,
+ bool bBroadcast)
+{
+ SwTextFormatColl *pFormatColl = new SwTextFormatColl( GetAttrPool(), rFormatName,
+ pDerivedFrom );
+ mpTextFormatCollTable->push_back(pFormatColl);
+ pFormatColl->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTextFormatCollCreate>(pFormatColl, pDerivedFrom,
+ *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Para,
+ SfxHintId::StyleSheetCreated);
+
+ return pFormatColl;
+}
+
+SwFormat *SwDoc::MakeTextFormatColl_(const OUString &rFormatName,
+ SwFormat *pDerivedFrom,
+ bool bBroadcast, bool /*bAuto*/)
+{
+ SwTextFormatColl *pTextFormatColl = dynamic_cast<SwTextFormatColl*>(pDerivedFrom);
+ pTextFormatColl = MakeTextFormatColl( rFormatName, pTextFormatColl, bBroadcast );
+ return pTextFormatColl;
+}
+
+//FEATURE::CONDCOLL
+SwConditionTextFormatColl* SwDoc::MakeCondTextFormatColl( const OUString &rFormatName,
+ SwTextFormatColl *pDerivedFrom,
+ bool bBroadcast)
+{
+ SwConditionTextFormatColl*pFormatColl = new SwConditionTextFormatColl( GetAttrPool(),
+ rFormatName, pDerivedFrom );
+ mpTextFormatCollTable->push_back(pFormatColl);
+ pFormatColl->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoCondTextFormatCollCreate>(pFormatColl, pDerivedFrom,
+ *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rFormatName, SfxStyleFamily::Para,
+ SfxHintId::StyleSheetCreated);
+
+ return pFormatColl;
+}
+//FEATURE::CONDCOLL
+
+// GRF
+SwGrfFormatColl* SwDoc::MakeGrfFormatColl( const OUString &rFormatName,
+ SwGrfFormatColl *pDerivedFrom )
+{
+ SwGrfFormatColl *pFormatColl = new SwGrfFormatColl( GetAttrPool(), rFormatName,
+ pDerivedFrom );
+ mpGrfFormatCollTable->push_back( pFormatColl );
+ pFormatColl->SetAuto(false);
+ getIDocumentState().SetModified();
+ return pFormatColl;
+}
+
+void SwDoc::DelTextFormatColl(size_t nFormatColl, bool bBroadcast)
+{
+ OSL_ENSURE( nFormatColl, "Remove of Coll 0." );
+
+ // Who has the to-be-deleted as their Next?
+ SwTextFormatColl *pDel = (*mpTextFormatCollTable)[nFormatColl];
+ if( mpDfltTextFormatColl.get() == pDel )
+ return; // never delete default!
+
+ if (bBroadcast)
+ BroadcastStyleOperation(pDel->GetName(), SfxStyleFamily::Para,
+ SfxHintId::StyleSheetErased);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoTextFormatCollDelete> pUndo;
+ if (RES_CONDTXTFMTCOLL == pDel->Which())
+ {
+ pUndo.reset(new SwUndoCondTextFormatCollDelete(pDel, *this));
+ }
+ else
+ {
+ pUndo.reset(new SwUndoTextFormatCollDelete(pDel, *this));
+ }
+
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ // Remove the FormatColl
+ mpTextFormatCollTable->erase(mpTextFormatCollTable->begin() + nFormatColl);
+ // Correct next
+ for( SwTextFormatColls::const_iterator it = mpTextFormatCollTable->begin() + 1; it != mpTextFormatCollTable->end(); ++it )
+ SetTextFormatCollNext( *it, pDel );
+ delete pDel;
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::DelTextFormatColl( SwTextFormatColl const *pColl, bool bBroadcast )
+{
+ size_t nFormat = mpTextFormatCollTable->GetPos( pColl );
+ OSL_ENSURE( SIZE_MAX != nFormat, "Collection not found," );
+ DelTextFormatColl( nFormat, bBroadcast );
+}
+
+static bool lcl_SetTextFormatColl( SwNode* pNode, void* pArgs )
+{
+ SwContentNode* pCNd = pNode->GetTextNode();
+
+ if( pCNd == nullptr)
+ return true;
+
+ sw::DocumentContentOperationsManager::ParaRstFormat* pPara = static_cast<sw::DocumentContentOperationsManager::ParaRstFormat*>(pArgs);
+
+ if (pPara->pLayout && pPara->pLayout->HasMergedParas())
+ {
+ if (pCNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return true;
+ }
+ if (pCNd->IsTextNode())
+ {
+ pCNd = sw::GetParaPropsNode(*pPara->pLayout, SwNodeIndex(*pCNd));
+ }
+ }
+
+ SwTextFormatColl* pFormat = static_cast<SwTextFormatColl*>(pPara->pFormatColl);
+ if ( pPara->bReset )
+ {
+ lcl_RstAttr(pCNd, pPara);
+
+ // #i62675# check, if paragraph style has changed
+ if ( pPara->bResetListAttrs &&
+ pFormat != pCNd->GetFormatColl() &&
+ pFormat->GetItemState( RES_PARATR_NUMRULE ) == SfxItemState::SET )
+ {
+ // Check, if the list style of the paragraph will change.
+ bool bChangeOfListStyleAtParagraph( true );
+ SwTextNode& rTNd(dynamic_cast<SwTextNode&>(*pCNd));
+ {
+ SwNumRule* pNumRuleAtParagraph(rTNd.GetNumRule());
+ if ( pNumRuleAtParagraph )
+ {
+ const SwNumRuleItem& rNumRuleItemAtParagraphStyle =
+ pFormat->GetNumRule();
+ if ( rNumRuleItemAtParagraphStyle.GetValue() ==
+ pNumRuleAtParagraph->GetName() )
+ {
+ bChangeOfListStyleAtParagraph = false;
+ }
+ }
+ }
+
+ if ( bChangeOfListStyleAtParagraph )
+ {
+ std::unique_ptr< SwRegHistory > pRegH;
+ if ( pPara->pHistory )
+ {
+ pRegH.reset(new SwRegHistory(&rTNd, rTNd, pPara->pHistory));
+ }
+
+ pCNd->ResetAttr( RES_PARATR_NUMRULE );
+
+ // reset all list attributes
+ pCNd->ResetAttr( RES_PARATR_LIST_LEVEL );
+ pCNd->ResetAttr( RES_PARATR_LIST_ISRESTART );
+ pCNd->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ pCNd->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+ pCNd->ResetAttr( RES_PARATR_LIST_ID );
+ }
+ }
+ }
+
+ // add to History so that old data is saved, if necessary
+ if( pPara->pHistory )
+ pPara->pHistory->Add( pCNd->GetFormatColl(), pCNd->GetIndex(),
+ SwNodeType::Text );
+
+ pCNd->ChgFormatColl( pFormat );
+
+ pPara->nWhich++;
+
+ return true;
+}
+
+bool SwDoc::SetTextFormatColl(const SwPaM &rRg,
+ SwTextFormatColl *pFormat,
+ const bool bReset,
+ const bool bResetListAttrs,
+ SwRootFrame const*const pLayout)
+{
+ SwDataChanged aTmp( rRg );
+ const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End();
+ SwHistory* pHst = nullptr;
+ bool bRet = true;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoFormatColl> pUndo(new SwUndoFormatColl( rRg, pFormat,
+ bReset,
+ bResetListAttrs ));
+ pHst = pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ sw::DocumentContentOperationsManager::ParaRstFormat aPara(
+ pStt, pEnd, pHst, nullptr, pLayout);
+ aPara.pFormatColl = pFormat;
+ aPara.bReset = bReset;
+ // #i62675#
+ aPara.bResetListAttrs = bResetListAttrs;
+
+ GetNodes().ForEach( pStt->nNode.GetIndex(), pEnd->nNode.GetIndex()+1,
+ lcl_SetTextFormatColl, &aPara );
+ if( !aPara.nWhich )
+ bRet = false; // didn't find a valid Node
+
+ if (bRet)
+ {
+ getIDocumentState().SetModified();
+ }
+
+ return bRet;
+}
+
+/// Copy the formats to itself
+SwFormat* SwDoc::CopyFormat( const SwFormat& rFormat,
+ const SwFormatsBase& rFormatArr,
+ FNCopyFormat fnCopyFormat, const SwFormat& rDfltFormat )
+{
+ // It's no autoformat, default format or collection format,
+ // then search for it.
+ if( !rFormat.IsAuto() || !rFormat.GetRegisteredIn() )
+ for( size_t n = 0; n < rFormatArr.GetFormatCount(); ++n )
+ {
+ // Does the Doc already contain the template?
+ if( rFormatArr.GetFormat(n)->GetName()==rFormat.GetName() )
+ return rFormatArr.GetFormat(n);
+ }
+
+ // Search for the "parent" first
+ SwFormat* pParent = const_cast<SwFormat*>(&rDfltFormat);
+ if( rFormat.DerivedFrom() && pParent != rFormat.DerivedFrom() )
+ pParent = CopyFormat( *rFormat.DerivedFrom(), rFormatArr,
+ fnCopyFormat, rDfltFormat );
+
+ // Create the format and copy the attributes
+ // #i40550#
+ SwFormat* pNewFormat = (this->*fnCopyFormat)( rFormat.GetName(), pParent, false, true );
+ pNewFormat->SetAuto( rFormat.IsAuto() );
+ pNewFormat->CopyAttrs( rFormat ); // copy the attributes
+
+ pNewFormat->SetPoolFormatId( rFormat.GetPoolFormatId() );
+ pNewFormat->SetPoolHelpId( rFormat.GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pNewFormat->SetPoolHlpFileId( UCHAR_MAX );
+
+ return pNewFormat;
+}
+
+/// copy the frame format
+SwFrameFormat* SwDoc::CopyFrameFormat( const SwFrameFormat& rFormat )
+{
+ return static_cast<SwFrameFormat*>(CopyFormat( rFormat, *GetFrameFormats(), &SwDoc::MakeFrameFormat_,
+ *GetDfltFrameFormat() ));
+}
+
+/// copy the char format
+SwCharFormat* SwDoc::CopyCharFormat( const SwCharFormat& rFormat )
+{
+ return static_cast<SwCharFormat*>(CopyFormat( rFormat, *GetCharFormats(),
+ &SwDoc::MakeCharFormat_,
+ *GetDfltCharFormat() ));
+}
+
+/// copy TextNodes
+SwTextFormatColl* SwDoc::CopyTextColl( const SwTextFormatColl& rColl )
+{
+ SwTextFormatColl* pNewColl = FindTextFormatCollByName( rColl.GetName() );
+ if( pNewColl )
+ return pNewColl;
+
+ // search for the "parent" first
+ SwTextFormatColl* pParent = mpDfltTextFormatColl.get();
+ if( pParent != rColl.DerivedFrom() )
+ pParent = CopyTextColl( *static_cast<SwTextFormatColl*>(rColl.DerivedFrom()) );
+
+//FEATURE::CONDCOLL
+ if( RES_CONDTXTFMTCOLL == rColl.Which() )
+ {
+ pNewColl = new SwConditionTextFormatColl( GetAttrPool(), rColl.GetName(),
+ pParent);
+ mpTextFormatCollTable->push_back( pNewColl );
+ pNewColl->SetAuto(false);
+ getIDocumentState().SetModified();
+
+ // copy the conditions
+ static_cast<SwConditionTextFormatColl*>(pNewColl)->SetConditions(
+ static_cast<const SwConditionTextFormatColl&>(rColl).GetCondColls() );
+ }
+ else
+//FEATURE::CONDCOLL
+ pNewColl = MakeTextFormatColl( rColl.GetName(), pParent );
+
+ // copy the auto formats or the attributes
+ pNewColl->CopyAttrs( rColl );
+
+ if(rColl.IsAssignedToListLevelOfOutlineStyle())
+ pNewColl->AssignToListLevelOfOutlineStyle(rColl.GetAssignedOutlineStyleLevel());
+ pNewColl->SetPoolFormatId( rColl.GetPoolFormatId() );
+ pNewColl->SetPoolHelpId( rColl.GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pNewColl->SetPoolHlpFileId( UCHAR_MAX );
+
+ if( &rColl.GetNextTextFormatColl() != &rColl )
+ pNewColl->SetNextTextFormatColl( *CopyTextColl( rColl.GetNextTextFormatColl() ));
+
+ // create the NumRule if necessary
+ if( this != rColl.GetDoc() )
+ {
+ const SwNumRuleItem* pItem = pNewColl->GetItemIfSet( RES_PARATR_NUMRULE,
+ false );
+ if( pItem )
+ {
+ const OUString& rName = pItem->GetValue();
+ if( !rName.isEmpty() )
+ {
+ const SwNumRule* pRule = rColl.GetDoc()->FindNumRulePtr( rName );
+ if( pRule && !pRule->IsAutoRule() )
+ {
+ SwNumRule* pDestRule = FindNumRulePtr( rName );
+ if( pDestRule )
+ pDestRule->SetInvalidRule( true );
+ else
+ MakeNumRule( rName, pRule );
+ }
+ }
+ }
+ }
+ return pNewColl;
+}
+
+/// copy the graphic nodes
+SwGrfFormatColl* SwDoc::CopyGrfColl( const SwGrfFormatColl& rColl )
+{
+ SwGrfFormatColl* pNewColl = mpGrfFormatCollTable->FindFormatByName( rColl.GetName() );
+ if( pNewColl )
+ return pNewColl;
+
+ // Search for the "parent" first
+ SwGrfFormatColl* pParent = mpDfltGrfFormatColl.get();
+ if( pParent != rColl.DerivedFrom() )
+ pParent = CopyGrfColl( *static_cast<SwGrfFormatColl*>(rColl.DerivedFrom()) );
+
+ // if not, copy them
+ pNewColl = MakeGrfFormatColl( rColl.GetName(), pParent );
+
+ // copy the attributes
+ pNewColl->CopyAttrs( rColl );
+
+ pNewColl->SetPoolFormatId( rColl.GetPoolFormatId() );
+ pNewColl->SetPoolHelpId( rColl.GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pNewColl->SetPoolHlpFileId( UCHAR_MAX );
+
+ return pNewColl;
+}
+
+void SwDoc::CopyFormatArr( const SwFormatsBase& rSourceArr,
+ SwFormatsBase const & rDestArr,
+ FNCopyFormat fnCopyFormat,
+ SwFormat& rDfltFormat )
+{
+ SwFormat* pSrc, *pDest;
+
+ // 1st step: Create all formats (skip the 0th - it's the default one)
+ for( size_t nSrc = rSourceArr.GetFormatCount(); nSrc > 1; )
+ {
+ pSrc = rSourceArr.GetFormat( --nSrc );
+ if( pSrc->IsDefault() || pSrc->IsAuto() )
+ continue;
+
+ if( nullptr == rDestArr.FindFormatByName( pSrc->GetName() ) )
+ {
+ if( RES_CONDTXTFMTCOLL == pSrc->Which() )
+ MakeCondTextFormatColl( pSrc->GetName(), static_cast<SwTextFormatColl*>(&rDfltFormat) );
+ else
+ // #i40550#
+ (this->*fnCopyFormat)( pSrc->GetName(), &rDfltFormat, false, true );
+ }
+ }
+
+ // 2nd step: Copy all attributes, set the right parents
+ for( size_t nSrc = rSourceArr.GetFormatCount(); nSrc > 1; )
+ {
+ pSrc = rSourceArr.GetFormat( --nSrc );
+ if( pSrc->IsDefault() || pSrc->IsAuto() )
+ continue;
+
+ pDest = rDestArr.FindFormatByName( pSrc->GetName() );
+ pDest->SetAuto(false);
+ pDest->DelDiffs( *pSrc );
+
+ // #i94285#: existing <SwFormatPageDesc> instance, before copying attributes
+ const SwFormatPageDesc* pItem;
+ if( &GetAttrPool() != pSrc->GetAttrSet().GetPool()
+ && (pItem = pSrc->GetAttrSet().GetItemIfSet( RES_PAGEDESC, false ))
+ && pItem->GetPageDesc() )
+ {
+ SwFormatPageDesc aPageDesc( *pItem );
+ const OUString& rNm = aPageDesc.GetPageDesc()->GetName();
+ SwPageDesc* pPageDesc = FindPageDesc( rNm );
+ if( !pPageDesc )
+ {
+ pPageDesc = MakePageDesc(rNm);
+ }
+ aPageDesc.RegisterToPageDesc( *pPageDesc );
+ SwAttrSet aTmpAttrSet( pSrc->GetAttrSet() );
+ aTmpAttrSet.Put( aPageDesc );
+ pDest->SetFormatAttr( aTmpAttrSet );
+ }
+ else
+ {
+ pDest->SetFormatAttr( pSrc->GetAttrSet() );
+ }
+
+ pDest->SetPoolFormatId( pSrc->GetPoolFormatId() );
+ pDest->SetPoolHelpId( pSrc->GetPoolHelpId() );
+
+ // Always set the HelpFile Id to default!
+ pDest->SetPoolHlpFileId( UCHAR_MAX );
+
+ if( pSrc->DerivedFrom() )
+ pDest->SetDerivedFrom( rDestArr.FindFormatByName(
+ pSrc->DerivedFrom()->GetName() ) );
+ if( RES_TXTFMTCOLL == pSrc->Which() ||
+ RES_CONDTXTFMTCOLL == pSrc->Which() )
+ {
+ SwTextFormatColl* pSrcColl = static_cast<SwTextFormatColl*>(pSrc),
+ * pDstColl = static_cast<SwTextFormatColl*>(pDest);
+ if( &pSrcColl->GetNextTextFormatColl() != pSrcColl )
+ pDstColl->SetNextTextFormatColl(
+ *static_cast<SwTextFormatColl*>(rDestArr.FindFormatByName( pSrcColl->GetNextTextFormatColl().GetName() )) );
+
+ if(pSrcColl->IsAssignedToListLevelOfOutlineStyle())
+ pDstColl->AssignToListLevelOfOutlineStyle(pSrcColl->GetAssignedOutlineStyleLevel());
+
+//FEATURE::CONDCOLL
+ if( RES_CONDTXTFMTCOLL == pSrc->Which() )
+ {
+ if (pDstColl->Which() != RES_CONDTXTFMTCOLL)
+ {
+ // Target already had a style with a matching name, but it's not a conditional
+ // style, then don't copy the conditions.
+ continue;
+ }
+
+ // Copy the conditions, but delete the old ones first!
+ static_cast<SwConditionTextFormatColl*>(pDstColl)->SetConditions(
+ static_cast<SwConditionTextFormatColl*>(pSrc)->GetCondColls() );
+ }
+//FEATURE::CONDCOLL
+ }
+ }
+}
+
+void SwDoc::CopyPageDescHeaderFooterImpl( bool bCpyHeader,
+ const SwFrameFormat& rSrcFormat, SwFrameFormat& rDestFormat )
+{
+ // Treat the header and footer attributes in the right way:
+ // Copy content nodes across documents!
+ sal_uInt16 nAttr = bCpyHeader ? sal_uInt16(RES_HEADER) : sal_uInt16(RES_FOOTER);
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET != rSrcFormat.GetAttrSet().GetItemState( nAttr, false, &pItem ))
+ return ;
+
+ // The header only contains the reference to the format from the other document!
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+
+ SwFrameFormat* pOldFormat;
+ if( bCpyHeader )
+ pOldFormat = pNewItem->StaticWhichCast(RES_HEADER).GetHeaderFormat();
+ else
+ pOldFormat = pNewItem->StaticWhichCast(RES_FOOTER).GetFooterFormat();
+
+ if( !pOldFormat )
+ return;
+
+ SwFrameFormat* pNewFormat = new SwFrameFormat( GetAttrPool(), "CpyDesc",
+ GetDfltFrameFormat() );
+ pNewFormat->CopyAttrs( *pOldFormat );
+
+ if( const SwFormatContent* pContent = pNewFormat->GetAttrSet().GetItemIfSet(
+ RES_CNTNT, false ) )
+ {
+ if( pContent->GetContentIdx() )
+ {
+ SwNodeIndex aTmpIdx( GetNodes().GetEndOfAutotext() );
+ const SwNodes& rSrcNds = rSrcFormat.GetDoc()->GetNodes();
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( aTmpIdx,
+ bCpyHeader
+ ? SwHeaderStartNode
+ : SwFooterStartNode );
+ const SwNode& rCSttNd = pContent->GetContentIdx()->GetNode();
+ SwNodeRange aRg( rCSttNd, SwNodeOffset(0), *rCSttNd.EndOfSectionNode() );
+ aTmpIdx = *pSttNd->EndOfSectionNode();
+ rSrcNds.Copy_( aRg, aTmpIdx );
+ aTmpIdx = *pSttNd;
+ rSrcFormat.GetDoc()->GetDocumentContentOperationsManager().CopyFlyInFlyImpl(aRg, nullptr, aTmpIdx);
+ // TODO: investigate calling CopyWithFlyInFly?
+ SwPaM const source(aRg.aStart, aRg.aEnd);
+ SwPosition dest(aTmpIdx);
+ sw::CopyBookmarks(source, dest);
+ pNewFormat->SetFormatAttr( SwFormatContent( pSttNd ));
+ }
+ else
+ pNewFormat->ResetFormatAttr( RES_CNTNT );
+ }
+ if( bCpyHeader )
+ pNewItem->StaticWhichCast(RES_HEADER).RegisterToFormat(*pNewFormat);
+ else
+ pNewItem->StaticWhichCast(RES_FOOTER).RegisterToFormat(*pNewFormat);
+ rDestFormat.SetFormatAttr( *pNewItem );
+}
+
+void SwDoc::CopyPageDesc( const SwPageDesc& rSrcDesc, SwPageDesc& rDstDesc,
+ bool bCopyPoolIds )
+{
+ bool bNotifyLayout = false;
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+
+ rDstDesc.SetLandscape( rSrcDesc.GetLandscape() );
+ rDstDesc.SetNumType( rSrcDesc.GetNumType() );
+ if( rDstDesc.ReadUseOn() != rSrcDesc.ReadUseOn() )
+ {
+ rDstDesc.WriteUseOn( rSrcDesc.ReadUseOn() );
+ bNotifyLayout = true;
+ }
+
+ if( bCopyPoolIds )
+ {
+ rDstDesc.SetPoolFormatId( rSrcDesc.GetPoolFormatId() );
+ rDstDesc.SetPoolHelpId( rSrcDesc.GetPoolHelpId() );
+ // Always set the HelpFile Id to default!
+ rDstDesc.SetPoolHlpFileId( UCHAR_MAX );
+ }
+
+ if( rSrcDesc.GetFollow() != &rSrcDesc )
+ {
+ const SwPageDesc* pSrcFollow = rSrcDesc.GetFollow();
+ SwPageDesc* pFollow = FindPageDesc( pSrcFollow->GetName() );
+ if( !pFollow )
+ {
+ // copy
+ pFollow = MakePageDesc( pSrcFollow->GetName() );
+ CopyPageDesc( *pSrcFollow, *pFollow );
+ }
+ rDstDesc.SetFollow( pFollow );
+ bNotifyLayout = true;
+ }
+
+ // the header and footer attributes are copied separately
+ // the content sections have to be copied in their entirety
+ {
+ SfxItemSet aAttrSet( rSrcDesc.GetMaster().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetMaster().DelDiffs( aAttrSet );
+ rDstDesc.GetMaster().SetFormatAttr( aAttrSet );
+
+ aAttrSet.ClearItem();
+ aAttrSet.Put( rSrcDesc.GetLeft().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetLeft().DelDiffs( aAttrSet );
+ rDstDesc.GetLeft().SetFormatAttr( aAttrSet );
+
+ aAttrSet.ClearItem();
+ aAttrSet.Put( rSrcDesc.GetFirstMaster().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetFirstMaster().DelDiffs( aAttrSet );
+ rDstDesc.GetFirstMaster().SetFormatAttr( aAttrSet );
+
+ aAttrSet.ClearItem();
+ aAttrSet.Put( rSrcDesc.GetFirstLeft().GetAttrSet() );
+ aAttrSet.ClearItem( RES_HEADER );
+ aAttrSet.ClearItem( RES_FOOTER );
+
+ rDstDesc.GetFirstLeft().DelDiffs( aAttrSet );
+ rDstDesc.GetFirstLeft().SetFormatAttr( aAttrSet );
+ }
+
+ CopyHeader( rSrcDesc.GetMaster(), rDstDesc.GetMaster() );
+ CopyFooter( rSrcDesc.GetMaster(), rDstDesc.GetMaster() );
+ if( !rDstDesc.IsHeaderShared() )
+ CopyHeader( rSrcDesc.GetLeft(), rDstDesc.GetLeft() );
+ else
+ rDstDesc.GetLeft().SetFormatAttr( rDstDesc.GetMaster().GetHeader() );
+ if( !rDstDesc.IsFirstShared() )
+ {
+ CopyHeader( rSrcDesc.GetFirstMaster(), rDstDesc.GetFirstMaster() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetFirstMaster().GetHeader());
+ }
+ else
+ {
+ rDstDesc.GetFirstMaster().SetFormatAttr( rDstDesc.GetMaster().GetHeader() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetLeft().GetHeader());
+ }
+
+ if( !rDstDesc.IsFooterShared() )
+ CopyFooter( rSrcDesc.GetLeft(), rDstDesc.GetLeft() );
+ else
+ rDstDesc.GetLeft().SetFormatAttr( rDstDesc.GetMaster().GetFooter() );
+ if( !rDstDesc.IsFirstShared() )
+ {
+ CopyFooter( rSrcDesc.GetFirstMaster(), rDstDesc.GetFirstMaster() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetFirstMaster().GetFooter());
+ }
+ else
+ {
+ rDstDesc.GetFirstMaster().SetFormatAttr( rDstDesc.GetMaster().GetFooter() );
+ rDstDesc.GetFirstLeft().SetFormatAttr(rDstDesc.GetLeft().GetFooter());
+ }
+
+ if( bNotifyLayout && pTmpRoot )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->AllCheckPageDescs();
+ }
+
+ // If foot notes change the pages have to be triggered
+ if( !(rDstDesc.GetFootnoteInfo() == rSrcDesc.GetFootnoteInfo()) )
+ {
+ sw::PageFootnoteHint aHint;
+ rDstDesc.SetFootnoteInfo( rSrcDesc.GetFootnoteInfo() );
+ rDstDesc.GetMaster().CallSwClientNotify(aHint);
+ rDstDesc.GetLeft().CallSwClientNotify(aHint);
+ rDstDesc.GetFirstMaster().CallSwClientNotify(aHint);
+ rDstDesc.GetFirstLeft().CallSwClientNotify(aHint);
+ }
+
+ // Copy the stashed formats as well between the page descriptors...
+ for (bool bFirst : { true, false })
+ for (bool bLeft : { true, false })
+ for (bool bHeader : { true, false })
+ {
+ if (!bLeft && !bFirst)
+ continue;
+ if (auto pStashedFormat = rSrcDesc.GetStashedFrameFormat(bHeader, bLeft, bFirst))
+ rDstDesc.StashFrameFormat(*pStashedFormat, bHeader, bLeft, bFirst);
+ }
+}
+
+void SwDoc::ReplaceStyles( const SwDoc& rSource, bool bIncludePageStyles )
+{
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ CopyFormatArr( *rSource.mpCharFormatTable, *mpCharFormatTable,
+ &SwDoc::MakeCharFormat_, *mpDfltCharFormat );
+ CopyFormatArr( *rSource.mpFrameFormatTable, *mpFrameFormatTable,
+ &SwDoc::MakeFrameFormat_, *mpDfltFrameFormat );
+ CopyFormatArr( *rSource.mpTextFormatCollTable, *mpTextFormatCollTable,
+ &SwDoc::MakeTextFormatColl_, *mpDfltTextFormatColl );
+
+ //To-Do:
+ // a) in rtf export don't export our hideous pgdsctbl
+ // extension to rtf anymore
+ // b) in sd rtf import (View::InsertData) don't use
+ // a super-fragile test for mere presence of \trowd to
+ // indicate import of rtf into a table
+ // c) then drop use of bIncludePageStyles
+ if (bIncludePageStyles)
+ {
+ // and now the page templates
+ SwPageDescs::size_type nCnt = rSource.m_PageDescs.size();
+ if( nCnt )
+ {
+ // a different Doc -> Number formatter needs to be merged
+ SwTableNumFormatMerge aTNFM( rSource, *this );
+
+ // 1st step: Create all formats (skip the 0th - it's the default!)
+ while( nCnt )
+ {
+ const SwPageDesc &rSrc = *rSource.m_PageDescs[ --nCnt ];
+ if( nullptr == FindPageDesc( rSrc.GetName() ) )
+ MakePageDesc( rSrc.GetName() );
+ }
+
+ // 2nd step: Copy all attributes, set the right parents
+ for (SwPageDescs::size_type i = rSource.m_PageDescs.size(); i; )
+ {
+ const SwPageDesc &rSrc = *rSource.m_PageDescs[ --i ];
+ SwPageDesc* pDesc = FindPageDesc( rSrc.GetName() );
+ CopyPageDesc( rSrc, *pDesc);
+ }
+ }
+ }
+
+ // then there are the numbering templates
+ const SwNumRuleTable::size_type nCnt = rSource.GetNumRuleTable().size();
+ if( nCnt )
+ {
+ const SwNumRuleTable& rArr = rSource.GetNumRuleTable();
+ for( SwNumRuleTable::size_type n = 0; n < nCnt; ++n )
+ {
+ const SwNumRule& rR = *rArr[ n ];
+ SwNumRule* pNew = FindNumRulePtr( rR.GetName());
+ if( pNew )
+ pNew->CopyNumRule(*this, rR);
+ else
+ {
+ if( !rR.IsAutoRule() )
+ MakeNumRule( rR.GetName(), &rR );
+ else
+ {
+ // as we reset all styles, there shouldn't be any unknown
+ // automatic SwNumRules, because all should have been
+ // created by the style copying!
+ // So just warn and ignore.
+ SAL_WARN( "sw.core", "Found unknown auto SwNumRule during reset!" );
+ }
+ }
+ }
+ }
+
+ if (undoGuard.UndoWasEnabled())
+ {
+ // nodes array was modified!
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::MoveLeftMargin(const SwPaM& rPam, bool bRight, bool bModulus,
+ SwRootFrame const*const pLayout)
+{
+ SwHistory* pHistory = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndoMoveLeftMargin> pUndo(new SwUndoMoveLeftMargin( rPam, bRight,
+ bModulus ));
+ pHistory = &pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ const SvxTabStopItem& rTabItem = GetDefault( RES_PARATR_TABSTOP );
+ const sal_Int32 nDefDist = rTabItem.Count() ? rTabItem[0].GetTabPos() : 1134;
+ const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End();
+ SwNodeIndex aIdx( rStt.nNode );
+ while( aIdx <= rEnd.nNode )
+ {
+ SwTextNode* pTNd = aIdx.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, aIdx);
+ SvxLRSpaceItem aLS(pTNd->SwContentNode::GetAttr(RES_LR_SPACE));
+
+ // #i93873# See also lcl_MergeListLevelIndentAsLRSpaceItem in thints.cxx
+ if ( pTNd->AreListLevelIndentsApplicable() )
+ {
+ const SwNumRule* pRule = pTNd->GetNumRule();
+ if ( pRule )
+ {
+ const int nListLevel = pTNd->GetActualListLevel();
+ if ( nListLevel >= 0 )
+ {
+ const SwNumFormat& rFormat = pRule->Get(o3tl::narrowing<sal_uInt16>(nListLevel));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ aLS.SetTextLeft( rFormat.GetIndentAt() );
+ aLS.SetTextFirstLineOffset( static_cast<short>(rFormat.GetFirstLineIndent()) );
+ }
+ }
+ }
+ }
+
+ tools::Long nNext = aLS.GetTextLeft();
+ if( bModulus )
+ nNext = ( nNext / nDefDist ) * nDefDist;
+
+ if( bRight )
+ nNext += nDefDist;
+ else
+ if(nNext >0) // fdo#75936 set limit for decreasing indent
+ nNext -= nDefDist;
+
+ aLS.SetTextLeft( nNext );
+
+ SwRegHistory aRegH( pTNd, *pTNd, pHistory );
+ pTNd->SetAttr( aLS );
+ aIdx = *sw::GetFirstAndLastNode(*pLayout, aIdx).second;
+ }
+ ++aIdx;
+ }
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::DontExpandFormat( const SwPosition& rPos, bool bFlag )
+{
+ bool bRet = false;
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ bRet = pTextNd->DontExpandFormat( rPos.nContent, bFlag );
+ if( bRet && GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoDontExpandFormat>(rPos) );
+ }
+ }
+ return bRet;
+}
+
+SwTableBoxFormat* SwDoc::MakeTableBoxFormat()
+{
+ SwTableBoxFormat* pFormat = new SwTableBoxFormat( GetAttrPool(), mpDfltFrameFormat.get() );
+ pFormat->SetName("TableBox" + OUString::number(reinterpret_cast<sal_IntPtr>(pFormat)));
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+SwTableLineFormat* SwDoc::MakeTableLineFormat()
+{
+ SwTableLineFormat* pFormat = new SwTableLineFormat( GetAttrPool(), mpDfltFrameFormat.get() );
+ pFormat->SetName("TableLine" + OUString::number(reinterpret_cast<sal_IntPtr>(pFormat)));
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+void SwDoc::EnsureNumberFormatter()
+{
+ if (mpNumberFormatter == nullptr)
+ {
+ LanguageType eLang = LANGUAGE_SYSTEM;
+ mpNumberFormatter = new SvNumberFormatter(comphelper::getProcessComponentContext(), eLang);
+ mpNumberFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_FORMAT_INTL );
+ if (!utl::ConfigManager::IsFuzzing())
+ mpNumberFormatter->SetYear2000(
+ officecfg::Office::Common::DateFormat::TwoDigitYear::get());
+ };
+}
+
+SwTableNumFormatMerge::SwTableNumFormatMerge( const SwDoc& rSrc, SwDoc& rDest )
+ : pNFormat( nullptr )
+{
+ // a different Doc -> Number formatter needs to be merged
+ if( &rSrc != &rDest )
+ {
+ SvNumberFormatter* pN = const_cast<SwDoc&>(rSrc).GetNumberFormatter( false );
+ if( pN )
+ {
+ pNFormat = rDest.GetNumberFormatter();
+ pNFormat->MergeFormatter( *pN );
+ }
+ }
+
+ if( &rSrc != &rDest )
+ static_cast<SwGetRefFieldType*>(rSrc.getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::GetRef ))->
+ MergeWithOtherDoc( rDest );
+}
+
+SwTableNumFormatMerge::~SwTableNumFormatMerge()
+{
+ if( pNFormat )
+ pNFormat->ClearMergeTable();
+}
+
+void SwDoc::SetTextFormatCollByAutoFormat( const SwPosition& rPos, sal_uInt16 nPoolId,
+ const SfxItemSet* pSet )
+{
+ SwPaM aPam( rPos );
+ SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
+ assert(pTNd);
+
+ if (mbIsAutoFormatRedline)
+ {
+ // create the redline object
+ const SwTextFormatColl& rColl = *pTNd->GetTextColl();
+ SwRangeRedline* pRedl = new SwRangeRedline( RedlineType::FmtColl, aPam );
+ pRedl->SetMark();
+
+ // Only those items that are not set by the Set again in the Node
+ // are of interest. Thus, we take the difference.
+ SwRedlineExtraData_FormatColl aExtraData( rColl.GetName(),
+ rColl.GetPoolFormatId() );
+ if( pSet && pTNd->HasSwAttrSet() )
+ {
+ SfxItemSet aTmp( *pTNd->GetpSwAttrSet() );
+ aTmp.Differentiate( *pSet );
+ // we handle the adjust item separately
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pTNd->GetpSwAttrSet()->GetItemState(
+ RES_PARATR_ADJUST, false, &pItem ))
+ aTmp.Put( *pItem );
+ aExtraData.SetItemSet( aTmp );
+ }
+ pRedl->SetExtraData( &aExtraData );
+
+ //TODO: Undo is still missing!
+ getIDocumentRedlineAccess().AppendRedline( pRedl, true );
+ }
+
+ SetTextFormatColl( aPam, getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId ) );
+
+ if (pSet && pSet->Count())
+ {
+ aPam.SetMark();
+ aPam.GetMark()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+ // sw_redlinehide: don't need layout currently because the only caller
+ // passes in the properties node
+ assert(static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(nullptr))->GetTextNodeForParaProps() == pTNd);
+ getIDocumentContentOperations().InsertItemSet( aPam, *pSet );
+ }
+}
+
+void SwDoc::SetFormatItemByAutoFormat( const SwPaM& rPam, const SfxItemSet& rSet )
+{
+ SwTextNode* pTNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ assert(pTNd);
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+
+ if (mbIsAutoFormatRedline)
+ {
+ // create the redline object
+ SwRangeRedline* pRedl = new SwRangeRedline( RedlineType::Format, rPam );
+ if( !pRedl->HasMark() )
+ pRedl->SetMark();
+
+ // Only those items that are not set by the Set again in the Node
+ // are of interest. Thus, we take the difference.
+ SwRedlineExtraData_Format aExtraData( rSet );
+
+ pRedl->SetExtraData( &aExtraData );
+
+ //TODO: Undo is still missing!
+ getIDocumentRedlineAccess().AppendRedline( pRedl, true );
+
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore );
+ }
+
+ const sal_Int32 nEnd(rPam.End()->nContent.GetIndex());
+ std::vector<WhichPair> whichIds;
+ SfxItemIter iter(rSet);
+ for (SfxPoolItem const* pItem = iter.GetCurItem(); pItem; pItem = iter.NextItem())
+ {
+ whichIds.push_back({pItem->Which(), pItem->Which()});
+ }
+ SfxItemSet currentSet(GetAttrPool(), WhichRangesContainer(whichIds.data(), whichIds.size()));
+ pTNd->GetParaAttr(currentSet, nEnd, nEnd);
+ for (const WhichPair& rPair : whichIds)
+ { // yuk - want to explicitly set the pool defaults too :-/
+ currentSet.Put(currentSet.Get(rPair.first));
+ }
+
+ getIDocumentContentOperations().InsertItemSet( rPam, rSet, SetAttrMode::DONTEXPAND );
+
+ // fdo#62536: DONTEXPAND does not work when there is already an AUTOFMT
+ // here, so insert the old attributes as an empty hint to stop expand
+ SwPaM endPam(*pTNd, nEnd);
+ endPam.SetMark();
+ getIDocumentContentOperations().InsertItemSet(endPam, currentSet);
+
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwDoc::ChgFormat(SwFormat & rFormat, const SfxItemSet & rSet)
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // copying <rSet> to <aSet>
+ SfxItemSet aSet(rSet);
+ // remove from <aSet> all items, which are already set at the format
+ aSet.Differentiate(rFormat.GetAttrSet());
+ // <aSet> contains now all *new* items for the format
+
+ // copying current format item set to <aOldSet>
+ SfxItemSet aOldSet(rFormat.GetAttrSet());
+ // insert new items into <aOldSet>
+ aOldSet.Put(aSet);
+ // invalidate all new items in <aOldSet> in order to clear these items,
+ // if the undo action is triggered.
+ {
+ SfxItemIter aIter(aSet);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ aOldSet.InvalidateItem(pItem->Which());
+ }
+ }
+
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoFormatAttr>(std::move(aOldSet), rFormat, /*bSaveDrawPt*/true));
+ }
+
+ rFormat.SetFormatAttr(rSet);
+}
+
+void SwDoc::RenameFormat(SwFormat & rFormat, const OUString & sNewName,
+ bool bBroadcast)
+{
+ SfxStyleFamily eFamily = SfxStyleFamily::All;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ std::unique_ptr<SwUndo> pUndo;
+
+ switch (rFormat.Which())
+ {
+ case RES_CHRFMT:
+ pUndo.reset(new SwUndoRenameCharFormat(rFormat.GetName(), sNewName, *this));
+ eFamily = SfxStyleFamily::Char;
+ break;
+ case RES_TXTFMTCOLL:
+ pUndo.reset(new SwUndoRenameFormatColl(rFormat.GetName(), sNewName, *this));
+ eFamily = SfxStyleFamily::Para;
+ break;
+ case RES_FRMFMT:
+ pUndo.reset(new SwUndoRenameFrameFormat(rFormat.GetName(), sNewName, *this));
+ eFamily = SfxStyleFamily::Frame;
+ break;
+
+ default:
+ break;
+ }
+
+ if (pUndo)
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ // name change means the o3tl::sorted_array is not property sorted
+ if (rFormat.Which() == RES_CHRFMT)
+ mpCharFormatTable->SetFormatNameAndReindex(static_cast<SwCharFormat*>(&rFormat), sNewName);
+ else
+ rFormat.SetName(sNewName);
+
+ if (bBroadcast)
+ BroadcastStyleOperation(sNewName, eFamily, SfxHintId::StyleSheetModified);
+}
+
+void SwDoc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ bool bOwns = false;
+ if (!pWriter)
+ {
+ pWriter = xmlNewTextWriterFilename("nodes.xml", 0);
+ xmlTextWriterSetIndent(pWriter,1);
+ (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr);
+ bOwns = true;
+ }
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwDoc"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ m_pNodes->dumpAsXml(pWriter);
+ m_PageDescs.dumpAsXml(pWriter);
+ maDBData.dumpAsXml(pWriter);
+ mpMarkManager->dumpAsXml(pWriter);
+ m_pUndoManager->dumpAsXml(pWriter);
+ m_pDocumentSettingManager->dumpAsXml(pWriter);
+ getIDocumentFieldsAccess().GetFieldTypes()->dumpAsXml(pWriter);
+ mpTextFormatCollTable->dumpAsXml(pWriter);
+ mpCharFormatTable->dumpAsXml(pWriter);
+ mpFrameFormatTable->dumpAsXml(pWriter, "frmFormatTable");
+ mpSpzFrameFormatTable->dumpAsXml(pWriter, "spzFrameFormatTable");
+ mpSectionFormatTable->dumpAsXml(pWriter);
+ mpTableFrameFormatTable->dumpAsXml(pWriter, "tableFrameFormatTable");
+ mpNumRuleTable->dumpAsXml(pWriter);
+ getIDocumentRedlineAccess().GetRedlineTable().dumpAsXml(pWriter);
+ getIDocumentRedlineAccess().GetExtraRedlineTable().dumpAsXml(pWriter);
+ if (const SdrModel* pModel = getIDocumentDrawModelAccess().GetDrawModel())
+ pModel->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mbModified"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(getIDocumentState().IsModified()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+ if (bOwns)
+ {
+ (void)xmlTextWriterEndDocument(pWriter);
+ xmlFreeTextWriter(pWriter);
+ }
+}
+
+void SwDBData::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwDBData"));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("sDataSource"), BAD_CAST(sDataSource.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("sCommand"), BAD_CAST(sCommand.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nCommandType"), BAD_CAST(OString::number(nCommandType).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+std::set<Color> SwDoc::GetDocColors()
+{
+ std::set<Color> aDocColors;
+ SwAttrPool& rPool = GetAttrPool();
+ const sal_uInt16 pAttribs[] = {RES_CHRATR_COLOR, RES_CHRATR_HIGHLIGHT, RES_BACKGROUND};
+ for (sal_uInt16 nAttrib : pAttribs)
+ {
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nAttrib))
+ {
+ auto pColorItem = static_cast<const SvxColorItem*>(pItem);
+ Color aColor( pColorItem->GetValue() );
+ if (COL_AUTO != aColor)
+ aDocColors.insert(aColor);
+ }
+ }
+ return aDocColors;
+}
+
+// #i69627#
+namespace docfunc
+{
+ bool HasOutlineStyleToBeWrittenAsNormalListStyle( SwDoc& rDoc )
+ {
+ // If a parent paragraph style of one of the paragraph styles, which
+ // are assigned to the list levels of the outline style, has a list style
+ // set or inherits a list style from its parent style, the outline style
+ // has to be written as a normal list style to the OpenDocument file
+ // format or the OpenOffice.org file format.
+ bool bRet( false );
+
+ const SwTextFormatColls* pTextFormatColls( rDoc.GetTextFormatColls() );
+ if ( pTextFormatColls )
+ {
+ for ( auto pTextFormatColl : *pTextFormatColls )
+ {
+ if ( pTextFormatColl->IsDefault() ||
+ ! pTextFormatColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ continue;
+ }
+
+ const SwTextFormatColl* pParentTextFormatColl =
+ dynamic_cast<const SwTextFormatColl*>( pTextFormatColl->DerivedFrom());
+ if ( !pParentTextFormatColl )
+ continue;
+
+ if ( SfxItemState::SET == pParentTextFormatColl->GetItemState( RES_PARATR_NUMRULE ) )
+ {
+ // #i106218# consider that the outline style is set
+ const SwNumRuleItem& rDirectItem = pParentTextFormatColl->GetNumRule();
+ if ( rDirectItem.GetValue() != rDoc.GetOutlineNumRule()->GetName() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ }
+ return bRet;
+ }
+}
+
+SwFrameFormats::SwFrameFormats()
+ : m_PosIndex( m_Array.get<0>() )
+ , m_TypeAndNameIndex( m_Array.get<1>() )
+{
+}
+
+SwFrameFormats::~SwFrameFormats()
+{
+ DeleteAndDestroyAll();
+}
+
+SwFrameFormats::const_iterator SwFrameFormats::find( const value_type& x ) const
+{
+ ByTypeAndName::iterator it = m_TypeAndNameIndex.find(
+ std::make_tuple(x->GetName(), x->Which(), x) );
+ return m_Array.project<0>( it );
+}
+
+SwFrameFormats::ByTypeAndName::const_iterator
+SwFrameFormats::findByTypeAndName( sal_uInt16 type, const OUString& name ) const
+{
+ return m_TypeAndNameIndex.find( std::make_tuple(name, type) );
+}
+
+std::pair<SwFrameFormats::ByTypeAndName::const_iterator, SwFrameFormats::ByTypeAndName::const_iterator>
+SwFrameFormats::findRangeByName( const OUString& rName ) const
+{
+ auto it = m_TypeAndNameIndex.lower_bound( std::make_tuple(rName, sal_uInt16(0)) );
+ auto itEnd = m_TypeAndNameIndex.upper_bound( std::make_tuple(rName, SAL_MAX_UINT16) );
+ return { it, itEnd };
+}
+
+SwFrameFormat* SwFrameFormats::FindFormatByName( const OUString& rName ) const
+{
+ auto it = m_TypeAndNameIndex.lower_bound( std::make_tuple(rName, sal_uInt16(0)) );
+ if (it != m_TypeAndNameIndex.end() && (*it)->GetName() == rName)
+ return *it;
+ return nullptr;
+}
+
+void SwFrameFormats::DeleteAndDestroyAll( bool keepDefault )
+{
+ if ( empty() )
+ return;
+ const int _offset = keepDefault ? 1 : 0;
+ for( const_iterator it = begin() + _offset; it != end(); ++it )
+ delete *it;
+ if ( _offset )
+ m_PosIndex.erase( begin() + _offset, end() );
+ else
+ m_Array.clear();
+}
+
+std::pair<SwFrameFormats::const_iterator,bool> SwFrameFormats::push_back( const value_type& x )
+{
+ SAL_WARN_IF(x->m_ffList != nullptr, "sw.core", "Inserting already assigned item");
+ assert(x->m_ffList == nullptr);
+ x->m_ffList = this;
+ return m_PosIndex.push_back( x );
+}
+
+bool SwFrameFormats::erase( const value_type& x )
+{
+ const_iterator const ret = find( x );
+ SAL_WARN_IF(x->m_ffList != this, "sw.core", "Removing invalid / unassigned item");
+ if (ret != end()) {
+ assert( x == *ret );
+ m_PosIndex.erase( ret );
+ x->m_ffList = nullptr;
+ return true;
+ }
+ return false;
+}
+
+void SwFrameFormats::erase( size_type index_ )
+{
+ erase( begin() + index_ );
+}
+
+void SwFrameFormats::erase( const_iterator const& position )
+{
+ (*position)->m_ffList = nullptr;
+ m_PosIndex.erase( begin() + (position - begin()) );
+}
+
+bool SwFrameFormats::ContainsFormat(const SwFrameFormat& x) const
+{
+ return (x.m_ffList == this);
+}
+
+bool SwFrameFormats::IsAlive(SwFrameFormat const*const p) const
+{
+ return find(const_cast<SwFrameFormat*>(p)) != end();
+}
+
+bool SwFrameFormats::newDefault( const value_type& x )
+{
+ std::pair<iterator,bool> res = m_PosIndex.push_front( x );
+ if( ! res.second )
+ newDefault( res.first );
+ return res.second;
+}
+
+void SwFrameFormats::newDefault( const_iterator const& position )
+{
+ if (position == begin())
+ return;
+ m_PosIndex.relocate( begin(), position );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docftn.cxx b/sw/source/core/doc/docftn.cxx
new file mode 100644
index 000000000..376994160
--- /dev/null
+++ b/sw/source/core/doc/docftn.cxx
@@ -0,0 +1,547 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <ftnidx.hxx>
+#include <rootfrm.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <pam.hxx>
+#include <pagedesc.hxx>
+#include <charfmt.hxx>
+#include <UndoAttribute.hxx>
+#include <rolbck.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <ftninfo.hxx>
+
+SwEndNoteInfo& SwEndNoteInfo::operator=(const SwEndNoteInfo& rInfo)
+{
+ m_pTextFormatColl = rInfo.m_pTextFormatColl;
+ m_pPageDesc = rInfo.m_pPageDesc;
+ m_pCharFormat = rInfo.m_pCharFormat;
+ m_pAnchorFormat = rInfo.m_pAnchorFormat;
+ m_aDepends.EndListeningAll();
+ m_aDepends.StartListening(m_pTextFormatColl);
+ m_aDepends.StartListening(m_pPageDesc);
+ m_aDepends.StartListening(m_pCharFormat);
+ m_aDepends.StartListening(m_pAnchorFormat);
+
+ m_aFormat = rInfo.m_aFormat;
+ m_nFootnoteOffset = rInfo.m_nFootnoteOffset;
+ m_bEndNote = rInfo.m_bEndNote;
+ m_sPrefix = rInfo.m_sPrefix;
+ m_sSuffix = rInfo.m_sSuffix;
+ return *this;
+}
+
+bool SwEndNoteInfo::operator==( const SwEndNoteInfo& rInfo ) const
+{
+ return
+ m_pTextFormatColl == rInfo.m_pTextFormatColl &&
+ m_pPageDesc == rInfo.m_pPageDesc &&
+ m_pCharFormat == rInfo.m_pCharFormat &&
+ m_pAnchorFormat == rInfo.m_pAnchorFormat &&
+ m_aFormat.GetNumberingType() == rInfo.m_aFormat.GetNumberingType() &&
+ m_nFootnoteOffset == rInfo.m_nFootnoteOffset &&
+ m_bEndNote == rInfo.m_bEndNote &&
+ m_sPrefix == rInfo.m_sPrefix &&
+ m_sSuffix == rInfo.m_sSuffix;
+}
+
+SwEndNoteInfo::SwEndNoteInfo(const SwEndNoteInfo& rInfo) :
+ SwClient(nullptr),
+ m_aDepends(*this),
+ m_pTextFormatColl(rInfo.m_pTextFormatColl),
+ m_pPageDesc(rInfo.m_pPageDesc),
+ m_pCharFormat(rInfo.m_pCharFormat),
+ m_pAnchorFormat(rInfo.m_pAnchorFormat),
+ m_sPrefix( rInfo.m_sPrefix ),
+ m_sSuffix( rInfo.m_sSuffix ),
+ m_bEndNote( true ),
+ m_aFormat( rInfo.m_aFormat ),
+ m_nFootnoteOffset( rInfo.m_nFootnoteOffset )
+{
+ m_aDepends.StartListening(m_pTextFormatColl);
+ m_aDepends.StartListening(m_pPageDesc);
+ m_aDepends.StartListening(m_pCharFormat);
+ m_aDepends.StartListening(m_pAnchorFormat);
+}
+
+SwEndNoteInfo::SwEndNoteInfo() :
+ SwClient(nullptr),
+ m_aDepends(*this),
+ m_pTextFormatColl(nullptr),
+ m_pPageDesc(nullptr),
+ m_pCharFormat(nullptr),
+ m_pAnchorFormat(nullptr),
+ m_bEndNote( true ),
+ m_nFootnoteOffset( 0 )
+{
+ m_aFormat.SetNumberingType(SVX_NUM_ROMAN_LOWER);
+}
+
+SwPageDesc* SwEndNoteInfo::GetPageDesc(SwDoc& rDoc) const
+{
+ if(!m_pPageDesc)
+ {
+ m_pPageDesc = rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( o3tl::narrowing<sal_uInt16>(
+ m_bEndNote ? RES_POOLPAGE_ENDNOTE : RES_POOLPAGE_FOOTNOTE ) );
+ m_aDepends.StartListening(m_pPageDesc);
+ }
+ return m_pPageDesc;
+}
+
+bool SwEndNoteInfo::KnowsPageDesc() const
+{
+ return m_pPageDesc != nullptr;
+}
+
+bool SwEndNoteInfo::DependsOn(const SwPageDesc* pDesc) const
+{
+ return m_pPageDesc == pDesc;
+}
+
+void SwEndNoteInfo::ChgPageDesc(SwPageDesc* pDesc)
+{
+ m_aDepends.EndListening(m_pPageDesc);
+ m_pPageDesc = pDesc;
+ m_aDepends.StartListening(m_pPageDesc);
+}
+
+void SwEndNoteInfo::SetFootnoteTextColl(SwTextFormatColl& rFormat)
+{
+ m_aDepends.EndListening(m_pTextFormatColl);
+ m_pTextFormatColl = &rFormat;
+ m_aDepends.StartListening(m_pTextFormatColl);
+}
+
+SwCharFormat* SwEndNoteInfo::GetCharFormat(SwDoc& rDoc) const
+{
+ auto pCharFormatFromDoc = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( o3tl::narrowing<sal_uInt16>(
+ m_bEndNote ? RES_POOLCHR_ENDNOTE : RES_POOLCHR_FOOTNOTE ) );
+ if (m_pCharFormat != pCharFormatFromDoc)
+ {
+ m_aDepends.EndListening(m_pCharFormat);
+ m_aDepends.StartListening(pCharFormatFromDoc);
+ m_pCharFormat = pCharFormatFromDoc;
+ }
+ return m_pCharFormat;
+}
+
+namespace
+{
+ void lcl_ResetPoolIdForDocAndSync(const sal_uInt16 nId, SwCharFormat* pFormat, const SwEndNoteInfo& rInfo)
+ {
+ auto pDoc = pFormat->GetDoc();
+ if(!pDoc)
+ return;
+ for(auto pDocFormat : *pDoc->GetCharFormats())
+ {
+ if(pDocFormat == pFormat)
+ pDocFormat->SetPoolFormatId(nId);
+ else if(pDocFormat->GetPoolFormatId() == nId)
+ pDocFormat->SetPoolFormatId(0);
+ }
+ rInfo.GetCharFormat(*pDoc);
+ rInfo.GetAnchorCharFormat(*pDoc);
+ }
+}
+
+void SwEndNoteInfo::SetCharFormat(SwCharFormat* pFormat)
+{
+ lcl_ResetPoolIdForDocAndSync(
+ o3tl::narrowing<sal_uInt16>(m_bEndNote
+ ? RES_POOLCHR_ENDNOTE
+ : RES_POOLCHR_FOOTNOTE),
+ pFormat,
+ *this);
+}
+
+SwCharFormat* SwEndNoteInfo::GetAnchorCharFormat(SwDoc& rDoc) const
+{
+ auto pAnchorFormatFromDoc = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( o3tl::narrowing<sal_uInt16>(
+ m_bEndNote ? RES_POOLCHR_ENDNOTE_ANCHOR : RES_POOLCHR_FOOTNOTE_ANCHOR ) );
+ if(m_pAnchorFormat != pAnchorFormatFromDoc)
+ {
+ m_aDepends.EndListening(m_pAnchorFormat);
+ m_aDepends.StartListening(pAnchorFormatFromDoc);
+ m_pAnchorFormat = pAnchorFormatFromDoc;
+ }
+ return m_pAnchorFormat;
+}
+
+void SwEndNoteInfo::SetAnchorCharFormat(SwCharFormat* pFormat)
+{
+ lcl_ResetPoolIdForDocAndSync(
+ o3tl::narrowing<sal_uInt16>(m_bEndNote
+ ? RES_POOLCHR_ENDNOTE_ANCHOR
+ : RES_POOLCHR_FOOTNOTE_ANCHOR),
+ pFormat,
+ *this);
+}
+
+SwCharFormat* SwEndNoteInfo::GetCurrentCharFormat(const bool bAnchor) const
+{
+ return bAnchor
+ ? m_pAnchorFormat
+ : m_pCharFormat;
+}
+
+void SwEndNoteInfo::UpdateFormatOrAttr()
+{
+ auto pFormat = GetCurrentCharFormat(m_pCharFormat == nullptr);
+ if (!pFormat || !m_aDepends.IsListeningTo(pFormat) || pFormat->IsFormatInDTOR())
+ return;
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFootnoteIdxs& rFootnoteIdxs = pDoc->GetFootnoteIdxs();
+ for(auto pTextFootnote : rFootnoteIdxs)
+ {
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if(rFootnote.IsEndNote() == m_bEndNote)
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+}
+
+
+void SwEndNoteInfo::SwClientNotify( const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ switch(pLegacyHint->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ case RES_FMT_CHG:
+ UpdateFormatOrAttr();
+ break;
+ default:
+ CheckRegistration( pLegacyHint->m_pOld );
+ }
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ auto pNew = const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(pModifyChangedHint->m_pNew));
+ if(m_pAnchorFormat == &rModify)
+ m_pAnchorFormat = static_cast<SwCharFormat*>(pNew);
+ else if(m_pCharFormat == &rModify)
+ m_pCharFormat = static_cast<SwCharFormat*>(pNew);
+ else if(m_pPageDesc == &rModify)
+ m_pPageDesc = static_cast<SwPageDesc*>(pNew);
+ else if(m_pTextFormatColl == &rModify)
+ m_pTextFormatColl = static_cast<SwTextFormatColl*>(pNew);
+ }
+}
+
+SwFootnoteInfo& SwFootnoteInfo::operator=(const SwFootnoteInfo& rInfo)
+{
+ SwEndNoteInfo::operator=(rInfo);
+ m_aQuoVadis = rInfo.m_aQuoVadis;
+ m_aErgoSum = rInfo.m_aErgoSum;
+ m_ePos = rInfo.m_ePos;
+ m_eNum = rInfo.m_eNum;
+ return *this;
+}
+
+bool SwFootnoteInfo::operator==( const SwFootnoteInfo& rInfo ) const
+{
+ return m_ePos == rInfo.m_ePos &&
+ m_eNum == rInfo.m_eNum &&
+ SwEndNoteInfo::operator==(rInfo) &&
+ m_aQuoVadis == rInfo.m_aQuoVadis &&
+ m_aErgoSum == rInfo.m_aErgoSum;
+}
+
+SwFootnoteInfo::SwFootnoteInfo(const SwFootnoteInfo& rInfo) :
+ SwEndNoteInfo( rInfo ),
+ m_aQuoVadis( rInfo.m_aQuoVadis ),
+ m_aErgoSum( rInfo.m_aErgoSum ),
+ m_ePos( rInfo.m_ePos ),
+ m_eNum( rInfo.m_eNum )
+{
+ m_bEndNote = false;
+}
+
+SwFootnoteInfo::SwFootnoteInfo() :
+ m_ePos( FTNPOS_PAGE ),
+ m_eNum( FTNNUM_DOC )
+{
+ m_aFormat.SetNumberingType(SVX_NUM_ARABIC);
+ m_bEndNote = false;
+}
+
+void SwDoc::SetFootnoteInfo(const SwFootnoteInfo& rInfo)
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if( GetFootnoteInfo() == rInfo )
+ return;
+
+ const SwFootnoteInfo &rOld = GetFootnoteInfo();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoFootNoteInfo>(rOld, *this) );
+ }
+
+ bool bFootnotePos = rInfo.m_ePos != rOld.m_ePos;
+ bool bFootnoteDesc = rOld.m_ePos == FTNPOS_CHAPTER &&
+ rInfo.GetPageDesc( *this ) != rOld.GetPageDesc( *this );
+ bool bExtra = rInfo.m_aQuoVadis != rOld.m_aQuoVadis ||
+ rInfo.m_aErgoSum != rOld.m_aErgoSum ||
+ rInfo.m_aFormat.GetNumberingType() != rOld.m_aFormat.GetNumberingType() ||
+ rInfo.GetPrefix() != rOld.GetPrefix() ||
+ rInfo.GetSuffix() != rOld.GetSuffix();
+ SwCharFormat *pOldChrFormat = rOld.GetCharFormat( *this ),
+ *pNewChrFormat = rInfo.GetCharFormat( *this );
+ bool bFootnoteChrFormats = pOldChrFormat != pNewChrFormat;
+
+ *mpFootnoteInfo = rInfo;
+
+ if (pTmpRoot)
+ {
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = GetAllLayouts();
+ if ( bFootnotePos )
+ for( auto aLayout : aAllLayouts )
+ aLayout->AllRemoveFootnotes();
+ else
+ {
+ for( auto aLayout : aAllLayouts )
+ aLayout->UpdateFootnoteNums();
+ if ( bFootnoteDesc )
+ for( auto aLayout : aAllLayouts )
+ aLayout->CheckFootnotePageDescs(false);
+ if ( bExtra )
+ {
+ // For messages regarding ErgoSum etc. we save the extra code and use the
+ // available methods.
+ SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
+ for( size_t nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
+ {
+ SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if ( !rFootnote.IsEndNote() )
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+ }
+ }
+ }
+ if( FTNNUM_PAGE != rInfo.m_eNum )
+ GetFootnoteIdxs().UpdateAllFootnote();
+ else if( bFootnoteChrFormats )
+ {
+ mpFootnoteInfo->UpdateFormatOrAttr();
+ }
+
+ // #i81002# no update during loading
+ if ( !IsInReading() )
+ {
+ getIDocumentFieldsAccess().UpdateRefFields();
+ }
+ getIDocumentState().SetModified();
+
+}
+
+void SwDoc::SetEndNoteInfo(const SwEndNoteInfo& rInfo)
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if( GetEndNoteInfo() == rInfo )
+ return;
+
+ if(GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoEndNoteInfo>( GetEndNoteInfo(), *this ) );
+ }
+
+ bool bNumChg = rInfo.m_nFootnoteOffset != GetEndNoteInfo().m_nFootnoteOffset;
+ // this seems to be an optimization: UpdateAllFootnote() is only called
+ // if the offset changes; if the offset is the same,
+ // but type/prefix/suffix changes, just set new numbers.
+ bool const bExtra = !bNumChg &&
+ ( (rInfo.m_aFormat.GetNumberingType() !=
+ GetEndNoteInfo().m_aFormat.GetNumberingType())
+ || (rInfo.GetPrefix() != GetEndNoteInfo().GetPrefix())
+ || (rInfo.GetSuffix() != GetEndNoteInfo().GetSuffix())
+ );
+ bool bFootnoteDesc = rInfo.GetPageDesc( *this ) !=
+ GetEndNoteInfo().GetPageDesc( *this );
+ SwCharFormat *pOldChrFormat = GetEndNoteInfo().GetCharFormat( *this ),
+ *pNewChrFormat = rInfo.GetCharFormat( *this );
+ bool bFootnoteChrFormats = pOldChrFormat != pNewChrFormat;
+
+ *mpEndNoteInfo = rInfo;
+
+ if ( pTmpRoot )
+ {
+ if ( bFootnoteDesc )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->CheckFootnotePageDescs(true);
+ }
+ if ( bExtra )
+ {
+ // For messages regarding ErgoSum etc. we save the extra code and use the
+ // available methods.
+ SwFootnoteIdxs& rFootnoteIdxs = GetFootnoteIdxs();
+ for( size_t nPos = 0; nPos < rFootnoteIdxs.size(); ++nPos )
+ {
+ SwTextFootnote *pTextFootnote = rFootnoteIdxs[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if ( rFootnote.IsEndNote() )
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rFootnote.GetNumStr());
+ }
+ }
+ }
+ if( bNumChg )
+ GetFootnoteIdxs().UpdateAllFootnote();
+ else if( bFootnoteChrFormats )
+ {
+ mpEndNoteInfo->UpdateFormatOrAttr();
+ }
+
+ // #i81002# no update during loading
+ if ( !IsInReading() )
+ {
+ getIDocumentFieldsAccess().UpdateRefFields();
+ }
+ getIDocumentState().SetModified();
+
+}
+
+bool SwDoc::SetCurFootnote( const SwPaM& rPam, const OUString& rNumStr,
+ bool bIsEndNote)
+{
+ SwFootnoteIdxs& rFootnoteArr = GetFootnoteIdxs();
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+
+ const SwPosition* pStt = rPam.Start(), *pEnd = rPam.End();
+ const SwNodeOffset nSttNd = pStt->nNode.GetIndex();
+ const sal_Int32 nSttCnt = pStt->nContent.GetIndex();
+ const SwNodeOffset nEndNd = pEnd->nNode.GetIndex();
+ const sal_Int32 nEndCnt = pEnd->nContent.GetIndex();
+
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( pStt->nNode, &nPos );
+
+ std::unique_ptr<SwUndoChangeFootNote> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
+ pUndo.reset(new SwUndoChangeFootNote( rPam, rNumStr, bIsEndNote ));
+ }
+
+ bool bChg = false;
+ bool bTypeChgd = false;
+ const size_t nPosSave = nPos;
+ while( nPos < rFootnoteArr.size() )
+ {
+ SwTextFootnote* pTextFootnote = rFootnoteArr[ nPos++ ];
+ SwNodeOffset nIdx = SwTextFootnote_GetIndex(pTextFootnote);
+ if( nIdx >= nEndNd &&
+ ( nIdx != nEndNd || nEndCnt < pTextFootnote->GetStart() ) )
+ continue;
+ if( nIdx > nSttNd || ( nIdx == nSttNd &&
+ nSttCnt <= pTextFootnote->GetStart() ) )
+ {
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr() != rNumStr ||
+ rFootnote.IsEndNote() != bIsEndNote )
+ {
+ bChg = true;
+ if ( pUndo )
+ {
+ pUndo->GetHistory().Add( *pTextFootnote );
+ }
+
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rNumStr);
+ if( rFootnote.IsEndNote() != bIsEndNote )
+ {
+ const_cast<SwFormatFootnote&>(rFootnote).SetEndNote( bIsEndNote );
+ bTypeChgd = true;
+ pTextFootnote->CheckCondColl();
+ //#i11339# dispose UNO wrapper when a footnote is changed to an endnote or vice versa
+ const_cast<SwFormatFootnote&>(rFootnote).InvalidateFootnote();
+ }
+ }
+ }
+ }
+
+ nPos = nPosSave; // There are more in the front!
+ while( nPos )
+ {
+ SwTextFootnote* pTextFootnote = rFootnoteArr[ --nPos ];
+ SwNodeOffset nIdx = SwTextFootnote_GetIndex(pTextFootnote);
+ if( nIdx <= nSttNd &&
+ ( nIdx != nSttNd || nSttCnt > pTextFootnote->GetStart() ) )
+ continue;
+ if( nIdx < nEndNd || ( nIdx == nEndNd &&
+ nEndCnt >= pTextFootnote->GetStart() ) )
+ {
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr() != rNumStr ||
+ rFootnote.IsEndNote() != bIsEndNote )
+ {
+ bChg = true;
+ if ( pUndo )
+ {
+ pUndo->GetHistory().Add( *pTextFootnote );
+ }
+
+ pTextFootnote->SetNumber(rFootnote.GetNumber(), rFootnote.GetNumberRLHidden(), rNumStr);
+ if( rFootnote.IsEndNote() != bIsEndNote )
+ {
+ const_cast<SwFormatFootnote&>(rFootnote).SetEndNote( bIsEndNote );
+ bTypeChgd = true;
+ pTextFootnote->CheckCondColl();
+ }
+ }
+ }
+ }
+
+ // Who needs to be triggered?
+ if( bChg )
+ {
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ if ( bTypeChgd )
+ rFootnoteArr.UpdateAllFootnote();
+ if( FTNNUM_PAGE != GetFootnoteInfo().m_eNum )
+ {
+ if ( !bTypeChgd )
+ rFootnoteArr.UpdateAllFootnote();
+ }
+ else if( pTmpRoot )
+ {
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->UpdateFootnoteNums();
+ }
+ getIDocumentState().SetModified();
+ }
+ return bChg;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docglbl.cxx b/sw/source/core/doc/docglbl.cxx
new file mode 100644
index 000000000..3f5546f9f
--- /dev/null
+++ b/sw/source/core/doc/docglbl.cxx
@@ -0,0 +1,518 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <osl/diagnose.h>
+#include <unotools/tempfile.hxx>
+#include <svl/stritem.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <tools/datetime.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtanchr.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <section.hxx>
+#include <calbck.hxx>
+#include <iodetect.hxx>
+#include <frameformats.hxx>
+#include <memory>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+enum SwSplitDocType
+{
+ SPLITDOC_TO_GLOBALDOC,
+ SPLITDOC_TO_HTML
+};
+
+}
+
+bool SwDoc::GenerateGlobalDoc( const OUString& rPath,
+ const SwTextFormatColl* pSplitColl )
+{
+ return SplitDoc( SPLITDOC_TO_GLOBALDOC, rPath, false, pSplitColl );
+}
+
+bool SwDoc::GenerateGlobalDoc( const OUString& rPath, int nOutlineLevel )
+{
+ return SplitDoc( SPLITDOC_TO_GLOBALDOC, rPath, true, nullptr, nOutlineLevel );
+}
+
+bool SwDoc::GenerateHTMLDoc( const OUString& rPath, int nOutlineLevel )
+{
+ return SplitDoc( SPLITDOC_TO_HTML, rPath, true, nullptr, nOutlineLevel );
+}
+
+bool SwDoc::GenerateHTMLDoc( const OUString& rPath,
+ const SwTextFormatColl* pSplitColl )
+{
+ return SplitDoc( SPLITDOC_TO_HTML, rPath, false, pSplitColl );
+}
+
+// two helpers for outline mode
+static SwNode* GetStartNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl )
+{
+ for( ; *nOutl < pOutlNds->size(); ++(*nOutl) )
+ {
+ SwNode* pNd = (*pOutlNds)[ *nOutl ];
+ if( pNd->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel && !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+
+ return nullptr;
+}
+
+static SwNode* GetEndNode( SwOutlineNodes const * pOutlNds, int nOutlineLevel, SwOutlineNodes::size_type* nOutl )
+{
+ SwNode* pNd;
+
+ for( ++(*nOutl); (*nOutl) < pOutlNds->size(); ++(*nOutl) )
+ {
+ pNd = (*pOutlNds)[ *nOutl ];
+
+ const int nLevel = pNd->GetTextNode()->GetAttrOutlineLevel();
+
+ if( ( 0 < nLevel && nLevel <= nOutlineLevel ) &&
+ !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+ return nullptr;
+}
+
+// two helpers for collection mode
+static SwNode* GetStartNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl )
+{
+ for( ; *nOutl < pOutlNds->size(); ++(*nOutl) )
+ {
+ SwNode* pNd = (*pOutlNds)[ *nOutl ];
+ if( pNd->GetTextNode()->GetTextColl() == pSplitColl &&
+ !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+ return nullptr;
+}
+
+static SwNode* GetEndNode( const SwOutlineNodes* pOutlNds, const SwTextFormatColl* pSplitColl, SwOutlineNodes::size_type* nOutl )
+{
+ SwNode* pNd;
+
+ for( ++(*nOutl); *nOutl < pOutlNds->size(); ++(*nOutl) )
+ {
+ pNd = (*pOutlNds)[ *nOutl ];
+ SwTextFormatColl* pTColl = pNd->GetTextNode()->GetTextColl();
+
+ if( ( pTColl == pSplitColl ||
+ ( pSplitColl->GetAttrOutlineLevel() > 0 &&
+ pTColl->GetAttrOutlineLevel() > 0 &&
+ pTColl->GetAttrOutlineLevel() <
+ pSplitColl->GetAttrOutlineLevel() )) &&
+ !pNd->FindTableNode() )
+ {
+ return pNd;
+ }
+ }
+ return nullptr;
+}
+
+bool SwDoc::SplitDoc( sal_uInt16 eDocType, const OUString& rPath, bool bOutline, const SwTextFormatColl* pSplitColl, int nOutlineLevel )
+{
+ // Iterate over all the template's Nodes, creating an own
+ // document for every single one and replace linked sections (GlobalDoc) for links (HTML).
+ // Finally, we save this document as a GlobalDoc/HTMLDoc.
+ if( !mpDocShell || !mpDocShell->GetMedium() ||
+ ( SPLITDOC_TO_GLOBALDOC == eDocType && GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT) ) )
+ return false;
+
+ SwOutlineNodes::size_type nOutl = 0;
+ SwOutlineNodes* pOutlNds = const_cast<SwOutlineNodes*>(&GetNodes().GetOutLineNds());
+ std::unique_ptr<SwOutlineNodes> xTmpOutlNds;
+ SwNode* pStartNd;
+
+ if ( !bOutline) {
+ if( pSplitColl )
+ {
+ // If it isn't an OutlineNumbering, then use an own array and collect the Nodes.
+ if( pSplitColl->GetAttrOutlineLevel() == 0 )
+ {
+ xTmpOutlNds.reset(new SwOutlineNodes);
+ pOutlNds = xTmpOutlNds.get();
+ SwIterator<SwTextNode,SwFormatColl> aIter( *pSplitColl );
+ for( SwTextNode* pTNd = aIter.First(); pTNd; pTNd = aIter.Next() )
+ if( pTNd->GetNodes().IsDocNodes() )
+ pOutlNds->insert( pTNd );
+
+ if( pOutlNds->empty() )
+ return false;
+ }
+ }
+ else
+ {
+ // Look for the 1st level OutlineTemplate
+ const SwTextFormatColls& rFormatColls =*GetTextFormatColls();
+ for( SwTextFormatColls::size_type n = rFormatColls.size(); n; )
+ if ( rFormatColls[ --n ]->GetAttrOutlineLevel() == 1 )
+ {
+ pSplitColl = rFormatColls[ n ];
+ break;
+ }
+
+ if( !pSplitColl )
+ return false;
+ }
+ }
+
+ std::shared_ptr<const SfxFilter> pFilter;
+ switch( eDocType )
+ {
+ case SPLITDOC_TO_HTML:
+ pFilter = SwIoSystem::GetFilterOfFormat(u"HTML");
+ break;
+
+ default:
+ pFilter = SwIoSystem::GetFilterOfFormat(FILTER_XML);
+ eDocType = SPLITDOC_TO_GLOBALDOC;
+ break;
+ }
+
+ if( !pFilter )
+ return false;
+
+ // Deactivate Undo/Redline in any case
+ GetIDocumentUndoRedo().DoUndo(false);
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( getIDocumentRedlineAccess().GetRedlineFlags() & ~RedlineFlags::On );
+
+ OUString sExt = pFilter->GetSuffixes().getToken(0, ',');
+ if( sExt.isEmpty() )
+ {
+ sExt = ".sxw";
+ }
+ else
+ {
+ if( '.' != sExt[ 0 ] )
+ {
+ sExt = "." + sExt;
+ }
+ }
+
+ INetURLObject aEntry(rPath);
+ OUString sLeading(aEntry.GetBase());
+ aEntry.removeSegment();
+ OUString sPath = aEntry.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ utl::TempFile aTemp(sLeading, true, &sExt, &sPath);
+ aTemp.EnableKillingFile();
+
+ DateTime aTmplDate( DateTime::SYSTEM );
+ {
+ tools::Time a2Min( 0 ); a2Min.SetMin( 2 );
+ aTmplDate += a2Min;
+ }
+
+ // Skip all invalid ones
+ while( nOutl < pOutlNds->size() &&
+ (*pOutlNds)[ nOutl ]->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
+ ++nOutl;
+
+ do {
+ if( bOutline )
+ pStartNd = GetStartNode( pOutlNds, nOutlineLevel, &nOutl );
+ else
+ pStartNd = GetStartNode( pOutlNds, pSplitColl, &nOutl );
+
+ if( pStartNd )
+ {
+ SwNode* pEndNd;
+ if( bOutline )
+ pEndNd = GetEndNode( pOutlNds, nOutlineLevel, &nOutl );
+ else
+ pEndNd = GetEndNode( pOutlNds, pSplitColl, &nOutl );
+ SwNodeIndex aEndIdx( pEndNd ? *pEndNd
+ : GetNodes().GetEndOfContent() );
+
+ // Write out the Nodes completely
+ OUString sFileName;
+ if( pStartNd->GetIndex() + 1 < aEndIdx.GetIndex() )
+ {
+ SfxObjectShellLock xDocSh( new SwDocShell( SfxObjectCreateMode::INTERNAL ));
+ if( xDocSh->DoInitNew() )
+ {
+ SwDoc* pDoc = static_cast<SwDocShell*>(&xDocSh)->GetDoc();
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ static_cast<SwDocShell*>(&xDocSh)->GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+ // the GlobalDoc is the template
+ xDocProps->setTemplateName(OUString());
+ ::util::DateTime uDT = aTmplDate.GetUNODateTime();
+ xDocProps->setTemplateDate(uDT);
+ xDocProps->setTemplateURL(rPath);
+ // Set the new doc's title to the text of the "split para".
+ // If the current doc has a title, insert it at the begin.
+ OUString sTitle( xDocProps->getTitle() );
+ if (!sTitle.isEmpty())
+ sTitle += ": ";
+ sTitle += pStartNd->GetTextNode()->GetExpandText(nullptr);
+ xDocProps->setTitle( sTitle );
+
+ // Replace template
+ pDoc->ReplaceStyles( *this );
+
+ // Take over chapter numbering
+ if( mpOutlineRule )
+ pDoc->SetOutlineNumRule( *mpOutlineRule );
+
+ SwNodeRange aRg( *pStartNd, SwNodeOffset(0), aEndIdx.GetNode() );
+ SwNodeIndex aTmpIdx( pDoc->GetNodes().GetEndOfContent() );
+ GetDocumentContentOperationsManager().CopyWithFlyInFly(
+ aRg, aTmpIdx, nullptr, false, false);
+
+ // Delete the initial TextNode
+ SwNodeIndex aIdx( pDoc->GetNodes().GetEndOfExtras(), 2 );
+ if( aIdx.GetIndex() + 1 !=
+ pDoc->GetNodes().GetEndOfContent().GetIndex() )
+ pDoc->GetNodes().Delete( aIdx );
+
+ utl::TempFile aTempFile2(sLeading, true, &sExt, &sPath);
+ sFileName = aTempFile2.GetURL();
+ SfxMedium* pTmpMed = new SfxMedium( sFileName,
+ StreamMode::STD_READWRITE );
+ pTmpMed->SetFilter( pFilter );
+
+ // We need to have a Layout for the HTMLFilter, so that
+ // TextFrames/Controls/OLE objects can be exported correctly as graphics.
+ if( SPLITDOC_TO_HTML == eDocType &&
+ !pDoc->GetSpzFrameFormats()->empty() )
+ {
+ SfxViewFrame::LoadHiddenDocument( *xDocSh, SFX_INTERFACE_NONE );
+ }
+ xDocSh->DoSaveAs( *pTmpMed );
+ xDocSh->DoSaveCompleted( pTmpMed );
+
+ // do not insert a FileLinkSection in case of error
+ if( xDocSh->GetError() )
+ sFileName.clear();
+ }
+ xDocSh->DoClose();
+ }
+
+ // We can now insert the section
+ if( !sFileName.isEmpty() )
+ {
+ switch( eDocType )
+ {
+ case SPLITDOC_TO_HTML:
+ {
+ // Delete all nodes in the section and, in the "start node",
+ // set the Link to the saved document.
+ SwNodeOffset nNodeDiff = aEndIdx.GetIndex() -
+ pStartNd->GetIndex() - 1;
+ if( nNodeDiff )
+ {
+ SwPaM aTmp( *pStartNd, aEndIdx.GetNode(), SwNodeOffset(1), SwNodeOffset(-1) );
+ aTmp.GetPoint()->nContent.Assign( nullptr, 0 );
+ aTmp.GetMark()->nContent.Assign( nullptr, 0 );
+ SwNodeIndex aSIdx( aTmp.GetMark()->nNode );
+ SwNodeIndex aEIdx( aTmp.GetPoint()->nNode );
+
+ // Try to move past the end
+ if( !aTmp.Move( fnMoveForward, GoInNode ) )
+ {
+ // well then, back to the beginning
+ aTmp.Exchange();
+ if( !aTmp.Move( fnMoveBackward, GoInNode ))
+ {
+ OSL_FAIL( "no more Nodes!" );
+ }
+ }
+ // Move Bookmarks and so forth
+ CorrAbs( aSIdx, aEIdx, *aTmp.GetPoint(), true);
+
+ // If FlyFrames are still around, delete these too
+ for( SwFrameFormats::size_type n = 0; n < GetSpzFrameFormats()->size(); ++n )
+ {
+ SwFrameFormat* pFly = (*GetSpzFrameFormats())[n];
+ const SwFormatAnchor* pAnchor = &pFly->GetAnchor();
+ SwPosition const*const pAPos =
+ pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ aSIdx <= pAPos->nNode &&
+ pAPos->nNode < aEIdx )
+ {
+ getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ --n;
+ }
+ }
+
+ GetNodes().Delete( aSIdx, nNodeDiff );
+ }
+
+ // set the link in the StartNode
+ SwFormatINetFormat aINet( sFileName , OUString() );
+ SwTextNode* pTNd = pStartNd->GetTextNode();
+ pTNd->InsertItem(aINet, 0, pTNd->GetText().getLength());
+
+ // If the link cannot be found anymore,
+ // it has to be a bug!
+ if( !pOutlNds->Seek_Entry( pStartNd, &nOutl ))
+ pStartNd = nullptr;
+ ++nOutl ;
+ }
+ break;
+
+ default:
+ {
+ const OUString sNm(INetURLObject(sFileName).GetLastName());
+ SwSectionData aSectData( SectionType::FileLink,
+ GetUniqueSectionName( &sNm ));
+ SwSectionFormat* pFormat = MakeSectionFormat();
+ aSectData.SetLinkFileName(sFileName);
+ aSectData.SetProtectFlag(true);
+
+ --aEndIdx; // in the InsertSection the end is inclusive
+ while( aEndIdx.GetNode().IsStartNode() )
+ --aEndIdx;
+
+ // If any Section ends or starts in the new sectionrange,
+ // they must end or start before or after the range!
+ SwSectionNode* pSectNd = pStartNd->FindSectionNode();
+ while( pSectNd && pSectNd->EndOfSectionIndex()
+ <= aEndIdx.GetIndex() )
+ {
+ const SwNode* pSectEnd = pSectNd->EndOfSectionNode();
+ if( pSectNd->GetIndex() + 1 ==
+ pStartNd->GetIndex() )
+ {
+ bool bMvIdx = aEndIdx == *pSectEnd;
+ DelSectionFormat( pSectNd->GetSection().GetFormat() );
+ if( bMvIdx )
+ --aEndIdx;
+ }
+ else
+ {
+ SwNodeRange aRg( *pStartNd, *pSectEnd );
+ SwNodeIndex aIdx( *pSectEnd, 1 );
+ GetNodes().MoveNodes( aRg, GetNodes(), aIdx );
+ }
+ pSectNd = pStartNd->FindSectionNode();
+ }
+
+ pSectNd = aEndIdx.GetNode().FindSectionNode();
+ while( pSectNd && pSectNd->GetIndex() >
+ pStartNd->GetIndex() )
+ {
+ // #i15712# don't attempt to split sections if
+ // they are fully enclosed in [pSectNd,aEndIdx].
+ if( aEndIdx < pSectNd->EndOfSectionIndex() )
+ {
+ SwNodeRange aRg( *pSectNd, SwNodeOffset(1), aEndIdx, SwNodeOffset(1) );
+ SwNodeIndex aIdx( *pSectNd );
+ GetNodes().MoveNodes( aRg, GetNodes(), aIdx );
+ }
+
+ pSectNd = pStartNd->FindSectionNode();
+ }
+
+ // -> #i26762#
+ // Ensure order of start and end of section is sane.
+ SwNodeIndex aStartIdx(*pStartNd);
+
+ if (aEndIdx >= aStartIdx)
+ {
+ pSectNd = GetNodes().InsertTextSection(aStartIdx,
+ *pFormat, aSectData, nullptr, &aEndIdx, false);
+ }
+ else
+ {
+ pSectNd = GetNodes().InsertTextSection(aEndIdx,
+ *pFormat, aSectData, nullptr, &aStartIdx, false);
+ }
+ // <- #i26762#
+
+ pSectNd->GetSection().CreateLink( LinkCreateType::Connect );
+ }
+ break;
+ }
+ }
+ }
+ } while( pStartNd );
+
+ xTmpOutlNds.reset();
+
+ switch( eDocType )
+ {
+ case SPLITDOC_TO_HTML:
+ if( GetDocumentSettingManager().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ {
+ // save all remaining sections
+ while( !GetSections().empty() )
+ DelSectionFormat( GetSections().front() );
+
+ SfxFilterContainer* pFCntnr = mpDocShell->GetFactory().GetFilterContainer();
+ pFilter = pFCntnr->GetFilter4EA( pFilter->GetTypeName(), SfxFilterFlags::EXPORT );
+ }
+ break;
+
+ default:
+ // save the Globaldoc
+ GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT, true);
+ GetDocumentSettingManager().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, false);
+ }
+
+ // The medium isn't locked after reopening the document.
+ SfxRequest aReq( SID_SAVEASDOC, SfxCallMode::SYNCHRON, GetAttrPool() );
+ aReq.AppendItem( SfxStringItem( SID_FILE_NAME, rPath ) );
+ aReq.AppendItem( SfxBoolItem( SID_SAVETO, true ) );
+ if(pFilter)
+ aReq.AppendItem( SfxStringItem( SID_FILTER_NAME, pFilter->GetName() ) );
+ const SfxBoolItem *pRet = static_cast<const SfxBoolItem*>(mpDocShell->ExecuteSlot( aReq ));
+
+ return pRet && pRet->GetValue();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docglos.cxx b/sw/source/core/doc/docglos.cxx
new file mode 100644
index 000000000..15a54a63a
--- /dev/null
+++ b/sw/source/core/doc/docglos.cxx
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <osl/diagnose.h>
+
+#include <doc.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <shellio.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <acorrect.hxx>
+#include <crsrsh.hxx>
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+
+void SwDoc::ReplaceUserDefinedDocumentProperties(
+ const uno::Reference<document::XDocumentProperties>& xSourceDocProps)
+{
+ OSL_ENSURE(xSourceDocProps.is(), "null reference");
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetDocShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties() );
+ OSL_ENSURE(xDocProps.is(), "null reference");
+
+ uno::Reference<beans::XPropertySet> xSourceUDSet(
+ xSourceDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertyContainer> xTargetUD(
+ xDocProps->getUserDefinedProperties());
+ uno::Reference<beans::XPropertySet> xTargetUDSet(xTargetUD,
+ uno::UNO_QUERY_THROW);
+ const uno::Sequence<beans::Property> tgtprops
+ = xTargetUDSet->getPropertySetInfo()->getProperties();
+
+ for (const auto& rTgtProp : tgtprops) {
+ try {
+ xTargetUD->removeProperty(rTgtProp.Name);
+ } catch (uno::Exception &) {
+ // ignore
+ }
+ }
+
+ uno::Reference<beans::XPropertySetInfo> xSetInfo
+ = xSourceUDSet->getPropertySetInfo();
+ const uno::Sequence<beans::Property> srcprops = xSetInfo->getProperties();
+
+ for (const auto& rSrcProp : srcprops) {
+ try {
+ OUString name = rSrcProp.Name;
+ xTargetUD->addProperty(name, rSrcProp.Attributes,
+ xSourceUDSet->getPropertyValue(name));
+ } catch (uno::Exception &) {
+ // ignore
+ }
+ }
+}
+
+void SwDoc::ReplaceDocumentProperties(const SwDoc& rSource, bool mailMerge)
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xSourceDPS(
+ rSource.GetDocShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xSourceDocProps(
+ xSourceDPS->getDocumentProperties() );
+ OSL_ENSURE(xSourceDocProps.is(), "null reference");
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetDocShell()->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties() );
+ OSL_ENSURE(xDocProps.is(), "null reference");
+
+ xDocProps->setAuthor(xSourceDocProps->getAuthor());
+ xDocProps->setGenerator(xSourceDocProps->getGenerator());
+ xDocProps->setCreationDate(xSourceDocProps->getCreationDate());
+ xDocProps->setTitle(xSourceDocProps->getTitle());
+ xDocProps->setSubject(xSourceDocProps->getSubject());
+ xDocProps->setDescription(xSourceDocProps->getDescription());
+ xDocProps->setKeywords(xSourceDocProps->getKeywords());
+ xDocProps->setLanguage(xSourceDocProps->getLanguage());
+ // Note: These below originally weren't copied for mailmerge, but I don't see why not.
+ xDocProps->setModifiedBy(xSourceDocProps->getModifiedBy());
+ xDocProps->setModificationDate(xSourceDocProps->getModificationDate());
+ xDocProps->setPrintedBy(xSourceDocProps->getPrintedBy());
+ xDocProps->setPrintDate(xSourceDocProps->getPrintDate());
+ xDocProps->setTemplateName(xSourceDocProps->getTemplateName());
+ xDocProps->setTemplateURL(xSourceDocProps->getTemplateURL());
+ xDocProps->setTemplateDate(xSourceDocProps->getTemplateDate());
+ xDocProps->setAutoloadURL(xSourceDocProps->getAutoloadURL());
+ xDocProps->setAutoloadSecs(xSourceDocProps->getAutoloadSecs());
+ xDocProps->setDefaultTarget(xSourceDocProps->getDefaultTarget());
+ xDocProps->setDocumentStatistics(xSourceDocProps->getDocumentStatistics());
+ xDocProps->setEditingCycles(xSourceDocProps->getEditingCycles());
+ xDocProps->setEditingDuration(xSourceDocProps->getEditingDuration());
+
+ if( mailMerge ) // Note: Not sure this is needed.
+ {
+ // Manually set the creation date, otherwise author field isn't filled
+ // during MM, as it's set when saving the document the first time.
+ xDocProps->setCreationDate( xSourceDocProps->getModificationDate() );
+ }
+
+ ReplaceUserDefinedDocumentProperties( xSourceDocProps );
+}
+
+/// inserts an AutoText block
+bool SwDoc::InsertGlossary( SwTextBlocks& rBlock, const OUString& rEntry,
+ SwPaM& rPaM, SwCursorShell* pShell )
+{
+ bool bRet = false;
+ const sal_uInt16 nIdx = rBlock.GetIndex( rEntry );
+ if( USHRT_MAX != nIdx )
+ {
+ bool bSav_IsInsGlossary = mbInsOnlyTextGlssry;
+ mbInsOnlyTextGlssry = rBlock.IsOnlyTextBlock( nIdx );
+
+ if( rBlock.BeginGetDoc( nIdx ) )
+ {
+ SwDoc* pGDoc = rBlock.GetDoc();
+
+ // Update all fixed fields, with the right DocInfo.
+ // FIXME: UGLY: Because we cannot limit the range in which to do
+ // field updates, we must update the fixed fields at the glossary
+ // entry document.
+ // To be able to do this, we copy the document properties of the
+ // target document to the glossary document
+ // OSL_ENSURE(GetDocShell(), "no SwDocShell"); // may be clipboard!
+ OSL_ENSURE(pGDoc->GetDocShell(), "no SwDocShell at glossary");
+ if (GetDocShell() && pGDoc->GetDocShell())
+ pGDoc->ReplaceDocumentProperties( *this );
+ pGDoc->getIDocumentFieldsAccess().SetFixFields(nullptr);
+
+ // StartAllAction();
+ getIDocumentFieldsAccess().LockExpFields();
+
+ SwNodeIndex aStt( pGDoc->GetNodes().GetEndOfExtras(), 1 );
+ SwContentNode* pContentNd = pGDoc->GetNodes().GoNext( &aStt );
+ const SwTableNode* pTableNd = pContentNd->FindTableNode();
+ SwPaM aCpyPam( pTableNd ? *const_cast<SwNode*>(static_cast<SwNode const *>(pTableNd)) : *static_cast<SwNode*>(pContentNd) );
+ aCpyPam.SetMark();
+
+ // till the nodes array's end
+ aCpyPam.GetPoint()->nNode = pGDoc->GetNodes().GetEndOfContent().GetIndex()-SwNodeOffset(1);
+ pContentNd = aCpyPam.GetContentNode();
+ aCpyPam.GetPoint()->nContent.Assign(
+ pContentNd, pContentNd ? pContentNd->Len() : 0 );
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr );
+ SwPaM *_pStartCursor = &rPaM, *_pStartCursor2 = _pStartCursor;
+ do {
+
+ SwPosition& rInsPos = *_pStartCursor->GetPoint();
+ SwStartNode* pBoxSttNd = const_cast<SwStartNode*>(rInsPos.nNode.GetNode().
+ FindTableBoxStartNode());
+
+ if( pBoxSttNd && SwNodeOffset(2) == pBoxSttNd->EndOfSectionIndex() -
+ pBoxSttNd->GetIndex() &&
+ aCpyPam.GetPoint()->nNode != aCpyPam.GetMark()->nNode )
+ {
+ // We copy more than one Node to the current Box.
+ // However, we have to remove the BoxAttributes then.
+ ClearBoxNumAttrs( rInsPos.nNode );
+ }
+
+ SwDontExpandItem aACD;
+ aACD.SaveDontExpandItems( rInsPos );
+
+ pGDoc->getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly);
+
+ aACD.RestoreDontExpandItems( rInsPos );
+ if( pShell )
+ pShell->SaveTableBoxContent( &rInsPos );
+ } while( (_pStartCursor = _pStartCursor->GetNext()) !=
+ _pStartCursor2 );
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr );
+
+ getIDocumentFieldsAccess().UnlockExpFields();
+ if( !getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+ bRet = true;
+ }
+ mbInsOnlyTextGlssry = bSav_IsInsGlossary;
+ }
+ rBlock.EndGetDoc();
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doclay.cxx b/sw/source/core/doc/doclay.cxx
new file mode 100644
index 000000000..b3275040a
--- /dev/null
+++ b/sw/source/core/doc/doclay.cxx
@@ -0,0 +1,1706 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <sot/exchange.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdouno.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <istype.hxx>
+#include <swmodule.hxx>
+#include <modcfg.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <SwStyleNameMapper.hxx>
+#include <drawdoc.hxx>
+#include <fchrfmt.hxx>
+#include <frmatr.hxx>
+#include <txatbase.hxx>
+#include <fmtfld.hxx>
+#include <fmtornt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <ndnotxt.hxx>
+#include <ndole.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <swundo.hxx>
+#include <flypos.hxx>
+#include <UndoInsert.hxx>
+#include <expfld.hxx>
+#include <poolfmt.hxx>
+#include <docary.hxx>
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <txtftn.hxx>
+#include <ftnidx.hxx>
+#include <ftninfo.hxx>
+#include <pagedesc.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <tools/datetimeutils.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <sortedobjs.hxx>
+
+#include <string_view>
+#include <vector>
+
+using namespace ::com::sun::star;
+
+#define DEF_FLY_WIDTH 2268 // Default width for FlyFrames (2268 == 4cm)
+
+static bool lcl_IsItemSet(const SwContentNode & rNode, sal_uInt16 which)
+{
+ bool bResult = false;
+
+ if (SfxItemState::SET == rNode.GetSwAttrSet().GetItemState(which))
+ bResult = true;
+
+ return bResult;
+}
+
+SdrObject* SwDoc::CloneSdrObj( const SdrObject& rObj, bool bMoveWithinDoc,
+ bool bInsInPage )
+{
+ // #i52858# - method name changed
+ SdrPage *pPg = getIDocumentDrawModelAccess().GetOrCreateDrawModel()->GetPage( 0 );
+ if( !pPg )
+ {
+ auto pNewPage = getIDocumentDrawModelAccess().GetDrawModel()->AllocPage( false );
+ getIDocumentDrawModelAccess().GetDrawModel()->InsertPage( pNewPage.get() );
+ pPg = pNewPage.get();
+ }
+
+ // TTTT Clone directly to target SdrModel
+ SdrObject *pObj(rObj.CloneSdrObject(*getIDocumentDrawModelAccess().GetDrawModel()));
+
+ if( bMoveWithinDoc && SdrInventor::FmForm == pObj->GetObjInventor() )
+ {
+ // We need to preserve the Name for Controls
+ uno::Reference< awt::XControlModel > xModel = static_cast<SdrUnoObj*>(pObj)->GetUnoControlModel();
+ uno::Any aVal;
+ uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY);
+ static const OUStringLiteral sName(u"Name");
+ if( xSet.is() )
+ aVal = xSet->getPropertyValue( sName );
+ if( bInsInPage )
+ pPg->InsertObjectThenMakeNameUnique( pObj );
+ if( xSet.is() )
+ xSet->setPropertyValue( sName, aVal );
+ }
+ else if( bInsInPage )
+ pPg->InsertObjectThenMakeNameUnique( pObj );
+
+ // For drawing objects: set layer of cloned object to invisible layer
+ SdrLayerID nLayerIdForClone = rObj.GetLayer();
+ if ( dynamic_cast<const SwFlyDrawObj*>( pObj) == nullptr &&
+ dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr &&
+ !isType<SdrObject>(pObj) )
+ {
+ if ( getIDocumentDrawModelAccess().IsVisibleLayerId( nLayerIdForClone ) )
+ {
+ nLayerIdForClone = getIDocumentDrawModelAccess().GetInvisibleLayerIdByVisibleOne( nLayerIdForClone );
+ }
+ }
+ pObj->SetLayer( nLayerIdForClone );
+
+ return pObj;
+}
+
+SwFlyFrameFormat* SwDoc::MakeFlySection_( const SwPosition& rAnchPos,
+ const SwContentNode& rNode,
+ RndStdIds eRequestId,
+ const SfxItemSet* pFlySet,
+ SwFrameFormat* pFrameFormat )
+{
+ if( !pFrameFormat )
+ pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME );
+
+ OUString sName;
+ switch( rNode.GetNodeType() )
+ {
+ case SwNodeType::Grf: sName = GetUniqueGrfName(); break;
+ case SwNodeType::Ole: sName = GetUniqueOLEName(); break;
+ default: sName = GetUniqueFrameName(); break;
+ }
+ SwFlyFrameFormat* pFormat = MakeFlyFrameFormat( sName, pFrameFormat );
+
+ // Create content and connect to the format.
+ // Create ContentNode and put it into the autotext selection.
+ SwNodeRange aRange( GetNodes().GetEndOfAutotext(), SwNodeOffset(-1),
+ GetNodes().GetEndOfAutotext() );
+ GetNodes().SectionDown( &aRange, SwFlyStartNode );
+
+ pFormat->SetFormatAttr( SwFormatContent( rNode.StartOfSectionNode() ));
+
+ const SwFormatAnchor* pAnchor = nullptr;
+ if( pFlySet )
+ {
+ pAnchor = pFlySet->GetItemIfSet( RES_ANCHOR, false );
+ if( SfxItemState::SET == pFlySet->GetItemState( RES_CNTNT, false ))
+ {
+ SfxItemSet aTmpSet( *pFlySet );
+ aTmpSet.ClearItem( RES_CNTNT );
+ pFormat->SetFormatAttr( aTmpSet );
+ }
+ else
+ pFormat->SetFormatAttr( *pFlySet );
+ }
+
+ // Anchor not yet set?
+ RndStdIds eAnchorId;
+ // #i107811# Assure that at-page anchored fly frames have a page num or a
+ // content anchor set.
+ if ( !pAnchor ||
+ ( RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId() &&
+ !pAnchor->GetContentAnchor() ) ||
+ ( RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId() &&
+ !pAnchor->GetContentAnchor() &&
+ pAnchor->GetPageNum() == 0 ) )
+ {
+ // set it again, needed for Undo
+ SwFormatAnchor aAnch( pFormat->GetAnchor() );
+ if (pAnchor && (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()))
+ {
+ SwPosition aPos( *rAnchPos.nNode.GetNode().FindFlyStartNode() );
+ aAnch.SetAnchor( &aPos );
+ eAnchorId = RndStdIds::FLY_AT_FLY;
+ }
+ else
+ {
+ if( eRequestId != aAnch.GetAnchorId() &&
+ SfxItemState::SET != pFormat->GetItemState( RES_ANCHOR ) )
+ {
+ aAnch.SetType( eRequestId );
+ }
+
+ eAnchorId = aAnch.GetAnchorId();
+ if ( RndStdIds::FLY_AT_PAGE != eAnchorId || !pAnchor || aAnch.GetPageNum() == 0)
+ {
+ aAnch.SetAnchor( &rAnchPos );
+ }
+ }
+ pFormat->SetFormatAttr( aAnch );
+ }
+ else
+ eAnchorId = pFormat->GetAnchor().GetAnchorId();
+
+ if ( RndStdIds::FLY_AS_CHAR == eAnchorId )
+ {
+ const sal_Int32 nStt = rAnchPos.nContent.GetIndex();
+ SwTextNode * pTextNode = rAnchPos.nNode.GetNode().GetTextNode();
+
+ OSL_ENSURE(pTextNode!= nullptr, "There should be a SwTextNode!");
+
+ if (pTextNode != nullptr)
+ {
+ SwFormatFlyCnt aFormat( pFormat );
+ // may fail if there's no space left or header/ftr
+ if (!pTextNode->InsertItem(aFormat, nStt, nStt))
+ { // pFormat is dead now
+ return nullptr;
+ }
+ }
+ }
+
+ if( SfxItemState::SET != pFormat->GetAttrSet().GetItemState( RES_FRM_SIZE ))
+ {
+ SwFormatFrameSize aFormatSize( SwFrameSize::Variable, 0, DEF_FLY_WIDTH );
+ const SwNoTextNode* pNoTextNode = rNode.GetNoTextNode();
+ if( pNoTextNode )
+ {
+ // Set size
+ Size aSize( pNoTextNode->GetTwipSize() );
+ if( MINFLY > aSize.Width() )
+ aSize.setWidth( DEF_FLY_WIDTH );
+ aFormatSize.SetWidth( aSize.Width() );
+ if( aSize.Height() )
+ {
+ aFormatSize.SetHeight( aSize.Height() );
+ aFormatSize.SetHeightSizeType( SwFrameSize::Fixed );
+ }
+ }
+ pFormat->SetFormatAttr( aFormatSize );
+ }
+
+ // Set up frames
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() )
+ pFormat->MakeFrames(); // ???
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ SwNodeOffset nNodeIdx = rAnchPos.nNode.GetIndex();
+ const sal_Int32 nCntIdx = rAnchPos.nContent.GetIndex();
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsLayFormat>( pFormat, nNodeIdx, nCntIdx ));
+ }
+
+ getIDocumentState().SetModified();
+ return pFormat;
+}
+
+SwFlyFrameFormat* SwDoc::MakeFlySection( RndStdIds eAnchorType,
+ const SwPosition* pAnchorPos,
+ const SfxItemSet* pFlySet,
+ SwFrameFormat* pFrameFormat, bool bCalledFromShell )
+{
+ SwFlyFrameFormat* pFormat = nullptr;
+ if ( !pAnchorPos && (RndStdIds::FLY_AT_PAGE != eAnchorType) )
+ {
+ const SwFormatAnchor* pAnch;
+ if( (pFlySet && (pAnch = pFlySet->GetItemIfSet( RES_ANCHOR, false ))) ||
+ ( pFrameFormat && (pAnch = pFrameFormat->GetItemIfSet(RES_ANCHOR)) ) )
+ {
+ if ( RndStdIds::FLY_AT_PAGE != pAnch->GetAnchorId() )
+ {
+ pAnchorPos = pAnch->GetContentAnchor();
+ }
+ }
+ }
+
+ if (pAnchorPos)
+ {
+ if( !pFrameFormat )
+ pFrameFormat = getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME );
+
+ sal_uInt16 nCollId = o3tl::narrowing<sal_uInt16>(
+ GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE) ? RES_POOLCOLL_TEXT : RES_POOLCOLL_FRAME );
+
+ /* If there is no adjust item in the paragraph style for the content node of the new fly section
+ propagate an existing adjust item at the anchor to the new content node. */
+ SwContentNode * pNewTextNd = GetNodes().MakeTextNode
+ (SwNodeIndex( GetNodes().GetEndOfAutotext()),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( nCollId ));
+ SwContentNode * pAnchorNode = pAnchorPos->nNode.GetNode().GetContentNode();
+ // pAnchorNode from cursor must be valid, unless a whole table is selected (in which
+ // case the node is not a content node, and pAnchorNode is nullptr). In the latter case,
+ // bCalledFromShell is false.
+ assert(!bCalledFromShell || pAnchorNode);
+
+ const SfxPoolItem * pItem = nullptr;
+
+ if (bCalledFromShell && !lcl_IsItemSet(*pNewTextNd, RES_PARATR_ADJUST) &&
+ SfxItemState::SET == pAnchorNode->GetSwAttrSet().GetItemState(RES_PARATR_ADJUST, true, &pItem))
+ {
+ pNewTextNd->SetAttr(*pItem);
+ }
+
+ pFormat = MakeFlySection_( *pAnchorPos, *pNewTextNd,
+ eAnchorType, pFlySet, pFrameFormat );
+ }
+ return pFormat;
+}
+
+SwFlyFrameFormat* SwDoc::MakeFlyAndMove( const SwPaM& rPam, const SfxItemSet& rSet,
+ const SwSelBoxes* pSelBoxes,
+ SwFrameFormat *pParent )
+{
+ const SwFormatAnchor& rAnch = rSet.Get( RES_ANCHOR );
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr );
+
+ SwFlyFrameFormat* pFormat = MakeFlySection( rAnch.GetAnchorId(), rPam.GetPoint(),
+ &rSet, pParent );
+
+ // If content is selected, it becomes the new frame's content.
+ // Namely, it is moved into the NodeArray's appropriate section.
+
+ if( pFormat )
+ {
+ do { // middle check loop
+ const SwFormatContent &rContent = pFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "No content prepared." );
+ SwNodeIndex aIndex( *(rContent.GetContentIdx()), 1 );
+ SwContentNode *pNode = aIndex.GetNode().GetContentNode();
+
+ // Attention: Do not create an index on the stack, or we
+ // cannot delete ContentNode in the end!
+ SwPosition aPos( aIndex );
+ aPos.nContent.Assign( pNode, 0 );
+
+ if( pSelBoxes && !pSelBoxes->empty() )
+ {
+ // Table selection
+ // Copy parts of a table: create a table with the same width as the
+ // original one and move (copy and delete) the selected boxes.
+ // The size is corrected on a percentage basis.
+
+ SwTableNode* pTableNd = const_cast<SwTableNode*>((*pSelBoxes)[0]->
+ GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ break;
+
+ SwTable& rTable = pTableNd->GetTable();
+
+ // Did we select the whole table?
+ if( pSelBoxes->size() == rTable.GetTabSortBoxes().size() )
+ {
+ // move the whole table
+ SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode(), SwNodeOffset(1) );
+
+ // If we move the whole table and it is located within a
+ // FlyFrame, the we create a TextNode after it.
+ // So that this FlyFrame is preserved.
+ if( aRg.aEnd.GetNode().IsEndNode() )
+ GetNodes().MakeTextNode( aRg.aStart,
+ GetDfltTextFormatColl() );
+
+ getIDocumentContentOperations().MoveNodeRange( aRg, aPos.nNode, SwMoveFlags::DEFAULT );
+ }
+ else
+ {
+ rTable.MakeCopy(*this, aPos, *pSelBoxes);
+ // Don't delete a part of a table with row span!!
+ // You could delete the content instead -> ToDo
+ //rTable.DeleteSel( this, *pSelBoxes, 0, 0, true, true );
+ }
+
+ // If the table is within the frame, then copy without the following TextNode
+ aIndex = rContent.GetContentIdx()->GetNode().EndOfSectionIndex() - 1;
+ OSL_ENSURE( aIndex.GetNode().GetTextNode(),
+ "a TextNode should be here" );
+ aPos.nContent.Assign( nullptr, 0 ); // Deregister index!
+ GetNodes().Delete( aIndex );
+
+ // This is a hack: whilst FlyFrames/Headers/Footers are not undoable we delete all Undo objects
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+ }
+ else
+ {
+ // copy all Pams and then delete all
+ bool bOldFlag = mbCopyIsMove;
+ bool const bOldUndo = GetIDocumentUndoRedo().DoesUndo();
+ bool const bOldRedlineMove(getIDocumentRedlineAccess().IsRedlineMove());
+ mbCopyIsMove = true;
+ GetIDocumentUndoRedo().DoUndo(false);
+ getIDocumentRedlineAccess().SetRedlineMove(true);
+ for(const SwPaM& rTmp : rPam.GetRingContainer())
+ {
+ if( rTmp.HasMark() &&
+ *rTmp.GetPoint() != *rTmp.GetMark() )
+ {
+ // aPos is the newly created fly section, so definitely outside rPam, it's pointless to check that again.
+ getIDocumentContentOperations().CopyRange(*const_cast<SwPaM*>(&rTmp), aPos, SwCopyFlags::IsMoveToFly);
+ }
+ }
+ getIDocumentRedlineAccess().SetRedlineMove(bOldRedlineMove);
+ mbCopyIsMove = bOldFlag;
+ GetIDocumentUndoRedo().DoUndo(bOldUndo);
+
+ for(const SwPaM& rTmp : rPam.GetRingContainer())
+ {
+ if( rTmp.HasMark() &&
+ *rTmp.GetPoint() != *rTmp.GetMark() )
+ {
+ getIDocumentContentOperations().DeleteAndJoin( *const_cast<SwPaM*>(&rTmp) );
+ }
+ }
+ }
+ } while( false );
+ }
+
+ getIDocumentState().SetModified();
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr );
+
+ return pFormat;
+}
+
+
+/*
+ * paragraph frames - o.k. if the PaM includes the paragraph from the beginning
+ * to the beginning of the next paragraph at least
+ * frames at character - o.k. if the PaM starts at least at the same position
+ * as the frame
+ */
+static bool lcl_TstFlyRange( const SwPaM* pPam, const SwPosition* pFlyPos,
+ RndStdIds nAnchorId )
+{
+ bool bOk = false;
+ const SwPaM* pTmp = pPam;
+ do {
+ const SwNodeOffset nFlyIndex = pFlyPos->nNode.GetIndex();
+ const SwPosition* pPaMStart = pTmp->Start();
+ const SwPosition* pPaMEnd = pTmp->End();
+ const SwNodeOffset nPamStartIndex = pPaMStart->nNode.GetIndex();
+ const SwNodeOffset nPamEndIndex = pPaMEnd->nNode.GetIndex();
+ if (RndStdIds::FLY_AT_PARA == nAnchorId)
+ bOk = (nPamStartIndex < nFlyIndex && nPamEndIndex > nFlyIndex) ||
+ (((nPamStartIndex == nFlyIndex) && (pPaMStart->nContent.GetIndex() == 0)) &&
+ (nPamEndIndex > nFlyIndex));
+ else
+ {
+ const sal_Int32 nFlyContentIndex = pFlyPos->nContent.GetIndex();
+ const sal_Int32 nPamEndContentIndex = pPaMEnd->nContent.GetIndex();
+ bOk = (nPamStartIndex < nFlyIndex &&
+ (( nPamEndIndex > nFlyIndex )||
+ ((nPamEndIndex == nFlyIndex) &&
+ (nPamEndContentIndex > nFlyContentIndex))) )
+ ||
+ (((nPamStartIndex == nFlyIndex) &&
+ (pPaMStart->nContent.GetIndex() <= nFlyContentIndex)) &&
+ ((nPamEndIndex > nFlyIndex) ||
+ (nPamEndContentIndex > nFlyContentIndex )));
+ }
+
+ if( bOk )
+ break;
+ pTmp = pTmp->GetNext();
+ } while( pPam != pTmp );
+ return bOk;
+}
+
+SwPosFlyFrames SwDoc::GetAllFlyFormats( const SwPaM* pCmpRange, bool bDrawAlso,
+ bool bAsCharAlso ) const
+{
+ SwPosFlyFrames aRetval;
+
+ // collect all anchored somehow to paragraphs
+ for( auto pFly : *GetSpzFrameFormats() )
+ {
+ bool bDrawFormat = bDrawAlso && RES_DRAWFRMFMT == pFly->Which();
+ bool bFlyFormat = RES_FLYFRMFMT == pFly->Which();
+ if( bFlyFormat || bDrawFormat )
+ {
+ const SwFormatAnchor& rAnchor = pFly->GetAnchor();
+ SwPosition const*const pAPos = rAnchor.GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) && bAsCharAlso)))
+ {
+ if( pCmpRange &&
+ !lcl_TstFlyRange( pCmpRange, pAPos, rAnchor.GetAnchorId() ))
+ continue; // not a valid FlyFrame
+ aRetval.insert(std::make_shared<SwPosFlyFrame>(pAPos->nNode, pFly, aRetval.size()));
+ }
+ }
+ }
+
+ // If we don't have a layout we can't get page anchored FlyFrames.
+ // Also, page anchored FlyFrames are only returned if no range is specified.
+ if( !getIDocumentLayoutAccess().GetCurrentViewShell() || pCmpRange )
+ {
+ return aRetval;
+ }
+
+ const SwPageFrame *pPage = static_cast<const SwPageFrame*>(getIDocumentLayoutAccess().GetCurrentLayout()->GetLower());
+ while( pPage )
+ {
+ if( pPage->GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for(SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ SwFrameFormat *pFly;
+ if ( pAnchoredObj->DynCastFlyFrame() != nullptr )
+ pFly = &(pAnchoredObj->GetFrameFormat());
+ else if ( bDrawAlso )
+ pFly = &(pAnchoredObj->GetFrameFormat());
+ else
+ continue;
+
+ const SwFormatAnchor& rAnchor = pFly->GetAnchor();
+ if ((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_FLY != rAnchor.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId()))
+ {
+ const SwContentFrame * pContentFrame = pPage->FindFirstBodyContent();
+ if ( !pContentFrame )
+ {
+ // Oops! An empty page.
+ // In order not to lose the whole frame (RTF) we
+ // look for the last Content before the page.
+ const SwPageFrame *pPrv = static_cast<const SwPageFrame*>(pPage->GetPrev());
+ while ( !pContentFrame && pPrv )
+ {
+ pContentFrame = pPrv->FindFirstBodyContent();
+ pPrv = static_cast<const SwPageFrame*>(pPrv->GetPrev());
+ }
+ }
+ if ( pContentFrame )
+ {
+ SwNodeIndex aIdx( pContentFrame->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pContentFrame)->GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame const*>(pContentFrame)->GetNode() );
+ aRetval.insert(std::make_shared<SwPosFlyFrame>(aIdx, pFly, aRetval.size()));
+ }
+ }
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ return aRetval;
+}
+
+/* #i6447# changed behaviour if lcl_CpyAttr:
+
+ If the old item set contains the item to set (no inheritance) copy the item
+ into the new set.
+
+ If the old item set contains the item by inheritance and the new set
+ contains the item, too:
+ If the two items differ copy the item from the old set to the new set.
+
+ Otherwise the new set will not be changed.
+*/
+static void lcl_CpyAttr( SfxItemSet &rNewSet, const SfxItemSet &rOldSet, sal_uInt16 nWhich )
+{
+ const SfxPoolItem *pOldItem = nullptr;
+
+ rOldSet.GetItemState( nWhich, false, &pOldItem);
+ if (pOldItem != nullptr)
+ rNewSet.Put( *pOldItem );
+ else
+ {
+ pOldItem = rOldSet.GetItem( nWhich );
+ if (pOldItem != nullptr)
+ {
+ const SfxPoolItem *pNewItem = rNewSet.GetItem( nWhich );
+ if (pNewItem != nullptr)
+ {
+ if (*pOldItem != *pNewItem)
+ rNewSet.Put( *pOldItem );
+ }
+ else {
+ OSL_FAIL("What am I doing here?");
+ }
+ }
+ else {
+ OSL_FAIL("What am I doing here?");
+ }
+ }
+
+}
+
+static SwFlyFrameFormat *
+lcl_InsertLabel(SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable,
+ SwUndoInsertLabel *const pUndo,
+ SwLabelType const eType, std::u16string_view rText, std::u16string_view rSeparator,
+ const OUString& rNumberingSeparator,
+ const bool bBefore, const sal_uInt16 nId, const SwNodeOffset nNdIdx,
+ const OUString& rCharacterStyle,
+ const bool bCpyBrd )
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ bool bTable = false; // To save some code.
+
+ // Get the field first, because we retrieve the TextColl via the field's name
+ OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(),
+ "FieldType index out of bounds." );
+ SwFieldType *pType = (nId != USHRT_MAX) ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr;
+ OSL_ENSURE(!pType || pType->Which() == SwFieldIds::SetExp, "wrong Id for Label");
+
+ SwTextFormatColl * pColl = nullptr;
+ if( pType )
+ {
+ for( auto i = pTextFormatCollTable->size(); i; )
+ {
+ if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() )
+ {
+ pColl = (*pTextFormatCollTable)[i];
+ break;
+ }
+ }
+ OSL_ENSURE( pColl, "no text collection found" );
+ }
+
+ if( !pColl )
+ {
+ pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL );
+ }
+
+ SwTextNode *pNew = nullptr;
+ SwFlyFrameFormat* pNewFormat = nullptr;
+
+ switch ( eType )
+ {
+ case SwLabelType::Table:
+ bTable = true;
+ [[fallthrough]];
+ case SwLabelType::Fly:
+ // At the FlySection's Beginning/End insert the corresponding Node with its Field.
+ // The Frame is created automatically.
+ {
+ SwStartNode *pSttNd = rDoc.GetNodes()[nNdIdx]->GetStartNode();
+ OSL_ENSURE( pSttNd, "No StartNode in InsertLabel." );
+ SwNodeOffset nNode;
+ if( bBefore )
+ {
+ nNode = pSttNd->GetIndex();
+ if( !bTable )
+ ++nNode;
+ }
+ else
+ {
+ nNode = pSttNd->EndOfSectionIndex();
+ if( bTable )
+ ++nNode;
+ }
+
+ if( pUndo )
+ pUndo->SetNodePos( nNode );
+
+ // Create Node for labeling paragraph.
+ SwNodeIndex aIdx( rDoc.GetNodes(), nNode );
+ pNew = rDoc.GetNodes().MakeTextNode( aIdx, pColl );
+ }
+ break;
+
+ case SwLabelType::Object:
+ {
+ // Destroy Frame,
+ // insert new Frame,
+ // insert the corresponding Node with Field into the new Frame,
+ // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame,
+ // create Frames.
+
+ // Get the FlyFrame's Format and decouple the Layout.
+ SwFrameFormat *pOldFormat = rDoc.GetNodes()[nNdIdx]->GetFlyFormat();
+ OSL_ENSURE( pOldFormat, "Couldn't find the Fly's Format." );
+ // #i115719#
+ // <title> and <description> attributes are lost when calling <DelFrames()>.
+ // Thus, keep them and restore them after the calling <MakeFrames()>
+ auto pOldFlyFrameFormat = dynamic_cast<SwFlyFrameFormat*>(pOldFormat);
+ const OUString sTitle( pOldFlyFrameFormat
+ ? pOldFlyFrameFormat->GetObjTitle()
+ : OUString() );
+ const OUString sDescription( pOldFlyFrameFormat
+ ? pOldFlyFrameFormat->GetObjDescription()
+ : OUString() );
+ pOldFormat->DelFrames();
+
+ pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(),
+ rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool(RES_POOLFRM_FRAME) );
+
+ /* #i6447#: Only the selected items are copied from the old
+ format. */
+ std::unique_ptr<SfxItemSet> pNewSet = pNewFormat->GetAttrSet().Clone();
+
+ // Copy only the set attributes.
+ // The others should apply from the Templates.
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_PRINT );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_OPAQUE );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_PROTECT );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_SURROUND );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_VERT_ORIENT );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_HORI_ORIENT );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_LR_SPACE );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_UL_SPACE );
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_BACKGROUND );
+ if( bCpyBrd )
+ {
+ // If there's no BoxItem at graphic, but the new Format has one, then set the
+ // default item in the new Set. Because the graphic's size has never changed!
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pOldFormat->GetAttrSet().
+ GetItemState( RES_BOX, true, &pItem ))
+ pNewSet->Put( *pItem );
+ else if( SfxItemState::SET == pNewFormat->GetAttrSet().
+ GetItemState( RES_BOX ))
+ pNewSet->Put( *GetDfltAttr( RES_BOX ) );
+
+ if( SfxItemState::SET == pOldFormat->GetAttrSet().
+ GetItemState( RES_SHADOW, true, &pItem ))
+ pNewSet->Put( *pItem );
+ else if( SfxItemState::SET == pNewFormat->GetAttrSet().
+ GetItemState( RES_SHADOW ))
+ pNewSet->Put( *GetDfltAttr( RES_SHADOW ) );
+ }
+ else
+ {
+ // Hard-set the attributes, because they could come from the Template
+ // and then size calculations could not be correct anymore.
+ pNewSet->Put( SvxBoxItem(RES_BOX) );
+ pNewSet->Put( SvxShadowItem(RES_SHADOW) );
+ }
+
+ // Always transfer the anchor, which is a hard attribute anyways.
+ pNewSet->Put( pOldFormat->GetAnchor() );
+
+ // The new one should be changeable in its height.
+ std::unique_ptr<SwFormatFrameSize> aFrameSize(pOldFormat->GetFrameSize().Clone());
+ aFrameSize->SetHeightSizeType( SwFrameSize::Minimum );
+ pNewSet->Put( std::move(aFrameSize) );
+
+ SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection(
+ SwNodeIndex( rDoc.GetNodes().GetEndOfAutotext() ),
+ SwFlyStartNode, pColl );
+ pNewSet->Put( SwFormatContent( pSttNd ));
+
+ pNewFormat->SetFormatAttr( *pNewSet );
+
+ // InContents need to be treated in a special way:
+ // The TextAttribute needs to be destroyed.
+ // Unfortunately, this also destroys the Format next to the Frames.
+ // To avoid this, we disconnect the attribute from the Format.
+
+ const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor();
+ if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() )
+ {
+ const SwPosition *pPos = rAnchor.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt(nIdx, RES_TXTATR_FLYCNT);
+
+ assert(pHint && "Missing Hint.");
+
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pOldFormat,
+ "Wrong TextFlyCnt-Hint." );
+
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat(
+ pNewFormat );
+ }
+
+ // The old one should not have a flow and it should be adjusted to above and
+ // middle.
+ // Also, the width should be 100% and it should also adjust the height, if changed.
+ pNewSet->ClearItem();
+
+ pNewSet->Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ pNewSet->Put( SvxOpaqueItem( RES_OPAQUE, true ) );
+
+ sal_Int16 eVert = bBefore ? text::VertOrientation::BOTTOM : text::VertOrientation::TOP;
+ pNewSet->Put( SwFormatVertOrient( 0, eVert ) );
+ pNewSet->Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER ) );
+
+ aFrameSize.reset(pOldFormat->GetFrameSize().Clone());
+
+ SwOLENode* pOleNode = rDoc.GetNodes()[nNdIdx + 1]->GetOLENode();
+ bool isMath = false;
+ if(pOleNode)
+ {
+ svt::EmbeddedObjectRef& xRef = pOleNode->GetOLEObj().GetObject();
+ if(xRef.is())
+ {
+ SvGlobalName aCLSID( xRef->getClassID() );
+ isMath = ( SotExchange::IsMath( aCLSID ) != 0 );
+ }
+ }
+ aFrameSize->SetWidthPercent(isMath ? 0 : 100);
+ aFrameSize->SetHeightPercent(SwFormatFrameSize::SYNCED);
+ pNewSet->Put( std::move(aFrameSize) );
+
+ // Hard-set the attributes, because they could come from the Template
+ // and then size calculations could not be correct anymore.
+ if( bCpyBrd )
+ {
+ pNewSet->Put( SvxBoxItem(RES_BOX) );
+ pNewSet->Put( SvxShadowItem(RES_SHADOW) );
+ }
+ pNewSet->Put( SvxLRSpaceItem(RES_LR_SPACE) );
+ pNewSet->Put( SvxULSpaceItem(RES_UL_SPACE) );
+
+ // The old one is paragraph-bound to the paragraph in the new one.
+ SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA );
+ SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 );
+ pNew = aAnchIdx.GetNode().GetTextNode();
+ SwPosition aPos( aAnchIdx );
+ aAnch.SetAnchor( &aPos );
+ pNewSet->Put( aAnch );
+
+ if( pUndo )
+ pUndo->SetFlys( *pOldFormat, *pNewSet, *pNewFormat );
+ else
+ pOldFormat->SetFormatAttr( *pNewSet );
+
+ pNewSet.reset();
+
+ // Have only the FlyFrames created.
+ // We leave this to established methods (especially for InCntFlys).
+ pNewFormat->MakeFrames();
+ // #i115719#
+ if ( pOldFlyFrameFormat )
+ {
+ pOldFlyFrameFormat->SetObjTitle( sTitle );
+ pOldFlyFrameFormat->SetObjDescription( sDescription );
+ }
+ }
+ break;
+
+ default:
+ OSL_ENSURE(false, "unknown LabelType?");
+ }
+ OSL_ENSURE( pNew, "No Label inserted" );
+ if( pNew )
+ {
+ // #i61007# order of captions
+ bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst();
+ // Work up OUString
+ OUString aText;
+ if( bOrderNumberingFirst )
+ {
+ aText = rNumberingSeparator;
+ }
+ if( pType)
+ {
+ aText += pType->GetName();
+ if( !bOrderNumberingFirst )
+ aText += " ";
+ }
+ sal_Int32 nIdx = aText.getLength();
+ if( !rText.empty() )
+ {
+ aText += rSeparator;
+ }
+ const sal_Int32 nSepIdx = aText.getLength();
+ aText += rText;
+
+ // Insert string
+ SwIndex aIdx( pNew, 0 );
+ pNew->InsertText( aText, aIdx );
+
+ // Insert field
+ if(pType)
+ {
+ SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC);
+ if( bOrderNumberingFirst )
+ nIdx = 0;
+ SwFormatField aFormat( aField );
+ pNew->InsertItem( aFormat, nIdx, nIdx );
+ if(!rCharacterStyle.isEmpty())
+ {
+ SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle);
+ if( !pCharFormat )
+ {
+ const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName(rCharacterStyle, SwGetPoolIdFromName::ChrFmt);
+ pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId );
+ }
+ if (pCharFormat)
+ {
+ SwFormatCharFormat aCharFormat( pCharFormat );
+ pNew->InsertItem( aCharFormat, 0,
+ nSepIdx + 1, SetAttrMode::DONTEXPAND );
+ }
+ }
+ }
+
+ if ( bTable )
+ {
+ if ( bBefore )
+ {
+ if ( !pNew->GetSwAttrSet().GetKeep().GetValue() )
+ pNew->SetAttr( SvxFormatKeepItem( true, RES_KEEP ) );
+ }
+ else
+ {
+ SwTableNode *const pNd =
+ rDoc.GetNodes()[nNdIdx]->GetStartNode()->GetTableNode();
+ SwTable &rTable = pNd->GetTable();
+ if ( !rTable.GetFrameFormat()->GetKeep().GetValue() )
+ rTable.GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem( true, RES_KEEP ) );
+ if ( pUndo )
+ pUndo->SetUndoKeep();
+ }
+ }
+ rDoc.getIDocumentState().SetModified();
+ }
+
+ return pNewFormat;
+}
+
+SwFlyFrameFormat *
+SwDoc::InsertLabel(
+ SwLabelType const eType, OUString const& rText, OUString const& rSeparator,
+ OUString const& rNumberingSeparator,
+ bool const bBefore, sal_uInt16 const nId, SwNodeOffset const nNdIdx,
+ OUString const& rCharacterStyle,
+ bool const bCpyBrd )
+{
+ std::unique_ptr<SwUndoInsertLabel> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoInsertLabel(
+ eType, rText, rSeparator, rNumberingSeparator,
+ bBefore, nId, rCharacterStyle, bCpyBrd, this ));
+ }
+
+ SwFlyFrameFormat *const pNewFormat = lcl_InsertLabel(*this, mpTextFormatCollTable.get(), pUndo.get(),
+ eType, rText, rSeparator, rNumberingSeparator, bBefore,
+ nId, nNdIdx, rCharacterStyle, bCpyBrd);
+
+ if (pUndo)
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ else
+ {
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ return pNewFormat;
+}
+
+static SwFlyFrameFormat *
+lcl_InsertDrawLabel( SwDoc & rDoc, SwTextFormatColls *const pTextFormatCollTable,
+ SwUndoInsertLabel *const pUndo, SwDrawFrameFormat *const pOldFormat,
+ OUString const& rText,
+ const OUString& rSeparator,
+ const OUString& rNumberSeparator,
+ const sal_uInt16 nId,
+ const OUString& rCharacterStyle,
+ SdrObject& rSdrObj )
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ ::sw::DrawUndoGuard const drawUndoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Because we get by the TextColl's name, we need to create the field first.
+ OSL_ENSURE( nId == USHRT_MAX || nId < rDoc.getIDocumentFieldsAccess().GetFieldTypes()->size(),
+ "FieldType index out of bounds" );
+ SwFieldType *pType = nId != USHRT_MAX ? (*rDoc.getIDocumentFieldsAccess().GetFieldTypes())[nId].get() : nullptr;
+ OSL_ENSURE( !pType || pType->Which() == SwFieldIds::SetExp, "Wrong label id" );
+
+ SwTextFormatColl *pColl = nullptr;
+ if( pType )
+ {
+ for( auto i = pTextFormatCollTable->size(); i; )
+ {
+ if( (*pTextFormatCollTable)[ --i ]->GetName()==pType->GetName() )
+ {
+ pColl = (*pTextFormatCollTable)[i];
+ break;
+ }
+ }
+ OSL_ENSURE( pColl, "no text collection found" );
+ }
+
+ if( !pColl )
+ {
+ pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_LABEL );
+ }
+
+ SwTextNode* pNew = nullptr;
+ SwFlyFrameFormat* pNewFormat = nullptr;
+
+ // Destroy Frame,
+ // insert new Frame,
+ // insert the corresponding Node with Field into the new Frame,
+ // insert the old Frame with the Object (Picture/OLE) paragraph-bound into the new Frame,
+ // create Frames.
+
+ // Keep layer ID of drawing object before removing
+ // its frames.
+ // Note: The layer ID is passed to the undo and have to be the correct value.
+ // Removing the frames of the drawing object changes its layer.
+ const SdrLayerID nLayerId = rSdrObj.GetLayer();
+
+ pOldFormat->DelFrames();
+
+ // InContents need to be treated in a special way:
+ // The TextAttribute needs to be destroyed.
+ // Unfortunately, this also destroys the Format next to the Frames.
+ // To avoid this, we disconnect the attribute from the Format.
+ std::unique_ptr<SfxItemSet> pNewSet = pOldFormat->GetAttrSet().Clone( false );
+
+ // Protect the Frame's size and position
+ if ( rSdrObj.IsMoveProtect() || rSdrObj.IsResizeProtect() )
+ {
+ SvxProtectItem aProtect(RES_PROTECT);
+ aProtect.SetContentProtect( false );
+ aProtect.SetPosProtect( rSdrObj.IsMoveProtect() );
+ aProtect.SetSizeProtect( rSdrObj.IsResizeProtect() );
+ pNewSet->Put( aProtect );
+ }
+
+ // Take over the text wrap
+ lcl_CpyAttr( *pNewSet, pOldFormat->GetAttrSet(), RES_SURROUND );
+
+ // Send the frame to the back, if needed.
+ // Consider the 'invisible' hell layer.
+ if ( rDoc.getIDocumentDrawModelAccess().GetHellId() != nLayerId &&
+ rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() != nLayerId )
+ {
+ SvxOpaqueItem aOpaque( RES_OPAQUE );
+ aOpaque.SetValue( true );
+ pNewSet->Put( aOpaque );
+ }
+
+ // Take over position
+ // #i26791# - use directly drawing object's positioning attributes
+ pNewSet->Put( pOldFormat->GetHoriOrient() );
+ pNewSet->Put( pOldFormat->GetVertOrient() );
+
+ pNewSet->Put( pOldFormat->GetAnchor() );
+
+ // The new one should be variable in its height!
+ Size aSz( rSdrObj.GetCurrentBoundRect().GetSize() );
+ SwFormatFrameSize aFrameSize( SwFrameSize::Minimum, aSz.Width(), aSz.Height() );
+ pNewSet->Put( aFrameSize );
+
+ // Apply the margin to the new Frame.
+ // Don't set a border, use the one from the Template.
+ pNewSet->Put( pOldFormat->GetLRSpace() );
+ pNewSet->Put( pOldFormat->GetULSpace() );
+
+ SwStartNode* pSttNd =
+ rDoc.GetNodes().MakeTextSection(
+ SwNodeIndex( rDoc.GetNodes().GetEndOfAutotext() ),
+ SwFlyStartNode, pColl );
+
+ pNewFormat = rDoc.MakeFlyFrameFormat( rDoc.GetUniqueFrameName(),
+ rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME ) );
+
+ // Set border and shadow to default if the template contains any.
+ if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState( RES_BOX ))
+ pNewSet->Put( *GetDfltAttr( RES_BOX ) );
+
+ if( SfxItemState::SET == pNewFormat->GetAttrSet().GetItemState(RES_SHADOW))
+ pNewSet->Put( *GetDfltAttr( RES_SHADOW ) );
+
+ pNewFormat->SetFormatAttr( SwFormatContent( pSttNd ));
+ pNewFormat->SetFormatAttr( *pNewSet );
+
+ const SwFormatAnchor& rAnchor = pNewFormat->GetAnchor();
+ if ( RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId() )
+ {
+ const SwPosition *pPos = rAnchor.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
+
+ assert(pHint && "Missing Hint.");
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().
+ GetFrameFormat() == static_cast<SwFrameFormat*>(pOldFormat),
+ "Wrong TextFlyCnt-Hint." );
+#endif
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat( pNewFormat );
+ }
+
+ // The old one should not have a flow
+ // and it should be adjusted to above and middle.
+ pNewSet->ClearItem();
+
+ pNewSet->Put( SwFormatSurround( css::text::WrapTextMode_NONE ) );
+ if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetHellId())
+ {
+ // Consider drawing objects in the 'invisible' hell layer
+ rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() );
+ }
+ else if (nLayerId == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId())
+ {
+ rSdrObj.SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
+ }
+ pNewSet->Put( SvxLRSpaceItem( RES_LR_SPACE ) );
+ pNewSet->Put( SvxULSpaceItem( RES_UL_SPACE ) );
+
+ // #i26791# - set position of the drawing object, which is labeled.
+ pNewSet->Put( SwFormatVertOrient( 0, text::VertOrientation::TOP, text::RelOrientation::FRAME ) );
+ pNewSet->Put( SwFormatHoriOrient( 0, text::HoriOrientation::CENTER, text::RelOrientation::FRAME ) );
+
+ // The old one is paragraph-bound to the new one's paragraph.
+ SwFormatAnchor aAnch( RndStdIds::FLY_AT_PARA );
+ SwNodeIndex aAnchIdx( *pNewFormat->GetContent().GetContentIdx(), 1 );
+ pNew = aAnchIdx.GetNode().GetTextNode();
+ SwPosition aPos( aAnchIdx );
+ aAnch.SetAnchor( &aPos );
+ pNewSet->Put( aAnch );
+
+ if( pUndo )
+ {
+ pUndo->SetFlys( *pOldFormat, *pNewSet, *pNewFormat );
+ // #i26791# - position no longer needed
+ pUndo->SetDrawObj( nLayerId );
+ }
+ else
+ pOldFormat->SetFormatAttr( *pNewSet );
+
+ pNewSet.reset();
+
+ // Have only the FlyFrames created.
+ // We leave this to established methods (especially for InCntFlys).
+ pNewFormat->MakeFrames();
+
+ OSL_ENSURE( pNew, "No Label inserted" );
+
+ if( pNew )
+ {
+ //#i61007# order of captions
+ bool bOrderNumberingFirst = SW_MOD()->GetModuleConfig()->IsCaptionOrderNumberingFirst();
+
+ // prepare string
+ OUString aText;
+ if( bOrderNumberingFirst )
+ {
+ aText = rNumberSeparator;
+ }
+ if ( pType )
+ {
+ aText += pType->GetName();
+ if( !bOrderNumberingFirst )
+ aText += " ";
+ }
+ sal_Int32 nIdx = aText.getLength();
+ aText += rSeparator;
+ const sal_Int32 nSepIdx = aText.getLength();
+ aText += rText;
+
+ // insert text
+ SwIndex aIdx( pNew, 0 );
+ pNew->InsertText( aText, aIdx );
+
+ // insert field
+ if ( pType )
+ {
+ SwSetExpField aField( static_cast<SwSetExpFieldType*>(pType), OUString(), SVX_NUM_ARABIC );
+ if( bOrderNumberingFirst )
+ nIdx = 0;
+ SwFormatField aFormat( aField );
+ pNew->InsertItem( aFormat, nIdx, nIdx );
+ if ( !rCharacterStyle.isEmpty() )
+ {
+ SwCharFormat * pCharFormat = rDoc.FindCharFormatByName(rCharacterStyle);
+ if ( !pCharFormat )
+ {
+ const sal_uInt16 nMyId = SwStyleNameMapper::GetPoolIdFromUIName( rCharacterStyle, SwGetPoolIdFromName::ChrFmt );
+ pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nMyId );
+ }
+ if ( pCharFormat )
+ {
+ SwFormatCharFormat aCharFormat( pCharFormat );
+ pNew->InsertItem( aCharFormat, 0, nSepIdx + 1,
+ SetAttrMode::DONTEXPAND );
+ }
+ }
+ }
+ }
+
+ return pNewFormat;
+}
+
+SwFlyFrameFormat* SwDoc::InsertDrawLabel(
+ OUString const& rText,
+ OUString const& rSeparator,
+ OUString const& rNumberSeparator,
+ sal_uInt16 const nId,
+ OUString const& rCharacterStyle,
+ SdrObject& rSdrObj )
+{
+ SwDrawContact *const pContact =
+ static_cast<SwDrawContact*>(GetUserCall( &rSdrObj ));
+ if (!pContact)
+ return nullptr;
+ OSL_ENSURE( RES_DRAWFRMFMT == pContact->GetFormat()->Which(),
+ "InsertDrawLabel(): not a DrawFrameFormat" );
+
+ SwDrawFrameFormat* pOldFormat = static_cast<SwDrawFrameFormat *>(pContact->GetFormat());
+ if (!pOldFormat)
+ return nullptr;
+
+ std::unique_ptr<SwUndoInsertLabel> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoInsertLabel(
+ SwLabelType::Draw, rText, rSeparator, rNumberSeparator, false,
+ nId, rCharacterStyle, false, this ));
+ }
+
+ SwFlyFrameFormat *const pNewFormat = lcl_InsertDrawLabel(
+ *this, mpTextFormatCollTable.get(), pUndo.get(), pOldFormat,
+ rText, rSeparator, rNumberSeparator, nId, rCharacterStyle, rSdrObj);
+
+ if (pUndo)
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ else
+ {
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ return pNewFormat;
+}
+
+static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, std::u16string_view rName, std::u16string_view rCmpName)
+{
+ if (o3tl::starts_with(rName, rCmpName))
+ {
+ // Only get and set the Flag
+ const sal_Int32 nNum = o3tl::toInt32(rName.substr(nNmLen)) - 1;
+ if (nNum >= 0)
+ rSetFlags.push_back(nNum);
+ }
+}
+
+static void lcl_collectUsedNums(std::vector<unsigned int>& rSetFlags, sal_Int32 nNmLen, const SdrObject& rObj, const OUString& rCmpName)
+{
+ OUString sName = rObj.GetName();
+ lcl_collectUsedNums(rSetFlags, nNmLen, sName, rCmpName);
+ // tdf#122487 take groups into account, iterate and recurse through their
+ // contents for name collision check
+ if (!rObj.IsGroupObject())
+ return;
+
+ const SdrObjList* pSub(rObj.GetSubList());
+ assert(pSub && "IsGroupObject is implemented as GetSubList != nullptr");
+ const size_t nCount = pSub->GetObjCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrObject* pObj = pSub->GetObj(i);
+ if (!pObj)
+ continue;
+ lcl_collectUsedNums(rSetFlags, nNmLen, *pObj, rCmpName);
+ }
+}
+
+namespace
+{
+ int first_available_number(std::vector<unsigned int>& numbers)
+ {
+ std::sort(numbers.begin(), numbers.end());
+ auto last = std::unique(numbers.begin(), numbers.end());
+ numbers.erase(last, numbers.end());
+
+ for (size_t i = 0; i < numbers.size(); ++i)
+ {
+ if (numbers[i] != i)
+ return i;
+ }
+
+ return numbers.size();
+ }
+}
+
+static OUString lcl_GetUniqueFlyName(const SwDoc& rDoc, TranslateId pDefStrId, sal_uInt16 eType, std::u16string_view rPrefix = std::u16string_view(), SwNodeType nNdTyp = SwNodeType::NONE)
+{
+ assert(eType >= RES_FMT_BEGIN && eType < RES_FMT_END);
+ if (rDoc.IsInMailMerge())
+ {
+ OUString newName = "MailMergeFly"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( rDoc.GetSpzFrameFormats()->size() + 1 );
+ return newName;
+ }
+
+ if (!rPrefix.empty())
+ {
+ // Generate a name that makes it possible to know this is a copy of which original name,
+ // e.g. 'Picture 1 Copy 1'.
+ assert(nNdTyp != SwNodeType::NONE);
+ sal_Int32 nCnt = 1;
+ OUString aPrefix = SwResId(STR_MARK_COPY).replaceFirst("%1", rPrefix);
+ OUString aTmp;
+ while(nCnt < SAL_MAX_INT32)
+ {
+ aTmp = aPrefix + OUString::number(nCnt);
+ ++nCnt;
+ if (!rDoc.FindFlyByName(aTmp, nNdTyp))
+ {
+ break;
+ }
+ }
+ return aTmp;
+ }
+
+ OUString aName(SwResId(pDefStrId));
+ sal_Int32 nNmLen = aName.getLength();
+
+ const SwFrameFormats& rFormats = *rDoc.GetSpzFrameFormats();
+
+ std::vector<unsigned int> aUsedNums;
+ aUsedNums.reserve(rFormats.size());
+
+ for( SwFrameFormats::size_type n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFlyFormat = rFormats[ n ];
+ if (eType != pFlyFormat->Which())
+ continue;
+ if (eType == RES_DRAWFRMFMT)
+ {
+ const SdrObject *pObj = pFlyFormat->FindSdrObject();
+ if (pObj)
+ lcl_collectUsedNums(aUsedNums, nNmLen, *pObj, aName);
+ }
+
+ OUString sName = pFlyFormat->GetName();
+ lcl_collectUsedNums(aUsedNums, nNmLen, sName, aName);
+ }
+
+ // All numbers are flagged accordingly, so determine the right one
+ SwFrameFormats::size_type nNum = first_available_number(aUsedNums) + 1;
+ return aName + OUString::number(nNum);
+}
+
+OUString SwDoc::GetUniqueGrfName(std::u16string_view rPrefix) const
+{
+ return lcl_GetUniqueFlyName(*this, STR_GRAPHIC_DEFNAME, RES_FLYFRMFMT, rPrefix, SwNodeType::Grf);
+}
+
+OUString SwDoc::GetUniqueOLEName() const
+{
+ return lcl_GetUniqueFlyName(*this, STR_OBJECT_DEFNAME, RES_FLYFRMFMT);
+}
+
+OUString SwDoc::GetUniqueFrameName() const
+{
+ return lcl_GetUniqueFlyName(*this, STR_FRAME_DEFNAME, RES_FLYFRMFMT);
+}
+
+OUString SwDoc::GetUniqueShapeName() const
+{
+ return lcl_GetUniqueFlyName(*this, STR_SHAPE_DEFNAME, RES_DRAWFRMFMT);
+}
+
+OUString SwDoc::GetUniqueDrawObjectName() const
+{
+ return lcl_GetUniqueFlyName(*this, TranslateId(nullptr, "DrawObject"), RES_DRAWFRMFMT);
+}
+
+const SwFlyFrameFormat* SwDoc::FindFlyByName( const OUString& rName, SwNodeType nNdTyp ) const
+{
+ auto it = GetSpzFrameFormats()->findByTypeAndName( RES_FLYFRMFMT, rName );
+ if( it == GetSpzFrameFormats()->typeAndNameEnd() )
+ return nullptr;
+
+ const SwFrameFormat* pFlyFormat = *it;
+ assert( RES_FLYFRMFMT == pFlyFormat->Which() && pFlyFormat->GetName() == rName );
+ const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ if( nNdTyp != SwNodeType::NONE )
+ {
+ // query for the right NodeType
+ const SwNode* pNd = GetNodes()[ pIdx->GetIndex()+1 ];
+ if( nNdTyp == SwNodeType::Text
+ ? !pNd->IsNoTextNode()
+ : nNdTyp == pNd->GetNodeType() )
+ return static_cast<const SwFlyFrameFormat*>(pFlyFormat);
+ }
+ else
+ return static_cast<const SwFlyFrameFormat*>(pFlyFormat);
+ }
+ return nullptr;
+}
+
+void SwDoc::SetFlyName( SwFlyFrameFormat& rFormat, const OUString& rName )
+{
+ if (rFormat.GetName() == rName)
+ {
+ return;
+ }
+ OUString sName( rName );
+ if( sName.isEmpty() || FindFlyByName( sName ) )
+ {
+ TranslateId pTyp = STR_FRAME_DEFNAME;
+ const SwNodeIndex* pIdx = rFormat.GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() )
+ {
+ case SwNodeType::Grf:
+ pTyp = STR_GRAPHIC_DEFNAME;
+ break;
+ case SwNodeType::Ole:
+ pTyp = STR_OBJECT_DEFNAME;
+ break;
+ default: break;
+ }
+ }
+ sName = lcl_GetUniqueFlyName(*this, pTyp, RES_FLYFRMFMT);
+ }
+ rFormat.SetName( sName, true );
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetAllUniqueFlyNames()
+{
+ sal_Int32 n, nFlyNum = 0, nGrfNum = 0, nOLENum = 0;
+
+ const OUString sFlyNm(SwResId(STR_FRAME_DEFNAME));
+ const OUString sGrfNm(SwResId(STR_GRAPHIC_DEFNAME));
+ const OUString sOLENm(SwResId(STR_OBJECT_DEFNAME));
+
+ n = GetSpzFrameFormats()->size();
+ if( 255 < n )
+ n = 255;
+ SwFrameFormatsV aArr;
+ aArr.reserve( n );
+ SwFrameFormat* pFlyFormat;
+ bool bContainsAtPageObjWithContentAnchor = false;
+
+ for( n = GetSpzFrameFormats()->size(); n; )
+ {
+ pFlyFormat = (*GetSpzFrameFormats())[ --n ];
+ if( RES_FLYFRMFMT == pFlyFormat->Which() )
+ {
+ const OUString& aNm = pFlyFormat->GetName();
+ if ( !aNm.isEmpty() )
+ {
+ sal_Int32 *pNum = nullptr;
+ sal_Int32 nLen = 0;
+ if ( aNm.startsWith(sGrfNm) )
+ {
+ nLen = sGrfNm.getLength();
+ pNum = &nGrfNum;
+ }
+ else if( aNm.startsWith(sFlyNm) )
+ {
+ nLen = sFlyNm.getLength();
+ pNum = &nFlyNum;
+ }
+ else if( aNm.startsWith(sOLENm) )
+ {
+ nLen = sOLENm.getLength();
+ pNum = &nOLENum;
+ }
+
+ if ( pNum )
+ {
+ const sal_Int32 nNewLen = o3tl::toInt32(aNm.subView( nLen ));
+ if (*pNum < nNewLen)
+ *pNum = nNewLen;
+ }
+ }
+ else
+ // we want to set that afterwards
+ aArr.push_back( pFlyFormat );
+
+ }
+ if ( !bContainsAtPageObjWithContentAnchor )
+ {
+ const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
+ if ( (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) &&
+ rAnchor.GetContentAnchor() )
+ {
+ bContainsAtPageObjWithContentAnchor = true;
+ }
+ }
+ }
+ SetContainsAtPageObjWithContentAnchor( bContainsAtPageObjWithContentAnchor );
+
+ for( n = aArr.size(); n; )
+ {
+ pFlyFormat = aArr[ --n ];
+ const SwNodeIndex* pIdx = pFlyFormat->GetContent().GetContentIdx();
+ if( pIdx && pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ switch( GetNodes()[ pIdx->GetIndex() + 1 ]->GetNodeType() )
+ {
+ case SwNodeType::Grf:
+ pFlyFormat->SetName( sGrfNm + OUString::number( ++nGrfNum ));
+ break;
+ case SwNodeType::Ole:
+ pFlyFormat->SetName( sOLENm + OUString::number( ++nOLENum ));
+ break;
+ default:
+ pFlyFormat->SetName( sFlyNm + OUString::number( ++nFlyNum ));
+ break;
+ }
+ }
+ }
+ aArr.clear();
+
+ if( GetFootnoteIdxs().empty() )
+ return;
+
+ SwTextFootnote::SetUniqueSeqRefNo( *this );
+ // #i52775# Chapter footnotes did not get updated correctly.
+ // Calling UpdateAllFootnote() instead of UpdateFootnote() solves this problem,
+ // but I do not dare to call UpdateAllFootnote() in all cases: Safety first.
+ if ( FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
+ {
+ GetFootnoteIdxs().UpdateAllFootnote();
+ }
+ else
+ {
+ SwNodeIndex aTmp( GetNodes() );
+ GetFootnoteIdxs().UpdateFootnote( aTmp );
+ }
+}
+
+bool SwDoc::IsInHeaderFooter( const SwNodeIndex& rIdx ) const
+{
+ // That can also be a Fly in a Fly in the Header.
+ // Is also used by sw3io, to determine if a Redline object is
+ // in the Header or Footer.
+ // Because Redlines are also attached to Start and EndNode,
+ // the Index must not necessarily be from a ContentNode.
+ SwNode* pNd = &rIdx.GetNode();
+ const SwNode* pFlyNd = pNd->FindFlyStartNode();
+ while( pFlyNd )
+ {
+ // get up by using the Anchor
+#if OSL_DEBUG_LEVEL > 0
+ std::vector<const SwFrameFormat*> checkFormats;
+ for( auto pFormat : *GetSpzFrameFormats() )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ checkFormats.push_back( pFormat );
+ }
+#endif
+ std::vector<SwFrameFormat*> const & rFlys(pFlyNd->GetAnchoredFlys());
+ bool bFound(false);
+ for (size_t i = 0; i < rFlys.size(); ++i)
+ {
+ const SwFrameFormat *const pFormat = rFlys[i];
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ auto checkPos = std::find(
+ checkFormats.begin(), checkFormats.end(), pFormat );
+ assert( checkPos != checkFormats.end());
+ checkFormats.erase( checkPos );
+#endif
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()) ||
+ !rAnchor.GetContentAnchor() )
+ {
+ return false;
+ }
+
+ pNd = &rAnchor.GetContentAnchor()->nNode.GetNode();
+ pFlyNd = pNd->FindFlyStartNode();
+ bFound = true;
+ break;
+ }
+ }
+ if (!bFound)
+ {
+ OSL_ENSURE(mbInReading, "Found a FlySection but not a Format!");
+ return false;
+ }
+ }
+
+ return nullptr != pNd->FindHeaderStartNode() ||
+ nullptr != pNd->FindFooterStartNode();
+}
+
+SvxFrameDirection SwDoc::GetTextDirection( const SwPosition& rPos,
+ const Point* pPt ) const
+{
+ SvxFrameDirection nRet = SvxFrameDirection::Unknown;
+
+ SwContentNode *pNd = rPos.nNode.GetNode().GetContentNode();
+
+ // #i42921# - use new method <SwContentNode::GetTextDirection(..)>
+ if ( pNd )
+ {
+ nRet = pNd->GetTextDirection( rPos, pPt );
+ }
+ if ( nRet == SvxFrameDirection::Unknown )
+ {
+ const SvxFrameDirectionItem* pItem = nullptr;
+ if( pNd )
+ {
+ // Are we in a FlyFrame? Then look at that for the correct attribute
+ const SwFrameFormat* pFlyFormat = pNd->GetFlyFormat();
+ while( pFlyFormat )
+ {
+ pItem = &pFlyFormat->GetFrameDir();
+ if( SvxFrameDirection::Environment == pItem->GetValue() )
+ {
+ pItem = nullptr;
+ const SwFormatAnchor* pAnchor = &pFlyFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
+ pAnchor->GetContentAnchor())
+ {
+ pFlyFormat = pAnchor->GetContentAnchor()->nNode.
+ GetNode().GetFlyFormat();
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+ else
+ pFlyFormat = nullptr;
+ }
+
+ if( !pItem )
+ {
+ const SwPageDesc* pPgDsc = pNd->FindPageDesc();
+ if( pPgDsc )
+ pItem = &pPgDsc->GetMaster().GetFrameDir();
+ }
+ }
+ if( !pItem )
+ pItem = &GetAttrPool().GetDefaultItem( RES_FRAMEDIR );
+ nRet = pItem->GetValue();
+ }
+ return nRet;
+}
+
+bool SwDoc::IsInVerticalText( const SwPosition& rPos ) const
+{
+ const SvxFrameDirection nDir = GetTextDirection( rPos );
+ return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir;
+}
+
+o3tl::sorted_vector<SwRootFrame*> SwDoc::GetAllLayouts()
+{
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts;
+ SwViewShell *pStart = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if(pStart)
+ {
+ for(const SwViewShell& rShell : pStart->GetRingContainer())
+ {
+ if(rShell.GetLayout())
+ aAllLayouts.insert(rShell.GetLayout());
+ }
+ }
+ return aAllLayouts;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docnew.cxx b/sw/source/core/doc/docnew.cxx
new file mode 100644
index 000000000..1a9f7e868
--- /dev/null
+++ b/sw/source/core/doc/docnew.cxx
@@ -0,0 +1,1317 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <o3tl/sorted_vector.hxx>
+
+#include <doc.hxx>
+#include <proofreadingiterator.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XFlatParagraphIteratorProvider.hpp>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/random.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/XmlIdRegistry.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <sfx2/linkmgr.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <svl/numformat.hxx>
+#include <unotools/lingucfg.hxx>
+#include <svx/svdpage.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtfordr.hxx>
+#include <fmtpdsc.hxx>
+#include <pvprtdat.hxx>
+#include <rootfrm.hxx>
+#include <pagedesc.hxx>
+#include <ndtxt.hxx>
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <charfmt.hxx>
+#include <frmfmt.hxx>
+#include <poolfmt.hxx>
+#include <dbmgr.hxx>
+#include <docsh.hxx>
+#include <acorrect.hxx>
+#include <visiturl.hxx>
+#include <docary.hxx>
+#include <lineinfo.hxx>
+#include <drawdoc.hxx>
+#include <extinput.hxx>
+#include <viewsh.hxx>
+#include <doctxm.hxx>
+#include <shellres.hxx>
+#include <laycache.hxx>
+#include <mvsave.hxx>
+#include <istyleaccess.hxx>
+#include "swstylemanager.hxx"
+#include <IGrammarContact.hxx>
+#include <tblafmt.hxx>
+#include <MarkManager.hxx>
+#include <UndoManager.hxx>
+#include <DocumentDeviceManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentDrawModelManager.hxx>
+#include <DocumentChartDataProviderManager.hxx>
+#include <DocumentTimerManager.hxx>
+#include <DocumentLinksAdministrationManager.hxx>
+#include <DocumentListItemsManager.hxx>
+#include <DocumentListsManager.hxx>
+#include <DocumentOutlineNodesManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <DocumentFieldsManager.hxx>
+#include <DocumentStatisticsManager.hxx>
+#include <DocumentStateManager.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <DocumentStylePoolManager.hxx>
+#include <DocumentExternalDataManager.hxx>
+#include <wrtsh.hxx>
+#include <unocrsr.hxx>
+#include <fmthdft.hxx>
+#include <frameformats.hxx>
+
+#include <numrule.hxx>
+
+#include <sfx2/Metadatable.hxx>
+#include <fmtmeta.hxx>
+
+#include <svx/xfillit0.hxx>
+#include <unotools/configmgr.hxx>
+#include <i18nlangtag/mslangid.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::document;
+
+constexpr OUStringLiteral DEFAULT_CHAR_FORMAT_NAME = u"Character style";
+
+/*
+ * global functions...
+ */
+ uno::Reference< linguistic2::XProofreadingIterator > const & SwDoc::GetGCIterator() const
+{
+ if (!m_xGCIterator.is() && SvtLinguConfig().HasGrammarChecker())
+ {
+ uno::Reference< uno::XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ try
+ {
+ m_xGCIterator = sw::proofreadingiterator::get( xContext );
+ }
+ catch (const uno::Exception &)
+ {
+ OSL_FAIL( "No GCIterator" );
+ }
+ }
+
+ return m_xGCIterator;
+}
+
+bool SwDoc::StartGrammarChecking( bool bSkipStart )
+{
+ // check for a visible view
+ bool bVisible = false;
+ bool bStarted = false;
+ const SwDocShell *pDocShell = GetDocShell();
+ SfxViewFrame *pFrame = SfxViewFrame::GetFirst( pDocShell, false );
+ while (pFrame && !bVisible)
+ {
+ if (pFrame->IsVisible())
+ bVisible = true;
+ pFrame = SfxViewFrame::GetNext( *pFrame, pDocShell, false );
+ }
+
+ //!! only documents with visible views need to be checked
+ //!! (E.g. don't check temporary documents created for printing, see printing of notes and selections.
+ //!! Those get created on the fly and get hard deleted a bit later as well, and no one should have
+ //!! a UNO reference to them)
+ if (bVisible)
+ {
+ uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( GetGCIterator() );
+ if ( xGCIterator.is() )
+ {
+ uno::Reference< lang::XComponent > xDoc = GetDocShell()->GetBaseModel();
+ uno::Reference< text::XFlatParagraphIteratorProvider > xFPIP( xDoc, uno::UNO_QUERY );
+
+ // start automatic background checking if not active already
+ if ( xFPIP.is() && !xGCIterator->isProofreading( xDoc ) )
+ {
+ bStarted = true;
+ if ( !bSkipStart )
+ {
+ for (auto pLayout : GetAllLayouts())
+ { // we're starting it now, don't start grammar checker
+ // again until the user modifies the document
+ pLayout->SetNeedGrammarCheck(false);
+ }
+ xGCIterator->startProofreading( xDoc, xFPIP );
+ }
+ }
+ }
+ }
+
+ return bStarted;
+}
+
+/*
+ * internal functions
+ */
+static void lcl_DelFormatIndices( SwFormat const * pFormat )
+{
+ SwFormatContent &rFormatContent = const_cast<SwFormatContent&>(pFormat->GetContent());
+ if ( rFormatContent.GetContentIdx() )
+ rFormatContent.SetNewContentIdx( nullptr );
+ SwFormatAnchor &rFormatAnchor = const_cast<SwFormatAnchor&>(pFormat->GetAnchor());
+ if ( rFormatAnchor.GetContentAnchor() )
+ rFormatAnchor.SetAnchor( nullptr );
+}
+
+/*
+ * exported methods
+ */
+SwDoc::SwDoc()
+ : m_pNodes(new SwNodes(*this)),
+ mpAttrPool(new SwAttrPool(this)),
+ maOLEModifiedIdle( "sw::SwDoc maOLEModifiedIdle" ),
+ mpMarkManager(new ::sw::mark::MarkManager(*this)),
+ m_pMetaFieldManager(new ::sw::MetaFieldManager()),
+ m_pDocumentDrawModelManager( new ::sw::DocumentDrawModelManager( *this ) ),
+ m_pDocumentRedlineManager( new ::sw::DocumentRedlineManager( *this ) ),
+ m_pDocumentStateManager( new ::sw::DocumentStateManager( *this ) ),
+ m_pUndoManager(new ::sw::UndoManager(
+ std::shared_ptr<SwNodes>(new SwNodes(*this)), *m_pDocumentDrawModelManager, *m_pDocumentRedlineManager, *m_pDocumentStateManager)),
+ m_pDocumentSettingManager(new ::sw::DocumentSettingManager(*this)),
+ m_pDocumentChartDataProviderManager( new sw::DocumentChartDataProviderManager( *this ) ),
+ m_pDeviceAccess( new ::sw::DocumentDeviceManager( *this ) ),
+ m_pDocumentTimerManager( new ::sw::DocumentTimerManager( *this ) ),
+ m_pDocumentLinksAdministrationManager( new ::sw::DocumentLinksAdministrationManager( *this ) ),
+ m_pDocumentListItemsManager( new ::sw::DocumentListItemsManager() ),
+ m_pDocumentListsManager( new ::sw::DocumentListsManager( *this ) ),
+ m_pDocumentOutlineNodesManager( new ::sw::DocumentOutlineNodesManager( *this ) ),
+ m_pDocumentContentOperationsManager( new ::sw::DocumentContentOperationsManager( *this ) ),
+ m_pDocumentFieldsManager( new ::sw::DocumentFieldsManager( *this ) ),
+ m_pDocumentStatisticsManager( new ::sw::DocumentStatisticsManager( *this ) ),
+ m_pDocumentLayoutManager( new ::sw::DocumentLayoutManager( *this ) ),
+ m_pDocumentStylePoolManager( new ::sw::DocumentStylePoolManager( *this ) ),
+ m_pDocumentExternalDataManager( new ::sw::DocumentExternalDataManager ),
+ mpDfltFrameFormat( new SwFrameFormat( GetAttrPool(), "Frameformat", nullptr ) ),
+ mpEmptyPageFormat( new SwFrameFormat( GetAttrPool(), "Empty Page", mpDfltFrameFormat.get() ) ),
+ mpColumnContFormat( new SwFrameFormat( GetAttrPool(), "Columncontainer", mpDfltFrameFormat.get() ) ),
+ mpDfltCharFormat( new SwCharFormat( GetAttrPool(), DEFAULT_CHAR_FORMAT_NAME, nullptr ) ),
+ mpDfltTextFormatColl( new SwTextFormatColl( GetAttrPool(), "Paragraph style" ) ),
+ mpDfltGrfFormatColl( new SwGrfFormatColl( GetAttrPool(), "Graphikformatvorlage" ) ),
+ mpFrameFormatTable( new SwFrameFormats() ),
+ mpCharFormatTable( new SwCharFormats ),
+ mpSpzFrameFormatTable( new SwFrameFormats() ),
+ mpSectionFormatTable( new SwSectionFormats ),
+ mpTableFrameFormatTable( new SwFrameFormats() ),
+ mpTextFormatCollTable( new SwTextFormatColls() ),
+ mpGrfFormatCollTable( new SwGrfFormatColls() ),
+ mpTOXTypes( new SwTOXTypes ),
+ mpDefTOXBases( new SwDefTOXBase_Impl() ),
+ mpOutlineRule( nullptr ),
+ mpFootnoteInfo( new SwFootnoteInfo ),
+ mpEndNoteInfo( new SwEndNoteInfo ),
+ mpLineNumberInfo( new SwLineNumberInfo ),
+ mpFootnoteIdxs( new SwFootnoteIdxs ),
+ mpDocShell( nullptr ),
+ mpNumberFormatter( nullptr ),
+ mpNumRuleTable( new SwNumRuleTable ),
+ mpExtInputRing( nullptr ),
+ mpGrammarContact(createGrammarContact()),
+ mpCellStyles(new SwCellStyleTable),
+ mReferenceCount(0),
+ mbDtor(false),
+ mbCopyIsMove(false),
+ mbInReading(false),
+ mbInWriting(false),
+ mbInMailMerge(false),
+ mbInXMLImport(false),
+ mbInWriterfilterImport(false),
+ mbUpdateTOX(false),
+ mbInLoadAsynchron(false),
+ mbIsAutoFormatRedline(false),
+ mbOLEPrtNotifyPending(false),
+ mbAllOLENotify(false),
+ mbInsOnlyTextGlssry(false),
+ mbContains_MSVBasic(false),
+ mbClipBoard( false ),
+ mbColumnSelection( false ),
+ mbIsPrepareSelAll(false),
+ meDictionaryMissing( MissingDictionary::Undefined ),
+ mbContainsAtPageObjWithContentAnchor(false), //#i119292#, fdo#37024
+
+ meDocType(DOCTYPE_NATIVE)
+{
+ // The DrawingLayer ItemPool which is used as 2nd pool for Writer documents' pool
+ // has a default for the XFillStyleItem of XFILL_SOLID and the color for it is the default
+ // fill color (blue7 or similar). This is a problem, in Writer we want the default fill
+ // style to be drawing::FillStyle_NONE. This cannot simply be done by changing it in the 2nd pool at the
+ // pool defaults when the DrawingLayer ItemPool is used for Writer, that would lead to
+ // countless problems like DrawObjects initial fill and others.
+ // It is also hard to find all places where the initial ItemSets for Writer (including
+ // style hierarchies) are created and to always set (but only at the root) the FillStyle
+ // to NONE fixed; that will add that attribute to the file format. It will be hard to reset
+ // attribute sets (which is done at import and using UI). Also not a good solution.
+ // Luckily Writer uses pDfltTextFormatColl as default parent for all paragraphs and similar, thus
+ // it is possible to set this attribute here. It will be not reset when importing.
+ mpDfltTextFormatColl->SetFormatAttr(XFillStyleItem(drawing::FillStyle_NONE));
+ mpDfltFrameFormat->SetFormatAttr(XFillStyleItem(drawing::FillStyle_NONE));
+ // prevent paragraph default margins being applied to everything
+ mpDfltFrameFormat->SetFormatAttr(SvxULSpaceItem(RES_UL_SPACE));
+ mpDfltFrameFormat->SetFormatAttr(SvxLRSpaceItem(RES_LR_SPACE));
+
+ /*
+ * DefaultFormats and DefaultFormatCollections (FormatColl)
+ * are inserted at position 0 at the respective array.
+ * The formats in the FormatColls are derived from the
+ * DefaultFormats and are also in the list.
+ */
+ /* Formats */
+ mpFrameFormatTable->push_back(mpDfltFrameFormat.get());
+ mpCharFormatTable->insert(mpDfltCharFormat.get());
+
+ /* FormatColls */
+ // TXT
+ mpTextFormatCollTable->push_back(mpDfltTextFormatColl.get());
+ // GRF
+ mpGrfFormatCollTable->push_back(mpDfltGrfFormatColl.get());
+
+ // Create PageDesc, EmptyPageFormat and ColumnFormat
+ if (m_PageDescs.empty())
+ getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+
+ // Set to "Empty Page"
+ mpEmptyPageFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Fixed ) );
+ // Set BodyFormat for columns
+ mpColumnContFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ) );
+
+ GetDocumentFieldsManager().InitFieldTypes();
+
+ // Create a default OutlineNumRule (for Filters)
+ mpOutlineRule = new SwNumRule( SwNumRule::GetOutlineRuleName(),
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode(),
+ OUTLINE_RULE );
+ AddNumRule(mpOutlineRule);
+ // Counting of phantoms depends on <IsOldNumbering()>
+ mpOutlineRule->SetCountPhantoms( !GetDocumentSettingManager().get(DocumentSettingId::OLD_NUMBERING) );
+
+ new SwTextNode(
+ SwNodeIndex(GetUndoManager().GetUndoNodes().GetEndOfContent()),
+ mpDfltTextFormatColl.get() );
+ new SwTextNode( SwNodeIndex( GetNodes().GetEndOfContent() ),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+
+ maOLEModifiedIdle.SetPriority( TaskPriority::LOWEST );
+ maOLEModifiedIdle.SetInvokeHandler( LINK( this, SwDoc, DoUpdateModifiedOLE ));
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ // Create DBManager
+ m_pOwnDBManager.reset(new SwDBManager(this));
+ m_pDBManager = m_pOwnDBManager.get();
+#else
+ m_pDBManager = nullptr;
+#endif
+
+ // create TOXTypes
+ InitTOXTypes();
+
+ // pass empty item set containing the paragraph's list attributes
+ // as ignorable items to the stype manager.
+ {
+ SfxItemSetFixed<RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1> aIgnorableParagraphItems( GetAttrPool() );
+ mpStyleAccess = createStyleManager( &aIgnorableParagraphItems );
+ }
+
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ if (bHack)
+ {
+ mnRsid = 0;
+ }
+ else
+ {
+ // Initialize the session id of the current document to a random number
+ // smaller than 2^21.
+ mnRsid = comphelper::rng::uniform_uint_distribution(1, (1 << 21) - 1);
+ }
+ mnRsidRoot = mnRsid;
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ // Make sure that in case the document language is not set, then we don't return
+ // LANGUAGE_DONTKNOW, but the UI locale.
+ const SvtLinguConfig aLinguConfig;
+ SvtLinguOptions aOptions;
+ aLinguConfig.GetOptions(aOptions);
+ LanguageType eLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage,
+ i18n::ScriptType::LATIN);
+ SetLanguage(eLang, RES_CHRATR_LANGUAGE);
+ eLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK,
+ i18n::ScriptType::ASIAN);
+ SetLanguage(eLang, RES_CHRATR_CJK_LANGUAGE);
+ eLang = MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL,
+ i18n::ScriptType::COMPLEX);
+ SetLanguage(eLang, RES_CHRATR_CTL_LANGUAGE);
+ }
+
+ getIDocumentState().ResetModified();
+}
+
+/**
+ * Speciality: a member of the class SwDoc is located at
+ * position 0 in the array of the Format and GDI objects.
+ * This MUST not be destroyed using 'delete' in any case!
+ */
+SwDoc::~SwDoc()
+{
+ // nothing here should create Undo actions!
+ GetIDocumentUndoRedo().DoUndo(false);
+
+ if (mpDocShell)
+ {
+ mpDocShell->SetUndoManager(nullptr);
+ }
+
+ mpGrammarContact.reset();
+
+ getIDocumentTimerAccess().StopIdling(); // stop idle timer
+
+ mpURLStateChgd.reset();
+
+ // Deactivate Undo notification from Draw
+ if( GetDocumentDrawModelManager().GetDrawModel() )
+ {
+ GetDocumentDrawModelManager().DrawNotifyUndoHdl();
+ ClrContourCache();
+ }
+
+ m_pPgPViewPrtData.reset();
+
+ mbDtor = true;
+
+ //Clear the redline table before the nodes array is destroyed
+ getIDocumentRedlineAccess().GetRedlineTable().DeleteAndDestroyAll();
+ getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAndDestroyAll();
+
+ const sw::UnoCursorHint aHint;
+ cleanupUnoCursorTable();
+ for(const auto& pWeakCursor : mvUnoCursorTable)
+ {
+ auto pCursor(pWeakCursor.lock());
+ if(pCursor)
+ pCursor->m_aNotifier.Broadcast(aHint);
+ }
+ mpACEWord.reset();
+
+ // Release the BaseLinks
+ {
+ ::sfx2::SvLinkSources aTemp(getIDocumentLinksAdministration().GetLinkManager().GetServers());
+ for( const auto& rpLinkSrc : aTemp )
+ rpLinkSrc->Closed();
+
+ if( !getIDocumentLinksAdministration().GetLinkManager().GetLinks().empty() )
+ getIDocumentLinksAdministration().GetLinkManager().Remove( 0, getIDocumentLinksAdministration().GetLinkManager().GetLinks().size() );
+ }
+
+ // The ChapterNumbers/Numbers need to be deleted before the styles
+ // or we update all the time!
+ m_pNodes->m_pOutlineNodes->clear();
+ SwNodes & rUndoNodes( GetUndoManager().GetUndoNodes() );
+ rUndoNodes.m_pOutlineNodes->clear();
+
+ mpFootnoteIdxs->clear();
+
+ // indices could be registered in attributes
+ m_pUndoManager->DelAllUndoObj();
+
+ // The BookMarks contain indices to the Content. These must be deleted
+ // before deleting the Nodes.
+ mpMarkManager->clearAllMarks();
+
+ if( mpExtInputRing )
+ {
+ SwPaM* pTmp = mpExtInputRing;
+ mpExtInputRing = nullptr;
+ while( pTmp->GetNext() != pTmp )
+ delete pTmp->GetNext();
+ delete pTmp;
+ }
+
+ // Any of the FrameFormats can still have indices registered.
+ // These need to be destroyed now at the latest.
+ for( SwFrameFormat* pFormat : *mpFrameFormatTable )
+ lcl_DelFormatIndices( pFormat );
+ for( SwFrameFormat* pFormat : *mpSpzFrameFormatTable )
+ lcl_DelFormatIndices( pFormat );
+ for( SwSectionFormat* pFormat : *mpSectionFormatTable )
+ lcl_DelFormatIndices( pFormat );
+
+ // The formats/styles that follow depend on the default formats.
+ // Destroy these only after destroying the FormatIndices, because the content
+ // of headers/footers has to be deleted as well. If in the headers/footers
+ // there are still Flys registered at that point, we have a problem.
+ for( SwPageDesc *pPageDesc : m_PageDescs )
+ delete pPageDesc;
+ m_PageDescs.clear();
+
+ // Delete content selections.
+ // Don't wait for the SwNodes dtor to destroy them; so that Formats
+ // do not have any dependencies anymore.
+ m_pNodes->DelNodes( SwNodeIndex(*m_pNodes), m_pNodes->Count() );
+ rUndoNodes.DelNodes( SwNodeIndex( rUndoNodes ), rUndoNodes.Count() );
+
+ // clear TOX after nodes - TOXMarks are gone now so SwTOXType has no clients
+ for (const auto& pType : *mpTOXTypes)
+ {
+ pType->CallSwClientNotify(sw::DocumentDyingHint());
+ }
+ mpTOXTypes->clear();
+ mpDefTOXBases.reset();
+
+ // Delete Formats, make it permanent some time in the future
+
+ // Delete for Collections
+ // So that we get rid of the dependencies
+ mpFootnoteInfo->EndListeningAll();
+ mpEndNoteInfo->EndListeningAll();
+
+ assert(mpDfltTextFormatColl.get() == (*mpTextFormatCollTable)[0]
+ && "Default-Text-Collection must always be at the start");
+
+ // Optimization: Based on the fact that Standard is always 2nd in the
+ // array, we should delete it as the last. With this we avoid
+ // reparenting the Formats all the time!
+ if( 2 < mpTextFormatCollTable->size() )
+ mpTextFormatCollTable->DeleteAndDestroy(2, mpTextFormatCollTable->size());
+ mpTextFormatCollTable->DeleteAndDestroy(1, mpTextFormatCollTable->size());
+ mpTextFormatCollTable.reset();
+
+ assert(mpDfltGrfFormatColl.get() == (*mpGrfFormatCollTable)[0]
+ && "DefaultGrfCollection must always be at the start");
+
+ mpGrfFormatCollTable->DeleteAndDestroy(1, mpGrfFormatCollTable->size());
+ mpGrfFormatCollTable.reset();
+
+ // Without explicitly freeing the DocumentDeviceManager
+ // and relying on the implicit freeing there would be a crash
+ // due to it happening after SwAttrPool is freed.
+ m_pDeviceAccess.reset();
+
+ /*
+ * DefaultFormats and DefaultFormatCollections (FormatColl)
+ * are at position 0 of their respective arrays.
+ * In order to not be deleted by the array's dtor, we remove them
+ * now.
+ */
+ mpFrameFormatTable->erase( mpFrameFormatTable->begin() );
+
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ // On load, SwDBManager::setEmbeddedName() may register a data source.
+ // If we have an embedded one, then sDataSource points to the registered name, so revoke it here.
+ if (!m_pOwnDBManager->getEmbeddedName().isEmpty() && !maDBData.sDataSource.isEmpty())
+ {
+ // Remove the revoke listener here first, so that we don't remove the data source from the document.
+ m_pOwnDBManager->releaseRevokeListener();
+ SwDBManager::RevokeDataSource(maDBData.sDataSource);
+ SwDBManager::RevokeDataSource(m_pOwnDBManager->getEmbeddedName());
+ }
+ else if (!m_pOwnDBManager->getEmbeddedName().isEmpty())
+ {
+ // Remove the revoke listener here first, so that we don't remove the data source from the document.
+ m_pOwnDBManager->releaseRevokeListener();
+ // Remove connections which was committed but not used.
+ m_pOwnDBManager->RevokeNotUsedConnections();
+ }
+
+ m_pOwnDBManager.reset();
+#endif
+
+ // All Flys need to be destroyed before the Drawing Model,
+ // because Flys can still contain DrawContacts, when no
+ // Layout could be constructed due to a read error.
+ mpSpzFrameFormatTable->DeleteAndDestroyAll();
+
+ // Only now destroy the Model, the drawing objects - which are also
+ // contained in the Undo - need to remove their attributes from the
+ // Model. Also, DrawContacts could exist before this.
+ GetDocumentDrawModelManager().ReleaseDrawModel();
+ // Destroy DrawModel before the LinkManager, because it's always set
+ // in the DrawModel.
+ //The LinkManager gets destroyed automatically with m_pLinksAdministrationManager
+
+ // Clear the Tables before deleting the defaults, or we crash due to
+ // dependencies on defaults.
+ mpFrameFormatTable.reset();
+ mpSpzFrameFormatTable.reset();
+
+ mpStyleAccess.reset();
+
+ mpCharFormatTable.reset();
+ mpSectionFormatTable.reset();
+ mpTableFrameFormatTable.reset();
+ mpDfltTextFormatColl.reset();
+ mpDfltGrfFormatColl.reset();
+ mpNumRuleTable.reset();
+
+ disposeXForms(); // #i113606#, dispose the XForms objects
+
+ {
+ std::scoped_lock lock(mNumberFormatterMutex);
+ delete mpNumberFormatter; mpNumberFormatter= nullptr;
+ }
+ mpFootnoteInfo.reset();
+ mpEndNoteInfo.reset();
+ mpLineNumberInfo.reset();
+ mpFootnoteIdxs.reset();
+ mpTOXTypes.reset();
+ mpEmptyPageFormat.reset();
+ mpColumnContFormat.reset();
+ mpDfltCharFormat.reset();
+ mpDfltFrameFormat.reset();
+ mpLayoutCache.reset();
+ mpAttrPool.clear();
+}
+
+void SwDoc::SetDocShell( SwDocShell* pDSh )
+{
+ if( mpDocShell == pDSh )
+ return;
+
+ if (mpDocShell)
+ {
+ mpDocShell->SetUndoManager(nullptr);
+ }
+ mpDocShell = pDSh;
+ if (mpDocShell)
+ {
+ mpDocShell->SetUndoManager(& GetUndoManager());
+ GetUndoManager().SetDocShell(mpDocShell);
+ }
+
+ getIDocumentLinksAdministration().GetLinkManager().SetPersist( mpDocShell );
+
+ // set DocShell pointer also on DrawModel
+ InitDrawModelAndDocShell(mpDocShell, GetDocumentDrawModelManager().GetDrawModel());
+ assert(!GetDocumentDrawModelManager().GetDrawModel() ||
+ GetDocumentDrawModelManager().GetDrawModel()->GetPersist() == GetPersist());
+}
+
+// Convenience method; to avoid excessive includes from docsh.hxx
+uno::Reference < embed::XStorage > SwDoc::GetDocStorage()
+{
+ if( mpDocShell )
+ return mpDocShell->GetStorage();
+ if( getIDocumentLinksAdministration().GetLinkManager().GetPersist() )
+ return getIDocumentLinksAdministration().GetLinkManager().GetPersist()->GetStorage();
+ return nullptr;
+}
+
+SfxObjectShell* SwDoc::GetPersist() const
+{
+ return mpDocShell ? mpDocShell : getIDocumentLinksAdministration().GetLinkManager().GetPersist();
+}
+
+void SwDoc::ClearDoc()
+{
+ GetIDocumentUndoRedo().DelAllUndoObj();
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Deactivate Undo notification from Draw
+ if( GetDocumentDrawModelManager().GetDrawModel() )
+ {
+ GetDocumentDrawModelManager().DrawNotifyUndoHdl();
+ ClrContourCache();
+ }
+
+ // if there are still FlyFrames dangling around, delete them too
+ while ( !mpSpzFrameFormatTable->empty() )
+ getIDocumentLayoutAccess().DelLayoutFormat((*mpSpzFrameFormatTable)[mpSpzFrameFormatTable->size()-1]);
+ assert(!GetDocumentDrawModelManager().GetDrawModel()
+ || !GetDocumentDrawModelManager().GetDrawModel()->GetPage(0)->GetObjCount());
+
+ getIDocumentRedlineAccess().GetRedlineTable().DeleteAndDestroyAll();
+ getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAndDestroyAll();
+
+ mpACEWord.reset();
+
+ // The BookMarks contain indices to the Content. These must be deleted
+ // before deleting the Nodes.
+ mpMarkManager->clearAllMarks();
+ InitTOXTypes();
+
+ // create a dummy pagedesc for the layout
+ SwPageDesc* pDummyPgDsc = MakePageDesc("?DUMMY?");
+
+ SwNodeIndex aSttIdx( *GetNodes().GetEndOfContent().StartOfSectionNode(), 1 );
+ // create the first one over and over again (without attributes/style etc.
+ SwTextNode* pFirstNd = GetNodes().MakeTextNode( aSttIdx, mpDfltTextFormatColl.get() );
+
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ // set the layout to the dummy pagedesc
+ pFirstNd->SetAttr( SwFormatPageDesc( pDummyPgDsc ));
+
+ SwPosition aPos( *pFirstNd, SwIndex( pFirstNd ));
+ SwPaM const tmpPaM(aSttIdx, SwNodeIndex(GetNodes().GetEndOfContent()));
+ ::PaMCorrAbs(tmpPaM, aPos);
+ }
+
+ GetNodes().Delete( aSttIdx,
+ GetNodes().GetEndOfContent().GetIndex() - aSttIdx.GetIndex() );
+
+ // #i62440#
+ // destruction of numbering rules and creation of new outline rule
+ // *after* the document nodes are deleted.
+ mpOutlineRule = nullptr;
+ for( SwNumRule* pNumRule : *mpNumRuleTable )
+ {
+ getIDocumentListsAccess().deleteListForListStyle(pNumRule->GetName());
+ delete pNumRule;
+ }
+ mpNumRuleTable->clear();
+ maNumRuleMap.clear();
+
+ // creation of new outline numbering rule
+ mpOutlineRule = new SwNumRule( SwNumRule::GetOutlineRuleName(),
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode(),
+ OUTLINE_RULE );
+ AddNumRule(mpOutlineRule);
+ // Counting of phantoms depends on <IsOldNumbering()>
+ mpOutlineRule->SetCountPhantoms( !GetDocumentSettingManager().get(DocumentSettingId::OLD_NUMBERING) );
+
+ // remove the dummy pagedesc from the array and delete all the old ones
+ size_t nDummyPgDsc = 0;
+ if (FindPageDesc(pDummyPgDsc->GetName(), &nDummyPgDsc))
+ m_PageDescs.erase( nDummyPgDsc );
+ for( SwPageDesc *pPageDesc : m_PageDescs )
+ delete pPageDesc;
+ m_PageDescs.clear();
+
+ // Delete for Collections
+ // So that we get rid of the dependencies
+ mpFootnoteInfo->EndListeningAll();
+ mpEndNoteInfo->EndListeningAll();
+
+ // Optimization: Based on the fact that Standard is always 2nd in the
+ // array, we should delete it as the last. With this we avoid
+ // reparenting the Formats all the time!
+ if( 2 < mpTextFormatCollTable->size() )
+ mpTextFormatCollTable->DeleteAndDestroy(2, mpTextFormatCollTable->size());
+ mpTextFormatCollTable->DeleteAndDestroy(1, mpTextFormatCollTable->size());
+ mpGrfFormatCollTable->DeleteAndDestroy(1, mpGrfFormatCollTable->size());
+ mpCharFormatTable->DeleteAndDestroyAll(/*keepDefault*/true);
+
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ // search the FrameFormat of the root frm. This is not allowed to delete
+ mpFrameFormatTable->erase( getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout()->GetFormat() );
+ mpFrameFormatTable->DeleteAndDestroyAll( true );
+ mpFrameFormatTable->push_back( getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout()->GetFormat() );
+ }
+ else
+ mpFrameFormatTable->DeleteAndDestroyAll( true );
+
+ GetDocumentFieldsManager().ClearFieldTypes();
+
+ {
+ std::scoped_lock lock(mNumberFormatterMutex);
+ delete mpNumberFormatter; mpNumberFormatter= nullptr;
+ }
+
+ getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD );
+ pFirstNd->ChgFormatColl( getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+ nDummyPgDsc = m_PageDescs.size();
+ m_PageDescs.push_back( pDummyPgDsc );
+ // set the layout back to the new standard pagedesc
+ pFirstNd->ResetAllAttr();
+ // delete now the dummy pagedesc
+ DelPageDesc( nDummyPgDsc );
+}
+
+void SwDoc::SetPreviewPrtData( const SwPagePreviewPrtData* pNew )
+{
+ if( pNew )
+ {
+ if (m_pPgPViewPrtData)
+ {
+ *m_pPgPViewPrtData = *pNew;
+ }
+ else
+ {
+ m_pPgPViewPrtData.reset(new SwPagePreviewPrtData(*pNew));
+ }
+ }
+ else if (m_pPgPViewPrtData)
+ {
+ m_pPgPViewPrtData.reset();
+ }
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetOLEObjModified()
+{
+ if( getIDocumentLayoutAccess().GetCurrentViewShell() ) maOLEModifiedIdle.Start();
+}
+
+/** SwDoc: Reading and writing of the layout cache. */
+void SwDoc::ReadLayoutCache( SvStream& rStream )
+{
+ if( !mpLayoutCache )
+ mpLayoutCache.reset( new SwLayoutCache() );
+ if( !mpLayoutCache->IsLocked() )
+ {
+ mpLayoutCache->GetLockCount() |= 0x8000;
+ mpLayoutCache->Read( rStream );
+ mpLayoutCache->GetLockCount() &= 0x7fff;
+ }
+}
+
+void SwDoc::WriteLayoutCache( SvStream& rStream )
+{
+ SwLayoutCache::Write( rStream, *this );
+}
+
+IGrammarContact* getGrammarContact( const SwTextNode& rTextNode )
+{
+ const SwDoc& rDoc = rTextNode.GetDoc();
+ if (rDoc.IsInDtor())
+ return nullptr;
+ return rDoc.getGrammarContact();
+}
+
+::sfx2::IXmlIdRegistry&
+SwDoc::GetXmlIdRegistry()
+{
+ // UGLY: this relies on SetClipBoard being called before GetXmlIdRegistry!
+ if (!m_pXmlIdRegistry)
+ {
+ m_pXmlIdRegistry.reset( ::sfx2::createXmlIdRegistry( IsClipBoard() ) );
+ }
+ return *m_pXmlIdRegistry;
+}
+
+void SwDoc::InitTOXTypes()
+{
+ ShellResource* pShellRes = SwViewShell::GetShellRes();
+ SwTOXType* pNew = new SwTOXType(*this, TOX_CONTENT, pShellRes->aTOXContentName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_INDEX, pShellRes->aTOXIndexName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_USER, pShellRes->aTOXUserName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_ILLUSTRATIONS, pShellRes->aTOXIllustrationsName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_OBJECTS, pShellRes->aTOXObjectsName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_TABLES, pShellRes->aTOXTablesName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_AUTHORITIES, pShellRes->aTOXAuthoritiesName);
+ mpTOXTypes->emplace_back( pNew );
+ pNew = new SwTOXType(*this, TOX_CITATION, pShellRes->aTOXCitationName);
+ mpTOXTypes->emplace_back( pNew );
+}
+
+void SwDoc::ReplaceDefaults(const SwDoc& rSource)
+{
+ // copy property defaults
+ static const WhichRangesContainer aRangeOfDefaults(svl::Items<
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_PARATR_BEGIN, RES_PARATR_END-1,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+ XATTR_START, XATTR_END-1
+ >);
+
+ SfxItemSet aNewDefaults(GetAttrPool(), aRangeOfDefaults);
+
+ for (const WhichPair& rPair : aRangeOfDefaults)
+ {
+ for (sal_uInt16 nWhich = rPair.first;
+ nWhich <= rPair.second; ++nWhich)
+ {
+ const SfxPoolItem& rSourceAttr =
+ rSource.mpAttrPool->GetDefaultItem(nWhich);
+ if (rSourceAttr != mpAttrPool->GetDefaultItem(nWhich))
+ aNewDefaults.Put(rSourceAttr);
+ }
+ }
+
+ if (aNewDefaults.Count())
+ SetDefault(aNewDefaults);
+}
+
+void SwDoc::ReplaceCompatibilityOptions(const SwDoc& rSource)
+{
+ m_pDocumentSettingManager->ReplaceCompatibilityOptions(rSource.GetDocumentSettingManager());
+}
+
+#ifdef DBG_UTIL
+#define CNTNT_DOC( doc ) \
+ ((doc)->GetNodes().GetEndOfContent().GetIndex() - (doc)->GetNodes().GetEndOfExtras().GetIndex() - SwNodeOffset(2))
+#define CNTNT_IDX( idx ) \
+ ((idx).GetNode().GetIndex() - GetNodes().GetEndOfExtras().GetIndex() - 1)
+#endif
+
+SfxObjectShell* SwDoc::CreateCopy( bool bCallInitNew, bool bEmpty ) const
+{
+ SAL_INFO( "sw.pageframe", "(SwDoc::CreateCopy in" );
+ rtl::Reference<SwDoc> xRet( new SwDoc );
+
+ // we have to use pointer here, since the callee has to decide whether
+ // SfxObjectShellLock or SfxObjectShellRef should be used sometimes the
+ // object will be returned with refcount set to 0 ( if no DoInitNew is done )
+ SfxObjectShell* pRetShell = new SwDocShell( *xRet, SfxObjectCreateMode::STANDARD );
+ if( bCallInitNew )
+ {
+ // it could happen that DoInitNew creates model,
+ // that increases the refcount of the object
+ pRetShell->DoInitNew();
+ }
+
+ xRet->ReplaceDefaults(*this);
+
+ xRet->ReplaceCompatibilityOptions(*this);
+
+ xRet->ReplaceStyles(*this);
+
+ uno::Reference<beans::XPropertySet> const xThisSet(
+ GetDocShell()->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> const xRetSet(
+ pRetShell->GetBaseModel(), uno::UNO_QUERY_THROW);
+ uno::Sequence<beans::PropertyValue> aInteropGrabBag;
+ xThisSet->getPropertyValue("InteropGrabBag") >>= aInteropGrabBag;
+ xRetSet->setPropertyValue("InteropGrabBag", uno::Any(aInteropGrabBag));
+
+ if( !bEmpty )
+ {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.createcopy", "CC-Nd-Src: " << CNTNT_DOC( this ) );
+ SAL_INFO( "sw.createcopy", "CC-Nd: " << CNTNT_DOC( xRet ) );
+#endif
+ xRet->AppendDoc(*this, 0, bCallInitNew, 0, 0);
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.createcopy", "CC-Nd: " << CNTNT_DOC( xRet ) );
+#endif
+ }
+
+ // remove the temporary shell if it is there as it was done before
+ xRet->SetTmpDocShell( nullptr );
+
+ SAL_INFO( "sw.pageframe", "SwDoc::CreateCopy out)" );
+ return pRetShell;
+}
+
+// save bulk letters as single documents
+static OUString lcl_FindUniqueName(SwWrtShell* pTargetShell, std::u16string_view rStartingPageDesc, sal_uLong nDocNo )
+{
+ do
+ {
+ OUString sTest = rStartingPageDesc + OUString::number( nDocNo );
+ if( !pTargetShell->FindPageDescByName( sTest ) )
+ return sTest;
+ ++nDocNo;
+ }
+ while( true );
+}
+
+/** Returns whether the passed SwPageDesc& or any of its (transitive) follows
+ contains a header or footer. */
+static bool lcl_PageDescOrFollowContainsHeaderFooter(const SwPageDesc& rPageDesc)
+{
+ // remember already checked page descs to avoid cycle
+ o3tl::sorted_vector<const SwPageDesc*> aCheckedPageDescs;
+ const SwPageDesc* pCurPageDesc = &rPageDesc;
+ while (aCheckedPageDescs.count(pCurPageDesc) == 0)
+ {
+ const SwFrameFormat& rMaster = pCurPageDesc->GetMaster();
+ if (rMaster.GetHeader().IsActive() || rMaster.GetFooter().IsActive())
+ return true;
+
+ aCheckedPageDescs.insert(pCurPageDesc);
+ pCurPageDesc = pCurPageDesc->GetFollow();
+ }
+ return false;
+}
+
+static void lcl_CopyFollowPageDesc(
+ SwWrtShell& rTargetShell,
+ const SwPageDesc& rSourcePageDesc,
+ const SwPageDesc& rTargetPageDesc,
+ const sal_uLong nDocNo )
+{
+ //now copy the follow page desc, too
+ // note: these may at any point form a cycle, so a loop is needed and it
+ // must be detected that the last iteration closes the cycle and doesn't
+ // copy the first page desc of the cycle again.
+ std::map<OUString, OUString> followMap{ { rSourcePageDesc.GetName(), rTargetPageDesc.GetName() } };
+ SwPageDesc const* pCurSourcePageDesc(&rSourcePageDesc);
+ SwPageDesc const* pCurTargetPageDesc(&rTargetPageDesc);
+ do
+ {
+ const SwPageDesc* pFollowPageDesc = pCurSourcePageDesc->GetFollow();
+ OUString sFollowPageDesc = pFollowPageDesc->GetName();
+ if (sFollowPageDesc == pCurSourcePageDesc->GetName())
+ {
+ break;
+ }
+ SwDoc* pTargetDoc = rTargetShell.GetDoc();
+ SwPageDesc* pTargetFollowPageDesc(nullptr);
+ auto const itMapped(followMap.find(sFollowPageDesc));
+ if (itMapped == followMap.end())
+ {
+ OUString sNewFollowPageDesc = lcl_FindUniqueName(&rTargetShell, sFollowPageDesc, nDocNo);
+ pTargetFollowPageDesc = pTargetDoc->MakePageDesc(sNewFollowPageDesc);
+ pTargetDoc->CopyPageDesc(*pFollowPageDesc, *pTargetFollowPageDesc, false);
+ }
+ else
+ {
+ pTargetFollowPageDesc = pTargetDoc->FindPageDesc(itMapped->second);
+ }
+ SwPageDesc aDesc(*pCurTargetPageDesc);
+ aDesc.SetFollow(pTargetFollowPageDesc);
+ pTargetDoc->ChgPageDesc(pCurTargetPageDesc->GetName(), aDesc);
+ if (itMapped != followMap.end())
+ {
+ break; // was already copied
+ }
+ pCurSourcePageDesc = pCurSourcePageDesc->GetFollow();
+ pCurTargetPageDesc = pTargetFollowPageDesc;
+ followMap[pCurSourcePageDesc->GetName()] = pCurTargetPageDesc->GetName();
+ }
+ while (true);
+}
+
+// appends all pages of source SwDoc - based on SwFEShell::Paste( SwDoc* )
+SwNodeIndex SwDoc::AppendDoc(const SwDoc& rSource, sal_uInt16 const nStartPageNumber,
+ bool const bDeletePrevious, int pageOffset, const sal_uLong nDocNo)
+{
+ SAL_INFO( "sw.pageframe", "(SwDoc::AppendDoc in " << bDeletePrevious );
+
+ // GetEndOfExtras + 1 = StartOfContent == no content node!
+ // This ensures it won't be merged in the SwTextNode at the position.
+ SwNodeIndex aSourceIdx( rSource.GetNodes().GetEndOfExtras(), 1 );
+ // CopyRange works on the range a [mark, point[ and considers an
+ // index < point outside the selection.
+ // @see IDocumentContentOperations::CopyRange
+ SwNodeIndex aSourceEndIdx( rSource.GetNodes().GetEndOfContent(), 0 );
+ SwPaM aCpyPam( aSourceIdx, aSourceEndIdx );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceIdx.GetNode().GetIndex() );
+ aSourceIdx++;
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceIdx.GetNode().GetIndex() );
+ if ( aSourceIdx.GetNode().GetNodeType() != SwNodeType::End ) {
+ aSourceIdx++;
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceIdx.GetNode().GetNodeType()) << std::dec );
+ aSourceIdx--;
+ }
+ aSourceIdx--;
+ SAL_INFO( "sw.docappend", ".." );
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceEndIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceEndIdx.GetNode().GetIndex() );
+ SAL_INFO( "sw.docappend", "NodeType 0x" << std::hex << static_cast<int>(aSourceEndIdx.GetNode().GetNodeType())
+ << std::dec << " " << aSourceEndIdx.GetNode().GetIndex() );
+ SAL_INFO( "sw.docappend", "Src-Nd: " << CNTNT_DOC( &rSource ) );
+ SAL_INFO( "sw.docappend", "Nd: " << CNTNT_DOC( this ) );
+#endif
+
+ SwWrtShell* pTargetShell = GetDocShell()->GetWrtShell();
+ SwPageDesc* pTargetPageDesc = nullptr;
+
+ if ( pTargetShell ) {
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Has target write shell" );
+#endif
+ pTargetShell->StartAllAction();
+
+ if( nDocNo > 0 )
+ {
+ // #i72517# put the styles to the target document
+ // if the source uses headers or footers the target document
+ // needs individual page styles
+ const SwWrtShell *pSourceShell = rSource.GetDocShell()->GetWrtShell();
+ const SwPageDesc& rSourcePageDesc = pSourceShell->GetPageDesc(
+ pSourceShell->GetCurPageDesc());
+ const OUString sStartingPageDesc = rSourcePageDesc.GetName();
+ const bool bPageStylesWithHeaderFooter = lcl_PageDescOrFollowContainsHeaderFooter(rSourcePageDesc);
+ if( bPageStylesWithHeaderFooter )
+ {
+ // create a new pagestyle
+ // copy the pagedesc from the current document to the new
+ // document and change the name of the to-be-applied style
+ OUString sNewPageDescName = lcl_FindUniqueName(pTargetShell, sStartingPageDesc, nDocNo );
+ pTargetPageDesc = MakePageDesc( sNewPageDescName );
+ if( pTargetPageDesc )
+ {
+ CopyPageDesc( rSourcePageDesc, *pTargetPageDesc, false );
+ lcl_CopyFollowPageDesc( *pTargetShell, rSourcePageDesc, *pTargetPageDesc, nDocNo );
+ }
+ }
+ else
+ pTargetPageDesc = pTargetShell->FindPageDescByName( sStartingPageDesc );
+ }
+
+ // Otherwise we have to handle SwPlaceholderNodes as first node
+ if ( pTargetPageDesc )
+ {
+ SwNodeIndex aBreakIdx( GetNodes().GetEndOfContent(), -1 );
+ SwPosition aBreakPos( aBreakIdx );
+ // insert new node - will be removed at the end...
+ // (don't SplitNode() as it may move flys to the wrong node)
+ getIDocumentContentOperations().AppendTextNode(aBreakPos);
+ SwFormatPageDesc pageDesc(pTargetPageDesc);
+ pageDesc.SetNumOffset(nStartPageNumber);
+ // set break on the last paragraph
+ getIDocumentContentOperations().InsertPoolItem(SwPaM(aBreakPos),
+ pageDesc, SetAttrMode::DEFAULT, pTargetShell->GetLayout());
+ // tdf#148309 move to the last node - so that the "flush page break"
+ // code below will format the frame of the node with the page break,
+ // which is required for new page frames to be created! Else layout
+ // performance will be terrible.
+ pTargetShell->SttEndDoc(false);
+
+ // There is now a new empty text node on the new page. If it has
+ // any marks, those are from the previous page: move them back
+ // there, otherwise later we can't delete that empty text node.
+ SwNodeIndex aNodeIndex(GetNodes().GetEndOfContent(), -1);
+ if (SwTextNode* pTextNode = aNodeIndex.GetNode().GetTextNode())
+ {
+ // Position of the last paragraph on the previous page.
+ --aNodeIndex;
+ SwPaM aPaM(aNodeIndex);
+ // Collect the marks starting or ending at this text node.
+ o3tl::sorted_vector<sw::mark::IMark*> aSeenMarks;
+ IDocumentMarkAccess* pMarkAccess = getIDocumentMarkAccess();
+ for (const SwIndex* pIndex = pTextNode->GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
+ {
+ sw::mark::IMark* pMark = const_cast<sw::mark::IMark*>(pIndex->GetMark());
+ if (!pMark)
+ continue;
+ if (!aSeenMarks.insert(pMark).second)
+ continue;
+ }
+ // And move them back.
+ for (sw::mark::IMark* pMark : aSeenMarks)
+ pMarkAccess->repositionMark(pMark, aPaM);
+ }
+
+ // Flush the page break, if we want to keep it
+ if ( !bDeletePrevious )
+ {
+ SAL_INFO( "sw.pageframe", "(Flush pagebreak AKA EndAllAction" );
+ assert(pTargetShell->GetCursor()->GetPoint()->nNode.GetNode().GetTextNode()->GetSwAttrSet().HasItem(RES_PAGEDESC));
+ pTargetShell->EndAllAction();
+ SAL_INFO( "sw.pageframe", "Flush changes AKA EndAllAction)" );
+ pTargetShell->StartAllAction();
+ }
+ }
+ }
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Nd: " << CNTNT_DOC( this ) );
+#endif
+
+ // -1, otherwise aFixupIdx would move to new EOC
+ SwNodeIndex aFixupIdx( GetNodes().GetEndOfContent(), -1 );
+
+ // append at the end of document / content
+ SwNodeIndex aTargetIdx( GetNodes().GetEndOfContent() );
+ SwPaM aInsertPam( aTargetIdx );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Pam-Nd: " << aCpyPam.GetNode().GetIndex() - aCpyPam.GetNode( false ).GetIndex() + 1
+ << " (0x" << std::hex << static_cast<int>(aCpyPam.GetNode( false ).GetNodeType()) << std::dec
+ << " " << aCpyPam.GetNode( false ).GetIndex()
+ << " - 0x" << std::hex << static_cast<int>(aCpyPam.GetNode().GetNodeType()) << std::dec
+ << " " << aCpyPam.GetNode().GetIndex() << ")" );
+#endif
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr );
+ getIDocumentFieldsAccess().LockExpFields();
+
+ // Position where the appended doc starts. Will be filled in later.
+ // Initially uses GetEndOfContent() because SwNodeIndex has no default ctor.
+ SwNodeIndex aStartAppendIndex( GetNodes().GetEndOfContent() );
+
+ {
+ // **
+ // ** refer to SwFEShell::Paste, if you change the following code **
+ // **
+
+ SwPosition& rInsPos = *aInsertPam.GetPoint();
+
+ {
+ SwNodeIndex aIndexBefore(rInsPos.nNode);
+
+ aIndexBefore--;
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "CopyRange In: " << CNTNT_DOC( this ) );
+#endif
+ rSource.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CopyAll|SwCopyFlags::CheckPosInFly);
+ // Note: aCpyPam is invalid now
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "CopyRange Out: " << CNTNT_DOC( this ) );
+#endif
+
+ ++aIndexBefore;
+ SwPaM aPaM(SwPosition(aIndexBefore),
+ SwPosition(rInsPos.nNode));
+
+ aPaM.GetDoc().MakeUniqueNumRules(aPaM);
+
+ // Update the rsid of each pasted text node
+ SwNodes &rDestNodes = GetNodes();
+ SwNodeOffset const nEndIdx = aPaM.End()->nNode.GetIndex();
+
+ for (SwNodeOffset nIdx = aPaM.Start()->nNode.GetIndex();
+ nIdx <= nEndIdx; ++nIdx)
+ {
+ SwTextNode *const pTextNode = rDestNodes[nIdx]->GetTextNode();
+ if ( pTextNode )
+ UpdateParRsid( pTextNode );
+ }
+ }
+
+ {
+ SwNodeOffset iDelNodes(0);
+ SwNodeIndex aDelIdx( aFixupIdx );
+
+ // we just need to set the new page description and reset numbering
+ // this keeps all other settings as in the pasted document
+ if ( nStartPageNumber || pTargetPageDesc ) {
+ std::unique_ptr<SfxPoolItem> pNewItem;
+ SwTextNode *aTextNd = nullptr;
+ SwFormat *pFormat = nullptr;
+
+ // find the first node allowed to contain a RES_PAGEDESC
+ while (true) {
+ aFixupIdx++;
+
+ SwNode &node = aFixupIdx.GetNode();
+ if ( node.IsTextNode() ) {
+ // every document contains at least one text node!
+ aTextNd = node.GetTextNode();
+ pNewItem.reset(aTextNd->GetAttr( RES_PAGEDESC ).Clone());
+ break;
+ }
+ else if ( node.IsTableNode() ) {
+ pFormat = node.GetTableNode()->GetTable().GetFrameFormat();
+ pNewItem.reset(pFormat->GetFormatAttr( RES_PAGEDESC ).Clone());
+ break;
+ }
+ }
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Idx Del " << CNTNT_IDX( aDelIdx ) );
+ SAL_INFO( "sw.docappend", "Idx Fix " << CNTNT_IDX( aFixupIdx ) );
+#endif
+ // just update the original instead of overwriting
+ SwFormatPageDesc *aDesc = static_cast< SwFormatPageDesc* >( pNewItem.get() );
+#ifdef DBG_UTIL
+ if ( aDesc->GetPageDesc() )
+ SAL_INFO( "sw.docappend", "PD Update " << aDesc->GetPageDesc()->GetName() );
+ else
+ SAL_INFO( "sw.docappend", "PD New" );
+#endif
+ if ( nStartPageNumber )
+ aDesc->SetNumOffset( nStartPageNumber );
+ if ( pTargetPageDesc )
+ aDesc->RegisterToPageDesc( *pTargetPageDesc );
+ if ( aTextNd )
+ aTextNd->SetAttr( *aDesc );
+ else
+ pFormat->SetFormatAttr( *aDesc );
+
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "Idx " << CNTNT_IDX( aDelIdx ) );
+#endif
+ iDelNodes++;
+ }
+
+ if ( bDeletePrevious )
+ iDelNodes++;
+
+ if ( iDelNodes ) {
+ // delete leading empty page(s), e.g. from InsertPageBreak or
+ // new SwDoc. this has to be done before copying the page bound
+ // frames, otherwise the drawing layer gets confused.
+ if ( pTargetShell )
+ pTargetShell->SttEndDoc( false );
+ aDelIdx -= iDelNodes - 1;
+#ifdef DBG_UTIL
+ SAL_INFO( "sw.docappend", "iDelNodes: " << iDelNodes
+ << " Idx: " << aDelIdx.GetNode().GetIndex()
+ << " EOE: " << GetNodes().GetEndOfExtras().GetIndex() );
+#endif
+ GetNodes().Delete( aDelIdx, iDelNodes );
+ aStartAppendIndex = aFixupIdx;
+ }
+ else
+ {
+ aStartAppendIndex = aFixupIdx;
+ ++aStartAppendIndex;
+ }
+ }
+
+ // finally copy page bound frames
+ for ( auto pCpyFormat : *rSource.GetSpzFrameFormats() )
+ {
+ const SwFrameFormat& rCpyFormat = *pCpyFormat;
+ SwFormatAnchor aAnchor( rCpyFormat.GetAnchor() );
+ if (RndStdIds::FLY_AT_PAGE != aAnchor.GetAnchorId())
+ continue;
+ SAL_INFO( "sw.docappend", "PaAn: " << aAnchor.GetPageNum()
+ << " => " << aAnchor.GetPageNum() + pageOffset );
+ if ( pageOffset != 0 )
+ aAnchor.SetPageNum( aAnchor.GetPageNum() + pageOffset );
+ getIDocumentLayoutAccess().CopyLayoutFormat( rCpyFormat, aAnchor, true, true );
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr );
+
+ getIDocumentFieldsAccess().UnlockExpFields();
+ getIDocumentFieldsAccess().UpdateFields(false);
+
+ if ( pTargetShell )
+ pTargetShell->EndAllAction();
+
+ SAL_INFO( "sw.pageframe", "SwDoc::AppendDoc out)" );
+ return aStartAppendIndex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docnum.cxx b/sw/source/core/doc/docnum.cxx
new file mode 100644
index 000000000..f9df32cd2
--- /dev/null
+++ b/sw/source/core/doc/docnum.cxx
@@ -0,0 +1,2682 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <UndoCore.hxx>
+#include <UndoRedline.hxx>
+#include <UndoNumbering.hxx>
+#include <swundo.hxx>
+#include <SwUndoFmt.hxx>
+#include <rolbck.hxx>
+#include <paratr.hxx>
+#include <docary.hxx>
+#include <mvsave.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <redline.hxx>
+#include <strings.hrc>
+#include <SwNodeNum.hxx>
+#include <list.hxx>
+#include <calbck.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/random.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <tools/datetimeutils.hxx>
+
+#include <map>
+#include <stdlib.h>
+
+#include <wrtsh.hxx>
+
+namespace {
+ void lcl_ResetIndentAttrs(SwDoc *pDoc, const SwPaM &rPam, sal_uInt16 marker,
+ SwRootFrame const*const pLayout)
+ {
+ const o3tl::sorted_vector<sal_uInt16> aResetAttrsArray{ marker };
+ // #i114929#
+ // On a selection setup a corresponding Point-and-Mark in order to get
+ // the indentation attribute reset on all paragraphs touched by the selection
+ if ( rPam.HasMark() &&
+ rPam.End()->nNode.GetNode().GetTextNode() )
+ {
+ SwPaM aPam( rPam.Start()->nNode,
+ rPam.End()->nNode );
+ aPam.Start()->nContent = 0;
+ aPam.End()->nContent = rPam.End()->nNode.GetNode().GetTextNode()->Len();
+ pDoc->ResetAttrs( aPam, false, aResetAttrsArray, true, pLayout );
+ }
+ else
+ {
+ pDoc->ResetAttrs( rPam, false, aResetAttrsArray, true, pLayout );
+ }
+ }
+
+ void ExpandPamForParaPropsNodes(SwPaM& rPam, SwRootFrame const*const pLayout)
+ {
+ if (!pLayout)
+ return;
+
+ // ensure that selection from the Shell includes the para-props node
+ // to which the attributes should be applied
+ if (rPam.GetPoint()->nNode.GetNode().IsTextNode())
+ {
+ rPam.GetPoint()->nNode = *sw::GetParaPropsNode(*pLayout, rPam.GetPoint()->nNode);
+ rPam.GetPoint()->nContent.Assign(rPam.GetPoint()->nNode.GetNode().GetContentNode(), 0);
+ }
+ if (rPam.GetMark()->nNode.GetNode().IsTextNode())
+ {
+ rPam.GetMark()->nNode = *sw::GetParaPropsNode(*pLayout, rPam.GetMark()->nNode);
+ rPam.GetMark()->nContent.Assign(rPam.GetMark()->nNode.GetNode().GetContentNode(), 0);
+ }
+ }
+}
+
+static sal_uInt8 GetUpperLvlChg( sal_uInt8 nCurLvl, sal_uInt8 nLevel, sal_uInt16 nMask )
+{
+ if( 1 < nLevel )
+ {
+ if( nCurLvl + 1 >= nLevel )
+ nCurLvl -= nLevel - 1;
+ else
+ nCurLvl = 0;
+ }
+ return static_cast<sal_uInt8>((nMask - 1) & ~(( 1 << nCurLvl ) - 1));
+}
+
+void SwDoc::SetOutlineNumRule( const SwNumRule& rRule )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_EDIT, nullptr);
+ if (mpOutlineRule)
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoOutlineEdit>(*mpOutlineRule, rRule, *this));
+ }
+ }
+
+ if( mpOutlineRule )
+ (*mpOutlineRule) = rRule;
+ else
+ {
+ mpOutlineRule = new SwNumRule( rRule );
+
+ AddNumRule(mpOutlineRule); // #i36749#
+ }
+
+ mpOutlineRule->SetRuleType( OUTLINE_RULE );
+ mpOutlineRule->SetName(SwNumRule::GetOutlineRuleName(), getIDocumentListsAccess());
+
+ // assure that the outline numbering rule is an automatic rule
+ mpOutlineRule->SetAutoRule( true );
+
+ // test whether the optional CharFormats are defined in this Document
+ mpOutlineRule->CheckCharFormats( *this );
+
+ // notify text nodes, which are registered at the outline style, about the
+ // changed outline style
+ SwNumRule::tTextNodeList aTextNodeList;
+ mpOutlineRule->GetTextNodeList( aTextNodeList );
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ pTextNd->NumRuleChgd();
+
+ // assure that list level corresponds to outline level
+ if ( pTextNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle() &&
+ pTextNd->GetAttrListLevel() != pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() )
+ {
+ pTextNd->SetAttrListLevel( pTextNd->GetTextColl()->GetAssignedOutlineStyleLevel() );
+ }
+ }
+
+ PropagateOutlineRule();
+ mpOutlineRule->SetInvalidRule(true);
+ UpdateNumRule();
+
+ // update if we have foot notes && numbering by chapter
+ if( !GetFootnoteIdxs().empty() && FTNNUM_CHAPTER == GetFootnoteInfo().m_eNum )
+ GetFootnoteIdxs().UpdateAllFootnote();
+
+ getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_EDIT, nullptr);
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::PropagateOutlineRule()
+{
+ SwNumRule* pMyOutlineRule = GetOutlineNumRule();
+ if (!pMyOutlineRule)
+ return;
+
+ for (auto pColl : *mpTextFormatCollTable)
+ {
+ if(pColl->IsAssignedToListLevelOfOutlineStyle())
+ {
+ // Check only the list style, which is set at the paragraph style
+ const SwNumRuleItem & rCollRuleItem = pColl->GetNumRule( false );
+
+ if ( rCollRuleItem.GetValue().isEmpty() )
+ {
+ SwNumRuleItem aNumItem( pMyOutlineRule->GetName() );
+ pColl->SetFormatAttr(aNumItem);
+ }
+ }
+ }
+}
+
+// Increase/Decrease
+bool SwDoc::OutlineUpDown(const SwPaM& rPam, short nOffset,
+ SwRootFrame const*const pLayout)
+{
+ if( GetNodes().GetOutLineNds().empty() || !nOffset )
+ return false;
+
+ // calculate the range
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+ const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
+ SwNode* const pSttNd = &aPam.Start()->nNode.GetNode();
+ SwNode* const pEndNd = &aPam.End()->nNode.GetNode();
+ SwOutlineNodes::size_type nSttPos, nEndPos;
+
+ if( !rOutlNds.Seek_Entry( pSttNd, &nSttPos ) &&
+ !nSttPos-- )
+ // we're not in an "Outline section"
+ return false;
+
+ if( rOutlNds.Seek_Entry( pEndNd, &nEndPos ) )
+ ++nEndPos;
+
+ // We now have the wanted range in the OutlineNodes array,
+ // so check now if we're not invalidating sublevels
+ // (stepping over the limits)
+
+ // Here we go:
+ // 1. Create the style array:
+ SwTextFormatColl* aCollArr[ MAXLEVEL ];
+ memset( aCollArr, 0, sizeof( SwTextFormatColl* ) * MAXLEVEL );
+
+ for( auto pTextFormatColl : *mpTextFormatCollTable )
+ {
+ if (pTextFormatColl->IsAssignedToListLevelOfOutlineStyle())
+ {
+ const int nLevel = pTextFormatColl->GetAssignedOutlineStyleLevel();
+ aCollArr[ nLevel ] = pTextFormatColl;
+ }
+ }
+
+ int n;
+
+ /* Find the last occupied level (backward). */
+ for (n = MAXLEVEL - 1; n > 0; n--)
+ {
+ if (aCollArr[n] != nullptr)
+ break;
+ }
+
+ /* If an occupied level is found, choose next level (which IS
+ unoccupied) until a valid level is found. If no occupied level
+ was found n is 0 and aCollArr[0] is 0. In this case no demoting
+ is possible. */
+ if (aCollArr[n] != nullptr)
+ {
+ while (n < MAXLEVEL - 1)
+ {
+ n++;
+
+ SwTextFormatColl *aTmpColl =
+ getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));
+
+ if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
+ aTmpColl->GetAssignedOutlineStyleLevel() == n )
+ {
+ aCollArr[n] = aTmpColl;
+ break;
+ }
+ }
+ }
+
+ /* Find the first occupied level (forward). */
+ for (n = 0; n < MAXLEVEL - 1; n++)
+ {
+ if (aCollArr[n] != nullptr)
+ break;
+ }
+
+ /* If an occupied level is found, choose previous level (which IS
+ unoccupied) until a valid level is found. If no occupied level
+ was found n is MAXLEVEL - 1 and aCollArr[MAXLEVEL - 1] is 0. In
+ this case no demoting is possible. */
+ if (aCollArr[n] != nullptr)
+ {
+ while (n > 0)
+ {
+ n--;
+
+ SwTextFormatColl *aTmpColl =
+ getIDocumentStylePoolAccess().GetTextCollFromPool(o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));
+
+ if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
+ aTmpColl->GetAssignedOutlineStyleLevel() == n )
+ {
+ aCollArr[n] = aTmpColl;
+ break;
+ }
+ }
+ }
+
+ /* --> #i13747#
+
+ Build a move table that states from which level to which other level
+ an outline will be moved.
+
+ the move table:
+ aMoveArr[n] = m: replace aCollArr[n] with aCollArr[m]
+ */
+ int aMoveArr[MAXLEVEL];
+ int nStep; // step size for searching in aCollArr: -1 or 1
+ int nNum; // amount of steps for stepping in aCollArr
+
+ if (nOffset < 0)
+ {
+ nStep = -1;
+ nNum = -nOffset;
+ }
+ else
+ {
+ nStep = 1;
+ nNum = nOffset;
+ }
+
+ /* traverse aCollArr */
+ for (n = 0; n < MAXLEVEL; n++)
+ {
+ /* If outline level n has an assigned paragraph style step
+ nNum steps forwards (nStep == 1) or backwards (nStep ==
+ -1). One step is to go to the next non-null entry in
+ aCollArr in the selected direction. If nNum steps were
+ possible write the index of the entry found to aCollArr[n],
+ i.e. outline level n will be replaced by outline level
+ aCollArr[n].
+
+ If outline level n has no assigned paragraph style
+ aMoveArr[n] is set to -1.
+ */
+ if (aCollArr[n] != nullptr)
+ {
+ int m = n;
+ int nCount = nNum;
+
+ while (nCount > 0 && m + nStep >= 0 && m + nStep < MAXLEVEL)
+ {
+ m += nStep;
+
+ if (aCollArr[m] != nullptr)
+ nCount--;
+ }
+
+ if (nCount == 0)
+ aMoveArr[n] = m;
+ else
+ aMoveArr[n] = -1;
+ }
+ else
+ aMoveArr[n] = -1;
+ }
+
+ /* If moving of the outline levels is applicable, i.e. for all
+ outline levels occurring in the document there has to be a valid
+ target outline level implied by aMoveArr. */
+ bool bMoveApplicable = true;
+ for (auto i = nSttPos; i < nEndPos; ++i)
+ {
+ SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode();
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
+ {
+ continue;
+ }
+ SwTextFormatColl* pColl = pTextNd->GetTextColl();
+
+ if( pColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ const int nLevel = pColl->GetAssignedOutlineStyleLevel();
+ if (aMoveArr[nLevel] == -1)
+ bMoveApplicable = false;
+ }
+
+ // Check on outline level attribute of text node, if text node is
+ // not an outline via a to outline style assigned paragraph style.
+ else
+ {
+ const int nNewOutlineLevel = pTextNd->GetAttrOutlineLevel() + nOffset;
+ if ( nNewOutlineLevel < 1 || nNewOutlineLevel > MAXLEVEL )
+ {
+ bMoveApplicable = false;
+ }
+ }
+ }
+
+ if (! bMoveApplicable )
+ return false;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::OUTLINE_LR, nullptr);
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoOutlineLeftRight>(aPam, nOffset) );
+ }
+
+ // 2. Apply the new style to all Nodes
+ for (auto i = nSttPos; i < nEndPos; ++i)
+ {
+ SwTextNode* pTextNd = rOutlNds[ i ]->GetTextNode();
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
+ {
+ continue;
+ }
+ SwTextFormatColl* pColl = pTextNd->GetTextColl();
+
+ if( pColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ const int nLevel = pColl->GetAssignedOutlineStyleLevel();
+
+ OSL_ENSURE(aMoveArr[nLevel] >= 0,
+ "move table: current TextColl not found when building table!");
+
+ if (nLevel < MAXLEVEL && aMoveArr[nLevel] >= 0)
+ {
+ pColl = aCollArr[ aMoveArr[nLevel] ];
+
+ if (pColl != nullptr)
+ pTextNd->ChgFormatColl( pColl );
+ }
+
+ }
+ else if( pTextNd->GetAttrOutlineLevel() > 0)
+ {
+ int nLevel = pTextNd->GetAttrOutlineLevel() + nOffset;
+ if( 0 <= nLevel && nLevel <= MAXLEVEL)
+ pTextNd->SetAttrOutlineLevel( nLevel );
+
+ }
+ // Undo ???
+ }
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::OUTLINE_LR, nullptr);
+ }
+
+ ChkCondColls();
+ getIDocumentState().SetModified();
+
+ return true;
+}
+
+// Move up/down
+bool SwDoc::MoveOutlinePara( const SwPaM& rPam, SwOutlineNodes::difference_type nOffset )
+{
+ // Do not move to special sections in the nodes array
+ const SwPosition& rStt = *rPam.Start(),
+ & rEnd = *rPam.End();
+ if( GetNodes().GetOutLineNds().empty() || !nOffset ||
+ (rStt.nNode.GetIndex() < GetNodes().GetEndOfExtras().GetIndex()) ||
+ (rEnd.nNode.GetIndex() < GetNodes().GetEndOfExtras().GetIndex()))
+ {
+ return false;
+ }
+
+ SwOutlineNodes::size_type nCurrentPos = 0;
+ SwNodeIndex aSttRg( rStt.nNode ), aEndRg( rEnd.nNode );
+
+ int nOutLineLevel = MAXLEVEL;
+ SwNode* pSrch = &aSttRg.GetNode();
+
+ if( pSrch->IsTextNode())
+ nOutLineLevel = static_cast<sal_uInt8>(pSrch->GetTextNode()->GetAttrOutlineLevel()-1);
+ SwNode* pEndSrch = &aEndRg.GetNode();
+ if( !GetNodes().GetOutLineNds().Seek_Entry( pSrch, &nCurrentPos ) )
+ {
+ if( !nCurrentPos )
+ return false; // Promoting or demoting before the first outline => no.
+ if( --nCurrentPos )
+ aSttRg = *GetNodes().GetOutLineNds()[ nCurrentPos ];
+ else if( 0 > nOffset )
+ return false; // Promoting at the top of document?!
+ else
+ aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
+ }
+ SwOutlineNodes::size_type nTmpPos = 0;
+ // If the given range ends at an outlined text node we have to decide if it has to be a part of
+ // the moving range or not. Normally it will be a sub outline of our chapter
+ // and has to be moved, too. But if the chapter ends with a table(or a section end),
+ // the next text node will be chosen and this could be the next outline of the same level.
+ // The criteria has to be the outline level: sub level => incorporate, same/higher level => no.
+ if( GetNodes().GetOutLineNds().Seek_Entry( pEndSrch, &nTmpPos ) )
+ {
+ if( !pEndSrch->IsTextNode() || pEndSrch == pSrch ||
+ nOutLineLevel < pEndSrch->GetTextNode()->GetAttrOutlineLevel()-1 )
+ ++nTmpPos; // For sub outlines only!
+ }
+
+ aEndRg = nTmpPos < GetNodes().GetOutLineNds().size()
+ ? *GetNodes().GetOutLineNds()[ nTmpPos ]
+ : GetNodes().GetEndOfContent();
+ if( nOffset >= 0 )
+ nCurrentPos = nTmpPos;
+ if( aEndRg == aSttRg )
+ {
+ OSL_FAIL( "Moving outlines: Surprising selection" );
+ ++aEndRg;
+ }
+
+ const SwNode* pNd;
+ // The following code corrects the range to handle sections (start/end nodes)
+ // The range will be extended if the least node before the range is a start node
+ // which ends inside the range => The complete section will be moved.
+ // The range will be shrunk if the last position is a start node.
+ // The range will be shrunk if the last node is an end node which starts before the range.
+ --aSttRg;
+ while( aSttRg.GetNode().IsStartNode() )
+ {
+ pNd = aSttRg.GetNode().EndOfSectionNode();
+ if( pNd->GetIndex() >= aEndRg.GetIndex() )
+ break;
+ --aSttRg;
+ }
+ ++aSttRg;
+
+ --aEndRg;
+ while( aEndRg.GetNode().IsStartNode() )
+ --aEndRg;
+
+ while( aEndRg.GetNode().IsEndNode() )
+ {
+ pNd = aEndRg.GetNode().StartOfSectionNode();
+ if( pNd->GetIndex() >= aSttRg.GetIndex() )
+ break;
+ --aEndRg;
+ }
+ ++aEndRg;
+
+ // calculation of the new position
+ if( nOffset < 0 && nCurrentPos < o3tl::make_unsigned(-nOffset) )
+ pNd = GetNodes().GetEndOfContent().StartOfSectionNode();
+ else if( nCurrentPos + nOffset >= GetNodes().GetOutLineNds().size() )
+ pNd = &GetNodes().GetEndOfContent();
+ else
+ pNd = GetNodes().GetOutLineNds()[ nCurrentPos + nOffset ];
+
+ SwNodeOffset nNewPos = pNd->GetIndex();
+
+ // And now a correction of the insert position if necessary...
+ SwNodeIndex aInsertPos( *pNd, -1 );
+ while( aInsertPos.GetNode().IsStartNode() )
+ {
+ // Just before the insert position starts a section:
+ // when I'm moving forward I do not want to enter the section,
+ // when I'm moving backward I want to stay in the section if I'm already a part of,
+ // I want to stay outside if I was outside before.
+ if( nOffset < 0 )
+ {
+ pNd = aInsertPos.GetNode().EndOfSectionNode();
+ if( pNd->GetIndex() >= aEndRg.GetIndex() )
+ break;
+ }
+ --aInsertPos;
+ --nNewPos;
+ }
+
+ if( nOffset >= 0 )
+ {
+ // When just before the insert position a section ends, it is okay when I'm moving backward
+ // because I want to stay outside the section.
+ // When moving forward I've to check if I started inside or outside the section
+ // because I don't want to enter of leave such a section
+ while( aInsertPos.GetNode().IsEndNode() )
+ {
+ pNd = aInsertPos.GetNode().StartOfSectionNode();
+ if( pNd->GetIndex() >= aSttRg.GetIndex() )
+ break;
+ --aInsertPos;
+ --nNewPos;
+ }
+ }
+ // We do not want to move into tables (at the moment)
+ ++aInsertPos;
+ pNd = &aInsertPos.GetNode();
+ if( pNd->IsTableNode() )
+ pNd = pNd->StartOfSectionNode();
+ if( pNd->FindTableNode() )
+ return false;
+
+ OSL_ENSURE( aSttRg.GetIndex() > nNewPos || nNewPos >= aEndRg.GetIndex(),
+ "Position lies within Move range" );
+
+ // If a Position inside the special nodes array sections was calculated,
+ // set it to document start instead.
+ // Sections or Tables at the document start will be pushed backwards.
+ nNewPos = std::max( nNewPos, GetNodes().GetEndOfExtras().GetIndex() + SwNodeOffset(2) );
+
+ SwNodeOffset nOffs = nNewPos - ( 0 < nOffset ? aEndRg.GetIndex() : aSttRg.GetIndex());
+ SwPaM aPam( aSttRg, aEndRg, SwNodeOffset(0), SwNodeOffset(-1) );
+ return MoveParagraph( aPam, nOffs, true );
+}
+
+static SwTextNode* lcl_FindOutlineName(const SwOutlineNodes& rOutlNds,
+ SwRootFrame const*const pLayout, const OUString& rName, bool const bExact)
+{
+ SwTextNode * pExactButDeleted(nullptr);
+ SwTextNode* pSavedNode = nullptr;
+ for( auto pOutlNd : rOutlNds )
+ {
+ SwTextNode* pTextNd = pOutlNd->GetTextNode();
+ const OUString sText( pTextNd->GetExpandText(pLayout) );
+ if (sText.startsWith(rName))
+ {
+ if (sText.getLength() == rName.getLength())
+ {
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTextNd))
+ {
+ pExactButDeleted = pTextNd;
+ }
+ else
+ {
+ // Found "exact", set Pos to the Node
+ return pTextNd;
+ }
+ }
+ if (!bExact && !pSavedNode
+ && (!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd)))
+ {
+ // maybe we just found the text's first part
+ pSavedNode = pTextNd;
+ }
+ }
+ }
+
+ return bExact ? pExactButDeleted : pSavedNode;
+}
+
+static SwTextNode* lcl_FindOutlineNum(const SwOutlineNodes& rOutlNds,
+ OUString& rName, SwRootFrame const*const pLayout)
+{
+ // Valid numbers are (always just offsets!):
+ // ([Number]+\.)+ (as a regular expression!)
+ // (Number followed by a period, with 5 repetitions)
+ // i.e.: "1.1.", "1.", "1.1.1."
+ sal_Int32 nPos = 0;
+ std::u16string_view sNum = o3tl::getToken(rName, 0, '.', nPos );
+ if( -1 == nPos )
+ return nullptr; // invalid number!
+
+ sal_uInt16 nLevelVal[ MAXLEVEL ]; // numbers of all levels
+ memset( nLevelVal, 0, MAXLEVEL * sizeof( nLevelVal[0] ));
+ int nLevel = 0;
+ std::u16string_view sName( rName );
+
+ while( -1 != nPos )
+ {
+ sal_uInt16 nVal = 0;
+ for( size_t n = 0; n < sNum.size(); ++n )
+ {
+ const sal_Unicode c {sNum[ n ]};
+ if( '0' <= c && c <= '9' )
+ {
+ nVal *= 10;
+ nVal += c - '0';
+ }
+ else if( nLevel )
+ break; // "almost" valid number
+ else
+ return nullptr; // invalid number!
+ }
+
+ if( MAXLEVEL > nLevel )
+ nLevelVal[ nLevel++ ] = nVal;
+
+ sName = sName.substr( nPos );
+ nPos = 0;
+ sNum = o3tl::getToken(sName, 0, '.', nPos );
+ // #i4533# without this check all parts delimited by a dot are treated as outline numbers
+ if(!comphelper::string::isdigitAsciiString(sNum))
+ break;
+ }
+ rName = sName; // that's the follow-up text
+
+ // read all levels, so search the document for this outline
+
+ // Without OutlineNodes searching doesn't pay off
+ // and we save a crash
+ if( rOutlNds.empty() )
+ return nullptr;
+
+ // search in the existing outline nodes for the required outline num array
+ for( auto pOutlNd : rOutlNds )
+ {
+ SwTextNode* pNd = pOutlNd->GetTextNode();
+ if ( pNd->GetAttrOutlineLevel() == nLevel )
+ {
+ // #i51089#, #i68289#
+ // Assure, that text node has the correct numbering level. Otherwise,
+ // its number vector will not fit to the searched level.
+ if (pNd->GetNum(pLayout) && pNd->GetActualListLevel() == nLevel - 1)
+ {
+ const SwNodeNum & rNdNum = *(pNd->GetNum(pLayout));
+ SwNumberTree::tNumberVector aLevelVal = rNdNum.GetNumberVector();
+ // now compare with the one searched for
+ bool bEqual = true;
+ nLevel = std::min<int>(nLevel, MAXLEVEL);
+ for( int n = 0; n < nLevel; ++n )
+ {
+ if ( aLevelVal[n] != nLevelVal[n] )
+ {
+ bEqual = false;
+ break;
+ }
+ }
+ if (bEqual)
+ return pNd;
+ }
+ else
+ {
+ // A text node, which has an outline paragraph style applied and
+ // has as hard attribute 'no numbering' set, has an outline level,
+ // but no numbering tree node. Thus, consider this situation in
+ // the assertion condition.
+ OSL_ENSURE( !pNd->GetNumRule(),
+ "<lcl_FindOutlineNum(..)> - text node with outline level and numbering rule, but without numbering tree node. This is a serious defect" );
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+// rName can contain a Number and/or the Text.
+// First, we try to find the correct Entry via the Number.
+// If it exists, we compare the Text to see if it's the right one.
+// If that's not the case, we search again via the Text. If it is
+// found, we got the right entry. Or else we use the one found by
+// searching for the Number.
+// If we don't have a Number, we search via the Text only.
+bool SwDoc::GotoOutline(SwPosition& rPos, const OUString& rName, SwRootFrame const*const pLayout) const
+{
+ if( !rName.isEmpty() )
+ {
+ const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
+
+ // 1. step: via the Number:
+ OUString sName( rName );
+ SwTextNode* pNd = ::lcl_FindOutlineNum(rOutlNds, sName, pLayout);
+ if ( pNd )
+ {
+ OUString sExpandedText = pNd->GetExpandText(pLayout);
+ //#i4533# leading numbers followed by a dot have been remove while
+ //searching for the outline position
+ //to compensate this they must be removed from the paragraphs text content, too
+ while(!sExpandedText.isEmpty())
+ {
+ sal_Int32 nPos = 0;
+ std::u16string_view sTempNum = o3tl::getToken(sExpandedText, 0, '.', nPos);
+ if( sTempNum.empty() || -1 == nPos ||
+ !comphelper::string::isdigitAsciiString(sTempNum))
+ break;
+ sExpandedText = sExpandedText.copy(nPos);
+ }
+
+ if( sExpandedText != sName )
+ {
+ SwTextNode *pTmpNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, true);
+ if ( pTmpNd ) // found via the Name
+ {
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTmpNd))
+ { // found the correct node but it's deleted!
+ return false; // avoid fallback to inexact search
+ }
+ pNd = pTmpNd;
+ }
+ }
+ rPos.nNode = *pNd;
+ rPos.nContent.Assign( pNd, 0 );
+ return true;
+ }
+
+ pNd = ::lcl_FindOutlineName(rOutlNds, pLayout, rName, false);
+ if ( pNd )
+ {
+ rPos.nNode = *pNd;
+ rPos.nContent.Assign( pNd, 0 );
+ return true;
+ }
+
+ // #i68289# additional search on hyperlink URL without its outline numbering part
+ if ( sName != rName )
+ {
+ pNd = ::lcl_FindOutlineName(rOutlNds, pLayout, sName, false);
+ if ( pNd )
+ {
+ rPos.nNode = *pNd;
+ rPos.nContent.Assign( pNd, 0 );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static void lcl_ChgNumRule( SwDoc& rDoc, const SwNumRule& rRule )
+{
+ SwNumRule* pOld = rDoc.FindNumRulePtr( rRule.GetName() );
+ if (!pOld) //we cannot proceed without the old NumRule
+ return;
+
+ sal_uInt16 nChgFormatLevel = 0;
+ sal_uInt16 nMask = 1;
+
+ for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n, nMask <<= 1 )
+ {
+ const SwNumFormat& rOldFormat = pOld->Get( n ), &rNewFormat = rRule.Get( n );
+
+ if ( rOldFormat != rNewFormat )
+ {
+ nChgFormatLevel |= nMask;
+ }
+ else if ( SVX_NUM_NUMBER_NONE > rNewFormat.GetNumberingType()
+ && 1 < rNewFormat.GetIncludeUpperLevels()
+ && 0 != ( nChgFormatLevel & GetUpperLvlChg( n, rNewFormat.GetIncludeUpperLevels(), nMask ) ) )
+ {
+ nChgFormatLevel |= nMask;
+ }
+ }
+
+ if( !nChgFormatLevel ) // Nothing has been changed?
+ {
+ const bool bInvalidateNumRule( pOld->IsContinusNum() != rRule.IsContinusNum() );
+ pOld->CheckCharFormats( rDoc );
+ pOld->SetContinusNum( rRule.IsContinusNum() );
+
+ if ( bInvalidateNumRule )
+ {
+ pOld->SetInvalidRule(true);
+ }
+
+ return ;
+ }
+
+ SwNumRule::tTextNodeList aTextNodeList;
+ pOld->GetTextNodeList( aTextNodeList );
+ sal_uInt8 nLvl( 0 );
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ nLvl = static_cast<sal_uInt8>(pTextNd->GetActualListLevel());
+
+ if( nLvl < MAXLEVEL )
+ {
+ if( nChgFormatLevel & ( 1 << nLvl ))
+ {
+ pTextNd->NumRuleChgd();
+ }
+ }
+ }
+
+ for ( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
+ if ( nChgFormatLevel & ( 1 << n ) )
+ pOld->Set( n, rRule.GetNumFormat( n ) );
+
+ pOld->CheckCharFormats( rDoc );
+ pOld->SetInvalidRule( true );
+ pOld->SetContinusNum( rRule.IsContinusNum() );
+
+ rDoc.UpdateNumRule();
+}
+
+OUString SwDoc::SetNumRule( const SwPaM& rPam,
+ const SwNumRule& rRule,
+ const bool bCreateNewList,
+ SwRootFrame const*const pLayout,
+ const OUString& sContinuedListId,
+ bool bSetItem,
+ const bool bResetIndentAttrs )
+{
+ OUString sListId;
+
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+
+ SwUndoInsNum * pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Start/End for attributes!
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSNUM, nullptr );
+ pUndo = new SwUndoInsNum( aPam, rRule );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ SwNumRule* pNewOrChangedNumRule = FindNumRulePtr( rRule.GetName() );
+ bool bNewNumRuleCreated = false;
+ if ( pNewOrChangedNumRule == nullptr )
+ {
+ // create new numbering rule based on given one
+ pNewOrChangedNumRule = ( *mpNumRuleTable )[MakeNumRule( rRule.GetName(), &rRule )];
+ bNewNumRuleCreated = true;
+ }
+ else if ( rRule != *pNewOrChangedNumRule )
+ {
+ // change existing numbering rule
+ if (pUndo)
+ {
+ pUndo->SaveOldNumRule( *pNewOrChangedNumRule );
+ }
+ ::lcl_ChgNumRule( *this, rRule );
+ if (pUndo)
+ {
+ pUndo->SetLRSpaceEndPos();
+ }
+ }
+
+ if ( bSetItem )
+ {
+ if ( bCreateNewList )
+ {
+ if ( bNewNumRuleCreated )
+ {
+ // apply list id of list, which has been created for the new list style
+ sListId = pNewOrChangedNumRule->GetDefaultListId();
+ }
+ else
+ {
+ // create new list and apply its list id
+ const SwList* pNewList = getIDocumentListsAccess().createList( OUString(), pNewOrChangedNumRule->GetName() );
+ OSL_ENSURE( pNewList,
+ "<SwDoc::SetNumRule(..)> - could not create new list. Serious defect." );
+ sListId = pNewList->GetListId();
+ }
+ }
+ else if ( !sContinuedListId.isEmpty() )
+ {
+ // apply given list id
+ sListId = sContinuedListId;
+ }
+ if (!sListId.isEmpty())
+ {
+ getIDocumentContentOperations().InsertPoolItem(aPam,
+ SfxStringItem(RES_PARATR_LIST_ID, sListId),
+ SetAttrMode::DEFAULT, pLayout);
+ }
+ }
+
+ if (!aPam.HasMark())
+ {
+ SwTextNode * pTextNd = aPam.GetPoint()->nNode.GetNode().GetTextNode();
+ // robust code: consider case that the PaM doesn't denote a text node - e.g. it denotes a graphic node
+ if ( pTextNd != nullptr )
+ {
+ assert(!pLayout || sw::IsParaPropsNode(*pLayout, *pTextNd));
+ SwNumRule * pRule = pTextNd->GetNumRule();
+
+ if (pRule && pRule->GetName() == pNewOrChangedNumRule->GetName())
+ {
+ bSetItem = false;
+ if ( !pTextNd->IsInList() )
+ {
+ pTextNd->AddToList();
+ }
+ }
+ // Only clear numbering attribute at text node, if at paragraph
+ // style the new numbering rule is found.
+ else if ( !pRule )
+ {
+ SwTextFormatColl* pColl = pTextNd->GetTextColl();
+ if ( pColl )
+ {
+ SwNumRule* pCollRule = FindNumRulePtr(pColl->GetNumRule().GetValue());
+ if ( pCollRule && pCollRule->GetName() == pNewOrChangedNumRule->GetName() )
+ {
+ pTextNd->ResetAttr( RES_PARATR_NUMRULE );
+ bSetItem = false;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bSetItem )
+ {
+ getIDocumentContentOperations().InsertPoolItem(aPam,
+ SwNumRuleItem(pNewOrChangedNumRule->GetName()),
+ SetAttrMode::DEFAULT, pLayout);
+ }
+
+ if ( bResetIndentAttrs
+ && pNewOrChangedNumRule->Get( 0 ).GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ ::lcl_ResetIndentAttrs(this, aPam, RES_LR_SPACE, pLayout);
+ }
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSNUM, nullptr );
+ }
+
+ getIDocumentState().SetModified();
+
+ return sListId;
+}
+
+void SwDoc::SetCounted(const SwPaM & rPam, bool bCounted,
+ SwRootFrame const*const pLayout)
+{
+ if ( bCounted )
+ {
+ ::lcl_ResetIndentAttrs(this, rPam, RES_PARATR_LIST_ISCOUNTED, pLayout);
+ }
+ else
+ {
+ getIDocumentContentOperations().InsertPoolItem(rPam,
+ SfxBoolItem(RES_PARATR_LIST_ISCOUNTED, false),
+ SetAttrMode::DEFAULT, pLayout);
+ }
+}
+
+void SwDoc::SetNumRuleStart( const SwPosition& rPos, bool bFlag )
+{
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+
+ if (!pTextNd)
+ return;
+
+ const SwNumRule* pRule = pTextNd->GetNumRule();
+ if( pRule && !bFlag != !pTextNd->IsListRestart())
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumRuleStart>(rPos, bFlag) );
+ }
+
+ pTextNd->SetListRestart(bFlag);
+
+ getIDocumentState().SetModified();
+ }
+}
+
+void SwDoc::SetNodeNumStart( const SwPosition& rPos, sal_uInt16 nStt )
+{
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+
+ if (!pTextNd)
+ return;
+
+ if ( !pTextNd->HasAttrListRestartValue() ||
+ pTextNd->GetAttrListRestartValue() != nStt )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumRuleStart>(rPos, nStt) );
+ }
+ pTextNd->SetAttrListRestartValue( nStt );
+
+ getIDocumentState().SetModified();
+ }
+}
+
+// We can only delete if the Rule is unused!
+bool SwDoc::DelNumRule( const OUString& rName, bool bBroadcast )
+{
+ sal_uInt16 nPos = FindNumRule( rName );
+
+ if (nPos == USHRT_MAX)
+ return false;
+
+ if ( (*mpNumRuleTable)[ nPos ] == GetOutlineNumRule() )
+ {
+ OSL_FAIL( "<SwDoc::DelNumRule(..)> - No deletion of outline list style. This is serious defect" );
+ return false;
+ }
+
+ if( !IsUsed( *(*mpNumRuleTable)[ nPos ] ))
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumruleDelete>(*(*mpNumRuleTable)[nPos], *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Pseudo,
+ SfxHintId::StyleSheetErased);
+
+ getIDocumentListsAccess().deleteListForListStyle( rName );
+ getIDocumentListsAccess().deleteListsByDefaultListStyle( rName );
+ // #i34097# DeleteAndDestroy deletes rName if
+ // rName is directly taken from the numrule.
+ const OUString aTmpName( rName );
+ delete (*mpNumRuleTable)[ nPos ];
+ mpNumRuleTable->erase( mpNumRuleTable->begin() + nPos );
+ maNumRuleMap.erase(aTmpName);
+
+ getIDocumentState().SetModified();
+ return true;
+ }
+ return false;
+}
+
+void SwDoc::ChgNumRuleFormats( const SwNumRule& rRule )
+{
+ SwNumRule* pRule = FindNumRulePtr( rRule.GetName() );
+ if( !pRule )
+ return;
+
+ SwUndoInsNum* pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoInsNum( *pRule, rRule, *this );
+ pUndo->GetHistory();
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ ::lcl_ChgNumRule( *this, rRule );
+ if (pUndo)
+ {
+ pUndo->SetLRSpaceEndPos();
+ }
+
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::RenameNumRule(const OUString & rOldName, const OUString & rNewName,
+ bool bBroadcast)
+{
+ assert(!FindNumRulePtr(rNewName));
+
+ bool bResult = false;
+ SwNumRule * pNumRule = FindNumRulePtr(rOldName);
+
+ if (pNumRule)
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumruleRename>(rOldName, rNewName, *this));
+ }
+
+ SwNumRule::tTextNodeList aTextNodeList;
+ pNumRule->GetTextNodeList( aTextNodeList );
+
+ pNumRule->SetName( rNewName, getIDocumentListsAccess() );
+
+ SwNumRuleItem aItem(rNewName);
+
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ pTextNd->SetAttr(aItem);
+ }
+
+ bResult = true;
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rOldName, SfxStyleFamily::Pseudo,
+ SfxHintId::StyleSheetModified);
+ }
+
+ return bResult;
+}
+
+void SwDoc::StopNumRuleAnimations( const OutputDevice* pOut )
+{
+ for( sal_uInt16 n = GetNumRuleTable().size(); n; )
+ {
+ SwNumRule::tTextNodeList aTextNodeList;
+ GetNumRuleTable()[ --n ]->GetTextNodeList( aTextNodeList );
+ for ( SwTextNode* pTNd : aTextNodeList )
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTNd);
+ for(SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ if (pFrame->HasAnimation() &&
+ (!pFrame->GetMergedPara() || pFrame->GetMergedPara()->pParaPropsNode == pTNd))
+ {
+ pFrame->StopAnimation( pOut );
+ }
+ }
+ }
+}
+
+void SwDoc::ReplaceNumRule( const SwPosition& rPos,
+ const OUString& rOldRule, const OUString& rNewRule )
+{
+ SwNumRule *pOldRule = FindNumRulePtr( rOldRule ),
+ *pNewRule = FindNumRulePtr( rNewRule );
+ if( !pOldRule || !pNewRule || pOldRule == pNewRule )
+ return;
+
+ SwUndoInsNum* pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // Start/End for attributes!
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ pUndo = new SwUndoInsNum( rPos, *pNewRule, rOldRule );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+
+ SwNumRule::tTextNodeList aTextNodeList;
+ pOldRule->GetTextNodeList( aTextNodeList );
+ if ( !aTextNodeList.empty() )
+ {
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+
+ const SwTextNode* pGivenTextNode = rPos.nNode.GetNode().GetTextNode();
+ SwNumRuleItem aRule( rNewRule );
+ for ( SwTextNode* pTextNd : aTextNodeList )
+ {
+ if ( pGivenTextNode &&
+ pGivenTextNode->GetListId() == pTextNd->GetListId() )
+ {
+ aRegH.RegisterInModify( pTextNd, *pTextNd );
+
+ pTextNd->SetAttr( aRule );
+ pTextNd->NumRuleChgd();
+ }
+ }
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ getIDocumentState().SetModified();
+ }
+}
+
+namespace
+{
+ struct ListStyleData
+ {
+ SwNumRule* pReplaceNumRule;
+ bool bCreateNewList;
+ OUString sListId;
+
+ ListStyleData()
+ : pReplaceNumRule( nullptr ),
+ bCreateNewList( false )
+ {}
+ };
+}
+
+void SwDoc::MakeUniqueNumRules(const SwPaM & rPaM)
+{
+ OSL_ENSURE( &rPaM.GetDoc() == this, "need same doc" );
+
+ std::map<SwNumRule *, ListStyleData> aMyNumRuleMap;
+
+ bool bFirst = true;
+
+ const SwNodeOffset nStt = rPaM.Start()->nNode.GetIndex();
+ const SwNodeOffset nEnd = rPaM.End()->nNode.GetIndex();
+ for (SwNodeOffset n = nStt; n <= nEnd; n++)
+ {
+ SwTextNode * pCNd = GetNodes()[n]->GetTextNode();
+
+ if (pCNd)
+ {
+ SwNumRule * pRule = pCNd->GetNumRule();
+
+ if (pRule && pRule->IsAutoRule() && ! pRule->IsOutlineRule())
+ {
+ ListStyleData aListStyleData = aMyNumRuleMap[pRule];
+
+ if ( aListStyleData.pReplaceNumRule == nullptr )
+ {
+ if (bFirst)
+ {
+ SwPosition aPos(*pCNd);
+ aListStyleData.pReplaceNumRule =
+ const_cast<SwNumRule *>
+ (SearchNumRule( aPos, false, pCNd->HasNumber(),
+ false, 0,
+ aListStyleData.sListId, nullptr, true ));
+ }
+
+ if ( aListStyleData.pReplaceNumRule == nullptr )
+ {
+ aListStyleData.pReplaceNumRule = new SwNumRule(*pRule);
+ aListStyleData.pReplaceNumRule->SetName( GetUniqueNumRuleName(), getIDocumentListsAccess() );
+ aListStyleData.bCreateNewList = true;
+ }
+
+ aMyNumRuleMap[pRule] = aListStyleData;
+ }
+
+ SwPaM aPam(*pCNd);
+
+ SetNumRule( aPam,
+ *aListStyleData.pReplaceNumRule,
+ aListStyleData.bCreateNewList,
+ nullptr,
+ aListStyleData.sListId );
+ if ( aListStyleData.bCreateNewList )
+ {
+ aListStyleData.bCreateNewList = false;
+ aListStyleData.sListId = pCNd->GetListId();
+ aMyNumRuleMap[pRule] = aListStyleData;
+ }
+
+ bFirst = false;
+ }
+ }
+ }
+}
+
+bool SwDoc::NoNum( const SwPaM& rPam )
+{
+
+ bool bRet = getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), false );
+ // Do we actually use Numbering at all?
+ if( bRet )
+ {
+ // Set NoNum and Update
+ const SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
+ SwTextNode* pNd = rIdx.GetNode().GetTextNode();
+ const SwNumRule* pRule = pNd->GetNumRule();
+ if( pRule )
+ {
+ pNd->SetCountedInList(false);
+
+ getIDocumentState().SetModified();
+ }
+ else
+ bRet = false; // no Numbering or just always true?
+ }
+ return bRet;
+}
+
+void SwDoc::DelNumRules(const SwPaM& rPam, SwRootFrame const*const pLayout)
+{
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+ SwNodeOffset nStt = aPam.Start()->nNode.GetIndex();
+ SwNodeOffset const nEnd = aPam.End()->nNode.GetIndex();
+
+ SwUndoDelNum* pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoDelNum( aPam );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ }
+ else
+ pUndo = nullptr;
+
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+
+ SwNumRuleItem aEmptyRule;
+ const SwNode* pOutlNd = nullptr;
+ for( ; nStt <= nEnd; ++nStt )
+ {
+ SwTextNode* pTNd = GetNodes()[ nStt ]->GetTextNode();
+ if (pLayout && pTNd)
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
+ }
+ SwNumRule* pNumRuleOfTextNode = pTNd ? pTNd->GetNumRule() : nullptr;
+ if ( pTNd && pNumRuleOfTextNode )
+ {
+ // recognize changes of attribute for undo
+ aRegH.RegisterInModify( pTNd, *pTNd );
+
+ if( pUndo )
+ pUndo->AddNode( *pTNd );
+
+ // directly set list style attribute is reset, otherwise empty
+ // list style is applied
+ const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
+ if ( pAttrSet &&
+ pAttrSet->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ pTNd->ResetAttr( RES_PARATR_NUMRULE );
+ else
+ pTNd->SetAttr( aEmptyRule );
+
+ pTNd->ResetAttr( RES_PARATR_LIST_ID );
+ pTNd->ResetAttr( RES_PARATR_LIST_LEVEL );
+ pTNd->ResetAttr( RES_PARATR_LIST_ISRESTART );
+ pTNd->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ pTNd->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+
+ if( RES_CONDTXTFMTCOLL == pTNd->GetFormatColl()->Which() )
+ {
+ pTNd->ChkCondColl();
+ }
+ else if( !pOutlNd &&
+ static_cast<SwTextFormatColl*>(pTNd->GetFormatColl())->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ pOutlNd = pTNd;
+ }
+ }
+ }
+
+ // Finally, update all
+ UpdateNumRule();
+
+ if( pOutlNd )
+ GetNodes().UpdateOutlineIdx( *pOutlNd );
+}
+
+void SwDoc::InvalidateNumRules()
+{
+ for (size_t n = 0; n < mpNumRuleTable->size(); ++n)
+ (*mpNumRuleTable)[n]->SetInvalidRule(true);
+}
+
+// To the next/preceding Bullet at the same Level
+static bool lcl_IsNumOk( sal_uInt8 nSrchNum, sal_uInt8& rLower, sal_uInt8& rUpper,
+ bool bOverUpper, sal_uInt8 nNumber )
+{
+ OSL_ENSURE( nNumber < MAXLEVEL,
+ "<lcl_IsNumOk(..)> - misusage of method" );
+
+ bool bRet = false;
+ {
+ if( bOverUpper ? nSrchNum == nNumber : nSrchNum >= nNumber )
+ bRet = true;
+ else if( nNumber > rLower )
+ rLower = nNumber;
+ else if( nNumber < rUpper )
+ rUpper = nNumber;
+ }
+ return bRet;
+}
+
+static bool lcl_IsValidPrevNextNumNode( const SwNodeIndex& rIdx )
+{
+ bool bRet = false;
+ const SwNode& rNd = rIdx.GetNode();
+ switch( rNd.GetNodeType() )
+ {
+ case SwNodeType::End:
+ bRet = SwTableBoxStartNode == rNd.StartOfSectionNode()->GetStartNodeType() ||
+ rNd.StartOfSectionNode()->IsSectionNode();
+ break;
+
+ case SwNodeType::Start:
+ bRet = SwTableBoxStartNode == static_cast<const SwStartNode&>(rNd).GetStartNodeType();
+ break;
+
+ case SwNodeType::Section: // that one's valid, so proceed
+ bRet = true;
+ break;
+
+ default: break;
+ }
+ return bRet;
+}
+
+namespace sw {
+
+void
+GotoPrevLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const*const pLayout)
+{
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (rIndex.GetNode().IsTextNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None &&
+ // not a tracked row deletion in Hide Changes mode
+ rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout) )
+ {
+ rIndex = *static_cast<SwTextFrame*>(rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout))->GetMergedPara()->pFirstNode;
+ }
+ }
+ else if (rIndex.GetNode().IsEndNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ rIndex = *rIndex.GetNode().StartOfSectionNode();
+ assert(rIndex.GetNode().IsTableNode());
+ }
+ }
+ }
+ --rIndex;
+ if (pLayout && rIndex.GetNode().IsTextNode())
+ {
+ rIndex = *sw::GetParaPropsNode(*pLayout, *rIndex.GetNode().GetTextNode());
+ }
+}
+
+void
+GotoNextLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const*const pLayout)
+{
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (rIndex.GetNode().IsTextNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() != SwNode::Merge::None)
+ {
+ rIndex = *static_cast<SwTextFrame*>(rIndex.GetNode().GetTextNode()->getLayoutFrame(pLayout))->GetMergedPara()->pLastNode;
+ }
+ }
+ else if (rIndex.GetNode().IsTableNode())
+ {
+ if (rIndex.GetNode().GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ rIndex = *rIndex.GetNode().EndOfSectionNode();
+ }
+ }
+ }
+ ++rIndex;
+ if (pLayout && rIndex.GetNode().IsTextNode())
+ {
+ rIndex = *sw::GetParaPropsNode(*pLayout, *rIndex.GetNode().GetTextNode());
+ }
+}
+
+} // namespace sw
+
+static bool lcl_GotoNextPrevNum( SwPosition& rPos, bool bNext,
+ bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower,
+ SwRootFrame const*const pLayout)
+{
+ const SwTextNode* pNd = rPos.nNode.GetNode().GetTextNode();
+ if (pNd && pLayout)
+ {
+ pNd = sw::GetParaPropsNode(*pLayout, *pNd);
+ }
+ if( !pNd || nullptr == pNd->GetNumRule() )
+ return false;
+
+ sal_uInt8 nSrchNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
+
+ SwNodeIndex aIdx( rPos.nNode );
+ if( ! pNd->IsCountedInList() )
+ {
+ bool bError = false;
+ do {
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ pNd = aIdx.GetNode().GetTextNode();
+ const SwNumRule* pRule = pNd->GetNumRule();
+
+ if( pRule )
+ {
+ sal_uInt8 nTmpNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
+ if( pNd->IsCountedInList() || (nTmpNum < nSrchNum ) )
+ break; // found it!
+ }
+ else
+ bError = true;
+ }
+ else
+ bError = !lcl_IsValidPrevNextNumNode( aIdx );
+
+ } while( !bError );
+ if( bError )
+ return false;
+ }
+
+ sal_uInt8 nLower = nSrchNum, nUpper = nSrchNum;
+ bool bRet = false;
+
+ const SwTextNode* pLast;
+ if( bNext )
+ {
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ pLast = pNd;
+ }
+ else
+ {
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ pLast = nullptr;
+ }
+
+ while( bNext ? ( aIdx.GetIndex() < aIdx.GetNodes().Count() - 1 )
+ : aIdx.GetIndex() != SwNodeOffset(0) )
+ {
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ pNd = aIdx.GetNode().GetTextNode();
+ const SwNumRule* pRule = pNd->GetNumRule();
+ if( pRule )
+ {
+ if( ::lcl_IsNumOk( nSrchNum, nLower, nUpper, bOverUpper,
+ static_cast<sal_uInt8>(pNd->GetActualListLevel()) ))
+ {
+ rPos.nNode = aIdx;
+ rPos.nContent.Assign( const_cast<SwTextNode*>(pNd), 0 );
+ bRet = true;
+ break;
+ }
+ else
+ pLast = pNd;
+ }
+ else
+ break;
+ }
+ else if( !lcl_IsValidPrevNextNumNode( aIdx ))
+ break;
+
+ if( bNext )
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ else
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ }
+
+ if( !bRet && !bOverUpper && pLast ) // do not iterate over higher numbers, but still to the end
+ {
+ if( bNext )
+ {
+ rPos.nNode = aIdx;
+ if( aIdx.GetNode().IsContentNode() )
+ rPos.nContent.Assign( aIdx.GetNode().GetContentNode(), 0 );
+ }
+ else
+ {
+ rPos.nNode.Assign( *pLast );
+ rPos.nContent.Assign( const_cast<SwTextNode*>(pLast), 0 );
+ }
+ bRet = true;
+ }
+
+ if( bRet )
+ {
+ if( pUpper )
+ *pUpper = nUpper;
+ if( pLower )
+ *pLower = nLower;
+ }
+ return bRet;
+}
+
+bool SwDoc::GotoNextNum(SwPosition& rPos, SwRootFrame const*const pLayout,
+ bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower)
+{
+ return ::lcl_GotoNextPrevNum(rPos, true, bOverUpper, pUpper, pLower, pLayout);
+}
+
+const SwNumRule * SwDoc::SearchNumRule(const SwPosition & rPos,
+ const bool bForward,
+ const bool bNum,
+ const bool bOutline,
+ int nNonEmptyAllowed,
+ OUString& sListId,
+ SwRootFrame const* pLayout,
+ const bool bInvestigateStartNode)
+{
+ const SwNumRule * pResult = nullptr;
+ SwTextNode * pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if (pLayout)
+ {
+ pTextNd = sw::GetParaPropsNode(*pLayout, rPos.nNode);
+ }
+ SwNode * pStartFromNode = pTextNd;
+
+ if (pTextNd)
+ {
+ SwNodeIndex aIdx(rPos.nNode);
+
+ // - the start node has also been investigated, if requested.
+ const SwNode * pNode = nullptr;
+ do
+ {
+ if ( !bInvestigateStartNode )
+ {
+ if (bForward)
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ else
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ }
+
+ if (aIdx.GetNode().IsTextNode())
+ {
+ pTextNd = aIdx.GetNode().GetTextNode();
+
+ const SwNumRule * pNumRule = pTextNd->GetNumRule();
+ if (pNumRule)
+ {
+ if ( ( pNumRule->IsOutlineRule() == bOutline ) &&
+ ( ( bNum && pNumRule->Get(0).IsEnumeration()) ||
+ ( !bNum && pNumRule->Get(0).IsItemize() ) ) ) // #i22362#, #i29560#
+ {
+ pResult = pTextNd->GetNumRule();
+ // provide also the list id, to which the text node belongs.
+ sListId = pTextNd->GetListId();
+ }
+
+ break;
+ }
+ else if (pTextNd->Len() > 0 || nullptr != pTextNd->GetNumRule())
+ {
+ if (nNonEmptyAllowed == 0)
+ break;
+
+ nNonEmptyAllowed--;
+
+ if (nNonEmptyAllowed < 0)
+ nNonEmptyAllowed = -1;
+ }
+ }
+
+ if ( bInvestigateStartNode )
+ {
+ if (bForward)
+ sw::GotoNextLayoutTextFrame(aIdx, pLayout);
+ else
+ sw::GotoPrevLayoutTextFrame(aIdx, pLayout);
+ }
+
+ pNode = &aIdx.GetNode();
+ }
+ while (pNode != GetNodes().DocumentSectionStartNode(pStartFromNode) &&
+ pNode != GetNodes().DocumentSectionEndNode(pStartFromNode));
+ }
+
+ return pResult;
+}
+
+bool SwDoc::GotoPrevNum(SwPosition& rPos, SwRootFrame const*const pLayout,
+ bool bOverUpper)
+{
+ return ::lcl_GotoNextPrevNum(rPos, false, bOverUpper, nullptr, nullptr, pLayout);
+}
+
+bool SwDoc::NumUpDown(const SwPaM& rPam, bool bDown, SwRootFrame const*const pLayout)
+{
+ SwPaM aPam(rPam, nullptr);
+ ExpandPamForParaPropsNodes(aPam, pLayout);
+ SwNodeOffset nStt = aPam.Start()->nNode.GetIndex();
+ SwNodeOffset const nEnd = aPam.End()->nNode.GetIndex();
+
+ // -> outline nodes are promoted or demoted differently
+ bool bOnlyOutline = true;
+ bool bOnlyNonOutline = true;
+ for (SwNodeOffset n = nStt; n <= nEnd; n++)
+ {
+ SwTextNode * pTextNd = GetNodes()[n]->GetTextNode();
+
+ if (pTextNd)
+ {
+ if (pLayout)
+ {
+ pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd);
+ }
+ SwNumRule * pRule = pTextNd->GetNumRule();
+
+ if (pRule)
+ {
+ if (pRule->IsOutlineRule())
+ bOnlyNonOutline = false;
+ else
+ bOnlyOutline = false;
+ }
+ }
+ }
+
+ bool bRet = true;
+ sal_Int8 nDiff = bDown ? 1 : -1;
+
+ if (bOnlyOutline)
+ bRet = OutlineUpDown(rPam, nDiff, pLayout);
+ else if (bOnlyNonOutline)
+ {
+ /* #i24560#
+ Only promote or demote if all selected paragraphs are
+ promotable resp. demotable.
+ */
+ for (SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp)
+ {
+ SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();
+
+ // Make code robust: consider case that the node doesn't denote a
+ // text node.
+ if ( pTNd )
+ {
+ if (pLayout)
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
+ }
+
+ SwNumRule * pRule = pTNd->GetNumRule();
+
+ if (pRule)
+ {
+ sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
+ if( (-1 == nDiff && 0 >= nLevel) ||
+ (1 == nDiff && MAXLEVEL - 1 <= nLevel))
+ bRet = false;
+ }
+ }
+ }
+
+ if( bRet )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumUpDown>(aPam, nDiff) );
+ }
+
+ SwTextNode* pPrev = nullptr;
+ for(SwNodeOffset nTmp = nStt; nTmp <= nEnd; ++nTmp )
+ {
+ SwTextNode* pTNd = GetNodes()[ nTmp ]->GetTextNode();
+
+ if( pTNd)
+ {
+ if (pLayout)
+ {
+ pTNd = sw::GetParaPropsNode(*pLayout, *pTNd);
+ if (pTNd == pPrev)
+ {
+ continue;
+ }
+ pPrev = pTNd;
+ }
+
+ SwNumRule * pRule = pTNd->GetNumRule();
+
+ if (pRule)
+ {
+ sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
+ nLevel = nLevel + nDiff;
+
+ pTNd->SetAttrListLevel(nLevel);
+ }
+ }
+ }
+
+ ChkCondColls();
+ getIDocumentState().SetModified();
+ }
+ }
+
+ return bRet;
+}
+
+// this function doesn't contain any numbering-related code, but it is
+// primarily called to move numbering-relevant paragraphs around, hence
+// it will expand its selection to include full SwTextFrames.
+bool SwDoc::MoveParagraph(SwPaM& rPam, SwNodeOffset nOffset, bool const bIsOutlMv)
+{
+ MakeAllOutlineContentTemporarilyVisible a(this);
+
+ // sw_redlinehide: as long as a layout with Hide mode exists, only
+ // move nodes that have merged frames *completely*
+ SwRootFrame const* pLayout(nullptr);
+ for (SwRootFrame const*const pLay : GetAllLayouts())
+ {
+ if (pLay->HasMergedParas())
+ {
+ pLayout = pLay;
+ }
+ }
+ if (pLayout)
+ {
+ std::pair<SwTextNode *, SwTextNode *> nodes(
+ sw::GetFirstAndLastNode(*pLayout, rPam.Start()->nNode));
+ if (nodes.first && nodes.first != &rPam.Start()->nNode.GetNode())
+ {
+ assert(nodes.second);
+ if (nOffset < SwNodeOffset(0))
+ {
+ nOffset += rPam.Start()->nNode.GetIndex() - nodes.first->GetIndex();
+ if (SwNodeOffset(0) <= nOffset) // hack: there are callers that know what
+ { // node they want; those should never need
+ nOffset = SwNodeOffset(-1); // this; other callers just pass in -1
+ } // and those should still move
+ }
+ if (!rPam.HasMark())
+ {
+ rPam.SetMark();
+ }
+ assert(nodes.first->GetIndex() < rPam.Start()->nNode.GetIndex());
+ rPam.Start()->nNode = *nodes.first;
+ rPam.Start()->nContent.Assign(nodes.first, 0);
+ }
+ nodes = sw::GetFirstAndLastNode(*pLayout, rPam.End()->nNode);
+ if (nodes.second && nodes.second != &rPam.End()->nNode.GetNode())
+ {
+ assert(nodes.first);
+ if (SwNodeOffset(0) < nOffset)
+ {
+ nOffset -= nodes.second->GetIndex() - rPam.End()->nNode.GetIndex();
+ if (nOffset <= SwNodeOffset(0)) // hack: there are callers that know what
+ { // node they want; those should never need
+ nOffset = SwNodeOffset(+1); // this; other callers just pass in +1
+ } // and those should still move
+ }
+ if (!rPam.HasMark())
+ {
+ rPam.SetMark();
+ }
+ assert(rPam.End()->nNode.GetIndex() < nodes.second->GetIndex());
+ rPam.End()->nNode = *nodes.second;
+ // until end, otherwise Impl will detect overlapping redline
+ rPam.End()->nContent.Assign(nodes.second, nodes.second->GetTextNode()->Len());
+ }
+
+ if (nOffset > SwNodeOffset(0))
+ { // sw_redlinehide: avoid moving into delete redline, skip forward
+ if (GetNodes().GetEndOfContent().GetIndex() <= rPam.End()->nNode.GetIndex() + nOffset)
+ {
+ return false; // can't move
+ }
+ SwNode const* pNode(GetNodes()[rPam.End()->nNode.GetIndex() + nOffset + 1]);
+ if ( pNode->GetRedlineMergeFlag() != SwNode::Merge::None
+ && pNode->GetRedlineMergeFlag() != SwNode::Merge::First)
+ {
+ for ( ; ; ++nOffset)
+ {
+ pNode = GetNodes()[rPam.End()->nNode.GetIndex() + nOffset];
+ if (pNode->IsTextNode())
+ {
+ nodes = GetFirstAndLastNode(*pLayout, *pNode->GetTextNode());
+ assert(nodes.first && nodes.second);
+ nOffset += nodes.second->GetIndex() - pNode->GetIndex();
+ // on last; will be incremented below to behind-last
+ break;
+ }
+ }
+ }
+ }
+ else
+ { // sw_redlinehide: avoid moving into delete redline, skip backward
+ if (rPam.Start()->nNode.GetIndex() + nOffset < SwNodeOffset(1))
+ {
+ return false; // can't move
+ }
+ SwNode const* pNode(GetNodes()[rPam.Start()->nNode.GetIndex() + nOffset]);
+ if ( pNode->GetRedlineMergeFlag() != SwNode::Merge::None
+ && pNode->GetRedlineMergeFlag() != SwNode::Merge::First)
+ {
+ for ( ; ; --nOffset)
+ {
+ pNode = GetNodes()[rPam.Start()->nNode.GetIndex() + nOffset];
+ if (pNode->IsTextNode())
+ {
+ nodes = GetFirstAndLastNode(*pLayout, *pNode->GetTextNode());
+ assert(nodes.first && nodes.second);
+ nOffset -= pNode->GetIndex() - nodes.first->GetIndex();
+ // on first
+ break;
+ }
+ }
+ }
+ }
+ }
+ return MoveParagraphImpl(rPam, nOffset, bIsOutlMv, pLayout);
+}
+
+bool SwDoc::MoveParagraphImpl(SwPaM& rPam, SwNodeOffset const nOffset,
+ bool const bIsOutlMv, SwRootFrame const*const pLayout)
+{
+ const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
+
+ SwNodeOffset nStIdx = pStt->nNode.GetIndex();
+ SwNodeOffset nEndIdx = pEnd->nNode.GetIndex();
+
+ // Here are some sophisticated checks whether the wished PaM will be moved or not.
+ // For moving outlines (bIsOutlMv) I've already done some checks, so here are two different
+ // checks...
+ SwNode *pTmp1;
+ SwNode *pTmp2;
+ if( bIsOutlMv )
+ {
+ // For moving chapters (outline) the following reason will deny the move:
+ // if a start node is inside the moved range and its end node outside or vice versa.
+ // If a start node is the first moved paragraph, its end node has to be within the moved
+ // range, too (e.g. as last node).
+ // If an end node is the last node of the moved range, its start node has to be a part of
+ // the moved section, too.
+ pTmp1 = GetNodes()[ nStIdx ];
+ if( pTmp1->IsStartNode() )
+ {
+ // coverity[copy_paste_error : FALSE] - First is a start node
+ pTmp2 = pTmp1->EndOfSectionNode();
+ if( pTmp2->GetIndex() > nEndIdx )
+ return false; // Its end node is behind the moved range
+ }
+ pTmp1 = pTmp1->StartOfSectionNode()->EndOfSectionNode();
+ if( pTmp1->GetIndex() <= nEndIdx )
+ return false; // End node inside but start node before moved range => no.
+ pTmp1 = GetNodes()[ nEndIdx ];
+ if( pTmp1->IsEndNode() )
+ { // The last one is an end node
+ pTmp1 = pTmp1->StartOfSectionNode();
+ if( pTmp1->GetIndex() < nStIdx )
+ return false; // Its start node is before the moved range.
+ }
+ pTmp1 = pTmp1->StartOfSectionNode();
+ if( pTmp1->GetIndex() >= nStIdx )
+ return false; // A start node which ends behind the moved range => no.
+ }
+
+ SwNodeOffset nInStIdx, nInEndIdx;
+ SwNodeOffset nOffs = nOffset;
+ if( nOffset > SwNodeOffset(0) )
+ {
+ nInEndIdx = nEndIdx;
+ nEndIdx += nOffset;
+ ++nOffs;
+ }
+ else
+ {
+ // Impossible to move to negative index
+ if( abs( nOffset ) > nStIdx)
+ return false;
+
+ nInEndIdx = nStIdx - 1;
+ nStIdx += nOffset;
+ }
+ nInStIdx = nInEndIdx + 1;
+ // The following paragraphs shall be swapped:
+ // Swap [ nStIdx, nInEndIdx ] with [ nInStIdx, nEndIdx ]
+
+ if( nEndIdx >= GetNodes().GetEndOfContent().GetIndex() )
+ return false;
+
+ if( !bIsOutlMv )
+ { // And here the restrictions for moving paragraphs other than chapters (outlines)
+ // The plan is to exchange [nStIdx,nInEndIdx] and [nStartIdx,nEndIdx]
+ // It will checked if the both "start" nodes as well as the both "end" notes belongs to
+ // the same start-end-section. This is more restrictive than the conditions checked above.
+ // E.g. a paragraph will not escape from a section or be inserted to another section.
+ pTmp1 = GetNodes()[ nStIdx ]->StartOfSectionNode();
+ pTmp2 = GetNodes()[ nInStIdx ]->StartOfSectionNode();
+ if( pTmp1 != pTmp2 )
+ return false; // "start" nodes in different sections
+ pTmp1 = GetNodes()[ nEndIdx ];
+ bool bIsEndNode = pTmp1->IsEndNode();
+ if( !pTmp1->IsStartNode() )
+ {
+ pTmp1 = pTmp1->StartOfSectionNode();
+ if( bIsEndNode ) // For end nodes the first start node is of course inside the range,
+ pTmp1 = pTmp1->StartOfSectionNode(); // I've to check the start node of the start node.
+ }
+ pTmp1 = pTmp1->EndOfSectionNode();
+ pTmp2 = GetNodes()[ nInEndIdx ];
+ if( !pTmp2->IsStartNode() )
+ {
+ bIsEndNode = pTmp2->IsEndNode();
+ pTmp2 = pTmp2->StartOfSectionNode();
+ if( bIsEndNode )
+ pTmp2 = pTmp2->StartOfSectionNode();
+ }
+ pTmp2 = pTmp2->EndOfSectionNode();
+ if( pTmp1 != pTmp2 )
+ return false; // The "end" notes are in different sections
+ }
+
+ // Test for Redlining - Can the Selection be moved at all, actually?
+ if( !getIDocumentRedlineAccess().IsIgnoreRedline() )
+ {
+ SwRedlineTable::size_type nRedlPos = getIDocumentRedlineAccess().GetRedlinePos( pStt->nNode.GetNode(), RedlineType::Delete );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ SwPosition aStPos( *pStt ), aEndPos( *pEnd );
+ aStPos.nContent = 0;
+ SwContentNode* pCNd = pEnd->nNode.GetNode().GetContentNode();
+ aEndPos.nContent = pCNd ? pCNd->Len() : 1;
+ bool bCheckDel = true;
+
+ // There is a some Redline Delete Object for the range
+ for( ; nRedlPos < getIDocumentRedlineAccess().GetRedlineTable().size(); ++nRedlPos )
+ {
+ const SwRangeRedline* pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ if( !bCheckDel || RedlineType::Delete == pTmp->GetType() )
+ {
+ const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
+ switch( ComparePosition( *pRStt, *pREnd, aStPos, aEndPos ))
+ {
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Behind: // Pos1 comes after Pos2
+ nRedlPos = getIDocumentRedlineAccess().GetRedlineTable().size();
+ break;
+
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Before: // Pos1 comes before Pos2
+ break;
+ case SwComparePosition::Inside: // Pos1 is completely inside Pos2
+ // that's valid, but check all following for overlapping
+ bCheckDel = false;
+ break;
+
+ case SwComparePosition::Outside: // Pos2 is completely inside Pos1
+ case SwComparePosition::Equal: // Pos1 is equal to Pos2
+ case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 in the beginning
+ case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ {
+ // Send DataChanged before moving. We then can detect
+ // which objects are still in the range.
+ // After the move they could come before/after the
+ // Position.
+ SwDataChanged aTmp( rPam );
+ }
+
+ SwNodeIndex aIdx( nOffset > SwNodeOffset(0) ? pEnd->nNode : pStt->nNode, nOffs );
+ SwNodeRange aMvRg( pStt->nNode, SwNodeOffset(0), pEnd->nNode, SwNodeOffset(+1) );
+
+ SwRangeRedline* pOwnRedl = nullptr;
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ // If the range is completely in the own Redline, we can move it!
+ SwRedlineTable::size_type nRedlPos = getIDocumentRedlineAccess().GetRedlinePos( pStt->nNode.GetNode(), RedlineType::Insert );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ SwRangeRedline* pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
+ SwRangeRedline aTmpRedl( RedlineType::Insert, rPam );
+ const SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+ // Is completely in the range and is the own Redline too?
+ if( aTmpRedl.IsOwnRedline( *pTmp ) &&
+ (pRStt->nNode < pStt->nNode ||
+ (pRStt->nNode == pStt->nNode && !pRStt->nContent.GetIndex()) ) &&
+ (pEnd->nNode < pREnd->nNode ||
+ (pEnd->nNode == pREnd->nNode &&
+ pCEndNd ? pREnd->nContent.GetIndex() == pCEndNd->Len()
+ : !pREnd->nContent.GetIndex() )) )
+ {
+ pOwnRedl = pTmp;
+ if( nRedlPos + 1 < getIDocumentRedlineAccess().GetRedlineTable().size() )
+ {
+ pTmp = getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos+1 ];
+ if( *pTmp->Start() == *pREnd )
+ // then don't!
+ pOwnRedl = nullptr;
+ }
+
+ if( pOwnRedl &&
+ ( pRStt->nNode > aIdx || aIdx > pREnd->nNode ||
+ // pOwnRedl doesn't start at the beginning of a node, so it's not
+ // possible to resize it to contain the line moved before it
+ ( pRStt->nNode == aIdx && pRStt->nContent.GetIndex() > 0 ) ) )
+ {
+ // it's not in itself, so don't move it
+ pOwnRedl = nullptr;
+ }
+ }
+ }
+
+ if( !pOwnRedl )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+
+ // First the Insert, then the Delete
+ SwPosition aInsPos( aIdx );
+ aInsPos.nContent.Assign( aIdx.GetNode().GetContentNode(), 0 );
+
+ SwPaM aPam( pStt->nNode, 0, aMvRg.aEnd, 0 );
+
+ SwPaM& rOrigPam(rPam);
+ rOrigPam.DeleteMark();
+ rOrigPam.GetPoint()->nNode = aIdx.GetIndex() - 1;
+ rOrigPam.GetPoint()->nContent.Assign( rOrigPam.GetContentNode(), 0 );
+
+ bool bDelLastPara = !aInsPos.nNode.GetNode().IsContentNode();
+ SwNodeOffset nOrigIdx = aIdx.GetIndex();
+
+ /* When copying to a non-content node Copy will
+ insert a paragraph before that node and insert before
+ that inserted node. Copy creates an SwUndoInserts that
+ does not cover the extra paragraph. Thus we insert the
+ extra paragraph ourselves, _with_ correct undo
+ information. */
+ if (bDelLastPara)
+ {
+ /* aInsPos points to the non-content node. Move it to
+ the previous content node. */
+ SwPaM aInsPam(aInsPos);
+ const bool bMoved = aInsPam.Move(fnMoveBackward);
+ OSL_ENSURE(bMoved, "No content node found!");
+
+ if (bMoved)
+ {
+ /* Append the new node after the content node
+ found. The new position to insert the moved
+ paragraph at is before the inserted
+ paragraph. */
+ getIDocumentContentOperations().AppendTextNode(*aInsPam.GetPoint());
+ aInsPos = *aInsPam.GetPoint();
+ }
+ }
+
+ --aIdx; // move before insertion
+
+ // adjust empty nodes later
+ SwTextNode const*const pIsEmptyNode(nOffset < SwNodeOffset(0)
+ ? aInsPos.nNode.GetNode().GetTextNode()
+ : aIdx.GetNode().GetTextNode());
+ bool bIsEmptyNode = pIsEmptyNode && pIsEmptyNode->Len() == 0;
+
+ getIDocumentContentOperations().CopyRange(aPam, aInsPos, SwCopyFlags::CheckPosInFly);
+
+ // now delete all the delete redlines that were copied
+#ifndef NDEBUG
+ size_t nRedlines(getIDocumentRedlineAccess().GetRedlineTable().size());
+#endif
+ if (nOffset > SwNodeOffset(0))
+ assert(aPam.End()->nNode.GetIndex() - aPam.Start()->nNode.GetIndex() + nOffset == aInsPos.nNode.GetIndex() - aPam.End()->nNode.GetIndex());
+ else
+ assert(aPam.Start()->nNode.GetIndex() - aPam.End()->nNode.GetIndex() + nOffset == aInsPos.nNode.GetIndex() - aPam.End()->nNode.GetIndex());
+ SwRedlineTable::size_type i;
+ getIDocumentRedlineAccess().GetRedline(*aPam.End(), &i);
+ for ( ; 0 < i; --i)
+ { // iterate backwards and offset via the start nodes difference
+ SwRangeRedline const*const pRedline = getIDocumentRedlineAccess().GetRedlineTable()[i - 1];
+ if (*pRedline->End() < *aPam.Start())
+ {
+ break;
+ }
+ if (pRedline->GetType() == RedlineType::Delete &&
+ // tdf#145066 skip full-paragraph deletion which was jumped over
+ // in Show Changes mode to avoid of deleting an extra row
+ *aPam.Start() <= *pRedline->Start())
+ {
+ SwRangeRedline* pNewRedline;
+ {
+ SwPaM pam(*pRedline, nullptr);
+ SwNodeOffset const nCurrentOffset(
+ nOrigIdx - aPam.Start()->nNode.GetIndex());
+ pam.GetPoint()->nNode += nCurrentOffset;
+ pam.GetPoint()->nContent.Assign(pam.GetPoint()->nNode.GetNode().GetContentNode(), pam.GetPoint()->nContent.GetIndex());
+ pam.GetMark()->nNode += nCurrentOffset;
+ pam.GetMark()->nContent.Assign(pam.GetMark()->nNode.GetNode().GetContentNode(), pam.GetMark()->nContent.GetIndex());
+
+ pNewRedline = new SwRangeRedline( RedlineType::Delete, pam );
+ }
+ // note: effectively this will DeleteAndJoin the pam!
+ getIDocumentRedlineAccess().AppendRedline(pNewRedline, true);
+ assert(getIDocumentRedlineAccess().GetRedlineTable().size() <= nRedlines);
+ }
+ }
+
+ if( bDelLastPara )
+ {
+ // We need to remove the last empty Node again
+ aIdx = aInsPos.nNode;
+ SwContentNode* pCNd = SwNodes::GoPrevious( &aInsPos.nNode );
+ aInsPos.nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
+
+ // All, that are in the to-be-deleted Node, need to be
+ // moved to the next Node
+ for(SwRangeRedline* pTmp : getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ SwPosition* pPos = &pTmp->GetBound();
+ if( pPos->nNode == aIdx )
+ {
+ ++pPos->nNode;
+ pPos->nContent.Assign( pPos->nNode.GetNode().GetContentNode(),0);
+ }
+ pPos = &pTmp->GetBound(false);
+ if( pPos->nNode == aIdx )
+ {
+ ++pPos->nNode;
+ pPos->nContent.Assign( pPos->nNode.GetNode().GetContentNode(),0);
+ }
+ }
+ CorrRel( aIdx, aInsPos );
+
+ if (pCNd)
+ pCNd->JoinNext();
+ }
+
+ ++rOrigPam.GetPoint()->nNode;
+ rOrigPam.GetPoint()->nContent.Assign( rOrigPam.GetContentNode(), 0 );
+ assert(*aPam.GetMark() < *aPam.GetPoint());
+ if (aPam.GetPoint()->nNode.GetNode().IsEndNode())
+ { // ensure redline ends on content node
+ --aPam.GetPoint()->nNode;
+ assert(aPam.GetPoint()->nNode.GetNode().IsTextNode());
+ SwTextNode *const pNode(aPam.GetPoint()->nNode.GetNode().GetTextNode());
+ aPam.GetPoint()->nContent.Assign(pNode, pNode->Len());
+ }
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // this should no longer happen in calls from the UI but maybe via API
+ SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask,
+ "sw.core", "redlines will be moved in DeleteAndJoin");
+
+ getIDocumentRedlineAccess().SetRedlineFlags(
+ RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete );
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoRedlineDelete>(aPam, SwUndoId::DELETE));
+ }
+
+ SwRangeRedline* pNewRedline = new SwRangeRedline( RedlineType::Delete, aPam );
+
+ // prevent assertion from aPam's target being deleted
+ // (Alternatively, one could just let aPam go out of scope, but
+ // that requires touching a lot of code.)
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound(false).nContent.Assign( nullptr, 0 );
+
+ getIDocumentRedlineAccess().AppendRedline( pNewRedline, true );
+
+ aPam.GetBound().nContent.Assign(aPam.GetBound().nNode.GetNode().GetContentNode(), 0);
+ aPam.GetBound(false).nContent.Assign(aPam.GetBound(false).nNode.GetNode().GetContentNode(), 0);
+ sw::UpdateFramesForAddDeleteRedline(*this, aPam);
+
+ // avoid setting empty nodes to tracked insertion
+ if ( bIsEmptyNode )
+ {
+ SwRedlineTable& rTable = getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type nRedlPosWithEmpty =
+ getIDocumentRedlineAccess().GetRedlinePos( pStt->nNode.GetNode(), RedlineType::Insert );
+ if ( SwRedlineTable::npos != nRedlPosWithEmpty )
+ {
+ pOwnRedl = rTable[nRedlPosWithEmpty];
+ SwPosition *pRPos = nOffset < SwNodeOffset(0) ? pOwnRedl->End() : pOwnRedl->Start();
+ SwNodeIndex aIdx2 ( pRPos->nNode );
+ SwTextNode const*const pEmptyNode0(aIdx2.GetNode().GetTextNode());
+ if ( nOffset < SwNodeOffset(0) )
+ {
+ // move up
+ --aIdx2;
+ SwTextNode const*const pEmptyNode(aIdx2.GetNode().GetTextNode());
+ if ( pEmptyNode && pEmptyNode->Len() == 0 )
+ {
+ --(pRPos->nNode);
+ pRPos->nContent.Assign( aIdx2.GetNode().GetContentNode(), 0 );
+ }
+ }
+ else if ( pEmptyNode0 && pEmptyNode0->Len() == 0 )
+ {
+ // move down
+ ++aIdx2;
+ SwTextNode const*const pEmptyNode(aIdx2.GetNode().GetTextNode());
+ if (pEmptyNode)
+ {
+ ++(pRPos->nNode);
+ pRPos->nContent.Assign( aIdx2.GetNode().GetContentNode(), 0 );
+ }
+ }
+
+ // sort redlines, when the trimmed range results bad redline order
+ if ( nRedlPosWithEmpty + 1 < rTable.size() &&
+ *rTable[nRedlPosWithEmpty + 1] < *rTable[nRedlPosWithEmpty] )
+ {
+ rTable.Remove(nRedlPosWithEmpty);
+ rTable.Insert(pOwnRedl);
+ }
+ }
+ }
+
+ getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ getIDocumentState().SetModified();
+
+ return true;
+ }
+ }
+
+ if( !pOwnRedl && !getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aTemp(aIdx);
+ getIDocumentRedlineAccess().SplitRedline(aTemp);
+ }
+
+ SwNodeOffset nRedlSttNd(0), nRedlEndNd(0);
+ if( pOwnRedl )
+ {
+ const SwPosition *pRStt = pOwnRedl->Start(), *pREnd = pOwnRedl->End();
+ nRedlSttNd = pRStt->nNode.GetIndex();
+ nRedlEndNd = pREnd->nNode.GetIndex();
+ }
+
+ std::unique_ptr<SwUndoMoveNum> pUndo;
+ SwNodeOffset nMoved(0);
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoMoveNum( rPam, nOffset, bIsOutlMv ));
+ nMoved = rPam.End()->nNode.GetIndex() - rPam.Start()->nNode.GetIndex() + 1;
+ }
+
+ (void) pLayout; // note: move will insert between aIdx-1 and aIdx
+ assert(!pLayout // check not moving *into* delete redline (caller's fault)
+ || aIdx.GetNode().GetRedlineMergeFlag() == SwNode::Merge::None
+ || aIdx.GetNode().GetRedlineMergeFlag() == SwNode::Merge::First);
+ getIDocumentContentOperations().MoveNodeRange( aMvRg, aIdx, SwMoveFlags::REDLINES );
+
+ if( pUndo )
+ {
+ // i57907: Under circumstances (sections at the end of a chapter)
+ // the rPam.Start() is not moved to the new position.
+ // But aIdx should be at the new end position and as long as the
+ // number of moved paragraphs is nMoved, I know, where the new
+ // position is.
+ pUndo->SetStartNode( aIdx.GetIndex() - nMoved );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+
+ if( pOwnRedl )
+ {
+ SwPosition *pRStt = pOwnRedl->Start(), *pREnd = pOwnRedl->End();
+ if( pRStt->nNode.GetIndex() != nRedlSttNd )
+ {
+ pRStt->nNode = nRedlSttNd;
+ pRStt->nContent.Assign( pRStt->nNode.GetNode().GetContentNode(),0);
+ }
+ if( pREnd->nNode.GetIndex() != nRedlEndNd )
+ {
+ pREnd->nNode = nRedlEndNd;
+ SwContentNode* pCNd = pREnd->nNode.GetNode().GetContentNode();
+ pREnd->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
+ }
+ }
+
+ getIDocumentState().SetModified();
+ return true;
+}
+
+bool SwDoc::NumOrNoNum( const SwNodeIndex& rIdx, bool bDel )
+{
+ bool bResult = false;
+ SwTextNode * pTextNd = rIdx.GetNode().GetTextNode();
+
+ if (pTextNd && pTextNd->GetNumRule() != nullptr &&
+ (pTextNd->HasNumber() || pTextNd->HasBullet()))
+ {
+ if ( !pTextNd->IsCountedInList() == !bDel)
+ {
+ bool bOldNum = bDel;
+ bool bNewNum = !bDel;
+ pTextNd->SetCountedInList(bNewNum);
+
+ getIDocumentState().SetModified();
+
+ bResult = true;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumOrNoNum>(rIdx, bOldNum, bNewNum));
+ }
+ }
+ else if (bDel && pTextNd->GetNumRule(false) &&
+ pTextNd->GetActualListLevel() >= 0 &&
+ pTextNd->GetActualListLevel() < MAXLEVEL)
+ {
+ SwPaM aPam(*pTextNd);
+ DelNumRules(aPam);
+
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+SwNumRule* SwDoc::GetNumRuleAtPos(SwPosition& rPos,
+ SwRootFrame const*const pLayout)
+{
+ SwNumRule* pRet = nullptr;
+ SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
+
+ if ( pTNd != nullptr )
+ {
+ if (pLayout && !sw::IsParaPropsNode(*pLayout, *pTNd))
+ {
+ pTNd = static_cast<SwTextFrame*>(pTNd->getLayoutFrame(pLayout))->GetMergedPara()->pParaPropsNode;
+ rPos.nNode = *pTNd;
+ rPos.nContent.Assign(pTNd, 0);
+ }
+ pRet = pTNd->GetNumRule();
+ }
+
+ return pRet;
+}
+
+sal_uInt16 SwDoc::FindNumRule( std::u16string_view rName ) const
+{
+ for( sal_uInt16 n = mpNumRuleTable->size(); n; )
+ if( (*mpNumRuleTable)[ --n ]->GetName() == rName )
+ return n;
+
+ return USHRT_MAX;
+}
+
+SwNumRule* SwDoc::FindNumRulePtr( const OUString& rName ) const
+{
+ SwNumRule * pResult = maNumRuleMap[rName];
+
+ if ( !pResult )
+ {
+ for (size_t n = 0; n < mpNumRuleTable->size(); ++n)
+ {
+ if ((*mpNumRuleTable)[n]->GetName() == rName)
+ {
+ pResult = (*mpNumRuleTable)[n];
+
+ break;
+ }
+ }
+ }
+
+ return pResult;
+}
+
+void SwDoc::AddNumRule(SwNumRule * pRule)
+{
+ if ((SAL_MAX_UINT16 - 1) <= mpNumRuleTable->size())
+ {
+ OSL_ENSURE(false, "SwDoc::AddNumRule: table full.");
+ abort(); // this should never happen on real documents
+ }
+ mpNumRuleTable->push_back(pRule);
+ maNumRuleMap[pRule->GetName()] = pRule;
+ pRule->SetNumRuleMap(&maNumRuleMap);
+
+ getIDocumentListsAccess().createListForListStyle( pRule->GetName() );
+}
+
+sal_uInt16 SwDoc::MakeNumRule( const OUString &rName,
+ const SwNumRule* pCpy,
+ bool bBroadcast,
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode )
+{
+ SwNumRule* pNew;
+ if( pCpy )
+ {
+ pNew = new SwNumRule( *pCpy );
+
+ pNew->SetName( GetUniqueNumRuleName( &rName ), getIDocumentListsAccess() );
+
+ if( pNew->GetName() != rName )
+ {
+ pNew->SetPoolFormatId( USHRT_MAX );
+ pNew->SetPoolHelpId( USHRT_MAX );
+ pNew->SetPoolHlpFileId( UCHAR_MAX );
+ pNew->SetDefaultListId( OUString() );
+ }
+ pNew->CheckCharFormats( *this );
+ }
+ else
+ {
+ pNew = new SwNumRule( GetUniqueNumRuleName( &rName ),
+ eDefaultNumberFormatPositionAndSpaceMode );
+ }
+
+ sal_uInt16 nRet = mpNumRuleTable->size();
+
+ AddNumRule(pNew);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoNumruleCreate>(pNew, *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(pNew->GetName(), SfxStyleFamily::Pseudo,
+ SfxHintId::StyleSheetCreated);
+
+ return nRet;
+}
+
+OUString SwDoc::GetUniqueNumRuleName( const OUString* pChkStr, bool bAutoNum ) const
+{
+ // If we got pChkStr, then the caller expects that in case it's not yet
+ // used, it'll be returned.
+ if( IsInMailMerge() && !pChkStr )
+ {
+ OUString newName = "MailMergeNumRule"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpNumRuleTable->size() + 1 );
+ return newName;
+ }
+
+ OUString aName;
+ if( bAutoNum )
+ {
+ static bool bHack = (getenv("LIBO_ONEWAY_STABLE_ODF_EXPORT") != nullptr);
+
+ if (bHack)
+ {
+ static sal_Int64 nIdCounter = SAL_CONST_INT64(8000000000);
+ aName = OUString::number(nIdCounter++);
+ }
+ else
+ {
+ unsigned int const n(comphelper::rng::uniform_uint_distribution(0,
+ std::numeric_limits<unsigned int>::max()));
+ aName = OUString::number(n);
+ }
+ if( pChkStr && pChkStr->isEmpty() )
+ pChkStr = nullptr;
+ }
+ else if( pChkStr && !pChkStr->isEmpty() )
+ aName = *pChkStr;
+ else
+ {
+ pChkStr = nullptr;
+ aName = SwResId( STR_NUMRULE_DEFNAME );
+ }
+
+ sal_uInt16 nNum(0), nTmp, nFlagSize = ( mpNumRuleTable->size() / 8 ) +2;
+ std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ sal_Int32 nNmLen = aName.getLength();
+ if( !bAutoNum && pChkStr )
+ {
+ while( nNmLen-- && '0' <= aName[nNmLen] && aName[nNmLen] <= '9' )
+ ; //nop
+
+ if( ++nNmLen < aName.getLength() )
+ {
+ aName = aName.copy(0, nNmLen );
+ pChkStr = nullptr;
+ }
+ }
+
+ for( auto const & pNumRule: *mpNumRuleTable )
+ if( nullptr != pNumRule )
+ {
+ const OUString sNm = pNumRule->GetName();
+ if( sNm.startsWith( aName ) )
+ {
+ // Determine Number and set the Flag
+ nNum = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sNm.subView( nNmLen )));
+ if( nNum-- && nNum < mpNumRuleTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ if( pChkStr && *pChkStr==sNm )
+ pChkStr = nullptr;
+ }
+
+ if( !pChkStr )
+ {
+ // All Numbers have been flagged accordingly, so identify the right Number
+ nNum = mpNumRuleTable->size();
+ for( sal_uInt16 n = 0; n < nFlagSize; ++n )
+ {
+ nTmp = pSetFlags[ n ];
+ if( 0xff != nTmp )
+ {
+ // identify the Number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+ }
+ if( pChkStr && !pChkStr->isEmpty() )
+ return *pChkStr;
+ return aName + OUString::number( ++nNum );
+}
+
+void SwDoc::UpdateNumRule()
+{
+ const SwNumRuleTable& rNmTable = GetNumRuleTable();
+ for( size_t n = 0; n < rNmTable.size(); ++n )
+ if( rNmTable[ n ]->IsInvalidRule() )
+ rNmTable[ n ]->Validate(*this);
+}
+
+void SwDoc::MarkListLevel( const OUString& sListId,
+ const int nListLevel,
+ const bool bValue )
+{
+ SwList* pList = getIDocumentListsAccess().getListByName( sListId );
+
+ if ( pList )
+ {
+ // Set new marked list level and notify all affected nodes of the changed mark.
+ pList->MarkListLevel( nListLevel, bValue );
+ }
+}
+
+bool SwDoc::IsFirstOfNumRuleAtPos(const SwPosition & rPos,
+ SwRootFrame const& rLayout)
+{
+ bool bResult = false;
+
+ const SwTextNode *const pTextNode = sw::GetParaPropsNode(rLayout, rPos.nNode);
+ if ( pTextNode != nullptr )
+ {
+ bResult = pTextNode->IsFirstOfNumRule(rLayout);
+ }
+
+ return bResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
new file mode 100644
index 000000000..002184cd6
--- /dev/null
+++ b/sw/source/core/doc/docredln.cxx
@@ -0,0 +1,2136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+#include <boost/property_tree/json_parser.hpp>
+
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <tools/datetimeutils.hxx>
+#include <hintids.hxx>
+#include <svl/itemiter.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <unotools/datetime.hxx>
+#include <sfx2/viewsh.hxx>
+#include <o3tl/string_view.hxx>
+#include <swmodule.hxx>
+#include <doc.hxx>
+#include <docredln.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <redline.hxx>
+#include <UndoCore.hxx>
+#include <hints.hxx>
+#include <pamtyp.hxx>
+#include <poolfmt.hxx>
+#include <algorithm>
+#include <limits>
+#include <view.hxx>
+#include <viewopt.hxx>
+#include <usrpref.hxx>
+#include <viewsh.hxx>
+#include <viscrs.hxx>
+#include <rootfrm.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <wrtsh.hxx>
+#include <txtfld.hxx>
+
+#include <flowfrm.hxx>
+#include <txtfrm.hxx>
+
+using namespace com::sun::star;
+
+#ifdef DBG_UTIL
+
+ void sw_DebugRedline( const SwDoc* pDoc )
+ {
+ static SwRedlineTable::size_type nWatch = 0; // loplugin:constvars:ignore
+ const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ for( SwRedlineTable::size_type n = 0; n < rTable.size(); ++n )
+ {
+ volatile SwRedlineTable::size_type nDummy = 0;
+ const SwRangeRedline* pCurrent = rTable[ n ];
+ const SwRangeRedline* pNext = n+1 < rTable.size() ? rTable[ n+1 ] : nullptr;
+ if( pCurrent == pNext )
+ (void) nDummy;
+ if( n == nWatch )
+ (void) nDummy; // Possible debugger breakpoint
+ }
+ }
+
+#endif
+
+
+SwExtraRedlineTable::~SwExtraRedlineTable()
+{
+ DeleteAndDestroyAll();
+}
+
+void SwExtraRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedlineTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ for (sal_uInt16 nCurExtraRedlinePos = 0; nCurExtraRedlinePos < GetSize(); ++nCurExtraRedlinePos)
+ {
+ const SwExtraRedline* pExtraRedline = GetRedline(nCurExtraRedlinePos);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExtraRedline"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*pExtraRedline).name()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+#if OSL_DEBUG_LEVEL > 0
+static bool CheckPosition( const SwPosition* pStt, const SwPosition* pEnd )
+{
+ int nError = 0;
+ SwNode* pSttNode = &pStt->nNode.GetNode();
+ SwNode* pEndNode = &pEnd->nNode.GetNode();
+ SwNode* pSttTab = pSttNode->StartOfSectionNode()->FindTableNode();
+ SwNode* pEndTab = pEndNode->StartOfSectionNode()->FindTableNode();
+ SwNode* pSttStart = pSttNode;
+ while( pSttStart && (!pSttStart->IsStartNode() || pSttStart->IsSectionNode() ||
+ pSttStart->IsTableNode() ) )
+ pSttStart = pSttStart->StartOfSectionNode();
+ SwNode* pEndStart = pEndNode;
+ while( pEndStart && (!pEndStart->IsStartNode() || pEndStart->IsSectionNode() ||
+ pEndStart->IsTableNode() ) )
+ pEndStart = pEndStart->StartOfSectionNode();
+ assert(pSttTab == pEndTab);
+ if( pSttTab != pEndTab )
+ nError = 1;
+ assert(pSttTab || pSttStart == pEndStart);
+ if( !pSttTab && pSttStart != pEndStart )
+ nError |= 2;
+ if( nError )
+ nError += 10;
+ return nError != 0;
+}
+#endif
+
+bool SwExtraRedlineTable::DeleteAllTableRedlines( SwDoc& rDoc, const SwTable& rTable, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for (sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ if (pTableCellRedline)
+ {
+ const SwTableBox *pRedTabBox = &pTableCellRedline->GetTableBox();
+ const SwTable& rRedTable = pRedTabBox->GetSttNd()->FindTableNode()->GetTable();
+ if ( &rRedTable == &rTable )
+ {
+ // Redline for this table
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if (RedlineType::Any == nRedlineTypeToDelete || nRedlineTypeToDelete == nRedlineType)
+ {
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ continue; // don't increment position after delete
+ }
+ }
+ }
+ ++nCurRedlinePos;
+ }
+
+ if( bChg )
+ rDoc.getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+bool SwExtraRedlineTable::DeleteTableRowRedline( SwDoc* pDoc, const SwTableLine& rTableLine, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableRowRedline* pTableRowRedline = dynamic_cast<const SwTableRowRedline*>(pExtraRedline);
+ const SwTableLine *pRedTabLine = pTableRowRedline ? &pTableRowRedline->GetTableLine() : nullptr;
+ if ( pRedTabLine == &rTableLine )
+ {
+ // Redline for this table row
+ const SwRedlineData& aRedlineData = pTableRowRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
+ continue;
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ }
+ }
+
+ if( bChg )
+ pDoc->getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+bool SwExtraRedlineTable::DeleteTableCellRedline( SwDoc* pDoc, const SwTableBox& rTableBox, bool bSaveInUndo, RedlineType nRedlineTypeToDelete )
+{
+ bool bChg = false;
+
+ if (bSaveInUndo && pDoc->GetIDocumentUndoRedo().DoesUndo())
+ {
+ // #TODO - Add 'Undo' support for deleting 'Table Cell' redlines
+ /*
+ SwUndoRedline* pUndo = new SwUndoRedline( SwUndoId::REDLINE, rRange );
+ if( pUndo->GetRedlSaveCount() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(pUndo);
+ }
+ else
+ delete pUndo;
+ */
+ }
+
+ for(sal_uInt16 nCurRedlinePos = 0; nCurRedlinePos < GetSize(); ++nCurRedlinePos )
+ {
+ SwExtraRedline* pExtraRedline = GetRedline(nCurRedlinePos);
+ const SwTableCellRedline* pTableCellRedline = dynamic_cast<const SwTableCellRedline*>(pExtraRedline);
+ const SwTableBox *pRedTabBox = pTableCellRedline ? &pTableCellRedline->GetTableBox() : nullptr;
+ if ( pRedTabBox == &rTableBox )
+ {
+ // Redline for this table cell
+ const SwRedlineData& aRedlineData = pTableCellRedline->GetRedlineData();
+ const RedlineType nRedlineType = aRedlineData.GetType();
+
+ // Check if this redline object type should be deleted
+ if( RedlineType::Any != nRedlineTypeToDelete && nRedlineTypeToDelete != nRedlineType )
+ continue;
+
+ DeleteAndDestroy( nCurRedlinePos );
+ bChg = true;
+ }
+ }
+
+ if( bChg )
+ pDoc->getIDocumentState().SetModified();
+
+ return bChg;
+}
+
+namespace
+{
+
+void lcl_LOKInvalidateFrames(const sw::BroadcastingModify& rMod, const SwRootFrame* pLayout,
+ SwFrameType const nFrameType, const Point* pPoint)
+{
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
+
+ for (SwFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
+ {
+ if ((pTmpFrame->GetType() & nFrameType) &&
+ (!pLayout || pLayout == pTmpFrame->getRootFrame()) &&
+ (!pTmpFrame->IsFlowFrame() || !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow()))
+ {
+ if (pPoint)
+ {
+ pTmpFrame->InvalidateSize();
+
+ // Also empty the text portion cache, so it gets rebuilt, taking the new redlines
+ // into account.
+ if (pTmpFrame->IsTextFrame())
+ {
+ auto pTextFrame = static_cast<SwTextFrame*>(pTmpFrame);
+ pTextFrame->ClearPara();
+ }
+ }
+ }
+ }
+}
+
+void lcl_LOKInvalidateStartEndFrames(SwShellCursor& rCursor)
+{
+ if (!(rCursor.HasMark() &&
+ rCursor.GetPoint()->nNode.GetNode().IsContentNode() &&
+ rCursor.GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout()) &&
+ (rCursor.GetMark()->nNode == rCursor.GetPoint()->nNode ||
+ (rCursor.GetMark()->nNode.GetNode().IsContentNode() &&
+ rCursor.GetMark()->nNode.GetNode().GetContentNode()->getLayoutFrame(rCursor.GetShell()->GetLayout())))))
+ {
+ return;
+ }
+
+
+ SwPosition *pStartPos = rCursor.Start(),
+ *pEndPos = rCursor.End();
+
+
+ lcl_LOKInvalidateFrames(*(pStartPos->nNode.GetNode().GetContentNode()),
+ rCursor.GetShell()->GetLayout(),
+ FRM_CNTNT, &rCursor.GetSttPos());
+
+ lcl_LOKInvalidateFrames(*(pEndPos->nNode.GetNode().GetContentNode()),
+ rCursor.GetShell()->GetLayout(),
+ FRM_CNTNT, &rCursor.GetEndPos());
+}
+
+bool lcl_LOKRedlineNotificationEnabled()
+{
+ static bool bDisableRedlineComments = getenv("DISABLE_REDLINE") != nullptr;
+ if (comphelper::LibreOfficeKit::isActive() && !bDisableRedlineComments)
+ return true;
+
+ return false;
+}
+
+} // anonymous namespace
+
+/// Emits LOK notification about one addition / removal of a redline item.
+void SwRedlineTable::LOKRedlineNotification(RedlineNotification nType, SwRangeRedline* pRedline)
+{
+ // Disable since usability is very low beyond some small number of changes.
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ boost::property_tree::ptree aRedline;
+ aRedline.put("action", (nType == RedlineNotification::Add ? "Add" :
+ (nType == RedlineNotification::Remove ? "Remove" :
+ (nType == RedlineNotification::Modify ? "Modify" : "???"))));
+ aRedline.put("index", pRedline->GetId());
+ aRedline.put("author", pRedline->GetAuthorString(1).toUtf8().getStr());
+ aRedline.put("type", SwRedlineTypeToOUString(pRedline->GetRedlineData().GetType()).toUtf8().getStr());
+ aRedline.put("comment", pRedline->GetRedlineData().GetComment().toUtf8().getStr());
+ aRedline.put("description", pRedline->GetDescr().toUtf8().getStr());
+ OUString sDateTime = utl::toISO8601(pRedline->GetRedlineData().GetTimeStamp().GetUNODateTime());
+ aRedline.put("dateTime", sDateTime.toUtf8().getStr());
+
+ SwPosition* pStartPos = pRedline->Start();
+ SwPosition* pEndPos = pRedline->End();
+ SwContentNode* pContentNd = pRedline->GetContentNode();
+ SwView* pView = dynamic_cast<SwView*>(SfxViewShell::Current());
+ if (pView && pContentNd)
+ {
+ SwShellCursor aCursor(pView->GetWrtShell(), *pStartPos);
+ aCursor.SetMark();
+ aCursor.GetMark()->nNode = pEndPos->nNode;
+ aCursor.GetMark()->nContent = pEndPos->nContent;
+
+ aCursor.FillRects();
+
+ SwRects* pRects(&aCursor);
+ std::vector<OString> aRects;
+ for(const SwRect& rNextRect : *pRects)
+ aRects.push_back(rNextRect.SVRect().toString());
+
+ const OString sRects = comphelper::string::join("; ", aRects);
+ aRedline.put("textRange", sRects.getStr());
+
+ lcl_LOKInvalidateStartEndFrames(aCursor);
+
+ // When this notify method is called text invalidation is not done yet
+ // Calling FillRects updates the text area so invalidation will not run on the correct rects
+ // So we need to do an own invalidation here. It invalidates text frames containing the redlining
+ SwDoc& rDoc = pRedline->GetDoc();
+ SwViewShell* pSh;
+ if( !rDoc.IsInDtor() )
+ {
+ pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh )
+ for(SwNodeIndex nIdx = pStartPos->nNode; nIdx <= pEndPos->nNode; ++nIdx)
+ {
+ SwContentNode* pContentNode = nIdx.GetNode().GetContentNode();
+ if (pContentNode)
+ pSh->InvalidateWindows(pContentNode->FindLayoutRect());
+ }
+ }
+ }
+
+ boost::property_tree::ptree aTree;
+ aTree.add_child("redline", aRedline);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTree);
+ std::string aPayload = aStream.str();
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ if (pView && pView->GetDocId() == pViewShell->GetDocId())
+ pViewShell->libreOfficeKitViewCallback(nType == RedlineNotification::Modify ? LOK_CALLBACK_REDLINE_TABLE_ENTRY_MODIFIED : LOK_CALLBACK_REDLINE_TABLE_SIZE_CHANGED, aPayload.c_str());
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+bool SwRedlineTable::Insert(SwRangeRedline*& p)
+{
+ if( p->HasValidRange() )
+ {
+ std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
+ size_type nP = rv.first - begin();
+ LOKRedlineNotification(RedlineNotification::Add, p);
+
+ // detect text moving by checking nearby redlines, except during Undo
+ // (apply isMoved() during OpenDocument and DOCX import, too, to fix
+ // missing text moving handling in ODF and e.g. web version of MSO)
+ if ( p->GetDoc().GetIDocumentUndoRedo().DoesUndo() ||
+ p->GetDoc().IsInWriterfilterImport() ||
+ p->GetDoc().IsInXMLImport() )
+ {
+ isMoved(nP);
+ }
+
+ p->CallDisplayFunc(nP);
+ if (rv.second)
+ CheckOverlapping(rv.first);
+ return rv.second;
+ }
+ return InsertWithValidRanges( p );
+}
+
+void SwRedlineTable::CheckOverlapping(vector_type::const_iterator it)
+{
+ if (m_bHasOverlappingElements)
+ return;
+ if (maVector.size() <= 1) // a single element cannot be overlapping
+ return;
+ auto pCurr = *it;
+ auto itNext = it + 1;
+ if (itNext != maVector.end())
+ {
+ auto pNext = *itNext;
+ if (pCurr->End()->nNode.GetIndex() >= pNext->Start()->nNode.GetIndex())
+ {
+ m_bHasOverlappingElements = true;
+ return;
+ }
+ }
+ if (it != maVector.begin())
+ {
+ auto pPrev = *(it - 1);
+ if (pPrev->End()->nNode.GetIndex() >= pCurr->Start()->nNode.GetIndex())
+ m_bHasOverlappingElements = true;
+ }
+}
+
+bool SwRedlineTable::Insert(SwRangeRedline*& p, size_type& rP)
+{
+ if( p->HasValidRange() )
+ {
+ std::pair<vector_type::const_iterator, bool> rv = maVector.insert( p );
+ rP = rv.first - begin();
+ p->CallDisplayFunc(rP);
+ if (rv.second)
+ CheckOverlapping(rv.first);
+ return rv.second;
+ }
+ return InsertWithValidRanges( p, &rP );
+}
+
+namespace sw {
+
+std::vector<SwRangeRedline*> GetAllValidRanges(std::unique_ptr<SwRangeRedline> p)
+{
+ std::vector<SwRangeRedline*> ret;
+ // Create valid "sub-ranges" from the Selection
+ SwPosition* pStt = p->Start(),
+ * pEnd = p->End();
+ SwPosition aNewStt( *pStt );
+ SwNodes& rNds = aNewStt.nNode.GetNodes();
+ SwContentNode* pC;
+
+ if( !aNewStt.nNode.GetNode().IsContentNode() )
+ {
+ pC = rNds.GoNext( &aNewStt.nNode );
+ if( pC )
+ aNewStt.nContent.Assign( pC, 0 );
+ else
+ aNewStt.nNode = rNds.GetEndOfContent();
+ }
+
+ SwRangeRedline* pNew = nullptr;
+
+ if( aNewStt < *pEnd )
+ do {
+ if( !pNew )
+ pNew = new SwRangeRedline( p->GetRedlineData(), aNewStt );
+ else
+ {
+ pNew->DeleteMark();
+ *pNew->GetPoint() = aNewStt;
+ }
+
+ pNew->SetMark();
+ GoEndSection( pNew->GetPoint() );
+ // i60396: If the redlines starts before a table but the table is the last member
+ // of the section, the GoEndSection will end inside the table.
+ // This will result in an incorrect redline, so we've to go back
+ SwNode* pTab = pNew->GetPoint()->nNode.GetNode().StartOfSectionNode()->FindTableNode();
+ // We end in a table when pTab != 0
+ if( pTab && !pNew->GetMark()->nNode.GetNode().StartOfSectionNode()->FindTableNode() )
+ { // but our Mark was outside the table => Correction
+ do
+ {
+ // We want to be before the table
+ *pNew->GetPoint() = SwPosition(*pTab);
+ pC = GoPreviousNds( &pNew->GetPoint()->nNode, false ); // here we are.
+ if( pC )
+ pNew->GetPoint()->nContent.Assign( pC, 0 );
+ pTab = pNew->GetPoint()->nNode.GetNode().StartOfSectionNode()->FindTableNode();
+ } while( pTab ); // If there is another table we have to repeat our step backwards
+ }
+
+ if( *pNew->GetPoint() > *pEnd )
+ {
+ pC = nullptr;
+ if( aNewStt.nNode != pEnd->nNode )
+ do {
+ SwNode& rCurNd = aNewStt.nNode.GetNode();
+ if( rCurNd.IsStartNode() )
+ {
+ if( rCurNd.EndOfSectionIndex() < pEnd->nNode.GetIndex() )
+ aNewStt.nNode = *rCurNd.EndOfSectionNode();
+ else
+ break;
+ }
+ else if( rCurNd.IsContentNode() )
+ pC = rCurNd.GetContentNode();
+ ++aNewStt.nNode;
+ } while( aNewStt.nNode.GetIndex() < pEnd->nNode.GetIndex() );
+
+ if( aNewStt.nNode == pEnd->nNode )
+ aNewStt.nContent = pEnd->nContent;
+ else if( pC )
+ {
+ aNewStt.nNode = *pC;
+ aNewStt.nContent.Assign( pC, pC->Len() );
+ }
+
+ if( aNewStt <= *pEnd )
+ *pNew->GetPoint() = aNewStt;
+ }
+ else
+ aNewStt = *pNew->GetPoint();
+#if OSL_DEBUG_LEVEL > 0
+ CheckPosition( pNew->GetPoint(), pNew->GetMark() );
+#endif
+
+ if( *pNew->GetPoint() != *pNew->GetMark() &&
+ pNew->HasValidRange())
+ {
+ ret.push_back(pNew);
+ pNew = nullptr;
+ }
+
+ if( aNewStt >= *pEnd )
+ break;
+ pC = rNds.GoNext( &aNewStt.nNode );
+ if( !pC )
+ break;
+
+ aNewStt.nContent.Assign( pC, 0 );
+
+ } while( aNewStt < *pEnd );
+
+ delete pNew;
+ p.reset();
+ return ret;
+}
+
+} // namespace sw
+
+bool SwRedlineTable::InsertWithValidRanges(SwRangeRedline*& p, size_type* pInsPos)
+{
+ bool bAnyIns = false;
+ std::vector<SwRangeRedline*> const redlines(
+ GetAllValidRanges(std::unique_ptr<SwRangeRedline>(p)));
+ for (SwRangeRedline * pRedline : redlines)
+ {
+ assert(pRedline->HasValidRange());
+ size_type nInsPos;
+ if (Insert(pRedline, nInsPos))
+ {
+ pRedline->CallDisplayFunc(nInsPos);
+ bAnyIns = true;
+ if (pInsPos && *pInsPos < nInsPos)
+ {
+ *pInsPos = nInsPos;
+ }
+ }
+ }
+ p = nullptr;
+ return bAnyIns;
+}
+
+bool CompareSwRedlineTable::operator()(SwRangeRedline* const &lhs, SwRangeRedline* const &rhs) const
+{
+ return *lhs < *rhs;
+}
+
+SwRedlineTable::~SwRedlineTable()
+{
+ maVector.DeleteAndDestroyAll();
+}
+
+SwRedlineTable::size_type SwRedlineTable::GetPos(const SwRangeRedline* p) const
+{
+ vector_type::const_iterator it = maVector.find(const_cast<SwRangeRedline*>(p));
+ if( it == maVector.end() )
+ return npos;
+ return it - maVector.begin();
+}
+
+void SwRedlineTable::Remove( const SwRangeRedline* p )
+{
+ const size_type nPos = GetPos(p);
+ if (nPos == npos)
+ return;
+ Remove(nPos);
+}
+
+void SwRedlineTable::Remove( size_type nP )
+{
+ LOKRedlineNotification(RedlineNotification::Remove, maVector[nP]);
+ SwDoc* pDoc = nullptr;
+ if( !nP && 1 == size() )
+ pDoc = &maVector.front()->GetDoc();
+
+ maVector.erase( maVector.begin() + nP );
+
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh )
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ }
+}
+
+void SwRedlineTable::DeleteAndDestroyAll()
+{
+ while (!maVector.empty())
+ {
+ auto const pRedline = maVector.back();
+ maVector.erase_at(maVector.size() - 1);
+ LOKRedlineNotification(RedlineNotification::Remove, pRedline);
+ delete pRedline;
+ }
+ m_bHasOverlappingElements = false;
+}
+
+void SwRedlineTable::DeleteAndDestroy(size_type const nP)
+{
+ auto const pRedline = maVector[nP];
+ maVector.erase(maVector.begin() + nP);
+ LOKRedlineNotification(RedlineNotification::Remove, pRedline);
+ delete pRedline;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindNextOfSeqNo( size_type nSttPos ) const
+{
+ return nSttPos + 1 < size()
+ ? FindNextSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos+1 )
+ : npos;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindPrevOfSeqNo( size_type nSttPos ) const
+{
+ return nSttPos ? FindPrevSeqNo( operator[]( nSttPos )->GetSeqNo(), nSttPos-1 )
+ : npos;
+}
+
+/// Find the next or preceding Redline with the same seq.no.
+/// We can limit the search using look ahead (0 searches the whole array).
+SwRedlineTable::size_type SwRedlineTable::FindNextSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
+{
+ auto constexpr nLookahead = 20;
+ size_type nRet = npos;
+ if( nSeqNo && nSttPos < size() )
+ {
+ size_type nEnd = size();
+ const size_type nTmp = nSttPos + nLookahead;
+ if (nTmp < nEnd)
+ {
+ nEnd = nTmp;
+ }
+
+ for( ; nSttPos < nEnd; ++nSttPos )
+ if( nSeqNo == operator[]( nSttPos )->GetSeqNo() )
+ {
+ nRet = nSttPos;
+ break;
+ }
+ }
+ return nRet;
+}
+
+SwRedlineTable::size_type SwRedlineTable::FindPrevSeqNo( sal_uInt16 nSeqNo, size_type nSttPos ) const
+{
+ auto constexpr nLookahead = 20;
+ size_type nRet = npos;
+ if( nSeqNo && nSttPos < size() )
+ {
+ size_type nEnd = 0;
+ if( nSttPos > nLookahead )
+ nEnd = nSttPos - nLookahead;
+
+ ++nSttPos;
+ while( nSttPos > nEnd )
+ if( nSeqNo == operator[]( --nSttPos )->GetSeqNo() )
+ {
+ nRet = nSttPos;
+ break;
+ }
+ }
+ return nRet;
+}
+
+const SwRangeRedline* SwRedlineTable::FindAtPosition( const SwPosition& rSttPos,
+ size_type& rPos,
+ bool bNext ) const
+{
+ const SwRangeRedline* pFnd = nullptr;
+ for( ; rPos < maVector.size() ; ++rPos )
+ {
+ const SwRangeRedline* pTmp = (*this)[ rPos ];
+ if( pTmp->HasMark() && pTmp->IsVisible() )
+ {
+ const SwPosition* pRStt = pTmp->Start(),
+ * pREnd = pTmp->End();
+ if( bNext ? *pRStt <= rSttPos : *pRStt < rSttPos )
+ {
+ if( bNext ? *pREnd > rSttPos : *pREnd >= rSttPos )
+ {
+ pFnd = pTmp;
+ break;
+ }
+ }
+ else
+ break;
+ }
+ }
+ return pFnd;
+}
+
+bool SwRedlineTable::isMoved( size_type rPos ) const
+{
+ bool bRet = false;
+ auto constexpr nLookahead = 20;
+ SwRangeRedline* pRedline = (*this)[ rPos ];
+
+ // set redline type of the searched pair
+ RedlineType nPairType = pRedline->GetType();
+ if ( RedlineType::Delete == nPairType )
+ nPairType = RedlineType::Insert;
+ else if ( RedlineType::Insert == nPairType )
+ nPairType = RedlineType::Delete;
+ else
+ // only deleted or inserted text can be moved
+ return false;
+
+ bool bDeletePaM = false;
+ SwPaM* pPaM;
+
+ // if this redline is visible the content is in this PaM
+ if ( nullptr == pRedline->GetContentIdx() )
+ {
+ pPaM = pRedline;
+ }
+ else // otherwise it is saved in pContentSect, e.g. during ODT import
+ {
+ SwNodeIndex aTmpIdx( *pRedline->GetContentIdx()->GetNode().EndOfSectionNode() );
+ pPaM = new SwPaM(*pRedline->GetContentIdx(), aTmpIdx );
+ bDeletePaM = true;
+ }
+
+ const OUString sTrimmed = pPaM->GetText().trim();
+ // detection of move needs at least 6 characters with an inner
+ // space after stripping white spaces of the redline to skip
+ // frequent deleted and inserted articles or other common
+ // word parts, e.g. 'the' and 'of a' to detect as text moving
+ if ( sTrimmed.getLength() < 6 || sTrimmed.indexOf(' ') == -1 )
+ {
+ if ( bDeletePaM )
+ delete pPaM;
+ return false;
+ }
+
+ // search pair around the actual redline
+ size_type nEnd = rPos + nLookahead < size()
+ ? rPos + nLookahead
+ : size();
+ rPos = rPos > nLookahead ? rPos - nLookahead : 0;
+ for ( ; rPos < nEnd && !bRet ; ++rPos )
+ {
+ SwRangeRedline* pPair = (*this)[ rPos ];
+
+ // redline must be the requested type and from the same author
+ if ( nPairType != pPair->GetType() ||
+ pRedline->GetAuthor() != pPair->GetAuthor() )
+ {
+ continue;
+ }
+
+ bool bDeletePairPaM = false;
+ SwPaM* pPairPaM;
+
+ // if this redline is visible the content is in this PaM
+ if ( nullptr == pPair->GetContentIdx() )
+ {
+ pPairPaM = pPair;
+ }
+ else // otherwise it is saved in pContentSect, e.g. during ODT import
+ {
+ // saved in pContentSect, e.g. during ODT import
+ SwNodeIndex aTmpIdx( *pPair->GetContentIdx()->GetNode().EndOfSectionNode() );
+ pPairPaM = new SwPaM(*pPair->GetContentIdx(), aTmpIdx );
+ bDeletePairPaM = true;
+ }
+
+ // pair at tracked moving: same text by trimming trailing white spaces
+ if ( abs(pPaM->GetText().getLength() - pPairPaM->GetText().getLength()) <= 2 &&
+ sTrimmed == o3tl::trim(pPairPaM->GetText()) )
+ {
+ pRedline->SetMoved();
+ pPair->SetMoved();
+ pPair->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ bRet = true;
+ }
+
+ if ( bDeletePairPaM )
+ delete pPairPaM;
+ }
+
+ if ( bDeletePaM )
+ delete pPaM;
+
+ return bRet;
+}
+
+void SwRedlineTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRedlineTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ for (SwRedlineTable::size_type nCurRedlinePos = 0; nCurRedlinePos < size(); ++nCurRedlinePos)
+ operator[](nCurRedlinePos)->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwRedlineExtraData::~SwRedlineExtraData()
+{
+}
+
+void SwRedlineExtraData::Reject( SwPaM& ) const
+{
+}
+
+bool SwRedlineExtraData::operator == ( const SwRedlineExtraData& ) const
+{
+ return false;
+}
+
+SwRedlineExtraData_FormatColl::SwRedlineExtraData_FormatColl( const OUString& rColl,
+ sal_uInt16 nPoolFormatId,
+ const SfxItemSet* pItemSet,
+ bool bFormatAll )
+ : m_sFormatNm(rColl), m_nPoolId(nPoolFormatId), m_bFormatAll(bFormatAll)
+{
+ if( pItemSet && pItemSet->Count() )
+ m_pSet.reset( new SfxItemSet( *pItemSet ) );
+}
+
+SwRedlineExtraData_FormatColl::~SwRedlineExtraData_FormatColl()
+{
+}
+
+SwRedlineExtraData* SwRedlineExtraData_FormatColl::CreateNew() const
+{
+ return new SwRedlineExtraData_FormatColl( m_sFormatNm, m_nPoolId, m_pSet.get(), m_bFormatAll );
+}
+
+void SwRedlineExtraData_FormatColl::Reject( SwPaM& rPam ) const
+{
+ SwDoc& rDoc = rPam.GetDoc();
+
+ // What about Undo? Is it turned off?
+ SwTextFormatColl* pColl = USHRT_MAX == m_nPoolId
+ ? rDoc.FindTextFormatCollByName( m_sFormatNm )
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( m_nPoolId );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() );
+
+ const SwPosition* pEnd = rPam.End();
+
+ if ( !m_bFormatAll || pEnd->nContent == 0 )
+ {
+ // don't reject the format of the next paragraph (that is handled by the next redline)
+ if (aPam.GetPoint()->nNode > aPam.GetMark()->nNode)
+ {
+ aPam.GetPoint()->nNode--;
+ SwContentNode* pNode = aPam.GetPoint()->nNode.GetNode().GetContentNode();
+ if ( pNode )
+ aPam.GetPoint()->nContent.Assign( pNode, pNode->Len() );
+ else
+ // tdf#147507 set it back to a content node to avoid of crashing
+ aPam.GetPoint()->nNode++;
+ }
+ else if (aPam.GetPoint()->nNode < aPam.GetMark()->nNode)
+ {
+ aPam.GetMark()->nNode--;
+ SwContentNode* pNode = aPam.GetMark()->nNode.GetNode().GetContentNode();
+ aPam.GetMark()->nContent.Assign( pNode, pNode->Len() );
+ }
+ }
+
+ if( pColl )
+ rDoc.SetTextFormatColl( aPam, pColl, false );
+
+ if( m_pSet )
+ rDoc.getIDocumentContentOperations().InsertItemSet( aPam, *m_pSet );
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwRedlineExtraData_FormatColl::operator == ( const SwRedlineExtraData& r) const
+{
+ const SwRedlineExtraData_FormatColl& rCmp = static_cast<const SwRedlineExtraData_FormatColl&>(r);
+ return m_sFormatNm == rCmp.m_sFormatNm && m_nPoolId == rCmp.m_nPoolId &&
+ m_bFormatAll == rCmp.m_bFormatAll &&
+ ( ( !m_pSet && !rCmp.m_pSet ) ||
+ ( m_pSet && rCmp.m_pSet && *m_pSet == *rCmp.m_pSet ) );
+}
+
+void SwRedlineExtraData_FormatColl::SetItemSet( const SfxItemSet& rSet )
+{
+ if( rSet.Count() )
+ m_pSet.reset( new SfxItemSet( rSet ) );
+ else
+ m_pSet.reset();
+}
+
+SwRedlineExtraData_Format::SwRedlineExtraData_Format( const SfxItemSet& rSet )
+{
+ SfxItemIter aIter( rSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ m_aWhichIds.push_back( pItem->Which() );
+ }
+}
+
+SwRedlineExtraData_Format::SwRedlineExtraData_Format(
+ const SwRedlineExtraData_Format& rCpy )
+ : SwRedlineExtraData()
+{
+ m_aWhichIds.insert( m_aWhichIds.begin(), rCpy.m_aWhichIds.begin(), rCpy.m_aWhichIds.end() );
+}
+
+SwRedlineExtraData_Format::~SwRedlineExtraData_Format()
+{
+}
+
+SwRedlineExtraData* SwRedlineExtraData_Format::CreateNew() const
+{
+ return new SwRedlineExtraData_Format( *this );
+}
+
+void SwRedlineExtraData_Format::Reject( SwPaM& rPam ) const
+{
+ SwDoc& rDoc = rPam.GetDoc();
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags(RedlineFlags::On | RedlineFlags::Ignore));
+
+ // Actually we need to reset the Attribute here!
+ for( const auto& rWhichId : m_aWhichIds )
+ {
+ rDoc.getIDocumentContentOperations().InsertPoolItem( rPam, *GetDfltAttr( rWhichId ),
+ SetAttrMode::DONTEXPAND );
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwRedlineExtraData_Format::operator == ( const SwRedlineExtraData& rCmp ) const
+{
+ const size_t nEnd = m_aWhichIds.size();
+ if( nEnd != static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds.size() )
+ return false;
+
+ for( size_t n = 0; n < nEnd; ++n )
+ {
+ if( static_cast<const SwRedlineExtraData_Format&>(rCmp).m_aWhichIds[n] != m_aWhichIds[n])
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+SwRedlineData::SwRedlineData( RedlineType eT, std::size_t nAut )
+ : m_pNext( nullptr ), m_pExtraData( nullptr ),
+ m_aStamp( DateTime::SYSTEM ),
+ m_nAuthor( nAut ), m_eType( eT ), m_nSeqNo( 0 ), m_bAutoFormat(false), m_bMoved(false)
+{
+ m_aStamp.SetNanoSec( 0 );
+}
+
+SwRedlineData::SwRedlineData(
+ const SwRedlineData& rCpy,
+ bool bCpyNext )
+ : m_pNext( ( bCpyNext && rCpy.m_pNext ) ? new SwRedlineData( *rCpy.m_pNext ) : nullptr )
+ , m_pExtraData( rCpy.m_pExtraData ? rCpy.m_pExtraData->CreateNew() : nullptr )
+ , m_sComment( rCpy.m_sComment )
+ , m_aStamp( rCpy.m_aStamp )
+ , m_nAuthor( rCpy.m_nAuthor )
+ , m_eType( rCpy.m_eType )
+ , m_nSeqNo( rCpy.m_nSeqNo )
+ , m_bAutoFormat(false)
+ , m_bMoved( rCpy.m_bMoved )
+{
+}
+
+// For sw3io: We now own pNext!
+SwRedlineData::SwRedlineData(RedlineType eT, std::size_t nAut, const DateTime& rDT,
+ const OUString& rCmnt, SwRedlineData *pNxt)
+ : m_pNext(pNxt), m_pExtraData(nullptr), m_sComment(rCmnt), m_aStamp(rDT),
+ m_nAuthor(nAut), m_eType(eT), m_nSeqNo(0), m_bAutoFormat(false), m_bMoved(false)
+{
+}
+
+SwRedlineData::~SwRedlineData()
+{
+ delete m_pExtraData;
+ delete m_pNext;
+}
+
+// Check whether the absolute difference between the two dates is no larger than one minute (can
+// give inaccurate results if at least one of the dates is not valid/normalized):
+static bool deltaOneMinute(DateTime const & t1, DateTime const & t2) {
+ auto const & [min, max] = std::minmax(t1, t2);
+ // Avoid overflow of `min + tools::Time(0, 1)` below when min is close to the maximum valid
+ // DateTime:
+ if (min >= DateTime({31, 12, std::numeric_limits<sal_Int16>::max()}, {23, 59})) {
+ return true;
+ }
+ return max <= min + tools::Time(0, 1);
+}
+
+bool SwRedlineData::CanCombine(const SwRedlineData& rCmp) const
+{
+ return m_nAuthor == rCmp.m_nAuthor &&
+ m_eType == rCmp.m_eType &&
+ m_sComment == rCmp.m_sComment &&
+ deltaOneMinute(GetTimeStamp(), rCmp.GetTimeStamp()) &&
+ m_bMoved == rCmp.m_bMoved &&
+ (( !m_pNext && !rCmp.m_pNext ) ||
+ ( m_pNext && rCmp.m_pNext &&
+ m_pNext->CanCombine( *rCmp.m_pNext ))) &&
+ (( !m_pExtraData && !rCmp.m_pExtraData ) ||
+ ( m_pExtraData && rCmp.m_pExtraData &&
+ *m_pExtraData == *rCmp.m_pExtraData ));
+}
+
+/// ExtraData is copied. The Pointer's ownership is thus NOT transferred
+/// to the Redline Object!
+void SwRedlineData::SetExtraData( const SwRedlineExtraData* pData )
+{
+ delete m_pExtraData;
+
+ // Check if there is data - and if so - delete it
+ if( pData )
+ m_pExtraData = pData->CreateNew();
+ else
+ m_pExtraData = nullptr;
+}
+
+const TranslateId STR_REDLINE_ARY[] =
+{
+ STR_UNDO_REDLINE_INSERT,
+ STR_UNDO_REDLINE_DELETE,
+ STR_UNDO_REDLINE_FORMAT,
+ STR_UNDO_REDLINE_TABLE,
+ STR_UNDO_REDLINE_FMTCOLL,
+ STR_UNDO_REDLINE_PARAGRAPH_FORMAT,
+ STR_UNDO_REDLINE_TABLE_ROW_INSERT,
+ STR_UNDO_REDLINE_TABLE_ROW_DELETE,
+ STR_UNDO_REDLINE_TABLE_CELL_INSERT,
+ STR_UNDO_REDLINE_TABLE_CELL_DELETE
+};
+
+OUString SwRedlineData::GetDescr() const
+{
+ return SwResId(STR_REDLINE_ARY[static_cast<int>(GetType())]);
+}
+
+sal_uInt32 SwRangeRedline::s_nLastId = 1;
+
+SwRangeRedline::SwRangeRedline(RedlineType eTyp, const SwPaM& rPam )
+ : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( eTyp, GetDoc().getIDocumentRedlineAccess().GetRedlineAuthor() ) ),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rPam.HasMark() )
+ DeleteMark();
+
+ // set default comment for single annotations added or deleted
+ if ( IsAnnotation() )
+ {
+ SetComment( RedlineType::Delete == eTyp
+ ? SwResId(STR_REDLINE_COMMENT_DELETED)
+ : SwResId(STR_REDLINE_COMMENT_ADDED) );
+ }
+}
+
+SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPaM& rPam )
+ : SwPaM( *rPam.GetMark(), *rPam.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( rData )),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rPam.HasMark() )
+ DeleteMark();
+}
+
+SwRangeRedline::SwRangeRedline( const SwRedlineData& rData, const SwPosition& rPos )
+ : SwPaM( rPos ),
+ m_pRedlineData( new SwRedlineData( rData )),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+}
+
+SwRangeRedline::SwRangeRedline( const SwRangeRedline& rCpy )
+ : SwPaM( *rCpy.GetMark(), *rCpy.GetPoint() ),
+ m_pRedlineData( new SwRedlineData( *rCpy.m_pRedlineData )),
+ m_pContentSect( nullptr ),
+ m_nId( s_nLastId++ )
+{
+ m_bDelLastPara = false;
+ m_bIsVisible = true;
+ if( !rCpy.HasMark() )
+ DeleteMark();
+}
+
+SwRangeRedline::~SwRangeRedline()
+{
+ if( m_pContentSect )
+ {
+ // delete the ContentSection
+ if( !GetDoc().IsInDtor() )
+ GetDoc().getIDocumentContentOperations().DeleteSection( &m_pContentSect->GetNode() );
+ delete m_pContentSect;
+ }
+ delete m_pRedlineData;
+}
+
+void MaybeNotifyRedlineModification(SwRangeRedline& rRedline, SwDoc& rDoc)
+{
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type i = 0; i < rRedTable.size(); ++i)
+ {
+ if (rRedTable[i] == &rRedline)
+ {
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, &rRedline);
+ break;
+ }
+ }
+}
+
+void SwRangeRedline::MaybeNotifyRedlinePositionModification(tools::Long nTop)
+{
+ if (!lcl_LOKRedlineNotificationEnabled())
+ return;
+
+ if(!m_oLOKLastNodeTop || *m_oLOKLastNodeTop != nTop)
+ {
+ m_oLOKLastNodeTop = nTop;
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, this);
+ }
+}
+
+void SwRangeRedline::SetStart( const SwPosition& rPos, SwPosition* pSttPtr )
+{
+ if( !pSttPtr ) pSttPtr = Start();
+ *pSttPtr = rPos;
+
+ MaybeNotifyRedlineModification(*this, GetDoc());
+}
+
+void SwRangeRedline::SetEnd( const SwPosition& rPos, SwPosition* pEndPtr )
+{
+ if( !pEndPtr ) pEndPtr = End();
+ *pEndPtr = rPos;
+
+ MaybeNotifyRedlineModification(*this, GetDoc());
+}
+
+/// Do we have a valid Selection?
+bool SwRangeRedline::HasValidRange() const
+{
+ const SwNode* pPtNd = &GetPoint()->nNode.GetNode(),
+ * pMkNd = &GetMark()->nNode.GetNode();
+ if( pPtNd->StartOfSectionNode() == pMkNd->StartOfSectionNode() &&
+ !pPtNd->StartOfSectionNode()->IsTableNode() &&
+ // invalid if points on the end of content
+ // end-of-content only invalid if no content index exists
+ ( pPtNd != pMkNd || GetContentIdx() != nullptr ||
+ pPtNd != &pPtNd->GetNodes().GetEndOfContent() )
+ )
+ return true;
+ return false;
+}
+
+void SwRangeRedline::CallDisplayFunc(size_t nMyPos)
+{
+ RedlineFlags eShow = RedlineFlags::ShowMask & GetDoc().getIDocumentRedlineAccess().GetRedlineFlags();
+ if (eShow == (RedlineFlags::ShowInsert | RedlineFlags::ShowDelete))
+ Show(0, nMyPos);
+ else if (eShow == RedlineFlags::ShowInsert)
+ Hide(0, nMyPos);
+ else if (eShow == RedlineFlags::ShowDelete)
+ ShowOriginal(0, nMyPos);
+}
+
+void SwRangeRedline::Show(sal_uInt16 nLoop, size_t nMyPos, bool bForced)
+{
+ SwDoc& rDoc = GetDoc();
+
+ bool bIsShowChangesInMargin = false;
+ if ( !bForced )
+ {
+ SwViewShell* pSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if (pSh)
+ bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
+ else
+ bIsShowChangesInMargin = SW_MOD()->GetUsrPref(false)->IsShowChangesInMargin();
+ }
+
+ if( 1 > nLoop && !bIsShowChangesInMargin )
+ return;
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ switch( GetType() )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = true;
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = !bIsShowChangesInMargin;
+
+ if (m_bIsVisible)
+ MoveFromSection(nMyPos);
+ else
+ {
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ }
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ InvalidateRange(Invalidation::Add);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwRangeRedline::Hide(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
+{
+ SwDoc& rDoc = GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ switch( GetType() )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = true;
+ if( 1 <= nLoop )
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = false;
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ if( 1 <= nLoop )
+ InvalidateRange(Invalidation::Remove);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwRangeRedline::ShowOriginal(sal_uInt16 nLoop, size_t nMyPos, bool /*bForced*/)
+{
+ SwDoc& rDoc = GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ SwRedlineData* pCur;
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // Determine the Type, it's the first on Stack
+ for( pCur = m_pRedlineData; pCur->m_pNext; )
+ pCur = pCur->m_pNext;
+
+ switch( pCur->m_eType )
+ {
+ case RedlineType::Insert: // Content has been inserted
+ m_bIsVisible = false;
+ switch( nLoop )
+ {
+ case 0: MoveToSection(); break;
+ case 1: CopyToSection(); break;
+ case 2: DelCopyOfSection(nMyPos); break;
+ }
+ break;
+
+ case RedlineType::Delete: // Content has been deleted
+ m_bIsVisible = true;
+ if( 1 <= nLoop )
+ MoveFromSection(nMyPos);
+ break;
+
+ case RedlineType::Format: // Attributes have been applied
+ case RedlineType::Table: // Table structure has been modified
+ if( 1 <= nLoop )
+ InvalidateRange(Invalidation::Remove);
+ break;
+ default:
+ break;
+ }
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+// trigger the Layout
+void SwRangeRedline::InvalidateRange(Invalidation const eWhy)
+{
+ SwNodeOffset nSttNd = Start()->nNode.GetIndex(),
+ nEndNd = End()->nNode.GetIndex();
+ sal_Int32 nSttCnt = Start()->nContent.GetIndex();
+ sal_Int32 nEndCnt = End()->nContent.GetIndex();
+
+ SwNodes& rNds = GetDoc().GetNodes();
+ for (SwNodeOffset n(nSttNd); n <= nEndNd; ++n)
+ {
+ SwNode* pNode = rNds[n];
+
+ if (pNode && pNode->IsTextNode())
+ {
+ SwTextNode* pNd = pNode->GetTextNode();
+
+ SwUpdateAttr aHt(
+ n == nSttNd ? nSttCnt : 0,
+ n == nEndNd ? nEndCnt : pNd->GetText().getLength(),
+ RES_FMT_CHG);
+
+ pNd->TriggerNodeUpdate(sw::LegacyModifyHint(&aHt, &aHt));
+
+ // SwUpdateAttr must be handled first, otherwise indexes are off
+ if (GetType() == RedlineType::Delete)
+ {
+ sal_Int32 const nStart(n == nSttNd ? nSttCnt : 0);
+ sal_Int32 const nLen((n == nEndNd ? nEndCnt : pNd->GetText().getLength()) - nStart);
+ if (eWhy == Invalidation::Add)
+ {
+ sw::RedlineDelText const hint(nStart, nLen);
+ pNd->CallSwClientNotify(hint);
+ }
+ else
+ {
+ sw::RedlineUnDelText const hint(nStart, nLen);
+ pNd->CallSwClientNotify(hint);
+ }
+ }
+ }
+ }
+}
+
+/** Calculates the start and end position of the intersection rTmp and
+ text node nNdIdx */
+void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, sal_Int32& rStart, sal_Int32& rEnd ) const
+{
+ const SwPosition *pRStt = Start(), *pREnd = End();
+ if( pRStt->nNode < nNdIdx )
+ {
+ if( pREnd->nNode > nNdIdx )
+ {
+ rStart = 0; // Paragraph is completely enclosed
+ rEnd = COMPLETE_STRING;
+ }
+ else if (pREnd->nNode == nNdIdx)
+ {
+ rStart = 0; // Paragraph is overlapped in the beginning
+ rEnd = pREnd->nContent.GetIndex();
+ }
+ else // redline ends before paragraph
+ {
+ rStart = COMPLETE_STRING;
+ rEnd = COMPLETE_STRING;
+ }
+ }
+ else if( pRStt->nNode == nNdIdx )
+ {
+ rStart = pRStt->nContent.GetIndex();
+ if( pREnd->nNode == nNdIdx )
+ rEnd = pREnd->nContent.GetIndex(); // Within the Paragraph
+ else
+ rEnd = COMPLETE_STRING; // Paragraph is overlapped in the end
+ }
+ else
+ {
+ rStart = COMPLETE_STRING;
+ rEnd = COMPLETE_STRING;
+ }
+}
+
+static void lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStt, const SwPosition* pEnd)
+{
+ // tdf#115815 keep original start position of collapsed annotation ranges
+ // as temporary bookmarks (removed after file saving and file loading)
+ IDocumentMarkAccess& rDMA(*rDoc.getIDocumentMarkAccess());
+ for (auto iter = rDMA.getAnnotationMarksBegin();
+ iter != rDMA.getAnnotationMarksEnd(); )
+ {
+ SwPosition const& rStartPos((**iter).GetMarkStart());
+ if ( *pStt <= rStartPos && rStartPos < *pEnd )
+ {
+ IDocumentMarkAccess::const_iterator_t pOldMark =
+ rDMA.findAnnotationBookmark((**iter).GetName());
+ if ( pOldMark == rDMA.getBookmarksEnd() )
+ {
+ // at start of redlines use a 1-character length bookmark range
+ // instead of a 0-character length bookmark position to avoid its losing
+ sal_Int32 nLen = (*pStt == rStartPos) ? 1 : 0;
+ SwPaM aPam( rStartPos.nNode, rStartPos.nContent.GetIndex(),
+ rStartPos.nNode, rStartPos.nContent.GetIndex() + nLen);
+ ::sw::mark::IMark* pMark = rDMA.makeAnnotationBookmark(
+ aPam,
+ (**iter).GetName(),
+ IDocumentMarkAccess::MarkType::BOOKMARK, sw::mark::InsertMode::New);
+ ::sw::mark::IBookmark* pBookmark = dynamic_cast< ::sw::mark::IBookmark* >(pMark);
+ if (pBookmark)
+ {
+ pBookmark->SetKeyCode(vcl::KeyCode());
+ pBookmark->SetShortName(OUString());
+ }
+ }
+ }
+ ++iter;
+ }
+}
+
+void SwRangeRedline::MoveToSection()
+{
+ if( !m_pContentSect )
+ {
+ const SwPosition* pStt = Start(),
+ * pEnd = End();
+
+ SwDoc& rDoc = GetDoc();
+ SwPaM aPam( *pStt, *pEnd );
+ SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+
+ if( !pCSttNd )
+ {
+ // In order to not move other Redlines' indices, we set them
+ // to the end (is exclusive)
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->GetBound() == *pStt )
+ pRedl->GetBound() = *pEnd;
+ if( pRedl->GetBound(false) == *pStt )
+ pRedl->GetBound(false) = *pEnd;
+ }
+ }
+
+ SwStartNode* pSttNd;
+ SwNodes& rNds = rDoc.GetNodes();
+ if( pCSttNd || pCEndNd )
+ {
+ SwTextFormatColl* pColl = (pCSttNd && pCSttNd->IsTextNode() )
+ ? pCSttNd->GetTextNode()->GetTextColl()
+ : (pCEndNd && pCEndNd->IsTextNode() )
+ ? pCEndNd->GetTextNode()->GetTextColl()
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ pSttNd = rNds.MakeTextSection( SwNodeIndex( rNds.GetEndOfRedlines() ),
+ SwNormalStartNode, pColl );
+ SwTextNode* pTextNd = rNds[ pSttNd->GetIndex() + 1 ]->GetTextNode();
+
+ SwNodeIndex aNdIdx( *pTextNd );
+ SwPosition aPos( aNdIdx, SwIndex( pTextNd ));
+ if( pCSttNd && pCEndNd )
+ {
+ // tdf#140982 keep annotation ranges in deletions in margin mode
+ lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
+ rDoc.getIDocumentContentOperations().MoveAndJoin( aPam, aPos );
+ }
+ else
+ {
+ if( pCSttNd && !pCEndNd )
+ m_bDelLastPara = true;
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::DEFAULT );
+ }
+ }
+ else
+ {
+ pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) );
+
+ SwPosition aPos( *pSttNd->EndOfSectionNode() );
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::DEFAULT );
+ }
+ m_pContentSect = new SwNodeIndex( *pSttNd );
+
+ if( pStt == GetPoint() )
+ Exchange();
+
+ DeleteMark();
+ }
+ else
+ InvalidateRange(Invalidation::Remove);
+}
+
+void SwRangeRedline::CopyToSection()
+{
+ if( m_pContentSect )
+ return;
+
+ const SwPosition* pStt = Start(),
+ * pEnd = End();
+
+ SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+
+ SwStartNode* pSttNd;
+ SwDoc& rDoc = GetDoc();
+ SwNodes& rNds = rDoc.GetNodes();
+
+ bool bSaveCopyFlag = rDoc.IsCopyIsMove(),
+ bSaveRdlMoveFlg = rDoc.getIDocumentRedlineAccess().IsRedlineMove();
+ rDoc.SetCopyIsMove( true );
+
+ // The IsRedlineMove() flag causes the behaviour of the
+ // DocumentContentOperationsManager::CopyFlyInFlyImpl() method to change,
+ // which will eventually be called by the CopyRange() below.
+ rDoc.getIDocumentRedlineAccess().SetRedlineMove(true);
+
+ if( pCSttNd )
+ {
+ SwTextFormatColl* pColl = pCSttNd->IsTextNode()
+ ? pCSttNd->GetTextNode()->GetTextColl()
+ : rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD);
+
+ pSttNd = rNds.MakeTextSection( SwNodeIndex( rNds.GetEndOfRedlines() ),
+ SwNormalStartNode, pColl );
+
+ SwNodeIndex aNdIdx( *pSttNd, 1 );
+ SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
+ SwPosition aPos( aNdIdx, SwIndex( pTextNd ));
+
+ // tdf#115815 keep original start position of collapsed annotation ranges
+ // as temporary bookmarks (removed after file saving and file loading)
+ lcl_storeAnnotationMarks( rDoc, pStt, pEnd );
+ rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
+
+ // Take over the style from the EndNode if needed
+ // We don't want this in Doc::Copy
+ if( pCEndNd && pCEndNd != pCSttNd )
+ {
+ SwContentNode* pDestNd = aPos.nNode.GetNode().GetContentNode();
+ if( pDestNd )
+ {
+ if( pDestNd->IsTextNode() && pCEndNd->IsTextNode() )
+ pCEndNd->GetTextNode()->CopyCollFormat(*pDestNd->GetTextNode());
+ else
+ pDestNd->ChgFormatColl( pCEndNd->GetFormatColl() );
+ }
+ }
+ }
+ else
+ {
+ pSttNd = SwNodes::MakeEmptySection( SwNodeIndex( rNds.GetEndOfRedlines() ) );
+
+ if( pCEndNd )
+ {
+ SwPosition aPos( *pSttNd->EndOfSectionNode() );
+ rDoc.getIDocumentContentOperations().CopyRange(*this, aPos, SwCopyFlags::CheckPosInFly);
+ }
+ else
+ {
+ SwNodeIndex aInsPos( *pSttNd->EndOfSectionNode() );
+ SwNodeRange aRg( pStt->nNode, SwNodeOffset(0), pEnd->nNode, SwNodeOffset(1) );
+ rDoc.GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aInsPos);
+ }
+ }
+ m_pContentSect = new SwNodeIndex( *pSttNd );
+
+ rDoc.SetCopyIsMove( bSaveCopyFlag );
+ rDoc.getIDocumentRedlineAccess().SetRedlineMove( bSaveRdlMoveFlg );
+}
+
+void SwRangeRedline::DelCopyOfSection(size_t nMyPos)
+{
+ if( !m_pContentSect )
+ return;
+
+ const SwPosition* pStt = Start(),
+ * pEnd = End();
+
+ SwDoc& rDoc = GetDoc();
+ SwPaM aPam( *pStt, *pEnd );
+ SwContentNode* pCSttNd = pStt->nNode.GetNode().GetContentNode();
+ SwContentNode* pCEndNd = pEnd->nNode.GetNode().GetContentNode();
+
+ if( !pCSttNd )
+ {
+ // In order to not move other Redlines' indices, we set them
+ // to the end (is exclusive)
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ if( pRedl->GetBound() == *pStt )
+ pRedl->GetBound() = *pEnd;
+ if( pRedl->GetBound(false) == *pStt )
+ pRedl->GetBound(false) = *pEnd;
+ }
+ }
+
+ if( pCSttNd && pCEndNd )
+ {
+ // #i100466# - force a <join next> on <delete and join> operation
+ // tdf#125319 - rather not?
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(aPam/*, true*/);
+ }
+ else if( pCSttNd || pCEndNd )
+ {
+ if( pCSttNd && !pCEndNd )
+ m_bDelLastPara = true;
+ rDoc.getIDocumentContentOperations().DeleteRange( aPam );
+
+ if( m_bDelLastPara )
+ {
+ // To prevent dangling references to the paragraph to
+ // be deleted, redline that point into this paragraph should be
+ // moved to the new end position. Since redlines in the redline
+ // table are sorted and the pEnd position is an endnode (see
+ // bDelLastPara condition above), only redlines before the
+ // current ones can be affected.
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ size_t n = nMyPos;
+ for( bool bBreak = false; !bBreak && n > 0; )
+ {
+ --n;
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *aPam.GetPoint() )
+ {
+ rTable[ n ]->GetBound() = *pEnd;
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *aPam.GetPoint() )
+ {
+ rTable[ n ]->GetBound(false) = *pEnd;
+ bBreak = false;
+ }
+ }
+
+ *GetPoint() = *pEnd;
+ *GetMark() = *pEnd;
+ DeleteMark();
+
+ aPam.GetBound().nContent.Assign( nullptr, 0 );
+ aPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ aPam.DeleteMark();
+ rDoc.getIDocumentContentOperations().DelFullPara( aPam );
+ }
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().DeleteRange( aPam );
+ }
+
+ if( pStt == GetPoint() )
+ Exchange();
+
+ DeleteMark();
+}
+
+void SwRangeRedline::MoveFromSection(size_t nMyPos)
+{
+ if( m_pContentSect )
+ {
+ SwDoc& rDoc = GetDoc();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ std::vector<SwPosition*> aBeforeArr, aBehindArr;
+ bool bBreak = false;
+ SwRedlineTable::size_type n;
+
+ for( n = nMyPos+1; !bBreak && n < rTable.size(); ++n )
+ {
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBehindArr.push_back(&pRedl->GetBound());
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBehindArr.push_back(&pRedl->GetBound(false));
+ bBreak = false;
+ }
+ }
+ for( bBreak = false, n = nMyPos; !bBreak && n ; )
+ {
+ --n;
+ bBreak = true;
+ if( rTable[ n ]->GetBound() == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBeforeArr.push_back(&pRedl->GetBound());
+ bBreak = false;
+ }
+ if( rTable[ n ]->GetBound(false) == *GetPoint() )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ aBeforeArr.push_back(&pRedl->GetBound(false));
+ bBreak = false;
+ }
+ }
+
+ const SwNode* pKeptContentSectNode( &m_pContentSect->GetNode() ); // #i95711#
+ {
+ SwPaM aPam( m_pContentSect->GetNode(),
+ *m_pContentSect->GetNode().EndOfSectionNode(), SwNodeOffset(1),
+ SwNodeOffset( m_bDelLastPara ? -2 : -1 ) );
+ SwContentNode* pCNd = aPam.GetContentNode();
+ if( pCNd )
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ else
+ ++aPam.GetPoint()->nNode;
+
+ SwFormatColl* pColl = pCNd && pCNd->Len() && aPam.GetPoint()->nNode !=
+ aPam.GetMark()->nNode
+ ? pCNd->GetFormatColl() : nullptr;
+
+ SwNodeIndex aNdIdx( GetPoint()->nNode, -1 );
+ const sal_Int32 nPos = GetPoint()->nContent.GetIndex();
+
+ SwPosition aPos( *GetPoint() );
+ if( m_bDelLastPara && *aPam.GetPoint() == *aPam.GetMark() )
+ {
+ --aPos.nNode;
+
+ rDoc.getIDocumentContentOperations().AppendTextNode( aPos );
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aPos,
+ SwMoveFlags::ALLFLYS );
+ }
+
+ SetMark();
+ *GetPoint() = aPos;
+ GetMark()->nNode = aNdIdx.GetIndex() + 1;
+ pCNd = GetMark()->nNode.GetNode().GetContentNode();
+ GetMark()->nContent.Assign( pCNd, nPos );
+
+ if( m_bDelLastPara )
+ {
+ ++GetPoint()->nNode;
+ pCNd = GetContentNode();
+ GetPoint()->nContent.Assign( pCNd, 0 );
+ m_bDelLastPara = false;
+ }
+ else if( pColl )
+ pCNd = GetContentNode();
+
+ if( pColl && pCNd )
+ pCNd->ChgFormatColl( pColl );
+ }
+
+ // #i95771#
+ // Under certain conditions the previous <SwDoc::Move(..)> has already
+ // removed the change tracking section of this <SwRangeRedline> instance from
+ // the change tracking nodes area.
+ // Thus, check if <pContentSect> still points to the change tracking section
+ // by comparing it with the "indexed" <SwNode> instance copied before
+ // perform the intrinsic move.
+ // Note: Such condition is e.g. a "delete" change tracking only containing a table.
+ if ( &m_pContentSect->GetNode() == pKeptContentSectNode )
+ {
+ rDoc.getIDocumentContentOperations().DeleteSection( &m_pContentSect->GetNode() );
+ }
+ delete m_pContentSect;
+ m_pContentSect = nullptr;
+
+ // adjustment of redline table positions must take start and
+ // end into account, not point and mark.
+ for( auto& pItem : aBeforeArr )
+ *pItem = *Start();
+ for( auto& pItem : aBehindArr )
+ *pItem = *End();
+ }
+ else
+ InvalidateRange(Invalidation::Add);
+}
+
+// for Undo
+void SwRangeRedline::SetContentIdx( const SwNodeIndex* pIdx )
+{
+ if( pIdx && !m_pContentSect )
+ {
+ m_pContentSect = new SwNodeIndex( *pIdx );
+ m_bIsVisible = false;
+ }
+ else if( !pIdx && m_pContentSect )
+ {
+ delete m_pContentSect;
+ m_pContentSect = nullptr;
+ m_bIsVisible = false;
+ }
+ else
+ {
+ OSL_FAIL("SwRangeRedline::SetContentIdx: invalid state");
+ }
+}
+
+bool SwRangeRedline::CanCombine( const SwRangeRedline& rRedl ) const
+{
+ return IsVisible() && rRedl.IsVisible() &&
+ m_pRedlineData->CanCombine( *rRedl.m_pRedlineData );
+}
+
+void SwRangeRedline::PushData( const SwRangeRedline& rRedl, bool bOwnAsNext )
+{
+ SwRedlineData* pNew = new SwRedlineData( *rRedl.m_pRedlineData, false );
+ if( bOwnAsNext )
+ {
+ pNew->m_pNext = m_pRedlineData;
+ m_pRedlineData = pNew;
+ }
+ else
+ {
+ pNew->m_pNext = m_pRedlineData->m_pNext;
+ m_pRedlineData->m_pNext = pNew;
+ }
+}
+
+bool SwRangeRedline::PopData()
+{
+ if( !m_pRedlineData->m_pNext )
+ return false;
+ SwRedlineData* pCur = m_pRedlineData;
+ m_pRedlineData = pCur->m_pNext;
+ pCur->m_pNext = nullptr;
+ delete pCur;
+ return true;
+}
+
+sal_uInt16 SwRangeRedline::GetStackCount() const
+{
+ sal_uInt16 nRet = 1;
+ for( SwRedlineData* pCur = m_pRedlineData; pCur->m_pNext; pCur = pCur->m_pNext )
+ ++nRet;
+ return nRet;
+}
+
+std::size_t SwRangeRedline::GetAuthor( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_nAuthor;
+}
+
+OUString const & SwRangeRedline::GetAuthorString( sal_uInt16 nPos ) const
+{
+ return SW_MOD()->GetRedlineAuthor(GetRedlineData(nPos).m_nAuthor);
+}
+
+const DateTime& SwRangeRedline::GetTimeStamp( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_aStamp;
+}
+
+RedlineType SwRangeRedline::GetType( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_eType;
+}
+
+bool SwRangeRedline::IsAnnotation() const
+{
+ return GetText().getLength() == 1 && GetText()[0] == CH_TXTATR_INWORD;
+}
+
+const OUString& SwRangeRedline::GetComment( sal_uInt16 nPos ) const
+{
+ return GetRedlineData(nPos).m_sComment;
+}
+
+bool SwRangeRedline::operator<( const SwRangeRedline& rCmp ) const
+{
+ if (*Start() < *rCmp.Start())
+ return true;
+
+ return *Start() == *rCmp.Start() && *End() < *rCmp.End();
+}
+
+const SwRedlineData & SwRangeRedline::GetRedlineData(const sal_uInt16 nPos) const
+{
+ SwRedlineData * pCur = m_pRedlineData;
+
+ sal_uInt16 nP = nPos;
+
+ while (nP > 0 && nullptr != pCur->m_pNext)
+ {
+ pCur = pCur->m_pNext;
+
+ nP--;
+ }
+
+ SAL_WARN_IF( nP != 0, "sw.core", "Pos " << nPos << " is " << nP << " too big");
+
+ return *pCur;
+}
+
+OUString SwRangeRedline::GetDescr(bool bSimplified)
+{
+ // get description of redline data (e.g.: "insert $1")
+ OUString aResult = GetRedlineData().GetDescr();
+
+ SwPaM * pPaM = nullptr;
+ bool bDeletePaM = false;
+
+ // if this redline is visible the content is in this PaM
+ if (nullptr == m_pContentSect)
+ {
+ pPaM = this;
+ }
+ else // otherwise it is saved in pContentSect
+ {
+ SwNodeIndex aTmpIdx( *m_pContentSect->GetNode().EndOfSectionNode() );
+ pPaM = new SwPaM(*m_pContentSect, aTmpIdx );
+ bDeletePaM = true;
+ }
+
+ OUString sDescr = DenoteSpecialCharacters(pPaM->GetText().replace('\n', ' '), /*bQuoted=*/!bSimplified);
+ if (const SwTextNode *pTextNode = pPaM->GetNode().GetTextNode())
+ {
+ if (const SwTextAttr* pTextAttr = pTextNode->GetFieldTextAttrAt(pPaM->GetPoint()->nContent.GetIndex() - 1, true ))
+ {
+ sDescr = ( bSimplified ? "" : SwResId(STR_START_QUOTE) )
+ + pTextAttr->GetFormatField().GetField()->GetFieldName()
+ + ( bSimplified ? "" : SwResId(STR_END_QUOTE) );
+ }
+ }
+
+ // replace $1 in description by description of the redlines text
+ const OUString aTmpStr = ShortenString(sDescr, nUndoStringLength, SwResId(STR_LDOTS));
+
+ if (!bSimplified)
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ aResult = aRewriter.Apply(aResult);
+ }
+ else
+ {
+ aResult = aTmpStr;
+ // more shortening
+ sal_Int32 nPos = aTmpStr.indexOf(SwResId(STR_LDOTS));
+ if (nPos > 5)
+ aResult = aTmpStr.copy(0, nPos + SwResId(STR_LDOTS).getLength());
+ }
+
+ if (bDeletePaM)
+ delete pPaM;
+
+ return aResult;
+}
+
+void SwRangeRedline::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwRangeRedline"));
+
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("id"), BAD_CAST(OString::number(GetSeqNo()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("author"), BAD_CAST(SW_MOD()->GetRedlineAuthor(GetAuthor()).toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"), BAD_CAST(DateTimeToOString(GetTimeStamp()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("descr"), BAD_CAST(const_cast<SwRangeRedline*>(this)->GetDescr().toUtf8().getStr()));
+
+ OString sRedlineType;
+ switch (GetType())
+ {
+ case RedlineType::Insert:
+ sRedlineType = "REDLINE_INSERT";
+ break;
+ case RedlineType::Delete:
+ sRedlineType = "REDLINE_DELETE";
+ break;
+ case RedlineType::Format:
+ sRedlineType = "REDLINE_FORMAT";
+ break;
+ default:
+ sRedlineType = "UNKNOWN";
+ break;
+ }
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(sRedlineType.getStr()));
+
+ SwPaM::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwExtraRedlineTable::Insert( SwExtraRedline* p )
+{
+ m_aExtraRedlines.push_back( p );
+ //p->CallDisplayFunc();
+}
+
+void SwExtraRedlineTable::DeleteAndDestroy(sal_uInt16 const nPos)
+{
+ /*
+ SwDoc* pDoc = 0;
+ if( !nP && nL && nL == size() )
+ pDoc = front()->GetDoc();
+ */
+
+ delete m_aExtraRedlines[nPos];
+ m_aExtraRedlines.erase(m_aExtraRedlines.begin() + nPos);
+
+ /*
+ SwViewShell* pSh;
+ if( pDoc && !pDoc->IsInDtor() &&
+ 0 != ( pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) )
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ */
+}
+
+void SwExtraRedlineTable::DeleteAndDestroyAll()
+{
+ while (!m_aExtraRedlines.empty())
+ {
+ auto const pRedline = m_aExtraRedlines.back();
+ m_aExtraRedlines.pop_back();
+ delete pRedline;
+ }
+}
+
+SwExtraRedline::~SwExtraRedline()
+{
+}
+
+SwTableRowRedline::SwTableRowRedline(const SwRedlineData& rData, const SwTableLine& rTableLine)
+ : m_aRedlineData(rData)
+ , m_rTableLine(rTableLine)
+{
+}
+
+SwTableRowRedline::~SwTableRowRedline()
+{
+}
+
+SwTableCellRedline::SwTableCellRedline(const SwRedlineData& rData, const SwTableBox& rTableBox)
+ : m_aRedlineData(rData)
+ , m_rTableBox(rTableBox)
+{
+}
+
+SwTableCellRedline::~SwTableCellRedline()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docruby.cxx b/sw/source/core/doc/docruby.cxx
new file mode 100644
index 000000000..1a9f39c3a
--- /dev/null
+++ b/sw/source/core/doc/docruby.cxx
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <string.h>
+
+#include <com/sun/star/i18n/UnicodeType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+
+#include <unotools/charclass.hxx>
+
+#include <hintids.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <ndtxt.hxx>
+#include <txatbase.hxx>
+#include <rubylist.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <breakit.hxx>
+#include <swcrsr.hxx>
+
+using namespace ::com::sun::star::i18n;
+
+/*
+ * Members in the list:
+ * - String - the orig text
+ * - SwFormatRuby - the ruby attribute
+ */
+sal_uInt16 SwDoc::FillRubyList( const SwPaM& rPam, SwRubyList& rList )
+{
+ const SwPaM *_pStartCursor = rPam.GetNext(),
+ *_pStartCursor2 = _pStartCursor;
+ bool bCheckEmpty = &rPam != _pStartCursor;
+ do {
+ const SwPosition* pStt = _pStartCursor->Start(),
+ * pEnd = _pStartCursor->End();
+ if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
+ {
+ SwPaM aPam( *pStt );
+ do {
+ std::unique_ptr<SwRubyListEntry> pNew(new SwRubyListEntry);
+ if( pEnd != pStt )
+ {
+ aPam.SetMark();
+ *aPam.GetMark() = *pEnd;
+ }
+ if( SelectNextRubyChars( aPam, *pNew ))
+ {
+ rList.push_back(std::move(pNew));
+ aPam.DeleteMark();
+ }
+ else
+ {
+ if( *aPam.GetPoint() < *pEnd )
+ {
+ // goto next paragraph
+ aPam.DeleteMark();
+ aPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ break;
+ }
+ } while( 30 > rList.size() && *aPam.GetPoint() < *pEnd );
+ }
+ if( 30 <= rList.size() )
+ break;
+ _pStartCursor = _pStartCursor->GetNext();
+ } while( _pStartCursor != _pStartCursor2 );
+
+ return rList.size();
+}
+
+void SwDoc::SetRubyList( const SwPaM& rPam, const SwRubyList& rList )
+{
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::SETRUBYATTR, nullptr );
+ const o3tl::sorted_vector<sal_uInt16> aDelArr{ RES_TXTATR_CJK_RUBY };
+
+ SwRubyList::size_type nListEntry = 0;
+
+ const SwPaM *_pStartCursor = rPam.GetNext(),
+ *_pStartCursor2 = _pStartCursor;
+ bool bCheckEmpty = &rPam != _pStartCursor;
+ do {
+ const SwPosition* pStt = _pStartCursor->Start(),
+ * pEnd = _pStartCursor->End();
+ if( !bCheckEmpty || ( pStt != pEnd && *pStt != *pEnd ))
+ {
+
+ SwPaM aPam( *pStt );
+ do {
+ SwRubyListEntry aCheckEntry;
+ if( pEnd != pStt )
+ {
+ aPam.SetMark();
+ *aPam.GetMark() = *pEnd;
+ }
+ if( SelectNextRubyChars( aPam, aCheckEntry ))
+ {
+ const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
+ if( aCheckEntry.GetRubyAttr() != pEntry->GetRubyAttr() )
+ {
+ // set/reset the attribute
+ if( !pEntry->GetRubyAttr().GetText().isEmpty() )
+ {
+ getIDocumentContentOperations().InsertPoolItem( aPam, pEntry->GetRubyAttr() );
+ }
+ else
+ {
+ ResetAttrs( aPam, true, aDelArr );
+ }
+ }
+
+ if( !pEntry->GetText().isEmpty() &&
+ aCheckEntry.GetText() != pEntry->GetText() )
+ {
+ // text is changed, so replace the original
+ getIDocumentContentOperations().ReplaceRange( aPam, pEntry->GetText(), false );
+ }
+ aPam.DeleteMark();
+ }
+ else
+ {
+ if( *aPam.GetPoint() < *pEnd )
+ {
+ // goto next paragraph
+ aPam.DeleteMark();
+ aPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ {
+ const SwRubyListEntry* pEntry = rList[ nListEntry++ ].get();
+
+ // set/reset the attribute
+ if( !pEntry->GetRubyAttr().GetText().isEmpty() &&
+ !pEntry->GetText().isEmpty() )
+ {
+ getIDocumentContentOperations().InsertString( aPam, pEntry->GetText() );
+ aPam.SetMark();
+ aPam.GetMark()->nContent -= pEntry->GetText().getLength();
+ getIDocumentContentOperations().InsertPoolItem(
+ aPam, pEntry->GetRubyAttr(), SetAttrMode::DONTEXPAND );
+ }
+ else
+ break;
+ aPam.DeleteMark();
+ }
+ }
+ } while( nListEntry < rList.size() && *aPam.GetPoint() < *pEnd );
+ }
+ if( 30 <= rList.size() )
+ break;
+ _pStartCursor = _pStartCursor->GetNext();
+ } while( _pStartCursor != _pStartCursor2 );
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::SETRUBYATTR, nullptr );
+}
+
+bool SwDoc::SelectNextRubyChars( SwPaM& rPam, SwRubyListEntry& rEntry )
+{
+ // Point must be the startposition, Mark is optional the end position
+ SwPosition* pPos = rPam.GetPoint();
+ const SwTextNode* pTNd = pPos->nNode.GetNode().GetTextNode();
+ OUString const& rText = pTNd->GetText();
+ sal_Int32 nStart = pPos->nContent.GetIndex();
+ sal_Int32 nEnd = rText.getLength();
+
+ bool bHasMark = rPam.HasMark();
+ if( bHasMark )
+ {
+ // in the same node?
+ if( rPam.GetMark()->nNode == pPos->nNode )
+ {
+ // then use that end
+ const sal_Int32 nTEnd = rPam.GetMark()->nContent.GetIndex();
+ if( nTEnd < nEnd )
+ nEnd = nTEnd;
+ }
+ rPam.DeleteMark();
+ }
+
+ // search the start
+ // look where a ruby attribute starts
+ const SwpHints* pHts = pTNd->GetpSwpHints();
+ const SwTextAttr* pAttr = nullptr;
+ if( pHts )
+ {
+ for( size_t nHtIdx = 0; nHtIdx < pHts->Count(); ++nHtIdx )
+ {
+ const SwTextAttr* pHt = pHts->Get(nHtIdx);
+ if( RES_TXTATR_CJK_RUBY == pHt->Which() &&
+ pHt->GetAnyEnd() > nStart )
+ {
+ if( pHt->GetStart() < nEnd )
+ {
+ pAttr = pHt;
+ if( !bHasMark && nStart > pAttr->GetStart() )
+ {
+ nStart = pAttr->GetStart();
+ pPos->nContent = nStart;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if( !bHasMark && nStart && ( !pAttr || nStart != pAttr->GetStart()) )
+ {
+ // skip to the word begin!
+ const sal_Int32 nWordStt = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rText, nStart,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
+ WordType::ANYWORD_IGNOREWHITESPACES,
+ true ).startPos;
+ if (nWordStt < nStart && nWordStt >= 0)
+ {
+ nStart = nWordStt;
+ pPos->nContent = nStart;
+ }
+ }
+
+ bool bAlphaNum = false;
+ sal_Int32 nWordEnd = nEnd;
+ CharClass& rCC = GetAppCharClass();
+ while( nStart < nEnd )
+ {
+ if( pAttr && nStart == pAttr->GetStart() )
+ {
+ pPos->nContent = nStart;
+ if( !rPam.HasMark() )
+ {
+ rPam.SetMark();
+ pPos->nContent = pAttr->GetAnyEnd();
+ if( pPos->nContent.GetIndex() > nEnd )
+ pPos->nContent = nEnd;
+ rEntry.SetRubyAttr( pAttr->GetRuby() );
+ }
+ break;
+ }
+
+ sal_Int32 nChType = rCC.getType(rText, nStart);
+ bool bIgnoreChar = false, bIsAlphaNum = false, bChkNxtWrd = false;
+ switch( nChType )
+ {
+ case UnicodeType::UPPERCASE_LETTER:
+ case UnicodeType::LOWERCASE_LETTER:
+ case UnicodeType::TITLECASE_LETTER:
+ case UnicodeType::DECIMAL_DIGIT_NUMBER:
+ bChkNxtWrd = bIsAlphaNum = true;
+ break;
+
+ case UnicodeType::SPACE_SEPARATOR:
+ case UnicodeType::CONTROL:
+/*??*/ case UnicodeType::PRIVATE_USE:
+ case UnicodeType::START_PUNCTUATION:
+ case UnicodeType::END_PUNCTUATION:
+ bIgnoreChar = true;
+ break;
+
+ case UnicodeType::OTHER_LETTER:
+ bChkNxtWrd = true;
+ [[fallthrough]];
+ default:
+ bIsAlphaNum = false;
+ break;
+ }
+
+ if( rPam.HasMark() )
+ {
+ if( bIgnoreChar || bIsAlphaNum != bAlphaNum || nStart >= nWordEnd )
+ break;
+ }
+ else if( !bIgnoreChar )
+ {
+ rPam.SetMark();
+ bAlphaNum = bIsAlphaNum;
+ if (bChkNxtWrd)
+ {
+ // search the end of this word
+ nWordEnd = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rText, nStart,
+ g_pBreakIt->GetLocale( pTNd->GetLang( nStart )),
+ WordType::ANYWORD_IGNOREWHITESPACES,
+ true ).endPos;
+ if( 0 > nWordEnd || nWordEnd > nEnd || nWordEnd == nStart )
+ nWordEnd = nEnd;
+ }
+ }
+ pTNd->GoNext( &pPos->nContent, CRSR_SKIP_CHARS );
+ nStart = pPos->nContent.GetIndex();
+ }
+
+ nStart = rPam.GetMark()->nContent.GetIndex();
+ rEntry.SetText( rText.copy( nStart,
+ rPam.GetPoint()->nContent.GetIndex() - nStart ));
+ return rPam.HasMark();
+}
+
+SwRubyListEntry::~SwRubyListEntry()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docsort.cxx b/sw/source/core/doc/docsort.cxx
new file mode 100644
index 000000000..bd86cf29c
--- /dev/null
+++ b/sw/source/core/doc/docsort.cxx
@@ -0,0 +1,941 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <docary.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <sortopt.hxx>
+#include <docsort.hxx>
+#include <UndoSort.hxx>
+#include <UndoRedline.hxx>
+#include <hints.hxx>
+#include <tblsel.hxx>
+#include <cellatr.hxx>
+#include <redline.hxx>
+#include <node2lay.hxx>
+#include <frameformats.hxx>
+#include <svl/numformat.hxx>
+
+#include <set>
+#include <utility>
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+SwSortOptions* SwSortElement::pOptions = nullptr;
+SwDoc* SwSortElement::pDoc = nullptr;
+const FlatFndBox* SwSortElement::pBox = nullptr;
+CollatorWrapper* SwSortElement::pSortCollator = nullptr;
+lang::Locale* SwSortElement::pLocale = nullptr;
+std::optional<OUString> SwSortElement::xLastAlgorithm;
+LocaleDataWrapper* SwSortElement::pLclData = nullptr;
+
+// List of all sorted elements
+
+/// Construct a SortElement for the Sort
+void SwSortElement::Init( SwDoc* pD, const SwSortOptions& rOpt,
+ FlatFndBox const * pFltBx )
+{
+ OSL_ENSURE( !pDoc && !pOptions && !pBox, "Who forgot to call Finit?" );
+ pDoc = pD;
+ pOptions = new SwSortOptions( rOpt );
+ pBox = pFltBx;
+
+ LanguageType nLang = rOpt.nLanguage;
+ if ( nLang.anyOf(
+ LANGUAGE_NONE,
+ LANGUAGE_DONTKNOW))
+ nLang = GetAppLanguage();
+ pLocale = new lang::Locale( LanguageTag::convertToLocale( nLang ) );
+
+ pSortCollator = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
+}
+
+void SwSortElement::Finit()
+{
+ delete pOptions;
+ pOptions = nullptr;
+ delete pLocale;
+ pLocale = nullptr;
+ xLastAlgorithm.reset();
+ delete pSortCollator;
+ pSortCollator = nullptr;
+ delete pLclData;
+ pLclData = nullptr;
+ pDoc = nullptr;
+ pBox = nullptr;
+}
+
+SwSortElement::~SwSortElement()
+{
+}
+
+double SwSortElement::StrToDouble( std::u16string_view rStr )
+{
+ if( !pLclData )
+ pLclData = new LocaleDataWrapper( LanguageTag( *pLocale ));
+
+ rtl_math_ConversionStatus eStatus;
+ sal_Int32 nEnd;
+ double nRet = pLclData->stringToDouble( rStr, true, &eStatus, &nEnd );
+
+ if( rtl_math_ConversionStatus_Ok != eStatus || nEnd == 0 )
+ nRet = 0.0;
+ return nRet;
+}
+
+int SwSortElement::keycompare(const SwSortElement& rCmp, sal_uInt16 nKey) const
+{
+ int nCmp = 0;
+ // The actual comparison
+ const SwSortElement *pOrig, *pCmp;
+
+ const SwSortKey& rSrtKey = pOptions->aKeys[ nKey ];
+ if( rSrtKey.eSortOrder == SwSortOrder::Ascending )
+ {
+ pOrig = this;
+ pCmp = &rCmp;
+ }
+ else
+ {
+ pOrig = &rCmp;
+ pCmp = this;
+ }
+
+ if( rSrtKey.bIsNumeric )
+ {
+ double n1 = pOrig->GetValue( nKey );
+ double n2 = pCmp->GetValue( nKey );
+
+ nCmp = n1 < n2 ? -1 : n1 == n2 ? 0 : 1;
+ }
+ else
+ {
+ if( !xLastAlgorithm || *xLastAlgorithm != rSrtKey.sSortType )
+ {
+ xLastAlgorithm = rSrtKey.sSortType;
+ pSortCollator->loadCollatorAlgorithm( *xLastAlgorithm,
+ *pLocale,
+ pOptions->bIgnoreCase ? SW_COLLATOR_IGNORES : 0 );
+ }
+
+ nCmp = pSortCollator->compareString(
+ pOrig->GetKey( nKey ), pCmp->GetKey( nKey ));
+ }
+ return nCmp;
+}
+
+bool SwSortElement::operator<(const SwSortElement& rCmp) const
+{
+ // The actual comparison
+ for(size_t nKey = 0; nKey < pOptions->aKeys.size(); ++nKey)
+ {
+ int nCmp = keycompare(rCmp, nKey);
+
+ if (nCmp == 0)
+ continue;
+
+ return nCmp < 0;
+ }
+
+ return false;
+}
+
+double SwSortElement::GetValue( sal_uInt16 nKey ) const
+{
+ return StrToDouble( GetKey( nKey ));
+}
+
+/// SortingElement for Text
+SwSortTextElement::SwSortTextElement(const SwNodeIndex& rPos)
+ : nOrg(rPos.GetIndex()), aPos(rPos)
+{
+}
+
+OUString SwSortTextElement::GetKey(sal_uInt16 nId) const
+{
+ SwTextNode* pTextNd = aPos.GetNode().GetTextNode();
+ if( !pTextNd )
+ return OUString();
+
+ // for TextNodes
+ const OUString& rStr = pTextNd->GetText();
+
+ sal_Unicode nDeli = pOptions->cDeli;
+ sal_uInt16 nDCount = pOptions->aKeys[nId].nColumnId, i = 1;
+ sal_Int32 nStart = 0;
+
+ // Find the delimiter
+ while( nStart != -1 && i < nDCount)
+ {
+ nStart = rStr.indexOf( nDeli, nStart );
+ if( -1 != nStart )
+ {
+ nStart++;
+ i++;
+ }
+ }
+
+ // Found next delimiter or end of String
+ // and copy
+ sal_Int32 nEnd = rStr.indexOf( nDeli, nStart+1 );
+ if (nEnd == -1)
+ return rStr.copy( nStart );
+ return rStr.copy( nStart, nEnd-nStart );
+}
+
+/// SortingElement for Tables
+SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC )
+ : nRow( nRC )
+{
+}
+
+/// Get Key for a cell
+OUString SwSortBoxElement::GetKey(sal_uInt16 nKey) const
+{
+ const FndBox_* pFndBox;
+ sal_uInt16 nCol = pOptions->aKeys[nKey].nColumnId-1;
+
+ if( SwSortDirection::Rows == pOptions->eDirection )
+ pFndBox = pBox->GetBox(nCol, nRow); // Sort rows
+ else
+ pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
+
+ // Extract the Text
+ OUStringBuffer aRetStr;
+ if( pFndBox )
+ { // Get StartNode and skip it
+ const SwTableBox* pMyBox = pFndBox->GetBox();
+ OSL_ENSURE(pMyBox, "No atomic Box");
+
+ if( pMyBox->GetSttNd() )
+ {
+ // Iterate over all the Box's TextNodes
+ const SwNode *pNd = nullptr, *pEndNd = pMyBox->GetSttNd()->EndOfSectionNode();
+ for( SwNodeOffset nIdx = pMyBox->GetSttIdx() + 1; pNd != pEndNd; ++nIdx )
+ {
+ pNd = pDoc->GetNodes()[ nIdx ];
+ if( pNd->IsTextNode() )
+ aRetStr.append(pNd->GetTextNode()->GetText());
+ }
+ }
+ }
+ return aRetStr.makeStringAndClear();
+}
+
+double SwSortBoxElement::GetValue( sal_uInt16 nKey ) const
+{
+ const FndBox_* pFndBox;
+ sal_uInt16 nCol = pOptions->aKeys[nKey].nColumnId-1;
+
+ if( SwSortDirection::Rows == pOptions->eDirection )
+ pFndBox = pBox->GetBox(nCol, nRow); // Sort rows
+ else
+ pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
+
+ double nVal;
+ if( pFndBox )
+ {
+ const SwFormat *pFormat = pFndBox->GetBox()->GetFrameFormat();
+ if (pDoc->GetNumberFormatter()->IsTextFormat( pFormat->GetTableBoxNumFormat().GetValue()))
+ nVal = SwSortElement::GetValue( nKey );
+ else
+ nVal = pFormat->GetTableBoxValue().GetValue();
+ }
+ else
+ nVal = 0;
+
+ return nVal;
+}
+
+/// Sort Text in the Document
+bool SwDoc::SortText(const SwPaM& rPaM, const SwSortOptions& rOpt)
+{
+ // Check if Frame is in the Text
+ const SwPosition *pStart = rPaM.Start(), *pEnd = rPaM.End();
+
+ // Set index to the Selection's start
+ for ( const auto *pFormat : *GetSpzFrameFormats() )
+ {
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+
+ if (pAPos && (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) &&
+ pStart->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode )
+ return false;
+ }
+
+ // Check if only TextNodes are within the Selection
+ {
+ SwNodeOffset nStart = pStart->nNode.GetIndex(),
+ nEnd = pEnd->nNode.GetIndex();
+ while( nStart <= nEnd )
+ // Iterate over a selected range
+ if( !GetNodes()[ nStart++ ]->IsTextNode() )
+ return false;
+ }
+
+ bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
+ if( bUndo )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ }
+
+ SwPaM* pRedlPam = nullptr;
+ SwUndoRedlineSort* pRedlUndo = nullptr;
+ SwUndoSort* pUndoSort = nullptr;
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ pRedlPam = new SwPaM( pStart->nNode, pEnd->nNode, SwNodeOffset(-1), SwNodeOffset(1) );
+ SwContentNode* pCNd = pRedlPam->GetContentNode( false );
+ if( pCNd )
+ pRedlPam->GetMark()->nContent = pCNd->Len();
+
+ if( getIDocumentRedlineAccess().IsRedlineOn() && !IDocumentRedlineAccess::IsShowOriginal( getIDocumentRedlineAccess().GetRedlineFlags() ) )
+ {
+ if( bUndo )
+ {
+ pRedlUndo = new SwUndoRedlineSort( *pRedlPam,rOpt );
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+ // First copy the range
+ SwNodeIndex aEndIdx( pEnd->nNode, 1 );
+ SwNodeRange aRg( pStart->nNode, aEndIdx );
+ GetNodes().Copy_( aRg, aEndIdx );
+
+ // range is new from pEnd->nNode+1 to aEndIdx
+ getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
+
+ pRedlPam->GetMark()->nNode.Assign( pEnd->nNode.GetNode(), 1 );
+ pCNd = pRedlPam->GetContentNode( false );
+ pRedlPam->GetMark()->nContent.Assign( pCNd, 0 );
+
+ pRedlPam->GetPoint()->nNode.Assign( aEndIdx.GetNode() );
+ pCNd = pRedlPam->GetContentNode();
+ sal_Int32 nCLen = 0;
+ if( !pCNd )
+ {
+ pCNd = GetNodes()[ aEndIdx.GetIndex()-SwNodeOffset(1) ]->GetContentNode();
+ if( pCNd )
+ {
+ nCLen = pCNd->Len();
+ pRedlPam->GetPoint()->nNode.Assign( *pCNd );
+ }
+ }
+ pRedlPam->GetPoint()->nContent.Assign( pCNd, nCLen );
+
+ if( pRedlUndo )
+ pRedlUndo->SetValues( rPaM );
+ }
+ else
+ {
+ getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
+ delete pRedlPam;
+ pRedlPam = nullptr;
+ }
+ }
+
+ SwNodeIndex aStart(pStart->nNode);
+ SwSortElement::Init( this, rOpt );
+ std::multiset<SwSortTextElement> aSortSet;
+ while( aStart <= pEnd->nNode )
+ {
+ // Iterate over a selected range
+ aSortSet.insert(SwSortTextElement(aStart));
+ ++aStart;
+ }
+
+ // Now comes the tricky part: Move Nodes (and always keep Undo in mind)
+ SwNodeOffset nBeg = pStart->nNode.GetIndex();
+ SwNodeRange aRg( aStart, aStart );
+
+ if( bUndo && !pRedlUndo )
+ {
+ pUndoSort = new SwUndoSort(rPaM, rOpt);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoSort));
+ }
+
+ GetIDocumentUndoRedo().DoUndo(false);
+
+ SwNodeOffset n(0);
+ for (const auto& rElem : aSortSet)
+ {
+ aStart = nBeg + n;
+ aRg.aStart = rElem.aPos.GetIndex();
+ aRg.aEnd = aRg.aStart.GetIndex() + 1;
+
+ // Move Nodes
+ getIDocumentContentOperations().MoveNodeRange( aRg, aStart,
+ SwMoveFlags::DEFAULT );
+
+ // Insert Move in Undo
+ if(pUndoSort)
+ {
+ pUndoSort->Insert(rElem.nOrg, nBeg + n);
+ }
+ ++n;
+ }
+ // Delete all elements from the SortArray
+ aSortSet.clear();
+ SwSortElement::Finit();
+
+ if( pRedlPam )
+ {
+ if( pRedlUndo )
+ {
+ pRedlUndo->SetSaveRange( *pRedlPam );
+ // UGLY: temp. enable Undo
+ GetIDocumentUndoRedo().DoUndo(true);
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pRedlUndo) );
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ // nBeg is start of sorted range
+ SwNodeIndex aSttIdx( GetNodes(), nBeg );
+
+ // the copied range is deleted
+ SwRangeRedline *const pDeleteRedline(
+ new SwRangeRedline( RedlineType::Delete, *pRedlPam ));
+
+ // pRedlPam points to nodes that may be deleted (hidden) by
+ // AppendRedline, so adjust it beforehand to prevent ASSERT
+ pRedlPam->GetPoint()->nNode = aSttIdx;
+ SwContentNode* pCNd = aSttIdx.GetNode().GetContentNode();
+ pRedlPam->GetPoint()->nContent.Assign( pCNd, 0 );
+
+ getIDocumentRedlineAccess().AppendRedline(pDeleteRedline, true);
+
+ // the sorted range is inserted
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlPam ), true);
+
+ if( pRedlUndo )
+ {
+ SwNodeIndex aInsEndIdx( pRedlPam->GetMark()->nNode, -1 );
+ pRedlPam->GetMark()->nNode = aInsEndIdx;
+ SwContentNode *const pPrevNode =
+ pRedlPam->GetMark()->nNode.GetNode().GetContentNode();
+ pRedlPam->GetMark()->nContent.Assign( pPrevNode, pPrevNode->Len() );
+
+ pRedlUndo->SetValues( *pRedlPam );
+ }
+
+ delete pRedlPam;
+ pRedlPam = nullptr;
+ }
+ GetIDocumentUndoRedo().DoUndo( bUndo );
+ if( bUndo )
+ {
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+
+ return true;
+}
+
+/// Sort Table in the Document
+bool SwDoc::SortTable(const SwSelBoxes& rBoxes, const SwSortOptions& rOpt)
+{
+ // Via SwDoc for Undo!
+ OSL_ENSURE( !rBoxes.empty(), "no valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // We begin sorting
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+
+ if(aFndBox.GetLines().empty())
+ return false;
+
+ if( !getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ getIDocumentRedlineAccess().DeleteRedline( *pTableNd, true, RedlineType::Any );
+
+ FndLines_t::size_type nStart = 0;
+ if( pTableNd->GetTable().GetRowsToRepeat() > 0 && rOpt.eDirection == SwSortDirection::Rows )
+ {
+ // Uppermost selected Cell
+ FndLines_t& rLines = aFndBox.GetLines();
+
+ while( nStart < rLines.size() )
+ {
+ // Respect Split Merge nesting,
+ // extract the upper most
+ SwTableLine* pLine = rLines[nStart]->GetLine();
+ while ( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ if( pTableNd->GetTable().IsHeadline( *pLine ) )
+ nStart++;
+ else
+ break;
+ }
+ // Are all selected in the HeaderLine? -> no Offset
+ if( nStart == rLines.size() )
+ nStart = 0;
+ }
+
+ // Switch to relative Formulas
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_RELBOXNAME;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // Table as a flat array structure
+ FlatFndBox aFlatBox(this, aFndBox);
+
+ if(!aFlatBox.IsSymmetric())
+ return false;
+
+ // Delete HTML layout
+ pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
+
+ // Delete the Table's Frames
+ pTableNd->DelFrames();
+ // ? TL_CHART2: ?
+
+ SwUndoSort* pUndoSort = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndoSort = new SwUndoSort( rBoxes[0]->GetSttIdx(),
+ rBoxes.back()->GetSttIdx(),
+ *pTableNd, rOpt, aFlatBox.HasItemSets() );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoSort));
+ }
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // Insert KeyElements
+ sal_uInt16 nCount = (rOpt.eDirection == SwSortDirection::Rows) ?
+ aFlatBox.GetRows() : aFlatBox.GetCols();
+
+ // Sort SortList by Key
+ SwSortElement::Init( this, rOpt, &aFlatBox );
+ std::multiset<SwSortBoxElement> aSortList;
+
+ // When sorting, do not include the first row if the HeaderLine is repeated
+ for( sal_uInt16 i = o3tl::narrowing<sal_uInt16>(nStart); i < nCount; ++i)
+ {
+ aSortList.insert(SwSortBoxElement(i));
+ }
+
+ // Move after Sorting
+ SwMovedBoxes aMovedList;
+ sal_uInt16 i = 0;
+ for (const auto& rElem : aSortList)
+ {
+ if(rOpt.eDirection == SwSortDirection::Rows)
+ {
+ MoveRow(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
+ }
+ else
+ {
+ MoveCol(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
+ }
+ ++i;
+ }
+
+ // Restore table frames:
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ const SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
+
+ // Delete all Elements in the SortArray
+ aSortList.clear();
+ SwSortElement::Finit();
+
+ getIDocumentState().SetModified();
+ return true;
+}
+
+/// Move a row
+void MoveRow(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
+ SwMovedBoxes& rMovedList, SwUndoSort* pUD)
+{
+ for( sal_uInt16 i=0; i < rBox.GetCols(); ++i )
+ { // Get old cell position and remember it
+ const FndBox_* pSource = rBox.GetBox(i, nS);
+
+ // new cell position
+ const FndBox_* pTarget = rBox.GetBox(i, nT);
+
+ const SwTableBox* pT = pTarget->GetBox();
+ const SwTableBox* pS = pSource->GetBox();
+
+ bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
+
+ // and move it
+ MoveCell(pDoc, pS, pT, bMoved, pUD);
+
+ rMovedList.push_back(pS);
+
+ if( pS != pT )
+ {
+ SwFrameFormat* pTFormat = pT->GetFrameFormat();
+ const SfxItemSet* pSSet = rBox.GetItemSet( i, nS );
+
+ if( pSSet ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ pTFormat = const_cast<SwTableBox*>(pT)->ClaimFrameFormat();
+ pTFormat->LockModify();
+ if( pTFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
+ pTFormat->ResetFormatAttr( RES_VERT_ORIENT );
+
+ if( pSSet )
+ pTFormat->SetFormatAttr( *pSSet );
+ pTFormat->UnlockModify();
+ }
+ }
+ }
+}
+
+/// Move a column
+void MoveCol(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
+ SwMovedBoxes& rMovedList, SwUndoSort* pUD)
+{
+ for(sal_uInt16 i=0; i < rBox.GetRows(); ++i)
+ { // Get old cell position and remember it
+ const FndBox_* pSource = rBox.GetBox(nS, i);
+
+ // new cell position
+ const FndBox_* pTarget = rBox.GetBox(nT, i);
+
+ // and move it
+ const SwTableBox* pT = pTarget->GetBox();
+ const SwTableBox* pS = pSource->GetBox();
+
+ // and move it
+ bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
+ MoveCell(pDoc, pS, pT, bMoved, pUD);
+
+ rMovedList.push_back(pS);
+
+ if( pS != pT )
+ {
+ SwFrameFormat* pTFormat = pT->GetFrameFormat();
+ const SfxItemSet* pSSet = rBox.GetItemSet( nS, i );
+
+ if( pSSet ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ pTFormat = const_cast<SwTableBox*>(pT)->ClaimFrameFormat();
+ pTFormat->LockModify();
+ if( pTFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
+ pTFormat->ResetFormatAttr( RES_VERT_ORIENT );
+
+ if( pSSet )
+ pTFormat->SetFormatAttr( *pSSet );
+ pTFormat->UnlockModify();
+ }
+ }
+ }
+}
+
+/// Move a single Cell
+void MoveCell(SwDoc* pDoc, const SwTableBox* pSource, const SwTableBox* pTar,
+ bool bMovedBefore, SwUndoSort* pUD)
+{
+ OSL_ENSURE(pSource && pTar,"Source or target missing");
+
+ if(pSource == pTar)
+ return;
+
+ if(pUD)
+ pUD->Insert( pSource->GetName(), pTar->GetName() );
+
+ // Set Pam source to the first ContentNode
+ SwNodeRange aRg( *pSource->GetSttNd(), SwNodeOffset(0), *pSource->GetSttNd() );
+ SwNode* pNd = pDoc->GetNodes().GoNext( &aRg.aStart );
+
+ // If the Cell (Source) wasn't moved
+ // -> insert an empty Node and move the rest or the Mark
+ // points to the first ContentNode
+ if( pNd->StartOfSectionNode() == pSource->GetSttNd() )
+ pNd = pDoc->GetNodes().MakeTextNode( aRg.aStart,
+ pDoc->GetDfltTextFormatColl() );
+ aRg.aEnd = *pNd->EndOfSectionNode();
+
+ // If the Target is empty (there is one empty Node)
+ // -> move and delete it
+ SwNodeIndex aTar( *pTar->GetSttNd() );
+ pNd = pDoc->GetNodes().GoNext( &aTar ); // next ContentNode
+ SwNodeOffset nCount = pNd->EndOfSectionIndex() - pNd->StartOfSectionIndex();
+
+ bool bDelFirst = false;
+ if( nCount == SwNodeOffset(2) )
+ {
+ OSL_ENSURE( pNd->GetContentNode(), "No ContentNode");
+ bDelFirst = !pNd->GetContentNode()->Len() && bMovedBefore;
+ }
+
+ if(!bDelFirst)
+ { // We already have Content -> old Content Section Down
+ SwNodeRange aRgTar( aTar.GetNode(), SwNodeOffset(0), *pNd->EndOfSectionNode() );
+ pDoc->GetNodes().SectionDown( &aRgTar );
+ }
+
+ // Insert the Source
+ SwNodeIndex aIns( *pTar->GetSttNd()->EndOfSectionNode() );
+ pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIns,
+ SwMoveFlags::DEFAULT );
+
+ // If first Node is empty -> delete it
+ if(bDelFirst)
+ pDoc->GetNodes().Delete( aTar );
+}
+
+/// Generate two-dimensional array of FndBoxes
+FlatFndBox::FlatFndBox(SwDoc* pDocPtr, const FndBox_& rBoxRef) :
+ m_pDoc(pDocPtr),
+ m_nRow(0),
+ m_nCol(0)
+{ // If the array is symmetric
+ m_bSym = CheckLineSymmetry(rBoxRef);
+ if( !m_bSym )
+ return;
+
+ // Determine column/row count
+ m_nCols = GetColCount(rBoxRef);
+ m_nRows = GetRowCount(rBoxRef);
+
+ // Create linear array
+ size_t nCount = static_cast<size_t>(m_nRows) * m_nCols;
+ m_pArr = std::make_unique<FndBox_ const *[]>(nCount);
+ memset(m_pArr.get(), 0, sizeof(const FndBox_*) * nCount);
+
+ FillFlat( rBoxRef );
+}
+
+FlatFndBox::~FlatFndBox()
+{
+}
+
+/// All Lines of a Box need to have same number of Boxes
+bool FlatFndBox::CheckLineSymmetry(const FndBox_& rBox)
+{
+ const FndLines_t &rLines = rBox.GetLines();
+ FndBoxes_t::size_type nBoxes {0};
+
+ for (FndLines_t::size_type i=0; i < rLines.size(); ++i)
+ {
+ const FndLine_* pLn = rLines[i].get();
+ const FndBoxes_t& rBoxes = pLn->GetBoxes();
+
+ // Number of Boxes of all Lines is unequal -> no symmetry
+ if( i && nBoxes != rBoxes.size())
+ return false;
+
+ nBoxes = rBoxes.size();
+ if( !CheckBoxSymmetry( *pLn ) )
+ return false;
+ }
+ return true;
+}
+
+/// Check Box for symmetry (All Boxes of a Line need to have same number of Lines)
+bool FlatFndBox::CheckBoxSymmetry(const FndLine_& rLn)
+{
+ const FndBoxes_t &rBoxes = rLn.GetBoxes();
+ FndLines_t::size_type nLines {0};
+
+ for (FndBoxes_t::size_type i = 0; i < rBoxes.size(); ++i)
+ {
+ FndBox_ const*const pBox = rBoxes[i].get();
+ const FndLines_t& rLines = pBox->GetLines();
+
+ // Number of Lines of all Boxes is unequal -> no symmetry
+ if( i && nLines != rLines.size() )
+ return false;
+
+ nLines = rLines.size();
+ if( nLines && !CheckLineSymmetry( *pBox ) )
+ return false;
+ }
+ return true;
+}
+
+/// Maximum count of Columns (Boxes)
+sal_uInt16 FlatFndBox::GetColCount(const FndBox_& rBox)
+{
+ const FndLines_t& rLines = rBox.GetLines();
+ // Iterate over Lines
+ if( rLines.empty() )
+ return 1;
+
+ sal_uInt16 nSum = 0;
+ for (const auto & pLine : rLines)
+ {
+ // The Boxes of a Line
+ sal_uInt16 nCount = 0;
+ const FndBoxes_t& rBoxes = pLine->GetBoxes();
+ for (const auto &rpB : rBoxes)
+ { // Iterate recursively over the Lines
+ nCount += rpB->GetLines().empty() ? 1 : GetColCount(*rpB);
+ }
+
+ if( nSum < nCount )
+ nSum = nCount;
+ }
+ return nSum;
+}
+
+/// Maximum count of Rows (Lines)
+sal_uInt16 FlatFndBox::GetRowCount(const FndBox_& rBox)
+{
+ const FndLines_t& rLines = rBox.GetLines();
+ if( rLines.empty() )
+ return 1;
+
+ sal_uInt16 nLines = 0;
+ for (const auto & pLine : rLines)
+ { // The Boxes of a Line
+ const FndBoxes_t& rBoxes = pLine->GetBoxes();
+ sal_uInt16 nLn = 1;
+ for (const auto &rpB : rBoxes)
+ {
+ if (!rpB->GetLines().empty())
+ { // Iterate recursively over the Lines
+ nLn = std::max(GetRowCount(*rpB), nLn);
+ }
+ }
+
+ nLines = nLines + nLn;
+ }
+ return nLines;
+}
+
+/// Create a linear array of atomic FndBoxes
+void FlatFndBox::FillFlat(const FndBox_& rBox, bool bLastBox)
+{
+ bool bModRow = false;
+ const FndLines_t& rLines = rBox.GetLines();
+
+ // Iterate over Lines
+ sal_uInt16 nOldRow = m_nRow;
+ for (const auto & pLine : rLines)
+ {
+ // The Boxes of a Line
+ const FndBoxes_t& rBoxes = pLine->GetBoxes();
+ sal_uInt16 nOldCol = m_nCol;
+ for( FndBoxes_t::size_type j = 0; j < rBoxes.size(); ++j )
+ {
+ // Check the Box if it's an atomic one
+ const FndBox_ *const pBox = rBoxes[j].get();
+
+ if( pBox->GetLines().empty() )
+ {
+ // save it
+ sal_uInt16 nOff = m_nRow * m_nCols + m_nCol;
+ m_pArr[nOff] = pBox;
+
+ // Save the Formula/Format/Value values
+ const SwFrameFormat* pFormat = pBox->GetBox()->GetFrameFormat();
+ if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>
+ aSet(m_pDoc->GetAttrPool());
+ aSet.Put( pFormat->GetAttrSet() );
+ if( m_vItemSets.empty() )
+ {
+ size_t nCount = static_cast<size_t>(m_nRows) * m_nCols;
+ m_vItemSets.resize(nCount);
+ }
+ m_vItemSets[nOff].emplace(std::move(aSet));
+ }
+
+ bModRow = true;
+ }
+ else
+ {
+ // Iterate recursively over the Lines of a Box
+ FillFlat( *pBox, ( j+1 == rBoxes.size() ) );
+ }
+ m_nCol++;
+ }
+ if(bModRow)
+ m_nRow++;
+ m_nCol = nOldCol;
+ }
+ if(!bLastBox)
+ m_nRow = nOldRow;
+}
+
+/// Access a specific Cell
+const FndBox_* FlatFndBox::GetBox(sal_uInt16 n_Col, sal_uInt16 n_Row) const
+{
+ sal_uInt16 nOff = n_Row * m_nCols + n_Col;
+ const FndBox_* pTmp = m_pArr[nOff];
+
+ OSL_ENSURE(n_Col < m_nCols && n_Row < m_nRows && pTmp, "invalid array access");
+ return pTmp;
+}
+
+const SfxItemSet* FlatFndBox::GetItemSet(sal_uInt16 n_Col, sal_uInt16 n_Row) const
+{
+ OSL_ENSURE( m_vItemSets.empty() || ( n_Col < m_nCols && n_Row < m_nRows), "invalid array access");
+
+ if (m_vItemSets.empty()) {
+ return nullptr;
+ }
+ auto const & el = m_vItemSets[unsigned(n_Row * m_nCols) + n_Col];
+ return el ? &*el : nullptr;
+}
+
+sal_uInt16 SwMovedBoxes::GetPos(const SwTableBox* pTableBox) const
+{
+ std::vector<const SwTableBox*>::const_iterator it = std::find(mBoxes.begin(), mBoxes.end(), pTableBox);
+ return it == mBoxes.end() ? USHRT_MAX : it - mBoxes.begin();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docstat.cxx b/sw/source/core/doc/docstat.cxx
new file mode 100644
index 000000000..c34e8d094
--- /dev/null
+++ b/sw/source/core/doc/docstat.cxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <docstat.hxx>
+
+SwDocStat::SwDocStat() :
+ nTable(0),
+ nGrf(0),
+ nOLE(0),
+ nPage(1),
+ nPara(1),
+ nAllPara(1),
+ nWord(0),
+ nAsianWord(0),
+ nChar(0),
+ nCharExcludingSpaces(0),
+ bModified(true)
+{}
+
+void SwDocStat::Reset()
+{
+ nTable = 0;
+ nGrf = 0;
+ nOLE = 0;
+ nPage = 1;
+ nPara = 1;
+ nAllPara= 1;
+ nWord = 0;
+ nAsianWord = 0;
+ nChar = 0;
+ nCharExcludingSpaces = 0;
+ bModified = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx
new file mode 100644
index 000000000..ea4cf47a3
--- /dev/null
+++ b/sw/source/core/doc/doctxm.cxx
@@ -0,0 +1,2133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <limits.h>
+#include <hintids.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <comphelper/classids.hxx>
+#include <o3tl/string_view.hxx>
+#include <docsh.hxx>
+#include <ndole.hxx>
+#include <txttxmrk.hxx>
+#include <fmtpdsc.hxx>
+#include <frmatr.hxx>
+#include <pagedesc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pagefrm.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <doctxm.hxx>
+#include <txmsrt.hxx>
+#include <rolbck.hxx>
+#include <poolfmt.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <UndoAttribute.hxx>
+#include <UndoSection.hxx>
+#include <swundo.hxx>
+#include <mdiexp.hxx>
+#include <docary.hxx>
+#include <charfmt.hxx>
+#include <fchrfmt.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <expfld.hxx>
+#include <mvsave.hxx>
+#include <node2lay.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <breakit.hxx>
+#include <calbck.hxx>
+#include <ToxTextGenerator.hxx>
+#include <ToxTabStopTokenHandler.hxx>
+#include <frameformats.hxx>
+#include <tools/datetimeutils.hxx>
+#include <tools/globname.hxx>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+template<typename T, typename... Args> static
+typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
+MakeSwTOXSortTabBase(SwRootFrame const*const pLayout, Args&& ... args)
+{
+ std::unique_ptr<T> pRet(new T(std::forward<Args>(args)...));
+ pRet->InitText(pLayout); // ensure it's expanded with the layout
+ return pRet;
+}
+
+void SwDoc::GetTOIKeys(SwTOIKeyType eTyp, std::vector<OUString>& rArr,
+ SwRootFrame const& rLayout) const
+{
+ rArr.clear();
+
+ // Look up all Primary and Secondary via the Pool
+ for (const SfxPoolItem* pPoolItem : GetAttrPool().GetItemSurrogates(RES_TXTATR_TOXMARK))
+ {
+ const SwTOXMark* pItem = dynamic_cast<const SwTOXMark*>(pPoolItem);
+ if( !pItem )
+ continue;
+ const SwTOXType* pTOXType = pItem->GetTOXType();
+ if ( !pTOXType || pTOXType->GetType()!=TOX_INDEX )
+ continue;
+ const SwTextTOXMark* pMark = pItem->GetTextTOXMark();
+ if ( pMark && pMark->GetpTextNd() &&
+ pMark->GetpTextNd()->GetNodes().IsDocNodes() &&
+ (!rLayout.IsHideRedlines()
+ || !sw::IsMarkHintHidden(rLayout, *pMark->GetpTextNd(), *pMark)))
+ {
+ const OUString sStr = TOI_PRIMARY == eTyp
+ ? pItem->GetPrimaryKey()
+ : pItem->GetSecondaryKey();
+
+ if( !sStr.isEmpty() )
+ rArr.push_back( sStr );
+ }
+ }
+}
+
+/// Get current table of contents Mark.
+sal_uInt16 SwDoc::GetCurTOXMark( const SwPosition& rPos,
+ SwTOXMarks& rArr )
+{
+ // search on Position rPos for all SwTOXMarks
+ SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( !pTextNd || !pTextNd->GetpSwpHints() )
+ return 0;
+
+ const SwpHints & rHts = *pTextNd->GetpSwpHints();
+ sal_Int32 nSttIdx;
+ const sal_Int32 *pEndIdx;
+
+ const sal_Int32 nCurrentPos = rPos.nContent.GetIndex();
+
+ for( size_t n = 0; n < rHts.Count(); ++n )
+ {
+ const SwTextAttr* pHt = rHts.Get(n);
+ if( RES_TXTATR_TOXMARK != pHt->Which() )
+ continue;
+ if( ( nSttIdx = pHt->GetStart() ) < nCurrentPos )
+ {
+ // also check the end
+ pEndIdx = pHt->End();
+ if( nullptr == pEndIdx || *pEndIdx <= nCurrentPos )
+ continue; // keep searching
+ }
+ else if( nSttIdx > nCurrentPos )
+ // If Hint's Start is greater than rPos, break, because
+ // the attributes are sorted by Start!
+ break;
+
+ SwTOXMark* pTMark = const_cast<SwTOXMark*>(&pHt->GetTOXMark());
+ rArr.push_back( pTMark );
+ }
+ return rArr.size();
+}
+
+/// Delete table of contents Mark
+void SwDoc::DeleteTOXMark( const SwTOXMark* pTOXMark )
+{
+ const SwTextTOXMark* pTextTOXMark = pTOXMark->GetTextTOXMark();
+ assert(pTextTOXMark);
+
+ SwTextNode& rTextNd = const_cast<SwTextNode&>(pTextTOXMark->GetTextNode());
+ assert(rTextNd.GetpSwpHints());
+
+ if (pTextTOXMark->HasDummyChar())
+ {
+ // tdf#106377 don't use SwUndoResetAttr, it uses NOTXTATRCHR
+ SwPaM tmp(rTextNd, pTextTOXMark->GetStart(),
+ rTextNd, pTextTOXMark->GetStart()+1);
+ assert(rTextNd.GetText()[pTextTOXMark->GetStart()] == CH_TXTATR_INWORD);
+ getIDocumentContentOperations().DeleteRange(tmp);
+ }
+ else
+ {
+ std::unique_ptr<SwRegHistory> aRHst;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ // save attributes for Undo
+ SwUndoResetAttr* pUndo = new SwUndoResetAttr(
+ SwPosition( rTextNd, SwIndex( &rTextNd, pTextTOXMark->GetStart() ) ),
+ RES_TXTATR_TOXMARK );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+
+ aRHst.reset(new SwRegHistory(rTextNd, &pUndo->GetHistory()));
+ rTextNd.GetpSwpHints()->Register(aRHst.get());
+ }
+
+ rTextNd.DeleteAttribute( const_cast<SwTextTOXMark*>(pTextTOXMark) );
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ if( rTextNd.GetpSwpHints() )
+ rTextNd.GetpSwpHints()->DeRegister();
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+namespace {
+
+/// Travel between table of content Marks
+class CompareNodeContent
+{
+ SwNodeOffset m_nNode;
+ sal_Int32 m_nContent;
+public:
+ CompareNodeContent( SwNodeOffset nNd, sal_Int32 nCnt )
+ : m_nNode( nNd ), m_nContent( nCnt ) {}
+
+ bool operator==( const CompareNodeContent& rCmp ) const
+ { return m_nNode == rCmp.m_nNode && m_nContent == rCmp.m_nContent; }
+ bool operator!=( const CompareNodeContent& rCmp ) const
+ { return m_nNode != rCmp.m_nNode || m_nContent != rCmp.m_nContent; }
+ bool operator< ( const CompareNodeContent& rCmp ) const
+ { return m_nNode < rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent < rCmp.m_nContent); }
+ bool operator<=( const CompareNodeContent& rCmp ) const
+ { return m_nNode < rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent <= rCmp.m_nContent); }
+ bool operator> ( const CompareNodeContent& rCmp ) const
+ { return m_nNode > rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent > rCmp.m_nContent); }
+ bool operator>=( const CompareNodeContent& rCmp ) const
+ { return m_nNode > rCmp.m_nNode ||
+ ( m_nNode == rCmp.m_nNode && m_nContent >= rCmp.m_nContent); }
+};
+
+}
+
+const SwTOXMark& SwDoc::GotoTOXMark( const SwTOXMark& rCurTOXMark,
+ SwTOXSearch eDir, bool bInReadOnly )
+{
+ const SwTextTOXMark* pMark = rCurTOXMark.GetTextTOXMark();
+
+ CompareNodeContent aAbsIdx(pMark ? pMark->GetpTextNd()->GetIndex() : SwNodeOffset(0), pMark ? pMark->GetStart() : 0);
+ CompareNodeContent aPrevPos( SwNodeOffset(0), 0 );
+ CompareNodeContent aNextPos( NODE_OFFSET_MAX, SAL_MAX_INT32 );
+ CompareNodeContent aMax( SwNodeOffset(0), 0 );
+ CompareNodeContent aMin( NODE_OFFSET_MAX, SAL_MAX_INT32 );
+
+ const SwTOXMark* pNew = nullptr;
+ const SwTOXMark* pMax = &rCurTOXMark;
+ const SwTOXMark* pMin = &rCurTOXMark;
+
+ const SwTOXType* pType = rCurTOXMark.GetTOXType();
+ SwTOXMarks aMarks;
+ pType->CollectTextMarks(aMarks);
+
+ for(SwTOXMark* pTOXMark : aMarks)
+ {
+ if ( pTOXMark == &rCurTOXMark )
+ continue;
+
+ pMark = pTOXMark->GetTextTOXMark();
+ if (!pMark)
+ continue;
+
+ SwTextNode const*const pTOXSrc = pMark->GetpTextNd();
+ if (!pTOXSrc)
+ continue;
+
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame* pCFrame = pTOXSrc->getLayoutFrame(
+ getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp);
+ if (!pCFrame)
+ continue;
+
+ if ( bInReadOnly || !pCFrame->IsProtected() )
+ {
+ CompareNodeContent aAbsNew( pTOXSrc->GetIndex(), pMark->GetStart() );
+ switch( eDir )
+ {
+ // The following (a bit more complicated) statements make it
+ // possible to also travel across Entries on the same (!)
+ // position. If someone has time, please feel free to optimize.
+ case TOX_SAME_PRV:
+ if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
+ break;
+ [[fallthrough]];
+ case TOX_PRV:
+ if ( (aAbsNew < aAbsIdx && aAbsNew > aPrevPos) ||
+ (aAbsIdx == aAbsNew &&
+ (reinterpret_cast<sal_uLong>(&rCurTOXMark) > reinterpret_cast<sal_uLong>(pTOXMark) &&
+ (!pNew || aPrevPos < aAbsIdx || reinterpret_cast<sal_uLong>(pNew) < reinterpret_cast<sal_uLong>(pTOXMark) ) )) ||
+ (aPrevPos == aAbsNew && aAbsIdx != aAbsNew &&
+ reinterpret_cast<sal_uLong>(pTOXMark) > reinterpret_cast<sal_uLong>(pNew)) )
+ {
+ pNew = pTOXMark;
+ aPrevPos = aAbsNew;
+ if ( aAbsNew >= aMax )
+ {
+ aMax = aAbsNew;
+ pMax = pTOXMark;
+ }
+ }
+ break;
+
+ case TOX_SAME_NXT:
+ if (pTOXMark->GetText(nullptr) != rCurTOXMark.GetText(nullptr))
+ break;
+ [[fallthrough]];
+ case TOX_NXT:
+ if ( (aAbsNew > aAbsIdx && aAbsNew < aNextPos) ||
+ (aAbsIdx == aAbsNew &&
+ (reinterpret_cast<sal_uLong>(&rCurTOXMark) < reinterpret_cast<sal_uLong>(pTOXMark) &&
+ (!pNew || aNextPos > aAbsIdx || reinterpret_cast<sal_uLong>(pNew) > reinterpret_cast<sal_uLong>(pTOXMark)) )) ||
+ (aNextPos == aAbsNew && aAbsIdx != aAbsNew &&
+ reinterpret_cast<sal_uLong>(pTOXMark) < reinterpret_cast<sal_uLong>(pNew)) )
+ {
+ pNew = pTOXMark;
+ aNextPos = aAbsNew;
+ if ( aAbsNew <= aMin )
+ {
+ aMin = aAbsNew;
+ pMin = pTOXMark;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // We couldn't find a successor
+ // Use minimum or maximum
+ if(!pNew)
+ {
+ switch(eDir)
+ {
+ case TOX_PRV:
+ case TOX_SAME_PRV:
+ pNew = pMax;
+ break;
+ case TOX_NXT:
+ case TOX_SAME_NXT:
+ pNew = pMin;
+ break;
+ default:
+ pNew = &rCurTOXMark;
+ }
+ }
+ return *pNew;
+}
+
+SwTOXBaseSection* SwDoc::InsertTableOf( const SwPosition& rPos,
+ const SwTOXBase& rTOX,
+ const SfxItemSet* pSet,
+ bool bExpand,
+ SwRootFrame const*const pLayout)
+{
+ SwPaM aPam( rPos );
+ return InsertTableOf( aPam, rTOX, pSet, bExpand, pLayout );
+}
+
+SwTOXBaseSection* SwDoc::InsertTableOf( const SwPaM& aPam,
+ const SwTOXBase& rTOX,
+ const SfxItemSet* pSet,
+ bool bExpand,
+ SwRootFrame const*const pLayout )
+{
+ assert(!bExpand || pLayout != nullptr);
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::INSTOX, nullptr );
+
+ OUString sSectNm = GetUniqueTOXBaseName( *rTOX.GetTOXType(), rTOX.GetTOXName() );
+ SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
+
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode> const tmp(
+ &rTOX,
+ pLayout && pLayout->IsHideRedlines()
+ ? sw::RedlineMode::Hidden
+ : sw::RedlineMode::Shown,
+ pLayout ? pLayout->GetFieldmarkMode() : sw::FieldmarkMode::ShowBoth);
+ SwTOXBaseSection *const pNewSection = dynamic_cast<SwTOXBaseSection *>(
+ InsertSwSection(aPam, aSectionData, & tmp, pSet, false));
+ if (pNewSection)
+ {
+ SwSectionNode *const pSectNd = pNewSection->GetFormat()->GetSectionNode();
+ pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
+
+ if( bExpand )
+ {
+ // add value for 2nd parameter = true to
+ // indicate, that a creation of a new table of content has to be performed.
+ // Value of 1st parameter = default value.
+ pNewSection->Update( nullptr, pLayout, true );
+ }
+ else if( rTOX.GetTitle().getLength()==1 && IsInReading() )
+ // insert title of TOX
+ {
+ // then insert the headline section
+ SwNodeIndex aIdx( *pSectNd, +1 );
+
+ SwTextNode* pHeadNd = GetNodes().MakeTextNode( aIdx,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+
+ SwSectionData headerData( SectionType::ToxHeader, pNewSection->GetTOXName()+"_Head" );
+
+ SwNodeIndex aStt( *pHeadNd ); --aIdx;
+ SwSectionFormat* pSectFormat = MakeSectionFormat();
+ GetNodes().InsertTextSection(
+ aStt, *pSectFormat, headerData, nullptr, &aIdx, true, false);
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::INSTOX, nullptr );
+
+ return pNewSection;
+}
+
+void SwDoc::InsertTableOf( SwNodeOffset nSttNd, SwNodeOffset nEndNd,
+ const SwTOXBase& rTOX,
+ const SfxItemSet* pSet )
+{
+ // check for recursive TOX
+ SwNode* pNd = GetNodes()[ nSttNd ];
+ SwSectionNode* pSectNd = pNd->FindSectionNode();
+ while( pSectNd )
+ {
+ SectionType eT = pSectNd->GetSection().GetType();
+ if( SectionType::ToxHeader == eT || SectionType::ToxContent == eT )
+ return;
+ pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
+ }
+
+ const OUString sSectNm = GetUniqueTOXBaseName(*rTOX.GetTOXType(), rTOX.GetTOXName());
+
+ SwSectionData aSectionData( SectionType::ToxContent, sSectNm );
+
+ SwNodeIndex aStt( GetNodes(), nSttNd ), aEnd( GetNodes(), nEndNd );
+ SwSectionFormat* pFormat = MakeSectionFormat();
+ if(pSet)
+ pFormat->SetFormatAttr(*pSet);
+
+ SwSectionNode *const pNewSectionNode =
+ GetNodes().InsertTextSection(aStt, *pFormat, aSectionData, &rTOX, &aEnd);
+ if (!pNewSectionNode)
+ {
+ DelSectionFormat( pFormat );
+ return;
+ }
+
+ SwTOXBaseSection *const pNewSection(
+ dynamic_cast<SwTOXBaseSection*>(& pNewSectionNode->GetSection()));
+ if (pNewSection)
+ pNewSection->SetTOXName(sSectNm); // rTOX may have had no name...
+}
+
+/// Get current table of contents
+SwTOXBase* SwDoc::GetCurTOX( const SwPosition& rPos )
+{
+ SwNode& rNd = rPos.nNode.GetNode();
+ SwSectionNode* pSectNd = rNd.FindSectionNode();
+ while( pSectNd )
+ {
+ SectionType eT = pSectNd->GetSection().GetType();
+ if( SectionType::ToxContent == eT )
+ {
+ assert( dynamic_cast< const SwTOXBaseSection *>( &pSectNd->GetSection()) &&
+ "no TOXBaseSection!" );
+ SwTOXBaseSection& rTOXSect = static_cast<SwTOXBaseSection&>(
+ pSectNd->GetSection());
+ return &rTOXSect;
+ }
+ pSectNd = pSectNd->StartOfSectionNode()->FindSectionNode();
+ }
+ return nullptr;
+}
+
+const SwAttrSet& SwDoc::GetTOXBaseAttrSet(const SwTOXBase& rTOXBase)
+{
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+ const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
+ SwSectionFormat const * pFormat = rTOXSect.GetFormat();
+ OSL_ENSURE( pFormat, "invalid TOXBaseSection!" );
+ return pFormat->GetAttrSet();
+}
+
+const SwTOXBase* SwDoc::GetDefaultTOXBase( TOXTypes eTyp, bool bCreate )
+{
+ std::unique_ptr<SwTOXBase>* prBase = nullptr;
+ switch(eTyp)
+ {
+ case TOX_CONTENT: prBase = &mpDefTOXBases->pContBase; break;
+ case TOX_INDEX: prBase = &mpDefTOXBases->pIdxBase; break;
+ case TOX_USER: prBase = &mpDefTOXBases->pUserBase; break;
+ case TOX_TABLES: prBase = &mpDefTOXBases->pTableBase; break;
+ case TOX_OBJECTS: prBase = &mpDefTOXBases->pObjBase; break;
+ case TOX_ILLUSTRATIONS: prBase = &mpDefTOXBases->pIllBase; break;
+ case TOX_AUTHORITIES: prBase = &mpDefTOXBases->pAuthBase; break;
+ case TOX_BIBLIOGRAPHY: prBase = &mpDefTOXBases->pBiblioBase; break;
+ case TOX_CITATION: /** TODO */break;
+ }
+ if (!prBase)
+ return nullptr;
+ if(!(*prBase) && bCreate)
+ {
+ SwForm aForm(eTyp);
+ const SwTOXType* pType = GetTOXType(eTyp, 0);
+ prBase->reset(new SwTOXBase(pType, aForm, SwTOXElement::NONE, pType->GetTypeName()));
+ }
+ return prBase->get();
+}
+
+void SwDoc::SetDefaultTOXBase(const SwTOXBase& rBase)
+{
+ std::unique_ptr<SwTOXBase>* prBase = nullptr;
+ switch(rBase.GetType())
+ {
+ case TOX_CONTENT: prBase = &mpDefTOXBases->pContBase; break;
+ case TOX_INDEX: prBase = &mpDefTOXBases->pIdxBase; break;
+ case TOX_USER: prBase = &mpDefTOXBases->pUserBase; break;
+ case TOX_TABLES: prBase = &mpDefTOXBases->pTableBase; break;
+ case TOX_OBJECTS: prBase = &mpDefTOXBases->pObjBase; break;
+ case TOX_ILLUSTRATIONS: prBase = &mpDefTOXBases->pIllBase; break;
+ case TOX_AUTHORITIES: prBase = &mpDefTOXBases->pAuthBase; break;
+ case TOX_BIBLIOGRAPHY: prBase = &mpDefTOXBases->pBiblioBase; break;
+ case TOX_CITATION: /** TODO */break;
+ }
+ if (!prBase)
+ return;
+ prBase->reset(new SwTOXBase(rBase));
+}
+
+/// Delete table of contents
+bool SwDoc::DeleteTOX( const SwTOXBase& rTOXBase, bool bDelNodes )
+{
+ // We only delete the TOX, not the Nodes
+ bool bRet = false;
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+
+ const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
+ SwSectionFormat const * pFormat = rTOXSect.GetFormat();
+ /* Save the start node of the TOX' section. */
+ SwSectionNode const * pMyNode = pFormat ? pFormat->GetSectionNode() : nullptr;
+ if (pMyNode)
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::CLEARTOXRANGE, nullptr );
+
+ /* Save start node of section's surrounding. */
+ SwNode const * pStartNd = pMyNode->StartOfSectionNode();
+
+ /* Look for the point where to move the cursors in the area to
+ delete to. This is done by first searching forward from the
+ end of the TOX' section. If no content node is found behind
+ the TOX one is searched before it. If this is not
+ successful, too, insert new text node behind the end of
+ the TOX' section. The cursors from the TOX' section will be
+ moved to the content node found or the new text node. */
+
+ /* Set PaM to end of TOX' section and search following content node.
+ aSearchPam will contain the point where to move the cursors
+ to. */
+ SwPaM aSearchPam(*pMyNode->EndOfSectionNode());
+ SwPosition aEndPos(*pStartNd->EndOfSectionNode());
+ if (! aSearchPam.Move() /* no content node found */
+ || *aSearchPam.GetPoint() >= aEndPos /* content node found
+ outside surrounding */
+ )
+ {
+ /* Set PaM to beginning of TOX' section and search previous
+ content node */
+ SwPaM aTmpPam(*pMyNode);
+ aSearchPam = aTmpPam;
+ SwPosition aStartPos(*pStartNd);
+
+ if ( ! aSearchPam.Move(fnMoveBackward) /* no content node found */
+ || *aSearchPam.GetPoint() <= aStartPos /* content node
+ found outside
+ surrounding */
+ )
+ {
+ /* There is no content node in the surrounding of
+ TOX'. Append text node behind TOX' section. */
+
+ SwPosition aInsPos(*pMyNode->EndOfSectionNode());
+ getIDocumentContentOperations().AppendTextNode(aInsPos);
+
+ SwPaM aTmpPam1(aInsPos);
+ aSearchPam = aTmpPam1;
+ }
+ }
+
+ /* PaM containing the TOX. */
+ SwPaM aPam(*pMyNode->EndOfSectionNode(), *pMyNode);
+
+ /* Move cursors contained in TOX to the above calculated point. */
+ PaMCorrAbs(aPam, *aSearchPam.GetPoint());
+
+ if( !bDelNodes )
+ {
+ SwSections aArr( 0 );
+ pFormat->GetChildSections( aArr, SectionSort::Not, false );
+ for( const auto pSect : aArr )
+ {
+ if( SectionType::ToxHeader == pSect->GetType() )
+ {
+ DelSectionFormat( pSect->GetFormat(), bDelNodes );
+ }
+ }
+ }
+
+ DelSectionFormat( const_cast<SwSectionFormat *>(pFormat), bDelNodes );
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::CLEARTOXRANGE, nullptr );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+/// Manage table of content types
+sal_uInt16 SwDoc::GetTOXTypeCount(TOXTypes eTyp) const
+{
+ sal_uInt16 nCnt = 0;
+ for( auto const & pTOXType : *mpTOXTypes )
+ if( eTyp == pTOXType->GetType() )
+ ++nCnt;
+ return nCnt;
+}
+
+const SwTOXType* SwDoc::GetTOXType( TOXTypes eTyp, sal_uInt16 nId ) const
+{
+ sal_uInt16 nCnt = 0;
+ for( auto const & pTOXType : *mpTOXTypes )
+ if( eTyp == pTOXType->GetType() && nCnt++ == nId )
+ return pTOXType.get();
+ return nullptr;
+}
+
+const SwTOXType* SwDoc::InsertTOXType( const SwTOXType& rTyp )
+{
+ SwTOXType * pNew = new SwTOXType(rTyp);
+ mpTOXTypes->emplace_back( pNew );
+ return pNew;
+}
+
+OUString SwDoc::GetUniqueTOXBaseName( const SwTOXType& rType,
+ const OUString& sChkStr ) const
+{
+ if( IsInMailMerge())
+ {
+ OUString newName = "MailMergeTOX"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpSectionFormatTable->size() + 1 );
+ if( !sChkStr.isEmpty())
+ newName += sChkStr;
+ return newName;
+ }
+
+ bool bUseChkStr = !sChkStr.isEmpty();
+ const OUString& aName( rType.GetTypeName() );
+ const sal_Int32 nNmLen = aName.getLength();
+
+ SwSectionFormats::size_type nNum = 0;
+ const SwSectionFormats::size_type nFlagSize = ( mpSectionFormatTable->size() / 8 ) +2;
+ std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ for( auto pSectionFormat : *mpSectionFormatTable )
+ {
+ const SwSectionNode *pSectNd = pSectionFormat->GetSectionNode();
+ if ( !pSectNd )
+ continue;
+
+ const SwSection& rSect = pSectNd->GetSection();
+ if (rSect.GetType()==SectionType::ToxContent)
+ {
+ const OUString& rNm = rSect.GetSectionName();
+ if ( rNm.startsWith(aName) )
+ {
+ // Calculate number and set the Flag
+ nNum = o3tl::toInt32(rNm.subView( nNmLen ));
+ if( nNum-- && nNum < mpSectionFormatTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ if ( bUseChkStr && sChkStr==rNm )
+ bUseChkStr = false;
+ }
+ }
+
+ if( !bUseChkStr )
+ {
+ // All Numbers have been flagged accordingly, so get the right Number
+ nNum = mpSectionFormatTable->size();
+ for( SwSectionFormats::size_type n = 0; n < nFlagSize; ++n )
+ {
+ sal_uInt8 nTmp = pSetFlags[ n ];
+ if( nTmp != 0xff )
+ {
+ // so get the Number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+ }
+ if ( bUseChkStr )
+ return sChkStr;
+ return aName + OUString::number( ++nNum );
+}
+
+bool SwDoc::SetTOXBaseName(const SwTOXBase& rTOXBase, const OUString& rName)
+{
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+ SwTOXBaseSection* pTOX = const_cast<SwTOXBaseSection*>(static_cast<const SwTOXBaseSection*>(&rTOXBase));
+
+ if (GetUniqueTOXBaseName(*rTOXBase.GetTOXType(), rName) == rName)
+ {
+ pTOX->SetTOXName(rName);
+ pTOX->SetSectionName(rName);
+ getIDocumentState().SetModified();
+ return true;
+ }
+ return false;
+}
+
+static const SwTextNode* lcl_FindChapterNode( const SwNode& rNd,
+ SwRootFrame const*const pLayout, sal_uInt8 const nLvl = 0 )
+{
+ const SwNode* pNd = &rNd;
+ if( pNd->GetNodes().GetEndOfExtras().GetIndex() > pNd->GetIndex() )
+ {
+ // then find the "Anchor" (Body) position
+ Point aPt;
+ SwNode2Layout aNode2Layout( *pNd, pNd->GetIndex() );
+ const SwFrame* pFrame = aNode2Layout.GetFrame( &aPt );
+
+ if( pFrame )
+ {
+ SwPosition aPos( *pNd );
+ pNd = GetBodyTextNode( pNd->GetDoc(), aPos, *pFrame );
+ OSL_ENSURE( pNd, "Where's the paragraph?" );
+ // tdf#151462 - search for outline node containing the current node
+ return pNd->FindOutlineNodeOfLevel(pNd->GetSectionLevel() - 1, pLayout);
+ }
+ }
+ return pNd ? pNd->FindOutlineNodeOfLevel(nLvl, pLayout) : nullptr;
+}
+
+static bool IsHeadingContained(const SwTextNode* pChptrNd, const SwNode& rNd)
+{
+ const SwNode* pNd = &rNd;
+ const SwOutlineNodes& rONds = pNd->GetNodes().GetOutLineNds();
+ bool bIsHeadingContained = false;
+ if (!rONds.empty())
+ {
+ bool bCheckFirst = false;
+ SwOutlineNodes::size_type nPos;
+
+ if (!rONds.Seek_Entry(const_cast<SwNode*>(pNd), &nPos))
+ {
+ if (nPos == 0)
+ bCheckFirst = true;
+ else
+ nPos--;
+ }
+
+ if (bCheckFirst)
+ {
+ const SwContentNode* pCNd = pNd->GetContentNode();
+
+ Point aPt(0, 0);
+ std::pair<Point, bool> const tmp(aPt, false);
+
+ const SwFrame* pChptrFrame = pChptrNd ? pChptrNd->getLayoutFrame(
+ pChptrNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ const SwPageFrame* pChptrPgFrame = pChptrFrame ? pChptrFrame->FindPageFrame() : nullptr;
+ const SwFrame* pNdFrame
+ = pCNd ? pCNd->getLayoutFrame(
+ pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)
+ : nullptr;
+
+ // Check if the one asking doesn't precede the page of the specified chapter note
+ bIsHeadingContained
+ = pNdFrame && pChptrPgFrame
+ && pChptrPgFrame->getFrameArea().Top() <= pNdFrame->getFrameArea().Top();
+ // Check if the one asking doesn't succeed the specified chapter note
+ if (bIsHeadingContained)
+ {
+ const SwNode* aChptrNd = pChptrNd;
+ if (!rONds.Seek_Entry(const_cast<SwNode*>(aChptrNd), &nPos) && nPos)
+ nPos--;
+ // Search for the next outline node with a larger level than the specified chapter node
+ while (nPos < rONds.size() - 1
+ && pChptrNd->GetAttrOutlineLevel()
+ < rONds[nPos + 1]->GetTextNode()->GetAttrOutlineLevel())
+ nPos++;
+ // If there exists such an outline node, check if the one asking doesn't succeed
+ // the specified chapter node
+ if (nPos < rONds.size() - 1) {
+ nPos++;
+ const auto aONdsTxtNd = rONds[nPos]->GetTextNode();
+ pChptrFrame = aONdsTxtNd->getLayoutFrame(
+ aONdsTxtNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr,
+ &tmp);
+ pChptrPgFrame = pChptrFrame ? pChptrFrame->FindPageFrame() : nullptr;
+ bIsHeadingContained
+ = pNdFrame && pChptrPgFrame
+ && pChptrPgFrame->getFrameArea().Top() >= pNdFrame->getFrameArea().Top();
+ }
+ }
+ }
+ else
+ {
+ // Search for the next outline node which lies not within the current chapter node
+ while (nPos > 0
+ && pChptrNd->GetAttrOutlineLevel()
+ < rONds[nPos]->GetTextNode()->GetAttrOutlineLevel())
+ nPos--;
+ bIsHeadingContained = pChptrNd == rONds[nPos]->GetTextNode();
+ }
+ }
+ else
+ {
+ // If there are no outline nodes, consider the heading contained,
+ // otherwise the _XDocumentIndex._update() test fails
+ bIsHeadingContained = true;
+ }
+ return bIsHeadingContained;
+}
+
+// Table of contents class
+SwTOXBaseSection::SwTOXBaseSection(SwTOXBase const& rBase, SwSectionFormat & rFormat)
+ : SwTOXBase( rBase )
+ , SwSection( SectionType::ToxContent, OUString(), rFormat )
+{
+ SetProtect( rBase.IsProtected() );
+ SetSectionName( GetTOXName() );
+}
+
+SwTOXBaseSection::~SwTOXBaseSection()
+{
+}
+
+bool SwTOXBaseSection::SetPosAtStartEnd( SwPosition& rPos ) const
+{
+ bool bRet = false;
+ const SwSectionNode* pSectNd = GetFormat()->GetSectionNode();
+ if( pSectNd )
+ {
+ rPos.nNode = *pSectNd;
+ SwContentNode* pCNd = pSectNd->GetDoc().GetNodes().GoNext( &rPos.nNode );
+ rPos.nContent.Assign( pCNd, 0 );
+ bRet = true;
+ }
+ return bRet;
+}
+
+/// Collect table of contents content
+void SwTOXBaseSection::Update(const SfxItemSet* pAttr,
+ SwRootFrame const*const pLayout,
+ const bool _bNewTOX)
+{
+ if (!GetFormat())
+ return;
+ SwSectionNode const*const pSectNd(GetFormat()->GetSectionNode());
+ if (nullptr == pSectNd ||
+ !pSectNd->GetNodes().IsDocNodes() ||
+ IsHiddenFlag() ||
+ (pLayout->HasMergedParas() && pSectNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden))
+ {
+ return;
+ }
+
+ if ( !mbKeepExpression )
+ {
+ maMSTOCExpression.clear();
+ }
+
+ SwDoc& rDoc = const_cast<SwDoc&>(pSectNd->GetDoc());
+
+ if (pAttr && GetFormat())
+ rDoc.ChgFormat(*GetFormat(), *pAttr);
+
+ // determine default page description, which will be used by the content nodes,
+ // if no appropriate one is found.
+ const SwPageDesc* pDefaultPageDesc;
+ {
+ pDefaultPageDesc =
+ pSectNd->GetSection().GetFormat()->GetPageDesc().GetPageDesc();
+ if ( !_bNewTOX && !pDefaultPageDesc )
+ {
+ // determine page description of table-of-content
+ SwNodeOffset nPgDescNdIdx = pSectNd->GetIndex() + 1;
+ SwNodeOffset* pPgDescNdIdx = &nPgDescNdIdx;
+ pDefaultPageDesc = pSectNd->FindPageDesc( pPgDescNdIdx );
+ if ( nPgDescNdIdx < pSectNd->GetIndex() )
+ {
+ pDefaultPageDesc = nullptr;
+ }
+ }
+ // consider end node of content section in the node array.
+ if ( !pDefaultPageDesc &&
+ ( pSectNd->EndOfSectionNode()->GetIndex() <
+ (pSectNd->GetNodes().GetEndOfContent().GetIndex() - 1) )
+ )
+ {
+ // determine page description of content after table-of-content
+ SwNodeIndex aIdx( *(pSectNd->EndOfSectionNode()) );
+ const SwContentNode* pNdAfterTOX = pSectNd->GetNodes().GoNext( &aIdx );
+ const SwAttrSet& aNdAttrSet = pNdAfterTOX->GetSwAttrSet();
+ const SvxBreak eBreak = aNdAttrSet.GetBreak().GetBreak();
+ if ( eBreak != SvxBreak::PageBefore && eBreak != SvxBreak::PageBoth )
+ {
+ pDefaultPageDesc = pNdAfterTOX->FindPageDesc();
+ }
+ }
+ // consider start node of content section in the node array.
+ if ( !pDefaultPageDesc &&
+ ( pSectNd->GetIndex() >
+ (pSectNd->GetNodes().GetEndOfContent().StartOfSectionIndex() + 1) )
+ )
+ {
+ // determine page description of content before table-of-content
+ SwNodeIndex aIdx( *pSectNd );
+ pDefaultPageDesc =
+ SwNodes::GoPrevious( &aIdx )->FindPageDesc();
+
+ }
+ if ( !pDefaultPageDesc )
+ {
+ // determine default page description
+ pDefaultPageDesc = &rDoc.GetPageDesc( 0 );
+ }
+ }
+
+ rDoc.getIDocumentState().SetModified();
+
+ // get current Language
+ SwTOXInternational aIntl( GetLanguage(),
+ TOX_INDEX == GetTOXType()->GetType() ?
+ GetOptions() : SwTOIOptions::NONE,
+ GetSortAlgorithm() );
+
+ m_aSortArr.clear();
+
+ // find the first layout node for this TOX, if it only find the content
+ // in his own chapter
+ const SwSectionNode* pChapterSectNd = IsFromChapter() ? pSectNd->FindSectionNode() : nullptr;
+ const SwTextNode* pOwnChapterNode = pChapterSectNd
+ ? ::lcl_FindChapterNode( *pSectNd, pLayout, pChapterSectNd->GetSectionLevel() + 1 )
+ : nullptr;
+
+ SwNode2LayoutSaveUpperFrames aN2L(*pSectNd);
+ const_cast<SwSectionNode*>(pSectNd)->DelFrames();
+
+ // This would be a good time to update the Numbering
+ rDoc.UpdateNumRule();
+
+ if( GetCreateType() & SwTOXElement::Mark )
+ UpdateMarks( aIntl, pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::OutlineLevel )
+ UpdateOutline( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Template )
+ UpdateTemplate( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Ole ||
+ TOX_OBJECTS == SwTOXBase::GetType())
+ UpdateContent( SwTOXElement::Ole, pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Table ||
+ (TOX_TABLES == SwTOXBase::GetType() && IsFromObjectNames()) )
+ UpdateTable( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Graphic ||
+ (TOX_ILLUSTRATIONS == SwTOXBase::GetType() && IsFromObjectNames()))
+ UpdateContent( SwTOXElement::Graphic, pOwnChapterNode, pLayout );
+
+ if( !GetSequenceName().isEmpty() && !IsFromObjectNames() &&
+ (TOX_TABLES == SwTOXBase::GetType() ||
+ TOX_ILLUSTRATIONS == SwTOXBase::GetType() ) )
+ UpdateSequence( pOwnChapterNode, pLayout );
+
+ if( GetCreateType() & SwTOXElement::Frame )
+ UpdateContent( SwTOXElement::Frame, pOwnChapterNode, pLayout );
+
+ if(TOX_AUTHORITIES == SwTOXBase::GetType())
+ UpdateAuthorities( aIntl, pLayout );
+
+ // Insert AlphaDelimiters if needed (just for keywords)
+ if( TOX_INDEX == SwTOXBase::GetType() &&
+ ( GetOptions() & SwTOIOptions::AlphaDelimiter ) )
+ InsertAlphaDelimiter( aIntl );
+
+ // remove old content an insert one empty textnode (to hold the layout!)
+ SwTextNode* pFirstEmptyNd;
+
+ SwUndoUpdateIndex * pUndo(nullptr);
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSectNd, true, RedlineType::Any );
+
+ SwNodeIndex aSttIdx( *pSectNd, +1 );
+ SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() );
+ pFirstEmptyNd = rDoc.GetNodes().MakeTextNode( aEndIdx,
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+
+ {
+ // Task 70995 - save and restore PageDesc and Break Attributes
+ SwNodeIndex aNxtIdx( aSttIdx );
+ const SwContentNode* pCNd = aNxtIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = rDoc.GetNodes().GoNext( &aNxtIdx );
+ assert(pCNd != pFirstEmptyNd);
+ assert(pCNd->GetIndex() < pFirstEmptyNd->GetIndex());
+ if( pCNd->HasSwAttrSet() )
+ {
+ SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
+ aBrkSet.Put( *pCNd->GetpSwAttrSet() );
+ if( aBrkSet.Count() )
+ pFirstEmptyNd->SetAttr( aBrkSet );
+ }
+ }
+
+ if (rDoc.GetIDocumentUndoRedo().DoesUndo())
+ {
+ // note: this will first append a SwUndoDelSection from the ctor...
+ pUndo = new SwUndoUpdateIndex(*this);
+ // tdf#123313 insert Undo *after* all CrossRefBookmark Undos have
+ // been inserted by the Update*() functions
+ rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndoUpdateIndex>(pUndo));
+ }
+ else
+ {
+ --aEndIdx;
+ SwPosition aPos( aEndIdx, SwIndex( pFirstEmptyNd, 0 ));
+ SwDoc::CorrAbs( aSttIdx, aEndIdx, aPos, true );
+
+ // delete flys in whole range including start node which requires
+ // giving the node before start node as Mark parameter, hence -1.
+ // (flys must be deleted because the anchor nodes are removed)
+ DelFlyInRange( SwNodeIndex(aSttIdx, -1), aEndIdx );
+
+ rDoc.GetNodes().Delete( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() );
+ }
+ }
+
+ // insert title of TOX
+ if ( !GetTitle().isEmpty() )
+ {
+ // then insert the headline section
+ SwNodeIndex aIdx( *pSectNd, +1 );
+
+ SwTextNode* pHeadNd = rDoc.GetNodes().MakeTextNode( aIdx,
+ GetTextFormatColl( FORM_TITLE ) );
+ pHeadNd->InsertText( GetTitle(), SwIndex( pHeadNd ) );
+
+ SwSectionData headerData( SectionType::ToxHeader, GetTOXName()+"_Head" );
+
+ SwNodeIndex aStt( *pHeadNd ); --aIdx;
+ SwSectionFormat* pSectFormat = rDoc.MakeSectionFormat();
+ rDoc.GetNodes().InsertTextSection(
+ aStt, *pSectFormat, headerData, nullptr, &aIdx, true, false);
+
+ if (pUndo)
+ {
+ pUndo->TitleSectionInserted(*pSectFormat);
+ }
+ }
+
+ // Sort the List of all TOC Marks and TOC Sections
+ std::vector<SwTextFormatColl*> aCollArr( GetTOXForm().GetFormMax(), nullptr );
+ std::unordered_map<OUString, int> markURLs;
+ SwNodeIndex aInsPos( *pFirstEmptyNd, 1 );
+ for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
+ {
+ ::SetProgressState( 0, rDoc.GetDocShell() );
+
+ // Put the Text into the TOC
+ sal_uInt16 nLvl = m_aSortArr[ nCnt ]->GetLevel();
+ SwTextFormatColl* pColl = aCollArr[ nLvl ];
+ if( !pColl )
+ {
+ pColl = GetTextFormatColl( nLvl );
+ aCollArr[ nLvl ] = pColl;
+ }
+
+ // Generate: Set dynamic TabStops
+ SwTextNode* pTOXNd = rDoc.GetNodes().MakeTextNode( aInsPos , pColl );
+ m_aSortArr[ nCnt ]->pTOXNd = pTOXNd;
+
+ // Generate: Evaluate Form and insert the place holder for the
+ // page number. If it is a TOX_INDEX and the SwForm IsCommaSeparated()
+ // then a range of entries must be generated into one paragraph
+ size_t nRange = 1;
+ if(TOX_INDEX == SwTOXBase::GetType() &&
+ GetTOXForm().IsCommaSeparated() &&
+ m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
+ {
+ const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
+ const OUString& sPrimKey = rMark.GetPrimaryKey();
+ const OUString& sSecKey = rMark.GetSecondaryKey();
+ const SwTOXMark* pNextMark = nullptr;
+ while(m_aSortArr.size() > (nCnt + nRange) &&
+ m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX )
+ {
+ pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark());
+ if( !pNextMark ||
+ pNextMark->GetPrimaryKey() != sPrimKey ||
+ pNextMark->GetSecondaryKey() != sSecKey)
+ break;
+ nRange++;
+ }
+ }
+ // pass node index of table-of-content section and default page description
+ // to method <GenerateText(..)>.
+ ::SetProgressState( 0, rDoc.GetDocShell() );
+
+ std::shared_ptr<sw::ToxTabStopTokenHandler> tabStopTokenHandler =
+ std::make_shared<sw::DefaultToxTabStopTokenHandler>(
+ pSectNd->GetIndex(), *pDefaultPageDesc, GetTOXForm().IsRelTabPos(),
+ rDoc.GetDocumentSettingManager().get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) ?
+ sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_INDENT :
+ sw::DefaultToxTabStopTokenHandler::TABSTOPS_RELATIVE_TO_PAGE);
+ sw::ToxTextGenerator ttgn(GetTOXForm(), tabStopTokenHandler);
+ ttgn.GenerateText(GetFormat()->GetDoc(), markURLs, m_aSortArr, nCnt, nRange, pLayout);
+ nCnt += nRange - 1;
+ }
+
+ // delete the first dummy node and remove all Cursor into the previous node
+ aInsPos = *pFirstEmptyNd;
+ {
+ SwPaM aCorPam( *pFirstEmptyNd );
+ aCorPam.GetPoint()->nContent.Assign( pFirstEmptyNd, 0 );
+ if( !aCorPam.Move( fnMoveForward ) )
+ aCorPam.Move( fnMoveBackward );
+ SwNodeIndex aEndIdx( aInsPos, 1 );
+ SwDoc::CorrAbs( aInsPos, aEndIdx, *aCorPam.GetPoint(), true );
+
+ // Task 70995 - save and restore PageDesc and Break Attributes
+ if( pFirstEmptyNd->HasSwAttrSet() )
+ {
+ if( !GetTitle().isEmpty() )
+ aEndIdx = *pSectNd;
+ else
+ aEndIdx = *pFirstEmptyNd;
+ SwContentNode* pCNd = rDoc.GetNodes().GoNext( &aEndIdx );
+ if( pCNd ) // Robust against defect documents, e.g. i60336
+ pCNd->SetAttr( *pFirstEmptyNd->GetpSwAttrSet() );
+ }
+ }
+
+ // now create the new Frames
+ SwNodeOffset nIdx = pSectNd->GetIndex();
+ // don't delete if index is empty
+ if(nIdx + SwNodeOffset(2) < pSectNd->EndOfSectionIndex())
+ rDoc.GetNodes().Delete( aInsPos );
+
+ aN2L.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
+ for ( const auto& rpLayout : aAllLayouts )
+ {
+ SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(rpLayout->Lower()) );
+ }
+
+ SetProtect( SwTOXBase::IsProtected() );
+}
+
+void SwTOXBaseSection::InsertAlphaDelimiter( const SwTOXInternational& rIntl )
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ OUString sLastDeli;
+ size_t i = 0;
+ while( i < m_aSortArr.size() )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ sal_uInt16 nLevel = m_aSortArr[i]->GetLevel();
+
+ // Skip AlphaDelimiter
+ if( nLevel == FORM_ALPHA_DELIMITER )
+ continue;
+
+ const OUString sDeli = rIntl.GetIndexKey( m_aSortArr[i]->GetText(),
+ m_aSortArr[i]->GetLocale() );
+
+ // Do we already have a Delimiter?
+ if( !sDeli.isEmpty() && sLastDeli != sDeli )
+ {
+ // We skip all that are less than a small Blank (these are special characters)
+ if( ' ' <= sDeli[0] )
+ {
+ std::unique_ptr<SwTOXCustom> pCst(
+ MakeSwTOXSortTabBase<SwTOXCustom>(nullptr,
+ TextAndReading(sDeli, OUString()),
+ FORM_ALPHA_DELIMITER,
+ rIntl, m_aSortArr[i]->GetLocale() ));
+ m_aSortArr.insert( m_aSortArr.begin() + i, std::move(pCst));
+ i++;
+ }
+ sLastDeli = sDeli;
+ }
+
+ // Skip until we get to the same or a lower Level
+ do {
+ i++;
+ } while (i < m_aSortArr.size() && m_aSortArr[i]->GetLevel() > nLevel);
+ }
+}
+
+/// Evaluate Template
+SwTextFormatColl* SwTOXBaseSection::GetTextFormatColl( sal_uInt16 nLevel )
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ const OUString& rName = GetTOXForm().GetTemplate( nLevel );
+ SwTextFormatColl* pColl = !rName.isEmpty() ? pDoc->FindTextFormatCollByName(rName) :nullptr;
+ if( !pColl )
+ {
+ sal_uInt16 nPoolFormat = 0;
+ const TOXTypes eMyType = SwTOXBase::GetType();
+ switch( eMyType )
+ {
+ case TOX_INDEX: nPoolFormat = RES_POOLCOLL_TOX_IDXH; break;
+ case TOX_USER:
+ if( nLevel < 6 )
+ nPoolFormat = RES_POOLCOLL_TOX_USERH;
+ else
+ nPoolFormat = RES_POOLCOLL_TOX_USER6 - 6;
+ break;
+ case TOX_ILLUSTRATIONS: nPoolFormat = RES_POOLCOLL_TOX_ILLUSH; break;
+ case TOX_OBJECTS: nPoolFormat = RES_POOLCOLL_TOX_OBJECTH; break;
+ case TOX_TABLES: nPoolFormat = RES_POOLCOLL_TOX_TABLESH; break;
+ case TOX_AUTHORITIES:
+ case TOX_BIBLIOGRAPHY:
+ nPoolFormat = RES_POOLCOLL_TOX_AUTHORITIESH; break;
+ case TOX_CITATION: /** TODO */break;
+ case TOX_CONTENT:
+ // There's a jump in the ContentArea!
+ if( nLevel < 6 )
+ nPoolFormat = RES_POOLCOLL_TOX_CNTNTH;
+ else
+ nPoolFormat = RES_POOLCOLL_TOX_CNTNT6 - 6;
+ break;
+ }
+
+ if(eMyType == TOX_AUTHORITIES && nLevel)
+ nPoolFormat = nPoolFormat + 1;
+ else if(eMyType == TOX_INDEX && nLevel)
+ {
+ // pool: Level 1,2,3, Delimiter
+ // SwForm: Delimiter, Level 1,2,3
+ nPoolFormat += 1 == nLevel ? nLevel + 3 : nLevel - 1;
+ }
+ else
+ nPoolFormat = nPoolFormat + nLevel;
+ pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolFormat );
+ }
+ return pColl;
+}
+
+void SwTOXBaseSection::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (auto pFindHint = dynamic_cast<const sw::FindContentFrameHint*>(&rHint))
+ {
+ if(pFindHint->m_rpContentFrame)
+ return;
+ auto pSectFormat = GetFormat();
+ if(!pSectFormat)
+ return;
+ const SwSectionNode* pSectNd = pSectFormat->GetSectionNode();
+ if(!pSectNd)
+ return;
+ SwNodeIndex aIdx(*pSectNd, 1);
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if(!pCNd)
+ pCNd = pFindHint->m_rDoc.GetNodes().GoNext(&aIdx);
+ if(!pCNd)
+ return;
+ if(pCNd->EndOfSectionIndex() >= pSectNd->EndOfSectionIndex())
+ return;
+ pFindHint->m_rpContentFrame = pCNd->getLayoutFrame(&pFindHint->m_rLayout);
+ } else
+ SwTOXBase::SwClientNotify(rModify, rHint);
+}
+
+/// Create from Marks
+void SwTOXBaseSection::UpdateMarks(const SwTOXInternational& rIntl,
+ const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ const auto pType = static_cast<SwTOXType*>(SwTOXBase::GetRegisteredIn());
+ auto pShell = GetFormat()->GetDoc()->GetDocShell();
+ const TOXTypes eTOXTyp = GetTOXType()->GetType();
+ std::vector<std::reference_wrapper<SwTextTOXMark>> vMarks;
+ pType->CollectTextTOXMarksForLayout(vMarks, pLayout);
+ for(auto& rMark: vMarks)
+ {
+ ::SetProgressState(0, pShell);
+ auto& rNode = rMark.get().GetTextNode();
+ if(IsFromChapter() && !IsHeadingContained(pOwnChapterNode, rNode))
+ continue;
+ auto rTOXMark = rMark.get().GetTOXMark();
+ if(TOX_INDEX == eTOXTyp)
+ {
+ // index entry mark
+ assert(g_pBreakIt);
+ lang::Locale aLocale = g_pBreakIt->GetLocale(rNode.GetLang(rMark.get().GetStart()));
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_ENTRY, rIntl, aLocale));
+ if(GetOptions() & SwTOIOptions::KeyAsEntry && !rTOXMark.GetPrimaryKey().isEmpty())
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_PRIMARY_KEY, rIntl, aLocale));
+ if (!rTOXMark.GetSecondaryKey().isEmpty())
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXIndex>(pLayout, rNode, &rMark.get(), GetOptions(), FORM_SECONDARY_KEY, rIntl, aLocale));
+ }
+ }
+ }
+ else if(TOX_USER == eTOXTyp || rTOXMark.GetLevel() <= GetLevel())
+ { // table of content mark, also used for user marks
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXContent>(pLayout, rNode, &rMark.get(), rIntl));
+ }
+ }
+}
+
+/// Generate table of contents from outline
+void SwTOXBaseSection::UpdateOutline( const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwNodes& rNds = pDoc->GetNodes();
+
+ const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
+ for( auto pOutlineNode : rOutlNds )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+ SwTextNode* pTextNd = pOutlineNode->GetTextNode();
+ if( pTextNd && pTextNd->Len() && pTextNd->HasWriterListeners() &&
+ o3tl::make_unsigned( pTextNd->GetAttrOutlineLevel()) <= GetLevel() &&
+ pTextNd->getLayoutFrame(pLayout) &&
+ !pTextNd->IsHiddenByParaField() &&
+ !pTextNd->HasHiddenCharAttribute( true ) &&
+ (!pLayout || !pLayout->HasMergedParas()
+ || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
+ ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd) ))
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::OutlineLevel));
+ }
+ }
+}
+
+/// Generate table of contents from template areas
+void SwTOXBaseSection::UpdateTemplate(const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ for(sal_uInt16 i = 0; i < MAXLEVEL; i++)
+ {
+ const OUString sTmpStyleNames = GetStyleNames(i);
+ if (sTmpStyleNames.isEmpty())
+ continue;
+
+ sal_Int32 nIndex = 0;
+ while (nIndex >= 0)
+ {
+ SwTextFormatColl* pColl = pDoc->FindTextFormatCollByName(
+ sTmpStyleNames.getToken( 0, TOX_STYLE_DELIMITER, nIndex ));
+ //TODO: no outline Collections in content indexes if OutlineLevels are already included
+ if( !pColl ||
+ ( TOX_CONTENT == SwTOXBase::GetType() &&
+ GetCreateType() & SwTOXElement::OutlineLevel &&
+ pColl->IsAssignedToListLevelOfOutlineStyle()) )
+ continue;
+
+ SwIterator<SwTextNode,SwFormatColl> aIter( *pColl );
+ for( SwTextNode* pTextNd = aIter.First(); pTextNd; pTextNd = aIter.Next() )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ if (pTextNd->GetText().getLength() &&
+ pTextNd->getLayoutFrame(pLayout) &&
+ pTextNd->GetNodes().IsDocNodes() &&
+ (!pLayout || !pLayout->HasMergedParas()
+ || static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLayout))->GetTextNodeForParaProps() == pTextNd) &&
+ (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pTextNd)))
+ {
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXPara>(pLayout, *pTextNd, SwTOXElement::Template, i + 1));
+ }
+ }
+ }
+ }
+}
+
+/// Generate content from sequence fields
+void SwTOXBaseSection::UpdateSequence(const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwFieldType* pSeqField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, GetSequenceName(), false);
+ if(!pSeqField)
+ return;
+
+ std::vector<SwFormatField*> vFields;
+ pSeqField->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ SwTextNode& rTextNode = pTextField->GetTextNode();
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ if (rTextNode.GetText().getLength() &&
+ rTextNode.getLayoutFrame(pLayout) &&
+ ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, rTextNode))
+ && (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
+ {
+ const SwSetExpField& rSeqField = dynamic_cast<const SwSetExpField&>(*(pFormatField->GetField()));
+ const OUString sName = GetSequenceName()
+ + OUStringChar(cSequenceMarkSeparator)
+ + OUString::number( rSeqField.GetSeqNumber() );
+ std::unique_ptr<SwTOXPara> pNew(new SwTOXPara( rTextNode, SwTOXElement::Sequence, 1, sName ));
+ // set indexes if the number or the reference text are to be displayed
+ if( GetCaptionDisplay() == CAPTION_TEXT )
+ {
+ pNew->SetStartIndex(
+ SwGetExpField::GetReferenceTextPos( *pFormatField, *pDoc ));
+ }
+ else if(GetCaptionDisplay() == CAPTION_NUMBER)
+ {
+ pNew->SetEndIndex(pTextField->GetStart() + 1);
+ }
+ pNew->InitText(pLayout);
+ InsertSorted(std::move(pNew));
+ }
+ }
+}
+
+void SwTOXBaseSection::UpdateAuthorities(const SwTOXInternational& rIntl,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwFieldType* pAuthField = pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::TableOfAuthorities, OUString(), false);
+ if(!pAuthField)
+ return;
+
+ std::vector<SwFormatField*> vFields;
+ pAuthField->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ const auto pTextField = pFormatField->GetTextField();
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ if (rTextNode.GetText().getLength() &&
+ rTextNode.getLayoutFrame(pLayout) &&
+ (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(pDoc->getIDocumentRedlineAccess(), *pTextField)))
+ {
+ //#106485# the body node has to be used!
+ SwContentFrame *const pFrame = rTextNode.getLayoutFrame(pLayout);
+ SwPosition aFieldPos(rTextNode);
+ const SwTextNode* pTextNode = nullptr;
+ if(pFrame && !pFrame->IsInDocBody())
+ pTextNode = GetBodyTextNode( *pDoc, aFieldPos, *pFrame );
+ if(!pTextNode)
+ pTextNode = &rTextNode;
+
+ InsertSorted(MakeSwTOXSortTabBase<SwTOXAuthority>(pLayout, *pTextNode, *pFormatField, rIntl));
+ }
+ }
+}
+
+static SwTOOElements lcl_IsSOObject( const SvGlobalName& rFactoryNm )
+{
+ static const struct SoObjType {
+ SwTOOElements nFlag;
+ // GlobalNameId
+ struct {
+ sal_uInt32 n1;
+ sal_uInt16 n2, n3;
+ sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
+ } aGlNmIds[4];
+ } aArr[] = {
+ { SwTOOElements::Math,
+ { {SO3_SM_CLASSID_60},{SO3_SM_CLASSID_50},
+ {SO3_SM_CLASSID_40},{SO3_SM_CLASSID_30} } },
+ { SwTOOElements::Chart,
+ { {SO3_SCH_CLASSID_60},{SO3_SCH_CLASSID_50},
+ {SO3_SCH_CLASSID_40},{SO3_SCH_CLASSID_30} } },
+ { SwTOOElements::Calc,
+ { {SO3_SC_CLASSID_60},{SO3_SC_CLASSID_50},
+ {SO3_SC_CLASSID_40},{SO3_SC_CLASSID_30} } },
+ { SwTOOElements::DrawImpress,
+ { {SO3_SIMPRESS_CLASSID_60},{SO3_SIMPRESS_CLASSID_50},
+ {SO3_SIMPRESS_CLASSID_40},{SO3_SIMPRESS_CLASSID_30} } },
+ { SwTOOElements::DrawImpress,
+ { {SO3_SDRAW_CLASSID_60},{SO3_SDRAW_CLASSID_50} } }
+ };
+
+ for( SoObjType const & rArr : aArr )
+ for (auto & rId : rArr.aGlNmIds)
+ {
+ if( !rId.n1 )
+ break;
+ SvGlobalName aGlbNm( rId.n1, rId.n2, rId.n3,
+ rId.b8, rId.b9, rId.b10, rId.b11,
+ rId.b12, rId.b13, rId.b14, rId.b15 );
+ if( rFactoryNm == aGlbNm )
+ {
+ return rArr.nFlag;
+ }
+ }
+
+ return SwTOOElements::NONE;
+}
+
+void SwTOXBaseSection::UpdateContent( SwTOXElement eMyType,
+ const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwNodes& rNds = pDoc->GetNodes();
+ // on the 1st Node of the 1st Section
+ SwNodeOffset nIdx = rNds.GetEndOfAutotext().StartOfSectionIndex() + SwNodeOffset(2),
+ nEndIdx = rNds.GetEndOfAutotext().GetIndex();
+
+ while( nIdx < nEndIdx )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ SwNode* pNd = rNds[ nIdx ];
+ SwContentNode* pCNd = nullptr;
+ switch( eMyType )
+ {
+ case SwTOXElement::Frame:
+ if( !pNd->IsNoTextNode() )
+ {
+ pCNd = pNd->GetContentNode();
+ if( !pCNd )
+ {
+ SwNodeIndex aTmp( *pNd );
+ pCNd = rNds.GoNext( &aTmp );
+ }
+ }
+ break;
+ case SwTOXElement::Graphic:
+ if( pNd->IsGrfNode() )
+ pCNd = static_cast<SwContentNode*>(pNd);
+ break;
+ case SwTOXElement::Ole:
+ if( pNd->IsOLENode() )
+ {
+ bool bInclude = true;
+ if(TOX_OBJECTS == SwTOXBase::GetType())
+ {
+ SwOLENode* pOLENode = pNd->GetOLENode();
+ SwTOOElements nMyOLEOptions = GetOLEOptions();
+ SwOLEObj& rOLEObj = pOLENode->GetOLEObj();
+
+ if( rOLEObj.IsOleRef() ) // Not yet loaded
+ {
+ SvGlobalName aTmpName( rOLEObj.GetOleRef()->getClassID() );
+ SwTOOElements nObj = ::lcl_IsSOObject( aTmpName );
+ bInclude = ( (nMyOLEOptions & SwTOOElements::Other) && SwTOOElements::NONE == nObj )
+ || (nMyOLEOptions & nObj);
+ }
+ else
+ {
+ OSL_FAIL("OLE Object no loaded?");
+ bInclude = false;
+ }
+ }
+
+ if(bInclude)
+ pCNd = static_cast<SwContentNode*>(pNd);
+ }
+ break;
+ default: break;
+ }
+
+ if( pCNd )
+ {
+ // find node in body text
+ int nSetLevel = USHRT_MAX;
+
+ //#111105# tables of tables|illustrations|objects don't support hierarchies
+ if( IsLevelFromChapter() &&
+ TOX_TABLES != SwTOXBase::GetType() &&
+ TOX_ILLUSTRATIONS != SwTOXBase::GetType() &&
+ TOX_OBJECTS != SwTOXBase::GetType() )
+ {
+ const SwTextNode* pOutlNd = ::lcl_FindChapterNode( *pCNd,
+ pLayout, MAXLEVEL - 1);
+ if( pOutlNd )
+ {
+ if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
+ {
+ nSetLevel = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
+ }
+ }
+ }
+
+ if (pCNd->getLayoutFrame(pLayout)
+ && (!pLayout || !pLayout->HasMergedParas()
+ || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ && ( !IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pCNd)))
+ {
+ std::unique_ptr<SwTOXPara> pNew( MakeSwTOXSortTabBase<SwTOXPara>(
+ pLayout, *pCNd, eMyType,
+ ( USHRT_MAX != nSetLevel )
+ ? o3tl::narrowing<sal_uInt16>(nSetLevel)
+ : FORM_ALPHA_DELIMITER ) );
+ InsertSorted( std::move(pNew) );
+ }
+ }
+
+ nIdx = pNd->StartOfSectionNode()->EndOfSectionIndex() + SwNodeOffset(2); // 2 == End/Start Node
+ }
+}
+
+/// Collect table entries
+void SwTOXBaseSection::UpdateTable(const SwTextNode* pOwnChapterNode,
+ SwRootFrame const*const pLayout)
+{
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ SwNodes& rNds = pDoc->GetNodes();
+ const SwFrameFormats& rArr = *pDoc->GetTableFrameFormats();
+
+ for( auto pFrameFormat : rArr )
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ SwTable* pTmpTable = SwTable::FindTable( pFrameFormat );
+ SwTableBox* pFBox;
+ if( pTmpTable && nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) &&
+ pFBox->GetSttNd() && pFBox->GetSttNd()->GetNodes().IsDocNodes() )
+ {
+ const SwTableNode* pTableNd = pFBox->GetSttNd()->FindTableNode();
+ SwNodeIndex aContentIdx( *pTableNd, 1 );
+
+ SwContentNode* pCNd;
+ while( nullptr != ( pCNd = rNds.GoNext( &aContentIdx ) ) &&
+ aContentIdx.GetIndex() < pTableNd->EndOfSectionIndex() )
+ {
+ if (pCNd->getLayoutFrame(pLayout)
+ && (!pLayout || !pLayout->HasMergedParas()
+ || pCNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ && (!IsFromChapter() || IsHeadingContained(pOwnChapterNode, *pCNd)))
+ {
+ std::unique_ptr<SwTOXTable> pNew(new SwTOXTable( *pCNd ));
+ if( IsLevelFromChapter() && TOX_TABLES != SwTOXBase::GetType())
+ {
+ const SwTextNode* pOutlNd =
+ ::lcl_FindChapterNode(*pCNd, pLayout, MAXLEVEL - 1);
+ if( pOutlNd )
+ {
+ if( pOutlNd->GetTextColl()->IsAssignedToListLevelOfOutlineStyle())
+ {
+ const int nTmp = pOutlNd->GetTextColl()->GetAttrOutlineLevel();
+ pNew->SetLevel(o3tl::narrowing<sal_uInt16>(nTmp));
+ }
+ }
+ }
+ pNew->InitText(pLayout);
+ InsertSorted(std::move(pNew));
+ break;
+ }
+ }
+ }
+ }
+}
+
+/// Calculate PageNumber and insert after formatting
+void SwTOXBaseSection::UpdatePageNum()
+{
+ if( m_aSortArr.empty() )
+ return ;
+
+ // Insert the current PageNumber into the TOC
+ SwPageFrame* pCurrentPage = nullptr;
+ sal_uInt16 nPage = 0;
+ SwDoc* pDoc = GetFormat()->GetDoc();
+
+ SwTOXInternational aIntl( GetLanguage(),
+ TOX_INDEX == GetTOXType()->GetType() ?
+ GetOptions() : SwTOIOptions::NONE,
+ GetSortAlgorithm() );
+
+ for( size_t nCnt = 0; nCnt < m_aSortArr.size(); ++nCnt )
+ {
+ // Loop over all SourceNodes
+
+ // process run in lines
+ size_t nRange = 0;
+ if(GetTOXForm().IsCommaSeparated() &&
+ m_aSortArr[nCnt]->GetType() == TOX_SORT_INDEX)
+ {
+ const SwTOXMark& rMark = m_aSortArr[nCnt]->pTextMark->GetTOXMark();
+ const OUString& sPrimKey = rMark.GetPrimaryKey();
+ const OUString& sSecKey = rMark.GetSecondaryKey();
+ const SwTOXMark* pNextMark = nullptr;
+ while(m_aSortArr.size() > (nCnt + nRange)&&
+ m_aSortArr[nCnt + nRange]->GetType() == TOX_SORT_INDEX &&
+ nullptr != (pNextMark = &(m_aSortArr[nCnt + nRange]->pTextMark->GetTOXMark())) &&
+ pNextMark->GetPrimaryKey() == sPrimKey &&
+ pNextMark->GetSecondaryKey() == sSecKey)
+ nRange++;
+ }
+ else
+ nRange = 1;
+
+ for(size_t nRunInEntry = nCnt; nRunInEntry < nCnt + nRange; ++nRunInEntry)
+ {
+ std::vector<sal_uInt16> aNums; // the PageNumber
+ std::vector<SwPageDesc*> aDescs; // The PageDescriptors matching the PageNumbers
+ std::vector<sal_uInt16> aMainNums; // contains page numbers of main entries
+ SwTOXSortTabBase* pSortBase = m_aSortArr[nRunInEntry].get();
+ size_t nSize = pSortBase->aTOXSources.size();
+ for (size_t j = 0; j < nSize; ++j)
+ {
+ ::SetProgressState( 0, pDoc->GetDocShell() );
+
+ SwTOXSource& rTOXSource = pSortBase->aTOXSources[j];
+ if( rTOXSource.pNd )
+ {
+ SwContentFrame* pFrame = rTOXSource.pNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() );
+ OSL_ENSURE( pFrame || pDoc->IsUpdateTOX(), "TOX, no Frame found");
+ if( !pFrame )
+ continue;
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
+ {
+ // find the right one
+ SwTextFrame* pNext;
+ TextFrameIndex const nPos(static_cast<SwTextFrame*>(pFrame)
+ ->MapModelToView(static_cast<SwTextNode const*>(rTOXSource.pNd),
+ rTOXSource.nPos));
+ for (;;)
+ {
+ pNext = static_cast<SwTextFrame*>(pFrame->GetFollow());
+ if (!pNext || nPos < pNext->GetOffset())
+ break;
+ pFrame = pNext;
+ }
+ }
+
+ SwPageFrame* pTmpPage = pFrame->FindPageFrame();
+ if( pTmpPage != pCurrentPage )
+ {
+ nPage = pTmpPage->GetVirtPageNum();
+ pCurrentPage = pTmpPage;
+ }
+
+ // Insert as sorted
+ std::vector<sal_uInt16>::size_type i;
+ for( i = 0; i < aNums.size() && aNums[i] < nPage; ++i )
+ ;
+
+ if( i >= aNums.size() || aNums[ i ] != nPage )
+ {
+ aNums.insert(aNums.begin() + i, nPage);
+ aDescs.insert(aDescs.begin() + i, pCurrentPage->GetPageDesc() );
+ }
+ // is it a main entry?
+ if(TOX_SORT_INDEX == pSortBase->GetType() &&
+ rTOXSource.bMainEntry)
+ {
+ aMainNums.push_back(nPage);
+ }
+ }
+ }
+ // Insert the PageNumber into the TOC TextNode
+ const SwTOXSortTabBase* pBase = m_aSortArr[ nCnt ].get();
+ if(pBase->pTOXNd)
+ {
+ const SwTextNode* pTextNd = pBase->pTOXNd->GetTextNode();
+ OSL_ENSURE( pTextNd, "no TextNode, wrong TOC" );
+
+ UpdatePageNum_( const_cast<SwTextNode*>(pTextNd), aNums, aDescs, &aMainNums,
+ aIntl );
+ }
+ }
+ }
+ // Delete the mapping array after setting the right PageNumber
+ m_aSortArr.clear();
+}
+
+/// Replace the PageNumber place holders. Search for the page no. in the array
+/// of main entry page numbers.
+static bool lcl_HasMainEntry( const std::vector<sal_uInt16>* pMainEntryNums, sal_uInt16 nToFind )
+{
+ if (!pMainEntryNums)
+ return false;
+
+ for( auto nMainEntry : *pMainEntryNums )
+ if (nToFind == nMainEntry)
+ return true;
+ return false;
+}
+
+void SwTOXBaseSection::UpdatePageNum_( SwTextNode* pNd,
+ const std::vector<sal_uInt16>& rNums,
+ const std::vector<SwPageDesc*>& rDescs,
+ const std::vector<sal_uInt16>* pMainEntryNums,
+ const SwTOXInternational& rIntl )
+{
+ // collect starts end ends of main entry character style
+ std::optional< std::vector<sal_uInt16> > xCharStyleIdx;
+ if (pMainEntryNums)
+ xCharStyleIdx.emplace();
+
+ OUString sSrchStr
+ = OUStringChar(C_NUM_REPL) + SwTOXMark::S_PAGE_DELI + OUStringChar(C_NUM_REPL);
+ sal_Int32 nStartPos = pNd->GetText().indexOf(sSrchStr);
+ sSrchStr = OUStringChar(C_NUM_REPL) + OUStringChar(C_END_PAGE_NUM);
+ sal_Int32 nEndPos = pNd->GetText().indexOf(sSrchStr);
+
+ if (-1 == nEndPos || rNums.empty())
+ return;
+
+ if (-1 == nStartPos || nStartPos > nEndPos)
+ nStartPos = nEndPos;
+
+ sal_uInt16 nOld = rNums[0],
+ nBeg = nOld,
+ nCount = 0;
+ OUString aNumStr( rDescs[0]->GetNumType().GetNumStr( nBeg ) );
+ if( xCharStyleIdx && lcl_HasMainEntry( pMainEntryNums, nBeg ))
+ {
+ xCharStyleIdx->push_back( 0 );
+ }
+
+ // Delete place holder
+ SwIndex aPos(pNd, nStartPos);
+ SwCharFormat* pPageNoCharFormat = nullptr;
+ SwpHints* pHints = pNd->GetpSwpHints();
+ if(pHints)
+ for(size_t nHintIdx = 0; nHintIdx < pHints->Count(); ++nHintIdx)
+ {
+ const SwTextAttr* pAttr = pHints->Get(nHintIdx);
+ const sal_Int32 nTmpEnd = pAttr->End() ? *pAttr->End() : 0;
+ if( nStartPos >= pAttr->GetStart() &&
+ (nStartPos + 2) <= nTmpEnd &&
+ pAttr->Which() == RES_TXTATR_CHARFMT)
+ {
+ pPageNoCharFormat = pAttr->GetCharFormat().GetCharFormat();
+ break;
+ }
+ }
+ pNd->EraseText(aPos, nEndPos - nStartPos + 2);
+
+ std::vector<sal_uInt16>::size_type i;
+ for( i = 1; i < rNums.size(); ++i)
+ {
+ SvxNumberType aType( rDescs[i]->GetNumType() );
+ if( TOX_INDEX == SwTOXBase::GetType() )
+ { // Summarize for the following
+ // Add up all following
+ // break up if main entry starts or ends and
+ // insert a char style index
+ bool bMainEntryChanges = lcl_HasMainEntry(pMainEntryNums, nOld)
+ != lcl_HasMainEntry(pMainEntryNums, rNums[i]);
+
+ if(nOld == rNums[i]-1 && !bMainEntryChanges &&
+ (GetOptions() & (SwTOIOptions::FF|SwTOIOptions::Dash)))
+ nCount++;
+ else
+ {
+ // Flush for the following old values
+ if(GetOptions() & SwTOIOptions::FF)
+ {
+ if ( nCount >= 1 )
+ aNumStr += rIntl.GetFollowingText( nCount > 1 );
+ }
+ else if (nCount) //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
+ {
+ if (nCount == 1 )
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ else
+ aNumStr += "-";
+
+ aNumStr += aType.GetNumStr( nBeg + nCount );
+ }
+
+ // Create new String
+ nBeg = rNums[i];
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ //the change of the character style must apply after sPageDeli is appended
+ if (xCharStyleIdx && bMainEntryChanges)
+ {
+ xCharStyleIdx->push_back(aNumStr.getLength());
+ }
+ aNumStr += aType.GetNumStr( nBeg );
+ nCount = 0;
+ }
+ nOld = rNums[i];
+ }
+ else
+ { // Insert all Numbers
+ aNumStr += aType.GetNumStr( rNums[i] );
+ if (i+1 != rNums.size())
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ }
+ }
+ // Flush when ending and the following old values
+ if( TOX_INDEX == SwTOXBase::GetType() )
+ {
+ if(GetOptions() & SwTOIOptions::FF)
+ {
+ if( nCount >= 1 )
+ aNumStr += rIntl.GetFollowingText( nCount > 1 );
+ }
+ else
+ {
+ if(nCount >= 2)
+ aNumStr += "-";
+ else if(nCount == 1)
+ aNumStr += SwTOXMark::S_PAGE_DELI;
+ //#58127# If nCount == 0, then the only PageNumber is already in aNumStr!
+ if(nCount)
+ aNumStr += rDescs[i-1]->GetNumType().GetNumStr( nBeg+nCount );
+ }
+ }
+ pNd->InsertText( aNumStr, aPos, SwInsertFlags::EMPTYEXPAND | SwInsertFlags::FORCEHINTEXPAND );
+ if(pPageNoCharFormat)
+ {
+ SwFormatCharFormat aCharFormat( pPageNoCharFormat );
+ pNd->InsertItem(aCharFormat, nStartPos, nStartPos + aNumStr.getLength(), SetAttrMode::DONTEXPAND);
+ }
+
+ // The main entries should get their character style
+ if (!xCharStyleIdx || xCharStyleIdx->empty() || GetMainEntryCharStyle().isEmpty())
+ return;
+
+ // eventually the last index must me appended
+ if (xCharStyleIdx->size()&0x01)
+ xCharStyleIdx->push_back(aNumStr.getLength());
+
+ // search by name
+ SwDoc& rDoc = pNd->GetDoc();
+ sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( GetMainEntryCharStyle(), SwGetPoolIdFromName::ChrFmt );
+ SwCharFormat* pCharFormat = nullptr;
+ if(USHRT_MAX != nPoolId)
+ pCharFormat = rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool(nPoolId);
+ else
+ pCharFormat = rDoc.FindCharFormatByName( GetMainEntryCharStyle() );
+ if(!pCharFormat)
+ pCharFormat = rDoc.MakeCharFormat(GetMainEntryCharStyle(), nullptr);
+
+ // find the page numbers in aNumStr and set the character style
+ sal_Int32 nOffset = pNd->GetText().getLength() - aNumStr.getLength();
+ SwFormatCharFormat aCharFormat(pCharFormat);
+ for (size_t j = 0; j < xCharStyleIdx->size(); j += 2)
+ {
+ sal_Int32 nStartIdx = (*xCharStyleIdx)[j] + nOffset;
+ sal_Int32 nEndIdx = (*xCharStyleIdx)[j + 1] + nOffset;
+ pNd->InsertItem(aCharFormat, nStartIdx, nEndIdx, SetAttrMode::DONTEXPAND);
+ }
+}
+
+void SwTOXBaseSection::InsertSorted(std::unique_ptr<SwTOXSortTabBase> pNew)
+{
+ Range aRange(0, m_aSortArr.size());
+ if( TOX_INDEX == SwTOXBase::GetType() && pNew->pTextMark )
+ {
+ const SwTOXMark& rMark = pNew->pTextMark->GetTOXMark();
+ // Evaluate Key
+ // Calculate the range where to insert
+ if( !(GetOptions() & SwTOIOptions::KeyAsEntry) &&
+ !rMark.GetPrimaryKey().isEmpty() )
+ {
+ aRange = GetKeyRange( rMark.GetPrimaryKey(),
+ rMark.GetPrimaryKeyReading(),
+ *pNew, FORM_PRIMARY_KEY, aRange );
+
+ if( !rMark.GetSecondaryKey().isEmpty() )
+ aRange = GetKeyRange( rMark.GetSecondaryKey(),
+ rMark.GetSecondaryKeyReading(),
+ *pNew, FORM_SECONDARY_KEY, aRange );
+ }
+ }
+ // Search for identical entries and remove the trailing one
+ if(TOX_AUTHORITIES == SwTOXBase::GetType())
+ {
+ for(short i = static_cast<short>(aRange.Min()); i < static_cast<short>(aRange.Max()); ++i)
+ {
+ SwTOXSortTabBase* pOld = m_aSortArr[i].get();
+ if (pOld->equivalent(*pNew))
+ {
+ if (pOld->sort_lt(*pNew))
+ {
+ return;
+ }
+ else
+ {
+ // remove the old content
+ m_aSortArr.erase( m_aSortArr.begin() + i );
+ aRange.Max()--;
+ break;
+ }
+ }
+ }
+ }
+
+ // find position and insert
+ tools::Long i;
+
+ for( i = aRange.Min(); i < aRange.Max(); ++i)
+ { // Only check for same level
+ SwTOXSortTabBase* pOld = m_aSortArr[i].get();
+ if (pOld->equivalent(*pNew))
+ {
+ if(TOX_AUTHORITIES != SwTOXBase::GetType())
+ {
+ // Own entry for double entries or keywords
+ if( pOld->GetType() == TOX_SORT_CUSTOM &&
+ SwTOXSortTabBase::GetOptions() & SwTOIOptions::KeyAsEntry)
+ continue;
+
+ if(!(SwTOXSortTabBase::GetOptions() & SwTOIOptions::SameEntry))
+ { // Own entry
+ m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pNew));
+ return;
+ }
+ // If the own entry is already present, add it to the references list
+ pOld->aTOXSources.push_back(pNew->aTOXSources[0]);
+
+ return;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_FAIL("Bibliography entries cannot be found here");
+#endif
+ }
+ if (pNew->sort_lt(*pOld))
+ break;
+ }
+ // Skip SubLevel
+ while( TOX_INDEX == SwTOXBase::GetType() && i < aRange.Max() &&
+ m_aSortArr[i]->GetLevel() > pNew->GetLevel() )
+ i++;
+
+ // Insert at position i
+ m_aSortArr.insert(m_aSortArr.begin()+i, std::move(pNew));
+}
+
+/// Find Key Range and insert if possible
+Range SwTOXBaseSection::GetKeyRange(const OUString& rStr, const OUString& rStrReading,
+ const SwTOXSortTabBase& rNew,
+ sal_uInt16 nLevel, const Range& rRange )
+{
+ const SwTOXInternational& rIntl = *rNew.pTOXIntl;
+ TextAndReading aToCompare(rStr, rStrReading);
+
+ if( SwTOIOptions::InitialCaps & GetOptions() )
+ {
+ aToCompare.sText = rIntl.ToUpper( aToCompare.sText, 0 )
+ + aToCompare.sText.subView(1);
+ }
+
+ OSL_ENSURE(rRange.Min() >= 0 && rRange.Max() >= 0, "Min Max < 0");
+
+ const tools::Long nMin = rRange.Min();
+ const tools::Long nMax = rRange.Max();
+
+ tools::Long i;
+
+ for( i = nMin; i < nMax; ++i)
+ {
+ SwTOXSortTabBase* pBase = m_aSortArr[i].get();
+
+ if( rIntl.IsEqual( pBase->GetText(), pBase->GetLocale(),
+ aToCompare, rNew.GetLocale() ) &&
+ pBase->GetLevel() == nLevel )
+ break;
+ }
+ if(i == nMax)
+ { // If not already present, create and insert
+ std::unique_ptr<SwTOXCustom> pKey(MakeSwTOXSortTabBase<SwTOXCustom>(
+ nullptr, aToCompare, nLevel, rIntl, rNew.GetLocale() ));
+ for(i = nMin; i < nMax; ++i)
+ {
+ if (nLevel == m_aSortArr[i]->GetLevel() && pKey->sort_lt(*m_aSortArr[i]))
+ break;
+ }
+ m_aSortArr.insert(m_aSortArr.begin() + i, std::move(pKey));
+ }
+ const tools::Long nStart = i+1;
+ const tools::Long nEnd = m_aSortArr.size();
+
+ // Find end of range
+ for(i = nStart; i < nEnd; ++i)
+ {
+ if(m_aSortArr[i]->GetLevel() <= nLevel)
+ {
+ return Range(nStart, i);
+ }
+ }
+ return Range(nStart, nEnd);
+}
+
+bool SwTOXBase::IsTOXBaseInReadonly() const
+{
+ const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
+ if (!pSect || !pSect->GetFormat())
+ return false;
+
+ const SwSectionNode* pSectNode = pSect->GetFormat()->GetSectionNode();
+ if (!pSectNode)
+ return false;
+
+ const SwDocShell* pDocSh = pSectNode->GetDoc().GetDocShell();
+ if (!pDocSh)
+ return false;
+
+ if (pDocSh->IsReadOnly())
+ return true;
+
+ pSectNode = pSectNode->StartOfSectionNode()->FindSectionNode();
+ if (!pSectNode)
+ return false;
+
+ return pSectNode->GetSection().IsProtectFlag();
+}
+
+const SfxItemSet* SwTOXBase::GetAttrSet() const
+{
+ const SwTOXBaseSection *pSect = dynamic_cast<const SwTOXBaseSection*>(this);
+ if(pSect && pSect->GetFormat())
+ return &pSect->GetFormat()->GetAttrSet();
+ return nullptr;
+}
+
+void SwTOXBase::SetAttrSet( const SfxItemSet& rSet )
+{
+ SwTOXBaseSection *pSect = dynamic_cast<SwTOXBaseSection*>(this);
+ if( pSect && pSect->GetFormat() )
+ pSect->GetFormat()->SetFormatAttr( rSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/docxforms.cxx b/sw/source/core/doc/docxforms.cxx
new file mode 100644
index 000000000..5cd7792c9
--- /dev/null
+++ b/sw/source/core/doc/docxforms.cxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/frame/XModule.hpp>
+#include <com/sun/star/xforms/Model.hpp>
+#include <com/sun/star/xforms/XModel2.hpp>
+#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
+#include <com/sun/star/xforms/XForms.hpp>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/container/XIndexAccess.hpp>
+
+using namespace ::com::sun::star;
+
+using uno::Reference;
+using uno::UNO_QUERY;
+using uno::Any;
+using uno::Exception;
+using xforms::XModel2;
+using frame::XModule;
+using xforms::XFormsUIHelper1;
+using com::sun::star::container::XIndexAccess;
+
+
+bool SwDoc::isXForms() const
+{
+ return mxXForms.is();
+}
+
+void SwDoc::initXForms( bool bCreateDefaultModel )
+{
+ OSL_ENSURE( ! isXForms(), "please initialize only once" );
+
+ try
+ {
+ // create XForms components
+ mxXForms = xforms::XForms::create( comphelper::getProcessComponentContext() );
+
+ // change our module identifier, to be able to have a dedicated UI
+ Reference< XModule > xModule;
+ SwDocShell* pShell( GetDocShell() );
+ if ( pShell )
+ xModule.set(pShell->GetModel(), css::uno::UNO_QUERY);
+ OSL_ENSURE( xModule.is(), "SwDoc::initXForms: no XModule at the document!" );
+ if ( xModule.is() )
+ xModule->setIdentifier( "com.sun.star.xforms.XMLFormDocument" );
+
+ // create default model
+ if( bCreateDefaultModel && mxXForms.is() )
+ {
+ OUString sName("Model 1");
+ Reference<XModel2> xModel = xforms::Model::create( comphelper::getProcessComponentContext() );
+ xModel->setID( sName );
+ Reference<XFormsUIHelper1>( xModel, uno::UNO_QUERY_THROW )->newInstance(
+ "Instance 1",
+ OUString(), true );
+ xModel->initialize();
+ mxXForms->insertByName( sName, Any( xModel ) );
+ OSL_ENSURE( mxXForms->hasElements(), "can't create XForms model" );
+ }
+
+ OSL_ENSURE( isXForms(), "initialization failed" );
+ }
+ catch( const Exception& )
+ {
+ }
+}
+
+// #i113606#, to release the cyclic reference between XFormModel and bindings/submissions.
+void SwDoc::disposeXForms( )
+{
+ // get XForms models
+ if( !mxXForms.is() )
+ return;
+
+ // iterate over all models
+ const uno::Sequence<OUString> aNames = mxXForms->getElementNames();
+ for( const OUString& rName : aNames )
+ {
+ Reference< xforms::XModel > xModel(
+ mxXForms->getByName( rName ), UNO_QUERY );
+
+ if( xModel.is() )
+ {
+ // ask model for bindings
+ Reference< XIndexAccess > xBindings(
+ xModel->getBindings(), UNO_QUERY );
+
+ // Then release them one by one
+ int nCount = xBindings->getCount();
+ for( int i = nCount-1; i >= 0; i-- )
+ {
+ xModel->getBindings()->remove(xBindings->getByIndex( i ));
+ }
+
+ // ask model for Submissions
+ Reference< XIndexAccess > xSubmissions(
+ xModel->getSubmissions(), UNO_QUERY );
+
+ // Then release them one by one
+ nCount = xSubmissions->getCount();
+ for( int i = nCount-1; i >= 0; i-- )
+ {
+ xModel->getSubmissions()->remove(xSubmissions->getByIndex( i ));
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/extinput.cxx b/sw/source/core/doc/extinput.cxx
new file mode 100644
index 000000000..6ad02a0a4
--- /dev/null
+++ b/sw/source/core/doc/extinput.cxx
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <algorithm>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include <editeng/langitem.hxx>
+#include <osl/diagnose.h>
+#include <svl/languageoptions.hxx>
+#include <vcl/commandevent.hxx>
+
+#include <hintids.hxx>
+#include <extinput.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <index.hxx>
+#include <ndtxt.hxx>
+#include <swundo.hxx>
+
+using namespace ::com::sun::star;
+
+SwExtTextInput::SwExtTextInput( const SwPaM& rPam, Ring* pRing )
+ : SwPaM( *rPam.GetPoint(), static_cast<SwPaM*>(pRing) ),
+ m_eInputLanguage(LANGUAGE_DONTKNOW)
+{
+ m_bIsOverwriteCursor = false;
+ m_bInsText = true;
+}
+
+SwExtTextInput::~SwExtTextInput()
+{
+ SwDoc& rDoc = GetDoc();
+ if (rDoc.IsInDtor()) { return; /* #i58606# */ }
+
+ SwTextNode* pTNd = GetPoint()->nNode.GetNode().GetTextNode();
+ if( !pTNd )
+ return;
+
+ SwIndex& rIdx = GetPoint()->nContent;
+ sal_Int32 nSttCnt = rIdx.GetIndex();
+ sal_Int32 nEndCnt = GetMark()->nContent.GetIndex();
+ if( nEndCnt == nSttCnt )
+ return;
+
+ // Prevent IME edited text being grouped with non-IME edited text.
+ bool bKeepGroupUndo = rDoc.GetIDocumentUndoRedo().DoesGroupUndo();
+ bool bWasIME = rDoc.GetIDocumentUndoRedo().GetUndoActionCount() == 0 || rDoc.getIDocumentContentOperations().GetIME();
+ if (!bWasIME)
+ {
+ rDoc.GetIDocumentUndoRedo().DoGroupUndo(false);
+ }
+ rDoc.getIDocumentContentOperations().SetIME(true);
+ if( nEndCnt < nSttCnt )
+ {
+ std::swap(nSttCnt, nEndCnt);
+ }
+
+ // In order to get Undo/Redlining etc. working correctly,
+ // we need to go through the Doc interface
+ rIdx = nSttCnt;
+ const OUString sText( pTNd->GetText().copy(nSttCnt, nEndCnt - nSttCnt));
+ if( m_bIsOverwriteCursor && !m_sOverwriteText.isEmpty() )
+ {
+ const sal_Int32 nLen = sText.getLength();
+ const sal_Int32 nOWLen = m_sOverwriteText.getLength();
+ if( nLen > nOWLen )
+ {
+ rIdx += nOWLen;
+ pTNd->EraseText( rIdx, nLen - nOWLen );
+ rIdx = nSttCnt;
+ pTNd->ReplaceText( rIdx, nOWLen, m_sOverwriteText );
+ if( m_bInsText )
+ {
+ rIdx = nSttCnt;
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::OVERWRITE, nullptr );
+ rDoc.getIDocumentContentOperations().Overwrite( *this, sText.copy( 0, nOWLen ) );
+ rDoc.getIDocumentContentOperations().InsertString( *this, sText.copy( nOWLen ) );
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::OVERWRITE, nullptr );
+ }
+ }
+ else
+ {
+ pTNd->ReplaceText( rIdx, nLen, m_sOverwriteText.copy( 0, nLen ));
+ if( m_bInsText )
+ {
+ rIdx = nSttCnt;
+ rDoc.getIDocumentContentOperations().Overwrite( *this, sText );
+ }
+ }
+ }
+ else
+ {
+ // 1. Insert text at start position with EMPTYEXPAND to use correct formatting
+ // ABC<NEW><OLD>
+ // 2. Then remove old (not tracked) content
+ // ABC<NEW>
+
+ sal_Int32 nLenghtOfOldString = nEndCnt - nSttCnt;
+
+ if( m_bInsText )
+ {
+ rIdx = nSttCnt;
+ rDoc.getIDocumentContentOperations().InsertString( *this, sText, SwInsertFlags::EMPTYEXPAND );
+ }
+
+ pTNd->EraseText( rIdx, nLenghtOfOldString );
+ }
+ if (!bWasIME)
+ {
+ rDoc.GetIDocumentUndoRedo().DoGroupUndo(bKeepGroupUndo);
+ }
+ if (m_eInputLanguage == LANGUAGE_DONTKNOW)
+ return;
+
+ sal_uInt16 nWhich = RES_CHRATR_LANGUAGE;
+ sal_Int16 nScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage(m_eInputLanguage);
+ switch(nScriptType)
+ {
+ case i18n::ScriptType::ASIAN:
+ nWhich = RES_CHRATR_CJK_LANGUAGE; break;
+ case i18n::ScriptType::COMPLEX:
+ nWhich = RES_CHRATR_CTL_LANGUAGE; break;
+ }
+ // #i41974# Only set language attribute for CJK/CTL scripts.
+ if (RES_CHRATR_LANGUAGE != nWhich && pTNd->GetLang( nSttCnt, nEndCnt-nSttCnt, nScriptType) != m_eInputLanguage)
+ {
+ SvxLanguageItem aLangItem( m_eInputLanguage, nWhich );
+ rIdx = nSttCnt;
+ GetMark()->nContent = nEndCnt;
+ rDoc.getIDocumentContentOperations().InsertPoolItem(*this, aLangItem );
+ }
+}
+
+void SwExtTextInput::SetInputData( const CommandExtTextInputData& rData )
+{
+ SwTextNode* pTNd = GetPoint()->nNode.GetNode().GetTextNode();
+ if( !pTNd )
+ return;
+
+ sal_Int32 nSttCnt = Start()->nContent.GetIndex();
+ sal_Int32 nEndCnt = End()->nContent.GetIndex();
+
+ SwIndex aIdx( pTNd, nSttCnt );
+ const OUString& rNewStr = rData.GetText();
+
+ if( m_bIsOverwriteCursor && !m_sOverwriteText.isEmpty() )
+ {
+ sal_Int32 nReplace = nEndCnt - nSttCnt;
+ const sal_Int32 nNewLen = rNewStr.getLength();
+ if( nNewLen < nReplace )
+ {
+ // We have to insert some characters from the saved original text
+ nReplace -= nNewLen;
+ aIdx += nNewLen;
+ pTNd->ReplaceText( aIdx, nReplace,
+ m_sOverwriteText.copy( nNewLen, nReplace ));
+ aIdx = nSttCnt;
+ nReplace = nNewLen;
+ }
+ else
+ {
+ const sal_Int32 nOWLen = m_sOverwriteText.getLength();
+ if( nOWLen < nReplace )
+ {
+ aIdx += nOWLen;
+ pTNd->EraseText( aIdx, nReplace-nOWLen );
+ aIdx = nSttCnt;
+ nReplace = nOWLen;
+ }
+ else
+ {
+ nReplace = std::min(nOWLen, nNewLen);
+ }
+ }
+
+ pTNd->ReplaceText( aIdx, nReplace, rNewStr );
+ if( !HasMark() )
+ SetMark();
+ GetMark()->nContent = aIdx;
+ }
+ else
+ {
+ if( nSttCnt < nEndCnt )
+ {
+ pTNd->EraseText( aIdx, nEndCnt - nSttCnt );
+ }
+
+ // NOHINTEXPAND so we can use correct formatting in destructor when we finish composing
+ pTNd->InsertText( rNewStr, aIdx, SwInsertFlags::NOHINTEXPAND );
+ if( !HasMark() )
+ SetMark();
+ }
+
+ GetPoint()->nContent = nSttCnt;
+
+ m_aAttrs.clear();
+ if( rData.GetTextAttr() )
+ {
+ const ExtTextInputAttr *pAttrs = rData.GetTextAttr();
+ m_aAttrs.insert( m_aAttrs.begin(), pAttrs, pAttrs + rData.GetText().getLength() );
+ }
+}
+
+void SwExtTextInput::SetOverwriteCursor( bool bFlag )
+{
+ m_bIsOverwriteCursor = bFlag;
+ if (!m_bIsOverwriteCursor)
+ return;
+
+ const SwTextNode *const pTNd = GetPoint()->nNode.GetNode().GetTextNode();
+ if (!pTNd)
+ return;
+
+ const sal_Int32 nSttCnt = GetPoint()->nContent.GetIndex();
+ const sal_Int32 nEndCnt = GetMark()->nContent.GetIndex();
+ m_sOverwriteText = pTNd->GetText().copy( std::min(nSttCnt, nEndCnt) );
+ if( m_sOverwriteText.isEmpty() )
+ return;
+
+ const sal_Int32 nInPos = m_sOverwriteText.indexOf( CH_TXTATR_INWORD );
+ const sal_Int32 nBrkPos = m_sOverwriteText.indexOf( CH_TXTATR_BREAKWORD );
+
+ // Find the first attr found, if any.
+ sal_Int32 nPos = std::min(nInPos, nBrkPos);
+ if (nPos<0)
+ {
+ nPos = std::max(nInPos, nBrkPos);
+ }
+ if (nPos>=0)
+ {
+ m_sOverwriteText = m_sOverwriteText.copy( 0, nPos );
+ }
+}
+
+// The Doc interfaces
+
+SwExtTextInput* SwDoc::CreateExtTextInput( const SwPaM& rPam )
+{
+ SwExtTextInput* pNew = new SwExtTextInput( rPam, mpExtInputRing );
+ if( !mpExtInputRing )
+ mpExtInputRing = pNew;
+ pNew->SetMark();
+ return pNew;
+}
+
+void SwDoc::DeleteExtTextInput( SwExtTextInput* pDel )
+{
+ if( pDel == mpExtInputRing )
+ {
+ if( pDel->GetNext() != mpExtInputRing )
+ mpExtInputRing = pDel->GetNext();
+ else
+ mpExtInputRing = nullptr;
+ }
+ delete pDel;
+}
+
+SwExtTextInput* SwDoc::GetExtTextInput( const SwNode& rNd,
+ sal_Int32 nContentPos ) const
+{
+ SwExtTextInput* pRet = nullptr;
+ if( mpExtInputRing )
+ {
+ SwNodeOffset nNdIdx = rNd.GetIndex();
+ SwExtTextInput* pTmp = mpExtInputRing;
+ do {
+ SwNodeOffset nStartNode = pTmp->Start()->nNode.GetIndex(),
+ nEndNode = pTmp->End()->nNode.GetIndex();
+ sal_Int32 nStartCnt = pTmp->Start()->nContent.GetIndex();
+ sal_Int32 nEndCnt = pTmp->End()->nContent.GetIndex();
+
+ if( nStartNode <= nNdIdx && nNdIdx <= nEndNode &&
+ ( nContentPos<0 ||
+ ( nStartCnt <= nContentPos && nContentPos <= nEndCnt )))
+ {
+ pRet = pTmp;
+ break;
+ }
+ pTmp = pTmp->GetNext();
+ } while ( pTmp!=mpExtInputRing );
+ }
+ return pRet;
+}
+
+SwExtTextInput* SwDoc::GetExtTextInput() const
+{
+ OSL_ENSURE( !mpExtInputRing || !mpExtInputRing->IsMultiSelection(),
+ "more than one InputEngine available" );
+ return mpExtInputRing;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/fmtcol.cxx b/sw/source/core/doc/fmtcol.cxx
new file mode 100644
index 000000000..e020aab19
--- /dev/null
+++ b/sw/source/core/doc/fmtcol.cxx
@@ -0,0 +1,657 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <libxml/xmlwriter.h>
+
+#include <editeng/fhgtitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <osl/diagnose.h>
+#include <sal/macros.h>
+#include <svl/intitem.hxx>
+
+#include <calbck.hxx>
+#include <doc.hxx>
+#include <fmtcol.hxx>
+#include <fmtcolfunc.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <node.hxx>
+#include <numrule.hxx>
+#include <paratr.hxx>
+#include <swfntcch.hxx>
+
+namespace TextFormatCollFunc
+{
+ // #i71574#
+ void CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle(
+ SwFormat* pFormat,
+ const SwNumRuleItem* pNewNumRuleItem )
+ {
+ SwTextFormatColl* pTextFormatColl = dynamic_cast<SwTextFormatColl*>(pFormat);
+ if ( !pTextFormatColl )
+ {
+ OSL_FAIL( "<TextFormatCollFunc::CheckTextFormatCollFuncForDeletionOfAssignmentToOutlineStyle> - misuse of method - it's only for instances of <SwTextFormatColl>" );
+ return;
+ }
+
+ // #i73790#
+ if ( pTextFormatColl->StayAssignedToListLevelOfOutlineStyle() ||
+ !pTextFormatColl->IsAssignedToListLevelOfOutlineStyle() )
+ return;
+
+ if (!pNewNumRuleItem)
+ {
+ pNewNumRuleItem = pTextFormatColl->GetItemIfSet(RES_PARATR_NUMRULE, false);
+ }
+ if (pNewNumRuleItem)
+ {
+ const OUString& sNumRuleName = pNewNumRuleItem->GetValue();
+ if ( sNumRuleName.isEmpty() ||
+ sNumRuleName != pTextFormatColl->GetDoc()->GetOutlineNumRule()->GetName() )
+ {
+ // delete assignment of paragraph style to list level of outline style.
+ pTextFormatColl->DeleteAssignmentToListLevelOfOutlineStyle();
+ }
+ }
+ }
+
+ SwNumRule* GetNumRule( SwTextFormatColl& rTextFormatColl )
+ {
+ SwNumRule* pNumRule( nullptr );
+
+ const SwNumRuleItem* pNumRuleItem = rTextFormatColl.GetItemIfSet(RES_PARATR_NUMRULE, false);
+ if (pNumRuleItem)
+ {
+ const OUString& sNumRuleName = pNumRuleItem->GetValue();
+ if ( !sNumRuleName.isEmpty() )
+ {
+ pNumRule = rTextFormatColl.GetDoc()->FindNumRulePtr( sNumRuleName );
+ }
+ }
+
+ return pNumRule;
+ }
+
+ void AddToNumRule( SwTextFormatColl& rTextFormatColl )
+ {
+ SwNumRule* pNumRule = GetNumRule( rTextFormatColl );
+ if ( pNumRule )
+ {
+ pNumRule->AddParagraphStyle( rTextFormatColl );
+ }
+ }
+
+ void RemoveFromNumRule( SwTextFormatColl& rTextFormatColl )
+ {
+ SwNumRule* pNumRule = GetNumRule( rTextFormatColl );
+ if ( pNumRule )
+ {
+ pNumRule->RemoveParagraphStyle( rTextFormatColl );
+ }
+ }
+} // end of namespace TextFormatCollFunc
+
+SwTextFormatColl::~SwTextFormatColl()
+{
+ if(m_bInSwFntCache)
+ pSwFontCache->Delete( this );
+
+ if (GetDoc()->IsInDtor())
+ {
+ return;
+ }
+
+ for (const auto& pCharFormat : *GetDoc()->GetCharFormats())
+ {
+ if (pCharFormat->GetLinkedParaFormat() == this)
+ {
+ pCharFormat->SetLinkedParaFormat(nullptr);
+ }
+ }
+}
+void SwTextFormatColl::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(GetDoc()->IsInDtor())
+ {
+ SwFormatColl::SwClientNotify(rModify, rHint);
+ return;
+ }
+ bool bNewParent( false ); // #i66431# - adjust type of <bNewParent>
+ const SvxULSpaceItem *pNewULSpace = nullptr, *pOldULSpace = nullptr;
+ const SvxLRSpaceItem *pNewLRSpace = nullptr, *pOldLRSpace = nullptr;
+ const SvxFontHeightItem* aFontSizeArr[3] = {nullptr,nullptr,nullptr};
+ // #i70223#
+ const bool bAssignedToListLevelOfOutlineStyle(IsAssignedToListLevelOfOutlineStyle());
+ const SwNumRuleItem* pNewNumRuleItem( nullptr );
+
+ const SwAttrSetChg *pNewChgSet = nullptr, *pOldChgSet = nullptr;
+ const auto pOld = pLegacy->m_pOld;
+ const auto pNew = pLegacy->m_pNew;
+ switch( pLegacy->GetWhich() )
+ {
+ case RES_ATTRSET_CHG:
+ // Only recalculate if we're not the sender!
+ pNewChgSet = &pNew->StaticWhichCast(RES_ATTRSET_CHG);
+ pOldChgSet = &pOld->StaticWhichCast(RES_ATTRSET_CHG);
+ pNewLRSpace = pNewChgSet->GetChgSet()->GetItemIfSet( RES_LR_SPACE, false );
+ pNewULSpace = pNewChgSet->GetChgSet()->GetItemIfSet( RES_UL_SPACE, false );
+ aFontSizeArr[0] = pNewChgSet->GetChgSet()->GetItemIfSet( RES_CHRATR_FONTSIZE, false );
+ aFontSizeArr[1] = pNewChgSet->GetChgSet()->GetItemIfSet( RES_CHRATR_CJK_FONTSIZE, false );
+ aFontSizeArr[2] = pNewChgSet->GetChgSet()->GetItemIfSet( RES_CHRATR_CTL_FONTSIZE, false );
+ // #i70223#, #i84745#
+ // check, if attribute set is applied to this paragraph style
+ if ( bAssignedToListLevelOfOutlineStyle &&
+ pNewChgSet->GetTheChgdSet() == &GetAttrSet() )
+ {
+ pNewNumRuleItem = pNewChgSet->GetChgSet()->GetItemIfSet( RES_PARATR_NUMRULE, false );
+ }
+
+ break;
+
+ case RES_FMT_CHG:
+ if( GetAttrSet().GetParent() )
+ {
+ const SfxItemSet* pParent = GetAttrSet().GetParent();
+ pNewLRSpace = &pParent->Get( RES_LR_SPACE );
+ pNewULSpace = &pParent->Get( RES_UL_SPACE );
+ aFontSizeArr[0] = &pParent->Get( RES_CHRATR_FONTSIZE );
+ aFontSizeArr[1] = &pParent->Get( RES_CHRATR_CJK_FONTSIZE );
+ aFontSizeArr[2] = &pParent->Get( RES_CHRATR_CTL_FONTSIZE );
+ // #i66431# - modify has to be propagated, because of new parent format.
+ bNewParent = true;
+ }
+ break;
+
+ case RES_LR_SPACE:
+ pNewLRSpace = &pNew->StaticWhichCast(RES_LR_SPACE);
+ break;
+ case RES_UL_SPACE:
+ pNewULSpace = &pNew->StaticWhichCast(RES_UL_SPACE);
+ break;
+ case RES_CHRATR_FONTSIZE:
+ aFontSizeArr[0] = &pNew->StaticWhichCast(RES_CHRATR_CJK_FONTSIZE);
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ aFontSizeArr[1] = &pNew->StaticWhichCast(RES_CHRATR_CJK_FONTSIZE);
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ aFontSizeArr[2] = &pNew->StaticWhichCast(RES_CHRATR_CTL_FONTSIZE);
+ break;
+ // #i70223#
+ case RES_PARATR_NUMRULE:
+ if (bAssignedToListLevelOfOutlineStyle)
+ {
+ pNewNumRuleItem = &pNew->StaticWhichCast(RES_PARATR_NUMRULE);
+ }
+ break;
+ default:
+ break;
+ }
+
+ // #i70223#
+ if ( bAssignedToListLevelOfOutlineStyle && pNewNumRuleItem )
+ {
+ TextFormatCollFunc::CheckTextFormatCollForDeletionOfAssignmentToOutlineStyle(
+ this, pNewNumRuleItem );
+ }
+
+ bool bContinue = true;
+
+ // Check against the own attributes
+ if( pNewLRSpace && (pOldLRSpace = GetItemIfSet( RES_LR_SPACE, false)) )
+ {
+ if( pOldLRSpace != pNewLRSpace ) // Avoid recursion (SetAttr!)
+ {
+ bool bChg = false;
+ SvxLRSpaceItem aNew( *pOldLRSpace );
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropLeft() )
+ {
+ tools::Long nTmp = aNew.GetLeft(); // keep so that we can compare
+ aNew.SetLeft( pNewLRSpace->GetLeft(), aNew.GetPropLeft() );
+ bChg |= nTmp != aNew.GetLeft();
+ }
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropRight() )
+ {
+ tools::Long nTmp = aNew.GetRight(); // keep so that we can compare
+ aNew.SetRight( pNewLRSpace->GetRight(), aNew.GetPropRight() );
+ bChg |= nTmp != aNew.GetRight();
+ }
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropTextFirstLineOffset() )
+ {
+ short nTmp = aNew.GetTextFirstLineOffset(); // keep so that we can compare
+ aNew.SetTextFirstLineOffset( pNewLRSpace->GetTextFirstLineOffset(),
+ aNew.GetPropTextFirstLineOffset() );
+ bChg |= nTmp != aNew.GetTextFirstLineOffset();
+ }
+ if( bChg )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ }
+
+ if( pNewULSpace && (pOldULSpace = GetItemIfSet(RES_UL_SPACE, false)) &&
+ pOldULSpace != pNewULSpace ) // Avoid recursion (SetAttr!)
+ {
+ SvxULSpaceItem aNew( *pOldULSpace );
+ bool bChg = false;
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropUpper() )
+ {
+ sal_uInt16 nTmp = aNew.GetUpper(); // keep so that we can compare
+ aNew.SetUpper( pNewULSpace->GetUpper(), aNew.GetPropUpper() );
+ bChg |= nTmp != aNew.GetUpper();
+ }
+ // We had a relative value -> recalculate
+ if( 100 != aNew.GetPropLower() )
+ {
+ sal_uInt16 nTmp = aNew.GetLower(); // keep so that we can compare
+ aNew.SetLower( pNewULSpace->GetLower(), aNew.GetPropLower() );
+ bChg |= nTmp != aNew.GetLower();
+ }
+ if( bChg )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+
+ for( int nC = 0; nC < int(SAL_N_ELEMENTS(aFontSizeArr)); ++nC )
+ {
+ const SvxFontHeightItem *pFSize = aFontSizeArr[ nC ], *pOldFSize;
+ if( pFSize && (SfxItemState::SET == GetItemState(
+ pFSize->Which(), false, reinterpret_cast<const SfxPoolItem**>(&pOldFSize) )) &&
+ // Avoid recursion (SetAttr!)
+ pFSize != pOldFSize )
+ {
+ if( 100 == pOldFSize->GetProp() &&
+ MapUnit::MapRelative == pOldFSize->GetPropUnit() )
+ {
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ else
+ {
+ // We had a relative value -> recalculate
+ sal_uInt32 nTmp = pOldFSize->GetHeight(); // keep so that we can compare
+ SvxFontHeightItem aNew(240 , 100, pFSize->Which());
+ aNew.SetHeight( pFSize->GetHeight(), pOldFSize->GetProp(),
+ pOldFSize->GetPropUnit() );
+ if( nTmp != aNew.GetHeight() )
+ {
+ SetFormatAttr( aNew );
+ bContinue = nullptr != pOldChgSet || bNewParent;
+ }
+ // We set it to absolute -> do not propagate it further, unless
+ // we set it!
+ else if( pNewChgSet )
+ bContinue = pNewChgSet->GetTheChgdSet() == &GetAttrSet();
+ }
+ }
+ }
+
+ if( bContinue )
+ SwFormatColl::SwClientNotify(rModify, rHint);
+}
+
+void SwTextFormatColl::SetLinkedCharFormat(SwCharFormat* pLink) { mpLinkedCharFormat = pLink; }
+
+const SwCharFormat* SwTextFormatColl::GetLinkedCharFormat() const { return mpLinkedCharFormat; }
+
+bool SwTextFormatColl::IsAtDocNodeSet() const
+{
+ SwIterator<SwContentNode,SwFormatColl> aIter( *this );
+ const SwNodes& rNds = GetDoc()->GetNodes();
+ for( SwContentNode* pNode = aIter.First(); pNode; pNode = aIter.Next() )
+ if( &(pNode->GetNodes()) == &rNds )
+ return true;
+
+ return false;
+}
+
+bool SwTextFormatColl::SetFormatAttr( const SfxPoolItem& rAttr )
+{
+ const bool bIsNumRuleItem = rAttr.Which() == RES_PARATR_NUMRULE;
+ if ( bIsNumRuleItem )
+ {
+ TextFormatCollFunc::RemoveFromNumRule( *this );
+ }
+
+ const bool bRet = SwFormatColl::SetFormatAttr( rAttr );
+
+ if ( bIsNumRuleItem )
+ {
+ TextFormatCollFunc::AddToNumRule( *this );
+ }
+
+ return bRet;
+}
+
+bool SwTextFormatColl::SetFormatAttr( const SfxItemSet& rSet )
+{
+ const bool bIsNumRuleItemAffected =
+ rSet.GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET;
+ if ( bIsNumRuleItemAffected )
+ {
+ TextFormatCollFunc::RemoveFromNumRule( *this );
+ }
+
+ const bool bRet = SwFormatColl::SetFormatAttr( rSet );
+
+ if ( bIsNumRuleItemAffected )
+ {
+ TextFormatCollFunc::AddToNumRule( *this );
+ }
+
+ return bRet;
+}
+
+bool SwTextFormatColl::ResetFormatAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
+{
+ const bool bIsNumRuleItemAffected =
+ ( nWhich2 != 0 && nWhich2 > nWhich1 )
+ ? ( nWhich1 <= RES_PARATR_NUMRULE &&
+ RES_PARATR_NUMRULE <= nWhich2 )
+ : nWhich1 == RES_PARATR_NUMRULE;
+ if ( bIsNumRuleItemAffected )
+ {
+ TextFormatCollFunc::RemoveFromNumRule( *this );
+ }
+
+ const bool bRet = SwFormatColl::ResetFormatAttr( nWhich1, nWhich2 );
+
+ return bRet;
+}
+
+// #i73790#
+sal_uInt16 SwTextFormatColl::ResetAllFormatAttr()
+{
+ const bool bOldState( mbStayAssignedToListLevelOfOutlineStyle );
+ mbStayAssignedToListLevelOfOutlineStyle = true;
+ // #i70748#
+ // Outline level is no longer a member, it is an attribute now.
+ // Thus, it needs to be restored, if the paragraph style is assigned
+ // to the outline style
+ const int nAssignedOutlineStyleLevel = IsAssignedToListLevelOfOutlineStyle()
+ ? GetAssignedOutlineStyleLevel()
+ : -1;
+
+ sal_uInt16 nRet = SwFormatColl::ResetAllFormatAttr();
+
+ // #i70748#
+ if ( nAssignedOutlineStyleLevel != -1 )
+ {
+ AssignToListLevelOfOutlineStyle( nAssignedOutlineStyleLevel );
+ }
+
+ mbStayAssignedToListLevelOfOutlineStyle = bOldState;
+
+ return nRet;
+}
+
+bool SwTextFormatColl::AreListLevelIndentsApplicable() const
+{
+ bool bAreListLevelIndentsApplicable( true );
+
+ if ( GetItemState( RES_PARATR_NUMRULE ) != SfxItemState::SET )
+ {
+ // no list style applied to paragraph style
+ bAreListLevelIndentsApplicable = false;
+ }
+ else if ( GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
+ {
+ // paragraph style has hard-set indent attributes
+ bAreListLevelIndentsApplicable = false;
+ }
+ else if ( GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ {
+ // list style is directly applied to paragraph style and paragraph
+ // style has no hard-set indent attributes
+ bAreListLevelIndentsApplicable = true;
+ }
+ else
+ {
+ // list style is applied through one of the parent paragraph styles and
+ // paragraph style has no hard-set indent attributes
+
+ // check parent paragraph styles
+ const SwTextFormatColl* pColl = dynamic_cast<const SwTextFormatColl*>(DerivedFrom());
+ while ( pColl )
+ {
+ if ( pColl->GetAttrSet().GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
+ {
+ // indent attributes found in the paragraph style hierarchy.
+ bAreListLevelIndentsApplicable = false;
+ break;
+ }
+
+ if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ {
+ // paragraph style with the list style found and until now no
+ // indent attributes are found in the paragraph style hierarchy.
+ bAreListLevelIndentsApplicable = true;
+ break;
+ }
+
+ pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
+ OSL_ENSURE( pColl,
+ "<SwTextFormatColl::AreListLevelIndentsApplicable()> - something wrong in paragraph style hierarchy. The applied list style is not found." );
+ }
+ }
+
+ return bAreListLevelIndentsApplicable;
+}
+
+void SwTextFormatColl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ if (mpNextTextFormatColl)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("next"), BAD_CAST(mpNextTextFormatColl->GetName().toUtf8().getStr()));
+ }
+ if (mpLinkedCharFormat)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("linked"), BAD_CAST(mpLinkedCharFormat->GetName().toUtf8().getStr()));
+ }
+ GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwTextFormatColls::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColls"));
+ for (size_t i = 0; i < size(); ++i)
+ GetFormat(i)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+//FEATURE::CONDCOLL
+
+SwCollCondition::SwCollCondition( SwTextFormatColl* pColl, Master_CollCondition nMasterCond,
+ sal_uLong nSubCond )
+ : SwClient( pColl ), m_nCondition( nMasterCond ),
+ m_nSubCondition( nSubCond )
+{
+}
+
+SwCollCondition::SwCollCondition( const SwCollCondition& rCopy )
+ : SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCopy.GetRegisteredIn())) ),
+ m_nCondition( rCopy.m_nCondition ),
+ m_nSubCondition( rCopy.m_nSubCondition )
+{
+}
+
+SwCollCondition::~SwCollCondition()
+{
+}
+
+void SwCollCondition::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+bool SwCollCondition::operator==( const SwCollCondition& rCmp ) const
+{
+ return ( m_nCondition == rCmp.m_nCondition )
+ && ( m_nSubCondition == rCmp.m_nSubCondition );
+}
+
+void SwCollCondition::SetCondition( Master_CollCondition nCond, sal_uLong nSubCond )
+{
+ m_nCondition = nCond;
+ m_nSubCondition = nSubCond;
+}
+
+SwConditionTextFormatColl::~SwConditionTextFormatColl()
+{
+}
+
+const SwCollCondition* SwConditionTextFormatColl::HasCondition(
+ const SwCollCondition& rCond ) const
+{
+ for (const auto &rpFnd : m_CondColls)
+ {
+ if (*rpFnd == rCond)
+ return rpFnd.get();
+ }
+
+ return nullptr;
+}
+
+void SwConditionTextFormatColl::InsertCondition( const SwCollCondition& rCond )
+{
+ for (SwFormatCollConditions::size_type n = 0; n < m_CondColls.size(); ++n)
+ {
+ if (*m_CondColls[ n ] == rCond)
+ {
+ m_CondColls.erase( m_CondColls.begin() + n );
+ break;
+ }
+ }
+
+ // Not found -> so insert it
+ m_CondColls.push_back( std::make_unique<SwCollCondition> (rCond) );
+}
+
+void SwConditionTextFormatColl::RemoveCondition( const SwCollCondition& rCond )
+{
+ for (SwFormatCollConditions::size_type n = 0; n < m_CondColls.size(); ++n)
+ {
+ if (*m_CondColls[ n ] == rCond)
+ {
+ m_CondColls.erase( m_CondColls.begin() + n );
+ }
+ }
+}
+
+void SwConditionTextFormatColl::SetConditions( const SwFormatCollConditions& rCndClls )
+{
+ // Copy the Conditions, but first delete the old ones
+ m_CondColls.clear();
+ SwDoc& rDoc = *GetDoc();
+ for (const auto &rpFnd : rCndClls)
+ {
+ SwTextFormatColl *const pTmpColl = rpFnd->GetTextFormatColl()
+ ? rDoc.CopyTextColl( *rpFnd->GetTextFormatColl() )
+ : nullptr;
+ std::unique_ptr<SwCollCondition> pNew;
+ pNew.reset(new SwCollCondition( pTmpColl, rpFnd->GetCondition(),
+ rpFnd->GetSubCondition() ));
+ m_CondColls.push_back( std::move(pNew) );
+ }
+}
+
+// FEATURE::CONDCOLL
+void SwTextFormatColl::SetAttrOutlineLevel( int nLevel)
+{
+ OSL_ENSURE( 0 <= nLevel && nLevel <= MAXLEVEL ,"SwTextFormatColl: Level Out Of Range" );
+ SetFormatAttr( SfxUInt16Item( RES_PARATR_OUTLINELEVEL,
+ o3tl::narrowing<sal_uInt16>(nLevel) ) );
+}
+
+int SwTextFormatColl::GetAttrOutlineLevel() const
+{
+ return GetFormatAttr(RES_PARATR_OUTLINELEVEL).GetValue();
+}
+
+int SwTextFormatColl::GetAssignedOutlineStyleLevel() const
+{
+ OSL_ENSURE( IsAssignedToListLevelOfOutlineStyle(),
+ "<SwTextFormatColl::GetAssignedOutlineStyleLevel()> - misuse of method");
+ return GetAttrOutlineLevel() - 1;
+}
+
+void SwTextFormatColl::AssignToListLevelOfOutlineStyle(const int nAssignedListLevel)
+{
+ mbAssignedToOutlineStyle = true;
+ SetAttrOutlineLevel(nAssignedListLevel+1);
+
+ // #i100277#
+ SwIterator<SwTextFormatColl,SwFormatColl> aIter( *this );
+ SwTextFormatColl* pDerivedTextFormatColl = aIter.First();
+ while ( pDerivedTextFormatColl != nullptr )
+ {
+ if ( !pDerivedTextFormatColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ if ( pDerivedTextFormatColl->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::DEFAULT )
+ {
+ SwNumRuleItem aItem;
+ pDerivedTextFormatColl->SetFormatAttr( aItem );
+ }
+ if ( pDerivedTextFormatColl->GetItemState( RES_PARATR_OUTLINELEVEL, false ) == SfxItemState::DEFAULT )
+ {
+ pDerivedTextFormatColl->SetAttrOutlineLevel( 0 );
+ }
+ }
+
+ pDerivedTextFormatColl = aIter.Next();
+ }
+}
+
+void SwTextFormatColl::DeleteAssignmentToListLevelOfOutlineStyle()
+{
+ mbAssignedToOutlineStyle = false;
+ ResetFormatAttr(RES_PARATR_OUTLINELEVEL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/ftnidx.cxx b/sw/source/core/doc/ftnidx.cxx
new file mode 100644
index 000000000..1c8a0bf6b
--- /dev/null
+++ b/sw/source/core/doc/ftnidx.cxx
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <ftninfo.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <ftnidx.hxx>
+#include <ndtxt.hxx>
+#include <ndindex.hxx>
+#include <section.hxx>
+#include <fmtftntx.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+
+namespace sw {
+
+bool IsFootnoteDeleted(IDocumentRedlineAccess const& rIDRA,
+ SwTextFootnote const& rTextFootnote)
+{
+ SwRedlineTable::size_type tmp;
+ SwPosition const pos(const_cast<SwTextNode&>(rTextFootnote.GetTextNode()),
+ rTextFootnote.GetStart());
+ SwRangeRedline const*const pRedline(rIDRA.GetRedline(pos, &tmp));
+ return (pRedline
+ && pRedline->GetType() == RedlineType::Delete
+ && *pRedline->GetPoint() != *pRedline->GetMark());
+}
+
+}
+
+using sw::IsFootnoteDeleted;
+
+bool CompareSwFootnoteIdxs::operator()(SwTextFootnote* const& lhs, SwTextFootnote* const& rhs) const
+{
+ SwNodeOffset nIdxLHS = SwTextFootnote_GetIndex( lhs );
+ SwNodeOffset nIdxRHS = SwTextFootnote_GetIndex( rhs );
+ return ( nIdxLHS == nIdxRHS && lhs->GetStart() < rhs->GetStart() ) || nIdxLHS < nIdxRHS;
+}
+
+void SwFootnoteIdxs::UpdateFootnote( const SwNodeIndex& rStt )
+{
+ if( empty() )
+ return;
+
+ // Get the NodesArray using the first foot note's StartIndex
+ SwDoc& rDoc = rStt.GetNode().GetDoc();
+ if( rDoc.IsInReading() )
+ return ;
+ SwTextFootnote* pTextFootnote;
+
+ const SwEndNoteInfo& rEndInfo = rDoc.GetEndNoteInfo();
+ const SwFootnoteInfo& rFootnoteInfo = rDoc.GetFootnoteInfo();
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+
+ // For normal foot notes we treat per-chapter and per-document numbering
+ // separately. For Endnotes we only have per-document numbering.
+ if( FTNNUM_CHAPTER == rFootnoteInfo.m_eNum )
+ {
+ SwRootFrame const* pLayout(nullptr);
+ o3tl::sorted_vector<SwRootFrame*> layouts = rDoc.GetAllLayouts();
+ // sw_redlinehide: here we need to know if there's *any* layout with
+ // IsHideRedlines(), because then the hidden-numbers have to be updated
+ for (SwRootFrame const* pTmp : layouts)
+ {
+ if (pTmp->IsHideRedlines())
+ {
+ pLayout = pTmp;
+ }
+ }
+
+ const SwOutlineNodes& rOutlNds = rDoc.GetNodes().GetOutLineNds();
+ const SwNode *pChapterStartHidden(&rDoc.GetNodes().GetEndOfExtras());
+ SwNodeOffset nChapterStart(pChapterStartHidden->GetIndex());
+ SwNodeOffset nChapterEnd(rDoc.GetNodes().GetEndOfContent().GetIndex());
+ SwNodeOffset nChapterEndHidden(nChapterEnd);
+ if( !rOutlNds.empty() )
+ {
+ // Find the Chapter's start, which contains rStt
+ size_t n = 0;
+
+ for( ; n < rOutlNds.size(); ++n )
+ if( rOutlNds[ n ]->GetIndex() > rStt.GetIndex() )
+ break; // found it!
+ else if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
+ {
+ nChapterStart = rOutlNds[ n ]->GetIndex();
+ if (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[n]->GetTextNode()))
+ {
+ pChapterStartHidden = rOutlNds[ n ];
+ }
+ }
+ // now find the end of the range
+ for( ; n < rOutlNds.size(); ++n )
+ if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
+ {
+ nChapterEnd = rOutlNds[ n ]->GetIndex();
+ break;
+ }
+
+ // continue to find end of hidden-chapter
+ for ( ; n < rOutlNds.size(); ++n)
+ {
+ if (rOutlNds[n]->GetTextNode()->GetAttrOutlineLevel() == 1
+ && (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[n]->GetTextNode())))
+ {
+ nChapterEndHidden = rOutlNds[n]->GetIndex();
+ break;
+ }
+ }
+ }
+
+ size_t nPos = 0;
+ size_t nFootnoteNo = 1;
+ size_t nFootnoteNoHidden = 1;
+ if (SeekEntry( *pChapterStartHidden, &nPos ) && nPos)
+ {
+ // Step forward until the Index is not the same anymore
+ const SwNode* pCmpNd = &rStt.GetNode();
+ while( nPos && pCmpNd == &((*this)[ --nPos ]->GetTextNode()) )
+ ;
+ ++nPos;
+ }
+
+ if( nPos == size() ) // nothing found
+ return;
+
+ if( rOutlNds.empty() )
+ {
+ nFootnoteNo = nPos+1;
+ if (nPos)
+ {
+ nFootnoteNoHidden = (*this)[nPos - 1]->GetFootnote().GetNumberRLHidden() + 1;
+ }
+ }
+
+ for( ; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ SwNodeOffset const nNode(pTextFootnote->GetTextNode().GetIndex());
+ if (nChapterEndHidden <= nNode)
+ break;
+
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() && !rFootnote.IsEndNote() &&
+ !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
+ {
+ pTextFootnote->SetNumber(
+ (nChapterStart <= nNode && nNode < nChapterEnd)
+ ? rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo
+ : rFootnote.GetNumber(),
+ rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden,
+ rFootnote.GetNumStr() );
+ if (nChapterStart <= nNode && nNode < nChapterEnd)
+ {
+ ++nFootnoteNo;
+ }
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+ }
+
+ SwUpdFootnoteEndNtAtEnd aNumArr;
+
+ // unless we have per-document numbering, only look at endnotes here
+ const bool bEndNoteOnly = FTNNUM_DOC != rFootnoteInfo.m_eNum;
+
+ size_t nPos;
+ size_t nFootnoteNo = 1;
+ size_t nEndNo = 1;
+ size_t nFootnoteNoHidden = 1;
+ size_t nEndNoHidden = 1;
+ SwNodeOffset nUpdNdIdx = rStt.GetIndex();
+ for( nPos = 0; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ if( nUpdNdIdx <= pTextFootnote->GetTextNode().GetIndex() )
+ break;
+
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() )
+ {
+ if (!aNumArr.ChkNumber(rIDRA, *pTextFootnote).first)
+ {
+ if( pTextFootnote->GetFootnote().IsEndNote() )
+ {
+ nEndNo++;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nEndNoHidden;
+ }
+ }
+ else
+ {
+ nFootnoteNo++;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+ }
+ }
+
+ // Set the array number for all footnotes starting from nPos
+ for( ; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() )
+ {
+ std::pair<sal_uInt16, sal_uInt16> nSectNo = aNumArr.ChkNumber(rIDRA, *pTextFootnote);
+ if (!nSectNo.first && (rFootnote.IsEndNote() || !bEndNoteOnly))
+ {
+ if (rFootnote.IsEndNote())
+ {
+ nSectNo.first = rEndInfo.m_nFootnoteOffset + nEndNo;
+ ++nEndNo;
+ nSectNo.second = rEndInfo.m_nFootnoteOffset + nEndNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nEndNoHidden;
+ }
+ }
+ else
+ {
+ nSectNo.first = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo;
+ ++nFootnoteNo;
+ nSectNo.second = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+
+ if (nSectNo.first)
+ {
+ pTextFootnote->SetNumber(nSectNo.first, nSectNo.second, rFootnote.GetNumStr());
+ }
+ }
+ }
+}
+
+void SwFootnoteIdxs::UpdateAllFootnote()
+{
+ if( empty() )
+ return;
+
+ // Get the NodesArray via the StartIndex of the first Footnote
+ SwDoc& rDoc = const_cast<SwDoc&>((*this)[ 0 ]->GetTextNode().GetDoc());
+ SwTextFootnote* pTextFootnote;
+ const SwEndNoteInfo& rEndInfo = rDoc.GetEndNoteInfo();
+ const SwFootnoteInfo& rFootnoteInfo = rDoc.GetFootnoteInfo();
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+
+ SwUpdFootnoteEndNtAtEnd aNumArr;
+
+ SwRootFrame const* pLayout = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ o3tl::sorted_vector<SwRootFrame*> aAllLayouts = rDoc.GetAllLayouts();
+ // For normal Footnotes per-chapter and per-document numbering are treated separately.
+ // For Endnotes we only have document-wise numbering.
+ if( FTNNUM_CHAPTER == rFootnoteInfo.m_eNum )
+ {
+ // sw_redlinehide: here we need to know if there's *any* layout with
+ // IsHideRedlines(), because then the hidden-numbers have to be updated
+ for (SwRootFrame const* pTmp : aAllLayouts)
+ {
+ if (pTmp->IsHideRedlines())
+ {
+ pLayout = pTmp;
+ }
+ }
+
+ const SwOutlineNodes& rOutlNds = rDoc.GetNodes().GetOutLineNds();
+ sal_uInt16 nNo = 1; // Number for the Footnotes
+ sal_uInt16 nNoNo = 1;
+ size_t nFootnoteIdx = 0; // Index into theFootnoteIdx array
+ for( size_t n = 0; n < rOutlNds.size(); ++n )
+ {
+ if ( rOutlNds[ n ]->GetTextNode()->GetAttrOutlineLevel() == 1 )
+ {
+ SwNodeOffset nCapStt = rOutlNds[ n ]->GetIndex(); // Start of a new chapter
+ for( ; nFootnoteIdx < size(); ++nFootnoteIdx )
+ {
+ pTextFootnote = (*this)[ nFootnoteIdx ];
+ if( pTextFootnote->GetTextNode().GetIndex() >= nCapStt )
+ break;
+
+ // Endnotes are per-document only
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( !rFootnote.IsEndNote() && rFootnote.GetNumStr().isEmpty() &&
+ !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
+ {
+ pTextFootnote->SetNumber(
+ rFootnoteInfo.m_nFootnoteOffset + nNo,
+ rFootnoteInfo.m_nFootnoteOffset + nNoNo,
+ rFootnote.GetNumStr() );
+ ++nNo;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nNoNo;
+ }
+ }
+ }
+ if( nFootnoteIdx >= size() )
+ break; // ok, everything is updated
+ nNo = 1;
+ // sw_redlinehide: this means the numbers are layout dependent in chapter case
+ if (!pLayout || sw::IsParaPropsNode(*pLayout, *rOutlNds[ n ]->GetTextNode()))
+ {
+ nNoNo = 1;
+ }
+ }
+ }
+
+ for (nNo = 1, nNoNo = 1; nFootnoteIdx < size(); ++nFootnoteIdx)
+ {
+ // Endnotes are per-document
+ pTextFootnote = (*this)[ nFootnoteIdx ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( !rFootnote.IsEndNote() && rFootnote.GetNumStr().isEmpty() &&
+ !SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *pTextFootnote ))
+ {
+ pTextFootnote->SetNumber(
+ rFootnoteInfo.m_nFootnoteOffset + nNo,
+ rFootnoteInfo.m_nFootnoteOffset + nNoNo,
+ rFootnote.GetNumStr() );
+ ++nNo;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nNoNo;
+ }
+ }
+ }
+ }
+
+ // We use bool here, so that we also iterate through the Endnotes with a chapter setting.
+ const bool bEndNoteOnly = FTNNUM_DOC != rFootnoteInfo.m_eNum;
+ sal_uInt16 nFootnoteNo = 1;
+ sal_uInt16 nEndnoteNo = 1;
+ sal_uInt16 nFootnoteNoHidden = 1;
+ sal_uInt16 nEndnoteNoHidden = 1;
+ for( size_t nPos = 0; nPos < size(); ++nPos )
+ {
+ pTextFootnote = (*this)[ nPos ];
+ const SwFormatFootnote &rFootnote = pTextFootnote->GetFootnote();
+ if( rFootnote.GetNumStr().isEmpty() )
+ {
+ std::pair<sal_uInt16, sal_uInt16> nSectNo = aNumArr.ChkNumber(rIDRA, *pTextFootnote);
+ if (!nSectNo.first && (rFootnote.IsEndNote() || !bEndNoteOnly))
+ {
+ if (rFootnote.IsEndNote())
+ {
+ nSectNo.first = rEndInfo.m_nFootnoteOffset + nEndnoteNo;
+ ++nEndnoteNo;
+ nSectNo.second = rEndInfo.m_nFootnoteOffset + nEndnoteNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nEndnoteNoHidden;
+ }
+ }
+ else
+ {
+ nSectNo.first = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNo;
+ ++nFootnoteNo;
+ nSectNo.second = rFootnoteInfo.m_nFootnoteOffset + nFootnoteNoHidden;
+ if (!IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ ++nFootnoteNoHidden;
+ }
+ }
+ }
+
+ if (nSectNo.first)
+ {
+ pTextFootnote->SetNumber(nSectNo.first, nSectNo.second, rFootnote.GetNumStr());
+ }
+ }
+ }
+
+ if (pLayout && FTNNUM_PAGE == rFootnoteInfo.m_eNum)
+ for( auto aLayout : aAllLayouts )
+ aLayout->UpdateFootnoteNums();
+}
+
+SwTextFootnote* SwFootnoteIdxs::SeekEntry( const SwNodeIndex& rPos, size_t* pFndPos ) const
+{
+ SwNodeOffset nIdx = rPos.GetIndex();
+
+ size_t nO = size();
+ size_t nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ const size_t nM = nU + ( nO - nU ) / 2;
+ SwNodeOffset nNdIdx = SwTextFootnote_GetIndex( (*this)[ nM ] );
+ if( nNdIdx == nIdx )
+ {
+ if( pFndPos )
+ *pFndPos = nM;
+ return (*this)[ nM ];
+ }
+ else if( nNdIdx < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pFndPos )
+ *pFndPos = nU;
+ return nullptr;
+}
+
+const SwSectionNode* SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr(
+ const SwTextFootnote& rTextFootnote )
+{
+ sal_uInt16 nWh = rTextFootnote.GetFootnote().IsEndNote() ?
+ sal_uInt16(RES_END_AT_TXTEND) : sal_uInt16(RES_FTN_AT_TXTEND);
+ const SwSectionNode* pNd = rTextFootnote.GetTextNode().FindSectionNode();
+ while( pNd )
+ {
+ sal_uInt16 nVal = static_cast<const SwFormatFootnoteEndAtTextEnd&>(pNd->GetSection().GetFormat()->
+ GetFormatAttr( nWh )).GetValue();
+ if( FTNEND_ATTXTEND_OWNNUMSEQ == nVal || FTNEND_ATTXTEND_OWNNUMANDFMT == nVal )
+ break;
+ pNd = pNd->StartOfSectionNode()->FindSectionNode();
+ }
+
+ return pNd;
+}
+
+std::pair<sal_uInt16, sal_uInt16> SwUpdFootnoteEndNtAtEnd::GetNumber(
+ IDocumentRedlineAccess const& rIDRA,
+ const SwTextFootnote& rTextFootnote,
+ const SwSectionNode& rNd )
+{
+ std::pair<sal_uInt16, sal_uInt16> nRet(0, 0);
+ sal_uInt16 nWh;
+ std::vector<const SwSectionNode*>* pArr;
+ std::vector<std::pair<sal_uInt16, sal_uInt16>> *pNum;
+ if( rTextFootnote.GetFootnote().IsEndNote() )
+ {
+ pArr = &m_aEndSections;
+ pNum = &m_aEndNumbers;
+ nWh = RES_END_AT_TXTEND;
+ }
+ else
+ {
+ pArr = &m_aFootnoteSections;
+ pNum = &m_aFootnoteNumbers;
+ nWh = RES_FTN_AT_TXTEND;
+ }
+
+ for( size_t n = pArr->size(); n; )
+ if( (*pArr)[ --n ] == &rNd )
+ {
+ nRet.first = ++((*pNum)[ n ].first);
+ if (!IsFootnoteDeleted(rIDRA, rTextFootnote))
+ {
+ ++((*pNum)[ n ].second);
+ }
+ nRet.second = ((*pNum)[ n ].second);
+ break;
+ }
+
+ if (!nRet.first)
+ {
+ pArr->push_back( &rNd );
+ sal_uInt16 const tmp = static_cast<const SwFormatFootnoteEndAtTextEnd&>(
+ rNd.GetSection().GetFormat()->
+ GetFormatAttr( nWh )).GetOffset();
+ nRet.first = tmp + 1;
+ nRet.second = tmp + 1;
+ pNum->push_back( nRet );
+ }
+ return nRet;
+}
+
+std::pair<sal_uInt16, sal_uInt16> SwUpdFootnoteEndNtAtEnd::ChkNumber(
+ IDocumentRedlineAccess const& rIDRA,
+ const SwTextFootnote& rTextFootnote)
+{
+ const SwSectionNode* pSectNd = FindSectNdWithEndAttr( rTextFootnote );
+ return pSectNd
+ ? GetNumber(rIDRA, rTextFootnote, *pSectNd)
+ : std::pair<sal_uInt16, sal_uInt16>(0, 0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/gctable.cxx b/sw/source/core/doc/gctable.cxx
new file mode 100644
index 000000000..30937cadd
--- /dev/null
+++ b/sw/source/core/doc/gctable.cxx
@@ -0,0 +1,463 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <tblrwcl.hxx>
+#include <algorithm>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::editeng;
+
+static const SvxBorderLine* GetLineTB( const SvxBoxItem* pBox, bool bTop )
+{
+ return bTop ? pBox->GetTop() : pBox->GetBottom();
+}
+
+bool SwGCBorder_BoxBrd::CheckLeftBorderOfFormat( const SwFrameFormat& rFormat )
+{
+ if( const SvxBoxItem* pItem = rFormat.GetItemIfSet( RES_BOX ) )
+ {
+ const SvxBorderLine* pBrd = pItem->GetLeft();
+ if( pBrd )
+ {
+ if( *m_pBorderLine == *pBrd )
+ m_bAnyBorderFind = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool lcl_GCBorder_ChkBoxBrd_B( const SwTableBox* pBox, SwGCBorder_BoxBrd* pPara );
+
+static bool lcl_GCBorder_ChkBoxBrd_L( const SwTableLine* pLine, SwGCBorder_BoxBrd* pPara )
+{
+ const SwTableBox* pBox = pLine->GetTabBoxes().front();
+ return lcl_GCBorder_ChkBoxBrd_B( pBox, pPara );
+}
+
+static bool lcl_GCBorder_ChkBoxBrd_B( const SwTableBox* pBox, SwGCBorder_BoxBrd* pPara )
+{
+ if( !pBox->GetTabLines().empty() )
+ {
+ for( auto pLine : pBox->GetTabLines() )
+ {
+ if (!lcl_GCBorder_ChkBoxBrd_L( pLine, pPara ))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ return pPara->CheckLeftBorderOfFormat( *pBox->GetFrameFormat() );
+}
+
+static void lcl_GCBorder_GetLastBox_B( const SwTableBox* pBox, SwTableBoxes* pPara );
+
+static void lcl_GCBorder_GetLastBox_L( const SwTableLine* pLine, SwTableBoxes* pPara )
+{
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ SwTableBox* pBox = rBoxes.back();
+ lcl_GCBorder_GetLastBox_B( pBox, pPara );
+}
+
+static void lcl_GCBorder_GetLastBox_B( const SwTableBox* pBox, SwTableBoxes* pPara )
+{
+ const SwTableLines& rLines = pBox->GetTabLines();
+ if( !rLines.empty() )
+ {
+ for( const SwTableLine* pLine : rLines )
+ lcl_GCBorder_GetLastBox_L( pLine, pPara );
+ }
+ else
+ pPara->push_back( const_cast<SwTableBox*>(pBox) );
+}
+
+// Find the "end" of the passed BorderLine. Returns the "Layout"Pos!
+static sal_uInt16 lcl_FindEndPosOfBorder( const SwCollectTableLineBoxes& rCollTLB,
+ const SvxBorderLine& rBrdLn, size_t& rStt, bool bTop )
+{
+ sal_uInt16 nPos, nLastPos = 0;
+ for( size_t nEnd = rCollTLB.Count(); rStt < nEnd; ++rStt )
+ {
+ const SvxBorderLine* pBrd;
+ const SwTableBox& rBox = rCollTLB.GetBox( rStt, &nPos );
+ const SvxBoxItem* pItem = rBox.GetFrameFormat()->GetItemIfSet(RES_BOX);
+
+ if( !pItem )
+ break;
+ pBrd = GetLineTB( pItem, bTop );
+ if( !pBrd || *pBrd != rBrdLn )
+ break;
+ nLastPos = nPos;
+ }
+ return nLastPos;
+}
+
+static const SvxBorderLine* lcl_GCBorder_GetBorder( const SwTableBox& rBox,
+ bool bTop,
+ const SvxBoxItem** ppItem )
+{
+ *ppItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOX );
+ if (*ppItem)
+ return GetLineTB( *ppItem, bTop );
+ return nullptr;
+}
+
+static void lcl_GCBorder_DelBorder( const SwCollectTableLineBoxes& rCollTLB,
+ size_t& rStt, bool bTop,
+ const SvxBorderLine& rLine,
+ const SvxBoxItem* pItem,
+ sal_uInt16 nEndPos,
+ SwShareBoxFormats* pShareFormats )
+{
+ SwTableBox* pBox = const_cast<SwTableBox*>(&rCollTLB.GetBox( rStt ));
+ sal_uInt16 nNextPos;
+ const SvxBorderLine* pLn = &rLine;
+
+ do {
+ if( pLn && *pLn == rLine )
+ {
+ SvxBoxItem aBox( *pItem );
+ if( bTop )
+ aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
+ else
+ aBox.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+
+ if( pShareFormats )
+ pShareFormats->SetAttr( *pBox, aBox );
+ else
+ pBox->ClaimFrameFormat()->SetFormatAttr( aBox );
+ }
+
+ if( ++rStt >= rCollTLB.Count() )
+ break;
+
+ pBox = const_cast<SwTableBox*>(&rCollTLB.GetBox( rStt, &nNextPos ));
+ if( nNextPos > nEndPos )
+ break;
+
+ pLn = lcl_GCBorder_GetBorder( *pBox, bTop, &pItem );
+
+ } while( true );
+}
+
+static void lcl_GC_Box_Border( const SwTableBox* pBox, SwGCLineBorder* pPara );
+
+void sw_GC_Line_Border( const SwTableLine* pLine, SwGCLineBorder* pGCPara )
+{
+ // First the right edge with the left edge of the succeeding Box within this Line
+ {
+ SwGCBorder_BoxBrd aBPara;
+ const SvxBorderLine* pBrd;
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( SwTableBoxes::size_type n = 0, nBoxes = rBoxes.size() - 1; n < nBoxes; ++n )
+ {
+ SwTableBoxes aBoxes;
+ {
+ SwTableBox* pBox = rBoxes[ n ];
+ if( pBox->GetSttNd() )
+ aBoxes.insert( aBoxes.begin(), pBox );
+ else
+ lcl_GCBorder_GetLastBox_B( pBox, &aBoxes );
+ }
+
+ for( SwTableBoxes::size_type i = aBoxes.size(); i; )
+ {
+ SwTableBox* pBox = aBoxes[ --i ];
+ if( const SvxBoxItem* pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOX ) )
+ {
+ pBrd = pItem->GetRight();
+ if( pBrd )
+ {
+ aBPara.SetBorder( *pBrd );
+ const SwTableBox* pNextBox = rBoxes[n+1];
+ if( lcl_GCBorder_ChkBoxBrd_B( pNextBox, &aBPara ) &&
+ aBPara.IsAnyBorderFound() )
+ {
+ SvxBoxItem aBox( *pItem );
+ aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ if( pGCPara->pShareFormats )
+ pGCPara->pShareFormats->SetAttr( *pBox, aBox );
+ else
+ pBox->ClaimFrameFormat()->SetFormatAttr( aBox );
+ }
+ }
+ }
+ }
+
+ aBoxes.clear();
+ }
+ }
+
+ // And now the own bottom edge with the succeeding top edge
+ if( !pGCPara->IsLastLine() )
+ {
+ SwCollectTableLineBoxes aBottom( false );
+ SwCollectTableLineBoxes aTop( true );
+
+ sw_Line_CollectBox( pLine, &aBottom );
+
+ const SwTableLine* pNextLine = (*pGCPara->pLines)[ pGCPara->nLinePos+1 ];
+ sw_Line_CollectBox( pNextLine, &aTop );
+
+ // remove all "duplicated" Lines that are the same
+ sal_uInt16 nBtmPos, nTopPos;
+
+ size_t nSttBtm {0};
+ size_t nSttTop {0};
+ const size_t nEndBtm {aBottom.Count()};
+ const size_t nEndTop {aTop.Count()};
+
+ const SwTableBox *pBtmBox = &aBottom.GetBox( nSttBtm++, &nBtmPos );
+ const SwTableBox *pTopBox = &aTop.GetBox( nSttTop++, &nTopPos );
+ const SvxBoxItem *pBtmItem = nullptr, *pTopItem = nullptr;
+ const SvxBorderLine *pBtmLine(nullptr), *pTopLine(nullptr);
+ bool bGetTopItem = true, bGetBtmItem = true;
+
+ do {
+ if( bGetBtmItem )
+ pBtmLine = lcl_GCBorder_GetBorder( *pBtmBox, false, &pBtmItem );
+ if( bGetTopItem )
+ pTopLine = lcl_GCBorder_GetBorder( *pTopBox, true, &pTopItem );
+
+ if( pTopLine && pBtmLine && *pTopLine == *pBtmLine )
+ {
+ // We can remove one, but which one?
+ const size_t nSavSttBtm {nSttBtm};
+ const size_t nSavSttTop {nSttTop};
+ sal_uInt16 nBtmEndPos = ::lcl_FindEndPosOfBorder( aBottom,
+ *pTopLine, nSttBtm, false );
+ if( !nBtmEndPos ) nBtmEndPos = nBtmPos;
+ sal_uInt16 nTopEndPos = ::lcl_FindEndPosOfBorder( aTop,
+ *pTopLine, nSttTop, true );
+ if( !nTopEndPos ) nTopEndPos = nTopPos;
+
+ if( nTopEndPos <= nBtmEndPos )
+ {
+ // Delete the TopBorders until BottomEndPos
+ nSttTop = nSavSttTop;
+ if( nTopPos <= nBtmEndPos )
+ lcl_GCBorder_DelBorder( aTop, --nSttTop, true,
+ *pBtmLine, pTopItem, nBtmEndPos,
+ pGCPara->pShareFormats );
+ else
+ nSttBtm = nSavSttBtm;
+ }
+ else
+ {
+ // Else delete the BottomBorders until TopEndPos
+ nSttBtm = nSavSttBtm;
+ if( nBtmPos <= nTopEndPos )
+ lcl_GCBorder_DelBorder( aBottom, --nSttBtm, false,
+ *pTopLine, pBtmItem, nTopEndPos,
+ pGCPara->pShareFormats );
+ else
+ nSttTop = nSavSttTop;
+ }
+ nTopPos = nBtmPos;
+ }
+
+ if( nTopPos == nBtmPos )
+ {
+ if( nSttBtm >= nEndBtm || nSttTop >= nEndTop )
+ break;
+
+ pBtmBox = &aBottom.GetBox( nSttBtm++, &nBtmPos );
+ pTopBox = &aTop.GetBox( nSttTop++, &nTopPos );
+ bGetTopItem = bGetBtmItem = true;
+ }
+ else if( nTopPos < nBtmPos )
+ {
+ if( nSttTop >= nEndTop )
+ break;
+ pTopBox = &aTop.GetBox( nSttTop++, &nTopPos );
+ bGetTopItem = true;
+ bGetBtmItem = false;
+ }
+ else
+ {
+ if( nSttBtm >= nEndBtm )
+ break;
+ pBtmBox = &aBottom.GetBox( nSttBtm++, &nBtmPos );
+ bGetTopItem = false;
+ bGetBtmItem = true;
+ }
+
+ } while( true );
+ }
+
+ for( const auto& rpBox : pLine->GetTabBoxes() )
+ lcl_GC_Box_Border(rpBox, pGCPara );
+
+ ++pGCPara->nLinePos;
+}
+
+static void lcl_GC_Box_Border( const SwTableBox* pBox, SwGCLineBorder* pPara )
+{
+ if( !pBox->GetTabLines().empty() )
+ {
+ SwGCLineBorder aPara( *pBox );
+ aPara.pShareFormats = pPara->pShareFormats;
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ sw_GC_Line_Border( pLine, &aPara );
+ }
+}
+
+namespace {
+
+struct GCLinePara
+{
+ SwTableLines* pLns;
+ SwShareBoxFormats* pShareFormats;
+
+ GCLinePara( SwTableLines& rLns, GCLinePara* pPara = nullptr )
+ : pLns( &rLns ), pShareFormats( pPara ? pPara->pShareFormats : nullptr )
+ {}
+};
+
+}
+
+static bool lcl_MergeGCLine(SwTableLine* pLine, GCLinePara* pPara);
+
+static bool lcl_MergeGCBox(SwTableBox* pTableBox, GCLinePara* pPara)
+{
+ if( !pTableBox->GetTabLines().empty() )
+ {
+ // ATTENTION: The Line count can change!
+ GCLinePara aPara( pTableBox->GetTabLines(), pPara );
+ for( SwTableLines::size_type n = 0;
+ n < pTableBox->GetTabLines().size() && lcl_MergeGCLine( pTableBox->GetTabLines()[n], &aPara );
+ ++n )
+ ;
+
+ if( 1 == pTableBox->GetTabLines().size() )
+ {
+ // we have a box with a single line, so we just replace it by the line's boxes
+ SwTableLine* pInsLine = pTableBox->GetUpper();
+ SwTableLine* pCpyLine = pTableBox->GetTabLines()[0];
+ SwTableBoxes::iterator it = std::find( pInsLine->GetTabBoxes().begin(), pInsLine->GetTabBoxes().end(), pTableBox );
+ for( auto pTabBox : pCpyLine->GetTabBoxes() )
+ pTabBox->SetUpper( pInsLine );
+
+ SfxPoolItem const* pRowBrush(nullptr);
+ pCpyLine->GetFrameFormat()->GetItemState(RES_BACKGROUND, true, &pRowBrush);
+ if (pRowBrush)
+ {
+ for (auto pBox : pCpyLine->GetTabBoxes())
+ {
+ if (pBox->GetFrameFormat()->GetItemState(RES_BACKGROUND) != SfxItemState::SET)
+ { // set inner row background on inner cell
+ pBox->ClaimFrameFormat();
+ pBox->GetFrameFormat()->SetFormatAttr(*pRowBrush);
+ }
+ }
+ }
+
+ // remove the old box from its parent line
+ it = pInsLine->GetTabBoxes().erase( it );
+ // insert the nested line's boxes in its place
+ pInsLine->GetTabBoxes().insert( it, pCpyLine->GetTabBoxes().begin(), pCpyLine->GetTabBoxes().end());
+ pCpyLine->GetTabBoxes().clear();
+ // destroy the removed box
+ delete pTableBox;
+
+ return false; // set up anew
+ }
+ }
+ return true;
+}
+
+static bool lcl_MergeGCLine(SwTableLine* pLn, GCLinePara* pGCPara)
+{
+ SwTableBoxes::size_type nBoxes = pLn->GetTabBoxes().size();
+ if( nBoxes )
+ {
+ while( 1 == nBoxes )
+ {
+ // We have a Box with Lines
+ SwTableBox* pBox = pLn->GetTabBoxes().front();
+ if( pBox->GetTabLines().empty() )
+ break;
+
+ SwTableLine* pLine = pBox->GetTabLines()[0];
+
+ // pLine turns into the current Line (that is rpLine), the rest is moved
+ // into the LinesArray past the current one.
+ // The LinesArray is in pPara!
+ SwTableLines::size_type nLines = pBox->GetTabLines().size();
+
+ SwTableLines& rLns = *pGCPara->pLns;
+ sal_uInt16 nInsPos = rLns.GetPos( pLn );
+ OSL_ENSURE( USHRT_MAX != nInsPos, "Could not find Line!" );
+
+ SwTableBox* pUpper = pLn->GetUpper();
+
+ rLns.erase( rLns.begin() + nInsPos ); // remove the Line from the array
+ rLns.insert( rLns.begin() + nInsPos, pBox->GetTabLines().begin(), pBox->GetTabLines().end() );
+
+ // JP 31.03.99: Bug 60000
+ // Pass the attributes of the to-be-deleted Lines to the "inserted" one
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pLn->GetFrameFormat()->GetItemState(
+ RES_BACKGROUND, true, &pItem ))
+ {
+ SwTableLines& rBoxLns = pBox->GetTabLines();
+ for( auto pBoxLine : rBoxLns )
+ if( SfxItemState::SET != pBoxLine->GetFrameFormat()->
+ GetItemState( RES_BACKGROUND ))
+ pGCPara->pShareFormats->SetAttr( *pBoxLine, *pItem );
+ }
+
+ pBox->GetTabLines().erase( pBox->GetTabLines().begin(), pBox->GetTabLines().begin() + nLines ); // Remove Lines from the array
+
+ delete pLn;
+
+ // Set the dependency anew
+ while( nLines-- )
+ rLns[ nInsPos++ ]->SetUpper( pUpper );
+
+ pLn = pLine; // and set up anew
+ nBoxes = pLn->GetTabBoxes().size();
+ }
+
+ // ATTENTION: The number of boxes can change!
+ for( SwTableBoxes::size_type nLen = 0; nLen < pLn->GetTabBoxes().size(); ++nLen )
+ if( !lcl_MergeGCBox( pLn->GetTabBoxes()[nLen], pGCPara ))
+ --nLen;
+ }
+ return true;
+}
+
+// Clean structure a bit
+void SwTable::GCLines()
+{
+ // ATTENTION: The Line count can change!
+ GCLinePara aPara( GetTabLines() );
+ SwShareBoxFormats aShareFormats;
+ aPara.pShareFormats = &aShareFormats;
+ for( SwTableLines::size_type n = 0; n < GetTabLines().size() &&
+ lcl_MergeGCLine( GetTabLines()[n], &aPara ); ++n )
+ ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/htmltbl.cxx b/sw/source/core/doc/htmltbl.cxx
new file mode 100644
index 000000000..19f8e3305
--- /dev/null
+++ b/sw/source/core/doc/htmltbl.cxx
@@ -0,0 +1,1772 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <memory>
+
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <frmfmt.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+#include <flyfrm.hxx>
+#include <poolfmt.hxx>
+#include <viewsh.hxx>
+#include <tabfrm.hxx>
+#include <viewopt.hxx>
+#include <htmltbl.hxx>
+#include <calbck.hxx>
+#include <o3tl/numeric.hxx>
+#include <osl/diagnose.h>
+#ifdef DBG_UTIL
+#include <tblrwcl.hxx>
+#endif
+
+using namespace ::com::sun::star;
+
+#define COLFUZZY 20
+#define MAX_TABWIDTH (USHRT_MAX - 2001)
+
+namespace {
+
+class SwHTMLTableLayoutConstraints
+{
+ sal_uInt16 m_nRow; // start row
+ sal_uInt16 m_nCol; // start column
+ sal_uInt16 m_nColSpan; // the column's COLSPAN
+
+ std::unique_ptr<SwHTMLTableLayoutConstraints> m_pNext; // the next constraint
+
+ sal_uLong m_nMinNoAlign, m_nMaxNoAlign; // provisional result of AL-Pass 1
+
+public:
+ SwHTMLTableLayoutConstraints( sal_uLong nMin, sal_uLong nMax, sal_uInt16 nRow,
+ sal_uInt16 nCol, sal_uInt16 nColSp );
+
+ sal_uLong GetMinNoAlign() const { return m_nMinNoAlign; }
+ sal_uLong GetMaxNoAlign() const { return m_nMaxNoAlign; }
+
+ SwHTMLTableLayoutConstraints *InsertNext( SwHTMLTableLayoutConstraints *pNxt );
+ SwHTMLTableLayoutConstraints* GetNext() const { return m_pNext.get(); }
+
+ sal_uInt16 GetColSpan() const { return m_nColSpan; }
+ sal_uInt16 GetColumn() const { return m_nCol; }
+};
+
+}
+
+SwHTMLTableLayoutCnts::SwHTMLTableLayoutCnts(const SwStartNode *pSttNd,
+ std::shared_ptr<SwHTMLTableLayout> const& rTab,
+ bool bNoBrTag,
+ std::shared_ptr<SwHTMLTableLayoutCnts> const& rNxt ) :
+ m_xNext( rNxt ), m_pBox( nullptr ), m_xTable( rTab ), m_pStartNode( pSttNd ),
+ m_nPass1Done( 0 ), m_nWidthSet( 0 ), m_bNoBreakTag( bNoBrTag )
+{}
+
+const SwStartNode *SwHTMLTableLayoutCnts::GetStartNode() const
+{
+ return m_pBox ? m_pBox->GetSttNd() : m_pStartNode;
+}
+
+SwHTMLTableLayoutCell::SwHTMLTableLayoutCell(std::shared_ptr<SwHTMLTableLayoutCnts> const& rCnts,
+ sal_uInt16 nRSpan, sal_uInt16 nCSpan,
+ sal_uInt16 nWidth, bool bPercentWidth,
+ bool bNWrapOpt ) :
+ m_xContents(rCnts),
+ m_nRowSpan( nRSpan ), m_nColSpan( nCSpan ),
+ m_nWidthOption( nWidth ), m_bPercentWidthOption( bPercentWidth ),
+ m_bNoWrapOption( bNWrapOpt )
+{}
+
+SwHTMLTableLayoutColumn::SwHTMLTableLayoutColumn( sal_uInt16 nWidth,
+ bool bRelWidth,
+ bool bLBorder ) :
+ m_nMinNoAlign(MINLAY), m_nMaxNoAlign(MINLAY), m_nAbsMinNoAlign(MINLAY),
+ m_nMin(0), m_nMax(0),
+ m_nAbsColWidth(0), m_nRelColWidth(0),
+ m_nWidthOption( nWidth ), m_bRelWidthOption( bRelWidth ),
+ m_bLeftBorder( bLBorder )
+{}
+
+SwHTMLTableLayoutConstraints::SwHTMLTableLayoutConstraints(sal_uLong nMin, sal_uLong nMax,
+ sal_uInt16 nRw, sal_uInt16 nColumn,
+ sal_uInt16 nColSp)
+ : m_nRow(nRw)
+ , m_nCol(nColumn)
+ , m_nColSpan(nColSp)
+ , m_nMinNoAlign(nMin)
+ , m_nMaxNoAlign(nMax)
+{}
+
+SwHTMLTableLayoutConstraints *SwHTMLTableLayoutConstraints::InsertNext(
+ SwHTMLTableLayoutConstraints *pNxt )
+{
+ SwHTMLTableLayoutConstraints *pPrev = nullptr;
+ SwHTMLTableLayoutConstraints *pConstr = this;
+ while( pConstr )
+ {
+ if (pConstr->m_nRow > pNxt->m_nRow || pConstr->GetColumn() > pNxt->GetColumn())
+ break;
+ pPrev = pConstr;
+ pConstr = pConstr->GetNext();
+ }
+
+ if( pPrev )
+ {
+ pNxt->m_pNext = std::move(pPrev->m_pNext);
+ pPrev->m_pNext.reset(pNxt);
+ pConstr = this;
+ }
+ else
+ {
+ pNxt->m_pNext.reset(this);
+ pConstr = pNxt;
+ }
+
+ return pConstr;
+}
+
+SwHTMLTableLayout::SwHTMLTableLayout( const SwTable * pTable,
+ sal_uInt16 nRws, sal_uInt16 nCls,
+ bool bColsOpt, bool bColTgs,
+ sal_uInt16 nWdth, bool bPercentWdth,
+ sal_uInt16 nBorderOpt, sal_uInt16 nCellPad,
+ sal_uInt16 nCellSp, SvxAdjust eAdjust,
+ sal_uInt16 nLMargin, sal_uInt16 nRMargin,
+ sal_uInt16 nBWidth, sal_uInt16 nLeftBWidth,
+ sal_uInt16 nRightBWidth )
+ : m_aResizeTimer("SwHTMLTableLayout m_aResizeTimer")
+ , m_aColumns( nCls )
+ , m_aCells( static_cast<size_t>(nRws)*nCls )
+ , m_pSwTable( pTable )
+ , m_nMin( 0 )
+ , m_nMax( 0 )
+ , m_nRows( nRws )
+ , m_nCols( nCls )
+ , m_nLeftMargin( nLMargin )
+ , m_nRightMargin( nRMargin )
+ , m_nInhAbsLeftSpace( 0 )
+ , m_nInhAbsRightSpace( 0 )
+ , m_nRelLeftFill( 0 )
+ , m_nRelRightFill( 0 )
+ , m_nRelTabWidth( 0 )
+ , m_nWidthOption( nWdth )
+ , m_nCellPadding( nCellPad )
+ , m_nCellSpacing( nCellSp )
+ , m_nBorder( nBorderOpt )
+ , m_nLeftBorderWidth( nLeftBWidth )
+ , m_nRightBorderWidth( nRightBWidth )
+ , m_nInhLeftBorderWidth( 0 )
+ , m_nInhRightBorderWidth( 0 )
+ , m_nBorderWidth( nBWidth )
+ , m_nDelayedResizeAbsAvail( 0 )
+ , m_nLastResizeAbsAvail( 0 )
+ , m_nPass1Done( 0 )
+ , m_nWidthSet( 0 )
+ , m_eTableAdjust( eAdjust )
+ , m_bColsOption( bColsOpt )
+ , m_bColTags( bColTgs )
+ , m_bPercentWidthOption( bPercentWdth )
+ , m_bUseRelWidth( false )
+ , m_bMustResize( true )
+ , m_bExportable( true )
+ , m_bBordersChanged( false )
+ , m_bMayBeInFlyFrame( false )
+ , m_bDelayedResizeRecalc( false)
+ , m_bMustNotResize( false )
+ , m_bMustNotRecalc( false )
+{
+ m_aResizeTimer.SetInvokeHandler( LINK( this, SwHTMLTableLayout,
+ DelayedResize_Impl ) );
+}
+
+SwHTMLTableLayout::~SwHTMLTableLayout()
+{
+}
+
+/// The border widths are calculated like in Netscape:
+/// Outer border: BORDER + CELLSPACING + CELLPADDING
+/// Inner border: CELLSPACING + CELLPADDING
+/// However, we respect the border widths in SW if bSwBorders is set,
+/// so that we don't wrap wrongly.
+/// We also need to respect the distance to the content. Even if
+/// only the opposite side has a border.
+sal_uInt16 SwHTMLTableLayout::GetLeftCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
+ bool bSwBorders ) const
+{
+ sal_uInt16 nSpace = m_nCellSpacing + m_nCellPadding;
+
+ if( nCol == 0 )
+ {
+ nSpace = nSpace + m_nBorder;
+
+ if( bSwBorders && nSpace < m_nLeftBorderWidth )
+ nSpace = m_nLeftBorderWidth;
+ }
+ else if( bSwBorders )
+ {
+ if( GetColumn(nCol)->HasLeftBorder() )
+ {
+ if( nSpace < m_nBorderWidth )
+ nSpace = m_nBorderWidth;
+ }
+ else if( nCol+nColSpan == m_nCols && m_nRightBorderWidth &&
+ nSpace < MIN_BORDER_DIST )
+ {
+ OSL_ENSURE( !m_nCellPadding, "GetLeftCellSpace: CELLPADDING!=0" );
+ // If the opposite side has a border we need to respect at
+ // least the minimum distance to the content.
+ // Additionally, we could also use nCellPadding for this.
+ nSpace = MIN_BORDER_DIST;
+ }
+ }
+
+ return nSpace;
+}
+
+sal_uInt16 SwHTMLTableLayout::GetRightCellSpace( sal_uInt16 nCol, sal_uInt16 nColSpan,
+ bool bSwBorders ) const
+{
+ sal_uInt16 nSpace = m_nCellPadding;
+
+ if( nCol+nColSpan == m_nCols )
+ {
+ nSpace += m_nBorder + m_nCellSpacing;
+ if( bSwBorders && nSpace < m_nRightBorderWidth )
+ nSpace = m_nRightBorderWidth;
+ }
+ else if( bSwBorders && GetColumn(nCol)->HasLeftBorder() &&
+ nSpace < MIN_BORDER_DIST )
+ {
+ OSL_ENSURE( !m_nCellPadding, "GetRightCellSpace: CELLPADDING!=0" );
+ // If the opposite side has a border we need to respect at
+ // least the minimum distance to the content.
+ // Additionally, we could also use nCellPadding for this.
+ nSpace = MIN_BORDER_DIST;
+ }
+
+ return nSpace;
+}
+
+void SwHTMLTableLayout::AddBorderWidth( sal_uLong &rMin, sal_uLong &rMax,
+ sal_uLong &rAbsMin,
+ sal_uInt16 nCol, sal_uInt16 nColSpan,
+ bool bSwBorders ) const
+{
+ sal_uLong nAdd = GetLeftCellSpace( nCol, nColSpan, bSwBorders ) +
+ GetRightCellSpace( nCol, nColSpan, bSwBorders );
+
+ rMin += nAdd;
+ rMax += nAdd;
+ rAbsMin += nAdd;
+}
+
+void SwHTMLTableLayout::SetBoxWidth( SwTableBox *pBox, sal_uInt16 nCol,
+ sal_uInt16 nColSpan ) const
+{
+ SwFrameFormat *pFrameFormat = pBox->GetFrameFormat();
+
+ // calculate the box's width
+ SwTwips nFrameWidth = 0;
+ while( nColSpan-- )
+ nFrameWidth += GetColumn( nCol++ )->GetRelColWidth();
+
+ // and reset
+ pFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nFrameWidth, 0 ));
+}
+
+void SwHTMLTableLayout::GetAvail( sal_uInt16 nCol, sal_uInt16 nColSpan,
+ sal_uInt16& rAbsAvail, sal_uInt16& rRelAvail ) const
+{
+ rAbsAvail = 0;
+ rRelAvail = 0;
+ for( sal_uInt16 i=nCol; i<nCol+nColSpan;i++ )
+ {
+ const SwHTMLTableLayoutColumn *pColumn = GetColumn(i);
+ rAbsAvail = rAbsAvail + pColumn->GetAbsColWidth();
+ rRelAvail = rRelAvail + pColumn->GetRelColWidth();
+ }
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByVisArea( const SwDoc& rDoc )
+{
+ SwViewShell const *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pVSh )
+ {
+ return o3tl::narrowing<sal_uInt16>(pVSh->GetBrowseWidth());
+ }
+
+ return 0;
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidth( const SwDoc& rDoc )
+{
+ // If we have a layout, we can get the width from there.
+ const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRootFrame )
+ {
+ const SwFrame *pPageFrame = pRootFrame->GetLower();
+ if( pPageFrame )
+ return o3tl::narrowing<sal_uInt16>(pPageFrame->getFramePrintArea().Width());
+ }
+
+ // #i91658#
+ // Assertion removed which state that no browse width is available.
+ // Investigation reveals that all calls can handle the case that no browse
+ // width is provided.
+ return GetBrowseWidthByVisArea( rDoc );
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTabFrame(
+ const SwTabFrame& rTabFrame ) const
+{
+ SwTwips nWidth = 0;
+
+ const SwFrame *pUpper = rTabFrame.GetUpper();
+ if( MayBeInFlyFrame() && pUpper->IsFlyFrame() &&
+ static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame() )
+ {
+ // If the table is located within a self-created frame, the anchor's
+ // width is relevant not the frame's width.
+ // For paragraph-bound frames we don't respect paragraph indents.
+ const SwFrame *pAnchor = static_cast<const SwFlyFrame *>(pUpper)->GetAnchorFrame();
+ if( pAnchor->IsTextFrame() )
+ nWidth = pAnchor->getFrameArea().Width();
+ else
+ nWidth = pAnchor->getFramePrintArea().Width();
+ }
+ else
+ {
+ nWidth = pUpper->getFramePrintArea().Width();
+ }
+
+ SwTwips nUpperDummy = 0;
+ tools::Long nRightOffset = 0,
+ nLeftOffset = 0;
+ rTabFrame.CalcFlyOffsets(nUpperDummy, nLeftOffset, nRightOffset, nullptr);
+ nWidth -= (nLeftOffset + nRightOffset);
+
+ return o3tl::narrowing<sal_uInt16>(std::min(nWidth, SwTwips(SAL_MAX_UINT16)));
+}
+
+sal_uInt16 SwHTMLTableLayout::GetBrowseWidthByTable( const SwDoc& rDoc ) const
+{
+ sal_uInt16 nBrowseWidth = 0;
+ SwTabFrame* pFrame = SwIterator<SwTabFrame,SwFormat>( *m_pSwTable->GetFrameFormat() ).First();
+ if( pFrame )
+ {
+ nBrowseWidth = GetBrowseWidthByTabFrame( *pFrame );
+ }
+ else
+ {
+ nBrowseWidth = SwHTMLTableLayout::GetBrowseWidth( rDoc );
+ }
+
+ return nBrowseWidth;
+}
+
+const SwStartNode *SwHTMLTableLayout::GetAnyBoxStartNode() const
+{
+ const SwStartNode *pBoxSttNd;
+
+ const SwTableBox* pBox = m_pSwTable->GetTabLines()[0]->GetTabBoxes()[0];
+ while( nullptr == (pBoxSttNd = pBox->GetSttNd()) )
+ {
+ OSL_ENSURE( !pBox->GetTabLines().empty(),
+ "Box without start node and lines" );
+ OSL_ENSURE( !pBox->GetTabLines().front()->GetTabBoxes().empty(),
+ "Line without boxes" );
+ pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
+ }
+
+ return pBoxSttNd;
+}
+
+SwFrameFormat *SwHTMLTableLayout::FindFlyFrameFormat() const
+{
+ const SwTableNode *pTableNd = GetAnyBoxStartNode()->FindTableNode();
+ OSL_ENSURE( pTableNd, "No Table-Node?" );
+ return pTableNd->GetFlyFormat();
+}
+
+static void lcl_GetMinMaxSize( sal_uLong& rMinNoAlignCnts, sal_uLong& rMaxNoAlignCnts,
+ sal_uLong& rAbsMinNoAlignCnts,
+ SwTextNode const *pTextNd, SwNodeOffset nIdx, bool bNoBreak )
+{
+ pTextNd->GetMinMaxSize( nIdx, rMinNoAlignCnts, rMaxNoAlignCnts,
+ rAbsMinNoAlignCnts );
+ OSL_ENSURE( rAbsMinNoAlignCnts <= rMinNoAlignCnts,
+ "GetMinMaxSize: absmin > min" );
+ OSL_ENSURE( rMinNoAlignCnts <= rMaxNoAlignCnts,
+ "GetMinMaxSize: max > min" );
+
+ // The maximal width for a <PRE> paragraph is the minimal width
+ const SwFormatColl *pColl = &pTextNd->GetAnyFormatColl();
+ while( pColl && !pColl->IsDefault() &&
+ (USER_FMT & pColl->GetPoolFormatId()) )
+ {
+ pColl = static_cast<const SwFormatColl *>(pColl->DerivedFrom());
+ }
+
+ // <NOBR> in the whole cell apply to text but not to tables.
+ // Netscape only considers this for graphics.
+ if( (pColl && RES_POOLCOLL_HTML_PRE==pColl->GetPoolFormatId()) || bNoBreak )
+ {
+ rMinNoAlignCnts = rMaxNoAlignCnts;
+ rAbsMinNoAlignCnts = rMaxNoAlignCnts;
+ }
+}
+
+void SwHTMLTableLayout::AutoLayoutPass1()
+{
+ m_nPass1Done++;
+
+ m_nMin = m_nMax = 0; // clear pass1 info
+
+ bool bFixRelWidths = false;
+ sal_uInt16 i;
+
+ std::unique_ptr<SwHTMLTableLayoutConstraints> xConstraints;
+
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ pColumn->ClearPass1Info( !HasColTags() );
+ sal_uInt16 nMinColSpan = USHRT_MAX; // Column count to which the calculated width refers to
+ sal_uInt16 nColSkip = USHRT_MAX; // How many columns need to be skipped
+
+ for( sal_uInt16 j=0; j<m_nRows; j++ )
+ {
+ SwHTMLTableLayoutCell *pCell = GetCell(j,i);
+ SwHTMLTableLayoutCnts *pCnts = pCell->GetContents().get();
+
+ // We need to examine all rows in order to
+ // get the column that should be calculated next.
+ sal_uInt16 nColSpan = pCell->GetColSpan();
+ if( nColSpan < nColSkip )
+ nColSkip = nColSpan;
+
+ if( !pCnts || !pCnts->IsPass1Done(m_nPass1Done) )
+ {
+ // The cell is empty or it's content was not edited
+ if( nColSpan < nMinColSpan )
+ nMinColSpan = nColSpan;
+
+ sal_uLong nMinNoAlignCell = 0;
+ sal_uLong nMaxNoAlignCell = 0;
+ sal_uLong nAbsMinNoAlignCell = 0;
+ sal_uLong nMaxTableCell = 0;
+ sal_uLong nAbsMinTableCell = 0;
+
+ while( pCnts )
+ {
+ const SwStartNode *pSttNd = pCnts->GetStartNode();
+ if( pSttNd )
+ {
+ const SwDoc& rDoc = pSttNd->GetDoc();
+ SwNodeOffset nIdx = pSttNd->GetIndex();
+ while (!rDoc.GetNodes()[nIdx]->IsEndNode())
+ {
+ SwTextNode *pTextNd = (rDoc.GetNodes()[nIdx])->GetTextNode();
+ if( pTextNd )
+ {
+ sal_uLong nMinNoAlignCnts = 0;
+ sal_uLong nMaxNoAlignCnts = 0;
+ sal_uLong nAbsMinNoAlignCnts = 0;
+
+ lcl_GetMinMaxSize( nMinNoAlignCnts,
+ nMaxNoAlignCnts,
+ nAbsMinNoAlignCnts,
+ pTextNd, nIdx,
+ pCnts->HasNoBreakTag() );
+
+ if( nMinNoAlignCnts > nMinNoAlignCell )
+ nMinNoAlignCell = nMinNoAlignCnts;
+ if( nMaxNoAlignCnts > nMaxNoAlignCell )
+ nMaxNoAlignCell = nMaxNoAlignCnts;
+ if( nAbsMinNoAlignCnts > nAbsMinNoAlignCell )
+ nAbsMinNoAlignCell = nAbsMinNoAlignCnts;
+ }
+ else
+ {
+ SwTableNode *pTabNd = (rDoc.GetNodes()[nIdx])->GetTableNode();
+ if( pTabNd )
+ {
+ SwHTMLTableLayout *pChild = pTabNd->GetTable().GetHTMLTableLayout();
+ if( pChild )
+ {
+ pChild->AutoLayoutPass1();
+ sal_uLong nMaxTableCnts = pChild->m_nMax;
+ sal_uLong nAbsMinTableCnts = pChild->m_nMin;
+
+ // A fixed table width is taken over as minimum and
+ // maximum at the same time
+ if( !pChild->m_bPercentWidthOption && pChild->m_nWidthOption )
+ {
+ sal_uLong nTabWidth = pChild->m_nWidthOption;
+ if( nTabWidth >= nAbsMinTableCnts )
+ {
+ nMaxTableCnts = nTabWidth;
+ nAbsMinTableCnts = nTabWidth;
+ }
+ else
+ {
+ nMaxTableCnts = nAbsMinTableCnts;
+ }
+ }
+
+ if( nMaxTableCnts > nMaxTableCell )
+ nMaxTableCell = nMaxTableCnts;
+ if( nAbsMinTableCnts > nAbsMinTableCell )
+ nAbsMinTableCell = nAbsMinTableCnts;
+ }
+ nIdx = pTabNd->EndOfSectionNode()->GetIndex();
+ }
+ }
+ nIdx++;
+ }
+ }
+ else if (SwHTMLTableLayout *pChild = pCnts->GetTable())
+ {
+ OSL_ENSURE( false, "Sub tables in HTML import?" );
+ pChild->AutoLayoutPass1();
+ sal_uLong nMaxTableCnts = pChild->m_nMax;
+ sal_uLong nAbsMinTableCnts = pChild->m_nMin;
+
+ // A fixed table width is taken over as minimum and
+ // maximum at the same time
+ if( !pChild->m_bPercentWidthOption && pChild->m_nWidthOption )
+ {
+ sal_uLong nTabWidth = pChild->m_nWidthOption;
+ if( nTabWidth >= nAbsMinTableCnts )
+ {
+ nMaxTableCnts = nTabWidth;
+ nAbsMinTableCnts = nTabWidth;
+ }
+ else
+ {
+ nMaxTableCnts = nAbsMinTableCnts;
+ }
+ }
+
+ if( nMaxTableCnts > nMaxTableCell )
+ nMaxTableCell = nMaxTableCnts;
+ if( nAbsMinTableCnts > nAbsMinTableCell )
+ nAbsMinTableCell = nAbsMinTableCnts;
+ }
+ pCnts->SetPass1Done( m_nPass1Done );
+ pCnts = pCnts->GetNext().get();
+ }
+
+// This code previously came after AddBorderWidth
+ // If a table's width is wider in a cell than what we've calculated
+ // for the other content we need to use the table's width.
+ if( nMaxTableCell > nMaxNoAlignCell )
+ nMaxNoAlignCell = nMaxTableCell;
+ if( nAbsMinTableCell > nAbsMinNoAlignCell )
+ {
+ nAbsMinNoAlignCell = nAbsMinTableCell;
+ if( nMinNoAlignCell < nAbsMinNoAlignCell )
+ nMinNoAlignCell = nAbsMinNoAlignCell;
+ if( nMaxNoAlignCell < nMinNoAlignCell )
+ nMaxNoAlignCell = nMinNoAlignCell;
+ }
+// This code previously came after AddBorderWidth
+
+ bool bRelWidth = pCell->IsPercentWidthOption();
+ sal_uInt16 nWidth = pCell->GetWidthOption();
+
+ // A NOWRAP option applies to text and tables, but is
+ // not applied for fixed cell width.
+ // Instead, the stated cell width behaves like a minimal
+ // width.
+ if( pCell->HasNoWrapOption() )
+ {
+ if( nWidth==0 || bRelWidth )
+ {
+ nMinNoAlignCell = nMaxNoAlignCell;
+ nAbsMinNoAlignCell = nMaxNoAlignCell;
+ }
+ else
+ {
+ if( nWidth>nMinNoAlignCell )
+ nMinNoAlignCell = nWidth;
+ if( nWidth>nAbsMinNoAlignCell )
+ nAbsMinNoAlignCell = nWidth;
+ }
+ }
+
+ // Respect minimum width for content
+ if( nMinNoAlignCell < MINLAY )
+ nMinNoAlignCell = MINLAY;
+ if( nMaxNoAlignCell < MINLAY )
+ nMaxNoAlignCell = MINLAY;
+ if( nAbsMinNoAlignCell < MINLAY )
+ nAbsMinNoAlignCell = MINLAY;
+
+ // Respect the border and distance to the content
+ AddBorderWidth( nMinNoAlignCell, nMaxNoAlignCell,
+ nAbsMinNoAlignCell, i, nColSpan );
+
+ if( 1==nColSpan )
+ {
+ // take over the values directly
+ pColumn->MergeMinMaxNoAlign( nMinNoAlignCell,
+ nMaxNoAlignCell,
+ nAbsMinNoAlignCell );
+
+ // the widest WIDTH wins
+ if( !HasColTags() )
+ pColumn->MergeCellWidthOption( nWidth, bRelWidth );
+ }
+ else
+ {
+ // Process the data line by line from left to right at the end
+
+ // When which values is taken over will be explained further down.
+ if( !HasColTags() && nWidth && !bRelWidth )
+ {
+ sal_uLong nAbsWidth = nWidth, nDummy = 0, nDummy2 = 0;
+ AddBorderWidth( nAbsWidth, nDummy, nDummy2,
+ i, nColSpan, false );
+
+ if( nAbsWidth >= nMinNoAlignCell )
+ {
+ nMaxNoAlignCell = nAbsWidth;
+ if( HasColsOption() )
+ nMinNoAlignCell = nAbsWidth;
+ }
+ else if( nAbsWidth >= nAbsMinNoAlignCell )
+ {
+ nMaxNoAlignCell = nAbsWidth;
+ nMinNoAlignCell = nAbsWidth;
+ }
+ else
+ {
+ nMaxNoAlignCell = nAbsMinNoAlignCell;
+ nMinNoAlignCell = nAbsMinNoAlignCell;
+ }
+ }
+ else if( HasColsOption() || HasColTags() )
+ nMinNoAlignCell = nAbsMinNoAlignCell;
+
+ SwHTMLTableLayoutConstraints *pConstr =
+ new SwHTMLTableLayoutConstraints( nMinNoAlignCell,
+ nMaxNoAlignCell, j, i, nColSpan );
+ if (xConstraints)
+ {
+ SwHTMLTableLayoutConstraints* pConstraints = xConstraints->InsertNext(pConstr);
+ xConstraints.release();
+ xConstraints.reset(pConstraints);
+ }
+ else
+ xConstraints.reset(pConstr);
+ }
+ }
+ }
+
+ OSL_ENSURE( nMinColSpan>0 && nColSkip>0 && nColSkip <= nMinColSpan,
+ "Layout pass 1: Columns are being forgotten!" );
+ OSL_ENSURE( nMinColSpan!=USHRT_MAX,
+ "Layout pass 1: unnecessary pass through the loop or a bug" );
+
+ if( 1==nMinColSpan )
+ {
+ // There are cells with COLSPAN 1 and therefore also useful
+ // values in pColumn
+
+ // Take over values according to the following table (Netscape 4.0 pv 3):
+
+ // WIDTH: no COLS COLS
+
+ // none min = min min = absmin
+ // max = max max = max
+
+ // >= min min = min min = width
+ // max = width max = width
+
+ // >= absmin min = width(*) min = width
+ // max = width max = width
+
+ // < absmin min = absmin min = absmin
+ // max = absmin max = absmin
+
+ // (*) Netscape uses the minimum width without a break before
+ // the last graphic here. We don't have that (yet?),
+ // so we leave it set to width.
+
+ if( pColumn->GetWidthOption() && !pColumn->IsRelWidthOption() )
+ {
+ // Take over absolute widths as minimal and maximal widths.
+ sal_uLong nAbsWidth = pColumn->GetWidthOption();
+ sal_uLong nDummy = 0, nDummy2 = 0;
+ AddBorderWidth( nAbsWidth, nDummy, nDummy2, i, 1, false );
+
+ if( nAbsWidth >= pColumn->GetMinNoAlign() )
+ {
+ pColumn->SetMinMax( HasColsOption() ? nAbsWidth
+ : pColumn->GetMinNoAlign(),
+ nAbsWidth );
+ }
+ else if( nAbsWidth >= pColumn->GetAbsMinNoAlign() )
+ {
+ pColumn->SetMinMax( nAbsWidth, nAbsWidth );
+ }
+ else
+ {
+ pColumn->SetMinMax( pColumn->GetAbsMinNoAlign(),
+ pColumn->GetAbsMinNoAlign() );
+ }
+ }
+ else
+ {
+ pColumn->SetMinMax( HasColsOption() ? pColumn->GetAbsMinNoAlign()
+ : pColumn->GetMinNoAlign(),
+ pColumn->GetMaxNoAlign() );
+ }
+ }
+ else if( USHRT_MAX!=nMinColSpan )
+ {
+ // Can be anything != 0, because it is altered by the constraints.
+ pColumn->SetMinMax( MINLAY, MINLAY );
+
+ // the next columns need not to be processed
+ i += (nColSkip-1);
+ }
+
+ m_nMin += pColumn->GetMin();
+ m_nMax += pColumn->GetMax();
+ if (pColumn->IsRelWidthOption()) bFixRelWidths = true;
+ }
+
+ // Now process the constraints
+ SwHTMLTableLayoutConstraints *pConstr = xConstraints.get();
+ while( pConstr )
+ {
+ // At first we need to process the width in the same way
+ // as the column widths
+ sal_uInt16 nCol = pConstr->GetColumn();
+ sal_uInt16 nColSpan = pConstr->GetColSpan();
+ sal_uLong nConstrMin = pConstr->GetMinNoAlign();
+ sal_uLong nConstrMax = pConstr->GetMaxNoAlign();
+
+ // We get the hitherto width of the spanned columns
+ sal_uLong nColsMin = 0;
+ sal_uLong nColsMax = 0;
+ for( sal_uInt16 j=nCol; j<nCol+nColSpan; j++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( j );
+ nColsMin += pColumn->GetMin();
+ nColsMax += pColumn->GetMax();
+ }
+
+ if( nColsMin<nConstrMin )
+ {
+ // Proportionately distribute the minimum value to the columns
+ sal_uLong nMinD = nConstrMin-nColsMin;
+
+ if( nConstrMin > nColsMax )
+ {
+ // Proportional according to the minimum widths
+ sal_uInt16 nEndCol = nCol+nColSpan;
+ sal_uLong nDiff = nMinD;
+ for( sal_uInt16 ic=nCol; ic<nEndCol; ic++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
+
+ sal_uLong nColMin = pColumn->GetMin();
+ sal_uLong nColMax = pColumn->GetMax();
+
+ m_nMin -= nColMin;
+ sal_uLong nAdd;
+ if (ic < nEndCol-1)
+ {
+ if (nColsMin == 0)
+ throw o3tl::divide_by_zero();
+ nAdd = (nColMin * nMinD) / nColsMin;
+ }
+ else
+ {
+ nAdd = nDiff;
+ }
+ nColMin += nAdd;
+ m_nMin += nColMin;
+ OSL_ENSURE( nDiff >= nAdd, "Ooops: nDiff is not correct anymore" );
+ nDiff -= nAdd;
+
+ if( nColMax < nColMin )
+ {
+ m_nMax -= nColMax;
+ nColsMax -= nColMax;
+ nColMax = nColMin;
+ m_nMax += nColMax;
+ nColsMax += nColMax;
+ }
+
+ pColumn->SetMinMax( nColMin, nColMax );
+ }
+ }
+ else
+ {
+ // Proportional according to the difference of max and min
+ for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
+
+ sal_uLong nDiff = pColumn->GetMax()-pColumn->GetMin();
+ if( nMinD < nDiff )
+ nDiff = nMinD;
+
+ pColumn->AddToMin( nDiff );
+
+ OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
+ "Why is the Column suddenly too narrow?" );
+
+ m_nMin += nDiff;
+ nMinD -= nDiff;
+ }
+ }
+ }
+
+ if( !HasColTags() && nColsMax<nConstrMax )
+ {
+ sal_uLong nMaxD = nConstrMax-nColsMax;
+
+ for( sal_uInt16 ic=nCol; ic<nCol+nColSpan; ic++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( ic );
+
+ m_nMax -= pColumn->GetMax();
+
+ pColumn->AddToMax( (pColumn->GetMax() * nMaxD) / nColsMax );
+
+ m_nMax += pColumn->GetMax();
+ }
+ }
+
+ pConstr = pConstr->GetNext();
+ }
+
+ if( !bFixRelWidths )
+ return;
+
+ if( HasColTags() )
+ {
+ // To adapt the relative widths, in a first step we multiply the
+ // minimum width of all affected cells with the relative width
+ // of the column.
+ // Thus, the width ratio among the columns is correct.
+
+ // Furthermore, a factor is calculated that says by how much the
+ // cell has gotten wider than the minimum width.
+
+ // In the second step the calculated widths are divided by this
+ // factor. Thereby a cell's width is preserved and serves as a
+ // basis for the other cells.
+ // We only change the maximum widths here!
+
+ sal_uLong nAbsMin = 0; // absolute minimum width of all widths with relative width
+ sal_uLong nRel = 0; // sum of all relative widths of all columns
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
+ {
+ nAbsMin += pColumn->GetMin();
+ nRel += pColumn->GetWidthOption();
+ }
+ }
+
+ sal_uLong nQuot = ULONG_MAX;
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() )
+ {
+ m_nMax -= pColumn->GetMax();
+ if( pColumn->GetWidthOption() && pColumn->GetMin() )
+ {
+ pColumn->SetMax( nAbsMin * pColumn->GetWidthOption() );
+ sal_uLong nColQuot = pColumn->GetMax() / pColumn->GetMin();
+ if( nColQuot<nQuot )
+ nQuot = nColQuot;
+ }
+ }
+ }
+ OSL_ENSURE( 0==nRel || nQuot!=ULONG_MAX,
+ "Where did the relative columns go?" );
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() )
+ {
+ if( pColumn->GetWidthOption() )
+ pColumn->SetMax( pColumn->GetMax() / nQuot );
+ else
+ pColumn->SetMax( pColumn->GetMin() );
+ OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
+ "Maximum column width is lower than the minimum column width" );
+ m_nMax += pColumn->GetMax();
+ }
+ }
+ }
+ else
+ {
+ sal_uInt16 nRel = 0; // sum of the relative widths of all columns
+ sal_uInt16 nRelCols = 0; // count of the columns with a relative setting
+ sal_uLong nRelMax = 0; // fraction of the maximum of this column
+ for( i=0; i<m_nCols; i++ )
+ {
+ OSL_ENSURE( nRel<=100, "relative width of all columns > 100%" );
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
+ {
+ // Make sure that the relative widths don't go above 100%
+ sal_uInt16 nColWidth = pColumn->GetWidthOption();
+ if( nRel+nColWidth > 100 )
+ {
+ nColWidth = 100 - nRel;
+ pColumn->SetWidthOption( nColWidth );
+ }
+ nRelMax += pColumn->GetMax();
+ nRel = nRel + nColWidth;
+ nRelCols++;
+ }
+ else if( !pColumn->GetMin() )
+ {
+ // The column is empty (so it was solely created by
+ // COLSPAN) and therefore must not be assigned a % width.
+ nRelCols++;
+ }
+ }
+
+ // If there are percentages left we distribute them to the columns
+ // that don't have a width setting. Like in Netscape we distribute
+ // the remaining percentages according to the ratio of the maximum
+ // width of the affected columns.
+ // For the maximum widths we also take the fixed-width columns
+ // into account. Is that correct?
+ sal_uLong nFixMax = 0;
+ if( nRel < 100 && nRelCols < m_nCols )
+ {
+ nFixMax = m_nMax - nRelMax;
+ SAL_WARN_IF(!nFixMax, "sw.core", "bad fixed width max");
+ }
+ if (nFixMax)
+ {
+ sal_uInt16 nRelLeft = 100 - nRel;
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( !pColumn->IsRelWidthOption() &&
+ !pColumn->GetWidthOption() &&
+ pColumn->GetMin() )
+ {
+ // the next column gets the rest
+ sal_uInt16 nColWidth =
+ o3tl::narrowing<sal_uInt16>((pColumn->GetMax() * nRelLeft) / nFixMax);
+ pColumn->SetWidthOption( nColWidth );
+ }
+ }
+ }
+
+ // adjust the maximum widths now accordingly
+ sal_uLong nQuotMax = ULONG_MAX;
+ sal_uLong nOldMax = m_nMax;
+ m_nMax = 0;
+ for( i=0; i<m_nCols; i++ )
+ {
+ // Columns with a % setting are adapted accordingly.
+ // Columns, that
+ // - do not have a % setting and are located within a tables
+ // with COLS and WIDTH, or
+ // - their width is 0%
+ // get set to the minimum width.
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() )
+ {
+ sal_uLong nNewMax;
+ sal_uLong nColQuotMax;
+ if( !m_nWidthOption )
+ {
+ nNewMax = nOldMax * pColumn->GetWidthOption();
+ nColQuotMax = nNewMax / pColumn->GetMax();
+ }
+ else
+ {
+ nNewMax = m_nMin * pColumn->GetWidthOption();
+ nColQuotMax = nNewMax / pColumn->GetMin();
+ }
+ pColumn->SetMax( nNewMax );
+ if( nColQuotMax < nQuotMax )
+ nQuotMax = nColQuotMax;
+ }
+ else if( HasColsOption() || m_nWidthOption ||
+ (pColumn->IsRelWidthOption() &&
+ !pColumn->GetWidthOption()) )
+ pColumn->SetMax( pColumn->GetMin() );
+ }
+ // and divide by the quotient
+ SAL_WARN_IF(!nQuotMax, "sw.core", "Where did the relative columns go?");
+ for (i = 0; i < m_nCols; ++i)
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if (pColumn->IsRelWidthOption() && pColumn->GetWidthOption() && nQuotMax)
+ {
+ pColumn->SetMax( pColumn->GetMax() / nQuotMax );
+ OSL_ENSURE( pColumn->GetMax() >= pColumn->GetMin(),
+ "Minimum width is one column bigger than maximum" );
+ if( pColumn->GetMax() < pColumn->GetMin() )
+ pColumn->SetMax( pColumn->GetMin() );
+ }
+ m_nMax += pColumn->GetMax();
+ }
+ }
+}
+
+//TODO: provide documentation
+/**
+
+ @param nAbsAvail available space in TWIPS.
+ @param nRelAvail available space related to USHRT_MAX or 0
+ @param nAbsSpace fraction of nAbsAvail, which is reserved by the surrounding
+ cell for the border and the distance to the paragraph.
+*/
+void SwHTMLTableLayout::AutoLayoutPass2( sal_uInt16 nAbsAvail, sal_uInt16 nRelAvail,
+ sal_uInt16 nAbsLeftSpace,
+ sal_uInt16 nAbsRightSpace,
+ sal_uInt16 nParentInhAbsSpace )
+{
+ // For a start we do a lot of plausibility tests
+
+ // An absolute width always has to be passed
+ OSL_ENSURE( nAbsAvail, "AutoLayout pass 2: No absolute width given" );
+
+ // A relative width must only be passed for tables within tables (?)
+ OSL_ENSURE( IsTopTable() == (nRelAvail==0),
+ "AutoLayout pass 2: Relative width at table in table or the other way around" );
+
+ // The table's minimum width must not be bigger than its maximum width
+ OSL_ENSURE( m_nMin<=m_nMax, "AutoLayout pass 2: nMin > nMax" );
+
+ // Remember the available width for which the table was calculated.
+ // This is a good place as we pass by here for the initial calculation
+ // of the table in the parser and for each Resize_ call.
+ m_nLastResizeAbsAvail = nAbsAvail;
+
+ // Step 1: The available space is readjusted for the left/right border,
+ // possibly existing filler cells and distances.
+
+ // Distance to the content and border
+ sal_uInt16 nAbsLeftFill = 0, nAbsRightFill = 0;
+ if( !IsTopTable() &&
+ GetMin() + nAbsLeftSpace + nAbsRightSpace <= nAbsAvail )
+ {
+ nAbsLeftFill = nAbsLeftSpace;
+ nAbsRightFill = nAbsRightSpace;
+ }
+
+ // Left and right distance
+ if( m_nLeftMargin || m_nRightMargin )
+ {
+ if( IsTopTable() )
+ {
+ // For the top table we always respect the borders, because we
+ // never go below the table's minimum width.
+ nAbsAvail -= (m_nLeftMargin + m_nRightMargin);
+ }
+ else if( GetMin() + m_nLeftMargin + m_nRightMargin <= nAbsAvail )
+ {
+ // Else, we only respect the borders if there's space available
+ // for them (nMin has already been calculated!)
+ nAbsLeftFill = nAbsLeftFill + m_nLeftMargin;
+ nAbsRightFill = nAbsRightFill + m_nRightMargin;
+ }
+ }
+
+ // Read just the available space
+ m_nRelLeftFill = 0;
+ m_nRelRightFill = 0;
+ if( !IsTopTable() && (nAbsLeftFill>0 || nAbsRightFill) )
+ {
+ sal_uLong nAbsLeftFillL = nAbsLeftFill, nAbsRightFillL = nAbsRightFill;
+
+ m_nRelLeftFill = o3tl::narrowing<sal_uInt16>((nAbsLeftFillL * nRelAvail) / nAbsAvail);
+ m_nRelRightFill = o3tl::narrowing<sal_uInt16>((nAbsRightFillL * nRelAvail) / nAbsAvail);
+
+ nAbsAvail -= (nAbsLeftFill + nAbsRightFill);
+ if( nRelAvail )
+ nRelAvail -= (m_nRelLeftFill + m_nRelRightFill);
+ }
+
+ // Step 2: Calculate the absolute table width.
+ sal_uInt16 nAbsTabWidth = 0;
+ m_bUseRelWidth = false;
+ if( m_nWidthOption )
+ {
+ if( m_bPercentWidthOption )
+ {
+ OSL_ENSURE( m_nWidthOption<=100, "Percentage value too high" );
+ if( m_nWidthOption > 100 )
+ m_nWidthOption = 100;
+
+ // The absolute width is equal to the given percentage of
+ // the available width.
+ // Top tables only get a relative width if the available space
+ // is *strictly larger* than the minimum width.
+
+ // CAUTION: We need the "strictly larger" because changing from a
+ // relative width to an absolute width by resizing would lead
+ // to an infinite loop.
+
+ // Because we do not call resize for tables in frames if the
+ // frame has a non-relative width, we cannot play such games.
+
+ // Let's play such games now anyway. We had a graphic in a 1% wide
+ // table and it didn't fit in of course.
+ nAbsTabWidth = o3tl::narrowing<sal_uInt16>( (static_cast<sal_uLong>(nAbsAvail) * m_nWidthOption) / 100 );
+ if( IsTopTable() &&
+ ( /*MayBeInFlyFrame() ||*/ static_cast<sal_uLong>(nAbsTabWidth) > m_nMin ) )
+ {
+ nRelAvail = USHRT_MAX;
+ m_bUseRelWidth = true;
+ }
+ }
+ else
+ {
+ nAbsTabWidth = m_nWidthOption;
+ if( nAbsTabWidth > MAX_TABWIDTH )
+ nAbsTabWidth = MAX_TABWIDTH;
+
+ // Tables within tables must never get wider than the available
+ // space.
+ if( !IsTopTable() && nAbsTabWidth > nAbsAvail )
+ nAbsTabWidth = nAbsAvail;
+ }
+ }
+
+ OSL_ENSURE( IsTopTable() || nAbsTabWidth<=nAbsAvail,
+ "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for table in table" );
+ OSL_ENSURE( !nRelAvail || nAbsTabWidth<=nAbsAvail,
+ "AutoLayout pass 2: nAbsTabWidth > nAbsAvail for relative width" );
+
+ // Catch for the two asserts above (we never know!)
+ if( (!IsTopTable() || nRelAvail>0) && nAbsTabWidth>nAbsAvail )
+ nAbsTabWidth = nAbsAvail;
+
+ // Step 3: Identify the column width and, if applicable, the absolute
+ // and relative table widths.
+ if( (!IsTopTable() && m_nMin > static_cast<sal_uLong>(nAbsAvail)) ||
+ m_nMin > MAX_TABWIDTH )
+ {
+ // If
+ // - an inner table's minimum is larger than the available space, or
+ // - a top table's minimum is larger than USHORT_MAX the table
+ // has to be adapted to the available space or USHORT_MAX.
+ // We preserve the widths' ratio amongst themselves, however.
+
+ nAbsTabWidth = IsTopTable() ? MAX_TABWIDTH : nAbsAvail;
+ m_nRelTabWidth = (nRelAvail ? nRelAvail : nAbsTabWidth );
+
+ // First of all, we check whether we can fit the layout constrains,
+ // which are: Every cell's width excluding the borders must be at least
+ // MINLAY:
+
+ sal_uLong nRealMin = 0;
+ for( sal_uInt16 i=0; i<m_nCols; i++ )
+ {
+ sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
+ AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
+ nRealMin += nRealColMin;
+ }
+ if( (nRealMin >= nAbsTabWidth) || (nRealMin >= m_nMin) )
+ {
+ // "Rien ne va plus": we cannot get the minimum column widths
+ // the layout wants to have.
+
+ sal_uInt16 nAbs = 0, nRel = 0;
+ SwHTMLTableLayoutColumn *pColumn;
+ for( sal_uInt16 i=0; i<m_nCols-1; i++ )
+ {
+ pColumn = GetColumn( i );
+ sal_uLong nColMin = pColumn->GetMin();
+ if( nColMin <= USHRT_MAX )
+ {
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMin * nAbsTabWidth) / m_nMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMin * m_nRelTabWidth) / m_nMin) );
+ }
+ else
+ {
+ double nColMinD = nColMin;
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMinD * nAbsTabWidth) / m_nMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMinD * m_nRelTabWidth) / m_nMin) );
+ }
+
+ nAbs = nAbs + pColumn->GetAbsColWidth();
+ nRel = nRel + pColumn->GetRelColWidth();
+ }
+ pColumn = GetColumn( m_nCols-1 );
+ pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
+ pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
+ }
+ else
+ {
+ sal_uLong nDistAbs = nAbsTabWidth - nRealMin;
+ sal_uLong nDistRel = m_nRelTabWidth - nRealMin;
+ sal_uLong nDistMin = m_nMin - nRealMin;
+ sal_uInt16 nAbs = 0, nRel = 0;
+ SwHTMLTableLayoutColumn *pColumn;
+ for( sal_uInt16 i=0; i<m_nCols-1; i++ )
+ {
+ pColumn = GetColumn( i );
+ sal_uLong nColMin = pColumn->GetMin();
+ sal_uLong nRealColMin = MINLAY, nDummy1 = 0, nDummy2 = 0;
+ AddBorderWidth( nRealColMin, nDummy1, nDummy2, i, 1 );
+
+ if( nColMin <= USHRT_MAX )
+ {
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMin-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMin-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
+ }
+ else
+ {
+ double nColMinD = nColMin;
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMinD-nRealColMin) * nDistAbs) / nDistMin) + nRealColMin) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((((nColMinD-nRealColMin) * nDistRel) / nDistMin) + nRealColMin) );
+ }
+
+ nAbs = nAbs + pColumn->GetAbsColWidth();
+ nRel = nRel + pColumn->GetRelColWidth();
+ }
+ pColumn = GetColumn( m_nCols-1 );
+ pColumn->SetAbsColWidth( nAbsTabWidth - nAbs );
+ pColumn->SetRelColWidth( m_nRelTabWidth - nRel );
+ }
+ }
+ else if( m_nMax <= static_cast<sal_uLong>(nAbsTabWidth ? nAbsTabWidth : nAbsAvail) )
+ {
+ // If
+ // - the table has a fixed width and the table's maximum is
+ // smaller, or
+ //- the maximum is smaller than the available space,
+ // we can take over the maximum as it is. Respectively
+ // the table can only be adapted to the fixed width by
+ // respecting the maximum.
+
+ // No fixed width, use the maximum.
+ if( !nAbsTabWidth )
+ nAbsTabWidth = o3tl::narrowing<sal_uInt16>(m_nMax);
+
+ // A top table may also get wider then the available space.
+ if( nAbsTabWidth > nAbsAvail )
+ {
+ OSL_ENSURE( IsTopTable(),
+ "Table in table should get wider than the surrounding cell." );
+ nAbsAvail = nAbsTabWidth;
+ }
+
+ // Only use the relative widths' fraction, that is used for the
+ // absolute width.
+ sal_uLong nAbsTabWidthL = nAbsTabWidth;
+ if (nRelAvail)
+ {
+ if (nAbsAvail == 0)
+ throw o3tl::divide_by_zero();
+ m_nRelTabWidth = o3tl::narrowing<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
+ }
+ else
+ m_nRelTabWidth = nAbsTabWidth;
+
+ // Are there columns width a percentage setting and some without one?
+ sal_uLong nFixMax = m_nMax;
+ for( sal_uInt16 i=0; i<m_nCols; i++ )
+ {
+ const SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption()>0 )
+ nFixMax -= pColumn->GetMax();
+ }
+
+ if( nFixMax > 0 && nFixMax < m_nMax )
+ {
+ // Yes, distribute the to-be-distributed space only to the
+ // columns with a percentage setting.
+
+ // In this case (and in this case only) there are columns
+ // that exactly keep their maximum width, that is they neither
+ // get smaller nor wider. When calculating the absolute width
+ // from the relative width we can get rounding errors.
+ // To correct this, we first make the fixed widths compensate for
+ // this error. We then fix the relative widths the same way.
+
+ sal_uInt16 nAbs = 0, nRel = 0;
+ sal_uInt16 nFixedCols = 0;
+ sal_uInt16 i;
+
+ for( i = 0; i < m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( !pColumn->IsRelWidthOption() || !pColumn->GetWidthOption() )
+ {
+ // The column keeps its width.
+ nFixedCols++;
+ sal_uLong nColMax = pColumn->GetMax();
+ pColumn->SetAbsColWidth( o3tl::narrowing<sal_uInt16>(nColMax) );
+
+ sal_uLong nRelColWidth =
+ (nColMax * m_nRelTabWidth) / nAbsTabWidth;
+ sal_uLong nChkWidth =
+ (nRelColWidth * nAbsTabWidth) / m_nRelTabWidth;
+ if( nChkWidth < nColMax )
+ nRelColWidth++;
+ else if( nChkWidth > nColMax )
+ nRelColWidth--;
+ pColumn->SetRelColWidth( o3tl::narrowing<sal_uInt16>(nRelColWidth) );
+
+ nAbs = nAbs + o3tl::narrowing<sal_uInt16>(nColMax);
+ nRel = nRel + o3tl::narrowing<sal_uInt16>(nRelColWidth);
+ }
+ }
+
+ // The to-be-distributed percentage of the maximum, the
+ // relative and absolute widths. Here, nFixMax corresponds
+ // to nAbs, so that we could've called it nAbs.
+ // The code is, however, more readable like that.
+ OSL_ENSURE( nFixMax == nAbs, "Two loops, two sums?" );
+ sal_uLong nDistMax = m_nMax - nFixMax;
+ sal_uInt16 nDistAbsTabWidth = nAbsTabWidth - nAbs;
+ sal_uInt16 nDistRelTabWidth = m_nRelTabWidth - nRel;
+
+ for( i=0; i<m_nCols; i++ )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( i );
+ if( pColumn->IsRelWidthOption() && pColumn->GetWidthOption() > 0 )
+ {
+ // The column gets proportionately wider.
+ nFixedCols++;
+ if( nFixedCols == m_nCols )
+ {
+ pColumn->SetAbsColWidth( nAbsTabWidth-nAbs );
+ pColumn->SetRelColWidth( m_nRelTabWidth-nRel );
+ }
+ else
+ {
+ sal_uLong nColMax = pColumn->GetMax();
+ pColumn->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * nDistAbsTabWidth) / nDistMax) );
+ pColumn->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * nDistRelTabWidth) / nDistMax) );
+ }
+ nAbs = nAbs + pColumn->GetAbsColWidth();
+ nRel = nRel + pColumn->GetRelColWidth();
+ }
+ }
+ OSL_ENSURE( m_nCols==nFixedCols, "Missed a column!" );
+ }
+ else if (m_nCols > 0)
+ {
+ if (m_nMax == 0)
+ throw o3tl::divide_by_zero();
+ // No. So distribute the space regularly among all columns.
+ for (sal_uInt16 i=0; i < m_nCols; ++i)
+ {
+ sal_uLong nColMax = GetColumn( i )->GetMax();
+ GetColumn( i )->SetAbsColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * nAbsTabWidth) / m_nMax) );
+ GetColumn( i )->SetRelColWidth(
+ o3tl::narrowing<sal_uInt16>((nColMax * m_nRelTabWidth) / m_nMax) );
+ }
+ }
+ }
+ else
+ {
+ // Proportionately distribute the space that extends over the minimum
+ // width among the columns.
+ if( !nAbsTabWidth )
+ nAbsTabWidth = nAbsAvail;
+ if( nAbsTabWidth < m_nMin )
+ nAbsTabWidth = o3tl::narrowing<sal_uInt16>(m_nMin);
+
+ if( nAbsTabWidth > nAbsAvail )
+ {
+ OSL_ENSURE( IsTopTable(),
+ "A nested table should become wider than the available space." );
+ nAbsAvail = nAbsTabWidth;
+ }
+
+ sal_uLong nAbsTabWidthL = nAbsTabWidth;
+ if (nRelAvail)
+ {
+ if (nAbsAvail == 0)
+ throw o3tl::divide_by_zero();
+ m_nRelTabWidth = o3tl::narrowing<sal_uInt16>((nAbsTabWidthL * nRelAvail) / nAbsAvail);
+ }
+ else
+ m_nRelTabWidth = nAbsTabWidth;
+ double nW = nAbsTabWidth - m_nMin;
+ double nD = (m_nMax==m_nMin ? 1 : m_nMax-m_nMin);
+ sal_uInt16 nAbs = 0, nRel = 0;
+ for( sal_uInt16 i=0; i<m_nCols-1; i++ )
+ {
+ double nd = GetColumn( i )->GetMax() - GetColumn( i )->GetMin();
+ sal_uLong nAbsColWidth = GetColumn( i )->GetMin() + static_cast<sal_uLong>((nd*nW)/nD);
+ sal_uLong nRelColWidth = nRelAvail
+ ? (nAbsColWidth * m_nRelTabWidth) / nAbsTabWidth
+ : nAbsColWidth;
+
+ GetColumn( i )->SetAbsColWidth( o3tl::narrowing<sal_uInt16>(nAbsColWidth) );
+ GetColumn( i )->SetRelColWidth( o3tl::narrowing<sal_uInt16>(nRelColWidth) );
+ nAbs = nAbs + o3tl::narrowing<sal_uInt16>(nAbsColWidth);
+ nRel = nRel + o3tl::narrowing<sal_uInt16>(nRelColWidth);
+ }
+ GetColumn( m_nCols-1 )->SetAbsColWidth( nAbsTabWidth - nAbs );
+ GetColumn( m_nCols-1 )->SetRelColWidth( m_nRelTabWidth - nRel );
+
+ }
+
+ // Step 4: For nested tables we can have balancing cells on the
+ // left or right. Here we calculate their width.
+ m_nInhAbsLeftSpace = 0;
+ m_nInhAbsRightSpace = 0;
+ if( IsTopTable() ||
+ !(m_nRelLeftFill>0 || m_nRelRightFill>0 || nAbsTabWidth<nAbsAvail) )
+ return;
+
+ // Calculate the width of additional cells we use for
+ // aligning inner tables.
+ sal_uInt16 nAbsDist = o3tl::narrowing<sal_uInt16>(nAbsAvail-nAbsTabWidth);
+ sal_uInt16 nRelDist = o3tl::narrowing<sal_uInt16>(nRelAvail-m_nRelTabWidth);
+ sal_uInt16 nParentInhAbsLeftSpace = 0, nParentInhAbsRightSpace = 0;
+
+ // Calculate the size and position of the additional cells.
+ switch( m_eTableAdjust )
+ {
+ case SvxAdjust::Right:
+ nAbsLeftFill = nAbsLeftFill + nAbsDist;
+ m_nRelLeftFill = m_nRelLeftFill + nRelDist;
+ nParentInhAbsLeftSpace = nParentInhAbsSpace;
+ break;
+ case SvxAdjust::Center:
+ {
+ sal_uInt16 nAbsLeftDist = nAbsDist / 2;
+ nAbsLeftFill = nAbsLeftFill + nAbsLeftDist;
+ nAbsRightFill += nAbsDist - nAbsLeftDist;
+ sal_uInt16 nRelLeftDist = nRelDist / 2;
+ m_nRelLeftFill = m_nRelLeftFill + nRelLeftDist;
+ m_nRelRightFill += nRelDist - nRelLeftDist;
+ nParentInhAbsLeftSpace = nParentInhAbsSpace / 2;
+ nParentInhAbsRightSpace = nParentInhAbsSpace -
+ nParentInhAbsLeftSpace;
+ }
+ break;
+ case SvxAdjust::Left:
+ default:
+ nAbsRightFill = nAbsRightFill + nAbsDist;
+ m_nRelRightFill = m_nRelRightFill + nRelDist;
+ nParentInhAbsRightSpace = nParentInhAbsSpace;
+ break;
+ }
+
+ // Filler widths are added to the outer columns, if there are no boxes
+ // for them after the first pass (nWidth>0) or their width would become
+ // too small or if there are COL tags and the filler width corresponds
+ // to the border width.
+ // In the last case we probably exported the table ourselves.
+ if( m_nRelLeftFill &&
+ ( m_nWidthSet>0 || nAbsLeftFill<MINLAY+m_nInhLeftBorderWidth ||
+ (HasColTags() && nAbsLeftFill < nAbsLeftSpace+nParentInhAbsLeftSpace+20) ) )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( 0 );
+ pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsLeftFill );
+ pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelLeftFill );
+ m_nRelLeftFill = 0;
+ m_nInhAbsLeftSpace = nAbsLeftSpace + nParentInhAbsLeftSpace;
+ }
+ if( m_nRelRightFill &&
+ ( m_nWidthSet>0 || nAbsRightFill<MINLAY+m_nInhRightBorderWidth ||
+ (HasColTags() && nAbsRightFill < nAbsRightSpace+nParentInhAbsRightSpace+20) ) )
+ {
+ SwHTMLTableLayoutColumn *pColumn = GetColumn( m_nCols-1 );
+ pColumn->SetAbsColWidth( pColumn->GetAbsColWidth()+nAbsRightFill );
+ pColumn->SetRelColWidth( pColumn->GetRelColWidth()+m_nRelRightFill );
+ m_nRelRightFill = 0;
+ m_nInhAbsRightSpace = nAbsRightSpace + nParentInhAbsRightSpace;
+ }
+}
+
+static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth );
+
+static void lcl_ResizeBox( const SwTableBox* pBox, SwTwips* pWidth )
+{
+ if( !pBox->GetSttNd() )
+ {
+ SwTwips nWidth = 0;
+ for( const SwTableLine *pLine : pBox->GetTabLines() )
+ lcl_ResizeLine( pLine, &nWidth );
+ pBox->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 ));
+ *pWidth = *pWidth + nWidth;
+ }
+ else
+ {
+ *pWidth = *pWidth + pBox->GetFrameFormat()->GetFrameSize().GetSize().Width();
+ }
+}
+
+static void lcl_ResizeLine( const SwTableLine* pLine, SwTwips *pWidth )
+{
+ SwTwips nOldWidth = *pWidth;
+ *pWidth = 0;
+ for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+ lcl_ResizeBox(pBox, pWidth );
+
+ SAL_WARN_IF( nOldWidth && std::abs(*pWidth-nOldWidth) >= COLFUZZY, "sw.core",
+ "A box's rows have all a different length" );
+}
+
+void SwHTMLTableLayout::SetWidths( bool bCallPass2, sal_uInt16 nAbsAvail,
+ sal_uInt16 nRelAvail, sal_uInt16 nAbsLeftSpace,
+ sal_uInt16 nAbsRightSpace,
+ sal_uInt16 nParentInhAbsSpace )
+{
+ // SetWidth must have been passed through once more for every cell in the
+ // end.
+ m_nWidthSet++;
+
+ // Step 0: If necessary, we call the layout algorithm of Pass2.
+ if( bCallPass2 )
+ AutoLayoutPass2( nAbsAvail, nRelAvail, nAbsLeftSpace, nAbsRightSpace,
+ nParentInhAbsSpace );
+
+ // Step 1: Set the new width in all content boxes.
+ // Because the boxes don't know anything about the HTML table structure,
+ // we iterate over the HTML table structure.
+ // For tables in tables in tables we call SetWidth recursively.
+ for( sal_uInt16 i=0; i<m_nRows; i++ )
+ {
+ for( sal_uInt16 j=0; j<m_nCols; j++ )
+ {
+ SwHTMLTableLayoutCell *pCell = GetCell( i, j );
+
+ SwHTMLTableLayoutCnts* pContents = pCell->GetContents().get();
+ while( pContents && !pContents->IsWidthSet(m_nWidthSet) )
+ {
+ SwTableBox *pBox = pContents->GetTableBox();
+ if( pBox )
+ {
+ SetBoxWidth( pBox, j, pCell->GetColSpan() );
+ }
+ else if (SwHTMLTableLayout *pTable = pContents->GetTable())
+ {
+ sal_uInt16 nAbs = 0, nRel = 0, nLSpace = 0, nRSpace = 0,
+ nInhSpace = 0;
+ if( bCallPass2 )
+ {
+ sal_uInt16 nColSpan = pCell->GetColSpan();
+ GetAvail( j, nColSpan, nAbs, nRel );
+ nLSpace = GetLeftCellSpace( j, nColSpan );
+ nRSpace = GetRightCellSpace( j, nColSpan );
+ nInhSpace = GetInhCellSpace( j, nColSpan );
+ }
+ pTable->SetWidths( bCallPass2, nAbs, nRel,
+ nLSpace, nRSpace,
+ nInhSpace );
+ }
+
+ pContents->SetWidthSet( m_nWidthSet );
+ pContents = pContents->GetNext().get();
+ }
+ }
+ }
+
+ // Step 2: If we have a top table, we adapt the formats of the
+ // non-content-boxes. Because they are not known in the HTML table
+ // due to garbage collection there, we need the iterate over the
+ // whole table.
+ // We also adapt the table frame format. For nested tables we set the
+ // filler cell's width instead.
+ if( !IsTopTable() )
+ return;
+
+ SwTwips nCalcTabWidth = 0;
+ for( const SwTableLine *pLine : m_pSwTable->GetTabLines() )
+ lcl_ResizeLine( pLine, &nCalcTabWidth );
+ SAL_WARN_IF( std::abs( m_nRelTabWidth-nCalcTabWidth ) >= COLFUZZY, "sw.core",
+ "Table width is not equal to the row width" );
+
+ // Lock the table format when altering it, or else the box formats
+ // are altered again.
+ // Also, we need to preserve a percent setting if it exists.
+ SwFrameFormat *pFrameFormat = m_pSwTable->GetFrameFormat();
+ const_cast<SwTable *>(m_pSwTable)->LockModify();
+ SwFormatFrameSize aFrameSize( pFrameFormat->GetFrameSize() );
+ aFrameSize.SetWidth( m_nRelTabWidth );
+ bool bRel = m_bUseRelWidth &&
+ text::HoriOrientation::FULL!=pFrameFormat->GetHoriOrient().GetHoriOrient();
+ aFrameSize.SetWidthPercent( static_cast<sal_uInt8>(bRel ? m_nWidthOption : 0) );
+ pFrameFormat->SetFormatAttr( aFrameSize );
+ const_cast<SwTable *>(m_pSwTable)->UnlockModify();
+
+ // If the table is located in a frame, we also need to adapt the
+ // frame's width.
+ if( MayBeInFlyFrame() )
+ {
+ SwFrameFormat *pFlyFrameFormat = FindFlyFrameFormat();
+ if( pFlyFrameFormat )
+ {
+ SwFormatFrameSize aFlyFrameSize( SwFrameSize::Variable, m_nRelTabWidth, MINLAY );
+
+ if( m_bUseRelWidth )
+ {
+ // For percentage settings we set the width to the minimum.
+ aFlyFrameSize.SetWidth( m_nMin > USHRT_MAX ? USHRT_MAX
+ : m_nMin );
+ aFlyFrameSize.SetWidthPercent( static_cast<sal_uInt8>(m_nWidthOption) );
+ }
+ pFlyFrameFormat->SetFormatAttr( aFlyFrameSize );
+ }
+ }
+
+#ifdef DBG_UTIL
+ {
+ // check if the tables have correct widths
+ SwTwips nSize = m_pSwTable->GetFrameFormat()->GetFrameSize().GetWidth();
+ const SwTableLines& rLines = m_pSwTable->GetTabLines();
+ for (size_t n = 0; n < rLines.size(); ++n)
+ {
+ CheckBoxWidth( *rLines[ n ], nSize );
+ }
+ }
+#endif
+}
+
+void SwHTMLTableLayout::Resize_( sal_uInt16 nAbsAvail, bool bRecalc )
+{
+ // If bRecalc is set, the table's content changed.
+ // We need to execute pass 1 again.
+ if( bRecalc )
+ AutoLayoutPass1();
+
+ SwRootFrame *pRoot = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()->GetLayout();
+ if ( pRoot && pRoot->IsCallbackActionEnabled() )
+ pRoot->StartAllAction();
+
+ // Else we can set the widths, in which we have to run Pass 2 in each case.
+ SetWidths( true, nAbsAvail );
+
+ if ( pRoot && pRoot->IsCallbackActionEnabled() )
+ pRoot->EndAllAction( true ); //True per VirDev (browsing is calmer)
+}
+
+IMPL_LINK_NOARG( SwHTMLTableLayout, DelayedResize_Impl, Timer*, void )
+{
+ m_aResizeTimer.Stop();
+ Resize_( m_nDelayedResizeAbsAvail, m_bDelayedResizeRecalc );
+}
+
+bool SwHTMLTableLayout::Resize( sal_uInt16 nAbsAvail, bool bRecalc,
+ bool bForce, sal_uLong nDelay )
+{
+ if( 0 == nAbsAvail )
+ return false;
+ OSL_ENSURE( IsTopTable(), "Resize must only be called for top tables!" );
+
+ // May the table be resized at all? Or is it forced?
+ if( m_bMustNotResize && !bForce )
+ return false;
+
+ // May the table be recalculated? Or is it forced?
+ if( m_bMustNotRecalc && !bForce )
+ bRecalc = false;
+
+ const SwDoc& rDoc = GetDoc();
+
+ // If there is a layout, the root frame's size instead of the
+ // VisArea's size was potentially passed.
+ // If we're not in a frame we need to calculate the table for the VisArea,
+ // because switching from relative to absolute wouldn't work.
+ if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() && rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()->GetViewOptions()->getBrowseMode() )
+ {
+ const sal_uInt16 nVisAreaWidth = GetBrowseWidthByVisArea( rDoc );
+ if( nVisAreaWidth < nAbsAvail && !FindFlyFrameFormat() )
+ nAbsAvail = nVisAreaWidth;
+ }
+
+ if( nDelay==0 && m_aResizeTimer.IsActive() )
+ {
+ m_nDelayedResizeAbsAvail = nAbsAvail;
+ return false;
+ }
+
+ // Optimisation:
+ // If the minimum or maximum should not be recalculated and
+ // - the table's width never needs to be recalculated, or
+ // - the table was already calculated for the passed width, or
+ // - the available space is less or equal to the minimum width
+ // and the table already has the minimum width, or
+ // - the available space is larger than the maximum width and
+ // the table already has the maximum width
+ // nothing will happen to the table.
+ if( !bRecalc && ( !m_bMustResize ||
+ (m_nLastResizeAbsAvail==nAbsAvail) ||
+ (nAbsAvail<=m_nMin && m_nRelTabWidth==m_nMin) ||
+ (!m_bPercentWidthOption && nAbsAvail>=m_nMax && m_nRelTabWidth==m_nMax) ) )
+ return false;
+
+ if( nDelay==HTMLTABLE_RESIZE_NOW )
+ {
+ if( m_aResizeTimer.IsActive() )
+ m_aResizeTimer.Stop();
+ Resize_( nAbsAvail, bRecalc );
+ }
+ else if( nDelay > 0 )
+ {
+ m_nDelayedResizeAbsAvail = nAbsAvail;
+ m_bDelayedResizeRecalc = bRecalc;
+ m_aResizeTimer.SetTimeout( nDelay );
+ m_aResizeTimer.Start();
+ }
+ else
+ {
+ Resize_( nAbsAvail, bRecalc );
+ }
+
+ return true;
+}
+
+void SwHTMLTableLayout::BordersChanged( sal_uInt16 nAbsAvail )
+{
+ m_bBordersChanged = true;
+
+ Resize( nAbsAvail, true/*bRecalc*/ );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/lineinfo.cxx b/sw/source/core/doc/lineinfo.cxx
new file mode 100644
index 000000000..628e90e32
--- /dev/null
+++ b/sw/source/core/doc/lineinfo.cxx
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentState.hxx>
+#include <lineinfo.hxx>
+#include <charfmt.hxx>
+#include <poolfmt.hxx>
+#include <rootfrm.hxx>
+#include <osl/diagnose.h>
+
+void SwDoc::SetLineNumberInfo( const SwLineNumberInfo &rNew )
+{
+ SwRootFrame* pTmpRoot = getIDocumentLayoutAccess().GetCurrentLayout();
+ if ( pTmpRoot &&
+ (rNew.IsCountBlankLines() != mpLineNumberInfo->IsCountBlankLines() ||
+ rNew.IsRestartEachPage() != mpLineNumberInfo->IsRestartEachPage()) )
+ {
+ pTmpRoot->StartAllAction();
+ // FME 2007-08-14 #i80120# Invalidate size, because ChgThisLines()
+ // is only (and may only be) called by the formatting routines
+ //pTmpRoot->InvalidateAllContent( SwInvalidateFlags::LineNum | SwInvalidateFlags::Size );
+ for( auto aLayout : GetAllLayouts() )
+ aLayout->InvalidateAllContent( SwInvalidateFlags::LineNum | SwInvalidateFlags::Size );
+ pTmpRoot->EndAllAction();
+ }
+ *mpLineNumberInfo = rNew;
+ getIDocumentState().SetModified();
+}
+
+const SwLineNumberInfo& SwDoc::GetLineNumberInfo() const
+{
+ return *mpLineNumberInfo;
+}
+
+SwLineNumberInfo::SwLineNumberInfo() :
+ m_nPosFromLeft(o3tl::toTwips(5, o3tl::Length::mm)),
+ m_nCountBy( 5 ),
+ m_nDividerCountBy( 3 ),
+ m_ePos( LINENUMBER_POS_LEFT ),
+ m_bPaintLineNumbers( false ),
+ m_bCountBlankLines( true ),
+ m_bCountInFlys( false ),
+ m_bRestartEachPage( false )
+{
+}
+
+SwLineNumberInfo::SwLineNumberInfo(const SwLineNumberInfo &rCpy ) : SwClient(),
+ m_aType( rCpy.GetNumType() ),
+ m_aDivider( rCpy.GetDivider() ),
+ m_nPosFromLeft( rCpy.GetPosFromLeft() ),
+ m_nCountBy( rCpy.GetCountBy() ),
+ m_nDividerCountBy( rCpy.GetDividerCountBy() ),
+ m_ePos( rCpy.GetPos() ),
+ m_bPaintLineNumbers( rCpy.IsPaintLineNumbers() ),
+ m_bCountBlankLines( rCpy.IsCountBlankLines() ),
+ m_bCountInFlys( rCpy.IsCountInFlys() ),
+ m_bRestartEachPage( rCpy.IsRestartEachPage() )
+{
+ StartListeningToSameModifyAs(rCpy);
+}
+
+SwLineNumberInfo& SwLineNumberInfo::operator=(const SwLineNumberInfo &rCpy)
+{
+ StartListeningToSameModifyAs(rCpy);
+
+ m_aType = rCpy.GetNumType();
+ m_aDivider = rCpy.GetDivider();
+ m_nPosFromLeft = rCpy.GetPosFromLeft();
+ m_nCountBy = rCpy.GetCountBy();
+ m_nDividerCountBy = rCpy.GetDividerCountBy();
+ m_ePos = rCpy.GetPos();
+ m_bPaintLineNumbers = rCpy.IsPaintLineNumbers();
+ m_bCountBlankLines = rCpy.IsCountBlankLines();
+ m_bCountInFlys = rCpy.IsCountInFlys();
+ m_bRestartEachPage = rCpy.IsRestartEachPage();
+
+ return *this;
+}
+
+SwCharFormat* SwLineNumberInfo::GetCharFormat( IDocumentStylePoolAccess& rIDSPA ) const
+{
+ if ( !GetRegisteredIn() )
+ {
+ SwCharFormat* pFormat = rIDSPA.GetCharFormatFromPool( RES_POOLCHR_LINENUM );
+ pFormat->Add( const_cast<SwLineNumberInfo*>(this) );
+ }
+ return const_cast<SwCharFormat*>(static_cast<const SwCharFormat*>(GetRegisteredIn()));
+}
+
+void SwLineNumberInfo::SetCharFormat( SwCharFormat *pChFormat )
+{
+ OSL_ENSURE( pChFormat, "SetCharFormat, 0 is not a valid pointer" );
+ pChFormat->Add( this );
+}
+
+void SwLineNumberInfo::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CheckRegistration( pLegacy->m_pOld );
+ SwDoc *pDoc = static_cast<SwCharFormat*>(GetRegisteredIn())->GetDoc();
+ SwRootFrame* pRoot = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRoot )
+ {
+ pRoot->StartAllAction();
+ for( auto aLayout : pDoc->GetAllLayouts() )
+ aLayout->AllAddPaintRect();
+ pRoot->EndAllAction();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/list.cxx b/sw/source/core/doc/list.cxx
new file mode 100644
index 000000000..9543e083d
--- /dev/null
+++ b/sw/source/core/doc/list.cxx
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <list.hxx>
+
+#include <numrule.hxx>
+#include <ndarr.hxx>
+#include <node.hxx>
+
+SwList::SwList( const OUString& sListId,
+ SwNumRule& rDefaultListStyle,
+ const SwNodes& rNodes )
+ : msListId( sListId ),
+ msDefaultListStyleName( rDefaultListStyle.GetName() ),
+ mnMarkedListLevel( MAXLEVEL )
+{
+ // create empty list trees for the document ranges
+ const SwNode* pNode = rNodes[SwNodeOffset(0)];
+ do
+ {
+ SwPaM aPam( *pNode, *pNode->EndOfSectionNode() );
+
+ maListTrees.emplace_back(
+ std::make_unique<SwNodeNum>( &rDefaultListStyle ),
+ std::make_unique<SwNodeNum>( &rDefaultListStyle ),
+ std::make_unique<SwNodeNum>( &rDefaultListStyle ),
+ std::make_unique<SwPaM>( *(aPam.Start()), *(aPam.End()) ));
+
+ pNode = pNode->EndOfSectionNode();
+ if (pNode != &rNodes.GetEndOfContent())
+ {
+ SwNodeOffset nIndex = pNode->GetIndex();
+ nIndex++;
+ pNode = rNodes[nIndex];
+ }
+ }
+ while ( pNode != &rNodes.GetEndOfContent() );
+}
+
+SwList::~SwList() COVERITY_NOEXCEPT_FALSE
+{
+ for ( auto& rNumberTree : maListTrees )
+ {
+ SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRoot));
+ SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRootRLHidden));
+ SwNodeNum::HandleNumberTreeRootNodeDelete(*(rNumberTree.pRootOrigText));
+ }
+}
+
+bool SwList::HasNodes() const
+{
+ for (auto const& rNumberTree : maListTrees)
+ {
+ if (rNumberTree.pRoot->GetChildCount() != 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwList::InsertListItem(SwNodeNum& rNodeNum, SwListRedlineType const eRedline,
+ const int nLevel, const SwDoc& rDoc)
+{
+ const SwPosition aPosOfNodeNum( rNodeNum.GetPosition() );
+ const SwNodes* pNodesOfNodeNum = &(aPosOfNodeNum.nNode.GetNode().GetNodes());
+
+ for ( const auto& rNumberTree : maListTrees )
+ {
+ const SwPosition* pStart = rNumberTree.pSection->Start();
+ const SwPosition* pEnd = rNumberTree.pSection->End();
+ const SwNodes* pRangeNodes = &(pStart->nNode.GetNode().GetNodes());
+
+ if ( pRangeNodes == pNodesOfNodeNum &&
+ *pStart <= aPosOfNodeNum && aPosOfNodeNum <= *pEnd)
+ {
+ auto const& pRoot(SwListRedlineType::HIDDEN == eRedline
+ ? rNumberTree.pRootRLHidden
+ : SwListRedlineType::SHOW == eRedline
+ ? rNumberTree.pRoot
+ : rNumberTree.pRootOrigText);
+ pRoot->AddChild(&rNodeNum, nLevel, rDoc);
+ break;
+ }
+ }
+}
+
+void SwList::RemoveListItem(SwNodeNum& rNodeNum, const SwDoc& rDoc)
+{
+ rNodeNum.RemoveMe(rDoc);
+}
+
+void SwList::InvalidateListTree()
+{
+ for ( const auto& rNumberTree : maListTrees )
+ {
+ rNumberTree.pRoot->InvalidateTree();
+ rNumberTree.pRootRLHidden->InvalidateTree();
+ rNumberTree.pRootOrigText->InvalidateTree();
+ }
+}
+
+void SwList::ValidateListTree(const SwDoc& rDoc)
+{
+ for ( auto& rNumberTree : maListTrees )
+ {
+ rNumberTree.pRoot->NotifyInvalidChildren(rDoc);
+ rNumberTree.pRootRLHidden->NotifyInvalidChildren(rDoc);
+ rNumberTree.pRootOrigText->NotifyInvalidChildren(rDoc);
+ }
+}
+
+void SwList::MarkListLevel( const int nListLevel,
+ const bool bValue )
+{
+ if ( bValue )
+ {
+ if ( nListLevel != mnMarkedListLevel )
+ {
+ if ( mnMarkedListLevel != MAXLEVEL )
+ {
+ // notify former marked list nodes
+ NotifyItemsOnListLevel( mnMarkedListLevel );
+ }
+
+ mnMarkedListLevel = nListLevel;
+
+ // notify new marked list nodes
+ NotifyItemsOnListLevel( mnMarkedListLevel );
+ }
+ }
+ else
+ {
+ if ( mnMarkedListLevel != MAXLEVEL )
+ {
+ // notify former marked list nodes
+ NotifyItemsOnListLevel( mnMarkedListLevel );
+ }
+
+ mnMarkedListLevel = MAXLEVEL;
+ }
+}
+
+bool SwList::IsListLevelMarked( const int nListLevel ) const
+{
+ return nListLevel == mnMarkedListLevel;
+}
+
+void SwList::NotifyItemsOnListLevel( const int nLevel )
+{
+ for ( auto& rNumberTree : maListTrees )
+ {
+ rNumberTree.pRoot->NotifyNodesOnListLevel( nLevel );
+ rNumberTree.pRootRLHidden->NotifyNodesOnListLevel( nLevel );
+ rNumberTree.pRootOrigText->NotifyNodesOnListLevel( nLevel );
+ }
+}
+
+void SwList::SetDefaultListStyleName(OUString const& rNew)
+{
+ msDefaultListStyleName = rNew;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/notxtfrm.cxx b/sw/source/core/doc/notxtfrm.cxx
new file mode 100644
index 000000000..9239d09d6
--- /dev/null
+++ b/sw/source/core/doc/notxtfrm.cxx
@@ -0,0 +1,1477 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/imap.hxx>
+#include <svl/urihelper.hxx>
+#include <sfx2/progress.hxx>
+#include <sfx2/printer.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/boxitem.hxx>
+#include <fmturl.hxx>
+#include <fmtsrnd.hxx>
+#include <frmfmt.hxx>
+#include <swrect.hxx>
+#include <fesh.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <flyfrm.hxx>
+#include <flyfrms.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <viewimp.hxx>
+#include <pam.hxx>
+#include <hints.hxx>
+#include <rootfrm.hxx>
+#include <dflyobj.hxx>
+#include <pagefrm.hxx>
+#include <notxtfrm.hxx>
+#include <grfatr.hxx>
+#include <charatr.hxx>
+#include <ndnotxt.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <swregion.hxx>
+#include <poolfmt.hxx>
+#include <strings.hrc>
+#include <accessibilityoptions.hxx>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <svtools/embedhlp.hxx>
+#include <dview.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/b2dclipstate.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <txtfly.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <osl/diagnose.h>
+
+// MM02 needed for VOC mechanism and getting the OC - may be moved to an own file
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+
+using namespace com::sun::star;
+
+static bool GetRealURL( const SwGrfNode& rNd, OUString& rText )
+{
+ bool bRet = rNd.GetFileFilterNms( &rText, nullptr );
+ if( bRet )
+ rText = URIHelper::removePassword( rText, INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::Unambiguous);
+ if (rText.startsWith("data:image")) rText = "inline image";
+
+ return bRet;
+}
+
+static void lcl_PaintReplacement( const SwRect &rRect, const OUString &rText,
+ const SwViewShell &rSh, const SwNoTextFrame *pFrame,
+ bool bDefect )
+{
+ static vcl::Font aFont = []()
+ {
+ vcl::Font tmp;
+ tmp.SetWeight( WEIGHT_BOLD );
+ tmp.SetStyleName( OUString() );
+ tmp.SetFamilyName("Noto Sans");
+ tmp.SetFamily( FAMILY_SWISS );
+ tmp.SetTransparent( true );
+ return tmp;
+ }();
+
+ Color aCol( COL_RED );
+ FontLineStyle eUnderline = LINESTYLE_NONE;
+ const SwFormatURL &rURL = pFrame->FindFlyFrame()->GetFormat()->GetURL();
+ if( !rURL.GetURL().isEmpty() || rURL.GetMap() )
+ {
+ bool bVisited = false;
+ if ( rURL.GetMap() )
+ {
+ ImageMap *pMap = const_cast<ImageMap*>(rURL.GetMap());
+ for( size_t i = 0; i < pMap->GetIMapObjectCount(); ++i )
+ {
+ IMapObject *pObj = pMap->GetIMapObject( i );
+ if( rSh.GetDoc()->IsVisitedURL( pObj->GetURL() ) )
+ {
+ bVisited = true;
+ break;
+ }
+ }
+ }
+ else if ( !rURL.GetURL().isEmpty() )
+ bVisited = rSh.GetDoc()->IsVisitedURL( rURL.GetURL() );
+
+ SwFormat *pFormat = rSh.GetDoc()->getIDocumentStylePoolAccess().GetFormatFromPool( o3tl::narrowing<sal_uInt16>
+ (bVisited ? RES_POOLCHR_INET_VISIT : RES_POOLCHR_INET_NORMAL ) );
+ aCol = pFormat->GetColor().GetValue();
+ eUnderline = pFormat->GetUnderline().GetLineStyle();
+ }
+
+ aFont.SetUnderline( eUnderline );
+ aFont.SetColor( aCol );
+
+ const BitmapEx& rBmp = const_cast<SwViewShell&>(rSh).GetReplacementBitmap(bDefect);
+ Graphic::DrawEx(*rSh.GetOut(), rText, aFont, rBmp, rRect.Pos(), rRect.SSize());
+}
+
+SwNoTextFrame::SwNoTextFrame(SwNoTextNode * const pNode, SwFrame* pSib )
+: SwContentFrame( pNode, pSib )
+{
+ mnFrameType = SwFrameType::NoTxt;
+}
+
+SwContentFrame *SwNoTextNode::MakeFrame( SwFrame* pSib )
+{
+ return new SwNoTextFrame(this, pSib);
+}
+
+void SwNoTextFrame::DestroyImpl()
+{
+ StopAnimation();
+
+ SwContentFrame::DestroyImpl();
+}
+
+SwNoTextFrame::~SwNoTextFrame()
+{
+}
+
+void SetOutDev( SwViewShell *pSh, OutputDevice *pOut )
+{
+ pSh->mpOut = pOut;
+}
+
+static void lcl_ClearArea( const SwFrame &rFrame,
+ vcl::RenderContext &rOut, const SwRect& rPtArea,
+ const SwRect &rGrfArea )
+{
+ SwRegionRects aRegion( rPtArea, 4 );
+ aRegion -= rGrfArea;
+
+ if ( aRegion.empty() )
+ return;
+
+ const SvxBrushItem *pItem;
+ std::optional<Color> xCol;
+ SwRect aOrigRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( rFrame.GetBackgroundBrush( aFillAttributes, pItem, xCol, aOrigRect, false, /*bConsiderTextBox=*/false ) )
+ {
+ SwRegionRects const region(rPtArea);
+ basegfx::utils::B2DClipState aClipState;
+ const bool bDone(::DrawFillAttributes(aFillAttributes, aOrigRect, region, aClipState, rOut));
+
+ if(!bDone)
+ {
+ for( const auto &rRegion : aRegion )
+ {
+ ::DrawGraphic(pItem, rOut, aOrigRect, rRegion);
+ }
+ }
+ }
+ else
+ {
+ rOut.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rOut.SetFillColor( rFrame.getRootFrame()->GetCurrShell()->Imp()->GetRetoucheColor());
+ rOut.SetLineColor();
+ for( const auto &rRegion : aRegion )
+ rOut.DrawRect( rRegion.SVRect() );
+ rOut.Pop();
+ }
+}
+
+void SwNoTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ if ( getFrameArea().IsEmpty() )
+ return;
+
+ const SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if( !pSh->GetViewOptions()->IsGraphic() )
+ {
+ StopAnimation();
+ // #i6467# - no paint of placeholder for page preview
+ if ( pSh->GetWin() && !pSh->IsPreview() )
+ {
+ const SwNoTextNode* pNd = GetNode()->GetNoTextNode();
+ OUString aText( pNd->GetTitle() );
+ if ( aText.isEmpty() && pNd->IsGrfNode() )
+ GetRealURL( *static_cast<const SwGrfNode*>(pNd), aText );
+ if( aText.isEmpty() )
+ aText = FindFlyFrame()->GetFormat()->GetName();
+ lcl_PaintReplacement( getFrameArea(), aText, *pSh, this, false );
+ }
+ return;
+ }
+
+ if( pSh->GetAccessibilityOptions()->IsStopAnimatedGraphics() ||
+ // #i9684# Stop animation during printing/pdf export
+ !pSh->GetWin() )
+ StopAnimation();
+
+ SfxProgress::EnterLock(); // No progress reschedules in paint (SwapIn)
+
+ rRenderContext.Push();
+ bool bClip = true;
+ tools::PolyPolygon aPoly;
+
+ SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(GetNode()));
+ SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
+ if( pGrfNd )
+ pGrfNd->SetFrameInPaint( true );
+
+ // #i13147# - add 2nd parameter with value <true> to
+ // method call <FindFlyFrame().GetContour(..)> to indicate that it is called
+ // for paint in order to avoid load of the intrinsic graphic.
+ if ( ( !rRenderContext.GetConnectMetaFile() ||
+ !pSh->GetWin() ) &&
+ FindFlyFrame()->GetContour( aPoly, true )
+ )
+ {
+ rRenderContext.SetClipRegion(vcl::Region(aPoly));
+ bClip = false;
+ }
+
+ SwRect aOrigPaint( rRect );
+ if ( HasAnimation() && pSh->GetWin() )
+ {
+ aOrigPaint = getFrameArea(); aOrigPaint += getFramePrintArea().Pos();
+ }
+
+ SwRect aGrfArea( getFrameArea() );
+ SwRect aPaintArea( aGrfArea );
+
+ // In case the picture fly frm was clipped, render it with the origin
+ // size instead of scaling it
+ if ( pGrfNd && rNoTNd.getIDocumentSettingAccess()->get( DocumentSettingId::CLIPPED_PICTURES ) )
+ {
+ auto pFindFly = FindFlyFrame();
+ if (pFindFly && pFindFly->IsFlyFreeFrame())
+ {
+ const SwFlyFreeFrame *pFly = static_cast< const SwFlyFreeFrame* >( pFindFly );
+ bool bGetUnclippedFrame=true;
+ const SvxBoxItem* pBoxItem;
+ if( pFly->GetFormat() && (pBoxItem = pFly->GetFormat()->GetItemIfSet(RES_BOX, false)) )
+ {
+ if( pBoxItem->HasBorder( /*bTreatPaddingAsBorder*/true) )
+ bGetUnclippedFrame = false;
+ }
+
+ if( bGetUnclippedFrame )
+ aGrfArea = SwRect( getFrameArea().Pos( ), pFly->GetUnclippedFrame( ).SSize( ) );
+ }
+ }
+
+ aPaintArea.Intersection_( aOrigPaint );
+
+ SwRect aNormal( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
+ aNormal.Justify(); // Normalized rectangle for the comparisons
+
+ if( aPaintArea.Overlaps( aNormal ) )
+ {
+ // Calculate the four to-be-deleted rectangles
+ if( pSh->GetWin() )
+ ::lcl_ClearArea( *this, rRenderContext, aPaintArea, aNormal );
+
+ // The intersection of the PaintArea and the Bitmap contains the absolutely visible area of the Frame
+ aPaintArea.Intersection_( aNormal );
+
+ if ( bClip )
+ rRenderContext.IntersectClipRegion( aPaintArea.SVRect() );
+ /// delete unused 3rd parameter
+ PaintPicture( &rRenderContext, aGrfArea );
+ }
+ else
+ // If it's not visible, simply delete the given Area
+ lcl_ClearArea( *this, rRenderContext, aPaintArea, SwRect() );
+ if( pGrfNd )
+ pGrfNd->SetFrameInPaint( false );
+
+ rRenderContext.Pop();
+ SfxProgress::LeaveLock();
+}
+
+/** Calculate the position and the size of the graphic in the Frame,
+ corresponding to the current graphic attributes
+
+ @param Point the position in the Frame (also returned)
+ @param Size the graphic's size (also returned)
+ @param nMirror the current mirror attribute
+*/
+static void lcl_CalcRect( Point& rPt, Size& rDim, MirrorGraph nMirror )
+{
+ if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both )
+ {
+ rPt.setX(rPt.getX() + rDim.Width() -1);
+ rDim.setWidth( -rDim.Width() );
+ }
+
+ if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both )
+ {
+ rPt.setY(rPt.getY() + rDim.Height() -1);
+ rDim.setHeight( -rDim.Height() );
+ }
+}
+
+/** Calculate the Bitmap's position and the size within the passed rectangle */
+void SwNoTextFrame::GetGrfArea( SwRect &rRect, SwRect* pOrigRect ) const
+{
+ // Currently only used for scaling, cropping and mirroring the contour of graphics!
+ // Everything else is handled by GraphicObject
+ // We put the graphic's visible rectangle into rRect.
+ // pOrigRect contains position and size of the whole graphic.
+
+ // RotateFlyFrame3: SwFrame may be transformed. Get untransformed
+ // SwRect(s) as base of calculation
+ const TransformableSwFrame* pTransformableSwFrame(getTransformableSwFrame());
+ const SwRect aFrameArea(pTransformableSwFrame ? pTransformableSwFrame->getUntransformedFrameArea() : getFrameArea());
+ const SwRect aFramePrintArea(pTransformableSwFrame ? pTransformableSwFrame->getUntransformedFramePrintArea() : getFramePrintArea());
+
+ const SwAttrSet& rAttrSet = GetNode()->GetSwAttrSet();
+ const SwCropGrf& rCrop = rAttrSet.GetCropGrf();
+ MirrorGraph nMirror = rAttrSet.GetMirrorGrf().GetValue();
+
+ if( rAttrSet.GetMirrorGrf().IsGrfToggle() )
+ {
+ if( !(FindPageFrame()->GetVirtPageNum() % 2) )
+ {
+ switch ( nMirror )
+ {
+ case MirrorGraph::Dont: nMirror = MirrorGraph::Vertical; break;
+ case MirrorGraph::Vertical: nMirror = MirrorGraph::Dont; break;
+ case MirrorGraph::Horizontal: nMirror = MirrorGraph::Both; break;
+ default: nMirror = MirrorGraph::Horizontal; break;
+ }
+ }
+ }
+
+ // We read graphic from the Node, if needed.
+ // It may fail, however.
+ tools::Long nLeftCrop, nRightCrop, nTopCrop, nBottomCrop;
+ Size aOrigSz( static_cast<const SwNoTextNode*>(GetNode())->GetTwipSize() );
+ if ( !aOrigSz.Width() )
+ {
+ aOrigSz.setWidth( aFramePrintArea.Width() );
+ nLeftCrop = -rCrop.GetLeft();
+ nRightCrop = -rCrop.GetRight();
+ }
+ else
+ {
+ nLeftCrop = std::max( aOrigSz.Width() -
+ (rCrop.GetRight() + rCrop.GetLeft()), tools::Long(1) );
+ const double nScale = double(aFramePrintArea.Width()) / double(nLeftCrop);
+ nLeftCrop = tools::Long(nScale * -rCrop.GetLeft() );
+ nRightCrop = tools::Long(nScale * -rCrop.GetRight() );
+ }
+
+ // crop values have to be mirrored too
+ if( nMirror == MirrorGraph::Vertical || nMirror == MirrorGraph::Both )
+ {
+ tools::Long nTmpCrop = nLeftCrop;
+ nLeftCrop = nRightCrop;
+ nRightCrop= nTmpCrop;
+ }
+
+ if( !aOrigSz.Height() )
+ {
+ aOrigSz.setHeight( aFramePrintArea.Height() );
+ nTopCrop = -rCrop.GetTop();
+ nBottomCrop= -rCrop.GetBottom();
+ }
+ else
+ {
+ nTopCrop = std::max( aOrigSz.Height() - (rCrop.GetTop() + rCrop.GetBottom()), tools::Long(1) );
+ const double nScale = double(aFramePrintArea.Height()) / double(nTopCrop);
+ nTopCrop = tools::Long(nScale * -rCrop.GetTop() );
+ nBottomCrop= tools::Long(nScale * -rCrop.GetBottom() );
+ }
+
+ // crop values have to be mirrored too
+ if( nMirror == MirrorGraph::Horizontal || nMirror == MirrorGraph::Both )
+ {
+ tools::Long nTmpCrop = nTopCrop;
+ nTopCrop = nBottomCrop;
+ nBottomCrop= nTmpCrop;
+ }
+
+ Size aVisSz( aFramePrintArea.SSize() );
+ Size aGrfSz( aVisSz );
+ Point aVisPt( aFrameArea.Pos() + aFramePrintArea.Pos() );
+ Point aGrfPt( aVisPt );
+
+ // Set the "visible" rectangle first
+ if ( nLeftCrop > 0 )
+ {
+ aVisPt.setX(aVisPt.getX() + nLeftCrop);
+ aVisSz.AdjustWidth( -nLeftCrop );
+ }
+ if ( nTopCrop > 0 )
+ {
+ aVisPt.setY(aVisPt.getY() + nTopCrop);
+ aVisSz.AdjustHeight( -nTopCrop );
+ }
+ if ( nRightCrop > 0 )
+ aVisSz.AdjustWidth( -nRightCrop );
+ if ( nBottomCrop > 0 )
+ aVisSz.AdjustHeight( -nBottomCrop );
+
+ rRect.Pos ( aVisPt );
+ rRect.SSize( aVisSz );
+
+ // Calculate the whole graphic if needed
+ if ( !pOrigRect )
+ return;
+
+ Size aTmpSz( aGrfSz );
+ aGrfPt.setX(aGrfPt.getX() + nLeftCrop);
+ aTmpSz.AdjustWidth( -(nLeftCrop + nRightCrop) );
+ aGrfPt.setY(aGrfPt.getY() + nTopCrop);
+ aTmpSz.AdjustHeight( -(nTopCrop + nBottomCrop) );
+
+ if( MirrorGraph::Dont != nMirror )
+ lcl_CalcRect( aGrfPt, aTmpSz, nMirror );
+
+ pOrigRect->Pos ( aGrfPt );
+ pOrigRect->SSize( aTmpSz );
+}
+
+/** By returning the surrounding Fly's size which equals the graphic's size */
+const Size& SwNoTextFrame::GetSize() const
+{
+ // Return the Frame's size
+ const SwFrame *pFly = FindFlyFrame();
+ if( !pFly )
+ pFly = this;
+ return pFly->getFramePrintArea().SSize();
+}
+
+void SwNoTextFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ // RotateFlyFrame3 - inner frame. Get rotation and check if used
+ const double fRotation(getLocalFrameRotation());
+ const bool bRotated(!basegfx::fTools::equalZero(fRotation));
+
+ if(bRotated)
+ {
+ SwFlyFreeFrame* pUpperFly(dynamic_cast< SwFlyFreeFrame* >(GetUpper()));
+
+ if(pUpperFly)
+ {
+ if(!pUpperFly->isFrameAreaDefinitionValid())
+ {
+ // RotateFlyFrame3: outer frame *needs* to be layouted first, force this by calling
+ // it's ::Calc directly
+ pUpperFly->Calc(pRenderContext);
+ }
+
+ // Reset outer frame to unrotated state. This is necessary to make the
+ // layouting below work as currently implemented in Writer. As expected
+ // using Transformations allows to do this on the fly due to all information
+ // being included there.
+ // The full solution would be to adapt the whole layouting
+ // process of Writer to take care of Transformations, but that
+ // is currently beyond scope
+ if(pUpperFly->isTransformableSwFrame())
+ {
+ pUpperFly->getTransformableSwFrame()->restoreFrameAreas();
+ }
+ }
+
+ // Re-layout may be partially (see all isFrameAreaDefinitionValid() flags),
+ // so resetting the local SwFrame(s) in the local SwFrameAreaDefinition is also
+ // needed (e.g. for PrintPreview).
+ // Reset to BoundAreas will be done below automatically
+ if(isTransformableSwFrame())
+ {
+ getTransformableSwFrame()->restoreFrameAreas();
+ }
+ }
+
+ SwContentNotify aNotify( this );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ MakePos();
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( GetUpper()->getFramePrintArea().Width() );
+ }
+
+ MakePrtArea( rAttrs );
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ }
+
+ // RotateFlyFrame3 - inner frame
+ if(bRotated)
+ {
+ SwFlyFreeFrame* pUpperFly(dynamic_cast< SwFlyFreeFrame* >(GetUpper()));
+
+ if(pUpperFly)
+ {
+ // restore outer frame back to Transformed state, that means
+ // set the SwFrameAreaDefinition(s) back to BoundAreas of
+ // the transformed SwFrame. All needed information is part
+ // of the already correctly created Transformations of the
+ // upper frame, so it can be re-created on the fly
+ if(pUpperFly->isTransformableSwFrame())
+ {
+ pUpperFly->getTransformableSwFrame()->adaptFrameAreasToTransformations();
+ }
+ }
+
+ // After the unrotated layout is finished, apply possible set rotation to it
+ // get center from outer frame (layout frame) to be on the safe side
+ const Point aCenter(GetUpper() ? GetUpper()->getFrameArea().Center() : getFrameArea().Center());
+ const basegfx::B2DPoint aB2DCenter(aCenter.X(), aCenter.Y());
+
+ if(!mpTransformableSwFrame)
+ {
+ mpTransformableSwFrame.reset(new TransformableSwFrame(*this));
+ }
+
+ getTransformableSwFrame()->createFrameAreaTransformations(
+ fRotation,
+ aB2DCenter);
+ getTransformableSwFrame()->adaptFrameAreasToTransformations();
+ }
+ else
+ {
+ // reset transformations to show that they are not used
+ mpTransformableSwFrame.reset();
+ }
+}
+
+// RotateFlyFrame3 - Support for Transformations - outer frame
+basegfx::B2DHomMatrix SwNoTextFrame::getFrameAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFrameAreaTransformation();
+ }
+
+ // call parent
+ return SwContentFrame::getFrameAreaTransformation();
+}
+
+basegfx::B2DHomMatrix SwNoTextFrame::getFramePrintAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFramePrintAreaTransformation();
+ }
+
+ // call parent
+ return SwContentFrame::getFramePrintAreaTransformation();
+}
+
+// RotateFlyFrame3 - Support for Transformations
+void SwNoTextFrame::transform_translate(const Point& rOffset)
+{
+ // call parent - this will do the basic transform for SwRect(s)
+ // in the SwFrameAreaDefinition
+ SwContentFrame::transform_translate(rOffset);
+
+ // check if the Transformations need to be adapted
+ if(isTransformableSwFrame())
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ rOffset.X(), rOffset.Y()));
+
+ // transform using TransformableSwFrame
+ getTransformableSwFrame()->transform(aTransform);
+ }
+}
+
+// RotateFlyFrame3 - inner frame
+// Check if we contain a SwGrfNode and get possible rotation from it
+double SwNoTextFrame::getLocalFrameRotation() const
+{
+ const SwNoTextNode* pSwNoTextNode(nullptr != GetNode() ? GetNode()->GetNoTextNode() : nullptr);
+
+ if(nullptr != pSwNoTextNode)
+ {
+ const SwGrfNode* pSwGrfNode(pSwNoTextNode->GetGrfNode());
+
+ if(nullptr != pSwGrfNode)
+ {
+ const SwAttrSet& rSwAttrSet(pSwGrfNode->GetSwAttrSet());
+ const SwRotationGrf& rSwRotationGrf(rSwAttrSet.GetRotationGrf());
+ const double fRotate = -toRadians(rSwRotationGrf.GetValue());
+
+ return basegfx::normalizeToRange(fRotate, 2 * M_PI);
+ }
+ }
+
+ // no rotation
+ return 0.0;
+}
+
+/** Calculate the Bitmap's site, if needed */
+void SwNoTextFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ const Size aNewSize( GetSize() );
+
+ // Did the height change?
+ SwTwips nChgHght = IsVertical() ?
+ static_cast<SwTwips>(aNewSize.Width() - getFramePrintArea().Width()) :
+ static_cast<SwTwips>(aNewSize.Height() - getFramePrintArea().Height());
+ if( nChgHght > 0)
+ Grow( nChgHght );
+ else if( nChgHght < 0)
+ Shrink( std::min(getFramePrintArea().Height(), tools::Long(-nChgHght)) );
+}
+
+bool SwNoTextFrame::GetCharRect( SwRect &rRect, const SwPosition& rPos,
+ SwCursorMoveState *pCMS, bool /*bAllowFarAway*/ ) const
+{
+ if ( &rPos.nNode.GetNode() != static_cast<SwNode const *>(GetNode()) )
+ return false;
+
+ Calc(getRootFrame()->GetCurrShell()->GetOut());
+ SwRect aFrameRect( getFrameArea() );
+ rRect = aFrameRect;
+ rRect.Pos( getFrameArea().Pos() + getFramePrintArea().Pos() );
+ rRect.SSize( getFramePrintArea().SSize() );
+
+ rRect.Justify();
+
+ // Is the Bitmap in the visible area at all?
+ if( !aFrameRect.Overlaps( rRect ) )
+ {
+ // If not, then the Cursor is on the Frame
+ rRect = aFrameRect;
+ rRect.Width( 1 );
+ }
+ else
+ rRect.Intersection_( aFrameRect );
+
+ if ( pCMS && pCMS->m_bRealHeight )
+ {
+ pCMS->m_aRealHeight.setY(rRect.Height());
+ pCMS->m_aRealHeight.setX(0);
+ }
+
+ return true;
+}
+
+bool SwNoTextFrame::GetModelPositionForViewPoint(SwPosition* pPos, Point& ,
+ SwCursorMoveState*, bool ) const
+{
+ SwContentNode* pCNd = const_cast<SwContentNode*>(GetNode());
+ pPos->nNode = *pCNd;
+ pPos->nContent.Assign( pCNd, 0 );
+ return true;
+}
+
+void SwNoTextFrame::ClearCache()
+{
+ SwFlyFrame* pFly = FindFlyFrame();
+ if( pFly && pFly->GetFormat()->GetSurround().IsContour() )
+ {
+ ClrContourCache( pFly->GetVirtDrawObj() );
+ pFly->NotifyBackground( FindPageFrame(), getFramePrintArea(), PrepareHint::FlyFrameAttributesChanged );
+ }
+}
+
+void SwNoTextFrame::OnGraphicArrived()
+{
+ if(GetNode()->GetNodeType() != SwNodeType::Grf)
+ {
+ InvalidatePrt();
+ SetCompletePaint();
+ return;
+ }
+ SwGrfNode* pNd = static_cast<SwGrfNode*>(GetNode());
+ ClearCache();
+ auto pVSh = pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ if(pVSh)
+ pVSh->OnGraphicArrived(getFrameArea());
+}
+
+void SwNoTextFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if(dynamic_cast<const sw::GrfRereadAndInCacheHint*>(&rHint))
+ {
+ if(SwNodeType::Grf != GetNode()->GetNodeType())
+ {
+ InvalidatePrt();
+ SetCompletePaint();
+ }
+ return;
+ }
+ if(dynamic_cast<const sw::PreGraphicArrivedHint*>(&rHint))
+ {
+ OnGraphicArrived();
+ return;
+ }
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ sal_uInt16 nWhich = pLegacy->GetWhich();
+
+ // #i73788#
+ // no <SwContentFrame::Modify(..)> for RES_LINKED_GRAPHIC_STREAM_ARRIVED
+ if ( RES_GRAPHIC_PIECE_ARRIVED != nWhich &&
+ RES_LINKED_GRAPHIC_STREAM_ARRIVED != nWhich )
+ {
+ SwContentFrame::SwClientNotify(rModify, rHint);
+ }
+
+ bool bComplete = true;
+
+ switch( nWhich )
+ {
+ case RES_GRAPHIC_PIECE_ARRIVED:
+ case RES_LINKED_GRAPHIC_STREAM_ARRIVED:
+ OnGraphicArrived();
+ return;
+
+ case RES_OBJECTDYING:
+ break;
+
+ case RES_UPDATE_ATTR:
+ if (GetNode()->GetNodeType() != SwNodeType::Grf) {
+ break;
+ }
+ [[fallthrough]];
+ case RES_FMT_CHG:
+ ClearCache();
+ break;
+
+ case RES_ATTRSET_CHG:
+ {
+ sal_uInt16 n;
+ for( n = RES_GRFATR_BEGIN; n < RES_GRFATR_END; ++n )
+ if( SfxItemState::SET == static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->
+ GetItemState( n, false ))
+ {
+ ClearCache();
+
+ if(RES_GRFATR_ROTATION == n)
+ {
+ // RotGrfFlyFrame: Update Handles in view, these may be rotation-dependent
+ // (e.g. crop handles) and need a visualisation update
+ if ( GetNode()->GetNodeType() == SwNodeType::Grf )
+ {
+ SwGrfNode* pNd = static_cast<SwGrfNode*>( GetNode());
+ SwViewShell *pVSh = pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if(pVSh)
+ {
+ SdrView* pDrawView = pVSh->GetDrawView();
+
+ if(pDrawView)
+ {
+ pDrawView->AdjustMarkHdl(nullptr);
+ }
+ }
+
+ // RotateFlyFrame3 - invalidate needed for ContentFrame (inner, this)
+ // and LayoutFrame (outer, GetUpper). It is possible to only invalidate
+ // the outer frame, but that leads to an in-between state that gets
+ // potentially painted
+ if(GetUpper())
+ {
+ GetUpper()->InvalidateAll_();
+ }
+
+ InvalidateAll_();
+ }
+ }
+ break;
+ }
+ if( RES_GRFATR_END == n ) // not found
+ return ;
+ }
+ break;
+
+ default:
+ if ( !pLegacy->m_pNew || !isGRFATR(nWhich) )
+ return;
+ }
+
+ if( bComplete )
+ {
+ InvalidatePrt();
+ SetCompletePaint();
+ }
+}
+
+static void lcl_correctlyAlignRect( SwRect& rAlignedGrfArea, const SwRect& rInArea, vcl::RenderContext const * pOut )
+{
+
+ if(!pOut)
+ return;
+ tools::Rectangle aPxRect = pOut->LogicToPixel( rInArea.SVRect() );
+ tools::Rectangle aNewPxRect( aPxRect );
+ while( aNewPxRect.Left() < aPxRect.Left() )
+ {
+ rAlignedGrfArea.AddLeft( 1 );
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+ while( aNewPxRect.Top() < aPxRect.Top() )
+ {
+ rAlignedGrfArea.AddTop(+1);
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+ while( aNewPxRect.Bottom() > aPxRect.Bottom() )
+ {
+ rAlignedGrfArea.AddBottom( -1 );
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+ while( aNewPxRect.Right() > aPxRect.Right() )
+ {
+ rAlignedGrfArea.AddRight(-1);
+ aNewPxRect = pOut->LogicToPixel( rAlignedGrfArea.SVRect() );
+ }
+}
+
+static bool paintUsingPrimitivesHelper(
+ vcl::RenderContext& rOutputDevice,
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence,
+ const basegfx::B2DRange& rSourceRange,
+ const basegfx::B2DRange& rTargetRange)
+{
+ if(!rSequence.empty() && !basegfx::fTools::equalZero(rSourceRange.getWidth()) && !basegfx::fTools::equalZero(rSourceRange.getHeight()))
+ {
+ if(!basegfx::fTools::equalZero(rTargetRange.getWidth()) && !basegfx::fTools::equalZero(rTargetRange.getHeight()))
+ {
+ // map graphic range to target range. This will e.g. automatically include
+ // the mapping from 1/100th mm content to twips if needed when the target
+ // range is defined in twips
+ const basegfx::B2DHomMatrix aMappingTransform(
+ basegfx::utils::createSourceRangeTargetRangeTransform(
+ rSourceRange,
+ rTargetRange));
+
+ // Fill ViewInformation. Use MappingTransform here, so there is no need to
+ // embed the primitives to it. Use original TargetRange here so there is also
+ // no need to embed the primitives to a MaskPrimitive for cropping. This works
+ // only in this case where the graphic object cannot be rotated, though.
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
+ aMappingTransform,
+ rOutputDevice.GetViewTransformation(),
+ rTargetRange,
+ nullptr,
+ 0.0);
+
+ // get a primitive processor for rendering
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ rOutputDevice, aViewInformation2D) );
+
+ // render and cleanup
+ pProcessor2D->process(rSequence);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// MM02 original using falölback to VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ GraphicObject const& rGrfObj,
+ GraphicAttr const& rGraphicAttr,
+ const basegfx::B2DHomMatrix& rGraphicTransform,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDescription)
+{
+ // RotGrfFlyFrame: unify using GraphicPrimitive2D
+ // -> the primitive handles all crop and mirror stuff
+ // -> the primitive renderer will create the needed pdf export data
+ // -> if bitmap content, it will be cached system-dependent
+ drawinglayer::primitive2d::Primitive2DContainer aContent(1);
+ aContent[0] = new drawinglayer::primitive2d::GraphicPrimitive2D(
+ rGraphicTransform,
+ rGrfObj,
+ rGraphicAttr);
+
+ // MM02 use primitive-based version for visualization
+ paintGraphicUsingPrimitivesHelper(
+ rOutputDevice,
+ aContent,
+ rGraphicTransform,
+ rName,
+ rTitle,
+ rDescription);
+}
+
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ drawinglayer::primitive2d::Primitive2DContainer& rContent,
+ const basegfx::B2DHomMatrix& rGraphicTransform,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDescription)
+{
+ // RotateFlyFrame3: If ClipRegion is set at OutputDevice, we
+ // need to use that. Usually the renderer would be a VCL-based
+ // PrimitiveRenderer, but there are system-specific shortcuts that
+ // will *not* use the VCL-Paint of Bitmap and thus ignore this.
+ // Anyways, indirectly using a CLipRegion set at the target OutDev
+ // when using a PrimitiveRenderer is a non-valid implication.
+ // First tried only to use when HasPolyPolygonOrB2DPolyPolygon(),
+ // but there is an optimization at ClipRegion creation that detects
+ // a single Rectangle in a tools::PolyPolygon and forces to a simple
+ // RegionBand-based implementation, so cannot use it here.
+ if(rOutputDevice.IsClipRegion())
+ {
+ const basegfx::B2DPolyPolygon aClip(rOutputDevice.GetClipRegion().GetAsB2DPolyPolygon());
+
+ if(0 != aClip.count())
+ {
+ rContent.resize(1);
+ rContent[0] =
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aClip,
+ drawinglayer::primitive2d::Primitive2DContainer(rContent));
+ }
+ }
+
+ if(!rName.isEmpty() || !rTitle.isEmpty() || !rDescription.isEmpty())
+ {
+ // Embed to ObjectInfoPrimitive2D when we have Name/Title/Description
+ // information available
+ rContent.resize(1);
+ rContent[0] =
+ new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer(rContent),
+ rName,
+ rTitle,
+ rDescription);
+ }
+
+ basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0);
+ aTargetRange.transform(rGraphicTransform);
+
+ paintUsingPrimitivesHelper(
+ rOutputDevice,
+ rContent,
+ aTargetRange,
+ aTargetRange);
+}
+
+// DrawContact section
+namespace { // anonymous namespace
+class ViewObjectContactOfSwNoTextFrame : public sdr::contact::ViewObjectContact
+{
+protected:
+ virtual void createPrimitive2DSequence(
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+
+public:
+ ViewObjectContactOfSwNoTextFrame(
+ sdr::contact::ObjectContact& rObjectContact,
+ sdr::contact::ViewContact& rViewContact);
+};
+
+class ViewContactOfSwNoTextFrame : public sdr::contact::ViewContact
+{
+private:
+ // owner
+ const SwNoTextFrame& mrSwNoTextFrame;
+
+protected:
+ // Create an Object-Specific ViewObjectContact, set ViewContact and
+ // ObjectContact. Always needs to return something.
+ virtual sdr::contact::ViewObjectContact& CreateObjectSpecificViewObjectContact(
+ sdr::contact::ObjectContact& rObjectContact) override;
+
+public:
+ // read-access to owner
+ const SwNoTextFrame& getSwNoTextFrame() const { return mrSwNoTextFrame; }
+
+ // basic constructor, used from SwNoTextFrame.
+ explicit ViewContactOfSwNoTextFrame(const SwNoTextFrame& rSwNoTextFrame);
+};
+
+void ViewObjectContactOfSwNoTextFrame::createPrimitive2DSequence(
+ const sdr::contact::DisplayInfo& /*rDisplayInfo*/,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // MM02 get all the parameters formally used in paintGraphicUsingPrimitivesHelper
+ ViewContactOfSwNoTextFrame& rVCOfNTF(static_cast<ViewContactOfSwNoTextFrame&>(GetViewContact()));
+ const SwNoTextFrame& rSwNoTextFrame(rVCOfNTF.getSwNoTextFrame());
+ SwNoTextNode& rNoTNd(const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(rSwNoTextFrame.GetNode())));
+ SwGrfNode* pGrfNd(rNoTNd.GetGrfNode());
+
+ if(nullptr != pGrfNd)
+ {
+ const bool bPrn(GetObjectContact().isOutputToPrinter() || GetObjectContact().isOutputToRecordingMetaFile());
+ const GraphicObject& rGrfObj(pGrfNd->GetGrfObj(bPrn));
+ GraphicAttr aGraphicAttr;
+ pGrfNd->GetGraphicAttr(aGraphicAttr, &rSwNoTextFrame);
+ const basegfx::B2DHomMatrix aGraphicTransform(rSwNoTextFrame.getFrameAreaTransformation());
+
+ // MM02 this is the right place in the VOC-Mechanism to create
+ // the primitives for visualization - these will be automatically
+ // buffered and reused
+ rVisitor.visit(new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aGraphicTransform,
+ rGrfObj,
+ aGraphicAttr));
+ }
+}
+
+ViewObjectContactOfSwNoTextFrame::ViewObjectContactOfSwNoTextFrame(
+ sdr::contact::ObjectContact& rObjectContact,
+ sdr::contact::ViewContact& rViewContact)
+: sdr::contact::ViewObjectContact(rObjectContact, rViewContact)
+{
+}
+
+sdr::contact::ViewObjectContact& ViewContactOfSwNoTextFrame::CreateObjectSpecificViewObjectContact(
+ sdr::contact::ObjectContact& rObjectContact)
+{
+ sdr::contact::ViewObjectContact* pRetval = new ViewObjectContactOfSwNoTextFrame(rObjectContact, *this);
+ return *pRetval;
+}
+
+ViewContactOfSwNoTextFrame::ViewContactOfSwNoTextFrame(
+ const SwNoTextFrame& rSwNoTextFrame
+)
+: mrSwNoTextFrame(rSwNoTextFrame)
+{
+}
+} // end of anonymous namespace
+
+sdr::contact::ViewContact& SwNoTextFrame::GetViewContact() const
+{
+ if(!mpViewContact)
+ {
+ const_cast< SwNoTextFrame* >(this)->mpViewContact =
+ std::make_unique<ViewContactOfSwNoTextFrame>(*this);
+ }
+
+ return *mpViewContact;
+}
+
+/** Paint the graphic.
+
+ We require either a QuickDraw-Bitmap or a graphic here. If we do not have
+ either, we return a replacement.
+
+ @todo use aligned rectangle for drawing graphic.
+ @todo pixel-align coordinations for drawing graphic. */
+void SwNoTextFrame::PaintPicture( vcl::RenderContext* pOut, const SwRect &rGrfArea ) const
+{
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+
+ SwNoTextNode& rNoTNd = const_cast<SwNoTextNode&>(*static_cast<const SwNoTextNode*>(GetNode()));
+ SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
+ SwOLENode* pOLENd = rNoTNd.GetOLENode();
+
+ const bool bPrn = pOut == rNoTNd.getIDocumentDeviceAccess().getPrinter( false ) ||
+ pOut->GetConnectMetaFile();
+
+ const bool bIsChart = pOLENd && pOLENd->GetOLEObj().GetObject().IsChart();
+
+ // calculate aligned rectangle from parameter <rGrfArea>.
+ // Use aligned rectangle <aAlignedGrfArea> instead of <rGrfArea> in
+ // the following code.
+ SwRect aAlignedGrfArea = rGrfArea;
+ ::SwAlignRect( aAlignedGrfArea, pShell, pOut );
+
+ if( !bIsChart )
+ {
+ // Because for drawing a graphic left-top-corner and size coordinations are
+ // used, these coordinations have to be determined on pixel level.
+ ::SwAlignGrfRect( &aAlignedGrfArea, *pOut );
+ }
+ else //if( bIsChart )
+ {
+ // #i78025# charts own borders are not completely visible
+ // the above pixel correction is not correct - at least not for charts
+ // so a different pixel correction is chosen here
+ // this might be a good idea for all other OLE objects also,
+ // but as I cannot oversee the consequences I fix it only for charts for now
+ lcl_correctlyAlignRect( aAlignedGrfArea, rGrfArea, pOut );
+ }
+
+ if( pGrfNd )
+ {
+ // Fix for bug fdo#33781
+ const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() );
+ if (SwDrawView::IsAntiAliasing())
+ {
+ pOut->SetAntialiasing( nFormerAntialiasingAtOutput | AntialiasingFlags::Enable );
+ }
+
+ bool bContinue = true;
+ const GraphicObject& rGrfObj = pGrfNd->GetGrfObj(bPrn);
+
+ GraphicAttr aGrfAttr;
+ pGrfNd->GetGraphicAttr( aGrfAttr, this );
+
+ if( !bPrn )
+ {
+ // #i73788#
+ if ( pGrfNd->IsLinkedInputStreamReady() )
+ {
+ pGrfNd->UpdateLinkWithInputStream();
+ }
+ // #i85717#, #i90395# - check, if asynchronous retrieval
+ // if input stream for the graphic is possible
+ else if ( ( rGrfObj.GetType() == GraphicType::Default ||
+ rGrfObj.GetType() == GraphicType::NONE ) &&
+ pGrfNd->IsLinkedFile() &&
+ pGrfNd->IsAsyncRetrieveInputStreamPossible() )
+ {
+ Size aTmpSz;
+ ::sfx2::SvLinkSource* pGrfObj = pGrfNd->GetLink()->GetObj();
+ if( !pGrfObj ||
+ !pGrfObj->IsDataComplete() ||
+ !(aTmpSz = pGrfNd->GetTwipSize()).Width() ||
+ !aTmpSz.Height())
+ {
+ pGrfNd->TriggerAsyncRetrieveInputStream(); // #i73788#
+ }
+ OUString aText( pGrfNd->GetTitle() );
+ if ( aText.isEmpty() )
+ GetRealURL( *pGrfNd, aText );
+ ::lcl_PaintReplacement( aAlignedGrfArea, aText, *pShell, this, false );
+ bContinue = false;
+ }
+ }
+
+ if( bContinue )
+ {
+ if( rGrfObj.GetGraphic().IsSupportedGraphic())
+ {
+ const bool bAnimate = rGrfObj.IsAnimated() &&
+ !pShell->IsPreview() &&
+ !pShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
+ // #i9684# Stop animation during printing/pdf export
+ pShell->GetWin();
+
+ if( bAnimate &&
+ FindFlyFrame() != ::GetFlyFromMarked( nullptr, pShell ))
+ {
+ OutputDevice* pVout;
+ if( pOut == pShell->GetOut() && SwRootFrame::FlushVout() )
+ {
+ pVout = pOut;
+ pOut = pShell->GetOut();
+ }
+ else if( pShell->GetWin() && pOut->IsVirtual() )
+ {
+ pVout = pOut;
+ pOut = pShell->GetWin()->GetOutDev();
+ }
+ else
+ pVout = nullptr;
+
+ OSL_ENSURE( !pOut->IsVirtual() ||
+ pShell->GetViewOptions()->IsPDFExport() || pShell->isOutputToWindow(),
+ "pOut should not be a virtual device" );
+
+ pGrfNd->StartGraphicAnimation(pOut, aAlignedGrfArea.Pos(),
+ aAlignedGrfArea.SSize(), reinterpret_cast<sal_IntPtr>(this),
+ pVout );
+ }
+ else
+ {
+ // MM02 To allow system-dependent buffering of the involved
+ // bitmaps it is necessary to re-use the involved primitives
+ // and their already executed decomposition (also for
+ // performance reasons). This is usually done in DrawingLayer
+ // by using the VOC-Mechanism (see descriptions elsewhere).
+ // To get that here, make the involved SwNoTextFrame (this)
+ // a sdr::contact::ViewContact supplier by supporting
+ // a GetViewContact() - call. For ObjectContact we can use
+ // the already existing ObjectContact from the involved
+ // DrawingLayer. For this, the helper classes
+ // ViewObjectContactOfSwNoTextFrame
+ // ViewContactOfSwNoTextFrame
+ // are created which support the VOC-mechanism in its minimal
+ // form. This allows automatic and view-dependent (multiple edit
+ // windows, print, etc.) re-use of the created primitives.
+ // Also: Will be very useful when completely changing the Writer
+ // repaint to VOC and Primitives, too.
+ static const char* pDisableMM02Goodies(getenv("SAL_DISABLE_MM02_GOODIES"));
+ static bool bUseViewObjectContactMechanism(nullptr == pDisableMM02Goodies);
+ // tdf#130951 for safety reasons use fallback if ViewObjectContactMechanism
+ // fails for some reason - usually could only be not to find the correct
+ // SdrPageWindow
+ bool bSucceeded(false);
+
+ if(bUseViewObjectContactMechanism)
+ {
+ // MM02 use VOC-mechanism and buffer primitives
+ SwViewShellImp* pImp(pShell->Imp());
+ SdrPageView* pPageView(nullptr != pImp
+ ? pImp->GetPageView()
+ : nullptr);
+ // tdf#130951 caution - target may be Window, use the correct OutputDevice
+ OutputDevice* pTarget(pShell->isOutputToWindow()
+ ? pShell->GetWin()->GetOutDev()
+ : pShell->GetOut());
+ SdrPageWindow* pPageWindow(nullptr != pPageView && nullptr != pTarget
+ ? pPageView->FindPageWindow(*pTarget)
+ : nullptr);
+
+ if(nullptr != pPageWindow)
+ {
+ sdr::contact::ObjectContact& rOC(pPageWindow->GetObjectContact());
+ sdr::contact::ViewContact& rVC(GetViewContact());
+ sdr::contact::ViewObjectContact& rVOC(rVC.GetViewObjectContact(rOC));
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitives(rVOC.getPrimitive2DSequence(aDisplayInfo));
+ const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+
+ paintGraphicUsingPrimitivesHelper(
+ *pOut,
+ aPrimitives,
+ aGraphicTransform,
+ nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
+ rNoTNd.GetTitle(),
+ rNoTNd.GetDescription());
+ bSucceeded = true;
+ }
+ }
+
+ if(!bSucceeded)
+ {
+ // MM02 fallback to direct paint with primitive-recreation
+ // which will block reusage of system-dependent bitmap data
+ const basegfx::B2DHomMatrix aGraphicTransform(getFrameAreaTransformation());
+
+ paintGraphicUsingPrimitivesHelper(
+ *pOut,
+ rGrfObj,
+ aGrfAttr,
+ aGraphicTransform,
+ nullptr == pGrfNd->GetFlyFormat() ? OUString() : pGrfNd->GetFlyFormat()->GetName(),
+ rNoTNd.GetTitle(),
+ rNoTNd.GetDescription());
+ }
+ }
+ }
+ else
+ {
+ TranslateId pResId;
+
+ if( GraphicType::NONE == rGrfObj.GetType() )
+ pResId = STR_COMCORE_READERROR;
+ else if ( !rGrfObj.GetGraphic().IsSupportedGraphic() )
+ pResId = STR_COMCORE_CANT_SHOW;
+
+ OUString aText;
+ if ( !pResId &&
+ (aText = pGrfNd->GetTitle()).isEmpty() &&
+ (!GetRealURL( *pGrfNd, aText ) || aText.isEmpty()))
+ {
+ pResId = STR_COMCORE_READERROR;
+ }
+ if (pResId)
+ aText = SwResId(pResId);
+
+ ::lcl_PaintReplacement( aAlignedGrfArea, aText, *pShell, this, true );
+ }
+ }
+
+ if ( SwDrawView::IsAntiAliasing() )
+ pOut->SetAntialiasing( nFormerAntialiasingAtOutput );
+ }
+ else // bIsChart || pOLENd
+ {
+ // Fix for bug fdo#33781
+ const AntialiasingFlags nFormerAntialiasingAtOutput( pOut->GetAntialiasing() );
+ if (SwDrawView::IsAntiAliasing())
+ {
+ AntialiasingFlags nNewAntialiasingAtOutput = nFormerAntialiasingAtOutput | AntialiasingFlags::Enable;
+
+ // #i99665#
+ // Adjust AntiAliasing mode at output device for chart OLE
+ if ( pOLENd->IsChart() )
+ nNewAntialiasingAtOutput |= AntialiasingFlags::PixelSnapHairline;
+
+ pOut->SetAntialiasing( nNewAntialiasingAtOutput );
+ }
+
+ bool bDone(false);
+
+ if(bIsChart)
+ {
+ basegfx::B2DRange aSourceRange;
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence(
+ pOLENd->GetOLEObj().tryToGetChartContentAsPrimitive2DSequence(
+ aSourceRange,
+ bPrn));
+
+ if(!aSequence.empty() && !aSourceRange.isEmpty())
+ {
+ const basegfx::B2DRange aTargetRange(
+ aAlignedGrfArea.Left(), aAlignedGrfArea.Top(),
+ aAlignedGrfArea.Right(), aAlignedGrfArea.Bottom());
+
+ bDone = paintUsingPrimitivesHelper(
+ *pOut,
+ aSequence,
+ aSourceRange,
+ aTargetRange);
+ }
+ }
+
+ if(!bDone && pOLENd)
+ {
+ // SwOLENode does not have a known GraphicObject, need to
+ // work with Graphic instead
+ const Graphic* pGraphic = pOLENd->GetGraphic();
+ const Point aPosition(aAlignedGrfArea.Pos());
+ const Size aSize(aAlignedGrfArea.SSize());
+
+ if ( pGraphic && pGraphic->GetType() != GraphicType::NONE )
+ {
+ pGraphic->Draw(*pOut, aPosition, aSize);
+
+ // shade the representation if the object is activated outplace
+ uno::Reference < embed::XEmbeddedObject > xObj = pOLENd->GetOLEObj().GetOleRef();
+ if ( xObj.is() && xObj->getCurrentState() == embed::EmbedStates::ACTIVE )
+ {
+
+ ::svt::EmbeddedObjectRef::DrawShading(
+ tools::Rectangle(
+ aPosition,
+ aSize),
+ pOut);
+ }
+ }
+ else
+ {
+ ::svt::EmbeddedObjectRef::DrawPaintReplacement(
+ tools::Rectangle(aPosition, aSize),
+ pOLENd->GetOLEObj().GetCurrentPersistName(),
+ pOut);
+ }
+
+ sal_Int64 nMiscStatus = pOLENd->GetOLEObj().GetOleRef()->getStatus( pOLENd->GetAspect() );
+ if ( !bPrn && dynamic_cast< const SwCursorShell *>( pShell ) != nullptr &&
+ (nMiscStatus & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE))
+ {
+ const SwFlyFrame *pFly = FindFlyFrame();
+ assert( pFly != nullptr );
+ static_cast<SwFEShell*>(pShell)->ConnectObj( pOLENd->GetOLEObj().GetObject(), pFly->getFramePrintArea(), pFly->getFrameArea());
+ }
+ }
+
+ // see #i99665#
+ if (SwDrawView::IsAntiAliasing())
+ {
+ pOut->SetAntialiasing( nFormerAntialiasingAtOutput );
+ }
+ }
+}
+
+bool SwNoTextFrame::IsTransparent() const
+{
+ const SwViewShell* pSh = getRootFrame()->GetCurrShell();
+
+ if ( !pSh || !pSh->GetViewOptions()->IsGraphic() )
+ {
+ return true;
+ }
+
+ const SwGrfNode *pNd;
+
+ if( nullptr != (pNd = GetNode()->GetGrfNode()) )
+ {
+ if(pNd->IsTransparent())
+ {
+ return true;
+ }
+ }
+
+ // RotateFlyFrame3: If we are transformed, there are 'free' areas between
+ // the Graphic and the Border/Padding stuff - at least as long as those
+ // (Border and Padding) are not transformed, too
+ if(isTransformableSwFrame())
+ {
+ // we can be more specific - rotations of multiples of
+ // 90 degrees will leave no gaps. Go from [0.0 .. 2PI]
+ // to [0 .. 360] and check modulo 90
+ const tools::Long nRot(static_cast<tools::Long>(basegfx::rad2deg(getLocalFrameRotation())));
+ const bool bMultipleOf90(0 == (nRot % 90));
+
+ if(!bMultipleOf90)
+ {
+ return true;
+ }
+ }
+
+ //#29381# OLE are always transparent
+ if(nullptr != GetNode()->GetOLENode())
+ {
+ return true;
+ }
+
+ // return false by default to avoid background paint
+ return false;
+}
+
+void SwNoTextFrame::StopAnimation( const OutputDevice* pOut ) const
+{
+ // Stop animated graphics
+ const SwGrfNode* pGrfNd = GetNode()->GetGrfNode();
+
+ if( pGrfNd && pGrfNd->IsAnimated() )
+ {
+ const_cast< SwGrfNode* >(pGrfNd)->StopGraphicAnimation( pOut, reinterpret_cast<sal_IntPtr>(this) );
+ }
+}
+
+bool SwNoTextFrame::HasAnimation() const
+{
+ const SwGrfNode* pGrfNd = GetNode()->GetGrfNode();
+ return pGrfNd && pGrfNd->IsAnimated();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/number.cxx b/sw/source/core/doc/number.cxx
new file mode 100644
index 000000000..4e353a2b2
--- /dev/null
+++ b/sw/source/core/doc/number.cxx
@@ -0,0 +1,1589 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+
+#include <vcl/font.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/numitem.hxx>
+#include <svl/grabbagitem.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <charfmt.hxx>
+#include <ndtxt.hxx>
+#include <docary.hxx>
+#include <SwStyleNameMapper.hxx>
+
+// Needed to load default bullet list configuration
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+
+#include <numrule.hxx>
+#include <SwNodeNum.hxx>
+
+#include <list.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+#include <libxml/xmlwriter.h>
+
+#include <rtl/ustrbuf.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/saveopt.hxx>
+#include <osl/diagnose.h>
+
+#include <IDocumentListsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentState.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <wrtsh.hxx>
+
+using namespace ::com::sun::star;
+
+sal_uInt16 SwNumRule::snRefCount = 0;
+SwNumFormat* SwNumRule::saBaseFormats[ RULE_END ][ MAXLEVEL ] = {
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } };
+
+SwNumFormat* SwNumRule::saLabelAlignmentBaseFormats[ RULE_END ][ MAXLEVEL ] = {
+ {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr } };
+
+const sal_uInt16 SwNumRule::saDefNumIndents[ MAXLEVEL ] = {
+ o3tl::toTwips(25, o3tl::Length::in100),
+ o3tl::toTwips(50, o3tl::Length::in100),
+ o3tl::toTwips(75, o3tl::Length::in100),
+ o3tl::toTwips(100, o3tl::Length::in100),
+ o3tl::toTwips(125, o3tl::Length::in100),
+ o3tl::toTwips(150, o3tl::Length::in100),
+ o3tl::toTwips(175, o3tl::Length::in100),
+ o3tl::toTwips(200, o3tl::Length::in100),
+ o3tl::toTwips(225, o3tl::Length::in100),
+ o3tl::toTwips(250, o3tl::Length::in100),
+};
+
+OUString SwNumRule::GetOutlineRuleName()
+{
+ return "Outline";
+}
+
+const SwNumFormat& SwNumRule::Get( sal_uInt16 i ) const
+{
+ assert( i < MAXLEVEL && meRuleType < RULE_END );
+ return maFormats[ i ]
+ ? *maFormats[ i ]
+ : ( meDefaultNumberFormatPositionAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION
+ ? *saBaseFormats[ meRuleType ][ i ]
+ : *saLabelAlignmentBaseFormats[ meRuleType ][ i ] );
+}
+
+const SwNumFormat* SwNumRule::GetNumFormat( sal_uInt16 i ) const
+{
+ const SwNumFormat * pResult = nullptr;
+
+ assert( i < MAXLEVEL && meRuleType < RULE_END );
+ if ( i < MAXLEVEL && meRuleType < RULE_END)
+ {
+ pResult = maFormats[ i ].get();
+ }
+
+ return pResult;
+}
+
+// #i91400#
+void SwNumRule::SetName( const OUString & rName,
+ IDocumentListsAccess& rDocListAccess)
+{
+ if ( msName == rName )
+ return;
+
+ if (mpNumRuleMap)
+ {
+ mpNumRuleMap->erase(msName);
+ (*mpNumRuleMap)[rName] = this;
+
+ if ( !GetDefaultListId().isEmpty() )
+ {
+ rDocListAccess.trackChangeOfListStyleName( msName, rName );
+ }
+ }
+
+ msName = rName;
+}
+
+void SwNumRule::GetTextNodeList( SwNumRule::tTextNodeList& rTextNodeList ) const
+{
+ rTextNodeList = maTextNodeList;
+}
+
+SwNumRule::tTextNodeList::size_type SwNumRule::GetTextNodeListSize() const
+{
+ return maTextNodeList.size();
+}
+
+void SwNumRule::AddTextNode( SwTextNode& rTextNode )
+{
+ tTextNodeList::iterator aIter =
+ std::find( maTextNodeList.begin(), maTextNodeList.end(), &rTextNode );
+
+ if ( aIter == maTextNodeList.end() )
+ {
+ maTextNodeList.push_back( &rTextNode );
+ }
+}
+
+void SwNumRule::RemoveTextNode( SwTextNode& rTextNode )
+{
+ tTextNodeList::iterator aIter =
+ std::find( maTextNodeList.begin(), maTextNodeList.end(), &rTextNode );
+
+ if ( aIter != maTextNodeList.end() )
+ {
+ maTextNodeList.erase( aIter );
+ }
+}
+
+void SwNumRule::SetNumRuleMap(std::unordered_map<OUString, SwNumRule *> *
+ pNumRuleMap)
+{
+ mpNumRuleMap = pNumRuleMap;
+}
+
+sal_uInt16 SwNumRule::GetNumIndent( sal_uInt8 nLvl )
+{
+ OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" );
+ return saDefNumIndents[ nLvl ];
+}
+
+sal_uInt16 SwNumRule::GetBullIndent( sal_uInt8 nLvl )
+{
+ OSL_ENSURE( MAXLEVEL > nLvl, "NumLevel is out of range" );
+ return saDefNumIndents[ nLvl ];
+}
+
+static void lcl_SetRuleChgd( SwTextNode& rNd, sal_uInt8 nLevel )
+{
+ if( rNd.GetActualListLevel() == nLevel )
+ rNd.NumRuleChgd();
+}
+
+SwNumFormat::SwNumFormat() :
+ SvxNumberFormat(SVX_NUM_ARABIC),
+ SwClient( nullptr ),
+ m_pVertOrient(new SwFormatVertOrient( 0, text::VertOrientation::NONE))
+ ,m_cGrfBulletCP(USHRT_MAX)//For i120928,record the cp info of graphic within bullet
+{
+}
+
+SwNumFormat::SwNumFormat( const SwNumFormat& rFormat) :
+ SvxNumberFormat(rFormat),
+ SwClient( rFormat.GetRegisteredInNonConst() ),
+ m_pVertOrient(new SwFormatVertOrient( 0, rFormat.GetVertOrient()))
+ ,m_cGrfBulletCP(rFormat.m_cGrfBulletCP)//For i120928,record the cp info of graphic within bullet
+{
+ sal_Int16 eMyVertOrient = rFormat.GetVertOrient();
+ SetGraphicBrush( rFormat.GetBrush(), &rFormat.GetGraphicSize(),
+ &eMyVertOrient);
+}
+
+SwNumFormat::SwNumFormat(const SvxNumberFormat& rNumFormat, SwDoc* pDoc)
+ : SvxNumberFormat(rNumFormat)
+ , m_pVertOrient(new SwFormatVertOrient( 0, rNumFormat.GetVertOrient()))
+ , m_cGrfBulletCP(USHRT_MAX)
+{
+ sal_Int16 eMyVertOrient = rNumFormat.GetVertOrient();
+ SetGraphicBrush( rNumFormat.GetBrush(), &rNumFormat.GetGraphicSize(),
+ &eMyVertOrient);
+ const OUString rCharStyleName = rNumFormat.SvxNumberFormat::GetCharFormatName();
+ if( !rCharStyleName.isEmpty() )
+ {
+ SwCharFormat* pCFormat = pDoc->FindCharFormatByName( rCharStyleName );
+ if( !pCFormat )
+ {
+ sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( rCharStyleName,
+ SwGetPoolIdFromName::ChrFmt );
+ pCFormat = nId != USHRT_MAX
+ ? pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId )
+ : pDoc->MakeCharFormat( rCharStyleName, nullptr );
+ }
+ pCFormat->Add( this );
+ }
+ else
+ EndListeningAll();
+}
+
+SwNumFormat::~SwNumFormat()
+{
+}
+
+// #i22362#
+bool SwNumFormat::IsEnumeration() const
+{
+ // #i30655# native numbering did not work any longer
+ // using this code. Therefore HBRINKM and I agreed upon defining
+ // IsEnumeration() as !IsItemize()
+ return !IsItemize();
+}
+
+bool SwNumFormat::IsItemize() const
+{
+ bool bResult;
+
+ switch(GetNumberingType())
+ {
+ case SVX_NUM_CHAR_SPECIAL:
+ case SVX_NUM_BITMAP:
+ bResult = true;
+
+ break;
+
+ default:
+ bResult = false;
+ }
+
+ return bResult;
+
+}
+
+SwNumFormat& SwNumFormat::operator=( const SwNumFormat& rNumFormat)
+{
+ SvxNumberFormat::operator=(rNumFormat);
+ StartListeningToSameModifyAs(rNumFormat);
+ //For i120928,record the cp info of graphic within bullet
+ m_cGrfBulletCP = rNumFormat.m_cGrfBulletCP;
+ return *this;
+}
+
+bool SwNumFormat::operator==( const SwNumFormat& rNumFormat) const
+{
+ bool bRet = SvxNumberFormat::operator==(rNumFormat) &&
+ GetRegisteredIn() == rNumFormat.GetRegisteredIn();
+ return bRet;
+}
+
+void SwNumFormat::SetCharFormat( SwCharFormat* pChFormat)
+{
+ if( pChFormat )
+ pChFormat->Add( this );
+ else
+ EndListeningAll();
+}
+
+void SwNumFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ // Look for the NumRules object in the Doc where this NumFormat is set.
+ // The format does not need to exist!
+ const SwCharFormat* pFormat = nullptr;
+ switch(pLegacy->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ case RES_FMT_CHG:
+ pFormat = GetCharFormat();
+ break;
+ }
+
+ if(pFormat && !pFormat->GetDoc()->IsInDtor())
+ UpdateNumNodes(*const_cast<SwDoc*>(pFormat->GetDoc()));
+ else
+ CheckRegistration(pLegacy->m_pOld);
+}
+
+OUString SwNumFormat::GetCharFormatName() const
+{
+ if(static_cast<const SwCharFormat*>(GetRegisteredIn()))
+ return static_cast<const SwCharFormat*>(GetRegisteredIn())->GetName();
+
+ return OUString();
+}
+
+void SwNumFormat::SetGraphicBrush( const SvxBrushItem* pBrushItem, const Size* pSize,
+ const sal_Int16* pOrient)
+{
+ if(pOrient)
+ m_pVertOrient->SetVertOrient( *pOrient );
+ SvxNumberFormat::SetGraphicBrush( pBrushItem, pSize, pOrient);
+}
+
+void SwNumFormat::UpdateNumNodes( SwDoc& rDoc )
+{
+ bool bDocIsModified = rDoc.getIDocumentState().IsModified();
+ bool bFnd = false;
+ for( SwNumRuleTable::size_type n = rDoc.GetNumRuleTable().size(); !bFnd && n; )
+ {
+ const SwNumRule* pRule = rDoc.GetNumRuleTable()[ --n ];
+ for( sal_uInt8 i = 0; i < MAXLEVEL; ++i )
+ if( pRule->GetNumFormat( i ) == this )
+ {
+ SwNumRule::tTextNodeList aTextNodeList;
+ pRule->GetTextNodeList( aTextNodeList );
+ for ( auto& rpTextNode : aTextNodeList )
+ {
+ lcl_SetRuleChgd( *rpTextNode, i );
+ }
+ bFnd = true;
+ break;
+ }
+ }
+
+ if( bFnd && !bDocIsModified )
+ rDoc.getIDocumentState().ResetModified();
+}
+
+const SwFormatVertOrient* SwNumFormat::GetGraphicOrientation() const
+{
+ sal_Int16 eOrient = SvxNumberFormat::GetVertOrient();
+ if(text::VertOrientation::NONE == eOrient)
+ return nullptr;
+ else
+ {
+ m_pVertOrient->SetVertOrient(eOrient);
+ return m_pVertOrient.get();
+ }
+}
+
+SwNumRule::SwNumRule( const OUString& rNm,
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode,
+ SwNumRuleType eType )
+ : mpNumRuleMap(nullptr),
+ msName( rNm ),
+ meRuleType( eType ),
+ mnPoolFormatId( USHRT_MAX ),
+ mnPoolHelpId( USHRT_MAX ),
+ mnPoolHlpFileId( UCHAR_MAX ),
+ mbAutoRuleFlag( true ),
+ mbInvalidRuleFlag( true ),
+ mbContinusNum( false ),
+ mbAbsSpaces( false ),
+ mbHidden( false ),
+ mbCountPhantoms( true ),
+ mbUsedByRedline( false ),
+ meDefaultNumberFormatPositionAndSpaceMode( eDefaultNumberFormatPositionAndSpaceMode )
+{
+ if( !snRefCount++ ) // for the first time, initialize
+ {
+ SwNumFormat* pFormat;
+ sal_uInt8 n;
+
+ // numbering:
+ // position-and-space mode LABEL_WIDTH_AND_POSITION:
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetIncludeUpperLevels( 1 );
+ pFormat->SetStart( 1 );
+ pFormat->SetAbsLSpace( lNumberIndent + SwNumRule::GetNumIndent( n ) );
+ pFormat->SetFirstLineOffset( lNumberFirstLineOffset );
+ pFormat->SetListFormat("%" + OUString::number(n + 1) + "%.");
+ pFormat->SetBulletChar(numfunc::GetBulletChar(n));
+ SwNumRule::saBaseFormats[ NUM_RULE ][ n ] = pFormat;
+ }
+ // position-and-space mode LABEL_ALIGNMENT
+ // first line indent of general numbering in inch: -0,25 inch
+ const tools::Long cFirstLineIndent = o3tl::toTwips(-0.25, o3tl::Length::in);
+ // indent values of general numbering in inch:
+ const tools::Long cIndentAt[ MAXLEVEL ] = {
+ o3tl::toTwips(50, o3tl::Length::in100),
+ o3tl::toTwips(75, o3tl::Length::in100),
+ o3tl::toTwips(100, o3tl::Length::in100),
+ o3tl::toTwips(125, o3tl::Length::in100),
+ o3tl::toTwips(150, o3tl::Length::in100),
+ o3tl::toTwips(175, o3tl::Length::in100),
+ o3tl::toTwips(200, o3tl::Length::in100),
+ o3tl::toTwips(225, o3tl::Length::in100),
+ o3tl::toTwips(250, o3tl::Length::in100),
+ o3tl::toTwips(275, o3tl::Length::in100),
+ };
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetIncludeUpperLevels( 1 );
+ pFormat->SetStart( 1 );
+ pFormat->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT );
+ pFormat->SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ pFormat->SetListtabPos( cIndentAt[ n ] );
+ pFormat->SetFirstLineIndent( cFirstLineIndent );
+ pFormat->SetIndentAt( cIndentAt[ n ] );
+ pFormat->SetListFormat( "%" + OUString::number(n + 1) + "%.");
+ pFormat->SetBulletChar( numfunc::GetBulletChar(n));
+ SwNumRule::saLabelAlignmentBaseFormats[ NUM_RULE ][ n ] = pFormat;
+ }
+
+ // outline:
+ // position-and-space mode LABEL_WIDTH_AND_POSITION:
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetNumberingType(SVX_NUM_NUMBER_NONE);
+ pFormat->SetIncludeUpperLevels( MAXLEVEL );
+ pFormat->SetStart( 1 );
+ pFormat->SetCharTextDistance( lOutlineMinTextDistance );
+ pFormat->SetBulletChar( numfunc::GetBulletChar(n));
+ SwNumRule::saBaseFormats[ OUTLINE_RULE ][ n ] = pFormat;
+ }
+ // position-and-space mode LABEL_ALIGNMENT:
+ for( n = 0; n < MAXLEVEL; ++n )
+ {
+ pFormat = new SwNumFormat;
+ pFormat->SetNumberingType(SVX_NUM_NUMBER_NONE);
+ pFormat->SetIncludeUpperLevels( MAXLEVEL );
+ pFormat->SetStart( 1 );
+ pFormat->SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT );
+ pFormat->SetBulletChar( numfunc::GetBulletChar(n));
+ SwNumRule::saLabelAlignmentBaseFormats[ OUTLINE_RULE ][ n ] = pFormat;
+ }
+ }
+ OSL_ENSURE( !msName.isEmpty(), "NumRule without a name!" );
+}
+
+SwNumRule::SwNumRule( const SwNumRule& rNumRule )
+ : mpNumRuleMap(nullptr),
+ msName( rNumRule.msName ),
+ meRuleType( rNumRule.meRuleType ),
+ mnPoolFormatId( rNumRule.GetPoolFormatId() ),
+ mnPoolHelpId( rNumRule.GetPoolHelpId() ),
+ mnPoolHlpFileId( rNumRule.GetPoolHlpFileId() ),
+ mbAutoRuleFlag( rNumRule.mbAutoRuleFlag ),
+ mbInvalidRuleFlag( true ),
+ mbContinusNum( rNumRule.mbContinusNum ),
+ mbAbsSpaces( rNumRule.mbAbsSpaces ),
+ mbHidden( rNumRule.mbHidden ),
+ mbCountPhantoms( true ),
+ mbUsedByRedline( false ),
+ meDefaultNumberFormatPositionAndSpaceMode( rNumRule.meDefaultNumberFormatPositionAndSpaceMode ),
+ msDefaultListId( rNumRule.msDefaultListId )
+{
+ ++snRefCount;
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ if( rNumRule.maFormats[ n ] )
+ Set( n, *rNumRule.maFormats[ n ] );
+}
+
+SwNumRule::~SwNumRule()
+{
+ for (auto & i : maFormats)
+ i.reset();
+
+ if (mpNumRuleMap)
+ {
+ mpNumRuleMap->erase(GetName());
+ }
+
+ if( !--snRefCount ) // the last one closes the door (?)
+ {
+ // Numbering:
+ SwNumFormat** ppFormats = &SwNumRule::saBaseFormats[0][0];
+ int n;
+
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+
+ // Outline:
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+
+ ppFormats = &SwNumRule::saLabelAlignmentBaseFormats[0][0];
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+ for( n = 0; n < MAXLEVEL; ++n, ++ppFormats )
+ {
+ delete *ppFormats;
+ *ppFormats = nullptr;
+ }
+ }
+
+ maTextNodeList.clear();
+ maParagraphStyleList.clear();
+}
+
+void SwNumRule::CheckCharFormats( SwDoc& rDoc )
+{
+ for(auto& rpNumFormat : maFormats)
+ {
+ if( rpNumFormat )
+ {
+ SwCharFormat* pFormat = rpNumFormat->GetCharFormat();
+ if( pFormat && pFormat->GetDoc() != &rDoc )
+ {
+ // copy
+ SwNumFormat* pNew = new SwNumFormat( *rpNumFormat );
+ pNew->SetCharFormat( rDoc.CopyCharFormat( *pFormat ) );
+ rpNumFormat.reset(pNew);
+ }
+ }
+ }
+}
+
+SwNumRule& SwNumRule::operator=( const SwNumRule& rNumRule )
+{
+ if( this != &rNumRule )
+ {
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ Set( n, rNumRule.maFormats[ n ].get() );
+
+ meRuleType = rNumRule.meRuleType;
+ msName = rNumRule.msName;
+ mbAutoRuleFlag = rNumRule.mbAutoRuleFlag;
+ mbInvalidRuleFlag = true;
+ mbContinusNum = rNumRule.mbContinusNum;
+ mbAbsSpaces = rNumRule.mbAbsSpaces;
+ mbHidden = rNumRule.mbHidden;
+ mnPoolFormatId = rNumRule.GetPoolFormatId();
+ mnPoolHelpId = rNumRule.GetPoolHelpId();
+ mnPoolHlpFileId = rNumRule.GetPoolHlpFileId();
+ }
+ return *this;
+}
+
+void SwNumRule::Reset( const OUString& rName )
+{
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ Set( n, nullptr);
+
+ meRuleType = NUM_RULE;
+ msName = rName;
+ mbAutoRuleFlag = true;
+ mbInvalidRuleFlag = true;
+ mbContinusNum = false;
+ mbAbsSpaces = false;
+ mbHidden = false;
+ mnPoolFormatId = USHRT_MAX;
+ mnPoolHelpId = USHRT_MAX;
+ mnPoolHlpFileId = UCHAR_MAX;
+}
+
+bool SwNumRule::operator==( const SwNumRule& rRule ) const
+{
+ bool bRet = meRuleType == rRule.meRuleType &&
+ msName == rRule.msName &&
+ mbAutoRuleFlag == rRule.mbAutoRuleFlag &&
+ mbContinusNum == rRule.mbContinusNum &&
+ mbAbsSpaces == rRule.mbAbsSpaces &&
+ mnPoolFormatId == rRule.GetPoolFormatId() &&
+ mnPoolHelpId == rRule.GetPoolHelpId() &&
+ mnPoolHlpFileId == rRule.GetPoolHlpFileId();
+ if( bRet )
+ {
+ for( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
+ if( rRule.Get( n ) != Get( n ) )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ return bRet;
+}
+
+void SwNumRule::Set( sal_uInt16 i, const SwNumFormat& rNumFormat )
+{
+ OSL_ENSURE( i < MAXLEVEL, "Serious defect" );
+ if( i < MAXLEVEL )
+ {
+ if( !maFormats[ i ] || (rNumFormat != Get( i )) )
+ {
+ maFormats[ i ].reset(new SwNumFormat( rNumFormat ));
+ mbInvalidRuleFlag = true;
+ }
+ }
+}
+
+void SwNumRule::Set( sal_uInt16 i, const SwNumFormat* pNumFormat )
+{
+ OSL_ENSURE( i < MAXLEVEL, "Serious defect" );
+ if( i >= MAXLEVEL )
+ return;
+ if( !maFormats[ i ] )
+ {
+ if( pNumFormat )
+ {
+ maFormats[ i ].reset(new SwNumFormat( *pNumFormat ));
+ mbInvalidRuleFlag = true;
+ }
+ }
+ else if( !pNumFormat )
+ {
+ maFormats[ i ].reset();
+ mbInvalidRuleFlag = true;
+ }
+ else if( *maFormats[i] != *pNumFormat )
+ {
+ *maFormats[ i ] = *pNumFormat;
+ mbInvalidRuleFlag = true;
+ }
+}
+
+OUString SwNumRule::MakeNumString( const SwNodeNum& rNum, bool bInclStrings ) const
+{
+ if (rNum.IsCounted())
+ return MakeNumString(rNum.GetNumberVector(), bInclStrings);
+
+ return OUString();
+}
+
+OUString SwNumRule::MakeNumString( const SwNumberTree::tNumberVector & rNumVector,
+ const bool bInclStrings,
+ const unsigned int _nRestrictToThisLevel,
+ SwNumRule::Extremities* pExtremities,
+ LanguageType nLang ) const
+{
+ OUStringBuffer aStr;
+
+ SwNumberTree::tNumberVector::size_type nLevel = rNumVector.size() - 1;
+
+ if ( pExtremities )
+ pExtremities->nPrefixChars = pExtremities->nSuffixChars = 0;
+
+ if ( nLevel > _nRestrictToThisLevel )
+ {
+ nLevel = _nRestrictToThisLevel;
+ }
+
+ assert(nLevel < MAXLEVEL);
+
+ const SwNumFormat& rMyNFormat = Get( o3tl::narrowing<sal_uInt16>(nLevel) );
+
+ if (rMyNFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE)
+ {
+ if (!rMyNFormat.HasListFormat())
+ return OUString();
+
+ // If numbering is disabled for this level we should emit just prefix/suffix
+ // Remove everything between first %1% and last %n% (including markers)
+ OUString sLevelFormat = rMyNFormat.GetListFormat(bInclStrings);
+ sal_Int32 nFirstPosition = sLevelFormat.indexOf("%");
+ sal_Int32 nLastPosition = sLevelFormat.lastIndexOf("%");
+ if (nFirstPosition >= 0 && nLastPosition >= nFirstPosition)
+ sLevelFormat = sLevelFormat.replaceAt(nFirstPosition, nLastPosition - nFirstPosition + 1, u"");
+ return sLevelFormat;
+ }
+
+ css::lang::Locale aLocale( LanguageTag::convertToLocale(nLang));
+
+ if (rMyNFormat.HasListFormat())
+ {
+ OUString sLevelFormat = rMyNFormat.GetListFormat(bInclStrings);
+
+ // In this case we are ignoring GetIncludeUpperLevels: we put all
+ // level numbers requested by level format
+ for (SwNumberTree::tNumberVector::size_type i=0; i <= nLevel; ++i)
+ {
+ OUString sReplacement;
+ const SwNumFormat& rNFormat = Get(i);
+ if (rNFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE)
+ {
+ // Numbering disabled - replacement is empty
+ // And we should skip all level string content until next level marker:
+ // so %1%.%2%.%3% with second level as NONE will result 1.1, not 1..1
+ OUString sFind("%" + OUString::number(i + 1) + "%");
+ sal_Int32 nPositionToken = sLevelFormat.indexOf(sFind);
+ sal_Int32 nPositionNextToken = sLevelFormat.indexOf('%', nPositionToken + sFind.getLength());
+ if (nPositionToken >= 0 && nPositionNextToken >= nPositionToken)
+ {
+ sLevelFormat = sLevelFormat.replaceAt(nPositionToken, nPositionNextToken - nPositionToken, u"");
+ }
+ }
+ else if (rNumVector[i])
+ sReplacement = Get(i).GetNumStr(rNumVector[i], aLocale);
+ else
+ sReplacement = "0"; // all 0 level are a 0
+
+ OUString sFind("%" + OUString::number(i + 1) + "%");
+ sal_Int32 nPosition = sLevelFormat.indexOf(sFind);
+ if (nPosition >= 0)
+ sLevelFormat = sLevelFormat.replaceAt(nPosition, sFind.getLength(), sReplacement);
+ }
+
+ aStr = sLevelFormat;
+ }
+ else
+ {
+ // Fallback case: level format is not defined
+ // So use old way with levels joining by dot "."
+ SwNumberTree::tNumberVector::size_type i = nLevel;
+
+ if (!IsContinusNum() &&
+ // - do not include upper levels, if level isn't numbered.
+ rMyNFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE &&
+ rMyNFormat.GetIncludeUpperLevels()) // Just the own level?
+ {
+ sal_uInt8 n = rMyNFormat.GetIncludeUpperLevels();
+ if (1 < n)
+ {
+ if (i + 1 >= n)
+ i -= n - 1;
+ else
+ i = 0;
+ }
+ }
+
+ for (; i <= nLevel; ++i)
+ {
+ const SwNumFormat& rNFormat = Get(i);
+ if (SVX_NUM_NUMBER_NONE == rNFormat.GetNumberingType())
+ {
+ // Should 1.1.1 --> 2. NoNum --> 1..1 or 1.1 ??
+ // if( i != rNum.nMyLevel )
+ // aStr += ".";
+ continue;
+ }
+
+ if (rNumVector[i])
+ aStr.append(rNFormat.GetNumStr(rNumVector[i], aLocale));
+ else
+ aStr.append("0"); // all 0 level are a 0
+ if (i != nLevel && !aStr.isEmpty())
+ aStr.append(".");
+ }
+
+ // The type doesn't have any number, so don't append
+ // the post-/prefix string
+ if (bInclStrings &&
+ SVX_NUM_CHAR_SPECIAL != rMyNFormat.GetNumberingType() &&
+ SVX_NUM_BITMAP != rMyNFormat.GetNumberingType())
+ {
+ const OUString& sPrefix = rMyNFormat.GetPrefix();
+ const OUString& sSuffix = rMyNFormat.GetSuffix();
+
+ aStr.insert(0, sPrefix);
+ aStr.append(sSuffix);
+ if (pExtremities)
+ {
+ pExtremities->nPrefixChars = sPrefix.getLength();
+ pExtremities->nSuffixChars = sSuffix.getLength();
+ }
+ }
+ }
+
+ return aStr.makeStringAndClear();
+}
+
+OUString SwNumRule::MakeRefNumString( const SwNodeNum& rNodeNum,
+ const bool bInclSuperiorNumLabels,
+ const int nRestrictInclToThisLevel ) const
+{
+ OUString aRefNumStr;
+
+ if ( rNodeNum.GetLevelInListTree() >= 0 )
+ {
+ bool bOldHadPrefix = true;
+
+ const SwNodeNum* pWorkingNodeNum( &rNodeNum );
+ do
+ {
+ bool bMakeNumStringForPhantom( false );
+ if ( pWorkingNodeNum->IsPhantom() )
+ {
+ int nListLevel = pWorkingNodeNum->GetLevelInListTree();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ SwNumFormat aFormat( Get( o3tl::narrowing<sal_uInt16>(nListLevel) ) );
+ bMakeNumStringForPhantom = aFormat.IsEnumeration() &&
+ SVX_NUM_NUMBER_NONE != aFormat.GetNumberingType();
+
+ }
+ if ( bMakeNumStringForPhantom ||
+ ( !pWorkingNodeNum->IsPhantom() &&
+ pWorkingNodeNum->GetTextNode() &&
+ pWorkingNodeNum->GetTextNode()->HasNumber() ) )
+ {
+ Extremities aExtremities;
+ OUString aPrevStr = MakeNumString( pWorkingNodeNum->GetNumberVector(),
+ true, MAXLEVEL,
+ &aExtremities);
+ sal_Int32 nStrip = 0;
+ while ( nStrip < aExtremities.nPrefixChars )
+ {
+ const sal_Unicode c = aPrevStr[nStrip];
+ if ( c!='\t' && c!=' ')
+ break;
+ ++nStrip;
+ }
+
+ if (nStrip)
+ {
+ aPrevStr = aPrevStr.copy( nStrip );
+ aExtremities.nPrefixChars -= nStrip;
+ }
+
+ if (bOldHadPrefix &&
+ aExtremities.nSuffixChars &&
+ !aExtremities.nPrefixChars
+ )
+ {
+ aPrevStr = aPrevStr.copy(0,
+ aPrevStr.getLength() - aExtremities.nSuffixChars);
+ }
+
+ bOldHadPrefix = ( aExtremities.nPrefixChars > 0);
+
+ aRefNumStr = aPrevStr + aRefNumStr;
+ }
+
+ if ( bInclSuperiorNumLabels && pWorkingNodeNum->GetLevelInListTree() > 0 )
+ {
+ sal_uInt8 n = Get( o3tl::narrowing<sal_uInt16>(pWorkingNodeNum->GetLevelInListTree()) ).GetIncludeUpperLevels();
+ pWorkingNodeNum = dynamic_cast<SwNodeNum*>(pWorkingNodeNum->GetParent());
+ // skip parents, whose list label is already contained in the actual list label.
+ while ( pWorkingNodeNum && n > 1 )
+ {
+ pWorkingNodeNum = dynamic_cast<SwNodeNum*>(pWorkingNodeNum->GetParent());
+ --n;
+ }
+ }
+ else
+ {
+ break;
+ }
+ } while ( pWorkingNodeNum &&
+ pWorkingNodeNum->GetLevelInListTree() >= 0 &&
+ pWorkingNodeNum->GetLevelInListTree() >= nRestrictInclToThisLevel );
+ }
+
+ if (aRefNumStr.endsWith("."))
+ {
+ // tdf#144563: looks like a special case for refs by MS Word: if numbering is ending with dot, this dot is removed
+ aRefNumStr = aRefNumStr.copy(0, aRefNumStr.getLength() - 1);
+ }
+
+ return aRefNumStr;
+}
+
+OUString SwNumRule::MakeParagraphStyleListString() const
+{
+ OUString aParagraphStyleListString;
+ for (const auto& rParagraphStyle : maParagraphStyleList)
+ {
+ if (!aParagraphStyleListString.isEmpty())
+ aParagraphStyleListString += ", ";
+ aParagraphStyleListString += rParagraphStyle->GetName();
+ }
+ return aParagraphStyleListString;
+}
+
+/** Copy method of SwNumRule
+
+ A kind of copy constructor, so that the num formats are attached to the
+ right CharFormats of a Document.
+ Copies the NumFormats and returns itself. */
+SwNumRule& SwNumRule::CopyNumRule( SwDoc& rDoc, const SwNumRule& rNumRule )
+{
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ {
+ Set( n, rNumRule.maFormats[ n ].get() );
+ if( maFormats[ n ] && maFormats[ n ]->GetCharFormat() &&
+ !rDoc.GetCharFormats()->ContainsFormat(maFormats[n]->GetCharFormat()))
+ {
+ // If we copy across different Documents, then copy the
+ // corresponding CharFormat into the new Document.
+ maFormats[n]->SetCharFormat( rDoc.CopyCharFormat( *maFormats[n]->
+ GetCharFormat() ) );
+ }
+ }
+ meRuleType = rNumRule.meRuleType;
+ msName = rNumRule.msName;
+ mbAutoRuleFlag = rNumRule.mbAutoRuleFlag;
+ mnPoolFormatId = rNumRule.GetPoolFormatId();
+ mnPoolHelpId = rNumRule.GetPoolHelpId();
+ mnPoolHlpFileId = rNumRule.GetPoolHlpFileId();
+ mbInvalidRuleFlag = true;
+ return *this;
+}
+
+void SwNumRule::SetSvxRule(const SvxNumRule& rNumRule, SwDoc* pDoc)
+{
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ {
+ const SvxNumberFormat* pSvxFormat = rNumRule.Get(n);
+ maFormats[n].reset( pSvxFormat ? new SwNumFormat(*pSvxFormat, pDoc) : nullptr );
+ }
+
+ mbInvalidRuleFlag = true;
+ mbContinusNum = rNumRule.IsContinuousNumbering();
+}
+
+SvxNumRule SwNumRule::MakeSvxNumRule() const
+{
+ SvxNumRule aRule(SvxNumRuleFlags::CONTINUOUS | SvxNumRuleFlags::CHAR_STYLE |
+ SvxNumRuleFlags::ENABLE_LINKED_BMP | SvxNumRuleFlags::ENABLE_EMBEDDED_BMP,
+ MAXLEVEL, mbContinusNum,
+ meRuleType == NUM_RULE ? SvxNumRuleType::NUMBERING : SvxNumRuleType::OUTLINE_NUMBERING );
+ for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
+ {
+ const SwNumFormat & rNumFormat = Get(n);
+ if(rNumFormat.GetCharFormat())
+ {
+ SwNumFormat aNewFormat = rNumFormat;
+ aNewFormat.SetCharFormatName(rNumFormat.GetCharFormat()->GetName());
+ aRule.SetLevel(n, aNewFormat, maFormats[n] != nullptr);
+ }
+ else
+ aRule.SetLevel(n, rNumFormat, maFormats[n] != nullptr);
+ }
+ return aRule;
+}
+
+void SwNumRule::SetInvalidRule(bool bFlag)
+{
+ if (bFlag)
+ {
+ o3tl::sorted_vector< SwList* > aLists;
+ for ( const SwTextNode* pTextNode : maTextNodeList )
+ {
+ // #i111681# - applying patch from cmc
+ SwList* pList = pTextNode->GetDoc().getIDocumentListsAccess().getListByName( pTextNode->GetListId() );
+ OSL_ENSURE( pList, "<SwNumRule::SetInvalidRule(..)> - list at which the text node is registered at does not exist. This is a serious issue.");
+ if ( pList )
+ {
+ aLists.insert( pList );
+ }
+ }
+ for ( auto aList : aLists )
+ aList->InvalidateListTree();
+ }
+
+ mbInvalidRuleFlag = bFlag;
+}
+
+/// change indent of all list levels by given difference
+void SwNumRule::ChangeIndent( const sal_Int32 nDiff )
+{
+ for ( sal_uInt16 i = 0; i < MAXLEVEL; ++i )
+ {
+ SwNumFormat aTmpNumFormat( Get(i) );
+
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode(
+ aTmpNumFormat.GetPositionAndSpaceMode() );
+ if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ auto nNewIndent = nDiff +
+ aTmpNumFormat.GetAbsLSpace();
+ if ( nNewIndent < 0 )
+ {
+ nNewIndent = 0;
+ }
+ aTmpNumFormat.SetAbsLSpace( nNewIndent );
+ }
+ else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ // adjust also the list tab position, if a list tab stop is applied
+ if ( aTmpNumFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB )
+ {
+ const tools::Long nNewListTab = aTmpNumFormat.GetListtabPos() + nDiff;
+ aTmpNumFormat.SetListtabPos( nNewListTab );
+ }
+
+ const tools::Long nNewIndent = nDiff +
+ aTmpNumFormat.GetIndentAt();
+ aTmpNumFormat.SetIndentAt( nNewIndent );
+ }
+
+ Set( i, aTmpNumFormat );
+ }
+
+ SetInvalidRule( true );
+}
+
+/// set indent of certain list level to given value
+void SwNumRule::SetIndent( const short nNewIndent,
+ const sal_uInt16 nListLevel )
+{
+ SwNumFormat aTmpNumFormat( Get(nListLevel) );
+
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode(
+ aTmpNumFormat.GetPositionAndSpaceMode() );
+ if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ aTmpNumFormat.SetAbsLSpace( nNewIndent );
+ }
+ else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ // adjust also the list tab position, if a list tab stop is applied
+ if ( aTmpNumFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB )
+ {
+ const tools::Long nNewListTab = aTmpNumFormat.GetListtabPos() +
+ ( nNewIndent - aTmpNumFormat.GetIndentAt() );
+ aTmpNumFormat.SetListtabPos( nNewListTab );
+ }
+
+ aTmpNumFormat.SetIndentAt( nNewIndent );
+ }
+
+ SetInvalidRule( true );
+}
+
+/// set indent of first list level to given value and change other list level's
+/// indents accordingly
+void SwNumRule::SetIndentOfFirstListLevelAndChangeOthers( const short nNewIndent )
+{
+ SwNumFormat aTmpNumFormat( Get(0) );
+
+ sal_Int32 nDiff( 0 );
+ const SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode(
+ aTmpNumFormat.GetPositionAndSpaceMode() );
+ if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ nDiff = nNewIndent
+ - aTmpNumFormat.GetFirstLineOffset()
+ - aTmpNumFormat.GetAbsLSpace();
+ }
+ else if ( ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ nDiff = nNewIndent - aTmpNumFormat.GetIndentAt();
+ }
+ if ( nDiff != 0 )
+ {
+ ChangeIndent( nDiff );
+ }
+}
+
+void SwNumRule::Validate(const SwDoc& rDoc)
+{
+ o3tl::sorted_vector< SwList* > aLists;
+ for ( const SwTextNode* pTextNode : maTextNodeList )
+ {
+ aLists.insert( pTextNode->GetDoc().getIDocumentListsAccess().getListByName( pTextNode->GetListId() ) );
+ }
+ for ( auto aList : aLists )
+ aList->ValidateListTree(rDoc);
+
+ SetInvalidRule(false);
+}
+
+void SwNumRule::SetCountPhantoms(bool bCountPhantoms)
+{
+ mbCountPhantoms = bCountPhantoms;
+}
+
+SwNumRule::tParagraphStyleList::size_type SwNumRule::GetParagraphStyleListSize() const
+{
+ return maParagraphStyleList.size();
+}
+
+void SwNumRule::AddParagraphStyle( SwTextFormatColl& rTextFormatColl )
+{
+ tParagraphStyleList::iterator aIter =
+ std::find( maParagraphStyleList.begin(), maParagraphStyleList.end(), &rTextFormatColl );
+
+ if ( aIter == maParagraphStyleList.end() )
+ {
+ maParagraphStyleList.push_back( &rTextFormatColl );
+ }
+}
+
+void SwNumRule::RemoveParagraphStyle( SwTextFormatColl& rTextFormatColl )
+{
+ tParagraphStyleList::iterator aIter =
+ std::find( maParagraphStyleList.begin(), maParagraphStyleList.end(), &rTextFormatColl );
+
+ if ( aIter != maParagraphStyleList.end() )
+ {
+ maParagraphStyleList.erase( aIter );
+ }
+}
+
+void SwNumRule::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRule"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("msName"), BAD_CAST(msName.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mnPoolFormatId"), BAD_CAST(OString::number(mnPoolFormatId).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mbAutoRuleFlag"), BAD_CAST(OString::boolean(mbAutoRuleFlag).getStr()));
+
+ for (const auto& pFormat : maFormats)
+ {
+ if (!pFormat)
+ {
+ continue;
+ }
+
+ pFormat->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwNumRule::GetGrabBagItem(uno::Any& rVal) const
+{
+ if (mpGrabBagItem)
+ mpGrabBagItem->QueryValue(rVal);
+ else
+ rVal <<= uno::Sequence<beans::PropertyValue>();
+}
+
+void SwNumRule::SetGrabBagItem(const uno::Any& rVal)
+{
+ if (!mpGrabBagItem)
+ mpGrabBagItem = std::make_shared<SfxGrabBagItem>();
+
+ mpGrabBagItem->PutValue(rVal, 0);
+}
+
+bool SwNumRule::HasContinueList() const
+{
+ // In case all text nodes are after each other, then we won't have a later list that wants to
+ // continue us.
+ SwNodeOffset nIndex(0);
+ for (size_t i = 0; i < maTextNodeList.size(); ++i)
+ {
+ SwTextNode* pNode = maTextNodeList[i];
+ if (i > 0)
+ {
+ if (pNode->GetIndex() != nIndex + 1)
+ {
+ // May have a continue list.
+ return true;
+ }
+ }
+ nIndex = pNode->GetIndex();
+ }
+
+ // Definitely won't have a continue list.
+ return false;
+}
+
+namespace numfunc
+{
+ namespace {
+
+ /** class containing default bullet list configuration data */
+ class SwDefBulletConfig : private utl::ConfigItem
+ {
+ public:
+ static SwDefBulletConfig& getInstance();
+
+ const OUString& GetFontname() const
+ {
+ return msFontname;
+ }
+
+ bool IsFontnameUserDefined() const
+ {
+ return mbUserDefinedFontname;
+ }
+
+ const vcl::Font& GetFont() const
+ {
+ return *mpFont;
+ }
+
+ sal_Unicode GetChar( sal_uInt8 p_nListLevel ) const
+ {
+ if (p_nListLevel >= MAXLEVEL)
+ {
+ p_nListLevel = MAXLEVEL - 1;
+ }
+
+ return mnLevelChars[p_nListLevel];
+ }
+
+ SwDefBulletConfig();
+
+ private:
+ /** sets internal default bullet configuration data to default values */
+ void SetToDefault();
+
+ /** returns sequence of default bullet configuration property names */
+ static uno::Sequence<OUString> GetPropNames();
+
+ /** loads default bullet configuration properties and applies
+ values to internal data */
+ void LoadConfig();
+
+ /** initialize font instance for default bullet list */
+ void InitFont();
+
+ /** catches notification about changed default bullet configuration data */
+ virtual void Notify( const uno::Sequence<OUString>& aPropertyNames ) override;
+ virtual void ImplCommit() override;
+
+ // default bullet list configuration data
+ OUString msFontname;
+ bool mbUserDefinedFontname;
+ FontWeight meFontWeight;
+ FontItalic meFontItalic;
+ sal_Unicode mnLevelChars[MAXLEVEL];
+
+ // default bullet list font instance
+ std::optional<vcl::Font> mpFont;
+ };
+
+ }
+
+ SwDefBulletConfig& SwDefBulletConfig::getInstance()
+ {
+ static SwDefBulletConfig theSwDefBulletConfig;
+ return theSwDefBulletConfig;
+ }
+
+ SwDefBulletConfig::SwDefBulletConfig()
+ : ConfigItem( "Office.Writer/Numbering/DefaultBulletList" ),
+ // default bullet font is now OpenSymbol
+ msFontname( OUString("OpenSymbol") ),
+ mbUserDefinedFontname( false ),
+ meFontWeight( WEIGHT_DONTKNOW ),
+ meFontItalic( ITALIC_NONE )
+ {
+ SetToDefault();
+ LoadConfig();
+ InitFont();
+
+ // enable notification for changes on default bullet configuration change
+ EnableNotification( GetPropNames() );
+ }
+
+ void SwDefBulletConfig::SetToDefault()
+ {
+ msFontname = "OpenSymbol";
+ mbUserDefinedFontname = false;
+ meFontWeight = WEIGHT_DONTKNOW;
+ meFontItalic = ITALIC_NONE;
+
+ mnLevelChars[0] = 0x2022;
+ mnLevelChars[1] = 0x25e6;
+ mnLevelChars[2] = 0x25aa;
+ mnLevelChars[3] = 0x2022;
+ mnLevelChars[4] = 0x25e6;
+ mnLevelChars[5] = 0x25aa;
+ mnLevelChars[6] = 0x2022;
+ mnLevelChars[7] = 0x25e6;
+ mnLevelChars[8] = 0x25aa;
+ mnLevelChars[9] = 0x2022;
+ }
+
+ uno::Sequence<OUString> SwDefBulletConfig::GetPropNames()
+ {
+ uno::Sequence<OUString> aPropNames(13);
+ OUString* pNames = aPropNames.getArray();
+ pNames[0] = "BulletFont/FontFamilyname";
+ pNames[1] = "BulletFont/FontWeight";
+ pNames[2] = "BulletFont/FontItalic";
+ pNames[3] = "BulletCharLvl1";
+ pNames[4] = "BulletCharLvl2";
+ pNames[5] = "BulletCharLvl3";
+ pNames[6] = "BulletCharLvl4";
+ pNames[7] = "BulletCharLvl5";
+ pNames[8] = "BulletCharLvl6";
+ pNames[9] = "BulletCharLvl7";
+ pNames[10] = "BulletCharLvl8";
+ pNames[11] = "BulletCharLvl9";
+ pNames[12] = "BulletCharLvl10";
+
+ return aPropNames;
+ }
+
+ void SwDefBulletConfig::LoadConfig()
+ {
+ uno::Sequence<OUString> aPropNames = GetPropNames();
+ uno::Sequence<uno::Any> aValues = GetProperties( aPropNames );
+ const uno::Any* pValues = aValues.getConstArray();
+ OSL_ENSURE( aValues.getLength() == aPropNames.getLength(),
+ "<SwDefBulletConfig::SwDefBulletConfig()> - GetProperties failed");
+ if ( aValues.getLength() != aPropNames.getLength() )
+ return;
+
+ for ( int nProp = 0; nProp < aPropNames.getLength(); ++nProp )
+ {
+ if ( pValues[nProp].hasValue() )
+ {
+ switch ( nProp )
+ {
+ case 0:
+ {
+ OUString aStr;
+ pValues[nProp] >>= aStr;
+ msFontname = aStr;
+ mbUserDefinedFontname = true;
+ }
+ break;
+ case 1:
+ case 2:
+ {
+ sal_Int16 nTmp = 0;
+ pValues[nProp] >>= nTmp;
+ if ( nProp == 1 )
+ meFontWeight = static_cast<FontWeight>(nTmp);
+ else if ( nProp == 2 )
+ meFontItalic = static_cast<FontItalic>(nTmp);
+ }
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ {
+ sal_Unicode cChar = sal_Unicode();
+ pValues[nProp] >>= cChar;
+ mnLevelChars[nProp-3] = cChar;
+ }
+ break;
+ }
+ }
+ }
+
+ }
+
+ void SwDefBulletConfig::InitFont()
+ {
+ mpFont.emplace( msFontname, OUString(), Size( 0, 14 ) );
+ mpFont->SetWeight( meFontWeight );
+ mpFont->SetItalic( meFontItalic );
+ mpFont->SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ }
+
+ void SwDefBulletConfig::Notify( const uno::Sequence<OUString>& )
+ {
+ SetToDefault();
+ LoadConfig();
+ InitFont();
+ }
+
+ void SwDefBulletConfig::ImplCommit()
+ {
+ }
+
+ OUString const & GetDefBulletFontname()
+ {
+ return SwDefBulletConfig::getInstance().GetFontname();
+ }
+
+ bool IsDefBulletFontUserDefined()
+ {
+ return SwDefBulletConfig::getInstance().IsFontnameUserDefined();
+ }
+
+ const vcl::Font& GetDefBulletFont()
+ {
+ return SwDefBulletConfig::getInstance().GetFont();
+ }
+
+ sal_Unicode GetBulletChar( sal_uInt8 nLevel )
+ {
+ return SwDefBulletConfig::getInstance().GetChar( nLevel );
+ }
+
+ namespace {
+
+ /** class containing configuration data about user interface behavior
+ regarding lists and list items.
+ configuration item about behavior of <TAB>/<SHIFT-TAB>-key at first
+ position of first list item
+ */
+ class SwNumberingUIBehaviorConfig : private utl::ConfigItem
+ {
+ public:
+ static SwNumberingUIBehaviorConfig& getInstance();
+
+ bool ChangeIndentOnTabAtFirstPosOfFirstListItem() const
+ {
+ return mbChangeIndentOnTabAtFirstPosOfFirstListItem;
+ }
+
+ SwNumberingUIBehaviorConfig();
+
+ private:
+
+ /** sets internal configuration data to default values */
+ void SetToDefault();
+
+ /** returns sequence of configuration property names */
+ static css::uno::Sequence<OUString> GetPropNames();
+
+ /** loads configuration properties and applies values to internal data */
+ void LoadConfig();
+
+ /** catches notification about changed configuration data */
+ virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames ) override;
+ virtual void ImplCommit() override;
+
+ // configuration data
+ bool mbChangeIndentOnTabAtFirstPosOfFirstListItem;
+ };
+
+ }
+
+ SwNumberingUIBehaviorConfig& SwNumberingUIBehaviorConfig::getInstance()
+ {
+ static SwNumberingUIBehaviorConfig theSwNumberingUIBehaviorConfig;
+ return theSwNumberingUIBehaviorConfig;
+ }
+
+ SwNumberingUIBehaviorConfig::SwNumberingUIBehaviorConfig()
+ : ConfigItem( "Office.Writer/Numbering/UserInterfaceBehavior" ),
+ mbChangeIndentOnTabAtFirstPosOfFirstListItem( true )
+ {
+ SetToDefault();
+ LoadConfig();
+
+ // enable notification for changes on configuration change
+ EnableNotification( GetPropNames() );
+ }
+
+ void SwNumberingUIBehaviorConfig::SetToDefault()
+ {
+ mbChangeIndentOnTabAtFirstPosOfFirstListItem = true;
+ }
+
+ css::uno::Sequence<OUString> SwNumberingUIBehaviorConfig::GetPropNames()
+ {
+ css::uno::Sequence<OUString> aPropNames { "ChangeIndentOnTabAtFirstPosOfFirstListItem" };
+
+ return aPropNames;
+ }
+
+ void SwNumberingUIBehaviorConfig::ImplCommit() {}
+
+ void SwNumberingUIBehaviorConfig::LoadConfig()
+ {
+ css::uno::Sequence<OUString> aPropNames = GetPropNames();
+ css::uno::Sequence<css::uno::Any> aValues = GetProperties( aPropNames );
+ const css::uno::Any* pValues = aValues.getConstArray();
+ OSL_ENSURE( aValues.getLength() == aPropNames.getLength(),
+ "<SwNumberingUIBehaviorConfig::LoadConfig()> - GetProperties failed");
+ if ( aValues.getLength() != aPropNames.getLength() )
+ return;
+
+ for ( int nProp = 0; nProp < aPropNames.getLength(); ++nProp )
+ {
+ if ( pValues[nProp].hasValue() )
+ {
+ switch ( nProp )
+ {
+ case 0:
+ {
+ pValues[nProp] >>= mbChangeIndentOnTabAtFirstPosOfFirstListItem;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwNumberingUIBehaviorConfig::LoadConfig()> - unknown configuration property");
+ }
+ }
+ }
+ }
+ }
+
+ void SwNumberingUIBehaviorConfig::Notify( const css::uno::Sequence<OUString>& )
+ {
+ SetToDefault();
+ LoadConfig();
+ }
+
+ bool ChangeIndentOnTabAtFirstPosOfFirstListItem()
+ {
+ return SwNumberingUIBehaviorConfig::getInstance().ChangeIndentOnTabAtFirstPosOfFirstListItem();
+ }
+
+ bool NumDownChangesIndent(const SwWrtShell& rShell)
+ {
+ SwPaM* pCursor = rShell.GetCursor();
+ if (!pCursor)
+ {
+ return true;
+ }
+
+ SwTextNode* pTextNode = pCursor->GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return true;
+ }
+
+ const SwNumRule* pNumRule = pTextNode->GetNumRule();
+ if (!pNumRule)
+ {
+ return true;
+ }
+
+ int nOldLevel = pTextNode->GetActualListLevel();
+ int nNewLevel = nOldLevel + 1;
+ if (nNewLevel >= MAXLEVEL)
+ {
+ return true;
+ }
+
+ const SwNumFormat& rOldFormat = pNumRule->Get(nOldLevel);
+ if (rOldFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE)
+ {
+ return true;
+ }
+
+ const SwNumFormat& rNewFormat = pNumRule->Get(nNewLevel);
+ if (rNewFormat.GetNumberingType() != SVX_NUM_NUMBER_NONE)
+ {
+ return true;
+ }
+
+ // This is the case when the numbering levels don't differ, so changing between them is not
+ // a better alternative to inserting a tab character.
+ return rOldFormat.GetIndentAt() != rNewFormat.GetIndentAt();
+ }
+
+ SvxNumberFormat::SvxNumPositionAndSpaceMode GetDefaultPositionAndSpaceMode()
+ {
+ if (utl::ConfigManager::IsFuzzing())
+ return SvxNumberFormat::LABEL_ALIGNMENT;
+
+ SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode;
+ switch (GetODFSaneDefaultVersion())
+ {
+ case SvtSaveOptions::ODFSVER_010:
+ case SvtSaveOptions::ODFSVER_011:
+ {
+ ePosAndSpaceMode = SvxNumberFormat::LABEL_WIDTH_AND_POSITION;
+ }
+ break;
+ default: // >= ODFSVER_012
+ {
+ ePosAndSpaceMode = SvxNumberFormat::LABEL_ALIGNMENT;
+ }
+ }
+
+ return ePosAndSpaceMode;
+ }
+}
+
+void SwNumRuleTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRuleTable"));
+ for (SwNumRule* pNumRule : *this)
+ pNumRule->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/poolfmt.cxx b/sw/source/core/doc/poolfmt.cxx
new file mode 100644
index 000000000..d061db873
--- /dev/null
+++ b/sw/source/core/doc/poolfmt.cxx
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <osl/diagnose.h>
+#include <doc.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <list.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <fmtcol.hxx>
+#include <numrule.hxx>
+#include <swtable.hxx>
+#include <tblafmt.hxx>
+#include <hints.hxx>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+
+void SetAllScriptItem( SfxItemSet& rSet, const SfxPoolItem& rItem )
+{
+ rSet.Put( rItem );
+ sal_uInt16 nWhCJK = 0, nWhCTL = 0;
+ switch( rItem.Which() )
+ {
+ case RES_CHRATR_FONTSIZE:
+ nWhCJK = RES_CHRATR_CJK_FONTSIZE;
+ nWhCTL = RES_CHRATR_CTL_FONTSIZE;
+ break;
+ case RES_CHRATR_FONT:
+ nWhCJK = RES_CHRATR_CJK_FONT;
+ nWhCTL = RES_CHRATR_CTL_FONT;
+ break;
+ case RES_CHRATR_LANGUAGE:
+ nWhCJK = RES_CHRATR_CJK_LANGUAGE;
+ nWhCTL = RES_CHRATR_CTL_LANGUAGE;
+ break;
+ case RES_CHRATR_POSTURE:
+ nWhCJK = RES_CHRATR_CJK_POSTURE;
+ nWhCTL = RES_CHRATR_CTL_POSTURE;
+ break;
+ case RES_CHRATR_WEIGHT:
+ nWhCJK = RES_CHRATR_CJK_WEIGHT;
+ nWhCTL = RES_CHRATR_CTL_WEIGHT;
+ break;
+ }
+
+ if( nWhCJK )
+ rSet.Put( rItem.CloneSetWhich(nWhCJK) );
+ if( nWhCTL )
+ rSet.Put( rItem.CloneSetWhich(nWhCTL) );
+}
+
+/// Return the AutoCollection by its Id. If it doesn't
+/// exist yet, create it.
+/// If the String pointer is defined, then only query for
+/// the Attribute descriptions. It doesn't create a style!
+SvxFrameDirection GetDefaultFrameDirection(LanguageType nLanguage)
+{
+ return MsLangId::isRightToLeft(nLanguage) ?
+ SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB;
+}
+
+// See if the Paragraph/Character/Frame/Page style is in use
+bool SwDoc::IsUsed( const sw::BroadcastingModify& rModify ) const
+{
+ // Check if we have dependent ContentNodes in the Nodes array
+ // (also indirect ones for derived Formats)
+ SwAutoFormatGetDocNode aGetHt( &GetNodes() );
+ return !rModify.GetInfo( aGetHt );
+}
+
+// See if Table style is in use
+bool SwDoc::IsUsed( const SwTableAutoFormat& rTableAutoFormat) const
+{
+ size_t nTableCount = GetTableFrameFormatCount(true);
+ for (size_t i=0; i < nTableCount; ++i)
+ {
+ SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
+ SwTable* pTable = SwTable::FindTable(pFrameFormat);
+ if (pTable->GetTableStyleName() == rTableAutoFormat.GetName())
+ return true;
+ }
+ return false;
+}
+
+// See if the NumRule is used
+bool SwDoc::IsUsed( const SwNumRule& rRule )
+{
+ SwList const*const pList(getIDocumentListsAccess().getListByName(rRule.GetDefaultListId()));
+ bool bUsed = rRule.GetTextNodeListSize() > 0 ||
+ rRule.GetParagraphStyleListSize() > 0 ||
+ rRule.IsUsedByRedline()
+ // tdf#135014 default num rule is used if any associated num rule is used
+ || (pList
+ && pList->GetDefaultListStyleName() == rRule.GetName()
+ && pList->HasNodes());
+
+ return bUsed;
+}
+
+const OUString* SwDoc::GetDocPattern(size_t const nPos) const
+{
+ if (nPos >= m_PatternNames.size())
+ return nullptr;
+ return &m_PatternNames[nPos];
+}
+
+// Look for the style name's position. If it doesn't exist,
+// insert an anew
+size_t SwDoc::SetDocPattern(const OUString& rPatternName)
+{
+ OSL_ENSURE( !rPatternName.isEmpty(), "no Document style name" );
+
+ auto const iter(
+ std::find(m_PatternNames.begin(), m_PatternNames.end(), rPatternName));
+ if (iter != m_PatternNames.end())
+ {
+ return std::distance(m_PatternNames.begin(), iter);
+ }
+ else
+ {
+ m_PatternNames.push_back(rPatternName);
+ getIDocumentState().SetModified();
+ return m_PatternNames.size() - 1;
+ }
+}
+
+sal_uInt16 GetPoolParent( sal_uInt16 nId )
+{
+ sal_uInt16 nRet = USHRT_MAX;
+ if( POOLGRP_NOCOLLID & nId ) // 1 == Formats / 0 == Collections
+ {
+ switch( ( COLL_GET_RANGE_BITS | POOLGRP_NOCOLLID ) & nId )
+ {
+ case POOLGRP_CHARFMT:
+ case POOLGRP_FRAMEFMT:
+ nRet = 0; // derived from the default
+ break;
+ case POOLGRP_PAGEDESC:
+ case POOLGRP_NUMRULE:
+ break; // there are no derivations
+ }
+ }
+ else
+ {
+ switch( COLL_GET_RANGE_BITS & nId )
+ {
+ case COLL_TEXT_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_STANDARD:
+ nRet = 0; break;
+ case RES_POOLCOLL_TEXT_IDENT:
+ case RES_POOLCOLL_TEXT_NEGIDENT:
+ case RES_POOLCOLL_TEXT_MOVE:
+ case RES_POOLCOLL_CONFRONTATION:
+ case RES_POOLCOLL_MARGINAL:
+ nRet = RES_POOLCOLL_TEXT; break;
+
+ case RES_POOLCOLL_TEXT:
+ case RES_POOLCOLL_GREETING:
+ case RES_POOLCOLL_SIGNATURE:
+ case RES_POOLCOLL_HEADLINE_BASE:
+ nRet = RES_POOLCOLL_STANDARD; break;
+
+ case RES_POOLCOLL_HEADLINE1:
+ case RES_POOLCOLL_HEADLINE2:
+ case RES_POOLCOLL_HEADLINE3:
+ case RES_POOLCOLL_HEADLINE4:
+ case RES_POOLCOLL_HEADLINE5:
+ case RES_POOLCOLL_HEADLINE6:
+ case RES_POOLCOLL_HEADLINE7:
+ case RES_POOLCOLL_HEADLINE8:
+ case RES_POOLCOLL_HEADLINE9:
+ case RES_POOLCOLL_HEADLINE10:
+ nRet = RES_POOLCOLL_HEADLINE_BASE; break;
+ }
+ break;
+
+ case COLL_LISTS_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_NUMBER_BULLET_BASE:
+ nRet = RES_POOLCOLL_TEXT; break;
+
+ default:
+ nRet = RES_POOLCOLL_NUMBER_BULLET_BASE; break;
+ }
+ break;
+
+ case COLL_EXTRA_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_TABLE_HDLN:
+ nRet = RES_POOLCOLL_TABLE; break;
+
+ case RES_POOLCOLL_FRAME:
+ case RES_POOLCOLL_TABLE:
+ case RES_POOLCOLL_FOOTNOTE:
+ case RES_POOLCOLL_ENDNOTE:
+ case RES_POOLCOLL_ENVELOPE_ADDRESS:
+ case RES_POOLCOLL_SEND_ADDRESS:
+ case RES_POOLCOLL_HEADERFOOTER:
+ case RES_POOLCOLL_LABEL:
+ nRet = RES_POOLCOLL_STANDARD; break;
+ case RES_POOLCOLL_HEADER:
+ nRet = RES_POOLCOLL_HEADERFOOTER; break;
+ case RES_POOLCOLL_HEADERL:
+ case RES_POOLCOLL_HEADERR:
+ nRet = RES_POOLCOLL_HEADER; break;
+ case RES_POOLCOLL_FOOTER:
+ nRet = RES_POOLCOLL_HEADERFOOTER; break;
+ case RES_POOLCOLL_FOOTERL:
+ case RES_POOLCOLL_FOOTERR:
+ nRet = RES_POOLCOLL_FOOTER; break;
+
+ case RES_POOLCOLL_LABEL_ABB:
+ case RES_POOLCOLL_LABEL_TABLE:
+ case RES_POOLCOLL_LABEL_FRAME:
+ case RES_POOLCOLL_LABEL_DRAWING:
+ case RES_POOLCOLL_LABEL_FIGURE:
+ nRet = RES_POOLCOLL_LABEL; break;
+ }
+ break;
+
+ case COLL_REGISTER_BITS:
+ switch( nId )
+ {
+ case RES_POOLCOLL_REGISTER_BASE:
+ nRet = RES_POOLCOLL_STANDARD; break;
+
+ case RES_POOLCOLL_TOX_IDXH:
+ nRet = RES_POOLCOLL_HEADLINE_BASE; break;
+
+ case RES_POOLCOLL_TOX_USERH:
+ case RES_POOLCOLL_TOX_CNTNTH:
+ case RES_POOLCOLL_TOX_ILLUSH:
+ case RES_POOLCOLL_TOX_OBJECTH:
+ case RES_POOLCOLL_TOX_TABLESH:
+ case RES_POOLCOLL_TOX_AUTHORITIESH:
+ nRet = RES_POOLCOLL_TOX_IDXH; break;
+
+ default:
+ nRet = RES_POOLCOLL_REGISTER_BASE; break;
+ }
+ break;
+
+ case COLL_DOC_BITS:
+ nRet = RES_POOLCOLL_HEADLINE_BASE;
+ break;
+
+ case COLL_HTML_BITS:
+ nRet = RES_POOLCOLL_STANDARD;
+ break;
+ }
+ }
+
+ return nRet;
+}
+
+void SwDoc::RemoveAllFormatLanguageDependencies()
+{
+ /* Restore the language independent pool defaults and styles. */
+ GetAttrPool().ResetPoolDefaultItem( RES_PARATR_ADJUST );
+
+ SwTextFormatColl * pTextFormatColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD );
+
+ pTextFormatColl->ResetFormatAttr( RES_PARATR_ADJUST );
+ /* koreans do not like SvxScriptItem(TRUE) */
+ pTextFormatColl->ResetFormatAttr( RES_PARATR_SCRIPTSPACE );
+
+ SvxFrameDirectionItem aFrameDir( SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR );
+
+ size_t nCount = GetPageDescCnt();
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SwPageDesc& rDesc = GetPageDesc( i );
+ rDesc.GetMaster().SetFormatAttr( aFrameDir );
+ rDesc.GetLeft().SetFormatAttr( aFrameDir );
+ }
+
+ //#i16874# AutoKerning as default for new documents
+ GetAttrPool().ResetPoolDefaultItem( RES_CHRATR_AUTOKERN );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/rdfhelper.cxx b/sw/source/core/doc/rdfhelper.cxx
new file mode 100644
index 000000000..651a899ff
--- /dev/null
+++ b/sw/source/core/doc/rdfhelper.cxx
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <rdfhelper.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/rdf/Literal.hpp>
+#include <com/sun/star/rdf/Statement.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+
+#include <comphelper/processfactory.hxx>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <ndtxt.hxx>
+#include <unoparagraph.hxx>
+
+using namespace com::sun::star;
+
+css::uno::Sequence<css::uno::Reference<css::rdf::XURI>> SwRDFHelper::getGraphNames(
+ const css::uno::Reference<rdf::XDocumentMetadataAccess>& xDocumentMetadataAccess,
+ const css::uno::Reference<rdf::XURI>& xType)
+{
+ try
+ {
+ return xDocumentMetadataAccess->getMetadataGraphsWithType(xType);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ return uno::Sequence<uno::Reference<rdf::XURI>>();
+ }
+}
+
+css::uno::Sequence<uno::Reference<css::rdf::XURI>>
+SwRDFHelper::getGraphNames(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType)
+{
+ try
+ {
+ uno::Reference<uno::XComponentContext> xComponentContext(
+ comphelper::getProcessComponentContext());
+ // rdf::URI::create may fail with type: com.sun.star.uno.DeploymentException
+ // message: component context fails to supply service com.sun.star.rdf.URI of type com.sun.star.rdf.XURI
+ // context: cppu::ComponentContext
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel,
+ uno::UNO_QUERY);
+ return getGraphNames(xDocumentMetadataAccess, xType);
+ }
+ catch (const ::css::uno::Exception&)
+ {
+ return uno::Sequence<uno::Reference<rdf::XURI>>();
+ }
+}
+
+std::map<OUString, OUString>
+SwRDFHelper::getStatements(const css::uno::Reference<css::frame::XModel>& xModel,
+ const uno::Sequence<uno::Reference<css::rdf::XURI>>& rGraphNames,
+ const css::uno::Reference<css::rdf::XResource>& xSubject)
+{
+ std::map<OUString, OUString> aRet;
+ if (!rGraphNames.hasElements())
+ return aRet;
+
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Reference<rdf::XRepository>& xRepo = xDocumentMetadataAccess->getRDFRepository();
+ for (const uno::Reference<rdf::XURI>& xGraphName : rGraphNames)
+ {
+ uno::Reference<rdf::XNamedGraph> xGraph = xRepo->getGraph(xGraphName);
+ if (!xGraph.is())
+ continue;
+
+ uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(
+ xSubject, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
+ while (xStatements->hasMoreElements())
+ {
+ const rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
+ aRet[aStatement.Predicate->getStringValue()] = aStatement.Object->getStringValue();
+ }
+ }
+
+ return aRet;
+}
+
+std::map<OUString, OUString>
+SwRDFHelper::getStatements(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSubject)
+{
+ return getStatements(xModel, getGraphNames(xModel, rType), xSubject);
+}
+
+void SwRDFHelper::addStatement(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType, const OUString& rPath,
+ const css::uno::Reference<css::rdf::XResource>& xSubject,
+ const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ uno::Reference<rdf::XURI> xGraphName;
+ if (aGraphNames.hasElements())
+ xGraphName = aGraphNames[0];
+ else
+ {
+ uno::Sequence< uno::Reference<rdf::XURI> > xTypes = { xType };
+ xGraphName = xDocumentMetadataAccess->addMetadataFile(rPath, xTypes);
+ }
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, rValue);
+ xGraph->addStatement(xSubject, xKey, xValue);
+}
+
+bool SwRDFHelper::hasMetadataGraph(const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rType)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ return getGraphNames(xDocumentMetadataAccess, xType).hasElements();
+}
+
+void SwRDFHelper::removeStatement(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSubject,
+ const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(aGraphNames[0]);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, rValue);
+ xGraph->removeStatements(xSubject, xKey, xValue);
+}
+
+void SwRDFHelper::clearStatements(const css::uno::Reference<css::frame::XModel>& xModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSubject)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ for (const uno::Reference<rdf::XURI>& xGraphName : aGraphNames)
+ {
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(xSubject, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
+ while (xStatements->hasMoreElements())
+ {
+ rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, aStatement.Predicate->getStringValue());
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, aStatement.Object->getStringValue());
+ xGraph->removeStatements(xSubject, xKey, xValue);
+ }
+ }
+}
+
+void SwRDFHelper::cloneStatements(const css::uno::Reference<css::frame::XModel>& xSrcModel,
+ const css::uno::Reference<css::frame::XModel>& xDstModel,
+ const OUString& rType,
+ const css::uno::Reference<css::rdf::XResource>& xSrcSubject,
+ const css::uno::Reference<css::rdf::XResource>& xDstSubject)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(xSrcModel, uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ for (const uno::Reference<rdf::XURI>& xGraphName : aGraphNames)
+ {
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<container::XEnumeration> xStatements = xGraph->getStatements(xSrcSubject, uno::Reference<rdf::XURI>(), uno::Reference<rdf::XURI>());
+ while (xStatements->hasMoreElements())
+ {
+ const rdf::Statement aStatement = xStatements->nextElement().get<rdf::Statement>();
+
+ const OUString sKey = aStatement.Predicate->getStringValue();
+ const OUString sValue = aStatement.Object->getStringValue();
+ addStatement(xDstModel, rType, xGraphName->getLocalName(), xDstSubject, sKey, sValue);
+ }
+ }
+}
+
+std::map<OUString, OUString> SwRDFHelper::getTextNodeStatements(const OUString& rType, SwTextNode& rTextNode)
+{
+ uno::Reference<rdf::XResource> xTextNode(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode), uno::UNO_QUERY);
+ return getStatements(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), rType, xTextNode);
+}
+
+void SwRDFHelper::addTextNodeStatement(const OUString& rType, const OUString& rPath, SwTextNode& rTextNode, const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<rdf::XResource> xSubject(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode), uno::UNO_QUERY);
+ addStatement(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), rType, rPath, xSubject, rKey, rValue);
+}
+
+void SwRDFHelper::removeTextNodeStatement(const OUString& rType, SwTextNode& rTextNode, const OUString& rKey, const OUString& rValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ if (!aGraphNames.hasElements())
+ return;
+
+ uno::Reference<rdf::XURI> xGraphName = aGraphNames[0];
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XResource> xSubject(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode), uno::UNO_QUERY);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+ uno::Reference<rdf::XLiteral> xValue = rdf::Literal::create(xComponentContext, rValue);
+ xGraph->removeStatements(xSubject, xKey, xValue);
+}
+
+void SwRDFHelper::updateTextNodeStatement(const OUString& rType, const OUString& rPath, SwTextNode& rTextNode, const OUString& rKey, const OUString& rOldValue, const OUString& rNewValue)
+{
+ uno::Reference<uno::XComponentContext> xComponentContext(comphelper::getProcessComponentContext());
+ uno::Reference<rdf::XURI> xType = rdf::URI::create(xComponentContext, rType);
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(rTextNode.GetDoc().GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ const uno::Sequence< uno::Reference<rdf::XURI> > aGraphNames = getGraphNames(xDocumentMetadataAccess, xType);
+ uno::Reference<rdf::XURI> xGraphName;
+ if (aGraphNames.hasElements())
+ {
+ xGraphName = aGraphNames[0];
+ }
+ else
+ {
+ uno::Sequence< uno::Reference<rdf::XURI> > xTypes = { xType };
+ xGraphName = xDocumentMetadataAccess->addMetadataFile(rPath, xTypes);
+ }
+
+ uno::Reference<rdf::XNamedGraph> xGraph = xDocumentMetadataAccess->getRDFRepository()->getGraph(xGraphName);
+ uno::Reference<rdf::XResource> xSubject(SwXParagraph::CreateXParagraph(rTextNode.GetDoc(), &rTextNode), uno::UNO_QUERY);
+ uno::Reference<rdf::XURI> xKey = rdf::URI::create(xComponentContext, rKey);
+
+ if (aGraphNames.hasElements())
+ {
+ // Remove the old value.
+ uno::Reference<rdf::XLiteral> xOldValue = rdf::Literal::create(xComponentContext, rOldValue);
+ xGraph->removeStatements(xSubject, xKey, xOldValue);
+ }
+
+ // Now add it with new value.
+ uno::Reference<rdf::XLiteral> xNewValue = rdf::Literal::create(xComponentContext, rNewValue);
+ xGraph->addStatement(xSubject, xKey, xNewValue);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/sortopt.cxx b/sw/source/core/doc/sortopt.cxx
new file mode 100644
index 000000000..b5fc62ba8
--- /dev/null
+++ b/sw/source/core/doc/sortopt.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <i18nlangtag/lang.h>
+#include <sortopt.hxx>
+
+SwSortKey::SwSortKey() :
+ eSortOrder( SwSortOrder::Ascending ),
+ nColumnId( 0 ),
+ bIsNumeric( true )
+{
+}
+
+SwSortKey::SwSortKey(sal_uInt16 nId, const OUString& rSrtType, SwSortOrder eOrder) :
+ sSortType( rSrtType ),
+ eSortOrder( eOrder ),
+ nColumnId( nId ),
+ bIsNumeric( rSrtType.isEmpty() )
+{
+}
+
+SwSortOptions::SwSortOptions()
+ : eDirection( SwSortDirection::Rows ),
+ cDeli( 9 ),
+ nLanguage( LANGUAGE_SYSTEM ),
+ bTable( false ),
+ bIgnoreCase( false )
+{
+}
+
+SwSortOptions::SwSortOptions(const SwSortOptions& rOpt) :
+ aKeys( rOpt.aKeys ),
+ eDirection( rOpt.eDirection ),
+ cDeli( rOpt.cDeli ),
+ nLanguage( rOpt.nLanguage ),
+ bTable( rOpt.bTable ),
+ bIgnoreCase( rOpt.bIgnoreCase )
+{
+}
+
+SwSortOptions::~SwSortOptions()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/swserv.cxx b/sw/source/core/doc/swserv.cxx
new file mode 100644
index 000000000..83ea7affe
--- /dev/null
+++ b/sw/source/core/doc/swserv.cxx
@@ -0,0 +1,320 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sot/exchange.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swserv.hxx>
+#include <swbaslnk.hxx>
+#include <mvsave.hxx>
+#include <IMark.hxx>
+#include <bookmark.hxx>
+#include <pam.hxx>
+#include <shellio.hxx>
+
+using namespace ::com::sun::star;
+
+SwServerObject::~SwServerObject()
+{
+}
+
+bool SwServerObject::GetData( uno::Any & rData,
+ const OUString & rMimeType, bool )
+{
+ bool bRet = false;
+ WriterRef xWrt;
+ switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
+ {
+ case SotClipboardFormatId::STRING:
+ ::GetASCWriter( std::u16string_view(), OUString(), xWrt );
+ break;
+
+ case SotClipboardFormatId::RTF:
+ case SotClipboardFormatId::RICHTEXT:
+ // mba: no BaseURL for data exchange
+ ::GetRTFWriter( std::u16string_view(), OUString(), xWrt );
+ break;
+ default: break;
+ }
+
+ if( xWrt.is() )
+ {
+ SwPaM* pPam = nullptr;
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if( m_CNTNT_TYPE.pBkmk->IsExpanded() )
+ {
+ // Span area
+ pPam = new SwPaM( m_CNTNT_TYPE.pBkmk->GetMarkPos(),
+ m_CNTNT_TYPE.pBkmk->GetOtherMarkPos() );
+ }
+ break;
+
+ case TABLE_SERVER:
+ pPam = new SwPaM( *m_CNTNT_TYPE.pTableNd,
+ *m_CNTNT_TYPE.pTableNd->EndOfSectionNode() );
+ break;
+
+ case SECTION_SERVER:
+ pPam = new SwPaM( SwPosition( *m_CNTNT_TYPE.pSectNd ) );
+ pPam->Move( fnMoveForward );
+ pPam->SetMark();
+ pPam->GetPoint()->nNode = *m_CNTNT_TYPE.pSectNd->EndOfSectionNode();
+ pPam->Move( fnMoveBackward );
+ break;
+ case NONE_SERVER: break;
+ }
+
+ if( pPam )
+ {
+ // Create stream
+ SvMemoryStream aMemStm( 65535, 65535 );
+ SwWriter aWrt( aMemStm, *pPam, false );
+ if( !aWrt.Write( xWrt ).IsError() )
+ {
+ aMemStm.WriteChar( '\0' ); // append a zero char
+ rData <<= uno::Sequence< sal_Int8 >(
+ static_cast<sal_Int8 const *>(aMemStm.GetData()),
+ aMemStm.Tell() );
+ bRet = true;
+ }
+ delete pPam;
+ }
+ }
+ return bRet;
+}
+
+void SwServerObject::SendDataChanged( const SwPosition& rPos )
+{
+ // Is someone interested in our changes?
+ if( !HasDataLinks() )
+ return;
+
+ bool bCall = false;
+ const SwStartNode* pNd = nullptr;
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if( m_CNTNT_TYPE.pBkmk->IsExpanded() )
+ {
+ bCall = m_CNTNT_TYPE.pBkmk->GetMarkStart() <= rPos
+ && rPos < m_CNTNT_TYPE.pBkmk->GetMarkEnd();
+ }
+ break;
+
+ case TABLE_SERVER: pNd = m_CNTNT_TYPE.pTableNd; break;
+ case SECTION_SERVER: pNd = m_CNTNT_TYPE.pSectNd; break;
+ case NONE_SERVER: break;
+ }
+ if( pNd )
+ {
+ SwNodeOffset nNd = rPos.nNode.GetIndex();
+ bCall = pNd->GetIndex() < nNd && nNd < pNd->EndOfSectionIndex();
+ }
+
+ if( bCall )
+ {
+ // Recognize recursions and flag them
+ IsLinkInServer( nullptr );
+ SvLinkSource::NotifyDataChanged();
+ }
+}
+
+void SwServerObject::SendDataChanged( const SwPaM& rRange )
+{
+ // Is someone interested in our changes?
+ if( !HasDataLinks() )
+ return;
+
+ bool bCall = false;
+ const SwStartNode* pNd = nullptr;
+ const SwPosition* pStt = rRange.Start(), *pEnd = rRange.End();
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if(m_CNTNT_TYPE.pBkmk->IsExpanded())
+ {
+ bCall = *pStt <= m_CNTNT_TYPE.pBkmk->GetMarkEnd()
+ && *pEnd > m_CNTNT_TYPE.pBkmk->GetMarkStart();
+ }
+ break;
+
+ case TABLE_SERVER: pNd = m_CNTNT_TYPE.pTableNd; break;
+ case SECTION_SERVER: pNd = m_CNTNT_TYPE.pSectNd; break;
+ case NONE_SERVER: break;
+ }
+ if( pNd )
+ {
+ // Is the start area within the node area?
+ bCall = pStt->nNode.GetIndex() < pNd->EndOfSectionIndex() &&
+ pEnd->nNode.GetIndex() >= pNd->GetIndex();
+ }
+
+ if( bCall )
+ {
+ // Recognize recursions and flag them
+ IsLinkInServer( nullptr );
+ SvLinkSource::NotifyDataChanged();
+ }
+}
+
+bool SwServerObject::IsLinkInServer( const SwBaseLink* pChkLnk ) const
+{
+ SwNodeOffset nSttNd(0), nEndNd(0);
+ const SwNode* pNd = nullptr;
+ const SwNodes* pNds = nullptr;
+
+ switch( m_eType )
+ {
+ case BOOKMARK_SERVER:
+ if( m_CNTNT_TYPE.pBkmk->IsExpanded() )
+ {
+ const SwPosition* pStt = &m_CNTNT_TYPE.pBkmk->GetMarkStart(),
+ * pEnd = &m_CNTNT_TYPE.pBkmk->GetMarkEnd();
+
+ nSttNd = pStt->nNode.GetIndex();
+ nEndNd = pEnd->nNode.GetIndex();
+ pNds = &pStt->nNode.GetNodes();
+ }
+ break;
+
+ case TABLE_SERVER: pNd = m_CNTNT_TYPE.pTableNd; break;
+ case SECTION_SERVER: pNd = m_CNTNT_TYPE.pSectNd; break;
+
+ case SECTION_SERVER+1:
+ return true;
+ }
+
+ if( pNd )
+ {
+ nSttNd = pNd->GetIndex();
+ nEndNd = pNd->EndOfSectionIndex();
+ pNds = &pNd->GetNodes();
+ }
+
+ if( nSttNd && nEndNd )
+ {
+ // Get LinkManager
+ const ::sfx2::SvBaseLinks& rLnks = pNds->GetDoc().getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+
+ // To avoid recursions: convert ServerType!
+ SwServerObject::ServerModes eSave = m_eType;
+ if( !pChkLnk )
+ const_cast<SwServerObject*>(this)->m_eType = NONE_SERVER;
+ for( size_t n = rLnks.size(); n; )
+ {
+ const ::sfx2::SvBaseLink* pLnk = &(*rLnks[ --n ]);
+ if (sfx2::SvBaseLinkObjectType::ClientGraphic != pLnk->GetObjType() &&
+ dynamic_cast<const SwBaseLink*>( pLnk) != nullptr &&
+ !static_cast<const SwBaseLink*>(pLnk)->IsNoDataFlag() &&
+ static_cast<const SwBaseLink*>(pLnk)->IsInRange( nSttNd, nEndNd ))
+ {
+ if( pChkLnk )
+ {
+ if( pLnk == pChkLnk ||
+ static_cast<const SwBaseLink*>(pLnk)->IsRecursion( pChkLnk ) )
+ return true;
+ }
+ else if( static_cast<const SwBaseLink*>(pLnk)->IsRecursion( static_cast<const SwBaseLink*>(pLnk) ) )
+ const_cast<SwBaseLink*>(static_cast<const SwBaseLink*>(pLnk))->SetNoDataFlag();
+ }
+ }
+ if( !pChkLnk )
+ const_cast<SwServerObject*>(this)->m_eType = eSave;
+ }
+
+ return false;
+}
+
+void SwServerObject::SetNoServer()
+{
+ if(m_eType == BOOKMARK_SERVER && m_CNTNT_TYPE.pBkmk)
+ {
+ ::sw::mark::DdeBookmark* const pDdeBookmark = dynamic_cast< ::sw::mark::DdeBookmark* >(m_CNTNT_TYPE.pBkmk);
+ if(pDdeBookmark)
+ {
+ m_CNTNT_TYPE.pBkmk = nullptr;
+ m_eType = NONE_SERVER;
+ pDdeBookmark->SetRefObject(nullptr);
+ }
+ }
+}
+
+void SwServerObject::SetDdeBookmark( ::sw::mark::IMark& rBookmark)
+{
+ ::sw::mark::DdeBookmark* const pDdeBookmark = dynamic_cast< ::sw::mark::DdeBookmark* >(&rBookmark);
+ if(pDdeBookmark)
+ {
+ m_eType = BOOKMARK_SERVER;
+ m_CNTNT_TYPE.pBkmk = &rBookmark;
+ pDdeBookmark->SetRefObject(this);
+ }
+ else
+ OSL_FAIL("SwServerObject::SetNoServer(..)"
+ " - setting a bookmark that is not DDE-capable");
+}
+
+SwDataChanged::SwDataChanged( const SwPaM& rPam )
+ : m_pPam( &rPam ), m_pPos( nullptr ), m_rDoc( rPam.GetDoc() )
+{
+ m_nContent = rPam.GetPoint()->nContent.GetIndex();
+}
+
+SwDataChanged::SwDataChanged( SwDoc& rDc, const SwPosition& rPos )
+ : m_pPam( nullptr ), m_pPos( &rPos ), m_rDoc( rDc )
+{
+ m_nContent = rPos.nContent.GetIndex();
+}
+
+SwDataChanged::~SwDataChanged()
+{
+ // JP 09.04.96: Only if the Layout is available (thus during input)
+ if( !m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ return;
+
+ const ::sfx2::SvLinkSources& rServers = m_rDoc.getIDocumentLinksAdministration().GetLinkManager().GetServers();
+
+ ::sfx2::SvLinkSources aTemp(rServers);
+ for( const auto& rpLinkSrc : aTemp )
+ {
+ ::sfx2::SvLinkSourceRef refObj( rpLinkSrc );
+ // Anyone else interested in the Object?
+ if( refObj->HasDataLinks())
+ if (auto pServerObj = dynamic_cast<SwServerObject*>( refObj.get() ))
+ {
+ if( m_pPos )
+ pServerObj->SendDataChanged( *m_pPos );
+ else
+ pServerObj->SendDataChanged( *m_pPam );
+ }
+
+ // We shouldn't have a connection anymore
+ if( !refObj->HasDataLinks() )
+ {
+ // Then remove from the list
+ m_rDoc.getIDocumentLinksAdministration().GetLinkManager().RemoveServer( rpLinkSrc );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/swstylemanager.cxx b/sw/source/core/doc/swstylemanager.cxx
new file mode 100644
index 000000000..eec167202
--- /dev/null
+++ b/sw/source/core/doc/swstylemanager.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "swstylemanager.hxx"
+#include <svl/stylepool.hxx>
+#include <istyleaccess.hxx>
+#include <unordered_map>
+#include <osl/diagnose.h>
+
+typedef std::unordered_map< OUString,
+ std::shared_ptr<SfxItemSet> > SwStyleNameCache;
+
+namespace {
+
+class SwStyleCache
+{
+ SwStyleNameCache mMap;
+public:
+ SwStyleCache() {}
+ void addStyleName( const std::shared_ptr<SfxItemSet>& pStyle )
+ { mMap[ StylePool::nameOf(pStyle) ] = pStyle; }
+ void addCompletePool( StylePool& rPool );
+ std::shared_ptr<SfxItemSet> getByName( const OUString& rName ) { return mMap[rName]; }
+};
+
+}
+
+void SwStyleCache::addCompletePool( StylePool& rPool )
+{
+ std::unique_ptr<IStylePoolIteratorAccess> pIter = rPool.createIterator();
+ std::shared_ptr<SfxItemSet> pStyle = pIter->getNext();
+ while( pStyle )
+ {
+ OUString aName( StylePool::nameOf(pStyle) );
+ mMap[ aName ] = pStyle;
+ pStyle = pIter->getNext();
+ }
+}
+
+namespace {
+
+class SwStyleManager : public IStyleAccess
+{
+ StylePool m_aAutoCharPool;
+ StylePool m_aAutoParaPool;
+ std::unique_ptr<SwStyleCache> mpCharCache;
+ std::unique_ptr<SwStyleCache> mpParaCache;
+
+public:
+ // accept empty item set for ignorable paragraph items.
+ explicit SwStyleManager(SfxItemSet const* pIgnorableParagraphItems)
+ : m_aAutoParaPool(pIgnorableParagraphItems)
+ {}
+ virtual std::shared_ptr<SfxItemSet> getAutomaticStyle( const SfxItemSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily,
+ const OUString* pParentName = nullptr ) override;
+ virtual std::shared_ptr<SfxItemSet> getByName( const OUString& rName,
+ IStyleAccess::SwAutoStyleFamily eFamily ) override;
+ virtual void getAllStyles( std::vector<std::shared_ptr<SfxItemSet>> &rStyles,
+ IStyleAccess::SwAutoStyleFamily eFamily ) override;
+ virtual std::shared_ptr<SfxItemSet> cacheAutomaticStyle( const SfxItemSet& rSet,
+ SwAutoStyleFamily eFamily ) override;
+ virtual void clearCaches() override;
+};
+
+}
+
+std::unique_ptr<IStyleAccess> createStyleManager( SfxItemSet const * pIgnorableParagraphItems )
+{
+ return std::make_unique<SwStyleManager>( pIgnorableParagraphItems );
+}
+
+void SwStyleManager::clearCaches()
+{
+ mpCharCache.reset();
+ mpParaCache.reset();
+}
+
+std::shared_ptr<SfxItemSet> SwStyleManager::getAutomaticStyle( const SfxItemSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily,
+ const OUString* pParentName )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ return rAutoPool.insertItemSet( rSet, pParentName );
+}
+
+std::shared_ptr<SfxItemSet> SwStyleManager::cacheAutomaticStyle( const SfxItemSet& rSet,
+ IStyleAccess::SwAutoStyleFamily eFamily )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ std::shared_ptr<SfxItemSet> pStyle = rAutoPool.insertItemSet( rSet );
+ if (eFamily == IStyleAccess::AUTO_STYLE_CHAR)
+ {
+ if (!mpCharCache)
+ mpCharCache.reset(new SwStyleCache());
+ mpCharCache->addStyleName( pStyle );
+ }
+ else
+ {
+ if (!mpParaCache)
+ mpParaCache.reset(new SwStyleCache());
+ mpParaCache->addStyleName( pStyle );
+ }
+ return pStyle;
+}
+
+std::shared_ptr<SfxItemSet> SwStyleManager::getByName( const OUString& rName,
+ IStyleAccess::SwAutoStyleFamily eFamily )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ std::unique_ptr<SwStyleCache> &rpCache = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? mpCharCache : mpParaCache;
+ if( !rpCache )
+ rpCache.reset(new SwStyleCache());
+ std::shared_ptr<SfxItemSet> pStyle = rpCache->getByName( rName );
+ if( !pStyle )
+ {
+ // Ok, ok, it's allowed to ask for uncached styles (from UNO) but it should not be done
+ // during loading a document
+ OSL_FAIL( "Don't ask for uncached styles" );
+ rpCache->addCompletePool( rAutoPool );
+ pStyle = rpCache->getByName( rName );
+ }
+ return pStyle;
+}
+
+void SwStyleManager::getAllStyles( std::vector<std::shared_ptr<SfxItemSet>> &rStyles,
+ IStyleAccess::SwAutoStyleFamily eFamily )
+{
+ StylePool& rAutoPool
+ = eFamily == IStyleAccess::AUTO_STYLE_CHAR ? m_aAutoCharPool : m_aAutoParaPool;
+ // setup <StylePool> iterator, which skips unused styles and ignorable items
+ std::unique_ptr<IStylePoolIteratorAccess> pIter = rAutoPool.createIterator( true, true );
+ std::shared_ptr<SfxItemSet> pStyle = pIter->getNext();
+ while( pStyle )
+ {
+ rStyles.push_back( pStyle );
+
+ pStyle = pIter->getNext();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/swstylemanager.hxx b/sw/source/core/doc/swstylemanager.hxx
new file mode 100644
index 000000000..4b27b0274
--- /dev/null
+++ b/sw/source/core/doc/swstylemanager.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_DOC_SWSTYLEMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_DOC_SWSTYLEMANAGER_HXX
+
+#include <memory>
+
+class IStyleAccess;
+class SfxItemSet;
+
+std::unique_ptr<IStyleAccess> createStyleManager(SfxItemSet const* pIgnorableParagraphItems);
+
+#endif // INCLUDED_SW_SOURCE_CORE_DOC_SWSTYLEMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/tblafmt.cxx b/sw/source/core/doc/tblafmt.cxx
new file mode 100644
index 000000000..5a1357f99
--- /dev/null
+++ b/sw/source/core/doc/tblafmt.cxx
@@ -0,0 +1,1237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/fileformat.h>
+#include <tools/stream.hxx>
+#include <sfx2/docfile.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+#include <swtable.hxx>
+#include <swtblfmt.hxx>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <swtypes.hxx>
+#include <doc.hxx>
+#include <poolfmt.hxx>
+#include <tblafmt.hxx>
+#include <cellatr.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <hintids.hxx>
+#include <fmtornt.hxx>
+#include <editsh.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtrowsplt.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+
+#include <editeng/adjustitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <editeng/legacyitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <svx/algitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <legacyitem.hxx>
+
+#include <memory>
+#include <vector>
+
+/*
+ * XXX: BIG RED NOTICE! Changes MUST be binary file format compatible and MUST
+ * be synchronized with Calc's ScAutoFormat sc/source/core/tool/autoform.cxx
+ */
+
+using ::editeng::SvxBorderLine;
+
+// until SO5PF
+const sal_uInt16 AUTOFORMAT_ID_X = 9501;
+const sal_uInt16 AUTOFORMAT_ID_358 = 9601;
+const sal_uInt16 AUTOFORMAT_DATA_ID_X = 9502;
+
+// from SO5
+//! In follow-up versions these IDs' values need to increase
+const sal_uInt16 AUTOFORMAT_ID_504 = 9801;
+const sal_uInt16 AUTOFORMAT_DATA_ID_504 = 9802;
+
+const sal_uInt16 AUTOFORMAT_DATA_ID_552 = 9902;
+
+// --- from 680/dr25 on: store strings as UTF-8
+const sal_uInt16 AUTOFORMAT_ID_680DR25 = 10021;
+
+// --- Bug fix to fdo#31005: Table Autoformats does not save/apply all properties (Writer and Calc)
+const sal_uInt16 AUTOFORMAT_ID_31005 = 10041;
+const sal_uInt16 AUTOFORMAT_DATA_ID_31005 = 10042;
+
+// current version
+const sal_uInt16 AUTOFORMAT_ID = AUTOFORMAT_ID_31005;
+const sal_uInt16 AUTOFORMAT_DATA_ID = AUTOFORMAT_DATA_ID_31005;
+const sal_uInt16 AUTOFORMAT_FILE_VERSION= SOFFICE_FILEFORMAT_50;
+
+SwBoxAutoFormat* SwTableAutoFormat::s_pDefaultBoxAutoFormat = nullptr;
+
+constexpr OUStringLiteral AUTOTABLE_FORMAT_NAME = u"autotbl.fmt";
+
+namespace
+{
+ /// Begins a writer-specific data block. Call before serializing any writer-specific properties.
+ sal_uInt64 BeginSwBlock(SvStream& rStream)
+ {
+ // We need to write down the offset of the end of the writer-specific data, so that
+ // calc can skip it. We'll only have that value after writing the data, so we
+ // write a placeholder value first, write the data, then jump back and write the
+ // real offset.
+
+ // Note that we explicitly use sal_uInt64 instead of sal_Size (which can be 32
+ // or 64 depending on platform) to ensure 64-bit portability on this front. I don't
+ // actually know if autotbl.fmt as a whole is portable, since that requires all serialization
+ // logic to be written with portability in mind.
+ sal_uInt64 whereToWriteEndOfSwBlock = rStream.Tell();
+
+ rStream.WriteUInt64( 0 ); // endOfSwBlock
+
+ return whereToWriteEndOfSwBlock;
+ }
+
+ /// Ends a writer-specific data block. Call after serializing writer-specific properties.
+ /// Closes a corresponding BeginSwBlock call.
+ void EndSwBlock(SvStream& rStream, sal_uInt64 whereToWriteEndOfSwBlock)
+ {
+ sal_uInt64 endOfSwBlock = rStream.Tell();
+ rStream.Seek(whereToWriteEndOfSwBlock);
+ rStream.WriteUInt64( endOfSwBlock );
+ rStream.Seek(endOfSwBlock);
+ }
+
+ /**
+ Helper class for writer-specific blocks. Begins a writer-specific block on construction,
+ and closes it on destruction.
+
+ See also: BeginSwBlock and EndSwBlock.
+ */
+ class WriterSpecificAutoFormatBlock
+ {
+ public:
+ explicit WriterSpecificAutoFormatBlock(SvStream& rStream)
+ : mrStream(rStream)
+ , mnWhereToWriteEndOfBlock(BeginSwBlock(rStream))
+ {
+ }
+
+ ~WriterSpecificAutoFormatBlock() { EndSwBlock(mrStream, mnWhereToWriteEndOfBlock); }
+
+ private:
+ WriterSpecificAutoFormatBlock(WriterSpecificAutoFormatBlock const&) = delete;
+ WriterSpecificAutoFormatBlock& operator=(WriterSpecificAutoFormatBlock const&) = delete;
+
+ SvStream& mrStream;
+ sal_uInt64 mnWhereToWriteEndOfBlock;
+ };
+
+ /// Checks whether a writer-specific block exists (i.e. size is not zero)
+ sal_Int64 WriterSpecificBlockExists(SvStream &stream)
+ {
+ sal_uInt64 endOfSwBlock = 0;
+ stream.ReadUInt64( endOfSwBlock );
+
+ // end-of-block pointing to itself indicates a zero-size block.
+ return endOfSwBlock - stream.Tell();
+ }
+}
+
+// Struct with version numbers of the Items
+
+struct SwAfVersions : public AutoFormatVersions
+{
+public:
+ sal_uInt16 m_nTextOrientationVersion;
+ sal_uInt16 m_nVerticalAlignmentVersion;
+
+ SwAfVersions();
+ void Load( SvStream& rStream, sal_uInt16 nVer );
+ static void Write(SvStream& rStream, sal_uInt16 fileVersion);
+};
+
+SwAfVersions::SwAfVersions()
+: m_nTextOrientationVersion(0),
+ m_nVerticalAlignmentVersion(0)
+{
+}
+
+void SwAfVersions::Load( SvStream& rStream, sal_uInt16 nVer )
+{
+ LoadBlockA(rStream, nVer);
+ if (nVer >= AUTOFORMAT_ID_31005 && WriterSpecificBlockExists(rStream))
+ {
+ rStream.ReadUInt16( m_nTextOrientationVersion );
+ rStream.ReadUInt16( m_nVerticalAlignmentVersion );
+ }
+ LoadBlockB(rStream, nVer);
+}
+
+void SwAfVersions::Write(SvStream& rStream, sal_uInt16 fileVersion)
+{
+ AutoFormatVersions::WriteBlockA(rStream, fileVersion);
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ {
+ WriterSpecificAutoFormatBlock block(rStream);
+
+ rStream.WriteUInt16(legacy::SvxFrameDirection::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SwFormatVert::GetVersion(fileVersion));
+ }
+
+ AutoFormatVersions::WriteBlockB(rStream, fileVersion);
+}
+
+
+
+SwBoxAutoFormat::SwBoxAutoFormat()
+: m_aTextOrientation(std::make_unique<SvxFrameDirectionItem>(SvxFrameDirection::Environment, RES_FRAMEDIR)),
+ m_aVerticalAlignment(std::make_unique<SwFormatVertOrient>(0, css::text::VertOrientation::NONE, css::text::RelOrientation::FRAME)),
+ m_eSysLanguage(::GetAppLanguage()),
+ m_eNumFormatLanguage(::GetAppLanguage())
+{
+ // need to set default instances for base class AutoFormatBase here
+ // due to resource defines (e.g. RES_CHRATR_FONT) which are not available
+ // in svx and different in the different usages of derivations
+ m_aFont = std::make_unique<SvxFontItem>(*GetDfltAttr( RES_CHRATR_FONT ) );
+ m_aHeight = std::make_unique<SvxFontHeightItem>(240, 100, RES_CHRATR_FONTSIZE );
+ m_aWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, RES_CHRATR_WEIGHT );
+ m_aPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, RES_CHRATR_POSTURE );
+ m_aCJKFont = std::make_unique<SvxFontItem>(*GetDfltAttr( RES_CHRATR_CJK_FONT ) );
+ m_aCJKHeight = std::make_unique<SvxFontHeightItem>(240, 100, RES_CHRATR_CJK_FONTSIZE );
+ m_aCJKWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, RES_CHRATR_CJK_WEIGHT );
+ m_aCJKPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, RES_CHRATR_CJK_POSTURE );
+ m_aCTLFont = std::make_unique<SvxFontItem>(*GetDfltAttr( RES_CHRATR_CTL_FONT ) );
+ m_aCTLHeight = std::make_unique<SvxFontHeightItem>(240, 100, RES_CHRATR_CTL_FONTSIZE );
+ m_aCTLWeight = std::make_unique<SvxWeightItem>(WEIGHT_NORMAL, RES_CHRATR_CTL_WEIGHT );
+ m_aCTLPosture = std::make_unique<SvxPostureItem>(ITALIC_NONE, RES_CHRATR_CTL_POSTURE );
+ m_aUnderline = std::make_unique<SvxUnderlineItem>(LINESTYLE_NONE, RES_CHRATR_UNDERLINE );
+ m_aOverline = std::make_unique<SvxOverlineItem>(LINESTYLE_NONE, RES_CHRATR_OVERLINE );
+ m_aCrossedOut = std::make_unique<SvxCrossedOutItem>(STRIKEOUT_NONE, RES_CHRATR_CROSSEDOUT );
+ m_aContour = std::make_unique<SvxContourItem>(false, RES_CHRATR_CONTOUR );
+ m_aShadowed = std::make_unique<SvxShadowedItem>(false, RES_CHRATR_SHADOWED );
+ m_aColor = std::make_unique<SvxColorItem>(RES_CHRATR_COLOR );
+ m_aBox = std::make_unique<SvxBoxItem>(RES_BOX );
+ m_aTLBR = std::make_unique<SvxLineItem>(0 );
+ m_aBLTR = std::make_unique<SvxLineItem>(0 );
+ m_aBackground = std::make_unique<SvxBrushItem>(RES_BACKGROUND );
+ m_aAdjust = std::make_unique<SvxAdjustItem>(SvxAdjust::Left, RES_PARATR_ADJUST );
+ m_aHorJustify = std::make_unique<SvxHorJustifyItem>(SvxCellHorJustify::Standard, 0);
+ m_aVerJustify = std::make_unique<SvxVerJustifyItem>(SvxCellVerJustify::Standard, 0);
+ m_aStacked = std::make_unique<SfxBoolItem>(0 );
+ m_aMargin = std::make_unique<SvxMarginItem>( TypedWhichId<SvxMarginItem>(0) );
+ m_aLinebreak = std::make_unique<SfxBoolItem>(0 );
+ m_aRotateAngle = std::make_unique<SfxInt32Item>(0 );
+ m_aRotateMode = std::make_unique<SvxRotateModeItem>(SVX_ROTATE_MODE_STANDARD, TypedWhichId<SvxRotateModeItem>(0) );
+
+// FIXME - add attribute IDs for the diagonal line items
+// aTLBR( RES_... ),
+// aBLTR( RES_... ),
+ m_aBox->SetAllDistances(55);
+}
+
+SwBoxAutoFormat::SwBoxAutoFormat( const SwBoxAutoFormat& rNew )
+: AutoFormatBase(rNew),
+ m_aTextOrientation(rNew.m_aTextOrientation->Clone()),
+ m_aVerticalAlignment(rNew.m_aVerticalAlignment->Clone()),
+ m_sNumFormatString( rNew.m_sNumFormatString ),
+ m_eSysLanguage( rNew.m_eSysLanguage ),
+ m_eNumFormatLanguage( rNew.m_eNumFormatLanguage )
+{
+}
+
+SwBoxAutoFormat::~SwBoxAutoFormat()
+{
+}
+
+SwBoxAutoFormat& SwBoxAutoFormat::operator=(const SwBoxAutoFormat& rRef)
+{
+ // check self-assignment
+ if(this == &rRef)
+ {
+ return *this;
+ }
+
+ // call baseclass implementation
+ AutoFormatBase::operator=(rRef);
+
+ // copy local members - this will use ::Clone() on all involved Items
+ SetTextOrientation(rRef.GetTextOrientation());
+ SetVerticalAlignment(rRef.GetVerticalAlignment());
+ SetNumFormatString(rRef.GetNumFormatString());
+ SetSysLanguage(rRef.GetSysLanguage());
+ SetNumFormatLanguage(rRef.GetNumFormatLanguage());
+
+ // m_wXObject used to not be copied before 1e2682235cded9a7cd90e55f0bfc60a1285e9a46
+ // "WIP: Further preparations for deeper Item changes" by this operator, so do not do it now, too
+ // rRef.SetXObject(GetXObject());
+
+ return *this;
+}
+
+bool SwBoxAutoFormat::operator==(const SwBoxAutoFormat& rRight) const
+{
+ return GetBackground().GetColor() == rRight.GetBackground().GetColor();
+}
+
+bool SwBoxAutoFormat::Load( SvStream& rStream, const SwAfVersions& rVersions, sal_uInt16 nVer )
+{
+ LoadBlockA( rStream, rVersions, nVer );
+
+ if (nVer >= AUTOFORMAT_DATA_ID_31005)
+ {
+ sal_Int64 const nSize(WriterSpecificBlockExists(rStream));
+ if (0 < nSize && nSize < std::numeric_limits<sal_uInt16>::max())
+ {
+ legacy::SvxFrameDirection::Create(*m_aTextOrientation, rStream, rVersions.m_nTextOrientationVersion);
+ // HORRIBLE HACK to read both 32-bit and 64-bit "long": abuse nSize
+ legacy::SwFormatVert::Create(*m_aVerticalAlignment, rStream, /*rVersions.m_nVerticalAlignmentVersion*/ nSize);
+ }
+ }
+
+ LoadBlockB( rStream, rVersions, nVer );
+
+ if( 0 == rVersions.nNumFormatVersion )
+ {
+ sal_uInt16 eSys, eLge;
+ // --- from 680/dr25 on: store strings as UTF-8
+ rtl_TextEncoding eCharSet = (nVer >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet();
+ m_sNumFormatString = rStream.ReadUniOrByteString( eCharSet );
+ rStream.ReadUInt16( eSys ).ReadUInt16( eLge );
+ m_eSysLanguage = LanguageType(eSys);
+ m_eNumFormatLanguage = LanguageType(eLge);
+ if ( m_eSysLanguage == LANGUAGE_SYSTEM ) // from old versions (Calc)
+ m_eSysLanguage = ::GetAppLanguage();
+ }
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+bool SwBoxAutoFormat::Save( SvStream& rStream, sal_uInt16 fileVersion ) const
+{
+ SaveBlockA( rStream, fileVersion );
+
+ if (fileVersion >= SOFFICE_FILEFORMAT_50)
+ {
+ WriterSpecificAutoFormatBlock block(rStream);
+
+ legacy::SvxFrameDirection::Store(*m_aTextOrientation, rStream, legacy::SvxFrameDirection::GetVersion(fileVersion));
+ legacy::SwFormatVert::Store(*m_aVerticalAlignment, rStream, legacy::SwFormatVert::GetVersion(fileVersion));
+ }
+
+ SaveBlockB( rStream, fileVersion );
+
+ // --- from 680/dr25 on: store strings as UTF-8
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStream, m_sNumFormatString,
+ RTL_TEXTENCODING_UTF8);
+ rStream.WriteUInt16( static_cast<sal_uInt16>(m_eSysLanguage) ).WriteUInt16( static_cast<sal_uInt16>(m_eNumFormatLanguage) );
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+SwTableAutoFormat::SwTableAutoFormat( const OUString& rName )
+ : m_aName( rName )
+ , m_nStrResId( USHRT_MAX )
+ , m_aKeepWithNextPara(std::make_shared<SvxFormatKeepItem>(false, RES_KEEP))
+ , m_aRepeatHeading( 0 )
+ , m_bLayoutSplit( true )
+ , m_bRowSplit( true )
+ , m_bCollapsingBorders(true)
+ , m_aShadow(std::make_shared<SvxShadowItem>(RES_SHADOW))
+ , m_bHidden( false )
+ , m_bUserDefined( true )
+{
+ m_bInclFont = true;
+ m_bInclJustify = true;
+ m_bInclFrame = true;
+ m_bInclBackground = true;
+ m_bInclValueFormat = true;
+ m_bInclWidthHeight = true;
+}
+
+SwTableAutoFormat::SwTableAutoFormat( const SwTableAutoFormat& rNew )
+ : m_aShadow(std::make_shared<SvxShadowItem>(RES_SHADOW))
+{
+ for(SwBoxAutoFormat* & rp : m_aBoxAutoFormat)
+ rp = nullptr;
+ *this = rNew;
+}
+
+SwTableAutoFormat& SwTableAutoFormat::operator=( const SwTableAutoFormat& rNew )
+{
+ if (&rNew == this)
+ return *this;
+
+ for( sal_uInt8 n = 0; n < 16; ++n )
+ {
+ if( m_aBoxAutoFormat[ n ] )
+ delete m_aBoxAutoFormat[ n ];
+
+ SwBoxAutoFormat* pFormat = rNew.m_aBoxAutoFormat[ n ];
+ if( pFormat ) // if is set -> copy
+ m_aBoxAutoFormat[ n ] = new SwBoxAutoFormat( *pFormat );
+ else // else default
+ m_aBoxAutoFormat[ n ] = nullptr;
+ }
+
+ m_aName = rNew.m_aName;
+ m_nStrResId = rNew.m_nStrResId;
+ m_bInclFont = rNew.m_bInclFont;
+ m_bInclJustify = rNew.m_bInclJustify;
+ m_bInclFrame = rNew.m_bInclFrame;
+ m_bInclBackground = rNew.m_bInclBackground;
+ m_bInclValueFormat = rNew.m_bInclValueFormat;
+ m_bInclWidthHeight = rNew.m_bInclWidthHeight;
+
+ m_aKeepWithNextPara.reset(rNew.m_aKeepWithNextPara->Clone());
+ m_aRepeatHeading = rNew.m_aRepeatHeading;
+ m_bLayoutSplit = rNew.m_bLayoutSplit;
+ m_bRowSplit = rNew.m_bRowSplit;
+ m_bCollapsingBorders = rNew.m_bCollapsingBorders;
+ m_aShadow.reset(rNew.m_aShadow->Clone());
+ m_bHidden = rNew.m_bHidden;
+ m_bUserDefined = rNew.m_bUserDefined;
+
+ return *this;
+}
+
+SwTableAutoFormat::~SwTableAutoFormat()
+{
+ SwBoxAutoFormat** ppFormat = m_aBoxAutoFormat;
+ for( sal_uInt8 n = 0; n < 16; ++n, ++ppFormat )
+ if( *ppFormat )
+ delete *ppFormat;
+}
+
+void SwTableAutoFormat::SetBoxFormat( const SwBoxAutoFormat& rNew, sal_uInt8 nPos )
+{
+ OSL_ENSURE( nPos < 16, "wrong area" );
+
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ nPos ];
+ if( pFormat ) // if is set -> copy
+ *m_aBoxAutoFormat[ nPos ] = rNew;
+ else // else set anew
+ m_aBoxAutoFormat[ nPos ] = new SwBoxAutoFormat( rNew );
+}
+
+const SwBoxAutoFormat& SwTableAutoFormat::GetBoxFormat( sal_uInt8 nPos ) const
+{
+ OSL_ENSURE( nPos < 16, "wrong area" );
+
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ nPos ];
+ if( pFormat ) // if is set -> copy
+ return *pFormat;
+ else // else return the default
+ {
+ // If it doesn't exist yet:
+ if( !s_pDefaultBoxAutoFormat )
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat;
+ return *s_pDefaultBoxAutoFormat;
+ }
+}
+
+SwBoxAutoFormat& SwTableAutoFormat::GetBoxFormat( sal_uInt8 nPos )
+{
+ SAL_WARN_IF(!(nPos < 16), "sw.core", "GetBoxFormat wrong area");
+
+ SwBoxAutoFormat** pFormat = &m_aBoxAutoFormat[ nPos ];
+ if( !*pFormat )
+ {
+ // If default doesn't exist yet:
+ if( !s_pDefaultBoxAutoFormat )
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat();
+ *pFormat = new SwBoxAutoFormat(*s_pDefaultBoxAutoFormat);
+ }
+ return **pFormat;
+}
+
+const SwBoxAutoFormat& SwTableAutoFormat::GetDefaultBoxFormat()
+{
+ if(!s_pDefaultBoxAutoFormat)
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat();
+
+ return *s_pDefaultBoxAutoFormat;
+}
+
+void SwTableAutoFormat::UpdateFromSet( sal_uInt8 nPos,
+ const SfxItemSet& rSet,
+ SwTableAutoFormatUpdateFlags eFlags,
+ SvNumberFormatter const * pNFormatr)
+{
+ OSL_ENSURE( nPos < 16, "wrong area" );
+
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ nPos ];
+ if( !pFormat ) // if is set -> copy
+ {
+ pFormat = new SwBoxAutoFormat;
+ m_aBoxAutoFormat[ nPos ] = pFormat;
+ }
+
+ if( SwTableAutoFormatUpdateFlags::Char & eFlags )
+ {
+ pFormat->SetFont( rSet.Get( RES_CHRATR_FONT ) );
+ pFormat->SetHeight( rSet.Get( RES_CHRATR_FONTSIZE ) );
+ pFormat->SetWeight( rSet.Get( RES_CHRATR_WEIGHT ) );
+ pFormat->SetPosture( rSet.Get( RES_CHRATR_POSTURE ) );
+ pFormat->SetCJKFont( rSet.Get( RES_CHRATR_CJK_FONT ) );
+ pFormat->SetCJKHeight( rSet.Get( RES_CHRATR_CJK_FONTSIZE ) );
+ pFormat->SetCJKWeight( rSet.Get( RES_CHRATR_CJK_WEIGHT ) );
+ pFormat->SetCJKPosture( rSet.Get( RES_CHRATR_CJK_POSTURE ) );
+ pFormat->SetCTLFont( rSet.Get( RES_CHRATR_CTL_FONT ) );
+ pFormat->SetCTLHeight( rSet.Get( RES_CHRATR_CTL_FONTSIZE ) );
+ pFormat->SetCTLWeight( rSet.Get( RES_CHRATR_CTL_WEIGHT ) );
+ pFormat->SetCTLPosture( rSet.Get( RES_CHRATR_CTL_POSTURE ) );
+ pFormat->SetUnderline( rSet.Get( RES_CHRATR_UNDERLINE ) );
+ pFormat->SetOverline( rSet.Get( RES_CHRATR_OVERLINE ) );
+ pFormat->SetCrossedOut( rSet.Get( RES_CHRATR_CROSSEDOUT ) );
+ pFormat->SetContour( rSet.Get( RES_CHRATR_CONTOUR ) );
+ pFormat->SetShadowed( rSet.Get( RES_CHRATR_SHADOWED ) );
+ pFormat->SetColor( rSet.Get( RES_CHRATR_COLOR ) );
+ pFormat->SetAdjust( rSet.Get( RES_PARATR_ADJUST ) );
+ }
+ if( !(SwTableAutoFormatUpdateFlags::Box & eFlags) )
+ return;
+
+ pFormat->SetBox( rSet.Get( RES_BOX ) );
+// FIXME - add attribute IDs for the diagonal line items
+// pFormat->SetTLBR( (SvxLineItem&)rSet.Get( RES_... ) );
+// pFormat->SetBLTR( (SvxLineItem&)rSet.Get( RES_... ) );
+ pFormat->SetBackground( rSet.Get( RES_BACKGROUND ) );
+ pFormat->SetTextOrientation(rSet.Get(RES_FRAMEDIR));
+ pFormat->SetVerticalAlignment(rSet.Get(RES_VERT_ORIENT));
+
+ const SwTableBoxNumFormat* pNumFormatItem;
+ const SvNumberformat* pNumFormat = nullptr;
+ if( pNFormatr && (pNumFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT )) &&
+ nullptr != (pNumFormat = pNFormatr->GetEntry( pNumFormatItem->GetValue() )) )
+ pFormat->SetValueFormat( pNumFormat->GetFormatstring(),
+ pNumFormat->GetLanguage(),
+ ::GetAppLanguage());
+ else
+ {
+ // default
+ pFormat->SetValueFormat( OUString(), LANGUAGE_SYSTEM,
+ ::GetAppLanguage() );
+ }
+
+ // we cannot handle the rest, that's specific to StarCalc
+}
+
+void SwTableAutoFormat::UpdateToSet(const sal_uInt8 nPos, const bool bSingleRowTable, const bool bSingleColTable, SfxItemSet& rSet,
+ SwTableAutoFormatUpdateFlags eFlags, SvNumberFormatter* pNFormatr) const
+{
+ const SwBoxAutoFormat& rChg = GetBoxFormat( nPos );
+
+ if( SwTableAutoFormatUpdateFlags::Char & eFlags )
+ {
+ if( IsFont() )
+ {
+ rSet.Put( rChg.GetFont() );
+ rSet.Put( rChg.GetHeight() );
+ rSet.Put( rChg.GetWeight() );
+ rSet.Put( rChg.GetPosture() );
+ // do not insert empty CJK font
+ const SvxFontItem& rCJKFont = rChg.GetCJKFont();
+ if (!rCJKFont.GetStyleName().isEmpty())
+ {
+ rSet.Put( rChg.GetCJKFont() );
+ rSet.Put( rChg.GetCJKHeight() );
+ rSet.Put( rChg.GetCJKWeight() );
+ rSet.Put( rChg.GetCJKPosture() );
+ }
+ else
+ {
+ rSet.Put( rChg.GetHeight().CloneSetWhich(RES_CHRATR_CJK_FONTSIZE) );
+ rSet.Put( rChg.GetWeight().CloneSetWhich(RES_CHRATR_CJK_WEIGHT) );
+ rSet.Put( rChg.GetPosture().CloneSetWhich(RES_CHRATR_CJK_POSTURE) );
+ }
+ // do not insert empty CTL font
+ const SvxFontItem& rCTLFont = rChg.GetCTLFont();
+ if (!rCTLFont.GetStyleName().isEmpty())
+ {
+ rSet.Put( rChg.GetCTLFont() );
+ rSet.Put( rChg.GetCTLHeight() );
+ rSet.Put( rChg.GetCTLWeight() );
+ rSet.Put( rChg.GetCTLPosture() );
+ }
+ else
+ {
+ rSet.Put( rChg.GetHeight().CloneSetWhich(RES_CHRATR_CTL_FONTSIZE) );
+ rSet.Put( rChg.GetWeight().CloneSetWhich(RES_CHRATR_CTL_WEIGHT) );
+ rSet.Put( rChg.GetPosture().CloneSetWhich(RES_CHRATR_CTL_POSTURE) );
+ }
+ rSet.Put( rChg.GetUnderline() );
+ rSet.Put( rChg.GetOverline() );
+ rSet.Put( rChg.GetCrossedOut() );
+ rSet.Put( rChg.GetContour() );
+ rSet.Put( rChg.GetShadowed() );
+ rSet.Put( rChg.GetColor() );
+ }
+ if( IsJustify() )
+ rSet.Put( rChg.GetAdjust() );
+ }
+
+ if( !(SwTableAutoFormatUpdateFlags::Box & eFlags) )
+ return;
+
+ if( IsFrame() )
+ {
+ SvxBoxItem aAutoFormatBox = rChg.GetBox();
+
+ // No format box is adequate to specify the borders of single column/row tables, so combine first/last.
+ if ( bSingleRowTable || bSingleColTable )
+ {
+ sal_uInt8 nSingleRowOrColumnId = 15; //LAST_ROW_END_COLUMN
+ if ( !bSingleRowTable )
+ nSingleRowOrColumnId = nPos + 3; //LAST COLUMN (3, 7, 11, 15)
+ else if ( !bSingleColTable )
+ nSingleRowOrColumnId = nPos + 12; //LAST ROW (12, 13, 14, 15)
+
+ assert( nSingleRowOrColumnId < 16 );
+ const SvxBoxItem aLastAutoFormatBox( GetBoxFormat(nSingleRowOrColumnId).GetBox() );
+ if ( bSingleRowTable )
+ aAutoFormatBox.SetLine( aLastAutoFormatBox.GetLine(SvxBoxItemLine::BOTTOM), SvxBoxItemLine::BOTTOM );
+ if ( bSingleColTable )
+ aAutoFormatBox.SetLine( aLastAutoFormatBox.GetLine(SvxBoxItemLine::RIGHT), SvxBoxItemLine::RIGHT );
+ }
+
+ rSet.Put( aAutoFormatBox );
+// FIXME - uncomment the lines to put the diagonal line items
+// rSet.Put( rChg.GetTLBR() );
+// rSet.Put( rChg.GetBLTR() );
+ }
+ if( IsBackground() )
+ rSet.Put( rChg.GetBackground() );
+
+ rSet.Put(rChg.GetTextOrientation());
+
+ // Do not put a VertAlign when it has default value.
+ // It prevents the export of default value by automatic cell-styles export.
+ if (rChg.GetVerticalAlignment().GetVertOrient() != GetDefaultBoxFormat().GetVerticalAlignment().GetVertOrient())
+ rSet.Put(rChg.GetVerticalAlignment());
+
+ if( !(IsValueFormat() && pNFormatr) )
+ return;
+
+ OUString sFormat;
+ LanguageType eLng, eSys;
+ rChg.GetValueFormat( sFormat, eLng, eSys );
+ if( !sFormat.isEmpty() )
+ {
+ SvNumFormatType nType;
+ bool bNew;
+ sal_Int32 nCheckPos;
+ sal_uInt32 nKey = pNFormatr->GetIndexPuttingAndConverting( sFormat, eLng,
+ eSys, nType, bNew, nCheckPos);
+ rSet.Put( SwTableBoxNumFormat( nKey ));
+ }
+ else
+ rSet.ClearItem( RES_BOXATR_FORMAT );
+
+ // we cannot handle the rest, that's specific to StarCalc
+}
+
+void SwTableAutoFormat::RestoreTableProperties(SwTable &table) const
+{
+ SwTableFormat* pFormat = table.GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ SwDoc *pDoc = pFormat->GetDoc();
+ if (!pDoc)
+ return;
+
+ SfxItemSet rSet(pDoc->GetAttrPool(), aTableSetRange);
+
+ rSet.Put(SwFormatLayoutSplit(m_bLayoutSplit));
+ rSet.Put(SfxBoolItem(RES_COLLAPSING_BORDERS, m_bCollapsingBorders));
+ if ( m_aKeepWithNextPara->GetValue() )
+ rSet.Put(*m_aKeepWithNextPara);
+ rSet.Put(*m_aShadow);
+
+ pFormat->SetFormatAttr(rSet);
+
+ if (SwEditShell *pShell = pDoc->GetEditShell())
+ pDoc->SetRowSplit(*pShell->getShellCursor(false), SwFormatRowSplit(m_bRowSplit));
+
+ table.SetRowsToRepeat(m_aRepeatHeading);
+}
+
+void SwTableAutoFormat::StoreTableProperties(const SwTable &table)
+{
+ SwTableFormat* pFormat = table.GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ SwDoc *pDoc = pFormat->GetDoc();
+ if (!pDoc)
+ return;
+
+ SwEditShell *pShell = pDoc->GetEditShell();
+ std::unique_ptr<SwFormatRowSplit> pRowSplit(pShell ? SwDoc::GetRowSplit(*pShell->getShellCursor(false)) : nullptr);
+ m_bRowSplit = pRowSplit && pRowSplit->GetValue();
+ pRowSplit.reset();
+
+ const SfxItemSet &rSet = pFormat->GetAttrSet();
+
+ const SwFormatLayoutSplit &layoutSplit = rSet.Get(RES_LAYOUT_SPLIT);
+ m_bLayoutSplit = layoutSplit.GetValue();
+ m_bCollapsingBorders = rSet.Get(RES_COLLAPSING_BORDERS).GetValue();
+
+ m_aKeepWithNextPara.reset(rSet.Get(RES_KEEP).Clone());
+ m_aRepeatHeading = table.GetRowsToRepeat();
+ m_aShadow.reset(rSet.Get(RES_SHADOW).Clone());
+}
+
+bool SwTableAutoFormat::FirstRowEndColumnIsRow()
+{
+ return GetBoxFormat(3) == GetBoxFormat(2);
+}
+bool SwTableAutoFormat::FirstRowStartColumnIsRow()
+{
+ return GetBoxFormat(0) == GetBoxFormat(1);
+}
+bool SwTableAutoFormat::LastRowEndColumnIsRow()
+{
+ return GetBoxFormat(14) == GetBoxFormat(15);
+}
+bool SwTableAutoFormat::LastRowStartColumnIsRow()
+{
+ return GetBoxFormat(12) == GetBoxFormat(13);
+}
+
+bool SwTableAutoFormat::Load( SvStream& rStream, const SwAfVersions& rVersions )
+{
+ sal_uInt16 nVal = 0;
+ rStream.ReadUInt16( nVal );
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+
+ if( bRet && (nVal == AUTOFORMAT_DATA_ID_X ||
+ (AUTOFORMAT_DATA_ID_504 <= nVal && nVal <= AUTOFORMAT_DATA_ID)) )
+ {
+ bool b;
+ // --- from 680/dr25 on: store strings as UTF-8
+ rtl_TextEncoding eCharSet = (nVal >= AUTOFORMAT_ID_680DR25) ? RTL_TEXTENCODING_UTF8 : rStream.GetStreamCharSet();
+ m_aName = rStream.ReadUniOrByteString( eCharSet );
+ if( AUTOFORMAT_DATA_ID_552 <= nVal )
+ {
+ rStream.ReadUInt16( m_nStrResId );
+ // start from 3d because default is added via constructor
+ if( m_nStrResId < RES_POOLTABLESTYLE_END - RES_POOLTABLESTYLE_3D )
+ {
+ m_aName = SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_3D + m_nStrResId, m_aName);
+ }
+ else
+ m_nStrResId = USHRT_MAX;
+ }
+ rStream.ReadCharAsBool( b ); m_bInclFont = b;
+ rStream.ReadCharAsBool( b ); m_bInclJustify = b;
+ rStream.ReadCharAsBool( b ); m_bInclFrame = b;
+ rStream.ReadCharAsBool( b ); m_bInclBackground = b;
+ rStream.ReadCharAsBool( b ); m_bInclValueFormat = b;
+ rStream.ReadCharAsBool( b ); m_bInclWidthHeight = b;
+
+ if (nVal >= AUTOFORMAT_DATA_ID_31005 && WriterSpecificBlockExists(rStream))
+ {
+ //this only exists for file format compat
+ SvxFormatBreakItem aBreak(SvxBreak::NONE, RES_BREAK);
+ legacy::SvxFormatBreak::Create(aBreak, rStream, AUTOFORMAT_FILE_VERSION);
+ legacy::SvxFormatKeep::Create(*m_aKeepWithNextPara, rStream, AUTOFORMAT_FILE_VERSION);
+
+ rStream.ReadUInt16( m_aRepeatHeading ).ReadCharAsBool( m_bLayoutSplit ).ReadCharAsBool( m_bRowSplit ).ReadCharAsBool( m_bCollapsingBorders );
+
+ legacy::SvxShadow::Create(*m_aShadow, rStream, AUTOFORMAT_FILE_VERSION);
+ }
+
+ bRet = ERRCODE_NONE== rStream.GetError();
+
+ for( sal_uInt8 i = 0; bRet && i < 16; ++i )
+ {
+ SwBoxAutoFormat* pFormat = new SwBoxAutoFormat;
+ bRet = pFormat->Load( rStream, rVersions, nVal );
+ if( bRet )
+ m_aBoxAutoFormat[ i ] = pFormat;
+ else
+ {
+ delete pFormat;
+ break;
+ }
+ }
+ }
+ m_bUserDefined = false;
+ return bRet;
+}
+
+bool SwTableAutoFormat::Save( SvStream& rStream, sal_uInt16 fileVersion ) const
+{
+ rStream.WriteUInt16( AUTOFORMAT_DATA_ID );
+ // --- from 680/dr25 on: store strings as UTF-8
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rStream, m_aName,
+ RTL_TEXTENCODING_UTF8 );
+ rStream.WriteUInt16( m_nStrResId );
+ rStream.WriteBool( m_bInclFont );
+ rStream.WriteBool( m_bInclJustify );
+ rStream.WriteBool( m_bInclFrame );
+ rStream.WriteBool( m_bInclBackground );
+ rStream.WriteBool( m_bInclValueFormat );
+ rStream.WriteBool( m_bInclWidthHeight );
+
+ {
+ WriterSpecificAutoFormatBlock block(rStream);
+ //this only exists for file format compat
+ SvxFormatBreakItem aBreak(SvxBreak::NONE, RES_BREAK);
+ legacy::SvxFormatBreak::Store(aBreak, rStream, legacy::SvxFormatBreak::GetVersion(fileVersion));
+ legacy::SvxFormatKeep::Store(*m_aKeepWithNextPara, rStream, legacy::SvxFormatKeep::GetVersion(fileVersion));
+ rStream.WriteUInt16( m_aRepeatHeading ).WriteBool( m_bLayoutSplit ).WriteBool( m_bRowSplit ).WriteBool( m_bCollapsingBorders );
+ legacy::SvxShadow::Store(*m_aShadow, rStream, legacy::SvxShadow::GetVersion(fileVersion));
+ }
+
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+
+ for( int i = 0; bRet && i < 16; ++i )
+ {
+ SwBoxAutoFormat* pFormat = m_aBoxAutoFormat[ i ];
+ if( !pFormat ) // if not set -> write default
+ {
+ // If it doesn't exist yet:
+ if( !s_pDefaultBoxAutoFormat )
+ s_pDefaultBoxAutoFormat = new SwBoxAutoFormat;
+ pFormat = s_pDefaultBoxAutoFormat;
+ }
+ bRet = pFormat->Save( rStream, fileVersion );
+ }
+ return bRet;
+}
+
+OUString SwTableAutoFormat::GetTableTemplateCellSubName(const SwBoxAutoFormat& rBoxFormat) const
+{
+ sal_Int32 nIndex = 0;
+ for (; nIndex < 16; ++nIndex)
+ if (m_aBoxAutoFormat[nIndex] == &rBoxFormat) break;
+
+ // box format doesn't belong to this table format
+ if (16 <= nIndex)
+ return OUString();
+
+ const std::vector<sal_Int32> aTableTemplateMap = GetTableTemplateMap();
+ for (size_t i=0; i < aTableTemplateMap.size(); ++i)
+ {
+ if (aTableTemplateMap[i] == nIndex)
+ return "." + OUString::number(i + 1);
+ }
+
+ // box format doesn't belong to a table template
+ return OUString();
+}
+
+/*
+ * Mapping schema
+ * 0 1 2 3 4 5
+ * +-----------------------------------------------------------------------+
+ * 0 | FRSC | FR | FREC | | | FRENC |
+ * +-----------------------------------------------------------------------+
+ * 1 | FC | ER | EC | | | LC |
+ * +-----------------------------------------------------------------------+
+ * 2 | OR | OC | BODY | | | BCKG |
+ * +-----------------------------------------------------------------------+
+ * 3 | | | | | | |
+ * +-----------------------------------------------------------------------+
+ * 4 | | | | | | |
+ * +-----------------------------------------------------------------------+
+ * 5 | LRSC | LR | LREC | | | LRENC |
+ * +-----------+-----------+-----------+-----------+-----------+-----------+
+ * ODD = 1, 3, 5, ...
+ * EVEN = 2, 4, 6, ...
+ */
+const std::vector<sal_Int32> & SwTableAutoFormat::GetTableTemplateMap()
+{
+ static std::vector<sal_Int32> const aTableTemplateMap
+ {
+ 1 , // FIRST_ROW // FR
+ 13, // LAST_ROW // LR
+ 4 , // FIRST_COLUMN // FC
+ 7 , // LAST_COLUMN // LC
+ 5 , // EVEN_ROWS // ER
+ 8 , // ODD_ROWS // OR
+ 6 , // EVEN_COLUMNS // EC
+ 9 , // ODD_COLUMNS // OC
+ 10, // BODY
+ 11, // BACKGROUND // BCKG
+ 0 , // FIRST_ROW_START_COLUMN // FRSC
+ 3 , // FIRST_ROW_END_COLUMN // FRENC
+ 12, // LAST_ROW_START_COLUMN // LRSC
+ 15, // LAST_ROW_END_COLUMN // LRENC
+ 2 , // FIRST_ROW_EVEN_COLUMN // FREC
+ 14, // LAST_ROW_EVEN_COLUMN // LREC
+ };
+ return aTableTemplateMap;
+}
+
+sal_uInt8 SwTableAutoFormat::CountPos(sal_uInt32 nCol, sal_uInt32 nCols, sal_uInt32 nRow,
+ sal_uInt32 nRows)
+{
+ sal_uInt8 nRet = static_cast<sal_uInt8>(
+ !nRow ? 0 : ((nRow + 1 == nRows) ? 12 : (4 * (1 + ((nRow - 1) & 1)))));
+ nRet = nRet
+ + static_cast<sal_uInt8>(!nCol ? 0 : (nCol + 1 == nCols ? 3 : (1 + ((nCol - 1) & 1))));
+ return nRet;
+}
+
+struct SwTableAutoFormatTable::Impl
+{
+ std::vector<std::unique_ptr<SwTableAutoFormat>> m_AutoFormats;
+};
+
+size_t SwTableAutoFormatTable::size() const
+{
+ return m_pImpl->m_AutoFormats.size();
+}
+
+SwTableAutoFormat const& SwTableAutoFormatTable::operator[](size_t const i) const
+{
+ return *m_pImpl->m_AutoFormats[i];
+}
+SwTableAutoFormat & SwTableAutoFormatTable::operator[](size_t const i)
+{
+ return *m_pImpl->m_AutoFormats[i];
+}
+
+void SwTableAutoFormatTable::AddAutoFormat(const SwTableAutoFormat& rTableStyle)
+{
+ // don't insert when we already have style of this name
+ if (FindAutoFormat(rTableStyle.GetName()))
+ return;
+
+ InsertAutoFormat(size(), std::make_unique<SwTableAutoFormat>(rTableStyle));
+}
+
+void SwTableAutoFormatTable::InsertAutoFormat(size_t const i, std::unique_ptr<SwTableAutoFormat> pFormat)
+{
+ m_pImpl->m_AutoFormats.insert(m_pImpl->m_AutoFormats.begin() + i, std::move(pFormat));
+}
+
+void SwTableAutoFormatTable::EraseAutoFormat(size_t const i)
+{
+ m_pImpl->m_AutoFormats.erase(m_pImpl->m_AutoFormats.begin() + i);
+}
+
+void SwTableAutoFormatTable::EraseAutoFormat(const OUString& rName)
+{
+ auto iter = std::find_if(m_pImpl->m_AutoFormats.begin(), m_pImpl->m_AutoFormats.end(),
+ [&rName](const std::unique_ptr<SwTableAutoFormat>& rpFormat) { return rpFormat->GetName() == rName; });
+ if (iter != m_pImpl->m_AutoFormats.end())
+ {
+ m_pImpl->m_AutoFormats.erase(iter);
+ return;
+ }
+ SAL_INFO("sw.core", "SwTableAutoFormatTable::EraseAutoFormat, SwTableAutoFormat with given name not found");
+}
+
+std::unique_ptr<SwTableAutoFormat> SwTableAutoFormatTable::ReleaseAutoFormat(size_t const i)
+{
+ auto const iter(m_pImpl->m_AutoFormats.begin() + i);
+ std::unique_ptr<SwTableAutoFormat> pRet(std::move(*iter));
+ m_pImpl->m_AutoFormats.erase(iter);
+ return pRet;
+}
+
+std::unique_ptr<SwTableAutoFormat> SwTableAutoFormatTable::ReleaseAutoFormat(const OUString& rName)
+{
+ std::unique_ptr<SwTableAutoFormat> pRet;
+ auto iter = std::find_if(m_pImpl->m_AutoFormats.begin(), m_pImpl->m_AutoFormats.end(),
+ [&rName](const std::unique_ptr<SwTableAutoFormat>& rpFormat) { return rpFormat->GetName() == rName; });
+ if (iter != m_pImpl->m_AutoFormats.end())
+ {
+ pRet = std::move(*iter);
+ m_pImpl->m_AutoFormats.erase(iter);
+ }
+ return pRet;
+}
+
+SwTableAutoFormat* SwTableAutoFormatTable::FindAutoFormat(std::u16string_view rName) const
+{
+ for (const auto &rFormat : m_pImpl->m_AutoFormats)
+ {
+ if (rFormat->GetName() == rName)
+ return rFormat.get();
+ }
+
+ return nullptr;
+}
+
+SwTableAutoFormatTable::~SwTableAutoFormatTable()
+{
+}
+
+SwTableAutoFormatTable::SwTableAutoFormatTable()
+ : m_pImpl(new Impl)
+{
+ std::unique_ptr<SwTableAutoFormat> pNew(new SwTableAutoFormat(
+ SwStyleNameMapper::GetUIName(RES_POOLTABLESTYLE_DEFAULT, OUString())));
+
+ sal_uInt8 i;
+
+ Color aColor( COL_BLACK );
+ SvxBoxItem aBox( RES_BOX );
+
+ aBox.SetAllDistances(55);
+ SvxBorderLine aLn( &aColor, SvxBorderLineWidth::VeryThin );
+ aBox.SetLine( &aLn, SvxBoxItemLine::LEFT );
+ aBox.SetLine( &aLn, SvxBoxItemLine::BOTTOM );
+
+ for( i = 0; i <= 15; ++i )
+ {
+ aBox.SetLine( i <= 3 ? &aLn : nullptr, SvxBoxItemLine::TOP );
+ aBox.SetLine( (3 == ( i & 3 )) ? &aLn : nullptr, SvxBoxItemLine::RIGHT );
+ pNew->GetBoxFormat( i ).SetBox( aBox );
+ }
+
+ pNew->SetUserDefined(false);
+ m_pImpl->m_AutoFormats.push_back(std::move(pNew));
+}
+
+void SwTableAutoFormatTable::Load()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ OUString sNm(AUTOTABLE_FORMAT_NAME);
+ SvtPathOptions aOpt;
+ if( aOpt.SearchFile( sNm ))
+ {
+ SfxMedium aStream( sNm, StreamMode::STD_READ );
+ Load( *aStream.GetInStream() );
+ }
+}
+
+bool SwTableAutoFormatTable::Save() const
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ SvtPathOptions aPathOpt;
+ const OUString sNm( aPathOpt.GetUserConfigPath() + "/" + AUTOTABLE_FORMAT_NAME );
+ SfxMedium aStream(sNm, StreamMode::STD_WRITE );
+ return Save( *aStream.GetOutStream() ) && aStream.Commit();
+}
+
+bool SwTableAutoFormatTable::Load( SvStream& rStream )
+{
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+ if (bRet)
+ {
+ // Attention: We need to read a general Header here
+ sal_uInt16 nVal = 0;
+ rStream.ReadUInt16( nVal );
+ bRet = ERRCODE_NONE == rStream.GetError();
+
+ if( bRet )
+ {
+ SwAfVersions aVersions;
+
+ // Default version is 5.0, unless we detect an old format ID.
+ sal_uInt16 nFileVers = SOFFICE_FILEFORMAT_50;
+ if(nVal < AUTOFORMAT_ID_31005)
+ nFileVers = SOFFICE_FILEFORMAT_40;
+
+ if( nVal == AUTOFORMAT_ID_358 ||
+ (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) )
+ {
+ sal_uInt8 nChrSet, nCnt;
+ sal_uInt64 nPos = rStream.Tell();
+ rStream.ReadUChar( nCnt ).ReadUChar( nChrSet );
+ if( rStream.Tell() != nPos + nCnt )
+ {
+ OSL_ENSURE( false, "The Header contains more or newer Data" );
+ rStream.Seek( nPos + nCnt );
+ }
+ rStream.SetStreamCharSet( static_cast<rtl_TextEncoding>(nChrSet) );
+ rStream.SetVersion( nFileVers );
+ }
+
+ if( nVal == AUTOFORMAT_ID_358 || nVal == AUTOFORMAT_ID_X ||
+ (AUTOFORMAT_ID_504 <= nVal && nVal <= AUTOFORMAT_ID) )
+ {
+ aVersions.Load( rStream, nVal ); // Item versions
+
+ sal_uInt16 nCount = 0;
+ rStream.ReadUInt16( nCount );
+
+ bRet = ERRCODE_NONE== rStream.GetError();
+ if (bRet)
+ {
+ const size_t nMinRecordSize = sizeof(sal_uInt16);
+ const size_t nMaxRecords = rStream.remainingSize() / nMinRecordSize;
+ if (nCount > nMaxRecords)
+ {
+ SAL_WARN("sw.core", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nCount << " claimed, truncating");
+ nCount = nMaxRecords;
+ }
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ std::unique_ptr<SwTableAutoFormat> pNew(
+ new SwTableAutoFormat( OUString() ));
+ bRet = pNew->Load( rStream, aVersions );
+ if( bRet )
+ {
+ m_pImpl->m_AutoFormats.push_back(std::move(pNew));
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwTableAutoFormatTable::Save( SvStream& rStream ) const
+{
+ bool bRet = ERRCODE_NONE == rStream.GetError();
+ if (bRet)
+ {
+ rStream.SetVersion(AUTOFORMAT_FILE_VERSION);
+
+ // Attention: We need to save a general Header here
+ rStream.WriteUInt16( AUTOFORMAT_ID )
+ .WriteUChar( 2 ) // Character count of the Header including this value
+ .WriteUChar( GetStoreCharSet( ::osl_getThreadTextEncoding() ) );
+
+ bRet = ERRCODE_NONE == rStream.GetError();
+ if (!bRet)
+ return false;
+
+ // Write this version number for all attributes
+ SwAfVersions::Write(rStream, AUTOFORMAT_FILE_VERSION);
+
+ rStream.WriteUInt16( m_pImpl->m_AutoFormats.size() - 1 );
+ bRet = ERRCODE_NONE == rStream.GetError();
+
+ for (size_t i = 1; bRet && i < m_pImpl->m_AutoFormats.size(); ++i)
+ {
+ SwTableAutoFormat const& rFormat = *m_pImpl->m_AutoFormats[i];
+ bRet = rFormat.Save(rStream, AUTOFORMAT_FILE_VERSION);
+ }
+ }
+ rStream.FlushBuffer();
+ return bRet;
+}
+
+SwCellStyleTable::SwCellStyleTable()
+{ }
+
+SwCellStyleTable::~SwCellStyleTable()
+{
+}
+
+size_t SwCellStyleTable::size() const
+{
+ return m_aCellStyles.size();
+}
+
+void SwCellStyleTable::clear()
+{
+ m_aCellStyles.clear();
+}
+
+SwCellStyleDescriptor SwCellStyleTable::operator[](size_t i) const
+{
+ return SwCellStyleDescriptor(m_aCellStyles[i]);
+}
+
+void SwCellStyleTable::AddBoxFormat(const SwBoxAutoFormat& rBoxFormat, const OUString& sName)
+{
+ m_aCellStyles.emplace_back(sName, std::make_unique<SwBoxAutoFormat>(rBoxFormat));
+}
+
+void SwCellStyleTable::RemoveBoxFormat(const OUString& sName)
+{
+ auto iter = std::find_if(m_aCellStyles.begin(), m_aCellStyles.end(),
+ [&sName](const std::pair<OUString, std::unique_ptr<SwBoxAutoFormat>>& rStyle) { return rStyle.first == sName; });
+ if (iter != m_aCellStyles.end())
+ {
+ m_aCellStyles.erase(iter);
+ return;
+ }
+ SAL_INFO("sw.core", "SwCellStyleTable::RemoveBoxFormat, format with given name doesn't exists");
+}
+
+OUString SwCellStyleTable::GetBoxFormatName(const SwBoxAutoFormat& rBoxFormat) const
+{
+ for (size_t i=0; i < m_aCellStyles.size(); ++i)
+ {
+ if (m_aCellStyles[i].second.get() == &rBoxFormat)
+ return m_aCellStyles[i].first;
+ }
+
+ // box format not found
+ return OUString();
+}
+
+SwBoxAutoFormat* SwCellStyleTable::GetBoxFormat(std::u16string_view sName) const
+{
+ for (size_t i=0; i < m_aCellStyles.size(); ++i)
+ {
+ if (m_aCellStyles[i].first == sName)
+ return m_aCellStyles[i].second.get();
+ }
+
+ return nullptr;
+}
+
+void SwCellStyleTable::ChangeBoxFormatName(std::u16string_view sFromName, const OUString& sToName)
+{
+ if (!GetBoxFormat(sToName))
+ {
+ SAL_INFO("sw.core", "SwCellStyleTable::ChangeBoxName, box with given name already exists");
+ return;
+ }
+ for (size_t i=0; i < m_aCellStyles.size(); ++i)
+ {
+ if (m_aCellStyles[i].first == sFromName)
+ {
+ m_aCellStyles[i].first = sToName;
+ // changed successfully
+ return;
+ }
+ }
+ SAL_INFO("sw.core", "SwCellStyleTable::ChangeBoxName, box with given name not found");
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/tblcpy.cxx b/sw/source/core/doc/tblcpy.cxx
new file mode 100644
index 000000000..135d558bb
--- /dev/null
+++ b/sw/source/core/doc/tblcpy.cxx
@@ -0,0 +1,1042 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <tblsel.hxx>
+#include <poolfmt.hxx>
+#include <cellatr.hxx>
+#include <mvsave.hxx>
+#include <fmtanchr.hxx>
+#include <hints.hxx>
+#include <UndoTable.hxx>
+#include <fmtfsize.hxx>
+#include <frameformats.hxx>
+#include <deque>
+#include <memory>
+#include <numeric>
+
+static void lcl_CpyBox( const SwTable& rCpyTable, const SwTableBox* pCpyBox,
+ SwTable& rDstTable, SwTableBox* pDstBox,
+ bool bDelContent, SwUndoTableCpyTable* pUndo );
+
+// The following type will be used by table copy functions to describe
+// the structure of tables (or parts of tables).
+// It's for new table model only.
+
+namespace
+{
+ struct BoxSpanInfo
+ {
+ SwTableBox* mpBox;
+ SwTableBox* mpCopy;
+ sal_uInt16 mnColSpan;
+ bool mbSelected;
+ };
+
+ typedef std::vector< BoxSpanInfo > BoxStructure;
+ typedef std::vector< BoxStructure > LineStructure;
+ typedef std::deque< sal_uLong > ColumnStructure;
+
+ struct SubBox
+ {
+ SwTableBox *mpBox;
+ bool mbCovered;
+ };
+
+ typedef std::vector< SubBox > SubLine;
+ typedef std::vector< SubLine > SubTable;
+
+ class TableStructure
+ {
+ public:
+ LineStructure maLines;
+ ColumnStructure maCols;
+ sal_uInt16 mnStartCol;
+ sal_uInt16 mnAddLine;
+ void addLine( sal_uInt16 &rLine, const SwTableBoxes&, const SwSelBoxes*,
+ bool bNewModel );
+ void addBox( sal_uInt16 nLine, const SwSelBoxes*, SwTableBox *pBox,
+ sal_uLong &rnB, sal_uInt16 &rnC, ColumnStructure::iterator& rpCl,
+ BoxStructure::iterator& rpSel, bool &rbSel, bool bCover );
+ void incColSpan( sal_uInt16 nLine, sal_uInt16 nCol );
+ explicit TableStructure( const SwTable& rTable );
+ TableStructure( const SwTable& rTable, FndBox_ &rFndBox,
+ const SwSelBoxes& rSelBoxes,
+ LineStructure::size_type nMinSize );
+ LineStructure::size_type getLineCount() const
+ { return maLines.size(); }
+ void moreLines( const SwTable& rTable );
+ void assignBoxes( const TableStructure &rSource );
+ void copyBoxes( const SwTable& rSource, SwTable& rDstTable,
+ SwUndoTableCpyTable* pUndo ) const;
+ };
+
+ SubTable::iterator insertSubLine( SubTable& rSubTable, SwTableLine& rLine,
+ const SubTable::iterator& pStartLn );
+
+ SubTable::iterator insertSubBox( SubTable& rSubTable, SwTableBox& rBox,
+ SubTable::iterator pStartLn, const SubTable::iterator& pEndLn )
+ {
+ if( !rBox.GetTabLines().empty() )
+ {
+ SubTable::size_type nSize = static_cast<SubTable::size_type>(std::distance( pStartLn, pEndLn ));
+ if( nSize < rBox.GetTabLines().size() )
+ {
+ SubLine aSubLine;
+ for( const auto& rSubBox : *pStartLn )
+ {
+ SubBox aSub;
+ aSub.mpBox = rSubBox.mpBox;
+ aSub.mbCovered = true;
+ aSubLine.push_back( aSub );
+ }
+ do
+ {
+ rSubTable.insert( pEndLn, aSubLine );
+ } while( ++nSize < rBox.GetTabLines().size() );
+ }
+ for( auto pLine : rBox.GetTabLines() )
+ pStartLn = insertSubLine( rSubTable, *pLine, pStartLn );
+ OSL_ENSURE( pStartLn == pEndLn, "Sub line confusion" );
+ }
+ else
+ {
+ SubBox aSub;
+ aSub.mpBox = &rBox;
+ aSub.mbCovered = false;
+ while( pStartLn != pEndLn )
+ {
+ pStartLn->push_back( aSub );
+ aSub.mbCovered = true;
+ ++pStartLn;
+ }
+ }
+ return pStartLn;
+ }
+
+ SubTable::iterator insertSubLine( SubTable& rSubTable, SwTableLine& rLine,
+ const SubTable::iterator& pStartLn )
+ {
+ SubTable::iterator pMax = pStartLn;
+ ++pMax;
+ SubTable::difference_type nMax = 1;
+ for( auto pBox : rLine.GetTabBoxes() )
+ {
+ SubTable::iterator pTmp = insertSubBox( rSubTable, *pBox, pStartLn, pMax );
+ SubTable::difference_type nTmp = std::distance( pStartLn, pTmp );
+ if( nTmp > nMax )
+ {
+ pMax = pTmp;
+ nMax = nTmp;
+ }
+ }
+ return pMax;
+ }
+
+ TableStructure::TableStructure( const SwTable& rTable ) :
+ maLines( rTable.GetTabLines().size() ), mnStartCol(USHRT_MAX),
+ mnAddLine(0)
+ {
+ maCols.push_front(0);
+ sal_uInt16 nCnt = 0;
+ for( auto pLine : rTable.GetTabLines() )
+ addLine( nCnt, pLine->GetTabBoxes(), nullptr, rTable.IsNewModel() );
+ }
+
+ TableStructure::TableStructure( const SwTable& rTable,
+ FndBox_ &rFndBox, const SwSelBoxes& rSelBoxes,
+ LineStructure::size_type nMinSize )
+ : mnStartCol(USHRT_MAX), mnAddLine(0)
+ {
+ if( rFndBox.GetLines().empty() )
+ return;
+
+ bool bNoSelection = rSelBoxes.size() < 2;
+ FndLines_t &rFndLines = rFndBox.GetLines();
+ maCols.push_front(0);
+ const SwTableLine* pLine = rFndLines.front()->GetLine();
+ const sal_uInt16 nStartLn = rTable.GetTabLines().GetPos( pLine );
+ SwTableLines::size_type nEndLn = nStartLn;
+ if( rFndLines.size() > 1 )
+ {
+ pLine = rFndLines.back()->GetLine();
+ nEndLn = rTable.GetTabLines().GetPos( pLine );
+ }
+ if( nStartLn < USHRT_MAX && nEndLn < USHRT_MAX )
+ {
+ const SwTableLines &rLines = rTable.GetTabLines();
+ if( bNoSelection && nMinSize > nEndLn - nStartLn + 1 )
+ {
+ SwTableLines::size_type nNewEndLn = nStartLn + nMinSize - 1;
+ if( nNewEndLn >= rLines.size() )
+ {
+ mnAddLine = nNewEndLn - rLines.size() + 1;
+ nNewEndLn = rLines.size() - 1;
+ }
+ while( nEndLn < nNewEndLn )
+ {
+ SwTableLine *pLine2 = rLines[ ++nEndLn ];
+ SwTableBox *pTmpBox = pLine2->GetTabBoxes()[0];
+ FndLine_ *pInsLine = new FndLine_( pLine2, &rFndBox );
+ pInsLine->GetBoxes().insert(pInsLine->GetBoxes().begin(), std::make_unique<FndBox_>(pTmpBox, pInsLine));
+ rFndLines.push_back(std::unique_ptr<FndLine_>(pInsLine));
+ }
+ }
+ maLines.resize( nEndLn - nStartLn + 1 );
+ const SwSelBoxes* pSelBoxes = &rSelBoxes;
+ sal_uInt16 nCnt = 0;
+ for( SwTableLines::size_type nLine = nStartLn; nLine <= nEndLn; ++nLine )
+ {
+ addLine( nCnt, rLines[nLine]->GetTabBoxes(),
+ pSelBoxes, rTable.IsNewModel() );
+ if( bNoSelection )
+ pSelBoxes = nullptr;
+ }
+ }
+ if( bNoSelection && mnStartCol < USHRT_MAX )
+ {
+ sal_uInt16 nIdx = std::min(mnStartCol, o3tl::narrowing<sal_uInt16>(maLines[0].size()));
+ mnStartCol = std::accumulate(maLines[0].begin(), maLines[0].begin() + nIdx, sal_uInt16(0),
+ [](sal_uInt16 sum, const BoxSpanInfo& rInfo) { return sum + rInfo.mnColSpan; });
+ }
+ else
+ mnStartCol = USHRT_MAX;
+ }
+
+ void TableStructure::addLine( sal_uInt16 &rLine, const SwTableBoxes& rBoxes,
+ const SwSelBoxes* pSelBoxes, bool bNewModel )
+ {
+ bool bComplex = false;
+ if( !bNewModel )
+ for( SwTableBoxes::size_type nBox = 0; !bComplex && nBox < rBoxes.size(); ++nBox )
+ bComplex = !rBoxes[nBox]->GetTabLines().empty();
+ if( bComplex )
+ {
+ SubTable aSubTable;
+ SubLine aSubLine;
+ aSubTable.push_back( aSubLine );
+ SubTable::iterator pStartLn = aSubTable.begin();
+ SubTable::iterator pEndLn = aSubTable.end();
+ for( auto pBox : rBoxes )
+ insertSubBox( aSubTable, *pBox, pStartLn, pEndLn );
+ SubTable::size_type nSize = aSubTable.size();
+ if( nSize )
+ {
+ maLines.resize( maLines.size() + nSize - 1 );
+ while( pStartLn != pEndLn )
+ {
+ bool bSelected = false;
+ sal_uLong nBorder = 0;
+ sal_uInt16 nCol = 0;
+ maLines[rLine].reserve( pStartLn->size() );
+ BoxStructure::iterator pSel = maLines[rLine].end();
+ ColumnStructure::iterator pCol = maCols.begin();
+ for( const auto& rBox : *pStartLn )
+ {
+ addBox( rLine, pSelBoxes, rBox.mpBox, nBorder, nCol,
+ pCol, pSel, bSelected, rBox.mbCovered );
+ }
+ ++rLine;
+ ++pStartLn;
+ }
+ }
+ }
+ else
+ {
+ bool bSelected = false;
+ sal_uLong nBorder = 0;
+ sal_uInt16 nCol = 0;
+ maLines[rLine].reserve( rBoxes.size() );
+ ColumnStructure::iterator pCol = maCols.begin();
+ BoxStructure::iterator pSel = maLines[rLine].end();
+ for( auto pBox : rBoxes )
+ addBox( rLine, pSelBoxes, pBox, nBorder, nCol,
+ pCol, pSel, bSelected, false );
+ ++rLine;
+ }
+ }
+
+ void TableStructure::addBox( sal_uInt16 nLine, const SwSelBoxes* pSelBoxes,
+ SwTableBox *pBox, sal_uLong &rnBorder, sal_uInt16 &rnCol,
+ ColumnStructure::iterator& rpCol, BoxStructure::iterator& rpSel,
+ bool &rbSelected, bool bCovered )
+ {
+ BoxSpanInfo aInfo;
+ if( pSelBoxes &&
+ pSelBoxes->end() != pSelBoxes->find( pBox ) )
+ {
+ aInfo.mbSelected = true;
+ if( mnStartCol == USHRT_MAX )
+ {
+ mnStartCol = o3tl::narrowing<sal_uInt16>(maLines[nLine].size());
+ if( pSelBoxes->size() < 2 )
+ {
+ pSelBoxes = nullptr;
+ aInfo.mbSelected = false;
+ }
+ }
+ }
+ else
+ aInfo.mbSelected = false;
+ rnBorder += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ const sal_uInt16 nLeftCol = rnCol;
+ while( rpCol != maCols.end() && *rpCol < rnBorder )
+ {
+ ++rnCol;
+ ++rpCol;
+ }
+ if( rpCol == maCols.end() || *rpCol > rnBorder )
+ {
+ rpCol = maCols.insert( rpCol, rnBorder );
+ incColSpan( nLine, rnCol );
+ }
+ aInfo.mnColSpan = rnCol - nLeftCol;
+ aInfo.mpCopy = nullptr;
+ aInfo.mpBox = bCovered ? nullptr : pBox;
+ maLines[nLine].push_back( aInfo );
+ if( !aInfo.mbSelected )
+ return;
+
+ if( rbSelected )
+ {
+ while( rpSel != maLines[nLine].end() )
+ {
+ rpSel->mbSelected = true;
+ ++rpSel;
+ }
+ }
+ else
+ {
+ rpSel = maLines[nLine].end();
+ rbSelected = true;
+ }
+ --rpSel;
+ }
+
+ void TableStructure::moreLines( const SwTable& rTable )
+ {
+ if( !mnAddLine )
+ return;
+
+ const SwTableLines &rLines = rTable.GetTabLines();
+ const sal_uInt16 nLineCount = rLines.size();
+ if( nLineCount < mnAddLine )
+ mnAddLine = nLineCount;
+ sal_uInt16 nLine = o3tl::narrowing<sal_uInt16>(maLines.size());
+ maLines.resize( nLine + mnAddLine );
+ while( mnAddLine )
+ {
+ SwTableLine *pLine = rLines[ nLineCount - mnAddLine ];
+ addLine( nLine, pLine->GetTabBoxes(), nullptr, rTable.IsNewModel() );
+ --mnAddLine;
+ }
+ }
+
+ void TableStructure::incColSpan( sal_uInt16 nLineMax, sal_uInt16 nNewCol )
+ {
+ for( sal_uInt16 nLine = 0; nLine < nLineMax; ++nLine )
+ {
+ BoxStructure::iterator pInfo = maLines[nLine].begin();
+ BoxStructure::iterator pEnd = maLines[nLine].end();
+ tools::Long nCol = pInfo->mnColSpan;
+ while( nNewCol > nCol && ++pInfo != pEnd )
+ nCol += pInfo->mnColSpan;
+ if( pInfo != pEnd )
+ ++(pInfo->mnColSpan);
+ }
+ }
+
+ void TableStructure::assignBoxes( const TableStructure &rSource )
+ {
+ LineStructure::const_iterator pFirstLine = rSource.maLines.begin();
+ LineStructure::const_iterator pLastLine = rSource.maLines.end();
+ if( pFirstLine == pLastLine )
+ return;
+ LineStructure::const_iterator pCurrLine = pFirstLine;
+ LineStructure::size_type nLineCount = maLines.size();
+ sal_uInt16 nFirstStartCol = 0;
+ {
+ BoxStructure::const_iterator pFirstBox = pFirstLine->begin();
+ if( pFirstBox != pFirstLine->end() && pFirstBox->mpBox &&
+ pFirstBox->mpBox->getDummyFlag() )
+ nFirstStartCol = pFirstBox->mnColSpan;
+ }
+ for( LineStructure::size_type nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ BoxStructure::const_iterator pFirstBox = pCurrLine->begin();
+ BoxStructure::const_iterator pLastBox = pCurrLine->end();
+ sal_uInt16 nCurrStartCol = mnStartCol;
+ if( pFirstBox != pLastBox )
+ {
+ BoxStructure::const_iterator pTmpBox = pLastBox;
+ --pTmpBox;
+ if( pTmpBox->mpBox && pTmpBox->mpBox->getDummyFlag() )
+ --pLastBox;
+ if( pFirstBox != pLastBox && pFirstBox->mpBox &&
+ pFirstBox->mpBox->getDummyFlag() )
+ {
+ if( nCurrStartCol < USHRT_MAX )
+ {
+ if( pFirstBox->mnColSpan > nFirstStartCol )
+ nCurrStartCol += pFirstBox->mnColSpan - nFirstStartCol;
+ }
+ ++pFirstBox;
+ }
+ }
+ if( pFirstBox != pLastBox )
+ {
+ BoxStructure::const_iterator pCurrBox = pFirstBox;
+ BoxStructure &rBox = maLines[nLine];
+ BoxStructure::size_type nBoxCount = rBox.size();
+ sal_uInt16 nCol = 0;
+ for( BoxStructure::size_type nBox = 0; nBox < nBoxCount; ++nBox )
+ {
+ BoxSpanInfo& rInfo = rBox[nBox];
+ nCol += rInfo.mnColSpan;
+ if( rInfo.mbSelected || nCol > nCurrStartCol )
+ {
+ rInfo.mpCopy = pCurrBox->mpBox;
+ if( rInfo.mbSelected && rInfo.mpCopy->getDummyFlag() )
+ {
+ ++pCurrBox;
+ if( pCurrBox == pLastBox )
+ {
+ pCurrBox = pFirstBox;
+ if( pCurrBox->mpBox->getDummyFlag() )
+ ++pCurrBox;
+ }
+ rInfo.mpCopy = pCurrBox->mpBox;
+ }
+ ++pCurrBox;
+ if( pCurrBox == pLastBox )
+ {
+ if( rInfo.mbSelected )
+ pCurrBox = pFirstBox;
+ else
+ {
+ rInfo.mbSelected = rInfo.mpCopy == nullptr;
+ break;
+ }
+ }
+ rInfo.mbSelected = rInfo.mpCopy == nullptr;
+ }
+ }
+ }
+ ++pCurrLine;
+ if( pCurrLine == pLastLine )
+ pCurrLine = pFirstLine;
+ }
+ }
+
+ void TableStructure::copyBoxes( const SwTable& rSource, SwTable& rDstTable,
+ SwUndoTableCpyTable* pUndo ) const
+ {
+ LineStructure::size_type nLineCount = maLines.size();
+ for( LineStructure::size_type nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ const BoxStructure &rBox = maLines[nLine];
+ BoxStructure::size_type nBoxCount = rBox.size();
+ for( BoxStructure::size_type nBox = 0; nBox < nBoxCount; ++nBox )
+ {
+ const BoxSpanInfo& rInfo = rBox[nBox];
+ if( ( rInfo.mpCopy && !rInfo.mpCopy->getDummyFlag() )
+ || rInfo.mbSelected )
+ {
+ SwTableBox *pBox = rInfo.mpBox;
+ if( pBox && pBox->getRowSpan() > 0 )
+ lcl_CpyBox( rSource, rInfo.mpCopy, rDstTable, pBox,
+ true, pUndo );
+ }
+ }
+ }
+ }
+}
+
+/** Copy Table into this Box.
+ Copy all Boxes of a Line into the corresponding Boxes. The old content
+ is deleted by doing this.
+ If no Box is left the remaining content goes to the Box of a "BaseLine".
+ If there's no Line anymore, put it also into the last Box of a "BaseLine". */
+static void lcl_CpyBox( const SwTable& rCpyTable, const SwTableBox* pCpyBox,
+ SwTable& rDstTable, SwTableBox* pDstBox,
+ bool bDelContent, SwUndoTableCpyTable* pUndo )
+{
+ OSL_ENSURE( ( !pCpyBox || pCpyBox->GetSttNd() ) && pDstBox->GetSttNd(),
+ "No content in this Box" );
+
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+ SwDoc* pDoc = rDstTable.GetFrameFormat()->GetDoc();
+
+ // First copy the new content and then delete the old one.
+ // Do not create empty Sections, otherwise they will be deleted!
+ std::unique_ptr< SwNodeRange > pRg( pCpyBox ?
+ new SwNodeRange ( *pCpyBox->GetSttNd(), SwNodeOffset(1),
+ *pCpyBox->GetSttNd()->EndOfSectionNode() ) : nullptr );
+
+ SwNodeIndex aInsIdx( *pDstBox->GetSttNd(), bDelContent ? SwNodeOffset(1) :
+ pDstBox->GetSttNd()->EndOfSectionIndex() -
+ pDstBox->GetSttIdx() );
+
+ if( pUndo )
+ pUndo->AddBoxBefore( *pDstBox, bDelContent );
+
+ bool bUndoRedline = pUndo && pDoc->getIDocumentRedlineAccess().IsRedlineOn();
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodeIndex aSavePos( aInsIdx, -1 );
+ if (pRg)
+ pCpyDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pRg, aInsIdx, nullptr, false);
+ else
+ pDoc->GetNodes().MakeTextNode( aInsIdx, pDoc->GetDfltTextFormatColl() );
+ ++aSavePos;
+
+ SwTableLine* pLine = pDstBox->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ bool bReplaceColl = true;
+ if( bDelContent && !bUndoRedline )
+ {
+ // Delete the Fly first, then the corresponding Nodes
+ SwNodeIndex aEndNdIdx( *aInsIdx.GetNode().EndOfSectionNode() );
+
+ // Move Bookmarks
+ {
+ SwPosition aMvPos( aInsIdx );
+ SwContentNode* pCNd = SwNodes::GoPrevious( &aMvPos.nNode );
+ aMvPos.nContent.Assign( pCNd, pCNd->Len() );
+ SwDoc::CorrAbs( aInsIdx, aEndNdIdx, aMvPos );
+ }
+
+ // If we still have FlyFrames hanging around, delete them too
+ for( const auto pFly : *pDoc->GetSpzFrameFormats() )
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ aInsIdx <= pAPos->nNode && pAPos->nNode <= aEndNdIdx )
+ {
+ pDoc->getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ }
+ }
+
+ // If DestBox is a Headline Box and has Table style set, then
+ // DO NOT automatically set the TableHeadline style!
+ if( 1 < rDstTable.GetTabLines().size() &&
+ pLine == rDstTable.GetTabLines().front() )
+ {
+ SwContentNode* pCNd = aInsIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ SwNodeIndex aTmp( aInsIdx );
+ pCNd = pDoc->GetNodes().GoNext( &aTmp );
+ }
+
+ if( pCNd &&
+ RES_POOLCOLL_TABLE_HDLN !=
+ pCNd->GetFormatColl()->GetPoolFormatId() )
+ bReplaceColl = false;
+ }
+
+ pDoc->GetNodes().Delete( aInsIdx, aEndNdIdx.GetIndex() - aInsIdx.GetIndex() );
+ }
+
+ //b6341295: Table copy redlining will be managed by AddBoxAfter()
+ if( pUndo )
+ pUndo->AddBoxAfter( *pDstBox, aInsIdx, bDelContent );
+
+ // heading
+ SwTextNode *const pTextNd = aSavePos.GetNode().GetTextNode();
+ if( !pTextNd )
+ return;
+
+ const sal_uInt16 nPoolId = pTextNd->GetTextColl()->GetPoolFormatId();
+ if( bReplaceColl &&
+ (( 1 < rDstTable.GetTabLines().size() &&
+ pLine == rDstTable.GetTabLines().front() )
+ // Is the Table's content still valid?
+ ? RES_POOLCOLL_TABLE == nPoolId
+ : RES_POOLCOLL_TABLE_HDLN == nPoolId ) )
+ {
+ SwTextFormatColl* pColl = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool(
+ o3tl::narrowing<sal_uInt16>(
+ RES_POOLCOLL_TABLE == nPoolId
+ ? RES_POOLCOLL_TABLE_HDLN
+ : RES_POOLCOLL_TABLE ) );
+ if( pColl ) // Apply style
+ {
+ SwPaM aPam( aSavePos );
+ aPam.SetMark();
+ aPam.Move( fnMoveForward, GoInSection );
+ pDoc->SetTextFormatColl( aPam, pColl );
+ }
+ }
+
+ // Delete the current Formula/Format/Value values
+ if( SfxItemState::SET == pDstBox->GetFrameFormat()->GetItemState( RES_BOXATR_FORMAT ) ||
+ SfxItemState::SET == pDstBox->GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA ) ||
+ SfxItemState::SET == pDstBox->GetFrameFormat()->GetItemState( RES_BOXATR_VALUE ) )
+ {
+ pDstBox->ClaimFrameFormat()->ResetFormatAttr( RES_BOXATR_FORMAT,
+ RES_BOXATR_VALUE );
+ }
+
+ // Copy the TableBoxAttributes - Formula/Format/Value
+ if( !pCpyBox )
+ return;
+
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxAttrSet( pCpyDoc->GetAttrPool() );
+ aBoxAttrSet.Put( pCpyBox->GetFrameFormat()->GetAttrSet() );
+ if( !aBoxAttrSet.Count() )
+ return;
+
+ const SwTableBoxNumFormat* pItem;
+ SvNumberFormatter* pN = pDoc->GetNumberFormatter( false );
+ if( pN && pN->HasMergeFormatTable() &&
+ (pItem = aBoxAttrSet.GetItemIfSet( RES_BOXATR_FORMAT, false )) )
+ {
+ sal_uLong nOldIdx = pItem->GetValue();
+ sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
+ if( nNewIdx != nOldIdx )
+ aBoxAttrSet.Put( SwTableBoxNumFormat( nNewIdx ));
+ }
+ pDstBox->ClaimFrameFormat()->SetFormatAttr( aBoxAttrSet );
+}
+
+bool SwTable::InsNewTable( const SwTable& rCpyTable, const SwSelBoxes& rSelBoxes,
+ SwUndoTableCpyTable* pUndo )
+{
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+
+ SwTableNumFormatMerge aTNFM( *pCpyDoc, *pDoc );
+
+ // Analyze source structure
+ TableStructure aCopyStruct( rCpyTable );
+
+ // Analyze target structure (from start box) and selected substructure
+ FndBox_ aFndBox( nullptr, nullptr );
+ { // get all boxes/lines
+ FndPara aPara( rSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ TableStructure aTarget( *this, aFndBox, rSelBoxes, aCopyStruct.getLineCount() );
+
+ bool bClear = false;
+ if( aTarget.mnAddLine && IsNewModel() )
+ {
+ SwSelBoxes aBoxes;
+ aBoxes.insert( GetTabLines().back()->GetTabBoxes().front() );
+ if( pUndo )
+ pUndo->InsertRow( *this, aBoxes, aTarget.mnAddLine );
+ else
+ InsertRow( pDoc, aBoxes, aTarget.mnAddLine, /*bBehind*/true );
+
+ aTarget.moreLines( *this );
+ bClear = true;
+ }
+
+ // Find mapping, if needed extend target table and/or selection
+ aTarget.assignBoxes( aCopyStruct );
+
+ {
+ // Change table formulas into relative representation
+ SwTableFormulaUpdate aMsgHint( &rCpyTable );
+ aMsgHint.m_eFlags = TBL_RELBOXNAME;
+ pCpyDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ // delete frames
+ aFndBox.SetTableLines( *this );
+ if( bClear )
+ aFndBox.ClearLineBehind();
+ aFndBox.DelFrames( *this );
+
+ // copy boxes
+ aTarget.copyBoxes( rCpyTable, *this, pUndo );
+
+ // adjust row span attributes accordingly
+
+ // make frames
+ aFndBox.MakeFrames( *this );
+
+ return true;
+}
+
+/** Copy Table into this Box.
+ Copy all Boxes of a Line into the corresponding Boxes. The old content is
+ deleted by doing this.
+ If no Box is left the remaining content goes to the Box of a "BaseLine".
+ If there's no Line anymore, put it also into the last Box of a "BaseLine". */
+bool SwTable::InsTable( const SwTable& rCpyTable, const SwNodeIndex& rSttBox,
+ SwUndoTableCpyTable* pUndo )
+{
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+
+ SwTableNode* pTableNd = pDoc->IsIdxInTable( rSttBox );
+
+ // Find the Box, to which should be copied:
+ SwTableBox* pMyBox = GetTableBox(
+ rSttBox.GetNode().FindTableBoxStartNode()->GetIndex() );
+
+ OSL_ENSURE( pMyBox, "Index is not in a Box in this Table" );
+
+ // First delete the Table's Frames
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.DelFrames( pTableNd->GetTable() );
+
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+
+ {
+ // Convert Table formulas to their relative representation
+ SwTableFormulaUpdate aMsgHint( &rCpyTable );
+ aMsgHint.m_eFlags = TBL_RELBOXNAME;
+ pCpyDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ SwTableNumFormatMerge aTNFM( *pCpyDoc, *pDoc );
+
+ bool bDelContent = true;
+ const SwTableBox* pTmp;
+
+ for( auto pLine : rCpyTable.GetTabLines() )
+ {
+ // Get the first from the CopyLine
+ const SwTableBox* pCpyBox = pLine->GetTabBoxes().front();
+ while( !pCpyBox->GetTabLines().empty() )
+ pCpyBox = pCpyBox->GetTabLines().front()->GetTabBoxes().front();
+
+ do {
+ // First copy the new content and then delete the old one.
+ // Do not create empty Sections, otherwise they will be deleted!
+ lcl_CpyBox( rCpyTable, pCpyBox, *this, pMyBox, bDelContent, pUndo );
+
+ pTmp = pCpyBox->FindNextBox( rCpyTable, pCpyBox, false );
+ if( !pTmp )
+ break; // no more Boxes
+ pCpyBox = pTmp;
+
+ pTmp = pMyBox->FindNextBox( *this, pMyBox, false );
+ if( !pTmp )
+ bDelContent = false; // No space left?
+ else
+ pMyBox = const_cast<SwTableBox*>(pTmp);
+
+ } while( true );
+
+ // Find the topmost Line
+ SwTableLine* pNxtLine = pMyBox->GetUpper();
+ while( pNxtLine->GetUpper() )
+ pNxtLine = pNxtLine->GetUpper()->GetUpper();
+ const SwTableLines::size_type nPos = GetTabLines().GetPos( pNxtLine ) + 1;
+ // Is there a next?
+ if( nPos >= GetTabLines().size() )
+ bDelContent = false; // there is none, all goes into the last Box
+ else
+ {
+ // Find the next Box with content
+ pNxtLine = GetTabLines()[ nPos ];
+ pMyBox = pNxtLine->GetTabBoxes().front();
+ while( !pMyBox->GetTabLines().empty() )
+ pMyBox = pMyBox->GetTabLines().front()->GetTabBoxes().front();
+ bDelContent = true;
+ }
+ }
+
+ aFndBox.MakeFrames( pTableNd->GetTable() ); // Create the Frames anew
+ return true;
+}
+
+bool SwTable::InsTable( const SwTable& rCpyTable, const SwSelBoxes& rSelBoxes,
+ SwUndoTableCpyTable* pUndo )
+{
+ OSL_ENSURE( !rSelBoxes.empty(), "Missing selection" );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ if( IsNewModel() || rCpyTable.IsNewModel() )
+ return InsNewTable( rCpyTable, rSelBoxes, pUndo );
+
+ OSL_ENSURE( !rCpyTable.IsTableComplex(), "Table too complex" );
+
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ SwDoc* pCpyDoc = rCpyTable.GetFrameFormat()->GetDoc();
+
+ SwTableNumFormatMerge aTNFM( *pCpyDoc, *pDoc );
+
+ FndLine_ *pFLine;
+ FndBox_ aFndBox( nullptr, nullptr );
+ // Find all Boxes/Lines
+ {
+ FndPara aPara( rSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+
+ // Special case: If a Box is located in a Table, copy it to all selected
+ // Boxes!
+ if( 1 != rCpyTable.GetTabSortBoxes().size() )
+ {
+ FndBox_* pFndBox;
+
+ const FndLines_t::size_type nFndCnt = aFndBox.GetLines().size();
+ if( !nFndCnt )
+ return false;
+
+ // Check if we have enough space for all Lines and Boxes
+ SwTableLines::size_type nTstLns = 0;
+ pFLine = aFndBox.GetLines().front().get();
+ sal_uInt16 nSttLine = GetTabLines().GetPos( pFLine->GetLine() );
+ // Do we have as many rows, actually?
+ if( 1 == nFndCnt )
+ {
+ // Is there still enough space in the Table?
+ if( (GetTabLines().size() - nSttLine ) <
+ rCpyTable.GetTabLines().size() )
+ {
+ // If we don't have enough Lines, then see if we can insert
+ // new ones to reach our goal. But only if the SSelection
+ // contains a Box!
+ if( 1 < rSelBoxes.size() )
+ return false;
+
+ const sal_uInt16 nNewLns = rCpyTable.GetTabLines().size() -
+ (GetTabLines().size() - nSttLine );
+
+ // See if the Box count is high enough for the Lines
+ SwTableLine* pLastLn = GetTabLines().back();
+
+ SwTableBox* pSttBox = pFLine->GetBoxes()[0]->GetBox();
+ const SwTableBoxes::size_type nSttBox = pFLine->GetLine()->GetBoxPos( pSttBox );
+ for( SwTableLines::size_type n = rCpyTable.GetTabLines().size() - nNewLns;
+ n < rCpyTable.GetTabLines().size(); ++n )
+ {
+ SwTableLine* pCpyLn = rCpyTable.GetTabLines()[ n ];
+
+ if( pLastLn->GetTabBoxes().size() < nSttBox ||
+ ( pLastLn->GetTabBoxes().size() - nSttBox ) <
+ pCpyLn->GetTabBoxes().size() )
+ return false;
+
+ // Test for nesting
+ for( SwTableBoxes::size_type nBx = 0; nBx < pCpyLn->GetTabBoxes().size(); ++nBx )
+ if( !pLastLn->GetTabBoxes()[ nSttBox + nBx ]->GetSttNd() )
+ return false;
+ }
+ // We have enough space for the to-be-copied, so insert new
+ // rows accordingly.
+ SwTableBox* pInsBox = pLastLn->GetTabBoxes()[ nSttBox ];
+ OSL_ENSURE( pInsBox && pInsBox->GetSttNd(),
+ "no ContentBox or it's not in this Table" );
+ SwSelBoxes aBoxes;
+
+ if( pUndo
+ ? !pUndo->InsertRow( *this, SelLineFromBox( pInsBox,
+ aBoxes ), nNewLns )
+ : !InsertRow( pDoc, SelLineFromBox( pInsBox,
+ aBoxes ), nNewLns, /*bBehind*/true ) )
+ return false;
+ }
+
+ nTstLns = rCpyTable.GetTabLines().size(); // copy this many
+ }
+ else if( 0 == (nFndCnt % rCpyTable.GetTabLines().size()) )
+ nTstLns = nFndCnt;
+ else
+ return false; // not enough space for the rows
+
+ for( SwTableLines::size_type nLn = 0; nLn < nTstLns; ++nLn )
+ {
+ // We have enough rows, so check the Boxes per row
+ pFLine = aFndBox.GetLines()[ nLn % nFndCnt ].get();
+ SwTableLine* pLine = pFLine->GetLine();
+ SwTableBox* pSttBox = pFLine->GetBoxes()[0]->GetBox();
+ const SwTableBoxes::size_type nSttBox = pLine->GetBoxPos( pSttBox );
+ std::unique_ptr<FndLine_> pInsFLine;
+ if( nLn >= nFndCnt )
+ {
+ // We have more rows in the ClipBoard than we have selected
+ pInsFLine.reset(new FndLine_( GetTabLines()[ nSttLine + nLn ],
+ &aFndBox ));
+ pLine = pInsFLine->GetLine();
+ }
+ SwTableLine* pCpyLn = rCpyTable.GetTabLines()[ nLn %
+ rCpyTable.GetTabLines().size() ];
+
+ // Selected too few rows?
+ if( pInsFLine )
+ {
+ // We insert a new row into the FndBox
+ if( pLine->GetTabBoxes().size() < nSttBox ||
+ pLine->GetTabBoxes().size() - nSttBox < pFLine->GetBoxes().size() )
+ {
+ return false;
+ }
+
+ // Test for nesting
+ for (FndBoxes_t::size_type nBx = 0; nBx < pFLine->GetBoxes().size(); ++nBx)
+ {
+ SwTableBox *pTmpBox = pLine->GetTabBoxes()[ nSttBox + nBx ];
+ if( !pTmpBox->GetSttNd() )
+ {
+ return false;
+ }
+ // if Ok, insert the Box into the FndLine
+ pFndBox = new FndBox_( pTmpBox, pInsFLine.get() );
+ pInsFLine->GetBoxes().insert( pInsFLine->GetBoxes().begin() + nBx,
+ std::unique_ptr<FndBox_>(pFndBox));
+ }
+ aFndBox.GetLines().insert( aFndBox.GetLines().begin() + nLn, std::move(pInsFLine));
+ }
+ else if( pFLine->GetBoxes().size() == 1 )
+ {
+ if( pLine->GetTabBoxes().size() < nSttBox ||
+ ( pLine->GetTabBoxes().size() - nSttBox ) <
+ pCpyLn->GetTabBoxes().size() )
+ return false;
+
+ // Test for nesting
+ for( SwTableBoxes::size_type nBx = 0; nBx < pCpyLn->GetTabBoxes().size(); ++nBx )
+ {
+ SwTableBox *pTmpBox = pLine->GetTabBoxes()[ nSttBox + nBx ];
+ if( !pTmpBox->GetSttNd() )
+ return false;
+ // if Ok, insert the Box into the FndLine
+ if( nBx == pFLine->GetBoxes().size() )
+ {
+ pFndBox = new FndBox_( pTmpBox, pFLine );
+ pFLine->GetBoxes().insert(pFLine->GetBoxes().begin() + nBx,
+ std::unique_ptr<FndBox_>(pFndBox));
+ }
+ }
+ }
+ else
+ {
+ // Match the selected Boxes with the ones in the Clipboard
+ // (n times)
+ if( 0 != ( pFLine->GetBoxes().size() %
+ pCpyLn->GetTabBoxes().size() ))
+ return false;
+
+ // Test for nesting
+ for (auto &rpBox : pFLine->GetBoxes())
+ {
+ if (!rpBox->GetBox()->GetSttNd())
+ return false;
+ }
+ }
+ }
+
+ if( aFndBox.GetLines().empty() )
+ return false;
+ }
+
+ {
+ // Convert Table formulas to their relative representation
+ SwTableFormulaUpdate aMsgHint( &rCpyTable );
+ aMsgHint.m_eFlags = TBL_RELBOXNAME;
+ pCpyDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ // Delete the Frames
+ aFndBox.SetTableLines( *this );
+ //Not dispose accessible table
+ aFndBox.DelFrames( *this );
+
+ if( 1 == rCpyTable.GetTabSortBoxes().size() )
+ {
+ SwTableBox *pTmpBx = rCpyTable.GetTabSortBoxes()[0];
+ for (size_t n = 0; n < rSelBoxes.size(); ++n)
+ {
+ lcl_CpyBox( rCpyTable, pTmpBx, *this,
+ rSelBoxes[n], true, pUndo );
+ }
+ }
+ else
+ for (FndLines_t::size_type nLn = 0; nLn < aFndBox.GetLines().size(); ++nLn)
+ {
+ pFLine = aFndBox.GetLines()[ nLn ].get();
+ SwTableLine* pCpyLn = rCpyTable.GetTabLines()[
+ nLn % rCpyTable.GetTabLines().size() ];
+ for (FndBoxes_t::size_type nBx = 0; nBx < pFLine->GetBoxes().size(); ++nBx)
+ {
+ // Copy the pCpyBox into pMyBox
+ lcl_CpyBox( rCpyTable, pCpyLn->GetTabBoxes()[
+ nBx % pCpyLn->GetTabBoxes().size() ],
+ *this, pFLine->GetBoxes()[nBx]->GetBox(), true, pUndo );
+ }
+ }
+
+ aFndBox.MakeFrames( *this );
+ return true;
+}
+
+static void FndContentLine( const SwTableLine* pLine, SwSelBoxes* pPara );
+
+static void FndContentBox( const SwTableBox* pBox, SwSelBoxes* pPara )
+{
+ if( !pBox->GetTabLines().empty() )
+ {
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ FndContentLine( pLine, pPara );
+ }
+ else
+ pPara->insert( const_cast<SwTableBox*>(pBox) );
+}
+
+static void FndContentLine( const SwTableLine* pLine, SwSelBoxes* pPara )
+{
+ for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+ FndContentBox(pBox, pPara );
+}
+
+// Find all Boxes with content in this Box
+SwSelBoxes& SwTable::SelLineFromBox( const SwTableBox* pBox,
+ SwSelBoxes& rBoxes, bool bToTop )
+{
+ SwTableLine* pLine = const_cast<SwTableLine*>(pBox->GetUpper());
+ if( bToTop )
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // Delete all old ones
+ rBoxes.clear();
+ for( const auto& rpBox : pLine->GetTabBoxes() )
+ FndContentBox(rpBox, &rBoxes );
+ return rBoxes;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/tblrwcl.cxx b/sw/source/core/doc/tblrwcl.cxx
new file mode 100644
index 000000000..b169b9d83
--- /dev/null
+++ b/sw/source/core/doc/tblrwcl.cxx
@@ -0,0 +1,3387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <hintids.hxx>
+
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <tools/fract.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docsh.hxx>
+#include <fesh.hxx>
+#include <tabfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <fldbas.hxx>
+#include <rowfrm.hxx>
+#include <ddefld.hxx>
+#include <hints.hxx>
+#include <UndoTable.hxx>
+#include <cellatr.hxx>
+#include <mvsave.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <poolfmt.hxx>
+#include <tblrwcl.hxx>
+#include <unochart.hxx>
+#include <o3tl/numeric.hxx>
+#include <calbck.hxx>
+#include <docary.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+#define COLFUZZY 20
+#define ROWFUZZY 10
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+namespace {
+
+// In order to set the Frame Formats for the Boxes, it's enough to look
+// up the current one in the array. If it's already there return the new one.
+struct CpyTabFrame
+{
+ SwFrameFormat* pFrameFormat;
+ SwTableBoxFormat *pNewFrameFormat;
+
+ explicit CpyTabFrame(SwFrameFormat* pCurrentFrameFormat) : pNewFrameFormat( nullptr )
+ { pFrameFormat = pCurrentFrameFormat; }
+
+ bool operator==( const CpyTabFrame& rCpyTabFrame ) const
+ { return pFrameFormat == rCpyTabFrame.pFrameFormat; }
+ bool operator<( const CpyTabFrame& rCpyTabFrame ) const
+ { return pFrameFormat < rCpyTabFrame.pFrameFormat; }
+};
+
+struct CR_SetBoxWidth
+{
+ SwShareBoxFormats aShareFormats;
+ SwTableNode* pTableNd;
+ SwTwips nDiff, nSide, nMaxSize, nLowerDiff;
+ TableChgMode nMode;
+ bool bBigger, bLeft;
+
+ CR_SetBoxWidth( TableChgWidthHeightType eType, SwTwips nDif, SwTwips nSid,
+ SwTwips nMax, SwTableNode* pTNd )
+ : pTableNd( pTNd ),
+ nDiff( nDif ), nSide( nSid ), nMaxSize( nMax ), nLowerDiff( 0 )
+ {
+ bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
+ TableChgWidthHeightType::CellLeft == extractPosition( eType );
+ bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
+ nMode = pTableNd->GetTable().GetTableChgMode();
+ }
+ CR_SetBoxWidth( const CR_SetBoxWidth& rCpy )
+ : pTableNd( rCpy.pTableNd ),
+ nDiff( rCpy.nDiff ), nSide( rCpy.nSide ),
+ nMaxSize( rCpy.nMaxSize ), nLowerDiff( 0 ),
+ nMode( rCpy.nMode ),
+ bBigger( rCpy.bBigger ), bLeft( rCpy.bLeft )
+ {
+ }
+
+ void LoopClear()
+ {
+ nLowerDiff = 0;
+ }
+};
+
+}
+
+static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck );
+static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck );
+
+typedef bool (*FN_lcl_SetBoxWidth)(SwTableLine*, CR_SetBoxWidth&, SwTwips, bool );
+
+#ifdef DBG_UTIL
+
+#define CHECKBOXWIDTH \
+ { \
+ SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth(); \
+ for (size_t nTmp = 0; nTmp < m_aLines.size(); ++nTmp) \
+ ::CheckBoxWidth( *m_aLines[ nTmp ], nSize ); \
+ }
+
+#define CHECKTABLELAYOUT \
+ { \
+ for ( size_t i = 0; i < GetTabLines().size(); ++i ) \
+ { \
+ SwFrameFormat* pFormat = GetTabLines()[i]->GetFrameFormat(); \
+ SwIterator<SwRowFrame,SwFormat> aIter( *pFormat ); \
+ for (SwRowFrame* pFrame=aIter.First(); pFrame; pFrame=aIter.Next())\
+ { \
+ if ( pFrame->GetTabLine() == GetTabLines()[i] ) \
+ { \
+ OSL_ENSURE( pFrame->GetUpper()->IsTabFrame(), \
+ "Table layout does not match table structure" ); \
+ } \
+ } \
+ } \
+ }
+
+#else
+
+#define CHECKBOXWIDTH
+#define CHECKTABLELAYOUT
+
+#endif // DBG_UTIL
+
+namespace {
+
+struct CR_SetLineHeight
+{
+ SwTableNode* pTableNd;
+ SwTwips nMaxSpace, nMaxHeight;
+ TableChgMode nMode;
+ bool bBigger;
+
+ CR_SetLineHeight( TableChgWidthHeightType eType, SwTableNode* pTNd )
+ : pTableNd( pTNd ),
+ nMaxSpace( 0 ), nMaxHeight( 0 )
+ {
+ bBigger = bool(eType & TableChgWidthHeightType::BiggerMode );
+ nMode = pTableNd->GetTable().GetTableChgMode();
+ }
+ CR_SetLineHeight( const CR_SetLineHeight& rCpy )
+ : pTableNd( rCpy.pTableNd ),
+ nMaxSpace( rCpy.nMaxSpace ), nMaxHeight( rCpy.nMaxHeight ),
+ nMode( rCpy.nMode ),
+ bBigger( rCpy.bBigger )
+ {}
+};
+
+}
+
+static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck );
+static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck );
+
+typedef bool (*FN_lcl_SetLineHeight)(SwTableLine*, CR_SetLineHeight&, SwTwips, bool );
+
+typedef o3tl::sorted_vector<CpyTabFrame> CpyTabFrames;
+
+namespace {
+
+struct CpyPara
+{
+ std::shared_ptr< std::vector< std::vector< sal_uLong > > > pWidths;
+ SwDoc& rDoc;
+ SwTableNode* pTableNd;
+ CpyTabFrames& rTabFrameArr;
+ SwTableLine* pInsLine;
+ SwTableBox* pInsBox;
+ sal_uLong nOldSize, nNewSize; // in order to correct the size attributes
+ sal_uLong nMinLeft, nMaxRight;
+ sal_uInt16 nCpyCnt, nInsPos;
+ sal_uInt16 nLnIdx, nBoxIdx;
+ sal_uInt8 nDelBorderFlag;
+ bool bCpyContent;
+
+ CpyPara( SwTableNode* pNd, sal_uInt16 nCopies, CpyTabFrames& rFrameArr )
+ : rDoc( pNd->GetDoc() ), pTableNd( pNd ), rTabFrameArr(rFrameArr),
+ pInsLine(nullptr), pInsBox(nullptr), nOldSize(0), nNewSize(0),
+ nMinLeft(ULONG_MAX), nMaxRight(0),
+ nCpyCnt(nCopies), nInsPos(0),
+ nLnIdx(0), nBoxIdx(0),
+ nDelBorderFlag(0), bCpyContent( true )
+ {}
+ CpyPara( const CpyPara& rPara, SwTableLine* pLine )
+ : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd),
+ rTabFrameArr(rPara.rTabFrameArr), pInsLine(pLine), pInsBox(rPara.pInsBox),
+ nOldSize(0), nNewSize(rPara.nNewSize), nMinLeft( rPara.nMinLeft ),
+ nMaxRight( rPara.nMaxRight ), nCpyCnt(rPara.nCpyCnt), nInsPos(0),
+ nLnIdx( rPara.nLnIdx), nBoxIdx( rPara.nBoxIdx ),
+ nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent )
+ {}
+ CpyPara( const CpyPara& rPara, SwTableBox* pBox )
+ : pWidths( rPara.pWidths ), rDoc(rPara.rDoc), pTableNd(rPara.pTableNd),
+ rTabFrameArr(rPara.rTabFrameArr), pInsLine(rPara.pInsLine), pInsBox(pBox),
+ nOldSize(rPara.nOldSize), nNewSize(rPara.nNewSize),
+ nMinLeft( rPara.nMinLeft ), nMaxRight( rPara.nMaxRight ),
+ nCpyCnt(rPara.nCpyCnt), nInsPos(0), nLnIdx(rPara.nLnIdx), nBoxIdx(rPara.nBoxIdx),
+ nDelBorderFlag( rPara.nDelBorderFlag ), bCpyContent( rPara.bCpyContent )
+ {}
+};
+
+}
+
+static SwTableLine* lcl_CopyRow(FndLine_ & rFndLine, CpyPara *const pCpyPara);
+
+static void lcl_CopyCol( FndBox_ & rFndBox, CpyPara *const pCpyPara)
+{
+ // Look up the Frame Format in the Frame Format Array
+ SwTableBox* pBox = rFndBox.GetBox();
+ CpyTabFrame aFindFrame(pBox->GetFrameFormat());
+
+ if( pCpyPara->nCpyCnt )
+ {
+ sal_uInt16 nFndPos;
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
+ nFndPos = itFind - pCpyPara->rTabFrameArr.begin();
+ if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) )
+ {
+ // For nested copying, also save the new Format as an old one.
+ SwTableBoxFormat* pNewFormat = static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat());
+
+ // Find the selected Boxes in the Line:
+ FndLine_ const* pCmpLine = nullptr;
+ SwFormatFrameSize aFrameSz( pNewFormat->GetFrameSize() );
+
+ bool bDiffCount = false;
+ if( !pBox->GetTabLines().empty() )
+ {
+ pCmpLine = rFndBox.GetLines().front().get();
+ if ( pCmpLine->GetBoxes().size() != pCmpLine->GetLine()->GetTabBoxes().size() )
+ bDiffCount = true;
+ }
+
+ if( bDiffCount )
+ {
+ // The first Line should be enough
+ FndBoxes_t const& rFndBoxes = pCmpLine->GetBoxes();
+ tools::Long nSz = 0;
+ for( auto n = rFndBoxes.size(); n; )
+ {
+ nSz += rFndBoxes[--n]->GetBox()->
+ GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ aFrameSz.SetWidth( aFrameSz.GetWidth() -
+ nSz / ( pCpyPara->nCpyCnt + 1 ) );
+ pNewFormat->SetFormatAttr( aFrameSz );
+ aFrameSz.SetWidth( nSz / ( pCpyPara->nCpyCnt + 1 ) );
+
+ // Create a new Format for the new Box, specifying its size.
+ aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pNewFormat->GetDoc()->
+ MakeTableLineFormat());
+ *aFindFrame.pNewFrameFormat = *pNewFormat;
+ aFindFrame.pNewFrameFormat->SetFormatAttr( aFrameSz );
+ }
+ else
+ {
+ aFrameSz.SetWidth( aFrameSz.GetWidth() / ( pCpyPara->nCpyCnt + 1 ) );
+ pNewFormat->SetFormatAttr( aFrameSz );
+
+ aFindFrame.pNewFrameFormat = pNewFormat;
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ aFindFrame.pFrameFormat = pNewFormat;
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+ }
+ else
+ {
+ aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ];
+ pBox->ChgFrameFormat( aFindFrame.pNewFrameFormat );
+ }
+ }
+ else
+ {
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame );
+ if( pCpyPara->nDelBorderFlag &&
+ itFind != pCpyPara->rTabFrameArr.end() )
+ aFindFrame = *itFind;
+ else
+ aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+ }
+
+ if (!rFndBox.GetLines().empty())
+ {
+ pBox = new SwTableBox( aFindFrame.pNewFrameFormat,
+ rFndBox.GetLines().size(), pCpyPara->pInsLine );
+ pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox );
+ CpyPara aPara( *pCpyPara, pBox );
+ aPara.nDelBorderFlag &= 7;
+
+ for (auto const& pFndLine : rFndBox.GetLines())
+ {
+ lcl_CopyRow(*pFndLine, &aPara);
+ }
+ }
+ else
+ {
+ ::InsTableBox( pCpyPara->rDoc, pCpyPara->pTableNd, pCpyPara->pInsLine,
+ aFindFrame.pNewFrameFormat, pBox, pCpyPara->nInsPos++ );
+
+ const FndBoxes_t& rFndBxs = rFndBox.GetUpper()->GetBoxes();
+ if( 8 > pCpyPara->nDelBorderFlag
+ ? pCpyPara->nDelBorderFlag != 0
+ : &rFndBox == rFndBxs[rFndBxs.size() - 1].get())
+ {
+ const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
+ if( 8 > pCpyPara->nDelBorderFlag
+ ? rBoxItem.GetTop()
+ : rBoxItem.GetRight() )
+ {
+ aFindFrame.pFrameFormat = pBox->GetFrameFormat();
+
+ SvxBoxItem aNew( rBoxItem );
+ if( 8 > pCpyPara->nDelBorderFlag )
+ aNew.SetLine( nullptr, SvxBoxItemLine::TOP );
+ else
+ aNew.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+
+ if( 1 == pCpyPara->nDelBorderFlag ||
+ 8 == pCpyPara->nDelBorderFlag )
+ {
+ // For all Boxes that delete TopBorderLine, we copy after that
+ pBox = pCpyPara->pInsLine->GetTabBoxes()[
+ pCpyPara->nInsPos - 1 ];
+ }
+
+ aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+
+ // Else we copy before that and the first Line keeps the TopLine
+ // and we remove it at the original
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+
+ if( !pCpyPara->nCpyCnt )
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+ }
+ }
+}
+
+static SwTableLine* lcl_CopyRow(FndLine_& rFndLine, CpyPara *const pCpyPara)
+{
+ SwTableLine* pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(rFndLine.GetLine()->GetFrameFormat()),
+ rFndLine.GetBoxes().size(), pCpyPara->pInsBox );
+ if( pCpyPara->pInsBox )
+ {
+ SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
+ }
+ else
+ {
+ SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
+ }
+
+ CpyPara aPara( *pCpyPara, pNewLine );
+ for (auto const& it : rFndLine.GetBoxes())
+ {
+ lcl_CopyCol(*it, &aPara);
+ }
+
+ pCpyPara->nDelBorderFlag &= 0xf8;
+
+ return pNewLine;
+}
+
+static void lcl_InsCol( FndLine_* pFndLn, CpyPara& rCpyPara, sal_uInt16 nCpyCnt,
+ bool bBehind )
+{
+ // Bug 29124: Not only copy in the BaseLines. If possible, we go down as far as possible
+ FndBox_* pFBox;
+ if( 1 == pFndLn->GetBoxes().size() &&
+ !( pFBox = pFndLn->GetBoxes()[0].get() )->GetBox()->GetSttNd() )
+ {
+ // A Box with multiple Lines, so insert into these Lines
+ for (auto &rpLine : pFBox->GetLines())
+ {
+ lcl_InsCol( rpLine.get(), rCpyPara, nCpyCnt, bBehind );
+ }
+ }
+ else
+ {
+ rCpyPara.pInsLine = pFndLn->GetLine();
+ SwTableBox* pBox = pFndLn->GetBoxes()[ bBehind ?
+ pFndLn->GetBoxes().size()-1 : 0 ]->GetBox();
+ rCpyPara.nInsPos = pFndLn->GetLine()->GetBoxPos( pBox );
+ if( bBehind )
+ ++rCpyPara.nInsPos;
+
+ for( sal_uInt16 n = 0; n < nCpyCnt; ++n )
+ {
+ if( n + 1 == nCpyCnt && bBehind )
+ rCpyPara.nDelBorderFlag = 9;
+ else
+ rCpyPara.nDelBorderFlag = 8;
+ for (auto const& it : pFndLn->GetBoxes())
+ {
+ lcl_CopyCol(*it, &rCpyPara);
+ }
+ }
+ }
+}
+
+static SwRowFrame* GetRowFrame( SwTableLine& rLine )
+{
+ SwIterator<SwRowFrame,SwFormat> aIter( *rLine.GetFrameFormat() );
+ for( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ if( pFrame->GetTabLine() == &rLine )
+ return pFrame;
+ return nullptr;
+}
+
+bool SwTable::InsertCol( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box List" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ bool bRes = true;
+ if( IsNewModel() )
+ bRes = NewInsertCol( rDoc, rBoxes, nCnt, bBehind );
+ else
+ {
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ // Find Lines for the layout update
+ aFndBox.SetTableLines( *this );
+ aFndBox.DelFrames( *this );
+
+ // TL_CHART2: nothing to be done since chart2 currently does not want to
+ // get notified about new rows/cols.
+
+ CpyTabFrames aTabFrameArr;
+ CpyPara aCpyPara( pTableNd, nCnt, aTabFrameArr );
+
+ for (auto & rpLine : aFndBox.GetLines())
+ {
+ lcl_InsCol( rpLine.get(), aCpyPara, nCnt, bBehind );
+ }
+
+ // clean up this Line's structure once again, generally all of them
+ GCLines();
+
+ // Update Layout
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH;
+ CHECKTABLELAYOUT;
+ bRes = true;
+ }
+
+ SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD && nCnt)
+ pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
+ rDoc.UpdateCharts( GetFrameFormat()->GetName() );
+
+ if (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ return bRes;
+}
+
+bool SwTable::InsertRow_( SwDoc* pDoc, const SwSelBoxes& rBoxes,
+ sal_uInt16 nCnt, bool bBehind )
+{
+ OSL_ENSURE( pDoc && !rBoxes.empty() && nCnt, "No valid Box List" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ FndBox_* pFndBox = &aFndBox;
+ {
+ FndLine_* pFndLine;
+ while( 1 == pFndBox->GetLines().size() )
+ {
+ pFndLine = pFndBox->GetLines()[0].get();
+ if( 1 != pFndLine->GetBoxes().size() )
+ break;
+ // Don't go down too far! One Line with Box needs to remain!
+ FndBox_ *const pTmpBox = pFndLine->GetBoxes().front().get();
+ if( !pTmpBox->GetLines().empty() )
+ pFndBox = pTmpBox;
+ else
+ break;
+ }
+ }
+
+ // Find Lines for the layout update
+ const bool bLayout = !IsNewModel() &&
+ nullptr != SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First();
+
+ if ( bLayout )
+ {
+ aFndBox.SetTableLines( *this );
+ if( pFndBox != &aFndBox )
+ aFndBox.DelFrames( *this );
+ // TL_CHART2: nothing to be done since chart2 currently does not want to
+ // get notified about new rows/cols.
+ }
+
+ CpyTabFrames aTabFrameArr;
+ CpyPara aCpyPara( pTableNd, 0, aTabFrameArr );
+
+ SwTableLine* pLine = pFndBox->GetLines()[ bBehind ?
+ pFndBox->GetLines().size()-1 : 0 ]->GetLine();
+ if( &aFndBox == pFndBox )
+ aCpyPara.nInsPos = GetTabLines().GetPos( pLine );
+ else
+ {
+ aCpyPara.pInsBox = pFndBox->GetBox();
+ aCpyPara.nInsPos = pFndBox->GetBox()->GetTabLines().GetPos( pLine );
+ }
+
+ if( bBehind )
+ {
+ ++aCpyPara.nInsPos;
+ aCpyPara.nDelBorderFlag = 1;
+ }
+ else
+ aCpyPara.nDelBorderFlag = 2;
+
+ for( sal_uInt16 nCpyCnt = 0; nCpyCnt < nCnt; ++nCpyCnt )
+ {
+ if( bBehind )
+ aCpyPara.nDelBorderFlag = 1;
+ for (auto & rpFndLine : pFndBox->GetLines())
+ {
+ SwTableLine* pNewTableLine = lcl_CopyRow( *rpFndLine, &aCpyPara );
+
+ // tracked insertion of empty table line
+ if ( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SvxPrintItem aSetTracking(RES_PRINT, false);
+ SwPosition aPos(*pNewTableLine->GetTabBoxes()[0]->GetSttNd());
+ SwCursor aCursor( aPos, nullptr );
+ SwNodeIndex aInsPos(*pNewTableLine->GetTabBoxes()[0]->GetSttNd(), 1 );
+ SwPaM aPaM(aInsPos);
+ pDoc->getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ pDoc->SetRowNotTracked( aCursor, aSetTracking, /*bAll=*/false, /*bIns=*/true );
+ }
+ }
+ }
+
+ // clean up this Line's structure once again, generally all of them
+ if( !pDoc->IsInReading() )
+ GCLines();
+
+ // Update Layout
+ if ( bLayout )
+ {
+ if( pFndBox != &aFndBox )
+ aFndBox.MakeFrames( *this );
+ else
+ aFndBox.MakeNewFrames( *this, nCnt, bBehind );
+ }
+
+ CHECKBOXWIDTH;
+ CHECKTABLELAYOUT;
+
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD && nCnt)
+ pPCD->AddRowCols( *this, rBoxes, nCnt, bBehind );
+ pDoc->UpdateCharts( GetFrameFormat()->GetName() );
+
+ if (SwFEShell* pFEShell = pDoc->GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTableNd);
+
+ return true;
+}
+
+static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset,
+ bool bFirst, SwShareBoxFormats& rShareFormats );
+
+static void lcl_LastBoxSetWidthLine( SwTableLines &rLines, const tools::Long nOffset,
+ bool bFirst, SwShareBoxFormats& rShareFormats )
+{
+ for ( auto pLine : rLines )
+ ::lcl_LastBoxSetWidth( pLine->GetTabBoxes(), nOffset, bFirst, rShareFormats );
+}
+
+static void lcl_LastBoxSetWidth( SwTableBoxes &rBoxes, const tools::Long nOffset,
+ bool bFirst, SwShareBoxFormats& rShareFormats )
+{
+ SwTableBox& rBox = *(bFirst ? rBoxes.front() : rBoxes.back());
+ if( !rBox.GetSttNd() )
+ ::lcl_LastBoxSetWidthLine( rBox.GetTabLines(), nOffset,
+ bFirst, rShareFormats );
+
+ // Adapt the Box
+ const SwFrameFormat *pBoxFormat = rBox.GetFrameFormat();
+ SwFormatFrameSize aNew( pBoxFormat->GetFrameSize() );
+ aNew.SetWidth( aNew.GetWidth() + nOffset );
+ SwFrameFormat *pFormat = rShareFormats.GetFormat( *pBoxFormat, aNew );
+ if( pFormat )
+ rBox.ChgFrameFormat( static_cast<SwTableBoxFormat*>(pFormat) );
+ else
+ {
+ pFormat = rBox.ClaimFrameFormat();
+
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aNew );
+ pFormat->UnlockModify();
+
+ rShareFormats.AddFormat( *pBoxFormat, *pFormat );
+ }
+}
+
+void DeleteBox_( SwTable& rTable, SwTableBox* pBox, SwUndo* pUndo,
+ bool bCalcNewSize, const bool bCorrBorder,
+ SwShareBoxFormats* pShareFormats )
+{
+ do {
+ SwTwips nBoxSz = bCalcNewSize ?
+ pBox->GetFrameFormat()->GetFrameSize().GetWidth() : 0;
+ SwTableLine* pLine = pBox->GetUpper();
+ SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
+ sal_uInt16 nDelPos = pLine->GetBoxPos( pBox );
+ SwTableBox* pUpperBox = pBox->GetUpper()->GetUpper();
+
+ // Special treatment for the border:
+ if( bCorrBorder && 1 < rTableBoxes.size() )
+ {
+ const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
+
+ if( rBoxItem.GetLeft() || rBoxItem.GetRight() )
+ {
+ bool bChgd = false;
+
+ // JP 02.04.97: 1st part for Bug 36271
+ // First the left/right edges
+ if( nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) )
+ {
+ SwTableBox* pNxtBox = rTableBoxes[ nDelPos + 1 ];
+ const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox();
+
+ SwTableBox* pPrvBox = nDelPos ? rTableBoxes[ nDelPos - 1 ] : nullptr;
+
+ if( pNxtBox->GetSttNd() && !rNxtBoxItem.GetLeft() &&
+ ( !pPrvBox || !pPrvBox->GetFrameFormat()->GetBox().GetRight()) )
+ {
+ SvxBoxItem aTmp( rNxtBoxItem );
+ aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft()
+ : rBoxItem.GetRight(),
+ SvxBoxItemLine::LEFT );
+ if( pShareFormats )
+ pShareFormats->SetAttr( *pNxtBox, aTmp );
+ else
+ pNxtBox->ClaimFrameFormat()->SetFormatAttr( aTmp );
+ bChgd = true;
+ }
+ }
+ if( !bChgd && nDelPos )
+ {
+ SwTableBox* pPrvBox = rTableBoxes[ nDelPos - 1 ];
+ const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox();
+
+ SwTableBox* pNxtBox = nDelPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size())
+ ? rTableBoxes[ nDelPos + 1 ] : nullptr;
+
+ if( pPrvBox->GetSttNd() && !rPrvBoxItem.GetRight() &&
+ ( !pNxtBox || !pNxtBox->GetFrameFormat()->GetBox().GetLeft()) )
+ {
+ SvxBoxItem aTmp( rPrvBoxItem );
+ aTmp.SetLine( rBoxItem.GetLeft() ? rBoxItem.GetLeft()
+ : rBoxItem.GetRight(),
+ SvxBoxItemLine::RIGHT );
+ if( pShareFormats )
+ pShareFormats->SetAttr( *pPrvBox, aTmp );
+ else
+ pPrvBox->ClaimFrameFormat()->SetFormatAttr( aTmp );
+ }
+ }
+ }
+ }
+
+ // Delete the Box first, then the Nodes!
+ SwStartNode* pSttNd = const_cast<SwStartNode*>(pBox->GetSttNd());
+ if( pShareFormats )
+ pShareFormats->RemoveFormat( *rTableBoxes[ nDelPos ]->GetFrameFormat() );
+
+ // Before deleting the 'Table Box' from memory - delete any redlines attached to it
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableCellRedline( rTable.GetFrameFormat()->GetDoc(), *(rTableBoxes[nDelPos]), true, RedlineType::Any );
+ delete rTableBoxes[nDelPos];
+ rTableBoxes.erase( rTableBoxes.begin() + nDelPos );
+
+ if( pSttNd )
+ {
+ // Has the UndoObject been prepared to save the Section?
+ if( pUndo && pUndo->IsDelBox() )
+ static_cast<SwUndoTableNdsChg*>(pUndo)->SaveSection( pSttNd );
+ else
+ pSttNd->GetDoc().getIDocumentContentOperations().DeleteSection( pSttNd );
+ }
+
+ // Also delete the Line?
+ if( !rTableBoxes.empty() )
+ {
+ // Then adapt the Frame-SSize
+ bool bLastBox = nDelPos == rTableBoxes.size();
+ if( bLastBox )
+ --nDelPos;
+ pBox = rTableBoxes[nDelPos];
+ if( bCalcNewSize )
+ {
+ SwFormatFrameSize aNew( pBox->GetFrameFormat()->GetFrameSize() );
+ aNew.SetWidth( aNew.GetWidth() + nBoxSz );
+ if( pShareFormats )
+ pShareFormats->SetSize( *pBox, aNew );
+ else
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+
+ if( !pBox->GetSttNd() )
+ {
+ // We need to this recursively in all Lines in all Cells!
+ SwShareBoxFormats aShareFormats;
+ ::lcl_LastBoxSetWidthLine( pBox->GetTabLines(), nBoxSz,
+ !bLastBox,
+ pShareFormats ? *pShareFormats
+ : aShareFormats );
+ }
+ }
+ break; // Stop deleting
+ }
+ // Delete the Line from the Table/Box
+ if( !pUpperBox )
+ {
+ // Also delete the Line from the Table
+ nDelPos = rTable.GetTabLines().GetPos( pLine );
+ if( pShareFormats )
+ pShareFormats->RemoveFormat( *rTable.GetTabLines()[ nDelPos ]->GetFrameFormat() );
+
+ SwTableLine* pTabLineToDelete = rTable.GetTabLines()[ nDelPos ];
+ // Before deleting the 'Table Line' from memory - delete any redlines attached to it
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any );
+ delete pTabLineToDelete;
+ rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nDelPos );
+ break; // we cannot delete more
+ }
+
+ // finally also delete the Line
+ pBox = pUpperBox;
+ nDelPos = pBox->GetTabLines().GetPos( pLine );
+ if( pShareFormats )
+ pShareFormats->RemoveFormat( *pBox->GetTabLines()[ nDelPos ]->GetFrameFormat() );
+
+ SwTableLine* pTabLineToDelete = pBox->GetTabLines()[ nDelPos ];
+ // Before deleting the 'Table Line' from memory - delete any redlines attached to it
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteTableRowRedline( rTable.GetFrameFormat()->GetDoc(), *pTabLineToDelete, true, RedlineType::Any );
+ delete pTabLineToDelete;
+ pBox->GetTabLines().erase( pBox->GetTabLines().begin() + nDelPos );
+ } while( pBox->GetTabLines().empty() );
+}
+
+static SwTableBox*
+lcl_FndNxtPrvDelBox( const SwTableLines& rTableLns,
+ SwTwips nBoxStt, SwTwips nBoxWidth,
+ sal_uInt16 nLinePos, bool bNxt,
+ SwSelBoxes* pAllDelBoxes, size_t *const pCurPos)
+{
+ SwTableBox* pFndBox = nullptr;
+ do {
+ if( bNxt )
+ ++nLinePos;
+ else
+ --nLinePos;
+ SwTableLine* pLine = rTableLns[ nLinePos ];
+ SwTwips nFndBoxWidth = 0;
+ SwTwips nFndWidth = nBoxStt + nBoxWidth;
+
+ pFndBox = pLine->GetTabBoxes()[ 0 ];
+ for( auto pBox : pLine->GetTabBoxes() )
+ {
+ if ( nFndWidth <= 0 )
+ {
+ break;
+ }
+ pFndBox = pBox;
+ nFndBoxWidth = pFndBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ nFndWidth -= nFndBoxWidth;
+ }
+
+ // Find the first ContentBox
+ while( !pFndBox->GetSttNd() )
+ {
+ const SwTableLines& rLowLns = pFndBox->GetTabLines();
+ if( bNxt )
+ pFndBox = rLowLns.front()->GetTabBoxes().front();
+ else
+ pFndBox = rLowLns.back()->GetTabBoxes().front();
+ }
+
+ if( std::abs( nFndWidth ) > COLFUZZY ||
+ std::abs( nBoxWidth - nFndBoxWidth ) > COLFUZZY )
+ pFndBox = nullptr;
+ else if( pAllDelBoxes )
+ {
+ // If the predecessor will also be deleted, there's nothing to do
+ SwSelBoxes::const_iterator aFndIt = pAllDelBoxes->find( pFndBox);
+ if( aFndIt == pAllDelBoxes->end() )
+ break;
+ size_t const nFndPos = aFndIt - pAllDelBoxes->begin() ;
+
+ // else, we keep on searching.
+ // We do not need to recheck the Box, however
+ pFndBox = nullptr;
+ if( nFndPos <= *pCurPos )
+ --*pCurPos;
+ pAllDelBoxes->erase( pAllDelBoxes->begin() + nFndPos );
+ }
+ } while( bNxt ? ( nLinePos + 1 < o3tl::narrowing<sal_uInt16>(rTableLns.size()) ) : nLinePos != 0 );
+ return pFndBox;
+}
+
+static void
+lcl_SaveUpperLowerBorder( SwTable& rTable, const SwTableBox& rBox,
+ SwShareBoxFormats& rShareFormats,
+ SwSelBoxes* pAllDelBoxes = nullptr,
+ size_t *const pCurPos = nullptr )
+{
+//JP 16.04.97: 2. part for Bug 36271
+ const SwTableLine* pLine = rBox.GetUpper();
+ const SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
+ const SwTableBox* pUpperBox = &rBox;
+ sal_uInt16 nDelPos = pLine->GetBoxPos( pUpperBox );
+ pUpperBox = rBox.GetUpper()->GetUpper();
+ const SvxBoxItem& rBoxItem = rBox.GetFrameFormat()->GetBox();
+
+ // then the top/bottom edges
+ if( !rBoxItem.GetTop() && !rBoxItem.GetBottom() )
+ return;
+
+ bool bChgd = false;
+ const SwTableLines* pTableLns;
+ if( pUpperBox )
+ pTableLns = &pUpperBox->GetTabLines();
+ else
+ pTableLns = &rTable.GetTabLines();
+
+ sal_uInt16 nLnPos = pTableLns->GetPos( pLine );
+
+ // Calculate the attribute position of the top-be-deleted Box and then
+ // search in the top/bottom Line of the respective counterparts.
+ SwTwips nBoxStt = 0;
+ for( sal_uInt16 n = 0; n < nDelPos; ++n )
+ nBoxStt += rTableBoxes[ n ]->GetFrameFormat()->GetFrameSize().GetWidth();
+ SwTwips nBoxWidth = rBox.GetFrameFormat()->GetFrameSize().GetWidth();
+
+ SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
+ if( nLnPos ) // Predecessor?
+ pPrvBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth,
+ nLnPos, false, pAllDelBoxes, pCurPos );
+
+ if( nLnPos + 1 < o3tl::narrowing<sal_uInt16>(pTableLns->size()) ) // Successor?
+ pNxtBox = ::lcl_FndNxtPrvDelBox( *pTableLns, nBoxStt, nBoxWidth,
+ nLnPos, true, pAllDelBoxes, pCurPos );
+
+ if( pNxtBox && pNxtBox->GetSttNd() )
+ {
+ const SvxBoxItem& rNxtBoxItem = pNxtBox->GetFrameFormat()->GetBox();
+ if( !rNxtBoxItem.GetTop() && ( !pPrvBox ||
+ !pPrvBox->GetFrameFormat()->GetBox().GetBottom()) )
+ {
+ SvxBoxItem aTmp( rNxtBoxItem );
+ aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop()
+ : rBoxItem.GetBottom(),
+ SvxBoxItemLine::TOP );
+ rShareFormats.SetAttr( *pNxtBox, aTmp );
+ bChgd = true;
+ }
+ }
+ if( !(!bChgd && pPrvBox && pPrvBox->GetSttNd()) )
+ return;
+
+ const SvxBoxItem& rPrvBoxItem = pPrvBox->GetFrameFormat()->GetBox();
+ if( !rPrvBoxItem.GetTop() && ( !pNxtBox ||
+ !pNxtBox->GetFrameFormat()->GetBox().GetTop()) )
+ {
+ SvxBoxItem aTmp( rPrvBoxItem );
+ aTmp.SetLine( rBoxItem.GetTop() ? rBoxItem.GetTop()
+ : rBoxItem.GetBottom(),
+ SvxBoxItemLine::BOTTOM );
+ rShareFormats.SetAttr( *pPrvBox, aTmp );
+ }
+
+}
+
+bool SwTable::DeleteSel(
+ SwDoc* pDoc
+ ,
+ const SwSelBoxes& rBoxes,
+ const SwSelBoxes* pMerged, SwUndo* pUndo,
+ const bool bDelMakeFrames, const bool bCorrBorder )
+{
+ OSL_ENSURE( pDoc, "No doc?" );
+ SwTableNode* pTableNd = nullptr;
+ if( !rBoxes.empty() )
+ {
+ pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+ }
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ if ( bDelMakeFrames )
+ {
+ if( pMerged && !pMerged->empty() )
+ aFndBox.SetTableLines( *pMerged, *this );
+ else if( !rBoxes.empty() )
+ aFndBox.SetTableLines( rBoxes, *this );
+ aFndBox.DelFrames( *this );
+ }
+
+ SwShareBoxFormats aShareFormats;
+
+ // First switch the Border, then delete
+ if( bCorrBorder )
+ {
+ SwSelBoxes aBoxes( rBoxes );
+ for (size_t n = 0; n < aBoxes.size(); ++n)
+ {
+ ::lcl_SaveUpperLowerBorder( *this, *rBoxes[ n ], aShareFormats,
+ &aBoxes, &n );
+ }
+ }
+
+ PrepareDelBoxes( rBoxes );
+
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ // Delete boxes from last to first
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ size_t const nIdx = rBoxes.size() - 1 - n;
+
+ // First adapt the data-sequence for chart if necessary
+ // (needed to move the implementation cursor properly to its new
+ // position which can't be done properly if the cell is already gone)
+ if (pPCD && pTableNd)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *rBoxes[nIdx] );
+
+ // ... then delete the boxes
+ DeleteBox_( *this, rBoxes[nIdx], pUndo, true, bCorrBorder, &aShareFormats );
+ }
+
+ // then clean up the structure of all Lines
+ GCLines();
+
+ if( bDelMakeFrames && aFndBox.AreLinesToRestore( *this ) )
+ aFndBox.MakeFrames( *this );
+
+ // TL_CHART2: now inform chart that sth has changed
+ pDoc->UpdateCharts( GetFrameFormat()->GetName() );
+
+ CHECKTABLELAYOUT;
+ CHECK_TABLE( *this );
+
+ return true;
+}
+
+bool SwTable::OldSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
+ bool bSameHeight )
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // TL_CHART2: splitting/merging of a number of cells or rows will usually make
+ // the table too complex to be handled with chart.
+ // Thus we tell the charts to use their own data provider and forget about this table
+ rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ // If the rows should get the same (min) height, we first have
+ // to store the old row heights before deleting the frames
+ std::unique_ptr<tools::Long[]> pRowHeights;
+ if ( bSameHeight )
+ {
+ pRowHeights.reset(new tools::Long[ rBoxes.size() ]);
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ SwTableBox* pSelBox = rBoxes[n];
+ const SwRowFrame* pRow = GetRowFrame( *pSelBox->GetUpper() );
+ OSL_ENSURE( pRow, "Where is the SwTableLine's Frame?" );
+ SwRectFnSet aRectFnSet(pRow);
+ pRowHeights[ n ] = aRectFnSet.GetHeight(pRow->getFrameArea());
+ }
+ }
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rBoxes, *this );
+ aFndBox.DelFrames( *this );
+
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ SwTableBox* pSelBox = rBoxes[n];
+ OSL_ENSURE( pSelBox, "Box is not within the Table" );
+
+ // Insert nCnt new Lines into the Box
+ SwTableLine* pInsLine = pSelBox->GetUpper();
+ SwTableBoxFormat* pFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat());
+
+ // Respect the Line's height, reset if needed
+ SwFormatFrameSize aFSz( pInsLine->GetFrameFormat()->GetFrameSize() );
+ if ( bSameHeight && SwFrameSize::Variable == aFSz.GetHeightSizeType() )
+ aFSz.SetHeightSizeType( SwFrameSize::Minimum );
+
+ bool bChgLineSz = 0 != aFSz.GetHeight() || bSameHeight;
+ if ( bChgLineSz )
+ aFSz.SetHeight( ( bSameHeight ? pRowHeights[ n ] : aFSz.GetHeight() ) /
+ (nCnt + 1) );
+
+ SwTableBox* pNewBox = new SwTableBox( pFrameFormat, nCnt, pInsLine );
+ sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox );
+ pInsLine->GetTabBoxes()[nBoxPos] = pNewBox; // overwrite old one
+
+ // Delete background/border attribute
+ SwTableBox* pLastBox = pSelBox; // To distribute the TextNodes!
+ // If Areas are contained in the Box, it stays as is
+ // !! If this is changed we need to adapt the Undo, too !!!
+ bool bMoveNodes = true;
+ {
+ SwNodeOffset nSttNd = pLastBox->GetSttIdx() + 1,
+ nEndNd = pLastBox->GetSttNd()->EndOfSectionIndex();
+ while( nSttNd < nEndNd )
+ if( !rDoc.GetNodes()[ nSttNd++ ]->IsTextNode() )
+ {
+ bMoveNodes = false;
+ break;
+ }
+ }
+
+ SwTableBoxFormat* pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat());
+ bool bChkBorder = nullptr != pCpyBoxFrameFormat->GetBox().GetTop();
+ if( bChkBorder )
+ pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->ClaimFrameFormat());
+
+ for( sal_uInt16 i = 0; i <= nCnt; ++i )
+ {
+ // Create a new Line in the new Box
+ SwTableLine* pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pInsLine->GetFrameFormat()), 1, pNewBox );
+ if( bChgLineSz )
+ {
+ pNewLine->ClaimFrameFormat()->SetFormatAttr( aFSz );
+ }
+
+ pNewBox->GetTabLines().insert( pNewBox->GetTabLines().begin() + i, pNewLine );
+ // then a new Box in the Line
+ if( !i ) // hang up the original Box
+ {
+ pSelBox->SetUpper( pNewLine );
+ pNewLine->GetTabBoxes().insert( pNewLine->GetTabBoxes().begin(), pSelBox );
+ }
+ else
+ {
+ ::InsTableBox( rDoc, pTableNd, pNewLine, pCpyBoxFrameFormat,
+ pLastBox, 0 );
+
+ if( bChkBorder )
+ {
+ pCpyBoxFrameFormat = static_cast<SwTableBoxFormat*>(pNewLine->GetTabBoxes()[ 0 ]->ClaimFrameFormat());
+ SvxBoxItem aTmp( pCpyBoxFrameFormat->GetBox() );
+ aTmp.SetLine( nullptr, SvxBoxItemLine::TOP );
+ pCpyBoxFrameFormat->SetFormatAttr( aTmp );
+ bChkBorder = false;
+ }
+
+ if( bMoveNodes )
+ {
+ const SwNode* pEndNd = pLastBox->GetSttNd()->EndOfSectionNode();
+ if( pLastBox->GetSttIdx()+SwNodeOffset(2) != pEndNd->GetIndex() )
+ {
+ // Move TextNodes
+ SwNodeRange aRg( *pLastBox->GetSttNd(), SwNodeOffset(+2), *pEndNd );
+ pLastBox = pNewLine->GetTabBoxes()[0]; // reset
+ SwNodeIndex aInsPos( *pLastBox->GetSttNd(), 1 );
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aInsPos, false);
+ rDoc.GetNodes().Delete( aInsPos ); // delete the empty one
+ }
+ }
+ }
+ }
+ // In Boxes with Lines, we can only have Size/Fillorder
+ pFrameFormat = static_cast<SwTableBoxFormat*>(pNewBox->ClaimFrameFormat());
+ pFrameFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 );
+ pFrameFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 );
+ }
+
+ pRowHeights.reset();
+
+ GCLines();
+
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+ return true;
+}
+
+bool SwTable::SplitCol(SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt)
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid values" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // TL_CHART2: splitting/merging of a number of cells or rows will usually make
+ // the table too complex to be handled with chart.
+ // Thus we tell the charts to use their own data provider and forget about this table
+ rDoc.getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+ SwSelBoxes aSelBoxes(rBoxes);
+ ExpandSelection( aSelBoxes );
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( aSelBoxes, *this );
+ aFndBox.DelFrames( *this );
+
+ CpyTabFrames aFrameArr;
+ std::vector<SwTableBoxFormat*> aLastBoxArr;
+ for (size_t n = 0; n < aSelBoxes.size(); ++n)
+ {
+ SwTableBox* pSelBox = aSelBoxes[n];
+ OSL_ENSURE( pSelBox, "Box is not in the table" );
+
+ // We don't want to split small table cells into very very small cells
+ if( pSelBox->GetFrameFormat()->GetFrameSize().GetWidth()/( nCnt + 1 ) < 10 )
+ continue;
+
+ // Then split the nCnt Box up into nCnt Boxes
+ SwTableLine* pInsLine = pSelBox->GetUpper();
+ sal_uInt16 nBoxPos = pInsLine->GetBoxPos( pSelBox );
+
+ // Find the Frame Format in the Frame Format Array
+ SwTableBoxFormat* pLastBoxFormat;
+ CpyTabFrame aFindFrame( static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat()) );
+ CpyTabFrames::const_iterator itFind = aFrameArr.lower_bound( aFindFrame );
+ const size_t nFndPos = itFind - aFrameArr.begin();
+ if( itFind == aFrameArr.end() || !(*itFind == aFindFrame) )
+ {
+ // Change the FrameFormat
+ aFindFrame.pNewFrameFormat = static_cast<SwTableBoxFormat*>(pSelBox->ClaimFrameFormat());
+ SwTwips nBoxSz = aFindFrame.pNewFrameFormat->GetFrameSize().GetWidth();
+ SwTwips nNewBoxSz = nBoxSz / ( nCnt + 1 );
+ aFindFrame.pNewFrameFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ nNewBoxSz, 0 ) );
+ aFrameArr.insert( aFindFrame );
+
+ pLastBoxFormat = aFindFrame.pNewFrameFormat;
+ if( nBoxSz != ( nNewBoxSz * (nCnt + 1)))
+ {
+ // We have a remainder, so we need to define an own Format
+ // for the last Box.
+ pLastBoxFormat = new SwTableBoxFormat( *aFindFrame.pNewFrameFormat );
+ pLastBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ nBoxSz - ( nNewBoxSz * nCnt ), 0 ) );
+ }
+ aLastBoxArr.insert( aLastBoxArr.begin() + nFndPos, pLastBoxFormat );
+ }
+ else
+ {
+ aFindFrame = aFrameArr[ nFndPos ];
+ pSelBox->ChgFrameFormat( aFindFrame.pNewFrameFormat );
+ pLastBoxFormat = aLastBoxArr[ nFndPos ];
+ }
+
+ // Insert the Boxes at the Position
+ for( sal_uInt16 i = 1; i < nCnt; ++i )
+ ::InsTableBox( rDoc, pTableNd, pInsLine, aFindFrame.pNewFrameFormat,
+ pSelBox, nBoxPos + i ); // insert after
+
+ ::InsTableBox( rDoc, pTableNd, pInsLine, pLastBoxFormat,
+ pSelBox, nBoxPos + nCnt ); // insert after
+
+ // Special treatment for the Border:
+ const SvxBoxItem& aSelBoxItem = aFindFrame.pNewFrameFormat->GetBox();
+ if( aSelBoxItem.GetRight() )
+ {
+ pInsLine->GetTabBoxes()[ nBoxPos + nCnt ]->ClaimFrameFormat();
+
+ SvxBoxItem aTmp( aSelBoxItem );
+ aTmp.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ aFindFrame.pNewFrameFormat->SetFormatAttr( aTmp );
+
+ // Remove the Format from the "cache"
+ for( auto i = aFrameArr.size(); i; )
+ {
+ const CpyTabFrame& rCTF = aFrameArr[ --i ];
+ if( rCTF.pNewFrameFormat == aFindFrame.pNewFrameFormat ||
+ rCTF.pFrameFormat == aFindFrame.pNewFrameFormat )
+ {
+ aFrameArr.erase( aFrameArr.begin() + i );
+ aLastBoxArr.erase( aLastBoxArr.begin() + i );
+ }
+ }
+ }
+ }
+
+ // Update Layout
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+ return true;
+}
+
+/*
+ * >> MERGE <<
+ * Algorithm:
+ * If we only have one Line in the FndBox_, take this Line and test
+ * the Box count:
+ * If we have more than one Box, we merge on Box level, meaning
+ * the new Box will be as wide as the old ones.
+ * All Lines that are above/under the Area, are inserted into
+ * the Box as Line + Box.
+ * All Lines that come before/after the Area, are inserted into
+ * the Boxes Left/Right.
+ *
+ * >> MERGE <<
+ */
+static void lcl_CpyLines( sal_uInt16 nStt, sal_uInt16 nEnd,
+ SwTableLines& rLines,
+ SwTableBox* pInsBox,
+ sal_uInt16 nPos = USHRT_MAX )
+{
+ for( sal_uInt16 n = nStt; n < nEnd; ++n )
+ rLines[n]->SetUpper( pInsBox );
+ if( USHRT_MAX == nPos )
+ nPos = pInsBox->GetTabLines().size();
+ pInsBox->GetTabLines().insert( pInsBox->GetTabLines().begin() + nPos,
+ rLines.begin() + nStt, rLines.begin() + nEnd );
+ rLines.erase( rLines.begin() + nStt, rLines.begin() + nEnd );
+}
+
+static void lcl_CpyBoxes( sal_uInt16 nStt, sal_uInt16 nEnd,
+ SwTableBoxes& rBoxes,
+ SwTableLine* pInsLine )
+{
+ for( sal_uInt16 n = nStt; n < nEnd; ++n )
+ rBoxes[n]->SetUpper( pInsLine );
+ sal_uInt16 nPos = pInsLine->GetTabBoxes().size();
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + nPos,
+ rBoxes.begin() + nStt, rBoxes.begin() + nEnd );
+ rBoxes.erase( rBoxes.begin() + nStt, rBoxes.begin() + nEnd );
+}
+
+static void lcl_CalcWidth( SwTableBox* pBox )
+{
+ // Assertion: Every Line in the Box is as large
+ SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
+ OSL_ENSURE( pBox->GetTabLines().size(), "Box does not have any Lines" );
+
+ SwTableLine* pLine = pBox->GetTabLines()[0];
+ OSL_ENSURE( pLine, "Box is not within a Line" );
+
+ tools::Long nWidth = 0;
+ for( auto pTabBox : pLine->GetTabBoxes() )
+ nWidth += pTabBox->GetFrameFormat()->GetFrameSize().GetWidth();
+
+ pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 ));
+
+ // Boxes with Lines can only have Size/Fillorder
+ pFormat->ResetFormatAttr( RES_LR_SPACE, RES_FRMATR_END - 1 );
+ pFormat->ResetFormatAttr( RES_BOXATR_BEGIN, RES_BOXATR_END - 1 );
+}
+
+namespace {
+
+struct InsULPara
+{
+ SwTableNode* pTableNd;
+ SwTableLine* pInsLine;
+ SwTableBox* pInsBox;
+ bool bUL_LR : 1; // Upper-Lower(true) or Left-Right(false) ?
+ bool bUL : 1; // Upper-Left(true) or Lower-Right(false) ?
+
+ SwTableBox* pLeftBox;
+
+ InsULPara( SwTableNode* pTNd,
+ SwTableBox* pLeft,
+ SwTableLine* pLine )
+ : pTableNd( pTNd ), pInsLine( pLine ), pInsBox( nullptr ),
+ pLeftBox( pLeft )
+ { bUL_LR = true; bUL = true; }
+
+ void SetLeft( SwTableBox* pBox )
+ { bUL_LR = false; bUL = true; if( pBox ) pInsBox = pBox; }
+ void SetRight( SwTableBox* pBox )
+ { bUL_LR = false; bUL = false; if( pBox ) pInsBox = pBox; }
+ void SetLower( SwTableLine* pLine )
+ { bUL_LR = true; bUL = false; if( pLine ) pInsLine = pLine; }
+};
+
+}
+
+static void lcl_Merge_MoveLine(FndLine_ & rFndLine, InsULPara *const pULPara);
+
+static void lcl_Merge_MoveBox(FndBox_ & rFndBox, InsULPara *const pULPara)
+{
+ SwTableBoxes* pBoxes;
+
+ sal_uInt16 nStt = 0, nEnd = rFndBox.GetLines().size();
+ sal_uInt16 nInsPos = USHRT_MAX;
+ if( !pULPara->bUL_LR ) // Left/Right
+ {
+ sal_uInt16 nPos;
+ SwTableBox* pFndTableBox = rFndBox.GetBox();
+ pBoxes = &pFndTableBox->GetUpper()->GetTabBoxes();
+ if( pULPara->bUL ) // Left ?
+ {
+ // if there are Boxes before it, move them
+ nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox );
+ if( 0 != nPos )
+ lcl_CpyBoxes( 0, nPos, *pBoxes, pULPara->pInsLine );
+ }
+ else // Right
+ {
+ // if there are Boxes behind it, move them
+ nPos = pFndTableBox->GetUpper()->GetBoxPos( pFndTableBox );
+ if( nPos +1 < o3tl::narrowing<sal_uInt16>(pBoxes->size()) )
+ {
+ nInsPos = pULPara->pInsLine->GetTabBoxes().size();
+ lcl_CpyBoxes( nPos+1, pBoxes->size(),
+ *pBoxes, pULPara->pInsLine );
+ }
+ }
+ }
+ // Upper/Lower and still deeper?
+ else if (!rFndBox.GetLines().empty())
+ {
+ // Only search the Line from which we need to move
+ nStt = pULPara->bUL ? 0 : rFndBox.GetLines().size()-1;
+ nEnd = nStt+1;
+ }
+
+ pBoxes = &pULPara->pInsLine->GetTabBoxes();
+
+ // Is there still a level to step down to?
+ if (rFndBox.GetBox()->GetTabLines().empty())
+ return;
+
+ SwTableBox* pBox = new SwTableBox(
+ static_cast<SwTableBoxFormat*>(rFndBox.GetBox()->GetFrameFormat()),
+ 0, pULPara->pInsLine );
+ InsULPara aPara( *pULPara );
+ aPara.pInsBox = pBox;
+ for (FndLines_t::iterator it = rFndBox.GetLines().begin() + nStt;
+ it != rFndBox.GetLines().begin() + nEnd; ++it )
+ {
+ lcl_Merge_MoveLine(**it, &aPara);
+ }
+ if( !pBox->GetTabLines().empty() )
+ {
+ if( USHRT_MAX == nInsPos )
+ nInsPos = pBoxes->size();
+ pBoxes->insert( pBoxes->begin() + nInsPos, pBox );
+ lcl_CalcWidth( pBox ); // calculate the Box's width
+ }
+ else
+ delete pBox;
+}
+
+static void lcl_Merge_MoveLine(FndLine_& rFndLine, InsULPara *const pULPara)
+{
+ SwTableLines* pLines;
+
+ sal_uInt16 nStt = 0, nEnd = rFndLine.GetBoxes().size();
+ sal_uInt16 nInsPos = USHRT_MAX;
+ if( pULPara->bUL_LR ) // UpperLower ?
+ {
+ sal_uInt16 nPos;
+ SwTableLine* pFndLn = rFndLine.GetLine();
+ pLines = pFndLn->GetUpper() ?
+ &pFndLn->GetUpper()->GetTabLines() :
+ &pULPara->pTableNd->GetTable().GetTabLines();
+
+ SwTableBox* pLBx = rFndLine.GetBoxes().front()->GetBox();
+ SwTableBox* pRBx = rFndLine.GetBoxes().back()->GetBox();
+ sal_uInt16 nLeft = pFndLn->GetBoxPos( pLBx );
+ sal_uInt16 nRight = pFndLn->GetBoxPos( pRBx );
+
+ if( !nLeft || nRight == pFndLn->GetTabBoxes().size() )
+ {
+ if( pULPara->bUL ) // Upper ?
+ {
+ // If there are Lines before it, move them
+ nPos = pLines->GetPos( pFndLn );
+ if( 0 != nPos )
+ lcl_CpyLines( 0, nPos, *pLines, pULPara->pInsBox );
+ }
+ else
+ // If there are Lines after it, move them
+ if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) )
+ {
+ nInsPos = pULPara->pInsBox->GetTabLines().size();
+ lcl_CpyLines( nPos+1, pLines->size(), *pLines,
+ pULPara->pInsBox );
+ }
+ }
+ else
+ {
+ // There are still Boxes on the left side, so put the Left-
+ // and Merge-Box into one Box and Line, insert before/after
+ // a Line with a Box, into which the upper/lower Lines are
+ // inserted
+ SwTableLine* pInsLine = pULPara->pLeftBox->GetUpper();
+ SwTableBox* pLMBox = new SwTableBox(
+ static_cast<SwTableBoxFormat*>(pULPara->pLeftBox->GetFrameFormat()), 0, pInsLine );
+ SwTableLine* pLMLn = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pInsLine->GetFrameFormat()), 2, pLMBox );
+ pLMLn->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );
+
+ pLMBox->GetTabLines().insert( pLMBox->GetTabLines().begin(), pLMLn );
+
+ lcl_CpyBoxes( 0, 2, pInsLine->GetTabBoxes(), pLMLn );
+
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLMBox );
+
+ if( pULPara->bUL ) // Upper ?
+ {
+ // If there are Lines before it, move them
+ nPos = pLines->GetPos( pFndLn );
+ if( 0 != nPos )
+ lcl_CpyLines( 0, nPos, *pLines, pLMBox, 0 );
+ }
+ else
+ // If there are Lines after it, move them
+ if( (nPos = pLines->GetPos( pFndLn )) + 1 < o3tl::narrowing<sal_uInt16>(pLines->size()) )
+ lcl_CpyLines( nPos+1, pLines->size(), *pLines,
+ pLMBox );
+ lcl_CalcWidth( pLMBox ); // calculate the Box's width
+ }
+ }
+ // Left/Right
+ else
+ {
+ // Find only the Line from which we need to move
+ nStt = pULPara->bUL ? 0 : rFndLine.GetBoxes().size()-1;
+ nEnd = nStt+1;
+ }
+ pLines = &pULPara->pInsBox->GetTabLines();
+
+ SwTableLine* pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(rFndLine.GetLine()->GetFrameFormat()), 0, pULPara->pInsBox );
+ InsULPara aPara( *pULPara ); // copying
+ aPara.pInsLine = pNewLine;
+ FndBoxes_t & rLineBoxes = rFndLine.GetBoxes();
+ for (FndBoxes_t::iterator it = rLineBoxes.begin() + nStt;
+ it != rLineBoxes.begin() + nEnd; ++it)
+ {
+ lcl_Merge_MoveBox(**it, &aPara);
+ }
+
+ if( !pNewLine->GetTabBoxes().empty() )
+ {
+ if( USHRT_MAX == nInsPos )
+ nInsPos = pLines->size();
+ pLines->insert( pLines->begin() + nInsPos, pNewLine );
+ }
+ else
+ delete pNewLine;
+}
+
+static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox );
+
+bool SwTable::OldMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes,
+ SwTableBox* pMergeBox, SwUndoTableMerge* pUndo )
+{
+ OSL_ENSURE( !rBoxes.empty() && pMergeBox, "no valid values" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ // TL_CHART2: splitting/merging of a number of cells or rows will usually make
+ // the table too complex to be handled with chart.
+ // Thus we tell the charts to use their own data provider and forget about this table
+ pDoc->getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( this );
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ if( pUndo )
+ pUndo->SetSelBoxes( rBoxes );
+
+ // Find Lines for the Layout update
+ aFndBox.SetTableLines( *this );
+ aFndBox.DelFrames( *this );
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size() )
+ {
+ pFndBox = pFndBox->GetLines().front()->GetBoxes().front().get();
+ }
+
+ SwTableLine* pInsLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pFndBox->GetLines().front()->GetLine()->GetFrameFormat()), 0,
+ !pFndBox->GetUpper() ? nullptr : pFndBox->GetBox() );
+ pInsLine->ClaimFrameFormat()->ResetFormatAttr( RES_FRM_SIZE );
+
+ // Add the new Line
+ SwTableLines* pLines = pFndBox->GetUpper() ?
+ &pFndBox->GetBox()->GetTabLines() : &GetTabLines();
+
+ SwTableLine* pNewLine = pFndBox->GetLines().front()->GetLine();
+ sal_uInt16 nInsPos = pLines->GetPos( pNewLine );
+ pLines->insert( pLines->begin() + nInsPos, pInsLine );
+
+ SwTableBox* pLeftBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pMergeBox->GetFrameFormat()), 0, pInsLine );
+ SwTableBox* pRightBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pMergeBox->GetFrameFormat()), 0, pInsLine );
+ pMergeBox->SetUpper( pInsLine );
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin(), pLeftBox );
+ pLeftBox->ClaimFrameFormat();
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 1, pMergeBox);
+ pInsLine->GetTabBoxes().insert( pInsLine->GetTabBoxes().begin() + 2, pRightBox );
+ pRightBox->ClaimFrameFormat();
+
+ // This contains all Lines that are above the selected Area,
+ // thus they form a Upper/Lower Line
+ InsULPara aPara( pTableNd, pLeftBox, pInsLine );
+
+ // Move the overlapping upper/lower Lines of the selected Area
+ for (auto & it : pFndBox->GetLines().front()->GetBoxes())
+ {
+ lcl_Merge_MoveBox(*it, &aPara);
+ }
+ aPara.SetLower( pInsLine );
+ const auto nEnd = pFndBox->GetLines().size()-1;
+ for (auto & it : pFndBox->GetLines()[nEnd]->GetBoxes())
+ {
+ lcl_Merge_MoveBox(*it, &aPara);
+ }
+
+ // Move the Boxes extending into the selected Area from left/right
+ aPara.SetLeft( pLeftBox );
+ for (auto & rpFndLine : pFndBox->GetLines())
+ {
+ lcl_Merge_MoveLine( *rpFndLine, &aPara );
+ }
+
+ aPara.SetRight( pRightBox );
+ for (auto & rpFndLine : pFndBox->GetLines())
+ {
+ lcl_Merge_MoveLine( *rpFndLine, &aPara );
+ }
+
+ if( pLeftBox->GetTabLines().empty() )
+ DeleteBox_( *this, pLeftBox, nullptr, false, false );
+ else
+ {
+ lcl_CalcWidth( pLeftBox ); // calculate the Box's width
+ if( pUndo && pLeftBox->GetSttNd() )
+ pUndo->AddNewBox( pLeftBox->GetSttIdx() );
+ }
+ if( pRightBox->GetTabLines().empty() )
+ DeleteBox_( *this, pRightBox, nullptr, false, false );
+ else
+ {
+ lcl_CalcWidth( pRightBox ); // calculate the Box's width
+ if( pUndo && pRightBox->GetSttNd() )
+ pUndo->AddNewBox( pRightBox->GetSttIdx() );
+ }
+
+ DeleteSel( pDoc, rBoxes, nullptr, nullptr, false, false );
+
+ // Clean up this Line's structure once again, generally all of them
+ GCLines();
+
+ for( const auto& rpBox : GetTabLines()[0]->GetTabBoxes() )
+ lcl_BoxSetHeadCondColl(rpBox);
+
+ aFndBox.MakeFrames( *this );
+
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+
+ return true;
+}
+
+static void lcl_CheckRowSpan( SwTable &rTable )
+{
+ const tools::Long nLineCount = static_cast<tools::Long>(rTable.GetTabLines().size());
+ tools::Long nMaxSpan = nLineCount;
+ tools::Long nMinSpan = 1;
+ while( nMaxSpan )
+ {
+ SwTableLine* pLine = rTable.GetTabLines()[ nLineCount - nMaxSpan ];
+ for( auto pBox : pLine->GetTabBoxes() )
+ {
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan > nMaxSpan )
+ pBox->setRowSpan( nMaxSpan );
+ else if( nRowSpan < nMinSpan )
+ pBox->setRowSpan( nMinSpan > 0 ? nMaxSpan : nMinSpan );
+ }
+ --nMaxSpan;
+ nMinSpan = -nMaxSpan;
+ }
+}
+
+static sal_uInt16 lcl_GetBoxOffset( const FndBox_& rBox )
+{
+ // Find the first Box
+ const FndBox_* pFirstBox = &rBox;
+ while (!pFirstBox->GetLines().empty())
+ {
+ pFirstBox = pFirstBox->GetLines().front()->GetBoxes().front().get();
+ }
+
+ sal_uInt16 nRet = 0;
+ // Calculate the position relative to above via the Lines
+ const SwTableBox* pBox = pFirstBox->GetBox();
+ do {
+ const SwTableBoxes& rBoxes = pBox->GetUpper()->GetTabBoxes();
+ for( auto pCmp : rBoxes )
+ {
+ if (pBox==pCmp)
+ break;
+ nRet = nRet + o3tl::narrowing<sal_uInt16>(pCmp->GetFrameFormat()->GetFrameSize().GetWidth());
+ }
+ pBox = pBox->GetUpper()->GetUpper();
+ } while( pBox );
+ return nRet;
+}
+
+static sal_uInt16 lcl_GetLineWidth( const FndLine_& rLine )
+{
+ sal_uInt16 nRet = 0;
+ for( auto n = rLine.GetBoxes().size(); n; )
+ {
+ nRet = nRet + o3tl::narrowing<sal_uInt16>(rLine.GetBoxes()[--n]->GetBox()
+ ->GetFrameFormat()->GetFrameSize().GetWidth());
+ }
+ return nRet;
+}
+
+static void lcl_CalcNewWidths(const FndLines_t& rFndLines, CpyPara& rPara)
+{
+ rPara.pWidths.reset();
+ const size_t nLineCount = rFndLines.size();
+ if( nLineCount )
+ {
+ rPara.pWidths = std::make_shared< std::vector< std::vector< sal_uLong > > >
+ ( nLineCount );
+ // First we collect information about the left/right borders of all
+ // selected cells
+ for( size_t nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ];
+ const FndLine_ *pFndLine = rFndLines[ nLine ].get();
+ if( pFndLine && !pFndLine->GetBoxes().empty() )
+ {
+ const SwTableLine *pLine = pFndLine->GetLine();
+ if( pLine && !pLine->GetTabBoxes().empty() )
+ {
+ size_t nBoxCount = pLine->GetTabBoxes().size();
+ sal_uLong nPos = 0;
+ // The first selected box...
+ const SwTableBox *const pSel =
+ pFndLine->GetBoxes().front()->GetBox();
+ size_t nBox = 0;
+ // Sum up the width of all boxes before the first selected box
+ while( nBox < nBoxCount )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nBox++];
+ if( pBox != pSel )
+ nPos += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ else
+ break;
+ }
+ // nPos is now the left border of the first selected box
+ if( rPara.nMinLeft > nPos )
+ rPara.nMinLeft = nPos;
+ nBoxCount = pFndLine->GetBoxes().size();
+ rWidth = std::vector< sal_uLong >( nBoxCount+2 );
+ rWidth[ 0 ] = nPos;
+ // Add now the widths of all selected boxes and store
+ // the positions in the vector
+ for( nBox = 0; nBox < nBoxCount; )
+ {
+ nPos += pFndLine->GetBoxes()[nBox]
+ ->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
+ rWidth[ ++nBox ] = nPos;
+ }
+ // nPos: The right border of the last selected box
+ if( rPara.nMaxRight < nPos )
+ rPara.nMaxRight = nPos;
+ if( nPos <= rWidth[ 0 ] )
+ rWidth.clear();
+ }
+ }
+ }
+ }
+ // Second step: calculate the new widths for the copied cells
+ sal_uLong nSelSize = rPara.nMaxRight - rPara.nMinLeft;
+ if( !nSelSize )
+ return;
+
+ for( size_t nLine = 0; nLine < nLineCount; ++nLine )
+ {
+ std::vector< sal_uLong > &rWidth = (*rPara.pWidths)[ nLine ];
+ const size_t nCount = rWidth.size();
+ if( nCount > 2 )
+ {
+ rWidth[ nCount - 1 ] = rPara.nMaxRight;
+ sal_uLong nLastPos = 0;
+ for( size_t nBox = 0; nBox < nCount; ++nBox )
+ {
+ sal_uInt64 nNextPos = rWidth[ nBox ];
+ nNextPos -= rPara.nMinLeft;
+ nNextPos *= rPara.nNewSize;
+ nNextPos /= nSelSize;
+ rWidth[ nBox ] = static_cast<sal_uLong>(nNextPos - nLastPos);
+ nLastPos = static_cast<sal_uLong>(nNextPos);
+ }
+ }
+ }
+}
+
+static void
+lcl_CopyLineToDoc(FndLine_ const& rpFndLn, CpyPara *const pCpyPara);
+
+static void lcl_CopyBoxToDoc(FndBox_ const& rFndBox, CpyPara *const pCpyPara)
+{
+ // Calculation of new size
+ sal_uLong nRealSize;
+ sal_uLong nDummy1 = 0;
+ sal_uLong nDummy2 = 0;
+ if( pCpyPara->pTableNd->GetTable().IsNewModel() )
+ {
+ if( pCpyPara->nBoxIdx == 1 )
+ nDummy1 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][0];
+ nRealSize = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx++];
+ if( pCpyPara->nBoxIdx == (*pCpyPara->pWidths)[pCpyPara->nLnIdx].size()-1 )
+ nDummy2 = (*pCpyPara->pWidths)[pCpyPara->nLnIdx][pCpyPara->nBoxIdx];
+ }
+ else
+ {
+ nRealSize = pCpyPara->nNewSize;
+ nRealSize *= rFndBox.GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
+ if (pCpyPara->nOldSize == 0)
+ throw o3tl::divide_by_zero();
+ nRealSize /= pCpyPara->nOldSize;
+ }
+
+ sal_uLong nSize;
+ bool bDummy = nDummy1 > 0;
+ if( bDummy )
+ nSize = nDummy1;
+ else
+ {
+ nSize = nRealSize;
+ nRealSize = 0;
+ }
+ do
+ {
+ // Find the Frame Format in the list of all Frame Formats
+ CpyTabFrame aFindFrame(static_cast<SwTableBoxFormat*>(rFndBox.GetBox()->GetFrameFormat()));
+
+ std::shared_ptr<SwFormatFrameSize> aFrameSz(std::make_shared<SwFormatFrameSize>());
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.lower_bound( aFindFrame );
+ const CpyTabFrames::size_type nFndPos = itFind - pCpyPara->rTabFrameArr.begin();
+
+ // It *is* sometimes cool to have multiple tests/if's and assignments
+ // in a single statement, and it is technically possible. But it is definitely
+ // not simply readable - where from my POV reading code is done 1000 times
+ // more often than writing it. Thus I dismantled the expression in smaller
+ // chunks to keep it handy/understandable/changeable (hopefully without error)
+ // The original for reference:
+ // if( itFind == pCpyPara->rTabFrameArr.end() || !(*itFind == aFindFrame) ||
+ // ( aFrameSz = ( aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ]).pNewFrameFormat->
+ // GetFrameSize()).GetWidth() != static_cast<SwTwips>(nSize) )
+
+ bool DoCopyIt(itFind == pCpyPara->rTabFrameArr.end());
+
+ if(!DoCopyIt)
+ {
+ DoCopyIt = !(*itFind == aFindFrame);
+ }
+
+ if(!DoCopyIt)
+ {
+ aFindFrame = pCpyPara->rTabFrameArr[ nFndPos ];
+ aFrameSz.reset(aFindFrame.pNewFrameFormat->GetFrameSize().Clone());
+ DoCopyIt = aFrameSz->GetWidth() != static_cast<SwTwips>(nSize);
+ }
+
+ if(DoCopyIt)
+ {
+ // It doesn't exist yet, so copy it
+ aFindFrame.pNewFrameFormat = pCpyPara->rDoc.MakeTableBoxFormat();
+ aFindFrame.pNewFrameFormat->CopyAttrs( *rFndBox.GetBox()->GetFrameFormat() );
+ if( !pCpyPara->bCpyContent )
+ aFindFrame.pNewFrameFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
+ aFrameSz->SetWidth( nSize );
+ aFindFrame.pNewFrameFormat->SetFormatAttr( *aFrameSz );
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+
+ SwTableBox* pBox;
+ if (!rFndBox.GetLines().empty())
+ {
+ pBox = new SwTableBox( aFindFrame.pNewFrameFormat,
+ rFndBox.GetLines().size(), pCpyPara->pInsLine );
+ pCpyPara->pInsLine->GetTabBoxes().insert( pCpyPara->pInsLine->GetTabBoxes().begin() + pCpyPara->nInsPos++, pBox );
+ CpyPara aPara( *pCpyPara, pBox );
+ aPara.nNewSize = nSize; // get the size
+ for (auto const& rpFndLine : rFndBox.GetLines())
+ {
+ lcl_CopyLineToDoc( *rpFndLine, &aPara );
+ }
+ }
+ else
+ {
+ // Create an empty Box
+ pCpyPara->rDoc.GetNodes().InsBoxen( pCpyPara->pTableNd, pCpyPara->pInsLine,
+ aFindFrame.pNewFrameFormat,
+ pCpyPara->rDoc.GetDfltTextFormatColl(),
+ nullptr, pCpyPara->nInsPos );
+ pBox = pCpyPara->pInsLine->GetTabBoxes()[ pCpyPara->nInsPos ];
+ if( bDummy )
+ pBox->setDummyFlag( true );
+ else if( pCpyPara->bCpyContent )
+ {
+ // Copy the content into this empty Box
+ pBox->setRowSpan(rFndBox.GetBox()->getRowSpan());
+
+ // We can also copy formulas and values, if we copy the content
+ {
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxAttrSet( pCpyPara->rDoc.GetAttrPool() );
+ aBoxAttrSet.Put(rFndBox.GetBox()->GetFrameFormat()->GetAttrSet());
+ if( aBoxAttrSet.Count() )
+ {
+ const SwTableBoxNumFormat* pItem;
+ SvNumberFormatter* pN = pCpyPara->rDoc.GetNumberFormatter( false );
+ if( pN && pN->HasMergeFormatTable() && (pItem = aBoxAttrSet.
+ GetItemIfSet( RES_BOXATR_FORMAT, false )) )
+ {
+ sal_uLong nOldIdx = pItem->GetValue();
+ sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
+ if( nNewIdx != nOldIdx )
+ aBoxAttrSet.Put( SwTableBoxNumFormat( nNewIdx ));
+ }
+ pBox->ClaimFrameFormat()->SetFormatAttr( aBoxAttrSet );
+ }
+ }
+ SwDoc* pFromDoc = rFndBox.GetBox()->GetFrameFormat()->GetDoc();
+ SwNodeRange aCpyRg( *rFndBox.GetBox()->GetSttNd(), SwNodeOffset(1),
+ *rFndBox.GetBox()->GetSttNd()->EndOfSectionNode() );
+ SwNodeIndex aInsIdx( *pBox->GetSttNd(), 1 );
+
+ pFromDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(aCpyRg, aInsIdx, nullptr, false);
+ // Delete the initial TextNode
+ pCpyPara->rDoc.GetNodes().Delete( aInsIdx );
+ }
+ ++pCpyPara->nInsPos;
+ }
+ if( nRealSize )
+ {
+ bDummy = false;
+ nSize = nRealSize;
+ nRealSize = 0;
+ }
+ else
+ {
+ bDummy = true;
+ nSize = nDummy2;
+ nDummy2 = 0;
+ }
+ }
+ while( nSize );
+}
+
+static void
+lcl_CopyLineToDoc(const FndLine_& rFndLine, CpyPara *const pCpyPara)
+{
+ // Find the Frame Format in the list of all Frame Formats
+ CpyTabFrame aFindFrame( rFndLine.GetLine()->GetFrameFormat() );
+ CpyTabFrames::const_iterator itFind = pCpyPara->rTabFrameArr.find( aFindFrame );
+ if( itFind == pCpyPara->rTabFrameArr.end() )
+ {
+ // It doesn't exist yet, so copy it
+ aFindFrame.pNewFrameFormat = reinterpret_cast<SwTableBoxFormat*>(pCpyPara->rDoc.MakeTableLineFormat());
+ aFindFrame.pNewFrameFormat->CopyAttrs( *rFndLine.GetLine()->GetFrameFormat() );
+ pCpyPara->rTabFrameArr.insert( aFindFrame );
+ }
+ else
+ aFindFrame = *itFind;
+
+ SwTableLine* pNewLine = new SwTableLine( reinterpret_cast<SwTableLineFormat*>(aFindFrame.pNewFrameFormat),
+ rFndLine.GetBoxes().size(), pCpyPara->pInsBox );
+ if( pCpyPara->pInsBox )
+ {
+ SwTableLines& rLines = pCpyPara->pInsBox->GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine );
+ }
+ else
+ {
+ SwTableLines& rLines = pCpyPara->pTableNd->GetTable().GetTabLines();
+ rLines.insert( rLines.begin() + pCpyPara->nInsPos++, pNewLine);
+ }
+
+ CpyPara aPara( *pCpyPara, pNewLine );
+
+ if( pCpyPara->pTableNd->GetTable().IsNewModel() )
+ {
+ aPara.nOldSize = 0; // will not be used
+ aPara.nBoxIdx = 1;
+ }
+ else if( rFndLine.GetBoxes().size() ==
+ rFndLine.GetLine()->GetTabBoxes().size() )
+ {
+ // Get the Parent's size
+ const SwFrameFormat* pFormat;
+
+ if( rFndLine.GetLine()->GetUpper() )
+ pFormat = rFndLine.GetLine()->GetUpper()->GetFrameFormat();
+ else
+ pFormat = pCpyPara->pTableNd->GetTable().GetFrameFormat();
+ aPara.nOldSize = pFormat->GetFrameSize().GetWidth();
+ }
+ else
+ // Calculate it
+ for (auto &rpBox : rFndLine.GetBoxes())
+ {
+ aPara.nOldSize += rpBox->GetBox()->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+
+ const FndBoxes_t& rBoxes = rFndLine.GetBoxes();
+ for (auto const& it : rBoxes)
+ {
+ lcl_CopyBoxToDoc(*it, &aPara);
+ }
+ if( pCpyPara->pTableNd->GetTable().IsNewModel() )
+ ++pCpyPara->nLnIdx;
+}
+
+void SwTable::CopyHeadlineIntoTable( SwTableNode& rTableNd )
+{
+ // Find all Boxes/Lines
+ SwSelBoxes aSelBoxes;
+ SwTableBox* pBox = GetTabSortBoxes()[ 0 ];
+ pBox = GetTableBox( pBox->GetSttNd()->StartOfSectionNode()->GetIndex() + 1 );
+ SelLineFromBox( pBox, aSelBoxes );
+
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( aSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return;
+
+ {
+ // Convert Table formulas to their relative representation
+ SwTableFormulaUpdate aMsgHint( this );
+ aMsgHint.m_eFlags = TBL_RELBOXNAME;
+ GetFrameFormat()->GetDoc()->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ CpyTabFrames aCpyFormat;
+ CpyPara aPara( &rTableNd, 1, aCpyFormat );
+ aPara.nNewSize = aPara.nOldSize = rTableNd.GetTable().GetFrameFormat()->GetFrameSize().GetWidth();
+ // Copy
+ if( IsNewModel() )
+ lcl_CalcNewWidths( aFndBox.GetLines(), aPara );
+ for (const auto & rpFndLine : aFndBox.GetLines())
+ {
+ lcl_CopyLineToDoc( *rpFndLine, &aPara );
+ }
+ if( rTableNd.GetTable().IsNewModel() )
+ { // The copied line must not contain any row span attributes > 1
+ SwTableLine* pLine = rTableNd.GetTable().GetTabLines()[0];
+ OSL_ENSURE( !pLine->GetTabBoxes().empty(), "Empty Table Line" );
+ for( auto pTableBox : pLine->GetTabBoxes() )
+ {
+ OSL_ENSURE( pTableBox, "Missing Table Box" );
+ pTableBox->setRowSpan( 1 );
+ }
+ }
+}
+
+bool SwTable::MakeCopy( SwDoc& rInsDoc, const SwPosition& rPos,
+ const SwSelBoxes& rSelBoxes,
+ bool bCpyName, const OUString& rStyleName ) const
+{
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rSelBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( const_cast<SwTableLines&>(GetTabLines()), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ // First copy the PoolTemplates for the Table, so that the Tables are
+ // actually copied and have valid values.
+ SwDoc* pSrcDoc = GetFrameFormat()->GetDoc();
+ if( pSrcDoc != &rInsDoc )
+ {
+ rInsDoc.CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ) );
+ rInsDoc.CopyTextColl( *pSrcDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN ) );
+ }
+
+ SwTable* pNewTable = const_cast<SwTable*>(rInsDoc.InsertTable(
+ SwInsertTableOptions( SwInsertTableFlags::HeadlineNoBorder, 1 ),
+ rPos, 1, 1, GetFrameFormat()->GetHoriOrient().GetHoriOrient(),
+ nullptr, nullptr, false, IsNewModel() ));
+ if( !pNewTable )
+ return false;
+
+ SwNodeIndex aIdx( rPos.nNode, -1 );
+ SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
+ ++aIdx;
+ OSL_ENSURE( pTableNd, "Where is the TableNode now?" );
+
+ pTableNd->GetTable().SetRowsToRepeat( GetRowsToRepeat() );
+
+ pNewTable->SetTableStyleName(pTableNd->GetTable().GetTableStyleName());
+
+ pTableNd->GetTable().SetTableStyleName(rStyleName);
+ if( auto pSwDDETable = dynamic_cast<const SwDDETable*>(this) )
+ {
+ // A DDE-Table is being copied
+ // Does the new Document actually have it's FieldType?
+ SwFieldType* pFieldType = rInsDoc.getIDocumentFieldsAccess().InsertFieldType(
+ *pSwDDETable->GetDDEFieldType() );
+ OSL_ENSURE( pFieldType, "unknown FieldType" );
+
+ // Change the Table Pointer at the Node
+ pNewTable = new SwDDETable( *pNewTable,
+ static_cast<SwDDEFieldType*>(pFieldType) );
+ pTableNd->SetNewTable( std::unique_ptr<SwTable>(pNewTable), false );
+ }
+
+ pNewTable->GetFrameFormat()->CopyAttrs( *GetFrameFormat() );
+ pNewTable->SetTableChgMode( GetTableChgMode() );
+
+ // Destroy the already created Frames
+ pTableNd->DelFrames();
+
+ {
+ // Convert the Table formulas to their relative representation
+ SwTableFormulaUpdate aMsgHint( this );
+ aMsgHint.m_eFlags = TBL_RELBOXNAME;
+ pSrcDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ SwTableNumFormatMerge aTNFM(*pSrcDoc, rInsDoc);
+
+ // Also copy Names or enforce a new unique one
+ if( bCpyName )
+ pNewTable->GetFrameFormat()->SetName( GetFrameFormat()->GetName() );
+
+ CpyTabFrames aCpyFormat;
+ CpyPara aPara( pTableNd, 1, aCpyFormat );
+ aPara.nNewSize = aPara.nOldSize = GetFrameFormat()->GetFrameSize().GetWidth();
+
+ if( IsNewModel() )
+ lcl_CalcNewWidths( aFndBox.GetLines(), aPara );
+ // Copy
+ for (const auto & rpFndLine : aFndBox.GetLines())
+ {
+ lcl_CopyLineToDoc( *rpFndLine, &aPara );
+ }
+
+ // Set the "right" margin above/below
+ {
+ FndLine_* pFndLn = aFndBox.GetLines().front().get();
+ SwTableLine* pLn = pFndLn->GetLine();
+ const SwTableLine* pTmp = pLn;
+ sal_uInt16 nLnPos = GetTabLines().GetPos( pTmp );
+ if( USHRT_MAX != nLnPos && nLnPos )
+ {
+ // There is a Line before it
+ SwCollectTableLineBoxes aLnPara( false, SplitTable_HeadlineOption::BorderCopy );
+
+ pLn = GetTabLines()[ nLnPos - 1 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox( rpBox, &aLnPara );
+
+ if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
+ lcl_GetLineWidth( *pFndLn )) )
+ {
+ aLnPara.SetValues( true );
+ pLn = pNewTable->GetTabLines()[ 0 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aLnPara );
+ }
+ }
+
+ pFndLn = aFndBox.GetLines().back().get();
+ pLn = pFndLn->GetLine();
+ pTmp = pLn;
+ nLnPos = GetTabLines().GetPos( pTmp );
+ if( nLnPos < GetTabLines().size() - 1 )
+ {
+ // There is a Line following it
+ SwCollectTableLineBoxes aLnPara( true, SplitTable_HeadlineOption::BorderCopy );
+
+ pLn = GetTabLines()[ nLnPos + 1 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox( rpBox, &aLnPara );
+
+ if( aLnPara.Resize( lcl_GetBoxOffset( aFndBox ),
+ lcl_GetLineWidth( *pFndLn )) )
+ {
+ aLnPara.SetValues( false );
+ pLn = pNewTable->GetTabLines().back();
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aLnPara );
+ }
+ }
+ }
+
+ // We need to delete the initial Box
+ DeleteBox_( *pNewTable, pNewTable->GetTabLines().back()->GetTabBoxes()[0],
+ nullptr, false, false );
+
+ if( pNewTable->IsNewModel() )
+ lcl_CheckRowSpan( *pNewTable );
+ // Clean up
+ pNewTable->GCLines();
+
+ pTableNd->MakeOwnFrames( &aIdx ); // re-generate the Frames
+
+ CHECKTABLELAYOUT
+
+ return true;
+}
+
+// Find the next Box with content from this Line
+SwTableBox* SwTableLine::FindNextBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox, bool bOvrTableLns ) const
+{
+ const SwTableLine* pLine = this; // for M800
+ SwTableBox* pBox;
+ sal_uInt16 nFndPos;
+ if( !GetTabBoxes().empty() && pSrchBox )
+ {
+ nFndPos = GetBoxPos( pSrchBox );
+ if( USHRT_MAX != nFndPos &&
+ nFndPos + 1 != o3tl::narrowing<sal_uInt16>(GetTabBoxes().size()) )
+ {
+ pBox = GetTabBoxes()[ nFndPos + 1 ];
+ while( !pBox->GetTabLines().empty() )
+ pBox = pBox->GetTabLines().front()->GetTabBoxes()[0];
+ return pBox;
+ }
+ }
+
+ if( GetUpper() )
+ {
+ nFndPos = GetUpper()->GetTabLines().GetPos( pLine );
+ OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" );
+ // Is there another Line?
+ if( nFndPos+1 >= o3tl::narrowing<sal_uInt16>(GetUpper()->GetTabLines().size()) )
+ return GetUpper()->GetUpper()->FindNextBox( rTable, GetUpper(), bOvrTableLns );
+ pLine = GetUpper()->GetTabLines()[nFndPos+1];
+ }
+ else if( bOvrTableLns ) // Over a Table's the "BaseLines"??
+ {
+ // Search for the next Line in the Table
+ nFndPos = rTable.GetTabLines().GetPos( pLine );
+ if( nFndPos + 1 >= o3tl::narrowing<sal_uInt16>(rTable.GetTabLines().size()) )
+ return nullptr; // there are no more Boxes
+
+ pLine = rTable.GetTabLines()[ nFndPos+1 ];
+ }
+ else
+ return nullptr;
+
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ pBox = pLine->GetTabBoxes().front();
+ while( !pBox->GetTabLines().empty() )
+ pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
+ return pBox;
+ }
+ return pLine->FindNextBox( rTable, nullptr, bOvrTableLns );
+}
+
+// Find the previous Box from this Line
+SwTableBox* SwTableLine::FindPreviousBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox, bool bOvrTableLns ) const
+{
+ const SwTableLine* pLine = this; // for M800
+ SwTableBox* pBox;
+ sal_uInt16 nFndPos;
+ if( !GetTabBoxes().empty() && pSrchBox )
+ {
+ nFndPos = GetBoxPos( pSrchBox );
+ if( USHRT_MAX != nFndPos && nFndPos )
+ {
+ pBox = GetTabBoxes()[ nFndPos - 1 ];
+ while( !pBox->GetTabLines().empty() )
+ {
+ pLine = pBox->GetTabLines().back();
+ pBox = pLine->GetTabBoxes().back();
+ }
+ return pBox;
+ }
+ }
+
+ if( GetUpper() )
+ {
+ nFndPos = GetUpper()->GetTabLines().GetPos( pLine );
+ OSL_ENSURE( USHRT_MAX != nFndPos, "Line is not in the Table" );
+ // Is there another Line?
+ if( !nFndPos )
+ return GetUpper()->GetUpper()->FindPreviousBox( rTable, GetUpper(), bOvrTableLns );
+ pLine = GetUpper()->GetTabLines()[nFndPos-1];
+ }
+ else if( bOvrTableLns ) // Over a Table's the "BaseLines"??
+ {
+ // Search for the next Line in the Table
+ nFndPos = rTable.GetTabLines().GetPos( pLine );
+ if( !nFndPos )
+ return nullptr; // there are no more Boxes
+
+ pLine = rTable.GetTabLines()[ nFndPos-1 ];
+ }
+ else
+ return nullptr;
+
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ pBox = pLine->GetTabBoxes().back();
+ while( !pBox->GetTabLines().empty() )
+ {
+ pLine = pBox->GetTabLines().back();
+ pBox = pLine->GetTabBoxes().back();
+ }
+ return pBox;
+ }
+ return pLine->FindPreviousBox( rTable, nullptr, bOvrTableLns );
+}
+
+// Find the next Box with content from this Line
+SwTableBox* SwTableBox::FindNextBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox, bool bOvrTableLns ) const
+{
+ if( !pSrchBox && GetTabLines().empty() )
+ return const_cast<SwTableBox*>(this);
+ return GetUpper()->FindNextBox( rTable, pSrchBox ? pSrchBox : this,
+ bOvrTableLns );
+
+}
+
+// Find the next Box with content from this Line
+SwTableBox* SwTableBox::FindPreviousBox( const SwTable& rTable,
+ const SwTableBox* pSrchBox ) const
+{
+ if( !pSrchBox && GetTabLines().empty() )
+ return const_cast<SwTableBox*>(this);
+ return GetUpper()->FindPreviousBox( rTable, pSrchBox ? pSrchBox : this );
+}
+
+static void lcl_BoxSetHeadCondColl( const SwTableBox* pBox )
+{
+ // We need to adapt the paragraphs with conditional templates in the HeadLine
+ const SwStartNode* pSttNd = pBox->GetSttNd();
+ if( pSttNd )
+ pSttNd->CheckSectionCondColl();
+ else
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ sw_LineSetHeadCondColl( pLine );
+}
+
+void sw_LineSetHeadCondColl( const SwTableLine* pLine )
+{
+ for( const SwTableBox* pBox : pLine->GetTabBoxes() )
+ lcl_BoxSetHeadCondColl(pBox);
+}
+
+static SwTwips lcl_GetDistance( SwTableBox* pBox, bool bLeft )
+{
+ bool bFirst = true;
+ SwTwips nRet = 0;
+ SwTableLine* pLine;
+ while( pBox )
+ {
+ pLine = pBox->GetUpper();
+ if( !pLine )
+ break;
+ sal_uInt16 nStt = 0, nPos = pLine->GetBoxPos( pBox );
+
+ if( bFirst && !bLeft )
+ ++nPos;
+ bFirst = false;
+
+ while( nStt < nPos )
+ nRet += pLine->GetTabBoxes()[ nStt++ ]->GetFrameFormat()
+ ->GetFrameSize().GetWidth();
+ pBox = pLine->GetUpper();
+ }
+ return nRet;
+}
+
+static bool lcl_SetSelBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ SwFrameFormat* pFormat = pBox->GetFrameFormat();
+ const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
+ SwTwips nWidth = rSz.GetWidth();
+ bool bGreaterBox = false;
+
+ if( bCheck )
+ {
+ for( auto pLn : pBox->GetTabLines() )
+ if( !::lcl_SetSelBoxWidth( pLn, rParam, nDist, true ))
+ return false;
+
+ // Collect all "ContentBoxes"
+ bGreaterBox = (TableChgMode::FixedWidthChangeAbs != rParam.nMode)
+ && ((nDist + (rParam.bLeft ? 0 : nWidth)) >= rParam.nSide);
+ if (bGreaterBox
+ || (!rParam.bBigger
+ && (std::abs(nDist + ((rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth) - rParam.nSide) < COLFUZZY)))
+ {
+ SwTwips nLowerDiff;
+ if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ // The "other Boxes" have been adapted, so change by this value
+ nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide;
+ nLowerDiff *= rParam.nDiff;
+ nLowerDiff /= rParam.nMaxSize;
+ nLowerDiff = rParam.nDiff - nLowerDiff;
+ }
+ else
+ nLowerDiff = rParam.nDiff;
+
+ if( nWidth < nLowerDiff || nWidth - nLowerDiff < MINLAY )
+ return false;
+ }
+ }
+ else
+ {
+ SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
+ for( auto pLn : pBox->GetTabLines() )
+ {
+ rParam.nLowerDiff = 0;
+ lcl_SetSelBoxWidth( pLn, rParam, nDist, false );
+
+ if( nLowerDiff < rParam.nLowerDiff )
+ nLowerDiff = rParam.nLowerDiff;
+ }
+ rParam.nLowerDiff = nOldLower;
+
+ if( nLowerDiff ||
+ (bGreaterBox = !nOldLower && TableChgMode::FixedWidthChangeAbs != rParam.nMode &&
+ ( nDist + ( rParam.bLeft ? 0 : nWidth ) ) >= rParam.nSide) ||
+ ( std::abs( nDist + ( (rParam.nMode != TableChgMode::FixedWidthChangeAbs && rParam.bLeft) ? 0 : nWidth )
+ - rParam.nSide ) < COLFUZZY ))
+ {
+ // This column contains the Cursor - so decrease/increase
+ SwFormatFrameSize aNew( rSz );
+
+ if( !nLowerDiff )
+ {
+ if( bGreaterBox && TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ // The "other Boxes" have been adapted, so change by this value
+ nLowerDiff = (nDist + ( rParam.bLeft ? 0 : nWidth ) ) - rParam.nSide;
+ nLowerDiff *= rParam.nDiff;
+ nLowerDiff /= rParam.nMaxSize;
+ nLowerDiff = rParam.nDiff - nLowerDiff;
+ }
+ else
+ nLowerDiff = rParam.nDiff;
+ }
+
+ rParam.nLowerDiff += nLowerDiff;
+
+ if( rParam.bBigger )
+ aNew.SetWidth( nWidth + nLowerDiff );
+ else
+ aNew.SetWidth( nWidth - nLowerDiff );
+ rParam.aShareFormats.SetSize( *pBox, aNew );
+ break;
+ }
+ }
+
+ if( rParam.bLeft && rParam.nMode != TableChgMode::FixedWidthChangeAbs && nDist >= rParam.nSide )
+ break;
+
+ nDist += nWidth;
+
+ // If it gets bigger, then that's it
+ if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || !rParam.bLeft ) &&
+ nDist >= rParam.nSide )
+ break;
+ }
+ return true;
+}
+
+static bool lcl_SetOtherBoxWidth( SwTableLine* pLine, CR_SetBoxWidth& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ SwFrameFormat* pFormat = pBox->GetFrameFormat();
+ const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
+ SwTwips nWidth = rSz.GetWidth();
+
+ if( bCheck )
+ {
+ for( auto pLn : pBox->GetTabLines() )
+ if( !::lcl_SetOtherBoxWidth( pLn, rParam, nDist, true ))
+ return false;
+
+ if( rParam.bBigger && ( TableChgMode::FixedWidthChangeAbs == rParam.nMode
+ ? std::abs( nDist - rParam.nSide ) < COLFUZZY
+ : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY
+ : nDist >= rParam.nSide - COLFUZZY )) )
+ {
+ SwTwips nDiff;
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional
+ {
+ // calculate relative
+ nDiff = nWidth;
+ nDiff *= rParam.nDiff;
+ nDiff /= rParam.nMaxSize;
+ }
+ else
+ nDiff = rParam.nDiff;
+
+ if( nWidth < nDiff || nWidth - nDiff < MINLAY )
+ return false;
+ }
+ }
+ else
+ {
+ SwTwips nLowerDiff = 0, nOldLower = rParam.nLowerDiff;
+ for( auto pLn : pBox->GetTabLines() )
+ {
+ rParam.nLowerDiff = 0;
+ lcl_SetOtherBoxWidth( pLn, rParam, nDist, false );
+
+ if( nLowerDiff < rParam.nLowerDiff )
+ nLowerDiff = rParam.nLowerDiff;
+ }
+ rParam.nLowerDiff = nOldLower;
+
+ if( nLowerDiff ||
+ ( TableChgMode::FixedWidthChangeAbs == rParam.nMode
+ ? std::abs( nDist - rParam.nSide ) < COLFUZZY
+ : ( rParam.bLeft ? nDist < rParam.nSide - COLFUZZY
+ : nDist >= rParam.nSide - COLFUZZY)
+ ) )
+ {
+ SwFormatFrameSize aNew( rSz );
+
+ if( !nLowerDiff )
+ {
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode ) // Table fixed, proportional
+ {
+ // calculate relative
+ nLowerDiff = nWidth;
+ nLowerDiff *= rParam.nDiff;
+ nLowerDiff /= rParam.nMaxSize;
+ }
+ else
+ nLowerDiff = rParam.nDiff;
+ }
+
+ rParam.nLowerDiff += nLowerDiff;
+
+ if( rParam.bBigger )
+ aNew.SetWidth( nWidth - nLowerDiff );
+ else
+ aNew.SetWidth( nWidth + nLowerDiff );
+
+ rParam.aShareFormats.SetSize( *pBox, aNew );
+ }
+ }
+
+ nDist += nWidth;
+ if( ( TableChgMode::FixedWidthChangeAbs == rParam.nMode || rParam.bLeft ) &&
+ nDist > rParam.nSide )
+ break;
+ }
+ return true;
+}
+
+static void lcl_AjustLines( SwTableLine* pLine, CR_SetBoxWidth& rParam )
+{
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ {
+ SwFormatFrameSize aSz( pBox->GetFrameFormat()->GetFrameSize() );
+ SwTwips nWidth = aSz.GetWidth();
+ nWidth *= rParam.nDiff;
+ nWidth /= rParam.nMaxSize;
+ aSz.SetWidth( nWidth );
+ rParam.aShareFormats.SetSize( *pBox, aSz );
+
+ for( auto pLn : pBox->GetTabLines() )
+ ::lcl_AjustLines( pLn, rParam );
+ }
+}
+
+#ifdef DBG_UTIL
+void CheckBoxWidth( const SwTableLine& rLine, SwTwips nSize )
+{
+ const SwTableBoxes& rBoxes = rLine.GetTabBoxes();
+
+ SwTwips nCurrentSize = 0;
+ // See if the tables have a correct width
+ for (const SwTableBox* pBox : rBoxes)
+ {
+ const SwTwips nBoxW = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ nCurrentSize += nBoxW;
+
+ for( auto pLn : pBox->GetTabLines() )
+ CheckBoxWidth( *pLn, nBoxW );
+ }
+
+ if (sal::static_int_cast< tools::ULong >(std::abs(nCurrentSize - nSize)) >
+ (COLFUZZY * rBoxes.size()))
+ {
+ OSL_FAIL( "Line's Boxes are too small or too large" );
+ }
+}
+#endif
+
+bool SwTable::SetColWidth( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo )
+{
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ const SwFormatFrameSize& rSz = GetFrameFormat()->GetFrameSize();
+ const SvxLRSpaceItem& rLR = GetFrameFormat()->GetLRSpace();
+
+ bool bBigger,
+ bRet = false,
+ bLeft = TableChgWidthHeightType::ColLeft == extractPosition( eType ) ||
+ TableChgWidthHeightType::CellLeft == extractPosition( eType );
+
+ // Get the current Box's edge
+ // Only needed for manipulating the width
+ const SwTwips nDist = ::lcl_GetDistance( &rCurrentBox, bLeft );
+ SwTwips nDistStt = 0;
+ CR_SetBoxWidth aParam( eType, nRelDiff, nDist,
+ bLeft ? nDist : rSz.GetWidth() - nDist,
+ const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
+ bBigger = aParam.bBigger;
+
+ FN_lcl_SetBoxWidth fnSelBox, fnOtherBox;
+ fnSelBox = lcl_SetSelBoxWidth;
+ fnOtherBox = lcl_SetOtherBoxWidth;
+
+ switch( extractPosition(eType) )
+ {
+ case TableChgWidthHeightType::ColRight:
+ case TableChgWidthHeightType::ColLeft:
+ if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
+ {
+ // First test if we have room at all
+ bool bChgLRSpace = true;
+ if( bBigger )
+ {
+ if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) &&
+ !rSz.GetWidthPercent() )
+ {
+ // silence -Wsign-compare on Android with the static cast
+ bRet = rSz.GetWidth() < static_cast<unsigned short>(USHRT_MAX) - nRelDiff;
+ bChgLRSpace = bLeft ? rLR.GetLeft() >= nAbsDiff
+ : rLR.GetRight() >= nAbsDiff;
+ }
+ else
+ bRet = bLeft ? rLR.GetLeft() >= nAbsDiff
+ : rLR.GetRight() >= nAbsDiff;
+
+ if( !bRet )
+ {
+ // Then call itself recursively; only with another mode (proportional)
+ TableChgMode eOld = m_eTableChgMode;
+ m_eTableChgMode = TableChgMode::FixedWidthChangeProp;
+
+ bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff,
+ ppUndo );
+ m_eTableChgMode = eOld;
+ return bRet;
+ }
+ }
+ else
+ {
+ bRet = true;
+ for( auto const & n: m_aLines )
+ {
+ aParam.LoopClear();
+ if( !(*fnSelBox)( n, aParam, nDistStt, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+
+ if( bRet )
+ {
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ tools::Long nFrameWidth = LONG_MAX;
+ LockModify();
+ SwFormatFrameSize aSz( rSz );
+ SvxLRSpaceItem aLR( rLR );
+ if( bBigger )
+ {
+ // If the Table does not have any room to grow, we need to create some!
+ // silence -Wsign-compare on Android with the static cast
+ if( aSz.GetWidth() + nRelDiff > static_cast<unsigned short>(USHRT_MAX) )
+ {
+ // Break down to USHRT_MAX / 2
+ CR_SetBoxWidth aTmpPara( TableChgWidthHeightType::ColLeft, aSz.GetWidth() / 2,
+ 0, aSz.GetWidth(), aParam.pTableNd );
+ for( size_t nLn = 0; nLn < m_aLines.size(); ++nLn )
+ ::lcl_AjustLines( m_aLines[ nLn ], aTmpPara );
+ aSz.SetWidth( aSz.GetWidth() / 2 );
+ aParam.nDiff = nRelDiff /= 2;
+ aParam.nSide /= 2;
+ aParam.nMaxSize /= 2;
+ }
+
+ if( bLeft )
+ aLR.SetLeft( sal_uInt16( aLR.GetLeft() - nAbsDiff ) );
+ else
+ aLR.SetRight( sal_uInt16( aLR.GetRight() - nAbsDiff ) );
+ }
+ else if( bLeft )
+ aLR.SetLeft( sal_uInt16( aLR.GetLeft() + nAbsDiff ) );
+ else
+ aLR.SetRight( sal_uInt16( aLR.GetRight() + nAbsDiff ) );
+
+ if( bChgLRSpace )
+ GetFrameFormat()->SetFormatAttr( aLR );
+ const SwFormatHoriOrient& rHOri = GetFrameFormat()->GetHoriOrient();
+ if( text::HoriOrientation::FULL == rHOri.GetHoriOrient() ||
+ (text::HoriOrientation::LEFT == rHOri.GetHoriOrient() && aLR.GetLeft()) ||
+ (text::HoriOrientation::RIGHT == rHOri.GetHoriOrient() && aLR.GetRight()))
+ {
+ SwFormatHoriOrient aHOri( rHOri );
+ aHOri.SetHoriOrient( text::HoriOrientation::NONE );
+ GetFrameFormat()->SetFormatAttr( aHOri );
+
+ // If the Table happens to contain relative values (USHORT_MAX),
+ // we need to convert them to absolute ones now.
+ // Bug 61494
+ if( GetFrameFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE) &&
+ !rSz.GetWidthPercent() )
+ {
+ SwTabFrame* pTabFrame = SwIterator<SwTabFrame,SwFormat>( *GetFrameFormat() ).First();
+ if( pTabFrame &&
+ pTabFrame->getFramePrintArea().Width() != rSz.GetWidth() )
+ {
+ nFrameWidth = pTabFrame->getFramePrintArea().Width();
+ if( bBigger )
+ nFrameWidth += nAbsDiff;
+ else
+ nFrameWidth -= nAbsDiff;
+ }
+ }
+ }
+
+ if( bBigger )
+ aSz.SetWidth( aSz.GetWidth() + nRelDiff );
+ else
+ aSz.SetWidth( aSz.GetWidth() - nRelDiff );
+
+ if( rSz.GetWidthPercent() )
+ aSz.SetWidthPercent( static_cast<sal_uInt8>(( aSz.GetWidth() * 100 ) /
+ ( aSz.GetWidth() + aLR.GetRight() + aLR.GetLeft())));
+
+ GetFrameFormat()->SetFormatAttr( aSz );
+
+ UnlockModify();
+
+ for( sal_uInt16 n = m_aLines.size(); n; )
+ {
+ --n;
+ aParam.LoopClear();
+ (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
+ }
+
+ // If the Table happens to contain relative values (USHORT_MAX),
+ // we need to convert them to absolute ones now.
+ // Bug 61494
+ if( LONG_MAX != nFrameWidth )
+ {
+ SwFormatFrameSize aAbsSz( aSz );
+ aAbsSz.SetWidth( nFrameWidth );
+ GetFrameFormat()->SetFormatAttr( aAbsSz );
+ }
+ }
+ }
+ else if( bLeft ? nDist != 0 : std::abs( rSz.GetWidth() - nDist ) > COLFUZZY )
+ {
+ bRet = true;
+ if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode )
+ aParam.bBigger = !bBigger;
+
+ // First test if we have room at all
+ if( aParam.bBigger )
+ {
+ for( auto const & n: m_aLines )
+ {
+ aParam.LoopClear();
+ if( !(*fnOtherBox)( n, aParam, 0, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for( auto const & n: m_aLines )
+ {
+ aParam.LoopClear();
+ if( !(*fnSelBox)( n, aParam, nDistStt, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+
+ // If true, set it
+ if( bRet )
+ {
+ CR_SetBoxWidth aParam1( aParam );
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft )
+ {
+ for( sal_uInt16 n = m_aLines.size(); n; )
+ {
+ --n;
+ aParam.LoopClear();
+ aParam1.LoopClear();
+ (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
+ (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false );
+ }
+ }
+ else
+ {
+ for( sal_uInt16 n = m_aLines.size(); n; )
+ {
+ --n;
+ aParam.LoopClear();
+ aParam1.LoopClear();
+ (*fnOtherBox)( m_aLines[ n ], aParam1, nDistStt, false );
+ (*fnSelBox)( m_aLines[ n ], aParam, nDistStt, false );
+ }
+ }
+ }
+ }
+ break;
+
+ case TableChgWidthHeightType::CellRight:
+ case TableChgWidthHeightType::CellLeft:
+ if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
+ {
+ // Then call itself recursively; only with another mode (proportional)
+ TableChgMode eOld = m_eTableChgMode;
+ m_eTableChgMode = TableChgMode::FixedWidthChangeAbs;
+
+ bRet = SetColWidth( rCurrentBox, eType, nAbsDiff, nRelDiff,
+ ppUndo );
+ m_eTableChgMode = eOld;
+ return bRet;
+ }
+ else if( bLeft ? nDist != 0 : (rSz.GetWidth() - nDist) > COLFUZZY )
+ {
+ if( bLeft && TableChgMode::FixedWidthChangeAbs == m_eTableChgMode )
+ aParam.bBigger = !bBigger;
+
+ // First, see if there is enough room at all
+ SwTableBox* pBox = &rCurrentBox;
+ SwTableLine* pLine = rCurrentBox.GetUpper();
+ while( pLine->GetUpper() )
+ {
+ const SwTableBoxes::size_type nPos = pLine->GetBoxPos( pBox );
+ if( bLeft ? nPos != 0 : nPos + 1 != pLine->GetTabBoxes().size() )
+ break;
+
+ pBox = pLine->GetUpper();
+ pLine = pBox->GetUpper();
+ }
+
+ if( pLine->GetUpper() )
+ {
+ // We need to correct the distance once again!
+ aParam.nSide -= ::lcl_GetDistance( pLine->GetUpper(), true );
+
+ if( bLeft )
+ aParam.nMaxSize = aParam.nSide;
+ else
+ aParam.nMaxSize = pLine->GetUpper()->GetFrameFormat()->
+ GetFrameSize().GetWidth() - aParam.nSide;
+ }
+
+ // First, see if there is enough room at all
+ FN_lcl_SetBoxWidth fnTmp = aParam.bBigger ? fnOtherBox : fnSelBox;
+ bRet = (*fnTmp)( pLine, aParam, nDistStt, true );
+
+ // If true, set it
+ if( bRet )
+ {
+ CR_SetBoxWidth aParam1( aParam );
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ if( TableChgMode::FixedWidthChangeAbs != m_eTableChgMode && bLeft )
+ {
+ (*fnSelBox)( pLine, aParam, nDistStt, false );
+ (*fnOtherBox)( pLine, aParam1, nDistStt, false );
+ }
+ else
+ {
+ (*fnOtherBox)( pLine, aParam1, nDistStt, false );
+ (*fnSelBox)( pLine, aParam, nDistStt, false );
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+
+#if defined DBG_UTIL
+ if( bRet )
+ {
+ CHECKBOXWIDTH
+ CHECKTABLELAYOUT
+ }
+#endif
+
+ return bRet;
+}
+
+static void SetLineHeight( SwTableLine& rLine, SwTwips nOldHeight, SwTwips nNewHeight,
+ bool bMinSize )
+{
+ SwLayoutFrame* pLineFrame = GetRowFrame( rLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" );
+
+ SwFrameFormat* pFormat = rLine.ClaimFrameFormat();
+
+ SwTwips nMyNewH, nMyOldH = pLineFrame->getFrameArea().Height();
+ if( !nOldHeight ) // the BaseLine and absolute
+ nMyNewH = nMyOldH + nNewHeight;
+ else
+ {
+ // Calculate as exactly as possible
+ Fraction aTmp( nMyOldH );
+ aTmp *= Fraction( nNewHeight, nOldHeight );
+ aTmp += Fraction( 1, 2 ); // round up if needed
+ nMyNewH = tools::Long(aTmp);
+ }
+
+ SwFrameSize eSize = SwFrameSize::Minimum;
+ if( !bMinSize &&
+ ( nMyOldH - nMyNewH ) > ( CalcRowRstHeight( pLineFrame ) + ROWFUZZY ))
+ eSize = SwFrameSize::Fixed;
+
+ pFormat->SetFormatAttr( SwFormatFrameSize( eSize, 0, nMyNewH ) );
+
+ // First adapt all internal ones
+ for( auto pBox : rLine.GetTabBoxes() )
+ {
+ for( auto pLine : pBox->GetTabLines() )
+ SetLineHeight( *pLine, nMyOldH, nMyNewH, bMinSize );
+ }
+}
+
+static bool lcl_SetSelLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ bool bRet = true;
+ if( !bCheck )
+ {
+ // Set line height
+ SetLineHeight( *pLine, 0, rParam.bBigger ? nDist : -nDist,
+ rParam.bBigger );
+ }
+ else if( !rParam.bBigger )
+ {
+ // Calculate the new relative size by means of the old one
+ SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" );
+ SwTwips nRstHeight = CalcRowRstHeight( pLineFrame );
+ if( (nRstHeight + ROWFUZZY) < nDist )
+ bRet = false;
+ }
+ return bRet;
+}
+
+static bool lcl_SetOtherLineHeight( SwTableLine* pLine, const CR_SetLineHeight& rParam,
+ SwTwips nDist, bool bCheck )
+{
+ bool bRet = true;
+ if( bCheck )
+ {
+ if( rParam.bBigger )
+ {
+ // Calculate the new relative size by means of the old one
+ SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine?" );
+
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ nDist *= pLineFrame->getFrameArea().Height();
+ nDist /= rParam.nMaxHeight;
+ }
+ bRet = nDist <= CalcRowRstHeight( pLineFrame );
+ }
+ }
+ else
+ {
+ // Set line height
+ // pLine is the following/preceding, thus adjust it
+ if( TableChgMode::FixedWidthChangeProp == rParam.nMode )
+ {
+ SwLayoutFrame* pLineFrame = GetRowFrame( *pLine );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine??" );
+
+ // Calculate the new relative size by means of the old one
+ // If the selected Box get bigger, adjust via the max space else
+ // via the max height.
+ if( (true) /*!rParam.bBigger*/ )
+ {
+ nDist *= pLineFrame->getFrameArea().Height();
+ nDist /= rParam.nMaxHeight;
+ }
+ else
+ {
+ // Calculate the new relative size by means of the old one
+ nDist *= CalcRowRstHeight( pLineFrame );
+ nDist /= rParam.nMaxSpace;
+ }
+ }
+ SetLineHeight( *pLine, 0, rParam.bBigger ? -nDist : nDist,
+ !rParam.bBigger );
+ }
+ return bRet;
+}
+
+bool SwTable::SetRowHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff, std::unique_ptr<SwUndo>* ppUndo )
+{
+ SwTableLine* pLine = rCurrentBox.GetUpper();
+
+ SwTableLine* pBaseLine = pLine;
+ while( pBaseLine->GetUpper() )
+ pBaseLine = pBaseLine->GetUpper()->GetUpper();
+
+ bool bBigger,
+ bRet = false,
+ bTop = TableChgWidthHeightType::CellTop == extractPosition( eType );
+ sal_uInt16 nBaseLinePos = GetTabLines().GetPos( pBaseLine );
+
+ CR_SetLineHeight aParam( eType,
+ const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode()) );
+ bBigger = aParam.bBigger;
+
+ SwTableLines* pLines = &m_aLines;
+
+ // How do we get to the height?
+ switch( extractPosition(eType) )
+ {
+ case TableChgWidthHeightType::CellTop:
+ case TableChgWidthHeightType::CellBottom:
+ if( pLine == pBaseLine )
+ break; // it doesn't work then!
+
+ // Is a nested Line (Box!)
+ pLines = &pLine->GetUpper()->GetTabLines();
+ nBaseLinePos = pLines->GetPos( pLine );
+ [[fallthrough]];
+
+ case TableChgWidthHeightType::RowBottom:
+ {
+ if( TableChgMode::VarWidthChangeAbs == m_eTableChgMode )
+ {
+ // First test if we have room at all
+ if( bBigger )
+ bRet = true;
+ else
+ bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, true );
+
+ if( bRet )
+ {
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, false );
+ }
+ }
+ else
+ {
+ bRet = true;
+ SwTableLines::size_type nStt;
+ SwTableLines::size_type nEnd;
+ if( bTop )
+ {
+ nStt = 0;
+ nEnd = nBaseLinePos;
+ }
+ else
+ {
+ nStt = nBaseLinePos + 1;
+ nEnd = pLines->size();
+ }
+
+ // Get the current Lines' height
+ if( TableChgMode::FixedWidthChangeProp == m_eTableChgMode )
+ {
+ for( auto n = nStt; n < nEnd; ++n )
+ {
+ SwLayoutFrame* pLineFrame = GetRowFrame( *(*pLines)[ n ] );
+ OSL_ENSURE( pLineFrame, "Where is the Frame from the SwTableLine??" );
+ aParam.nMaxSpace += CalcRowRstHeight( pLineFrame );
+ aParam.nMaxHeight += pLineFrame->getFrameArea().Height();
+ }
+ if( bBigger && aParam.nMaxSpace < nAbsDiff )
+ bRet = false;
+ }
+ else
+ {
+ if( bTop ? nEnd != 0 : nStt < nEnd )
+ {
+ if( bTop )
+ nStt = nEnd - 1;
+ else
+ nEnd = nStt + 1;
+ }
+ else
+ bRet = false;
+ }
+
+ if( bRet )
+ {
+ if( bBigger )
+ {
+ for( auto n = nStt; n < nEnd; ++n )
+ {
+ if( !lcl_SetOtherLineHeight( (*pLines)[ n ], aParam,
+ nAbsDiff, true ))
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ else
+ bRet = lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, true );
+ }
+
+ if( bRet )
+ {
+ // Adjust
+ if( ppUndo )
+ ppUndo->reset(new SwUndoAttrTable( *aParam.pTableNd, true ));
+
+ CR_SetLineHeight aParam1( aParam );
+
+ if( bTop )
+ {
+ lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, false );
+ for( auto n = nStt; n < nEnd; ++n )
+ lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1,
+ nAbsDiff, false );
+ }
+ else
+ {
+ for( auto n = nStt; n < nEnd; ++n )
+ lcl_SetOtherLineHeight( (*pLines)[ n ], aParam1,
+ nAbsDiff, false );
+ lcl_SetSelLineHeight( (*pLines)[ nBaseLinePos ], aParam,
+ nAbsDiff, false );
+ }
+ }
+ else
+ {
+ // Then call itself recursively; only with another mode (proportional)
+ TableChgMode eOld = m_eTableChgMode;
+ m_eTableChgMode = TableChgMode::VarWidthChangeAbs;
+
+ bRet = SetRowHeight( rCurrentBox, eType, nAbsDiff,
+ nRelDiff, ppUndo );
+
+ m_eTableChgMode = eOld;
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+
+ CHECKTABLELAYOUT
+
+ return bRet;
+}
+
+SwFrameFormat* SwShareBoxFormat::GetFormat( tools::Long nWidth ) const
+{
+ SwFrameFormat *pRet = nullptr, *pTmp;
+ for( auto n = m_aNewFormats.size(); n; )
+ if( ( pTmp = m_aNewFormats[ --n ])->GetFrameSize().GetWidth()
+ == nWidth )
+ {
+ pRet = pTmp;
+ break;
+ }
+ return pRet;
+}
+
+SwFrameFormat* SwShareBoxFormat::GetFormat( const SfxPoolItem& rItem ) const
+{
+ const SfxPoolItem* pItem;
+ sal_uInt16 nWhich = rItem.Which();
+ SwFrameFormat *pRet = nullptr, *pTmp;
+ const SfxPoolItem& rFrameSz = m_pOldFormat->GetFormatAttr( RES_FRM_SIZE, false );
+ for( auto n = m_aNewFormats.size(); n; )
+ if( SfxItemState::SET == ( pTmp = m_aNewFormats[ --n ])->
+ GetItemState( nWhich, false, &pItem ) && *pItem == rItem &&
+ pTmp->GetFormatAttr( RES_FRM_SIZE, false ) == rFrameSz )
+ {
+ pRet = pTmp;
+ break;
+ }
+ return pRet;
+}
+
+void SwShareBoxFormat::AddFormat( SwFrameFormat& rNew )
+{
+ m_aNewFormats.push_back( &rNew );
+}
+
+bool SwShareBoxFormat::RemoveFormat( const SwFrameFormat& rFormat )
+{
+ // returns true, if we can delete
+ if( m_pOldFormat == &rFormat )
+ return true;
+
+ std::vector<SwFrameFormat*>::iterator it = std::find( m_aNewFormats.begin(), m_aNewFormats.end(), &rFormat );
+ if( m_aNewFormats.end() != it )
+ m_aNewFormats.erase( it );
+ return m_aNewFormats.empty();
+}
+
+SwShareBoxFormats::~SwShareBoxFormats()
+{
+}
+
+SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat, tools::Long nWidth ) const
+{
+ sal_uInt16 nPos;
+ return Seek_Entry( rFormat, &nPos )
+ ? m_ShareArr[ nPos ].GetFormat(nWidth)
+ : nullptr;
+}
+SwFrameFormat* SwShareBoxFormats::GetFormat( const SwFrameFormat& rFormat,
+ const SfxPoolItem& rItem ) const
+{
+ sal_uInt16 nPos;
+ return Seek_Entry( rFormat, &nPos )
+ ? m_ShareArr[ nPos ].GetFormat(rItem)
+ : nullptr;
+}
+
+void SwShareBoxFormats::AddFormat( const SwFrameFormat& rOld, SwFrameFormat& rNew )
+{
+ sal_uInt16 nPos;
+ if( !Seek_Entry( rOld, &nPos ))
+ {
+ SwShareBoxFormat aEntry(rOld);
+ aEntry.AddFormat( rNew );
+ m_ShareArr.insert(m_ShareArr.begin() + nPos, aEntry);
+ }
+ else
+ m_ShareArr[ nPos ].AddFormat(rNew);
+}
+
+void SwShareBoxFormats::ChangeFrameFormat( SwTableBox* pBox, SwTableLine* pLn,
+ SwFrameFormat& rFormat )
+{
+ SwClient aCl;
+ SwFrameFormat* pOld = nullptr;
+ if( pBox )
+ {
+ pOld = pBox->GetFrameFormat();
+ pOld->Add( &aCl );
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(&rFormat) );
+ }
+ else if( pLn )
+ {
+ pOld = pLn->GetFrameFormat();
+ pOld->Add( &aCl );
+ pLn->ChgFrameFormat( static_cast<SwTableLineFormat*>(&rFormat) );
+ }
+ if( pOld && pOld->HasOnlyOneListener() )
+ {
+ RemoveFormat( *pOld );
+ delete pOld;
+ }
+}
+
+void SwShareBoxFormats::SetSize( SwTableBox& rBox, const SwFormatFrameSize& rSz )
+{
+ SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(),
+ *pRet = GetFormat( *pBoxFormat, rSz.GetWidth() );
+ if( pRet )
+ ChangeFrameFormat( &rBox, nullptr, *pRet );
+ else
+ {
+ pRet = rBox.ClaimFrameFormat();
+ pRet->SetFormatAttr( rSz );
+ AddFormat( *pBoxFormat, *pRet );
+ }
+}
+
+void SwShareBoxFormats::SetAttr( SwTableBox& rBox, const SfxPoolItem& rItem )
+{
+ SwFrameFormat *pBoxFormat = rBox.GetFrameFormat(),
+ *pRet = GetFormat( *pBoxFormat, rItem );
+ if( pRet )
+ ChangeFrameFormat( &rBox, nullptr, *pRet );
+ else
+ {
+ pRet = rBox.ClaimFrameFormat();
+ pRet->SetFormatAttr( rItem );
+ AddFormat( *pBoxFormat, *pRet );
+ }
+}
+
+void SwShareBoxFormats::SetAttr( SwTableLine& rLine, const SfxPoolItem& rItem )
+{
+ SwFrameFormat *pLineFormat = rLine.GetFrameFormat(),
+ *pRet = GetFormat( *pLineFormat, rItem );
+ if( pRet )
+ ChangeFrameFormat( nullptr, &rLine, *pRet );
+ else
+ {
+ pRet = rLine.ClaimFrameFormat();
+ pRet->SetFormatAttr( rItem );
+ AddFormat( *pLineFormat, *pRet );
+ }
+}
+
+void SwShareBoxFormats::RemoveFormat( const SwFrameFormat& rFormat )
+{
+ for (auto i = m_ShareArr.size(); i; )
+ {
+ if (m_ShareArr[ --i ].RemoveFormat(rFormat))
+ {
+ m_ShareArr.erase( m_ShareArr.begin() + i );
+ }
+ }
+}
+
+bool SwShareBoxFormats::Seek_Entry( const SwFrameFormat& rFormat, sal_uInt16* pPos ) const
+{
+ sal_uIntPtr nIdx = reinterpret_cast<sal_uIntPtr>(&rFormat);
+ auto nO = m_ShareArr.size();
+ decltype(nO) nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ const auto nM = nU + ( nO - nU ) / 2;
+ sal_uIntPtr nFormat = reinterpret_cast<sal_uIntPtr>(&m_ShareArr[ nM ].GetOldFormat());
+ if( nFormat == nIdx )
+ {
+ if( pPos )
+ *pPos = nM;
+ return true;
+ }
+ else if( nFormat < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ {
+ if( pPos )
+ *pPos = nU;
+ return false;
+ }
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pPos )
+ *pPos = nU;
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/textboxhelper.cxx b/sw/source/core/doc/textboxhelper.cxx
new file mode 100644
index 000000000..358baaf7d
--- /dev/null
+++ b/sw/source/core/doc/textboxhelper.cxx
@@ -0,0 +1,1970 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <textboxhelper.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcnct.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docsh.hxx>
+#include <unocoll.hxx>
+#include <unoframe.hxx>
+#include <unodraw.hxx>
+#include <unotextrange.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <unoprnms.hxx>
+#include <mvsave.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <frmfmt.hxx>
+#include <frameformats.hxx>
+#include <dflyobj.hxx>
+#include <swtable.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <editeng/memberids.h>
+#include <svx/svdoashp.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdogrp.hxx>
+#include <svl/itemiter.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <sal/log.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svx/swframetypes.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <frmatr.hxx>
+
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/SizeType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+
+using namespace com::sun::star;
+
+void SwTextBoxHelper::create(SwFrameFormat* pShape, SdrObject* pObject, bool bCopyText)
+{
+ assert(pShape);
+ assert(pObject);
+
+ // If TextBox wasn't enabled previously
+ if (pShape->GetOtherTextBoxFormats() && pShape->GetOtherTextBoxFormats()->GetTextBox(pObject))
+ return;
+
+ // Store the current text content of the shape
+ OUString sCopyableText;
+
+ if (bCopyText)
+ {
+ if (pObject)
+ {
+ uno::Reference<text::XText> xSrcCnt(pObject->getWeakUnoShape().get(), uno::UNO_QUERY);
+ auto xCur = xSrcCnt->createTextCursor();
+ xCur->gotoStart(false);
+ xCur->gotoEnd(true);
+ sCopyableText = xCur->getText()->getString();
+ }
+ }
+
+ // Create the associated TextFrame and insert it into the document.
+ uno::Reference<text::XTextContent> xTextFrame(
+ SwXServiceProvider::MakeInstance(SwServiceType::TypeTextFrame, *pShape->GetDoc()),
+ uno::UNO_QUERY);
+ uno::Reference<text::XTextDocument> xTextDocument(
+ pShape->GetDoc()->GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+ uno::Reference<text::XTextContentAppend> xTextContentAppend(xTextDocument->getText(),
+ uno::UNO_QUERY);
+ xTextContentAppend->appendTextContent(xTextFrame, uno::Sequence<beans::PropertyValue>());
+
+ // Link FLY and DRAW formats, so it becomes a text box (needed for syncProperty calls).
+ uno::Reference<text::XTextFrame> xRealTextFrame(xTextFrame, uno::UNO_QUERY);
+ auto pTextFrame = dynamic_cast<SwXTextFrame*>(xRealTextFrame.get());
+ assert(nullptr != pTextFrame);
+ SwFrameFormat* pFormat = pTextFrame->GetFrameFormat();
+
+ assert(nullptr != dynamic_cast<SwDrawFrameFormat*>(pShape));
+ assert(nullptr != dynamic_cast<SwFlyFrameFormat*>(pFormat));
+
+ if (!pShape->GetOtherTextBoxFormats())
+ {
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShape));
+ pTextBox->AddTextBox(pObject, pFormat);
+ pShape->SetOtherTextBoxFormats(pTextBox);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ else
+ {
+ auto& pTextBox = pShape->GetOtherTextBoxFormats();
+ pTextBox->AddTextBox(pObject, pFormat);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ // Initialize properties.
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextFrame, uno::UNO_QUERY);
+ uno::Any aEmptyBorder{ table::BorderLine2() };
+ xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
+
+ xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
+
+ xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+
+ xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
+
+ uno::Reference<container::XNamed> xNamed(xTextFrame, uno::UNO_QUERY);
+ assert(!xNamed->getName().isEmpty());
+ (void)xNamed;
+
+ // Link its text range to the original shape.
+ uno::Reference<text::XTextRange> xTextBox(xTextFrame, uno::UNO_QUERY_THROW);
+ SwUnoInternalPaM aInternalPaM(*pShape->GetDoc());
+ if (sw::XTextRangeToSwPaM(aInternalPaM, xTextBox))
+ {
+ SwAttrSet aSet(pShape->GetAttrSet());
+ SwFormatContent aContent(aInternalPaM.GetNode().StartOfSectionNode());
+ aSet.Put(aContent);
+ pShape->SetFormatAttr(aSet);
+ }
+
+ DoTextBoxZOrderCorrection(pShape, pObject);
+
+ // Also initialize the properties, which are not constant, but inherited from the shape's ones.
+ uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObject);
+
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ syncProperty(pShape, RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW,
+ xShapePropertySet->getPropertyValue(UNO_NAME_IS_FOLLOWING_TEXT_FLOW), pObject);
+ syncProperty(pShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObject);
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObject);
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObject);
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObject);
+ syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST), pObject);
+ text::WritingMode eMode;
+ if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObject);
+
+ changeAnchor(pShape, pObject);
+ syncTextBoxSize(pShape, pObject);
+
+ // Check if the shape had text before and move it to the new textframe
+ if (!bCopyText || sCopyableText.isEmpty())
+ return;
+
+ if (pObject)
+ {
+ auto pSourceText = dynamic_cast<SdrTextObj*>(pObject);
+ uno::Reference<text::XTextRange> xDestText(xRealTextFrame, uno::UNO_QUERY);
+
+ xDestText->setString(sCopyableText);
+
+ if (pSourceText)
+ pSourceText->SetText(OUString());
+
+ pShape->GetDoc()->getIDocumentState().SetModified();
+ }
+}
+
+void SwTextBoxHelper::set(SwFrameFormat* pShapeFormat, SdrObject* pObj,
+ uno::Reference<text::XTextFrame> xNew)
+{
+ // Do not set invalid data
+ assert(pShapeFormat && pObj && xNew);
+ // Firstly find the format of the new textbox.
+ SwFrameFormat* pFormat = nullptr;
+ if (auto pTextFrame = dynamic_cast<SwXTextFrame*>(xNew.get()))
+ pFormat = pTextFrame->GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ // If there is a format, check if the shape already has a textbox assigned to.
+ if (auto& pTextBoxNode = pShapeFormat->GetOtherTextBoxFormats())
+ {
+ // If it has a texbox, destroy it.
+ if (pTextBoxNode->GetTextBox(pObj))
+ pTextBoxNode->DelTextBox(pObj, true);
+ // And set the new one.
+ pTextBoxNode->AddTextBox(pObj, pFormat);
+ pFormat->SetOtherTextBoxFormats(pTextBoxNode);
+ }
+ else
+ {
+ // If the shape do not have a texbox node and textbox,
+ // create that for the shape.
+ auto pTextBox = std::make_shared<SwTextBoxNode>(SwTextBoxNode(pShapeFormat));
+ pTextBox->AddTextBox(pObj, pFormat);
+ pShapeFormat->SetOtherTextBoxFormats(pTextBox);
+ pFormat->SetOtherTextBoxFormats(pTextBox);
+ }
+ // Initialize its properties
+ uno::Reference<beans::XPropertySet> xPropertySet(xNew, uno::UNO_QUERY);
+ uno::Any aEmptyBorder{ table::BorderLine2() };
+ xPropertySet->setPropertyValue(UNO_NAME_TOP_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_BOTTOM_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_LEFT_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_RIGHT_BORDER, aEmptyBorder);
+ xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(sal_Int32(100)));
+ xPropertySet->setPropertyValue(UNO_NAME_SIZE_TYPE, uno::Any(text::SizeType::FIX));
+ xPropertySet->setPropertyValue(UNO_NAME_SURROUND, uno::Any(text::WrapTextMode_THROUGH));
+ // Add a new name to it
+ uno::Reference<container::XNamed> xNamed(xNew, uno::UNO_QUERY);
+ assert(!xNamed->getName().isEmpty());
+ (void)xNamed;
+ // And sync. properties.
+ uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(xShape->getSize()), pObj);
+ uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
+ syncProperty(pShapeFormat, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ xShapePropertySet->getPropertyValue(UNO_NAME_ANCHOR_TYPE), pObj);
+ syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT), pObj);
+ syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_RELATION), pObj);
+ syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_ORIENT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT), pObj);
+ syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_RELATION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_RELATION), pObj);
+ syncProperty(pShapeFormat, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_HORI_ORIENT_POSITION), pObj);
+ syncProperty(pShapeFormat, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ xShapePropertySet->getPropertyValue(UNO_NAME_VERT_ORIENT_POSITION), pObj);
+ syncProperty(pShapeFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT,
+ xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT), pObj);
+ drawing::TextVerticalAdjust aVertAdj = drawing::TextVerticalAdjust_CENTER;
+ if ((uno::Reference<beans::XPropertyState>(xShape, uno::UNO_QUERY_THROW))
+ ->getPropertyState(UNO_NAME_TEXT_VERT_ADJUST)
+ != beans::PropertyState::PropertyState_DEFAULT_VALUE)
+ {
+ aVertAdj = xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_VERT_ADJUST)
+ .get<drawing::TextVerticalAdjust>();
+ }
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_VERT_ADJUST, uno::Any(aVertAdj));
+ text::WritingMode eMode;
+ if (xShapePropertySet->getPropertyValue(UNO_NAME_TEXT_WRITINGMODE) >>= eMode)
+ syncProperty(pShapeFormat, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
+
+ // Do sync for the new textframe.
+ synchronizeGroupTextBoxProperty(&changeAnchor, pShapeFormat, pObj);
+ synchronizeGroupTextBoxProperty(&syncTextBoxSize, pShapeFormat, pObj);
+
+ updateTextBoxMargin(pObj);
+}
+
+void SwTextBoxHelper::destroy(const SwFrameFormat* pShape, const SdrObject* pObject)
+{
+ // If a TextBox was enabled previously
+ auto& pTextBox = pShape->GetOtherTextBoxFormats();
+ if (pTextBox)
+ {
+ // Unlink the TextBox's text range from the original shape.
+ // Delete the associated TextFrame.
+ pTextBox->DelTextBox(pObject, true);
+ }
+}
+
+bool SwTextBoxHelper::isTextBox(const SwFrameFormat* pFormat, sal_uInt16 nType,
+ const SdrObject* pObject)
+{
+ SolarMutexGuard aGuard;
+ assert(nType == RES_FLYFRMFMT || nType == RES_DRAWFRMFMT);
+ if (!pFormat || pFormat->Which() != nType)
+ return false;
+
+ auto& pTextBox = pFormat->GetOtherTextBoxFormats();
+ if (!pTextBox)
+ return false;
+
+ if (nType == RES_DRAWFRMFMT)
+ {
+ if (pObject)
+ return pTextBox->GetTextBox(pObject);
+ if (auto pObj = pFormat->FindRealSdrObject())
+ return pTextBox->GetTextBox(pObj);
+ }
+
+ if (nType == RES_FLYFRMFMT)
+ {
+ return pTextBox->GetOwnerShape();
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::hasTextFrame(const SdrObject* pObj)
+{
+ if (!pObj)
+ return false;
+
+ uno::Reference<drawing::XShape> xShape(pObj->getWeakUnoShape().get(), uno::UNO_QUERY);
+ if (!xShape)
+ return false;
+ return SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+}
+
+sal_Int32 SwTextBoxHelper::getCount(SdrPage const* pPage)
+{
+ sal_Int32 nRet = 0;
+ for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
+ {
+ SdrObject* p = pPage->GetObj(i);
+ if (p && p->IsTextBox())
+ continue;
+ ++nRet;
+ }
+ return nRet;
+}
+
+sal_Int32 SwTextBoxHelper::getCount(const SwDoc& rDoc)
+{
+ sal_Int32 nRet = 0;
+ const SwFrameFormats& rSpzFrameFormats = *rDoc.GetSpzFrameFormats();
+ for (const auto pFormat : rSpzFrameFormats)
+ {
+ if (isTextBox(pFormat, RES_FLYFRMFMT))
+ ++nRet;
+ }
+ return nRet;
+}
+
+uno::Any SwTextBoxHelper::getByIndex(SdrPage const* pPage, sal_Int32 nIndex)
+{
+ if (nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+
+ SdrObject* pRet = nullptr;
+ sal_Int32 nCount = 0; // Current logical index.
+ for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
+ {
+ SdrObject* p = pPage->GetObj(i);
+ if (p && p->IsTextBox())
+ continue;
+ if (nCount == nIndex)
+ {
+ pRet = p;
+ break;
+ }
+ ++nCount;
+ }
+
+ if (!pRet)
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any(uno::Reference<drawing::XShape>(pRet->getUnoShape(), uno::UNO_QUERY));
+}
+
+sal_Int32 SwTextBoxHelper::getOrdNum(const SdrObject* pObject)
+{
+ if (const SdrPage* pPage = pObject->getSdrPageFromSdrObject())
+ {
+ sal_Int32 nOrder = 0; // Current logical order.
+ for (std::size_t i = 0; i < pPage->GetObjCount(); ++i)
+ {
+ SdrObject* p = pPage->GetObj(i);
+ if (p && p->IsTextBox())
+ continue;
+ if (p == pObject)
+ return nOrder;
+ ++nOrder;
+ }
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxHelper::getOrdNum: no page or page doesn't contain the object");
+ return pObject->GetOrdNum();
+}
+
+void SwTextBoxHelper::getShapeWrapThrough(const SwFrameFormat* pTextBox, bool& rWrapThrough)
+{
+ SwFrameFormat* pShape = SwTextBoxHelper::getOtherTextBoxFormat(pTextBox, RES_FLYFRMFMT);
+ if (pShape)
+ rWrapThrough = pShape->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
+}
+
+SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(const SwFrameFormat* pFormat,
+ sal_uInt16 nType, const SdrObject* pObject)
+{
+ SolarMutexGuard aGuard;
+ if (!isTextBox(pFormat, nType, pObject))
+ return nullptr;
+
+ if (nType == RES_DRAWFRMFMT)
+ {
+ if (pObject)
+ return pFormat->GetOtherTextBoxFormats()->GetTextBox(pObject);
+ if (pFormat->FindRealSdrObject())
+ return pFormat->GetOtherTextBoxFormats()->GetTextBox(pFormat->FindRealSdrObject());
+ return nullptr;
+ }
+ if (nType == RES_FLYFRMFMT)
+ {
+ return pFormat->GetOtherTextBoxFormats()->GetOwnerShape();
+ }
+ return nullptr;
+}
+
+SwFrameFormat* SwTextBoxHelper::getOtherTextBoxFormat(uno::Reference<drawing::XShape> const& xShape)
+{
+ auto pShape = dynamic_cast<SwXShape*>(xShape.get());
+ if (!pShape)
+ return nullptr;
+
+ SwFrameFormat* pFormat = pShape->GetFrameFormat();
+ return getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT,
+ SdrObject::getSdrObjectFromXShape(xShape));
+}
+
+uno::Reference<text::XTextFrame>
+SwTextBoxHelper::getUnoTextFrame(uno::Reference<drawing::XShape> const& xShape)
+{
+ if (xShape)
+ {
+ auto pFrameFormat = SwTextBoxHelper::getOtherTextBoxFormat(xShape);
+ if (pFrameFormat)
+ {
+ auto pSdrObj = pFrameFormat->FindSdrObject();
+ if (pSdrObj)
+ {
+ return { pSdrObj->getUnoShape(), uno::UNO_QUERY };
+ }
+ }
+ }
+ return {};
+}
+
+template <typename T>
+static void lcl_queryInterface(const SwFrameFormat* pShape, uno::Any& rAny, SdrObject* pObj)
+{
+ if (SwFrameFormat* pFormat
+ = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ uno::Reference<T> const xInterface(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+ rAny <<= xInterface;
+ }
+}
+
+uno::Any SwTextBoxHelper::queryInterface(const SwFrameFormat* pShape, const uno::Type& rType,
+ SdrObject* pObj)
+{
+ uno::Any aRet;
+
+ if (rType == cppu::UnoType<css::text::XTextAppend>::get())
+ {
+ lcl_queryInterface<text::XTextAppend>(pShape, aRet, pObj);
+ }
+ else if (rType == cppu::UnoType<css::text::XText>::get())
+ {
+ lcl_queryInterface<text::XText>(pShape, aRet, pObj);
+ }
+ else if (rType == cppu::UnoType<css::text::XTextRange>::get())
+ {
+ lcl_queryInterface<text::XTextRange>(pShape, aRet, pObj);
+ }
+
+ return aRet;
+}
+
+tools::Rectangle SwTextBoxHelper::getRelativeTextRectangle(SdrObject* pShape)
+{
+ tools::Rectangle aRet;
+ aRet.SetEmpty();
+
+ assert(pShape);
+
+ auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pShape);
+ if (pCustomShape)
+ {
+ // Need to temporarily release the lock acquired in
+ // SdXMLShapeContext::AddShape(), otherwise we get an empty rectangle,
+ // see EnhancedCustomShapeEngine::getTextBounds().
+ uno::Reference<document::XActionLockable> xLockable(pCustomShape->getUnoShape(),
+ uno::UNO_QUERY);
+ sal_Int16 nLocks = 0;
+ if (xLockable.is())
+ nLocks = xLockable->resetActionLocks();
+ pCustomShape->GetTextBounds(aRet);
+ if (nLocks)
+ xLockable->setActionLocks(nLocks);
+ }
+ else if (pShape)
+ {
+ // fallback - get *any* bound rect we can possibly get hold of
+ aRet = pShape->GetCurrentBoundRect();
+ }
+
+ if (pShape)
+ {
+ // Relative, so count the logic (reference) rectangle, see the EnhancedCustomShape2d ctor.
+ Point aPoint(pShape->GetSnapRect().Center());
+ Size aSize(pShape->GetLogicRect().GetSize());
+ aPoint.AdjustX(-(aSize.Width() / 2));
+ aPoint.AdjustY(-(aSize.Height() / 2));
+ tools::Rectangle aLogicRect(aPoint, aSize);
+ aRet.Move(-1 * aLogicRect.Left(), -1 * aLogicRect.Top());
+ }
+
+ return aRet;
+}
+
+void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, std::u16string_view rPropertyName,
+ const css::uno::Any& rValue, SdrObject* pObj)
+{
+ // Textframes does not have valid horizontal adjust property, so map it to paragraph adjust property
+ if (rPropertyName == UNO_NAME_TEXT_HORZADJUST)
+ {
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ auto xTextFrame = SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat);
+ uno::Reference<text::XTextCursor> xCursor = xTextFrame->getText()->createTextCursor();
+
+ // Select all paragraphs in the textframe
+ xCursor->gotoStart(false);
+ xCursor->gotoEnd(true);
+ uno::Reference<beans::XPropertySet> xFrameParaProps(xCursor, uno::UNO_QUERY);
+
+ // And simply map the property
+ const auto eValue = rValue.get<drawing::TextHorizontalAdjust>();
+ switch (eValue)
+ {
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_CENTER:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_CENTER)); //3
+ break;
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_LEFT:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_LEFT)); //0
+ break;
+ case drawing::TextHorizontalAdjust::TextHorizontalAdjust_RIGHT:
+ xFrameParaProps->setPropertyValue(
+ UNO_NAME_PARA_ADJUST,
+ uno::Any(style::ParagraphAdjust::ParagraphAdjust_RIGHT)); //1
+ break;
+ default:
+ SAL_WARN("sw.core",
+ "SwTextBoxHelper::syncProperty: unhandled TextHorizontalAdjust: "
+ << static_cast<sal_Int32>(eValue));
+ break;
+ }
+ return;
+ }
+
+ if (rPropertyName == u"CustomShapeGeometry")
+ {
+ // CustomShapeGeometry changes the textbox position offset and size, so adjust both.
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any());
+
+ SdrObject* pObject = pObj ? pObj : pShape->FindRealSdrObject();
+ if (pObject)
+ {
+ tools::Rectangle aRectangle(pObject->GetSnapRect());
+ syncProperty(pShape, RES_HORI_ORIENT, MID_HORIORIENT_POSITION,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Left()))));
+ syncProperty(pShape, RES_VERT_ORIENT, MID_VERTORIENT_POSITION,
+ uno::Any(static_cast<sal_Int32>(convertTwipToMm100(aRectangle.Top()))));
+ }
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ comphelper::SequenceAsHashMap aCustomShapeGeometry(rValue);
+ auto it = aCustomShapeGeometry.find("TextPreRotateAngle");
+ if (it == aCustomShapeGeometry.end())
+ {
+ it = aCustomShapeGeometry.find("TextRotateAngle");
+ }
+
+ if (it != aCustomShapeGeometry.end())
+ {
+ auto nAngle = it->second.has<sal_Int32>() ? it->second.get<sal_Int32>() : 0;
+ if (nAngle == 0)
+ {
+ nAngle = it->second.has<double>() ? it->second.get<double>() : 0;
+ }
+
+ sal_Int16 nDirection = 0;
+ switch (nAngle)
+ {
+ case -90:
+ nDirection = text::WritingMode2::TB_RL;
+ break;
+ case -270:
+ nDirection = text::WritingMode2::BT_LR;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled property value: "
+ "CustomShapeGeometry:TextPreRotateAngle: "
+ << nAngle);
+ break;
+ }
+
+ if (nDirection)
+ {
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(nDirection), pObj);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_TEXT_VERT_ADJUST)
+ syncProperty(pShape, RES_TEXT_VERT_ADJUST, 0, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_AUTOGROWHEIGHT)
+ syncProperty(pShape, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_LEFTDIST)
+ syncProperty(pShape, RES_BOX, LEFT_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_RIGHTDIST)
+ syncProperty(pShape, RES_BOX, RIGHT_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_UPPERDIST)
+ syncProperty(pShape, RES_BOX, TOP_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_LOWERDIST)
+ syncProperty(pShape, RES_BOX, BOTTOM_BORDER_DISTANCE, rValue, pObj);
+ else if (rPropertyName == UNO_NAME_TEXT_WRITINGMODE)
+ {
+ text::WritingMode eMode;
+ sal_Int16 eMode2;
+ if (rValue >>= eMode)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(sal_Int16(eMode)), pObj);
+ else if (rValue >>= eMode2)
+ syncProperty(pShape, RES_FRAMEDIR, 0, uno::Any(eMode2), pObj);
+ }
+ else
+ SAL_INFO("sw.core", "SwTextBoxHelper::syncProperty: unhandled property: "
+ << static_cast<OUString>(rPropertyName));
+}
+
+void SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
+ css::uno::Any& rValue)
+{
+ if (!pShape)
+ return;
+
+ nMemberID &= ~CONVERT_TWIPS;
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
+ if (!pFormat)
+ return;
+
+ if (nWID != RES_CHAIN)
+ return;
+
+ switch (nMemberID)
+ {
+ case MID_CHAIN_PREVNAME:
+ case MID_CHAIN_NEXTNAME:
+ {
+ const SwFormatChain& rChain = pFormat->GetChain();
+ rChain.QueryValue(rValue, nMemberID);
+ }
+ break;
+ case MID_CHAIN_NAME:
+ rValue <<= pFormat->GetName();
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::getProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID));
+ break;
+ }
+}
+
+css::uno::Any SwTextBoxHelper::getProperty(SwFrameFormat const* pShape, const OUString& rPropName)
+{
+ if (!pShape)
+ return {};
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT);
+ if (!pFormat)
+ return {};
+
+ uno::Reference<beans::XPropertySet> const xPropertySet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+
+ return xPropertySet->getPropertyValue(rPropName);
+}
+
+void SwTextBoxHelper::syncProperty(SwFrameFormat* pShape, sal_uInt16 nWID, sal_uInt8 nMemberID,
+ const css::uno::Any& rValue, SdrObject* pObj)
+{
+ // No shape yet? Then nothing to do, initial properties are set by create().
+ if (!pShape)
+ return;
+
+ uno::Any aValue(rValue);
+ nMemberID &= ~CONVERT_TWIPS;
+
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ OUString aPropertyName;
+ bool bAdjustX = false;
+ bool bAdjustY = false;
+ bool bAdjustSize = false;
+ switch (nWID)
+ {
+ case RES_HORI_ORIENT:
+ switch (nMemberID)
+ {
+ case MID_HORIORIENT_ORIENT:
+ aPropertyName = UNO_NAME_HORI_ORIENT;
+ break;
+ case MID_HORIORIENT_RELATION:
+ if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ aPropertyName = UNO_NAME_HORI_ORIENT_RELATION;
+ else
+ return;
+ break;
+ case MID_HORIORIENT_POSITION:
+ aPropertyName = UNO_NAME_HORI_ORIENT_POSITION;
+ bAdjustX = true;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_LR_SPACE:
+ {
+ switch (nMemberID)
+ {
+ case MID_L_MARGIN:
+ aPropertyName = UNO_NAME_LEFT_MARGIN;
+ break;
+ case MID_R_MARGIN:
+ aPropertyName = UNO_NAME_RIGHT_MARGIN;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ }
+ case RES_VERT_ORIENT:
+ switch (nMemberID)
+ {
+ case MID_VERTORIENT_ORIENT:
+ aPropertyName = UNO_NAME_VERT_ORIENT;
+ break;
+ case MID_VERTORIENT_RELATION:
+ if (pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ aPropertyName = UNO_NAME_VERT_ORIENT_RELATION;
+ else
+ return;
+ break;
+ case MID_VERTORIENT_POSITION:
+ aPropertyName = UNO_NAME_VERT_ORIENT_POSITION;
+ bAdjustY = true;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_FRM_SIZE:
+ switch (nMemberID)
+ {
+ case MID_FRMSIZE_WIDTH_TYPE:
+ aPropertyName = UNO_NAME_WIDTH_TYPE;
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ aPropertyName = UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT;
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ aPropertyName = UNO_NAME_RELATIVE_HEIGHT_RELATION;
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ aPropertyName = UNO_NAME_RELATIVE_WIDTH_RELATION;
+ break;
+ default:
+ aPropertyName = UNO_NAME_SIZE;
+ bAdjustSize = true;
+ break;
+ }
+ break;
+ case RES_ANCHOR:
+ switch (nMemberID)
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+ {
+ changeAnchor(pShape, pObj);
+ return;
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case FN_TEXT_RANGE:
+ {
+ uno::Reference<text::XTextRange> xRange;
+ rValue >>= xRange;
+ SwUnoInternalPaM aInternalPaM(*pFormat->GetDoc());
+ if (sw::XTextRangeToSwPaM(aInternalPaM, xRange))
+ {
+ SwFormatAnchor aAnchor(pFormat->GetAnchor());
+ aAnchor.SetAnchor(aInternalPaM.Start());
+ pFormat->SetFormatAttr(aAnchor);
+ }
+ }
+ break;
+ case RES_CHAIN:
+ switch (nMemberID)
+ {
+ case MID_CHAIN_PREVNAME:
+ aPropertyName = UNO_NAME_CHAIN_PREV_NAME;
+ break;
+ case MID_CHAIN_NEXTNAME:
+ aPropertyName = UNO_NAME_CHAIN_NEXT_NAME;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_TEXT_VERT_ADJUST:
+ aPropertyName = UNO_NAME_TEXT_VERT_ADJUST;
+ break;
+ case RES_BOX:
+ switch (nMemberID)
+ {
+ case LEFT_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_LEFT_BORDER_DISTANCE;
+ break;
+ case RIGHT_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_RIGHT_BORDER_DISTANCE;
+ break;
+ case TOP_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_TOP_BORDER_DISTANCE;
+ break;
+ case BOTTOM_BORDER_DISTANCE:
+ aPropertyName = UNO_NAME_BOTTOM_BORDER_DISTANCE;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ case RES_OPAQUE:
+ aPropertyName = UNO_NAME_OPAQUE;
+ break;
+ case RES_FRAMEDIR:
+ aPropertyName = UNO_NAME_WRITING_MODE;
+ break;
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ switch (nMemberID)
+ {
+ case MID_ALLOW_OVERLAP:
+ aPropertyName = UNO_NAME_ALLOW_OVERLAP;
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID)
+ << " (which-id: " << nWID << ")");
+ break;
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncProperty: unhandled which-id: "
+ << nWID << " (member-id: "
+ << o3tl::narrowing<sal_uInt16>(nMemberID) << ")");
+ break;
+ }
+
+ if (aPropertyName.isEmpty())
+ return;
+
+ // Position/size should be the text position/size, not the shape one as-is.
+ if (bAdjustX || bAdjustY || bAdjustSize)
+ {
+ changeAnchor(pShape, pObj);
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ {
+ if (bAdjustX || bAdjustY)
+ {
+ sal_Int32 nValue;
+ if (aValue >>= nValue)
+ {
+ nValue += convertTwipToMm100(bAdjustX ? aRect.Left() : aRect.Top());
+ aValue <<= nValue;
+ }
+ }
+ else if (bAdjustSize)
+ {
+ awt::Size aSize(convertTwipToMm100(aRect.getWidth()),
+ convertTwipToMm100(aRect.getHeight()));
+ aValue <<= aSize;
+ }
+ }
+ }
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ uno::Reference<beans::XPropertySet> const xPropertySet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+ xPropertySet->setPropertyValue(aPropertyName, aValue);
+}
+
+void SwTextBoxHelper::saveLinks(const SwFrameFormats& rFormats,
+ std::map<const SwFrameFormat*, const SwFrameFormat*>& rLinks)
+{
+ for (const auto pFormat : rFormats)
+ {
+ if (SwFrameFormat* pTextBox = getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
+ rLinks[pFormat] = pTextBox;
+ }
+}
+
+void SwTextBoxHelper::restoreLinks(std::set<ZSortFly>& rOld, std::vector<SwFrameFormat*>& rNew,
+ SavedLink& rSavedLinks)
+{
+ std::size_t i = 0;
+ for (const auto& rIt : rOld)
+ {
+ auto aTextBoxIt = rSavedLinks.find(rIt.GetFormat());
+ if (aTextBoxIt != rSavedLinks.end())
+ {
+ std::size_t j = 0;
+ for (const auto& rJt : rOld)
+ {
+ if (rJt.GetFormat() == aTextBoxIt->second)
+ rNew[i]->SetFormatAttr(rNew[j]->GetContent());
+ ++j;
+ }
+ }
+ ++i;
+ }
+}
+
+text::TextContentAnchorType SwTextBoxHelper::mapAnchorType(const RndStdIds& rAnchorID)
+{
+ text::TextContentAnchorType aAnchorType;
+ switch (rAnchorID)
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AS_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PAGE;
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_FRAME;
+ break;
+ default:
+ aAnchorType = text::TextContentAnchorType::TextContentAnchorType_AT_PARAGRAPH;
+ SAL_WARN("sw.core", "SwTextBoxHelper::mapAnchorType: Unknown AnchorType!");
+ break;
+ }
+ return aAnchorType;
+}
+
+void SwTextBoxHelper::syncFlyFrameAttr(SwFrameFormat& rShape, SfxItemSet const& rSet,
+ SdrObject* pObj)
+{
+ SwFrameFormat* pFormat = getOtherTextBoxFormat(&rShape, RES_DRAWFRMFMT, pObj);
+ if (!pFormat)
+ return;
+
+ const bool bInlineAnchored = rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
+ const bool bLayoutInCell
+ = rShape.GetFollowTextFlow().GetValue() && rShape.GetAnchor().GetContentAnchor()
+ && rShape.GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode();
+ SfxItemSet aTextBoxSet(pFormat->GetDoc()->GetAttrPool(), aFrameFormatSetRange);
+
+ SfxItemIter aIter(rSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+
+ do
+ {
+ switch (pItem->Which())
+ {
+ case RES_VERT_ORIENT:
+ {
+ // The new position can be with anchor changing so sync it!
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
+ pObj);
+ if (bInlineAnchored || bLayoutInCell)
+ return;
+ SwFormatVertOrient aOrient(pItem->StaticWhichCast(RES_VERT_ORIENT));
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ aOrient.SetPos(aOrient.GetPos() + aRect.Top());
+
+ if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rShape.GetAnchor().GetPageNum() != 0)
+ aOrient.SetRelationOrient(rShape.GetVertOrient().GetRelationOrient());
+ aTextBoxSet.Put(aOrient);
+
+ // restore height (shrunk for extending beyond the page bottom - tdf#91260)
+ SwFormatFrameSize aSize(pFormat->GetFrameSize());
+ if (!aRect.IsEmpty())
+ {
+ aSize.SetHeight(aRect.getHeight());
+ aTextBoxSet.Put(aSize);
+ }
+ }
+ break;
+ case RES_HORI_ORIENT:
+ {
+ // The new position can be with anchor changing so sync it!
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, uno::Any(aNewAnchorType),
+ pObj);
+ if (bInlineAnchored || bLayoutInCell)
+ return;
+ SwFormatHoriOrient aOrient(pItem->StaticWhichCast(RES_HORI_ORIENT));
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ aOrient.SetPos(aOrient.GetPos() + aRect.Left());
+
+ if (rShape.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rShape.GetAnchor().GetPageNum() != 0)
+ aOrient.SetRelationOrient(rShape.GetHoriOrient().GetRelationOrient());
+ aTextBoxSet.Put(aOrient);
+ }
+ break;
+ case RES_FRM_SIZE:
+ {
+ // In case the shape got resized, then we need to adjust both
+ // the position and the size of the textbox (e.g. larger
+ // rounded edges of a rectangle -> need to push right/down the
+ // textbox).
+ SwFormatVertOrient aVertOrient(rShape.GetVertOrient());
+ SwFormatHoriOrient aHoriOrient(rShape.GetHoriOrient());
+ SwFormatFrameSize aSize(pFormat->GetFrameSize());
+
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : rShape.FindRealSdrObject());
+ if (!aRect.IsEmpty())
+ {
+ if (!bInlineAnchored)
+ {
+ aVertOrient.SetPos(
+ (pObj ? pObj->GetRelativePos().getX() : aVertOrient.GetPos())
+ + aRect.Top());
+ aHoriOrient.SetPos(
+ (pObj ? pObj->GetRelativePos().getY() : aHoriOrient.GetPos())
+ + aRect.Left());
+
+ aTextBoxSet.Put(aVertOrient);
+ aTextBoxSet.Put(aHoriOrient);
+ }
+
+ aSize.SetWidth(aRect.getWidth());
+ aSize.SetHeight(aRect.getHeight());
+ aTextBoxSet.Put(aSize);
+ }
+ }
+ break;
+ case RES_ANCHOR:
+ {
+ if (pItem->StaticWhichCast(RES_ANCHOR) == rShape.GetAnchor())
+ // the anchor have to be synced
+ {
+ const text::TextContentAnchorType aNewAnchorType
+ = mapAnchorType(rShape.GetAnchor().GetAnchorId());
+ syncProperty(&rShape, RES_ANCHOR, MID_ANCHOR_ANCHORTYPE,
+ uno::Any(aNewAnchorType), pObj);
+ }
+ else
+ {
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: The anchor of the "
+ "shape different from the textframe!");
+ }
+ }
+ break;
+ default:
+ SAL_WARN("sw.core", "SwTextBoxHelper::syncFlyFrameAttr: unhandled which-id: "
+ << pItem->Which());
+ break;
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem && (0 != pItem->Which()));
+
+ if (aTextBoxSet.Count())
+ {
+ auto aGuard = SwTextBoxLockGuard(*rShape.GetOtherTextBoxFormats());
+ pFormat->SetFormatAttr(aTextBoxSet);
+ }
+ DoTextBoxZOrderCorrection(&rShape, pObj);
+}
+
+void SwTextBoxHelper::updateTextBoxMargin(SdrObject* pObj)
+{
+ if (!pObj)
+ return;
+ uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY);
+ if (!xShape)
+ return;
+ uno::Reference<beans::XPropertySet> const xPropertySet(xShape, uno::UNO_QUERY);
+
+ auto pParentFormat = getOtherTextBoxFormat(getOtherTextBoxFormat(xShape), RES_FLYFRMFMT);
+ if (!pParentFormat)
+ return;
+
+ // Sync the padding
+ syncProperty(pParentFormat, UNO_NAME_TEXT_LEFTDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_LEFTDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_RIGHTDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_RIGHTDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_UPPERDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_UPPERDIST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_LOWERDIST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_LOWERDIST), pObj);
+
+ // Sync the text aligning
+ syncProperty(pParentFormat, UNO_NAME_TEXT_VERTADJUST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_VERTADJUST), pObj);
+ syncProperty(pParentFormat, UNO_NAME_TEXT_HORZADJUST,
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_HORZADJUST), pObj);
+
+ // tdf137803: Sync autogrow:
+ const bool bIsAutoGrow
+ = xPropertySet->getPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT).get<bool>();
+ const bool bIsAutoWrap = xPropertySet->getPropertyValue(UNO_NAME_TEXT_WORDWRAP).get<bool>();
+
+ syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_IS_AUTO_HEIGHT, uno::Any(bIsAutoGrow),
+ pObj);
+
+ syncProperty(pParentFormat, RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE,
+ uno::Any(bIsAutoWrap ? text::SizeType::FIX : text::SizeType::MIN), pObj);
+
+ changeAnchor(pParentFormat, pObj);
+ DoTextBoxZOrderCorrection(pParentFormat, pObj);
+}
+
+bool SwTextBoxHelper::changeAnchor(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ if (!isAnchorSyncNeeded(pShape, pFormat))
+ {
+ doTextBoxPositioning(pShape, pObj);
+ DoTextBoxZOrderCorrection(pShape, pObj);
+ if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ && pFormat->GetVertOrient().GetRelationOrient() != text::RelOrientation::PRINT_AREA)
+ {
+ SwFormatVertOrient aTmp = pFormat->GetVertOrient();
+ aTmp.SetRelationOrient(text::RelOrientation::PRINT_AREA);
+ pFormat->SetFormatAttr(aTmp);
+ }
+
+ return false;
+ }
+
+ const SwFormatAnchor& rOldAnch = pFormat->GetAnchor();
+ const SwFormatAnchor& rNewAnch = pShape->GetAnchor();
+
+ const auto pOldCnt = rOldAnch.GetContentAnchor();
+ const auto pNewCnt = rNewAnch.GetContentAnchor();
+
+ const uno::Any aShapeHorRelOrient(pShape->GetHoriOrient().GetRelationOrient());
+
+ try
+ {
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+ uno::Reference<beans::XPropertySet> const xPropertySet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat), uno::UNO_QUERY);
+ if (pOldCnt && rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rNewAnch.GetPageNum())
+ {
+ uno::Any aValue(text::TextContentAnchorType_AT_PAGE);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, aShapeHorRelOrient);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_PAGE_NO,
+ uno::Any(rNewAnch.GetPageNum()));
+ }
+ else if (rOldAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE && pNewCnt)
+ {
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(pNewCnt);
+ uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::CHAR));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::PRINT_AREA));
+ SwFormatAnchor aPos(pFormat->GetAnchor());
+ aPos.SetAnchor(pNewCnt);
+ pFormat->SetFormatAttr(aPos);
+ }
+ else
+ {
+ uno::Any aValue(mapAnchorType(rNewAnch.GetAnchorId()));
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ aShapeHorRelOrient);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ pFormat->SetFormatAttr(rNewAnch);
+ }
+ }
+ else
+ {
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(pNewCnt);
+ uno::Any aValue(text::TextContentAnchorType_AT_CHARACTER);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, aValue);
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::CHAR));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION,
+ uno::Any(text::RelOrientation::PRINT_AREA));
+ SwFormatAnchor aPos(pFormat->GetAnchor());
+ aPos.SetAnchor(pNewCnt);
+ pFormat->SetFormatAttr(aPos);
+ }
+ else
+ {
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION,
+ aShapeHorRelOrient);
+ if (rNewAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rNewAnch.GetPageNum() == 0)
+ {
+ pFormat->SetFormatAttr(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, 1));
+ }
+ else
+ pFormat->SetFormatAttr(pShape->GetAnchor());
+ }
+ }
+ }
+ catch (uno::Exception& e)
+ {
+ SAL_WARN("sw.core", "SwTextBoxHelper::changeAnchor(): " << e.Message);
+ }
+
+ doTextBoxPositioning(pShape, pObj);
+ DoTextBoxZOrderCorrection(pShape, pObj);
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::doTextBoxPositioning(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ // Set the position of the textboxes according to the position of its shape-pair
+ const bool bIsGroupObj = (pObj != pShape->FindRealSdrObject()) && pObj;
+ if (auto pFormat = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ // Do not create undo entry for the positioning
+ ::sw::UndoGuard const UndoGuard(pShape->GetDoc()->GetIDocumentUndoRedo());
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ // Special treatment for AS_CHAR textboxes:
+ if (pShape->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ // Get the text area of the shape
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+
+ // Get the left spacing of the text area of the shape
+ auto nLeftSpace = pShape->GetLRSpace().GetLeft();
+
+ // Set the textbox position at the X-axis:
+ SwFormatHoriOrient aNewHOri(pFormat->GetHoriOrient());
+ if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
+ aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
+ aNewHOri.SetPos(aRect.Left() + nLeftSpace
+ + (bIsGroupObj ? pObj->GetRelativePos().getX() : 0));
+ SwFormatVertOrient aNewVOri(pFormat->GetVertOrient());
+
+ // Special handling of group textboxes
+ if (bIsGroupObj)
+ {
+ // There are the following cases:
+ // case 1: The textbox should be in that position where the shape is.
+ // case 2: The shape has negative offset so that have to be subtracted
+ // case 3: The shape and its parent shape also has negative offset, so subtract
+ aNewVOri.SetPos(
+ ((pObj->GetRelativePos().getY()) > 0
+ ? (pShape->GetVertOrient().GetPos() > 0
+ ? pObj->GetRelativePos().getY()
+ : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos())
+ : (pShape->GetVertOrient().GetPos() > 0
+ ? 0 // Is this can be a variation?
+ : pObj->GetRelativePos().getY() - pShape->GetVertOrient().GetPos()))
+ + aRect.Top());
+ }
+ else
+ {
+ // Simple textboxes: vertical position equals to the vertical offset of the shape
+ aNewVOri.SetPos(
+ ((pShape->GetVertOrient().GetPos()) > 0 ? pShape->GetVertOrient().GetPos() : 0)
+ + aRect.Top());
+ }
+
+ // Special cases when the shape is aligned to the line
+ if (pShape->GetVertOrient().GetVertOrient() != text::VertOrientation::NONE)
+ {
+ aNewVOri.SetVertOrient(text::VertOrientation::NONE);
+ switch (pShape->GetVertOrient().GetVertOrient())
+ {
+ // Top aligned shape
+ case text::VertOrientation::TOP:
+ case text::VertOrientation::CHAR_TOP:
+ case text::VertOrientation::LINE_TOP:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos() - pShape->GetFrameSize().GetHeight());
+ break;
+ }
+ // Bottom aligned shape
+ case text::VertOrientation::BOTTOM:
+ case text::VertOrientation::CHAR_BOTTOM:
+ case text::VertOrientation::LINE_BOTTOM:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos() + pShape->GetFrameSize().GetHeight());
+ break;
+ }
+ // Center aligned shape
+ case text::VertOrientation::CENTER:
+ case text::VertOrientation::CHAR_CENTER:
+ case text::VertOrientation::LINE_CENTER:
+ {
+ aNewVOri.SetPos(aNewVOri.GetPos()
+ + std::lroundf(pShape->GetFrameSize().GetHeight() / 2));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ pFormat->SetFormatAttr(aNewHOri);
+ pFormat->SetFormatAttr(aNewVOri);
+ }
+ // Other cases when the shape has different anchor from AS_CHAR
+ else
+ {
+ // Text area of the shape
+ tools::Rectangle aRect
+ = getRelativeTextRectangle(pObj ? pObj : pShape->FindRealSdrObject());
+
+ // X Offset of the shape spacing
+ auto nLeftSpace = pShape->GetLRSpace().GetLeft();
+
+ // Set the same position as the (child) shape has
+ SwFormatHoriOrient aNewHOri(pShape->GetHoriOrient());
+ if (bIsGroupObj && aNewHOri.GetHoriOrient() != text::HoriOrientation::NONE)
+ aNewHOri.SetHoriOrient(text::HoriOrientation::NONE);
+
+ aNewHOri.SetPos(
+ (bIsGroupObj && pObj ? pObj->GetRelativePos().getX() : aNewHOri.GetPos())
+ + aRect.Left());
+ SwFormatVertOrient aNewVOri(pShape->GetVertOrient());
+ aNewVOri.SetPos(
+ (bIsGroupObj && pObj ? pObj->GetRelativePos().getY() : aNewVOri.GetPos())
+ + aRect.Top());
+
+ // Get the distance of the child shape inside its parent
+ const auto& nInshapePos
+ = pObj ? pObj->GetRelativePos() - pShape->FindRealSdrObject()->GetRelativePos()
+ : Point();
+
+ // Special case: the shape has relative position from the page
+ if (pShape->GetHoriOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
+ {
+ aNewHOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ aNewHOri.SetPos(pShape->GetHoriOrient().GetPos() + nInshapePos.getX()
+ + aRect.Left());
+ }
+
+ if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ && pShape->GetAnchor().GetAnchorId() != RndStdIds::FLY_AT_PAGE)
+ {
+ aNewVOri.SetRelationOrient(text::RelOrientation::PAGE_FRAME);
+ aNewVOri.SetPos(pShape->GetVertOrient().GetPos() + nInshapePos.getY()
+ + aRect.Top());
+ }
+
+ // Other special case: shape is inside a table or floating table following the text flow
+ if (pShape->GetFollowTextFlow().GetValue() && pShape->GetAnchor().GetContentAnchor()
+ && pShape->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode())
+ {
+ // Table position
+ Point nTableOffset;
+ // Floating table
+ if (auto pFly = pShape->GetAnchor()
+ .GetContentAnchor()
+ ->nNode.GetNode()
+ .FindTableNode()
+ ->FindFlyStartNode())
+ {
+ if (auto pFlyFormat = pFly->GetFlyFormat())
+ {
+ nTableOffset.setX(pFlyFormat->GetHoriOrient().GetPos());
+ nTableOffset.setY(pFlyFormat->GetVertOrient().GetPos());
+ }
+ }
+ else
+ // Normal table
+ {
+ auto pTableNode
+ = pShape->GetAnchor().GetContentAnchor()->nNode.GetNode().FindTableNode();
+ if (auto pTableFormat = pTableNode->GetTable().GetFrameFormat())
+ {
+ nTableOffset.setX(pTableFormat->GetHoriOrient().GetPos());
+ nTableOffset.setY(pTableFormat->GetVertOrient().GetPos());
+ }
+ }
+
+ // Add the table positions to the textbox.
+ aNewHOri.SetPos(aNewHOri.GetPos() + nTableOffset.getX() + nLeftSpace);
+ if (pShape->GetVertOrient().GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ || pShape->GetVertOrient().GetRelationOrient()
+ == text::RelOrientation::PAGE_PRINT_AREA)
+ aNewVOri.SetPos(aNewVOri.GetPos() + nTableOffset.getY());
+ }
+
+ pFormat->SetFormatAttr(aNewHOri);
+ pFormat->SetFormatAttr(aNewVOri);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::syncTextBoxSize(SwFrameFormat* pShape, SdrObject* pObj)
+{
+ if (!pShape || !pObj)
+ return false;
+
+ if (auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj))
+ {
+ auto aGuard = SwTextBoxLockGuard(*pShape->GetOtherTextBoxFormats());
+ const auto& rSize = getRelativeTextRectangle(pObj).GetSize();
+ if (!rSize.IsEmpty())
+ {
+ SwFormatFrameSize aSize(pTextBox->GetFrameSize());
+ aSize.SetSize(rSize);
+ return pTextBox->SetFormatAttr(aSize);
+ }
+ }
+
+ return false;
+}
+
+bool SwTextBoxHelper::DoTextBoxZOrderCorrection(SwFrameFormat* pShape, const SdrObject* pObj)
+{
+ // TODO: do this with group shape textboxes.
+ SdrObject* pShpObj = nullptr;
+
+ pShpObj = pShape->FindRealSdrObject();
+
+ if (pShpObj)
+ {
+ auto pTextBox = getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT, pObj);
+ if (!pTextBox)
+ return false;
+ SdrObject* pFrmObj = pTextBox->FindRealSdrObject();
+ if (!pFrmObj)
+ {
+ // During loading there is no ready SdrObj for z-ordering, so create and cache it here
+ pFrmObj
+ = SwXTextFrame::GetOrCreateSdrObject(*dynamic_cast<SwFlyFrameFormat*>(pTextBox));
+ }
+ if (pFrmObj)
+ {
+ // Get the draw model from the doc
+ SwDrawModel* pDrawModel
+ = pShape->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
+ if (pDrawModel)
+ {
+ // Not really sure this will work on all pages, but it seems it will.
+ auto pPage = pDrawModel->GetPage(0);
+ // Recalc all Z-orders
+ pPage->RecalcObjOrdNums();
+ // Here is a counter avoiding running to in infinity:
+ sal_uInt16 nIterator = 0;
+ // If the shape is behind the frame, is good, but if there are some objects
+ // between of them that is wrong so put the frame exactly one level higher
+ // than the shape.
+ if (pFrmObj->GetOrdNum() > pShpObj->GetOrdNum())
+ pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pShpObj->GetOrdNum() + 1);
+ else
+ // Else, if the frame is behind the shape, bring to the front of it.
+ while (pFrmObj->GetOrdNum() <= pShpObj->GetOrdNum())
+ {
+ pPage->SetObjectOrdNum(pFrmObj->GetOrdNum(), pFrmObj->GetOrdNum() + 1);
+ // If there is any problem with the indexes, do not run over the infinity
+ if (pPage->GetObjCount() == pFrmObj->GetOrdNum())
+ break;
+ ++nIterator;
+ if (nIterator > 300)
+ break; // Do not run to infinity
+ }
+ pPage->RecalcObjOrdNums();
+ return true; // Success
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid Draw model for SdrObject for the shape!");
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid SdrObject for the frame!");
+ }
+ SAL_WARN("sw.core", "SwTextBoxHelper::DoTextBoxZOrderCorrection(): "
+ "No Valid SdrObject for the shape!");
+
+ return false;
+}
+
+void SwTextBoxHelper::synchronizeGroupTextBoxProperty(bool pFunc(SwFrameFormat*, SdrObject*),
+ SwFrameFormat* pFormat, SdrObject* pObj)
+{
+ if (auto pChildren = pObj->getChildrenOfSdrObject())
+ {
+ for (size_t i = 0; i < pChildren->GetObjCount(); ++i)
+ synchronizeGroupTextBoxProperty(pFunc, pFormat, pChildren->GetObj(i));
+ }
+ else
+ {
+ (*pFunc)(pFormat, pObj);
+ }
+}
+
+std::vector<SwFrameFormat*> SwTextBoxHelper::CollectTextBoxes(const SdrObject* pGroupObject,
+ SwFrameFormat* pFormat)
+{
+ std::vector<SwFrameFormat*> vRet;
+ if (auto pChildren = pGroupObject->getChildrenOfSdrObject())
+ {
+ for (size_t i = 0; i < pChildren->GetObjCount(); ++i)
+ {
+ auto pChildTextBoxes = CollectTextBoxes(pChildren->GetObj(i), pFormat);
+ for (auto& rChildTextBox : pChildTextBoxes)
+ vRet.push_back(rChildTextBox);
+ }
+ }
+ else
+ {
+ if (isTextBox(pFormat, RES_DRAWFRMFMT, pGroupObject))
+ vRet.push_back(getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pGroupObject));
+ }
+ return vRet;
+}
+
+bool SwTextBoxHelper::isAnchorSyncNeeded(const SwFrameFormat* pFirst, const SwFrameFormat* pSecond)
+{
+ if (!pFirst)
+ return false;
+
+ if (!pSecond)
+ return false;
+
+ if (pFirst == pSecond)
+ return false;
+
+ if (!pFirst->GetOtherTextBoxFormats())
+ return false;
+
+ if (!pSecond->GetOtherTextBoxFormats())
+ return false;
+
+ if (pFirst->GetOtherTextBoxFormats() != pSecond->GetOtherTextBoxFormats())
+ return false;
+
+ if (pFirst->GetOtherTextBoxFormats()->GetOwnerShape() == pSecond
+ || pFirst == pSecond->GetOtherTextBoxFormats()->GetOwnerShape())
+ {
+ const auto& rShapeAnchor
+ = pFirst->Which() == RES_DRAWFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
+ const auto& rFrameAnchor
+ = pFirst->Which() == RES_FLYFRMFMT ? pFirst->GetAnchor() : pSecond->GetAnchor();
+
+ if (rShapeAnchor.GetAnchorId() == rFrameAnchor.GetAnchorId())
+ {
+ if (rShapeAnchor.GetContentAnchor() && rFrameAnchor.GetContentAnchor())
+ {
+ if (rShapeAnchor.GetContentAnchor()->nContent
+ != rFrameAnchor.GetContentAnchor()->nContent)
+ return true;
+
+ if (rShapeAnchor.GetContentAnchor()->nNode
+ != rFrameAnchor.GetContentAnchor()->nNode)
+ return true;
+
+ return false;
+ }
+
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE
+ && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ if (rShapeAnchor.GetPageNum() == rFrameAnchor.GetPageNum())
+ return false;
+ else
+ return true;
+ }
+
+ return true;
+ }
+
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && rFrameAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ if (rShapeAnchor.GetContentAnchor() && rFrameAnchor.GetContentAnchor())
+ {
+ if (rShapeAnchor.GetContentAnchor()->nContent
+ != rFrameAnchor.GetContentAnchor()->nContent)
+ return true;
+
+ if (rShapeAnchor.GetContentAnchor()->nNode
+ != rFrameAnchor.GetContentAnchor()->nNode)
+ return true;
+
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+SwTextBoxNode::SwTextBoxNode(SwFrameFormat* pOwnerShape)
+{
+ assert(pOwnerShape);
+ assert(pOwnerShape->Which() == RES_DRAWFRMFMT);
+
+ m_bIsCloningInProgress = false;
+ m_bLock = false;
+
+ m_pOwnerShapeFormat = pOwnerShape;
+ if (!m_pTextBoxes.empty())
+ m_pTextBoxes.clear();
+}
+
+SwTextBoxNode::~SwTextBoxNode()
+{
+ if (m_pTextBoxes.size() != 0)
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::~SwTextBoxNode(): Text-Box-Vector still not empty!");
+ assert(false);
+ }
+}
+
+void SwTextBoxNode::AddTextBox(SdrObject* pDrawObject, SwFrameFormat* pNewTextBox)
+{
+ assert(pNewTextBox);
+ assert(pNewTextBox->Which() == RES_FLYFRMFMT);
+
+ assert(pDrawObject);
+
+ SwTextBoxElement aElem;
+ aElem.m_pDrawObject = pDrawObject;
+ aElem.m_pTextBoxFormat = pNewTextBox;
+
+ for (const auto& rE : m_pTextBoxes)
+ {
+ if (rE.m_pDrawObject == pDrawObject || rE.m_pTextBoxFormat == pNewTextBox)
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::AddTextBox(): Already exist!");
+ return;
+ }
+ }
+
+ auto pSwFlyDraw = dynamic_cast<SwFlyDrawObj*>(pDrawObject);
+ if (pSwFlyDraw)
+ {
+ pSwFlyDraw->SetTextBox(true);
+ }
+ m_pTextBoxes.push_back(aElem);
+}
+
+void SwTextBoxNode::DelTextBox(const SdrObject* pDrawObject, bool bDelFromDoc)
+{
+ assert(pDrawObject);
+ if (m_pTextBoxes.empty())
+ return;
+
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
+ {
+ if (it->m_pDrawObject == pDrawObject)
+ {
+ if (bDelFromDoc)
+ {
+ it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ it->m_pTextBoxFormat);
+ // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
+ // then the ~SwFrameFormat() will call this method again to remove the entry.
+ return;
+ }
+ else
+ {
+ it = m_pTextBoxes.erase(it);
+ return;
+ }
+ }
+ ++it;
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
+}
+
+void SwTextBoxNode::DelTextBox(const SwFrameFormat* pTextBox, bool bDelFromDoc)
+{
+ if (m_pTextBoxes.empty())
+ return;
+
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end();)
+ {
+ if (it->m_pTextBoxFormat == pTextBox)
+ {
+ if (bDelFromDoc)
+ {
+ it->m_pTextBoxFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ it->m_pTextBoxFormat);
+ // What about m_pTextBoxes? So, when the DelLayoutFormat() removes the format
+ // then the ~SwFrameFormat() will call this method again to remove the entry.
+ return;
+ }
+ else
+ {
+ it = m_pTextBoxes.erase(it);
+ return;
+ }
+ }
+ ++it;
+ }
+
+ SAL_WARN("sw.core", "SwTextBoxNode::DelTextBox(): Not found!");
+}
+
+SwFrameFormat* SwTextBoxNode::GetTextBox(const SdrObject* pDrawObject) const
+{
+ assert(pDrawObject);
+ assert(m_pOwnerShapeFormat);
+
+ if (auto& pTextBoxes = m_pOwnerShapeFormat->GetOtherTextBoxFormats())
+ {
+ if (size_t(pTextBoxes.use_count()) != pTextBoxes->GetTextBoxCount() + size_t(1))
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): RefCount and TexBox count mismatch!");
+ assert(false);
+ }
+ }
+
+ if (m_bLock)
+ return nullptr;
+
+ if (!m_pTextBoxes.empty())
+ {
+ for (auto it = m_pTextBoxes.begin(); it != m_pTextBoxes.end(); it++)
+ {
+ if (it->m_pDrawObject == pDrawObject)
+ {
+ return it->m_pTextBoxFormat;
+ }
+ }
+ SAL_WARN("sw.core", "SwTextBoxNode::GetTextBox(): Not found!");
+ }
+
+ return nullptr;
+}
+
+void SwTextBoxNode::ClearAll()
+{
+ // If this called from ~SwDoc(), then only the address entries
+ // have to be removed, the format will be deleted by the
+ // the mpSpzFrameFormatTable->DeleteAndDestroyAll() in ~SwDoc()!
+ if (m_pOwnerShapeFormat->GetDoc()->IsInDtor())
+ {
+ m_pTextBoxes.clear();
+ return;
+ }
+
+ // For loop control
+ sal_uInt16 nLoopCount = 0;
+
+ // Reference not enough, copy needed.
+ const size_t nTextBoxCount = m_pTextBoxes.size();
+
+ // For loop has problems: When one entry deleted, the iterator has
+ // to be refreshed according to the new situation. So using While() instead.
+ while (!m_pTextBoxes.empty())
+ {
+ // Delete the last textbox of the vector from the doc
+ // (what will call deregister in ~SwFrameFormat()
+ m_pOwnerShapeFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(
+ m_pTextBoxes.back().m_pTextBoxFormat);
+
+ // Check if we are looping
+ if (nLoopCount > (nTextBoxCount + 1))
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Maximum loop count reached!");
+ break;
+ }
+ else
+ {
+ nLoopCount++;
+ }
+ }
+
+ // Ensure the vector is empty.
+ if (!m_pTextBoxes.empty())
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::ClearAll(): Text-Box-Vector still not empty!");
+ assert(false);
+ }
+}
+
+bool SwTextBoxNode::IsGroupTextBox() const { return m_pTextBoxes.size() > 1; }
+
+std::map<SdrObject*, SwFrameFormat*> SwTextBoxNode::GetAllTextBoxes() const
+{
+ std::map<SdrObject*, SwFrameFormat*> aRet;
+ for (auto& rElem : m_pTextBoxes)
+ {
+ aRet.emplace(rElem.m_pDrawObject, rElem.m_pTextBoxFormat);
+ }
+ return aRet;
+}
+
+void SwTextBoxNode::Clone(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ bool bSetAttr, bool bMakeFrame) const
+{
+ if (!o_pTarget || !pDoc)
+ return;
+
+ if (o_pTarget->Which() != RES_DRAWFRMFMT)
+ return;
+
+ if (m_bIsCloningInProgress)
+ return;
+
+ m_bIsCloningInProgress = true;
+
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, m_pOwnerShapeFormat->FindSdrObject(),
+ o_pTarget->FindSdrObject(), bSetAttr, bMakeFrame);
+
+ m_bIsCloningInProgress = false;
+
+ for (auto& rElem : m_pTextBoxes)
+ {
+ SwTextBoxHelper::changeAnchor(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::doTextBoxPositioning(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::DoTextBoxZOrderCorrection(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ SwTextBoxHelper::syncTextBoxSize(m_pOwnerShapeFormat, rElem.m_pDrawObject);
+ }
+}
+
+void SwTextBoxNode::Clone_Impl(SwDoc* pDoc, const SwFormatAnchor& rNewAnc, SwFrameFormat* o_pTarget,
+ const SdrObject* pSrcObj, SdrObject* pDestObj, bool bSetAttr,
+ bool bMakeFrame) const
+{
+ if (!pSrcObj || !pDestObj)
+ return;
+
+ auto pSrcList = pSrcObj->getChildrenOfSdrObject();
+ auto pDestList = pDestObj->getChildrenOfSdrObject();
+
+ if (pSrcList && pDestList)
+ {
+ if (pSrcList->GetObjCount() != pDestList->GetObjCount())
+ {
+ SAL_WARN("sw.core", "SwTextBoxNode::Clone_Impl(): Difference between the shapes!");
+ return;
+ }
+
+ for (size_t i = 0; i < pSrcList->GetObjCount(); ++i)
+ {
+ Clone_Impl(pDoc, rNewAnc, o_pTarget, pSrcList->GetObj(i), pDestList->GetObj(i),
+ bSetAttr, bMakeFrame);
+ }
+ return;
+ }
+
+ if (!pSrcList && !pDestList)
+ {
+ if (auto pSrcFormat = GetTextBox(pSrcObj))
+ {
+ SwFormatAnchor aNewAnchor(rNewAnc);
+ if (aNewAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ aNewAnchor.SetType(RndStdIds::FLY_AT_CHAR);
+
+ if (!bMakeFrame)
+ bMakeFrame = true;
+ }
+
+ if (auto pTargetFormat = pDoc->getIDocumentLayoutAccess().CopyLayoutFormat(
+ *pSrcFormat, aNewAnchor, bSetAttr, bMakeFrame))
+ {
+ if (!o_pTarget->GetOtherTextBoxFormats())
+ {
+ auto pNewTextBoxes = std::make_shared<SwTextBoxNode>(SwTextBoxNode(o_pTarget));
+ o_pTarget->SetOtherTextBoxFormats(pNewTextBoxes);
+ pNewTextBoxes->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(pNewTextBoxes);
+ }
+ else
+ {
+ o_pTarget->GetOtherTextBoxFormats()->AddTextBox(pDestObj, pTargetFormat);
+ pTargetFormat->SetOtherTextBoxFormats(o_pTarget->GetOtherTextBoxFormats());
+ }
+ o_pTarget->SetFormatAttr(pTargetFormat->GetContent());
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/visiturl.cxx b/sw/source/core/doc/visiturl.cxx
new file mode 100644
index 000000000..24db9230b
--- /dev/null
+++ b/sw/source/core/doc/visiturl.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sfx2/docfile.hxx>
+#include <svl/inethist.hxx>
+#include <fmtinfmt.hxx>
+#include <txtinet.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <visiturl.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <editsh.hxx>
+#include <docsh.hxx>
+
+SwURLStateChanged::SwURLStateChanged( SwDoc& rD )
+ : m_rDoc( rD )
+{
+ StartListening( *INetURLHistory::GetOrCreate() );
+}
+
+SwURLStateChanged::~SwURLStateChanged()
+{
+ EndListening( *INetURLHistory::GetOrCreate() );
+}
+
+void SwURLStateChanged::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ if( !(dynamic_cast<const INetURLHistoryHint*>(&rHint) && m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ return;
+
+ // This URL has been changed:
+ const INetURLObject* pIURL = static_cast<const INetURLHistoryHint&>(rHint).GetObject();
+ OUString sURL( pIURL->GetMainURL( INetURLObject::DecodeMechanism::NONE ) ), sBkmk;
+
+ SwEditShell* pESh = m_rDoc.GetEditShell();
+
+ if( m_rDoc.GetDocShell() && m_rDoc.GetDocShell()->GetMedium() &&
+ // If this is our Doc, we can also have local jumps!
+ m_rDoc.GetDocShell()->GetMedium()->GetName() == sURL )
+ sBkmk = "#" + pIURL->GetMark();
+
+ bool bAction = false, bUnLockView = false;
+ for (const SfxPoolItem* pItem : m_rDoc.GetAttrPool().GetItemSurrogates(RES_TXTATR_INETFMT))
+ {
+ const SwFormatINetFormat* pFormatItem = dynamic_cast<const SwFormatINetFormat*>(pItem);
+ if( pFormatItem != nullptr &&
+ ( pFormatItem->GetValue() == sURL || ( !sBkmk.isEmpty() && pFormatItem->GetValue() == sBkmk )))
+ {
+ const SwTextINetFormat* pTextAttr = pFormatItem->GetTextINetFormat();
+ if (pTextAttr != nullptr)
+ {
+ const SwTextNode* pTextNd = pTextAttr->GetpTextNode();
+ if (pTextNd != nullptr)
+ {
+ if( !bAction && pESh )
+ {
+ pESh->StartAllAction();
+ bAction = true;
+ bUnLockView = !pESh->IsViewLocked();
+ pESh->LockView( true );
+ }
+ const_cast<SwTextINetFormat*>(pTextAttr)->SetVisitedValid(false);
+ const SwTextAttr* pAttr = pTextAttr;
+ SwUpdateAttr aUpdateAttr(
+ pAttr->GetStart(),
+ *pAttr->End(),
+ RES_FMT_CHG);
+
+ const_cast<SwTextNode*>(pTextNd)->TriggerNodeUpdate(sw::LegacyModifyHint(&aUpdateAttr, &aUpdateAttr));
+ }
+ }
+ }
+ }
+
+ if( bAction )
+ pESh->EndAllAction();
+ if( bUnLockView )
+ pESh->LockView( false );
+}
+
+// Check if the URL has been visited before. Via the Doc, if only one Bookmark is set
+// We need to put the Doc's name before it!
+bool SwDoc::IsVisitedURL( std::u16string_view rURL )
+{
+ bool bRet = false;
+ if( !rURL.empty() )
+ {
+ INetURLHistory *pHist = INetURLHistory::GetOrCreate();
+ if( '#' == rURL[0] && mpDocShell && mpDocShell->GetMedium() )
+ {
+ INetURLObject aIObj( mpDocShell->GetMedium()->GetURLObject() );
+ aIObj.SetMark( rURL.substr( 1 ) );
+ bRet = pHist->QueryUrl( aIObj );
+ }
+ else
+ bRet = pHist->QueryUrl( rURL );
+
+ // We also want to be informed about status updates in the History
+ if( !mpURLStateChgd )
+ {
+ SwDoc* pD = this;
+ pD->mpURLStateChgd.reset( new SwURLStateChanged(*this) );
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/cancellablejob.cxx b/sw/source/core/docnode/cancellablejob.cxx
new file mode 100644
index 000000000..eecd1d336
--- /dev/null
+++ b/sw/source/core/docnode/cancellablejob.cxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include "cancellablejob.hxx"
+#include <observablethread.hxx>
+
+CancellableJob::CancellableJob( const rtl::Reference< ObservableThread >& rThread ) :
+ mrThread( rThread )
+{
+}
+
+// css::util::XCancellable:
+void SAL_CALL CancellableJob::cancel()
+{
+ mrThread->join();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/cancellablejob.hxx b/sw/source/core/docnode/cancellablejob.hxx
new file mode 100644
index 000000000..010e1f4de
--- /dev/null
+++ b/sw/source/core/docnode/cancellablejob.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_DOCNODE_CANCELLABLEJOB_HXX
+#define INCLUDED_SW_SOURCE_CORE_DOCNODE_CANCELLABLEJOB_HXX
+
+#include <sal/config.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/util/XCancellable.hpp>
+
+#include <rtl/ref.hxx>
+
+#include <observablethread.hxx>
+
+class CancellableJob : public ::cppu::WeakImplHelper<css::util::XCancellable>
+{
+public:
+ explicit CancellableJob(const ::rtl::Reference<ObservableThread>& rThread);
+
+ // css::util::XCancellable:
+ virtual void SAL_CALL cancel() override;
+
+private:
+ CancellableJob(CancellableJob const&) = delete;
+ void operator=(CancellableJob const&) = delete;
+
+ ::rtl::Reference<ObservableThread> mrThread;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/finalthreadmanager.cxx b/sw/source/core/docnode/finalthreadmanager.cxx
new file mode 100644
index 000000000..bfae4a6b4
--- /dev/null
+++ b/sw/source/core/docnode/finalthreadmanager.cxx
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <finalthreadmanager.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/thread.hxx>
+#include <pausethreadstarting.hxx>
+#include <swthreadjoiner.hxx>
+
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <rtl/ustring.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <mutex>
+
+/** thread to cancel a give list of cancellable jobs
+
+ helper class for FinalThreadManager
+*/
+class CancelJobsThread : public osl::Thread
+{
+ public:
+ explicit CancelJobsThread( std::list< css::uno::Reference< css::util::XCancellable > >&& rJobs )
+ : maJobs( std::move(rJobs) ),
+ mbAllJobsCancelled( false ),
+ mbStopped( false )
+ {
+ }
+
+ void addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs );
+ bool allJobsCancelled() const;
+ void stopWhenAllJobsCancelled();
+
+ private:
+ bool existJobs() const;
+
+ css::uno::Reference< css::util::XCancellable > getNextJob();
+
+ bool stopped() const;
+ virtual void SAL_CALL run() override;
+ mutable std::mutex maMutex;
+
+ std::list< css::uno::Reference< css::util::XCancellable > > maJobs;
+
+ bool mbAllJobsCancelled;
+ bool mbStopped;
+};
+
+void CancelJobsThread::addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs )
+{
+ std::scoped_lock aGuard(maMutex);
+
+ maJobs.insert( maJobs.end(), rJobs.begin(), rJobs.end() );
+ mbAllJobsCancelled = !maJobs.empty();
+}
+
+bool CancelJobsThread::existJobs() const
+{
+ std::scoped_lock aGuard(maMutex);
+
+ return !maJobs.empty();
+}
+
+bool CancelJobsThread::allJobsCancelled() const
+{
+ std::scoped_lock aGuard(maMutex);
+
+ return maJobs.empty() && mbAllJobsCancelled;
+}
+
+void CancelJobsThread::stopWhenAllJobsCancelled()
+{
+ std::scoped_lock aGuard(maMutex);
+
+ mbStopped = true;
+}
+
+css::uno::Reference< css::util::XCancellable > CancelJobsThread::getNextJob()
+{
+ css::uno::Reference< css::util::XCancellable > xRet;
+
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ if ( !maJobs.empty() )
+ {
+ xRet = maJobs.front();
+ maJobs.pop_front();
+ }
+ }
+
+ return xRet;
+}
+
+bool CancelJobsThread::stopped() const
+{
+ std::scoped_lock aGuard(maMutex);
+
+ return mbStopped;
+}
+
+void SAL_CALL CancelJobsThread::run()
+{
+ osl_setThreadName("sw CancelJobsThread");
+
+ while ( !stopped() )
+ {
+ while ( existJobs() )
+ {
+ css::uno::Reference< css::util::XCancellable > aJob( getNextJob() );
+ if ( aJob.is() )
+ aJob->cancel();
+ }
+
+ mbAllJobsCancelled = true;
+
+ {
+ osl::Thread::wait(std::chrono::seconds(1));
+ }
+ }
+}
+
+/** thread to terminate office, when all jobs are cancelled.
+
+ helper class for FinalThreadManager
+*/
+class TerminateOfficeThread : public osl::Thread
+{
+ public:
+ TerminateOfficeThread( CancelJobsThread const & rCancelJobsThread,
+ css::uno::Reference< css::uno::XComponentContext > const & xContext )
+ : mrCancelJobsThread( rCancelJobsThread ),
+ mbStopOfficeTermination( false ),
+ mxContext( xContext )
+ {
+ }
+
+ void StopOfficeTermination();
+
+ private:
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+ bool OfficeTerminationStopped();
+ void PerformOfficeTermination();
+
+ osl::Mutex maMutex;
+
+ const CancelJobsThread& mrCancelJobsThread;
+ bool mbStopOfficeTermination;
+
+ css::uno::Reference< css::uno::XComponentContext > mxContext;
+};
+
+void TerminateOfficeThread::StopOfficeTermination()
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ mbStopOfficeTermination = true;
+}
+
+bool TerminateOfficeThread::OfficeTerminationStopped()
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ return mbStopOfficeTermination;
+}
+
+void SAL_CALL TerminateOfficeThread::run()
+{
+ osl_setThreadName("sw TerminateOfficeThread");
+
+ while ( !OfficeTerminationStopped() )
+ {
+ osl::MutexGuard aGuard(maMutex);
+
+ if ( mrCancelJobsThread.allJobsCancelled() )
+ break;
+ }
+
+ if ( !OfficeTerminationStopped() )
+ PerformOfficeTermination();
+}
+
+void TerminateOfficeThread::PerformOfficeTermination()
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(mxContext);
+
+ css::uno::Reference< css::container::XElementAccess > xList = xDesktop->getFrames();
+ if ( !xList.is() )
+ {
+ OSL_FAIL( "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" );
+ return;
+ }
+
+ if ( !xList->hasElements() )
+ {
+ if ( !OfficeTerminationStopped() )
+ xDesktop->terminate();
+ }
+}
+
+void SAL_CALL TerminateOfficeThread::onTerminated()
+{
+ if ( OfficeTerminationStopped() )
+ delete this;
+}
+
+FinalThreadManager::FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const & context)
+ : m_xContext(context),
+ mpTerminateOfficeThread( nullptr ),
+ mbRegisteredAtDesktop( false )
+{
+
+}
+
+void FinalThreadManager::registerAsListenerAtDesktop()
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
+ xDesktop->addTerminateListener( css::uno::Reference< css::frame::XTerminateListener >( this ) );
+}
+
+FinalThreadManager::~FinalThreadManager()
+{
+ if ( mpPauseThreadStarting )
+ {
+ mpPauseThreadStarting.reset();
+ }
+
+ if ( mpTerminateOfficeThread != nullptr )
+ {
+ mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
+ mpTerminateOfficeThread = nullptr;
+ }
+
+ if ( !maThreads.empty() )
+ {
+ OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" );
+ cancelAllJobs();
+ }
+
+ if ( mpCancelJobsThread != nullptr )
+ {
+ if ( !mpCancelJobsThread->allJobsCancelled() )
+ OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" );
+
+ mpCancelJobsThread->stopWhenAllJobsCancelled();
+ mpCancelJobsThread->join();
+ mpCancelJobsThread.reset();
+ }
+}
+
+// com.sun.star.uno.XServiceInfo:
+OUString SAL_CALL FinalThreadManager::getImplementationName()
+{
+ return "com.sun.star.util.comp.FinalThreadManager";
+}
+
+sal_Bool SAL_CALL FinalThreadManager::supportsService(OUString const & serviceName)
+{
+ return cppu::supportsService(this, serviceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FinalThreadManager::getSupportedServiceNames()
+{
+ return { "com.sun.star.util.JobManager" };
+}
+
+// css::util::XJobManager:
+void SAL_CALL FinalThreadManager::registerJob(const css::uno::Reference< css::util::XCancellable > & Job)
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ maThreads.push_back( Job );
+
+ if ( !mbRegisteredAtDesktop )
+ {
+ registerAsListenerAtDesktop();
+ mbRegisteredAtDesktop = true;
+ }
+}
+
+void SAL_CALL FinalThreadManager::releaseJob(const css::uno::Reference< css::util::XCancellable > & Job)
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ maThreads.remove( Job );
+}
+
+void SAL_CALL FinalThreadManager::cancelAllJobs()
+{
+ std::list< css::uno::Reference< css::util::XCancellable > > aThreads;
+ {
+ osl::MutexGuard aGuard(maMutex);
+
+ aThreads.insert( aThreads.end(), maThreads.begin(), maThreads.end() );
+ maThreads.clear();
+ }
+
+ if ( aThreads.empty() )
+ return;
+
+ osl::MutexGuard aGuard(maMutex);
+
+ if ( mpCancelJobsThread == nullptr )
+ {
+ mpCancelJobsThread.reset(new CancelJobsThread( std::list(aThreads) ));
+ if ( !mpCancelJobsThread->create() )
+ {
+ mpCancelJobsThread.reset();
+ for (auto const& elem : aThreads)
+ {
+ elem->cancel();
+ }
+ aThreads.clear();
+ }
+ }
+ else
+ mpCancelJobsThread->addJobs( aThreads );
+}
+
+// css::frame::XTerminateListener
+void SAL_CALL FinalThreadManager::queryTermination( const css::lang::EventObject& )
+{
+ osl::MutexGuard aGuard(maMutex);
+
+ cancelAllJobs();
+ // Sleep 1 second to give the thread for job cancellation some time.
+ // Probably, all started threads have already finished its work.
+ if ( mpCancelJobsThread != nullptr &&
+ !mpCancelJobsThread->allJobsCancelled() )
+ {
+ osl::Thread::wait(std::chrono::seconds(1));
+ }
+
+ if ( mpCancelJobsThread != nullptr &&
+ !mpCancelJobsThread->allJobsCancelled() )
+ {
+ if ( mpTerminateOfficeThread != nullptr )
+ {
+ if ( mpTerminateOfficeThread->isRunning() )
+ mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
+ else
+ delete mpTerminateOfficeThread;
+
+ mpTerminateOfficeThread = nullptr;
+ }
+ mpTerminateOfficeThread = new TerminateOfficeThread( *mpCancelJobsThread,
+ m_xContext );
+ if ( !mpTerminateOfficeThread->create() )
+ {
+ delete mpTerminateOfficeThread;
+ mpTerminateOfficeThread = nullptr;
+ }
+
+ throw css::frame::TerminationVetoException();
+ }
+
+ mpPauseThreadStarting.reset(new SwPauseThreadStarting());
+}
+
+void SAL_CALL FinalThreadManager::cancelTermination( const css::lang::EventObject& )
+{
+ mpPauseThreadStarting.reset();
+}
+
+void SAL_CALL FinalThreadManager::notifyTermination( const css::lang::EventObject& )
+{
+ if ( mpTerminateOfficeThread != nullptr )
+ {
+ if ( mpTerminateOfficeThread->isRunning() )
+ mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
+ else
+ delete mpTerminateOfficeThread;
+
+ mpTerminateOfficeThread = nullptr;
+ }
+
+ if ( !maThreads.empty() )
+ cancelAllJobs();
+
+ if ( mpCancelJobsThread != nullptr )
+ {
+ mpCancelJobsThread->stopWhenAllJobsCancelled();
+ mpCancelJobsThread->join();
+ mpCancelJobsThread.reset();
+ }
+
+ // get reference of this
+ css::uno::Reference< css::uno::XInterface > aOwnRef( static_cast< cppu::OWeakObject* >( this ));
+ // notify <SwThreadJoiner> to release its reference
+ SwThreadJoiner::ReleaseThreadJoiner();
+}
+
+// ::com::sun:star::lang::XEventListener (inherited via css::frame::XTerminateListener)
+void SAL_CALL FinalThreadManager::disposing( const css::lang::EventObject& )
+{
+ // nothing to do, because instance doesn't hold any references of observed objects
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_util_comp_FinalThreadManager_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FinalThreadManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndcopy.cxx b/sw/source/core/docnode/ndcopy.cxx
new file mode 100644
index 000000000..4b26acc0e
--- /dev/null
+++ b/sw/source/core/docnode/ndcopy.cxx
@@ -0,0 +1,378 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <node.hxx>
+#include <frmfmt.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <swtblfmt.hxx>
+#include <cellatr.hxx>
+#include <ddefld.hxx>
+#include <swddetbl.hxx>
+#include <ndindex.hxx>
+#include <frameformats.hxx>
+#include <vector>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+namespace {
+
+// Structure for the mapping from old and new frame formats to the
+// boxes and lines of a table
+struct MapTableFrameFormat
+{
+ const SwFrameFormat *pOld;
+ SwFrameFormat *pNew;
+ MapTableFrameFormat( const SwFrameFormat *pOldFormat, SwFrameFormat*pNewFormat )
+ : pOld( pOldFormat ), pNew( pNewFormat )
+ {}
+};
+
+}
+
+typedef std::vector<MapTableFrameFormat> MapTableFrameFormats;
+
+SwContentNode* SwTextNode::MakeCopy(SwDoc& rDoc, const SwNodeIndex& rIdx, bool const bNewFrames) const
+{
+ // the Copy-Textnode is the Node with the Text, the Copy-Attrnode is the
+ // node with the collection and hard attributes. Normally is the same
+ // node, but if insert a glossary without formatting, then the Attrnode
+ // is the prev node of the destination position in dest. document.
+ SwTextNode* pCpyTextNd = const_cast<SwTextNode*>(this);
+ SwTextNode* pCpyAttrNd = pCpyTextNd;
+
+ // Copy the formats to the other document
+ SwTextFormatColl* pColl = nullptr;
+ if( rDoc.IsInsOnlyTextGlossary() )
+ {
+ SwNodeIndex aIdx( rIdx, -1 );
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ pCpyAttrNd = aIdx.GetNode().GetTextNode();
+ pColl = &pCpyAttrNd->GetTextColl()->GetNextTextFormatColl();
+ }
+ }
+ if( !pColl )
+ pColl = rDoc.CopyTextColl( *GetTextColl() );
+
+ SwTextNode* pTextNd = rDoc.GetNodes().MakeTextNode(rIdx, pColl, bNewFrames);
+
+ // METADATA: register copy
+ pTextNd->RegisterAsCopyOf(*pCpyTextNd);
+
+ // Copy Attribute/Text
+ if( !pCpyAttrNd->HasSwAttrSet() )
+ // An AttrSet was added for numbering, so delete it
+ pTextNd->ResetAllAttr();
+
+ // if Copy-Textnode unequal to Copy-Attrnode, then copy first
+ // the attributes into the new Node.
+ if( pCpyAttrNd != pCpyTextNd )
+ {
+ pCpyAttrNd->CopyAttr( pTextNd, 0, 0 );
+ if( pCpyAttrNd->HasSwAttrSet() )
+ {
+ SwAttrSet aSet( *pCpyAttrNd->GetpSwAttrSet() );
+ aSet.ClearItem( RES_PAGEDESC );
+ aSet.ClearItem( RES_BREAK );
+ aSet.CopyToModify( *pTextNd );
+ }
+ }
+
+ // Is that enough? What about PostIts/Fields/FieldTypes?
+ // #i96213# - force copy of all attributes
+ pCpyTextNd->CopyText( pTextNd, SwIndex( pCpyTextNd ),
+ pCpyTextNd->GetText().getLength(), true );
+
+ if( RES_CONDTXTFMTCOLL == pColl->Which() )
+ pTextNd->ChkCondColl();
+
+ return pTextNd;
+}
+
+static bool lcl_SrchNew( const MapTableFrameFormat& rMap, SwFrameFormat** pPara )
+{
+ if( rMap.pOld != *pPara )
+ return true;
+ *pPara = rMap.pNew;
+ return false;
+}
+
+namespace {
+
+struct CopyTable
+{
+ SwDoc& m_rDoc;
+ SwNodeOffset m_nOldTableSttIdx;
+ MapTableFrameFormats& m_rMapArr;
+ SwTableLine* m_pInsLine;
+ SwTableBox* m_pInsBox;
+ SwTableNode *m_pTableNd;
+ const SwTable *m_pOldTable;
+
+ CopyTable(SwDoc& rDc, MapTableFrameFormats& rArr, SwNodeOffset nOldStt,
+ SwTableNode& rTableNd, const SwTable* pOldTable)
+ : m_rDoc(rDc), m_nOldTableSttIdx(nOldStt), m_rMapArr(rArr),
+ m_pInsLine(nullptr), m_pInsBox(nullptr), m_pTableNd(&rTableNd), m_pOldTable(pOldTable)
+ {}
+};
+
+}
+
+static void lcl_CopyTableLine( const SwTableLine* pLine, CopyTable* pCT );
+
+static void lcl_CopyTableBox( SwTableBox* pBox, CopyTable* pCT )
+{
+ SwTableBoxFormat * pBoxFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+ for (const auto& rMap : pCT->m_rMapArr)
+ if ( !lcl_SrchNew( rMap, reinterpret_cast<SwFrameFormat**>(&pBoxFormat) ) )
+ break;
+
+ if (pBoxFormat == pBox->GetFrameFormat()) // Create a new one?
+ {
+ const SwTableBoxFormula* pFormulaItem = pBoxFormat->GetItemIfSet( RES_BOXATR_FORMULA, false );
+ if( pFormulaItem && pFormulaItem->IsIntrnlName() )
+ {
+ const_cast<SwTableBoxFormula*>(pFormulaItem)->PtrToBoxNm(pCT->m_pOldTable);
+ }
+
+ pBoxFormat = pCT->m_rDoc.MakeTableBoxFormat();
+ pBoxFormat->CopyAttrs( *pBox->GetFrameFormat() );
+
+ if( pBox->GetSttIdx() )
+ {
+ SvNumberFormatter* pN = pCT->m_rDoc.GetNumberFormatter(false);
+ const SwTableBoxNumFormat* pFormatItem;
+ if( pN && pN->HasMergeFormatTable() &&
+ (pFormatItem = pBoxFormat->GetItemIfSet( RES_BOXATR_FORMAT, false )) )
+ {
+ sal_uLong nOldIdx = pFormatItem->GetValue();
+ sal_uLong nNewIdx = pN->GetMergeFormatIndex( nOldIdx );
+ if( nNewIdx != nOldIdx )
+ pBoxFormat->SetFormatAttr( SwTableBoxNumFormat( nNewIdx ));
+
+ }
+ }
+
+ pCT->m_rMapArr.emplace_back(pBox->GetFrameFormat(), pBoxFormat);
+ }
+
+ sal_uInt16 nLines = pBox->GetTabLines().size();
+ SwTableBox* pNewBox;
+ if( nLines )
+ pNewBox = new SwTableBox(pBoxFormat, nLines, pCT->m_pInsLine);
+ else
+ {
+ SwNodeIndex aNewIdx(*pCT->m_pTableNd, pBox->GetSttIdx() - pCT->m_nOldTableSttIdx);
+ assert(aNewIdx.GetNode().IsStartNode() && "Index is not on the start node");
+
+ pNewBox = new SwTableBox(pBoxFormat, aNewIdx, pCT->m_pInsLine);
+ pNewBox->setRowSpan( pBox->getRowSpan() );
+ }
+
+ pCT->m_pInsLine->GetTabBoxes().push_back( pNewBox );
+
+ if (nLines)
+ {
+ CopyTable aPara(*pCT);
+ aPara.m_pInsBox = pNewBox;
+ for( const SwTableLine* pLine : pBox->GetTabLines() )
+ lcl_CopyTableLine( pLine, &aPara );
+ }
+ else if (pNewBox->IsInHeadline(&pCT->m_pTableNd->GetTable()))
+ {
+ // In the headline, the paragraphs must match conditional styles
+ pNewBox->GetSttNd()->CheckSectionCondColl();
+ }
+}
+
+static void lcl_CopyTableLine( const SwTableLine* pLine, CopyTable* pCT )
+{
+ SwTableLineFormat * pLineFormat = static_cast<SwTableLineFormat*>(pLine->GetFrameFormat());
+ for (const auto& rMap : pCT->m_rMapArr)
+ if ( !lcl_SrchNew( rMap, reinterpret_cast<SwFrameFormat**>(&pLineFormat) ) )
+ break;
+
+ if( pLineFormat == pLine->GetFrameFormat() ) // Create a new one?
+ {
+ pLineFormat = pCT->m_rDoc.MakeTableLineFormat();
+ pLineFormat->CopyAttrs( *pLine->GetFrameFormat() );
+ pCT->m_rMapArr.emplace_back(pLine->GetFrameFormat(), pLineFormat);
+ }
+
+ SwTableLine* pNewLine = new SwTableLine(pLineFormat, pLine->GetTabBoxes().size(), pCT->m_pInsBox);
+ // Insert the new row into the table
+ if (pCT->m_pInsBox)
+ {
+ pCT->m_pInsBox->GetTabLines().push_back(pNewLine);
+ }
+ else
+ {
+ pCT->m_pTableNd->GetTable().GetTabLines().push_back(pNewLine);
+ }
+
+ pCT->m_pInsLine = pNewLine;
+ for( auto& rpBox : const_cast<SwTableLine*>(pLine)->GetTabBoxes() )
+ lcl_CopyTableBox(rpBox, pCT);
+}
+
+SwTableNode* SwTableNode::MakeCopy( SwDoc& rDoc, const SwNodeIndex& rIdx ) const
+{
+ // In which array are we? Nodes? UndoNodes?
+ SwNodes& rNds = const_cast<SwNodes&>(GetNodes());
+
+ {
+ if( rIdx < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
+ rIdx >= rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex() )
+ return nullptr;
+ }
+
+ // Copy the TableFrameFormat
+ OUString sTableName( GetTable().GetFrameFormat()->GetName() );
+ if( !rDoc.IsCopyIsMove() )
+ {
+ const SwFrameFormats& rTableFormats = *rDoc.GetTableFrameFormats();
+ for( size_t n = rTableFormats.size(); n; )
+ if( rTableFormats[ --n ]->GetName() == sTableName )
+ {
+ sTableName = rDoc.GetUniqueTableName();
+ break;
+ }
+ }
+
+ SwFrameFormat* pTableFormat = rDoc.MakeTableFrameFormat( sTableName, rDoc.GetDfltFrameFormat() );
+ pTableFormat->CopyAttrs( *GetTable().GetFrameFormat() );
+ SwTableNode* pTableNd = new SwTableNode( rIdx );
+ SwEndNode* pEndNd = new SwEndNode( rIdx, *pTableNd );
+ SwNodeIndex aInsPos( *pEndNd );
+
+ SwTable& rTable = pTableNd->GetTable();
+ rTable.SetTableStyleName(GetTable().GetTableStyleName());
+ rTable.RegisterToFormat( *pTableFormat );
+
+ rTable.SetRowsToRepeat( GetTable().GetRowsToRepeat() );
+ rTable.SetTableChgMode( GetTable().GetTableChgMode() );
+ rTable.SetTableModel( GetTable().IsNewModel() );
+
+ SwDDEFieldType* pDDEType = nullptr;
+ if( auto pSwDDETable = dynamic_cast<const SwDDETable*>( &GetTable() ) )
+ {
+ // We're copying a DDE table
+ // Is the field type available in the new document?
+ pDDEType = const_cast<SwDDETable*>(pSwDDETable)->GetDDEFieldType();
+ if( pDDEType->IsDeleted() )
+ rDoc.getIDocumentFieldsAccess().InsDeletedFieldType( *pDDEType );
+ else
+ pDDEType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType( *pDDEType ));
+ OSL_ENSURE( pDDEType, "unknown FieldType" );
+
+ // Swap the table pointers in the node
+ std::unique_ptr<SwDDETable> pNewTable(new SwDDETable( pTableNd->GetTable(), pDDEType ));
+ pTableNd->SetNewTable( std::move(pNewTable), false );
+ }
+ // First copy the content of the tables, we will later assign the
+ // boxes/lines and create the frames
+ SwNodeRange aRg( *this, SwNodeOffset(+1), *EndOfSectionNode() );
+
+ // If there is a table in this table, the table format for the outer table
+ // does not seem to be used, because the table does not have any contents yet
+ // (see IsUsed). Therefore the inner table gets the same name as the outer table.
+ // We have to make sure that the table node of the SwTable is accessible, even
+ // without any content in m_TabSortContentBoxes. #i26629#
+ pTableNd->GetTable().SetTableNode( pTableNd );
+ rNds.Copy_( aRg, aInsPos, false );
+ pTableNd->GetTable().SetTableNode( nullptr );
+
+ // Special case for a single box
+ if( 1 == GetTable().GetTabSortBoxes().size() )
+ {
+ aRg.aStart.Assign( *pTableNd, 1 );
+ aRg.aEnd.Assign( *pTableNd->EndOfSectionNode() );
+ rDoc.GetNodes().SectionDown( &aRg, SwTableBoxStartNode );
+ }
+
+ // Delete all frames from the copied area, they will be created
+ // during the generation of the table frame
+ pTableNd->DelFrames();
+
+ MapTableFrameFormats aMapArr;
+ CopyTable aPara( rDoc, aMapArr, GetIndex(), *pTableNd, &GetTable() );
+
+ for( const SwTableLine* pLine : GetTable().GetTabLines() )
+ lcl_CopyTableLine( pLine, &aPara );
+
+ if( pDDEType )
+ pDDEType->IncRefCnt();
+
+ CHECK_TABLE( GetTable() );
+ return pTableNd;
+}
+
+void SwTextNode::CopyCollFormat(SwTextNode& rDestNd, bool const bUndoForChgFormatColl)
+{
+ // Copy the formats into the other document:
+ // Special case for PageBreak/PageDesc/ColBrk
+ SwDoc& rDestDoc = rDestNd.GetDoc();
+ SwAttrSet aPgBrkSet( rDestDoc.GetAttrPool(), aBreakSetRange );
+ const SwAttrSet* pSet;
+
+ pSet = rDestNd.GetpSwAttrSet();
+ if( nullptr != pSet )
+ {
+ // Special cases for Break-Attributes
+ const SfxPoolItem* pAttr;
+ if( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pAttr ) )
+ aPgBrkSet.Put( *pAttr );
+
+ if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pAttr ) )
+ aPgBrkSet.Put( *pAttr );
+ }
+
+ // this may create undo action SwUndoFormatCreate
+ auto const pCopy( rDestDoc.CopyTextColl( *GetTextColl() ) );
+ if (bUndoForChgFormatColl)
+ {
+ rDestNd.ChgFormatColl(pCopy);
+ }
+ else // tdf#138897
+ {
+ ::sw::UndoGuard const ug(rDestDoc.GetIDocumentUndoRedo());
+ rDestNd.ChgFormatColl(pCopy);
+ }
+ pSet = GetpSwAttrSet();
+ if( nullptr != pSet )
+ {
+ // note: this may create undo actions but not for setting the items
+ pSet->CopyToModify( rDestNd );
+ }
+
+ if( aPgBrkSet.Count() )
+ rDestNd.SetAttr( aPgBrkSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndnotxt.cxx b/sw/source/core/docnode/ndnotxt.cxx
new file mode 100644
index 000000000..7ae423a04
--- /dev/null
+++ b/sw/source/core/docnode/ndnotxt.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <tools/poly.hxx>
+#include <svl/stritem.hxx>
+#include <svx/contdlg.hxx>
+#include <vcl/svapp.hxx>
+#include <doc.hxx>
+#include <fmtcol.hxx>
+#include <ndnotxt.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <ndindex.hxx>
+#include <istyleaccess.hxx>
+#include <SwStyleNameMapper.hxx>
+
+#include <frmfmt.hxx>
+
+SwNoTextNode::SwNoTextNode( const SwNodeIndex & rWhere,
+ const SwNodeType nNdType,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwContentNode( rWhere, nNdType, pGrfColl ),
+ m_bAutomaticContour( false ),
+ m_bContourMapModeValid( true ),
+ m_bPixelContour( false )
+{
+ // Should this set a hard attribute?
+ if( pAutoAttr )
+ SetAttr( *pAutoAttr );
+}
+
+SwNoTextNode::~SwNoTextNode()
+{
+}
+
+/// Creates an AttrSet for all derivations with ranges for frame-
+/// and graphics-attributes.
+void SwNoTextNode::NewAttrSet( SwAttrPool& rPool )
+{
+ OSL_ENSURE( !mpAttrSet, "AttrSet is already set" );
+ SwAttrSet aNewAttrSet( rPool, aNoTextNodeSetRange );
+
+ // put names of parent style and conditional style:
+ const SwFormatColl* pFormatColl = GetFormatColl();
+ OUString sVal;
+ SwStyleNameMapper::FillProgName( pFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ SfxStringItem aFormatColl( RES_FRMATR_STYLE_NAME, sVal );
+ aNewAttrSet.Put( aFormatColl );
+
+ aNewAttrSet.SetParent( &GetFormatColl()->GetAttrSet() );
+ mpAttrSet = GetDoc().GetIStyleAccess().getAutomaticStyle( aNewAttrSet, IStyleAccess::AUTO_STYLE_NOTXT );
+}
+
+/// Dummies for loading/saving of persistent data
+/// when working with graphics and OLE objects
+bool SwNoTextNode::RestorePersistentData()
+{
+ return true;
+}
+
+bool SwNoTextNode::SavePersistentData()
+{
+ return true;
+}
+
+void SwNoTextNode::SetContour( const tools::PolyPolygon *pPoly, bool bAutomatic )
+{
+ if ( pPoly )
+ m_pContour = *pPoly;
+ else
+ m_pContour.reset();
+ m_bAutomaticContour = bAutomatic;
+ m_bContourMapModeValid = true;
+ m_bPixelContour = false;
+}
+
+void SwNoTextNode::CreateContour()
+{
+ OSL_ENSURE( !m_pContour, "Contour available." );
+ m_pContour = SvxContourDlg::CreateAutoContour(GetGraphic());
+ m_bAutomaticContour = true;
+ m_bContourMapModeValid = true;
+ m_bPixelContour = false;
+}
+
+const tools::PolyPolygon *SwNoTextNode::HasContour() const
+{
+ if( !m_bContourMapModeValid )
+ {
+ const MapMode aGrfMap( GetGraphic().GetPrefMapMode() );
+ bool bPixelGrf = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+ const MapMode aContourMap( bPixelGrf ? MapUnit::MapPixel : MapUnit::Map100thMM );
+ if( bPixelGrf ? !m_bPixelContour : aGrfMap != aContourMap )
+ {
+ double nGrfDPIx = 0.0;
+ double nGrfDPIy = 0.0;
+ {
+ if ( !bPixelGrf && m_bPixelContour )
+ {
+ basegfx::B2DSize aDPI = GetGraphic().GetPPI();
+ nGrfDPIx = aDPI.getX();
+ nGrfDPIy = aDPI.getY();
+ }
+ }
+ OSL_ENSURE( !bPixelGrf || aGrfMap == aContourMap,
+ "scale factor for pixel unsupported" );
+ OutputDevice* pOutDev =
+ (bPixelGrf || m_bPixelContour) ? Application::GetDefaultDevice()
+ : nullptr;
+ sal_uInt16 nPolyCount = m_pContour->Count();
+ for( sal_uInt16 j=0; j<nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = (*m_pContour)[j];
+
+ sal_uInt16 nCount = rPoly.GetSize();
+ for( sal_uInt16 i=0 ; i<nCount; i++ )
+ {
+ if( bPixelGrf )
+ rPoly[i] = pOutDev->LogicToPixel( rPoly[i],
+ aContourMap );
+ else if( m_bPixelContour )
+ {
+ rPoly[i] = pOutDev->PixelToLogic( rPoly[i], aGrfMap );
+
+ if ( nGrfDPIx != 0 && nGrfDPIy != 0 )
+ {
+ rPoly[i] = Point( rPoly[i].getX() * pOutDev->GetDPIX() / nGrfDPIx,
+ rPoly[i].getY() * pOutDev->GetDPIY() / nGrfDPIy );
+ }
+ }
+ else
+ rPoly[i] = OutputDevice::LogicToLogic( rPoly[i],
+ aContourMap,
+ aGrfMap );
+ }
+ }
+ }
+ m_bContourMapModeValid = true;
+ m_bPixelContour = false;
+ }
+
+ return m_pContour ? &*m_pContour : nullptr;
+}
+
+void SwNoTextNode::GetContour( tools::PolyPolygon &rPoly ) const
+{
+ OSL_ENSURE( m_pContour, "Contour not available." );
+ rPoly = *HasContour();
+}
+
+void SwNoTextNode::SetContourAPI( const tools::PolyPolygon *pPoly )
+{
+ if ( pPoly )
+ m_pContour = *pPoly;
+ else
+ m_pContour.reset();
+ m_bContourMapModeValid = false;
+}
+
+bool SwNoTextNode::GetContourAPI( tools::PolyPolygon &rContour ) const
+{
+ if( !m_pContour )
+ return false;
+
+ rContour = *m_pContour;
+ if( m_bContourMapModeValid )
+ {
+ const MapMode aGrfMap( GetGraphic().GetPrefMapMode() );
+ const MapMode aContourMap( MapUnit::Map100thMM );
+ OSL_ENSURE( aGrfMap.GetMapUnit() != MapUnit::MapPixel ||
+ aGrfMap == MapMode( MapUnit::MapPixel ),
+ "scale factor for pixel unsupported" );
+ if( aGrfMap.GetMapUnit() != MapUnit::MapPixel &&
+ aGrfMap != aContourMap )
+ {
+ sal_uInt16 nPolyCount = rContour.Count();
+ for( sal_uInt16 j=0; j<nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = rContour[j];
+
+ sal_uInt16 nCount = rPoly.GetSize();
+ for( sal_uInt16 i=0 ; i<nCount; i++ )
+ {
+ rPoly[i] = OutputDevice::LogicToLogic( rPoly[i], aGrfMap,
+ aContourMap );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SwNoTextNode::IsPixelContour() const
+{
+ bool bRet;
+ if( m_bContourMapModeValid )
+ {
+ const MapMode aGrfMap( GetGraphic().GetPrefMapMode() );
+ bRet = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+ }
+ else
+ {
+ bRet = m_bPixelContour;
+ }
+
+ return bRet;
+}
+
+Graphic SwNoTextNode::GetGraphic() const
+{
+ Graphic aRet;
+ if ( GetGrfNode() )
+ {
+ aRet = static_cast<const SwGrfNode*>(this)->GetGrf(true);
+ }
+ else
+ {
+ OSL_ENSURE( GetOLENode(), "new type of Node?" );
+ aRet = *const_cast<SwOLENode*>(static_cast<const SwOLENode*>(this))->SwOLENode::GetGraphic();
+ }
+ return aRet;
+}
+
+// #i73249#
+void SwNoTextNode::SetTitle( const OUString& rTitle )
+{
+ // Title attribute of <SdrObject> replaces own AlternateText attribute
+ SwFlyFrameFormat* pFlyFormat = dynamic_cast<SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::SetTitle(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return;
+ }
+
+ pFlyFormat->SetObjTitle( rTitle );
+}
+
+OUString SwNoTextNode::GetTitle() const
+{
+ const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::GetTitle(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return OUString();
+ }
+
+ return pFlyFormat->GetObjTitle();
+}
+
+void SwNoTextNode::SetDescription( const OUString& rDescription )
+{
+ SwFlyFrameFormat* pFlyFormat = dynamic_cast<SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::SetDescription(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return;
+ }
+
+ pFlyFormat->SetObjDescription( rDescription );
+}
+
+OUString SwNoTextNode::GetDescription() const
+{
+ const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(GetFlyFormat());
+ OSL_ENSURE( pFlyFormat, "<SwNoTextNode::GetDescription(..)> - missing <SwFlyFrameFormat> instance" );
+ if ( !pFlyFormat )
+ {
+ return OUString();
+ }
+
+ return pFlyFormat->GetObjDescription();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndnum.cxx b/sw/source/core/docnode/ndnum.cxx
new file mode 100644
index 000000000..89a7acb09
--- /dev/null
+++ b/sw/source/core/docnode/ndnum.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <node.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <ndtxt.hxx>
+#include <fldbas.hxx>
+#include <osl/diagnose.h>
+
+bool CompareSwOutlineNodes::operator()( SwNode* const& lhs, SwNode* const& rhs) const
+{
+ return lhs->GetIndex() < rhs->GetIndex();
+}
+
+bool SwOutlineNodes::Seek_Entry(SwNode* rP, size_type* pnPos) const
+{
+ const_iterator it = lower_bound(rP);
+ *pnPos = it - begin();
+ return it != end() && rP->GetIndex() == (*it)->GetIndex();
+}
+
+void SwNodes::UpdateOutlineNode(SwNode & rNd)
+{
+ assert(IsDocNodes()); // no point in m_pOutlineNodes for undo nodes
+
+ SwTextNode * pTextNd = rNd.GetTextNode();
+
+ if (!pTextNd || !pTextNd->IsOutlineStateChanged())
+ return;
+
+ bool bFound = m_pOutlineNodes->find(pTextNd) != m_pOutlineNodes->end();
+
+ if (pTextNd->IsOutline())
+ {
+ if (! bFound)
+ {
+ // assure that text is in the correct nodes array
+ if ( &(pTextNd->GetNodes()) == this )
+ {
+ m_pOutlineNodes->insert(pTextNd);
+ }
+ else
+ {
+ OSL_FAIL( "<SwNodes::UpdateOutlineNode(..)> - given text node isn't in the correct nodes array. This is a serious defect" );
+ }
+ }
+ }
+ else
+ {
+ if (bFound)
+ m_pOutlineNodes->erase(pTextNd);
+ }
+
+ pTextNd->UpdateOutlineState();
+
+ // update the structure fields
+ GetDoc().getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Chapter )->UpdateFields();
+}
+
+void SwNodes::UpdateOutlineIdx( const SwNode& rNd )
+{
+ if( m_pOutlineNodes->empty() ) // no OutlineNodes present ?
+ return;
+
+ SwNode* const pSrch = const_cast<SwNode*>(&rNd);
+
+ SwOutlineNodes::size_type nPos;
+ if (!m_pOutlineNodes->Seek_Entry(pSrch, &nPos))
+ return;
+ if( nPos == m_pOutlineNodes->size() ) // none present for updating ?
+ return;
+
+ if( nPos )
+ --nPos;
+
+ if( !GetDoc().IsInDtor() && IsDocNodes() )
+ UpdateOutlineNode( *(*m_pOutlineNodes)[ nPos ]);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndsect.cxx b/sw/source/core/docnode/ndsect.cxx
new file mode 100644
index 000000000..76831ae61
--- /dev/null
+++ b/sw/source/core/docnode/ndsect.cxx
@@ -0,0 +1,1475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <libxml/xmlwriter.h>
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/linkmgr.hxx>
+#include <svl/itemiter.hxx>
+#include <sal/log.hxx>
+#include <fmtcntnt.hxx>
+#include <txtftn.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <section.hxx>
+#include <UndoSection.hxx>
+#include <UndoDelete.hxx>
+#include <swundo.hxx>
+#include <calc.hxx>
+#include <swtable.hxx>
+#include <swserv.hxx>
+#include <frmfmt.hxx>
+#include <frmtool.hxx>
+#include <ftnidx.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+#include <sectfrm.hxx>
+#include <cntfrm.hxx>
+#include <node2lay.hxx>
+#include <doctxm.hxx>
+#include <fmtftntx.hxx>
+#include <strings.hrc>
+#include <viewsh.hxx>
+#include <memory>
+#include "ndsect.hxx"
+#include <tools/datetimeutils.hxx>
+#include <o3tl/string_view.hxx>
+
+// #i21457# - new implementation of local method <lcl_IsInSameTableBox(..)>.
+// Method now determines the previous/next on its own. Thus, it can be controlled,
+// for which previous/next is checked, if it's visible.
+static bool lcl_IsInSameTableBox( SwNodes const & _rNds,
+ const SwNode& _rNd,
+ const bool _bPrev )
+{
+ const SwTableNode* pTableNd = _rNd.FindTableNode();
+ if ( !pTableNd )
+ {
+ return true;
+ }
+
+ // determine index to be checked. Its assumed that a previous/next exist.
+ SwNodeIndex aChkIdx( _rNd );
+ {
+ // determine index of previous/next - skip hidden ones, which are
+ // inside the table.
+ // If found one is before/after table, this one isn't in the same
+ // table box as <_rNd>.
+ bool bFound = false;
+ do
+ {
+ if ( _bPrev
+ ? !SwNodes::GoPrevSection( &aChkIdx, false, false )
+ : !_rNds.GoNextSection( &aChkIdx, false, false ) )
+ {
+ OSL_FAIL( "<lcl_IsInSameTableBox(..)> - no previous/next!" );
+ return false;
+ }
+ else
+ {
+ if ( aChkIdx < pTableNd->GetIndex() ||
+ aChkIdx > pTableNd->EndOfSectionNode()->GetIndex() )
+ {
+ return false;
+ }
+ else
+ {
+ // check, if found one isn't inside a hidden section, which
+ // is also inside the table.
+ SwSectionNode* pSectNd = aChkIdx.GetNode().FindSectionNode();
+ if ( !pSectNd ||
+ pSectNd->GetIndex() < pTableNd->GetIndex() ||
+ !pSectNd->GetSection().IsHiddenFlag() )
+ {
+ bFound = true;
+ }
+ }
+ }
+ } while ( !bFound );
+ }
+
+ // Find the Box's StartNode
+ const SwTableSortBoxes& rSortBoxes = pTableNd->GetTable().GetTabSortBoxes();
+ SwNodeOffset nIdx = _rNd.GetIndex();
+ for (size_t n = 0; n < rSortBoxes.size(); ++n)
+ {
+ const SwStartNode* pNd = rSortBoxes[ n ]->GetSttNd();
+ if ( pNd->GetIndex() < nIdx && nIdx < pNd->EndOfSectionIndex() )
+ {
+ // The other index needs to be within the same Section
+ nIdx = aChkIdx.GetIndex();
+ return pNd->GetIndex() < nIdx && nIdx < pNd->EndOfSectionIndex();
+ }
+ }
+
+ return true;
+}
+
+static void lcl_CheckEmptyLayFrame( SwNodes const & rNds, SwSectionData& rSectionData,
+ const SwNode& rStt, const SwNode& rEnd )
+{
+ SwNodeIndex aIdx( rStt );
+ if( !SwNodes::GoPrevSection( &aIdx, true, false ) ||
+ !CheckNodesRange( rStt, aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, rStt, true ))
+ {
+ aIdx = rEnd;
+ if( !rNds.GoNextSection( &aIdx, true, false ) ||
+ !CheckNodesRange( rEnd, aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, rEnd, false ))
+ {
+ rSectionData.SetHidden( false );
+ }
+ }
+}
+
+SwSection *
+SwDoc::InsertSwSection(SwPaM const& rRange, SwSectionData & rNewData,
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode> const*const pTOXBaseAndMode,
+ SfxItemSet const*const pAttr, bool const bUpdate)
+{
+ const SwNode* pPrvNd = nullptr;
+ sal_uInt16 nRegionRet = 0;
+ if( rRange.HasMark() )
+ {
+ nRegionRet = IsInsRegionAvailable( rRange, &pPrvNd );
+ if( 0 == nRegionRet )
+ {
+ // demoted to info because this is called from SwXTextSection::attach,
+ // so it could be invalid input
+ SAL_INFO("sw.core" , "InsertSwSection: rRange overlaps other sections");
+ return nullptr;
+ }
+ }
+
+ // See if the whole Document should be hidden, which we currently are not able to do.
+ if (rNewData.IsHidden() && rRange.HasMark())
+ {
+ const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
+ if( !pStt->nContent.GetIndex() &&
+ pEnd->nNode.GetNode().GetContentNode()->Len() ==
+ pEnd->nContent.GetIndex() )
+ {
+ ::lcl_CheckEmptyLayFrame( GetNodes(),
+ rNewData,
+ pStt->nNode.GetNode(),
+ pEnd->nNode.GetNode() );
+ }
+ }
+
+ SwUndoInsSection* pUndoInsSect = nullptr;
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ if (bUndo)
+ {
+ pUndoInsSect = new SwUndoInsSection(rRange, rNewData, pAttr, pTOXBaseAndMode);
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndoInsSect) );
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ SwSectionFormat* const pFormat = MakeSectionFormat();
+ pFormat->SetName(rNewData.GetSectionName());
+ if ( pAttr )
+ {
+ pFormat->SetFormatAttr( *pAttr );
+ }
+
+ SwTOXBase const*const pTOXBase(pTOXBaseAndMode ? std::get<0>(*pTOXBaseAndMode) : nullptr);
+ SwSectionNode* pNewSectNode = nullptr;
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( (eOld & ~RedlineFlags::ShowMask) | RedlineFlags::Ignore );
+
+ if( rRange.HasMark() )
+ {
+ SwPosition *pSttPos = const_cast<SwPosition*>(rRange.Start()),
+ *pEndPos = const_cast<SwPosition*>(rRange.End());
+ if( pPrvNd && 3 == nRegionRet )
+ {
+ OSL_ENSURE( pPrvNd, "The SectionNode is missing" );
+ SwNodeIndex aStt( pSttPos->nNode ), aEnd( pEndPos->nNode, +1 );
+ while( pPrvNd != aStt.GetNode().StartOfSectionNode() )
+ --aStt;
+ while( pPrvNd != aEnd.GetNode().StartOfSectionNode() )
+ ++aEnd;
+
+ --aEnd; // End is inclusive in the InsertSection
+ pNewSectNode = GetNodes().InsertTextSection(
+ aStt, *pFormat, rNewData, pTOXBase, & aEnd);
+ }
+ else
+ {
+ if( pUndoInsSect )
+ {
+ if( !( pPrvNd && 1 == nRegionRet ) &&
+ pSttPos->nContent.GetIndex() )
+ {
+ SwTextNode* const pTNd =
+ pSttPos->nNode.GetNode().GetTextNode();
+ if (pTNd)
+ {
+ pUndoInsSect->SaveSplitNode( pTNd, true );
+ }
+ }
+
+ if ( !( pPrvNd && 2 == nRegionRet ) )
+ {
+ SwTextNode *const pTNd =
+ pEndPos->nNode.GetNode().GetTextNode();
+ if (pTNd && (pTNd->GetText().getLength()
+ != pEndPos->nContent.GetIndex()))
+ {
+ pUndoInsSect->SaveSplitNode( pTNd, false );
+ }
+ }
+ }
+
+ if( pPrvNd && 1 == nRegionRet )
+ {
+ pSttPos->nNode.Assign( *pPrvNd );
+ pSttPos->nContent.Assign( pSttPos->nNode.GetNode().GetContentNode(), 0 );
+ }
+ else if( pSttPos->nContent.GetIndex() )
+ {
+ getIDocumentContentOperations().SplitNode( *pSttPos, false );
+ }
+
+ if( pPrvNd && 2 == nRegionRet )
+ {
+ pEndPos->nNode.Assign( *pPrvNd );
+ pEndPos->nContent.Assign( pEndPos->nNode.GetNode().GetContentNode(), 0 );
+ }
+ else
+ {
+ const SwContentNode* pCNd = pEndPos->nNode.GetNode().GetContentNode();
+ if( pCNd && pCNd->Len() != pEndPos->nContent.GetIndex() )
+ {
+ sal_Int32 nContent = pSttPos->nContent.GetIndex();
+ getIDocumentContentOperations().SplitNode( *pEndPos, false );
+
+ SwTextNode* pTNd;
+ if( pEndPos->nNode.GetIndex() == pSttPos->nNode.GetIndex() )
+ {
+ --pSttPos->nNode;
+ --pEndPos->nNode;
+ pTNd = pSttPos->nNode.GetNode().GetTextNode();
+ pSttPos->nContent.Assign( pTNd, nContent );
+ }
+ else
+ {
+ // Set to the end of the previous
+ --pEndPos->nNode;
+ pTNd = pEndPos->nNode.GetNode().GetTextNode();
+ }
+ nContent = pTNd ? pTNd->GetText().getLength() : 0;
+ pEndPos->nContent.Assign( pTNd, nContent );
+ }
+ }
+ pNewSectNode = GetNodes().InsertTextSection(
+ pSttPos->nNode, *pFormat, rNewData, pTOXBase, &pEndPos->nNode);
+ }
+ }
+ else
+ {
+ const SwPosition* pPos = rRange.GetPoint();
+ const SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
+ if( !pPos->nContent.GetIndex() )
+ {
+ pNewSectNode = GetNodes().InsertTextSection(
+ pPos->nNode, *pFormat, rNewData, pTOXBase, nullptr);
+ }
+ else if( pPos->nContent.GetIndex() == pCNd->Len() )
+ {
+ pNewSectNode = GetNodes().InsertTextSection(
+ pPos->nNode, *pFormat, rNewData, pTOXBase, nullptr, false);
+ }
+ else
+ {
+ if( pUndoInsSect && pCNd->IsTextNode() )
+ {
+ pUndoInsSect->SaveSplitNode( const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pCNd)), true );
+ }
+ getIDocumentContentOperations().SplitNode( *pPos, false );
+ pNewSectNode = GetNodes().InsertTextSection(
+ pPos->nNode, *pFormat, rNewData, pTOXBase, nullptr);
+ }
+ }
+
+//FEATURE::CONDCOLL
+ pNewSectNode->CheckSectionCondColl();
+//FEATURE::CONDCOLL
+
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( *pNewSectNode->EndOfSectionNode(), *pNewSectNode, SwNodeOffset(1) );
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ }
+ else
+ {
+ getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+ }
+
+ // Is a Condition set?
+ if (rNewData.IsHidden() && !rNewData.GetCondition().isEmpty())
+ {
+ // The calculate up to that position
+ SwCalc aCalc( *this );
+ if( ! IsInReading() )
+ {
+ getIDocumentFieldsAccess().FieldsToCalc(aCalc, pNewSectNode->GetIndex(), SAL_MAX_INT32);
+ }
+ SwSection& rNewSect = pNewSectNode->GetSection();
+ rNewSect.SetCondHidden( aCalc.Calculate( rNewSect.GetCondition() ).GetBool() );
+ }
+
+ bool bUpdateFootnote = false;
+ if( !GetFootnoteIdxs().empty() && pAttr )
+ {
+ sal_uInt16 nVal = pAttr->Get( RES_FTN_AT_TXTEND ).GetValue();
+ if( ( FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal ) ||
+ ( FTNEND_ATTXTEND_OWNNUMSEQ == ( nVal = pAttr->Get( RES_END_AT_TXTEND ).GetValue() ) ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal ))
+ {
+ bUpdateFootnote = true;
+ }
+ }
+
+ if( pUndoInsSect )
+ {
+ pUndoInsSect->SetSectNdPos( pNewSectNode->GetIndex() );
+ pUndoInsSect->SetUpdateFootnoteFlag( bUpdateFootnote );
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+
+ if (rNewData.IsLinkType())
+ {
+ pNewSectNode->GetSection().CreateLink( bUpdate ? LinkCreateType::Update : LinkCreateType::Connect );
+ }
+
+ if( bUpdateFootnote )
+ {
+ GetFootnoteIdxs().UpdateFootnote( SwNodeIndex( *pNewSectNode ));
+ }
+
+ getIDocumentState().SetModified();
+ return &pNewSectNode->GetSection();
+}
+
+sal_uInt16 SwDoc::IsInsRegionAvailable( const SwPaM& rRange,
+ const SwNode** ppSttNd )
+{
+ sal_uInt16 nRet = 1;
+ if( rRange.HasMark() )
+ {
+ // See if we have a valid Section
+ const SwPosition* pStt = rRange.Start();
+ const SwPosition* pEnd = rRange.End();
+
+ const SwContentNode* pCNd = pEnd->nNode.GetNode().GetContentNode();
+ const SwNode* pNd = &pStt->nNode.GetNode();
+ const SwSectionNode* pSectNd = pNd->FindSectionNode();
+ const SwSectionNode* pEndSectNd = pCNd ? pCNd->FindSectionNode() : nullptr;
+ if( pSectNd && pEndSectNd && pSectNd != pEndSectNd )
+ {
+ // Try to create an enclosing Section, but only if Start is
+ // located at the Section's beginning and End at it's end
+ nRet = 0;
+ if( !pStt->nContent.GetIndex()
+ && pSectNd->GetIndex() == pStt->nNode.GetIndex() - 1
+ && pEnd->nContent.GetIndex() == pCNd->Len() )
+ {
+ SwNodeIndex aIdx( pStt->nNode, -1 );
+ SwNodeOffset nCmp = pEnd->nNode.GetIndex();
+ const SwStartNode* pPrvNd;
+ const SwEndNode* pNxtNd;
+ while( nullptr != ( pPrvNd = (pNd = &aIdx.GetNode())->GetSectionNode() ) &&
+ ( aIdx.GetIndex() >= nCmp ||
+ nCmp >= pPrvNd->EndOfSectionIndex() ) )
+ {
+ --aIdx;
+ }
+ if( !pPrvNd )
+ pPrvNd = pNd->IsStartNode() ? static_cast<const SwStartNode*>(pNd)
+ : pNd->StartOfSectionNode();
+
+ aIdx = pEnd->nNode.GetIndex() + 1;
+ nCmp = pStt->nNode.GetIndex();
+ while( nullptr != ( pNxtNd = (pNd = &aIdx.GetNode())->GetEndNode() ) &&
+ pNxtNd->StartOfSectionNode()->IsSectionNode() &&
+ ( pNxtNd->StartOfSectionIndex() >= nCmp ||
+ nCmp >= aIdx.GetIndex() ) )
+ {
+ ++aIdx;
+ }
+ if( !pNxtNd )
+ pNxtNd = pNd->EndOfSectionNode();
+
+ if( pPrvNd && pNxtNd && pPrvNd == pNxtNd->StartOfSectionNode() )
+ {
+ nRet = 3;
+
+ if( ppSttNd )
+ *ppSttNd = pPrvNd;
+ }
+ }
+ }
+ else if( !pSectNd && pEndSectNd )
+ {
+ // Try to create an enclosing Section, but only if the End
+ // is at the Section's end.
+ nRet = 0;
+ if( pEnd->nContent.GetIndex() == pCNd->Len() )
+ {
+ SwNodeIndex aIdx( pEnd->nNode, 1 );
+ if( aIdx.GetNode().IsEndNode() &&
+ nullptr != aIdx.GetNode().FindSectionNode() )
+ {
+ do {
+ ++aIdx;
+ } while( aIdx.GetNode().IsEndNode() &&
+ nullptr != aIdx.GetNode().FindSectionNode() );
+ {
+ nRet = 2;
+ if( ppSttNd )
+ {
+ --aIdx;
+ *ppSttNd = &aIdx.GetNode();
+ }
+ }
+ }
+ }
+ }
+ else if( pSectNd && !pEndSectNd )
+ {
+ // Try to create an enclosing Section, but only if Start
+ // is at the Section's start.
+ nRet = 0;
+ if( !pStt->nContent.GetIndex() )
+ {
+ SwNodeIndex aIdx( pStt->nNode, -1 );
+ if( aIdx.GetNode().IsSectionNode() )
+ {
+ do {
+ --aIdx;
+ } while( aIdx.GetNode().IsSectionNode() );
+ if( !aIdx.GetNode().IsSectionNode() )
+ {
+ nRet = 1;
+ if( ppSttNd )
+ {
+ ++aIdx;
+ *ppSttNd = &aIdx.GetNode();
+ }
+ }
+ }
+ }
+ }
+ }
+ return nRet;
+}
+
+SwSection* SwDoc::GetCurrSection( const SwPosition& rPos )
+{
+ const SwSectionNode* pSectNd = rPos.nNode.GetNode().FindSectionNode();
+ if( pSectNd )
+ return const_cast<SwSection*>(&pSectNd->GetSection());
+ return nullptr;
+}
+
+SwSectionFormat* SwDoc::MakeSectionFormat()
+{
+ SwSectionFormat* pNew = new SwSectionFormat( mpDfltFrameFormat.get(), this );
+ mpSectionFormatTable->push_back( pNew );
+ return pNew;
+}
+
+void SwDoc::DelSectionFormat( SwSectionFormat *pFormat, bool bDelNodes )
+{
+ SwSectionFormats::iterator itFormatPos = std::find( mpSectionFormatTable->begin(), mpSectionFormatTable->end(), pFormat );
+
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::DELSECTION, nullptr);
+
+ if( mpSectionFormatTable->end() != itFormatPos )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent( false ).GetContentIdx();
+ const SfxPoolItem* pFootnoteEndAtTextEnd = pFormat->GetItemIfSet(
+ RES_FTN_AT_TXTEND);
+ if( !pFootnoteEndAtTextEnd )
+ pFootnoteEndAtTextEnd = pFormat->GetItemIfSet(RES_END_AT_TXTEND);
+
+ const SwSectionNode* pSectNd;
+
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ if( bDelNodes && pIdx && &GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwNodeIndex aUpdIdx( *pIdx );
+ SwPaM aPaM( *pSectNd->EndOfSectionNode(), *pSectNd );
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(aPaM, SwDeleteFlags::Default));
+ if( pFootnoteEndAtTextEnd )
+ GetFootnoteIdxs().UpdateFootnote( aUpdIdx );
+ getIDocumentState().SetModified();
+ //#126178# start/end undo have to be pairs!
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::DELSECTION, nullptr);
+ return ;
+ }
+ GetIDocumentUndoRedo().AppendUndo( MakeUndoDelSection( *pFormat ) );
+ }
+ else if( bDelNodes && pIdx && &GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwNodeIndex aUpdIdx( *pIdx );
+ getIDocumentContentOperations().DeleteSection( const_cast<SwNode*>(static_cast<SwNode const *>(pSectNd)) );
+ if( pFootnoteEndAtTextEnd )
+ GetFootnoteIdxs().UpdateFootnote( aUpdIdx );
+ getIDocumentState().SetModified();
+ //#126178# start/end undo have to be pairs!
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::DELSECTION, nullptr);
+ return ;
+ }
+
+ pFormat->RemoveAllUnos();
+
+ // A ClearRedo could result in a recursive call of this function and delete some section
+ // formats, thus the position inside the SectionFormatTable could have changed
+ itFormatPos = std::find( mpSectionFormatTable->begin(), mpSectionFormatTable->end(), pFormat );
+
+ // WARNING: First remove from the array and then delete,
+ // as the Section DTOR tries to delete it's format itself.
+ mpSectionFormatTable->erase( itFormatPos );
+//FEATURE::CONDCOLL
+ SwNodeOffset nCnt(0), nSttNd(0);
+ if( pIdx && &GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ nSttNd = pSectNd->GetIndex();
+ nCnt = pSectNd->EndOfSectionIndex() - nSttNd - 1;
+ }
+//FEATURE::CONDCOLL
+
+ delete pFormat;
+
+ if( nSttNd && pFootnoteEndAtTextEnd )
+ {
+ SwNodeIndex aUpdIdx( GetNodes(), nSttNd );
+ GetFootnoteIdxs().UpdateFootnote( aUpdIdx );
+ }
+
+//FEATURE::CONDCOLL
+ SwContentNode* pCNd;
+ for( ; nCnt--; ++nSttNd )
+ if( nullptr != (pCNd = GetNodes()[ nSttNd ]->GetContentNode() ) &&
+ RES_CONDTXTFMTCOLL == pCNd->GetFormatColl()->Which() )
+ pCNd->ChkCondColl();
+//FEATURE::CONDCOLL
+ }
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::DELSECTION, nullptr);
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ { // TODO is this ever needed?
+ getIDocumentState().SetModified();
+ }
+}
+
+void SwDoc::UpdateSection( size_t const nPos, SwSectionData & rNewData,
+ SfxItemSet const*const pAttr, bool const bPreventLinkUpdate )
+{
+ SwSectionFormat* pFormat = (*mpSectionFormatTable)[ nPos ];
+ SwSection* pSection = pFormat->GetSection();
+
+ /// remember hidden condition flag of SwSection before changes
+ bool bOldCondHidden = pSection->IsCondHidden();
+
+ if (pSection->DataEquals(rNewData))
+ {
+ // Check Attributes
+ bool bOnlyAttrChg = false;
+ if( pAttr && pAttr->Count() )
+ {
+ SfxItemIter aIter( *pAttr );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if (pFormat->GetFormatAttr(pItem->Which()) != *pItem)
+ {
+ bOnlyAttrChg = true;
+ break;
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+ }
+
+ if( bOnlyAttrChg )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ MakeUndoUpdateSection( *pFormat, true ) );
+ }
+ // #i32968# Inserting columns in the section causes MakeFrameFormat
+ // to put two objects of type SwUndoFrameFormat on the undo stack.
+ // We don't want them.
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ pFormat->SetFormatAttr( *pAttr );
+ getIDocumentState().SetModified();
+ }
+ return;
+ }
+
+ // Test if the whole Content Section (Document/TableBox/Fly) should be hidden,
+ // which we're currently not able to do.
+ const SwNodeIndex* pIdx = nullptr;
+ {
+ if (rNewData.IsHidden())
+ {
+ pIdx = pFormat->GetContent().GetContentIdx();
+ if (pIdx)
+ {
+ const SwSectionNode* pSectNd =
+ pIdx->GetNode().GetSectionNode();
+ if (pSectNd)
+ {
+ ::lcl_CheckEmptyLayFrame( GetNodes(), rNewData,
+ *pSectNd, *pSectNd->EndOfSectionNode() );
+ }
+ }
+ }
+ }
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(MakeUndoUpdateSection(*pFormat, false));
+ }
+ // #i32968# Inserting columns in the section causes MakeFrameFormat to put two
+ // objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ // The LinkFileName could only consist of separators
+ OUString sCompareString = OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator);
+ const bool bUpdate =
+ (!pSection->IsLinkType() && rNewData.IsLinkType())
+ || (!rNewData.GetLinkFileName().isEmpty()
+ && (rNewData.GetLinkFileName() != sCompareString)
+ && (rNewData.GetLinkFileName() != pSection->GetLinkFileName()));
+
+ OUString sSectName( rNewData.GetSectionName() );
+ if (sSectName != pSection->GetSectionName())
+ sSectName = GetUniqueSectionName( &sSectName );
+ else
+ sSectName.clear();
+
+ /// In SwSection::operator=(..) class member m_bCondHiddenFlag is always set to true.
+ /// IMHO this have to be changed, but I can't estimate the consequences:
+ /// Either it is set to true using corresponding method <SwSection.SetCondHidden(..)>,
+ /// or it is set to the value of SwSection which is assigned to it.
+ /// Discussion with AMA results that the adjustment to the assignment operator
+ /// could be very risky.
+ pSection->SetSectionData(rNewData);
+
+ if( pAttr )
+ pSection->GetFormat()->SetFormatAttr( *pAttr );
+
+ if( !sSectName.isEmpty() )
+ {
+ pSection->SetSectionName( sSectName );
+ }
+
+ // Is a Condition set
+ if( pSection->IsHidden() && !pSection->GetCondition().isEmpty() )
+ {
+ // Then calculate up to that position
+ SwCalc aCalc( *this );
+ if( !pIdx )
+ pIdx = pFormat->GetContent().GetContentIdx();
+ getIDocumentFieldsAccess().FieldsToCalc(aCalc, pIdx->GetIndex(), SAL_MAX_INT32);
+
+ /// Because on using SwSection::operator=() to set up <pSection>
+ /// with <rNewData> and the above given note, the hidden condition flag
+ /// has to be set to false, if hidden condition flag of <pFormat->GetSection()>
+ /// (SwSection before the changes) is false (already saved in <bOldCondHidden>)
+ /// and new calculated condition is true.
+ /// This is necessary, because otherwise the <SetCondHidden> would have
+ /// no effect.
+ bool bCalculatedCondHidden =
+ aCalc.Calculate( pSection->GetCondition() ).GetBool();
+ if ( bCalculatedCondHidden && !bOldCondHidden )
+ {
+ pSection->SetCondHidden( false );
+ }
+ pSection->SetCondHidden( bCalculatedCondHidden );
+ }
+
+ if( bUpdate )
+ pSection->CreateLink( bPreventLinkUpdate ? LinkCreateType::Connect : LinkCreateType::Update );
+ else if( !pSection->IsLinkType() && pSection->IsConnected() )
+ {
+ pSection->Disconnect();
+ getIDocumentLinksAdministration().GetLinkManager().Remove( &pSection->GetBaseLink() );
+ }
+
+ getIDocumentState().SetModified();
+}
+
+void sw_DeleteFootnote( SwSectionNode *pNd, SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ SwFootnoteIdxs& rFootnoteArr = pNd->GetDoc().GetFootnoteIdxs();
+ if( rFootnoteArr.empty() )
+ return;
+
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( SwNodeIndex( *pNd ), &nPos );
+ SwTextFootnote* pSrch;
+
+ // Delete all succeeding Footnotes
+ while( nPos < rFootnoteArr.size() &&
+ SwTextFootnote_GetIndex( (pSrch = rFootnoteArr[ nPos ]) ) <= nEnd )
+ {
+ // If the Nodes are not deleted, they need to deregister at the Pages
+ // (delete Frames) or else they will remain there (Undo does not delete them!)
+ pSrch->DelFrames(nullptr);
+ ++nPos;
+ }
+
+ while( nPos-- &&
+ SwTextFootnote_GetIndex( (pSrch = rFootnoteArr[ nPos ]) ) >= nStt )
+ {
+ // If the Nodes are not deleted, they need to deregister at the Pages
+ // (delete Frames) or else they will remain there (Undo does not delete them!)
+ pSrch->DelFrames(nullptr);
+ }
+}
+
+static bool lcl_IsTOXSection(SwSectionData const& rSectionData)
+{
+ return (SectionType::ToxContent == rSectionData.GetType())
+ || (SectionType::ToxHeader == rSectionData.GetType());
+}
+
+SwSectionNode* SwNodes::InsertTextSection(SwNodeIndex const& rNdIdx,
+ SwSectionFormat& rSectionFormat,
+ SwSectionData const& rSectionData,
+ SwTOXBase const*const pTOXBase,
+ SwNodeIndex const*const pEnd,
+ bool const bInsAtStart, bool const bCreateFrames)
+{
+ SwNodeIndex aInsPos( rNdIdx );
+ if( !pEnd ) // No Area, thus create a new Section before/after it
+ {
+ // #i26762#
+ OSL_ENSURE(!pEnd || rNdIdx <= *pEnd,
+ "Section start and end in wrong order!");
+
+ if( bInsAtStart )
+ {
+ if (!lcl_IsTOXSection(rSectionData))
+ {
+ do {
+ --aInsPos;
+ } while( aInsPos.GetNode().IsSectionNode() );
+ ++aInsPos;
+ }
+ }
+ else
+ {
+ ++aInsPos;
+ if (!lcl_IsTOXSection(rSectionData))
+ {
+ SwNode* pNd;
+ while( aInsPos.GetIndex() < Count() - 1 &&
+ ( pNd = &aInsPos.GetNode())->IsEndNode() &&
+ pNd->StartOfSectionNode()->IsSectionNode())
+ {
+ ++aInsPos;
+ }
+ }
+ }
+ }
+
+ SwSectionNode *const pSectNd =
+ new SwSectionNode(aInsPos, rSectionFormat, pTOXBase);
+
+ if (lcl_IsTOXSection(rSectionData))
+ {
+ // We're inserting a ToX. Make sure that if a redline ends right before the ToX start, then
+ // that end now doesn't cross a section start node.
+ SwRedlineTable& rRedlines = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type nIndex = 0; nIndex < rRedlines.size(); ++nIndex)
+ {
+ SwRangeRedline* pRedline = rRedlines[nIndex];
+ if ( RedlineType::Delete != pRedline->GetType() ||
+ !pRedline->HasMark() || pRedline->GetMark()->nNode != aInsPos )
+ {
+ continue;
+ }
+
+ // The redline ends at the new section content start, so it originally ended before the
+ // section start: move it back.
+ SwPaM aRedlineEnd(*pRedline->GetMark());
+ aRedlineEnd.Move(fnMoveBackward);
+ *pRedline->GetMark() = *aRedlineEnd.GetPoint();
+ break;
+ }
+ }
+
+ if( pEnd )
+ {
+ // Special case for the Reader/Writer
+ if( &pEnd->GetNode() != &GetEndOfContent() )
+ aInsPos = pEnd->GetIndex()+1;
+ // #i58710: We created a RTF document with a section break inside a table cell
+ // We are not able to handle a section start inside a table and the section end outside.
+ const SwNode* pLastNode = pSectNd->StartOfSectionNode()->EndOfSectionNode();
+ if( aInsPos > pLastNode->GetIndex() )
+ aInsPos = pLastNode->GetIndex();
+ // Another way round: if the section starts outside a table but the end is inside...
+ // aInsPos is at the moment the Position where my EndNode will be inserted
+ const SwStartNode* pStartNode = aInsPos.GetNode().StartOfSectionNode();
+ // This StartNode should be in front of me, but if not, I want to survive
+ SwNodeOffset nMyIndex = pSectNd->GetIndex();
+ if( pStartNode->GetIndex() > nMyIndex ) // Suspicious!
+ {
+ const SwNode* pTemp;
+ do
+ {
+ pTemp = pStartNode; // pTemp is a suspicious one
+ pStartNode = pStartNode->StartOfSectionNode();
+ }
+ while( pStartNode->GetIndex() > nMyIndex );
+ pTemp = pTemp->EndOfSectionNode();
+ // If it starts behind me but ends behind my end...
+ if( pTemp->GetIndex() >= aInsPos.GetIndex() )
+ aInsPos = pTemp->GetIndex()+1; // ...I have to correct my end position
+ }
+ }
+ else
+ {
+ SwTextNode* pCpyTNd = rNdIdx.GetNode().GetTextNode();
+ if( pCpyTNd )
+ {
+ SwTextNode* pTNd = new SwTextNode( aInsPos, pCpyTNd->GetTextColl() );
+ if( pCpyTNd->HasSwAttrSet() )
+ {
+ // Move PageDesc/Break to the first Node of the section
+ const SfxItemSet& rSet = *pCpyTNd->GetpSwAttrSet();
+ if( SfxItemState::SET == rSet.GetItemState( RES_BREAK ) ||
+ SfxItemState::SET == rSet.GetItemState( RES_PAGEDESC ))
+ {
+ SfxItemSet aSet( rSet );
+ if( bInsAtStart )
+ pCpyTNd->ResetAttr( RES_PAGEDESC, RES_BREAK );
+ else
+ {
+ aSet.ClearItem( RES_PAGEDESC );
+ aSet.ClearItem( RES_BREAK );
+ }
+ pTNd->SetAttr( aSet );
+ }
+ else
+ pTNd->SetAttr( rSet );
+ }
+ // Do not forget to create the Frame!
+ pCpyTNd->MakeFramesForAdjacentContentNode(*pTNd);
+ }
+ else
+ new SwTextNode( aInsPos, GetDoc().GetDfltTextFormatColl() );
+ }
+ new SwEndNode( aInsPos, *pSectNd );
+
+ pSectNd->GetSection().SetSectionData(rSectionData);
+ SwSectionFormat* pSectFormat = pSectNd->GetSection().GetFormat();
+
+ // We could optimize this, by not removing already contained Frames and recreating them,
+ // but by simply rewiring them
+ bool bInsFrame = bCreateFrames && !pSectNd->GetSection().IsHiddenFlag() &&
+ GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ SwNode2LayoutSaveUpperFrames *pNode2Layout = nullptr;
+ if( bInsFrame )
+ {
+ SwNodeIndex aTmp( *pSectNd );
+ if( !pSectNd->GetNodes().FindPrvNxtFrameNode( aTmp, pSectNd->EndOfSectionNode() ) )
+ // Collect all Uppers
+ pNode2Layout = new SwNode2LayoutSaveUpperFrames(*pSectNd);
+ }
+
+ // Set the right StartNode for all in this Area
+ SwNodeOffset nEnd = pSectNd->EndOfSectionIndex();
+ SwNodeOffset nStart = pSectNd->GetIndex()+1;
+ SwNodeOffset nSkipIdx = NODE_OFFSET_MAX;
+ for( SwNodeOffset n = nStart; n < nEnd; ++n )
+ {
+ SwNode* pNd = (*this)[n];
+
+ // Attach all Sections in the NodeSection underneath the new one
+ if( NODE_OFFSET_MAX == nSkipIdx )
+ pNd->m_pStartOfSection = pSectNd;
+ else if( n >= nSkipIdx )
+ nSkipIdx = NODE_OFFSET_MAX;
+
+ if( pNd->IsStartNode() )
+ {
+ // Make up the Format's nesting
+ if( pNd->IsSectionNode() )
+ {
+ static_cast<SwSectionNode*>(pNd)->GetSection().GetFormat()->
+ SetDerivedFrom( pSectFormat );
+ static_cast<SwSectionNode*>(pNd)->DelFrames();
+ n = pNd->EndOfSectionIndex();
+ }
+ else
+ {
+ if( pNd->IsTableNode() )
+ static_cast<SwTableNode*>(pNd)->DelFrames();
+
+ if( NODE_OFFSET_MAX == nSkipIdx )
+ nSkipIdx = pNd->EndOfSectionIndex();
+ }
+ }
+ else if( pNd->IsContentNode() )
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ }
+
+ sw_DeleteFootnote( pSectNd, nStart, nEnd );
+
+ if( bInsFrame )
+ {
+ if( pNode2Layout )
+ {
+ SwNodeOffset nIdx = pSectNd->GetIndex();
+ pNode2Layout->RestoreUpperFrames( pSectNd->GetNodes(), nIdx, nIdx + 1 );
+ delete pNode2Layout;
+ }
+ else
+ pSectNd->MakeOwnFrames(&aInsPos);
+ }
+
+ return pSectNd;
+}
+
+SwSectionNode* SwNode::FindSectionNode()
+{
+ if( IsSectionNode() )
+ return GetSectionNode();
+ SwStartNode* pTmp = m_pStartOfSection;
+ while( !pTmp->IsSectionNode() && pTmp->GetIndex() )
+ pTmp = pTmp->m_pStartOfSection;
+ return pTmp->GetSectionNode();
+}
+
+// SwSectionNode
+
+// ugly hack to make m_pSection const
+static SwSectionFormat &
+lcl_initParent(SwSectionNode & rThis, SwSectionFormat & rFormat)
+{
+ SwSectionNode *const pParent =
+ rThis.StartOfSectionNode()->FindSectionNode();
+ if( pParent )
+ {
+ // Register the Format at the right Parent
+ rFormat.SetDerivedFrom( pParent->GetSection().GetFormat() );
+ }
+ return rFormat;
+}
+
+SwSectionNode::SwSectionNode(SwNodeIndex const& rIdx,
+ SwSectionFormat & rFormat, SwTOXBase const*const pTOXBase)
+ : SwStartNode( rIdx, SwNodeType::Section )
+ , m_pSection( pTOXBase
+ ? new SwTOXBaseSection(*pTOXBase, lcl_initParent(*this, rFormat))
+ : new SwSection( SectionType::Content, rFormat.GetName(),
+ lcl_initParent(*this, rFormat) ) )
+{
+ // Set the connection from Format to Node
+ // Suppress Modify; no one's interested anyway
+ rFormat.LockModify();
+ rFormat.SetFormatAttr( SwFormatContent( this ) );
+ rFormat.UnlockModify();
+}
+
+SwSectionNode::~SwSectionNode()
+{
+ // mba: test if iteration works as clients will be removed in callback
+ // use hint which allows to specify, if the content shall be saved or not
+ m_pSection->GetFormat()->CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( true ) );
+ SwSectionFormat* pFormat = m_pSection->GetFormat();
+ if( pFormat )
+ {
+ // Remove the Attribute, because the Section deletes it's Format
+ // and it will neutralize the Section, if the Content Attribute is set
+ pFormat->LockModify();
+ pFormat->ResetFormatAttr( RES_CNTNT );
+ pFormat->UnlockModify();
+ }
+}
+
+SwFrame *SwSectionNode::MakeFrame( SwFrame *pSib )
+{
+ m_pSection->m_Data.SetHiddenFlag(false);
+ return new SwSectionFrame( *m_pSection, pSib );
+}
+
+// Creates all Document Views for the preceding Node.
+// The created ContentFrames are attached to the corresponding Layout
+void SwSectionNode::MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)
+{
+ // Take my successive or preceding ContentFrame
+ SwNodes& rNds = GetNodes();
+ if( !(rNds.IsDocNodes() && rNds.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ return;
+
+ if (GetSection().IsHiddenFlag() || IsContentHidden())
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode() );
+ SwContentNode* pCNd = rNds.GoNextSection( &aIdx, true, false );
+ if( !pCNd )
+ {
+ aIdx = *this;
+ pCNd = SwNodes::GoPrevSection(&aIdx, true, false);
+ if (!pCNd)
+ return;
+ }
+ pCNd = aIdx.GetNode().GetContentNode();
+ pCNd->MakeFramesForAdjacentContentNode(static_cast<SwContentNode&>(rIdx.GetNode()));
+ }
+ else
+ {
+ SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
+ SwFrame *pFrame;
+ while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
+ {
+ OSL_ENSURE( pFrame->IsSctFrame(), "Depend of Section not a Section." );
+ if (pFrame->getRootFrame()->HasMergedParas()
+ && !rIdx.GetNode().IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ SwFrame *pNew = rIdx.GetNode().GetContentNode()->MakeFrame( pFrame );
+
+ SwSectionNode* pS = rIdx.GetNode().FindSectionNode();
+
+ // Assure that node is not inside a table, which is inside the
+ // found section.
+ if ( pS )
+ {
+ SwTableNode* pTableNode = rIdx.GetNode().FindTableNode();
+ if ( pTableNode &&
+ pTableNode->GetIndex() > pS->GetIndex() )
+ {
+ pS = nullptr;
+ }
+ }
+
+ // if the node is in a section, the sectionframe now
+ // has to be created...
+ // boolean to control <Init()> of a new section frame.
+ bool bInitNewSect = false;
+ if( pS )
+ {
+ SwSectionFrame *pSct = new SwSectionFrame( pS->GetSection(), pFrame );
+ // prepare <Init()> of new section frame.
+ bInitNewSect = true;
+ SwLayoutFrame* pUp = pSct;
+ while( pUp->Lower() ) // for columned sections
+ {
+ OSL_ENSURE( pUp->Lower()->IsLayoutFrame(),"Who's in there?" );
+ pUp = static_cast<SwLayoutFrame*>(pUp->Lower());
+ }
+ pNew->Paste( pUp );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pNew->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ pNew = pSct;
+ }
+
+ // If a Node got Frames attached before or after
+ if ( rIdx < GetIndex() )
+ // the new one precedes me
+ pNew->Paste( pFrame->GetUpper(), pFrame );
+ else
+ // the new one succeeds me
+ pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pNew->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if ( bInitNewSect )
+ static_cast<SwSectionFrame*>(pNew)->Init();
+ }
+ }
+}
+
+// Create a new SectionFrame for every occurrence in the Layout and insert before
+// the corresponding ContentFrame
+void SwSectionNode::MakeOwnFrames(SwNodeIndex* pIdxBehind, SwNodeIndex* pEndIdx)
+{
+ OSL_ENSURE( pIdxBehind, "no Index" );
+ SwNodes& rNds = GetNodes();
+ SwDoc& rDoc = rNds.GetDoc();
+
+ *pIdxBehind = *this;
+
+ m_pSection->m_Data.SetHiddenFlag(true);
+
+ if( rNds.IsDocNodes() )
+ {
+ SwNodeIndex *pEnd = pEndIdx ? pEndIdx :
+ new SwNodeIndex( *EndOfSectionNode(), 1 );
+ ::MakeFrames( &rDoc, *pIdxBehind, *pEnd );
+ if( !pEndIdx )
+ delete pEnd;
+ }
+}
+
+void SwSectionNode::DelFrames(SwRootFrame const*const /*FIXME TODO*/, bool const bForce)
+{
+ SwNodeOffset nStt = GetIndex()+1, nEnd = EndOfSectionIndex();
+ if( nStt >= nEnd )
+ {
+ return ;
+ }
+
+ SwNodes& rNds = GetNodes();
+ m_pSection->GetFormat()->DelFrames();
+
+ // Update our Flag
+ m_pSection->m_Data.SetHiddenFlag(true);
+
+ // If the Area is within a Fly or TableBox, we can only hide it if
+ // there is more Content which has Frames.
+ // Or else the Fly/TableBox Frame does not have a Lower!
+ if (bForce)
+ return;
+
+ SwNodeIndex aIdx( *this );
+ if( !SwNodes::GoPrevSection( &aIdx, true, false ) ||
+ !CheckNodesRange( *this, aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, *this, true ))
+ {
+ aIdx = *EndOfSectionNode();
+ if( !rNds.GoNextSection( &aIdx, true, false ) ||
+ !CheckNodesRange( *EndOfSectionNode(), aIdx, true ) ||
+ // #i21457#
+ !lcl_IsInSameTableBox( rNds, *EndOfSectionNode(), false ))
+ {
+ m_pSection->m_Data.SetHiddenFlag(false);
+ }
+ }
+}
+
+SwSectionNode* SwSectionNode::MakeCopy( SwDoc& rDoc, const SwNodeIndex& rIdx ) const
+{
+ // In which array am I: Nodes, UndoNodes?
+ const SwNodes& rNds = GetNodes();
+
+ // Copy the SectionFrameFormat
+ SwSectionFormat* pSectFormat = rDoc.MakeSectionFormat();
+ pSectFormat->CopyAttrs( *GetSection().GetFormat() );
+
+ std::unique_ptr<SwTOXBase> pTOXBase;
+ if (SectionType::ToxContent == GetSection().GetType())
+ {
+ assert( dynamic_cast< const SwTOXBaseSection* >( &GetSection() ) && "no TOXBaseSection!" );
+ SwTOXBaseSection const& rTBS(
+ dynamic_cast<SwTOXBaseSection const&>(GetSection()));
+ pTOXBase.reset( new SwTOXBase(rTBS, &rDoc) );
+ }
+
+ SwSectionNode *const pSectNd =
+ new SwSectionNode(rIdx, *pSectFormat, pTOXBase.get());
+ SwEndNode* pEndNd = new SwEndNode( rIdx, *pSectNd );
+ SwNodeIndex aInsPos( *pEndNd );
+
+ // Take over values
+ SwSection *const pNewSect = pSectNd->m_pSection.get();
+
+ if (SectionType::ToxContent != GetSection().GetType())
+ {
+ // Keep the Name for Move
+ if( &rNds.GetDoc() == &rDoc && rDoc.IsCopyIsMove() )
+ {
+ pNewSect->SetSectionName( GetSection().GetSectionName() );
+ }
+ else
+ {
+ const OUString sSectionName(GetSection().GetSectionName());
+ pNewSect->SetSectionName(rDoc.GetUniqueSectionName( &sSectionName ));
+ }
+ }
+
+ pNewSect->SetType( GetSection().GetType() );
+ pNewSect->SetCondition( GetSection().GetCondition() );
+ pNewSect->SetCondHidden( GetSection().IsCondHidden() );
+ pNewSect->SetLinkFileName( GetSection().GetLinkFileName() );
+ if( !pNewSect->IsHiddenFlag() && GetSection().IsHidden() )
+ pNewSect->SetHidden();
+ if( !pNewSect->IsProtectFlag() && GetSection().IsProtect() )
+ pNewSect->SetProtect();
+ // edit in readonly sections
+ if( !pNewSect->IsEditInReadonlyFlag() && GetSection().IsEditInReadonly() )
+ pNewSect->SetEditInReadonly();
+
+ SwNodeRange aRg( *this, SwNodeOffset(+1), *EndOfSectionNode() ); // Where am I?
+ rNds.Copy_( aRg, aInsPos, false );
+
+ // Delete all Frames from the copied Area. They are created when creating
+ // the SectionFrames.
+ pSectNd->DelFrames();
+
+ // Copy the Links/Server
+ if( pNewSect->IsLinkType() ) // Add the Link
+ pNewSect->CreateLink( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() ? LinkCreateType::Connect : LinkCreateType::NONE );
+
+ // If we copy from the Undo as Server, enter it again
+ if (m_pSection->IsServer()
+ && rDoc.GetIDocumentUndoRedo().IsUndoNodes(rNds))
+ {
+ pNewSect->SetRefObject( m_pSection->GetObject() );
+ rDoc.getIDocumentLinksAdministration().GetLinkManager().InsertServer( pNewSect->GetObject() );
+ }
+
+ // METADATA: copy xml:id; must be done after insertion of node
+ pSectFormat->RegisterAsCopyOf(*GetSection().GetFormat());
+
+ return pSectNd;
+}
+
+bool SwSectionNode::IsContentHidden() const
+{
+ OSL_ENSURE( !m_pSection->IsHidden(),
+ "That's simple: Hidden Section => Hidden Content" );
+ SwNodeIndex aTmp( *this, 1 );
+ SwNodeOffset nEnd = EndOfSectionIndex();
+ while( aTmp < nEnd )
+ {
+ if( aTmp.GetNode().IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode&>(aTmp.GetNode()).GetSection();
+ if( rSect.IsHiddenFlag() )
+ // Skip this Section
+ aTmp = *aTmp.GetNode().EndOfSectionNode();
+ }
+ else
+ {
+ if( aTmp.GetNode().IsContentNode() || aTmp.GetNode().IsTableNode() )
+ return false; // We found non-hidden content
+ OSL_ENSURE( aTmp.GetNode().IsEndNode(), "EndNode expected" );
+ }
+ ++aTmp;
+ }
+ return true; // Hide everything
+}
+
+void SwSectionNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("section"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("type"),
+ BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (m_pSection)
+ {
+ m_pSection->dumpAsXml(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+void SwSectionNode::NodesArrChgd()
+{
+ SwSectionFormat *const pFormat = m_pSection->GetFormat();
+ if( !pFormat )
+ return;
+
+ SwNodes& rNds = GetNodes();
+ SwDoc* pDoc = pFormat->GetDoc();
+
+ if( !rNds.IsDocNodes() )
+ {
+ pFormat->RemoveAllUnos();
+ }
+
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( SwFormatContent( this ));
+ pFormat->UnlockModify();
+
+ SwSectionNode* pSectNd = StartOfSectionNode()->FindSectionNode();
+ // set the correct parent from the new section
+ pFormat->SetDerivedFrom( pSectNd ? pSectNd->GetSection().GetFormat()
+ : pDoc->GetDfltFrameFormat() );
+
+ // Set the right StartNode for all in this Area
+ SwNodeOffset nStart = GetIndex()+1, nEnd = EndOfSectionIndex();
+ for( SwNodeOffset n = nStart; n < nEnd; ++n )
+ {
+ // Make up the Format's nesting
+ pSectNd = rNds[ n ]->GetSectionNode();
+ if( nullptr != pSectNd )
+ {
+ pSectNd->GetSection().GetFormat()->SetDerivedFrom( pFormat );
+ n = pSectNd->EndOfSectionIndex();
+ }
+ }
+
+ // Moving Nodes to the UndoNodes array?
+ if( rNds.IsDocNodes() )
+ {
+ OSL_ENSURE( pDoc == &GetDoc(),
+ "Moving to different Documents?" );
+ if( m_pSection->IsLinkType() ) // Remove the Link
+ m_pSection->CreateLink( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ? LinkCreateType::Connect : LinkCreateType::NONE );
+
+ if (m_pSection->IsServer())
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertServer( m_pSection->GetObject() );
+ }
+ else
+ {
+ if (SectionType::Content != m_pSection->GetType()
+ && m_pSection->IsConnected())
+ {
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( &m_pSection->GetBaseLink() );
+ }
+ if (m_pSection->IsServer())
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_pSection->GetObject() );
+ }
+
+}
+
+OUString SwDoc::GetUniqueSectionName( const OUString* pChkStr ) const
+{
+ if( IsInMailMerge())
+ {
+ OUString newName = "MailMergeSection"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpSectionFormatTable->size() + 1 );
+ if( pChkStr )
+ newName += *pChkStr;
+ return newName;
+ }
+
+ const OUString aName(SwResId(STR_REGION_DEFNAME));
+
+ SwSectionFormats::size_type nNum = 0;
+ const SwSectionFormats::size_type nFlagSize = ( mpSectionFormatTable->size() / 8 ) + 2;
+ std::unique_ptr<sal_uInt8[]> pSetFlags(new sal_uInt8[ nFlagSize ]);
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ for( auto pFormat : *mpSectionFormatTable )
+ {
+ const SwSectionNode *const pSectNd = pFormat->GetSectionNode();
+ if( pSectNd != nullptr )
+ {
+ const OUString& rNm = pSectNd->GetSection().GetSectionName();
+ if (rNm.startsWith( aName ))
+ {
+ // Calculate the Number and reset the Flag
+ nNum = o3tl::toInt32(rNm.subView( aName.getLength() ));
+ if( nNum-- && nNum < mpSectionFormatTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ if( pChkStr && *pChkStr==rNm )
+ pChkStr = nullptr;
+ }
+ }
+
+ if( !pChkStr )
+ {
+ // Flagged all Numbers accordingly, so get the right Number
+ nNum = mpSectionFormatTable->size();
+ for( SwSectionFormats::size_type n = 0; n < nFlagSize; ++n )
+ {
+ auto nTmp = pSetFlags[ n ];
+ if( nTmp != 0xFF )
+ {
+ // Calculate the Number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+ }
+ pSetFlags.reset();
+ if( pChkStr )
+ return *pChkStr;
+ return aName + OUString::number( ++nNum );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndsect.hxx b/sw/source/core/docnode/ndsect.hxx
new file mode 100644
index 000000000..57a9f7080
--- /dev/null
+++ b/sw/source/core/docnode/ndsect.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_DOCNODE_NDSECT_HXX
+#define INCLUDED_SW_SOURCE_CORE_DOCNODE_NDSECT_HXX
+
+#include <nodeoffset.hxx>
+
+class SwSectionNode;
+
+void sw_DeleteFootnote(SwSectionNode* pNd, SwNodeOffset nStt, SwNodeOffset nEnd);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx
new file mode 100644
index 000000000..e76a0bbf1
--- /dev/null
+++ b/sw/source/core/docnode/ndtbl.cxx
@@ -0,0 +1,4684 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <config_wasm_strip.h>
+
+#include <memory>
+#include <fesh.hxx>
+#include <hintids.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <svl/stritem.hxx>
+#include <editeng/shaditem.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtfordr.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtanchr.hxx>
+#include <fmtlsplt.hxx>
+#include <frmatr.hxx>
+#include <cellfrm.hxx>
+#include <pagefrm.hxx>
+#include <tabcol.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <UndoManager.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <cntfrm.hxx>
+#include <pam.hxx>
+#include <swcrsr.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <tblsel.hxx>
+#include <poolfmt.hxx>
+#include <tabfrm.hxx>
+#include <UndoCore.hxx>
+#include <UndoRedline.hxx>
+#include <UndoDelete.hxx>
+#include <UndoNumbering.hxx>
+#include <UndoTable.hxx>
+#include <hints.hxx>
+#include <tblafmt.hxx>
+#include <frminf.hxx>
+#include <cellatr.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+#include <rolbck.hxx>
+#include <tblrwcl.hxx>
+#include <editsh.hxx>
+#include <txtfrm.hxx>
+#include <section.hxx>
+#include <frmtool.hxx>
+#include <node2lay.hxx>
+#include <strings.hrc>
+#include <docsh.hxx>
+#include <unochart.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <cstdlib>
+#include <map>
+#include <algorithm>
+#include <rootfrm.hxx>
+#include <fldupde.hxx>
+#include <calbck.hxx>
+#include <fntcache.hxx>
+#include <frameformats.hxx>
+#include <o3tl/numeric.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/numformat.hxx>
+#include <tools/datetimeutils.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+const sal_Unicode T2T_PARA = 0x0a;
+
+static void lcl_SetDfltBoxAttr( SwFrameFormat& rFormat, sal_uInt8 nId )
+{
+ bool bTop = false, bBottom = false, bLeft = false, bRight = false;
+ switch ( nId )
+ {
+ case 0: bTop = bBottom = bLeft = true; break;
+ case 1: bTop = bBottom = bLeft = bRight = true; break;
+ case 2: bBottom = bLeft = true; break;
+ case 3: bBottom = bLeft = bRight = true; break;
+ }
+
+ const bool bHTML = rFormat.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE);
+ Color aCol( bHTML ? COL_GRAY : COL_BLACK );
+ // Default border in Writer: 0.5pt (matching Word)
+ SvxBorderLine aLine( &aCol, SvxBorderLineWidth::VeryThin );
+ if ( bHTML )
+ {
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ }
+ SvxBoxItem aBox(RES_BOX);
+ aBox.SetAllDistances(55);
+ if ( bTop )
+ aBox.SetLine( &aLine, SvxBoxItemLine::TOP );
+ if ( bBottom )
+ aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ if ( bLeft )
+ aBox.SetLine( &aLine, SvxBoxItemLine::LEFT );
+ if ( bRight )
+ aBox.SetLine( &aLine, SvxBoxItemLine::RIGHT );
+ rFormat.SetFormatAttr( aBox );
+}
+
+typedef std::map<SwFrameFormat *, SwTableBoxFormat *> DfltBoxAttrMap_t;
+typedef std::vector<DfltBoxAttrMap_t *> DfltBoxAttrList_t;
+
+static void
+lcl_SetDfltBoxAttr(SwTableBox& rBox, DfltBoxAttrList_t & rBoxFormatArr,
+ sal_uInt8 const nId, SwTableAutoFormat const*const pAutoFormat = nullptr)
+{
+ DfltBoxAttrMap_t * pMap = rBoxFormatArr[ nId ];
+ if (!pMap)
+ {
+ pMap = new DfltBoxAttrMap_t;
+ rBoxFormatArr[ nId ] = pMap;
+ }
+
+ SwTableBoxFormat* pNewTableBoxFormat = nullptr;
+ SwFrameFormat* pBoxFrameFormat = rBox.GetFrameFormat();
+ DfltBoxAttrMap_t::iterator const iter(pMap->find(pBoxFrameFormat));
+ if (pMap->end() != iter)
+ {
+ pNewTableBoxFormat = iter->second;
+ }
+ else
+ {
+ SwDoc* pDoc = pBoxFrameFormat->GetDoc();
+ // format does not exist, so create it
+ pNewTableBoxFormat = pDoc->MakeTableBoxFormat();
+ pNewTableBoxFormat->SetFormatAttr( pBoxFrameFormat->GetAttrSet().Get( RES_FRM_SIZE ) );
+
+ if( pAutoFormat )
+ pAutoFormat->UpdateToSet( nId, false, false,
+ const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pNewTableBoxFormat->GetAttrSet())),
+ SwTableAutoFormatUpdateFlags::Box,
+ pDoc->GetNumberFormatter() );
+ else
+ ::lcl_SetDfltBoxAttr( *pNewTableBoxFormat, nId );
+
+ (*pMap)[pBoxFrameFormat] = pNewTableBoxFormat;
+ }
+ rBox.ChgFrameFormat( pNewTableBoxFormat );
+}
+
+static SwTableBoxFormat *lcl_CreateDfltBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
+ sal_uInt16 nCols, sal_uInt8 nId )
+{
+ if ( !rBoxFormatArr[nId] )
+ {
+ SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
+ if( USHRT_MAX != nCols )
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ USHRT_MAX / nCols, 0 ));
+ ::lcl_SetDfltBoxAttr( *pBoxFormat, nId );
+ rBoxFormatArr[ nId ] = pBoxFormat;
+ }
+ return rBoxFormatArr[nId];
+}
+
+static SwTableBoxFormat *lcl_CreateAFormatBoxFormat( SwDoc &rDoc, std::vector<SwTableBoxFormat*> &rBoxFormatArr,
+ const SwTableAutoFormat& rAutoFormat,
+ const sal_uInt16 nRows, const sal_uInt16 nCols, sal_uInt8 nId )
+{
+ if( !rBoxFormatArr[nId] )
+ {
+ SwTableBoxFormat* pBoxFormat = rDoc.MakeTableBoxFormat();
+ rAutoFormat.UpdateToSet( nId, nRows==1, nCols==1,
+ const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pBoxFormat->GetAttrSet())),
+ SwTableAutoFormatUpdateFlags::Box,
+ rDoc.GetNumberFormatter( ) );
+ if( USHRT_MAX != nCols )
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable,
+ USHRT_MAX / nCols, 0 ));
+ rBoxFormatArr[ nId ] = pBoxFormat;
+ }
+ return rBoxFormatArr[nId];
+}
+
+SwTableNode* SwDoc::IsIdxInTable(const SwNodeIndex& rIdx)
+{
+ SwTableNode* pTableNd = nullptr;
+ SwNodeOffset nIndex = rIdx.GetIndex();
+ do {
+ SwNode* pNd = GetNodes()[ nIndex ]->StartOfSectionNode();
+ pTableNd = pNd->GetTableNode();
+ if( nullptr != pTableNd )
+ break;
+
+ nIndex = pNd->GetIndex();
+ } while ( nIndex );
+ return pTableNd;
+}
+
+/**
+ * Insert a new Box before the InsPos
+ */
+bool SwNodes::InsBoxen( SwTableNode* pTableNd,
+ SwTableLine* pLine,
+ SwTableBoxFormat* pBoxFormat,
+ SwTextFormatColl* pTextColl,
+ const SfxItemSet* pAutoAttr,
+ sal_uInt16 nInsPos,
+ sal_uInt16 nCnt )
+{
+ if( !nCnt )
+ return false;
+ OSL_ENSURE( pLine, "No valid Line" );
+
+ // Move Index after the Line's last Box
+ SwNodeOffset nIdxPos(0);
+ SwTableBox *pPrvBox = nullptr, *pNxtBox = nullptr;
+ if( !pLine->GetTabBoxes().empty() )
+ {
+ if( nInsPos < pLine->GetTabBoxes().size() )
+ {
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable(),
+ pLine->GetTabBoxes()[ nInsPos ] );
+ if( nullptr == pPrvBox )
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
+ }
+ else
+ {
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable(),
+ pLine->GetTabBoxes().back() );
+ if( nullptr == pNxtBox )
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
+ }
+ }
+ else
+ {
+ pNxtBox = pLine->FindNextBox( pTableNd->GetTable() );
+ if( nullptr == pNxtBox )
+ pPrvBox = pLine->FindPreviousBox( pTableNd->GetTable() );
+ }
+
+ if( !pPrvBox && !pNxtBox )
+ {
+ bool bSetIdxPos = true;
+ if( !pTableNd->GetTable().GetTabLines().empty() && !nInsPos )
+ {
+ const SwTableLine* pTableLn = pLine;
+ while( pTableLn->GetUpper() )
+ pTableLn = pTableLn->GetUpper()->GetUpper();
+
+ if( pTableNd->GetTable().GetTabLines()[ 0 ] == pTableLn )
+ {
+ // Before the Table's first Box
+ while( !( pNxtBox = pLine->GetTabBoxes()[0])->GetTabLines().empty() )
+ pLine = pNxtBox->GetTabLines()[0];
+ nIdxPos = pNxtBox->GetSttIdx();
+ bSetIdxPos = false;
+ }
+ }
+ if( bSetIdxPos )
+ // Tables without content or at the end; move before the End
+ nIdxPos = pTableNd->EndOfSectionIndex();
+ }
+ else if( pNxtBox ) // There is a successor
+ nIdxPos = pNxtBox->GetSttIdx();
+ else // There is a predecessor
+ nIdxPos = pPrvBox->GetSttNd()->EndOfSectionIndex() + 1;
+
+ SwNodeIndex aEndIdx( *this, nIdxPos );
+ for( sal_uInt16 n = 0; n < nCnt; ++n )
+ {
+ SwStartNode* pSttNd = new SwStartNode( aEndIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+ new SwEndNode( aEndIdx, *pSttNd );
+
+ pPrvBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+
+ SwTableBoxes & rTabBoxes = pLine->GetTabBoxes();
+ sal_uInt16 nRealInsPos = nInsPos + n;
+ if (nRealInsPos > rTabBoxes.size())
+ nRealInsPos = rTabBoxes.size();
+
+ rTabBoxes.insert( rTabBoxes.begin() + nRealInsPos, pPrvBox );
+
+ if( ! pTextColl->IsAssignedToListLevelOfOutlineStyle()
+//FEATURE::CONDCOLL
+ && RES_CONDTXTFMTCOLL != pTextColl->Which()
+//FEATURE::CONDCOLL
+ )
+ new SwTextNode( SwNodeIndex( *pSttNd->EndOfSectionNode() ),
+ pTextColl, pAutoAttr );
+ else
+ {
+ // Handle Outline numbering correctly!
+ SwTextNode* pTNd = new SwTextNode(
+ SwNodeIndex( *pSttNd->EndOfSectionNode() ),
+ GetDoc().GetDfltTextFormatColl(),
+ pAutoAttr );
+ pTNd->ChgFormatColl( pTextColl );
+ }
+ }
+ return true;
+}
+
+/**
+ * Insert a new Table
+ */
+const SwTable* SwDoc::InsertTable( const SwInsertTableOptions& rInsTableOpts,
+ const SwPosition& rPos, sal_uInt16 nRows,
+ sal_uInt16 nCols, sal_Int16 eAdjust,
+ const SwTableAutoFormat* pTAFormat,
+ const std::vector<sal_uInt16> *pColArr,
+ bool bCalledFromShell,
+ bool bNewModel )
+{
+ assert(nRows && "Table without line?");
+ assert(nCols && "Table without rows?");
+
+ {
+ // Do not copy into Footnotes!
+ if( rPos.nNode < GetNodes().GetEndOfInserts().GetIndex() &&
+ rPos.nNode >= GetNodes().GetEndOfInserts().StartOfSectionIndex() )
+ return nullptr;
+
+ // If the ColumnArray has a wrong count, ignore it!
+ if( pColArr &&
+ static_cast<size_t>(nCols + ( text::HoriOrientation::NONE == eAdjust ? 2 : 1 )) != pColArr->size() )
+ pColArr = nullptr;
+ }
+
+ OUString aTableName = GetUniqueTableName();
+
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoInsTable>( rPos, nCols, nRows, o3tl::narrowing<sal_uInt16>(eAdjust),
+ rInsTableOpts, pTAFormat, pColArr,
+ aTableName));
+ }
+
+ // Start with inserting the Nodes and get the AutoFormat for the Table
+ SwTextFormatColl *pBodyColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE ),
+ *pHeadColl = pBodyColl;
+
+ bool bDfltBorders( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder );
+
+ if( (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) && (1 != nRows || !bDfltBorders) )
+ pHeadColl = getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TABLE_HDLN );
+
+ const sal_uInt16 nRowsToRepeat =
+ SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
+ rInsTableOpts.mnRowsToRepeat :
+ 0;
+
+ /* Save content node to extract FRAMEDIR from. */
+ const SwContentNode * pContentNd = rPos.nNode.GetNode().GetContentNode();
+
+ /* If we are called from a shell pass the attrset from
+ pContentNd (aka the node the table is inserted at) thus causing
+ SwNodes::InsertTable to propagate an adjust item if
+ necessary. */
+ SwTableNode *pTableNd = SwNodes::InsertTable(
+ rPos.nNode,
+ nCols,
+ pBodyColl,
+ nRows,
+ nRowsToRepeat,
+ pHeadColl,
+ bCalledFromShell ? &pContentNd->GetSwAttrSet() : nullptr );
+
+ // Create the Box/Line/Table construct
+ SwTableLineFormat* pLineFormat = MakeTableLineFormat();
+ SwTableFormat* pTableFormat = MakeTableFrameFormat( aTableName, GetDfltFrameFormat() );
+
+ /* If the node to insert the table at is a context node and has a
+ non-default FRAMEDIR propagate it to the table. */
+ if (pContentNd)
+ {
+ const SwAttrSet & aNdSet = pContentNd->GetSwAttrSet();
+ if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+ }
+
+ // Set Orientation at the Table's Format
+ pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
+ // All lines use the left-to-right Fill-Order!
+ pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
+
+ // Set USHRT_MAX as the Table's default SSize
+ SwTwips nWidth = USHRT_MAX;
+ if( pColArr )
+ {
+ sal_uInt16 nSttPos = pColArr->front();
+ sal_uInt16 nLastPos = pColArr->back();
+ if( text::HoriOrientation::NONE == eAdjust )
+ {
+ sal_uInt16 nFrameWidth = nLastPos;
+ nLastPos = (*pColArr)[ pColArr->size()-2 ];
+ pTableFormat->SetFormatAttr( SvxLRSpaceItem( nSttPos, nFrameWidth - nLastPos, 0, 0, RES_LR_SPACE ) );
+ }
+ nWidth = nLastPos - nSttPos;
+ }
+ else
+ {
+ nWidth /= nCols;
+ nWidth *= nCols; // to avoid rounding problems
+ }
+ pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
+ if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
+ pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
+
+ // Move the hard PageDesc/PageBreak Attributes if needed
+ SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]
+ ->GetContentNode();
+ if( pNextNd && pNextNd->HasSwAttrSet() )
+ {
+ const SfxItemSet* pNdSet = pNextNd->GetpSwAttrSet();
+ if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ pNextNd->ResetAttr( RES_PAGEDESC );
+ pNdSet = pNextNd->GetpSwAttrSet();
+ }
+ const SvxFormatBreakItem* pItem;
+ if( pNdSet && (pItem = pNdSet->GetItemIfSet( RES_BREAK, false )) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ pNextNd->ResetAttr( RES_BREAK );
+ }
+ }
+
+ SwTable& rNdTable = pTableNd->GetTable();
+ rNdTable.RegisterToFormat( *pTableFormat );
+
+ rNdTable.SetRowsToRepeat( nRowsToRepeat );
+ rNdTable.SetTableModel( bNewModel );
+
+ std::vector<SwTableBoxFormat*> aBoxFormatArr;
+ SwTableBoxFormat* pBoxFormat = nullptr;
+ if( !bDfltBorders && !pTAFormat )
+ {
+ pBoxFormat = MakeTableBoxFormat();
+ pBoxFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX / nCols, 0 ));
+ }
+ else
+ {
+ const sal_uInt16 nBoxArrLen = pTAFormat ? 16 : 4;
+ aBoxFormatArr.resize( nBoxArrLen, nullptr );
+ }
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
+
+ SwNodeIndex aNdIdx( *pTableNd, 1 ); // Set to StartNode of first Box
+ SwTableLines& rLines = rNdTable.GetTabLines();
+ for( sal_uInt16 n = 0; n < nRows; ++n )
+ {
+ SwTableLine* pLine = new SwTableLine( pLineFormat, nCols, nullptr );
+ rLines.insert( rLines.begin() + n, pLine );
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for( sal_uInt16 i = 0; i < nCols; ++i )
+ {
+ SwTableBoxFormat *pBoxF;
+ if( pTAFormat )
+ {
+ sal_uInt8 nId = SwTableAutoFormat::CountPos(i, nCols, n, nRows);
+ pBoxF = ::lcl_CreateAFormatBoxFormat( *this, aBoxFormatArr, *pTAFormat,
+ nRows, nCols, nId );
+
+ // Set the Paragraph/Character Attributes if needed
+ if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
+ {
+ aCharSet.ClearItem();
+ pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ if( aCharSet.Count() )
+ GetNodes()[ aNdIdx.GetIndex()+1 ]->GetContentNode()->
+ SetAttr( aCharSet );
+ }
+ }
+ else if( bDfltBorders )
+ {
+ sal_uInt8 nBoxId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
+ pBoxF = ::lcl_CreateDfltBoxFormat( *this, aBoxFormatArr, nCols, nBoxId);
+ }
+ else
+ pBoxF = pBoxFormat;
+
+ // For AutoFormat on input: the columns are set when inserting the Table
+ // The Array contains the columns positions and not their widths!
+ if( pColArr )
+ {
+ nWidth = (*pColArr)[ i + 1 ] - (*pColArr)[ i ];
+ if( pBoxF->GetFrameSize().GetWidth() != nWidth )
+ {
+ if( pBoxF->HasWriterListeners() ) // Create new Format
+ {
+ SwTableBoxFormat *pNewFormat = MakeTableBoxFormat();
+ *pNewFormat = *pBoxF;
+ pBoxF = pNewFormat;
+ }
+ pBoxF->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth ));
+ }
+ }
+
+ SwTableBox *pBox = new SwTableBox( pBoxF, aNdIdx, pLine);
+ rBoxes.insert( rBoxes.begin() + i, pBox );
+ aNdIdx += SwNodeOffset(3); // StartNode, TextNode, EndNode == 3 Nodes
+ }
+ }
+ // Insert Frames
+ GetNodes().GoNext( &aNdIdx ); // Go to the next ContentNode
+ pTableNd->MakeOwnFrames( &aNdIdx );
+
+ // To-Do - add 'SwExtraRedlineTable' also ?
+ if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
+ {
+ SwPaM aPam( *pTableNd->EndOfSectionNode(), *pTableNd, SwNodeOffset(1) );
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true);
+ else
+ getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ getIDocumentState().SetModified();
+ CHECK_TABLE(rNdTable);
+ return &rNdTable;
+}
+
+SwTableNode* SwNodes::InsertTable( const SwNodeIndex& rNdIdx,
+ sal_uInt16 nBoxes,
+ SwTextFormatColl* pContentTextColl,
+ sal_uInt16 nLines,
+ sal_uInt16 nRepeat,
+ SwTextFormatColl* pHeadlineTextColl,
+ const SwAttrSet * pAttrSet)
+{
+ if( !nBoxes )
+ return nullptr;
+
+ // If Lines is given, create the Matrix from Lines and Boxes
+ if( !pHeadlineTextColl || !nLines )
+ pHeadlineTextColl = pContentTextColl;
+
+ SwTableNode * pTableNd = new SwTableNode( rNdIdx );
+ SwEndNode* pEndNd = new SwEndNode( rNdIdx, *pTableNd );
+
+ if( !nLines ) // For the for loop
+ ++nLines;
+
+ SwNodeIndex aIdx( *pEndNd );
+ SwTextFormatColl* pTextColl = pHeadlineTextColl;
+ for( sal_uInt16 nL = 0; nL < nLines; ++nL )
+ {
+ for( sal_uInt16 nB = 0; nB < nBoxes; ++nB )
+ {
+ SwStartNode* pSttNd = new SwStartNode( aIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+
+ SwTextNode * pTmpNd = new SwTextNode( aIdx, pTextColl );
+
+ // #i60422# Propagate some more attributes.
+ const SfxPoolItem* pItem = nullptr;
+ if ( nullptr != pAttrSet )
+ {
+ static const sal_uInt16 aPropagateItems[] = {
+ RES_PARATR_ADJUST,
+ RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONTSIZE,
+ RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONTSIZE, 0 };
+
+ const sal_uInt16* pIdx = aPropagateItems;
+ while ( *pIdx != 0 )
+ {
+ if ( SfxItemState::SET != pTmpNd->GetSwAttrSet().GetItemState( *pIdx ) &&
+ SfxItemState::SET == pAttrSet->GetItemState( *pIdx, true, &pItem ) )
+ static_cast<SwContentNode *>(pTmpNd)->SetAttr(*pItem);
+ ++pIdx;
+ }
+ }
+
+ new SwEndNode( aIdx, *pSttNd );
+ }
+ if ( nL + 1 >= nRepeat )
+ pTextColl = pContentTextColl;
+ }
+ return pTableNd;
+}
+
+/**
+ * Text to Table
+ */
+const SwTable* SwDoc::TextToTable( const SwInsertTableOptions& rInsTableOpts,
+ const SwPaM& rRange, sal_Unicode cCh,
+ sal_Int16 eAdjust,
+ const SwTableAutoFormat* pTAFormat )
+{
+ // See if the selection contains a Table
+ const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
+ {
+ SwNodeOffset nCnt = pStt->nNode.GetIndex();
+ for( ; nCnt <= pEnd->nNode.GetIndex(); ++nCnt )
+ if( !GetNodes()[ nCnt ]->IsTextNode() )
+ return nullptr;
+ }
+
+ // Save first node in the selection if it is a context node
+ SwContentNode * pSttContentNd = pStt->nNode.GetNode().GetContentNode();
+
+ SwPaM aOriginal( *pStt, *pEnd );
+ pStt = aOriginal.GetMark();
+ pEnd = aOriginal.GetPoint();
+
+ SwUndoTextToTable* pUndo = nullptr;
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TEXTTOTABLE, nullptr );
+ pUndo = new SwUndoTextToTable( aOriginal, rInsTableOpts, cCh,
+ o3tl::narrowing<sal_uInt16>(eAdjust), pTAFormat );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+
+ // Do not add splitting the TextNode to the Undo history
+ GetIDocumentUndoRedo().DoUndo( false );
+ }
+
+ ::PaMCorrAbs( aOriginal, *pEnd );
+
+ // Make sure that the range is on Node Edges
+ SwNodeRange aRg( pStt->nNode, pEnd->nNode );
+ if( pStt->nContent.GetIndex() )
+ getIDocumentContentOperations().SplitNode( *pStt, false );
+
+ bool bEndContent = 0 != pEnd->nContent.GetIndex();
+
+ // Do not split at the End of a Line (except at the End of the Doc)
+ if( bEndContent )
+ {
+ if( pEnd->nNode.GetNode().GetContentNode()->Len() != pEnd->nContent.GetIndex()
+ || pEnd->nNode.GetIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
+ {
+ getIDocumentContentOperations().SplitNode( *pEnd, false );
+ --const_cast<SwNodeIndex&>(pEnd->nNode);
+ const_cast<SwIndex&>(pEnd->nContent).Assign(
+ pEnd->nNode.GetNode().GetContentNode(), 0 );
+ // A Node and at the End?
+ if( pStt->nNode.GetIndex() >= pEnd->nNode.GetIndex() )
+ --aRg.aStart;
+ }
+ else
+ ++aRg.aEnd;
+ }
+
+ if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
+ {
+ OSL_FAIL( "empty range" );
+ ++aRg.aEnd;
+ }
+
+ // We always use Upper to insert the Table
+ SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
+
+ GetIDocumentUndoRedo().DoUndo( nullptr != pUndo );
+
+ // Create the Box/Line/Table construct
+ SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
+ SwTableLineFormat* pLineFormat = MakeTableLineFormat();
+ SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
+
+ // All Lines have a left-to-right Fill Order
+ pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
+ // The Table's SSize is USHRT_MAX
+ pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
+ if( !(rInsTableOpts.mnInsMode & SwInsertTableFlags::SplitLayout) )
+ pTableFormat->SetFormatAttr( SwFormatLayoutSplit( false ));
+
+ /* If the first node in the selection is a context node and if it
+ has an item FRAMEDIR set (no default) propagate the item to the
+ replacing table. */
+ if (pSttContentNd)
+ {
+ const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
+ if (const SvxFrameDirectionItem *pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ) )
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+ }
+
+ //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
+ //until after RegisterToFormat is completed
+ bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
+ getIDocumentState().SetEnableSetModified(false);
+
+ SwTableNode* pTableNd = GetNodes().TextToTable(
+ aRg, cCh, pTableFormat, pLineFormat, pBoxFormat,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ), pUndo );
+
+ SwTable& rNdTable = pTableNd->GetTable();
+
+ const sal_uInt16 nRowsToRepeat =
+ SwInsertTableFlags::Headline == (rInsTableOpts.mnInsMode & SwInsertTableFlags::Headline) ?
+ rInsTableOpts.mnRowsToRepeat :
+ 0;
+ rNdTable.SetRowsToRepeat(nRowsToRepeat);
+
+ bool bUseBoxFormat = false;
+ if( !pBoxFormat->HasWriterListeners() )
+ {
+ // The Box's Formats already have the right size, we must only set
+ // the right Border/AutoFormat.
+ bUseBoxFormat = true;
+ pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
+ delete pBoxFormat;
+ eAdjust = text::HoriOrientation::NONE;
+ }
+
+ // Set Orientation in the Table's Format
+ pTableFormat->SetFormatAttr( SwFormatHoriOrient( 0, eAdjust ) );
+ rNdTable.RegisterToFormat(*pTableFormat);
+
+ if( pTAFormat || ( rInsTableOpts.mnInsMode & SwInsertTableFlags::DefaultBorder) )
+ {
+ sal_uInt8 nBoxArrLen = pTAFormat ? 16 : 4;
+ std::unique_ptr< DfltBoxAttrList_t > aBoxFormatArr1;
+ std::optional< std::vector<SwTableBoxFormat*> > aBoxFormatArr2;
+ if( bUseBoxFormat )
+ {
+ aBoxFormatArr1.reset(new DfltBoxAttrList_t( nBoxArrLen, nullptr ));
+ }
+ else
+ {
+ aBoxFormatArr2 = std::vector<SwTableBoxFormat*>( nBoxArrLen, nullptr );
+ }
+
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet( GetAttrPool() );
+
+ SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr;
+
+ SwTableBoxFormat *pBoxF = nullptr;
+ SwTableLines& rLines = rNdTable.GetTabLines();
+ const SwTableLines::size_type nRows = rLines.size();
+ for( SwTableLines::size_type n = 0; n < nRows; ++n )
+ {
+ SwTableBoxes& rBoxes = rLines[ n ]->GetTabBoxes();
+ const SwTableBoxes::size_type nCols = rBoxes.size();
+ for( SwTableBoxes::size_type i = 0; i < nCols; ++i )
+ {
+ SwTableBox* pBox = rBoxes[ i ];
+ bool bChgSz = false;
+
+ if( pTAFormat )
+ {
+ sal_uInt8 nId = static_cast<sal_uInt8>(!n ? 0 : (( n+1 == nRows )
+ ? 12 : (4 * (1 + ((n-1) & 1 )))));
+ nId = nId + static_cast<sal_uInt8>(!i ? 0 :
+ ( i+1 == nCols ? 3 : (1 + ((i-1) & 1))));
+ if( bUseBoxFormat )
+ ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId, pTAFormat );
+ else
+ {
+ bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
+ pBoxF = ::lcl_CreateAFormatBoxFormat( *this, *aBoxFormatArr2,
+ *pTAFormat, USHRT_MAX, USHRT_MAX, nId );
+ }
+
+ // Set Paragraph/Character Attributes if needed
+ if( pTAFormat->IsFont() || pTAFormat->IsJustify() )
+ {
+ aCharSet.ClearItem();
+ pTAFormat->UpdateToSet( nId, nRows==1, nCols==1, aCharSet,
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ if( aCharSet.Count() )
+ {
+ SwNodeOffset nSttNd = pBox->GetSttIdx()+1;
+ SwNodeOffset nEndNd = pBox->GetSttNd()->EndOfSectionIndex();
+ for( ; nSttNd < nEndNd; ++nSttNd )
+ {
+ SwContentNode* pNd = GetNodes()[ nSttNd ]->GetContentNode();
+ if( pNd )
+ {
+ if( pHistory )
+ {
+ SwRegHistory aReg( pNd, *pNd, pHistory );
+ pNd->SetAttr( aCharSet );
+ }
+ else
+ pNd->SetAttr( aCharSet );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ sal_uInt8 nId = (i < nCols - 1 ? 0 : 1) + (n ? 2 : 0 );
+ if( bUseBoxFormat )
+ ::lcl_SetDfltBoxAttr( *pBox, *aBoxFormatArr1, nId );
+ else
+ {
+ bChgSz = nullptr == (*aBoxFormatArr2)[ nId ];
+ pBoxF = ::lcl_CreateDfltBoxFormat( *this, *aBoxFormatArr2,
+ USHRT_MAX, nId );
+ }
+ }
+
+ if( !bUseBoxFormat )
+ {
+ if( bChgSz )
+ pBoxF->SetFormatAttr( pBox->GetFrameFormat()->GetFrameSize() );
+ pBox->ChgFrameFormat( pBoxF );
+ }
+ }
+ }
+
+ if( bUseBoxFormat )
+ {
+ for( sal_uInt8 i = 0; i < nBoxArrLen; ++i )
+ {
+ delete (*aBoxFormatArr1)[ i ];
+ }
+ }
+ }
+
+ // Check the boxes for numbers
+ if( IsInsTableFormatNum() )
+ {
+ for (size_t nBoxes = rNdTable.GetTabSortBoxes().size(); nBoxes; )
+ {
+ ChkBoxNumFormat(*rNdTable.GetTabSortBoxes()[ --nBoxes ], false);
+ }
+ }
+
+ SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ {
+ SwPaM& rTmp = const_cast<SwPaM&>(rRange); // Point always at the Start
+ rTmp.DeleteMark();
+ rTmp.GetPoint()->nNode = *pTableNd;
+ SwContentNode* pCNd = GetNodes().GoNext( &rTmp.GetPoint()->nNode );
+ rTmp.GetPoint()->nContent.Assign( pCNd, 0 );
+ }
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::TEXTTOTABLE, nullptr );
+ }
+
+ getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
+ return &rNdTable;
+}
+
+static void lcl_RemoveBreaks(SwContentNode & rNode, SwTableFormat *const pTableFormat)
+{
+ // delete old layout frames, new ones need to be created...
+ rNode.DelFrames(nullptr);
+
+ if (!rNode.IsTextNode())
+ {
+ return;
+ }
+
+ SwTextNode & rTextNode = *rNode.GetTextNode();
+ // remove PageBreaks/PageDesc/ColBreak
+ SfxItemSet const* pSet = rTextNode.GetpSwAttrSet();
+ if (!pSet)
+ return;
+
+ if (const SvxFormatBreakItem* pItem = pSet->GetItemIfSet(RES_BREAK, false))
+ {
+ if (pTableFormat)
+ {
+ pTableFormat->SetFormatAttr(*pItem);
+ }
+ rTextNode.ResetAttr(RES_BREAK);
+ pSet = rTextNode.GetpSwAttrSet();
+ }
+
+ const SwFormatPageDesc* pPageDescItem;
+ if (pSet
+ && (pPageDescItem = pSet->GetItemIfSet(RES_PAGEDESC, false))
+ && pPageDescItem->GetPageDesc())
+ {
+ if (pTableFormat)
+ {
+ pTableFormat->SetFormatAttr(*pPageDescItem);
+ }
+ rTextNode.ResetAttr(RES_PAGEDESC);
+ }
+}
+
+/**
+ * balance lines in table, insert empty boxes so all lines have the size
+ */
+static void
+lcl_BalanceTable(SwTable & rTable, size_t const nMaxBoxes,
+ SwTableNode & rTableNd, SwTableBoxFormat & rBoxFormat, SwTextFormatColl & rTextColl,
+ SwUndoTextToTable *const pUndo, std::vector<sal_uInt16> *const pPositions)
+{
+ for (size_t n = 0; n < rTable.GetTabLines().size(); ++n)
+ {
+ SwTableLine *const pCurrLine = rTable.GetTabLines()[ n ];
+ size_t const nBoxes = pCurrLine->GetTabBoxes().size();
+ if (nMaxBoxes != nBoxes)
+ {
+ rTableNd.GetNodes().InsBoxen(&rTableNd, pCurrLine, &rBoxFormat, &rTextColl,
+ nullptr, nBoxes, nMaxBoxes - nBoxes);
+
+ if (pUndo)
+ {
+ for (size_t i = nBoxes; i < nMaxBoxes; ++i)
+ {
+ pUndo->AddFillBox( *pCurrLine->GetTabBoxes()[i] );
+ }
+ }
+
+ // if the first line is missing boxes, the width array is useless!
+ if (!n && pPositions)
+ {
+ pPositions->clear();
+ }
+ }
+ }
+}
+
+static void
+lcl_SetTableBoxWidths(SwTable & rTable, size_t const nMaxBoxes,
+ SwTableBoxFormat & rBoxFormat, SwDoc & rDoc,
+ std::vector<sal_uInt16> *const pPositions)
+{
+ if (pPositions && !pPositions->empty())
+ {
+ SwTableLines& rLns = rTable.GetTabLines();
+ sal_uInt16 nLastPos = 0;
+ for (size_t n = 0; n < pPositions->size(); ++n)
+ {
+ SwTableBoxFormat *pNewFormat = rDoc.MakeTableBoxFormat();
+ pNewFormat->SetFormatAttr(
+ SwFormatFrameSize(SwFrameSize::Variable, (*pPositions)[n] - nLastPos));
+ for (size_t nTmpLine = 0; nTmpLine < rLns.size(); ++nTmpLine)
+ {
+ // Have to do an Add here, because the BoxFormat
+ // is still needed by the caller
+ pNewFormat->Add( rLns[ nTmpLine ]->GetTabBoxes()[ n ] );
+ }
+
+ nLastPos = (*pPositions)[ n ];
+ }
+
+ // propagate size upwards from format, so the table gets the right size
+ SAL_WARN_IF(rBoxFormat.HasWriterListeners(), "sw.core",
+ "who is still registered in the format?");
+ rBoxFormat.SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nLastPos ));
+ }
+ else
+ {
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
+ }
+}
+
+SwTableNode* SwNodes::TextToTable( const SwNodeRange& rRange, sal_Unicode cCh,
+ SwTableFormat* pTableFormat,
+ SwTableLineFormat* pLineFormat,
+ SwTableBoxFormat* pBoxFormat,
+ SwTextFormatColl* pTextColl,
+ SwUndoTextToTable* pUndo )
+{
+ if( rRange.aStart >= rRange.aEnd )
+ return nullptr;
+
+ SwTableNode * pTableNd = new SwTableNode( rRange.aStart );
+ new SwEndNode( rRange.aEnd, *pTableNd );
+
+ SwDoc& rDoc = GetDoc();
+ std::vector<sal_uInt16> aPosArr;
+ SwTable& rTable = pTableNd->GetTable();
+ SwTableBox* pBox;
+ sal_uInt16 nBoxes, nLines, nMaxBoxes = 0;
+
+ SwNodeIndex aSttIdx( *pTableNd, 1 );
+ SwNodeIndex aEndIdx( rRange.aEnd, -1 );
+ for( nLines = 0, nBoxes = 0;
+ aSttIdx.GetIndex() < aEndIdx.GetIndex();
+ aSttIdx += SwNodeOffset(2), nLines++, nBoxes = 0 )
+ {
+ SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "Only add TextNodes to the Table" );
+
+ if( !nLines && 0x0b == cCh )
+ {
+ cCh = 0x09;
+
+ // Get the separator's position from the first Node, in order for the Boxes to be set accordingly
+ SwTextFrameInfo aFInfo( static_cast<SwTextFrame*>(pTextNd->getLayoutFrame( pTextNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )) );
+ if( aFInfo.IsOneLine() ) // only makes sense in this case
+ {
+ OUString const& rText(pTextNd->GetText());
+ for (sal_Int32 nChPos = 0; nChPos < rText.getLength(); ++nChPos)
+ {
+ if (rText[nChPos] == cCh)
+ {
+ // sw_redlinehide: no idea if this makes any sense...
+ TextFrameIndex const nPos(aFInfo.GetFrame()->MapModelToView(pTextNd, nChPos));
+ aPosArr.push_back( o3tl::narrowing<sal_uInt16>(
+ aFInfo.GetCharPos(nPos+TextFrameIndex(1), false)) );
+ }
+ }
+
+ aPosArr.push_back(
+ o3tl::narrowing<sal_uInt16>(aFInfo.GetFrame()->IsVertical() ?
+ aFInfo.GetFrame()->getFramePrintArea().Bottom() :
+ aFInfo.GetFrame()->getFramePrintArea().Right()) );
+
+ }
+ }
+
+ lcl_RemoveBreaks(*pTextNd, (0 == nLines) ? pTableFormat : nullptr);
+
+ // Set the TableNode as StartNode for all TextNodes in the Table
+ pTextNd->m_pStartOfSection = pTableNd;
+
+ SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
+ rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
+
+ SwStartNode* pSttNd;
+ SwPosition aCntPos( aSttIdx, SwIndex( pTextNd ));
+
+ const std::shared_ptr< sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(rDoc, aSttIdx.GetIndex(), SAL_MAX_INT32);
+
+ if( T2T_PARA != cCh )
+ {
+ for (sal_Int32 nChPos = 0; nChPos < pTextNd->GetText().getLength();)
+ {
+ if (pTextNd->GetText()[nChPos] == cCh)
+ {
+ aCntPos.nContent = nChPos;
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(*pNewNode, nChPos, nChPos + 1, eMode);
+ }
+ });
+ SwContentNode *const pNewNd =
+ pTextNd->SplitContentNode(aCntPos, &restoreFunc);
+
+ // Delete separator and correct search string
+ pTextNd->EraseText( aCntPos.nContent, 1 );
+ nChPos = 0;
+
+ // Set the TableNode as StartNode for all TextNodes in the Table
+ const SwNodeIndex aTmpIdx( aCntPos.nNode, -1 );
+ pSttNd = new SwStartNode( aTmpIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ new SwEndNode( aCntPos.nNode, *pSttNd );
+ pNewNd->m_pStartOfSection = pSttNd;
+
+ // Assign Section to the Box
+ pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
+ }
+ else
+ {
+ ++nChPos;
+ }
+ }
+ }
+
+ // Now for the last substring
+ if( !pContentStore->Empty())
+ pContentStore->Restore( *pTextNd, pTextNd->GetText().getLength(), pTextNd->GetText().getLength()+1 );
+
+ pSttNd = new SwStartNode( aCntPos.nNode, SwNodeType::Start, SwTableBoxStartNode );
+ const SwNodeIndex aTmpIdx( aCntPos.nNode, 1 );
+ new SwEndNode( aTmpIdx, *pSttNd );
+ pTextNd->m_pStartOfSection = pSttNd;
+
+ pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
+ if( nMaxBoxes < nBoxes )
+ nMaxBoxes = nBoxes;
+ }
+
+ lcl_BalanceTable(rTable, nMaxBoxes, *pTableNd, *pBoxFormat, *pTextColl,
+ pUndo, &aPosArr);
+ lcl_SetTableBoxWidths(rTable, nMaxBoxes, *pBoxFormat, rDoc, &aPosArr);
+
+ return pTableNd;
+}
+
+const SwTable* SwDoc::TextToTable( const std::vector< std::vector<SwNodeRange> >& rTableNodes )
+{
+ if (rTableNodes.empty())
+ return nullptr;
+
+ const std::vector<SwNodeRange>& rFirstRange = *rTableNodes.begin();
+
+ if (rFirstRange.empty())
+ return nullptr;
+
+ const std::vector<SwNodeRange>& rLastRange = *rTableNodes.rbegin();
+
+ if (rLastRange.empty())
+ return nullptr;
+
+ /* Save first node in the selection if it is a content node. */
+ SwContentNode * pSttContentNd = rFirstRange.begin()->aStart.GetNode().GetContentNode();
+
+ const SwNodeRange& rStartRange = *rFirstRange.begin();
+ const SwNodeRange& rEndRange = *rLastRange.rbegin();
+
+ //!!! not necessarily TextNodes !!!
+ SwPaM aOriginal( rStartRange.aStart, rEndRange.aEnd );
+ const SwPosition *pStt = aOriginal.GetMark();
+ const SwPosition *pEnd = aOriginal.GetPoint();
+
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ if (bUndo)
+ {
+ // Do not add splitting the TextNode to the Undo history
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ ::PaMCorrAbs( aOriginal, *pEnd );
+
+ // make sure that the range is on Node Edges
+ SwNodeRange aRg( pStt->nNode, pEnd->nNode );
+ if( pStt->nContent.GetIndex() )
+ getIDocumentContentOperations().SplitNode( *pStt, false );
+
+ bool bEndContent = 0 != pEnd->nContent.GetIndex();
+
+ // Do not split at the End of a Line (except at the End of the Doc)
+ if( bEndContent )
+ {
+ if( pEnd->nNode.GetNode().GetContentNode()->Len() != pEnd->nContent.GetIndex()
+ || pEnd->nNode.GetIndex() >= GetNodes().GetEndOfContent().GetIndex()-1 )
+ {
+ getIDocumentContentOperations().SplitNode( *pEnd, false );
+ --const_cast<SwNodeIndex&>(pEnd->nNode);
+ const_cast<SwIndex&>(pEnd->nContent).Assign(
+ pEnd->nNode.GetNode().GetContentNode(), 0 );
+ // A Node and at the End?
+ if( pStt->nNode.GetIndex() >= pEnd->nNode.GetIndex() )
+ --aRg.aStart;
+ }
+ else
+ ++aRg.aEnd;
+ }
+
+ assert(aRg.aEnd == pEnd->nNode);
+ assert(aRg.aStart == pStt->nNode);
+ if( aRg.aEnd.GetIndex() == aRg.aStart.GetIndex() )
+ {
+ OSL_FAIL( "empty range" );
+ ++aRg.aEnd;
+ }
+
+
+ {
+ // TODO: this is not Undo-able - only good enough for file import
+ IDocumentRedlineAccess & rIDRA(getIDocumentRedlineAccess());
+ SwNodeIndex const prev(rTableNodes.begin()->begin()->aStart, -1);
+ SwNodeIndex const* pPrev(&prev);
+ // pPrev could point to non-textnode now
+ for (const auto& rRow : rTableNodes)
+ {
+ for (const auto& rCell : rRow)
+ {
+ assert(SwNodeIndex(*pPrev, +1) == rCell.aStart);
+ SwPaM pam(rCell.aStart, 0, *pPrev,
+ (pPrev->GetNode().IsContentNode())
+ ? pPrev->GetNode().GetContentNode()->Len() : 0);
+ rIDRA.SplitRedline(pam);
+ pPrev = &rCell.aEnd;
+ }
+ }
+ // another one to break between last cell and node after table
+ SwPaM pam(SwNodeIndex(*pPrev, +1), 0, *pPrev,
+ (pPrev->GetNode().IsContentNode())
+ ? pPrev->GetNode().GetContentNode()->Len() : 0);
+ rIDRA.SplitRedline(pam);
+ }
+
+ // We always use Upper to insert the Table
+ SwNode2LayoutSaveUpperFrames aNode2Layout( aRg.aStart.GetNode() );
+
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+
+ // Create the Box/Line/Table construct
+ SwTableBoxFormat* pBoxFormat = MakeTableBoxFormat();
+ SwTableLineFormat* pLineFormat = MakeTableLineFormat();
+ SwTableFormat* pTableFormat = MakeTableFrameFormat( GetUniqueTableName(), GetDfltFrameFormat() );
+
+ // All Lines have a left-to-right Fill Order
+ pLineFormat->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ));
+ // The Table's SSize is USHRT_MAX
+ pTableFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, USHRT_MAX ));
+
+ /* If the first node in the selection is a context node and if it
+ has an item FRAMEDIR set (no default) propagate the item to the
+ replacing table. */
+ if (pSttContentNd)
+ {
+ const SwAttrSet & aNdSet = pSttContentNd->GetSwAttrSet();
+ if (const SvxFrameDirectionItem* pItem = aNdSet.GetItemIfSet( RES_FRAMEDIR ))
+ {
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+ }
+
+ //Resolves: tdf#87977, tdf#78599, disable broadcasting modifications
+ //until after RegisterToFormat is completed
+ bool bEnableSetModified = getIDocumentState().IsEnableSetModified();
+ getIDocumentState().SetEnableSetModified(false);
+
+ SwTableNode* pTableNd = GetNodes().TextToTable(
+ rTableNodes, pTableFormat, pLineFormat, pBoxFormat );
+
+ SwTable& rNdTable = pTableNd->GetTable();
+ rNdTable.RegisterToFormat(*pTableFormat);
+
+ if( !pBoxFormat->HasWriterListeners() )
+ {
+ // The Box's Formats already have the right size, we must only set
+ // the right Border/AutoFormat.
+ pTableFormat->SetFormatAttr( pBoxFormat->GetFrameSize() );
+ delete pBoxFormat;
+ }
+
+ SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
+
+ getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ return &rNdTable;
+}
+
+std::unique_ptr<SwNodeRange> SwNodes::ExpandRangeForTableBox(const SwNodeRange & rRange)
+{
+ bool bChanged = false;
+
+ SwNodeIndex aNewStart = rRange.aStart;
+ SwNodeIndex aNewEnd = rRange.aEnd;
+
+ SwNodeIndex aEndIndex = rRange.aEnd;
+ SwNodeIndex aIndex = rRange.aStart;
+
+ while (aIndex < aEndIndex)
+ {
+ SwNode& rNode = aIndex.GetNode();
+
+ if (rNode.IsStartNode())
+ {
+ // advance aIndex to the end node of this start node
+ SwNode * pEndNode = rNode.EndOfSectionNode();
+ aIndex = *pEndNode;
+
+ if (aIndex > aNewEnd)
+ {
+ aNewEnd = aIndex;
+ bChanged = true;
+ }
+ }
+ else if (rNode.IsEndNode())
+ {
+ SwNode * pStartNode = rNode.StartOfSectionNode();
+ SwNodeIndex aStartIndex = *pStartNode;
+
+ if (aStartIndex < aNewStart)
+ {
+ aNewStart = aStartIndex;
+ bChanged = true;
+ }
+ }
+
+ if (aIndex < aEndIndex)
+ ++aIndex;
+ }
+
+ SwNode * pNode = &aIndex.GetNode();
+ while (pNode->IsEndNode() && aIndex < Count() - 1)
+ {
+ SwNode * pStartNode = pNode->StartOfSectionNode();
+ SwNodeIndex aStartIndex(*pStartNode);
+ aNewStart = aStartIndex;
+ aNewEnd = aIndex;
+ bChanged = true;
+
+ ++aIndex;
+ pNode = &aIndex.GetNode();
+ }
+
+ std::unique_ptr<SwNodeRange> pResult;
+ if (bChanged)
+ pResult.reset(new SwNodeRange(aNewStart, aNewEnd));
+ return pResult;
+}
+
+static void
+lcl_SetTableBoxWidths2(SwTable & rTable, size_t const nMaxBoxes,
+ SwTableBoxFormat & rBoxFormat, SwDoc & rDoc)
+{
+ // rhbz#820283, fdo#55462: set default box widths so table width is covered
+ SwTableLines & rLines = rTable.GetTabLines();
+ for (size_t nTmpLine = 0; nTmpLine < rLines.size(); ++nTmpLine)
+ {
+ SwTableBoxes & rBoxes = rLines[nTmpLine]->GetTabBoxes();
+ assert(!rBoxes.empty()); // ensured by convertToTable
+ size_t const nMissing = nMaxBoxes - rBoxes.size();
+ if (nMissing)
+ {
+ // default width for box at the end of an incomplete line
+ SwTableBoxFormat *const pNewFormat = rDoc.MakeTableBoxFormat();
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ pNewFormat->SetFormatAttr( SwFormatFrameSize(SwFrameSize::Variable,
+ nWidth * (nMissing + 1)) );
+ pNewFormat->Add(rBoxes.back());
+ }
+ }
+ size_t nWidth = nMaxBoxes ? USHRT_MAX / nMaxBoxes : USHRT_MAX;
+ // default width for all boxes not at the end of an incomplete line
+ rBoxFormat.SetFormatAttr(SwFormatFrameSize(SwFrameSize::Variable, nWidth));
+}
+
+SwTableNode* SwNodes::TextToTable( const SwNodes::TableRanges_t & rTableNodes,
+ SwTableFormat* pTableFormat,
+ SwTableLineFormat* pLineFormat,
+ SwTableBoxFormat* pBoxFormat )
+{
+ if( rTableNodes.empty() )
+ return nullptr;
+
+ SwTableNode * pTableNd = new SwTableNode( rTableNodes.begin()->begin()->aStart );
+ //insert the end node after the last text node
+ SwNodeIndex aInsertIndex( rTableNodes.rbegin()->rbegin()->aEnd );
+ ++aInsertIndex;
+
+ //!! ownership will be transferred in c-tor to SwNodes array.
+ //!! Thus no real problem here...
+ new SwEndNode( aInsertIndex, *pTableNd );
+
+ SwDoc& rDoc = GetDoc();
+ SwTable& rTable = pTableNd->GetTable();
+ SwTableBox* pBox;
+ sal_uInt16 nLines, nMaxBoxes = 0;
+
+ SwNodeIndex aNodeIndex = rTableNodes.begin()->begin()->aStart;
+ // delete frames of all contained content nodes
+ for( nLines = 0; aNodeIndex <= rTableNodes.rbegin()->rbegin()->aEnd; ++aNodeIndex,++nLines )
+ {
+ SwNode& rNode = aNodeIndex.GetNode();
+ if( rNode.IsContentNode() )
+ {
+ lcl_RemoveBreaks(static_cast<SwContentNode&>(rNode),
+ (0 == nLines) ? pTableFormat : nullptr);
+ }
+ }
+
+ nLines = 0;
+ for( const auto& rRow : rTableNodes )
+ {
+ sal_uInt16 nBoxes = 0;
+ SwTableLine* pLine = new SwTableLine( pLineFormat, 1, nullptr );
+ rTable.GetTabLines().insert(rTable.GetTabLines().begin() + nLines, pLine);
+
+ for( const auto& rCell : rRow )
+ {
+ const SwNodeIndex aTmpIdx( rCell.aStart,0 );
+
+ SwNodeIndex aCellEndIdx(rCell.aEnd);
+ ++aCellEndIdx;
+ SwStartNode* pSttNd = new SwStartNode( aTmpIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+
+ // Quotation of http://nabble.documentfoundation.org/Some-strange-lines-by-taking-a-look-at-the-bt-of-fdo-51916-tp3994561p3994639.html
+ // SwNode's constructor adds itself to the same SwNodes array as the other node (pSttNd).
+ // So this statement is only executed for the side-effect.
+ new SwEndNode( aCellEndIdx, *pSttNd );
+
+ //set the start node on all node of the current cell
+ SwNodeIndex aCellNodeIdx = rCell.aStart;
+ for(;aCellNodeIdx <= rCell.aEnd; ++aCellNodeIdx )
+ {
+ aCellNodeIdx.GetNode().m_pStartOfSection = pSttNd;
+ //skip start/end node pairs
+ if( aCellNodeIdx.GetNode().IsStartNode() )
+ aCellNodeIdx.Assign(*aCellNodeIdx.GetNode().EndOfSectionNode());
+ }
+
+ // assign Section to the Box
+ pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin() + nBoxes++, pBox );
+ }
+ if( nMaxBoxes < nBoxes )
+ nMaxBoxes = nBoxes;
+
+ nLines++;
+ }
+
+ lcl_SetTableBoxWidths2(rTable, nMaxBoxes, *pBoxFormat, rDoc);
+
+ return pTableNd;
+}
+
+/**
+ * Table to Text
+ */
+bool SwDoc::TableToText( const SwTableNode* pTableNd, sal_Unicode cCh )
+{
+ if( !pTableNd )
+ return false;
+
+ // #i34471#
+ // If this is triggered by SwUndoTableToText::Repeat() nobody ever deleted
+ // the table cursor.
+ SwEditShell* pESh = GetEditShell();
+ if (pESh && pESh->IsTableMode())
+ pESh->ClearMark();
+
+ SwNodeRange aRg( *pTableNd, SwNodeOffset(0), *pTableNd->EndOfSectionNode() );
+ std::unique_ptr<SwUndoTableToText> pUndo;
+ SwNodeRange* pUndoRg = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndoRg = new SwNodeRange( aRg.aStart, SwNodeOffset(-1), aRg.aEnd, SwNodeOffset(+1) );
+ pUndo.reset(new SwUndoTableToText( pTableNd->GetTable(), cCh ));
+ }
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bool bRet = GetNodes().TableToText( aRg, cCh, pUndo.get() );
+ if( pUndoRg )
+ {
+ ++pUndoRg->aStart;
+ --pUndoRg->aEnd;
+ pUndo->SetRange( *pUndoRg );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ delete pUndoRg;
+ }
+
+ if( bRet )
+ getIDocumentState().SetModified();
+
+ return bRet;
+}
+
+namespace {
+
+/**
+ * Use the ForEach method from PtrArray to recreate Text from a Table.
+ * The Boxes can also contain Lines!
+ */
+struct DelTabPara
+{
+ SwTextNode* pLastNd;
+ SwNodes& rNds;
+ SwUndoTableToText* pUndo;
+ sal_Unicode cCh;
+
+ DelTabPara( SwNodes& rNodes, sal_Unicode cChar, SwUndoTableToText* pU ) :
+ pLastNd(nullptr), rNds( rNodes ), pUndo( pU ), cCh( cChar ) {}
+};
+
+}
+
+// Forward declare so that the Lines and Boxes can use recursion
+static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara );
+
+static void lcl_DelLine( SwTableLine* pLine, DelTabPara* pPara )
+{
+ assert(pPara && "The parameters are missing!");
+ DelTabPara aPara( *pPara );
+ for( auto& rpBox : pLine->GetTabBoxes() )
+ lcl_DelBox(rpBox, &aPara );
+ if( pLine->GetUpper() ) // Is there a parent Box?
+ // Return the last TextNode
+ pPara->pLastNd = aPara.pLastNd;
+}
+
+static void lcl_DelBox( SwTableBox* pBox, DelTabPara* pDelPara )
+{
+ assert(pDelPara && "The parameters are missing");
+
+ // Delete the Box's Lines
+ if( !pBox->GetTabLines().empty() )
+ {
+ for( SwTableLine* pLine : pBox->GetTabLines() )
+ lcl_DelLine( pLine, pDelPara );
+ }
+ else
+ {
+ SwDoc& rDoc = pDelPara->rNds.GetDoc();
+ SwNodeRange aDelRg( *pBox->GetSttNd(), SwNodeOffset(0),
+ *pBox->GetSttNd()->EndOfSectionNode() );
+ // Delete the Section
+ pDelPara->rNds.SectionUp( &aDelRg );
+ const SwTextNode* pCurTextNd = nullptr;
+ if (T2T_PARA != pDelPara->cCh && pDelPara->pLastNd)
+ pCurTextNd = aDelRg.aStart.GetNode().GetTextNode();
+ if (nullptr != pCurTextNd)
+ {
+ // Join the current text node with the last from the previous box if possible
+ SwNodeOffset nNdIdx = aDelRg.aStart.GetIndex();
+ --aDelRg.aStart;
+ if( pDelPara->pLastNd == &aDelRg.aStart.GetNode() )
+ {
+ // Inserting the separator
+ SwIndex aCntIdx( pDelPara->pLastNd,
+ pDelPara->pLastNd->GetText().getLength());
+ pDelPara->pLastNd->InsertText( OUString(pDelPara->cCh), aCntIdx,
+ SwInsertFlags::EMPTYEXPAND );
+ if( pDelPara->pUndo )
+ pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex(),
+ aCntIdx.GetIndex() );
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ const sal_Int32 nOldTextLen = aCntIdx.GetIndex();
+ pContentStore->Save(rDoc, nNdIdx, SAL_MAX_INT32);
+
+ pDelPara->pLastNd->JoinNext();
+
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( rDoc, pDelPara->pLastNd->GetIndex(), nOldTextLen );
+ }
+ else if( pDelPara->pUndo )
+ {
+ ++aDelRg.aStart;
+ pDelPara->pUndo->AddBoxPos( rDoc, nNdIdx, aDelRg.aEnd.GetIndex() );
+ }
+ }
+ else if( pDelPara->pUndo )
+ pDelPara->pUndo->AddBoxPos( rDoc, aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
+ --aDelRg.aEnd;
+ pDelPara->pLastNd = aDelRg.aEnd.GetNode().GetTextNode();
+
+ // Do not take over the NumberFormatting's adjustment
+ if( pDelPara->pLastNd && pDelPara->pLastNd->HasSwAttrSet() )
+ pDelPara->pLastNd->ResetAttr( RES_PARATR_ADJUST );
+ }
+}
+
+bool SwNodes::TableToText( const SwNodeRange& rRange, sal_Unicode cCh,
+ SwUndoTableToText* pUndo )
+{
+ // Is a Table selected?
+ if (rRange.aStart.GetIndex() >= rRange.aEnd.GetIndex())
+ return false;
+ SwTableNode *const pTableNd(rRange.aStart.GetNode().GetTableNode());
+ if (nullptr == pTableNd ||
+ &rRange.aEnd.GetNode() != pTableNd->EndOfSectionNode() )
+ return false;
+
+ // If the Table was alone in a Section, create the Frames via the Table's Upper
+ SwNode2LayoutSaveUpperFrames * pNode2Layout = nullptr;
+ SwNodeIndex aFrameIdx( rRange.aStart );
+ SwNode* pFrameNd = FindPrvNxtFrameNode( aFrameIdx, &rRange.aEnd.GetNode() );
+ if( !pFrameNd )
+ // Collect all Uppers
+ pNode2Layout = new SwNode2LayoutSaveUpperFrames(*pTableNd);
+
+ // Delete the Frames
+ pTableNd->DelFrames();
+
+ // "Delete" the Table and merge all Lines/Boxes
+ DelTabPara aDelPara( *this, cCh, pUndo );
+ for( SwTableLine *pLine : pTableNd->m_pTable->GetTabLines() )
+ lcl_DelLine( pLine, &aDelPara );
+
+ // We just created a TextNode with fitting separator for every TableLine.
+ // Now we only need to delete the TableSection and create the Frames for the
+ // new TextNode.
+ SwNodeRange aDelRg( rRange.aStart, rRange.aEnd );
+
+ // If the Table has PageDesc/Break Attributes, carry them over to the
+ // first Text Node
+ {
+ // What about UNDO?
+ const SfxItemSet& rTableSet = pTableNd->m_pTable->GetFrameFormat()->GetAttrSet();
+ const SvxFormatBreakItem* pBreak = rTableSet.GetItemIfSet( RES_BREAK, false );
+ const SwFormatPageDesc* pDesc = rTableSet.GetItemIfSet( RES_PAGEDESC, false );
+
+ if( pBreak || pDesc )
+ {
+ SwNodeIndex aIdx( *pTableNd );
+ SwContentNode* pCNd = GoNext( &aIdx );
+ if( pBreak )
+ pCNd->SetAttr( *pBreak );
+ if( pDesc )
+ pCNd->SetAttr( *pDesc );
+ }
+ }
+
+ SectionUp( &aDelRg ); // Delete this Section and by that the Table
+ // #i28006#
+ SwNodeOffset nStt = aDelRg.aStart.GetIndex(), nEnd = aDelRg.aEnd.GetIndex();
+ if( !pFrameNd )
+ {
+ pNode2Layout->RestoreUpperFrames( *this,
+ aDelRg.aStart.GetIndex(), aDelRg.aEnd.GetIndex() );
+ delete pNode2Layout;
+ }
+ else
+ {
+ SwContentNode *pCNd;
+ SwSectionNode *pSNd;
+ while( aDelRg.aStart.GetIndex() < nEnd )
+ {
+ pCNd = aDelRg.aStart.GetNode().GetContentNode();
+ if( nullptr != pCNd )
+ {
+ if( pFrameNd->IsContentNode() )
+ static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
+ else if( pFrameNd->IsTableNode() )
+ static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
+ else if( pFrameNd->IsSectionNode() )
+ static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aDelRg.aStart);
+ pFrameNd = pCNd;
+ }
+ else
+ {
+ pSNd = aDelRg.aStart.GetNode().GetSectionNode();
+ if( pSNd )
+ {
+ if( !pSNd->GetSection().IsHidden() && !pSNd->IsContentHidden() )
+ {
+ pSNd->MakeOwnFrames(&aFrameIdx, &aDelRg.aEnd);
+ break;
+ }
+ aDelRg.aStart = *pSNd->EndOfSectionNode();
+ }
+ }
+ ++aDelRg.aStart;
+ }
+ }
+
+ // #i28006# Fly frames have to be restored even if the table was
+ // #alone in the section
+ const SwFrameFormats& rFlyArr = *GetDoc().GetSpzFrameFormats();
+ for( auto pFly : rFlyArr )
+ {
+ SwFrameFormat *const pFormat = pFly;
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnchor.GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
+ nStt <= pAPos->nNode.GetIndex() &&
+ pAPos->nNode.GetIndex() < nEnd )
+ {
+ pFormat->MakeFrames();
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Inserting Columns/Rows
+ */
+void SwDoc::InsertCol( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
+{
+ if( !::CheckSplitCells( rCursor, nCnt + 1, SwTableSearchType::Col ) )
+ return;
+
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ ::GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
+
+ if( !aBoxes.empty() )
+ InsertCol( aBoxes, nCnt, bBehind );
+}
+
+bool SwDoc::InsertCol( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
+{
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
+ return false;
+
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSCOL, rBoxes, *pTableNd,
+ 0, 0, nCnt, bBehind, false ));
+ aTmpLst.insert( rTable.GetTabSortBoxes() );
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bRet = rTable.InsertCol(*this, rBoxes, nCnt, bBehind);
+ if (bRet)
+ {
+ getIDocumentState().SetModified();
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ return bRet;
+}
+
+void SwDoc::InsertRow( const SwCursor& rCursor, sal_uInt16 nCnt, bool bBehind )
+{
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
+
+ if( !aBoxes.empty() )
+ InsertRow( aBoxes, nCnt, bBehind );
+}
+
+bool SwDoc::InsertRow( const SwSelBoxes& rBoxes, sal_uInt16 nCnt, bool bBehind )
+{
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
+ return false;
+
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW,rBoxes, *pTableNd,
+ 0, 0, nCnt, bBehind, false ));
+ aTmpLst.insert( rTable.GetTabSortBoxes() );
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bRet = rTable.InsertRow( this, rBoxes, nCnt, bBehind );
+ if (bRet)
+ {
+ getIDocumentState().SetModified();
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ return bRet;
+
+}
+
+/**
+ * Deleting Columns/Rows
+ */
+void SwDoc::DeleteRow( const SwCursor& rCursor )
+{
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ GetTableSel( rCursor, aBoxes, SwTableSearchType::Row );
+ if( ::HasProtectedCells( aBoxes ))
+ return;
+
+ // Remove the Cursor from the to-be-deleted Section.
+ // The Cursor is placed after the table, except for
+ // - when there's another Line, we place it in that one
+ // - when a Line precedes it, we place it in that one
+ {
+ SwTableNode* pTableNd = rCursor.GetNode().FindTableNode();
+
+ if(dynamic_cast<const SwDDETable*>( & pTableNd->GetTable()) != nullptr)
+ return;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( aBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+
+ if( aFndBox.GetLines().empty() )
+ return;
+
+ if (SwEditShell* pESh = GetEditShell())
+ {
+ pESh->KillPams();
+ // FIXME: actually we should be iterating over all Shells!
+ }
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size() )
+ {
+ FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ if( pTmp->GetBox()->GetSttNd() )
+ break; // Else it gets too far
+ pFndBox = pTmp;
+ }
+
+ SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
+ SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
+ while( !pDelBox->GetSttNd() )
+ {
+ SwTableLine* pLn = pDelBox->GetTabLines()[
+ pDelBox->GetTabLines().size()-1 ];
+ pDelBox = pLn->GetTabBoxes().back();
+ }
+ SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
+ pDelBox );
+ while( pNextBox &&
+ pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
+
+ if( !pNextBox ) // No succeeding Boxes? Then take the preceding one
+ {
+ pDelLine = pFndBox->GetLines().front()->GetLine();
+ pDelBox = pDelLine->GetTabBoxes()[ 0 ];
+ while( !pDelBox->GetSttNd() )
+ pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
+ pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
+ pDelBox );
+ while( pNextBox &&
+ pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
+ }
+
+ SwNodeOffset nIdx;
+ if( pNextBox ) // Place the Cursor here
+ nIdx = pNextBox->GetSttIdx() + 1;
+ else // Else after the Table
+ nIdx = pTableNd->EndOfSectionIndex() + 1;
+
+ SwNodeIndex aIdx( GetNodes(), nIdx );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = GetNodes().GoNext( &aIdx );
+
+ if( pCNd )
+ {
+ // Change the Shell's Cursor or the one passed?
+ SwPaM* pPam = const_cast<SwPaM*>(static_cast<SwPaM const *>(&rCursor));
+ pPam->GetPoint()->nNode = aIdx;
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+ pPam->SetMark(); // Both want a part of it
+ pPam->DeleteMark();
+ }
+ }
+
+ // Thus delete the Rows
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::ROW_DELETE, nullptr);
+ DeleteRowCol( aBoxes );
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::ROW_DELETE, nullptr);
+}
+
+void SwDoc::DeleteCol( const SwCursor& rCursor )
+{
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ GetTableSel( rCursor, aBoxes, SwTableSearchType::Col );
+ if( ::HasProtectedCells( aBoxes ))
+ return;
+
+ // The Cursors need to be removed from the to-be-deleted range.
+ // Always place them after/on top of the Table; they are always set
+ // to the old position via the document position.
+ if (SwEditShell* pESh = GetEditShell())
+ {
+ const SwNode* pNd = rCursor.GetNode().FindTableBoxStartNode();
+ pESh->ParkCursor( SwNodeIndex( *pNd ) );
+ }
+
+ // Thus delete the Columns
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::COL_DELETE, nullptr);
+ DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::COL_DELETE, nullptr);
+}
+
+bool SwDoc::DeleteRowCol(const SwSelBoxes& rBoxes, RowColMode const eMode)
+{
+ if (!(eMode & SwDoc::RowColMode::DeleteProtected)
+ && ::HasProtectedCells(rBoxes))
+ {
+ return false;
+ }
+
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ if (!(eMode & SwDoc::RowColMode::DeleteProtected)
+ && dynamic_cast<const SwDDETable*>(&pTableNd->GetTable()) != nullptr)
+ {
+ return false;
+ }
+
+ ::ClearFEShellTabCols(*this, nullptr);
+ SwSelBoxes aSelBoxes( rBoxes );
+ SwTable &rTable = pTableNd->GetTable();
+ tools::Long nMin = 0;
+ tools::Long nMax = 0;
+ if( rTable.IsNewModel() )
+ {
+ if (eMode & SwDoc::RowColMode::DeleteColumn)
+ rTable.ExpandColumnSelection( aSelBoxes, nMin, nMax );
+ else
+ rTable.FindSuperfluousRows( aSelBoxes );
+ }
+
+ // Are we deleting the whole Table?
+ const SwNodeOffset nTmpIdx1 = pTableNd->GetIndex();
+ const SwNodeOffset nTmpIdx2 = aSelBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1;
+ if( pTableNd->GetTable().GetTabSortBoxes().size() == aSelBoxes.size() &&
+ aSelBoxes[0]->GetSttIdx()-1 == nTmpIdx1 &&
+ nTmpIdx2 == pTableNd->EndOfSectionIndex() )
+ {
+ bool bNewTextNd = false;
+ // Is it alone in a FlyFrame?
+ SwNodeIndex aIdx( *pTableNd, -1 );
+ const SwStartNode* pSttNd = aIdx.GetNode().GetStartNode();
+ if( pSttNd )
+ {
+ const SwNodeOffset nTableEnd = pTableNd->EndOfSectionIndex() + 1;
+ const SwNodeOffset nSectEnd = pSttNd->EndOfSectionIndex();
+ if( nTableEnd == nSectEnd )
+ {
+ if( SwFlyStartNode == pSttNd->GetStartNodeType() )
+ {
+ SwFrameFormat* pFormat = pSttNd->GetFlyFormat();
+ if( pFormat )
+ {
+ // That's the FlyFormat we're looking for
+ getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+ return true;
+ }
+ }
+ // No Fly? Thus Header or Footer: always leave a TextNode
+ // We can forget about Undo then!
+ bNewTextNd = true;
+ }
+ }
+
+ // No Fly? Then it is a Header or Footer, so keep always a TextNode
+ ++aIdx;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ SwPaM aPaM( *pTableNd->EndOfSectionNode(), aIdx.GetNode() );
+
+ if( bNewTextNd )
+ {
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ GetNodes().MakeTextNode( aTmpIdx,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ // Save the cursors (UNO and otherwise)
+ SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) );
+ if( ! aSavePaM.Move( fnMoveForward, GoInNode ) )
+ {
+ *aSavePaM.GetMark() = SwPosition( *pTableNd );
+ aSavePaM.Move( fnMoveBackward, GoInNode );
+ }
+ {
+ SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
+ ::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark());
+ }
+
+ // Move hard PageBreaks to the succeeding Node
+ bool bSavePageBreak = false, bSavePageDesc = false;
+ SwNodeOffset nNextNd = pTableNd->EndOfSectionIndex()+1;
+ SwContentNode* pNextNd = GetNodes()[ nNextNd ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ {
+ pNextNd->SetAttr( *pItem );
+ bSavePageDesc = true;
+ }
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ {
+ pNextNd->SetAttr( *pItem );
+ bSavePageBreak = true;
+ }
+ }
+ std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aPaM, SwDeleteFlags::Default));
+ if( bNewTextNd )
+ pUndo->SetTableDelLastNd();
+ pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc );
+ pUndo->SetTableName(pTableNd->GetTable().GetFrameFormat()->GetName());
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ else
+ {
+ if( bNewTextNd )
+ {
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ GetNodes().MakeTextNode( aTmpIdx,
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ // Save the cursors (UNO and otherwise)
+ SwPaM aSavePaM( SwNodeIndex( *pTableNd->EndOfSectionNode() ) );
+ if( ! aSavePaM.Move( fnMoveForward, GoInNode ) )
+ {
+ *aSavePaM.GetMark() = SwPosition( *pTableNd );
+ aSavePaM.Move( fnMoveBackward, GoInNode );
+ }
+ {
+ SwPaM const tmpPaM(*pTableNd, *pTableNd->EndOfSectionNode());
+ ::PaMCorrAbs(tmpPaM, *aSavePaM.GetMark());
+ }
+
+ // Move hard PageBreaks to the succeeding Node
+ SwContentNode* pNextNd = GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_PAGEDESC,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( SfxItemState::SET == pTableFormat->GetItemState( RES_BREAK,
+ false, &pItem ) )
+ pNextNd->SetAttr( *pItem );
+ }
+
+ pTableNd->DelFrames();
+ getIDocumentContentOperations().DeleteSection( pTableNd );
+ }
+
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ return true;
+ }
+
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_DELBOX, aSelBoxes, *pTableNd,
+ nMin, nMax, 0, false, false ));
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if (rTable.IsNewModel())
+ {
+ if (eMode & SwDoc::RowColMode::DeleteColumn)
+ rTable.PrepareDeleteCol( nMin, nMax );
+ rTable.FindSuperfluousRows( aSelBoxes );
+ if (pUndo)
+ pUndo->ReNewBoxes( aSelBoxes );
+ }
+ bRet = rTable.DeleteSel( this, aSelBoxes, nullptr, pUndo.get(), true, true );
+ if (bRet)
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ return bRet;
+}
+
+/**
+ * Split up/merge Boxes in the Table
+ */
+bool SwDoc::SplitTable( const SwSelBoxes& rBoxes, bool bVert, sal_uInt16 nCnt,
+ bool bSameHeight )
+{
+ OSL_ENSURE( !rBoxes.empty() && nCnt, "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr)
+ return false;
+
+ std::vector<SwNodeOffset> aNdsCnts;
+ SwTableSortBoxes aTmpLst;
+ std::unique_ptr<SwUndoTableNdsChg> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo.reset(new SwUndoTableNdsChg( SwUndoId::TABLE_SPLIT, rBoxes, *pTableNd, 0, 0,
+ nCnt, bVert, bSameHeight ));
+
+ aTmpLst.insert( rTable.GetTabSortBoxes() );
+ if( !bVert )
+ {
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ const SwStartNode* pSttNd = rBoxes[ n ]->GetSttNd();
+ aNdsCnts.push_back( pSttNd->EndOfSectionIndex() -
+ pSttNd->GetIndex() );
+ }
+ }
+ }
+
+ bool bRet(false);
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if (bVert)
+ bRet = rTable.SplitCol(*this, rBoxes, nCnt);
+ else
+ bRet = rTable.SplitRow(*this, rBoxes, nCnt, bSameHeight);
+
+ if (bRet)
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ }
+
+ if( pUndo && bRet )
+ {
+ if( bVert )
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ else
+ pUndo->SaveNewBoxes( *pTableNd, aTmpLst, rBoxes, aNdsCnts );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ return bRet;
+}
+
+TableMergeErr SwDoc::MergeTable( SwPaM& rPam )
+{
+ // Check if the current cursor's Point/Mark are inside a Table
+ SwTableNode* pTableNd = rPam.GetNode().FindTableNode();
+ if( !pTableNd )
+ return TableMergeErr::NoSelection;
+ SwTable& rTable = pTableNd->GetTable();
+ if( dynamic_cast<const SwDDETable*>( &rTable) != nullptr )
+ return TableMergeErr::NoSelection;
+ TableMergeErr nRet = TableMergeErr::NoSelection;
+ if( !rTable.IsNewModel() )
+ {
+ nRet =::CheckMergeSel( rPam );
+ if( TableMergeErr::Ok != nRet )
+ return nRet;
+ nRet = TableMergeErr::NoSelection;
+ }
+
+ // #i33394#
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_MERGE, nullptr );
+
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore);
+
+ std::unique_ptr<SwUndoTableMerge> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoTableMerge( rPam ));
+
+ // Find the Boxes via the Layout
+ SwSelBoxes aBoxes;
+ SwSelBoxes aMerged;
+ SwTableBox* pMergeBox;
+
+ if( !rTable.PrepareMerge( rPam, aBoxes, aMerged, &pMergeBox, pUndo.get() ) )
+ { // No cells found to merge
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ if( pUndo )
+ {
+ pUndo.reset();
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ if (GetIDocumentUndoRedo().GetLastUndoInfo(nullptr, & nLastUndoId)
+ && (SwUndoId::REDLINE == nLastUndoId))
+ {
+ // FIXME: why is this horrible cleanup necessary?
+ SwUndoRedline *const pU = dynamic_cast<SwUndoRedline*>(
+ GetUndoManager().RemoveLastUndo());
+ if (pU && pU->GetRedlSaveCount())
+ {
+ SwEditShell *const pEditShell(GetEditShell());
+ assert(pEditShell);
+ ::sw::UndoRedoContext context(*this, *pEditShell);
+ static_cast<SfxUndoAction *>(pU)->UndoWithContext(context);
+ }
+ delete pU;
+ }
+ }
+ }
+ else
+ {
+ // The PaMs need to be removed from the to-be-deleted range. Thus always place
+ // them at the end of/on top of the Table; it's always set to the old position via
+ // the Document Position.
+ // For a start remember an index for the temporary position, because we cannot
+ // access it after GetMergeSel
+ {
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = *pMergeBox->GetSttNd();
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ rPam.SetMark();
+ rPam.DeleteMark();
+
+ SwPaM* pTmp = &rPam;
+ while( &rPam != ( pTmp = pTmp->GetNext() ))
+ for( int i = 0; i < 2; ++i )
+ pTmp->GetBound( static_cast<bool>(i) ) = *rPam.GetPoint();
+
+ if (SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(&rPam))
+ {
+ // tdf#135098 update selection so rPam's m_SelectedBoxes is updated
+ // to not contain the soon to-be-deleted SwTableBox so if the rPam
+ // is queried via a11y it doesn't claim the deleted cell still
+ // exists
+ pTableCursor->NewTableSelection();
+ }
+ }
+
+ // Merge them
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ if( pTableNd->GetTable().Merge( this, aBoxes, aMerged, pMergeBox, pUndo.get() ))
+ {
+ nRet = TableMergeErr::Ok;
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+
+ rPam.GetPoint()->nNode = *pMergeBox->GetSttNd();
+ rPam.Move();
+
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_MERGE, nullptr );
+ return nRet;
+}
+
+SwTableNode::SwTableNode( const SwNodeIndex& rIdx )
+ : SwStartNode( rIdx, SwNodeType::Table )
+{
+ m_pTable.reset(new SwTable);
+}
+
+SwTableNode::~SwTableNode()
+{
+ // Notify UNO wrappers
+ GetTable().GetFrameFormat()->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
+ DelFrames();
+ m_pTable->SetTableNode(this); // set this so that ~SwDDETable can read it!
+ m_pTable.reset();
+}
+
+SwTabFrame *SwTableNode::MakeFrame( SwFrame* pSib )
+{
+ return new SwTabFrame( *m_pTable, pSib );
+}
+
+/**
+ * Creates all Views from the Document for the preceding Node. The resulting ContentFrames
+ * are added to the corresponding Layout.
+ */
+void SwTableNode::MakeFramesForAdjacentContentNode(const SwNodeIndex & rIdx)
+{
+ if( !GetTable().GetFrameFormat()->HasWriterListeners()) // Do we actually have Frame?
+ return;
+
+ SwFrame *pFrame;
+ SwContentNode * pNode = rIdx.GetNode().GetContentNode();
+
+ OSL_ENSURE( pNode, "No ContentNode or CopyNode and new Node is identical");
+
+ bool bBefore = rIdx < GetIndex();
+
+ SwNode2Layout aNode2Layout( *this, rIdx.GetIndex() );
+
+ while( nullptr != (pFrame = aNode2Layout.NextFrame()) )
+ {
+ if ( ( pFrame->getRootFrame()->HasMergedParas() &&
+ !pNode->IsCreateFrameWhenHidingRedlines() ) ||
+ // tdf#153819 table deletion with change tracking:
+ // table node without frames in Hide Changes mode
+ !pFrame->GetUpper() )
+ {
+ continue;
+ }
+ SwFrame *pNew = pNode->MakeFrame( pFrame );
+ // Will the Node receive Frames before or after?
+ if ( bBefore )
+ // The new one precedes me
+ pNew->Paste( pFrame->GetUpper(), pFrame );
+ else
+ // The new one succeeds me
+ pNew->Paste( pFrame->GetUpper(), pFrame->GetNext() );
+ }
+}
+
+/**
+ * Create a TableFrame for every Shell and insert before the corresponding ContentFrame.
+ */
+void SwTableNode::MakeOwnFrames(SwNodeIndex* pIdxBehind)
+{
+ OSL_ENSURE( pIdxBehind, "No Index" );
+ *pIdxBehind = *this;
+ SwNode *pNd = GetNodes().FindPrvNxtFrameNode( *pIdxBehind, EndOfSectionNode() );
+ if( !pNd )
+ return ;
+
+ SwFrame *pFrame( nullptr );
+ SwLayoutFrame *pUpper( nullptr );
+ SwNode2Layout aNode2Layout( *pNd, GetIndex() );
+ while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, *this )) )
+ {
+ if (pUpper->getRootFrame()->HasMergedParas()
+ && !IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ SwTabFrame* pNew = MakeFrame( pUpper );
+ pNew->Paste( pUpper, pFrame );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ pNew->RegistFlys();
+ }
+}
+
+void SwTableNode::DelFrames(SwRootFrame const*const pLayout)
+{
+ /* For a start, cut out and delete the TabFrames (which will also delete the Columns and Rows)
+ The TabFrames are attached to the FrameFormat of the SwTable.
+ We need to delete them in a more cumbersome way, for the Master to also delete the Follows. */
+
+ SwIterator<SwTabFrame,SwFormat> aIter( *(m_pTable->GetFrameFormat()) );
+ SwTabFrame *pFrame = aIter.First();
+ while ( pFrame )
+ {
+ bool bAgain = false;
+ {
+ if (!pFrame->IsFollow() && (!pLayout || pLayout == pFrame->getRootFrame()))
+ {
+ while ( pFrame->HasFollow() )
+ pFrame->JoinAndDelFollows();
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if (pFrame->GetUpper())
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ bAgain = true;
+ }
+ }
+ pFrame = bAgain ? aIter.First() : aIter.Next();
+ }
+}
+
+void SwTableNode::SetNewTable( std::unique_ptr<SwTable> pNewTable, bool bNewFrames )
+{
+ DelFrames();
+ m_pTable->SetTableNode(this);
+ m_pTable = std::move(pNewTable);
+ if( bNewFrames )
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode());
+ GetNodes().GoNext( &aIdx );
+ MakeOwnFrames(&aIdx);
+ }
+}
+
+void SwTableNode::RemoveRedlines()
+{
+ SwDoc& rDoc = GetDoc();
+ SwTable& rTable = GetTable();
+ rDoc.getIDocumentRedlineAccess().GetExtraRedlineTable().DeleteAllTableRedlines(rDoc, rTable, true, RedlineType::Any);
+}
+
+void SwTableNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableNode"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (m_pTable)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTable"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", m_pTable.get());
+ m_pTable->GetFrameFormat()->dumpAsXml(pWriter);
+ for (const auto& pLine : m_pTable->GetTabLines())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTableLine"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", pLine);
+ pLine->GetFrameFormat()->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+void SwDoc::GetTabCols( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
+{
+ OSL_ENSURE( pBoxFrame, "pBoxFrame needs to be specified!" );
+ if( !pBoxFrame )
+ return;
+
+ SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
+ const SwTableBox* pBox = pBoxFrame->GetTabBox();
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ SwRectFnSet aRectFnSet(pTab);
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+ const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ rFill.SetLeftMin ( nLeftMin );
+ rFill.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
+ rFill.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
+ rFill.SetRightMax( nRightMax - nLeftMin );
+
+ pTab->GetTable()->GetTabCols( rFill, pBox );
+}
+
+// Here are some little helpers used in SwDoc::GetTabRows
+
+#define ROWFUZZY 25
+
+namespace {
+
+struct FuzzyCompare
+{
+ bool operator() ( tools::Long s1, tools::Long s2 ) const;
+};
+
+}
+
+bool FuzzyCompare::operator() ( tools::Long s1, tools::Long s2 ) const
+{
+ return ( s1 < s2 && std::abs( s1 - s2 ) > ROWFUZZY );
+}
+
+static bool lcl_IsFrameInColumn( const SwCellFrame& rFrame, SwSelBoxes const & rBoxes )
+{
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ if ( rFrame.GetTabBox() == rBoxes[ i ] )
+ return true;
+ }
+
+ return false;
+}
+
+void SwDoc::GetTabRows( SwTabCols &rFill, const SwCellFrame* pBoxFrame )
+{
+ OSL_ENSURE( pBoxFrame, "GetTabRows called without pBoxFrame" );
+
+ // Make code robust:
+ if ( !pBoxFrame )
+ return;
+
+ // #i39552# Collection of the boxes of the current
+ // column has to be done at the beginning of this function, because
+ // the table may be formatted in ::GetTableSel.
+ SwDeletionChecker aDelCheck( pBoxFrame );
+
+ SwSelBoxes aBoxes;
+ const SwContentFrame* pContent = ::GetCellContent( *pBoxFrame );
+ if ( pContent && pContent->IsTextFrame() )
+ {
+ const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
+ const SwCursor aTmpCursor( aPos, nullptr );
+ ::GetTableSel( aTmpCursor, aBoxes, SwTableSearchType::Col );
+ }
+
+ // Make code robust:
+ if ( aDelCheck.HasBeenDeleted() )
+ {
+ OSL_FAIL( "Current box has been deleted during GetTabRows()" );
+ return;
+ }
+
+ // Make code robust:
+ const SwTabFrame* pTab = pBoxFrame->FindTabFrame();
+ OSL_ENSURE( pTab, "GetTabRows called without a table" );
+ if ( !pTab )
+ return;
+
+ const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ SwRectFnSet aRectFnSet(pTab);
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
+ pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
+ pTab->GetPrtTop() - pPage->getFrameArea().Top() );
+ const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
+ const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
+ const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
+
+ rFill.SetLeftMin( nLeftMin );
+ rFill.SetLeft( nLeft );
+ rFill.SetRight( nRight );
+ rFill.SetRightMax( nRightMax );
+
+ typedef std::map< tools::Long, std::pair< tools::Long, long >, FuzzyCompare > BoundaryMap;
+ BoundaryMap aBoundaries;
+ BoundaryMap::iterator aIter;
+ std::pair< tools::Long, long > aPair;
+
+ typedef std::map< tools::Long, bool > HiddenMap;
+ HiddenMap aHidden;
+ HiddenMap::iterator aHiddenIter;
+
+ while ( pFrame && pTab->IsAnLower( pFrame ) )
+ {
+ if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
+ {
+ // upper and lower borders of current cell frame:
+ tools::Long nUpperBorder = aRectFnSet.GetTop(pFrame->getFrameArea());
+ tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
+
+ // get boundaries for nUpperBorder:
+ aIter = aBoundaries.find( nUpperBorder );
+ if ( aIter == aBoundaries.end() )
+ {
+ aPair.first = nUpperBorder; aPair.second = LONG_MAX;
+ aBoundaries[ nUpperBorder ] = aPair;
+ }
+
+ // get boundaries for nLowerBorder:
+ aIter = aBoundaries.find( nLowerBorder );
+ if ( aIter == aBoundaries.end() )
+ {
+ aPair.first = nUpperBorder; aPair.second = LONG_MAX;
+ }
+ else
+ {
+ nLowerBorder = (*aIter).first;
+ tools::Long nNewLowerBorderUpperBoundary = std::max( (*aIter).second.first, nUpperBorder );
+ aPair.first = nNewLowerBorderUpperBoundary; aPair.second = LONG_MAX;
+ }
+ aBoundaries[ nLowerBorder ] = aPair;
+
+ // calculate hidden flags for entry nUpperBorder/nLowerBorder:
+ tools::Long nTmpVal = nUpperBorder;
+ for ( sal_uInt8 i = 0; i < 2; ++i )
+ {
+ aHiddenIter = aHidden.find( nTmpVal );
+ if ( aHiddenIter == aHidden.end() )
+ aHidden[ nTmpVal ] = !lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes );
+ else
+ {
+ if ( aHidden[ nTmpVal ] &&
+ lcl_IsFrameInColumn( *static_cast<const SwCellFrame*>(pFrame), aBoxes ) )
+ aHidden[ nTmpVal ] = false;
+ }
+ nTmpVal = nLowerBorder;
+ }
+ }
+
+ pFrame = pFrame->GetNextLayoutLeaf();
+ }
+
+ // transfer calculated values from BoundaryMap and HiddenMap into rFill:
+ size_t nIdx = 0;
+ for ( const auto& rEntry : aBoundaries )
+ {
+ const tools::Long nTabTop = aRectFnSet.GetPrtTop(*pTab);
+ const tools::Long nKey = aRectFnSet.YDiff( rEntry.first, nTabTop );
+ const std::pair< tools::Long, long > aTmpPair = rEntry.second;
+ const tools::Long nFirst = aRectFnSet.YDiff( aTmpPair.first, nTabTop );
+ const tools::Long nSecond = aTmpPair.second;
+
+ aHiddenIter = aHidden.find( rEntry.first );
+ const bool bHidden = aHiddenIter != aHidden.end() && (*aHiddenIter).second;
+ rFill.Insert( nKey, nFirst, nSecond, bHidden, nIdx++ );
+ }
+
+ // delete first and last entry
+ OSL_ENSURE( rFill.Count(), "Deleting from empty vector. Fasten your seatbelts!" );
+ // #i60818# There may be only one entry in rFill. Make
+ // code robust by checking count of rFill.
+ if ( rFill.Count() ) rFill.Remove( 0 );
+ if ( rFill.Count() ) rFill.Remove( rFill.Count() - 1 );
+ rFill.SetLastRowAllowedToChange( !pTab->HasFollowFlowLine() );
+}
+
+void SwDoc::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly,
+ const SwCellFrame* pBoxFrame )
+{
+ const SwTableBox* pBox = nullptr;
+ SwTabFrame *pTab = nullptr;
+
+ if( pBoxFrame )
+ {
+ pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
+ pBox = pBoxFrame->GetTabBox();
+ }
+ else
+ {
+ OSL_ENSURE( false, "must specify pBoxFrame" );
+ return ;
+ }
+
+ // If the Table is still using relative values (USHRT_MAX)
+ // we need to switch to absolute ones.
+ SwTable& rTab = *pTab->GetTable();
+ const SwFormatFrameSize& rTableFrameSz = rTab.GetFrameFormat()->GetFrameSize();
+ SwRectFnSet aRectFnSet(pTab);
+ // #i17174# - With fix for #i9040# the shadow size is taken
+ // from the table width. Thus, add its left and right size to current table
+ // printing area width in order to get the correct table size attribute.
+ SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ {
+ SvxShadowItem aShadow( rTab.GetFrameFormat()->GetShadow() );
+ nPrtWidth += aShadow.CalcShadowSpace( SvxShadowItemSide::LEFT ) +
+ aShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
+ }
+ if( nPrtWidth != rTableFrameSz.GetWidth() )
+ {
+ SwFormatFrameSize aSz( rTableFrameSz );
+ aSz.SetWidth( nPrtWidth );
+ rTab.GetFrameFormat()->SetFormatAttr( aSz );
+ }
+
+ SwTabCols aOld( rNew.Count() );
+
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+ const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ aOld.SetLeftMin ( nLeftMin );
+ aOld.SetLeft ( aRectFnSet.GetLeft(pTab->getFramePrintArea()) );
+ aOld.SetRight ( aRectFnSet.GetRight(pTab->getFramePrintArea()));
+ aOld.SetRightMax( nRightMax - nLeftMin );
+
+ rTab.GetTabCols( aOld, pBox );
+ SetTabCols(rTab, rNew, aOld, pBox, bCurRowOnly );
+}
+
+void SwDoc::SetTabRows( const SwTabCols &rNew, bool bCurColOnly,
+ const SwCellFrame* pBoxFrame )
+{
+ SwTabFrame *pTab = nullptr;
+
+ if( pBoxFrame )
+ {
+ pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoxFrame))->ImplFindTabFrame();
+ }
+ else
+ {
+ OSL_ENSURE( false, "must specify pBoxFrame" );
+ return ;
+ }
+
+ // If the Table is still using relative values (USHRT_MAX)
+ // we need to switch to absolute ones.
+ SwRectFnSet aRectFnSet(pTab);
+ SwTabCols aOld( rNew.Count() );
+
+ // Set fixed points, LeftMin in Document coordinates, all others relative
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+
+ aOld.SetRight( aRectFnSet.GetHeight(pTab->getFramePrintArea()) );
+ tools::Long nLeftMin;
+ if ( aRectFnSet.IsVert() )
+ {
+ nLeftMin = pTab->GetPrtLeft() - pPage->getFrameArea().Left();
+ aOld.SetLeft ( LONG_MAX );
+ aOld.SetRightMax( aOld.GetRight() );
+
+ }
+ else
+ {
+ nLeftMin = pTab->GetPrtTop() - pPage->getFrameArea().Top();
+ aOld.SetLeft ( 0 );
+ aOld.SetRightMax( LONG_MAX );
+ }
+ aOld.SetLeftMin ( nLeftMin );
+
+ GetTabRows( aOld, pBoxFrame );
+
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_ATTR, nullptr );
+
+ // check for differences between aOld and rNew:
+ const size_t nCount = rNew.Count();
+ const SwTable* pTable = pTab->GetTable();
+ OSL_ENSURE( pTable, "My colleague told me, this couldn't happen" );
+
+ for ( size_t i = 0; i <= nCount; ++i )
+ {
+ const size_t nIdxStt = aRectFnSet.IsVert() ? nCount - i : i - 1;
+ const size_t nIdxEnd = aRectFnSet.IsVert() ? nCount - i - 1 : i;
+
+ const tools::Long nOldRowStart = i == 0 ? 0 : aOld[ nIdxStt ];
+ const tools::Long nOldRowEnd = i == nCount ? aOld.GetRight() : aOld[ nIdxEnd ];
+ const tools::Long nOldRowHeight = nOldRowEnd - nOldRowStart;
+
+ const tools::Long nNewRowStart = i == 0 ? 0 : rNew[ nIdxStt ];
+ const tools::Long nNewRowEnd = i == nCount ? rNew.GetRight() : rNew[ nIdxEnd ];
+ const tools::Long nNewRowHeight = nNewRowEnd - nNewRowStart;
+
+ const tools::Long nDiff = nNewRowHeight - nOldRowHeight;
+ if ( std::abs( nDiff ) >= ROWFUZZY )
+ {
+ // For the old table model pTextFrame and pLine will be set for every box.
+ // For the new table model pTextFrame will be set if the box is not covered,
+ // but the pLine will be set if the box is not an overlapping box
+ // In the new table model the row height can be adjusted,
+ // when both variables are set.
+ const SwTextFrame* pTextFrame = nullptr;
+ const SwTableLine* pLine = nullptr;
+
+ // Iterate over all SwCellFrames with Bottom = nOldPos
+ const SwFrame* pFrame = pTab->GetNextLayoutLeaf();
+ while ( pFrame && pTab->IsAnLower( pFrame ) )
+ {
+ if ( pFrame->IsCellFrame() && pFrame->FindTabFrame() == pTab )
+ {
+ const tools::Long nLowerBorder = aRectFnSet.GetBottom(pFrame->getFrameArea());
+ const sal_uLong nTabTop = aRectFnSet.GetPrtTop(*pTab);
+ if ( std::abs( aRectFnSet.YInc( nTabTop, nOldRowEnd ) - nLowerBorder ) <= ROWFUZZY )
+ {
+ if ( !bCurColOnly || pFrame == pBoxFrame )
+ {
+ const SwFrame* pContent = ::GetCellContent( static_cast<const SwCellFrame&>(*pFrame) );
+
+ if ( pContent && pContent->IsTextFrame() )
+ {
+ const SwTableBox* pBox = static_cast<const SwCellFrame*>(pFrame)->GetTabBox();
+ const sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan > 0 ) // Not overlapped
+ pTextFrame = static_cast<const SwTextFrame*>(pContent);
+ if( nRowSpan < 2 ) // Not overlapping for row height
+ pLine = pBox->GetUpper();
+ if( pLine && pTextFrame ) // always for old table model
+ {
+ // The new row height must not to be calculated from an overlapping box
+ SwFormatFrameSize aNew( pLine->GetFrameFormat()->GetFrameSize() );
+ const tools::Long nNewSize = aRectFnSet.GetHeight(pFrame->getFrameArea()) + nDiff;
+ if( nNewSize != aNew.GetHeight() )
+ {
+ aNew.SetHeight( nNewSize );
+ if ( SwFrameSize::Variable == aNew.GetHeightSizeType() )
+ aNew.SetHeightSizeType( SwFrameSize::Minimum );
+ // This position must not be in an overlapped box
+ const SwPosition aPos(*static_cast<const SwTextFrame*>(pContent)->GetTextNodeFirst());
+ const SwCursor aTmpCursor( aPos, nullptr );
+ SetRowHeight( aTmpCursor, aNew );
+ // For the new table model we're done, for the old one
+ // there might be another (sub)row to adjust...
+ if( pTable->IsNewModel() )
+ break;
+ }
+ pLine = nullptr;
+ }
+ }
+ }
+ }
+ }
+ pFrame = pFrame->GetNextLayoutLeaf();
+ }
+ }
+ }
+
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::TABLE_ATTR, nullptr );
+
+ ::ClearFEShellTabCols(*this, nullptr);
+}
+
+/**
+ * Direct access for UNO
+ */
+void SwDoc::SetTabCols(SwTable& rTab, const SwTabCols &rNew, const SwTabCols &rOld,
+ const SwTableBox *pStart, bool bCurRowOnly )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAttrTable>( *rTab.GetTableNode(), true ));
+ }
+ rTab.SetTabCols( rNew, rOld, pStart, bCurRowOnly );
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetRowsToRepeat( SwTable &rTable, sal_uInt16 nSet )
+{
+ if( nSet == rTable.GetRowsToRepeat() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableHeadline>(rTable, rTable.GetRowsToRepeat(), nSet) );
+ }
+
+ rTable.SetRowsToRepeat(nSet);
+ const SwMsgPoolItem aChg(RES_TBLHEADLINECHG);
+ rTable.GetFrameFormat()->CallSwClientNotify(sw::LegacyModifyHint(&aChg, &aChg));
+ getIDocumentState().SetModified();
+}
+
+void SwCollectTableLineBoxes::AddToUndoHistory( const SwContentNode& rNd )
+{
+ if( m_pHistory )
+ m_pHistory->Add( rNd.GetFormatColl(), rNd.GetIndex(), SwNodeType::Text );
+}
+
+void SwCollectTableLineBoxes::AddBox( const SwTableBox& rBox )
+{
+ m_aPositionArr.push_back(m_nWidth);
+ SwTableBox* p = const_cast<SwTableBox*>(&rBox);
+ m_Boxes.push_back(p);
+ m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
+}
+
+const SwTableBox* SwCollectTableLineBoxes::GetBoxOfPos( const SwTableBox& rBox )
+{
+ const SwTableBox* pRet = nullptr;
+
+ if( !m_aPositionArr.empty() )
+ {
+ std::vector<sal_uInt16>::size_type n;
+ for( n = 0; n < m_aPositionArr.size(); ++n )
+ if( m_aPositionArr[ n ] == m_nWidth )
+ break;
+ else if( m_aPositionArr[ n ] > m_nWidth )
+ {
+ if( n )
+ --n;
+ break;
+ }
+
+ if( n >= m_aPositionArr.size() )
+ --n;
+
+ m_nWidth = m_nWidth + o3tl::narrowing<sal_uInt16>(rBox.GetFrameFormat()->GetFrameSize().GetWidth());
+ pRet = m_Boxes[ n ];
+ }
+ return pRet;
+}
+
+bool SwCollectTableLineBoxes::Resize( sal_uInt16 nOffset, sal_uInt16 nOldWidth )
+{
+ if( !m_aPositionArr.empty() )
+ {
+ std::vector<sal_uInt16>::size_type n;
+ for( n = 0; n < m_aPositionArr.size(); ++n )
+ {
+ if( m_aPositionArr[ n ] == nOffset )
+ break;
+ else if( m_aPositionArr[ n ] > nOffset )
+ {
+ if( n )
+ --n;
+ break;
+ }
+ }
+
+ m_aPositionArr.erase( m_aPositionArr.begin(), m_aPositionArr.begin() + n );
+ m_Boxes.erase(m_Boxes.begin(), m_Boxes.begin() + n);
+
+ size_t nArrSize = m_aPositionArr.size();
+ if (nArrSize)
+ {
+ if (nOldWidth == 0)
+ throw o3tl::divide_by_zero();
+
+ // Adapt the positions to the new Size
+ for( n = 0; n < nArrSize; ++n )
+ {
+ sal_uLong nSize = m_nWidth;
+ nSize *= ( m_aPositionArr[ n ] - nOffset );
+ nSize /= nOldWidth;
+ m_aPositionArr[ n ] = sal_uInt16( nSize );
+ }
+ }
+ }
+ return !m_aPositionArr.empty();
+}
+
+bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara )
+{
+ SwCollectTableLineBoxes* pSplPara = static_cast<SwCollectTableLineBoxes*>(pPara);
+ if( pSplPara->IsGetValues() )
+ for( const auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, pSplPara );
+ else
+ for( auto& rpBox : const_cast<SwTableLine*>(rpLine)->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, pSplPara );
+ return true;
+}
+
+void sw_Box_CollectBox( const SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
+{
+ auto nLen = pBox->GetTabLines().size();
+ if( nLen )
+ {
+ // Continue with the actual Line
+ if( pSplPara->IsGetFromTop() )
+ nLen = 0;
+ else
+ --nLen;
+
+ const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
+ sw_Line_CollectBox( pLn, pSplPara );
+ }
+ else
+ pSplPara->AddBox( *pBox );
+}
+
+void sw_BoxSetSplitBoxFormats( SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara )
+{
+ auto nLen = pBox->GetTabLines().size();
+ if( nLen )
+ {
+ // Continue with the actual Line
+ if( pSplPara->IsGetFromTop() )
+ nLen = 0;
+ else
+ --nLen;
+
+ const SwTableLine* pLn = pBox->GetTabLines()[ nLen ];
+ sw_Line_CollectBox( pLn, pSplPara );
+ }
+ else
+ {
+ const SwTableBox* pSrcBox = pSplPara->GetBoxOfPos( *pBox );
+ SwFrameFormat* pFormat = pSrcBox->GetFrameFormat();
+
+ if( SplitTable_HeadlineOption::BorderCopy == pSplPara->GetMode() )
+ {
+ const SvxBoxItem& rBoxItem = pBox->GetFrameFormat()->GetBox();
+ if( !rBoxItem.GetTop() )
+ {
+ SvxBoxItem aNew( rBoxItem );
+ aNew.SetLine( pFormat->GetBox().GetBottom(), SvxBoxItemLine::TOP );
+ if( aNew != rBoxItem )
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+ }
+ }
+ else
+ {
+ SfxItemSetFixed<RES_LR_SPACE, RES_UL_SPACE,
+ RES_PROTECT, RES_PROTECT,
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BACKGROUND, RES_SHADOW>
+ aTmpSet( pFormat->GetDoc()->GetAttrPool() );
+ aTmpSet.Put( pFormat->GetAttrSet() );
+ if( aTmpSet.Count() )
+ pBox->ClaimFrameFormat()->SetFormatAttr( aTmpSet );
+
+ if( SplitTable_HeadlineOption::BoxAttrAllCopy == pSplPara->GetMode() )
+ {
+ SwNodeIndex aIdx( *pSrcBox->GetSttNd(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNext( &aIdx );
+ aIdx = *pBox->GetSttNd();
+ SwContentNode* pDNd = aIdx.GetNodes().GoNext( &aIdx );
+
+ // If the Node is alone in the Section
+ if( SwNodeOffset(2) == pDNd->EndOfSectionIndex() -
+ pDNd->StartOfSectionIndex() )
+ {
+ pSplPara->AddToUndoHistory( *pDNd );
+ pDNd->ChgFormatColl( pCNd->GetFormatColl() );
+ }
+ }
+
+ // note conditional template
+ pBox->GetSttNd()->CheckSectionCondColl();
+ }
+ }
+}
+
+/**
+ * Splits a Table in the top-level Line which contains the Index.
+ * All succeeding top-level Lines go into a new Table/Node.
+ *
+ * @param bCalcNewSize true
+ * Calculate the new Size for both from the
+ * Boxes' Max; but only if Size is using absolute
+ * values (USHRT_MAX)
+ */
+void SwDoc::SplitTable( const SwPosition& rPos, SplitTable_HeadlineOption eHdlnMode,
+ bool bCalcNewSize )
+{
+ SwNode* pNd = &rPos.nNode.GetNode();
+ SwTableNode* pTNd = pNd->FindTableNode();
+ if( !pTNd || pNd->IsTableNode() )
+ return;
+
+ if( dynamic_cast<const SwDDETable*>( &pTNd->GetTable() ) != nullptr)
+ return;
+
+ SwTable& rTable = pTNd->GetTable();
+ rTable.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // Delete HTML Layout
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+
+ SwHistory aHistory;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ aMsgHint.m_pHistory = &aHistory;
+ }
+
+ {
+ SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
+
+ // Find top-level Line
+ SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
+ if( pBox )
+ {
+ SwTableLine* pLine = pBox->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // pLine contains the top-level Line now
+ aMsgHint.m_nSplitLine = rTable.GetTabLines().GetPos( pLine );
+ }
+
+ OUString sNewTableNm( GetUniqueTableName() );
+ aMsgHint.m_aData.pNewTableNm = &sNewTableNm;
+ aMsgHint.m_eFlags = TBL_SPLITTBL;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ }
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rTable );
+ aFndBox.DelFrames( rTable );
+
+ SwTableNode* pNew = GetNodes().SplitTable( rPos.nNode, false, bCalcNewSize );
+
+ if( pNew )
+ {
+ std::unique_ptr<SwSaveRowSpan> pSaveRowSp = pNew->GetTable().CleanUpTopRowSpan( rTable.GetTabLines().size() );
+ SwUndoSplitTable* pUndo = nullptr;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoSplitTable(
+ *pNew, std::move(pSaveRowSp), eHdlnMode, bCalcNewSize);
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ if( aHistory.Count() )
+ pUndo->SaveFormula( aHistory );
+ }
+
+ switch( eHdlnMode )
+ {
+ // Set the lower Border of the preceding Line to
+ // the upper Border of the current one
+ case SplitTable_HeadlineOption::BorderCopy:
+ {
+ SwCollectTableLineBoxes aPara( false, eHdlnMode );
+ SwTableLine* pLn = rTable.GetTabLines()[
+ rTable.GetTabLines().size() - 1 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, &aPara );
+
+ aPara.SetValues( true );
+ pLn = pNew->GetTable().GetTabLines()[ 0 ];
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aPara );
+
+ // Switch off repeating Header
+ pNew->GetTable().SetRowsToRepeat( 0 );
+ }
+ break;
+
+ // Take over the Attributes of the first Line to the new one
+ case SplitTable_HeadlineOption::BoxAttrCopy:
+ case SplitTable_HeadlineOption::BoxAttrAllCopy:
+ {
+ SwHistory* pHst = nullptr;
+ if( SplitTable_HeadlineOption::BoxAttrAllCopy == eHdlnMode && pUndo )
+ pHst = pUndo->GetHistory();
+
+ SwCollectTableLineBoxes aPara( true, eHdlnMode, pHst );
+ SwTableLine* pLn = rTable.GetTabLines()[ 0 ];
+ for( const auto& rpBox : pLn->GetTabBoxes() )
+ sw_Box_CollectBox(rpBox, &aPara );
+
+ aPara.SetValues( true );
+ pLn = pNew->GetTable().GetTabLines()[ 0 ];
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ sw_BoxSetSplitBoxFormats(rpBox, &aPara );
+ }
+ break;
+
+ case SplitTable_HeadlineOption::ContentCopy:
+ rTable.CopyHeadlineIntoTable( *pNew );
+ if( pUndo )
+ pUndo->SetTableNodeOffset( pNew->GetIndex() );
+ break;
+
+ case SplitTable_HeadlineOption::NONE:
+ // Switch off repeating the Header
+ pNew->GetTable().SetRowsToRepeat( 0 );
+ break;
+ }
+
+ // And insert Frames
+ SwNodeIndex aNdIdx( *pNew->EndOfSectionNode() );
+ GetNodes().GoNext( &aNdIdx ); // To the next ContentNode
+ pNew->MakeOwnFrames( &aNdIdx );
+
+ // Insert a paragraph between the Table
+ GetNodes().MakeTextNode( SwNodeIndex( *pNew ),
+ getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+ }
+
+ // Update Layout
+ aFndBox.MakeFrames( rTable );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ UpdateCharts( rTable.GetFrameFormat()->GetName() );
+
+ // update table style formatting of both the tables
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ {
+ pFEShell->UpdateTableStyleFormatting(pTNd);
+ pFEShell->UpdateTableStyleFormatting(pNew);
+ }
+
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+}
+
+static bool lcl_ChgTableSize( SwTable& rTable )
+{
+ // The Attribute must not be set via the Modify or else all Boxes are
+ // set back to 0.
+ // So lock the Format.
+ SwFrameFormat* pFormat = rTable.GetFrameFormat();
+ SwFormatFrameSize aTableMaxSz( pFormat->GetFrameSize() );
+
+ if( USHRT_MAX == aTableMaxSz.GetWidth() )
+ return false;
+
+ bool bLocked = pFormat->IsModifyLocked();
+ pFormat->LockModify();
+
+ aTableMaxSz.SetWidth( 0 );
+
+ SwTableLines& rLns = rTable.GetTabLines();
+ for( auto pLn : rLns )
+ {
+ SwTwips nMaxLnWidth = 0;
+ SwTableBoxes& rBoxes = pLn->GetTabBoxes();
+ for( auto pBox : rBoxes )
+ nMaxLnWidth += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+
+ if( nMaxLnWidth > aTableMaxSz.GetWidth() )
+ aTableMaxSz.SetWidth( nMaxLnWidth );
+ }
+ pFormat->SetFormatAttr( aTableMaxSz );
+ if( !bLocked ) // Release the Lock if appropriate
+ pFormat->UnlockModify();
+
+ return true;
+}
+
+namespace {
+
+class SplitTable_Para
+{
+ std::map<SwFrameFormat const*, SwFrameFormat*> m_aSrcDestMap;
+ SwTableNode* m_pNewTableNode;
+ SwTable& m_rOldTable;
+
+public:
+ SplitTable_Para(SwTableNode* pNew, SwTable& rOld)
+ : m_pNewTableNode(pNew)
+ , m_rOldTable(rOld)
+ {}
+ SwFrameFormat* GetDestFormat( SwFrameFormat* pSrcFormat ) const
+ {
+ auto it = m_aSrcDestMap.find(pSrcFormat);
+ return it == m_aSrcDestMap.end() ? nullptr : it->second;
+ }
+
+ void InsertSrcDest( SwFrameFormat const * pSrcFormat, SwFrameFormat* pDestFormat )
+ {
+ m_aSrcDestMap[pSrcFormat] = pDestFormat;
+ }
+
+ void ChgBox( SwTableBox* pBox )
+ {
+ m_rOldTable.GetTabSortBoxes().erase(pBox);
+ m_pNewTableNode->GetTable().GetTabSortBoxes().insert(pBox);
+ }
+};
+
+}
+
+static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara );
+
+static void lcl_SplitTable_CpyLine( SwTableLine* pLn, SplitTable_Para* pPara )
+{
+ SwFrameFormat *pSrcFormat = pLn->GetFrameFormat();
+ SwTableLineFormat* pDestFormat = static_cast<SwTableLineFormat*>( pPara->GetDestFormat( pSrcFormat ) );
+ if( pDestFormat == nullptr )
+ {
+ pPara->InsertSrcDest( pSrcFormat, pLn->ClaimFrameFormat() );
+ }
+ else
+ pLn->ChgFrameFormat( pDestFormat );
+
+ for( auto& rpBox : pLn->GetTabBoxes() )
+ lcl_SplitTable_CpyBox(rpBox, pPara );
+}
+
+static void lcl_SplitTable_CpyBox( SwTableBox* pBox, SplitTable_Para* pPara )
+{
+ SwFrameFormat *pSrcFormat = pBox->GetFrameFormat();
+ SwTableBoxFormat* pDestFormat = static_cast<SwTableBoxFormat*>(pPara->GetDestFormat( pSrcFormat ));
+ if( pDestFormat == nullptr )
+ {
+ pPara->InsertSrcDest( pSrcFormat, pBox->ClaimFrameFormat() );
+ }
+ else
+ pBox->ChgFrameFormat( pDestFormat );
+
+ if( pBox->GetSttNd() )
+ pPara->ChgBox( pBox );
+ else
+ for( SwTableLine* pLine : pBox->GetTabLines() )
+ lcl_SplitTable_CpyLine( pLine, pPara );
+}
+
+SwTableNode* SwNodes::SplitTable( const SwNodeIndex& rPos, bool bAfter,
+ bool bCalcNewSize )
+{
+ SwNode* pNd = &rPos.GetNode();
+ SwTableNode* pTNd = pNd->FindTableNode();
+ if( !pTNd || pNd->IsTableNode() )
+ return nullptr;
+
+ SwNodeOffset nSttIdx = pNd->FindTableBoxStartNode()->GetIndex();
+
+ // Find this Box/top-level line
+ SwTable& rTable = pTNd->GetTable();
+ SwTableBox* pBox = rTable.GetTableBox( nSttIdx );
+ if( !pBox )
+ return nullptr;
+
+ SwTableLine* pLine = pBox->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // pLine now contains the top-level line
+ sal_uInt16 nLinePos = rTable.GetTabLines().GetPos( pLine );
+ if( USHRT_MAX == nLinePos ||
+ ( bAfter ? ++nLinePos >= rTable.GetTabLines().size() : !nLinePos ))
+ return nullptr; // Not found or last Line!
+
+ // Find the first Box of the succeeding Line
+ SwTableLine* pNextLine = rTable.GetTabLines()[ nLinePos ];
+ pBox = pNextLine->GetTabBoxes()[0];
+ while( !pBox->GetSttNd() )
+ pBox = pBox->GetTabLines()[0]->GetTabBoxes()[0];
+
+ // Insert an EndNode and TableNode into the Nodes Array
+ SwTableNode * pNewTableNd;
+ {
+ SwEndNode* pOldTableEndNd = pTNd->EndOfSectionNode()->GetEndNode();
+ assert(pOldTableEndNd && "Where is the EndNode?");
+
+ SwNodeIndex aIdx( *pBox->GetSttNd() );
+ new SwEndNode( aIdx, *pTNd );
+ pNewTableNd = new SwTableNode( aIdx );
+ pNewTableNd->GetTable().SetTableModel( rTable.IsNewModel() );
+
+ pOldTableEndNd->m_pStartOfSection = pNewTableNd;
+ pNewTableNd->m_pEndOfSection = pOldTableEndNd;
+
+ SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
+ do {
+ OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
+ pBoxNd->m_pStartOfSection = pNewTableNd;
+ pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
+ } while( pBoxNd != pOldTableEndNd );
+ }
+
+ {
+ // Move the Lines
+ SwTable& rNewTable = pNewTableNd->GetTable();
+ rNewTable.GetTabLines().insert( rNewTable.GetTabLines().begin(),
+ rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
+
+ /* From the back (bottom right) to the front (top left) deregister all Boxes from the
+ Chart Data Provider. The Modify event is triggered in the calling function.
+ TL_CHART2: */
+ SwChartDataProvider *pPCD = rTable.GetFrameFormat()->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if( pPCD )
+ {
+ for (SwTableLines::size_type k = nLinePos; k < rTable.GetTabLines().size(); ++k)
+ {
+ const SwTableLines::size_type nLineIdx = (rTable.GetTabLines().size() - 1) - k + nLinePos;
+ const SwTableBoxes::size_type nBoxCnt = rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes().size();
+ for (SwTableBoxes::size_type j = 0; j < nBoxCnt; ++j)
+ {
+ const SwTableBoxes::size_type nIdx = nBoxCnt - 1 - j;
+ pPCD->DeleteBox( &rTable, *rTable.GetTabLines()[ nLineIdx ]->GetTabBoxes()[nIdx] );
+ }
+ }
+ }
+
+ // Delete
+ sal_uInt16 nDeleted = rTable.GetTabLines().size() - nLinePos;
+ rTable.GetTabLines().erase( rTable.GetTabLines().begin() + nLinePos, rTable.GetTabLines().end() );
+
+ // Move the affected Boxes. Make the Formats unique and correct the StartNodes
+ SplitTable_Para aPara( pNewTableNd, rTable );
+ for( SwTableLine* pNewLine : rNewTable.GetTabLines() )
+ lcl_SplitTable_CpyLine( pNewLine, &aPara );
+ rTable.CleanUpBottomRowSpan( nDeleted );
+ }
+
+ {
+ // Copy the Table FrameFormat
+ SwFrameFormat* pOldTableFormat = rTable.GetFrameFormat();
+ SwFrameFormat* pNewTableFormat = pOldTableFormat->GetDoc()->MakeTableFrameFormat(
+ pOldTableFormat->GetDoc()->GetUniqueTableName(),
+ pOldTableFormat->GetDoc()->GetDfltFrameFormat() );
+
+ *pNewTableFormat = *pOldTableFormat;
+ pNewTableNd->GetTable().RegisterToFormat( *pNewTableFormat );
+
+ pNewTableNd->GetTable().SetTableStyleName(rTable.GetTableStyleName());
+
+ // Calculate a new Size?
+ // lcl_ChgTableSize: Only execute the second call if the first call was
+ // successful, thus has an absolute Size
+ if( bCalcNewSize && lcl_ChgTableSize( rTable ) )
+ lcl_ChgTableSize( pNewTableNd->GetTable() );
+ }
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ rTable.UpdateCharts();
+
+ return pNewTableNd; // That's it!
+}
+
+/**
+ * rPos needs to be in the Table that remains
+ *
+ * @param bWithPrev merge the current Table with the preceding
+ * or succeeding one
+ */
+bool SwDoc::MergeTable( const SwPosition& rPos, bool bWithPrev, sal_uInt16 nMode )
+{
+ SwTableNode* pTableNd = rPos.nNode.GetNode().FindTableNode(), *pDelTableNd;
+ if( !pTableNd )
+ return false;
+
+ SwNodes& rNds = GetNodes();
+ if( bWithPrev )
+ pDelTableNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
+ else
+ pDelTableNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
+ if( !pDelTableNd )
+ return false;
+
+ if( dynamic_cast<const SwDDETable*>( &pTableNd->GetTable() ) != nullptr ||
+ dynamic_cast<const SwDDETable*>( &pDelTableNd->GetTable() ) != nullptr)
+ return false;
+
+ // Delete HTML Layout
+ pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+ pDelTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+
+ // Both Tables are present; we can start
+ SwUndoMergeTable* pUndo = nullptr;
+ std::unique_ptr<SwHistory> pHistory;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ pUndo = new SwUndoMergeTable( *pTableNd, *pDelTableNd, bWithPrev, nMode );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ pHistory.reset(new SwHistory);
+ }
+
+ // Adapt all "TableFormulas"
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_aData.pDelTable = &pDelTableNd->GetTable();
+ aMsgHint.m_eFlags = TBL_MERGETBL;
+ aMsgHint.m_pHistory = pHistory.get();
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // The actual merge
+ SwNodeIndex aIdx( bWithPrev ? *pTableNd : *pDelTableNd );
+ bool bRet = rNds.MergeTable( aIdx, !bWithPrev, nMode );
+
+ if( pHistory )
+ {
+ if( pHistory->Count() )
+ pUndo->SaveFormula( *pHistory );
+ pHistory.reset();
+ }
+ if( bRet )
+ {
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting();
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ return bRet;
+}
+
+bool SwNodes::MergeTable( const SwNodeIndex& rPos, bool bWithPrev,
+ sal_uInt16 nMode )
+{
+ SwTableNode* pDelTableNd = rPos.GetNode().GetTableNode();
+ OSL_ENSURE( pDelTableNd, "Where did the TableNode go?" );
+
+ SwTableNode* pTableNd = (*this)[ rPos.GetIndex() - 1]->FindTableNode();
+ OSL_ENSURE( pTableNd, "Where did the TableNode go?" );
+
+ if( !pDelTableNd || !pTableNd )
+ return false;
+
+ pDelTableNd->DelFrames();
+
+ SwTable& rDelTable = pDelTableNd->GetTable();
+ SwTable& rTable = pTableNd->GetTable();
+
+ // Find Lines for the Layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rTable );
+ aFndBox.DelFrames( rTable );
+
+ // TL_CHART2:
+ // tell the charts about the table to be deleted and have them use their own data
+ GetDoc().getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &rDelTable );
+
+ // Sync the TableFormat's Width
+ {
+ const SwFormatFrameSize& rTableSz = rTable.GetFrameFormat()->GetFrameSize();
+ const SwFormatFrameSize& rDelTableSz = rDelTable.GetFrameFormat()->GetFrameSize();
+ if( rTableSz != rDelTableSz )
+ {
+ // The needs correction
+ if( bWithPrev )
+ rDelTable.GetFrameFormat()->SetFormatAttr( rTableSz );
+ else
+ rTable.GetFrameFormat()->SetFormatAttr( rDelTableSz );
+ }
+ }
+
+ if( !bWithPrev )
+ {
+ // Transfer all Attributes of the succeeding Table to the preceding one
+ // We do this, because the succeeding one is deleted when deleting the Node
+ rTable.SetRowsToRepeat( rDelTable.GetRowsToRepeat() );
+ rTable.SetTableChgMode( rDelTable.GetTableChgMode() );
+
+ rTable.GetFrameFormat()->LockModify();
+ *rTable.GetFrameFormat() = *rDelTable.GetFrameFormat();
+ // Also switch the Name
+ rTable.GetFrameFormat()->SetName( rDelTable.GetFrameFormat()->GetName() );
+ rTable.GetFrameFormat()->UnlockModify();
+ }
+
+ // Move the Lines and Boxes
+ SwTableLines::size_type nOldSize = rTable.GetTabLines().size();
+ rTable.GetTabLines().insert( rTable.GetTabLines().begin() + nOldSize,
+ rDelTable.GetTabLines().begin(), rDelTable.GetTabLines().end() );
+ rDelTable.GetTabLines().clear();
+
+ rTable.GetTabSortBoxes().insert( rDelTable.GetTabSortBoxes() );
+ rDelTable.GetTabSortBoxes().clear();
+
+ // The preceding Table always remains, while the succeeding one is deleted
+ SwEndNode* pTableEndNd = pDelTableNd->EndOfSectionNode();
+ pTableNd->m_pEndOfSection = pTableEndNd;
+
+ SwNodeIndex aIdx( *pDelTableNd, 1 );
+
+ SwNode* pBoxNd = aIdx.GetNode().GetStartNode();
+ do {
+ OSL_ENSURE( pBoxNd->IsStartNode(), "This needs to be a StartNode!" );
+ pBoxNd->m_pStartOfSection = pTableNd;
+ pBoxNd = (*this)[ pBoxNd->EndOfSectionIndex() + 1 ];
+ } while( pBoxNd != pTableEndNd );
+ pBoxNd->m_pStartOfSection = pTableNd;
+
+ aIdx -= SwNodeOffset(2);
+ DelNodes( aIdx, SwNodeOffset(2) );
+
+ // tweak the conditional styles at the first inserted Line
+ const SwTableLine* pFirstLn = rTable.GetTabLines()[ nOldSize ];
+ if( 1 == nMode )
+ {
+ // Set Header Template in the Line and save in the History
+ // if needed for Undo!
+ }
+ sw_LineSetHeadCondColl( pFirstLn );
+
+ // Clean up the Borders
+ if( nOldSize )
+ {
+ SwGCLineBorder aPara( rTable );
+ aPara.nLinePos = --nOldSize;
+ pFirstLn = rTable.GetTabLines()[ nOldSize ];
+ sw_GC_Line_Border( pFirstLn, &aPara );
+ }
+
+ // Update Layout
+ aFndBox.MakeFrames( rTable );
+
+ return true;
+}
+
+namespace {
+
+// Use the PtrArray's ForEach method
+struct SetAFormatTabPara
+{
+ SwTableAutoFormat& rTableFormat;
+ SwUndoTableAutoFormat* pUndo;
+ sal_uInt16 nEndBox, nCurBox;
+ sal_uInt8 nAFormatLine, nAFormatBox;
+ bool bSingleRowTable;
+
+ explicit SetAFormatTabPara( const SwTableAutoFormat& rNew )
+ : rTableFormat( const_cast<SwTableAutoFormat&>(rNew) ), pUndo( nullptr ),
+ nEndBox( 0 ), nCurBox( 0 ), nAFormatLine( 0 ), nAFormatBox( 0 ), bSingleRowTable(false)
+ {}
+};
+
+}
+
+// Forward declare so that the Lines and Boxes can use recursion
+static bool lcl_SetAFormatBox(FndBox_ &, SetAFormatTabPara *pSetPara, bool bResetDirect);
+static bool lcl_SetAFormatLine(FndLine_ &, SetAFormatTabPara *pPara, bool bResetDirect);
+
+static bool lcl_SetAFormatLine(FndLine_ & rLine, SetAFormatTabPara *pPara, bool bResetDirect)
+{
+ for (auto const& it : rLine.GetBoxes())
+ {
+ lcl_SetAFormatBox(*it, pPara, bResetDirect);
+ }
+ return true;
+}
+
+static bool lcl_SetAFormatBox(FndBox_ & rBox, SetAFormatTabPara *pSetPara, bool bResetDirect)
+{
+ if (!rBox.GetUpper()->GetUpper()) // Box on first level?
+ {
+ if( !pSetPara->nCurBox )
+ pSetPara->nAFormatBox = 0;
+ else if( pSetPara->nCurBox == pSetPara->nEndBox )
+ pSetPara->nAFormatBox = 3;
+ else //Even column(1) or Odd column(2)
+ pSetPara->nAFormatBox = static_cast<sal_uInt8>(1 + ((pSetPara->nCurBox-1) & 1));
+ }
+
+ if (rBox.GetBox()->GetSttNd())
+ {
+ SwTableBox* pSetBox = rBox.GetBox();
+ if (!pSetBox->HasDirectFormatting() || bResetDirect)
+ {
+ if (bResetDirect)
+ pSetBox->SetDirectFormatting(false);
+
+ SwDoc* pDoc = pSetBox->GetFrameFormat()->GetDoc();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_END-1> aCharSet(pDoc->GetAttrPool());
+ SfxItemSet aBoxSet(pDoc->GetAttrPool(), aTableBoxSetRange);
+ sal_uInt8 nPos = pSetPara->nAFormatLine * 4 + pSetPara->nAFormatBox;
+ const bool bSingleRowTable = pSetPara->bSingleRowTable;
+ const bool bSingleColTable = pSetPara->nEndBox == 0;
+ pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aCharSet, SwTableAutoFormatUpdateFlags::Char, nullptr);
+ pSetPara->rTableFormat.UpdateToSet(nPos, bSingleRowTable, bSingleColTable, aBoxSet, SwTableAutoFormatUpdateFlags::Box, pDoc->GetNumberFormatter());
+
+ if (aCharSet.Count())
+ {
+ SwNodeOffset nSttNd = pSetBox->GetSttIdx()+1;
+ SwNodeOffset nEndNd = pSetBox->GetSttNd()->EndOfSectionIndex();
+ for (; nSttNd < nEndNd; ++nSttNd)
+ {
+ SwContentNode* pNd = pDoc->GetNodes()[ nSttNd ]->GetContentNode();
+ if (pNd)
+ pNd->SetAttr(aCharSet);
+ }
+ }
+
+ if (aBoxSet.Count())
+ {
+ if (pSetPara->pUndo && SfxItemState::SET == aBoxSet.GetItemState(RES_BOXATR_FORMAT))
+ pSetPara->pUndo->SaveBoxContent( *pSetBox );
+
+ pSetBox->ClaimFrameFormat()->SetFormatAttr(aBoxSet);
+ }
+ }
+ }
+ else
+ {
+ // Not sure how this situation can occur, but apparently we have some kind of table in table.
+ // I am guessing at how to best handle singlerow in this situation.
+ const bool bOrigSingleRowTable = pSetPara->bSingleRowTable;
+ pSetPara->bSingleRowTable = rBox.GetLines().size() == 1;
+ for (auto const& rpFndLine : rBox.GetLines())
+ {
+ lcl_SetAFormatLine(*rpFndLine, pSetPara, bResetDirect);
+ }
+ pSetPara->bSingleRowTable = bOrigSingleRowTable;
+ }
+
+ if (!rBox.GetUpper()->GetUpper()) // a BaseLine
+ ++pSetPara->nCurBox;
+ return true;
+}
+
+bool SwDoc::SetTableAutoFormat(const SwSelBoxes& rBoxes, const SwTableAutoFormat& rNew, bool bResetDirect, bool const isSetStyleName)
+{
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ SwTable &table = pTableNd->GetTable();
+ table.SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size())
+ {
+ pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ }
+
+ if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
+ pFndBox = pFndBox->GetUpper()->GetUpper();
+
+ // Disable Undo, but first store parameters
+ SwUndoTableAutoFormat* pUndo = nullptr;
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ if (bUndo)
+ {
+ pUndo = new SwUndoTableAutoFormat( *pTableNd, rNew );
+ GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo));
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ if (isSetStyleName)
+ { // tdf#98226 do this here where undo can record it
+ pTableNd->GetTable().SetTableStyleName(rNew.GetName());
+ }
+
+ rNew.RestoreTableProperties(table);
+
+ SetAFormatTabPara aPara( rNew );
+ FndLines_t& rFLns = pFndBox->GetLines();
+ aPara.bSingleRowTable = rFLns.size() == 1;
+
+ for (FndLines_t::size_type n = 0; n < rFLns.size(); ++n)
+ {
+ FndLine_* pLine = rFLns[n].get();
+
+ // Set Upper to 0 (thus simulate BaseLine)
+ FndBox_* pSaveBox = pLine->GetUpper();
+ pLine->SetUpper( nullptr );
+
+ if( !n )
+ aPara.nAFormatLine = 0;
+ else if (static_cast<size_t>(n+1) == rFLns.size())
+ aPara.nAFormatLine = 3;
+ else
+ aPara.nAFormatLine = static_cast<sal_uInt8>(1 + ((n-1) & 1 ));
+
+ aPara.nAFormatBox = 0;
+ aPara.nCurBox = 0;
+ aPara.nEndBox = pLine->GetBoxes().size()-1;
+ aPara.pUndo = pUndo;
+ for (auto const& it : pLine->GetBoxes())
+ {
+ lcl_SetAFormatBox(*it, &aPara, bResetDirect);
+ }
+
+ pLine->SetUpper( pSaveBox );
+ }
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ return true;
+}
+
+/**
+ * Find out who has the Attributes
+ */
+bool SwDoc::GetTableAutoFormat( const SwSelBoxes& rBoxes, SwTableAutoFormat& rGet )
+{
+ OSL_ENSURE( !rBoxes.empty(), "No valid Box list" );
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
+ if( !pTableNd )
+ return false;
+
+ // Find all Boxes/Lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( rBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+ if( aFndBox.GetLines().empty() )
+ return false;
+
+ // Store table properties
+ SwTable &table = pTableNd->GetTable();
+ rGet.StoreTableProperties(table);
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size())
+ {
+ pFndBox = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ }
+
+ if( pFndBox->GetLines().empty() ) // One too far? (only one sel. Box)
+ pFndBox = pFndBox->GetUpper()->GetUpper();
+
+ FndLines_t& rFLns = pFndBox->GetLines();
+
+ sal_uInt16 aLnArr[4];
+ aLnArr[0] = 0;
+ aLnArr[1] = 1 < rFLns.size() ? 1 : 0;
+ aLnArr[2] = 2 < rFLns.size() ? 2 : aLnArr[1];
+ aLnArr[3] = rFLns.size() - 1;
+
+ for( sal_uInt8 nLine = 0; nLine < 4; ++nLine )
+ {
+ FndLine_& rLine = *rFLns[ aLnArr[ nLine ] ];
+
+ sal_uInt16 aBoxArr[4];
+ aBoxArr[0] = 0;
+ aBoxArr[1] = 1 < rLine.GetBoxes().size() ? 1 : 0;
+ aBoxArr[2] = 2 < rLine.GetBoxes().size() ? 2 : aBoxArr[1];
+ aBoxArr[3] = rLine.GetBoxes().size() - 1;
+
+ for( sal_uInt8 nBox = 0; nBox < 4; ++nBox )
+ {
+ SwTableBox* pFBox = rLine.GetBoxes()[ aBoxArr[ nBox ] ]->GetBox();
+ // Always apply to the first ones
+ while( !pFBox->GetSttNd() )
+ pFBox = pFBox->GetTabLines()[0]->GetTabBoxes()[0];
+
+ sal_uInt8 nPos = nLine * 4 + nBox;
+ SwNodeIndex aIdx( *pFBox->GetSttNd(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = GetNodes().GoNext( &aIdx );
+
+ if( pCNd )
+ rGet.UpdateFromSet( nPos, pCNd->GetSwAttrSet(),
+ SwTableAutoFormatUpdateFlags::Char, nullptr );
+ rGet.UpdateFromSet( nPos, pFBox->GetFrameFormat()->GetAttrSet(),
+ SwTableAutoFormatUpdateFlags::Box,
+ GetNumberFormatter() );
+ }
+ }
+
+ return true;
+}
+
+SwTableAutoFormatTable& SwDoc::GetTableStyles()
+{
+ if (!m_pTableStyles)
+ {
+ m_pTableStyles.reset(new SwTableAutoFormatTable);
+ m_pTableStyles->Load();
+ }
+ return *m_pTableStyles;
+}
+
+OUString SwDoc::GetUniqueTableName() const
+{
+ if( IsInMailMerge())
+ {
+ OUString newName = "MailMergeTable"
+ + OStringToOUString( DateTimeToOString( DateTime( DateTime::SYSTEM )), RTL_TEXTENCODING_ASCII_US )
+ + OUString::number( mpTableFrameFormatTable->size() + 1 );
+ return newName;
+ }
+
+ const OUString aName(SwResId(STR_TABLE_DEFNAME));
+
+ const size_t nFlagSize = ( mpTableFrameFormatTable->size() / 8 ) + 2;
+
+ std::unique_ptr<sal_uInt8[]> pSetFlags( new sal_uInt8[ nFlagSize ] );
+ memset( pSetFlags.get(), 0, nFlagSize );
+
+ for( size_t n = 0; n < mpTableFrameFormatTable->size(); ++n )
+ {
+ const SwFrameFormat* pFormat = (*mpTableFrameFormatTable)[ n ];
+ if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
+ pFormat->GetName().startsWith( aName ) )
+ {
+ // Get number and set the Flag
+ const sal_Int32 nNmLen = aName.getLength();
+ size_t nNum = o3tl::toInt32(pFormat->GetName().subView( nNmLen ));
+ if( nNum-- && nNum < mpTableFrameFormatTable->size() )
+ pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
+ }
+ }
+
+ // All numbers are flagged properly, thus calculate the right number
+ size_t nNum = mpTableFrameFormatTable->size();
+ for( size_t n = 0; n < nFlagSize; ++n )
+ {
+ auto nTmp = pSetFlags[ n ];
+ if( nTmp != 0xFF )
+ {
+ // Calculate the number
+ nNum = n * 8;
+ while( nTmp & 1 )
+ {
+ ++nNum;
+ nTmp >>= 1;
+ }
+ break;
+ }
+ }
+
+ return aName + OUString::number( ++nNum );
+}
+
+SwTableFormat* SwDoc::FindTableFormatByName( const OUString& rName, bool bAll ) const
+{
+ const SwFormat* pRet = nullptr;
+ if( bAll )
+ pRet = mpTableFrameFormatTable->FindFormatByName( rName );
+ else
+ {
+ auto [it, itEnd] = mpTableFrameFormatTable->findRangeByName(rName);
+ // Only the ones set in the Doc
+ for( ; it != itEnd; ++it )
+ {
+ const SwFrameFormat* pFormat = *it;
+ if( !pFormat->IsDefault() && IsUsed( *pFormat ) &&
+ pFormat->GetName() == rName )
+ {
+ pRet = pFormat;
+ break;
+ }
+ }
+ }
+ return const_cast<SwTableFormat*>(static_cast<const SwTableFormat*>(pRet));
+}
+
+void SwDoc::SetColRowWidthHeight( SwTableBox& rCurrentBox, TableChgWidthHeightType eType,
+ SwTwips nAbsDiff, SwTwips nRelDiff )
+{
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rCurrentBox.GetSttNd()->FindTableNode());
+ std::unique_ptr<SwUndo> pUndo;
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ bool const bUndo(GetIDocumentUndoRedo().DoesUndo());
+ bool bRet = false;
+ switch( extractPosition(eType) )
+ {
+ case TableChgWidthHeightType::ColLeft:
+ case TableChgWidthHeightType::ColRight:
+ case TableChgWidthHeightType::CellLeft:
+ case TableChgWidthHeightType::CellRight:
+ {
+ bRet = pTableNd->GetTable().SetColWidth( rCurrentBox,
+ eType, nAbsDiff, nRelDiff,
+ bUndo ? &pUndo : nullptr );
+ }
+ break;
+ case TableChgWidthHeightType::RowBottom:
+ case TableChgWidthHeightType::CellTop:
+ case TableChgWidthHeightType::CellBottom:
+ bRet = pTableNd->GetTable().SetRowHeight( rCurrentBox,
+ eType, nAbsDiff, nRelDiff,
+ bUndo ? &pUndo : nullptr );
+ break;
+ default: break;
+ }
+
+ GetIDocumentUndoRedo().DoUndo(bUndo); // SetColWidth can turn it off
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+
+ if( bRet )
+ {
+ getIDocumentState().SetModified();
+ }
+}
+
+bool SwDoc::IsNumberFormat( const OUString& rString, sal_uInt32& F_Index, double& fOutNumber )
+{
+ if( rString.getLength() > 308 ) // optimization matches svl:IsNumberFormat arbitrary value
+ return false;
+
+ // remove any comment anchor marks
+ OUStringBuffer sStringBuffer(rString);
+ sal_Int32 nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD );
+ while( nCommentPosition != -1 )
+ {
+ sStringBuffer.remove( nCommentPosition, 1 );
+ nCommentPosition = sStringBuffer.indexOf( CH_TXTATR_INWORD, nCommentPosition );
+ }
+
+ return GetNumberFormatter()->IsNumberFormat( sStringBuffer.makeStringAndClear(), F_Index, fOutNumber );
+}
+
+void SwDoc::ChkBoxNumFormat( SwTableBox& rBox, bool bCallUpdate )
+{
+ // Optimization: If the Box says it's Text, it remains Text
+ const SwTableBoxNumFormat* pNumFormatItem = rBox.GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT,
+ false );
+ if( pNumFormatItem && GetNumberFormatter()->IsTextFormat(pNumFormatItem->GetValue()) )
+ return ;
+
+ std::unique_ptr<SwUndoTableNumFormat> pUndo;
+
+ bool bIsEmptyTextNd;
+ bool bChgd = true;
+ sal_uInt32 nFormatIdx;
+ double fNumber;
+ if( rBox.HasNumContent( fNumber, nFormatIdx, bIsEmptyTextNd ) )
+ {
+ if( !rBox.IsNumberChanged() )
+ bChgd = false;
+ else
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
+ pUndo.reset(new SwUndoTableNumFormat( rBox ));
+ pUndo->SetNumFormat( nFormatIdx, fNumber );
+ }
+
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( GetAttrPool() );
+
+ bool bLockModify = true;
+ bool bSetNumberFormat = IsInsTableFormatNum();
+ const bool bForceNumberFormat = IsInsTableFormatNum() && IsInsTableChangeNumFormat();
+
+ // if the user forced a number format in this cell previously,
+ // keep it, unless the user set that she wants the full number
+ // format recognition
+ if( pNumFormatItem && !bForceNumberFormat )
+ {
+ sal_uLong nOldNumFormat = pNumFormatItem->GetValue();
+ SvNumberFormatter* pNumFormatr = GetNumberFormatter();
+
+ SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIdx );
+ if( nFormatType == pNumFormatr->GetType( nOldNumFormat ) || SvNumFormatType::NUMBER == nFormatType )
+ {
+ // Current and specified NumFormat match
+ // -> keep old Format
+ nFormatIdx = nOldNumFormat;
+ bSetNumberFormat = true;
+ }
+ else
+ {
+ // Current and specified NumFormat do not match
+ // -> insert as Text
+ bLockModify = bSetNumberFormat = false;
+ }
+ }
+
+ if( bSetNumberFormat || bForceNumberFormat )
+ {
+ pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
+
+ aBoxSet.Put( SwTableBoxValue( fNumber ));
+ aBoxSet.Put( SwTableBoxNumFormat( nFormatIdx ));
+ }
+
+ // It's not enough to only reset the Formula.
+ // Make sure that the Text is formatted accordingly
+ if( !bSetNumberFormat && !bIsEmptyTextNd && pNumFormatItem )
+ {
+ // Just resetting Attributes is not enough
+ // Make sure that the Text is formatted accordingly
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
+ }
+
+ if( bLockModify ) pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ if( bLockModify ) pBoxFormat->UnlockModify();
+
+ if( bSetNumberFormat )
+ pBoxFormat->SetFormatAttr( aBoxSet );
+ }
+ }
+ else
+ {
+ // It's not a number
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.GetFrameFormat());
+ if( SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_FORMAT, false ) ||
+ SfxItemState::SET == pBoxFormat->GetItemState( RES_BOXATR_VALUE, false ) )
+ {
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().StartUndo( SwUndoId::TABLE_AUTOFMT, nullptr );
+ pUndo.reset(new SwUndoTableNumFormat( rBox ));
+ }
+
+ pBoxFormat = static_cast<SwTableBoxFormat*>(rBox.ClaimFrameFormat());
+
+ // Remove all number formats
+ sal_uInt16 nWhich1 = RES_BOXATR_FORMULA;
+ if( !bIsEmptyTextNd )
+ {
+ nWhich1 = RES_BOXATR_FORMAT;
+
+ // Just resetting Attributes is not enough
+ // Make sure that the Text is formatted accordingly
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( nWhich1 ));
+ }
+ pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
+ }
+ else
+ bChgd = false;
+ }
+
+ if( !bChgd )
+ return;
+
+ if( pUndo )
+ {
+ pUndo->SetBox( rBox );
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+
+ const SwTableNode* pTableNd = rBox.GetSttNd()->FindTableNode();
+ if( bCallUpdate )
+ {
+ SwTableFormulaUpdate aTableUpdate( &pTableNd->GetTable() );
+ getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+
+ // TL_CHART2: update charts (when cursor leaves cell and
+ // automatic update is enabled)
+ if (AUTOUPD_FIELD_AND_CHARTS == GetDocumentSettingManager().getFieldUpdateFlags(true))
+ pTableNd->GetTable().UpdateCharts();
+ }
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::SetTableBoxFormulaAttrs( SwTableBox& rBox, const SfxItemSet& rSet )
+{
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoTableNumFormat>(rBox, &rSet) );
+ }
+
+ SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
+ if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA ))
+ {
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
+ pBoxFormat->UnlockModify();
+ }
+ else if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE ))
+ {
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ pBoxFormat->UnlockModify();
+ }
+ pBoxFormat->SetFormatAttr( rSet );
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::ClearLineNumAttrs( SwPosition const & rPos )
+{
+ SwPaM aPam(rPos);
+ aPam.Move(fnMoveBackward);
+ SwContentNode *pNode = aPam.GetContentNode();
+ if ( nullptr == pNode )
+ return ;
+ if( !pNode->IsTextNode() )
+ return;
+
+ SwTextNode * pTextNode = pNode->GetTextNode();
+ if (!(pTextNode && pTextNode->IsNumbered()
+ && pTextNode->GetText().isEmpty()))
+ return;
+
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PARATR_END - 1>
+ rSet( pTextNode->GetDoc().GetAttrPool() );
+ pTextNode->SwContentNode::GetAttr( rSet );
+ const SfxStringItem* pFormatItem = rSet.GetItemIfSet( RES_PARATR_NUMRULE, false );
+ if ( !pFormatItem )
+ return;
+
+ SwUndoDelNum * pUndo;
+ if( GetIDocumentUndoRedo().DoesUndo() )
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo = new SwUndoDelNum( aPam );
+ GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) );
+ }
+ else
+ pUndo = nullptr;
+ SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : nullptr );
+ aRegH.RegisterInModify( pTextNode , *pTextNode );
+ if ( pUndo )
+ pUndo->AddNode( *pTextNode );
+ std::unique_ptr<SfxStringItem> pNewItem(pFormatItem->Clone());
+ pNewItem->SetValue(OUString());
+ rSet.Put( std::move(pNewItem) );
+ pTextNode->SetAttr( rSet );
+}
+
+void SwDoc::ClearBoxNumAttrs( const SwNodeIndex& rNode )
+{
+ SwStartNode* pSttNd = rNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
+ if( nullptr == pSttNd ||
+ SwNodeOffset(2) != pSttNd->EndOfSectionIndex() - pSttNd->GetIndex())
+ return;
+
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
+ GetTableBox( pSttNd->GetIndex() );
+
+ const SfxItemSet& rSet = pBox->GetFrameFormat()->GetAttrSet();
+ const SwTableBoxNumFormat* pFormatItem = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false );
+ if( !pFormatItem ||
+ SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA, false ) ||
+ SfxItemState::SET == rSet.GetItemState( RES_BOXATR_VALUE, false ))
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoTableNumFormat>(*pBox));
+ }
+
+ SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
+
+ // Keep TextFormats!
+ sal_uInt16 nWhich1 = RES_BOXATR_FORMAT;
+ if( pFormatItem && GetNumberFormatter()->IsTextFormat(
+ pFormatItem->GetValue() ))
+ nWhich1 = RES_BOXATR_FORMULA;
+ else
+ // Just resetting Attributes is not enough
+ // Make sure that the Text is formatted accordingly
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
+
+ pBoxFormat->ResetFormatAttr( nWhich1, RES_BOXATR_VALUE );
+ getIDocumentState().SetModified();
+}
+
+/**
+ * Copies a Table from the same or another Doc into itself
+ * We create a new Table or an existing one is filled with the Content.
+ * We either fill in the Content from a certain Box or a certain TableSelection
+ *
+ * This method is called by edglss.cxx/fecopy.cxx
+ */
+bool SwDoc::InsCopyOfTable( SwPosition& rInsPos, const SwSelBoxes& rBoxes,
+ const SwTable* pCpyTable, bool bCpyName, bool bCorrPos, const OUString& rStyleName )
+{
+ bool bRet;
+
+ const SwTableNode* pSrcTableNd = pCpyTable
+ ? pCpyTable->GetTableNode()
+ : rBoxes[ 0 ]->GetSttNd()->FindTableNode();
+
+ SwTableNode * pInsTableNd = rInsPos.nNode.GetNode().FindTableNode();
+
+ bool const bUndo( GetIDocumentUndoRedo().DoesUndo() );
+ if( !pCpyTable && !pInsTableNd )
+ {
+ std::unique_ptr<SwUndoCpyTable> pUndo;
+ if (bUndo)
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoCpyTable(*this));
+ }
+
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ bRet = pSrcTableNd->GetTable().MakeCopy( *this, rInsPos, rBoxes,
+ bCpyName, rStyleName );
+ }
+
+ if( pUndo && bRet )
+ {
+ pInsTableNd = GetNodes()[ rInsPos.nNode.GetIndex() - 1 ]->FindTableNode();
+
+ pUndo->SetTableSttIdx( pInsTableNd->GetIndex() );
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ }
+ else
+ {
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ if( getIDocumentRedlineAccess().IsRedlineOn() )
+ getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::On |
+ RedlineFlags::ShowInsert |
+ RedlineFlags::ShowDelete );
+
+ std::unique_ptr<SwUndoTableCpyTable> pUndo;
+ if (bUndo)
+ {
+ GetIDocumentUndoRedo().ClearRedo();
+ pUndo.reset(new SwUndoTableCpyTable(*this));
+ GetIDocumentUndoRedo().DoUndo(false);
+ }
+
+ rtl::Reference<SwDoc> xCpyDoc(&const_cast<SwDoc&>(pSrcTableNd->GetDoc()));
+ bool bDelCpyDoc = xCpyDoc == this;
+
+ if( bDelCpyDoc )
+ {
+ // Copy the Table into a temporary Doc
+ xCpyDoc = new SwDoc;
+
+ SwPosition aPos( SwNodeIndex( xCpyDoc->GetNodes().GetEndOfContent() ));
+ if( !pSrcTableNd->GetTable().MakeCopy( *xCpyDoc, aPos, rBoxes, true ))
+ {
+ xCpyDoc.clear();
+
+ if( pUndo )
+ {
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+ return false;
+ }
+ aPos.nNode -= SwNodeOffset(1); // Set to the Table's EndNode
+ pSrcTableNd = aPos.nNode.GetNode().FindTableNode();
+ }
+
+ const SwStartNode* pSttNd = rInsPos.nNode.GetNode().FindTableBoxStartNode();
+
+ rInsPos.nContent.Assign( nullptr, 0 );
+
+ // no complex into complex, but copy into or from new model is welcome
+ if( ( !pSrcTableNd->GetTable().IsTableComplex() || pInsTableNd->GetTable().IsNewModel() )
+ && ( bDelCpyDoc || !rBoxes.empty() ) )
+ {
+ // Copy the Table "relatively"
+ const SwSelBoxes* pBoxes;
+ SwSelBoxes aBoxes;
+
+ if( bDelCpyDoc )
+ {
+ SwTableBox* pBox = pInsTableNd->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "Box is not in this Table" );
+ aBoxes.insert( pBox );
+ pBoxes = &aBoxes;
+ }
+ else
+ pBoxes = &rBoxes;
+
+ // Copy Table to the selected Lines
+ bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
+ *pBoxes, pUndo.get() );
+ }
+ else
+ {
+ SwNodeIndex aNdIdx( *pSttNd, 1 );
+ bRet = pInsTableNd->GetTable().InsTable( pSrcTableNd->GetTable(),
+ aNdIdx, pUndo.get() );
+ }
+
+ xCpyDoc.clear();
+
+ if( pUndo )
+ {
+ // If the Table could not be copied, delete the Undo object
+ GetIDocumentUndoRedo().DoUndo(bUndo);
+ if( bRet || !pUndo->IsEmpty() )
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::move(pUndo));
+ }
+ }
+
+ if( bCorrPos )
+ {
+ rInsPos.nNode = *pSttNd;
+ rInsPos.nContent.Assign( GetNodes().GoNext( &rInsPos.nNode ), 0 );
+ }
+ getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ }
+
+ if( bRet )
+ {
+ getIDocumentState().SetModified();
+ getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ }
+ return bRet;
+}
+
+bool SwDoc::UnProtectTableCells( SwTable& rTable )
+{
+ bool bChgd = false;
+ std::unique_ptr<SwUndoAttrTable> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoAttrTable( *rTable.GetTableNode() ));
+
+ SwTableSortBoxes& rSrtBox = rTable.GetTabSortBoxes();
+ for (size_t i = rSrtBox.size(); i; )
+ {
+ SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
+ if( pBoxFormat->GetProtect().IsContentProtected() )
+ {
+ pBoxFormat->ResetFormatAttr( RES_PROTECT );
+ bChgd = true;
+ }
+ }
+
+ if( pUndo && bChgd )
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ return bChgd;
+}
+
+void SwDoc::UnProtectCells( const OUString& rName )
+{
+ SwTableFormat* pFormat = FindTableFormatByName( rName );
+ if( pFormat )
+ {
+ bool bChgd = UnProtectTableCells( *SwTable::FindTable( pFormat ) );
+ if( bChgd )
+ getIDocumentState().SetModified();
+ }
+}
+
+bool SwDoc::UnProtectCells( const SwSelBoxes& rBoxes )
+{
+ bool bChgd = false;
+ if( !rBoxes.empty() )
+ {
+ std::unique_ptr<SwUndoAttrTable> pUndo;
+ if (GetIDocumentUndoRedo().DoesUndo())
+ pUndo.reset(new SwUndoAttrTable( *rBoxes[0]->GetSttNd()->FindTableNode() ));
+
+ std::map<SwFrameFormat*, SwTableBoxFormat*> aFormatsMap;
+ for (size_t i = rBoxes.size(); i; )
+ {
+ SwTableBox* pBox = rBoxes[ --i ];
+ SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
+ if( pBoxFormat->GetProtect().IsContentProtected() )
+ {
+ std::map<SwFrameFormat*, SwTableBoxFormat*>::const_iterator const it =
+ aFormatsMap.find(pBoxFormat);
+ if (aFormatsMap.end() != it)
+ pBox->ChgFrameFormat(it->second);
+ else
+ {
+ SwTableBoxFormat *const pNewBoxFormat(
+ static_cast<SwTableBoxFormat*>(pBox->ClaimFrameFormat()));
+ pNewBoxFormat->ResetFormatAttr( RES_PROTECT );
+ aFormatsMap.insert(std::make_pair(pBoxFormat, pNewBoxFormat));
+ }
+ bChgd = true;
+ }
+ }
+
+ if( pUndo && bChgd )
+ GetIDocumentUndoRedo().AppendUndo( std::move(pUndo) );
+ }
+ return bChgd;
+}
+
+void SwDoc::UnProtectTables( const SwPaM& rPam )
+{
+ GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ bool bChgd = false, bHasSel = rPam.HasMark() ||
+ rPam.GetNext() != &rPam;
+ SwFrameFormats& rFormats = *GetTableFrameFormats();
+ SwTable* pTable;
+ const SwTableNode* pTableNd;
+ for( auto n = rFormats.size(); n ; )
+ if( nullptr != (pTable = SwTable::FindTable( rFormats[ --n ] )) &&
+ nullptr != (pTableNd = pTable->GetTableNode() ) &&
+ pTableNd->GetNodes().IsDocNodes() )
+ {
+ SwNodeOffset nTableIdx = pTableNd->GetIndex();
+
+ // Check whether the Table is within the Selection
+ if( bHasSel )
+ {
+ bool bFound = false;
+ SwPaM* pTmp = const_cast<SwPaM*>(&rPam);
+ do {
+ const SwPosition *pStt = pTmp->Start(),
+ *pEnd = pTmp->End();
+ bFound = pStt->nNode.GetIndex() < nTableIdx &&
+ nTableIdx < pEnd->nNode.GetIndex();
+
+ } while( !bFound && &rPam != ( pTmp = pTmp->GetNext() ) );
+ if( !bFound )
+ continue; // Continue searching
+ }
+
+ // Lift the protection
+ bChgd |= UnProtectTableCells( *pTable );
+ }
+
+ GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ if( bChgd )
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::HasTableAnyProtection( const SwPosition* pPos,
+ const OUString* pTableName,
+ bool* pFullTableProtection )
+{
+ bool bHasProtection = false;
+ SwTable* pTable = nullptr;
+ if( pTableName )
+ pTable = SwTable::FindTable( FindTableFormatByName( *pTableName ) );
+ else if( pPos )
+ {
+ SwTableNode* pTableNd = pPos->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ pTable = &pTableNd->GetTable();
+ }
+
+ if( pTable )
+ {
+ SwTableSortBoxes& rSrtBox = pTable->GetTabSortBoxes();
+ for (size_t i = rSrtBox.size(); i; )
+ {
+ SwFrameFormat *pBoxFormat = rSrtBox[ --i ]->GetFrameFormat();
+ if( pBoxFormat->GetProtect().IsContentProtected() )
+ {
+ if( !bHasProtection )
+ {
+ bHasProtection = true;
+ if( !pFullTableProtection )
+ break;
+ *pFullTableProtection = true;
+ }
+ }
+ else if( bHasProtection && pFullTableProtection )
+ {
+ *pFullTableProtection = false;
+ break;
+ }
+ }
+ }
+ return bHasProtection;
+}
+
+SwTableAutoFormat* SwDoc::MakeTableStyle(const OUString& rName, bool bBroadcast)
+{
+ SwTableAutoFormat aTableFormat(rName);
+ GetTableStyles().AddAutoFormat(aTableFormat);
+ SwTableAutoFormat* pTableFormat = GetTableStyles().FindAutoFormat(rName);
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleMake>(rName, *this));
+ }
+
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetCreated);
+
+ return pTableFormat;
+}
+
+std::unique_ptr<SwTableAutoFormat> SwDoc::DelTableStyle(const OUString& rName, bool bBroadcast)
+{
+ if (bBroadcast)
+ BroadcastStyleOperation(rName, SfxStyleFamily::Table, SfxHintId::StyleSheetErased);
+
+ std::unique_ptr<SwTableAutoFormat> pReleasedFormat = GetTableStyles().ReleaseAutoFormat(rName);
+
+ std::vector<SwTable*> vAffectedTables;
+ if (pReleasedFormat)
+ {
+ size_t nTableCount = GetTableFrameFormatCount(true);
+ for (size_t i=0; i < nTableCount; ++i)
+ {
+ SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
+ SwTable* pTable = SwTable::FindTable(pFrameFormat);
+ if (pTable->GetTableStyleName() == pReleasedFormat->GetName())
+ {
+ pTable->SetTableStyleName("");
+ vAffectedTables.push_back(pTable);
+ }
+ }
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleDelete>(std::move(pReleasedFormat), std::move(vAffectedTables), *this));
+ }
+ }
+
+ return pReleasedFormat;
+}
+
+void SwDoc::ChgTableStyle(const OUString& rName, const SwTableAutoFormat& rNewFormat)
+{
+ SwTableAutoFormat* pFormat = GetTableStyles().FindAutoFormat(rName);
+ if (!pFormat)
+ return;
+
+ SwTableAutoFormat aOldFormat = *pFormat;
+ *pFormat = rNewFormat;
+ pFormat->SetName(rName);
+
+ size_t nTableCount = GetTableFrameFormatCount(true);
+ for (size_t i=0; i < nTableCount; ++i)
+ {
+ SwFrameFormat* pFrameFormat = &GetTableFrameFormat(i, true);
+ SwTable* pTable = SwTable::FindTable(pFrameFormat);
+ if (pTable->GetTableStyleName() == rName)
+ if (SwFEShell* pFEShell = GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTable->GetTableNode());
+ }
+
+ getIDocumentState().SetModified();
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoTableStyleUpdate>(*pFormat, aOldFormat, *this));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/ndtbl1.cxx b/sw/source/core/docnode/ndtbl1.cxx
new file mode 100644
index 000000000..fb1b59a40
--- /dev/null
+++ b/sw/source/core/docnode/ndtbl1.cxx
@@ -0,0 +1,1774 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fesh.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtrowsplt.hxx>
+#include <tabcol.hxx>
+#include <frmatr.hxx>
+#include <cellfrm.hxx>
+#include <tabfrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <svx/svxids.hrc>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <swcrsr.hxx>
+#include <viscrs.hxx>
+#include <swtable.hxx>
+#include <htmltbl.hxx>
+#include <tblsel.hxx>
+#include <swtblfmt.hxx>
+#include <ndindex.hxx>
+#include <undobj.hxx>
+#include <calbck.hxx>
+#include <UndoTable.hxx>
+#include <o3tl/enumrange.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <redline.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+// See swtable.cxx too
+#define COLFUZZY 20L
+
+static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; }
+
+namespace {
+
+// SwTableLine::ChgFrameFormat may delete old format which doesn't have writer listeners anymore.
+// This may invalidate my pointers, and lead to use-after-free. For this reason, I register myself
+// as a writer listener for the old format here, and take care to delete formats without listeners
+// in my own dtor.
+class SwTableFormatCmp : public SwClient
+{
+public:
+ SwTableFormatCmp( SwFrameFormat *pOld, SwFrameFormat *pNew, sal_Int16 nType );
+ ~SwTableFormatCmp() override;
+
+ static SwFrameFormat* FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
+ SwFrameFormat const* pOld, sal_Int16 nType);
+
+private:
+ SwFrameFormat *m_pOld, *m_pNew;
+ sal_Int16 m_nType;
+};
+
+}
+
+SwTableFormatCmp::SwTableFormatCmp(SwFrameFormat* pO, SwFrameFormat* pN, sal_Int16 nT)
+ : m_pOld(pO)
+ , m_pNew(pN)
+ , m_nType(nT)
+{
+ if (m_pOld)
+ m_pOld->Add(this);
+}
+
+SwTableFormatCmp::~SwTableFormatCmp()
+{
+ if (m_pOld)
+ {
+ m_pOld->Remove(this);
+ if (!m_pOld->HasWriterListeners())
+ delete m_pOld;
+ }
+}
+
+// static
+SwFrameFormat* SwTableFormatCmp::FindNewFormat(std::vector<std::unique_ptr<SwTableFormatCmp>>& rArr,
+ SwFrameFormat const* pOld, sal_Int16 nType)
+{
+ for (const auto& pCmp : rArr)
+ {
+ if (pCmp->m_pOld == pOld && pCmp->m_nType == nType)
+ return pCmp->m_pNew;
+ }
+ return nullptr;
+}
+
+static void lcl_GetStartEndCell( const SwCursor& rCursor,
+ SwLayoutFrame *&prStart, SwLayoutFrame *&prEnd )
+{
+ OSL_ENSURE( rCursor.GetContentNode() && rCursor.GetContentNode( false ),
+ "Tab selection not at ContentNode" );
+
+ Point aPtPos, aMkPos;
+ const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor);
+ if( pShCursor )
+ {
+ aPtPos = pShCursor->GetPtPos();
+ aMkPos = pShCursor->GetMkPos();
+ }
+
+ // Robust:
+ SwContentNode* pPointNd = rCursor.GetContentNode();
+ SwContentNode* pMarkNd = rCursor.GetContentNode(false);
+
+ std::pair<Point, bool> tmp(aPtPos, true);
+ SwFrame *const pPointFrame = pPointNd ? pPointNd->getLayoutFrame(pPointNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ tmp.first = aMkPos;
+ SwFrame *const pMarkFrame = pMarkNd ? pMarkNd->getLayoutFrame(pMarkNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+
+ prStart = pPointFrame ? pPointFrame->GetUpper() : nullptr;
+ prEnd = pMarkFrame ? pMarkFrame->GetUpper() : nullptr;
+}
+
+static bool lcl_GetBoxSel( const SwCursor& rCursor, SwSelBoxes& rBoxes,
+ bool bAllCursor = false )
+{
+ const SwTableCursor* pTableCursor =
+ dynamic_cast<const SwTableCursor*>(&rCursor);
+ if( pTableCursor )
+ ::GetTableSelCrs( *pTableCursor, rBoxes );
+ else
+ {
+ const SwPaM *pCurPam = &rCursor, *pSttPam = pCurPam;
+ do {
+ const SwNode* pNd = pCurPam->GetNode().FindTableBoxStartNode();
+ if( pNd )
+ {
+ SwTableBox* pBox = const_cast<SwTableBox*>(pNd->FindTableNode()->GetTable().
+ GetTableBox( pNd->GetIndex() ));
+ rBoxes.insert( pBox );
+ }
+ } while( bAllCursor &&
+ pSttPam != ( pCurPam = pCurPam->GetNext()) );
+ }
+ return !rBoxes.empty();
+}
+
+static void InsertLine( std::vector<SwTableLine*>& rLineArr, SwTableLine* pLine )
+{
+ if( rLineArr.end() == std::find( rLineArr.begin(), rLineArr.end(), pLine ) )
+ rLineArr.push_back( pLine );
+}
+
+static bool lcl_IsAnLower( const SwTableLine *pLine, const SwTableLine *pAssumed )
+{
+ const SwTableLine *pTmp = pAssumed->GetUpper() ?
+ pAssumed->GetUpper()->GetUpper() : nullptr;
+ while ( pTmp )
+ {
+ if ( pTmp == pLine )
+ return true;
+ pTmp = pTmp->GetUpper() ? pTmp->GetUpper()->GetUpper() : nullptr;
+ }
+ return false;
+}
+
+namespace {
+
+struct LinesAndTable
+{
+ std::vector<SwTableLine*> &m_rLines;
+ const SwTable &m_rTable;
+ bool m_bInsertLines;
+
+ LinesAndTable(std::vector<SwTableLine*> &rL, const SwTable &rTable) :
+ m_rLines(rL), m_rTable(rTable), m_bInsertLines(true) {}
+};
+
+}
+
+static bool FindLine_( FndLine_ & rLine, LinesAndTable* pPara );
+
+static bool FindBox_( FndBox_ & rBox, LinesAndTable* pPara )
+{
+ if (!rBox.GetLines().empty())
+ {
+ pPara->m_bInsertLines = true;
+ for (auto const& rpFndLine : rBox.GetLines())
+ {
+ FindLine_(*rpFndLine, pPara);
+ }
+
+ if (pPara->m_bInsertLines)
+ {
+ const SwTableLines &rLines = (rBox.GetBox())
+ ? rBox.GetBox()->GetTabLines()
+ : pPara->m_rTable.GetTabLines();
+ if (rBox.GetLines().size() == rLines.size())
+ {
+ for ( auto pLine : rLines )
+ ::InsertLine(pPara->m_rLines, pLine);
+ }
+ else
+ pPara->m_bInsertLines = false;
+ }
+ }
+ else if (rBox.GetBox())
+ {
+ ::InsertLine(pPara->m_rLines, rBox.GetBox()->GetUpper());
+ }
+ return true;
+}
+
+bool FindLine_( FndLine_& rLine, LinesAndTable* pPara )
+{
+ for (auto const& it : rLine.GetBoxes())
+ {
+ FindBox_(*it, pPara);
+ }
+ return true;
+}
+
+static void lcl_CollectLines( std::vector<SwTableLine*> &rArr, const SwCursor& rCursor, bool bRemoveLines )
+{
+ // Collect the selected Boxes first
+ SwSelBoxes aBoxes;
+ if( !::lcl_GetBoxSel( rCursor, aBoxes ))
+ return ;
+
+ // Copy the selected structure
+ const SwTable &rTable = aBoxes[0]->GetSttNd()->FindTableNode()->GetTable();
+ LinesAndTable aPara( rArr, rTable );
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aTmpPara( aBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( const_cast<SwTableLines&>(rTable.GetTabLines()), &aTmpPara );
+ }
+
+ // Collect the Lines which only contain selected Boxes
+ ::FindBox_(aFndBox, &aPara);
+
+ // Remove lines, that have a common superordinate row.
+ // (Not for row split)
+ if ( !bRemoveLines )
+ return;
+
+ for ( std::vector<SwTableLine*>::size_type i = 0; i < rArr.size(); ++i )
+ {
+ SwTableLine *pUpLine = rArr[i];
+ for ( std::vector<SwTableLine*>::size_type k = 0; k < rArr.size(); ++k )
+ {
+ if ( k != i && ::lcl_IsAnLower( pUpLine, rArr[k] ) )
+ {
+ rArr.erase( rArr.begin() + k );
+ if ( k <= i )
+ --i;
+ --k;
+ }
+ }
+ }
+}
+
+static void lcl_ProcessRowAttr(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableLine* pLine, const SfxPoolItem& rNew)
+{
+ SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( rFormatCmp, pLine->GetFrameFormat(), 0 );
+ if ( nullptr != pNewFormat )
+ pLine->ChgFrameFormat( static_cast<SwTableLineFormat*>(pNewFormat) );
+ else
+ {
+ SwFrameFormat *pOld = pLine->GetFrameFormat();
+ SwFrameFormat *pNew = pLine->ClaimFrameFormat();
+ pNew->SetFormatAttr( rNew );
+ rFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
+ }
+}
+
+static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableBox* pBox, const SwFormatFrameSize& rNew);
+
+static void lcl_ProcessRowSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableLine* pLine, const SwFormatFrameSize& rNew)
+{
+ lcl_ProcessRowAttr( rFormatCmp, pLine, rNew );
+ SwTableBoxes &rBoxes = pLine->GetTabBoxes();
+ for ( auto pBox : rBoxes )
+ ::lcl_ProcessBoxSize( rFormatCmp, pBox, rNew );
+}
+
+static void lcl_ProcessBoxSize(std::vector<std::unique_ptr<SwTableFormatCmp>>& rFormatCmp,
+ SwTableBox* pBox, const SwFormatFrameSize& rNew)
+{
+ SwTableLines &rLines = pBox->GetTabLines();
+ if ( !rLines.empty() )
+ {
+ SwFormatFrameSize aSz( rNew );
+ aSz.SetHeight( rNew.GetHeight() ? rNew.GetHeight() / rLines.size() : 0 );
+ for ( auto pLine : rLines )
+ ::lcl_ProcessRowSize( rFormatCmp, pLine, aSz );
+ }
+}
+
+void SwDoc::SetRowSplit( const SwCursor& rCursor, const SwFormatRowSplit &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, false );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+
+ for( auto pLn : aRowArr )
+ ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
+
+ getIDocumentState().SetModified();
+}
+
+std::unique_ptr<SwFormatRowSplit> SwDoc::GetRowSplit( const SwCursor& rCursor )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return nullptr;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, false );
+
+ if( aRowArr.empty() )
+ return nullptr;
+
+ SwFormatRowSplit* pSz = &const_cast<SwFormatRowSplit&>(aRowArr[0]->GetFrameFormat()->GetRowSplit());
+
+ for ( auto pLn : aRowArr )
+ {
+ if ( pSz->GetValue() != pLn->GetFrameFormat()->GetRowSplit().GetValue() )
+ {
+ return nullptr;
+ }
+ }
+ return std::make_unique<SwFormatRowSplit>( *pSz );
+}
+
+/* Class: SwDoc
+ * Methods: SetRowHeight(), GetRowHeight()
+ *
+ * The line height is calculated from the Selection.
+ * Starting with every Cell within the Selection, all Cells are iterated
+ * through in an upwards fashion.
+ *
+ * The topmost Line gets the requested value, all Lines below it get
+ * a respective value that is calculated from the relation of the old and
+ * new size of the topmost Line in the lower line's own size.
+ *
+ * All changed Lines may get an own FrameFormat.
+ * Of course we can only touch every Line once.
+ */
+
+void SwDoc::SetRowHeight( const SwCursor& rCursor, const SwFormatFrameSize &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+ for ( auto pLn : aRowArr )
+ ::lcl_ProcessRowSize( aFormatCmp, pLn, rNew );
+
+ getIDocumentState().SetModified();
+}
+
+std::unique_ptr<SwFormatFrameSize> SwDoc::GetRowHeight( const SwCursor& rCursor )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return nullptr;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return nullptr;
+
+ SwFormatFrameSize* pSz = &const_cast<SwFormatFrameSize&>(aRowArr[0]->GetFrameFormat()->GetFrameSize());
+
+ for ( auto pLn : aRowArr )
+ {
+ if ( *pSz != pLn->GetFrameFormat()->GetFrameSize() )
+ return nullptr;
+ }
+ return std::make_unique<SwFormatFrameSize>( *pSz );
+}
+
+bool SwDoc::BalanceRowHeight( const SwCursor& rCursor, bool bTstOnly, const bool bOptimize )
+{
+ bool bRet = false;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( 1 < aRowArr.size() )
+ {
+ if( !bTstOnly )
+ {
+ tools::Long nHeight = 0;
+ sal_Int32 nTotalHeight = 0;
+ for ( auto pLn : aRowArr )
+ {
+ if (bOptimize)
+ nHeight = 0;
+ SwIterator<SwFrame,SwFormat> aIter( *pLn->GetFrameFormat() );
+ SwFrame* pFrame = aIter.First();
+ while ( pFrame )
+ {
+ nHeight = std::max( nHeight, pFrame->getFrameArea().Height() );
+ pFrame = aIter.Next();
+ }
+ nTotalHeight += nHeight;
+ }
+
+ if ( bOptimize )
+ nHeight = nTotalHeight / aRowArr.size();
+
+ SwFormatFrameSize aNew( SwFrameSize::Minimum, 0, nHeight );
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+ for( auto pLn : aRowArr )
+ ::lcl_ProcessRowSize( aFormatCmp, pLn, aNew );
+
+ getIDocumentState().SetModified();
+ }
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SwDoc::SetRowBackground( const SwCursor& rCursor, const SvxBrushItem &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+
+ for( auto pLn : aRowArr )
+ ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
+
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::GetRowBackground( const SwCursor& rCursor, std::unique_ptr<SvxBrushItem>& rToFill )
+{
+ bool bRet = false;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( !aRowArr.empty() )
+ {
+ rToFill = aRowArr[0]->GetFrameFormat()->makeBackgroundBrushItem();
+
+ bRet = true;
+ for ( std::vector<SwTableLine*>::size_type i = 1; i < aRowArr.size(); ++i )
+ {
+ std::unique_ptr<SvxBrushItem> aAlternative(aRowArr[i]->GetFrameFormat()->makeBackgroundBrushItem());
+
+ if ( *rToFill != *aAlternative )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+// has a table row, which is not a tracked deletion
+bool SwDoc::HasRowNotTracked( const SwCursor& rCursor )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return false;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return false;
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ SwDoc* pDoc = aRowArr[0]->GetFrameFormat()->GetDoc();
+ const IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+
+ for( auto pLn : aRowArr )
+ {
+ auto pHasTextChangesOnlyProp = pLn->GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
+ // there is a not tracked row in the table selection
+ return true;
+
+ // tdf#150666 examine tracked row: it's possible to delete a tracked insertion
+ SwRedlineTable::size_type nPos = pLn->UpdateTextChangesOnly(nRedlinePos);
+ if ( nPos != SwRedlineTable::npos )
+ {
+ const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
+ SwRangeRedline* pTmp = aRedlineTable[ nPos ];
+ if ( RedlineType::Insert == pTmp->GetType() )
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwDoc::SetRowNotTracked( const SwCursor& rCursor,
+ const SvxPrintItem &rNew, bool bAll, bool bIns )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ if( !pTableNd )
+ return;
+
+ std::vector<SwTableLine*> aRowArr; // For Lines collecting
+ if ( bAll )
+ {
+ const SwTableLines &rLines = pTableNd->GetTable().GetTabLines();
+ aRowArr.insert(aRowArr.end(), rLines.begin(), rLines.end());
+ }
+ else
+ ::lcl_CollectLines( aRowArr, rCursor, true );
+
+ if( aRowArr.empty() )
+ return;
+
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ bool bInsertDummy = !bAll && !bIns &&
+ // HasTextChangesOnly == false, i.e. a tracked row change (deletion, if bIns == false)
+ !rNew.GetValue();
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( std::max( 255, static_cast<int>(aRowArr.size()) ) );
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for( auto pLn : aRowArr )
+ {
+ // tdf#150666 deleting row insertion from the same author needs special handling,
+ // because removing redlines of the author can result an empty line,
+ // which doesn't contain any redline for the tracked row
+ bool bDeletionOfOwnRowInsertion = false;
+ if ( bInsertDummy )
+ {
+ SwRedlineTable::size_type nPos = pLn->UpdateTextChangesOnly(nRedlinePos);
+ if ( nPos != SwRedlineTable::npos )
+ {
+ SwDoc* pDoc = pLn->GetFrameFormat()->GetDoc();
+ IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+ const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable();
+ SwRangeRedline* pTmp = aRedlineTable[ nPos ];
+ if ( RedlineType::Insert == pTmp->GetType() &&
+ rIDRA.GetRedlineAuthor() == pTmp->GetRedlineData().GetAuthor() &&
+ pTmp->GetText()[0] == CH_TXT_TRACKED_DUMMY_CHAR )
+ {
+ bDeletionOfOwnRowInsertion = true;
+ }
+ }
+ }
+
+ ::lcl_ProcessRowAttr( aFormatCmp, pLn, rNew );
+ // as a workaround for the rows without text content,
+ // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
+ // (unless the table is part of a bigger deletion, where the
+ // new redline can cause a problem)
+ if ( bInsertDummy && (pLn->IsEmpty() || bDeletionOfOwnRowInsertion ) )
+ {
+ ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
+ SwNodeIndex aInsPos( *(pLn->GetTabBoxes()[0]->GetSttNd()), 1 );
+ RedlineFlags eOld = getIDocumentRedlineAccess().GetRedlineFlags();
+ getIDocumentRedlineAccess().SetRedlineFlags_intern(RedlineFlags::NONE);
+ SwPaM aPaM(aInsPos);
+ getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ aPaM.SetMark();
+ aPaM.GetMark()->nContent.Assign(aPaM.GetContentNode(), 0);
+ getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ getIDocumentContentOperations().DeleteAndJoin( aPaM );
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+static void InsertCell( std::vector<SwCellFrame*>& rCellArr, SwCellFrame* pCellFrame )
+{
+ if( rCellArr.end() == std::find( rCellArr.begin(), rCellArr.end(), pCellFrame ) )
+ rCellArr.push_back( pCellFrame );
+}
+
+static void lcl_CollectCells( std::vector<SwCellFrame*> &rArr, const SwRect &rUnion,
+ SwTabFrame *pTab )
+{
+ SwLayoutFrame *pCell = pTab->FirstCell();
+ do
+ {
+ // If the Cell contains a CellFrame, we need to use it
+ // in order to get to the Cell
+ while ( !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell, "Frame is not a Cell" );
+ if ( rUnion.Overlaps( pCell->getFrameArea() ) )
+ ::InsertCell( rArr, static_cast<SwCellFrame*>(pCell) );
+
+ // Make sure the Cell is left (Areas)
+ SwLayoutFrame *pTmp = pCell;
+ do
+ { pTmp = pTmp->GetNextLayoutLeaf();
+ } while ( pCell->IsAnLower( pTmp ) );
+ pCell = pTmp;
+ } while( pCell && pTab->IsAnLower( pCell ) );
+}
+
+void SwDoc::SetTabBorders( const SwCursor& rCursor, const SfxItemSet& rSet )
+{
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ if( aUnions.empty() )
+ return;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve( 255 );
+ const SvxBoxItem* pSetBox;
+ const SvxBoxInfoItem *pSetBoxInfo;
+
+ const SvxBorderLine* pLeft = nullptr;
+ const SvxBorderLine* pRight = nullptr;
+ const SvxBorderLine* pTop = nullptr;
+ const SvxBorderLine* pBottom = nullptr;
+ const SvxBorderLine* pHori = nullptr;
+ const SvxBorderLine* pVert = nullptr;
+ bool bHoriValid = true, bVertValid = true,
+ bTopValid = true, bBottomValid = true,
+ bLeftValid = true, bRightValid = true;
+
+ // The Flags in the BoxInfo Item decide whether a BorderLine is valid!
+ pSetBoxInfo = rSet.GetItemIfSet( SID_ATTR_BORDER_INNER, false );
+ if( pSetBoxInfo )
+ {
+ pHori = pSetBoxInfo->GetHori();
+ pVert = pSetBoxInfo->GetVert();
+
+ bHoriValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::HORI);
+ bVertValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::VERT);
+
+ // Do we want to evaluate these?
+ bTopValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::TOP);
+ bBottomValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ bLeftValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ bRightValid = pSetBoxInfo->IsValid(SvxBoxInfoItemValidFlags::RIGHT);
+ }
+
+ pSetBox = rSet.GetItemIfSet( RES_BOX, false );
+ if( pSetBox )
+ {
+ pLeft = pSetBox->GetLeft();
+ pRight = pSetBox->GetRight();
+ pTop = pSetBox->GetTop();
+ pBottom = pSetBox->GetBottom();
+ }
+ else
+ {
+ // Not set, thus not valid values
+ bTopValid = bBottomValid = bLeftValid = bRightValid = false;
+ pSetBox = nullptr;
+ }
+
+ bool bFirst = true;
+ for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ SwTabFrame *pTab = pUnion->GetTable();
+ const SwRect &rUnion = pUnion->GetUnion();
+ const bool bLast = (i == aUnions.size() - 1);
+
+ std::vector<SwCellFrame*> aCellArr;
+ aCellArr.reserve( 255 );
+ ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );
+
+ // All Cell Borders that match the UnionRect or extend it are
+ // Outer Borders. All others are Inner Borders.
+
+ // New: The Outer Borders can, depending on whether it's a
+ // Start/Middle/Follow Table (for Selection via FollowTabs),
+ // also not be Outer Borders.
+ // Outer Borders are set on the left, right, at the top and at the bottom.
+ // Inner Borders are only set at the top and on the left.
+ for ( auto pCell : aCellArr )
+ {
+ const bool bVert = pTab->IsVertical();
+ const bool bRTL = pTab->IsRightToLeft();
+ bool bTopOver, bLeftOver, bRightOver, bBottomOver;
+ if ( bVert )
+ {
+ bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ }
+ else
+ {
+ bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ }
+
+ if ( bRTL )
+ {
+ bool bTmp = bRightOver;
+ bRightOver = bLeftOver;
+ bLeftOver = bTmp;
+ }
+
+ // Do not set anything by default in HeadlineRepeats
+ if ( pTab->IsFollow() &&
+ ( pTab->IsInHeadline( *pCell ) ||
+ // Same holds for follow flow rows
+ pCell->IsInFollowFlowRow() ) )
+ continue;
+
+ SvxBoxItem aBox( pCell->GetFormat()->GetBox() );
+
+ sal_Int16 nType = 0;
+
+ // Top Border
+ if( bTopValid )
+ {
+ if ( bFirst && bTopOver )
+ {
+ aBox.SetLine( pTop, SvxBoxItemLine::TOP );
+ nType |= 0x0001;
+ }
+ else if ( bHoriValid )
+ {
+ aBox.SetLine( nullptr, SvxBoxItemLine::TOP );
+ nType |= 0x0002;
+ }
+ }
+
+ // Fix fdo#62470 correct the input for RTL table
+ if (bRTL)
+ {
+ if( bLeftOver && bRightOver)
+ {
+ if ( bLeftValid )
+ {
+ aBox.SetLine( pLeft, SvxBoxItemLine::RIGHT );
+ nType |= 0x0010;
+ }
+ if ( bRightValid )
+ {
+ aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
+ nType |= 0x0004;
+ }
+ }
+ else
+ {
+ if ( bLeftValid )
+ {
+ aBox.SetLine( bRightOver ? pLeft : nullptr, SvxBoxItemLine::RIGHT );
+ if (bVertValid)
+ nType |= 0x0020;
+ else
+ nType |= 0x0010;
+ }
+ if ( bLeftOver )
+ {
+ if ( bRightValid )
+ {
+ aBox.SetLine( pRight, SvxBoxItemLine::LEFT );
+ nType |= 0x0004;
+ }
+ }
+ else if ( bVertValid )
+ {
+ aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
+ nType |= 0x0008;
+ }
+ }
+ }
+ else
+ {
+ // Left Border
+ if ( bLeftOver )
+ {
+ if( bLeftValid )
+ {
+ aBox.SetLine( pLeft, SvxBoxItemLine::LEFT );
+ nType |= 0x0004;
+ }
+ }
+ else if( bVertValid )
+ {
+ aBox.SetLine( pVert, SvxBoxItemLine::LEFT );
+ nType |= 0x0008;
+ }
+
+ // Right Border
+ if( bRightValid )
+ {
+ if ( bRightOver )
+ {
+ aBox.SetLine( pRight, SvxBoxItemLine::RIGHT );
+ nType |= 0x0010;
+ }
+ else if ( bVertValid )
+ {
+ aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ nType |= 0x0020;
+ }
+ }
+ }
+
+ // Bottom Border
+ if ( bLast && bBottomOver )
+ {
+ if( bBottomValid )
+ {
+ aBox.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
+ nType |= 0x0040;
+ }
+ }
+ else if( bHoriValid )
+ {
+ aBox.SetLine( pHori, SvxBoxItemLine::BOTTOM );
+ nType |= 0x0080;
+ }
+
+ if( pSetBox )
+ {
+ for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
+ aBox.SetDistance( pSetBox->GetDistance( k ), k );
+ }
+
+ SwTableBox *pBox = const_cast<SwTableBox*>(pCell->GetTabBox());
+ SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), nType );
+ if ( nullptr != pNewFormat )
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
+ else
+ {
+ SwFrameFormat *pOld = pBox->GetFrameFormat();
+ SwFrameFormat *pNew = pBox->ClaimFrameFormat();
+ pNew->SetFormatAttr( aBox );
+ aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, nType));
+ }
+ }
+
+ bFirst = false;
+ }
+
+ SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
+ if( pTableLayout )
+ {
+ SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
+
+ pTableLayout->BordersChanged(
+ pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
+ }
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentState().SetModified();
+}
+
+static void lcl_SetLineStyle( SvxBorderLine *pToSet,
+ const Color *pColor, const SvxBorderLine *pBorderLine)
+{
+ if ( pBorderLine )
+ {
+ if ( !pColor )
+ {
+ Color aTmp( pToSet->GetColor() );
+ *pToSet = *pBorderLine;
+ pToSet->SetColor( aTmp );
+ }
+ else
+ *pToSet = *pBorderLine;
+ }
+ if ( pColor )
+ pToSet->SetColor( *pColor );
+}
+
+void SwDoc::SetTabLineStyle( const SwCursor& rCursor,
+ const Color* pColor, bool bSetLine,
+ const SvxBorderLine* pBorderLine )
+{
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ if( aUnions.empty() )
+ return;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoAttrTable>(*pTableNd));
+ }
+
+ SvxBorderLine aDefaultBorder(pBorderLine ? *pBorderLine
+ : SvxBorderLine(pColor, SvxBorderLineWidth::VeryThin));
+ if (pColor && pBorderLine)
+ aDefaultBorder.SetColor(*pColor);
+
+ for( auto &rU : aUnions )
+ {
+ SwSelUnion *pUnion = &rU;
+ SwTabFrame *pTab = pUnion->GetTable();
+ std::vector<SwCellFrame*> aCellArr;
+ aCellArr.reserve( 255 );
+ ::lcl_CollectCells( aCellArr, pUnion->GetUnion(), pTab );
+
+ for ( auto pCell : aCellArr )
+ {
+ // Do not set anything by default in HeadlineRepeats
+ if ( pTab->IsFollow() && pTab->IsInHeadline( *pCell ) )
+ continue;
+
+ const_cast<SwTableBox*>(pCell->GetTabBox())->ClaimFrameFormat();
+ SwFrameFormat *pFormat = pCell->GetFormat();
+ std::unique_ptr<SvxBoxItem> aBox(pFormat->GetBox().Clone());
+
+ SvxBorderLine* pTop = const_cast<SvxBorderLine*>(aBox->GetTop());
+ SvxBorderLine* pBot = const_cast<SvxBorderLine*>(aBox->GetBottom());
+ SvxBorderLine* pLeft = const_cast<SvxBorderLine*>(aBox->GetLeft());
+ SvxBorderLine* pRight = const_cast<SvxBorderLine*>(aBox->GetRight());
+
+ if ( !pBorderLine && bSetLine )
+ {
+ aBox.reset(::GetDfltAttr(RES_BOX)->Clone());
+ }
+ else if ((pColor || pBorderLine) && !pTop && !pBot && !pLeft && !pRight)
+ {
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::TOP);
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::BOTTOM);
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::LEFT);
+ aBox->SetLine(&aDefaultBorder, SvxBoxItemLine::RIGHT);
+ }
+ else
+ {
+ if (pTop)
+ ::lcl_SetLineStyle(pTop, pColor, pBorderLine);
+ if (pBot)
+ ::lcl_SetLineStyle(pBot, pColor, pBorderLine);
+ if (pLeft)
+ ::lcl_SetLineStyle(pLeft, pColor, pBorderLine);
+ if (pRight)
+ ::lcl_SetLineStyle(pRight, pColor, pBorderLine);
+ }
+ pFormat->SetFormatAttr( *aBox );
+ }
+ }
+
+ SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
+ if( pTableLayout )
+ {
+ SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
+
+ pTableLayout->BordersChanged(
+ pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ) );
+ }
+ ::ClearFEShellTabCols(*this, nullptr);
+ getIDocumentState().SetModified();
+}
+
+void SwDoc::GetTabBorders( const SwCursor& rCursor, SfxItemSet& rSet )
+{
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ if( aUnions.empty() )
+ return;
+
+ SvxBoxItem aSetBox ( rSet.Get(RES_BOX ) );
+ SvxBoxInfoItem aSetBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
+
+ bool bTopSet = false,
+ bBottomSet = false,
+ bLeftSet = false,
+ bRightSet = false,
+ bHoriSet = false,
+ bVertSet = false,
+ bDistanceSet = false,
+ bRTLTab = false;
+
+ aSetBoxInfo.ResetFlags();
+
+ for ( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ const SwTabFrame *pTab = pUnion->GetTable();
+ const SwRect &rUnion = pUnion->GetUnion();
+ const bool bFirst = i == 0;
+ const bool bLast = (i == aUnions.size() - 1);
+
+ std::vector<SwCellFrame*> aCellArr;
+ aCellArr.reserve(255);
+ ::lcl_CollectCells( aCellArr, rUnion, const_cast<SwTabFrame*>(pTab) );
+
+ for ( auto pCell : aCellArr )
+ {
+ const bool bVert = pTab->IsVertical();
+ const bool bRTL = bRTLTab = pTab->IsRightToLeft();
+ bool bTopOver, bLeftOver, bRightOver, bBottomOver;
+ if ( bVert )
+ {
+ bTopOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bLeftOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bRightOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ bBottomOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ }
+ else
+ {
+ bTopOver = pCell->getFrameArea().Top() <= rUnion.Top();
+ bLeftOver = pCell->getFrameArea().Left() <= rUnion.Left();
+ bRightOver = pCell->getFrameArea().Right() >= rUnion.Right();
+ bBottomOver = pCell->getFrameArea().Bottom() >= rUnion.Bottom();
+ }
+
+ if ( bRTL )
+ {
+ bool bTmp = bRightOver;
+ bRightOver = bLeftOver;
+ bLeftOver = bTmp;
+ }
+
+ const SwFrameFormat *pFormat = pCell->GetFormat();
+ const SvxBoxItem &rBox = pFormat->GetBox();
+
+ // Top Border
+ if ( bFirst && bTopOver )
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::TOP))
+ {
+ if ( !bTopSet )
+ { bTopSet = true;
+ aSetBox.SetLine( rBox.GetTop(), SvxBoxItemLine::TOP );
+ }
+ else if ((aSetBox.GetTop() && rBox.GetTop() &&
+ (*aSetBox.GetTop() != *rBox.GetTop())) ||
+ ((!aSetBox.GetTop()) != (!rBox.GetTop()))) // != expression is true, if one and only one of the two pointers is !0
+ {
+ aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::TOP );
+ }
+ }
+ }
+
+ // Left Border
+ if ( bLeftOver )
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT))
+ {
+ if ( !bLeftSet )
+ { bLeftSet = true;
+ aSetBox.SetLine( rBox.GetLeft(), SvxBoxItemLine::LEFT );
+ }
+ else if ((aSetBox.GetLeft() && rBox.GetLeft() &&
+ (*aSetBox.GetLeft() != *rBox.GetLeft())) ||
+ ((!aSetBox.GetLeft()) != (!rBox.GetLeft())))
+ {
+ aSetBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ }
+ }
+ }
+ else
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::VERT))
+ {
+ if ( !bVertSet )
+ { bVertSet = true;
+ aSetBoxInfo.SetLine( rBox.GetLeft(), SvxBoxInfoItemLine::VERT );
+ }
+ else if ((aSetBoxInfo.GetVert() && rBox.GetLeft() &&
+ (*aSetBoxInfo.GetVert() != *rBox.GetLeft())) ||
+ ((!aSetBoxInfo.GetVert()) != (!rBox.GetLeft())))
+ { aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::VERT, false );
+ aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ }
+ }
+ }
+
+ // Right Border
+ if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) && bRightOver )
+ {
+ if ( !bRightSet )
+ { bRightSet = true;
+ aSetBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
+ }
+ else if ((aSetBox.GetRight() && rBox.GetRight() &&
+ (*aSetBox.GetRight() != *rBox.GetRight())) ||
+ (!aSetBox.GetRight() != !rBox.GetRight()))
+ { aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ }
+
+ // Bottom Border
+ if ( bLast && bBottomOver )
+ {
+ if ( aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
+ {
+ if ( !bBottomSet )
+ { bBottomSet = true;
+ aSetBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
+ }
+ else if ((aSetBox.GetBottom() && rBox.GetBottom() &&
+ (*aSetBox.GetBottom() != *rBox.GetBottom())) ||
+ (!aSetBox.GetBottom() != !rBox.GetBottom()))
+ { aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, false );
+ aSetBox.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+ }
+ }
+ }
+ // In all Lines, except for the last one, the horizontal Line
+ // is taken from the Bottom Line.
+ else
+ {
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::HORI))
+ {
+ if ( !bHoriSet )
+ { bHoriSet = true;
+ aSetBoxInfo.SetLine( rBox.GetBottom(), SvxBoxInfoItemLine::HORI );
+ }
+ else if ((aSetBoxInfo.GetHori() && rBox.GetBottom() &&
+ (*aSetBoxInfo.GetHori() != *rBox.GetBottom())) ||
+ ((!aSetBoxInfo.GetHori()) != (!rBox.GetBottom())))
+ {
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::HORI, false );
+ aSetBoxInfo.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ }
+ }
+ }
+
+ // Distance to text
+ if (aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::DISTANCE))
+ {
+ if( !bDistanceSet ) // Set on first iteration
+ {
+ bDistanceSet = true;
+ for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
+ aSetBox.SetDistance( rBox.GetDistance( k ), k );
+ }
+ else
+ {
+ for( SvxBoxItemLine k : o3tl::enumrange<SvxBoxItemLine>() )
+ if( aSetBox.GetDistance( k ) !=
+ rBox.GetDistance( k ) )
+ {
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::DISTANCE, false );
+ aSetBox.SetAllDistances(0);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // fdo#62470 fix the reading for table format.
+ if ( bRTLTab )
+ {
+ SvxBoxItem aTempBox ( rSet.Get(RES_BOX ) );
+ SvxBoxInfoItem aTempBoxInfo( rSet.Get(SID_ATTR_BORDER_INNER) );
+
+ aTempBox.SetLine( aSetBox.GetRight(), SvxBoxItemLine::RIGHT);
+ aSetBox.SetLine( aSetBox.GetLeft(), SvxBoxItemLine::RIGHT);
+ aSetBox.SetLine( aTempBox.GetRight(), SvxBoxItemLine::LEFT);
+
+ aTempBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT, aSetBoxInfo.IsValid(SvxBoxInfoItemValidFlags::RIGHT) );
+ aSetBoxInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT, aTempBoxInfo.IsValid(SvxBoxInfoItemValidFlags::LEFT) );
+ }
+
+ rSet.Put( aSetBox );
+ rSet.Put( aSetBoxInfo );
+}
+
+void SwDoc::SetBoxAttr( const SwCursor& rCursor, const SfxPoolItem &rNew )
+{
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ SwSelBoxes aBoxes;
+ if( !(pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes, true )) )
+ return;
+
+ SwTable& rTable = pTableNd->GetTable();
+ if (GetIDocumentUndoRedo().DoesUndo())
+ {
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoAttrTable>(*pTableNd) );
+ }
+
+ std::vector<std::unique_ptr<SwTableFormatCmp>> aFormatCmp;
+ aFormatCmp.reserve(std::max<size_t>(255, aBoxes.size()));
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ SwTableBox *pBox = aBoxes[i];
+
+ SwFrameFormat *pNewFormat = SwTableFormatCmp::FindNewFormat( aFormatCmp, pBox->GetFrameFormat(), 0 );
+ if ( nullptr != pNewFormat )
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
+ else
+ {
+ SwFrameFormat *pOld = pBox->GetFrameFormat();
+ SwFrameFormat *pNew = pBox->ClaimFrameFormat();
+ pNew->SetFormatAttr( rNew );
+ aFormatCmp.push_back(std::make_unique<SwTableFormatCmp>(pOld, pNew, 0));
+ }
+
+ pBox->SetDirectFormatting(true);
+ }
+
+ SwHTMLTableLayout *pTableLayout = rTable.GetHTMLTableLayout();
+ if( pTableLayout )
+ {
+ SwContentFrame* pFrame = rCursor.GetContentNode()->getLayoutFrame( rCursor.GetContentNode()->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
+ SwTabFrame* pTabFrame = pFrame->ImplFindTabFrame();
+
+ pTableLayout->Resize(
+ pTableLayout->GetBrowseWidthByTabFrame( *pTabFrame ), true );
+ }
+ getIDocumentState().SetModified();
+}
+
+bool SwDoc::GetBoxAttr( const SwCursor& rCursor, std::unique_ptr<SfxPoolItem>& rToFill )
+{
+ // tdf#144843 calling GetBoxAttr *requires* object
+ assert(rToFill && "requires object here");
+ bool bRet = false;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ SwSelBoxes aBoxes;
+ if( pTableNd && lcl_GetBoxSel( rCursor, aBoxes ))
+ {
+ bRet = true;
+ bool bOneFound = false;
+ const sal_uInt16 nWhich = rToFill->Which();
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ switch ( nWhich )
+ {
+ case RES_BACKGROUND:
+ {
+ std::unique_ptr<SvxBrushItem> xBack =
+ aBoxes[i]->GetFrameFormat()->makeBackgroundBrushItem();
+ if( !bOneFound )
+ {
+ rToFill = std::move(xBack);
+ bOneFound = true;
+ }
+ else if( *rToFill != *xBack )
+ bRet = false;
+ }
+ break;
+
+ case RES_FRAMEDIR:
+ {
+ const SvxFrameDirectionItem& rDir =
+ aBoxes[i]->GetFrameFormat()->GetFrameDir();
+ if( !bOneFound )
+ {
+ rToFill.reset(rDir.Clone());
+ bOneFound = true;
+ }
+ else if( rToFill && *rToFill != rDir )
+ bRet = false;
+ }
+ break;
+ case RES_VERT_ORIENT:
+ {
+ const SwFormatVertOrient& rOrient =
+ aBoxes[i]->GetFrameFormat()->GetVertOrient();
+ if( !bOneFound )
+ {
+ rToFill.reset(rOrient.Clone());
+ bOneFound = true;
+ }
+ else if( rToFill && *rToFill != rOrient )
+ bRet = false;
+ }
+ break;
+ }
+
+ if ( !bRet )
+ break;
+ }
+ }
+ return bRet;
+}
+
+void SwDoc::SetBoxAlign( const SwCursor& rCursor, sal_uInt16 nAlign )
+{
+ OSL_ENSURE( nAlign == text::VertOrientation::NONE ||
+ nAlign == text::VertOrientation::CENTER ||
+ nAlign == text::VertOrientation::BOTTOM, "Wrong alignment" );
+ SwFormatVertOrient aVertOri( 0, nAlign );
+ SetBoxAttr( rCursor, aVertOri );
+}
+
+sal_uInt16 SwDoc::GetBoxAlign( const SwCursor& rCursor )
+{
+ sal_uInt16 nAlign = USHRT_MAX;
+ SwTableNode* pTableNd = rCursor.GetPoint()->nNode.GetNode().FindTableNode();
+ SwSelBoxes aBoxes;
+ if( pTableNd && ::lcl_GetBoxSel( rCursor, aBoxes ))
+ {
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ const SwFormatVertOrient &rOri =
+ aBoxes[i]->GetFrameFormat()->GetVertOrient();
+ if( USHRT_MAX == nAlign )
+ nAlign = o3tl::narrowing<sal_uInt16>(rOri.GetVertOrient());
+ else if( rOri.GetVertOrient() != nAlign )
+ {
+ nAlign = USHRT_MAX;
+ break;
+ }
+ }
+ }
+ return nAlign;
+}
+
+static sal_uInt16 lcl_CalcCellFit( const SwLayoutFrame *pCell )
+{
+ SwTwips nRet = 0;
+ const SwFrame *pFrame = pCell->Lower(); // The whole Line
+ SwRectFnSet aRectFnSet(pCell);
+ while ( pFrame )
+ {
+ const SwTwips nAdd = aRectFnSet.GetWidth(pFrame->getFrameArea()) -
+ aRectFnSet.GetWidth(pFrame->getFramePrintArea());
+
+ // pFrame does not necessarily have to be a SwTextFrame!
+ const SwTwips nCalcFitToContent = pFrame->IsTextFrame() ?
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent() :
+ aRectFnSet.GetWidth(pFrame->getFramePrintArea());
+
+ nRet = std::max( nRet, nCalcFitToContent + nAdd );
+ pFrame = pFrame->GetNext();
+ }
+ // Surrounding border as well as left and Right Border also need to be respected
+ nRet += aRectFnSet.GetWidth(pCell->getFrameArea()) -
+ aRectFnSet.GetWidth(pCell->getFramePrintArea());
+
+ // To compensate for the accuracy of calculation later on in SwTable::SetTabCols
+ // we keep adding up a little.
+ nRet += COLFUZZY;
+ return o3tl::narrowing<sal_uInt16>(std::max( SwTwips(MINLAY), nRet ));
+}
+
+/* The Line is within the Selection but not outlined by the TabCols.
+ *
+ * That means that the Line has been "split" by other Cells due to the
+ * two-dimensional representation used. Thus, we have to distribute the cell's
+ * default or minimum value amongst the Cell it has been split by.
+ *
+ * First, we collect the Columns (not the Column separators) which overlap
+ * with the Cell. We then distribute the desired value according to the
+ * amount of overlapping amongst the Cells.
+ *
+ * A Cell's default value stays the same if it already has a larger value than
+ * the desired one. It's overwritten if it's smaller.
+ */
+static void lcl_CalcSubColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
+ const SwLayoutFrame *pCell, const SwLayoutFrame *pTab,
+ bool bWishValues )
+{
+ const sal_uInt16 nWish = bWishValues ?
+ ::lcl_CalcCellFit( pCell ) :
+ MINLAY + sal_uInt16(pCell->getFrameArea().Width() - pCell->getFramePrintArea().Width());
+
+ SwRectFnSet aRectFnSet(pTab);
+
+ for ( size_t i = 0 ; i <= rCols.Count(); ++i )
+ {
+ tools::Long nColLeft = i == 0 ? rCols.GetLeft() : rCols[i-1];
+ tools::Long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
+ nColLeft += rCols.GetLeftMin();
+ nColRight += rCols.GetLeftMin();
+
+ // Adapt values to the proportions of the Table (Follows)
+ if ( rCols.GetLeftMin() != aRectFnSet.GetLeft(pTab->getFrameArea()) )
+ {
+ const tools::Long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
+ nColLeft += nDiff;
+ nColRight += nDiff;
+ }
+ const tools::Long nCellLeft = aRectFnSet.GetLeft(pCell->getFrameArea());
+ const tools::Long nCellRight = aRectFnSet.GetRight(pCell->getFrameArea());
+
+ // Calculate overlapping value
+ tools::Long nWidth = 0;
+ if ( nColLeft <= nCellLeft && nColRight >= (nCellLeft+COLFUZZY) )
+ nWidth = nColRight - nCellLeft;
+ else if ( nColLeft <= (nCellRight-COLFUZZY) && nColRight >= nCellRight )
+ nWidth = nCellRight - nColLeft;
+ else if ( nColLeft >= nCellLeft && nColRight <= nCellRight )
+ nWidth = nColRight - nColLeft;
+ if ( nWidth && pCell->getFrameArea().Width() )
+ {
+ tools::Long nTmp = nWidth * nWish / pCell->getFrameArea().Width();
+ if ( o3tl::make_unsigned(nTmp) > rToFill[i] )
+ rToFill[i] = sal_uInt16(nTmp);
+ }
+ }
+}
+
+/**
+ * Retrieves new values to set the TabCols.
+ *
+ * We do not iterate over the TabCols' entries, but over the gaps that describe Cells.
+ * We set TabCol entries for which we did not calculate Cells to 0.
+ *
+ * @param bWishValues == true: We calculate the desired value of all affected
+ * Cells for the current Selection/current Cell.
+ * If more Cells are within a Column, the highest
+ * desired value is returned.
+ * We set TabCol entries for which we did not calculate
+ * Cells to 0.
+ *
+ * @param bWishValues == false: The Selection is expanded vertically.
+ * We calculate the minimum value for every
+ * Column in the TabCols that intersects with the
+ * Selection.
+ */
+static void lcl_CalcColValues( std::vector<sal_uInt16> &rToFill, const SwTabCols &rCols,
+ const SwLayoutFrame *pStart, const SwLayoutFrame *pEnd,
+ bool bWishValues )
+{
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd,
+ bWishValues ? SwTableSearchType::NONE : SwTableSearchType::Col );
+
+ for ( auto &rU : aUnions )
+ {
+ SwSelUnion *pSelUnion = &rU;
+ const SwTabFrame *pTab = pSelUnion->GetTable();
+ const SwRect &rUnion = pSelUnion->GetUnion();
+
+ SwRectFnSet aRectFnSet(pTab);
+ bool bRTL = pTab->IsRightToLeft();
+
+ const SwLayoutFrame *pCell = pTab->FirstCell();
+ if (!pCell)
+ continue;
+ do
+ {
+ if ( pCell->IsCellFrame() && pCell->FindTabFrame() == pTab && ::IsFrameInTableSel( rUnion, pCell ) )
+ {
+ const tools::Long nCLeft = aRectFnSet.GetLeft(pCell->getFrameArea());
+ const tools::Long nCRight = aRectFnSet.GetRight(pCell->getFrameArea());
+
+ bool bNotInCols = true;
+
+ for ( size_t i = 0; i <= rCols.Count(); ++i )
+ {
+ sal_uInt16 nFit = rToFill[i];
+ tools::Long nColLeft = i == 0 ? rCols.GetLeft() : rCols[i-1];
+ tools::Long nColRight = i == rCols.Count() ? rCols.GetRight() : rCols[i];
+
+ if ( bRTL )
+ {
+ tools::Long nTmpRight = nColRight;
+ nColRight = rCols.GetRight() - nColLeft;
+ nColLeft = rCols.GetRight() - nTmpRight;
+ }
+
+ nColLeft += rCols.GetLeftMin();
+ nColRight += rCols.GetLeftMin();
+
+ // Adapt values to the proportions of the Table (Follows)
+ tools::Long nLeftA = nColLeft;
+ tools::Long nRightA = nColRight;
+ if ( rCols.GetLeftMin() != sal_uInt16(aRectFnSet.GetLeft(pTab->getFrameArea())) )
+ {
+ const tools::Long nDiff = aRectFnSet.GetLeft(pTab->getFrameArea()) - rCols.GetLeftMin();
+ nLeftA += nDiff;
+ nRightA += nDiff;
+ }
+
+ // We don't want to take a too close look
+ if ( ::IsSame(nCLeft, nLeftA) && ::IsSame(nCRight, nRightA))
+ {
+ bNotInCols = false;
+ if ( bWishValues )
+ {
+ const sal_uInt16 nWish = ::lcl_CalcCellFit( pCell );
+ if ( nWish > nFit )
+ nFit = nWish;
+ }
+ else
+ { const sal_uInt16 nMin = MINLAY + sal_uInt16(pCell->getFrameArea().Width() -
+ pCell->getFramePrintArea().Width());
+ if ( !nFit || nMin < nFit )
+ nFit = nMin;
+ }
+ if ( rToFill[i] < nFit )
+ rToFill[i] = nFit;
+ }
+ }
+ if ( bNotInCols )
+ ::lcl_CalcSubColValues( rToFill, rCols, pCell, pTab, bWishValues );
+ }
+ do {
+ pCell = pCell->GetNextLayoutLeaf();
+ } while( pCell && pCell->getFrameArea().Width() == 0 );
+ } while ( pCell && pTab->IsAnLower( pCell ) );
+ }
+}
+
+void SwDoc::AdjustCellWidth( const SwCursor& rCursor,
+ const bool bBalance,
+ const bool bNoShrink )
+{
+ // Check whether the current Cursor has it's Point/Mark in a Table
+ SwContentNode* pCntNd = rCursor.GetPoint()->nNode.GetNode().GetContentNode();
+ SwTableNode* pTableNd = pCntNd ? pCntNd->FindTableNode() : nullptr;
+ if( !pTableNd )
+ return ;
+
+ SwLayoutFrame *pStart, *pEnd;
+ ::lcl_GetStartEndCell( rCursor, pStart, pEnd );
+
+ // Collect TabCols; we reset the Table with them
+ SwFrame* pBoxFrame = pStart;
+ while( pBoxFrame && !pBoxFrame->IsCellFrame() )
+ pBoxFrame = pBoxFrame->GetUpper();
+
+ if ( !pBoxFrame )
+ return; // Robust
+
+ SwTabCols aTabCols;
+ GetTabCols( aTabCols, static_cast<SwCellFrame*>(pBoxFrame) );
+
+ if ( ! aTabCols.Count() )
+ return;
+
+ std::vector<sal_uInt16> aWish(aTabCols.Count() + 1);
+ std::vector<sal_uInt16> aMins(aTabCols.Count() + 1);
+
+ ::lcl_CalcColValues( aWish, aTabCols, pStart, pEnd, /*bWishValues=*/true );
+
+ // It's more robust if we calculate the minimum values for the whole Table
+ const SwTabFrame *pTab = pStart->ImplFindTabFrame();
+ pStart = const_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame const *>(pTab->FirstCell()));
+ pEnd = const_cast<SwLayoutFrame*>(pTab->FindLastContentOrTable()->GetUpper());
+ while( !pEnd->IsCellFrame() )
+ pEnd = pEnd->GetUpper();
+ ::lcl_CalcColValues( aMins, aTabCols, pStart, pEnd, /*bWishValues=*/false );
+
+ sal_uInt16 nSelectedWidth = 0, nCols = 0;
+ float fTotalWish = 0;
+ if ( bBalance || bNoShrink )
+ {
+ // Find the combined size of the selected columns
+ for ( size_t i = 0; i <= aTabCols.Count(); ++i )
+ {
+ if ( aWish[i] )
+ {
+ if ( i == 0 )
+ nSelectedWidth += aTabCols[i] - aTabCols.GetLeft();
+ else if ( i == aTabCols.Count() )
+ nSelectedWidth += aTabCols.GetRight() - aTabCols[i-1];
+ else
+ nSelectedWidth += aTabCols[i] - aTabCols[i-1];
+ ++nCols;
+ }
+ fTotalWish += aWish[i];
+ }
+ assert(nCols);
+ const sal_uInt16 nEqualWidth = nCols ? nSelectedWidth / nCols : 0;
+ // bBalance: Distribute the width evenly
+ for (sal_uInt16 & rn : aWish)
+ if ( rn && bBalance )
+ rn = nEqualWidth;
+ }
+
+ const tools::Long nOldRight = aTabCols.GetRight();
+
+ // In order to make the implementation easier, but still use the available
+ // space properly, we do this twice.
+
+ // The problem: The first column is getting wider, the others get slimmer
+ // only afterwards.
+ // The first column's desired width would be discarded as it would cause
+ // the Table's width to exceed the maximum width.
+ const sal_uInt16 nEqualWidth = (aTabCols.GetRight() - aTabCols.GetLeft()) / (aTabCols.Count() + 1);
+ const sal_Int16 nTablePadding = nSelectedWidth - fTotalWish;
+ for ( int k = 0; k < 2; ++k )
+ {
+ for ( size_t i = 0; i <= aTabCols.Count(); ++i )
+ {
+ // bNoShrink: distribute excess space proportionately on pass 2.
+ if ( bNoShrink && k && nTablePadding > 0 && fTotalWish > 0 )
+ aWish[i] += round( aWish[i] / fTotalWish * nTablePadding );
+
+ // First pass is primarily a shrink pass. Give all columns a chance
+ // to grow by requesting the maximum width as "balanced".
+ // Second pass is a first-come, first-served chance to max out.
+ int nDiff = k ? aWish[i] : std::min(aWish[i], nEqualWidth);
+ if ( nDiff )
+ {
+ int nMin = aMins[i];
+ if ( nMin > nDiff )
+ nDiff = nMin;
+
+ if ( i == 0 )
+ {
+ if( aTabCols.Count() )
+ nDiff -= aTabCols[0] - aTabCols.GetLeft();
+ else
+ nDiff -= aTabCols.GetRight() - aTabCols.GetLeft();
+ }
+ else if ( i == aTabCols.Count() )
+ nDiff -= aTabCols.GetRight() - aTabCols[i-1];
+ else
+ nDiff -= aTabCols[i] - aTabCols[i-1];
+
+ tools::Long nTabRight = aTabCols.GetRight() + nDiff;
+ const tools::Long nMaxRight = std::max(aTabCols.GetRightMax(), nOldRight);
+
+ // If the Table would become (or is already) too wide,
+ // restrict the column growth to the allowed maximum.
+ if (!bBalance && nTabRight > nMaxRight)
+ {
+ const tools::Long nTmpD = nTabRight - nMaxRight;
+ nDiff -= nTmpD;
+ nTabRight -= nTmpD;
+ }
+
+ // all the remaining columns need to be shifted by the same amount
+ for ( size_t i2 = i; i2 < aTabCols.Count(); ++i2 )
+ aTabCols[i2] += nDiff;
+ aTabCols.SetRight( nTabRight );
+ }
+ }
+ }
+
+ const tools::Long nNewRight = aTabCols.GetRight();
+
+ SwFrameFormat *pFormat = pTableNd->GetTable().GetFrameFormat();
+ const sal_Int16 nOriHori = pFormat->GetHoriOrient().GetHoriOrient();
+
+ // We can leave the "real" work to the SwTable now
+ SetTabCols( aTabCols, false, static_cast<SwCellFrame*>(pBoxFrame) );
+
+ // Alignment might have been changed in SetTabCols; restore old value
+ const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
+ SwFormatHoriOrient aHori( rHori );
+ if ( aHori.GetHoriOrient() != nOriHori )
+ {
+ aHori.SetHoriOrient( nOriHori );
+ pFormat->SetFormatAttr( aHori );
+ }
+
+ // We switch to left-adjusted for automatic width
+ // We adjust the right border for Border attributes
+ if( !bBalance && nNewRight < nOldRight )
+ {
+ if( aHori.GetHoriOrient() == text::HoriOrientation::FULL )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::LEFT );
+ pFormat->SetFormatAttr( aHori );
+ }
+ }
+
+ getIDocumentState().SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/node.cxx b/sw/source/core/docnode/node.cxx
new file mode 100644
index 000000000..d95b18cee
--- /dev/null
+++ b/sw/source/core/docnode/node.cxx
@@ -0,0 +1,2138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <hintids.hxx>
+#include <editeng/protitem.hxx>
+#include <osl/diagnose.h>
+#include <tools/gen.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <fmtcntnt.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <txtftn.hxx>
+#include <ftnfrm.hxx>
+#include <doc.hxx>
+#include <node.hxx>
+#include <ndindex.hxx>
+#include <numrule.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <section.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <txtfrm.hxx>
+#include <tabfrm.hxx>
+#include <viewsh.hxx>
+#include <paratr.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <fmthdft.hxx>
+#include <frmatr.hxx>
+#include <fmtautofmt.hxx>
+#include <frmtool.hxx>
+#include <pagefrm.hxx>
+#include <node2lay.hxx>
+#include <pagedesc.hxx>
+#include <fmtpdsc.hxx>
+#include <breakit.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <scriptinfo.hxx>
+#include <rootfrm.hxx>
+#include <istyleaccess.hxx>
+#include <IDocumentListItems.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <calbck.hxx>
+#include <ndole.hxx>
+#include <memory>
+#include <swcrsr.hxx>
+#include <hints.hxx>
+#include <frameformats.hxx>
+#ifdef DBG_UTIL
+#include <sal/backtrace.hxx>
+#endif
+
+using namespace ::com::sun::star::i18n;
+
+
+/*
+ * Some local helper functions for the attribute set handle of a content node.
+ * Since the attribute set of a content node may not be modified directly,
+ * we always have to create a new SwAttrSet, do the modifications, and get
+ * a new handle from the style access
+ */
+
+namespace AttrSetHandleHelper
+{
+
+static void GetNewAutoStyle( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ SwAttrSet const & rNewAttrSet )
+{
+ const SwAttrSet* pAttrSet = static_cast<const SwAttrSet*>(rpAttrSet.get());
+ if( rNode.GetModifyAtAttr() )
+ const_cast<SwAttrSet*>(pAttrSet)->SetModifyAtAttr( nullptr );
+ IStyleAccess& rSA = pAttrSet->GetPool()->GetDoc()->GetIStyleAccess();
+ rpAttrSet = rSA.getAutomaticStyle( rNewAttrSet, rNode.IsTextNode() ?
+ IStyleAccess::AUTO_STYLE_PARA :
+ IStyleAccess::AUTO_STYLE_NOTXT );
+ const bool bSetModifyAtAttr = const_cast<SwAttrSet*>(static_cast<const SwAttrSet*>(rpAttrSet.get()))->SetModifyAtAttr( &rNode );
+ rNode.SetModifyAtAttr( bSetModifyAtAttr );
+}
+
+static void SetParent( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ const SwFormat* pParentFormat,
+ const SwFormat* pConditionalFormat )
+{
+ const SwAttrSet* pAttrSet = static_cast<const SwAttrSet*>(rpAttrSet.get());
+ OSL_ENSURE( pAttrSet, "no SwAttrSet" );
+ OSL_ENSURE( pParentFormat || !pConditionalFormat, "ConditionalFormat without ParentFormat?" );
+
+ const SwAttrSet* pParentSet = pParentFormat ? &pParentFormat->GetAttrSet() : nullptr;
+
+ if ( pParentSet == pAttrSet->GetParent() )
+ return;
+
+ SwAttrSet aNewSet( *pAttrSet );
+ aNewSet.SetParent( pParentSet );
+ aNewSet.ClearItem( RES_FRMATR_STYLE_NAME );
+ aNewSet.ClearItem( RES_FRMATR_CONDITIONAL_STYLE_NAME );
+
+ if ( pParentFormat )
+ {
+ OUString sVal;
+ SwStyleNameMapper::FillProgName( pParentFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ const SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
+ aNewSet.Put( aAnyFormatColl );
+
+ if ( pConditionalFormat != pParentFormat )
+ SwStyleNameMapper::FillProgName( pConditionalFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+
+ const SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal );
+ aNewSet.Put( aFormatColl );
+ }
+
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+}
+
+static const SfxPoolItem* Put( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ const SfxPoolItem& rAttr )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+ const SfxPoolItem* pRet = aNewSet.Put( rAttr );
+ if ( pRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+ return pRet;
+}
+
+static bool Put( std::shared_ptr<const SfxItemSet>& rpAttrSet, const SwContentNode& rNode,
+ const SfxItemSet& rSet )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+
+ // #i76273# Robust
+ std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
+ if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
+ {
+ pStyleNames.emplace( *aNewSet.GetPool() );
+ pStyleNames->Put( aNewSet );
+ }
+
+ const bool bRet = aNewSet.Put( rSet );
+
+ // #i76273# Robust
+ if ( pStyleNames )
+ {
+ aNewSet.Put( *pStyleNames );
+ }
+
+ if ( bRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+
+ return bRet;
+}
+
+static bool Put_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode, const SfxPoolItem& rAttr,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+
+ // for a correct broadcast, we need to do a SetModifyAtAttr with the items
+ // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+
+ const bool bRet = aNewSet.Put_BC( rAttr, pOld, pNew );
+
+ if ( bRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+
+ return bRet;
+}
+
+static bool Put_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode, const SfxItemSet& rSet,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+
+ // #i76273# Robust
+ std::optional<SfxItemSetFixed<RES_FRMATR_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME>> pStyleNames;
+ if ( SfxItemState::SET == rSet.GetItemState( RES_FRMATR_STYLE_NAME, false ) )
+ {
+ pStyleNames.emplace( *aNewSet.GetPool() );
+ pStyleNames->Put( aNewSet );
+ }
+
+ // for a correct broadcast, we need to do a SetModifyAtAttr with the items
+ // from aNewSet. The 'regular' SetModifyAtAttr is done in GetNewAutoStyle
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+
+ const bool bRet = aNewSet.Put_BC( rSet, pOld, pNew );
+
+ // #i76273# Robust
+ if ( pStyleNames )
+ {
+ aNewSet.Put( *pStyleNames );
+ }
+
+ if ( bRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+
+ return bRet;
+}
+
+static sal_uInt16 ClearItem_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode, sal_uInt16 nWhich,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+ const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich, pOld, pNew );
+ if ( nRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+ return nRet;
+}
+
+static sal_uInt16 ClearItem_BC( std::shared_ptr<const SfxItemSet>& rpAttrSet,
+ const SwContentNode& rNode,
+ sal_uInt16 nWhich1, sal_uInt16 nWhich2,
+ SwAttrSet* pOld, SwAttrSet* pNew )
+{
+ SwAttrSet aNewSet( static_cast<const SwAttrSet&>(*rpAttrSet) );
+ if( rNode.GetModifyAtAttr() )
+ aNewSet.SetModifyAtAttr( &rNode );
+ const sal_uInt16 nRet = aNewSet.ClearItem_BC( nWhich1, nWhich2, pOld, pNew );
+ if ( nRet )
+ GetNewAutoStyle( rpAttrSet, rNode, aNewSet );
+ return nRet;
+}
+
+}
+
+/** Returns the section level at the position given by aIndex.
+ *
+ * We use the following logic:
+ * S = Start, E = End, C = ContentNode
+ * Level 0 = E
+ * 1 = S E
+ * 2 = SC
+ *
+ * All EndNodes of the BaseSection have level 0
+ * All StartNodes of the BaseSection have level 1
+ */
+sal_uInt16 SwNode::GetSectionLevel() const
+{
+ // EndNode of a BaseSection? They are always 0!
+ if( IsEndNode() && SwNodeOffset(0) == m_pStartOfSection->StartOfSectionIndex() )
+ return 0;
+
+ sal_uInt16 nLevel;
+ const SwNode* pNode = IsStartNode() ? this : m_pStartOfSection;
+ for( nLevel = 1; SwNodeOffset(0) != pNode->StartOfSectionIndex(); ++nLevel )
+ pNode = pNode->m_pStartOfSection;
+ return IsEndNode() ? nLevel-1 : nLevel;
+}
+
+#ifdef DBG_UTIL
+tools::Long SwNode::s_nSerial = 0;
+#endif
+
+SwNode::SwNode( const SwNodeIndex &rWhere, const SwNodeType nNdType )
+ : m_nNodeType( nNdType )
+ , m_nAFormatNumLvl( 0 )
+ , m_bIgnoreDontExpand( false)
+ , m_eMerge(Merge::None)
+#ifdef DBG_UTIL
+ , m_nSerial( s_nSerial++)
+#endif
+ , m_pStartOfSection( nullptr )
+{
+ if( !rWhere.GetIndex() )
+ return;
+
+ SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
+ SwNode* pNd = rNodes[ rWhere.GetIndex() -1 ];
+ rNodes.InsertNode( this, rWhere );
+ m_pStartOfSection = pNd->GetStartNode();
+ if( nullptr == m_pStartOfSection )
+ {
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ if( pNd->GetEndNode() ) // Skip EndNode ? Section
+ {
+ pNd = m_pStartOfSection;
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ }
+ }
+}
+
+/** Inserts a node into the rNodes array at the rWhere position
+ *
+ * @param rNodes the variable array in that the node will be inserted
+ * @param nPos position within the array where the node will be inserted
+ * @param nNdType the type of node to insert
+ */
+SwNode::SwNode( SwNodes& rNodes, SwNodeOffset nPos, const SwNodeType nNdType )
+ : m_nNodeType( nNdType )
+ , m_nAFormatNumLvl( 0 )
+ , m_bIgnoreDontExpand( false)
+ , m_eMerge(Merge::None)
+#ifdef DBG_UTIL
+ , m_nSerial( s_nSerial++)
+#endif
+ , m_pStartOfSection( nullptr )
+{
+ if( !nPos )
+ return;
+
+ SwNode* pNd = rNodes[ nPos - 1 ];
+ rNodes.InsertNode( this, nPos );
+ m_pStartOfSection = pNd->GetStartNode();
+ if( nullptr == m_pStartOfSection )
+ {
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ if( pNd->GetEndNode() ) // Skip EndNode ? Section!
+ {
+ pNd = m_pStartOfSection;
+ m_pStartOfSection = pNd->m_pStartOfSection;
+ }
+ }
+}
+
+SwNode::~SwNode()
+{
+ assert(m_aAnchoredFlys.empty() || GetDoc().IsInDtor()); // must all be deleted
+ InvalidateInSwCache(RES_OBJECTDYING);
+ assert(!IsInCache());
+}
+
+/// Find the TableNode in which it is located.
+/// If we're not in a table: return 0
+SwTableNode* SwNode::FindTableNode()
+{
+ if( IsTableNode() )
+ return GetTableNode();
+ SwStartNode* pTmp = m_pStartOfSection;
+ while( !pTmp->IsTableNode() && pTmp->GetIndex() )
+ pTmp = pTmp->m_pStartOfSection;
+ return pTmp->GetTableNode();
+}
+
+/// Is the node located in the visible area of the Shell?
+bool SwNode::IsInVisibleArea( SwViewShell const * pSh ) const
+{
+ bool bRet = false;
+ const SwContentNode* pNd;
+
+ if( SwNodeType::Start & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *this );
+ pNd = GetNodes().GoNext( &aIdx );
+ }
+ else if( SwNodeType::End & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode() );
+ pNd = SwNodes::GoPrevious( &aIdx );
+ }
+ else
+ pNd = GetContentNode();
+
+ if( !pSh )
+ // Get the Shell from the Doc
+ pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if( pSh )
+ {
+ const SwFrame* pFrame;
+ if (pNd && nullptr != (pFrame = pNd->getLayoutFrame(pSh->GetLayout(), nullptr, nullptr)))
+ {
+
+ if ( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+
+ if( !pFrame->isFrameAreaDefinitionValid() )
+ {
+ do
+ {
+ pFrame = pFrame->FindPrev();
+ }
+ while ( pFrame && !pFrame->isFrameAreaDefinitionValid() );
+ }
+
+ if( !pFrame || pSh->VisArea().Overlaps( pFrame->getFrameArea() ) )
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+bool SwNode::IsInProtectSect() const
+{
+ const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
+ const SwSectionNode* pSectNd = pNd->FindSectionNode();
+ return pSectNd && pSectNd->GetSection().IsProtectFlag();
+}
+
+/// Does the node contain anything protected?
+/// I.e.: Area/Frame/Table rows/... including the Anchor for
+/// Frames/Footnotes/...
+bool SwNode::IsProtect() const
+{
+ const SwNode* pNd = SwNodeType::Section == m_nNodeType ? m_pStartOfSection : this;
+ const SwStartNode* pSttNd = pNd->FindSectionNode();
+ if( pSttNd && static_cast<const SwSectionNode*>(pSttNd)->GetSection().IsProtectFlag() )
+ return true;
+
+ pSttNd = FindTableBoxStartNode();
+ if( nullptr != pSttNd )
+ {
+ SwContentFrame* pCFrame;
+ if( IsContentNode() && nullptr != (pCFrame = static_cast<const SwContentNode*>(this)->getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) ))
+ return pCFrame->IsProtected();
+
+ const SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().
+ GetTableBox( pSttNd->GetIndex() );
+ //Robust #149568
+ if( pBox && pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ return true;
+ }
+
+ SwFrameFormat* pFlyFormat = GetFlyFormat();
+ if( pFlyFormat )
+ {
+ if (pFlyFormat->GetProtect().IsContentProtected())
+ return true;
+ const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
+ const SwPosition* pAnchorPos = rAnchor.GetContentAnchor();
+ if (!pAnchorPos)
+ return false;
+ const SwNode& rAnchorNd = pAnchorPos->nNode.GetNode();
+ return &rAnchorNd != this && rAnchorNd.IsProtect();
+ }
+
+ pSttNd = FindFootnoteStartNode();
+ if( nullptr != pSttNd )
+ {
+ const SwTextFootnote* pTFootnote = GetDoc().GetFootnoteIdxs().SeekEntry(
+ SwNodeIndex( *pSttNd ) );
+ if( pTFootnote )
+ return pTFootnote->GetTextNode().IsProtect();
+ }
+
+ return false;
+}
+
+/// Find the PageDesc that is used to format this node. If the Layout is available,
+/// we search through that. Else we can only do it the hard way by searching onwards through the nodes.
+const SwPageDesc* SwNode::FindPageDesc( SwNodeOffset* pPgDescNdIdx ) const
+{
+ if ( !GetNodes().IsDocNodes() )
+ {
+ return nullptr;
+ }
+
+ const SwPageDesc* pPgDesc = nullptr;
+
+ const SwContentNode* pNode;
+ if( SwNodeType::Start & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *this );
+ pNode = GetNodes().GoNext( &aIdx );
+ }
+ else if( SwNodeType::End & m_nNodeType )
+ {
+ SwNodeIndex aIdx( *EndOfSectionNode() );
+ pNode = SwNodes::GoPrevious( &aIdx );
+ }
+ else
+ {
+ pNode = GetContentNode();
+ if( pNode )
+ pPgDesc = pNode->GetAttr( RES_PAGEDESC ).GetPageDesc();
+ }
+
+ // Are we going through the layout?
+ if( !pPgDesc )
+ {
+ const SwFrame* pFrame;
+ const SwPageFrame* pPage;
+ if (pNode && nullptr != (pFrame = pNode->getLayoutFrame(pNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr)) &&
+ nullptr != ( pPage = pFrame->FindPageFrame() ) )
+ {
+ pPgDesc = pPage->GetPageDesc();
+ if ( pPgDescNdIdx )
+ {
+ *pPgDescNdIdx = pNode->GetIndex();
+ }
+ }
+ }
+
+ if( !pPgDesc )
+ {
+ // Thus via the nodes array
+ const SwDoc& rDoc = GetDoc();
+ const SwNode* pNd = this;
+ const SwStartNode* pSttNd;
+ if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() &&
+ nullptr != ( pSttNd = pNd->FindFlyStartNode() ) )
+ {
+ // Find the right Anchor first
+ const SwFrameFormat* pFormat = nullptr;
+ const SwFrameFormats& rFormats = *rDoc.GetSpzFrameFormats();
+
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFrameFormat = rFormats[ n ];
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() &&
+ &rContent.GetContentIdx()->GetNode() == static_cast<SwNode const *>(pSttNd) )
+ {
+ pFormat = pFrameFormat;
+ break;
+ }
+ }
+
+ if( pFormat )
+ {
+ const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) &&
+ pAnchor->GetContentAnchor() )
+ {
+ pNd = &pAnchor->GetContentAnchor()->nNode.GetNode();
+ const SwNode* pFlyNd = pNd->FindFlyStartNode();
+ while( pFlyNd )
+ {
+ // Get up through the Anchor
+ size_t n;
+ for( n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFrameFormat = rFormats[ n ];
+ const SwNodeIndex* pIdx = pFrameFormat->GetContent().
+ GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ {
+ if( pFormat == pFrameFormat )
+ {
+ pNd = pFlyNd;
+ pFlyNd = nullptr;
+ break;
+ }
+ pAnchor = &pFrameFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId()) ||
+ !pAnchor->GetContentAnchor() )
+ {
+ pFlyNd = nullptr;
+ break;
+ }
+
+ pFlyNd = pAnchor->GetContentAnchor()->nNode.
+ GetNode().FindFlyStartNode();
+ break;
+ }
+ }
+ if( n >= rFormats.size() )
+ {
+ OSL_ENSURE( false, "FlySection, but no Format found" );
+ return nullptr;
+ }
+ }
+ }
+ }
+ // pNd should now contain the correct Anchor or it's still this
+ }
+
+ if( pNd->GetIndex() < GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ if( pNd->GetIndex() > GetNodes().GetEndOfAutotext().GetIndex() )
+ {
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ pNd = nullptr;
+ }
+ else
+ {
+ // Find the Body text node
+ if( nullptr != ( pSttNd = pNd->FindHeaderStartNode() ) ||
+ nullptr != ( pSttNd = pNd->FindFooterStartNode() ))
+ {
+ // Then find this StartNode in the PageDescs
+ sal_uInt16 nId;
+ UseOnPage eAskUse;
+ if( SwHeaderStartNode == pSttNd->GetStartNodeType())
+ {
+ nId = RES_HEADER;
+ eAskUse = UseOnPage::HeaderShare;
+ }
+ else
+ {
+ nId = RES_FOOTER;
+ eAskUse = UseOnPage::FooterShare;
+ }
+
+ for( size_t n = rDoc.GetPageDescCnt(); n && !pPgDesc; )
+ {
+ const SwPageDesc& rPgDsc = rDoc.GetPageDesc( --n );
+ const SwFrameFormat* pFormat = &rPgDsc.GetMaster();
+ int nStt = 0, nLast = 1;
+ if( !( eAskUse & rPgDsc.ReadUseOn() )) ++nLast;
+
+ for( ; nStt < nLast; ++nStt, pFormat = &rPgDsc.GetLeft() )
+ {
+ const SwFrameFormat * pHdFtFormat = nId == RES_HEADER
+ ? static_cast<SwFormatHeader const &>(
+ pFormat->GetFormatAttr(nId)).GetHeaderFormat()
+ : static_cast<SwFormatFooter const &>(
+ pFormat->GetFormatAttr(nId)).GetFooterFormat();
+ if( pHdFtFormat )
+ {
+ const SwFormatContent& rContent = pHdFtFormat->GetContent();
+ if( rContent.GetContentIdx() &&
+ &rContent.GetContentIdx()->GetNode() ==
+ static_cast<SwNode const *>(pSttNd) )
+ {
+ pPgDesc = &rPgDsc;
+ break;
+ }
+ }
+ }
+ }
+
+ if( !pPgDesc )
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ pNd = nullptr;
+ }
+ else if( nullptr != ( pSttNd = pNd->FindFootnoteStartNode() ))
+ {
+ // the Anchor can only be in the Body text
+ const SwTextFootnote* pTextFootnote;
+ const SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs();
+ for( size_t n = 0; n < rFootnoteArr.size(); ++n )
+ if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
+ static_cast<SwNode const *>(pSttNd) ==
+ &pTextFootnote->GetStartNode()->GetNode() )
+ {
+ pNd = &pTextFootnote->GetTextNode();
+ break;
+ }
+ }
+ else
+ {
+ // Can only be a page-bound Fly (or something newer).
+ // we can only return the standard here
+ OSL_ENSURE( pNd->FindFlyStartNode(),
+ "Where is this Node?" );
+
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ pNd = nullptr;
+ }
+ }
+ }
+
+ if( pNd )
+ {
+ SwFindNearestNode aInfo( *pNd );
+ // Over all Nodes of all PageDescs
+ for (const SfxPoolItem* pItem : rDoc.GetAttrPool().GetItemSurrogates(RES_PAGEDESC))
+ {
+ auto pPageDescItem = dynamic_cast<const SwFormatPageDesc*>(pItem);
+ if( pPageDescItem && pPageDescItem->GetDefinedIn() )
+ {
+ const sw::BroadcastingModify* pMod = pPageDescItem->GetDefinedIn();
+ if( auto pContentNode = dynamic_cast<const SwContentNode*>( pMod) )
+ aInfo.CheckNode( *pContentNode );
+ else if( auto pFormat = dynamic_cast<const SwFormat*>( pMod) )
+ pFormat->GetInfo( aInfo );
+ }
+ }
+
+ pNd = aInfo.GetFoundNode();
+ if( nullptr != pNd )
+ {
+ if( pNd->IsContentNode() )
+ pPgDesc = pNd->GetContentNode()->GetAttr( RES_PAGEDESC ).GetPageDesc();
+ else if( pNd->IsTableNode() )
+ pPgDesc = pNd->GetTableNode()->GetTable().
+ GetFrameFormat()->GetPageDesc().GetPageDesc();
+ else if( pNd->IsSectionNode() )
+ pPgDesc = pNd->GetSectionNode()->GetSection().
+ GetFormat()->GetPageDesc().GetPageDesc();
+ if ( pPgDescNdIdx )
+ {
+ *pPgDescNdIdx = pNd->GetIndex();
+ }
+ }
+ if( !pPgDesc )
+ pPgDesc = &rDoc.GetPageDesc( 0 );
+ }
+ }
+ return pPgDesc;
+}
+
+/// If the node is located in a Fly, we return it formatted accordingly
+SwFrameFormat* SwNode::GetFlyFormat() const
+{
+ SwFrameFormat* pRet = nullptr;
+ const SwNode* pSttNd = FindFlyStartNode();
+ if( pSttNd )
+ {
+ if( IsContentNode() )
+ {
+ SwContentFrame* pFrame = SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<const SwContentNode*>(this)).First();
+ if( pFrame )
+ pRet = pFrame->FindFlyFrame()->GetFormat();
+ }
+ if( !pRet )
+ {
+ // The hard way through the Doc is our last way out
+ const SwFrameFormats& rFrameFormatTable = *GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
+ {
+ SwFrameFormat* pFormat = rFrameFormatTable[n];
+ // Only Writer fly frames can contain Writer nodes.
+ if (pFormat->Which() != RES_FLYFRMFMT)
+ continue;
+ const SwFormatContent& rContent = pFormat->GetContent();
+ if( rContent.GetContentIdx() &&
+ &rContent.GetContentIdx()->GetNode() == pSttNd )
+ {
+ pRet = pFormat;
+ break;
+ }
+ }
+ }
+ }
+ return pRet;
+}
+
+SwTableBox* SwNode::GetTableBox() const
+{
+ SwTableBox* pBox = nullptr;
+ const SwNode* pSttNd = FindTableBoxStartNode();
+ if( pSttNd )
+ pBox = const_cast<SwTableBox*>(pSttNd->FindTableNode()->GetTable().GetTableBox(
+ pSttNd->GetIndex() ));
+ return pBox;
+}
+
+SwStartNode* SwNode::FindSttNodeByType( SwStartNodeType eTyp )
+{
+ SwStartNode* pTmp = IsStartNode() ? static_cast<SwStartNode*>(this) : m_pStartOfSection;
+
+ while( eTyp != pTmp->GetStartNodeType() && pTmp->GetIndex() )
+ pTmp = pTmp->m_pStartOfSection;
+ return eTyp == pTmp->GetStartNodeType() ? pTmp : nullptr;
+}
+
+const SwTextNode* SwNode::FindOutlineNodeOfLevel(sal_uInt8 const nLvl,
+ SwRootFrame const*const pLayout) const
+{
+ const SwTextNode* pRet = nullptr;
+ const SwOutlineNodes& rONds = GetNodes().GetOutLineNds();
+ if( MAXLEVEL > nLvl && !rONds.empty() )
+ {
+ SwOutlineNodes::size_type nPos;
+ SwNode* pNd = const_cast<SwNode*>(this);
+ bool bCheckFirst = false;
+ if( !rONds.Seek_Entry( pNd, &nPos ))
+ {
+ if (nPos == 0)
+ bCheckFirst = true;
+ }
+ else
+ {
+ ++nPos;
+ }
+
+ if( bCheckFirst )
+ {
+ // The first OutlineNode comes after the one asking.
+ // Test if both are on the same page.
+ // If not it's invalid.
+ for (nPos = 0; nPos < rONds.size(); ++nPos)
+ {
+ pRet = rONds[nPos]->GetTextNode();
+ if (!pLayout || sw::IsParaPropsNode(*pLayout, *pRet))
+ {
+ break;
+ }
+ }
+ if (nPos == rONds.size())
+ {
+ return nullptr;
+ }
+
+ const SwContentNode* pCNd = GetContentNode();
+
+ Point aPt( 0, 0 );
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwFrame* pFrame = pRet->getLayoutFrame(pRet->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp),
+ * pMyFrame = pCNd ? pCNd->getLayoutFrame(pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp) : nullptr;
+ const SwPageFrame* pPgFrame = pFrame ? pFrame->FindPageFrame() : nullptr;
+ if( pPgFrame && pMyFrame &&
+ pPgFrame->getFrameArea().Top() > pMyFrame->getFrameArea().Top() )
+ {
+ // The one asking precedes the Page, thus its invalid
+ pRet = nullptr;
+ }
+ }
+ else
+ {
+ for ( ; 0 < nPos; --nPos)
+ {
+ SwTextNode const*const pNode = rONds[nPos - 1]->GetTextNode();
+ if ((nPos == 1 /*as before*/ || pNode->GetAttrOutlineLevel() - 1 <= nLvl)
+ && (!pLayout || sw::IsParaPropsNode(*pLayout, *pNode)))
+ {
+ pRet = pNode;
+ break;
+ }
+ }
+ }
+ }
+ return pRet;
+}
+
+static bool IsValidNextPrevNd( const SwNode& rNd )
+{
+ return SwNodeType::Table == rNd.GetNodeType() ||
+ ( SwNodeType::ContentMask & rNd.GetNodeType() ) ||
+ ( SwNodeType::End == rNd.GetNodeType() && rNd.StartOfSectionNode() &&
+ SwNodeType::Table == rNd.StartOfSectionNode()->GetNodeType() );
+}
+
+sal_uInt8 SwNode::HasPrevNextLayNode() const
+{
+ // assumption: <this> node is a node inside the document nodes array section.
+
+ sal_uInt8 nRet = 0;
+ if( IsValidNextPrevNd( *this ))
+ {
+ SwNodeIndex aIdx( *this, -1 );
+ // #i77805# - skip section start and end nodes
+ while ( aIdx.GetNode().IsSectionNode() ||
+ ( aIdx.GetNode().IsEndNode() &&
+ aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
+ {
+ --aIdx;
+ }
+ if( IsValidNextPrevNd( aIdx.GetNode() ))
+ nRet |= ND_HAS_PREV_LAYNODE;
+ // #i77805# - skip section start and end nodes
+ aIdx.Assign(*this, +1);
+ while ( aIdx.GetNode().IsSectionNode() ||
+ ( aIdx.GetNode().IsEndNode() &&
+ aIdx.GetNode().StartOfSectionNode()->IsSectionNode() ) )
+ {
+ ++aIdx;
+ }
+ if( IsValidNextPrevNd( aIdx.GetNode() ))
+ nRet |= ND_HAS_NEXT_LAYNODE;
+ }
+ return nRet;
+}
+
+void SwNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ const char* pName = "???";
+ switch (GetNodeType())
+ {
+ case SwNodeType::End:
+ pName = "end";
+ break;
+ case SwNodeType::Start:
+ case SwNodeType::Text:
+ case SwNodeType::Ole:
+ abort(); // overridden
+ case SwNodeType::Table:
+ pName = "table";
+ break;
+ case SwNodeType::Grf:
+ pName = "grf";
+ break;
+ default: break;
+ }
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
+
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ switch (GetNodeType())
+ {
+ case SwNodeType::Grf:
+ {
+ auto pNoTextNode = static_cast<const SwNoTextNode*>(this);
+ const tools::PolyPolygon* pContour = pNoTextNode->HasContour();
+ if (pContour)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pContour"));
+ for (sal_uInt16 i = 0; i < pContour->Count(); ++i)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("polygon"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(i).getStr()));
+ const tools::Polygon& rPolygon = pContour->GetObject(i);
+ for (sal_uInt16 j = 0; j < rPolygon.GetSize(); ++j)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(j).getStr()));
+ const Point& rPoint = rPolygon.GetPoint(j);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("x"),
+ BAD_CAST(OString::number(rPoint.X()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("y"),
+ BAD_CAST(OString::number(rPoint.Y()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+ if (GetNodeType() == SwNodeType::End)
+ (void)xmlTextWriterEndElement(pWriter); // end start node
+}
+
+SwStartNode::SwStartNode( const SwNodeIndex &rWhere, const SwNodeType nNdType,
+ SwStartNodeType eSttNd )
+ : SwNode( rWhere, nNdType ), m_eStartNodeType( eSttNd )
+{
+ if( !rWhere.GetIndex() )
+ {
+ SwNodes& rNodes = const_cast<SwNodes&> (rWhere.GetNodes());
+ rNodes.InsertNode( this, rWhere );
+ m_pStartOfSection = this;
+ }
+ // Just do this temporarily until the EndNode is inserted
+ m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
+}
+
+SwStartNode::SwStartNode( SwNodes& rNodes, SwNodeOffset nPos )
+ : SwNode( rNodes, nPos, SwNodeType::Start ), m_eStartNodeType( SwNormalStartNode )
+{
+ if( !nPos )
+ {
+ rNodes.InsertNode( this, nPos );
+ m_pStartOfSection = this;
+ }
+ // Just do this temporarily until the EndNode is inserted
+ m_pEndOfSection = reinterpret_cast<SwEndNode*>(this);
+}
+
+void SwStartNode::CheckSectionCondColl() const
+{
+//FEATURE::CONDCOLL
+ SwNodeIndex aIdx( *this );
+ SwNodeOffset nEndIdx = EndOfSectionIndex();
+ const SwNodes& rNds = GetNodes();
+ SwContentNode* pCNd;
+ while( nullptr != ( pCNd = rNds.GoNext( &aIdx )) && pCNd->GetIndex() < nEndIdx )
+ pCNd->ChkCondColl();
+//FEATURE::CONDCOLL
+}
+
+void SwStartNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ const char* pName = "???";
+ switch (GetNodeType())
+ {
+ case SwNodeType::Table:
+ pName = "table";
+ break;
+ default:
+ switch(GetStartNodeType())
+ {
+ case SwNormalStartNode:
+ pName = "start";
+ break;
+ case SwTableBoxStartNode:
+ pName = "tablebox";
+ break;
+ case SwFlyStartNode:
+ pName = "fly";
+ break;
+ case SwFootnoteStartNode:
+ pName = "footnote";
+ break;
+ case SwHeaderStartNode:
+ pName = "header";
+ break;
+ case SwFooterStartNode:
+ pName = "footer";
+ break;
+ }
+ break;
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("type"), BAD_CAST(OString::number(static_cast<sal_uInt8>(GetNodeType())).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ if (GetStartNodeType() == SwTableBoxStartNode)
+ {
+ if (SwTableBox* pBox = GetTableBox())
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("rowspan"), BAD_CAST(OString::number(pBox->getRowSpan()).getStr()));
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("attrset"));
+ if (SwTableBox* pBox = GetTableBox())
+ pBox->GetFrameFormat()->GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ // (void)xmlTextWriterEndElement(pWriter); - it is a start node, so don't end, will make xml better nested
+}
+
+
+/** Insert a node into the array
+ *
+ * The StartOfSection pointer is set to the given node.
+ *
+ * The EndOfSection pointer of the corresponding start node is set to this node.
+ *
+ * @param rWhere position where the node should be inserted
+ * @param rSttNd the start note of the section
+ */
+
+SwEndNode::SwEndNode( const SwNodeIndex &rWhere, SwStartNode& rSttNd )
+ : SwNode( rWhere, SwNodeType::End )
+{
+ m_pStartOfSection = &rSttNd;
+ m_pStartOfSection->m_pEndOfSection = this;
+}
+
+SwEndNode::SwEndNode( SwNodes& rNds, SwNodeOffset nPos, SwStartNode& rSttNd )
+ : SwNode( rNds, nPos, SwNodeType::End )
+{
+ m_pStartOfSection = &rSttNd;
+ m_pStartOfSection->m_pEndOfSection = this;
+}
+
+SwContentNode::SwContentNode( const SwNodeIndex &rWhere, const SwNodeType nNdType,
+ SwFormatColl *pColl )
+ : SwNode( rWhere, nNdType )
+ , m_aCondCollListener( *this )
+ , m_pCondColl( nullptr )
+ , mbSetModifyAtAttr( false )
+{
+ if(pColl)
+ pColl->Add(this);
+}
+
+SwContentNode::~SwContentNode()
+{
+ // The base class SwClient of SwFrame excludes itself from the dependency list!
+ // Thus, we need to delete all Frames in the dependency list.
+ if (!IsTextNode()) // see ~SwTextNode
+ {
+ DelFrames(nullptr);
+ }
+
+ m_aCondCollListener.EndListeningAll();
+ m_pCondColl = nullptr;
+
+ if ( mpAttrSet && mbSetModifyAtAttr )
+ const_cast<SwAttrSet*>(static_cast<const SwAttrSet*>(mpAttrSet.get()))->SetModifyAtAttr( nullptr );
+ InvalidateInSwCache(RES_OBJECTDYING);
+}
+void SwContentNode::UpdateAttr(const SwUpdateAttr& rUpdate)
+{
+ if (GetNodes().IsDocNodes()
+ && IsTextNode()
+ && RES_ATTRSET_CHG == rUpdate.getWhichAttr())
+ static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
+ CallSwClientNotify(sw::LegacyModifyHint(&rUpdate, &rUpdate));
+}
+
+void SwContentNode::SwClientNotify( const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyHint->GetWhich();
+ InvalidateInSwCache(nWhich);
+
+ bool bSetParent = false;
+ bool bCalcHidden = false;
+ SwFormatColl* pFormatColl = nullptr;
+ switch(nWhich)
+ {
+ case RES_OBJECTDYING:
+ {
+ SwFormat* pFormat = pLegacyHint->m_pNew
+ ? static_cast<SwFormat*>(static_cast<const SwPtrMsgPoolItem*>(pLegacyHint->m_pNew)->pObject)
+ : nullptr;
+ // Do not mangle pointers if it is the upper-most format!
+ if(pFormat && GetRegisteredIn() == pFormat)
+ {
+ if(pFormat->GetRegisteredIn())
+ {
+ // If Parent, register anew in the new Parent
+ pFormat->GetRegisteredIn()->Add(this);
+ pFormatColl = GetFormatColl();
+ }
+ else
+ EndListeningAll();
+ bSetParent = true;
+ }
+ }
+ break;
+
+ case RES_FMT_CHG:
+ // If the Format parent was switched, register the Attrset at the new one
+ // Skip own Modify!
+ if(GetpSwAttrSet()
+ && pLegacyHint->m_pNew
+ && static_cast<const SwFormatChg*>(pLegacyHint->m_pNew)->pChangedFormat == GetRegisteredIn())
+ {
+ pFormatColl = GetFormatColl();
+ bSetParent = true;
+ }
+ break;
+
+ case RES_ATTRSET_CHG:
+ if (GetNodes().IsDocNodes()
+ && IsTextNode()
+ && pLegacyHint->m_pOld
+ && SfxItemState::SET == pLegacyHint->m_pOld->StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItemState(RES_CHRATR_HIDDEN, false))
+ bCalcHidden = true;
+ break;
+
+ case RES_UPDATE_ATTR:
+ // RES_UPDATE_ATTR _should_ always contain a SwUpdateAttr hint in old and new.
+ // However, faking one with just a basic SfxPoolItem setting a WhichId has been observed.
+ // This makes the crude "WhichId" type divert from the true type, which is bad.
+ // Thus we are asserting here, but falling back to an proper
+ // hint instead. so that we at least will not spread such poison further.
+#ifdef DBG_UTIL
+ if(pLegacyHint->m_pNew != pLegacyHint->m_pOld)
+ {
+ auto pBT = sal::backtrace_get(20);
+ SAL_WARN("sw.core", "UpdateAttr not matching! " << sal::backtrace_to_string(pBT.get()));
+ }
+#endif
+ assert(pLegacyHint->m_pNew == pLegacyHint->m_pOld);
+ assert(dynamic_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew));
+ const SwUpdateAttr aFallbackHint(0,0,0);
+ const SwUpdateAttr& rUpdateAttr = pLegacyHint->m_pNew ? *static_cast<const SwUpdateAttr*>(pLegacyHint->m_pNew) : aFallbackHint;
+ UpdateAttr(rUpdateAttr);
+ return;
+ }
+ if(bSetParent && GetpSwAttrSet())
+ AttrSetHandleHelper::SetParent(mpAttrSet, *this, pFormatColl, pFormatColl);
+ if(bCalcHidden)
+ static_cast<SwTextNode*>(this)->SetCalcHiddenCharFlags();
+ CallSwClientNotify(rHint);
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ m_pCondColl = const_cast<SwFormatColl*>(static_cast<const SwFormatColl*>(pModifyChangedHint->m_pNew));
+ }
+ else if(auto pCondCollCondChgHint = dynamic_cast<const sw::CondCollCondChg*>(&rHint))
+ {
+ ChkCondColl(&pCondCollCondChgHint->m_rColl);
+ }
+}
+
+bool SwContentNode::InvalidateNumRule()
+{
+ SwNumRule* pRule = nullptr;
+ const SfxPoolItem* pItem;
+ if( GetNodes().IsDocNodes() &&
+ nullptr != ( pItem = GetNoCondAttr( RES_PARATR_NUMRULE, true )) &&
+ !static_cast<const SwNumRuleItem*>(pItem)->GetValue().isEmpty() &&
+ nullptr != (pRule = GetDoc().FindNumRulePtr(
+ static_cast<const SwNumRuleItem*>(pItem)->GetValue() ) ) )
+ {
+ pRule->SetInvalidRule( true );
+ }
+ return nullptr != pRule;
+}
+
+SwContentFrame *SwContentNode::getLayoutFrame( const SwRootFrame* _pRoot,
+ const SwPosition *const pPos,
+ std::pair<Point, bool> const*const pViewPosAndCalcFrame) const
+{
+ return static_cast<SwContentFrame*>( ::GetFrameOfModify( _pRoot, *this, FRM_CNTNT,
+ pPos, pViewPosAndCalcFrame));
+}
+
+SwRect SwContentNode::FindLayoutRect( const bool bPrtArea, const Point* pPoint ) const
+{
+ SwRect aRet;
+ std::pair<Point, bool> tmp;
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = false;
+ }
+ SwContentFrame* pFrame = static_cast<SwContentFrame*>( ::GetFrameOfModify( nullptr, *this,
+ FRM_CNTNT, nullptr, pPoint ? &tmp : nullptr) );
+ if( pFrame )
+ aRet = bPrtArea ? pFrame->getFramePrintArea() : pFrame->getFrameArea();
+ return aRet;
+}
+
+SwRect SwContentNode::FindPageFrameRect() const
+{
+ SwRect aRet;
+ SwFrame* pFrame = ::GetFrameOfModify( nullptr, *this, FRM_CNTNT );
+ if( pFrame && nullptr != ( pFrame = pFrame->FindPageFrame() ))
+ aRet = pFrame->getFrameArea();
+ return aRet;
+}
+
+sal_Int32 SwContentNode::Len() const { return 0; }
+
+SwFormatColl *SwContentNode::ChgFormatColl( SwFormatColl *pNewColl )
+{
+ OSL_ENSURE( pNewColl, "Collectionpointer is 0." );
+ SwFormatColl *pOldColl = GetFormatColl();
+
+ if( pNewColl != pOldColl )
+ {
+ pNewColl->Add( this );
+
+ // Set the Parent of out AutoAttributes to the new Collection
+ if( GetpSwAttrSet() )
+ AttrSetHandleHelper::SetParent( mpAttrSet, *this, pNewColl, pNewColl );
+
+ SetCondFormatColl( nullptr );
+
+ if( !IsModifyLocked() )
+ {
+ assert(dynamic_cast<SwTextFormatColl*>(pNewColl));
+ ChkCondColl(static_cast<SwTextFormatColl*>(pNewColl));
+ SwFormatChg aTmp1( pOldColl );
+ SwFormatChg aTmp2( pNewColl );
+ CallSwClientNotify( sw::LegacyModifyHint(&aTmp1, &aTmp2) );
+ }
+ }
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ return pOldColl;
+}
+
+bool SwContentNode::GoNext(SwIndex * pIdx, sal_uInt16 nMode ) const
+{
+ bool bRet = true;
+ if( pIdx->GetIndex() < Len() )
+ {
+ if( !IsTextNode() )
+ ++(*pIdx);
+ else
+ {
+ const SwTextNode& rTNd = *GetTextNode();
+ sal_Int32 nPos = pIdx->GetIndex();
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_Int32 nDone = 0;
+ sal_uInt16 nItrMode = ( CRSR_SKIP_CELLS & nMode ) ?
+ CharacterIteratorMode::SKIPCELL :
+ CharacterIteratorMode::SKIPCONTROLCHARACTER;
+ nPos = g_pBreakIt->GetBreakIter()->nextCharacters( rTNd.GetText(), nPos,
+ g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
+ nItrMode, 1, nDone );
+
+ // Check if nPos is inside hidden text range:
+ if ( CRSR_SKIP_HIDDEN & nMode )
+ {
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
+ if ( nHiddenStart != COMPLETE_STRING && nHiddenStart != nPos )
+ nPos = nHiddenEnd;
+ }
+
+ if( 1 == nDone )
+ *pIdx = nPos;
+ else
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+bool SwContentNode::GoPrevious(SwIndex * pIdx, sal_uInt16 nMode ) const
+{
+ bool bRet = true;
+ if( pIdx->GetIndex() > 0 )
+ {
+ if( !IsTextNode() )
+ --(*pIdx);
+ else
+ {
+ const SwTextNode& rTNd = *GetTextNode();
+ sal_Int32 nPos = pIdx->GetIndex();
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ sal_Int32 nDone = 0;
+ sal_uInt16 nItrMode = ( CRSR_SKIP_CELLS & nMode ) ?
+ CharacterIteratorMode::SKIPCELL :
+ CharacterIteratorMode::SKIPCONTROLCHARACTER;
+ nPos = g_pBreakIt->GetBreakIter()->previousCharacters( rTNd.GetText(), nPos,
+ g_pBreakIt->GetLocale( rTNd.GetLang( nPos ) ),
+ nItrMode, 1, nDone );
+
+ // Check if nPos is inside hidden text range:
+ if ( CRSR_SKIP_HIDDEN & nMode )
+ {
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ SwScriptInfo::GetBoundsOfHiddenRange( rTNd, nPos, nHiddenStart, nHiddenEnd );
+ if ( nHiddenStart != COMPLETE_STRING )
+ nPos = nHiddenStart;
+ }
+
+ if( 1 == nDone )
+ *pIdx = nPos;
+ else
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+/**
+ * Creates all Views for the Doc for this Node.
+ * The created ContentFrames are attached to the corresponding Layout.
+ */
+void SwContentNode::MakeFramesForAdjacentContentNode(SwContentNode& rNode)
+{
+ OSL_ENSURE( &rNode != this,
+ "No ContentNode or CopyNode and new Node identical." );
+
+ if( !HasWriterListeners() || &rNode == this ) // Do we actually have Frames?
+ return;
+
+ SwFrame *pFrame;
+ SwLayoutFrame *pUpper;
+ // Create Frames for Nodes which come after the Table?
+ OSL_ENSURE( FindTableNode() == rNode.FindTableNode(), "Table confusion" );
+
+ SwNode2Layout aNode2Layout( *this, rNode.GetIndex() );
+
+ while( nullptr != (pUpper = aNode2Layout.UpperFrame( pFrame, rNode )) )
+ {
+ if (pUpper->getRootFrame()->HasMergedParas()
+ && !rNode.IsCreateFrameWhenHidingRedlines())
+ {
+ continue;
+ }
+ SwFrame *pNew = rNode.MakeFrame( pUpper );
+ pNew->Paste( pUpper, pFrame );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pNew->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ auto pPrev = pNew->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ }
+}
+
+/**
+ * Deletes all Views from the Doc for this Node.
+ * The ContentFrames are removed from the corresponding Layout.
+ */
+void SwContentNode::DelFrames(SwRootFrame const*const pLayout)
+{
+ if( !HasWriterListeners() )
+ return;
+
+ SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for( SwContentFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ if (pLayout && pLayout != pFrame->getRootFrame())
+ {
+ continue; // skip it
+ }
+ if (pFrame->IsTextFrame())
+ {
+ if (sw::MergedPara * pMerged =
+ static_cast<SwTextFrame *>(pFrame)->GetMergedPara())
+ {
+ if (this != pMerged->pFirstNode)
+ {
+ // SwNodes::RemoveNode iterates *backwards* - so
+ // ensure there are no more extents pointing to this
+ // node as SwFrame::InvalidatePage() will access them.
+ // Note: cannot send via SwClientNotify from dtor
+ // because that would access deleted wrong-lists
+ sw::UpdateMergedParaForDelete(*pMerged, true,
+ *static_cast<SwTextNode*>(this), 0, Len());
+ if (this == pMerged->pParaPropsNode)
+ {
+ // otherwise pointer should have been updated to a different node
+ assert(this == pMerged->pLastNode);
+ assert(pMerged->extents.empty());
+ for (SwNodeOffset i = pMerged->pLastNode->GetIndex() - 1;;
+ --i)
+ {
+ assert(pMerged->pFirstNode->GetIndex() <= i);
+ SwNode *const pNode(GetNodes()[i]);
+ if (pNode->IsTextNode()
+ && pNode->GetRedlineMergeFlag() != Merge::Hidden)
+ {
+ pMerged->pParaPropsNode = pNode->GetTextNode();
+ break;
+ }
+ else if (pMerged->pFirstNode->GetIndex() == i)
+ { // this can only happen when called from CheckParaRedlineMerge()
+ // and the pMerged will be deleted anyway
+ pMerged->pParaPropsNode = pMerged->pFirstNode;
+ break;
+ }
+ }
+ assert(pMerged->listener.IsListeningTo(pMerged->pParaPropsNode));
+ }
+ assert(GetIndex() <= pMerged->pLastNode->GetIndex());
+ if (this == pMerged->pLastNode)
+ {
+ // tdf#130680 find the previous node that is a
+ // listener of pMerged; see CheckParaRedlineMerge()
+ for (SwNodeOffset i = GetIndex() - 1;
+ this == pMerged->pLastNode; --i)
+ {
+ SwNode *const pNode = GetNodes()[i];
+ if (pNode->IsTextNode())
+ {
+ pMerged->pLastNode = pNode->GetTextNode();
+ }
+ else if (SwEndNode const*const pEnd = pNode->GetEndNode())
+ {
+ SwStartNode const*const pStart(pEnd->StartOfSectionNode());
+ i = pStart->GetIndex(); // skip table or section
+ }
+ }
+ assert(pMerged->pFirstNode->GetIndex() <= pMerged->pLastNode->GetIndex());
+ assert(pMerged->listener.IsListeningTo(pMerged->pLastNode));
+ }
+ // avoid re-parenting mess (ModifyChangedHint)
+ pMerged->listener.EndListening(this);
+ continue; // don't delete
+ }
+ }
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+#endif
+ }
+
+ if( pFrame->IsFollow() )
+ {
+ SwContentFrame* pMaster = pFrame->FindMaster();
+ pMaster->SetFollow( pFrame->GetFollow() );
+ }
+ pFrame->SetFollow( nullptr );//So it doesn't get funny ideas.
+ //Otherwise it could be possible that a follow
+ //gets destroyed before its master. Following
+ //the now invalid pointer will then lead to an
+ //illegal memory access. The chain can be
+ //crushed here because we'll destroy all of it
+ //anyway.
+
+ if( pFrame->GetUpper() && pFrame->IsInFootnote() && !pFrame->GetIndNext() &&
+ !pFrame->GetIndPrev() )
+ {
+ SwFootnoteFrame *pFootnote = pFrame->FindFootnoteFrame();
+ OSL_ENSURE( pFootnote, "You promised a FootnoteFrame?" );
+ SwContentFrame* pCFrame;
+ if( !pFootnote->GetFollow() && !pFootnote->GetMaster() &&
+ nullptr != ( pCFrame = pFootnote->GetRefFromAttr()) && pCFrame->IsFollow() )
+ {
+ OSL_ENSURE( pCFrame->IsTextFrame(), "NoTextFrame has Footnote?" );
+ pCFrame->FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone );
+ }
+ }
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+}
+
+SwContentNode *SwContentNode::JoinNext()
+{
+ return this;
+}
+
+/// Get info from Modify
+bool SwContentNode::GetInfo( SfxPoolItem& rInfo ) const
+{
+ switch( rInfo.Which() )
+ {
+ case RES_AUTOFMT_DOCNODE:
+ if( &GetNodes() == static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes )
+ {
+ return false;
+ }
+ break;
+
+ case RES_FINDNEARESTNODE:
+ if( GetAttr( RES_PAGEDESC ).GetPageDesc() )
+ static_cast<SwFindNearestNode&>(rInfo).CheckNode( *this );
+ return true;
+
+ case RES_CONTENT_VISIBLE:
+ {
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject =
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*this).First();
+ }
+ return false;
+ }
+
+ return sw::BroadcastingModify::GetInfo( rInfo );
+}
+
+/// @param rAttr the attribute to set
+bool SwContentNode::SetAttr(const SfxPoolItem& rAttr )
+{
+ if( !GetpSwAttrSet() ) // Have the Nodes created by the corresponding AttrSets
+ NewAttrSet( GetDoc().GetAttrPool() );
+
+ OSL_ENSURE( GetpSwAttrSet(), "Why did't we create an AttrSet?");
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ bool bRet = false;
+ // If Modify is locked, we do not send any Modifys
+ if( IsModifyLocked() ||
+ ( !HasWriterListeners() && RES_PARATR_NUMRULE != rAttr.Which() ))
+ {
+ bRet = nullptr != AttrSetHandleHelper::Put( mpAttrSet, *this, rAttr );
+ }
+ else
+ {
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rAttr, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ }
+ return bRet;
+}
+
+bool SwContentNode::SetAttr( const SfxItemSet& rSet )
+{
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ if( const SwFormatAutoFormat* pFnd = rSet.GetItemIfSet( RES_AUTO_STYLE, false ) )
+ {
+ OSL_ENSURE( rSet.Count() == 1, "SetAutoStyle mixed with other attributes?!" );
+
+ // If there already is an attribute set (usually containing a numbering
+ // item), we have to merge the attribute of the new set into the old set:
+ bool bSetParent = true;
+ if ( GetpSwAttrSet() )
+ {
+ bSetParent = false;
+ AttrSetHandleHelper::Put( mpAttrSet, *this, *pFnd->GetStyleHandle() );
+ }
+ else
+ {
+ mpAttrSet = pFnd->GetStyleHandle();
+ }
+
+ if ( bSetParent )
+ {
+ // If the content node has a conditional style, we have to set the
+ // string item containing the correct conditional style name (the
+ // style name property has already been set during the import!)
+ // In case we do not have a conditional style, we make use of the
+ // fact that nobody else uses the attribute set behind the handle.
+ // FME 2007-07-10 #i78124# If autostyle does not have a parent,
+ // the string is empty.
+ const SfxStringItem* pNameItem = nullptr;
+ if ( nullptr != GetCondFormatColl() ||
+ !(pNameItem = mpAttrSet->GetItemIfSet( RES_FRMATR_STYLE_NAME, false )) ||
+ pNameItem->GetValue().isEmpty() )
+ AttrSetHandleHelper::SetParent( mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl() );
+ else
+ const_cast<SfxItemSet*>(mpAttrSet.get())->SetParent( &GetFormatColl()->GetAttrSet() );
+ }
+
+ return true;
+ }
+
+ if( !GetpSwAttrSet() ) // Have the AttrsSets created by the corresponding Nodes
+ NewAttrSet( GetDoc().GetAttrPool() );
+
+ bool bRet = false;
+ // If Modify is locked, do not send any Modifys
+ if ( IsModifyLocked() ||
+ ( !HasWriterListeners() &&
+ SfxItemState::SET != rSet.GetItemState( RES_PARATR_NUMRULE, false ) ) )
+ {
+ // Some special treatment for Attributes
+ bRet = AttrSetHandleHelper::Put( mpAttrSet, *this, rSet );
+ }
+ else
+ {
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bRet = AttrSetHandleHelper::Put_BC( mpAttrSet, *this, rSet, &aOld, &aNew );
+ if( bRet )
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ }
+ return bRet;
+}
+
+// With nWhich it takes the Hint from the Delta array
+bool SwContentNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
+{
+ if( !GetpSwAttrSet() )
+ return false;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // If Modify is locked, do not send out any Modifys
+ if( IsModifyLocked() )
+ {
+ sal_uInt16 nDel = 0;
+ if ( !nWhich2 || nWhich2 < nWhich1 )
+ {
+ nDel = ClearItemsFromAttrSet( { nWhich1 } );
+ }
+ else
+ nDel = AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, nullptr, nullptr );
+
+ if( !GetpSwAttrSet()->Count() ) // Empty? Delete
+ mpAttrSet.reset();
+ return 0 != nDel;
+ }
+
+ // No valid area defined?
+ if( !nWhich2 || nWhich2 < nWhich1 )
+ nWhich2 = nWhich1; // Then set only this Item to 1st Id
+
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, nWhich1, nWhich2, &aOld, &aNew );
+
+ if( bRet )
+ {
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+
+ if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
+ mpAttrSet.reset();
+ }
+ return bRet;
+}
+
+bool SwContentNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
+{
+ if( !GetpSwAttrSet() )
+ return false;
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ // If Modify is locked, do not send out any Modifys
+ sal_uInt16 nDel = 0;
+ if( IsModifyLocked() )
+ {
+ nDel = ClearItemsFromAttrSet( rWhichArr );
+ }
+ else
+ {
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+
+ for ( const auto& rWhich : rWhichArr )
+ if( AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, rWhich, &aOld, &aNew ))
+ ++nDel;
+
+ if( nDel )
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ }
+ if( !GetpSwAttrSet()->Count() ) // Empty?, delete it
+ mpAttrSet.reset();
+ return 0 != nDel ;
+}
+
+sal_uInt16 SwContentNode::ResetAllAttr()
+{
+ if( !GetpSwAttrSet() )
+ return 0;
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // If Modify is locked, do not send out any Modifys
+ if( IsModifyLocked() )
+ {
+ sal_uInt16 nDel = ClearItemsFromAttrSet( { 0 } );
+ if( !GetpSwAttrSet()->Count() ) // Empty? Delete
+ mpAttrSet.reset();
+ return nDel;
+ }
+
+ SwAttrSet aOld( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() ),
+ aNew( *GetpSwAttrSet()->GetPool(), GetpSwAttrSet()->GetRanges() );
+ bool bRet = 0 != AttrSetHandleHelper::ClearItem_BC( mpAttrSet, *this, 0, &aOld, &aNew );
+
+ if( bRet )
+ {
+ sw::ClientNotifyAttrChg(*this, *GetpSwAttrSet(), aOld, aNew);
+ if( !GetpSwAttrSet()->Count() ) // Empty? Delete
+ mpAttrSet.reset();
+ }
+ return aNew.Count();
+}
+
+bool SwContentNode::GetAttr( SfxItemSet& rSet ) const
+{
+ if( rSet.Count() )
+ rSet.ClearItem();
+
+ const SwAttrSet& rAttrSet = GetSwAttrSet();
+ return rSet.Set( rAttrSet );
+}
+
+sal_uInt16 SwContentNode::ClearItemsFromAttrSet( const std::vector<sal_uInt16>& rWhichIds )
+{
+ sal_uInt16 nRet = 0;
+ if ( rWhichIds.empty() )
+ return nRet;
+
+ OSL_ENSURE( GetpSwAttrSet(), "no item set" );
+ SwAttrSet aNewAttrSet( *GetpSwAttrSet() );
+ for ( const auto& rWhichId : rWhichIds )
+ {
+ nRet = nRet + aNewAttrSet.ClearItem( rWhichId );
+ }
+ if ( nRet )
+ AttrSetHandleHelper::GetNewAutoStyle( mpAttrSet, *this, aNewAttrSet );
+
+ return nRet;
+}
+
+const SfxPoolItem* SwContentNode::GetNoCondAttr( sal_uInt16 nWhich,
+ bool bInParents ) const
+{
+ const SfxPoolItem* pFnd = nullptr;
+ if( m_pCondColl && m_pCondColl->GetRegisteredIn() )
+ {
+ if( !GetpSwAttrSet() || ( SfxItemState::SET != GetpSwAttrSet()->GetItemState(
+ nWhich, false, &pFnd ) && bInParents ))
+ {
+ (void)static_cast<const SwFormat*>(GetRegisteredIn())->GetItemState( nWhich, bInParents, &pFnd );
+ }
+ }
+ // undo change of issue #i51029#
+ // Note: <GetSwAttrSet()> returns <mpAttrSet>, if set, otherwise it returns
+ // the attribute set of the paragraph style, which is valid for the
+ // content node - see file <node.hxx>
+ else
+ {
+ GetSwAttrSet().GetItemState( nWhich, bInParents, &pFnd );
+ }
+ return pFnd;
+}
+
+static bool lcl_CheckMaxLength(SwNode const& rPrev, SwNode const& rNext)
+{
+ if (rPrev.GetNodeType() != rNext.GetNodeType())
+ {
+ return false;
+ }
+ if (!rPrev.IsTextNode())
+ {
+ return true;
+ }
+
+ // Check if a node can contain the other (order is not significant)
+ return rPrev.GetTextNode()->GetSpaceLeft() > rNext.GetTextNode()->Len();
+}
+
+/// Can we join two Nodes?
+/// We can return the 2nd position in pIdx.
+bool SwContentNode::CanJoinNext( SwNodeIndex* pIdx ) const
+{
+ const SwNodes& rNds = GetNodes();
+ SwNodeIndex aIdx( *this, 1 );
+
+ const SwNode* pNd = this;
+ while( aIdx < rNds.Count()-1 &&
+ (( pNd = &aIdx.GetNode())->IsSectionNode() ||
+ ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
+ ++aIdx;
+
+ if (rNds.Count()-1 == aIdx.GetIndex())
+ return false;
+ if (!lcl_CheckMaxLength(*this, *pNd))
+ {
+ return false;
+ }
+ if( pIdx )
+ *pIdx = aIdx;
+ return true;
+}
+
+/// Can we join two Nodes?
+/// We can return the 2nd position in pIdx.
+bool SwContentNode::CanJoinPrev( SwNodeIndex* pIdx ) const
+{
+ SwNodeIndex aIdx( *this, -1 );
+
+ const SwNode* pNd = this;
+ while( aIdx.GetIndex() &&
+ (( pNd = &aIdx.GetNode())->IsSectionNode() ||
+ ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )))
+ --aIdx;
+
+ if (SwNodeOffset(0) == aIdx.GetIndex())
+ return false;
+ if (!lcl_CheckMaxLength(*pNd, *this))
+ {
+ return false;
+ }
+ if( pIdx )
+ *pIdx = aIdx;
+ return true;
+}
+
+void SwContentNode::SetCondFormatColl(SwFormatColl* pColl)
+{
+ if( !((!pColl && m_pCondColl) || ( pColl && !m_pCondColl ) ||
+ ( pColl && pColl != m_pCondColl->GetRegisteredIn() )) )
+ return;
+
+ SwFormatColl* pOldColl = GetCondFormatColl();
+ m_aCondCollListener.EndListeningAll();
+ if(pColl)
+ m_aCondCollListener.StartListening(pColl);
+ m_pCondColl = pColl;
+ if(GetpSwAttrSet())
+ AttrSetHandleHelper::SetParent(mpAttrSet, *this, &GetAnyFormatColl(), GetFormatColl());
+
+ if(!IsModifyLocked())
+ {
+ SwFormatChg aTmp1(pOldColl ? pOldColl : GetFormatColl());
+ SwFormatChg aTmp2(pColl ? pColl : GetFormatColl());
+ CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp2));
+ }
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+}
+
+bool SwContentNode::IsAnyCondition( SwCollCondition& rTmp ) const
+{
+ const SwNodes& rNds = GetNodes();
+ {
+ Master_CollCondition nCond = Master_CollCondition::NONE;
+ const SwStartNode* pSttNd = StartOfSectionNode();
+ while( pSttNd )
+ {
+ switch( pSttNd->GetNodeType() )
+ {
+ case SwNodeType::Table: nCond = Master_CollCondition::PARA_IN_TABLEBODY; break;
+ case SwNodeType::Section: nCond = Master_CollCondition::PARA_IN_SECTION; break;
+
+ default:
+ switch( pSttNd->GetStartNodeType() )
+ {
+ case SwTableBoxStartNode:
+ {
+ nCond = Master_CollCondition::PARA_IN_TABLEBODY;
+ const SwTableNode* pTableNd = pSttNd->FindTableNode();
+ const SwTableBox* pBox;
+ if( pTableNd && nullptr != ( pBox = pTableNd->GetTable().
+ GetTableBox(pSttNd->GetIndex()) ) &&
+ pBox->IsInHeadline( &pTableNd->GetTable() ) )
+ nCond = Master_CollCondition::PARA_IN_TABLEHEAD;
+ }
+ break;
+ case SwFlyStartNode: nCond = Master_CollCondition::PARA_IN_FRAME; break;
+ case SwFootnoteStartNode:
+ {
+ nCond = Master_CollCondition::PARA_IN_FOOTNOTE;
+ const SwFootnoteIdxs& rFootnoteArr = rNds.GetDoc().GetFootnoteIdxs();
+ const SwTextFootnote* pTextFootnote;
+ const SwNode* pSrchNd = pSttNd;
+
+ for( size_t n = 0; n < rFootnoteArr.size(); ++n )
+ if( nullptr != ( pTextFootnote = rFootnoteArr[ n ])->GetStartNode() &&
+ pSrchNd == &pTextFootnote->GetStartNode()->GetNode() )
+ {
+ if( pTextFootnote->GetFootnote().IsEndNote() )
+ nCond = Master_CollCondition::PARA_IN_ENDNOTE;
+ break;
+ }
+ }
+ break;
+ case SwHeaderStartNode: nCond = Master_CollCondition::PARA_IN_HEADER; break;
+ case SwFooterStartNode: nCond = Master_CollCondition::PARA_IN_FOOTER; break;
+ case SwNormalStartNode: break;
+ }
+ }
+
+ if( nCond != Master_CollCondition::NONE )
+ {
+ rTmp.SetCondition( nCond, 0 );
+ return true;
+ }
+ pSttNd = pSttNd->GetIndex()
+ ? pSttNd->StartOfSectionNode()
+ : nullptr;
+ }
+ }
+
+ {
+ SwOutlineNodes::size_type nPos;
+ const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
+ if( !rOutlNds.empty() )
+ {
+ if( !rOutlNds.Seek_Entry( const_cast<SwContentNode*>(this), &nPos ) && nPos )
+ --nPos;
+ if( nPos < rOutlNds.size() &&
+ rOutlNds[ nPos ]->GetIndex() < GetIndex() )
+ {
+ SwTextNode* pOutlNd = rOutlNds[ nPos ]->GetTextNode();
+
+ if( pOutlNd->IsOutline())
+ {
+ rTmp.SetCondition( Master_CollCondition::PARA_IN_OUTLINE, pOutlNd->GetAttrOutlineLevel() - 1 );
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void SwContentNode::ChkCondColl(const SwTextFormatColl* pColl)
+{
+ if(pColl != GetRegisteredIn())
+ {
+ SAL_WARN("sw.core", "Wrong cond collection, skipping check of Cond Colls.");
+ return;
+ }
+ if(&GetNodes() != &GetDoc().GetNodes())
+ {
+ SAL_WARN("sw.core", "Nodes amiss, skipping check of Cond Colls.");
+ return;
+ }
+ // Check, just to be sure
+ if( RES_CONDTXTFMTCOLL != GetFormatColl()->Which() )
+ return;
+
+ SwCollCondition aTmp( nullptr, Master_CollCondition::NONE, 0 );
+ const SwCollCondition* pCColl;
+
+ bool bDone = false;
+
+ if( IsAnyCondition( aTmp ))
+ {
+ pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())
+ ->HasCondition( aTmp );
+
+ if (pCColl)
+ {
+ SetCondFormatColl( pCColl->GetTextFormatColl() );
+ bDone = true;
+ }
+ }
+
+ if (bDone)
+ return;
+
+ if( IsTextNode() && static_cast<SwTextNode*>(this)->GetNumRule())
+ {
+ // Is at which Level in a list?
+ aTmp.SetCondition( Master_CollCondition::PARA_IN_LIST,
+ static_cast<SwTextNode*>(this)->GetActualListLevel() );
+ pCColl = static_cast<SwConditionTextFormatColl*>(GetFormatColl())->
+ HasCondition( aTmp );
+ }
+ else
+ pCColl = nullptr;
+
+ if( pCColl )
+ SetCondFormatColl( pCColl->GetTextFormatColl() );
+ else if( m_pCondColl )
+ SetCondFormatColl( nullptr );
+}
+
+// #i42921#
+SvxFrameDirection SwContentNode::GetTextDirection( const SwPosition& rPos,
+ const Point* pPt ) const
+{
+ SvxFrameDirection nRet = SvxFrameDirection::Unknown;
+
+ Point aPt;
+ if( pPt )
+ aPt = *pPt;
+
+ // #i72024# - No format of the frame, because this can cause recursive layout actions
+ std::pair<Point, bool> const tmp(aPt, false);
+ SwFrame* pFrame = getLayoutFrame(GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
+
+ if ( pFrame )
+ {
+ if ( pFrame->IsVertical() )
+ {
+ if (pFrame->IsVertLRBT())
+ nRet = SvxFrameDirection::Vertical_LR_BT;
+ else if (pFrame->IsRightToLeft())
+ nRet = SvxFrameDirection::Vertical_LR_TB;
+ else
+ nRet = SvxFrameDirection::Vertical_RL_TB;
+ }
+ else
+ {
+ if ( pFrame->IsRightToLeft() )
+ nRet = SvxFrameDirection::Horizontal_RL_TB;
+ else
+ nRet = SvxFrameDirection::Horizontal_LR_TB;
+ }
+ }
+
+ return nRet;
+}
+
+std::unique_ptr<SwOLENodes> SwContentNode::CreateOLENodesArray( const SwFormatColl& rColl, bool bOnlyWithInvalidSize )
+{
+ std::unique_ptr<SwOLENodes> pNodes;
+ SwIterator<SwContentNode,SwFormatColl> aIter( rColl );
+ for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() )
+ {
+ SwOLENode *pONd = pNd->GetOLENode();
+ if ( pONd && (!bOnlyWithInvalidSize || pONd->IsOLESizeInvalid()) )
+ {
+ if ( !pNodes )
+ pNodes.reset(new SwOLENodes);
+ pNodes->push_back( pONd );
+ }
+ }
+
+ return pNodes;
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwContentNode::getSdrAllFillAttributesHelper() const
+{
+ return drawinglayer::attribute::SdrAllFillAttributesHelperPtr();
+}
+
+/*
+ * Document Interface Access
+ */
+const IDocumentSettingAccess* SwNode::getIDocumentSettingAccess() const { return &GetDoc().GetDocumentSettingManager(); }
+const IDocumentDeviceAccess& SwNode::getIDocumentDeviceAccess() const { return GetDoc().getIDocumentDeviceAccess(); }
+const IDocumentRedlineAccess& SwNode::getIDocumentRedlineAccess() const { return GetDoc().getIDocumentRedlineAccess(); }
+const IDocumentStylePoolAccess& SwNode::getIDocumentStylePoolAccess() const { return GetDoc().getIDocumentStylePoolAccess(); }
+const IDocumentDrawModelAccess& SwNode::getIDocumentDrawModelAccess() const { return GetDoc().getIDocumentDrawModelAccess(); }
+const IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() const { return GetDoc().getIDocumentLayoutAccess(); }
+IDocumentLayoutAccess& SwNode::getIDocumentLayoutAccess() { return GetDoc().getIDocumentLayoutAccess(); }
+const IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() const { return GetDoc().getIDocumentLinksAdministration(); }
+IDocumentLinksAdministration& SwNode::getIDocumentLinksAdministration() { return GetDoc().getIDocumentLinksAdministration(); }
+const IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() const { return GetDoc().getIDocumentFieldsAccess(); }
+IDocumentFieldsAccess& SwNode::getIDocumentFieldsAccess() { return GetDoc().getIDocumentFieldsAccess(); }
+IDocumentContentOperations& SwNode::getIDocumentContentOperations() { return GetDoc().getIDocumentContentOperations(); }
+IDocumentListItems& SwNode::getIDocumentListItems() { return GetDoc().getIDocumentListItems(); } // #i83479#
+
+const IDocumentMarkAccess* SwNode::getIDocumentMarkAccess() const { return GetDoc().getIDocumentMarkAccess(); }
+IStyleAccess& SwNode::getIDocumentStyleAccess() { return GetDoc().GetIStyleAccess(); }
+
+bool SwNode::IsInRedlines() const
+{
+ const SwDoc& rDoc = GetDoc();
+
+ return rDoc.getIDocumentRedlineAccess().IsInRedlines(*this);
+}
+
+void SwNode::AddAnchoredFly(SwFrameFormat *const pFlyFormat)
+{
+ assert(pFlyFormat);
+ assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->nNode.GetNode() == this);
+ // check node type, cf. SwFormatAnchor::SetAnchor()
+ assert(IsTextNode() || IsStartNode() || IsTableNode());
+ m_aAnchoredFlys.push_back(pFlyFormat);
+}
+
+void SwNode::RemoveAnchoredFly(SwFrameFormat *const pFlyFormat)
+{
+ assert(pFlyFormat);
+ // cannot assert this in Remove because it is called when new anchor is already set
+// assert(&pFlyFormat->GetAnchor(false).GetContentAnchor()->nNode.GetNode() == this);
+ assert(IsTextNode() || IsStartNode() || IsTableNode());
+ auto it(std::find(m_aAnchoredFlys.begin(), m_aAnchoredFlys.end(), pFlyFormat));
+ assert(it != m_aAnchoredFlys.end());
+ m_aAnchoredFlys.erase(it);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/node2lay.cxx b/sw/source/core/docnode/node2lay.cxx
new file mode 100644
index 000000000..5cc15c310
--- /dev/null
+++ b/sw/source/core/docnode/node2lay.cxx
@@ -0,0 +1,517 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <calbck.hxx>
+#include <node.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ftnfrm.hxx>
+#include <sectfrm.hxx>
+#include <cntfrm.hxx>
+#include <tabfrm.hxx>
+#include <frmtool.hxx>
+#include <section.hxx>
+#include <node2lay.hxx>
+#include <osl/diagnose.h>
+
+/**
+ * The SwNode2LayImpl class does the actual work, the SwNode2Layout class is
+ * just the public interface.
+ */
+class SwNode2LayImpl
+{
+ std::unique_ptr<SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti>> mpIter;
+ sw::BroadcastingModify* mpMod;
+ std::vector<SwFrame*> mvUpperFrames; // To collect the Upper
+ SwNodeOffset mnIndex; // The Index of the to-be-inserted Nodes
+ bool mbMaster : 1; // true => only Master, false => only Frames without Follow
+ bool mbInit : 1; // Did we already call First() at SwClient?
+
+ SwNode2LayImpl(const SwNode2LayImpl&) = delete;
+ SwNode2LayImpl& operator=(const SwNode2LayImpl&) = delete;
+
+public:
+ SwNode2LayImpl( const SwNode& rNode, SwNodeOffset nIdx, bool bSearch );
+ SwFrame* NextFrame(); // Returns the next "useful" Frame
+ SwLayoutFrame* UpperFrame( SwFrame* &rpFrame, const SwNode &rNode );
+ void SaveUpperFrames(); // Saves (and locks if needed) the pUpper
+ // Inserts a Frame under every pUpper of the array
+ void RestoreUpperFrames( SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd );
+
+ SwFrame* GetFrame( const Point* pDocPos ) const;
+};
+
+static SwNode* GoNextWithFrame(const SwNodes& rNodes, SwNodeIndex *pIdx, SwFlowFrame const**const ppFrame)
+{
+ if( pIdx->GetIndex() >= rNodes.Count() - 1 )
+ return nullptr;
+
+ SwNodeIndex aTmp(*pIdx, +1);
+ SwNode* pNd = nullptr;
+ while( aTmp < rNodes.Count()-1 )
+ {
+ pNd = &aTmp.GetNode();
+ SwFrame const* pFound(nullptr);
+ if ( pNd->IsContentNode() )
+ // sw_redlinehide: assume that it's OK to find a node with the same
+ // frame as the caller's one
+ pFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First();
+ else if ( pNd->IsTableNode() )
+ pFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First() ;
+ else if( pNd->IsEndNode() && !pNd->StartOfSectionNode()->IsSectionNode() )
+ {
+ pNd = nullptr;
+ break;
+ }
+ if (pFound != nullptr)
+ {
+ if (ppFrame)
+ {
+ *ppFrame = SwFlowFrame::CastFlowFrame(pFound);
+ assert(*ppFrame);
+ }
+ break;
+ }
+ ++aTmp;
+ }
+
+ if( aTmp == rNodes.Count()-SwNodeOffset(1) )
+ pNd = nullptr;
+ else if( pNd )
+ (*pIdx) = aTmp;
+ return pNd;
+}
+
+static SwNode* GoPreviousWithFrame(SwNodeIndex *pIdx, SwFlowFrame const**const ppFrame)
+{
+ if( !pIdx->GetIndex() )
+ return nullptr;
+
+ SwNodeIndex aTmp( *pIdx, -1 );
+ SwNode* pNd(nullptr);
+ while( aTmp.GetIndex() )
+ {
+ pNd = &aTmp.GetNode();
+ SwFrame const* pFound(nullptr);
+ if ( pNd->IsContentNode() )
+ // sw_redlinehide: assume that it's OK to find a node with the same
+ // frame as the caller's one
+ pFound = SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*static_cast<SwContentNode*>(pNd)).First();
+ else if ( pNd->IsTableNode() )
+ pFound = SwIterator<SwFrame,SwFormat>(*static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat()).First();
+ else if( pNd->IsStartNode() && !pNd->IsSectionNode() )
+ {
+ pNd = nullptr;
+ break;
+ }
+ if (pFound != nullptr)
+ {
+ if (ppFrame)
+ {
+ *ppFrame = SwFlowFrame::CastFlowFrame(pFound);
+ assert(*ppFrame);
+ }
+ break;
+ }
+ --aTmp;
+ }
+
+ if( !aTmp.GetIndex() )
+ pNd = nullptr;
+ else if( pNd )
+ (*pIdx) = aTmp;
+ return pNd;
+}
+
+namespace sw {
+
+SwFrame const* FindNeighbourFrameForNode(SwNode const& rNode)
+{
+ SwNodeIndex idx(rNode);
+ SwFlowFrame const* pFlow(nullptr);
+ if (SwNode *const pNode = GoPreviousWithFrame(&idx, &pFlow))
+ {
+ if (::CheckNodesRange(rNode, idx, true))
+ {
+ while (pFlow->HasFollow())
+ { // try to get the one on the current page
+ pFlow = pFlow->GetFollow();
+ }
+ return &pFlow->GetFrame();
+ }
+ }
+ idx = rNode;
+ if (SwNode *const pNode = GoNextWithFrame(idx.GetNodes(), &idx, &pFlow))
+ {
+ if (::CheckNodesRange(rNode, idx, true))
+ {
+ while (pFlow->IsFollow())
+ { // try to get the one on the current page
+ pFlow = pFlow->GetPrecede();
+ }
+ return &pFlow->GetFrame();
+ }
+ }
+ return nullptr;
+}
+
+}
+
+/**
+ * The main purpose of this ctor is to find the right sw::BroadcastingModify to iterate over.
+ *
+ * @param bSearch true: find the next Content or TableNode which contains
+ * Frames (to collect the pUpper).
+ * Else we assume that rNode points already to such a
+ * Content or TableNode.
+ * We insert before or after it.
+ */
+SwNode2LayImpl::SwNode2LayImpl( const SwNode& rNode, SwNodeOffset nIdx, bool bSearch )
+ : mnIndex( nIdx ), mbInit( false )
+{
+ const SwNode* pNd;
+ if( bSearch || rNode.IsSectionNode() )
+ {
+ // Find the next Content/TableNode that contains a Frame, so that we can add
+ // ourselves before/after it
+ if( !bSearch && rNode.GetIndex() < mnIndex )
+ {
+ SwNodeIndex aTmp( *rNode.EndOfSectionNode(), +1 );
+ pNd = GoPreviousWithFrame(&aTmp, nullptr);
+ if( pNd && rNode.GetIndex() > pNd->GetIndex() )
+ pNd = nullptr; // Do not go over the limits
+ mbMaster = false;
+ }
+ else
+ {
+ SwNodeIndex aTmp( rNode, -1 );
+ pNd = GoNextWithFrame(rNode.GetNodes(), &aTmp, nullptr);
+ mbMaster = true;
+ if( !bSearch && pNd && rNode.EndOfSectionIndex() < pNd->GetIndex() )
+ pNd = nullptr; // Do not go over the limits
+ }
+ }
+ else
+ {
+ pNd = &rNode;
+ mbMaster = mnIndex < rNode.GetIndex();
+ }
+ if( pNd )
+ {
+ if( pNd->IsContentNode() )
+ mpMod = const_cast<sw::BroadcastingModify*>(static_cast<sw::BroadcastingModify const *>(pNd->GetContentNode()));
+ else
+ {
+ assert(pNd->IsTableNode());
+ mpMod = pNd->GetTableNode()->GetTable().GetFrameFormat();
+ }
+ mpIter.reset(new SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti>(*mpMod));
+ }
+ else
+ {
+ mpIter = nullptr;
+ mpMod = nullptr;
+ }
+}
+
+/**
+ * Returns the next "useful" Frame.
+ *
+ * When calling this method for the first time, a First is triggered at the
+ * actual Iterator. The result is check for suitability: Follows are not
+ * accepted, a Master is accepted when collecting the pUpper and when
+ * inserting before it.
+ * When inserting after it, we find and return the last Follow starting
+ * from the Master.
+ *
+ * If the Frame is located in a SectionFrame, we check to see whether the
+ * SectionFrame is the suitable return value (instead of the Frame itself).
+ * This is the case if the to-be-inserted Node is outside of the Section.
+ */
+SwFrame* SwNode2LayImpl::NextFrame()
+{
+ SwFrame* pRet;
+ if( !mpIter )
+ return nullptr;
+ if( !mbInit )
+ {
+ pRet = mpIter->First();
+ mbInit = true;
+ }
+ else
+ pRet = mpIter->Next();
+ while( pRet )
+ {
+ SwFlowFrame* pFlow = SwFlowFrame::CastFlowFrame( pRet );
+ assert(pFlow);
+ // Follows are pretty volatile, thus we ignore them.
+ // Even if we insert after the Frame, we start from the Master
+ // and iterate through it until the last Follow
+ if( !pFlow->IsFollow() )
+ {
+ if( !mbMaster )
+ {
+ while( pFlow->HasFollow() )
+ pFlow = pFlow->GetFollow();
+ pRet = &(pFlow->GetFrame());
+ }
+ if( pRet->IsInSct() )
+ {
+ SwSectionFrame* pSct = pRet->FindSctFrame();
+ // ATTENTION: If we are in a Footnote, from a Layout point of view
+ // it could be located in a Section with columns, although it
+ // should be outside of it when looking at the Nodes.
+ // Thus, when dealing with Footnotes, we need to check whether the
+ // SectionFrame is also located within the Footnote and not outside of it.
+ if( !pRet->IsInFootnote() || pSct->IsInFootnote() )
+ {
+ assert(pSct && pSct->GetSection());
+ SwSectionNode* pNd = pSct->GetSection()->GetFormat()->GetSectionNode();
+ assert(pNd);
+ // If the result Frame is located within a Section Frame
+ // whose Section does not contain the Node, we return with
+ // the SectionFrame, else we return with the Content/TabFrame
+ if( mbMaster )
+ {
+ if( pNd->GetIndex() >= mnIndex )
+ pRet = pSct;
+ }
+ else if( pNd->EndOfSectionIndex() < mnIndex )
+ pRet = pSct;
+ }
+ }
+ return pRet;
+ }
+ pRet = mpIter->Next();
+ }
+ return nullptr;
+}
+
+void SwNode2LayImpl::SaveUpperFrames()
+{
+ SwFrame* pFrame;
+ while( nullptr != (pFrame = NextFrame()) )
+ {
+ SwFrame* pPrv = pFrame->GetPrev();
+ pFrame = pFrame->GetUpper();
+ if( pFrame )
+ {
+ if( pFrame->IsFootnoteFrame() )
+ static_cast<SwFootnoteFrame*>(pFrame)->ColLock();
+ else if( pFrame->IsInSct() )
+ pFrame->FindSctFrame()->ColLock();
+ if( pPrv && pPrv->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pPrv)->LockJoin();
+ mvUpperFrames.push_back( pPrv );
+ mvUpperFrames.push_back( pFrame );
+ }
+ }
+ mpIter.reset();
+ mpMod = nullptr;
+}
+
+SwLayoutFrame* SwNode2LayImpl::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode )
+{
+ rpFrame = NextFrame();
+ if( !rpFrame )
+ return nullptr;
+ SwLayoutFrame* pUpper = rpFrame->GetUpper();
+ if( rpFrame->IsSctFrame() )
+ {
+ const SwNode* pNode = rNode.StartOfSectionNode();
+ if( pNode->IsSectionNode() )
+ {
+ SwFrame* pFrame = mbMaster ? rpFrame->FindPrev() : rpFrame->FindNext();
+ if( pFrame && pFrame->IsSctFrame() )
+ {
+ // pFrame could be a "dummy"-section
+ if( static_cast<SwSectionFrame*>(pFrame)->GetSection() &&
+ (&static_cast<const SwSectionNode*>(pNode)->GetSection() ==
+ static_cast<SwSectionFrame*>(pFrame)->GetSection()) )
+ {
+ // #i22922# - consider columned sections
+ // 'Go down' the section frame as long as the layout frame
+ // is found, which would contain content.
+ while ( pFrame->IsLayoutFrame() &&
+ static_cast<SwLayoutFrame*>(pFrame)->Lower() &&
+ !static_cast<SwLayoutFrame*>(pFrame)->Lower()->IsFlowFrame() &&
+ static_cast<SwLayoutFrame*>(pFrame)->Lower()->IsLayoutFrame() )
+ {
+ pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ }
+ assert(pFrame->IsLayoutFrame());
+ rpFrame = mbMaster ? nullptr
+ : static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ assert((!rpFrame || rpFrame->IsFlowFrame()) &&
+ "<SwNode2LayImpl::UpperFrame(..)> - expected sibling isn't a flow frame." );
+ return static_cast<SwLayoutFrame*>(pFrame);
+ }
+
+ pUpper = new SwSectionFrame(const_cast<SwSectionNode*>(static_cast<const SwSectionNode*>(pNode))->GetSection(), rpFrame);
+ pUpper->Paste( rpFrame->GetUpper(),
+ mbMaster ? rpFrame : rpFrame->GetNext() );
+ static_cast<SwSectionFrame*>(pUpper)->Init();
+ rpFrame = nullptr;
+ // 'Go down' the section frame as long as the layout frame
+ // is found, which would contain content.
+ while ( pUpper->Lower() &&
+ !pUpper->Lower()->IsFlowFrame() &&
+ pUpper->Lower()->IsLayoutFrame() )
+ {
+ pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower());
+ }
+ return pUpper;
+ }
+ }
+ }
+ if( !mbMaster )
+ rpFrame = rpFrame->GetNext();
+ return pUpper;
+}
+
+void SwNode2LayImpl::RestoreUpperFrames( SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd )
+{
+ SwNode* pNd;
+ SwDoc& rDoc = rNds.GetDoc();
+ bool bFirst = true;
+ for( ; nStt < nEnd; ++nStt )
+ {
+ SwFrame* pNew = nullptr;
+ SwFrame* pNxt;
+ SwLayoutFrame* pUp;
+ if( (pNd = rNds[nStt])->IsContentNode() )
+ for( std::vector<SwFrame*>::size_type n = 0; n < mvUpperFrames.size(); )
+ {
+ pNxt = mvUpperFrames[n++];
+ if( bFirst && pNxt && pNxt->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pNxt)->UnlockJoin();
+ pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[n++]);
+ if( pNxt )
+ pNxt = pNxt->GetNext();
+ else
+ pNxt = pUp->Lower();
+ pNew = static_cast<SwContentNode*>(pNd)->MakeFrame( pUp );
+ pNew->Paste( pUp, pNxt );
+ mvUpperFrames[n-2] = pNew;
+ }
+ else if( pNd->IsTableNode() )
+ for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); )
+ {
+ pNxt = mvUpperFrames[x++];
+ if( bFirst && pNxt && pNxt->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pNxt)->UnlockJoin();
+ pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[x++]);
+ if( pNxt )
+ pNxt = pNxt->GetNext();
+ else
+ pNxt = pUp->Lower();
+ pNew = static_cast<SwTableNode*>(pNd)->MakeFrame( pUp );
+ assert(pNew->IsTabFrame());
+ pNew->Paste( pUp, pNxt );
+ static_cast<SwTabFrame*>(pNew)->RegistFlys();
+ mvUpperFrames[x-2] = pNew;
+ }
+ else if( pNd->IsSectionNode() )
+ {
+ nStt = pNd->EndOfSectionIndex();
+ for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); )
+ {
+ pNxt = mvUpperFrames[x++];
+ if( bFirst && pNxt && pNxt->IsSctFrame() )
+ static_cast<SwSectionFrame*>(pNxt)->UnlockJoin();
+ pUp = static_cast<SwLayoutFrame*>(mvUpperFrames[x++]);
+ OSL_ENSURE( pUp->GetUpper() || pUp->IsFlyFrame(), "Lost Upper" );
+ ::InsertCnt_( pUp, &rDoc, pNd->GetIndex(), false, nStt+1, pNxt );
+ pNxt = pUp->GetLastLower();
+ mvUpperFrames[x-2] = pNxt;
+ }
+ }
+ bFirst = false;
+ }
+ for( std::vector<SwFrame*>::size_type x = 0; x < mvUpperFrames.size(); ++x )
+ {
+ SwFrame* pTmp = mvUpperFrames[++x];
+ if( pTmp->IsFootnoteFrame() )
+ static_cast<SwFootnoteFrame*>(pTmp)->ColUnlock();
+ else if ( pTmp->IsInSct() )
+ {
+ SwSectionFrame* pSctFrame = pTmp->FindSctFrame();
+ pSctFrame->ColUnlock();
+ // #i18103# - invalidate size of section in order to
+ // assure, that the section is formatted, unless it was 'Collocked'
+ // from its 'collection' until its 'restoration'.
+ pSctFrame->InvalidateSize_();
+ }
+ }
+}
+
+SwFrame* SwNode2LayImpl::GetFrame( const Point* pDocPos ) const
+{
+ // test if change of member pIter -> pMod broke anything
+ std::pair<Point, bool> tmp;
+ if (pDocPos)
+ {
+ tmp.first = *pDocPos;
+ tmp.second = false;
+ }
+ return mpMod ? ::GetFrameOfModify(nullptr, *mpMod, FRM_ALL, nullptr, pDocPos ? &tmp : nullptr) : nullptr;
+}
+
+SwNode2Layout::SwNode2Layout( const SwNode& rNd, SwNodeOffset nIdx )
+ : m_pImpl( new SwNode2LayImpl( rNd, nIdx, false ) )
+{
+}
+
+SwNode2LayoutSaveUpperFrames::SwNode2LayoutSaveUpperFrames(const SwNode& rNd)
+ : m_pImpl( new SwNode2LayImpl( rNd, rNd.GetIndex(), true ) )
+{
+ m_pImpl->SaveUpperFrames();
+}
+
+void SwNode2LayoutSaveUpperFrames::RestoreUpperFrames(
+ SwNodes& rNds, SwNodeOffset const nStt, SwNodeOffset const nEnd)
+{
+ m_pImpl->RestoreUpperFrames( rNds, nStt, nEnd );
+}
+
+SwFrame* SwNode2Layout::NextFrame()
+{
+ return m_pImpl->NextFrame();
+}
+
+SwLayoutFrame* SwNode2Layout::UpperFrame( SwFrame* &rpFrame, const SwNode &rNode )
+{
+ return m_pImpl->UpperFrame( rpFrame, rNode );
+}
+
+SwNode2Layout::~SwNode2Layout()
+{
+}
+
+SwNode2LayoutSaveUpperFrames::~SwNode2LayoutSaveUpperFrames()
+{
+}
+
+SwFrame* SwNode2Layout::GetFrame( const Point* pDocPos ) const
+{
+ return m_pImpl->GetFrame( pDocPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/nodes.cxx b/sw/source/core/docnode/nodes.cxx
new file mode 100644
index 000000000..85f01cea7
--- /dev/null
+++ b/sw/source/core/docnode/nodes.cxx
@@ -0,0 +1,2392 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <stdlib.h>
+
+#include <libxml/xmlwriter.h>
+#include <osl/diagnose.h>
+
+#include <node.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <numrule.hxx>
+#include <ndtxt.hxx>
+#include <ndnotxt.hxx>
+#include <swtable.hxx>
+#include <section.hxx>
+#include <ddefld.hxx>
+#include <swddetbl.hxx>
+#include <txtatr.hxx>
+#include <tox.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtftn.hxx>
+
+#include <docsh.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+
+typedef std::vector<SwStartNode*> SwStartNodePointers;
+
+// function to determine the highest level in the given range
+static sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange );
+
+/** Constructor
+ *
+ * creates the base sections (PostIts, Inserts, AutoText, RedLines, Content)
+ *
+ * @param rDocument TODO: provide documentation
+ */
+SwNodes::SwNodes( SwDoc& rDocument )
+ : m_vIndices(nullptr), m_rMyDoc( rDocument )
+{
+ m_bInNodesDel = m_bInDelUpdOutline = false;
+
+ SwNodeOffset nPos(0);
+ SwStartNode* pSttNd = new SwStartNode( *this, nPos++ );
+ m_pEndOfPostIts = new SwEndNode( *this, nPos++, *pSttNd );
+
+ SwStartNode* pTmp = new SwStartNode( *this, nPos++ );
+ m_pEndOfInserts = new SwEndNode( *this, nPos++, *pTmp );
+
+ pTmp = new SwStartNode( *this, nPos++ );
+ pTmp->m_pStartOfSection = pSttNd;
+ m_pEndOfAutotext = new SwEndNode( *this, nPos++, *pTmp );
+
+ pTmp = new SwStartNode( *this, nPos++ );
+ pTmp->m_pStartOfSection = pSttNd;
+ m_pEndOfRedlines = new SwEndNode( *this, nPos++, *pTmp );
+
+ pTmp = new SwStartNode( *this, nPos++ );
+ pTmp->m_pStartOfSection = pSttNd;
+ m_pEndOfContent.reset(new SwEndNode( *this, nPos++, *pTmp ));
+
+ m_pOutlineNodes.reset(new SwOutlineNodes);
+}
+
+/** Destructor
+ *
+ * Deletes all nodes whose pointer are in a dynamic array. This should be no
+ * problem as nodes cannot be created outside this array and, thus, cannot be
+ * part of multiple arrays.
+ */
+SwNodes::~SwNodes()
+{
+ m_pOutlineNodes.reset();
+
+ {
+ SwNodeIndex aNdIdx( *this );
+ while( true )
+ {
+ SwNode *pNode = &aNdIdx.GetNode();
+ if( pNode == m_pEndOfContent.get() )
+ break;
+
+ ++aNdIdx;
+ delete pNode;
+ }
+ }
+
+ // here, all SwNodeIndices must be unregistered
+ m_pEndOfContent.reset();
+}
+
+static bool IsInsertOutline(SwNodes const& rNodes, SwNodeOffset const nIndex)
+{
+ if (!rNodes.IsDocNodes())
+ {
+ return false;
+ }
+ return nIndex < rNodes.GetEndOfRedlines().StartOfSectionNode()->GetIndex()
+ || rNodes.GetEndOfRedlines().GetIndex() < nIndex;
+}
+
+void SwNodes::ChgNode( SwNodeIndex const & rDelPos, SwNodeOffset nSz,
+ SwNodeIndex& rInsPos, bool bNewFrames )
+{
+ // no need for frames in the UndoArea
+ SwNodes& rNds = rInsPos.GetNodes();
+ const SwNode* pPrevInsNd = rNds[ rInsPos.GetIndex() -SwNodeOffset(1) ];
+
+ // declare all fields as invalid, updating will happen
+ // in the idle-handler of the doc
+ if( GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rDelPos.GetNode(), nSz ) &&
+ &rNds.GetDoc() != &GetDoc() )
+ rNds.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ // NEVER include nodes from the RedLineArea
+ SwNodeOffset nNd = rInsPos.GetIndex();
+ bool const bInsOutlineIdx = IsInsertOutline(rNds, nNd);
+
+ if( &rNds == this ) // if in the same node array -> move
+ {
+ // Move order: from front to back, so that new entries are added at
+ // first position, thus, deletion position stays the same
+ const SwNodeOffset nDiff(rDelPos.GetIndex() < rInsPos.GetIndex() ? 0 : 1);
+
+ for( SwNodeOffset n = rDelPos.GetIndex(); nSz; n += nDiff, --nSz )
+ {
+ SwNodeIndex aDelIdx( *this, n );
+ SwNode& rNd = aDelIdx.GetNode();
+
+ // #i57920# - correction of refactoring done by cws swnumtree:
+ // - <SwTextNode::SetLevel( NO_NUMBERING ) is deprecated and
+ // set <IsCounted> state of the text node to <false>, which
+ // isn't correct here.
+ if ( rNd.IsTextNode() )
+ {
+ SwTextNode* pTextNode = rNd.GetTextNode();
+
+ pTextNode->RemoveFromList();
+
+ if (pTextNode->IsOutline())
+ {
+ SwNode* pSrch = &rNd;
+ m_pOutlineNodes->erase( pSrch );
+ }
+ }
+
+ BigPtrArray::Move( sal_Int32(aDelIdx.GetIndex()), sal_Int32(rInsPos.GetIndex()) );
+
+ if( rNd.IsTextNode() )
+ {
+ SwTextNode& rTextNd = static_cast<SwTextNode&>(rNd);
+
+ rTextNd.AddToList();
+
+ if (bInsOutlineIdx && rTextNd.IsOutline())
+ {
+ SwNode* pSrch = &rNd;
+ m_pOutlineNodes->insert( pSrch );
+ }
+ rTextNd.InvalidateNumRule();
+
+//FEATURE::CONDCOLL
+ if( RES_CONDTXTFMTCOLL == rTextNd.GetTextColl()->Which() )
+ rTextNd.ChkCondColl();
+//FEATURE::CONDCOLL
+ }
+ else if( rNd.IsContentNode() )
+ static_cast<SwContentNode&>(rNd).InvalidateNumRule();
+ }
+ }
+ else
+ {
+ bool bSavePersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds));
+ bool bRestPersData(GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this));
+ SwDoc* pDestDoc = &rNds.GetDoc() != &GetDoc() ? &rNds.GetDoc() : nullptr;
+ OSL_ENSURE(!pDestDoc, "SwNodes::ChgNode(): "
+ "the code to handle text fields here looks broken\n"
+ "if the target is in a different document.");
+ if( !bRestPersData && !bSavePersData && pDestDoc )
+ bSavePersData = bRestPersData = true;
+
+ OUString sNumRule;
+ for( SwNodeOffset n(0); n < nSz; n++ )
+ {
+ SwNode* pNd = &rDelPos.GetNode();
+
+ // NoTextNode keep their persistent data
+ if( pNd->IsNoTextNode() )
+ {
+ if( bSavePersData )
+ static_cast<SwNoTextNode*>(pNd)->SavePersistentData();
+ }
+ else if( pNd->IsTextNode() )
+ {
+ SwTextNode* pTextNd = static_cast<SwTextNode*>(pNd);
+
+ // remove outline index from old nodes array
+ if (pTextNd->IsOutline())
+ {
+ m_pOutlineNodes->erase( pNd );
+ }
+
+ // copy rules if needed
+ if( pDestDoc )
+ {
+ const SwNumRule* pNumRule = pTextNd->GetNumRule();
+ if( pNumRule && sNumRule != pNumRule->GetName() )
+ {
+ sNumRule = pNumRule->GetName();
+ SwNumRule* pDestRule = pDestDoc->FindNumRulePtr( sNumRule );
+ if( pDestRule )
+ pDestRule->SetInvalidRule( true );
+ else
+ pDestDoc->MakeNumRule( sNumRule, pNumRule );
+ }
+ }
+ else
+ {
+ // if movement into the UndoNodes-array, update numbering
+ if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd))
+ {
+ pTextNd->InvalidateNumRule();
+ }
+ }
+
+ pTextNd->RemoveFromList();
+ }
+
+ RemoveNode( rDelPos.GetIndex(), SwNodeOffset(1), false ); // move indices
+ SwContentNode * pCNd = pNd->GetContentNode();
+ rNds.InsertNode( pNd, rInsPos );
+
+ if( pCNd )
+ {
+ SwTextNode* pTextNd = pCNd->GetTextNode();
+ if( pTextNd )
+ {
+ SwpHints * const pHts = pTextNd->GetpSwpHints();
+ // OutlineNodes set the new nodes in the array
+ if (bInsOutlineIdx && pTextNd->IsOutline())
+ {
+ rNds.m_pOutlineNodes->insert( pTextNd );
+ }
+
+ pTextNd->AddToList();
+
+ // special treatment for fields
+ if( pHts && pHts->Count() )
+ {
+ bool const bToUndo = !pDestDoc &&
+ GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNds);
+ for( size_t i = pHts->Count(); i; )
+ {
+ SwTextAttr * const pAttr = pHts->Get( --i );
+ switch ( pAttr->Which() )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ {
+ SwTextField* pTextField = static_txtattr_cast<SwTextField*>(pAttr);
+ rNds.GetDoc().getIDocumentFieldsAccess().InsDelFieldInFieldLst( !bToUndo, *pTextField );
+
+ const SwFieldType* pTyp = pTextField->GetFormatField().GetField()->GetTyp();
+ if ( SwFieldIds::Postit == pTyp->Which() )
+ {
+ rNds.GetDoc().GetDocShell()->Broadcast(
+ SwFormatFieldHint(
+ &pTextField->GetFormatField(),
+ ( pTextField->GetFormatField().IsFieldInDoc()
+ ? SwFormatFieldHintWhich::INSERTED
+ : SwFormatFieldHintWhich::REMOVED ) ) );
+ }
+ else if( SwFieldIds::Dde == pTyp->Which() )
+ {
+ if( bToUndo )
+ const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->DecRefCnt();
+ else
+ const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pTyp))->IncRefCnt();
+ }
+ static_cast<SwFormatField&>(pAttr->GetAttr())
+ .InvalidateField();
+ }
+ break;
+
+ case RES_TXTATR_FTN:
+ static_cast<SwFormatFootnote&>(pAttr->GetAttr())
+ .InvalidateFootnote();
+ break;
+
+ case RES_TXTATR_TOXMARK:
+ static_cast<SwTOXMark&>(pAttr->GetAttr())
+ .InvalidateTOXMark();
+ break;
+
+ case RES_TXTATR_REFMARK:
+ static_cast<SwFormatRefMark&>(pAttr->GetAttr())
+ .InvalidateRefMark();
+ break;
+
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ {
+ SwTextMeta *const pTextMeta(
+ static_txtattr_cast<SwTextMeta*>(pAttr));
+ // force removal of UNO object
+ pTextMeta->ChgTextNode(nullptr);
+ pTextMeta->ChgTextNode(pTextNd);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ //FEATURE::CONDCOLL
+ if( RES_CONDTXTFMTCOLL == pTextNd->GetTextColl()->Which() )
+ pTextNd->ChkCondColl();
+ //FEATURE::CONDCOLL
+ }
+ else
+ {
+ // Moved into different Docs? Persist data again!
+ if( pCNd->IsNoTextNode() && bRestPersData )
+ static_cast<SwNoTextNode*>(pCNd)->RestorePersistentData();
+ }
+ }
+ }
+ }
+
+ // declare all fields as invalid, updating will happen
+ // in the idle-handler of the doc
+ GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+ if( &rNds.GetDoc() != &GetDoc() )
+ rNds.GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+
+ if( bNewFrames )
+ bNewFrames = &GetDoc().GetNodes() == &rNds &&
+ GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if( !bNewFrames )
+ return;
+
+ // get the frames:
+ SwNodeIndex aIdx( *pPrevInsNd, 1 );
+ SwNodeIndex aFrameNdIdx( aIdx );
+ SwNode* pFrameNd = rNds.FindPrvNxtFrameNode( aFrameNdIdx,
+ rNds[ rInsPos.GetIndex() - 1 ] );
+
+ if( !pFrameNd )
+ return;
+
+ while( aIdx != rInsPos )
+ {
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( pCNd )
+ {
+ if( pFrameNd->IsTableNode() )
+ static_cast<SwTableNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
+ else if( pFrameNd->IsSectionNode() )
+ static_cast<SwSectionNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(aIdx);
+ else
+ static_cast<SwContentNode*>(pFrameNd)->MakeFramesForAdjacentContentNode(*pCNd);
+ pFrameNd = pCNd;
+ }
+ ++aIdx;
+ }
+}
+
+// TODO: provide documentation
+/** move the node pointer
+ *
+ * Move the node pointer from "(inclusive) start position to (exclusive) end
+ * position" to target position.
+ * If the target is in front of the first or in the area between first and
+ * last element to move, nothing happens.
+ * If the area to move is empty or the end position is before the start
+ * position, nothing happens.
+ *
+ * @param aRange range to move (excluding end node)
+ * @param rNodes
+ * @param aIndex
+ * @param bNewFrames
+ * @return
+ */
+bool SwNodes::MoveNodes( const SwNodeRange& aRange, SwNodes & rNodes,
+ const SwNodeIndex& aIndex, bool bNewFrames )
+{
+ SwNode * pCurrentNode;
+ if( aIndex == SwNodeOffset(0) ||
+ ( (pCurrentNode = &aIndex.GetNode())->GetStartNode() &&
+ !pCurrentNode->StartOfSectionIndex() ))
+ return false;
+
+ SwNodeRange aRg( aRange );
+
+ // skip "simple" start or end nodes
+ while( SwNodeType::Start == (pCurrentNode = &aRg.aStart.GetNode())->GetNodeType()
+ || ( pCurrentNode->IsEndNode() &&
+ !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
+ ++aRg.aStart;
+ --aRg.aStart;
+
+ // if aEnd-1 points to no ContentNode, search previous one
+ --aRg.aEnd;
+ while( ( (( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
+ !pCurrentNode->IsSectionNode() ) ||
+ ( pCurrentNode->IsEndNode() &&
+ SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) ) &&
+ aRg.aEnd > aRg.aStart )
+ --aRg.aEnd;
+
+ // if in same array, check insertion position
+ if( aRg.aStart >= aRg.aEnd )
+ return false;
+
+ if( this == &rNodes )
+ {
+ if( ( aIndex.GetIndex()-SwNodeOffset(1) >= aRg.aStart.GetIndex() &&
+ aIndex.GetIndex()-SwNodeOffset(1) < aRg.aEnd.GetIndex()) ||
+ ( aIndex.GetIndex()-SwNodeOffset(1) == aRg.aEnd.GetIndex() ) )
+ return false;
+ }
+
+ SwNodeOffset nInsPos(0); // counter for tmp array
+
+ // array as a stack, storing all StartOfSelections
+ SwStartNodePointers aSttNdStack;
+ SwStartNodePointers::size_type nLevel = 0; // level counter
+
+ // set start index
+ SwNodeIndex aIdx( aIndex );
+
+ SwStartNode* pStartNode = aIdx.GetNode().m_pStartOfSection;
+ aSttNdStack.insert( aSttNdStack.begin(), pStartNode );
+
+ SwNodeRange aOrigInsPos( aIdx, SwNodeOffset(-1), aIdx ); // original insertion position
+
+ // call DelFrames/MakeFrames for the upmost SectionNode
+ int nSectNdCnt = 0;
+ bool bSaveNewFrames = bNewFrames;
+
+ // continue until everything has been moved
+ while( aRg.aStart < aRg.aEnd )
+ {
+ pCurrentNode = &aRg.aEnd.GetNode();
+ switch( pCurrentNode->GetNodeType() )
+ {
+ case SwNodeType::End:
+ {
+ if( nInsPos ) // move everything until here
+ {
+ // delete and copy. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+
+ SwStartNode* pSttNd = pCurrentNode->m_pStartOfSection;
+ if( pSttNd->IsTableNode() )
+ {
+ SwTableNode* pTableNd = static_cast<SwTableNode*>(pSttNd);
+
+ // move the whole table/range
+ nInsPos = (aRg.aEnd.GetIndex() -
+ pSttNd->GetIndex() )+1;
+ aRg.aEnd -= nInsPos;
+
+ // NEVER include nodes from the RedLineArea
+ SwNodeOffset nNd = aIdx.GetIndex();
+ bool const bInsOutlineIdx = IsInsertOutline(rNodes, nNd);
+
+ if( bNewFrames )
+ // delete all frames
+ pTableNd->DelFrames(nullptr);
+ if( &rNodes == this ) // move into self?
+ {
+ // move all Start/End/ContentNodes
+ // ContentNodes: delete also the frames!
+ pTableNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
+ for( SwNodeOffset n(0); n < nInsPos; ++n )
+ {
+ SwNodeIndex aMvIdx( aRg.aEnd, 1 );
+ SwContentNode* pCNd = nullptr;
+ SwNode* pTmpNd = &aMvIdx.GetNode();
+ if( pTmpNd->IsContentNode() )
+ {
+ pCNd = static_cast<SwContentNode*>(pTmpNd);
+ if( pTmpNd->IsTextNode() )
+ static_cast<SwTextNode*>(pTmpNd)->RemoveFromList();
+
+ // remove outline index from old nodes array
+ if (pCNd->IsTextNode() && pCNd->GetTextNode()->IsOutline())
+ {
+ m_pOutlineNodes->erase( pCNd );
+ }
+ else
+ pCNd = nullptr;
+ }
+
+ BigPtrArray::Move( sal_Int32(aMvIdx.GetIndex()), sal_Int32(aIdx.GetIndex()) );
+
+ if( bInsOutlineIdx && pCNd )
+ m_pOutlineNodes->insert( pCNd );
+ if( pTmpNd->IsTextNode() )
+ static_cast<SwTextNode*>(pTmpNd)->AddToList();
+ }
+ }
+ else
+ {
+ // get StartNode
+ // Even aIdx points to a startnode, we need the startnode
+ // of the environment of aIdx (#i80941)
+ SwStartNode* pSttNode = aIdx.GetNode().m_pStartOfSection;
+
+ // get all boxes with content because their indices
+ // pointing to the StartNodes need to be reset
+ // (copying the array and deleting all found ones eases
+ // searching)
+ SwNodeIndex aMvIdx( aRg.aEnd, 1 );
+ for( SwNodeOffset n(0); n < nInsPos; ++n )
+ {
+ SwNode* pNd = &aMvIdx.GetNode();
+
+ const bool bOutlNd = pNd->IsTextNode() && pNd->GetTextNode()->IsOutline();
+ // delete outline indices from old node array
+ if( bOutlNd )
+ m_pOutlineNodes->erase( pNd );
+
+ RemoveNode( aMvIdx.GetIndex(), SwNodeOffset(1), false );
+ pNd->m_pStartOfSection = pSttNode;
+ rNodes.InsertNode( pNd, aIdx );
+
+ // set correct indices in Start/EndNodes
+ if( bInsOutlineIdx && bOutlNd )
+ // and put them into the new node array
+ rNodes.m_pOutlineNodes->insert( pNd );
+ else if( pNd->IsStartNode() )
+ pSttNode = static_cast<SwStartNode*>(pNd);
+ else if( pNd->IsEndNode() )
+ {
+ pSttNode->m_pEndOfSection = static_cast<SwEndNode*>(pNd);
+ if( pSttNode->IsSectionNode() )
+ static_cast<SwSectionNode*>(pSttNode)->NodesArrChgd();
+ pSttNode = pSttNode->m_pStartOfSection;
+ }
+ }
+
+ if( auto pDDETable = dynamic_cast<SwDDETable*>(&pTableNd->GetTable()) )
+ {
+ SwDDEFieldType* pTyp = pDDETable->GetDDEFieldType();
+ if( pTyp )
+ {
+ if( rNodes.IsDocNodes() )
+ pTyp->IncRefCnt();
+ else
+ pTyp->DecRefCnt();
+ }
+ }
+
+ if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ pTableFormat->GetNotifier().Broadcast(SfxHint(SfxHintId::Dying));
+ }
+ }
+ if( bNewFrames )
+ {
+ SwNodeIndex aTmp( aIdx );
+ pTableNd->MakeOwnFrames(&aTmp);
+ }
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+ else if( pSttNd->GetIndex() < aRg.aStart.GetIndex() )
+ {
+ // SectionNode: not the whole section will be moved, thus,
+ // move only the ContentNodes
+ // StartNode: create a new section at the given position
+ do { // middle check loop
+ if( !pSttNd->IsSectionNode() )
+ {
+ // create StartNode and EndNode at InsertPos
+ SwStartNode* pTmp = new SwStartNode( aIdx,
+ SwNodeType::Start,
+/*?? NodeType ??*/ SwNormalStartNode );
+
+ nLevel++; // put the index to StartNode on the stack
+ aSttNdStack.insert( aSttNdStack.begin() + nLevel, pTmp );
+
+ // create EndNode
+ new SwEndNode( aIdx, *pTmp );
+ }
+ else if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(
+ rNodes))
+ {
+ // use placeholder in UndoNodes array
+ new SwPlaceholderNode(aIdx);
+ }
+ else
+ {
+ // JP 18.5.2001 (Bug 70454) creating new section?
+ --aRg.aEnd;
+ break;
+
+ }
+
+ --aRg.aEnd;
+ --aIdx;
+ } while( false );
+ }
+ else
+ {
+ // move StartNode and EndNode in total
+
+ // if Start is exactly the Start of the area,
+ // then the Node needs to be re-visited
+ if( &aRg.aStart.GetNode() == pSttNd )
+ --aRg.aStart;
+
+ SwSectionNode* pSctNd = pSttNd->GetSectionNode();
+ if( bNewFrames && pSctNd )
+ { // tdf#135056 skip over code in DelFrames() that moves
+ // SwNodeIndex around because in case of nested
+ // sections, m_pStartOfSection will point between
+ // undo nodes-array and doc nodes-array
+ pSctNd->DelFrames(nullptr, true);
+ }
+
+ RemoveNode( aRg.aEnd.GetIndex(), SwNodeOffset(1), false ); // delete EndNode
+ SwNodeOffset nSttPos = pSttNd->GetIndex();
+
+ // this StartNode will be removed later
+ SwStartNode* pTmpSttNd = new SwStartNode( *this, nSttPos+1 );
+ pTmpSttNd->m_pStartOfSection = pSttNd->m_pStartOfSection;
+
+ RemoveNode( nSttPos, SwNodeOffset(1), false ); // delete SttNode
+
+ pSttNd->m_pStartOfSection = aIdx.GetNode().m_pStartOfSection;
+ rNodes.InsertNode( pSttNd, aIdx );
+ rNodes.InsertNode( pCurrentNode, aIdx );
+ --aIdx;
+ pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
+
+ --aRg.aEnd;
+
+ nLevel++; // put the index pointing to the StartNode onto the stack
+ aSttNdStack.insert( aSttNdStack.begin() + nLevel, pSttNd );
+
+ // reset remaining indices if SectionNode
+ if( pSctNd )
+ {
+ pSctNd->NodesArrChgd();
+ ++nSectNdCnt;
+ // tdf#132326 do not let frames survive in undo nodes
+ if (!GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
+ {
+ bNewFrames = false;
+ }
+ }
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ if( !nLevel &&
+ GetDoc().GetIDocumentUndoRedo().IsUndoNodes(rNodes))
+ {
+ // here, a SectionDummyNode needs to be inserted at the current position
+ if( nInsPos ) // move everything until here
+ {
+ // delete and copy. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+ new SwPlaceholderNode(aIdx);
+ --aRg.aEnd;
+ --aIdx;
+ break;
+ }
+ [[fallthrough]];
+ case SwNodeType::Table:
+ case SwNodeType::Start:
+ {
+ // empty section -> nothing to do
+ // and only if it's a top level section
+ if( !nInsPos && !nLevel )
+ {
+ --aRg.aEnd;
+ break;
+ }
+
+ if( !nLevel ) // level is decreasing
+ {
+ // create decrease
+ SwNodeIndex aTmpSIdx( aOrigInsPos.aStart, 1 );
+ SwStartNode* pTmpStt = new SwStartNode( aTmpSIdx,
+ SwNodeType::Start,
+ static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
+
+ --aTmpSIdx;
+
+ SwNodeIndex aTmpEIdx( aOrigInsPos.aEnd );
+ new SwEndNode( aTmpEIdx, *pTmpStt );
+ --aTmpEIdx;
+ ++aTmpSIdx;
+
+ // set correct StartOfSection
+ ++aRg.aEnd;
+ {
+ SwNodeIndex aCntIdx( aRg.aEnd );
+ for( SwNodeOffset n(0); n < nInsPos; n++, ++aCntIdx)
+ aCntIdx.GetNode().m_pStartOfSection = pTmpStt;
+ }
+
+ // also set correct StartNode for all decreased nodes
+ while( aTmpSIdx < aTmpEIdx )
+ if( nullptr != (( pCurrentNode = &aTmpEIdx.GetNode())->GetEndNode()) )
+ aTmpEIdx = pCurrentNode->StartOfSectionIndex();
+ else
+ {
+ pCurrentNode->m_pStartOfSection = pTmpStt;
+ --aTmpEIdx;
+ }
+
+ --aIdx; // after the inserted StartNode
+ --aRg.aEnd; // before StartNode
+ // copy array. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos+1;
+ nInsPos = SwNodeOffset(0);
+ }
+ else // all nodes between StartNode and EndNode were moved
+ {
+ OSL_ENSURE( pCurrentNode == aSttNdStack[nLevel] ||
+ ( pCurrentNode->IsStartNode() &&
+ aSttNdStack[nLevel]->IsSectionNode()),
+ "wrong StartNode" );
+
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos+1; // before inserted StartNode
+ nInsPos = SwNodeOffset(0);
+
+ // remove pointer from node array
+ RemoveNode( aRg.aEnd.GetIndex(), SwNodeOffset(1), true );
+ --aRg.aEnd;
+
+ SwSectionNode* pSectNd = aSttNdStack[ nLevel ]->GetSectionNode();
+ if( pSectNd && !--nSectNdCnt )
+ {
+ SwNodeIndex aTmp( *pSectNd );
+ pSectNd->MakeOwnFrames(&aTmp);
+ bNewFrames = bSaveNewFrames;
+ }
+ aSttNdStack.erase( aSttNdStack.begin() + nLevel ); // remove from stack
+ nLevel--;
+ }
+
+ // delete all resulting empty start/end node pairs
+ SwNode* pTmpNode = (*this)[ aRg.aEnd.GetIndex()+1 ]->GetEndNode();
+ if( pTmpNode && SwNodeType::Start == (pCurrentNode = &aRg.aEnd.GetNode())
+ ->GetNodeType() && pCurrentNode->StartOfSectionIndex() &&
+ pTmpNode->StartOfSectionNode() == pCurrentNode )
+ {
+ DelNodes( aRg.aEnd, SwNodeOffset(2) );
+ --aRg.aEnd;
+ }
+ }
+ break;
+
+ case SwNodeType::Text:
+ //Add special function to text node.
+ {
+ if( bNewFrames && pCurrentNode->GetContentNode() )
+ static_cast<SwContentNode*>(pCurrentNode)->DelFrames(nullptr);
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
+ nInsPos++;
+ --aRg.aEnd;
+ }
+ break;
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ {
+ if( bNewFrames && pCurrentNode->GetContentNode() )
+ static_cast<SwContentNode*>(pCurrentNode)->DelFrames(nullptr);
+
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
+ nInsPos++;
+ --aRg.aEnd;
+ }
+ break;
+
+ case SwNodeType::PlaceHolder:
+ if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
+ {
+ if( &rNodes == this ) // inside UndoNodesArray
+ {
+ // move everything
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ nLevel ];
+ nInsPos++;
+ }
+ else // move into "normal" node array
+ {
+ // than a SectionNode (start/end) is needed at the current
+ // InsPos; if so skip it, otherwise ignore current node
+ if( nInsPos ) // move everything until here
+ {
+ // delete and copy. CAUTION: all indices after
+ // "aRg.aEnd+1" will be moved as well!
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ aIdx -= nInsPos;
+ nInsPos = SwNodeOffset(0);
+ }
+ SwNode* pTmpNd = &aIdx.GetNode();
+ if( pTmpNd->IsSectionNode() ||
+ pTmpNd->StartOfSectionNode()->IsSectionNode() )
+ --aIdx; // skip
+ }
+ }
+ else {
+ assert(!"How can this node be in the node array?");
+ }
+ --aRg.aEnd;
+ break;
+
+ default:
+ assert(!"Unknown node type");
+ break;
+ }
+ }
+
+ if( nInsPos ) // copy remaining rest
+ {
+ // rest should be ok
+ SwNodeIndex aSwIndex( aRg.aEnd, 1 );
+ ChgNode( aSwIndex, nInsPos, aIdx, bNewFrames );
+ }
+ ++aRg.aEnd; // again, exclusive end
+
+ // delete all resulting empty start/end node pairs
+ if( ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
+ pCurrentNode->StartOfSectionIndex() &&
+ aRg.aEnd.GetNode().GetEndNode() )
+ DelNodes( aRg.aStart, SwNodeOffset(2) );
+
+ // initialize numbering update
+ ++aOrigInsPos.aStart;
+ // Moved in same node array? Then call update top down!
+ if( this == &rNodes &&
+ aRg.aEnd.GetIndex() >= aOrigInsPos.aStart.GetIndex() )
+ {
+ UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
+ UpdateOutlineIdx( aRg.aEnd.GetNode() );
+ }
+ else
+ {
+ UpdateOutlineIdx( aRg.aEnd.GetNode() );
+ rNodes.UpdateOutlineIdx( aOrigInsPos.aStart.GetNode() );
+ }
+
+ return true;
+}
+
+/** create a start/end section pair
+ *
+ * Other nodes might be in between.
+ *
+ * After this method call, the start node of pRange will be pointing to the
+ * first node after the start section node and the end node will be the index
+ * of the end section node. If this method is called multiple times with the
+ * same input, multiple sections containing the previous ones will be created
+ * (no content nodes between start or end node).
+ *
+ * @note Start and end node of the range must be on the same level but MUST
+ * NOT be on the top level.
+ *
+ * @param [IN,OUT] pRange the range (excl. end)
+ * @param eSttNdTyp type of the start node
+ */
+void SwNodes::SectionDown(SwNodeRange *pRange, SwStartNodeType eSttNdTyp )
+{
+ if( pRange->aStart >= pRange->aEnd ||
+ pRange->aEnd >= Count() ||
+ !::CheckNodesRange(pRange->aStart, pRange->aEnd, false))
+ {
+ return;
+ }
+
+ // If the beginning of a range is before or at a start node position, so
+ // delete it, otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, create a new start node.
+ SwNode * pCurrentNode = &pRange->aStart.GetNode();
+ SwNodeIndex aTmpIdx( *pCurrentNode->StartOfSectionNode() );
+
+ if( pCurrentNode->GetEndNode() )
+ DelNodes( pRange->aStart ); // prevent empty section
+ else
+ {
+ // insert a new StartNode
+ SwNode* pSttNd = new SwStartNode( pRange->aStart, SwNodeType::Start, eSttNdTyp );
+ pRange->aStart = *pSttNd;
+ aTmpIdx = pRange->aStart;
+ }
+
+ // If the end of a range is before or at a StartNode, so delete it,
+ // otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, insert a new end node.
+ --pRange->aEnd;
+ if( pRange->aEnd.GetNode().GetStartNode() )
+ DelNodes( pRange->aEnd );
+ else
+ {
+ ++pRange->aEnd;
+ // insert a new EndNode
+ new SwEndNode( pRange->aEnd, *pRange->aStart.GetNode().GetStartNode() );
+ }
+ --pRange->aEnd;
+
+ SectionUpDown( aTmpIdx, pRange->aEnd );
+}
+
+/** increase level of the given range
+ *
+ * The range contained in pRange will be lifted to the next higher level.
+ * This is done by adding an end node at pRange.start and a start node at
+ * pRange.end. Furthermore all indices for this range will be updated.
+ *
+ * After this method call, the start node of pRange will be pointing to the
+ * first node inside the lifted range and the end node will be pointing to the
+ * last position inside the lifted range.
+ *
+ * @param [IN,OUT] pRange the range of nodes where the level should be increased
+ */
+void SwNodes::SectionUp(SwNodeRange *pRange)
+{
+ if( pRange->aStart >= pRange->aEnd ||
+ pRange->aEnd >= Count() ||
+ !::CheckNodesRange(pRange->aStart, pRange->aEnd, false) ||
+ ( HighestLevel( *this, *pRange ) <= 1 ))
+ {
+ return;
+ }
+
+ // If the beginning of a range is before or at a start node position, so
+ // delete it, otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, create a new start node.
+ SwNode * pCurrentNode = &pRange->aStart.GetNode();
+ SwNodeIndex aIdx( *pCurrentNode->StartOfSectionNode() );
+ if( pCurrentNode->IsStartNode() ) // is StartNode itself
+ {
+ SwEndNode* pEndNd = pRange->aEnd.GetNode().GetEndNode();
+ if (pEndNd && pCurrentNode == pEndNd->m_pStartOfSection)
+ {
+ // there was a pairwise reset, adjust only those in the range
+ SwStartNode* pTmpSttNd = pCurrentNode->m_pStartOfSection;
+ RemoveNode( pRange->aStart.GetIndex(), SwNodeOffset(1), true );
+ RemoveNode( pRange->aEnd.GetIndex(), SwNodeOffset(1), true );
+
+ SwNodeIndex aTmpIdx( pRange->aStart );
+ while( aTmpIdx < pRange->aEnd )
+ {
+ pCurrentNode = &aTmpIdx.GetNode();
+ pCurrentNode->m_pStartOfSection = pTmpSttNd;
+ if( pCurrentNode->IsStartNode() )
+ aTmpIdx = pCurrentNode->EndOfSectionIndex() + 1;
+ else
+ ++aTmpIdx;
+ }
+ return ;
+ }
+ DelNodes( pRange->aStart );
+ }
+ else if( aIdx == pRange->aStart.GetIndex()-1 ) // before StartNode
+ DelNodes( aIdx );
+ else
+ new SwEndNode( pRange->aStart, *aIdx.GetNode().GetStartNode() );
+
+ // If the end of a range is before or at a StartNode, so delete it,
+ // otherwise empty S/E or E/S nodes would be created.
+ // For other nodes, insert a new end node.
+ SwNodeIndex aTmpIdx( pRange->aEnd );
+ if( pRange->aEnd.GetNode().IsEndNode() )
+ DelNodes( pRange->aEnd );
+ else
+ {
+ new SwStartNode( pRange->aEnd );
+/*?? which NodeType ??*/
+ aTmpIdx = *pRange->aEnd.GetNode().EndOfSectionNode();
+ --pRange->aEnd;
+ }
+
+ SectionUpDown( aIdx, aTmpIdx );
+}
+
+/** correct indices after movement
+ *
+ * Update all indices after movement so that the levels are consistent again.
+ *
+ * @param aStart index of the start node
+ * @param aEnd index of the end point
+ *
+ * @see SwNodes::SectionUp
+ * @see SwNodes::SectionDown
+ */
+void SwNodes::SectionUpDown( const SwNodeIndex & aStart, const SwNodeIndex & aEnd )
+{
+ SwNodeIndex aTmpIdx( aStart, +1 );
+ // array forms a stack, holding all StartOfSelections
+ SwStartNodePointers aSttNdStack;
+ SwStartNode* pTmp = aStart.GetNode().GetStartNode();
+ aSttNdStack.push_back( pTmp );
+
+ // loop until the first start node that needs to be change was found
+ // (the indices are updated from the end node backwards to the start)
+ for( ;; ++aTmpIdx )
+ {
+ SwNode * pCurrentNode = &aTmpIdx.GetNode();
+ pCurrentNode->m_pStartOfSection = aSttNdStack[ aSttNdStack.size()-1 ];
+
+ if( pCurrentNode->GetStartNode() )
+ {
+ pTmp = static_cast<SwStartNode*>(pCurrentNode);
+ aSttNdStack.push_back( pTmp );
+ }
+ else if( pCurrentNode->GetEndNode() )
+ {
+ SwStartNode* pSttNd = aSttNdStack[ aSttNdStack.size() - 1 ];
+ pSttNd->m_pEndOfSection = static_cast<SwEndNode*>(pCurrentNode);
+ aSttNdStack.pop_back();
+ if( !aSttNdStack.empty() )
+ continue; // still enough EndNodes on the stack
+
+ else if( aTmpIdx < aEnd ) // too many StartNodes
+ // if the end is not reached, yet, get the start of the section above
+ {
+ aSttNdStack.insert( aSttNdStack.begin(), pSttNd->m_pStartOfSection );
+ }
+ else // finished, as soon as out of the range
+ break;
+ }
+ }
+}
+
+/** delete nodes
+ *
+ * This is a specific implementation of a delete function for a variable array.
+ * It is necessary as there might be inconsistencies after deleting start or
+ * end nodes. This method can clean those up.
+ *
+ * @param rIndex position to delete at (unchanged afterwards)
+ * @param nNodes number of nodes to delete (default: 1)
+ */
+void SwNodes::Delete(const SwNodeIndex &rIndex, SwNodeOffset nNodes)
+{
+ int nLevel = 0; // level counter
+ SwNode * pCurrentNode;
+
+ SwNodeOffset nCnt = Count() - rIndex.GetIndex() - 1;
+ if( nCnt > nNodes ) nCnt = nNodes;
+
+ if( nCnt == SwNodeOffset(0) ) // no count -> return
+ return;
+
+ SwNodeRange aRg( rIndex, SwNodeOffset(0), rIndex, nCnt-1 );
+ // check if [rIndex..rIndex + nCnt] is larger than the range
+ if( ( !aRg.aStart.GetNode().StartOfSectionIndex() &&
+ !aRg.aStart.GetIndex() ) ||
+ !::CheckNodesRange(aRg.aStart, aRg.aEnd, false))
+ {
+ return;
+ }
+
+ // if aEnd is not on a ContentNode, search the previous one
+ while( ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() ||
+ ( pCurrentNode->GetEndNode() &&
+ !pCurrentNode->m_pStartOfSection->IsTableNode() ))
+ --aRg.aEnd;
+
+ nCnt = SwNodeOffset(0);
+//TODO: check/improve comment
+ // increase start so that we are able to use "<" (using "<=" might cause
+ // problems if aEnd == aStart and aEnd is deleted, so aEnd <= aStart)
+ --aRg.aStart;
+
+ bool bSaveInNodesDel = m_bInNodesDel;
+ m_bInNodesDel = true;
+ bool bUpdateOutline = false;
+
+ // loop until everything is deleted
+ while( aRg.aStart < aRg.aEnd )
+ {
+ pCurrentNode = &aRg.aEnd.GetNode();
+
+ if( pCurrentNode->GetEndNode() )
+ {
+ // delete the whole section?
+ if( pCurrentNode->StartOfSectionIndex() > aRg.aStart.GetIndex() )
+ {
+ SwTableNode* pTableNd = pCurrentNode->m_pStartOfSection->GetTableNode();
+ if( pTableNd )
+ pTableNd->DelFrames();
+
+ SwNode *pNd, *pChkNd = pCurrentNode->m_pStartOfSection;
+ SwOutlineNodes::size_type nIdxPos;
+ do {
+ pNd = &aRg.aEnd.GetNode();
+
+ if( pNd->IsTextNode() )
+ {
+ SwTextNode *const pTextNode(pNd->GetTextNode());
+ if (pTextNode->IsOutline() &&
+ m_pOutlineNodes->Seek_Entry( pNd, &nIdxPos ))
+ {
+ // remove outline indices
+ m_pOutlineNodes->erase_at(nIdxPos);
+ bUpdateOutline = true;
+ }
+ pTextNode->InvalidateNumRule();
+ }
+ else if( pNd->IsEndNode() &&
+ pNd->m_pStartOfSection->IsTableNode() )
+ static_cast<SwTableNode*>(pNd->m_pStartOfSection)->DelFrames();
+
+ --aRg.aEnd;
+ nCnt++;
+
+ } while( pNd != pChkNd );
+ }
+ else
+ {
+ RemoveNode( aRg.aEnd.GetIndex()+1, nCnt, true ); // delete
+ nCnt = SwNodeOffset(0);
+ --aRg.aEnd; // before the EndNode
+ nLevel++;
+ }
+ }
+ else if( pCurrentNode->GetStartNode() ) // found StartNode
+ {
+ if( nLevel == 0 ) // decrease one level
+ {
+ if( nCnt )
+ {
+ // now delete array
+ ++aRg.aEnd;
+ RemoveNode( aRg.aEnd.GetIndex(), nCnt, true );
+ nCnt = SwNodeOffset(0);
+ }
+ }
+ else // remove all nodes between start and end node (incl. both)
+ {
+ RemoveNode( aRg.aEnd.GetIndex(), nCnt + 2, true ); // delete array
+ nCnt = SwNodeOffset(0);
+ nLevel--;
+ }
+
+ // after deletion, aEnd might point to an EndNode...
+ // delete all empty start/end node pairs
+ SwNode* pTmpNode = aRg.aEnd.GetNode().GetEndNode();
+ --aRg.aEnd;
+ while( pTmpNode &&
+ ( pCurrentNode = &aRg.aEnd.GetNode())->GetStartNode() &&
+ pCurrentNode->StartOfSectionIndex() )
+ {
+ // remove end and start node
+ DelNodes( aRg.aEnd, SwNodeOffset(2) );
+ pTmpNode = aRg.aEnd.GetNode().GetEndNode();
+ --aRg.aEnd;
+ }
+ }
+ else // "normal" node, so insert into TmpArray
+ {
+ SwTextNode* pTextNd = pCurrentNode->GetTextNode();
+ if( pTextNd )
+ {
+ if( pTextNd->IsOutline())
+ {
+ // delete outline indices
+ m_pOutlineNodes->erase( pTextNd );
+ bUpdateOutline = true;
+ }
+ if (sw::HasNumberingWhichNeedsLayoutUpdate(*pTextNd))
+ {
+ pTextNd->InvalidateNumRule();
+ }
+ }
+ else if( pCurrentNode->IsContentNode() )
+ static_cast<SwContentNode*>(pCurrentNode)->InvalidateNumRule();
+
+ --aRg.aEnd;
+ nCnt++;
+ }
+ }
+
+ ++aRg.aEnd;
+ if( nCnt != SwNodeOffset(0) )
+ RemoveNode( aRg.aEnd.GetIndex(), nCnt, true ); // delete the rest
+
+ // delete all empty start/end node pairs
+ while( aRg.aEnd.GetNode().GetEndNode() &&
+ ( pCurrentNode = &aRg.aStart.GetNode())->GetStartNode() &&
+ pCurrentNode->StartOfSectionIndex() )
+ // but none of the holy 5. (???)
+ {
+ DelNodes( aRg.aStart, SwNodeOffset(2) ); // delete start and end node
+ --aRg.aStart;
+ }
+
+ m_bInNodesDel = bSaveInNodesDel;
+
+ if( !m_bInNodesDel )
+ {
+ // update numbering
+ if( bUpdateOutline || m_bInDelUpdOutline )
+ {
+ UpdateOutlineIdx( aRg.aEnd.GetNode() );
+ m_bInDelUpdOutline = false;
+ }
+
+ }
+ else
+ {
+ if( bUpdateOutline )
+ m_bInDelUpdOutline = true;
+ }
+}
+
+/** get section level at the given position
+ *
+ * @note The first node in an array should always be a start node.
+ * Because of this, there is a special treatment here based on the
+ * assumption that this is true in this context as well.
+ *
+ * @param rIdx position of the node
+ * @return section level at the given position
+ */
+sal_uInt16 SwNodes::GetSectionLevel(const SwNodeIndex &rIdx)
+{
+ // special treatment for 1st Node
+ if(rIdx == SwNodeOffset(0)) return 1;
+ // no recursion! This calls a SwNode::GetSectionLevel (missing "s")
+ return rIdx.GetNode().GetSectionLevel();
+}
+
+void SwNodes::GoStartOfSection(SwNodeIndex *pIdx)
+{
+ // after the next start node
+ SwNodeIndex aTmp( *pIdx->GetNode().StartOfSectionNode(), +1 );
+
+ // If index points to no ContentNode, then go to one.
+ // If there is no further available, do not change the index' position!
+ while( !aTmp.GetNode().IsContentNode() )
+ { // go from this StartNode (can only be one) to its end
+ if( *pIdx <= aTmp )
+ return; // ERROR: already after the section
+ aTmp = aTmp.GetNode().EndOfSectionIndex()+1;
+ if( *pIdx <= aTmp )
+ return; // ERROR: already after the section
+ }
+ (*pIdx) = aTmp; // is on a ContentNode
+}
+
+void SwNodes::GoEndOfSection(SwNodeIndex *pIdx)
+{
+ if( !pIdx->GetNode().IsEndNode() )
+ (*pIdx) = *pIdx->GetNode().EndOfSectionNode();
+}
+
+SwContentNode* SwNodes::GoNext(SwNodeIndex *pIdx) const
+{
+ if( pIdx->GetIndex() >= Count() - 1 )
+ return nullptr;
+
+ SwNodeIndex aTmp(*pIdx, +1);
+ SwNode* pNd = nullptr;
+ while( aTmp < Count()-1 && !( pNd = &aTmp.GetNode())->IsContentNode() )
+ ++aTmp;
+
+ if( aTmp == Count()-1 )
+ pNd = nullptr;
+ else
+ (*pIdx) = aTmp;
+ return static_cast<SwContentNode*>(pNd);
+}
+
+SwContentNode* SwNodes::GoPrevious(SwNodeIndex *pIdx)
+{
+ if( !pIdx->GetIndex() )
+ return nullptr;
+
+ SwNodeIndex aTmp( *pIdx, -1 );
+ SwNode* pNd = nullptr;
+ while( aTmp.GetIndex() && !( pNd = &aTmp.GetNode())->IsContentNode() )
+ --aTmp;
+
+ if( !aTmp.GetIndex() )
+ pNd = nullptr;
+ else
+ (*pIdx) = aTmp;
+ return static_cast<SwContentNode*>(pNd);
+}
+
+/** Delete a number of nodes
+ *
+ * @param rStart starting position in this nodes array
+ * @param nCnt number of nodes to delete
+ */
+void SwNodes::DelNodes( const SwNodeIndex & rStart, SwNodeOffset nCnt )
+{
+ SwNodeOffset nSttIdx = rStart.GetIndex();
+
+ if( !nSttIdx && nCnt == GetEndOfContent().GetIndex()+1 )
+ {
+ // The whole nodes array will be destroyed, you're in the Doc's DTOR!
+ // The initial start/end nodes should be only destroyed in the SwNodes' DTOR!
+ SwNode* aEndNdArr[] = { m_pEndOfContent.get(),
+ m_pEndOfPostIts, m_pEndOfInserts,
+ m_pEndOfAutotext, m_pEndOfRedlines,
+ nullptr
+ };
+
+ SwNode** ppEndNdArr = aEndNdArr;
+ while( *ppEndNdArr )
+ {
+ nSttIdx = (*ppEndNdArr)->StartOfSectionIndex() + 1;
+ SwNodeOffset nEndIdx = (*ppEndNdArr)->GetIndex();
+
+ if( nSttIdx != nEndIdx )
+ RemoveNode( nSttIdx, nEndIdx - nSttIdx, true );
+
+ ++ppEndNdArr;
+ }
+ }
+ else
+ {
+ int bUpdateNum = 0;
+ for( SwNodeOffset n = nSttIdx, nEnd = nSttIdx + nCnt; n < nEnd; ++n )
+ {
+ SwNode* pNd = (*this)[ n ];
+
+ if (pNd->IsTextNode() && pNd->GetTextNode()->IsOutline())
+ {
+ // remove the outline indices
+ if (m_pOutlineNodes->erase(pNd))
+ bUpdateNum = 1;
+ }
+ if( pNd->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNd)->InvalidateNumRule();
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ }
+ }
+ RemoveNode( nSttIdx, nCnt, true );
+
+ // update numbering
+ if( bUpdateNum )
+ UpdateOutlineIdx( rStart.GetNode() );
+ }
+}
+
+namespace {
+
+struct HighLevel
+{
+ sal_uInt16 nLevel, nTop;
+ explicit HighLevel( sal_uInt16 nLv ) : nLevel( nLv ), nTop( nLv ) {}
+};
+
+}
+
+static bool lcl_HighestLevel( SwNode* pNode, void * pPara )
+{
+ HighLevel * pHL = static_cast<HighLevel*>(pPara);
+ if( pNode->GetStartNode() )
+ pHL->nLevel++;
+ else if( pNode->GetEndNode() )
+ pHL->nLevel--;
+ if( pHL->nTop > pHL->nLevel )
+ pHL->nTop = pHL->nLevel;
+ return true;
+
+}
+
+/** Calculate the highest level in a range
+ *
+ * @param rNodes the nodes array
+ * @param rRange the range to inspect
+ * @return the highest level
+ */
+sal_uInt16 HighestLevel( SwNodes & rNodes, const SwNodeRange & rRange )
+{
+ HighLevel aPara( SwNodes::GetSectionLevel( rRange.aStart ));
+ rNodes.ForEach( rRange.aStart, rRange.aEnd, lcl_HighestLevel, &aPara );
+ return aPara.nTop;
+
+}
+
+/** move a range
+ *
+ * @param rPam the range to move
+ * @param rPos to destination position in the given nodes array
+ * @param rNodes the node array to move the range into
+ */
+void SwNodes::MoveRange( SwPaM & rPam, SwPosition & rPos, SwNodes& rNodes )
+{
+ SwPosition * const pStt = rPam.Start();
+ SwPosition * const pEnd = rPam.End();
+
+ if( !rPam.HasMark() || *pStt >= *pEnd )
+ return;
+
+ if( this == &rNodes && *pStt <= rPos && rPos < *pEnd )
+ return;
+
+ SwNodeIndex aEndIdx( pEnd->nNode );
+ SwNodeIndex aSttIdx( pStt->nNode );
+ SwTextNode *const pSrcNd = aSttIdx.GetNode().GetTextNode();
+ SwTextNode * pDestNd = rPos.nNode.GetNode().GetTextNode();
+ bool bSplitDestNd = true;
+ bool bCopyCollFormat = pDestNd && pDestNd->GetText().isEmpty();
+
+ if( pSrcNd )
+ {
+ // if the first node is a TextNode, then there must
+ // be also a TextNode in the NodesArray to store the content
+ if( !pDestNd )
+ {
+ pDestNd = rNodes.MakeTextNode( rPos.nNode, pSrcNd->GetTextColl() );
+ --rPos.nNode;
+ rPos.nContent.Assign( pDestNd, 0 );
+ bCopyCollFormat = true;
+ }
+ bSplitDestNd = pDestNd->Len() > rPos.nContent.GetIndex() ||
+ pEnd->nNode.GetNode().IsTextNode();
+
+ // move the content into the new node
+ bool bOneNd = pStt->nNode == pEnd->nNode;
+ const sal_Int32 nLen =
+ ( bOneNd ? std::min(pEnd->nContent.GetIndex(), pSrcNd->Len()) : pSrcNd->Len() )
+ - pStt->nContent.GetIndex();
+
+ if( !pEnd->nNode.GetNode().IsContentNode() )
+ {
+ bOneNd = true;
+ SwNodeOffset nSttNdIdx = pStt->nNode.GetIndex() + 1;
+ const SwNodeOffset nEndNdIdx = pEnd->nNode.GetIndex();
+ for( ; nSttNdIdx < nEndNdIdx; ++nSttNdIdx )
+ {
+ if( (*this)[ nSttNdIdx ]->IsContentNode() )
+ {
+ bOneNd = false;
+ break;
+ }
+ }
+ }
+
+ // templates must be copied/set after a split
+ if( !bOneNd && bSplitDestNd )
+ {
+ if( !rPos.nContent.GetIndex() )
+ {
+ bCopyCollFormat = true;
+ }
+ if( rNodes.IsDocNodes() )
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
+ rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+ else
+ {
+ pDestNd->SplitContentNode(rPos, nullptr);
+ }
+
+ if( rPos.nNode == aEndIdx )
+ {
+ --aEndIdx;
+ }
+ bSplitDestNd = true;
+
+ pDestNd = rNodes[ rPos.nNode.GetIndex() - 1 ]->GetTextNode();
+ if( nLen )
+ {
+ pSrcNd->CutText( pDestNd, SwIndex( pDestNd, pDestNd->Len()),
+ pStt->nContent, nLen );
+ }
+ }
+ else if ( nLen )
+ {
+ pSrcNd->CutText( pDestNd, rPos.nContent, pStt->nContent, nLen );
+ }
+
+ if( bCopyCollFormat )
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const undoGuard(rInsDoc.GetIDocumentUndoRedo());
+ pSrcNd->CopyCollFormat( *pDestNd );
+ }
+
+ if( bOneNd )
+ {
+ // Correct the PaM, because it might have happened that the move
+ // went over the node borders (so the data might be in different nodes).
+ // Also, a selection is invalidated.
+ pEnd->nContent = pStt->nContent;
+ rPam.DeleteMark();
+ GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
+ rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
+ return;
+ }
+
+ ++aSttIdx;
+ }
+ else if( pDestNd )
+ {
+ if( rPos.nContent.GetIndex() )
+ {
+ if( rPos.nContent.GetIndex() == pDestNd->Len() )
+ {
+ ++rPos.nNode;
+ }
+ else if( rPos.nContent.GetIndex() )
+ {
+ // if the EndNode is split than correct the EndIdx
+ const bool bCorrEnd = aEndIdx == rPos.nNode;
+
+ // if no text is attached to the TextNode, split it
+ if( rNodes.IsDocNodes() )
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
+ rInsDoc.getIDocumentContentOperations().SplitNode( rPos, false );
+ }
+ else
+ {
+ pDestNd->SplitContentNode(rPos, nullptr);
+ }
+
+ if ( bCorrEnd )
+ {
+ --aEndIdx;
+ }
+ }
+ }
+ // at the end only an empty TextNode is left over
+ bSplitDestNd = true;
+ }
+
+ SwTextNode* const pEndSrcNd = aEndIdx.GetNode().GetTextNode();
+ if ( pEndSrcNd )
+ {
+ // at the end of this range a new TextNode will be created
+ if( !bSplitDestNd )
+ {
+ if( rPos.nNode < rNodes.GetEndOfContent().GetIndex() )
+ {
+ ++rPos.nNode;
+ }
+
+ pDestNd =
+ rNodes.MakeTextNode( rPos.nNode, pEndSrcNd->GetTextColl() );
+ --rPos.nNode;
+ rPos.nContent.Assign( pDestNd, 0 );
+ }
+ else
+ {
+ pDestNd = rPos.nNode.GetNode().GetTextNode();
+ }
+
+ if (pDestNd && pEnd->nContent.GetIndex())
+ {
+ // move the content into the new node
+ SwIndex aIdx( pEndSrcNd, 0 );
+ pEndSrcNd->CutText( pDestNd, rPos.nContent, aIdx,
+ pEnd->nContent.GetIndex());
+ }
+
+ if (pDestNd && bCopyCollFormat)
+ {
+ SwDoc& rInsDoc = pDestNd->GetDoc();
+ ::sw::UndoGuard const ug(rInsDoc.GetIDocumentUndoRedo());
+ pEndSrcNd->CopyCollFormat( *pDestNd );
+ }
+ }
+ else
+ {
+ if ( pSrcNd && aEndIdx.GetNode().IsContentNode() )
+ {
+ ++aEndIdx;
+ }
+ if( !bSplitDestNd )
+ {
+ ++rPos.nNode;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), 0 );
+ }
+ }
+
+ if( aEndIdx != aSttIdx )
+ {
+ // move the nodes into the NodesArray
+ const SwNodeOffset nSttDiff = aSttIdx.GetIndex() - pStt->nNode.GetIndex();
+ SwNodeRange aRg( aSttIdx, aEndIdx );
+ MoveNodes( aRg, rNodes, rPos.nNode );
+
+ // if in the same node array, all indices are now at new positions (so correct them)
+ if( &rNodes == this )
+ {
+ pStt->nNode = aRg.aEnd.GetIndex() - nSttDiff;
+ }
+ }
+
+ // if the StartNode was moved to whom the cursor pointed, so
+ // the content must be registered in the current content!
+ if ( &pStt->nNode.GetNode() == &GetEndOfContent() )
+ {
+ const bool bSuccess = GoPrevious( &pStt->nNode );
+ OSL_ENSURE( bSuccess, "Move() - no ContentNode here" );
+ }
+ pStt->nContent.Assign( pStt->nNode.GetNode().GetContentNode(),
+ pStt->nContent.GetIndex() );
+ // Correct the PaM, because it might have happened that the move
+ // went over the node borders (so the data might be in different nodes).
+ // Also, a selection is invalidated.
+ *pEnd = *pStt;
+ rPam.DeleteMark();
+ GetDoc().GetDocShell()->Broadcast( SwFormatFieldHint( nullptr,
+ rNodes.IsDocNodes() ? SwFormatFieldHintWhich::INSERTED : SwFormatFieldHintWhich::REMOVED ) );
+}
+
+///@see SwNodes::MoveNodes (TODO: seems to be C&P programming here)
+void SwNodes::CopyNodes( const SwNodeRange& rRange,
+ const SwNodeIndex& rIndex, bool bNewFrames, bool bTableInsDummyNode ) const
+{
+ SwDoc& rDoc = rIndex.GetNode().GetDoc();
+
+ SwNode * pCurrentNode;
+ if( rIndex == SwNodeOffset(0) ||
+ ( (pCurrentNode = &rIndex.GetNode())->GetStartNode() &&
+ !pCurrentNode->StartOfSectionIndex() ))
+ return;
+
+ SwNodeRange aRg( rRange );
+
+ // skip "simple" StartNodes or EndNodes
+ while( SwNodeType::Start == (pCurrentNode = & aRg.aStart.GetNode())->GetNodeType()
+ || ( pCurrentNode->IsEndNode() &&
+ !pCurrentNode->m_pStartOfSection->IsSectionNode() ) )
+ ++aRg.aStart;
+
+ const SwNode *aEndNode = &aRg.aEnd.GetNode();
+ SwNodeOffset nIsEndOfContent((aEndNode == &aEndNode->GetNodes().GetEndOfContent()) ? 1 : 0);
+
+ if (SwNodeOffset(0) == nIsEndOfContent)
+ {
+ // if aEnd-1 points to no ContentNode, search previous one
+ --aRg.aEnd;
+ // #i107142#: if aEnd is start node of a special section, do nothing.
+ // Otherwise this could lead to crash: going through all previous
+ // special section nodes and then one before the first.
+ if (aRg.aEnd.GetNode().StartOfSectionIndex() != SwNodeOffset(0))
+ {
+ while( ((pCurrentNode = & aRg.aEnd.GetNode())->GetStartNode() &&
+ !pCurrentNode->IsSectionNode() ) ||
+ ( pCurrentNode->IsEndNode() &&
+ SwNodeType::Start == pCurrentNode->m_pStartOfSection->GetNodeType()) )
+ {
+ --aRg.aEnd;
+ }
+ }
+ ++aRg.aEnd;
+ }
+
+ // is there anything left to copy?
+ if( aRg.aStart >= aRg.aEnd )
+ return;
+
+ // when inserting into the source range, nothing need to be done
+ OSL_ENSURE( &aRg.aStart.GetNodes() == this,
+ "aRg should use this node array" );
+ OSL_ENSURE( &aRg.aStart.GetNodes() == &aRg.aEnd.GetNodes(),
+ "Range across different nodes arrays? You deserve punishment!");
+ if( &rIndex.GetNodes() == &aRg.aStart.GetNodes() &&
+ rIndex.GetIndex() >= aRg.aStart.GetIndex() &&
+ rIndex.GetIndex() < aRg.aEnd.GetIndex() )
+ return;
+
+ SwNodeIndex aInsPos( rIndex );
+ SwNodeIndex aOrigInsPos( rIndex, -1 ); // original insertion position
+ int nLevel = 0; // level counter
+
+ for( SwNodeOffset nNodeCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ nNodeCnt > SwNodeOffset(0); --nNodeCnt )
+ {
+ pCurrentNode = &aRg.aStart.GetNode();
+ switch( pCurrentNode->GetNodeType() )
+ {
+ case SwNodeType::Table:
+ // Does it copy a table in(to) a footnote?
+ if( aInsPos < rDoc.GetNodes().GetEndOfInserts().GetIndex() &&
+ rDoc.GetNodes().GetEndOfInserts().StartOfSectionIndex()
+ < aInsPos.GetIndex() )
+ {
+ const SwNodeOffset nDistance =
+ pCurrentNode->EndOfSectionIndex() -
+ aRg.aStart.GetIndex();
+ if (nDistance < nNodeCnt)
+ nNodeCnt -= nDistance;
+ else
+ nNodeCnt = SwNodeOffset(1);
+
+ // insert a DummyNode for a TableNode
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+
+ // copy all of the table's nodes into the current cell
+ for( ++aRg.aStart; aRg.aStart.GetIndex() <
+ pCurrentNode->EndOfSectionIndex();
+ ++aRg.aStart )
+ {
+ // insert a DummyNode for the box-StartNode?
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+
+ SwStartNode* pSttNd = aRg.aStart.GetNode().GetStartNode();
+ CopyNodes( SwNodeRange( *pSttNd, SwNodeOffset(+ 1),
+ *pSttNd->EndOfSectionNode() ),
+ aInsPos, bNewFrames );
+
+ // insert a DummyNode for the box-EndNode?
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+ aRg.aStart = *pSttNd->EndOfSectionNode();
+ }
+ // insert a DummyNode for the table-EndNode
+ if( bTableInsDummyNode )
+ new SwPlaceholderNode(aInsPos);
+ aRg.aStart = *pCurrentNode->EndOfSectionNode();
+ }
+ else
+ {
+ SwNodeIndex nStt( aInsPos, -1 );
+ SwTableNode* pTableNd = static_cast<SwTableNode*>(pCurrentNode)->
+ MakeCopy( rDoc, aInsPos );
+ const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
+ if (nDistance < nNodeCnt)
+ nNodeCnt -= nDistance;
+ else
+ nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
+
+ aRg.aStart = pCurrentNode->EndOfSectionIndex();
+
+ if( bNewFrames && pTableNd )
+ {
+ nStt = aInsPos;
+ pTableNd->MakeOwnFrames(&nStt);
+ }
+ }
+ break;
+
+ case SwNodeType::Section:
+ // If the end of the section is outside the copy range,
+ // the section node will skipped, not copied!
+ // If someone want to change this behaviour, he has to adjust the function
+ // lcl_NonCopyCount(..) in ndcopy.cxx which relies on it.
+ if( pCurrentNode->EndOfSectionIndex() < aRg.aEnd.GetIndex() )
+ {
+ // copy of the whole section, so create a new SectionNode
+ SwNodeIndex nStt( aInsPos, -1 );
+ SwSectionNode* pSectNd = static_cast<SwSectionNode*>(pCurrentNode)->
+ MakeCopy( rDoc, aInsPos );
+
+ const SwNodeOffset nDistance = aInsPos.GetIndex() - nStt.GetIndex() - 2;
+ if (nDistance < nNodeCnt)
+ nNodeCnt -= nDistance;
+ else
+ nNodeCnt = SwNodeOffset(1) - nIsEndOfContent;
+ aRg.aStart = pCurrentNode->EndOfSectionIndex();
+
+ if( bNewFrames && pSectNd &&
+ !pSectNd->GetSection().IsHidden() )
+ pSectNd->MakeOwnFrames(&nStt);
+ }
+ break;
+
+ case SwNodeType::Start:
+ {
+ SwStartNode* pTmp = new SwStartNode( aInsPos, SwNodeType::Start,
+ static_cast<SwStartNode*>(pCurrentNode)->GetStartNodeType() );
+ new SwEndNode( aInsPos, *pTmp );
+ --aInsPos;
+ nLevel++;
+ }
+ break;
+
+ case SwNodeType::End:
+ if( nLevel ) // complete section
+ {
+ --nLevel;
+ ++aInsPos; // EndNode already exists
+ }
+ else if( SwNodeOffset(1) == nNodeCnt && SwNodeOffset(1) == nIsEndOfContent )
+ // we have reached the EndOfContent node - nothing to do!
+ continue;
+ else if( !pCurrentNode->m_pStartOfSection->IsSectionNode() )
+ {
+ // create a section at the original InsertPosition
+ SwNodeRange aTmpRg( aOrigInsPos, SwNodeOffset(1), aInsPos );
+ rDoc.GetNodes().SectionDown( &aTmpRg,
+ pCurrentNode->m_pStartOfSection->GetStartNodeType() );
+ }
+ break;
+
+ case SwNodeType::Text:
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ {
+ static_cast<SwContentNode*>(pCurrentNode)->MakeCopy(
+ rDoc, aInsPos, bNewFrames);
+ }
+ break;
+
+ case SwNodeType::PlaceHolder:
+ if (GetDoc().GetIDocumentUndoRedo().IsUndoNodes(*this))
+ {
+ // than a SectionNode (start/end) is needed at the current
+ // InsPos; if so skip it, otherwise ignore current node
+ SwNode *const pTmpNd = & aInsPos.GetNode();
+ if( pTmpNd->IsSectionNode() ||
+ pTmpNd->StartOfSectionNode()->IsSectionNode() )
+ ++aInsPos; // skip
+ }
+ else {
+ assert(!"How can this node be in the node array?");
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ ++aRg.aStart;
+ }
+}
+
+void SwNodes::DelDummyNodes( const SwNodeRange& rRg )
+{
+ SwNodeIndex aIdx( rRg.aStart );
+ while( aIdx.GetIndex() < rRg.aEnd.GetIndex() )
+ {
+ if (SwNodeType::PlaceHolder == aIdx.GetNode().GetNodeType())
+ RemoveNode( aIdx.GetIndex(), SwNodeOffset(1), true );
+ else
+ ++aIdx;
+ }
+}
+
+SwStartNode* SwNodes::MakeEmptySection( const SwNodeIndex& rIdx,
+ SwStartNodeType eSttNdTyp )
+{
+ SwStartNode* pSttNd = new SwStartNode( rIdx, SwNodeType::Start, eSttNdTyp );
+ new SwEndNode( rIdx, *pSttNd );
+ return pSttNd;
+}
+
+SwStartNode* SwNodes::MakeTextSection( const SwNodeIndex & rWhere,
+ SwStartNodeType eSttNdTyp,
+ SwTextFormatColl *pColl )
+{
+ SwStartNode* pSttNd = new SwStartNode( rWhere, SwNodeType::Start, eSttNdTyp );
+ new SwEndNode( rWhere, *pSttNd );
+ MakeTextNode( SwNodeIndex( rWhere, - 1 ), pColl );
+ return pSttNd;
+}
+
+//TODO: provide better documentation
+/** go to next section that is not protected nor hidden
+ *
+ * @note if !bSkipHidden and !bSkipProtect, use GoNext/GoPrevious
+ *
+ * @param pIdx
+ * @param bSkipHidden
+ * @param bSkipProtect
+ * @return
+ * @see SwNodes::GoNext
+ * @see SwNodes::GoPrevious
+ * @see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
+*/
+SwContentNode* SwNodes::GoNextSection( SwNodeIndex * pIdx,
+ bool bSkipHidden, bool bSkipProtect ) const
+{
+ bool bFirst = true;
+ SwNodeIndex aTmp( *pIdx );
+ const SwNode* pNd;
+ while( aTmp < Count() - 1 )
+ {
+ pNd = & aTmp.GetNode();
+ if (SwNodeType::Section == pNd->GetNodeType())
+ {
+ const SwSection& rSect = static_cast<const SwSectionNode*>(pNd)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip the section
+ aTmp = *pNd->EndOfSectionNode();
+ }
+ else if( bFirst )
+ {
+ if( pNd->m_pStartOfSection->IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
+ m_pStartOfSection)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip the section
+ aTmp = *pNd->EndOfSectionNode();
+ }
+ }
+ else if( SwNodeType::ContentMask & pNd->GetNodeType() )
+ {
+ const SwSectionNode* pSectNd;
+ if( ( bSkipHidden || bSkipProtect ) &&
+ nullptr != (pSectNd = pNd->FindSectionNode() ) &&
+ ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) ||
+ ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) )
+ {
+ aTmp = *pSectNd->EndOfSectionNode();
+ }
+ else
+ {
+ (*pIdx) = aTmp;
+ return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
+ }
+ }
+ ++aTmp;
+ bFirst = false;
+ }
+ return nullptr;
+}
+
+///@see SwNodes::GoNextSection (TODO: seems to be C&P programming here)
+SwContentNode* SwNodes::GoPrevSection( SwNodeIndex * pIdx,
+ bool bSkipHidden, bool bSkipProtect )
+{
+ bool bFirst = true;
+ SwNodeIndex aTmp( *pIdx );
+ const SwNode* pNd;
+ while( aTmp > SwNodeOffset(0) )
+ {
+ pNd = & aTmp.GetNode();
+ if (SwNodeType::End == pNd->GetNodeType())
+ {
+ if( pNd->m_pStartOfSection->IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
+ m_pStartOfSection)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip section
+ aTmp = *pNd->StartOfSectionNode();
+ }
+ bFirst = false;
+ }
+ else if( bFirst )
+ {
+ bFirst = false;
+ if( pNd->m_pStartOfSection->IsSectionNode() )
+ {
+ const SwSection& rSect = static_cast<SwSectionNode*>(pNd->
+ m_pStartOfSection)->GetSection();
+ if( (bSkipHidden && rSect.IsHiddenFlag()) ||
+ (bSkipProtect && rSect.IsProtectFlag()) )
+ // than skip section
+ aTmp = *pNd->StartOfSectionNode();
+ }
+ }
+ else if( SwNodeType::ContentMask & pNd->GetNodeType() )
+ {
+ const SwSectionNode* pSectNd;
+ if( ( bSkipHidden || bSkipProtect ) &&
+ nullptr != (pSectNd = pNd->FindSectionNode() ) &&
+ ( ( bSkipHidden && pSectNd->GetSection().IsHiddenFlag() ) ||
+ ( bSkipProtect && pSectNd->GetSection().IsProtectFlag() )) )
+ {
+ aTmp = *pSectNd;
+ }
+ else
+ {
+ (*pIdx) = aTmp;
+ return const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd));
+ }
+ }
+ --aTmp;
+ }
+ return nullptr;
+}
+
+//TODO: The inventor of the "single responsibility principle" will be crying if you ever show this code to him!
+/** find the next/previous ContentNode or table node that should have layout
+ * frames that are siblings to the ones of the node at rFrameIdx.
+ *
+ * Search is started backward with the one before rFrameIdx and
+ * forward after pEnd.
+ *
+ * @param rFrameIdx in: node with frames to search in; out: found node
+ * @param pEnd last node after rFrameIdx that should be excluded from search
+ * @return result node; 0 if not found
+ */
+SwNode* SwNodes::FindPrvNxtFrameNode( SwNodeIndex& rFrameIdx,
+ SwNode const*const pEnd,
+ SwRootFrame const*const pLayout) const
+{
+ assert(pEnd != nullptr); // every caller currently
+
+ SwNode* pFrameNd = nullptr;
+
+ // no layout -> skip
+ if( GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ SwNode *const pSttNd = &rFrameIdx.GetNode();
+
+ // inside a hidden section?
+ SwSectionNode *const pSectNd = pSttNd->IsSectionNode()
+ ? pSttNd->StartOfSectionNode()->FindSectionNode()
+ : pSttNd->FindSectionNode();
+ if( !( pSectNd && pSectNd->GetSection().CalcHiddenFlag() ) )
+ {
+ // in a table in table situation we have to assure that we don't leave the
+ // outer table cell when the inner table is looking for a PrvNxt...
+ SwTableNode *const pTableNd = pSttNd->IsTableNode()
+ ? pSttNd->StartOfSectionNode()->FindTableNode()
+ : pSttNd->FindTableNode();
+ SwNodeIndex aIdx( rFrameIdx );
+
+ // search backward for a content or table node
+
+ --aIdx;
+ pFrameNd = &aIdx.GetNode();
+
+ do
+ {
+ if (pFrameNd->IsContentNode())
+ {
+ // TODO why does this not check for nested tables like forward direction
+ rFrameIdx = aIdx;
+ return pFrameNd;
+ }
+ else if (pFrameNd->IsEndNode() && pFrameNd->StartOfSectionNode()->IsTableNode())
+ {
+ if (pLayout == nullptr
+ || !pLayout->HasMergedParas()
+ || pFrameNd->StartOfSectionNode()->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ {
+ pFrameNd = pFrameNd->StartOfSectionNode();
+ rFrameIdx = *pFrameNd;
+ return pFrameNd;
+ }
+ else
+ {
+ aIdx = *pFrameNd->StartOfSectionNode();
+ --aIdx;
+ pFrameNd = &aIdx.GetNode();
+ }
+ }
+ else
+ {
+ pFrameNd = GoPrevSection( &aIdx, true, false );
+ if ( nullptr != pFrameNd && !(
+ ::CheckNodesRange( aIdx, rFrameIdx, true ) &&
+ // Never out of the table at the start
+ pFrameNd->FindTableNode() == pTableNd &&
+ // Bug 37652: Never out of the table at the end
+ (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
+ == pSttNd->FindTableBoxStartNode() ) &&
+ (!pSectNd || pSttNd->IsSectionNode() ||
+ pSectNd->GetIndex() < pFrameNd->GetIndex())
+ ))
+ {
+ pFrameNd = nullptr; // no preceding content node, stop search
+ }
+ }
+ }
+ while (pFrameNd != nullptr);
+
+ // search forward for a content or table node
+
+ aIdx = pEnd->GetIndex() + 1;
+ pFrameNd = &aIdx.GetNode();
+
+ do
+ {
+ if (pFrameNd->IsContentNode())
+ {
+ // Undo when merging a table with one before, if there is also one after it.
+ // However, if the node is in a table, it needs to be returned if the
+ // SttNode is a section or a table!
+ SwTableNode *const pTableNode = pFrameNd->FindTableNode();
+ if (pSttNd->IsTableNode() &&
+ nullptr != pTableNode &&
+ // TABLE IN TABLE:
+ pTableNode != pSttNd->StartOfSectionNode()->FindTableNode())
+ {
+ pFrameNd = pTableNode;
+ rFrameIdx = *pFrameNd;
+ }
+ else
+ {
+ rFrameIdx = aIdx;
+ }
+ return pFrameNd;
+ }
+ else if (pFrameNd->IsTableNode())
+ {
+ if (pLayout == nullptr
+ || !pLayout->HasMergedParas()
+ || pFrameNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ {
+ rFrameIdx = *pFrameNd;
+ return pFrameNd;
+ }
+ else
+ {
+ aIdx = *pFrameNd->EndOfSectionNode();
+ ++aIdx;
+ pFrameNd = &aIdx.GetNode();
+ }
+ }
+ else
+ {
+ pFrameNd = GoNextSection( &aIdx, true, false );
+ // NEVER leave the section when doing this!
+ if (pFrameNd
+ && !(::CheckNodesRange(aIdx, rFrameIdx, true)
+ && (pFrameNd->FindTableNode() == pTableNd &&
+ // NEVER go out of the table cell at the end
+ (!pFrameNd->FindTableNode() || pFrameNd->FindTableBoxStartNode()
+ == pSttNd->FindTableBoxStartNode()))
+ && (!pSectNd || pSttNd->IsSectionNode() ||
+ pSectNd->EndOfSectionIndex() > pFrameNd->GetIndex()))
+ )
+ {
+ pFrameNd = nullptr; // no following content node, stop search
+ }
+ }
+ }
+ while (pFrameNd != nullptr);
+
+ // probably this is dead code, because the GoNextSection()
+ // should have ended up in the first text node in the table and
+ // then checked it's in a table?
+ {
+ aIdx = pEnd->GetIndex() + 1;
+
+ pFrameNd = &aIdx.GetNode();
+ {
+ pFrameNd = nullptr;
+
+ // is there some sectionnodes before a tablenode?
+ while( aIdx.GetNode().IsSectionNode() )
+ {
+ const SwSection& rSect = aIdx.GetNode().
+ GetSectionNode()->GetSection();
+ if( rSect.IsHiddenFlag() )
+ aIdx = aIdx.GetNode().EndOfSectionIndex()+1;
+ else
+ ++aIdx;
+ }
+ if( aIdx.GetNode().IsTableNode() )
+ {
+ rFrameIdx = aIdx;
+ pFrameNd = &aIdx.GetNode();
+ assert(!"this isn't dead code?");
+ }
+ }
+ }
+ }
+ }
+ return pFrameNd;
+}
+
+void SwNodes::ForEach( SwNodeOffset nStart, SwNodeOffset nEnd,
+ FnForEach_SwNodes fn, void* pArgs )
+{
+ if( nEnd > SwNodeOffset(m_nSize) )
+ nEnd = SwNodeOffset(m_nSize);
+
+ if( nStart >= nEnd )
+ return;
+
+ sal_uInt16 cur = Index2Block( sal_Int32(nStart) );
+ BlockInfo** pp = m_ppInf.get() + cur;
+ BlockInfo* p = *pp;
+ sal_uInt16 nElem = sal_uInt16( sal_Int32(nStart) - p->nStart );
+ auto pElem = p->mvData.begin() + nElem;
+ nElem = p->nElem - nElem;
+ for(;;)
+ {
+ if( !(*fn)( static_cast<SwNode *>(*pElem++), pArgs ) || ++nStart >= nEnd )
+ break;
+
+ // next element
+ if( !--nElem )
+ {
+ // new block
+ p = *++pp;
+ pElem = p->mvData.begin();
+ nElem = p->nElem;
+ }
+ }
+}
+
+void SwNodes::ForEach( const SwNodeIndex& rStart, const SwNodeIndex& rEnd,
+ FnForEach_SwNodes fnForEach, void* pArgs )
+{
+ ForEach( rStart.GetIndex(), rEnd.GetIndex(), fnForEach, pArgs );
+}
+
+void SwNodes::RemoveNode( SwNodeOffset nDelPos, SwNodeOffset nSz, bool bDel )
+{
+#ifndef NDEBUG
+ SwNode *const pFirst((*this)[nDelPos]);
+#endif
+ for (SwNodeOffset nCnt(0); nCnt < nSz; nCnt++)
+ {
+ SwNode* pNode = (*this)[ nDelPos + nCnt ];
+ SwTextNode * pTextNd = pNode->GetTextNode();
+
+ if (pTextNd)
+ {
+ pTextNd->RemoveFromList();
+ // remove RndStdIds::FLY_AS_CHAR *before* adjusting SwNodeIndex
+ // so their anchor still points to correct node when deleted!
+ // NOTE: this will call RemoveNode() recursively!
+ // so adjust our indexes to account for removed nodes
+ SwNodeOffset const nPos = pTextNd->GetIndex();
+ SwpHints *const pHints(pTextNd->GetpSwpHints());
+ if (pHints)
+ {
+ std::vector<SwTextAttr*> flys;
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ SwTextAttr *const pHint(pHints->Get(i));
+ if (RES_TXTATR_FLYCNT == pHint->Which())
+ {
+ flys.push_back(pHint);
+ }
+ }
+ for (SwTextAttr * pHint : flys)
+ {
+ pTextNd->DeleteAttribute(pHint);
+ } // pHints may be dead now
+ SwNodeOffset const nDiff = nPos - pTextNd->GetIndex();
+ if (nDiff)
+ {
+ nDelPos -= nDiff;
+ }
+ assert(pTextNd == (*this)[nDelPos + nCnt]);
+ assert(pFirst == (*this)[nDelPos]);
+ }
+ }
+ SwTableNode* pTableNode = pNode->GetTableNode();
+ if (pTableNode)
+ {
+ // The node that is deleted is a table node.
+ // Need to make sure that all the redlines that are
+ // related to this table are removed from the
+ // 'Extra Redlines' array
+ pTableNode->RemoveRedlines();
+ }
+ }
+
+ SwNodeOffset nEnd = nDelPos + nSz;
+ SwNode* pNew = (*this)[ nEnd ];
+
+ for (SwNodeIndex& rIndex : m_vIndices->GetRingContainer())
+ {
+ SwNodeOffset const nIdx = rIndex.GetIndex();
+ if (nDelPos <= nIdx && nIdx < nEnd)
+ rIndex = *pNew;
+ }
+
+ std::vector<BigPtrEntry> aTempEntries;
+ if( bDel )
+ {
+ SwNodeOffset nCnt = nSz;
+ BigPtrEntry *pDel = (*this)[ nDelPos+nCnt-1 ], *pPrev = (*this)[ nDelPos+nCnt-2 ];
+
+ // set temporary object
+ // JP 24.08.98: this should actually be removed because one could
+ // call Remove recursively, e.g. for character bound frames. However,
+ // since there happens way too much here, this temporary object was
+ // inserted that will be deleted in Remove again (see Bug 55406)
+ aTempEntries.resize(sal_Int32(nCnt));
+
+ while( nCnt-- )
+ {
+ delete pDel;
+ // coverity[use_after_free : FALSE] - pPrev will be reassigned if there will be another iteration to the loop
+ pDel = pPrev;
+ sal_uLong nPrevNdIdx = pPrev->GetPos();
+ BigPtrEntry* pTempEntry = &aTempEntries[sal_Int32(nCnt)];
+ BigPtrArray::Replace( nPrevNdIdx+1, pTempEntry );
+ if( nCnt )
+ pPrev = BigPtrArray::operator []( nPrevNdIdx - 1 );
+ // the accessed element can be a naked BigPtrEntry from
+ // aTempEntries, so the downcast to SwNode* in
+ // SwNodes::operator[] would be illegal (and unnecessary)
+ }
+ nDelPos = SwNodeOffset(pDel->GetPos() + 1);
+ }
+
+ BigPtrArray::Remove( sal_Int32(nDelPos), sal_Int32(nSz) );
+}
+
+void SwNodes::InsertNode( SwNode* pNode, const SwNodeIndex& rPos )
+{
+ BigPtrEntry* pIns = pNode;
+ BigPtrArray::Insert( pIns, sal_Int32(rPos.GetIndex()) );
+}
+
+void SwNodes::InsertNode( SwNode* pNode, SwNodeOffset nPos )
+{
+ BigPtrEntry* pIns = pNode;
+ BigPtrArray::Insert( pIns, sal_Int32(nPos) );
+}
+
+// ->#112139#
+SwNode * SwNodes::DocumentSectionStartNode(SwNode * pNode) const
+{
+ if (nullptr != pNode)
+ {
+ SwNodeIndex aIdx(*pNode);
+
+ if (aIdx <= (*this)[SwNodeOffset(0)]->EndOfSectionIndex())
+ pNode = (*this)[SwNodeOffset(0)];
+ else
+ {
+ while ((*this)[SwNodeOffset(0)] != pNode->StartOfSectionNode())
+ pNode = pNode->StartOfSectionNode();
+ }
+ }
+
+ return pNode;
+}
+
+SwNode * SwNodes::DocumentSectionEndNode(SwNode * pNode) const
+{
+ return DocumentSectionStartNode(pNode)->EndOfSectionNode();
+}
+
+bool SwNodes::IsDocNodes() const
+{
+ return this == &m_rMyDoc.GetNodes();
+}
+
+void SwNodes::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNodes"));
+ for (SwNodeOffset i(0); i < Count(); ++i)
+ (*this)[i]->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/observablethread.cxx b/sw/source/core/docnode/observablethread.cxx
new file mode 100644
index 000000000..98eaabb29
--- /dev/null
+++ b/sw/source/core/docnode/observablethread.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <observablethread.hxx>
+#include <ifinishedthreadlistener.hxx>
+#include <memory>
+
+/* class for an observable thread
+
+ #i73788#
+*/
+ObservableThread::ObservableThread()
+ : mnThreadID( 0 )
+{
+}
+
+ObservableThread::~ObservableThread()
+{
+}
+
+void ObservableThread::SetListener( std::weak_ptr< IFinishedThreadListener > const & pThreadListener,
+ const oslInterlockedCount nThreadID )
+{
+ mpThreadListener = pThreadListener;
+ mnThreadID = nThreadID;
+}
+
+void SAL_CALL ObservableThread::run()
+{
+ acquire();
+
+ threadFunction();
+}
+
+void SAL_CALL ObservableThread::onTerminated()
+{
+ // notify observer
+ std::shared_ptr< IFinishedThreadListener > pThreadListener = mpThreadListener.lock();
+ if ( pThreadListener )
+ {
+ pThreadListener->NotifyAboutFinishedThread( mnThreadID );
+ }
+
+ release();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/pausethreadstarting.cxx b/sw/source/core/docnode/pausethreadstarting.cxx
new file mode 100644
index 000000000..953bd3a3f
--- /dev/null
+++ b/sw/source/core/docnode/pausethreadstarting.cxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <pausethreadstarting.hxx>
+#include <swthreadmanager.hxx>
+
+/* Helper class to pause starting of threads during existence of an instance
+ of this class
+
+ #i73788#
+*/
+
+SwPauseThreadStarting::SwPauseThreadStarting()
+ : mbPausedThreadStarting(false)
+{
+ if (SwThreadManager::ExistsThreadManager()
+ && !SwThreadManager::GetThreadManager().StartingOfThreadsSuspended())
+ {
+ SwThreadManager::GetThreadManager().SuspendStartingOfThreads();
+ mbPausedThreadStarting = true;
+ }
+}
+
+SwPauseThreadStarting::~SwPauseThreadStarting() COVERITY_NOEXCEPT_FALSE
+{
+ if (mbPausedThreadStarting)
+ {
+ SwThreadManager::GetThreadManager().ResumeStartingOfThreads();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/retrievedinputstreamdata.cxx b/sw/source/core/docnode/retrievedinputstreamdata.cxx
new file mode 100644
index 000000000..311be07ca
--- /dev/null
+++ b/sw/source/core/docnode/retrievedinputstreamdata.cxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <retrievedinputstreamdata.hxx>
+#include <retrieveinputstreamconsumer.hxx>
+#include <vcl/svapp.hxx>
+
+// #i73788#
+
+SwRetrievedInputStreamDataManager::tDataKey SwRetrievedInputStreamDataManager::snNextKeyValue = 1;
+
+SwRetrievedInputStreamDataManager& SwRetrievedInputStreamDataManager::GetManager()
+{
+ static SwRetrievedInputStreamDataManager theSwRetrievedInputStreamDataManager;
+ return theSwRetrievedInputStreamDataManager;
+}
+
+SwRetrievedInputStreamDataManager::tDataKey SwRetrievedInputStreamDataManager::ReserveData(
+ std::weak_ptr< SwAsyncRetrieveInputStreamThreadConsumer > const & pThreadConsumer )
+{
+ std::unique_lock aGuard(maMutex);
+
+ // create empty data container for given thread Consumer
+ tDataKey nDataKey( snNextKeyValue );
+ tData aNewEntry( pThreadConsumer );
+ maInputStreamData[ nDataKey ] = aNewEntry;
+
+ // prepare next data key value
+ if ( snNextKeyValue < SAL_MAX_UINT64 )
+ {
+ ++snNextKeyValue;
+ }
+ else
+ {
+ snNextKeyValue = 1;
+ }
+
+ return nDataKey;
+}
+
+void SwRetrievedInputStreamDataManager::PushData(
+ const tDataKey nDataKey,
+ css::uno::Reference<css::io::XInputStream> const & xInputStream,
+ const bool bIsStreamReadOnly )
+{
+ std::unique_lock aGuard(maMutex);
+
+ std::map< tDataKey, tData >::iterator aIter = maInputStreamData.find( nDataKey );
+
+ if ( aIter == maInputStreamData.end() )
+ return;
+
+ // Fill data container.
+ (*aIter).second.mxInputStream = xInputStream;
+ (*aIter).second.mbIsStreamReadOnly = bIsStreamReadOnly;
+
+ // post user event to process the retrieved input stream data
+ if ( GetpApp() )
+ {
+
+ tDataKey* pDataKey = new tDataKey;
+ *pDataKey = nDataKey;
+ Application::PostUserEvent( LINK( this, SwRetrievedInputStreamDataManager, LinkedInputStreamReady ), pDataKey );
+ }
+ else
+ {
+ // no application available -> discard data
+ maInputStreamData.erase( aIter );
+ }
+}
+
+bool SwRetrievedInputStreamDataManager::PopData( const tDataKey nDataKey,
+ tData& rData )
+{
+ std::unique_lock aGuard(maMutex);
+
+ bool bDataProvided( false );
+
+ std::map< tDataKey, tData >::iterator aIter = maInputStreamData.find( nDataKey );
+
+ if ( aIter != maInputStreamData.end() )
+ {
+ rData.mpThreadConsumer = (*aIter).second.mpThreadConsumer;
+ rData.mxInputStream = (*aIter).second.mxInputStream;
+ rData.mbIsStreamReadOnly = (*aIter).second.mbIsStreamReadOnly;
+
+ maInputStreamData.erase( aIter );
+
+ bDataProvided = true;
+ }
+
+ return bDataProvided;
+}
+
+/** callback function, which is triggered by input stream data manager on
+ filling of the data container to provide retrieved input stream to the
+ thread Consumer using <Application::PostUserEvent(..)>
+
+ #i73788#
+ Note: This method has to be run in the main thread.
+*/
+IMPL_STATIC_LINK( SwRetrievedInputStreamDataManager, LinkedInputStreamReady,
+ void*, p, void )
+{
+ SwRetrievedInputStreamDataManager::tDataKey* pDataKey = static_cast<SwRetrievedInputStreamDataManager::tDataKey*>(p);
+ if ( !pDataKey )
+ {
+ return;
+ }
+
+ SwRetrievedInputStreamDataManager& rDataManager =
+ SwRetrievedInputStreamDataManager::GetManager();
+ SwRetrievedInputStreamDataManager::tData aInputStreamData;
+ if ( rDataManager.PopData( *pDataKey, aInputStreamData ) )
+ {
+ std::shared_ptr< SwAsyncRetrieveInputStreamThreadConsumer > pThreadConsumer =
+ aInputStreamData.mpThreadConsumer.lock();
+ if ( pThreadConsumer )
+ {
+ pThreadConsumer->ApplyInputStream( aInputStreamData.mxInputStream,
+ aInputStreamData.mbIsStreamReadOnly );
+ }
+ }
+ delete pDataKey;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/retrieveinputstream.cxx b/sw/source/core/docnode/retrieveinputstream.cxx
new file mode 100644
index 000000000..8054e341b
--- /dev/null
+++ b/sw/source/core/docnode/retrieveinputstream.cxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <retrieveinputstream.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+/* class for a thread to retrieve an input stream given by a URL
+
+ #i73788#
+*/
+::rtl::Reference< ObservableThread > SwAsyncRetrieveInputStreamThread::createThread(
+ const SwRetrievedInputStreamDataManager::tDataKey nDataKey,
+ const OUString& rLinkedURL, const OUString& rReferer )
+{
+ SwAsyncRetrieveInputStreamThread* pNewThread =
+ new SwAsyncRetrieveInputStreamThread( nDataKey, rLinkedURL, rReferer );
+ return pNewThread;
+}
+
+SwAsyncRetrieveInputStreamThread::SwAsyncRetrieveInputStreamThread(
+ const SwRetrievedInputStreamDataManager::tDataKey nDataKey,
+ const OUString& rLinkedURL,
+ const OUString& rReferer )
+ : mnDataKey( nDataKey ),
+ mrLinkedURL( rLinkedURL ),
+ mrReferer( rReferer )
+{
+}
+
+SwAsyncRetrieveInputStreamThread::~SwAsyncRetrieveInputStreamThread()
+{
+}
+
+void SwAsyncRetrieveInputStreamThread::threadFunction()
+{
+ osl_setThreadName("SwAsyncRetrieveInputStreamThread");
+
+ css::uno::Sequence < css::beans::PropertyValue > xProps{
+ comphelper::makePropertyValue("URL", mrLinkedURL),
+ comphelper::makePropertyValue("Referer", mrReferer)
+ };
+ utl::MediaDescriptor aMedium( xProps );
+
+ aMedium.addInputStream();
+
+ css::uno::Reference<css::io::XInputStream> xInputStream;
+ aMedium[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ if ( !xInputStream.is() )
+ {
+ css::uno::Reference<css::io::XStream> xStream;
+ aMedium[utl::MediaDescriptor::PROP_STREAM] >>= xStream;
+ if ( xStream.is() )
+ {
+ xInputStream = xStream->getInputStream();
+ }
+ }
+
+ SwRetrievedInputStreamDataManager::GetManager().PushData( mnDataKey,
+ xInputStream,
+ aMedium.isStreamReadOnly() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/retrieveinputstreamconsumer.cxx b/sw/source/core/docnode/retrieveinputstreamconsumer.cxx
new file mode 100644
index 000000000..d89a05361
--- /dev/null
+++ b/sw/source/core/docnode/retrieveinputstreamconsumer.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <retrieveinputstreamconsumer.hxx>
+#include <ndgrf.hxx>
+#include <retrieveinputstream.hxx>
+#include <swthreadmanager.hxx>
+
+/* class to provide creation of a thread to retrieve an input stream given by
+ a URL and to consume the retrieved input stream.
+
+ #i73788#
+*/
+SwAsyncRetrieveInputStreamThreadConsumer::SwAsyncRetrieveInputStreamThreadConsumer(
+ SwGrfNode& rGrfNode )
+ : mrGrfNode( rGrfNode ),
+ mnThreadID( 0 )
+{
+}
+
+SwAsyncRetrieveInputStreamThreadConsumer::~SwAsyncRetrieveInputStreamThreadConsumer() COVERITY_NOEXCEPT_FALSE
+{
+ SwThreadManager::GetThreadManager().RemoveThread( mnThreadID );
+}
+
+void SwAsyncRetrieveInputStreamThreadConsumer::CreateThread( const OUString& rURL, const OUString& rReferer )
+{
+ // Get new data container for input stream data
+ SwRetrievedInputStreamDataManager::tDataKey nDataKey =
+ SwRetrievedInputStreamDataManager::GetManager().ReserveData(
+ mrGrfNode.GetThreadConsumer() );
+
+ rtl::Reference< ObservableThread > pNewThread =
+ SwAsyncRetrieveInputStreamThread::createThread( nDataKey, rURL, rReferer );
+
+ // Add thread to thread manager and pass ownership of thread to thread manager.
+ mnThreadID = SwThreadManager::GetThreadManager().AddThread( pNewThread );
+}
+
+void SwAsyncRetrieveInputStreamThreadConsumer::ApplyInputStream(
+ css::uno::Reference<css::io::XInputStream> const & xInputStream,
+ const bool bIsStreamReadOnly )
+{
+ mrGrfNode.ApplyInputStream( xInputStream, bIsStreamReadOnly );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/section.cxx b/sw/source/core/docnode/section.cxx
new file mode 100644
index 000000000..c35a614f8
--- /dev/null
+++ b/sw/source/core/docnode/section.cxx
@@ -0,0 +1,1512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlstring.h>
+#include <libxml/xmlwriter.h>
+#include <stdlib.h>
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <sot/exchange.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/docfile.hxx>
+#include <editeng/protitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <docary.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtpdsc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentLinksAdministrationManager.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <editsh.hxx>
+#include <hints.hxx>
+#include <docsh.hxx>
+#include <ndtxt.hxx>
+#include <section.hxx>
+#include <swserv.hxx>
+#include <shellio.hxx>
+#include <poolfmt.hxx>
+#include <swbaslnk.hxx>
+#include <mvsave.hxx>
+#include <ftnidx.hxx>
+#include <doctxm.hxx>
+#include <fmteiro.hxx>
+#include <unosection.hxx>
+#include <calbck.hxx>
+#include <fmtclds.hxx>
+#include <algorithm>
+#include "ndsect.hxx"
+
+using namespace ::com::sun::star;
+
+namespace {
+ class SwIntrnlSectRefLink : public SwBaseLink
+ {
+ SwSectionFormat& m_rSectFormat;
+
+ public:
+ SwIntrnlSectRefLink(SwSectionFormat& rFormat, SfxLinkUpdateMode nUpdateType)
+ : SwBaseLink(nUpdateType, SotClipboardFormatId::RTF)
+ , m_rSectFormat(rFormat)
+ {}
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ virtual const SwNode* GetAnchor() const override;
+ virtual bool IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const override;
+
+ SwSectionNode* GetSectNode()
+ {
+ const SwNode* pSectNd( GetAnchor() );
+ return const_cast<SwSectionNode*>( dynamic_cast<const SwSectionNode*>( pSectNd ) );
+ }
+ };
+}
+
+SwSectionData::SwSectionData(SectionType const eType, OUString const& rName)
+ : m_eType(eType)
+ , m_sSectionName(rName)
+ , m_bHiddenFlag(false)
+ , m_bProtectFlag(false)
+ , m_bEditInReadonlyFlag(false) // edit in readonly sections
+ , m_bHidden(false)
+ , m_bCondHiddenFlag(true)
+ , m_bConnectFlag(true)
+{
+}
+
+// this must have the same semantics as operator=()
+SwSectionData::SwSectionData(SwSection const& rSection)
+ : m_eType(rSection.GetType())
+ , m_sSectionName(rSection.GetSectionName())
+ , m_sCondition(rSection.GetCondition())
+ , m_sLinkFileName(rSection.GetLinkFileName())
+ , m_sLinkFilePassword(rSection.GetLinkFilePassword())
+ , m_Password(rSection.GetPassword())
+ , m_bHiddenFlag(rSection.IsHiddenFlag())
+ , m_bProtectFlag(rSection.IsProtect())
+ // edit in readonly sections
+ , m_bEditInReadonlyFlag(rSection.IsEditInReadonly())
+ , m_bHidden(rSection.IsHidden())
+ , m_bCondHiddenFlag(true)
+ , m_bConnectFlag(rSection.IsConnectFlag())
+{
+}
+
+// this must have the same semantics as operator=()
+SwSectionData::SwSectionData(SwSectionData const& rOther)
+ : m_eType(rOther.m_eType)
+ , m_sSectionName(rOther.m_sSectionName)
+ , m_sCondition(rOther.m_sCondition)
+ , m_sLinkFileName(rOther.m_sLinkFileName)
+ , m_sLinkFilePassword(rOther.m_sLinkFilePassword)
+ , m_Password(rOther.m_Password)
+ , m_bHiddenFlag(rOther.m_bHiddenFlag)
+ , m_bProtectFlag(rOther.m_bProtectFlag)
+ // edit in readonly sections
+ , m_bEditInReadonlyFlag(rOther.m_bEditInReadonlyFlag)
+ , m_bHidden(rOther.m_bHidden)
+ , m_bCondHiddenFlag(true)
+ , m_bConnectFlag(rOther.m_bConnectFlag)
+{
+}
+
+// the semantics here are weird for reasons of backward compatibility
+SwSectionData & SwSectionData::operator= (SwSectionData const& rOther)
+{
+ m_eType = rOther.m_eType;
+ m_sSectionName = rOther.m_sSectionName;
+ m_sCondition = rOther.m_sCondition;
+ m_sLinkFileName = rOther.m_sLinkFileName;
+ m_sLinkFilePassword = rOther.m_sLinkFilePassword;
+ m_bConnectFlag = rOther.m_bConnectFlag;
+ m_Password = rOther.m_Password;
+
+ m_bEditInReadonlyFlag = rOther.m_bEditInReadonlyFlag;
+ m_bProtectFlag = rOther.m_bProtectFlag;
+
+ m_bHidden = rOther.m_bHidden;
+ // FIXME: old code did not assign m_bHiddenFlag ?
+ // FIXME: why should m_bCondHiddenFlag always default to true?
+ m_bCondHiddenFlag = true;
+
+ return *this;
+}
+
+// the semantics here are weird for reasons of backward compatibility
+bool SwSectionData::operator==(SwSectionData const& rOther) const
+{
+ return (m_eType == rOther.m_eType)
+ && (m_sSectionName == rOther.m_sSectionName)
+ && (m_sCondition == rOther.m_sCondition)
+ && (m_bHidden == rOther.m_bHidden)
+ && (m_bProtectFlag == rOther.m_bProtectFlag)
+ && (m_bEditInReadonlyFlag == rOther.m_bEditInReadonlyFlag)
+ && (m_sLinkFileName == rOther.m_sLinkFileName)
+ && (m_sLinkFilePassword == rOther.m_sLinkFilePassword)
+ && (m_Password == rOther.m_Password);
+ // FIXME: old code ignored m_bCondHiddenFlag m_bHiddenFlag m_bConnectFlag
+}
+
+SwSection::SwSection(
+ SectionType const eType, OUString const& rName, SwSectionFormat & rFormat)
+ : SwClient(& rFormat)
+ , m_Data(eType, rName)
+{
+ StartListening(rFormat.GetNotifier());
+
+ SwSection *const pParentSect = GetParent();
+ if( pParentSect )
+ {
+ // edit in readonly sections
+ m_Data.SetEditInReadonlyFlag( pParentSect->IsEditInReadonlyFlag() );
+ }
+
+ m_Data.SetProtectFlag( rFormat.GetProtect().IsContentProtected() );
+
+ if (!m_Data.IsEditInReadonlyFlag()) // edit in readonly sections
+ {
+ m_Data.SetEditInReadonlyFlag( rFormat.GetEditInReadonly().GetValue() );
+ }
+}
+
+SwSection::~SwSection()
+{
+ SwSectionFormat* pFormat = GetFormat();
+ if( !pFormat )
+ return;
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ if( pDoc->IsInDtor() )
+ {
+ // We reattach our Format to the default FrameFormat
+ // to not get any dependencies
+ if( pFormat->DerivedFrom() != pDoc->GetDfltFrameFormat() )
+ pFormat->RegisterToFormat( *pDoc->GetDfltFrameFormat() );
+ }
+ else
+ {
+ pFormat->Remove( this ); // remove
+ SvtListener::EndListeningAll();
+
+ if (SectionType::Content != m_Data.GetType())
+ {
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+
+ if (m_RefObj.is())
+ {
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_RefObj.get() );
+ }
+
+ // If the Section is the last Client in the Format we can delete it
+ pFormat->RemoveAllUnos();
+ if( !pFormat->HasWriterListeners() )
+ {
+ // Do not add to the Undo. This should've happened earlier.
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ pDoc->DelSectionFormat( pFormat );
+ }
+ }
+ if (m_RefObj.is())
+ {
+ m_RefObj->Closed();
+ }
+}
+
+void SwSection::SetSectionData(SwSectionData const& rData)
+{
+ bool const bOldHidden( m_Data.IsHidden() );
+ m_Data = rData;
+ // The next two may actually overwrite the m_Data.m_bProtect or EditInReadonly Flag
+ // in Modify, which should result in same flag value as the old code!
+ SetProtect(m_Data.IsProtectFlag());
+ SetEditInReadonly(m_Data.IsEditInReadonlyFlag());
+ if (bOldHidden != m_Data.IsHidden()) // check if changed...
+ {
+ ImplSetHiddenFlag(m_Data.IsHidden(), m_Data.IsCondHidden());
+ }
+}
+
+bool SwSection::DataEquals(SwSectionData const& rCmp) const
+{
+ // note that the old code compared the flags of the parameter with the
+ // format attributes of this; the following mess should do the same...
+ (void) GetLinkFileName(); // updates m_sLinkFileName
+ bool const bProtect(m_Data.IsProtectFlag());
+ bool const bEditInReadonly(m_Data.IsEditInReadonlyFlag());
+ m_Data.SetProtectFlag(IsProtect());
+ m_Data.SetEditInReadonlyFlag(IsEditInReadonly());
+ bool const bResult( m_Data == rCmp );
+ m_Data.SetProtectFlag(bProtect);
+ m_Data.SetEditInReadonlyFlag(bEditInReadonly);
+ return bResult;
+}
+
+void SwSection::ImplSetHiddenFlag(bool const bTmpHidden, bool const bCondition)
+{
+ SwSectionFormat* pFormat = GetFormat();
+ OSL_ENSURE(pFormat, "ImplSetHiddenFlag: no format?");
+ if( !pFormat )
+ return;
+
+ const bool bHide = bTmpHidden && bCondition;
+
+ if (bHide) // should be hidden
+ {
+ if (!m_Data.IsHiddenFlag()) // is not hidden
+ {
+ // Is the Parent hidden?
+ // This should be shown by the bHiddenFlag.
+
+ // Tell all Children that they are hidden
+ const SwMsgPoolItem aMsgItem( RES_SECTION_HIDDEN );
+ pFormat->CallSwClientNotify(sw::LegacyModifyHint(&aMsgItem, &aMsgItem));
+
+ // Delete all Frames
+ pFormat->DelFrames();
+ }
+ }
+ else if (m_Data.IsHiddenFlag()) // show Nodes again
+ {
+ // Show all Frames (Child Sections are accounted for by MakeFrames)
+ // Only if the Parent Section is not restricting us!
+ SwSection* pParentSect = pFormat->GetParentSection();
+ if( !pParentSect || !pParentSect->IsHiddenFlag() )
+ {
+ // Tell all Children that the Parent is not hidden anymore
+ const SwMsgPoolItem aMsgItem( RES_SECTION_NOT_HIDDEN );
+ pFormat->CallSwClientNotify(sw::LegacyModifyHint(&aMsgItem, &aMsgItem));
+
+ pFormat->MakeFrames();
+ }
+ }
+}
+
+bool SwSection::CalcHiddenFlag() const
+{
+ const SwSection* pSect = this;
+ do {
+ if( pSect->IsHidden() && pSect->IsCondHidden() )
+ return true;
+ } while( nullptr != ( pSect = pSect->GetParent()) );
+
+ return false;
+}
+
+bool SwSection::IsProtect() const
+{
+ SwSectionFormat const *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::IsProtect: no format?");
+ return pFormat
+ ? pFormat->GetProtect().IsContentProtected()
+ : IsProtectFlag();
+}
+
+// edit in readonly sections
+bool SwSection::IsEditInReadonly() const
+{
+ SwSectionFormat const *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::IsEditInReadonly: no format?");
+ return pFormat
+ ? pFormat->GetEditInReadonly().GetValue()
+ : IsEditInReadonlyFlag();
+}
+
+void SwSection::SetHidden(bool const bFlag)
+{
+ if (!m_Data.IsHidden() == !bFlag)
+ return;
+
+ m_Data.SetHidden(bFlag);
+ ImplSetHiddenFlag(bFlag, m_Data.IsCondHidden());
+}
+
+void SwSection::SetProtect(bool const bFlag)
+{
+ SwSectionFormat *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::SetProtect: no format?");
+ if (pFormat)
+ {
+ SvxProtectItem aItem( RES_PROTECT );
+ aItem.SetContentProtect( bFlag );
+ pFormat->SetFormatAttr( aItem );
+ // note: this will call m_Data.SetProtectFlag via Modify!
+ }
+ else
+ {
+ m_Data.SetProtectFlag(bFlag);
+ }
+}
+
+// edit in readonly sections
+void SwSection::SetEditInReadonly(bool const bFlag)
+{
+ SwSectionFormat *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::SetEditInReadonly: no format?");
+ if (pFormat)
+ {
+ SwFormatEditInReadonly aItem;
+ aItem.SetValue( bFlag );
+ pFormat->SetFormatAttr( aItem );
+ // note: this will call m_Data.SetEditInReadonlyFlag via Modify!
+ }
+ else
+ {
+ m_Data.SetEditInReadonlyFlag(bFlag);
+ }
+}
+
+void SwSection::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ Notify(rHint);
+}
+
+void SwSection::Notify(SfxHint const& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ auto pOld = pLegacy->m_pOld;
+ auto pNew = pLegacy->m_pNew;
+ bool bUpdateFootnote = false;
+ switch(pLegacy->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ if (pNew && pOld)
+ {
+ SfxItemSet* pNewSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pNew))->GetChgSet();
+ SfxItemSet* pOldSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pOld))->GetChgSet();
+
+ if( const SvxProtectItem* pItem = pNewSet->GetItemIfSet(
+ RES_PROTECT, false ) )
+ {
+ m_Data.SetProtectFlag( pItem->IsContentProtected() );
+ pNewSet->ClearItem( RES_PROTECT );
+ pOldSet->ClearItem( RES_PROTECT );
+ }
+
+ // --> edit in readonly sections
+ if( const SwFormatEditInReadonly* pItem = pNewSet->GetItemIfSet(
+ RES_EDIT_IN_READONLY, false ) )
+ {
+ m_Data.SetEditInReadonlyFlag(pItem->GetValue());
+ pNewSet->ClearItem( RES_EDIT_IN_READONLY );
+ pOldSet->ClearItem( RES_EDIT_IN_READONLY );
+ }
+
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_FTN_AT_TXTEND, false ) ||
+ SfxItemState::SET == pNewSet->GetItemState(
+ RES_END_AT_TXTEND, false ))
+ {
+ bUpdateFootnote = true;
+ }
+
+ if( !pNewSet->Count() )
+ return;
+ }
+ break;
+
+ case RES_PROTECT:
+ if( pNew )
+ {
+ bool bNewFlag =
+ static_cast<const SvxProtectItem*>(pNew)->IsContentProtected();
+ // this used to inherit the flag from the parent, but then there is
+ // no way to turn it off in an inner section
+ m_Data.SetProtectFlag( bNewFlag );
+ }
+ return;
+ // edit in readonly sections
+ case RES_EDIT_IN_READONLY:
+ if( pNew )
+ {
+ const bool bNewFlag =
+ static_cast<const SwFormatEditInReadonly*>(pNew)->GetValue();
+ m_Data.SetEditInReadonlyFlag( bNewFlag );
+ }
+ return;
+
+ case RES_SECTION_HIDDEN:
+ m_Data.SetHiddenFlag(true);
+ return;
+
+ case RES_SECTION_NOT_HIDDEN:
+ m_Data.SetHiddenFlag( m_Data.IsHidden() && m_Data.IsCondHidden() );
+ return;
+
+ case RES_COL:
+ // Is handled by the Layout, if appropriate
+ break;
+
+ case RES_FTN_AT_TXTEND:
+ if( pNew && pOld )
+ {
+ bUpdateFootnote = true;
+ }
+ break;
+
+ case RES_END_AT_TXTEND:
+ if( pNew && pOld )
+ {
+ bUpdateFootnote = true;
+ }
+ break;
+
+ default:
+ CheckRegistration( pOld );
+ break;
+ }
+
+ if( bUpdateFootnote )
+ {
+ SwSectionNode* pSectNd = GetFormat()->GetSectionNode();
+ if( pSectNd )
+ pSectNd->GetDoc().GetFootnoteIdxs().UpdateFootnote(SwNodeIndex( *pSectNd ));
+ }
+}
+
+void SwSection::SetRefObject( SwServerObject* pObj )
+{
+ m_RefObj = pObj;
+}
+
+void SwSection::SetCondHidden(bool const bFlag)
+{
+ if (!m_Data.IsCondHidden() == !bFlag)
+ return;
+
+ m_Data.SetCondHidden(bFlag);
+ ImplSetHiddenFlag(m_Data.IsHidden(), bFlag);
+}
+
+// Set/remove the linked FileName
+OUString const & SwSection::GetLinkFileName() const
+{
+ if (m_RefLink.is())
+ {
+ OUString sTmp;
+ switch (m_Data.GetType())
+ {
+ case SectionType::DdeLink:
+ sTmp = m_RefLink->GetLinkSourceName();
+ break;
+
+ case SectionType::FileLink:
+ {
+ OUString sRange;
+ OUString sFilter;
+ if (m_RefLink->GetLinkManager() &&
+ sfx2::LinkManager::GetDisplayNames(
+ m_RefLink.get(), nullptr, &sTmp, &sRange, &sFilter ))
+ {
+ sTmp += OUStringChar(sfx2::cTokenSeparator) + sFilter
+ + OUStringChar(sfx2::cTokenSeparator) + sRange;
+ }
+ else if( GetFormat() && !GetFormat()->GetSectionNode() )
+ {
+ // If the Section is in the UndoNodesArray, the LinkManager
+ // does not contain the Link, thus it cannot be queried for it.
+ // Thus return the current Name.
+ return m_Data.GetLinkFileName();
+ }
+ }
+ break;
+ default: break;
+ }
+ m_Data.SetLinkFileName(sTmp);
+ }
+ return m_Data.GetLinkFileName();
+}
+
+void SwSection::SetLinkFileName(const OUString& rNew)
+{
+ if (m_RefLink.is())
+ {
+ m_RefLink->SetLinkSourceName( rNew );
+ }
+ m_Data.SetLinkFileName(rNew);
+}
+
+// If it was a Linked Section, we need to make all Child Links visible
+void SwSection::MakeChildLinksVisible( const SwSectionNode& rSectNd )
+{
+ const SwNode* pNd;
+ const ::sfx2::SvBaseLinks& rLnks = rSectNd.GetDoc().getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+ for( auto n = rLnks.size(); n; )
+ {
+ sfx2::SvBaseLink& rBLnk = *rLnks[--n];
+ if (!rBLnk.IsVisible() && dynamic_cast<const SwBaseLink*>(&rBLnk) != nullptr
+ && nullptr != (pNd = static_cast<SwBaseLink&>(rBLnk).GetAnchor()))
+ {
+ pNd = pNd->StartOfSectionNode(); // If it's a SectionNode
+ const SwSectionNode* pParent;
+ while( nullptr != ( pParent = pNd->FindSectionNode() ) &&
+ ( SectionType::Content == pParent->GetSection().GetType()
+ || pNd == &rSectNd ))
+ pNd = pParent->StartOfSectionNode();
+
+ // It's within a normal Section, so show again
+ if( !pParent )
+ rBLnk.SetVisible(true);
+ }
+ }
+}
+
+const SwTOXBase* SwSection::GetTOXBase() const
+{
+ const SwTOXBase* pRet = nullptr;
+ if( SectionType::ToxContent == GetType() )
+ pRet = dynamic_cast<const SwTOXBaseSection*>(this);
+ return pRet;
+}
+
+SwSectionFormat::SwSectionFormat( SwFrameFormat* pDrvdFrame, SwDoc *pDoc )
+ : SwFrameFormat( pDoc->GetAttrPool(), OUString(), pDrvdFrame )
+{
+ LockModify();
+ SetFormatAttr( *GetDfltAttr( RES_COL ) );
+ UnlockModify();
+}
+
+SwSectionFormat::~SwSectionFormat()
+{
+ if( GetDoc()->IsInDtor() )
+ return;
+
+ SwSectionNode* pSectNd;
+ const SwNodeIndex* pIdx = GetContent( false ).GetContentIdx();
+ if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwSection& rSect = pSectNd->GetSection();
+ // If it was a linked Section, we need to make all Child Links
+ // visible again
+ if( rSect.IsConnected() )
+ SwSection::MakeChildLinksVisible( *pSectNd );
+
+ // Check whether we need to be visible, before deleting the Nodes
+ if( rSect.IsHiddenFlag() )
+ {
+ SwSection* pParentSect = rSect.GetParent();
+ if( !pParentSect || !pParentSect->IsHiddenFlag() )
+ {
+ // Make Nodes visible again
+ rSect.SetHidden(false);
+ }
+ }
+ // mba: test iteration; objects are removed while iterating
+ // use hint which allows to specify, if the content shall be saved or not
+ CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( true ) );
+
+ // Raise the Section up
+ SwNodeRange aRg( *pSectNd, SwNodeOffset(0), *pSectNd->EndOfSectionNode() );
+ GetDoc()->GetNodes().SectionUp( &aRg );
+ }
+ LockModify();
+ ResetFormatAttr( RES_CNTNT );
+ UnlockModify();
+}
+
+SwSection * SwSectionFormat::GetSection() const
+{
+ return SwIterator<SwSection,SwSectionFormat>( *this ).First();
+}
+
+// Do not destroy all Frames in aDepend (Frames are recognized with a dynamic_cast).
+void SwSectionFormat::DelFrames()
+{
+ SwSectionNode* pSectNd;
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+ if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ // First delete the <SwSectionFrame> of the <SwSectionFormat> instance
+ // mba: test iteration as objects are removed in iteration
+ // use hint which allows to specify, if the content shall be saved or not
+ CallSwClientNotify( SwSectionFrameMoveAndDeleteHint( false ) );
+
+ // Then delete frames of the nested <SwSectionFormat> instances
+ SwIterator<SwSectionFormat,SwSectionFormat> aIter( *this );
+ SwSectionFormat *pLast = aIter.First();
+ while ( pLast )
+ {
+ pLast->DelFrames();
+ pLast = aIter.Next();
+ }
+
+ SwNodeOffset nEnd = pSectNd->EndOfSectionIndex();
+ SwNodeOffset nStart = pSectNd->GetIndex()+1;
+ sw_DeleteFootnote( pSectNd, nStart, nEnd );
+ }
+ if( !pIdx )
+ return;
+
+ // Send Hint for PageDesc. Actually the Layout contained in the
+ // Paste of the Frame itself would need to do this. But that leads
+ // to subsequent errors, which we'd need to solve at run-time.
+ SwNodeIndex aNextNd( *pIdx );
+ SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection( &aNextNd, true, false );
+ if( pCNd )
+ {
+ const SfxPoolItem& rItem = pCNd->GetSwAttrSet().Get(RES_PAGEDESC);
+ pCNd->CallSwClientNotify(sw::LegacyModifyHint(&rItem, &rItem));
+ }
+}
+
+// Create the Views
+void SwSectionFormat::MakeFrames()
+{
+ SwSectionNode* pSectNd;
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+
+ if( pIdx && &GetDoc()->GetNodes() == &pIdx->GetNodes() &&
+ nullptr != (pSectNd = pIdx->GetNode().GetSectionNode() ))
+ {
+ SwNodeIndex aIdx( *pIdx );
+ pSectNd->MakeOwnFrames( &aIdx );
+ }
+}
+
+void SwSectionFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ sal_uInt16 nWhich = pLegacy->GetWhich();
+ auto pOld = pLegacy->m_pOld;
+ auto pNew = pLegacy->m_pNew;
+ switch( nWhich )
+ {
+ case RES_ATTRSET_CHG:
+ if (HasWriterListeners() && pOld && pNew)
+ {
+ SfxItemSet* pNewSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pNew))->GetChgSet();
+ SfxItemSet* pOldSet = const_cast<SwAttrSetChg*>(static_cast<const SwAttrSetChg*>(pOld))->GetChgSet();
+ const SfxPoolItem *pItem;
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_PROTECT, false, &pItem ))
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_PROTECT );
+ pOldSet->ClearItem( RES_PROTECT );
+ }
+
+ // --> edit in readonly sections
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_EDIT_IN_READONLY, false, &pItem ) )
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_EDIT_IN_READONLY );
+ pOldSet->ClearItem( RES_EDIT_IN_READONLY );
+ }
+
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_FTN_AT_TXTEND, false, &pItem ))
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_FTN_AT_TXTEND );
+ pOldSet->ClearItem( RES_FTN_AT_TXTEND );
+ }
+ if( SfxItemState::SET == pNewSet->GetItemState(
+ RES_END_AT_TXTEND, false, &pItem ))
+ {
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pItem, pItem));
+ pNewSet->ClearItem( RES_END_AT_TXTEND );
+ pOldSet->ClearItem( RES_END_AT_TXTEND );
+ }
+ if( !static_cast<const SwAttrSetChg*>(pOld)->GetChgSet()->Count() )
+ return;
+ }
+ break;
+
+ case RES_SECTION_HIDDEN:
+ case RES_SECTION_NOT_HIDDEN:
+ {
+ auto pSect = GetSection();
+ if(!pSect || (RES_SECTION_HIDDEN == nWhich) == pSect->IsHiddenFlag()) // already at target state, skipping.
+ return;
+ }
+ [[fallthrough]];
+ case RES_FTN_AT_TXTEND:
+ case RES_END_AT_TXTEND:
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pOld, pNew));
+ return;
+ case RES_PROTECT:
+ case RES_EDIT_IN_READONLY: // edit in readonly sections
+ // Pass through these Messages until the End of the tree!
+ GetNotifier().Broadcast(sw::LegacyModifyHint(pOld, pNew));
+ return; // That's it!
+
+ case RES_OBJECTDYING:
+ if( !GetDoc()->IsInDtor() && pOld &&
+ static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject == static_cast<void*>(GetRegisteredIn()) )
+ {
+ // My Parents will be destroyed, so get the Parent's Parent
+ // and update
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+ UpdateParent();
+ return;
+ }
+ break;
+
+ case RES_FMT_CHG:
+ if( !GetDoc()->IsInDtor() &&
+ static_cast<const SwFormatChg*>(pNew)->pChangedFormat == static_cast<void*>(GetRegisteredIn()) &&
+ dynamic_cast<const SwSectionFormat*>(static_cast<const SwFormatChg*>(pNew)->pChangedFormat) != nullptr )
+ {
+ // My Parent will be changed, thus I need to update
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+ UpdateParent();
+ return;
+ }
+ break;
+ }
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+
+ if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which()))
+ { // invalidate cached uno object
+ SetXTextSection(uno::Reference<text::XTextSection>(nullptr));
+ }
+}
+
+// Get info from the Format
+bool SwSectionFormat::GetInfo( SfxPoolItem& rInfo ) const
+{
+ switch( rInfo.Which() )
+ {
+ case RES_FINDNEARESTNODE:
+ if( GetFormatAttr( RES_PAGEDESC ).GetPageDesc() )
+ {
+ const SwSectionNode* pNd = GetSectionNode();
+ if( pNd )
+ static_cast<SwFindNearestNode&>(rInfo).CheckNode( *pNd );
+ }
+ return true;
+
+ case RES_CONTENT_VISIBLE:
+ {
+ SwFrame* pFrame = SwIterator<SwFrame,SwFormat>(*this).First();
+ // if the current section has no own frame search for the children
+ if(!pFrame)
+ {
+ SwIterator<SwSectionFormat,SwSectionFormat> aFormatIter(*this);
+ SwSectionFormat* pChild = aFormatIter.First();
+ while(pChild && !pFrame)
+ {
+ pFrame = SwIterator<SwFrame,SwFormat>(*pChild).First();
+ pChild = aFormatIter.Next();
+ }
+ }
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject = pFrame;
+ }
+ return false;
+ }
+ return sw::BroadcastingModify::GetInfo( rInfo );
+}
+
+static bool lcl_SectionCmpPos( const SwSection *pFirst, const SwSection *pSecond)
+{
+ const SwSectionFormat* pFSectFormat = pFirst->GetFormat();
+ const SwSectionFormat* pSSectFormat = pSecond->GetFormat();
+ OSL_ENSURE( pFSectFormat && pSSectFormat &&
+ pFSectFormat->GetContent(false).GetContentIdx() &&
+ pSSectFormat->GetContent(false).GetContentIdx(),
+ "Invalid sections" );
+ return pFSectFormat->GetContent(false).GetContentIdx()->GetIndex() <
+ pSSectFormat->GetContent(false).GetContentIdx()->GetIndex();
+}
+
+// get all Sections that have been derived from this one
+void SwSectionFormat::GetChildSections( SwSections& rArr,
+ SectionSort eSort,
+ bool bAllSections ) const
+{
+ rArr.clear();
+
+ if( !HasWriterListeners() )
+ return;
+
+ SwIterator<SwSectionFormat,SwSectionFormat> aIter(*this);
+ const SwNodeIndex* pIdx;
+ for( SwSectionFormat* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ if( bAllSections ||
+ ( nullptr != ( pIdx = pLast->GetContent(false).
+ GetContentIdx()) && &pIdx->GetNodes() == &GetDoc()->GetNodes() ))
+ {
+ SwSection* pDummy = pLast->GetSection();
+ rArr.push_back( pDummy );
+ }
+
+ // Do we need any sorting?
+ if( 1 < rArr.size() )
+ switch( eSort )
+ {
+ case SectionSort::Pos:
+ std::sort( rArr.begin(), rArr.end(), lcl_SectionCmpPos );
+ break;
+ case SectionSort::Not: break;
+ }
+}
+
+// See whether the Section is within the Nodes or the UndoNodes array
+bool SwSectionFormat::IsInNodesArr() const
+{
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+ return pIdx && &pIdx->GetNodes() == &GetDoc()->GetNodes();
+}
+
+// Parent was changed
+void SwSectionFormat::UpdateParent()
+{
+ if(!HasWriterListeners())
+ return;
+
+ const SwSection* pSection = GetSection();
+ const SvxProtectItem* pProtect = &GetProtect();
+ // edit in readonly sections
+ const SwFormatEditInReadonly* pEditInReadonly = &GetEditInReadonly();
+ bool bIsHidden = pSection->IsHidden();
+ if(GetRegisteredIn())
+ {
+ const SwSection* pPS = GetParentSection();
+ pProtect = &pPS->GetFormat()->GetProtect();
+ pEditInReadonly = &pPS->GetFormat()->GetEditInReadonly();
+ bIsHidden = pPS->IsHiddenFlag();
+ }
+ if(!pProtect->IsContentProtected() != !pSection->IsProtectFlag())
+ CallSwClientNotify(sw::LegacyModifyHint(pProtect, pProtect));
+
+ // edit in readonly sections
+ if(!pEditInReadonly->GetValue() != !pSection->IsEditInReadonlyFlag())
+ CallSwClientNotify(sw::LegacyModifyHint(pEditInReadonly, pEditInReadonly));
+
+ if(bIsHidden == pSection->IsHiddenFlag())
+ {
+ SwMsgPoolItem aMsgItem(o3tl::narrowing<sal_uInt16>(bIsHidden
+ ? RES_SECTION_HIDDEN
+ : RES_SECTION_NOT_HIDDEN));
+ CallSwClientNotify(sw::LegacyModifyHint(&aMsgItem, &aMsgItem));
+ }
+}
+
+SwSectionNode* SwSectionFormat::GetSectionNode()
+{
+ const SwNodeIndex* pIdx = GetContent(false).GetContentIdx();
+ if( pIdx && ( &pIdx->GetNodes() == &GetDoc()->GetNodes() ))
+ return pIdx->GetNode().GetSectionNode();
+ return nullptr;
+}
+
+// Is this Section valid for the GlobalDocument?
+const SwSection* SwSectionFormat::GetGlobalDocSection() const
+{
+ const SwSectionNode* pNd = GetSectionNode();
+ if( pNd &&
+ ( SectionType::FileLink == pNd->GetSection().GetType() ||
+ SectionType::ToxContent == pNd->GetSection().GetType() ) &&
+ pNd->GetIndex() > pNd->GetNodes().GetEndOfExtras().GetIndex() &&
+ !pNd->StartOfSectionNode()->IsSectionNode() &&
+ !pNd->StartOfSectionNode()->FindSectionNode() )
+ return &pNd->GetSection();
+ return nullptr;
+}
+
+// sw::Metadatable
+::sfx2::IXmlIdRegistry& SwSectionFormat::GetRegistry()
+{
+ return GetDoc()->GetXmlIdRegistry();
+}
+
+bool SwSectionFormat::IsInClipboard() const
+{
+ return GetDoc()->IsClipBoard();
+}
+
+bool SwSectionFormat::IsInUndo() const
+{
+ return !IsInNodesArr();
+}
+
+bool SwSectionFormat::IsInContent() const
+{
+ SwNodeIndex const*const pIdx = GetContent(false).GetContentIdx();
+ OSL_ENSURE(pIdx, "SwSectionFormat::IsInContent: no index?");
+ return pIdx == nullptr || !GetDoc()->IsInHeaderFooter(*pIdx);
+}
+
+// n.b.: if the section format represents an index, then there is both a
+// SwXDocumentIndex and a SwXTextSection instance for this single core object.
+// these two can both implement XMetadatable and forward to the same core
+// section format. but here only one UNO object can be returned,
+// so always return the text section.
+uno::Reference< rdf::XMetadatable >
+SwSectionFormat::MakeUnoObject()
+{
+ uno::Reference<rdf::XMetadatable> xMeta;
+ SwSection *const pSection( GetSection() );
+ if (pSection)
+ {
+ xMeta.set( SwXTextSection::CreateXTextSection(this,
+ SectionType::ToxHeader == pSection->GetType()),
+ uno::UNO_QUERY );
+ }
+ return xMeta;
+}
+
+bool SwSectionFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return false;
+}
+
+void SwSectionFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormat"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwSectionFormats::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwSectionFormats"));
+ for (size_t i = 0; i < size(); ++i)
+ GetFormat(i)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Method to break section links inside a linked section
+static void lcl_BreakSectionLinksInSect( const SwSectionNode& rSectNd )
+{
+ if ( !rSectNd.GetSection().IsConnected() )
+ {
+ OSL_FAIL( "method <lcl_RemoveSectionLinksInSect(..)> - no Link at Section of SectionNode" );
+ return;
+ }
+ const ::sfx2::SvBaseLink* pOwnLink( &(rSectNd.GetSection().GetBaseLink() ) );
+ const ::sfx2::SvBaseLinks& rLnks = rSectNd.GetDoc().getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+ for ( auto n = rLnks.size(); n > 0; )
+ {
+ SwIntrnlSectRefLink* pSectLnk = dynamic_cast<SwIntrnlSectRefLink*>(&(*rLnks[ --n ]));
+ if ( pSectLnk && pSectLnk != pOwnLink &&
+ pSectLnk->IsInRange( rSectNd.GetIndex(), rSectNd.EndOfSectionIndex() ) )
+ {
+ // break the link of the corresponding section.
+ // the link is also removed from the link manager
+ SwSectionNode* pSectNode = pSectLnk->GetSectNode();
+ assert(pSectNode);
+ pSectNode->GetSection().BreakLink();
+
+ // for robustness, because link is removed from the link manager
+ if ( n > rLnks.size() )
+ {
+ n = rLnks.size();
+ }
+ }
+ }
+}
+
+static void lcl_UpdateLinksInSect( const SwBaseLink& rUpdLnk, SwSectionNode& rSectNd )
+{
+ SwDoc& rDoc = rSectNd.GetDoc();
+ SwDocShell* pDShell = rDoc.GetDocShell();
+ if( !pDShell || !pDShell->GetMedium() )
+ return ;
+
+ const OUString sName( pDShell->GetMedium()->GetName() );
+ const OUString sMimeType( SotExchange::GetFormatMimeType( SotClipboardFormatId::SIMPLE_FILE ));
+ uno::Any aValue;
+ aValue <<= sName; // Arbitrary name
+
+ const ::sfx2::SvBaseLinks& rLnks = rDoc.getIDocumentLinksAdministration().GetLinkManager().GetLinks();
+ for( auto n = rLnks.size(); n; )
+ {
+ ::sfx2::SvBaseLink* pLnk = &(*rLnks[ --n ]);
+ if( pLnk == &rUpdLnk )
+ continue;
+ if( sfx2::SvBaseLinkObjectType::ClientFile != pLnk->GetObjType() )
+ continue;
+ SwBaseLink* pBLink = dynamic_cast<SwBaseLink*>( pLnk );
+ if( pBLink && pBLink->IsInRange( rSectNd.GetIndex(),
+ rSectNd.EndOfSectionIndex() ) )
+ {
+ // It's in the Section, so update. But only if it's not in the same File!
+ OUString sFName;
+ sfx2::LinkManager::GetDisplayNames( pBLink, nullptr, &sFName );
+ if( sFName != sName )
+ {
+ pBLink->DataChanged( sMimeType, aValue );
+
+ // If needed find the Link pointer to avoid skipping one or calling one twice
+ if( n >= rLnks.size() && 0 != ( n = rLnks.size() ))
+ --n;
+
+ if( n && pLnk != &(*rLnks[ n ]) )
+ {
+ // Find - it can only precede it!
+ while( n )
+ if( pLnk == &(*rLnks[ --n ] ) )
+ break;
+ }
+ }
+ }
+ }
+}
+
+::sfx2::SvBaseLink::UpdateResult SwIntrnlSectRefLink::DataChanged(
+ const OUString& rMimeType, const uno::Any & rValue )
+{
+ SwSectionNode* pSectNd = m_rSectFormat.GetSectionNode();
+ SwDoc* pDoc = m_rSectFormat.GetDoc();
+
+ SotClipboardFormatId nDataFormat = SotExchange::GetFormatIdFromMimeType( rMimeType );
+
+ if( !pSectNd || !pDoc || pDoc->IsInDtor() || ChkNoDataFlag() ||
+ sfx2::LinkManager::RegisterStatusInfoId() == nDataFormat )
+ {
+ // Should we be in the Undo already?
+ return SUCCESS;
+ }
+
+ // #i38810# - Due to possible existing signatures, the
+ // document has to be modified after updating a link.
+ pDoc->getIDocumentState().SetModified();
+ // set additional flag that links have been updated, in order to check this
+ // during load.
+ pDoc->getIDocumentLinksAdministration().SetLinksUpdated( true );
+
+ // Always switch off Undo
+ bool const bWasUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
+ pDoc->GetIDocumentUndoRedo().DoUndo(false);
+ bool bWasVisibleLinks = pDoc->getIDocumentLinksAdministration().IsVisibleLinks();
+ pDoc->getIDocumentLinksAdministration().SetVisibleLinks( false );
+
+ SwPaM* pPam;
+ SwViewShell* pVSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ SwEditShell* pESh = pDoc->GetEditShell();
+ pDoc->getIDocumentFieldsAccess().LockExpFields();
+ {
+ // Insert an empty TextNode at the Section's start
+ SwNodeIndex aIdx( *pSectNd, +1 );
+ SwNodeIndex aEndIdx( *pSectNd->EndOfSectionNode() );
+ SwTextNode* pNewNd = pDoc->GetNodes().MakeTextNode( aIdx,
+ pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT ) );
+
+ if( pESh )
+ pESh->StartAllAction();
+ else if( pVSh )
+ pVSh->StartAction();
+
+ SwPosition aPos( aIdx, SwIndex( pNewNd, 0 ));
+ --aPos.nNode;
+ SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true );
+
+ pPam = new SwPaM( aPos );
+
+ // Delete everything succeeding it
+ --aIdx;
+ DelFlyInRange( aIdx, aEndIdx );
+ DelBookmarks(aIdx, aEndIdx);
+ ++aIdx;
+
+ pDoc->GetNodes().Delete( aIdx, aEndIdx.GetIndex() - aIdx.GetIndex() );
+ }
+
+ SwSection& rSection = pSectNd->GetSection();
+ rSection.SetConnectFlag(false);
+
+ Reader* pRead = nullptr;
+ switch( nDataFormat )
+ {
+ case SotClipboardFormatId::STRING:
+ pRead = ReadAscii;
+ break;
+
+ case SotClipboardFormatId::RICHTEXT:
+ case SotClipboardFormatId::RTF:
+ pRead = SwReaderWriter::GetRtfReader();
+ break;
+
+ case SotClipboardFormatId::SIMPLE_FILE:
+ if ( rValue.hasValue() )
+ {
+ OUString sFileName;
+ if ( !(rValue >>= sFileName) )
+ break;
+ OUString sFilter;
+ OUString sRange;
+ sfx2::LinkManager::GetDisplayNames( this, nullptr, &sFileName,
+ &sRange, &sFilter );
+
+ RedlineFlags eOldRedlineFlags = RedlineFlags::NONE;
+ SfxObjectShellRef xDocSh;
+ SfxObjectShellLock xLockRef;
+ int nRet;
+ if( sFileName.isEmpty() )
+ {
+ xDocSh = pDoc->GetDocShell();
+ nRet = 1;
+ }
+ else
+ {
+ nRet = SwFindDocShell( xDocSh, xLockRef, sFileName,
+ rSection.GetLinkFilePassword(),
+ sFilter, 0, pDoc->GetDocShell() );
+ if( nRet )
+ {
+ SwDoc* pSrcDoc = static_cast<SwDocShell*>( xDocSh.get() )->GetDoc();
+ eOldRedlineFlags = pSrcDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pSrcDoc->getIDocumentRedlineAccess().SetRedlineFlags( RedlineFlags::ShowInsert );
+ }
+ }
+
+ if( nRet )
+ {
+ rSection.SetConnectFlag();
+
+ SwNodeIndex aSave( pPam->GetPoint()->nNode, -1 );
+ std::unique_ptr<SwNodeRange> pCpyRg;
+
+ if( xDocSh->GetMedium() &&
+ rSection.GetLinkFilePassword().isEmpty() )
+ {
+ if( const SfxStringItem* pItem = xDocSh->GetMedium()->GetItemSet()->
+ GetItemIfSet( SID_PASSWORD, false ) )
+ rSection.SetLinkFilePassword( pItem->GetValue() );
+ }
+
+ SwDoc* pSrcDoc = static_cast<SwDocShell*>( xDocSh.get() )->GetDoc();
+
+ if( !sRange.isEmpty() )
+ {
+ // Catch recursion
+ bool bRecursion = false;
+ if( pSrcDoc == pDoc )
+ {
+ tools::SvRef<SwServerObject> refObj( static_cast<SwServerObject*>(
+ pDoc->getIDocumentLinksAdministration().CreateLinkSource( sRange )));
+ if( refObj.is() )
+ {
+ bRecursion = refObj->IsLinkInServer( this ) ||
+ ChkNoDataFlag();
+ }
+ }
+
+ SwNodeIndex& rInsPos = pPam->GetPoint()->nNode;
+
+ SwPaM* pCpyPam = nullptr;
+ if( !bRecursion &&
+ pSrcDoc->GetDocumentLinksAdministrationManager().SelectServerObj( sRange, pCpyPam, pCpyRg )
+ && pCpyPam )
+ {
+ if( pSrcDoc != pDoc ||
+ pCpyPam->Start()->nNode > rInsPos ||
+ rInsPos >= pCpyPam->End()->nNode )
+ {
+ pSrcDoc->getIDocumentContentOperations().CopyRange(*pCpyPam, *pPam->GetPoint(), SwCopyFlags::CheckPosInFly);
+ }
+ delete pCpyPam;
+ }
+ if( pCpyRg && pSrcDoc == pDoc &&
+ pCpyRg->aStart < rInsPos && rInsPos < pCpyRg->aEnd )
+ {
+ pCpyRg.reset();
+ }
+ }
+ else if( pSrcDoc != pDoc )
+ pCpyRg.reset(new SwNodeRange( pSrcDoc->GetNodes().GetEndOfExtras(), SwNodeOffset(2),
+ pSrcDoc->GetNodes().GetEndOfContent() ));
+
+ // #i81653#
+ // Update links of extern linked document or extern linked
+ // document section, if section is protected.
+ if ( pSrcDoc != pDoc &&
+ rSection.IsProtectFlag() )
+ {
+ pSrcDoc->getIDocumentLinksAdministration().GetLinkManager().UpdateAllLinks( false, false, nullptr );
+ }
+
+ if( pCpyRg )
+ {
+ SwNodeIndex& rInsPos = pPam->GetPoint()->nNode;
+ bool bCreateFrame = rInsPos.GetIndex() <=
+ pDoc->GetNodes().GetEndOfExtras().GetIndex() ||
+ rInsPos.GetNode().FindTableNode();
+
+ SwTableNumFormatMerge aTNFM( *pSrcDoc, *pDoc );
+
+ pSrcDoc->GetDocumentContentOperationsManager().CopyWithFlyInFly(*pCpyRg, rInsPos, nullptr, bCreateFrame);
+ ++aSave;
+
+ if( !bCreateFrame )
+ ::MakeFrames( pDoc, aSave, rInsPos );
+
+ // Delete last Node, only if it was copied successfully
+ // (the Section contains more than one Node)
+ if( SwNodeOffset(2) < pSectNd->EndOfSectionIndex() - pSectNd->GetIndex() )
+ {
+ aSave = rInsPos;
+ pPam->Move( fnMoveBackward, GoInNode );
+ pPam->SetMark(); // Rewire both SwPositions
+
+ pDoc->CorrAbs( aSave, *pPam->GetPoint(), 0, true );
+ pDoc->GetNodes().Delete( aSave );
+ }
+ pCpyRg.reset();
+ }
+
+ lcl_BreakSectionLinksInSect( *pSectNd );
+
+ // Update all Links in this Section
+ lcl_UpdateLinksInSect( *this, *pSectNd );
+ }
+ if( xDocSh.is() )
+ {
+ if( 2 == nRet )
+ xDocSh->DoClose();
+ else if( static_cast<SwDocShell*>( xDocSh.get() )->GetDoc() )
+ static_cast<SwDocShell*>( xDocSh.get() )->GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags(
+ eOldRedlineFlags );
+ }
+ }
+ break;
+ default: break;
+ }
+
+ // Only create DDE if Shell is available!
+ uno::Sequence< sal_Int8 > aSeq;
+ if( pRead && rValue.hasValue() && ( rValue >>= aSeq ) )
+ {
+ if( pESh )
+ {
+ pESh->Push();
+ SwPaM* pCursor = pESh->GetCursor();
+ *pCursor->GetPoint() = *pPam->GetPoint();
+ delete pPam;
+ pPam = pCursor;
+ }
+
+ SvMemoryStream aStrm( const_cast<sal_Int8 *>(aSeq.getConstArray()), aSeq.getLength(),
+ StreamMode::READ );
+ aStrm.Seek( 0 );
+
+ // TODO/MBA: it's impossible to set a BaseURL here!
+ SwReader aTmpReader( aStrm, OUString(), pDoc->GetDocShell()->GetMedium()->GetBaseURL(), *pPam );
+
+ if( ! aTmpReader.Read( *pRead ).IsError() )
+ {
+ rSection.SetConnectFlag();
+ }
+
+ if( pESh )
+ {
+ pESh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ pPam = nullptr; // pam was deleted earlier
+ }
+ }
+
+ // remove all undo actions and turn undo on again
+ pDoc->GetIDocumentUndoRedo().DelAllUndoObj();
+ pDoc->GetIDocumentUndoRedo().DoUndo(bWasUndo);
+ pDoc->getIDocumentLinksAdministration().SetVisibleLinks( bWasVisibleLinks );
+
+ pDoc->getIDocumentFieldsAccess().UnlockExpFields();
+ if( !pDoc->getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ pDoc->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+
+ if( pESh )
+ pESh->EndAllAction();
+ else if( pVSh )
+ pVSh->EndAction();
+ delete pPam; // Was created at the start
+
+ return SUCCESS;
+}
+
+void SwIntrnlSectRefLink::Closed()
+{
+ SwDoc* pDoc = m_rSectFormat.GetDoc();
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ // Advise says goodbye: mark the Section as not protected
+ // and change the Flag
+ const SwSectionFormats& rFormats = pDoc->GetSections();
+ for( auto n = rFormats.size(); n; )
+ if (rFormats[--n] == &m_rSectFormat)
+ {
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ SwEditShell* pESh = pDoc->GetEditShell();
+
+ if( pESh )
+ pESh->StartAllAction();
+ else
+ pSh->StartAction();
+
+ SwSectionData aSectionData(*m_rSectFormat.GetSection());
+ aSectionData.SetType( SectionType::Content );
+ aSectionData.SetLinkFileName( OUString() );
+ aSectionData.SetProtectFlag( false );
+ // edit in readonly sections
+ aSectionData.SetEditInReadonlyFlag( false );
+
+ aSectionData.SetConnectFlag( false );
+
+ pDoc->UpdateSection( n, aSectionData );
+
+ // Make all Links within the Section visible again
+ SwSectionNode* pSectNd = m_rSectFormat.GetSectionNode();
+ if( pSectNd )
+ SwSection::MakeChildLinksVisible( *pSectNd );
+
+ if( pESh )
+ pESh->EndAllAction();
+ else
+ pSh->EndAction();
+ break;
+ }
+ }
+ SvBaseLink::Closed();
+}
+
+void SwSection::CreateLink( LinkCreateType eCreateType )
+{
+ SwSectionFormat* pFormat = GetFormat();
+ OSL_ENSURE(pFormat, "SwSection::CreateLink: no format?");
+ if (!pFormat || (SectionType::Content == m_Data.GetType()))
+ return ;
+
+ SfxLinkUpdateMode nUpdateType = SfxLinkUpdateMode::ALWAYS;
+
+ if (!m_RefLink.is())
+ {
+ // create BaseLink
+ m_RefLink = new SwIntrnlSectRefLink( *pFormat, nUpdateType );
+ }
+ else
+ {
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+
+ SwIntrnlSectRefLink *const pLnk =
+ static_cast<SwIntrnlSectRefLink*>( m_RefLink.get() );
+
+ const OUString sCmd(m_Data.GetLinkFileName());
+ pLnk->SetUpdateMode( nUpdateType );
+ pLnk->SetVisible( pFormat->GetDoc()->getIDocumentLinksAdministration().IsVisibleLinks() );
+
+ switch (m_Data.GetType())
+ {
+ case SectionType::DdeLink:
+ pLnk->SetLinkSourceName( sCmd );
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( pLnk );
+ break;
+ case SectionType::FileLink:
+ {
+ pLnk->SetContentType( SotClipboardFormatId::SIMPLE_FILE );
+ sal_Int32 nIndex = 0;
+ const OUString sFile(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex ));
+ const OUString sFltr(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex ));
+ const OUString sRange(sCmd.getToken( 0, sfx2::cTokenSeparator, nIndex ));
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *pLnk,
+ static_cast<sfx2::SvBaseLinkObjectType>(m_Data.GetType()),
+ sFile,
+ ( !sFltr.isEmpty() ? &sFltr : nullptr ),
+ ( !sRange.isEmpty() ? &sRange : nullptr ) );
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "What kind of Link is this?" );
+ }
+
+ switch( eCreateType )
+ {
+ case LinkCreateType::Connect: // Connect Link right away
+ pLnk->Connect();
+ break;
+
+ case LinkCreateType::Update: // Connect Link and update
+ pLnk->Update();
+ break;
+ case LinkCreateType::NONE: break;
+ }
+}
+
+void SwSection::BreakLink()
+{
+ const SectionType eCurrentType( GetType() );
+ if ( eCurrentType == SectionType::Content ||
+ eCurrentType == SectionType::ToxHeader ||
+ eCurrentType == SectionType::ToxContent )
+ {
+ // nothing to do
+ return;
+ }
+
+ // Release link, if it exists
+ if (m_RefLink.is())
+ {
+ SwSectionFormat *const pFormat( GetFormat() );
+ OSL_ENSURE(pFormat, "SwSection::BreakLink: no format?");
+ if (pFormat)
+ {
+ pFormat->GetDoc()->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+ m_RefLink.clear();
+ }
+ // change type
+ SetType( SectionType::Content );
+ // reset linked file data
+ SetLinkFileName( OUString() );
+ SetLinkFilePassword( OUString() );
+}
+
+void SwSection::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwSection"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("registered-in"), "%p",
+ GetRegisteredIn());
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+const SwNode* SwIntrnlSectRefLink::GetAnchor() const { return m_rSectFormat.GetSectionNode(); }
+
+bool SwIntrnlSectRefLink::IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const
+{
+ SwStartNode* pSttNd = m_rSectFormat.GetSectionNode();
+ return pSttNd &&
+ nSttNd < pSttNd->GetIndex() &&
+ pSttNd->EndOfSectionIndex() < nEndNd;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/swbaslnk.cxx b/sw/source/core/docnode/swbaslnk.cxx
new file mode 100644
index 000000000..5a5967c87
--- /dev/null
+++ b/sw/source/core/docnode/swbaslnk.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/svapp.hxx>
+
+#include <osl/diagnose.h>
+#include <sfx2/lnkbase.hxx>
+#include <editeng/boxitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/event.hxx>
+#include <sot/exchange.hxx>
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <frmatr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <swevent.hxx>
+#include <swbaslnk.hxx>
+#include <swserv.hxx>
+#include <viewsh.hxx>
+#include <ndgrf.hxx>
+#include <htmltbl.hxx>
+#include <dialoghelp.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+static bool SetGrfFlySize( const Size& rGrfSz, SwGrfNode* pGrfNd, const Size &rOrigGrfSize );
+
+
+::sfx2::SvBaseLink::UpdateResult SwBaseLink::DataChanged(
+ const OUString& rMimeType, const uno::Any & rValue )
+{
+ if( !m_pContentNode )
+ {
+ OSL_ENSURE(false, "DataChanged without ContentNode" );
+ return ERROR_GENERAL;
+ }
+
+ SwDoc& rDoc = m_pContentNode->GetDoc();
+ if( rDoc.IsInDtor() || ChkNoDataFlag() )
+ {
+ return SUCCESS;
+ }
+
+ SotClipboardFormatId nFormat = SotExchange::GetFormatIdFromMimeType( rMimeType );
+
+ if( m_pContentNode->IsNoTextNode() &&
+ nFormat == sfx2::LinkManager::RegisterStatusInfoId() )
+ {
+ // Only a status change - serve Events?
+ OUString sState;
+
+ if( rValue.hasValue() && ( rValue >>= sState ))
+ {
+ SvMacroItemId nEvent = SvMacroItemId::NONE;
+ switch( sState.toInt32() )
+ {
+ case sfx2::LinkManager::STATE_LOAD_OK: nEvent = SvMacroItemId::OnImageLoadDone; break;
+ case sfx2::LinkManager::STATE_LOAD_ERROR: nEvent = SvMacroItemId::OnImageLoadError; break;
+ case sfx2::LinkManager::STATE_LOAD_ABORT: nEvent = SvMacroItemId::OnImageLoadCancel; break;
+ }
+
+ SwFrameFormat* pFormat;
+ if( nEvent != SvMacroItemId::NONE && nullptr != ( pFormat = m_pContentNode->GetFlyFormat() ))
+ {
+ SwCallMouseEvent aCallEvent;
+ aCallEvent.Set( EVENT_OBJECT_IMAGE, pFormat );
+ rDoc.CallEvent( nEvent, aCallEvent );
+ }
+ }
+ return SUCCESS; // That's it!
+ }
+
+ bool bUpdate = false;
+ bool bFrameInPaint = false;
+ Size aGrfSz, aOldSz;
+
+ SwGrfNode* pSwGrfNode = nullptr;
+
+ if (m_pContentNode->IsGrfNode())
+ {
+ pSwGrfNode = m_pContentNode->GetGrfNode();
+ assert(pSwGrfNode && "Error, pSwGrfNode expected when node answers IsGrfNode() with true (!)");
+ aOldSz = pSwGrfNode->GetTwipSize();
+ const GraphicObject& rGrfObj = pSwGrfNode->GetGrfObj();
+
+ bFrameInPaint = pSwGrfNode->IsFrameInPaint();
+
+ Graphic aGrf;
+
+ // tdf#124698 if any auth dialog is needed, find what the parent window should be
+ weld::Window* pDlgParent = GetFrameWeld(&rDoc);
+
+ if (rDoc.getIDocumentLinksAdministration().GetLinkManager().GetGraphicFromAny(rMimeType, rValue, aGrf, pDlgParent) &&
+ ( GraphicType::Default != aGrf.GetType() ||
+ GraphicType::Default != rGrfObj.GetType() ) )
+ {
+ aGrfSz = ::GetGraphicSizeTwip( aGrf, nullptr );
+
+ pSwGrfNode->SetGraphic(aGrf);
+ bUpdate = true;
+
+ // Always use the correct graphic size
+ if( aGrfSz.Height() && aGrfSz.Width() &&
+ aOldSz.Height() && aOldSz.Width() &&
+ aGrfSz != aOldSz )
+ {
+ pSwGrfNode->SetTwipSize(aGrfSz);
+ aOldSz = aGrfSz;
+ }
+ }
+ }
+ else if( m_pContentNode->IsOLENode() )
+ bUpdate = true;
+
+ if ( !bUpdate || bFrameInPaint )
+ return SUCCESS;
+
+ if(pSwGrfNode && !SetGrfFlySize(aGrfSz, pSwGrfNode, aOldSz))
+ pSwGrfNode->TriggerGraphicArrived();
+
+ return SUCCESS;
+}
+
+static bool SetGrfFlySize( const Size& rGrfSz, SwGrfNode* pGrfNd, const Size& rOrigGrfSize )
+{
+ bool bRet = false;
+ SwViewShell *pSh = pGrfNd->GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ std::unique_ptr<CurrShell> pCurr;
+ if ( pGrfNd->GetDoc().GetEditShell() )
+ pCurr.reset(new CurrShell( pSh ));
+
+ Size aSz = rOrigGrfSize;
+ if ( !(aSz.Width() && aSz.Height()) &&
+ rGrfSz.Width() && rGrfSz.Height() )
+ {
+ SwFrameFormat* pFormat = nullptr;
+ if (pGrfNd->IsChgTwipSize())
+ pFormat = pGrfNd->GetFlyFormat();
+ if (nullptr != pFormat)
+ {
+ Size aCalcSz( aSz );
+ if ( !aSz.Height() && aSz.Width() )
+ // Calculate the right height
+ aCalcSz.setHeight( rGrfSz.Height() *
+ aSz.Width() / rGrfSz.Width() );
+ else if ( !aSz.Width() && aSz.Height() )
+ // Calculate the right width
+ aCalcSz.setWidth( rGrfSz.Width() *
+ aSz.Height() / rGrfSz.Height() );
+ else
+ // Take over height and width
+ aCalcSz = rGrfSz;
+
+ const SvxBoxItem &rBox = pFormat->GetBox();
+ aCalcSz.AdjustWidth(rBox.CalcLineSpace(SvxBoxItemLine::LEFT) +
+ rBox.CalcLineSpace(SvxBoxItemLine::RIGHT) );
+ aCalcSz.AdjustHeight(rBox.CalcLineSpace(SvxBoxItemLine::TOP) +
+ rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM) );
+ const SwFormatFrameSize& rOldAttr = pFormat->GetFrameSize();
+ if( rOldAttr.GetSize() != aCalcSz )
+ {
+ SwFormatFrameSize aAttr( rOldAttr );
+ aAttr.SetSize( aCalcSz );
+ pFormat->SetFormatAttr( aAttr );
+ bRet = true;
+ }
+
+ if( !aSz.Width() )
+ {
+ // If the graphic is anchored in a table, we need to recalculate
+ // the table rows
+ const SwDoc& rDoc = pGrfNd->GetDoc();
+ const SwPosition* pAPos = pFormat->GetAnchor().GetContentAnchor();
+ SwTableNode *pTableNd;
+ if (pAPos && nullptr != (pTableNd = pAPos->nNode.GetNode().FindTableNode()))
+ {
+ const bool bLastGrf = !pTableNd->GetTable().DecGrfsThatResize();
+ SwHTMLTableLayout *pLayout =
+ pTableNd->GetTable().GetHTMLTableLayout();
+ if( pLayout )
+ {
+ const sal_uInt16 nBrowseWidth =
+ pLayout->GetBrowseWidthByTable( rDoc );
+ if ( nBrowseWidth )
+ {
+ pLayout->Resize( nBrowseWidth, true, true,
+ bLastGrf ? HTMLTABLE_RESIZE_NOW
+ : 500 );
+ }
+ }
+ }
+ }
+ }
+
+ // SetTwipSize rescales an ImageMap if needed for which
+ // it requires the Frame Format
+ pGrfNd->SetTwipSize( rGrfSz );
+ }
+
+ return bRet;
+}
+
+bool SwBaseLink::SwapIn( bool bWaitForData, bool bNativFormat )
+{
+ if( !GetObj() && ( bNativFormat || ( !IsSynchron() && bWaitForData ) ))
+ {
+ AddNextRef();
+ GetRealObject_();
+ ReleaseRef();
+ }
+
+ bool bRes = false;
+
+ if( GetObj() )
+ {
+ OUString aMimeType( SotExchange::GetFormatMimeType( GetContentType() ));
+ uno::Any aValue;
+ (void)GetObj()->GetData( aValue, aMimeType, !IsSynchron() && bWaitForData );
+
+ if( bWaitForData && !GetObj() )
+ {
+ OSL_ENSURE( false, "The SvxFileObject was deleted in a GetData!" );
+ }
+ else
+ {
+ bRes = aValue.hasValue();
+ if ( bRes )
+ {
+ DataChanged( aMimeType, aValue );
+ }
+ }
+ }
+ else if( !IsSynchron() && bWaitForData )
+ {
+ SetSynchron( true );
+ bRes = Update();
+ SetSynchron( false );
+ }
+ else
+ bRes = Update();
+
+ return bRes;
+}
+
+void SwBaseLink::Closed()
+{
+ if( m_pContentNode && !m_pContentNode->GetDoc().IsInDtor() )
+ {
+ // Delete the connection
+ if( m_pContentNode->IsGrfNode() )
+ static_cast<SwGrfNode*>(m_pContentNode)->ReleaseLink();
+ }
+ SvBaseLink::Closed();
+}
+
+const SwNode* SwBaseLink::GetAnchor() const
+{
+ if (m_pContentNode)
+ {
+ SwFrameFormat *const pFormat = m_pContentNode->GetFlyFormat();
+ if (pFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ SwPosition const*const pAPos = rAnchor.GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId())))
+ {
+ return &pAPos->nNode.GetNode();
+ }
+ return nullptr;
+ }
+ }
+
+ OSL_ENSURE( false, "GetAnchor is not shadowed" );
+ return nullptr;
+}
+
+bool SwBaseLink::IsRecursion( const SwBaseLink* pChkLnk ) const
+{
+ tools::SvRef<SwServerObject> aRef( static_cast<SwServerObject*>(GetObj()) );
+ if( aRef.is() )
+ {
+ // As it's a ServerObject, we query all contained Links
+ // if we are contained in them. Else we have a recursion.
+ return aRef->IsLinkInServer( pChkLnk );
+ }
+ return false;
+}
+
+bool SwBaseLink::IsInRange( SwNodeOffset, SwNodeOffset ) const
+{
+ // Not Graphic or OLE Links
+ // Fields or Sections have their own derivation!
+ return false;
+}
+
+SwBaseLink::~SwBaseLink()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/swthreadjoiner.cxx b/sw/source/core/docnode/swthreadjoiner.cxx
new file mode 100644
index 000000000..6ff198d7d
--- /dev/null
+++ b/sw/source/core/docnode/swthreadjoiner.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swthreadjoiner.hxx>
+#include <com/sun/star/util/JobManager.hpp>
+#include <comphelper/processfactory.hxx>
+#include <mutex>
+
+// Testing
+
+using namespace ::com::sun::star;
+
+namespace
+{
+uno::Reference<util::XJobManager> pThreadJoiner;
+}
+
+uno::Reference<util::XJobManager>& SwThreadJoiner::GetThreadJoiner()
+{
+ static std::mutex theJoinerMutex;
+ std::unique_lock aGuard(theJoinerMutex);
+
+ if (!pThreadJoiner.is())
+ {
+ pThreadJoiner = util::JobManager::create(comphelper::getProcessComponentContext());
+ }
+
+ return pThreadJoiner;
+}
+
+void SwThreadJoiner::ReleaseThreadJoiner() { pThreadJoiner.clear(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/swthreadmanager.cxx b/sw/source/core/docnode/swthreadmanager.cxx
new file mode 100644
index 000000000..3c81ff570
--- /dev/null
+++ b/sw/source/core/docnode/swthreadmanager.cxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swthreadmanager.hxx>
+#include <swthreadjoiner.hxx>
+#include <observablethread.hxx>
+#include "threadmanager.hxx"
+
+/** class to manage threads in Writer - it conforms the singleton pattern
+
+ #i73788#
+*/
+bool SwThreadManager::sbThreadManagerInstantiated = false;
+
+SwThreadManager::SwThreadManager()
+ : mpThreadManagerImpl( new ThreadManager( SwThreadJoiner::GetThreadJoiner() ) )
+{
+ mpThreadManagerImpl->Init();
+ sbThreadManagerInstantiated = true;
+}
+
+SwThreadManager::~SwThreadManager()
+{
+}
+
+SwThreadManager& SwThreadManager::GetThreadManager()
+{
+ static SwThreadManager gThreadManager;
+ return gThreadManager;
+}
+
+bool SwThreadManager::ExistsThreadManager()
+{
+ return sbThreadManagerInstantiated;
+}
+
+oslInterlockedCount SwThreadManager::AddThread( const rtl::Reference< ObservableThread >& rThread )
+{
+ return mpThreadManagerImpl->AddThread( rThread );
+}
+
+void SwThreadManager::RemoveThread( const oslInterlockedCount nThreadID )
+{
+ mpThreadManagerImpl->RemoveThread( nThreadID );
+}
+
+void SwThreadManager::SuspendStartingOfThreads()
+{
+ mpThreadManagerImpl->SuspendStartingOfThreads();
+}
+
+void SwThreadManager::ResumeStartingOfThreads()
+{
+ mpThreadManagerImpl->ResumeStartingOfThreads();
+}
+
+bool SwThreadManager::StartingOfThreadsSuspended()
+{
+ return mpThreadManagerImpl->StartingOfThreadsSuspended();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/threadlistener.cxx b/sw/source/core/docnode/threadlistener.cxx
new file mode 100644
index 000000000..cba118e46
--- /dev/null
+++ b/sw/source/core/docnode/threadlistener.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#include <threadlistener.hxx>
+#include "threadmanager.hxx"
+
+/** helper class to observe threads
+
+ #i73788#
+*/
+ThreadListener::ThreadListener( ThreadManager& rThreadListenerOwner )
+ : mrThreadListenerOwner( rThreadListenerOwner )
+{
+}
+
+ThreadListener::~ThreadListener()
+{
+}
+
+void ThreadListener::ListenToThread( const oslInterlockedCount nThreadID,
+ ObservableThread& rThread )
+{
+ rThread.SetListener( mrThreadListenerOwner.GetThreadListenerWeakRef(),
+ nThreadID );
+}
+
+void ThreadListener::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
+{
+ mrThreadListenerOwner.NotifyAboutFinishedThread( nThreadID );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/threadmanager.cxx b/sw/source/core/docnode/threadmanager.cxx
new file mode 100644
index 000000000..20d71e746
--- /dev/null
+++ b/sw/source/core/docnode/threadmanager.cxx
@@ -0,0 +1,250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "cancellablejob.hxx"
+#include "threadmanager.hxx"
+#include <threadlistener.hxx>
+
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+#include <com/sun/star/util/XJobManager.hpp>
+
+using namespace ::com::sun::star;
+
+/** class to manage threads
+
+ #i73788#
+*/
+const std::deque< ThreadManager::tThreadData >::size_type ThreadManager::snStartedSize = 10;
+
+ThreadManager::ThreadManager( uno::Reference< util::XJobManager > const & rThreadJoiner )
+ : mrThreadJoiner( rThreadJoiner ),
+ mnThreadIDCounter( 0 ),
+ maStartNewThreadIdle("SW ThreadManager StartNewThreadIdle"),
+ mbStartingOfThreadsSuspended( false )
+{
+}
+
+void ThreadManager::Init()
+{
+ mpThreadListener = std::make_shared<ThreadListener>( *this );
+
+ maStartNewThreadIdle.SetPriority( TaskPriority::LOWEST );
+ maStartNewThreadIdle.SetInvokeHandler( LINK( this, ThreadManager, TryToStartNewThread ) );
+}
+
+ThreadManager::~ThreadManager()
+{
+ maWaitingForStartThreads.clear();
+ maStartedThreads.clear();
+}
+
+std::weak_ptr< IFinishedThreadListener > ThreadManager::GetThreadListenerWeakRef() const
+{
+ return mpThreadListener;
+}
+
+void ThreadManager::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
+{
+ RemoveThread( nThreadID, true );
+}
+
+oslInterlockedCount ThreadManager::AddThread(
+ const rtl::Reference< ObservableThread >& rThread )
+
+{
+ std::unique_lock aGuard(maMutex);
+
+ // create new thread
+ tThreadData aThreadData;
+ oslInterlockedCount nNewThreadID( osl_atomic_increment( &mnThreadIDCounter ) );
+ {
+ aThreadData.nThreadID = nNewThreadID;
+
+ aThreadData.pThread = rThread;
+ aThreadData.aJob = new CancellableJob( aThreadData.pThread );
+
+ aThreadData.pThread->setPriority( osl_Thread_PriorityBelowNormal );
+ mpThreadListener->ListenToThread( aThreadData.nThreadID,
+ *(aThreadData.pThread) );
+ }
+
+ // add thread to manager
+ if ( maStartedThreads.size() < snStartedSize &&
+ !mbStartingOfThreadsSuspended )
+ {
+ // Try to start thread
+ if ( !StartThread( aThreadData ) )
+ {
+ // No success on starting thread
+ // If no more started threads exist, but still threads are waiting,
+ // setup Timer to start thread from waiting ones
+ if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
+ {
+ maStartNewThreadIdle.Start();
+ }
+ }
+ }
+ else
+ {
+ // Thread will be started later
+ maWaitingForStartThreads.push_back( aThreadData );
+ }
+
+ return nNewThreadID;
+}
+
+void ThreadManager::RemoveThread( const oslInterlockedCount nThreadID,
+ const bool bThreadFinished )
+{
+ // --> SAFE ----
+ std::unique_lock aGuard(maMutex);
+
+ std::deque< tThreadData >::iterator aIter =
+ std::find_if( maStartedThreads.begin(), maStartedThreads.end(),
+ ThreadPred( nThreadID ) );
+
+ if ( aIter != maStartedThreads.end() )
+ {
+ tThreadData aTmpThreadData( *aIter );
+
+ maStartedThreads.erase( aIter );
+
+ if ( bThreadFinished )
+ {
+ // release thread as job from thread joiner instance
+ css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
+ if ( rThreadJoiner.is() )
+ {
+ rThreadJoiner->releaseJob( aTmpThreadData.aJob );
+ }
+ else
+ {
+ OSL_FAIL( "<ThreadManager::RemoveThread(..)> - ThreadJoiner already gone!" );
+ }
+ }
+
+ // Try to start thread from waiting ones
+ aGuard.unlock();
+ TryToStartNewThread( nullptr );
+ }
+ else
+ {
+ aIter = std::find_if( maWaitingForStartThreads.begin(),
+ maWaitingForStartThreads.end(), ThreadPred( nThreadID ) );
+
+ if ( aIter != maWaitingForStartThreads.end() )
+ {
+ maWaitingForStartThreads.erase( aIter );
+ }
+ }
+ // <-- SAFE ----
+}
+
+bool ThreadManager::StartWaitingThread()
+{
+ if ( !maWaitingForStartThreads.empty() )
+ {
+ tThreadData aThreadData( maWaitingForStartThreads.front() );
+ maWaitingForStartThreads.pop_front();
+ return StartThread( aThreadData );
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool ThreadManager::StartThread( const tThreadData& rThreadData )
+{
+ bool bThreadStarted( false );
+
+ if ( rThreadData.pThread->create() )
+ {
+ // start of thread successful.
+ bThreadStarted = true;
+
+ maStartedThreads.push_back( rThreadData );
+
+ // register thread as job at thread joiner instance
+ css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
+ if ( rThreadJoiner.is() )
+ {
+ rThreadJoiner->registerJob( rThreadData.aJob );
+ }
+ else
+ {
+ OSL_FAIL( "<ThreadManager::StartThread(..)> - ThreadJoiner already gone!" );
+ }
+ }
+ else
+ {
+ // thread couldn't be started.
+ maWaitingForStartThreads.push_front( rThreadData );
+ }
+
+ return bThreadStarted;
+}
+
+IMPL_LINK_NOARG(ThreadManager, TryToStartNewThread, Timer *, void)
+{
+ std::unique_lock aGuard(maMutex);
+
+ if ( mbStartingOfThreadsSuspended )
+ return;
+
+ // Try to start thread from waiting ones
+ if ( !StartWaitingThread() )
+ {
+ // No success on starting thread
+ // If no more started threads exist, but still threads are waiting,
+ // setup Timer to start thread from waiting ones
+ if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
+ {
+ maStartNewThreadIdle.Start();
+ }
+ }
+}
+
+void ThreadManager::ResumeStartingOfThreads()
+{
+ std::unique_lock aGuard(maMutex);
+
+ mbStartingOfThreadsSuspended = false;
+
+ while ( maStartedThreads.size() < snStartedSize &&
+ !maWaitingForStartThreads.empty() )
+ {
+ if ( !StartWaitingThread() )
+ {
+ // No success on starting thread
+ // If no more started threads exist, but still threads are waiting,
+ // setup Timer to start thread from waiting ones
+ if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
+ {
+ maStartNewThreadIdle.Start();
+ break;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/docnode/threadmanager.hxx b/sw/source/core/docnode/threadmanager.hxx
new file mode 100644
index 000000000..ace0bc031
--- /dev/null
+++ b/sw/source/core/docnode/threadmanager.hxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <vcl/idle.hxx>
+#include <mutex>
+#include <osl/interlck.h>
+#include <rtl/ref.hxx>
+
+#include <deque>
+#include <cppuhelper/weakref.hxx>
+#include <observablethread.hxx>
+
+#include <memory>
+
+namespace com::sun::star::util { class XCancellable; }
+namespace com::sun::star::util { class XJobManager; }
+
+class IFinishedThreadListener;
+class ThreadListener;
+class Timer;
+
+/** class to manage threads
+
+ OD 2007-01-29 #i73788#
+ An instance of this class takes care of the starting of threads.
+ It assures that not more than <mnStartedSize> threads
+ are started.
+*/
+class ThreadManager final
+{
+ public:
+
+ explicit ThreadManager( css::uno::Reference< css::util::XJobManager > const & rThreadJoiner );
+ ~ThreadManager();
+
+ std::weak_ptr< IFinishedThreadListener > GetThreadListenerWeakRef() const;
+ void NotifyAboutFinishedThread( const oslInterlockedCount nThreadID );
+
+ /** initialization
+
+ IMPORTANT NOTE: Needs to be called directly after construction
+ */
+ void Init();
+
+ /** add thread to the thread manager and taking ownership for the thread
+
+ @return unique ID for added thread
+ */
+ oslInterlockedCount AddThread(
+ const ::rtl::Reference< ObservableThread >& rThread );
+
+ void RemoveThread( const oslInterlockedCount nThreadID,
+ const bool bThreadFinished = false );
+
+ DECL_LINK( TryToStartNewThread, Timer*, void );
+
+ /** suspend the starting of threads
+
+ Suspending the starting of further threads is sensible during the
+ destruction of a Writer document.
+ */
+ void SuspendStartingOfThreads()
+ {
+ std::unique_lock aGuard(maMutex);
+
+ mbStartingOfThreadsSuspended = true;
+ }
+
+ /** continues the starting of threads after it has been suspended
+ */
+ void ResumeStartingOfThreads();
+
+ bool StartingOfThreadsSuspended()
+ {
+ std::unique_lock aGuard(maMutex);
+
+ return mbStartingOfThreadsSuspended;
+ }
+
+ struct tThreadData
+ {
+ oslInterlockedCount nThreadID;
+ ::rtl::Reference< ObservableThread > pThread;
+ css::uno::Reference< css::util::XCancellable > aJob;
+
+ tThreadData()
+ : nThreadID( 0 ),
+ aJob()
+ {}
+ };
+
+ private:
+
+ static const std::deque< tThreadData >::size_type snStartedSize;
+
+ std::mutex maMutex;
+
+ css::uno::WeakReference< css::util::XJobManager > mrThreadJoiner;
+
+ std::shared_ptr< ThreadListener > mpThreadListener;
+
+ oslInterlockedCount mnThreadIDCounter;
+
+ std::deque< tThreadData > maWaitingForStartThreads;
+ std::deque< tThreadData > maStartedThreads;
+
+ Idle maStartNewThreadIdle;
+
+ bool mbStartingOfThreadsSuspended;
+
+ struct ThreadPred
+ {
+ oslInterlockedCount mnThreadID;
+ explicit ThreadPred( oslInterlockedCount nThreadID )
+ : mnThreadID( nThreadID )
+ {}
+
+ bool operator() ( const tThreadData& rThreadData ) const
+ {
+ return rThreadData.nThreadID == mnThreadID;
+ }
+ };
+
+ bool StartWaitingThread();
+
+ bool StartThread( const tThreadData& aThreadData );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dcontact.cxx b/sw/source/core/draw/dcontact.cxx
new file mode 100644
index 000000000..54e156710
--- /dev/null
+++ b/sw/source/core/draw/dcontact.cxx
@@ -0,0 +1,2632 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdviter.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/shapepropertynotifier.hxx>
+#include <drawdoc.hxx>
+#include <fmtornt.hxx>
+#include <viewimp.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtanchr.hxx>
+#include <node.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtfsize.hxx>
+#include <pam.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <frmtool.hxx>
+#include <flyfrm.hxx>
+#include <textboxhelper.hxx>
+#include <frmfmt.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <unodraw.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <doc.hxx>
+#include <hints.hxx>
+#include <txtfrm.hxx>
+#include <frameformats.hxx>
+#include <sortedobjs.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/sdr/contact/viewcontactofvirtobj.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <calbck.hxx>
+#include <algorithm>
+#include <txtfly.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ /** unary function used to find a 'virtual' drawing object anchored at a given frame */
+ struct VirtObjAnchoredAtFramePred
+ {
+ const SwFrame* m_pAnchorFrame;
+
+ // #i26791# - compare with master frame
+ static const SwFrame* FindFrame(const SwFrame* pFrame)
+ {
+ if(!pFrame || !pFrame->IsContentFrame())
+ return pFrame;
+ auto pContentFrame = static_cast<const SwContentFrame*>(pFrame);
+ while(pContentFrame->IsFollow())
+ pContentFrame = pContentFrame->FindMaster();
+ return pContentFrame;
+ }
+
+ VirtObjAnchoredAtFramePred(const SwFrame* pAnchorFrame)
+ : m_pAnchorFrame(FindFrame(pAnchorFrame))
+ {}
+
+ bool operator()(const SwDrawVirtObjPtr& rpDrawVirtObj)
+ {
+ return FindFrame(rpDrawVirtObj->GetAnchorFrame()) == m_pAnchorFrame;
+ }
+ };
+}
+
+void setContextWritingMode(SdrObject* pObj, SwFrame const * pAnchor)
+{
+ if(!pObj || !pAnchor)
+ return;
+ short nWritingDirection =
+ pAnchor->IsVertical() ? text::WritingMode2::TB_RL :
+ pAnchor->IsRightToLeft() ? text::WritingMode2::RL_TB :
+ text::WritingMode2::LR_TB;
+ pObj->SetContextWritingMode(nWritingDirection);
+}
+
+
+/** The Get reverse way: seeks the format to the specified object.
+ * If the object is a SwVirtFlyDrawObj then the format of this
+ * will be acquired.
+ * Otherwise it is just a simple drawing object. This has a
+ * UserCall and is the client of the searched format.
+*/
+SwFrameFormat *FindFrameFormat( SdrObject *pObj )
+{
+ SwFrameFormat* pRetval = nullptr;
+
+ if (SwVirtFlyDrawObj* pFlyDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
+ {
+ pRetval = pFlyDrawObj->GetFormat();
+ }
+ else
+ {
+ SwContact* pContact = GetUserCall(pObj);
+ if ( pContact )
+ {
+ pRetval = pContact->GetFormat();
+ }
+ }
+ return pRetval;
+}
+
+bool HasWrap( const SdrObject* pObj )
+{
+ if ( pObj )
+ {
+ const SwFrameFormat* pFormat = ::FindFrameFormat( pObj );
+ if ( pFormat )
+ {
+ return css::text::WrapTextMode_THROUGH != pFormat->GetSurround().GetSurround();
+ }
+ }
+
+ return false;
+}
+
+/// returns the BoundRect _inclusive_ distance of the object.
+SwRect GetBoundRectOfAnchoredObj( const SdrObject* pObj )
+{
+ SwRect aRet( pObj->GetCurrentBoundRect() );
+ // #i68520# - call cache of <SwAnchoredObject>
+ SwContact* pContact( GetUserCall( pObj ) );
+ if ( pContact )
+ {
+ const SwAnchoredObject* pAnchoredObj( pContact->GetAnchoredObj( pObj ) );
+ if ( pAnchoredObj )
+ {
+ aRet = pAnchoredObj->GetObjRectWithSpaces();
+ }
+ }
+ return aRet;
+}
+
+/// Returns the UserCall if applicable from the group object
+SwContact* GetUserCall( const SdrObject* pObj )
+{
+ SdrObject *pTmp;
+ while ( !pObj->GetUserCall() && nullptr != (pTmp = pObj->getParentSdrObjectFromSdrObject()) )
+ pObj = pTmp;
+ assert((!pObj->GetUserCall() || nullptr != dynamic_cast<const SwContact*>(pObj->GetUserCall())) &&
+ "<::GetUserCall(..)> - wrong type of found object user call." );
+ return static_cast<SwContact*>(pObj->GetUserCall());
+}
+
+/// Returns true if the SrdObject is a Marquee-Object (scrolling text)
+bool IsMarqueeTextObj( const SdrObject& rObj )
+{
+ if (SdrInventor::Default != rObj.GetObjInventor() ||
+ SdrObjKind::Text != rObj.GetObjIdentifier())
+ return false;
+ SdrTextAniKind eTKind = static_cast<const SdrTextObj&>(rObj).GetTextAniKind();
+ return ( SdrTextAniKind::Scroll == eTKind
+ || SdrTextAniKind::Alternate == eTKind || SdrTextAniKind::Slide == eTKind );
+}
+
+SwContact::SwContact( SwFrameFormat *pToRegisterIn ) :
+ SwClient( pToRegisterIn ),
+ mbInDTOR( false )
+{}
+
+SwContact::~SwContact()
+{
+ SetInDTOR();
+}
+
+
+void SwContact::SetInDTOR()
+{
+ mbInDTOR = true;
+}
+
+/// method to move drawing object to corresponding visible layer
+void SwContact::MoveObjToVisibleLayer( SdrObject* _pDrawObj )
+{
+ // #i46297# - notify background about the arriving of
+ // the object and invalidate its position.
+ const bool bNotify( !GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) );
+
+ MoveObjToLayer( true, _pDrawObj );
+
+ // #i46297#
+ if ( !bNotify )
+ return;
+
+ SwAnchoredObject* pAnchoredObj = GetAnchoredObj( _pDrawObj );
+ assert(pAnchoredObj);
+ ::setContextWritingMode( _pDrawObj, pAnchoredObj->GetAnchorFrameContainingAnchPos() );
+ // Note: as-character anchored objects aren't registered at a page frame and
+ // a notification of its background isn't needed.
+ if ( pAnchoredObj->GetPageFrame() )
+ {
+ ::Notify_Background( _pDrawObj, pAnchoredObj->GetPageFrame(),
+ pAnchoredObj->GetObjRect(), PrepareHint::FlyFrameArrive, true );
+ }
+
+ pAnchoredObj->InvalidateObjPos();
+}
+
+/// method to move drawing object to corresponding invisible layer - #i18447#
+void SwContact::MoveObjToInvisibleLayer( SdrObject* _pDrawObj )
+{
+ // #i46297# - notify background about the leaving of the object.
+ const bool bNotify( GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) );
+
+ MoveObjToLayer( false, _pDrawObj );
+
+ // #i46297#
+ if ( bNotify )
+ {
+ SwAnchoredObject* pAnchoredObj = GetAnchoredObj( _pDrawObj );
+ assert(pAnchoredObj);
+ // Note: as-character anchored objects aren't registered at a page frame and
+ // a notification of its background isn't needed.
+ if (pAnchoredObj->GetPageFrame())
+ {
+ ::Notify_Background( _pDrawObj, pAnchoredObj->GetPageFrame(),
+ pAnchoredObj->GetObjRect(), PrepareHint::FlyFrameLeave, true );
+ }
+ }
+}
+
+/** method to move object to visible/invisible layer - #i18447#
+
+ implementation for the public method <MoveObjToVisibleLayer(..)>
+ and <MoveObjToInvisibleLayer(..)>
+*/
+void SwContact::MoveObjToLayer( const bool _bToVisible,
+ SdrObject* _pDrawObj )
+{
+ if ( !_pDrawObj )
+ {
+ OSL_FAIL( "SwDrawContact::MoveObjToLayer(..) - no drawing object!" );
+ return;
+ }
+
+ if ( !GetRegisteredIn() )
+ {
+ OSL_FAIL( "SwDrawContact::MoveObjToLayer(..) - no drawing frame format!" );
+ return;
+ }
+
+ const IDocumentDrawModelAccess& rIDDMA = static_cast<SwFrameFormat*>(GetRegisteredInNonConst())->getIDocumentDrawModelAccess();
+
+ SdrLayerID nToHellLayerId =
+ _bToVisible ? rIDDMA.GetHellId() : rIDDMA.GetInvisibleHellId();
+ SdrLayerID nToHeavenLayerId =
+ _bToVisible ? rIDDMA.GetHeavenId() : rIDDMA.GetInvisibleHeavenId();
+ SdrLayerID nToControlLayerId =
+ _bToVisible ? rIDDMA.GetControlsId() : rIDDMA.GetInvisibleControlsId();
+ SdrLayerID nFromHellLayerId =
+ _bToVisible ? rIDDMA.GetInvisibleHellId() : rIDDMA.GetHellId();
+ SdrLayerID nFromHeavenLayerId =
+ _bToVisible ? rIDDMA.GetInvisibleHeavenId() : rIDDMA.GetHeavenId();
+ SdrLayerID nFromControlLayerId =
+ _bToVisible ? rIDDMA.GetInvisibleControlsId() : rIDDMA.GetControlsId();
+
+ if ( dynamic_cast<const SdrObjGroup*>( _pDrawObj) != nullptr )
+ {
+ // determine layer for group object
+ {
+ // proposed layer of a group object is the hell layer
+ SdrLayerID nNewLayerId = nToHellLayerId;
+ if ( ::CheckControlLayer( _pDrawObj ) )
+ {
+ // it has to be the control layer, if one of the member
+ // is a control
+ nNewLayerId = nToControlLayerId;
+ }
+ else if ( _pDrawObj->GetLayer() == rIDDMA.GetHeavenId() ||
+ _pDrawObj->GetLayer() == rIDDMA.GetInvisibleHeavenId() )
+ {
+ // it has to be the heaven layer, if method <GetLayer()> reveals
+ // a heaven layer
+ nNewLayerId = nToHeavenLayerId;
+ }
+ // set layer at group object, but do *not* broadcast and
+ // no propagation to the members.
+ // Thus, call <NbcSetLayer(..)> at super class
+ _pDrawObj->SdrObject::NbcSetLayer( nNewLayerId );
+ }
+
+ // call method recursively for group object members
+ const SdrObjList* pLst =
+ static_cast<SdrObjGroup*>(_pDrawObj)->GetSubList();
+ if ( pLst )
+ {
+ for ( size_t i = 0; i < pLst->GetObjCount(); ++i )
+ {
+ MoveObjToLayer( _bToVisible, pLst->GetObj( i ) );
+ }
+ }
+ }
+ else
+ {
+ const SdrLayerID nLayerIdOfObj = _pDrawObj->GetLayer();
+ if ( nLayerIdOfObj == nFromHellLayerId )
+ {
+ _pDrawObj->SetLayer( nToHellLayerId );
+ }
+ else if ( nLayerIdOfObj == nFromHeavenLayerId )
+ {
+ _pDrawObj->SetLayer( nToHeavenLayerId );
+ }
+ else if ( nLayerIdOfObj == nFromControlLayerId )
+ {
+ _pDrawObj->SetLayer( nToControlLayerId );
+ }
+ }
+}
+
+/// get minimum order number of anchored objects handled by with contact
+sal_uInt32 SwContact::GetMinOrdNum() const
+{
+ sal_uInt32 nMinOrdNum( SAL_MAX_UINT32 );
+
+ std::vector< SwAnchoredObject* > aObjs;
+ GetAnchoredObjs( aObjs );
+
+ while ( !aObjs.empty() )
+ {
+ sal_uInt32 nTmpOrdNum = aObjs.back()->GetDrawObj()->GetOrdNum();
+
+ if ( nTmpOrdNum < nMinOrdNum )
+ {
+ nMinOrdNum = nTmpOrdNum;
+ }
+
+ aObjs.pop_back();
+ }
+
+ OSL_ENSURE( nMinOrdNum != SAL_MAX_UINT32,
+ "<SwContact::GetMinOrdNum()> - no order number found." );
+ return nMinOrdNum;
+}
+
+/// get maximum order number of anchored objects handled by with contact
+sal_uInt32 SwContact::GetMaxOrdNum() const
+{
+ sal_uInt32 nMaxOrdNum( 0 );
+
+ std::vector< SwAnchoredObject* > aObjs;
+ GetAnchoredObjs( aObjs );
+
+ while ( !aObjs.empty() )
+ {
+ sal_uInt32 nTmpOrdNum = aObjs.back()->GetDrawObj()->GetOrdNum();
+
+ if ( nTmpOrdNum > nMaxOrdNum )
+ {
+ nMaxOrdNum = nTmpOrdNum;
+ }
+
+ aObjs.pop_back();
+ }
+
+ return nMaxOrdNum;
+}
+
+namespace
+{
+ Point lcl_GetWW8Pos(SwAnchoredObject const * pAnchoredObj, const bool bFollowTextFlow, sw::WW8AnchorConv& reConv)
+ {
+ switch(reConv)
+ {
+ case sw::WW8AnchorConv::CONV2PG:
+ {
+ bool bRelToTableCell(false);
+ Point aPos(pAnchoredObj->GetRelPosToPageFrame(bFollowTextFlow, bRelToTableCell));
+ if(bRelToTableCell)
+ reConv = sw::WW8AnchorConv::RELTOTABLECELL;
+ return aPos;
+ }
+ case sw::WW8AnchorConv::CONV2COL_OR_PARA:
+ return pAnchoredObj->GetRelPosToAnchorFrame();
+ case sw::WW8AnchorConv::CONV2CHAR:
+ return pAnchoredObj->GetRelPosToChar();
+ case sw::WW8AnchorConv::CONV2LINE:
+ return pAnchoredObj->GetRelPosToLine();
+ default: ;
+ }
+ return Point();
+ }
+}
+void SwContact::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ // this does not call SwClient::SwClientNotify and thus doesn't handle RES_OBJECTDYING as usual. Is this intentional?
+ if (auto pFindSdrObjectHint = dynamic_cast<const sw::FindSdrObjectHint*>(&rHint))
+ {
+ if(!pFindSdrObjectHint->m_rpObject)
+ pFindSdrObjectHint->m_rpObject = GetMaster();
+ }
+ else if (auto pWW8AnchorConvHint = dynamic_cast<const sw::WW8AnchorConvHint*>(&rHint))
+ {
+ // determine anchored object
+ SwAnchoredObject* pAnchoredObj(nullptr);
+ {
+ std::vector<SwAnchoredObject*> aAnchoredObjs;
+ GetAnchoredObjs(aAnchoredObjs);
+ if(!aAnchoredObjs.empty())
+ pAnchoredObj = aAnchoredObjs.front();
+ }
+ // no anchored object found. Thus, the needed layout information can't
+ // be determined. --> no conversion
+ if(!pAnchoredObj)
+ return;
+ // no conversion for anchored drawing object, which aren't attached to an
+ // anchor frame.
+ // This is the case for drawing objects, which are anchored inside a page
+ // header/footer of an *unused* page style.
+ if(dynamic_cast<SwAnchoredDrawObject*>(pAnchoredObj) && !pAnchoredObj->GetAnchorFrame())
+ return;
+ const bool bFollowTextFlow = static_cast<const SwFrameFormat&>(rMod).GetFollowTextFlow().GetValue();
+ sw::WW8AnchorConvResult& rResult(pWW8AnchorConvHint->m_rResult);
+ // No distinction between layout directions, because of missing
+ // information about WW8 in vertical layout.
+ rResult.m_aPos.setX(lcl_GetWW8Pos(pAnchoredObj, bFollowTextFlow, rResult.m_eHoriConv).getX());
+ rResult.m_aPos.setY(lcl_GetWW8Pos(pAnchoredObj, bFollowTextFlow, rResult.m_eVertConv).getY());
+ rResult.m_bConverted = true;
+ }
+}
+
+
+SwFlyDrawContact::SwFlyDrawContact(
+ SwFlyFrameFormat *pToRegisterIn,
+ SdrModel& rTargetModel)
+: SwContact(pToRegisterIn),
+ mpMasterObj(new SwFlyDrawObj(rTargetModel))
+{
+ // #i26791# - class <SwFlyDrawContact> contains the 'master'
+ // drawing object of type <SwFlyDrawObj> on its own.
+ mpMasterObj->SetOrdNum( 0xFFFFFFFE );
+ mpMasterObj->SetUserCall( this );
+}
+
+SwFlyDrawContact::~SwFlyDrawContact()
+{
+ if ( mpMasterObj )
+ {
+ mpMasterObj->SetUserCall( nullptr );
+ if ( mpMasterObj->getSdrPageFromSdrObject() )
+ mpMasterObj->getSdrPageFromSdrObject()->RemoveObject( mpMasterObj->GetOrdNum() );
+ }
+}
+
+sal_uInt32 SwFlyDrawContact::GetOrdNumForNewRef(const SwFlyFrame* pFly,
+ SwFrame const& rAnchorFrame)
+{
+ // maintain invariant that a shape's textbox immediately follows the shape
+ // also for the multiple SdrVirtObj created for shapes in header/footer
+ if (SwFrameFormat const*const pDrawFormat =
+ SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT))
+ {
+ // assume that the draw SdrVirtObj is always created before the flyframe one
+ if (SwSortedObjs const*const pObjs = rAnchorFrame.GetDrawObjs())
+ {
+ for (SwAnchoredObject const*const pAnchoredObj : *pObjs)
+ {
+ if (&pAnchoredObj->GetFrameFormat() == pDrawFormat)
+ {
+ return pAnchoredObj->GetDrawObj()->GetOrdNum() + 1;
+ }
+ }
+ }
+ // if called from AppendObjs(), this is a problem; if called from lcl_SetFlyFrameAttr() it's not
+ SAL_INFO("sw", "GetOrdNumForNewRef: cannot find SdrObject for text box's shape");
+ }
+ // search for another Writer fly frame registered at same frame format
+ SwIterator<SwFlyFrame,SwFormat> aIter(*GetFormat());
+ const SwFlyFrame* pFlyFrame(nullptr);
+ for(pFlyFrame = aIter.First(); pFlyFrame; pFlyFrame = aIter.Next())
+ {
+ if(pFlyFrame != pFly)
+ break;
+ }
+
+ if(pFlyFrame)
+ {
+ // another Writer fly frame found. Take its order number
+ return pFlyFrame->GetVirtDrawObj()->GetOrdNum();
+ }
+ // no other Writer fly frame found. Take order number of 'master' object
+ // #i35748# - use method <GetOrdNumDirect()> instead
+ // of method <GetOrdNum()> to avoid a recalculation of the order number,
+ // which isn't intended.
+ return GetMaster()->GetOrdNumDirect();
+}
+
+SwVirtFlyDrawObj* SwFlyDrawContact::CreateNewRef(SwFlyFrame* pFly,
+ SwFlyFrameFormat* pFormat, SwFrame const& rAnchorFrame)
+{
+ // Find ContactObject from the Format. If there's already one, we just
+ // need to create a new Ref, else we create the Contact now.
+
+ IDocumentDrawModelAccess& rIDDMA = pFormat->getIDocumentDrawModelAccess();
+ SwFlyDrawContact* pContact = pFormat->GetOrCreateContact();
+ SwVirtFlyDrawObj* pDrawObj(
+ new SwVirtFlyDrawObj(
+ pContact->GetMaster()->getSdrModelFromSdrObject(),
+ *pContact->GetMaster(),
+ pFly));
+ pDrawObj->SetUserCall(pContact);
+
+ // The Reader creates the Masters and inserts them into the Page in
+ // order to transport the z-order.
+ // After creating the first Reference the Masters are removed from the
+ // List and are not important anymore.
+ SdrPage* pPg = pContact->GetMaster()->getSdrPageFromSdrObject();
+ if(nullptr != pPg)
+ {
+ const size_t nOrdNum = pContact->GetMaster()->GetOrdNum();
+ pPg->ReplaceObject(pDrawObj, nOrdNum);
+ }
+ // #i27030# - insert new <SwVirtFlyDrawObj> instance
+ // into drawing page with correct order number
+ else
+ rIDDMA.GetDrawModel()->GetPage(0)->InsertObject(pDrawObj, pContact->GetOrdNumForNewRef(pFly, rAnchorFrame));
+ // #i38889# - assure, that new <SwVirtFlyDrawObj> instance
+ // is in a visible layer.
+ pContact->MoveObjToVisibleLayer(pDrawObj);
+ return pDrawObj;
+}
+
+// #i26791#
+const SwAnchoredObject* SwFlyDrawContact::GetAnchoredObj(const SdrObject* pSdrObj) const
+{
+ assert(pSdrObj);
+ assert(dynamic_cast<const SwVirtFlyDrawObj*>(pSdrObj) != nullptr);
+ assert(GetUserCall(pSdrObj) == this &&
+ "<SwFlyDrawContact::GetAnchoredObj(..)> - provided object doesn't belong to this contact");
+
+ const SwAnchoredObject *const pRetAnchoredObj =
+ static_cast<const SwVirtFlyDrawObj*>(pSdrObj)->GetFlyFrame();
+
+ return pRetAnchoredObj;
+}
+
+SwAnchoredObject* SwFlyDrawContact::GetAnchoredObj(SdrObject *const pSdrObj)
+{
+ return const_cast<SwAnchoredObject *>(const_cast<SwFlyDrawContact const*>(this)->GetAnchoredObj(pSdrObj));
+}
+
+SdrObject* SwFlyDrawContact::GetMaster()
+{
+ return mpMasterObj.get();
+}
+
+/**
+ * @note Overriding method to control Writer fly frames, which are linked, and
+ * to assure that all objects anchored at/inside the Writer fly frame are
+ * also made visible.
+ */
+void SwFlyDrawContact::MoveObjToVisibleLayer( SdrObject* _pDrawObj )
+{
+ assert(dynamic_cast<const SwVirtFlyDrawObj*>(_pDrawObj) != nullptr);
+
+ if ( GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) )
+ {
+ // nothing to do
+ return;
+ }
+
+ SwFlyFrame* pFlyFrame = static_cast<SwVirtFlyDrawObj*>(_pDrawObj)->GetFlyFrame();
+
+ // #i44464# - consider, that Writer fly frame content
+ // already exists - (e.g. WW8 document is inserted into an existing document).
+ if ( !pFlyFrame->Lower() )
+ {
+ pFlyFrame->InsertColumns();
+ pFlyFrame->Chain( pFlyFrame->AnchorFrame() );
+ pFlyFrame->InsertCnt();
+ }
+ if ( pFlyFrame->GetDrawObjs() )
+ {
+ for (SwAnchoredObject* i : *pFlyFrame->GetDrawObjs())
+ {
+ // #i28701# - consider type of objects in sorted object list.
+ SdrObject* pObj = i->DrawObj();
+ SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
+ pContact->MoveObjToVisibleLayer( pObj );
+ }
+ }
+
+ // make fly frame visible
+ SwContact::MoveObjToVisibleLayer( _pDrawObj );
+}
+
+/**
+ * @note Override method to control Writer fly frames, which are linked, and
+ * to assure that all objects anchored at/inside the Writer fly frame are
+ * also made invisible.
+ */
+void SwFlyDrawContact::MoveObjToInvisibleLayer( SdrObject* _pDrawObj )
+{
+ assert(dynamic_cast<const SwVirtFlyDrawObj*>(_pDrawObj) != nullptr);
+
+ if ( !GetFormat()->getIDocumentDrawModelAccess().IsVisibleLayerId( _pDrawObj->GetLayer() ) )
+ {
+ // nothing to do
+ return;
+ }
+
+ SwFlyFrame* pFlyFrame = static_cast<SwVirtFlyDrawObj*>(_pDrawObj)->GetFlyFrame();
+
+ pFlyFrame->Unchain();
+ pFlyFrame->DeleteCnt();
+ if ( pFlyFrame->GetDrawObjs() )
+ {
+ for (SwAnchoredObject* i : *pFlyFrame->GetDrawObjs())
+ {
+ // #i28701# - consider type of objects in sorted object list.
+ SdrObject* pObj = i->DrawObj();
+ SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
+ pContact->MoveObjToInvisibleLayer( pObj );
+ }
+ }
+
+ // make fly frame invisible
+ SwContact::MoveObjToInvisibleLayer( _pDrawObj );
+}
+
+/// get data collection of anchored objects, handled by with contact
+void SwFlyDrawContact::GetAnchoredObjs( std::vector<SwAnchoredObject*>& _roAnchoredObjs ) const
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ SwFlyFrame::GetAnchoredObjects( _roAnchoredObjs, *pFormat );
+}
+void SwFlyDrawContact::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ SwContact::SwClientNotify(rMod, rHint);
+ if(auto pGetZOrdnerHint = dynamic_cast<const sw::GetZOrderHint*>(&rHint))
+ {
+ // #i11176#
+ // This also needs to work when no layout exists. Thus, for
+ // FlyFrames an alternative method is used now in that case.
+ auto pFormat(dynamic_cast<const SwFrameFormat*>(&rMod));
+ if (pFormat && pFormat->Which() == RES_FLYFRMFMT && !pFormat->getIDocumentLayoutAccess().GetCurrentViewShell())
+ pGetZOrdnerHint->m_rnZOrder = GetMaster()->GetOrdNum();
+ }
+}
+
+// SwDrawContact
+
+bool CheckControlLayer( const SdrObject *pObj )
+{
+ if ( SdrInventor::FmForm == pObj->GetObjInventor() )
+ return true;
+ if (const SdrObjGroup *pObjGroup = dynamic_cast<const SdrObjGroup*>(pObj))
+ {
+ const SdrObjList *pLst = pObjGroup->GetSubList();
+ for ( size_t i = 0; i < pLst->GetObjCount(); ++i )
+ {
+ if ( ::CheckControlLayer( pLst->GetObj( i ) ) )
+ {
+ // #i18447# - return correct value ;-)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SwDrawContact::SwDrawContact( SwFrameFormat* pToRegisterIn, SdrObject* pObj ) :
+ SwContact( pToRegisterIn ),
+ mbMasterObjCleared( false ),
+ mbDisconnectInProgress( false ),
+ mbUserCallActive( false ),
+ // Note: value of <meEventTypeOfCurrentUserCall> isn't of relevance, because
+ // <mbUserCallActive> is false.
+ meEventTypeOfCurrentUserCall( SdrUserCallType::MoveOnly )
+{
+ // --> #i33909# - assure, that drawing object is inserted
+ // in the drawing page.
+ if ( !pObj->IsInserted() )
+ {
+ pToRegisterIn->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)->
+ InsertObject( pObj, pObj->GetOrdNumDirect() );
+ }
+
+ // Controls have to be always in the Control-Layer. This is also true for
+ // group objects, if they contain controls.
+ if ( ::CheckControlLayer( pObj ) )
+ {
+ // set layer of object to corresponding invisible layer.
+ pObj->SetLayer( pToRegisterIn->getIDocumentDrawModelAccess().GetInvisibleControlsId() );
+ }
+
+ // #i26791#
+ pObj->SetUserCall( this );
+ maAnchoredDrawObj.SetDrawObj( *pObj );
+
+ // if there already exists an SwXShape for the object, ensure it knows about us, and the SdrObject
+ // #i99056#
+ SwXShape::AddExistingShapeToFormat( *pObj );
+}
+
+SwDrawContact::~SwDrawContact()
+{
+ SetInDTOR();
+
+ DisconnectFromLayout();
+
+ // remove 'master' from drawing page
+ RemoveMasterFromDrawPage();
+
+ // remove and destroy 'virtual' drawing objects.
+ RemoveAllVirtObjs();
+
+ if ( !mbMasterObjCleared )
+ {
+ SdrObject* pObject = const_cast< SdrObject* >( maAnchoredDrawObj.GetDrawObj() );
+ SdrObject::Free( pObject );
+ }
+}
+
+void SwDrawContact::GetTextObjectsFromFormat(std::list<SdrTextObj*>& o_rTextObjects, SwDoc& rDoc)
+{
+ for(auto& rpFly : *rDoc.GetSpzFrameFormats())
+ {
+ if(dynamic_cast<const SwDrawFrameFormat*>(rpFly))
+ rpFly->CallSwClientNotify(sw::CollectTextObjectsHint(o_rTextObjects));
+ }
+}
+
+// #i26791#
+const SwAnchoredObject* SwDrawContact::GetAnchoredObj(const SdrObject* pSdrObj ) const
+{
+ // handle default parameter value
+ if (!pSdrObj)
+ {
+ pSdrObj = GetMaster();
+ }
+
+ assert(pSdrObj);
+ assert(dynamic_cast<const SwDrawVirtObj*>(pSdrObj) != nullptr ||
+ dynamic_cast<const SdrVirtObj*>(pSdrObj) == nullptr);
+ assert((GetUserCall(pSdrObj) == this ||
+ pSdrObj == GetMaster()) &&
+ "<SwDrawContact::GetAnchoredObj(..)> - provided object doesn't belongs to this contact" );
+
+ const SwAnchoredObject* pRetAnchoredObj = nullptr;
+
+ if (auto pVirtObj = dynamic_cast<const SwDrawVirtObj*>(pSdrObj))
+ {
+ pRetAnchoredObj = &(pVirtObj->GetAnchoredObj());
+ }
+ else
+ {
+ assert(dynamic_cast<const SdrVirtObj*>(pSdrObj) == nullptr);
+ pRetAnchoredObj = &maAnchoredDrawObj;
+ }
+
+ return pRetAnchoredObj;
+}
+
+SwAnchoredObject* SwDrawContact::GetAnchoredObj(SdrObject *const pSdrObj)
+{
+ return const_cast<SwAnchoredObject*>(const_cast<SwDrawContact const*>(this)->GetAnchoredObj(pSdrObj));
+}
+
+SdrObject* SwDrawContact::GetMaster()
+{
+ return !mbMasterObjCleared
+ ? maAnchoredDrawObj.DrawObj()
+ : nullptr;
+}
+
+const SwFrame* SwDrawContact::GetAnchorFrame( const SdrObject* _pDrawObj ) const
+{
+ const SwFrame* pAnchorFrame = nullptr;
+ if ( !_pDrawObj ||
+ _pDrawObj == GetMaster() ||
+ ( !_pDrawObj->GetUserCall() &&
+ GetUserCall( _pDrawObj ) == this ) )
+ {
+ pAnchorFrame = maAnchoredDrawObj.GetAnchorFrame();
+ }
+ else
+ {
+ assert(dynamic_cast<SwDrawVirtObj const*>(_pDrawObj) != nullptr);
+ pAnchorFrame = static_cast<const SwDrawVirtObj*>(_pDrawObj)->GetAnchorFrame();
+ }
+
+ return pAnchorFrame;
+}
+
+SwFrame* SwDrawContact::GetAnchorFrame(SdrObject const *const pDrawObj)
+{
+ return const_cast<SwFrame *>(const_cast<SwDrawContact const*>(this)->GetAnchorFrame(pDrawObj));
+}
+
+/** add a 'virtual' drawing object to drawing page.
+ */
+SwDrawVirtObj* SwDrawContact::AddVirtObj(SwFrame const& rAnchorFrame)
+{
+ maDrawVirtObjs.push_back(
+ SwDrawVirtObjPtr(
+ new SwDrawVirtObj(
+ GetMaster()->getSdrModelFromSdrObject(),
+ *GetMaster(),
+ *this)));
+ maDrawVirtObjs.back()->AddToDrawingPage(rAnchorFrame);
+ return maDrawVirtObjs.back().get();
+}
+
+/// remove 'virtual' drawing objects and destroy them.
+void SwDrawContact::RemoveAllVirtObjs()
+{
+ for(auto& rpDrawVirtObj : maDrawVirtObjs)
+ {
+ // remove and destroy 'virtual object'
+ rpDrawVirtObj->RemoveFromWriterLayout();
+ rpDrawVirtObj->RemoveFromDrawingPage();
+ }
+ maDrawVirtObjs.clear();
+}
+
+
+/// get drawing object ('master' or 'virtual') by frame.
+SdrObject* SwDrawContact::GetDrawObjectByAnchorFrame( const SwFrame& _rAnchorFrame )
+{
+ SdrObject* pRetDrawObj = nullptr;
+
+ // #i26791# - compare master frames instead of direct frames
+ const SwFrame* pProposedAnchorFrame = &_rAnchorFrame;
+ if ( pProposedAnchorFrame->IsContentFrame() )
+ {
+ const SwContentFrame* pTmpFrame =
+ static_cast<const SwContentFrame*>( pProposedAnchorFrame );
+ while ( pTmpFrame->IsFollow() )
+ {
+ pTmpFrame = pTmpFrame->FindMaster();
+ }
+ pProposedAnchorFrame = pTmpFrame;
+ }
+
+ const SwFrame* pMasterObjAnchorFrame = GetAnchorFrame();
+ if ( pMasterObjAnchorFrame && pMasterObjAnchorFrame->IsContentFrame() )
+ {
+ const SwContentFrame* pTmpFrame =
+ static_cast<const SwContentFrame*>( pMasterObjAnchorFrame );
+ while ( pTmpFrame->IsFollow() )
+ {
+ pTmpFrame = pTmpFrame->FindMaster();
+ }
+ pMasterObjAnchorFrame = pTmpFrame;
+ }
+
+ if ( pMasterObjAnchorFrame && pMasterObjAnchorFrame == pProposedAnchorFrame )
+ {
+ pRetDrawObj = GetMaster();
+ }
+ else
+ {
+ const auto ppFoundVirtObj(std::find_if(maDrawVirtObjs.begin(), maDrawVirtObjs.end(),
+ VirtObjAnchoredAtFramePred(pProposedAnchorFrame)));
+ if(ppFoundVirtObj != maDrawVirtObjs.end())
+ pRetDrawObj = ppFoundVirtObj->get();
+ }
+
+ return pRetDrawObj;
+}
+
+void SwDrawContact::NotifyBackgroundOfAllVirtObjs(const tools::Rectangle* pOldBoundRect)
+{
+ for(const auto& rpDrawVirtObj : maDrawVirtObjs)
+ {
+ SwDrawVirtObj* pDrawVirtObj(rpDrawVirtObj.get());
+ if ( pDrawVirtObj->GetAnchorFrame() )
+ {
+ // #i34640# - determine correct page frame
+ SwPageFrame* pPage = pDrawVirtObj->AnchoredObj().FindPageFrameOfAnchor();
+ if( pOldBoundRect && pPage )
+ {
+ SwRect aOldRect( *pOldBoundRect );
+ aOldRect.Pos() += pDrawVirtObj->GetOffset();
+ if( aOldRect.HasArea() )
+ ::Notify_Background( pDrawVirtObj, pPage,
+ aOldRect, PrepareHint::FlyFrameLeave,true);
+ }
+ // #i34640# - include spacing for wrapping
+ SwRect aRect( pDrawVirtObj->GetAnchoredObj().GetObjRectWithSpaces() );
+ if (aRect.HasArea() && pPage)
+ {
+ SwPageFrame* pPg = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aRect, pPage )));
+ if ( pPg )
+ ::Notify_Background( pDrawVirtObj, pPg, aRect,
+ PrepareHint::FlyFrameArrive, true );
+ }
+ ::ClrContourCache( pDrawVirtObj );
+ }
+ }
+}
+
+/// local method to notify the background for a drawing object - #i26791#
+static void lcl_NotifyBackgroundOfObj( SwDrawContact const & _rDrawContact,
+ const SdrObject& _rObj,
+ const tools::Rectangle* _pOldObjRect )
+{
+ // #i34640#
+ SwAnchoredObject* pAnchoredObj =
+ const_cast<SwAnchoredObject*>(_rDrawContact.GetAnchoredObj( &_rObj ));
+ if ( !(pAnchoredObj && pAnchoredObj->GetAnchorFrame()) )
+ return;
+
+ // #i34640# - determine correct page frame
+ SwPageFrame* pPageFrame = pAnchoredObj->FindPageFrameOfAnchor();
+ if( _pOldObjRect && pPageFrame )
+ {
+ SwRect aOldRect( *_pOldObjRect );
+ if( aOldRect.HasArea() )
+ {
+ // #i34640# - determine correct page frame
+ SwPageFrame* pOldPageFrame = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aOldRect, pPageFrame )));
+ ::Notify_Background( &_rObj, pOldPageFrame, aOldRect,
+ PrepareHint::FlyFrameLeave, true);
+ }
+ }
+ // #i34640# - include spacing for wrapping
+ SwRect aNewRect( pAnchoredObj->GetObjRectWithSpaces() );
+ if( aNewRect.HasArea() && pPageFrame )
+ {
+ pPageFrame = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aNewRect, pPageFrame )));
+ ::Notify_Background( &_rObj, pPageFrame, aNewRect,
+ PrepareHint::FlyFrameArrive, true );
+ }
+ ClrContourCache( &_rObj );
+}
+
+void SwDrawContact::Changed( const SdrObject& rObj,
+ SdrUserCallType eType,
+ const tools::Rectangle& rOldBoundRect )
+{
+ // #i26791# - no event handling, if existing <SwViewShell>
+ // is in construction
+ SwDoc* pDoc = GetFormat()->GetDoc();
+ if ( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() &&
+ pDoc->getIDocumentLayoutAccess().GetCurrentViewShell()->IsInConstructor() )
+ {
+ return;
+ }
+
+ // #i44339#
+ // no event handling, if document is in destruction.
+ // Exception: It's the SdrUserCallType::Delete event
+ if ( pDoc->IsInDtor() && eType != SdrUserCallType::Delete )
+ {
+ return;
+ }
+
+ //Put on Action, but not if presently anywhere an action runs.
+ bool bHasActions(true);
+ SwRootFrame *pTmpRoot = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if ( pTmpRoot && pTmpRoot->IsCallbackActionEnabled() )
+ {
+ SwViewShell* const pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( pSh )
+ {
+ for(SwViewShell& rShell : pSh->GetRingContainer() )
+ {
+ if ( rShell.Imp()->IsAction() || rShell.Imp()->IsIdleAction() )
+ {
+ bHasActions = true;
+ break;
+ }
+ bHasActions = false;
+ }
+ }
+ if(!bHasActions)
+ pTmpRoot->StartAllAction();
+ }
+ SdrObjUserCall::Changed( rObj, eType, rOldBoundRect );
+ Changed_( rObj, eType, &rOldBoundRect ); //Attention, possibly suicidal!
+
+ if(!bHasActions)
+ pTmpRoot->EndAllAction();
+}
+
+/// helper class for method <SwDrawContact::Changed_(..)> for handling nested
+/// <SdrObjUserCall> events
+class NestedUserCallHdl
+{
+ private:
+ SwDrawContact* mpDrawContact;
+ bool mbParentUserCallActive;
+ SdrUserCallType meParentUserCallEventType;
+
+ public:
+ NestedUserCallHdl( SwDrawContact* _pDrawContact,
+ SdrUserCallType _eEventType )
+ : mpDrawContact( _pDrawContact ),
+ mbParentUserCallActive( _pDrawContact->mbUserCallActive ),
+ meParentUserCallEventType( _pDrawContact->meEventTypeOfCurrentUserCall )
+ {
+ mpDrawContact->mbUserCallActive = true;
+ mpDrawContact->meEventTypeOfCurrentUserCall = _eEventType;
+ }
+
+ ~NestedUserCallHdl()
+ {
+ if ( mpDrawContact )
+ {
+ mpDrawContact->mbUserCallActive = mbParentUserCallActive;
+ mpDrawContact->meEventTypeOfCurrentUserCall = meParentUserCallEventType;
+ }
+ }
+
+ void DrawContactDeleted()
+ {
+ mpDrawContact = nullptr;
+ }
+
+ bool IsNestedUserCall() const
+ {
+ return mbParentUserCallActive;
+ }
+
+ void AssertNestedUserCall()
+ {
+ if ( !IsNestedUserCall() )
+ return;
+
+ bool bTmpAssert( true );
+ // Currently its known, that a nested event SdrUserCallType::Resize
+ // could occur during parent user call SdrUserCallType::Inserted,
+ // SdrUserCallType::Delete and SdrUserCallType::Resize for edge objects.
+ // Also possible are nested SdrUserCallType::ChildResize events for
+ // edge objects
+ // Thus, assert all other combinations
+ if ( ( meParentUserCallEventType == SdrUserCallType::Inserted ||
+ meParentUserCallEventType == SdrUserCallType::Delete ||
+ meParentUserCallEventType == SdrUserCallType::Resize ) &&
+ mpDrawContact->meEventTypeOfCurrentUserCall == SdrUserCallType::Resize )
+ {
+ bTmpAssert = false;
+ }
+ else if ( meParentUserCallEventType == SdrUserCallType::ChildResize &&
+ mpDrawContact->meEventTypeOfCurrentUserCall == SdrUserCallType::ChildResize )
+ {
+ bTmpAssert = false;
+ }
+
+ if ( bTmpAssert )
+ {
+ OSL_FAIL( "<SwDrawContact::Changed_(..)> - unknown nested <UserCall> event. This is serious." );
+ }
+ }
+};
+
+/// Notify the format's textbox that it should reconsider its position / size.
+static void lcl_textBoxSizeNotify(SwFrameFormat* pFormat)
+{
+ if (SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT))
+ {
+ // Just notify the textbox that the size has changed, the actual object size is not interesting.
+ SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE> aResizeSet(pFormat->GetDoc()->GetAttrPool());
+ SwFormatFrameSize aSize;
+ aResizeSet.Put(aSize);
+ SwTextBoxHelper::syncFlyFrameAttr(*pFormat, aResizeSet, pFormat->FindRealSdrObject());
+ }
+}
+
+// !!!ATTENTION!!! The object may commit suicide!!!
+
+void SwDrawContact::Changed_( const SdrObject& rObj,
+ SdrUserCallType eType,
+ const tools::Rectangle* pOldBoundRect )
+{
+ // suppress handling of nested <SdrObjUserCall> events
+ NestedUserCallHdl aNestedUserCallHdl( this, eType );
+ if ( aNestedUserCallHdl.IsNestedUserCall() )
+ {
+ aNestedUserCallHdl.AssertNestedUserCall();
+ return;
+ }
+ // do *not* notify, if document is destructing
+ // #i35912# - do *not* notify for as-character anchored
+ // drawing objects.
+ // #i35007#
+ // improvement: determine as-character anchored object flag only once.
+ const bool bAnchoredAsChar = ObjAnchoredAsChar();
+ const bool bNotify = !(GetFormat()->GetDoc()->IsInDtor()) &&
+ ( css::text::WrapTextMode_THROUGH != GetFormat()->GetSurround().GetSurround() ) &&
+ !bAnchoredAsChar;
+ switch( eType )
+ {
+ case SdrUserCallType::Delete:
+ {
+ if ( bNotify )
+ {
+ lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect );
+ // --> #i36181# - background of 'virtual'
+ // drawing objects have also been notified.
+ NotifyBackgroundOfAllVirtObjs( pOldBoundRect );
+ }
+ DisconnectFromLayout( false );
+ mbMasterObjCleared = true;
+ delete this;
+ // --> #i65784# Prevent memory corruption
+ aNestedUserCallHdl.DrawContactDeleted();
+ break;
+ }
+ case SdrUserCallType::Inserted:
+ {
+ if ( mbDisconnectInProgress )
+ {
+ OSL_FAIL( "<SwDrawContact::Changed_(..)> - Insert event during disconnection from layout is invalid." );
+ }
+ else
+ {
+ ConnectToLayout();
+ if ( bNotify )
+ {
+ lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect );
+ }
+ }
+ break;
+ }
+ case SdrUserCallType::Removed:
+ {
+ if ( bNotify )
+ {
+ lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect );
+ }
+ DisconnectFromLayout( false );
+ break;
+ }
+ case SdrUserCallType::ChildInserted :
+ case SdrUserCallType::ChildRemoved :
+ {
+ // --> #i113730#
+ // force layer of controls for group objects containing control objects
+ if(dynamic_cast< SdrObjGroup* >(maAnchoredDrawObj.DrawObj()))
+ {
+ if(::CheckControlLayer(maAnchoredDrawObj.DrawObj()))
+ {
+ const IDocumentDrawModelAccess& rIDDMA = static_cast<SwFrameFormat*>(GetRegisteredInNonConst())->getIDocumentDrawModelAccess();
+ const SdrLayerID aCurrentLayer(maAnchoredDrawObj.DrawObj()->GetLayer());
+ const SdrLayerID aControlLayerID(rIDDMA.GetControlsId());
+ const SdrLayerID aInvisibleControlLayerID(rIDDMA.GetInvisibleControlsId());
+
+ if(aCurrentLayer != aControlLayerID && aCurrentLayer != aInvisibleControlLayerID)
+ {
+ if ( aCurrentLayer == rIDDMA.GetInvisibleHellId() ||
+ aCurrentLayer == rIDDMA.GetInvisibleHeavenId() )
+ {
+ maAnchoredDrawObj.DrawObj()->SetLayer(aInvisibleControlLayerID);
+ }
+ else
+ {
+ maAnchoredDrawObj.DrawObj()->SetLayer(aControlLayerID);
+ }
+ }
+ }
+ }
+ [[fallthrough]];
+ }
+ case SdrUserCallType::MoveOnly:
+ case SdrUserCallType::Resize:
+ case SdrUserCallType::ChildMoveOnly :
+ case SdrUserCallType::ChildResize :
+ case SdrUserCallType::ChildChangeAttr :
+ case SdrUserCallType::ChildDelete :
+ {
+ // #i31698# - improvement
+ // get instance <SwAnchoredDrawObject> only once
+ const SwAnchoredDrawObject* pAnchoredDrawObj =
+ static_cast<const SwAnchoredDrawObject*>( GetAnchoredObj( &rObj ) );
+
+ /* protect against NULL pointer dereferencing */
+ if(!pAnchoredDrawObj)
+ {
+ break;
+ }
+
+ // #i26791# - adjust positioning and alignment attributes,
+ // if positioning of drawing object isn't in progress.
+ // #i53320# - no adjust of positioning attributes,
+ // if drawing object isn't positioned.
+ if ( !pAnchoredDrawObj->IsPositioningInProgress() &&
+ !pAnchoredDrawObj->NotYetPositioned() )
+ {
+ // #i34748# - If no last object rectangle is
+ // provided by the anchored object, use parameter <pOldBoundRect>.
+ const tools::Rectangle& aOldObjRect = pAnchoredDrawObj->GetLastObjRect()
+ ? *(pAnchoredDrawObj->GetLastObjRect())
+ : *pOldBoundRect;
+ // #i79400#
+ // always invalidate object rectangle inclusive spaces
+ pAnchoredDrawObj->InvalidateObjRectWithSpaces();
+ // #i41324# - notify background before
+ // adjusting position
+ if ( bNotify )
+ {
+ // #i31573# - correction
+ // background of given drawing object.
+ lcl_NotifyBackgroundOfObj( *this, rObj, &aOldObjRect );
+ }
+ // #i31698# - determine layout direction
+ // via draw frame format.
+ SwFrameFormat::tLayoutDir eLayoutDir =
+ pAnchoredDrawObj->GetFrameFormat().GetLayoutDir();
+ // use geometry of drawing object
+ tools::Rectangle aObjRect( rObj.GetSnapRect() );
+ // If drawing object is a member of a group, the adjustment
+ // of the positioning and the alignment attributes has to
+ // be done for the top group object.
+ if ( rObj.getParentSdrObjectFromSdrObject() )
+ {
+ const SdrObject* pGroupObj = rObj.getParentSdrObjectFromSdrObject();
+ while ( pGroupObj->getParentSdrObjectFromSdrObject() )
+ {
+ pGroupObj = pGroupObj->getParentSdrObjectFromSdrObject();
+ }
+ // use geometry of drawing object
+ aObjRect = pGroupObj->GetSnapRect();
+
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(&SwTextBoxHelper::changeAnchor, GetFormat(), &const_cast<SdrObject&>(rObj));
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(&SwTextBoxHelper::syncTextBoxSize, GetFormat(), &const_cast<SdrObject&>(rObj));
+
+ }
+ SwTwips nXPosDiff(0);
+ SwTwips nYPosDiff(0);
+ switch ( eLayoutDir )
+ {
+ case SwFrameFormat::HORI_L2R:
+ {
+ nXPosDiff = aObjRect.Left() - aOldObjRect.Left();
+ nYPosDiff = aObjRect.Top() - aOldObjRect.Top();
+ }
+ break;
+ case SwFrameFormat::HORI_R2L:
+ {
+ nXPosDiff = aOldObjRect.Right() - aObjRect.Right();
+ nYPosDiff = aObjRect.Top() - aOldObjRect.Top();
+ }
+ break;
+ case SwFrameFormat::VERT_R2L:
+ {
+ nXPosDiff = aObjRect.Top() - aOldObjRect.Top();
+ nYPosDiff = aOldObjRect.Right() - aObjRect.Right();
+ }
+ break;
+ default:
+ {
+ assert(!"<SwDrawContact::Changed_(..)> - unsupported layout direction");
+ }
+ }
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aSet( GetFormat()->GetDoc()->GetAttrPool() );
+ const SwFormatVertOrient& rVert = GetFormat()->GetVertOrient();
+ if ( nYPosDiff != 0 )
+ {
+ if ( rVert.GetRelationOrient() == text::RelOrientation::CHAR ||
+ rVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ nYPosDiff = -nYPosDiff;
+ }
+ aSet.Put( SwFormatVertOrient( rVert.GetPos()+nYPosDiff,
+ text::VertOrientation::NONE,
+ rVert.GetRelationOrient() ) );
+ }
+
+ const SwFormatHoriOrient& rHori = GetFormat()->GetHoriOrient();
+ if ( !bAnchoredAsChar && nXPosDiff != 0 )
+ {
+ aSet.Put( SwFormatHoriOrient( rHori.GetPos()+nXPosDiff,
+ text::HoriOrientation::NONE,
+ rHori.GetRelationOrient() ) );
+ }
+
+ if ( nYPosDiff ||
+ ( !bAnchoredAsChar && nXPosDiff != 0 ) )
+ {
+ GetFormat()->GetDoc()->SetFlyFrameAttr( *(GetFormat()), aSet );
+ // keep new object rectangle, to avoid multiple
+ // changes of the attributes by multiple event from
+ // the drawing layer - e.g. group objects and its members
+ // #i34748# - use new method
+ // <SwAnchoredDrawObject::SetLastObjRect(..)>.
+ const_cast<SwAnchoredDrawObject*>(pAnchoredDrawObj)
+ ->SetLastObjRect( aObjRect );
+ }
+ else if ( aObjRect.GetSize() != aOldObjRect.GetSize() )
+ {
+ InvalidateObjs_();
+ // #i35007# - notify anchor frame
+ // of as-character anchored object
+ if ( bAnchoredAsChar )
+ {
+ SwFrame* pAnchorFrame = const_cast<SwAnchoredDrawObject*>(pAnchoredDrawObj)->AnchorFrame();
+ if(pAnchorFrame)
+ {
+ pAnchorFrame->Prepare( PrepareHint::FlyFrameAttributesChanged, GetFormat() );
+ }
+ }
+
+ lcl_textBoxSizeNotify(GetFormat());
+ }
+ else if (eType == SdrUserCallType::Resize)
+ // Even if the bounding box of the shape didn't change,
+ // notify about the size change, as an adjustment change
+ // may affect the size of the underlying textbox.
+ lcl_textBoxSizeNotify(GetFormat());
+ }
+
+ // tdf#135198: keep text box together with its shape
+ const SwPageFrame* rPageFrame = pAnchoredDrawObj->GetPageFrame();
+ if (rPageFrame && rPageFrame->isFrameAreaPositionValid() && GetFormat()
+ && GetFormat()->GetOtherTextBoxFormats())
+ {
+ SwDoc* const pDoc = GetFormat()->GetDoc();
+
+ // avoid Undo creation
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+
+ // hide any artificial "changes" made by synchronizing the textbox position
+ const bool bEnableSetModified = pDoc->getIDocumentState().IsEnableSetModified();
+ pDoc->getIDocumentState().SetEnableSetModified(false);
+
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT, RES_ANCHOR, RES_ANCHOR>
+ aSyncSet( pDoc->GetAttrPool() );
+ aSyncSet.Put(GetFormat()->GetHoriOrient());
+ bool bRelToTableCell(false);
+ aSyncSet.Put(SwFormatVertOrient(pAnchoredDrawObj->GetRelPosToPageFrame(false, bRelToTableCell).getY(),
+ text::VertOrientation::NONE,
+ text::RelOrientation::PAGE_FRAME));
+ aSyncSet.Put(SwFormatAnchor(RndStdIds::FLY_AT_PAGE, rPageFrame->GetPhyPageNum()));
+
+ auto pSdrObj = const_cast<SdrObject*>(&rObj);
+ if (pSdrObj != GetFormat()->FindRealSdrObject())
+ {
+ SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE> aSet( pDoc->GetAttrPool() );
+
+ aSet.Put(aSyncSet);
+ aSet.Put(pSdrObj->GetMergedItem(RES_FRM_SIZE));
+ SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSet, pSdrObj);
+
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ &SwTextBoxHelper::changeAnchor, GetFormat(),
+ GetFormat()->FindRealSdrObject());
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ &SwTextBoxHelper::syncTextBoxSize, GetFormat(),
+ GetFormat()->FindRealSdrObject());
+ }
+ else
+ SwTextBoxHelper::syncFlyFrameAttr(*GetFormat(), aSyncSet, GetFormat()->FindRealSdrObject());
+
+ pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ }
+ }
+ break;
+ case SdrUserCallType::ChangeAttr:
+ if ( bNotify )
+ {
+ lcl_NotifyBackgroundOfObj( *this, rObj, pOldBoundRect );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+namespace
+{
+ const SwFormatAnchor* lcl_getAnchorFormat( const SfxPoolItem& _rItem )
+ {
+ sal_uInt16 nWhich = _rItem.Which();
+ const SwFormatAnchor* pAnchorFormat = nullptr;
+ if ( RES_ATTRSET_CHG == nWhich )
+ {
+ pAnchorFormat = static_cast<const SwAttrSetChg&>(_rItem).GetChgSet()->
+ GetItemIfSet( RES_ANCHOR, false );
+ }
+ else if ( RES_ANCHOR == nWhich )
+ {
+ pAnchorFormat = &static_cast<const SwFormatAnchor&>(_rItem);
+ }
+ return pAnchorFormat;
+ }
+}
+
+void SwDrawContact::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ SwClient::SwClientNotify(rMod, rHint); // needed as SwContact::SwClientNotify doesn't explicitly call SwClient::SwClientNotify
+ SwContact::SwClientNotify(rMod, rHint);
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SAL_WARN_IF(mbDisconnectInProgress, "sw.core", "<SwDrawContact::Modify(..)> called during disconnection.");
+
+ const SfxPoolItem* pNew = pLegacyHint->m_pNew;
+ sal_uInt16 nWhich = pNew ? pNew->Which() : 0;
+ if(const SwFormatAnchor* pNewAnchorFormat = pNew ? lcl_getAnchorFormat(*pNew) : nullptr)
+ {
+ // Do not respond to a Reset Anchor!
+ if(GetFormat()->GetAttrSet().GetItemState(RES_ANCHOR, false) == SfxItemState::SET)
+ {
+ // no connect to layout during disconnection
+ if(!mbDisconnectInProgress)
+ {
+ // determine old object rectangle of 'master' drawing object
+ // for notification
+ const tools::Rectangle* pOldRect = nullptr;
+ tools::Rectangle aOldRect;
+ if(GetAnchorFrame())
+ {
+ // --> #i36181# - include spacing in object
+ // rectangle for notification.
+ aOldRect = maAnchoredDrawObj.GetObjRectWithSpaces().SVRect();
+ pOldRect = &aOldRect;
+ }
+ // re-connect to layout due to anchor format change
+ ConnectToLayout(pNewAnchorFormat);
+ // notify background of drawing objects
+ lcl_NotifyBackgroundOfObj(*this, *GetMaster(), pOldRect);
+ NotifyBackgroundOfAllVirtObjs(pOldRect);
+
+ const SwFormatAnchor* pOldAnchorFormat = pLegacyHint->m_pOld ? lcl_getAnchorFormat(*pLegacyHint->m_pOld) : nullptr;
+ if(!pOldAnchorFormat || (pOldAnchorFormat->GetAnchorId() != pNewAnchorFormat->GetAnchorId()))
+ {
+ if(maAnchoredDrawObj.DrawObj())
+ {
+ // --> #i102752#
+ // assure that a ShapePropertyChangeNotifier exists
+ maAnchoredDrawObj.DrawObj()->notifyShapePropertyChange(svx::ShapePropertyProviderId::TextDocAnchor);
+ }
+ else
+ SAL_WARN("sw.core", "SwDrawContact::Modify: no draw object here?");
+ }
+ }
+ }
+ else
+ DisconnectFromLayout();
+ }
+ else if (nWhich == RES_REMOVE_UNO_OBJECT)
+ {} // nothing to do
+ // --> #i62875# - no further notification, if not connected to Writer layout
+ else if ( maAnchoredDrawObj.GetAnchorFrame() &&
+ maAnchoredDrawObj.GetDrawObj()->GetUserCall() )
+ {
+ bool bUpdateSortedObjsList(false);
+ switch(nWhich)
+ {
+ case RES_UL_SPACE:
+ case RES_LR_SPACE:
+ case RES_HORI_ORIENT:
+ case RES_VERT_ORIENT:
+ case RES_FOLLOW_TEXT_FLOW: // #i28701# - add attribute 'Follow text flow'
+ break;
+ case RES_SURROUND:
+ case RES_OPAQUE:
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ // --> #i28701# - on change of wrapping style, hell|heaven layer,
+ // or wrapping style influence an update of the <SwSortedObjs> list,
+ // the drawing object is registered in, has to be performed. This is triggered
+ // by the 1st parameter of method call <InvalidateObjs_(..)>.
+ bUpdateSortedObjsList = true;
+ break;
+ case RES_ATTRSET_CHG: // #i35443#
+ {
+ auto pChgSet = static_cast<const SwAttrSetChg*>(pNew)->GetChgSet();
+ if(pChgSet->GetItemState(RES_SURROUND, false) == SfxItemState::SET ||
+ pChgSet->GetItemState(RES_OPAQUE, false) == SfxItemState::SET ||
+ pChgSet->GetItemState(RES_WRAP_INFLUENCE_ON_OBJPOS, false) == SfxItemState::SET)
+ bUpdateSortedObjsList = true;
+ }
+ break;
+ default:
+ assert(!"<SwDraw Contact::Modify(..)> - unhandled attribute?");
+ }
+ lcl_NotifyBackgroundOfObj(*this, *GetMaster(), nullptr);
+ NotifyBackgroundOfAllVirtObjs(nullptr);
+ InvalidateObjs_(bUpdateSortedObjsList);
+ }
+
+ // #i51474#
+ GetAnchoredObj(nullptr)->ResetLayoutProcessBools();
+ }
+ else if (auto pDrawFrameFormatHint = dynamic_cast<const sw::DrawFrameFormatHint*>(&rHint))
+ {
+ switch(pDrawFrameFormatHint->m_eId)
+ {
+ case sw::DrawFrameFormatHintId::DYING:
+ delete this;
+ break;
+ case sw::DrawFrameFormatHintId::PREPPASTING:
+ MoveObjToVisibleLayer(GetMaster());
+ break;
+ case sw::DrawFrameFormatHintId::PREP_INSERT_FLY:
+ InsertMasterIntoDrawPage();
+ // #i40845# - follow-up of #i35635#
+ // move object to visible layer
+ MoveObjToVisibleLayer(GetMaster());
+ // tdf#135661 InsertMasterIntoDrawPage may have created a new
+ // SwXShape with null m_pFormat; fix that
+ SwXShape::AddExistingShapeToFormat(*GetMaster());
+ break;
+ case sw::DrawFrameFormatHintId::PREP_DELETE_FLY:
+ RemoveMasterFromDrawPage();
+ break;
+ case sw::DrawFrameFormatHintId::PAGE_OUT_OF_BOUNDS:
+ case sw::DrawFrameFormatHintId::DELETE_FRAMES:
+ DisconnectFromLayout();
+ break;
+ case sw::DrawFrameFormatHintId::MAKE_FRAMES:
+ ConnectToLayout();
+ break;
+ case sw::DrawFrameFormatHintId::POST_RESTORE_FLY_ANCHOR:
+ GetAnchoredObj(GetMaster())->MakeObjPos();
+ break;
+ default:
+ ;
+ }
+ }
+ else if (auto pCheckDrawFrameFormatLayerHint = dynamic_cast<const sw::CheckDrawFrameFormatLayerHint*>(&rHint))
+ {
+ *(pCheckDrawFrameFormatLayerHint->m_bCheckControlLayer) |= (GetMaster() && CheckControlLayer(GetMaster()));
+ }
+ else if (auto pContactChangedHint = dynamic_cast<const sw::ContactChangedHint*>(&rHint))
+ {
+ if(!*pContactChangedHint->m_ppObject)
+ *pContactChangedHint->m_ppObject = GetMaster();
+ auto pObject = *pContactChangedHint->m_ppObject;
+ Changed(*pObject, SdrUserCallType::Delete, pObject->GetLastBoundRect());
+ }
+ else if (auto pDrawFormatLayoutCopyHint = dynamic_cast<const sw::DrawFormatLayoutCopyHint*>(&rHint))
+ {
+ const SwDrawFrameFormat& rFormat = static_cast<const SwDrawFrameFormat&>(rMod);
+ new SwDrawContact(
+ &pDrawFormatLayoutCopyHint->m_rDestFormat,
+ pDrawFormatLayoutCopyHint->m_rDestDoc.CloneSdrObj(
+ *GetMaster(),
+ pDrawFormatLayoutCopyHint->m_rDestDoc.IsCopyIsMove() && &pDrawFormatLayoutCopyHint->m_rDestDoc == rFormat.GetDoc()));
+ // #i49730# - notify draw frame format that position attributes are
+ // already set, if the position attributes are already set at the
+ // source draw frame format.
+ if(rFormat.IsPosAttrSet())
+ pDrawFormatLayoutCopyHint->m_rDestFormat.PosAttrSet();
+ }
+ else if (auto pRestoreFlyAnchorHint = dynamic_cast<const sw::RestoreFlyAnchorHint*>(&rHint))
+ {
+ SdrObject* pObj = GetMaster();
+ if(GetAnchorFrame() && !pObj->IsInserted())
+ {
+ auto pDrawModel = const_cast<SwDrawFrameFormat&>(static_cast<const SwDrawFrameFormat&>(rMod)).GetDoc()->getIDocumentDrawModelAccess().GetDrawModel();
+ assert(pDrawModel);
+ pDrawModel->GetPage(0)->InsertObject(pObj);
+ }
+ pObj->SetRelativePos(pRestoreFlyAnchorHint->m_aPos);
+ }
+ else if (auto pCreatePortionHint = dynamic_cast<const sw::CreatePortionHint*>(&rHint))
+ {
+ if(*pCreatePortionHint->m_ppContact)
+ return;
+ *pCreatePortionHint->m_ppContact = this; // This is kind of ridiculous: the FrameFormat doesn't even hold a pointer to the contact itself, but here we are leaking it out randomly
+ if(!GetAnchorFrame())
+ {
+ // No direct positioning needed any more
+ ConnectToLayout();
+ // Move object to visible layer
+ MoveObjToVisibleLayer(GetMaster());
+ }
+ }
+ else if (auto pCollectTextObjectsHint = dynamic_cast<const sw::CollectTextObjectsHint*>(&rHint))
+ {
+ auto pSdrO = GetMaster();
+ if(!pSdrO)
+ return;
+ if(dynamic_cast<const SdrObjGroup*>(pSdrO))
+ {
+ SdrObjListIter aListIter(*pSdrO, SdrIterMode::DeepNoGroups);
+ //iterate inside of a grouped object
+ while(aListIter.IsMore())
+ {
+ SdrObject* pSdrOElement = aListIter.Next();
+ auto pTextObj = const_cast<SdrTextObj*>(dynamic_cast<const SdrTextObj*>(pSdrOElement));
+ if(pTextObj && pTextObj->HasText())
+ pCollectTextObjectsHint->m_rTextObjects.push_back(pTextObj);
+ }
+ }
+ else if(auto pTextObj = const_cast<SdrTextObj*>(dynamic_cast<const SdrTextObj*>(pSdrO)))
+ {
+ if(pTextObj->HasText())
+ pCollectTextObjectsHint->m_rTextObjects.push_back(pTextObj);
+ }
+ }
+ else if (auto pGetZOrdnerHint = dynamic_cast<const sw::GetZOrderHint*>(&rHint))
+ {
+ auto pFormat(dynamic_cast<const SwFrameFormat*>(&rMod));
+ if(pFormat->Which() == RES_DRAWFRMFMT)
+ pGetZOrdnerHint->m_rnZOrder = GetMaster()->GetOrdNum();
+ }
+ else if (auto pConnectedHint = dynamic_cast<const sw::GetObjectConnectedHint*>(&rHint))
+ {
+ pConnectedHint->m_risConnected |= (GetAnchorFrame() != nullptr);
+ }
+}
+
+// #i26791#
+// #i28701# - added parameter <_bUpdateSortedObjsList>
+void SwDrawContact::InvalidateObjs_( const bool _bUpdateSortedObjsList )
+{
+ for(const auto& rpDrawVirtObj : maDrawVirtObjs)
+ // invalidate position of existing 'virtual' drawing objects
+ {
+ SwDrawVirtObj* pDrawVirtObj(rpDrawVirtObj.get());
+ // #i33313# - invalidation only for connected
+ // 'virtual' drawing objects
+ if ( pDrawVirtObj->IsConnected() )
+ {
+ pDrawVirtObj->AnchoredObj().InvalidateObjPos();
+ // #i28701#
+ if ( _bUpdateSortedObjsList )
+ {
+ pDrawVirtObj->AnchoredObj().UpdateObjInSortedList();
+ }
+ }
+ }
+
+ // invalidate position of 'master' drawing object
+ SwAnchoredObject* pAnchoredObj = GetAnchoredObj( nullptr );
+ pAnchoredObj->InvalidateObjPos();
+ // #i28701#
+ if ( _bUpdateSortedObjsList )
+ {
+ pAnchoredObj->UpdateObjInSortedList();
+ }
+}
+
+void SwDrawContact::DisconnectFromLayout( bool _bMoveMasterToInvisibleLayer )
+{
+ mbDisconnectInProgress = true;
+
+ // --> #i36181# - notify background of drawing object
+ if ( _bMoveMasterToInvisibleLayer &&
+ !(GetFormat()->GetDoc()->IsInDtor()) &&
+ GetAnchorFrame() && !GetAnchorFrame()->IsInDtor() )
+ {
+ const tools::Rectangle aOldRect( maAnchoredDrawObj.GetObjRectWithSpaces().SVRect() );
+ lcl_NotifyBackgroundOfObj( *this, *GetMaster(), &aOldRect );
+ NotifyBackgroundOfAllVirtObjs( &aOldRect );
+ }
+
+ // remove 'virtual' drawing objects from writer
+ // layout and from drawing page
+ for(auto& rpVirtDrawObj : maDrawVirtObjs)
+ {
+ rpVirtDrawObj->RemoveFromWriterLayout();
+ rpVirtDrawObj->RemoveFromDrawingPage();
+ }
+
+ if ( maAnchoredDrawObj.GetAnchorFrame() )
+ {
+ maAnchoredDrawObj.AnchorFrame()->RemoveDrawObj( maAnchoredDrawObj );
+ }
+
+ if ( _bMoveMasterToInvisibleLayer && GetMaster() && GetMaster()->IsInserted() )
+ {
+ SdrViewIter aIter( GetMaster() );
+ for( SdrView* pView = aIter.FirstView(); pView;
+ pView = aIter.NextView() )
+ {
+ pView->MarkObj( GetMaster(), pView->GetSdrPageView(), true );
+ }
+
+ // Instead of removing 'master' object from drawing page, move the
+ // 'master' drawing object into the corresponding invisible layer.
+ {
+ //static_cast<SwFrameFormat*>(GetRegisteredIn())->getIDocumentDrawModelAccess()->GetDrawModel()->GetPage(0)->
+ // RemoveObject( GetMaster()->GetOrdNum() );
+ // #i18447# - in order to consider group object correct
+ // use new method <SwDrawContact::MoveObjToInvisibleLayer(..)>
+ MoveObjToInvisibleLayer( GetMaster() );
+ }
+ }
+
+ mbDisconnectInProgress = false;
+}
+
+/// method to remove 'master' drawing object from drawing page.
+void SwDrawContact::RemoveMasterFromDrawPage()
+{
+ if ( GetMaster() )
+ {
+ GetMaster()->SetUserCall( nullptr );
+ if ( GetMaster()->IsInserted() )
+ {
+ static_cast<SwFrameFormat*>(GetRegisteredIn())->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)->
+ RemoveObject( GetMaster()->GetOrdNum() );
+ }
+ }
+}
+
+// disconnect for a dedicated drawing object - could be 'master' or 'virtual'.
+// a 'master' drawing object will disconnect a 'virtual' drawing object
+// in order to take its place.
+// #i19919# - no special case, if drawing object isn't in
+// page header/footer, in order to get drawing objects in repeating table headers
+// also working.
+void SwDrawContact::DisconnectObjFromLayout( SdrObject* _pDrawObj )
+{
+ if ( auto pSwDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( _pDrawObj) )
+ {
+ pSwDrawVirtObj->RemoveFromWriterLayout();
+ pSwDrawVirtObj->RemoveFromDrawingPage();
+ }
+ else
+ {
+ const auto ppVirtDrawObj(std::find_if(maDrawVirtObjs.begin(), maDrawVirtObjs.end(),
+ [] (const SwDrawVirtObjPtr& pObj) { return pObj->IsConnected(); }));
+
+ if(ppVirtDrawObj != maDrawVirtObjs.end())
+ {
+ // replace found 'virtual' drawing object by 'master' drawing
+ // object and disconnect the 'virtual' one
+ SwDrawVirtObj* pDrawVirtObj(ppVirtDrawObj->get());
+ SwFrame* pNewAnchorFrameOfMaster = pDrawVirtObj->AnchorFrame();
+ // disconnect 'virtual' drawing object
+ pDrawVirtObj->RemoveFromWriterLayout();
+ pDrawVirtObj->RemoveFromDrawingPage();
+ // disconnect 'master' drawing object from current frame
+ GetAnchorFrame()->RemoveDrawObj( maAnchoredDrawObj );
+ // re-connect 'master' drawing object to frame of found 'virtual'
+ // drawing object.
+ pNewAnchorFrameOfMaster->AppendDrawObj( maAnchoredDrawObj );
+ }
+ else
+ {
+ // no connected 'virtual' drawing object found. Thus, disconnect
+ // completely from layout.
+ DisconnectFromLayout();
+ }
+ }
+}
+
+static SwTextFrame* lcl_GetFlyInContentAnchor( SwTextFrame* _pProposedAnchorFrame,
+ SwPosition const& rAnchorPos)
+{
+ SwTextFrame* pAct = _pProposedAnchorFrame;
+ SwTextFrame* pTmp;
+ TextFrameIndex const nTextOffset(_pProposedAnchorFrame->MapModelToViewPos(rAnchorPos));
+ do
+ {
+ pTmp = pAct;
+ pAct = pTmp->GetFollow();
+ }
+ while (pAct && nTextOffset >= pAct->GetOffset());
+ return pTmp;
+}
+
+void SwDrawContact::ConnectToLayout( const SwFormatAnchor* pAnch )
+{
+ // *no* connect to layout during disconnection from layout.
+ if ( mbDisconnectInProgress )
+ {
+ OSL_FAIL( "<SwDrawContact::ConnectToLayout(..)> called during disconnection.");
+ return;
+ }
+
+ // --> #i33909# - *no* connect to layout, if 'master' drawing
+ // object isn't inserted in the drawing page
+ if ( !GetMaster()->IsInserted() )
+ {
+ OSL_FAIL( "<SwDrawContact::ConnectToLayout(..)> - master drawing object not inserted -> no connect to layout. Please inform od@openoffice.org" );
+ return;
+ }
+
+ SwFrameFormat* pDrawFrameFormat = static_cast<SwFrameFormat*>(GetRegisteredIn());
+
+ if( !pDrawFrameFormat->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ return;
+
+ // remove 'virtual' drawing objects from writer
+ // layout and from drawing page, and remove 'master' drawing object from
+ // writer layout - 'master' object will remain in drawing page.
+ DisconnectFromLayout( false );
+
+ if ( !pAnch )
+ {
+ pAnch = &(pDrawFrameFormat->GetAnchor());
+ }
+
+ switch ( pAnch->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ sal_uInt16 nPgNum = pAnch->GetPageNum();
+ SwViewShell *pShell = pDrawFrameFormat->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( !pShell )
+ break;
+ SwRootFrame* pRoot = pShell->GetLayout();
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(pRoot->Lower());
+
+ for ( sal_uInt16 i = 1; i < nPgNum && pPage; ++i )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ if ( pPage )
+ {
+ pPage->AppendDrawObj( maAnchoredDrawObj );
+ }
+ else
+ //Looks stupid but is allowed (compare SwFEShell::SetPageObjsNewPage)
+ pRoot->SetAssertFlyPages();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ case RndStdIds::FLY_AS_CHAR:
+ {
+ if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ ClrContourCache( GetMaster() );
+ }
+ // support drawing objects in header/footer,
+ // but not control objects:
+ // anchor at first found frame the 'master' object and
+ // at the following frames 'virtual' drawing objects.
+ // Note: method is similar to <SwFlyFrameFormat::MakeFrames(..)>
+ sw::BroadcastingModify *pModify = nullptr;
+ if( pAnch->GetContentAnchor() )
+ {
+ if ( pAnch->GetAnchorId() == RndStdIds::FLY_AT_FLY )
+ {
+ SwNodeIndex aIdx( pAnch->GetContentAnchor()->nNode );
+ SwContentNode* pCNd = pDrawFrameFormat->GetDoc()->GetNodes().GoNext( &aIdx );
+ if (SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First())
+ pModify = pCNd;
+ else
+ {
+ const SwNodeIndex& rIdx = pAnch->GetContentAnchor()->nNode;
+ SwFrameFormats& rFormats = *(pDrawFrameFormat->GetDoc()->GetSpzFrameFormats());
+ for( auto pFlyFormat : rFormats )
+ {
+ if( pFlyFormat->GetContent().GetContentIdx() &&
+ rIdx == *(pFlyFormat->GetContent().GetContentIdx()) )
+ {
+ pModify = pFlyFormat;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ pModify = pAnch->GetContentAnchor()->nNode.GetNode().GetContentNode();
+ }
+ }
+
+ // #i29199# - It is possible, that
+ // the anchor doesn't exist - E.g., reordering the
+ // sub-documents in a master document.
+ // Note: The anchor will be inserted later.
+ if ( !pModify )
+ {
+ // break to end of the current switch case.
+ break;
+ }
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*pModify);
+ SwFrame* pAnchorFrameOfMaster = nullptr;
+ for( SwFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ // append drawing object, if
+ // (1) proposed anchor frame isn't a follow and...
+ const bool bFollow = pFrame->IsContentFrame() && static_cast<SwContentFrame*>(pFrame)->IsFollow();
+ if (bFollow)
+ continue;
+
+ // (2) drawing object isn't a control object to be anchored
+ // in header/footer.
+ const bool bControlInHF = ::CheckControlLayer(GetMaster()) && pFrame->FindFooterOrHeader();
+ // tdf#129542 but make an exception for control objects so they can get added to just the first frame,
+ // the Master Anchor Frame and not the others
+ if (bControlInHF && pAnchorFrameOfMaster)
+ continue;
+
+ bool bAdd;
+ if (RndStdIds::FLY_AT_FLY == pAnch->GetAnchorId())
+ bAdd = true;
+ else
+ {
+ assert(pFrame->IsTextFrame());
+ bAdd = IsAnchoredObjShown(*static_cast<SwTextFrame*>(pFrame), *pAnch);
+ }
+
+ if( bAdd )
+ {
+ if ( RndStdIds::FLY_AT_FLY == pAnch->GetAnchorId() && !pFrame->IsFlyFrame() )
+ {
+ pFrame = pFrame->FindFlyFrame();
+ assert(pFrame);
+ }
+
+ // find correct follow for as character anchored objects
+ if ((pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR) &&
+ pFrame->IsTextFrame() )
+ {
+ pFrame = lcl_GetFlyInContentAnchor(
+ static_cast<SwTextFrame*>(pFrame),
+ *pAnch->GetContentAnchor());
+ }
+
+ if ( !pAnchorFrameOfMaster )
+ {
+ // append 'master' drawing object
+ pAnchorFrameOfMaster = pFrame;
+ pFrame->AppendDrawObj( maAnchoredDrawObj );
+ }
+ else
+ {
+ // append 'virtual' drawing object
+ SwDrawVirtObj* pDrawVirtObj = AddVirtObj(*pFrame);
+ if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ ClrContourCache( pDrawVirtObj );
+ }
+ pFrame->AppendDrawObj( pDrawVirtObj->AnchoredObj() );
+
+ pDrawVirtObj->ActionChanged();
+ }
+
+ if ( pAnch->GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ pFrame->InvalidatePrt();
+ }
+ }
+ }
+ }
+ break;
+ default:
+ assert(!"Unknown Anchor.");
+ break;
+ }
+ if ( GetAnchorFrame() )
+ {
+ ::setContextWritingMode( maAnchoredDrawObj.DrawObj(), GetAnchorFrame() );
+ // #i26791# - invalidate objects instead of direct positioning
+ InvalidateObjs_();
+ }
+}
+
+/// insert 'master' drawing object into drawing page
+void SwDrawContact::InsertMasterIntoDrawPage()
+{
+ if ( !GetMaster()->IsInserted() )
+ {
+ GetFormat()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)
+ ->InsertObject( GetMaster(), GetMaster()->GetOrdNumDirect() );
+ }
+ GetMaster()->SetUserCall( this );
+}
+
+SwPageFrame* SwDrawContact::FindPage( const SwRect &rRect )
+{
+ // --> #i28701# - use method <GetPageFrame()>
+ SwPageFrame* pPg = GetPageFrame();
+ if ( !pPg && GetAnchorFrame() )
+ pPg = GetAnchorFrame()->FindPageFrame();
+ if ( pPg )
+ pPg = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( rRect, pPg )));
+ return pPg;
+}
+
+void SwDrawContact::ChkPage()
+{
+ if ( mbDisconnectInProgress )
+ {
+ OSL_FAIL( "<SwDrawContact::ChkPage()> called during disconnection." );
+ return;
+ }
+
+ // --> #i28701#
+ SwPageFrame* pPg = ( maAnchoredDrawObj.GetAnchorFrame() &&
+ maAnchoredDrawObj.GetAnchorFrame()->IsPageFrame() )
+ ? GetPageFrame()
+ : FindPage( SwRect(GetMaster()->GetCurrentBoundRect()) );
+ if ( GetPageFrame() == pPg )
+ return;
+
+ // if drawing object is anchor in header/footer a change of the page
+ // is a dramatic change. Thus, completely re-connect to the layout
+ if ( maAnchoredDrawObj.GetAnchorFrame() &&
+ maAnchoredDrawObj.GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ ConnectToLayout();
+ }
+ else
+ {
+ // --> #i28701# - use methods <GetPageFrame()> and <SetPageFrame>
+ if ( GetPageFrame() )
+ GetPageFrame()->RemoveDrawObjFromPage( maAnchoredDrawObj );
+ pPg->AppendDrawObjToPage( maAnchoredDrawObj );
+ maAnchoredDrawObj.SetPageFrame( pPg );
+ }
+}
+
+// Important note:
+// method is called by method <SwDPage::ReplaceObject(..)>, which called its
+// corresponding superclass method <FmFormPage::ReplaceObject(..)>.
+// Note: 'master' drawing object *has* to be connected to layout triggered
+// by the caller of this, if method is called.
+void SwDrawContact::ChangeMasterObject(SdrObject* pNewMaster)
+{
+ DisconnectFromLayout( false );
+ // consider 'virtual' drawing objects
+ RemoveAllVirtObjs();
+
+ GetMaster()->SetUserCall( nullptr );
+ if(pNewMaster)
+ maAnchoredDrawObj.SetDrawObj(*pNewMaster);
+ else
+ mbMasterObjCleared = true;
+ GetMaster()->SetUserCall( this );
+
+ InvalidateObjs_();
+}
+
+/// get data collection of anchored objects, handled by with contact
+void SwDrawContact::GetAnchoredObjs(std::vector<SwAnchoredObject*>& o_rAnchoredObjs) const
+{
+ o_rAnchoredObjs.push_back(const_cast<SwAnchoredDrawObject*>(&maAnchoredDrawObj));
+
+ for(auto& rpDrawVirtObj : maDrawVirtObjs)
+ o_rAnchoredObjs.push_back(&rpDrawVirtObj->AnchoredObj());
+}
+
+// AW: own sdr::contact::ViewContact (VC) sdr::contact::ViewObjectContact (VOC) needed
+// since offset is defined different from SdrVirtObj's sdr::contact::ViewContactOfVirtObj.
+// For paint, that offset is used by setting at the OutputDevice; for primitives this is
+// not possible since we have no OutputDevice, but define the geometry itself.
+
+namespace sdr::contact
+{
+ namespace {
+
+ class VOCOfDrawVirtObj : public ViewObjectContactOfSdrObj
+ {
+ protected:
+ /**
+ * This method is responsible for creating the graphical visualisation data which is
+ * stored/cached in the local primitive. Default gets view-independent Primitive from
+ * the ViewContact using ViewContact::getViewIndependentPrimitive2DContainer(), takes
+ * care of visibility, handles glue and ghosted.
+ *
+ * This method will not handle included hierarchies and not check geometric visibility.
+ */
+ virtual void createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+
+ public:
+ VOCOfDrawVirtObj(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+ };
+
+ class VCOfDrawVirtObj : public ViewContactOfVirtObj
+ {
+ protected:
+ /** Create an Object-Specific ViewObjectContact, set ViewContact and ObjectContact.
+ *
+ * Always needs to return something. Default is to create a standard ViewObjectContact
+ * containing the given ObjectContact and *this.
+ */
+ virtual ViewObjectContact& CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) override;
+
+ public:
+ /// basic constructor, used from SdrObject.
+ explicit VCOfDrawVirtObj(SwDrawVirtObj& rObj)
+ : ViewContactOfVirtObj(rObj)
+ {
+ }
+
+ /// access to SwDrawVirtObj
+ SwDrawVirtObj& GetSwDrawVirtObj() const
+ {
+ return static_cast<SwDrawVirtObj&>(mrObject);
+ }
+ };
+
+ }
+} // end of namespace sdr::contact
+
+namespace sdr::contact
+{
+ /// recursively collect primitive data from given VOC with given offset
+ static void impAddPrimitivesFromGroup(const ViewObjectContact& rVOC, const basegfx::B2DHomMatrix& rOffsetMatrix, const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DContainer& rxTarget)
+ {
+ const sal_uInt32 nSubHierarchyCount(rVOC.GetViewContact().GetObjectCount());
+
+ for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
+ {
+ const ViewObjectContact& rCandidate(rVOC.GetViewContact().GetViewContact(a).GetViewObjectContact(rVOC.GetObjectContact()));
+
+ if(rCandidate.GetViewContact().GetObjectCount())
+ {
+ // is a group object itself, call recursively
+ impAddPrimitivesFromGroup(rCandidate, rOffsetMatrix, rDisplayInfo, rxTarget);
+ }
+ else
+ {
+ // single object, add primitives; check model-view visibility
+ if(rCandidate.isPrimitiveVisible(rDisplayInfo))
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aNewSequence(rCandidate.getPrimitive2DSequence(rDisplayInfo));
+
+ if(!aNewSequence.empty())
+ {
+ // get ranges
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(rCandidate.GetObjectContact().getViewInformation2D());
+ const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport());
+ basegfx::B2DRange aObjectRange(rCandidate.getObjectRange());
+
+ // correct with virtual object's offset
+ aObjectRange.transform(rOffsetMatrix);
+
+ // check geometrical visibility (with offset)
+ if(!aViewRange.overlaps(aObjectRange))
+ {
+ // not visible, release
+ aNewSequence.clear();
+ }
+ }
+
+ if(!aNewSequence.empty())
+ {
+ rxTarget.append(aNewSequence);
+ }
+ }
+ }
+ }
+ }
+
+ void VOCOfDrawVirtObj::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ // tdf#91260 have already checked top-level one is on the right page
+ assert(isPrimitiveVisible(rDisplayInfo));
+ // nasty corner case: override to clear page frame to disable the
+ // sub-objects' anchor check, because their anchor is always on
+ // the first page that the page style is applied to
+ DisplayInfo aDisplayInfo(rDisplayInfo);
+ aDisplayInfo.SetWriterPageFrame(basegfx::B2IRectangle());
+ const VCOfDrawVirtObj& rVC = static_cast< const VCOfDrawVirtObj& >(GetViewContact());
+ const SdrObject& rReferencedObject = rVC.GetSwDrawVirtObj().GetReferencedObj();
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+
+ // create offset transformation
+ basegfx::B2DHomMatrix aOffsetMatrix;
+ const Point aLocalOffset(rVC.GetSwDrawVirtObj().GetOffset());
+
+ if(aLocalOffset.X() || aLocalOffset.Y())
+ {
+ aOffsetMatrix.set(0, 2, aLocalOffset.X());
+ aOffsetMatrix.set(1, 2, aLocalOffset.Y());
+ }
+
+ if(dynamic_cast<const SdrObjGroup*>( &rReferencedObject) != nullptr)
+ {
+ // group object. Since the VOC/OC/VC hierarchy does not represent the
+ // hierarchy virtual objects when they have group objects
+ // (ViewContactOfVirtObj::GetObjectCount() returns null for that purpose)
+ // to avoid multiple usages of VOCs (which would not work), the primitives
+ // for the sub-hierarchy need to be collected here
+
+ // Get the VOC of the referenced object (the Group) and fetch primitives from it
+ const ViewObjectContact& rVOCOfRefObj = rReferencedObject.GetViewContact().GetViewObjectContact(GetObjectContact());
+ impAddPrimitivesFromGroup(rVOCOfRefObj, aOffsetMatrix, aDisplayInfo, xRetval);
+ }
+ else
+ {
+ // single object, use method from referenced object to get the Primitive2DSequence
+ rReferencedObject.GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ }
+
+ if(!xRetval.empty())
+ {
+ // create transform primitive
+ drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::TransformPrimitive2D(aOffsetMatrix, std::move(xRetval)));
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
+ }
+
+ rVisitor.visit(xRetval);
+ }
+
+ ViewObjectContact& VCOfDrawVirtObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+ {
+ return *(new VOCOfDrawVirtObj(rObjectContact, *this));
+ }
+
+} // end of namespace sdr::contact
+
+/// implementation of class <SwDrawVirtObj>
+std::unique_ptr<sdr::contact::ViewContact> SwDrawVirtObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::VCOfDrawVirtObj>(*this);
+}
+
+SwDrawVirtObj::SwDrawVirtObj(
+ SdrModel& rSdrModel,
+ SdrObject& _rNewObj,
+ SwDrawContact& _rDrawContact)
+: SdrVirtObj(rSdrModel, _rNewObj ),
+ mrDrawContact(_rDrawContact)
+{
+ // #i26791#
+ maAnchoredDrawObj.SetDrawObj( *this );
+
+ // #i35635# - set initial position out of sight
+ NbcMove( Size( -16000, -16000 ) );
+}
+
+SwDrawVirtObj::SwDrawVirtObj(
+ SdrModel& rSdrModel,
+ SwDrawVirtObj const & rSource)
+: SdrVirtObj(rSdrModel, rSource),
+ mrDrawContact(rSource.mrDrawContact)
+{
+ // #i26791#
+ maAnchoredDrawObj.SetDrawObj( *this );
+
+ // #i35635# - set initial position out of sight
+ NbcMove( Size( -16000, -16000 ) );
+
+ // Note: Members <maAnchoredDrawObj> and <mrDrawContact>
+ // haven't to be considered.
+}
+
+SwDrawVirtObj::~SwDrawVirtObj()
+{
+}
+
+SwDrawVirtObj* SwDrawVirtObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SwDrawVirtObj(rTargetModel, *this);
+}
+
+const SwFrame* SwDrawVirtObj::GetAnchorFrame() const
+{
+ // #i26791# - use new member <maAnchoredDrawObj>
+ return maAnchoredDrawObj.GetAnchorFrame();
+}
+
+SwFrame* SwDrawVirtObj::AnchorFrame()
+{
+ // #i26791# - use new member <maAnchoredDrawObj>
+ return maAnchoredDrawObj.AnchorFrame();
+}
+
+void SwDrawVirtObj::RemoveFromWriterLayout()
+{
+ // remove contact object from frame for 'virtual' drawing object
+ // #i26791# - use new member <maAnchoredDrawObj>
+ if ( maAnchoredDrawObj.GetAnchorFrame() )
+ {
+ maAnchoredDrawObj.AnchorFrame()->RemoveDrawObj( maAnchoredDrawObj );
+ }
+}
+
+void SwDrawVirtObj::AddToDrawingPage(SwFrame const& rAnchorFrame)
+{
+ // determine 'master'
+ SdrObject* pOrgMasterSdrObj = mrDrawContact.GetMaster();
+
+ // insert 'virtual' drawing object into page, set layer and user call.
+ SdrPage* pDrawPg = pOrgMasterSdrObj->getSdrPageFromSdrObject();
+ // default: insert before master object
+ auto nOrdNum(GetReferencedObj().GetOrdNum());
+
+ // maintain invariant that a shape's textbox immediately follows the shape
+ // also for the multiple SdrDrawVirtObj created for shapes in header/footer
+ if (SwFrameFormat const*const pFlyFormat =
+ SwTextBoxHelper::getOtherTextBoxFormat(mrDrawContact.GetFormat(), RES_DRAWFRMFMT))
+ {
+ // this is for the case when the flyframe SdrVirtObj is created before the draw one
+ if (SwSortedObjs const*const pObjs = rAnchorFrame.GetDrawObjs())
+ {
+ for (SwAnchoredObject const*const pAnchoredObj : *pObjs)
+ {
+ if (&pAnchoredObj->GetFrameFormat() == pFlyFormat)
+ {
+ assert(dynamic_cast<SwFlyFrame const*>(pAnchoredObj));
+ nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
+ // the master SdrObj should have the highest index
+ assert(nOrdNum < GetReferencedObj().GetOrdNum());
+ break;
+ }
+ }
+ }
+ // this happens on initial insertion, the draw object is created first
+ SAL_INFO_IF(GetReferencedObj().GetOrdNum() == nOrdNum, "sw", "AddToDrawingPage: cannot find SdrObject for text box's shape");
+ }
+
+ // #i27030# - apply order number of referenced object
+ if ( nullptr != pDrawPg )
+ {
+ // #i27030# - apply order number of referenced object
+ pDrawPg->InsertObject(this, nOrdNum);
+ }
+ else
+ {
+ pDrawPg = getSdrPageFromSdrObject();
+ if ( pDrawPg )
+ {
+ pDrawPg->SetObjectOrdNum(GetOrdNumDirect(), nOrdNum);
+ }
+ else
+ {
+ SetOrdNum(nOrdNum);
+ }
+ }
+ SetUserCall( &mrDrawContact );
+}
+
+void SwDrawVirtObj::RemoveFromDrawingPage()
+{
+ SetUserCall( nullptr );
+ if ( getSdrPageFromSdrObject() )
+ {
+ getSdrPageFromSdrObject()->RemoveObject( GetOrdNum() );
+ }
+}
+
+/// Is 'virtual' drawing object connected to writer layout and to drawing layer?
+bool SwDrawVirtObj::IsConnected() const
+{
+ bool bRetVal = GetAnchorFrame() &&
+ ( getSdrPageFromSdrObject() && GetUserCall() );
+
+ return bRetVal;
+}
+
+void SwDrawVirtObj::NbcSetAnchorPos(const Point& rPnt)
+{
+ SdrObject::NbcSetAnchorPos( rPnt );
+}
+
+// #i97197#
+// the methods relevant for positioning
+
+const tools::Rectangle& SwDrawVirtObj::GetCurrentBoundRect() const
+{
+ if(m_aOutRect.IsEmpty())
+ {
+ const_cast<SwDrawVirtObj*>(this)->RecalcBoundRect();
+ }
+
+ return m_aOutRect;
+}
+
+const tools::Rectangle& SwDrawVirtObj::GetLastBoundRect() const
+{
+ return m_aOutRect;
+}
+
+Point SwDrawVirtObj::GetOffset() const
+{
+ // do NOT use IsEmpty() here, there is already a useful offset
+ // in the position
+ if(m_aOutRect == tools::Rectangle())
+ {
+ return Point();
+ }
+ else
+ {
+ return m_aOutRect.TopLeft() - GetReferencedObj().GetCurrentBoundRect().TopLeft();
+ }
+}
+
+void SwDrawVirtObj::SetBoundRectDirty()
+{
+ // do nothing to not lose model information in aOutRect
+}
+
+void SwDrawVirtObj::RecalcBoundRect()
+{
+ // #i26791# - switch order of calling <GetOffset()> and
+ // <ReferencedObj().GetCurrentBoundRect()>, because <GetOffset()> calculates
+ // its value by the 'BoundRect' of the referenced object.
+
+ const Point aOffset(GetOffset());
+ m_aOutRect = ReferencedObj().GetCurrentBoundRect() + aOffset;
+}
+
+basegfx::B2DPolyPolygon SwDrawVirtObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aRetval(rRefObj.TakeXorPoly());
+ aRetval.transform(basegfx::utils::createTranslateB2DHomMatrix(GetOffset().X(), GetOffset().Y()));
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SwDrawVirtObj::TakeContour() const
+{
+ basegfx::B2DPolyPolygon aRetval(rRefObj.TakeContour());
+ aRetval.transform(basegfx::utils::createTranslateB2DHomMatrix(GetOffset().X(), GetOffset().Y()));
+
+ return aRetval;
+}
+
+void SwDrawVirtObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrHdlList tmpList(nullptr);
+ rRefObj.AddToHdlList(tmpList);
+
+ size_t cnt = tmpList.GetHdlCount();
+ for(size_t i=0; i < cnt; ++i)
+ {
+ SdrHdl* pHdl = tmpList.GetHdl(i);
+ Point aP(pHdl->GetPos() + GetOffset());
+ pHdl->SetPos(aP);
+ }
+ tmpList.MoveTo(rHdlList);
+}
+
+void SwDrawVirtObj::NbcMove(const Size& rSiz)
+{
+ SdrObject::NbcMove( rSiz );
+}
+
+void SwDrawVirtObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ rRefObj.NbcResize(rRef - GetOffset(), xFact, yFact);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SwDrawVirtObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ rRefObj.NbcRotate(rRef - GetOffset(), nAngle, sn, cs);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SwDrawVirtObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ rRefObj.NbcMirror(rRef1 - GetOffset(), rRef2 - GetOffset());
+ SetBoundAndSnapRectsDirty();
+}
+
+void SwDrawVirtObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ rRefObj.NbcShear(rRef - GetOffset(), nAngle, tn, bVShear);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SwDrawVirtObj::Move(const Size& rSiz)
+{
+ SdrObject::Move( rSiz );
+}
+
+void SwDrawVirtObj::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if(xFact.GetNumerator() != xFact.GetDenominator() || yFact.GetNumerator() != yFact.GetDenominator())
+ {
+ tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect();
+ rRefObj.Resize(rRef - GetOffset(), xFact, yFact, bUnsetRelative);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+ }
+}
+
+void SwDrawVirtObj::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if(nAngle)
+ {
+ tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect();
+ rRefObj.Rotate(rRef - GetOffset(), nAngle, sn, cs);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+ }
+}
+
+void SwDrawVirtObj::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect();
+ rRefObj.Mirror(rRef1 - GetOffset(), rRef2 - GetOffset());
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+}
+
+void SwDrawVirtObj::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if(nAngle)
+ {
+ tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect();
+ rRefObj.Shear(rRef - GetOffset(), nAngle, tn, bVShear);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+ }
+}
+
+void SwDrawVirtObj::RecalcSnapRect()
+{
+ aSnapRect = rRefObj.GetSnapRect();
+ aSnapRect += GetOffset();
+}
+
+const tools::Rectangle& SwDrawVirtObj::GetSnapRect() const
+{
+ const_cast<SwDrawVirtObj*>(this)->aSnapRect = rRefObj.GetSnapRect();
+ const_cast<SwDrawVirtObj*>(this)->aSnapRect += GetOffset();
+
+ return aSnapRect;
+}
+
+void SwDrawVirtObj::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect();
+ tools::Rectangle aR(rRect);
+ aR -= GetOffset();
+ rRefObj.SetSnapRect(aR);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+}
+
+void SwDrawVirtObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aR(rRect);
+ aR -= GetOffset();
+ SetBoundAndSnapRectsDirty();
+ rRefObj.NbcSetSnapRect(aR);
+}
+
+const tools::Rectangle& SwDrawVirtObj::GetLogicRect() const
+{
+ const_cast<SwDrawVirtObj*>(this)->aSnapRect = rRefObj.GetLogicRect();
+ const_cast<SwDrawVirtObj*>(this)->aSnapRect += GetOffset();
+
+ return aSnapRect;
+}
+
+void SwDrawVirtObj::SetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if(m_pUserCall) aBoundRect0 = GetLastBoundRect();
+ tools::Rectangle aR(rRect);
+ aR -= GetOffset();
+ rRefObj.SetLogicRect(aR);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+}
+
+void SwDrawVirtObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aR(rRect);
+ aR -= GetOffset();
+ rRefObj.NbcSetLogicRect(aR);
+ SetBoundAndSnapRectsDirty();
+}
+
+Point SwDrawVirtObj::GetSnapPoint(sal_uInt32 i) const
+{
+ Point aP(rRefObj.GetSnapPoint(i));
+ aP += GetOffset();
+
+ return aP;
+}
+
+Point SwDrawVirtObj::GetPoint(sal_uInt32 i) const
+{
+ return rRefObj.GetPoint(i) + GetOffset();
+}
+
+void SwDrawVirtObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ Point aP(rPnt);
+ aP -= GetOffset();
+ rRefObj.SetPoint(aP, i);
+ SetBoundAndSnapRectsDirty();
+}
+
+bool SwDrawVirtObj::HasTextEdit() const
+{
+ return rRefObj.HasTextEdit();
+}
+
+// override 'layer' methods for 'virtual' drawing object to assure
+// that layer of 'virtual' object is the layer of the referenced object.
+SdrLayerID SwDrawVirtObj::GetLayer() const
+{
+ return GetReferencedObj().GetLayer();
+}
+
+void SwDrawVirtObj::NbcSetLayer(SdrLayerID nLayer)
+{
+ ReferencedObj().NbcSetLayer( nLayer );
+ SdrVirtObj::NbcSetLayer( ReferencedObj().GetLayer() );
+}
+
+void SwDrawVirtObj::SetLayer(SdrLayerID nLayer)
+{
+ ReferencedObj().SetLayer( nLayer );
+ SdrVirtObj::NbcSetLayer( ReferencedObj().GetLayer() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dflyobj.cxx b/sw/source/core/draw/dflyobj.cxx
new file mode 100644
index 000000000..3203bbc8d
--- /dev/null
+++ b/sw/source/core/draw/dflyobj.cxx
@@ -0,0 +1,1295 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdtrans.hxx>
+#include <editeng/protitem.hxx>
+#include <svx/svdpage.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#include <fmtclds.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmturl.hxx>
+#include <viewsh.hxx>
+#include <frmatr.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <dflyobj.hxx>
+#include <flyfrm.hxx>
+#include <frmfmt.hxx>
+#include <viewopt.hxx>
+#include <frmtool.hxx>
+#include <flyfrms.hxx>
+#include <ndnotxt.hxx>
+#include <grfatr.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <textboxhelper.hxx>
+#include <wrtsh.hxx>
+#include <ndgrf.hxx>
+#include <frmmgr.hxx>
+
+#include <svx/sdr/properties/defaultproperties.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+// AW: For VCOfDrawVirtObj and stuff
+#include <svx/sdr/contact/viewcontactofvirtobj.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <sw_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <notxtfrm.hxx>
+
+using namespace ::com::sun::star;
+
+static bool bInResize = false;
+
+
+namespace sdr::contact
+{
+ namespace {
+
+ /**
+ * @see #i95264#
+ *
+ * currently needed since createViewIndependentPrimitive2DSequence() is called when
+ * RecalcBoundRect() is used. There should currently no VOCs being constructed since it
+ * gets not visualized (instead the corresponding SwVirtFlyDrawObj's referencing this one
+ * are visualized).
+ */
+ class VCOfSwFlyDrawObj : public ViewContactOfSdrObj
+ {
+ protected:
+ /** This method is responsible for creating the graphical visualisation data
+ *
+ * @note ONLY based on model data
+ */
+ virtual void createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+
+ public:
+ /// basic constructor, used from SdrObject.
+ explicit VCOfSwFlyDrawObj(SwFlyDrawObj& rObj)
+ : ViewContactOfSdrObj(rObj)
+ {
+ }
+ };
+
+ }
+
+ void VCOfSwFlyDrawObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const
+ {
+ // currently gets not visualized, return empty sequence
+ }
+
+} // end of namespace sdr::contact
+
+std::unique_ptr<sdr::properties::BaseProperties> SwFlyDrawObj::CreateObjectSpecificProperties()
+{
+ // create default properties
+ return std::make_unique<sdr::properties::DefaultProperties>(*this);
+}
+
+std::unique_ptr<sdr::contact::ViewContact> SwFlyDrawObj::CreateObjectSpecificViewContact()
+{
+ // needs an own VC since createViewIndependentPrimitive2DSequence()
+ // is called when RecalcBoundRect() is used
+ return std::make_unique<sdr::contact::VCOfSwFlyDrawObj>(*this);
+}
+
+SwFlyDrawObj::SwFlyDrawObj(SdrModel& rSdrModel)
+: SdrObject(rSdrModel),
+ mbIsTextBox(false)
+{
+}
+
+SwFlyDrawObj::~SwFlyDrawObj()
+{
+}
+
+// SwFlyDrawObj - Factory-Methods
+SdrInventor SwFlyDrawObj::GetObjInventor() const
+{
+ return SdrInventor::Swg;
+}
+
+SdrObjKind SwFlyDrawObj::GetObjIdentifier() const
+{
+ return SdrObjKind::SwFlyDrawObjIdentifier;
+}
+
+// TODO: Need own primitive to get the FlyFrame paint working
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class SwVirtFlyDrawObjPrimitive : public BufferedDecompositionPrimitive2D
+ {
+ private:
+ const SwVirtFlyDrawObj& mrSwVirtFlyDrawObj;
+ const basegfx::B2DRange maOuterRange;
+
+ protected:
+ /// method which is to be used to implement the local decomposition of a 2D primitive
+ virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const override;
+
+ public:
+ SwVirtFlyDrawObjPrimitive(
+ const SwVirtFlyDrawObj& rSwVirtFlyDrawObj,
+ const basegfx::B2DRange &rOuterRange)
+ : mrSwVirtFlyDrawObj(rSwVirtFlyDrawObj),
+ maOuterRange(rOuterRange)
+ {
+ }
+
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
+
+ // override to allow callbacks to wrap_DoPaintObject
+ virtual void get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const override;
+
+ // data read access
+ const SwVirtFlyDrawObj& getSwVirtFlyDrawObj() const { return mrSwVirtFlyDrawObj; }
+ const basegfx::B2DRange& getOuterRange() const { return maOuterRange; }
+
+ /// provide unique ID
+ virtual sal_uInt32 getPrimitive2DID() const override;
+ };
+
+ }
+} // end of namespace drawinglayer::primitive2d
+
+namespace drawinglayer::primitive2d
+{
+ void SwVirtFlyDrawObjPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getOuterRange().isEmpty())
+ return;
+
+ // currently this SW object has no primitive representation. As long as this is the case,
+ // create invisible geometry to allow correct HitTest and BoundRect calculations for the
+ // object. Use a filled primitive to get 'inside' as default object hit. The special cases from
+ // the old SwVirtFlyDrawObj::CheckHit implementation are handled now in SwDrawView::PickObj;
+ // this removed the 'hack' to get a view from inside model data or to react on null-tolerance
+ // as it was done in the old implementation
+ rContainer.push_back(
+ createHiddenGeometryPrimitives2D(
+ true,
+ getOuterRange()));
+ }
+
+ bool SwVirtFlyDrawObjPrimitive::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SwVirtFlyDrawObjPrimitive& rCompare = static_cast<const SwVirtFlyDrawObjPrimitive&>(rPrimitive);
+
+ return (&getSwVirtFlyDrawObj() == &rCompare.getSwVirtFlyDrawObj()
+ && getOuterRange() == rCompare.getOuterRange());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SwVirtFlyDrawObjPrimitive::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ return getOuterRange();
+ }
+
+ void SwVirtFlyDrawObjPrimitive::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // This is the callback to keep the FlyFrame painting in SW alive as long as it
+ // is not changed to primitives. This is the method which will be called by the processors
+ // when they do not know this primitive (and they do not). Inside wrap_DoPaintObject
+ // there needs to be a test that paint is only done during SW repaints (see there).
+ // Using this mechanism guarantees the correct Z-Order of the VirtualObject-based FlyFrames.
+ getSwVirtFlyDrawObj().wrap_DoPaintObject(rViewInformation);
+
+ // call parent
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 SwVirtFlyDrawObjPrimitive::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SWVIRTFLYDRAWOBJPRIMITIVE2D;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+// AW: own sdr::contact::ViewContact (VC) sdr::contact::ViewObjectContact (VOC) needed
+// since offset is defined different from SdrVirtObj's sdr::contact::ViewContactOfVirtObj.
+// For paint, that offset is used by setting at the OutputDevice; for primitives this is
+// not possible since we have no OutputDevice, but define the geometry itself.
+
+namespace sdr::contact
+{
+ namespace {
+
+ class VCOfSwVirtFlyDrawObj : public ViewContactOfVirtObj
+ {
+ protected:
+ /** This method is responsible for creating the graphical visualisation data
+ *
+ * @note ONLY based on model data
+ */
+ virtual void createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+
+ public:
+ /// basic constructor, used from SdrObject.
+ explicit VCOfSwVirtFlyDrawObj(SwVirtFlyDrawObj& rObj)
+ : ViewContactOfVirtObj(rObj)
+ {
+ }
+
+ /// access to SwVirtFlyDrawObj
+ SwVirtFlyDrawObj& GetSwVirtFlyDrawObj() const
+ {
+ return static_cast<SwVirtFlyDrawObj&>(mrObject);
+ }
+ };
+
+ }
+} // end of namespace sdr::contact
+
+namespace sdr::contact
+{
+ void VCOfSwVirtFlyDrawObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SdrObject& rReferencedObject = GetSwVirtFlyDrawObj().GetReferencedObj();
+
+ if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) != nullptr)
+ {
+ // create an own specialized primitive which is used as repaint callpoint and HitTest
+ // for HitTest processor (see primitive implementation above)
+ const basegfx::B2DRange aOuterRange(GetSwVirtFlyDrawObj().getOuterBound());
+
+ if(!aOuterRange.isEmpty())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xPrimitive(
+ new drawinglayer::primitive2d::SwVirtFlyDrawObjPrimitive(
+ GetSwVirtFlyDrawObj(),
+ aOuterRange));
+
+ rVisitor.visit(xPrimitive);
+ }
+ }
+ }
+
+} // end of namespace sdr::contact
+
+basegfx::B2DRange SwVirtFlyDrawObj::getOuterBound() const
+{
+ basegfx::B2DRange aOuterRange;
+ const SdrObject& rReferencedObject = GetReferencedObj();
+
+ if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) != nullptr)
+ {
+ const SwFlyFrame* pFlyFrame = GetFlyFrame();
+
+ if(pFlyFrame)
+ {
+ const tools::Rectangle aOuterRectangle(pFlyFrame->getFrameArea().Pos(), pFlyFrame->getFrameArea().SSize());
+
+ if(!aOuterRectangle.IsEmpty())
+ {
+ aOuterRange.expand(basegfx::B2DTuple(aOuterRectangle.Left(), aOuterRectangle.Top()));
+ aOuterRange.expand(basegfx::B2DTuple(aOuterRectangle.Right(), aOuterRectangle.Bottom()));
+ }
+ }
+ }
+
+ return aOuterRange;
+}
+
+basegfx::B2DRange SwVirtFlyDrawObj::getInnerBound() const
+{
+ basegfx::B2DRange aInnerRange;
+ const SdrObject& rReferencedObject = GetReferencedObj();
+
+ if(dynamic_cast<const SwFlyDrawObj*>( &rReferencedObject) != nullptr)
+ {
+ const SwFlyFrame* pFlyFrame = GetFlyFrame();
+
+ if(pFlyFrame)
+ {
+ const tools::Rectangle aInnerRectangle(pFlyFrame->getFrameArea().Pos() + pFlyFrame->getFramePrintArea().Pos(), pFlyFrame->getFramePrintArea().SSize());
+
+ if(!aInnerRectangle.IsEmpty())
+ {
+ aInnerRange.expand(basegfx::B2DTuple(aInnerRectangle.Left(), aInnerRectangle.Top()));
+ aInnerRange.expand(basegfx::B2DTuple(aInnerRectangle.Right(), aInnerRectangle.Bottom()));
+ }
+ }
+ }
+
+ return aInnerRange;
+}
+
+bool SwVirtFlyDrawObj::ContainsSwGrfNode() const
+{
+ // RotGrfFlyFrame: Check if this is a SwGrfNode
+ const SwFlyFrame* pFlyFrame(GetFlyFrame());
+
+ if(nullptr != pFlyFrame && pFlyFrame->Lower() && pFlyFrame->Lower()->IsNoTextFrame())
+ {
+ const SwNoTextFrame *const pNTF(static_cast<const SwNoTextFrame*>(pFlyFrame->Lower()));
+
+ const SwGrfNode *const pGrfNd(pNTF->GetNode()->GetGrfNode());
+
+ return nullptr != pGrfNd;
+ }
+
+ return false;
+}
+
+bool SwVirtFlyDrawObj::HasLimitedRotation() const
+{
+ // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation.
+ // This is the case for SwGrfNode instances
+ return ContainsSwGrfNode();
+}
+
+void SwVirtFlyDrawObj::Rotate(const Point& rRef, Degree100 nAngle100, double sn, double cs)
+{
+ if(ContainsSwGrfNode())
+ {
+ // RotGrfFlyFrame: Here is where the positively completed rotate interaction is executed.
+ // Rotation is in 1/100th degree and may be signed (!)
+ Degree10 nAngle10 = to<Degree10>(nAngle100);
+
+ while(nAngle10 < 0_deg10)
+ {
+ nAngle10 += 3600_deg10;
+ }
+
+ SwWrtShell *pShForAngle = nAngle10 ? dynamic_cast<SwWrtShell*>(GetFlyFrame()->getRootFrame()->GetCurrShell()) : nullptr;
+ if (pShForAngle)
+ {
+ // RotGrfFlyFrame: Add transformation to placeholder object
+ Size aSize;
+ const Degree10 nOldRot(SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(aSize));
+ SwFlyFrameAttrMgr aMgr(false, pShForAngle, Frmmgr_Type::NONE, nullptr);
+
+ aMgr.SetRotation(nOldRot, (nOldRot + nAngle10) % 3600_deg10, aSize);
+ }
+ }
+ else
+ {
+ // call parent
+ SdrVirtObj::Rotate(rRef, nAngle100, sn, cs);
+ }
+}
+
+std::unique_ptr<sdr::contact::ViewContact> SwVirtFlyDrawObj::CreateObjectSpecificViewContact()
+{
+ // need an own ViewContact (VC) to allow creation of a specialized primitive
+ // for being able to visualize the FlyFrames in primitive renderers
+ return std::make_unique<sdr::contact::VCOfSwVirtFlyDrawObj>(*this);
+}
+
+SwVirtFlyDrawObj::SwVirtFlyDrawObj(
+ SdrModel& rSdrModel,
+ SdrObject& rNew,
+ SwFlyFrame* pFly)
+: SdrVirtObj(rSdrModel, rNew),
+ m_pFlyFrame(pFly)
+{
+ const SvxProtectItem &rP = m_pFlyFrame->GetFormat()->GetProtect();
+ m_bMovProt = rP.IsPosProtected();
+ m_bSizProt = rP.IsSizeProtected();
+}
+
+SwVirtFlyDrawObj::~SwVirtFlyDrawObj()
+{
+ if ( getSdrPageFromSdrObject() ) //Withdraw SdrPage the responsibility.
+ getSdrPageFromSdrObject()->RemoveObject( GetOrdNum() );
+}
+
+const SwFrameFormat *SwVirtFlyDrawObj::GetFormat() const
+{
+ return GetFlyFrame()->GetFormat();
+}
+SwFrameFormat *SwVirtFlyDrawObj::GetFormat()
+{
+ return GetFlyFrame()->GetFormat();
+}
+
+// --> OD #i102707#
+namespace
+{
+ class RestoreMapMode
+ {
+ public:
+ explicit RestoreMapMode( SwViewShell const * pViewShell )
+ : mbMapModeRestored( false )
+ , mpOutDev( pViewShell->GetOut() )
+ {
+ if ( pViewShell->getPrePostMapMode() == mpOutDev->GetMapMode() )
+ return;
+
+ mpOutDev->Push(vcl::PushFlags::MAPMODE);
+
+ GDIMetaFile* pMetaFile = mpOutDev->GetConnectMetaFile();
+ if ( pMetaFile &&
+ pMetaFile->IsRecord() && !pMetaFile->IsPause() )
+ {
+ OSL_FAIL( "MapMode restoration during meta file creation is somehow suspect - using <SetRelativeMapMode(..)>, but not sure, if correct." );
+ mpOutDev->SetRelativeMapMode( pViewShell->getPrePostMapMode() );
+ }
+ else
+ {
+ mpOutDev->SetMapMode( pViewShell->getPrePostMapMode() );
+ }
+
+ mbMapModeRestored = true;
+ };
+
+ ~RestoreMapMode()
+ {
+ if ( mbMapModeRestored )
+ {
+ mpOutDev->Pop();
+ }
+ };
+
+ private:
+ bool mbMapModeRestored;
+ VclPtr<OutputDevice> mpOutDev;
+ };
+}
+// <--
+
+void SwVirtFlyDrawObj::wrap_DoPaintObject(
+ drawinglayer::geometry::ViewInformation2D const& rViewInformation) const
+{
+ SwViewShell* pShell = m_pFlyFrame->getRootFrame()->GetCurrShell();
+
+ // Only paint when we have a current shell and a DrawingLayer paint is in progress.
+ // This avoids evtl. problems with renderers which do processing stuff,
+ // but no paints. IsPaintInProgress() depends on SW repaint, so, as long
+ // as SW paints self and calls DrawLayer() for Heaven and Hell, this will
+ // be correct
+ if ( !(pShell && pShell->IsDrawingLayerPaintInProgress()) )
+ return;
+
+ bool bDrawObject(true);
+
+ if ( !SwFlyFrame::IsPaint( const_cast<SwVirtFlyDrawObj*>(this), pShell ) )
+ {
+ bDrawObject = false;
+ }
+
+ if ( !bDrawObject )
+ return;
+
+ // if there's no viewport set, all fly-frames will be painted,
+ // which is slow, wastes memory, and can cause other trouble.
+ (void) rViewInformation; // suppress "unused parameter" warning
+ assert(comphelper::LibreOfficeKit::isActive() || !rViewInformation.getViewport().isEmpty());
+ if ( m_pFlyFrame->IsFlyInContentFrame() )
+ return;
+
+ // it is also necessary to restore the VCL MapMode from ViewInformation since e.g.
+ // the VCL PixelRenderer resets it at the used OutputDevice. Unfortunately, this
+ // excludes shears and rotates which are not expressible in MapMode.
+ // OD #i102707#
+ // new helper class to restore MapMode - restoration, only if
+ // needed and consideration of paint for meta file creation .
+ RestoreMapMode aRestoreMapModeIfNeeded( pShell );
+
+ // paint the FlyFrame (use standard VCL-Paint)
+ m_pFlyFrame->PaintSwFrame( *pShell->GetOut(), m_pFlyFrame->GetPageFrame()->getFrameArea());
+}
+
+void SwVirtFlyDrawObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const
+{
+ rInfo.bMoveAllowed =
+ rInfo.bResizeFreeAllowed = rInfo.bResizePropAllowed = true;
+
+ // RotGrfFlyFrame: Some rotation may be allowed
+ rInfo.bRotateFreeAllowed = rInfo.bRotate90Allowed = HasLimitedRotation();
+
+ rInfo.bMirrorFreeAllowed = rInfo.bMirror45Allowed =
+ rInfo.bMirror90Allowed = rInfo.bShearAllowed =
+ rInfo.bCanConvToPath = rInfo.bCanConvToPoly =
+ rInfo.bCanConvToPathLineToArea = rInfo.bCanConvToPolyLineToArea = false;
+}
+
+// SwVirtFlyDrawObj - Size Determination
+
+void SwVirtFlyDrawObj::SetRect() const
+{
+ if ( GetFlyFrame()->getFrameArea().HasArea() )
+ const_cast<SwVirtFlyDrawObj*>(this)->m_aOutRect = GetFlyFrame()->getFrameArea().SVRect();
+ else
+ const_cast<SwVirtFlyDrawObj*>(this)->m_aOutRect = tools::Rectangle();
+}
+
+const tools::Rectangle& SwVirtFlyDrawObj::GetCurrentBoundRect() const
+{
+ SetRect();
+ return m_aOutRect;
+}
+
+const tools::Rectangle& SwVirtFlyDrawObj::GetLastBoundRect() const
+{
+ return GetCurrentBoundRect();
+}
+
+void SwVirtFlyDrawObj::RecalcBoundRect()
+{
+ SetRect();
+}
+
+void SwVirtFlyDrawObj::RecalcSnapRect()
+{
+ SetRect();
+}
+
+const tools::Rectangle& SwVirtFlyDrawObj::GetSnapRect() const
+{
+ SetRect();
+ return m_aOutRect;
+}
+
+void SwVirtFlyDrawObj::SetSnapRect(const tools::Rectangle& )
+{
+ tools::Rectangle aTmp( GetLastBoundRect() );
+ SetRect();
+ SetChanged();
+ BroadcastObjectChange();
+ if (m_pUserCall!=nullptr)
+ m_pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp);
+}
+
+void SwVirtFlyDrawObj::NbcSetSnapRect(const tools::Rectangle& )
+{
+ SetRect();
+}
+
+const tools::Rectangle& SwVirtFlyDrawObj::GetLogicRect() const
+{
+ SetRect();
+ return m_aOutRect;
+}
+
+void SwVirtFlyDrawObj::SetLogicRect(const tools::Rectangle& )
+{
+ tools::Rectangle aTmp( GetLastBoundRect() );
+ SetRect();
+ SetChanged();
+ BroadcastObjectChange();
+ if (m_pUserCall!=nullptr)
+ m_pUserCall->Changed(*this, SdrUserCallType::Resize, aTmp);
+}
+
+void SwVirtFlyDrawObj::NbcSetLogicRect(const tools::Rectangle& )
+{
+ SetRect();
+}
+
+::basegfx::B2DPolyPolygon SwVirtFlyDrawObj::TakeXorPoly() const
+{
+ const tools::Rectangle aSourceRectangle(GetFlyFrame()->getFrameArea().SVRect());
+ const ::basegfx::B2DRange aSourceRange = vcl::unotools::b2DRectangleFromRectangle(aSourceRectangle);
+ ::basegfx::B2DPolyPolygon aRetval;
+
+ aRetval.append(::basegfx::utils::createPolygonFromRect(aSourceRange));
+
+ return aRetval;
+}
+
+// SwVirtFlyDrawObj::Move() and Resize()
+void SwVirtFlyDrawObj::NbcMove(const Size& rSiz)
+{
+ if(GetFlyFrame()->IsFlyFreeFrame() && static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame())
+ {
+ // RotateFlyFrame3: When we have a change and are in transformed state (e.g. rotation used),
+ // we need to fall back to the un-transformed state to keep the old code below
+ // working properly. Restore FrameArea and use aOutRect from old FrameArea.
+ TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame());
+ pTransformableSwFrame->restoreFrameAreas();
+ m_aOutRect = GetFlyFrame()->getFrameArea().SVRect();
+ }
+
+ m_aOutRect.Move( rSiz );
+ const Point aOldPos( GetFlyFrame()->getFrameArea().Pos() );
+ const Point aNewPos( m_aOutRect.TopLeft() );
+ const SwRect aFlyRect( m_aOutRect );
+
+ //If the Fly has an automatic align (right or top),
+ //so preserve the automatic.
+ SwFrameFormat *pFormat = GetFlyFrame()->GetFormat();
+ const sal_Int16 eHori = pFormat->GetHoriOrient().GetHoriOrient();
+ const sal_Int16 eVert = pFormat->GetVertOrient().GetVertOrient();
+ const sal_Int16 eRelHori = pFormat->GetHoriOrient().GetRelationOrient();
+ const sal_Int16 eRelVert = pFormat->GetVertOrient().GetRelationOrient();
+ //On paragraph bound Flys starting from the new position a new
+ //anchor must be set. Anchor and the new RelPos is calculated and
+ //placed by the Fly itself.
+ if( GetFlyFrame()->IsFlyAtContentFrame() )
+ {
+ static_cast<SwFlyAtContentFrame*>(GetFlyFrame())->SetAbsPos( aNewPos );
+ }
+ else
+ {
+ const SwFrameFormat *pTmpFormat = GetFormat();
+ const SwFormatVertOrient &rVert = pTmpFormat->GetVertOrient();
+ const SwFormatHoriOrient &rHori = pTmpFormat->GetHoriOrient();
+ tools::Long lXDiff = aNewPos.X() - aOldPos.X();
+ if( rHori.IsPosToggle() && text::HoriOrientation::NONE == eHori &&
+ !GetFlyFrame()->FindPageFrame()->OnRightPage() )
+ lXDiff = -lXDiff;
+
+ if( GetFlyFrame()->GetAnchorFrame()->IsRightToLeft() &&
+ text::HoriOrientation::NONE == eHori )
+ lXDiff = -lXDiff;
+
+ tools::Long lYDiff = aNewPos.Y() - aOldPos.Y();
+ if( GetFlyFrame()->GetAnchorFrame()->IsVertical() )
+ {
+ //lXDiff -= rVert.GetPos();
+ //lYDiff += rHori.GetPos();
+
+ if ( GetFlyFrame()->GetAnchorFrame()->IsVertLR() )
+ {
+ lXDiff += rVert.GetPos();
+ lXDiff = -lXDiff;
+ }
+ else
+ {
+ lXDiff -= rVert.GetPos();
+ lYDiff += rHori.GetPos();
+ }
+ }
+ else
+ {
+ lXDiff += rHori.GetPos();
+ lYDiff += rVert.GetPos();
+ }
+
+ if( GetFlyFrame()->GetAnchorFrame()->IsRightToLeft() &&
+ text::HoriOrientation::NONE != eHori )
+ lXDiff = GetFlyFrame()->GetAnchorFrame()->getFrameArea().Width() -
+ aFlyRect.Width() - lXDiff;
+
+ const Point aTmp( lXDiff, lYDiff );
+ GetFlyFrame()->ChgRelPos( aTmp );
+ }
+
+ SwAttrSet aSet( pFormat->GetDoc()->GetAttrPool(),
+ RES_VERT_ORIENT, RES_HORI_ORIENT );
+ SwFormatHoriOrient aHori( pFormat->GetHoriOrient() );
+ SwFormatVertOrient aVert( pFormat->GetVertOrient() );
+ bool bPut = false;
+
+ if( !GetFlyFrame()->IsFlyLayFrame() &&
+ ::GetHtmlMode(pFormat->GetDoc()->GetDocShell()) )
+ {
+ //In HTML-Mode only automatic aligns are allowed.
+ //Only we can try a snap to left/right respectively left-/right border
+ const SwFrame* pAnch = GetFlyFrame()->GetAnchorFrame();
+ bool bNextLine = false;
+
+ if( !GetFlyFrame()->IsAutoPos() || text::RelOrientation::PAGE_FRAME != aHori.GetRelationOrient() )
+ {
+ if( text::RelOrientation::CHAR == eRelHori )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::LEFT );
+ aHori.SetRelationOrient( text::RelOrientation::CHAR );
+ }
+ else
+ {
+ bNextLine = true;
+ //Horizontal Align:
+ const bool bLeftFrame =
+ aFlyRect.Left() < pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Left(),
+ bLeftPrt = aFlyRect.Left() + aFlyRect.Width() <
+ pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width()/2;
+ if ( bLeftFrame || bLeftPrt )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::LEFT );
+ aHori.SetRelationOrient( bLeftFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA );
+ }
+ else
+ {
+ const bool bRightFrame = aFlyRect.Left() >
+ pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width();
+ aHori.SetHoriOrient( text::HoriOrientation::RIGHT );
+ aHori.SetRelationOrient( bRightFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA );
+ }
+ }
+ aSet.Put( aHori );
+ }
+ //Vertical alignment simply is retained principally,
+ //only on manual align will be switched over.
+ bool bRelChar = text::RelOrientation::CHAR == eRelVert;
+ aVert.SetVertOrient( eVert != text::VertOrientation::NONE ? eVert :
+ GetFlyFrame()->IsFlyInContentFrame() ? text::VertOrientation::CHAR_CENTER :
+ bRelChar && bNextLine ? text::VertOrientation::CHAR_TOP : text::VertOrientation::TOP );
+ if( bRelChar )
+ aVert.SetRelationOrient( text::RelOrientation::CHAR );
+ else
+ aVert.SetRelationOrient( text::RelOrientation::PRINT_AREA );
+ aSet.Put( aVert );
+ bPut = true;
+ }
+
+ //We want preferably not to lose the automatic alignments.
+ if ( !bPut && bInResize )
+ {
+ if ( text::HoriOrientation::NONE != eHori )
+ {
+ aHori.SetHoriOrient( eHori );
+ aHori.SetRelationOrient( eRelHori );
+ aSet.Put( aHori );
+ bPut = true;
+ }
+ if ( text::VertOrientation::NONE != eVert )
+ {
+ aVert.SetVertOrient( eVert );
+ aVert.SetRelationOrient( eRelVert );
+ aSet.Put( aVert );
+ bPut = true;
+ }
+ }
+ if ( bPut )
+ pFormat->SetFormatAttr( aSet );
+}
+
+
+void SwVirtFlyDrawObj::NbcCrop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
+{
+ // Get Wrt Shell
+ SwWrtShell *pSh = dynamic_cast<SwWrtShell*>( GetFlyFrame()->getRootFrame()->GetCurrShell() );
+
+ if (!pSh)
+ {
+ return;
+ }
+
+ GraphicObject const *pGraphicObject = pSh->GetGraphicObj();
+
+ if (!pGraphicObject)
+ {
+ return;
+ }
+
+ // Get graphic object size in 100th of mm
+ const MapMode aMapMode100thmm(MapUnit::Map100thMM);
+ Size aGraphicSize(pGraphicObject->GetPrefSize());
+
+ if( MapUnit::MapPixel == pGraphicObject->GetPrefMapMode().GetMapUnit() )
+ {
+ aGraphicSize = Application::GetDefaultDevice()->PixelToLogic( aGraphicSize, aMapMode100thmm );
+ }
+ else
+ {
+ aGraphicSize = OutputDevice::LogicToLogic( aGraphicSize, pGraphicObject->GetPrefMapMode(), aMapMode100thmm);
+ }
+
+ if( aGraphicSize.IsEmpty() )
+ {
+ return ;
+ }
+
+ const bool bIsTransformableSwFrame(
+ GetFlyFrame()->IsFlyFreeFrame() &&
+ static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame());
+
+ if(bIsTransformableSwFrame)
+ {
+ // When we have a change and are in transformed state (e.g. rotation used),
+ // we need to fall back to the un-transformed state to keep the old code below
+ // working properly. Restore FrameArea and use aOutRect from old FrameArea.
+ TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame());
+ pTransformableSwFrame->restoreFrameAreas();
+ m_aOutRect = GetFlyFrame()->getFrameArea().SVRect();
+ }
+
+ // Compute old and new rect. This will give us the deformation to apply to
+ // the object to crop. OldRect is the inner frame, see getFullDragClone()
+ // below where getFramePrintAreaTransformation is used as object geometry for Crop
+ const tools::Rectangle aOldRect(
+ GetFlyFrame()->getFrameArea().TopLeft() + GetFlyFrame()->getFramePrintArea().TopLeft(),
+ GetFlyFrame()->getFramePrintArea().SSize());
+ const tools::Long nOldWidth(aOldRect.GetWidth());
+ const tools::Long nOldHeight(aOldRect.GetHeight());
+
+ if (!nOldWidth || !nOldHeight)
+ {
+ return;
+ }
+
+ // rRef is relative to the Crop-Action, si in X/Y-Ranges of [0.0 .. 1.0],
+ // to get the correct absolute position, transform using the old Rect
+ const Point aRef(
+ aOldRect.Left() + basegfx::fround(aOldRect.GetWidth() * rRef.getX()),
+ aOldRect.Top() + basegfx::fround(aOldRect.GetHeight() * rRef.getY()));
+
+ // apply transformation, use old ResizeRect for now
+ tools::Rectangle aNewRect( aOldRect );
+ ResizeRect(
+ aNewRect,
+ aRef,
+ Fraction(fxFact),
+ Fraction(fyFact));
+
+ // Get old values for crop in 10th of mm
+ SfxItemSetFixed<RES_GRFATR_CROPGRF, RES_GRFATR_CROPGRF> aSet( pSh->GetAttrPool() );
+ pSh->GetCurAttr( aSet );
+ SwCropGrf aCrop( aSet.Get(RES_GRFATR_CROPGRF) );
+
+ tools::Rectangle aCropRectangle(
+ convertTwipToMm100(aCrop.GetLeft()),
+ convertTwipToMm100(aCrop.GetTop()),
+ convertTwipToMm100(aCrop.GetRight()),
+ convertTwipToMm100(aCrop.GetBottom()) );
+
+ // Compute delta to apply
+ double fScaleX = ( aGraphicSize.Width() - aCropRectangle.Left() - aCropRectangle.Right() ) / static_cast<double>(nOldWidth);
+ double fScaleY = ( aGraphicSize.Height() - aCropRectangle.Top() - aCropRectangle.Bottom() ) / static_cast<double>(nOldHeight);
+
+ sal_Int32 nDiffLeft = aNewRect.Left() - aOldRect.Left();
+ sal_Int32 nDiffTop = aNewRect.Top() - aOldRect.Top();
+ sal_Int32 nDiffRight = aNewRect.Right() - aOldRect.Right();
+ sal_Int32 nDiffBottom = aNewRect.Bottom() - aOldRect.Bottom();
+
+ // Compute new values in 10th of mm
+ sal_Int32 nLeftCrop = static_cast<sal_Int32>( aCropRectangle.Left() + nDiffLeft * fScaleX );
+ sal_Int32 nTopCrop = static_cast<sal_Int32>( aCropRectangle.Top() + nDiffTop * fScaleY );
+ sal_Int32 nRightCrop = static_cast<sal_Int32>( aCropRectangle.Right() - nDiffRight * fScaleX );
+ sal_Int32 nBottomCrop = static_cast<sal_Int32>( aCropRectangle.Bottom() - nDiffBottom * fScaleY );
+
+ // Apply values
+ pSh->StartAllAction();
+ // pSh->StartUndo(SwUndoId::START);
+
+ // Set new crop values in twips
+ aCrop.SetLeft (o3tl::toTwips(nLeftCrop, o3tl::Length::mm100));
+ aCrop.SetTop (o3tl::toTwips(nTopCrop, o3tl::Length::mm100));
+ aCrop.SetRight (o3tl::toTwips(nRightCrop, o3tl::Length::mm100));
+ aCrop.SetBottom(o3tl::toTwips(nBottomCrop, o3tl::Length::mm100));
+ pSh->SetAttrItem(aCrop);
+
+ // Set new frame size
+ SwFrameFormat *pFormat = GetFormat();
+ SwFormatFrameSize aSz( pFormat->GetFrameSize() );
+ const tools::Long aNewWidth(aNewRect.GetWidth() + (m_aOutRect.GetWidth() - aOldRect.GetWidth()));
+ const tools::Long aNewHeight(aNewRect.GetHeight() + (m_aOutRect.GetHeight() - aOldRect.GetHeight()));
+ aSz.SetWidth(aNewWidth);
+ aSz.SetHeight(aNewHeight);
+ pFormat->GetDoc()->SetAttr( aSz, *pFormat );
+
+ // add move - to make result look better. Fill with defaults
+ // for the untransformed case
+ Point aNewTopLeft(aNewRect.TopLeft());
+ const Point aOldTopLeft(aOldRect.TopLeft());
+
+ if(bIsTransformableSwFrame)
+ {
+ // Need to correct the NewTopLeft position in transformed state to make
+ // the interaction look correct. First, extract rotation
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ GetFlyFrame()->getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // calc the center of the unchanged object
+ const basegfx::B2DPoint aFormerCenter(
+ GetFlyFrame()->getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5));
+
+ // define the existing rotation around that former center
+ const basegfx::B2DHomMatrix aRotFormerCenter(
+ basegfx::utils::createRotateAroundPoint(
+ aFormerCenter.getX(),
+ aFormerCenter.getY(),
+ fRotate));
+
+ // use the new center of the unrotated object, rotate it around the
+ // former center
+ const Point aNewCenter(aNewRect.Center());
+ const basegfx::B2DPoint aRotNewCenter(
+ aRotFormerCenter * basegfx::B2DPoint(aNewCenter.X(), aNewCenter.Y()));
+
+ // Create the new TopLeft of the unrotated, cropped object by creating
+ // as if re-creating the unrotated geometry
+ aNewTopLeft = Point(
+ basegfx::fround(aRotNewCenter.getX() - (0.5 * aNewRect.getWidth())),
+ basegfx::fround(aRotNewCenter.getY() - (0.5 * aNewRect.getHeight())));
+ }
+
+ // check if we have movement and execute if yes
+ const Size aDeltaMove(
+ aNewTopLeft.X() - aOldTopLeft.X(),
+ aNewTopLeft.Y() - aOldTopLeft.Y());
+
+ if(0 != aDeltaMove.Width() || 0 != aDeltaMove.Height())
+ {
+ NbcMove(aDeltaMove);
+ }
+
+ // pSh->EndUndo(SwUndoId::END);
+ pSh->EndAllAction();
+}
+
+void SwVirtFlyDrawObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ const SwFrame* pTmpFrame = GetFlyFrame()->GetAnchorFrame();
+
+ if( !pTmpFrame )
+ {
+ pTmpFrame = GetFlyFrame();
+ }
+
+ const bool bVertX(pTmpFrame->IsVertical());
+ const bool bRTL(pTmpFrame->IsRightToLeft());
+ const bool bVertL2RX(pTmpFrame->IsVertLR());
+ const bool bUseRightEdge((bVertX && !bVertL2RX ) || bRTL);
+ const bool bIsTransformableSwFrame(
+ GetFlyFrame()->IsFlyFreeFrame() &&
+ static_cast< SwFlyFreeFrame* >(GetFlyFrame())->isTransformableSwFrame());
+
+ if(bIsTransformableSwFrame)
+ {
+ // When we have a change in transformed state, we need to fall back to the
+ // state without possible transformations.
+ // In the Resize case to correctly handle the changes, apply to the transformation
+ // and extract the new, untransformed state from that modified transformation
+ basegfx::B2DHomMatrix aNewMat(GetFlyFrame()->getFrameAreaTransformation());
+ const basegfx::B2DPoint aRef(rRef.X(), rRef.Y());
+
+ // apply state to already valid transformation
+ aNewMat.translate(-aRef.getX(), -aRef.getY());
+ aNewMat.scale(double(xFact), double(yFact));
+ aNewMat.translate(aRef.getX(), aRef.getY());
+
+ // get center of transformed state
+ const basegfx::B2DPoint aCenter(aNewMat * basegfx::B2DPoint(0.5, 0.5));
+
+ // decompose to extract scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aNewMat.decompose(aScale, aTranslate, fRotate, fShearX);
+ const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
+
+ // create new modified, but untransformed OutRect
+ m_aOutRect = tools::Rectangle(
+ basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())),
+ basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())),
+ basegfx::fround(aCenter.getX() + (0.5 * aAbsScale.getX())),
+ basegfx::fround(aCenter.getY() + (0.5 * aAbsScale.getY())));
+
+ // restore FrameAreas so that actions below not adapted to new
+ // full transformations take the correct actions
+ TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(GetFlyFrame())->getTransformableSwFrame());
+ pTransformableSwFrame->restoreFrameAreas();
+ }
+ else
+ {
+ ResizeRect( m_aOutRect, rRef, xFact, yFact );
+ }
+
+ // Position may also change, remember old one. This is now already
+ // the one in the unrotated, old coordinate system
+ Point aOldPos(bUseRightEdge ? GetFlyFrame()->getFrameArea().TopRight() : GetFlyFrame()->getFrameArea().Pos());
+
+ // get target size in old coordinate system
+ Size aSz( m_aOutRect.Right() - m_aOutRect.Left() + 1, m_aOutRect.Bottom()- m_aOutRect.Top() + 1 );
+
+ // compare with restored FrameArea
+ if( aSz != GetFlyFrame()->getFrameArea().SSize() )
+ {
+ //The width of the columns should not be too narrow
+ if ( GetFlyFrame()->Lower() && GetFlyFrame()->Lower()->IsColumnFrame() )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), GetFlyFrame() );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ tools::Long nMin = rAttrs.CalcLeftLine()+rAttrs.CalcRightLine();
+ const SwFormatCol& rCol = rAttrs.GetAttrSet().GetCol();
+ if ( rCol.GetColumns().size() > 1 )
+ {
+ for ( const auto &rC : rCol.GetColumns() )
+ {
+ nMin += rC.GetLeft() + rC.GetRight() + MINFLY;
+ }
+ nMin -= MINFLY;
+ }
+ aSz.setWidth( std::max( aSz.Width(), nMin ) );
+ }
+
+ SwFrameFormat *pFormat = GetFormat();
+ const SwFormatFrameSize aOldFrameSz( pFormat->GetFrameSize() );
+ GetFlyFrame()->ChgSize( aSz );
+ SwFormatFrameSize aFrameSz( pFormat->GetFrameSize() );
+
+ if ( aFrameSz.GetWidthPercent() || aFrameSz.GetHeightPercent() )
+ {
+ tools::Long nRelWidth, nRelHeight;
+ const SwFrame *pRel = GetFlyFrame()->IsFlyLayFrame() ?
+ GetFlyFrame()->GetAnchorFrame() :
+ GetFlyFrame()->GetAnchorFrame()->GetUpper();
+ const SwViewShell *pSh = GetFlyFrame()->getRootFrame()->GetCurrShell();
+
+ if ( pSh && pRel->IsBodyFrame() &&
+ pSh->GetViewOptions()->getBrowseMode() &&
+ pSh->VisArea().HasArea() )
+ {
+ nRelWidth = pSh->GetBrowseWidth();
+ nRelHeight = pSh->VisArea().Height();
+ const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
+ nRelHeight -= 2*aBorder.Height();
+ }
+ else
+ {
+ nRelWidth = pRel->getFramePrintArea().Width();
+ nRelHeight = pRel->getFramePrintArea().Height();
+ }
+
+ if ( aFrameSz.GetWidthPercent() && aFrameSz.GetWidthPercent() != SwFormatFrameSize::SYNCED &&
+ aOldFrameSz.GetWidth() != aFrameSz.GetWidth() )
+ {
+ aFrameSz.SetWidthPercent( sal_uInt8(aSz.Width() * 100.0 / nRelWidth + 0.5) );
+ }
+
+ if ( aFrameSz.GetHeightPercent() && aFrameSz.GetHeightPercent() != SwFormatFrameSize::SYNCED &&
+ aOldFrameSz.GetHeight() != aFrameSz.GetHeight() )
+ {
+ aFrameSz.SetHeightPercent( sal_uInt8(aSz.Height() * 100.0 / nRelHeight + 0.5) );
+ }
+
+ pFormat->GetDoc()->SetAttr( aFrameSz, *pFormat );
+ }
+ }
+
+ //Position can also be changed, get new one
+ const Point aNewPos(bUseRightEdge ? m_aOutRect.Right() + 1 : m_aOutRect.Left(), m_aOutRect.Top());
+
+ if ( aNewPos == aOldPos )
+ return;
+
+ // Former late change in aOutRect by ChgSize
+ // is now taken into account directly by calculating
+ // aNewPos *after* calling ChgSize (see old code).
+ // Still need to adapt aOutRect since the 'Move' is already applied
+ // here (see ResizeRect) and it's the same SdrObject
+ const Size aDeltaMove(
+ aNewPos.X() - aOldPos.X(),
+ aNewPos.Y() - aOldPos.Y());
+ m_aOutRect.Move(-aDeltaMove.Width(), -aDeltaMove.Height());
+
+ // Now, move as needed (no empty delta which was a hack anyways)
+ if(bIsTransformableSwFrame)
+ {
+ // need to save aOutRect to FrameArea, will be restored to aOutRect in
+ // SwVirtFlyDrawObj::NbcMove currently for TransformableSwFrames
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*GetFlyFrame());
+ aFrm.setSwRect(SwRect(m_aOutRect));
+ }
+
+ // keep old hack - not clear what happens here
+ bInResize = true;
+ NbcMove(aDeltaMove);
+ bInResize = false;
+}
+
+void SwVirtFlyDrawObj::Move(const Size& rSiz)
+{
+ NbcMove( rSiz );
+ SetChanged();
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
+}
+
+void SwVirtFlyDrawObj::Resize(const Point& rRef,
+ const Fraction& xFact, const Fraction& yFact, bool /*bUnsetRelative*/)
+{
+ NbcResize( rRef, xFact, yFact );
+ SetChanged();
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
+}
+
+void SwVirtFlyDrawObj::Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
+{
+ NbcCrop( rRef, fxFact, fyFact );
+ SetChanged();
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
+}
+
+// RotGrfFlyFrame: Helper to access possible rotation of Graphic contained in FlyFrame
+Degree10 SwVirtFlyDrawObj::getPossibleRotationFromFraphicFrame(Size& rSize) const
+{
+ Degree10 nRetval;
+ const SwNoTextFrame* pNoTx = dynamic_cast< const SwNoTextFrame* >(GetFlyFrame()->Lower());
+
+ if(pNoTx)
+ {
+ SwNoTextNode& rNoTNd = const_cast< SwNoTextNode& >(*static_cast<const SwNoTextNode*>(pNoTx->GetNode()));
+ SwGrfNode* pGrfNd = rNoTNd.GetGrfNode();
+
+ if(nullptr != pGrfNd)
+ {
+ const SwAttrSet& rSet = pGrfNd->GetSwAttrSet();
+ const SwRotationGrf& rRotation = rSet.GetRotationGrf();
+
+ rSize = rRotation.GetUnrotatedSize();
+ nRetval = rRotation.GetValue();
+ }
+ }
+
+ return nRetval;
+}
+
+Degree100 SwVirtFlyDrawObj::GetRotateAngle() const
+{
+ if(ContainsSwGrfNode())
+ {
+ Size aSize;
+ return to<Degree100>(getPossibleRotationFromFraphicFrame(aSize));
+ }
+ else
+ {
+ return SdrVirtObj::GetRotateAngle();
+ }
+}
+
+SdrObjectUniquePtr SwVirtFlyDrawObj::getFullDragClone() const
+{
+ // call parent
+ SdrObjectUniquePtr pRetval = SdrVirtObj::getFullDragClone();
+
+ if(pRetval && GetFlyFrame() && ContainsSwGrfNode())
+ {
+ // RotGrfFlyFrame3: get inner bounds/transformation
+ const basegfx::B2DHomMatrix aTargetTransform(GetFlyFrame()->getFramePrintAreaTransformation());
+
+ pRetval->TRSetBaseGeometry(aTargetTransform, basegfx::B2DPolyPolygon());
+ }
+
+ return pRetval;
+}
+
+void SwVirtFlyDrawObj::addCropHandles(SdrHdlList& rTarget) const
+{
+ // RotGrfFlyFrame: Adapt to possible rotated Graphic contained in FlyFrame
+ if(!GetFlyFrame()->getFrameArea().HasArea())
+ return;
+
+ // Use InnerBound, OuterBound (same as GetFlyFrame()->getFrameArea().SVRect())
+ // may have a distance to InnerBound which needs to be taken into account.
+ // The Graphic is mapped to InnerBound, as is the rotated Graphic.
+ const basegfx::B2DRange aTargetRange(getInnerBound());
+
+ if(aTargetRange.isEmpty())
+ return;
+
+ // RotGrfFlyFrame3: get inner bounds/transformation
+ const basegfx::B2DHomMatrix aTargetTransform(GetFlyFrame()->getFramePrintAreaTransformation());
+
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate(0.0);
+ double fShearX(0.0);
+ aTargetTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+ basegfx::B2DPoint aPos;
+
+ aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperLeft, fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(0.5, 0.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Upper, fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::UpperRight, fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(0.0, 0.5);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Left , fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(1.0, 0.5);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Right, fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(0.0, 1.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerLeft, fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(0.5, 1.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::Lower, fShearX, fRotate));
+ aPos = aTargetTransform * basegfx::B2DPoint(1.0, 1.0);
+ rTarget.AddHdl(std::make_unique<SdrCropHdl>(Point(basegfx::fround(aPos.getX()), basegfx::fround(aPos.getY())), SdrHdlKind::LowerRight, fShearX, fRotate));
+}
+
+// Macro
+
+PointerStyle SwVirtFlyDrawObj::GetMacroPointer(
+ const SdrObjMacroHitRec& ) const
+{
+ return PointerStyle::RefHand;
+}
+
+bool SwVirtFlyDrawObj::HasMacro() const
+{
+ const SwFormatURL &rURL = m_pFlyFrame->GetFormat()->GetURL();
+ return rURL.GetMap() || !rURL.GetURL().isEmpty();
+}
+
+SdrObject* SwVirtFlyDrawObj::CheckMacroHit( const SdrObjMacroHitRec& rRec ) const
+{
+ const SwFormatURL &rURL = m_pFlyFrame->GetFormat()->GetURL();
+ if( rURL.GetMap() || !rURL.GetURL().isEmpty() )
+ {
+ SwRect aRect;
+ if ( m_pFlyFrame->Lower() && m_pFlyFrame->Lower()->IsNoTextFrame() )
+ {
+ aRect = m_pFlyFrame->getFramePrintArea();
+ aRect += m_pFlyFrame->getFrameArea().Pos();
+ }
+ else
+ aRect = m_pFlyFrame->getFrameArea();
+
+ if( aRect.Contains( rRec.aPos ) )
+ {
+ aRect.Pos().setX(aRect.Pos().getX() + rRec.nTol);
+ aRect.Pos().setY(aRect.Pos().getY() + rRec.nTol);
+ aRect.AddHeight( -(2 * rRec.nTol) );
+ aRect.AddWidth( -(2 * rRec.nTol) );
+
+ if( aRect.Contains( rRec.aPos ) )
+ {
+ if( !rURL.GetMap() ||
+ m_pFlyFrame->GetFormat()->GetIMapObject( rRec.aPos, m_pFlyFrame ))
+ return const_cast<SwVirtFlyDrawObj*>(this);
+
+ return nullptr;
+ }
+ }
+ }
+ return SdrObject::CheckMacroHit( rRec );
+}
+
+bool SwVirtFlyDrawObj::IsTextBox() const
+{
+ return SwTextBoxHelper::isTextBox(GetFormat(), RES_FLYFRMFMT, this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dobjfac.cxx b/sw/source/core/draw/dobjfac.cxx
new file mode 100644
index 000000000..818b870da
--- /dev/null
+++ b/sw/source/core/draw/dobjfac.cxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <dobjfac.hxx>
+#include <dflyobj.hxx>
+#include <osl/diagnose.h>
+
+SwObjectFactory aSwObjectFactory;
+
+IMPL_STATIC_LINK(
+ SwObjectFactory, MakeObject, SdrObjCreatorParams, aParams, SdrObject* )
+{
+ if ( aParams.nInventor == SdrInventor::Swg )
+ {
+ // No switch, there's only one at the moment
+ OSL_ENSURE( aParams.nObjIdentifier == SdrObjKind::SwFlyDrawObjIdentifier,
+ "Wrong inventor or identifier" );
+ return new SwFlyDrawObj(aParams.rSdrModel);
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dpage.cxx b/sw/source/core/draw/dpage.cxx
new file mode 100644
index 000000000..fc1c1ebb7
--- /dev/null
+++ b/sw/source/core/draw/dpage.cxx
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/flditem.hxx>
+#include <vcl/imapobj.hxx>
+#include <svl/urihelper.hxx>
+#include <sfx2/sfxhelp.hxx>
+#include <vcl/help.hxx>
+#include <svx/svdview.hxx>
+#include <osl/diagnose.h>
+#include <fmturl.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <viewimp.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <viewsh.hxx>
+#include <drawdoc.hxx>
+#include <dpage.hxx>
+#include <dcontact.hxx>
+#include <dflyobj.hxx>
+#include <docsh.hxx>
+#include <flyfrm.hxx>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::frame;
+
+SwDPage::SwDPage(SwDrawModel& rNewModel, bool bMasterPage)
+: FmFormPage(rNewModel, bMasterPage),
+ m_pDoc(&rNewModel.GetDoc())
+{
+}
+
+SwDPage::~SwDPage()
+{
+}
+
+void SwDPage::lateInit(const SwDPage& rSrcPage)
+{
+ FmFormPage::lateInit( rSrcPage );
+
+ if ( rSrcPage.m_pGridLst )
+ {
+ m_pGridLst.reset( new SdrPageGridFrameList );
+ for ( sal_uInt16 i = 0; i != rSrcPage.m_pGridLst->GetCount(); ++i )
+ m_pGridLst->Insert( ( *rSrcPage.m_pGridLst )[ i ] );
+ }
+}
+
+rtl::Reference<SdrPage> SwDPage::CloneSdrPage(SdrModel& rTargetModel) const
+{
+ SwDrawModel& rSwDrawModel(static_cast< SwDrawModel& >(rTargetModel));
+ rtl::Reference<SwDPage> pClonedSwDPage(
+ new SwDPage(
+ rSwDrawModel,
+ IsMasterPage()));
+ pClonedSwDPage->lateInit(*this);
+ return pClonedSwDPage;
+}
+
+SdrObject* SwDPage::ReplaceObject( SdrObject* pNewObj, size_t nObjNum )
+{
+ SdrObject *pOld = GetObj( nObjNum );
+ OSL_ENSURE( pOld, "Oups, Object not replaced" );
+ SdrObjUserCall* pContact;
+ if ( nullptr != ( pContact = GetUserCall(pOld) ) &&
+ RES_DRAWFRMFMT == static_cast<SwContact*>(pContact)->GetFormat()->Which())
+ static_cast<SwDrawContact*>(pContact)->ChangeMasterObject( pNewObj );
+ return FmFormPage::ReplaceObject( pNewObj, nObjNum );
+}
+
+static void InsertGridFrame( SdrPageGridFrameList *pLst, const SwFrame *pPg )
+{
+ SwRect aPrt( pPg->getFramePrintArea() );
+ aPrt += pPg->getFrameArea().Pos();
+ const tools::Rectangle aUser( aPrt.SVRect() );
+ const tools::Rectangle aPaper( pPg->getFrameArea().SVRect() );
+ pLst->Insert( SdrPageGridFrame( aPaper, aUser ) );
+}
+
+const SdrPageGridFrameList* SwDPage::GetGridFrameList(
+ const SdrPageView* pPV, const tools::Rectangle *pRect ) const
+{
+ SwViewShell* pSh = static_cast< SwDrawModel& >(getSdrModelFromSdrPage()).GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ if(pSh)
+ {
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ {
+ if(rShell.Imp()->GetPageView() == pPV)
+ {
+ pSh = &rShell;
+ break;
+ }
+ }
+ if ( m_pGridLst )
+ const_cast<SwDPage*>(this)->m_pGridLst->Clear();
+ else
+ const_cast<SwDPage*>(this)->m_pGridLst.reset( new SdrPageGridFrameList );
+
+ if ( pRect )
+ {
+ //The drawing demands all pages which overlap with the rest.
+ const SwRect aRect( *pRect );
+ const SwFrame *pPg = pSh->GetLayout()->Lower();
+ do
+ { if ( pPg->getFrameArea().Overlaps( aRect ) )
+ ::InsertGridFrame( const_cast<SwDPage*>(this)->m_pGridLst.get(), pPg );
+ pPg = pPg->GetNext();
+ } while ( pPg );
+ }
+ else
+ {
+ //The drawing demands all visible pages
+ const SwFrame *pPg = pSh->Imp()->GetFirstVisPage(pSh->GetOut());
+ if ( pPg )
+ do
+ { ::InsertGridFrame( const_cast<SwDPage*>(this)->m_pGridLst.get(), pPg );
+ pPg = pPg->GetNext();
+ } while ( pPg && pPg->getFrameArea().Overlaps( pSh->VisArea() ) );
+ }
+ }
+ return m_pGridLst.get();
+}
+
+bool SwDPage::RequestHelp( vcl::Window* pWindow, SdrView const * pView,
+ const HelpEvent& rEvt )
+{
+ assert( m_pDoc );
+
+ bool bContinue = true;
+
+ if( rEvt.GetMode() & ( HelpEventMode::QUICK | HelpEventMode::BALLOON ))
+ {
+ Point aPos( rEvt.GetMousePosPixel() );
+ aPos = pWindow->ScreenToOutputPixel( aPos );
+ aPos = pWindow->PixelToLogic( aPos );
+
+ SdrPageView* pPV;
+ SdrObject* pObj = pView->PickObj(aPos, 0, pPV, SdrSearchOptions::PICKMACRO);
+ SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
+ OUString sText;
+ tools::Rectangle aPixRect;
+ bool bTooltip = false;
+ if (pDrawObj)
+ {
+ SwFlyFrame *pFly = pDrawObj->GetFlyFrame();
+
+ aPixRect = pWindow->LogicToPixel(pFly->getFrameArea().SVRect());
+
+ const SwFormatURL &rURL = pFly->GetFormat()->GetURL();
+ if (!pFly->GetFormat()->GetObjTooltip().isEmpty())
+ {
+ // Tooltips have priority over URLs.
+ sText = pFly->GetFormat()->GetObjTooltip();
+ bTooltip = true;
+ }
+ else if( rURL.GetMap() )
+ {
+ IMapObject *pTmpObj = pFly->GetFormat()->GetIMapObject( aPos, pFly );
+ if( pTmpObj )
+ {
+ sText = pTmpObj->GetAltText();
+ if ( sText.isEmpty() )
+ sText = URIHelper::removePassword( pTmpObj->GetURL(),
+ INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::Unambiguous);
+ }
+ }
+ else if ( !rURL.GetURL().isEmpty() )
+ {
+ sText = URIHelper::removePassword( rURL.GetURL(),
+ INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::Unambiguous);
+
+ if( rURL.IsServerMap() )
+ {
+ // then append the relative pixel position!!
+ Point aPt( aPos );
+ aPt -= pFly->getFrameArea().Pos();
+ // without MapMode-Offset !!!!!
+ // without MapMode-Offset, without Offset, w ... !!!!!
+ aPt = pWindow->LogicToPixel(
+ aPt, MapMode( MapUnit::MapTwip ) );
+ sText += "?" + OUString::number( aPt.getX() )
+ + "," + OUString::number( aPt.getY() );
+ }
+ }
+ }
+ else
+ {
+ SdrViewEvent aVEvt;
+ MouseEvent aMEvt(pWindow->ScreenToOutputPixel(rEvt.GetMousePosPixel()), 1,
+ MouseEventModifiers::NONE, MOUSE_LEFT);
+ pView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt);
+ if (aVEvt.meEvent == SdrEventKind::ExecuteUrl)
+ {
+ sText = aVEvt.mpURLField->GetURL();
+ aPixRect = pWindow->LogicToPixel(aVEvt.mpObj->GetLogicRect());
+ }
+ }
+
+ if (!sText.isEmpty())
+ {
+ // #i80029#
+ bool bExecHyperlinks = m_pDoc->GetDocShell()->IsReadOnly();
+ if (!bExecHyperlinks && !bTooltip)
+ sText = SfxHelp::GetURLHelpText(sText);
+
+ // then display the help:
+ tools::Rectangle aScreenRect(pWindow->OutputToScreenPixel(aPixRect.TopLeft()),
+ pWindow->OutputToScreenPixel(aPixRect.BottomRight()));
+
+ if (rEvt.GetMode() & HelpEventMode::BALLOON)
+ Help::ShowBalloon(pWindow, rEvt.GetMousePosPixel(), aScreenRect, sText);
+ else
+ Help::ShowQuickHelp(pWindow, aScreenRect, sText);
+ bContinue = false;
+ }
+ }
+
+ if( bContinue )
+ bContinue = !FmFormPage::RequestHelp( pWindow, pView, rEvt );
+
+ return bContinue;
+}
+
+Reference< XInterface > SwDPage::createUnoPage()
+{
+ assert( m_pDoc );
+
+ Reference < XInterface > xRet;
+ SwDocShell* pDocShell = m_pDoc->GetDocShell();
+ if ( pDocShell )
+ {
+ Reference<XModel> xModel = pDocShell->GetBaseModel();
+ Reference<XDrawPageSupplier> xPageSupp(xModel, UNO_QUERY);
+ xRet = xPageSupp->getDrawPage();
+ }
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/drawdoc.cxx b/sw/source/core/draw/drawdoc.cxx
new file mode 100644
index 000000000..4c3050ebf
--- /dev/null
+++ b/sw/source/core/draw/drawdoc.cxx
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <svx/svxids.hrc>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <svx/drawitem.hxx>
+#include <osl/diagnose.h>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <dpage.hxx>
+#include <docsh.hxx>
+#include <hintids.hxx>
+#include <DocumentSettingManager.hxx>
+
+using namespace com::sun::star;
+
+// Constructor
+SwDrawModel::SwDrawModel(SwDoc& rDoc)
+ : FmFormModel(&rDoc.GetAttrPool(), rDoc.GetDocShell())
+ , m_rDoc(rDoc)
+{
+ SetScaleUnit( MapUnit::MapTwip );
+ SetSwapGraphics();
+
+ // use common InitDrawModelAndDocShell which will set the associations as needed,
+ // including SvxColorTableItem with WhichID SID_COLOR_TABLE
+ InitDrawModelAndDocShell(m_rDoc.GetDocShell(), this);
+
+ // copy all the default values to the SdrModel
+ SfxItemPool* pSdrPool = m_rDoc.GetAttrPool().GetSecondaryPool();
+ if( pSdrPool )
+ {
+ const sal_uInt16 aWhichRanges[] =
+ {
+ RES_CHRATR_BEGIN, RES_CHRATR_END,
+ RES_PARATR_BEGIN, RES_PARATR_END,
+ 0
+ };
+
+ SfxItemPool& rDocPool = m_rDoc.GetAttrPool();
+ sal_uInt16 nEdtWhich, nSlotId;
+ const SfxPoolItem* pItem;
+ for( const sal_uInt16* pRangeArr = aWhichRanges;
+ *pRangeArr; pRangeArr += 2 )
+ for( sal_uInt16 nW = *pRangeArr, nEnd = *(pRangeArr+1);
+ nW < nEnd; ++nW )
+ if( nullptr != (pItem = rDocPool.GetPoolDefaultItem( nW )) &&
+ 0 != (nSlotId = rDocPool.GetSlotId( nW ) ) &&
+ nSlotId != nW &&
+ 0 != (nEdtWhich = pSdrPool->GetWhich( nSlotId )) &&
+ nSlotId != nEdtWhich )
+ {
+ std::unique_ptr<SfxPoolItem> pCpy(pItem->Clone());
+ pCpy->SetWhich( nEdtWhich );
+ pSdrPool->SetPoolDefaultItem( *pCpy );
+ }
+ }
+
+ SetForbiddenCharsTable(m_rDoc.GetDocumentSettingManager().getForbiddenCharacterTable());
+ // Implementation for asian compression
+ SetCharCompressType( m_rDoc.GetDocumentSettingManager().getCharacterCompressionType() );
+}
+
+// Destructor
+
+SwDrawModel::~SwDrawModel()
+{
+ Broadcast(SdrHint(SdrHintKind::ModelCleared));
+
+ ClearModel(true);
+}
+
+/** Create a new page (SdPage) and return a pointer to it back.
+ *
+ * The drawing engine is using this method while loading for the creating of
+ * pages (whose type it not even know, because they are inherited from SdrPage).
+ *
+ * @return Pointer to the new page.
+ */
+rtl::Reference<SdrPage> SwDrawModel::AllocPage(bool bMasterPage)
+{
+ rtl::Reference<SwDPage> pPage = new SwDPage(*this, bMasterPage);
+ pPage->SetName("Controls");
+ return pPage;
+}
+
+uno::Reference<embed::XStorage> SwDrawModel::GetDocumentStorage() const
+{
+ return m_rDoc.GetDocStorage();
+}
+
+uno::Reference< uno::XInterface > SwDrawModel::createUnoModel()
+{
+ uno::Reference< uno::XInterface > xModel;
+
+ try
+ {
+ if ( GetDoc().GetDocShell() )
+ {
+ xModel = GetDoc().GetDocShell()->GetModel();
+ }
+ }
+ catch( uno::RuntimeException& )
+ {
+ OSL_FAIL( "<SwDrawModel::createUnoModel()> - could *not* retrieve model at <SwDocShell>" );
+ }
+
+ return xModel;
+}
+
+void SwDrawModel::PutAreaListItems(SfxItemSet& rSet) const
+{
+ rSet.Put(SvxColorListItem(GetColorList(), SID_COLOR_TABLE));
+ rSet.Put(SvxGradientListItem(GetGradientList(), SID_GRADIENT_LIST));
+ rSet.Put(SvxHatchListItem(GetHatchList(), SID_HATCH_LIST));
+ rSet.Put(SvxBitmapListItem(GetBitmapList(), SID_BITMAP_LIST));
+ rSet.Put(SvxPatternListItem(GetPatternList(), SID_PATTERN_LIST));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/draw/dview.cxx b/sw/source/core/draw/dview.cxx
new file mode 100644
index 000000000..93b787c91
--- /dev/null
+++ b/sw/source/core/draw/dview.cxx
@@ -0,0 +1,1019 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <hintids.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/fmmodel.hxx>
+#include <sot/exchange.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <tools/globname.hxx>
+#include <editeng/outliner.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrm.hxx>
+#include <frmfmt.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <textboxhelper.hxx>
+#include <viewsh.hxx>
+#include <viewimp.hxx>
+#include <dview.hxx>
+#include <doc.hxx>
+#include <mdiexp.hxx>
+#include <ndole.hxx>
+#include <ndgrf.hxx>
+#include <fmtanchr.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <com/sun/star/embed/Aspects.hpp>
+
+#include <vector>
+
+#include <sortedobjs.hxx>
+
+using namespace com::sun::star;
+
+namespace {
+
+class SwSdrHdl : public SdrHdl
+{
+public:
+ SwSdrHdl(const Point& rPnt, bool bTopRight ) :
+ SdrHdl( rPnt, bTopRight ? SdrHdlKind::Anchor_TR : SdrHdlKind::Anchor ) {}
+ virtual bool IsFocusHdl() const override;
+};
+
+}
+
+bool SwSdrHdl::IsFocusHdl() const
+{
+ if( SdrHdlKind::Anchor == eKind || SdrHdlKind::Anchor_TR == eKind )
+ return true;
+ return SdrHdl::IsFocusHdl();
+}
+
+static const SwFrame *lcl_FindAnchor( const SdrObject *pObj, bool bAll )
+{
+ const SwVirtFlyDrawObj *pVirt = dynamic_cast< const SwVirtFlyDrawObj *>( pObj );
+ if ( pVirt )
+ {
+ if ( bAll || !pVirt->GetFlyFrame()->IsFlyInContentFrame() )
+ return pVirt->GetFlyFrame()->GetAnchorFrame();
+ }
+ else
+ {
+ const SwDrawContact *pCont = static_cast<const SwDrawContact*>(GetUserCall(pObj));
+ if ( pCont )
+ return pCont->GetAnchorFrame( pObj );
+ }
+ return nullptr;
+}
+
+SwDrawView::SwDrawView(
+ SwViewShellImp& rI,
+ FmFormModel& rFmFormModel,
+ OutputDevice* pOutDev)
+: FmFormView(rFmFormModel, pOutDev),
+ m_rImp( rI )
+{
+ SetPageVisible( false );
+ SetBordVisible( false );
+ SetGridVisible( false );
+ SetHlplVisible( false );
+ SetGlueVisible( false );
+ SetFrameDragSingles();
+ SetSwapAsynchron();
+
+ EnableExtendedKeyInputDispatcher( false );
+ EnableExtendedMouseEventDispatcher( false );
+
+ SetHitTolerancePixel( GetMarkHdlSizePixel()/2 );
+
+ SetPrintPreview( rI.GetShell()->IsPreview() );
+
+ // #i73602# Use default from the configuration
+ SetBufferedOverlayAllowed(SvtOptionsDrawinglayer::IsOverlayBuffer_Writer());
+
+ // #i74769#, #i75172# Use default from the configuration
+ SetBufferedOutputAllowed(SvtOptionsDrawinglayer::IsPaintBuffer_Writer());
+}
+
+// #i99665#
+bool SwDrawView::IsAntiAliasing()
+{
+ return SvtOptionsDrawinglayer::IsAntiAliasing();
+}
+
+static SdrObject* impLocalHitCorrection(SdrObject* pRetval, const Point& rPnt, sal_uInt16 nTol, const SdrMarkList &rMrkList)
+{
+ if(!nTol)
+ {
+ // the old method forced back to outer bounds test when nTol == 0, so
+ // do not try to correct when nTol is not set (used from HelpContent)
+ }
+ else
+ {
+ // rebuild logic from former SwVirtFlyDrawObj::CheckSdrObjectHit. This is needed since
+ // the SdrObject-specific CheckHit implementations are now replaced with primitives and
+ // 'tricks' like in the old implementation (e.g. using a view from a model-data class to
+ // detect if object is selected) are no longer valid.
+ // The standard primitive hit-test for SwVirtFlyDrawObj now is the outer bound. The old
+ // implementation reduced this excluding the inner bound when the object was not selected.
+ SwVirtFlyDrawObj* pSwVirtFlyDrawObj = dynamic_cast< SwVirtFlyDrawObj* >(pRetval);
+
+ if(pSwVirtFlyDrawObj)
+ {
+ if(pSwVirtFlyDrawObj->GetFlyFrame()->Lower() && pSwVirtFlyDrawObj->GetFlyFrame()->Lower()->IsNoTextFrame())
+ {
+ // the old method used IsNoTextFrame (should be for SW's own OLE and
+ // graphic's) to accept hit only based on outer bounds; nothing to do
+ }
+ else
+ {
+ // check if the object is selected in this view
+ const size_t nMarkCount(rMrkList.GetMarkCount());
+ bool bSelected(false);
+
+ for(size_t a = 0; !bSelected && a < nMarkCount; ++a)
+ {
+ if(pSwVirtFlyDrawObj == rMrkList.GetMark(a)->GetMarkedSdrObj())
+ {
+ bSelected = true;
+ }
+ }
+
+ if(!bSelected)
+ {
+ // when not selected, the object is not hit when hit position is inside
+ // inner range. Get and shrink inner range
+ basegfx::B2DRange aInnerBound(pSwVirtFlyDrawObj->getInnerBound());
+
+ aInnerBound.grow(-1.0 * nTol);
+
+ if(aInnerBound.isInside(basegfx::B2DPoint(rPnt.X(), rPnt.Y())))
+ {
+ // exclude this hit
+ pRetval = nullptr;
+ }
+ }
+ }
+ }
+ }
+
+ return pRetval;
+}
+
+SdrObject* SwDrawView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const
+{
+ // call parent
+ SdrObject* pRetval = FmFormView::CheckSingleSdrObjectHit(rPnt, nTol, pObj, pPV, nOptions, pMVisLay);
+
+ if(pRetval)
+ {
+ // override to allow extra handling when picking SwVirtFlyDrawObj's
+ pRetval = impLocalHitCorrection(pRetval, rPnt, nTol, GetMarkedObjectList());
+ }
+
+ return pRetval;
+}
+
+/// Gets called every time the handles need to be build
+void SwDrawView::AddCustomHdl()
+{
+ const SdrMarkList &rMrkList = GetMarkedObjectList();
+
+ if(rMrkList.GetMarkCount() != 1 || !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj()))
+ return;
+
+ SdrObject *pObj = rMrkList.GetMark(0)->GetMarkedSdrObj();
+ // make code robust
+ SwFrameFormat* pFrameFormat( ::FindFrameFormat( pObj ) );
+ if ( !pFrameFormat )
+ {
+ OSL_FAIL( "<SwDrawView::AddCustomHdl()> - missing frame format!" );
+ return;
+ }
+ const SwFormatAnchor &rAnchor = pFrameFormat->GetAnchor();
+
+ if (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())
+ return;
+
+ const SwFrame* pAnch = CalcAnchor();
+ if(nullptr == pAnch)
+ return;
+
+ Point aPos(m_aAnchorPoint);
+
+ if ( RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId() )
+ {
+ // #i28701# - use last character rectangle saved at object
+ // in order to avoid a format of the anchor frame
+ SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj );
+
+ // Invalidate/recalc LastCharRect which can contain invalid frame offset because
+ // of later frame changes
+ pAnchoredObj->CheckCharRectAndTopOfLine(false);
+
+ SwRect aAutoPos = pAnchoredObj->GetLastCharRect();
+ if ( aAutoPos.Height() )
+ {
+ aPos = aAutoPos.Pos();
+ }
+ }
+
+ // add anchor handle:
+ std::unique_ptr<SdrHdl> hdl = std::make_unique<SwSdrHdl>( aPos, ( pAnch->IsVertical() && !pAnch->IsVertLR() ) ||
+ pAnch->IsRightToLeft() );
+ hdl->SetObjHdlNum(maHdlList.GetHdlCount());
+ maHdlList.AddHdl(std::move(hdl));
+}
+
+SdrObject* SwDrawView::GetMaxToTopObj( SdrObject* pObj ) const
+{
+ if ( GetUserCall(pObj) )
+ {
+ const SwFrame *pAnch = ::lcl_FindAnchor( pObj, false );
+ if ( pAnch )
+ {
+ //The topmost Obj within the anchor must not be overtaken.
+ const SwFlyFrame *pFly = pAnch->FindFlyFrame();
+ if ( pFly )
+ {
+ const SwPageFrame *pPage = pFly->FindPageFrame();
+ if ( pPage->GetSortedObjs() )
+ {
+ size_t nOrdNum = 0;
+ for (SwAnchoredObject* i : *pPage->GetSortedObjs())
+ {
+ const SdrObject *pO = i->GetDrawObj();
+
+ if ( pO->GetOrdNumDirect() > nOrdNum )
+ {
+ const SwFrame *pTmpAnch = ::lcl_FindAnchor( pO, false );
+ if ( pFly->IsAnLower( pTmpAnch ) )
+ {
+ nOrdNum = pO->GetOrdNumDirect();
+ }
+ }
+ }
+ if ( nOrdNum )
+ {
+ SdrPage *pTmpPage = GetModel()->GetPage( 0 );
+ ++nOrdNum;
+ if ( nOrdNum < pTmpPage->GetObjCount() )
+ {
+ return pTmpPage->GetObj( nOrdNum );
+ }
+ }
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+SdrObject* SwDrawView::GetMaxToBtmObj(SdrObject* pObj) const
+{
+ if ( GetUserCall(pObj) )
+ {
+ const SwFrame *pAnch = ::lcl_FindAnchor( pObj, false );
+ if ( pAnch )
+ {
+ //The Fly of the anchor must not be "flying under".
+ const SwFlyFrame *pFly = pAnch->FindFlyFrame();
+ if ( pFly )
+ {
+ SdrObject *pRet = const_cast<SdrObject*>(static_cast<SdrObject const *>(pFly->GetVirtDrawObj()));
+ return pRet != pObj ? pRet : nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/// determine maximal order number for a 'child' object of given 'parent' object
+sal_uInt32 SwDrawView::GetMaxChildOrdNum( const SwFlyFrame& _rParentObj,
+ const SdrObject* _pExclChildObj )
+{
+ sal_uInt32 nMaxChildOrdNum = _rParentObj.GetDrawObj()->GetOrdNum();
+
+ const SdrPage* pDrawPage = _rParentObj.GetDrawObj()->getSdrPageFromSdrObject();
+ OSL_ENSURE( pDrawPage,
+ "<SwDrawView::GetMaxChildOrdNum(..) - missing drawing page at parent object - crash!" );
+
+ const size_t nObjCount = pDrawPage->GetObjCount();
+ for ( size_t i = nObjCount-1; i > _rParentObj.GetDrawObj()->GetOrdNum() ; --i )
+ {
+ const SdrObject* pObj = pDrawPage->GetObj( i );
+
+ // Don't consider 'child' object <_pExclChildObj>
+ if ( pObj == _pExclChildObj )
+ {
+ continue;
+ }
+
+ if ( pObj->GetOrdNum() > nMaxChildOrdNum &&
+ _rParentObj.IsAnLower( lcl_FindAnchor( pObj, true ) ) )
+ {
+ nMaxChildOrdNum = pObj->GetOrdNum();
+ break;
+ }
+ }
+
+ return nMaxChildOrdNum;
+}
+
+/// method to move 'repeated' objects of the given moved object to the according level
+void SwDrawView::MoveRepeatedObjs( const SwAnchoredObject& _rMovedAnchoredObj,
+ const std::vector<SdrObject*>& _rMovedChildObjs ) const
+{
+ // determine 'repeated' objects of already moved object <_rMovedAnchoredObj>
+ std::vector<SwAnchoredObject*> aAnchoredObjs;
+ {
+ const SwContact* pContact = ::GetUserCall( _rMovedAnchoredObj.GetDrawObj() );
+ assert(pContact && "SwDrawView::MoveRepeatedObjs(..) - missing contact object -> crash.");
+ pContact->GetAnchoredObjs( aAnchoredObjs );
+ }
+
+ // check, if 'repeated' objects exists.
+ if ( aAnchoredObjs.size() <= 1 )
+ return;
+
+ SdrPage* pDrawPage = GetModel()->GetPage( 0 );
+
+ // move 'repeated' ones to the same order number as the already moved one.
+ const size_t nNewPos = _rMovedAnchoredObj.GetDrawObj()->GetOrdNum();
+ while ( !aAnchoredObjs.empty() )
+ {
+ SwAnchoredObject* pAnchoredObj = aAnchoredObjs.back();
+ if ( pAnchoredObj != &_rMovedAnchoredObj )
+ {
+ pDrawPage->SetObjectOrdNum( pAnchoredObj->GetDrawObj()->GetOrdNum(),
+ nNewPos );
+ pDrawPage->RecalcObjOrdNums();
+ // adjustments for accessibility API
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( auto pTmpFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ m_rImp.DisposeAccessibleFrame( pTmpFlyFrame );
+ m_rImp.AddAccessibleFrame( pTmpFlyFrame );
+ }
+ else
+ {
+ m_rImp.DisposeAccessibleObj(pAnchoredObj->GetDrawObj(), true);
+ m_rImp.AddAccessibleObj( pAnchoredObj->GetDrawObj() );
+ }
+#endif
+ }
+ aAnchoredObjs.pop_back();
+ }
+
+ // move 'repeated' ones of 'child' objects
+ for ( SdrObject* pChildObj : _rMovedChildObjs )
+ {
+ {
+ const SwContact* pContact = ::GetUserCall( pChildObj );
+ assert(pContact && "SwDrawView::MoveRepeatedObjs(..) - missing contact object -> crash.");
+ pContact->GetAnchoredObjs( aAnchoredObjs );
+ }
+ // move 'repeated' ones to the same order number as the already moved one.
+ const size_t nTmpNewPos = pChildObj->GetOrdNum();
+ while ( !aAnchoredObjs.empty() )
+ {
+ SwAnchoredObject* pAnchoredObj = aAnchoredObjs.back();
+ if ( pAnchoredObj->GetDrawObj() != pChildObj )
+ {
+ pDrawPage->SetObjectOrdNum( pAnchoredObj->GetDrawObj()->GetOrdNum(),
+ nTmpNewPos );
+ pDrawPage->RecalcObjOrdNums();
+ // adjustments for accessibility API
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( auto pTmpFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ m_rImp.DisposeAccessibleFrame( pTmpFlyFrame );
+ m_rImp.AddAccessibleFrame( pTmpFlyFrame );
+ }
+ else
+ {
+ m_rImp.DisposeAccessibleObj(pAnchoredObj->GetDrawObj(), true);
+ m_rImp.AddAccessibleObj( pAnchoredObj->GetDrawObj() );
+ }
+#endif
+ }
+ aAnchoredObjs.pop_back();
+ }
+ }
+}
+
+// --> adjustment and re-factoring of method
+void SwDrawView::ObjOrderChanged( SdrObject* pObj, size_t nOldPos,
+ size_t nNewPos )
+{
+ // nothing to do for group members
+ if ( pObj->getParentSdrObjectFromSdrObject() )
+ {
+ return;
+ }
+
+ // determine drawing page and assure that the order numbers are correct.
+ SdrPage* pDrawPage = GetModel()->GetPage( 0 );
+ if ( pDrawPage->IsObjOrdNumsDirty() )
+ pDrawPage->RecalcObjOrdNums();
+ const size_t nObjCount = pDrawPage->GetObjCount();
+
+ SwAnchoredObject* pMovedAnchoredObj =
+ ::GetUserCall( pObj )->GetAnchoredObj( pObj );
+ const SwFlyFrame* pParentAnchoredObj =
+ pMovedAnchoredObj->GetAnchorFrame()->FindFlyFrame();
+
+ const bool bMovedForward = nOldPos < nNewPos;
+
+ // assure for a 'child' object, that it doesn't exceed the limits of its 'parent'
+ if ( pParentAnchoredObj )
+ {
+ if ( bMovedForward )
+ {
+ const size_t nMaxChildOrdNumWithoutMoved =
+ GetMaxChildOrdNum( *pParentAnchoredObj, pMovedAnchoredObj->GetDrawObj() );
+ if ( nNewPos > nMaxChildOrdNumWithoutMoved+1 )
+ {
+ // set position to the top of the 'child' object group
+ pDrawPage->SetObjectOrdNum( nNewPos, nMaxChildOrdNumWithoutMoved+1 );
+ nNewPos = nMaxChildOrdNumWithoutMoved+1;
+ }
+ }
+ else
+ {
+ const size_t nParentOrdNum = pParentAnchoredObj->GetDrawObj()->GetOrdNum();
+ if ( nNewPos < nParentOrdNum )
+ {
+ // set position to the bottom of the 'child' object group
+ pDrawPage->SetObjectOrdNum( nNewPos, nParentOrdNum );
+ nNewPos = nParentOrdNum;
+ }
+ }
+ if ( pDrawPage->IsObjOrdNumsDirty() )
+ pDrawPage->RecalcObjOrdNums();
+ }
+
+ // Assure, that object isn't positioned between 'repeated' ones
+ if ( ( bMovedForward && nNewPos < nObjCount - 1 ) ||
+ ( !bMovedForward && nNewPos > 0 ) )
+ {
+ const SdrObject* pTmpObj =
+ pDrawPage->GetObj( bMovedForward ? nNewPos - 1 : nNewPos + 1 );
+ if ( pTmpObj )
+ {
+ size_t nTmpNewPos( nNewPos );
+ if ( bMovedForward )
+ {
+ // move before the top 'repeated' object
+ const sal_uInt32 nTmpMaxOrdNum =
+ ::GetUserCall( pTmpObj )->GetMaxOrdNum();
+ if ( nTmpMaxOrdNum > nNewPos )
+ nTmpNewPos = nTmpMaxOrdNum;
+ }
+ else
+ {
+ // move behind the bottom 'repeated' object
+ const sal_uInt32 nTmpMinOrdNum =
+ ::GetUserCall( pTmpObj )->GetMinOrdNum();
+ if ( nTmpMinOrdNum < nNewPos )
+ nTmpNewPos = nTmpMinOrdNum;
+ }
+ if ( nTmpNewPos != nNewPos )
+ {
+ pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos );
+ nNewPos = nTmpNewPos;
+ pDrawPage->RecalcObjOrdNums();
+ }
+ }
+ }
+
+ // On move forward, assure that object is moved before its own children.
+ // Only Writer fly frames can have children.
+ if ( pMovedAnchoredObj->DynCastFlyFrame() &&
+ bMovedForward && nNewPos < nObjCount - 1 )
+ {
+ sal_uInt32 nMaxChildOrdNum =
+ GetMaxChildOrdNum( *static_cast<const SwFlyFrame*>(pMovedAnchoredObj) );
+ if ( nNewPos < nMaxChildOrdNum )
+ {
+ // determine position before the object before its top 'child' object
+ const SdrObject* pTmpObj = pDrawPage->GetObj( nMaxChildOrdNum );
+ size_t nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum() + 1;
+ if ( nTmpNewPos >= nObjCount )
+ {
+ --nTmpNewPos;
+ }
+ // assure, that determined position isn't between 'repeated' objects
+ pTmpObj = pDrawPage->GetObj( nTmpNewPos );
+ nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum();
+ // apply new position
+ pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos );
+ nNewPos = nTmpNewPos;
+ pDrawPage->RecalcObjOrdNums();
+ }
+ }
+
+ // Assure, that object isn't positioned between nested objects
+ if ( ( bMovedForward && nNewPos < nObjCount - 1 ) ||
+ ( !bMovedForward && nNewPos > 0 ) )
+ {
+ size_t nTmpNewPos( nNewPos );
+ const SwFrameFormat* pParentFrameFormat =
+ pParentAnchoredObj ? &(pParentAnchoredObj->GetFrameFormat()) : nullptr;
+ const SdrObject* pTmpObj = pDrawPage->GetObj( nNewPos + 1 );
+ while ( pTmpObj )
+ {
+ // #i38563# - assure, that anchor frame exists.
+ // If object is anchored inside an invisible part of the document
+ // (e.g. page header, whose page style isn't applied, or hidden
+ // section), no anchor frame exists.
+ const SwFrame* pTmpAnchorFrame = lcl_FindAnchor( pTmpObj, true );
+ const SwFlyFrame* pTmpParentObj = pTmpAnchorFrame
+ ? pTmpAnchorFrame->FindFlyFrame() : nullptr;
+ if ( pTmpParentObj &&
+ &(pTmpParentObj->GetFrameFormat()) != pParentFrameFormat )
+ {
+ if ( bMovedForward )
+ {
+ nTmpNewPos = ::GetUserCall( pTmpObj )->GetMaxOrdNum();
+ pTmpObj = pDrawPage->GetObj( nTmpNewPos + 1 );
+ }
+ else
+ {
+ nTmpNewPos = ::GetUserCall( pTmpParentObj->GetDrawObj() )
+ ->GetMinOrdNum();
+ pTmpObj = pTmpParentObj->GetDrawObj();
+ }
+ }
+ else
+ break;
+ }
+ if ( nTmpNewPos != nNewPos )
+ {
+ pDrawPage->SetObjectOrdNum( nNewPos, nTmpNewPos );
+ nNewPos = nTmpNewPos;
+ pDrawPage->RecalcObjOrdNums();
+ }
+ }
+
+ // setup collection of moved 'child' objects to move its 'repeated' objects.
+ std::vector< SdrObject* > aMovedChildObjs;
+
+ // move 'children' accordingly
+ if ( auto pFlyFrame = pMovedAnchoredObj->DynCastFlyFrame() )
+ {
+ // adjustments for accessibility API
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ m_rImp.DisposeAccessibleFrame( pFlyFrame );
+ m_rImp.AddAccessibleFrame( pFlyFrame );
+#endif
+
+ const sal_uInt32 nChildNewPos = bMovedForward ? nNewPos : nNewPos+1;
+ size_t i = bMovedForward ? nOldPos : nObjCount-1;
+ do
+ {
+ SdrObject* pTmpObj = pDrawPage->GetObj( i );
+ if ( pTmpObj == pObj )
+ break;
+
+ // #i38563# - assure, that anchor frame exists.
+ // If object is anchored inside an invisible part of the document
+ // (e.g. page header, whose page style isn't applied, or hidden
+ // section), no anchor frame exists.
+ const SwFrame* pTmpAnchorFrame = lcl_FindAnchor( pTmpObj, true );
+ const SwFlyFrame* pTmpParentObj = pTmpAnchorFrame
+ ? pTmpAnchorFrame->FindFlyFrame() : nullptr;
+ if ( pTmpParentObj &&
+ ( ( pTmpParentObj == pFlyFrame ) ||
+ ( pFlyFrame->IsUpperOf( *pTmpParentObj ) ) ) )
+ {
+ // move child object.,
+ pDrawPage->SetObjectOrdNum( i, nChildNewPos );
+ pDrawPage->RecalcObjOrdNums();
+ // collect 'child' object
+ aMovedChildObjs.push_back( pTmpObj );
+ // adjustments for accessibility API
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( auto pFlyDrawObj = dynamic_cast<SwVirtFlyDrawObj *>( pTmpObj ) )
+ {
+ const SwFlyFrame *pTmpFlyFrame = pFlyDrawObj->GetFlyFrame();
+ m_rImp.DisposeAccessibleFrame( pTmpFlyFrame );
+ m_rImp.AddAccessibleFrame( pTmpFlyFrame );
+ }
+ else
+ {
+ m_rImp.DisposeAccessibleObj(pTmpObj, true);
+ m_rImp.AddAccessibleObj( pTmpObj );
+ }
+#endif
+ }
+ else
+ {
+ // adjust loop counter
+ if ( bMovedForward )
+ ++i;
+ else if (i > 0)
+ --i;
+ }
+
+ } while ( ( bMovedForward && i < ( nObjCount - aMovedChildObjs.size() ) ) ||
+ ( !bMovedForward && i > ( nNewPos + aMovedChildObjs.size() ) ) );
+ }
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ else
+ {
+ // adjustments for accessibility API
+ m_rImp.DisposeAccessibleObj(pObj, true);
+ m_rImp.AddAccessibleObj( pObj );
+ }
+#endif
+
+ MoveRepeatedObjs( *pMovedAnchoredObj, aMovedChildObjs );
+}
+
+bool SwDrawView::TakeDragLimit( SdrDragMode eMode,
+ tools::Rectangle& rRect ) const
+{
+ const SdrMarkList &rMrkList = GetMarkedObjectList();
+ bool bRet = false;
+ if( 1 == rMrkList.GetMarkCount() )
+ {
+ const SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ SwRect aRect;
+ if( ::CalcClipRect( pObj, aRect, eMode == SdrDragMode::Move ) )
+ {
+ rRect = aRect.SVRect();
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+const SwFrame* SwDrawView::CalcAnchor()
+{
+ const SdrMarkList &rMrkList = GetMarkedObjectList();
+ if ( rMrkList.GetMarkCount() != 1 )
+ return nullptr;
+
+ SdrObject* pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ //Search for paragraph bound objects, otherwise only the
+ //current anchor. Search only if we currently drag.
+ const SwFrame* pAnch;
+ tools::Rectangle aMyRect;
+ auto pFlyDrawObj = dynamic_cast<SwVirtFlyDrawObj *>( pObj );
+ if ( pFlyDrawObj )
+ {
+ pAnch = pFlyDrawObj->GetFlyFrame()->GetAnchorFrame();
+ aMyRect = pFlyDrawObj->GetFlyFrame()->getFrameArea().SVRect();
+ }
+ else
+ {
+ SwDrawContact *pC = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ // determine correct anchor position for 'virtual' drawing objects.
+ // #i26791#
+ pAnch = pC->GetAnchorFrame( pObj );
+ if( !pAnch )
+ {
+ pC->ConnectToLayout();
+ // determine correct anchor position for 'virtual' drawing objects.
+ // #i26791#
+ pAnch = pC->GetAnchorFrame( pObj );
+ }
+ aMyRect = pObj->GetSnapRect();
+ }
+
+ const bool bTopRight = pAnch && ( ( pAnch->IsVertical() &&
+ !pAnch->IsVertLR() ) ||
+ pAnch->IsRightToLeft() );
+ const Point aMyPt = bTopRight ? aMyRect.TopRight() : aMyRect.TopLeft();
+
+ Point aPt;
+ if ( IsAction() )
+ {
+ if ( !TakeDragObjAnchorPos( aPt, bTopRight ) )
+ return nullptr;
+ }
+ else
+ {
+ tools::Rectangle aRect = pObj->GetSnapRect();
+ aPt = bTopRight ? aRect.TopRight() : aRect.TopLeft();
+ }
+
+ if ( aPt != aMyPt )
+ {
+ if ( pAnch && pAnch->IsContentFrame() )
+ {
+ // allow drawing objects in header/footer,
+ // but exclude control objects.
+ bool bBodyOnly = CheckControlLayer( pObj );
+ pAnch = ::FindAnchor( static_cast<const SwContentFrame*>(pAnch), aPt, bBodyOnly );
+ }
+ else if ( !pFlyDrawObj )
+ {
+ const SwRect aRect( aPt.getX(), aPt.getY(), 1, 1 );
+
+ SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ if ( pContact->GetAnchorFrame( pObj ) &&
+ pContact->GetAnchorFrame( pObj )->IsPageFrame() )
+ pAnch = pContact->GetPageFrame();
+ else
+ pAnch = pContact->FindPage( aRect );
+ }
+ }
+ if( pAnch && !pAnch->IsProtected() )
+ m_aAnchorPoint = pAnch->GetFrameAnchorPos( ::HasWrap( pObj ) );
+ else
+ pAnch = nullptr;
+ return pAnch;
+}
+
+void SwDrawView::ShowDragAnchor()
+{
+ SdrHdl* pHdl = maHdlList.GetHdl(SdrHdlKind::Anchor);
+ if ( ! pHdl )
+ pHdl = maHdlList.GetHdl(SdrHdlKind::Anchor_TR);
+
+ if(pHdl)
+ {
+ CalcAnchor();
+ pHdl->SetPos(m_aAnchorPoint);
+ }
+}
+
+void SwDrawView::MarkListHasChanged()
+{
+ Imp().GetShell()->DrawSelChanged();
+ FmFormView::MarkListHasChanged();
+}
+
+// #i7672#
+void SwDrawView::ModelHasChanged()
+{
+ // The ModelHasChanged() call in DrawingLayer also updates
+ // an eventually active text edit view (OutlinerView). This also leads
+ // to newly setting the background color for that edit view. Thus,
+ // this method rescues the current background color if an OutlinerView
+ // exists and re-establishes it then. To be more safe, the OutlinerView
+ // will be fetched again (maybe textedit has ended).
+ OutlinerView* pView = GetTextEditOutlinerView();
+ Color aBackColor;
+ bool bColorWasSaved(false);
+
+ if(pView)
+ {
+ aBackColor = pView->GetBackgroundColor();
+ bColorWasSaved = true;
+ }
+
+ // call parent
+ FmFormView::ModelHasChanged();
+
+ if(bColorWasSaved)
+ {
+ pView = GetTextEditOutlinerView();
+
+ if(pView)
+ {
+ pView->SetBackgroundColor(aBackColor);
+ }
+ }
+}
+
+void SwDrawView::MakeVisible( const tools::Rectangle &rRect, vcl::Window & )
+{
+ OSL_ENSURE( m_rImp.GetShell()->GetWin(), "MakeVisible, unknown Window");
+ m_rImp.GetShell()->MakeVisible( SwRect( rRect ) );
+}
+
+void SwDrawView::CheckPossibilities()
+{
+ FmFormView::CheckPossibilities();
+
+ //In addition to the existing flags of the objects themselves,
+ //which are evaluated by the DrawingEngine, other circumstances
+ //lead to a protection.
+ //Objects that are anchored in frames need to be protected
+ //if the content of the frame is protected.
+ //OLE-Objects may themselves wish a resize protection (StarMath)
+
+ const SdrMarkList &rMrkList = GetMarkedObjectList();
+ bool bProtect = false;
+ bool bSzProtect = false;
+ bool bRotate(false);
+
+ for ( size_t i = 0; !bProtect && i < rMrkList.GetMarkCount(); ++i )
+ {
+ const SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ const SwFrame *pFrame = nullptr;
+ if ( auto pVirtFlyDrawObj = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) )
+ {
+ const SwFlyFrame *pFly = pVirtFlyDrawObj->GetFlyFrame();
+ if ( pFly )
+ {
+ pFrame = pFly->GetAnchorFrame();
+ if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ const SwNoTextFrame *const pNTF(static_cast<const SwNoTextFrame*>(pFly->Lower()));
+ const SwOLENode *const pOLENd = pNTF->GetNode()->GetOLENode();
+ const SwGrfNode *const pGrfNd = pNTF->GetNode()->GetGrfNode();
+
+ if ( pOLENd )
+ {
+ const uno::Reference < embed::XEmbeddedObject > xObj = const_cast< SwOLEObj& >(pOLENd->GetOLEObj()).GetOleRef();
+
+ if ( xObj.is() )
+ {
+ // --> improvement for the future, when more
+ // than one Writer fly frame can be selected.
+
+ // TODO/LATER: retrieve Aspect - from where?!
+ bSzProtect |= ( embed::EmbedMisc::EMBED_NEVERRESIZE & xObj->getStatus( embed::Aspects::MSOLE_CONTENT ) ) != 0;
+
+ // #i972: protect position if it is a Math object anchored 'as char' and baseline alignment is activated
+ SwDoc* pDoc = Imp().GetShell()->GetDoc();
+ const bool bProtectMathPos = SotExchange::IsMath( xObj->getClassID() )
+ && RndStdIds::FLY_AS_CHAR == pFly->GetFormat()->GetAnchor().GetAnchorId()
+ && pDoc->GetDocumentSettingManager().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT );
+ if (bProtectMathPos)
+ m_bMoveProtect = true;
+ }
+ }
+ else if(pGrfNd)
+ {
+ // RotGrfFlyFrame: GraphicNode allows rotation(s). The loop ew are in stops
+ // as soon as bMoveProtect is set, but since rotation is valid only with
+ // a single object selected this makes no difference
+ bRotate = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ SwDrawContact *pC = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ if ( pC )
+ pFrame = pC->GetAnchorFrame( pObj );
+ }
+ if ( pFrame )
+ bProtect = pFrame->IsProtected(); //Frames, areas etc.
+ {
+ SwFrameFormat* pFrameFormat( ::FindFrameFormat( const_cast<SdrObject*>(pObj) ) );
+ if ( !pFrameFormat )
+ {
+ OSL_FAIL( "<SwDrawView::CheckPossibilities()> - missing frame format" );
+ bProtect = true;
+ }
+ else if ((RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId()) &&
+ rMrkList.GetMarkCount() > 1 )
+ {
+ bProtect = true;
+ }
+ }
+ }
+ m_bMoveProtect |= bProtect;
+ m_bResizeProtect |= bProtect || bSzProtect;
+
+ // RotGrfFlyFrame: allow rotation when SwGrfNode is selected and not size protected
+ m_bRotateFreeAllowed |= bRotate && !bProtect;
+ m_bRotate90Allowed |= m_bRotateFreeAllowed;
+}
+
+/// replace marked <SwDrawVirtObj>-objects by its reference object for delete marked objects.
+void SwDrawView::ReplaceMarkedDrawVirtObjs( SdrMarkView& _rMarkView )
+{
+ SdrPageView* pDrawPageView = _rMarkView.GetSdrPageView();
+ const SdrMarkList& rMarkList = _rMarkView.GetMarkedObjectList();
+
+ if( !rMarkList.GetMarkCount() )
+ return;
+
+ // collect marked objects in a local data structure
+ std::vector<SdrObject*> aMarkedObjs;
+ for( size_t i = 0; i < rMarkList.GetMarkCount(); ++i )
+ {
+ SdrObject* pMarkedObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ aMarkedObjs.push_back( pMarkedObj );
+ }
+ // unmark all objects
+ _rMarkView.UnmarkAllObj();
+ // re-mark objects, but for marked <SwDrawVirtObj>-objects marked its
+ // reference object.
+ while ( !aMarkedObjs.empty() )
+ {
+ SdrObject* pMarkObj = aMarkedObjs.back();
+ if ( auto pVirtObj = dynamic_cast<SwDrawVirtObj *>( pMarkObj ) )
+ {
+ SdrObject* pRefObj = &(pVirtObj->ReferencedObj());
+ if ( !_rMarkView.IsObjMarked( pRefObj ) )
+ {
+ _rMarkView.MarkObj( pRefObj, pDrawPageView );
+ }
+ }
+ else
+ {
+ _rMarkView.MarkObj( pMarkObj, pDrawPageView );
+ }
+
+ aMarkedObjs.pop_back();
+ }
+ // sort marked list in order to assure consistent state in drawing layer
+ _rMarkView.SortMarkedObjects();
+}
+
+SfxViewShell* SwDrawView::GetSfxViewShell() const
+{
+ return m_rImp.GetShell()->GetSfxViewShell();
+}
+
+void SwDrawView::DeleteMarked()
+{
+ SwDoc* pDoc = Imp().GetShell()->GetDoc();
+ SwRootFrame *pTmpRoot = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if ( pTmpRoot )
+ pTmpRoot->StartAllAction();
+ pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ // replace marked <SwDrawVirtObj>-objects by its reference objects.
+ if (SdrPageView* pDrawPageView = m_rImp.GetPageView())
+ {
+ ReplaceMarkedDrawVirtObjs(pDrawPageView->GetView());
+ }
+
+ // Check what textboxes have to be deleted afterwards.
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ std::vector<SwFrameFormat*> aTextBoxesToDelete;
+ for (size_t i = 0; i < rMarkList.GetMarkCount(); ++i)
+ {
+ SdrObject *pObject = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ SwContact* pContact = GetUserCall(pObject);
+ SwFrameFormat* pFormat = pContact->GetFormat();
+ if (pObject->getChildrenOfSdrObject())
+ {
+ auto pChildTextBoxes = SwTextBoxHelper::CollectTextBoxes(pObject, pFormat);
+ for (auto& rChildTextBox : pChildTextBoxes)
+ aTextBoxesToDelete.push_back(rChildTextBox);
+ }
+ else
+ if (SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
+ aTextBoxesToDelete.push_back(pTextBox);
+ }
+
+ if ( pDoc->DeleteSelection( *this ) )
+ {
+ FmFormView::DeleteMarked();
+ ::FrameNotify( Imp().GetShell(), FLY_DRAG_END );
+ }
+
+ // Only delete these now: earlier deletion would clear the mark list as well.
+ // Delete in reverse order, assuming that the container is sorted by anchor positions.
+ for (int i = aTextBoxesToDelete.size() - 1; i >= 0; --i)
+ {
+ SwFrameFormat*& rpTextBox = aTextBoxesToDelete[i];
+ pDoc->getIDocumentLayoutAccess().DelLayoutFormat(rpTextBox);
+ }
+
+ pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ if( pTmpRoot )
+ pTmpRoot->EndAllAction();
+}
+
+// Create a new view-local UndoManager manager for Writer
+std::unique_ptr<SdrUndoManager> SwDrawView::createLocalTextUndoManager()
+{
+ std::unique_ptr<SdrUndoManager> pUndoManager(new SdrUndoManager);
+ pUndoManager->SetDocShell(SfxObjectShell::Current());
+ return pUndoManager;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/acorrect.cxx b/sw/source/core/edit/acorrect.cxx
new file mode 100644
index 000000000..ed2a0fb3c
--- /dev/null
+++ b/sw/source/core/edit/acorrect.cxx
@@ -0,0 +1,716 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <fmtinfmt.hxx>
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <unocrsr.hxx>
+#include <txatbase.hxx>
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <acorrect.hxx>
+#include <shellio.hxx>
+#include <swundo.hxx>
+#include <viscrs.hxx>
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
+#include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+
+#include <editeng/acorrcfg.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <rootfrm.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class PaMIntoCursorShellRing
+{
+ SwPaM &m_rDelPam, &m_rCursor;
+ SwPaM* m_pPrevDelPam;
+ SwPaM* m_pPrevCursor;
+
+ static void RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev );
+public:
+ PaMIntoCursorShellRing( SwCursorShell& rSh, SwPaM& rCursor, SwPaM& rPam );
+ ~PaMIntoCursorShellRing();
+};
+
+}
+
+PaMIntoCursorShellRing::PaMIntoCursorShellRing(SwCursorShell& rCSh, SwPaM& rShCursor, SwPaM& rPam)
+ : m_rDelPam(rPam)
+ , m_rCursor(rShCursor)
+{
+ SwPaM* pShCursor = rCSh.GetCursor_();
+
+ m_pPrevDelPam = m_rDelPam.GetPrev();
+ m_pPrevCursor = m_rCursor.GetPrev();
+
+ m_rDelPam.GetRingContainer().merge(pShCursor->GetRingContainer());
+ m_rCursor.GetRingContainer().merge(pShCursor->GetRingContainer());
+}
+
+PaMIntoCursorShellRing::~PaMIntoCursorShellRing()
+{
+ // and take out the Pam again:
+ RemoveFromRing(m_rDelPam, m_pPrevDelPam);
+ RemoveFromRing(m_rCursor, m_pPrevCursor);
+}
+
+void PaMIntoCursorShellRing::RemoveFromRing( SwPaM& rPam, SwPaM const * pPrev )
+{
+ SwPaM* p;
+ SwPaM* pNext = &rPam;
+ do {
+ p = pNext;
+ pNext = p->GetNext();
+ p->MoveTo( &rPam );
+ } while( p != pPrev );
+}
+
+SwAutoCorrDoc::SwAutoCorrDoc( SwEditShell& rEditShell, SwPaM& rPam,
+ sal_Unicode cIns )
+ : m_rEditSh( rEditShell ), m_rCursor( rPam )
+ , m_nEndUndoCounter(0)
+ , m_bUndoIdInitialized( cIns == 0 )
+{
+}
+
+SwAutoCorrDoc::~SwAutoCorrDoc()
+{
+ for (int i = 0; i < m_nEndUndoCounter; ++i)
+ {
+ m_rEditSh.EndUndo();
+ }
+}
+
+void SwAutoCorrDoc::DeleteSel( SwPaM& rDelPam )
+{
+ // this should work with plain SwPaM as well because start and end
+ // are always in same node, but since there is GetRanges already...
+ std::vector<std::shared_ptr<SwUnoCursor>> ranges;
+ if (sw::GetRanges(ranges, *m_rEditSh.GetDoc(), rDelPam))
+ {
+ DeleteSelImpl(rDelPam);
+ }
+ else
+ {
+ for (auto const& pCursor : ranges)
+ {
+ DeleteSelImpl(*pCursor);
+ }
+ }
+}
+
+void SwAutoCorrDoc::DeleteSelImpl(SwPaM & rDelPam)
+{
+ SwDoc* pDoc = m_rEditSh.GetDoc();
+ if( pDoc->IsAutoFormatRedline() )
+ {
+ // so that also the DelPam be moved, include it in the
+ // Shell-Cursr-Ring !!
+ // ??? is that really necessary - this should never join nodes, so Update should be enough?
+// PaMIntoCursorShellRing aTmp( rEditSh, rCursor, rDelPam );
+ assert(rDelPam.GetPoint()->nNode == rDelPam.GetMark()->nNode);
+ pDoc->getIDocumentContentOperations().DeleteAndJoin( rDelPam );
+ }
+ else
+ {
+ pDoc->getIDocumentContentOperations().DeleteRange( rDelPam );
+ }
+}
+
+bool SwAutoCorrDoc::Delete( sal_Int32 nStt, sal_Int32 nEnd )
+{
+ SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+ SwPaM aSel(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
+ pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
+ DeleteSel( aSel );
+
+ if( m_bUndoIdInitialized )
+ m_bUndoIdInitialized = true;
+ return true;
+}
+
+bool SwAutoCorrDoc::Insert( sal_Int32 nPos, const OUString& rText )
+{
+ SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+ SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nPos)));
+ m_rEditSh.GetDoc()->getIDocumentContentOperations().InsertString( aPam, rText );
+ if( !m_bUndoIdInitialized )
+ {
+ m_bUndoIdInitialized = true;
+ if( 1 == rText.getLength() )
+ {
+ m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
+ ++m_nEndUndoCounter;
+ }
+ }
+ return true;
+}
+
+bool SwAutoCorrDoc::Replace( sal_Int32 nPos, const OUString& rText )
+{
+ return ReplaceRange( nPos, rText.getLength(), rText );
+}
+
+bool SwAutoCorrDoc::ReplaceRange( sal_Int32 nPos, sal_Int32 nSourceLength, const OUString& rText )
+{
+ assert(nSourceLength == 1); // sw_redlinehide: this is currently the case,
+ // and ensures that the replace range cannot *contain* delete redlines,
+ // so we don't need something along the lines of:
+ // if (sw::GetRanges(ranges, *rEditSh.GetDoc(), aPam))
+ // ReplaceImpl(...)
+ // else
+ // ReplaceImpl(ranges.begin())
+ // for (ranges.begin() + 1; ranges.end(); )
+ // DeleteImpl(*it)
+
+ SwTextNode * const pNd = m_rCursor.GetNode().GetTextNode();
+ if ( !pNd )
+ {
+ return false;
+ }
+
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+ std::pair<SwTextNode *, sal_Int32> const pos(pFrame->MapViewToModel(TextFrameIndex(nPos)));
+
+ SwPaM* pPam = &m_rCursor;
+ if (pPam->GetPoint()->nNode != *pos.first
+ || pPam->GetPoint()->nContent != pos.second)
+ {
+ pPam = new SwPaM(*pos.first, pos.second);
+ }
+
+ // text attributes with dummy characters must not be replaced!
+ bool bDoReplace = true;
+ sal_Int32 const nLen = rText.getLength();
+ for (sal_Int32 n = 0; n < nLen && n + nPos < pFrame->GetText().getLength(); ++n)
+ {
+ sal_Unicode const Char = pFrame->GetText()[n + nPos];
+ if (CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char)
+ {
+ assert(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).first->GetTextAttrForCharAt(pFrame->MapViewToModel(TextFrameIndex(n+nPos)).second));
+ bDoReplace = false;
+ break;
+ }
+ }
+
+ // tdf#83419 avoid bad autocorrect with visible redlines
+ // e.g. replacing the first letter of the tracked deletion
+ // with its capitalized (and not deleted) version.
+ if ( bDoReplace && !pFrame->getRootFrame()->IsHideRedlines() &&
+ m_rEditSh.GetDoc()->getIDocumentRedlineAccess().HasRedline( *pPam, RedlineType::Delete, /*bStartOrEndInRange=*/false ) )
+ {
+ bDoReplace = false;
+ }
+
+ if ( bDoReplace )
+ {
+ SwDoc* pDoc = m_rEditSh.GetDoc();
+
+ if( pDoc->IsAutoFormatRedline() )
+ {
+ if (nPos == pFrame->GetText().getLength()) // at the End do an Insert
+ {
+ pDoc->getIDocumentContentOperations().InsertString( *pPam, rText );
+ }
+ else
+ {
+ assert(pos.second != pos.first->Len()); // must be _before_ char
+ PaMIntoCursorShellRing aTmp( m_rEditSh, m_rCursor, *pPam );
+
+ pPam->SetMark();
+ pPam->GetPoint()->nContent = std::min<sal_Int32>(
+ pos.first->GetText().getLength(), pos.second + nSourceLength);
+ pDoc->getIDocumentContentOperations().ReplaceRange( *pPam, rText, false );
+ pPam->Exchange();
+ pPam->DeleteMark();
+ }
+ }
+ else
+ {
+ pPam->SetMark();
+ pPam->GetPoint()->nContent = std::min<sal_Int32>(
+ pos.first->GetText().getLength(), pos.second + nSourceLength);
+ pDoc->getIDocumentContentOperations().ReplaceRange( *pPam, rText, false );
+ pPam->Exchange();
+ pPam->DeleteMark();
+ }
+
+ if( m_bUndoIdInitialized )
+ {
+ m_bUndoIdInitialized = true;
+ if( 1 == rText.getLength() )
+ {
+ m_rEditSh.StartUndo( SwUndoId::AUTOCORRECT );
+ ++m_nEndUndoCounter;
+ }
+ }
+ }
+
+ if( pPam != &m_rCursor )
+ delete pPam;
+
+ return true;
+}
+
+void SwAutoCorrDoc::SetAttr( sal_Int32 nStt, sal_Int32 nEnd, sal_uInt16 nSlotId,
+ SfxPoolItem& rItem )
+{
+ SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+ SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
+ pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
+
+ SfxItemPool& rPool = m_rEditSh.GetDoc()->GetAttrPool();
+ sal_uInt16 nWhich = rPool.GetWhich( nSlotId, false );
+ if( nWhich )
+ {
+ rItem.SetWhich( nWhich );
+
+ SfxItemSet aSet( rPool, aCharFormatSetRange );
+ SetAllScriptItem( aSet, rItem );
+
+ m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
+
+ if( m_bUndoIdInitialized )
+ m_bUndoIdInitialized = true;
+ }
+}
+
+bool SwAutoCorrDoc::SetINetAttr( sal_Int32 nStt, sal_Int32 nEnd, const OUString& rURL )
+{
+ SwTextNode const*const pTextNd = m_rCursor.GetNode().GetTextNode();
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+ SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(nStt)),
+ pFrame->MapViewToModelPos(TextFrameIndex(nEnd)));
+
+ SfxItemSetFixed<RES_TXTATR_INETFMT, RES_TXTATR_INETFMT>
+ aSet( m_rEditSh.GetDoc()->GetAttrPool() );
+ aSet.Put( SwFormatINetFormat( rURL, OUString() ));
+ m_rEditSh.GetDoc()->SetFormatItemByAutoFormat( aPam, aSet );
+ if( m_bUndoIdInitialized )
+ m_bUndoIdInitialized = true;
+ return true;
+}
+
+/** Return the text of a previous paragraph
+ *
+ * @param bAtNormalPos If <true> before the normal insert position; if <false> in which the
+ * corrected word was inserted. (Doesn't need to be the same paragraph!)
+ * @return text or 0, if previous paragraph does not exists or there are only blankness
+ */
+OUString const* SwAutoCorrDoc::GetPrevPara(bool const bAtNormalPos)
+{
+ OUString const* pStr(nullptr);
+
+ if( bAtNormalPos || !m_pIndex )
+ {
+ m_pIndex.reset(new SwNodeIndex(m_rCursor.GetPoint()->nNode));
+ }
+ sw::GotoPrevLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
+
+ SwTextFrame const* pFrame(nullptr);
+ for (SwTextNode * pTextNd = m_pIndex->GetNode().GetTextNode();
+ pTextNd; pTextNd = m_pIndex->GetNode().GetTextNode())
+ {
+ pFrame = static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout()));
+ if (pFrame && !pFrame->GetText().isEmpty())
+ {
+ break;
+ }
+ sw::GotoPrevLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
+ }
+ if (pFrame && !pFrame->GetText().isEmpty() &&
+ 0 == pFrame->GetTextNodeForParaProps()->GetAttrOutlineLevel())
+ {
+ pStr = & pFrame->GetText();
+ }
+
+ if( m_bUndoIdInitialized )
+ m_bUndoIdInitialized = true;
+
+ return pStr;
+}
+
+bool SwAutoCorrDoc::ChgAutoCorrWord( sal_Int32& rSttPos, sal_Int32 nEndPos,
+ SvxAutoCorrect& rACorrect,
+ OUString* pPara )
+{
+ if( m_bUndoIdInitialized )
+ m_bUndoIdInitialized = true;
+
+ // Found a beginning of a paragraph or a Blank,
+ // search for the word Kuerzel (Shortcut) in the Auto
+ SwTextNode* pTextNd = m_rCursor.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "where is the TextNode?" );
+
+ bool bRet = false;
+ if( nEndPos == rSttPos )
+ return bRet;
+
+ LanguageType eLang = GetLanguage(nEndPos);
+ if(LANGUAGE_SYSTEM == eLang)
+ eLang = GetAppLanguage();
+ LanguageTag aLanguageTag( eLang);
+
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+
+ const OUString sFrameText = pFrame->GetText();
+ const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList(
+ sFrameText, rSttPos, nEndPos, *this, aLanguageTag);
+ SwDoc* pDoc = m_rEditSh.GetDoc();
+ if( pFnd )
+ {
+ // replace also last colon of keywords surrounded by colons (for example, ":name:")
+ const bool replaceLastChar = sFrameText.getLength() > nEndPos && pFnd->GetShort()[0] == ':'
+ && pFnd->GetShort().endsWith(":");
+
+ SwPosition aStartPos( pFrame->MapViewToModelPos(TextFrameIndex(rSttPos) ));
+ SwPosition aEndPos( pFrame->MapViewToModelPos(TextFrameIndex(nEndPos + (replaceLastChar ? 1 : 0))) );
+ SwPaM aPam(aStartPos, aEndPos);
+
+ // don't replace, if a redline starts or ends within the original text
+ if ( pDoc->getIDocumentRedlineAccess().HasRedline( aPam, RedlineType::Any, /*bStartOrEndInRange=*/true ) )
+ {
+ return bRet;
+ }
+
+ if( pFnd->IsTextOnly() )
+ {
+ //JP 22.04.99: Bug 63883 - Special treatment for dots.
+ const bool bLastCharIsPoint
+ = nEndPos < sFrameText.getLength() && ('.' == sFrameText[nEndPos]);
+ if( !bLastCharIsPoint || pFnd->GetLong().isEmpty() ||
+ '.' != pFnd->GetLong()[ pFnd->GetLong().getLength() - 1 ] )
+ {
+ // replace the selection
+ std::vector<std::shared_ptr<SwUnoCursor>> ranges;
+ if (sw::GetRanges(ranges, *m_rEditSh.GetDoc(), aPam))
+ {
+ pDoc->getIDocumentContentOperations().ReplaceRange(aPam, pFnd->GetLong(), false);
+ bRet = true;
+ }
+ else if (!ranges.empty())
+ {
+ assert(ranges.front()->GetPoint()->nNode == ranges.front()->GetMark()->nNode);
+ pDoc->getIDocumentContentOperations().ReplaceRange(
+ *ranges.front(), pFnd->GetLong(), false);
+ for (auto it = ranges.begin() + 1; it != ranges.end(); ++it)
+ {
+ DeleteSelImpl(**it);
+ }
+ bRet = true;
+ }
+
+ // tdf#83260 After calling sw::DocumentContentOperationsManager::ReplaceRange
+ // pTextNd may become invalid when change tracking is on and Edit -> Track Changes -> Show == OFF.
+ // ReplaceRange shows changes, this moves deleted nodes from special section to document.
+ // Then Show mode is disabled again. As a result pTextNd may be invalidated.
+ pTextNd = m_rCursor.GetNode().GetTextNode();
+ }
+ }
+ else
+ {
+ SwTextBlocks aTBlks( rACorrect.GetAutoCorrFileName( aLanguageTag, false, true ));
+ sal_uInt16 nPos = aTBlks.GetIndex( pFnd->GetShort() );
+ if( USHRT_MAX != nPos && aTBlks.BeginGetDoc( nPos ) )
+ {
+ DeleteSel( aPam );
+ pDoc->DontExpandFormat( *aPam.GetPoint() );
+
+ if( pPara )
+ {
+ OSL_ENSURE( !m_pIndex, "who has not deleted his Index?" );
+ m_pIndex.reset(new SwNodeIndex( m_rCursor.GetPoint()->nNode ));
+ sw::GotoPrevLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
+ }
+
+ SwDoc* pAutoDoc = aTBlks.GetDoc();
+ SwNodeIndex aSttIdx( pAutoDoc->GetNodes().GetEndOfExtras(), 1 );
+ SwContentNode* pContentNd = pAutoDoc->GetNodes().GoNext( &aSttIdx );
+ SwPaM aCpyPam( aSttIdx );
+
+ const SwTableNode* pTableNd = pContentNd->FindTableNode();
+ if( pTableNd )
+ {
+ aCpyPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ aCpyPam.GetPoint()->nNode = *pTableNd;
+ }
+ aCpyPam.SetMark();
+
+ // then until the end of the Nodes Array
+ aCpyPam.GetPoint()->nNode.Assign( pAutoDoc->GetNodes().GetEndOfContent(), -1 );
+ pContentNd = aCpyPam.GetContentNode();
+ aCpyPam.GetPoint()->nContent.Assign(
+ pContentNd, pContentNd ? pContentNd->Len() : 0);
+
+ SwDontExpandItem aExpItem;
+ aExpItem.SaveDontExpandItems( *aPam.GetPoint() );
+
+ pAutoDoc->getIDocumentContentOperations().CopyRange(aCpyPam, *aPam.GetPoint(), SwCopyFlags::CheckPosInFly);
+
+ aExpItem.RestoreDontExpandItems( *aPam.GetPoint() );
+
+ if( pPara )
+ {
+ sw::GotoNextLayoutTextFrame(*m_pIndex, m_rEditSh.GetLayout());
+ pTextNd = m_pIndex->GetNode().GetTextNode();
+ }
+ bRet = true;
+ }
+ aTBlks.EndGetDoc();
+ }
+ }
+
+ if( bRet && pPara && pTextNd )
+ {
+ SwTextFrame const*const pNewFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ *pPara = pNewFrame->GetText();
+ }
+
+ return bRet;
+}
+
+bool SwAutoCorrDoc::TransliterateRTLWord( sal_Int32& rSttPos, sal_Int32 nEndPos, bool bApply )
+{
+ if( m_bUndoIdInitialized )
+ m_bUndoIdInitialized = true;
+
+ SwTextNode* pTextNd = m_rCursor.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "where is the TextNode?" );
+
+ bool bRet = false;
+ if( nEndPos == rSttPos )
+ return bRet;
+
+ LanguageType eLang = GetLanguage(nEndPos);
+ if(LANGUAGE_SYSTEM == eLang)
+ eLang = GetAppLanguage();
+ LanguageTag aLanguageTag(eLang);
+
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+
+ const OUString sFrameText = pFrame->GetText();
+ SwDoc* pDoc = m_rEditSh.GetDoc();
+ if ( pFrame->IsRightToLeft() || bApply )
+ {
+ // transliterate to Old Hungarian using Numbertext via NatNum12 number format modifier
+ OUString sWord(sFrameText.copy(rSttPos, nEndPos - rSttPos));
+ // Consonant disambiguation using hyphenation
+ uno::Reference< linguistic2::XHyphenator > xHyph;
+ xHyph = ::GetHyphenator();
+ OUStringBuffer sDisambiguatedWord;
+
+ const ::css::uno::Sequence< ::css::beans::PropertyValue > aProperties;
+ css::uno::Reference< css::linguistic2::XHyphenatedWord > xHyphWord;
+ for (int i = 0; i+1 < sWord.getLength(); i++ )
+ {
+ xHyphWord = xHyph->hyphenate( sWord,
+ aLanguageTag.getLocale(),
+ i,
+ aProperties );
+ // insert ZWSP at a hyphenation point, if it's not an alternative one (i.e. ssz->sz-sz)
+ if (xHyphWord.is() && xHyphWord->getHyphenationPos()+1 == i && !xHyphWord->isAlternativeSpelling())
+ {
+ sDisambiguatedWord.append(CHAR_ZWSP);
+ }
+ sDisambiguatedWord.append(sWord[i]);
+ }
+ sDisambiguatedWord.append(sWord[sWord.getLength()-1]);
+
+ SvNumberFormatter* pFormatter = pDoc->GetNumberFormatter();
+ OUString sConverted;
+ if (pFormatter && !sWord.isEmpty())
+ {
+ const Color* pColor = nullptr;
+
+ // Send text as NatNum12 prefix: "word" -> "[NatNum12 word]0"
+
+ // Closing bracket doesn't allowed in NatNum parameters, remove it from transliteration:
+ // "[word]" -> "[NatNum12 [word]0"
+ bool bHasBracket = sWord.endsWith("]");
+ if ( !bHasBracket )
+ sDisambiguatedWord.append("]");
+ OUString sPrefix("[NatNum12 " + sDisambiguatedWord.makeStringAndClear() + "0");
+ if (pFormatter->GetPreviewString(sPrefix, 0, sConverted, &pColor, LANGUAGE_USER_HUNGARIAN_ROVAS))
+ {
+ if ( bHasBracket )
+ sConverted = sConverted + "]";
+ bRet = true;
+ }
+ }
+
+ SwPaM aPam(pFrame->MapViewToModelPos(TextFrameIndex(rSttPos)),
+ pFrame->MapViewToModelPos(TextFrameIndex(nEndPos)));
+ if (bRet && nEndPos <= sFrameText.getLength())
+ pDoc->getIDocumentContentOperations().ReplaceRange(aPam, sConverted, false);
+ }
+
+ return bRet;
+}
+
+// Called by the functions:
+// - FnCapitalStartWord
+// - FnCapitalStartSentence
+// after the exchange of characters. Then the words, if necessary, can be inserted
+// into the exception list.
+void SwAutoCorrDoc::SaveCpltSttWord( ACFlags nFlag, sal_Int32 nPos,
+ const OUString& rExceptWord,
+ sal_Unicode cChar )
+{
+ SwNodeOffset nNode = m_pIndex ? m_pIndex->GetIndex() : m_rCursor.GetPoint()->nNode.GetIndex();
+ LanguageType eLang = GetLanguage(nPos);
+ m_rEditSh.GetDoc()->SetAutoCorrExceptWord( std::make_unique<SwAutoCorrExceptWord>( nFlag,
+ nNode, nPos, rExceptWord, cChar, eLang ));
+}
+
+LanguageType SwAutoCorrDoc::GetLanguage( sal_Int32 nPos ) const
+{
+ LanguageType eRet = LANGUAGE_SYSTEM;
+
+ SwTextNode* pNd = m_rCursor.GetPoint()->nNode.GetNode().GetTextNode();
+
+ if( pNd )
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ pNd->getLayoutFrame(m_rEditSh.GetLayout())));
+ assert(pFrame);
+ eRet = pFrame->GetLangOfChar(TextFrameIndex(nPos), 0, true);
+ }
+ if(LANGUAGE_SYSTEM == eRet)
+ eRet = GetAppLanguage();
+ return eRet;
+}
+
+void SwAutoCorrExceptWord::CheckChar( const SwPosition& rPos, sal_Unicode cChr )
+{
+ // test only if this is an improvement.
+ // If yes, then add the word to the list.
+ if (m_cChar == cChr && rPos.nNode.GetIndex() == m_nNode && rPos.nContent.GetIndex() == m_nContent)
+ {
+ // get the current autocorrection:
+ SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
+
+ // then add to the list:
+ if (ACFlags::CapitalStartWord & m_nFlags)
+ pACorr->AddWordStartException(m_sWord, m_eLanguage);
+ else if (ACFlags::CapitalStartSentence & m_nFlags)
+ pACorr->AddCplSttException(m_sWord, m_eLanguage);
+ }
+}
+
+bool SwAutoCorrExceptWord::CheckDelChar( const SwPosition& rPos )
+{
+ bool bRet = false;
+ if (!m_bDeleted && rPos.nNode.GetIndex() == m_nNode && rPos.nContent.GetIndex() == m_nContent)
+ m_bDeleted = bRet = true;
+ return bRet;
+}
+
+SwDontExpandItem::~SwDontExpandItem()
+{
+}
+
+void SwDontExpandItem::SaveDontExpandItems( const SwPosition& rPos )
+{
+ const SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ m_pDontExpandItems.reset( new SfxItemSet( const_cast<SwDoc&>(pTextNd->GetDoc()).GetAttrPool(),
+ aCharFormatSetRange ) );
+ const sal_Int32 n = rPos.nContent.GetIndex();
+ if (!pTextNd->GetParaAttr( *m_pDontExpandItems, n, n,
+ n != pTextNd->GetText().getLength() ))
+ {
+ m_pDontExpandItems.reset();
+ }
+ }
+}
+
+void SwDontExpandItem::RestoreDontExpandItems( const SwPosition& rPos )
+{
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( !pTextNd )
+ return;
+
+ const sal_Int32 nStart = rPos.nContent.GetIndex();
+ if( nStart == pTextNd->GetText().getLength() )
+ pTextNd->FormatToTextAttr( pTextNd );
+
+ if( !(pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count()) )
+ return;
+
+ const size_t nSize = pTextNd->GetpSwpHints()->Count();
+ sal_Int32 nAttrStart;
+
+ for( size_t n = 0; n < nSize; ++n )
+ {
+ SwTextAttr* pHt = pTextNd->GetpSwpHints()->Get( n );
+ nAttrStart = pHt->GetStart();
+ if( nAttrStart > nStart ) // beyond the area
+ break;
+
+ const sal_Int32* pAttrEnd;
+ if( nullptr != ( pAttrEnd = pHt->End() ) &&
+ ( ( nAttrStart < nStart &&
+ ( pHt->DontExpand() ? nStart < *pAttrEnd
+ : nStart <= *pAttrEnd )) ||
+ ( nStart == nAttrStart &&
+ ( nAttrStart == *pAttrEnd || !nStart ))) )
+ {
+ const SfxPoolItem* pItem;
+ if( !m_pDontExpandItems || SfxItemState::SET != m_pDontExpandItems->
+ GetItemState( pHt->Which(), false, &pItem ) ||
+ *pItem != pHt->GetAttr() )
+ {
+ // The attribute was not previously set in this form in the
+ // paragraph, so it can only be created through insert/copy
+ // Because of that it is a candidate for DontExpand
+ pHt->SetDontExpand( true );
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/autofmt.cxx b/sw/source/core/edit/autofmt.cxx
new file mode 100644
index 000000000..a4e0260f3
--- /dev/null
+++ b/sw/source/core/edit/autofmt.cxx
@@ -0,0 +1,2831 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+
+#include <editeng/boxitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <swwait.hxx>
+#include <fmtpdsc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <redline.hxx>
+#include <unocrsr.hxx>
+#include <docary.hxx>
+#include <editsh.hxx>
+#include <index.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <poolfmt.hxx>
+#include <ndtxt.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <frminf.hxx>
+#include <pagedesc.hxx>
+#include <paratr.hxx>
+#include <acorrect.hxx>
+#include <shellres.hxx>
+#include <section.hxx>
+#include <frmatr.hxx>
+#include <charatr.hxx>
+#include <mdiexp.hxx>
+#include <strings.hrc>
+#include <comcore.hxx>
+#include <numrule.hxx>
+#include <itabenum.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+//JP 16.12.99: definition:
+// from pos cPosEnDash to cPosEmDash all chars changed to em dashes,
+// from pos cPosEmDash to cPosEnd all chars changed to em dashes
+// all other chars are changed to the user configuration
+
+const sal_Unicode pBulletChar[6] = { '+', '*', '-', 0x2013, 0x2014, 0 };
+const int cnPosEnDash = 2, cnPosEmDash = 4;
+
+const sal_Unicode cStarSymbolEnDash = 0x2013;
+const sal_Unicode cStarSymbolEmDash = 0x2014;
+
+SvxSwAutoFormatFlags* SwEditShell::s_pAutoFormatFlags = nullptr;
+
+// Number of num-/bullet-paragraph templates. MAXLEVEL will soon be raised
+// to x, but not the number of templates. (Artifact from <= 4.0)
+const sal_uInt16 cnNumBullColls = 4;
+
+class SwAutoFormat
+{
+ SvxSwAutoFormatFlags m_aFlags;
+ SwPaM m_aDelPam; // a Pam that can be used
+ SwNodeIndex m_aNdIdx; // the index on the current TextNode
+ SwNodeIndex m_aEndNdIdx; // index on the end of the area
+
+ SwEditShell* m_pEditShell;
+ SwDoc* m_pDoc;
+ SwTextNode* m_pCurTextNd; // the current TextNode
+ SwTextFrame* m_pCurTextFrame; // frame of the current TextNode
+ bool m_bIsRightToLeft; // text direction of the current frame
+ SwNodeOffset m_nEndNdIdx; // for the percentage-display
+ mutable std::unique_ptr<CharClass> m_pCharClass; // Character classification
+ mutable LanguageType m_eCharClassLang;
+
+ sal_uInt16 m_nRedlAutoFormatSeqId;
+
+ enum
+ {
+ NONE = 0,
+ DELIM = 1,
+ DIGIT = 2,
+ CHG = 4,
+ LOWER_ALPHA = 8,
+ UPPER_ALPHA = 16,
+ LOWER_ROMAN = 32,
+ UPPER_ROMAN = 64,
+ NO_DELIM = (DIGIT|LOWER_ALPHA|UPPER_ALPHA|LOWER_ROMAN|UPPER_ROMAN)
+ };
+
+ bool m_bEnd : 1;
+ bool m_bMoreLines : 1;
+
+ CharClass& GetCharClass( LanguageType eLang ) const
+ {
+ if( !m_pCharClass || eLang != m_eCharClassLang )
+ {
+ m_pCharClass.reset( new CharClass( LanguageTag( eLang ) ) );
+ m_eCharClassLang = eLang;
+ }
+ return *m_pCharClass;
+ }
+
+ static bool IsSpace( const sal_Unicode c )
+ { return (' ' == c || '\t' == c || 0x0a == c|| 0x3000 == c /* Jap. space */); }
+
+ void SetColl( sal_uInt16 nId, bool bHdLineOrText = false );
+ void GoNextPara();
+ static bool HasObjects(const SwTextFrame &);
+
+ // TextNode methods
+ const SwTextFrame * GetNextNode(bool isCheckEnd = true) const;
+ static bool IsEmptyLine(const SwTextFrame & rFrame)
+ {
+ return rFrame.GetText().isEmpty()
+ || rFrame.GetText().getLength() == GetLeadingBlanks(rFrame.GetText());
+ }
+
+ bool IsOneLine(const SwTextFrame &) const;
+ bool IsFastFullLine(const SwTextFrame &) const;
+ bool IsNoAlphaLine(const SwTextFrame &) const;
+ bool IsEnumericChar(const SwTextFrame &) const;
+ static bool IsBlanksInString(const SwTextFrame&);
+ sal_uInt16 CalcLevel(const SwTextFrame&, sal_uInt16 *pDigitLvl = nullptr) const;
+ sal_Int32 GetBigIndent(TextFrameIndex & rCurrentSpacePos) const;
+
+ static OUString DelLeadingBlanks(const OUString& rStr);
+ static OUString DelTrailingBlanks( const OUString& rStr );
+ static sal_Int32 GetLeadingBlanks( const OUString& rStr );
+ static sal_Int32 GetTrailingBlanks( const OUString& rStr );
+
+ bool IsFirstCharCapital(const SwTextFrame & rNd) const;
+ sal_uInt16 GetDigitLevel(const SwTextFrame& rFrame, TextFrameIndex& rPos,
+ OUString* pPrefix = nullptr, OUString* pPostfix = nullptr,
+ OUString* pNumTypes = nullptr ) const;
+ /// get the FORMATTED TextFrame
+ SwTextFrame* GetFrame( const SwTextNode& rTextNd ) const;
+ SwTextFrame * EnsureFormatted(SwTextFrame const&) const;
+
+ void BuildIndent();
+ void BuildText();
+ void BuildTextIndent();
+ void BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel );
+ void BuildNegIndent( SwTwips nSpaces );
+ void BuildHeadLine( sal_uInt16 nLvl );
+
+ static bool HasBreakAttr(const SwTextFrame &);
+ void DeleteSel( SwPaM& rPam );
+ void DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect);
+ bool DeleteJoinCurNextPara(SwTextFrame const* pNextFrame, bool bIgnoreLeadingBlanks = false);
+ /// delete in the node start and/or end
+ void DeleteLeadingTrailingBlanks( bool bStart = true, bool bEnd = true );
+ void DelEmptyLine( bool bTstNextPara = true );
+ /// when using multiline paragraphs delete the "left" and/or
+ /// "right" margins
+ void DelMoreLinesBlanks( bool bWithLineBreaks = false );
+ /// join with the previous paragraph
+ void JoinPrevPara();
+ /// execute AutoCorrect on current TextNode
+ void AutoCorrect(TextFrameIndex nSttPos = TextFrameIndex(0));
+
+ bool CanJoin(const SwTextFrame * pNextFrame) const
+ {
+ return !m_bEnd && pNextFrame
+ && !IsEmptyLine(*pNextFrame)
+ && !IsNoAlphaLine(*pNextFrame)
+ && !IsEnumericChar(*pNextFrame)
+ // check the last / first nodes here...
+ && ((COMPLETE_STRING - 50 - pNextFrame->GetTextNodeFirst()->GetText().getLength())
+ > (m_pCurTextFrame->GetMergedPara()
+ ? m_pCurTextFrame->GetMergedPara()->pLastNode
+ : m_pCurTextNd)->GetText().getLength())
+ && !HasBreakAttr(*pNextFrame);
+ }
+
+ /// is a dot at the end ??
+ static bool IsSentenceAtEnd(const SwTextFrame & rTextFrame);
+
+ bool DoUnderline();
+ bool DoTable();
+
+ void SetRedlineText_( sal_uInt16 nId );
+ bool SetRedlineText( sal_uInt16 nId ) {
+ if( m_aFlags.bWithRedlining )
+ SetRedlineText_( nId );
+ return true;
+ }
+ void ClearRedlineText() {
+ if( m_aFlags.bWithRedlining )
+ m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment(nullptr);
+ }
+
+public:
+ SwAutoFormat( SwEditShell* pEdShell, SvxSwAutoFormatFlags const & rFlags,
+ SwNodeIndex const * pSttNd = nullptr, SwNodeIndex const * pEndNd = nullptr );
+};
+
+static const sal_Unicode* StrChr( const sal_Unicode* pSrc, sal_Unicode c )
+{
+ while( *pSrc && *pSrc != c )
+ ++pSrc;
+ return *pSrc ? pSrc : nullptr;
+}
+
+SwTextFrame* SwAutoFormat::GetFrame( const SwTextNode& rTextNd ) const
+{
+ // get the Frame
+ const SwContentFrame *pFrame = rTextNd.getLayoutFrame( m_pEditShell->GetLayout() );
+ assert(pFrame && "For Autoformat a Layout is needed");
+ return EnsureFormatted(*static_cast<SwTextFrame const*>(pFrame));
+}
+
+SwTextFrame * SwAutoFormat::EnsureFormatted(SwTextFrame const& rFrame) const
+{
+ SwTextFrame *const pFrame(const_cast<SwTextFrame*>(&rFrame));
+ if( m_aFlags.bAFormatByInput && !pFrame->isFrameAreaDefinitionValid() )
+ {
+ DisableCallbackAction a(*pFrame->getRootFrame());
+ SwRect aTmpFrame( pFrame->getFrameArea() );
+ SwRect aTmpPrt( pFrame->getFramePrintArea() );
+ pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ if( pFrame->getFrameArea() != aTmpFrame || pFrame->getFramePrintArea() != aTmpPrt ||
+ !pFrame->GetPaintSwRect().IsEmpty())
+ {
+ pFrame->SetCompletePaint();
+ }
+ }
+
+ return pFrame->GetFormatted();
+}
+
+void SwAutoFormat::SetRedlineText_( sal_uInt16 nActionId )
+{
+ OUString sText;
+ sal_uInt16 nSeqNo = 0;
+ if( STR_AUTOFMTREDL_END > nActionId )
+ {
+ sText = SwViewShell::GetShellRes()->GetAutoFormatNameLst()[ nActionId ];
+ switch( nActionId )
+ {
+ case STR_AUTOFMTREDL_SET_NUMBER_BULLET:
+ case STR_AUTOFMTREDL_DEL_MORELINES:
+
+ // AutoCorrect actions
+ case STR_AUTOFMTREDL_USE_REPLACE:
+ case STR_AUTOFMTREDL_CPTL_STT_WORD:
+ case STR_AUTOFMTREDL_CPTL_STT_SENT:
+ case STR_AUTOFMTREDL_TYPO:
+ case STR_AUTOFMTREDL_UNDER:
+ case STR_AUTOFMTREDL_BOLD:
+ case STR_AUTOFMTREDL_FRACTION:
+ case STR_AUTOFMTREDL_DASH:
+ case STR_AUTOFMTREDL_ORDINAL:
+ case STR_AUTOFMTREDL_NON_BREAK_SPACE:
+ case STR_AUTOFMTREDL_TRANSLITERATE_RTL:
+ nSeqNo = ++m_nRedlAutoFormatSeqId;
+ break;
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ sText = "Action text is missing";
+#endif
+
+ m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment( &sText, nSeqNo );
+}
+
+void SwAutoFormat::GoNextPara()
+{
+ SwNode* pNewNd = nullptr;
+ do {
+ // has to be checked twice before and after incrementation
+ if( m_aNdIdx.GetIndex() >= m_aEndNdIdx.GetIndex() )
+ {
+ m_bEnd = true;
+ return;
+ }
+
+ sw::GotoNextLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
+ if( m_aNdIdx.GetIndex() >= m_aEndNdIdx.GetIndex() )
+ {
+ m_bEnd = true;
+ return;
+ }
+ else
+ pNewNd = &m_aNdIdx.GetNode();
+
+ // not a TextNode ->
+ // TableNode : skip table
+ // NoTextNode : skip nodes
+ // EndNode : at the end, terminate
+ if( pNewNd->IsEndNode() )
+ {
+ m_bEnd = true;
+ return;
+ }
+ else if( pNewNd->IsTableNode() )
+ m_aNdIdx = *pNewNd->EndOfSectionNode();
+ else if( pNewNd->IsSectionNode() )
+ {
+ const SwSection& rSect = pNewNd->GetSectionNode()->GetSection();
+ if( rSect.IsHiddenFlag() || rSect.IsProtectFlag() )
+ m_aNdIdx = *pNewNd->EndOfSectionNode();
+ }
+ } while( !pNewNd->IsTextNode() );
+
+ if( !m_aFlags.bAFormatByInput )
+ ::SetProgressState( sal_Int32(m_aNdIdx.GetIndex() + m_nEndNdIdx - m_aEndNdIdx.GetIndex()),
+ m_pDoc->GetDocShell() );
+
+ m_pCurTextNd = static_cast<SwTextNode*>(pNewNd);
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ m_bIsRightToLeft = m_pCurTextFrame->IsRightToLeft();
+}
+
+bool SwAutoFormat::HasObjects(const SwTextFrame & rFrame)
+{
+ // Is there something bound to the paragraph in the paragraph
+ // like Frames, DrawObjects, ...
+ SwNodeIndex node(*rFrame.GetTextNodeFirst());
+ do
+ {
+ if (!node.GetNode().GetAnchoredFlys().empty())
+ return true;
+ ++node;
+ }
+ while (sw::FrameContainsNode(rFrame, node.GetIndex()));
+ return false;
+}
+
+const SwTextFrame* SwAutoFormat::GetNextNode(bool const isCheckEnd) const
+{
+ SwNodeIndex tmp(m_aNdIdx);
+ sw::GotoNextLayoutTextFrame(tmp, m_pEditShell->GetLayout());
+ if ((isCheckEnd && m_aEndNdIdx <= tmp) || !tmp.GetNode().IsTextNode())
+ return nullptr;
+ // note: the returned frame is not necessarily formatted, have to call
+ // EnsureFormatted for that
+ return static_cast<SwTextFrame*>(tmp.GetNode().GetTextNode()->getLayoutFrame(m_pEditShell->GetLayout()));
+}
+
+bool SwAutoFormat::IsOneLine(const SwTextFrame & rFrame) const
+{
+ SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
+ return aFInfo.IsOneLine();
+}
+
+bool SwAutoFormat::IsFastFullLine(const SwTextFrame & rFrame) const
+{
+ bool bRet = m_aFlags.bRightMargin;
+ if( bRet )
+ {
+ SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
+ bRet = aFInfo.IsFilled( m_aFlags.nRightMargin );
+ }
+ return bRet;
+}
+
+bool SwAutoFormat::IsEnumericChar(const SwTextFrame& rFrame) const
+{
+ const OUString& rText = rFrame.GetText();
+ TextFrameIndex nBlanks(GetLeadingBlanks(rText));
+ const TextFrameIndex nLen = TextFrameIndex(rText.getLength()) - nBlanks;
+ if( !nLen )
+ return false;
+
+ // -, +, * separated by blank ??
+ if (TextFrameIndex(2) < nLen && IsSpace(rText[sal_Int32(nBlanks) + 1]))
+ {
+ if (StrChr(pBulletChar, rText[sal_Int32(nBlanks)]))
+ return true;
+ // Should there be a symbol font at the position?
+ SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
+ if (aFInfo.IsBullet(nBlanks))
+ return true;
+ }
+
+ // 1.) / 1. / 1.1.1 / (1). / (1) / ...
+ return USHRT_MAX != GetDigitLevel(rFrame, nBlanks);
+}
+
+bool SwAutoFormat::IsBlanksInString(const SwTextFrame& rFrame)
+{
+ // Search more than 5 consecutive blanks/tabs in the string.
+ OUString sTmp( DelLeadingBlanks(rFrame.GetText()) );
+ const sal_Int32 nLen = sTmp.getLength();
+ sal_Int32 nIdx = 0;
+ while (nIdx < nLen)
+ {
+ // Skip non-blanks
+ while (nIdx < nLen && !IsSpace(sTmp[nIdx])) ++nIdx;
+ if (nIdx == nLen)
+ return false;
+ // Then count consecutive blanks
+ const sal_Int32 nFirst = nIdx;
+ while (nIdx < nLen && IsSpace(sTmp[nIdx])) ++nIdx;
+ // And exit if enough consecutive blanks were found
+ if (nIdx-nFirst > 5)
+ return true;
+ }
+ return false;
+}
+
+sal_uInt16 SwAutoFormat::CalcLevel(const SwTextFrame & rFrame,
+ sal_uInt16 *const pDigitLvl) const
+{
+ sal_uInt16 nLvl = 0, nBlnk = 0;
+ const OUString& rText = rFrame.GetText();
+ if( pDigitLvl )
+ *pDigitLvl = USHRT_MAX;
+
+ if (RES_POOLCOLL_TEXT_MOVE == rFrame.GetTextNodeForParaProps()->GetTextColl()->GetPoolFormatId())
+ {
+ if( m_aFlags.bAFormatByInput )
+ {
+ // this is very non-obvious: on the *first* invocation of
+ // AutoFormat, the node will have the tabs (any number) converted
+ // to a fixed indent in BuildTextIndent(), and the number of tabs
+ // is stored in the node;
+ // on the *second* invocation of AutoFormat, CalcLevel() will
+ // retrieve the stored number, and it will be used by
+ // BuildHeadLine() to select the corresponding heading style.
+ nLvl = rFrame.GetTextNodeForParaProps()->GetAutoFormatLvl();
+ const_cast<SwTextNode *>(rFrame.GetTextNodeForParaProps())->SetAutoFormatLvl(0);
+ if( nLvl )
+ return nLvl;
+ }
+ ++nLvl;
+ }
+
+ for (TextFrameIndex n(0),
+ nEnd(rText.getLength()); n < nEnd; ++n)
+ {
+ switch (rText[sal_Int32(n)])
+ {
+ case ' ': if( 3 == ++nBlnk )
+ {
+ ++nLvl;
+ nBlnk = 0;
+ }
+ break;
+ case '\t': ++nLvl;
+ nBlnk = 0;
+ break;
+ default:
+ if( pDigitLvl )
+ // test 1.) / 1. / 1.1.1 / (1). / (1) / ...
+ *pDigitLvl = GetDigitLevel(rFrame, n);
+ return nLvl;
+ }
+ }
+ return nLvl;
+}
+
+sal_Int32 SwAutoFormat::GetBigIndent(TextFrameIndex & rCurrentSpacePos) const
+{
+ SwTextFrameInfo aFInfo( m_pCurTextFrame );
+ const SwTextFrame* pNextFrame = nullptr;
+
+ if( !m_bMoreLines )
+ {
+ pNextFrame = GetNextNode();
+ if (!CanJoin(pNextFrame) || !IsOneLine(*pNextFrame))
+ return 0;
+
+ pNextFrame = EnsureFormatted(*pNextFrame);
+ }
+
+ return aFInfo.GetBigIndent( rCurrentSpacePos, pNextFrame );
+}
+
+bool SwAutoFormat::IsNoAlphaLine(const SwTextFrame & rFrame) const
+{
+ const OUString& rStr = rFrame.GetText();
+ if( rStr.isEmpty() )
+ return false;
+ // or better: determine via number of AlphaNum and !AlphaNum characters
+ sal_Int32 nANChar = 0, nBlnk = 0;
+
+ for (TextFrameIndex n(0),
+ nEnd(rStr.getLength()); n < nEnd; ++n)
+ if (IsSpace(rStr[sal_Int32(n)]))
+ ++nBlnk;
+ else
+ {
+ auto const pair = rFrame.MapViewToModel(n);
+ CharClass& rCC = GetCharClass(pair.first->GetSwAttrSet().GetLanguage().GetLanguage());
+ if (rCC.isLetterNumeric(rStr, sal_Int32(n)))
+ ++nANChar;
+ }
+
+ // If there are 75% of non-alphanumeric characters, then true
+ sal_uLong nLen = rStr.getLength() - nBlnk;
+ nLen = ( nLen * 3 ) / 4; // long overflow, if the strlen > sal_uInt16
+ return sal_Int32(nLen) < (rStr.getLength() - nANChar - nBlnk);
+}
+
+bool SwAutoFormat::DoUnderline()
+{
+ if( !m_aFlags.bSetBorder )
+ return false;
+
+ OUString const& rText(m_pCurTextFrame->GetText());
+ int eState = 0;
+ sal_Int32 nCnt = 0;
+ while (nCnt < rText.getLength())
+ {
+ int eTmp = 0;
+ switch (rText[nCnt])
+ {
+ case '-': eTmp = 1; break;
+ case '_': eTmp = 2; break;
+ case '=': eTmp = 3; break;
+ case '*': eTmp = 4; break;
+ case '~': eTmp = 5; break;
+ case '#': eTmp = 6; break;
+ default:
+ return false;
+ }
+ if( 0 == eState )
+ eState = eTmp;
+ else if( eState != eTmp )
+ return false;
+ ++nCnt;
+ }
+
+ if( 2 < nCnt )
+ {
+ // then underline the previous paragraph if one exists
+ DelEmptyLine( false ); // -> point will be on end of current paragraph
+ // WARNING: rText may be deleted now, m_pCurTextFrame may be nullptr
+ m_aDelPam.SetMark();
+ // apply to last node & rely on InsertItemSet to apply it to props-node
+ m_aDelPam.GetMark()->nContent = 0;
+
+ editeng::SvxBorderLine aLine;
+ switch( eState )
+ {
+ case 1: // single, hairline
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
+ aLine.SetWidth( SvxBorderLineWidth::Hairline );
+ break;
+ case 2: // single, thin
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
+ aLine.SetWidth( SvxBorderLineWidth::Thin );
+ break;
+ case 3: // double, thin
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ aLine.SetWidth( SvxBorderLineWidth::Thin );
+ break;
+ case 4: // double, thick/thin
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_SMALLGAP);
+ aLine.SetWidth( SvxBorderLineWidth::Thick );
+ break;
+ case 5: // double, thin/thick
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_SMALLGAP);
+ aLine.SetWidth( SvxBorderLineWidth::Thick );
+ break;
+ case 6: // double, medium
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ aLine.SetWidth( SvxBorderLineWidth::Medium );
+ break;
+ }
+ SfxItemSetFixed<RES_PARATR_CONNECT_BORDER, RES_PARATR_CONNECT_BORDER,
+ RES_BOX, RES_BOX> aSet(m_pDoc->GetAttrPool());
+ aSet.Put( SwParaConnectBorderItem( false ) );
+ SvxBoxItem aBox( RES_BOX );
+ aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ aBox.SetDistance(42, SvxBoxItemLine::BOTTOM ); // ~0,75 mm
+ aSet.Put(aBox);
+ m_pDoc->getIDocumentContentOperations().InsertItemSet(m_aDelPam, aSet,
+ SetAttrMode::DEFAULT, m_pEditShell->GetLayout());
+
+ m_aDelPam.DeleteMark();
+ }
+ return 2 < nCnt;
+}
+
+bool SwAutoFormat::DoTable()
+{
+ if( !m_aFlags.bCreateTable || !m_aFlags.bAFormatByInput ||
+ m_pCurTextNd->FindTableNode() )
+ return false;
+
+ const OUString& rTmp = m_pCurTextFrame->GetText();
+ TextFrameIndex nSttPlus(GetLeadingBlanks(rTmp));
+ TextFrameIndex nEndPlus(GetTrailingBlanks(rTmp));
+ sal_Unicode cChar;
+
+ if (TextFrameIndex(2) > nEndPlus - nSttPlus
+ || ('+' != (cChar = rTmp[sal_Int32(nSttPlus)]) && '|' != cChar)
+ || ('+' != (cChar = rTmp[sal_Int32(nEndPlus) - 1]) && '|' != cChar))
+ return false;
+
+ SwTextFrameInfo aInfo( m_pCurTextFrame );
+
+ TextFrameIndex n = nSttPlus;
+ std::vector<sal_uInt16> aPosArr;
+
+ while (n < TextFrameIndex(rTmp.getLength()))
+ {
+ switch (rTmp[sal_Int32(n)])
+ {
+ case '-':
+ case '_':
+ case '=':
+ case ' ':
+ case '\t':
+ break;
+
+ case '+':
+ case '|':
+ aPosArr.push_back( o3tl::narrowing<sal_uInt16>(aInfo.GetCharPos(n)) );
+ break;
+
+ default:
+ return false;
+ }
+ if( ++n == nEndPlus )
+ break;
+ }
+
+ if( 1 < aPosArr.size() )
+ {
+ // get the text node's alignment
+ sal_uInt16 nColCnt = aPosArr.size() - 1;
+ SwTwips nSttPos = aPosArr[ 0 ];
+ sal_Int16 eHori;
+ switch (m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust())
+ {
+ case SvxAdjust::Center: eHori = text::HoriOrientation::CENTER; break;
+ case SvxAdjust::Right: eHori = text::HoriOrientation::RIGHT; break;
+
+ default:
+ if( nSttPos )
+ {
+ eHori = text::HoriOrientation::NONE;
+ // then - as last - we need to add the current frame width into the array
+ aPosArr.push_back( o3tl::narrowing<sal_uInt16>(m_pCurTextFrame->getFrameArea().Width()) );
+ }
+ else
+ eHori = text::HoriOrientation::LEFT;
+ break;
+ }
+
+ // then create a table that matches the character
+ DelEmptyLine();
+ // WARNING: rTmp may be deleted now, m_pCurTextFrame may be nullptr
+ SwNodeIndex aIdx( m_aDelPam.GetPoint()->nNode );
+ m_aDelPam.Move( fnMoveForward );
+ m_pDoc->InsertTable( SwInsertTableOptions( SwInsertTableFlags::All , 1 ),
+ *m_aDelPam.GetPoint(), 1, nColCnt, eHori,
+ nullptr, &aPosArr );
+ m_aDelPam.GetPoint()->nNode = aIdx;
+ }
+ return 1 < aPosArr.size();
+}
+
+OUString SwAutoFormat::DelLeadingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL, n;
+ for( nL = rStr.getLength(), n = 0; n < nL && IsSpace( rStr[n] ); ++n )
+ ;
+ if( n ) // no Spaces
+ return rStr.copy(n);
+ return rStr;
+}
+
+OUString SwAutoFormat::DelTrailingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL = rStr.getLength(), n = nL;
+ if( !nL )
+ return rStr;
+
+ while( --n && IsSpace( rStr[ n ] ) )
+ ;
+ if( n+1 != nL ) // no Spaces
+ return rStr.copy( 0, n+1 );
+ return rStr;
+}
+
+sal_Int32 SwAutoFormat::GetLeadingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL;
+ sal_Int32 n;
+
+ for( nL = rStr.getLength(), n = 0; n < nL && IsSpace( rStr[ n ] ); ++n )
+ ;
+ return n;
+}
+
+sal_Int32 SwAutoFormat::GetTrailingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL = rStr.getLength(), n = nL;
+ if( !nL )
+ return 0;
+
+ while( --n && IsSpace( rStr[ n ] ) )
+ ;
+ return ++n;
+}
+
+bool SwAutoFormat::IsFirstCharCapital(const SwTextFrame& rFrame) const
+{
+ const OUString& rText = rFrame.GetText();
+ for (TextFrameIndex n(0),
+ nEnd(rText.getLength()); n < nEnd; ++n)
+ if (!IsSpace(rText[sal_Int32(n)]))
+ {
+ auto const pair = rFrame.MapViewToModel(n);
+ CharClass& rCC = GetCharClass( pair.first->GetSwAttrSet().
+ GetLanguage().GetLanguage() );
+ sal_Int32 nCharType = rCC.getCharacterType(rText, sal_Int32(n));
+ return CharClass::isLetterType( nCharType ) &&
+ 0 != ( i18n::KCharacterType::UPPER &
+ nCharType );
+ }
+ return false;
+}
+
+sal_uInt16
+SwAutoFormat::GetDigitLevel(const SwTextFrame& rFrame, TextFrameIndex& rPos,
+ OUString* pPrefix, OUString* pPostfix, OUString* pNumTypes ) const
+{
+
+ // check for 1.) / 1. / 1.1.1 / (1). / (1) / ...
+ const OUString& rText = rFrame.GetText();
+ sal_Int32 nPos(rPos);
+ int eScan = NONE;
+
+ sal_uInt16 nStart = 0;
+ sal_uInt8 nDigitLvl = 0, nDigitCnt = 0;
+ // count number of parenthesis to assure a sensible order is found
+ sal_uInt16 nOpeningParentheses = 0;
+ sal_uInt16 nClosingParentheses = 0;
+
+ while (nPos < rText.getLength() && nDigitLvl < MAXLEVEL - 1)
+ {
+ auto const pair = rFrame.MapViewToModel(TextFrameIndex(nPos));
+ CharClass& rCC = GetCharClass(pair.first->GetSwAttrSet().GetLanguage().GetLanguage());
+ const sal_Unicode cCurrentChar = rText[nPos];
+ if( ('0' <= cCurrentChar && '9' >= cCurrentChar) ||
+ (0xff10 <= cCurrentChar && 0xff19 >= cCurrentChar) )
+ {
+ if( eScan & DELIM )
+ {
+ if( eScan & CHG ) // not if it starts with a number
+ {
+ ++nDigitLvl;
+ if( pPostfix )
+ *pPostfix += "\x01";
+ }
+
+ if( pNumTypes )
+ *pNumTypes += OUStringChar(sal_Unicode('0' + SVX_NUM_ARABIC));
+
+ eScan = eScan | CHG;
+ }
+ else if( pNumTypes && !(eScan & DIGIT) )
+ *pNumTypes += OUStringChar(sal_Unicode('0' + SVX_NUM_ARABIC));
+
+ eScan &= ~DELIM; // remove Delim
+ if( 0 != (eScan & ~CHG) && DIGIT != (eScan & ~CHG))
+ return USHRT_MAX;
+
+ eScan |= DIGIT; // add Digit
+ if( 3 == ++nDigitCnt ) // more than 2 numbers are not an enum anymore
+ return USHRT_MAX;
+
+ nStart *= 10;
+ nStart += cCurrentChar <= '9' ? cCurrentChar - '0' : cCurrentChar - 0xff10;
+ }
+ else if( rCC.isAlpha( rText, nPos ) )
+ {
+ bool bIsUpper =
+ 0 != ( i18n::KCharacterType::UPPER &
+ rCC.getCharacterType( rText, nPos ));
+ sal_Unicode cLow = rCC.lowercase(rText, nPos, 1)[0], cNumTyp;
+ int eTmpScan;
+
+ // Roman numbers are "mdclxvi". Since we want to start numbering with c or d more often,
+ // convert first to characters and later to roman numbers if needed.
+ if( 256 > cLow && strchr( "mdclxvi", cLow ) )
+ {
+ if( bIsUpper )
+ {
+ cNumTyp = '0' + SVX_NUM_ROMAN_UPPER;
+ eTmpScan = UPPER_ROMAN;
+ }
+ else
+ {
+ cNumTyp = '0' + SVX_NUM_ROMAN_LOWER;
+ eTmpScan = LOWER_ROMAN;
+ }
+ }
+ else if( bIsUpper )
+ {
+ cNumTyp = '0' + SVX_NUM_CHARS_UPPER_LETTER;
+ eTmpScan = UPPER_ALPHA;
+ }
+ else
+ {
+ cNumTyp = '0' + SVX_NUM_CHARS_LOWER_LETTER;
+ eTmpScan = LOWER_ALPHA;
+ }
+
+ // Switch to roman numbers (only for c/d!)
+ if( 1 == nDigitCnt && ( eScan & (UPPER_ALPHA|LOWER_ALPHA) ) &&
+ ( 3 == nStart || 4 == nStart) && 256 > cLow &&
+ strchr( "mdclxvi", cLow ) &&
+ (( eScan & UPPER_ALPHA ) ? (eTmpScan & (UPPER_ALPHA|UPPER_ROMAN))
+ : (eTmpScan & (LOWER_ALPHA|LOWER_ROMAN))) )
+ {
+ sal_Unicode c = '0';
+ nStart = 3 == nStart ? 100 : 500;
+ if( UPPER_ALPHA == eTmpScan )
+ {
+ eTmpScan = UPPER_ROMAN;
+ c += SVX_NUM_ROMAN_UPPER;
+ }
+ else
+ {
+ eTmpScan = LOWER_ROMAN;
+ c += SVX_NUM_ROMAN_LOWER;
+ }
+
+ eScan = (eScan & ~(UPPER_ALPHA|LOWER_ALPHA)) | eTmpScan;
+ if( pNumTypes )
+ (*pNumTypes) = pNumTypes->replaceAt( pNumTypes->getLength() - 1, 1, rtl::OUStringChar(c) );
+ }
+
+ if( eScan & DELIM )
+ {
+ if( eScan & CHG ) // not if it starts with a number
+ {
+ ++nDigitLvl;
+ if( pPostfix )
+ *pPostfix += "\x01";
+ }
+
+ if( pNumTypes )
+ *pNumTypes += OUStringChar(cNumTyp);
+ eScan = eScan | CHG;
+ }
+ else if( pNumTypes && !(eScan & eTmpScan) )
+ *pNumTypes += OUStringChar(cNumTyp);
+
+ eScan &= ~DELIM; // remove Delim
+
+ // if another type is set, stop here
+ if( 0 != ( eScan & ~CHG ) && eTmpScan != ( eScan & ~CHG ))
+ return USHRT_MAX;
+
+ if( eTmpScan & (UPPER_ALPHA | LOWER_ALPHA) )
+ {
+ // allow characters only if they appear once
+ return USHRT_MAX;
+ }
+ else
+ {
+ // roman numbers, check if valid characters
+ sal_uInt16 nVal;
+ bool bError = false;
+ switch( cLow )
+ {
+ case 'm': nVal = 1000; goto CHECK_ROMAN_1;
+ case 'd': nVal = 500; goto CHECK_ROMAN_5;
+ case 'c': nVal = 100; goto CHECK_ROMAN_1;
+ case 'l': nVal = 50; goto CHECK_ROMAN_5;
+ case 'x': nVal = 10; goto CHECK_ROMAN_1;
+ case 'v': nVal = 5; goto CHECK_ROMAN_5;
+
+CHECK_ROMAN_1:
+ {
+ int nMod5 = nStart % (nVal * 5);
+ int nLast = nStart % nVal;
+ int n10 = nVal / 10;
+
+ if( nMod5 == ((3 * nVal) + n10 ) ||
+ nMod5 == ((4 * nVal) + n10 ) ||
+ nLast == n10 )
+ nStart = o3tl::narrowing<sal_uInt16>(nStart + (n10 * 8));
+ else if( nMod5 == 0 ||
+ nMod5 == (1 * nVal) ||
+ nMod5 == (2 * nVal) )
+ nStart = nStart + nVal;
+ else
+ bError = true;
+ }
+ break;
+
+CHECK_ROMAN_5:
+ {
+ if( ( nStart / nVal ) & 1 )
+ bError = true;
+ else
+ {
+ int nMod = nStart % nVal;
+ int n10 = nVal / 5;
+ if( n10 == nMod )
+ nStart = o3tl::narrowing<sal_uInt16>(nStart + (3 * n10));
+ else if( 0 == nMod )
+ nStart = nStart + nVal;
+ else
+ bError = true;
+ }
+ }
+ break;
+
+ case 'i':
+ if( nStart % 5 >= 3 )
+ bError = true;
+ else
+ nStart += 1;
+ break;
+
+ default:
+ bError = true;
+ }
+
+ if( bError )
+ return USHRT_MAX;
+ }
+ eScan |= eTmpScan; // add Digit
+ ++nDigitCnt;
+ }
+ else if( (256 > cCurrentChar &&
+ strchr( ".)(", cCurrentChar )) ||
+ 0x3002 == cCurrentChar /* Chinese trad. dot */||
+ 0xff0e == cCurrentChar /* Japanese dot */||
+ 0xFF08 == cCurrentChar /* opening bracket Chin./Jap.*/||
+ 0xFF09 == cCurrentChar )/* closing bracket Chin./Jap. */
+ {
+ if(cCurrentChar == '(' || cCurrentChar == 0xFF09)
+ nOpeningParentheses++;
+ else if(cCurrentChar == ')'|| cCurrentChar == 0xFF08)
+ nClosingParentheses++;
+ // only if no numbers were read until here
+ if( pPrefix && !( eScan & ( NO_DELIM | CHG )) )
+ *pPrefix += OUStringChar(rText[nPos]);
+ else if( pPostfix )
+ *pPostfix += OUStringChar(rText[nPos]);
+
+ if( NO_DELIM & eScan )
+ {
+ eScan |= CHG;
+ if( pPrefix )
+ *pPrefix += "\x01" + OUString::number( nStart );
+ }
+ eScan &= ~NO_DELIM; // remove Delim
+ eScan |= DELIM; // add Digit
+ nDigitCnt = 0;
+ nStart = 0;
+ }
+ else
+ break;
+ ++nPos;
+ }
+ if (!( CHG & eScan ) || rPos == TextFrameIndex(nPos) ||
+ nPos == rText.getLength() || !IsSpace(rText[nPos]) ||
+ (nOpeningParentheses > nClosingParentheses))
+ return USHRT_MAX;
+
+ if( (NO_DELIM & eScan) && pPrefix ) // do not forget the last one
+ *pPrefix += "\x01" + OUString::number( nStart );
+
+ rPos = TextFrameIndex(nPos);
+ return nDigitLvl; // 0 .. 9 (MAXLEVEL - 1)
+}
+
+void SwAutoFormat::SetColl( sal_uInt16 nId, bool bHdLineOrText )
+{
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeForParaProps();
+ m_aDelPam.GetPoint()->nContent.Assign(m_aDelPam.GetPoint()->nNode.GetNode().GetContentNode(), 0);
+
+ // keep hard tabs, alignment, language, hyphenation, DropCaps and nearly all frame attributes
+ SfxItemSetFixed<
+ RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE,
+ RES_PARATR_ADJUST, RES_PARATR_ADJUST,
+ RES_PARATR_TABSTOP, RES_PARATR_DROP,
+ RES_BACKGROUND, RES_SHADOW> aSet(m_pDoc->GetAttrPool());
+
+ if (m_aDelPam.GetPoint()->nNode.GetNode().GetTextNode()->HasSwAttrSet())
+ {
+ aSet.Put(*m_aDelPam.GetPoint()->nNode.GetNode().GetTextNode()->GetpSwAttrSet());
+ // take HeaderLine/TextBody only if centered or right aligned, otherwise only justification
+ if( SvxAdjustItem const * pAdj = aSet.GetItemIfSet( RES_PARATR_ADJUST, false) )
+ {
+ SvxAdjust eAdj = pAdj->GetAdjust();
+ if( bHdLineOrText ? (SvxAdjust::Right != eAdj &&
+ SvxAdjust::Center != eAdj)
+ : SvxAdjust::Block != eAdj )
+ aSet.ClearItem( RES_PARATR_ADJUST );
+ }
+ }
+
+ m_pDoc->SetTextFormatCollByAutoFormat( *m_aDelPam.GetPoint(), nId, &aSet );
+}
+
+static bool HasSelBlanks(
+ SwTextFrame const*const pStartFrame, TextFrameIndex & rStartIndex,
+ SwTextFrame const*const pEndFrame, TextFrameIndex & rEndIndex)
+{
+ if (TextFrameIndex(0) < rEndIndex
+ && rEndIndex < TextFrameIndex(pEndFrame->GetText().getLength())
+ && ' ' == pEndFrame->GetText()[sal_Int32(rEndIndex) - 1])
+ {
+ --rEndIndex;
+ return true;
+ }
+ if (rStartIndex < TextFrameIndex(pStartFrame->GetText().getLength())
+ && ' ' == pStartFrame->GetText()[sal_Int32(rStartIndex)])
+ {
+ ++rStartIndex;
+ return true;
+ }
+ return false;
+}
+
+bool SwAutoFormat::HasBreakAttr(const SwTextFrame& rTextFrame)
+{
+ const SfxItemSet *const pSet = rTextFrame.GetTextNodeFirst()->GetpSwAttrSet();
+ if( !pSet )
+ return false;
+
+ const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet( RES_BREAK, false );
+ if( pBreakItem && SvxBreak::NONE != pBreakItem->GetBreak() )
+ return true;
+
+ const SwFormatPageDesc* pItem = pSet->GetItemIfSet( RES_PAGEDESC, false );
+ if( pItem && pItem->GetPageDesc()
+ && UseOnPage::NONE != pItem->GetPageDesc()->GetUseOn() )
+ return true;
+ return false;
+}
+
+/// Is there a dot at the end?
+bool SwAutoFormat::IsSentenceAtEnd(const SwTextFrame & rTextFrame)
+{
+ const OUString& rStr = rTextFrame.GetText();
+ sal_Int32 n = rStr.getLength();
+ if( !n )
+ return true;
+
+ while( --n && IsSpace( rStr[ n ] ) )
+ ;
+ return '.' == rStr[ n ];
+}
+
+/// Delete beginning and/or end in a node
+void SwAutoFormat::DeleteLeadingTrailingBlanks(bool bStart, bool bEnd)
+{
+ if( !(m_aFlags.bAFormatByInput
+ ? m_aFlags.bAFormatByInpDelSpacesAtSttEnd
+ : m_aFlags.bAFormatDelSpacesAtSttEnd) )
+ return;
+
+ // delete blanks at the end of the current and at the beginning of the next one
+ m_aDelPam.DeleteMark();
+ TextFrameIndex nPos(GetLeadingBlanks(m_pCurTextFrame->GetText()));
+ if (bStart && TextFrameIndex(0) != nPos)
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ DeleteSel( m_aDelPam );
+ m_aDelPam.DeleteMark();
+ }
+ nPos = TextFrameIndex(GetTrailingBlanks(m_pCurTextFrame->GetText()));
+ if (bEnd && TextFrameIndex(m_pCurTextFrame->GetText().getLength()) != nPos)
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ DeleteSel( m_aDelPam );
+ m_aDelPam.DeleteMark();
+ }
+}
+
+namespace sw {
+
+bool GetRanges(std::vector<std::shared_ptr<SwUnoCursor>> & rRanges,
+ SwDoc & rDoc, SwPaM const& rDelPam)
+{
+ bool isNoRedline(true);
+ SwRedlineTable::size_type tmp;
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+ if (!(rIDRA.GetRedlineFlags() & RedlineFlags::ShowDelete))
+ {
+ return isNoRedline;
+ }
+ rIDRA.GetRedline(*rDelPam.Start(), &tmp);
+ SwPosition const* pCurrent(rDelPam.Start());
+ for ( ; tmp < rIDRA.GetRedlineTable().size(); ++tmp)
+ {
+ SwRangeRedline const*const pRedline(rIDRA.GetRedlineTable()[tmp]);
+ if (*rDelPam.End() <= *pRedline->Start())
+ {
+ break;
+ }
+ if (*pRedline->End() <= *rDelPam.Start())
+ {
+ continue;
+ }
+ if (pRedline->GetType() == RedlineType::Delete)
+ {
+ assert(*pRedline->Start() != *pRedline->End());
+ isNoRedline = false;
+ if (*pCurrent < *pRedline->Start())
+ {
+ rRanges.push_back(rDoc.CreateUnoCursor(*pCurrent));
+ rRanges.back()->SetMark();
+ *rRanges.back()->GetPoint() = *pRedline->Start();
+ }
+ pCurrent = pRedline->End();
+ }
+ }
+ if (!isNoRedline && *pCurrent < *rDelPam.End())
+ {
+ rRanges.push_back(rDoc.CreateUnoCursor(*pCurrent));
+ rRanges.back()->SetMark();
+ *rRanges.back()->GetPoint() = *rDelPam.End();
+ }
+ return isNoRedline;
+}
+
+} // namespace sw
+
+void SwAutoFormat::DeleteSel(SwPaM & rDelPam)
+{
+ std::vector<std::shared_ptr<SwUnoCursor>> ranges; // need correcting cursor
+ if (GetRanges(ranges, *m_pDoc, rDelPam))
+ {
+ DeleteSelImpl(rDelPam, rDelPam);
+ }
+ else
+ {
+ for (auto const& pCursor : ranges)
+ {
+ DeleteSelImpl(*pCursor, rDelPam);
+ }
+ }
+}
+
+void SwAutoFormat::DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect)
+{
+ if (m_aFlags.bWithRedlining || &rDelPam != &rPamToCorrect)
+ {
+ // Add to Shell-Cursor-Ring so that DelPam will be moved as well!
+ SwPaM* pShCursor = m_pEditShell->GetCursor_();
+ SwPaM aTmp( *m_pCurTextNd, 0, pShCursor );
+
+ SwPaM* pPrev = rPamToCorrect.GetPrev();
+ rPamToCorrect.GetRingContainer().merge( pShCursor->GetRingContainer() );
+
+ m_pEditShell->DeleteSel(rDelPam, true);
+
+ // and remove Pam again:
+ SwPaM* p;
+ SwPaM* pNext = &rPamToCorrect;
+ do {
+ p = pNext;
+ pNext = p->GetNext();
+ p->MoveTo( &rPamToCorrect );
+ } while( p != pPrev );
+
+ m_aNdIdx = aTmp.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame(*m_pCurTextNd); // keep it up to date
+ }
+ else
+ m_pEditShell->DeleteSel(rDelPam, true);
+}
+
+bool SwAutoFormat::DeleteJoinCurNextPara(SwTextFrame const*const pNextFrame,
+ bool const bIgnoreLeadingBlanks)
+{
+ // delete blanks at the end of the current and at the beginning of the next one
+ m_aDelPam.DeleteMark();
+ TextFrameIndex nTrailingPos(GetTrailingBlanks(m_pCurTextFrame->GetText()));
+
+ SwTextFrame const*const pEndFrame(pNextFrame ? pNextFrame : m_pCurTextFrame);
+ TextFrameIndex nLeadingPos(0);
+ if (pNextFrame)
+ {
+ nLeadingPos = TextFrameIndex(
+ bIgnoreLeadingBlanks ? 0 : GetLeadingBlanks(pNextFrame->GetText()));
+ }
+ else
+ {
+ nLeadingPos = TextFrameIndex(m_pCurTextFrame->GetText().getLength());
+ }
+
+ // Is there a Blank at the beginning or end?
+ // Do not delete it, it will be inserted again.
+ bool bHasBlnks = HasSelBlanks(m_pCurTextFrame, nTrailingPos, pEndFrame, nLeadingPos);
+
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nTrailingPos);
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = pEndFrame->MapViewToModelPos(nLeadingPos);
+
+ if( *m_aDelPam.GetPoint() != *m_aDelPam.GetMark() )
+ DeleteSel( m_aDelPam );
+ m_aDelPam.DeleteMark();
+ // note: keep m_aDelPam point at insert pos. for clients
+
+ return !bHasBlnks;
+}
+
+void SwAutoFormat::DelEmptyLine( bool bTstNextPara )
+{
+ SetRedlineText( STR_AUTOFMTREDL_DEL_EMPTY_PARA );
+ // delete blanks in empty paragraph
+ m_aDelPam.DeleteMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(0));
+ m_aDelPam.SetMark();
+
+ m_aDelPam.GetMark()->nNode = m_pCurTextFrame->GetTextNodeFirst()->GetIndex() - 1;
+ SwTextNode* pTNd = m_aDelPam.GetNode( false ).GetTextNode();
+ if( pTNd )
+ // first use the previous text node
+ m_aDelPam.GetMark()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+ else if( bTstNextPara )
+ {
+ // then try the next (at the beginning of a Doc, table cells, frames, ...)
+ m_aDelPam.GetMark()->nNode = (m_pCurTextFrame->GetMergedPara()
+ ? m_pCurTextFrame->GetMergedPara()->pLastNode
+ : m_pCurTextNd
+ )->GetIndex() + 1;
+ pTNd = m_aDelPam.GetNode( false ).GetTextNode();
+ if( pTNd )
+ {
+ m_aDelPam.GetMark()->nContent.Assign( pTNd, 0 );
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
+ }
+ }
+ if( pTNd )
+ { // join with previous or next paragraph
+ DeleteSel(m_aDelPam);
+ }
+ assert(m_aDelPam.GetNode().IsTextNode());
+ assert(!m_aDelPam.HasMark());
+ m_aDelPam.SetMark(); // mark remains at join position
+ m_pCurTextFrame = GetFrame(*m_aDelPam.GetNode().GetTextNode());
+ // replace until the end of the merged paragraph
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
+ if (*m_aDelPam.GetPoint() != *m_aDelPam.GetMark())
+ { // tdf#137245 replace (not delete) to preserve any flys
+ m_pDoc->getIDocumentContentOperations().ReplaceRange(m_aDelPam, "", false);
+ }
+
+ m_aDelPam.DeleteMark();
+ ClearRedlineText();
+ // note: this likely has deleted m_pCurTextFrame - update it...
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = m_pCurTextNd ? GetFrame( *m_pCurTextNd ) : nullptr;
+}
+
+void SwAutoFormat::DelMoreLinesBlanks( bool bWithLineBreaks )
+{
+ if( !(m_aFlags.bAFormatByInput
+ ? m_aFlags.bAFormatByInpDelSpacesBetweenLines
+ : m_aFlags.bAFormatDelSpacesBetweenLines) )
+ return;
+
+ // delete all blanks on the left and right of the indentation
+ m_aDelPam.DeleteMark();
+
+ SwTextFrameInfo aFInfo( m_pCurTextFrame );
+ std::vector<std::pair<TextFrameIndex, TextFrameIndex>> spaces;
+ aFInfo.GetSpaces(spaces, !m_aFlags.bAFormatByInput || bWithLineBreaks);
+
+ // tdf#123285 iterate backwards - delete invalidates following indexes
+ for (auto iter = spaces.rbegin(); iter != spaces.rend(); ++iter)
+ {
+ auto & rSpaceRange(*iter);
+ assert(rSpaceRange.first != rSpaceRange.second);
+ bool const bHasBlanks = HasSelBlanks(
+ m_pCurTextFrame, rSpaceRange.first,
+ m_pCurTextFrame, rSpaceRange.second);
+ if (rSpaceRange.first != rSpaceRange.second)
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(rSpaceRange.first);
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(rSpaceRange.second);
+ DeleteSel(m_aDelPam);
+ if (!bHasBlanks)
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString(m_aDelPam, OUString(' '));
+ }
+ m_aDelPam.DeleteMark();
+ }
+ }
+}
+
+void SwAutoFormat::JoinPrevPara()
+{
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeFirst();
+ m_aDelPam.GetPoint()->nContent.Assign(m_pCurTextFrame->GetTextNodeFirst(), 0);
+ m_aDelPam.SetMark();
+
+ --m_aDelPam.GetPoint()->nNode;
+ SwTextNode* pTNd = m_aDelPam.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ // use the previous text node first
+ m_aDelPam.GetPoint()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+ DeleteSel( m_aDelPam );
+ }
+ m_aDelPam.DeleteMark();
+}
+
+void SwAutoFormat::BuildIndent()
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_INDENT );
+
+ // read all succeeding paragraphs that belong to this indentation
+ bool bBreak = true;
+ if( m_bMoreLines )
+ DelMoreLinesBlanks( true );
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+ SetColl( RES_POOLCOLL_TEXT_IDENT );
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ if (pNextFrame && !m_bEnd)
+ {
+ do {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ pNextFrame = GetNextNode();
+ }
+ while (CanJoin(pNextFrame)
+ && !CalcLevel(*pNextFrame));
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildTextIndent()
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_TEXT_INDENT);
+ // read all succeeding paragraphs that belong to this indentation
+ bool bBreak = true;
+ if( m_bMoreLines )
+ DelMoreLinesBlanks( true );
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+
+ if( m_aFlags.bAFormatByInput )
+ {
+ const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAutoFormatLvl(
+ static_cast<sal_uInt8>(CalcLevel(*m_pCurTextFrame)));
+ }
+
+ SetColl( RES_POOLCOLL_TEXT_MOVE );
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame) &&
+ CalcLevel(*pNextFrame))
+ {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ pNextFrame = GetNextNode();
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildText()
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_TEXT );
+ // read all succeeding paragraphs that belong to this text without indentation
+ bool bBreak = true;
+ if( m_bMoreLines )
+ DelMoreLinesBlanks();
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+ SetColl( RES_POOLCOLL_TEXT, true );
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame) &&
+ !CalcLevel(*pNextFrame))
+ {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ const SwTextFrame *const pCurrNode = pNextFrame;
+ pNextFrame = GetNextNode();
+ if (!pNextFrame || pCurrNode == pNextFrame)
+ break;
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel )
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_NUMBER_BULLET );
+
+ bool bBreak = true;
+
+ // first, determine current indentation and frame width
+ SwTwips nFrameWidth = m_pCurTextFrame->getFramePrintArea().Width();
+ SwTwips nLeftTextPos;
+ {
+ TextFrameIndex nPos(0);
+ while (nPos < TextFrameIndex(m_pCurTextFrame->GetText().getLength())
+ && IsSpace(m_pCurTextFrame->GetText()[sal_Int32(nPos)]))
+ {
+ ++nPos;
+ }
+
+ SwTextFrameInfo aInfo( m_pCurTextFrame );
+ nLeftTextPos = aInfo.GetCharPos(nPos);
+ nLeftTextPos -= m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace().GetLeft();
+ }
+
+ if( m_bMoreLines )
+ DelMoreLinesBlanks();
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+ bool bRTL = m_pEditShell->IsInRightToLeftText();
+ DeleteLeadingTrailingBlanks();
+
+ bool bChgBullet = false, bChgEnum = false;
+ TextFrameIndex nAutoCorrPos(0);
+
+ // if numbering is set, get the current one
+ SwNumRule aRule( m_pDoc->GetUniqueNumRuleName(),
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode() );
+
+ const SwNumRule* pCur = nullptr;
+ if (m_aFlags.bSetNumRule)
+ {
+ pCur = m_pCurTextFrame->GetTextNodeForParaProps()->GetNumRule();
+ if (pCur)
+ {
+ aRule = *pCur;
+ }
+ }
+
+ // replace bullet character with defined one
+ const OUString& rStr = m_pCurTextFrame->GetText();
+ TextFrameIndex nTextStt(0);
+ const sal_Unicode* pFndBulletChr = nullptr;
+ if (m_aFlags.bChgEnumNum && 2 < rStr.getLength())
+ pFndBulletChr = StrChr(pBulletChar, rStr[sal_Int32(nTextStt)]);
+ if (nullptr != pFndBulletChr && IsSpace(rStr[sal_Int32(nTextStt) + 1]))
+ {
+ if( m_aFlags.bAFormatByInput )
+ {
+ if( m_aFlags.bSetNumRule )
+ {
+ SwCharFormat* pCFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_BULLET_LEVEL );
+ bChgBullet = true;
+ // Was the format already somewhere adjusted?
+ if( !aRule.GetNumFormat( nLvl ) )
+ {
+ int nBulletPos = pFndBulletChr - pBulletChar;
+ sal_UCS4 cBullChar;
+ const vcl::Font* pBullFnt( nullptr );
+ if( nBulletPos < cnPosEnDash )
+ {
+ cBullChar = m_aFlags.cBullet;
+ pBullFnt = &m_aFlags.aBulletFont;
+ }
+ else
+ {
+ cBullChar = nBulletPos < cnPosEmDash
+ ? cStarSymbolEnDash
+ : cStarSymbolEmDash;
+ // #i63395#
+ // Only apply user defined default bullet font
+ if ( numfunc::IsDefBulletFontUserDefined() )
+ {
+ pBullFnt = &numfunc::GetDefBulletFont();
+ }
+ }
+
+ sal_Int32 nAbsPos = lBulletIndent;
+ SwTwips nSpaceSteps = nLvl
+ ? nLeftTextPos / nLvl
+ : lBulletIndent;
+ for( sal_uInt8 n = 0; n < MAXLEVEL; ++n, nAbsPos = nAbsPos + nSpaceSteps )
+ {
+ SwNumFormat aFormat( aRule.Get( n ) );
+ aFormat.SetBulletFont( pBullFnt );
+ aFormat.SetBulletChar( cBullChar );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ // #i93908# clear suffix for bullet lists
+ aFormat.SetPrefix(OUString());
+ aFormat.SetSuffix(OUString());
+ aFormat.SetFirstLineOffset( lBulletFirstLineOffset );
+ aFormat.SetAbsLSpace( nAbsPos );
+ if( !aFormat.GetCharFormat() )
+ aFormat.SetCharFormat( pCFormat );
+ if( bRTL )
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+
+ aRule.Set( n, aFormat );
+
+ if( n == nLvl &&
+ nFrameWidth < ( nSpaceSteps * MAXLEVEL ) )
+ nSpaceSteps = ( nFrameWidth - nLeftTextPos ) /
+ ( MAXLEVEL - nLvl );
+ }
+ }
+ }
+ }
+ else
+ {
+ bChgBullet = true;
+ SetColl( o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_BULLET_LEVEL1 + ( std::min( nLvl, cnNumBullColls ) * 4 )) );
+ }
+ }
+ else
+ {
+ // Then it is a numbering
+
+ //JP 21.11.97: The NumLevel is either the DigitLevel or, if the latter is not existent or 0,
+ // it is determined by the indentation level.
+
+ OUString aPostfix, aPrefix, aNumTypes;
+ nDigitLevel = GetDigitLevel(*m_pCurTextFrame, nTextStt,
+ &aPrefix, &aPostfix, &aNumTypes);
+ if (USHRT_MAX != nDigitLevel)
+ {
+ bChgEnum = true;
+
+ // Level 0 and Indentation, determine level by left indentation and default NumIndent
+ if( !nDigitLevel && nLeftTextPos )
+ nLvl = std::min( sal_uInt16( nLeftTextPos / lNumberIndent ),
+ sal_uInt16( MAXLEVEL - 1 ) );
+ else
+ nLvl = nDigitLevel;
+ }
+
+ if( bChgEnum && m_aFlags.bSetNumRule )
+ {
+ if( !pCur ) // adjust NumRule if it is new
+ {
+ SwCharFormat* pCFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_NUM_LEVEL );
+
+ sal_Int32 nPrefixIdx{ 0 };
+ if( !nDigitLevel )
+ {
+ SwNumFormat aFormat( aRule.Get( nLvl ) );
+ aFormat.SetPrefix( aPrefix.getToken( 0, u'\x0001', nPrefixIdx ));
+ aFormat.SetStart( o3tl::narrowing<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aPrefix, 0, u'\x0001', nPrefixIdx ))));
+ aFormat.SetSuffix( aPostfix.getToken( 0, u'\x0001' ));
+ aFormat.SetIncludeUpperLevels( 0 );
+
+ if( !aFormat.GetCharFormat() )
+ aFormat.SetCharFormat( pCFormat );
+
+ if( !aNumTypes.isEmpty() )
+ aFormat.SetNumberingType(static_cast<SvxNumType>(aNumTypes[ 0 ] - '0'));
+
+ if( bRTL )
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+ aRule.Set( nLvl, aFormat );
+ }
+ else
+ {
+ auto const nSpaceSteps = nLvl ? nLeftTextPos / nLvl : 0;
+ sal_uInt16 n;
+ sal_Int32 nPostfixIdx{ 0 };
+ for( n = 0; n <= nLvl; ++n )
+ {
+ SwNumFormat aFormat( aRule.Get( n ) );
+
+ if( !n )
+ aFormat.SetPrefix( aPrefix.getToken( 0, u'\x0001', nPrefixIdx )); // token 0, read only on first loop
+ aFormat.SetStart( o3tl::narrowing<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aPrefix, 0, u'\x0001', nPrefixIdx )) ));
+ aFormat.SetSuffix( aPostfix.getToken( 0, u'\x0001', nPostfixIdx ));
+ aFormat.SetIncludeUpperLevels( MAXLEVEL );
+ if( n < aNumTypes.getLength() )
+ aFormat.SetNumberingType(static_cast<SvxNumType>(aNumTypes[ n ] - '0'));
+
+ aFormat.SetAbsLSpace( nSpaceSteps * n
+ + lNumberIndent );
+
+ if( !aFormat.GetCharFormat() )
+ aFormat.SetCharFormat( pCFormat );
+ if( bRTL )
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+
+ aRule.Set( n, aFormat );
+ }
+
+ // Does it fit completely into the frame?
+ bool bDefStep = nFrameWidth < (nSpaceSteps * MAXLEVEL);
+ for( ; n < MAXLEVEL; ++n )
+ {
+ SwNumFormat aFormat( aRule.Get( n ) );
+ aFormat.SetIncludeUpperLevels( MAXLEVEL );
+ if( bDefStep )
+ aFormat.SetAbsLSpace( nLeftTextPos +
+ SwNumRule::GetNumIndent(static_cast<sal_uInt8>(n-nLvl)));
+ else
+ aFormat.SetAbsLSpace( nSpaceSteps * n
+ + lNumberIndent );
+ aRule.Set( n, aFormat );
+ }
+ }
+ }
+ }
+ else if( !m_aFlags.bAFormatByInput )
+ SetColl( o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_NUM_LEVEL1 + ( std::min( nLvl, cnNumBullColls ) * 4 ) ));
+ else
+ bChgEnum = false;
+ }
+
+ if ( bChgEnum || bChgBullet )
+ {
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeForParaProps();
+
+ if( m_aFlags.bSetNumRule )
+ {
+ if( m_aFlags.bAFormatByInput )
+ {
+ m_aDelPam.SetMark();
+ SwTextFrame const*const pNextFrame = GetNextNode(false);
+ assert(pNextFrame);
+ m_aDelPam.GetMark()->nNode = *pNextFrame->GetTextNodeForParaProps();
+ m_aDelPam.GetNode(false).GetTextNode()->SetAttrListLevel( nLvl );
+ }
+
+ const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAttrListLevel(nLvl);
+
+ // start new list
+ m_pDoc->SetNumRule(m_aDelPam, aRule, true, m_pEditShell->GetLayout());
+ m_aDelPam.DeleteMark();
+
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ bChgEnum ? nTextStt : TextFrameIndex(0));
+ }
+ m_aDelPam.SetMark();
+
+ if ( bChgBullet )
+ nTextStt += TextFrameIndex(2);
+
+ while (nTextStt < TextFrameIndex(rStr.getLength()) && IsSpace(rStr[sal_Int32(nTextStt)]))
+ nTextStt++;
+
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nTextStt);
+ DeleteSel( m_aDelPam );
+
+ if( !m_aFlags.bSetNumRule )
+ {
+ OUString sChgStr('\t');
+ if( bChgBullet )
+ sChgStr = OUString(&m_aFlags.cBullet, 1) + sChgStr;
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, sChgStr );
+
+ SfxItemSet aSet( m_pDoc->GetAttrPool(), aTextNodeSetRange );
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ assert(&m_aDelPam.GetPoint()->nNode.GetNode() == m_pCurTextFrame->GetTextNodeForParaProps());
+ if( bChgBullet )
+ {
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(1));
+ SetAllScriptItem( aSet,
+ SvxFontItem( m_aFlags.aBulletFont.GetFamilyType(),
+ m_aFlags.aBulletFont.GetFamilyName(),
+ m_aFlags.aBulletFont.GetStyleName(),
+ m_aFlags.aBulletFont.GetPitch(),
+ m_aFlags.aBulletFont.GetCharSet(),
+ RES_CHRATR_FONT ) );
+ m_pDoc->SetFormatItemByAutoFormat( m_aDelPam, aSet );
+ m_aDelPam.DeleteMark();
+ nAutoCorrPos = TextFrameIndex(2);
+ aSet.ClearItem();
+ }
+ SvxTabStopItem aTStops( RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( 0 ) );
+ aSet.Put( aTStops );
+ assert(&m_aDelPam.GetPoint()->nNode.GetNode() == m_pCurTextFrame->GetTextNodeForParaProps());
+ m_pDoc->SetFormatItemByAutoFormat( m_aDelPam, aSet );
+ }
+ }
+
+ if( bBreak )
+ {
+ AutoCorrect( nAutoCorrPos ); /* Offset due to Bullet + Tab */
+ return;
+ }
+
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame)
+ && nLvl == CalcLevel(*pNextFrame))
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ const SwTextFrame *const pCurrNode = pNextFrame;
+ pNextFrame = GetNextNode();
+ if (!pNextFrame || pCurrNode == pNextFrame)
+ break;
+ }
+ DeleteLeadingTrailingBlanks( false );
+ AutoCorrect( nAutoCorrPos );
+}
+
+void SwAutoFormat::BuildNegIndent( SwTwips nSpaces )
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_NEG_INDENT );
+ // Test of contraposition (n words, divided by spaces/tabs, with same indentation in 2nd line)
+
+ // read all succeeding paragraphs that belong to this enumeration
+ bool bBreak = true;
+ TextFrameIndex nSpacePos(0);
+ const sal_Int32 nTextPos = GetBigIndent( nSpacePos );
+ if( m_bMoreLines )
+ DelMoreLinesBlanks( true );
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || (!nTextPos && IsBlanksInString(*m_pCurTextFrame))
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+
+ SetColl( o3tl::narrowing<sal_uInt16>( nTextPos
+ ? RES_POOLCOLL_CONFRONTATION
+ : RES_POOLCOLL_TEXT_NEGIDENT ) );
+
+ if( nTextPos )
+ {
+ const OUString& rStr = m_pCurTextFrame->GetText();
+ bool bInsTab = true;
+
+ if ('\t' == rStr[sal_Int32(nSpacePos) + 1]) // leave tab alone
+ {
+ --nSpacePos;
+ bInsTab = false;
+ }
+
+ TextFrameIndex nSpaceStt = nSpacePos;
+ while (nSpaceStt && IsSpace(rStr[sal_Int32(--nSpaceStt)]))
+ ;
+ ++nSpaceStt;
+
+ if (bInsTab && '\t' == rStr[sal_Int32(nSpaceStt)]) // leave tab alone
+ {
+ ++nSpaceStt;
+ bInsTab = false;
+ }
+
+ m_aDelPam.DeleteMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nSpacePos);
+
+ // delete old Spaces, etc.
+ if( nSpaceStt < nSpacePos )
+ {
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetMark() = m_pCurTextFrame->MapViewToModelPos(nSpaceStt);
+ DeleteSel( m_aDelPam );
+ if( bInsTab )
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString('\t') );
+ }
+ }
+ }
+
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ SwTextFrameInfo aFInfo( m_pCurTextFrame );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame) &&
+ 20 < std::abs( static_cast<tools::Long>(nSpaces - aFInfo.SetFrame(
+ EnsureFormatted(*pNextFrame)).GetLineStart()) )
+ )
+ {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ pNextFrame = GetNextNode();
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildHeadLine( sal_uInt16 nLvl )
+{
+ if( m_aFlags.bWithRedlining )
+ {
+ OUString sText(SwViewShell::GetShellRes()->GetAutoFormatNameLst()[
+ STR_AUTOFMTREDL_SET_TMPL_HEADLINE ] );
+ sText = sText.replaceAll( "$(ARG1)", OUString::number( nLvl + 1 ) );
+ m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment( &sText );
+ }
+
+ SetColl( o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + nLvl ), true );
+ if( m_aFlags.bAFormatByInput )
+ {
+ SwTextFormatColl& rNxtColl = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetNextTextFormatColl();
+
+ JoinPrevPara();
+
+ DeleteLeadingTrailingBlanks( true, false );
+ const SwTextFrame* pNextFrame = GetNextNode(false);
+ if (pNextFrame->GetNext())
+ {
+ (void)DeleteJoinCurNextPara(pNextFrame, true);
+ pNextFrame = GetNextNode(false);
+ }
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *pNextFrame->GetTextNodeForParaProps();
+ m_aDelPam.GetPoint()->nContent.Assign( m_aDelPam.GetContentNode(), 0 );
+ m_pDoc->SetTextFormatColl( m_aDelPam, &rNxtColl );
+ }
+ else
+ {
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+ }
+}
+
+/// Start autocorrection for the current TextNode
+void SwAutoFormat::AutoCorrect(TextFrameIndex nPos)
+{
+ SvxAutoCorrect* pATst = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ ACFlags aSvxFlags = pATst->GetFlags( );
+ bool bReplaceQuote( aSvxFlags & ACFlags::ChgQuotes );
+ bool bReplaceSglQuote( aSvxFlags & ACFlags::ChgSglQuotes );
+
+ if( m_aFlags.bAFormatByInput ||
+ (!m_aFlags.bAutoCorrect && !bReplaceQuote && !bReplaceSglQuote &&
+ !m_aFlags.bCapitalStartSentence && !m_aFlags.bCapitalStartWord &&
+ !m_aFlags.bChgOrdinalNumber && !m_aFlags.bTransliterateRTL &&
+ !m_aFlags.bChgToEnEmDash && !m_aFlags.bSetINetAttr &&
+ !m_aFlags.bChgWeightUnderl && !m_aFlags.bAddNonBrkSpace) )
+ return;
+
+ const OUString* pText = &m_pCurTextFrame->GetText();
+ if (TextFrameIndex(pText->getLength()) <= nPos)
+ return;
+
+ bool bGetLanguage = m_aFlags.bChgOrdinalNumber || m_aFlags.bTransliterateRTL ||
+ m_aFlags.bChgToEnEmDash || m_aFlags.bSetINetAttr ||
+ m_aFlags.bCapitalStartWord || m_aFlags.bCapitalStartSentence ||
+ m_aFlags.bAddNonBrkSpace;
+
+ m_aDelPam.DeleteMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+
+ SwAutoCorrDoc aACorrDoc( *m_pEditShell, m_aDelPam );
+
+ SwTextFrameInfo aFInfo( nullptr );
+
+ TextFrameIndex nSttPos, nLastBlank = nPos;
+ bool bFirst = m_aFlags.bCapitalStartSentence, bFirstSent = bFirst;
+ sal_Unicode cChar = 0;
+ bool bNbspRunNext = false;
+
+ CharClass& rAppCC = GetAppCharClass();
+
+ do {
+ while (nPos < TextFrameIndex(pText->getLength())
+ && IsSpace(cChar = (*pText)[sal_Int32(nPos)]))
+ ++nPos;
+ if (nPos == TextFrameIndex(pText->getLength()))
+ break; // that's it
+
+ if( ( ( bReplaceQuote && '\"' == cChar ) ||
+ ( bReplaceSglQuote && '\'' == cChar ) ) &&
+ (!nPos || ' ' == (*pText)[sal_Int32(nPos)-1]))
+ {
+
+ // note: special case symbol fonts !!!
+ if( !aFInfo.GetFrame() )
+ aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
+ if( !aFInfo.IsBullet( nPos ))
+ {
+ SetRedlineText( STR_AUTOFMTREDL_TYPO );
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ bool bSetHardBlank = false;
+
+ OUString sReplace( pATst->GetQuote( aACorrDoc,
+ sal_Int32(nPos), cChar, true ));
+
+ m_aDelPam.SetMark();
+ m_aDelPam.GetPoint()->nContent = m_aDelPam.GetMark()->nContent.GetIndex() + 1;
+ if( 2 == sReplace.getLength() && ' ' == sReplace[ 1 ])
+ {
+ sReplace = sReplace.copy( 0, 1 );
+ bSetHardBlank = true;
+ }
+ m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ aFInfo.SetFrame( nullptr );
+ }
+
+ nPos += TextFrameIndex(sReplace.getLength() - 1);
+ m_aDelPam.DeleteMark();
+ if( bSetHardBlank )
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(CHAR_HARDBLANK) );
+ ++nPos;
+ }
+ }
+ }
+
+ bool bCallACorr = false;
+ int bBreak = 0;
+ if (nPos && IsSpace((*pText)[sal_Int32(nPos) - 1]))
+ nLastBlank = nPos;
+ for (nSttPos = nPos; !bBreak && nPos < TextFrameIndex(pText->getLength()); ++nPos)
+ {
+ cChar = (*pText)[sal_Int32(nPos)];
+ switch (cChar)
+ {
+ case '\"':
+ case '\'':
+ if( ( cChar == '\"' && bReplaceQuote ) || ( cChar == '\'' && bReplaceSglQuote ) )
+ {
+ // consider Symbolfonts!
+ if( !aFInfo.GetFrame() )
+ aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
+ if( !aFInfo.IsBullet( nPos ))
+ {
+ SetRedlineText( STR_AUTOFMTREDL_TYPO );
+ bool bSetHardBlank = false;
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ OUString sReplace( pATst->GetQuote( aACorrDoc,
+ sal_Int32(nPos), cChar, false) );
+
+ if( 2 == sReplace.getLength() && ' ' == sReplace[ 0 ])
+ {
+ sReplace = sReplace.copy( 1 );
+ bSetHardBlank = true;
+ }
+
+ m_aDelPam.SetMark();
+ m_aDelPam.GetPoint()->nContent = m_aDelPam.GetMark()->nContent.GetIndex() + 1;
+ m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ aFInfo.SetFrame( nullptr );
+ }
+
+ nPos += TextFrameIndex(sReplace.getLength() - 1);
+ m_aDelPam.DeleteMark();
+
+ if( bSetHardBlank )
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(CHAR_HARDBLANK) );
+ ++nPos;
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ }
+ }
+ }
+ break;
+ case '*':
+ case '_':
+ if( m_aFlags.bChgWeightUnderl )
+ {
+ // consider Symbolfonts!
+ if( !aFInfo.GetFrame() )
+ aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
+ if( !aFInfo.IsBullet( nPos ))
+ {
+ SetRedlineText( '*' == cChar
+ ? STR_AUTOFMTREDL_BOLD
+ : STR_AUTOFMTREDL_UNDER );
+
+ sal_Unicode cBlank = nSttPos ? (*pText)[sal_Int32(nSttPos) - 1] : 0;
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+
+ if (pATst->FnChgWeightUnderl(aACorrDoc, *pText, sal_Int32(nPos)))
+ {
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ aFInfo.SetFrame( nullptr );
+ }
+ //#125102# in case of the mode RedlineFlags::ShowDelete the ** are still contained in pText
+ if(!(m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() & RedlineFlags::ShowDelete))
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint()) - TextFrameIndex(1);
+ // Was a character deleted before starting?
+ if (cBlank && cBlank != (*pText)[sal_Int32(nSttPos) - 1])
+ --nSttPos;
+ }
+ }
+ }
+ break;
+ case '/':
+ if ( m_aFlags.bAddNonBrkSpace )
+ {
+ LanguageType eLang = bGetLanguage
+ ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true)
+ : LANGUAGE_SYSTEM;
+
+ SetRedlineText( STR_AUTOFMTREDL_NON_BREAK_SPACE );
+ if (pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext))
+ --nPos;
+ }
+ break;
+
+ case '.':
+ case '!':
+ case '?':
+ if( m_aFlags.bCapitalStartSentence )
+ bFirstSent = true;
+ [[fallthrough]];
+ default:
+ if (!(rAppCC.isLetterNumeric(*pText, sal_Int32(nPos))
+ || '/' == cChar )) // '/' should not be a word separator (e.g. '1/2' needs to be handled as one word for replacement)
+ {
+ --nPos; // revert ++nPos which was decremented in for loop
+ ++bBreak;
+ }
+ break;
+ }
+ }
+
+ if( nPos == nSttPos )
+ {
+ if (++nPos == TextFrameIndex(pText->getLength()))
+ bCallACorr = true;
+ }
+ else
+ bCallACorr = true;
+
+ if( bCallACorr )
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ SetRedlineText( STR_AUTOFMTREDL_USE_REPLACE );
+
+ LanguageType eLang = bGetLanguage
+ ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true)
+ : LANGUAGE_SYSTEM;
+
+ if( m_bIsRightToLeft && m_aFlags.bTransliterateRTL && eLang == LANGUAGE_HUNGARIAN &&
+ SetRedlineText( STR_AUTOFMTREDL_TRANSLITERATE_RTL ) &&
+ aACorrDoc.TransliterateRTLWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos), /*bApply=*/true))
+ {
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ }
+
+ continue; // do not check further
+ }
+
+ if( m_aFlags.bAutoCorrect &&
+ aACorrDoc.ChgAutoCorrWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos), *pATst, nullptr) )
+ {
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ }
+
+ continue; // do not check further
+ }
+
+ if ( m_aFlags.bAddNonBrkSpace && nPos < TextFrameIndex(pText->getLength()) )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_NON_BREAK_SPACE );
+ pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext);
+ }
+
+ if( ( m_aFlags.bChgOrdinalNumber &&
+ SetRedlineText( STR_AUTOFMTREDL_ORDINAL ) &&
+ pATst->FnChgOrdinalNumber(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang)) ||
+ ( m_aFlags.bChgToEnEmDash &&
+ SetRedlineText( STR_AUTOFMTREDL_DASH ) &&
+ pATst->FnChgToEnEmDash(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang)) ||
+ ( m_aFlags.bSetINetAttr &&
+ (nPos == TextFrameIndex(pText->getLength()) || IsSpace((*pText)[sal_Int32(nPos)])) &&
+ SetRedlineText( STR_AUTOFMTREDL_DETECT_URL ) &&
+ pATst->FnSetINetAttr(aACorrDoc, *pText, sal_Int32(nLastBlank), sal_Int32(nPos), eLang)))
+ {
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
+ }
+ else
+ {
+ // two capital letters at the beginning of a word?
+ if( m_aFlags.bCapitalStartWord )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_CPTL_STT_WORD );
+ pATst->FnCapitalStartWord(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang);
+ }
+ // capital letter at the beginning of a sentence?
+ if( m_aFlags.bCapitalStartSentence && bFirst )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_CPTL_STT_SENT );
+ pATst->FnCapitalStartSentence(aACorrDoc, *pText, true, sal_Int32(nSttPos), sal_Int32(nPos), eLang);
+ }
+
+ bFirst = bFirstSent;
+ bFirstSent = false;
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ }
+ }
+ }
+ }
+ while (nPos < TextFrameIndex(pText->getLength()));
+ ClearRedlineText();
+}
+
+SwAutoFormat::SwAutoFormat( SwEditShell* pEdShell, SvxSwAutoFormatFlags const & rFlags,
+ SwNodeIndex const * pSttNd, SwNodeIndex const * pEndNd )
+ : m_aFlags( rFlags ),
+ m_aDelPam( pEdShell->GetDoc()->GetNodes().GetEndOfExtras() ),
+ m_aNdIdx( pEdShell->GetDoc()->GetNodes().GetEndOfExtras(), SwNodeOffset(+1) ),
+ m_aEndNdIdx( pEdShell->GetDoc()->GetNodes().GetEndOfContent() ),
+ m_pEditShell( pEdShell ),
+ m_pDoc( pEdShell->GetDoc() ),
+ m_pCurTextNd( nullptr ), m_pCurTextFrame( nullptr ),
+ m_nRedlAutoFormatSeqId( 0 )
+{
+ OSL_ENSURE( (pSttNd && pEndNd) || (!pSttNd && !pEndNd),
+ "Got no area" );
+
+ if( m_aFlags.bSetNumRule && !m_aFlags.bAFormatByInput )
+ m_aFlags.bSetNumRule = false;
+
+ bool bReplaceStyles = !m_aFlags.bAFormatByInput || m_aFlags.bReplaceStyles;
+
+ const SwTextFrame * pNextFrame = nullptr;
+ bool bNxtEmpty = false;
+ bool bNxtAlpha = false;
+ sal_uInt16 nNxtLevel = 0;
+ bool bEmptyLine;
+
+ // set area for autoformatting
+ if( pSttNd )
+ {
+ m_aNdIdx = *pSttNd;
+ // for GoNextPara, one paragraph prior to that
+ sw::GotoPrevLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
+ m_aEndNdIdx = *pEndNd;
+ sw::GotoNextLayoutTextFrame(m_aEndNdIdx, m_pEditShell->GetLayout());
+
+ // check the previous TextNode
+ SwTextFrame const*const pPrevFrame = m_aNdIdx.GetNode().GetTextNode()
+ ? static_cast<SwTextFrame const*>(m_aNdIdx.GetNode().GetTextNode()->getLayoutFrame(m_pEditShell->GetLayout()))
+ : nullptr;
+ bEmptyLine = !pPrevFrame
+ || IsEmptyLine(*pPrevFrame)
+ || IsNoAlphaLine(*pPrevFrame);
+ }
+ else
+ bEmptyLine = true; // at document beginning
+
+ m_bEnd = false;
+
+ // set value for percentage display
+ m_nEndNdIdx = m_aEndNdIdx.GetIndex();
+
+ if( !m_aFlags.bAFormatByInput )
+ {
+ m_nEndNdIdx = m_aEndNdIdx.GetIndex();
+ ::StartProgress( STR_STATSTR_AUTOFORMAT, sal_Int32(m_aNdIdx.GetIndex()),
+ sal_Int32(m_nEndNdIdx),
+ m_pDoc->GetDocShell() );
+ }
+
+ RedlineFlags eRedlMode = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode;
+ if( m_aFlags.bWithRedlining )
+ {
+ m_pDoc->SetAutoFormatRedline( true );
+ eRedlMode = RedlineFlags::On | (eOldMode & RedlineFlags::ShowMask);
+ }
+ else
+ eRedlMode = RedlineFlags::Ignore | (eOldMode & RedlineFlags::ShowMask);
+ m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode );
+
+ // save undo state (might be turned off)
+ bool const bUndoState = m_pDoc->GetIDocumentUndoRedo().DoesUndo();
+
+ // If multiple lines, then do not merge with next paragraph
+ m_bMoreLines = false;
+
+ sal_uInt16 nLastCalcHeadLvl = 0;
+ sal_uInt16 nLastHeadLvl = USHRT_MAX;
+ sal_uInt16 nLevel = 0;
+ sal_uInt16 nDigitLvl = 0;
+
+ // set defaults
+ SwTextFrameInfo aFInfo( nullptr );
+
+ enum Format_Status
+ {
+ READ_NEXT_PARA, // -> ISEND, TST_EMPTY_LINE
+ TST_EMPTY_LINE, // -> READ_NEXT_PARA, TST_ALPHA_LINE
+ TST_ALPHA_LINE, // -> READ_NEXT_PARA, GET_ALL_INFO, IS_END
+ GET_ALL_INFO, // -> READ_NEXT_PARA, IS_ONE_LINE, TST_ENUMERIC, HAS_FMTCOLL
+ IS_ONE_LINE, // -> READ_NEXT_PARA, TST_ENUMERIC
+ TST_ENUMERIC, // -> READ_NEXT_PARA, TST_IDENT, TST_NEG_IDENT
+ TST_IDENT, // -> READ_NEXT_PARA, TST_TXT_BODY
+ TST_NEG_IDENT, // -> READ_NEXT_PARA, TST_TXT_BODY
+ TST_TXT_BODY, // -> READ_NEXT_PARA
+ HAS_FMTCOLL, // -> READ_NEXT_PARA
+ IS_END
+ } eStat;
+
+ // This is the automat for autoformatting
+ eStat = READ_NEXT_PARA;
+ while( !m_bEnd )
+ {
+ switch( eStat )
+ {
+ case READ_NEXT_PARA:
+ {
+ GoNextPara();
+ eStat = m_bEnd ? IS_END : TST_EMPTY_LINE;
+ }
+ break;
+
+ case TST_EMPTY_LINE:
+ if (IsEmptyLine(*m_pCurTextFrame))
+ {
+ if (m_aFlags.bDelEmptyNode && !HasObjects(*m_pCurTextFrame))
+ {
+ bEmptyLine = true;
+ SwNodeOffset nOldCnt = m_pDoc->GetNodes().Count();
+ DelEmptyLine();
+ // Was there really a deletion of a node?
+ if( nOldCnt != m_pDoc->GetNodes().Count() )
+ {
+ // do not skip the next paragraph
+ sw::GotoPrevLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
+ }
+ }
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = TST_ALPHA_LINE;
+ break;
+
+ case TST_ALPHA_LINE:
+ if (IsNoAlphaLine(*m_pCurTextFrame))
+ {
+ // recognize a table definition +---+---+
+ if( m_aFlags.bAFormatByInput && m_aFlags.bCreateTable && DoTable() )
+ {
+ //JP 30.09.96: DoTable() builds on PopCursor and MoveCursor after AutoFormat!
+ pEdShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ static_cast<SwPaM&>(*pEdShell->GetCursor()) = m_aDelPam;
+ pEdShell->Push();
+
+ eStat = IS_END;
+ break;
+ }
+
+ // Check for 3 "---" or "===". In this case, the previous paragraph should be
+ // underlined and the current be deleted!
+ if( !DoUnderline() && bReplaceStyles )
+ {
+ SetColl( RES_POOLCOLL_STANDARD, true );
+ bEmptyLine = true;
+ }
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = GET_ALL_INFO;
+ break;
+
+ case GET_ALL_INFO:
+ {
+ if (m_pCurTextFrame->GetTextNodeForParaProps()->GetNumRule())
+ {
+ // do nothing in numbering, go to next
+ bEmptyLine = false;
+ eStat = READ_NEXT_PARA;
+ // delete all blanks at beginning/end and in between
+ //JP 29.04.98: first only "all in between"
+ DelMoreLinesBlanks();
+ break;
+ }
+
+ aFInfo.SetFrame( m_pCurTextFrame );
+
+ // so far: if there were templates assigned, keep these and go to next node
+ sal_uInt16 nPoolId = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetPoolFormatId();
+ if( IsPoolUserFormat( nPoolId )
+ ? !m_aFlags.bChgUserColl
+ : ( RES_POOLCOLL_STANDARD != nPoolId &&
+ ( !m_aFlags.bAFormatByInput ||
+ (RES_POOLCOLL_TEXT_MOVE != nPoolId &&
+ RES_POOLCOLL_TEXT != nPoolId )) ))
+ {
+ eStat = HAS_FMTCOLL;
+ break;
+ }
+
+ // check for hard spaces or LRSpaces set by the template
+ if( IsPoolUserFormat( nPoolId ) ||
+ RES_POOLCOLL_STANDARD == nPoolId )
+ {
+ short nSz;
+ SvxLRSpaceItem const * pLRSpace = m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().
+ GetItemIfSet( RES_LR_SPACE );
+ if (pLRSpace &&
+ ( 0 != (nSz = pLRSpace->GetTextFirstLineOffset()) ||
+ 0 != pLRSpace->GetTextLeft() ) )
+ {
+ // exception: numbering/enumeration can have an indentation
+ if (IsEnumericChar(*m_pCurTextFrame))
+ {
+ nLevel = CalcLevel(*m_pCurTextFrame, &nDigitLvl);
+ if( nLevel >= MAXLEVEL )
+ nLevel = MAXLEVEL-1;
+ BuildEnum( nLevel, nDigitLvl );
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+
+ // never merge (maybe only indent as exception)
+ m_bMoreLines = true;
+
+ if( bReplaceStyles )
+ {
+ // then use one of our templates
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else if( pLRSpace->GetTextLeft() ) // is indentation
+ BuildTextIndent();
+ }
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+ }
+
+ nLevel = CalcLevel( *m_pCurTextFrame, &nDigitLvl );
+ m_bMoreLines = !IsOneLine(*m_pCurTextFrame);
+ // note: every use of pNextFrame in following states, until the
+ // next READ_NEXT_PARA, relies on this update
+ pNextFrame = GetNextNode();
+ if (pNextFrame)
+ {
+ bNxtEmpty = IsEmptyLine(*pNextFrame);
+ bNxtAlpha = IsNoAlphaLine(*pNextFrame);
+ nNxtLevel = CalcLevel(*pNextFrame);
+
+ if (!bEmptyLine && HasBreakAttr(*m_pCurTextFrame))
+ bEmptyLine = true;
+ if (!bNxtEmpty && HasBreakAttr(*pNextFrame))
+ bNxtEmpty = true;
+
+ }
+ else
+ {
+ bNxtEmpty = false;
+ bNxtAlpha = false;
+ nNxtLevel = 0;
+ }
+ eStat = !m_bMoreLines ? IS_ONE_LINE : TST_ENUMERIC;
+ }
+ break;
+
+ case IS_ONE_LINE:
+ {
+ eStat = TST_ENUMERIC;
+ if( !bReplaceStyles )
+ break;
+
+ const OUString sClrStr( DelLeadingBlanks(m_pCurTextFrame->GetText()) );
+
+ if( sClrStr.isEmpty() )
+ {
+ bEmptyLine = true;
+ eStat = READ_NEXT_PARA;
+ break; // read next paragraph
+ }
+
+ // check if headline
+ if (!bEmptyLine || !IsFirstCharCapital(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame))
+ break;
+
+ bEmptyLine = false;
+ const OUString sEndClrStr( DelTrailingBlanks(sClrStr) );
+ const sal_Unicode cLast = sEndClrStr[sEndClrStr.getLength() - 1];
+
+ // not, then check if headline
+ if( ':' == cLast )
+ {
+ BuildHeadLine( 2 );
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+ else if( 256 <= cLast || !strchr( ",.;", cLast ) )
+ {
+ if( bNxtEmpty || bNxtAlpha
+ || (pNextFrame && IsEnumericChar(*pNextFrame)))
+ {
+
+ // one level below?
+ if( nLevel >= MAXLEVEL )
+ nLevel = MAXLEVEL-1;
+
+ if( USHRT_MAX == nLastHeadLvl )
+ nLastHeadLvl = 0;
+ else if( nLastCalcHeadLvl < nLevel )
+ {
+ if( nLastHeadLvl+1 < MAXLEVEL )
+ ++nLastHeadLvl;
+ }
+ // one level above?
+ else if( nLastCalcHeadLvl > nLevel )
+ {
+ if( nLastHeadLvl )
+ --nLastHeadLvl;
+ }
+ nLastCalcHeadLvl = nLevel;
+
+ if( m_aFlags.bAFormatByInput )
+ BuildHeadLine( nLevel );
+ else
+ BuildHeadLine( nLastHeadLvl );
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+ }
+ }
+ break;
+
+ case TST_ENUMERIC:
+ {
+ bEmptyLine = false;
+ if (IsEnumericChar(*m_pCurTextFrame))
+ {
+ if( nLevel >= MAXLEVEL )
+ nLevel = MAXLEVEL-1;
+ BuildEnum( nLevel, nDigitLvl );
+ eStat = READ_NEXT_PARA;
+ }
+ else if( bReplaceStyles )
+ eStat = nLevel ? TST_IDENT : TST_NEG_IDENT;
+ else
+ eStat = READ_NEXT_PARA;
+ }
+ break;
+
+ case TST_IDENT:
+ // Spaces at the beginning, check again for indentation
+ if( m_bMoreLines && nLevel )
+ {
+ SwTwips nSz = aFInfo.GetFirstIndent();
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else // is indentation
+ BuildTextIndent();
+ eStat = READ_NEXT_PARA;
+ }
+ else if (nLevel && pNextFrame &&
+ !bNxtEmpty && !bNxtAlpha && !nNxtLevel &&
+ !IsEnumericChar(*pNextFrame))
+ {
+ // is an indentation
+ BuildIndent();
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = TST_TXT_BODY;
+ break;
+
+ case TST_NEG_IDENT:
+ // no spaces at the beginning, check again for negative indentation
+ {
+ if( m_bMoreLines && !nLevel )
+ {
+ SwTwips nSz = aFInfo.GetFirstIndent();
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else // is _no_ indentation
+ BuildText();
+ eStat = READ_NEXT_PARA;
+ }
+ else if (!nLevel && pNextFrame &&
+ !bNxtEmpty && !bNxtAlpha && nNxtLevel &&
+ !IsEnumericChar(*pNextFrame))
+ {
+ // is a negative indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = TST_TXT_BODY;
+ }
+ break;
+
+ case TST_TXT_BODY:
+ {
+ if( m_bMoreLines )
+ {
+ SwTwips nSz = aFInfo.GetFirstIndent();
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else if( nLevel ) // is indentation
+ BuildTextIndent();
+ else
+ BuildText();
+ }
+ else if( nLevel )
+ BuildTextIndent();
+ else
+ BuildText();
+ eStat = READ_NEXT_PARA;
+ }
+ break;
+
+ case HAS_FMTCOLL:
+ {
+ // so far: if there were templates assigned, keep these and go to next node
+ bEmptyLine = false;
+ eStat = READ_NEXT_PARA;
+ // delete all blanks at beginning/end and in between
+ //JP 29.04.98: first only "all in between"
+ DelMoreLinesBlanks();
+
+ // handle hard attributes
+ if (m_pCurTextFrame->GetTextNodeForParaProps()->HasSwAttrSet())
+ {
+ short nSz;
+ SvxLRSpaceItem const * pLRSpace;
+ if( bReplaceStyles &&
+ (pLRSpace = m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().
+ GetItemIfSet( RES_LR_SPACE, false )) &&
+ ( 0 != (nSz = pLRSpace->GetTextFirstLineOffset()) ||
+ 0 != pLRSpace->GetTextLeft() ) )
+ {
+ // then use one of our templates
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ {
+ BuildNegIndent( aFInfo.GetLineStart() );
+ }
+ else if( pLRSpace->GetTextLeft() ) // is indentation
+ BuildTextIndent();
+ else
+ BuildText();
+ }
+ }
+ }
+ break;
+
+ case IS_END:
+ m_bEnd = true;
+ break;
+ }
+ }
+
+ if( m_aFlags.bWithRedlining )
+ m_pDoc->SetAutoFormatRedline( false );
+ m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode );
+
+ // restore undo (in case it has been changed)
+ m_pDoc->GetIDocumentUndoRedo().DoUndo(bUndoState);
+
+ // disable display of percentage again
+ if( !m_aFlags.bAFormatByInput )
+ ::EndProgress( m_pDoc->GetDocShell() );
+}
+
+void SwEditShell::AutoFormat( const SvxSwAutoFormatFlags* pAFlags )
+{
+ std::unique_ptr<SwWait> pWait;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+ StartUndo( SwUndoId::AUTOFORMAT );
+
+ SvxSwAutoFormatFlags aAFFlags; // use default values or add params?
+ if( pAFlags )
+ {
+ aAFFlags = *pAFlags;
+ if( !aAFFlags.bAFormatByInput )
+ pWait.reset(new SwWait( *GetDoc()->GetDocShell(), true ));
+ }
+
+ SwPaM* pCursor = GetCursor();
+ // There are more than one or a selection is open
+ if( pCursor->GetNext() != pCursor || pCursor->HasMark() )
+ {
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() )
+ {
+ SwAutoFormat aFormat( this, aAFFlags, &(rPaM.Start()->nNode),
+ &(rPaM.End()->nNode) );
+ }
+ }
+ }
+ else
+ {
+ SwAutoFormat aFormat( this, aAFFlags );
+ }
+
+ EndUndo( SwUndoId::AUTOFORMAT );
+ EndAllAction();
+}
+
+void SwEditShell::AutoFormatBySplitNode()
+{
+ CurrShell aCurr( this );
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->IsMultiSelection() || !pCursor->Move( fnMoveBackward, GoInNode ) )
+ return;
+
+ StartAllAction();
+ StartUndo( SwUndoId::AUTOFORMAT );
+
+ bool bRange = false;
+ pCursor->SetMark();
+ SwIndex* pContent = &pCursor->GetMark()->nContent;
+ if( pContent->GetIndex() )
+ {
+ *pContent = 0;
+ bRange = true;
+ }
+ else
+ {
+ // then go one node backwards
+ SwNodeIndex aNdIdx(pCursor->GetMark()->nNode);
+ sw::GotoPrevLayoutTextFrame(aNdIdx, GetLayout());
+ SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
+ if (pTextNd && !pTextNd->GetText().isEmpty())
+ {
+ pContent->Assign( pTextNd, 0 );
+ pCursor->GetMark()->nNode = aNdIdx;
+ bRange = true;
+ }
+ }
+
+ if( bRange )
+ {
+ Push(); // save cursor
+
+ SvxSwAutoFormatFlags aAFFlags = *GetAutoFormatFlags(); // use default values so far
+
+ SwAutoFormat aFormat( this, aAFFlags, &pCursor->GetMark()->nNode,
+ &pCursor->GetPoint()->nNode );
+ SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ if( pACorr && !pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord |
+ ACFlags::AddNonBrkSpace | ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL |
+ ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | ACFlags::Autocorrect ))
+ pACorr = nullptr;
+
+ if( pACorr )
+ AutoCorrect( *pACorr,false, u'\0' );
+
+ //JP 30.09.96: DoTable() builds on PopCursor and MoveCursor!
+ Pop(PopMode::DeleteCurrent);
+ pCursor = GetCursor();
+ }
+ pCursor->DeleteMark();
+ pCursor->Move( fnMoveForward, GoInNode );
+
+ EndUndo( SwUndoId::AUTOFORMAT );
+ EndAllAction();
+
+}
+
+SvxSwAutoFormatFlags* SwEditShell::GetAutoFormatFlags()
+{
+ if (!s_pAutoFormatFlags)
+ s_pAutoFormatFlags = new SvxSwAutoFormatFlags;
+
+ return s_pAutoFormatFlags;
+}
+
+void SwEditShell::SetAutoFormatFlags(SvxSwAutoFormatFlags const * pFlags)
+{
+ SvxSwAutoFormatFlags* pEditFlags = GetAutoFormatFlags();
+
+ pEditFlags->bSetNumRule = pFlags->bSetNumRule;
+ pEditFlags->bChgEnumNum = pFlags->bChgEnumNum;
+ pEditFlags->bSetBorder = pFlags->bSetBorder;
+ pEditFlags->bCreateTable = pFlags->bCreateTable;
+ pEditFlags->bReplaceStyles = pFlags->bReplaceStyles;
+ pEditFlags->bAFormatByInpDelSpacesAtSttEnd =
+ pFlags->bAFormatByInpDelSpacesAtSttEnd;
+ pEditFlags->bAFormatByInpDelSpacesBetweenLines =
+ pFlags->bAFormatByInpDelSpacesBetweenLines;
+
+ //JP 15.12.98: copy BulletChar and Font into "normal" ones
+ // because AutoFormat can only work with the latter!
+ pEditFlags->cBullet = pFlags->cByInputBullet;
+ pEditFlags->aBulletFont = pFlags->aByInputBulletFont;
+ pEditFlags->cByInputBullet = pFlags->cByInputBullet;
+ pEditFlags->aByInputBulletFont = pFlags->aByInputBulletFont;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edatmisc.cxx b/sw/source/core/edit/edatmisc.cxx
new file mode 100644
index 000000000..12abec250
--- /dev/null
+++ b/sw/source/core/edit/edatmisc.cxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <ndtxt.hxx>
+
+/*
+ * hard formatting (Attribute)
+ */
+
+void SwEditShell::ResetAttr( const o3tl::sorted_vector<sal_uInt16> &attrs, SwPaM* pPaM )
+{
+ CurrShell aCurr( this );
+ SwPaM* pCursor = pPaM ? pPaM : GetCursor( );
+
+ StartAllAction();
+ bool bUndoGroup = pCursor->GetNext() != pCursor;
+ if( bUndoGroup )
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::RESETATTR, nullptr);
+ }
+
+ for(const SwPaM& rCurrentCursor : pCursor->GetRingContainer())
+ GetDoc()->ResetAttrs(rCurrentCursor, true, attrs, true, GetLayout());
+
+ if( bUndoGroup )
+ {
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::RESETATTR, nullptr);
+ }
+ CallChgLnk();
+ EndAllAction();
+}
+
+void SwEditShell::GCAttr()
+{
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if ( !rPaM.HasMark() )
+ {
+ SwTextNode *const pTextNode =
+ rPaM.GetPoint()->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ pTextNode->GCAttr();
+ }
+ }
+ else
+ {
+ const SwNodeIndex& rEnd = rPaM.End()->nNode;
+ SwNodeIndex aIdx( rPaM.Start()->nNode );
+ SwNode* pNd = &aIdx.GetNode();
+ do {
+ if( pNd->IsTextNode() )
+ static_cast<SwTextNode*>(pNd)->GCAttr();
+ }
+ while( nullptr != ( pNd = GetDoc()->GetNodes().GoNext( &aIdx )) &&
+ aIdx <= rEnd );
+ }
+ }
+}
+
+/// Set the attribute as new default attribute in the document.
+void SwEditShell::SetDefault( const SfxPoolItem& rFormatHint )
+{
+ // 7502: Action-Parenthesis
+ StartAllAction();
+ GetDoc()->SetDefault( rFormatHint );
+ EndAllAction();
+}
+
+/// request the default attribute in this document.
+const SfxPoolItem& SwEditShell::GetDefault( sal_uInt16 nFormatHint ) const
+{
+ return GetDoc()->GetDefault( nFormatHint );
+}
+
+void SwEditShell::SetAttrItem( const SfxPoolItem& rHint, SetAttrMode nFlags, const bool /*bParagraphSetting*/ )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor ) // Ring of Cursors
+ {
+ bool bIsTableMode = IsTableMode();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSATTR, nullptr);
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() && ( bIsTableMode ||
+ *rPaM.GetPoint() != *rPaM.GetMark() ))
+ {
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem(rPaM, rHint, nFlags, GetLayout());
+ }
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr);
+ }
+ else
+ {
+ if( !HasSelection() )
+ UpdateAttr();
+
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem(*pCursor, rHint, nFlags, GetLayout());
+ }
+ EndAllAction();
+}
+
+void SwEditShell::SetAttrSet( const SfxItemSet& rSet, SetAttrMode nFlags, SwPaM* pPaM, const bool /*bParagraphSetting*/ )
+{
+ CurrShell aCurr( this );
+ SwPaM* pCursor = pPaM ? pPaM : GetCursor();
+ StartAllAction();
+ if( pCursor->GetNext() != pCursor ) // Ring of Cursors
+ {
+ bool bIsTableMode = IsTableMode();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSATTR, nullptr);
+
+ for(SwPaM& rTmpCursor : pCursor->GetRingContainer())
+ {
+ if( rTmpCursor.HasMark() && ( bIsTableMode ||
+ *rTmpCursor.GetPoint() != *rTmpCursor.GetMark() ))
+ {
+ GetDoc()->getIDocumentContentOperations().InsertItemSet(rTmpCursor, rSet, nFlags, GetLayout());
+ }
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr);
+ }
+ else
+ {
+ if( !HasSelection() )
+ UpdateAttr();
+
+ GetDoc()->getIDocumentContentOperations().InsertItemSet(*pCursor, rSet, nFlags, GetLayout());
+ }
+ EndAllAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edattr.cxx b/sw/source/core/edit/edattr.cxx
new file mode 100644
index 000000000..956110c00
--- /dev/null
+++ b/sw/source/core/edit/edattr.cxx
@@ -0,0 +1,847 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <osl/diagnose.h>
+#include <txatbase.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <editsh.hxx>
+#include <edimp.hxx>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <ndtxt.hxx>
+#include <ftnidx.hxx>
+#include <expfld.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <breakit.hxx>
+#include <fmtfld.hxx>
+#include <txtfrm.hxx>
+#include <scriptinfo.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/languageoptions.hxx>
+#include <charfmt.hxx>
+#include <numrule.hxx>
+
+/*
+ * hard Formatting (Attributes)
+ */
+
+// if selection is bigger as max nodes or more than max selections
+// => no attributes
+static sal_uInt16 getMaxLookup()
+{
+ return 10000;
+}
+
+bool SwEditShell::GetPaMAttr( SwPaM* pPaM, SfxItemSet& rSet,
+ const bool bMergeIndentValuesOfNumRule ) const
+{
+ // ??? pPaM can be different from the Cursor ???
+ if( GetCursorCnt() > getMaxLookup() )
+ {
+ rSet.InvalidateAllItems();
+ return false;
+ }
+
+ SfxItemSet aSet( *rSet.GetPool(), rSet.GetRanges() );
+ SfxItemSet *pSet = &rSet;
+
+ for(SwPaM& rCurrentPaM : pPaM->GetRingContainer())
+ {
+ // #i27615# if the cursor is in front of the numbering label
+ // the attributes to get are those from the numbering format.
+ if (rCurrentPaM.IsInFrontOfLabel())
+ {
+ SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(),
+ rCurrentPaM.GetPoint()->nNode);
+
+ if (pTextNd)
+ {
+ SwNumRule * pNumRule = pTextNd->GetNumRule();
+
+ if (pNumRule)
+ {
+ int nListLevel = pTextNd->GetActualListLevel();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ const OUString & aCharFormatName =
+ pNumRule->Get(o3tl::narrowing<sal_uInt16>(nListLevel)).GetCharFormatName();
+ SwCharFormat * pCharFormat =
+ GetDoc()->FindCharFormatByName(aCharFormatName);
+
+ if (pCharFormat)
+ rSet.Put(pCharFormat->GetAttrSet());
+ }
+ }
+
+ continue;
+ }
+
+ SwNodeOffset nSttNd = rCurrentPaM.Start()->nNode.GetIndex(),
+ nEndNd = rCurrentPaM.End()->nNode.GetIndex();
+ sal_Int32 nSttCnt = rCurrentPaM.Start()->nContent.GetIndex();
+ sal_Int32 nEndCnt = rCurrentPaM.End()->nContent.GetIndex();
+
+ if( sal_Int32(nEndNd - nSttNd) >= getMaxLookup() )
+ {
+ rSet.ClearItem();
+ rSet.InvalidateAllItems();
+ return false;
+ }
+
+ // at first node the node enter his values into the GetSet (Initial)
+ // all additional nodes are additional merged to GetSet
+ for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
+ {
+ SwNode* pNd = GetDoc()->GetNodes()[ n ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ {
+ const sal_Int32 nStt = (n == nSttNd) ? nSttCnt : 0;
+ const sal_Int32 nEnd = (n == nEndNd)
+ ? nEndCnt
+ : pNd->GetTextNode()->GetText().getLength();
+
+ static_cast<SwTextNode*>(pNd)->GetParaAttr(*pSet, nStt, nEnd,
+ false, true,
+ bMergeIndentValuesOfNumRule,
+ GetLayout());
+ }
+ break;
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ static_cast<SwContentNode*>(pNd)->GetAttr( *pSet );
+ break;
+
+ default:
+ pNd = nullptr;
+ }
+
+ if( pNd )
+ {
+ if( pSet != &rSet )
+ {
+ if (!GetLayout()->HasMergedParas()
+ || pNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden)
+ {
+ rSet.MergeValues( aSet );
+ }
+ }
+
+ if( aSet.Count() )
+ aSet.ClearItem();
+ }
+ pSet = &aSet;
+ }
+
+ }
+
+ return true;
+}
+
+bool SwEditShell::GetCurAttr( SfxItemSet& rSet,
+ const bool bMergeIndentValuesOfNumRule ) const
+{
+ return GetPaMAttr( GetCursor(), rSet, bMergeIndentValuesOfNumRule );
+}
+
+void SwEditShell::GetCurParAttr( SfxItemSet& rSet) const
+{
+ GetPaMParAttr( GetCursor(), rSet );
+}
+
+void SwEditShell::GetPaMParAttr( SwPaM* pPaM, SfxItemSet& rSet ) const
+{
+ // number of nodes the function has explored so far
+ sal_uInt16 numberOfLookup = 0;
+
+ SfxItemSet aSet( *rSet.GetPool(), rSet.GetRanges() );
+ SfxItemSet* pSet = &rSet;
+
+ for(SwPaM& rCurrentPaM : pPaM->GetRingContainer())
+ { // for all the point and mark (selections)
+
+ // get the start and the end node of the current selection
+ SwNodeOffset nSttNd = rCurrentPaM.GetMark()->nNode.GetIndex(),
+ nEndNd = rCurrentPaM.GetPoint()->nNode.GetIndex();
+
+ // reverse start and end if there number aren't sorted correctly
+ if( nSttNd > nEndNd )
+ std::swap(nSttNd, nEndNd);
+
+ // for all the nodes in the current selection
+ // get the node (paragraph) attributes
+ // and merge them in rSet
+ for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
+ {
+ // get the node
+ SwNode* pNd = GetDoc()->GetNodes()[ n ];
+
+ if (GetLayout()->HasMergedParas()
+ && pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ continue;
+ }
+
+ if( pNd->IsTextNode() )
+ {
+ // get the node (paragraph) attributes
+ sw::GetAttrMerged(*pSet, *pNd->GetTextNode(), GetLayout());
+
+ if( pSet != &rSet && aSet.Count() )
+ {
+ rSet.MergeValues( aSet );
+ aSet.ClearItem();
+ }
+
+ pSet = &aSet;
+ }
+
+ ++numberOfLookup;
+
+ // if the maximum number of node that can be inspected has been reached
+ if (numberOfLookup >= getMaxLookup())
+ return;
+ }
+ }
+}
+
+SwTextFormatColl* SwEditShell::GetCurTextFormatColl( ) const
+{
+ return GetPaMTextFormatColl( GetCursor() );
+}
+
+SwTextFormatColl* SwEditShell::GetPaMTextFormatColl( SwPaM* pPaM ) const
+{
+ // number of nodes the function have explored so far
+ sal_uInt16 numberOfLookup = 0;
+
+ for(SwPaM& rCurrentPaM : pPaM->GetRingContainer())
+ { // for all the point and mark (selections)
+
+ // get the start and the end node of the current selection
+ SwNodeOffset nSttNd = rCurrentPaM.Start()->nNode.GetIndex(),
+ nEndNd = rCurrentPaM.End()->nNode.GetIndex();
+
+ // for all the nodes in the current Point and Mark
+ for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
+ {
+ // get the node
+ SwNode* pNd = GetDoc()->GetNodes()[ n ];
+
+ ++numberOfLookup;
+
+ // if the maximum number of node that can be inspected has been reached
+ if (numberOfLookup >= getMaxLookup())
+ return nullptr;
+
+ if( pNd->IsTextNode() )
+ {
+ SwTextNode *const pTextNode(sw::GetParaPropsNode(*GetLayout(), SwNodeIndex(*pNd)));
+ // if it's a text node get its named paragraph format
+ SwTextFormatColl *const pFormat = pTextNode->GetTextColl();
+
+ // if the paragraph format exist stop here and return it
+ if( pFormat != nullptr )
+ return pFormat;
+ }
+ }
+ }
+
+ // if none of the selected node contain a named paragraph format
+ return nullptr;
+}
+
+std::vector<std::pair< const SfxPoolItem*, std::unique_ptr<SwPaM> >> SwEditShell::GetItemWithPaM( sal_uInt16 nWhich )
+{
+ assert(isCHRATR(nWhich)); // sw_redlinehide: only thing that works
+ std::vector<std::pair< const SfxPoolItem*, std::unique_ptr<SwPaM> >> vItem;
+ for(SwPaM& rCurrentPaM : GetCursor()->GetRingContainer())
+ { // for all the point and mark (selections)
+
+ // get the start and the end node of the current selection
+ SwNodeOffset nSttNd = rCurrentPaM.Start()->nNode.GetIndex(),
+ nEndNd = rCurrentPaM.End()->nNode.GetIndex();
+ sal_Int32 nSttCnt = rCurrentPaM.Start()->nContent.GetIndex();
+ sal_Int32 nEndCnt = rCurrentPaM.End()->nContent.GetIndex();
+
+ SwPaM* pNewPaM = nullptr;
+ const SfxPoolItem* pItem = nullptr;
+
+ // for all the nodes in the current selection
+ for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
+ {
+ SwNode* pNd = GetDoc()->GetNodes()[ n ];
+ if( pNd->IsTextNode() )
+ {
+ SwTextNode* pTextNd = static_cast< SwTextNode* >( pNd );
+ const sal_Int32 nStt = (n == nSttNd) ? nSttCnt : 0;
+ const sal_Int32 nEnd = (n == nEndNd)
+ ? nEndCnt : pTextNd->GetText().getLength();
+ SwTextFrame const* pFrame;
+ const SwScriptInfo *const pScriptInfo =
+ SwScriptInfo::GetScriptInfo(*pTextNd, &pFrame);
+ TextFrameIndex const iStt(pScriptInfo
+ ? pFrame->MapModelToView(pTextNd, nStt)
+ : TextFrameIndex(-1/*invalid, do not use*/));
+ sal_uInt8 nScript = pScriptInfo
+ ? pScriptInfo->ScriptType(iStt)
+ : css::i18n::ScriptType::WEAK;
+ nWhich = GetWhichOfScript( nWhich, nScript );
+
+ // item from attribute set
+ if( pTextNd->HasSwAttrSet() )
+ {
+ pNewPaM = new SwPaM(*pNd, nStt, *pNd, nEnd);
+ pItem = pTextNd->GetSwAttrSet().GetItem( nWhich );
+ vItem.emplace_back( pItem, std::unique_ptr<SwPaM>(pNewPaM) );
+ }
+
+ if( !pTextNd->HasHints() )
+ continue;
+
+ // items with limited range
+ const size_t nSize = pTextNd->GetpSwpHints()->Count();
+ for( size_t m = 0; m < nSize; m++ )
+ {
+ const SwTextAttr* pHt = pTextNd->GetpSwpHints()->Get(m);
+ if( pHt->Which() == RES_TXTATR_AUTOFMT ||
+ pHt->Which() == RES_TXTATR_CHARFMT ||
+ pHt->Which() == RES_TXTATR_INETFMT )
+ {
+ const sal_Int32 nAttrStart = pHt->GetStart();
+ const sal_Int32* pAttrEnd = pHt->End();
+
+ // Ignore items not in selection
+ if( nAttrStart > nEnd )
+ break;
+ if( *pAttrEnd <= nStt )
+ continue;
+
+ nScript = pScriptInfo
+ ? pScriptInfo->ScriptType(iStt)
+ : css::i18n::ScriptType::WEAK;
+ nWhich = GetWhichOfScript( nWhich, nScript );
+ const SfxItemSet* pAutoSet = CharFormat::GetItemSet( pHt->GetAttr() );
+ if( pAutoSet )
+ {
+ SfxItemIter aItemIter( *pAutoSet );
+ pItem = aItemIter.GetCurItem();
+ while( pItem )
+ {
+ if( pItem->Which() == nWhich )
+ {
+ sal_Int32 nStart = 0, nStop = 0;
+ if( nAttrStart < nStt ) // Attribute starts before selection
+ nStart = nStt;
+ else
+ nStart = nAttrStart;
+ if( *pAttrEnd > nEnd ) // Attribute ends after selection
+ nStop = nEnd;
+ else
+ nStop = *pAttrEnd;
+ pNewPaM = new SwPaM(*pNd, nStart, *pNd, nStop);
+ vItem.emplace_back( pItem, std::unique_ptr<SwPaM>(pNewPaM) );
+ break;
+ }
+ pItem = aItemIter.NextItem();
+ }
+ // default item
+ if( !pItem && !pTextNd->HasSwAttrSet() )
+ {
+ pNewPaM = new SwPaM(*pNd, nStt, *pNd, nEnd);
+ pItem = pAutoSet->GetPool()->GetPoolDefaultItem( nWhich );
+ vItem.emplace_back( pItem, std::unique_ptr<SwPaM>(pNewPaM) );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return vItem;
+}
+
+bool SwEditShell::GetCurFootnote( SwFormatFootnote* pFillFootnote )
+{
+ // The cursor must be positioned on the current footnotes anchor:
+ SwPaM* pCursor = GetCursor();
+ SwTextNode* pTextNd = pCursor->GetNode().GetTextNode();
+ if( !pTextNd )
+ return false;
+
+ SwTextAttr *const pFootnote = pTextNd->GetTextAttrForCharAt(
+ pCursor->GetPoint()->nContent.GetIndex(), RES_TXTATR_FTN);
+ if( pFootnote && pFillFootnote )
+ {
+ // Transfer data from the attribute
+ const SwFormatFootnote &rFootnote = static_cast<SwTextFootnote*>(pFootnote)->GetFootnote();
+ pFillFootnote->SetNumber( rFootnote );
+ pFillFootnote->SetEndNote( rFootnote.IsEndNote() );
+ }
+ return nullptr != pFootnote;
+}
+
+bool SwEditShell::SetCurFootnote( const SwFormatFootnote& rFillFootnote )
+{
+ bool bChgd = false;
+ StartAllAction();
+
+ for(const SwPaM& rCursor : GetCursor()->GetRingContainer())
+ {
+ bChgd |=
+ mxDoc->SetCurFootnote(rCursor, rFillFootnote.GetNumStr(), rFillFootnote.IsEndNote());
+
+ }
+
+ EndAllAction();
+ return bChgd;
+}
+
+bool SwEditShell::HasFootnotes( bool bEndNotes ) const
+{
+ const SwFootnoteIdxs &rIdxs = mxDoc->GetFootnoteIdxs();
+ for ( auto pIdx : rIdxs )
+ {
+ const SwFormatFootnote &rFootnote = pIdx->GetFootnote();
+ if ( bEndNotes == rFootnote.IsEndNote() )
+ return true;
+ }
+ return false;
+}
+
+/// Give a List of all footnotes and their beginning texts
+size_t SwEditShell::GetSeqFootnoteList( SwSeqFieldList& rList, bool bEndNotes )
+{
+ rList.Clear();
+
+ IDocumentRedlineAccess & rIDRA(mxDoc->getIDocumentRedlineAccess());
+
+ const size_t nFootnoteCnt = mxDoc->GetFootnoteIdxs().size();
+ SwTextFootnote* pTextFootnote;
+ for( size_t n = 0; n < nFootnoteCnt; ++n )
+ {
+ pTextFootnote = mxDoc->GetFootnoteIdxs()[ n ];
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if ( rFootnote.IsEndNote() != bEndNotes )
+ continue;
+
+ SwNodeIndex* pIdx = pTextFootnote->GetStartNode();
+ if( pIdx )
+ {
+ SwNodeIndex aIdx( *pIdx, 1 );
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ if( !pTextNd )
+ pTextNd = static_cast<SwTextNode*>(mxDoc->GetNodes().GoNext( &aIdx ));
+
+ if( pTextNd )
+ {
+ if (GetLayout()->IsHideRedlines()
+ && sw::IsFootnoteDeleted(rIDRA, *pTextFootnote))
+ {
+ continue;
+ }
+
+ OUString sText(rFootnote.GetViewNumStr(*mxDoc, GetLayout()));
+ if( !sText.isEmpty() )
+ sText += " ";
+ sText += pTextNd->GetExpandText(GetLayout());
+
+ SeqFieldLstElem aNew( sText, pTextFootnote->GetSeqRefNo() );
+ while( rList.InsertSort( aNew ) )
+ aNew.sDlgEntry += " ";
+ }
+ }
+ }
+
+ return rList.Count();
+}
+
+/// Adjust left margin via object bar (similar to adjustment of numerations).
+bool SwEditShell::IsMoveLeftMargin( bool bRight, bool bModulus ) const
+{
+ bool bRet = true;
+
+ constexpr sal_uInt16 constTwips_2cm = o3tl::toTwips(20, o3tl::Length::mm);
+ constexpr tools::Long constTwips_5mm = o3tl::toTwips(5, o3tl::Length::mm);
+
+ const SvxTabStopItem& rTabItem = GetDoc()->GetDefault( RES_PARATR_TABSTOP );
+ sal_uInt16 nDefDist = o3tl::narrowing<sal_uInt16>(
+ rTabItem.Count() ? rTabItem[0].GetTabPos() : constTwips_2cm);
+
+ if( !nDefDist )
+ return false;
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ SwNodeOffset nSttNd = rPaM.Start()->nNode.GetIndex(),
+ nEndNd = rPaM.End()->nNode.GetIndex();
+
+ SwContentNode* pCNd;
+ for( SwNodeOffset n = nSttNd; bRet && n <= nEndNd; ++n )
+ {
+ pCNd = GetDoc()->GetNodes()[ n ]->GetTextNode();
+ if( nullptr != pCNd )
+ {
+ pCNd = sw::GetParaPropsNode(*GetLayout(), *pCNd);
+ const SvxLRSpaceItem& rLS = pCNd->GetAttr( RES_LR_SPACE );
+ if( bRight )
+ {
+ tools::Long nNext = rLS.GetTextLeft() + nDefDist;
+ if( bModulus )
+ nNext = ( nNext / nDefDist ) * nDefDist;
+ SwFrame* pFrame = pCNd->getLayoutFrame( GetLayout() );
+ if ( pFrame )
+ {
+ const SwTwips nFrameWidth = pFrame->IsVertical() ?
+ pFrame->getFrameArea().Height() :
+ pFrame->getFrameArea().Width();
+ bRet = nFrameWidth > (nNext + constTwips_5mm);
+ }
+ else
+ bRet = false;
+ }
+ }
+ }
+
+ if( !bRet )
+ break;
+
+ }
+ return bRet;
+}
+
+void SwEditShell::MoveLeftMargin( bool bRight, bool bModulus )
+{
+ StartAllAction();
+ StartUndo( SwUndoId::START );
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor ) // Multiple selection ?
+ {
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ GetDoc()->MoveLeftMargin( aRangeArr.SetPam( n, aPam ),
+ bRight, bModulus, GetLayout() );
+ }
+ else
+ GetDoc()->MoveLeftMargin( *pCursor, bRight, bModulus, GetLayout() );
+
+ EndUndo( SwUndoId::END );
+ EndAllAction();
+}
+
+static SvtScriptType lcl_SetScriptFlags( sal_uInt16 nType )
+{
+ switch( nType )
+ {
+ case css::i18n::ScriptType::LATIN:
+ return SvtScriptType::LATIN;
+ case css::i18n::ScriptType::ASIAN:
+ return SvtScriptType::ASIAN;
+ case css::i18n::ScriptType::COMPLEX:
+ return SvtScriptType::COMPLEX;
+ default:
+ return SvtScriptType::NONE;
+ }
+}
+
+static bool lcl_IsNoEndTextAttrAtPos(SwRootFrame const& rLayout,
+ const SwTextNode& rTNd, sal_Int32 const nPos,
+ SvtScriptType &rScrpt, bool bInSelection, bool bNum )
+{
+ bool bRet = false;
+ OUString sExp;
+
+ // consider numbering
+ if ( bNum )
+ {
+ bRet = false;
+ SwTextNode const*const pPropsNode(sw::GetParaPropsNode(rLayout, rTNd));
+ if (pPropsNode->IsInList())
+ {
+ OSL_ENSURE( pPropsNode->GetNumRule(),
+ "<lcl_IsNoEndTextAttrAtPos(..)> - no list style found at text node. Serious defect." );
+ const SwNumRule* pNumRule = pPropsNode->GetNumRule();
+ if(pNumRule)
+ {
+ int nListLevel = pPropsNode->GetActualListLevel();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ const SwNumFormat &rNumFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
+ if( SVX_NUM_BITMAP != rNumFormat.GetNumberingType() )
+ {
+ if ( SVX_NUM_CHAR_SPECIAL == rNumFormat.GetNumberingType() )
+ {
+ sal_UCS4 cBullet = rNumFormat.GetBulletChar();
+ sExp = OUString(&cBullet, 1);
+ }
+ else
+ sExp = pPropsNode->GetNumString(true, MAXLEVEL, &rLayout);
+ }
+ }
+ }
+ }
+
+ // and fields
+ if (nPos < rTNd.GetText().getLength() && CH_TXTATR_BREAKWORD == rTNd.GetText()[nPos])
+ {
+ const SwTextAttr* const pAttr = rTNd.GetTextAttrForCharAt( nPos );
+ if (pAttr)
+ {
+ bRet = true; // all other than fields can be
+ // defined as weak-script ?
+ if ( RES_TXTATR_FIELD == pAttr->Which() )
+ {
+ const SwField* const pField = pAttr->GetFormatField().GetField();
+ if (pField)
+ {
+ sExp += pField->ExpandField(true, &rLayout);
+ }
+ }
+ }
+ }
+
+ const sal_Int32 nEnd = sExp.getLength();
+ if ( nEnd )
+ {
+ if( bInSelection )
+ {
+ sal_uInt16 nScript;
+ for( sal_Int32 n = 0; n < nEnd;
+ n = g_pBreakIt->GetBreakIter()->endOfScript( sExp, n, nScript ))
+ {
+ nScript = g_pBreakIt->GetBreakIter()->getScriptType( sExp, n );
+ rScrpt |= lcl_SetScriptFlags( nScript );
+ }
+ }
+ else
+ rScrpt |= lcl_SetScriptFlags( g_pBreakIt->GetBreakIter()->
+ getScriptType( sExp, nEnd-1 ));
+ }
+
+ return bRet;
+}
+
+/// returns the script type of the selection
+SvtScriptType SwEditShell::GetScriptType() const
+{
+ SvtScriptType nRet = SvtScriptType::NONE;
+
+ {
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ const SwPosition *pStt = rPaM.Start(),
+ *pEnd = rPaM.End();
+ if( pStt == pEnd || *pStt == *pEnd )
+ {
+ const SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ // try to get SwScriptInfo
+ SwTextFrame const* pFrame;
+ const SwScriptInfo *const pScriptInfo =
+ SwScriptInfo::GetScriptInfo(*pTNd, &pFrame);
+
+ sal_Int32 nPos = pStt->nContent.GetIndex();
+ //Task 90448: we need the scripttype of the previous
+ // position, if no selection exist!
+ if( nPos )
+ {
+ SwIndex aIdx( pStt->nContent );
+ if( pTNd->GoPrevious( &aIdx, CRSR_SKIP_CHARS ) )
+ nPos = aIdx.GetIndex();
+ }
+
+ sal_uInt16 nScript;
+
+ if (!pTNd->GetText().isEmpty())
+ {
+ nScript = pScriptInfo
+ ? pScriptInfo->ScriptType(pFrame->MapModelToView(pTNd, nPos))
+ : g_pBreakIt->GetBreakIter()->getScriptType( pTNd->GetText(), nPos );
+ }
+ else
+ nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+
+ if (!lcl_IsNoEndTextAttrAtPos(*GetLayout(), *pTNd, nPos, nRet, false, false))
+ nRet |= lcl_SetScriptFlags( nScript );
+ }
+ }
+ else
+ {
+ SwNodeOffset nEndIdx = pEnd->nNode.GetIndex();
+ SwNodeIndex aIdx( pStt->nNode );
+ for( ; aIdx.GetIndex() <= nEndIdx; ++aIdx )
+ if( aIdx.GetNode().IsTextNode() )
+ {
+ const SwTextNode* pTNd = aIdx.GetNode().GetTextNode();
+ const OUString& rText = pTNd->GetText();
+
+ // try to get SwScriptInfo
+ SwTextFrame const* pFrame;
+ const SwScriptInfo *const pScriptInfo =
+ SwScriptInfo::GetScriptInfo(*pTNd, &pFrame);
+
+ sal_Int32 nChg = aIdx == pStt->nNode
+ ? pStt->nContent.GetIndex()
+ : 0;
+ sal_Int32 nEndPos = aIdx == nEndIdx
+ ? pEnd->nContent.GetIndex()
+ : rText.getLength();
+
+ OSL_ENSURE( nEndPos <= rText.getLength(),
+ "Index outside the range - endless loop!" );
+ if (nEndPos > rText.getLength())
+ nEndPos = rText.getLength();
+
+ bool const isUntilEnd(pScriptInfo
+ ? pFrame->MapViewToModelPos(TextFrameIndex(pFrame->GetText().getLength())) <= *pEnd
+ : rText.getLength() == nEndPos);
+ sal_uInt16 nScript;
+ while( nChg < nEndPos )
+ {
+ TextFrameIndex iChg(pScriptInfo
+ ? pFrame->MapModelToView(pTNd, nChg)
+ : TextFrameIndex(-1/*invalid, do not use*/));
+ nScript = pScriptInfo ?
+ pScriptInfo->ScriptType( iChg ) :
+ g_pBreakIt->GetBreakIter()->getScriptType(
+ rText, nChg );
+
+ if (!lcl_IsNoEndTextAttrAtPos(*GetLayout(), *pTNd, nChg, nRet, true,
+ TextFrameIndex(0) == iChg && isUntilEnd))
+ {
+ nRet |= lcl_SetScriptFlags( nScript );
+ }
+
+ if( (SvtScriptType::LATIN | SvtScriptType::ASIAN |
+ SvtScriptType::COMPLEX) == nRet )
+ break;
+
+ sal_Int32 nFieldPos = nChg+1;
+
+ if (pScriptInfo)
+ {
+ iChg = pScriptInfo->NextScriptChg(iChg);
+ if (iChg == TextFrameIndex(COMPLETE_STRING))
+ {
+ nChg = pTNd->Len();
+ }
+ else
+ {
+ std::pair<SwTextNode*, sal_Int32> const tmp(
+ pFrame->MapViewToModel(iChg));
+ nChg = (tmp.first == pTNd)
+ ? tmp.second
+ : pTNd->Len();
+ }
+ }
+ else
+ {
+ nChg = g_pBreakIt->GetBreakIter()->endOfScript(
+ rText, nChg, nScript );
+ }
+
+ nFieldPos = rText.indexOf(
+ CH_TXTATR_BREAKWORD, nFieldPos);
+ if ((-1 != nFieldPos) && (nFieldPos < nChg))
+ nChg = nFieldPos;
+ }
+ if( (SvtScriptType::LATIN | SvtScriptType::ASIAN |
+ SvtScriptType::COMPLEX) == nRet )
+ break;
+ }
+ }
+ if( (SvtScriptType::LATIN | SvtScriptType::ASIAN |
+ SvtScriptType::COMPLEX) == nRet )
+ break;
+
+ }
+ }
+ if( nRet == SvtScriptType::NONE )
+ nRet = SvtLanguageOptions::GetScriptTypeOfLanguage( LANGUAGE_SYSTEM );
+ return nRet;
+}
+
+LanguageType SwEditShell::GetCurLang() const
+{
+ const SwPaM* pCursor = GetCursor();
+ const SwPosition& rPos = *pCursor->GetPoint();
+ const SwTextNode* pTNd = rPos.nNode.GetNode().GetTextNode();
+ LanguageType nLang;
+ if( pTNd )
+ {
+ //JP 24.9.2001: if exist no selection, then get the language before
+ // the current character!
+ sal_Int32 nPos = rPos.nContent.GetIndex();
+ if( nPos && !pCursor->HasMark() )
+ --nPos;
+ nLang = pTNd->GetLang( nPos );
+ }
+ else
+ nLang = LANGUAGE_DONTKNOW;
+ return nLang;
+}
+
+sal_uInt16 SwEditShell::GetScalingOfSelectedText() const
+{
+ const SwPaM* pCursor = GetCursor();
+ const SwPosition* pStt = pCursor->Start();
+ const SwTextNode* pTNd = pStt->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTNd, "no textnode available" );
+
+ sal_uInt16 nScaleWidth;
+ if( pTNd )
+ {
+ SwTextFrame *const pFrame(static_cast<SwTextFrame *>(
+ pTNd->getLayoutFrame(GetLayout(), pStt)));
+ assert(pFrame); // shell cursor must be positioned in node with frame
+ TextFrameIndex const nStart(pFrame->MapModelToViewPos(*pStt));
+ TextFrameIndex const nEnd(
+ sw::FrameContainsNode(*pFrame, pCursor->End()->nNode.GetIndex())
+ ? pFrame->MapModelToViewPos(*pCursor->End())
+ : TextFrameIndex(pFrame->GetText().getLength()));
+ nScaleWidth = pFrame->GetScalingOfSelectedText(nStart, nEnd);
+ }
+ else
+ nScaleWidth = 100; // default are no scaling -> 100%
+ return nScaleWidth;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/eddel.cxx b/sw/source/core/edit/eddel.cxx
new file mode 100644
index 000000000..8b19e8c8b
--- /dev/null
+++ b/sw/source/core/edit/eddel.cxx
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <editsh.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <undobj.hxx>
+#include <SwRewriter.hxx>
+#include <osl/diagnose.h>
+
+#include <strings.hrc>
+#include <vector>
+
+void SwEditShell::DeleteSel(SwPaM& rPam, bool const isArtificialSelection, bool *const pUndo)
+{
+ bool bSelectAll = StartsWithTable() && ExtendedSelectedAll();
+ // only for selections
+ if (!rPam.HasMark()
+ || (*rPam.GetPoint() == *rPam.GetMark()
+ && !IsFlySelectedByCursor(*GetDoc(), *rPam.Start(), *rPam.End())))
+ {
+ return;
+ }
+
+ // Is the selection in a table? Then delete only the content of the selected boxes.
+ // Here, there are two cases:
+ // 1. Point and Mark are in one box, delete selection as usual
+ // 2. Point and Mark are in different boxes, search all selected boxes and delete content
+ // 3. Point and Mark are at the document start and end, Point is in a table: delete selection as usual
+ if( rPam.GetNode().FindTableNode() &&
+ rPam.GetNode().StartOfSectionNode() !=
+ rPam.GetNode(false).StartOfSectionNode() && !bSelectAll )
+ {
+ // group the Undo in the table
+ if( pUndo && !*pUndo )
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ *pUndo = true;
+ }
+ SwPaM aDelPam( *rPam.Start() );
+ const SwPosition* pEndSelPos = rPam.End();
+ do {
+ aDelPam.SetMark();
+ SwNode& rNd = aDelPam.GetNode();
+ const SwNode& rEndNd = *rNd.EndOfSectionNode();
+ if( pEndSelPos->nNode.GetIndex() <= rEndNd.GetIndex() )
+ {
+ *aDelPam.GetPoint() = *pEndSelPos;
+ pEndSelPos = nullptr; // misuse a pointer as a flag
+ }
+ else
+ {
+ // then go to the end of the selection
+ aDelPam.GetPoint()->nNode = rEndNd;
+ aDelPam.Move( fnMoveBackward, GoInContent );
+ }
+ // skip protected boxes
+ if( !rNd.IsContentNode() ||
+ !rNd.IsInProtectSect() )
+ {
+ // delete everything
+ GetDoc()->getIDocumentContentOperations().DeleteAndJoin( aDelPam );
+ SaveTableBoxContent( aDelPam.GetPoint() );
+ }
+
+ if( !pEndSelPos ) // at the end of a selection
+ break;
+ aDelPam.DeleteMark();
+ aDelPam.Move( fnMoveForward, GoInContent ); // next box
+ } while( pEndSelPos );
+ }
+ else
+ {
+ std::optional<SwPaM> pNewPam;
+ SwPaM * pPam = &rPam;
+ if (bSelectAll)
+ {
+ assert(dynamic_cast<SwShellCursor*>(&rPam)); // must be corrected pam
+ pNewPam.emplace(*rPam.GetMark(), *rPam.GetPoint());
+ // Selection starts at the first para of the first cell, but we
+ // want to delete the table node before the first cell as well.
+ while (SwTableNode const* pTableNode =
+ pNewPam->Start()->nNode.GetNode().StartOfSectionNode()->FindTableNode())
+ {
+ pNewPam->Start()->nNode = *pTableNode;
+ }
+ // tdf#133990 ensure section is included in SwUndoDelete
+ while (SwSectionNode const* pSectionNode =
+ pNewPam->Start()->nNode.GetNode().StartOfSectionNode()->FindSectionNode())
+ {
+ pNewPam->Start()->nNode = *pSectionNode;
+ }
+ pNewPam->Start()->nContent.Assign(nullptr, 0);
+ pPam = &*pNewPam;
+ }
+ // delete everything
+ GetDoc()->getIDocumentContentOperations().DeleteAndJoin(*pPam,
+ isArtificialSelection ? SwDeleteFlags::ArtificialSelection : SwDeleteFlags::Default);
+ SaveTableBoxContent( pPam->GetPoint() );
+ }
+
+ // Selection is not needed anymore
+ rPam.DeleteMark();
+}
+
+bool SwEditShell::Delete(bool const isArtificialSelection)
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+ if ( !HasReadonlySel() || CursorInsideInputField() )
+ {
+ StartAllAction();
+
+ bool bUndo = GetCursor()->GetNext() != GetCursor();
+ if( bUndo ) // more than one selection?
+ {
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_MULTISEL));
+
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE, &aRewriter);
+ }
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ DeleteSel(rPaM, isArtificialSelection, &bUndo);
+ }
+
+ // If undo container then close here
+ if( bUndo )
+ {
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ EndAllAction();
+ bRet = true;
+ }
+ else
+ {
+ bRet = RemoveParagraphMetadataFieldAtCursor();
+ }
+
+ return bRet;
+}
+
+bool SwEditShell::Copy( SwEditShell& rDestShell )
+{
+ CurrShell aCurr( &rDestShell );
+
+ // List of insert positions for smart insert of block selections
+ std::vector< std::shared_ptr<SwPosition> > aInsertList;
+
+ // Fill list of insert positions
+ {
+ SwPosition * pPos = nullptr;
+ std::shared_ptr<SwPosition> pInsertPos;
+ sal_uInt16 nMove = 0;
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( !pPos )
+ {
+ if( &rDestShell == this )
+ {
+ // First cursor represents the target position!!
+ rPaM.DeleteMark();
+ pPos = rPaM.GetPoint();
+ continue;
+ }
+ else
+ pPos = rDestShell.GetCursor()->GetPoint();
+ }
+ if( IsBlockMode() )
+ { // In block mode different insert positions will be calculated
+ // by simulated cursor movements from the given first insert position
+ if( nMove )
+ {
+ SwCursor aCursor( *pPos, nullptr);
+ if (aCursor.UpDown(false, nMove, nullptr, 0, *GetLayout()))
+ {
+ pInsertPos = std::make_shared<SwPosition>( *aCursor.GetPoint() );
+ aInsertList.push_back( pInsertPos );
+ }
+ }
+ else
+ pInsertPos = std::make_shared<SwPosition>( *pPos );
+ ++nMove;
+ }
+ SwPosition *pTmp = IsBlockMode() ? pInsertPos.get() : pPos;
+ // Check if a selection would be copied into itself
+ if( rDestShell.GetDoc() == GetDoc() &&
+ *rPaM.Start() <= *pTmp && *pTmp < *rPaM.End() )
+ return false;
+ }
+ }
+
+ rDestShell.StartAllAction();
+ SwPosition *pPos = nullptr;
+ bool bRet = false;
+ bool bFirstMove = true;
+ SwNodeIndex aSttNdIdx( rDestShell.GetDoc()->GetNodes() );
+ sal_Int32 nSttCntIdx = 0;
+ // For block selection this list is filled with the insert positions
+ auto pNextInsert = aInsertList.begin();
+
+ rDestShell.GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( !pPos )
+ {
+ if( &rDestShell == this )
+ {
+ // First cursor represents the target position!!
+ rPaM.DeleteMark();
+ pPos = rPaM.GetPoint();
+ continue;
+ }
+ else
+ pPos = rDestShell.GetCursor()->GetPoint();
+ }
+ if( !bFirstMove )
+ {
+ if( pNextInsert != aInsertList.end() )
+ {
+ pPos = pNextInsert->get();
+ ++pNextInsert;
+ }
+ else if( IsBlockMode() )
+ GetDoc()->getIDocumentContentOperations().SplitNode( *pPos, false );
+ }
+
+ // Only for a selection (non-text nodes have selection but Point/GetMark are equal)
+ if( !rPaM.HasMark() || *rPaM.GetPoint() == *rPaM.GetMark() )
+ continue;
+
+ if( bFirstMove )
+ {
+ // Store start position of the new area
+ aSttNdIdx = pPos->nNode.GetIndex()-1;
+ nSttCntIdx = pPos->nContent.GetIndex();
+ bFirstMove = false;
+ }
+
+ const bool bSuccess( GetDoc()->getIDocumentContentOperations().CopyRange(rPaM, *pPos, SwCopyFlags::CheckPosInFly) );
+ if (!bSuccess)
+ continue;
+
+ SwPaM aInsertPaM(*pPos, SwPosition(aSttNdIdx));
+ rDestShell.GetDoc()->MakeUniqueNumRules(aInsertPaM);
+
+ bRet = true;
+ }
+
+ // Maybe nothing has been moved?
+ if( !bFirstMove )
+ {
+ SwPaM* pCursor = rDestShell.GetCursor();
+ pCursor->SetMark();
+ pCursor->GetPoint()->nNode = aSttNdIdx.GetIndex()+1;
+ pCursor->GetPoint()->nContent.Assign( pCursor->GetContentNode(),nSttCntIdx);
+ pCursor->Exchange();
+ }
+ else
+ {
+ // If the cursor moved during move process, move also its GetMark
+ rDestShell.GetCursor()->SetMark();
+ rDestShell.GetCursor()->DeleteMark();
+ }
+#if OSL_DEBUG_LEVEL > 0
+ // check if the indices are registered in the correct nodes
+ {
+ for(SwPaM& rCmp : rDestShell.GetCursor()->GetRingContainer())
+ {
+ OSL_ENSURE( rCmp.GetPoint()->nContent.GetIdxReg()
+ == rCmp.GetContentNode(), "Point in wrong Node" );
+ OSL_ENSURE( rCmp.GetMark()->nContent.GetIdxReg()
+ == rCmp.GetContentNode(false), "Mark in wrong Node" );
+ }
+ }
+#endif
+
+ // close Undo container here
+ rDestShell.GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ rDestShell.EndAllAction();
+
+ rDestShell.SaveTableBoxContent( rDestShell.GetCursor()->GetPoint() );
+
+ return bRet;
+}
+
+/** Replace a selected area in a text node with a given string.
+ *
+ * Intended for "search & replace".
+ *
+ * @param bRegExpRplc if <true> replace tabs (\\t) and replace with found string (not \&).
+ * E.g. [Fnd: "zzz", Repl: "xx\t\\t..&..\&"] --> "xx\t<Tab>..zzz..&"
+ */
+bool SwEditShell::Replace( const OUString& rNewStr, bool bRegExpRplc )
+{
+ CurrShell aCurr( this );
+
+ bool bRet = false;
+ if( !HasReadonlySel() )
+ {
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() && *rPaM.GetMark() != *rPaM.GetPoint() )
+ {
+ bRet = sw::ReplaceImpl(rPaM, rNewStr, bRegExpRplc, *GetDoc(), GetLayout())
+ || bRet;
+ SaveTableBoxContent( rPaM.GetPoint() );
+ }
+ }
+
+ // close Undo container here
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ EndAllAction();
+ }
+ return bRet;
+}
+
+/// special method for JOE's wizards
+bool SwEditShell::DelFullPara()
+{
+ bool bRet = false;
+ if( !IsTableMode() )
+ {
+ SwPaM* pCursor = GetCursor();
+ // no multi selection
+ if( !pCursor->IsMultiSelection() && !HasReadonlySel() )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+ bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( *pCursor );
+ EndAllAction();
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edfcol.cxx b/sw/source/core/edit/edfcol.cxx
new file mode 100644
index 000000000..fd1cd62cb
--- /dev/null
+++ b/sw/source/core/edit/edfcol.cxx
@@ -0,0 +1,2329 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <editsh.hxx>
+
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/HomogenMatrix3.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/text/XParagraphAppend.hpp>
+#include <com/sun/star/text/XParagraphCursor.hpp>
+#include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/rdf/XMetadatable.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/unoprnms.hxx>
+#include <sfx2/classificationhelper.hxx>
+#include <svx/ClassificationCommon.hxx>
+#include <svx/ClassificationField.hxx>
+#include <svl/cryptosign.hxx>
+#include <svl/sigstruct.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/virdev.hxx>
+
+#include <redline.hxx>
+#include <poolfmt.hxx>
+#include <hintids.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <viewopt.hxx>
+#include <SwRewriter.hxx>
+#include <numrule.hxx>
+#include <swundo.hxx>
+#include <docary.hxx>
+#include <docsh.hxx>
+#include <unoprnms.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <rdfhelper.hxx>
+#include <sfx2/watermarkitem.hxx>
+
+#include <unoparagraph.hxx>
+#include <strings.hrc>
+#include <undobj.hxx>
+#include <UndoParagraphSignature.hxx>
+#include <txtatr.hxx>
+#include <fmtmeta.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <IDocumentRedlineAccess.hxx>
+
+constexpr OUStringLiteral WATERMARK_NAME = u"PowerPlusWaterMarkObject";
+#define WATERMARK_AUTO_SIZE sal_uInt32(1)
+
+namespace
+{
+constexpr OUStringLiteral MetaFilename(u"tscp/bails.rdf");
+constexpr OUStringLiteral MetaNS(u"urn:bails");
+constexpr OUStringLiteral ParagraphSignatureRDFNamespace = u"urn:bails:loext:paragraph:signature:";
+constexpr OUStringLiteral ParagraphSignatureIdRDFName = u"urn:bails:loext:paragraph:signature:id";
+constexpr OUStringLiteral ParagraphSignatureDigestRDFName = u":digest";
+constexpr OUStringLiteral ParagraphSignatureDateRDFName = u":date";
+constexpr OUStringLiteral ParagraphSignatureUsageRDFName = u":usage";
+constexpr OUStringLiteral ParagraphSignatureLastIdRDFName = u"urn:bails:loext:paragraph:signature:lastid";
+constexpr OUStringLiteral ParagraphClassificationNameRDFName = u"urn:bails:loext:paragraph:classification:name";
+constexpr OUStringLiteral ParagraphClassificationValueRDFName = u"urn:bails:loext:paragraph:classification:value";
+constexpr OUStringLiteral ParagraphClassificationAbbrRDFName = u"urn:bails:loext:paragraph:classification:abbreviation";
+constexpr OUStringLiteral ParagraphClassificationFieldNamesRDFName = u"urn:bails:loext:paragraph:classification:fields";
+constexpr OUStringLiteral MetadataFieldServiceName = u"com.sun.star.text.textfield.MetadataField";
+constexpr OUStringLiteral DocInfoServiceName = u"com.sun.star.text.TextField.DocInfo.Custom";
+
+/// Find all page styles which are currently used in the document.
+std::vector<OUString> lcl_getUsedPageStyles(SwViewShell const * pShell)
+{
+ std::vector<OUString> aReturn;
+
+ SwRootFrame* pLayout = pShell->GetLayout();
+ for (SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pFrame);
+ if (const SwPageDesc *pDesc = pPage->FindPageDesc())
+ aReturn.push_back(pDesc->GetName());
+ }
+
+ return aReturn;
+}
+
+/// Search for a field named rFieldName of type rServiceName in xText and return it.
+uno::Reference<text::XTextField> lcl_findField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, std::u16string_view rFieldName)
+{
+ uno::Reference<text::XTextField> xField;
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xTextField;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
+ if (!xTextField->supportsService(rServiceName))
+ continue;
+
+ OUString aName;
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
+ if (aName == rFieldName)
+ {
+ xField = uno::Reference<text::XTextField>(xTextField, uno::UNO_QUERY);
+ break;
+ }
+ }
+ }
+
+ return xField;
+}
+
+/// Search for a field named rFieldName of type rServiceName in xText and return true iff found.
+bool lcl_hasField(const uno::Reference<text::XText>& xText, const OUString& rServiceName, std::u16string_view rFieldName)
+{
+ return lcl_findField(xText, rServiceName, rFieldName).is();
+}
+
+/// Search for a frame with WATERMARK_NAME in name of type rServiceName in xText. Returns found name in rShapeName.
+uno::Reference<drawing::XShape> lcl_getWatermark(const uno::Reference<text::XText>& xText,
+ const OUString& rServiceName, OUString& rShapeName, bool& bSuccess)
+{
+ bSuccess = false;
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ continue;
+
+ bSuccess = true;
+
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != "Frame")
+ continue;
+
+ uno::Reference<container::XContentEnumerationAccess> xContentEnumerationAccess(xTextPortion, uno::UNO_QUERY);
+ if (!xContentEnumerationAccess.is())
+ continue;
+
+ uno::Reference<container::XEnumeration> xEnumeration = xContentEnumerationAccess->createContentEnumeration("com.sun.star.text.TextContent");
+ if (!xEnumeration->hasMoreElements())
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xWatermark(xEnumeration->nextElement(), uno::UNO_QUERY);
+ if (!xWatermark->supportsService(rServiceName))
+ continue;
+
+ uno::Reference<container::XNamed> xNamed(xWatermark, uno::UNO_QUERY);
+
+ if (!xNamed->getName().match(WATERMARK_NAME))
+ continue;
+
+ rShapeName = xNamed->getName();
+
+ uno::Reference<drawing::XShape> xShape(xWatermark, uno::UNO_QUERY);
+ return xShape;
+ }
+ }
+
+ return uno::Reference<drawing::XShape>();
+}
+
+/// Extract the text of the paragraph without any of the fields.
+/// TODO: Consider moving to SwTextNode, or extend ModelToViewHelper.
+OString lcl_getParagraphBodyText(const uno::Reference<text::XTextContent>& xText)
+{
+ OUStringBuffer strBuf;
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xText, uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ return OString();
+
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Any elem = xTextPortions->nextElement();
+
+ //TODO: Consider including hidden and conditional texts/portions.
+ OUString aTextPortionType;
+ uno::Reference<beans::XPropertySet> xPropertySet(elem, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType == "Text")
+ {
+ uno::Reference<text::XTextRange> xTextRange(elem, uno::UNO_QUERY);
+ if (xTextRange.is())
+ strBuf.append(xTextRange->getString());
+ }
+ }
+
+ // Cleanup the dummy characters added by fields (which we exclude).
+ comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDSTART);
+ comphelper::string::remove(strBuf, CH_TXT_ATR_INPUTFIELDEND);
+ comphelper::string::remove(strBuf, CH_TXTATR_BREAKWORD);
+
+ return strBuf.makeStringAndClear().trim().toUtf8();
+}
+
+template <typename T>
+std::map<OUString, OUString> lcl_getRDFStatements(const uno::Reference<frame::XModel>& xModel,
+ const T& xRef)
+{
+ try
+ {
+ const css::uno::Reference<css::rdf::XResource> xSubject(xRef, uno::UNO_QUERY);
+ return SwRDFHelper::getStatements(xModel, MetaNS, xSubject);
+ }
+ catch (const ::css::uno::Exception&)
+ {
+ }
+
+ return std::map<OUString, OUString>();
+}
+
+/// Returns RDF (key, value) pair associated with the field, if any.
+std::pair<OUString, OUString> lcl_getFieldRDFByPrefix(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextField>& xField,
+ std::u16string_view sPrefix)
+{
+ for (const auto& pair : lcl_getRDFStatements(xModel, xField))
+ {
+ if (pair.first.startsWith(sPrefix))
+ return pair;
+ }
+
+ return std::make_pair(OUString(), OUString());
+}
+
+/// Returns RDF (key, value) pair associated with the field, if any.
+template <typename T>
+std::pair<OUString, OUString> lcl_getRDF(const uno::Reference<frame::XModel>& xModel,
+ const T& xRef,
+ const OUString& sRDFName)
+{
+ const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xRef);
+ const auto it = aStatements.find(sRDFName);
+ return (it != aStatements.end()) ? std::make_pair(it->first, it->second) : std::make_pair(OUString(), OUString());
+}
+
+/// Returns true iff the field in question is paragraph signature.
+/// Note: must have associated RDF, since signatures are otherwise just metadata fields.
+bool lcl_IsParagraphSignatureField(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextField>& xField)
+{
+ return (lcl_getRDF(xModel, xField, ParagraphSignatureIdRDFName).first == ParagraphSignatureIdRDFName);
+}
+
+uno::Reference<text::XTextField> lcl_findFieldByRDF(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<text::XTextContent>& xParagraph,
+ const OUString& sRDFName,
+ std::u16string_view sRDFValue)
+{
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ return uno::Reference<text::XTextField>();
+
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ if (!xTextPortions.is())
+ return uno::Reference<text::XTextField>();
+
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xTextField;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
+ if (!xTextField->supportsService(MetadataFieldServiceName))
+ continue;
+
+ uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
+ const std::pair<OUString, OUString> pair = lcl_getRDF(xModel, xField, sRDFName);
+ if (pair.first == sRDFName && (sRDFValue.empty() || sRDFValue == pair.second))
+ return xField;
+ }
+
+ return uno::Reference<text::XTextField>();
+}
+
+struct SignatureDescr
+{
+ OUString msSignature;
+ OUString msUsage;
+ OUString msDate;
+
+ bool isValid() const { return !msSignature.isEmpty(); }
+};
+
+SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextContent>& xParagraph,
+ std::u16string_view sFieldId)
+{
+ SignatureDescr aDescr;
+
+ const OUString prefix = ParagraphSignatureRDFNamespace + sFieldId;
+ const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xParagraph);
+
+ const auto itSig = aStatements.find(prefix + ParagraphSignatureDigestRDFName);
+ aDescr.msSignature = (itSig != aStatements.end() ? itSig->second : OUString());
+
+ const auto itDate = aStatements.find(prefix + ParagraphSignatureDateRDFName);
+ aDescr.msDate = (itDate != aStatements.end() ? itDate->second : OUString());
+
+ const auto itUsage = aStatements.find(prefix + ParagraphSignatureUsageRDFName);
+ aDescr.msUsage = (itUsage != aStatements.end() ? itUsage->second : OUString());
+
+ return aDescr;
+}
+
+SignatureDescr lcl_getSignatureDescr(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextContent>& xParagraph,
+ const uno::Reference<css::text::XTextField>& xField)
+{
+ const OUString sFieldId = lcl_getRDF(xModel, xField, ParagraphSignatureIdRDFName).second;
+ if (!sFieldId.isEmpty())
+ return lcl_getSignatureDescr(xModel, xParagraph, sFieldId);
+
+ return SignatureDescr();
+}
+
+/// Validate and create the signature field display text from the fields.
+std::pair<bool, OUString> lcl_MakeParagraphSignatureFieldText(const SignatureDescr& aDescr,
+ const OString& utf8Text)
+{
+ OUString msg = SwResId(STR_INVALID_SIGNATURE);
+ bool valid = false;
+
+ if (aDescr.isValid())
+ {
+ const char* pData = utf8Text.getStr();
+ const std::vector<unsigned char> data(pData, pData + utf8Text.getLength());
+
+ OString encSignature;
+ if (aDescr.msSignature.convertToString(&encSignature, RTL_TEXTENCODING_UTF8, 0))
+ {
+ const std::vector<unsigned char> sig(svl::crypto::DecodeHexString(encSignature));
+ SignatureInformation aInfo(0);
+ valid = svl::crypto::Signing::Verify(data, false, sig, aInfo);
+ valid = valid
+ && aInfo.nStatus == xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
+
+ assert(aInfo.GetSigningCertificate()); // it was valid
+ msg = SwResId(STR_SIGNED_BY) + ": " + aInfo.GetSigningCertificate()->X509Subject + ", " +
+ aDescr.msDate;
+ msg += (!aDescr.msUsage.isEmpty() ? (" (" + aDescr.msUsage + "): ") : OUString(": "));
+ msg += (valid ? SwResId(STR_VALID) : SwResId(STR_INVALID));
+ }
+ }
+
+ return std::make_pair(valid, msg);
+}
+
+/// Validate and return validation result and signature field display text.
+std::pair<bool, OUString>
+lcl_MakeParagraphSignatureFieldText(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextContent>& xParagraph,
+ const uno::Reference<css::text::XTextField>& xField,
+ const OString& utf8Text)
+{
+ const SignatureDescr aDescr = lcl_getSignatureDescr(xModel, xParagraph, xField);
+ return lcl_MakeParagraphSignatureFieldText(aDescr, utf8Text);
+}
+
+/// Generate the next valid ID for the new signature on this paragraph.
+OUString lcl_getNextSignatureId(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<text::XTextContent>& xParagraph)
+{
+ const OUString sFieldId = lcl_getRDF(xModel, xParagraph, ParagraphSignatureLastIdRDFName).second;
+ return OUString::number(!sFieldId.isEmpty() ? sFieldId.toInt32() + 1 : 1);
+}
+
+/// Creates and inserts Paragraph Signature Metadata field and creates the RDF entry
+uno::Reference<text::XTextField> lcl_InsertParagraphSignature(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<text::XTextContent>& xParagraph,
+ const OUString& signature,
+ const OUString& usage)
+{
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
+ auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
+
+ // Add the signature at the end.
+ xField->attach(xParagraph->getAnchor()->getEnd());
+
+ const OUString sId = lcl_getNextSignatureId(xModel, xParagraph);
+
+ const css::uno::Reference<css::rdf::XResource> xSubject(xField, uno::UNO_QUERY);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xSubject, ParagraphSignatureIdRDFName, sId);
+
+ // First convert the UTC UNIX timestamp to a tools::DateTime then to local time.
+ DateTime aDateTime = DateTime::CreateFromUnixTime(time(nullptr));
+ aDateTime.ConvertToLocalTime();
+ OUStringBuffer rBuffer;
+ rBuffer.append(static_cast<sal_Int32>(aDateTime.GetYear()));
+ rBuffer.append('-');
+ if (aDateTime.GetMonth() < 10)
+ rBuffer.append('0');
+ rBuffer.append(static_cast<sal_Int32>(aDateTime.GetMonth()));
+ rBuffer.append('-');
+ if (aDateTime.GetDay() < 10)
+ rBuffer.append('0');
+ rBuffer.append(static_cast<sal_Int32>(aDateTime.GetDay()));
+
+ // Now set the RDF on the paragraph, since that's what is preserved in .doc(x).
+ const css::uno::Reference<css::rdf::XResource> xParaSubject(xParagraph, uno::UNO_QUERY);
+ const OUString prefix = ParagraphSignatureRDFNamespace + sId;
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, ParagraphSignatureLastIdRDFName, sId);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureDigestRDFName, signature);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureUsageRDFName, usage);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xParaSubject, prefix + ParagraphSignatureDateRDFName, rBuffer.makeStringAndClear());
+
+ return xField;
+}
+
+/// Updates the signature field text if changed and returns true only iff updated.
+bool lcl_DoUpdateParagraphSignatureField(SwDoc& rDoc,
+ const uno::Reference<css::text::XTextField>& xField,
+ const OUString& sDisplayText)
+{
+ // Disable undo to avoid introducing noise when we edit the metadata field.
+ const bool isUndoEnabled = rDoc.GetIDocumentUndoRedo().DoesUndo();
+ rDoc.GetIDocumentUndoRedo().DoUndo(false);
+ comphelper::ScopeGuard const g([&rDoc, isUndoEnabled]() {
+ rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
+ });
+
+ try
+ {
+ uno::Reference<css::text::XTextRange> xText(xField, uno::UNO_QUERY);
+ const OUString curText = xText->getString();
+ if (curText != sDisplayText)
+ {
+ xText->setString(sDisplayText);
+ return true;
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ // We failed; avoid crashing.
+ DBG_UNHANDLED_EXCEPTION("sw.uno", "Failed to update paragraph signature");
+ }
+
+ return false;
+}
+
+/// Updates the signature field text if changed and returns true only iff updated.
+bool lcl_UpdateParagraphSignatureField(SwDoc& rDoc,
+ const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextContent>& xParagraph,
+ const uno::Reference<css::text::XTextField>& xField,
+ const OString& utf8Text)
+{
+ const OUString sDisplayText
+ = lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).second;
+ return lcl_DoUpdateParagraphSignatureField(rDoc, xField, sDisplayText);
+}
+
+void lcl_RemoveParagraphMetadataField(const uno::Reference<css::text::XTextField>& xField)
+{
+ uno::Reference<css::text::XTextRange> xParagraph(xField->getAnchor());
+ xParagraph->getText()->removeTextContent(xField);
+}
+
+/// Returns true iff the field in question is paragraph classification.
+/// Note: must have associated RDF, since classifications are otherwise just metadata fields.
+bool lcl_IsParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextField>& xField,
+ std::u16string_view sKey)
+{
+ const std::pair<OUString, OUString> rdfPair = lcl_getRDF(xModel, xField, ParagraphClassificationNameRDFName);
+ return rdfPair.first == ParagraphClassificationNameRDFName && (sKey.empty() || rdfPair.second == sKey);
+}
+
+uno::Reference<text::XTextField> lcl_FindParagraphClassificationField(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<text::XTextContent>& xParagraph,
+ std::u16string_view sKey = u"")
+{
+ uno::Reference<text::XTextField> xTextField;
+
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ return xTextField;
+
+ // Enumerate text portions to find metadata fields. This is expensive, best to enumerate fields only.
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xServiceInfo;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xServiceInfo;
+ if (!xServiceInfo->supportsService(MetadataFieldServiceName))
+ continue;
+
+ uno::Reference<text::XTextField> xField(xServiceInfo, uno::UNO_QUERY);
+ if (lcl_IsParagraphClassificationField(xModel, xField, sKey))
+ {
+ xTextField = xField;
+ break;
+ }
+ }
+
+ return xTextField;
+}
+
+/// Creates and inserts Paragraph Classification Metadata field and creates the RDF entry
+uno::Reference<text::XTextField> lcl_InsertParagraphClassification(const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<text::XTextContent>& xParent)
+{
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
+ auto xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
+
+ // Add the classification at the start.
+ xField->attach(xParent->getAnchor()->getStart());
+ return xField;
+}
+
+/// Updates the paragraph classification field text if changed and returns true only iff updated.
+bool lcl_UpdateParagraphClassificationField(SwDoc* pDoc,
+ const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<css::text::XTextContent>& xTextNode,
+ const OUString& sKey,
+ const OUString& sValue,
+ const OUString& sDisplayText)
+{
+ // Disable undo to avoid introducing noise when we edit the metadata field.
+ const bool isUndoEnabled = pDoc->GetIDocumentUndoRedo().DoesUndo();
+ pDoc->GetIDocumentUndoRedo().DoUndo(false);
+ comphelper::ScopeGuard const g([pDoc, isUndoEnabled] () {
+ pDoc->GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
+ });
+
+ uno::Reference<text::XTextField> xField = lcl_InsertParagraphClassification(xModel, xTextNode);
+
+ css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, sKey, sValue);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationNameRDFName, sKey);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphClassificationValueRDFName, sValue);
+
+ css::uno::Reference<css::rdf::XResource> xNodeSubject(xTextNode, uno::UNO_QUERY);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, sKey, sValue);
+
+ return lcl_DoUpdateParagraphSignatureField(*pDoc, xField, sDisplayText);
+}
+
+void lcl_ValidateParagraphSignatures(SwDoc& rDoc, const uno::Reference<text::XTextContent>& xParagraph, const bool updateDontRemove)
+{
+ SwDocShell* pDocShell = rDoc.GetDocShell();
+ if (!pDocShell)
+ return;
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+
+ // Check if the paragraph is signed.
+ try
+ {
+ const std::pair<OUString, OUString> pair = lcl_getRDF(xModel, xParagraph, ParagraphSignatureLastIdRDFName);
+ if (pair.second.isEmpty())
+ return;
+ }
+ catch (const ::css::uno::Exception&)
+ {
+ return;
+ }
+
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ if (!xTextPortions.is())
+ return;
+
+ // Get the text (without fields).
+ const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
+ if (utf8Text.isEmpty())
+ return;
+
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xTextField;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
+ if (!xTextField->supportsService(MetadataFieldServiceName))
+ continue;
+
+ uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
+ if (!lcl_IsParagraphSignatureField(xModel, xField))
+ {
+ continue;
+ }
+
+ if (updateDontRemove)
+ {
+ lcl_UpdateParagraphSignatureField(rDoc, xModel, xParagraph, xField, utf8Text);
+ }
+ else if (!lcl_MakeParagraphSignatureFieldText(xModel, xParagraph, xField, utf8Text).first)
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
+ rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoParagraphSigning>(rDoc, xField, xParagraph, false));
+ lcl_RemoveParagraphMetadataField(xField);
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
+ }
+ }
+}
+
+} // anonymous namespace
+
+SwTextFormatColl& SwEditShell::GetDfltTextFormatColl() const
+{
+ return *GetDoc()->GetDfltTextFormatColl();
+}
+
+sal_uInt16 SwEditShell::GetTextFormatCollCount() const
+{
+ return GetDoc()->GetTextFormatColls()->size();
+}
+
+SwTextFormatColl& SwEditShell::GetTextFormatColl(sal_uInt16 nFormatColl) const
+{
+ return *((*(GetDoc()->GetTextFormatColls()))[nFormatColl]);
+}
+
+static void insertFieldToDocument(uno::Reference<lang::XMultiServiceFactory> const & rxMultiServiceFactory,
+ uno::Reference<text::XText> const & rxText, uno::Reference<text::XParagraphCursor> const & rxParagraphCursor,
+ OUString const & rsKey)
+{
+ uno::Reference<beans::XPropertySet> xField(rxMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
+ xField->setPropertyValue(UNO_NAME_NAME, uno::Any(rsKey));
+ uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
+
+ rxText->insertTextContent(rxParagraphCursor, xTextContent, false);
+}
+
+static void removeAllClassificationFields(std::u16string_view rPolicy, uno::Reference<text::XText> const & rxText)
+{
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(rxText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xTextField;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
+ if (!xTextField->supportsService(DocInfoServiceName))
+ continue;
+
+ OUString aName;
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
+ if (aName.startsWith(rPolicy))
+ {
+ uno::Reference<text::XTextField> xField(xTextField, uno::UNO_QUERY);
+ rxText->removeTextContent(xField);
+ }
+ }
+ }
+}
+
+static sal_Int32 getNumberOfParagraphs(uno::Reference<text::XText> const & xText)
+{
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumAccess(xText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphEnum = xParagraphEnumAccess->createEnumeration();
+ sal_Int32 nResult = 0;
+ while (xParagraphEnum->hasMoreElements())
+ {
+ xParagraphEnum->nextElement();
+ nResult++;
+ }
+ return nResult;
+}
+
+static void equaliseNumberOfParagraph(std::vector<svx::ClassificationResult> const & rResults, uno::Reference<text::XText> const & xText)
+{
+ sal_Int32 nNumberOfParagraphs = 0;
+ for (svx::ClassificationResult const & rResult : rResults)
+ {
+ if (rResult.meType == svx::ClassificationType::PARAGRAPH)
+ nNumberOfParagraphs++;
+ }
+
+ while (getNumberOfParagraphs(xText) < nNumberOfParagraphs)
+ {
+ uno::Reference<text::XParagraphAppend> xParagraphAppend(xText, uno::UNO_QUERY);
+ xParagraphAppend->finishParagraph(uno::Sequence<beans::PropertyValue>());
+ }
+}
+
+void SwEditShell::ApplyAdvancedClassification(std::vector<svx::ClassificationResult> const & rResults)
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell)
+ return;
+
+ const SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if (!pObjSh)
+ return;
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
+
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = pObjSh->getDocProperties();
+
+ const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
+ const std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
+ for (const OUString& rPageStyleName : aUsedPageStyles)
+ {
+ uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
+
+ // HEADER
+ bool bHeaderIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+ uno::Reference<text::XText> xHeaderText;
+ if (bHeaderIsOn)
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+ if (xHeaderText.is())
+ removeAllClassificationFields(sPolicy, xHeaderText);
+
+ // FOOTER
+ bool bFooterIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
+ uno::Reference<text::XText> xFooterText;
+ if (bFooterIsOn)
+ xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
+ if (xFooterText.is())
+ removeAllClassificationFields(sPolicy, xFooterText);
+ }
+
+ // Clear properties
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ svx::classification::removeAllProperties(xPropertyContainer);
+
+ SfxClassificationHelper aHelper(xDocumentProperties);
+
+ // Apply properties from the BA policy
+ for (svx::ClassificationResult const & rResult : rResults)
+ {
+ if (rResult.meType == svx::ClassificationType::CATEGORY)
+ {
+ aHelper.SetBACName(rResult.msName, SfxClassificationHelper::getPolicyType());
+ }
+ }
+
+ sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
+
+ // Insert origin document property
+ svx::classification::insertCreationOrigin(xPropertyContainer, aCreator, sfx::ClassificationCreationOrigin::MANUAL);
+
+ // Insert full text as document property
+ svx::classification::insertFullTextualRepresentationAsDocumentProperty(xPropertyContainer, aCreator, rResults);
+
+ for (const OUString& rPageStyleName : aUsedPageStyles)
+ {
+ uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
+
+ // HEADER
+ bool bHeaderIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+ if (!bHeaderIsOn)
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::Any(true));
+ uno::Reference<text::XText> xHeaderText;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+ equaliseNumberOfParagraph(rResults, xHeaderText);
+
+ // FOOTER
+ bool bFooterIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
+ if (!bFooterIsOn)
+ xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::Any(true));
+ uno::Reference<text::XText> xFooterText;
+ xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
+ equaliseNumberOfParagraph(rResults, xFooterText);
+
+ // SET/DELETE WATERMARK
+ SfxWatermarkItem aWatermarkItem;
+ aWatermarkItem.SetText(aHelper.GetDocumentWatermark());
+ SetWatermark(aWatermarkItem);
+
+ uno::Reference<text::XParagraphCursor> xHeaderParagraphCursor(xHeaderText->createTextCursor(), uno::UNO_QUERY);
+ uno::Reference<text::XParagraphCursor> xFooterParagraphCursor(xFooterText->createTextCursor(), uno::UNO_QUERY);
+
+ sal_Int32 nParagraph = -1;
+
+ for (svx::ClassificationResult const & rResult : rResults)
+ {
+ switch(rResult.meType)
+ {
+ case svx::ClassificationType::TEXT:
+ {
+ OUString sKey = aCreator.makeNumberedTextKey();
+
+ svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
+ insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
+ insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
+ }
+ break;
+
+ case svx::ClassificationType::CATEGORY:
+ {
+ OUString sKey = aCreator.makeCategoryNameKey();
+ insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
+ insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
+ }
+ break;
+
+ case svx::ClassificationType::MARKING:
+ {
+ OUString sKey = aCreator.makeNumberedMarkingKey();
+ svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
+ insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
+ insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
+ }
+ break;
+
+ case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
+ {
+ OUString sKey = aCreator.makeNumberedIntellectualPropertyPartKey();
+ svx::classification::addOrInsertDocumentProperty(xPropertyContainer, sKey, rResult.msName);
+ insertFieldToDocument(xMultiServiceFactory, xHeaderText, xHeaderParagraphCursor, sKey);
+ insertFieldToDocument(xMultiServiceFactory, xFooterText, xFooterParagraphCursor, sKey);
+ }
+ break;
+
+ case svx::ClassificationType::PARAGRAPH:
+ {
+ nParagraph++;
+
+ if (nParagraph != 0) // only jump to next paragraph, if we aren't at the first paragraph
+ {
+ xHeaderParagraphCursor->gotoNextParagraph(false);
+ xFooterParagraphCursor->gotoNextParagraph(false);
+ }
+
+ xHeaderParagraphCursor->gotoStartOfParagraph(false);
+ xFooterParagraphCursor->gotoStartOfParagraph(false);
+
+ uno::Reference<beans::XPropertySet> xHeaderPropertySet(xHeaderParagraphCursor, uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xFooterPropertySet(xFooterParagraphCursor, uno::UNO_QUERY_THROW);
+ if (rResult.msName == "BOLD")
+ {
+ xHeaderPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::BOLD));
+ xFooterPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::BOLD));
+ }
+ else
+ {
+ xHeaderPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::NORMAL));
+ xFooterPropertySet->setPropertyValue("CharWeight", uno::Any(awt::FontWeight::NORMAL));
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+std::vector<svx::ClassificationResult> SwEditShell::CollectAdvancedClassification()
+{
+ std::vector<svx::ClassificationResult> aResult;
+
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell)
+ return aResult;
+
+ const SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if (!pObjSh)
+ return aResult;
+
+ const OUString sBlank;
+
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = pObjSh->getDocProperties();
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+ sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
+
+ std::vector<OUString> aPageStyles = lcl_getUsedPageStyles(this);
+ OUString aPageStyleString = aPageStyles.back();
+ uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(aPageStyleString), uno::UNO_QUERY);
+
+ bool bHeaderIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+ if (!bHeaderIsOn)
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aCreator.makeCategoryNameKey());
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
+
+ return aResult;
+ }
+
+ uno::Reference<text::XText> xHeaderText;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xHeaderText, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+
+ // set to true if category was found in the header
+ bool bFoundClassificationCategory = false;
+
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraphs->nextElement(), uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ continue;
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+
+ // Check font weight
+ uno::Reference<beans::XPropertySet> xParagraphPropertySet(xTextPortionEnumerationAccess, uno::UNO_QUERY_THROW);
+ uno::Any aAny = xParagraphPropertySet->getPropertyValue("CharWeight");
+
+ OUString sWeight = (aAny.get<float>() >= awt::FontWeight::BOLD) ? OUString("BOLD") : OUString("NORMAL");
+
+ aResult.push_back({ svx::ClassificationType::PARAGRAPH, sWeight, sBlank, sBlank });
+
+ // Process portions
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xTextField;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xTextField;
+ if (!xTextField->supportsService(DocInfoServiceName))
+ continue;
+
+ OUString aName;
+ uno::Reference<beans::XPropertySet> xPropertySet(xTextField, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue(UNO_NAME_NAME) >>= aName;
+
+ if (aCreator.isMarkingTextKey(aName))
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank });
+ }
+ else if (aCreator.isCategoryNameKey(aName))
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
+ bFoundClassificationCategory = true;
+ }
+ else if (aCreator.isCategoryIdentifierKey(aName))
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::CATEGORY, sBlank, sBlank, aValue });
+ bFoundClassificationCategory = true;
+ }
+ else if (aCreator.isMarkingKey(aName))
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank });
+ }
+ else if (aCreator.isIntellectualPropertyPartKey(aName))
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aName);
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, aValue, sBlank, sBlank });
+ }
+ }
+ }
+
+ if (!bFoundClassificationCategory)
+ {
+ const OUString aValue = svx::classification::getProperty(xPropertyContainer, aCreator.makeCategoryNameKey());
+ if (!aValue.isEmpty())
+ aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
+ }
+
+ return aResult;
+}
+
+void SwEditShell::SetClassification(const OUString& rName, SfxClassificationPolicyType eType)
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell)
+ return;
+
+ SfxClassificationHelper aHelper(pDocShell->getDocProperties());
+
+ const bool bHadWatermark = !aHelper.GetDocumentWatermark().isEmpty();
+
+ // This updates the infobar as well.
+ aHelper.SetBACName(rName, eType);
+
+ // Insert origin document property
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = pDocShell->getDocProperties()->getUserDefinedProperties();
+ sfx::ClassificationKeyCreator aCreator(SfxClassificationHelper::getPolicyType());
+ svx::classification::insertCreationOrigin(xPropertyContainer, aCreator, sfx::ClassificationCreationOrigin::BAF_POLICY);
+
+ bool bHeaderIsNeeded = aHelper.HasDocumentHeader();
+ bool bFooterIsNeeded = aHelper.HasDocumentFooter();
+ OUString aWatermark = aHelper.GetDocumentWatermark();
+ bool bWatermarkIsNeeded = !aWatermark.isEmpty();
+
+ if (!bHeaderIsNeeded && !bFooterIsNeeded && !bWatermarkIsNeeded && !bHadWatermark)
+ return;
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
+ const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
+
+ for (const OUString& rPageStyleName : aStyles)
+ {
+ uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
+
+ if (bHeaderIsNeeded || bWatermarkIsNeeded || bHadWatermark)
+ {
+ // If the header is off, turn it on.
+ bool bHeaderIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+ if (!bHeaderIsOn)
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::Any(true));
+
+ // If the header already contains a document header field, no need to do anything.
+ uno::Reference<text::XText> xHeaderText;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+
+ if (bHeaderIsNeeded)
+ {
+ if (!lcl_hasField(xHeaderText, DocInfoServiceName, OUStringConcatenation(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER())))
+ {
+ // Append a field to the end of the header text.
+ uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
+ xField->setPropertyValue(UNO_NAME_NAME, uno::Any(SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCHEADER()));
+ uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
+ xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, /*bAbsorb=*/false);
+ }
+ }
+
+ SfxWatermarkItem aWatermarkItem;
+ aWatermarkItem.SetText(aWatermark);
+ SetWatermark(aWatermarkItem);
+ }
+
+ if (bFooterIsNeeded)
+ {
+ // If the footer is off, turn it on.
+ bool bFooterIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_FOOTER_IS_ON) >>= bFooterIsOn;
+ if (!bFooterIsOn)
+ xPageStyle->setPropertyValue(UNO_NAME_FOOTER_IS_ON, uno::Any(true));
+
+ // If the footer already contains a document header field, no need to do anything.
+ uno::Reference<text::XText> xFooterText;
+ xPageStyle->getPropertyValue(UNO_NAME_FOOTER_TEXT) >>= xFooterText;
+ static OUString sFooter = SfxClassificationHelper::PROP_PREFIX_INTELLECTUALPROPERTY() + SfxClassificationHelper::PROP_DOCFOOTER();
+ if (!lcl_hasField(xFooterText, DocInfoServiceName, sFooter))
+ {
+ // Append a field to the end of the footer text.
+ uno::Reference<beans::XPropertySet> xField(xMultiServiceFactory->createInstance(DocInfoServiceName), uno::UNO_QUERY);
+ xField->setPropertyValue(UNO_NAME_NAME, uno::Any(sFooter));
+ uno::Reference<text::XTextContent> xTextContent(xField, uno::UNO_QUERY);
+ xFooterText->insertTextContent(xFooterText->getEnd(), xTextContent, /*bAbsorb=*/false);
+ }
+ }
+ }
+}
+
+// We pass xParent and xNodeSubject even though they point to the same thing because the UNO_QUERY is
+// on a performance-sensitive path.
+static void lcl_ApplyParagraphClassification(SwDoc* pDoc,
+ const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<text::XTextContent>& xParent,
+ const css::uno::Reference<css::rdf::XResource>& xNodeSubject,
+ std::vector<svx::ClassificationResult> aResults)
+{
+ if (!xNodeSubject.is())
+ return;
+
+ // Remove all paragraph classification fields.
+ for (;;)
+ {
+ uno::Reference<text::XTextField> xTextField = lcl_FindParagraphClassificationField(xModel, xParent);
+ if (!xTextField.is())
+ break;
+ lcl_RemoveParagraphMetadataField(xTextField);
+ }
+
+ if (aResults.empty())
+ return;
+
+ // Since we always insert at the start of the paragraph,
+ // need to insert in reverse order.
+ std::reverse(aResults.begin(), aResults.end());
+ // Ignore "PARAGRAPH" types
+ aResults.erase(std::remove_if(aResults.begin(),
+ aResults.end(),
+ [](const svx::ClassificationResult& rResult)-> bool
+ { return rResult.meType == svx::ClassificationType::PARAGRAPH; }),
+ aResults.end());
+
+ sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+ std::vector<OUString> aFieldNames;
+ for (size_t nIndex = 0; nIndex < aResults.size(); ++nIndex)
+ {
+ const svx::ClassificationResult& rResult = aResults[nIndex];
+
+ const bool isLast = nIndex == 0;
+ const bool isFirst = (nIndex == aResults.size() - 1);
+ OUString sKey;
+ OUString sValue = rResult.msName;
+ switch (rResult.meType)
+ {
+ case svx::ClassificationType::TEXT:
+ {
+ sKey = aKeyCreator.makeNumberedTextKey();
+ }
+ break;
+
+ case svx::ClassificationType::CATEGORY:
+ {
+ if (rResult.msIdentifier.isEmpty())
+ {
+ sKey = aKeyCreator.makeCategoryNameKey();
+ }
+ else
+ {
+ sValue = rResult.msIdentifier;
+ sKey = aKeyCreator.makeCategoryIdentifierKey();
+ }
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, ParagraphClassificationAbbrRDFName, rResult.msAbbreviatedName);
+ }
+ break;
+
+ case svx::ClassificationType::MARKING:
+ {
+ sKey = aKeyCreator.makeNumberedMarkingKey();
+ }
+ break;
+
+ case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
+ {
+ sKey = aKeyCreator.makeNumberedIntellectualPropertyPartKey();
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ OUString sDisplayText = (isFirst ? ("(" + rResult.msAbbreviatedName) : rResult.msAbbreviatedName);
+ if (isLast)
+ sDisplayText += ")";
+ lcl_UpdateParagraphClassificationField(pDoc, xModel, xParent, sKey, sValue, sDisplayText);
+ aFieldNames.emplace_back(sKey);
+ }
+
+ // Correct the order
+ std::reverse(aFieldNames.begin(), aFieldNames.end());
+ OUStringBuffer sFieldNames;
+ bool first = true;
+ for (const OUString& rFieldName : aFieldNames)
+ {
+ if (!first)
+ sFieldNames.append("/");
+ sFieldNames.append(rFieldName);
+ first = false;
+ }
+
+ const OUString sOldFieldNames = lcl_getRDF(xModel, xNodeSubject, ParagraphClassificationFieldNamesRDFName).second;
+ SwRDFHelper::removeStatement(xModel, MetaNS, xNodeSubject, ParagraphClassificationFieldNamesRDFName, sOldFieldNames);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xNodeSubject, ParagraphClassificationFieldNamesRDFName, sFieldNames.makeStringAndClear());
+}
+
+void SwEditShell::ApplyParagraphClassification(std::vector<svx::ClassificationResult> aResults)
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell || !GetCursor() || !GetCursor()->Start())
+ return;
+
+ SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
+ if (pNode == nullptr)
+ return;
+
+ // Prevent recursive validation since this is triggered on node updates, which we do below.
+ const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([this, bOldValidationFlag]() {
+ SetParagraphSignatureValidation(bOldValidationFlag);
+ });
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
+ lcl_ApplyParagraphClassification(GetDoc(), xModel, xParent, css::uno::Reference<css::rdf::XResource>(xParent, uno::UNO_QUERY), std::move(aResults));
+}
+
+static std::vector<svx::ClassificationResult> lcl_CollectParagraphClassification(const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
+{
+ std::vector<svx::ClassificationResult> aResult;
+
+ uno::Reference<container::XEnumerationAccess> xTextPortionEnumerationAccess(xParagraph, uno::UNO_QUERY);
+ if (!xTextPortionEnumerationAccess.is())
+ return aResult;
+
+ uno::Reference<container::XEnumeration> xTextPortions = xTextPortionEnumerationAccess->createEnumeration();
+
+ const sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+
+ while (xTextPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xTextPortion(xTextPortions->nextElement(), uno::UNO_QUERY);
+ OUString aTextPortionType;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_PORTION_TYPE) >>= aTextPortionType;
+ if (aTextPortionType != UNO_NAME_TEXT_FIELD)
+ continue;
+
+ uno::Reference<lang::XServiceInfo> xField;
+ xTextPortion->getPropertyValue(UNO_NAME_TEXT_FIELD) >>= xField;
+ if (!xField->supportsService(MetadataFieldServiceName))
+ continue;
+
+ uno::Reference<text::XTextField> xTextField(xField, uno::UNO_QUERY);
+ const OUString sPolicy = SfxClassificationHelper::policyTypeToString(SfxClassificationHelper::getPolicyType());
+ const std::pair<OUString, OUString> rdfNamePair = lcl_getFieldRDFByPrefix(xModel, xTextField, sPolicy);
+
+ uno::Reference<text::XTextRange> xTextRange(xField, uno::UNO_QUERY);
+ const OUString aName = rdfNamePair.first;
+ const OUString aValue = rdfNamePair.second;
+ static const OUStringLiteral sBlank(u"");
+ if (aKeyCreator.isMarkingTextKey(aName))
+ {
+ aResult.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank });
+ }
+ else if (aKeyCreator.isCategoryNameKey(aName))
+ {
+ aResult.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank });
+ }
+ else if (aKeyCreator.isCategoryIdentifierKey(aName))
+ {
+ aResult.push_back({ svx::ClassificationType::CATEGORY, sBlank, sBlank, aValue });
+ }
+ else if (aKeyCreator.isMarkingKey(aName))
+ {
+ aResult.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank });
+ }
+ else if (aKeyCreator.isIntellectualPropertyPartKey(aName))
+ {
+ aResult.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, xTextRange->getString(), sBlank, sBlank });
+ }
+ }
+
+ return aResult;
+}
+
+std::vector<svx::ClassificationResult> SwEditShell::CollectParagraphClassification()
+{
+ std::vector<svx::ClassificationResult> aResult;
+
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell || !GetCursor() || !GetCursor()->Start())
+ return aResult;
+
+ SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
+ if (pNode == nullptr)
+ return aResult;
+
+ uno::Reference<text::XTextContent> xParent = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ return lcl_CollectParagraphClassification(xModel, xParent);
+}
+
+static sal_Int16 lcl_GetAngle(const drawing::HomogenMatrix3& rMatrix)
+{
+ basegfx::B2DHomMatrix aTransformation;
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate = 0;
+ double fShear = 0;
+
+ aTransformation.set(0, 0, rMatrix.Line1.Column1);
+ aTransformation.set(0, 1, rMatrix.Line1.Column2);
+ aTransformation.set(0, 2, rMatrix.Line1.Column3);
+ aTransformation.set(1, 0, rMatrix.Line2.Column1);
+ aTransformation.set(1, 1, rMatrix.Line2.Column2);
+ aTransformation.set(1, 2, rMatrix.Line2.Column3);
+ aTransformation.set(2, 0, rMatrix.Line3.Column1);
+ aTransformation.set(2, 1, rMatrix.Line3.Column2);
+ aTransformation.set(2, 2, rMatrix.Line3.Column3);
+
+ aTransformation.decompose(aScale, aTranslate, fRotate, fShear);
+ sal_Int16 nDeg = round(basegfx::rad2deg(fRotate));
+ return nDeg < 0 ? round(nDeg) * -1 : round(360.0 - nDeg);
+}
+
+SfxWatermarkItem SwEditShell::GetWatermark() const
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell)
+ return SfxWatermarkItem();
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
+ std::vector<OUString> aUsedPageStyles = lcl_getUsedPageStyles(this);
+ for (const OUString& rPageStyleName : aUsedPageStyles)
+ {
+ uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
+
+ bool bHeaderIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+ if (!bHeaderIsOn)
+ return SfxWatermarkItem();
+
+ uno::Reference<text::XText> xHeaderText;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+
+ OUString sWatermark = "";
+ bool bSuccess = false;
+ uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, "com.sun.star.drawing.CustomShape", sWatermark, bSuccess);
+
+ if (xWatermark.is())
+ {
+ SfxWatermarkItem aItem;
+ uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
+ Color nColor;
+ sal_Int16 nTransparency;
+ OUString aFont;
+ drawing::HomogenMatrix3 aMatrix;
+
+ aItem.SetText(xTextRange->getString());
+
+ if (xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont)
+ aItem.SetFont(aFont);
+ if (xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor)
+ aItem.SetColor(nColor);
+ if (xPropertySet->getPropertyValue("Transformation") >>= aMatrix)
+ aItem.SetAngle(lcl_GetAngle(aMatrix));
+ if (xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency)
+ aItem.SetTransparency(nTransparency);
+
+ return aItem;
+ }
+ }
+ return SfxWatermarkItem();
+}
+
+static void lcl_placeWatermarkInHeader(const SfxWatermarkItem& rWatermark,
+ const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<beans::XPropertySet>& xPageStyle,
+ const uno::Reference<text::XText>& xHeaderText)
+{
+ if (!xHeaderText.is())
+ return;
+
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
+ OUString aShapeServiceName = "com.sun.star.drawing.CustomShape";
+ OUString sWatermark = WATERMARK_NAME;
+ bool bSuccess = false;
+ uno::Reference<drawing::XShape> xWatermark = lcl_getWatermark(xHeaderText, aShapeServiceName, sWatermark, bSuccess);
+
+ bool bDeleteWatermark = rWatermark.GetText().isEmpty();
+ if (xWatermark.is())
+ {
+ drawing::HomogenMatrix3 aMatrix;
+ Color nColor = 0xc0c0c0;
+ sal_Int16 nTransparency = 50;
+ sal_Int16 nAngle = 45;
+ OUString aFont = "";
+
+ uno::Reference<beans::XPropertySet> xPropertySet(xWatermark, uno::UNO_QUERY);
+ xPropertySet->getPropertyValue(UNO_NAME_CHAR_FONT_NAME) >>= aFont;
+ xPropertySet->getPropertyValue(UNO_NAME_FILLCOLOR) >>= nColor;
+ xPropertySet->getPropertyValue(UNO_NAME_FILL_TRANSPARENCE) >>= nTransparency;
+ xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
+ nAngle = lcl_GetAngle(aMatrix);
+
+ // If the header already contains a watermark, see if it its text is up to date.
+ uno::Reference<text::XTextRange> xTextRange(xWatermark, uno::UNO_QUERY);
+ if (xTextRange->getString() != rWatermark.GetText()
+ || aFont != rWatermark.GetFont()
+ || nColor != rWatermark.GetColor()
+ || nAngle != rWatermark.GetAngle()
+ || nTransparency != rWatermark.GetTransparency()
+ || bDeleteWatermark)
+ {
+ // No: delete it and we'll insert a replacement.
+ uno::Reference<lang::XComponent> xComponent(xWatermark, uno::UNO_QUERY);
+ xComponent->dispose();
+ xWatermark.clear();
+ }
+ }
+
+ if (!bSuccess || xWatermark.is() || bDeleteWatermark)
+ return;
+
+ const OUString& sFont = rWatermark.GetFont();
+ sal_Int16 nAngle = rWatermark.GetAngle();
+ sal_Int16 nTransparency = rWatermark.GetTransparency();
+ Color nColor = rWatermark.GetColor();
+
+ // Calc the ratio.
+ double fRatio = 0;
+
+ ScopedVclPtrInstance<VirtualDevice> pDevice;
+ vcl::Font aFont = pDevice->GetFont();
+ aFont.SetFamilyName(sFont);
+ aFont.SetFontSize(Size(0, 96));
+ pDevice->SetFont(aFont);
+
+ auto nTextWidth = pDevice->GetTextWidth(rWatermark.GetText());
+ if (nTextWidth)
+ {
+ fRatio = pDevice->GetTextHeight();
+ fRatio /= nTextWidth;
+ }
+
+ // Calc the size.
+ sal_Int32 nWidth = 0;
+ awt::Size aSize;
+ xPageStyle->getPropertyValue(UNO_NAME_SIZE) >>= aSize;
+ if (aSize.Width < aSize.Height)
+ {
+ // Portrait.
+ sal_Int32 nLeftMargin = 0;
+ xPageStyle->getPropertyValue(UNO_NAME_LEFT_MARGIN) >>= nLeftMargin;
+ sal_Int32 nRightMargin = 0;
+ xPageStyle->getPropertyValue(UNO_NAME_RIGHT_MARGIN) >>= nRightMargin;
+ nWidth = aSize.Width - nLeftMargin - nRightMargin;
+ }
+ else
+ {
+ // Landscape.
+ sal_Int32 nTopMargin = 0;
+ xPageStyle->getPropertyValue(UNO_NAME_TOP_MARGIN) >>= nTopMargin;
+ sal_Int32 nBottomMargin = 0;
+ xPageStyle->getPropertyValue(UNO_NAME_BOTTOM_MARGIN) >>= nBottomMargin;
+ nWidth = aSize.Height - nTopMargin - nBottomMargin;
+ }
+ sal_Int32 nHeight = fRatio * nWidth;
+
+ // Create and insert the shape.
+ uno::Reference<drawing::XShape> xShape(xMultiServiceFactory->createInstance(aShapeServiceName), uno::UNO_QUERY);
+
+ uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY);
+ xNamed->setName(sWatermark);
+
+ basegfx::B2DHomMatrix aTransformation;
+ aTransformation.identity();
+ aTransformation.scale(nWidth, nHeight);
+ aTransformation.rotate(-basegfx::deg2rad(nAngle));
+ drawing::HomogenMatrix3 aMatrix;
+ aMatrix.Line1.Column1 = aTransformation.get(0, 0);
+ aMatrix.Line1.Column2 = aTransformation.get(0, 1);
+ aMatrix.Line1.Column3 = aTransformation.get(0, 2);
+ aMatrix.Line2.Column1 = aTransformation.get(1, 0);
+ aMatrix.Line2.Column2 = aTransformation.get(1, 1);
+ aMatrix.Line2.Column3 = aTransformation.get(1, 2);
+ aMatrix.Line3.Column1 = aTransformation.get(2, 0);
+ aMatrix.Line3.Column2 = aTransformation.get(2, 1);
+ aMatrix.Line3.Column3 = aTransformation.get(2, 2);
+ uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
+ xPropertySet->setPropertyValue(UNO_NAME_ANCHOR_TYPE, uno::Any(text::TextContentAnchorType_AT_CHARACTER));
+ uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
+ xHeaderText->insertTextContent(xHeaderText->getEnd(), xTextContent, false);
+
+ // The remaining properties have to be set after the shape is inserted: do that in one batch to avoid flickering.
+ uno::Reference<document::XActionLockable> xLockable(xShape, uno::UNO_QUERY);
+ xLockable->addActionLock();
+ xPropertySet->setPropertyValue(UNO_NAME_FILLCOLOR, uno::Any(static_cast<sal_Int32>(nColor)));
+ xPropertySet->setPropertyValue(UNO_NAME_FILLSTYLE, uno::Any(drawing::FillStyle_SOLID));
+ xPropertySet->setPropertyValue(UNO_NAME_FILL_TRANSPARENCE, uno::Any(nTransparency));
+ xPropertySet->setPropertyValue(UNO_NAME_LINESTYLE, uno::Any(drawing::LineStyle_NONE));
+ xPropertySet->setPropertyValue(UNO_NAME_OPAQUE, uno::Any(false));
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWHEIGHT, uno::Any(false));
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_AUTOGROWWIDTH, uno::Any(false));
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEHEIGHT, uno::Any(nHeight));
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_MINFRAMEWIDTH, uno::Any(nWidth));
+ xPropertySet->setPropertyValue(UNO_NAME_TEXT_WRAP, uno::Any(text::WrapTextMode_THROUGH));
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT_RELATION, uno::Any(text::RelOrientation::PAGE_PRINT_AREA));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT_RELATION, uno::Any(text::RelOrientation::PAGE_PRINT_AREA));
+ xPropertySet->setPropertyValue(UNO_NAME_CHAR_FONT_NAME, uno::Any(sFont));
+ xPropertySet->setPropertyValue(UNO_NAME_CHAR_HEIGHT, uno::Any(WATERMARK_AUTO_SIZE));
+ xPropertySet->setPropertyValue("Transformation", uno::Any(aMatrix));
+
+ uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY);
+ xTextRange->setString(rWatermark.GetText());
+
+ uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY);
+ xDefaulter->createCustomShapeDefaults("fontwork-plain-text");
+
+ auto aGeomPropSeq = xPropertySet->getPropertyValue("CustomShapeGeometry").get< uno::Sequence<beans::PropertyValue> >();
+ auto aGeomPropVec = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aGeomPropSeq);
+ uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence(
+ {
+ {"TextPath", uno::Any(true)},
+ }));
+ auto it = std::find_if(aGeomPropVec.begin(), aGeomPropVec.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "TextPath";
+ });
+ if (it == aGeomPropVec.end())
+ aGeomPropVec.push_back(comphelper::makePropertyValue("TextPath", aPropertyValues));
+ else
+ it->Value <<= aPropertyValues;
+ xPropertySet->setPropertyValue("CustomShapeGeometry", uno::Any(comphelper::containerToSequence(aGeomPropVec)));
+
+ // tdf#108494, tdf#109313 the header height was switched to height of a watermark
+ // and shape was moved to the lower part of a page, force position update
+ xPropertySet->getPropertyValue("Transformation") >>= aMatrix;
+ xPropertySet->setPropertyValue("Transformation", uno::Any(aMatrix));
+
+ xPropertySet->setPropertyValue(UNO_NAME_HORI_ORIENT, uno::Any(text::HoriOrientation::CENTER));
+ xPropertySet->setPropertyValue(UNO_NAME_VERT_ORIENT, uno::Any(text::VertOrientation::CENTER));
+
+ xLockable->removeActionLock();
+}
+
+void SwEditShell::SetWatermark(const SfxWatermarkItem& rWatermark)
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell)
+ return;
+ const bool bNoWatermark = rWatermark.GetText().isEmpty();
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(xModel, uno::UNO_QUERY);
+ uno::Reference<container::XNameAccess> xStyleFamilies = xStyleFamiliesSupplier->getStyleFamilies();
+ uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName("PageStyles"), uno::UNO_QUERY);
+ const uno::Sequence<OUString> aStyles = xStyleFamily->getElementNames();
+
+ for (const OUString& rPageStyleName : aStyles)
+ {
+ uno::Reference<beans::XPropertySet> xPageStyle(xStyleFamily->getByName(rPageStyleName), uno::UNO_QUERY);
+
+ // If the header is off, turn it on.
+ bool bHeaderIsOn = false;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_ON) >>= bHeaderIsOn;
+ if (!bHeaderIsOn)
+ {
+ if (bNoWatermark)
+ continue; // the style doesn't have any watermark - no need to do anything
+
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_ON, uno::Any(true));
+ }
+
+ // backup header height
+ bool bDynamicHeight = true;
+ sal_Int32 nOldValue;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_HEIGHT) >>= nOldValue;
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT) >>= bDynamicHeight;
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(false));
+
+ // If the header already contains a document header field, no need to do anything.
+ uno::Reference<text::XText> xHeaderText;
+ uno::Reference<text::XText> xHeaderTextFirst;
+ uno::Reference<text::XText> xHeaderTextLeft;
+ uno::Reference<text::XText> xHeaderTextRight;
+
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT) >>= xHeaderText;
+ lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderText);
+
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_FIRST) >>= xHeaderTextFirst;
+ lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextFirst);
+
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_LEFT) >>= xHeaderTextLeft;
+ lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextLeft);
+
+ xPageStyle->getPropertyValue(UNO_NAME_HEADER_TEXT_RIGHT) >>= xHeaderTextRight;
+ lcl_placeWatermarkInHeader(rWatermark, xModel, xPageStyle, xHeaderTextRight);
+
+ // tdf#108494 the header height was switched to height of a watermark
+ // and shape was moved to the lower part of a page
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::Any(sal_Int32(11)));
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_HEIGHT, uno::Any(nOldValue));
+ xPageStyle->setPropertyValue(UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(bDynamicHeight));
+ }
+}
+
+SwUndoParagraphSigning::SwUndoParagraphSigning(SwDoc& rDoc,
+ const uno::Reference<text::XTextField>& xField,
+ const uno::Reference<text::XTextContent>& xParent,
+ const bool bRemove)
+ : SwUndo(SwUndoId::PARA_SIGN_ADD, &rDoc),
+ m_rDoc(rDoc),
+ m_xField(xField),
+ m_xParent(xParent),
+ m_bRemove(bRemove)
+{
+ // Save the metadata and field content to undo/redo.
+ uno::Reference<frame::XModel> xModel = m_rDoc.GetDocShell()->GetBaseModel();
+ const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, m_xField);
+ const auto it = aStatements.find(ParagraphSignatureIdRDFName);
+ if (it != aStatements.end())
+ m_signature = it->second;
+
+ const auto it2 = aStatements.find(ParagraphSignatureUsageRDFName);
+ if (it2 != aStatements.end())
+ m_usage = it2->second;
+
+ uno::Reference<css::text::XTextRange> xText(m_xField, uno::UNO_QUERY);
+ m_display = xText->getString();
+}
+
+void SwUndoParagraphSigning::UndoImpl(::sw::UndoRedoContext&)
+{
+ if (m_bRemove)
+ Remove();
+ else
+ Insert();
+}
+
+void SwUndoParagraphSigning::RedoImpl(::sw::UndoRedoContext&)
+{
+ if (m_bRemove)
+ Insert();
+ else
+ Remove();
+}
+
+void SwUndoParagraphSigning::RepeatImpl(::sw::RepeatContext&)
+{
+}
+
+void SwUndoParagraphSigning::Insert()
+{
+ // Disable undo to avoid introducing noise when we edit the metadata field.
+ const bool isUndoEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
+
+ // Prevent validation since this will trigger a premature validation
+ // upon inserting, but before setting the metadata.
+ SwEditShell* pEditSh = m_rDoc.GetEditShell();
+ const bool bOldValidationFlag = pEditSh && pEditSh->SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([&] () {
+ if (pEditSh)
+ pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
+ });
+
+ m_xField = lcl_InsertParagraphSignature(m_rDoc.GetDocShell()->GetBaseModel(), m_xParent, m_signature, m_usage);
+ lcl_DoUpdateParagraphSignatureField(m_rDoc, m_xField, m_display);
+}
+
+void SwUndoParagraphSigning::Remove()
+{
+ // Disable undo to avoid introducing noise when we edit the metadata field.
+ const bool isUndoEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
+
+ // Prevent validation since this will trigger a premature validation
+ // upon removing.
+ SwEditShell* pEditSh = m_rDoc.GetEditShell();
+ const bool bOldValidationFlag = pEditSh && pEditSh->SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([&] () {
+ if (pEditSh)
+ pEditSh->SetParagraphSignatureValidation(bOldValidationFlag);
+ m_rDoc.GetIDocumentUndoRedo().DoUndo(isUndoEnabled);
+ });
+
+ lcl_RemoveParagraphMetadataField(m_xField);
+}
+
+void SwEditShell::SignParagraph()
+{
+ SwDoc& rDoc = *GetDoc();
+ SwDocShell* pDocShell = rDoc.GetDocShell();
+ if (!pDocShell || !GetCursor() || !GetCursor()->Start())
+ return;
+ const SwPosition* pPosStart = GetCursor()->Start();
+ if (!pPosStart)
+ return;
+ SwTextNode* pNode = pPosStart->nNode.GetNode().GetTextNode();
+ if (!pNode)
+ return;
+
+ // Table text signing is not supported.
+ if (pNode->FindTableNode() != nullptr)
+ return;
+
+ // 1. Get the text (without fields).
+ const uno::Reference<text::XTextContent> xParagraph = SwXParagraph::CreateXParagraph(pNode->GetDoc(), pNode);
+ const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
+ if (utf8Text.isEmpty())
+ return;
+
+ // 2. Get certificate.
+ uno::Reference<security::XDocumentDigitalSignatures> xSigner(
+ // here none of the version-dependent methods are called
+ security::DocumentDigitalSignatures::createDefault(
+ comphelper::getProcessComponentContext()));
+
+ uno::Sequence<css::beans::PropertyValue> aProperties;
+ uno::Reference<security::XCertificate> xCertificate = xSigner->chooseCertificateWithProps(aProperties);
+ if (!xCertificate.is())
+ return;
+
+ // 3. Sign it.
+ svl::crypto::Signing signing(xCertificate);
+ signing.AddDataRange(utf8Text.getStr(), utf8Text.getLength());
+ OStringBuffer sigBuf;
+ if (!signing.Sign(sigBuf))
+ return;
+
+ const OUString signature = OStringToOUString(sigBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8, 0);
+
+ auto it = std::find_if(std::as_const(aProperties).begin(), std::as_const(aProperties).end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "Usage";
+ });
+
+ OUString aUsage;
+ if (it != std::as_const(aProperties).end())
+ it->Value >>= aUsage;
+
+ // 4. Add metadata
+ // Prevent validation since this will trigger a premature validation
+ // upon inserting, but before setting the metadata.
+ const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
+ SetParagraphSignatureValidation(bOldValidationFlag);
+ });
+
+ rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
+
+ const uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ uno::Reference<css::text::XTextField> xField = lcl_InsertParagraphSignature(xModel, xParagraph, signature, aUsage);
+
+ lcl_UpdateParagraphSignatureField(*GetDoc(), xModel, xParagraph, xField, utf8Text);
+
+ rDoc.GetIDocumentUndoRedo().AppendUndo(
+ std::make_unique<SwUndoParagraphSigning>(rDoc, xField, xParagraph, true));
+
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::PARA_SIGN_ADD, nullptr);
+}
+
+void SwEditShell::ValidateParagraphSignatures(SwTextNode* pNode, bool updateDontRemove)
+{
+ if (!pNode || !IsParagraphSignatureValidationEnabled())
+ return;
+
+ // Table text signing is not supported.
+ if (pNode->FindTableNode() != nullptr)
+ return;
+
+ // Prevent recursive validation since this is triggered on node updates, which we do below.
+ const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
+ SetParagraphSignatureValidation(bOldValidationFlag);
+ });
+
+ uno::Reference<text::XTextContent> xParentText = SwXParagraph::CreateXParagraph(*GetDoc(), pNode);
+ lcl_ValidateParagraphSignatures(*GetDoc(), xParentText, updateDontRemove);
+}
+
+void SwEditShell::ValidateCurrentParagraphSignatures(bool updateDontRemove)
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell || !GetCursor() || !GetCursor()->Start() || !IsParagraphSignatureValidationEnabled())
+ return;
+
+ SwPaM* pPaM = GetCursor();
+ const SwPosition* pPosStart = pPaM->Start();
+ SwTextNode* pNode = pPosStart->nNode.GetNode().GetTextNode();
+ ValidateParagraphSignatures(pNode, updateDontRemove);
+}
+
+void SwEditShell::ValidateAllParagraphSignatures(bool updateDontRemove)
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell || !IsParagraphSignatureValidationEnabled())
+ return;
+
+ // Prevent recursive validation since this is triggered on node updates, which we do below.
+ const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
+ SetParagraphSignatureValidation(bOldValidationFlag);
+ });
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ const uno::Reference<text::XTextDocument> xDoc(xModel, uno::UNO_QUERY);
+ uno::Reference<text::XText> xParent = xDoc->getText();
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
+ if (!xParagraphEnumerationAccess.is())
+ return;
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+ if (!xParagraphs.is())
+ return;
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
+ lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph, updateDontRemove);
+ }
+}
+
+static uno::Reference<text::XTextField> lcl_GetParagraphMetadataFieldAtIndex(const SwDocShell* pDocSh, SwTextNode const * pNode, const sal_uLong index)
+{
+ uno::Reference<text::XTextField> xTextField;
+ if (pNode != nullptr && pDocSh != nullptr)
+ {
+ SwTextAttr* pAttr = pNode->GetTextAttrAt(index, RES_TXTATR_METAFIELD);
+ SwTextMeta* pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
+ if (pTextMeta != nullptr)
+ {
+ SwFormatMeta& rFormatMeta(static_cast<SwFormatMeta&>(pTextMeta->GetAttr()));
+ if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
+ {
+ const css::uno::Reference<css::rdf::XResource> xSubject = pMeta->MakeUnoObject();
+ uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
+ const std::map<OUString, OUString> aStatements = lcl_getRDFStatements(xModel, xSubject);
+ if (aStatements.find(ParagraphSignatureIdRDFName) != aStatements.end() ||
+ aStatements.find(ParagraphClassificationNameRDFName) != aStatements.end())
+ {
+ xTextField = uno::Reference<text::XTextField>(xSubject, uno::UNO_QUERY);
+ }
+ }
+ }
+ }
+
+ return xTextField;
+}
+
+void SwEditShell::RestoreMetadataFieldsAndValidateParagraphSignatures()
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell || !IsParagraphSignatureValidationEnabled())
+ return;
+
+ // Prevent recursive validation since this is triggered on node updates, which we do below.
+ const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([this, bOldValidationFlag] () {
+ SetParagraphSignatureValidation(bOldValidationFlag);
+ });
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ const uno::Reference<text::XTextDocument> xDoc(xModel, uno::UNO_QUERY);
+ uno::Reference<text::XText> xParent = xDoc->getText();
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
+ if (!xParagraphEnumerationAccess.is())
+ return;
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+ if (!xParagraphs.is())
+ return;
+
+ static constexpr OUStringLiteral sBlank(u"");
+ const sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+ const css::uno::Sequence<css::uno::Reference<rdf::XURI>> aGraphNames = SwRDFHelper::getGraphNames(xModel, MetaNS);
+
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
+
+ try
+ {
+ const css::uno::Reference<css::rdf::XResource> xSubject(xParagraph, uno::UNO_QUERY);
+ const std::map<OUString, OUString> aStatements = SwRDFHelper::getStatements(xModel, aGraphNames, xSubject);
+
+ const auto it = aStatements.find(ParagraphClassificationFieldNamesRDFName);
+ const OUString sFieldNames = (it != aStatements.end() ? it->second : sBlank);
+ std::vector<svx::ClassificationResult> aResults;
+ if (!sFieldNames.isEmpty())
+ {
+ assert(it != aStatements.end() && "can only be non-empty if it was valid");
+ // Order the fields
+ sal_Int32 nIndex = 0;
+ do
+ {
+ const OUString sCurFieldName = sFieldNames.getToken(0, '/', nIndex);
+ if (sCurFieldName.isEmpty())
+ break;
+
+ const auto it2 = aStatements.find(sCurFieldName);
+ bool bStatementFound = it2 != aStatements.end();
+ const OUString sName = bStatementFound ? it->first : sBlank;
+ const OUString sValue = bStatementFound ? it->second : sBlank;
+
+ if (aKeyCreator.isMarkingTextKey(sName))
+ {
+ aResults.push_back({ svx::ClassificationType::TEXT, sValue, sValue, sBlank });
+ }
+ else if (aKeyCreator.isCategoryNameKey(sName))
+ {
+ const auto it3 = aStatements.find(ParagraphClassificationAbbrRDFName);
+ const OUString sAbbreviatedName = (it3 != aStatements.end() && !it3->second.isEmpty() ? it3->second : sValue);
+ aResults.push_back({ svx::ClassificationType::CATEGORY, sValue, sAbbreviatedName, sBlank });
+ }
+ else if (aKeyCreator.isCategoryIdentifierKey(sName))
+ {
+ const auto it3 = aStatements.find(ParagraphClassificationAbbrRDFName);
+ const OUString sAbbreviatedName = (it3 != aStatements.end() && !it3->second.isEmpty() ? it3->second : sValue);
+ aResults.push_back({ svx::ClassificationType::CATEGORY, sBlank, sAbbreviatedName, sValue });
+ }
+ else if (aKeyCreator.isMarkingKey(sName))
+ {
+ aResults.push_back({ svx::ClassificationType::MARKING, sValue, sValue, sBlank });
+ }
+ else if (aKeyCreator.isIntellectualPropertyPartKey(sName))
+ {
+ aResults.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, sValue, sValue, sBlank });
+ }
+ }
+ while (nIndex >= 0);
+ }
+
+ // Update classification based on results.
+ lcl_ApplyParagraphClassification(GetDoc(), xModel, xParagraph, xSubject, aResults);
+
+ // Get Signatures
+ std::map<OUString, SignatureDescr> aSignatures;
+ for (const auto& pair : lcl_getRDFStatements(xModel, xParagraph))
+ {
+ const OUString& sName = pair.first;
+ if (sName.startsWith(ParagraphSignatureRDFNamespace))
+ {
+ const OUString sSuffix = sName.copy(ParagraphSignatureRDFNamespace.getLength());
+ const sal_Int32 index = sSuffix.indexOf(":");
+ if (index >= 0)
+ {
+ const OUString id = sSuffix.copy(0, index);
+ const OUString type = sSuffix.copy(index);
+ const OUString& sValue = pair.second;
+ if (type == ParagraphSignatureDateRDFName)
+ aSignatures[id].msDate = sValue;
+ else if (type == ParagraphSignatureUsageRDFName)
+ aSignatures[id].msUsage = sValue;
+ else if (type == ParagraphSignatureDigestRDFName)
+ aSignatures[id].msSignature = sValue;
+ }
+ }
+ }
+
+ for (const auto& pair : aSignatures)
+ {
+ uno::Reference<text::XTextField> xField = lcl_findFieldByRDF(xModel, xParagraph, ParagraphSignatureIdRDFName, pair.first);
+ if (!xField.is())
+ {
+ uno::Reference<lang::XMultiServiceFactory> xMultiServiceFactory(xModel, uno::UNO_QUERY);
+ xField = uno::Reference<text::XTextField>(xMultiServiceFactory->createInstance(MetadataFieldServiceName), uno::UNO_QUERY);
+
+ // Add the signature at the end.
+ xField->attach(xParagraph->getAnchor()->getEnd());
+
+ const css::uno::Reference<css::rdf::XResource> xFieldSubject(xField, uno::UNO_QUERY);
+ SwRDFHelper::addStatement(xModel, MetaNS, MetaFilename, xFieldSubject, ParagraphSignatureIdRDFName, pair.first);
+
+ const OString utf8Text = lcl_getParagraphBodyText(xParagraph);
+ lcl_UpdateParagraphSignatureField(*GetDoc(), xModel, xParagraph, xField, utf8Text);
+ }
+ }
+
+ lcl_ValidateParagraphSignatures(*GetDoc(), xParagraph, true); // Validate and Update signatures.
+ }
+ catch (const std::exception&)
+ {
+ }
+ }
+}
+
+bool SwEditShell::IsCursorInParagraphMetadataField() const
+{
+ if (GetCursor() && GetCursor()->Start())
+ {
+ SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
+ const sal_uLong index = GetCursor()->Start()->nContent.GetIndex();
+ uno::Reference<text::XTextField> xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
+ return xField.is();
+ }
+
+ return false;
+}
+
+bool SwEditShell::RemoveParagraphMetadataFieldAtCursor()
+{
+ if (GetCursor() && GetCursor()->Start())
+ {
+ SwTextNode* pNode = GetCursor()->Start()->nNode.GetNode().GetTextNode();
+ sal_uLong index = GetCursor()->Start()->nContent.GetIndex();
+ uno::Reference<text::XTextField> xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
+ if (!xField.is())
+ {
+ // Try moving the cursor to see if we're _facing_ a metafield or not,
+ // as opposed to being within one.
+ index--; // Backspace moves left
+
+ xField = lcl_GetParagraphMetadataFieldAtIndex(GetDoc()->GetDocShell(), pNode, index);
+ }
+
+ if (xField.is())
+ {
+ lcl_RemoveParagraphMetadataField(xField);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static OUString lcl_GetParagraphClassification(SfxClassificationHelper & rHelper, sfx::ClassificationKeyCreator const & rKeyCreator,
+ const uno::Reference<frame::XModel>& xModel, const uno::Reference<text::XTextContent>& xParagraph)
+{
+ uno::Reference<text::XTextField> xTextField;
+ xTextField = lcl_FindParagraphClassificationField(xModel, xParagraph, rKeyCreator.makeCategoryIdentifierKey());
+ if (xTextField.is())
+ {
+ const std::pair<OUString, OUString> rdfValuePair = lcl_getRDF(xModel, xTextField, ParagraphClassificationValueRDFName);
+ return rHelper.GetBACNameForIdentifier(rdfValuePair.second);
+ }
+
+ xTextField = lcl_FindParagraphClassificationField(xModel, xParagraph, rKeyCreator.makeCategoryNameKey());
+ if (xTextField.is())
+ {
+ return lcl_getRDF(xModel, xTextField, ParagraphClassificationNameRDFName).second;
+ }
+
+ return OUString();
+}
+
+static OUString lcl_GetHighestClassificationParagraphClass(SwPaM* pCursor)
+{
+ OUString sHighestClass;
+
+ SwTextNode* pNode = pCursor->Start()->nNode.GetNode().GetTextNode();
+ if (pNode == nullptr)
+ return sHighestClass;
+
+ SwDocShell* pDocShell = pNode->GetDoc().GetDocShell();
+ if (!pDocShell)
+ return sHighestClass;
+
+ SfxClassificationHelper aHelper(pDocShell->getDocProperties());
+ sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+
+ uno::Reference<frame::XModel> xModel = pDocShell->GetBaseModel();
+ const uno::Reference< text::XTextDocument > xDoc(xModel, uno::UNO_QUERY);
+ uno::Reference<text::XText> xParent = xDoc->getText();
+
+ uno::Reference<container::XEnumerationAccess> xParagraphEnumerationAccess(xParent, uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphs = xParagraphEnumerationAccess->createEnumeration();
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<text::XTextContent> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
+ const OUString sCurrentClass = lcl_GetParagraphClassification(aHelper, aKeyCreator, xModel, xParagraph);
+ sHighestClass = aHelper.GetHigherClass(sHighestClass, sCurrentClass);
+ }
+
+ return sHighestClass;
+}
+
+void SwEditShell::ClassifyDocPerHighestParagraphClass()
+{
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if (!pDocShell)
+ return;
+
+ // Bail out as early as possible if we don't have paragraph classification.
+ if (!SwRDFHelper::hasMetadataGraph(pDocShell->GetBaseModel(), MetaNS))
+ return;
+
+ uno::Reference<document::XDocumentProperties> xDocumentProperties = pDocShell->getDocProperties();
+ uno::Reference<beans::XPropertyContainer> xPropertyContainer = xDocumentProperties->getUserDefinedProperties();
+
+ sfx::ClassificationKeyCreator aKeyCreator(SfxClassificationHelper::getPolicyType());
+ SfxClassificationHelper aHelper(xDocumentProperties);
+
+ OUString sHighestClass = lcl_GetHighestClassificationParagraphClass(GetCursor());
+
+ const OUString aClassificationCategory = svx::classification::getProperty(xPropertyContainer, aKeyCreator.makeCategoryNameKey());
+
+ if (!aClassificationCategory.isEmpty())
+ {
+ sHighestClass = aHelper.GetHigherClass(sHighestClass, aClassificationCategory);
+ }
+
+ if (aClassificationCategory != sHighestClass)
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Question, VclButtonsType::Ok,
+ SwResId(STR_CLASSIFICATION_LEVEL_CHANGED)));
+ xQueryBox->run();
+ }
+
+ const SfxClassificationPolicyType eHighestClassType = SfxClassificationHelper::stringToPolicyType(sHighestClass);
+
+ // Prevent paragraph signature validation since the below changes (f.e. watermarking) are benign.
+ const bool bOldValidationFlag = SetParagraphSignatureValidation(false);
+ comphelper::ScopeGuard const g([this, bOldValidationFlag]() {
+ SetParagraphSignatureValidation(bOldValidationFlag);
+ });
+
+ // Check the origin, if "manual" (created via advanced classification dialog),
+ // then we just need to set the category name.
+ if (sfx::getCreationOriginProperty(xPropertyContainer, aKeyCreator) == sfx::ClassificationCreationOrigin::MANUAL)
+ {
+ aHelper.SetBACName(sHighestClass, eHighestClassType);
+ ApplyAdvancedClassification(CollectAdvancedClassification());
+ }
+ else
+ {
+ SetClassification(sHighestClass, eHighestClassType);
+ }
+}
+
+// #i62675#
+void SwEditShell::SetTextFormatColl(SwTextFormatColl *pFormat,
+ const bool bResetListAttrs)
+{
+ SwTextFormatColl *pLocal = pFormat? pFormat: (*GetDoc()->GetTextFormatColls())[0];
+ StartAllAction();
+
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, pLocal->GetName());
+
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::SETFMTCOLL, &aRewriter);
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ // If in table cells select mode, ignore the cells that aren't actually selected
+ if (IsTableMode() && !rPaM.HasMark())
+ continue;
+
+ if ( !rPaM.HasReadonlySel( GetViewOptions()->IsFormView() ) )
+ {
+ // store previous paragraph style for track changes
+ OUString sParaStyleName;
+ sal_uInt16 nPoolId = USHRT_MAX;
+ SwContentNode * pCnt = rPaM.Start()->nNode.GetNode().GetContentNode();
+ if ( pCnt && pCnt->GetTextNode() && GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ const SwTextFormatColl* pTextFormatColl = pCnt->GetTextNode()->GetTextColl();
+ sal_uInt16 nStylePoolId = pTextFormatColl->GetPoolFormatId();
+ // default paragraph style
+ if ( nStylePoolId == RES_POOLCOLL_STANDARD )
+ nPoolId = nStylePoolId;
+ else
+ sParaStyleName = pTextFormatColl->GetName();
+ }
+
+ // Change the paragraph style to pLocal and remove all direct paragraph formatting.
+ GetDoc()->SetTextFormatColl(rPaM, pLocal, true, bResetListAttrs, GetLayout());
+
+ // If there are hints on the nodes which cover the whole node, then remove those, too.
+ SwPaM aPaM(*rPaM.Start(), *rPaM.End());
+ if (SwTextNode* pEndTextNode = aPaM.End()->nNode.GetNode().GetTextNode())
+ {
+ aPaM.Start()->nContent = 0;
+ aPaM.End()->nContent = pEndTextNode->GetText().getLength();
+ }
+ GetDoc()->RstTextAttrs(aPaM, /*bInclRefToxMark=*/false, /*bExactRange=*/true, GetLayout());
+
+ // add redline tracking the previous paragraph style
+ if ( GetDoc()->getIDocumentRedlineAccess().IsRedlineOn() &&
+ // multi-paragraph ParagraphFormat redline ranges
+ // haven't supported by AppendRedline(), yet
+ // TODO handle multi-paragraph selections, too,
+ // e.g. by breaking them to single paragraphs
+ aPaM.Start()->nNode == aPaM.End()->nNode )
+ {
+ SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::ParagraphFormat, aPaM );
+ auto const result(GetDoc()->getIDocumentRedlineAccess().AppendRedline( pRedline, true));
+ // store original paragraph style to reject formatting change
+ if ( IDocumentRedlineAccess::AppendResult::IGNORED != result &&
+ ( nPoolId == RES_POOLCOLL_STANDARD || !sParaStyleName.isEmpty() ) )
+ {
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra;
+ xExtra.reset(new SwRedlineExtraData_FormatColl(sParaStyleName, nPoolId, nullptr));
+ if (xExtra)
+ pRedline->SetExtraData( xExtra.get() );
+ }
+ }
+ }
+
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::SETFMTCOLL, &aRewriter);
+ EndAllAction();
+}
+
+SwTextFormatColl* SwEditShell::MakeTextFormatColl(const OUString& rFormatCollName,
+ SwTextFormatColl* pParent)
+{
+ SwTextFormatColl *pColl;
+ if ( pParent == nullptr )
+ pParent = &GetTextFormatColl(0);
+ pColl = GetDoc()->MakeTextFormatColl(rFormatCollName, pParent);
+ if ( pColl == nullptr )
+ {
+ OSL_FAIL( "MakeTextFormatColl failed" );
+ }
+ return pColl;
+
+}
+
+void SwEditShell::FillByEx(SwTextFormatColl* pColl)
+{
+ SwPaM * pCursor = GetCursor();
+ SwContentNode * pCnt = pCursor->GetContentNode();
+ if (pCnt->IsTextNode()) // uhm... what nonsense would happen if not?
+ { // only need properties-node because BREAK/PAGEDESC filtered anyway!
+ pCnt = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
+ }
+ const SfxItemSet* pSet = pCnt->GetpSwAttrSet();
+ if( !pSet )
+ return;
+
+ // JP 05.10.98: Special treatment if one of the attributes Break/PageDesc/NumRule(auto) is
+ // in the ItemSet. Otherwise there will be too much or wrong processing (NumRules!)
+ // Bug 57568
+
+ // Do NOT copy AutoNumRules into the template
+ const SwNumRuleItem* pItem;
+ const SwNumRule* pRule = nullptr;
+ if (SfxItemState::SET == pSet->GetItemState(RES_BREAK, false)
+ || SfxItemState::SET == pSet->GetItemState(RES_PAGEDESC, false)
+ || ((pItem = pSet->GetItemIfSet(RES_PARATR_NUMRULE, false))
+ && nullptr != (pRule = GetDoc()->FindNumRulePtr(pItem->GetValue()))
+ && pRule->IsAutoRule()))
+ {
+ SfxItemSet aSet( *pSet );
+ aSet.ClearItem( RES_BREAK );
+ aSet.ClearItem( RES_PAGEDESC );
+
+ if (pRule
+ || ((pItem = pSet->GetItemIfSet(RES_PARATR_NUMRULE, false))
+ && nullptr != (pRule = GetDoc()->FindNumRulePtr(pItem->GetValue()))
+ && pRule->IsAutoRule()))
+ aSet.ClearItem( RES_PARATR_NUMRULE );
+
+ if( aSet.Count() )
+ GetDoc()->ChgFormat(*pColl, aSet );
+ }
+ else
+ GetDoc()->ChgFormat(*pColl, *pSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edfld.cxx b/sw/source/core/edit/edfld.cxx
new file mode 100644
index 000000000..9bd2ba252
--- /dev/null
+++ b/sw/source/core/edit/edfld.cxx
@@ -0,0 +1,410 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#include <editsh.hxx>
+#include <fldbas.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docary.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <pamtyp.hxx>
+#include <expfld.hxx>
+#include <swundo.hxx>
+#include <dbmgr.hxx>
+#include <hints.hxx>
+#include <fieldhint.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentContentOperations.hxx>
+
+/// count field types with a ResId, if SwFieldIds::Unknown count all
+size_t SwEditShell::GetFieldTypeCount(SwFieldIds nResId ) const
+{
+ const SwFieldTypes* pFieldTypes = GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+
+ if(nResId == SwFieldIds::Unknown)
+ {
+ return o3tl::narrowing<sal_uInt16>(pFieldTypes->size());
+ }
+
+ // all types with the same ResId
+ size_t nIdx = 0;
+ for(const auto & pFieldType : *pFieldTypes)
+ {
+ // same ResId -> increment index
+ if(pFieldType->Which() == nResId)
+ nIdx++;
+ }
+ return nIdx;
+}
+
+/// get field types with a ResId, if 0 get all
+SwFieldType* SwEditShell::GetFieldType(size_t nField, SwFieldIds nResId ) const
+{
+ const SwFieldTypes* pFieldTypes = GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+
+ if(nResId == SwFieldIds::Unknown && nField < pFieldTypes->size())
+ {
+ return (*pFieldTypes)[nField].get();
+ }
+
+ size_t nIdx = 0;
+ for(const auto & pFieldType : *pFieldTypes)
+ {
+ // same ResId -> increment index
+ if(pFieldType->Which() == nResId)
+ {
+ if(nIdx == nField)
+ return pFieldType.get();
+ nIdx++;
+ }
+ }
+ return nullptr;
+}
+
+/// get first type with given ResId and name
+SwFieldType* SwEditShell::GetFieldType(SwFieldIds nResId, const OUString& rName) const
+{
+ return GetDoc()->getIDocumentFieldsAccess().GetFieldType( nResId, rName, false );
+}
+
+/// delete field type
+void SwEditShell::RemoveFieldType(size_t nField)
+{
+ GetDoc()->getIDocumentFieldsAccess().RemoveFieldType(nField);
+}
+
+/// delete field type based on its name
+void SwEditShell::RemoveFieldType(SwFieldIds nResId, const OUString& rStr)
+{
+ const SwFieldTypes* pFieldTypes = GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+ const SwFieldTypes::size_type nSize = pFieldTypes->size();
+ const CharClass& rCC = GetAppCharClass();
+
+ OUString aTmp( rCC.lowercase( rStr ));
+
+ for(SwFieldTypes::size_type i = 0; i < nSize; ++i)
+ {
+ // same ResId -> increment index
+ SwFieldType* pFieldType = (*pFieldTypes)[i].get();
+ if( pFieldType->Which() == nResId )
+ {
+ if( aTmp == rCC.lowercase( pFieldType->GetName() ) )
+ {
+ GetDoc()->getIDocumentFieldsAccess().RemoveFieldType(i);
+ return;
+ }
+ }
+ }
+}
+
+void SwEditShell::FieldToText( SwFieldType const * pType )
+{
+ if( !pType->HasWriterListeners() )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+ StartUndo( SwUndoId::DELETE );
+ Push();
+ SwPaM* pPaM = GetCursor();
+ const SwFieldHint aHint(pPaM, GetLayout());
+ pType->CallSwClientNotify(aHint);
+
+ Pop(PopMode::DeleteCurrent);
+ EndAllAction();
+ EndUndo( SwUndoId::DELETE );
+}
+
+/// add a field at the cursor position
+bool SwEditShell::InsertField(SwField const & rField, const bool bForceExpandHints)
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ SwFormatField aField( rField );
+
+ const SetAttrMode nInsertFlags = bForceExpandHints
+ ? SetAttrMode::FORCEHINTEXPAND
+ : SetAttrMode::DEFAULT;
+
+ bool bSuccess(false);
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer()) // for each PaM
+ {
+ bSuccess |= GetDoc()->getIDocumentContentOperations().InsertPoolItem(rPaM, aField, nInsertFlags);
+ OSL_ENSURE( bSuccess, "Doc->Insert(Field) failed");
+ }
+
+ EndAllAction();
+ return bSuccess;
+}
+
+/// Are the PaMs positioned on fields?
+static SwTextField* lcl_FindInputField( SwDoc* pDoc, const 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()
+ || (SwFieldIds::SetExp == rField.Which()
+ && static_cast<const SwSetExpField&>(rField).GetInputFlag()
+ && (static_cast<SwSetExpFieldType*>(rField.GetTyp())->GetType()
+ & nsSwGetSetExpType::GSE_STRING)))
+ {
+ for (const SfxPoolItem* pItem : pDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INPUTFIELD))
+ {
+ auto pFormatField = dynamic_cast<const SwFormatField*>(pItem);
+ if( pFormatField && pFormatField->GetField() == &rField )
+ {
+ pTField = const_cast<SwFormatField*>(pFormatField)->GetTextField();
+ break;
+ }
+ }
+ }
+ else if( SwFieldIds::SetExp == rField.Which()
+ && static_cast<const SwSetExpField&>(rField).GetInputFlag() )
+ {
+ for (const SfxPoolItem* pItem : pDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_FIELD))
+ {
+ auto pFormatField = dynamic_cast<const SwFormatField*>(pItem);
+ if( pFormatField && pFormatField->GetField() == &rField )
+ {
+ pTField = const_cast<SwFormatField*>(pFormatField)->GetTextField();
+ break;
+ }
+ }
+ }
+ return pTField;
+}
+
+void SwEditShell::UpdateOneField(SwField &rField)
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ {
+ // If there are no selections so take the value of the current cursor position.
+ SwMsgPoolItem* pMsgHint = nullptr;
+ SwRefMarkFieldUpdate aRefMkHt( GetOut() );
+ SwFieldIds nFieldWhich = rField.GetTyp()->Which();
+ if( SwFieldIds::GetRef == nFieldWhich )
+ pMsgHint = &aRefMkHt;
+
+ SwPaM* pCursor = GetCursor();
+ SwTextField *pTextField;
+ SwFormatField *pFormatField;
+
+ if ( !pCursor->IsMultiSelection() && !pCursor->HasMark())
+ {
+ pTextField = GetTextFieldAtPos( pCursor->Start(), true );
+
+ if (!pTextField) // #i30221#
+ pTextField = lcl_FindInputField( GetDoc(), rField);
+
+ if (pTextField != nullptr)
+ GetDoc()->getIDocumentFieldsAccess().UpdateField(pTextField, rField, pMsgHint, true);
+ }
+
+ // bOkay (instead of return because of EndAllAction) becomes false,
+ // 1) if only one PaM has more than one field or
+ // 2) if there are mixed field types
+ bool bOkay = true;
+ bool bTableSelBreak = false;
+
+ SwMsgPoolItem aFieldHint( RES_TXTATR_FIELD ); // Search-Hint
+ SwMsgPoolItem aAnnotationFieldHint( RES_TXTATR_ANNOTATION );
+ SwMsgPoolItem aInputFieldHint( RES_TXTATR_INPUTFIELD );
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer()) // for each PaM
+ {
+ if( rPaM.HasMark() && bOkay ) // ... with selection
+ {
+ // copy of the PaM
+ SwPaM aCurPam( *rPaM.GetMark(), *rPaM.GetPoint() );
+ SwPaM aPam( *rPaM.GetPoint() );
+
+ SwPosition *pCurStt = aCurPam.Start(), *pCurEnd =
+ aCurPam.End();
+ /*
+ * In case that there are two contiguous fields in a PaM, the aPam goes step by step
+ * to the end. aCurPam is reduced in each loop. If aCurPam was searched completely,
+ * the loop terminates because Start = End.
+ */
+
+ // Search for SwTextField ...
+ while( bOkay
+ && pCurStt->nContent != pCurEnd->nContent
+ && (sw::FindAttrImpl(aPam, aFieldHint, fnMoveForward, aCurPam, true, GetLayout())
+ || sw::FindAttrImpl(aPam, aAnnotationFieldHint, fnMoveForward, aCurPam, false, GetLayout())
+ || sw::FindAttrImpl(aPam, aInputFieldHint, fnMoveForward, aCurPam, false, GetLayout())))
+ {
+ // if only one PaM has more than one field ...
+ if( aPam.Start()->nContent != pCurStt->nContent )
+ bOkay = false;
+
+ pTextField = GetTextFieldAtPos( pCurStt, true );
+ if( nullptr != pTextField )
+ {
+ pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+ SwField *pCurField = pFormatField->GetField();
+
+ // if there are mixed field types
+ if( pCurField->GetTyp()->Which() !=
+ rField.GetTyp()->Which() )
+ bOkay = false;
+
+ bTableSelBreak = GetDoc()->getIDocumentFieldsAccess().UpdateField(pTextField, rField,
+ pMsgHint, false);
+ }
+ // The search area is reduced by the found area:
+ ++pCurStt->nContent;
+ }
+ }
+
+ if( bTableSelBreak ) // If table section and table formula are updated -> finish
+ break;
+
+ }
+ }
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+}
+
+SwDBData const & SwEditShell::GetDBData() const
+{
+ return GetDoc()->GetDBData();
+}
+
+void SwEditShell::ChgDBData(const SwDBData& rNewData)
+{
+ GetDoc()->ChgDBData(rNewData);
+}
+
+void SwEditShell::GetAllUsedDB( std::vector<OUString>& rDBNameList,
+ std::vector<OUString> const * pAllDBNames )
+{
+ GetDoc()->GetAllUsedDB( rDBNameList, pAllDBNames );
+}
+
+void SwEditShell::ChangeDBFields( const std::vector<OUString>& rOldNames,
+ const OUString& rNewName )
+{
+ GetDoc()->ChangeDBFields( rOldNames, rNewName );
+}
+
+/// Update all expression fields
+void SwEditShell::UpdateExpFields(bool bCloseDB)
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+ if (bCloseDB)
+ {
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ GetDoc()->GetDBManager()->CloseAll(); // close all database connections
+#endif
+ }
+ EndAllAction();
+}
+
+SwDBManager* SwEditShell::GetDBManager() const
+{
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ return GetDoc()->GetDBManager();
+#else
+ return NULL;
+#endif
+}
+
+/// insert field type
+SwFieldType* SwEditShell::InsertFieldType(const SwFieldType& rFieldType)
+{
+ return GetDoc()->getIDocumentFieldsAccess().InsertFieldType(rFieldType);
+}
+
+void SwEditShell::LockExpFields()
+{
+ GetDoc()->getIDocumentFieldsAccess().LockExpFields();
+}
+
+void SwEditShell::UnlockExpFields()
+{
+ GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
+}
+
+bool SwEditShell::IsExpFieldsLocked() const
+{
+ return GetDoc()->getIDocumentFieldsAccess().IsExpFieldsLocked();
+}
+
+void SwEditShell::SetFieldUpdateFlags( SwFieldUpdateFlags eFlags )
+{
+ getIDocumentSettingAccess().setFieldUpdateFlags( eFlags );
+}
+
+SwFieldUpdateFlags SwEditShell::GetFieldUpdateFlags() const
+{
+ return getIDocumentSettingAccess().getFieldUpdateFlags( false );
+}
+
+void SwEditShell::SetLabelDoc( bool bFlag )
+{
+ GetDoc()->GetDocumentSettingManager().set(DocumentSettingId::LABEL_DOCUMENT, bFlag );
+}
+
+bool SwEditShell::IsLabelDoc() const
+{
+ return getIDocumentSettingAccess().get(DocumentSettingId::LABEL_DOCUMENT);
+}
+
+void SwEditShell::ChangeAuthorityData(const SwAuthEntry* pNewData)
+{
+ GetDoc()->ChangeAuthorityData(pNewData);
+}
+
+bool SwEditShell::IsAnyDatabaseFieldInDoc()const
+{
+ // Similar to: SwDoc::GetDBDesc
+ const SwFieldTypes * pFieldTypes = GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+ for(const auto & pFieldType : *pFieldTypes)
+ {
+ if(IsUsed(*pFieldType))
+ {
+ switch(pFieldType->Which())
+ {
+ case SwFieldIds::Database:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbSetNumber:
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields);
+ return vFields.size();
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edfldexp.cxx b/sw/source/core/edit/edfldexp.cxx
new file mode 100644
index 000000000..3247d0500
--- /dev/null
+++ b/sw/source/core/edit/edfldexp.cxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editsh.hxx>
+#include <dbfld.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/sdb/DatabaseContext.hpp>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docary.hxx>
+#include <fmtfld.hxx>
+
+using namespace com::sun::star;
+
+bool SwEditShell::IsFieldDataSourceAvailable(OUString& rUsedDataSource) const
+{
+ const SwFieldTypes* pFieldTypes = GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+ uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ uno::Reference<sdb::XDatabaseContext> xDBContext = sdb::DatabaseContext::create(xContext);
+ std::vector<SwFormatField*> vFields;
+ for (const auto& pFieldType : *pFieldTypes)
+ {
+ if (IsUsed(*pFieldType) && pFieldType->Which() == SwFieldIds::Database)
+ pFieldType->GatherFields(vFields);
+ }
+ if (!vFields.size())
+ return true;
+
+ const SwDBData& rData
+ = static_cast<SwDBFieldType*>(vFields.front()->GetField()->GetTyp())->GetDBData();
+ try
+ {
+ return xDBContext->getByName(rData.sDataSource).hasValue();
+ }
+ catch (uno::Exception const&)
+ {
+ rUsedDataSource = rData.sDataSource;
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edfmt.cxx b/sw/source/core/edit/edfmt.cxx
new file mode 100644
index 000000000..3e5c735c5
--- /dev/null
+++ b/sw/source/core/edit/edfmt.cxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <editsh.hxx>
+#include <pam.hxx>
+#include <fchrfmt.hxx>
+#include <frmfmt.hxx>
+#include <charfmt.hxx>
+#include <ndtxt.hxx>
+
+sal_uInt16 SwEditShell::GetCharFormatCount() const
+{
+ return GetDoc()->GetCharFormats()->size();
+}
+
+SwCharFormat& SwEditShell::GetCharFormat(sal_uInt16 nFormat) const
+{
+ return *((*(GetDoc()->GetCharFormats()))[nFormat]);
+}
+
+SwCharFormat* SwEditShell::GetCurCharFormat() const
+{
+ SwCharFormat *pFormat = nullptr;
+ SfxItemSetFixed<RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> aSet( GetDoc()->GetAttrPool() );
+ const SwFormatCharFormat* pItem;
+ if( GetCurAttr( aSet ) &&
+ (pItem = aSet.GetItemIfSet( RES_TXTATR_CHARFMT, false ) ) )
+ pFormat = pItem->GetCharFormat();
+
+ return pFormat;
+}
+
+void SwEditShell::FillByEx(SwCharFormat* pCharFormat)
+{
+ SwPaM* pPam = GetCursor();
+ const SwContentNode* pCNd = pPam->GetContentNode();
+ if( pCNd->IsTextNode() )
+ {
+ SwTextNode const*const pTextNode(pCNd->GetTextNode());
+ sal_Int32 nStt;
+ sal_Int32 nEnd;
+ if( pPam->HasMark() )
+ {
+ const SwPosition* pPtPos = pPam->GetPoint();
+ const SwPosition* pMkPos = pPam->GetMark();
+ if( pPtPos->nNode == pMkPos->nNode ) // in the same node?
+ {
+ nStt = pPtPos->nContent.GetIndex();
+ if( nStt < pMkPos->nContent.GetIndex() )
+ nEnd = pMkPos->nContent.GetIndex();
+ else
+ {
+ nEnd = nStt;
+ nStt = pMkPos->nContent.GetIndex();
+ }
+ }
+ else
+ {
+ nStt = pMkPos->nContent.GetIndex();
+ if( pPtPos->nNode < pMkPos->nNode )
+ {
+ nEnd = nStt;
+ nStt = 0;
+ }
+ else
+ nEnd = pTextNode->GetText().getLength();
+ }
+ }
+ else
+ nStt = nEnd = pPam->GetPoint()->nContent.GetIndex();
+
+ SfxItemSet aSet( mxDoc->GetAttrPool(),
+ pCharFormat->GetAttrSet().GetRanges() );
+ pTextNode->GetParaAttr(aSet, nStt, nEnd, false, true, false, GetLayout());
+ pCharFormat->SetFormatAttr( aSet );
+ }
+ else if( pCNd->HasSwAttrSet() )
+ pCharFormat->SetFormatAttr( *pCNd->GetpSwAttrSet() );
+}
+
+size_t SwEditShell::GetTableFrameFormatCount(bool bUsed) const
+{
+ return GetDoc()->GetTableFrameFormatCount(bUsed);
+}
+
+SwFrameFormat& SwEditShell::GetTableFrameFormat(size_t nFormat, bool bUsed ) const
+{
+ return GetDoc()->GetTableFrameFormat(nFormat, bUsed );
+}
+
+OUString SwEditShell::GetUniqueTableName() const
+{
+ return GetDoc()->GetUniqueTableName();
+}
+
+SwCharFormat* SwEditShell::MakeCharFormat( const OUString& rName )
+{
+ SwCharFormat* pDerivedFrom = GetDoc()->GetDfltCharFormat();
+
+ return GetDoc()->MakeCharFormat( rName, pDerivedFrom );
+}
+
+SwTextFormatColl* SwEditShell::GetTextCollFromPool( sal_uInt16 nId )
+{
+ return GetDoc()->getIDocumentStylePoolAccess().GetTextCollFromPool( nId );
+}
+
+/// return the requested automatic format - base-class !
+SwFormat* SwEditShell::GetFormatFromPool( sal_uInt16 nId )
+{
+ return GetDoc()->getIDocumentStylePoolAccess().GetFormatFromPool( nId );
+}
+
+SwPageDesc* SwEditShell::GetPageDescFromPool( sal_uInt16 nId )
+{
+ return GetDoc()->getIDocumentStylePoolAccess().GetPageDescFromPool( nId );
+}
+
+bool SwEditShell::IsUsed( const sw::BroadcastingModify& rModify ) const
+{
+ return mxDoc->IsUsed( rModify );
+}
+
+const SwFlyFrameFormat* SwEditShell::FindFlyByName( const OUString& rName ) const
+{
+ return mxDoc->FindFlyByName(rName);
+}
+
+SwCharFormat* SwEditShell::FindCharFormatByName( const OUString& rName ) const
+{
+ return mxDoc->FindCharFormatByName( rName );
+}
+
+SwTextFormatColl* SwEditShell::FindTextFormatCollByName( const OUString& rName ) const
+{
+ return mxDoc->FindTextFormatCollByName( rName );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edglbldc.cxx b/sw/source/core/edit/edglbldc.cxx
new file mode 100644
index 000000000..80a97ba33
--- /dev/null
+++ b/sw/source/core/edit/edglbldc.cxx
@@ -0,0 +1,383 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentState.hxx>
+#include <editsh.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <section.hxx>
+#include <doctxm.hxx>
+#include <edglbldc.hxx>
+
+bool SwEditShell::IsGlobalDoc() const
+{
+ return getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT);
+}
+
+void SwEditShell::SetGlblDocSaveLinks( bool bFlag )
+{
+ getIDocumentSettingAccess().set(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS, bFlag);
+ if( !GetDoc()->getIDocumentState().IsModified() ) // Bug 57028
+ {
+ GetDoc()->GetIDocumentUndoRedo().SetUndoNoResetModified();
+ }
+ GetDoc()->getIDocumentState().SetModified();
+}
+
+bool SwEditShell::IsGlblDocSaveLinks() const
+{
+ return getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT_SAVE_LINKS);
+}
+
+void SwEditShell::GetGlobalDocContent( SwGlblDocContents& rArr ) const
+{
+ rArr.clear();
+
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ return;
+
+ // then all linked areas on the topmost level
+ SwDoc* pMyDoc = GetDoc();
+ const SwSectionFormats& rSectFormats = pMyDoc->GetSections();
+
+ for( auto n = rSectFormats.size(); n; )
+ {
+ const SwSection* pSect = rSectFormats[ --n ]->GetGlobalDocSection();
+ if( pSect )
+ {
+ std::unique_ptr<SwGlblDocContent> pNew;
+ switch( pSect->GetType() )
+ {
+ case SectionType::ToxHeader:
+ break; // ignore
+ case SectionType::ToxContent:
+ assert( dynamic_cast<const SwTOXBaseSection*>( pSect) && "no TOXBaseSection!" );
+ pNew.reset(new SwGlblDocContent( static_cast<const SwTOXBaseSection*>(pSect) ));
+ break;
+
+ default:
+ pNew.reset(new SwGlblDocContent( pSect ));
+ break;
+ }
+ rArr.insert( std::move(pNew) );
+ }
+ }
+
+ // and finally add the dummies (other text)
+ SwNode* pNd;
+ SwNodeOffset nSttIdx = pMyDoc->GetNodes().GetEndOfExtras().GetIndex() + 2;
+ for( SwGlblDocContents::size_type n = 0; n < rArr.size(); ++n )
+ {
+ const SwGlblDocContent& rNew = *rArr[ n ];
+ // Search from StartPos until rNew.DocPos for a content node.
+ // If one exists then a dummy entry is needed.
+ for( ; nSttIdx < rNew.GetDocPos(); ++nSttIdx )
+ if( ( pNd = pMyDoc->GetNodes()[ nSttIdx ])->IsContentNode()
+ || pNd->IsSectionNode() || pNd->IsTableNode() )
+ {
+ std::unique_ptr<SwGlblDocContent> pNew(new SwGlblDocContent( nSttIdx ));
+ if( rArr.insert( std::move(pNew) ).second )
+ ++n; // to the next position
+ break;
+ }
+
+ // set StartPosition to the end
+ nSttIdx = pMyDoc->GetNodes()[ rNew.GetDocPos() ]->EndOfSectionIndex();
+ ++nSttIdx;
+ }
+
+ // Should the end also be set?
+ if( !rArr.empty() )
+ {
+ SwNodeOffset nNdEnd = pMyDoc->GetNodes().GetEndOfContent().GetIndex();
+ for( ; nSttIdx < nNdEnd; ++nSttIdx )
+ if( ( pNd = pMyDoc->GetNodes()[ nSttIdx ])->IsContentNode()
+ || pNd->IsSectionNode() || pNd->IsTableNode() )
+ {
+ rArr.insert( std::make_unique<SwGlblDocContent>( nSttIdx ) );
+ break;
+ }
+ }
+ else
+ {
+ std::unique_ptr<SwGlblDocContent> pNew(new SwGlblDocContent(
+ pMyDoc->GetNodes().GetEndOfExtras().GetIndex() + 2 ));
+ rArr.insert( std::move(pNew) );
+ }
+}
+
+void SwEditShell::InsertGlobalDocContent( const SwGlblDocContent& rInsPos,
+ SwSectionData & rNew)
+{
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor || IsTableMode() )
+ ClearMark();
+
+ SwPosition& rPos = *pCursor->GetPoint();
+ rPos.nNode = rInsPos.GetDocPos();
+
+ bool bEndUndo = false;
+ SwDoc* pMyDoc = GetDoc();
+ SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ rPos.nContent.Assign( pTextNd, 0 );
+ else
+ {
+ bEndUndo = true;
+ pMyDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ --rPos.nNode;
+ pMyDoc->getIDocumentContentOperations().AppendTextNode( rPos );
+ pCursor->SetMark();
+ }
+
+ InsertSection( rNew );
+
+ if( bEndUndo )
+ {
+ pMyDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ EndAllAction();
+}
+
+bool SwEditShell::InsertGlobalDocContent( const SwGlblDocContent& rInsPos,
+ const SwTOXBase& rTOX )
+{
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ return false;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor || IsTableMode() )
+ ClearMark();
+
+ SwPosition& rPos = *pCursor->GetPoint();
+ rPos.nNode = rInsPos.GetDocPos();
+
+ bool bEndUndo = false;
+ SwDoc* pMyDoc = GetDoc();
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if (pTextNd && pTextNd->GetText().getLength() && rPos.nNode.GetIndex() + 1 !=
+ pMyDoc->GetNodes().GetEndOfContent().GetIndex() )
+ rPos.nContent.Assign( pTextNd, 0 );
+ else
+ {
+ bEndUndo = true;
+ pMyDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ --rPos.nNode;
+ pMyDoc->getIDocumentContentOperations().AppendTextNode( rPos );
+ }
+
+ InsertTableOf( rTOX );
+
+ if( bEndUndo )
+ {
+ pMyDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ EndAllAction();
+
+ return true;
+}
+
+bool SwEditShell::InsertGlobalDocContent( const SwGlblDocContent& rInsPos )
+{
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ return false;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor || IsTableMode() )
+ ClearMark();
+
+ SwPosition& rPos = *pCursor->GetPoint();
+ rPos.nNode = rInsPos.GetDocPos() - 1;
+ rPos.nContent.Assign( nullptr, 0 );
+
+ SwDoc* pMyDoc = GetDoc();
+ pMyDoc->getIDocumentContentOperations().AppendTextNode( rPos );
+ EndAllAction();
+ return true;
+}
+
+void SwEditShell::DeleteGlobalDocContent( const SwGlblDocContents& rArr ,
+ size_t nDelPos )
+{
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+ StartUndo( SwUndoId::START );
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor || IsTableMode() )
+ ClearMark();
+
+ SwPosition& rPos = *pCursor->GetPoint();
+
+ SwDoc* pMyDoc = GetDoc();
+ const SwGlblDocContent& rDelPos = *rArr[ nDelPos ];
+ SwNodeOffset nDelIdx = rDelPos.GetDocPos();
+ if( 1 == rArr.size() )
+ {
+ // we need at least one node!
+ rPos.nNode = nDelIdx - 1;
+ rPos.nContent.Assign( nullptr, 0 );
+
+ pMyDoc->getIDocumentContentOperations().AppendTextNode( rPos );
+ ++nDelIdx;
+ }
+
+ switch( rDelPos.GetType() )
+ {
+ case GLBLDOC_UNKNOWN:
+ {
+ rPos.nNode = nDelIdx;
+ pCursor->SetMark();
+ if( ++nDelPos < rArr.size() )
+ rPos.nNode = rArr[ nDelPos ]->GetDocPos();
+ else
+ rPos.nNode = pMyDoc->GetNodes().GetEndOfContent();
+ --rPos.nNode;
+ if( !pMyDoc->getIDocumentContentOperations().DelFullPara( *pCursor ) )
+ Delete(false);
+ }
+ break;
+
+ case GLBLDOC_TOXBASE:
+ {
+ const SwTOXBaseSection* pTOX = static_cast<const SwTOXBaseSection*>(rDelPos.GetTOX());
+ pMyDoc->DeleteTOX( *pTOX, true );
+ }
+ break;
+
+ case GLBLDOC_SECTION:
+ {
+ SwSectionFormat* pSectFormat = const_cast<SwSectionFormat*>(rDelPos.GetSection()->GetFormat());
+ pMyDoc->DelSectionFormat( pSectFormat, true );
+ }
+ break;
+ }
+
+ EndUndo( SwUndoId::END );
+ EndAllAction();
+}
+
+bool SwEditShell::MoveGlobalDocContent( const SwGlblDocContents& rArr ,
+ size_t nFromPos, size_t nToPos,
+ size_t nInsPos )
+{
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) ||
+ nFromPos >= rArr.size() || nToPos > rArr.size() ||
+ nInsPos > rArr.size() || nFromPos >= nToPos ||
+ ( nFromPos <= nInsPos && nInsPos <= nToPos ) )
+ return false;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor || IsTableMode() )
+ ClearMark();
+
+ SwDoc* pMyDoc = GetDoc();
+ SwNodeRange aRg( pMyDoc->GetNodes(), rArr[ nFromPos ]->GetDocPos() );
+ if( nToPos < rArr.size() )
+ aRg.aEnd = rArr[ nToPos ]->GetDocPos();
+ else
+ aRg.aEnd = pMyDoc->GetNodes().GetEndOfContent();
+
+ SwNodeIndex aInsPos( pMyDoc->GetNodes() );
+ if( nInsPos < rArr.size() )
+ aInsPos = rArr[ nInsPos ]->GetDocPos();
+ else
+ aInsPos = pMyDoc->GetNodes().GetEndOfContent();
+
+ bool bRet = pMyDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aInsPos,
+ SwMoveFlags::CREATEUNDOOBJ );
+
+ EndAllAction();
+ return bRet;
+}
+
+void SwEditShell::GotoGlobalDocContent( const SwGlblDocContent& rPos )
+{
+ if( !getIDocumentSettingAccess().get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ return;
+
+ CurrShell aCurr( this );
+ SttCursorMove();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor || IsTableMode() )
+ ClearMark();
+
+ SwPosition& rCursorPos = *pCursor->GetPoint();
+ rCursorPos.nNode = rPos.GetDocPos();
+
+ SwDoc* pMyDoc = GetDoc();
+ SwContentNode * pCNd = rCursorPos.nNode.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = pMyDoc->GetNodes().GoNext( &rCursorPos.nNode );
+
+ rCursorPos.nContent.Assign( pCNd, 0 );
+
+ EndCursorMove();
+}
+
+SwGlblDocContent::SwGlblDocContent( SwNodeOffset nPos )
+{
+ m_eType = GLBLDOC_UNKNOWN;
+ m_PTR.pTOX = nullptr;
+ m_nDocPos = nPos;
+}
+
+SwGlblDocContent::SwGlblDocContent( const SwTOXBaseSection* pTOX )
+{
+ m_eType = GLBLDOC_TOXBASE;
+ m_PTR.pTOX = pTOX;
+
+ const SwSectionNode* pSectNd = pTOX->GetFormat()->GetSectionNode();
+ m_nDocPos = pSectNd ? pSectNd->GetIndex() : SwNodeOffset(0);
+}
+
+SwGlblDocContent::SwGlblDocContent( const SwSection* pSect )
+{
+ m_eType = GLBLDOC_SECTION;
+ m_PTR.pSect = pSect;
+
+ const SwSectionNode* pSectNd = pSect->GetFormat()->GetSectionNode();
+ m_nDocPos = pSectNd ? pSectNd->GetIndex() : SwNodeOffset(0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edglss.cxx b/sw/source/core/edit/edglss.cxx
new file mode 100644
index 000000000..47791dc69
--- /dev/null
+++ b/sw/source/core/edit/edglss.cxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <osl/endian.h>
+#include <tools/urlobj.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <pam.hxx>
+#include <editsh.hxx>
+#include <frmfmt.hxx>
+#include <rootfrm.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <shellio.hxx>
+#include <iodetect.hxx>
+#include <frameformats.hxx>
+
+void SwEditShell::InsertGlossary( SwTextBlocks& rGlossary, const OUString& rStr )
+{
+ StartAllAction();
+ GetDoc()->InsertGlossary( rGlossary, rStr, *GetCursor(), this );
+ EndAllAction();
+}
+
+/// convert current selection into text block and add to the text block document, incl. templates
+sal_uInt16 SwEditShell::MakeGlossary( SwTextBlocks& rBlks, const OUString& rName, const OUString& rShortName,
+ bool bSaveRelFile, const OUString* pOnlyText )
+{
+ SwDoc* pGDoc = rBlks.GetDoc();
+
+ OUString sBase;
+ if(bSaveRelFile)
+ {
+ INetURLObject aURL( rBlks.GetFileName() );
+ sBase = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ rBlks.SetBaseURL( sBase );
+
+ if( pOnlyText )
+ return rBlks.PutText( rShortName, rName, *pOnlyText );
+
+ rBlks.ClearDoc();
+ if( rBlks.BeginPutDoc( rShortName, rName ) )
+ {
+ rBlks.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines );
+ CopySelToDoc(*pGDoc);
+ rBlks.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE );
+ return rBlks.PutDoc();
+ }
+
+ return USHRT_MAX;
+}
+
+sal_uInt16 SwEditShell::SaveGlossaryDoc( SwTextBlocks& rBlock,
+ const OUString& rName,
+ const OUString& rShortName,
+ bool bSaveRelFile,
+ bool bOnlyText )
+{
+ StartAllAction();
+
+ SwDoc* pGDoc = rBlock.GetDoc();
+ SwDoc* pMyDoc = GetDoc();
+
+ OUString sBase;
+ if(bSaveRelFile)
+ {
+ INetURLObject aURL( rBlock.GetFileName() );
+ sBase = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ rBlock.SetBaseURL( sBase );
+ sal_uInt16 nRet = USHRT_MAX;
+
+ if( bOnlyText )
+ {
+ KillPams();
+
+ SwPaM* pCursor = GetCursor();
+
+ SwNodeIndex aStt( pMyDoc->GetNodes().GetEndOfExtras(), 1 );
+ SwContentNode* pContentNd = pMyDoc->GetNodes().GoNext( &aStt );
+ const SwNode* pNd = pContentNd->FindTableNode();
+ if( !pNd )
+ pNd = pContentNd;
+
+ pCursor->GetPoint()->nNode = *pNd;
+ if( pNd == pContentNd )
+ pCursor->GetPoint()->nContent.Assign( pContentNd, 0 );
+ pCursor->SetMark();
+
+ // then until the end of the Node array
+ pCursor->GetPoint()->nNode = pMyDoc->GetNodes().GetEndOfContent().GetIndex()-1;
+ pContentNd = pCursor->GetContentNode();
+ if( pContentNd )
+ pCursor->GetPoint()->nContent.Assign( pContentNd, pContentNd->Len() );
+
+ OUString sBuf;
+ GetSelectedText( sBuf, ParaBreakType::ToOnlyCR );
+ if( !sBuf.isEmpty() )
+ nRet = rBlock.PutText( rShortName, rName, sBuf );
+ }
+ else
+ {
+ rBlock.ClearDoc();
+ if( rBlock.BeginPutDoc( rShortName, rName ) )
+ {
+ SwNodeIndex aStt( pMyDoc->GetNodes().GetEndOfExtras(), 1 );
+ SwContentNode* pContentNd = pMyDoc->GetNodes().GoNext( &aStt );
+ const SwNode* pNd = pContentNd->FindTableNode();
+ if( !pNd ) pNd = pContentNd;
+ SwPaM aCpyPam( *pNd );
+ aCpyPam.SetMark();
+
+ // then until the end of the nodes array
+ aCpyPam.GetPoint()->nNode = pMyDoc->GetNodes().GetEndOfContent().GetIndex()-1;
+ pContentNd = aCpyPam.GetContentNode();
+ aCpyPam.GetPoint()->nContent.Assign(
+ pContentNd, pContentNd ? pContentNd->Len() : 0);
+
+ aStt = pGDoc->GetNodes().GetEndOfExtras();
+ pContentNd = pGDoc->GetNodes().GoNext( &aStt );
+ SwPosition aInsPos( aStt, SwIndex( pContentNd ));
+ pMyDoc->getIDocumentContentOperations().CopyRange(aCpyPam, aInsPos, SwCopyFlags::CheckPosInFly);
+
+ nRet = rBlock.PutDoc();
+ }
+ }
+ EndAllAction();
+ return nRet;
+}
+
+/// copy all selections to the doc
+bool SwEditShell::CopySelToDoc( SwDoc& rInsDoc )
+{
+ SwNodes& rNds = rInsDoc.GetNodes();
+
+ SwNodeIndex aIdx( rNds.GetEndOfContent(), -1 );
+ SwContentNode *const pContentNode = aIdx.GetNode().GetContentNode();
+ SwPosition aPos( aIdx,
+ SwIndex(pContentNode, pContentNode ? pContentNode->Len() : 0));
+
+ bool bRet = false;
+ CurrShell aCurr( this );
+
+ rInsDoc.getIDocumentFieldsAccess().LockExpFields();
+
+ if( IsTableMode() )
+ {
+ // Copy parts of a table: create a table with the width of the original one and copy the
+ // selected boxes. The sizes are corrected on a percentage basis.
+
+ // search boxes using the layout
+ SwTableNode* pTableNd;
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes );
+ if( !aBoxes.empty() && nullptr != (pTableNd = const_cast<SwTableNode*>(aBoxes[0]
+ ->GetSttNd()->FindTableNode()) ))
+ {
+ // check if the table name can be copied
+ bool bCpyTableNm = aBoxes.size() == pTableNd->GetTable().GetTabSortBoxes().size();
+ if( bCpyTableNm )
+ {
+ const OUString rTableName = pTableNd->GetTable().GetFrameFormat()->GetName();
+ const SwFrameFormats& rTableFormats = *rInsDoc.GetTableFrameFormats();
+ for( auto n = rTableFormats.size(); n; )
+ if( rTableFormats[ --n ]->GetName() == rTableName )
+ {
+ bCpyTableNm = false;
+ break;
+ }
+ }
+ bRet = rInsDoc.InsCopyOfTable( aPos, aBoxes, nullptr, bCpyTableNm, false, pTableNd->GetTable().GetTableStyleName() );
+ }
+ else
+ bRet = false;
+ }
+ else
+ {
+ bool bColSel = GetCursor_()->IsColumnSelection();
+ if( bColSel && rInsDoc.IsClipBoard() )
+ rInsDoc.SetColumnSelection( true );
+ bool bSelectAll = StartsWithTable() && ExtendedSelectedAll();
+ {
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( !rPaM.HasMark() )
+ {
+ SwContentNode *const pNd = rPaM.GetContentNode();
+ if (nullptr != pNd &&
+ ( bColSel || !pNd->GetTextNode() ) )
+ {
+ rPaM.SetMark();
+ rPaM.Move( fnMoveForward, GoInContent );
+ bRet = GetDoc()->getIDocumentContentOperations().CopyRange(rPaM, aPos, SwCopyFlags::CheckPosInFly)
+ || bRet;
+ rPaM.Exchange();
+ rPaM.DeleteMark();
+ }
+ }
+ else
+ {
+ // Make a copy, so that in case we need to adjust the selection
+ // for the purpose of copying, our shell cursor is not touched.
+ // (Otherwise we would have to restore it.)
+ SwPaM aPaM(*rPaM.GetMark(), *rPaM.GetPoint());
+ if (bSelectAll)
+ {
+ // Selection starts at the first para of the first cell,
+ // but we want to copy the table and the start node before
+ // the first cell as well.
+ // tdf#133982 tables can be nested
+ while (SwTableNode const* pTableNode =
+ aPaM.Start()->nNode.GetNode().StartOfSectionNode()->FindTableNode())
+ {
+ aPaM.Start()->nNode = *pTableNode;
+ }
+ while (SwSectionNode const* pSectionNode =
+ aPaM.Start()->nNode.GetNode().StartOfSectionNode()->FindSectionNode())
+ {
+ aPaM.Start()->nNode = *pSectionNode;
+ }
+ aPaM.Start()->nContent.Assign(nullptr, 0);
+ }
+ bRet = GetDoc()->getIDocumentContentOperations().CopyRange( aPaM, aPos, SwCopyFlags::CheckPosInFly)
+ || bRet;
+ }
+ }
+ }
+ }
+
+ rInsDoc.getIDocumentFieldsAccess().UnlockExpFields();
+ if( !rInsDoc.getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ rInsDoc.getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+
+ return bRet;
+}
+
+/** Get text in a Selection
+ */
+void SwEditShell::GetSelectedText( OUString &rBuf, ParaBreakType nHndlParaBrk )
+{
+ GetCursor(); // creates all cursors if needed
+ if( IsSelOnePara() )
+ {
+ rBuf = GetSelText();
+ if( ParaBreakType::ToBlank == nHndlParaBrk )
+ {
+ rBuf = rBuf.replaceAll("\x0a", " ");
+ }
+ else if( IsSelFullPara() &&
+ ParaBreakType::ToOnlyCR != nHndlParaBrk )
+ {
+#ifdef _WIN32
+ rBuf += "\015\012";
+#else
+ rBuf += "\012";
+#endif
+ }
+ }
+ else if( IsSelection() )
+ {
+ SvMemoryStream aStream;
+#ifdef OSL_BIGENDIAN
+ aStream.SetEndian( SvStreamEndian::BIG );
+#else
+ aStream.SetEndian( SvStreamEndian::LITTLE );
+#endif
+ WriterRef xWrt;
+ SwReaderWriter::GetWriter( FILTER_TEXT, OUString(), xWrt );
+ if( xWrt.is() )
+ {
+ // write selected areas into an ASCII document
+ SwWriter aWriter( aStream, *this);
+ xWrt->SetShowProgress(false);
+
+ switch( nHndlParaBrk )
+ {
+ case ParaBreakType::ToBlank:
+ xWrt->m_bASCII_ParaAsBlank = true;
+ xWrt->m_bASCII_NoLastLineEnd = true;
+ break;
+
+ case ParaBreakType::ToOnlyCR:
+ xWrt->m_bASCII_ParaAsCR = true;
+ xWrt->m_bASCII_NoLastLineEnd = true;
+ break;
+ }
+
+ //JP 09.05.00: write as UNICODE ! (and not as ANSI)
+ SwAsciiOptions aAsciiOpt( xWrt->GetAsciiOptions() );
+ aAsciiOpt.SetCharSet( RTL_TEXTENCODING_UCS2 );
+ xWrt->SetAsciiOptions( aAsciiOpt );
+ xWrt->m_bUCS2_WithStartChar = false;
+ xWrt->m_bHideDeleteRedlines = GetLayout()->IsHideRedlines();
+
+ if ( ! aWriter.Write(xWrt).IsError() )
+ {
+ aStream.WriteUInt16( '\0' );
+
+ const sal_Unicode *p = static_cast<sal_Unicode const *>(aStream.GetData());
+ if (p)
+ rBuf = OUString(p);
+ else
+ {
+ const sal_uInt64 nLen = aStream.GetSize();
+ OSL_ENSURE( nLen/sizeof( sal_Unicode )<o3tl::make_unsigned(SAL_MAX_INT32), "Stream can't fit in OUString" );
+ rtl_uString *pStr = rtl_uString_alloc(static_cast<sal_Int32>(nLen / sizeof( sal_Unicode )));
+ aStream.Seek( 0 );
+ aStream.ResetError();
+ //endian specific?, yipes!
+ aStream.ReadBytes(pStr->buffer, nLen);
+ rBuf = OUString(pStr, SAL_NO_ACQUIRE);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/editsh.cxx b/sw/source/core/edit/editsh.cxx
new file mode 100644
index 000000000..060c64171
--- /dev/null
+++ b/sw/source/core/edit/editsh.cxx
@@ -0,0 +1,1100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <vcl/commandevent.hxx>
+#include <unotools/charclass.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtinfmt.hxx>
+#include <txtinet.hxx>
+#include <frmfmt.hxx>
+#include <charfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentState.hxx>
+#include <editsh.hxx>
+#include <frame.hxx>
+#include <cntfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <flyfrm.hxx>
+#include <swundo.hxx>
+#include <calc.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <extinput.hxx>
+#include <scriptinfo.hxx>
+#include <unocrsrhelper.hxx>
+#include <section.hxx>
+#include <numrule.hxx>
+#include <SwNodeNum.hxx>
+#include <unocrsr.hxx>
+#include <calbck.hxx>
+
+using namespace com::sun::star;
+
+void SwEditShell::Insert( sal_Unicode c, bool bOnlyCurrCursor )
+{
+ StartAllAction();
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ const bool bSuccess = GetDoc()->getIDocumentContentOperations().InsertString(rPaM, OUString(c));
+ OSL_ENSURE( bSuccess, "Doc->Insert() failed." );
+
+ SaveTableBoxContent( rPaM.GetPoint() );
+ if( bOnlyCurrCursor )
+ break;
+
+ }
+
+ EndAllAction();
+}
+
+void SwEditShell::Insert2(const OUString &rStr, const bool bForceExpandHints )
+{
+ StartAllAction();
+ {
+ const SwInsertFlags nInsertFlags =
+ bForceExpandHints
+ ? (SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
+ : SwInsertFlags::EMPTYEXPAND;
+
+ for(SwPaM& rCurrentCursor : getShellCursor( true )->GetRingContainer())
+ {
+ //OPT: GetSystemCharSet
+ GetDoc()->getIDocumentContentOperations().SetIME(false);
+ const bool bSuccess =
+ GetDoc()->getIDocumentContentOperations().InsertString(rCurrentCursor, rStr, nInsertFlags);
+ OSL_ENSURE( bSuccess, "Doc->Insert() failed." );
+
+ if (bSuccess)
+ {
+ GetDoc()->UpdateRsid( rCurrentCursor, rStr.getLength() );
+
+ // Set paragraph rsid if beginning of paragraph
+ SwTextNode *const pTextNode =
+ rCurrentCursor.GetPoint()->nNode.GetNode().GetTextNode();
+ if( pTextNode && pTextNode->Len() == 1)
+ GetDoc()->UpdateParRsid( pTextNode );
+ }
+
+ SaveTableBoxContent( rCurrentCursor.GetPoint() );
+
+ }
+ }
+
+ // calculate cursor bidi level
+ SwCursor* pTmpCursor = GetCursor_();
+ const bool bDoNotSetBidiLevel = ! pTmpCursor ||
+ ( dynamic_cast<SwUnoCursor*>(pTmpCursor) != nullptr );
+
+ if ( ! bDoNotSetBidiLevel )
+ {
+ SwNode& rNode = pTmpCursor->GetPoint()->nNode.GetNode();
+ if ( rNode.IsTextNode() )
+ {
+ SwIndex& rIdx = pTmpCursor->GetPoint()->nContent;
+ sal_Int32 nPrevPos = rIdx.GetIndex();
+ if ( nPrevPos )
+ --nPrevPos;
+
+ SwTextFrame const* pFrame;
+ SwScriptInfo *const pSI = SwScriptInfo::GetScriptInfo(
+ static_cast<SwTextNode&>(rNode), &pFrame, true);
+
+ sal_uInt8 nLevel = 0;
+ if ( ! pSI )
+ {
+ // seems to be an empty paragraph.
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ pFrame = static_cast<SwTextFrame*>(
+ static_cast<SwTextNode&>(rNode).getLayoutFrame(
+ GetLayout(), pTmpCursor->GetPoint(), &tmp));
+
+ SwScriptInfo aScriptInfo;
+ aScriptInfo.InitScriptInfo(static_cast<SwTextNode&>(rNode),
+ pFrame->GetMergedPara(), pFrame->IsRightToLeft());
+ TextFrameIndex const iPrevPos(pFrame->MapModelToView(
+ &static_cast<SwTextNode&>(rNode), nPrevPos));
+ nLevel = aScriptInfo.DirType( iPrevPos );
+ }
+ else
+ {
+ if (TextFrameIndex(COMPLETE_STRING) != pSI->GetInvalidityA())
+ {
+ // mystery why this doesn't use the other overload?
+ pSI->InitScriptInfo(static_cast<SwTextNode&>(rNode), pFrame->GetMergedPara());
+ }
+ TextFrameIndex const iPrevPos(pFrame->MapModelToView(
+ &static_cast<SwTextNode&>(rNode), nPrevPos));
+ nLevel = pSI->DirType(iPrevPos);
+ }
+
+ pTmpCursor->SetCursorBidiLevel( nLevel );
+ }
+ }
+
+ SetInFrontOfLabel( false ); // #i27615#
+
+ EndAllAction();
+}
+
+void SwEditShell::Overwrite(const OUString &rStr)
+{
+ StartAllAction();
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( !GetDoc()->getIDocumentContentOperations().Overwrite(rPaM, rStr ) )
+ {
+ OSL_FAIL( "Doc->getIDocumentContentOperations().Overwrite(Str) failed." );
+ }
+ SaveTableBoxContent( rPaM.GetPoint() );
+ }
+ EndAllAction();
+}
+
+void SwEditShell::SplitNode( bool bAutoFormat, bool bCheckTableStart )
+{
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ // Here, a table cell becomes a normal text cell.
+ GetDoc()->ClearBoxNumAttrs( rPaM.GetPoint()->nNode );
+ GetDoc()->getIDocumentContentOperations().SplitNode( *rPaM.GetPoint(), bCheckTableStart );
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ if( bAutoFormat )
+ AutoFormatBySplitNode();
+
+ ClearTableBoxContent();
+
+ EndAllAction();
+}
+
+bool SwEditShell::AppendTextNode()
+{
+ bool bRet = false;
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ GetDoc()->ClearBoxNumAttrs( rPaM.GetPoint()->nNode );
+ bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( *rPaM.GetPoint()) || bRet;
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+
+ ClearTableBoxContent();
+
+ EndAllAction();
+ return bRet;
+}
+
+// the returned SwGrfNode pointer is used in GetGraphic() and GetGraphicSize()
+SwGrfNode * SwEditShell::GetGrfNode_() const
+{
+ SwGrfNode *pGrfNode = nullptr;
+ SwPaM* pCursor = GetCursor();
+ if( !pCursor->HasMark() ||
+ pCursor->GetPoint()->nNode == pCursor->GetMark()->nNode )
+ pGrfNode = pCursor->GetPoint()->nNode.GetNode().GetGrfNode();
+
+ return pGrfNode;
+}
+
+// returns a Graphic pointer if CurrentCursor->GetPoint() points to a SwGrfNode and
+// GetMark is not set or points to the same Graphic
+const Graphic* SwEditShell::GetGraphic( bool bWait ) const
+{
+ SwGrfNode* pGrfNode = GetGrfNode_();
+ const Graphic* pGrf( nullptr );
+ if ( pGrfNode )
+ {
+ pGrf = &(pGrfNode->GetGrf(bWait && GraphicType::Default == pGrfNode->GetGrf().GetType()));
+ }
+ return pGrf;
+}
+
+bool SwEditShell::IsLinkedGrfSwapOut() const
+{
+ SwGrfNode *pGrfNode = GetGrfNode_();
+ return pGrfNode &&
+ pGrfNode->IsLinkedFile() &&
+ GraphicType::Default == pGrfNode->GetGrfObj().GetType();
+}
+
+const GraphicObject* SwEditShell::GetGraphicObj() const
+{
+ SwGrfNode* pGrfNode = GetGrfNode_();
+ return pGrfNode ? &(pGrfNode->GetGrfObj()) : nullptr;
+}
+
+const GraphicAttr* SwEditShell::GetGraphicAttr( GraphicAttr& rGA ) const
+{
+ SwGrfNode* pGrfNode = GetGrfNode_();
+ const SwFrame* pFrame = GetCurrFrame(false);
+ return pGrfNode ? &(pGrfNode->GetGraphicAttr( rGA, pFrame )) : nullptr;
+}
+
+GraphicType SwEditShell::GetGraphicType() const
+{
+ SwGrfNode *pGrfNode = GetGrfNode_();
+ return pGrfNode ? pGrfNode->GetGrfObj().GetType() : GraphicType::NONE;
+}
+
+// returns the size of a graphic in <rSz> if CurrentCursor->GetPoint() points to a SwGrfNode and
+// GetMark is not set or points to the same graphic
+bool SwEditShell::GetGrfSize(Size& rSz) const
+{
+ SwNoTextNode* pNoTextNd;
+ SwPaM* pCurrentCursor = GetCursor();
+ if( ( !pCurrentCursor->HasMark()
+ || pCurrentCursor->GetPoint()->nNode == pCurrentCursor->GetMark()->nNode )
+ && nullptr != ( pNoTextNd = pCurrentCursor->GetNode().GetNoTextNode() ) )
+ {
+ rSz = pNoTextNd->GetTwipSize();
+ return true;
+ }
+ return false;
+
+}
+
+/// Read again if graphic is not OK and replace old one
+void SwEditShell::ReRead( const OUString& rGrfName, const OUString& rFltName,
+ const Graphic* pGraphic )
+{
+ StartAllAction();
+ mxDoc->getIDocumentContentOperations().ReRead( *GetCursor(), rGrfName, rFltName, pGraphic );
+ EndAllAction();
+}
+
+/// Returns the name and the filter name of a graphic if the pointer is on a graphic.
+/// If a String-pointer is != 0 then return corresponding name.
+void SwEditShell::GetGrfNms( OUString* pGrfName, OUString* pFltName,
+ const SwFlyFrameFormat* pFormat ) const
+{
+ OSL_ENSURE( pGrfName || pFltName, "No parameters" );
+ if( pFormat )
+ SwDoc::GetGrfNms( *pFormat, pGrfName, pFltName );
+ else
+ {
+ SwGrfNode *pGrfNode = GetGrfNode_();
+ if( pGrfNode && pGrfNode->IsLinkedFile() )
+ pGrfNode->GetFileFilterNms( pGrfName, pFltName );
+ }
+}
+
+const tools::PolyPolygon *SwEditShell::GetGraphicPolygon() const
+{
+ SwNoTextNode *pNd = GetCursor()->GetNode().GetNoTextNode();
+ return pNd->HasContour();
+}
+
+void SwEditShell::SetGraphicPolygon( const tools::PolyPolygon *pPoly )
+{
+ SwNoTextNode *pNd = GetCursor()->GetNode().GetNoTextNode();
+ StartAllAction();
+ pNd->SetContour( pPoly );
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pNd->getLayoutFrame(GetLayout())->GetUpper());
+ const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
+ pFly->GetFormat()->CallSwClientNotify(sw::LegacyModifyHint(&rSur, &rSur));
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+}
+
+void SwEditShell::ClearAutomaticContour()
+{
+ SwNoTextNode *pNd = GetCursor()->GetNode().GetNoTextNode();
+ OSL_ENSURE( pNd, "is no NoTextNode!" );
+ if( pNd->HasAutomaticContour() )
+ {
+ StartAllAction();
+ pNd->SetContour( nullptr );
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pNd->getLayoutFrame(GetLayout())->GetUpper());
+ const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
+ pFly->GetFormat()->CallSwClientNotify(sw::LegacyModifyHint(&rSur, &rSur));
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+ }
+}
+
+/** Get OLE object at pointer.
+ *
+ * Returns a pointer to a SvInPlaceObjectRef if CurrentCursor->GetPoint() points to a SwOLENode and
+ * GetMark is not set or points to the same object reference. Gets this pointer from the Doc
+ * if the object should be searched by name.
+ */
+svt::EmbeddedObjectRef& SwEditShell::GetOLEObject() const
+{
+ OSL_ENSURE( CNT_OLE == GetCntType(), "GetOLEObj: no OLENode." );
+ OSL_ENSURE( !GetCursor()->HasMark() ||
+ (GetCursor()->HasMark() &&
+ GetCursor()->GetPoint()->nNode == GetCursor()->GetMark()->nNode),
+ "GetOLEObj: no OLENode." );
+
+ SwOLENode *pOLENode = GetCursor()->GetNode().GetOLENode();
+ OSL_ENSURE( pOLENode, "GetOLEObj: no OLENode." );
+ SwOLEObj& rOObj = pOLENode->GetOLEObj();
+ return rOObj.GetObject();
+}
+
+bool SwEditShell::HasOLEObj( std::u16string_view rName ) const
+{
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwNode& rNd = aIdx.GetNode();
+ if( rNd.IsOLENode() &&
+ rName == static_cast<SwOLENode&>(rNd).GetChartTableName() &&
+ static_cast<SwOLENode&>(rNd).getLayoutFrame( GetLayout() ) )
+ return true;
+
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+ return false;
+}
+
+void SwEditShell::SetChartName( const OUString &rName )
+{
+ SwOLENode *pONd = GetCursor()->GetNode().GetOLENode();
+ OSL_ENSURE( pONd, "ChartNode not found" );
+ pONd->SetChartTableName( rName );
+}
+
+void SwEditShell::UpdateCharts( const OUString& rName )
+{
+ GetDoc()->UpdateCharts( rName );
+}
+
+/// change table name
+void SwEditShell::SetTableName( SwFrameFormat& rTableFormat, const OUString &rNewName )
+{
+ GetDoc()->SetTableName( rTableFormat, rNewName );
+}
+
+/// request current word
+OUString SwEditShell::GetCurWord() const
+{
+ const SwPaM& rPaM = *GetCursor();
+ const SwTextNode* pNd = rPaM.GetNode().GetTextNode();
+ if (!pNd)
+ {
+ return OUString();
+ }
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame*>(pNd->getLayoutFrame(GetLayout())));
+ if (pFrame)
+ {
+ return pFrame->GetCurWord(*rPaM.GetPoint());
+ }
+ return OUString();
+}
+
+void SwEditShell::UpdateDocStat( )
+{
+ StartAllAction();
+ GetDoc()->getIDocumentStatistics().UpdateDocStat( false, true );
+ EndAllAction();
+}
+
+const SwDocStat& SwEditShell::GetUpdatedDocStat()
+{
+ StartAllAction();
+ const SwDocStat &rRet = GetDoc()->getIDocumentStatistics().GetUpdatedDocStat( false, true );
+ EndAllAction();
+ return rRet;
+}
+
+/// get the reference of a given name in the Doc
+const SwFormatRefMark* SwEditShell::GetRefMark( std::u16string_view rName ) const
+{
+ return GetDoc()->GetRefMark( rName );
+}
+
+/// get the names of all references in a Doc
+sal_uInt16 SwEditShell::GetRefMarks( std::vector<OUString>* pStrings ) const
+{
+ return GetDoc()->GetRefMarks( pStrings );
+}
+
+OUString SwEditShell::GetDropText( const sal_Int32 nChars ) const
+{
+ /*
+ * pb: made changes for #i74939#
+ *
+ * always return a string even though there is a selection
+ */
+
+ OUString aText;
+ SwPaM* pCursor = GetCursor();
+ if ( IsMultiSelection() )
+ {
+ // if a multi selection exists, search for the first line
+ // -> it is the cursor with the lowest index
+ SwNodeOffset nIndex = pCursor->GetMark()->nNode.GetIndex();
+ bool bPrev = true;
+ SwPaM* pLast = pCursor;
+ SwPaM* pTemp = pCursor;
+ while ( bPrev )
+ {
+ SwPaM* pPrev2 = pTemp->GetPrev();
+ bPrev = ( pPrev2 && pPrev2 != pLast );
+ if ( bPrev )
+ {
+ pTemp = pPrev2;
+ SwNodeOffset nTemp = pPrev2->GetMark()->nNode.GetIndex();
+ if ( nTemp < nIndex )
+ {
+ nIndex = nTemp;
+ pCursor = pPrev2;
+ }
+ }
+ }
+ }
+
+ SwTextNode const*const pTextNd = pCursor->GetNode(false).GetTextNode();
+ if( pTextNd )
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(
+ pTextNd->getLayoutFrame(GetLayout())));
+ SAL_WARN_IF(!pTextFrame, "sw.core", "GetDropText cursor has no frame?");
+ if (pTextFrame)
+ {
+ TextFrameIndex const nDropLen(pTextFrame->GetDropLen(TextFrameIndex(nChars)));
+ aText = pTextFrame->GetText().copy(0, sal_Int32(nDropLen));
+ }
+ }
+
+ return aText;
+}
+
+void SwEditShell::ReplaceDropText( const OUString &rStr, SwPaM* pPaM )
+{
+ SwPaM* pCursor = pPaM ? pPaM : GetCursor();
+ if( !(pCursor->GetPoint()->nNode == pCursor->GetMark()->nNode &&
+ pCursor->GetNode().GetTextNode()->IsTextNode()) )
+ return;
+
+ StartAllAction();
+
+ const SwNodeIndex& rNd = pCursor->GetPoint()->nNode;
+ SwPaM aPam( rNd, rStr.getLength(), rNd, 0 );
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(
+ rNd.GetNode().GetTextNode()->getLayoutFrame(GetLayout())));
+ if (pTextFrame)
+ {
+ *aPam.GetPoint() = pTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ *aPam.GetMark() = pTextFrame->MapViewToModelPos(TextFrameIndex(
+ std::min(rStr.getLength(), pTextFrame->GetText().getLength())));
+ }
+ if( !GetDoc()->getIDocumentContentOperations().Overwrite( aPam, rStr ) )
+ {
+ OSL_FAIL( "Doc->getIDocumentContentOperations().Overwrite(Str) failed." );
+ }
+
+ EndAllAction();
+}
+
+OUString SwEditShell::Calculate()
+{
+ OUStringBuffer aFormel; // the final formula
+ SwCalc aCalc( *GetDoc() );
+ const CharClass& rCC = GetAppCharClass();
+
+ for(SwPaM& rCurrentPaM : GetCursor()->GetNext()->GetRingContainer())
+ {
+ SwTextNode* pTextNd = rCurrentPaM.GetNode().GetTextNode();
+ if(pTextNd)
+ {
+ const SwPosition *pStart = rCurrentPaM.Start(), *pEnd = rCurrentPaM.End();
+ const sal_Int32 nStt = pStart->nContent.GetIndex();
+ OUString aStr = pTextNd->GetExpandText(GetLayout(),
+ nStt, pEnd->nContent.GetIndex() - nStt);
+
+ aStr = rCC.lowercase( aStr );
+
+ bool bValidFields = false;
+ sal_Int32 nPos = 0;
+
+ while( nPos < aStr.getLength() )
+ {
+ sal_Unicode ch = aStr[ nPos++ ];
+ if( rCC.isLetter( aStr, nPos-1 ) || ch == '_' )
+ {
+ sal_Int32 nTmpStt = nPos-1;
+ while( nPos < aStr.getLength() &&
+ 0 != ( ch = aStr[ nPos++ ]) &&
+ (rCC.isLetterNumeric( aStr, nPos - 1 ) ||
+ ch == '_'|| ch == '.' ))
+ ;
+
+ if( nPos < aStr.getLength() )
+ --nPos;
+
+ OUString sVar = aStr.copy( nTmpStt, nPos - nTmpStt );
+ if( !::FindOperator( sVar ) &&
+ (aCalc.GetVarTable().Find(sVar) ||
+ aCalc.VarLook( sVar )) )
+ {
+ if( !bValidFields )
+ {
+ GetDoc()->getIDocumentFieldsAccess().FieldsToCalc( aCalc,
+ pStart->nNode.GetIndex(),
+ pStart->nContent.GetIndex() );
+ bValidFields = true;
+ }
+ aFormel.append("(" + aCalc.GetStrResult( aCalc.VarLook( sVar )->nValue ) + ")");
+ }
+ else
+ aFormel.append(sVar);
+ }
+ else
+ aFormel.append(ch);
+ }
+ }
+ }
+
+ return aCalc.GetStrResult( aCalc.Calculate(aFormel.makeStringAndClear()) );
+}
+
+sfx2::LinkManager& SwEditShell::GetLinkManager()
+{
+ return mxDoc->getIDocumentLinksAdministration().GetLinkManager();
+}
+
+void *SwEditShell::GetIMapInventor() const
+{
+ // The node on which the cursor points should be sufficient as a unique identifier
+ return static_cast<void*>(&(GetCursor()->GetNode()));
+}
+
+// #i73788#
+Graphic SwEditShell::GetIMapGraphic() const
+{
+ // returns always a graphic if the cursor is in a Fly
+ CurrShell aCurr( const_cast<SwEditShell*>(this) );
+ Graphic aRet;
+ SwPaM* pCursor = GetCursor();
+ if ( !pCursor->HasMark() )
+ {
+ SwNode& rNd =pCursor->GetNode();
+ if( rNd.IsGrfNode() )
+ {
+ SwGrfNode & rGrfNode(static_cast<SwGrfNode&>(rNd));
+ aRet = rGrfNode.GetGrf(GraphicType::Default == rGrfNode.GetGrf().GetType());
+ }
+ else if ( rNd.IsOLENode() )
+ {
+ if (const Graphic* pGraphic = static_cast<SwOLENode&>(rNd).GetGraphic())
+ aRet = *pGraphic;
+ }
+ else
+ {
+ SwFlyFrame* pFlyFrame = rNd.GetContentNode()->getLayoutFrame( GetLayout() )->FindFlyFrame();
+ if(pFlyFrame)
+ aRet = pFlyFrame->GetFormat()->MakeGraphic();
+ }
+ }
+ return aRet;
+}
+
+bool SwEditShell::InsertURL( const SwFormatINetFormat& rFormat, const OUString& rStr, bool bKeepSelection )
+{
+ // URL and hint text (directly or via selection) necessary
+ if( rFormat.GetValue().isEmpty() || ( rStr.isEmpty() && !HasSelection() ) )
+ return false;
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_INSERT_URLTXT, nullptr);
+ bool bInsText = true;
+
+ if( !rStr.isEmpty() )
+ {
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->HasMark() && *pCursor->GetPoint() != *pCursor->GetMark() )
+ {
+ // Selection existent, multi selection?
+ bool bDelText = true;
+ if( !pCursor->IsMultiSelection() )
+ {
+ // simple selection -> check the text
+ const OUString sText(comphelper::string::stripEnd(GetSelText(), ' '));
+ if( sText == rStr )
+ bDelText = bInsText = false;
+ }
+ else if( rFormat.GetValue() == rStr ) // Are Name and URL equal?
+ bDelText = bInsText = false;
+
+ if( bDelText )
+ Delete(true);
+ }
+ else if( pCursor->IsMultiSelection() && rFormat.GetValue() == rStr )
+ bInsText = false;
+
+ if( bInsText )
+ {
+ Insert2( rStr );
+ SetMark();
+ ExtendSelection( false, rStr.getLength() );
+ }
+ }
+ else
+ bInsText = false;
+
+ SetAttrItem( rFormat );
+ if (bInsText && !IsCursorPtAtEnd())
+ SwapPam();
+ if(!bKeepSelection)
+ ClearMark();
+ if( bInsText )
+ DontExpandFormat();
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_INSERT_URLTXT, nullptr );
+ EndAllAction();
+ return true;
+}
+
+void SwEditShell::GetINetAttrs(SwGetINetAttrs& rArr, bool bIncludeInToxContent)
+{
+ rArr.clear();
+
+ const SwCharFormats* pFormats = GetDoc()->GetCharFormats();
+ for( auto n = pFormats->size(); 1 < n; )
+ {
+ SwIterator<SwTextINetFormat,SwCharFormat> aIter(*(*pFormats)[--n]);
+ for( SwTextINetFormat* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
+ {
+ SwTextNode const*const pTextNd(pFnd->GetpTextNode());
+ SwTextFrame const*const pFrame(pTextNd
+ ? static_cast<SwTextFrame const*>(pTextNd->getLayoutFrame(GetLayout()))
+ : nullptr);
+ if (nullptr != pTextNd && nullptr != pFrame
+ && pTextNd->GetNodes().IsDocNodes()
+ // check it's not fully deleted
+ && pFrame->MapModelToView(pTextNd, pFnd->GetStart())
+ != pFrame->MapModelToView(pTextNd, *pFnd->GetEnd()))
+ {
+ // tdf#52113, tdf#148312 Don't include table of contents hyperlinks in the
+ // Navigator content tree Hyperlinks entries
+ if (!bIncludeInToxContent)
+ {
+ if(const SwSectionNode* pSectNd = pTextNd->FindSectionNode())
+ {
+ SectionType eType = pSectNd->GetSection().GetType();
+ if(SectionType::ToxContent == eType)
+ continue;
+ }
+ }
+
+ SwTextINetFormat& rAttr = *pFnd;
+ OUString sText( pTextNd->GetExpandText(GetLayout(),
+ rAttr.GetStart(), *rAttr.GetEnd() - rAttr.GetStart()) );
+
+ sText = sText.replaceAll("\x0a", "");
+ sText = comphelper::string::strip(sText, ' ');
+
+ if( !sText.isEmpty() )
+ {
+ rArr.emplace_back(sText, rAttr);
+ }
+ }
+ }
+ }
+}
+
+/// If the cursor is in an INetAttribute then it will be deleted completely (incl. hint text, the
+/// latter is needed for drag & drop)
+void SwEditShell::DelINetAttrWithText()
+{
+ bool bRet = SelectTextAttr( RES_TXTATR_INETFMT, false );
+ if( bRet )
+ DeleteSel(*GetCursor(), true);
+}
+
+/// Set the DontExpand flag at the text character attributes
+bool SwEditShell::DontExpandFormat()
+{
+ bool bRet = false;
+ if( !IsTableMode() && GetDoc()->DontExpandFormat( *GetCursor()->GetPoint() ))
+ {
+ bRet = true;
+ CallChgLnk();
+ }
+ return bRet;
+}
+
+SvNumberFormatter* SwEditShell::GetNumberFormatter()
+{
+ return GetDoc()->GetNumberFormatter();
+}
+
+bool SwEditShell::ConvertFieldsToText()
+{
+ StartAllAction();
+ bool bRet = GetDoc()->ConvertFieldsToText(*GetLayout());
+ EndAllAction();
+ return bRet;
+}
+
+void SwEditShell::SetNumberingRestart()
+{
+ StartAllAction();
+ Push();
+ // iterate over all text contents - body, frames, header, footer, footnote text
+ SwPaM* pCursor = GetCursor();
+ for(int i = 0; i < 2; i++)
+ {
+ if(!i)
+ MakeFindRange(SwDocPositions::Start, SwDocPositions::End, pCursor); // body content
+ else
+ MakeFindRange(SwDocPositions::OtherStart, SwDocPositions::OtherEnd, pCursor); // extra content
+ SwPosition* pSttPos = pCursor->Start(), *pEndPos = pCursor->End();
+ SwNodeOffset nCurrNd = pSttPos->nNode.GetIndex();
+ SwNodeOffset nEndNd = pEndPos->nNode.GetIndex();
+ if( nCurrNd <= nEndNd )
+ {
+ SwContentFrame* pContentFrame;
+ bool bGoOn = true;
+ // iterate over all paragraphs
+ while( bGoOn )
+ {
+ SwNode* pNd = GetDoc()->GetNodes()[ nCurrNd ];
+ switch( pNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ pContentFrame = static_cast<SwTextNode*>(pNd)->getLayoutFrame( GetLayout() );
+ if( nullptr != pContentFrame )
+ {
+ // skip hidden frames - ignore protection!
+ if( !static_cast<SwTextFrame*>(pContentFrame)->IsHiddenNow() )
+ {
+ // if the node is numbered and the starting value of the numbering equals the
+ // start value of the numbering rule then set this value as hard starting value
+
+ // get the node num
+ // OD 2005-11-09
+ SwTextNode* pTextNd( pNd->GetTextNode() );
+ SwNumRule* pNumRule( pTextNd->GetNumRule() );
+
+ // sw_redlinehide: not sure what this should do, only called from mail-merge
+ bool bIsNodeNum =
+ ( pNumRule && pTextNd->GetNum() &&
+ ( pTextNd->HasNumber() || pTextNd->HasBullet() ) &&
+ pTextNd->IsCountedInList() &&
+ !pTextNd->IsListRestart() );
+ if (bIsNodeNum)
+ {
+ int nListLevel = pTextNd->GetActualListLevel();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ bIsNodeNum = pTextNd->GetNum()->GetNumber() ==
+ pNumRule->Get( o3tl::narrowing<sal_uInt16>(nListLevel) ).GetStart();
+ }
+ if (bIsNodeNum)
+ {
+ // now set the start value as attribute
+ SwPosition aCurrentNode(*pNd);
+ GetDoc()->SetNumRuleStart( aCurrentNode );
+ }
+ }
+ }
+ break;
+ case SwNodeType::Section:
+ // skip hidden sections - ignore protection!
+ if(static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() )
+ nCurrNd = pNd->EndOfSectionIndex();
+ break;
+ default: break;
+ }
+
+ bGoOn = nCurrNd < nEndNd;
+ ++nCurrNd;
+ }
+ }
+ }
+
+ Pop(PopMode::DeleteCurrent);
+ EndAllAction();
+}
+
+sal_uInt16 SwEditShell::GetLineCount()
+{
+ sal_uInt16 nRet = 0;
+ CalcLayout();
+ SwPaM* pPam = GetCursor();
+ SwNodeIndex& rPtIdx = pPam->GetPoint()->nNode;
+ SwNodeIndex aStart( rPtIdx );
+ SwContentNode* pCNd;
+ SwContentFrame *pContentFrame = nullptr;
+
+ aStart = SwNodeOffset(0);
+
+ while( nullptr != ( pCNd = GetDoc()->GetNodes().GoNextSection(
+ &aStart, true, false )) )
+ {
+ if( nullptr != ( pContentFrame = pCNd->getLayoutFrame( GetLayout() ) ) && pContentFrame->IsTextFrame() )
+ {
+ SwTextFrame *const pFrame(static_cast<SwTextFrame*>(pContentFrame));
+ nRet = nRet + pFrame->GetLineCount(TextFrameIndex(COMPLETE_STRING));
+ if (GetLayout()->HasMergedParas())
+ {
+ if (auto const*const pMerged = pFrame->GetMergedPara())
+ {
+ aStart = *pMerged->pLastNode;
+ }
+ }
+ }
+ }
+ return nRet;
+}
+
+tools::Long SwEditShell::CompareDoc( const SwDoc& rDoc )
+{
+ StartAllAction();
+ tools::Long nRet = GetDoc()->CompareDoc( rDoc );
+ EndAllAction();
+ return nRet;
+}
+
+tools::Long SwEditShell::MergeDoc( const SwDoc& rDoc )
+{
+ StartAllAction();
+ tools::Long nRet = GetDoc()->MergeDoc( rDoc );
+ EndAllAction();
+ return nRet;
+}
+
+const SwFootnoteInfo& SwEditShell::GetFootnoteInfo() const
+{
+ return GetDoc()->GetFootnoteInfo();
+}
+
+void SwEditShell::SetFootnoteInfo(const SwFootnoteInfo& rInfo)
+{
+ StartAllAction();
+ CurrShell aCurr( this );
+ GetDoc()->SetFootnoteInfo(rInfo);
+ CallChgLnk();
+ EndAllAction();
+}
+
+const SwEndNoteInfo& SwEditShell::GetEndNoteInfo() const
+{
+ return GetDoc()->GetEndNoteInfo();
+}
+
+void SwEditShell::SetEndNoteInfo(const SwEndNoteInfo& rInfo)
+{
+ StartAllAction();
+ CurrShell aCurr( this );
+ GetDoc()->SetEndNoteInfo(rInfo);
+ EndAllAction();
+}
+
+const SwLineNumberInfo& SwEditShell::GetLineNumberInfo() const
+{
+ return GetDoc()->GetLineNumberInfo();
+}
+
+void SwEditShell::SetLineNumberInfo(const SwLineNumberInfo& rInfo)
+{
+ StartAllAction();
+ CurrShell aCurr( this );
+ GetDoc()->SetLineNumberInfo(rInfo);
+ AddPaintRect( GetLayout()->getFrameArea() );
+ EndAllAction();
+}
+
+sal_uInt16 SwEditShell::GetLinkUpdMode() const
+{
+ return getIDocumentSettingAccess().getLinkUpdateMode( false );
+}
+
+void SwEditShell::SetLinkUpdMode( sal_uInt16 nMode )
+{
+ getIDocumentSettingAccess().setLinkUpdateMode( nMode );
+}
+
+// Interface for TextInputData - (for text input of japanese/chinese characters)
+void SwEditShell::CreateExtTextInput(LanguageType eInputLanguage)
+{
+ SwExtTextInput* pRet = GetDoc()->CreateExtTextInput( *GetCursor() );
+ pRet->SetLanguage(eInputLanguage);
+ pRet->SetOverwriteCursor( SwCursorShell::IsOverwriteCursor() );
+}
+
+OUString SwEditShell::DeleteExtTextInput( bool bInsText )
+{
+ const SwPosition& rPos = *GetCursor()->GetPoint();
+ SwExtTextInput* pDel = GetDoc()->GetExtTextInput( rPos.nNode.GetNode(),
+ rPos.nContent.GetIndex() );
+ if( !pDel )
+ {
+ //JP 25.10.2001: under UNIX the cursor is moved before the Input-
+ // Engine event comes in. So take any - normally there
+ // exist only one at the time. -- Task 92016
+ pDel = GetDoc()->GetExtTextInput();
+ }
+ OUString sRet;
+ if( pDel )
+ {
+ OUString sTmp;
+ SwUnoCursorHelper::GetTextFromPam(*pDel, sTmp);
+ sRet = sTmp;
+ CurrShell aCurr( this );
+ StartAllAction();
+ pDel->SetInsText( bInsText );
+ SetOverwriteCursor( pDel->IsOverwriteCursor() );
+ const SwPosition aPos( *pDel->GetPoint() );
+ GetDoc()->DeleteExtTextInput( pDel );
+
+ // In this case, the "replace" function did not set the cursor
+ // to the original position. Therefore we have to do this manually.
+ if ( ! bInsText && IsOverwriteCursor() )
+ *GetCursor()->GetPoint() = aPos;
+
+ EndAllAction();
+ }
+ return sRet;
+}
+
+void SwEditShell::SetExtTextInputData( const CommandExtTextInputData& rData )
+{
+ SwPaM* pCurrentCursor = GetCursor();
+ const SwPosition& rPos = *pCurrentCursor->GetPoint();
+ SwExtTextInput* pInput = GetDoc()->GetExtTextInput( rPos.nNode.GetNode() );
+ if( !pInput )
+ return;
+
+ StartAllAction();
+ CurrShell aCurr( this );
+
+ if( !rData.IsOnlyCursorChanged() )
+ pInput->SetInputData( rData );
+ // position cursor
+ const SwPosition& rStt = *pInput->Start();
+ const sal_Int32 nNewCursorPos = rStt.nContent.GetIndex() + rData.GetCursorPos();
+
+ // ugly but works
+ ShowCursor();
+ const sal_Int32 nDiff = nNewCursorPos - rPos.nContent.GetIndex();
+ if( nDiff != 0)
+ {
+ bool bLeft = nDiff < 0;
+ sal_Int32 nMaxGuard = std::abs(nDiff);
+ while (true)
+ {
+ auto nOldPos = pCurrentCursor->GetPoint()->nContent.GetIndex();
+ if (bLeft)
+ Left(1, CRSR_SKIP_CHARS);
+ else
+ Right(1, CRSR_SKIP_CHARS);
+ auto nNewPos = pCurrentCursor->GetPoint()->nContent.GetIndex();
+
+ // expected success
+ if (nNewPos == nNewCursorPos)
+ break;
+
+ if (nNewPos == nOldPos)
+ {
+ // if there was no movement, we have failed for some reason
+ SAL_WARN("sw.core", "IM cursor move failed");
+ break;
+ }
+
+ if (--nMaxGuard == 0)
+ {
+ // if it takes more cursor moves than there are utf-16 chars to move past
+ // something has probably gone wrong
+ SAL_WARN("sw.core", "IM abandoning cursor positioning");
+ break;
+ }
+ }
+ }
+
+ SetOverwriteCursor( rData.IsCursorOverwrite() );
+
+ EndAllAction();
+
+ if( !rData.IsCursorVisible() ) // must be called after the EndAction
+ HideCursor();
+
+}
+
+void SwEditShell::TransliterateText( TransliterationFlags nType )
+{
+ utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), nType );
+ StartAllAction();
+ CurrShell aCurr( this );
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor )
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() )
+ GetDoc()->getIDocumentContentOperations().TransliterateText( rPaM, aTrans );
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ }
+ else
+ GetDoc()->getIDocumentContentOperations().TransliterateText( *pCursor, aTrans );
+
+ EndAllAction();
+}
+
+void SwEditShell::CountWords( SwDocStat& rStat ) const
+{
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() )
+ SwDoc::CountWords( rPaM, rStat );
+
+ }
+}
+
+void SwEditShell::ApplyViewOptions( const SwViewOption &rOpt )
+{
+ SwCursorShell::StartAction();
+ SwViewShell::ApplyViewOptions( rOpt );
+ SwEditShell::EndAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edlingu.cxx b/sw/source/core/edit/edlingu.cxx
new file mode 100644
index 000000000..3e3fe0a63
--- /dev/null
+++ b/sw/source/core/edit/edlingu.cxx
@@ -0,0 +1,1729 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/linguistic2/ProofreadingResult.hpp>
+#include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
+#include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
+#include <com/sun/star/linguistic2/XLinguProperties.hpp>
+#include <com/sun/star/text/XFlatParagraph.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <o3tl/any.hxx>
+
+#include <unoflatpara.hxx>
+
+#include <strings.hrc>
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <unotools/linguprops.hxx>
+#include <linguistic/lngprops.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/SpellPortions.hxx>
+#include <svl/languageoptions.hxx>
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <ndtxt.hxx>
+#include <viewopt.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <mdiexp.hxx>
+#include <cntfrm.hxx>
+#include <splargs.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <docsh.hxx>
+#include <txatbase.hxx>
+#include <txtfrm.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::svx;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+
+namespace {
+
+class SwLinguIter
+{
+ SwEditShell* m_pSh;
+ std::unique_ptr<SwPosition> m_pStart;
+ std::unique_ptr<SwPosition> m_pEnd;
+ std::unique_ptr<SwPosition> m_pCurr;
+ std::unique_ptr<SwPosition> m_pCurrX;
+ sal_uInt16 m_nCursorCount;
+
+public:
+ SwLinguIter();
+
+ SwEditShell* GetSh() { return m_pSh; }
+
+ const SwPosition *GetEnd() const { return m_pEnd.get(); }
+ void SetEnd(SwPosition* pNew) { m_pEnd.reset(pNew); }
+
+ const SwPosition *GetStart() const { return m_pStart.get(); }
+ void SetStart(SwPosition* pNew) { m_pStart.reset(pNew); }
+
+ const SwPosition *GetCurr() const { return m_pCurr.get(); }
+ void SetCurr(SwPosition* pNew) { m_pCurr.reset(pNew); }
+
+ const SwPosition *GetCurrX() const { return m_pCurrX.get(); }
+ void SetCurrX(SwPosition* pNew) { m_pCurrX.reset(pNew); }
+
+ sal_uInt16& GetCursorCnt() { return m_nCursorCount; }
+
+ // for the UI:
+ void Start_( SwEditShell *pSh, SwDocPositions eStart,
+ SwDocPositions eEnd );
+ void End_(bool bRestoreSelection = true);
+};
+
+// #i18881# to be able to identify the positions of the changed words
+// the content positions of each portion need to be saved
+struct SpellContentPosition
+{
+ sal_Int32 nLeft;
+ sal_Int32 nRight;
+};
+
+}
+
+typedef std::vector<SpellContentPosition> SpellContentPositions;
+
+namespace {
+
+class SwSpellIter : public SwLinguIter
+{
+ uno::Reference<XSpellChecker1> m_xSpeller;
+ svx::SpellPortions m_aLastPortions;
+
+ SpellContentPositions m_aLastPositions;
+ bool m_bBackToStartOfSentence;
+
+ void CreatePortion(uno::Reference< XSpellAlternatives > const & xAlt,
+ const linguistic2::ProofreadingResult* pGrammarResult,
+ bool bIsField, bool bIsHidden);
+
+ void AddPortion(uno::Reference< XSpellAlternatives > const & xAlt,
+ const linguistic2::ProofreadingResult* pGrammarResult,
+ const SpellContentPositions& rDeletedRedlines);
+public:
+ SwSpellIter()
+ : m_bBackToStartOfSentence(false)
+ {
+ }
+
+ void Start( SwEditShell *pSh, SwDocPositions eStart, SwDocPositions eEnd );
+
+ uno::Any Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt );
+
+ bool SpellSentence(svx::SpellPortions& rPortions, bool bIsGrammarCheck);
+ void ToSentenceStart();
+ const svx::SpellPortions& GetLastPortions() const { return m_aLastPortions; }
+ const SpellContentPositions& GetLastPositions() const { return m_aLastPositions; }
+};
+
+/// used for text conversion
+class SwConvIter : public SwLinguIter
+{
+ SwConversionArgs& m_rArgs;
+
+public:
+ explicit SwConvIter(SwConversionArgs& rConvArgs)
+ : m_rArgs(rConvArgs)
+ {
+ }
+
+ void Start( SwEditShell *pSh, SwDocPositions eStart, SwDocPositions eEnd );
+
+ uno::Any Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt );
+};
+
+class SwHyphIter : public SwLinguIter
+{
+ // With that we save a GetFrame() in Hyphenate //TODO: does it actually matter?
+ const SwTextNode *m_pLastNode;
+ SwTextFrame *m_pLastFrame;
+ friend SwTextFrame * sw::SwHyphIterCacheLastTextFrame(SwTextNode const * pNode, const sw::Creator& rCreator);
+
+ bool m_bOldIdle;
+ static void DelSoftHyph( SwPaM &rPam );
+
+public:
+ SwHyphIter()
+ : m_pLastNode(nullptr)
+ , m_pLastFrame(nullptr)
+ , m_bOldIdle(false)
+ {
+ }
+
+ void Start( SwEditShell *pSh, SwDocPositions eStart, SwDocPositions eEnd );
+ void End();
+
+ void Ignore();
+
+ uno::Any Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt );
+
+ static bool IsAuto();
+ void InsertSoftHyph( const sal_Int32 nHyphPos );
+ void ShowSelection();
+};
+
+}
+
+static SwSpellIter* g_pSpellIter = nullptr;
+static SwConvIter* g_pConvIter = nullptr;
+static SwHyphIter* g_pHyphIter = nullptr;
+
+SwLinguIter::SwLinguIter()
+ : m_pSh(nullptr)
+ , m_nCursorCount(0)
+{
+ // TODO missing: ensurance of re-entrance, OSL_ENSURE( etc.
+}
+
+void SwLinguIter::Start_( SwEditShell *pShell, SwDocPositions eStart,
+ SwDocPositions eEnd )
+{
+ // TODO missing: ensurance of re-entrance, locking
+ if (m_pSh)
+ return;
+
+ bool bSetCurr;
+
+ m_pSh = pShell;
+
+ CurrShell aCurr(m_pSh);
+
+ OSL_ENSURE(!m_pEnd, "SwLinguIter::Start_ without End?");
+
+ SwPaM* pCursor = m_pSh->GetCursor();
+
+ if( pShell->HasSelection() || pCursor != pCursor->GetNext() )
+ {
+ bSetCurr = nullptr != GetCurr();
+ m_nCursorCount = m_pSh->GetCursorCnt();
+ if (m_pSh->IsTableMode())
+ m_pSh->TableCursorToCursor();
+
+ m_pSh->Push();
+ sal_uInt16 n;
+ for (n = 0; n < m_nCursorCount; ++n)
+ {
+ m_pSh->Push();
+ m_pSh->DestroyCursor();
+ }
+ m_pSh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ else
+ {
+ bSetCurr = false;
+ m_nCursorCount = 1;
+ m_pSh->Push();
+ m_pSh->SetLinguRange(eStart, eEnd);
+ }
+
+ pCursor = m_pSh->GetCursor();
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+
+ m_pStart.reset(new SwPosition(*pCursor->GetPoint()));
+ m_pEnd.reset(new SwPosition(*pCursor->GetMark()));
+ if( bSetCurr )
+ {
+ SwPosition* pNew = new SwPosition( *GetStart() );
+ SetCurr( pNew );
+ pNew = new SwPosition( *pNew );
+ SetCurrX( pNew );
+ }
+
+ pCursor->SetMark();
+}
+
+void SwLinguIter::End_(bool bRestoreSelection)
+{
+ if (!m_pSh)
+ return;
+
+ OSL_ENSURE(m_pEnd, "SwLinguIter::End_ without end?");
+ if(bRestoreSelection)
+ {
+ while (m_nCursorCount--)
+ m_pSh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+
+ m_pSh->KillPams();
+ m_pSh->ClearMark();
+ }
+ m_pStart.reset();
+ m_pEnd.reset();
+ m_pCurr.reset();
+ m_pCurrX.reset();
+
+ m_pSh = nullptr;
+}
+
+void SwSpellIter::Start( SwEditShell *pShell, SwDocPositions eStart,
+ SwDocPositions eEnd )
+{
+ if( GetSh() )
+ return;
+
+ m_xSpeller = ::GetSpellChecker();
+ if (m_xSpeller.is())
+ Start_( pShell, eStart, eEnd );
+ m_aLastPortions.clear();
+ m_aLastPositions.clear();
+}
+
+// This method is the origin of SwEditShell::SpellContinue()
+uno::Any SwSpellIter::Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ //!!
+ //!! Please check SwConvIter also when modifying this
+ //!!
+
+ uno::Any aSpellRet;
+ SwEditShell *pMySh = GetSh();
+ if( !pMySh )
+ return aSpellRet;
+
+ OSL_ENSURE( GetEnd(), "SwSpellIter::Continue without start?");
+
+ uno::Reference< uno::XInterface > xSpellRet;
+ bool bGoOn = true;
+ do {
+ SwPaM *pCursor = pMySh->GetCursor();
+ if ( !pCursor->HasMark() )
+ pCursor->SetMark();
+
+ *pMySh->GetCursor()->GetPoint() = *GetCurr();
+ *pMySh->GetCursor()->GetMark() = *GetEnd();
+ pMySh->GetDoc()->Spell(*pMySh->GetCursor(), m_xSpeller, pPageCnt, pPageSt, false,
+ pMySh->GetLayout())
+ >>= xSpellRet;
+ bGoOn = GetCursorCnt() > 1;
+ if( xSpellRet.is() )
+ {
+ bGoOn = false;
+ SwPosition* pNewPoint = new SwPosition( *pCursor->GetPoint() );
+ SwPosition* pNewMark = new SwPosition( *pCursor->GetMark() );
+ SetCurr( pNewPoint );
+ SetCurrX( pNewMark );
+ }
+ if( bGoOn )
+ {
+ pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ pCursor = pMySh->GetCursor();
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ SwPosition* pNew = new SwPosition( *pCursor->GetPoint() );
+ SetStart( pNew );
+ pNew = new SwPosition( *pCursor->GetMark() );
+ SetEnd( pNew );
+ pNew = new SwPosition( *GetStart() );
+ SetCurr( pNew );
+ pNew = new SwPosition( *pNew );
+ SetCurrX( pNew );
+ pCursor->SetMark();
+ --GetCursorCnt();
+ }
+ }while ( bGoOn );
+ aSpellRet <<= xSpellRet;
+ return aSpellRet;
+}
+
+void SwConvIter::Start( SwEditShell *pShell, SwDocPositions eStart,
+ SwDocPositions eEnd )
+{
+ if( GetSh() )
+ return;
+ Start_( pShell, eStart, eEnd );
+}
+
+uno::Any SwConvIter::Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ //!!
+ //!! Please check SwSpellIter also when modifying this
+ //!!
+
+ uno::Any aConvRet{ OUString() };
+ SwEditShell *pMySh = GetSh();
+ if( !pMySh )
+ return aConvRet;
+
+ OSL_ENSURE( GetEnd(), "SwConvIter::Continue() without Start?");
+
+ OUString aConvText;
+ bool bGoOn = true;
+ do {
+ SwPaM *pCursor = pMySh->GetCursor();
+ if ( !pCursor->HasMark() )
+ pCursor->SetMark();
+
+ *pMySh->GetCursor()->GetPoint() = *GetCurr();
+ *pMySh->GetCursor()->GetMark() = *GetEnd();
+
+ // call function to find next text portion to be converted
+ uno::Reference< linguistic2::XSpellChecker1 > xEmpty;
+ pMySh->GetDoc()->Spell(*pMySh->GetCursor(), xEmpty, pPageCnt, pPageSt, false,
+ pMySh->GetLayout(), &m_rArgs)
+ >>= aConvText;
+
+ bGoOn = GetCursorCnt() > 1;
+ if( !aConvText.isEmpty() )
+ {
+ bGoOn = false;
+ SwPosition* pNewPoint = new SwPosition( *pCursor->GetPoint() );
+ SwPosition* pNewMark = new SwPosition( *pCursor->GetMark() );
+
+ SetCurr( pNewPoint );
+ SetCurrX( pNewMark );
+ }
+ if( bGoOn )
+ {
+ pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ pCursor = pMySh->GetCursor();
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ SwPosition* pNew = new SwPosition( *pCursor->GetPoint() );
+ SetStart( pNew );
+ pNew = new SwPosition( *pCursor->GetMark() );
+ SetEnd( pNew );
+ pNew = new SwPosition( *GetStart() );
+ SetCurr( pNew );
+ pNew = new SwPosition( *pNew );
+ SetCurrX( pNew );
+ pCursor->SetMark();
+ --GetCursorCnt();
+ }
+ }while ( bGoOn );
+ return Any( aConvText );
+}
+
+bool SwHyphIter::IsAuto()
+{
+ uno::Reference< beans::XPropertySet > xProp( ::GetLinguPropertySet() );
+ return xProp.is() && *o3tl::doAccess<bool>(xProp->getPropertyValue(
+ UPN_IS_HYPH_AUTO ));
+}
+
+void SwHyphIter::ShowSelection()
+{
+ SwEditShell *pMySh = GetSh();
+ if( pMySh )
+ {
+ pMySh->StartAction();
+ // Caution! Due to EndAction() formatting is started which can lead to the fact that new
+ // words are added to/set in the Hyphenator. Thus: save!
+ pMySh->EndAction();
+ }
+}
+
+void SwHyphIter::Start( SwEditShell *pShell, SwDocPositions eStart, SwDocPositions eEnd )
+{
+ // robust
+ if( GetSh() || GetEnd() )
+ {
+ OSL_ENSURE( !GetSh(), "SwHyphIter::Start: missing HyphEnd()" );
+ return;
+ }
+
+ // nothing to do (at least not in the way as in the "else" part)
+ m_bOldIdle = pShell->GetViewOptions()->IsIdle();
+ pShell->GetViewOptions()->SetIdle( false );
+ Start_( pShell, eStart, eEnd );
+}
+
+// restore selections
+void SwHyphIter::End()
+{
+ if( !GetSh() )
+ return;
+ GetSh()->GetViewOptions()->SetIdle(m_bOldIdle);
+ End_();
+}
+
+uno::Any SwHyphIter::Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ uno::Any aHyphRet;
+ SwEditShell *pMySh = GetSh();
+ if( !pMySh )
+ return aHyphRet;
+
+ const bool bAuto = IsAuto();
+ uno::Reference< XHyphenatedWord > xHyphWord;
+ bool bGoOn = false;
+ do {
+ SwPaM *pCursor;
+ do {
+ OSL_ENSURE( GetEnd(), "SwHyphIter::Continue without Start?" );
+ pCursor = pMySh->GetCursor();
+ if ( !pCursor->HasMark() )
+ pCursor->SetMark();
+ if ( *pCursor->GetPoint() < *pCursor->GetMark() )
+ {
+ pCursor->Exchange();
+ pCursor->SetMark();
+ }
+
+ if ( *pCursor->End() <= *GetEnd() )
+ {
+ *pCursor->GetMark() = *GetEnd();
+
+ // Do we need to break the word at the current cursor position?
+ const Point aCursorPos( pMySh->GetCharRect().Pos() );
+ xHyphWord = pMySh->GetDoc()->Hyphenate( pCursor, aCursorPos,
+ pPageCnt, pPageSt );
+ }
+
+ if( bAuto && xHyphWord.is() )
+ {
+ SwEditShell::InsertSoftHyph( xHyphWord->getHyphenationPos() + 1);
+ }
+ } while( bAuto && xHyphWord.is() ); //end of do-while
+ bGoOn = !xHyphWord.is() && GetCursorCnt() > 1;
+
+ if( bGoOn )
+ {
+ pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ pCursor = pMySh->GetCursor();
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ SwPosition* pNew = new SwPosition(*pCursor->End());
+ SetEnd( pNew );
+ pCursor->SetMark();
+ --GetCursorCnt();
+ }
+ } while ( bGoOn );
+ aHyphRet <<= xHyphWord;
+ return aHyphRet;
+}
+
+/// ignore hyphenation
+void SwHyphIter::Ignore()
+{
+ SwEditShell *pMySh = GetSh();
+ SwPaM *pCursor = pMySh->GetCursor();
+
+ // delete old SoftHyphen
+ DelSoftHyph( *pCursor );
+
+ // and continue
+ pCursor->Start()->nContent = pCursor->End()->nContent;
+ pCursor->SetMark();
+}
+
+void SwHyphIter::DelSoftHyph( SwPaM &rPam )
+{
+ const SwPosition* pStt = rPam.Start();
+ const sal_Int32 nStart = pStt->nContent.GetIndex();
+ const sal_Int32 nEnd = rPam.End()->nContent.GetIndex();
+ SwTextNode *pNode = pStt->nNode.GetNode().GetTextNode();
+ pNode->DelSoftHyph( nStart, nEnd );
+}
+
+void SwHyphIter::InsertSoftHyph( const sal_Int32 nHyphPos )
+{
+ SwEditShell *pMySh = GetSh();
+ OSL_ENSURE( pMySh, "SwHyphIter::InsertSoftHyph: missing HyphStart()");
+ if( !pMySh )
+ return;
+
+ SwPaM *pCursor = pMySh->GetCursor();
+ SwPosition* pSttPos = pCursor->Start();
+ SwPosition* pEndPos = pCursor->End();
+
+ const sal_Int32 nLastHyphLen = GetEnd()->nContent.GetIndex() -
+ pSttPos->nContent.GetIndex();
+
+ if( pSttPos->nNode != pEndPos->nNode || !nLastHyphLen )
+ {
+ OSL_ENSURE( pSttPos->nNode == pEndPos->nNode,
+ "SwHyphIter::InsertSoftHyph: node warp during hyphenation" );
+ OSL_ENSURE(nLastHyphLen, "SwHyphIter::InsertSoftHyph: missing HyphContinue()");
+ *pSttPos = *pEndPos;
+ return;
+ }
+
+ pMySh->StartAction();
+ {
+ SwDoc *pDoc = pMySh->GetDoc();
+ DelSoftHyph( *pCursor );
+ pSttPos->nContent += nHyphPos;
+ SwPaM aRg( *pSttPos );
+ pDoc->getIDocumentContentOperations().InsertString( aRg, OUString(CHAR_SOFTHYPHEN) );
+ }
+ // revoke selection
+ pCursor->DeleteMark();
+ pMySh->EndAction();
+ pCursor->SetMark();
+}
+
+namespace sw {
+
+SwTextFrame *
+SwHyphIterCacheLastTextFrame(SwTextNode const * pNode, const sw::Creator& create)
+{
+ assert(g_pHyphIter);
+ if (pNode != g_pHyphIter->m_pLastNode || !g_pHyphIter->m_pLastFrame)
+ {
+ g_pHyphIter->m_pLastNode = pNode;
+ g_pHyphIter->m_pLastFrame = create();
+ }
+ return g_pHyphIter->m_pLastFrame;
+}
+
+}
+
+bool SwEditShell::HasLastSentenceGotGrammarChecked()
+{
+ bool bTextWasGrammarChecked = false;
+ if (g_pSpellIter)
+ {
+ svx::SpellPortions aLastPortions( g_pSpellIter->GetLastPortions() );
+ for (size_t i = 0; i < aLastPortions.size() && !bTextWasGrammarChecked; ++i)
+ {
+ // bIsGrammarError is also true if the text was only checked but no
+ // grammar error was found. (That is if a ProofreadingResult was obtained in
+ // SwDoc::Spell and in turn bIsGrammarError was set in SwSpellIter::CreatePortion)
+ if (aLastPortions[i].bIsGrammarError)
+ bTextWasGrammarChecked = true;
+ }
+ }
+ return bTextWasGrammarChecked;
+}
+
+bool SwEditShell::HasConvIter()
+{
+ return nullptr != g_pConvIter;
+}
+
+bool SwEditShell::HasHyphIter()
+{
+ return nullptr != g_pHyphIter;
+}
+
+void SwEditShell::SetLinguRange( SwDocPositions eStart, SwDocPositions eEnd )
+{
+ SwPaM *pCursor = GetCursor();
+ MakeFindRange( eStart, eEnd, pCursor );
+ if( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+}
+
+void SwEditShell::SpellStart(
+ SwDocPositions eStart, SwDocPositions eEnd, SwDocPositions eCurr,
+ SwConversionArgs *pConvArgs )
+{
+ SwLinguIter *pLinguIter = nullptr;
+
+ // do not spell if interactive spelling is active elsewhere
+ if (!pConvArgs && !g_pSpellIter)
+ {
+ g_pSpellIter = new SwSpellIter;
+ pLinguIter = g_pSpellIter;
+ }
+ // do not do text conversion if it is active elsewhere
+ if (pConvArgs && !g_pConvIter)
+ {
+ g_pConvIter = new SwConvIter( *pConvArgs );
+ pLinguIter = g_pConvIter;
+ }
+
+ if (pLinguIter)
+ {
+ SwCursor* pSwCursor = GetCursor();
+
+ SwPosition *pTmp = new SwPosition( *pSwCursor->GetPoint() );
+ pSwCursor->FillFindPos( eCurr, *pTmp );
+ pLinguIter->SetCurr( pTmp );
+
+ pTmp = new SwPosition( *pTmp );
+ pLinguIter->SetCurrX( pTmp );
+ }
+
+ if (!pConvArgs && g_pSpellIter)
+ g_pSpellIter->Start( this, eStart, eEnd );
+ if (pConvArgs && g_pConvIter)
+ g_pConvIter->Start( this, eStart, eEnd );
+}
+
+void SwEditShell::SpellEnd( SwConversionArgs const *pConvArgs, bool bRestoreSelection )
+{
+ if (!pConvArgs && g_pSpellIter && g_pSpellIter->GetSh() == this)
+ {
+ g_pSpellIter->End_(bRestoreSelection);
+ delete g_pSpellIter;
+ g_pSpellIter = nullptr;
+ }
+ if (pConvArgs && g_pConvIter && g_pConvIter->GetSh() == this)
+ {
+ g_pConvIter->End_();
+ delete g_pConvIter;
+ g_pConvIter = nullptr;
+ }
+}
+
+/// @returns SPL_ return values as in splchk.hxx
+uno::Any SwEditShell::SpellContinue(
+ sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
+ SwConversionArgs const *pConvArgs )
+{
+ uno::Any aRes;
+
+ if ((!pConvArgs && g_pSpellIter->GetSh() != this) ||
+ ( pConvArgs && g_pConvIter->GetSh() != this))
+ return aRes;
+
+ if( pPageCnt && !*pPageCnt )
+ {
+ sal_uInt16 nEndPage = GetLayout()->GetPageNum();
+ nEndPage += nEndPage * 10 / 100;
+ *pPageCnt = nEndPage;
+ if( nEndPage )
+ ::StartProgress( STR_STATSTR_SPELL, 0, nEndPage, GetDoc()->GetDocShell() );
+ }
+
+ OSL_ENSURE( pConvArgs || g_pSpellIter, "SpellIter missing" );
+ OSL_ENSURE( !pConvArgs || g_pConvIter, "ConvIter missing" );
+ //JP 18.07.95: prevent displaying selection on error messages. NO StartAction so that all
+ // Paints are also disabled.
+ ++mnStartAction;
+ OUString aRet;
+ uno::Reference< uno::XInterface > xRet;
+ if (pConvArgs)
+ {
+ g_pConvIter->Continue( pPageCnt, pPageSt ) >>= aRet;
+ aRes <<= aRet;
+ }
+ else
+ {
+ g_pSpellIter->Continue( pPageCnt, pPageSt ) >>= xRet;
+ aRes <<= xRet;
+ }
+ --mnStartAction;
+
+ if( !aRet.isEmpty() || xRet.is() )
+ {
+ // then make awt::Selection again visible
+ StartAction();
+ EndAction();
+ }
+ return aRes;
+}
+
+/* Interactive Hyphenation (BP 10.03.93)
+ *
+ * 1) HyphStart
+ * - Revoke all Selections
+ * - Save current Cursor
+ * - if no selections existent:
+ * - create new selection reaching until document end
+ * 2) HyphContinue
+ * - add nLastHyphLen onto SelectionStart
+ * - iterate over all selected areas
+ * - pDoc->Hyphenate() iterates over all Nodes of a selection
+ * - pTextNode->Hyphenate() calls SwTextFrame::Hyphenate of the EditShell
+ * - SwTextFrame:Hyphenate() iterates over all rows of the Pam
+ * - LineIter::Hyphenate() sets the Hyphenator and the Pam based on
+ * the to be separated word.
+ * - Returns true if there is a hyphenation and false if the Pam is processed.
+ * - If true, show the selected word and set nLastHyphLen.
+ * - If false, delete current selection and select next one. Returns HYPH_OK if no more.
+ * 3) InsertSoftHyph (might be called by UI if needed)
+ * - Place current cursor and add attribute.
+ * 4) HyphEnd
+ * - Restore old cursor, EndAction
+ */
+void SwEditShell::HyphStart( SwDocPositions eStart, SwDocPositions eEnd )
+{
+ // do not hyphenate if interactive hyphenation is active elsewhere
+ if (!g_pHyphIter)
+ {
+ g_pHyphIter = new SwHyphIter;
+ g_pHyphIter->Start( this, eStart, eEnd );
+ }
+}
+
+/// restore selections
+void SwEditShell::HyphEnd()
+{
+ assert(g_pHyphIter);
+ if (g_pHyphIter->GetSh() == this)
+ {
+ g_pHyphIter->End();
+ delete g_pHyphIter;
+ g_pHyphIter = nullptr;
+ }
+}
+
+/// @returns HYPH_CONTINUE if hyphenation, HYPH_OK if selected area was processed.
+uno::Reference< uno::XInterface >
+ SwEditShell::HyphContinue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
+{
+ assert(g_pHyphIter);
+ if (g_pHyphIter->GetSh() != this)
+ return nullptr;
+
+ if( pPageCnt && !*pPageCnt && !*pPageSt )
+ {
+ sal_uInt16 nEndPage = GetLayout()->GetPageNum();
+ nEndPage += nEndPage * 10 / 100;
+ if( nEndPage > 14 )
+ {
+ *pPageCnt = nEndPage;
+ ::StartProgress( STR_STATSTR_HYPHEN, 0, nEndPage, GetDoc()->GetDocShell());
+ }
+ else // here we once and for all suppress StatLineStartPercent
+ *pPageSt = 1;
+ }
+
+ //JP 18.07.95: prevent displaying selection on error messages. NO StartAction so that all
+ // Paints are also disabled.
+ ++mnStartAction;
+ uno::Reference< uno::XInterface > xRet;
+ g_pHyphIter->Continue( pPageCnt, pPageSt ) >>= xRet;
+ --mnStartAction;
+
+ if( xRet.is() )
+ g_pHyphIter->ShowSelection();
+
+ return xRet;
+}
+
+/** Insert soft hyphen
+ *
+ * @param nHyphPos Offset in the to be separated word
+ */
+void SwEditShell::InsertSoftHyph( const sal_Int32 nHyphPos )
+{
+ assert(g_pHyphIter);
+ g_pHyphIter->InsertSoftHyph( nHyphPos );
+}
+
+/// ignore hyphenation
+void SwEditShell::HyphIgnore()
+{
+ assert(g_pHyphIter);
+ //JP 18.07.95: prevent displaying selection on error messages. NO StartAction so that all
+ // Paints are also disabled.
+ ++mnStartAction;
+ g_pHyphIter->Ignore();
+ --mnStartAction;
+
+ g_pHyphIter->ShowSelection();
+}
+
+void SwEditShell::HandleCorrectionError(const OUString& aText, SwPosition aPos, sal_Int32 nBegin,
+ sal_Int32 nLen, const Point* pPt,
+ SwRect& rSelectRect)
+{
+ // save the start and end positions of the line and the starting point
+ SwNode const& rNode(GetCursor()->GetPoint()->nNode.GetNode());
+ Push();
+ LeftMargin();
+ const sal_Int32 nLineStart = &rNode == &GetCursor()->GetPoint()->nNode.GetNode()
+ ? GetCursor()->GetPoint()->nContent.GetIndex()
+ : 0;
+ RightMargin();
+ const sal_Int32 nLineEnd = &rNode == &GetCursor()->GetPoint()->nNode.GetNode()
+ ? GetCursor()->GetPoint()->nContent.GetIndex()
+ : rNode.GetTextNode()->Len();
+ Pop(PopMode::DeleteCurrent);
+
+ // make sure the selection build later from the data below does
+ // not "in word" character to the left and right in order to
+ // preserve those. Therefore count those "in words" in order to
+ // modify the selection accordingly.
+ const sal_Unicode* pChar = aText.getStr();
+ sal_Int32 nLeft = 0;
+ while (*pChar++ == CH_TXTATR_INWORD)
+ ++nLeft;
+ pChar = aText.getLength() ? aText.getStr() + aText.getLength() - 1 : nullptr;
+ sal_Int32 nRight = 0;
+ while (pChar && *pChar-- == CH_TXTATR_INWORD)
+ ++nRight;
+
+ aPos.nContent = nBegin + nLeft;
+ SwPaM* pCursor = GetCursor();
+ *pCursor->GetPoint() = aPos;
+ pCursor->SetMark();
+ ExtendSelection( true, nLen - nLeft - nRight );
+ // don't determine the rectangle in the current line
+ const sal_Int32 nWordStart = (nBegin + nLeft) < nLineStart ? nLineStart : nBegin + nLeft;
+ // take one less than the line end - otherwise the next line would be calculated
+ const sal_Int32 nWordEnd = (nBegin + nLen - nLeft - nRight) > nLineEnd
+ ? nLineEnd : (nBegin + nLen - nLeft - nRight);
+ Push();
+ pCursor->DeleteMark();
+ SwIndex& rContent = GetCursor()->GetPoint()->nContent;
+ rContent = nWordStart;
+ SwRect aStartRect;
+ SwCursorMoveState aState;
+ aState.m_bRealWidth = true;
+ SwContentNode* pContentNode = pCursor->GetContentNode();
+ std::pair<Point, bool> tmp;
+ if (pPt)
+ {
+ tmp.first = *pPt;
+ tmp.second = false;
+ }
+ SwContentFrame *const pContentFrame = pContentNode->getLayoutFrame(GetLayout(), pCursor->GetPoint(), pPt ? &tmp : nullptr);
+
+ pContentFrame->GetCharRect( aStartRect, *pCursor->GetPoint(), &aState );
+ rContent = nWordEnd - 1;
+ SwRect aEndRect;
+ pContentFrame->GetCharRect( aEndRect, *pCursor->GetPoint(),&aState );
+ rSelectRect = aStartRect.Union( aEndRect );
+ Pop(PopMode::DeleteCurrent);
+}
+
+/** Get a list of potential corrections for misspelled word.
+ *
+ * If empty, word is unknown but there are no corrections available.
+ * If NULL then the word is not misspelled but correct.
+ *
+ * @brief SwEditShell::GetCorrection
+ * @return list or NULL pointer
+ */
+uno::Reference< XSpellAlternatives >
+ SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect )
+{
+ uno::Reference< XSpellAlternatives > xSpellAlt;
+
+ if( IsTableMode() )
+ return nullptr;
+ SwPaM* pCursor = GetCursor();
+ SwPosition aPos( *pCursor->GetPoint() );
+ SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
+ SwTextNode *pNode = nullptr;
+ SwWrongList *pWrong = nullptr;
+ if (pPt && GetLayout()->GetModelPositionForViewPoint( &aPos, *const_cast<Point*>(pPt), &eTmpState ))
+ pNode = aPos.nNode.GetNode().GetTextNode();
+ if (nullptr == pNode)
+ pNode = pCursor->GetNode().GetTextNode();
+ if (nullptr != pNode)
+ pWrong = pNode->GetWrong();
+ if (nullptr != pWrong && !pNode->IsInProtectSect())
+ {
+ sal_Int32 nBegin = aPos.nContent.GetIndex();
+ sal_Int32 nLen = 1;
+ if (pWrong->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin))
+ {
+ const OUString aText(pNode->GetText().copy(nBegin, nLen));
+ // TODO: this doesn't handle fieldmarks properly
+ ModelToViewHelper const aConversionMap(*pNode, GetLayout(),
+ ExpandMode::ExpandFields | ExpandMode::ExpandFootnote | ExpandMode::ReplaceMode
+ | ExpandMode::HideFieldmarkCommands
+ | (GetLayout()->IsHideRedlines() ? ExpandMode::HideDeletions : ExpandMode(0))
+ | (GetViewOptions()->IsShowHiddenChar() ? ExpandMode(0) : ExpandMode::HideInvisible));
+ auto const nBeginView(aConversionMap.ConvertToViewPosition(nBegin));
+ OUString const aWord(aConversionMap.getViewText().copy(nBeginView,
+ aConversionMap.ConvertToViewPosition(nBegin+nLen) - nBeginView));
+
+ uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() );
+ if( xSpell.is() )
+ {
+ LanguageType eActLang = pNode->GetLang( nBegin, nLen );
+ if( xSpell->hasLanguage( static_cast<sal_uInt16>(eActLang) ))
+ {
+ // restrict the maximal number of suggestions displayed
+ // in the context menu.
+ // Note: That could of course be done by clipping the
+ // resulting sequence but the current third party
+ // implementations result differs greatly if the number of
+ // suggestions to be returned gets changed. Statistically
+ // it gets much better if told to return e.g. only 7 strings
+ // than returning e.g. 16 suggestions and using only the
+ // first 7. Thus we hand down the value to use to that
+ // implementation here by providing an additional parameter.
+ Sequence< PropertyValue > aPropVals ( { comphelper::makePropertyValue( UPN_MAX_NUMBER_OF_SUGGESTIONS, sal_Int16(7)) } );
+
+ xSpellAlt = xSpell->spell( aWord, static_cast<sal_uInt16>(eActLang), aPropVals );
+ }
+ }
+
+ if ( xSpellAlt.is() ) // error found?
+ {
+ HandleCorrectionError( aText, std::move(aPos), nBegin, nLen, pPt, rSelectRect );
+ }
+ }
+ }
+ return xSpellAlt;
+}
+
+bool SwEditShell::GetGrammarCorrection(
+ linguistic2::ProofreadingResult /*out*/ &rResult, // the complete result
+ sal_Int32 /*out*/ &rErrorPosInText, // offset of error position in string that was grammar checked...
+ sal_Int32 /*out*/ &rErrorIndexInResult, // index of error in rResult.aGrammarErrors
+ uno::Sequence< OUString > /*out*/ &rSuggestions, // suggestions to be used for the error found
+ const Point *pPt, SwRect &rSelectRect )
+{
+ bool bRes = false;
+
+ if( IsTableMode() )
+ return bRes;
+
+ SwPaM* pCursor = GetCursor();
+ SwPosition aPos( *pCursor->GetPoint() );
+ SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
+ SwTextNode *pNode = nullptr;
+ SwGrammarMarkUp *pWrong = nullptr;
+ if (pPt && GetLayout()->GetModelPositionForViewPoint( &aPos, *const_cast<Point*>(pPt), &eTmpState ))
+ pNode = aPos.nNode.GetNode().GetTextNode();
+ if (nullptr == pNode)
+ pNode = pCursor->GetNode().GetTextNode();
+ if (nullptr != pNode)
+ pWrong = pNode->GetGrammarCheck();
+ if (nullptr != pWrong && !pNode->IsInProtectSect())
+ {
+ sal_Int32 nBegin = aPos.nContent.GetIndex();
+ sal_Int32 nLen = 1;
+ if (pWrong->InWrongWord(nBegin, nLen))
+ {
+ const OUString aText(pNode->GetText().copy(nBegin, nLen));
+
+ uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( mxDoc->GetGCIterator() );
+ if (xGCIterator.is())
+ {
+ uno::Reference< lang::XComponent > xDoc = mxDoc->GetDocShell()->GetBaseModel();
+
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pNode, GetLayout());
+ const OUString& aExpandText = aConversionMap.getViewText();
+ // get XFlatParagraph to use...
+ uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *pNode, aExpandText, aConversionMap );
+
+ // get error position of cursor in XFlatParagraph
+ rErrorPosInText = aConversionMap.ConvertToViewPosition( nBegin );
+
+ const sal_Int32 nStartOfSentence = aConversionMap.ConvertToViewPosition( pWrong->getSentenceStart( nBegin ) );
+ const sal_Int32 nEndOfSentence = aConversionMap.ConvertToViewPosition( pWrong->getSentenceEnd( nBegin ) );
+
+ rResult = xGCIterator->checkSentenceAtPosition(
+ xDoc, xFlatPara, aExpandText, lang::Locale(), nStartOfSentence,
+ nEndOfSentence == COMPLETE_STRING ? aExpandText.getLength() : nEndOfSentence,
+ rErrorPosInText );
+ bRes = true;
+
+ // get suggestions to use for the specific error position
+ rSuggestions.realloc( 0 );
+ // return suggestions for first error that includes the given error position
+ auto pError = std::find_if(std::cbegin(rResult.aErrors), std::cend(rResult.aErrors),
+ [rErrorPosInText, nLen](const linguistic2::SingleProofreadingError &rError) {
+ return rError.nErrorStart <= rErrorPosInText
+ && rErrorPosInText + nLen <= rError.nErrorStart + rError.nErrorLength; });
+ if (pError != std::cend(rResult.aErrors))
+ {
+ rSuggestions = pError->aSuggestions;
+ rErrorIndexInResult = static_cast<sal_Int32>(std::distance(std::cbegin(rResult.aErrors), pError));
+ }
+ }
+
+ if (rResult.aErrors.hasElements()) // error found?
+ {
+ HandleCorrectionError( aText, std::move(aPos), nBegin, nLen, pPt, rSelectRect );
+ }
+ }
+ }
+
+ return bRes;
+}
+
+bool SwEditShell::SpellSentence(svx::SpellPortions& rPortions, bool bIsGrammarCheck)
+{
+ OSL_ENSURE( g_pSpellIter, "SpellIter missing" );
+ if (!g_pSpellIter)
+ return false;
+ bool bRet = g_pSpellIter->SpellSentence(rPortions, bIsGrammarCheck);
+
+ // make Selection visible - this should simply move the
+ // cursor to the end of the sentence
+ StartAction();
+ EndAction();
+ return bRet;
+}
+
+///make SpellIter start with the current sentence when called next time
+void SwEditShell::PutSpellingToSentenceStart()
+{
+ OSL_ENSURE( g_pSpellIter, "SpellIter missing" );
+ if (!g_pSpellIter)
+ return;
+ g_pSpellIter->ToSentenceStart();
+}
+
+static sal_uInt32 lcl_CountRedlines(const svx::SpellPortions& rLastPortions)
+{
+ return static_cast<sal_uInt32>(std::count_if(rLastPortions.begin(), rLastPortions.end(),
+ [](const svx::SpellPortion& rPortion) { return rPortion.bIsHidden; }));
+}
+
+void SwEditShell::MoveContinuationPosToEndOfCheckedSentence()
+{
+ // give hint that continuation position for spell/grammar checking is
+ // at the end of this sentence
+ if (g_pSpellIter)
+ {
+ g_pSpellIter->SetCurr( new SwPosition( *g_pSpellIter->GetCurrX() ) );
+ }
+}
+
+void SwEditShell::ApplyChangedSentence(const svx::SpellPortions& rNewPortions, bool bRecheck)
+{
+ // Note: rNewPortions.size() == 0 is valid and happens when the whole
+ // sentence got removed in the dialog
+
+ OSL_ENSURE( g_pSpellIter, "SpellIter missing" );
+ if (!g_pSpellIter ||
+ g_pSpellIter->GetLastPortions().empty()) // no portions -> no text to be changed
+ return;
+
+ const SpellPortions& rLastPortions = g_pSpellIter->GetLastPortions();
+ const SpellContentPositions rLastPositions = g_pSpellIter->GetLastPositions();
+ OSL_ENSURE(!rLastPortions.empty() &&
+ rLastPortions.size() == rLastPositions.size(),
+ "last vectors of spelling results are not set or not equal");
+
+ // iterate over the new portions, beginning at the end to take advantage of the previously
+ // saved content positions
+
+ mxDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_TEXT_CORRECTION, nullptr );
+ StartAction();
+
+ SwPaM *pCursor = GetCursor();
+ // save cursor position (which should be at the end of the current sentence)
+ // for later restoration
+ Push();
+
+ sal_uInt32 nRedlinePortions = lcl_CountRedlines(rLastPortions);
+ if((rLastPortions.size() - nRedlinePortions) == rNewPortions.size())
+ {
+ OSL_ENSURE( !rNewPortions.empty(), "rNewPortions should not be empty here" );
+ OSL_ENSURE( !rLastPortions.empty(), "rLastPortions should not be empty here" );
+ OSL_ENSURE( !rLastPositions.empty(), "rLastPositions should not be empty here" );
+
+ // the simple case: the same number of elements on both sides
+ // each changed element has to be applied to the corresponding source element
+ svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
+ SpellPortions::const_iterator aCurrentOldPortion = rLastPortions.end();
+ SpellContentPositions::const_iterator aCurrentOldPosition = rLastPositions.end();
+ do
+ {
+ --aCurrentNewPortion;
+ --aCurrentOldPortion;
+ --aCurrentOldPosition;
+ //jump over redline portions
+ while(aCurrentOldPortion->bIsHidden)
+ {
+ if (aCurrentOldPortion != rLastPortions.begin() &&
+ aCurrentOldPosition != rLastPositions.begin())
+ {
+ --aCurrentOldPortion;
+ --aCurrentOldPosition;
+ }
+ else
+ {
+ OSL_FAIL("ApplyChangedSentence: iterator positions broken" );
+ break;
+ }
+ }
+ if ( !pCursor->HasMark() )
+ pCursor->SetMark();
+ pCursor->GetPoint()->nContent = aCurrentOldPosition->nLeft;
+ pCursor->GetMark()->nContent = aCurrentOldPosition->nRight;
+ sal_uInt16 nScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
+ sal_uInt16 nLangWhichId = RES_CHRATR_LANGUAGE;
+ switch(nScriptType)
+ {
+ case css::i18n::ScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
+ case css::i18n::ScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
+ }
+ if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
+ {
+ // change text ...
+ // ... and apply language if necessary
+ if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
+ SetAttrItem( SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId) );
+ mxDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, aCurrentNewPortion->sText, false);
+ }
+ else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
+ {
+ // apply language
+ SetAttrItem( SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId) );
+ }
+ else if( aCurrentNewPortion->bIgnoreThisError )
+ {
+ // add the 'ignore' markup to the TextNode's grammar ignore markup list
+ IgnoreGrammarErrorAt( *pCursor );
+ OSL_FAIL("TODO: add ignore mark to text node");
+ }
+ }
+ while(aCurrentNewPortion != rNewPortions.begin());
+ }
+ else
+ {
+ OSL_ENSURE( !rLastPositions.empty(), "rLastPositions should not be empty here" );
+
+ // select the complete sentence
+ SpellContentPositions::const_iterator aCurrentEndPosition = rLastPositions.end();
+ --aCurrentEndPosition;
+ SpellContentPositions::const_iterator aCurrentStartPosition = rLastPositions.begin();
+ pCursor->GetPoint()->nContent = aCurrentStartPosition->nLeft;
+ pCursor->GetMark()->nContent = aCurrentEndPosition->nRight;
+
+ // delete the sentence completely
+ mxDoc->getIDocumentContentOperations().DeleteAndJoin(*pCursor);
+ for(const auto& rCurrentNewPortion : rNewPortions)
+ {
+ // set the language attribute
+ SvtScriptType nScriptType = GetScriptType();
+ sal_uInt16 nLangWhichId = RES_CHRATR_LANGUAGE;
+ switch(nScriptType)
+ {
+ case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
+ case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
+ default: break;
+ }
+ SfxItemSet aSet(GetAttrPool(), nLangWhichId, nLangWhichId);
+ GetCurAttr( aSet );
+ const SvxLanguageItem& rLang = static_cast<const SvxLanguageItem& >(aSet.Get(nLangWhichId));
+ if(rLang.GetLanguage() != rCurrentNewPortion.eLanguage)
+ SetAttrItem( SvxLanguageItem(rCurrentNewPortion.eLanguage, nLangWhichId) );
+ // insert the new string
+ mxDoc->getIDocumentContentOperations().InsertString(*pCursor, rCurrentNewPortion.sText);
+
+ // set the cursor to the end of the inserted string
+ *pCursor->Start() = *pCursor->End();
+ }
+ }
+
+ // restore cursor to the end of the sentence
+ // (will work also if the sentence length has changed,
+ // since cursors get updated automatically!)
+ Pop(PopMode::DeleteCurrent);
+
+ // collapse cursor to the end of the modified sentence
+ *pCursor->Start() = *pCursor->End();
+ if (bRecheck)
+ {
+ // in grammar check the current sentence has to be checked again
+ GoStartSentence();
+ }
+ // set continuation position for spell/grammar checking to the end of this sentence
+ g_pSpellIter->SetCurr( new SwPosition(*pCursor->Start()) );
+
+ mxDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_TEXT_CORRECTION, nullptr );
+ EndAction();
+
+}
+/** Collect all deleted redlines of the current text node
+ * beginning at the start of the cursor position
+ */
+static SpellContentPositions lcl_CollectDeletedRedlines(SwEditShell const * pSh)
+{
+ SpellContentPositions aRedlines;
+ SwDoc* pDoc = pSh->GetDoc();
+ const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ if ( bShowChg )
+ {
+ SwPaM *pCursor = pSh->GetCursor();
+ const SwPosition* pStartPos = pCursor->Start();
+ const SwTextNode* pTextNode = pCursor->GetNode().GetTextNode();
+
+ SwRedlineTable::size_type nAct = pDoc->getIDocumentRedlineAccess().GetRedlinePos( *pTextNode, RedlineType::Any );
+ const sal_Int32 nStartIndex = pStartPos->nContent.GetIndex();
+ for ( ; nAct < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); nAct++ )
+ {
+ const SwRangeRedline* pRed = pDoc->getIDocumentRedlineAccess().GetRedlineTable()[ nAct ];
+
+ if ( pRed->Start()->nNode > pTextNode->GetIndex() )
+ break;
+
+ if( RedlineType::Delete == pRed->GetType() )
+ {
+ sal_Int32 nStart_, nEnd_;
+ pRed->CalcStartEnd( pTextNode->GetIndex(), nStart_, nEnd_ );
+ sal_Int32 nStart = nStart_;
+ sal_Int32 nEnd = nEnd_;
+ if(nStart >= nStartIndex || nEnd >= nStartIndex)
+ {
+ SpellContentPosition aAdd;
+ aAdd.nLeft = nStart;
+ aAdd.nRight = nEnd;
+ aRedlines.push_back(aAdd);
+ }
+ }
+ }
+ }
+ return aRedlines;
+}
+
+/// remove the redline positions after the current selection
+static void lcl_CutRedlines( SpellContentPositions& aDeletedRedlines, SwEditShell const * pSh )
+{
+ if(!aDeletedRedlines.empty())
+ {
+ SwPaM *pCursor = pSh->GetCursor();
+ const SwPosition* pEndPos = pCursor->End();
+ const sal_Int32 nEnd = pEndPos->nContent.GetIndex();
+ while(!aDeletedRedlines.empty() &&
+ aDeletedRedlines.back().nLeft > nEnd)
+ {
+ aDeletedRedlines.pop_back();
+ }
+ }
+}
+
+static SpellContentPosition lcl_FindNextDeletedRedline(
+ const SpellContentPositions& rDeletedRedlines,
+ sal_Int32 nSearchFrom )
+{
+ SpellContentPosition aRet;
+ aRet.nLeft = aRet.nRight = SAL_MAX_INT32;
+ if(!rDeletedRedlines.empty())
+ {
+ auto aIter = std::find_if_not(rDeletedRedlines.begin(), rDeletedRedlines.end(),
+ [nSearchFrom](const SpellContentPosition& rPos) { return rPos.nLeft < nSearchFrom; });
+ if (aIter != rDeletedRedlines.end())
+ aRet = *aIter;
+ }
+ return aRet;
+}
+
+bool SwSpellIter::SpellSentence(svx::SpellPortions& rPortions, bool bIsGrammarCheck)
+{
+ bool bRet = false;
+ m_aLastPortions.clear();
+ m_aLastPositions.clear();
+
+ SwEditShell *pMySh = GetSh();
+ if( !pMySh )
+ return false;
+
+ OSL_ENSURE( GetEnd(), "SwSpellIter::SpellSentence without Start?");
+
+ uno::Reference< XSpellAlternatives > xSpellRet;
+ linguistic2::ProofreadingResult aGrammarResult;
+ bool bGoOn = true;
+ bool bGrammarErrorFound = false;
+ do {
+ SwPaM *pCursor = pMySh->GetCursor();
+ if ( !pCursor->HasMark() )
+ pCursor->SetMark();
+
+ *pCursor->GetPoint() = *GetCurr();
+ *pCursor->GetMark() = *GetEnd();
+
+ if (m_bBackToStartOfSentence)
+ {
+ pMySh->GoStartSentence();
+ m_bBackToStartOfSentence = false;
+ }
+ uno::Any aSpellRet = pMySh->GetDoc()->Spell(*pCursor, m_xSpeller, nullptr, nullptr,
+ bIsGrammarCheck, pMySh->GetLayout());
+ aSpellRet >>= xSpellRet;
+ aSpellRet >>= aGrammarResult;
+ bGoOn = GetCursorCnt() > 1;
+ bGrammarErrorFound = aGrammarResult.aErrors.hasElements();
+ if( xSpellRet.is() || bGrammarErrorFound )
+ {
+ bGoOn = false;
+ SwPosition* pNewPoint = new SwPosition( *pCursor->GetPoint() );
+ SwPosition* pNewMark = new SwPosition( *pCursor->GetMark() );
+
+ SetCurr( pNewPoint );
+ SetCurrX( pNewMark );
+ }
+ if( bGoOn )
+ {
+ pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ pCursor = pMySh->GetCursor();
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ SwPosition* pNew = new SwPosition( *pCursor->GetPoint() );
+ SetStart( pNew );
+ pNew = new SwPosition( *pCursor->GetMark() );
+ SetEnd( pNew );
+ pNew = new SwPosition( *GetStart() );
+ SetCurr( pNew );
+ pNew = new SwPosition( *pNew );
+ SetCurrX( pNew );
+ pCursor->SetMark();
+ --GetCursorCnt();
+ }
+ } while ( bGoOn );
+
+ if(xSpellRet.is() || bGrammarErrorFound)
+ {
+ // an error has been found
+ // To fill the spell portions the beginning of the sentence has to be found
+ SwPaM *pCursor = pMySh->GetCursor();
+ // set the mark to the right if necessary
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ // the cursor has to be collapsed on the left to go to the start of the sentence - if sentence ends inside of the error
+ pCursor->DeleteMark();
+ pCursor->SetMark();
+ bool bStartSent = pMySh->GoStartSentence();
+ SpellContentPositions aDeletedRedlines = lcl_CollectDeletedRedlines(pMySh);
+ if(bStartSent)
+ {
+ // create a portion from the start part
+ AddPortion(nullptr, nullptr, aDeletedRedlines);
+ }
+ // Set the cursor to the error already found
+ *pCursor->GetPoint() = *GetCurrX();
+ *pCursor->GetMark() = *GetCurr();
+ AddPortion(xSpellRet, &aGrammarResult, aDeletedRedlines);
+
+ // save the end position of the error to continue from here
+ SwPosition aSaveStartPos = *pCursor->End();
+ // determine the end of the current sentence
+ if ( *pCursor->GetPoint() < *pCursor->GetMark() )
+ pCursor->Exchange();
+ // again collapse to start marking after the end of the error
+ pCursor->DeleteMark();
+ pCursor->SetMark();
+
+ pMySh->GoEndSentence();
+ if( bGrammarErrorFound )
+ {
+ const ModelToViewHelper aConversionMap(static_cast<SwTextNode&>(pCursor->GetNode()), pMySh->GetLayout());
+ const OUString& aExpandText = aConversionMap.getViewText();
+ sal_Int32 nSentenceEnd =
+ aConversionMap.ConvertToViewPosition( aGrammarResult.nBehindEndOfSentencePosition );
+ // remove trailing space
+ if( aExpandText[nSentenceEnd - 1] == ' ' )
+ --nSentenceEnd;
+ if( pCursor->End()->nContent.GetIndex() < nSentenceEnd )
+ {
+ pCursor->End()->nContent.Assign(
+ pCursor->End()->nNode.GetNode().GetContentNode(), nSentenceEnd);
+ }
+ }
+
+ lcl_CutRedlines( aDeletedRedlines, pMySh );
+ // save the 'global' end of the spellchecking
+ const SwPosition aSaveEndPos = *GetEnd();
+ // set the sentence end as 'local' end
+ SetEnd( new SwPosition( *pCursor->End() ));
+
+ *pCursor->GetPoint() = aSaveStartPos;
+ *pCursor->GetMark() = *GetEnd();
+ // now the rest of the sentence has to be searched for errors
+ // for each error the non-error text between the current and the last error has
+ // to be added to the portions - if necessary broken into same-language-portions
+ if( !bGrammarErrorFound ) //in grammar check there's only one error returned
+ {
+ do
+ {
+ xSpellRet = nullptr;
+ // don't search for grammar errors here anymore!
+ pMySh->GetDoc()->Spell(*pCursor, m_xSpeller, nullptr, nullptr, false,
+ pMySh->GetLayout())
+ >>= xSpellRet;
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ SetCurr( new SwPosition( *pCursor->GetPoint() ));
+ SetCurrX( new SwPosition( *pCursor->GetMark() ));
+
+ // if an error has been found go back to the text preceding the error
+ if(xSpellRet.is())
+ {
+ *pCursor->GetPoint() = aSaveStartPos;
+ *pCursor->GetMark() = *GetCurr();
+ }
+ // add the portion
+ AddPortion(nullptr, nullptr, aDeletedRedlines);
+
+ if(xSpellRet.is())
+ {
+ *pCursor->GetPoint() = *GetCurr();
+ *pCursor->GetMark() = *GetCurrX();
+ AddPortion(xSpellRet, nullptr, aDeletedRedlines);
+ // move the cursor to the end of the error string
+ *pCursor->GetPoint() = *GetCurrX();
+ // and save the end of the error as new start position
+ aSaveStartPos = *GetCurrX();
+ // and the end of the sentence
+ *pCursor->GetMark() = *GetEnd();
+ }
+ // if the end of the sentence has already been reached then break here
+ if(*GetCurrX() >= *GetEnd())
+ break;
+ }
+ while(xSpellRet.is());
+ }
+ else
+ {
+ // go to the end of sentence as the grammar check returned it
+ // at this time the Point is behind the grammar error
+ // and the mark points to the sentence end as
+ if ( *pCursor->GetPoint() < *pCursor->GetMark() )
+ pCursor->Exchange();
+ }
+
+ // the part between the last error and the end of the sentence has to be added
+ *pMySh->GetCursor()->GetPoint() = *GetEnd();
+ if(*GetCurrX() < *GetEnd())
+ {
+ AddPortion(nullptr, nullptr, aDeletedRedlines);
+ }
+ // set the shell cursor to the end of the sentence to prevent a visible selection
+ *pCursor->GetMark() = *GetEnd();
+ if( !bIsGrammarCheck )
+ {
+ // set the current position to the end of the sentence
+ SetCurr( new SwPosition(*GetEnd()) );
+ }
+ // restore the 'global' end
+ SetEnd( new SwPosition(aSaveEndPos) );
+ rPortions = m_aLastPortions;
+ bRet = true;
+ }
+ else
+ {
+ // if no error could be found the selection has to be corrected - at least if it's not in the body
+ *pMySh->GetCursor()->GetPoint() = *GetEnd();
+ pMySh->GetCursor()->DeleteMark();
+ }
+
+ return bRet;
+}
+
+void SwSpellIter::ToSentenceStart() { m_bBackToStartOfSentence = true; }
+
+static LanguageType lcl_GetLanguage(SwEditShell& rSh)
+{
+ SvtScriptType nScriptType = rSh.GetScriptType();
+ sal_uInt16 nLangWhichId = RES_CHRATR_LANGUAGE;
+
+ switch(nScriptType)
+ {
+ case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
+ case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
+ default: break;
+ }
+ SfxItemSet aSet(rSh.GetAttrPool(), nLangWhichId, nLangWhichId);
+ rSh.GetCurAttr( aSet );
+ const SvxLanguageItem& rLang = static_cast<const SvxLanguageItem& >(aSet.Get(nLangWhichId));
+ return rLang.GetLanguage();
+}
+
+/// create a text portion at the given position
+void SwSpellIter::CreatePortion(uno::Reference< XSpellAlternatives > const & xAlt,
+ const linguistic2::ProofreadingResult* pGrammarResult,
+ bool bIsField, bool bIsHidden)
+{
+ svx::SpellPortion aPortion;
+ OUString sText;
+ GetSh()->GetSelectedText( sText );
+ if(sText.isEmpty())
+ return;
+
+ // in case of redlined deletions the selection of an error is not the same as the _real_ word
+ if(xAlt.is())
+ aPortion.sText = xAlt->getWord();
+ else if(pGrammarResult)
+ {
+ aPortion.bIsGrammarError = true;
+ if(pGrammarResult->aErrors.hasElements())
+ {
+ aPortion.aGrammarError = pGrammarResult->aErrors[0];
+ aPortion.sText = pGrammarResult->aText.copy( aPortion.aGrammarError.nErrorStart, aPortion.aGrammarError.nErrorLength );
+ aPortion.xGrammarChecker = pGrammarResult->xProofreader;
+ auto pProperty = std::find_if(std::cbegin(pGrammarResult->aProperties), std::cend(pGrammarResult->aProperties),
+ [](const beans::PropertyValue& rProperty) { return rProperty.Name == "DialogTitle"; });
+ if (pProperty != std::cend(pGrammarResult->aProperties))
+ pProperty->Value >>= aPortion.sDialogTitle;
+ }
+ }
+ else
+ aPortion.sText = sText;
+ aPortion.eLanguage = lcl_GetLanguage(*GetSh());
+ aPortion.bIsField = bIsField;
+ aPortion.bIsHidden = bIsHidden;
+ aPortion.xAlternatives = xAlt;
+ SpellContentPosition aPosition;
+ SwPaM *pCursor = GetSh()->GetCursor();
+ aPosition.nLeft = pCursor->Start()->nContent.GetIndex();
+ aPosition.nRight = pCursor->End()->nContent.GetIndex();
+ m_aLastPortions.push_back(aPortion);
+ m_aLastPositions.push_back(aPosition);
+}
+
+void SwSpellIter::AddPortion(uno::Reference< XSpellAlternatives > const & xAlt,
+ const linguistic2::ProofreadingResult* pGrammarResult,
+ const SpellContentPositions& rDeletedRedlines)
+{
+ SwEditShell *pMySh = GetSh();
+ OUString sText;
+ pMySh->GetSelectedText( sText );
+ if(sText.isEmpty())
+ return;
+
+ if(xAlt.is() || pGrammarResult != nullptr)
+ {
+ CreatePortion(xAlt, pGrammarResult, false, false);
+ }
+ else
+ {
+ SwPaM *pCursor = GetSh()->GetCursor();
+ if ( *pCursor->GetPoint() > *pCursor->GetMark() )
+ pCursor->Exchange();
+ // save the start and end positions
+ SwPosition aStart(*pCursor->GetPoint());
+ SwPosition aEnd(*pCursor->GetMark());
+ // iterate over the text to find changes in language
+ // set the mark equal to the point
+ *pCursor->GetMark() = aStart;
+ SwTextNode* pTextNode = pCursor->GetNode().GetTextNode();
+ LanguageType eStartLanguage = lcl_GetLanguage(*GetSh());
+ SpellContentPosition aNextRedline = lcl_FindNextDeletedRedline(
+ rDeletedRedlines, aStart.nContent.GetIndex() );
+ if( aNextRedline.nLeft == aStart.nContent.GetIndex() )
+ {
+ // select until the end of the current redline
+ const sal_Int32 nEnd = aEnd.nContent.GetIndex() < aNextRedline.nRight ?
+ aEnd.nContent.GetIndex() : aNextRedline.nRight;
+ pCursor->GetPoint()->nContent.Assign( pTextNode, nEnd );
+ CreatePortion(xAlt, pGrammarResult, false, true);
+ aStart = *pCursor->End();
+ // search for next redline
+ aNextRedline = lcl_FindNextDeletedRedline(
+ rDeletedRedlines, aStart.nContent.GetIndex() );
+ }
+ while(*pCursor->GetPoint() < aEnd)
+ {
+ // #125786 in table cell with fixed row height the cursor might not move forward
+ if(!GetSh()->Right(1, CRSR_SKIP_CELLS))
+ break;
+
+ bool bField = false;
+ // read the character at the current position to check if it's a field
+ sal_Unicode const cChar =
+ pTextNode->GetText()[pCursor->GetMark()->nContent.GetIndex()];
+ if( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
+ {
+ const SwTextAttr* pTextAttr = pTextNode->GetTextAttrForCharAt(
+ pCursor->GetMark()->nContent.GetIndex() );
+ const sal_uInt16 nWhich = pTextAttr
+ ? pTextAttr->Which()
+ : RES_TXTATR_END;
+ switch (nWhich)
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_FTN:
+ case RES_TXTATR_FLYCNT:
+ bField = true;
+ break;
+ }
+ }
+ else if (cChar == CH_TXT_ATR_FORMELEMENT)
+ {
+ SwPosition aPos(*pCursor->GetMark());
+ bField = pMySh->GetDoc()->getIDocumentMarkAccess()->getDropDownFor(aPos);
+ }
+
+ LanguageType eCurLanguage = lcl_GetLanguage(*GetSh());
+ bool bRedline = aNextRedline.nLeft == pCursor->GetPoint()->nContent.GetIndex();
+ // create a portion if the next character
+ // - is a field,
+ // - is at the beginning of a deleted redline
+ // - has a different language
+ if(bField || bRedline || eCurLanguage != eStartLanguage)
+ {
+ eStartLanguage = eCurLanguage;
+ // go one step back - the cursor currently selects the first character
+ // with a different language
+ // in the case of redlining it's different
+ if(eCurLanguage != eStartLanguage || bField)
+ *pCursor->GetPoint() = *pCursor->GetMark();
+ // set to the last start
+ *pCursor->GetMark() = aStart;
+ // create portion should only be called if a selection exists
+ // there's no selection if there's a field at the beginning
+ if(*pCursor->Start() != *pCursor->End())
+ CreatePortion(xAlt, pGrammarResult, false, false);
+ aStart = *pCursor->End();
+ // now export the field - if there is any
+ if(bField)
+ {
+ *pCursor->GetMark() = *pCursor->GetPoint();
+ GetSh()->Right(1, CRSR_SKIP_CELLS);
+ CreatePortion(xAlt, pGrammarResult, true, false);
+ aStart = *pCursor->End();
+ }
+ }
+ // if a redline start then create a portion for it
+ if(bRedline)
+ {
+ *pCursor->GetMark() = *pCursor->GetPoint();
+ // select until the end of the current redline
+ const sal_Int32 nEnd = aEnd.nContent.GetIndex() < aNextRedline.nRight ?
+ aEnd.nContent.GetIndex() : aNextRedline.nRight;
+ pCursor->GetPoint()->nContent.Assign( pTextNode, nEnd );
+ CreatePortion(xAlt, pGrammarResult, false, true);
+ aStart = *pCursor->End();
+ // search for next redline
+ aNextRedline = lcl_FindNextDeletedRedline(
+ rDeletedRedlines, aStart.nContent.GetIndex() );
+ }
+ *pCursor->GetMark() = *pCursor->GetPoint();
+ }
+ pCursor->SetMark();
+ *pCursor->GetMark() = aStart;
+ CreatePortion(xAlt, pGrammarResult, false, false);
+ }
+}
+
+void SwEditShell::IgnoreGrammarErrorAt( SwPaM& rErrorPosition )
+{
+ SwTextNode *pNode;
+ SwWrongList *pWrong;
+ SwNodeIndex aIdx = rErrorPosition.Start()->nNode;
+ SwNodeIndex aEndIdx = rErrorPosition.Start()->nNode;
+ sal_Int32 nStart = rErrorPosition.Start()->nContent.GetIndex();
+ sal_Int32 nEnd = COMPLETE_STRING;
+ while( aIdx <= aEndIdx )
+ {
+ pNode = aIdx.GetNode().GetTextNode();
+ if( pNode ) {
+ if( aIdx == aEndIdx )
+ nEnd = rErrorPosition.End()->nContent.GetIndex();
+ pWrong = pNode->GetGrammarCheck();
+ if( pWrong )
+ pWrong->RemoveEntry( nStart, nEnd );
+ pWrong = pNode->GetWrong();
+ if( pWrong )
+ pWrong->RemoveEntry( nStart, nEnd );
+ SwTextFrame::repaintTextFrames( *pNode );
+ }
+ ++aIdx;
+ nStart = 0;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/ednumber.cxx b/sw/source/core/edit/ednumber.cxx
new file mode 100644
index 000000000..3304ddb19
--- /dev/null
+++ b/sw/source/core/edit/ednumber.cxx
@@ -0,0 +1,905 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editsh.hxx>
+#include <edimp.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <swundo.hxx>
+#include <numrule.hxx>
+#include <osl/diagnose.h>
+
+SwPamRanges::SwPamRanges( const SwPaM& rRing )
+{
+ for(SwPaM& rTmp : const_cast<SwPaM*>(&rRing)->GetRingContainer())
+ Insert( rTmp.GetMark()->nNode, rTmp.GetPoint()->nNode );
+}
+
+void SwPamRanges::Insert( const SwNodeIndex& rIdx1, const SwNodeIndex& rIdx2 )
+{
+ SwPamRange aRg( rIdx1.GetIndex(), rIdx2.GetIndex() );
+ if( aRg.nEnd < aRg.nStart )
+ { aRg.nStart = aRg.nEnd; aRg.nEnd = rIdx1.GetIndex(); }
+
+ o3tl::sorted_vector<SwPamRange>::const_iterator it = maVector.lower_bound(aRg); //search Insert Position
+ size_t nPos = it - maVector.begin();
+ if (!maVector.empty() && (it != maVector.end()) && (*it) == aRg)
+ {
+ // is the one in the Array smaller?
+ SwPamRange const& rTmp = maVector[nPos];
+ if( rTmp.nEnd < aRg.nEnd )
+ {
+ aRg.nEnd = rTmp.nEnd;
+ maVector.erase(maVector.begin() + nPos); // combine
+ }
+ else
+ return; // done, because by precondition everything is combined
+ }
+
+ bool bEnd;
+ do {
+ bEnd = true;
+
+ // combine with predecessor?
+ if( nPos > 0 )
+ {
+ SwPamRange const& rTmp = maVector[nPos-1];
+ if( rTmp.nEnd == aRg.nStart
+ || rTmp.nEnd+1 == aRg.nStart )
+ {
+ aRg.nStart = rTmp.nStart;
+ bEnd = false;
+ maVector.erase( maVector.begin() + --nPos ); // combine
+ }
+ // range contained in rTmp?
+ else if( rTmp.nStart <= aRg.nStart && aRg.nEnd <= rTmp.nEnd )
+ return;
+ }
+ // combine with successor?
+ if( nPos < maVector.size() )
+ {
+ SwPamRange const& rTmp = maVector[nPos];
+ if( rTmp.nStart == aRg.nEnd ||
+ rTmp.nStart == aRg.nEnd+1 )
+ {
+ aRg.nEnd = rTmp.nEnd;
+ bEnd = false;
+ maVector.erase( maVector.begin() + nPos ); // combine
+ }
+
+ // range contained in rTmp?
+ else if( rTmp.nStart <= aRg.nStart && aRg.nEnd <= rTmp.nEnd )
+ return;
+ }
+ } while( !bEnd );
+
+ maVector.insert( aRg );
+}
+
+SwPaM& SwPamRanges::SetPam( size_t nArrPos, SwPaM& rPam )
+{
+ assert( nArrPos < Count() );
+ const SwPamRange& rTmp = maVector[ nArrPos ];
+ rPam.GetPoint()->nNode = rTmp.nStart;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), 0 );
+ rPam.SetMark();
+ rPam.GetPoint()->nNode = rTmp.nEnd;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), 0 );
+ return rPam;
+}
+
+// Rule book for outline numbering
+
+void SwEditShell::SetOutlineNumRule(const SwNumRule& rRule)
+{
+ StartAllAction(); // bracketing for updating!
+ GetDoc()->SetOutlineNumRule(rRule);
+ EndAllAction();
+}
+
+const SwNumRule* SwEditShell::GetOutlineNumRule() const
+{
+ return GetDoc()->GetOutlineNumRule();
+}
+
+// Set if there is no numbering yet, else update.
+// Works with old and new rules. Update only differences.
+
+// paragraphs without numbering, with indentations
+void SwEditShell::NoNum()
+{
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor ) // Multiple selection?
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ GetDoc()->NoNum( aRangeArr.SetPam( n, aPam ));
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ // sw_redlinehide: leave cursor as is, will be split at Point & apply to new node
+ GetDoc()->NoNum( *pCursor );
+
+ EndAllAction();
+}
+
+bool SwEditShell::SelectionHasNumber() const
+{
+ bool bResult = HasNumber();
+ const SwTextNode * pTextNd = sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode);
+ if (!bResult && pTextNd && pTextNd->Len()==0 && !pTextNd->GetNumRule()) {
+ SwPamRanges aRangeArr( *GetCursor() );
+ SwPaM aPam( *GetCursor()->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ aRangeArr.SetPam( n, aPam );
+ {
+ SwNodeOffset nStt = aPam.Start()->nNode.GetIndex(),
+ nEnd = aPam.End()->nNode.GetIndex();
+ for (SwNodeOffset nPos = nStt; nPos<=nEnd; nPos++)
+ {
+ pTextNd = mxDoc->GetNodes()[nPos]->GetTextNode();
+ if (pTextNd)
+ {
+ pTextNd = sw::GetParaPropsNode(*GetLayout(), SwNodeIndex(*pTextNd));
+ }
+ if (pTextNd && pTextNd->Len()!=0)
+ {
+ bResult = pTextNd->HasNumber();
+
+ // #b6340308# special case: outline numbered, not counted paragraph
+ if ( bResult &&
+ pTextNd->GetNumRule() == GetDoc()->GetOutlineNumRule() &&
+ !pTextNd->IsCountedInList() )
+ {
+ bResult = false;
+ }
+ if (!bResult) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ return bResult;
+}
+
+// add a new function to determine number on/off status
+bool SwEditShell::SelectionHasBullet() const
+{
+ bool bResult = HasBullet();
+ const SwTextNode * pTextNd = sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode);
+ if (!bResult && pTextNd && pTextNd->Len()==0 && !pTextNd->GetNumRule()) {
+ SwPamRanges aRangeArr( *GetCursor() );
+ SwPaM aPam( *GetCursor()->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ aRangeArr.SetPam( n, aPam );
+ {
+ SwNodeOffset nStt = aPam.Start()->nNode.GetIndex(),
+ nEnd = aPam.End()->nNode.GetIndex();
+ for (SwNodeOffset nPos = nStt; nPos<=nEnd; nPos++)
+ {
+ pTextNd = mxDoc->GetNodes()[nPos]->GetTextNode();
+ if (pTextNd)
+ {
+ pTextNd = sw::GetParaPropsNode(*GetLayout(), SwNodeIndex(*pTextNd));
+ }
+ if (pTextNd && pTextNd->Len()!=0)
+ {
+ bResult = pTextNd->HasBullet();
+
+ if (!bResult) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bResult;
+}
+
+// -> #i29560#
+bool SwEditShell::HasNumber() const
+{
+ bool bResult = false;
+
+ const SwTextNode *const pTextNd = sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode);
+
+ if (pTextNd)
+ {
+ bResult = pTextNd->HasNumber();
+
+ // special case: outline numbered, not counted paragraph
+ if ( bResult &&
+ pTextNd->GetNumRule() == GetDoc()->GetOutlineNumRule() &&
+ !pTextNd->IsCountedInList() )
+ {
+ bResult = false;
+ }
+ }
+
+ return bResult;
+}
+
+bool SwEditShell::HasBullet() const
+{
+ bool bResult = false;
+
+ const SwTextNode *const pTextNd = sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode);
+
+ if (pTextNd)
+ {
+ bResult = pTextNd->HasBullet();
+ }
+
+ return bResult;
+}
+// <- #i29560#
+
+// delete, split list
+void SwEditShell::DelNumRules()
+{
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->IsMultiSelection() )
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ GetDoc()->DelNumRules(aRangeArr.SetPam( n, aPam ), GetLayout());
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ GetDoc()->DelNumRules(*pCursor, GetLayout());
+
+ // Call AttrChangeNotify on the UI-side. Should actually be redundant but there was a bug once.
+ CallChgLnk();
+
+ // Cursor cannot be in front of a label anymore, because numbering/bullet is deleted.
+ SetInFrontOfLabel( false );
+
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+}
+
+// up- & downgrading
+void SwEditShell::NumUpDown( bool bDown )
+{
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( !pCursor->IsMultiSelection() )
+ GetDoc()->NumUpDown(*pCursor, bDown, GetLayout());
+ else
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ GetDoc()->NumUpDown(aRangeArr.SetPam( n, aPam ), bDown, GetLayout());
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ GetDoc()->getIDocumentState().SetModified();
+
+ // #i54693# Update marked numbering levels
+ if ( IsInFrontOfLabel() )
+ UpdateMarkedListLevel();
+
+ CallChgLnk();
+
+ EndAllAction();
+}
+
+bool SwEditShell::IsFirstOfNumRuleAtCursorPos() const
+{
+ return SwDoc::IsFirstOfNumRuleAtPos(*GetCursor()->GetPoint(), *GetLayout());
+}
+
+// -> #i23725#, #i90078#
+void SwEditShell::ChangeIndentOfAllListLevels( const sal_Int32 nDiff )
+{
+ StartAllAction();
+
+ const SwNumRule *pCurNumRule = GetNumRuleAtCurrCursorPos();
+ if ( pCurNumRule != nullptr )
+ {
+ SwNumRule aRule(*pCurNumRule);
+ const SwNumFormat& aRootNumFormat(aRule.Get(0));
+ if( nDiff > 0 || aRootNumFormat.GetIndentAt() + nDiff > 0) // fdo#42708
+ {
+ // #i90078#
+ aRule.ChangeIndent( nDiff );
+ }
+ // no start of new list
+ SetCurNumRule( aRule, false );
+ }
+
+ EndAllAction();
+}
+
+// #i90078#
+void SwEditShell::SetIndent(short nIndent, const SwPosition & rPos)
+{
+ StartAllAction();
+
+ SwPosition pos(rPos);
+ SwNumRule *pCurNumRule = SwDoc::GetNumRuleAtPos(pos, GetLayout());
+
+ if (pCurNumRule)
+ {
+ SwNumRule aRule(*pCurNumRule);
+ if ( !IsMultiSelection() && IsFirstOfNumRuleAtCursorPos() )
+ {
+ aRule.SetIndentOfFirstListLevelAndChangeOthers( nIndent );
+ }
+ else
+ {
+ const SwTextNode* pTextNode = pos.nNode.GetNode().GetTextNode();
+ if ( pTextNode != nullptr
+ && pTextNode->GetActualListLevel() >= 0 )
+ {
+ aRule.SetIndent( nIndent, static_cast< sal_uInt16 >( pTextNode->GetActualListLevel() ) );
+ }
+ }
+
+ // change numbering rule - changed numbering rule is not applied at <aPaM>
+ SwPaM aPaM(pos);
+ GetDoc()->SetNumRule(aPaM, aRule, false, GetLayout(), OUString(), false);
+ }
+
+ EndAllAction();
+}
+
+bool SwEditShell::MoveParagraph( SwNodeOffset nOffset )
+{
+ StartAllAction();
+
+ SwPaM *pCursor = GetCursor();
+
+ bool bRet = GetDoc()->MoveParagraph( *pCursor, nOffset );
+
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+ return bRet;
+}
+
+int SwEditShell::GetCurrentParaOutlineLevel( ) const
+{
+ int nLevel = 0;
+
+ SwPaM* pCursor = GetCursor();
+ const SwTextNode *const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
+ if (pTextNd)
+ nLevel = pTextNd->GetAttrOutlineLevel();
+ return nLevel;
+}
+
+void SwEditShell::GetCurrentOutlineLevels( sal_uInt8& rUpper, sal_uInt8& rLower )
+{
+ SwPaM* pCursor = GetCursor();
+ SwPaM aCursor( *pCursor->Start() );
+ aCursor.SetMark();
+ if( pCursor->HasMark() )
+ *aCursor.GetPoint() = *pCursor->End();
+ SwDoc::GotoNextNum(*aCursor.GetPoint(), GetLayout(), false, &rUpper, &rLower);
+}
+
+bool SwEditShell::MoveNumParas( bool bUpperLower, bool bUpperLeft )
+{
+ StartAllAction();
+
+ // On all selections?
+ SwPaM* pCursor = GetCursor();
+ SwPaM aCursor( *pCursor->Start() );
+ aCursor.SetMark();
+
+ if( pCursor->HasMark() )
+ *aCursor.GetPoint() = *pCursor->End();
+
+ bool bRet = false;
+ sal_uInt8 nUpperLevel, nLowerLevel;
+ if (SwDoc::GotoNextNum( *aCursor.GetPoint(), GetLayout(), false,
+ &nUpperLevel, &nLowerLevel ))
+ {
+ if( bUpperLower )
+ {
+ // on top of the next numbering
+ SwNodeOffset nOffset(0);
+ const SwNode* pNd;
+
+ if( bUpperLeft ) // move up
+ {
+ SwPosition aPos( *aCursor.GetMark() );
+ if (SwDoc::GotoPrevNum( aPos, GetLayout(), false ))
+ nOffset = aPos.nNode.GetIndex() -
+ aCursor.GetMark()->nNode.GetIndex();
+ else
+ {
+ SwNodeOffset nStt = aPos.nNode.GetIndex(), nIdx = nStt - 1;
+
+ if (SwTextNode const*const pStt = aPos.nNode.GetNode().GetTextNode())
+ {
+ std::pair<SwTextNode *, SwTextNode *> nodes(
+ sw::GetFirstAndLastNode(*GetLayout(), *pStt));
+ nIdx = nodes.first->GetIndex() - 1;
+ }
+ while( nIdx && (
+ ( pNd = GetDoc()->GetNodes()[ nIdx ])->IsSectionNode() ||
+ ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode())))
+ --nIdx;
+ if( GetDoc()->GetNodes()[ nIdx ]->IsTextNode() )
+ nOffset = nIdx - nStt;
+ }
+ }
+ else // move down
+ {
+ assert(!aCursor.GetNode().IsTextNode()
+ || sw::IsParaPropsNode(*GetLayout(), *aCursor.GetNode().GetTextNode()));
+ const SwNumRule* pOrig = sw::GetParaPropsNode(*GetLayout(), *aCursor.GetNode(false).GetTextNode())->GetNumRule();
+ if( aCursor.GetNode().IsTextNode() &&
+ pOrig == aCursor.GetNode().GetTextNode()->GetNumRule() )
+ {
+ SwNodeOffset nStt = aCursor.GetPoint()->nNode.GetIndex(), nIdx = nStt+1;
+ if (SwTextNode const*const pStt = aCursor.GetPoint()->nNode.GetNode().GetTextNode())
+ {
+ std::pair<SwTextNode *, SwTextNode *> nodes(
+ sw::GetFirstAndLastNode(*GetLayout(), *pStt));
+ nIdx = nodes.second->GetIndex() + 1;
+ }
+
+ while (nIdx < GetDoc()->GetNodes().Count()-1)
+ {
+ pNd = GetDoc()->GetNodes()[ nIdx ];
+
+ if (pNd->IsSectionNode() ||
+ (pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode()))
+ {
+ ++nIdx;
+ }
+ else if (pNd->IsTextNode())
+ {
+ SwTextNode const*const pTextNode =
+ sw::GetParaPropsNode(*GetLayout(), SwNodeIndex(*pNd));
+ if (pOrig == pTextNode->GetNumRule()
+ && pTextNode->GetActualListLevel() > nUpperLevel)
+ {
+ std::pair<SwTextNode *, SwTextNode *> nodes(
+ sw::GetFirstAndLastNode(*GetLayout(), *pTextNode));
+ nIdx = nodes.second->GetIndex() + 1;
+ }
+ else
+ {
+ break;
+ }
+ }
+ // #i57856#
+ else
+ {
+ break;
+ }
+ }
+
+ if( nStt == nIdx || !GetDoc()->GetNodes()[ nIdx ]->IsTextNode() )
+ nOffset = SwNodeOffset(1);
+ else
+ nOffset = nIdx - nStt;
+ }
+ else
+ nOffset = SwNodeOffset(1);
+ }
+
+ if( nOffset )
+ {
+ aCursor.Move( fnMoveBackward, GoInNode );
+ bRet = GetDoc()->MoveParagraph( aCursor, nOffset );
+ }
+ }
+ else if( (bUpperLeft ? nUpperLevel : nLowerLevel+1) < MAXLEVEL )
+ {
+ aCursor.Move( fnMoveBackward, GoInNode );
+ bRet = GetDoc()->NumUpDown(aCursor, !bUpperLeft, GetLayout());
+ }
+ }
+
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::OutlineUpDown( short nOffset )
+{
+ StartAllAction();
+
+ bool bRet = true;
+ SwPaM* pCursor = GetCursor();
+ if( !pCursor->IsMultiSelection() )
+ bRet = GetDoc()->OutlineUpDown(*pCursor, nOffset, GetLayout());
+ else
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ bRet = bRet && GetDoc()->OutlineUpDown(
+ aRangeArr.SetPam(n, aPam), nOffset, GetLayout());
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::MoveOutlinePara( SwOutlineNodes::difference_type nOffset )
+{
+ StartAllAction();
+ bool bRet = GetDoc()->MoveOutlinePara( *GetCursor(), nOffset );
+ EndAllAction();
+ return bRet;
+}
+
+// Outlines and SubOutline are ReadOnly?
+bool SwEditShell::IsProtectedOutlinePara() const
+{
+ bool bRet = false;
+ const SwNode& rNd = GetCursor()->Start()->nNode.GetNode();
+ if( rNd.IsTextNode() )
+ {
+ const SwOutlineNodes& rOutlNd = GetDoc()->GetNodes().GetOutLineNds();
+ SwNode* pNd = const_cast<SwNode*>(&rNd);
+ bool bFirst = true;
+ SwOutlineNodes::size_type nPos;
+ int nLvl(0);
+ if( !rOutlNd.Seek_Entry( pNd, &nPos ) && nPos )
+ --nPos;
+
+ for( ; nPos < rOutlNd.size(); ++nPos )
+ {
+ SwNode* pTmpNd = rOutlNd[ nPos ];
+
+ if (!sw::IsParaPropsNode(*GetLayout(), *pTmpNd->GetTextNode()))
+ {
+ continue;
+ }
+
+ int nTmpLvl = pTmpNd->GetTextNode()->GetAttrOutlineLevel();
+
+ OSL_ENSURE( nTmpLvl >= 0 && nTmpLvl <= MAXLEVEL,
+ "<SwEditShell::IsProtectedOutlinePara()>" );
+
+ if( bFirst )
+ {
+ nLvl = nTmpLvl;
+ bFirst = false;
+ }
+ else if( nLvl >= nTmpLvl )
+ break;
+
+ if( pTmpNd->IsProtect() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL("Cursor not on an outline node");
+ }
+#endif
+ return bRet;
+}
+
+/** Test whether outline may be moved (bCopy == false)
+ * or copied (bCopy == true)
+ * Verify these conditions:
+ * 1) outline must be within main body (and not in redline)
+ * 2) outline must not be within table
+ * 3) if bCopy is set, outline must not be write protected
+ */
+static bool lcl_IsOutlineMoveAndCopyable(SwEditShell const& rShell,
+ SwOutlineNodes::size_type const nIdx, bool const bCopy)
+{
+ const SwNodes& rNds = rShell.GetDoc()->GetNodes();
+ const SwNode* pNd = rNds.GetOutLineNds()[ nIdx ];
+ return pNd->GetIndex() >= rNds.GetEndOfExtras().GetIndex() && // 1) body
+ !pNd->FindTableNode() && // 2) table
+ sw::IsParaPropsNode(*rShell.GetLayout(), *pNd->GetTextNode()) &&
+ ( bCopy || !pNd->IsProtect() ); // 3) write
+}
+
+bool SwEditShell::IsOutlineMovable( SwOutlineNodes::size_type nIdx ) const
+{
+ return lcl_IsOutlineMoveAndCopyable( *this, nIdx, false );
+}
+
+bool SwEditShell::IsOutlineCopyable( SwOutlineNodes::size_type nIdx ) const
+{
+ return lcl_IsOutlineMoveAndCopyable( *this, nIdx, true );
+}
+
+bool SwEditShell::NumOrNoNum(
+ bool bNumOn,
+ bool bChkStart )
+{
+ bool bRet = false;
+
+ if ( !IsMultiSelection()
+ && !HasSelection()
+ && ( !bChkStart || IsSttPara() ) )
+ {
+ StartAllAction();
+ SwPosition const pos(sw::GetParaPropsPos(*GetLayout(), *GetCursor()->GetPoint()));
+ bRet = GetDoc()->NumOrNoNum(pos.nNode, !bNumOn);
+ EndAllAction();
+ }
+ return bRet;
+}
+
+bool SwEditShell::IsNoNum( bool bChkStart ) const
+{
+ // a Backspace in the paragraph without number becomes a Delete
+ bool bResult = false;
+
+ if ( !IsMultiSelection()
+ && !HasSelection()
+ && ( !bChkStart || IsSttPara() ) )
+ {
+ const SwTextNode* pTextNd = sw::GetParaPropsNode(*GetLayout(), GetCursor()->GetPoint()->nNode);
+ if ( pTextNd != nullptr )
+ {
+ bResult = !pTextNd->IsCountedInList();
+ }
+ }
+
+ return bResult;
+}
+
+sal_uInt8 SwEditShell::GetNumLevel() const
+{
+ // return current level where the point of the cursor is
+ sal_uInt8 nLevel = MAXLEVEL;
+
+ SwPaM* pCursor = GetCursor();
+ const SwTextNode *const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
+
+ OSL_ENSURE( pTextNd, "GetNumLevel() without text node" );
+ if ( pTextNd == nullptr )
+ return nLevel;
+
+ const SwNumRule* pRule = pTextNd->GetNumRule();
+ if ( pRule != nullptr )
+ {
+ const int nListLevelOfTextNode( pTextNd->GetActualListLevel() );
+ if ( nListLevelOfTextNode >= 0 )
+ {
+ nLevel = static_cast<sal_uInt8>( nListLevelOfTextNode );
+ }
+ }
+
+ return nLevel;
+}
+
+const SwNumRule* SwEditShell::GetNumRuleAtCurrCursorPos() const
+{
+ SwPosition pos(*GetCursor()->GetPoint());
+ return SwDoc::GetNumRuleAtPos( pos, GetLayout() );
+}
+
+const SwNumRule* SwEditShell::GetNumRuleAtCurrentSelection() const
+{
+ const SwNumRule* pNumRuleAtCurrentSelection = nullptr;
+
+ bool bDifferentNumRuleFound = false;
+ for(const SwPaM& rCurrentCursor : GetCursor()->GetRingContainer())
+ {
+ const SwNodeIndex aEndNode = rCurrentCursor.End()->nNode;
+
+ for ( SwNodeIndex aNode = rCurrentCursor.Start()->nNode; aNode <= aEndNode; ++aNode )
+ {
+ SwPosition pos(aNode);
+ const SwNumRule* pNumRule = SwDoc::GetNumRuleAtPos(pos, GetLayout());
+ if ( pNumRule == nullptr )
+ {
+ continue;
+ }
+ else if ( pNumRule != pNumRuleAtCurrentSelection )
+ {
+ if ( pNumRuleAtCurrentSelection == nullptr )
+ {
+ pNumRuleAtCurrentSelection = pNumRule;
+ }
+ else
+ {
+ pNumRuleAtCurrentSelection = nullptr;
+ bDifferentNumRuleFound = true;
+ break;
+ }
+ }
+ }
+ if(bDifferentNumRuleFound)
+ break;
+ }
+
+ return pNumRuleAtCurrentSelection;
+}
+
+void SwEditShell::SetCurNumRule( const SwNumRule& rRule,
+ bool bCreateNewList,
+ const OUString& rContinuedListId,
+ const bool bResetIndentAttrs )
+{
+ StartAllAction();
+
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSATTR, nullptr );
+
+ SwPaM* pCursor = GetCursor();
+ if( IsMultiSelection() )
+ {
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ OUString sContinuedListId(rContinuedListId);
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ aRangeArr.SetPam( n, aPam );
+ OUString sListId = GetDoc()->SetNumRule( aPam, rRule,
+ bCreateNewList, GetLayout(), sContinuedListId,
+ true, bResetIndentAttrs );
+
+ //tdf#87548 On creating a new list for a multi-selection only
+ //create a single new list for the multi-selection, not one per selection
+ if (bCreateNewList)
+ {
+ sContinuedListId = sListId;
+ bCreateNewList = false;
+ }
+
+ GetDoc()->SetCounted(aPam, true, GetLayout());
+ }
+ }
+ else
+ {
+ GetDoc()->SetNumRule( *pCursor, rRule,
+ bCreateNewList, GetLayout(), rContinuedListId,
+ true, bResetIndentAttrs );
+ GetDoc()->SetCounted( *pCursor, true, GetLayout() );
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSATTR, nullptr );
+
+ EndAllAction();
+}
+
+OUString SwEditShell::GetUniqueNumRuleName() const
+{
+ return GetDoc()->GetUniqueNumRuleName();
+}
+
+void SwEditShell::ChgNumRuleFormats( const SwNumRule& rRule )
+{
+ StartAllAction();
+ GetDoc()->ChgNumRuleFormats( rRule );
+ EndAllAction();
+}
+
+void SwEditShell::ReplaceNumRule( const OUString& rOldRule, const OUString& rNewRule )
+{
+ StartAllAction();
+ SwPosition const pos(sw::GetParaPropsPos(*GetLayout(), *GetCursor()->GetPoint()));
+ GetDoc()->ReplaceNumRule( pos, rOldRule, rNewRule );
+ EndAllAction();
+}
+
+void SwEditShell::SetNumRuleStart( bool bFlag, SwPaM* pPaM )
+{
+ StartAllAction();
+ SwPaM* pCursor = pPaM ? pPaM : GetCursor();
+ if( pCursor->IsMultiSelection() ) // multiple selection ?
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ SwPosition const pos(sw::GetParaPropsPos(*GetLayout(), *aRangeArr.SetPam( n, aPam ).GetPoint()));
+ GetDoc()->SetNumRuleStart( pos, bFlag );
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ {
+ SwPosition const pos(sw::GetParaPropsPos(*GetLayout(), *GetCursor()->GetPoint()));
+ GetDoc()->SetNumRuleStart(pos, bFlag);
+ }
+
+ EndAllAction();
+}
+
+bool SwEditShell::IsNumRuleStart( SwPaM* pPaM ) const
+{
+ SwPaM* pCursor = pPaM ? pPaM : GetCursor( );
+ const SwTextNode *const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
+ return pTextNd && pTextNd->IsListRestart();
+}
+
+void SwEditShell::SetNodeNumStart( sal_uInt16 nStt )
+{
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->IsMultiSelection() ) // multiple selection ?
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( *pCursor );
+ SwPaM aPam( *pCursor->GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ SwPosition const pos(sw::GetParaPropsPos(*GetLayout(), *aRangeArr.SetPam( n, aPam ).GetPoint()));
+ GetDoc()->SetNodeNumStart( pos, nStt );
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ {
+ SwPosition const pos(sw::GetParaPropsPos(*GetLayout(), *pCursor->GetPoint()));
+ GetDoc()->SetNodeNumStart( pos, nStt );
+ }
+
+ EndAllAction();
+}
+
+sal_uInt16 SwEditShell::GetNodeNumStart( SwPaM* pPaM ) const
+{
+ SwPaM* pCursor = pPaM ? pPaM : GetCursor();
+ const SwTextNode *const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode);
+ // correction: check, if list restart value is set at text node and
+ // use new method <SwTextNode::GetAttrListRestartValue()>.
+ // return USHRT_MAX, if no list restart value is found.
+ if ( pTextNd && pTextNd->HasAttrListRestartValue() )
+ {
+ return o3tl::narrowing<sal_uInt16>(pTextNd->GetAttrListRestartValue());
+ }
+ return USHRT_MAX;
+}
+
+const SwNumRule * SwEditShell::SearchNumRule( const bool bNum,
+ OUString& sListId )
+{
+ return GetDoc()->SearchNumRule( *(GetCursor()->Start()),
+ false/*bForward*/, bNum, false/*bOutline*/, -1/*nNonEmptyAllowe*/,
+ sListId, GetLayout() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edredln.cxx b/sw/source/core/edit/edredln.cxx
new file mode 100644
index 000000000..835a1c373
--- /dev/null
+++ b/sw/source/core/edit/edredln.cxx
@@ -0,0 +1,196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <IDocumentRedlineAccess.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+#include <doc.hxx>
+#include <editsh.hxx>
+#include <frmtool.hxx>
+
+RedlineFlags SwEditShell::GetRedlineFlags() const
+{
+ return GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags();
+}
+
+void SwEditShell::SetRedlineFlags( RedlineFlags eMode )
+{
+ if( eMode != GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags() )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eMode );
+ EndAllAction();
+ }
+}
+
+bool SwEditShell::IsRedlineOn() const
+{
+ return GetDoc()->getIDocumentRedlineAccess().IsRedlineOn();
+}
+
+SwRedlineTable::size_type SwEditShell::GetRedlineCount() const
+{
+ return GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().size();
+}
+
+const SwRangeRedline& SwEditShell::GetRedline( SwRedlineTable::size_type nPos ) const
+{
+ return *GetDoc()->getIDocumentRedlineAccess().GetRedlineTable()[ nPos ];
+}
+
+static void lcl_InvalidateAll( SwViewShell* pSh )
+{
+ for(SwViewShell& rCurrentShell : pSh->GetRingContainer())
+ {
+ if ( rCurrentShell.GetWin() )
+ rCurrentShell.GetWin()->Invalidate();
+ }
+}
+
+bool SwEditShell::AcceptRedline( SwRedlineTable::size_type nPos )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ bool bRet = GetDoc()->getIDocumentRedlineAccess().AcceptRedline( nPos, true );
+ if( !nPos && !::IsExtraData( GetDoc() ) )
+ lcl_InvalidateAll( this );
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::RejectRedline( SwRedlineTable::size_type nPos )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ bool bRet = GetDoc()->getIDocumentRedlineAccess().RejectRedline( nPos, true );
+ if( !nPos && !::IsExtraData( GetDoc() ) )
+ lcl_InvalidateAll( this );
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::AcceptRedlinesInSelection()
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ // in table selection mode, process the selected boxes in reverse order
+ // to allow accepting their text changes and the tracked row deletions
+ bool bRet = false;
+ if ( IsTableMode() )
+ {
+ const SwSelBoxes& rBoxes = GetTableCursor()->GetSelectedBoxes();
+ std::vector<std::unique_ptr<SwPaM>> vBoxes;
+ for(auto pBox : rBoxes)
+ {
+ if ( !pBox->IsEmpty() )
+ {
+ const SwStartNode *pSttNd = pBox->GetSttNd();
+ SwNode* pEndNode = pSttNd->GetNodes()[pSttNd->EndOfSectionIndex()];
+ vBoxes.push_back(std::unique_ptr<SwPaM>(new SwPaM(*pEndNode, 0, *pSttNd, 0)));
+ }
+ }
+
+ for (size_t i = 0; i < vBoxes.size(); ++i)
+ bRet |= GetDoc()->getIDocumentRedlineAccess().AcceptRedline( *vBoxes[vBoxes.size()-i-1], true );
+ }
+ else
+ bRet = GetDoc()->getIDocumentRedlineAccess().AcceptRedline( *GetCursor(), true );
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::RejectRedlinesInSelection()
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ bool bRet = false;
+ // in table selection mode, process the selected boxes in reverse order
+ // to allow rejecting their text changes and the tracked row insertions
+ if ( IsTableMode() )
+ {
+ const SwSelBoxes& rBoxes = GetTableCursor()->GetSelectedBoxes();
+ std::vector<std::unique_ptr<SwPaM>> vBoxes;
+ for(auto pBox : rBoxes)
+ {
+ if ( !pBox->IsEmpty() )
+ {
+ const SwStartNode *pSttNd = pBox->GetSttNd();
+ SwNode* pEndNode = pSttNd->GetNodes()[pSttNd->EndOfSectionIndex()];
+ vBoxes.push_back(std::unique_ptr<SwPaM>(new SwPaM(*pEndNode, 0, *pSttNd, 0)));
+ }
+ }
+
+ for (size_t i = 0; i < vBoxes.size(); ++i)
+ bRet |= GetDoc()->getIDocumentRedlineAccess().RejectRedline( *vBoxes[vBoxes.size()-i-1], true );
+ }
+ else
+ bRet = GetDoc()->getIDocumentRedlineAccess().RejectRedline( *GetCursor(), true );
+ EndAllAction();
+ return bRet;
+}
+
+// Set the comment at the Redline
+bool SwEditShell::SetRedlineComment( const OUString& rS )
+{
+ bool bRet = false;
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ bRet = bRet || GetDoc()->getIDocumentRedlineAccess().SetRedlineComment( rPaM, rS );
+ }
+
+ return bRet;
+}
+
+const SwRangeRedline* SwEditShell::GetCurrRedline() const
+{
+ if (const SwRangeRedline* pRed = GetDoc()->getIDocumentRedlineAccess().GetRedline( *GetCursor()->GetPoint(), nullptr ))
+ return pRed;
+ // check the other side of the selection to handle completely selected changes, where the Point is at the end
+ return GetDoc()->getIDocumentRedlineAccess().GetRedline( *GetCursor()->GetMark(), nullptr );
+}
+
+void SwEditShell::UpdateRedlineAttr()
+{
+ if( IDocumentRedlineAccess::IsShowChanges(GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags()) )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ GetDoc()->getIDocumentRedlineAccess().UpdateRedlineAttr();
+
+ EndAllAction();
+ }
+}
+
+/** Search the Redline of the data given
+ *
+ * @return Returns the Pos of the Array, or SwRedlineTable::npos if not present
+ */
+SwRedlineTable::size_type SwEditShell::FindRedlineOfData( const SwRedlineData& rData ) const
+{
+ const SwRedlineTable& rTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+
+ for( SwRedlineTable::size_type i = 0, nCnt = rTable.size(); i < nCnt; ++i )
+ if( &rTable[ i ]->GetRedlineData() == &rData )
+ return i;
+ return SwRedlineTable::npos;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edsect.cxx b/sw/source/core/edit/edsect.cxx
new file mode 100644
index 000000000..71541b4de
--- /dev/null
+++ b/sw/source/core/edit/edsect.cxx
@@ -0,0 +1,422 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <pam.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <section.hxx>
+#include <sectfrm.hxx>
+#include <cntfrm.hxx>
+#include <tabfrm.hxx>
+#include <rootfrm.hxx>
+#include <osl/diagnose.h>
+
+SwSection const*
+SwEditShell::InsertSection(
+ SwSectionData & rNewData, SfxItemSet const*const pAttr)
+{
+ const SwSection* pRet = nullptr;
+ if( !IsTableMode() )
+ {
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSSECTION, nullptr );
+
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ SwSection const*const pNew =
+ GetDoc()->InsertSwSection( rPaM, rNewData, nullptr, pAttr );
+ if( !pRet )
+ pRet = pNew;
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSSECTION, nullptr );
+ EndAllAction();
+ }
+ return pRet;
+}
+
+bool SwEditShell::IsInsRegionAvailable() const
+{
+ if( IsTableMode() )
+ return false;
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->GetNext() != pCursor )
+ return false;
+ if( pCursor->HasMark() )
+ return 0 != SwDoc::IsInsRegionAvailable( *pCursor );
+
+ return true;
+}
+
+const SwSection* SwEditShell::GetCurrSection() const
+{
+ if( IsTableMode() )
+ return nullptr;
+
+ return SwDoc::GetCurrSection( *GetCursor()->GetPoint() );
+}
+
+/** Deliver the responsible area of the columns.
+ *
+ * In footnotes it may not be the area within the footnote.
+ */
+SwSection* SwEditShell::GetAnySection( bool bOutOfTab, const Point* pPt )
+{
+ SwFrame *pFrame;
+ if ( pPt )
+ {
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( *pPt );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
+ SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(*pPt, true);
+ pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ }
+ else
+ pFrame = GetCurrFrame( false );
+
+ if( bOutOfTab && pFrame )
+ pFrame = pFrame->FindTabFrame();
+ if( pFrame && pFrame->IsInSct() )
+ {
+ SwSectionFrame* pSect = pFrame->FindSctFrame();
+ OSL_ENSURE( pSect, "GetAnySection: Where's my Sect?" );
+ if( pSect->IsInFootnote() && pSect->GetUpper()->IsInSct() )
+ {
+ pSect = pSect->GetUpper()->FindSctFrame();
+ OSL_ENSURE( pSect, "GetAnySection: Where's my SectFrame?" );
+ }
+ return pSect->GetSection();
+ }
+ return nullptr;
+}
+
+size_t SwEditShell::GetSectionFormatCount() const
+{
+ return GetDoc()->GetSections().size();
+}
+
+bool SwEditShell::IsAnySectionInDoc() const
+{
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+
+ for( const SwSectionFormat* pFormat : rFormats )
+ {
+ SectionType eTmpType;
+ if( pFormat->IsInNodesArr() &&
+ ( (eTmpType = pFormat->GetSection()->GetType()) != SectionType::ToxContent
+ && SectionType::ToxHeader != eTmpType ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+size_t SwEditShell::GetSectionFormatPos( const SwSectionFormat& rFormat ) const
+{
+ SwSectionFormat* pFormat = const_cast<SwSectionFormat*>(&rFormat);
+ return GetDoc()->GetSections().GetPos( pFormat );
+}
+
+const SwSectionFormat& SwEditShell::GetSectionFormat(size_t nFormat) const
+{
+ return *GetDoc()->GetSections()[ nFormat ];
+}
+
+void SwEditShell::DelSectionFormat(size_t nFormat)
+{
+ StartAllAction();
+ GetDoc()->DelSectionFormat( GetDoc()->GetSections()[ nFormat ] );
+ // Call the AttrChangeNotify on the UI page.
+ CallChgLnk();
+ EndAllAction();
+}
+
+void SwEditShell::UpdateSection(size_t const nSect,
+ SwSectionData & rNewData, SfxItemSet const*const pAttr)
+{
+ StartAllAction();
+ GetDoc()->UpdateSection( nSect, rNewData, pAttr );
+ // Call the AttrChangeNotify on the UI page.
+ CallChgLnk();
+ EndAllAction();
+}
+
+OUString SwEditShell::GetUniqueSectionName( const OUString* pChkStr ) const
+{
+ return GetDoc()->GetUniqueSectionName( pChkStr );
+}
+
+void SwEditShell::SetSectionAttr( const SfxItemSet& rSet,
+ SwSectionFormat* pSectFormat )
+{
+ if( pSectFormat )
+ SetSectionAttr_( *pSectFormat, rSet );
+ else
+ {
+ // for all section in the selection
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ const SwPosition* pStt = rPaM.Start(),
+ * pEnd = rPaM.End();
+
+ SwSectionNode* pSttSectNd = pStt->nNode.GetNode().FindSectionNode(),
+ * pEndSectNd = pEnd->nNode.GetNode().FindSectionNode();
+
+ if( pSttSectNd || pEndSectNd )
+ {
+ if( pSttSectNd )
+ SetSectionAttr_( *pSttSectNd->GetSection().GetFormat(),
+ rSet );
+ if( pEndSectNd && pSttSectNd != pEndSectNd )
+ SetSectionAttr_( *pEndSectNd->GetSection().GetFormat(),
+ rSet );
+
+ if( pSttSectNd && pEndSectNd )
+ {
+ SwNodeIndex aSIdx( pStt->nNode );
+ SwNodeIndex aEIdx( pEnd->nNode );
+ if( pSttSectNd->EndOfSectionIndex() <
+ pEndSectNd->GetIndex() )
+ {
+ aSIdx = pSttSectNd->EndOfSectionIndex() + 1;
+ aEIdx = *pEndSectNd;
+ }
+
+ while( aSIdx < aEIdx )
+ {
+ if( nullptr != (pSttSectNd = aSIdx.GetNode().GetSectionNode())
+ || ( aSIdx.GetNode().IsEndNode() &&
+ nullptr != ( pSttSectNd = aSIdx.GetNode().
+ StartOfSectionNode()->GetSectionNode())) )
+ SetSectionAttr_( *pSttSectNd->GetSection().GetFormat(),
+ rSet );
+ ++aSIdx;
+ }
+ }
+ }
+
+ }
+ }
+}
+
+void SwEditShell::SetSectionAttr_( SwSectionFormat& rSectFormat,
+ const SfxItemSet& rSet )
+{
+ StartAllAction();
+ if(SfxItemState::SET == rSet.GetItemState(RES_CNTNT, false))
+ {
+ SfxItemSet aSet(rSet);
+ aSet.ClearItem(RES_CNTNT);
+ GetDoc()->SetAttr( aSet, rSectFormat );
+ }
+ else
+ GetDoc()->SetAttr( rSet, rSectFormat );
+
+ // Call the AttrChangeNotify on the UI page.
+ CallChgLnk();
+ EndAllAction();
+}
+
+/** Search inside the cursor selection for full selected sections.
+ *
+ * @return If any part of section in the selection return 0, if more than one return the count.
+ */
+sal_uInt16 SwEditShell::GetFullSelectedSectionCount() const
+{
+ sal_uInt16 nRet = 0;
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+
+ const SwPosition* pStt = rPaM.Start(),
+ * pEnd = rPaM.End();
+ const SwContentNode* pCNd;
+ // check the selection, if Start at Node begin and End at Node end
+ if( pStt->nContent.GetIndex() ||
+ ( nullptr == ( pCNd = pEnd->nNode.GetNode().GetContentNode() )) ||
+ pCNd->Len() != pEnd->nContent.GetIndex() )
+ {
+ nRet = 0;
+ break;
+ }
+
+// !!!
+// what about table at start or end ?
+// There is no selection possible!
+// What about only a table inside the section ?
+// There is only a table selection possible!
+
+ SwNodeIndex aSIdx( pStt->nNode, -1 ), aEIdx( pEnd->nNode, +1 );
+ if( !aSIdx.GetNode().IsSectionNode() ||
+ !aEIdx.GetNode().IsEndNode() ||
+ !aEIdx.GetNode().StartOfSectionNode()->IsSectionNode() )
+ {
+ nRet = 0;
+ break;
+ }
+
+ ++nRet;
+ if( &aSIdx.GetNode() != aEIdx.GetNode().StartOfSectionNode() )
+ ++nRet;
+
+ }
+ return nRet;
+}
+
+/** Find the suitable node for a special insert (alt-enter).
+ *
+ * This should enable inserting text before/after sections and tables.
+ *
+ * A node is found if:
+ * 1) the innermost table/section is not in a write-protected area
+ * 2) pCurrentPos is at or just before an end node
+ * (or at or just after a start node)
+ * 3) there are only start/end nodes between pCurrentPos and the innermost
+ * table/section
+ *
+ * If a suitable node is found, an SwNode* is returned; else it is NULL.
+ */
+static const SwNode* lcl_SpecialInsertNode(const SwPosition* pCurrentPos)
+{
+ const SwNode* pReturn = nullptr;
+
+ // the current position
+ OSL_ENSURE( pCurrentPos != nullptr, "Strange, we have no position!" );
+ const SwNode& rCurrentNode = pCurrentPos->nNode.GetNode();
+
+ // find innermost section or table. At the end of this scope,
+ // pInnermostNode contains the section/table before/after which we should
+ // insert our empty paragraph, or it will be NULL if none is found.
+ const SwNode* pInnermostNode = nullptr;
+ {
+ const SwNode* pTableNode = rCurrentNode.FindTableNode();
+ const SwNode* pSectionNode = rCurrentNode.FindSectionNode();
+
+ // find the table/section which is close
+ if( pTableNode == nullptr )
+ pInnermostNode = pSectionNode;
+ else if ( pSectionNode == nullptr )
+ pInnermostNode = pTableNode;
+ else
+ {
+ // compare and choose the larger one
+ pInnermostNode =
+ ( pSectionNode->GetIndex() > pTableNode->GetIndex() )
+ ? pSectionNode : pTableNode;
+ }
+ }
+
+ // The previous version had a check to skip empty read-only sections. Those
+ // shouldn't occur, so we only need to check whether our pInnermostNode is
+ // inside a protected area.
+
+ // Now, pInnermostNode is NULL or the innermost section or table node.
+ if( (pInnermostNode != nullptr) && !pInnermostNode->IsProtect() )
+ {
+ OSL_ENSURE( pInnermostNode->IsTableNode() ||
+ pInnermostNode->IsSectionNode(), "wrong node found" );
+ OSL_ENSURE( ( pInnermostNode->GetIndex() <= rCurrentNode.GetIndex() )&&
+ ( pInnermostNode->EndOfSectionNode()->GetIndex() >=
+ rCurrentNode.GetIndex() ), "wrong node found" );
+
+ // we now need to find the possible start/end positions
+
+ // we found a start if
+ // - we're at or just before a start node
+ // - there are only start nodes between the current and pInnermostNode
+ SwNodeIndex aBegin( pCurrentPos->nNode );
+ if( rCurrentNode.IsContentNode() &&
+ (pCurrentPos->nContent.GetIndex() == 0))
+ --aBegin;
+ while( (aBegin != pInnermostNode->GetIndex()) &&
+ aBegin.GetNode().IsStartNode() )
+ --aBegin;
+ bool bStart = ( aBegin == pInnermostNode->GetIndex() );
+
+ // we found an end if
+ // - we're at or just before an end node
+ // - there are only end nodes between the current node and
+ // pInnermostNode's end node
+ SwNodeIndex aEnd( pCurrentPos->nNode );
+ if( rCurrentNode.IsContentNode() &&
+ ( pCurrentPos->nContent.GetIndex() ==
+ rCurrentNode.GetContentNode()->Len() ) )
+ ++aEnd;
+ while( (aEnd != pInnermostNode->EndOfSectionNode()->GetIndex()) &&
+ aEnd.GetNode().IsEndNode() )
+ ++aEnd;
+ bool bEnd = ( aEnd == pInnermostNode->EndOfSectionNode()->GetIndex() );
+
+ // evaluate result: if both start + end, end is preferred
+ if( bEnd )
+ pReturn = pInnermostNode->EndOfSectionNode();
+ else if ( bStart )
+ pReturn = pInnermostNode;
+ }
+
+ OSL_ENSURE( ( pReturn == nullptr ) || pReturn->IsStartNode() ||
+ pReturn->IsEndNode(),
+ "SpecialInsertNode failed" );
+ return pReturn;
+}
+
+/** a node can be special-inserted (alt-Enter) whenever lcl_SpecialInsertNode
+ finds a suitable position
+*/
+bool SwEditShell::CanSpecialInsert() const
+{
+ return nullptr != lcl_SpecialInsertNode( GetCursor()->GetPoint() );
+}
+
+/** check whether a node can be special-inserted (alt-Enter), and do so. Return
+ whether insertion was possible.
+ */
+void SwEditShell::DoSpecialInsert()
+{
+ // get current node
+ SwPosition* pCursorPos = GetCursor()->GetPoint();
+ const SwNode* pInsertNode = lcl_SpecialInsertNode( pCursorPos );
+ if( pInsertNode == nullptr )
+ return;
+
+ StartAllAction();
+
+ // adjust insert position to insert before start nodes and after end
+ // nodes
+ SwNodeIndex aInsertIndex( *pInsertNode,
+ SwNodeOffset(pInsertNode->IsStartNode() ? -1 : 0) );
+ SwPosition aInsertPos( aInsertIndex );
+
+ // insert a new text node, and set the cursor
+ GetDoc()->getIDocumentContentOperations().AppendTextNode( aInsertPos );
+ *pCursorPos = aInsertPos;
+
+ // call AttrChangeNotify for the UI
+ CallChgLnk();
+
+ EndAllAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edtab.cxx b/sw/source/core/edit/edtab.cxx
new file mode 100644
index 000000000..30cb3b412
--- /dev/null
+++ b/sw/source/core/edit/edtab.cxx
@@ -0,0 +1,532 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fesh.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+
+#include <swwait.hxx>
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <cntfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <tblsel.hxx>
+#include <cellfrm.hxx>
+#include <cellatr.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <mdiexp.hxx>
+#include <itabenum.hxx>
+#include <svl/numformat.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+namespace {
+
+void collectUIInformation(const OUString& rAction, const OUString& aParameters)
+{
+ EventDescription aDescription;
+ aDescription.aAction = rAction;
+ aDescription.aParameters = {{"parameters", aParameters}};
+ aDescription.aID = "writer_edit";
+ aDescription.aKeyWord = "SwEditWinUIObject";
+ aDescription.aParent = "MainWindow";
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+//Added for bug #i119954# Application crashed if undo/redo convert nest table to text
+static bool ConvertTableToText( const SwTableNode *pTableNode, sal_Unicode cCh );
+
+static void ConvertNestedTablesToText( const SwTableLines &rTableLines, sal_Unicode cCh )
+{
+ for (size_t n = 0; n < rTableLines.size(); ++n)
+ {
+ SwTableLine* pTableLine = rTableLines[ n ];
+ for (size_t i = 0; i < pTableLine->GetTabBoxes().size(); ++i)
+ {
+ SwTableBox* pTableBox = pTableLine->GetTabBoxes()[ i ];
+ if (pTableBox->GetTabLines().empty())
+ {
+ SwNodeIndex nodeIndex( *pTableBox->GetSttNd(), 1 );
+ SwNodeIndex endNodeIndex( *pTableBox->GetSttNd()->EndOfSectionNode() );
+ for( ; nodeIndex < endNodeIndex ; ++nodeIndex )
+ {
+ if ( SwTableNode* pTableNode = nodeIndex.GetNode().GetTableNode() )
+ ConvertTableToText( pTableNode, cCh );
+ }
+ }
+ else
+ {
+ ConvertNestedTablesToText( pTableBox->GetTabLines(), cCh );
+ }
+ }
+ }
+}
+
+bool ConvertTableToText( const SwTableNode *pConstTableNode, sal_Unicode cCh )
+{
+ SwTableNode *pTableNode = const_cast< SwTableNode* >( pConstTableNode );
+ ConvertNestedTablesToText( pTableNode->GetTable().GetTabLines(), cCh );
+ return pTableNode->GetDoc().TableToText( pTableNode, cCh );
+}
+//End for bug #i119954#
+
+const SwTable& SwEditShell::InsertTable( const SwInsertTableOptions& rInsTableOpts,
+ sal_uInt16 nRows, sal_uInt16 nCols,
+ const SwTableAutoFormat* pTAFormat )
+{
+ StartAllAction();
+ SwPosition* pPos = GetCursor()->GetPoint();
+
+ bool bEndUndo = 0 != pPos->nContent.GetIndex();
+ if( bEndUndo )
+ {
+ StartUndo( SwUndoId::START );
+ GetDoc()->getIDocumentContentOperations().SplitNode( *pPos, false );
+ }
+
+ // If called from a shell the adjust item is propagated
+ // from pPos to the new content nodes in the table.
+ const SwTable *pTable = GetDoc()->InsertTable( rInsTableOpts, *pPos,
+ nRows, nCols,
+ css::text::HoriOrientation::FULL, pTAFormat,
+ nullptr, true );
+ if( bEndUndo )
+ EndUndo( SwUndoId::END );
+
+ EndAllAction();
+
+ OUString parameter = " Columns : " + OUString::number( nCols ) + " , Rows : " + OUString::number( nRows ) + " ";
+ collectUIInformation("CREATE_TABLE", parameter);
+
+ return *pTable;
+}
+
+bool SwEditShell::TextToTable( const SwInsertTableOptions& rInsTableOpts,
+ sal_Unicode cCh,
+ const SwTableAutoFormat* pTAFormat )
+{
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ bool bRet = false;
+ StartAllAction();
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() )
+ bRet |= nullptr != GetDoc()->TextToTable( rInsTableOpts, rPaM, cCh,
+ css::text::HoriOrientation::FULL, pTAFormat );
+ }
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::TableToText( sal_Unicode cCh )
+{
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ SwPaM* pCursor = GetCursor();
+ const SwTableNode* pTableNd =
+ GetDoc()->IsIdxInTable( pCursor->GetPoint()->nNode );
+ if (!pTableNd)
+ return false;
+
+ if( IsTableMode() )
+ {
+ ClearMark();
+ pCursor = GetCursor();
+ }
+ else if (pCursor->GetNext() != pCursor)
+ return false;
+
+ // TL_CHART2:
+ // tell the charts about the table to be deleted and have them use their own data
+ GetDoc()->getIDocumentChartDataProviderAccess().CreateChartInternalDataProviders( &pTableNd->GetTable() );
+
+ StartAllAction();
+
+ // move current Cursor out of the listing area
+ SwNodeIndex aTabIdx( *pTableNd );
+ pCursor->DeleteMark();
+ pCursor->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
+ pCursor->GetPoint()->nContent.Assign( nullptr, 0 );
+ // move sPoint and Mark out of the area!
+ pCursor->SetMark();
+ pCursor->DeleteMark();
+
+ //Modified for bug #i119954# Application crashed if undo/redo convert nest table to text
+ StartUndo();
+ bool bRet = ConvertTableToText( pTableNd, cCh );
+ EndUndo();
+ //End for bug #i119954#
+ pCursor->GetPoint()->nNode = aTabIdx;
+
+ SwContentNode* pCNd = pCursor->GetContentNode();
+ if( !pCNd )
+ pCursor->Move( fnMoveForward, GoInContent );
+ else
+ pCursor->GetPoint()->nContent.Assign( pCNd, 0 );
+
+ EndAllAction();
+ return bRet;
+}
+
+bool SwEditShell::IsTextToTableAvailable() const
+{
+ bool bOnlyText = false;
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() && *rPaM.GetPoint() != *rPaM.GetMark() )
+ {
+ bOnlyText = true;
+
+ // check if selection is in listing
+ SwNodeOffset nStt = rPaM.Start()->nNode.GetIndex(),
+ nEnd = rPaM.End()->nNode.GetIndex();
+
+ for( ; nStt <= nEnd; ++nStt )
+ if( !GetDoc()->GetNodes()[ nStt ]->IsTextNode() )
+ {
+ bOnlyText = false;
+ break;
+ }
+
+ if( !bOnlyText )
+ break;
+ }
+ }
+
+ return bOnlyText;
+}
+
+void SwEditShell::InsertDDETable( const SwInsertTableOptions& rInsTableOpts,
+ SwDDEFieldType* pDDEType,
+ sal_uInt16 nRows, sal_uInt16 nCols )
+{
+ SwPosition* pPos = GetCursor()->GetPoint();
+
+ StartAllAction();
+
+ bool bEndUndo = 0 != pPos->nContent.GetIndex();
+ if( bEndUndo )
+ {
+ StartUndo( SwUndoId::START );
+ GetDoc()->getIDocumentContentOperations().SplitNode( *pPos, false );
+ }
+
+ const SwInsertTableOptions aInsTableOpts( rInsTableOpts.mnInsMode | SwInsertTableFlags::DefaultBorder,
+ rInsTableOpts.mnRowsToRepeat );
+ SwTable* pTable = const_cast<SwTable*>(GetDoc()->InsertTable( aInsTableOpts, *pPos,
+ nRows, nCols, css::text::HoriOrientation::FULL ));
+
+ SwTableNode* pTableNode = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[ 0 ]->
+ GetSttNd()->FindTableNode());
+ std::unique_ptr<SwDDETable> pDDETable(new SwDDETable( *pTable, pDDEType ));
+ pTableNode->SetNewTable( std::move(pDDETable) ); // set the DDE table
+
+ if( bEndUndo )
+ EndUndo( SwUndoId::END );
+
+ EndAllAction();
+}
+
+/** update fields of a listing */
+void SwEditShell::UpdateTable()
+{
+ const SwTableNode* pTableNd = IsCursorInTable();
+
+ if( pTableNd )
+ {
+ StartAllAction();
+ if( DoesUndo() )
+ StartUndo();
+ EndAllTableBoxEdit();
+ SwTableFormulaUpdate aTableUpdate( &pTableNd->GetTable() );
+ GetDoc()->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+ if( DoesUndo() )
+ EndUndo();
+ EndAllAction();
+ }
+}
+
+// get/set Change Mode
+
+TableChgMode SwEditShell::GetTableChgMode() const
+{
+ TableChgMode eMode;
+ const SwTableNode* pTableNd = IsCursorInTable();
+ if( pTableNd )
+ eMode = pTableNd->GetTable().GetTableChgMode();
+ else
+ eMode = GetTableChgDefaultMode();
+ return eMode;
+}
+
+void SwEditShell::SetTableChgMode( TableChgMode eMode )
+{
+ const SwTableNode* pTableNd = IsCursorInTable();
+
+ if( pTableNd )
+ {
+ const_cast<SwTable&>(pTableNd->GetTable()).SetTableChgMode( eMode );
+ if( !GetDoc()->getIDocumentState().IsModified() ) // Bug 57028
+ {
+ GetDoc()->GetIDocumentUndoRedo().SetUndoNoResetModified();
+ }
+ GetDoc()->getIDocumentState().SetModified();
+ }
+}
+
+bool SwEditShell::GetTableBoxFormulaAttrs( SfxItemSet& rSet ) const
+{
+ SwSelBoxes aBoxes;
+ if( IsTableMode() )
+ ::GetTableSelCrs( *this, aBoxes );
+ else
+ {
+ do {
+ SwFrame *pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+ if ( pFrame )
+ {
+ SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
+ aBoxes.insert( pBox );
+ }
+ } while( false );
+ }
+
+ for (size_t n = 0; n < aBoxes.size(); ++n)
+ {
+ const SwTableBox* pSelBox = aBoxes[ n ];
+ const SwTableBoxFormat* pTableFormat = static_cast<SwTableBoxFormat*>(pSelBox->GetFrameFormat());
+ if( !n )
+ {
+ // Convert formulae into external presentation
+ const SwTable& rTable = pSelBox->GetSttNd()->FindTableNode()->GetTable();
+
+ SwTableFormulaUpdate aTableUpdate( &rTable );
+ aTableUpdate.m_eFlags = TBL_BOXNAME;
+ GetDoc()->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+
+ rSet.Put( pTableFormat->GetAttrSet() );
+ }
+ else
+ rSet.MergeValues( pTableFormat->GetAttrSet() );
+ }
+ return 0 != rSet.Count();
+}
+
+void SwEditShell::SetTableBoxFormulaAttrs( const SfxItemSet& rSet )
+{
+ CurrShell aCurr( this );
+ SwSelBoxes aBoxes;
+ if( IsTableMode() )
+ ::GetTableSelCrs( *this, aBoxes );
+ else
+ {
+ do {
+ SwFrame *pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+ if ( pFrame )
+ {
+ SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
+ aBoxes.insert( pBox );
+ }
+ } while( false );
+ }
+
+ // When setting a formula, do not check further!
+ if( SfxItemState::SET == rSet.GetItemState( RES_BOXATR_FORMULA ))
+ ClearTableBoxContent();
+
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ for (size_t n = 0; n < aBoxes.size(); ++n)
+ {
+ GetDoc()->SetTableBoxFormulaAttrs( *aBoxes[ n ], rSet );
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ EndAllAction();
+}
+
+bool SwEditShell::IsTableBoxTextFormat() const
+{
+ if( IsTableMode() )
+ return false;
+
+ const SwTableBox *pBox = nullptr;
+ {
+ SwFrame *pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+ if ( pFrame )
+ pBox = static_cast<SwCellFrame*>(pFrame)->GetTabBox();
+ }
+
+ if( !pBox )
+ return false;
+
+ sal_uInt32 nFormat = 0;
+ if( const SwTableBoxNumFormat* pItem = pBox->GetFrameFormat()->GetAttrSet().GetItemIfSet(
+ RES_BOXATR_FORMAT ))
+ {
+ nFormat = pItem->GetValue();
+ return GetDoc()->GetNumberFormatter()->IsTextFormat( nFormat );
+ }
+
+ SwNodeOffset nNd = pBox->IsValidNumTextNd();
+ if( NODE_OFFSET_MAX == nNd )
+ return true;
+
+ const OUString& rText = GetDoc()->GetNodes()[ nNd ]->GetTextNode()->GetText();
+ if( rText.isEmpty() )
+ return false;
+
+ double fVal;
+ return !GetDoc()->IsNumberFormat( rText, nFormat, fVal );
+}
+
+OUString SwEditShell::GetTableBoxText() const
+{
+ OUString sRet;
+ if( !IsTableMode() )
+ {
+ const SwTableBox *pBox = nullptr;
+ {
+ SwFrame *pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+ if ( pFrame )
+ pBox = static_cast<SwCellFrame*>(pFrame)->GetTabBox();
+ }
+
+ SwNodeOffset nNd;
+ if( pBox && NODE_OFFSET_MAX != ( nNd = pBox->IsValidNumTextNd() ) )
+ sRet = GetDoc()->GetNodes()[ nNd ]->GetTextNode()->GetText();
+ }
+ return sRet;
+}
+
+void SwEditShell::SplitTable( SplitTable_HeadlineOption eMode )
+{
+ SwPaM *pCursor = GetCursor();
+ if( pCursor->GetNode().FindTableNode() )
+ {
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ GetDoc()->SplitTable( *pCursor->GetPoint(), eMode, true );
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ ClearFEShellTabCols(*GetDoc(), nullptr);
+ EndAllAction();
+ }
+}
+
+bool SwEditShell::MergeTable( bool bWithPrev )
+{
+ bool bRet = false;
+ SwPaM *pCursor = GetCursor();
+ if( pCursor->GetNode().FindTableNode() )
+ {
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+
+ bRet = GetDoc()->MergeTable( *pCursor->GetPoint(), bWithPrev );
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr);
+ ClearFEShellTabCols(*GetDoc(), nullptr);
+ EndAllAction();
+ }
+ return bRet;
+}
+
+bool SwEditShell::CanMergeTable( bool bWithPrev, bool* pChkNxtPrv ) const
+{
+ bool bRet = false;
+ const SwPaM *pCursor = GetCursor();
+ const SwTableNode* pTableNd = pCursor->GetNode().FindTableNode();
+ if( pTableNd && dynamic_cast< const SwDDETable* >(&pTableNd->GetTable()) == nullptr)
+ {
+ bool bNew = pTableNd->GetTable().IsNewModel();
+ const SwNodes& rNds = GetDoc()->GetNodes();
+ if( pChkNxtPrv )
+ {
+ const SwTableNode* pChkNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
+ if( pChkNd && dynamic_cast< const SwDDETable* >(&pChkNd->GetTable()) == nullptr &&
+ bNew == pChkNd->GetTable().IsNewModel() &&
+ // Consider table in table case
+ pChkNd->EndOfSectionIndex() == pTableNd->GetIndex() - 1 )
+ {
+ *pChkNxtPrv = true;
+ bRet = true; // using Prev is possible
+ }
+ else
+ {
+ pChkNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
+ if( pChkNd && dynamic_cast< const SwDDETable* >(&pChkNd->GetTable()) == nullptr &&
+ bNew == pChkNd->GetTable().IsNewModel() )
+ {
+ *pChkNxtPrv = false;
+ bRet = true; // using Next is possible
+ }
+ }
+ }
+ else
+ {
+ const SwTableNode* pTmpTableNd = nullptr;
+
+ if( bWithPrev )
+ {
+ pTmpTableNd = rNds[ pTableNd->GetIndex() - 1 ]->FindTableNode();
+ // Consider table in table case
+ if ( pTmpTableNd && pTmpTableNd->EndOfSectionIndex() != pTableNd->GetIndex() - 1 )
+ pTmpTableNd = nullptr;
+ }
+ else
+ pTmpTableNd = rNds[ pTableNd->EndOfSectionIndex() + 1 ]->GetTableNode();
+
+ bRet = pTmpTableNd && dynamic_cast< const SwDDETable* >(&pTmpTableNd->GetTable()) == nullptr &&
+ bNew == pTmpTableNd->GetTable().IsNewModel();
+ }
+ }
+ return bRet;
+}
+
+/** create InsertDB as table Undo */
+void SwEditShell::AppendUndoForInsertFromDB( bool bIsTable )
+{
+ GetDoc()->AppendUndoForInsertFromDB( *GetCursor(), bIsTable );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edtox.cxx b/sw/source/core/edit/edtox.cxx
new file mode 100644
index 000000000..1a9c4ba21
--- /dev/null
+++ b/sw/source/core/edit/edtox.cxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <i18nutil/searchopt.hxx>
+#include <svl/fstathelper.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <unotools/syslocale.hxx>
+
+#include <sfx2/docfile.hxx>
+
+#include <swtypes.hxx>
+#include <rootfrm.hxx>
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <tox.hxx>
+#include <doctxm.hxx>
+#include <docary.hxx>
+#include <mdiexp.hxx>
+#include <strings.hrc>
+#include <iodetect.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+
+// Add/delete listing markers to a document
+
+void SwEditShell::Insert(const SwTOXMark& rMark)
+{
+ bool bInsAtPos = rMark.IsAlternativeText();
+ StartAllAction();
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ const SwPosition *pStt = rPaM.Start(),
+ *pEnd = rPaM.End();
+ if( bInsAtPos )
+ {
+ SwPaM aTmp( *pStt );
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem( aTmp, rMark );
+ }
+ else if( *pEnd != *pStt )
+ {
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem(
+ rPaM, rMark, SetAttrMode::DONTEXPAND );
+ }
+
+ }
+ EndAllAction();
+}
+
+void SwEditShell::DeleteTOXMark( SwTOXMark const * pMark )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ mxDoc->DeleteTOXMark( pMark );
+
+ EndAllAction();
+}
+
+/// Collect all listing markers
+void SwEditShell::GetCurTOXMarks(SwTOXMarks& rMarks) const
+{
+ SwDoc::GetCurTOXMark( *GetCursor()->Start(), rMarks );
+}
+
+bool SwEditShell::IsTOXBaseReadonly(const SwTOXBase& rTOXBase)
+{
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+ const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
+ return rTOXSect.IsProtect();
+}
+
+void SwEditShell::SetTOXBaseReadonly(const SwTOXBase& rTOXBase, bool bReadonly)
+{
+ assert( dynamic_cast<const SwTOXBaseSection*>( &rTOXBase) && "no TOXBaseSection!" );
+ const SwTOXBaseSection& rTOXSect = static_cast<const SwTOXBaseSection&>(rTOXBase);
+ const_cast<SwTOXBase&>(rTOXBase).SetProtected(bReadonly);
+ OSL_ENSURE( rTOXSect.SwSection::GetType() == SectionType::ToxContent, "not a TOXContentSection" );
+
+ SwSectionData aSectionData(rTOXSect);
+ aSectionData.SetProtectFlag(bReadonly);
+ UpdateSection( GetSectionFormatPos( *rTOXSect.GetFormat() ), aSectionData );
+}
+
+const SwTOXBase* SwEditShell::GetDefaultTOXBase( TOXTypes eTyp, bool bCreate )
+{
+ return GetDoc()->GetDefaultTOXBase( eTyp, bCreate );
+}
+
+void SwEditShell::SetDefaultTOXBase(const SwTOXBase& rBase)
+{
+ GetDoc()->SetDefaultTOXBase(rBase);
+}
+
+/// Insert listing and create content
+void SwEditShell::InsertTableOf( const SwTOXBase& rTOX, const SfxItemSet* pSet )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwDocShell* pDocSh = GetDoc()->GetDocShell();
+ ::StartProgress( STR_STATSTR_TOX_INSERT, 0, 0, pDocSh );
+
+ // Insert listing
+ const SwTOXBaseSection* pTOX = mxDoc->InsertTableOf(
+ *GetCursor()->GetPoint(), rTOX, pSet, true, GetLayout() );
+ OSL_ENSURE(pTOX, "No current TOX");
+
+ // start formatting
+ CalcLayout();
+
+ // insert page numbering
+ const_cast<SwTOXBaseSection*>(pTOX)->UpdatePageNum();
+
+ pTOX->SetPosAtStartEnd( *GetCursor()->GetPoint() );
+
+ // Fix for empty listing
+ InvalidateWindows( maVisArea );
+ ::EndProgress( pDocSh );
+ EndAllAction();
+}
+
+/// update tables of content
+void SwEditShell::UpdateTableOf(const SwTOXBase& rTOX, const SfxItemSet* pSet)
+{
+ assert(dynamic_cast<const SwTOXBaseSection*>(&rTOX) && "no TOXBaseSection!");
+ SwTOXBaseSection& rTOXSect = static_cast<SwTOXBaseSection&>(const_cast<SwTOXBase&>(rTOX));
+ if (!rTOXSect.GetFormat()->GetSectionNode())
+ return;
+
+ SwDoc* pMyDoc = GetDoc();
+ SwDocShell* pDocSh = pMyDoc->GetDocShell();
+
+ bool bInIndex = &rTOX == GetCurTOX();
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ ::StartProgress( STR_STATSTR_TOX_UPDATE, 0, 0, pDocSh );
+
+ pMyDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::TOXCHANGE, nullptr);
+
+ // create listing stub
+ rTOXSect.Update(pSet, GetLayout());
+
+ // correct Cursor
+ if( bInIndex )
+ rTOXSect.SetPosAtStartEnd(*GetCursor()->GetPoint());
+
+ // start formatting
+ // tdf#139426 ...but allow suppression of AssertFlyPages
+ GetLayout()->SetTableUpdateInProgress(true);
+ CalcLayout();
+ GetLayout()->SetTableUpdateInProgress(false);
+
+ // insert page numbering
+ rTOXSect.UpdatePageNum();
+
+ pMyDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::TOXCHANGE, nullptr);
+
+ ::EndProgress( pDocSh );
+ EndAllAction();
+}
+
+/// Get current listing before or at the Cursor
+const SwTOXBase* SwEditShell::GetCurTOX() const
+{
+ return SwDoc::GetCurTOX( *GetCursor()->GetPoint() );
+}
+
+bool SwEditShell::DeleteTOX( const SwTOXBase& rTOXBase, bool bDelNodes )
+{
+ return GetDoc()->DeleteTOX( rTOXBase, bDelNodes );
+}
+
+// manage types of listings
+
+const SwTOXType* SwEditShell::GetTOXType(TOXTypes eTyp, sal_uInt16 nId) const
+{
+ return mxDoc->GetTOXType(eTyp, nId);
+}
+
+// manage keys for the alphabetical index
+
+void SwEditShell::GetTOIKeys( SwTOIKeyType eTyp, std::vector<OUString>& rArr ) const
+{
+ GetDoc()->GetTOIKeys( eTyp, rArr, *GetLayout() );
+}
+
+sal_uInt16 SwEditShell::GetTOXCount() const
+{
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ sal_uInt16 nRet = 0;
+ for( auto n = rFormats.size(); n; )
+ {
+ const SwSection* pSect = rFormats[ --n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode() )
+ ++nRet;
+ }
+ return nRet;
+}
+
+const SwTOXBase* SwEditShell::GetTOX( sal_uInt16 nPos ) const
+{
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ sal_uInt16 nCnt {0};
+ for( const SwSectionFormat *pFormat : rFormats )
+ {
+ const SwSection* pSect = pFormat->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode() &&
+ nCnt++ == nPos )
+ {
+ assert( dynamic_cast<const SwTOXBaseSection*>( pSect) && "no TOXBaseSection!" );
+ return static_cast<const SwTOXBaseSection*>(pSect);
+ }
+ }
+ return nullptr;
+}
+
+/** Update of all listings after reading-in a file */
+void SwEditShell::SetUpdateTOX( bool bFlag )
+{
+ GetDoc()->SetUpdateTOX( bFlag );
+}
+
+bool SwEditShell::IsUpdateTOX() const
+{
+ return GetDoc()->IsUpdateTOX();
+}
+
+OUString const & SwEditShell::GetTOIAutoMarkURL() const
+{
+ return GetDoc()->GetTOIAutoMarkURL();
+}
+
+void SwEditShell::SetTOIAutoMarkURL(const OUString& rSet)
+{
+ GetDoc()->SetTOIAutoMarkURL(rSet);
+}
+
+void SwEditShell::ApplyAutoMark()
+{
+ StartAllAction();
+ bool bDoesUndo = DoesUndo();
+ DoUndo(false);
+ //1. remove all automatic generated index entries if AutoMarkURL has a
+ // length and the file exists
+ //2. load file
+ //3. select all occurrences of the searched words
+ //4. apply index entries
+
+ OUString sAutoMarkURL(GetDoc()->GetTOIAutoMarkURL());
+ if( !sAutoMarkURL.isEmpty() && FStatHelper::IsDocument( sAutoMarkURL ))
+ {
+ //1.
+ const SwTOXType* pTOXType = GetTOXType(TOX_INDEX, 0);
+
+ SwTOXMarks aMarks;
+ pTOXType->CollectTextMarks(aMarks);
+ for( SwTOXMark* pMark : aMarks )
+ {
+ if(pMark->IsAutoGenerated() && pMark->GetTextTOXMark())
+ // mba: test iteration; objects are deleted in iteration
+ DeleteTOXMark(pMark);
+ }
+
+ //2.
+ SfxMedium aMedium( sAutoMarkURL, StreamMode::STD_READ );
+ SvStream& rStrm = *aMedium.GetInStream();
+ Push();
+ // tdf#106899 - import tox concordance file using the appropriate character set
+ rtl_TextEncoding eChrSet = SwIoSystem::GetTextEncoding(rStrm);
+ if (eChrSet == RTL_TEXTENCODING_DONTKNOW)
+ eChrSet = ::osl_getThreadTextEncoding();
+
+ // SearchOptions to be used in loop below
+ sal_Int32 const nLEV_Other = 2; // -> changedChars;
+ sal_Int32 const nLEV_Longer = 3; //! -> deletedChars;
+ sal_Int32 const nLEV_Shorter = 1; //! -> insertedChars;
+
+ i18nutil::SearchOptions2 aSearchOpt(
+ SearchAlgorithms_ABSOLUTE,
+ SearchFlags::LEV_RELAXED,
+ "", "",
+ SvtSysLocale().GetLanguageTag().getLocale(),
+ nLEV_Other, nLEV_Longer, nLEV_Shorter,
+ TransliterationFlags::NONE,
+ SearchAlgorithms2::ABSOLUTE,
+ '\\' );
+
+ OStringBuffer aRdLine;
+ while (rStrm.good())
+ {
+ rStrm.ReadLine( aRdLine );
+
+ // # -> comment
+ // ; -> delimiter between entries ->
+ // Format: TextToSearchFor;AlternativeString;PrimaryKey;SecondaryKey;CaseSensitive;WordOnly
+ // Leading and trailing blanks are ignored
+ if( !aRdLine.isEmpty() && '#' != aRdLine[0] )
+ {
+ OUString sLine(OStringToOUString(aRdLine, eChrSet));
+
+ sal_Int32 nTokenPos = 0;
+ OUString sToSelect( sLine.getToken(0, ';', nTokenPos ) );
+ if( !sToSelect.isEmpty() )
+ {
+ OUString sAlternative = sLine.getToken(0, ';', nTokenPos);
+ OUString sPrimary = sLine.getToken(0, ';', nTokenPos);
+ OUString sSecondary = sLine.getToken(0, ';', nTokenPos);
+ OUString sCase = sLine.getToken(0, ';', nTokenPos);
+ OUString sWordOnly = sLine.getToken(0, ';', nTokenPos);
+
+ //3.
+ bool bCaseSensitive = !sCase.isEmpty() && sCase != "0";
+ bool bWordOnly = !sWordOnly.isEmpty() && sWordOnly != "0";
+
+ if (!bCaseSensitive)
+ {
+ aSearchOpt.transliterateFlags |=
+ TransliterationFlags::IGNORE_CASE;
+ }
+ else
+ {
+ aSearchOpt.transliterateFlags &=
+ ~TransliterationFlags::IGNORE_CASE;
+ }
+ if ( bWordOnly)
+ aSearchOpt.searchFlag |= SearchFlags::NORM_WORD_ONLY;
+ else
+ aSearchOpt.searchFlag &= ~SearchFlags::NORM_WORD_ONLY;
+
+ aSearchOpt.searchString = sToSelect;
+
+ KillPams();
+ bool bCancel;
+
+ // todo/mba: assuming that notes shouldn't be searched
+ sal_uLong nRet = Find_Text(aSearchOpt, false/*bSearchInNotes*/, SwDocPositions::Start, SwDocPositions::End, bCancel,
+ FindRanges::InSelAll );
+
+ if(nRet)
+ {
+ SwTOXMark* pTmpMark = new SwTOXMark(pTOXType);
+ if( !sPrimary.isEmpty() )
+ {
+ pTmpMark->SetPrimaryKey( sPrimary );
+ if( !sSecondary.isEmpty() )
+ pTmpMark->SetSecondaryKey( sSecondary );
+ }
+ if( !sAlternative.isEmpty() )
+ pTmpMark->SetAlternativeText(sAlternative);
+ pTmpMark->SetMainEntry(false);
+ pTmpMark->SetAutoGenerated(true);
+ //4.
+ SwEditShell::Insert(*pTmpMark);
+ }
+ }
+ }
+ }
+ KillPams();
+ Pop(PopMode::DeleteCurrent);
+ }
+ DoUndo(bDoesUndo);
+ EndAllAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edundo.cxx b/sw/source/core/edit/edundo.cxx
new file mode 100644
index 000000000..2614e5005
--- /dev/null
+++ b/sw/source/core/edit/edundo.cxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <svx/svdmark.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <editsh.hxx>
+#include <fesh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <UndoCore.hxx>
+#include <swundo.hxx>
+#include <flyfrm.hxx>
+#include <frmfmt.hxx>
+#include <docsh.hxx>
+#include <pagefrm.hxx>
+#include <textboxhelper.hxx>
+#include <fmtanchr.hxx>
+
+#include <wrtsh.hxx>
+
+/** helper function to select all objects in an SdrMarkList;
+ * implementation: see below */
+static void lcl_SelectSdrMarkList( SwEditShell* pShell,
+ const SdrMarkList* pSdrMarkList );
+
+bool SwEditShell::CursorsLocked() const
+{
+ return GetDoc()->GetDocShell()->GetModel()->hasControllersLocked();
+}
+
+void SwEditShell::HandleUndoRedoContext(::sw::UndoRedoContext & rContext)
+{
+ // do nothing if somebody has locked controllers!
+ if (CursorsLocked())
+ {
+ return;
+ }
+
+ SwFrameFormat * pSelFormat(nullptr);
+ SdrMarkList * pMarkList(nullptr);
+ rContext.GetSelections(pSelFormat, pMarkList);
+
+ if (pSelFormat) // select frame
+ {
+ if (RES_DRAWFRMFMT == pSelFormat->Which())
+ {
+ SdrObject* pSObj = pSelFormat->FindSdrObject();
+
+ // Before layout calc, inline anchored textboxes have to be synced unless crash.
+ if (pSelFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && pSelFormat->GetOtherTextBoxFormats() && pSObj)
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor,
+ pSelFormat, pSObj);
+
+ static_cast<SwFEShell*>(this)->SelectObj(
+ pSObj->GetCurrentBoundRect().Center() );
+ }
+ else
+ {
+ Point aPt;
+ SwFlyFrame *const pFly =
+ static_cast<SwFlyFrameFormat*>(pSelFormat)->GetFrame(& aPt);
+ if (pFly)
+ {
+ // fdo#36681: Invalidate the content and layout to refresh
+ // the picture anchoring properly
+ SwPageFrame* pPageFrame = pFly->FindPageFrameOfAnchor();
+ pPageFrame->InvalidateFlyLayout();
+ pPageFrame->InvalidateContent();
+
+ static_cast<SwFEShell*>(this)->SelectFlyFrame(*pFly);
+ }
+ }
+ }
+ else if (pMarkList)
+ {
+ lcl_SelectSdrMarkList( this, pMarkList );
+ }
+ else if (GetCursor()->GetNext() != GetCursor())
+ {
+ // current cursor is the last one:
+ // go around the ring, to the first cursor
+ GoNextCursor();
+ }
+}
+
+void SwEditShell::Undo(sal_uInt16 const nCount, sal_uInt16 nOffset)
+{
+ MakeAllOutlineContentTemporarilyVisible a(GetDoc());
+
+ CurrShell aCurr( this );
+
+ // current undo state was not saved
+ ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
+ bool bRet = false;
+
+ StartAllAction();
+ {
+ // Actually it should be enough to just work on the current Cursor, i.e. if there is a cycle
+ // cancel the latter temporarily, so that an insert during Undo is not done in all areas.
+ KillPams();
+ SetMark(); // Bound1 and Bound2 in the same Node
+ ClearMark();
+
+ // Keep Cursor - so that we're able to set it at
+ // the same position for autoformat or autocorrection
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ GetLastUndoInfo(nullptr, & nLastUndoId);
+ const bool bRestoreCursor = nCount == 1
+ && ( SwUndoId::AUTOFORMAT == nLastUndoId
+ || SwUndoId::AUTOCORRECT == nLastUndoId
+ || SwUndoId::SETDEFTATTR == nLastUndoId );
+ Push();
+
+ // Destroy stored TableBoxPtr. A detection is only permitted for the new "Box"!
+ ClearTableBoxContent();
+
+ const RedlineFlags eOld = GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags();
+
+ try {
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ bRet = GetDoc()->GetIDocumentUndoRedo().UndoWithOffset(nOffset) || bRet;
+ }
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("sw.core", "SwEditShell::Undo()");
+ }
+
+ if (bRestoreCursor)
+ { // fdo#39003 Pop does not touch the rest of the cursor ring
+ KillPams(); // so call this first to get rid of unwanted cursors
+ }
+ Pop(bRestoreCursor ? PopMode::DeleteCurrent : PopMode::DeleteStack);
+
+ GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ GetDoc()->getIDocumentRedlineAccess().CompressRedlines();
+
+ // automatic detection of the new "Box"
+ SaveTableBoxContent();
+ }
+ EndAllAction();
+}
+
+void SwEditShell::Redo(sal_uInt16 const nCount)
+{
+ MakeAllOutlineContentTemporarilyVisible a(GetDoc());
+
+ CurrShell aCurr( this );
+
+ bool bRet = false;
+
+ // undo state was not saved
+ ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
+
+ StartAllAction();
+
+ {
+ // Actually it should be enough to just work on the current Cursor, i.e. if there is a cycle
+ // cancel the latter temporarily, so that an insert during Undo is not done in all areas.
+ KillPams();
+ SetMark(); // Bound1 and Bound2 in the same Node
+ ClearMark();
+
+ SwUndoId nFirstRedoId(SwUndoId::EMPTY);
+ GetDoc()->GetIDocumentUndoRedo().GetFirstRedoInfo(nullptr, & nFirstRedoId);
+ const bool bRestoreCursor = nCount == 1 && SwUndoId::SETDEFTATTR == nFirstRedoId;
+ Push();
+
+ // Destroy stored TableBoxPtr. A detection is only permitted for the new "Box"!
+ ClearTableBoxContent();
+
+ RedlineFlags eOld = GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags();
+
+ try {
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ bRet = GetDoc()->GetIDocumentUndoRedo().Redo()
+ || bRet;
+ }
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("sw.core", "SwEditShell::Redo()");
+ }
+
+ Pop(bRestoreCursor ? PopMode::DeleteCurrent : PopMode::DeleteStack);
+
+ GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOld );
+ GetDoc()->getIDocumentRedlineAccess().CompressRedlines();
+
+ // automatic detection of the new "Box"
+ SaveTableBoxContent();
+ }
+
+ EndAllAction();
+}
+
+void SwEditShell::Repeat(sal_uInt16 const nCount)
+{
+ CurrShell aCurr( this );
+
+ StartAllAction();
+
+ try {
+ ::sw::RepeatContext context(*GetDoc(), *GetCursor());
+ GetDoc()->GetIDocumentUndoRedo().Repeat( context, nCount );
+ } catch (const css::uno::Exception &) {
+ TOOLS_WARN_EXCEPTION("sw.core", "SwEditShell::Repeat()");
+ }
+
+ EndAllAction();
+}
+
+static void lcl_SelectSdrMarkList( SwEditShell* pShell,
+ const SdrMarkList* pSdrMarkList )
+{
+ OSL_ENSURE( pShell != nullptr, "need shell!" );
+ OSL_ENSURE( pSdrMarkList != nullptr, "need mark list" );
+
+ SwFEShell* pFEShell = dynamic_cast<SwFEShell*>( pShell );
+ if( !pFEShell )
+ return;
+
+ bool bFirst = true;
+ for( size_t i = 0; i < pSdrMarkList->GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = pSdrMarkList->GetMark( i )->GetMarkedSdrObj();
+ if( pObj )
+ {
+ pFEShell->SelectObj( Point(), bFirst ? 0 : SW_ADD_SELECT, pObj );
+ bFirst = false;
+ }
+ }
+
+ // the old implementation would always unselect
+ // objects, even if no new ones were selected. If this
+ // is a problem, we need to re-work this a little.
+ OSL_ENSURE( pSdrMarkList->GetMarkCount() != 0, "empty mark list" );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/edit/edws.cxx b/sw/source/core/edit/edws.cxx
new file mode 100644
index 000000000..647e59e11
--- /dev/null
+++ b/sw/source/core/edit/edws.cxx
@@ -0,0 +1,327 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editsh.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <osl/diagnose.h>
+#include <unotools/configmgr.hxx>
+#include <vcl/window.hxx>
+
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <pam.hxx>
+#include <acorrect.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <swundo.hxx>
+#include <SwRewriter.hxx>
+#include <frameformats.hxx>
+
+// masqueraded copy constructor
+SwEditShell::SwEditShell( SwEditShell& rEdSH, vcl::Window *pWindow )
+ : SwCursorShell( rEdSH, pWindow )
+ , m_bNbspRunNext(false) // TODO: would copying that make sense? only if editing continues
+ , m_bDoParagraphSignatureValidation(true)
+{
+}
+
+SwEditShell::SwEditShell( SwDoc& rDoc, vcl::Window *pWindow, const SwViewOption *pOptions )
+ : SwCursorShell( rDoc, pWindow, pOptions )
+ , m_bNbspRunNext(false)
+ , m_bDoParagraphSignatureValidation(true)
+{
+ if (!utl::ConfigManager::IsFuzzing() && 0 < officecfg::Office::Common::Undo::Steps::get())
+ {
+ GetDoc()->GetIDocumentUndoRedo().DoUndo(true);
+ }
+
+ // Restore the paragraph metadata fields and validate signatures.
+ RestoreMetadataFieldsAndValidateParagraphSignatures();
+}
+
+SwEditShell::~SwEditShell() // USED
+{
+}
+
+bool SwEditShell::IsModified() const
+{
+ return GetDoc()->getIDocumentState().IsModified();
+}
+
+void SwEditShell::SetModified()
+{
+ GetDoc()->getIDocumentState().SetModified();
+}
+
+void SwEditShell::ResetModified()
+{
+ GetDoc()->getIDocumentState().ResetModified();
+}
+
+void SwEditShell::SetUndoNoResetModified()
+{
+ GetDoc()->getIDocumentState().SetModified();
+ GetDoc()->GetIDocumentUndoRedo().SetUndoNoResetModified();
+}
+
+void SwEditShell::StartAllAction()
+{
+ for(SwViewShell& rCurrentShell : GetRingContainer())
+ {
+ if (SwEditShell* pEditShell = dynamic_cast<SwEditShell*>(&rCurrentShell))
+ pEditShell->StartAction();
+ else
+ rCurrentShell.StartAction();
+ }
+}
+
+void SwEditShell::EndAllAction()
+{
+ for(SwViewShell& rCurrentShell : GetRingContainer())
+ {
+ if( auto pEditShell = dynamic_cast<SwEditShell *>(&rCurrentShell))
+ pEditShell->EndAction();
+ else
+ rCurrentShell.EndAction();
+ }
+}
+
+void SwEditShell::CalcLayout()
+{
+ StartAllAction();
+ SwViewShell::CalcLayout();
+
+ for(SwViewShell& rCurrentShell : GetRingContainer())
+ {
+ if ( rCurrentShell.GetWin() )
+ rCurrentShell.GetWin()->Invalidate();
+ }
+
+ EndAllAction();
+}
+
+/** Get the content type of a shell
+ *
+ * @todo Is this called for every attribute?
+ */
+sal_uInt16 SwEditShell::GetCntType() const
+{
+ sal_uInt16 nRet = 0;
+ if( IsTableMode() )
+ nRet = CNT_TXT;
+ else
+ switch( GetCursor()->GetNode().GetNodeType() )
+ {
+ case SwNodeType::Text: nRet = CNT_TXT; break;
+ case SwNodeType::Grf: nRet = CNT_GRF; break;
+ case SwNodeType::Ole: nRet = CNT_OLE; break;
+ default: break;
+ }
+
+ OSL_ASSERT( nRet );
+ return nRet;
+}
+
+bool SwEditShell::HasOtherCnt() const
+
+{
+ if ( !GetDoc()->GetSpzFrameFormats()->empty() )
+ return true;
+
+ const SwNodes &rNds = GetDoc()->GetNodes();
+ const SwNode *pNd;
+
+ pNd = &rNds.GetEndOfInserts();
+ if ( SwNodeOffset(1) != (pNd->GetIndex() - pNd->StartOfSectionIndex()) )
+ return true;
+
+ pNd = &rNds.GetEndOfAutotext();
+ return SwNodeOffset(1) != (pNd->GetIndex() - pNd->StartOfSectionIndex());
+}
+
+SwActContext::SwActContext(SwEditShell *pShell)
+ : m_rShell(*pShell)
+{
+ m_rShell.StartAction();
+}
+
+SwActContext::~SwActContext() COVERITY_NOEXCEPT_FALSE
+{
+ m_rShell.EndAction();
+}
+
+SwMvContext::SwMvContext(SwEditShell *pShell)
+ : m_rShell(*pShell)
+{
+ m_rShell.SttCursorMove();
+}
+
+SwMvContext::~SwMvContext() COVERITY_NOEXCEPT_FALSE
+{
+ m_rShell.EndCursorMove();
+}
+
+SwFrameFormat *SwEditShell::GetTableFormat() // fastest test on a table
+{
+ const SwTableNode* pTableNd = IsCursorInTable();
+ return pTableNd ? static_cast<SwFrameFormat*>(pTableNd->GetTable().GetFrameFormat()) : nullptr;
+}
+
+// TODO: Why is this called 3x for a new document?
+sal_uInt16 SwEditShell::GetTOXTypeCount(TOXTypes eTyp) const
+{
+ return mxDoc->GetTOXTypeCount(eTyp);
+}
+
+void SwEditShell::InsertTOXType(const SwTOXType& rTyp)
+{
+ mxDoc->InsertTOXType(rTyp);
+}
+
+void SwEditShell::DoUndo( bool bOn )
+{ GetDoc()->GetIDocumentUndoRedo().DoUndo( bOn ); }
+
+bool SwEditShell::DoesUndo() const
+{ return GetDoc()->GetIDocumentUndoRedo().DoesUndo(); }
+
+void SwEditShell::DoGroupUndo( bool bOn )
+{ GetDoc()->GetIDocumentUndoRedo().DoGroupUndo( bOn ); }
+
+bool SwEditShell::DoesGroupUndo() const
+{ return GetDoc()->GetIDocumentUndoRedo().DoesGroupUndo(); }
+
+void SwEditShell::DelAllUndoObj()
+{
+ GetDoc()->GetIDocumentUndoRedo().DelAllUndoObj();
+}
+
+// Combine continuous calls of Insert/Delete/Overwrite on characters. Default: sdbcx::Group-Undo.
+
+/** open undo container
+ *
+ * @return nUndoId ID of the container
+ */
+SwUndoId SwEditShell::StartUndo( SwUndoId eUndoId,
+ const SwRewriter *pRewriter )
+{ return GetDoc()->GetIDocumentUndoRedo().StartUndo( eUndoId, pRewriter ); }
+
+/** close undo container
+ *
+ * not used by UI
+ *
+ * @param eUndoId ID of the undo container
+ * @param pRewriter ?
+*/
+SwUndoId SwEditShell::EndUndo(SwUndoId eUndoId, const SwRewriter *pRewriter)
+{ return GetDoc()->GetIDocumentUndoRedo().EndUndo(eUndoId, pRewriter); }
+
+bool SwEditShell::GetLastUndoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView) const
+{
+ return GetDoc()->GetIDocumentUndoRedo().GetLastUndoInfo(o_pStr, o_pId, pView);
+}
+
+bool SwEditShell::GetFirstRedoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView) const
+{
+ return GetDoc()->GetIDocumentUndoRedo().GetFirstRedoInfo(o_pStr, o_pId, pView);
+}
+
+SwUndoId SwEditShell::GetRepeatInfo(OUString *const o_pStr) const
+{ return GetDoc()->GetIDocumentUndoRedo().GetRepeatInfo(o_pStr); }
+
+/** Auto correction */
+void SwEditShell::AutoCorrect( SvxAutoCorrect& rACorr, bool bInsert,
+ sal_Unicode cChar )
+{
+ CurrShell aCurr( this );
+
+ StartAllAction();
+
+ SwPaM* pCursor = getShellCursor( true );
+ SwTextNode* pTNd = pCursor->GetNode().GetTextNode();
+
+ SwAutoCorrDoc aSwAutoCorrDoc( *this, *pCursor, cChar );
+ // FIXME: this _must_ be called with reference to the actual node text!
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(GetLayout())));
+ TextFrameIndex const nPos(pFrame->MapModelToViewPos(*pCursor->GetPoint()));
+ // tdf#147414 sw_redlinehide: if cursor moved backward, it may be at the
+ // start of a delete redline - but MapViewToModelPos() always returns end
+ // of redline and it will be called when AutoCorrect actually inserts
+ // something - so first normalize cursor point to end of redline so that
+ // point will then be moved forward when something is inserted.
+ *pCursor->GetPoint() = pFrame->MapViewToModelPos(nPos);
+ OUString const& rMergedText(pFrame->GetText());
+ rACorr.DoAutoCorrect( aSwAutoCorrDoc,
+ rMergedText, sal_Int32(nPos),
+ cChar, bInsert, m_bNbspRunNext, GetWin() );
+ if( cChar )
+ SaveTableBoxContent( pCursor->GetPoint() );
+ EndAllAction();
+}
+
+void SwEditShell::SetNewDoc()
+{
+ GetDoc()->getIDocumentState().SetNewDoc(true);
+}
+
+OUString SwEditShell::GetPrevAutoCorrWord(SvxAutoCorrect& rACorr)
+{
+ CurrShell aCurr( this );
+
+ OUString sRet;
+ SwPaM* pCursor = getShellCursor( true );
+ SwTextNode* pTNd = pCursor->GetNode().GetTextNode();
+ if (pTNd)
+ {
+ SwAutoCorrDoc aSwAutoCorrDoc( *this, *pCursor, 0 );
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(GetLayout())));
+ TextFrameIndex const nPos(pFrame->MapModelToViewPos(*pCursor->GetPoint()));
+ sRet = rACorr.GetPrevAutoCorrWord(aSwAutoCorrDoc, pFrame->GetText(), sal_Int32(nPos));
+ }
+ return sRet;
+}
+
+std::vector<OUString> SwEditShell::GetChunkForAutoText()
+{
+ CurrShell aCurr(this);
+
+ std::vector<OUString> aRet;
+ SwPaM* pCursor = getShellCursor(true);
+ SwTextNode* pTNd = pCursor->GetNode().GetTextNode();
+ if (pTNd)
+ {
+ const auto pFrame = static_cast<SwTextFrame const*>(pTNd->getLayoutFrame(GetLayout()));
+ TextFrameIndex const nPos(pFrame->MapModelToViewPos(*pCursor->GetPoint()));
+ aRet = SvxAutoCorrect::GetChunkForAutoText(pFrame->GetText(), sal_Int32(nPos));
+ }
+ return aRet;
+}
+
+SwAutoCompleteWord& SwEditShell::GetAutoCompleteWords()
+{
+ return SwDoc::GetAutoCompleteWords();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/authfld.cxx b/sw/source/core/fields/authfld.cxx
new file mode 100644
index 000000000..8a6212e22
--- /dev/null
+++ b/sw/source/core/fields/authfld.cxx
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+
+#include <libxml/xmlwriter.h>
+
+#include <comphelper/string.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <swtypes.hxx>
+#include <strings.hrc>
+#include <authfld.hxx>
+#include <expfld.hxx>
+#include <pam.hxx>
+#include <cntfrm.hxx>
+#include <rootfrm.hxx>
+#include <tox.hxx>
+#include <txmsrt.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <unofldmid.h>
+#include <unoprnms.hxx>
+#include <docsh.hxx>
+
+#include <com/sun/star/beans/PropertyValues.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+SwAuthEntry::SwAuthEntry(const SwAuthEntry& rCopy)
+ : SimpleReferenceObject()
+{
+ for(int i = 0; i < AUTH_FIELD_END; ++i)
+ m_aAuthFields[i] = rCopy.m_aAuthFields[i];
+}
+
+bool SwAuthEntry::operator==(const SwAuthEntry& rComp) const
+{
+ for(int i = 0; i < AUTH_FIELD_END; ++i)
+ if(m_aAuthFields[i] != rComp.m_aAuthFields[i])
+ return false;
+ return true;
+}
+
+SwAuthorityFieldType::SwAuthorityFieldType(SwDoc* pDoc)
+ : SwFieldType( SwFieldIds::TableOfAuthorities ),
+ m_pDoc(pDoc),
+ m_cPrefix('['),
+ m_cSuffix(']'),
+ m_bIsSequence(false),
+ m_bSortByDocument(true),
+ m_eLanguage(::GetAppLanguage())
+{
+}
+
+SwAuthorityFieldType::~SwAuthorityFieldType()
+{
+}
+
+std::unique_ptr<SwFieldType> SwAuthorityFieldType::Copy() const
+{
+ return std::make_unique<SwAuthorityFieldType>(m_pDoc);
+}
+
+void SwAuthorityFieldType::RemoveField(const SwAuthEntry* pEntry)
+{
+ for(SwAuthDataArr::size_type j = 0; j < m_DataArr.size(); ++j)
+ {
+ if(m_DataArr[j].get() == pEntry)
+ {
+ if (m_DataArr[j]->m_nCount <= 1)
+ {
+ m_DataArr.erase(m_DataArr.begin() + j);
+ //re-generate positions of the fields
+ DelSequenceArray();
+ }
+ return;
+ }
+ }
+ assert(false && "SwAuthorityFieldType::RemoveField: pEntry was not added previously");
+}
+
+SwAuthEntry* SwAuthorityFieldType::AddField(std::u16string_view rFieldContents)
+{
+ rtl::Reference<SwAuthEntry> pEntry(new SwAuthEntry);
+ sal_Int32 nIdx{ 0 };
+ for( sal_Int32 i = 0; i < AUTH_FIELD_END; ++i )
+ pEntry->SetAuthorField( static_cast<ToxAuthorityField>(i),
+ OUString(o3tl::getToken(rFieldContents, 0, TOX_STYLE_DELIMITER, nIdx )));
+
+ for (const auto &rpTemp : m_DataArr)
+ {
+ if (*rpTemp == *pEntry)
+ {
+ return rpTemp.get();
+ }
+ }
+
+ //if it is a new Entry - insert
+ m_DataArr.push_back(std::move(pEntry));
+ //re-generate positions of the fields
+ DelSequenceArray();
+ return m_DataArr.back().get();
+}
+
+void SwAuthorityFieldType::GetAllEntryIdentifiers(
+ std::vector<OUString>& rToFill )const
+{
+ for (const auto & rpTemp : m_DataArr)
+ {
+ rToFill.push_back(rpTemp->GetAuthorField(AUTH_FIELD_IDENTIFIER));
+ }
+}
+
+SwAuthEntry* SwAuthorityFieldType::GetEntryByIdentifier(
+ std::u16string_view rIdentifier)const
+{
+ for (const auto &rpTemp : m_DataArr)
+ {
+ if (rIdentifier == rpTemp->GetAuthorField(AUTH_FIELD_IDENTIFIER))
+ {
+ return rpTemp.get();
+ }
+ }
+ return nullptr;
+}
+
+bool SwAuthorityFieldType::ChangeEntryContent(const SwAuthEntry* pNewEntry)
+{
+ for (auto &rpTemp : m_DataArr)
+ {
+ if (rpTemp->GetAuthorField(AUTH_FIELD_IDENTIFIER) ==
+ pNewEntry->GetAuthorField(AUTH_FIELD_IDENTIFIER))
+ {
+ for(int i = 0; i < AUTH_FIELD_END; ++i)
+ {
+ rpTemp->SetAuthorField(static_cast<ToxAuthorityField>(i),
+ pNewEntry->GetAuthorField(static_cast<ToxAuthorityField>(i)));
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/// appends a new entry (if new) and returns the copied entry
+SwAuthEntry* SwAuthorityFieldType::AppendField( const SwAuthEntry& rInsert )
+{
+ for( SwAuthDataArr::size_type nRet = 0; nRet < m_DataArr.size(); ++nRet )
+ {
+ if( *m_DataArr[ nRet ] == rInsert )
+ return m_DataArr[ nRet ].get();
+ }
+
+ //if it is a new Entry - insert
+ m_DataArr.push_back(new SwAuthEntry(rInsert));
+ return m_DataArr.back().get();
+}
+
+std::unique_ptr<SwTOXInternational> SwAuthorityFieldType::CreateTOXInternational() const
+{
+ return std::make_unique<SwTOXInternational>(m_eLanguage, SwTOIOptions::NONE, m_sSortAlgorithm);
+}
+
+sal_uInt16 SwAuthorityFieldType::GetSequencePos(const SwAuthEntry* pAuthEntry,
+ SwRootFrame const*const pLayout)
+{
+ //find the field in a sorted array of handles,
+ if(!m_SequArr.empty() && m_SequArr.size() != m_DataArr.size())
+ DelSequenceArray();
+ if(m_SequArr.empty())
+ {
+ IDocumentRedlineAccess const& rIDRA(m_pDoc->getIDocumentRedlineAccess());
+ std::unique_ptr<SwTOXInternational> pIntl = CreateTOXInternational();
+ // sw_redlinehide: need 2 arrays because the sorting may be different,
+ // if multiple fields refer to the same entry and first one is deleted
+ std::vector<std::unique_ptr<SwTOXSortTabBase>> aSortArr;
+ std::vector<std::unique_ptr<SwTOXSortTabBase>> aSortArrRLHidden;
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ for(SwFormatField* pFormatField : vFields)
+ {
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ if(!pTextField || !pTextField->GetpTextNode())
+ {
+ continue;
+ }
+ const SwTextNode& rFieldTextNode = pTextField->GetTextNode();
+ SwPosition aFieldPos(rFieldTextNode);
+ SwDoc& rDoc = const_cast<SwDoc&>(rFieldTextNode.GetDoc());
+ SwContentFrame *pFrame = rFieldTextNode.getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ const SwTextNode* pTextNode = nullptr;
+ if(pFrame && !pFrame->IsInDocBody())
+ pTextNode = GetBodyTextNode( rDoc, aFieldPos, *pFrame );
+ //if no text node could be found or the field is in the document
+ //body the directly available text node will be used
+ if(!pTextNode)
+ pTextNode = &rFieldTextNode;
+ if (pTextNode->GetText().isEmpty()
+ || !pTextNode->getLayoutFrame(rDoc.getIDocumentLayoutAccess().GetCurrentLayout())
+ || !pTextNode->GetNodes().IsDocNodes())
+ {
+ continue;
+ }
+ auto const InsertImpl = [&pIntl, pTextNode, pFormatField]
+ (std::vector<std::unique_ptr<SwTOXSortTabBase>> & rSortArr)
+ {
+ std::unique_ptr<SwTOXAuthority> pNew(
+ new SwTOXAuthority(*pTextNode, *pFormatField, *pIntl));
+
+ for (size_t i = 0; i < rSortArr.size(); ++i)
+ {
+ SwTOXSortTabBase* pOld = rSortArr[i].get();
+ if (pOld->equivalent(*pNew))
+ {
+ //only the first occurrence in the document
+ //has to be in the array
+ if (pOld->sort_lt(*pNew))
+ pNew.reset();
+ else // remove the old content
+ rSortArr.erase(rSortArr.begin() + i);
+ break;
+ }
+ }
+ //if it still exists - insert at the correct position
+ if (pNew)
+ {
+ size_t j {0};
+
+ while (j < rSortArr.size())
+ {
+ SwTOXSortTabBase* pOld = rSortArr[j].get();
+ if (pNew->sort_lt(*pOld))
+ break;
+ ++j;
+ }
+ rSortArr.insert(rSortArr.begin() + j, std::move(pNew));
+ }
+ };
+ InsertImpl(aSortArr);
+ if (!sw::IsFieldDeletedInModel(rIDRA, *pTextField))
+ {
+ InsertImpl(aSortArrRLHidden);
+ }
+ }
+
+ for(auto & pBase : aSortArr)
+ {
+ SwFormatField& rFormatField = static_cast<SwTOXAuthority&>(*pBase).GetFieldFormat();
+ SwAuthorityField* pAField = static_cast<SwAuthorityField*>(rFormatField.GetField());
+ m_SequArr.push_back(pAField->GetAuthEntry());
+ }
+ for (auto & pBase : aSortArrRLHidden)
+ {
+ SwFormatField& rFormatField = static_cast<SwTOXAuthority&>(*pBase).GetFieldFormat();
+ SwAuthorityField* pAField = static_cast<SwAuthorityField*>(rFormatField.GetField());
+ m_SequArrRLHidden.push_back(pAField->GetAuthEntry());
+ }
+ }
+ //find nHandle
+ auto const& rSequArr(pLayout && pLayout->IsHideRedlines() ? m_SequArrRLHidden : m_SequArr);
+ for (std::vector<sal_IntPtr>::size_type i = 0; i < rSequArr.size(); ++i)
+ {
+ if (rSequArr[i] == pAuthEntry)
+ {
+ return i + 1;
+ }
+ }
+ return 0;
+}
+
+void SwAuthorityFieldType::QueryValue( Any& rVal, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ case FIELD_PROP_PAR2:
+ {
+ OUString sVal;
+ sal_Unicode uRet = FIELD_PROP_PAR1 == nWhichId ? m_cPrefix : m_cSuffix;
+ if(uRet)
+ sVal = OUString(uRet);
+ rVal <<= sVal;
+ }
+ break;
+ case FIELD_PROP_PAR3:
+ rVal <<= GetSortAlgorithm();
+ break;
+
+ case FIELD_PROP_BOOL1:
+ rVal <<= m_bIsSequence;
+ break;
+
+ case FIELD_PROP_BOOL2:
+ rVal <<= m_bSortByDocument;
+ break;
+
+ case FIELD_PROP_LOCALE:
+ rVal <<= LanguageTag(GetLanguage()).getLocale();
+ break;
+
+ case FIELD_PROP_PROP_SEQ:
+ {
+ Sequence<PropertyValues> aRet(m_SortKeyArr.size());
+ PropertyValues* pValues = aRet.getArray();
+ for(SortKeyArr::size_type i = 0; i < m_SortKeyArr.size(); ++i)
+ {
+ const SwTOXSortKey* pKey = &m_SortKeyArr[i];
+ pValues[i].realloc(2);
+ PropertyValue* pValue = pValues[i].getArray();
+ pValue[0].Name = UNO_NAME_SORT_KEY;
+ pValue[0].Value <<= sal_Int16(pKey->eField);
+ pValue[1].Name = UNO_NAME_IS_SORT_ASCENDING;
+ pValue[1].Value <<= pKey->bSortAscending;
+ }
+ rVal <<= aRet;
+ }
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void SwAuthorityFieldType::PutValue( const Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ case FIELD_PROP_PAR2:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ const sal_Unicode uSet = !sTmp.isEmpty() ? sTmp[0] : 0;
+ if( FIELD_PROP_PAR1 == nWhichId )
+ m_cPrefix = uSet;
+ else
+ m_cSuffix = uSet;
+ }
+ break;
+ case FIELD_PROP_PAR3:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ SetSortAlgorithm(sTmp);
+ break;
+ }
+ case FIELD_PROP_BOOL1:
+ m_bIsSequence = *o3tl::doAccess<bool>(rAny);
+ break;
+ case FIELD_PROP_BOOL2:
+ m_bSortByDocument = *o3tl::doAccess<bool>(rAny);
+ break;
+
+ case FIELD_PROP_LOCALE:
+ {
+ css::lang::Locale aLocale;
+ if( rAny >>= aLocale )
+ SetLanguage( LanguageTag::convertToLanguageType( aLocale ));
+ }
+ break;
+
+ case FIELD_PROP_PROP_SEQ:
+ {
+ Sequence<PropertyValues> aSeq;
+ if( rAny >>= aSeq )
+ {
+ m_SortKeyArr.clear();
+ const PropertyValues* pValues = aSeq.getConstArray();
+ //TODO: Limiting to the first SAL_MAX_UINT16 elements of aSeq so that size of
+ // m_SortKeyArr remains in range of sal_uInt16, as GetSortKeyCount and GetSortKey
+ // still expect m_SortKeyArr to be indexed by sal_uInt16:
+ auto nSize = std::min<sal_Int32>(aSeq.getLength(), SAL_MAX_UINT16);
+ for(sal_Int32 i = 0; i < nSize; i++)
+ {
+ SwTOXSortKey aSortKey;
+ for(const PropertyValue& rValue : pValues[i])
+ {
+ if(rValue.Name == UNO_NAME_SORT_KEY)
+ {
+ sal_Int16 nVal = -1; rValue.Value >>= nVal;
+ if(nVal >= 0 && nVal < AUTH_FIELD_END)
+ aSortKey.eField = static_cast<ToxAuthorityField>(nVal);
+ }
+ else if(rValue.Name == UNO_NAME_IS_SORT_ASCENDING)
+ {
+ aSortKey.bSortAscending = *o3tl::doAccess<bool>(rValue.Value);
+ }
+ }
+ m_SortKeyArr.push_back(aSortKey);
+ }
+ }
+ }
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void SwAuthorityFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ //re-generate positions of the fields
+ DelSequenceArray();
+ CallSwClientNotify(rHint);
+}
+
+sal_uInt16 SwAuthorityFieldType::GetSortKeyCount() const
+{
+ return m_SortKeyArr.size();
+}
+
+const SwTOXSortKey* SwAuthorityFieldType::GetSortKey(sal_uInt16 nIdx) const
+{
+ if(m_SortKeyArr.size() > nIdx)
+ return &m_SortKeyArr[nIdx];
+ OSL_FAIL("Sort key not found");
+ return nullptr;
+}
+
+void SwAuthorityFieldType::SetSortKeys(sal_uInt16 nKeyCount, SwTOXSortKey const aKeys[])
+{
+ m_SortKeyArr.clear();
+ for(sal_uInt16 i = 0; i < nKeyCount; i++)
+ if(aKeys[i].eField < AUTH_FIELD_END)
+ m_SortKeyArr.push_back(aKeys[i]);
+}
+
+SwAuthorityField::SwAuthorityField( SwAuthorityFieldType* pInitType,
+ std::u16string_view rFieldContents )
+ : SwField(pInitType)
+ , m_nTempSequencePos( -1 )
+ , m_nTempSequencePosRLHidden( -1 )
+{
+ m_xAuthEntry = pInitType->AddField( rFieldContents );
+}
+
+SwAuthorityField::SwAuthorityField( SwAuthorityFieldType* pInitType,
+ SwAuthEntry* pAuthEntry )
+ : SwField( pInitType )
+ , m_xAuthEntry( pAuthEntry )
+ , m_nTempSequencePos( -1 )
+ , m_nTempSequencePosRLHidden( -1 )
+{
+}
+
+SwAuthorityField::~SwAuthorityField()
+{
+ static_cast<SwAuthorityFieldType* >(GetTyp())->RemoveField(m_xAuthEntry.get());
+}
+
+OUString SwAuthorityField::ExpandImpl(SwRootFrame const*const pLayout) const
+{
+ return ConditionalExpandAuthIdentifier(pLayout);
+}
+
+OUString SwAuthorityField::ConditionalExpandAuthIdentifier(
+ SwRootFrame const*const pLayout) const
+{
+ SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(GetTyp());
+ OUString sRet;
+ if(pAuthType->GetPrefix())
+ sRet = OUString(pAuthType->GetPrefix());
+
+ if( pAuthType->IsSequence() )
+ {
+ sal_IntPtr & rnTempSequencePos(pLayout && pLayout->IsHideRedlines()
+ ? m_nTempSequencePosRLHidden : m_nTempSequencePos);
+ if(!pAuthType->GetDoc()->getIDocumentFieldsAccess().IsExpFieldsLocked())
+ rnTempSequencePos = pAuthType->GetSequencePos(m_xAuthEntry.get(), pLayout);
+ if (0 <= rnTempSequencePos)
+ sRet += OUString::number(rnTempSequencePos);
+ }
+ else
+ {
+ //TODO: Expand to: identifier, number sequence, ...
+ if(m_xAuthEntry)
+ {
+ OUString sIdentifier(m_xAuthEntry->GetAuthorField(AUTH_FIELD_IDENTIFIER));
+ // tdf#107784 Use title if it's a ooxml citation
+ if (o3tl::starts_with(o3tl::trim(sIdentifier), u"CITATION"))
+ return m_xAuthEntry->GetAuthorField(AUTH_FIELD_TITLE);
+ else
+ sRet += sIdentifier;
+ }
+ }
+ if(pAuthType->GetSuffix())
+ sRet += OUStringChar(pAuthType->GetSuffix());
+ return sRet;
+}
+
+OUString SwAuthorityField::ExpandCitation(ToxAuthorityField eField,
+ SwRootFrame const*const pLayout) const
+{
+ SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(GetTyp());
+ OUString sRet;
+
+ if( pAuthType->IsSequence() )
+ {
+ sal_IntPtr & rnTempSequencePos(pLayout && pLayout->IsHideRedlines()
+ ? m_nTempSequencePosRLHidden : m_nTempSequencePos);
+ if(!pAuthType->GetDoc()->getIDocumentFieldsAccess().IsExpFieldsLocked())
+ rnTempSequencePos = pAuthType->GetSequencePos(m_xAuthEntry.get(), pLayout);
+ if (0 <= rnTempSequencePos)
+ sRet += OUString::number(rnTempSequencePos);
+ }
+ else
+ {
+ //TODO: Expand to: identifier, number sequence, ...
+ if(m_xAuthEntry)
+ sRet += m_xAuthEntry->GetAuthorField(eField);
+ }
+ return sRet;
+}
+
+std::unique_ptr<SwField> SwAuthorityField::Copy() const
+{
+ SwAuthorityFieldType* pAuthType = static_cast<SwAuthorityFieldType*>(GetTyp());
+ return std::make_unique<SwAuthorityField>(pAuthType, m_xAuthEntry.get());
+}
+
+const OUString & SwAuthorityField::GetFieldText(ToxAuthorityField eField) const
+{
+ return m_xAuthEntry->GetAuthorField( eField );
+}
+
+void SwAuthorityField::SetPar1(const OUString& rStr)
+{
+ SwAuthorityFieldType* pInitType = static_cast<SwAuthorityFieldType* >(GetTyp());
+ pInitType->RemoveField(m_xAuthEntry.get());
+ m_xAuthEntry = pInitType->AddField(rStr);
+}
+
+OUString SwAuthorityField::GetDescription() const
+{
+ return SwResId(STR_AUTHORITY_ENTRY);
+}
+
+OUString SwAuthorityField::GetAuthority(const SwTextAttr* pTextAttr,
+ const SwRootFrame* pLayout) const
+{
+ OUString aText;
+
+ SwForm aForm(TOX_AUTHORITIES);
+ if (!pTextAttr)
+ {
+ return aText;
+ }
+
+ auto& rFormatField = const_cast<SwFormatField&>(pTextAttr->GetFormatField());
+ SwTextField* pTextField = rFormatField.GetTextField();
+ if (!pTextField)
+ {
+ return aText;
+ }
+
+ const SwTextNode& rNode = pTextField->GetTextNode();
+ const auto pFieldType = static_cast<const SwAuthorityFieldType*>(GetTyp());
+ std::unique_ptr<SwTOXInternational> pIntl(pFieldType->CreateTOXInternational());
+ SwTOXAuthority aAuthority(rNode, rFormatField, *pIntl);
+ sal_uInt16 nLevel = aAuthority.GetLevel();
+ SwFormTokens aPattern = aForm.GetPattern(nLevel);
+ aAuthority.InitText(pLayout);
+ for (const auto& rToken : aPattern)
+ {
+ switch (rToken.eTokenType)
+ {
+ case TOKEN_TEXT:
+ {
+ aText += rToken.sText;
+ break;
+ }
+ case TOKEN_AUTHORITY:
+ {
+ sal_uInt16 eField = rToken.nAuthorityField;
+ aText += aAuthority.GetText(eField, pLayout);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return aText;
+}
+
+bool SwAuthorityField::HasURL() const
+{
+ const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL);
+ return !rURL.isEmpty();
+}
+
+OUString SwAuthorityField::GetAbsoluteURL() const
+{
+ const OUString& rURL = GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL);
+ SwDoc* pDoc = static_cast<SwAuthorityFieldType*>(GetTyp())->GetDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ OUString aBasePath = pDocShell->getDocumentBaseURL();
+ return INetURLObject::GetAbsURL(aBasePath, rURL, INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::WithCharset);
+}
+
+void SwAuthorityField::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAuthorityField"));
+ SwField::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_xAuthEntry"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", m_xAuthEntry.get());
+ if (m_xAuthEntry.is())
+ {
+ m_xAuthEntry->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nTempSequencePos"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(m_nTempSequencePos).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nTempSequencePosRLHidden"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(m_nTempSequencePosRLHidden).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+const char* const aFieldNames[] =
+{
+ "Identifier",
+ "BibiliographicType",
+ "Address",
+ "Annote",
+ "Author",
+ "Booktitle",
+ "Chapter",
+ "Edition",
+ "Editor",
+ "Howpublished",
+ "Institution",
+ "Journal",
+ "Month",
+ "Note",
+ "Number",
+ "Organizations",
+ "Pages",
+ "Publisher",
+ "School",
+ "Series",
+ "Title",
+ "Report_Type",
+ "Volume",
+ "Year",
+ "URL",
+ "Custom1",
+ "Custom2",
+ "Custom3",
+ "Custom4",
+ "Custom5",
+ "ISBN",
+ "LocalURL"
+};
+
+void SwAuthEntry::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAuthEntry"));
+
+ for (int i = 0; i < AUTH_FIELD_END; ++i)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_aAuthField"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("key"), BAD_CAST(aFieldNames[i]));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(m_aAuthFields[i].toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SwAuthorityField::QueryValue( Any& rAny, sal_uInt16 /*nWhichId*/ ) const
+{
+ if(!GetTyp())
+ return false;
+ if(!m_xAuthEntry)
+ return false;
+ Sequence <PropertyValue> aRet(AUTH_FIELD_END);
+ PropertyValue* pValues = aRet.getArray();
+ for(int i = 0; i < AUTH_FIELD_END; ++i)
+ {
+ pValues[i].Name = OUString::createFromAscii(aFieldNames[i]);
+ const OUString& sField = m_xAuthEntry->GetAuthorField(static_cast<ToxAuthorityField>(i));
+ if(i == AUTH_FIELD_AUTHORITY_TYPE)
+ pValues[i].Value <<= sal_Int16(sField.toInt32());
+ else
+ pValues[i].Value <<= sField;
+ }
+ rAny <<= aRet;
+ /* FIXME: it is weird that we always return false here */
+ return false;
+}
+
+static sal_Int32 lcl_Find(std::u16string_view rFieldName)
+{
+ for(sal_Int32 i = 0; i < AUTH_FIELD_END; ++i)
+ if(o3tl::equalsAscii(rFieldName, aFieldNames[i]))
+ return i;
+ return -1;
+}
+
+bool SwAuthorityField::PutValue( const Any& rAny, sal_uInt16 /*nWhichId*/ )
+{
+ if(!GetTyp() || !m_xAuthEntry)
+ return false;
+
+ Sequence <PropertyValue> aParam;
+ if(!(rAny >>= aParam))
+ return false;
+
+ OUStringBuffer sBuf(+AUTH_FIELD_LOCAL_URL);
+ comphelper::string::padToLength(sBuf, AUTH_FIELD_LOCAL_URL, TOX_STYLE_DELIMITER);
+ OUString sToSet(sBuf.makeStringAndClear());
+ for(const PropertyValue& rParam : std::as_const(aParam))
+ {
+ const sal_Int32 nFound = lcl_Find(rParam.Name);
+ if(nFound >= 0)
+ {
+ OUString sContent;
+ if(AUTH_FIELD_AUTHORITY_TYPE == nFound)
+ {
+ sal_Int16 nVal = 0;
+ rParam.Value >>= nVal;
+ sContent = OUString::number(nVal);
+ }
+ else
+ rParam.Value >>= sContent;
+ sToSet = comphelper::string::setToken(sToSet, nFound, TOX_STYLE_DELIMITER, sContent);
+ }
+ }
+
+ static_cast<SwAuthorityFieldType*>(GetTyp())->RemoveField(m_xAuthEntry.get());
+ m_xAuthEntry = static_cast<SwAuthorityFieldType*>(GetTyp())->AddField(sToSet);
+
+ /* FIXME: it is weird that we always return false here */
+ return false;
+}
+
+SwFieldType* SwAuthorityField::ChgTyp( SwFieldType* pFieldTyp )
+{
+ SwAuthorityFieldType* pSrcTyp = static_cast<SwAuthorityFieldType*>(GetTyp()),
+ * pDstTyp = static_cast<SwAuthorityFieldType*>(pFieldTyp);
+ if( pSrcTyp != pDstTyp )
+ {
+ const SwAuthEntry* pSrcEntry = m_xAuthEntry.get();
+ m_xAuthEntry = pDstTyp->AppendField( *pSrcEntry );
+ pSrcTyp->RemoveField( pSrcEntry );
+ SwField::ChgTyp( pFieldTyp );
+ }
+ return pSrcTyp;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/cellfml.cxx b/sw/source/core/fields/cellfml.cxx
new file mode 100644
index 000000000..339a0337b
--- /dev/null
+++ b/sw/source/core/fields/cellfml.cxx
@@ -0,0 +1,1253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <float.h>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <frmfmt.hxx>
+#include <layfrm.hxx>
+#include <cntfrm.hxx>
+#include <tabfrm.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <cellfml.hxx>
+#include <calc.hxx>
+#include <expfld.hxx>
+#include <usrfld.hxx>
+#include <flddat.hxx>
+#include <cellatr.hxx>
+#include <ndindex.hxx>
+#include <frameformats.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+
+namespace
+{
+
+const sal_Unicode cRelSeparator = ',';
+const sal_Unicode cRelIdentifier = '\x12'; // CTRL-R
+
+enum
+{
+ cMAXSTACKSIZE = 50
+};
+
+}
+
+static const SwFrame* lcl_GetBoxFrame( const SwTableBox& rBox );
+static sal_Int32 lcl_GetLongBoxNum( OUString& rStr );
+static const SwTableBox* lcl_RelToBox( const SwTable& rTable,
+ const SwTableBox* pRefBox,
+ const OUString& sGetName);
+static OUString lcl_BoxNmToRel( const SwTable& rTable,
+ const SwTableNode& rTableNd,
+ const OUString& sRefBoxNm,
+ const OUString& sGetStr,
+ bool bExtrnlNm);
+
+/** Get value of this box.
+ *
+ * The value is comes from the first TextNode. If it starts with a number/
+ * formula then calculate it, if it starts with a field then get the value.
+ * All other conditions return 0 (and an error?).
+ */
+double SwTableBox::GetValue( SwTableCalcPara& rCalcPara ) const
+{
+ double nRet = 0;
+
+ if( rCalcPara.m_rCalc.IsCalcError() )
+ return nRet; // stop if there is already an error set
+
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::Syntax ); // default: error
+
+ // no content box?
+ if( !m_pStartNode )
+ return nRet;
+
+ if( rCalcPara.IncStackCnt() )
+ return nRet;
+
+ rCalcPara.SetLastTableBox( this );
+
+ // Does it create a recursion?
+ SwTableBox* pBox = const_cast<SwTableBox*>(this);
+ if( rCalcPara.m_pBoxStack->find( pBox ) != rCalcPara.m_pBoxStack->end() )
+ return nRet; // already on the stack: error
+
+ // re-start with this box
+ rCalcPara.SetLastTableBox( this );
+
+ rCalcPara.m_pBoxStack->insert( pBox ); // add
+ do { // Middle-Check-Loop, so that we can jump from here. Used so that the box pointer
+ // will be removed from stack at the end.
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+
+ if( const SwTableBoxFormula* pFormulaItem = GetFrameFormat()->GetItemIfSet(
+ RES_BOXATR_FORMULA, false ) )
+ {
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
+ if( !pFormulaItem->IsValid() )
+ {
+ // calculate
+ const SwTable* pTmp = rCalcPara.m_pTable;
+ rCalcPara.m_pTable = &pBox->GetSttNd()->FindTableNode()->GetTable();
+ const_cast<SwTableBoxFormula*>(pFormulaItem)->Calc( rCalcPara, nRet );
+
+ if( !rCalcPara.IsStackOverflow() )
+ {
+ SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
+ SfxItemSetFixed<RES_BOXATR_BEGIN,RES_BOXATR_END-1> aTmp( pDoc->GetAttrPool() );
+ aTmp.Put( SwTableBoxValue( nRet ) );
+ if( SfxItemState::SET != pFormat->GetItemState( RES_BOXATR_FORMAT ))
+ aTmp.Put( SwTableBoxNumFormat( 0 ));
+ pFormat->SetFormatAttr( aTmp );
+ }
+ rCalcPara.m_pTable = pTmp;
+ }
+ else
+ nRet = GetFrameFormat()->GetTableBoxValue().GetValue();
+ break;
+ }
+ else if( const SwTableBoxValue* pBoxValueItem = pBox->GetFrameFormat()->GetItemIfSet(
+ RES_BOXATR_VALUE, false ) )
+ {
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
+ nRet = pBoxValueItem->GetValue();
+ break;
+ }
+
+ SwTextNode* pTextNd = pDoc->GetNodes()[ m_pStartNode->GetIndex() + 1 ]->GetTextNode();
+ if( !pTextNd )
+ break;
+
+ sal_Int32 nSttPos = 0;
+ OUString sText = pTextNd->GetText();
+
+ // use text of the tracked changes
+ if ( sText.getLength() > 0 &&
+ sText[0] != CH_TXTATR_BREAKWORD && sText[0] != CH_TXTATR_INWORD )
+ {
+ sText = pTextNd->GetRedlineText();
+ }
+
+ while ( nSttPos < sText.getLength() && ( sText[nSttPos]==' ' || sText[nSttPos]=='\t' ) )
+ ++nSttPos;
+
+ // if there is a calculation field at position 1, get the value of it
+ const bool bOK = nSttPos<sText.getLength();
+ const sal_Unicode Char = bOK ? sText[nSttPos] : 0;
+ SwTextField * pTextField = nullptr;
+ if ( bOK && (Char==CH_TXTATR_BREAKWORD || Char==CH_TXTATR_INWORD) )
+ {
+ pTextField = static_txtattr_cast<SwTextField*>(pTextNd->GetTextAttrForCharAt(nSttPos, RES_TXTATR_FIELD));
+ }
+ if ( pTextField != nullptr )
+ {
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
+
+ const SwField* pField = pTextField->GetFormatField().GetField();
+ switch ( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::SetExp:
+ nRet = static_cast<const SwSetExpField*>(pField)->GetValue(rCalcPara.m_pLayout);
+ break;
+ case SwFieldIds::User:
+ nRet = static_cast<const SwUserField*>(pField)->GetValue();
+ break;
+ case SwFieldIds::Table:
+ {
+ SwTableField* pTableField = const_cast<SwTableField*>(static_cast<const SwTableField*>(pField));
+ if( !pTableField->IsValid() )
+ {
+ // use the right table!
+ const SwTable* pTmp = rCalcPara.m_pTable;
+ rCalcPara.m_pTable = &pTextNd->FindTableNode()->GetTable();
+ pTableField->CalcField( rCalcPara );
+ rCalcPara.m_pTable = pTmp;
+ }
+ nRet = pTableField->GetValue();
+ }
+ break;
+
+ case SwFieldIds::DateTime:
+ nRet = static_cast<const SwDateTimeField*>( pField )->GetValue();
+ break;
+
+ case SwFieldIds::JumpEdit:
+ //JP 14.09.98: Bug 56112 - placeholder never have the right content!
+ nRet = 0;
+ break;
+
+ default:
+ nRet = rCalcPara.m_rCalc.Calculate( pField->ExpandField(true, nullptr) ).GetDouble();
+ }
+ }
+ else if ( nSttPos < sText.getLength()
+ && Char == CH_TXT_ATR_INPUTFIELDSTART )
+ {
+ const SwTextInputField * pTextInputField =
+ dynamic_cast< const SwTextInputField* >(
+ pTextNd->GetTextAttrAt( nSttPos, RES_TXTATR_INPUTFIELD ) );
+ if ( pTextInputField == nullptr )
+ break;
+ nRet = rCalcPara.m_rCalc.Calculate( pTextInputField->GetFieldContent() ).GetDouble();
+ }
+ else if ( Char != CH_TXTATR_BREAKWORD )
+ {
+ // result is 0 but no error!
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::NONE ); // reset status
+
+ double aNum = 0.0;
+ sText = bOK ? sText.copy( nSttPos ) : OUString();
+ sal_uInt32 nFormatIndex = GetFrameFormat()->GetTableBoxNumFormat().GetValue();
+
+ SvNumberFormatter* pNumFormatr = pDoc->GetNumberFormatter();
+
+ const SvNumFormatType nFormatType = pNumFormatr->GetType( nFormatIndex );
+ if( nFormatType == SvNumFormatType::TEXT )
+ nFormatIndex = 0;
+ // JP 22.04.98: Bug 49659 - special treatment for percentages
+ else if( !sText.isEmpty() &&
+ SvNumFormatType::PERCENT == nFormatType)
+ {
+ sal_uInt32 nTmpFormat = 0;
+ if( pDoc->IsNumberFormat( sText, nTmpFormat, aNum ) &&
+ SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat ))
+ sText += "%";
+ }
+
+ if( pDoc->IsNumberFormat( sText, nFormatIndex, aNum ))
+ nRet = aNum;
+ else
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::NaN ); // set for interoperability functions
+ }
+ // ?? otherwise it is an error
+ } while( false );
+
+ if( !rCalcPara.IsStackOverflow() )
+ {
+ rCalcPara.m_pBoxStack->erase( pBox ); // remove from stack
+ rCalcPara.DecStackCnt();
+ }
+
+ //JP 12.01.99: error detection, Bug 60794
+ if( DBL_MAX == nRet )
+ rCalcPara.m_rCalc.SetCalcError( SwCalcError::Syntax ); // set error
+
+ return nRet;
+}
+
+// structure needed for calculation of tables
+
+SwTableCalcPara::SwTableCalcPara(SwCalc& rCalculator, const SwTable& rTable,
+ SwRootFrame const*const pLayout)
+ : m_pLastTableBox(nullptr)
+ , m_nStackCount( 0 )
+ , m_nMaxSize( cMAXSTACKSIZE )
+ , m_pLayout(pLayout)
+ , m_pBoxStack( new SwTableSortBoxes )
+ , m_rCalc( rCalculator )
+ , m_pTable( &rTable )
+{
+}
+
+SwTableCalcPara::~SwTableCalcPara()
+{
+}
+
+bool SwTableCalcPara::CalcWithStackOverflow()
+{
+ // If a stack overflow was detected, redo with last box.
+ sal_uInt16 nSaveMaxSize = m_nMaxSize;
+
+ m_nMaxSize = cMAXSTACKSIZE - 5;
+ sal_uInt16 nCnt = 0;
+ SwTableBoxes aStackOverflows;
+ do {
+ SwTableBox* pBox = const_cast<SwTableBox*>(m_pLastTableBox);
+ m_nStackCount = 0;
+ m_rCalc.SetCalcError( SwCalcError::NONE );
+ aStackOverflows.insert( aStackOverflows.begin() + nCnt++, pBox );
+
+ m_pBoxStack->erase( pBox );
+ pBox->GetValue( *this );
+ } while( IsStackOverflow() );
+
+ m_nMaxSize = cMAXSTACKSIZE - 3; // decrease at least one level
+
+ // if recursion was detected
+ m_nStackCount = 0;
+ m_rCalc.SetCalcError( SwCalcError::NONE );
+ m_pBoxStack->clear();
+
+ while( !m_rCalc.IsCalcError() && nCnt )
+ {
+ aStackOverflows[ --nCnt ]->GetValue( *this );
+ if( IsStackOverflow() && !CalcWithStackOverflow() )
+ break;
+ }
+
+ m_nMaxSize = nSaveMaxSize;
+ aStackOverflows.clear();
+ return !m_rCalc.IsCalcError();
+}
+
+SwTableFormula::SwTableFormula( const OUString& rFormula )
+: m_sFormula( rFormula )
+, m_eNmType( EXTRNL_NAME )
+, m_bValidValue( false )
+{
+}
+
+SwTableFormula::~SwTableFormula()
+{
+}
+
+void SwTableFormula::MakeFormula_( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ SwTableCalcPara* pCalcPara = static_cast<SwTableCalcPara*>(pPara);
+ if( pCalcPara->m_rCalc.IsCalcError() ) // stop if there is already an error set
+ return;
+
+ SwTableBox *pEndBox = nullptr;
+
+ rFirstBox = rFirstBox.copy(1); // erase label of this box
+ // a region in this area?
+ if( pLastBox )
+ {
+ pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
+
+ // Is it actually a valid pointer?
+ if( rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() )
+ pEndBox = nullptr;
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+ SwTableBox* pSttBox = reinterpret_cast<SwTableBox*>(
+ sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64()));
+ // Is it actually a valid pointer?
+ if( rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() )
+ pSttBox = nullptr;
+
+ rNewStr.append(" ");
+ if( pEndBox && pSttBox ) // area?
+ {
+ // get all selected boxes via layout and calculate their values
+ SwSelBoxes aBoxes;
+ GetBoxes( *pSttBox, *pEndBox, aBoxes );
+
+ // don't use empty cells or cells with text content as zeroes in interoperability functions
+ sal_Int16 nUseOnlyNumber = -1;
+
+ rNewStr.append("(");
+ bool bDelim = false;
+ for (size_t n = 0; n < aBoxes.size() &&
+ !pCalcPara->m_rCalc.IsCalcError(); ++n)
+ {
+ const SwTableBox* pTableBox = aBoxes[n];
+ if ( pTableBox->getRowSpan() >= 1 )
+ {
+ double fVal = pTableBox->GetValue( *pCalcPara );
+
+ if ( pCalcPara->m_rCalc.IsCalcNotANumber() )
+ {
+ if ( nUseOnlyNumber == -1 )
+ {
+ OUString sFormula = rNewStr.toString().toAsciiUpperCase();
+ nUseOnlyNumber = sal_Int16(
+ sFormula.lastIndexOf("AVERAGE") > -1 ||
+ sFormula.lastIndexOf("COUNT") > -1 ||
+ sFormula.lastIndexOf("PRODUCT") > -1 );
+ }
+ if ( nUseOnlyNumber > 0 )
+ continue;
+ }
+
+ if( bDelim )
+ rNewStr.append(cListDelim);
+ bDelim = true;
+ rNewStr.append(pCalcPara->m_rCalc.GetStrResult( fVal ));
+ }
+ }
+ rNewStr.append(")");
+ }
+ else if( pSttBox && !pLastBox ) // only the StartBox?
+ {
+ // JP 12.01.99: and no EndBox in the formula!
+ // calculate the value of the box
+ if ( pSttBox->getRowSpan() >= 1 )
+ {
+ rNewStr.append("(");
+ double fVal = pSttBox->GetValue( *pCalcPara );
+ // don't use empty cell or a cell with text content as zero in interoperability functions
+ // (except PRODUCT, where the result is correct anyway)
+ if ( !pCalcPara->m_rCalc.IsCalcNotANumber() ||
+ ( rNewStr.toString().toAsciiUpperCase().lastIndexOf("AVERAGE") == -1 &&
+ rNewStr.toString().toAsciiUpperCase().lastIndexOf("COUNT") == -1 ) )
+ {
+ rNewStr.append(pCalcPara->m_rCalc.GetStrResult( fVal ));
+ }
+ rNewStr.append(")");
+ }
+ }
+ else
+ pCalcPara->m_rCalc.SetCalcError( SwCalcError::Syntax ); // set error
+ rNewStr.append(" ");
+}
+
+void SwTableFormula::RelNmsToBoxNms( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ // relative name w.r.t. box name (external presentation)
+ SwNode* pNd = static_cast<SwNode*>(pPara);
+ OSL_ENSURE( pNd, "Field isn't in any TextNode" );
+ const SwTableBox *pBox = rTable.GetTableBox(
+ pNd->FindTableBoxStartNode()->GetIndex() );
+
+ rNewStr.append(rFirstBox[0]); // get label for the box
+ rFirstBox = rFirstBox.copy(1);
+ if( pLastBox )
+ {
+ const SwTableBox *pRelLastBox = lcl_RelToBox( rTable, pBox, *pLastBox );
+ if ( pRelLastBox )
+ rNewStr.append(pRelLastBox->GetName());
+ else
+ rNewStr.append("A1");
+ rNewStr.append(":");
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+
+ const SwTableBox *pRelFirstBox = lcl_RelToBox( rTable, pBox, rFirstBox );
+
+ if (pRelFirstBox)
+ rNewStr.append(pRelFirstBox->GetName());
+ else
+ rNewStr.append("A1");
+
+ // get label for the box
+ rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
+}
+
+void SwTableFormula::RelBoxNmsToPtr( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ // relative name w.r.t. box name (internal presentation)
+ SwNode* pNd = static_cast<SwNode*>(pPara);
+ OSL_ENSURE( pNd, "Field not placed in any Node" );
+ const SwTableBox *pBox = rTable.GetTableBox(
+ pNd->FindTableBoxStartNode()->GetIndex() );
+
+ rNewStr.append(rFirstBox[0]); // get label for the box
+ rFirstBox = rFirstBox.copy(1);
+ if( pLastBox )
+ {
+ const SwTableBox *pRelLastBox = lcl_RelToBox( rTable, pBox, *pLastBox );
+ if ( pRelLastBox )
+ rNewStr.append(reinterpret_cast<sal_PtrDiff>(pRelLastBox));
+ else
+ rNewStr.append("0");
+ rNewStr.append(":");
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+
+ const SwTableBox *pRelFirstBox = lcl_RelToBox( rTable, pBox, rFirstBox );
+ if ( pRelFirstBox )
+ rNewStr.append(reinterpret_cast<sal_PtrDiff>(pRelFirstBox));
+ else
+ rNewStr.append("0");
+
+ // get label for the box
+ rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
+}
+
+void SwTableFormula::BoxNmsToRelNm( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ // box name (external presentation) w.r.t. relative name
+ SwNode* pNd = static_cast<SwNode*>(pPara);
+ OSL_ENSURE( pNd, "Field not placed in any Node" );
+ const SwTableNode* pTableNd = pNd->FindTableNode();
+
+ OUString sRefBoxNm;
+ if( &pTableNd->GetTable() == &rTable )
+ {
+ const SwTableBox *pBox = rTable.GetTableBox(
+ pNd->FindTableBoxStartNode()->GetIndex() );
+ OSL_ENSURE( pBox, "Field not placed in any Table" );
+ sRefBoxNm = pBox->GetName();
+ }
+
+ rNewStr.append(rFirstBox[0]); // get label for the box
+ rFirstBox = rFirstBox.copy(1);
+ if( pLastBox )
+ {
+ rNewStr.append(lcl_BoxNmToRel( rTable, *pTableNd, sRefBoxNm, *pLastBox,
+ m_eNmType == EXTRNL_NAME ));
+ rNewStr.append(":");
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+
+ rNewStr.append(lcl_BoxNmToRel( rTable, *pTableNd, sRefBoxNm, rFirstBox,
+ m_eNmType == EXTRNL_NAME ));
+
+ // get label for the box
+ rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
+}
+
+void SwTableFormula::PtrToBoxNms( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* ) const
+{
+ // area in these parentheses?
+ SwTableBox* pBox;
+
+ rNewStr.append(rFirstBox[0]); // get label for the box
+ rFirstBox = rFirstBox.copy(1);
+ if( pLastBox )
+ {
+ pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
+
+ // Is it actually a valid pointer?
+ if( rTable.GetTabSortBoxes().find( pBox ) != rTable.GetTabSortBoxes().end() )
+ rNewStr.append(pBox->GetName());
+ else
+ rNewStr.append("?");
+ rNewStr.append(":");
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+
+ pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64()));
+ // Is it actually a valid pointer?
+ if( rTable.GetTabSortBoxes().find( pBox ) != rTable.GetTabSortBoxes().end() )
+ rNewStr.append(pBox->GetName());
+ else
+ rNewStr.append("?");
+
+ // get label for the box
+ rNewStr.append(rFirstBox[ rFirstBox.getLength()-1 ]);
+}
+
+void SwTableFormula::BoxNmsToPtr( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* ) const
+{
+ // area in these parentheses?
+ const SwTableBox* pBox;
+
+ rNewStr.append(rFirstBox[0]); // get label for the box
+ rFirstBox = rFirstBox.copy(1);
+ if( pLastBox )
+ {
+ pBox = rTable.GetTableBox( *pLastBox );
+ rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pBox)) +
+ ":");
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+
+ pBox = rTable.GetTableBox( rFirstBox );
+ rNewStr.append(reinterpret_cast<sal_PtrDiff>(pBox))
+ .append(rFirstBox[ rFirstBox.getLength()-1 ]); // get label for the box
+}
+
+/// create external formula (for UI)
+void SwTableFormula::PtrToBoxNm( const SwTable* pTable )
+{
+ const SwNode* pNd = nullptr;
+ FnScanFormula fnFormula = nullptr;
+ switch (m_eNmType)
+ {
+ case INTRNL_NAME:
+ if( pTable )
+ fnFormula = &SwTableFormula::PtrToBoxNms;
+ break;
+ case REL_NAME:
+ if( pTable )
+ {
+ fnFormula = &SwTableFormula::RelNmsToBoxNms;
+ pNd = GetNodeOfFormula();
+ }
+ break;
+ case EXTRNL_NAME:
+ return;
+ }
+ m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) );
+ m_eNmType = EXTRNL_NAME;
+}
+
+/// create internal formula (in CORE)
+void SwTableFormula::BoxNmToPtr( const SwTable* pTable )
+{
+ const SwNode* pNd = nullptr;
+ FnScanFormula fnFormula = nullptr;
+ switch (m_eNmType)
+ {
+ case EXTRNL_NAME:
+ if( pTable )
+ fnFormula = &SwTableFormula::BoxNmsToPtr;
+ break;
+ case REL_NAME:
+ if( pTable )
+ {
+ fnFormula = &SwTableFormula::RelBoxNmsToPtr;
+ pNd = GetNodeOfFormula();
+ }
+ break;
+ case INTRNL_NAME:
+ return;
+ }
+ m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) );
+ m_eNmType = INTRNL_NAME;
+}
+
+/// create relative formula (for copy)
+void SwTableFormula::ToRelBoxNm( const SwTable* pTable )
+{
+ const SwNode* pNd = nullptr;
+ FnScanFormula fnFormula = nullptr;
+ switch (m_eNmType)
+ {
+ case INTRNL_NAME:
+ case EXTRNL_NAME:
+ if( pTable )
+ {
+ fnFormula = &SwTableFormula::BoxNmsToRelNm;
+ pNd = GetNodeOfFormula();
+ }
+ break;
+ case REL_NAME:
+ return;
+ }
+ m_sFormula = ScanString( fnFormula, *pTable, const_cast<void*>(static_cast<void const *>(pNd)) );
+ m_eNmType = REL_NAME;
+}
+
+OUString SwTableFormula::ScanString( FnScanFormula fnFormula, const SwTable& rTable,
+ void* pPara ) const
+{
+ OUStringBuffer aStr;
+ sal_Int32 nFormula = 0;
+ sal_Int32 nEnd = 0;
+
+ do {
+ // If the formula is preceded by a name, use this table!
+ const SwTable* pTable = &rTable;
+
+ sal_Int32 nStt = m_sFormula.indexOf( '<', nFormula );
+ if ( nStt>=0 )
+ {
+ while ( nStt>=0 )
+ {
+ const sal_Int32 nNxt = nStt+1;
+ if (nNxt>=m_sFormula.getLength())
+ {
+ nStt = -1;
+ break;
+ }
+ if ( m_sFormula[nNxt]!=' ' && m_sFormula[nNxt]!='=' )
+ break;
+ nStt = m_sFormula.indexOf( '<', nNxt );
+ }
+
+ if ( nStt>=0 )
+ // Start searching from current position, which is valid for sure
+ nEnd = m_sFormula.indexOf( '>', nStt );
+ }
+ if (nStt<0 || nEnd<0 )
+ {
+ // set the rest and finish
+ aStr.append(m_sFormula.subView(nFormula));
+ break;
+ }
+
+ // write beginning
+ aStr.append(m_sFormula.subView(nFormula, nStt - nFormula));
+
+ if (fnFormula)
+ {
+ sal_Int32 nSeparator = 0;
+ // Is a table name preceded?
+ // JP 16.02.99: SplitMergeBoxNm take care of the name themself
+ // JP 22.02.99: Linux compiler needs cast
+ // JP 28.06.99: rel. BoxName has no preceding tablename!
+ if( fnFormula != &SwTableFormula::SplitMergeBoxNm_ &&
+ m_sFormula.getLength()>(nStt+1) && cRelIdentifier != m_sFormula[nStt+1] &&
+ (nSeparator = m_sFormula.indexOf( '.', nStt ))>=0
+ && nSeparator < nEnd )
+ {
+ OUString sTableNm( m_sFormula.copy( nStt, nEnd - nStt ));
+
+ // If there are dots in the name, then they appear in pairs (e.g. A1.1.1)!
+ if( (comphelper::string::getTokenCount(sTableNm, '.') - 1) & 1 )
+ {
+ sTableNm = sTableNm.copy( 0, nSeparator - nStt );
+
+ // when creating a formula the table name is unwanted
+ if( fnFormula != &SwTableFormula::MakeFormula_ )
+ aStr.append(sTableNm);
+ nStt = nSeparator;
+
+ sTableNm = sTableNm.copy( 1 ); // delete separator
+ if( sTableNm != rTable.GetFrameFormat()->GetName() )
+ {
+ // then search for table
+ const SwTable* pFnd = FindTable(
+ *rTable.GetFrameFormat()->GetDoc(),
+ sTableNm );
+ if( pFnd )
+ pTable = pFnd;
+ // ??
+ OSL_ENSURE( pFnd, "No table found. What now?" );
+ }
+ }
+ }
+
+ OUString sBox( m_sFormula.copy( nStt, nEnd - nStt + 1 ));
+ // area in these parentheses?
+ nSeparator = m_sFormula.indexOf( ':', nStt );
+ if ( nSeparator>=0 && nSeparator<nEnd )
+ {
+ // without opening parenthesis
+ OUString aFirstBox( m_sFormula.copy( nStt+1, nSeparator - nStt - 1 ));
+ (this->*fnFormula)( *pTable, aStr, sBox, &aFirstBox, pPara );
+ }
+ else
+ (this->*fnFormula)( *pTable, aStr, sBox, nullptr, pPara );
+ }
+
+ nFormula = nEnd+1;
+ } while( true );
+ return aStr.makeStringAndClear();
+}
+
+const SwTable* SwTableFormula::FindTable( SwDoc& rDoc, std::u16string_view rNm )
+{
+ const SwFrameFormats& rTableFormats = *rDoc.GetTableFrameFormats();
+ const SwTable* pTmpTable = nullptr, *pRet = nullptr;
+ for( auto nFormatCnt = rTableFormats.size(); nFormatCnt; )
+ {
+ SwFrameFormat* pFormat = rTableFormats[ --nFormatCnt ];
+ // if we are called from Sw3Writer, a number is dependent on the format name
+ SwTableBox* pFBox;
+ if ( rNm == o3tl::getToken(pFormat->GetName(), 0, 0x0a) &&
+ nullptr != ( pTmpTable = SwTable::FindTable( pFormat ) ) &&
+ nullptr != (pFBox = pTmpTable->GetTabSortBoxes()[0] ) &&
+ pFBox->GetSttNd() &&
+ pFBox->GetSttNd()->GetNodes().IsDocNodes() )
+ {
+ // a table in the normal NodesArr
+ pRet = pTmpTable;
+ break;
+ }
+ }
+ return pRet;
+}
+
+static const SwFrame* lcl_GetBoxFrame( const SwTableBox& rBox )
+{
+ SwNodeIndex aIdx( *rBox.GetSttNd() );
+ SwContentNode* pCNd = aIdx.GetNodes().GoNext( &aIdx );
+ OSL_ENSURE( pCNd, "Box has no TextNode" );
+ Point aPt; // get the first frame of the layout - table headline
+ std::pair<Point, bool> const tmp(aPt, false);
+ return pCNd->getLayoutFrame(pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp);
+}
+
+static sal_Int32 lcl_GetLongBoxNum( OUString& rStr )
+{
+ sal_Int32 nRet;
+ const sal_Int32 nPos = rStr.indexOf( cRelSeparator );
+ if ( nPos<0 )
+ {
+ nRet = rStr.toInt32();
+ rStr.clear();
+ }
+ else
+ {
+ nRet = o3tl::toInt32(rStr.subView( 0, nPos ));
+ rStr = rStr.copy( nPos+1 );
+ }
+ return nRet;
+}
+
+static const SwTableBox* lcl_RelToBox( const SwTable& rTable,
+ const SwTableBox* pRefBox,
+ const OUString& _sGetName )
+{
+ // get line
+ const SwTableBox* pBox = nullptr;
+ OUString sGetName = _sGetName;
+
+ // Is it really a relative value?
+ if ( cRelIdentifier == sGetName[0] ) // yes
+ {
+ if( !pRefBox )
+ return nullptr;
+
+ sGetName = sGetName.copy( 1 );
+
+ const SwTableLines* pLines = &rTable.GetTabLines();
+ const SwTableBoxes* pBoxes;
+ const SwTableLine* pLine;
+
+ // determine starting values of the box,...
+ pBox = pRefBox;
+ pLine = pBox->GetUpper();
+ while( pLine->GetUpper() )
+ {
+ pBox = pLine->GetUpper();
+ pLine = pBox->GetUpper();
+ }
+ sal_uInt16 nSttBox = pLine->GetBoxPos( pBox );
+ sal_uInt16 nSttLine = rTable.GetTabLines().GetPos( pLine );
+
+ const sal_Int32 nBoxOffset = lcl_GetLongBoxNum( sGetName ) + nSttBox;
+ const sal_Int32 nLineOffset = lcl_GetLongBoxNum( sGetName ) + nSttLine;
+
+ if( nBoxOffset < 0 ||
+ nLineOffset < 0 )
+ return nullptr;
+
+ if( o3tl::make_unsigned(nLineOffset) >= pLines->size() )
+ return nullptr;
+
+ pLine = (*pLines)[ nLineOffset ];
+
+ // ... then search the box
+ pBoxes = &pLine->GetTabBoxes();
+ if( o3tl::make_unsigned(nBoxOffset) >= pBoxes->size() )
+ return nullptr;
+ pBox = (*pBoxes)[ nBoxOffset ];
+
+ while (!sGetName.isEmpty())
+ {
+ nSttBox = SwTable::GetBoxNum( sGetName );
+ pLines = &pBox->GetTabLines();
+ if( nSttBox )
+ --nSttBox;
+
+ nSttLine = SwTable::GetBoxNum( sGetName );
+
+ // determine line
+ if( !nSttLine || nSttLine > pLines->size() )
+ break;
+ pLine = (*pLines)[ nSttLine-1 ];
+
+ // determine box
+ pBoxes = &pLine->GetTabBoxes();
+ if( nSttBox >= pBoxes->size() )
+ break;
+ pBox = (*pBoxes)[ nSttBox ];
+ }
+
+ if( pBox )
+ {
+ if( !pBox->GetSttNd() )
+ // "bubble up" to first box
+ while( !pBox->GetTabLines().empty() )
+ pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
+ }
+ }
+ else
+ {
+ // otherwise it is an absolute external presentation
+ pBox = rTable.GetTableBox( sGetName );
+ }
+ return pBox;
+}
+
+static OUString lcl_BoxNmToRel( const SwTable& rTable, const SwTableNode& rTableNd,
+ const OUString& _sRefBoxNm, const OUString& _sTmp, bool bExtrnlNm )
+{
+ OUString sTmp = _sTmp;
+ OUString sRefBoxNm = _sRefBoxNm;
+ if( !bExtrnlNm )
+ {
+ // convert into external presentation
+ SwTableBox* pBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(sTmp.toInt64()));
+ if( rTable.GetTabSortBoxes().find( pBox ) == rTable.GetTabSortBoxes().end() )
+ return OUString('?');
+ sTmp = pBox->GetName();
+ }
+
+ // If the formula is spanning over a table then keep external presentation
+ if( &rTable == &rTableNd.GetTable() )
+ {
+ tools::Long nBox = SwTable::GetBoxNum( sTmp, true );
+ nBox -= SwTable::GetBoxNum( sRefBoxNm, true );
+ tools::Long nLine = SwTable::GetBoxNum( sTmp );
+ nLine -= SwTable::GetBoxNum( sRefBoxNm );
+
+ const OUString sCpy = sTmp; //JP 01.11.95: add rest from box name
+
+ sTmp = OUStringChar(cRelIdentifier) + OUString::number( nBox )
+ + OUStringChar(cRelSeparator) + OUString::number( nLine );
+
+ if (!sCpy.isEmpty())
+ {
+ sTmp += OUStringChar(cRelSeparator) + sCpy;
+ }
+ }
+
+ if (sTmp.endsWith(">"))
+ return sTmp.copy(0, sTmp.getLength()-1 );
+
+ return sTmp;
+}
+
+void SwTableFormula::GetBoxesOfFormula( const SwTable& rTable,
+ SwSelBoxes& rBoxes )
+{
+ rBoxes.clear();
+
+ BoxNmToPtr( &rTable );
+ ScanString( &SwTableFormula::GetFormulaBoxes, rTable, &rBoxes );
+}
+
+void SwTableFormula::GetFormulaBoxes( const SwTable& rTable, OUStringBuffer& ,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ SwSelBoxes* pBoxes = static_cast<SwSelBoxes*>(pPara);
+ SwTableBox* pEndBox = nullptr;
+
+ rFirstBox = rFirstBox.copy(1); // delete box label
+ // area in these parentheses?
+ if( pLastBox )
+ {
+ pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
+
+ // Is it actually a valid pointer?
+ if( rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() )
+ pEndBox = nullptr;
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+ }
+
+ SwTableBox *pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64()));
+ // Is it actually a valid pointer?
+ if( !pSttBox || rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() )
+ return;
+
+ if ( pEndBox ) // area?
+ {
+ // get all selected boxes via layout and calculate their values
+ SwSelBoxes aBoxes;
+ GetBoxes( *pSttBox, *pEndBox, aBoxes );
+ pBoxes->insert( aBoxes );
+ }
+ else // only the StartBox?
+ pBoxes->insert( pSttBox );
+}
+
+void SwTableFormula::GetBoxes( const SwTableBox& rSttBox,
+ const SwTableBox& rEndBox,
+ SwSelBoxes& rBoxes )
+{
+ // get all selected boxes via layout
+ const SwLayoutFrame *pStt, *pEnd;
+ const SwFrame* pFrame = lcl_GetBoxFrame( rSttBox );
+ pStt = pFrame ? pFrame->GetUpper() : nullptr;
+ pFrame = lcl_GetBoxFrame( rEndBox );
+ pEnd = pFrame ? pFrame->GetUpper() : nullptr;
+ if( !pStt || !pEnd )
+ return ; // no valid selection
+
+ GetTableSel( pStt, pEnd, rBoxes, nullptr );
+
+ const SwTable* pTable = pStt->FindTabFrame()->GetTable();
+
+ // filter headline boxes
+ if( pTable->GetRowsToRepeat() <= 0 )
+ return;
+
+ do { // middle-check loop
+ const SwTableLine* pLine = rSttBox.GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ if( pTable->IsHeadline( *pLine ) )
+ break; // headline in this area!
+
+ // maybe start and end are swapped
+ pLine = rEndBox.GetUpper();
+ while ( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ if( pTable->IsHeadline( *pLine ) )
+ break; // headline in this area!
+
+ const SwTabFrame *pStartTable = pStt->FindTabFrame();
+ const SwTabFrame *pEndTable = pEnd->FindTabFrame();
+
+ if (pStartTable == pEndTable) // no split table
+ break;
+
+ // then remove table headers
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ pLine = rBoxes[n]->GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ if( pTable->IsHeadline( *pLine ) )
+ rBoxes.erase( rBoxes.begin() + n-- );
+ }
+ } while( false );
+}
+
+/// Are all boxes valid that are referenced by the formula?
+void SwTableFormula::HasValidBoxes_( const SwTable& rTable, OUStringBuffer& ,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ bool* pBValid = static_cast<bool*>(pPara);
+ if( !(*pBValid) ) // wrong is wrong
+ return;
+
+ SwTableBox* pSttBox = nullptr, *pEndBox = nullptr;
+ rFirstBox = rFirstBox.copy(1); // delete identifier of box
+
+ // area in this parenthesis?
+ if( pLastBox )
+ rFirstBox = rFirstBox.copy( pLastBox->getLength()+1 );
+
+ switch (m_eNmType)
+ {
+ case INTRNL_NAME:
+ if( pLastBox )
+ pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
+ pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64()));
+ break;
+
+ case REL_NAME:
+ {
+ const SwNode* pNd = GetNodeOfFormula();
+ const SwTableBox* pBox = !pNd ? nullptr
+ : const_cast<SwTableBox *>(rTable.GetTableBox(
+ pNd->FindTableBoxStartNode()->GetIndex() ));
+ if( pLastBox )
+ pEndBox = const_cast<SwTableBox*>(lcl_RelToBox( rTable, pBox, *pLastBox ));
+ pSttBox = const_cast<SwTableBox*>(lcl_RelToBox( rTable, pBox, rFirstBox ));
+ }
+ break;
+
+ case EXTRNL_NAME:
+ if( pLastBox )
+ pEndBox = const_cast<SwTableBox*>(rTable.GetTableBox( *pLastBox ));
+ pSttBox = const_cast<SwTableBox*>(rTable.GetTableBox( rFirstBox ));
+ break;
+ }
+
+ // Are these valid pointers?
+ if( ( pLastBox &&
+ ( !pEndBox || rTable.GetTabSortBoxes().find( pEndBox ) == rTable.GetTabSortBoxes().end() ) ) ||
+ ( !pSttBox || rTable.GetTabSortBoxes().find( pSttBox ) == rTable.GetTabSortBoxes().end() ) )
+ *pBValid = false;
+}
+
+bool SwTableFormula::HasValidBoxes() const
+{
+ bool bRet = true;
+ const SwNode* pNd = GetNodeOfFormula();
+ if( pNd && nullptr != ( pNd = pNd->FindTableNode() ) )
+ ScanString( &SwTableFormula::HasValidBoxes_,
+ static_cast<const SwTableNode*>(pNd)->GetTable(), &bRet );
+ return bRet;
+}
+
+sal_uInt16 SwTableFormula::GetLnPosInTable( const SwTable& rTable, const SwTableBox* pBox )
+{
+ sal_uInt16 nRet = USHRT_MAX;
+ if( pBox )
+ {
+ const SwTableLine* pLn = pBox->GetUpper();
+ while( pLn->GetUpper() )
+ pLn = pLn->GetUpper()->GetUpper();
+ nRet = rTable.GetTabLines().GetPos( pLn );
+ }
+ return nRet;
+}
+
+void SwTableFormula::SplitMergeBoxNm_( const SwTable& rTable, OUStringBuffer& rNewStr,
+ OUString& rFirstBox, OUString* pLastBox, void* pPara ) const
+{
+ SwTableFormulaUpdate& rTableUpd = *static_cast<SwTableFormulaUpdate*>(pPara);
+
+ rNewStr.append(rFirstBox[0]); // get label for the box
+ rFirstBox = rFirstBox.copy(1);
+
+ OUString sTableNm;
+ const SwTable* pTable = &rTable;
+
+ OUString* pTableNmBox = pLastBox ? pLastBox : &rFirstBox;
+
+ const sal_Int32 nLastBoxLen = pTableNmBox->getLength();
+ const sal_Int32 nSeparator = pTableNmBox->indexOf('.');
+ if ( nSeparator>=0 &&
+ // If there are dots in the name, then these appear in pairs (e.g. A1.1.1)!
+ (comphelper::string::getTokenCount(*pTableNmBox, '.') - 1) & 1 )
+ {
+ sTableNm = pTableNmBox->copy( 0, nSeparator );
+ *pTableNmBox = pTableNmBox->copy( nSeparator + 1); // remove dot
+ const SwTable* pFnd = FindTable( *rTable.GetFrameFormat()->GetDoc(), sTableNm );
+ if( pFnd )
+ pTable = pFnd;
+
+ if( TBL_MERGETBL == rTableUpd.m_eFlags )
+ {
+ if( pFnd )
+ {
+ if( pFnd == rTableUpd.m_aData.pDelTable )
+ {
+ if( rTableUpd.m_pTable != &rTable ) // not the current one
+ rNewStr.append(rTableUpd.m_pTable->GetFrameFormat()->GetName() + "."); // set new table name
+ rTableUpd.m_bModified = true;
+ }
+ else if( pFnd != rTableUpd.m_pTable ||
+ ( rTableUpd.m_pTable != &rTable && &rTable != rTableUpd.m_aData.pDelTable))
+ rNewStr.append(sTableNm + "."); // keep table name
+ else
+ rTableUpd.m_bModified = true;
+ }
+ else
+ rNewStr.append(sTableNm + "."); // keep table name
+ }
+ }
+ if( pTableNmBox == pLastBox )
+ rFirstBox = rFirstBox.copy( nLastBoxLen + 1 );
+
+ SwTableBox* pSttBox = nullptr, *pEndBox = nullptr;
+ switch (m_eNmType)
+ {
+ case INTRNL_NAME:
+ if( pLastBox )
+ pEndBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(pLastBox->toInt64()));
+ pSttBox = reinterpret_cast<SwTableBox*>(sal::static_int_cast<sal_IntPtr>(rFirstBox.toInt64()));
+ break;
+
+ case REL_NAME:
+ {
+ const SwNode* pNd = GetNodeOfFormula();
+ const SwTableBox* pBox = pNd ? pTable->GetTableBox(
+ pNd->FindTableBoxStartNode()->GetIndex() ) : nullptr;
+ if( pLastBox )
+ pEndBox = const_cast<SwTableBox*>(lcl_RelToBox( *pTable, pBox, *pLastBox ));
+ pSttBox = const_cast<SwTableBox*>(lcl_RelToBox( *pTable, pBox, rFirstBox ));
+ }
+ break;
+
+ case EXTRNL_NAME:
+ if( pLastBox )
+ pEndBox = const_cast<SwTableBox*>(pTable->GetTableBox( *pLastBox ));
+ pSttBox = const_cast<SwTableBox*>(pTable->GetTableBox( rFirstBox ));
+ break;
+ }
+
+ if( pLastBox && pTable->GetTabSortBoxes().find( pEndBox ) == pTable->GetTabSortBoxes().end() )
+ pEndBox = nullptr;
+ if( pTable->GetTabSortBoxes().find( pSttBox ) == pTable->GetTabSortBoxes().end() )
+ pSttBox = nullptr;
+
+ if( TBL_SPLITTBL == rTableUpd.m_eFlags )
+ {
+ // Where are the boxes - in the old or in the new table?
+ bool bInNewTable = false;
+ if( pLastBox )
+ {
+ // It is the "first" box in this selection. It determines if the formula is placed in
+ // the new or the old table.
+ sal_uInt16 nEndLnPos = SwTableFormula::GetLnPosInTable( *pTable, pEndBox ),
+ nSttLnPos = SwTableFormula::GetLnPosInTable( *pTable, pSttBox );
+
+ if( USHRT_MAX != nSttLnPos && USHRT_MAX != nEndLnPos &&
+ ((rTableUpd.m_nSplitLine <= nSttLnPos) ==
+ (rTableUpd.m_nSplitLine <= nEndLnPos)) )
+ {
+ // stay in same table
+ bInNewTable = rTableUpd.m_nSplitLine <= nEndLnPos &&
+ pTable == rTableUpd.m_pTable;
+ }
+ else
+ {
+ // this is definitely an invalid formula, also mark as modified for Undo
+ rTableUpd.m_bModified = true;
+ if( pEndBox )
+ bInNewTable = USHRT_MAX != nEndLnPos &&
+ rTableUpd.m_nSplitLine <= nEndLnPos &&
+ pTable == rTableUpd.m_pTable;
+ }
+ }
+ else
+ {
+ sal_uInt16 nSttLnPos = SwTableFormula::GetLnPosInTable( *pTable, pSttBox );
+ // Put it in the new table?
+ bInNewTable = USHRT_MAX != nSttLnPos &&
+ rTableUpd.m_nSplitLine <= nSttLnPos &&
+ pTable == rTableUpd.m_pTable;
+ }
+
+ // formula goes into new table
+ if( rTableUpd.m_bBehindSplitLine )
+ {
+ if( !bInNewTable )
+ {
+ rTableUpd.m_bModified = true;
+ rNewStr.append(rTableUpd.m_pTable->GetFrameFormat()->GetName() + ".");
+ }
+ else if( !sTableNm.isEmpty() )
+ rNewStr.append(sTableNm + ".");
+ }
+ else if( bInNewTable )
+ {
+ rTableUpd.m_bModified = true;
+ rNewStr.append(*rTableUpd.m_aData.pNewTableNm + ".");
+ }
+ else if( !sTableNm.isEmpty() )
+ rNewStr.append(sTableNm + ".");
+ }
+
+ if( pLastBox )
+ rNewStr.append(OUString::number(reinterpret_cast<sal_PtrDiff>(pEndBox)) + ":");
+
+ rNewStr.append(reinterpret_cast<sal_PtrDiff>(pSttBox))
+ .append(rFirstBox[ rFirstBox.getLength()-1] );
+}
+
+/// Create external formula but remember that the formula is placed in a split/merged table
+void SwTableFormula::ToSplitMergeBoxNm( SwTableFormulaUpdate& rTableUpd )
+{
+ const SwTable* pTable;
+ const SwNode* pNd = GetNodeOfFormula();
+ if( pNd && nullptr != ( pNd = pNd->FindTableNode() ))
+ pTable = &static_cast<const SwTableNode*>(pNd)->GetTable();
+ else
+ pTable = rTableUpd.m_pTable;
+
+ m_sFormula = ScanString( &SwTableFormula::SplitMergeBoxNm_, *pTable, static_cast<void*>(&rTableUpd) );
+ m_eNmType = INTRNL_NAME;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/chpfld.cxx b/sw/source/core/fields/chpfld.cxx
new file mode 100644
index 000000000..fe591d11a
--- /dev/null
+++ b/sw/source/core/fields/chpfld.cxx
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/text/ChapterFormat.hpp>
+#include <osl/diagnose.h>
+#include <doc.hxx>
+#include <frame.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <chpfld.hxx>
+#include <expfld.hxx>
+#include <unofldmid.h>
+#include <numrule.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+
+OUString removeControlChars(std::u16string_view sIn)
+{
+ OUStringBuffer aBuf(sIn);
+ aBuf = aBuf.replace('\n', ' ').replace('\t', ' ');
+ sal_Int32 nLen = aBuf.getLength();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if (aBuf[i] < ' ')
+ {
+ sal_Int32 j = i+1;
+ while (j<nLen && aBuf[j]<' ') ++j;
+ aBuf.remove(i, j-i);
+ nLen = aBuf.getLength();
+ }
+ }
+ return aBuf.makeStringAndClear();
+}
+
+}
+
+SwChapterFieldType::SwChapterFieldType()
+ : SwFieldType( SwFieldIds::Chapter )
+{
+}
+
+std::unique_ptr<SwFieldType> SwChapterFieldType::Copy() const
+{
+ return std::make_unique<SwChapterFieldType>();
+}
+
+// chapter field
+
+SwChapterField::SwChapterField(SwChapterFieldType* pTyp, sal_uInt32 nFormat)
+ : SwField(pTyp, nFormat)
+{
+}
+
+sal_uInt8 SwChapterField::GetLevel(SwRootFrame const*const pLayout) const
+{
+ State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State);
+ return rState.nLevel;
+}
+
+// this is called from UI or from import filters, so override both states
+void SwChapterField::SetLevel(sal_uInt8 nLev)
+{
+ m_State.nLevel = nLev;
+ m_StateRLHidden.nLevel = nLev;
+}
+
+const OUString& SwChapterField::GetNumber(SwRootFrame const*const pLayout) const
+{
+ State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State);
+ return rState.sNumber;
+}
+
+const OUString& SwChapterField::GetTitle(SwRootFrame const*const pLayout) const
+{
+ State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State);
+ return rState.sTitle;
+}
+
+OUString SwChapterField::ExpandImpl(SwRootFrame const*const pLayout) const
+{
+ State const& rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State);
+ switch( GetFormat() )
+ {
+ case CF_TITLE:
+ return rState.sTitle;
+ case CF_NUMBER:
+ return rState.sPre + rState.sNumber + rState.sPost;
+ case CF_NUM_TITLE:
+ return rState.sPre + rState.sNumber + rState.sPost + rState.sLabelFollowedBy + rState.sTitle;
+ case CF_NUM_NOPREPST_TITLE:
+ return rState.sNumber + rState.sLabelFollowedBy + rState.sTitle;
+ }
+ // CF_NUMBER_NOPREPST
+ return rState.sNumber;
+}
+
+std::unique_ptr<SwField> SwChapterField::Copy() const
+{
+ std::unique_ptr<SwChapterField> pTmp(
+ new SwChapterField(static_cast<SwChapterFieldType*>(GetTyp()), GetFormat()));
+ pTmp->m_State = m_State;
+ pTmp->m_StateRLHidden = m_StateRLHidden;
+
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+// #i53420#
+void SwChapterField::ChangeExpansion(const SwFrame & rFrame,
+ const SwContentNode* pContentNode,
+ bool bSrchNum )
+{
+ SwDoc& rDoc = const_cast<SwDoc&>(pContentNode->GetDoc());
+
+ const SwTextNode* pTextNode = dynamic_cast<const SwTextNode*>(pContentNode);
+ if (!pTextNode || !rFrame.IsInDocBody())
+ {
+ SwPosition aDummyPos( rDoc.GetNodes().GetEndOfContent() );
+ pTextNode = GetBodyTextNode( rDoc, aDummyPos, rFrame );
+ }
+
+ if ( pTextNode )
+ {
+ ChangeExpansion( *pTextNode, bSrchNum, rFrame.getRootFrame() );
+ }
+}
+
+void SwChapterField::ChangeExpansion(const SwTextNode &rTextNd, bool bSrchNum,
+ SwRootFrame const*const pLayout)
+{
+ State & rState(pLayout && pLayout->IsHideRedlines() ? m_StateRLHidden : m_State);
+ rState.sNumber.clear();
+ rState.sLabelFollowedBy.clear();
+ rState.sTitle.clear();
+ rState.sPost.clear();
+ rState.sPre.clear();
+
+ SwDoc& rDoc = const_cast<SwDoc&>(rTextNd.GetDoc());
+ const SwTextNode *pTextNd = rTextNd.FindOutlineNodeOfLevel(rState.nLevel, pLayout);
+ if( !pTextNd )
+ return;
+
+ if( bSrchNum )
+ {
+ const SwTextNode* pONd = pTextNd;
+ do {
+ if( pONd && pONd->GetTextColl() )
+ {
+ sal_uInt8 nPrevLvl = rState.nLevel;
+
+ OSL_ENSURE( pONd->GetAttrOutlineLevel() >= 0 && pONd->GetAttrOutlineLevel() <= MAXLEVEL,
+ "<SwChapterField::ChangeExpansion(..)> - outline node with inconsistent outline level. Serious defect." );
+ rState.nLevel = static_cast<sal_uInt8>(pONd->GetAttrOutlineLevel());
+
+ if (nPrevLvl < rState.nLevel)
+ rState.nLevel = nPrevLvl;
+ else if( SVX_NUM_NUMBER_NONE != rDoc.GetOutlineNumRule()
+ ->Get( rState.nLevel ).GetNumberingType() )
+ {
+ pTextNd = pONd;
+ break;
+ }
+
+ if (!rState.nLevel--)
+ break;
+ pONd = pTextNd->FindOutlineNodeOfLevel(rState.nLevel, pLayout);
+ }
+ else
+ break;
+ } while( true );
+ }
+
+ // get the number without Pre-/Post-fixstrings
+
+ if ( pTextNd->IsOutline() )
+ {
+ // correction of refactoring done by cws swnumtree:
+ // retrieve numbering string without prefix and suffix strings
+ // as stated in the above german comment.
+ rState.sNumber = pTextNd->GetNumString(false, MAXLEVEL, pLayout);
+
+ SwNumRule* pRule( pTextNd->GetNumRule() );
+ if ( pTextNd->IsCountedInList() && pRule )
+ {
+ int nListLevel = pTextNd->GetActualListLevel();
+ if (nListLevel < 0)
+ nListLevel = 0;
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ const SwNumFormat& rNFormat = pRule->Get(nListLevel);
+ rState.sPost = rNFormat.GetSuffix();
+ rState.sPre = rNFormat.GetPrefix();
+ rState.sLabelFollowedBy = removeControlChars(rNFormat.GetLabelFollowedByAsString());
+ }
+ }
+ else
+ {
+ rState.sNumber = "??";
+ }
+
+ rState.sTitle = removeControlChars(sw::GetExpandTextMerged(pLayout,
+ *pTextNd, false, false, ExpandMode(0)));
+}
+
+bool SwChapterField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BYTE1:
+ rAny <<= static_cast<sal_Int8>(m_State.nLevel);
+ break;
+
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nRet;
+ switch( GetFormat() )
+ {
+ case CF_NUMBER: nRet = text::ChapterFormat::NUMBER; break;
+ case CF_TITLE: nRet = text::ChapterFormat::NAME; break;
+ case CF_NUMBER_NOPREPST:
+ nRet = text::ChapterFormat::DIGIT;
+ break;
+ case CF_NUM_NOPREPST_TITLE:
+ nRet = text::ChapterFormat::NO_PREFIX_SUFFIX;
+ break;
+ case CF_NUM_TITLE:
+ default: nRet = text::ChapterFormat::NAME_NUMBER;
+ }
+ rAny <<= nRet;
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwChapterField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BYTE1:
+ {
+ sal_Int8 nTmp = 0;
+ rAny >>= nTmp;
+ if(nTmp >= 0 && nTmp < MAXLEVEL)
+ {
+ m_State.nLevel = nTmp;
+ m_StateRLHidden.nLevel = nTmp;
+ }
+ else
+ bRet = false;
+ break;
+ }
+
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nVal = 0;
+ rAny >>= nVal;
+ switch( nVal )
+ {
+ case text::ChapterFormat::NAME: SetFormat(CF_TITLE); break;
+ case text::ChapterFormat::NUMBER: SetFormat(CF_NUMBER); break;
+ case text::ChapterFormat::NO_PREFIX_SUFFIX:
+ SetFormat(CF_NUM_NOPREPST_TITLE);
+ break;
+ case text::ChapterFormat::DIGIT:
+ SetFormat(CF_NUMBER_NOPREPST);
+ break;
+
+ default: SetFormat(CF_NUM_TITLE);
+ }
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/dbfld.cxx b/sw/source/core/fields/dbfld.cxx
new file mode 100644
index 000000000..7263997fe
--- /dev/null
+++ b/sw/source/core/fields/dbfld.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <float.h>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <svl/numformat.hxx>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <calc.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docary.hxx>
+#include <fldbas.hxx>
+#include <dbfld.hxx>
+#include <dbmgr.hxx>
+#include <unofldmid.h>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star;
+
+/// replace database separator by dots for display
+static OUString lcl_DBSeparatorConvert(const OUString& aContent)
+{
+ return aContent.replaceAll(OUStringChar(DB_DELIM), ".");
+}
+
+// database field type
+
+SwDBFieldType::SwDBFieldType(SwDoc* pDocPtr, const OUString& rNam, const SwDBData& rDBData ) :
+ SwValueFieldType( pDocPtr, SwFieldIds::Database ),
+ m_aDBData(rDBData),
+ m_sName(rNam),
+ m_sColumn(rNam),
+ m_nRefCnt(0)
+{
+ if(!m_aDBData.sDataSource.isEmpty() || !m_aDBData.sCommand.isEmpty())
+ {
+ m_sName = m_aDBData.sDataSource
+ + OUStringChar(DB_DELIM)
+ + m_aDBData.sCommand
+ + OUStringChar(DB_DELIM)
+ + m_sName;
+ }
+}
+
+SwDBFieldType::~SwDBFieldType()
+{
+}
+
+std::unique_ptr<SwFieldType> SwDBFieldType::Copy() const
+{
+ return std::make_unique<SwDBFieldType>(GetDoc(), m_sColumn, m_aDBData);
+}
+
+OUString SwDBFieldType::GetName() const
+{
+ return m_sName;
+}
+
+void SwDBFieldType::ReleaseRef()
+{
+ OSL_ENSURE(m_nRefCnt > 0, "RefCount < 0!");
+
+ if (--m_nRefCnt > 0)
+ return;
+
+ size_t nPos = 0;
+ for (auto const & pFieldType : *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if (pFieldType.get() == this)
+ break;
+ ++nPos;
+ }
+ if (nPos < GetDoc()->getIDocumentFieldsAccess().GetFieldTypes()->size())
+ {
+ GetDoc()->getIDocumentFieldsAccess().RemoveFieldType(nPos);
+ delete this;
+ }
+}
+
+void SwDBFieldType::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aDBData.sDataSource;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_aDBData.sCommand;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= m_sColumn;
+ break;
+ case FIELD_PROP_SHORT1:
+ rAny <<= m_aDBData.nCommandType;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void SwDBFieldType::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= m_aDBData.sDataSource;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_aDBData.sCommand;
+ break;
+ case FIELD_PROP_PAR3:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ if( sTmp != m_sColumn )
+ {
+ m_sColumn = sTmp;
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ SwDBField* pDBField = static_cast<SwDBField*>(pFormatField->GetField());
+ pDBField->ClearInitialized();
+ pDBField->InitContent();
+ }
+ }
+ }
+ break;
+ case FIELD_PROP_SHORT1:
+ rAny >>= m_aDBData.nCommandType;
+ break;
+ default:
+ assert(false);
+ }
+}
+
+// database field
+
+SwDBField::SwDBField(SwDBFieldType* pTyp, sal_uInt32 nFormat)
+ : SwValueField(pTyp, nFormat),
+ m_nSubType(0),
+ m_bIsInBodyText(true),
+ m_bValidValue(false),
+ m_bInitialized(false)
+{
+ if (GetTyp())
+ static_cast<SwDBFieldType*>(GetTyp())->AddRef();
+ InitContent();
+}
+
+SwDBField::~SwDBField()
+{
+ if (GetTyp())
+ static_cast<SwDBFieldType*>(GetTyp())->ReleaseRef();
+}
+
+void SwDBField::InitContent()
+{
+ if (!IsInitialized())
+ {
+ m_aContent = "<" + static_cast<const SwDBFieldType*>(GetTyp())->GetColumnName() + ">";
+ }
+}
+
+void SwDBField::InitContent(const OUString& rExpansion)
+{
+ if (rExpansion.startsWith("<") && rExpansion.endsWith(">"))
+ {
+ const OUString sColumn( rExpansion.copy( 1, rExpansion.getLength() - 2 ) );
+ if( ::GetAppCmpStrIgnore().isEqual( sColumn,
+ static_cast<SwDBFieldType *>(GetTyp())->GetColumnName() ))
+ {
+ InitContent();
+ return;
+ }
+ }
+ SetExpansion( rExpansion );
+}
+
+OUString SwDBField::ExpandImpl(SwRootFrame const*const) const
+{
+ if(0 ==(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE))
+ return lcl_DBSeparatorConvert(m_aContent);
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwDBField::Copy() const
+{
+ std::unique_ptr<SwDBField> pTmp(new SwDBField(static_cast<SwDBFieldType*>(GetTyp()), GetFormat()));
+ pTmp->m_aContent = m_aContent;
+ pTmp->m_bIsInBodyText = m_bIsInBodyText;
+ pTmp->m_bValidValue = m_bValidValue;
+ pTmp->m_bInitialized = m_bInitialized;
+ pTmp->m_nSubType = m_nSubType;
+ pTmp->SetValue(GetValue());
+ pTmp->m_sFieldCode = m_sFieldCode;
+
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+OUString SwDBField::GetFieldName() const
+{
+ const OUString rDBName = static_cast<SwDBFieldType*>(GetTyp())->GetName();
+
+ OUString sContent( rDBName.getToken(0, DB_DELIM) );
+
+ if (sContent.getLength() > 1)
+ {
+ sContent += OUStringChar(DB_DELIM)
+ + o3tl::getToken(rDBName, 1, DB_DELIM)
+ + OUStringChar(DB_DELIM)
+ + o3tl::getToken(rDBName, 2, DB_DELIM);
+ }
+ return lcl_DBSeparatorConvert(sContent);
+}
+
+void SwDBField::ChgValue( double d, bool bVal )
+{
+ m_bValidValue = bVal;
+ SetValue(d);
+
+ if( m_bValidValue )
+ m_aContent = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(d, GetFormat(), GetLanguage());
+}
+
+SwFieldType* SwDBField::ChgTyp( SwFieldType* pNewType )
+{
+ SwFieldType* pOld = SwValueField::ChgTyp( pNewType );
+
+ static_cast<SwDBFieldType*>(pNewType)->AddRef();
+ static_cast<SwDBFieldType*>(pOld)->ReleaseRef();
+
+ return pOld;
+}
+
+bool SwDBField::FormatValue( SvNumberFormatter const * pDocFormatter, OUString const &aString, sal_uInt32 nFormat,
+ double &aNumber, sal_Int32 nColumnType, SwDBField *pField )
+{
+ bool bValidValue = false;
+
+ if( DBL_MAX != aNumber )
+ {
+ if( DataType::DATE == nColumnType || DataType::TIME == nColumnType ||
+ DataType::TIMESTAMP == nColumnType )
+ {
+ Date aStandard( 1, 1, 1900 );
+ if( pDocFormatter->GetNullDate() != aStandard )
+ aNumber += (aStandard - pDocFormatter->GetNullDate());
+ }
+ bValidValue = true;
+ if( pField )
+ pField->SetValue( aNumber );
+ }
+ else
+ {
+ SwSbxValue aVal;
+ aVal.PutString( aString );
+
+ if (aVal.IsNumeric())
+ {
+ if( pField )
+ pField->SetValue(aVal.GetDouble());
+ else
+ aNumber = aVal.GetDouble();
+
+ if (nFormat && nFormat != SAL_MAX_UINT32 && !pDocFormatter->IsTextFormat(nFormat))
+ bValidValue = true; // because of bug #60339 not for all strings
+ }
+ else
+ {
+ // if string length > 0 then true, else false
+ if( pField )
+ pField->SetValue(aString.isEmpty() ? 0 : 1);
+ else
+ aNumber = aString.isEmpty() ? 0 : 1;
+ }
+ }
+
+ return bValidValue;
+}
+
+/// get current field value and cache it
+void SwDBField::Evaluate()
+{
+ SwDBManager* pMgr = GetDoc()->GetDBManager();
+
+ // first delete
+ m_bValidValue = false;
+ double nValue = DBL_MAX;
+ const SwDBData& aTmpData = GetDBData();
+
+ if(!pMgr || !pMgr->IsDataSourceOpen(aTmpData.sDataSource, aTmpData.sCommand, true))
+ return ;
+
+ sal_uInt32 nFormat = 0;
+
+ // search corresponding column name
+ OUString aColNm( static_cast<SwDBFieldType*>(GetTyp())->GetColumnName() );
+
+ SvNumberFormatter* pDocFormatter = GetDoc()->GetNumberFormatter();
+ pMgr->GetMergeColumnCnt(aColNm, GetLanguage(), m_aContent, &nValue);
+ if( !( m_nSubType & nsSwExtendedSubType::SUB_OWN_FMT ) )
+ {
+ nFormat = pMgr->GetColumnFormat( aTmpData.sDataSource, aTmpData.sCommand,
+ aColNm, pDocFormatter, GetLanguage() );
+ SetFormat( nFormat );
+ }
+
+ sal_Int32 nColumnType = nValue == DBL_MAX
+ ? 0
+ : pMgr->GetColumnType(aTmpData.sDataSource, aTmpData.sCommand, aColNm);
+
+ m_bValidValue = FormatValue( pDocFormatter, m_aContent, nFormat, nValue, nColumnType, this );
+
+ if( DBL_MAX != nValue )
+ m_aContent = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(nValue, GetFormat(), GetLanguage());
+
+ m_bInitialized = true;
+}
+
+/// get name
+OUString SwDBField::GetPar1() const
+{
+ return static_cast<const SwDBFieldType*>(GetTyp())->GetName();
+}
+
+sal_uInt16 SwDBField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwDBField::SetSubType(sal_uInt16 nType)
+{
+ m_nSubType = nType;
+}
+
+bool SwDBField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ rAny <<= 0 == (GetSubType()&nsSwExtendedSubType::SUB_OWN_FMT);
+ break;
+ case FIELD_PROP_BOOL2:
+ rAny <<= 0 == (GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE);
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int32>(GetFormat());
+ break;
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aContent;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_sFieldCode;
+ break;
+ default:
+ OSL_FAIL("illegal property");
+ }
+ return true;
+}
+
+bool SwDBField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ if( *o3tl::doAccess<bool>(rAny) )
+ SetSubType(GetSubType()&~nsSwExtendedSubType::SUB_OWN_FMT);
+ else
+ SetSubType(GetSubType()|nsSwExtendedSubType::SUB_OWN_FMT);
+ break;
+ case FIELD_PROP_BOOL2:
+ {
+ sal_uInt16 nSubTyp = GetSubType();
+ bool bVisible = false;
+ if(!(rAny >>= bVisible))
+ return false;
+ if(bVisible)
+ nSubTyp &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ nSubTyp |= nsSwExtendedSubType::SUB_INVISIBLE;
+ SetSubType(nSubTyp);
+ //invalidate text node
+ auto pType = GetTyp();
+ if(!pType)
+ break;
+ std::vector<SwFormatField*> vFields;
+ pType->GatherFields(vFields, false);
+ for(auto pFormatField: vFields)
+ {
+ SwTextField* pTextField = pFormatField->GetTextField();
+ if(pTextField && static_cast<SwDBField*>(pFormatField->GetField()) == this)
+ {
+ //notify the change
+ pTextField->NotifyContentChange(*pFormatField);
+ break;
+ }
+ }
+ }
+ break;
+ case FIELD_PROP_FORMAT:
+ {
+ sal_Int32 nTemp = 0;
+ rAny >>= nTemp;
+ SetFormat(nTemp);
+ }
+ break;
+ case FIELD_PROP_PAR1:
+ rAny >>= m_aContent;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_sFieldCode;
+ break;
+ default:
+ OSL_FAIL("illegal property");
+ }
+ return true;
+}
+
+// base class for all further database fields
+
+SwDBNameInfField::SwDBNameInfField(SwFieldType* pTyp, const SwDBData& rDBData, sal_uInt32 nFormat) :
+ SwField(pTyp, nFormat),
+ m_aDBData(rDBData),
+ m_nSubType(0)
+{
+}
+
+SwDBData SwDBNameInfField::GetDBData(SwDoc* pDoc)
+{
+ SwDBData aRet;
+ if(!m_aDBData.sDataSource.isEmpty())
+ aRet = m_aDBData;
+ else
+ aRet = pDoc->GetDBData();
+ return aRet;
+}
+
+void SwDBNameInfField::SetDBData(const SwDBData & rDBData)
+{
+ m_aDBData = rDBData;
+}
+
+OUString SwDBNameInfField::GetFieldName() const
+{
+ OUString sStr( SwField::GetFieldName() );
+ if (!m_aDBData.sDataSource.isEmpty())
+ {
+ sStr += ":"
+ + m_aDBData.sDataSource
+ + OUStringChar(DB_DELIM)
+ + m_aDBData.sCommand;
+ }
+ return lcl_DBSeparatorConvert(sStr);
+}
+
+bool SwDBNameInfField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aDBData.sDataSource;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_aDBData.sCommand;
+ break;
+ case FIELD_PROP_SHORT1:
+ rAny <<= m_aDBData.nCommandType;
+ break;
+ case FIELD_PROP_BOOL2:
+ rAny <<= 0 == (GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE);
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwDBNameInfField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= m_aDBData.sDataSource;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_aDBData.sCommand;
+ break;
+ case FIELD_PROP_SHORT1:
+ rAny >>= m_aDBData.nCommandType;
+ break;
+ case FIELD_PROP_BOOL2:
+ {
+ sal_uInt16 nSubTyp = GetSubType();
+ bool bVisible = false;
+ if(!(rAny >>= bVisible))
+ return false;
+ if(bVisible)
+ nSubTyp &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ nSubTyp |= nsSwExtendedSubType::SUB_INVISIBLE;
+ SetSubType(nSubTyp);
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+sal_uInt16 SwDBNameInfField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwDBNameInfField::SetSubType(sal_uInt16 nType)
+{
+ m_nSubType = nType;
+}
+
+// next dataset
+
+SwDBNextSetFieldType::SwDBNextSetFieldType()
+ : SwFieldType( SwFieldIds::DbNextSet )
+{
+}
+
+std::unique_ptr<SwFieldType> SwDBNextSetFieldType::Copy() const
+{
+ return std::make_unique<SwDBNextSetFieldType>();
+}
+
+// SwDBSetField
+
+SwDBNextSetField::SwDBNextSetField(SwDBNextSetFieldType* pTyp,
+ const OUString& rCond,
+ const SwDBData& rDBData) :
+ SwDBNameInfField(pTyp, rDBData), m_aCond(rCond), m_bCondValid(true)
+{}
+
+OUString SwDBNextSetField::ExpandImpl(SwRootFrame const*const) const
+{
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwDBNextSetField::Copy() const
+{
+ std::unique_ptr<SwDBNextSetField> pTmp(new SwDBNextSetField(static_cast<SwDBNextSetFieldType*>(GetTyp()),
+ m_aCond, GetDBData()));
+ pTmp->SetSubType(GetSubType());
+ pTmp->m_bCondValid = m_bCondValid;
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+void SwDBNextSetField::Evaluate(const SwDoc& rDoc)
+{
+ SwDBManager* pMgr = rDoc.GetDBManager();
+ const SwDBData& rData = GetDBData();
+ if( !m_bCondValid ||
+ !pMgr || !pMgr->IsDataSourceOpen(rData.sDataSource, rData.sCommand, false))
+ return ;
+ pMgr->ToNextRecord(rData.sDataSource, rData.sCommand);
+}
+
+/// get condition
+OUString SwDBNextSetField::GetPar1() const
+{
+ return m_aCond;
+}
+
+/// set condition
+void SwDBNextSetField::SetPar1(const OUString& rStr)
+{
+ m_aCond = rStr;
+}
+
+bool SwDBNextSetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR3:
+ rAny <<= m_aCond;
+ break;
+ default:
+ bRet = SwDBNameInfField::QueryValue( rAny, nWhichId );
+ }
+ return bRet;
+}
+
+bool SwDBNextSetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR3:
+ rAny >>= m_aCond;
+ break;
+ default:
+ bRet = SwDBNameInfField::PutValue( rAny, nWhichId );
+ }
+ return bRet;
+}
+
+// dataset with certain ID
+
+SwDBNumSetFieldType::SwDBNumSetFieldType() :
+ SwFieldType( SwFieldIds::DbNumSet )
+{
+}
+
+std::unique_ptr<SwFieldType> SwDBNumSetFieldType::Copy() const
+{
+ return std::make_unique<SwDBNumSetFieldType>();
+}
+
+SwDBNumSetField::SwDBNumSetField(SwDBNumSetFieldType* pTyp,
+ const OUString& rCond,
+ const OUString& rDBNum,
+ const SwDBData& rDBData) :
+ SwDBNameInfField(pTyp, rDBData),
+ m_aCond(rCond),
+ m_aPar2(rDBNum),
+ m_bCondValid(true)
+{}
+
+OUString SwDBNumSetField::ExpandImpl(SwRootFrame const*const) const
+{
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwDBNumSetField::Copy() const
+{
+ std::unique_ptr<SwDBNumSetField> pTmp(new SwDBNumSetField(static_cast<SwDBNumSetFieldType*>(GetTyp()),
+ m_aCond, m_aPar2, GetDBData()));
+ pTmp->m_bCondValid = m_bCondValid;
+ pTmp->SetSubType(GetSubType());
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+void SwDBNumSetField::Evaluate(const SwDoc& rDoc)
+{
+ SwDBManager* pMgr = rDoc.GetDBManager();
+ const SwDBData& aTmpData = GetDBData();
+
+ if( m_bCondValid && pMgr && pMgr->IsInMerge() &&
+ pMgr->IsDataSourceOpen(aTmpData.sDataSource, aTmpData.sCommand, true))
+ { // condition OK -> adjust current Set
+ pMgr->ToRecordId(std::max(o3tl::narrowing<sal_uInt16>(m_aPar2.toInt32()), sal_uInt16(1))-1);
+ }
+}
+
+/// get LogDBName
+OUString SwDBNumSetField::GetPar1() const
+{
+ return m_aCond;
+}
+
+/// set LogDBName
+void SwDBNumSetField::SetPar1(const OUString& rStr)
+{
+ m_aCond = rStr;
+}
+
+/// get condition
+OUString SwDBNumSetField::GetPar2() const
+{
+ return m_aPar2;
+}
+
+/// set condition
+void SwDBNumSetField::SetPar2(const OUString& rStr)
+{
+ m_aPar2 = rStr;
+}
+
+bool SwDBNumSetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR3:
+ rAny <<= m_aCond;
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= m_aPar2.toInt32();
+ break;
+ default:
+ bRet = SwDBNameInfField::QueryValue(rAny, nWhichId );
+ }
+ return bRet;
+}
+
+bool SwDBNumSetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR3:
+ rAny >>= m_aCond;
+ break;
+ case FIELD_PROP_FORMAT:
+ {
+ sal_Int32 nVal = 0;
+ rAny >>= nVal;
+ m_aPar2 = OUString::number(nVal);
+ }
+ break;
+ default:
+ bRet = SwDBNameInfField::PutValue(rAny, nWhichId );
+ }
+ return bRet;
+}
+
+SwDBNameFieldType::SwDBNameFieldType(SwDoc* pDocument)
+ : SwFieldType( SwFieldIds::DatabaseName )
+{
+ m_pDoc = pDocument;
+}
+
+OUString SwDBNameFieldType::Expand() const
+{
+ const SwDBData aData = m_pDoc->GetDBData();
+ return aData.sDataSource + "." + aData.sCommand;
+}
+
+std::unique_ptr<SwFieldType> SwDBNameFieldType::Copy() const
+{
+ return std::make_unique<SwDBNameFieldType>(m_pDoc);
+}
+
+// name of the connected database
+
+SwDBNameField::SwDBNameField(SwDBNameFieldType* pTyp, const SwDBData& rDBData)
+ : SwDBNameInfField(pTyp, rDBData, 0)
+{}
+
+OUString SwDBNameField::ExpandImpl(SwRootFrame const*const) const
+{
+ if(0 ==(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE))
+ return static_cast<SwDBNameFieldType*>(GetTyp())->Expand();
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwDBNameField::Copy() const
+{
+ std::unique_ptr<SwDBNameField> pTmp(new SwDBNameField(static_cast<SwDBNameFieldType*>(GetTyp()), GetDBData()));
+ pTmp->ChangeFormat(GetFormat());
+ pTmp->SetLanguage(GetLanguage());
+ pTmp->SetSubType(GetSubType());
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+bool SwDBNameField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ return SwDBNameInfField::QueryValue(rAny, nWhichId );
+}
+
+bool SwDBNameField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ return SwDBNameInfField::PutValue(rAny, nWhichId );
+}
+
+SwDBSetNumberFieldType::SwDBSetNumberFieldType()
+ : SwFieldType( SwFieldIds::DbSetNumber )
+{
+}
+
+std::unique_ptr<SwFieldType> SwDBSetNumberFieldType::Copy() const
+{
+ return std::make_unique<SwDBSetNumberFieldType>();
+}
+
+// set-number of the connected database
+
+SwDBSetNumberField::SwDBSetNumberField(SwDBSetNumberFieldType* pTyp,
+ const SwDBData& rDBData,
+ sal_uInt32 nFormat)
+ : SwDBNameInfField(pTyp, rDBData, nFormat), m_nNumber(0)
+{}
+
+OUString SwDBSetNumberField::ExpandImpl(SwRootFrame const*const) const
+{
+ if(0 !=(GetSubType() & nsSwExtendedSubType::SUB_INVISIBLE) || m_nNumber == 0)
+ return OUString();
+ return FormatNumber(m_nNumber, static_cast<SvxNumType>(GetFormat()));
+}
+
+void SwDBSetNumberField::Evaluate(const SwDoc& rDoc)
+{
+ SwDBManager* pMgr = rDoc.GetDBManager();
+
+ const SwDBData& aTmpData = GetDBData();
+ if (!pMgr || !pMgr->IsInMerge() ||
+ !pMgr->IsDataSourceOpen(aTmpData.sDataSource, aTmpData.sCommand, false))
+ return;
+ m_nNumber = pMgr->GetSelectedRecordId();
+}
+
+std::unique_ptr<SwField> SwDBSetNumberField::Copy() const
+{
+ std::unique_ptr<SwDBSetNumberField> pTmp(
+ new SwDBSetNumberField(static_cast<SwDBSetNumberFieldType*>(GetTyp()), GetDBData(), GetFormat()));
+ pTmp->SetLanguage(GetLanguage());
+ pTmp->SetSetNumber(m_nNumber);
+ pTmp->SetSubType(GetSubType());
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+bool SwDBSetNumberField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ rAny <<= static_cast<sal_Int16>(GetFormat());
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= m_nNumber;
+ break;
+ default:
+ bRet = SwDBNameInfField::QueryValue( rAny, nWhichId );
+ }
+ return bRet;
+}
+
+bool SwDBSetNumberField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = true;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nSet = 0;
+ rAny >>= nSet;
+ if(nSet < css::style::NumberingType::NUMBER_NONE )
+ SetFormat(nSet);
+ }
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny >>= m_nNumber;
+ break;
+ default:
+ bRet = SwDBNameInfField::PutValue( rAny, nWhichId );
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/ddefld.cxx b/sw/source/core/fields/ddefld.cxx
new file mode 100644
index 000000000..bbb567aaf
--- /dev/null
+++ b/sw/source/core/fields/ddefld.cxx
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/ustrbuf.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sot/exchange.hxx>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <editsh.hxx>
+#include <fmtfld.hxx>
+#include <ddefld.hxx>
+#include <swddetbl.hxx>
+#include <swbaslnk.hxx>
+#include <unofldmid.h>
+#include <hints.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SwIntrnlRefLink : public SwBaseLink
+{
+ SwDDEFieldType& m_rFieldType;
+
+public:
+ SwIntrnlRefLink(SwDDEFieldType& rType, SfxLinkUpdateMode nUpdateType)
+ : SwBaseLink(nUpdateType, SotClipboardFormatId::STRING)
+ , m_rFieldType(rType)
+ {}
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ virtual const SwNode* GetAnchor() const override;
+ virtual bool IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const override;
+};
+
+}
+
+::sfx2::SvBaseLink::UpdateResult SwIntrnlRefLink::DataChanged( const OUString& rMimeType,
+ const uno::Any & rValue )
+{
+ switch( SotExchange::GetFormatIdFromMimeType( rMimeType ) )
+ {
+ case SotClipboardFormatId::STRING:
+ if( !IsNoDataFlag() )
+ {
+ OUString sStr;
+ if (!(rValue >>= sStr))
+ {
+ uno::Sequence< sal_Int8 > aSeq;
+ rValue >>= aSeq;
+ sStr = OUString(reinterpret_cast<char const*>(aSeq.getConstArray()), aSeq.getLength(), osl_getThreadTextEncoding());
+ }
+
+ // remove not needed CR-LF at the end
+ sal_Int32 n = sStr.getLength();
+ while( n && 0 == sStr[ n-1 ] )
+ --n;
+ if( n && 0x0a == sStr[ n-1 ] )
+ --n;
+ if( n && 0x0d == sStr[ n-1 ] )
+ --n;
+
+ bool bDel = n != sStr.getLength();
+ if( bDel )
+ sStr = sStr.copy( 0, n );
+
+ m_rFieldType.SetExpansion(sStr);
+ // set Expansion first! (otherwise this flag will be deleted)
+ m_rFieldType.SetCRLFDelFlag(bDel);
+ }
+ break;
+
+ // other formats
+ default:
+ return SUCCESS;
+ }
+
+ if(!ChkNoDataFlag())
+ m_rFieldType.UpdateDDE();
+
+ return SUCCESS;
+}
+
+void SwIntrnlRefLink::Closed()
+{
+ if (m_rFieldType.GetDoc() && !m_rFieldType.GetDoc()->IsInDtor())
+ {
+ // advise goes, convert all fields into text?
+ SwViewShell* pSh = m_rFieldType.GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if (SwEditShell* pESh = m_rFieldType.GetDoc()->GetEditShell())
+ {
+ pESh->StartAllAction();
+ pESh->FieldToText(&m_rFieldType);
+ pESh->EndAllAction();
+ }
+ else
+ {
+ pSh->StartAction();
+ // to call at the doc ??
+ pSh->EndAction();
+ }
+ }
+ SvBaseLink::Closed();
+}
+
+sw::LinkAnchorSearchHint::~LinkAnchorSearchHint() {};
+
+const SwNode* SwIntrnlRefLink::GetAnchor() const
+{
+ // here, any anchor of the normal NodesArray should be sufficient
+ const SwNode* pNd = nullptr;
+ m_rFieldType.CallSwClientNotify(
+ sw::LinkAnchorSearchHint(m_rFieldType.GetDoc()->GetNodes(), pNd));
+ return pNd;
+}
+
+bool SwIntrnlRefLink::IsInRange( SwNodeOffset nSttNd, SwNodeOffset nEndNd ) const
+{
+ bool bInRange = false;
+ m_rFieldType.CallSwClientNotify(sw::InRangeSearchHint(nSttNd, nEndNd, bInRange));
+ return bInRange;
+}
+
+SwDDEFieldType::SwDDEFieldType(const OUString& rName,
+ const OUString& rCmd, SfxLinkUpdateMode nUpdateType )
+ : SwFieldType( SwFieldIds::Dde ),
+ m_aName( rName ), m_pDoc( nullptr ), m_nRefCount( 0 )
+{
+ m_bCRLFFlag = m_bDeleted = false;
+ m_RefLink = new SwIntrnlRefLink( *this, nUpdateType );
+ SetCmd( rCmd );
+}
+
+SwDDEFieldType::~SwDDEFieldType()
+{
+ if( m_pDoc && !m_pDoc->IsInDtor() )
+ m_pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ m_RefLink->Disconnect();
+}
+
+std::unique_ptr<SwFieldType> SwDDEFieldType::Copy() const
+{
+ std::unique_ptr<SwDDEFieldType> pType(new SwDDEFieldType( m_aName, GetCmd(), GetType() ));
+ pType->m_aExpansion = m_aExpansion;
+ pType->m_bCRLFFlag = m_bCRLFFlag;
+ pType->m_bDeleted = m_bDeleted;
+ pType->SetDoc( m_pDoc );
+ return pType;
+}
+
+OUString SwDDEFieldType::GetName() const
+{
+ return m_aName;
+}
+
+void SwDDEFieldType::SetCmd( const OUString& _aStr )
+{
+ OUString aStr = _aStr;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aStr = aStr.replaceFirst(" ", " ", &nIndex);
+ } while (nIndex>=0);
+ m_RefLink->SetLinkSourceName( aStr );
+}
+
+OUString const & SwDDEFieldType::GetCmd() const
+{
+ return m_RefLink->GetLinkSourceName();
+}
+
+void SwDDEFieldType::SetDoc( SwDoc* pNewDoc )
+{
+ if( pNewDoc == m_pDoc )
+ return;
+
+ if( m_pDoc && m_RefLink.is() )
+ {
+ OSL_ENSURE( !m_nRefCount, "How do we get the references?" );
+ m_pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+
+ m_pDoc = pNewDoc;
+ if( m_pDoc && m_nRefCount )
+ {
+ m_RefLink->SetVisible( m_pDoc->getIDocumentLinksAdministration().IsVisibleLinks() );
+ m_pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( m_RefLink.get() );
+ }
+}
+
+void SwDDEFieldType::RefCntChgd()
+{
+ if( m_nRefCount )
+ {
+ m_RefLink->SetVisible( m_pDoc->getIDocumentLinksAdministration().IsVisibleLinks() );
+ m_pDoc->getIDocumentLinksAdministration().GetLinkManager().InsertDDELink( m_RefLink.get() );
+ if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ m_RefLink->Update();
+ }
+ else
+ {
+ Disconnect();
+ m_pDoc->getIDocumentLinksAdministration().GetLinkManager().Remove( m_RefLink.get() );
+ }
+}
+
+void SwDDEFieldType::QueryValue( uno::Any& rVal, sal_uInt16 nWhichId ) const
+{
+ sal_Int32 nPart = -1;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR2: nPart = 2; break;
+ case FIELD_PROP_PAR4: nPart = 1; break;
+ case FIELD_PROP_SUBTYPE: nPart = 0; break;
+ case FIELD_PROP_BOOL1:
+ rVal <<= GetType() == SfxLinkUpdateMode::ALWAYS;
+ break;
+ case FIELD_PROP_PAR5:
+ rVal <<= m_aExpansion;
+ break;
+ default:
+ assert(false);
+ }
+ if ( nPart>=0 )
+ rVal <<= GetCmd().getToken(nPart, sfx2::cTokenSeparator);
+}
+
+void SwDDEFieldType::PutValue( const uno::Any& rVal, sal_uInt16 nWhichId )
+{
+ sal_Int32 nPart = -1;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR2: nPart = 2; break;
+ case FIELD_PROP_PAR4: nPart = 1; break;
+ case FIELD_PROP_SUBTYPE: nPart = 0; break;
+ case FIELD_PROP_BOOL1:
+ SetType( *o3tl::doAccess<bool>(rVal) ?
+ SfxLinkUpdateMode::ALWAYS :
+ SfxLinkUpdateMode::ONCALL );
+ break;
+ case FIELD_PROP_PAR5:
+ rVal >>= m_aExpansion;
+ break;
+ default:
+ assert(false);
+ }
+ if( nPart<0 )
+ return;
+
+ const OUString sOldCmd( GetCmd() );
+ OUStringBuffer sNewCmd;
+ sal_Int32 nIndex = 0;
+ for (sal_Int32 i=0; i<3; ++i)
+ {
+ OUString sToken = sOldCmd.getToken(0, sfx2::cTokenSeparator, nIndex);
+ if (i==nPart)
+ {
+ rVal >>= sToken;
+ }
+ sNewCmd.append((i < 2)
+ ? sToken + OUStringChar(sfx2::cTokenSeparator) : sToken);
+ }
+ SetCmd( sNewCmd.makeStringAndClear() );
+}
+
+void SwDDEFieldType::UpdateDDE(const bool bNotifyShells)
+{
+ auto pDoc = GetDoc();
+ assert(pDoc);
+ if(IsModifyLocked())
+ return;
+ SwViewShell* pSh = bNotifyShells ? pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() : nullptr;
+ SwEditShell* pESh = bNotifyShells ? pDoc->GetEditShell() : nullptr;
+
+ // Search for fields. If no valid found, disconnect.
+ LockModify();
+
+ std::vector<SwFormatField*> vFields;
+ std::vector<SwDDETable*> vTables;
+ GatherFields(vFields, false);
+ GatherDdeTables(vTables);
+ const bool bDoAction = vFields.size() || vTables.size();
+ if(bDoAction)
+ {
+ if(pESh)
+ pESh->StartAllAction();
+ else if(pSh)
+ pSh->StartAction();
+ }
+
+ // DDE fields attribute in the text
+ SwMsgPoolItem aUpdateDDE(RES_UPDATEDDETBL);
+ for(auto pFormatField: vFields)
+ {
+ if(pFormatField->GetTextField())
+ pFormatField->UpdateTextNode( nullptr, &aUpdateDDE );
+ }
+ // a DDE tables in the text
+ for(auto pTable: vTables)
+ pTable->ChangeContent();
+
+ UnlockModify();
+
+ if(bDoAction)
+ {
+ if(pESh)
+ pESh->EndAllAction();
+ else if(pSh)
+ pSh->EndAction();
+
+ if(pSh)
+ pSh->GetDoc()->getIDocumentState().SetModified();
+ }
+}
+
+SwDDEField::SwDDEField( SwDDEFieldType* pInitType )
+ : SwField(pInitType)
+{
+}
+
+SwDDEField::~SwDDEField()
+{
+ if( GetTyp()->HasOnlyOneListener() )
+ static_cast<SwDDEFieldType*>(GetTyp())->Disconnect();
+}
+
+OUString SwDDEField::ExpandImpl(SwRootFrame const*const) const
+{
+ OUString aStr = static_cast<SwDDEFieldType*>(GetTyp())->GetExpansion();
+ aStr = aStr.replaceAll("\r", "");
+ aStr = aStr.replaceAll("\t", " ");
+ aStr = aStr.replaceAll("\n", "|");
+ if (aStr.endsWith("|"))
+ {
+ return aStr.copy(0, aStr.getLength()-1);
+ }
+ return aStr;
+}
+
+std::unique_ptr<SwField> SwDDEField::Copy() const
+{
+ return std::make_unique<SwDDEField>(static_cast<SwDDEFieldType*>(GetTyp()));
+}
+
+/// get field type name
+OUString SwDDEField::GetPar1() const
+{
+ return static_cast<const SwDDEFieldType*>(GetTyp())->GetName();
+}
+
+/// get field type command
+OUString SwDDEField::GetPar2() const
+{
+ return static_cast<const SwDDEFieldType*>(GetTyp())->GetCmd();
+}
+
+/// set field type command
+void SwDDEField::SetPar2(const OUString& rStr)
+{
+ static_cast<SwDDEFieldType*>(GetTyp())->SetCmd(rStr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/ddetbl.cxx b/sw/source/core/fields/ddetbl.cxx
new file mode 100644
index 000000000..b1576e1a1
--- /dev/null
+++ b/sw/source/core/fields/ddetbl.cxx
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <index.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <swddetbl.hxx>
+#include <fmtfld.hxx>
+#include <ddefld.hxx>
+#include <ndindex.hxx>
+#include <fldupde.hxx>
+#include <swtblfmt.hxx>
+#include <fieldhint.hxx>
+#include <osl/diagnose.h>
+#include <pam.hxx>
+
+/// Ctor moves all lines/boxes from a SwTable into itself.
+/// Afterwards the SwTable is empty and must be deleted.
+SwDDETable::SwDDETable( SwTable& rTable, SwDDEFieldType* pDDEType, bool bUpdate )
+ : SwTable(rTable), m_aDepends(*this), m_pDDEType(pDDEType)
+{
+ m_aDepends.StartListening(m_pDDEType);
+ // copy the table data
+ m_TabSortContentBoxes.insert(rTable.GetTabSortBoxes());
+ rTable.GetTabSortBoxes().clear();
+
+ m_aLines.insert( m_aLines.begin(),
+ rTable.GetTabLines().begin(), rTable.GetTabLines().end() ); // move lines
+ rTable.GetTabLines().clear();
+
+ if( !m_aLines.empty() )
+ {
+ const SwNode& rNd = *GetTabSortBoxes()[0]->GetSttNd();
+ if( rNd.GetNodes().IsDocNodes() )
+ {
+ pDDEType->IncRefCnt();
+
+ // update box content only if update flag is set (false in import)
+ if (bUpdate)
+ ChangeContent();
+ }
+ }
+}
+
+SwDDETable::~SwDDETable()
+{
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ if (!pDoc->IsInDtor() && !m_aLines.empty())
+ {
+ assert(m_pTableNode);
+ if (m_pTableNode->GetNodes().IsDocNodes())
+ {
+ m_pDDEType->DecRefCnt();
+ }
+ }
+
+ // If it is the last dependent of the "deleted field" than delete it finally
+ if( m_pDDEType->IsDeleted() && m_pDDEType->HasOnlyOneListener() )
+ {
+ m_aDepends.EndListeningAll();
+ delete m_pDDEType;
+ m_pDDEType = nullptr;
+ }
+}
+
+void SwDDETable::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ switch(pLegacy->GetWhich())
+ {
+ case RES_UPDATEDDETBL:
+ ChangeContent();
+ break;
+ default:
+ SwTable::SwClientNotify(rModify, rHint);
+ }
+ }
+ else if(auto pFieldHint = dynamic_cast<const SwFieldHint*>(&rHint))
+ {
+ pFieldHint->m_pPaM->DeleteMark(); // TODO: this is really hackish
+ // replace DDETable by real table
+ NoDDETable();
+ }
+ else if(const auto pLinkAnchorHint = dynamic_cast<const sw::LinkAnchorSearchHint*>(&rHint))
+ {
+ if(pLinkAnchorHint->m_rpFoundNode)
+ return;
+ const auto pNd = GetTabSortBoxes()[0]->GetSttNd();
+ if( pNd && &pLinkAnchorHint->m_rNodes == &pNd->GetNodes() )
+ pLinkAnchorHint->m_rpFoundNode = pNd;
+ }
+ else if(const sw::InRangeSearchHint* pInRangeHint = dynamic_cast<const sw::InRangeSearchHint*>(&rHint))
+ {
+ if(pInRangeHint->m_rIsInRange)
+ return;
+ const SwTableNode* pTableNd = GetTabSortBoxes()[0]->GetSttNd()->FindTableNode();
+ if( pTableNd->GetNodes().IsDocNodes() &&
+ pInRangeHint->m_nSttNd < pTableNd->EndOfSectionIndex() &&
+ pInRangeHint->m_nEndNd > pTableNd->GetIndex() )
+ pInRangeHint->m_rIsInRange = true;
+ } else if (const auto pGatherDdeTablesHint = dynamic_cast<const sw::GatherDdeTablesHint*>(&rHint))
+ {
+ pGatherDdeTablesHint->m_rvTables.push_back(this);
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ if(m_pDDEType == &rModify)
+ m_pDDEType = const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pModifyChangedHint->m_pNew));
+ }
+}
+
+void SwDDETable::ChangeContent()
+{
+ OSL_ENSURE( GetFrameFormat(), "No FrameFormat" );
+
+ // Is this the correct NodesArray? (because of UNDO)
+ if( m_aLines.empty() )
+ return;
+ OSL_ENSURE( !GetTabSortBoxes().empty(), "Table without content?" );
+ if( !GetTabSortBoxes()[0]->GetSttNd()->GetNodes().IsDocNodes() )
+ return;
+
+
+ OUString aExpand = m_pDDEType->GetExpansion().replaceAll("\r", "");
+ sal_Int32 nExpandTokenPos = 0;
+
+ for( size_t n = 0; n < m_aLines.size(); ++n )
+ {
+ OUString aLine = aExpand.getToken( 0, '\n', nExpandTokenPos );
+ sal_Int32 nLineTokenPos = 0;
+ SwTableLine* pLine = m_aLines[ n ];
+ for( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[ i ];
+ OSL_ENSURE( pBox->GetSttIdx(), "no content box" );
+ SwNodeIndex aNdIdx( *pBox->GetSttNd(), 1 );
+ SwTextNode* pTextNode = aNdIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNode, "No Node" );
+ SwIndex aCntIdx( pTextNode, 0 );
+ pTextNode->EraseText( aCntIdx );
+ pTextNode->InsertText( aLine.getToken( 0, '\t', nLineTokenPos ), aCntIdx );
+
+ SwTableBoxFormat* pBoxFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
+ pBoxFormat->UnlockModify();
+ }
+ }
+
+ const IDocumentSettingAccess& rIDSA = GetFrameFormat()->getIDocumentSettingAccess();
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ if( AUTOUPD_FIELD_AND_CHARTS == rIDSA.getFieldUpdateFlags(true) )
+ pDoc->getIDocumentFieldsAccess().SetFieldsDirty( true, nullptr, SwNodeOffset(0) );
+}
+
+SwDDEFieldType* SwDDETable::GetDDEFieldType()
+{
+ return m_pDDEType;
+}
+
+void SwDDETable::NoDDETable()
+{
+ // search table node
+ OSL_ENSURE( GetFrameFormat(), "No FrameFormat" );
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+
+ // Is this the correct NodesArray? (because of UNDO)
+ if( m_aLines.empty() )
+ return;
+ OSL_ENSURE( !GetTabSortBoxes().empty(), "Table without content?" );
+ SwNode* pNd = const_cast<SwNode*>(static_cast<SwNode const *>(GetTabSortBoxes()[0]->GetSttNd()));
+ if( !pNd->GetNodes().IsDocNodes() )
+ return;
+
+ SwTableNode* pTableNd = pNd->FindTableNode();
+ OSL_ENSURE( pTableNd, "Where is the table?");
+
+ std::unique_ptr<SwTable> pNewTable(new SwTable( *this ));
+
+ // copy the table data
+ pNewTable->GetTabSortBoxes().insert( GetTabSortBoxes() ); // move content boxes
+ GetTabSortBoxes().clear();
+
+ pNewTable->GetTabLines().insert( pNewTable->GetTabLines().begin(),
+ GetTabLines().begin(), GetTabLines().end() ); // move lines
+ GetTabLines().clear();
+
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ m_pDDEType->DecRefCnt();
+
+ pTableNd->SetNewTable( std::move(pNewTable) ); // replace table
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/docufld.cxx b/sw/source/core/fields/docufld.cxx
new file mode 100644
index 000000000..45793b492
--- /dev/null
+++ b/sw/source/core/fields/docufld.cxx
@@ -0,0 +1,2660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <textapi.hxx>
+
+#include <hintids.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <com/sun/star/script/Converter.hpp>
+#include <com/sun/star/text/PlaceholderType.hpp>
+#include <com/sun/star/text/TemplateDisplayFormat.hpp>
+#include <com/sun/star/text/PageNumberType.hpp>
+#include <com/sun/star/text/FilenameDisplayFormat.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/document/XDocumentProperties.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Duration.hpp>
+#include <o3tl/any.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/numformat.hxx>
+#include <svl/urihelper.hxx>
+#include <unotools/useroptions.hxx>
+#include <unotools/syslocale.hxx>
+#include <libxml/xmlstring.h>
+#include <libxml/xmlwriter.h>
+
+#include <tools/time.hxx>
+#include <tools/datetime.hxx>
+
+#include <com/sun/star/util/DateTime.hpp>
+
+#include <swmodule.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/doctempl.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <charfmt.hxx>
+#include <docstat.hxx>
+#include <pagedesc.hxx>
+#include <fmtpdsc.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <pam.hxx>
+#include <viewsh.hxx>
+#include <dbmgr.hxx>
+#include <shellres.hxx>
+#include <docufld.hxx>
+#include <flddat.hxx>
+#include <docfld.hxx>
+#include <ndtxt.hxx>
+#include <expfld.hxx>
+#include <poolfmt.hxx>
+#include <docsh.hxx>
+#include <unofldmid.h>
+#include <swunohelper.hxx>
+#include <strings.hrc>
+
+#include <editeng/outlobj.hxx>
+#include <calbck.hxx>
+#include <hints.hxx>
+
+#define URL_DECODE INetURLObject::DecodeMechanism::Unambiguous
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace nsSwDocInfoSubType;
+
+SwPageNumberFieldType::SwPageNumberFieldType()
+ : SwFieldType( SwFieldIds::PageNumber ),
+ m_nNumberingType( SVX_NUM_ARABIC ),
+ m_bVirtual( false )
+{
+}
+
+OUString SwPageNumberFieldType::Expand( SvxNumType nFormat, short nOff,
+ sal_uInt16 const nPageNumber, sal_uInt16 const nMaxPage,
+ const OUString& rUserStr, LanguageType nLang ) const
+{
+ SvxNumType nTmpFormat = (SVX_NUM_PAGEDESC == nFormat) ? m_nNumberingType : nFormat;
+ int const nTmp = nPageNumber + nOff;
+
+ if (0 > nTmp || SVX_NUM_NUMBER_NONE == nTmpFormat || (!m_bVirtual && nTmp > nMaxPage))
+ return OUString();
+
+ if( SVX_NUM_CHAR_SPECIAL == nTmpFormat )
+ return rUserStr;
+
+ return FormatNumber( nTmp, nTmpFormat, nLang );
+}
+
+std::unique_ptr<SwFieldType> SwPageNumberFieldType::Copy() const
+{
+ std::unique_ptr<SwPageNumberFieldType> pTmp(new SwPageNumberFieldType());
+
+ pTmp->m_nNumberingType = m_nNumberingType;
+ pTmp->m_bVirtual = m_bVirtual;
+
+ return pTmp;
+}
+
+void SwPageNumberFieldType::ChangeExpansion( SwDoc* pDoc,
+ bool bVirt,
+ const SvxNumType* pNumFormat )
+{
+ if( pNumFormat )
+ m_nNumberingType = *pNumFormat;
+
+ m_bVirtual = false;
+ if (!(bVirt && pDoc))
+ return;
+
+ // check the flag since the layout NEVER sets it back
+ const SfxItemPool &rPool = pDoc->GetAttrPool();
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_PAGEDESC))
+ {
+ auto pDesc = dynamic_cast<const SwFormatPageDesc*>(pItem);
+ if( pDesc && pDesc->GetNumOffset() && pDesc->GetDefinedIn() )
+ {
+ const SwContentNode* pNd = dynamic_cast<const SwContentNode*>( pDesc->GetDefinedIn() );
+ if( pNd )
+ {
+ if (SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pNd).First())
+ // sw_redlinehide: not sure if this should happen only if
+ // it's the first node, because that's where RES_PAGEDESC
+ // is effective?
+ m_bVirtual = true;
+ }
+ else if( dynamic_cast< const SwFormat* >(pDesc->GetDefinedIn()) != nullptr)
+ {
+ SwAutoFormatGetDocNode aGetHt( &pDoc->GetNodes() );
+ m_bVirtual = !pDesc->GetDefinedIn()->GetInfo( aGetHt );
+ break;
+ }
+ }
+ }
+}
+
+SwPageNumberField::SwPageNumberField(SwPageNumberFieldType* pTyp,
+ sal_uInt16 nSub, sal_uInt32 nFormat, short nOff,
+ sal_uInt16 const nPageNumber, sal_uInt16 const nMaxPage)
+ : SwField(pTyp, nFormat), m_nSubType(nSub), m_nOffset(nOff)
+ , m_nPageNumber(nPageNumber)
+ , m_nMaxPage(nMaxPage)
+{
+}
+
+void SwPageNumberField::ChangeExpansion(sal_uInt16 const nPageNumber,
+ sal_uInt16 const nMaxPage)
+{
+ m_nPageNumber = nPageNumber;
+ m_nMaxPage = nMaxPage;
+}
+
+OUString SwPageNumberField::ExpandImpl(SwRootFrame const*const) const
+{
+ OUString sRet;
+ SwPageNumberFieldType* pFieldType = static_cast<SwPageNumberFieldType*>(GetTyp());
+
+ if( PG_NEXT == m_nSubType && 1 != m_nOffset )
+ {
+ sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), 1, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage());
+ if (!sRet.isEmpty())
+ {
+ sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), m_nOffset, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage());
+ }
+ }
+ else if( PG_PREV == m_nSubType && -1 != m_nOffset )
+ {
+ sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), -1, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage());
+ if (!sRet.isEmpty())
+ {
+ sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), m_nOffset, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage());
+ }
+ }
+ else
+ sRet = pFieldType->Expand(static_cast<SvxNumType>(GetFormat()), m_nOffset, m_nPageNumber, m_nMaxPage, m_sUserStr, GetLanguage());
+ return sRet;
+}
+
+std::unique_ptr<SwField> SwPageNumberField::Copy() const
+{
+ std::unique_ptr<SwPageNumberField> pTmp(new SwPageNumberField(
+ static_cast<SwPageNumberFieldType*>(GetTyp()), m_nSubType,
+ GetFormat(), m_nOffset, m_nPageNumber, m_nMaxPage));
+ pTmp->SetLanguage( GetLanguage() );
+ pTmp->SetUserString( m_sUserStr );
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+OUString SwPageNumberField::GetPar2() const
+{
+ return OUString::number(m_nOffset);
+}
+
+void SwPageNumberField::SetPar2(const OUString& rStr)
+{
+ m_nOffset = static_cast<short>(rStr.toInt32());
+}
+
+sal_uInt16 SwPageNumberField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+bool SwPageNumberField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int16>(GetFormat());
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny <<= m_nOffset;
+ break;
+ case FIELD_PROP_SUBTYPE:
+ {
+ text::PageNumberType eType;
+ eType = text::PageNumberType_CURRENT;
+ if(m_nSubType == PG_PREV)
+ eType = text::PageNumberType_PREV;
+ else if(m_nSubType == PG_NEXT)
+ eType = text::PageNumberType_NEXT;
+ rAny <<= eType;
+ }
+ break;
+ case FIELD_PROP_PAR1:
+ rAny <<= m_sUserStr;
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwPageNumberField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = true;
+ sal_Int16 nSet = 0;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_FORMAT:
+ rAny >>= nSet;
+
+ // TODO: where do the defines come from?
+ if(nSet <= SVX_NUM_PAGEDESC )
+ SetFormat(nSet);
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny >>= nSet;
+ m_nOffset = nSet;
+ break;
+ case FIELD_PROP_SUBTYPE:
+ switch( static_cast<text::PageNumberType>(SWUnoHelper::GetEnumAsInt32( rAny )) )
+ {
+ case text::PageNumberType_CURRENT:
+ m_nSubType = PG_RANDOM;
+ break;
+ case text::PageNumberType_PREV:
+ m_nSubType = PG_PREV;
+ break;
+ case text::PageNumberType_NEXT:
+ m_nSubType = PG_NEXT;
+ break;
+ default:
+ bRet = false;
+ }
+ break;
+ case FIELD_PROP_PAR1:
+ rAny >>= m_sUserStr;
+ break;
+
+ default:
+ assert(false);
+ }
+ return bRet;
+}
+
+SwAuthorFieldType::SwAuthorFieldType()
+ : SwFieldType( SwFieldIds::Author )
+{
+}
+
+OUString SwAuthorFieldType::Expand(sal_uLong nFormat)
+{
+ SvtUserOptions& rOpt = SW_MOD()->GetUserOptions();
+ if((nFormat & 0xff) == AF_NAME)
+ return rOpt.GetFullName();
+
+ return rOpt.GetID();
+}
+
+std::unique_ptr<SwFieldType> SwAuthorFieldType::Copy() const
+{
+ return std::make_unique<SwAuthorFieldType>();
+}
+
+SwAuthorField::SwAuthorField(SwAuthorFieldType* pTyp, sal_uInt32 nFormat)
+ : SwField(pTyp, nFormat)
+{
+ m_aContent = SwAuthorFieldType::Expand(GetFormat());
+}
+
+OUString SwAuthorField::ExpandImpl(SwRootFrame const*const) const
+{
+ if (!IsFixed())
+ const_cast<SwAuthorField*>(this)->m_aContent =
+ SwAuthorFieldType::Expand(GetFormat());
+
+ return m_aContent;
+}
+
+std::unique_ptr<SwField> SwAuthorField::Copy() const
+{
+ std::unique_ptr<SwAuthorField> pTmp(new SwAuthorField( static_cast<SwAuthorFieldType*>(GetTyp()),
+ GetFormat()));
+ pTmp->SetExpansion(m_aContent);
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+bool SwAuthorField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ rAny <<= (GetFormat() & 0xff) == AF_NAME;
+ break;
+
+ case FIELD_PROP_BOOL2:
+ rAny <<= IsFixed();
+ break;
+
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aContent;
+ break;
+
+ case FIELD_PROP_TITLE:
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwAuthorField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ SetFormat( *o3tl::doAccess<bool>(rAny) ? AF_NAME : AF_SHORTCUT );
+ break;
+
+ case FIELD_PROP_BOOL2:
+ if( *o3tl::doAccess<bool>(rAny) )
+ SetFormat( GetFormat() | AF_FIXED);
+ else
+ SetFormat( GetFormat() & ~AF_FIXED);
+ break;
+
+ case FIELD_PROP_PAR1:
+ rAny >>= m_aContent;
+ break;
+
+ case FIELD_PROP_TITLE:
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+SwFileNameFieldType::SwFileNameFieldType(SwDoc& rDocument)
+ : SwFieldType( SwFieldIds::Filename )
+ , m_rDoc(rDocument)
+{
+}
+
+OUString SwFileNameFieldType::Expand(sal_uLong nFormat) const
+{
+ OUString aRet;
+ const SwDocShell* pDShell = m_rDoc.GetDocShell();
+ if( pDShell && pDShell->HasName() )
+ {
+ const INetURLObject& rURLObj = pDShell->GetMedium()->GetURLObject();
+ switch( nFormat & ~FF_FIXED )
+ {
+ case FF_PATH:
+ {
+ if( INetProtocol::File == rURLObj.GetProtocol() )
+ {
+ INetURLObject aTemp(rURLObj);
+ aTemp.removeSegment();
+ // last slash should belong to the pathname
+ aRet = aTemp.PathToFileName();
+ }
+ else
+ {
+ aRet = URIHelper::removePassword(
+ rURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ INetURLObject::EncodeMechanism::WasEncoded, URL_DECODE );
+ const sal_Int32 nPos = aRet.indexOf(rURLObj.GetLastName( URL_DECODE ));
+ if (nPos>=0)
+ {
+ aRet = aRet.copy(0, nPos);
+ }
+ }
+ }
+ break;
+
+ case FF_NAME:
+ aRet = rURLObj.GetLastName( INetURLObject::DecodeMechanism::WithCharset );
+ break;
+
+ case FF_NAME_NOEXT:
+ aRet = rURLObj.GetBase();
+ break;
+
+ default:
+ if( INetProtocol::File == rURLObj.GetProtocol() )
+ aRet = rURLObj.GetFull();
+ else
+ aRet = URIHelper::removePassword(
+ rURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ INetURLObject::EncodeMechanism::WasEncoded, URL_DECODE );
+ }
+ }
+ return aRet;
+}
+
+std::unique_ptr<SwFieldType> SwFileNameFieldType::Copy() const
+{
+ return std::make_unique<SwFileNameFieldType>(m_rDoc);
+}
+
+SwFileNameField::SwFileNameField(SwFileNameFieldType* pTyp, sal_uInt32 nFormat)
+ : SwField(pTyp, nFormat)
+{
+ m_aContent = static_cast<SwFileNameFieldType*>(GetTyp())->Expand(GetFormat());
+}
+
+OUString SwFileNameField::ExpandImpl(SwRootFrame const*const) const
+{
+ if (!IsFixed())
+ const_cast<SwFileNameField*>(this)->m_aContent = static_cast<SwFileNameFieldType*>(GetTyp())->Expand(GetFormat());
+
+ return m_aContent;
+}
+
+std::unique_ptr<SwField> SwFileNameField::Copy() const
+{
+ std::unique_ptr<SwFileNameField> pTmp(
+ new SwFileNameField(static_cast<SwFileNameFieldType*>(GetTyp()), GetFormat()));
+ pTmp->SetExpansion(m_aContent);
+
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+bool SwFileNameField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_FORMAT:
+ {
+ sal_Int16 nRet;
+ switch( GetFormat() &(~FF_FIXED) )
+ {
+ case FF_PATH:
+ nRet = text::FilenameDisplayFormat::PATH;
+ break;
+ case FF_NAME_NOEXT:
+ nRet = text::FilenameDisplayFormat::NAME;
+ break;
+ case FF_NAME:
+ nRet = text::FilenameDisplayFormat::NAME_AND_EXT;
+ break;
+ default: nRet = text::FilenameDisplayFormat::FULL;
+ }
+ rAny <<= nRet;
+ }
+ break;
+
+ case FIELD_PROP_BOOL2:
+ rAny <<= IsFixed();
+ break;
+
+ case FIELD_PROP_PAR3:
+ rAny <<= m_aContent;
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwFileNameField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_FORMAT:
+ {
+ //JP 24.10.2001: int32 because in UnoField.cxx a putvalue is
+ // called with a int32 value! But normally we need
+ // here only a int16
+ sal_Int32 nType = 0;
+ rAny >>= nType;
+ bool bFixed = IsFixed();
+ switch( nType )
+ {
+ case text::FilenameDisplayFormat::PATH:
+ nType = FF_PATH;
+ break;
+ case text::FilenameDisplayFormat::NAME:
+ nType = FF_NAME_NOEXT;
+ break;
+ case text::FilenameDisplayFormat::NAME_AND_EXT:
+ nType = FF_NAME;
+ break;
+ default: nType = FF_PATHNAME;
+ }
+ if(bFixed)
+ nType |= FF_FIXED;
+ SetFormat(nType);
+ }
+ break;
+
+ case FIELD_PROP_BOOL2:
+ if( *o3tl::doAccess<bool>(rAny) )
+ SetFormat( GetFormat() | FF_FIXED);
+ else
+ SetFormat( GetFormat() & ~FF_FIXED);
+ break;
+
+ case FIELD_PROP_PAR3:
+ rAny >>= m_aContent;
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+SwTemplNameFieldType::SwTemplNameFieldType(SwDoc& rDocument)
+ : SwFieldType( SwFieldIds::TemplateName )
+ , m_rDoc(rDocument)
+{
+}
+
+OUString SwTemplNameFieldType::Expand(sal_uLong nFormat) const
+{
+ OSL_ENSURE( nFormat < FF_END, "Expand: no valid Format!" );
+
+ OUString aRet;
+ SwDocShell *pDocShell(m_rDoc.GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (pDocShell) {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+
+ if( FF_UI_NAME == nFormat )
+ aRet = xDocProps->getTemplateName();
+ else if( !xDocProps->getTemplateURL().isEmpty() )
+ {
+ if( FF_UI_RANGE == nFormat )
+ {
+ // for getting region names!
+ SfxDocumentTemplates aFac;
+ OUString sTmp;
+ OUString sRegion;
+ aFac.GetLogicNames( xDocProps->getTemplateURL(), sRegion, sTmp );
+ aRet = sRegion;
+ }
+ else
+ {
+ INetURLObject aPathName( xDocProps->getTemplateURL() );
+ if( FF_NAME == nFormat )
+ aRet = aPathName.GetLastName(URL_DECODE);
+ else if( FF_NAME_NOEXT == nFormat )
+ aRet = aPathName.GetBase();
+ else
+ {
+ if( FF_PATH == nFormat )
+ {
+ aPathName.removeSegment();
+ aRet = aPathName.GetFull();
+ }
+ else
+ aRet = aPathName.GetFull();
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+std::unique_ptr<SwFieldType> SwTemplNameFieldType::Copy() const
+{
+ return std::make_unique<SwTemplNameFieldType>(m_rDoc);
+}
+
+SwTemplNameField::SwTemplNameField(SwTemplNameFieldType* pTyp, sal_uInt32 nFormat)
+ : SwField(pTyp, nFormat)
+{}
+
+OUString SwTemplNameField::ExpandImpl(SwRootFrame const*const) const
+{
+ return static_cast<SwTemplNameFieldType*>(GetTyp())->Expand(GetFormat());
+}
+
+std::unique_ptr<SwField> SwTemplNameField::Copy() const
+{
+ return std::make_unique<SwTemplNameField>(static_cast<SwTemplNameFieldType*>(GetTyp()), GetFormat());
+}
+
+bool SwTemplNameField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_FORMAT:
+ {
+ sal_Int16 nRet;
+ switch( GetFormat() )
+ {
+ case FF_PATH: nRet = text::FilenameDisplayFormat::PATH; break;
+ case FF_NAME_NOEXT: nRet = text::FilenameDisplayFormat::NAME; break;
+ case FF_NAME: nRet = text::FilenameDisplayFormat::NAME_AND_EXT; break;
+ case FF_UI_RANGE: nRet = text::TemplateDisplayFormat::AREA; break;
+ case FF_UI_NAME: nRet = text::TemplateDisplayFormat::TITLE; break;
+ default: nRet = text::FilenameDisplayFormat::FULL;
+
+ }
+ rAny <<= nRet;
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwTemplNameField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_FORMAT:
+ {
+ //JP 24.10.2001: int32 because in UnoField.cxx a putvalue is
+ // called with a int32 value! But normally we need
+ // here only a int16
+ sal_Int32 nType = 0;
+ rAny >>= nType;
+ switch( nType )
+ {
+ case text::FilenameDisplayFormat::PATH:
+ SetFormat(FF_PATH);
+ break;
+ case text::FilenameDisplayFormat::NAME:
+ SetFormat(FF_NAME_NOEXT);
+ break;
+ case text::FilenameDisplayFormat::NAME_AND_EXT:
+ SetFormat(FF_NAME);
+ break;
+ case text::TemplateDisplayFormat::AREA :
+ SetFormat(FF_UI_RANGE);
+ break;
+ case text::TemplateDisplayFormat::TITLE :
+ SetFormat(FF_UI_NAME);
+ break;
+ default: SetFormat(FF_PATHNAME);
+ }
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+SwDocStatFieldType::SwDocStatFieldType(SwDoc& rDocument)
+ : SwFieldType(SwFieldIds::DocStat)
+ , m_rDoc(rDocument)
+ , m_nNumberingType(SVX_NUM_ARABIC)
+{
+}
+
+OUString SwDocStatFieldType::Expand(sal_uInt16 nSubType, SvxNumType nFormat) const
+{
+ sal_uInt32 nVal = 0;
+ const SwDocStat& rDStat = m_rDoc.getIDocumentStatistics().GetDocStat();
+ switch( nSubType )
+ {
+ case DS_TBL: nVal = rDStat.nTable; break;
+ case DS_GRF: nVal = rDStat.nGrf; break;
+ case DS_OLE: nVal = rDStat.nOLE; break;
+ case DS_PARA: nVal = rDStat.nPara; break;
+ case DS_WORD: nVal = rDStat.nWord; break;
+ case DS_CHAR: nVal = rDStat.nChar; break;
+ case DS_PAGE:
+ if( m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout() )
+ const_cast<SwDocStat &>(rDStat).nPage = m_rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetPageNum();
+ nVal = rDStat.nPage;
+ if( SVX_NUM_PAGEDESC == nFormat )
+ nFormat = m_nNumberingType;
+ break;
+ default:
+ OSL_FAIL( "SwDocStatFieldType::Expand: unknown SubType" );
+ }
+
+ if( nVal <= SHRT_MAX )
+ return FormatNumber( nVal, nFormat );
+
+ return OUString::number( nVal );
+}
+
+std::unique_ptr<SwFieldType> SwDocStatFieldType::Copy() const
+{
+ return std::make_unique<SwDocStatFieldType>(m_rDoc);
+}
+
+/**
+ * @param pTyp
+ * @param nSub SubType
+ * @param nFormat
+ */
+SwDocStatField::SwDocStatField(SwDocStatFieldType* pTyp, sal_uInt16 nSub, sal_uInt32 nFormat)
+ : SwField(pTyp, nFormat),
+ m_nSubType(nSub)
+{}
+
+OUString SwDocStatField::ExpandImpl(SwRootFrame const*const) const
+{
+ return static_cast<SwDocStatFieldType*>(GetTyp())->Expand(m_nSubType, static_cast<SvxNumType>(GetFormat()));
+}
+
+std::unique_ptr<SwField> SwDocStatField::Copy() const
+{
+ return std::make_unique<SwDocStatField>(
+ static_cast<SwDocStatFieldType*>(GetTyp()), m_nSubType, GetFormat() );
+}
+
+sal_uInt16 SwDocStatField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwDocStatField::SetSubType(sal_uInt16 nSub)
+{
+ m_nSubType = nSub;
+}
+
+void SwDocStatField::ChangeExpansion( const SwFrame* pFrame )
+{
+ if( DS_PAGE == m_nSubType && SVX_NUM_PAGEDESC == GetFormat() )
+ static_cast<SwDocStatFieldType*>(GetTyp())->SetNumFormat(
+ pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType() );
+}
+
+bool SwDocStatField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_USHORT2:
+ rAny <<= static_cast<sal_Int16>(GetFormat());
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwDocStatField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = false;
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_USHORT2:
+ {
+ sal_Int16 nSet = 0;
+ rAny >>= nSet;
+ if(nSet <= SVX_NUM_CHARS_LOWER_LETTER_N &&
+ nSet != SVX_NUM_CHAR_SPECIAL &&
+ nSet != SVX_NUM_BITMAP)
+ {
+ SetFormat(nSet);
+ bRet = true;
+ }
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ return bRet;
+}
+
+// Document info field type
+
+SwDocInfoFieldType::SwDocInfoFieldType(SwDoc* pDc)
+ : SwValueFieldType( pDc, SwFieldIds::DocInfo )
+{
+}
+
+std::unique_ptr<SwFieldType> SwDocInfoFieldType::Copy() const
+{
+ return std::make_unique<SwDocInfoFieldType>(GetDoc());
+}
+
+static void lcl_GetLocalDataWrapper( LanguageType nLang,
+ const LocaleDataWrapper **ppAppLocalData,
+ const LocaleDataWrapper **ppLocalData )
+{
+ SvtSysLocale aLocale;
+ *ppAppLocalData = &aLocale.GetLocaleData();
+ *ppLocalData = *ppAppLocalData;
+ if( nLang != (*ppLocalData)->getLanguageTag().getLanguageType() )
+ *ppLocalData = new LocaleDataWrapper(LanguageTag( nLang ));
+}
+
+OUString SwDocInfoFieldType::Expand( sal_uInt16 nSub, sal_uInt32 nFormat,
+ LanguageType nLang, const OUString& rName ) const
+{
+ const LocaleDataWrapper *pAppLocalData = nullptr, *pLocalData = nullptr;
+ SwDocShell *pDocShell(GetDoc()->GetDocShell());
+ OSL_ENSURE(pDocShell, "no SwDocShell");
+ if (!pDocShell) { return OUString(); }
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ OSL_ENSURE(xDocProps.is(), "Doc has no DocumentProperties");
+
+ sal_uInt16 nExtSub = nSub & 0xff00;
+ nSub &= 0xff; // do not consider extended SubTypes
+
+ OUString aStr;
+ switch(nSub)
+ {
+ case DI_TITLE: aStr = xDocProps->getTitle(); break;
+ case DI_SUBJECT:aStr = xDocProps->getSubject(); break;
+ case DI_KEYS: aStr = ::comphelper::string::convertCommaSeparated(
+ xDocProps->getKeywords());
+ break;
+ case DI_COMMENT:aStr = xDocProps->getDescription(); break;
+ case DI_DOCNO: aStr = OUString::number(
+ xDocProps->getEditingCycles() );
+ break;
+ case DI_EDIT:
+ if ( !nFormat )
+ {
+ lcl_GetLocalDataWrapper( nLang, &pAppLocalData, &pLocalData );
+ sal_Int32 dur = xDocProps->getEditingDuration();
+ // If Seconds > 0 then bSec should be TRUE otherwise Seconds
+ // information will be lost if file has EditTime in Seconds format.
+ aStr = pLocalData->getTime( tools::Time(dur/3600, (dur%3600)/60, dur%60),
+ dur%60 > 0);
+ }
+ else
+ {
+ sal_Int32 dur = xDocProps->getEditingDuration();
+ double fVal = tools::Time(dur/3600, (dur%3600)/60, dur%60).GetTimeInDays();
+ aStr = ExpandValue(fVal, nFormat, nLang);
+ }
+ break;
+ case DI_CUSTOM:
+ {
+ OUString sVal;
+ try
+ {
+ uno::Any aAny;
+ uno::Reference < beans::XPropertySet > xSet(
+ xDocProps->getUserDefinedProperties(),
+ uno::UNO_QUERY_THROW);
+ aAny = xSet->getPropertyValue( rName );
+
+ uno::Reference < script::XTypeConverter > xConverter( script::Converter::create(comphelper::getProcessComponentContext()) );
+ uno::Any aNew = xConverter->convertToSimpleType( aAny, uno::TypeClass_STRING );
+ aNew >>= sVal;
+ }
+ catch (uno::Exception&) {}
+ return sVal;
+ }
+
+ default:
+ {
+ OUString aName( xDocProps->getAuthor() );
+ util::DateTime uDT( xDocProps->getCreationDate() );
+ DateTime aDate(uDT);
+ if( nSub == DI_CREATE )
+ ; // that's it !!
+ else if( nSub == DI_CHANGE )
+ {
+ aName = xDocProps->getModifiedBy();
+ uDT = xDocProps->getModificationDate();
+ aDate = DateTime(uDT);
+ }
+ else if( nSub == DI_PRINT )
+ {
+ aName = xDocProps->getPrintedBy();
+ uDT = xDocProps->getPrintDate();
+ aDate = DateTime(uDT);
+ }
+ else
+ break;
+
+ if (aDate.IsValidAndGregorian())
+ {
+ switch (nExtSub & ~DI_SUB_FIXED)
+ {
+ case DI_SUB_AUTHOR:
+ aStr = aName;
+ break;
+
+ case DI_SUB_TIME:
+ if (!nFormat)
+ {
+ lcl_GetLocalDataWrapper( nLang, &pAppLocalData,
+ &pLocalData );
+ aStr = pLocalData->getTime( aDate,
+ false);
+ }
+ else
+ {
+ // start the number formatter
+ double fVal = SwDateTimeField::GetDateTime( *GetDoc(),
+ aDate);
+ aStr = ExpandValue(fVal, nFormat, nLang);
+ }
+ break;
+
+ case DI_SUB_DATE:
+ if (!nFormat)
+ {
+ lcl_GetLocalDataWrapper( nLang, &pAppLocalData,
+ &pLocalData );
+ aStr = pLocalData->getDate( aDate );
+ }
+ else
+ {
+ // start the number formatter
+ double fVal = SwDateTimeField::GetDateTime( *GetDoc(),
+ aDate);
+ aStr = ExpandValue(fVal, nFormat, nLang);
+ }
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ if( pAppLocalData != pLocalData )
+ delete pLocalData;
+
+ return aStr;
+}
+
+// document info field
+
+SwDocInfoField::SwDocInfoField(SwDocInfoFieldType* pTyp, sal_uInt16 nSub, const OUString& rName, sal_uInt32 nFormat) :
+ SwValueField(pTyp, nFormat), m_nSubType(nSub)
+{
+ m_aName = rName;
+ m_aContent = static_cast<SwDocInfoFieldType*>(GetTyp())->Expand(m_nSubType, nFormat, GetLanguage(), m_aName);
+}
+
+SwDocInfoField::SwDocInfoField(SwDocInfoFieldType* pTyp, sal_uInt16 nSub, const OUString& rName, const OUString& rValue, sal_uInt32 nFormat) :
+ SwValueField(pTyp, nFormat), m_nSubType(nSub)
+{
+ m_aName = rName;
+ m_aContent = rValue;
+}
+
+template<class T>
+static double lcl_TimeToDouble( const T& rTime )
+{
+ const double fNanoSecondsPerDay = 86400000000000.0;
+ return ( (rTime.Hours * SAL_CONST_INT64(3600000000000))
+ + (rTime.Minutes * SAL_CONST_INT64( 60000000000))
+ + (rTime.Seconds * SAL_CONST_INT64( 1000000000))
+ + (rTime.NanoSeconds))
+ / fNanoSecondsPerDay;
+}
+
+template<class D>
+static double lcl_DateToDouble( const D& rDate, const Date& rNullDate )
+{
+ tools::Long nDate = Date::DateToDays( rDate.Day, rDate.Month, rDate.Year );
+ tools::Long nNullDate = Date::DateToDays( rNullDate.GetDay(), rNullDate.GetMonth(), rNullDate.GetYear() );
+ return double( nDate - nNullDate );
+}
+
+OUString SwDocInfoField::ExpandImpl(SwRootFrame const*const) const
+{
+ if ( ( m_nSubType & 0xFF ) == DI_CUSTOM )
+ {
+ // custom properties currently need special treatment
+ // We don't have a secure way to detect "real" custom properties in Word import of text
+ // fields, so we treat *every* unknown property as a custom property, even the "built-in"
+ // section in Word's document summary information stream as these properties have not been
+ // inserted when the document summary information was imported, we do it here.
+ // This approach is still a lot better than the old one to import such fields as
+ // "user fields" and simple text
+ SwDocShell* pDocShell = GetDoc()->GetDocShell();
+ if( !pDocShell )
+ return m_aContent;
+ try
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS( pDocShell->GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps( xDPS->getDocumentProperties());
+ uno::Reference < beans::XPropertySet > xSet( xDocProps->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
+ uno::Reference < beans::XPropertySetInfo > xSetInfo = xSet->getPropertySetInfo();
+
+ uno::Any aAny;
+ if( xSetInfo->hasPropertyByName( m_aName ) )
+ aAny = xSet->getPropertyValue( m_aName );
+ if ( aAny.getValueType() != cppu::UnoType<void>::get() )
+ {
+ // "void" type means that the property has not been inserted until now
+ if ( !IsFixed() )
+ {
+ // if the field is "fixed" we don't update it from the property
+ OUString sVal;
+ uno::Reference < script::XTypeConverter > xConverter( script::Converter::create(comphelper::getProcessComponentContext()) );
+ util::Date aDate;
+ util::DateTime aDateTime;
+ util::Duration aDuration;
+ if( aAny >>= aDate)
+ {
+ SvNumberFormatter* pFormatter = pDocShell->GetDoc()->GetNumberFormatter();
+ const Date& rNullDate = pFormatter->GetNullDate();
+ sVal = ExpandValue( lcl_DateToDouble<util::Date>( aDate, rNullDate ), GetFormat(), GetLanguage());
+ }
+ else if( aAny >>= aDateTime )
+ {
+ double fDateTime = lcl_TimeToDouble<util::DateTime>( aDateTime );
+ SvNumberFormatter* pFormatter = pDocShell->GetDoc()->GetNumberFormatter();
+ const Date& rNullDate = pFormatter->GetNullDate();
+ fDateTime += lcl_DateToDouble<util::DateTime>( aDateTime, rNullDate );
+ sVal = ExpandValue( fDateTime, GetFormat(), GetLanguage());
+ }
+ else if( aAny >>= aDuration )
+ {
+ sVal = OUStringChar(aDuration.Negative ? '-' : '+')
+ + SwViewShell::GetShellRes()->sDurationFormat;
+ sVal = sVal.replaceFirst("%1", OUString::number( aDuration.Years ) );
+ sVal = sVal.replaceFirst("%2", OUString::number( aDuration.Months ) );
+ sVal = sVal.replaceFirst("%3", OUString::number( aDuration.Days ) );
+ sVal = sVal.replaceFirst("%4", OUString::number( aDuration.Hours ) );
+ sVal = sVal.replaceFirst("%5", OUString::number( aDuration.Minutes) );
+ sVal = sVal.replaceFirst("%6", OUString::number( aDuration.Seconds) );
+ }
+ else
+ {
+ uno::Any aNew = xConverter->convertToSimpleType( aAny, uno::TypeClass_STRING );
+ aNew >>= sVal;
+ }
+ const_cast<SwDocInfoField*>(this)->m_aContent = sVal;
+ }
+ }
+ }
+ catch (uno::Exception&) {}
+ }
+ else if ( !IsFixed() )
+ const_cast<SwDocInfoField*>(this)->m_aContent = static_cast<SwDocInfoFieldType*>(GetTyp())->Expand(m_nSubType, GetFormat(), GetLanguage(), m_aName);
+
+ return m_aContent;
+}
+
+OUString SwDocInfoField::GetFieldName() const
+{
+ OUString aStr(SwFieldType::GetTypeStr(GetTypeId()) + ":");
+
+ sal_uInt16 const nSub = m_nSubType & 0xff;
+
+ switch (nSub)
+ {
+ case DI_CUSTOM:
+ aStr += m_aName;
+ break;
+
+ default:
+ aStr += SwViewShell::GetShellRes()
+ ->aDocInfoLst[ nSub - DI_SUBTYPE_BEGIN ];
+ break;
+ }
+ if (IsFixed())
+ {
+ aStr += " " + SwViewShell::GetShellRes()->aFixedStr;
+ }
+ return aStr;
+}
+
+std::unique_ptr<SwField> SwDocInfoField::Copy() const
+{
+ std::unique_ptr<SwDocInfoField> pField(new SwDocInfoField(static_cast<SwDocInfoFieldType*>(GetTyp()), m_nSubType, m_aName, GetFormat()));
+ pField->SetAutomaticLanguage(IsAutomaticLanguage());
+ pField->m_aContent = m_aContent;
+
+ return std::unique_ptr<SwField>(pField.release());
+}
+
+sal_uInt16 SwDocInfoField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwDocInfoField::SetSubType(sal_uInt16 nSub)
+{
+ m_nSubType = nSub;
+}
+
+void SwDocInfoField::SetLanguage(LanguageType nLng)
+{
+ if (!GetFormat())
+ SwField::SetLanguage(nLng);
+ else
+ SwValueField::SetLanguage(nLng);
+}
+
+bool SwDocInfoField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aContent;
+ break;
+
+ case FIELD_PROP_PAR4:
+ rAny <<= m_aName;
+ break;
+
+ case FIELD_PROP_USHORT1:
+ rAny <<= static_cast<sal_Int16>(m_aContent.toInt32());
+ break;
+
+ case FIELD_PROP_BOOL1:
+ rAny <<= 0 != (m_nSubType & DI_SUB_FIXED);
+ break;
+
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int32>(GetFormat());
+ break;
+
+ case FIELD_PROP_DOUBLE:
+ {
+ double fVal = GetValue();
+ rAny <<= fVal;
+ }
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= ExpandImpl(nullptr);
+ break;
+ case FIELD_PROP_BOOL2:
+ {
+ sal_uInt16 nExtSub = (m_nSubType & 0xff00) & ~DI_SUB_FIXED;
+ rAny <<= nExtSub == DI_SUB_DATE;
+ }
+ break;
+ default:
+ return SwField::QueryValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+bool SwDocInfoField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ sal_Int32 nValue = 0;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ if( m_nSubType & DI_SUB_FIXED )
+ rAny >>= m_aContent;
+ break;
+
+ case FIELD_PROP_USHORT1:
+ if( m_nSubType & DI_SUB_FIXED )
+ {
+ rAny >>= nValue;
+ m_aContent = OUString::number(nValue);
+ }
+ break;
+
+ case FIELD_PROP_BOOL1:
+ if(*o3tl::doAccess<bool>(rAny))
+ m_nSubType |= DI_SUB_FIXED;
+ else
+ m_nSubType &= ~DI_SUB_FIXED;
+ break;
+ case FIELD_PROP_FORMAT:
+ {
+ rAny >>= nValue;
+ if( nValue >= 0)
+ SetFormat(nValue);
+ }
+ break;
+
+ case FIELD_PROP_PAR3:
+ rAny >>= m_aContent;
+ break;
+ case FIELD_PROP_BOOL2:
+ m_nSubType &= 0xf0ff;
+ if(*o3tl::doAccess<bool>(rAny))
+ m_nSubType |= DI_SUB_DATE;
+ else
+ m_nSubType |= DI_SUB_TIME;
+ break;
+ default:
+ return SwField::PutValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+SwHiddenTextFieldType::SwHiddenTextFieldType( bool bSetHidden )
+ : SwFieldType( SwFieldIds::HiddenText ), m_bHidden( bSetHidden )
+{
+}
+
+std::unique_ptr<SwFieldType> SwHiddenTextFieldType::Copy() const
+{
+ return std::make_unique<SwHiddenTextFieldType>( m_bHidden );
+}
+
+void SwHiddenTextFieldType::SetHiddenFlag( bool bSetHidden )
+{
+ if( m_bHidden != bSetHidden )
+ {
+ m_bHidden = bSetHidden;
+ UpdateFields(); // notify all HiddenTexts
+ }
+}
+
+SwHiddenTextField::SwHiddenTextField( SwHiddenTextFieldType* pFieldType,
+ bool bConditional,
+ const OUString& rCond,
+ const OUString& rStr,
+ bool bHidden,
+ SwFieldTypesEnum nSub) :
+ SwField( pFieldType ), m_aCond(rCond), m_nSubType(nSub),
+ m_bCanToggle(bConditional), m_bIsHidden(bHidden), m_bValid(false)
+{
+ if(m_nSubType == SwFieldTypesEnum::ConditionalText)
+ {
+ sal_Int32 nPos = 0;
+ m_aTRUEText = rStr.getToken(0, '|', nPos);
+
+ if(nPos != -1)
+ {
+ m_aFALSEText = rStr.getToken(0, '|', nPos);
+ if(nPos != -1)
+ {
+ m_aContent = rStr.getToken(0, '|', nPos);
+ m_bValid = true;
+ }
+ }
+ }
+ else
+ m_aTRUEText = rStr;
+}
+
+SwHiddenTextField::SwHiddenTextField( SwHiddenTextFieldType* pFieldType,
+ const OUString& rCond,
+ const OUString& rTrue,
+ const OUString& rFalse,
+ SwFieldTypesEnum nSub)
+ : SwField( pFieldType ), m_aTRUEText(rTrue), m_aFALSEText(rFalse), m_aCond(rCond), m_nSubType(nSub),
+ m_bIsHidden(true), m_bValid(false)
+{
+ m_bCanToggle = !m_aCond.isEmpty();
+}
+
+OUString SwHiddenTextField::ExpandImpl(SwRootFrame const*const) const
+{
+ // Type: !Hidden -> show always
+ // Hide -> evaluate condition
+
+ if( SwFieldTypesEnum::ConditionalText == m_nSubType )
+ {
+ if( m_bValid )
+ return m_aContent;
+
+ if( m_bCanToggle && !m_bIsHidden )
+ return m_aTRUEText;
+ }
+ else if( !static_cast<SwHiddenTextFieldType*>(GetTyp())->GetHiddenFlag() ||
+ ( m_bCanToggle && m_bIsHidden ))
+ return m_aTRUEText;
+
+ return m_aFALSEText;
+}
+
+/// get current field value and cache it
+void SwHiddenTextField::Evaluate(SwDoc& rDoc)
+{
+ if( SwFieldTypesEnum::ConditionalText != m_nSubType )
+ return;
+
+#if !HAVE_FEATURE_DBCONNECTIVITY || ENABLE_FUZZERS
+ (void) rDoc;
+#else
+ SwDBManager* pMgr = rDoc.GetDBManager();
+#endif
+ m_bValid = false;
+ OUString sTmpName = (m_bCanToggle && !m_bIsHidden) ? m_aTRUEText : m_aFALSEText;
+
+ // Database expressions need to be different from normal text. Therefore, normal text is set
+ // in quotes. If the latter exist they will be removed. If not, check if potential DB name.
+ // Only if there are two or more dots and no quotes, we assume a database.
+ if (sTmpName.getLength()>1 &&
+ sTmpName.startsWith("\"") &&
+ sTmpName.endsWith("\""))
+ {
+ m_aContent = sTmpName.copy(1, sTmpName.getLength() - 2);
+ m_bValid = true;
+ }
+ else if(sTmpName.indexOf('\"')<0 &&
+ comphelper::string::getTokenCount(sTmpName, '.') > 2)
+ {
+ sTmpName = ::ReplacePoint(sTmpName);
+ if(sTmpName.startsWith("[") && sTmpName.endsWith("]"))
+ { // remove brackets
+ sTmpName = sTmpName.copy(1, sTmpName.getLength() - 2);
+ }
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ if( pMgr)
+ {
+ sal_Int32 nIdx{ 0 };
+ OUString sDBName( GetDBName( sTmpName, rDoc ));
+ OUString sDataSource(sDBName.getToken(0, DB_DELIM, nIdx));
+ OUString sDataTableOrQuery(sDBName.getToken(0, DB_DELIM, nIdx));
+ if( pMgr->IsInMerge() && !sDBName.isEmpty() &&
+ pMgr->IsDataSourceOpen( sDataSource,
+ sDataTableOrQuery, false))
+ {
+ double fNumber;
+ pMgr->GetMergeColumnCnt(GetColumnName( sTmpName ),
+ GetLanguage(), m_aContent, &fNumber );
+ m_bValid = true;
+ }
+ else if( !sDBName.isEmpty() && !sDataSource.isEmpty() &&
+ !sDataTableOrQuery.isEmpty() )
+ m_bValid = true;
+ }
+#endif
+ }
+}
+
+OUString SwHiddenTextField::GetFieldName() const
+{
+ OUString aStr = SwFieldType::GetTypeStr(m_nSubType) +
+ " " + m_aCond + " " + m_aTRUEText;
+
+ if (m_nSubType == SwFieldTypesEnum::ConditionalText)
+ {
+ aStr += " : " + m_aFALSEText;
+ }
+ return aStr;
+}
+
+std::unique_ptr<SwField> SwHiddenTextField::Copy() const
+{
+ std::unique_ptr<SwHiddenTextField> pField(
+ new SwHiddenTextField(static_cast<SwHiddenTextFieldType*>(GetTyp()), m_aCond,
+ m_aTRUEText, m_aFALSEText));
+ pField->m_bIsHidden = m_bIsHidden;
+ pField->m_bValid = m_bValid;
+ pField->m_aContent = m_aContent;
+ pField->SetFormat(GetFormat());
+ pField->m_nSubType = m_nSubType;
+ return std::unique_ptr<SwField>(pField.release());
+}
+
+/// set condition
+void SwHiddenTextField::SetPar1(const OUString& rStr)
+{
+ m_aCond = rStr;
+ m_bCanToggle = !m_aCond.isEmpty();
+}
+
+OUString SwHiddenTextField::GetPar1() const
+{
+ return m_aCond;
+}
+
+/// set True/False text
+void SwHiddenTextField::SetPar2(const OUString& rStr)
+{
+ if (m_nSubType == SwFieldTypesEnum::ConditionalText)
+ {
+ sal_Int32 nPos = rStr.indexOf('|');
+ if (nPos == -1)
+ m_aTRUEText = rStr;
+ else
+ {
+ m_aTRUEText = rStr.copy(0, nPos);
+ m_aFALSEText = rStr.copy(nPos + 1);
+ }
+ }
+ else
+ m_aTRUEText = rStr;
+}
+
+/// get True/False text
+OUString SwHiddenTextField::GetPar2() const
+{
+ if(m_nSubType != SwFieldTypesEnum::ConditionalText)
+ {
+ return m_aTRUEText;
+ }
+ return m_aTRUEText + "|" + m_aFALSEText;
+}
+
+sal_uInt16 SwHiddenTextField::GetSubType() const
+{
+ return static_cast<sal_uInt16>(m_nSubType);
+}
+
+bool SwHiddenTextField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aCond;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_aTRUEText;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= m_aFALSEText;
+ break;
+ case FIELD_PROP_PAR4 :
+ rAny <<= m_aContent;
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= m_bIsHidden;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwHiddenTextField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ {
+ OUString sVal;
+ rAny >>= sVal;
+ SetPar1(sVal);
+ }
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_aTRUEText;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny >>= m_aFALSEText;
+ break;
+ case FIELD_PROP_BOOL1:
+ m_bIsHidden = *o3tl::doAccess<bool>(rAny);
+ break;
+ case FIELD_PROP_PAR4:
+ rAny >>= m_aContent;
+ m_bValid = true;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+OUString SwHiddenTextField::GetColumnName(const OUString& rName)
+{
+ sal_Int32 nPos = rName.indexOf(DB_DELIM);
+ if( nPos>=0 )
+ {
+ nPos = rName.indexOf(DB_DELIM, nPos + 1);
+
+ if( nPos>=0 )
+ return rName.copy(nPos + 1);
+ }
+ return rName;
+}
+
+OUString SwHiddenTextField::GetDBName(std::u16string_view rName, SwDoc& rDoc)
+{
+ size_t nPos = rName.find(DB_DELIM);
+ if( nPos != std::u16string_view::npos )
+ {
+ nPos = rName.find(DB_DELIM, nPos + 1);
+
+ if( nPos != std::u16string_view::npos )
+ return OUString(rName.substr(0, nPos));
+ }
+
+ SwDBData aData = rDoc.GetDBData();
+ return aData.sDataSource + OUStringChar(DB_DELIM) + aData.sCommand;
+}
+
+// [aFieldDefinition] value sample : " IF A == B \"TrueText\" \"FalseText\""
+void SwHiddenTextField::ParseIfFieldDefinition(const OUString& aFieldDefinition,
+ OUString& rCondition,
+ OUString& rTrue,
+ OUString& rFalse)
+{
+ // get all positions inside the input string where words are started
+ //
+ // In: " IF A == B \"TrueText\" \"FalseText\""
+ // 0 1 2 3
+ // 01234567890 123456789 01 2345678901 2
+ //
+ // result:
+ // [1, 4, 6, 9, 11, 22]
+ std::vector<sal_Int32> wordPosition;
+ {
+ bool quoted = false;
+ bool insideWord = false;
+ for (sal_Int32 i = 0; i < aFieldDefinition.getLength(); i++)
+ {
+ if (quoted)
+ {
+ if (aFieldDefinition[i] == '\"')
+ {
+ quoted = false;
+ insideWord = false;
+ }
+ }
+ else
+ {
+ if (aFieldDefinition[i] == ' ')
+ {
+ // word delimiter
+ insideWord = false;
+ }
+ else
+ {
+ if (insideWord)
+ {
+ quoted = (aFieldDefinition[i] == '\"');
+ }
+ else
+ {
+ insideWord = true;
+ wordPosition.push_back(i);
+ quoted = (aFieldDefinition[i] == '\"');
+ }
+ }
+ }
+ }
+ }
+
+ // first word is always "IF"
+ // last two words are: true-case and false-case,
+ // everything before is treated as condition expression
+ // => we need at least 4 words to be inside the input string
+ if (wordPosition.size() < 4)
+ {
+ return;
+ }
+
+
+ const sal_Int32 conditionBegin = wordPosition[1];
+ const sal_Int32 trueBegin = wordPosition[wordPosition.size() - 2];
+ const sal_Int32 falseBegin = wordPosition[wordPosition.size() - 1];
+
+ const sal_Int32 conditionLength = trueBegin - conditionBegin;
+ const sal_Int32 trueLength = falseBegin - trueBegin;
+
+ // Syntax
+ // OUString::copy( sal_Int32 beginIndex, sal_Int32 count )
+ rCondition = aFieldDefinition.copy(conditionBegin, conditionLength);
+ rTrue = aFieldDefinition.copy(trueBegin, trueLength);
+ rFalse = aFieldDefinition.copy(falseBegin);
+
+ // trim
+ rCondition = rCondition.trim();
+ rTrue = rTrue.trim();
+ rFalse = rFalse.trim();
+
+ // remove quotes
+ if (rCondition.getLength() >= 2)
+ {
+ if (rCondition[0] == '\"' && rCondition[rCondition.getLength() - 1] == '\"')
+ rCondition = rCondition.copy(1, rCondition.getLength() - 2);
+ }
+ if (rTrue.getLength() >= 2)
+ {
+ if (rTrue[0] == '\"' && rTrue[rTrue.getLength() - 1] == '\"')
+ rTrue = rTrue.copy(1, rTrue.getLength() - 2);
+ }
+ if (rFalse.getLength() >= 2)
+ {
+ if (rFalse[0] == '\"' && rFalse[rFalse.getLength() - 1] == '\"')
+ rFalse = rFalse.copy(1, rFalse.getLength() - 2);
+ }
+
+ // Note: do not make trim once again, while this is a user defined data
+}
+
+// field type for line height 0
+
+SwHiddenParaFieldType::SwHiddenParaFieldType()
+ : SwFieldType( SwFieldIds::HiddenPara )
+{
+}
+
+std::unique_ptr<SwFieldType> SwHiddenParaFieldType::Copy() const
+{
+ return std::make_unique<SwHiddenParaFieldType>();
+}
+
+// field for line height 0
+
+SwHiddenParaField::SwHiddenParaField(SwHiddenParaFieldType* pTyp, const OUString& rStr)
+ : SwField(pTyp), m_aCond(rStr)
+{
+ m_bIsHidden = false;
+}
+
+OUString SwHiddenParaField::ExpandImpl(SwRootFrame const*const) const
+{
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwHiddenParaField::Copy() const
+{
+ std::unique_ptr<SwHiddenParaField> pField(new SwHiddenParaField(static_cast<SwHiddenParaFieldType*>(GetTyp()), m_aCond));
+ pField->m_bIsHidden = m_bIsHidden;
+ return std::unique_ptr<SwField>(pField.release());
+}
+
+bool SwHiddenParaField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aCond;
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= m_bIsHidden;
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwHiddenParaField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= m_aCond;
+ break;
+ case FIELD_PROP_BOOL1:
+ m_bIsHidden = *o3tl::doAccess<bool>(rAny);
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+/// set condition
+void SwHiddenParaField::SetPar1(const OUString& rStr)
+{
+ m_aCond = rStr;
+}
+
+OUString SwHiddenParaField::GetPar1() const
+{
+ return m_aCond;
+}
+
+// PostIt field type
+
+SwPostItFieldType::SwPostItFieldType(SwDoc& rDoc)
+ : SwFieldType( SwFieldIds::Postit )
+ , mrDoc(rDoc)
+{}
+
+std::unique_ptr<SwFieldType> SwPostItFieldType::Copy() const
+{
+ return std::make_unique<SwPostItFieldType>(mrDoc);
+}
+
+// PostIt field
+
+sal_uInt32 SwPostItField::s_nLastPostItId = 1;
+
+SwPostItField::SwPostItField( SwPostItFieldType* pT,
+ const OUString& rAuthor,
+ const OUString& rText,
+ const OUString& rInitials,
+ const OUString& rName,
+ const DateTime& rDateTime,
+ const bool bResolved,
+ const sal_uInt32 nPostItId
+)
+ : SwField( pT )
+ , m_sText( rText )
+ , m_sAuthor( rAuthor )
+ , m_sInitials( rInitials )
+ , m_sName( rName )
+ , m_aDateTime( rDateTime )
+ , m_bResolved( bResolved )
+{
+ m_nPostItId = nPostItId == 0 ? s_nLastPostItId++ : nPostItId;
+}
+
+SwPostItField::~SwPostItField()
+{
+ if ( m_xTextObject.is() )
+ {
+ m_xTextObject->DisposeEditSource();
+ }
+
+ mpText.reset();
+}
+
+OUString SwPostItField::ExpandImpl(SwRootFrame const*const) const
+{
+ return OUString();
+}
+
+OUString SwPostItField::GetDescription() const
+{
+ return SwResId(STR_NOTE);
+}
+
+void SwPostItField::SetResolved(bool bNewState)
+{
+ m_bResolved = bNewState;
+}
+
+void SwPostItField::ToggleResolved()
+{
+ m_bResolved = !m_bResolved;
+}
+
+bool SwPostItField::GetResolved() const
+{
+ return m_bResolved;
+}
+
+std::unique_ptr<SwField> SwPostItField::Copy() const
+{
+ std::unique_ptr<SwPostItField> pRet(new SwPostItField( static_cast<SwPostItFieldType*>(GetTyp()), m_sAuthor, m_sText, m_sInitials, m_sName,
+ m_aDateTime, m_bResolved, m_nPostItId));
+ if (mpText)
+ pRet->SetTextObject( *mpText );
+
+ // Note: member <m_xTextObject> not copied.
+
+ return std::unique_ptr<SwField>(pRet.release());
+}
+
+/// set author
+void SwPostItField::SetPar1(const OUString& rStr)
+{
+ m_sAuthor = rStr;
+}
+
+/// get author
+OUString SwPostItField::GetPar1() const
+{
+ return m_sAuthor;
+}
+
+/// set the PostIt's text
+void SwPostItField::SetPar2(const OUString& rStr)
+{
+ m_sText = rStr;
+}
+
+/// get the PostIt's text
+OUString SwPostItField::GetPar2() const
+{
+ return m_sText;
+}
+
+
+void SwPostItField::SetName(const OUString& rName)
+{
+ m_sName = rName;
+}
+
+
+void SwPostItField::SetTextObject( std::optional<OutlinerParaObject> pText )
+{
+ mpText = std::move(pText);
+}
+
+sal_Int32 SwPostItField::GetNumberOfParagraphs() const
+{
+ return mpText ? mpText->Count() : 1;
+}
+
+void SwPostItField::SetPostItId(const sal_uInt32 nPostItId)
+{
+ m_nPostItId = nPostItId == 0 ? s_nLastPostItId++ : nPostItId;
+}
+
+bool SwPostItField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_sAuthor;
+ break;
+ case FIELD_PROP_PAR2:
+ {
+ rAny <<= m_sText;
+ break;
+ }
+ case FIELD_PROP_PAR3:
+ rAny <<= m_sInitials;
+ break;
+ case FIELD_PROP_PAR4:
+ rAny <<= m_sName;
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= m_bResolved;
+ break;
+ case FIELD_PROP_TEXT:
+ {
+ if ( !m_xTextObject.is() )
+ {
+ SwPostItFieldType* pGetType = static_cast<SwPostItFieldType*>(GetTyp());
+ SwDoc& rDoc = pGetType->GetDoc();
+ auto pObj = std::make_unique<SwTextAPIEditSource>( &rDoc );
+ const_cast <SwPostItField*> (this)->m_xTextObject = new SwTextAPIObject( std::move(pObj) );
+ }
+
+ if ( mpText )
+ m_xTextObject->SetText( *mpText );
+ else
+ m_xTextObject->SetString( m_sText );
+
+ uno::Reference < text::XText > xText( m_xTextObject );
+ rAny <<= xText;
+ break;
+ }
+ case FIELD_PROP_DATE:
+ {
+ rAny <<= m_aDateTime.GetUNODate();
+ }
+ break;
+ case FIELD_PROP_DATE_TIME:
+ {
+ rAny <<= m_aDateTime.GetUNODateTime();
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwPostItField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= m_sAuthor;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_sText;
+ //#i100374# new string via api, delete complex text object so SwPostItNote picks up the new string
+ mpText.reset();
+ break;
+ case FIELD_PROP_PAR3:
+ rAny >>= m_sInitials;
+ break;
+ case FIELD_PROP_PAR4:
+ rAny >>= m_sName;
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny >>= m_bResolved;
+ break;
+ case FIELD_PROP_TEXT:
+ OSL_FAIL("Not implemented!");
+ break;
+ case FIELD_PROP_DATE:
+ if( auto aSetDate = o3tl::tryAccess<util::Date>(rAny) )
+ {
+ m_aDateTime = Date(aSetDate->Day, aSetDate->Month, aSetDate->Year);
+ }
+ break;
+ case FIELD_PROP_DATE_TIME:
+ {
+ util::DateTime aDateTimeValue;
+ if(!(rAny >>= aDateTimeValue))
+ return false;
+ m_aDateTime = DateTime(aDateTimeValue);
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+void SwPostItField::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPostItField"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+
+ SwField::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mpText"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", mpText ? &*mpText : nullptr);
+ if (mpText)
+ mpText->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// extended user information field type
+
+SwExtUserFieldType::SwExtUserFieldType()
+ : SwFieldType( SwFieldIds::ExtUser )
+{
+}
+
+std::unique_ptr<SwFieldType> SwExtUserFieldType::Copy() const
+{
+ return std::make_unique<SwExtUserFieldType>();
+}
+
+OUString SwExtUserFieldType::Expand(sal_uInt16 nSub )
+{
+ UserOptToken nRet = static_cast<UserOptToken>(USHRT_MAX);
+ switch(nSub)
+ {
+ case EU_FIRSTNAME: nRet = UserOptToken::FirstName; break;
+ case EU_NAME: nRet = UserOptToken::LastName; break;
+ case EU_SHORTCUT: nRet = UserOptToken::ID; break;
+
+ case EU_COMPANY: nRet = UserOptToken::Company; break;
+ case EU_STREET: nRet = UserOptToken::Street; break;
+ case EU_TITLE: nRet = UserOptToken::Title; break;
+ case EU_POSITION: nRet = UserOptToken::Position; break;
+ case EU_PHONE_PRIVATE: nRet = UserOptToken::TelephoneHome; break;
+ case EU_PHONE_COMPANY: nRet = UserOptToken::TelephoneWork; break;
+ case EU_FAX: nRet = UserOptToken::Fax; break;
+ case EU_EMAIL: nRet = UserOptToken::Email; break;
+ case EU_COUNTRY: nRet = UserOptToken::Country; break;
+ case EU_ZIP: nRet = UserOptToken::Zip; break;
+ case EU_CITY: nRet = UserOptToken::City; break;
+ case EU_STATE: nRet = UserOptToken::State; break;
+ case EU_FATHERSNAME: nRet = UserOptToken::FathersName; break;
+ case EU_APARTMENT: nRet = UserOptToken::Apartment; break;
+ default: OSL_ENSURE( false, "Field unknown");
+ }
+ if( static_cast<UserOptToken>(USHRT_MAX) != nRet )
+ {
+ SvtUserOptions& rUserOpt = SW_MOD()->GetUserOptions();
+ return rUserOpt.GetToken( nRet );
+ }
+ return OUString();
+}
+
+// extended user information field
+
+SwExtUserField::SwExtUserField(SwExtUserFieldType* pTyp, sal_uInt16 nSubTyp, sal_uInt32 nFormat) :
+ SwField(pTyp, nFormat), m_nType(nSubTyp)
+{
+ m_aContent = SwExtUserFieldType::Expand(m_nType);
+}
+
+OUString SwExtUserField::ExpandImpl(SwRootFrame const*const) const
+{
+ if (!IsFixed())
+ const_cast<SwExtUserField*>(this)->m_aContent = SwExtUserFieldType::Expand(m_nType);
+
+ return m_aContent;
+}
+
+std::unique_ptr<SwField> SwExtUserField::Copy() const
+{
+ std::unique_ptr<SwExtUserField> pField(new SwExtUserField(static_cast<SwExtUserFieldType*>(GetTyp()), m_nType, GetFormat()));
+ pField->SetExpansion(m_aContent);
+
+ return std::unique_ptr<SwField>(pField.release());
+}
+
+sal_uInt16 SwExtUserField::GetSubType() const
+{
+ return m_nType;
+}
+
+void SwExtUserField::SetSubType(sal_uInt16 nSub)
+{
+ m_nType = nSub;
+}
+
+bool SwExtUserField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_aContent;
+ break;
+
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nTmp = m_nType;
+ rAny <<= nTmp;
+ }
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= IsFixed();
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwExtUserField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= m_aContent;
+ break;
+
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nTmp = 0;
+ rAny >>= nTmp;
+ m_nType = nTmp;
+ }
+ break;
+ case FIELD_PROP_BOOL1:
+ if( *o3tl::doAccess<bool>(rAny) )
+ SetFormat(GetFormat() | AF_FIXED);
+ else
+ SetFormat(GetFormat() & ~AF_FIXED);
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+// field type for relative page numbers
+
+SwRefPageSetFieldType::SwRefPageSetFieldType()
+ : SwFieldType( SwFieldIds::RefPageSet )
+{
+}
+
+std::unique_ptr<SwFieldType> SwRefPageSetFieldType::Copy() const
+{
+ return std::make_unique<SwRefPageSetFieldType>();
+}
+
+// overridden since there is nothing to update
+void SwRefPageSetFieldType::SwClientNotify(const SwModify&, const SfxHint&)
+{
+}
+
+// field for relative page numbers
+
+SwRefPageSetField::SwRefPageSetField( SwRefPageSetFieldType* pTyp,
+ short nOff, bool bFlag )
+ : SwField( pTyp ), m_nOffset( nOff ), m_bOn( bFlag )
+{
+}
+
+OUString SwRefPageSetField::ExpandImpl(SwRootFrame const*const) const
+{
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwRefPageSetField::Copy() const
+{
+ return std::make_unique<SwRefPageSetField>( static_cast<SwRefPageSetFieldType*>(GetTyp()), m_nOffset, m_bOn );
+}
+
+OUString SwRefPageSetField::GetPar2() const
+{
+ return OUString::number(GetOffset());
+}
+
+void SwRefPageSetField::SetPar2(const OUString& rStr)
+{
+ SetOffset( static_cast<short>(rStr.toInt32()) );
+}
+
+bool SwRefPageSetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ rAny <<= m_bOn;
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny <<= static_cast<sal_Int16>(m_nOffset);
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwRefPageSetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ m_bOn = *o3tl::doAccess<bool>(rAny);
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny >>=m_nOffset;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+// relative page numbers - query field
+
+SwRefPageGetFieldType::SwRefPageGetFieldType( SwDoc& rDc )
+ : SwFieldType( SwFieldIds::RefPageGet ), m_rDoc( rDc ), m_nNumberingType( SVX_NUM_ARABIC )
+{
+}
+
+std::unique_ptr<SwFieldType> SwRefPageGetFieldType::Copy() const
+{
+ std::unique_ptr<SwRefPageGetFieldType> pNew(new SwRefPageGetFieldType( m_rDoc ));
+ pNew->m_nNumberingType = m_nNumberingType;
+ return pNew;
+}
+
+void SwRefPageGetFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ auto const ModifyImpl = [this](SwRootFrame const*const pLayout)
+ {
+ // first collect all SetPageRefFields
+ SetGetExpFields aTmpLst;
+ if (MakeSetList(aTmpLst, pLayout))
+ {
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ UpdateField(pFormatField->GetTextField(), aTmpLst, pLayout);
+ }
+ };
+
+ // update all GetReference fields
+ if( !pLegacy->m_pNew && !pLegacy->m_pOld && HasWriterListeners() )
+ {
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame const* pLayoutRLHidden(nullptr);
+ for (SwRootFrame const*const pLay : m_rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ pLayoutRLHidden = pLay;
+ }
+ else
+ {
+ pLayout = pLay;
+ }
+ }
+ ModifyImpl(pLayout);
+ if (pLayoutRLHidden)
+ {
+ ModifyImpl(pLayoutRLHidden);
+ }
+ }
+
+ // forward to text fields, they "expand" the text
+ CallSwClientNotify(rHint);
+}
+
+bool SwRefPageGetFieldType::MakeSetList(SetGetExpFields& rTmpLst,
+ SwRootFrame const*const pLayout)
+{
+ IDocumentRedlineAccess const& rIDRA(m_rDoc.getIDocumentRedlineAccess());
+ std::vector<SwFormatField*> vFields;
+ m_rDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::RefPageSet)->GatherFields(vFields);
+ for(auto pFormatField: vFields)
+ {
+ // update only the GetRef fields
+ const SwTextField* pTField = pFormatField->GetTextField();
+ if (!pLayout || !pLayout->IsHideRedlines() || !sw::IsFieldDeletedInModel(rIDRA, *pTField))
+ {
+ const SwTextNode& rTextNd = pTField->GetTextNode();
+
+ // Always the first! (in Tab-Headline, header/footer )
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame *const pFrame = rTextNd.getLayoutFrame(
+ pLayout, nullptr, &tmp);
+
+ std::unique_ptr<SetGetExpField> pNew;
+
+ if( !pFrame ||
+ pFrame->IsInDocBody() ||
+ // #i31868#
+ // Check if pFrame is not yet connected to the layout.
+ !pFrame->FindPageFrame() )
+ {
+ // create index for determination of the TextNode
+ SwNodeIndex aIdx( rTextNd );
+ pNew.reset( new SetGetExpField( aIdx, pTField ) );
+ }
+ else
+ {
+ // create index for determination of the TextNode
+ SwPosition aPos( m_rDoc.GetNodes().GetEndOfPostIts() );
+ bool const bResult = GetBodyTextNode( m_rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the Field?");
+ pNew.reset( new SetGetExpField( aPos.nNode, pTField,
+ &aPos.nContent ) );
+ }
+
+ rTmpLst.insert( std::move(pNew) );
+ }
+ }
+ return !rTmpLst.empty();
+}
+
+void SwRefPageGetFieldType::UpdateField( SwTextField const * pTextField,
+ SetGetExpFields const & rSetList,
+ SwRootFrame const*const pLayout)
+{
+ SwRefPageGetField* pGetField = const_cast<SwRefPageGetField*>(static_cast<const SwRefPageGetField*>(pTextField->GetFormatField().GetField()));
+ pGetField->SetText( OUString(), pLayout );
+
+ // then search the correct RefPageSet field
+ SwTextNode* pTextNode = &pTextField->GetTextNode();
+ if( pTextNode->StartOfSectionIndex() >
+ m_rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ SwNodeIndex aIdx( *pTextNode );
+ SetGetExpField aEndField( aIdx, pTextField );
+
+ SetGetExpFields::const_iterator itLast = rSetList.lower_bound( &aEndField );
+
+ if( itLast != rSetList.begin() )
+ {
+ --itLast;
+ const SwTextField* pRefTextField = (*itLast)->GetTextField();
+ const SwRefPageSetField* pSetField =
+ static_cast<const SwRefPageSetField*>(pRefTextField->GetFormatField().GetField());
+ if( pSetField->IsOn() )
+ {
+ // determine the correct offset
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame *const pFrame = pTextNode->getLayoutFrame(
+ pLayout, nullptr, &tmp);
+ const SwContentFrame *const pRefFrame = pRefTextField->GetTextNode().getLayoutFrame(
+ pLayout, nullptr, &tmp);
+ const SwPageFrame* pPgFrame = nullptr;
+ short nDiff = 1;
+ if ( pFrame && pRefFrame )
+ {
+ pPgFrame = pFrame->FindPageFrame();
+ nDiff = pPgFrame->GetPhyPageNum() -
+ pRefFrame->FindPageFrame()->GetPhyPageNum() + 1;
+ }
+
+ SvxNumType nTmpFormat = SVX_NUM_PAGEDESC == static_cast<SvxNumType>(pGetField->GetFormat())
+ ? ( !pPgFrame
+ ? SVX_NUM_ARABIC
+ : pPgFrame->GetPageDesc()->GetNumType().GetNumberingType() )
+ : static_cast<SvxNumType>(pGetField->GetFormat());
+ const short nPageNum = std::max<short>(0, pSetField->GetOffset() + nDiff);
+ pGetField->SetText(FormatNumber(nPageNum, nTmpFormat), pLayout);
+ }
+ }
+ }
+ // start formatting
+ const_cast<SwFormatField&>(pTextField->GetFormatField()).UpdateTextNode(nullptr, nullptr);
+}
+
+// queries for relative page numbering
+
+SwRefPageGetField::SwRefPageGetField( SwRefPageGetFieldType* pTyp,
+ sal_uInt32 nFormat )
+ : SwField( pTyp, nFormat )
+{
+}
+
+void SwRefPageGetField::SetText(const OUString& rText,
+ SwRootFrame const*const pLayout)
+{
+ if (!pLayout || !pLayout->IsHideRedlines())
+ {
+ m_sText = rText;
+ }
+ if (!pLayout || pLayout->IsHideRedlines())
+ {
+ m_sTextRLHidden = rText;
+ }
+}
+
+OUString SwRefPageGetField::ExpandImpl(SwRootFrame const*const pLayout) const
+{
+ return pLayout && pLayout->IsHideRedlines() ? m_sTextRLHidden : m_sText;
+}
+
+std::unique_ptr<SwField> SwRefPageGetField::Copy() const
+{
+ std::unique_ptr<SwRefPageGetField> pCpy(new SwRefPageGetField(
+ static_cast<SwRefPageGetFieldType*>(GetTyp()), GetFormat() ));
+ pCpy->m_sText = m_sText;
+ pCpy->m_sTextRLHidden = m_sTextRLHidden;
+ return std::unique_ptr<SwField>(pCpy.release());
+}
+
+void SwRefPageGetField::ChangeExpansion(const SwFrame& rFrame,
+ const SwTextField* pField )
+{
+ // only fields in Footer, Header, FootNote, Flys
+ SwRefPageGetFieldType* pGetType = static_cast<SwRefPageGetFieldType*>(GetTyp());
+ SwDoc& rDoc = pGetType->GetDoc();
+ if( pField->GetTextNode().StartOfSectionIndex() >
+ rDoc.GetNodes().GetEndOfExtras().GetIndex() )
+ return;
+
+ SwRootFrame const& rLayout(*rFrame.getRootFrame());
+ OUString & rText(rLayout.IsHideRedlines() ? m_sTextRLHidden : m_sText);
+ rText.clear();
+
+ OSL_ENSURE(!rFrame.IsInDocBody(), "Flag incorrect, frame is in DocBody");
+
+ // collect all SetPageRefFields
+ SetGetExpFields aTmpLst;
+ if (!pGetType->MakeSetList(aTmpLst, &rLayout))
+ return ;
+
+ // create index for determination of the TextNode
+ SwPosition aPos( SwNodeIndex( rDoc.GetNodes() ) );
+ SwTextNode* pTextNode = const_cast<SwTextNode*>(GetBodyTextNode(rDoc, aPos, rFrame));
+
+ // If no layout exists, ChangeExpansion is called for header and
+ // footer lines via layout formatting without existing TextNode.
+ if(!pTextNode)
+ return;
+
+ SetGetExpField aEndField( aPos.nNode, pField, &aPos.nContent );
+
+ SetGetExpFields::const_iterator itLast = aTmpLst.lower_bound( &aEndField );
+
+ if( itLast == aTmpLst.begin() )
+ return; // there is no corresponding set-field in front
+ --itLast;
+
+ const SwTextField* pRefTextField = (*itLast)->GetTextField();
+ const SwRefPageSetField* pSetField =
+ static_cast<const SwRefPageSetField*>(pRefTextField->GetFormatField().GetField());
+ Point aPt;
+ std::pair<Point, bool> const tmp(aPt, false);
+ const SwContentFrame *const pRefFrame = pRefTextField->GetTextNode().getLayoutFrame(
+ &rLayout, nullptr, &tmp);
+ if( !(pSetField->IsOn() && pRefFrame) )
+ return;
+
+ // determine the correct offset
+ const SwPageFrame* pPgFrame = rFrame.FindPageFrame();
+ const short nDiff = pPgFrame->GetPhyPageNum() -
+ pRefFrame->FindPageFrame()->GetPhyPageNum() + 1;
+
+ SwRefPageGetField* pGetField = const_cast<SwRefPageGetField*>(static_cast<const SwRefPageGetField*>(pField->GetFormatField().GetField()));
+ SvxNumType nTmpFormat = SVX_NUM_PAGEDESC == pGetField->GetFormat()
+ ? pPgFrame->GetPageDesc()->GetNumType().GetNumberingType()
+ : static_cast<SvxNumType>(pGetField->GetFormat());
+ const short nPageNum = std::max<short>(0, pSetField->GetOffset() + nDiff);
+ pGetField->SetText(FormatNumber(nPageNum, nTmpFormat), &rLayout);
+}
+
+bool SwRefPageGetField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ rAny <<= static_cast<sal_Int16>(GetFormat());
+ break;
+ case FIELD_PROP_PAR1:
+ rAny <<= m_sText;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwRefPageGetField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nSet = 0;
+ rAny >>= nSet;
+ if(nSet <= SVX_NUM_PAGEDESC )
+ SetFormat(nSet);
+ }
+ break;
+ case FIELD_PROP_PAR1:
+ rAny >>= m_sText;
+ m_sTextRLHidden = m_sText;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+// field type to jump to and edit
+
+SwJumpEditFieldType::SwJumpEditFieldType( SwDoc& rD )
+ : SwFieldType( SwFieldIds::JumpEdit ), m_rDoc( rD ), m_aDep( *this )
+{
+}
+
+std::unique_ptr<SwFieldType> SwJumpEditFieldType::Copy() const
+{
+ return std::make_unique<SwJumpEditFieldType>( m_rDoc );
+}
+
+SwCharFormat* SwJumpEditFieldType::GetCharFormat()
+{
+ SwCharFormat* pFormat = m_rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( RES_POOLCHR_JUMPEDIT );
+ m_aDep.StartListening(pFormat);
+ return pFormat;
+}
+
+SwJumpEditField::SwJumpEditField( SwJumpEditFieldType* pTyp, sal_uInt32 nForm,
+ const OUString& rText, const OUString& rHelp )
+ : SwField( pTyp, nForm ), m_sText( rText ), m_sHelp( rHelp )
+{
+}
+
+OUString SwJumpEditField::ExpandImpl(SwRootFrame const*const) const
+{
+ return "<" + m_sText + ">";
+}
+
+std::unique_ptr<SwField> SwJumpEditField::Copy() const
+{
+ return std::make_unique<SwJumpEditField>( static_cast<SwJumpEditFieldType*>(GetTyp()), GetFormat(),
+ m_sText, m_sHelp );
+}
+
+/// get place holder text
+OUString SwJumpEditField::GetPar1() const
+{
+ return m_sText;
+}
+
+/// set place holder text
+void SwJumpEditField::SetPar1(const OUString& rStr)
+{
+ m_sText = rStr;
+}
+
+/// get hint text
+OUString SwJumpEditField::GetPar2() const
+{
+ return m_sHelp;
+}
+
+/// set hint text
+void SwJumpEditField::SetPar2(const OUString& rStr)
+{
+ m_sHelp = rStr;
+}
+
+bool SwJumpEditField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nRet;
+ switch( GetFormat() )
+ {
+ case JE_FMT_TABLE: nRet = text::PlaceholderType::TABLE; break;
+ case JE_FMT_FRAME: nRet = text::PlaceholderType::TEXTFRAME; break;
+ case JE_FMT_GRAPHIC:nRet = text::PlaceholderType::GRAPHIC; break;
+ case JE_FMT_OLE: nRet = text::PlaceholderType::OBJECT; break;
+ default:
+ nRet = text::PlaceholderType::TEXT; break;
+ }
+ rAny <<= nRet;
+ }
+ break;
+ case FIELD_PROP_PAR1 :
+ rAny <<= m_sHelp;
+ break;
+ case FIELD_PROP_PAR2 :
+ rAny <<= m_sText;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwJumpEditField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ {
+ //JP 24.10.2001: int32 because in UnoField.cxx a putvalue is
+ // called with a int32 value! But normally we need
+ // here only a int16
+ sal_Int32 nSet = 0;
+ rAny >>= nSet;
+ switch( nSet )
+ {
+ case text::PlaceholderType::TEXT : SetFormat(JE_FMT_TEXT); break;
+ case text::PlaceholderType::TABLE : SetFormat(JE_FMT_TABLE); break;
+ case text::PlaceholderType::TEXTFRAME: SetFormat(JE_FMT_FRAME); break;
+ case text::PlaceholderType::GRAPHIC : SetFormat(JE_FMT_GRAPHIC); break;
+ case text::PlaceholderType::OBJECT : SetFormat(JE_FMT_OLE); break;
+ }
+ }
+ break;
+ case FIELD_PROP_PAR1 :
+ rAny >>= m_sHelp;
+ break;
+ case FIELD_PROP_PAR2 :
+ rAny >>= m_sText;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+// combined character field type
+
+SwCombinedCharFieldType::SwCombinedCharFieldType()
+ : SwFieldType( SwFieldIds::CombinedChars )
+{
+}
+
+std::unique_ptr<SwFieldType> SwCombinedCharFieldType::Copy() const
+{
+ return std::make_unique<SwCombinedCharFieldType>();
+}
+
+// combined character field
+
+SwCombinedCharField::SwCombinedCharField( SwCombinedCharFieldType* pFTyp,
+ const OUString& rChars )
+ : SwField( pFTyp, 0 ),
+ m_sCharacters( rChars.copy( 0, std::min<sal_Int32>(rChars.getLength(), MAX_COMBINED_CHARACTERS) ))
+{
+}
+
+OUString SwCombinedCharField::ExpandImpl(SwRootFrame const*const) const
+{
+ return m_sCharacters;
+}
+
+std::unique_ptr<SwField> SwCombinedCharField::Copy() const
+{
+ return std::make_unique<SwCombinedCharField>( static_cast<SwCombinedCharFieldType*>(GetTyp()),
+ m_sCharacters );
+}
+
+OUString SwCombinedCharField::GetPar1() const
+{
+ return m_sCharacters;
+}
+
+void SwCombinedCharField::SetPar1(const OUString& rStr)
+{
+ m_sCharacters = rStr.copy(0, std::min<sal_Int32>(rStr.getLength(), MAX_COMBINED_CHARACTERS));
+}
+
+bool SwCombinedCharField::QueryValue( uno::Any& rAny,
+ sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_sCharacters;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwCombinedCharField::PutValue( const uno::Any& rAny,
+ sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ SetPar1(sTmp);
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/expfld.cxx b/sw/source/core/fields/expfld.cxx
new file mode 100644
index 000000000..e6d810506
--- /dev/null
+++ b/sw/source/core/fields/expfld.cxx
@@ -0,0 +1,1445 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <limits>
+
+#include <UndoTable.hxx>
+#include <hintids.hxx>
+#include <o3tl/any.hxx>
+#include <osl/diagnose.h>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <com/sun/star/text/SetVariableType.hpp>
+#include <unofield.hxx>
+#include <frmfmt.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <fmtanchr.hxx>
+#include <txtftn.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <layfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <tabfrm.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <rowfrm.hxx>
+#include <expfld.hxx>
+#include <usrfld.hxx>
+#include <ndtxt.hxx>
+#include <calc.hxx>
+#include <pam.hxx>
+#include <docfld.hxx>
+#include <swtable.hxx>
+#include <breakit.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <unofldmid.h>
+#include <numrule.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::text;
+
+static sal_Int16 lcl_SubTypeToAPI(sal_uInt16 nSubType)
+{
+ sal_Int16 nRet = 0;
+ switch(nSubType)
+ {
+ case nsSwGetSetExpType::GSE_EXPR:
+ nRet = SetVariableType::VAR; // 0
+ break;
+ case nsSwGetSetExpType::GSE_SEQ:
+ nRet = SetVariableType::SEQUENCE; // 1
+ break;
+ case nsSwGetSetExpType::GSE_FORMULA:
+ nRet = SetVariableType::FORMULA; // 2
+ break;
+ case nsSwGetSetExpType::GSE_STRING:
+ nRet = SetVariableType::STRING; // 3
+ break;
+ }
+ return nRet;
+}
+
+static sal_Int32 lcl_APIToSubType(const uno::Any& rAny)
+{
+ sal_Int16 nVal = 0;
+ rAny >>= nVal;
+ sal_Int32 nSet = 0;
+ switch(nVal)
+ {
+ case SetVariableType::VAR: nSet = nsSwGetSetExpType::GSE_EXPR; break;
+ case SetVariableType::SEQUENCE: nSet = nsSwGetSetExpType::GSE_SEQ; break;
+ case SetVariableType::FORMULA: nSet = nsSwGetSetExpType::GSE_FORMULA; break;
+ case SetVariableType::STRING: nSet = nsSwGetSetExpType::GSE_STRING; break;
+ default:
+ OSL_FAIL("wrong value");
+ nSet = -1;
+ }
+ return nSet;
+}
+
+OUString ReplacePoint( const OUString& rTmpName, bool bWithCommandType )
+{
+ // replace first and last (if bWithCommandType: last two) dot
+ // since table names may contain dots
+
+ sal_Int32 nIndex = rTmpName.lastIndexOf('.');
+ if (nIndex<0)
+ {
+ return rTmpName;
+ }
+
+ OUString sRes = rTmpName.replaceAt(nIndex, 1, rtl::OUStringChar(DB_DELIM));
+
+ if (bWithCommandType)
+ {
+ nIndex = sRes.lastIndexOf('.', nIndex);
+ if (nIndex<0)
+ {
+ return sRes;
+ }
+ sRes = sRes.replaceAt(nIndex, 1, rtl::OUStringChar(DB_DELIM));
+ }
+
+ nIndex = sRes.indexOf('.');
+ if (nIndex>=0)
+ {
+ sRes = sRes.replaceAt(nIndex, 1, rtl::OUStringChar(DB_DELIM));
+ }
+ return sRes;
+}
+
+static SwTextNode* GetFirstTextNode( const SwDoc& rDoc, SwPosition& rPos,
+ const SwContentFrame *pCFrame, Point &rPt )
+{
+ SwTextNode* pTextNode = nullptr;
+ if ( !pCFrame )
+ {
+ const SwNodes& rNodes = rDoc.GetNodes();
+ rPos.nNode = *rNodes.GetEndOfContent().StartOfSectionNode();
+ SwContentNode* pCNd;
+ while( nullptr != (pCNd = rNodes.GoNext( &rPos.nNode ) ) &&
+ nullptr == ( pTextNode = pCNd->GetTextNode() ) )
+ ;
+ OSL_ENSURE( pTextNode, "Where is the 1. TextNode?" );
+ rPos.nContent.Assign( pTextNode, 0 );
+ }
+ else if ( !pCFrame->isFrameAreaDefinitionValid() )
+ {
+ assert(pCFrame->IsTextFrame());
+ rPos = static_cast<SwTextFrame const*>(pCFrame)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ pCFrame->GetModelPositionForViewPoint( &rPos, rPt );
+ pTextNode = rPos.nNode.GetNode().GetTextNode();
+ }
+ return pTextNode;
+}
+
+const SwTextNode* GetBodyTextNode( const SwDoc& rDoc, SwPosition& rPos,
+ const SwFrame& rFrame )
+{
+ const SwLayoutFrame* pLayout = rFrame.GetUpper();
+ const SwTextNode* pTextNode = nullptr;
+
+ while( pLayout )
+ {
+ if( pLayout->IsFlyFrame() )
+ {
+ // get the FlyFormat
+ const SwFrameFormat* pFlyFormat = static_cast<const SwFlyFrame*>(pLayout)->GetFormat();
+ OSL_ENSURE( pFlyFormat, "Could not find FlyFormat, where is the field?" );
+
+ const SwFormatAnchor &rAnchor = pFlyFormat->GetAnchor();
+
+ if( RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId() )
+ {
+ // the fly needs to be attached somewhere, so ask it
+ pLayout = static_cast<const SwLayoutFrame*>(static_cast<const SwFlyFrame*>(pLayout)->GetAnchorFrame());
+ continue;
+ }
+ else if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()))
+ {
+ OSL_ENSURE( rAnchor.GetContentAnchor(), "no valid position" );
+ rPos = *rAnchor.GetContentAnchor();
+ pTextNode = rPos.nNode.GetNode().GetTextNode();
+ if ( RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId() )
+ {
+ const_cast<SwTextNode*>(pTextNode)->MakeStartIndex(
+ &rPos.nContent );
+ }
+
+ // do not break yet, might be as well in Header/Footer/Footnote/Fly
+ pLayout = static_cast<const SwFlyFrame*>(pLayout)->GetAnchorFrame()
+ ? static_cast<const SwFlyFrame*>(pLayout)->GetAnchorFrame()->GetUpper() : nullptr;
+ continue;
+ }
+ else
+ {
+ pLayout->FindPageFrame()->GetContentPosition(
+ pLayout->getFrameArea().Pos(), rPos );
+ pTextNode = rPos.nNode.GetNode().GetTextNode();
+ }
+ }
+ else if( pLayout->IsFootnoteFrame() )
+ {
+ // get the anchor's node
+ const SwTextFootnote* pFootnote = static_cast<const SwFootnoteFrame*>(pLayout)->GetAttr();
+ pTextNode = &pFootnote->GetTextNode();
+ rPos.nNode = *pTextNode;
+ rPos.nContent = pFootnote->GetStart();
+ }
+ else if( pLayout->IsHeaderFrame() || pLayout->IsFooterFrame() )
+ {
+ const SwContentFrame* pContentFrame;
+ const SwPageFrame* pPgFrame = pLayout->FindPageFrame();
+ if( pLayout->IsHeaderFrame() )
+ {
+ const SwTabFrame *pTab;
+ if( nullptr != ( pContentFrame = pPgFrame->FindFirstBodyContent()) &&
+ nullptr != (pTab = pContentFrame->FindTabFrame()) && pTab->IsFollow() &&
+ pTab->GetTable()->GetRowsToRepeat() > 0 &&
+ pTab->IsInHeadline( *pContentFrame ) )
+ {
+ // take the next line
+ const SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow();
+ pContentFrame = pRow->ContainsContent();
+ }
+ }
+ else
+ pContentFrame = pPgFrame->FindLastBodyContent();
+
+ if( pContentFrame )
+ {
+ assert(pContentFrame->IsTextFrame());
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pContentFrame));
+ rPos = pFrame->MapViewToModelPos(TextFrameIndex(pFrame->GetText().getLength()));
+ pTextNode = rPos.nNode.GetNode().GetTextNode();
+ assert(pTextNode);
+ }
+ else
+ {
+ Point aPt( pLayout->getFrameArea().Pos() );
+ aPt.AdjustY( 1 ); // get out of the header
+ pContentFrame = pPgFrame->GetContentPos( aPt, false, true );
+ pTextNode = GetFirstTextNode( rDoc, rPos, pContentFrame, aPt );
+ }
+ }
+ else
+ {
+ pLayout = pLayout->GetUpper();
+ continue;
+ }
+ break; // found, so finish loop
+ }
+ return pTextNode;
+}
+
+SwGetExpFieldType::SwGetExpFieldType(SwDoc* pDc)
+ : SwValueFieldType( pDc, SwFieldIds::GetExp )
+{
+}
+
+std::unique_ptr<SwFieldType> SwGetExpFieldType::Copy() const
+{
+ return std::make_unique<SwGetExpFieldType>(GetDoc());
+}
+
+void SwGetExpFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ // do not expand anything else
+ if(pLegacy->GetWhich() != RES_DOCPOS_UPDATE)
+ return;
+ CallSwClientNotify(rHint);
+}
+
+SwGetExpField::SwGetExpField(SwGetExpFieldType* pTyp, const OUString& rFormel,
+ sal_uInt16 nSub, sal_uLong nFormat)
+ : SwFormulaField( pTyp, nFormat, 0.0 )
+ , m_fValueRLHidden(0.0)
+ ,
+ m_bIsInBodyText( true ),
+ m_nSubType(nSub),
+ m_bLateInitialization( false )
+{
+ SetFormula( rFormel );
+}
+
+void SwGetExpField::ChgExpStr(const OUString& rExpand, SwRootFrame const*const pLayout)
+{
+ if (!pLayout || pLayout->IsHideRedlines())
+ {
+ m_sExpandRLHidden = rExpand;
+ }
+ if (!pLayout || !pLayout->IsHideRedlines())
+ {
+ m_sExpand = rExpand;
+ }
+}
+
+OUString SwGetExpField::ExpandImpl(SwRootFrame const*const pLayout) const
+{
+ if(m_nSubType & nsSwExtendedSubType::SUB_CMD)
+ return GetFormula();
+
+ return (pLayout && pLayout->IsHideRedlines()) ? m_sExpandRLHidden : m_sExpand;
+}
+
+OUString SwGetExpField::GetFieldName() const
+{
+ const SwFieldTypesEnum nType =
+ (nsSwGetSetExpType::GSE_FORMULA & m_nSubType)
+ ? SwFieldTypesEnum::Formel
+ : SwFieldTypesEnum::Get;
+
+ return SwFieldType::GetTypeStr(nType) + " " + GetFormula();
+}
+
+std::unique_ptr<SwField> SwGetExpField::Copy() const
+{
+ std::unique_ptr<SwGetExpField> pTmp(new SwGetExpField(static_cast<SwGetExpFieldType*>(GetTyp()),
+ GetFormula(), m_nSubType, GetFormat()));
+ pTmp->SetLanguage(GetLanguage());
+ pTmp->m_fValueRLHidden = m_fValueRLHidden;
+ pTmp->SwValueField::SetValue(GetValue());
+ pTmp->m_sExpand = m_sExpand;
+ pTmp->m_sExpandRLHidden = m_sExpandRLHidden;
+ pTmp->m_bIsInBodyText = m_bIsInBodyText;
+ pTmp->SetAutomaticLanguage(IsAutomaticLanguage());
+ if( m_bLateInitialization )
+ pTmp->SetLateInitialization();
+
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+void SwGetExpField::ChangeExpansion( const SwFrame& rFrame, const SwTextField& rField )
+{
+ if( m_bIsInBodyText ) // only fields in Footer, Header, FootNote, Flys
+ return;
+
+ OSL_ENSURE( !rFrame.IsInDocBody(), "Flag incorrect, frame is in DocBody" );
+
+ // determine document (or is there an easier way?)
+ const SwTextNode* pTextNode = &rField.GetTextNode();
+ SwDoc& rDoc = const_cast<SwDoc&>(pTextNode->GetDoc());
+
+ // create index for determination of the TextNode
+ SwPosition aPos( SwNodeIndex( rDoc.GetNodes() ) );
+ pTextNode = GetBodyTextNode( rDoc, aPos, rFrame );
+
+ // If no layout exists, ChangeExpansion is called for header and
+ // footer lines via layout formatting without existing TextNode.
+ if(!pTextNode)
+ return;
+ // #i82544#
+ if( m_bLateInitialization )
+ {
+ SwFieldType* pSetExpField = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, GetFormula(), false);
+ if( pSetExpField )
+ {
+ m_bLateInitialization = false;
+ if( !(GetSubType() & nsSwGetSetExpType::GSE_STRING) &&
+ static_cast< SwSetExpFieldType* >(pSetExpField)->GetType() == nsSwGetSetExpType::GSE_STRING )
+ SetSubType( nsSwGetSetExpType::GSE_STRING );
+ }
+ }
+
+ SwRootFrame const& rLayout(*rFrame.getRootFrame());
+ OUString & rExpand(rLayout.IsHideRedlines() ? m_sExpandRLHidden : m_sExpand);
+ // here a page number is needed to sort correctly
+ SetGetExpField aEndField(aPos.nNode, &rField, &aPos.nContent, rFrame.GetPhyPageNum());
+ if(GetSubType() & nsSwGetSetExpType::GSE_STRING)
+ {
+ SwHashTable<HashStr> aHashTable(0);
+ rDoc.getIDocumentFieldsAccess().FieldsToExpand(aHashTable, aEndField, rLayout);
+ rExpand = LookString( aHashTable, GetFormula() );
+ }
+ else
+ {
+ // fill calculator with values
+ SwCalc aCalc( rDoc );
+ rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, aEndField, &rLayout);
+
+ // calculate value
+ SetValue(aCalc.Calculate(GetFormula()).GetDouble(), &rLayout);
+
+ // analyse based on format
+ rExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(
+ GetValue(&rLayout), GetFormat(), GetLanguage());
+ }
+}
+
+OUString SwGetExpField::GetPar2() const
+{
+ return GetFormula();
+}
+
+void SwGetExpField::SetPar2(const OUString& rStr)
+{
+ SetFormula(rStr);
+}
+
+sal_uInt16 SwGetExpField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwGetExpField::SetSubType(sal_uInt16 nType)
+{
+ m_nSubType = nType;
+}
+
+void SwGetExpField::SetLanguage(LanguageType nLng)
+{
+ if (m_nSubType & nsSwExtendedSubType::SUB_CMD)
+ SwField::SetLanguage(nLng);
+ else
+ SwValueField::SetLanguage(nLng);
+}
+
+bool SwGetExpField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_DOUBLE:
+ rAny <<= GetValue();
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int32>(GetFormat());
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny <<= static_cast<sal_Int16>(m_nSubType);
+ break;
+ case FIELD_PROP_PAR1:
+ rAny <<= GetFormula();
+ break;
+ case FIELD_PROP_SUBTYPE:
+ {
+ sal_Int16 nRet = lcl_SubTypeToAPI(GetSubType() & 0xff);
+ rAny <<= nRet;
+ }
+ break;
+ case FIELD_PROP_BOOL2:
+ rAny <<= 0 != (m_nSubType & nsSwExtendedSubType::SUB_CMD);
+ break;
+ case FIELD_PROP_PAR4:
+ rAny <<= m_sExpand;
+ break;
+ default:
+ return SwField::QueryValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+bool SwGetExpField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ sal_Int32 nTmp = 0;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_DOUBLE:
+ SwValueField::SetValue(*o3tl::doAccess<double>(rAny));
+ m_fValueRLHidden = *o3tl::doAccess<double>(rAny);
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny >>= nTmp;
+ SetFormat(nTmp);
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny >>= nTmp;
+ m_nSubType = o3tl::narrowing<sal_uInt16>(nTmp);
+ break;
+ case FIELD_PROP_PAR1:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ SetFormula(sTmp);
+ break;
+ }
+ case FIELD_PROP_SUBTYPE:
+ nTmp = lcl_APIToSubType(rAny);
+ if( nTmp >=0 )
+ SetSubType( o3tl::narrowing<sal_uInt16>((GetSubType() & 0xff00) | nTmp));
+ break;
+ case FIELD_PROP_BOOL2:
+ if(*o3tl::doAccess<bool>(rAny))
+ m_nSubType |= nsSwExtendedSubType::SUB_CMD;
+ else
+ m_nSubType &= (~nsSwExtendedSubType::SUB_CMD);
+ break;
+ case FIELD_PROP_PAR4:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ ChgExpStr(sTmp, nullptr);
+ break;
+ }
+ default:
+ return SwField::PutValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+SwSetExpFieldType::SwSetExpFieldType( SwDoc* pDc, const OUString& rName, sal_uInt16 nTyp )
+ : SwValueFieldType( pDc, SwFieldIds::SetExp ),
+ m_sName( rName ),
+ m_sDelim( "." ),
+ m_nType(nTyp), m_nLevel( UCHAR_MAX ),
+ m_bDeleted( false )
+{
+ if( ( nsSwGetSetExpType::GSE_SEQ | nsSwGetSetExpType::GSE_STRING ) & m_nType )
+ EnableFormat(false); // do not use Numberformatter
+}
+
+std::unique_ptr<SwFieldType> SwSetExpFieldType::Copy() const
+{
+ std::unique_ptr<SwSetExpFieldType> pNew(new SwSetExpFieldType(GetDoc(), m_sName, m_nType));
+ pNew->m_bDeleted = m_bDeleted;
+ pNew->m_sDelim = m_sDelim;
+ pNew->m_nLevel = m_nLevel;
+
+ return pNew;
+}
+
+OUString SwSetExpFieldType::GetName() const
+{
+ return m_sName;
+}
+
+const OUString& SwSetExpField::GetExpStr(SwRootFrame const*const pLayout) const
+{
+ return (pLayout && pLayout->IsHideRedlines()) ? msExpandRLHidden : msExpand;
+}
+
+void SwSetExpField::ChgExpStr(const OUString& rExpand, SwRootFrame const*const pLayout)
+{
+ if (!pLayout || pLayout->IsHideRedlines())
+ {
+ msExpandRLHidden = rExpand;
+ }
+ if (!pLayout || !pLayout->IsHideRedlines())
+ {
+ msExpand = rExpand;
+ }
+}
+
+void SwSetExpFieldType::SwClientNotify(const SwModify&, const SfxHint&)
+{
+ // do not expand further
+}
+
+void SwSetExpFieldType::SetSeqFormat(sal_uLong nFormat)
+{
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields, false);
+ for(auto pFormatField: vFields)
+ pFormatField->GetField()->ChangeFormat(nFormat);
+}
+
+sal_uLong SwSetExpFieldType::GetSeqFormat() const
+{
+ if( !HasWriterListeners() )
+ return SVX_NUM_ARABIC;
+
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields, false);
+ return vFields.front()->GetField()->GetFormat();
+}
+
+void SwSetExpFieldType::SetSeqRefNo( SwSetExpField& rField )
+{
+ if( !HasWriterListeners() || !(nsSwGetSetExpType::GSE_SEQ & m_nType) )
+ return;
+
+ std::vector<sal_uInt16> aArr;
+
+ // check if number is already used and if a new one needs to be created
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ for(SwFormatField* pF: vFields)
+ if(pF->GetField() != &rField)
+ InsertSort(aArr, static_cast<SwSetExpField*>(pF->GetField())->GetSeqNumber());
+
+ // check first if number already exists
+ sal_uInt16 nNum = rField.GetSeqNumber();
+ if( USHRT_MAX != nNum )
+ {
+ std::vector<sal_uInt16>::size_type n {0};
+
+ for( n = 0; n < aArr.size(); ++n )
+ if( aArr[ n ] >= nNum )
+ break;
+
+ if( n == aArr.size() || aArr[ n ] > nNum )
+ return; // no -> use it
+ }
+
+ // flagged all numbers, so determine the right number
+ std::vector<sal_uInt16>::size_type n = aArr.size();
+ OSL_ENSURE( n <= std::numeric_limits<sal_uInt16>::max(), "Array is too big for using a sal_uInt16 index" );
+
+ if ( n > 0 && aArr[ n-1 ] != n-1 )
+ {
+ for( n = 0; n < aArr.size(); ++n )
+ if( n != aArr[ n ] )
+ break;
+ }
+
+ rField.SetSeqNumber( n );
+}
+
+size_t SwSetExpFieldType::GetSeqFieldList(SwSeqFieldList& rList,
+ SwRootFrame const*const pLayout)
+{
+ rList.Clear();
+
+ IDocumentRedlineAccess const& rIDRA(GetDoc()->getIDocumentRedlineAccess());
+
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ for(SwFormatField* pF: vFields)
+ {
+ const SwTextNode* pNd;
+ if( nullptr != ( pNd = pF->GetTextField()->GetpTextNode() )
+ && (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(rIDRA, *pF->GetTextField())))
+ {
+ SeqFieldLstElem aNew(
+ pNd->GetExpandText(pLayout),
+ static_cast<SwSetExpField*>(pF->GetField())->GetSeqNumber() );
+ rList.InsertSort( aNew );
+ }
+ }
+ return rList.Count();
+}
+
+void SwSetExpFieldType::SetChapter(SwSetExpField& rField, const SwNode& rNd,
+ SwRootFrame const*const pLayout)
+{
+ const SwTextNode* pTextNd = rNd.FindOutlineNodeOfLevel(m_nLevel, pLayout);
+ if( !pTextNd )
+ return;
+
+ SwNumRule * pRule = pTextNd->GetNumRule();
+
+ if (!pRule)
+ return;
+
+ // --> OD 2005-11-02 #i51089 - TUNING#
+ if (SwNodeNum const*const pNum = pTextNd->GetNum(pLayout))
+ {
+ // only get the number, without pre-/post-fixstrings
+ OUString const sNumber(pRule->MakeNumString(*pNum, false));
+
+ if( !sNumber.isEmpty() )
+ rField.ChgExpStr(sNumber + m_sDelim + rField.GetExpStr(pLayout), pLayout);
+ }
+ else
+ {
+ OSL_ENSURE(pTextNd->GetNum(nullptr), "<SwSetExpFieldType::SetChapter(..)> - text node with numbering rule, but without number. This is a serious defect");
+ }
+}
+
+void SwSetExpFieldType::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_SUBTYPE:
+ {
+ sal_Int16 nRet = lcl_SubTypeToAPI(GetType());
+ rAny <<= nRet;
+ }
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= GetDelimiter();
+ break;
+ case FIELD_PROP_SHORT1:
+ {
+ sal_Int8 nRet = m_nLevel < MAXLEVEL? m_nLevel : -1;
+ rAny <<= nRet;
+ }
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void SwSetExpFieldType::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_SUBTYPE:
+ {
+ sal_Int32 nSet = lcl_APIToSubType(rAny);
+ if(nSet >=0)
+ SetType(o3tl::narrowing<sal_uInt16>(nSet));
+ }
+ break;
+ case FIELD_PROP_PAR2:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ if( !sTmp.isEmpty() )
+ SetDelimiter( sTmp );
+ else
+ SetDelimiter( " " );
+ }
+ break;
+ case FIELD_PROP_SHORT1:
+ {
+ sal_Int8 nLvl = 0;
+ rAny >>= nLvl;
+ if(nLvl < 0 || nLvl >= MAXLEVEL)
+ SetOutlineLvl(UCHAR_MAX);
+ else
+ SetOutlineLvl(nLvl);
+ }
+ break;
+ default:
+ assert(false);
+ }
+}
+
+bool SwSeqFieldList::InsertSort( SeqFieldLstElem aNew )
+{
+ OUStringBuffer aBuf(aNew.sDlgEntry);
+ const sal_Int32 nLen = aBuf.getLength();
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if (aBuf[i]<' ')
+ {
+ aBuf[i]=' ';
+ }
+ }
+ aNew.sDlgEntry = aBuf.makeStringAndClear();
+
+ size_t nPos = 0;
+ bool bRet = SeekEntry( aNew, &nPos );
+ if( !bRet )
+ maData.insert( maData.begin() + nPos, aNew );
+ return bRet;
+}
+
+bool SwSeqFieldList::SeekEntry( const SeqFieldLstElem& rNew, size_t* pP ) const
+{
+ size_t nO = maData.size();
+ size_t nU = 0;
+ if( nO > 0 )
+ {
+ CollatorWrapper & rCaseColl = ::GetAppCaseCollator(),
+ & rColl = ::GetAppCollator();
+ const CharClass& rCC = GetAppCharClass();
+
+ //#59900# Sorting should sort number correctly (e.g. "10" after "9" not after "1")
+ const OUString rTmp2 = rNew.sDlgEntry;
+ sal_Int32 nFndPos2 = 0;
+ const OUString sNum2( rTmp2.getToken( 0, ' ', nFndPos2 ));
+ bool bIsNum2IsNumeric = CharClass::isAsciiNumeric( sNum2 );
+ sal_Int32 nNum2 = bIsNum2IsNumeric ? sNum2.toInt32() : 0;
+
+ nO--;
+ while( nU <= nO )
+ {
+ const size_t nM = nU + ( nO - nU ) / 2;
+
+ //#59900# Sorting should sort number correctly (e.g. "10" after "9" not after "1")
+ const OUString rTmp1 = maData[nM].sDlgEntry;
+ sal_Int32 nFndPos1 = 0;
+ const OUString sNum1( rTmp1.getToken( 0, ' ', nFndPos1 ));
+ sal_Int32 nCmp;
+
+ if( bIsNum2IsNumeric && rCC.isNumeric( sNum1 ) )
+ {
+ sal_Int32 nNum1 = sNum1.toInt32();
+ nCmp = nNum2 - nNum1;
+ if( 0 == nCmp )
+ {
+ OUString aTmp1 = nFndPos1 != -1 ? rTmp1.copy(nFndPos1) : OUString();
+ OUString aTmp2 = nFndPos2 != -1 ? rTmp2.copy(nFndPos2) : OUString();
+ nCmp = rCaseColl.compareString(aTmp2, aTmp1);
+ }
+ }
+ else
+ nCmp = rColl.compareString( rTmp2, rTmp1 );
+
+ if( 0 == nCmp )
+ {
+ if( pP ) *pP = nM;
+ return true;
+ }
+ else if( 0 < nCmp )
+ nU = nM + 1;
+ else if( nM == 0 )
+ break;
+ else
+ nO = nM - 1;
+ }
+ }
+ if( pP ) *pP = nU;
+ return false;
+}
+
+SwSetExpField::SwSetExpField(SwSetExpFieldType* pTyp, const OUString& rFormel,
+ sal_uLong nFormat)
+ : SwFormulaField( pTyp, nFormat, 0.0 )
+ , m_fValueRLHidden(0.0)
+ , mnSeqNo( USHRT_MAX )
+ , mnSubType(0)
+ , mpFormatField(nullptr)
+{
+ SetFormula(rFormel);
+ // ignore SubType
+ mbInput = false;
+ if( IsSequenceField() )
+ {
+ SwValueField::SetValue(1.0);
+ m_fValueRLHidden = 1.0;
+ if( rFormel.isEmpty() )
+ {
+ SetFormula(pTyp->GetName() + "+1");
+ }
+ }
+}
+
+void SwSetExpField::SetFormatField(SwFormatField & rFormatField)
+{
+ mpFormatField = &rFormatField;
+}
+
+OUString SwSetExpField::ExpandImpl(SwRootFrame const*const pLayout) const
+{
+ if (mnSubType & nsSwExtendedSubType::SUB_CMD)
+ { // we need the CommandString
+ return GetTyp()->GetName() + " = " + GetFormula();
+ }
+ if(!(mnSubType & nsSwExtendedSubType::SUB_INVISIBLE))
+ { // value is visible
+ return (pLayout && pLayout->IsHideRedlines()) ? msExpandRLHidden : msExpand;
+ }
+ return OUString();
+}
+
+/// @return the field name
+OUString SwSetExpField::GetFieldName() const
+{
+ SwFieldTypesEnum const nStrType( (IsSequenceField())
+ ? SwFieldTypesEnum::Sequence
+ : mbInput
+ ? SwFieldTypesEnum::SetInput
+ : SwFieldTypesEnum::Set );
+
+ OUString aStr(
+ SwFieldType::GetTypeStr( nStrType )
+ + " "
+ + GetTyp()->GetName() );
+
+ // Sequence: without formula
+ if (SwFieldTypesEnum::Sequence != nStrType)
+ {
+ aStr += " = " + GetFormula();
+ }
+ return aStr;
+}
+
+std::unique_ptr<SwField> SwSetExpField::Copy() const
+{
+ std::unique_ptr<SwSetExpField> pTmp(new SwSetExpField(static_cast<SwSetExpFieldType*>(GetTyp()),
+ GetFormula(), GetFormat()));
+ pTmp->SwValueField::SetValue(GetValue());
+ pTmp->m_fValueRLHidden = m_fValueRLHidden;
+ pTmp->msExpand = msExpand;
+ pTmp->msExpandRLHidden = msExpandRLHidden;
+ pTmp->SetAutomaticLanguage(IsAutomaticLanguage());
+ pTmp->SetLanguage(GetLanguage());
+ pTmp->maPText = maPText;
+ pTmp->mbInput = mbInput;
+ pTmp->mnSeqNo = mnSeqNo;
+ pTmp->SetSubType(GetSubType());
+
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+void SwSetExpField::SetSubType(sal_uInt16 nSub)
+{
+ static_cast<SwSetExpFieldType*>(GetTyp())->SetType(nSub & 0xff);
+ mnSubType = nSub & 0xff00;
+
+ OSL_ENSURE( (nSub & 0xff) != 3, "SubType is illegal!" );
+}
+
+sal_uInt16 SwSetExpField::GetSubType() const
+{
+ return static_cast<SwSetExpFieldType*>(GetTyp())->GetType() | mnSubType;
+}
+
+void SwSetExpField::SetValue( const double& rAny )
+{
+ SwValueField::SetValue(rAny);
+
+ if( IsSequenceField() )
+ msExpand = FormatNumber( GetValue(), static_cast<SvxNumType>(GetFormat()), GetLanguage() );
+ else
+ msExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( rAny,
+ GetFormat(), GetLanguage());
+}
+
+void SwSetExpField::SetValue(const double& rValue, SwRootFrame const*const pLayout)
+{
+ if (!pLayout || !pLayout->IsHideRedlines())
+ {
+ SetValue(rValue);
+ }
+ if (pLayout && !pLayout->IsHideRedlines())
+ return;
+
+ m_fValueRLHidden = rValue;
+ if (IsSequenceField())
+ {
+ msExpandRLHidden = FormatNumber(rValue, static_cast<SvxNumType>(GetFormat()), GetLanguage());
+ }
+ else
+ {
+ msExpandRLHidden = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(
+ rValue, GetFormat(), GetLanguage());
+ }
+}
+
+double SwSetExpField::GetValue(SwRootFrame const* pLayout) const
+{
+ return (pLayout && pLayout->IsHideRedlines()) ? m_fValueRLHidden : GetValue();
+}
+
+void SwGetExpField::SetValue( const double& rAny )
+{
+ SwValueField::SetValue(rAny);
+ m_sExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue( rAny, GetFormat(),
+ GetLanguage());
+}
+
+void SwGetExpField::SetValue(const double& rValue, SwRootFrame const*const pLayout)
+{
+ if (!pLayout || !pLayout->IsHideRedlines())
+ {
+ SetValue(rValue);
+ }
+ if (!pLayout || pLayout->IsHideRedlines())
+ {
+ m_fValueRLHidden = rValue;
+ m_sExpandRLHidden = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(
+ rValue, GetFormat(), GetLanguage());
+ }
+}
+
+double SwGetExpField::GetValue(SwRootFrame const* pLayout) const
+{
+ return (pLayout && pLayout->IsHideRedlines()) ? m_fValueRLHidden : GetValue();
+}
+
+/** Find the index of the reference text following the current field
+ *
+ * @param rFormat
+ * @param rDoc
+ * @param nHint search starting position after the current field (or 0 if default)
+ * @return
+ */
+sal_Int32 SwGetExpField::GetReferenceTextPos( const SwFormatField& rFormat, SwDoc& rDoc, sal_Int32 nHint)
+{
+
+ const SwTextField* pTextField = rFormat.GetTextField();
+ const SwTextNode& rTextNode = pTextField->GetTextNode();
+
+ sal_Int32 nRet = nHint ? nHint : pTextField->GetStart() + 1;
+ OUString sNodeText = rTextNode.GetText();
+
+ if(nRet<sNodeText.getLength())
+ {
+ sNodeText = sNodeText.copy(nRet);
+
+ // now check if sNodeText starts with a non-alphanumeric character plus blanks
+ sal_uInt16 nSrcpt = g_pBreakIt->GetRealScriptOfText( sNodeText, 0 );
+
+ static const WhichRangesContainer nIds(svl::Items<
+ RES_CHRATR_FONT, RES_CHRATR_FONT,
+ RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE,
+ RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT,
+ RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE,
+ RES_CHRATR_CTL_FONT, RES_CHRATR_CTL_FONT,
+ RES_CHRATR_CTL_LANGUAGE, RES_CHRATR_CTL_LANGUAGE
+ >);
+ SwAttrSet aSet(rDoc.GetAttrPool(), nIds);
+ rTextNode.GetParaAttr(aSet, nRet, nRet+1);
+
+ TypedWhichId<SvxFontItem> nFontWhich = GetWhichOfScript( RES_CHRATR_FONT, nSrcpt );
+ if( RTL_TEXTENCODING_SYMBOL != aSet.Get( nFontWhich ).GetCharSet() )
+ {
+ TypedWhichId<SvxLanguageItem> nLangWhich = GetWhichOfScript( RES_CHRATR_LANGUAGE, nSrcpt ) ;
+ LanguageType eLang = aSet.Get(nLangWhich).GetLanguage();
+ CharClass aCC(( LanguageTag(eLang) ));
+ sal_Unicode c0 = sNodeText[0];
+ bool bIsAlphaNum = aCC.isAlphaNumeric( sNodeText, 0 );
+ if( !bIsAlphaNum ||
+ (c0 == ' ' || c0 == '\t'))
+ {
+ // ignoring blanks
+ nRet++;
+ const sal_Int32 nLen = sNodeText.getLength();
+ for (sal_Int32 i = 1;
+ i<nLen && (sNodeText[i]==' ' || sNodeText[i]=='\t');
+ ++i
+ )
+ ++nRet;
+ }
+ }
+ }
+ return nRet;
+}
+
+OUString SwSetExpField::GetPar1() const
+{
+ return static_cast<const SwSetExpFieldType*>(GetTyp())->GetName();
+}
+
+OUString SwSetExpField::GetPar2() const
+{
+ sal_uInt16 nType = static_cast<SwSetExpFieldType*>(GetTyp())->GetType();
+
+ if (nType & nsSwGetSetExpType::GSE_STRING)
+ return GetFormula();
+ return GetExpandedFormula();
+}
+
+void SwSetExpField::SetPar2(const OUString& rStr)
+{
+ sal_uInt16 nType = static_cast<SwSetExpFieldType*>(GetTyp())->GetType();
+
+ if( !(nType & nsSwGetSetExpType::GSE_SEQ) || !rStr.isEmpty() )
+ {
+ if (nType & nsSwGetSetExpType::GSE_STRING)
+ SetFormula(rStr);
+ else
+ SetExpandedFormula(rStr);
+ }
+}
+
+bool SwSetExpField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ sal_Int32 nTmp32 = 0;
+ sal_Int16 nTmp16 = 0;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL2:
+ if(*o3tl::doAccess<bool>(rAny))
+ mnSubType &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ mnSubType |= nsSwExtendedSubType::SUB_INVISIBLE;
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny >>= nTmp32;
+ SetFormat(nTmp32);
+ break;
+ case FIELD_PROP_USHORT2:
+ {
+ rAny >>= nTmp16;
+ if(nTmp16 <= css::style::NumberingType::NUMBER_NONE )
+ SetFormat(nTmp16);
+ else {
+ //exception(wrong_value)
+ ;
+ }
+ }
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny >>= nTmp16;
+ mnSeqNo = nTmp16;
+ break;
+ case FIELD_PROP_PAR1:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ SetPar1( SwStyleNameMapper::GetUIName( sTmp, SwGetPoolIdFromName::TxtColl ) );
+ }
+ break;
+ case FIELD_PROP_PAR2:
+ {
+ OUString uTmp;
+ rAny >>= uTmp;
+ //I18N - if the formula contains only "TypeName+1"
+ //and it's one of the initially created sequence fields
+ //then the localized names has to be replaced by a programmatic name
+ OUString sMyFormula = SwXFieldMaster::LocalizeFormula(*this, uTmp, false);
+ SetFormula( sMyFormula );
+ }
+ break;
+ case FIELD_PROP_DOUBLE:
+ {
+ double fVal = 0.0;
+ rAny >>= fVal;
+ SetValue(fVal);
+ m_fValueRLHidden = fVal;
+ }
+ break;
+ case FIELD_PROP_SUBTYPE:
+ nTmp32 = lcl_APIToSubType(rAny);
+ if(nTmp32 >= 0)
+ SetSubType(o3tl::narrowing<sal_uInt16>((GetSubType() & 0xff00) | nTmp32));
+ break;
+ case FIELD_PROP_PAR3:
+ rAny >>= maPText;
+ break;
+ case FIELD_PROP_BOOL3:
+ if(*o3tl::doAccess<bool>(rAny))
+ mnSubType |= nsSwExtendedSubType::SUB_CMD;
+ else
+ mnSubType &= (~nsSwExtendedSubType::SUB_CMD);
+ break;
+ case FIELD_PROP_BOOL1:
+ {
+ 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:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ ChgExpStr(sTmp, nullptr);
+ }
+ break;
+ default:
+ return SwField::PutValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+bool SwSetExpField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL2:
+ rAny <<= 0 == (mnSubType & nsSwExtendedSubType::SUB_INVISIBLE);
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int32>(GetFormat());
+ break;
+ case FIELD_PROP_USHORT2:
+ rAny <<= static_cast<sal_Int16>(GetFormat());
+ break;
+ case FIELD_PROP_USHORT1:
+ rAny <<= static_cast<sal_Int16>(mnSeqNo);
+ break;
+ case FIELD_PROP_PAR1:
+ rAny <<= SwStyleNameMapper::GetProgName(GetPar1(), SwGetPoolIdFromName::TxtColl );
+ break;
+ case FIELD_PROP_PAR2:
+ {
+ //I18N - if the formula contains only "TypeName+1"
+ //and it's one of the initially created sequence fields
+ //then the localized names has to be replaced by a programmatic name
+ OUString sMyFormula = SwXFieldMaster::LocalizeFormula(*this, GetFormula(), true);
+ rAny <<= sMyFormula;
+ }
+ break;
+ case FIELD_PROP_DOUBLE:
+ rAny <<= GetValue();
+ break;
+ case FIELD_PROP_SUBTYPE:
+ {
+ sal_Int16 nRet = lcl_SubTypeToAPI(GetSubType() & 0xff);
+ rAny <<= nRet;
+ }
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= maPText;
+ break;
+ case FIELD_PROP_BOOL3:
+ rAny <<= 0 != (mnSubType & nsSwExtendedSubType::SUB_CMD);
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= GetInputFlag();
+ break;
+ case FIELD_PROP_PAR4:
+ rAny <<= GetExpStr(nullptr);
+ break;
+ default:
+ return SwField::QueryValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+SwInputFieldType::SwInputFieldType( SwDoc* pD )
+ : SwFieldType( SwFieldIds::Input )
+ , mpDoc( pD )
+{
+}
+
+std::unique_ptr<SwFieldType> SwInputFieldType::Copy() const
+{
+ return std::make_unique<SwInputFieldType>( mpDoc );
+}
+
+SwInputField::SwInputField( SwInputFieldType* pFieldType,
+ const OUString& rContent,
+ const OUString& rPrompt,
+ sal_uInt16 nSub,
+ sal_uLong nFormat,
+ bool bIsFormField )
+ : SwField( pFieldType, nFormat, LANGUAGE_SYSTEM, false )
+ , maContent(rContent)
+ , maPText(rPrompt)
+ , mnSubType(nSub)
+ , mbIsFormField( bIsFormField )
+ , mpFormatField( nullptr )
+{
+}
+
+SwInputField::~SwInputField()
+{
+}
+
+void SwInputField::SetFormatField( SwFormatField& rFormatField )
+{
+ mpFormatField = &rFormatField;
+}
+
+
+void SwInputField::applyFieldContent( const OUString& rNewFieldContent )
+{
+ if ( (mnSubType & 0x00ff) == INP_TXT )
+ {
+ maContent = rNewFieldContent;
+ }
+ else if( (mnSubType & 0x00ff) == INP_USR )
+ {
+ SwUserFieldType* pUserTyp = static_cast<SwUserFieldType*>(
+ static_cast<SwInputFieldType*>(GetTyp())->GetDoc()->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::User, getContent(), false ) );
+ if( pUserTyp )
+ {
+ pUserTyp->SetContent( rNewFieldContent );
+ if (!pUserTyp->IsModifyLocked())
+ {
+ // trigger update of the corresponding User Fields and other
+ // related Input Fields
+ bool bUnlock(false);
+ if (GetFormatField() != nullptr)
+ {
+ SwTextInputField *const pTextInputField =
+ dynamic_cast<SwTextInputField*>(GetFormatField()->GetTextField());
+ if (pTextInputField != nullptr)
+ {
+ bUnlock = pTextInputField->LockNotifyContentChange();
+ }
+ }
+ pUserTyp->UpdateFields();
+ if (bUnlock)
+ {
+ SwTextInputField *const pTextInputField =
+ dynamic_cast<SwTextInputField*>(GetFormatField()->GetTextField());
+ if (pTextInputField != nullptr)
+ {
+ pTextInputField->UnlockNotifyContentChange();
+ }
+ }
+ }
+ }
+ }
+}
+
+OUString SwInputField::GetFieldName() const
+{
+ OUString aStr(SwField::GetFieldName());
+ if ((mnSubType & 0x00ff) == INP_USR)
+ {
+ aStr += GetTyp()->GetName() + " " + getContent();
+ }
+ return aStr;
+}
+
+std::unique_ptr<SwField> SwInputField::Copy() const
+{
+ std::unique_ptr<SwInputField> pField(
+ new SwInputField(
+ static_cast<SwInputFieldType*>(GetTyp()),
+ getContent(),
+ maPText,
+ GetSubType(),
+ GetFormat(),
+ mbIsFormField ));
+
+ pField->SetHelp( maHelp );
+ pField->SetToolTip( maToolTip );
+ pField->maGrabBag = maGrabBag;
+
+ pField->SetAutomaticLanguage(IsAutomaticLanguage());
+ return std::unique_ptr<SwField>(pField.release());
+}
+
+OUString SwInputField::ExpandImpl(SwRootFrame const*const) const
+{
+ if((mnSubType & 0x00ff) == INP_TXT)
+ {
+ return getContent();
+ }
+
+ if( (mnSubType & 0x00ff) == INP_USR )
+ {
+ SwUserFieldType* pUserTyp = static_cast<SwUserFieldType*>(
+ static_cast<SwInputFieldType*>(GetTyp())->GetDoc()->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::User, getContent(), false ) );
+ if( pUserTyp )
+ return pUserTyp->GetContent();
+ }
+
+ return OUString();
+}
+
+bool SwInputField::isFormField() const
+{
+ return mbIsFormField
+ || !maHelp.isEmpty()
+ || !maToolTip.isEmpty();
+}
+
+bool SwInputField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= getContent();
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= maPText;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= maHelp;
+ break;
+ case FIELD_PROP_PAR4:
+ rAny <<= maToolTip;
+ break;
+ case FIELD_PROP_GRABBAG:
+ rAny <<= maGrabBag;
+ break;
+ case FIELD_PROP_TITLE:
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwInputField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= maContent;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= maPText;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny >>= maHelp;
+ break;
+ case FIELD_PROP_PAR4:
+ rAny >>= maToolTip;
+ break;
+ case FIELD_PROP_GRABBAG:
+ rAny >>= maGrabBag;
+ break;
+ case FIELD_PROP_TITLE:
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+/// set condition
+void SwInputField::SetPar1(const OUString& rStr)
+{
+ maContent = rStr;
+}
+
+OUString SwInputField::GetPar1() const
+{
+ return getContent();
+}
+
+void SwInputField::SetPar2(const OUString& rStr)
+{
+ maPText = rStr;
+}
+
+OUString SwInputField::GetPar2() const
+{
+ return maPText;
+}
+
+void SwInputField::SetHelp(const OUString & rStr)
+{
+ maHelp = rStr;
+}
+
+const OUString& SwInputField::GetHelp() const
+{
+ return maHelp;
+}
+
+void SwInputField::SetToolTip(const OUString & rStr)
+{
+ maToolTip = rStr;
+}
+
+const OUString& SwInputField::GetToolTip() const
+{
+ return maToolTip;
+}
+
+sal_uInt16 SwInputField::GetSubType() const
+{
+ return mnSubType;
+}
+
+void SwInputField::SetSubType(sal_uInt16 nSub)
+{
+ mnSubType = nSub;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/fldbas.cxx b/sw/source/core/fields/fldbas.cxx
new file mode 100644
index 000000000..59e54e291
--- /dev/null
+++ b/sw/source/core/fields/fldbas.cxx
@@ -0,0 +1,865 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fldbas.hxx>
+
+#include <float.h>
+
+#include <libxml/xmlwriter.h>
+
+#include <rtl/math.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <o3tl/enumarray.hxx>
+#include <osl/diagnose.h>
+#include <unofldmid.h>
+#include <doc.hxx>
+#include <fmtfld.hxx>
+#include <docufld.hxx>
+#include <expfld.hxx>
+#include <shellres.hxx>
+#include <calc.hxx>
+#include <strings.hrc>
+#include <docary.hxx>
+#include <authfld.hxx>
+#include <calbck.hxx>
+#include <viewsh.hxx>
+#include <hints.hxx>
+
+using namespace ::com::sun::star;
+using namespace nsSwDocInfoSubType;
+
+static LanguageType lcl_GetLanguageOfFormat( LanguageType nLng, sal_uLong nFormat,
+ const SvNumberFormatter& rFormatter )
+{
+ if( nLng == LANGUAGE_NONE ) // Bug #60010
+ nLng = LANGUAGE_SYSTEM;
+ else if( nLng == ::GetAppLanguage() )
+ switch( rFormatter.GetIndexTableOffset( nFormat ))
+ {
+ case NF_NUMBER_SYSTEM:
+ case NF_DATE_SYSTEM_SHORT:
+ case NF_DATE_SYSTEM_LONG:
+ case NF_DATETIME_SYSTEM_SHORT_HHMM:
+ nLng = LANGUAGE_SYSTEM;
+ break;
+ default: break;
+ }
+ return nLng;
+}
+
+// Globals
+
+/// field names
+std::vector<OUString>* SwFieldType::s_pFieldNames = nullptr;
+
+namespace
+{
+
+ const o3tl::enumarray<SwFieldIds,SwFieldTypesEnum> aTypeTab {
+ /* SwFieldIds::Database */ SwFieldTypesEnum::Database,
+ /* SwFieldIds::User */ SwFieldTypesEnum::User,
+ /* SwFieldIds::Filename */ SwFieldTypesEnum::Filename,
+ /* SwFieldIds::DatabaseName */ SwFieldTypesEnum::DatabaseName,
+ /* SwFieldIds::Date */ SwFieldTypesEnum::Date,
+ /* SwFieldIds::Time */ SwFieldTypesEnum::Time,
+ /* SwFieldIds::PageNumber */ SwFieldTypesEnum::PageNumber, // dynamic
+ /* SwFieldIds::Author */ SwFieldTypesEnum::Author,
+ /* SwFieldIds::Chapter */ SwFieldTypesEnum::Chapter,
+ /* SwFieldIds::DocStat */ SwFieldTypesEnum::DocumentStatistics,
+ /* SwFieldIds::GetExp */ SwFieldTypesEnum::Get, // dynamic
+ /* SwFieldIds::SetExp */ SwFieldTypesEnum::Set, // dynamic
+ /* SwFieldIds::GetRef */ SwFieldTypesEnum::GetRef,
+ /* SwFieldIds::HiddenText */ SwFieldTypesEnum::HiddenText,
+ /* SwFieldIds::Postit */ SwFieldTypesEnum::Postit,
+ /* SwFieldIds::FixDate */ SwFieldTypesEnum::FixedDate,
+ /* SwFieldIds::FixTime */ SwFieldTypesEnum::FixedTime,
+ /* SwFieldIds::Reg */ SwFieldTypesEnum::Begin, // old (no change since 2000)
+ /* SwFieldIds::VarReg */ SwFieldTypesEnum::Begin, // old (no change since 2000)
+ /* SwFieldIds::SetRef */ SwFieldTypesEnum::SetRef,
+ /* SwFieldIds::Input */ SwFieldTypesEnum::Input,
+ /* SwFieldIds::Macro */ SwFieldTypesEnum::Macro,
+ /* SwFieldIds::Dde */ SwFieldTypesEnum::DDE,
+ /* SwFieldIds::Table */ SwFieldTypesEnum::Formel,
+ /* SwFieldIds::HiddenPara */ SwFieldTypesEnum::HiddenParagraph,
+ /* SwFieldIds::DocInfo */ SwFieldTypesEnum::DocumentInfo,
+ /* SwFieldIds::TemplateName */ SwFieldTypesEnum::TemplateName,
+ /* SwFieldIds::DbNextSet */ SwFieldTypesEnum::DatabaseNextSet,
+ /* SwFieldIds::DbNumSet */ SwFieldTypesEnum::DatabaseNumberSet,
+ /* SwFieldIds::DbSetNumber */ SwFieldTypesEnum::DatabaseSetNumber,
+ /* SwFieldIds::ExtUser */ SwFieldTypesEnum::ExtendedUser,
+ /* SwFieldIds::RefPageSet */ SwFieldTypesEnum::SetRefPage,
+ /* SwFieldIds::RefPageGet */ SwFieldTypesEnum::GetRefPage,
+ /* SwFieldIds::Internet */ SwFieldTypesEnum::Internet,
+ /* SwFieldIds::JumpEdit */ SwFieldTypesEnum::JumpEdit,
+ /* SwFieldIds::Script */ SwFieldTypesEnum::Script,
+ /* SwFieldIds::DateTime */ SwFieldTypesEnum::Begin, // dynamic
+ /* SwFieldIds::TableOfAuthorities*/ SwFieldTypesEnum::Authority,
+ /* SwFieldIds::CombinedChars */ SwFieldTypesEnum::CombinedChars,
+ /* SwFieldIds::Dropdown */ SwFieldTypesEnum::Dropdown,
+ /* SwFieldIds::ParagraphSignature */ SwFieldTypesEnum::ParagraphSignature
+ };
+
+}
+
+const OUString & SwFieldType::GetTypeStr(SwFieldTypesEnum nTypeId)
+{
+ if (!s_pFieldNames)
+ GetFieldName_();
+
+ return (*SwFieldType::s_pFieldNames)[static_cast<int>(nTypeId)];
+}
+
+// each field references a field type that is unique for each document
+SwFieldType::SwFieldType( SwFieldIds nWhichId )
+ : sw::BroadcastingModify()
+ , m_nWhich(nWhichId)
+{
+}
+
+OUString SwFieldType::GetName() const
+{
+ return OUString();
+}
+
+void SwFieldType::QueryValue( uno::Any&, sal_uInt16 ) const
+{
+}
+void SwFieldType::PutValue( const uno::Any& , sal_uInt16 )
+{
+}
+
+void SwFieldType::PrintHiddenPara()
+{
+ const SwMsgPoolItem aHint(RES_HIDDENPARA_PRINT);
+ SwClientNotify(*this, sw::LegacyModifyHint(&aHint, nullptr));
+}
+
+void SwFieldType::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ if(!vFields.size())
+ return;
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldType"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+ for(const auto pFormatField: vFields)
+ pFormatField->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwFormatField* SwFieldType::FindFormatForField(const SwField* pField) const {
+ SwFormatField* pFormat = nullptr;
+ CallSwClientNotify(sw::FindFormatForFieldHint(pField, pFormat));
+ return pFormat;
+}
+
+SwFormatField* SwFieldType::FindFormatForPostItId(sal_uInt32 nPostItId) const {
+ SwFormatField* pFormat = nullptr;
+ CallSwClientNotify(sw::FindFormatForPostItIdHint(nPostItId, pFormat));
+ return pFormat;
+}
+
+void SwFieldType::CollectPostIts(std::vector<SwFormatField*>& rvFormatFields, IDocumentRedlineAccess const& rIDRA, const bool bHideRedlines)
+{
+ CallSwClientNotify(sw::CollectPostItsHint(rvFormatFields, rIDRA, bHideRedlines));
+}
+
+bool SwFieldType::HasHiddenInformationNotes() const
+{
+ bool bHasHiddenInformationNotes = false;
+ CallSwClientNotify(sw::HasHiddenInformationNotesHint(bHasHiddenInformationNotes));
+ return bHasHiddenInformationNotes;
+}
+
+void SwFieldType::GatherNodeIndex(std::vector<SwNodeOffset>& rvNodeIndex)
+{
+ CallSwClientNotify(sw::GatherNodeIndexHint(rvNodeIndex));
+}
+
+void SwFieldType::GatherRefFields(std::vector<SwGetRefField*>& rvRFields, const sal_uInt16 nTyp)
+{
+ CallSwClientNotify(sw::GatherRefFieldsHint(rvRFields, nTyp));
+}
+
+void SwFieldType::GatherFields(std::vector<SwFormatField*>& rvFields, bool bCollectOnlyInDocNodes) const
+{
+ CallSwClientNotify(sw::GatherFieldsHint(rvFields, bCollectOnlyInDocNodes));
+}
+
+void SwFieldType::GatherDdeTables(std::vector<SwDDETable*>& rvTables) const
+{
+ CallSwClientNotify(sw::GatherDdeTablesHint(rvTables));
+}
+
+void SwFieldType::UpdateFields()
+{
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+};
+
+void SwFieldTypes::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldTypes"));
+ sal_uInt16 nCount = size();
+ for (sal_uInt16 nType = 0; nType < nCount; ++nType)
+ (*this)[nType]->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Base class for all fields.
+// A field (multiple can exist) references a field type (can exists only once)
+SwField::SwField(
+ SwFieldType* pType,
+ sal_uInt32 nFormat,
+ LanguageType nLang,
+ bool bUseFieldValueCache)
+ : m_pType( pType )
+ , m_nFormat( nFormat )
+ , m_nLang( nLang )
+ , m_bUseFieldValueCache( bUseFieldValueCache )
+ , m_bIsAutomaticLanguage( true )
+{
+ assert(m_pType);
+}
+
+SwField::~SwField()
+{
+}
+
+// instead of indirectly via the type
+
+#ifdef DBG_UTIL
+SwFieldIds SwField::Which() const
+{
+ assert(m_pType);
+ return m_pType->Which();
+}
+#endif
+
+SwFieldTypesEnum SwField::GetTypeId() const
+{
+
+ SwFieldTypesEnum nRet;
+ switch (m_pType->Which())
+ {
+ case SwFieldIds::DateTime:
+ if (GetSubType() & FIXEDFLD)
+ nRet = GetSubType() & DATEFLD ? SwFieldTypesEnum::FixedDate : SwFieldTypesEnum::FixedTime;
+ else
+ nRet = GetSubType() & DATEFLD ? SwFieldTypesEnum::Date : SwFieldTypesEnum::Time;
+ break;
+ case SwFieldIds::GetExp:
+ nRet = nsSwGetSetExpType::GSE_FORMULA & GetSubType() ? SwFieldTypesEnum::Formel : SwFieldTypesEnum::Get;
+ break;
+
+ case SwFieldIds::HiddenText:
+ nRet = static_cast<SwFieldTypesEnum>(GetSubType());
+ break;
+
+ case SwFieldIds::SetExp:
+ if( nsSwGetSetExpType::GSE_SEQ & GetSubType() )
+ nRet = SwFieldTypesEnum::Sequence;
+ else if( static_cast<const SwSetExpField*>(this)->GetInputFlag() )
+ nRet = SwFieldTypesEnum::SetInput;
+ else
+ nRet = SwFieldTypesEnum::Set;
+ break;
+
+ case SwFieldIds::PageNumber:
+ {
+ auto nSubType = GetSubType();
+ if( PG_NEXT == nSubType )
+ nRet = SwFieldTypesEnum::NextPage;
+ else if( PG_PREV == nSubType )
+ nRet = SwFieldTypesEnum::PreviousPage;
+ else
+ nRet = SwFieldTypesEnum::PageNumber;
+ }
+ break;
+
+ default:
+ nRet = aTypeTab[ m_pType->Which() ];
+ }
+ return nRet;
+}
+
+/// get name or content
+OUString SwField::GetFieldName() const
+{
+ SwFieldTypesEnum nTypeId = GetTypeId();
+ if (SwFieldIds::DateTime == GetTyp()->Which())
+ {
+ nTypeId =
+ ((GetSubType() & DATEFLD) != 0) ? SwFieldTypesEnum::Date : SwFieldTypesEnum::Time;
+ }
+ OUString sRet = SwFieldType::GetTypeStr( nTypeId );
+ if (IsFixed())
+ {
+ sRet += " " + SwViewShell::GetShellRes()->aFixedStr;
+ }
+ return sRet;
+}
+
+OUString SwField::GetPar1() const
+{
+ return OUString();
+}
+
+OUString SwField::GetPar2() const
+{
+ return OUString();
+}
+
+OUString SwField::GetFormula() const
+{
+ return GetPar2();
+}
+
+void SwField::SetPar1(const OUString& )
+{}
+
+void SwField::SetPar2(const OUString& )
+{}
+
+sal_uInt16 SwField::GetSubType() const
+{
+ return 0;
+}
+
+void SwField::SetSubType(sal_uInt16 )
+{
+}
+
+bool SwField::QueryValue( uno::Any& rVal, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL4:
+ rVal <<= !m_bIsAutomaticLanguage;
+ break;
+ case FIELD_PROP_TITLE:
+ {
+ rVal <<= m_aTitle;
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwField::PutValue( const uno::Any& rVal, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL4:
+ {
+ bool bFixed = false;
+ if(rVal >>= bFixed)
+ m_bIsAutomaticLanguage = !bFixed;
+ }
+ break;
+ case FIELD_PROP_TITLE:
+ {
+ OUString aTitle;
+ if (rVal >>= aTitle)
+ {
+ m_aTitle = aTitle;
+ }
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+/** Set a new type
+ *
+ * This is needed/used for copying between documents.
+ * Needs to be always of the same type.
+ * @param pNewType The new type.
+ * @return The old type.
+ */
+SwFieldType* SwField::ChgTyp( SwFieldType* pNewType )
+{
+ assert(pNewType && pNewType->Which() == m_pType->Which());
+
+ SwFieldType* pOld = m_pType;
+ m_pType = pNewType;
+ return pOld;
+}
+
+/// Does the field have an action on a ClickHandler? (E.g. INetFields,...)
+bool SwField::HasClickHdl() const
+{
+ bool bRet = false;
+ switch (m_pType->Which())
+ {
+ case SwFieldIds::Internet:
+ case SwFieldIds::JumpEdit:
+ case SwFieldIds::GetRef:
+ case SwFieldIds::Macro:
+ case SwFieldIds::Input:
+ case SwFieldIds::Dropdown :
+ bRet = true;
+ break;
+
+ case SwFieldIds::SetExp:
+ bRet = static_cast<const SwSetExpField*>(this)->GetInputFlag();
+ break;
+
+ case SwFieldIds::TableOfAuthorities:
+ {
+ const auto pAuthorityField = static_cast<const SwAuthorityField*>(this);
+ bRet = pAuthorityField->HasURL();
+ break;
+ }
+
+ default: break;
+ }
+ return bRet;
+}
+
+void SwField::SetLanguage(LanguageType const nLang)
+{
+ m_nLang = nLang;
+}
+
+void SwField::ChangeFormat(sal_uInt32 const nFormat)
+{
+ m_nFormat = nFormat;
+}
+
+bool SwField::IsFixed() const
+{
+ bool bRet = false;
+ switch (m_pType->Which())
+ {
+ case SwFieldIds::FixDate:
+ case SwFieldIds::FixTime:
+ bRet = true;
+ break;
+
+ case SwFieldIds::DateTime:
+ bRet = 0 != (GetSubType() & FIXEDFLD);
+ break;
+
+ case SwFieldIds::ExtUser:
+ case SwFieldIds::Author:
+ bRet = 0 != (GetFormat() & AF_FIXED);
+ break;
+
+ case SwFieldIds::Filename:
+ bRet = 0 != (GetFormat() & FF_FIXED);
+ break;
+
+ case SwFieldIds::DocInfo:
+ bRet = 0 != (GetSubType() & DI_SUB_FIXED);
+ break;
+ default: break;
+ }
+ return bRet;
+}
+
+OUString
+SwField::ExpandField(bool const bCached, SwRootFrame const*const pLayout) const
+{
+ if ( m_bUseFieldValueCache )
+ {
+ if (!bCached) // #i85766# do not expand fields in clipboard documents
+ {
+ if (GetTypeId() == SwFieldTypesEnum::Authority)
+ {
+ const SwAuthorityField* pAuthorityField = static_cast<const SwAuthorityField*>(this);
+ m_Cache = pAuthorityField->ConditionalExpandAuthIdentifier(pLayout);
+ }
+ else
+ m_Cache = ExpandImpl(pLayout);
+ }
+ return m_Cache;
+ }
+
+ return ExpandImpl(pLayout);
+}
+
+std::unique_ptr<SwField> SwField::CopyField() const
+{
+ std::unique_ptr<SwField> pNew = Copy();
+ // #i85766# cache expansion of source (for clipboard)
+ // use this->cache, not this->Expand(): only text formatting calls Expand()
+ pNew->m_Cache = m_Cache;
+ pNew->m_bUseFieldValueCache = m_bUseFieldValueCache;
+
+ return pNew;
+}
+
+/// expand numbering
+OUString FormatNumber(sal_uInt32 nNum, SvxNumType nFormat, LanguageType nLang)
+{
+ if(SVX_NUM_PAGEDESC == nFormat)
+ return OUString::number( nNum );
+ SvxNumberType aNumber;
+
+ OSL_ENSURE(nFormat != SVX_NUM_NUMBER_NONE, "wrong number format" );
+
+ aNumber.SetNumberingType(nFormat);
+
+ if (nLang == LANGUAGE_NONE)
+ return aNumber.GetNumStr(nNum);
+ else
+ return aNumber.GetNumStr(nNum, LanguageTag::convertToLocale(nLang));
+}
+
+SwValueFieldType::SwValueFieldType(SwDoc *const pDoc, SwFieldIds const nWhichId)
+ : SwFieldType(nWhichId)
+ , m_pDoc(pDoc)
+ , m_bUseFormat(true)
+{
+}
+
+SwValueFieldType::SwValueFieldType( const SwValueFieldType& rTyp )
+ : SwFieldType(rTyp.Which())
+ , m_pDoc(rTyp.GetDoc())
+ , m_bUseFormat(rTyp.UseFormat())
+{
+}
+
+/// return value formatted as string
+OUString SwValueFieldType::ExpandValue( const double& rVal,
+ sal_uInt32 nFormat, LanguageType nLng) const
+{
+ if (rVal >= DBL_MAX) // error string for calculator
+ return SwViewShell::GetShellRes()->aCalc_Error;
+
+ OUString sExpand;
+ SvNumberFormatter* pFormatter = m_pDoc->GetNumberFormatter();
+ const Color* pCol = nullptr;
+
+ // Bug #60010
+ LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, nFormat, *pFormatter );
+
+ if( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && LANGUAGE_SYSTEM != nFormatLng )
+ {
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat);
+
+ if (pEntry && nLng != pEntry->GetLanguage())
+ {
+ sal_uInt32 nNewFormat = pFormatter->GetFormatForLanguageIfBuiltIn(nFormat,
+ nFormatLng);
+ if (nNewFormat == nFormat)
+ {
+ // probably user-defined format
+ OUString sFormat(pEntry->GetFormatstring());
+ sal_Int32 nDummy;
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+
+ pFormatter->PutandConvertEntry(sFormat, nDummy, nType, nFormat,
+ pEntry->GetLanguage(), nFormatLng, false);
+ }
+ else
+ nFormat = nNewFormat;
+ }
+ OSL_ENSURE(pEntry, "unknown number format!");
+ }
+
+ if( pFormatter->IsTextFormat( nFormat ) )
+ {
+ pFormatter->GetOutputString(DoubleToString(rVal, nFormatLng), nFormat,
+ sExpand, &pCol);
+ }
+ else
+ {
+ pFormatter->GetOutputString(rVal, nFormat, sExpand, &pCol);
+ }
+ return sExpand;
+}
+
+OUString SwValueFieldType::DoubleToString(const double &rVal,
+ sal_uInt32 nFormat) const
+{
+ SvNumberFormatter* pFormatter = m_pDoc->GetNumberFormatter();
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat);
+
+ if (!pEntry)
+ return OUString();
+
+ return DoubleToString(rVal, pEntry->GetLanguage());
+}
+
+OUString SwValueFieldType::DoubleToString( const double &rVal,
+ LanguageType nLng ) const
+{
+ SvNumberFormatter* pFormatter = m_pDoc->GetNumberFormatter();
+
+ // Bug #60010
+ if( nLng == LANGUAGE_NONE )
+ nLng = LANGUAGE_SYSTEM;
+
+ pFormatter->ChangeIntl( nLng ); // get separator in the correct language
+ return ::rtl::math::doubleToUString( rVal, rtl_math_StringFormat_F, 12,
+ pFormatter->GetNumDecimalSep()[0], true );
+}
+
+SwValueField::SwValueField( SwValueFieldType* pFieldType, sal_uInt32 nFormat,
+ LanguageType nLng, const double fVal )
+ : SwField(pFieldType, nFormat, nLng)
+ , m_fValue(fVal)
+{
+}
+
+SwValueField::SwValueField( const SwValueField& rField )
+ : SwField(rField)
+ , m_fValue(rField.GetValue())
+{
+}
+
+SwValueField::~SwValueField()
+{
+}
+
+/** Set a new type
+ *
+ * This is needed/used for copying between documents.
+ * Needs to be always of the same type.
+ * @param pNewType The new type.
+ * @return The old type.
+ */
+SwFieldType* SwValueField::ChgTyp( SwFieldType* pNewType )
+{
+ SwDoc* pNewDoc = static_cast<SwValueFieldType *>(pNewType)->GetDoc();
+ SwDoc* pDoc = GetDoc();
+
+ if( pNewDoc && pDoc && pDoc != pNewDoc)
+ {
+ SvNumberFormatter* pFormatter = pNewDoc->GetNumberFormatter();
+
+ if( pFormatter && pFormatter->HasMergeFormatTable() &&
+ static_cast<SwValueFieldType *>(GetTyp())->UseFormat() )
+ SetFormat(pFormatter->GetMergeFormatIndex( GetFormat() ));
+ }
+
+ return SwField::ChgTyp(pNewType);
+}
+
+/// get format in office language
+sal_uInt32 SwValueField::GetSystemFormat(SvNumberFormatter* pFormatter, sal_uInt32 nFormat)
+{
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nFormat);
+ LanguageType nLng = SvtSysLocale().GetLanguageTag().getLanguageType();
+
+ if (pEntry && nLng != pEntry->GetLanguage())
+ {
+ sal_uInt32 nNewFormat = pFormatter->GetFormatForLanguageIfBuiltIn(nFormat,
+ nLng);
+
+ if (nNewFormat == nFormat)
+ {
+ // probably user-defined format
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ sal_Int32 nDummy;
+
+ OUString sFormat(pEntry->GetFormatstring());
+
+ sal_uInt32 nTempFormat = nFormat;
+ pFormatter->PutandConvertEntry(sFormat, nDummy, nType,
+ nTempFormat, pEntry->GetLanguage(), nLng, true);
+ nFormat = nTempFormat;
+ }
+ else
+ nFormat = nNewFormat;
+ }
+
+ return nFormat;
+}
+
+void SwValueField::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwValueField"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_fValue"), BAD_CAST(OString::number(m_fValue).getStr()));
+ SwField::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/// set language of the format
+void SwValueField::SetLanguage( LanguageType nLng )
+{
+ if( IsAutomaticLanguage() &&
+ static_cast<SwValueFieldType *>(GetTyp())->UseFormat() &&
+ GetFormat() != SAL_MAX_UINT32 )
+ {
+ // Bug #60010
+ SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter();
+ LanguageType nFormatLng = ::lcl_GetLanguageOfFormat( nLng, GetFormat(),
+ *pFormatter );
+
+ if( (GetFormat() >= SV_COUNTRY_LANGUAGE_OFFSET ||
+ LANGUAGE_SYSTEM != nFormatLng ) &&
+ !(Which() == SwFieldIds::User && (GetSubType()&nsSwExtendedSubType::SUB_CMD) ) )
+ {
+ const SvNumberformat* pEntry = pFormatter->GetEntry(GetFormat());
+
+ if( pEntry && nFormatLng != pEntry->GetLanguage() )
+ {
+ sal_uInt32 nNewFormat = pFormatter->GetFormatForLanguageIfBuiltIn(
+ GetFormat(), nFormatLng );
+
+ if( nNewFormat == GetFormat() )
+ {
+ // probably user-defined format
+ SvNumFormatType nType = SvNumFormatType::DEFINED;
+ sal_Int32 nDummy;
+ OUString sFormat( pEntry->GetFormatstring() );
+ pFormatter->PutandConvertEntry( sFormat, nDummy, nType,
+ nNewFormat,
+ pEntry->GetLanguage(),
+ nFormatLng, false);
+ }
+ SetFormat( nNewFormat );
+ }
+ OSL_ENSURE(pEntry, "unknown number format!");
+ }
+ }
+
+ SwField::SetLanguage(nLng);
+}
+
+double SwValueField::GetValue() const
+{
+ return m_fValue;
+}
+
+void SwValueField::SetValue( const double& rVal )
+{
+ m_fValue = rVal;
+}
+
+SwFormulaField::SwFormulaField( SwValueFieldType* pFieldType, sal_uInt32 nFormat, const double fVal)
+ : SwValueField(pFieldType, nFormat, LANGUAGE_SYSTEM, fVal)
+{
+}
+
+SwFormulaField::SwFormulaField( const SwFormulaField& rField )
+ : SwValueField(static_cast<SwValueFieldType *>(rField.GetTyp()), rField.GetFormat(),
+ rField.GetLanguage(), rField.GetValue())
+{
+}
+
+OUString SwFormulaField::GetFormula() const
+{
+ return m_sFormula;
+}
+
+void SwFormulaField::SetFormula(const OUString& rStr)
+{
+ m_sFormula = rStr;
+
+ sal_uLong nFormat(GetFormat());
+
+ if( nFormat && SAL_MAX_UINT32 != nFormat )
+ {
+ sal_Int32 nPos = 0;
+ double fTmpValue;
+ if( SwCalc::Str2Double( rStr, nPos, fTmpValue, GetDoc() ) )
+ SwValueField::SetValue( fTmpValue );
+ }
+}
+
+void SwFormulaField::SetExpandedFormula( const OUString& rStr )
+{
+ sal_uInt32 nFormat(GetFormat());
+
+ if (nFormat && nFormat != SAL_MAX_UINT32 && static_cast<SwValueFieldType *>(GetTyp())->UseFormat())
+ {
+ double fTmpValue;
+
+ if (GetDoc()->IsNumberFormat(rStr, nFormat, fTmpValue))
+ {
+ SwValueField::SetValue(fTmpValue);
+
+ m_sFormula = static_cast<SwValueFieldType *>(GetTyp())->DoubleToString(fTmpValue, nFormat);
+ return;
+ }
+ }
+ m_sFormula = rStr;
+}
+
+OUString SwFormulaField::GetExpandedFormula() const
+{
+ sal_uInt32 nFormat(GetFormat());
+
+ if (nFormat && nFormat != SAL_MAX_UINT32 && static_cast<SwValueFieldType *>(GetTyp())->UseFormat())
+ {
+ OUString sFormattedValue;
+ const Color* pCol = nullptr;
+
+ SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter();
+
+ if (pFormatter->IsTextFormat(nFormat))
+ {
+ OUString sTempIn(static_cast<SwValueFieldType *>(GetTyp())->DoubleToString(GetValue(), nFormat));
+ pFormatter->GetOutputString(sTempIn, nFormat, sFormattedValue, &pCol);
+ }
+ else
+ {
+ pFormatter->GetOutputString(GetValue(), nFormat, sFormattedValue, &pCol);
+ }
+ return sFormattedValue;
+ }
+ else
+ return GetFormula();
+}
+
+OUString SwField::GetDescription() const
+{
+ return SwResId(STR_FIELD);
+}
+
+bool SwField::IsClickable() const
+{
+ switch (Which())
+ {
+ case SwFieldIds::JumpEdit:
+ case SwFieldIds::Macro:
+ case SwFieldIds::GetRef:
+ case SwFieldIds::Input:
+ case SwFieldIds::SetExp:
+ case SwFieldIds::Dropdown:
+ case SwFieldIds::TableOfAuthorities:
+ return true;
+ default: break;
+ }
+ return false;
+}
+
+void SwField::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwField"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nFormat"), BAD_CAST(OString::number(m_nFormat).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nLang"), BAD_CAST(OString::number(m_nLang.get()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_aTitle"), BAD_CAST(m_aTitle.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/flddat.cxx b/sw/source/core/fields/flddat.cxx
new file mode 100644
index 000000000..51246f6ee
--- /dev/null
+++ b/sw/source/core/fields/flddat.cxx
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/any.hxx>
+#include <o3tl/temporary.hxx>
+#include <tools/datetime.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+#include <doc.hxx>
+#include <fldbas.hxx>
+#include <flddat.hxx>
+#include <unofldmid.h>
+
+using namespace ::com::sun::star;
+
+SwDateTimeFieldType::SwDateTimeFieldType(SwDoc* pInitDoc)
+ : SwValueFieldType( pInitDoc, SwFieldIds::DateTime )
+{}
+
+std::unique_ptr<SwFieldType> SwDateTimeFieldType::Copy() const
+{
+ return std::make_unique<SwDateTimeFieldType>(GetDoc());
+}
+
+SwDateTimeField::SwDateTimeField(SwDateTimeFieldType* pInitType, sal_uInt16 nSub, sal_uLong nFormat, LanguageType nLng)
+ : SwValueField(pInitType, nFormat, nLng, 0.0),
+ m_nSubType(nSub),
+ m_nOffset(0)
+{
+ if (!nFormat)
+ {
+ SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter();
+ if (m_nSubType & DATEFLD)
+ ChangeFormat(pFormatter->GetFormatIndex(NF_DATE_SYSTEM_SHORT, GetLanguage()));
+ else
+ ChangeFormat(pFormatter->GetFormatIndex(NF_TIME_HHMMSS, GetLanguage()));
+ }
+ if (IsFixed())
+ {
+ DateTime aDateTime( DateTime::SYSTEM );
+ SetDateTime(aDateTime);
+ }
+}
+
+OUString SwDateTimeField::ExpandImpl(SwRootFrame const*const) const
+{
+ double fVal;
+
+ if (!(IsFixed()))
+ {
+ DateTime aDateTime( DateTime::SYSTEM );
+ fVal = GetDateTime(*GetDoc(), aDateTime);
+ }
+ else
+ fVal = GetValue();
+
+ if (m_nOffset)
+ fVal += m_nOffset * ( 60 / 86400.0 );
+
+ return ExpandValue(fVal, GetFormat(), GetLanguage());
+}
+
+std::unique_ptr<SwField> SwDateTimeField::Copy() const
+{
+ std::unique_ptr<SwDateTimeField> pTmp(
+ new SwDateTimeField(static_cast<SwDateTimeFieldType*>(GetTyp()), m_nSubType,
+ GetFormat(), GetLanguage()) );
+
+ pTmp->SetValue(GetValue());
+ pTmp->SetOffset(m_nOffset);
+ pTmp->SetAutomaticLanguage(IsAutomaticLanguage());
+
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+sal_uInt16 SwDateTimeField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwDateTimeField::SetSubType(sal_uInt16 nType)
+{
+ m_nSubType = nType;
+}
+
+void SwDateTimeField::SetPar2(const OUString& rStr)
+{
+ m_nOffset = rStr.toInt32();
+}
+
+OUString SwDateTimeField::GetPar2() const
+{
+ if (m_nOffset)
+ return OUString::number(m_nOffset);
+ return OUString();
+}
+
+void SwDateTimeField::SetDateTime(const DateTime& rDT)
+{
+ SetValue(GetDateTime(*GetDoc(), rDT));
+}
+
+double SwDateTimeField::GetDateTime(SwDoc& rDoc, const DateTime& rDT)
+{
+ SvNumberFormatter* pFormatter = rDoc.GetNumberFormatter();
+ const Date& rNullDate = pFormatter->GetNullDate();
+
+ double fResult = rDT - DateTime(rNullDate);
+
+ return fResult;
+}
+
+double SwDateTimeField::GetValue() const
+{
+ if (IsFixed())
+ return SwValueField::GetValue();
+ else
+ return GetDateTime(*GetDoc(), DateTime( DateTime::SYSTEM ));
+}
+
+Date SwDateTimeField::GetDate() const
+{
+ SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter();
+ const Date& rNullDate = pFormatter->GetNullDate();
+
+ tools::Long nVal = static_cast<tools::Long>( GetValue() );
+
+ Date aDate = rNullDate + nVal;
+
+ return aDate;
+}
+
+tools::Time SwDateTimeField::GetTime() const
+{
+ double fFract = modf(GetValue(), &o3tl::temporary(double()));
+ DateTime aDT( DateTime::EMPTY );
+ aDT.AddTime(fFract);
+ return static_cast<tools::Time>(aDT);
+}
+
+bool SwDateTimeField::QueryValue( uno::Any& rVal, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ rVal <<= IsFixed();
+ break;
+ case FIELD_PROP_BOOL2:
+ rVal <<= (m_nSubType & DATEFLD) != 0;
+ break;
+ case FIELD_PROP_FORMAT:
+ rVal <<= static_cast<sal_Int32>(GetFormat());
+ break;
+ case FIELD_PROP_SUBTYPE:
+ rVal <<= static_cast<sal_Int32>(m_nOffset);
+ break;
+ case FIELD_PROP_DATE_TIME:
+ {
+ DateTime aDateTime(GetDate(), GetTime());
+ rVal <<= aDateTime.GetUNODateTime();
+ }
+ break;
+ default:
+ return SwField::QueryValue(rVal, nWhichId);
+ }
+ return true;
+}
+
+bool SwDateTimeField::PutValue( const uno::Any& rVal, sal_uInt16 nWhichId )
+{
+ sal_Int32 nTmp = 0;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ if(*o3tl::doAccess<bool>(rVal))
+ m_nSubType |= FIXEDFLD;
+ else
+ m_nSubType &= ~FIXEDFLD;
+ break;
+ case FIELD_PROP_BOOL2:
+ m_nSubType &= ~(DATEFLD|TIMEFLD);
+ m_nSubType |= *o3tl::doAccess<bool>(rVal) ? DATEFLD : TIMEFLD;
+ break;
+ case FIELD_PROP_FORMAT:
+ rVal >>= nTmp;
+ ChangeFormat(nTmp);
+ break;
+ case FIELD_PROP_SUBTYPE:
+ rVal >>= nTmp;
+ m_nOffset = nTmp;
+ break;
+ case FIELD_PROP_DATE_TIME:
+ {
+ util::DateTime aDateTimeValue;
+ if(!(rVal >>= aDateTimeValue))
+ return false;
+ DateTime aDateTime(aDateTimeValue);
+ SetDateTime(aDateTime);
+ }
+ break;
+ default:
+ return SwField::PutValue(rVal, nWhichId);
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/flddropdown.cxx b/sw/source/core/fields/flddropdown.cxx
new file mode 100644
index 000000000..57106978e
--- /dev/null
+++ b/sw/source/core/fields/flddropdown.cxx
@@ -0,0 +1,217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <flddropdown.hxx>
+
+#include <algorithm>
+
+#include <svl/poolitem.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <unofldmid.h>
+
+using namespace com::sun::star;
+
+using std::vector;
+
+SwDropDownFieldType::SwDropDownFieldType()
+ : SwFieldType(SwFieldIds::Dropdown)
+{
+}
+
+SwDropDownFieldType::~SwDropDownFieldType()
+{
+}
+
+std::unique_ptr<SwFieldType> SwDropDownFieldType::Copy() const
+{
+ return std::make_unique<SwDropDownFieldType>();
+}
+
+SwDropDownField::SwDropDownField(SwFieldType * pTyp)
+ : SwField(pTyp, 0, LANGUAGE_SYSTEM)
+{
+}
+
+SwDropDownField::SwDropDownField(const SwDropDownField & rSrc)
+ : SwField(rSrc.GetTyp(), rSrc.GetFormat(), rSrc.GetLanguage()),
+ m_aValues(rSrc.m_aValues), m_aSelectedItem(rSrc.m_aSelectedItem),
+ m_aName(rSrc.m_aName), m_aHelp(rSrc.m_aHelp), m_aToolTip(rSrc.m_aToolTip)
+{
+}
+
+SwDropDownField::~SwDropDownField()
+{
+}
+
+OUString SwDropDownField::ExpandImpl(SwRootFrame const*const) const
+{
+ OUString sSelect = GetSelectedItem();
+ if (sSelect.isEmpty())
+ {
+ vector<OUString>::const_iterator aIt = m_aValues.begin();
+ if ( aIt != m_aValues.end())
+ sSelect = *aIt;
+ }
+ // if still no list value is available a default text of 10 spaces is to be set
+ if (sSelect.isEmpty())
+ sSelect = " ";
+ return sSelect;
+}
+
+std::unique_ptr<SwField> SwDropDownField::Copy() const
+{
+ return std::make_unique<SwDropDownField>(*this);
+}
+
+OUString SwDropDownField::GetPar1() const
+{
+ return GetSelectedItem();
+}
+
+OUString SwDropDownField::GetPar2() const
+{
+ return GetName();
+}
+
+void SwDropDownField::SetPar1(const OUString & rStr)
+{
+ SetSelectedItem(rStr);
+}
+
+void SwDropDownField::SetPar2(const OUString & rName)
+{
+ SetName(rName);
+}
+
+void SwDropDownField::SetItems(vector<OUString> && rItems)
+{
+ m_aValues = std::move(rItems);
+ m_aSelectedItem.clear();
+}
+
+void SwDropDownField::SetItems(const uno::Sequence<OUString> & rItems)
+{
+ m_aValues.clear();
+
+ comphelper::sequenceToContainer(m_aValues, rItems);
+
+ m_aSelectedItem.clear();
+}
+
+uno::Sequence<OUString> SwDropDownField::GetItemSequence() const
+{
+ return comphelper::containerToSequence(m_aValues);
+}
+
+
+void SwDropDownField::SetSelectedItem(const OUString & rItem)
+{
+ vector<OUString>::const_iterator aIt =
+ std::find(m_aValues.begin(), m_aValues.end(), rItem);
+
+ if (aIt != m_aValues.end())
+ m_aSelectedItem = *aIt;
+ else
+ m_aSelectedItem.clear();
+}
+
+void SwDropDownField::SetName(const OUString & rName)
+{
+ m_aName = rName;
+}
+
+void SwDropDownField::SetHelp(const OUString & rHelp)
+{
+ m_aHelp = rHelp;
+}
+
+void SwDropDownField::SetToolTip(const OUString & rToolTip)
+{
+ m_aToolTip = rToolTip;
+}
+
+bool SwDropDownField::QueryValue(::uno::Any &rVal, sal_uInt16 nWhich) const
+{
+ nWhich &= ~CONVERT_TWIPS;
+ switch( nWhich )
+ {
+ case FIELD_PROP_PAR1:
+ rVal <<= m_aSelectedItem;
+ break;
+ case FIELD_PROP_PAR2:
+ rVal <<= m_aName;
+ break;
+ case FIELD_PROP_PAR3:
+ rVal <<= m_aHelp;
+ break;
+ case FIELD_PROP_PAR4:
+ rVal <<= m_aToolTip;
+ break;
+ case FIELD_PROP_STRINGS:
+ rVal <<= GetItemSequence();
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwDropDownField::PutValue(const uno::Any &rVal,
+ sal_uInt16 nWhich)
+{
+ switch( nWhich )
+ {
+ case FIELD_PROP_PAR1:
+ {
+ OUString aTmpStr;
+ rVal >>= aTmpStr;
+
+ SetSelectedItem(aTmpStr);
+ }
+ break;
+
+ case FIELD_PROP_PAR2:
+ rVal >>= m_aName;
+ break;
+
+ case FIELD_PROP_PAR3:
+ rVal >>= m_aHelp;
+ break;
+
+ case FIELD_PROP_PAR4:
+ rVal >>= m_aToolTip;
+ break;
+
+ case FIELD_PROP_STRINGS:
+ {
+ uno::Sequence<OUString> aSeq;
+ rVal >>= aSeq;
+ SetItems(aSeq);
+ }
+ break;
+
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/fldlst.cxx b/sw/source/core/fields/fldlst.cxx
new file mode 100644
index 000000000..24eb3e1a4
--- /dev/null
+++ b/sw/source/core/fields/fldlst.cxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editsh.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docary.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <expfld.hxx>
+#include <docfld.hxx>
+#include <ndtxt.hxx>
+
+#include <osl/diagnose.h>
+
+// sort input values
+
+SwInputFieldList::SwInputFieldList( SwEditShell* pShell, bool bBuildTmpLst )
+ : mpSh(pShell)
+{
+ // create sorted list of all input fields
+ mpSrtLst.reset( new SetGetExpFields );
+
+ const SwFieldTypes& rFieldTypes = *mpSh->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+ const size_t nSize = rFieldTypes.size();
+
+ // iterate over all types
+ std::vector<SwFormatField*> vFields;
+ for(size_t i = 0; i < nSize; ++i)
+ {
+ SwFieldType* pFieldType = rFieldTypes[ i ].get();
+ const SwFieldIds nType = pFieldType->Which();
+ if(SwFieldIds::SetExp == nType || SwFieldIds::Input == nType || SwFieldIds::Dropdown == nType)
+ pFieldType->GatherFields(vFields);
+ }
+ for(auto pFormatField: vFields)
+ {
+ auto pSetExpField = dynamic_cast<SwSetExpField*>(pFormatField->GetField());
+ if(pSetExpField && !pSetExpField->GetInputFlag())
+ continue;
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ if(bBuildTmpLst)
+ maTmpLst.insert(pTextField);
+ else
+ {
+ SwNodeIndex aIdx(pTextField->GetTextNode());
+ std::unique_ptr<SetGetExpField> pNew(new SetGetExpField(aIdx, pTextField));
+ mpSrtLst->insert(std::move(pNew));
+ }
+ }
+}
+
+SwInputFieldList::~SwInputFieldList()
+{
+}
+
+size_t SwInputFieldList::Count() const
+{
+ return mpSrtLst->size();
+}
+
+// get field from list in sorted order
+SwField* SwInputFieldList::GetField(size_t nId)
+{
+ const SwTextField* pTextField = (*mpSrtLst)[ nId ]->GetTextField();
+ OSL_ENSURE( pTextField, "no TextField" );
+ return const_cast<SwField*>(pTextField->GetFormatField().GetField());
+}
+
+/// save cursor
+void SwInputFieldList::PushCursor()
+{
+ mpSh->Push();
+ mpSh->ClearMark();
+}
+
+/// get cursor
+void SwInputFieldList::PopCursor()
+{
+ mpSh->Pop(SwCursorShell::PopMode::DeleteCurrent);
+}
+
+/// go to position of a field
+void SwInputFieldList::GotoFieldPos(size_t nId)
+{
+ mpSh->StartAllAction();
+ (*mpSrtLst)[ nId ]->GetPosOfContent( *mpSh->GetCursor()->GetPoint() );
+ mpSh->EndAllAction();
+}
+
+/** Compare TmpLst with current fields.
+ *
+ * All new ones are added to SortList so that they can be updated.
+ * For text blocks: update only input fields.
+ *
+ * @return true if not empty
+ */
+bool SwInputFieldList::BuildSortLst()
+{
+ const SwFieldTypes& rFieldTypes = *mpSh->GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+ const size_t nSize = rFieldTypes.size();
+
+ // iterate over all types
+ std::vector<SwFormatField*> vFields;
+ for(size_t i = 0; i < nSize; ++i)
+ {
+ SwFieldType* pFieldType = rFieldTypes[ i ].get();
+ const SwFieldIds nType = pFieldType->Which();
+ if(SwFieldIds::SetExp == nType || SwFieldIds::Input == nType)
+ pFieldType->GatherFields(vFields);
+ }
+ for(auto pFormatField: vFields)
+ {
+ auto pSetExpField = dynamic_cast<SwSetExpField*>(pFormatField->GetField());
+ if(pSetExpField && !pSetExpField->GetInputFlag())
+ continue;
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ // not in TempList, thus add to SortList
+ auto it = maTmpLst.find(pTextField);
+ if(maTmpLst.end() == it)
+ {
+ SwNodeIndex aIdx(pTextField->GetTextNode());
+ std::unique_ptr<SetGetExpField> pNew(new SetGetExpField(aIdx, pTextField ));
+ mpSrtLst->insert(std::move(pNew));
+ }
+ else
+ maTmpLst.erase(it);
+ }
+
+ // the pointers are not needed anymore
+ maTmpLst.clear();
+ return !mpSrtLst->empty();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/macrofld.cxx b/sw/source/core/fields/macrofld.cxx
new file mode 100644
index 000000000..2430a859a
--- /dev/null
+++ b/sw/source/core/fields/macrofld.cxx
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <docufld.hxx>
+#include <unofldmid.h>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XVndSunStarScriptUrl.hpp>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+SwMacroFieldType::SwMacroFieldType(SwDoc& rDocument)
+ : SwFieldType( SwFieldIds::Macro )
+ , m_rDoc(rDocument)
+{
+}
+
+std::unique_ptr<SwFieldType> SwMacroFieldType::Copy() const
+{
+ return std::make_unique<SwMacroFieldType>(m_rDoc);
+}
+
+SwMacroField::SwMacroField(SwMacroFieldType* pInitType,
+ const OUString& rLibAndName, const OUString& rText) :
+ SwField(pInitType), m_aMacro(rLibAndName), m_aText(rText), m_bIsScriptURL(false)
+{
+ m_bIsScriptURL = isScriptURL(m_aMacro);
+}
+
+OUString SwMacroField::ExpandImpl(SwRootFrame const*const) const
+{
+ return m_aText ;
+}
+
+std::unique_ptr<SwField> SwMacroField::Copy() const
+{
+ return std::make_unique<SwMacroField>(static_cast<SwMacroFieldType*>(GetTyp()), m_aMacro, m_aText);
+}
+
+OUString SwMacroField::GetFieldName() const
+{
+ return GetTyp()->GetName() + " " + m_aMacro;
+}
+
+OUString SwMacroField::GetLibName() const
+{
+ // if it is a Scripting Framework macro return an empty string
+ if (m_bIsScriptURL)
+ {
+ return OUString();
+ }
+
+ if (!m_aMacro.isEmpty())
+ {
+ sal_Int32 nPos = m_aMacro.getLength();
+
+ for (sal_Int32 i = 0; i < 3 && nPos > 0; i++)
+ while (m_aMacro[--nPos] != '.' && nPos > 0) ;
+
+ return m_aMacro.copy(0, nPos);
+ }
+
+ OSL_FAIL("No LibName");
+ return OUString();
+}
+
+OUString SwMacroField::GetMacroName() const
+{
+ if (!m_aMacro.isEmpty())
+ {
+ if (m_bIsScriptURL)
+ {
+ return m_aMacro;
+ }
+ else
+ {
+ sal_Int32 nPos = m_aMacro.getLength();
+
+ for (sal_Int32 i = 0; i < 3 && nPos > 0; i++)
+ while (m_aMacro[--nPos] != '.' && nPos > 0) ;
+
+ return m_aMacro.copy( ++nPos );
+ }
+ }
+
+ OSL_FAIL("No MacroName");
+ return OUString();
+}
+
+SvxMacro SwMacroField::GetSvxMacro() const
+{
+ if (m_bIsScriptURL)
+ {
+ return SvxMacro(m_aMacro, OUString(), EXTENDED_STYPE);
+ }
+ else
+ {
+ return SvxMacro(GetMacroName(), GetLibName(), STARBASIC);
+ }
+}
+
+/// LibName and MacroName
+void SwMacroField::SetPar1(const OUString& rStr)
+{
+ m_aMacro = rStr;
+ m_bIsScriptURL = isScriptURL(m_aMacro);
+}
+
+/// Get macro
+OUString SwMacroField::GetPar1() const
+{
+ return m_aMacro;
+}
+
+/// set macro text
+void SwMacroField::SetPar2(const OUString& rStr)
+{
+ m_aText = rStr;
+}
+
+/// get macro text
+OUString SwMacroField::GetPar2() const
+{
+ return m_aText;
+}
+
+bool SwMacroField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= GetMacroName();
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_aText;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= GetLibName();
+ break;
+ case FIELD_PROP_PAR4:
+ rAny <<= m_bIsScriptURL ? GetMacroName() : OUString();
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwMacroField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ OUString sTmp;
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= sTmp;
+ CreateMacroString( m_aMacro, sTmp, GetLibName());
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_aText;
+ break;
+ case FIELD_PROP_PAR3:
+ rAny >>= sTmp;
+ CreateMacroString(m_aMacro, GetMacroName(), sTmp );
+ break;
+ case FIELD_PROP_PAR4:
+ rAny >>= m_aMacro;
+ m_bIsScriptURL = isScriptURL(m_aMacro);
+ break;
+ default:
+ assert(false);
+ }
+
+ return true;
+}
+
+/// create an internally used macro name from the library and macro name parts
+void SwMacroField::CreateMacroString(
+ OUString& rMacro,
+ std::u16string_view rMacroName,
+ const OUString& rLibraryName )
+{
+ // concatenate library and name; use dot only if both strings have content
+ rMacro = rLibraryName;
+ if ( !rLibraryName.isEmpty() && !rMacroName.empty() )
+ rMacro += ".";
+ rMacro += rMacroName;
+}
+
+bool SwMacroField::isScriptURL( const OUString& str )
+{
+ try
+ {
+ uno::Reference<uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference<uri::XUriReferenceFactory> xFactory = uri::UriReferenceFactory::create(xContext);
+ uno::Reference<uri::XVndSunStarScriptUrl> xUrl(xFactory->parse(str), uno::UNO_QUERY);
+ return xUrl.is();
+ }
+ catch (...)
+ {
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/postithelper.cxx b/sw/source/core/fields/postithelper.cxx
new file mode 100644
index 000000000..26d81b373
--- /dev/null
+++ b/sw/source/core/fields/postithelper.cxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <postithelper.hxx>
+#include <PostItMgr.hxx>
+#include <AnnotationWin.hxx>
+
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <ndtxt.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <redline.hxx>
+#include <scriptinfo.hxx>
+#include <calbck.hxx>
+#include <IMark.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+#include <fmtanchr.hxx>
+
+class Point;
+
+namespace
+{
+/// Checks if pAnnotationMark covers exactly rAnchorPos (the comment anchor).
+bool AnnotationMarkCoversCommentAnchor(const sw::mark::IMark* pAnnotationMark,
+ const SwPosition& rAnchorPos)
+{
+ if (!pAnnotationMark)
+ {
+ return false;
+ }
+
+ const SwPosition& rMarkStart = pAnnotationMark->GetMarkStart();
+ const SwPosition& rMarkEnd = pAnnotationMark->GetMarkEnd();
+
+ if (rMarkStart != rAnchorPos)
+ {
+ // This can be the as-char case: the comment placeholder character is exactly between the
+ // annotation mark start and end.
+ SwPosition aPosition(rMarkStart);
+ ++aPosition.nContent;
+ if (aPosition != rAnchorPos)
+ {
+ return false;
+ }
+
+ ++aPosition.nContent;
+ if (aPosition != rMarkEnd)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ if (rMarkStart.nNode != rMarkEnd.nNode)
+ {
+ return false;
+ }
+
+ return rMarkEnd.nContent.GetIndex() == rMarkStart.nContent.GetIndex() + 1;
+}
+
+/**
+ * Finds the first draw object of rTextFrame which has the same anchor position as the start of
+ * rAnnotationMark.
+ */
+SwAnchoredObject* GetAnchoredObjectOfAnnotationMark(const sw::mark::IMark& rAnnotationMark,
+ const SwTextFrame& rTextFrame)
+{
+ const SwSortedObjs* pAnchored = rTextFrame.GetDrawObjs();
+ if (!pAnchored)
+ {
+ return nullptr;
+ }
+
+ for (SwAnchoredObject* pObject : *pAnchored)
+ {
+ SwFrameFormat& rFrameFormat = pObject->GetFrameFormat();
+ const SwPosition* pFrameAnchor = rFrameFormat.GetAnchor().GetContentAnchor();
+ if (!pFrameAnchor)
+ {
+ continue;
+ }
+
+ if (rAnnotationMark.GetMarkStart() == *pFrameAnchor)
+ {
+ return pObject;
+ }
+ }
+
+ return nullptr;
+}
+}
+
+SwSidebarItem::SwSidebarItem(const bool aFocus)
+ : mpPostIt(nullptr)
+ , mbShow(true)
+ , mbFocus(aFocus)
+ , mbPendingLayout(false)
+ , mLayoutStatus(SwPostItHelper::INVISIBLE)
+{
+}
+
+SwSidebarItem::~SwSidebarItem() {}
+
+SwPostItHelper::SwLayoutStatus SwPostItHelper::getLayoutInfos(
+ SwLayoutInfo& o_rInfo,
+ const SwPosition& rAnchorPos,
+ const sw::mark::IMark* pAnnotationMark )
+{
+ SwLayoutStatus aRet = INVISIBLE;
+ SwTextNode* pTextNode = rAnchorPos.nNode.GetNode().GetTextNode();
+ if ( pTextNode == nullptr )
+ return aRet;
+
+ SwIterator<SwTextFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
+ for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame != nullptr; pTextFrame = aIter.Next() )
+ {
+ if( !pTextFrame->IsFollow() )
+ {
+ pTextFrame = pTextFrame->GetFrameAtPos( rAnchorPos );
+ SwPageFrame *pPage = pTextFrame ? pTextFrame->FindPageFrame() : nullptr;
+ if ( pPage != nullptr && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
+ {
+ aRet = VISIBLE;
+
+ o_rInfo.mpAnchorFrame = pTextFrame;
+ {
+ DisableCallbackAction a(*pTextFrame->getRootFrame());
+ bool bPositionFromCommentAnchor = true;
+ if (AnnotationMarkCoversCommentAnchor(pAnnotationMark, rAnchorPos))
+ {
+ SwAnchoredObject* pFrame
+ = GetAnchoredObjectOfAnnotationMark(*pAnnotationMark, *pTextFrame);
+ if (pFrame)
+ {
+ o_rInfo.mPosition = pFrame->GetObjRect();
+ bPositionFromCommentAnchor = false;
+ }
+ }
+ if (bPositionFromCommentAnchor)
+ {
+ pTextFrame->GetCharRect(o_rInfo.mPosition, rAnchorPos, nullptr, false);
+ }
+ o_rInfo.mPositionFromCommentAnchor = bPositionFromCommentAnchor;
+ }
+ if (pAnnotationMark != nullptr)
+ {
+ const SwPosition& rAnnotationStartPos = pAnnotationMark->GetMarkStart();
+ o_rInfo.mnStartNodeIdx = rAnnotationStartPos.nNode.GetIndex();
+ o_rInfo.mnStartContent = rAnnotationStartPos.nContent.GetIndex();
+ }
+ else
+ {
+ o_rInfo.mnStartNodeIdx = SwNodeOffset(0);
+ o_rInfo.mnStartContent = -1;
+ }
+ o_rInfo.mPageFrame = pPage->getFrameArea();
+ o_rInfo.mPagePrtArea = pPage->getFramePrintArea();
+ o_rInfo.mPagePrtArea.Pos() += o_rInfo.mPageFrame.Pos();
+ o_rInfo.mnPageNumber = pPage->GetPhyPageNum();
+ o_rInfo.meSidebarPosition = pPage->SidebarPosition();
+ o_rInfo.mRedlineAuthor = 0;
+
+ const IDocumentRedlineAccess& rIDRA = pTextNode->getIDocumentRedlineAccess();
+ if( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) )
+ {
+ const SwRangeRedline* pRedline = rIDRA.GetRedline( rAnchorPos, nullptr );
+ if( pRedline )
+ {
+ if( RedlineType::Insert == pRedline->GetType() )
+ aRet = INSERTED;
+ else if( RedlineType::Delete == pRedline->GetType() )
+ {
+ bool bDeleted = pAnnotationMark == nullptr;
+ if( !bDeleted )
+ {
+ IDocumentMarkAccess& rDMA(*pTextNode->GetDoc().getIDocumentMarkAccess());
+ IDocumentMarkAccess::const_iterator_t pAnnotationBookmark =
+ rDMA.findAnnotationBookmark(pAnnotationMark->GetName());
+ // tdf#140980 only really deleted, if there is no helper bookmark
+ // in ChangesInMargin mode
+ if ( pAnnotationBookmark == rDMA.getBookmarksEnd() )
+ bDeleted = true;
+ }
+ if ( bDeleted )
+ aRet = DELETED;
+ }
+ o_rInfo.mRedlineAuthor = pRedline->GetAuthor();
+ }
+ }
+ }
+ }
+ }
+
+ return ( (aRet==VISIBLE) && SwScriptInfo::IsInHiddenRange( *pTextNode , rAnchorPos.nContent.GetIndex()) )
+ ? HIDDEN
+ : aRet;
+}
+
+tools::Long SwPostItHelper::getLayoutHeight( const SwRootFrame* pRoot )
+{
+ tools::Long nRet = pRoot ? pRoot->getFrameArea().Height() : 0;
+ return nRet;
+}
+
+void SwPostItHelper::setSidebarChanged( SwRootFrame* pRoot, bool bBrowseMode )
+{
+ if( pRoot )
+ {
+ pRoot->SetSidebarChanged();
+ if( bBrowseMode )
+ pRoot->InvalidateBrowseWidth();
+ }
+}
+
+tools::ULong SwPostItHelper::getPageInfo( SwRect& rPageFrame, const SwRootFrame* pRoot, const Point& rPoint )
+{
+ tools::ULong nRet = 0;
+ const SwFrame* pPage = pRoot->GetPageAtPos( rPoint, nullptr, true );
+ if( pPage )
+ {
+ nRet = pPage->GetPhyPageNum();
+ rPageFrame = pPage->getFrameArea();
+ }
+ return nRet;
+}
+
+SwPosition SwAnnotationItem::GetAnchorPosition() const
+{
+ SwTextField* pTextField = mrFormatField.GetTextField();
+ SwTextNode* pTextNode = pTextField->GetpTextNode();
+
+ SwPosition aPos( *pTextNode );
+ aPos.nContent.Assign( pTextNode, pTextField->GetStart() );
+ return aPos;
+}
+
+bool SwAnnotationItem::UseElement(SwRootFrame const& rLayout,
+ IDocumentRedlineAccess const& rIDRA)
+{
+ return mrFormatField.IsFieldInDoc()
+ && (!rLayout.IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(rIDRA, *mrFormatField.GetTextField()));
+}
+
+VclPtr<sw::annotation::SwAnnotationWin> SwAnnotationItem::GetSidebarWindow(
+ SwEditWin& rEditWin,
+ SwPostItMgr& aMgr)
+{
+ return VclPtr<sw::annotation::SwAnnotationWin>::Create( rEditWin,
+ aMgr,
+ *this,
+ &mrFormatField );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/reffld.cxx b/sw/source/core/fields/reffld.cxx
new file mode 100644
index 000000000..a79c314a3
--- /dev/null
+++ b/sw/source/core/fields/reffld.cxx
@@ -0,0 +1,1500 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/text/ReferenceFieldPart.hpp>
+#include <com/sun/star/text/ReferenceFieldSource.hpp>
+#include <o3tl/unreachable.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <pam.hxx>
+#include <cntfrm.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <modeltoviewhelper.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <txtftn.hxx>
+#include <fmtrfmrk.hxx>
+#include <txtrfmrk.hxx>
+#include <fmtftn.hxx>
+#include <ndtxt.hxx>
+#include <chpfld.hxx>
+#include <reffld.hxx>
+#include <expfld.hxx>
+#include <txtfrm.hxx>
+#include <flyfrm.hxx>
+#include <pagedesc.hxx>
+#include <IMark.hxx>
+#include <crossrefbookmark.hxx>
+#include <ftnidx.hxx>
+#include <viewsh.hxx>
+#include <unofldmid.h>
+#include <SwStyleNameMapper.hxx>
+#include <shellres.hxx>
+#include <poolfmt.hxx>
+#include <strings.hrc>
+#include <numrule.hxx>
+#include <SwNodeNum.hxx>
+#include <calbck.hxx>
+
+#include <cstddef>
+#include <memory>
+#include <vector>
+#include <set>
+#include <string_view>
+#include <map>
+#include <algorithm>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::lang;
+
+static std::pair<OUString, bool> MakeRefNumStr(SwRootFrame const* pLayout,
+ const SwTextNode& rTextNodeOfField,
+ const SwTextNode& rTextNodeOfReferencedItem,
+ sal_uInt32 nRefNumFormat);
+
+static void lcl_GetLayTree( const SwFrame* pFrame, std::vector<const SwFrame*>& rArr )
+{
+ while( pFrame )
+ {
+ if( pFrame->IsBodyFrame() ) // unspectacular
+ pFrame = pFrame->GetUpper();
+ else
+ {
+ rArr.push_back( pFrame );
+
+ // this is the last page
+ if( pFrame->IsPageFrame() )
+ break;
+
+ if( pFrame->IsFlyFrame() )
+ pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ else
+ pFrame = pFrame->GetUpper();
+ }
+ }
+}
+
+bool IsFrameBehind( const SwTextNode& rMyNd, sal_Int32 nMySttPos,
+ const SwTextNode& rBehindNd, sal_Int32 nSttPos )
+{
+ const SwTextFrame * pMyFrame = static_cast<SwTextFrame*>(rMyNd.getLayoutFrame(
+ rMyNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr));
+ const SwTextFrame * pFrame = static_cast<SwTextFrame*>(rBehindNd.getLayoutFrame(
+ rBehindNd.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr));
+
+ if( !pFrame || !pMyFrame)
+ return false;
+
+ TextFrameIndex const nMySttPosIndex(pMyFrame->MapModelToView(&rMyNd, nMySttPos));
+ TextFrameIndex const nSttPosIndex(pFrame->MapModelToView(&rBehindNd, nSttPos));
+ while (pFrame && !pFrame->IsInside(nSttPosIndex))
+ pFrame = pFrame->GetFollow();
+ while (pMyFrame && !pMyFrame->IsInside(nMySttPosIndex))
+ pMyFrame = pMyFrame->GetFollow();
+
+ if( !pFrame || !pMyFrame || pFrame == pMyFrame )
+ return false;
+
+ std::vector<const SwFrame*> aRefArr, aArr;
+ ::lcl_GetLayTree( pFrame, aRefArr );
+ ::lcl_GetLayTree( pMyFrame, aArr );
+
+ size_t nRefCnt = aRefArr.size() - 1, nCnt = aArr.size() - 1;
+ bool bVert = false;
+ bool bR2L = false;
+
+ // Loop as long as a frame does not equal?
+ while( nRefCnt && nCnt && aRefArr[ nRefCnt ] == aArr[ nCnt ] )
+ {
+ const SwFrame* pTmpFrame = aArr[ nCnt ];
+ bVert = pTmpFrame->IsVertical();
+ bR2L = pTmpFrame->IsRightToLeft();
+ --nCnt;
+ --nRefCnt;
+ }
+
+ // If a counter overflows?
+ if( aRefArr[ nRefCnt ] == aArr[ nCnt ] )
+ {
+ if( nCnt )
+ --nCnt;
+ else
+ --nRefCnt;
+ }
+
+ const SwFrame* pRefFrame = aRefArr[ nRefCnt ];
+ const SwFrame* pFieldFrame = aArr[ nCnt ];
+
+ // different frames, check their Y-/X-position
+ bool bRefIsLower = false;
+ if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() ||
+ ( SwFrameType::Column | SwFrameType::Cell ) & pRefFrame->GetType() )
+ {
+ if( pFieldFrame->GetType() == pRefFrame->GetType() )
+ {
+ // here, the X-pos is more important
+ if( bVert )
+ {
+ if( bR2L )
+ bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
+ ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
+ pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() );
+ else
+ bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
+ ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
+ pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() );
+ }
+ else if( bR2L )
+ bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ||
+ ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
+ pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
+ else
+ bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ||
+ ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
+ pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
+ pRefFrame = nullptr;
+ }
+ else if( ( SwFrameType::Column | SwFrameType::Cell ) & pFieldFrame->GetType() )
+ pFieldFrame = aArr[ nCnt - 1 ];
+ else
+ pRefFrame = aRefArr[ nRefCnt - 1 ];
+ }
+
+ if( pRefFrame ) // misuse as flag
+ {
+ if( bVert )
+ {
+ if( bR2L )
+ bRefIsLower = pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() ||
+ ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
+ pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
+ else
+ bRefIsLower = pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() ||
+ ( pRefFrame->getFrameArea().Left() == pFieldFrame->getFrameArea().Left() &&
+ pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() );
+ }
+ else if( bR2L )
+ bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
+ ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
+ pRefFrame->getFrameArea().Left() > pFieldFrame->getFrameArea().Left() );
+ else
+ bRefIsLower = pRefFrame->getFrameArea().Top() < pFieldFrame->getFrameArea().Top() ||
+ ( pRefFrame->getFrameArea().Top() == pFieldFrame->getFrameArea().Top() &&
+ pRefFrame->getFrameArea().Left() < pFieldFrame->getFrameArea().Left() );
+ }
+ return bRefIsLower;
+}
+
+// tdf#115319 create alternative reference formats, if the user asked for it
+// (ReferenceFieldLanguage attribute of the reference field is not empty), and
+// language of the text and ReferenceFieldLanguage are the same.
+// Right now only HUNGARIAN seems to need this (as in the related issue,
+// the reversed caption order in autocaption, solved by #i61007#)
+static void lcl_formatReferenceLanguage( OUString& rRefText,
+ bool bClosingParenthesis, LanguageType eLang,
+ std::u16string_view rReferenceLanguage)
+{
+ if (eLang != LANGUAGE_HUNGARIAN || (rReferenceLanguage != u"hu" && rReferenceLanguage != u"Hu"))
+ return;
+
+ // Add Hungarian definitive article (a/az) before references,
+ // similar to \aref, \apageref etc. of LaTeX Babel package.
+ //
+ // for example:
+ //
+ // "az 1. oldalon" ("on page 1"), but
+ // "a 2. oldalon" ("on page 2")
+ // "a fentebbi", "az alábbi" (above/below)
+ // "a Lorem", "az Ipsum"
+ //
+ // Support following numberings of EU publications:
+ //
+ // 1., 1a., a), (1), (1a), iii., III., IA.
+ //
+ // (http://publications.europa.eu/code/hu/hu-120700.htm,
+ // http://publications.europa.eu/code/hu/hu-4100600.htm)
+
+ CharClass aCharClass(( LanguageTag(eLang) ));
+ sal_Int32 nLen = rRefText.getLength();
+ sal_Int32 i;
+ // substring of rRefText starting with letter or number
+ OUString sNumbering;
+ // is article "az"?
+ bool bArticleAz = false;
+ // is numbering a number?
+ bool bNum = false;
+
+ // search first member of the numbering (numbers or letters)
+ for (i=0; i<nLen && (sNumbering.isEmpty() ||
+ ((bNum && aCharClass.isDigit(rRefText, i)) ||
+ (!bNum && aCharClass.isLetter(rRefText, i)))); ++i)
+ {
+ // start of numbering within the field text
+ if (sNumbering.isEmpty() && aCharClass.isLetterNumeric(rRefText, i)) {
+ sNumbering = rRefText.copy(i);
+ bNum = aCharClass.isDigit(rRefText, i);
+ }
+ }
+
+ // length of numbering
+ nLen = i - (rRefText.getLength() - sNumbering.getLength());
+
+ if (bNum)
+ {
+ // az 1, 1000, 1000000, 1000000000...
+ // az 5, 50, 500...
+ if ((sNumbering.startsWith("1") && (nLen == 1 || nLen == 4 || nLen == 7 || nLen == 10)) ||
+ sNumbering.startsWith("5"))
+ bArticleAz = true;
+ }
+ else if (nLen == 1 && sNumbering[0] < 128)
+ {
+ // ASCII 1-letter numbering
+ // az a), e), f) ... x)
+ // az i., v. (but, a x.)
+ static const std::u16string_view sLettersStartingWithVowels = u"aefilmnorsuxyAEFILMNORSUXY";
+ if (sLettersStartingWithVowels.find(sNumbering[0]) != std::u16string_view::npos)
+ {
+ // x), X) are letters, but x. and X. etc. are Roman numbers
+ if (bClosingParenthesis ||
+ (sNumbering[0] != 'x' && sNumbering[0] != 'X'))
+ bArticleAz = true;
+ } else if ((sNumbering[0] == 'v' || sNumbering[0] == 'V') && !bClosingParenthesis)
+ // v), V) are letters, but v. and V. are Roman numbers
+ bArticleAz = true;
+ }
+ else
+ {
+ static const sal_Unicode sVowelsWithDiacritic[] = {
+ 0x00E1, 0x00C1, 0x00E9, 0x00C9, 0x00ED, 0x00CD,
+ 0x00F3, 0x00D3, 0x00F6, 0x00D6, 0x0151, 0x0150,
+ 0x00FA, 0x00DA, 0x00FC, 0x00DC, 0x0171, 0x0170, 0 };
+ static const OUString sVowels = OUString::Concat(u"aAeEiIoOuU") + sVowelsWithDiacritic;
+
+ // handle more than 1-letter long Roman numbers and
+ // their possible combinations with letters:
+ // az IA, a IIB, a IIIC., az Ia, a IIb., a iiic), az LVIII. szonett
+ bool bRomanNumber = false;
+ if (nLen > 1 && (nLen + 1 >= sNumbering.getLength() || sNumbering[nLen] == '.'))
+ {
+ sal_Unicode last = sNumbering[nLen - 1];
+ OUString sNumberingTrim;
+ if ((last >= 'A' && last < 'I') || (last >= 'a' && last < 'i'))
+ sNumberingTrim = sNumbering.copy(0, nLen - 1);
+ else
+ sNumberingTrim = sNumbering.copy(0, nLen);
+ bRomanNumber =
+ sNumberingTrim.replaceAll("i", "").replaceAll("v", "").replaceAll("x", "").replaceAll("l", "").replaceAll("c", "").isEmpty() ||
+ sNumberingTrim.replaceAll("I", "").replaceAll("V", "").replaceAll("X", "").replaceAll("L", "").replaceAll("C", "").isEmpty();
+ }
+
+ if (
+ // Roman number and a letter optionally
+ ( bRomanNumber && (
+ (sNumbering[0] == 'i' && sNumbering[1] != 'i' && sNumbering[1] != 'v' && sNumbering[1] != 'x') ||
+ (sNumbering[0] == 'I' && sNumbering[1] != 'I' && sNumbering[1] != 'V' && sNumbering[1] != 'X') ||
+ (sNumbering[0] == 'v' && sNumbering[1] != 'i') ||
+ (sNumbering[0] == 'V' && sNumbering[1] != 'I') ||
+ (sNumbering[0] == 'l' && sNumbering[1] != 'x') ||
+ (sNumbering[0] == 'L' && sNumbering[1] != 'X')) ) ||
+ // a word starting with vowel (not Roman number)
+ ( !bRomanNumber && sVowels.indexOf(sNumbering[0]) != -1))
+ {
+ bArticleAz = true;
+ }
+ }
+ // not a title text starting already with a definitive article
+ if ( sNumbering.startsWith("A ") || sNumbering.startsWith("Az ") ||
+ sNumbering.startsWith("a ") || sNumbering.startsWith("az ") )
+ return;
+
+ // lowercase, if rReferenceLanguage == "hu", not "Hu"
+ OUString sArticle;
+
+ if ( rReferenceLanguage == u"hu" )
+ sArticle = "a";
+ else
+ sArticle = "A";
+
+ if (bArticleAz)
+ sArticle += "z";
+
+ rRefText = sArticle + " " + rRefText;
+}
+
+/// get references
+SwGetRefField::SwGetRefField( SwGetRefFieldType* pFieldType,
+ const OUString& rSetRef, const OUString& rSetReferenceLanguage, sal_uInt16 nSubTyp,
+ sal_uInt16 nSequenceNo, sal_uLong nFormat )
+ : SwField( pFieldType, nFormat ),
+ m_sSetRefName( rSetRef ),
+ m_sSetReferenceLanguage( rSetReferenceLanguage ),
+ m_nSubType( nSubTyp ),
+ m_nSeqNo( nSequenceNo )
+{
+}
+
+SwGetRefField::~SwGetRefField()
+{
+}
+
+OUString SwGetRefField::GetDescription() const
+{
+ return SwResId(STR_REFERENCE);
+}
+
+sal_uInt16 SwGetRefField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwGetRefField::SetSubType( sal_uInt16 n )
+{
+ m_nSubType = n;
+}
+
+// #i81002#
+bool SwGetRefField::IsRefToHeadingCrossRefBookmark() const
+{
+ return GetSubType() == REF_BOOKMARK &&
+ ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_sSetRefName);
+}
+
+bool SwGetRefField::IsRefToNumItemCrossRefBookmark() const
+{
+ return GetSubType() == REF_BOOKMARK &&
+ ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_sSetRefName);
+}
+
+const SwTextNode* SwGetRefField::GetReferencedTextNode() const
+{
+ SwGetRefFieldType *pTyp = dynamic_cast<SwGetRefFieldType*>(GetTyp());
+ if (!pTyp)
+ return nullptr;
+ sal_Int32 nDummy = -1;
+ return SwGetRefFieldType::FindAnchor( &pTyp->GetDoc(), m_sSetRefName, m_nSubType, m_nSeqNo, &nDummy );
+}
+
+// strikethrough for tooltips using Unicode combining character
+static OUString lcl_formatStringByCombiningCharacter(const OUString& sText, const sal_Unicode cChar)
+{
+ OUStringBuffer sRet;
+ for (sal_Int32 i = 0; i < sText.getLength(); ++i)
+ {
+ sRet.append(sText[i]);
+ sRet.append(cChar);
+ }
+ return sRet.makeStringAndClear();
+}
+
+// #i85090#
+OUString SwGetRefField::GetExpandedTextOfReferencedTextNode(
+ SwRootFrame const& rLayout) const
+{
+ const SwTextNode* pReferencedTextNode( GetReferencedTextNode() );
+ if ( !pReferencedTextNode )
+ return OUString();
+
+ // show the referenced text without the deletions, but if the whole text was
+ // deleted, show the original text for the sake of the comfortable reviewing,
+ // but with Unicode strikethrough in the tooltip
+ OUString sRet = sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode::HideDeletions);
+ if ( sRet.isEmpty() )
+ {
+ static const sal_Unicode cStrikethrough = u'\x0336';
+ sRet = sw::GetExpandTextMerged(&rLayout, *pReferencedTextNode, true, false, ExpandMode(0));
+ sRet = lcl_formatStringByCombiningCharacter( sRet, cStrikethrough );
+ }
+ return sRet;
+}
+
+void SwGetRefField::SetExpand( const OUString& rStr )
+{
+ m_sText = rStr;
+ m_sTextRLHidden = rStr;
+}
+
+OUString SwGetRefField::ExpandImpl(SwRootFrame const*const pLayout) const
+{
+ return pLayout && pLayout->IsHideRedlines() ? m_sTextRLHidden : m_sText;
+}
+
+OUString SwGetRefField::GetFieldName() const
+{
+ const OUString aName = GetTyp()->GetName();
+ if ( !aName.isEmpty() || !m_sSetRefName.isEmpty() )
+ {
+ return aName + " " + m_sSetRefName;
+ }
+ return ExpandImpl(nullptr);
+}
+
+
+static void FilterText(OUString & rText, LanguageType const eLang,
+ std::u16string_view rSetReferenceLanguage)
+{
+ // remove all special characters (replace them with blanks)
+ if (rText.isEmpty())
+ return;
+
+ rText = rText.replaceAll(u"\u00ad", "");
+ OUStringBuffer aBuf(rText);
+ const sal_Int32 l = aBuf.getLength();
+ for (sal_Int32 i = 0; i < l; ++i)
+ {
+ if (aBuf[i] < ' ')
+ {
+ aBuf[i] = ' ';
+ }
+ else if (aBuf[i] == 0x2011)
+ {
+ aBuf[i] = '-';
+ }
+ }
+ rText = aBuf.makeStringAndClear();
+ if (!rSetReferenceLanguage.empty())
+ {
+ lcl_formatReferenceLanguage(rText, false, eLang, rSetReferenceLanguage);
+ }
+}
+
+// #i81002# - parameter <pFieldTextAttr> added
+void SwGetRefField::UpdateField( const SwTextField* pFieldTextAttr )
+{
+ m_sText.clear();
+ m_sTextRLHidden.clear();
+
+ SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
+ // finding the reference target (the number)
+ sal_Int32 nNumStart = -1;
+ sal_Int32 nNumEnd = -1;
+ SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor(
+ &rDoc, m_sSetRefName, m_nSubType, m_nSeqNo, &nNumStart, &nNumEnd
+ );
+ // not found?
+ if ( !pTextNd )
+ {
+ m_sText = SwViewShell::GetShellRes()->aGetRefField_RefItemNotFound;
+ m_sTextRLHidden = m_sText;
+ return ;
+ }
+
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame const* pLayoutRLHidden(nullptr);
+ for (SwRootFrame const*const pLay : rDoc.GetAllLayouts())
+ {
+ if (pLay->IsHideRedlines())
+ {
+ pLayoutRLHidden = pLay;
+ }
+ else
+ {
+ pLayout = pLay;
+ }
+ }
+
+ // where is the category name (e.g. "Illustration")?
+ const OUString aText = pTextNd->GetText();
+ const sal_Int32 nCatStart = aText.indexOf(m_sSetRefName);
+ const bool bHasCat = nCatStart>=0;
+ const sal_Int32 nCatEnd = bHasCat ? nCatStart + m_sSetRefName.getLength() : -1;
+
+ // length of the referenced text
+ const sal_Int32 nLen = aText.getLength();
+
+ // which format?
+ switch( GetFormat() )
+ {
+ case REF_CONTENT:
+ case REF_ONLYNUMBER:
+ case REF_ONLYCAPTION:
+ case REF_ONLYSEQNO:
+ {
+ // needed part of Text
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+
+ switch( m_nSubType )
+ {
+ case REF_SEQUENCEFLD:
+
+ switch( GetFormat() )
+ {
+ // "Category and Number"
+ case REF_ONLYNUMBER:
+ if (bHasCat) {
+ nStart = std::min(nNumStart, nCatStart);
+ nEnd = std::max(nNumEnd, nCatEnd);
+ } else {
+ nStart = nNumStart;
+ nEnd = nNumEnd;
+ }
+ break;
+
+ // "Caption Text"
+ case REF_ONLYCAPTION: {
+ // next alphanumeric character after category+number
+ if (const SwTextAttr* const pTextAttr =
+ pTextNd->GetTextAttrForCharAt(nNumStart, RES_TXTATR_FIELD)
+ ) {
+ // start searching from nFrom
+ const sal_Int32 nFrom = bHasCat
+ ? std::max(nNumStart + 1, nCatEnd)
+ : nNumStart + 1;
+ nStart = SwGetExpField::GetReferenceTextPos( pTextAttr->GetFormatField(), rDoc, nFrom );
+ } else {
+ nStart = bHasCat ? std::max(nNumEnd, nCatEnd) : nNumEnd;
+ }
+ nEnd = nLen;
+ break;
+ }
+
+ // "Numbering"
+ case REF_ONLYSEQNO:
+ nStart = nNumStart;
+ nEnd = std::min(nStart + 1, nLen);
+ break;
+
+ // "Reference" (whole Text)
+ case REF_CONTENT:
+ nStart = 0;
+ nEnd = nLen;
+ break;
+
+ default:
+ O3TL_UNREACHABLE;
+ }
+ break;
+
+ case REF_BOOKMARK:
+ nStart = nNumStart;
+ // text is spread across multiple nodes - get whole text or only until end of node?
+ nEnd = nNumEnd<0 ? nLen : nNumEnd;
+ break;
+
+ case REF_OUTLINE:
+ nStart = nNumStart;
+ nEnd = nNumEnd;
+ break;
+
+ case REF_FOOTNOTE:
+ case REF_ENDNOTE:
+ // get number or numString
+ for( size_t i = 0; i < rDoc.GetFootnoteIdxs().size(); ++i )
+ {
+ SwTextFootnote* const pFootnoteIdx = rDoc.GetFootnoteIdxs()[i];
+ if( m_nSeqNo == pFootnoteIdx->GetSeqRefNo() )
+ {
+ m_sText = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, nullptr);
+ m_sTextRLHidden = pFootnoteIdx->GetFootnote().GetViewNumStr(rDoc, pLayoutRLHidden);
+ if (!m_sSetReferenceLanguage.isEmpty())
+ {
+ lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage);
+ lcl_formatReferenceLanguage(m_sTextRLHidden, false, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ break;
+ }
+ }
+ return;
+
+ case REF_SETREFATTR:
+ nStart = nNumStart;
+ nEnd = nNumEnd;
+ break;
+
+ default:
+ O3TL_UNREACHABLE;
+ }
+
+ if( nStart != nEnd ) // a section?
+ {
+ m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions);
+ // show the referenced text without the deletions, but if the whole text was
+ // deleted, show the original text for the sake of the comfortable reviewing
+ // (with strikethrough in tooltip, see GetExpandedTextOfReferencedTextNode())
+ if ( m_sText.isEmpty() )
+ m_sText = pTextNd->GetExpandText(pLayout, nStart, nEnd - nStart, false, false, false, ExpandMode(0));
+
+ if (m_nSubType == REF_OUTLINE
+ || (m_nSubType == REF_SEQUENCEFLD && REF_CONTENT == GetFormat()))
+ {
+ m_sTextRLHidden = sw::GetExpandTextMerged(
+ pLayoutRLHidden, *pTextNd, false, false, ExpandMode(0));
+ }
+ else
+ {
+ m_sTextRLHidden = pTextNd->GetExpandText(pLayoutRLHidden,
+ nStart, nEnd - nStart, false, false, false, ExpandMode::HideDeletions);
+ }
+ FilterText(m_sText, GetLanguage(), m_sSetReferenceLanguage);
+ FilterText(m_sTextRLHidden, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ }
+ break;
+
+ case REF_PAGE:
+ case REF_PAGE_PGDESC:
+ {
+ auto const func =
+ [this, pTextNd, nNumStart](OUString & rText, SwRootFrame const*const pLay)
+ {
+ SwTextFrame const* pFrame = static_cast<SwTextFrame*>(pTextNd->getLayoutFrame(pLay, nullptr, nullptr));
+ SwTextFrame const*const pSave = pFrame;
+ if (pFrame)
+ {
+ TextFrameIndex const nNumStartIndex(pFrame->MapModelToView(pTextNd, nNumStart));
+ while (pFrame && !pFrame->IsInside(nNumStartIndex))
+ pFrame = pFrame->GetFollow();
+ }
+
+ if( pFrame || nullptr != ( pFrame = pSave ))
+ {
+ sal_uInt16 nPageNo = pFrame->GetVirtPageNum();
+ const SwPageFrame *pPage;
+ if( REF_PAGE_PGDESC == GetFormat() &&
+ nullptr != ( pPage = pFrame->FindPageFrame() ) &&
+ pPage->GetPageDesc() )
+ {
+ rText = pPage->GetPageDesc()->GetNumType().GetNumStr(nPageNo);
+ }
+ else
+ {
+ rText = OUString::number(nPageNo);
+ }
+
+ if (!m_sSetReferenceLanguage.isEmpty())
+ lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ };
+ // sw_redlinehide: currently only one of these layouts will exist,
+ // so the getLayoutFrame will use the same frame in both cases
+ func(m_sText, pLayout);
+ func(m_sTextRLHidden, pLayoutRLHidden);
+ }
+ break;
+
+ case REF_CHAPTER:
+ {
+ auto const func =
+ [this, pTextNd](OUString & rText, SwRootFrame const*const pLay)
+ {
+ // a bit tricky: search any frame
+ SwFrame const*const pFrame = pTextNd->getLayoutFrame(pLay);
+ if( pFrame )
+ {
+ SwChapterFieldType aFieldTyp;
+ SwChapterField aField( &aFieldTyp, 0 );
+ aField.SetLevel( MAXLEVEL - 1 );
+ aField.ChangeExpansion( *pFrame, pTextNd, true );
+ rText = aField.GetNumber(pLay);
+
+ if (!m_sSetReferenceLanguage.isEmpty())
+ lcl_formatReferenceLanguage(rText, false, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ };
+ func(m_sText, pLayout);
+ func(m_sTextRLHidden, pLayoutRLHidden);
+ }
+ break;
+
+ case REF_UPDOWN:
+ {
+ // #i81002#
+ // simplified: use parameter <pFieldTextAttr>
+ if( !pFieldTextAttr || !pFieldTextAttr->GetpTextNode() )
+ break;
+
+ LocaleDataWrapper aLocaleData(( LanguageTag( GetLanguage() ) ));
+
+ // first a "short" test - in case both are in the same node
+ if( pFieldTextAttr->GetpTextNode() == pTextNd )
+ {
+ m_sText = nNumStart < pFieldTextAttr->GetStart()
+ ? aLocaleData.getAboveWord()
+ : aLocaleData.getBelowWord();
+ m_sTextRLHidden = m_sText;
+ break;
+ }
+
+ m_sText = ::IsFrameBehind( *pFieldTextAttr->GetpTextNode(), pFieldTextAttr->GetStart(),
+ *pTextNd, nNumStart )
+ ? aLocaleData.getAboveWord()
+ : aLocaleData.getBelowWord();
+
+ if (!m_sSetReferenceLanguage.isEmpty())
+ lcl_formatReferenceLanguage(m_sText, false, GetLanguage(), m_sSetReferenceLanguage);
+
+ m_sTextRLHidden = m_sText;
+ }
+ break;
+ // #i81002#
+ case REF_NUMBER:
+ case REF_NUMBER_NO_CONTEXT:
+ case REF_NUMBER_FULL_CONTEXT:
+ {
+ if ( pFieldTextAttr && pFieldTextAttr->GetpTextNode() )
+ {
+ auto result =
+ MakeRefNumStr(pLayout, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat());
+ m_sText = result.first;
+ // for differentiation of Roman numbers and letters in Hungarian article handling
+ bool bClosingParenthesis = result.second;
+ if (!m_sSetReferenceLanguage.isEmpty())
+ {
+ lcl_formatReferenceLanguage(m_sText, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ result =
+ MakeRefNumStr(pLayoutRLHidden, pFieldTextAttr->GetTextNode(), *pTextNd, GetFormat());
+ m_sTextRLHidden = result.first;
+ bClosingParenthesis = result.second;
+ if (!m_sSetReferenceLanguage.isEmpty())
+ {
+ lcl_formatReferenceLanguage(m_sTextRLHidden, bClosingParenthesis, GetLanguage(), m_sSetReferenceLanguage);
+ }
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL("<SwGetRefField::UpdateField(..)> - unknown format type");
+ }
+}
+
+// #i81002#
+static std::pair<OUString, bool> MakeRefNumStr(
+ SwRootFrame const*const pLayout,
+ const SwTextNode& i_rTextNodeOfField,
+ const SwTextNode& i_rTextNodeOfReferencedItem,
+ const sal_uInt32 nRefNumFormat)
+{
+ SwTextNode const& rTextNodeOfField(pLayout
+ ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfField)
+ : i_rTextNodeOfField);
+ SwTextNode const& rTextNodeOfReferencedItem(pLayout
+ ? *sw::GetParaPropsNode(*pLayout, i_rTextNodeOfReferencedItem)
+ : i_rTextNodeOfReferencedItem);
+ if ( rTextNodeOfReferencedItem.HasNumber(pLayout) &&
+ rTextNodeOfReferencedItem.IsCountedInList() )
+ {
+ OSL_ENSURE( rTextNodeOfReferencedItem.GetNum(pLayout),
+ "<SwGetRefField::MakeRefNumStr(..)> - referenced paragraph has number, but no <SwNodeNum> instance!" );
+
+ // Determine, up to which level the superior list labels have to be
+ // included - default is to include all superior list labels.
+ int nRestrictInclToThisLevel( 0 );
+ // Determine for format REF_NUMBER the level, up to which the superior
+ // list labels have to be restricted, if the text node of the reference
+ // field and the text node of the referenced item are in the same
+ // document context.
+ if ( nRefNumFormat == REF_NUMBER &&
+ rTextNodeOfField.FindFlyStartNode()
+ == rTextNodeOfReferencedItem.FindFlyStartNode() &&
+ rTextNodeOfField.FindFootnoteStartNode()
+ == rTextNodeOfReferencedItem.FindFootnoteStartNode() &&
+ rTextNodeOfField.FindHeaderStartNode()
+ == rTextNodeOfReferencedItem.FindHeaderStartNode() &&
+ rTextNodeOfField.FindFooterStartNode()
+ == rTextNodeOfReferencedItem.FindFooterStartNode() )
+ {
+ const SwNodeNum* pNodeNumForTextNodeOfField( nullptr );
+ if ( rTextNodeOfField.HasNumber(pLayout) &&
+ rTextNodeOfField.GetNumRule() == rTextNodeOfReferencedItem.GetNumRule() )
+ {
+ pNodeNumForTextNodeOfField = rTextNodeOfField.GetNum(pLayout);
+ }
+ else
+ {
+ pNodeNumForTextNodeOfField =
+ rTextNodeOfReferencedItem.GetNum(pLayout)->GetPrecedingNodeNumOf(rTextNodeOfField);
+ }
+ if ( pNodeNumForTextNodeOfField )
+ {
+ const SwNumberTree::tNumberVector rFieldNumVec =
+ pNodeNumForTextNodeOfField->GetNumberVector();
+ const SwNumberTree::tNumberVector rRefItemNumVec =
+ rTextNodeOfReferencedItem.GetNum()->GetNumberVector();
+ std::size_t nLevel( 0 );
+ while ( nLevel < rFieldNumVec.size() && nLevel < rRefItemNumVec.size() )
+ {
+ if ( rRefItemNumVec[nLevel] == rFieldNumVec[nLevel] )
+ {
+ nRestrictInclToThisLevel = nLevel + 1;
+ }
+ else
+ {
+ break;
+ }
+ ++nLevel;
+ }
+ }
+ }
+
+ // Determine, if superior list labels have to be included
+ const bool bInclSuperiorNumLabels(
+ ( nRestrictInclToThisLevel < rTextNodeOfReferencedItem.GetActualListLevel() &&
+ ( nRefNumFormat == REF_NUMBER || nRefNumFormat == REF_NUMBER_FULL_CONTEXT ) ) );
+
+ OSL_ENSURE( rTextNodeOfReferencedItem.GetNumRule(),
+ "<SwGetRefField::MakeRefNumStr(..)> - referenced numbered paragraph has no numbering rule set!" );
+ return std::make_pair(
+ rTextNodeOfReferencedItem.GetNumRule()->MakeRefNumString(
+ *(rTextNodeOfReferencedItem.GetNum(pLayout)),
+ bInclSuperiorNumLabels,
+ nRestrictInclToThisLevel ),
+ rTextNodeOfReferencedItem.GetNumRule()->MakeNumString(
+ *(rTextNodeOfReferencedItem.GetNum(pLayout)),
+ true).endsWith(")") );
+ }
+
+ return std::make_pair(OUString(), false);
+}
+
+std::unique_ptr<SwField> SwGetRefField::Copy() const
+{
+ std::unique_ptr<SwGetRefField> pField( new SwGetRefField( static_cast<SwGetRefFieldType*>(GetTyp()),
+ m_sSetRefName, m_sSetReferenceLanguage, m_nSubType,
+ m_nSeqNo, GetFormat() ) );
+ pField->m_sText = m_sText;
+ pField->m_sTextRLHidden = m_sTextRLHidden;
+ return std::unique_ptr<SwField>(pField.release());
+}
+
+/// get reference name
+OUString SwGetRefField::GetPar1() const
+{
+ return m_sSetRefName;
+}
+
+/// set reference name
+void SwGetRefField::SetPar1( const OUString& rName )
+{
+ m_sSetRefName = rName;
+}
+
+OUString SwGetRefField::GetPar2() const
+{
+ return ExpandImpl(nullptr);
+}
+
+bool SwGetRefField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nPart = 0;
+ switch(GetFormat())
+ {
+ case REF_PAGE : nPart = ReferenceFieldPart::PAGE ; break;
+ case REF_CHAPTER : nPart = ReferenceFieldPart::CHAPTER ; break;
+ case REF_CONTENT : nPart = ReferenceFieldPart::TEXT ; break;
+ case REF_UPDOWN : nPart = ReferenceFieldPart::UP_DOWN ; break;
+ case REF_PAGE_PGDESC: nPart = ReferenceFieldPart::PAGE_DESC ; break;
+ case REF_ONLYNUMBER : nPart = ReferenceFieldPart::CATEGORY_AND_NUMBER ; break;
+ case REF_ONLYCAPTION: nPart = ReferenceFieldPart::ONLY_CAPTION ; break;
+ case REF_ONLYSEQNO : nPart = ReferenceFieldPart::ONLY_SEQUENCE_NUMBER; break;
+ // #i81002#
+ case REF_NUMBER: nPart = ReferenceFieldPart::NUMBER; break;
+ case REF_NUMBER_NO_CONTEXT: nPart = ReferenceFieldPart::NUMBER_NO_CONTEXT; break;
+ case REF_NUMBER_FULL_CONTEXT: nPart = ReferenceFieldPart::NUMBER_FULL_CONTEXT; break;
+ }
+ rAny <<= nPart;
+ }
+ break;
+ case FIELD_PROP_USHORT2:
+ {
+ sal_Int16 nSource = 0;
+ switch(m_nSubType)
+ {
+ case REF_SETREFATTR : nSource = ReferenceFieldSource::REFERENCE_MARK; break;
+ case REF_SEQUENCEFLD: nSource = ReferenceFieldSource::SEQUENCE_FIELD; break;
+ case REF_BOOKMARK : nSource = ReferenceFieldSource::BOOKMARK; break;
+ case REF_OUTLINE : OSL_FAIL("not implemented"); break;
+ case REF_FOOTNOTE : nSource = ReferenceFieldSource::FOOTNOTE; break;
+ case REF_ENDNOTE : nSource = ReferenceFieldSource::ENDNOTE; break;
+ }
+ rAny <<= nSource;
+ }
+ break;
+ case FIELD_PROP_PAR1:
+ {
+ OUString sTmp(GetPar1());
+ if(REF_SEQUENCEFLD == m_nSubType)
+ {
+ sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( sTmp, SwGetPoolIdFromName::TxtColl );
+ switch( nPoolId )
+ {
+ case RES_POOLCOLL_LABEL_ABB:
+ case RES_POOLCOLL_LABEL_TABLE:
+ case RES_POOLCOLL_LABEL_FRAME:
+ case RES_POOLCOLL_LABEL_DRAWING:
+ case RES_POOLCOLL_LABEL_FIGURE:
+ SwStyleNameMapper::FillProgName(nPoolId, sTmp) ;
+ break;
+ }
+ }
+ rAny <<= sTmp;
+ }
+ break;
+ case FIELD_PROP_PAR3:
+ rAny <<= ExpandImpl(nullptr);
+ break;
+ case FIELD_PROP_PAR4:
+ rAny <<= m_sSetReferenceLanguage;
+ break;
+ case FIELD_PROP_SHORT1:
+ rAny <<= static_cast<sal_Int16>(m_nSeqNo);
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwGetRefField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_USHORT1:
+ {
+ sal_Int16 nPart = 0;
+ rAny >>= nPart;
+ switch(nPart)
+ {
+ case ReferenceFieldPart::PAGE: nPart = REF_PAGE; break;
+ case ReferenceFieldPart::CHAPTER: nPart = REF_CHAPTER; break;
+ case ReferenceFieldPart::TEXT: nPart = REF_CONTENT; break;
+ case ReferenceFieldPart::UP_DOWN: nPart = REF_UPDOWN; break;
+ case ReferenceFieldPart::PAGE_DESC: nPart = REF_PAGE_PGDESC; break;
+ case ReferenceFieldPart::CATEGORY_AND_NUMBER: nPart = REF_ONLYNUMBER; break;
+ case ReferenceFieldPart::ONLY_CAPTION: nPart = REF_ONLYCAPTION; break;
+ case ReferenceFieldPart::ONLY_SEQUENCE_NUMBER : nPart = REF_ONLYSEQNO; break;
+ // #i81002#
+ case ReferenceFieldPart::NUMBER: nPart = REF_NUMBER; break;
+ case ReferenceFieldPart::NUMBER_NO_CONTEXT: nPart = REF_NUMBER_NO_CONTEXT; break;
+ case ReferenceFieldPart::NUMBER_FULL_CONTEXT: nPart = REF_NUMBER_FULL_CONTEXT; break;
+ default: return false;
+ }
+ SetFormat(nPart);
+ }
+ break;
+ case FIELD_PROP_USHORT2:
+ {
+ sal_Int16 nSource = 0;
+ rAny >>= nSource;
+ switch(nSource)
+ {
+ case ReferenceFieldSource::REFERENCE_MARK : m_nSubType = REF_SETREFATTR ; break;
+ case ReferenceFieldSource::SEQUENCE_FIELD :
+ {
+ if(REF_SEQUENCEFLD == m_nSubType)
+ break;
+ m_nSubType = REF_SEQUENCEFLD;
+ ConvertProgrammaticToUIName();
+ }
+ break;
+ case ReferenceFieldSource::BOOKMARK : m_nSubType = REF_BOOKMARK ; break;
+ case ReferenceFieldSource::FOOTNOTE : m_nSubType = REF_FOOTNOTE ; break;
+ case ReferenceFieldSource::ENDNOTE : m_nSubType = REF_ENDNOTE ; break;
+ }
+ }
+ break;
+ case FIELD_PROP_PAR1:
+ {
+ OUString sTmpStr;
+ rAny >>= sTmpStr;
+ SetPar1(sTmpStr);
+ ConvertProgrammaticToUIName();
+ }
+ break;
+ case FIELD_PROP_PAR3:
+ {
+ OUString sTmpStr;
+ rAny >>= sTmpStr;
+ SetExpand( sTmpStr );
+ }
+ break;
+ case FIELD_PROP_PAR4:
+ rAny >>= m_sSetReferenceLanguage;
+ break;
+ case FIELD_PROP_SHORT1:
+ {
+ sal_Int16 nSetSeq = 0;
+ rAny >>= nSetSeq;
+ if(nSetSeq >= 0)
+ m_nSeqNo = nSetSeq;
+ }
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+void SwGetRefField::ConvertProgrammaticToUIName()
+{
+ if(!(GetTyp() && REF_SEQUENCEFLD == m_nSubType))
+ return;
+
+ SwDoc& rDoc = static_cast<SwGetRefFieldType*>(GetTyp())->GetDoc();
+ const OUString rPar1 = GetPar1();
+ // don't convert when the name points to an existing field type
+ if (rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, rPar1, false))
+ return;
+
+ sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromProgName( rPar1, SwGetPoolIdFromName::TxtColl );
+ TranslateId pResId;
+ switch( nPoolId )
+ {
+ case RES_POOLCOLL_LABEL_ABB:
+ pResId = STR_POOLCOLL_LABEL_ABB;
+ break;
+ case RES_POOLCOLL_LABEL_TABLE:
+ pResId = STR_POOLCOLL_LABEL_TABLE;
+ break;
+ case RES_POOLCOLL_LABEL_FRAME:
+ pResId = STR_POOLCOLL_LABEL_FRAME;
+ break;
+ case RES_POOLCOLL_LABEL_DRAWING:
+ pResId = STR_POOLCOLL_LABEL_DRAWING;
+ break;
+ case RES_POOLCOLL_LABEL_FIGURE:
+ pResId = STR_POOLCOLL_LABEL_FIGURE;
+ break;
+ }
+ if (pResId)
+ SetPar1(SwResId(pResId));
+}
+
+SwGetRefFieldType::SwGetRefFieldType( SwDoc& rDc )
+ : SwFieldType( SwFieldIds::GetRef ), m_rDoc( rDc )
+{}
+
+std::unique_ptr<SwFieldType> SwGetRefFieldType::Copy() const
+{
+ return std::make_unique<SwGetRefFieldType>( m_rDoc );
+}
+
+void SwGetRefFieldType::UpdateGetReferences()
+{
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields, false);
+ for(auto pFormatField: vFields)
+ {
+ // update only the GetRef fields
+ //JP 3.4.2001: Task 71231 - we need the correct language
+ SwGetRefField* pGRef = static_cast<SwGetRefField*>(pFormatField->GetField());
+ const SwTextField* pTField;
+ if(!pGRef->GetLanguage() &&
+ nullptr != (pTField = pFormatField->GetTextField()) &&
+ pTField->GetpTextNode())
+ {
+ pGRef->SetLanguage(pTField->GetpTextNode()->GetLang(pTField->GetStart()));
+ }
+
+ // #i81002#
+ pGRef->UpdateField(pFormatField->GetTextField());
+ }
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, nullptr));
+}
+
+void SwGetRefFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pNew && !pLegacy->m_pOld)
+ // update to all GetReference fields
+ // hopefully, this codepath is soon dead code, and
+ // UpdateGetReferences gets only called directly
+ UpdateGetReferences();
+ else
+ // forward to text fields, they "expand" the text
+ CallSwClientNotify(rHint);
+}
+
+namespace sw {
+
+bool IsMarkHintHidden(SwRootFrame const& rLayout,
+ SwTextNode const& rNode, SwTextAttrEnd const& rHint)
+{
+ if (!rLayout.HasMergedParas())
+ {
+ return false;
+ }
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(
+ rNode.getLayoutFrame(&rLayout)));
+ if (!pFrame)
+ {
+ return true;
+ }
+ sal_Int32 const*const pEnd(rHint.GetEnd());
+ if (pEnd)
+ {
+ return pFrame->MapModelToView(&rNode, rHint.GetStart())
+ == pFrame->MapModelToView(&rNode, *pEnd);
+ }
+ else
+ {
+ assert(rHint.HasDummyChar());
+ return pFrame->MapModelToView(&rNode, rHint.GetStart())
+ == pFrame->MapModelToView(&rNode, rHint.GetStart() + 1);
+ }
+}
+
+} // namespace sw
+
+SwTextNode* SwGetRefFieldType::FindAnchor( SwDoc* pDoc, const OUString& rRefMark,
+ sal_uInt16 nSubType, sal_uInt16 nSeqNo,
+ sal_Int32* pStt, sal_Int32* pEnd,
+ SwRootFrame const*const pLayout)
+{
+ OSL_ENSURE( pStt, "Why did no one check the StartPos?" );
+
+ IDocumentRedlineAccess & rIDRA(pDoc->getIDocumentRedlineAccess());
+ SwTextNode* pTextNd = nullptr;
+ switch( nSubType )
+ {
+ case REF_SETREFATTR:
+ {
+ const SwFormatRefMark *pRef = pDoc->GetRefMark( rRefMark );
+ SwTextRefMark const*const pRefMark(pRef ? pRef->GetTextRefMark() : nullptr);
+ if (pRefMark && (!pLayout || !sw::IsMarkHintHidden(*pLayout,
+ pRefMark->GetTextNode(), *pRefMark)))
+ {
+ pTextNd = const_cast<SwTextNode*>(&pRef->GetTextRefMark()->GetTextNode());
+ *pStt = pRef->GetTextRefMark()->GetStart();
+ if( pEnd )
+ *pEnd = pRef->GetTextRefMark()->GetAnyEnd();
+ }
+ }
+ break;
+
+ case REF_SEQUENCEFLD:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, rRefMark, false );
+ if( pFieldType && pFieldType->HasWriterListeners() &&
+ nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pFieldType)->GetType() )
+ {
+ std::vector<SwFormatField*> vFields;
+ pFieldType->GatherFields(vFields, false);
+ for(auto pFormatField: vFields)
+ {
+ SwTextField *const pTextField(pFormatField->GetTextField());
+ if (pTextField && nSeqNo ==
+ static_cast<SwSetExpField*>(pFormatField->GetField())->GetSeqNumber()
+ && (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsFieldDeletedInModel(rIDRA, *pTextField)))
+ {
+ pTextNd = pTextField->GetpTextNode();
+ *pStt = pTextField->GetStart();
+ if( pEnd )
+ *pEnd = (*pStt) + 1;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case REF_BOOKMARK:
+ {
+ IDocumentMarkAccess::const_iterator_t ppMark = pDoc->getIDocumentMarkAccess()->findMark(rRefMark);
+ if (ppMark != pDoc->getIDocumentMarkAccess()->getAllMarksEnd()
+ && (!pLayout || !pLayout->IsHideRedlines()
+ || !sw::IsMarkHidden(*pLayout, **ppMark)))
+ {
+ const ::sw::mark::IMark* pBkmk = *ppMark;
+ const SwPosition* pPos = &pBkmk->GetMarkStart();
+
+ pTextNd = pPos->nNode.GetNode().GetTextNode();
+ *pStt = pPos->nContent.GetIndex();
+ if(pEnd)
+ {
+ if(!pBkmk->IsExpanded())
+ {
+ *pEnd = *pStt;
+ // #i81002#
+ if(dynamic_cast< ::sw::mark::CrossRefBookmark const *>(pBkmk))
+ {
+ OSL_ENSURE( pTextNd,
+ "<SwGetRefFieldType::FindAnchor(..)> - node marked by cross-reference bookmark isn't a text node --> crash" );
+ *pEnd = pTextNd->Len();
+ }
+ }
+ else if(pBkmk->GetOtherMarkPos().nNode == pBkmk->GetMarkPos().nNode)
+ *pEnd = pBkmk->GetMarkEnd().nContent.GetIndex();
+ else
+ *pEnd = -1;
+ }
+ }
+ }
+ break;
+
+ case REF_OUTLINE:
+ break;
+
+ case REF_FOOTNOTE:
+ case REF_ENDNOTE:
+ {
+ for( auto pFootnoteIdx : pDoc->GetFootnoteIdxs() )
+ if( nSeqNo == pFootnoteIdx->GetSeqRefNo() )
+ {
+ if (pLayout && pLayout->IsHideRedlines()
+ && sw::IsFootnoteDeleted(rIDRA, *pFootnoteIdx))
+ {
+ return nullptr;
+ }
+ // otherwise: the position at the start of the footnote
+ // will be mapped to something visible at least...
+ SwNodeIndex* pIdx = pFootnoteIdx->GetStartNode();
+ if( pIdx )
+ {
+ SwNodeIndex aIdx( *pIdx, 1 );
+ pTextNd = aIdx.GetNode().GetTextNode();
+ if( nullptr == pTextNd )
+ pTextNd = static_cast<SwTextNode*>(pDoc->GetNodes().GoNext( &aIdx ));
+ }
+ *pStt = 0;
+ if( pEnd )
+ *pEnd = 0;
+ break;
+ }
+ }
+ break;
+ }
+
+ return pTextNd;
+}
+
+namespace {
+
+struct RefIdsMap
+{
+private:
+ OUString aName;
+ std::set<sal_uInt16> aIds;
+ std::set<sal_uInt16> aDstIds;
+ std::map<sal_uInt16, sal_uInt16> sequencedIds; /// ID numbers sorted by sequence number.
+ bool bInit;
+
+ void Init(SwDoc& rDoc, SwDoc& rDestDoc, bool bField );
+ static void GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds );
+ void GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds );
+ void AddId( sal_uInt16 id, sal_uInt16 seqNum );
+ static sal_uInt16 GetFirstUnusedId( std::set<sal_uInt16> &rIds );
+
+public:
+ explicit RefIdsMap( const OUString& rName ) : aName( rName ), bInit( false ) {}
+
+ void Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField, bool bField );
+
+ const OUString& GetName() const { return aName; }
+};
+
+}
+
+/// Get a sorted list of the field IDs from a document.
+/// @param[in] rDoc The document to search.
+/// @param[in,out] rIds The list of IDs found in the document.
+void RefIdsMap::GetFieldIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds)
+{
+ SwFieldType *const pType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, aName, false);
+ if (!pType)
+ return;
+ std::vector<SwFormatField*> vFields;
+ pType->GatherFields(vFields);
+ for(const auto pF: vFields)
+ rIds.insert(static_cast<SwSetExpField const*>(pF->GetField())->GetSeqNumber());
+}
+
+/// Get a sorted list of the footnote/endnote IDs from a document.
+/// @param[in] rDoc The document to search.
+/// @param[in,out] rIds The list of IDs found in the document.
+void RefIdsMap::GetNoteIdsFromDoc( SwDoc& rDoc, std::set<sal_uInt16> &rIds)
+{
+ for( auto n = rDoc.GetFootnoteIdxs().size(); n; )
+ rIds.insert( rDoc.GetFootnoteIdxs()[ --n ]->GetSeqRefNo() );
+}
+
+/// Initialise the aIds and aDestIds collections from the source documents.
+/// @param[in] rDoc The source document.
+/// @param[in] rDestDoc The destination document.
+/// @param[in] bField True if we're interested in all fields, false for footnotes.
+void RefIdsMap::Init( SwDoc& rDoc, SwDoc& rDestDoc, bool bField )
+{
+ if( bInit )
+ return;
+
+ if( bField )
+ {
+ GetFieldIdsFromDoc( rDestDoc, aIds );
+ GetFieldIdsFromDoc( rDoc, aDstIds );
+
+ // Map all the new src fields to the next available unused id
+ for (const auto& rId : aDstIds)
+ AddId( GetFirstUnusedId(aIds), rId );
+
+ // Change the Sequence number of all SetExp fields in the source document
+ SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType( SwFieldIds::SetExp, aName, false );
+ if(pType)
+ {
+ std::vector<SwFormatField*> vFields;
+ pType->GatherFields(vFields, false);
+ for(auto pF: vFields)
+ {
+ if(!pF->GetTextField())
+ continue;
+ SwSetExpField *const pSetExp(static_cast<SwSetExpField *>(pF->GetField()));
+ sal_uInt16 const n = pSetExp->GetSeqNumber();
+ pSetExp->SetSeqNumber(sequencedIds[n]);
+ }
+ }
+ }
+ else
+ {
+ GetNoteIdsFromDoc( rDestDoc, aIds );
+ GetNoteIdsFromDoc( rDoc, aDstIds );
+
+ for (const auto& rId : aDstIds)
+ AddId( GetFirstUnusedId(aIds), rId );
+
+ // Change the footnotes/endnotes in the source doc to the new ID
+ for ( const auto pFootnoteIdx : rDoc.GetFootnoteIdxs() )
+ {
+ sal_uInt16 const n = pFootnoteIdx->GetSeqRefNo();
+ pFootnoteIdx->SetSeqNo(sequencedIds[n]);
+ }
+ }
+ bInit = true;
+}
+
+/// Get the lowest number unused in the passed set.
+/// @param[in] rIds The set of used ID numbers.
+/// @returns The lowest number unused by the passed set
+sal_uInt16 RefIdsMap::GetFirstUnusedId( std::set<sal_uInt16> &rIds )
+{
+ sal_uInt16 num(0);
+
+ for( const auto& rId : rIds )
+ {
+ if( num != rId )
+ {
+ return num;
+ }
+ ++num;
+ }
+ return num;
+}
+
+/// Add a new ID and sequence number to the "occupied" collection.
+/// @param[in] id The ID number.
+/// @param[in] seqNum The sequence number.
+void RefIdsMap::AddId( sal_uInt16 id, sal_uInt16 seqNum )
+{
+ aIds.insert( id );
+ sequencedIds[ seqNum ] = id;
+}
+
+void RefIdsMap::Check( SwDoc& rDoc, SwDoc& rDestDoc, SwGetRefField& rField,
+ bool bField )
+{
+ Init( rDoc, rDestDoc, bField);
+
+ sal_uInt16 const nSeqNo = rField.GetSeqNo();
+
+ // check if it needs to be remapped
+ // if sequencedIds doesn't contain the number, it means there is no
+ // SetExp field / footnote in the source document: do not modify
+ // the number, which works well for copy from/paste to same document
+ // (and if it is not the same document, there's no "correct" result anyway)
+ if (sequencedIds.count(nSeqNo))
+ {
+ rField.SetSeqNo( sequencedIds[nSeqNo] );
+ }
+}
+
+/// 1. if _both_ SetExp + GetExp / Footnote + GetExp field are copied,
+/// ensure that both get a new unused matching number
+/// 2. if only SetExp / Footnote is copied, it gets a new unused number
+/// 3. if only GetExp field is copied, for the case of copy from / paste to
+/// same document it's desirable to keep the same number;
+/// for other cases of copy/paste or master documents it's not obvious
+/// what is most desirable since it's going to be wrong anyway
+void SwGetRefFieldType::MergeWithOtherDoc( SwDoc& rDestDoc )
+{
+ if (&rDestDoc == &m_rDoc)
+ return;
+
+ if (rDestDoc.IsClipBoard())
+ {
+ // when copying _to_ clipboard, expectation is that no fields exist
+ // so no re-mapping is required to avoid collisions
+ assert(!rDestDoc.getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef)->HasWriterListeners());
+ return; // don't modify the fields in the source doc
+ }
+
+ // then there are RefFields in the DescDox - so all RefFields in the SourceDoc
+ // need to be converted to have unique IDs for both documents
+ RefIdsMap aFntMap { OUString() };
+ std::vector<std::unique_ptr<RefIdsMap>> aFieldMap;
+
+ std::vector<SwFormatField*> vFields;
+ GatherFields(vFields);
+ for(auto pField: vFields)
+ {
+ SwGetRefField& rRefField = *static_cast<SwGetRefField*>(pField->GetField());
+ switch( rRefField.GetSubType() )
+ {
+ case REF_SEQUENCEFLD:
+ {
+ RefIdsMap* pMap = nullptr;
+ for( auto n = aFieldMap.size(); n; )
+ {
+ if (aFieldMap[ --n ]->GetName() == rRefField.GetSetRefName())
+ {
+ pMap = aFieldMap[ n ].get();
+ break;
+ }
+ }
+ if( !pMap )
+ {
+ pMap = new RefIdsMap( rRefField.GetSetRefName() );
+ aFieldMap.push_back(std::unique_ptr<RefIdsMap>(pMap));
+ }
+
+ pMap->Check(m_rDoc, rDestDoc, rRefField, true);
+ }
+ break;
+
+ case REF_FOOTNOTE:
+ case REF_ENDNOTE:
+ aFntMap.Check(m_rDoc, rDestDoc, rRefField, false);
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/scrptfld.cxx b/sw/source/core/fields/scrptfld.cxx
new file mode 100644
index 000000000..ded42cc7f
--- /dev/null
+++ b/sw/source/core/fields/scrptfld.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <docufld.hxx>
+#include <unofldmid.h>
+#include <strings.hrc>
+#include <o3tl/any.hxx>
+#include <swtypes.hxx>
+
+using namespace ::com::sun::star;
+
+SwScriptFieldType::SwScriptFieldType( SwDoc& rD )
+ : SwFieldType( SwFieldIds::Script ), m_rDoc( rD )
+{}
+
+std::unique_ptr<SwFieldType> SwScriptFieldType::Copy() const
+{
+ return std::make_unique<SwScriptFieldType>( m_rDoc );
+}
+
+SwScriptField::SwScriptField( SwScriptFieldType* pInitType,
+ const OUString& rType, const OUString& rCode,
+ bool bURL )
+ : SwField( pInitType ), m_sType( rType ), m_sCode( rCode ), m_bCodeURL( bURL )
+{
+}
+
+OUString SwScriptField::GetDescription() const
+{
+ return SwResId(STR_SCRIPT);
+}
+
+OUString SwScriptField::ExpandImpl(SwRootFrame const*const) const
+{
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwScriptField::Copy() const
+{
+ return std::make_unique<SwScriptField>( static_cast<SwScriptFieldType*>(GetTyp()), m_sType, m_sCode, m_bCodeURL );
+}
+
+/// set type
+void SwScriptField::SetPar1( const OUString& rStr )
+{
+ m_sType = rStr;
+}
+
+OUString SwScriptField::GetPar1() const
+{
+ return m_sType;
+}
+
+/// set code
+void SwScriptField::SetPar2( const OUString& rStr )
+{
+ m_sCode = rStr;
+}
+
+OUString SwScriptField::GetPar2() const
+{
+ return m_sCode;
+}
+
+bool SwScriptField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny <<= m_sType;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_sCode;
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= m_bCodeURL;
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+bool SwScriptField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_PAR1:
+ rAny >>= m_sType;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_sCode;
+ break;
+ case FIELD_PROP_BOOL1:
+ m_bCodeURL = *o3tl::doAccess<bool>(rAny);
+ break;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/tblcalc.cxx b/sw/source/core/fields/tblcalc.cxx
new file mode 100644
index 000000000..d9c78be88
--- /dev/null
+++ b/sw/source/core/fields/tblcalc.cxx
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/any.hxx>
+
+#include <calc.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <expfld.hxx>
+#include <unofldmid.h>
+
+using namespace ::com::sun::star;
+
+SwTableFieldType::SwTableFieldType(SwDoc* pDocPtr)
+ : SwValueFieldType( pDocPtr, SwFieldIds::Table )
+{}
+
+std::unique_ptr<SwFieldType> SwTableFieldType::Copy() const
+{
+ return std::make_unique<SwTableFieldType>(GetDoc());
+}
+
+void SwTableField::CalcField( SwTableCalcPara& rCalcPara )
+{
+ if( rCalcPara.m_rCalc.IsCalcError() ) // stop if there is already an error set
+ return;
+
+ // create pointers from box name
+ BoxNmToPtr( rCalcPara.m_pTable );
+ OUString sFormula( MakeFormula( rCalcPara ));
+ SetValue( rCalcPara.m_rCalc.Calculate( sFormula ).GetDouble() );
+ ChgValid( !rCalcPara.IsStackOverflow() ); // is the value again valid?
+}
+
+SwTableField::SwTableField( SwTableFieldType* pInitType, const OUString& rFormel,
+ sal_uInt16 nType, sal_uLong nFormat )
+ : SwValueField( pInitType, nFormat ), SwTableFormula( rFormel ),
+ m_nSubType(nType)
+{
+ m_sExpand = "0";
+}
+
+std::unique_ptr<SwField> SwTableField::Copy() const
+{
+ std::unique_ptr<SwTableField> pTmp(new SwTableField( static_cast<SwTableFieldType*>(GetTyp()),
+ SwTableFormula::GetFormula(), m_nSubType, GetFormat() ));
+ pTmp->m_sExpand = m_sExpand;
+ pTmp->SwValueField::SetValue(GetValue());
+ pTmp->SwTableFormula::operator=( *this );
+ pTmp->SetAutomaticLanguage(IsAutomaticLanguage());
+ return std::unique_ptr<SwField>(pTmp.release());
+}
+
+OUString SwTableField::GetFieldName() const
+{
+ return GetTyp()->GetName() + " " + const_cast<SwTableField *>(this)->GetCommand();
+}
+
+/// search TextNode containing this field
+const SwNode* SwTableField::GetNodeOfFormula() const
+{
+ auto pFormat = GetTyp()->FindFormatForField(this);
+ return pFormat ? &pFormat->GetTextField()->GetTextNode() : nullptr;
+}
+
+OUString SwTableField::GetCommand()
+{
+ if (EXTRNL_NAME != GetNameType())
+ {
+ SwNode const*const pNd = GetNodeOfFormula();
+ SwTableNode const*const pTableNd = pNd ? pNd->FindTableNode() : nullptr;
+ if (pTableNd)
+ {
+ PtrToBoxNm( &pTableNd->GetTable() );
+ }
+ }
+ return (EXTRNL_NAME == GetNameType())
+ ? SwTableFormula::GetFormula()
+ : OUString();
+}
+
+OUString SwTableField::ExpandImpl(SwRootFrame const*const) const
+{
+ if (m_nSubType & nsSwExtendedSubType::SUB_CMD)
+ {
+ return const_cast<SwTableField *>(this)->GetCommand();
+ }
+
+ if(m_nSubType & nsSwGetSetExpType::GSE_STRING)
+ {
+ // it is a string
+ return m_sExpand.copy(1, m_sExpand.getLength()-2);
+ }
+
+ return m_sExpand;
+}
+
+sal_uInt16 SwTableField::GetSubType() const
+{
+ return m_nSubType;
+}
+
+void SwTableField::SetSubType(sal_uInt16 nType)
+{
+ m_nSubType = nType;
+}
+
+void SwTableField::SetValue( const double& rVal )
+{
+ SwValueField::SetValue(rVal);
+ m_sExpand = static_cast<SwValueFieldType*>(GetTyp())->ExpandValue(rVal, GetFormat(), GetLanguage());
+}
+
+OUString SwTableField::GetPar2() const
+{
+ return SwTableFormula::GetFormula();
+}
+
+void SwTableField::SetPar2(const OUString& rStr)
+{
+ SetFormula( rStr );
+}
+
+bool SwTableField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ bool bRet = true;
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_PAR2:
+ {
+ sal_uInt16 nOldSubType = m_nSubType;
+ SwTableField* pThis = const_cast<SwTableField*>(this);
+ pThis->m_nSubType |= nsSwExtendedSubType::SUB_CMD;
+ rAny <<= ExpandImpl(nullptr);
+ pThis->m_nSubType = nOldSubType;
+ }
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= 0 != (nsSwExtendedSubType::SUB_CMD & m_nSubType);
+ break;
+ case FIELD_PROP_PAR1:
+ rAny <<= m_sExpand;
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int32>(GetFormat());
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwTableField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ bool bRet = true;
+ switch ( nWhichId )
+ {
+ case FIELD_PROP_PAR2:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ SetFormula( sTmp );
+ }
+ break;
+ case FIELD_PROP_BOOL1:
+ if(*o3tl::doAccess<bool>(rAny))
+ m_nSubType = nsSwGetSetExpType::GSE_FORMULA|nsSwExtendedSubType::SUB_CMD;
+ else
+ m_nSubType = nsSwGetSetExpType::GSE_FORMULA;
+ break;
+ case FIELD_PROP_PAR1:
+ {
+ OUString sTmp;
+ rAny >>= sTmp;
+ ChgExpStr( sTmp );
+ }
+ break;
+ case FIELD_PROP_FORMAT:
+ {
+ sal_Int32 nTmp = 0;
+ rAny >>= nTmp;
+ SetFormat(nTmp);
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/textapi.cxx b/sw/source/core/fields/textapi.cxx
new file mode 100644
index 000000000..2efb97ef6
--- /dev/null
+++ b/sw/source/core/fields/textapi.cxx
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <textapi.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <docsh.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoprnms.hxx>
+#include <editeng/unoforou.hxx>
+#include <editeng/unoipset.hxx>
+
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+
+using namespace com::sun::star;
+
+static const SvxItemPropertySet* ImplGetSvxTextPortionPropertySet()
+{
+ static const SfxItemPropertyMapEntry aSvxTextPortionPropertyMap[] =
+ {
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_FONT_PROPERTIES,
+ SVX_UNOEDIT_OUTLINER_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ {u"TextField", EE_FEATURE_FIELD,
+ cppu::UnoType<text::XTextField>::get(), beans::PropertyAttribute::READONLY, 0 },
+ {u"TextPortionType", WID_PORTIONTYPE,
+ ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::READONLY, 0 },
+ {u"TextUserDefinedAttributes", EE_CHAR_XMLATTRIBS,
+ cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS,
+ cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static SvxItemPropertySet aSvxTextPortionPropertySet( aSvxTextPortionPropertyMap, EditEngine::GetGlobalItemPool() );
+ return &aSvxTextPortionPropertySet;
+}
+
+SwTextAPIObject::SwTextAPIObject( std::unique_ptr<SwTextAPIEditSource> p )
+: SvxUnoText( p.get(), ImplGetSvxTextPortionPropertySet(), uno::Reference < text::XText >() )
+, m_pSource(std::move(p))
+{
+}
+
+SwTextAPIObject::~SwTextAPIObject() noexcept
+{
+ m_pSource->Dispose();
+ m_pSource.reset();
+}
+
+struct SwTextAPIEditSource_Impl
+{
+ // needed for "internal" refcounting
+ SfxItemPool* mpPool;
+ SwDoc* mpDoc;
+ std::unique_ptr<Outliner> mpOutliner;
+ std::unique_ptr<SvxOutlinerForwarder> mpTextForwarder;
+ sal_Int32 mnRef;
+};
+
+SwTextAPIEditSource::SwTextAPIEditSource( const SwTextAPIEditSource& rSource )
+: SvxEditSource( *this )
+{
+ // shallow copy; uses internal refcounting
+ m_pImpl = rSource.m_pImpl;
+ m_pImpl->mnRef++;
+}
+
+std::unique_ptr<SvxEditSource> SwTextAPIEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new SwTextAPIEditSource( *this ));
+}
+
+void SwTextAPIEditSource::UpdateData()
+{
+ // data is kept in outliner all the time
+}
+
+SwTextAPIEditSource::SwTextAPIEditSource(SwDoc* pDoc)
+: m_pImpl(new SwTextAPIEditSource_Impl)
+{
+ m_pImpl->mpPool = &pDoc->GetDocShell()->GetPool();
+ m_pImpl->mpDoc = pDoc;
+ m_pImpl->mnRef = 1;
+}
+
+SwTextAPIEditSource::~SwTextAPIEditSource()
+{
+ if (!--m_pImpl->mnRef)
+ delete m_pImpl;
+}
+
+void SwTextAPIEditSource::Dispose()
+{
+ m_pImpl->mpPool=nullptr;
+ m_pImpl->mpDoc=nullptr;
+ m_pImpl->mpTextForwarder.reset();
+ m_pImpl->mpOutliner.reset();
+}
+
+SvxTextForwarder* SwTextAPIEditSource::GetTextForwarder()
+{
+ if( !m_pImpl->mpPool )
+ return nullptr; // mpPool == 0 can be used to flag this as disposed
+
+ if( !m_pImpl->mpOutliner )
+ {
+ //init draw model first
+ m_pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ m_pImpl->mpOutliner.reset(new Outliner(m_pImpl->mpPool, OutlinerMode::TextObject));
+ m_pImpl->mpDoc->SetCalcFieldValueHdl(m_pImpl->mpOutliner.get());
+ }
+
+ if( !m_pImpl->mpTextForwarder )
+ {
+ m_pImpl->mpTextForwarder.reset(new SvxOutlinerForwarder(*m_pImpl->mpOutliner, false));
+ }
+
+ return m_pImpl->mpTextForwarder.get();
+}
+
+void SwTextAPIEditSource::SetText( OutlinerParaObject const & rText )
+{
+ if ( m_pImpl->mpPool )
+ {
+ if( !m_pImpl->mpOutliner )
+ {
+ //init draw model first
+ m_pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ m_pImpl->mpOutliner.reset(new Outliner(m_pImpl->mpPool, OutlinerMode::TextObject));
+ m_pImpl->mpDoc->SetCalcFieldValueHdl(m_pImpl->mpOutliner.get());
+ }
+
+ m_pImpl->mpOutliner->SetText( rText );
+ }
+}
+
+void SwTextAPIEditSource::SetString( const OUString& rText )
+{
+ if ( !m_pImpl->mpPool )
+ return;
+
+ if( !m_pImpl->mpOutliner )
+ {
+ //init draw model first
+ m_pImpl->mpDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ m_pImpl->mpOutliner.reset(new Outliner(m_pImpl->mpPool, OutlinerMode::TextObject));
+ m_pImpl->mpDoc->SetCalcFieldValueHdl(m_pImpl->mpOutliner.get());
+ }
+ else
+ m_pImpl->mpOutliner->Clear();
+ m_pImpl->mpOutliner->Insert( rText );
+}
+
+std::optional<OutlinerParaObject> SwTextAPIEditSource::CreateText()
+{
+ if ( m_pImpl->mpPool && m_pImpl->mpOutliner )
+ return m_pImpl->mpOutliner->CreateParaObject();
+ else
+ return std::nullopt;
+}
+
+OUString SwTextAPIEditSource::GetText() const
+{
+ if ( m_pImpl->mpPool && m_pImpl->mpOutliner )
+ return m_pImpl->mpOutliner->GetEditEngine().GetText();
+ else
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/fields/usrfld.cxx b/sw/source/core/fields/usrfld.cxx
new file mode 100644
index 000000000..0067d3b6d
--- /dev/null
+++ b/sw/source/core/fields/usrfld.cxx
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <libxml/xmlwriter.h>
+
+#include <o3tl/any.hxx>
+
+#include <svl/numformat.hxx>
+#include <unotools/charclass.hxx>
+
+#include <calc.hxx>
+#include <usrfld.hxx>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <unofldmid.h>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/**
+ * Returns the language used for float <-> string conversions in
+ * SwUserFieldType.
+ */
+LanguageType GetFieldTypeLanguage()
+{
+ return LANGUAGE_SYSTEM;
+}
+}
+
+// Userfields
+
+SwUserField::SwUserField(SwUserFieldType* pTyp, sal_uInt16 nSub, sal_uInt32 nFormat)
+ : SwValueField(pTyp, nFormat),
+ m_nSubType(nSub)
+{
+}
+
+OUString SwUserField::ExpandImpl(SwRootFrame const*const) const
+{
+ if(!(m_nSubType & nsSwExtendedSubType::SUB_INVISIBLE))
+ return static_cast<SwUserFieldType*>(GetTyp())->Expand(GetFormat(), m_nSubType, GetLanguage());
+
+ return OUString();
+}
+
+std::unique_ptr<SwField> SwUserField::Copy() const
+{
+ std::unique_ptr<SwField> pTmp(new SwUserField(static_cast<SwUserFieldType*>(GetTyp()), m_nSubType, GetFormat()));
+ pTmp->SetAutomaticLanguage(IsAutomaticLanguage());
+ pTmp->SetTitle(GetTitle());
+ return pTmp;
+}
+
+OUString SwUserField::GetFieldName() const
+{
+ return SwFieldType::GetTypeStr(SwFieldTypesEnum::User) +
+ " " + GetTyp()->GetName() + " = " +
+ static_cast<SwUserFieldType*>(GetTyp())->GetContent();
+}
+
+double SwUserField::GetValue() const
+{
+ return static_cast<SwUserFieldType*>(GetTyp())->GetValue();
+}
+
+void SwUserField::SetValue( const double& rVal )
+{
+ static_cast<SwUserFieldType*>(GetTyp())->SetValue(rVal);
+}
+
+/// Get name
+OUString SwUserField::GetPar1() const
+{
+ return static_cast<const SwUserFieldType*>(GetTyp())->GetName();
+}
+
+/// Get content
+OUString SwUserField::GetPar2() const
+{
+ return static_cast<SwUserFieldType*>(GetTyp())->GetContent(GetFormat());
+}
+
+void SwUserField::SetPar2(const OUString& rStr)
+{
+ static_cast<SwUserFieldType*>(GetTyp())->SetContent(rStr, GetFormat());
+}
+
+sal_uInt16 SwUserField::GetSubType() const
+{
+ return static_cast<SwUserFieldType*>(GetTyp())->GetType() | m_nSubType;
+}
+
+void SwUserField::SetSubType(sal_uInt16 nSub)
+{
+ static_cast<SwUserFieldType*>(GetTyp())->SetType(nSub & 0x00ff);
+ m_nSubType = nSub & 0xff00;
+}
+
+bool SwUserField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL2:
+ rAny <<= 0 != (m_nSubType & nsSwExtendedSubType::SUB_CMD);
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= 0 == (m_nSubType & nsSwExtendedSubType::SUB_INVISIBLE);
+ break;
+ case FIELD_PROP_FORMAT:
+ rAny <<= static_cast<sal_Int32>(GetFormat());
+ break;
+ default:
+ return SwField::QueryValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+bool SwUserField::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_BOOL1:
+ if(*o3tl::doAccess<bool>(rAny))
+ m_nSubType &= (~nsSwExtendedSubType::SUB_INVISIBLE);
+ else
+ m_nSubType |= nsSwExtendedSubType::SUB_INVISIBLE;
+ break;
+ case FIELD_PROP_BOOL2:
+ if(*o3tl::doAccess<bool>(rAny))
+ m_nSubType |= nsSwExtendedSubType::SUB_CMD;
+ else
+ m_nSubType &= (~nsSwExtendedSubType::SUB_CMD);
+ break;
+ case FIELD_PROP_FORMAT:
+ {
+ sal_Int32 nTmp = 0;
+ rAny >>= nTmp;
+ SetFormat(nTmp);
+ }
+ break;
+ default:
+ return SwField::PutValue(rAny, nWhichId);
+ }
+ return true;
+}
+
+void SwUserField::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUserField"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nSubType"), BAD_CAST(OString::number(m_nSubType).getStr()));
+ SwValueField::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwUserFieldType::SwUserFieldType( SwDoc* pDocPtr, const OUString& aNam )
+ : SwValueFieldType( pDocPtr, SwFieldIds::User ),
+ m_nValue( 0 ),
+ m_nType(nsSwGetSetExpType::GSE_STRING)
+{
+ m_bValidValue = m_bDeleted = false;
+ m_aName = aNam;
+
+ EnableFormat(false); // Do not use a Numberformatter for nsSwGetSetExpType::GSE_STRING
+}
+
+OUString SwUserFieldType::Expand(sal_uInt32 nFormat, sal_uInt16 nSubType, LanguageType nLng)
+{
+ if((m_nType & nsSwGetSetExpType::GSE_EXPR) && !(nSubType & nsSwExtendedSubType::SUB_CMD))
+ {
+ EnableFormat();
+ return ExpandValue(m_nValue, nFormat, nLng);
+ }
+
+ EnableFormat(false); // Do not use a Numberformatter
+ return m_aContent;
+}
+
+std::unique_ptr<SwFieldType> SwUserFieldType::Copy() const
+{
+ std::unique_ptr<SwUserFieldType> pTmp(new SwUserFieldType( GetDoc(), m_aName ));
+ pTmp->m_aContent = m_aContent;
+ pTmp->m_aContentLang = m_aContentLang;
+ pTmp->m_nType = m_nType;
+ pTmp->m_bValidValue = m_bValidValue;
+ pTmp->m_nValue = m_nValue;
+ pTmp->m_bDeleted = m_bDeleted;
+
+ return pTmp;
+}
+
+OUString SwUserFieldType::GetName() const
+{
+ return m_aName;
+}
+
+void SwUserFieldType::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if (!pLegacy->m_pOld && !pLegacy->m_pNew)
+ m_bValidValue = false;
+ }
+
+ CallSwClientNotify(rHint);
+ // update input fields that might be connected to the user field
+ if (!IsModifyLocked())
+ {
+ LockModify();
+ GetDoc()->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Input)->UpdateFields();
+ UnlockModify();
+ }
+}
+
+double SwUserFieldType::GetValue( SwCalc& rCalc )
+{
+ if(m_bValidValue)
+ return m_nValue;
+
+ if(!rCalc.Push( this ))
+ {
+ rCalc.SetCalcError( SwCalcError::Syntax );
+ return 0;
+ }
+
+ // See if we need to temporarily switch rCalc's language: in case it
+ // differs from the field type locale.
+ const CharClass* pCharClass = rCalc.GetCharClass();
+ LanguageTag aCharClassLanguage = pCharClass->getLanguageTag();
+ LanguageTag aContentLang(m_aContentLang);
+
+ // for the call of calculate we need the language that was used for putting/setting
+ // the m_aContent string, otherwise the aContent could be interpreted wrongly,
+
+ bool bSwitchLanguage = m_aContentLang != aCharClassLanguage.getBcp47();
+
+ if (bSwitchLanguage)
+ rCalc.SetCharClass(aContentLang);
+
+ m_nValue = rCalc.Calculate( m_aContent ).GetDouble();
+
+ // we than have to set the proper char class languageTag again
+
+ if (bSwitchLanguage)
+ rCalc.SetCharClass(aCharClassLanguage);
+
+ rCalc.Pop();
+
+ if( !rCalc.IsCalcError() )
+ m_bValidValue = true;
+ else
+ m_nValue = 0;
+
+ return m_nValue;
+}
+
+OUString SwUserFieldType::GetContent( sal_uInt32 nFormat )
+{
+ if (nFormat && nFormat != SAL_MAX_UINT32)
+ {
+ OUString sFormattedValue;
+ const Color* pCol = nullptr;
+
+ SvNumberFormatter* pFormatter = GetDoc()->GetNumberFormatter();
+
+ pFormatter->GetOutputString(GetValue(), nFormat, sFormattedValue, &pCol);
+ return sFormattedValue;
+ }
+
+ return m_aContent;
+}
+
+void SwUserFieldType::SetContent( const OUString& rStr, sal_uInt32 nFormat )
+{
+ if( m_aContent == rStr )
+ return;
+
+ m_aContent = rStr;
+
+ if (nFormat && nFormat != SAL_MAX_UINT32)
+ {
+ double fValue;
+
+ if (GetDoc()->IsNumberFormat(rStr, nFormat, fValue))
+ {
+ SetValue(fValue);
+ LanguageTag aContentLanguage(GetFieldTypeLanguage());
+ m_aContentLang = aContentLanguage.getBcp47();
+ m_aContent = DoubleToString(fValue, nFormat);
+ }
+ }
+
+ bool bModified = GetDoc()->getIDocumentState().IsModified();
+ GetDoc()->getIDocumentState().SetModified();
+ if( !bModified ) // Bug 57028
+ {
+ GetDoc()->GetIDocumentUndoRedo().SetUndoNoResetModified();
+ }
+}
+
+void SwUserFieldType::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_DOUBLE:
+ rAny <<= m_nValue;
+ break;
+ case FIELD_PROP_PAR2:
+ rAny <<= m_aContent;
+ break;
+ case FIELD_PROP_BOOL1:
+ rAny <<= 0 != (nsSwGetSetExpType::GSE_EXPR&m_nType);
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void SwUserFieldType::PutValue( const uno::Any& rAny, sal_uInt16 nWhichId )
+{
+ switch( nWhichId )
+ {
+ case FIELD_PROP_DOUBLE:
+ {
+ double fVal = 0;
+ rAny >>= fVal;
+ m_nValue = fVal;
+ LanguageTag aContentLanguage(GetFieldTypeLanguage());
+ m_aContentLang = aContentLanguage.getBcp47();
+ m_aContent = DoubleToString(m_nValue, static_cast<sal_uInt16>(GetFieldTypeLanguage()));
+ }
+ break;
+ case FIELD_PROP_PAR2:
+ rAny >>= m_aContent;
+ break;
+ case FIELD_PROP_BOOL1:
+ if(*o3tl::doAccess<bool>(rAny))
+ {
+ m_nType |= nsSwGetSetExpType::GSE_EXPR;
+ m_nType &= ~nsSwGetSetExpType::GSE_STRING;
+ }
+ else
+ {
+ m_nType &= ~nsSwGetSetExpType::GSE_EXPR;
+ m_nType |= nsSwGetSetExpType::GSE_STRING;
+ }
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void SwUserFieldType::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUserFieldType"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nValue"), BAD_CAST(OString::number(m_nValue).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aContent"), BAD_CAST(m_aContent.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aContentLang"), BAD_CAST(m_aContentLang.toUtf8().getStr()));
+ SwFieldType::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/fecopy.cxx b/sw/source/core/frmedt/fecopy.cxx
new file mode 100644
index 000000000..591451480
--- /dev/null
+++ b/sw/source/core/frmedt/fecopy.cxx
@@ -0,0 +1,1629 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+
+#include <vcl/graph.hxx>
+#include <sot/formats.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/unomodel.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <tools/stream.hxx>
+#include <unotools/streamwrap.hxx>
+#include <osl/diagnose.h>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtornt.hxx>
+#include <fmtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <txtfrm.hxx>
+#include <txtflcnt.hxx>
+#include <fesh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <DocumentFieldsManager.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <tblsel.hxx>
+#include <swtable.hxx>
+#include <flyfrm.hxx>
+#include <pagefrm.hxx>
+#include <fldbas.hxx>
+#include <swundo.hxx>
+#include <viewimp.hxx>
+#include <dview.hxx>
+#include <dcontact.hxx>
+#include <dflyobj.hxx>
+#include <docsh.hxx>
+#include <pagedesc.hxx>
+#include <mvsave.hxx>
+#include <textboxhelper.hxx>
+#include <frameformats.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svdundo.hxx>
+
+using namespace ::com::sun::star;
+
+// Copy for the internal clipboard. Copies all selections to the clipboard.
+void SwFEShell::Copy( SwDoc& rClpDoc, const OUString* pNewClpText )
+{
+ rClpDoc.GetIDocumentUndoRedo().DoUndo(false); // always false!
+
+ // delete content if ClpDocument contains content
+ SwNodeIndex aSttIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 );
+ SwNodeIndex aEndNdIdx( *aSttIdx.GetNode().EndOfSectionNode() );
+ SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
+ if (!pTextNd || !pTextNd->GetText().isEmpty() ||
+ aSttIdx.GetIndex()+1 != rClpDoc.GetNodes().GetEndOfContent().GetIndex() )
+ {
+ rClpDoc.GetNodes().Delete( aSttIdx,
+ rClpDoc.GetNodes().GetEndOfContent().GetIndex() - aSttIdx.GetIndex() );
+ pTextNd = rClpDoc.GetNodes().MakeTextNode( aSttIdx,
+ rClpDoc.GetDfltTextFormatColl() );
+ --aSttIdx;
+ }
+
+ // also delete surrounding FlyFrames if any
+ for( const auto pFly : *rClpDoc.GetSpzFrameFormats() )
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) &&
+ aSttIdx <= pAPos->nNode && pAPos->nNode <= aEndNdIdx )
+ {
+ rClpDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly );
+ }
+ }
+
+ rClpDoc.GetDocumentFieldsManager().GCFieldTypes(); // delete the FieldTypes
+
+ // if a string was passed, copy it to the clipboard-
+ // document. Then also the Calculator can use the internal
+ // clipboard
+ if( pNewClpText )
+ {
+ pTextNd->InsertText( *pNewClpText, SwIndex( pTextNd ) );
+ return; // that's it
+ }
+
+ rClpDoc.getIDocumentFieldsAccess().LockExpFields();
+ rClpDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines );
+
+ // do we want to copy a FlyFrame?
+ if( IsFrameSelected() )
+ {
+ // get the FlyFormat
+ SwFlyFrame* pFly = GetSelectedFlyFrame();
+ SwFrameFormat* pFlyFormat = pFly->GetFormat();
+ SwFormatAnchor aAnchor( pFlyFormat->GetAnchor() );
+
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ SwPosition aPos( aSttIdx );
+ if ( RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId() )
+ {
+ aPos.nContent.Assign( pTextNd, 0 );
+ }
+ aAnchor.SetAnchor( &aPos );
+ }
+ pFlyFormat = rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFlyFormat, aAnchor, true, true );
+
+ // assure the "RootFormat" is the first element in Spz-Array
+ // (if necessary Flys were copied in Flys)
+ SwFrameFormats& rSpzFrameFormats = *rClpDoc.GetSpzFrameFormats();
+ if( rSpzFrameFormats[ 0 ] != pFlyFormat )
+ {
+#ifndef NDEBUG
+ bool inserted =
+#endif
+ rSpzFrameFormats.newDefault( pFlyFormat );
+ assert( !inserted && "Fly not contained in Spz-Array" );
+ }
+
+ if ( RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId() )
+ {
+ // JP 13.02.99 Bug 61863: if a frameselection is passed to the
+ // clipboard, it should be found at pasting. Therefore
+ // the copied TextAttribut should be removed in the node
+ // otherwise it will be recognised as TextSelektion
+ const SwIndex& rIdx = pFlyFormat->GetAnchor().GetContentAnchor()->nContent;
+ SwTextFlyCnt *const pTextFly = static_cast<SwTextFlyCnt *>(
+ pTextNd->GetTextAttrForCharAt(
+ rIdx.GetIndex(), RES_TXTATR_FLYCNT));
+ if( pTextFly )
+ {
+ const_cast<SwFormatFlyCnt&>(pTextFly->GetFlyCnt()).SetFlyFormat();
+ pTextNd->EraseText( rIdx, 1 );
+ }
+ }
+ }
+ else if ( IsObjSelected() )
+ {
+ SwPosition aPos( aSttIdx, SwIndex( pTextNd, 0 ));
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+
+ if( Imp()->GetDrawView()->IsGroupEntered() ||
+ ( !pObj->GetUserCall() && pObj->getParentSdrObjectFromSdrObject()) )
+ {
+ SfxItemSet aSet( rClpDoc.GetAttrPool(), aFrameFormatSetRange );
+
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AT_PARA );
+ aAnchor.SetAnchor( &aPos );
+ aSet.Put( aAnchor );
+
+ SdrObject *const pNew =
+ rClpDoc.CloneSdrObj( *pObj );
+
+ SwPaM aTemp(aPos);
+ rClpDoc.getIDocumentContentOperations().InsertDrawObj(aTemp, *pNew, aSet );
+ }
+ else
+ {
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall( pObj ));
+ SwFrameFormat *pFormat = pContact->GetFormat();
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ aAnchor.SetAnchor( &aPos );
+ }
+
+ rClpDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true );
+ }
+ }
+ }
+ else
+ CopySelToDoc(rClpDoc); // copy the selections
+
+ rClpDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE );
+ rClpDoc.getIDocumentFieldsAccess().UnlockExpFields();
+ if( !rClpDoc.getIDocumentFieldsAccess().IsExpFieldsLocked() )
+ rClpDoc.getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+}
+
+static const Point &lcl_FindBasePos( const SwFrame *pFrame, const Point &rPt )
+{
+ const SwFrame *pF = pFrame;
+ while ( pF && !pF->getFrameArea().Contains( rPt ) )
+ {
+ if ( pF->IsContentFrame() )
+ pF = static_cast<const SwContentFrame*>(pF)->GetFollow();
+ else
+ pF = nullptr;
+ }
+ if ( pF )
+ return pF->getFrameArea().Pos();
+ else
+ return pFrame->getFrameArea().Pos();
+}
+
+static bool lcl_SetAnchor( const SwPosition& rPos, const SwNode& rNd, SwFlyFrame const * pFly,
+ const Point& rInsPt, SwFEShell const & rDestShell, SwFormatAnchor& rAnchor,
+ Point& rNewPos, bool bCheckFlyRecur )
+{
+ bool bRet = true;
+ rAnchor.SetAnchor( &rPos );
+ std::pair<Point, bool> const tmp(rInsPt, false);
+ SwContentFrame *const pTmpFrame = rNd.GetContentNode()->getLayoutFrame(
+ rDestShell.GetLayout(), nullptr, &tmp);
+ SwFlyFrame *pTmpFly = pTmpFrame->FindFlyFrame();
+ if( pTmpFly && bCheckFlyRecur && pFly->IsUpperOf( *pTmpFly ) )
+ {
+ bRet = false;
+ }
+ else if ( RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId() )
+ {
+ if( pTmpFly )
+ {
+ const SwNodeIndex& rIdx = *pTmpFly->GetFormat()->GetContent().GetContentIdx();
+ SwPosition aPos( rIdx );
+ rAnchor.SetAnchor( &aPos );
+ rNewPos = pTmpFly->getFrameArea().Pos();
+ }
+ else
+ {
+ rAnchor.SetType( RndStdIds::FLY_AT_PAGE );
+ rAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) );
+ const SwFrame *pPg = pTmpFrame->FindPageFrame();
+ rNewPos = pPg->getFrameArea().Pos();
+ }
+ }
+ else
+ rNewPos = ::lcl_FindBasePos( pTmpFrame, rInsPt );
+ return bRet;
+}
+
+bool SwFEShell::CopyDrawSel( SwFEShell& rDestShell, const Point& rSttPt,
+ const Point& rInsPt, bool bIsMove, bool bSelectInsert )
+{
+ bool bRet = true;
+
+ // The list should be copied, because below new objects will be selected
+ const SdrMarkList aMrkList( Imp()->GetDrawView()->GetMarkedObjectList() );
+ const size_t nMarkCount = aMrkList.GetMarkCount();
+ if( !rDestShell.Imp()->GetDrawView() )
+ // should create it now
+ rDestShell.MakeDrawView();
+ else if( bSelectInsert )
+ rDestShell.Imp()->GetDrawView()->UnmarkAll();
+
+ SdrPageView *pDestPgView = rDestShell.Imp()->GetPageView(),
+ *pSrcPgView = Imp()->GetPageView();
+ SwDrawView *pDestDrwView = rDestShell.Imp()->GetDrawView(),
+ *pSrcDrwView = Imp()->GetDrawView();
+ SwDoc* pDestDoc = rDestShell.GetDoc();
+
+ Size aSiz( rInsPt.X() - rSttPt.X(), rInsPt.Y() - rSttPt.Y() );
+ for( size_t i = 0; i < nMarkCount; ++i )
+ {
+ SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj();
+
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall( pObj ));
+ SwFrameFormat *pFormat = pContact->GetFormat();
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+
+ bool bInsWithFormat = true;
+
+ if( pDestDrwView->IsGroupEntered() )
+ {
+ // insert into the group, when it belongs to an entered group
+ // or when the object is not anchored as a character
+ if( pSrcDrwView->IsGroupEntered() ||
+ (RndStdIds::FLY_AS_CHAR != rAnchor.GetAnchorId()) )
+
+ {
+ SdrObject* pNew = pDestDoc->CloneSdrObj( *pObj, bIsMove &&
+ GetDoc() == pDestDoc, false );
+ pNew->NbcMove( aSiz );
+ pDestDrwView->InsertObjectAtView( pNew, *pDestPgView );
+ bInsWithFormat = false;
+ }
+ }
+
+ if( bInsWithFormat )
+ {
+ SwFormatAnchor aAnchor( rAnchor );
+ Point aNewAnch;
+
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ if ( this == &rDestShell )
+ {
+ // same shell? Then request the position
+ // from the passed DocumentPosition
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( rInsPt );
+ aPt -= rSttPt - pObj->GetSnapRect().TopLeft();
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aState );
+ const SwNode *pNd;
+ if( (pNd = &aPos.nNode.GetNode())->IsNoTextNode() )
+ bRet = false;
+ else
+ bRet = ::lcl_SetAnchor( aPos, *pNd, nullptr, rInsPt,
+ rDestShell, aAnchor, aNewAnch, false );
+ }
+ else
+ {
+ SwPaM *pCursor = rDestShell.GetCursor();
+ if( pCursor->GetNode().IsNoTextNode() )
+ bRet = false;
+ else
+ bRet = ::lcl_SetAnchor( *pCursor->GetPoint(),
+ pCursor->GetNode(), nullptr, rInsPt,
+ rDestShell, aAnchor,
+ aNewAnch, false );
+ }
+ }
+ else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() )
+ {
+ aAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) );
+ const SwRootFrame* pTmpRoot = rDestShell.GetLayout();
+ const SwFrame* pPg = pTmpRoot->GetPageAtPos( rInsPt, nullptr, true );
+ if ( pPg )
+ aNewAnch = pPg->getFrameArea().Pos();
+ }
+
+ if( bRet )
+ {
+ if( pSrcDrwView->IsGroupEntered() ||
+ ( !pObj->GetUserCall() && pObj->getParentSdrObjectFromSdrObject()) )
+ {
+ SfxItemSet aSet( pDestDoc->GetAttrPool(),aFrameFormatSetRange);
+ aSet.Put( aAnchor );
+ SdrObject* pNew = pDestDoc->CloneSdrObj( *pObj, bIsMove &&
+ GetDoc() == pDestDoc );
+ pFormat = pDestDoc->getIDocumentContentOperations().InsertDrawObj( *rDestShell.GetCursor(), *pNew, aSet );
+ }
+ else
+ pFormat = pDestDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, true, true );
+
+ // Can be 0, as Draws are not allowed in Headers/Footers
+ if ( pFormat )
+ {
+ // #tdf33692 - drawing object has to be made visible on ctrl+drag copy.
+ pFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING));
+ SdrObject* pNew = pFormat->FindSdrObject();
+ if ( RndStdIds::FLY_AS_CHAR != aAnchor.GetAnchorId() )
+ {
+ Point aPos( rInsPt );
+ aPos -= aNewAnch;
+ aPos -= rSttPt - pObj->GetSnapRect().TopLeft();
+ // OD 2004-04-05 #i26791# - change attributes instead of
+ // direct positioning
+ pFormat->SetFormatAttr( SwFormatHoriOrient( aPos.getX(), text::HoriOrientation::NONE, text::RelOrientation::FRAME ) );
+ pFormat->SetFormatAttr( SwFormatVertOrient( aPos.getY(), text::VertOrientation::NONE, text::RelOrientation::FRAME ) );
+ // #i47455# - notify draw frame format
+ // that position attributes are already set.
+ if (SwDrawFrameFormat *pDrawFormat = dynamic_cast<SwDrawFrameFormat*>(pFormat))
+ pDrawFormat->PosAttrSet();
+ }
+ if (SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT, pObj))
+ {
+ SwTextBoxHelper::syncFlyFrameAttr(*pFormat, pFormat->GetAttrSet(), pObj);
+ }
+
+ if( bSelectInsert )
+ pDestDrwView->MarkObj( pNew, pDestPgView );
+ }
+ }
+ }
+ }
+
+ if ( bIsMove && bRet )
+ {
+ if( &rDestShell == this )
+ {
+ const SdrMarkList aList( pSrcDrwView->GetMarkedObjectList() );
+ pSrcDrwView->UnmarkAll();
+
+ for ( size_t i = 0, nMrkCnt = aMrkList.GetMarkCount(); i < nMrkCnt; ++i )
+ {
+ SdrObject *pObj = aMrkList.GetMark( i )->GetMarkedSdrObj();
+ pSrcDrwView->MarkObj( pObj, pSrcPgView );
+ }
+ DelSelectedObj();
+ for ( size_t i = 0, nMrkCnt = aList.GetMarkCount(); i < nMrkCnt; ++i )
+ {
+ SdrObject *pObj = aList.GetMark( i )->GetMarkedSdrObj();
+ pSrcDrwView->MarkObj( pObj, pSrcPgView );
+ }
+ }
+ else
+ DelSelectedObj();
+ }
+
+ return bRet;
+}
+
+bool SwFEShell::Copy( SwFEShell& rDestShell, const Point& rSttPt,
+ const Point& rInsPt, bool bIsMove, bool bSelectInsert )
+{
+ bool bRet = false;
+
+ OSL_ENSURE( this == &rDestShell || !rDestShell.IsObjSelected(),
+ "Dest-Shell cannot be in Obj-Mode" );
+
+ CurrShell aCurr( &rDestShell );
+
+ rDestShell.StartAllAction();
+ rDestShell.GetDoc()->getIDocumentFieldsAccess().LockExpFields();
+
+ // Shift references
+ bool bCopyIsMove = mxDoc->IsCopyIsMove();
+ if( bIsMove )
+ // set a flag in Doc, handled in TextNodes
+ mxDoc->SetCopyIsMove( true );
+
+ RedlineFlags eOldRedlMode = rDestShell.GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags();
+ rDestShell.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOldRedlMode | RedlineFlags::DeleteRedlines );
+
+ // If there are table formulas in the area, then display the table first
+ // so that the table formula can calculate a new value first
+ // (individual boxes in the area are retrieved via the layout)
+ SwFieldType* pTableFieldTyp = rDestShell.GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table );
+
+ if( IsFrameSelected() )
+ {
+ SwFlyFrame* pFly = GetSelectedFlyFrame();
+ SwFrameFormat* pFlyFormat = pFly->GetFormat();
+ SwFormatAnchor aAnchor( pFlyFormat->GetAnchor() );
+ bRet = true;
+ Point aNewAnch;
+
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ if ( this == &rDestShell )
+ {
+ // same shell? Then request the position
+ // from the passed DocumentPosition
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( rInsPt );
+ aPt -= rSttPt - pFly->getFrameArea().Pos();
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aState );
+ const SwNode *pNd;
+ if( (pNd = &aPos.nNode.GetNode())->IsNoTextNode() )
+ bRet = false;
+ else
+ {
+ // do not copy in itself
+ const SwNodeIndex *pTmp = pFlyFormat->GetContent().GetContentIdx();
+ if ( aPos.nNode > *pTmp && aPos.nNode <
+ pTmp->GetNode().EndOfSectionIndex() )
+ {
+ bRet = false;
+ }
+ else
+ bRet = ::lcl_SetAnchor( aPos, *pNd, pFly, rInsPt,
+ rDestShell, aAnchor, aNewAnch, true );
+ }
+ }
+ else
+ {
+ const SwPaM *pCursor = rDestShell.GetCursor();
+ if( pCursor->GetNode().IsNoTextNode() )
+ bRet = false;
+ else
+ bRet = ::lcl_SetAnchor( *pCursor->GetPoint(), pCursor->GetNode(),
+ pFly, rInsPt, rDestShell, aAnchor,
+ aNewAnch, GetDoc() == rDestShell.GetDoc());
+ }
+ }
+ else if ( RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId() )
+ {
+ aAnchor.SetPageNum( rDestShell.GetPageNumber( rInsPt ) );
+ const SwRootFrame* pTmpRoot = rDestShell.GetLayout();
+ const SwFrame* pPg = pTmpRoot->GetPageAtPos( rInsPt, nullptr, true );
+ if ( pPg )
+ aNewAnch = pPg->getFrameArea().Pos();
+ }
+ else {
+ OSL_ENSURE( false, "what anchor is it?" );
+ }
+
+ if( bRet )
+ {
+ SwFrameFormat *pOldFormat = pFlyFormat;
+ pFlyFormat = rDestShell.GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pFlyFormat, aAnchor, true, true );
+
+ if ( RndStdIds::FLY_AS_CHAR != aAnchor.GetAnchorId() )
+ {
+ Point aPos( rInsPt );
+ aPos -= aNewAnch;
+ aPos -= rSttPt - pFly->getFrameArea().Pos();
+ pFlyFormat->SetFormatAttr( SwFormatHoriOrient( aPos.getX(),text::HoriOrientation::NONE, text::RelOrientation::FRAME ) );
+ pFlyFormat->SetFormatAttr( SwFormatVertOrient( aPos.getY(),text::VertOrientation::NONE, text::RelOrientation::FRAME ) );
+ }
+
+ const Point aPt( rDestShell.GetCursorDocPos() );
+
+ if( bIsMove )
+ GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pOldFormat );
+
+ // only select if it can be shifted/copied in the same shell
+ if( bSelectInsert )
+ {
+ SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pFlyFormat)->GetFrame( &aPt );
+ if( pFlyFrame )
+ {
+ //JP 12.05.98: should this be in SelectFlyFrame???
+ rDestShell.Imp()->GetDrawView()->UnmarkAll();
+ rDestShell.SelectFlyFrame( *pFlyFrame );
+ }
+ }
+
+ if (this != &rDestShell && !rDestShell.HasShellFocus())
+ rDestShell.Imp()->GetDrawView()->hideMarkHandles();
+ }
+ }
+ else if ( IsObjSelected() )
+ bRet = CopyDrawSel( rDestShell, rSttPt, rInsPt, bIsMove, bSelectInsert );
+ else if( IsTableMode() )
+ {
+ // Copy parts from a table: create a table with the same
+ // width as the original and copy the selected boxes.
+ // Sizes will be corrected by percentage.
+
+ // find boxes via the layout
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes );
+ SwTableNode const*const pTableNd(
+ aBoxes.empty() ? nullptr : aBoxes[0]->GetSttNd()->FindTableNode());
+ if (nullptr != pTableNd)
+ {
+ std::unique_ptr<SwPosition> pDstPos;
+ if( this == &rDestShell )
+ {
+ // same shell? Then create new Cursor at the
+ // DocumentPosition passed
+ pDstPos.reset(new SwPosition( *GetCursor()->GetPoint() ));
+ Point aPt( rInsPt );
+ GetLayout()->GetModelPositionForViewPoint( pDstPos.get(), aPt );
+ if( !pDstPos->nNode.GetNode().IsNoTextNode() )
+ bRet = true;
+ }
+ else if( !rDestShell.GetCursor()->GetNode().IsNoTextNode() )
+ {
+ pDstPos.reset(new SwPosition( *rDestShell.GetCursor()->GetPoint() ));
+ bRet = true;
+ }
+
+ if( bRet )
+ {
+ if( GetDoc() == rDestShell.GetDoc() )
+ ParkTableCursor();
+
+ bRet = rDestShell.GetDoc()->InsCopyOfTable( *pDstPos, aBoxes,nullptr,
+ bIsMove && this == &rDestShell &&
+ aBoxes.size() == pTableNd->GetTable().
+ GetTabSortBoxes().size(),
+ this != &rDestShell );
+
+ if( this != &rDestShell )
+ *rDestShell.GetCursor()->GetPoint() = *pDstPos;
+
+ // create all parked Cursor?
+ if( GetDoc() == rDestShell.GetDoc() )
+ GetCursor();
+
+ // JP 16.04.99: Bug 64908 - Set InsPos, to assure the parked
+ // Cursor is positioned at the insert position
+ if( this == &rDestShell )
+ GetCursorDocPos() = rInsPt;
+ }
+ }
+ }
+ else
+ {
+ bRet = true;
+ if( this == &rDestShell )
+ {
+ // same shell? then request the position
+ // at the passed document position
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( rInsPt );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
+ bRet = !aPos.nNode.GetNode().IsNoTextNode();
+ }
+ else if( rDestShell.GetCursor()->GetNode().IsNoTextNode() )
+ bRet = false;
+
+ if( bRet )
+ bRet = SwEditShell::Copy( rDestShell );
+ }
+
+ rDestShell.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOldRedlMode );
+ mxDoc->SetCopyIsMove( bCopyIsMove );
+
+ // have new table formulas been inserted?
+ if( pTableFieldTyp->HasWriterListeners() )
+ {
+ // finish old actions: the table frames are created and
+ // a selection can be made
+ sal_uInt16 nActCnt;
+ for( nActCnt = 0; rDestShell.ActionPend(); ++nActCnt )
+ rDestShell.EndAllAction();
+
+ for( ; nActCnt; --nActCnt )
+ rDestShell.StartAllAction();
+ }
+ rDestShell.GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
+ rDestShell.GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);
+
+ rDestShell.EndAllAction();
+ return bRet;
+}
+
+// Paste for the internal clipboard. Copy the content of the clipboard
+// in the document
+namespace {
+ typedef std::shared_ptr<SwPaM> PaMPtr;
+ typedef std::shared_ptr<SwPosition> PositionPtr;
+ typedef std::pair< PaMPtr, PositionPtr > Insertion;
+
+ bool PamHasSelection(const SwPaM& rPaM)
+ {
+ return rPaM.HasMark() && *rPaM.GetPoint() != *rPaM.GetMark();
+ }
+
+ /// Is pFormat anchored in a fly frame which has an associated draw format?
+ bool IsInTextBox(const SwFrameFormat* pFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ const SwPosition* pPosition = rAnchor.GetContentAnchor();
+ if (!pPosition)
+ {
+ return false;
+ }
+
+ const SwStartNode* pFlyNode = pPosition->nNode.GetNode().FindFlyStartNode();
+ if (!pFlyNode)
+ {
+ return false;
+ }
+
+ for ( const auto& pSpzFormat : *pFormat->GetDoc()->GetSpzFrameFormats() )
+ {
+ if (pSpzFormat->Which() != RES_FLYFRMFMT)
+ {
+ continue;
+ }
+
+ const SwNodeIndex* pIdx = pSpzFormat->GetContent().GetContentIdx();
+ if (!pIdx || pFlyNode != &pIdx->GetNode())
+ {
+ continue;
+ }
+
+ return SwTextBoxHelper::isTextBox(pSpzFormat, RES_FLYFRMFMT);
+ }
+
+ return false;
+ }
+}
+
+namespace {
+ SwFrameFormat* lcl_PasteFlyOrDrawFormat(SwPaM& rPaM, SwFrameFormat* pCpyFormat, SwFEShell& rSh)
+ {
+ auto& rImp = *rSh.Imp();
+ auto& rDoc = *rSh.GetDoc();
+ auto& rDrawView = *rImp.GetDrawView();
+ if(rDrawView.IsGroupEntered() &&
+ RES_DRAWFRMFMT == pCpyFormat->Which() &&
+ (RndStdIds::FLY_AS_CHAR != pCpyFormat->GetAnchor().GetAnchorId()))
+ {
+ const SdrObject* pSdrObj = pCpyFormat->FindSdrObject();
+ if(pSdrObj)
+ {
+ SdrObject* pNew = rDoc.CloneSdrObj(*pSdrObj, false, false);
+ // Insert object sets any anchor position to 0.
+ // Therefore we calculate the absolute position here
+ // and after the insert the anchor of the object
+ // is set to the anchor of the group object.
+ tools::Rectangle aSnapRect = pNew->GetSnapRect();
+ if(pNew->GetAnchorPos().X() || pNew->GetAnchorPos().Y())
+ {
+ const Point aPoint(0, 0);
+ // OD 2004-04-05 #i26791# - direct drawing object
+ // positioning for group members
+ pNew->NbcSetAnchorPos(aPoint);
+ pNew->NbcSetSnapRect(aSnapRect);
+ }
+
+ rDrawView.InsertObjectAtView(pNew, *rImp.GetPageView());
+
+ Point aGrpAnchor(0, 0);
+ SdrObjList* pList = pNew->getParentSdrObjListFromSdrObject();
+ if(pList)
+ {
+ SdrObjGroup* pOwner(dynamic_cast<SdrObjGroup*>(pList->getSdrObjectFromSdrObjList()));
+
+ if(nullptr != pOwner)
+ aGrpAnchor = pOwner->GetAnchorPos();
+ }
+
+ // OD 2004-04-05 #i26791# - direct drawing object
+ // positioning for group members
+ pNew->NbcSetAnchorPos(aGrpAnchor);
+ pNew->SetSnapRect(aSnapRect);
+ return nullptr;
+ }
+ }
+ SwFormatAnchor aAnchor(pCpyFormat->GetAnchor());
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ SwPosition* pPos = rPaM.GetPoint();
+ // allow shapes (no controls) in header/footer
+ if(RES_DRAWFRMFMT == pCpyFormat->Which() && rDoc.IsInHeaderFooter(pPos->nNode))
+ {
+ const SdrObject *pCpyObj = pCpyFormat->FindSdrObject();
+ if(pCpyObj && CheckControlLayer(pCpyObj))
+ return nullptr;
+ }
+ else if(pCpyFormat->Which() == RES_FLYFRMFMT && IsInTextBox(pCpyFormat))
+ {
+ // This is a fly frame which is anchored in a TextBox, ignore it as
+ // it's already copied as part of copying the content of the
+ // TextBox.
+ return nullptr;
+ }
+ // Ignore TextBoxes, they are already handled in sw::DocumentLayoutManager::CopyLayoutFormat().
+ if(SwTextBoxHelper::isTextBox(pCpyFormat, RES_FLYFRMFMT))
+ return nullptr;
+ aAnchor.SetAnchor(pPos);
+ }
+ else if(RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId())
+ {
+ aAnchor.SetPageNum(rSh.GetPhyPageNum());
+ }
+ else if(RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId())
+ {
+ Point aPt;
+ (void)lcl_SetAnchor(*rPaM.GetPoint(), rPaM.GetNode(), nullptr, aPt, rSh, aAnchor, aPt, false);
+ }
+
+ SwFrameFormat* pNew = rDoc.getIDocumentLayoutAccess().CopyLayoutFormat(*pCpyFormat, aAnchor, true, true);
+ return pNew;
+ }
+
+ void lcl_SelectFlyFormat(SwFrameFormat *const pNew, SwFEShell& rSh)
+ {
+ if(!pNew)
+ return;
+ switch(pNew->Which())
+ {
+ case RES_FLYFRMFMT:
+ {
+ assert(dynamic_cast<SwFlyFrameFormat*>(pNew));
+ const Point aPt(rSh.GetCursorDocPos());
+ SwFlyFrame* pFlyFrame = static_cast<SwFlyFrameFormat*>(pNew)->GetFrame(&aPt);
+ if(pFlyFrame)
+ rSh.SelectFlyFrame(*pFlyFrame);
+ break;
+ }
+ case RES_DRAWFRMFMT:
+ {
+ auto& rDrawView = *rSh.Imp()->GetDrawView();
+ assert(dynamic_cast<SwDrawFrameFormat*>(pNew));
+ SwDrawFrameFormat* pDrawFormat = static_cast<SwDrawFrameFormat*>(pNew);
+ // #i52780# - drawing object has to be made visible on paste.
+ pDrawFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREPPASTING));
+ SdrObject* pObj = pDrawFormat->FindSdrObject();
+ rDrawView.MarkObj(pObj, rDrawView.GetSdrPageView());
+ // #i47455# - notify draw frame format
+ // that position attributes are already set.
+ pDrawFormat->PosAttrSet();
+ break;
+ }
+ default:
+ SAL_WARN("sw.core", "unknown fly type");
+ }
+ }
+}
+
+bool SwFEShell::Paste(SwDoc& rClpDoc, bool bNestedTable)
+{
+ CurrShell aCurr( this );
+ // then till end of the nodes array
+ SwNodeIndex aIdx( rClpDoc.GetNodes().GetEndOfExtras(), 2 );
+ // select content section, whatever it may contain
+ SwPaM aCpyPam(aIdx, SwNodeIndex(rClpDoc.GetNodes().GetEndOfContent(), -1));
+ if (SwContentNode *const pAtEnd = aCpyPam.GetNode(true).GetContentNode())
+ {
+ pAtEnd->MakeEndIndex(&aCpyPam.GetPoint()->nContent);
+ }
+
+ // If there are table formulas in the area, then display the table first
+ // so that the table formula can calculate a new value first
+ // (individual boxes in the area are retrieved via the layout)
+ SwFieldType* pTableFieldTyp = GetDoc()->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::Table );
+
+ SwTableNode *const pSrcNd = aCpyPam.GetNode(false).GetTableNode();
+
+ bool bRet = true;
+ StartAllAction();
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSGLOSSARY, nullptr );
+ GetDoc()->getIDocumentFieldsAccess().LockExpFields();
+
+ // When the clipboard content has been created by a rectangular selection
+ // the pasting is more sophisticated:
+ // every paragraph will be inserted into another position.
+ // The first positions are given by the actual cursor ring,
+ // if there are more text portions to insert than cursor in this ring,
+ // the additional insert positions will be created by moving the last
+ // cursor position into the next line (like pressing the cursor down key)
+ if( rClpDoc.IsColumnSelection() && !IsTableMode() )
+ {
+ // Creation of the list of insert positions
+ std::vector< Insertion > aCopyVector;
+ // The number of text portions of the rectangular selection
+ const SwNodeOffset nSelCount = aCpyPam.GetPoint()->nNode.GetIndex()
+ - aCpyPam.GetMark()->nNode.GetIndex();
+ SwNodeOffset nCount = nSelCount;
+ SwNodeIndex aClpIdx( aIdx );
+ SwPaM* pStartCursor = GetCursor();
+ SwPaM* pCurrCursor = pStartCursor;
+ SwNodeOffset nCursorCount( pStartCursor->GetRingContainer().size() );
+ // If the target selection is a multi-selection, often the last and first
+ // cursor of the ring points to identical document positions. Then
+ // we should avoid double insertion of text portions...
+ while( nCursorCount > SwNodeOffset(1) && *pCurrCursor->GetPoint() ==
+ *(pCurrCursor->GetPrev()->GetPoint()) )
+ {
+ --nCursorCount;
+ pCurrCursor = pCurrCursor->GetNext();
+ pStartCursor = pCurrCursor;
+ }
+ SwPosition aStartPos( *pStartCursor->GetPoint() );
+ SwPosition aInsertPos( aStartPos ); // first insertion position
+ bool bCompletePara = false;
+ sal_uInt16 nMove = 0;
+ while( nCount )
+ {
+ --nCount;
+ OSL_ENSURE( aIdx.GetNode().GetContentNode(), "Who filled the clipboard?!" );
+ if( aIdx.GetNode().GetContentNode() ) // robust
+ {
+ Insertion aInsertion( std::make_shared<SwPaM>( aIdx ),
+ std::make_shared<SwPosition>( aInsertPos ) );
+ ++aIdx;
+ aInsertion.first->SetMark();
+ if( pStartCursor == pCurrCursor->GetNext() )
+ { // Now we have to look for insertion positions...
+ if( !nMove ) // Annotate the last given insert position
+ aStartPos = aInsertPos;
+ SwCursor aCursor( aStartPos, nullptr);
+ // Check if we find another insert position by moving
+ // down the last given position
+ if (aCursor.UpDown(false, ++nMove, nullptr, 0, *GetLayout()))
+ aInsertPos = *aCursor.GetPoint();
+ else // if there is no paragraph we have to create it
+ bCompletePara = nCount > SwNodeOffset(0);
+ nCursorCount = SwNodeOffset(0);
+ }
+ else // as long as we find more insert positions in the cursor ring
+ { // we'll take them
+ pCurrCursor = pCurrCursor->GetNext();
+ aInsertPos = *pCurrCursor->GetPoint();
+ --nCursorCount;
+ }
+ // If there are no more paragraphs e.g. at the end of a document,
+ // we insert complete paragraphs instead of text portions
+ if( bCompletePara )
+ aInsertion.first->GetPoint()->nNode = aIdx;
+ else
+ aInsertion.first->GetPoint()->nContent =
+ aInsertion.first->GetContentNode()->Len();
+ aCopyVector.push_back( aInsertion );
+ }
+ // If there are no text portions left but there are some more
+ // cursor positions to fill we have to restart with the first
+ // text portion
+ if( !nCount && nCursorCount )
+ {
+ nCount = min( nSelCount, nCursorCount );
+ aIdx = aClpIdx; // Start of clipboard content
+ }
+ }
+ for (auto const& item : aCopyVector)
+ {
+ SwPosition& rInsPos = *item.second;
+ SwPaM& rCopy = *item.first;
+ const SwStartNode* pBoxNd = rInsPos.nNode.GetNode().FindTableBoxStartNode();
+ if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() - pBoxNd->GetIndex() &&
+ rCopy.GetPoint()->nNode != rCopy.GetMark()->nNode )
+ {
+ // if more than one node will be copied into a cell
+ // the box attributes have to be removed
+ GetDoc()->ClearBoxNumAttrs( rInsPos.nNode );
+ }
+ {
+ SwNodeIndex aIndexBefore(rInsPos.nNode);
+ --aIndexBefore;
+ rClpDoc.getIDocumentContentOperations().CopyRange(rCopy, rInsPos, SwCopyFlags::CheckPosInFly);
+ {
+ ++aIndexBefore;
+ SwPaM aPaM(SwPosition(aIndexBefore),
+ SwPosition(rInsPos.nNode));
+ aPaM.GetDoc().MakeUniqueNumRules(aPaM);
+ }
+ }
+ SaveTableBoxContent( &rInsPos );
+ }
+ }
+ else
+ {
+ bool bDelTable = true;
+
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+
+ SwTableNode *const pDestNd(GetDoc()->IsIdxInTable(rPaM.GetPoint()->nNode));
+ if (pSrcNd && nullptr != pDestNd &&
+ // not a forced nested table insertion
+ !bNestedTable &&
+ // Heuristics to allow copying table rows or nesting tables without
+ // using Edit -> Paste Special -> Paste as Nested Table:
+ // Using table cursor, or if the text selection starts in the
+ // first paragraph, or if there is no selection and the text cursor
+ // is there in the first paragraph, overwrite content of the cell(s)
+ // (else insert a nested table later, i.e. if nothing selected and
+ // the cursor is not in the first paragraph, or the selected text
+ // doesn't contain the first paragraph of the cell)
+ rPaM.GetNode().GetIndex() == rPaM.GetNode().FindTableBoxStartNode()->GetIndex() + 1)
+ {
+ SwPosition aDestPos( *rPaM.GetPoint() );
+
+ bool bParkTableCursor = false;
+ const SwStartNode* pSttNd = rPaM.GetNode().FindTableBoxStartNode();
+
+ // TABLE IN TABLE: copy table in table
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ if( IsTableMode() ) // table selection?
+ {
+ GetTableSel( *this, aBoxes );
+ ParkTableCursor();
+ bParkTableCursor = true;
+ }
+ else if( !PamHasSelection(rPaM) && rPaM.GetNext() == &rPaM &&
+ ( !pSrcNd->GetTable().IsTableComplex() ||
+ pDestNd->GetTable().IsNewModel() ) )
+ {
+ // make relative table copy
+ SwTableBox* pBox = pDestNd->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "Box is not in this table" );
+ aBoxes.insert( pBox );
+ }
+
+ SwNodeIndex aNdIdx( *pDestNd->EndOfSectionNode());
+ if( !bParkTableCursor )
+ {
+ // exit first the complete table
+ // ???? what about only table in a frame ?????
+ SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aNdIdx );
+ SwPosition aPos( aNdIdx, SwIndex( pCNd, 0 ));
+ // #i59539: Don't remove all redline
+ SwPaM const tmpPaM(*pDestNd, *pDestNd->EndOfSectionNode());
+ ::PaMCorrAbs(tmpPaM, aPos);
+ }
+
+ bRet = GetDoc()->InsCopyOfTable( aDestPos, aBoxes, &pSrcNd->GetTable() );
+
+ if( bParkTableCursor )
+ GetCursor();
+ else
+ {
+ // return to the box
+ aNdIdx = *pSttNd;
+ SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aNdIdx );
+ SwPosition aPos( aNdIdx, SwIndex( pCNd, 0 ));
+ // #i59539: Don't remove all redline
+ SwNode & rNode(rPaM.GetPoint()->nNode.GetNode());
+ SwContentNode *const pContentNode( rNode.GetContentNode() );
+ SwPaM const tmpPam(rNode, 0,
+ rNode, pContentNode ? pContentNode->Len() : 0);
+ ::PaMCorrAbs(tmpPam, aPos);
+ }
+
+ break; // exit the "while-loop"
+ }
+ else if(*aCpyPam.GetPoint() == *aCpyPam.GetMark() && !rClpDoc.GetSpzFrameFormats()->empty())
+ {
+ // we need a DrawView
+ if(!Imp()->GetDrawView())
+ MakeDrawView();
+ ::std::vector<SwFrameFormat*> inserted;
+ for (auto const pFlyFormat : *rClpDoc.GetSpzFrameFormats())
+ {
+ // if anchored inside other fly, will be copied when copying
+ // top-level fly, so skip here! (other non-body anchor
+ // shouldn't happen here)
+ SwFormatAnchor const& rAnchor(pFlyFormat->GetAnchor());
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()
+ || rClpDoc.GetNodes().GetEndOfExtras().GetIndex() < rAnchor.GetContentAnchor()->nNode.GetIndex())
+ {
+ inserted.emplace_back(
+ lcl_PasteFlyOrDrawFormat(rPaM, pFlyFormat, *this));
+ }
+ }
+ for (auto const pFlyFormat : inserted)
+ {
+ lcl_SelectFlyFormat(pFlyFormat, *this);
+ }
+ }
+ else
+ {
+ if( bDelTable && IsTableMode() )
+ {
+ SwEditShell::Delete(false);
+ bDelTable = false;
+ }
+
+ SwPosition& rInsPos = *rPaM.GetPoint();
+ const SwStartNode* pBoxNd = rInsPos.nNode.GetNode().
+ FindTableBoxStartNode();
+ if( pBoxNd && SwNodeOffset(2) == pBoxNd->EndOfSectionIndex() -
+ pBoxNd->GetIndex() &&
+ aCpyPam.GetPoint()->nNode != aCpyPam.GetMark()->nNode )
+ {
+ // Copy more than 1 node in the current box. But
+ // then the BoxAttribute should be removed
+ GetDoc()->ClearBoxNumAttrs( rInsPos.nNode );
+ }
+
+ // **
+ // ** Update SwDoc::Append, if you change the following code **
+ // **
+ {
+ SwNodeIndex aIndexBefore(rInsPos.nNode);
+
+ --aIndexBefore;
+
+ rClpDoc.getIDocumentContentOperations().CopyRange(aCpyPam, rInsPos, SwCopyFlags::CheckPosInFly);
+ // Note: aCpyPam is invalid now
+
+ ++aIndexBefore;
+ SwPaM aPaM(SwPosition(aIndexBefore),
+ SwPosition(rInsPos.nNode));
+
+ aPaM.GetDoc().MakeUniqueNumRules(aPaM);
+
+ // Update the rsid of each pasted text node.
+ SwNodes &rDestNodes = GetDoc()->GetNodes();
+ SwNodeOffset const nEndIdx = aPaM.End()->nNode.GetIndex();
+
+ for (SwNodeOffset nIdx = aPaM.Start()->nNode.GetIndex();
+ nIdx <= nEndIdx; ++nIdx)
+ {
+ SwTextNode *const pTextNode = rDestNodes[nIdx]->GetTextNode();
+ if ( pTextNode )
+ {
+ GetDoc()->UpdateParRsid( pTextNode );
+ }
+ }
+ }
+
+ SaveTableBoxContent( &rInsPos );
+ }
+ }
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSGLOSSARY, nullptr );
+
+ // have new table formulas been inserted?
+ if( pTableFieldTyp->HasWriterListeners() )
+ {
+ // finish old action: table-frames have been created
+ // a selection can be made now
+ sal_uInt16 nActCnt;
+ for( nActCnt = 0; ActionPend(); ++nActCnt )
+ EndAllAction();
+
+ for( ; nActCnt; --nActCnt )
+ StartAllAction();
+ }
+ GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
+ GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);
+ EndAllAction();
+
+ return bRet;
+}
+
+void SwFEShell::PastePages( SwFEShell& rToFill, sal_uInt16 nStartPage, sal_uInt16 nEndPage)
+{
+ Push();
+ if(!GotoPage(nStartPage))
+ {
+ Pop(PopMode::DeleteCurrent);
+ return;
+ }
+ MovePage( GetThisFrame, GetFirstSub );
+ ::std::optional<SwPaM> oSourcePam( *GetCursor()->GetPoint() );
+ OUString sStartingPageDesc = GetPageDesc( GetCurPageDesc()).GetName();
+ SwPageDesc* pDesc = rToFill.FindPageDescByName( sStartingPageDesc, true );
+ if( pDesc )
+ rToFill.ChgCurPageDesc( *pDesc );
+
+ if(!GotoPage(nEndPage))
+ {
+ Pop(PopMode::DeleteCurrent);
+ return;
+ }
+ //if the page starts with a table a paragraph has to be inserted before
+ SwNode *const pTableNode = oSourcePam->GetNode().FindTableNode();
+ if(pTableNode)
+ {
+ //insert a paragraph
+ StartUndo(SwUndoId::INSERT);
+ SwNodeIndex aTableIdx( *pTableNode, -1 );
+ SwPosition aBefore(aTableIdx);
+ if(GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore ))
+ {
+ SwPaM aTmp(aBefore);
+ *oSourcePam = aTmp;
+ }
+ EndUndo(SwUndoId::INSERT);
+ }
+
+ MovePage( GetThisFrame, GetLastSub );
+ oSourcePam->SetMark();
+ *oSourcePam->GetMark() = *GetCursor()->GetPoint();
+
+ CurrShell aCurr( this );
+
+ StartAllAction();
+ GetDoc()->getIDocumentFieldsAccess().LockExpFields();
+ SetSelection(*oSourcePam);
+ // copy the text of the selection
+ SwEditShell::Copy(rToFill);
+ oSourcePam.reset(); // delete it because Undo will remove its node!
+
+ if(pTableNode)
+ {
+ //remove the inserted paragraph
+ Undo();
+ //remove the paragraph in the second doc, too
+ SwNodeIndex aIdx( rToFill.GetDoc()->GetNodes().GetEndOfExtras(), 2 );
+ SwPaM aPara( aIdx ); //DocStart
+ rToFill.GetDoc()->getIDocumentContentOperations().DelFullPara(aPara);
+ }
+ // now the page bound objects
+ // additionally copy page bound frames
+ if( !GetDoc()->GetSpzFrameFormats()->empty() )
+ {
+ // create a draw view if necessary
+ if( !rToFill.Imp()->GetDrawView() )
+ rToFill.MakeDrawView();
+
+ for ( auto pCpyFormat : *GetDoc()->GetSpzFrameFormats() )
+ {
+ SwFormatAnchor aAnchor( pCpyFormat->GetAnchor() );
+ if ((RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()) &&
+ aAnchor.GetPageNum() >= nStartPage && aAnchor.GetPageNum() <= nEndPage)
+ {
+ aAnchor.SetPageNum( aAnchor.GetPageNum() - nStartPage + 1);
+ }
+ else
+ continue;
+ rToFill.GetDoc()->getIDocumentLayoutAccess().CopyLayoutFormat( *pCpyFormat, aAnchor, true, true );
+ }
+ }
+ GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
+ GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);
+ Pop(PopMode::DeleteCurrent);
+ EndAllAction();
+}
+
+comphelper::OInterfaceContainerHelper3<css::text::XPasteListener>& SwFEShell::GetPasteListeners() { return m_aPasteListeners; }
+
+bool SwFEShell::GetDrawObjGraphic( SotClipboardFormatId nFormat, Graphic& rGrf ) const
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "GetDrawObjGraphic without DrawView?" );
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ bool bConvert = true;
+ if( rMrkList.GetMarkCount() )
+ {
+ if( rMrkList.GetMarkCount() == 1 &&
+ dynamic_cast< const SwVirtFlyDrawObj* >(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) != nullptr )
+ {
+ // select frame
+ if( CNT_GRF == GetCntType() )
+ {
+ const Graphic* pGrf( GetGraphic() );
+ if ( pGrf )
+ {
+ Graphic aGrf( *pGrf );
+ if( SotClipboardFormatId::GDIMETAFILE == nFormat )
+ {
+ if( GraphicType::Bitmap != aGrf.GetType() )
+ {
+ rGrf = aGrf;
+ bConvert = false;
+ }
+ else if( GetWin() )
+ {
+ Size aSz;
+ Point aPt;
+ GetGrfSize( aSz );
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev;
+ pVirtDev->EnableOutput( false );
+
+ MapMode aTmp( GetWin()->GetMapMode() );
+ aTmp.SetOrigin( aPt );
+ pVirtDev->SetMapMode( aTmp );
+
+ GDIMetaFile aMtf;
+ aMtf.Record( pVirtDev.get() );
+ aGrf.Draw(*pVirtDev, aPt, aSz);
+ aMtf.Stop();
+ aMtf.SetPrefMapMode( aTmp );
+ aMtf.SetPrefSize( aSz );
+ rGrf = aMtf;
+ }
+ }
+ else if( GraphicType::Bitmap == aGrf.GetType() )
+ {
+ rGrf = aGrf;
+ bConvert = false;
+ }
+ else
+ {
+ // Not the original size, but the current one.
+ // Otherwise it could happen that for vector graphics
+ // many MB's of memory are allocated.
+ const Size aSz( GetSelectedFlyFrame()->getFramePrintArea().SSize() );
+ ScopedVclPtrInstance< VirtualDevice > pVirtDev(*GetWin()->GetOutDev());
+
+ MapMode aTmp( MapUnit::MapTwip );
+ pVirtDev->SetMapMode( aTmp );
+ if( pVirtDev->SetOutputSize( aSz ) )
+ {
+ aGrf.Draw(*pVirtDev, Point(), aSz);
+ rGrf = pVirtDev->GetBitmapEx( Point(), aSz );
+ }
+ else
+ {
+ rGrf = aGrf;
+ bConvert = false;
+ }
+ }
+ }
+ }
+ }
+ else if( SotClipboardFormatId::GDIMETAFILE == nFormat )
+ rGrf = Imp()->GetDrawView()->GetMarkedObjMetaFile();
+ else if( SotClipboardFormatId::BITMAP == nFormat || SotClipboardFormatId::PNG == nFormat )
+ rGrf = Imp()->GetDrawView()->GetMarkedObjBitmapEx();
+ }
+ return bConvert;
+}
+
+// #i50824#
+// replace method <lcl_RemoveOleObjsFromSdrModel> by <lcl_ConvertSdrOle2ObjsToSdrGrafObjs>
+static void lcl_ConvertSdrOle2ObjsToSdrGrafObjs( SdrModel& _rModel )
+{
+ for ( sal_uInt16 nPgNum = 0; nPgNum < _rModel.GetPageCount(); ++nPgNum )
+ {
+ // setup object iterator in order to iterate through all objects
+ // including objects in group objects, but exclusive group objects.
+ SdrObjListIter aIter(_rModel.GetPage(nPgNum));
+ while( aIter.IsMore() )
+ {
+ SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( aIter.Next() );
+ if( pOle2Obj )
+ {
+ // found an ole2 shape
+ SdrObjList* pObjList = pOle2Obj->getParentSdrObjListFromSdrObject();
+
+ // get its graphic
+ Graphic aGraphic;
+ pOle2Obj->Connect();
+ const Graphic* pGraphic = pOle2Obj->GetGraphic();
+ if( pGraphic )
+ aGraphic = *pGraphic;
+ pOle2Obj->Disconnect();
+
+ // create new graphic shape with the ole graphic and shape size
+ SdrGrafObj* pGraphicObj = new SdrGrafObj(
+ _rModel,
+ aGraphic,
+ pOle2Obj->GetCurrentBoundRect());
+ // apply layer of ole2 shape at graphic shape
+ pGraphicObj->SetLayer( pOle2Obj->GetLayer() );
+
+ // replace ole2 shape with the new graphic object and delete the ol2 shape
+ SdrObject* pRemovedObject = pObjList->ReplaceObject( pGraphicObj, pOle2Obj->GetOrdNum() );
+ SdrObject::Free( pRemovedObject );
+ }
+ }
+ }
+}
+
+void SwFEShell::Paste( SvStream& rStrm, SwPasteSdr nAction, const Point* pPt )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ StartUndo();
+
+ std::unique_ptr< FmFormModel > pModel(
+ new FmFormModel(
+ nullptr,
+ GetDoc()->GetDocShell()));
+
+ pModel->GetItemPool().FreezeIdRanges();
+
+ rStrm.Seek(0);
+
+ uno::Reference< io::XInputStream > xInputStream( new utl::OInputStreamWrapper( rStrm ) );
+ SvxDrawingLayerImport( pModel.get(), xInputStream );
+
+ if ( !Imp()->HasDrawView() )
+ Imp()->MakeDrawView();
+
+ Point aPos( pPt ? *pPt : GetCharRect().Pos() );
+ SdrView *pView = Imp()->GetDrawView();
+
+ // drop on the existing object: replace object or apply new attributes
+ if( pModel->GetPageCount() > 0 &&
+ 1 == pModel->GetPage(0)->GetObjCount() &&
+ 1 == pView->GetMarkedObjectList().GetMarkCount() )
+ {
+ // replace a marked 'virtual' drawing object
+ // by its corresponding 'master' drawing object in the mark list.
+ SwDrawView::ReplaceMarkedDrawVirtObjs( *pView );
+
+ SdrObject* pClpObj = pModel->GetPage(0)->GetObj(0);
+ SdrObject* pOldObj = pView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj();
+
+ if( SwPasteSdr::SetAttr == nAction && dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) != nullptr )
+ nAction = SwPasteSdr::Replace;
+
+ switch( nAction )
+ {
+ case SwPasteSdr::Replace:
+ {
+ const SwFrameFormat* pFormat(nullptr);
+ const SwFrame* pAnchor(nullptr);
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) != nullptr )
+ {
+ pFormat = FindFrameFormat( pOldObj );
+
+ Point aNullPt;
+ SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrameFormat*>(pFormat)->GetFrame( &aNullPt );
+ pAnchor = pFlyFrame ? pFlyFrame->GetAnchorFrame() : nullptr;
+
+ if (!pAnchor || pAnchor->FindFooterOrHeader())
+ {
+ // if there is a textframe in the header/footer:
+ // do not replace but insert
+ nAction = SwPasteSdr::Insert;
+ break;
+ }
+ }
+
+ SdrObject* pNewObj(pClpObj->CloneSdrObject(pOldObj->getSdrModelFromSdrObject()));
+ tools::Rectangle aOldObjRect( pOldObj->GetCurrentBoundRect() );
+ Size aOldObjSize( aOldObjRect.GetSize() );
+ tools::Rectangle aNewRect( pNewObj->GetCurrentBoundRect() );
+ Size aNewSize( aNewRect.GetSize() );
+
+ Fraction aScaleWidth( aOldObjSize.Width(), aNewSize.Width() );
+ Fraction aScaleHeight( aOldObjSize.Height(), aNewSize.Height());
+ pNewObj->NbcResize( aNewRect.TopLeft(), aScaleWidth, aScaleHeight);
+
+ Point aVec = aOldObjRect.TopLeft() - aNewRect.TopLeft();
+ pNewObj->NbcMove(Size(aVec.getX(), aVec.getY()));
+
+ if( dynamic_cast<const SdrUnoObj*>( pNewObj) != nullptr )
+ pNewObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetControlsId() );
+ else if( dynamic_cast<const SdrUnoObj*>( pOldObj) != nullptr )
+ pNewObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetHeavenId() );
+ else
+ pNewObj->SetLayer( pOldObj->GetLayer() );
+
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pOldObj) != nullptr )
+ {
+ // store attributes, then set SdrObject
+ SfxItemSetFixed<RES_SURROUND, RES_ANCHOR> aFrameSet( mxDoc->GetAttrPool() );
+ aFrameSet.Set( pFormat->GetAttrSet() );
+
+ Point aNullPt;
+ if( pAnchor->IsTextFrame() && static_cast<const SwTextFrame*>(pAnchor)->IsFollow() )
+ {
+ const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnchor);
+ do {
+ pTmp = pTmp->FindMaster();
+ OSL_ENSURE( pTmp, "Where's my Master?" );
+ } while( pTmp->IsFollow() );
+ pAnchor = pTmp;
+ }
+ if( auto pCaptionObj = dynamic_cast<SdrCaptionObj*>( pOldObj))
+ aNullPt = pCaptionObj->GetTailPos();
+ else
+ aNullPt = aOldObjRect.TopLeft();
+
+ Point aNewAnchor = pAnchor->GetFrameAnchorPos( ::HasWrap( pOldObj ) );
+ // OD 2004-04-05 #i26791# - direct positioning of Writer
+ // fly frame object for <SwDoc::Insert(..)>
+ pNewObj->NbcSetRelativePos( aNullPt - aNewAnchor );
+ pNewObj->NbcSetAnchorPos( aNewAnchor );
+
+ pOldObj->GetOrdNum();
+
+ DelSelectedObj();
+
+ GetDoc()->getIDocumentContentOperations().InsertDrawObj( *GetCursor(), *pNewObj, aFrameSet );
+ }
+ else
+ {
+ // #i123922# for handling MasterObject and virtual ones correctly, SW
+ // wants us to call ReplaceObject at the page, but that also
+ // triggers the same assertion (I tried it), so stay at the view method
+ pView->ReplaceObjectAtView(pOldObj, *Imp()->GetPageView(), pNewObj);
+ }
+ }
+ break;
+
+ case SwPasteSdr::SetAttr:
+ {
+ SfxItemSet aSet( GetAttrPool() );
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pClpObj);
+
+ if(pSdrGrafObj)
+ {
+ SdrObject* pTarget = nullptr;
+
+ if(0 != pView->GetMarkedObjectList().GetMarkCount())
+ {
+ // try to get target (if it's at least one, take first)
+ SdrMark* pMark = pView->GetMarkedObjectList().GetMark(0);
+
+ if(pMark)
+ {
+ pTarget = pMark->GetMarkedSdrObj();
+ }
+ }
+
+ if(pTarget)
+ {
+ // copy ItemSet from target
+ aSet.Set(pTarget->GetMergedItemSet());
+ }
+
+ // for SdrGrafObj, use the graphic as fill style argument
+ const Graphic& rGraphic = pSdrGrafObj->GetGraphic();
+
+ if(GraphicType::NONE != rGraphic.GetType() && GraphicType::Default != rGraphic.GetType())
+ {
+ aSet.Put(XFillBitmapItem(OUString(), rGraphic));
+ aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+ }
+ }
+ else
+ {
+ aSet.Put(pClpObj->GetMergedItemSet());
+ }
+
+ pView->SetAttributes( aSet );
+ }
+ break;
+
+ default:
+ nAction = SwPasteSdr::Insert;
+ break;
+ }
+ }
+ else
+ nAction = SwPasteSdr::Insert;
+
+ if( SwPasteSdr::Insert == nAction )
+ {
+ ::sw::DrawUndoGuard drawUndoGuard(GetDoc()->GetIDocumentUndoRedo());
+
+ bool bDesignMode = pView->IsDesignMode();
+ if( !bDesignMode )
+ pView->SetDesignMode();
+
+ // #i50824#
+ // method <lcl_RemoveOleObjsFromSdrModel> replaced by <lcl_ConvertSdrOle2ObjsToSdrGrafObjs>
+ lcl_ConvertSdrOle2ObjsToSdrGrafObjs(*pModel);
+ pView->Paste(*pModel, aPos, nullptr, SdrInsertFlags::NONE);
+
+ const size_t nCnt = pView->GetMarkedObjectList().GetMarkCount();
+ if( nCnt )
+ {
+ const Point aNull( 0, 0 );
+ for( size_t i=0; i < nCnt; ++i )
+ {
+ SdrObject *pObj = pView->GetMarkedObjectList().GetMark(i)->GetMarkedSdrObj();
+ pObj->ImpSetAnchorPos( aNull );
+ }
+
+ pView->SetCurrentObj( SdrObjKind::Group );
+ if ( nCnt > 1 )
+ pView->GroupMarked();
+ SdrObject *pObj = pView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrUnoObj*>( pObj) != nullptr )
+ {
+ pObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetControlsId() );
+ bDesignMode = true;
+ }
+ else
+ pObj->SetLayer( GetDoc()->getIDocumentDrawModelAccess().GetHeavenId() );
+ const tools::Rectangle &rSnap = pObj->GetSnapRect();
+ const Size aDiff( rSnap.GetWidth()/2, rSnap.GetHeight()/2 );
+ pView->MoveMarkedObj( aDiff );
+ ImpEndCreate();
+ if( !bDesignMode )
+ pView->SetDesignMode( false );
+ }
+ }
+ EndUndo();
+ EndAllAction();
+}
+
+bool SwFEShell::Paste(const Graphic &rGrf, const OUString& rURL)
+{
+ CurrShell aCurr( this );
+ SdrObject* pObj = nullptr;
+ SdrView *pView = Imp()->GetDrawView();
+
+ bool bRet = 1 == pView->GetMarkedObjectList().GetMarkCount();
+ if (bRet)
+ {
+ pObj = pView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj();
+ bRet = pObj->IsClosedObj() && dynamic_cast<const SdrOle2Obj*>( pObj) == nullptr;
+ }
+
+ if( bRet && pObj )
+ {
+ // #i123922# added code to handle the two cases of SdrGrafObj and a fillable, non-
+ // OLE object in focus
+ SdrObject* pResult = pObj;
+
+ if(dynamic_cast< SdrGrafObj* >(pObj))
+ {
+ SdrGrafObj* pNewGrafObj(static_cast<SdrGrafObj*>(pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject())));
+
+ pNewGrafObj->SetGraphic(rGrf);
+
+ // #i123922# for handling MasterObject and virtual ones correctly, SW
+ // wants us to call ReplaceObject at the page, but that also
+ // triggers the same assertion (I tried it), so stay at the view method
+ pView->ReplaceObjectAtView(pObj, *pView->GetSdrPageView(), pNewGrafObj);
+
+ // set in all cases - the Clone() will have copied an existing link (!)
+ pNewGrafObj->SetGraphicLink(rURL);
+
+ pResult = pNewGrafObj;
+ }
+ else
+ {
+ pView->AddUndo(std::make_unique<SdrUndoAttrObj>(*pObj));
+
+ SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(pView->GetModel()->GetItemPool());
+
+ aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+ aSet.Put(XFillBitmapItem(OUString(), rGrf));
+ pObj->SetMergedItemSetAndBroadcast(aSet);
+ }
+
+ // we are done; mark the modified/new object
+ pView->MarkObj(pResult, pView->GetSdrPageView());
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/fedesc.cxx b/sw/source/core/frmedt/fedesc.cxx
new file mode 100644
index 000000000..f9f58949c
--- /dev/null
+++ b/sw/source/core/frmedt/fedesc.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fesh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <pam.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <tabfrm.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+
+size_t SwFEShell::GetPageDescCnt() const
+{
+ return GetDoc()->GetPageDescCnt();
+}
+
+void SwFEShell::ChgCurPageDesc( const SwPageDesc& rDesc )
+{
+#if OSL_DEBUG_LEVEL > 0
+ // SS does not change PageDesc, but only sets the attribute.
+ // The Pagedesc should be available in the document
+ bool bFound = false;
+ for ( size_t nTst = 0; nTst < GetPageDescCnt(); ++nTst )
+ if ( &rDesc == &GetPageDesc( nTst ) )
+ bFound = true;
+ OSL_ENSURE( bFound, "ChgCurPageDesc with invalid descriptor." );
+#endif
+
+ StartAllAction();
+
+ SwPageFrame *pPage = GetCurrFrame()->FindPageFrame();
+ const SwFrame *pFlow = nullptr;
+ ::std::optional<sal_uInt16> oPageNumOffset;
+
+ OSL_ENSURE( !GetCursor()->HasMark(), "ChgCurPageDesc only without selection!");
+
+ CurrShell aCurr( this );
+ while ( pPage )
+ {
+ pFlow = pPage->FindFirstBodyContent();
+ if ( pFlow )
+ {
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
+ if( rPgDesc.GetPageDesc() )
+ {
+ // we found the culprit
+ oPageNumOffset = rPgDesc.GetNumOffset();
+ break;
+ }
+ }
+ pPage = static_cast<SwPageFrame*>( pPage->GetPrev() );
+ }
+ if ( !pPage )
+ {
+ pPage = static_cast<SwPageFrame*>(GetLayout()->Lower());
+ pFlow = pPage->FindFirstBodyContent();
+ if ( !pFlow )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pFlow = pPage->FindFirstBodyContent();
+ OSL_ENSURE( pFlow, "Document without content?!?" );
+ }
+ }
+
+ // use pagenumber
+ SwFormatPageDesc aNew( &rDesc );
+ aNew.SetNumOffset( oPageNumOffset );
+
+ if ( pFlow->IsInTab() )
+ GetDoc()->SetAttr( aNew, *const_cast<SwFormat*>(static_cast<SwFormat const *>(pFlow->FindTabFrame()->GetFormat())) );
+ else
+ {
+ assert(pFlow->IsContentFrame());
+ SwPaM aPaM( pFlow->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pFlow)->GetTextNodeFirst() // first, for PAGEDESC
+ : *static_cast<const SwNoTextFrame*>(pFlow)->GetNode() );
+ GetDoc()->getIDocumentContentOperations().InsertPoolItem(
+ aPaM, aNew, SetAttrMode::DEFAULT, GetLayout());
+ }
+ EndAllActionAndCall();
+}
+
+void SwFEShell::ChgPageDesc( size_t i, const SwPageDesc &rChged )
+{
+ StartAllAction();
+ CurrShell aCurr( this );
+ //Fix i64842: because Undo has a very special way to handle header/footer content
+ // we have to copy the page descriptor before calling ChgPageDesc.
+ SwPageDesc aDesc( rChged );
+ {
+ ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
+ GetDoc()->CopyPageDesc(rChged, aDesc);
+ }
+ GetDoc()->ChgPageDesc( i, aDesc );
+ EndAllActionAndCall();
+}
+
+const SwPageDesc& SwFEShell::GetPageDesc( size_t i ) const
+{
+ return GetDoc()->GetPageDesc( i );
+}
+
+SwPageDesc* SwFEShell::FindPageDescByName( const OUString& rName,
+ bool bGetFromPool,
+ size_t* pPos )
+{
+ SwPageDesc* pDesc = GetDoc()->FindPageDesc(rName, pPos);
+ if( !pDesc && bGetFromPool )
+ {
+ sal_uInt16 nPoolId = SwStyleNameMapper::GetPoolIdFromUIName( rName, SwGetPoolIdFromName::PageDesc );
+ if( USHRT_MAX != nPoolId &&
+ nullptr != (pDesc = GetDoc()->getIDocumentStylePoolAccess().GetPageDescFromPool( nPoolId ))
+ && pPos )
+ // appended always
+ *pPos = GetDoc()->GetPageDescCnt() - 1 ;
+ }
+ return pDesc;
+}
+
+size_t SwFEShell::GetMousePageDesc( const Point &rPt ) const
+{
+ if( GetLayout() )
+ {
+ const SwPageFrame* pPage =
+ static_cast<const SwPageFrame*>( GetLayout()->Lower() );
+ if( pPage )
+ {
+ while( pPage->GetNext() && rPt.Y() > pPage->getFrameArea().Bottom() )
+ pPage = static_cast<const SwPageFrame*>( pPage->GetNext() );
+ SwDoc *pMyDoc = GetDoc();
+ size_t nPos;
+ if (pMyDoc->ContainsPageDesc( pPage->GetPageDesc(), &nPos ) )
+ return nPos;
+ }
+ }
+ return 0;
+}
+
+size_t SwFEShell::GetCurPageDesc( const bool bCalcFrame ) const
+{
+ const SwFrame *pFrame = GetCurrFrame( bCalcFrame );
+ if ( pFrame )
+ {
+ const SwPageFrame *pPage = pFrame->FindPageFrame();
+ if ( pPage )
+ {
+ size_t nPos;
+ if (GetDoc()->ContainsPageDesc( pPage->GetPageDesc(), &nPos ))
+ return nPos;
+ }
+ }
+ return 0;
+}
+
+// if inside all selection only one PageDesc, return this.
+// Otherwise return 0 pointer
+const SwPageDesc* SwFEShell::GetSelectedPageDescs() const
+{
+ const SwContentNode* pCNd;
+ const SwFrame* pMkFrame, *pPtFrame;
+ const SwPageDesc* pFnd, *pRetDesc = reinterpret_cast<SwPageDesc*>(sal_IntPtr(-1));
+ const Point aNulPt;
+ std::pair<Point, bool> const tmp(aNulPt, false);
+
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+
+ if( nullptr != (pCNd = rPaM.GetContentNode() ) &&
+ nullptr != (pPtFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp)))
+ pPtFrame = pPtFrame->FindPageFrame();
+ else
+ pPtFrame = nullptr;
+
+ if( rPaM.HasMark() &&
+ nullptr != (pCNd = rPaM.GetContentNode( false ) ) &&
+ nullptr != (pMkFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp)))
+ pMkFrame = pMkFrame->FindPageFrame();
+ else
+ pMkFrame = pPtFrame;
+
+ if( !pMkFrame || !pPtFrame )
+ pFnd = nullptr;
+ else if( pMkFrame == pPtFrame )
+ pFnd = static_cast<const SwPageFrame*>(pMkFrame)->GetPageDesc();
+ else
+ {
+ // swap pointer if PtFrame before MkFrame
+ if( static_cast<const SwPageFrame*>(pMkFrame)->GetPhyPageNum() >
+ static_cast<const SwPageFrame*>(pPtFrame)->GetPhyPageNum() )
+ {
+ const SwFrame* pTmp = pMkFrame; pMkFrame = pPtFrame; pPtFrame = pTmp;
+ }
+
+ // now check from MkFrame to PtFrame for equal PageDescs
+ pFnd = static_cast<const SwPageFrame*>(pMkFrame)->GetPageDesc();
+ while( pFnd && pMkFrame != pPtFrame )
+ {
+ pMkFrame = pMkFrame->GetNext();
+ if( !pMkFrame || pFnd != static_cast<const SwPageFrame*>(pMkFrame)->GetPageDesc() )
+ pFnd = nullptr;
+ }
+ }
+
+ if( reinterpret_cast<SwPageDesc*>(sal_IntPtr(-1)) == pRetDesc )
+ pRetDesc = pFnd;
+ else if( pFnd != pRetDesc )
+ {
+ pRetDesc = nullptr;
+ break;
+ }
+
+ }
+
+ return pRetDesc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/fefly1.cxx b/sw/source/core/frmedt/fefly1.cxx
new file mode 100644
index 000000000..323816ef2
--- /dev/null
+++ b/sw/source/core/frmedt/fefly1.cxx
@@ -0,0 +1,2134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <o3tl/any.hxx>
+#include <svl/itemiter.hxx>
+#include <vcl/imapobj.hxx>
+#include <editeng/protitem.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdouno.hxx>
+#include <tools/globname.hxx>
+#include <sot/exchange.hxx>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <comphelper/types.hxx>
+#include <osl/diagnose.h>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtornt.hxx>
+#include <fmturl.hxx>
+#include <fmtfsize.hxx>
+#include <fesh.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <viewimp.hxx>
+#include <viscrs.hxx>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <dview.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <frmfmt.hxx>
+#include <flyfrm.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <ndgrf.hxx>
+#include <flyfrms.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <swundo.hxx>
+#include <txatbase.hxx>
+#include <frame.hxx>
+#include <notxtfrm.hxx>
+#include <HandleAnchorNodeChg.hxx>
+#include <frmatr.hxx>
+#include <fmtsrnd.hxx>
+#include <ndole.hxx>
+#include <fefly.hxx>
+#include <fmtcnct.hxx>
+#include <frameformats.hxx>
+#include <textboxhelper.hxx>
+
+
+using namespace ::com::sun::star;
+
+// Based on the request, changes to the specific layouts will be made, to
+// fit to the format
+static bool lcl_SetNewFlyPos( const SwNode& rNode, SwFormatAnchor& rAnchor,
+ const Point& rPt )
+{
+ bool bRet = false;
+ const SwStartNode* pStNode = rNode.FindFlyStartNode();
+ if( pStNode )
+ {
+ SwPosition aPos( *pStNode );
+ rAnchor.SetAnchor( &aPos );
+ bRet = true;
+ }
+ else
+ {
+ const SwContentNode *pCntNd = rNode.GetContentNode();
+ std::pair<Point, bool> const tmp(rPt, false);
+ const SwContentFrame* pCFrame = pCntNd ? pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp) : nullptr;
+ const SwPageFrame *pPg = pCFrame ? pCFrame->FindPageFrame() : nullptr;
+
+ rAnchor.SetPageNum( pPg ? pPg->GetPhyPageNum() : 1 );
+ rAnchor.SetType( RndStdIds::FLY_AT_PAGE );
+ }
+ return bRet;
+}
+
+static bool lcl_FindAnchorPos(
+ SwDoc& rDoc,
+ const Point& rPt,
+ const SwFrame& rFrame,
+ SfxItemSet& rSet )
+{
+ bool bRet = true;
+ SwFormatAnchor aNewAnch( rSet.Get( RES_ANCHOR ) );
+ RndStdIds nNew = aNewAnch.GetAnchorId();
+ const SwFrame *pNewAnch;
+
+ //determine new anchor
+ Point aTmpPnt( rPt );
+ switch( nNew )
+ {
+ case RndStdIds::FLY_AS_CHAR: // also include this?
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
+ {
+ // starting from the upper-left corner of the Fly,
+ // search nearest ContentFrame
+ const SwFrame* pFrame = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame&>(rFrame).GetAnchorFrame()
+ : &rFrame;
+ pNewAnch = ::FindAnchor( pFrame, aTmpPnt );
+ if( pNewAnch->IsProtected() )
+ {
+ bRet = false;
+ break;
+ }
+ SwPosition aPos( pNewAnch->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pNewAnch)->GetTextNodeForParaProps()
+ : *static_cast<const SwNoTextFrame*>(pNewAnch)->GetNode() );
+ if ((RndStdIds::FLY_AT_CHAR == nNew) || (RndStdIds::FLY_AS_CHAR == nNew))
+ {
+ // textnode should be found, as only in those
+ // a content bound frame can be anchored
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ aTmpPnt.setX(aTmpPnt.getX() - 1); // do not land in the fly!
+ if( !pNewAnch->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState ) )
+ {
+ assert(pNewAnch->IsTextFrame()); // because AT_CHAR/AS_CHAR
+ SwTextFrame const*const pTextFrame(
+ static_cast<SwTextFrame const*>(pNewAnch));
+ if( pNewAnch->getFrameArea().Bottom() < aTmpPnt.Y() )
+ {
+ aPos = pTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ aPos = pTextFrame->MapViewToModelPos(
+ TextFrameIndex(pTextFrame->GetText().getLength()));
+ }
+ }
+ else
+ {
+ if ( SwCursorShell::PosInsideInputField( aPos ) )
+ {
+ aPos.nContent = SwCursorShell::StartOfInputFieldAtPos( aPos );
+ }
+ }
+ }
+ aNewAnch.SetAnchor( &aPos );
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY: // LAYER_IMPL
+ {
+ // starting from the upper-left corner of the Fly
+ // search nearest SwFlyFrame
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ SwPosition aPos( rDoc.GetNodes() );
+ aTmpPnt.setX(aTmpPnt.getX() - 1); // do not land in the fly!
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState );
+ pNewAnch = ::FindAnchor(
+ aPos.nNode.GetNode().GetContentNode()->getLayoutFrame(rFrame.getRootFrame(), nullptr, nullptr),
+ aTmpPnt )->FindFlyFrame();
+
+ if( pNewAnch && &rFrame != pNewAnch && !pNewAnch->IsProtected() )
+ {
+ aPos.nNode = *static_cast<const SwFlyFrame*>(pNewAnch)->GetFormat()->GetContent().
+ GetContentIdx();
+ aNewAnch.SetAnchor( &aPos );
+ break;
+ }
+ }
+
+ nNew = RndStdIds::FLY_AT_PAGE;
+ aNewAnch.SetType( nNew );
+ [[fallthrough]];
+
+ case RndStdIds::FLY_AT_PAGE:
+ pNewAnch = rFrame.FindPageFrame();
+ aNewAnch.SetPageNum( pNewAnch->GetPhyPageNum() );
+ break;
+
+ default:
+ OSL_ENSURE( false, "Wrong Id for new anchor." );
+ }
+
+ rSet.Put( aNewAnch );
+ return bRet;
+}
+
+//! also used in unoframe.cxx
+
+bool sw_ChkAndSetNewAnchor(
+ const SwFlyFrame& rFly,
+ SfxItemSet& rSet )
+{
+ const SwFrameFormat& rFormat = *rFly.GetFormat();
+ const SwFormatAnchor &rOldAnch = rFormat.GetAnchor();
+ const RndStdIds nOld = rOldAnch.GetAnchorId();
+
+ RndStdIds nNew = rSet.Get( RES_ANCHOR ).GetAnchorId();
+
+ if( nOld == nNew )
+ return false;
+
+ SwDoc* pDoc = const_cast<SwDoc*>(rFormat.GetDoc());
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( !(nNew == RndStdIds::FLY_AT_PAGE &&
+ (RndStdIds::FLY_AT_PARA==nOld || RndStdIds::FLY_AT_CHAR==nOld || RndStdIds::FLY_AS_CHAR==nOld ) &&
+ pDoc->IsInHeaderFooter( rOldAnch.GetContentAnchor()->nNode )),
+ "forbidden anchor change in Head/Foot." );
+#endif
+
+ return ::lcl_FindAnchorPos( *pDoc, rFly.getFrameArea().Pos(), rFly, rSet );
+}
+
+void SwFEShell::SelectFlyFrame( SwFlyFrame& rFrame )
+{
+ CurrShell aCurr( this );
+
+ // The frame is new, thus select it.
+ // !! Always select the frame, if it's not selected.
+ // - it could be a new "old" one because the anchor was changed
+ // - "old" frames have had to be selected previously otherwise they could
+ // not have been changed
+ // The frames should not be selected by the document position, because
+ // it should have been selected!
+ SwViewShellImp *pImpl = Imp();
+ if( !GetWin() )
+ return;
+
+ OSL_ENSURE( rFrame.IsFlyFrame(), "SelectFlyFrame wants a Fly" );
+
+ // nothing to be done if the Fly already was selected
+ if (GetSelectedFlyFrame() == &rFrame)
+ return;
+
+ // assure the anchor is drawn
+ if( rFrame.IsFlyInContentFrame() && rFrame.GetAnchorFrame() )
+ rFrame.GetAnchorFrame()->SetCompletePaint();
+
+ if( pImpl->GetDrawView()->AreObjectsMarked() )
+ pImpl->GetDrawView()->UnmarkAll();
+
+ pImpl->GetDrawView()->MarkObj( rFrame.GetVirtDrawObj(),
+ pImpl->GetPageView() );
+
+ rFrame.SelectionHasChanged(this);
+
+ KillPams();
+ ClearMark();
+ SelFlyGrabCursor();
+}
+
+// Get selected fly
+SwFlyFrame* SwFEShell::GetSelectedFlyFrame() const
+{
+ if ( Imp()->HasDrawView() )
+ {
+ // A Fly is only accessible if it is selected
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if( rMrkList.GetMarkCount() != 1 )
+ return nullptr;
+
+ SdrObject *pO = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ SwVirtFlyDrawObj *pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pO);
+
+ return pFlyObj ? pFlyObj->GetFlyFrame() : nullptr;
+ }
+ return nullptr;
+}
+
+// Get current fly in which the cursor is positioned
+SwFlyFrame* SwFEShell::GetCurrFlyFrame(const bool bCalcFrame) const
+{
+ SwContentFrame *pContent = GetCurrFrame(bCalcFrame);
+ return pContent ? pContent->FindFlyFrame() : nullptr;
+}
+
+// Get selected fly, but if none Get current fly in which the cursor is positioned
+SwFlyFrame* SwFEShell::GetSelectedOrCurrFlyFrame() const
+{
+ SwFlyFrame *pFly = GetSelectedFlyFrame();
+ if (pFly)
+ return pFly;
+ return GetCurrFlyFrame();
+}
+
+// Returns non-null pointer, if the current Fly could be anchored to another one (so it is inside)
+const SwFrameFormat* SwFEShell::IsFlyInFly()
+{
+ CurrShell aCurr( this );
+
+ if ( !Imp()->HasDrawView() )
+ return nullptr;
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( !rMrkList.GetMarkCount() )
+ {
+ SwFlyFrame *pFly = GetCurrFlyFrame(false);
+ if (!pFly)
+ return nullptr;
+ return pFly->GetFormat();
+ }
+ else if ( rMrkList.GetMarkCount() != 1 ||
+ !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) )
+ return nullptr;
+
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+
+ SwFrameFormat *pFormat = FindFrameFormat( pObj );
+ if( pFormat && RndStdIds::FLY_AT_FLY == pFormat->GetAnchor().GetAnchorId() )
+ {
+ const SwFrame* pFly;
+ if (SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj *>(pObj))
+ {
+ pFly = pFlyObj->GetFlyFrame()->GetAnchorFrame();
+ }
+ else
+ {
+ pFly = static_cast<SwDrawContact*>(GetUserCall(pObj))->GetAnchorFrame(pObj);
+ }
+
+ OSL_ENSURE( pFly, "IsFlyInFly: Where's my anchor?" );
+ OSL_ENSURE( pFly->IsFlyFrame(), "IsFlyInFly: Funny anchor!" );
+ return static_cast<const SwFlyFrame*>(pFly)->GetFormat();
+ }
+
+ Point aTmpPos = pObj->GetCurrentBoundRect().TopLeft();
+
+ SwFrame *pTextFrame;
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ SwNodeIndex aSwNodeIndex( GetDoc()->GetNodes() );
+ SwPosition aPos( aSwNodeIndex );
+ Point aPoint( aTmpPos );
+ aPoint.setX(aPoint.getX() - 1); //do not land in the fly!!
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+ // determine text frame by left-top-corner of object
+ SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(aTmpPos, false);
+ pTextFrame = pNd ? pNd->getLayoutFrame(GetLayout(), nullptr, &tmp) : nullptr;
+ }
+ const SwFrame *pTmp = pTextFrame ? ::FindAnchor(pTextFrame, aTmpPos) : nullptr;
+ const SwFlyFrame *pFly = pTmp ? pTmp->FindFlyFrame() : nullptr;
+ if( pFly )
+ return pFly->GetFormat();
+ return nullptr;
+}
+
+void SwFEShell::SetFlyPos( const Point& rAbsPos )
+{
+ CurrShell aCurr( this );
+
+ // Determine reference point in document coordinates
+ SwFlyFrame *pFly = GetCurrFlyFrame(false);
+ if (!pFly)
+ return;
+
+ //SwSaveHdl aSaveX( Imp() );
+
+ // Set an anchor starting from the absolute position for paragraph bound Flys
+ // Anchor and new RelPos will be calculated and set by the Fly
+ if ( pFly->IsFlyAtContentFrame() )
+ {
+ if(pFly->IsFlyFreeFrame() && static_cast< SwFlyFreeFrame* >(pFly)->isTransformableSwFrame())
+ {
+ // RotateFlyFrame3: When we have a change and are in transformed state (e.g. rotation used),
+ // we need to correct the absolute position (rAbsPos) which was created in
+ // transformed coordinates to untransformed state
+ TransformableSwFrame* pTransformableSwFrame(static_cast<SwFlyFreeFrame*>(pFly)->getTransformableSwFrame());
+ const SwRect aUntransformedFrameArea(pTransformableSwFrame->getUntransformedFrameArea());
+ const Point aNewAbsPos(
+ rAbsPos.X() + aUntransformedFrameArea.Left() - pFly->getFrameArea().Left(),
+ rAbsPos.Y() + aUntransformedFrameArea.Top() - pFly->getFrameArea().Top());
+ static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos(aNewAbsPos);
+ }
+ else
+ {
+ static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos( rAbsPos );
+ }
+ }
+ else
+ {
+ const SwFrame *pAnch = pFly->GetAnchorFrame();
+ Point aOrient( pAnch->getFrameArea().Pos() );
+
+ if ( pFly->IsFlyInContentFrame() )
+ aOrient.setX(rAbsPos.getX());
+
+ // calculate RelPos.
+ aOrient.setX(rAbsPos.getX() - aOrient.getX());
+ aOrient.setY(rAbsPos.getY() - aOrient.getY());
+ pFly->ChgRelPos( aOrient );
+ }
+ CallChgLnk(); // call the AttrChangeNotify on the UI-side.
+}
+
+Point SwFEShell::FindAnchorPos( const Point& rAbsPos, bool bMoveIt )
+{
+ Point aRet;
+
+ CurrShell aCurr( this );
+
+ if ( !Imp()->HasDrawView() )
+ return aRet;
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( rMrkList.GetMarkCount() != 1 ||
+ !GetUserCall(rMrkList.GetMark( 0 )->GetMarkedSdrObj()) )
+ return aRet;
+
+ SdrObject* pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj );
+ SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ const RndStdIds nAnchorId = rFormat.GetAnchor().GetAnchorId();
+
+ if ( RndStdIds::FLY_AS_CHAR == nAnchorId )
+ return aRet;
+
+ bool bFlyFrame = dynamic_cast<SwVirtFlyDrawObj *>(pObj) != nullptr;
+
+ bool bTextBox = false;
+ if (rFormat.Which() == RES_DRAWFRMFMT)
+ {
+ bTextBox = SwTextBoxHelper::isTextBox(&rFormat, RES_DRAWFRMFMT, pObj);
+ }
+
+ SwFlyFrame* pFly = nullptr;
+ const SwFrame* pFooterOrHeader = nullptr;
+
+ if( bFlyFrame )
+ {
+ // Calculate reference point in document coordinates
+ SwContentFrame *pContent = GetCurrFrame( false );
+ if( !pContent )
+ return aRet;
+ pFly = pContent->FindFlyFrame();
+ if ( !pFly )
+ return aRet;
+ const SwFrame* pOldAnch = pFly->GetAnchorFrame();
+ if( !pOldAnch )
+ return aRet;
+ if ( RndStdIds::FLY_AT_PAGE != nAnchorId )
+ {
+ pFooterOrHeader = pContent->FindFooterOrHeader();
+ }
+ }
+ else if (bTextBox)
+ {
+ auto pFlyFormat
+ = dynamic_cast<const SwFlyFrameFormat*>(SwTextBoxHelper::getOtherTextBoxFormat(
+ &rFormat, RES_DRAWFRMFMT, pObj ? pObj : rFormat.FindRealSdrObject()));
+ if (pFlyFormat)
+ {
+ pFly = pFlyFormat->GetFrame();
+ }
+ }
+
+ // set <pFooterOrHeader> also for drawing
+ // objects, but not for control objects.
+ // Necessary for moving 'anchor symbol' at the user interface inside header/footer.
+ else if ( !::CheckControlLayer( pObj ) )
+ {
+ SwContentFrame *pContent = GetCurrFrame( false );
+ if( !pContent )
+ return aRet;
+ pFooterOrHeader = pContent->FindFooterOrHeader();
+ }
+
+ // Search nearest SwFlyFrame starting from the upper-left corner
+ // of the fly
+ SwContentFrame *pTextFrame = nullptr;
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ SwPosition aPos( GetDoc()->GetNodes().GetEndOfExtras() );
+ Point aTmpPnt( rAbsPos );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aTmpPnt, &aState );
+ if (aPos.nNode != GetDoc()->GetNodes().GetEndOfExtras().GetIndex()
+ && (nAnchorId != RndStdIds::FLY_AT_CHAR || !PosInsideInputField(aPos)))
+ {
+ SwContentNode* pCNode = aPos.nNode.GetNode().GetContentNode();
+ assert(pCNode);
+ pTextFrame = pCNode->getLayoutFrame(GetLayout(), &aPos, nullptr);
+ }
+ }
+ const SwFrame *pNewAnch = nullptr;
+ if( pTextFrame != nullptr )
+ {
+ if ( RndStdIds::FLY_AT_PAGE == nAnchorId )
+ {
+ pNewAnch = pTextFrame->FindPageFrame();
+ }
+ else
+ {
+ pNewAnch = ::FindAnchor( pTextFrame, rAbsPos );
+
+ if( RndStdIds::FLY_AT_FLY == nAnchorId ) // LAYER_IMPL
+ {
+ pNewAnch = pNewAnch->FindFlyFrame();
+ }
+ }
+ }
+
+ if( pNewAnch && !pNewAnch->IsProtected() )
+ {
+ const SwFlyFrame* pCheck = (bFlyFrame || bTextBox) ? pNewAnch->FindFlyFrame() : nullptr;
+ // If we land inside the frame, make sure
+ // that the frame does not land inside its own content
+ while( pCheck )
+ {
+ if( pCheck == pFly )
+ break;
+ const SwFrame *pTmp = pCheck->GetAnchorFrame();
+ pCheck = pTmp ? pTmp->FindFlyFrame() : nullptr;
+ }
+
+ // Do not switch from header/footer to another area,
+ // do not switch to a header/footer
+ if( !pCheck &&
+ pFooterOrHeader == pNewAnch->FindFooterOrHeader() )
+ {
+ aRet = pNewAnch->GetFrameAnchorPos( ::HasWrap( pObj ) );
+
+ if ( bMoveIt || (nAnchorId == RndStdIds::FLY_AT_CHAR) )
+ {
+ SwFormatAnchor aAnch( rFormat.GetAnchor() );
+ switch ( nAnchorId )
+ {
+ case RndStdIds::FLY_AT_PARA:
+ {
+ SwPosition pos = *aAnch.GetContentAnchor();
+ pos.nNode = pTextFrame->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pTextFrame)->GetTextNodeForParaProps()
+ : *static_cast<const SwNoTextFrame*>(pTextFrame)->GetNode();
+ pos.nContent.Assign(nullptr,0);
+ aAnch.SetAnchor( &pos );
+ break;
+ }
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ aAnch.SetPageNum( static_cast<const SwPageFrame*>(pNewAnch)->
+ GetPhyPageNum() );
+ break;
+ }
+
+ case RndStdIds::FLY_AT_FLY:
+ {
+ SwPosition aPos( *static_cast<const SwFlyFrame*>(pNewAnch)->GetFormat()->
+ GetContent().GetContentIdx() );
+ aAnch.SetAnchor( &aPos );
+ break;
+ }
+
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ SwPosition pos = *aAnch.GetContentAnchor();
+ Point aTmpPnt( rAbsPos );
+ if( pTextFrame->GetModelPositionForViewPoint( &pos, aTmpPnt ) )
+ {
+ SwRect aTmpRect;
+ pTextFrame->GetCharRect( aTmpRect, pos );
+ aRet = aTmpRect.Pos();
+ }
+ else
+ {
+ pos = static_cast<SwTextFrame const*>(pTextFrame)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ aAnch.SetAnchor( &pos );
+ break;
+ }
+ default:
+ break;
+
+ }
+
+ if( bMoveIt )
+ {
+ StartAllAction();
+ // --> handle change of anchor node:
+ // if count of the anchor frame also change, the fly frames have to be
+ // re-created. Thus, delete all fly frames except the <this> before the
+ // anchor attribute is change and re-create them afterwards.
+ {
+ std::unique_ptr<SwHandleAnchorNodeChg> pHandleAnchorNodeChg;
+ SwFlyFrameFormat* pFlyFrameFormat( dynamic_cast<SwFlyFrameFormat*>(&rFormat) );
+ if ( pFlyFrameFormat )
+ {
+ pHandleAnchorNodeChg.reset(
+ new SwHandleAnchorNodeChg( *pFlyFrameFormat, aAnch ));
+ }
+ rFormat.GetDoc()->SetAttr( aAnch, rFormat );
+ if (SwTextBoxHelper::getOtherTextBoxFormat(&rFormat, RES_DRAWFRMFMT,
+ pObj ? pObj : rFormat.FindRealSdrObject()))
+ {
+ if (pObj->getChildrenOfSdrObject())
+ {
+ for (size_t i = 0;
+ i < pObj->getChildrenOfSdrObject()->GetObjCount(); ++i)
+ SwTextBoxHelper::changeAnchor(
+ &rFormat, pObj->getChildrenOfSdrObject()->GetObj(i));
+ }
+ else
+ SwTextBoxHelper::syncFlyFrameAttr(
+ rFormat, rFormat.GetAttrSet(),
+ pObj ? pObj : rFormat.FindRealSdrObject());
+ }
+ }
+ // #i28701# - no call of method
+ // <CheckCharRectAndTopOfLine()> for to-character anchored
+ // Writer fly frame needed. This method call can cause a
+ // format of the anchor frame, which is no longer intended.
+ // Instead clear the anchor character rectangle and
+ // the top of line values for all to-character anchored objects.
+ pAnchoredObj->ClearCharRectAndTopOfLine();
+ EndAllAction();
+ }
+ }
+
+ SwRect aTmpRect( aRet, rAbsPos );
+ if( aTmpRect.HasArea() )
+ MakeVisible( aTmpRect );
+#if OSL_DEBUG_LEVEL > 0
+ //TODO: That doesn't seem to be intended
+ if( COL_TRANSPARENT != GetOut()->GetLineColor() )
+ {
+ OSL_FAIL( "Hey, Joe: Where's my Null Pen?" );
+ GetOut()->SetLineColor( COL_TRANSPARENT );
+ }
+#endif
+ }
+ }
+
+ return aRet;
+}
+
+const SwFrameFormat *SwFEShell::NewFlyFrame( const SfxItemSet& rSet, bool bAnchValid,
+ SwFrameFormat *pParent )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwPaM* pCursor = GetCursor();
+ const Point aPt( GetCursorDocPos() );
+
+ SwSelBoxes aBoxes;
+ bool bMoveContent = true;
+ if( IsTableMode() )
+ {
+ GetTableSel( *this, aBoxes );
+ if( !aBoxes.empty() )
+ {
+ // Cursor should be removed from the removal area.
+ // Always put it after/on the table; via the
+ // document position they will be set to the old
+ // position
+ ParkCursor( SwNodeIndex( *aBoxes[0]->GetSttNd() ));
+
+ // #i127787# pCurrentCursor will be deleted in ParkCursor,
+ // we better get the current pCurrentCursor instead of working with the
+ // deleted one:
+ pCursor = GetCursor();
+ }
+ else
+ bMoveContent = false;
+ }
+ else if( !pCursor->HasMark() && !pCursor->IsMultiSelection() )
+ bMoveContent = false;
+
+ const SwPosition& rPos = *pCursor->Start();
+
+ SwFormatAnchor& rAnch = const_cast<SwFormatAnchor&>(rSet.Get( RES_ANCHOR ));
+ RndStdIds eRndId = rAnch.GetAnchorId();
+ switch( eRndId )
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ if( !rAnch.GetPageNum() ) //HotFix: Bug in UpdateByExample
+ rAnch.SetPageNum( 1 );
+ break;
+
+ case RndStdIds::FLY_AT_FLY:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AS_CHAR:
+ if( !bAnchValid )
+ {
+ if( RndStdIds::FLY_AT_FLY != eRndId )
+ {
+ rAnch.SetAnchor( &rPos );
+ }
+ else if( lcl_SetNewFlyPos( rPos.nNode.GetNode(), rAnch, aPt ) )
+ {
+ eRndId = RndStdIds::FLY_AT_PAGE;
+ }
+ }
+ break;
+
+ default:
+ OSL_ENSURE( false, "What is the purpose of this Fly?" );
+ break;
+ }
+
+ SwFlyFrameFormat *pRet;
+ if( bMoveContent )
+ {
+ GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSLAYFMT, nullptr );
+ std::unique_ptr<SwFormatAnchor> pOldAnchor;
+ bool bHOriChgd = false, bVOriChgd = false;
+ std::shared_ptr<SwFormatVertOrient> aOldV;
+ std::shared_ptr<SwFormatHoriOrient> aOldH;
+
+ if ( RndStdIds::FLY_AT_PAGE != eRndId )
+ {
+ // First as with page link. Paragraph/character link on if
+ // everything was shifted. Then the position is valid!
+ // JP 13.05.98: if necessary also convert the horizontal/vertical
+ // orientation, to prevent correction during re-anchoring
+ pOldAnchor.reset(new SwFormatAnchor( rAnch ));
+ const_cast<SfxItemSet&>(rSet).Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, 1 ) );
+
+ const SwFormatHoriOrient* pHoriOrientItem;
+ if( (pHoriOrientItem = rSet.GetItemIfSet( RES_HORI_ORIENT, false ))
+ && text::HoriOrientation::NONE == pHoriOrientItem->GetHoriOrient() )
+ {
+ bHOriChgd = true;
+ aOldH.reset(pHoriOrientItem->Clone());
+ const_cast<SfxItemSet&>(rSet).Put( SwFormatHoriOrient( 0, text::HoriOrientation::LEFT ) );
+ }
+ const SwFormatVertOrient* pVertOrientItem;
+ if( (pVertOrientItem = rSet.GetItemIfSet( RES_VERT_ORIENT, false ))
+ && text::VertOrientation::NONE == pVertOrientItem->GetVertOrient() )
+ {
+ bVOriChgd = true;
+ aOldV.reset(pVertOrientItem->Clone());
+ const_cast<SfxItemSet&>(rSet).Put( SwFormatVertOrient( 0, text::VertOrientation::TOP ) );
+ }
+ }
+
+ pRet = GetDoc()->MakeFlyAndMove( *pCursor, rSet, &aBoxes, pParent );
+
+ KillPams();
+
+ if( pOldAnchor )
+ {
+ if( pRet )
+ {
+ // calculate new position
+ // JP 24.03.97: also go via page links
+ // anchor should not lie in the shifted area
+ pRet->DelFrames();
+
+ const SwFrame* pAnch = ::FindAnchor( GetLayout(), aPt );
+ SwPosition aPos( pAnch->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pAnch)->GetTextNodeForParaProps()
+ : *static_cast<const SwNoTextFrame*>(pAnch)->GetNode() );
+
+ if ( RndStdIds::FLY_AS_CHAR == eRndId )
+ {
+ assert(pAnch->IsTextFrame());
+ aPos = static_cast<SwTextFrame const*>(pAnch)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ pOldAnchor->SetAnchor( &aPos );
+
+ // shifting of table selection is not Undo-capable. therefore
+ // changing the anchors should not be recorded
+ bool const bDoesUndo =
+ GetDoc()->GetIDocumentUndoRedo().DoesUndo();
+ SwUndoId nLastUndoId(SwUndoId::EMPTY);
+ if (bDoesUndo &&
+ GetDoc()->GetIDocumentUndoRedo().GetLastUndoInfo(nullptr,
+ & nLastUndoId))
+ {
+ if (SwUndoId::INSLAYFMT == nLastUndoId)
+ {
+ GetDoc()->GetIDocumentUndoRedo().DoUndo(false);
+ }
+ }
+
+ const_cast<SfxItemSet&>(rSet).Put( std::move(pOldAnchor) );
+
+ if( bHOriChgd )
+ const_cast<SfxItemSet&>(rSet).Put( *aOldH );
+ if( bVOriChgd )
+ const_cast<SfxItemSet&>(rSet).Put( *aOldV );
+
+ GetDoc()->SetFlyFrameAttr( *pRet, const_cast<SfxItemSet&>(rSet) );
+ GetDoc()->GetIDocumentUndoRedo().DoUndo(bDoesUndo);
+ }
+ }
+ GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSLAYFMT, nullptr );
+ }
+ else
+ /* If called from a shell try to propagate an
+ existing adjust item from rPos to the content node of the
+ new frame. */
+ pRet = GetDoc()->MakeFlySection( eRndId, &rPos, &rSet, pParent, true );
+
+ if( pRet )
+ {
+ SwFlyFrame* pFrame = pRet->GetFrame( &aPt );
+ if( pFrame )
+ SelectFlyFrame( *pFrame );
+ else
+ {
+ GetLayout()->SetAssertFlyPages();
+ pRet = nullptr;
+ }
+ }
+ EndAllActionAndCall();
+
+ return pRet;
+}
+
+void SwFEShell::Insert( const OUString& rGrfName, const OUString& rFltName,
+ const Graphic* pGraphic,
+ const SfxItemSet* pFlyAttrSet )
+{
+ SwFlyFrameFormat* pFormat = nullptr;
+ CurrShell aCurr( this );
+ StartAllAction();
+ SwShellCursor *pStartCursor = dynamic_cast<SwShellCursor*>(GetCursor());
+ SwShellCursor *pCursor = pStartCursor;
+ do
+ {
+ if (!pCursor)
+ break;
+
+ // Has the anchor not been set or been set incompletely?
+ if( pFlyAttrSet )
+ {
+ if( const SwFormatAnchor* pItem = pFlyAttrSet->GetItemIfSet( RES_ANCHOR, false ) )
+ {
+ SwFormatAnchor* pAnchor = const_cast<SwFormatAnchor*>(pItem);
+ switch( pAnchor->GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR: // LAYER_IMPL
+ case RndStdIds::FLY_AS_CHAR:
+ if( !pAnchor->GetContentAnchor() )
+ {
+ pAnchor->SetAnchor( pCursor->GetPoint() );
+ }
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ if( !pAnchor->GetContentAnchor() )
+ {
+ lcl_SetNewFlyPos( pCursor->GetNode(),
+ *pAnchor, GetCursorDocPos() );
+ }
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ if( !pAnchor->GetPageNum() )
+ {
+ pAnchor->SetPageNum( pCursor->GetPageNum(
+ true, &pCursor->GetPtPos() ) );
+ }
+ break;
+ default :
+ break;
+ }
+ }
+ }
+ pFormat = GetDoc()->getIDocumentContentOperations().InsertGraphic(
+ *pCursor, rGrfName,
+ rFltName, pGraphic,
+ pFlyAttrSet,
+ nullptr, nullptr );
+ OSL_ENSURE(pFormat, "IDocumentContentOperations::InsertGraphic failed.");
+
+ pCursor = pCursor->GetNext();
+ } while( pCursor != pStartCursor );
+
+ EndAllAction();
+
+ if( !pFormat )
+ return;
+
+ const Point aPt( GetCursorDocPos() );
+ SwFlyFrame* pFrame = pFormat->GetFrame( &aPt );
+
+ if( pFrame )
+ {
+ // add a redline to the anchor point at tracked insertion of picture
+ if ( IsRedlineOn() )
+ {
+ SwPosition aPos(*pFormat->GetAnchor().GetContentAnchor());
+ SwPaM aPaM(aPos.nNode.GetNode(), aPos.nContent.GetIndex(),
+ aPos.nNode.GetNode(), aPos.nContent.GetIndex() + 1);
+ GetDoc()->getIDocumentRedlineAccess().AppendRedline(
+ new SwRangeRedline( RedlineType::Insert, aPaM ), true);
+ }
+
+ // fdo#36681: Invalidate the content and layout to refresh
+ // the picture anchoring properly
+ SwPageFrame* pPageFrame = pFrame->FindPageFrameOfAnchor();
+ pPageFrame->InvalidateFlyLayout();
+ pPageFrame->InvalidateContent();
+
+ SelectFlyFrame( *pFrame );
+ }
+ else
+ GetLayout()->SetAssertFlyPages();
+}
+
+SwFlyFrameFormat* SwFEShell::InsertObject( const svt::EmbeddedObjectRef& xObj,
+ SfxItemSet* pFlyAttrSet )
+{
+ SwFlyFrameFormat* pFormat = nullptr;
+ CurrShell aCurr( this );
+ StartAllAction();
+ {
+ for(const SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ pFormat = GetDoc()->getIDocumentContentOperations().InsertEmbObject(
+ rPaM, xObj, pFlyAttrSet );
+ OSL_ENSURE(pFormat, "IDocumentContentOperations::InsertEmbObject failed.");
+ }
+ }
+ EndAllAction();
+
+ if( pFormat )
+ {
+ const Point aPt( GetCursorDocPos() );
+ SwFlyFrame* pFrame = pFormat->GetFrame( &aPt );
+
+ if( pFrame )
+ SelectFlyFrame( *pFrame );
+ else
+ GetLayout()->SetAssertFlyPages();
+ }
+
+ return pFormat;
+}
+
+void SwFEShell::InsertDrawObj( SdrObject& rDrawObj,
+ const Point& rInsertPosition )
+{
+ CurrShell aCurr( this );
+
+ SfxItemSet rFlyAttrSet( GetDoc()->GetAttrPool(), aFrameFormatSetRange );
+ rFlyAttrSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PARA ));
+ // #i89920#
+ rFlyAttrSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) );
+ rDrawObj.SetLayer( getIDocumentDrawModelAccess().GetHeavenId() );
+
+ // find anchor position
+ SwPaM aPam( mxDoc->GetNodes() );
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ Point aTmpPt( rInsertPosition );
+ GetLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aTmpPt, &aState );
+ const SwFrame* pFrame = aPam.GetContentNode()->getLayoutFrame(GetLayout(), nullptr, nullptr);
+ const Point aRelPos( rInsertPosition.X() - pFrame->getFrameArea().Left(),
+ rInsertPosition.Y() - pFrame->getFrameArea().Top() );
+ rDrawObj.SetRelativePos( aRelPos );
+ ::lcl_FindAnchorPos( *GetDoc(), rInsertPosition, *pFrame, rFlyAttrSet );
+ }
+ // insert drawing object into the document creating a new <SwDrawFrameFormat> instance
+ SwDrawFrameFormat* pFormat = GetDoc()->getIDocumentContentOperations().InsertDrawObj( aPam, rDrawObj, rFlyAttrSet );
+
+ // move object to visible layer
+ SwContact* pContact = static_cast<SwContact*>(rDrawObj.GetUserCall());
+ if ( pContact )
+ {
+ pContact->MoveObjToVisibleLayer( &rDrawObj );
+ }
+
+ if (pFormat)
+ {
+ pFormat->SetName(rDrawObj.GetName());
+ // select drawing object
+ Imp()->GetDrawView()->MarkObj( &rDrawObj, Imp()->GetPageView() );
+ }
+ else
+ {
+ GetLayout()->SetAssertFlyPages();
+ }
+}
+
+void SwFEShell::GetPageObjs( std::vector<SwFrameFormat*>& rFillArr )
+{
+ rFillArr.clear();
+
+ for( auto pFormat : *mxDoc->GetSpzFrameFormats() )
+ {
+ if (RndStdIds::FLY_AT_PAGE == pFormat->GetAnchor().GetAnchorId())
+ {
+ rFillArr.push_back( pFormat );
+ }
+ }
+}
+
+void SwFEShell::SetPageObjsNewPage( std::vector<SwFrameFormat*>& rFillArr )
+{
+ if( rFillArr.empty() )
+ return;
+
+ StartAllAction();
+ StartUndo();
+
+ SwRootFrame* pTmpRootFrame = GetLayout();
+ sal_uInt16 nMaxPage = pTmpRootFrame->GetPageNum();
+ bool bTmpAssert = false;
+ for( auto pFormat : rFillArr )
+ {
+ if (mxDoc->GetSpzFrameFormats()->IsAlive(pFormat))
+ {
+ // FlyFormat is still valid, therefore process
+
+ SwFormatAnchor aNewAnchor( pFormat->GetAnchor() );
+ if (RndStdIds::FLY_AT_PAGE != aNewAnchor.GetAnchorId())
+ // Anchor has been changed, therefore: do not change!
+ continue;
+ sal_uInt16 nNewPage = aNewAnchor.GetPageNum() + 1;
+ if (nNewPage > nMaxPage)
+ {
+ if ( RES_DRAWFRMFMT == pFormat->Which() )
+ pFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PAGE_OUT_OF_BOUNDS));
+ else
+ pFormat->DelFrames();
+ bTmpAssert = true;
+ }
+ aNewAnchor.SetPageNum(nNewPage);
+ mxDoc->SetAttr( aNewAnchor, *pFormat );
+ }
+ }
+
+ if( bTmpAssert )
+ pTmpRootFrame->SetAssertFlyPages();
+
+ EndUndo();
+ EndAllAction();
+}
+
+// All attributes in the "baskets" will be filled with the attributes of the
+// current FlyFrames. Attributes which cannot be filled due to being at the
+// wrong place or which are ambiguous (multiple selections) will be removed.
+bool SwFEShell::GetFlyFrameAttr( SfxItemSet &rSet ) const
+{
+ SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame();
+ if (!pFly)
+ {
+ OSL_ENSURE( false, "GetFlyFrameAttr, no Fly selected." );
+ return false;
+ }
+
+ CurrShell aCurr( const_cast<SwFEShell*>(this) );
+
+ if( !rSet.Set( pFly->GetFormat()->GetAttrSet() ) )
+ return false;
+
+ // now examine all attributes. Remove forbidden attributes, then
+ // get all remaining attributes and enter them
+
+ if( const SwFormatAnchor* pAnchor = rSet.GetItemIfSet( RES_ANCHOR, false ) )
+ {
+ RndStdIds eType = pAnchor->GetAnchorId();
+
+ if ( RndStdIds::FLY_AT_PAGE != eType )
+ {
+ // OD 12.11.2003 #i22341# - content anchor of anchor item is needed.
+ // Thus, don't overwrite anchor item by default constructed anchor item.
+ if ( RndStdIds::FLY_AS_CHAR == eType )
+ {
+ rSet.ClearItem( RES_OPAQUE );
+ rSet.ClearItem( RES_SURROUND );
+ }
+ }
+ }
+ rSet.SetParent( pFly->GetFormat()->GetAttrSet().GetParent() );
+ // attributes must be removed
+ rSet.ClearItem( RES_FILL_ORDER );
+ rSet.ClearItem( RES_CNTNT );
+ //MA: remove first (Template by example etc.)
+ rSet.ClearItem( RES_CHAIN );
+ return true;
+}
+
+// Attributes of the current fly will change.
+bool SwFEShell::SetFlyFrameAttr( SfxItemSet& rSet )
+{
+ CurrShell aCurr( this );
+ bool bRet = false;
+
+ if( rSet.Count() )
+ {
+ SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame();
+ OSL_ENSURE(pFly, "SetFlyFrameAttr, no Fly selected.");
+ if (pFly)
+ {
+ StartAllAction();
+ const Point aPt( pFly->getFrameArea().Pos() );
+
+ if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
+ sw_ChkAndSetNewAnchor( *pFly, rSet );
+ SwFlyFrameFormat* pFlyFormat = pFly->GetFormat();
+
+ if( GetDoc()->SetFlyFrameAttr( *pFlyFormat, rSet ))
+ {
+ bRet = true;
+ SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt );
+ if( pFrame )
+ SelectFlyFrame( *pFrame );
+ else
+ GetLayout()->SetAssertFlyPages();
+ }
+
+ EndAllActionAndCall();
+ }
+ }
+ return bRet;
+}
+
+SfxItemSetFixed<RES_VERT_ORIENT, RES_ANCHOR> SwFEShell::makeItemSetFromFormatAnchor(SfxItemPool& rPool, const SwFormatAnchor &rAnchor)
+{
+ // The set also includes VERT/HORI_ORIENT, because the align
+ // shall be changed in FEShell::SetFlyFrameAttr/SetFlyFrameAnchor,
+ // possibly as a result of the anchor change.
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_ANCHOR> aSet(rPool);
+ aSet.Put(rAnchor);
+ return aSet;
+}
+
+bool SwFEShell::SetDrawingAttr( SfxItemSet& rSet )
+{
+ bool bRet = false;
+ CurrShell aCurr( this );
+ if ( !rSet.Count() ||
+ !Imp()->HasDrawView() )
+ return bRet;
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( rMrkList.GetMarkCount() != 1 )
+ return bRet;
+
+ StartUndo();
+ SdrObject *pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ SwFrameFormat *pFormat = FindFrameFormat( pObj );
+ StartAllAction();
+ if( SfxItemState::SET == rSet.GetItemState( RES_ANCHOR, false ))
+ {
+ RndStdIds nNew = rSet.Get( RES_ANCHOR ).GetAnchorId();
+ if ( nNew != pFormat->GetAnchor().GetAnchorId() )
+ {
+ ChgAnchor( nNew );
+ // #i26791# - clear anchor attribute in item set,
+ // because method <ChgAnchor(..)> takes care of it.
+ rSet.ClearItem( RES_ANCHOR );
+ }
+ }
+
+ if( GetDoc()->SetFlyFrameAttr( *pFormat, rSet ))
+ {
+ bRet = true;
+ SelectObj( Point(), 0, pObj );
+ }
+ EndAllActionAndCall();
+ EndUndo();
+ return bRet;
+}
+
+// Reset attributes contained in the set.
+void SwFEShell::ResetFlyFrameAttr( const SfxItemSet* pSet )
+{
+ CurrShell aCurr( this );
+
+ SwFlyFrame *pFly = GetSelectedOrCurrFlyFrame();
+ OSL_ENSURE( pFly, "SetFlyFrameAttr, no Fly selected." );
+ if( !pFly )
+ return;
+
+ StartAllAction();
+
+ SfxItemIter aIter( *pSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if( !IsInvalidItem( pItem ) )
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if( RES_ANCHOR != nWhich && RES_CHAIN != nWhich && RES_CNTNT != nWhich )
+ pFly->GetFormat()->ResetFormatAttr( nWhich );
+ }
+ }
+
+ EndAllActionAndCall();
+ GetDoc()->getIDocumentState().SetModified();
+}
+
+// Returns frame-format if frame, otherwise 0
+SwFrameFormat* SwFEShell::GetSelectedFrameFormat() const
+{
+ SwFrameFormat* pRet = nullptr;
+ SwLayoutFrame *pFly = GetSelectedFlyFrame();
+ if( pFly && ( pRet = static_cast<SwFrameFormat*>(pFly->GetFormat()->DerivedFrom()) ) ==
+ GetDoc()->GetDfltFrameFormat() )
+ pRet = nullptr;
+ return pRet;
+}
+
+void SwFEShell::SetFrameFormat( SwFrameFormat *pNewFormat, bool bKeepOrient, Point const * pDocPos )
+{
+ SwFlyFrame *pFly = nullptr;
+ if(pDocPos)
+ {
+ const SwFrameFormat* pFormat = GetFormatFromObj( *pDocPos );
+
+ if (const SwFlyFrameFormat* pFlyFormat = dynamic_cast<const SwFlyFrameFormat*>(pFormat))
+ pFly = pFlyFormat->GetFrame();
+ }
+ else
+ pFly = GetSelectedFlyFrame();
+ OSL_ENSURE( pFly, "SetFrameFormat: no frame" );
+ if( !pFly )
+ return;
+
+ StartAllAction();
+ CurrShell aCurr( this );
+
+ SwFlyFrameFormat* pFlyFormat = pFly->GetFormat();
+ const Point aPt( pFly->getFrameArea().Pos() );
+
+ std::optional<SfxItemSet> oSet;
+ const SfxPoolItem* pItem;
+ if( SfxItemState::SET == pNewFormat->GetItemState( RES_ANCHOR, false, &pItem ))
+ {
+ oSet.emplace( GetDoc()->GetAttrPool(), aFrameFormatSetRange );
+ oSet->Put( *pItem );
+ if( !sw_ChkAndSetNewAnchor( *pFly, *oSet ))
+ {
+ oSet.reset();
+ }
+ }
+
+ if( GetDoc()->SetFrameFormatToFly( *pFlyFormat, *pNewFormat, oSet ? &*oSet : nullptr, bKeepOrient ))
+ {
+ SwFlyFrame* pFrame = pFlyFormat->GetFrame( &aPt );
+ if( pFrame )
+ SelectFlyFrame( *pFrame );
+ else
+ GetLayout()->SetAssertFlyPages();
+ }
+ oSet.reset();
+
+ EndAllActionAndCall();
+}
+
+const SwFrameFormat* SwFEShell::GetFlyFrameFormat() const
+{
+ const SwFlyFrame* pFly = GetSelectedOrCurrFlyFrame();
+ if (pFly)
+ return pFly->GetFormat();
+ return nullptr;
+}
+
+SwFrameFormat* SwFEShell::GetFlyFrameFormat()
+{
+ SwFlyFrame* pFly = GetSelectedOrCurrFlyFrame();
+ if (pFly)
+ return pFly->GetFormat();
+ return nullptr;
+}
+
+SwRect SwFEShell::GetFlyRect() const
+{
+ SwFlyFrame *pFly = GetCurrFlyFrame(false);
+ if (!pFly)
+ {
+ SwRect aRect;
+ return aRect;
+ }
+ else
+ return pFly->getFrameArea();
+}
+
+SwRect SwFEShell::GetObjRect() const
+{
+ if( Imp()->HasDrawView() )
+ return SwRect(Imp()->GetDrawView()->GetAllMarkedRect());
+ else
+ {
+ SwRect aRect;
+ return aRect;
+ }
+}
+
+void SwFEShell::SetObjRect( const SwRect& rRect )
+{
+ if ( Imp()->HasDrawView() )
+ {
+ Imp()->GetDrawView()->SetAllMarkedRect( rRect.SVRect() );
+ CallChgLnk(); // call AttrChangeNotify on the UI-side.
+ }
+}
+
+Size SwFEShell::RequestObjectResize( const SwRect &rRect, const uno::Reference < embed::XEmbeddedObject >& xObj )
+{
+ Size aResult;
+
+ SwFlyFrame *pFly = FindFlyFrame( xObj );
+ if ( !pFly )
+ {
+ aResult = rRect.SSize();
+ return aResult;
+ }
+
+ aResult = pFly->getFramePrintArea().SSize();
+
+ bool bPosProt = pFly->GetFormat()->GetProtect().IsPosProtected();
+ bool bSizeProt = pFly->GetFormat()->GetProtect().IsSizeProtected();
+
+ StartAllAction();
+
+ // MA we do not allow to clip the Fly, as the OLE server can
+ // request various wishes. Clipping is done via the formatting.
+ // Correct display is done by scaling.
+ // Scaling is done by SwNoTextFrame::Format by calling
+ // SwWrtShell::CalcAndSetScale()
+ if ( rRect.SSize() != pFly->getFramePrintArea().SSize() && !bSizeProt )
+ {
+ Size aSz( rRect.SSize() );
+
+ //JP 28.02.2001: Task 74707 - ask for fly in fly with automatic size
+
+ const SwFrame* pAnchor;
+ const SwFormatFrameSize& rFrameSz = pFly->GetFormat()->GetFrameSize();
+ if (m_bCheckForOLEInCaption &&
+ 0 != rFrameSz.GetWidthPercent() &&
+ nullptr != (pAnchor = pFly->GetAnchorFrame()) &&
+ pAnchor->IsTextFrame() &&
+ !pAnchor->GetNext() && !pAnchor->GetPrev() &&
+ pAnchor->GetUpper()->IsFlyFrame())
+ {
+ // search for a sequence field:
+ sw::MergedAttrIter iter(*static_cast<SwTextFrame const*>(pAnchor));
+ for (SwTextAttr const* pHint = iter.NextAttr(); pHint; pHint = iter.NextAttr())
+ {
+ const SfxPoolItem* pItem = &pHint->GetAttr();
+ if( RES_TXTATR_FIELD == pItem->Which()
+ && SwFieldTypesEnum::Sequence == static_cast<const SwFormatField*>(pItem)->GetField()->GetTypeId() )
+ {
+ // sequence field found
+ SwFlyFrame* pChgFly = const_cast<SwFlyFrame*>(static_cast<const SwFlyFrame*>(pAnchor->GetUpper()));
+ // calculate the changed size:
+ // width must change, height can change
+ Size aNewSz( aSz.Width() + pChgFly->getFrameArea().Width() -
+ pFly->getFramePrintArea().Width(), aSz.Height() );
+
+ SwFrameFormat *pFormat = pChgFly->GetFormat();
+ SwFormatFrameSize aFrameSz( pFormat->GetFrameSize() );
+ aFrameSz.SetWidth( aNewSz.Width() );
+ if( SwFrameSize::Minimum != aFrameSz.GetHeightSizeType() )
+ {
+ aNewSz.AdjustHeight(pChgFly->getFrameArea().Height() -
+ pFly->getFramePrintArea().Height() );
+ if( std::abs( aNewSz.Height() - pChgFly->getFrameArea().Height()) > 1 )
+ aFrameSz.SetHeight( aNewSz.Height() );
+ }
+ // via Doc for the Undo!
+ pFormat->GetDoc()->SetAttr( aFrameSz, *pFormat );
+ break;
+ }
+ }
+ }
+
+ // set the new Size at the fly themself
+ if ( !pFly->getFramePrintArea().IsEmpty() )
+ {
+ aSz.AdjustWidth(pFly->getFrameArea().Width() - pFly->getFramePrintArea().Width() );
+ aSz.AdjustHeight(pFly->getFrameArea().Height()- pFly->getFramePrintArea().Height() );
+ }
+ aResult = pFly->ChgSize( aSz );
+
+ // if the object changes, the contour is outside the object
+ assert(pFly->Lower()->IsNoTextFrame());
+ SwNoTextNode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetNoTextNode();
+ assert(pNd);
+ pNd->SetContour( nullptr );
+ ClrContourCache();
+ }
+
+ // if only the size is to be adjusted, a position is transported with
+ // allocated values
+ Point aPt( pFly->getFramePrintArea().Pos() );
+ aPt += pFly->getFrameArea().Pos();
+ if ( rRect.Top() != LONG_MIN && rRect.Pos() != aPt && !bPosProt )
+ {
+ aPt = rRect.Pos();
+ aPt.setX(aPt.getX() - pFly->getFramePrintArea().Left());
+ aPt.setY(aPt.getY() - pFly->getFramePrintArea().Top());
+
+ // in case of paragraph-bound Flys, starting from the new position,
+ // a new anchor is to be set. The anchor and the new RelPos are
+ // calculated by the Fly and set
+ if( pFly->IsFlyAtContentFrame() )
+ static_cast<SwFlyAtContentFrame*>(pFly)->SetAbsPos( aPt );
+ else
+ {
+ const SwFrameFormat *pFormat = pFly->GetFormat();
+ const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
+ const SwFormatHoriOrient &rHori = pFormat->GetHoriOrient();
+ const tools::Long lXDiff = aPt.getX() - pFly->getFrameArea().Left();
+ const tools::Long lYDiff = aPt.getY() - pFly->getFrameArea().Top();
+ const Point aTmp( rHori.GetPos() + lXDiff,
+ rVert.GetPos() + lYDiff );
+ pFly->ChgRelPos( aTmp );
+ }
+ }
+
+ SwFlyFrameFormat *pFlyFrameFormat = pFly->GetFormat();
+ OSL_ENSURE( pFlyFrameFormat, "fly frame format missing!" );
+ if ( pFlyFrameFormat )
+ pFlyFrameFormat->SetLastFlyFramePrtRectPos( pFly->getFramePrintArea().Pos() ); //stores the value of last Prt rect
+
+ EndAllAction();
+
+ return aResult;
+}
+
+SwFrameFormat* SwFEShell::WizardGetFly()
+{
+ // do not search the Fly via the layout. Now we can delete a frame
+ // without a valid layout. ( e.g. for the wizards )
+ SwFrameFormats& rSpzArr = *mxDoc->GetSpzFrameFormats();
+ if( !rSpzArr.empty() )
+ {
+ SwNodeIndex& rCursorNd = GetCursor()->GetPoint()->nNode;
+ if( rCursorNd.GetIndex() > mxDoc->GetNodes().GetEndOfExtras().GetIndex() )
+ // Cursor is in the body area!
+ return nullptr;
+
+ for( auto pFormat : rSpzArr )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent( false ).GetContentIdx();
+ SwStartNode* pSttNd;
+ if( pIdx &&
+ nullptr != ( pSttNd = pIdx->GetNode().GetStartNode() ) &&
+ pSttNd->GetIndex() < rCursorNd.GetIndex() &&
+ rCursorNd.GetIndex() < pSttNd->EndOfSectionIndex() )
+ {
+ // found: return immediately
+ return pFormat;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SwFEShell::SetFlyName( const OUString& rName )
+{
+ SwLayoutFrame *pFly = GetSelectedFlyFrame();
+ if( pFly )
+ GetDoc()->SetFlyName( *static_cast<SwFlyFrameFormat*>(pFly->GetFormat()), rName );
+ else {
+ OSL_ENSURE( false, "no FlyFrame selected" );
+ }
+}
+
+OUString SwFEShell::GetFlyName() const
+{
+ SwLayoutFrame *pFly = GetSelectedFlyFrame();
+ if( pFly )
+ return pFly->GetFormat()->GetName();
+
+ OSL_ENSURE( false, "no FlyFrame selected" );
+ return OUString();
+}
+
+uno::Reference < embed::XEmbeddedObject > SwFEShell::GetOleRef() const
+{
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ SwFlyFrame * pFly = GetSelectedFlyFrame();
+ if (pFly && pFly->Lower() && pFly->Lower()->IsNoTextFrame())
+ {
+ SwOLENode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode();
+ if (pNd)
+ xObj = pNd->GetOLEObj().GetOleRef();
+ }
+ return xObj;
+}
+
+OUString SwFEShell::GetUniqueGrfName() const
+{
+ return GetDoc()->GetUniqueGrfName();
+}
+
+const SwFrameFormat* SwFEShell::IsURLGrfAtPos( const Point& rPt, OUString* pURL,
+ OUString *pTargetFrameName,
+ OUString *pDescription ) const
+{
+ if( !Imp()->HasDrawView() )
+ return nullptr;
+
+ SdrPageView* pPV;
+ const SwFrameFormat* pRet = nullptr;
+ SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());
+
+ const auto nOld = pDView->GetHitTolerancePixel();
+ pDView->SetHitTolerancePixel( 2 );
+
+ SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO);
+ SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
+ if (pFlyObj)
+ {
+ SwFlyFrame *pFly = pFlyObj->GetFlyFrame();
+ const SwFormatURL &rURL = pFly->GetFormat()->GetURL();
+ if( !rURL.GetURL().isEmpty() || rURL.GetMap() )
+ {
+ bool bSetTargetFrameName = pTargetFrameName != nullptr;
+ bool bSetDescription = pDescription != nullptr;
+ if ( rURL.GetMap() )
+ {
+ IMapObject *pObject = pFly->GetFormat()->GetIMapObject( rPt, pFly );
+ if ( pObject && !pObject->GetURL().isEmpty() )
+ {
+ if( pURL )
+ *pURL = pObject->GetURL();
+ if ( bSetTargetFrameName && !pObject->GetTarget().isEmpty() )
+ {
+ bSetTargetFrameName = false;
+ *pTargetFrameName = pObject->GetTarget();
+ }
+ if ( bSetDescription )
+ {
+ bSetDescription = false;
+ *pDescription = pObject->GetAltText();
+ }
+ pRet = pFly->GetFormat();
+ }
+ }
+ else
+ {
+ if( pURL )
+ {
+ *pURL = rURL.GetURL();
+ if( rURL.IsServerMap() )
+ {
+ // append the relative pixel position !!
+ Point aPt( rPt );
+ aPt -= pFly->getFrameArea().Pos();
+ // without MapMode-Offset, without Offset, o ... !!!!!
+ aPt = GetOut()->LogicToPixel(
+ aPt, MapMode( MapUnit::MapTwip ) );
+ *pURL = *pURL + "?" + OUString::number( aPt.getX() )
+ + "," + OUString::number(aPt.getY() );
+ }
+ }
+ pRet = pFly->GetFormat();
+ }
+ if ( bSetTargetFrameName )
+ *pTargetFrameName = rURL.GetTargetFrameName();
+ if ( bSetDescription )
+ *pDescription = pFly->GetFormat()->GetName();
+ }
+ }
+ pDView->SetHitTolerancePixel( nOld );
+ return pRet;
+}
+
+const Graphic *SwFEShell::GetGrfAtPos( const Point &rPt,
+ OUString &rName, bool &rbLink ) const
+{
+ if( !Imp()->HasDrawView() )
+ return nullptr;
+
+ SdrPageView* pPV;
+ SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());
+
+ SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV);
+ SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
+ if (pFlyObj)
+ {
+ SwFlyFrame *pFly = pFlyObj->GetFlyFrame();
+ if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ SwGrfNode *const pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetGrfNode();
+ if ( pNd )
+ {
+ if ( pNd->IsGrfLink() )
+ {
+ // halfway ready graphic?
+ ::sfx2::SvLinkSource* pLnkObj = pNd->GetLink()->GetObj();
+ if( pLnkObj && pLnkObj->IsPending() )
+ return nullptr;
+ rbLink = true;
+ }
+
+ pNd->GetFileFilterNms( &rName, nullptr );
+ if ( rName.isEmpty() )
+ rName = pFly->GetFormat()->GetName();
+ return &pNd->GetGrf(true);
+ }
+ }
+ }
+ return nullptr;
+}
+
+const SwFrameFormat* SwFEShell::GetFormatFromObj( const Point& rPt, SwRect** pRectToFill ) const
+{
+ SwFrameFormat* pRet = nullptr;
+
+ if( Imp()->HasDrawView() )
+ {
+ SdrPageView* pPView;
+
+ SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());
+
+ const auto nOld = pDView->GetHitTolerancePixel();
+ // tolerance for Drawing-SS
+ pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 );
+
+ SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE);
+ if (pObj)
+ {
+ // first check it:
+ if (SwVirtFlyDrawObj* pFlyObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
+ pRet = pFlyObj->GetFormat();
+ else if ( pObj->GetUserCall() ) //not for group objects
+ pRet = static_cast<SwDrawContact*>(pObj->GetUserCall())->GetFormat();
+ if(pRet && pRectToFill)
+ **pRectToFill = SwRect(pObj->GetCurrentBoundRect());
+ }
+ pDView->SetHitTolerancePixel( nOld );
+ }
+ return pRet;
+}
+
+// returns a format too, if the point is over the text of any fly
+const SwFrameFormat* SwFEShell::GetFormatFromAnyObj( const Point& rPt ) const
+{
+ const SwFrameFormat* pRet = GetFormatFromObj( rPt );
+ if( !pRet || RES_FLYFRMFMT == pRet->Which() )
+ {
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( rPt );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
+ SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(rPt, false);
+ SwFrame* pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp)->FindFlyFrame();
+ pRet = pFrame ? static_cast<SwLayoutFrame*>(pFrame)->GetFormat() : nullptr;
+ }
+ return pRet;
+}
+
+ObjCntType SwFEShell::GetObjCntType( const SdrObject& rObj )
+{
+ ObjCntType eType = OBJCNT_NONE;
+
+ // investigate 'master' drawing object, if method
+ // is called for a 'virtual' drawing object.
+ const SdrObject* pInvestigatedObj;
+ if (const SwDrawVirtObj* pDrawVirtObj = dynamic_cast<const SwDrawVirtObj*>( &rObj))
+ {
+ pInvestigatedObj = &(pDrawVirtObj->GetReferencedObj());
+ }
+ else
+ {
+ pInvestigatedObj = &rObj;
+ }
+
+ if( SdrInventor::FmForm == pInvestigatedObj->GetObjInventor() )
+ {
+ eType = OBJCNT_CONTROL;
+ uno::Reference< awt::XControlModel > xModel =
+ static_cast<const SdrUnoObj&>(*pInvestigatedObj).GetUnoControlModel();
+ if( xModel.is() )
+ {
+ uno::Any aVal;
+ OUString sName("ButtonType");
+ uno::Reference< beans::XPropertySet > xSet(xModel, uno::UNO_QUERY);
+
+ uno::Reference< beans::XPropertySetInfo > xInfo = xSet->getPropertySetInfo();
+ if(xInfo->hasPropertyByName( sName ))
+ {
+ aVal = xSet->getPropertyValue( sName );
+ if( aVal.hasValue() && form::FormButtonType_URL == *o3tl::doAccess<form::FormButtonType>(aVal) )
+ eType = OBJCNT_URLBUTTON;
+ }
+ }
+ }
+ else if (const SwVirtFlyDrawObj *pFlyObj = dynamic_cast<const SwVirtFlyDrawObj*>(pInvestigatedObj))
+ {
+ const SwFlyFrame *pFly = pFlyObj->GetFlyFrame();
+ if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ if (static_cast<const SwNoTextFrame*>(pFly->Lower())->GetNode()->GetGrfNode())
+ eType = OBJCNT_GRF;
+ else
+ eType = OBJCNT_OLE;
+ }
+ else
+ eType = OBJCNT_FLY;
+ }
+ else if ( dynamic_cast<const SdrObjGroup*>( pInvestigatedObj) != nullptr )
+ {
+ SwDrawContact* pDrawContact( dynamic_cast<SwDrawContact*>(GetUserCall( pInvestigatedObj ) ) );
+ if ( !pDrawContact )
+ {
+ OSL_FAIL( "<SwFEShell::GetObjCntType(..)> - missing draw contact object" );
+ eType = OBJCNT_NONE;
+ }
+ else
+ {
+ SwFrameFormat* pFrameFormat( pDrawContact->GetFormat() );
+ if ( !pFrameFormat )
+ {
+ OSL_FAIL( "<SwFEShell::GetObjCntType(..)> - missing frame format" );
+ eType = OBJCNT_NONE;
+ }
+ else if ( RndStdIds::FLY_AS_CHAR != pFrameFormat->GetAnchor().GetAnchorId() )
+ {
+ eType = OBJCNT_GROUPOBJ;
+ }
+ }
+ }
+ else
+ eType = OBJCNT_SIMPLE;
+ return eType;
+}
+
+ObjCntType SwFEShell::GetObjCntType( const Point &rPt, SdrObject *&rpObj ) const
+{
+ ObjCntType eType = OBJCNT_NONE;
+
+ if( Imp()->HasDrawView() )
+ {
+ SdrPageView* pPView;
+
+ SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());
+
+ const auto nOld = pDView->GetHitTolerancePixel();
+ // tolerance for Drawing-SS
+ pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 );
+
+ SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE);
+ if (pObj)
+ {
+ rpObj = pObj;
+ eType = GetObjCntType( *rpObj );
+ }
+
+ pDView->SetHitTolerancePixel( nOld );
+ }
+ return eType;
+}
+
+ObjCntType SwFEShell::GetObjCntTypeOfSelection() const
+{
+ ObjCntType eType = OBJCNT_NONE;
+
+ if( Imp()->HasDrawView() )
+ {
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for( size_t i = 0, nE = rMrkList.GetMarkCount(); i < nE; ++i )
+ {
+ SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if( !pObj )
+ continue;
+ ObjCntType eTmp = GetObjCntType( *pObj );
+ if( !i )
+ {
+ eType = eTmp;
+ }
+ else if( eTmp != eType )
+ {
+ eType = OBJCNT_DONTCARE;
+ // once DontCare, always DontCare!
+ break;
+ }
+ }
+ }
+ return eType;
+}
+
+void SwFEShell::ReplaceSdrObj( const OUString& rGrfName, const Graphic* pGrf )
+{
+ CurrShell aCurr( this );
+
+ const SdrMarkList *pMrkList;
+ if( !(Imp()->HasDrawView() && 1 ==
+ ( pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList())->GetMarkCount()) )
+ return;
+
+ SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ SwFrameFormat *pFormat = FindFrameFormat( pObj );
+
+ // store attributes, then set the graphic
+ SfxItemSet aFrameSet( mxDoc->GetAttrPool(),
+ pFormat->GetAttrSet().GetRanges() );
+ aFrameSet.Set( pFormat->GetAttrSet() );
+
+ // set size and position?
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ {
+ // then let's do it:
+ const tools::Rectangle &rBound = pObj->GetSnapRect();
+ Point aRelPos( pObj->GetRelativePos() );
+
+ const tools::Long nWidth = rBound.Right() - rBound.Left();
+ const tools::Long nHeight= rBound.Bottom() - rBound.Top();
+ aFrameSet.Put( SwFormatFrameSize( SwFrameSize::Minimum,
+ std::max( nWidth, tools::Long(MINFLY) ),
+ std::max( nHeight, tools::Long(MINFLY) )));
+
+ if( SfxItemState::SET != aFrameSet.GetItemState( RES_HORI_ORIENT ))
+ aFrameSet.Put( SwFormatHoriOrient( aRelPos.getX(), text::HoriOrientation::NONE, text::RelOrientation::FRAME ));
+
+ if( SfxItemState::SET != aFrameSet.GetItemState( RES_VERT_ORIENT ))
+ aFrameSet.Put( SwFormatVertOrient( aRelPos.getY(), text::VertOrientation::NONE, text::RelOrientation::FRAME ));
+
+ }
+
+ pObj->GetOrdNum();
+
+ StartAllAction();
+ StartUndo();
+
+ // delete "Sdr-Object", insert the graphic instead
+ DelSelectedObj();
+
+ GetDoc()->getIDocumentContentOperations().InsertGraphic(
+ *GetCursor(), rGrfName, "", pGrf, &aFrameSet, nullptr, nullptr);
+
+ EndUndo();
+ EndAllAction();
+}
+
+static sal_uInt16 SwFormatGetPageNum(const SwFlyFrameFormat * pFormat)
+{
+ OSL_ENSURE(pFormat != nullptr, "invalid argument");
+
+ SwFlyFrame * pFrame = pFormat->GetFrame();
+
+ sal_uInt16 aResult;
+
+ if (pFrame != nullptr)
+ aResult = pFrame->GetPhyPageNum();
+ else
+ aResult = pFormat->GetAnchor().GetPageNum();
+
+ return aResult;
+}
+
+void SwFEShell::GetConnectableFrameFormats(SwFrameFormat & rFormat,
+ const OUString & rReference,
+ bool bSuccessors,
+ std::vector< OUString > & aPrevPageVec,
+ std::vector< OUString > & aThisPageVec,
+ std::vector< OUString > & aNextPageVec,
+ std::vector< OUString > & aRestVec)
+{
+ StartAction();
+
+ SwFormatChain rChain = rFormat.GetChain();
+ SwFrameFormat * pOldChainNext = rChain.GetNext();
+ SwFrameFormat * pOldChainPrev = rChain.GetPrev();
+
+ if (pOldChainNext)
+ mxDoc->Unchain(rFormat);
+
+ if (pOldChainPrev)
+ mxDoc->Unchain(*pOldChainPrev);
+
+ const size_t nCnt = mxDoc->GetFlyCount(FLYCNTTYPE_FRM);
+
+ /* potential successors resp. predecessors */
+ std::vector< const SwFrameFormat * > aTmpSpzArray;
+
+ mxDoc->FindFlyByName(rReference);
+
+ for (size_t n = 0; n < nCnt; ++n)
+ {
+ const SwFrameFormat & rFormat1 = *(mxDoc->GetFlyNum(n, FLYCNTTYPE_FRM));
+
+ /*
+ pFormat is a potential successor of rFormat if it is chainable after
+ rFormat.
+
+ pFormat is a potential predecessor of rFormat if rFormat is chainable
+ after pFormat.
+ */
+
+ SwChainRet nChainState;
+
+ if (bSuccessors)
+ nChainState = mxDoc->Chainable(rFormat, rFormat1);
+ else
+ nChainState = mxDoc->Chainable(rFormat1, rFormat);
+
+ if (nChainState == SwChainRet::OK)
+ {
+ aTmpSpzArray.push_back(&rFormat1);
+
+ }
+
+ }
+
+ if (!aTmpSpzArray.empty())
+ {
+ aPrevPageVec.clear();
+ aThisPageVec.clear();
+ aNextPageVec.clear();
+ aRestVec.clear();
+
+ /* number of page rFormat resides on */
+ sal_uInt16 nPageNum = SwFormatGetPageNum(static_cast<SwFlyFrameFormat *>(&rFormat));
+
+ for (const auto& rpFormat : aTmpSpzArray)
+ {
+ const OUString aString = rpFormat->GetName();
+
+ /* rFormat is not a valid successor or predecessor of
+ itself */
+ if (aString != rReference && aString != rFormat.GetName())
+ {
+ sal_uInt16 nNum1 =
+ SwFormatGetPageNum(static_cast<const SwFlyFrameFormat *>(rpFormat));
+
+ if (nNum1 == nPageNum -1)
+ aPrevPageVec.push_back(aString);
+ else if (nNum1 == nPageNum)
+ aThisPageVec.push_back(aString);
+ else if (nNum1 == nPageNum + 1)
+ aNextPageVec.push_back(aString);
+ else
+ aRestVec.push_back(aString);
+ }
+ }
+
+ }
+
+ if (pOldChainNext)
+ mxDoc->Chain(rFormat, *pOldChainNext);
+
+ if (pOldChainPrev)
+ mxDoc->Chain(*pOldChainPrev, rFormat);
+
+ EndAction();
+}
+
+// #i73249#
+OUString SwFEShell::GetObjTitle() const
+{
+ if ( Imp()->HasDrawView() )
+ {
+ const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( pMrkList->GetMarkCount() == 1 )
+ {
+ const SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ const SwFrameFormat* pFormat = FindFrameFormat( pObj );
+ if ( pFormat->Which() == RES_FLYFRMFMT )
+ {
+ return static_cast<const SwFlyFrameFormat*>(pFormat)->GetObjTitle();
+ }
+ return pObj->GetTitle();
+ }
+ }
+
+ return OUString();
+}
+
+void SwFEShell::SetObjTitle( const OUString& rTitle )
+{
+ if ( !Imp()->HasDrawView() )
+ return;
+
+ const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( pMrkList->GetMarkCount() != 1 )
+ return;
+
+ SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ SwFrameFormat* pFormat = FindFrameFormat( pObj );
+ if ( pFormat->Which() == RES_FLYFRMFMT )
+ {
+ GetDoc()->SetFlyFrameTitle( dynamic_cast<SwFlyFrameFormat&>(*pFormat),
+ rTitle );
+ }
+ else
+ {
+ pObj->SetTitle( rTitle );
+ }
+}
+
+OUString SwFEShell::GetObjDescription() const
+{
+ if ( Imp()->HasDrawView() )
+ {
+ const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( pMrkList->GetMarkCount() == 1 )
+ {
+ const SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ const SwFrameFormat* pFormat = FindFrameFormat( pObj );
+ if ( pFormat->Which() == RES_FLYFRMFMT )
+ {
+ return dynamic_cast<const SwFlyFrameFormat&>(*pFormat).GetObjDescription();
+ }
+ return pObj->GetDescription();
+ }
+ }
+
+ return OUString();
+}
+
+void SwFEShell::SetObjDescription( const OUString& rDescription )
+{
+ if ( !Imp()->HasDrawView() )
+ return;
+
+ const SdrMarkList *pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ if ( pMrkList->GetMarkCount() != 1 )
+ return;
+
+ SdrObject* pObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ SwFrameFormat* pFormat = FindFrameFormat( pObj );
+ if ( pFormat->Which() == RES_FLYFRMFMT )
+ {
+ GetDoc()->SetFlyFrameDescription(dynamic_cast<SwFlyFrameFormat&>(*pFormat),
+ rDescription);
+ }
+ else
+ {
+ pObj->SetDescription( rDescription );
+ }
+}
+
+void SwFEShell::AlignFormulaToBaseline( const uno::Reference < embed::XEmbeddedObject >& xObj )
+{
+#if OSL_DEBUG_LEVEL > 0
+ SvGlobalName aCLSID( xObj->getClassID() );
+ const bool bStarMath = ( SotExchange::IsMath( aCLSID ) != 0 );
+ OSL_ENSURE( bStarMath, "AlignFormulaToBaseline should only be called for Math objects" );
+
+ if ( !bStarMath )
+ return;
+#endif
+
+ SwFlyFrame * pFly = FindFlyFrame( xObj );
+ OSL_ENSURE( pFly , "No fly frame!" );
+ SwFrameFormat * pFrameFormat = pFly ? pFly->GetFormat() : nullptr;
+
+ // baseline to baseline alignment should only be applied to formulas anchored as char
+ if ( !pFly || !pFrameFormat || RndStdIds::FLY_AS_CHAR != pFrameFormat->GetAnchor().GetAnchorId() )
+ return;
+
+ // get baseline from Math object
+ uno::Any aBaseline;
+ if( svt::EmbeddedObjectRef::TryRunningState( xObj ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( xObj->getComponent(), uno::UNO_QUERY );
+ if ( xSet.is() )
+ {
+ try
+ {
+ aBaseline = xSet->getPropertyValue("BaseLine");
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "Baseline could not be retrieved from Starmath!" );
+ }
+ }
+ }
+
+ sal_Int32 nBaseline = ::comphelper::getINT32(aBaseline);
+ nBaseline = o3tl::toTwips( nBaseline, o3tl::Length::mm100 );
+
+ OSL_ENSURE( nBaseline > 0, "Wrong value of Baseline while retrieving from Starmath!" );
+ //nBaseline must be moved by aPrt position
+ const SwFlyFrameFormat *pFlyFrameFormat = pFly->GetFormat();
+ OSL_ENSURE( pFlyFrameFormat, "fly frame format missing!" );
+ if ( pFlyFrameFormat )
+ nBaseline += pFlyFrameFormat->GetLastFlyFramePrtRectPos().Y();
+
+ const SwFormatVertOrient &rVert = pFrameFormat->GetVertOrient();
+ SwFormatVertOrient aVert( rVert );
+ aVert.SetPos( -nBaseline );
+ aVert.SetVertOrient( css::text::VertOrientation::NONE );
+
+ pFrameFormat->LockModify();
+ pFrameFormat->SetFormatAttr( aVert );
+ pFrameFormat->UnlockModify();
+ pFly->InvalidatePos();
+
+}
+
+void SwFEShell::AlignAllFormulasToBaseline()
+{
+ StartAllAction();
+
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwOLENode *pOleNode = dynamic_cast< SwOLENode * >( &aIdx.GetNode() );
+ if ( pOleNode )
+ {
+ const uno::Reference < embed::XEmbeddedObject > & xObj( pOleNode->GetOLEObj().GetOleRef() );
+ if (xObj.is())
+ {
+ SvGlobalName aCLSID( xObj->getClassID() );
+ if ( SotExchange::IsMath( aCLSID ) )
+ AlignFormulaToBaseline( xObj );
+ }
+ }
+
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+
+ EndAllAction();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/feflyole.cxx b/sw/source/core/frmedt/feflyole.cxx
new file mode 100644
index 000000000..efcbaaa17
--- /dev/null
+++ b/sw/source/core/frmedt/feflyole.cxx
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include <sfx2/ipclient.hxx>
+#include <sfx2/viewsh.hxx>
+#include <osl/diagnose.h>
+
+#include <fesh.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <doc.hxx>
+#include <notxtfrm.hxx>
+#include <ndole.hxx>
+#include <swcli.hxx>
+#include <docsh.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <sfx2/linkmgr.hxx>
+
+using namespace com::sun::star;
+
+SwFlyFrame *SwFEShell::FindFlyFrame( const uno::Reference < embed::XEmbeddedObject >& xObj ) const
+{
+ SwFlyFrame *pFly = GetSelectedFlyFrame();
+ if ( pFly && pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ SwOLENode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode();
+ if ( !pNd || pNd->GetOLEObj().GetOleRef() != xObj )
+ pFly = nullptr;
+ }
+ else
+ pFly = nullptr;
+
+ if ( !pFly )
+ {
+ // No or wrong fly selected: we have to search.
+ bool bExist = false;
+ SwStartNode *pStNd;
+ SwNodeOffset nSttIdx = GetNodes().GetEndOfAutotext().StartOfSectionIndex() + 1,
+ nEndIdx = GetNodes().GetEndOfAutotext().GetIndex();
+ while( nSttIdx < nEndIdx &&
+ nullptr != (pStNd = GetNodes()[ nSttIdx ]->GetStartNode()) )
+ {
+ SwNode *pNd = GetNodes()[ nSttIdx+1 ];
+ if ( pNd->IsOLENode() &&
+ static_cast<SwOLENode*>(pNd)->GetOLEObj().GetOleRef() == xObj )
+ {
+ bExist = true;
+ SwFrame *pFrame = static_cast<SwOLENode*>(pNd)->getLayoutFrame( GetLayout() );
+ if ( pFrame )
+ pFly = pFrame->FindFlyFrame();
+ break;
+ }
+ nSttIdx = pStNd->EndOfSectionIndex() + 1;
+ }
+
+ OSL_ENSURE( bExist, "OLE-Object unknown and FlyFrame not found." );
+ }
+ return pFly;
+}
+
+OUString SwFEShell::GetUniqueOLEName() const
+{
+ return GetDoc()->GetUniqueOLEName();
+}
+
+OUString SwFEShell::GetUniqueFrameName() const
+{
+ return GetDoc()->GetUniqueFrameName();
+}
+
+bool SwFEShell::FinishOLEObj() // Server is terminated
+{
+ SfxInPlaceClient* pIPClient = GetSfxViewShell()->GetIPClient();
+ if ( !pIPClient )
+ return false;
+
+ bool bRet = pIPClient->IsObjectInPlaceActive();
+ if( bRet )
+ {
+ if( CNT_OLE == GetCntType() )
+ ClearAutomaticContour();
+
+ if( static_cast<SwOleClient*>(pIPClient)->IsCheckForOLEInCaption() !=
+ IsCheckForOLEInCaption() )
+ SetCheckForOLEInCaption( !IsCheckForOLEInCaption() );
+
+ // enable update of the link preview
+ comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = GetDoc()->GetDocShell()->getEmbeddedObjectContainer();
+ const bool aUserAllowsLinkUpdate = rEmbeddedObjectContainer.getUserAllowsLinkUpdate();
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true);
+
+ // leave UIActive state
+ pIPClient->DeactivateObject();
+
+ // if we have more than one link let's update them too
+ sfx2::LinkManager& rLinkManager = GetDoc()->getIDocumentLinksAdministration().GetLinkManager();
+ if (rLinkManager.GetLinks().size() > 1)
+ rLinkManager.UpdateAllLinks(false, false, nullptr);
+
+ // return back original value of the "update of the link preview" flag
+ rEmbeddedObjectContainer.setUserAllowsLinkUpdate(aUserAllowsLinkUpdate);
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/feshview.cxx b/sw/source/core/frmedt/feshview.cxx
new file mode 100644
index 000000000..8369004ac
--- /dev/null
+++ b/sw/source/core/frmedt/feshview.cxx
@@ -0,0 +1,3360 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <svx/strings.hrc>
+#include <svx/sdrobjectfilter.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/sxciaitm.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/protitem.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/dialmgr.hxx>
+#include <tools/globname.hxx>
+#include <sot/exchange.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <drawdoc.hxx>
+#include <textboxhelper.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtcnct.hxx>
+#include <swmodule.hxx>
+#include <fesh.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <sectfrm.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <dview.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <viewimp.hxx>
+#include <flyfrm.hxx>
+#include <pam.hxx>
+#include <ndole.hxx>
+#include <ndgrf.hxx>
+#include <ndtxt.hxx>
+#include <viewopt.hxx>
+#include <swundo.hxx>
+#include <notxtfrm.hxx>
+#include <txtfrm.hxx>
+#include <mdiexp.hxx>
+#include <sortedobjs.hxx>
+#include <HandleAnchorNodeChg.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <calbck.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/svxids.hrc>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <svx/srchdlg.hxx>
+
+#define SCROLLVAL 75
+
+using namespace com::sun::star;
+
+/**
+ * set line starts and ends for the object to be created
+ */
+
+namespace {
+
+::basegfx::B2DPolyPolygon getPolygon(TranslateId pResId, const SdrModel& rModel)
+{
+ ::basegfx::B2DPolyPolygon aRetval;
+ XLineEndListRef pLineEndList(rModel.GetLineEndList());
+
+ if( pLineEndList.is() )
+ {
+ OUString aArrowName( SvxResId(pResId) );
+ tools::Long nCount = pLineEndList->Count();
+ tools::Long nIndex;
+ for( nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ const XLineEndEntry* pEntry = pLineEndList->GetLineEnd(nIndex);
+ if( pEntry->GetName() == aArrowName )
+ {
+ aRetval = pEntry->GetLineEnd();
+ break;
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+}
+
+SwFlyFrame *GetFlyFromMarked( const SdrMarkList *pLst, SwViewShell *pSh )
+{
+ if ( !pLst )
+ pLst = pSh->HasDrawView() ? &pSh->Imp()->GetDrawView()->GetMarkedObjectList():nullptr;
+
+ if ( pLst && pLst->GetMarkCount() == 1 )
+ {
+ SdrObject *pO = pLst->GetMark( 0 )->GetMarkedSdrObj();
+ if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pO))
+ return pVirtO->GetFlyFrame();
+ }
+ return nullptr;
+}
+
+static void lcl_GrabCursor( SwFEShell* pSh, SwFlyFrame* pOldSelFly)
+{
+ const SwFrameFormat *pFlyFormat = pSh->SelFlyGrabCursor();
+ if( pFlyFormat && !pSh->ActionPend() &&
+ (!pOldSelFly || pOldSelFly->GetFormat() != pFlyFormat) )
+ {
+ // now call set macro if applicable
+ pSh->GetFlyMacroLnk().Call( static_cast<const SwFlyFrameFormat*>(pFlyFormat) );
+ // if a dialog was started inside a macro, then
+ // MouseButtonUp arrives at macro and not to us. Therefore
+ // flag is always set here and will never be switched to
+ // respective Shell !!!!!!!
+
+ g_bNoInterrupt = false;
+ }
+ else if( !pFlyFormat || RES_DRAWFRMFMT == pFlyFormat->Which() )
+ {
+ // --> assure consistent cursor
+ pSh->KillPams();
+ pSh->ClearMark();
+ pSh->SetCursor( pSh->Imp()->GetDrawView()->GetAllMarkedRect().TopLeft(), true);
+ }
+}
+
+bool SwFEShell::SelectObj( const Point& rPt, sal_uInt8 nFlag, SdrObject *pObj )
+{
+ SwDrawView *pDView = Imp()->GetDrawView();
+ if(!pDView)
+ return false;
+ CurrShell aCurr( this );
+ StartAction(); // action is necessary to assure only one AttrChgdNotify
+ // (e.g. due to Unmark->MarkListHasChgd) arrives
+
+ const SdrMarkList &rMrkList = pDView->GetMarkedObjectList();
+ const bool bHadSelection = rMrkList.GetMarkCount();
+ const bool bAddSelect = 0 != (SW_ADD_SELECT & nFlag);
+ const bool bEnterGroup = 0 != (SW_ENTER_GROUP & nFlag);
+ SwFlyFrame* pOldSelFly = nullptr;
+ const Point aOldPos( pDView->GetAllMarkedRect().TopLeft() );
+
+ if( bHadSelection )
+ {
+ // call Unmark when !bAddSelect or if fly was selected
+ bool bUnmark = !bAddSelect;
+
+ if ( rMrkList.GetMarkCount() == 1 )
+ {
+ // if fly was selected, deselect it first
+ pOldSelFly = ::GetFlyFromMarked( &rMrkList, this );
+ if ( pOldSelFly )
+ {
+ const sal_uInt16 nType = GetCntType();
+ if( nType != CNT_TXT || (SW_LEAVE_FRAME & nFlag) ||
+ ( pOldSelFly->GetFormat()->GetProtect().IsContentProtected()
+ && !IsReadOnlyAvailable() ))
+ {
+ // If a fly is deselected, which contains graphic, OLE or
+ // otherwise, the cursor should be removed from it.
+ // Similar if a fly with protected content is deselected.
+ // For simplicity we put the cursor next to the upper-left
+ // corner.
+ Point aPt( pOldSelFly->getFrameArea().Pos() );
+ aPt.setX(aPt.getX() - 1);
+ bool bUnLockView = !IsViewLocked();
+ LockView( true );
+ SetCursor( aPt, true );
+ if( bUnLockView )
+ LockView( false );
+ }
+ if ( nType & CNT_GRF &&
+ static_cast<SwNoTextFrame*>(pOldSelFly->Lower())->HasAnimation() )
+ {
+ GetWin()->Invalidate( pOldSelFly->getFrameArea().SVRect() );
+ }
+
+ // Cancel crop mode
+ if ( SdrDragMode::Crop == GetDragMode() )
+ SetDragMode( SdrDragMode::Move );
+
+ bUnmark = true;
+ }
+ }
+ if ( bUnmark )
+ {
+ pDView->UnmarkAll();
+ if (pOldSelFly)
+ pOldSelFly->SelectionHasChanged(this);
+ }
+ }
+ else
+ {
+ KillPams();
+ ClearMark();
+ }
+
+ if ( pObj )
+ {
+ OSL_ENSURE( !bEnterGroup, "SW_ENTER_GROUP is not supported" );
+ pDView->MarkObj( pObj, Imp()->GetPageView() );
+ }
+ else
+ {
+ // tolerance limit of Drawing-SS
+ const auto nHdlSizePixel = Imp()->GetDrawView()->GetMarkHdlSizePixel();
+ const short nMinMove = static_cast<short>(GetOut()->PixelToLogic(Size(nHdlSizePixel/2, 0)).Width());
+ pDView->MarkObj( rPt, nMinMove, bAddSelect, bEnterGroup );
+ }
+
+ const bool bRet = 0 != rMrkList.GetMarkCount();
+
+ if ( rMrkList.GetMarkCount() > 1 )
+ {
+ // It sucks if Drawing objects were selected and now
+ // additionally a fly is selected.
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pTmpObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ bool bForget = dynamic_cast<const SwVirtFlyDrawObj*>( pTmpObj) != nullptr;
+ if( bForget )
+ {
+ pDView->UnmarkAll();
+ pDView->MarkObj( pTmpObj, Imp()->GetPageView(), bAddSelect, bEnterGroup );
+ break;
+ }
+ }
+ }
+
+ if ( rMrkList.GetMarkCount() == 1 )
+ {
+ SwFlyFrame *pSelFly = ::GetFlyFromMarked( &rMrkList, this );
+ if (pSelFly)
+ pSelFly->SelectionHasChanged(this);
+ }
+
+ if (!(nFlag & SW_ALLOW_TEXTBOX))
+ {
+ // If the fly frame is a textbox of a shape, then select the shape instead.
+ for (size_t i = 0; i < rMrkList.GetMarkCount(); ++i)
+ {
+ SdrObject* pObject = rMrkList.GetMark(i)->GetMarkedSdrObj();
+ SwContact* pContact = GetUserCall(pObject);
+ if (!pContact)
+ {
+ continue;
+ }
+
+ SwFrameFormat* pFormat = pContact->GetFormat();
+ if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT))
+ {
+ SdrObject* pShape = pShapeFormat->FindSdrObject();
+ pDView->UnmarkAll();
+ pDView->MarkObj(pShape, Imp()->GetPageView(), bAddSelect, bEnterGroup);
+ break;
+ }
+ }
+ }
+
+ if ( bRet )
+ {
+ ::lcl_GrabCursor(this, pOldSelFly);
+ if ( GetCntType() & CNT_GRF )
+ {
+ const SwFlyFrame *pTmp = GetFlyFromMarked( &rMrkList, this );
+ OSL_ENSURE( pTmp, "Graphic without Fly" );
+ if ( pTmp && static_cast<const SwNoTextFrame*>(pTmp->Lower())->HasAnimation() )
+ static_cast<const SwNoTextFrame*>(pTmp->Lower())->StopAnimation( GetOut() );
+ }
+ }
+ else if ( !pOldSelFly && bHadSelection )
+ SetCursor( aOldPos, true);
+
+ if( bRet || !bHadSelection )
+ CallChgLnk();
+
+ // update status line
+ ::FrameNotify( this, bRet ? FLY_DRAG_START : FLY_DRAG_END );
+
+ EndAction();
+ return bRet;
+}
+
+/*
+ * Description: MoveAnchor( nDir ) looked for an another Anchor for
+ * the selected drawing object (or fly frame) in the given direction.
+ * An object "as character" doesn't moves anyway.
+ * A page bounded object could move to the previous/next page with up/down,
+ * an object bounded "at paragraph" moves to the previous/next paragraph, too.
+ * An object bounded "at character" moves to the previous/next paragraph
+ * with up/down and to the previous/next character with left/right.
+ * If the anchor for at paragraph/character bounded objects has vertical or
+ * right_to_left text direction, the directions for up/down/left/right will
+ * interpreted accordingly.
+ * An object bounded "at fly" takes the center of the actual anchor and looks
+ * for the nearest fly frame in the given direction.
+ */
+
+static bool LessX( Point const & aPt1, Point const & aPt2, bool bOld )
+{
+ return aPt1.getX() < aPt2.getX()
+ || ( aPt1.getX() == aPt2.getX()
+ && ( aPt1.getY() < aPt2.getY()
+ || ( aPt1.getY() == aPt2.getY() && bOld ) ) );
+}
+static bool LessY( Point const & aPt1, Point const & aPt2, bool bOld )
+{
+ return aPt1.getY() < aPt2.getY()
+ || ( aPt1.getY() == aPt2.getY()
+ && ( aPt1.getX() < aPt2.getX()
+ || ( aPt1.getX() == aPt2.getX() && bOld ) ) );
+}
+
+bool SwFEShell::MoveAnchor( SwMove nDir )
+{
+ if (!Imp()->GetDrawView())
+ return false;
+ const SdrMarkList& pMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if (1 != pMrkList.GetMarkCount())
+ return false;
+ SwFrame* pOld;
+ SwFlyFrame* pFly = nullptr;
+ SdrObject *pObj = pMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
+ {
+ pFly = pVirtO->GetFlyFrame();
+ pOld = pFly->AnchorFrame();
+ }
+ else
+ pOld = static_cast<SwDrawContact*>(GetUserCall(pObj))->GetAnchorFrame( pObj );
+ bool bRet = false;
+ if( pOld )
+ {
+ SwFrame* pNew = pOld;
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj );
+ SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ SwFormatAnchor aAnch( rFormat.GetAnchor() );
+ RndStdIds nAnchorId = aAnch.GetAnchorId();
+ if ( RndStdIds::FLY_AS_CHAR == nAnchorId )
+ return false;
+ if( pOld->IsVertical() )
+ {
+ if( pOld->IsTextFrame() )
+ {
+ switch( nDir ) {
+ case SwMove::UP: nDir = SwMove::LEFT; break;
+ case SwMove::DOWN: nDir = SwMove::RIGHT; break;
+ case SwMove::LEFT: nDir = SwMove::DOWN; break;
+ case SwMove::RIGHT: nDir = SwMove::UP; break;
+ }
+ if( pOld->IsRightToLeft() )
+ {
+ if( nDir == SwMove::LEFT )
+ nDir = SwMove::RIGHT;
+ else if( nDir == SwMove::RIGHT )
+ nDir = SwMove::LEFT;
+ }
+ }
+ }
+ switch ( nAnchorId ) {
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ OSL_ENSURE( pOld->IsPageFrame(), "Wrong anchor, page expected." );
+ if( SwMove::UP == nDir )
+ pNew = pOld->GetPrev();
+ else if( SwMove::DOWN == nDir )
+ pNew = pOld->GetNext();
+ if( pNew && pNew != pOld )
+ {
+ aAnch.SetPageNum( static_cast<SwPageFrame*>(pNew)->GetPhyPageNum() );
+ bRet = true;
+ }
+ break;
+ }
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ OSL_ENSURE(pOld->IsTextFrame(), "Wrong anchor, text frame expected.");
+ if( SwMove::LEFT == nDir || SwMove::RIGHT == nDir )
+ {
+ SwPosition pos = *aAnch.GetContentAnchor();
+ SwTextFrame *const pOldFrame(static_cast<SwTextFrame*>(pOld));
+ TextFrameIndex const nAct(pOldFrame->MapModelToViewPos(pos));
+ if( SwMove::LEFT == nDir )
+ {
+ bRet = true;
+ if( nAct )
+ {
+ pos = pOldFrame->MapViewToModelPos(nAct - TextFrameIndex(1));
+ }
+ else
+ nDir = SwMove::UP;
+ }
+ else
+ {
+ TextFrameIndex const nMax(pOldFrame->GetText().getLength());
+ if( nAct < nMax )
+ {
+ bRet = true;
+ pos = pOldFrame->MapViewToModelPos(nAct + TextFrameIndex(1));
+ }
+ else
+ nDir = SwMove::DOWN;
+ }
+ if( pos != *aAnch.GetContentAnchor())
+ aAnch.SetAnchor( &pos );
+ }
+ [[fallthrough]];
+ }
+ case RndStdIds::FLY_AT_PARA:
+ {
+ OSL_ENSURE(pOld->IsTextFrame(), "Wrong anchor, text frame expected.");
+ if( SwMove::UP == nDir )
+ pNew = pOld->FindPrev();
+ else if( SwMove::DOWN == nDir )
+ pNew = pOld->FindNext();
+ if( pNew && pNew != pOld && pNew->IsContentFrame() )
+ {
+ SwTextFrame *const pNewFrame(static_cast<SwTextFrame*>(pNew));
+ SwPosition const pos = pNewFrame->MapViewToModelPos(
+ TextFrameIndex(
+ (bRet && pNewFrame->GetText().getLength() != 0)
+ ? pNewFrame->GetText().getLength() - 1
+ : 0));
+ aAnch.SetAnchor( &pos );
+ bRet = true;
+ }
+ else if( SwMove::UP == nDir || SwMove::DOWN == nDir )
+ bRet = false;
+ break;
+ }
+ case RndStdIds::FLY_AT_FLY:
+ {
+ OSL_ENSURE( pOld->IsFlyFrame(), "Wrong anchor, fly frame expected.");
+ SwPageFrame* pPage = pOld->FindPageFrame();
+ OSL_ENSURE( pPage, "Where's my page?" );
+ SwFlyFrame* pNewFly = nullptr;
+ if( pPage->GetSortedObjs() )
+ {
+ bool bOld = false;
+ Point aCenter( pOld->getFrameArea().Left() + pOld->getFrameArea().Width()/2,
+ pOld->getFrameArea().Top() + pOld->getFrameArea().Height()/2 );
+ Point aBest;
+ for(SwAnchoredObject* pAnchObj : *pPage->GetSortedObjs())
+ {
+ if( auto pTmp = pAnchObj->DynCastFlyFrame() )
+ {
+ if( pTmp == pOld )
+ bOld = true;
+ else
+ {
+ const SwFlyFrame* pCheck = pFly ? pTmp : nullptr;
+ while( pCheck )
+ {
+ if( pCheck == pFly )
+ break;
+ const SwFrame *pNxt = pCheck->GetAnchorFrame();
+ pCheck = pNxt ? pNxt->FindFlyFrame() : nullptr;
+ }
+ if( pCheck || pTmp->IsProtected() )
+ continue;
+ Point aNew( pTmp->getFrameArea().Left() +
+ pTmp->getFrameArea().Width()/2,
+ pTmp->getFrameArea().Top() +
+ pTmp->getFrameArea().Height()/2 );
+ bool bAccept = false;
+ switch( nDir ) {
+ case SwMove::RIGHT:
+ {
+ bAccept = LessX( aCenter, aNew, bOld )
+ && ( !pNewFly ||
+ LessX( aNew, aBest, false ) );
+ break;
+ }
+ case SwMove::LEFT:
+ {
+ bAccept = LessX( aNew, aCenter, !bOld )
+ && ( !pNewFly ||
+ LessX( aBest, aNew, true ) );
+ break;
+ }
+ case SwMove::UP:
+ {
+ bAccept = LessY( aNew, aCenter, !bOld )
+ && ( !pNewFly ||
+ LessY( aBest, aNew, true ) );
+ break;
+ }
+ case SwMove::DOWN:
+ {
+ bAccept = LessY( aCenter, aNew, bOld )
+ && ( !pNewFly ||
+ LessY( aNew, aBest, false ) );
+ break;
+ }
+ }
+ if( bAccept )
+ {
+ pNewFly = pTmp;
+ aBest = aNew;
+ }
+ }
+ }
+ }
+ }
+
+ if( pNewFly )
+ {
+ SwPosition aPos( *pNewFly->GetFormat()->
+ GetContent().GetContentIdx());
+ aAnch.SetAnchor( &aPos );
+ bRet = true;
+ }
+ break;
+ }
+ default: break;
+ }
+ if( bRet )
+ {
+ StartAllAction();
+ // --> handle change of anchor node:
+ // if count of the anchor frame also change, the fly frames have to be
+ // re-created. Thus, delete all fly frames except the <this> before the
+ // anchor attribute is change and re-create them afterwards.
+ {
+ std::unique_ptr<SwHandleAnchorNodeChg> pHandleAnchorNodeChg;
+ SwFlyFrameFormat* pFlyFrameFormat( dynamic_cast<SwFlyFrameFormat*>(&rFormat) );
+ if ( pFlyFrameFormat )
+ {
+ pHandleAnchorNodeChg.reset(
+ new SwHandleAnchorNodeChg( *pFlyFrameFormat, aAnch ));
+ }
+ rFormat.GetDoc()->SetAttr( aAnch, rFormat );
+ }
+ // #i28701# - no call of method
+ // <CheckCharRectAndTopOfLine()> for to-character anchored
+ // Writer fly frame needed. This method call can cause a
+ // format of the anchor frame, which is no longer intended.
+ // Instead clear the anchor character rectangle and
+ // the top of line values for all to-character anchored objects.
+ pAnchoredObj->ClearCharRectAndTopOfLine();
+ EndAllAction();
+ }
+ }
+ return bRet;
+}
+
+const SdrMarkList* SwFEShell::GetMarkList_() const
+{
+ const SdrMarkList* pMarkList = nullptr;
+ if( Imp()->GetDrawView() != nullptr )
+ pMarkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ return pMarkList;
+}
+
+FrameTypeFlags SwFEShell::GetSelFrameType() const
+{
+ FrameTypeFlags eType;
+
+ // get marked frame list, and check if anything is selected
+ const SdrMarkList* pMarkList = GetMarkList_();
+ if( pMarkList == nullptr || pMarkList->GetMarkCount() == 0 )
+ eType = FrameTypeFlags::NONE;
+ else
+ {
+ // obtain marked item as fly frame; if no fly frame, it must
+ // be a draw object
+ const SwFlyFrame* pFly = ::GetFlyFromMarked(pMarkList, const_cast<SwFEShell*>(this));
+ if ( pFly != nullptr )
+ {
+ if( pFly->IsFlyLayFrame() )
+ eType = FrameTypeFlags::FLY_FREE;
+ else if( pFly->IsFlyAtContentFrame() )
+ eType = FrameTypeFlags::FLY_ATCNT;
+ else
+ {
+ OSL_ENSURE( pFly->IsFlyInContentFrame(), "New frametype?" );
+ eType = FrameTypeFlags::FLY_INCNT;
+ }
+ }
+ else
+ eType = FrameTypeFlags::DRAWOBJ;
+ }
+
+ return eType;
+}
+
+// does the draw selection contain a control?
+bool SwFEShell::IsSelContainsControl() const
+{
+ bool bRet = false;
+
+ // basically, copy the mechanism from GetSelFrameType(), but call
+ // CheckControl... if you get a drawing object
+ const SdrMarkList* pMarkList = GetMarkList_();
+ if( pMarkList != nullptr && pMarkList->GetMarkCount() == 1 )
+ {
+ // if we have one marked object, get the SdrObject and check
+ // whether it contains a control
+ const SdrObject* pSdrObject = pMarkList->GetMark( 0 )->GetMarkedSdrObj();
+ bRet = pSdrObject && ::CheckControlLayer( pSdrObject );
+ }
+ return bRet;
+}
+
+void SwFEShell::ScrollTo( const Point &rPt )
+{
+ const SwRect aRect( rPt, rPt );
+ if ( IsScrollMDI( this, aRect ) &&
+ (!Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ||
+ Imp()->IsDragPossible( rPt )) )
+ {
+ ScrollMDI( this, aRect, SCROLLVAL, SCROLLVAL );
+ }
+}
+
+void SwFEShell::SetDragMode( SdrDragMode eDragMode )
+{
+ if ( Imp()->HasDrawView() )
+ Imp()->GetDrawView()->SetDragMode( eDragMode );
+}
+
+SdrDragMode SwFEShell::GetDragMode() const
+{
+ SdrDragMode nRet = SdrDragMode(0);
+ if ( Imp()->HasDrawView() )
+ {
+ nRet = Imp()->GetDrawView()->GetDragMode();
+ }
+ return nRet;
+}
+
+void SwFEShell::StartCropImage()
+{
+ if ( !Imp()->HasDrawView() )
+ {
+ return;
+ }
+ SdrView *pView = Imp()->GetDrawView();
+ if (!pView) return;
+
+ const SdrMarkList &rMarkList = pView->GetMarkedObjectList();
+ if( 0 == rMarkList.GetMarkCount() ) {
+ // No object selected
+ return;
+ }
+
+ // If more than a single SwVirtFlyDrawObj is selected, select only the first SwVirtFlyDrawObj
+ if ( rMarkList.GetMarkCount() > 1 )
+ {
+ for ( size_t i = 0; i < rMarkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pTmpObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ bool bForget = dynamic_cast<const SwVirtFlyDrawObj*>( pTmpObj) != nullptr;
+ if( bForget )
+ {
+ pView->UnmarkAll();
+ pView->MarkObj( pTmpObj, Imp()->GetPageView() );
+ break;
+ }
+ }
+ }
+
+ // Activate CROP mode
+ pView->SetEditMode( SdrViewEditMode::Edit );
+ SetDragMode( SdrDragMode::Crop );
+}
+
+void SwFEShell::BeginDrag( const Point* pPt, bool bIsShift)
+{
+ SdrView *pView = Imp()->GetDrawView();
+ if ( pView && pView->AreObjectsMarked() )
+ {
+ m_pChainFrom.reset();
+ m_pChainTo.reset();
+ SdrHdl* pHdl = pView->PickHandle( *pPt );
+ if (pView->BegDragObj( *pPt, nullptr, pHdl ))
+ pView->GetDragMethod()->SetShiftPressed( bIsShift );
+ ::FrameNotify( this );
+ }
+}
+
+void SwFEShell::Drag( const Point *pPt, bool )
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "Drag without DrawView?" );
+ if ( HasDrawViewDrag() )
+ {
+ ScrollTo( *pPt );
+ Imp()->GetDrawView()->MovDragObj( *pPt );
+ Imp()->GetDrawView()->ShowDragAnchor();
+ ::FrameNotify( this );
+ }
+}
+
+void SwFEShell::EndDrag()
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "EndDrag without DrawView?" );
+ SdrView *pView = Imp()->GetDrawView();
+ if ( !pView->IsDragObj() )
+ return;
+
+ for(SwViewShell& rSh : GetRingContainer())
+ rSh.StartAction();
+
+ StartUndo( SwUndoId::START );
+
+ // #50778# Bug during dragging: In StartAction a HideShowXor is called.
+ // In EndDragObj() this is reversed, for no reason and even wrong.
+ // To restore consistency we should bring up the Xor again.
+
+ // Reanimation from the hack #50778 to fix bug #97057
+ // May be not the best solution, but the one with lowest risc at the moment.
+ // pView->ShowShownXor( GetOut() );
+
+ pView->EndDragObj();
+
+ // DrawUndo on to flyframes are not stored
+ // The flys change the flag.
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(true);
+ ChgAnchor( RndStdIds::FLY_AT_PARA, true );
+
+ EndUndo( SwUndoId::END );
+
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ rSh.EndAction();
+ if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rSh) )
+ pCursorShell->CallChgLnk();
+ }
+
+ GetDoc()->getIDocumentState().SetModified();
+ ::FrameNotify( this );
+}
+
+void SwFEShell::BreakDrag()
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "BreakDrag without DrawView?" );
+ if( HasDrawViewDrag() )
+ Imp()->GetDrawView()->BrkDragObj();
+ SetChainMarker();
+}
+
+// If a fly is selected, pulls the crsr in the first ContentFrame
+const SwFrameFormat* SwFEShell::SelFlyGrabCursor()
+{
+ if ( Imp()->HasDrawView() )
+ {
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ SwFlyFrame *pFly = ::GetFlyFromMarked( &rMrkList, this );
+
+ if( pFly )
+ {
+ SwContentFrame *pCFrame = pFly->ContainsContent();
+ if ( pCFrame )
+ {
+ // --> assure, that the cursor is consistent.
+ KillPams();
+ ClearMark();
+ SwPaM *pCursor = GetCursor();
+
+ if (pCFrame->IsTextFrame())
+ {
+ *pCursor->GetPoint() = static_cast<SwTextFrame *>(pCFrame)
+ ->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ assert(pCFrame->IsNoTextFrame());
+ SwContentNode *const pCNode = static_cast<SwNoTextFrame *>(pCFrame)->GetNode();
+ pCursor->GetPoint()->nNode = *pCNode;
+ pCursor->GetPoint()->nContent.Assign( pCNode, 0 );
+ }
+
+ SwRect& rChrRect = const_cast<SwRect&>(GetCharRect());
+ rChrRect = pFly->getFramePrintArea();
+ rChrRect.Pos() += pFly->getFrameArea().Pos();
+ GetCursorDocPos() = rChrRect.Pos();
+ }
+ return pFly->GetFormat();
+ }
+ }
+ return nullptr;
+}
+
+// Selection to above/below (Z-Order)
+static void lcl_NotifyNeighbours( const SdrMarkList *pLst )
+{
+ // Rules for evasion have changed.
+ // 1. The environment of the fly and everything inside should be notified
+ // 2. The content of the frame itself has to be notified
+ // 3. Frames displaced by the frame have to be notified
+ // 4. Also Drawing objects can displace frames
+ for( size_t j = 0; j < pLst->GetMarkCount(); ++j )
+ {
+ SwPageFrame *pPage;
+ bool bCheckNeighbours = false;
+ sal_Int16 aHori = text::HoriOrientation::NONE;
+ SwRect aRect;
+ SdrObject *pO = pLst->GetMark( j )->GetMarkedSdrObj();
+ if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pO))
+ {
+ SwFlyFrame *pFly = pVirtO->GetFlyFrame();
+
+ const SwFormatHoriOrient &rHori = pFly->GetFormat()->GetHoriOrient();
+ aHori = rHori.GetHoriOrient();
+ if( text::HoriOrientation::NONE != aHori && text::HoriOrientation::CENTER != aHori &&
+ pFly->IsFlyAtContentFrame() )
+ {
+ bCheckNeighbours = true;
+ pFly->InvalidatePos();
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
+ aFrm.Pos().AdjustY(1 );
+ }
+
+ pPage = pFly->FindPageFrame();
+ aRect = pFly->getFrameArea();
+ }
+ else
+ {
+ SwFrame* pAnch = static_cast<SwDrawContact*>( GetUserCall(pO) )->GetAnchorFrame( pO );
+ if( !pAnch )
+ continue;
+ pPage = pAnch->FindPageFrame();
+ // #i68520# - naming changed
+ aRect = GetBoundRectOfAnchoredObj( pO );
+ }
+
+ const size_t nCount = pPage->GetSortedObjs() ? pPage->GetSortedObjs()->size() : 0;
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
+ SwFlyFrame* pAct = pAnchoredObj->DynCastFlyFrame();
+ if ( !pAct )
+ continue;
+
+ SwRect aTmpCalcPnt( pAct->getFramePrintArea() );
+ aTmpCalcPnt += pAct->getFrameArea().Pos();
+ if ( aRect.Overlaps( aTmpCalcPnt ) )
+ {
+ SwContentFrame *pCnt = pAct->ContainsContent();
+ while ( pCnt )
+ {
+ aTmpCalcPnt = pCnt->getFramePrintArea();
+ aTmpCalcPnt += pCnt->getFrameArea().Pos();
+ if ( aRect.Overlaps( aTmpCalcPnt ) )
+ static_cast<SwFrame*>(pCnt)->Prepare( PrepareHint::FlyFrameAttributesChanged );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ if ( bCheckNeighbours && pAct->IsFlyAtContentFrame() )
+ {
+ const SwFormatHoriOrient &rH = pAct->GetFormat()->GetHoriOrient();
+ if ( rH.GetHoriOrient() == aHori &&
+ pAct->getFrameArea().Top() <= aRect.Bottom() &&
+ pAct->getFrameArea().Bottom() >= aRect.Top() )
+ {
+ pAct->InvalidatePos();
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pAct);
+ aFrm.Pos().AdjustY(1 );
+ }
+ }
+ }
+ }
+}
+
+void SwFEShell::SetLineEnds(SfxItemSet& rAttr, SdrObject const & rObj, sal_uInt16 nSlotId)
+{
+ SdrModel& rModel(rObj.getSdrModelFromSdrObject());
+
+ if ( !(nSlotId == SID_LINE_ARROW_START ||
+ nSlotId == SID_LINE_ARROW_END ||
+ nSlotId == SID_LINE_ARROWS ||
+ nSlotId == SID_LINE_ARROW_CIRCLE ||
+ nSlotId == SID_LINE_CIRCLE_ARROW ||
+ nSlotId == SID_LINE_ARROW_SQUARE ||
+ nSlotId == SID_LINE_SQUARE_ARROW ||
+ nSlotId == SID_DRAW_MEASURELINE) )
+ return;
+
+ // set attributes of line start and ends
+
+ // arrowhead
+ ::basegfx::B2DPolyPolygon aArrow( getPolygon( RID_SVXSTR_ARROW, rModel ) );
+ if( !aArrow.count() )
+ {
+ ::basegfx::B2DPolygon aNewArrow;
+ aNewArrow.append(::basegfx::B2DPoint(10.0, 0.0));
+ aNewArrow.append(::basegfx::B2DPoint(0.0, 30.0));
+ aNewArrow.append(::basegfx::B2DPoint(20.0, 30.0));
+ aNewArrow.setClosed(true);
+ aArrow.append(aNewArrow);
+ }
+
+ // Circles
+ ::basegfx::B2DPolyPolygon aCircle( getPolygon( RID_SVXSTR_CIRCLE, rModel ) );
+ if( !aCircle.count() )
+ {
+ ::basegfx::B2DPolygon aNewCircle = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 250.0, 250.0);
+ aNewCircle.setClosed(true);
+ aCircle.append(aNewCircle);
+ }
+
+ // Square
+ ::basegfx::B2DPolyPolygon aSquare( getPolygon( RID_SVXSTR_SQUARE, rModel ) );
+ if( !aSquare.count() )
+ {
+ ::basegfx::B2DPolygon aNewSquare;
+ aNewSquare.append(::basegfx::B2DPoint(0.0, 0.0));
+ aNewSquare.append(::basegfx::B2DPoint(10.0, 0.0));
+ aNewSquare.append(::basegfx::B2DPoint(10.0, 10.0));
+ aNewSquare.append(::basegfx::B2DPoint(0.0, 10.0));
+ aNewSquare.setClosed(true);
+ aSquare.append(aNewSquare);
+ }
+
+ SfxItemSet aSet( rModel.GetItemPool() );
+ tools::Long nWidth = 100; // (1/100th mm)
+
+ // determine line width and calculate with it the line end width
+ if( aSet.GetItemState( XATTR_LINEWIDTH ) != SfxItemState::DONTCARE )
+ {
+ tools::Long nValue = aSet.Get( XATTR_LINEWIDTH ).GetValue();
+ if( nValue > 0 )
+ nWidth = nValue * 3;
+ }
+
+ switch (nSlotId)
+ {
+ case SID_LINE_ARROWS:
+ case SID_DRAW_MEASURELINE:
+ {
+ // connector with arrow ends
+ rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow));
+ rAttr.Put(XLineStartWidthItem(nWidth));
+ rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow));
+ rAttr.Put(XLineEndWidthItem(nWidth));
+ }
+ break;
+
+ case SID_LINE_ARROW_START:
+ case SID_LINE_ARROW_CIRCLE:
+ case SID_LINE_ARROW_SQUARE:
+ {
+ // connector with arrow start
+ rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow));
+ rAttr.Put(XLineStartWidthItem(nWidth));
+ }
+ break;
+
+ case SID_LINE_ARROW_END:
+ case SID_LINE_CIRCLE_ARROW:
+ case SID_LINE_SQUARE_ARROW:
+ {
+ // connector with arrow end
+ rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow));
+ rAttr.Put(XLineEndWidthItem(nWidth));
+ }
+ break;
+ }
+
+ // and again, for the still missing ends
+ switch (nSlotId)
+ {
+ case SID_LINE_ARROW_CIRCLE:
+ {
+ // circle end
+ rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle));
+ rAttr.Put(XLineEndWidthItem(nWidth));
+ }
+ break;
+
+ case SID_LINE_CIRCLE_ARROW:
+ {
+ // circle start
+ rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle));
+ rAttr.Put(XLineStartWidthItem(nWidth));
+ }
+ break;
+
+ case SID_LINE_ARROW_SQUARE:
+ {
+ // square end
+ rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_SQUARE), aSquare));
+ rAttr.Put(XLineEndWidthItem(nWidth));
+ }
+ break;
+
+ case SID_LINE_SQUARE_ARROW:
+ {
+ // square start
+ rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_SQUARE), aSquare));
+ rAttr.Put(XLineStartWidthItem(nWidth));
+ }
+ break;
+ }
+
+}
+
+void SwFEShell::SelectionToTop( bool bTop )
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "SelectionToTop without DrawView?" );
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ OSL_ENSURE( rMrkList.GetMarkCount(), "No object selected." );
+
+ SwFlyFrame *pFly = ::GetFlyFromMarked( &rMrkList, this );
+ if ( pFly && pFly->IsFlyInContentFrame() )
+ return;
+
+ StartAllAction();
+ if ( bTop )
+ Imp()->GetDrawView()->PutMarkedToTop();
+ else
+ Imp()->GetDrawView()->MovMarkedToTop();
+ ::lcl_NotifyNeighbours( &rMrkList );
+
+ // Does the selection contain a textbox?
+ for (size_t i = 0; i < rMrkList.GetMarkCount(); i++)
+ if (auto pObj = rMrkList.GetMark(i)->GetMarkedSdrObj())
+ // Get the textbox-shape
+ if (auto pFormat = FindFrameFormat(pObj))
+ {
+ // If it has not textframe skip...
+ if (!SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj))
+ continue;
+ // If it has a textframe so it is a textbox, get its page
+ if (auto pDrwModel
+ = pFormat->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel())
+ // Not really understood why everything is on page 0...
+ // but it is easier to handle sdrobjects, that's true
+ if (auto pPage = pDrwModel->GetPage(0))
+ {
+ // nShift: it means how many layers the pObj have to be shifted up,
+ // in order not to interfere with other shapes and textboxes.
+ // Situations:
+ // - The next shape has textframe: This shape have to shifted with
+ // two layers.
+ // - The next shape has not got textframe: This shape have to be
+ // shifted only one layer up.
+ // - The next shape is null:
+ // - This shape is already at heaven: Only the textframe have
+ // to be adjusted.
+ sal_uInt32 nShift = 0;
+ // Get the one level higher object (note: can be nullptr!)
+ const auto pNextObj = pPage->SetObjectOrdNum(pObj->GetOrdNum() + 1, pObj->GetOrdNum() + 1);
+ // If there is a higher object (not null)...
+ if (pNextObj)
+ {
+ // One level shift is necessary
+ nShift++;
+ // If this object is a textbox, two level increasing needed
+ // (one for the shape and one for the frame)
+ if (auto pNextFormat = FindFrameFormat(pNextObj))
+ if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT, pNextObj)
+ || SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT))
+ nShift++;
+ }
+ // Set the new z-order.
+ pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() + nShift);
+ }
+ // The shape is on the right level, correct the layer of the frame
+ SwTextBoxHelper::DoTextBoxZOrderCorrection(pFormat, pObj);
+ }
+
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+}
+
+void SwFEShell::SelectionToBottom( bool bBottom )
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "SelectionToBottom without DrawView?" );
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ OSL_ENSURE( rMrkList.GetMarkCount(), "No object selected." );
+
+ SwFlyFrame *pFly = ::GetFlyFromMarked( &rMrkList, this );
+ if ( pFly && pFly->IsFlyInContentFrame() )
+ return;
+
+ StartAllAction();
+ if ( bBottom )
+ Imp()->GetDrawView()->PutMarkedToBtm();
+ else
+ Imp()->GetDrawView()->MovMarkedToBtm();
+ ::lcl_NotifyNeighbours( &rMrkList );
+
+ // If the selection has textbox
+ for(size_t i = 0; i < rMrkList.GetMarkCount(); i++)
+ if (auto pObj = rMrkList.GetMark(i)->GetMarkedSdrObj())
+ // Get the shape of the textbox
+ if (auto pFormat = FindFrameFormat(pObj))
+ {
+ // If the shape has not textframes skip.
+ if (!SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT, pObj))
+ continue;
+ // If has, move the shape to correct level with...
+ if (auto pDrwModel
+ = pFormat->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel())
+ if (auto pPage = pDrwModel->GetPage(0))
+ {
+ const auto pNextObj = pPage->SetObjectOrdNum(pObj->GetOrdNum() - 1, pObj->GetOrdNum() - 1);
+ // If there is a lower object (not null)...
+ if (pNextObj)
+ {
+ // If the lower has no textframe, just do nothing, else move by one lower
+ if (auto pNextFormat = FindFrameFormat(pNextObj))
+ if (SwTextBoxHelper::isTextBox(pNextFormat, RES_DRAWFRMFMT, pNextObj)
+ || SwTextBoxHelper::isTextBox(pNextFormat, RES_FLYFRMFMT))
+ pPage->SetObjectOrdNum(pObj->GetOrdNum(), pObj->GetOrdNum() - 1);
+ }
+ }
+ // And set correct layer for the selected textbox.
+ SwTextBoxHelper::DoTextBoxZOrderCorrection(pFormat, pObj);
+ }
+
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllAction();
+}
+
+// Object above/below the document? 2 Controls, 1 Heaven, 0 Hell,
+// SDRLAYER_NOTFOUND Ambiguous
+SdrLayerID SwFEShell::GetLayerId() const
+{
+ if ( !Imp()->HasDrawView() )
+ return SDRLAYER_NOTFOUND;
+
+ SdrLayerID nRet = SDRLAYER_NOTFOUND;
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ const SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if( !pObj )
+ continue;
+ if ( nRet == SDRLAYER_NOTFOUND )
+ nRet = pObj->GetLayer();
+ else if ( nRet != pObj->GetLayer() )
+ {
+ return SDRLAYER_NOTFOUND;
+ }
+ }
+ return nRet;
+}
+
+// Object above/below the document
+// Note: only visible objects can be marked. Thus, objects with invisible
+// layer IDs have not to be considered.
+// If <SwFEShell> exists, layout exists!!
+void SwFEShell::ChangeOpaque( SdrLayerID nLayerId )
+{
+ if ( !Imp()->HasDrawView() )
+ return;
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess();
+ // correct type of <nControls>
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if( !pObj )
+ continue;
+ // or group objects containing controls.
+ // --> #i113730#
+ // consider that a member of a drawing group has been selected.
+ const SwContact* pContact = ::GetUserCall( pObj );
+ OSL_ENSURE( pContact && pContact->GetMaster(), "<SwFEShell::ChangeOpaque(..)> - missing contact or missing master object at contact!" );
+ const bool bControlObj = ( pContact && pContact->GetMaster() )
+ ? ::CheckControlLayer( pContact->GetMaster() )
+ : ::CheckControlLayer( pObj );
+ if ( !bControlObj && pObj->GetLayer() != nLayerId )
+ {
+ pObj->SetLayer( nLayerId );
+ InvalidateWindows( SwRect( pObj->GetCurrentBoundRect() ) );
+ if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
+ {
+ SwFormat *pFormat = pVirtO->GetFlyFrame()->GetFormat();
+ SvxOpaqueItem aOpa( pFormat->GetOpaque() );
+ aOpa.SetValue( nLayerId == rIDDMA.GetHellId() );
+ pFormat->SetFormatAttr( aOpa );
+ // If pObj has textframe, put its textframe to the right level
+ if (auto pTextBx = FindFrameFormat(pObj))
+ SwTextBoxHelper::DoTextBoxZOrderCorrection(pTextBx, pObj);
+ }
+ }
+ }
+ GetDoc()->getIDocumentState().SetModified();
+}
+
+void SwFEShell::SelectionToHeaven()
+{
+ ChangeOpaque( getIDocumentDrawModelAccess().GetHeavenId() );
+}
+
+void SwFEShell::SelectionToHell()
+{
+ ChangeOpaque( getIDocumentDrawModelAccess().GetHellId() );
+}
+
+size_t SwFEShell::IsObjSelected() const
+{
+ if ( IsFrameSelected() || !Imp()->HasDrawView() )
+ return 0;
+
+ return Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount();
+}
+
+bool SwFEShell::IsFrameSelected() const
+{
+ if ( !Imp()->HasDrawView() )
+ return false;
+ else
+ return nullptr != ::GetFlyFromMarked( &Imp()->GetDrawView()->GetMarkedObjectList(),
+ const_cast<SwFEShell*>(this) );
+}
+
+bool SwFEShell::IsObjSelected( const SdrObject& rObj ) const
+{
+ if ( IsFrameSelected() || !Imp()->HasDrawView() )
+ return false;
+ else
+ return Imp()->GetDrawView()->IsObjMarked( &rObj );
+}
+
+bool SwFEShell::IsRotationOfSwGrfNodePossible() const
+{
+ // RotGrfFlyFrame: check if RotationMode is possible
+ const SdrView *pSdrView = Imp()->GetDrawView();
+
+ if(pSdrView)
+ {
+ const SdrMarkList& rList(pSdrView->GetMarkedObjectList());
+
+ if(1 == rList.GetMarkCount())
+ {
+ const SwVirtFlyDrawObj* pVirtFlyDraw(dynamic_cast< const SwVirtFlyDrawObj* >(rList.GetMark(0)->GetMarkedSdrObj()));
+
+ if(nullptr != pVirtFlyDraw)
+ {
+ return pVirtFlyDraw->ContainsSwGrfNode();
+ }
+ }
+ }
+
+ return false;
+}
+
+bool SwFEShell::IsObjSameLevelWithMarked(const SdrObject* pObj) const
+{
+ if (pObj)
+ {
+ const SdrMarkList& aMarkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if (aMarkList.GetMarkCount() == 0)
+ {
+ return true;
+ }
+ SdrMark* pM=aMarkList.GetMark(0);
+ if (pM)
+ {
+ SdrObject* pMarkObj = pM->GetMarkedSdrObj();
+ if (pMarkObj && pMarkObj->getParentSdrObjectFromSdrObject() == pObj->getParentSdrObjectFromSdrObject())
+ return true;
+ }
+ }
+ return false;
+}
+
+void SwFEShell::EndTextEdit()
+{
+ // Terminate the TextEditMode. If required (default if the object
+ // does not contain any more text and does not carry attributes) the object
+ // is deleted. All other objects marked are preserved.
+
+ OSL_ENSURE( Imp()->HasDrawView() && Imp()->GetDrawView()->IsTextEdit(),
+ "EndTextEdit a no Object" );
+
+ StartAllAction();
+ SdrView *pView = Imp()->GetDrawView();
+ SdrObject *pObj = pView->GetTextEditObject();
+ SdrObjUserCall* pUserCall = GetUserCall(pObj);
+ if( nullptr != pUserCall )
+ {
+ SdrObject *pTmp = static_cast<SwContact*>(pUserCall)->GetMaster();
+ if( !pTmp )
+ pTmp = pObj;
+ pUserCall->Changed( *pTmp, SdrUserCallType::Resize, pTmp->GetLastBoundRect() );
+ }
+ if ( !pObj->getParentSdrObjectFromSdrObject() )
+ {
+ if ( SdrEndTextEditKind::ShouldBeDeleted == pView->SdrEndTextEdit(true) )
+ {
+ if ( pView->GetMarkedObjectList().GetMarkCount() > 1 )
+ {
+ SdrMarkList aSave( pView->GetMarkedObjectList() );
+ aSave.DeleteMark( aSave.FindObject( pObj ) );
+ if ( aSave.GetMarkCount() )
+ {
+ pView->UnmarkAll();
+ pView->MarkObj( pObj, Imp()->GetPageView() );
+ }
+ DelSelectedObj();
+ for ( size_t i = 0; i < aSave.GetMarkCount(); ++i )
+ pView->MarkObj( aSave.GetMark( i )->GetMarkedSdrObj(), Imp()->GetPageView() );
+ }
+ else
+ DelSelectedObj();
+ }
+ }
+ else
+ pView->SdrEndTextEdit();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY");
+
+ EndAllAction();
+}
+
+bool SwFEShell::IsInsideSelectedObj( const Point &rPt )
+{
+ if( Imp()->HasDrawView() )
+ {
+ SwDrawView *pDView = Imp()->GetDrawView();
+
+ if( pDView->GetMarkedObjectList().GetMarkCount() &&
+ pDView->IsMarkedObjHit( rPt ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SwFEShell::IsObjSelectable( const Point& rPt )
+{
+ CurrShell aCurr(this);
+ SwDrawView *pDView = Imp()->GetDrawView();
+ bool bRet = false;
+ if( pDView )
+ {
+ SdrPageView* pPV;
+ const auto nOld = pDView->GetHitTolerancePixel();
+ pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 );
+
+ bRet = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMARKABLE) != nullptr;
+ pDView->SetHitTolerancePixel( nOld );
+ }
+ return bRet;
+}
+
+SdrObject* SwFEShell::GetObjAt( const Point& rPt )
+{
+ SdrObject* pRet = nullptr;
+ CurrShell aCurr(this);
+ SwDrawView *pDView = Imp()->GetDrawView();
+ if( pDView )
+ {
+ SdrPageView* pPV;
+ const auto nOld = pDView->GetHitTolerancePixel();
+ pDView->SetHitTolerancePixel( pDView->GetMarkHdlSizePixel()/2 );
+
+ pRet = pDView->PickObj(rPt, pDView->getHitTolLog(), pPV, SdrSearchOptions::PICKMARKABLE);
+ pDView->SetHitTolerancePixel( nOld );
+ }
+ return pRet;
+}
+
+// Test if there is an object at that position and if it should be selected.
+bool SwFEShell::ShouldObjectBeSelected(const Point& rPt)
+{
+ CurrShell aCurr(this);
+ SwDrawView *pDrawView = Imp()->GetDrawView();
+ bool bRet(false);
+
+ if(pDrawView)
+ {
+ SdrPageView* pPV;
+ const auto nOld(pDrawView->GetHitTolerancePixel());
+
+ pDrawView->SetHitTolerancePixel(pDrawView->GetMarkHdlSizePixel()/2);
+ SdrObject* pObj = pDrawView->PickObj(rPt, pDrawView->getHitTolLog(), pPV, SdrSearchOptions::PICKMARKABLE);
+ pDrawView->SetHitTolerancePixel(nOld);
+
+ if (pObj)
+ {
+ bRet = true;
+ const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess();
+ // #i89920#
+ // Do not select object in background which is overlapping this text
+ // at the given position.
+ bool bObjInBackground( false );
+ {
+ if ( pObj->GetLayer() == rIDDMA.GetHellId() )
+ {
+ const SwAnchoredObject* pAnchoredObj = ::GetUserCall( pObj )->GetAnchoredObj( pObj );
+ const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatSurround& rSurround = rFormat.GetSurround();
+ if ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH )
+ {
+ bObjInBackground = true;
+ }
+ }
+ }
+ if ( bObjInBackground )
+ {
+ const SwPageFrame* pPageFrame = GetLayout()->GetPageAtPos( rPt );
+ if( pPageFrame )
+ {
+ const SwContentFrame* pContentFrame( pPageFrame->ContainsContent() );
+ while ( pContentFrame )
+ {
+ if ( pContentFrame->UnionFrame().Contains( rPt ) )
+ {
+ const SwTextFrame* pTextFrame = pContentFrame->DynCastTextFrame();
+ if ( pTextFrame )
+ {
+ SwPosition aPos(GetDoc()->GetNodes());
+ Point aTmpPt( rPt );
+ if (pTextFrame->GetKeyCursorOfst(&aPos, aTmpPt))
+ {
+ SwRect aCursorCharRect;
+ if (pTextFrame->GetCharRect(aCursorCharRect,
+ aPos))
+ {
+ if ( aCursorCharRect.Overlaps( SwRect( pObj->GetLastBoundRect() ) ) )
+ {
+ bRet = false;
+ }
+ }
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ break;
+ }
+
+ pContentFrame = pContentFrame->GetNextContentFrame();
+ }
+ }
+ }
+
+ // Don't select header / footer objects in body edition and vice-versa
+ SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
+ if (pContact && !pContact->ObjAnchoredAtPage() )
+ {
+ const SwPosition& rPos = pContact->GetContentAnchor();
+ bool bInHdrFtr = GetDoc()->IsInHeaderFooter( rPos.nNode );
+ if (IsHeaderFooterEdit() != bInHdrFtr)
+ {
+ bRet = false;
+ }
+ }
+
+ if ( bRet )
+ {
+ const SdrPage* pPage = rIDDMA.GetDrawModel()->GetPage(0);
+ for(size_t a = pObj->GetOrdNum()+1; bRet && a < pPage->GetObjCount(); ++a)
+ {
+ SdrObject *pCandidate = pPage->GetObj(a);
+
+ SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pCandidate);
+ if (pDrawObj && pDrawObj->GetCurrentBoundRect().Contains(rPt))
+ {
+ bRet = false;
+ }
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+/*
+ * If an object was selected, we assume its upper-left corner
+ * otherwise the middle of the current CharRects.
+ * Does the object include a control or groups,
+ * which comprise only controls
+ */
+static bool lcl_IsControlGroup( const SdrObject *pObj )
+{
+ bool bRet = false;
+ if(dynamic_cast<const SdrUnoObj*>( pObj) != nullptr)
+ bRet = true;
+ else if( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
+ {
+ bRet = true;
+ const SdrObjList *pLst = pObjGroup->GetSubList();
+ for ( size_t i = 0; i < pLst->GetObjCount(); ++i )
+ if( !::lcl_IsControlGroup( pLst->GetObj( i ) ) )
+ return false;
+ }
+ return bRet;
+}
+
+namespace
+{
+ class MarkableObjectsOnly : public svx::ISdrObjectFilter
+ {
+ public:
+ explicit MarkableObjectsOnly( SdrPageView* i_pPV )
+ :m_pPV( i_pPV )
+ {
+ }
+
+ virtual bool includeObject( const SdrObject& i_rObject ) const override
+ {
+ return m_pPV && m_pPV->GetView().IsObjMarkable( &i_rObject, m_pPV );
+ }
+
+ private:
+ SdrPageView* m_pPV;
+ };
+}
+
+const SdrObject* SwFEShell::GetBestObject(bool bNext, GotoObjFlags eType, bool bFlat,
+ const svx::ISdrObjectFilter* pFilter,
+ bool* pbWrapped)
+{
+ if (pbWrapped)
+ *pbWrapped = false;
+
+ if( !Imp()->HasDrawView() )
+ return nullptr;
+
+ const SdrObject *pBest = nullptr,
+ *pTop = nullptr;
+
+ const tools::Long nTmp = bNext ? LONG_MAX : 0;
+ Point aBestPos( nTmp, nTmp );
+ Point aTopPos( nTmp, nTmp );
+ Point aCurPos;
+ Point aPos;
+ bool bNoDraw((GotoObjFlags::DrawAny & eType) == GotoObjFlags::NONE);
+ bool bNoFly((GotoObjFlags::FlyAny & eType) == GotoObjFlags::NONE);
+
+ if( !bNoFly && bNoDraw )
+ {
+ SwFlyFrame *pFly = GetCurrFrame( false )->FindFlyFrame();
+ if( pFly )
+ pBest = pFly->GetVirtDrawObj();
+ }
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ SdrPageView* pPV = Imp()->GetDrawView()->GetSdrPageView();
+
+ MarkableObjectsOnly aDefaultFilter( pPV );
+ if ( !pFilter )
+ pFilter = &aDefaultFilter;
+
+ if( !pBest || rMrkList.GetMarkCount() == 1 )
+ {
+ // Determine starting point
+ SdrObjList* pList = nullptr;
+ if ( rMrkList.GetMarkCount() )
+ {
+ const SdrObject* pStartObj = rMrkList.GetMark(0)->GetMarkedSdrObj();
+ if( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pStartObj) )
+ aPos = pVirtFlyDrawObj->GetFlyFrame()->getFrameArea().Pos();
+ else
+ aPos = pStartObj->GetSnapRect().TopLeft();
+
+ // If an object inside a group is selected, we want to
+ // iterate over the group members.
+ if ( ! pStartObj->GetUserCall() )
+ pList = pStartObj->getParentSdrObjListFromSdrObject();
+ }
+ else
+ {
+ // If no object is selected, we check if we just entered a group.
+ // In this case we want to iterate over the group members.
+ aPos = GetCharRect().Center();
+ const SdrObject* pStartObj = pPV ? pPV->GetCurrentGroup() : nullptr;
+ if ( dynamic_cast<const SdrObjGroup*>( pStartObj) )
+ pList = pStartObj->GetSubList();
+ }
+
+ if ( ! pList )
+ {
+ // Here we are if
+ // A No object has been selected and no group has been entered or
+ // B An object has been selected and it is not inside a group
+ pList = getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 );
+ }
+
+ OSL_ENSURE( pList, "No object list to iterate" );
+
+ SdrObjListIter aObjIter( pList, bFlat ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups );
+ while ( aObjIter.IsMore() )
+ {
+ SdrObject* pObj = aObjIter.Next();
+ SwVirtFlyDrawObj *pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
+ if( ( bNoFly && pVirtO ) ||
+ ( bNoDraw && !pVirtO ) ||
+ // Ignore TextBoxes of draw shapes here, so that
+ // SwFEShell::SelectObj() won't jump back on this list, meaning
+ // we never jump to the next draw shape.
+ (pVirtO && pVirtO->IsTextBox()) ||
+ ( eType == GotoObjFlags::DrawSimple && lcl_IsControlGroup( pObj ) ) ||
+ ( eType == GotoObjFlags::DrawControl && !lcl_IsControlGroup( pObj ) ) ||
+ !pFilter->includeObject( *pObj ) )
+ continue;
+ if (pVirtO)
+ {
+ SwFlyFrame *pFly = pVirtO->GetFlyFrame();
+ if( GotoObjFlags::FlyAny != ( GotoObjFlags::FlyAny & eType ) )
+ {
+ switch ( eType )
+ {
+ case GotoObjFlags::FlyFrame:
+ if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ continue;
+ break;
+ case GotoObjFlags::FlyGrf:
+ if ( pFly->Lower() &&
+ (!pFly->Lower()->IsNoTextFrame() ||
+ !static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetGrfNode()))
+ continue;
+ break;
+ case GotoObjFlags::FlyOLE:
+ if ( pFly->Lower() &&
+ (!pFly->Lower()->IsNoTextFrame() ||
+ !static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode()))
+ continue;
+ break;
+ default: break;
+ }
+ }
+ aCurPos = pFly->getFrameArea().Pos();
+ }
+ else
+ aCurPos = pObj->GetSnapRect().TopLeft();
+
+ // Special case if another object is on same Y.
+ if( aCurPos != aPos && // only when it is not me
+ aCurPos.getY() == aPos.getY() && // Y positions equal
+ (bNext? (aCurPos.getX() > aPos.getX()) : // lies next to me
+ (aCurPos.getX() < aPos.getX())) ) // " reverse
+ {
+ aBestPos = Point( nTmp, nTmp );
+ SdrObjListIter aTmpIter( pList, bFlat ? SdrIterMode::Flat : SdrIterMode::DeepNoGroups );
+ while ( aTmpIter.IsMore() )
+ {
+ SdrObject* pTmpObj = aTmpIter.Next();
+ pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pTmpObj);
+ if( ( bNoFly && pVirtO ) || ( bNoDraw && !pVirtO ) )
+ continue;
+ if (pVirtO)
+ {
+ aCurPos = pVirtO->GetFlyFrame()->getFrameArea().Pos();
+ }
+ else
+ aCurPos = pTmpObj->GetCurrentBoundRect().TopLeft();
+
+ if( aCurPos != aPos && aCurPos.Y() == aPos.Y() &&
+ (bNext? (aCurPos.getX() > aPos.getX()) : // lies next to me
+ (aCurPos.getX() < aPos.getX())) && // " reverse
+ (bNext? (aCurPos.getX() < aBestPos.getX()) : // better as best
+ (aCurPos.getX() > aBestPos.getX())) ) // " reverse
+ {
+ aBestPos = aCurPos;
+ pBest = pTmpObj;
+ }
+ }
+ break;
+ }
+
+ if( (
+ (bNext? (aPos.getY() < aCurPos.getY()) : // only below me
+ (aPos.getY() > aCurPos.getY())) && // " reverse
+ (bNext? (aBestPos.getY() > aCurPos.getY()) : // closer below
+ (aBestPos.getY() < aCurPos.getY()))
+ ) || // " reverse
+ (aBestPos.getY() == aCurPos.getY() &&
+ (bNext? (aBestPos.getX() > aCurPos.getX()) : // further left
+ (aBestPos.getX() < aCurPos.getX())))) // " reverse
+
+ {
+ aBestPos = aCurPos;
+ pBest = pObj;
+ }
+
+ if( (bNext? (aTopPos.getY() > aCurPos.getY()) : // higher as best
+ (aTopPos.getY() < aCurPos.getY())) || // " reverse
+ (aTopPos.getY() == aCurPos.getY() &&
+ (bNext? (aTopPos.getX() > aCurPos.getX()) : // further left
+ (aTopPos.getX() < aCurPos.getX())))) // " reverse
+ {
+ aTopPos = aCurPos;
+ pTop = pObj;
+ }
+ }
+ // unfortunately nothing found
+ if( bNext ? (aBestPos.getX() == LONG_MAX) : (aBestPos.getX() == 0) )
+ {
+ pBest = pTop;
+ if (pbWrapped && pBest)
+ *pbWrapped = true;
+ }
+ }
+
+ return pBest;
+}
+
+bool SwFEShell::GotoObj( bool bNext, GotoObjFlags eType )
+{
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
+
+ bool bWrapped(false);
+ const SdrObject* pBest = GetBestObject(bNext, eType, true, nullptr, &bWrapped);
+
+ if ( !pBest )
+ {
+ SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
+ return false;
+ }
+
+ const SwVirtFlyDrawObj *pVirtO = dynamic_cast<const SwVirtFlyDrawObj*>(pBest);
+ if (pVirtO)
+ {
+ const SwRect& rFrame = pVirtO->GetFlyFrame()->getFrameArea();
+ SelectObj( rFrame.Pos(), 0, const_cast<SdrObject*>(pBest) );
+ if( !ActionPend() )
+ MakeVisible( rFrame );
+ }
+ else
+ {
+ SelectObj( Point(), 0, const_cast<SdrObject*>(pBest) );
+ if( !ActionPend() )
+ MakeVisible( SwRect(pBest->GetCurrentBoundRect()) );
+ }
+ CallChgLnk();
+
+ if (bWrapped)
+ SvxSearchDialogWrapper::SetSearchLabel(bNext ? SearchLabel::EndWrapped :
+ SearchLabel::StartWrapped);
+
+ return true;
+}
+
+bool SwFEShell::BeginCreate( SdrObjKind eSdrObjectKind, const Point &rPos )
+{
+ bool bRet = false;
+
+ if ( !Imp()->HasDrawView() )
+ Imp()->MakeDrawView();
+
+ if ( GetPageNumber( rPos ) )
+ {
+ Imp()->GetDrawView()->SetCurrentObj( eSdrObjectKind );
+ if ( eSdrObjectKind == SdrObjKind::Caption )
+ bRet = Imp()->GetDrawView()->BegCreateCaptionObj(
+ rPos, Size( lMinBorder - MINFLY, lMinBorder - MINFLY ),
+ GetOut() );
+ else
+ bRet = Imp()->GetDrawView()->BegCreateObj( rPos, GetOut() );
+ }
+ if ( bRet )
+ {
+ ::FrameNotify( this, FLY_DRAG_START );
+ }
+ return bRet;
+}
+
+bool SwFEShell::BeginCreate( SdrObjKind eSdrObjectKind, SdrInventor eObjInventor,
+ const Point &rPos )
+{
+ bool bRet = false;
+
+ if ( !Imp()->HasDrawView() )
+ Imp()->MakeDrawView();
+
+ if ( GetPageNumber( rPos ) )
+ {
+ Imp()->GetDrawView()->SetCurrentObj( eSdrObjectKind, eObjInventor );
+ bRet = Imp()->GetDrawView()->BegCreateObj( rPos, GetOut() );
+ }
+ if ( bRet )
+ ::FrameNotify( this, FLY_DRAG_START );
+ return bRet;
+}
+
+void SwFEShell::MoveCreate( const Point &rPos )
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "MoveCreate without DrawView?" );
+ if ( GetPageNumber( rPos ) )
+ {
+ ScrollTo( rPos );
+ Imp()->GetDrawView()->MovCreateObj( rPos );
+ ::FrameNotify( this );
+ }
+}
+
+bool SwFEShell::EndCreate( SdrCreateCmd eSdrCreateCmd )
+{
+ // To assure undo-object from the DrawEngine is not stored,
+ // (we create our own undo-object!), temporarily switch-off Undo
+ OSL_ENSURE( Imp()->HasDrawView(), "EndCreate without DrawView?" );
+ if( !Imp()->GetDrawView()->IsGroupEntered() )
+ {
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
+ }
+ bool bCreate = Imp()->GetDrawView()->EndCreateObj( eSdrCreateCmd );
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(true);
+
+ if ( !bCreate )
+ {
+ ::FrameNotify( this, FLY_DRAG_END );
+ return false;
+ }
+
+ if ( eSdrCreateCmd == SdrCreateCmd::NextPoint )
+ {
+ ::FrameNotify( this );
+ return true;
+ }
+ return ImpEndCreate();
+}
+
+bool SwFEShell::ImpEndCreate()
+{
+ if (Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 0)
+ return false;
+
+ SdrObject& rSdrObj = *Imp()->GetDrawView()->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj();
+
+ if( rSdrObj.GetSnapRect().IsEmpty() )
+ {
+ // preferably we forget the object, only gives problems
+ Imp()->GetDrawView()->DeleteMarked();
+ Imp()->GetDrawView()->UnmarkAll();
+ ::FrameNotify( this, FLY_DRAG_END );
+ return false;
+ }
+
+ if( rSdrObj.getParentSdrObjectFromSdrObject() )
+ {
+ Point aTmpPos( rSdrObj.GetSnapRect().TopLeft() );
+ Point aNewAnchor( rSdrObj.getParentSdrObjectFromSdrObject()->GetAnchorPos() );
+ // OD 2004-04-05 #i26791# - direct object positioning for group members
+ rSdrObj.NbcSetRelativePos( aTmpPos - aNewAnchor );
+ rSdrObj.NbcSetAnchorPos( aNewAnchor );
+ ::FrameNotify( this );
+ return true;
+ }
+
+ LockPaint();
+ StartAllAction();
+
+ Imp()->GetDrawView()->UnmarkAll();
+
+ const tools::Rectangle &rBound = rSdrObj.GetSnapRect();
+ Point aPt( rBound.TopRight() );
+
+ // default for controls character bound, otherwise paragraph bound.
+ SwFormatAnchor aAnch;
+ const SwFrame *pAnch = nullptr;
+ bool bCharBound = false;
+ if( dynamic_cast<const SdrUnoObj*>( &rSdrObj) != nullptr )
+ {
+ SwPosition aPos( GetDoc()->GetNodes() );
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ Point aPoint( aPt.getX(), aPt.getY() + rBound.GetHeight()/2 );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+
+ // characterbinding not allowed in readonly-content
+ if( !aPos.nNode.GetNode().IsProtect() )
+ {
+ std::pair<Point, bool> const tmp(aPoint, true);
+ pAnch = aPos.nNode.GetNode().GetContentNode()->getLayoutFrame(GetLayout(), &aPos, &tmp);
+ SwRect aTmp;
+ pAnch->GetCharRect( aTmp, aPos );
+
+ // The crsr should not be too far away
+ bCharBound = true;
+ constexpr tools::Long constTwips_1cm = o3tl::toTwips(1, o3tl::Length::cm);
+ tools::Rectangle aRect( aTmp.SVRect() );
+ // Extend by 1 cm in each direction
+ aRect.AdjustLeft(-constTwips_1cm);
+ aRect.AdjustTop(-constTwips_1cm);
+ aRect.AdjustRight(constTwips_1cm);
+ aRect.AdjustBottom(constTwips_1cm);
+
+ if( !aRect.Overlaps( rBound ) && !::GetHtmlMode( GetDoc()->GetDocShell() ))
+ bCharBound = false;
+
+ // anchor in header/footer also not allowed.
+ if( bCharBound )
+ bCharBound = !GetDoc()->IsInHeaderFooter( aPos.nNode );
+
+ if( bCharBound )
+ {
+ aAnch.SetType( RndStdIds::FLY_AS_CHAR );
+ aAnch.SetAnchor( &aPos );
+ }
+ }
+ }
+
+ if( !bCharBound )
+ {
+ // allow native drawing objects in header/footer.
+ // Thus, set <bBodyOnly> to <false> for these objects using value
+ // of <nIdent> - value <0xFFFF> indicates control objects, which aren't
+ // allowed in header/footer.
+ //bool bBodyOnly = OBJ_NONE != nIdent;
+ bool bBodyOnly = SdrInventor::Default != rSdrObj.GetObjInventor();
+ bool bAtPage = false;
+ const SwFrame* pPage = nullptr;
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ Point aPoint( aPt );
+ SwPosition aPos( GetDoc()->GetNodes() );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPoint, &aState );
+
+ // do not set in ReadnOnly-content
+ if (aPos.nNode.GetNode().IsProtect())
+ {
+ // then only page bound. Or should we
+ // search the next not-readonly position?
+ bAtPage = true;
+ }
+
+ SwContentNode* pCNode = aPos.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(aPoint, false);
+ pAnch = pCNode ? pCNode->getLayoutFrame(GetLayout(), nullptr, &tmp) : nullptr;
+ if (!pAnch)
+ {
+ // Hidden content. Anchor to the page instead
+ bAtPage = true;
+ }
+
+ if( !bAtPage )
+ {
+ const SwFlyFrame *pTmp = pAnch->FindFlyFrame();
+ if( pTmp )
+ {
+ const SwFrame* pTmpFrame = pAnch;
+ SwRect aBound( rBound );
+ while( pTmp )
+ {
+ if( pTmp->getFrameArea().Contains( aBound ) )
+ {
+ if( !bBodyOnly || !pTmp->FindFooterOrHeader() )
+ pPage = pTmpFrame;
+ break;
+ }
+ pTmp = pTmp->GetAnchorFrame()
+ ? pTmp->GetAnchorFrame()->FindFlyFrame()
+ : nullptr;
+ pTmpFrame = pTmp;
+ }
+ }
+
+ if( !pPage )
+ pPage = pAnch->FindPageFrame();
+
+ // Always via FindAnchor, to assure the frame will be bound
+ // to the previous. With GetCrsOfst we can also reach the next. THIS IS WRONG.
+ pAnch = ::FindAnchor( pPage, aPt, bBodyOnly );
+ if (pAnch->IsTextFrame())
+ {
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ static_cast<SwTextFrame const*>(pAnch)->MapViewToModel(TextFrameIndex(0)));
+ aPos.nNode = *pos.first;
+ }
+ else
+ {
+ aPos.nNode = *static_cast<const SwNoTextFrame*>(pAnch)->GetNode();
+ }
+
+ // do not set in ReadnOnly-content
+ if( aPos.nNode.GetNode().IsProtect() )
+ // then only page bound. Or should we
+ // search the next not-readonly position?
+ bAtPage = true;
+ else
+ {
+ aAnch.SetType( RndStdIds::FLY_AT_PARA );
+ aAnch.SetAnchor( &aPos );
+ }
+ }
+
+ if( bAtPage )
+ {
+ pPage = pAnch ? pAnch->FindPageFrame() : GetLayout()->GetPageAtPos(aPoint);
+
+ aAnch.SetType( RndStdIds::FLY_AT_PAGE );
+ aAnch.SetPageNum( pPage->GetPhyPageNum() );
+ pAnch = pPage; // page becomes an anchor
+ }
+ }
+
+ SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE,
+ RES_SURROUND, RES_ANCHOR> aSet( GetDoc()->GetAttrPool() );
+ aSet.Put( aAnch );
+
+ // OD 2004-03-30 #i26791# - determine relative object position
+ SwTwips nXOffset;
+ SwTwips nYOffset = rBound.Top() - pAnch->getFrameArea().Top();
+ {
+ if( pAnch->IsVertical() )
+ {
+ nXOffset = nYOffset;
+ nYOffset = pAnch->getFrameArea().Left()+pAnch->getFrameArea().Width()-rBound.Right();
+ }
+ else if( pAnch->IsRightToLeft() )
+ nXOffset = pAnch->getFrameArea().Left()+pAnch->getFrameArea().Width()-rBound.Right();
+ else
+ nXOffset = rBound.Left() - pAnch->getFrameArea().Left();
+ if (pAnch->IsTextFrame())
+ {
+ const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnch);
+ if (pTmp->IsFollow())
+ {
+ do {
+ pTmp = pTmp->FindMaster();
+ OSL_ENSURE(pTmp, "Where's my Master?");
+ // OD 2004-03-30 #i26791# - correction: add frame area height
+ // of master frames.
+ nYOffset += pTmp->IsVertical() ?
+ pTmp->getFrameArea().Width() : pTmp->getFrameArea().Height();
+ } while (pTmp->IsFollow());
+ }
+
+ nYOffset -= pTmp->GetBaseVertOffsetForFly(false);
+ }
+ }
+
+ if( SdrInventor::Default == rSdrObj.GetObjInventor() && rSdrObj.GetObjIdentifier() == SdrObjKind::NONE )
+ {
+ // For OBJ_NONE a fly is inserted.
+ const tools::Long nWidth = rBound.Right() - rBound.Left();
+ const tools::Long nHeight= rBound.Bottom() - rBound.Top();
+ aSet.Put( SwFormatFrameSize( SwFrameSize::Minimum, std::max( nWidth, tools::Long(MINFLY) ),
+ std::max( nHeight, tools::Long(MINFLY) )));
+
+ SwFormatHoriOrient aHori( nXOffset, text::HoriOrientation::NONE, text::RelOrientation::FRAME );
+ SwFormatVertOrient aVert( nYOffset, text::VertOrientation::NONE, text::RelOrientation::FRAME );
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_PARALLEL ) );
+ aSet.Put( aHori );
+ aSet.Put( aVert );
+
+ // Quickly store the square
+ const SwRect aFlyRect( rBound );
+
+ // Throw away generated object, now the fly can nicely
+ // via the available SS be generated.
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false); // see above
+ // #i52858# - method name changed
+ SdrPage *pPg = getIDocumentDrawModelAccess().GetOrCreateDrawModel()->GetPage( 0 );
+ if( !pPg )
+ {
+ SdrModel* pTmpSdrModel = getIDocumentDrawModelAccess().GetDrawModel();
+ auto pNewPage = pTmpSdrModel->AllocPage( false );
+ pTmpSdrModel->InsertPage( pNewPage.get() );
+ pPg = pNewPage.get();
+ }
+ pPg->RecalcObjOrdNums();
+ SdrObject* pRemovedObject = pPg->RemoveObject( rSdrObj.GetOrdNumDirect() );
+ SdrObject::Free( pRemovedObject );
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(true);
+
+ SwFlyFrame* pFlyFrame;
+ if( NewFlyFrame( aSet, true ) &&
+ ::GetHtmlMode( GetDoc()->GetDocShell() ) &&
+ nullptr != ( pFlyFrame = GetSelectedFlyFrame() ))
+ {
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aHtmlSet( GetDoc()->GetAttrPool() );
+ // horizontal orientation:
+ const bool bLeftFrame = aFlyRect.Left() <
+ pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Left(),
+ bLeftPrt = aFlyRect.Left() + aFlyRect.Width() <
+ pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width()/2;
+ if( bLeftFrame || bLeftPrt )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::LEFT );
+ aHori.SetRelationOrient( bLeftFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA );
+ }
+ else
+ {
+ const bool bRightFrame = aFlyRect.Left() >
+ pAnch->getFrameArea().Left() + pAnch->getFramePrintArea().Width();
+ aHori.SetHoriOrient( text::HoriOrientation::RIGHT );
+ aHori.SetRelationOrient( bRightFrame ? text::RelOrientation::FRAME : text::RelOrientation::PRINT_AREA );
+ }
+ aHtmlSet.Put( aHori );
+ aVert.SetVertOrient( text::VertOrientation::TOP );
+ aVert.SetRelationOrient( text::RelOrientation::PRINT_AREA );
+ aHtmlSet.Put( aVert );
+
+ GetDoc()->SetAttr( aHtmlSet, *pFlyFrame->GetFormat() );
+ }
+ }
+ else
+ {
+ if (rSdrObj.GetName().isEmpty())
+ {
+ bool bRestore = GetDoc()->GetIDocumentUndoRedo().DoesDrawUndo();
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(false);
+ rSdrObj.MakeNameUnique();
+ GetDoc()->GetIDocumentUndoRedo().DoDrawUndo(bRestore);
+ }
+
+ aSet.Put( aAnch );
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) );
+ // OD 2004-03-30 #i26791# - set horizontal position
+ SwFormatHoriOrient aHori( nXOffset, text::HoriOrientation::NONE, text::RelOrientation::FRAME );
+ aSet.Put( aHori );
+ // OD 2004-03-30 #i26791# - set vertical position
+ if( pAnch->IsTextFrame() && static_cast<const SwTextFrame*>(pAnch)->IsFollow() )
+ {
+ const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnch);
+ do {
+ pTmp = pTmp->FindMaster();
+ assert(pTmp && "Where's my Master?");
+ nYOffset += pTmp->IsVertical() ?
+ pTmp->getFramePrintArea().Width() : pTmp->getFramePrintArea().Height();
+ } while ( pTmp->IsFollow() );
+ }
+ SwFormatVertOrient aVert( nYOffset, text::VertOrientation::NONE, text::RelOrientation::FRAME );
+ aSet.Put( aVert );
+ SwDrawFrameFormat* pFormat = static_cast<SwDrawFrameFormat*>(getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::DRAW_OBJECT, &aSet ));
+ // #i36010# - set layout direction of the position
+ pFormat->SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+ // #i44344#, #i44681# - positioning attributes already set
+ pFormat->PosAttrSet();
+ pFormat->SetName(rSdrObj.GetName());
+
+ SwDrawContact *pContact = new SwDrawContact( pFormat, &rSdrObj );
+ // #i35635#
+ pContact->MoveObjToVisibleLayer( &rSdrObj );
+ if( bCharBound )
+ {
+ OSL_ENSURE( aAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR, "wrong AnchorType" );
+ SwTextNode *pNd = aAnch.GetContentAnchor()->nNode.GetNode().GetTextNode();
+ SwFormatFlyCnt aFormat( pFormat );
+ pNd->InsertItem(aFormat,
+ aAnch.GetContentAnchor()->nContent.GetIndex(), 0 );
+ SwFormatVertOrient aVertical( pFormat->GetVertOrient() );
+ aVertical.SetVertOrient( text::VertOrientation::LINE_CENTER );
+ pFormat->SetFormatAttr( aVertical );
+ }
+ if( pAnch->IsTextFrame() && static_cast<const SwTextFrame*>(pAnch)->IsFollow() )
+ {
+ const SwTextFrame* pTmp = static_cast<const SwTextFrame*>(pAnch);
+ do {
+ pTmp = pTmp->FindMaster();
+ OSL_ENSURE( pTmp, "Where's my Master?" );
+ } while( pTmp->IsFollow() );
+ pAnch = pTmp;
+ }
+
+ pContact->ConnectToLayout();
+
+ // mark object at frame the object is inserted at.
+ {
+ SdrObject* pMarkObj = pContact->GetDrawObjectByAnchorFrame( *pAnch );
+ if ( pMarkObj )
+ {
+ Imp()->GetDrawView()->MarkObj( pMarkObj, Imp()->GetPageView() );
+ }
+ else
+ {
+ Imp()->GetDrawView()->MarkObj( &rSdrObj, Imp()->GetPageView() );
+ }
+ }
+ }
+
+ GetDoc()->getIDocumentState().SetModified();
+
+ KillPams();
+ EndAllActionAndCall();
+ UnlockPaint();
+ return true;
+}
+
+void SwFEShell::BreakCreate()
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "BreakCreate without DrawView?" );
+ Imp()->GetDrawView()->BrkCreateObj();
+ ::FrameNotify( this, FLY_DRAG_END );
+}
+
+bool SwFEShell::IsDrawCreate() const
+{
+ return Imp()->HasDrawView() && Imp()->GetDrawView()->IsCreateObj();
+}
+
+bool SwFEShell::BeginMark( const Point &rPos )
+{
+ if ( !Imp()->HasDrawView() )
+ Imp()->MakeDrawView();
+
+ if ( GetPageNumber( rPos ) )
+ {
+ SwDrawView* pDView = Imp()->GetDrawView();
+
+ if (pDView->HasMarkablePoints())
+ return pDView->BegMarkPoints( rPos );
+ else
+ {
+ pDView->BegMarkObj( rPos );
+ return true;
+ }
+ }
+ else
+ return false;
+}
+
+void SwFEShell::MoveMark( const Point &rPos )
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "MoveMark without DrawView?" );
+
+ if ( GetPageNumber( rPos ) )
+ {
+ ScrollTo( rPos );
+ SwDrawView* pDView = Imp()->GetDrawView();
+
+ if (pDView->IsInsObjPoint())
+ pDView->MovInsObjPoint( rPos );
+ else if (pDView->IsMarkPoints())
+ pDView->MovMarkPoints( rPos );
+ else
+ pDView->MovAction( rPos );
+ }
+}
+
+bool SwFEShell::EndMark()
+{
+ bool bRet = false;
+ OSL_ENSURE( Imp()->HasDrawView(), "EndMark without DrawView?" );
+
+ if (Imp()->GetDrawView()->IsMarkObj())
+ {
+ bRet = Imp()->GetDrawView()->EndMarkObj();
+
+ if ( bRet )
+ {
+ bool bShowHdl = false;
+ SwDrawView* pDView = Imp()->GetDrawView();
+ // frames are not selected this way, except when
+ // it is only one frame
+ SdrMarkList &rMrkList = const_cast<SdrMarkList&>(pDView->GetMarkedObjectList());
+ SwFlyFrame* pOldSelFly = ::GetFlyFromMarked( &rMrkList, this );
+
+ if ( rMrkList.GetMarkCount() > 1 )
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) != nullptr )
+ {
+ if ( !bShowHdl )
+ {
+ bShowHdl = true;
+ }
+ rMrkList.DeleteMark( i );
+ --i; // no exceptions
+ }
+ }
+
+ if( bShowHdl )
+ {
+ pDView->MarkListHasChanged();
+ pDView->AdjustMarkHdl();
+ }
+
+ if ( rMrkList.GetMarkCount() )
+ ::lcl_GrabCursor(this, pOldSelFly);
+ else
+ bRet = false;
+ }
+ if ( bRet )
+ ::FrameNotify( this, FLY_DRAG_START );
+ }
+ else
+ {
+ if (Imp()->GetDrawView()->IsMarkPoints())
+ bRet = Imp()->GetDrawView()->EndMarkPoints();
+ }
+
+ SetChainMarker();
+ return bRet;
+}
+
+RndStdIds SwFEShell::GetAnchorId() const
+{
+ RndStdIds nRet = RndStdIds(SHRT_MAX);
+ if ( Imp()->HasDrawView() )
+ {
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) != nullptr )
+ {
+ nRet = RndStdIds::UNKNOWN;
+ break;
+ }
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ RndStdIds nId = pContact->GetFormat()->GetAnchor().GetAnchorId();
+ if ( nRet == RndStdIds(SHRT_MAX) )
+ nRet = nId;
+ else if ( nRet != nId )
+ {
+ nRet = RndStdIds::UNKNOWN;
+ break;
+ }
+ }
+ }
+ if ( nRet == RndStdIds(SHRT_MAX) )
+ nRet = RndStdIds::UNKNOWN;
+ return nRet;
+}
+
+void SwFEShell::ChgAnchor( RndStdIds eAnchorId, bool bSameOnly, bool bPosCorr )
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "ChgAnchor without DrawView?" );
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if( rMrkList.GetMarkCount() &&
+ !rMrkList.GetMark( 0 )->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject() )
+ {
+ StartAllAction();
+
+ if( GetDoc()->ChgAnchor( rMrkList, eAnchorId, bSameOnly, bPosCorr ))
+ Imp()->GetDrawView()->UnmarkAll();
+
+ EndAllAction();
+
+ ::FrameNotify( this );
+ }
+}
+
+void SwFEShell::DelSelectedObj()
+{
+ OSL_ENSURE( Imp()->HasDrawView(), "DelSelectedObj(), no DrawView available" );
+ if ( Imp()->HasDrawView() )
+ {
+ StartAllAction();
+ Imp()->GetDrawView()->DeleteMarked();
+ EndAllAction();
+ ::FrameNotify( this, FLY_DRAG_END );
+ }
+}
+
+// For the statusline to request the current conditions
+Size SwFEShell::GetObjSize() const
+{
+ tools::Rectangle aRect;
+ if ( Imp()->HasDrawView() )
+ {
+ if ( Imp()->GetDrawView()->IsAction() )
+ Imp()->GetDrawView()->TakeActionRect( aRect );
+ else
+ aRect = Imp()->GetDrawView()->GetAllMarkedRect();
+ }
+ return aRect.GetSize();
+}
+
+Point SwFEShell::GetAnchorObjDiff() const
+{
+ const SdrView *pView = Imp()->GetDrawView();
+ OSL_ENSURE( pView, "GetAnchorObjDiff without DrawView?" );
+
+ tools::Rectangle aRect;
+ if ( Imp()->GetDrawView()->IsAction() )
+ Imp()->GetDrawView()->TakeActionRect( aRect );
+ else
+ aRect = Imp()->GetDrawView()->GetAllMarkedRect();
+
+ Point aRet( aRect.TopLeft() );
+
+ if ( IsFrameSelected() )
+ {
+ SwFlyFrame *pFly = GetSelectedFlyFrame();
+ aRet -= pFly->GetAnchorFrame()->getFrameArea().Pos();
+ }
+ else
+ {
+ const SdrObject *pObj = pView->GetMarkedObjectList().GetMarkCount() == 1 ?
+ pView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj() : nullptr;
+ if ( pObj )
+ aRet -= pObj->GetAnchorPos();
+ }
+
+ return aRet;
+}
+
+Point SwFEShell::GetObjAbsPos() const
+{
+ OSL_ENSURE( Imp()->GetDrawView(), "GetObjAbsPos() without DrawView?" );
+ return Imp()->GetDrawView()->GetDragStat().GetActionRect().TopLeft();
+}
+
+bool SwFEShell::IsGroupSelected(bool bAllowDiagams)
+{
+ if ( IsObjSelected() )
+ {
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ // consider 'virtual' drawing objects.
+ // Thus, use corresponding method instead of checking type.
+ if ( pObj->IsGroupObject() &&
+ // --> #i38505# No ungroup allowed for 3d objects
+ !pObj->Is3DObj() &&
+ RndStdIds::FLY_AS_CHAR != static_cast<SwDrawContact*>(GetUserCall(pObj))->
+ GetFormat()->GetAnchor().GetAnchorId() )
+ {
+ if(!bAllowDiagams)
+ {
+ // Don't allow enter Diagrams
+ if(pObj->isDiagram())
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+namespace
+{
+ bool HasSuitableGroupingAnchor(const SdrObject* pObj)
+ {
+ bool bSuitable = true;
+ SwFrameFormat* pFrameFormat(::FindFrameFormat(const_cast<SdrObject*>(pObj)));
+ if (!pFrameFormat)
+ {
+ OSL_FAIL( "<HasSuitableGroupingAnchor> - missing frame format" );
+ bSuitable = false;
+ }
+ else if (RndStdIds::FLY_AS_CHAR == pFrameFormat->GetAnchor().GetAnchorId())
+ {
+ bSuitable = false;
+ }
+ return bSuitable;
+ }
+}
+
+// Change return type.
+// Adjustments for drawing objects in header/footer:
+// allow group, only if all selected objects are in the same header/footer
+// or not in header/footer.
+bool SwFEShell::IsGroupAllowed() const
+{
+ bool bIsGroupAllowed = false;
+ if ( IsObjSelected() > 1 )
+ {
+ bIsGroupAllowed = true;
+ const SdrObject* pUpGroup = nullptr;
+ const SwFrame* pHeaderFooterFrame = nullptr;
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; bIsGroupAllowed && i < rMrkList.GetMarkCount(); ++i )
+ {
+ const SdrObject* pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( i )
+ bIsGroupAllowed = pObj->getParentSdrObjectFromSdrObject() == pUpGroup;
+ else
+ pUpGroup = pObj->getParentSdrObjectFromSdrObject();
+
+ if ( bIsGroupAllowed )
+ bIsGroupAllowed = HasSuitableGroupingAnchor(pObj);
+
+ // check, if all selected objects are in the
+ // same header/footer or not in header/footer.
+ if ( bIsGroupAllowed )
+ {
+ const SwFrame* pAnchorFrame = nullptr;
+ if ( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) )
+ {
+ const SwFlyFrame* pFlyFrame = pVirtFlyDrawObj->GetFlyFrame();
+ if ( pFlyFrame )
+ {
+ pAnchorFrame = pFlyFrame->GetAnchorFrame();
+ }
+ }
+ else
+ {
+ SwDrawContact* pDrawContact = static_cast<SwDrawContact*>(GetUserCall( pObj ));
+ if ( pDrawContact )
+ {
+ pAnchorFrame = pDrawContact->GetAnchorFrame( pObj );
+ }
+ }
+ if ( pAnchorFrame )
+ {
+ if ( i )
+ {
+ bIsGroupAllowed =
+ ( pAnchorFrame->FindFooterOrHeader() == pHeaderFooterFrame );
+ }
+ else
+ {
+ pHeaderFooterFrame = pAnchorFrame->FindFooterOrHeader();
+ }
+ }
+ }
+ }
+ }
+
+ return bIsGroupAllowed;
+}
+
+bool SwFEShell::IsUnGroupAllowed() const
+{
+ bool bIsUnGroupAllowed = false;
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for (size_t i = 0; i < rMrkList.GetMarkCount(); ++i)
+ {
+ const SdrObject* pObj = rMrkList.GetMark(i)->GetMarkedSdrObj();
+ bIsUnGroupAllowed = HasSuitableGroupingAnchor(pObj);
+ if (!bIsUnGroupAllowed)
+ break;
+ }
+
+ return bIsUnGroupAllowed;
+}
+
+// The group gets the anchor and the contactobject of the first in the selection
+void SwFEShell::GroupSelection()
+{
+ if ( IsGroupAllowed() )
+ {
+ StartAllAction();
+ StartUndo( SwUndoId::START );
+
+ GetDoc()->GroupSelection( *Imp()->GetDrawView() );
+
+ EndUndo( SwUndoId::END );
+ EndAllAction();
+ }
+}
+
+// The individual objects get a copy of the anchor and the contactobject of the group
+void SwFEShell::UnGroupSelection()
+{
+ if ( IsGroupSelected(true) )
+ {
+ StartAllAction();
+ StartUndo( SwUndoId::START );
+
+ GetDoc()->UnGroupSelection( *Imp()->GetDrawView() );
+
+ EndUndo( SwUndoId::END );
+ EndAllAction();
+ }
+}
+
+void SwFEShell::MirrorSelection( bool bHorizontal )
+{
+ SdrView *pView = Imp()->GetDrawView();
+ if ( IsObjSelected() && pView->IsMirrorAllowed() )
+ {
+ if ( bHorizontal )
+ pView->MirrorAllMarkedHorizontal();
+ else
+ pView->MirrorAllMarkedVertical();
+ }
+}
+
+// jump to named frame (Graphic/OLE)
+
+bool SwFEShell::GotoFly( const OUString& rName, FlyCntType eType, bool bSelFrame )
+{
+ bool bRet = false;
+ static SwNodeType const aChkArr[ 4 ] = {
+ /* FLYCNTTYPE_ALL */ SwNodeType::NONE,
+ /* FLYCNTTYPE_FRM */ SwNodeType::Text,
+ /* FLYCNTTYPE_GRF */ SwNodeType::Grf,
+ /* FLYCNTTYPE_OLE */ SwNodeType::Ole
+ };
+
+ const SwFlyFrameFormat* pFlyFormat = mxDoc->FindFlyByName( rName, aChkArr[ eType]);
+ if( pFlyFormat )
+ {
+ CurrShell aCurr( this );
+
+ SwFlyFrame* pFrame = SwIterator<SwFlyFrame,SwFormat>( *pFlyFormat ).First();
+ if( pFrame )
+ {
+ if( bSelFrame )
+ {
+ // first make visible, to get a11y events in proper order
+ if (!ActionPend())
+ MakeVisible( pFrame->getFrameArea() );
+ SelectObj( pFrame->getFrameArea().Pos(), 0, pFrame->GetVirtDrawObj() );
+ }
+ else
+ {
+ SwContentFrame *pCFrame = pFrame->ContainsContent();
+ if ( pCFrame )
+ {
+ ClearMark();
+ SwPaM* pCursor = GetCursor();
+
+ if (pCFrame->IsTextFrame())
+ {
+ *pCursor->GetPoint() = static_cast<SwTextFrame *>(pCFrame)
+ ->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ assert(pCFrame->IsNoTextFrame());
+ SwContentNode *const pCNode = static_cast<SwNoTextFrame *>(pCFrame)->GetNode();
+
+ pCursor->GetPoint()->nNode = *pCNode;
+ pCursor->GetPoint()->nContent.Assign( pCNode, 0 );
+ }
+
+ SwRect& rChrRect = const_cast<SwRect&>(GetCharRect());
+ rChrRect = pFrame->getFramePrintArea();
+ rChrRect.Pos() += pFrame->getFrameArea().Pos();
+ GetCursorDocPos() = rChrRect.Pos();
+ }
+ }
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+size_t SwFEShell::GetFlyCount( FlyCntType eType, bool bIgnoreTextBoxes ) const
+{
+ return GetDoc()->GetFlyCount(eType, bIgnoreTextBoxes);
+}
+
+const SwFrameFormat* SwFEShell::GetFlyNum(size_t nIdx, FlyCntType eType, bool bIgnoreTextBoxes ) const
+{
+ return GetDoc()->GetFlyNum(nIdx, eType, bIgnoreTextBoxes);
+}
+
+std::vector<SwFrameFormat const*> SwFEShell::GetFlyFrameFormats(
+ FlyCntType const eType, bool const bIgnoreTextBoxes)
+{
+ return GetDoc()->GetFlyFrameFormats(eType, bIgnoreTextBoxes);
+}
+
+// show the current selected object
+void SwFEShell::MakeSelVisible()
+{
+ if ( Imp()->HasDrawView() &&
+ Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() )
+ {
+ GetCurrFrame(); // just to trigger formatting in case the selected object is not formatted.
+ MakeVisible( SwRect(Imp()->GetDrawView()->GetAllMarkedRect()) );
+ }
+ else
+ SwCursorShell::MakeSelVisible();
+}
+
+// how is the selected object protected?
+FlyProtectFlags SwFEShell::IsSelObjProtected( FlyProtectFlags eType ) const
+{
+ FlyProtectFlags nChk = FlyProtectFlags::NONE;
+ const bool bParent(eType & FlyProtectFlags::Parent);
+ if( Imp()->HasDrawView() )
+ {
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for( size_t i = rMrkList.GetMarkCount(); i; )
+ {
+ SdrObject *pObj = rMrkList.GetMark( --i )->GetMarkedSdrObj();
+ if (!pObj)
+ {
+ continue;
+ }
+
+ if( !bParent )
+ {
+ nChk |= ( pObj->IsMoveProtect() ? FlyProtectFlags::Pos : FlyProtectFlags::NONE ) |
+ ( pObj->IsResizeProtect()? FlyProtectFlags::Size : FlyProtectFlags::NONE );
+
+ if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
+ {
+ SwFlyFrame *pFly = pVirtO->GetFlyFrame();
+ if ( (FlyProtectFlags::Content & eType) && pFly->GetFormat()->GetProtect().IsContentProtected() )
+ nChk |= FlyProtectFlags::Content;
+
+ if ( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ SwOLENode *pNd = static_cast<SwNoTextFrame*>(pFly->Lower())->GetNode()->GetOLENode();
+ uno::Reference < embed::XEmbeddedObject > xObj( pNd ? pNd->GetOLEObj().GetOleRef() : nullptr );
+ if ( xObj.is() )
+ {
+ // TODO/LATER: use correct aspect
+ const bool bNeverResize = (embed::EmbedMisc::EMBED_NEVERRESIZE & xObj->getStatus( embed::Aspects::MSOLE_CONTENT ));
+ if ( ( (FlyProtectFlags::Content & eType) || (FlyProtectFlags::Size & eType) ) && bNeverResize )
+ {
+ nChk |= FlyProtectFlags::Size;
+ nChk |= FlyProtectFlags::Fixed;
+ }
+
+ // set FlyProtectFlags::Pos if it is a Math object anchored 'as char' and baseline alignment is activated
+ const bool bProtectMathPos = SotExchange::IsMath( xObj->getClassID() )
+ && RndStdIds::FLY_AS_CHAR == pFly->GetFormat()->GetAnchor().GetAnchorId()
+ && mxDoc->GetDocumentSettingManager().get( DocumentSettingId::MATH_BASELINE_ALIGNMENT );
+ if ((FlyProtectFlags::Pos & eType) && bProtectMathPos)
+ nChk |= FlyProtectFlags::Pos;
+ }
+ }
+ }
+ nChk &= eType;
+ if( nChk == eType )
+ return eType;
+ }
+ const SwFrame* pAnch;
+ if (SwVirtFlyDrawObj* pVirtO = dynamic_cast<SwVirtFlyDrawObj*>(pObj))
+ pAnch = pVirtO->GetFlyFrame()->GetAnchorFrame();
+ else
+ {
+ SwDrawContact* pTmp = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ pAnch = pTmp ? pTmp->GetAnchorFrame( pObj ) : nullptr;
+ }
+ if( pAnch && pAnch->IsProtected() )
+ return eType;
+ }
+ }
+ return nChk;
+}
+
+bool SwFEShell::GetObjAttr( SfxItemSet &rSet ) const
+{
+ if ( !IsObjSelected() )
+ return false;
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ // --> make code robust
+ OSL_ENSURE( pContact, "<SwFEShell::GetObjAttr(..)> - missing <pContact>." );
+ if ( pContact )
+ {
+ if ( i )
+ rSet.MergeValues( pContact->GetFormat()->GetAttrSet() );
+ else
+ rSet.Put( pContact->GetFormat()->GetAttrSet() );
+ }
+ }
+ return true;
+}
+
+void SwFEShell::SetObjAttr( const SfxItemSet& rSet )
+{
+ CurrShell aCurr( this );
+
+ if ( !rSet.Count() )
+ {
+ OSL_ENSURE( false, "SetObjAttr, empty set." );
+ return;
+ }
+
+ StartAllAction();
+ StartUndo( SwUndoId::INSATTR );
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ GetDoc()->SetAttr( rSet, *pContact->GetFormat() );
+ }
+
+ EndUndo( SwUndoId::INSATTR );
+ EndAllActionAndCall();
+ GetDoc()->getIDocumentState().SetModified();
+}
+
+bool SwFEShell::IsAlignPossible() const
+{
+ return Imp()->GetDrawView()->IsAlignPossible();
+}
+
+void SwFEShell::CheckUnboundObjects()
+{
+ CurrShell aCurr( this );
+
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject *pObj = rMrkList.GetMark( i )->GetMarkedSdrObj();
+ if ( !GetUserCall(pObj) )
+ {
+ const tools::Rectangle &rBound = pObj->GetSnapRect();
+ const Point aPt( rBound.TopLeft() );
+ const SwFrame *pPage = GetLayout()->Lower();
+ const SwFrame *pLast = pPage;
+ while ( pPage && !pPage->getFrameArea().Contains( aPt ) )
+ {
+ if ( aPt.Y() > pPage->getFrameArea().Bottom() )
+ pLast = pPage;
+ pPage = pPage->GetNext();
+ }
+ if ( !pPage )
+ pPage = pLast;
+ OSL_ENSURE( pPage, "Page not found." );
+
+ SwFormatAnchor aAnch;
+ {
+ const SwContentFrame *const pAnch = ::FindAnchor(pPage, aPt, true);
+ SwPosition aPos( pAnch->IsTextFrame()
+ ? *static_cast<SwTextFrame const*>(pAnch)->GetTextNodeForParaProps()
+ : *static_cast<SwNoTextFrame const*>(pAnch)->GetNode() );
+ aAnch.SetType( RndStdIds::FLY_AT_PARA );
+ aAnch.SetAnchor( &aPos );
+ const_cast<SwRect&>(GetCharRect()).Pos() = aPt;
+ }
+
+ // First the action here, to assure GetCharRect delivers current values.
+ StartAllAction();
+
+ SfxItemSetFixed<RES_FRM_SIZE, RES_FRM_SIZE,
+ RES_SURROUND, RES_ANCHOR> aSet( GetAttrPool() );
+ aSet.Put( aAnch );
+ aSet.Put( SwFormatSurround( css::text::WrapTextMode_THROUGH ) );
+ SwFrameFormat* pFormat = getIDocumentLayoutAccess().MakeLayoutFormat( RndStdIds::DRAW_OBJECT, &aSet );
+
+ SwDrawContact *pContact = new SwDrawContact(
+ static_cast<SwDrawFrameFormat*>(pFormat), pObj );
+
+ // #i35635#
+ pContact->MoveObjToVisibleLayer( pObj );
+ pContact->ConnectToLayout();
+
+ EndAllAction();
+ }
+ }
+}
+
+void SwFEShell::SetCalcFieldValueHdl(Outliner* pOutliner)
+{
+ GetDoc()->SetCalcFieldValueHdl(pOutliner);
+}
+
+SwChainRet SwFEShell::Chainable( SwRect &rRect, const SwFrameFormat &rSource,
+ const Point &rPt ) const
+{
+ rRect.Clear();
+
+ // The source is not allowed to have a follow.
+ const SwFormatChain &rChain = rSource.GetChain();
+ if ( rChain.GetNext() )
+ return SwChainRet::SOURCE_CHAINED;
+
+ SwChainRet nRet = SwChainRet::NOT_FOUND;
+ if( Imp()->HasDrawView() )
+ {
+ SdrPageView* pPView;
+ SwDrawView *pDView = const_cast<SwDrawView*>(Imp()->GetDrawView());
+ const auto nOld = pDView->GetHitTolerancePixel();
+ pDView->SetHitTolerancePixel( 0 );
+ SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE);
+ SwVirtFlyDrawObj* pDrawObj = dynamic_cast<SwVirtFlyDrawObj*>(pObj);
+ if (pDrawObj)
+ {
+ SwFlyFrame *pFly = pDrawObj->GetFlyFrame();
+ rRect = pFly->getFrameArea();
+
+ // Target and source should not be equal and the list
+ // should not be cyclic
+ SwFrameFormat *pFormat = pFly->GetFormat();
+ nRet = GetDoc()->Chainable(rSource, *pFormat);
+ }
+ pDView->SetHitTolerancePixel( nOld );
+ }
+ return nRet;
+}
+
+void SwFEShell::Chain( SwFrameFormat &rSource, const SwFrameFormat &rDest )
+{
+ GetDoc()->Chain(rSource, rDest);
+}
+
+SwChainRet SwFEShell::Chain( SwFrameFormat &rSource, const Point &rPt )
+{
+ SwRect aDummy;
+ SwChainRet nErr = Chainable( aDummy, rSource, rPt );
+ if ( nErr == SwChainRet::OK )
+ {
+ StartAllAction();
+ SdrPageView* pPView;
+ SwDrawView *pDView = Imp()->GetDrawView();
+ const auto nOld = pDView->GetHitTolerancePixel();
+ pDView->SetHitTolerancePixel( 0 );
+ SdrObject* pObj = pDView->PickObj(rPt, pDView->getHitTolLog(), pPView, SdrSearchOptions::PICKMARKABLE);
+ pDView->SetHitTolerancePixel( nOld );
+ SwFlyFrame *pFly = static_cast<SwVirtFlyDrawObj*>(pObj)->GetFlyFrame();
+
+ SwFlyFrameFormat *pFormat = pFly->GetFormat();
+ GetDoc()->Chain(rSource, *pFormat);
+ EndAllAction();
+ SetChainMarker();
+ }
+ return nErr;
+}
+
+void SwFEShell::Unchain( SwFrameFormat &rFormat )
+{
+ StartAllAction();
+ GetDoc()->Unchain(rFormat);
+ EndAllAction();
+}
+
+void SwFEShell::HideChainMarker()
+{
+ m_pChainFrom.reset();
+ m_pChainTo.reset();
+}
+
+void SwFEShell::SetChainMarker()
+{
+ bool bDelFrom = true,
+ bDelTo = true;
+ if ( IsFrameSelected() )
+ {
+ SwFlyFrame *pFly = GetSelectedFlyFrame();
+
+ if ( pFly->GetPrevLink() )
+ {
+ bDelFrom = false;
+ const SwFrame *pPre = pFly->GetPrevLink();
+
+ Point aStart( pPre->getFrameArea().Right(), pPre->getFrameArea().Bottom());
+ Point aEnd(pFly->getFrameArea().Pos());
+
+ if (!m_pChainFrom)
+ {
+ m_pChainFrom.reset(
+ new SdrDropMarkerOverlay( *GetDrawView(), aStart, aEnd ));
+ }
+ }
+ if ( pFly->GetNextLink() )
+ {
+ bDelTo = false;
+ const SwFlyFrame *pNxt = pFly->GetNextLink();
+
+ Point aStart( pFly->getFrameArea().Right(), pFly->getFrameArea().Bottom());
+ Point aEnd(pNxt->getFrameArea().Pos());
+
+ if (!m_pChainTo)
+ {
+ m_pChainTo.reset(
+ new SdrDropMarkerOverlay( *GetDrawView(), aStart, aEnd ));
+ }
+ }
+ }
+
+ if ( bDelFrom )
+ {
+ m_pChainFrom.reset();
+ }
+
+ if ( bDelTo )
+ {
+ m_pChainTo.reset();
+ }
+}
+
+tools::Long SwFEShell::GetSectionWidth( SwFormat const & rFormat ) const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ // Is the cursor at this moment in a SectionFrame?
+ if( pFrame && pFrame->IsInSct() )
+ {
+ SwSectionFrame* pSect = pFrame->FindSctFrame();
+ do
+ {
+ // Is it the right one?
+ if( pSect->KnowsFormat( rFormat ) )
+ return pSect->getFrameArea().Width();
+ // for nested areas
+ pSect = pSect->GetUpper()->FindSctFrame();
+ }
+ while( pSect );
+ }
+ SwIterator<SwSectionFrame,SwFormat> aIter( rFormat );
+ for ( SwSectionFrame* pSct = aIter.First(); pSct; pSct = aIter.Next() )
+ {
+ if( !pSct->IsFollow() )
+ {
+ return pSct->getFrameArea().Width();
+ }
+ }
+ return 0;
+}
+
+void SwFEShell::CreateDefaultShape( SdrObjKind eSdrObjectKind, const tools::Rectangle& rRect,
+ sal_uInt16 nSlotId)
+{
+ SdrView* pDrawView = GetDrawView();
+ SdrModel* pDrawModel = pDrawView->GetModel();
+ SdrObject* pObj = SdrObjFactory::MakeNewObject(
+ *pDrawModel,
+ SdrInventor::Default,
+ eSdrObjectKind);
+
+ if(pObj)
+ {
+ tools::Rectangle aRect(rRect);
+ if(SdrObjKind::CircleArc == eSdrObjectKind || SdrObjKind::CircleCut == eSdrObjectKind)
+ {
+ // force quadratic
+ if(aRect.GetWidth() > aRect.GetHeight())
+ {
+ aRect = tools::Rectangle(
+ Point(aRect.Left() + ((aRect.GetWidth() - aRect.GetHeight()) / 2), aRect.Top()),
+ Size(aRect.GetHeight(), aRect.GetHeight()));
+ }
+ else
+ {
+ aRect = tools::Rectangle(
+ Point(aRect.Left(), aRect.Top() + ((aRect.GetHeight() - aRect.GetWidth()) / 2)),
+ Size(aRect.GetWidth(), aRect.GetWidth()));
+ }
+ }
+ pObj->SetLogicRect(aRect);
+
+ Point aStart = aRect.TopLeft();
+ Point aEnd = aRect.BottomRight();
+
+ if(dynamic_cast<const SdrCircObj*>( pObj) != nullptr)
+ {
+ SfxItemSet aAttr(pDrawModel->GetItemPool());
+ aAttr.Put(makeSdrCircStartAngleItem(9000_deg100));
+ aAttr.Put(makeSdrCircEndAngleItem(0_deg100));
+ pObj->SetMergedItemSet(aAttr);
+ }
+ else if(auto pPathObj = dynamic_cast<SdrPathObj*>( pObj))
+ {
+ basegfx::B2DPolyPolygon aPoly;
+
+ switch(eSdrObjectKind)
+ {
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ {
+ basegfx::B2DPolygon aInnerPoly;
+
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Bottom()));
+
+ const basegfx::B2DPoint aCenterBottom(aRect.Center().getX(), aRect.Bottom());
+ aInnerPoly.appendBezierSegment(
+ aCenterBottom,
+ aCenterBottom,
+ basegfx::B2DPoint(aRect.Center().getX(), aRect.Center().getY()));
+
+ const basegfx::B2DPoint aCenterTop(aRect.Center().getX(), aRect.Top());
+ aInnerPoly.appendBezierSegment(
+ aCenterTop,
+ aCenterTop,
+ basegfx::B2DPoint(aRect.Right(), aRect.Top()));
+
+ aInnerPoly.setClosed(true);
+ aPoly.append(aInnerPoly);
+ }
+ break;
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ {
+ basegfx::B2DPolygon aInnerPoly;
+
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Bottom()));
+
+ aInnerPoly.appendBezierSegment(
+ basegfx::B2DPoint(aRect.Left(), aRect.Top()),
+ basegfx::B2DPoint(aRect.Center().getX(), aRect.Top()),
+ basegfx::B2DPoint(aRect.Center().getX(), aRect.Center().getY()));
+
+ aInnerPoly.appendBezierSegment(
+ basegfx::B2DPoint(aRect.Center().getX(), aRect.Bottom()),
+ basegfx::B2DPoint(aRect.Right(), aRect.Bottom()),
+ basegfx::B2DPoint(aRect.Right(), aRect.Top()));
+
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Right(), aRect.Bottom()));
+ aInnerPoly.setClosed(true);
+ aPoly.append(aInnerPoly);
+ }
+ break;
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PolyLine:
+ {
+ basegfx::B2DPolygon aInnerPoly;
+ sal_Int32 nWdt(aRect.GetWidth());
+ sal_Int32 nHgt(aRect.GetHeight());
+
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Bottom()));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 30) / 100, aRect.Top() + (nHgt * 70) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left(), aRect.Top() + (nHgt * 15) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 65) / 100, aRect.Top()));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + nWdt, aRect.Top() + (nHgt * 30) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 80) / 100, aRect.Top() + (nHgt * 50) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Left() + (nWdt * 80) / 100, aRect.Top() + (nHgt * 75) / 100));
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Bottom(), aRect.Right()));
+
+ if(SdrObjKind::PolyLine == eSdrObjectKind)
+ {
+ aInnerPoly.append(basegfx::B2DPoint(aRect.Center().getX(), aRect.Bottom()));
+ }
+ else
+ {
+ aInnerPoly.setClosed(true);
+ }
+
+ aPoly.append(aInnerPoly);
+ }
+ break;
+ case SdrObjKind::Line :
+ {
+ sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2);
+ basegfx::B2DPolygon aTempPoly;
+ aTempPoly.append(basegfx::B2DPoint(aRect.TopLeft().getX(), nYMiddle));
+ aTempPoly.append(basegfx::B2DPoint(aRect.BottomRight().getX(), nYMiddle));
+ aPoly.append(aTempPoly);
+
+ SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool());
+ SetLineEnds(aAttr, *pObj, nSlotId);
+ pObj->SetMergedItemSet(aAttr);
+ }
+ break;
+ default:
+ break;
+ }
+
+ pPathObj->SetPathPoly(aPoly);
+ }
+ else if(auto pMeasureObj = dynamic_cast<SdrMeasureObj*>( pObj))
+ {
+ sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2);
+ pMeasureObj->SetPoint(Point(aStart.X(), nYMiddle), 0);
+ pMeasureObj->SetPoint(Point(aEnd.X(), nYMiddle), 1);
+
+ SfxItemSet aAttr(pObj->getSdrModelFromSdrObject().GetItemPool());
+ SetLineEnds(aAttr, *pObj, nSlotId);
+ pObj->SetMergedItemSet(aAttr);
+ }
+ else if(auto pCaptionObj = dynamic_cast<SdrCaptionObj*>( pObj))
+ {
+ bool bVerticalText = ( SID_DRAW_TEXT_VERTICAL == nSlotId ||
+ SID_DRAW_CAPTION_VERTICAL == nSlotId );
+ pCaptionObj->SetVerticalWriting(bVerticalText);
+ if(bVerticalText)
+ {
+ SfxItemSet aSet(pObj->GetMergedItemSet());
+ aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER));
+ aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT));
+ pObj->SetMergedItemSet(aSet);
+ }
+
+ pCaptionObj->SetLogicRect(aRect);
+ pCaptionObj->SetTailPos(
+ aRect.TopLeft() - Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2));
+ }
+ else if(auto pText = dynamic_cast<SdrTextObj*>( pObj))
+ {
+ pText->SetLogicRect(aRect);
+
+ bool bVertical = (SID_DRAW_TEXT_VERTICAL == nSlotId);
+ bool bMarquee = (SID_DRAW_TEXT_MARQUEE == nSlotId);
+
+ pText->SetVerticalWriting(bVertical);
+
+ if(bVertical)
+ {
+ SfxItemSet aSet(pDrawModel->GetItemPool());
+ aSet.Put(makeSdrTextAutoGrowWidthItem(true));
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP));
+ aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT));
+ pText->SetMergedItemSet(aSet);
+ }
+
+ if(bMarquee)
+ {
+ SfxItemSetFixed<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST> aSet(pDrawModel->GetItemPool());
+ aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
+ aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
+ aSet.Put( SdrTextAniKindItem( SdrTextAniKind::Slide ) );
+ aSet.Put( SdrTextAniDirectionItem( SdrTextAniDirection::Left ) );
+ aSet.Put( SdrTextAniCountItem( 1 ) );
+ aSet.Put( SdrTextAniAmountItem( static_cast<sal_Int16>(GetWin()->PixelToLogic(Size(2,1)).Width())) );
+ pObj->SetMergedItemSetAndBroadcast(aSet);
+ }
+ }
+ SdrPageView* pPageView = pDrawView->GetSdrPageView();
+ SdrCreateView::SetupObjLayer(pPageView, pDrawView->GetActiveLayer(), pObj);
+ // switch undo off or this combined with ImpEndCreate will cause two undos
+ // see comment made in SwFEShell::EndCreate (we create our own undo-object!)
+ const bool bUndo(GetDoc()->GetIDocumentUndoRedo().DoesUndo());
+ GetDoc()->GetIDocumentUndoRedo().DoUndo(false);
+ pDrawView->InsertObjectAtView(pObj, *pPageView);
+ GetDoc()->GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+ ImpEndCreate();
+}
+
+/** SwFEShell::GetShapeBackground
+ method determines background color of the page the selected drawing
+ object is on and returns this color.
+ If no color is found, because no drawing object is selected or ...,
+ color COL_BLACK (default color on constructing object of class Color)
+ is returned.
+
+ @returns an object of class Color
+*/
+Color SwFEShell::GetShapeBackground() const
+{
+ Color aRetColor;
+
+ // check, if a draw view exists
+ OSL_ENSURE( Imp()->GetDrawView(), "wrong usage of SwFEShell::GetShapeBackground - no draw view!");
+ if( Imp()->GetDrawView() )
+ {
+ // determine list of selected objects
+ const SdrMarkList* pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ // check, if exactly one object is selected.
+ OSL_ENSURE( pMrkList->GetMarkCount() == 1, "wrong usage of SwFEShell::GetShapeBackground - no selected object!");
+ if ( pMrkList->GetMarkCount() == 1)
+ {
+ // get selected object
+ const SdrObject *pSdrObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ // check, if selected object is a shape (drawing object)
+ OSL_ENSURE( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr, "wrong usage of SwFEShell::GetShapeBackground - selected object is not a drawing object!");
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr )
+ {
+ // determine page frame of the frame the shape is anchored.
+ const SwFrame* pAnchorFrame =
+ static_cast<SwDrawContact*>(GetUserCall(pSdrObj))->GetAnchorFrame( pSdrObj );
+ OSL_ENSURE( pAnchorFrame, "inconsistent model - no anchor at shape!");
+ if ( pAnchorFrame )
+ {
+ const SwPageFrame* pPageFrame = pAnchorFrame->FindPageFrame();
+ OSL_ENSURE( pPageFrame, "inconsistent model - no page!");
+ if ( pPageFrame )
+ {
+ aRetColor = pPageFrame->GetDrawBackgroundColor();
+ }
+ }
+ }
+ }
+ }
+
+ return aRetColor;
+}
+
+/** Is default horizontal text direction for selected drawing object right-to-left
+ Because drawing objects only painted for each page only, the default
+ horizontal text direction of a drawing object is given by the corresponding
+ page property.
+
+ @returns boolean, indicating, if the horizontal text direction of the
+ page, the selected drawing object is on, is right-to-left.
+*/
+bool SwFEShell::IsShapeDefaultHoriTextDirR2L() const
+{
+ bool bRet = false;
+
+ // check, if a draw view exists
+ OSL_ENSURE( Imp()->GetDrawView(), "wrong usage of SwFEShell::GetShapeBackground - no draw view!");
+ if( Imp()->GetDrawView() )
+ {
+ // determine list of selected objects
+ const SdrMarkList* pMrkList = &Imp()->GetDrawView()->GetMarkedObjectList();
+ // check, if exactly one object is selected.
+ OSL_ENSURE( pMrkList->GetMarkCount() == 1, "wrong usage of SwFEShell::GetShapeBackground - no selected object!");
+ if ( pMrkList->GetMarkCount() == 1)
+ {
+ // get selected object
+ const SdrObject *pSdrObj = pMrkList->GetMark( 0 )->GetMarkedSdrObj();
+ // check, if selected object is a shape (drawing object)
+ OSL_ENSURE( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr, "wrong usage of SwFEShell::GetShapeBackground - selected object is not a drawing object!");
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pSdrObj) == nullptr )
+ {
+ // determine page frame of the frame the shape is anchored.
+ const SwFrame* pAnchorFrame =
+ static_cast<SwDrawContact*>(GetUserCall(pSdrObj))->GetAnchorFrame( pSdrObj );
+ OSL_ENSURE( pAnchorFrame, "inconsistent model - no anchor at shape!");
+ if ( pAnchorFrame )
+ {
+ const SwPageFrame* pPageFrame = pAnchorFrame->FindPageFrame();
+ OSL_ENSURE( pPageFrame, "inconsistent model - no page!");
+ if ( pPageFrame )
+ {
+ bRet = pPageFrame->IsRightToLeft();
+ }
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+Point SwFEShell::GetRelativePagePosition(const Point& rDocPos)
+{
+ Point aRet(-1, -1);
+ const SwFrame *pPage = GetLayout()->Lower();
+ while ( pPage && !pPage->getFrameArea().Contains( rDocPos ) )
+ {
+ pPage = pPage->GetNext();
+ }
+ if(pPage)
+ {
+ aRet = rDocPos - pPage->getFrameArea().TopLeft();
+ }
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx
new file mode 100644
index 000000000..a77d19e02
--- /dev/null
+++ b/sw/source/core/frmedt/fetab.cxx
@@ -0,0 +1,2438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <hintids.hxx>
+
+#include <vcl/errinf.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <svtools/ruler.hxx>
+#include <osl/diagnose.h>
+#include <swwait.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <frmatr.hxx>
+#include <fesh.hxx>
+#include <wrtsh.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <flyfrm.hxx>
+#include <swtable.hxx>
+#include <swddetbl.hxx>
+#include <ndtxt.hxx>
+#include <calc.hxx>
+#include <dialoghelp.hxx>
+#include <tabcol.hxx>
+#include <tblafmt.hxx>
+#include <cellatr.hxx>
+#include <pam.hxx>
+#include <viscrs.hxx>
+#include <tblsel.hxx>
+#include <swerror.h>
+#include <swundo.hxx>
+#include <frmtool.hxx>
+#include <fmtrowsplt.hxx>
+#include <node.hxx>
+#include <sortedobjs.hxx>
+
+using namespace ::com::sun::star;
+
+// also see swtable.cxx
+#define COLFUZZY 20L
+
+static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; }
+
+namespace {
+
+class TableWait
+{
+ const std::unique_ptr<SwWait> m_pWait;
+ // this seems really fishy: do some locking, if an arbitrary number of lines is exceeded
+ static const size_t our_kLineLimit = 20;
+ static bool ShouldWait(size_t nCnt, SwFrame *pFrame, size_t nCnt2)
+ { return our_kLineLimit < nCnt || our_kLineLimit < nCnt2 || (pFrame && our_kLineLimit < pFrame->ImplFindTabFrame()->GetTable()->GetTabLines().size()); }
+public:
+ TableWait(size_t nCnt, SwFrame *pFrame, SwDocShell &rDocShell, size_t nCnt2 = 0)
+ : m_pWait( ShouldWait(nCnt, pFrame, nCnt2) ? std::make_unique<SwWait>( rDocShell, true ) : nullptr )
+ { }
+};
+
+}
+
+void SwFEShell::ParkCursorInTab()
+{
+ SwCursor * pSwCursor = GetCursor();
+
+ OSL_ENSURE(pSwCursor, "no SwCursor");
+
+ SwPosition aStartPos = *pSwCursor->GetPoint(), aEndPos = aStartPos;
+
+ /* Search least and greatest position in current cursor ring.
+ */
+ for(SwPaM& rTmpCursor : pSwCursor->GetRingContainer())
+ {
+ SwCursor* pTmpCursor = static_cast<SwCursor *>(&rTmpCursor);
+ const SwPosition * pPt = pTmpCursor->GetPoint(),
+ * pMk = pTmpCursor->GetMark();
+
+ if (*pPt < aStartPos)
+ aStartPos = *pPt;
+
+ if (*pPt > aEndPos)
+ aEndPos = *pPt;
+
+ if (*pMk < aStartPos)
+ aStartPos = *pMk;
+
+ if (*pMk > aEndPos)
+ aEndPos = *pMk;
+
+ }
+
+ KillPams();
+
+ /* @@@ semantic: SwCursor::operator=() is not implemented @@@ */
+
+ /* Set cursor to end of selection to ensure IsLastCellInRow works
+ properly. */
+ {
+ SwCursor aTmpCursor( aEndPos, nullptr );
+ *pSwCursor = aTmpCursor;
+ }
+
+ /* Move the cursor out of the columns to delete and stay in the
+ same row. If the table has only one column the cursor will
+ stay in the row and the shell will take care of it. */
+ if (IsLastCellInRow())
+ {
+ /* If the cursor is in the last row of the table, first
+ try to move it to the previous cell. If that fails move
+ it to the next cell. */
+
+ {
+ SwCursor aTmpCursor( aStartPos, nullptr );
+ *pSwCursor = aTmpCursor;
+ }
+
+ if (! pSwCursor->GoPrevCell())
+ {
+ SwCursor aTmpCursor( aEndPos, nullptr );
+ *pSwCursor = aTmpCursor;
+ pSwCursor->GoNextCell();
+ }
+ }
+ else
+ {
+ /* If the cursor is not in the last row of the table, first
+ try to move it to the next cell. If that fails move it
+ to the previous cell. */
+
+ {
+ SwCursor aTmpCursor( aEndPos, nullptr );
+ *pSwCursor = aTmpCursor;
+ }
+
+ if (! pSwCursor->GoNextCell())
+ {
+ SwCursor aTmpCursor( aStartPos, nullptr );
+ *pSwCursor = aTmpCursor;
+ pSwCursor->GoPrevCell();
+ }
+ }
+}
+
+void SwFEShell::InsertRow( sal_uInt16 nCnt, bool bBehind )
+{
+ // check if Point/Mark of current cursor are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+
+ if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return;
+ }
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ bool bSelectAll = StartsWithTable() && ExtendedSelectedAll();
+ if (bSelectAll)
+ {
+ // Set the end of the selection to the last paragraph of the last cell of the table.
+ SwPaM* pPaM = getShellCursor(false);
+ SwNode* pNode = pPaM->Start()->nNode.GetNode().FindTableNode()->EndOfSectionNode();
+ // pNode is the end node of the table, we want the last node before the end node of the last cell.
+ pPaM->End()->nNode = pNode->GetIndex() - 2;
+ pPaM->End()->nContent.Assign(pPaM->End()->nNode.GetNode().GetContentNode(), 0);
+ }
+ GetTableSel( *this, aBoxes, SwTableSearchType::Row );
+
+ TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
+
+ if ( !aBoxes.empty() )
+ GetDoc()->InsertRow( aBoxes, nCnt, bBehind );
+
+ EndAllActionAndCall();
+}
+
+void SwFEShell::InsertCol( sal_uInt16 nCnt, bool bBehind )
+{
+ // check if Point/Mark of current cursor are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+
+ if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return;
+ }
+
+ CurrShell aCurr( this );
+
+ if( !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::Col ) )
+ {
+ ErrorHandler::HandleError( ERR_TBLINSCOL_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return;
+ }
+
+ StartAllAction();
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes, SwTableSearchType::Col );
+
+ TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
+
+ if( !aBoxes.empty() )
+ GetDoc()->InsertCol( aBoxes, nCnt, bBehind );
+
+ EndAllActionAndCall();
+}
+
+// Determines if the current cursor is in the last row of the table.
+bool SwFEShell::IsLastCellInRow() const
+{
+ SwTabCols aTabCols;
+ GetTabCols( aTabCols );
+ bool bResult = false;
+
+ if (IsTableRightToLeft())
+ /* If the table is right-to-left the last row is the most left one. */
+ bResult = 0 == GetCurTabColNum();
+ else
+ /* If the table is left-to-right the last row is the most right one. */
+ bResult = aTabCols.Count() == GetCurTabColNum();
+
+ return bResult;
+}
+
+bool SwFEShell::DeleteCol()
+{
+ // check if Point/Mark of current cursor are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return false;
+
+ if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return false;
+ }
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ // search boxes via the layout
+ bool bRet;
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes, SwTableSearchType::Col );
+ if ( !aBoxes.empty() )
+ {
+ TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
+
+ // remove crsr from the deletion area.
+ // Put them behind/on the table; via the
+ // document position they will be put to the old position
+ while( !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+
+ ParkCursorInTab();
+
+ // then delete the column
+ StartUndo(SwUndoId::COL_DELETE);
+ bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
+ EndUndo(SwUndoId::COL_DELETE);
+ }
+ else
+ bRet = false;
+
+ EndAllActionAndCall();
+ return bRet;
+}
+
+void SwFEShell::DeleteTable()
+{
+ DeleteRow(true);
+}
+
+bool SwFEShell::DeleteRow(bool bCompleteTable)
+{
+ // check if Point/Mark of current cursor are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return false;
+
+ if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return false;
+ }
+
+ CurrShell aCurr( this );
+
+ bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording();
+ bool bRecordAndHideChanges = bRecordChanges &&
+ GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
+
+ // tracked deletion: all rows have already had tracked row change in the table selection
+ if ( bRecordChanges && !SwDoc::HasRowNotTracked( *getShellCursor( false ) ) )
+ return false;
+
+ if ( bRecordChanges )
+ StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
+
+ StartAllAction();
+
+ // tracked deletion: remove only textbox content,
+ // and set IsNoTracked table line property to false
+ if ( bRecordChanges )
+ {
+ SvxPrintItem aNotTracked(RES_PRINT, false);
+ GetDoc()->SetRowNotTracked( *getShellCursor( false ), aNotTracked );
+
+ if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
+ pWrtShell->SelectTableRow();
+
+ // don't need to remove the row frames in Show Changes mode
+ if ( !bRecordAndHideChanges )
+ {
+ if (SwEditShell* pEditShell = GetDoc()->GetEditShell())
+ pEditShell->Delete(false);
+
+ EndAllActionAndCall();
+ EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
+
+ return true;
+ }
+ }
+
+ // search for boxes via the layout
+ bool bRet;
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes, SwTableSearchType::Row );
+
+ if( !aBoxes.empty() )
+ {
+ TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
+
+ // Delete cursors from the deletion area.
+ // Then the cursor is:
+ // 1. the following row, if there is another row after this
+ // 2. the preceding row, if there is another row before this
+ // 3. otherwise below the table
+ {
+ SwTableNode* pTableNd = pFrame->IsTextFrame()
+ ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode()
+ : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode();
+
+ // search all boxes / lines
+ FndBox_ aFndBox( nullptr, nullptr );
+ {
+ FndPara aPara( aBoxes, &aFndBox );
+ ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
+ }
+
+ if( aFndBox.GetLines().empty() )
+ {
+ EndAllActionAndCall();
+ return false;
+ }
+
+ KillPams();
+
+ FndBox_* pFndBox = &aFndBox;
+ while( 1 == pFndBox->GetLines().size() &&
+ 1 == pFndBox->GetLines().front()->GetBoxes().size())
+ {
+ FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
+ if( pTmp->GetBox()->GetSttNd() )
+ break; // otherwise too far
+ pFndBox = pTmp;
+ }
+
+ SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
+ SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
+ while( !pDelBox->GetSttNd() )
+ {
+ SwTableLine* pLn = pDelBox->GetTabLines().back();
+ pDelBox = pLn->GetTabBoxes().back();
+ }
+ SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
+ pDelBox );
+ // skip deleted lines in Hide Changes mode with enabled change tracking
+ if ( bRecordAndHideChanges )
+ {
+ SwRedlineTable::size_type nRedlinePos = 0;
+ while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) )
+ pNextBox = pNextBox->GetUpper()->FindNextBox( pTableNd->GetTable(),
+ pNextBox->GetUpper()->GetTabBoxes().back() );
+ }
+
+ // skip protected cells
+ while( pNextBox &&
+ pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
+
+ if( !pNextBox ) // no next? then the previous
+ {
+ pDelLine = pFndBox->GetLines().front()->GetLine();
+ pDelBox = pDelLine->GetTabBoxes()[ 0 ];
+ while( !pDelBox->GetSttNd() )
+ pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
+ pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
+ pDelBox );
+ // skip previous deleted lines in Hide Changes mode with enabled change tracking
+ if ( bRecordAndHideChanges )
+ {
+ SwRedlineTable::size_type nRedlinePos = 0;
+ while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) )
+ {
+ pNextBox = pNextBox->GetUpper()->FindPreviousBox( pTableNd->GetTable(),
+ pNextBox->GetUpper()->GetTabBoxes()[0] );
+ nRedlinePos = 0;
+ }
+ }
+
+ // skip previous protected cells
+ while( pNextBox &&
+ pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
+ }
+
+ // delete row content in Hide Changes mode
+ if ( bRecordAndHideChanges )
+ {
+ SwEditShell* pEditShell = GetDoc()->GetEditShell();
+
+ // select the rows deleted with change tracking
+ if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
+ {
+ pWrtShell->SelectTableRow();
+ SwShellTableCursor* pTableCursor = GetTableCursor();
+ auto pStt = aBoxes[0];
+ auto pEnd = aBoxes.back();
+ pTableCursor->DeleteMark();
+
+ // set start and end of the selection
+ pTableCursor->GetPoint()->nNode = *pEnd->GetSttNd();
+ pTableCursor->Move( fnMoveForward, GoInContent );
+ pTableCursor->SetMark();
+ pTableCursor->GetPoint()->nNode = *pStt->GetSttNd()->EndOfSectionNode();
+ pTableCursor->Move( fnMoveBackward, GoInContent );
+ pWrtShell->UpdateCursor();
+ }
+
+ if (pEditShell)
+ pEditShell->Delete(false);
+ }
+
+ SwNodeOffset nIdx;
+ if( pNextBox ) // put cursor here
+ nIdx = pNextBox->GetSttIdx() + 1;
+ else // otherwise below the table
+ nIdx = pTableNd->EndOfSectionIndex() + 1;
+
+ SwNodeIndex aIdx( GetDoc()->GetNodes(), nIdx );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
+
+ // remove row frames in Hide Changes mode (and table frames, if needed)
+ if ( bRecordAndHideChanges )
+ {
+ // remove all frames of the table, and make them again without the deleted ones
+ // TODO remove only the deleted frames
+ pTableNd->DelFrames();
+ if ( !pTableNd->GetTable().IsDeleted() )
+ {
+ SwNodeIndex aTableIdx( *pTableNd->EndOfSectionNode(), 1 );
+ pTableNd->MakeOwnFrames(&aTableIdx);
+ }
+
+ EndAllActionAndCall();
+
+ // put cursor
+ SwPaM* pPam = GetCursor();
+ pPam->GetPoint()->nNode = aIdx;
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+ pPam->SetMark(); // both want something
+ pPam->DeleteMark();
+ if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
+ pWrtShell->UpdateCursor();
+
+ EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
+ return true;
+ }
+ else if( pCNd )
+ {
+ // put cursor
+ SwPaM* pPam = GetCursor();
+ pPam->GetPoint()->nNode = aIdx;
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+ pPam->SetMark(); // both want something
+ pPam->DeleteMark();
+ }
+ }
+
+ // now delete the lines
+ StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
+ bRet = GetDoc()->DeleteRowCol( aBoxes );
+ EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
+ }
+ else
+ bRet = false;
+
+ EndAllActionAndCall();
+ return bRet;
+}
+
+TableMergeErr SwFEShell::MergeTab()
+{
+ // check if Point/Mark of current cursor are in a table
+ TableMergeErr nRet = TableMergeErr::NoSelection;
+ if( IsTableMode() )
+ {
+ SwShellTableCursor* pTableCursor = GetTableCursor();
+ const SwTableNode* pTableNd = pTableCursor->GetNode().FindTableNode();
+ if( dynamic_cast< const SwDDETable* >(&pTableNd->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ }
+ else
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ TableWait aWait(pTableCursor->GetSelectedBoxesCount(), nullptr,
+ *GetDoc()->GetDocShell(),
+ pTableNd->GetTable().GetTabLines().size() );
+
+ nRet = GetDoc()->MergeTable( *pTableCursor );
+
+ KillPams();
+
+ EndAllActionAndCall();
+ }
+ }
+ return nRet;
+}
+
+void SwFEShell::SplitTab( bool bVert, sal_uInt16 nCnt, bool bSameHeight )
+{
+ // check if Point/Mark of current cursor are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+
+ if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return;
+ }
+
+ CurrShell aCurr( this );
+
+ if( bVert && !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::NONE ) )
+ {
+ ErrorHandler::HandleError( ERR_TBLSPLIT_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return;
+ }
+ StartAllAction();
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ GetTableSel( *this, aBoxes );
+ if( !aBoxes.empty() )
+ {
+ TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
+
+ // now delete the columns
+ GetDoc()->SplitTable( aBoxes, bVert, nCnt, bSameHeight );
+
+ ClearFEShellTabCols(*GetDoc(), nullptr);
+ }
+ EndAllActionAndCall();
+}
+
+void SwFEShell::GetTabCols_(SwTabCols &rToFill, const SwFrame *pBox) const
+{
+ const SwTabFrame *pTab = pBox->FindTabFrame();
+ if (m_pColumnCache)
+ {
+ bool bDel = true;
+ if (m_pColumnCache->pLastTable == pTab->GetTable())
+ {
+ bDel = false;
+ SwRectFnSet aRectFnSet(pTab);
+
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+ const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ if (m_pColumnCache->pLastTabFrame != pTab)
+ {
+ // if TabFrame was changed, we only shift a little bit
+ // as the width is the same
+ SwRectFnSet fnRectX(m_pColumnCache->pLastTabFrame);
+ if (fnRectX.GetWidth(m_pColumnCache->pLastTabFrame->getFrameArea()) ==
+ aRectFnSet.GetWidth(pTab->getFrameArea()) )
+ {
+ m_pColumnCache->pLastCols->SetLeftMin( nLeftMin );
+
+ m_pColumnCache->pLastTabFrame = pTab;
+ }
+ else
+ bDel = true;
+ }
+
+ if ( !bDel &&
+ m_pColumnCache->pLastCols->GetLeftMin () == o3tl::narrowing<sal_uInt16>(nLeftMin) &&
+ m_pColumnCache->pLastCols->GetLeft () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetLeft(pTab->getFramePrintArea())) &&
+ m_pColumnCache->pLastCols->GetRight () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetRight(pTab->getFramePrintArea()))&&
+ m_pColumnCache->pLastCols->GetRightMax() == o3tl::narrowing<sal_uInt16>(nRightMax) - m_pColumnCache->pLastCols->GetLeftMin() )
+ {
+ if (m_pColumnCache->pLastCellFrame != pBox)
+ {
+ pTab->GetTable()->GetTabCols( *m_pColumnCache->pLastCols,
+ static_cast<const SwCellFrame*>(pBox)->GetTabBox(), true);
+ m_pColumnCache->pLastCellFrame = pBox;
+ }
+ rToFill = *m_pColumnCache->pLastCols;
+ }
+ else
+ bDel = true;
+ }
+ if ( bDel )
+ m_pColumnCache.reset();
+ }
+ if (!m_pColumnCache)
+ {
+ SwDoc::GetTabCols( rToFill, static_cast<const SwCellFrame*>(pBox) );
+
+ m_pColumnCache.reset(new SwColCache);
+ m_pColumnCache->pLastCols.reset(new SwTabCols(rToFill));
+ m_pColumnCache->pLastTable = pTab->GetTable();
+ m_pColumnCache->pLastTabFrame = pTab;
+ m_pColumnCache->pLastCellFrame = pBox;
+ }
+}
+
+void SwFEShell::GetTabRows_(SwTabCols &rToFill, const SwFrame *pBox) const
+{
+ const SwTabFrame *pTab = pBox->FindTabFrame();
+ if (m_pRowCache)
+ {
+ bool bDel = true;
+ if (m_pRowCache->pLastTable == pTab->GetTable())
+ {
+ bDel = false;
+ SwRectFnSet aRectFnSet(pTab);
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
+ pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
+ pTab->GetPrtTop() - pPage->getFrameArea().Top() );
+ const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
+ const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
+ const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
+
+ if (m_pRowCache->pLastTabFrame != pTab || m_pRowCache->pLastCellFrame != pBox)
+ bDel = true;
+
+ if ( !bDel &&
+ m_pRowCache->pLastCols->GetLeftMin () == nLeftMin &&
+ m_pRowCache->pLastCols->GetLeft () == nLeft &&
+ m_pRowCache->pLastCols->GetRight () == nRight &&
+ m_pRowCache->pLastCols->GetRightMax() == nRightMax )
+ {
+ rToFill = *m_pRowCache->pLastCols;
+ }
+ else
+ bDel = true;
+ }
+ if ( bDel )
+ m_pRowCache.reset();
+ }
+ if (!m_pRowCache)
+ {
+ SwDoc::GetTabRows( rToFill, static_cast<const SwCellFrame*>(pBox) );
+
+ m_pRowCache.reset(new SwColCache);
+ m_pRowCache->pLastCols.reset(new SwTabCols(rToFill));
+ m_pRowCache->pLastTable = pTab->GetTable();
+ m_pRowCache->pLastTabFrame = pTab;
+ m_pRowCache->pLastCellFrame = pBox;
+ }
+}
+
+void SwFEShell::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly )
+{
+ SwFrame *pBox = GetCurrFrame();
+ if( !pBox || !pBox->IsInTab() )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ do
+ {
+ pBox = pBox->GetUpper();
+ } while (pBox && !pBox->IsCellFrame());
+
+ GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<SwCellFrame*>(pBox) );
+ EndAllActionAndCall();
+}
+
+void SwFEShell::GetTabCols( SwTabCols &rToFill ) const
+{
+ const SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+ do
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ while (pFrame && !pFrame->IsCellFrame());
+
+ if (!pFrame)
+ return;
+
+ GetTabCols_( rToFill, pFrame );
+}
+
+void SwFEShell::GetTabRows( SwTabCols &rToFill ) const
+{
+ const SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+ do
+ {
+ pFrame = pFrame->GetUpper();
+ } while (pFrame && !pFrame->IsCellFrame());
+
+ if (!pFrame)
+ return;
+
+ GetTabRows_( rToFill, pFrame );
+}
+
+void SwFEShell::SetTabRows( const SwTabCols &rNew, bool bCurColOnly )
+{
+ SwFrame *pBox = GetCurrFrame();
+ if( !pBox || !pBox->IsInTab() )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ do
+ {
+ pBox = pBox->GetUpper();
+ } while (pBox && !pBox->IsCellFrame());
+
+ GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<SwCellFrame*>(pBox) );
+ EndAllActionAndCall();
+}
+
+void SwFEShell::GetMouseTabRows( SwTabCols &rToFill, const Point &rPt ) const
+{
+ const SwFrame *pBox = GetBox( rPt );
+ if ( pBox )
+ GetTabRows_( rToFill, pBox );
+}
+
+void SwFEShell::SetMouseTabRows( const SwTabCols &rNew, bool bCurColOnly, const Point &rPt )
+{
+ const SwFrame *pBox = GetBox( rPt );
+ if( pBox )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<const SwCellFrame*>(pBox) );
+ EndAllActionAndCall();
+ }
+}
+
+void SwFEShell::SetRowSplit( const SwFormatRowSplit& rNew )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetRowSplit( *getShellCursor( false ), rNew );
+ EndAllActionAndCall();
+}
+
+std::unique_ptr<SwFormatRowSplit> SwFEShell::GetRowSplit() const
+{
+ return SwDoc::GetRowSplit( *getShellCursor( false ) );
+}
+
+void SwFEShell::SetRowHeight( const SwFormatFrameSize &rNew )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetRowHeight( *getShellCursor( false ), rNew );
+ EndAllActionAndCall();
+}
+
+std::unique_ptr<SwFormatFrameSize> SwFEShell::GetRowHeight() const
+{
+ return SwDoc::GetRowHeight( *getShellCursor( false ) );
+}
+
+bool SwFEShell::BalanceRowHeight( bool bTstOnly, const bool bOptimize )
+{
+ CurrShell aCurr( this );
+ if( !bTstOnly )
+ StartAllAction();
+ bool bRet = GetDoc()->BalanceRowHeight( *getShellCursor( false ), bTstOnly, bOptimize );
+ if( !bTstOnly )
+ EndAllActionAndCall();
+ return bRet;
+}
+
+void SwFEShell::SetRowBackground( const SvxBrushItem &rNew )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetRowBackground( *getShellCursor( false ), rNew );
+ EndAllActionAndCall();
+}
+
+bool SwFEShell::GetRowBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
+{
+ return SwDoc::GetRowBackground( *getShellCursor( false ), rToFill );
+}
+
+void SwFEShell::SetTabBorders( const SfxItemSet& rSet )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetTabBorders( *getShellCursor( false ), rSet );
+ EndAllActionAndCall();
+}
+
+void SwFEShell::SetTabLineStyle( const Color* pColor, bool bSetLine,
+ const editeng::SvxBorderLine* pBorderLine )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetTabLineStyle( *getShellCursor( false ),
+ pColor, bSetLine, pBorderLine );
+ EndAllActionAndCall();
+}
+
+void SwFEShell::GetTabBorders( SfxItemSet& rSet ) const
+{
+ SwDoc::GetTabBorders( *getShellCursor( false ), rSet );
+}
+
+void SwFEShell::SetBoxBackground( const SvxBrushItem &rNew )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew );
+ EndAllActionAndCall();
+}
+
+bool SwFEShell::GetBoxBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
+{
+ std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill);
+ bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp));
+ rToFill.reset(static_cast<SvxBrushItem*>(aTemp.release()));
+ return bRetval;
+}
+
+void SwFEShell::SetBoxDirection( const SvxFrameDirectionItem& rNew )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew );
+ EndAllActionAndCall();
+}
+
+bool SwFEShell::GetBoxDirection( std::unique_ptr<SvxFrameDirectionItem>& rToFill ) const
+{
+ std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill);
+ bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp));
+ rToFill.reset(static_cast<SvxFrameDirectionItem*>(aTemp.release()));
+ return bRetval;
+}
+
+void SwFEShell::SetBoxAlign( sal_uInt16 nAlign )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetBoxAlign( *getShellCursor( false ), nAlign );
+ EndAllActionAndCall();
+}
+
+sal_uInt16 SwFEShell::GetBoxAlign() const
+{
+ return SwDoc::GetBoxAlign( *getShellCursor( false ) );
+}
+
+void SwFEShell::SetTabBackground( const SvxBrushItem &rNew )
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetAttr( rNew, *pFrame->ImplFindTabFrame()->GetFormat() );
+ EndAllAction(); // no call, nothing changes!
+ GetDoc()->getIDocumentState().SetModified();
+}
+
+void SwFEShell::GetTabBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if( pFrame && pFrame->IsInTab() )
+ rToFill = pFrame->ImplFindTabFrame()->GetFormat()->makeBackgroundBrushItem();
+}
+
+bool SwFEShell::HasWholeTabSelection() const
+{
+ // whole table selected?
+ if ( IsTableMode() )
+ {
+ SwSelBoxes aBoxes;
+ ::GetTableSelCrs( *this, aBoxes );
+ if( !aBoxes.empty() )
+ {
+ const SwTableNode *pTableNd = IsCursorInTable();
+ return pTableNd &&
+ aBoxes[0]->GetSttIdx() - 1 == pTableNd->EndOfSectionNode()->StartOfSectionIndex() &&
+ aBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1 == pTableNd->EndOfSectionIndex();
+ }
+ }
+ return false;
+}
+
+bool SwFEShell::HasBoxSelection() const
+{
+ if(!IsCursorInTable())
+ return false;
+ // whole table selected?
+ if( IsTableMode() )
+ return true;
+ SwPaM* pPam = GetCursor();
+ // empty boxes are also selected as the absence of selection
+ bool bChg = false;
+ if( pPam->GetPoint() == pPam->End())
+ {
+ bChg = true;
+ pPam->Exchange();
+ }
+ SwNode* pNd;
+ if( pPam->GetPoint()->nNode.GetIndex() -1 ==
+ ( pNd = &pPam->GetNode())->StartOfSectionIndex() &&
+ !pPam->GetPoint()->nContent.GetIndex() &&
+ pPam->GetMark()->nNode.GetIndex() + 1 ==
+ pNd->EndOfSectionIndex())
+ {
+ SwNodeIndex aIdx( *pNd->EndOfSectionNode(), -1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ {
+ pCNd = SwNodes::GoPrevious( &aIdx );
+ OSL_ENSURE( pCNd, "no ContentNode in box ??" );
+ }
+ if( pPam->GetMark()->nContent == pCNd->Len() )
+ {
+ if( bChg )
+ pPam->Exchange();
+ return true;
+ }
+ }
+ if( bChg )
+ pPam->Exchange();
+ return false;
+}
+
+void SwFEShell::ProtectCells()
+{
+ SvxProtectItem aProt( RES_PROTECT );
+ aProt.SetContentProtect( true );
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ GetDoc()->SetBoxAttr( *getShellCursor( false ), aProt );
+
+ if( !IsCursorReadonly() )
+ {
+ if( IsTableMode() )
+ ClearMark();
+ ParkCursorInTab();
+ }
+ EndAllActionAndCall();
+}
+
+// cancel table selection
+void SwFEShell::UnProtectCells()
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ SwSelBoxes aBoxes;
+ if( IsTableMode() )
+ ::GetTableSelCrs( *this, aBoxes );
+ else
+ {
+ SwFrame *pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+ if( pFrame )
+ {
+ SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
+ aBoxes.insert( pBox );
+ }
+ }
+
+ if( !aBoxes.empty() )
+ GetDoc()->UnProtectCells( aBoxes );
+
+ EndAllActionAndCall();
+}
+
+void SwFEShell::UnProtectTables()
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->UnProtectTables( *GetCursor() );
+ EndAllActionAndCall();
+}
+
+bool SwFEShell::HasTableAnyProtection( const OUString* pTableName,
+ bool* pFullTableProtection )
+{
+ return GetDoc()->HasTableAnyProtection( GetCursor()->GetPoint(), pTableName,
+ pFullTableProtection );
+}
+
+bool SwFEShell::CanUnProtectCells() const
+{
+ bool bUnProtectAvailable = false;
+ const SwTableNode *pTableNd = IsCursorInTable();
+ if( pTableNd && !pTableNd->IsProtect() )
+ {
+ SwSelBoxes aBoxes;
+ if( IsTableMode() )
+ ::GetTableSelCrs( *this, aBoxes );
+ else
+ {
+ SwFrame *pFrame = GetCurrFrame();
+ do {
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsCellFrame() );
+ if( pFrame )
+ {
+ SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
+ aBoxes.insert( pBox );
+ }
+ }
+ if( !aBoxes.empty() )
+ bUnProtectAvailable = ::HasProtectedCells( aBoxes );
+ }
+ return bUnProtectAvailable;
+}
+
+sal_uInt16 SwFEShell::GetRowsToRepeat() const
+{
+ const SwFrame *pFrame = GetCurrFrame();
+ const SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr;
+ if( pTab )
+ return pTab->GetTable()->GetRowsToRepeat();
+ return 0;
+}
+
+void SwFEShell::SetRowsToRepeat( sal_uInt16 nSet )
+{
+ SwFrame *pFrame = GetCurrFrame();
+ SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr;
+ if( pTab && pTab->GetTable()->GetRowsToRepeat() != nSet )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetRowsToRepeat( *pTab->GetTable(), nSet );
+ EndAllActionAndCall();
+ }
+}
+
+// returns the number of rows consecutively selected from top
+static sal_uInt16 lcl_GetRowNumber( const SwPosition& rPos )
+{
+ Point aTmpPt;
+ const SwContentNode *pNd;
+ const SwContentFrame *pFrame;
+
+ std::pair<Point, bool> const tmp(aTmpPt, false);
+ pNd = rPos.nNode.GetNode().GetContentNode();
+ if( nullptr != pNd )
+ pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
+ else
+ pFrame = nullptr;
+
+ const SwFrame* pRow = (pFrame && pFrame->IsInTab()) ? pFrame->GetUpper() : nullptr;
+
+ while (pRow && (!pRow->GetUpper() || !pRow->GetUpper()->IsTabFrame()))
+ pRow = pRow->GetUpper();
+
+ if (!pRow)
+ return USHRT_MAX;
+
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
+ const SwTableLine* pTabLine = static_cast<const SwRowFrame*>(pRow)->GetTabLine();
+ sal_uInt16 nRet = USHRT_MAX;
+ sal_uInt16 nI = 0;
+ while ( sal::static_int_cast<SwTableLines::size_type>(nI) < pTabFrame->GetTable()->GetTabLines().size() )
+ {
+ if ( pTabFrame->GetTable()->GetTabLines()[ nI ] == pTabLine )
+ {
+ nRet = nI;
+ break;
+ }
+ ++nI;
+ }
+
+ return nRet;
+}
+
+sal_uInt16 SwFEShell::GetRowSelectionFromTop() const
+{
+ sal_uInt16 nRet = 0;
+ const SwPaM* pPaM = IsTableMode() ? GetTableCursor() : GetCursor_();
+ const sal_uInt16 nPtLine = lcl_GetRowNumber( *pPaM->GetPoint() );
+
+ if ( !IsTableMode() )
+ {
+ nRet = 0 == nPtLine ? 1 : 0;
+ }
+ else
+ {
+ const sal_uInt16 nMkLine = lcl_GetRowNumber( *pPaM->GetMark() );
+
+ if ( ( nPtLine == 0 && nMkLine != USHRT_MAX ) ||
+ ( nMkLine == 0 && nPtLine != USHRT_MAX ) )
+ {
+ nRet = std::max( nPtLine, nMkLine ) + 1;
+ }
+ }
+
+ return nRet;
+}
+
+/*
+ * 1. case: bRepeat = true
+ * returns true if the current frame is located inside a table headline in
+ * a follow frame
+ *
+ * 2. case: bRepeat = false
+ * returns true if the current frame is located inside a table headline OR
+ * inside the first line of a table!!!
+ */
+bool SwFEShell::CheckHeadline( bool bRepeat ) const
+{
+ bool bRet = false;
+ if ( !IsTableMode() )
+ {
+ SwFrame *pFrame = GetCurrFrame(); // DONE MULTIIHEADER
+ SwTabFrame* pTab = (pFrame && pFrame->IsInTab()) ? pFrame->FindTabFrame() : nullptr;
+ if (pTab)
+ {
+ if ( bRepeat )
+ {
+ bRet = pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
+ }
+ else
+ {
+ bRet = static_cast<SwLayoutFrame*>(pTab->Lower())->IsAnLower( pFrame ) ||
+ pTab->IsInHeadline( *pFrame );
+ }
+ }
+ }
+ return bRet;
+}
+
+void SwFEShell::AdjustCellWidth( const bool bBalance, const bool bNoShrink )
+{
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ // switch on wait-cursor, as we do not know how
+ // much content is affected
+ TableWait aWait(std::numeric_limits<size_t>::max(), nullptr,
+ *GetDoc()->GetDocShell());
+
+ GetDoc()->AdjustCellWidth( *getShellCursor( false ), bBalance, bNoShrink );
+ EndAllActionAndCall();
+}
+
+bool SwFEShell::IsAdjustCellWidthAllowed( bool bBalance ) const
+{
+ // at least one row with content should be contained in the selection
+
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return false;
+
+ SwSelBoxes aBoxes;
+ ::GetTableSelCrs( *this, aBoxes );
+
+ if ( bBalance )
+ return aBoxes.size() > 1;
+
+ if ( aBoxes.empty() )
+ {
+ do
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ while (pFrame && !pFrame->IsCellFrame());
+
+ if (!pFrame)
+ return false;
+
+ SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
+ aBoxes.insert( pBox );
+ }
+
+ for (size_t i = 0; i < aBoxes.size(); ++i)
+ {
+ SwTableBox *pBox = aBoxes[i];
+ if ( pBox->GetSttNd() )
+ {
+ SwNodeIndex aIdx( *pBox->GetSttNd(), 1 );
+ SwTextNode* pCNd = aIdx.GetNode().GetTextNode();
+ if( !pCNd )
+ pCNd = static_cast<SwTextNode*>(GetDoc()->GetNodes().GoNext( &aIdx ));
+
+ while ( pCNd )
+ {
+ if (!pCNd->GetText().isEmpty())
+ return true;
+ ++aIdx;
+ pCNd = aIdx.GetNode().GetTextNode();
+ }
+ }
+ }
+ return false;
+}
+
+void SwFEShell::SetTableStyle(const OUString& rStyleName)
+{
+ // make sure SwDoc has the style
+ SwTableAutoFormat *pTableFormat = GetDoc()->GetTableStyles().FindAutoFormat(rStyleName);
+ if (!pTableFormat)
+ return;
+
+ SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
+ if (!pTableNode)
+ return;
+
+ // set the name & update
+ UpdateTableStyleFormatting(pTableNode, false, &rStyleName);
+}
+
+ // AutoFormat for the table/table selection
+bool SwFEShell::SetTableStyle(const SwTableAutoFormat& rStyle)
+{
+ // make sure SwDoc has the style
+ GetDoc()->GetTableStyles().AddAutoFormat(rStyle);
+
+ SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
+ if (!pTableNode)
+ return false;
+
+ // set the name & update
+ return UpdateTableStyleFormatting(pTableNode, false, &rStyle.GetName());
+}
+
+bool SwFEShell::UpdateTableStyleFormatting(SwTableNode *pTableNode,
+ bool bResetDirect, OUString const*const pStyleName)
+{
+ if (!pTableNode)
+ {
+ pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
+ if (!pTableNode || pTableNode->GetTable().IsTableComplex())
+ return false;
+ }
+
+ OUString const aTableStyleName(pStyleName
+ ? *pStyleName
+ : pTableNode->GetTable().GetTableStyleName());
+ SwTableAutoFormat* pTableStyle = GetDoc()->GetTableStyles().FindAutoFormat(aTableStyleName);
+ if (!pTableStyle)
+ return false;
+
+ SwSelBoxes aBoxes;
+
+ // whole table or only current selection
+ if( IsTableMode() )
+ ::GetTableSelCrs( *this, aBoxes );
+ else
+ {
+ const SwTableSortBoxes& rTBoxes = pTableNode->GetTable().GetTabSortBoxes();
+ for (size_t n = 0; n < rTBoxes.size(); ++n)
+ {
+ SwTableBox* pBox = rTBoxes[ n ];
+ aBoxes.insert( pBox );
+ }
+ }
+
+ bool bRet;
+ if( !aBoxes.empty() )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+ bRet = GetDoc()->SetTableAutoFormat(
+ aBoxes, *pTableStyle, bResetDirect, pStyleName != nullptr);
+ ClearFEShellTabCols(*GetDoc(), nullptr);
+ EndAllActionAndCall();
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+bool SwFEShell::GetTableAutoFormat( SwTableAutoFormat& rGet )
+{
+ const SwTableNode *pTableNd = IsCursorInTable();
+ if( !pTableNd || pTableNd->GetTable().IsTableComplex() )
+ return false;
+
+ SwSelBoxes aBoxes;
+
+ if ( !IsTableMode() ) // if cursor are not current
+ GetCursor();
+
+ // whole table or only current selection
+ if( IsTableMode() )
+ ::GetTableSelCrs( *this, aBoxes );
+ else
+ {
+ const SwTableSortBoxes& rTBoxes = pTableNd->GetTable().GetTabSortBoxes();
+ for (size_t n = 0; n < rTBoxes.size(); ++n)
+ {
+ SwTableBox* pBox = rTBoxes[ n ];
+ aBoxes.insert( pBox );
+ }
+ }
+
+ return GetDoc()->GetTableAutoFormat( aBoxes, rGet );
+}
+
+bool SwFEShell::DeleteTableSel()
+{
+ // check if SPoint/Mark of current cursor are in a table
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return false;
+
+ if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
+ {
+ ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
+ DialogMask::MessageInfo | DialogMask::ButtonsOk );
+ return false;
+ }
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ // search boxes via the layout
+ bool bRet;
+ SwSelBoxes aBoxes;
+ GetTableSelCrs( *this, aBoxes );
+ if( !aBoxes.empty() )
+ {
+ TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
+
+ // cursor should be removed from deletion area.
+ // Put them behind/on the table; via the document
+ // position they'll be set to the old position
+ while( !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+ ParkCursor( SwNodeIndex( *static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetSttNd() ));
+
+ bRet = GetDoc()->DeleteRowCol( aBoxes );
+
+ ClearFEShellTabCols(*GetDoc(), nullptr);
+ }
+ else
+ bRet = false;
+ EndAllActionAndCall();
+ return bRet;
+}
+
+size_t SwFEShell::GetCurTabColNum() const
+{
+ //!!!GetCurMouseTabColNum() mitpflegen!!!!
+ SwFrame *pFrame = GetCurrFrame();
+ OSL_ENSURE( pFrame, "Cursor parked?" );
+
+ // check if SPoint/Mark of current cursor are in a table
+ if (!pFrame || !pFrame->IsInTab())
+ return 0;
+
+ do
+ {
+ // JP 26.09.95: why compare with ContentFrame
+ // and not with CellFrame ????
+ pFrame = pFrame->GetUpper();
+ } while (pFrame && !pFrame->IsCellFrame());
+
+ if (!pFrame)
+ return 0;
+
+ size_t nRet = 0;
+
+ SwRectFnSet aRectFnSet(pFrame);
+
+ const SwPageFrame* pPage = pFrame->FindPageFrame();
+
+ // get TabCols, as only via these we get to the position
+ SwTabCols aTabCols;
+ GetTabCols( aTabCols );
+
+ if( pFrame->FindTabFrame()->IsRightToLeft() )
+ {
+ tools::Long nX = aRectFnSet.GetRight(pFrame->getFrameArea()) - aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ const tools::Long nRight = aTabCols.GetLeftMin() + aTabCols.GetRight();
+
+ if ( !::IsSame( nX, nRight ) )
+ {
+ nX = nRight - nX + aTabCols.GetLeft();
+ for ( size_t i = 0; i < aTabCols.Count(); ++i )
+ if ( ::IsSame( nX, aTabCols[i] ) )
+ {
+ nRet = i + 1;
+ break;
+ }
+ }
+ }
+ else
+ {
+ const tools::Long nX = aRectFnSet.GetLeft(pFrame->getFrameArea()) -
+ aRectFnSet.GetLeft(pPage->getFrameArea());
+
+ const tools::Long nLeft = aTabCols.GetLeftMin();
+
+ if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) )
+ {
+ for ( size_t i = 0; i < aTabCols.Count(); ++i )
+ if ( ::IsSame( nX, nLeft + aTabCols[i] ) )
+ {
+ nRet = i + 1;
+ break;
+ }
+ }
+ }
+ return nRet;
+}
+
+static const SwFrame *lcl_FindFrameInTab( const SwLayoutFrame *pLay, const Point &rPt, SwTwips nFuzzy )
+{
+ const SwFrame *pFrame = pLay->Lower();
+
+ while( pFrame && pLay->IsAnLower( pFrame ) )
+ {
+ if ( pFrame->getFrameArea().IsNear( rPt, nFuzzy ) )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ {
+ const SwFrame *pTmp = ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), rPt, nFuzzy );
+ if ( pTmp )
+ return pTmp;
+ }
+
+ return pFrame;
+ }
+
+ pFrame = pFrame->FindNext();
+ }
+
+ return nullptr;
+}
+
+static const SwCellFrame *lcl_FindFrame( const SwLayoutFrame *pLay, const Point &rPt,
+ SwTwips nFuzzy, bool* pbRow, bool* pbCol )
+{
+ // bMouseMoveRowCols :
+ // Method is called for
+ // - Moving columns/rows with the mouse or
+ // - Enhanced table selection
+ const bool bMouseMoveRowCols = nullptr == pbCol;
+
+ bool bCloseToRow = false;
+ bool bCloseToCol = false;
+
+ const SwFrame *pFrame = pLay->ContainsContent();
+ const SwFrame* pRet = nullptr;
+
+ if ( pFrame )
+ {
+ do
+ {
+ if ( pFrame->IsInTab() )
+ pFrame = const_cast<SwFrame*>(pFrame)->ImplFindTabFrame();
+
+ if (!pFrame)
+ break;
+
+ if ( pFrame->IsTabFrame() )
+ {
+ Point aPt( rPt );
+ bool bSearchForFrameInTab = true;
+ SwTwips nTmpFuzzy = nFuzzy;
+
+ if ( !bMouseMoveRowCols )
+ {
+ // We ignore nested tables for the enhanced table selection:
+ while ( pFrame->GetUpper()->IsInTab() )
+ pFrame = pFrame->GetUpper()->FindTabFrame();
+
+ // We first check if the given point is 'close' to the left or top
+ // border of the table frame:
+ OSL_ENSURE( pFrame, "Nested table frame without outer table" );
+ SwRectFnSet aRectFnSet(pFrame);
+ const bool bRTL = pFrame->IsRightToLeft();
+
+ SwRect aTabRect = pFrame->getFramePrintArea();
+ aTabRect.Pos() += pFrame->getFrameArea().Pos();
+
+ const SwTwips nLeft = bRTL ?
+ aRectFnSet.GetRight(aTabRect) :
+ aRectFnSet.GetLeft(aTabRect);
+ const SwTwips nTop = aRectFnSet.GetTop(aTabRect);
+
+ SwTwips const rPointX = aRectFnSet.IsVert() ? aPt.Y() : aPt.X();
+ SwTwips const rPointY = aRectFnSet.IsVert() ? aPt.X() : aPt.Y();
+
+ const SwTwips nXDiff = aRectFnSet.XDiff( nLeft, rPointX ) * ( bRTL ? -1 : 1 );
+ const SwTwips nYDiff = aRectFnSet.YDiff( nTop, rPointY );
+
+ bCloseToRow = nXDiff >= 0 && nXDiff < nFuzzy;
+ bCloseToCol = nYDiff >= 0 && nYDiff < nFuzzy;
+
+ if ( bCloseToCol && 2 * nYDiff > nFuzzy )
+ {
+ const SwFrame* pPrev = pFrame->GetPrev();
+ if ( pPrev )
+ {
+ SwRect aPrevRect = pPrev->getFramePrintArea();
+ aPrevRect.Pos() += pPrev->getFrameArea().Pos();
+
+ if( aPrevRect.Contains( rPt ) )
+ {
+ bCloseToCol = false;
+ }
+ }
+
+ }
+
+ // If we found the point to be 'close' to the left or top border
+ // of the table frame, we adjust the point to be on that border:
+ if ( bCloseToRow && bCloseToCol )
+ aPt = bRTL ? aTabRect.TopRight() : aRectFnSet.GetPos(aTabRect);
+ else if ( bCloseToRow )
+ aRectFnSet.IsVert() ? aPt.setY(nLeft) : aPt.setX(nLeft);
+ else if ( bCloseToCol )
+ aRectFnSet.IsVert() ? aPt.setX(nTop) : aPt.setY(nTop);
+
+ if ( !bCloseToRow && !bCloseToCol )
+ bSearchForFrameInTab = false;
+
+ // Since the point has been adjusted, we call lcl_FindFrameInTab()
+ // with a fuzzy value of 1:
+ nTmpFuzzy = 1;
+ }
+
+ const SwFrame* pTmp = bSearchForFrameInTab ?
+ ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), aPt, nTmpFuzzy ) :
+ nullptr;
+
+ if ( pTmp )
+ {
+ pFrame = pTmp;
+ break;
+ }
+ }
+ pFrame = pFrame->FindNextCnt();
+
+ } while ( pFrame && pLay->IsAnLower( pFrame ) );
+ }
+
+ if ( pFrame && pFrame->IsInTab() && pLay->IsAnLower( pFrame ) )
+ {
+ do
+ {
+ // We allow mouse drag of table borders within nested tables,
+ // but disallow hotspot selection of nested tables.
+ if ( bMouseMoveRowCols )
+ {
+ // find the next cell frame
+ while ( pFrame && !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+ }
+ else
+ {
+ // find the most upper cell frame:
+ while ( pFrame &&
+ ( !pFrame->IsCellFrame() ||
+ !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
+ pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
+ pFrame = pFrame->GetUpper();
+ }
+
+ if ( pFrame ) // Note: this condition should be the same like the while condition!!!
+ {
+ // #i32329# Enhanced table selection
+ // used for hotspot selection of tab/cols/rows
+ if ( !bMouseMoveRowCols )
+ {
+
+ OSL_ENSURE( pbCol && pbRow, "pbCol or pbRow missing" );
+
+ if ( bCloseToRow || bCloseToCol )
+ {
+ *pbRow = bCloseToRow;
+ *pbCol = bCloseToCol;
+ pRet = pFrame;
+ break;
+ }
+ }
+ else
+ {
+ // used for mouse move of columns/rows
+ const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
+ SwRect aTabRect = pTabFrame->getFramePrintArea();
+ aTabRect.Pos() += pTabFrame->getFrameArea().Pos();
+
+ SwRectFnSet aRectFnSet(pTabFrame);
+
+ const SwTwips nTabTop = aRectFnSet.GetTop(aTabRect);
+ const SwTwips nMouseTop = aRectFnSet.IsVert() ? rPt.X() : rPt.Y();
+
+ // Do not allow to drag upper table border:
+ if ( !::IsSame( nTabTop, nMouseTop ) )
+ {
+ if ( ::IsSame( pFrame->getFrameArea().Left(), rPt.X() ) ||
+ ::IsSame( pFrame->getFrameArea().Right(),rPt.X() ) )
+ {
+ if ( pbRow ) *pbRow = false;
+ pRet = pFrame;
+ break;
+ }
+ if ( ::IsSame( pFrame->getFrameArea().Top(), rPt.Y() ) ||
+ ::IsSame( pFrame->getFrameArea().Bottom(),rPt.Y() ) )
+ {
+ if ( pbRow ) *pbRow = true;
+ pRet = pFrame;
+ break;
+ }
+ }
+ }
+
+ pFrame = pFrame->GetUpper();
+ }
+ } while ( pFrame );
+ }
+
+ // robust:
+ OSL_ENSURE( !pRet || pRet->IsCellFrame(), "lcl_FindFrame() is supposed to find a cell frame!" );
+ return pRet && pRet->IsCellFrame() ? static_cast<const SwCellFrame*>(pRet) : nullptr;
+}
+
+// pbCol = 0 => Used for moving table rows/cols with mouse
+// pbCol != 0 => Used for selecting table/rows/cols
+
+#define ENHANCED_TABLE_SELECTION_FUZZY 10
+
+const SwFrame* SwFEShell::GetBox( const Point &rPt, bool* pbRow, bool* pbCol ) const
+{
+ const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower());
+ vcl::Window* pOutWin = GetWin();
+ SwTwips nFuzzy = COLFUZZY;
+ if( pOutWin )
+ {
+ // #i32329# Enhanced table selection
+ SwTwips nSize = pbCol ? ENHANCED_TABLE_SELECTION_FUZZY : RULER_MOUSE_MARGINWIDTH;
+ Size aTmp( nSize, nSize );
+ aTmp = pOutWin->PixelToLogic( aTmp );
+ nFuzzy = aTmp.Width();
+ }
+
+ while ( pPage && !pPage->getFrameArea().IsNear( rPt, nFuzzy ) )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+
+ const SwCellFrame *pFrame = nullptr;
+ if ( pPage )
+ {
+ // We cannot search the box by GetModelPositionForViewPoint or GetContentPos.
+ // This would lead to a performance collapse for documents
+ // with a lot of paragraphs/tables on one page
+ //(BrowseMode!)
+
+ // check flys first
+ if ( pPage->GetSortedObjs() )
+ {
+ for ( size_t i = 0; !pFrame && i < pPage->GetSortedObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
+ if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ pFrame = lcl_FindFrame( pFlyFrame, rPt, nFuzzy, pbRow, pbCol );
+ }
+ }
+ }
+ const SwLayoutFrame *pLay = static_cast<const SwLayoutFrame*>(pPage->Lower());
+ while ( pLay && !pFrame )
+ {
+ pFrame = lcl_FindFrame( pLay, rPt, nFuzzy, pbRow, pbCol );
+ pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
+ }
+ }
+ return pFrame;
+}
+
+/* Helper function*/
+/* calculated the distance between Point rC and Line Segment (rA, rB) */
+static double lcl_DistancePoint2Segment( const Point& rA, const Point& rB, const Point& rC )
+{
+ double nRet = 0;
+
+ const basegfx::B2DVector aBC( rC.X() - rB.X(), rC.Y() - rB.Y() );
+ const basegfx::B2DVector aAB( rB.X() - rA.X(), rB.Y() - rA.Y() );
+ const double nDot1 = aBC.scalar( aAB );
+
+ if ( nDot1 > 0 ) // check outside case 1
+ nRet = aBC.getLength();
+ else
+ {
+ const basegfx::B2DVector aAC( rC.X() - rA.X(), rC.Y() - rA.Y() );
+ const basegfx::B2DVector aBA( rA.X() - rB.X(), rA.Y() - rB.Y() );
+ const double nDot2 = aAC.scalar( aBA );
+
+ if ( nDot2 > 0 ) // check outside case 2
+ nRet = aAC.getLength();
+ else
+ {
+ const double nDiv = aAB.getLength();
+ nRet = nDiv ? aAB.cross( aAC ) / nDiv : 0;
+ }
+ }
+
+ return std::abs(nRet);
+}
+
+/* Helper function*/
+static Point lcl_ProjectOntoClosestTableFrame( const SwTabFrame& rTab, const Point& rPoint, bool bRowDrag )
+{
+ Point aRet( rPoint );
+ const SwTabFrame* pCurrentTab = &rTab;
+ const bool bVert = pCurrentTab->IsVertical();
+ const bool bRTL = pCurrentTab->IsRightToLeft();
+
+ // Western Layout:
+ // bRowDrag = true => compare to left border of table
+ // bRowDrag = false => compare to top border of table
+
+ // Asian Layout:
+ // bRowDrag = true => compare to right border of table
+ // bRowDrag = false => compare to top border of table
+
+ // RTL Layout:
+ // bRowDrag = true => compare to right border of table
+ // bRowDrag = false => compare to top border of table
+ bool bLeft = false;
+ bool bRight = false;
+
+ if ( bRowDrag )
+ {
+ if ( bVert || bRTL )
+ bRight = true;
+ else
+ bLeft = true;
+ }
+
+ // used to find the minimal distance
+ double nMin = -1;
+ Point aMin1;
+ Point aMin2;
+
+ Point aS1;
+ Point aS2;
+
+ while ( pCurrentTab )
+ {
+ SwRect aTabRect( pCurrentTab->getFramePrintArea() );
+ aTabRect += pCurrentTab->getFrameArea().Pos();
+
+ if ( bLeft )
+ {
+ // distance to left table border
+ aS1 = aTabRect.TopLeft();
+ aS2 = aTabRect.BottomLeft();
+ }
+ else if ( bRight )
+ {
+ // distance to right table border
+ aS1 = aTabRect.TopRight();
+ aS2 = aTabRect.BottomRight();
+ }
+ else //if ( bTop )
+ {
+ // distance to top table border
+ aS1 = aTabRect.TopLeft();
+ aS2 = aTabRect.TopRight();
+ }
+
+ const double nDist = lcl_DistancePoint2Segment( aS1, aS2, rPoint );
+
+ if ( nDist < nMin || -1 == nMin )
+ {
+ aMin1 = aS1;
+ aMin2 = aS2;
+ nMin = nDist;
+ }
+
+ pCurrentTab = pCurrentTab->GetFollow();
+ }
+
+ // project onto closest line:
+ if ( bLeft || bRight )
+ {
+ aRet.setX(aMin1.getX());
+ if ( aRet.getY() > aMin2.getY() )
+ aRet.setY(aMin2.getY());
+ else if ( aRet.getY() < aMin1.getY() )
+ aRet.setY(aMin1.getY());
+ }
+ else
+ {
+ aRet.setY(aMin1.getY());
+ if ( aRet.getX() > aMin2.getX() )
+ aRet.setX(aMin2.getX());
+ else if ( aRet.getX() < aMin1.getX() )
+ aRet.setX(aMin1.getX());
+ }
+
+ return aRet;
+}
+
+// #i32329# Enhanced table selection
+bool SwFEShell::SelTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag )
+{
+ bool bRet = false;
+ Point aEndPt;
+ if ( pEnd )
+ aEndPt = *pEnd;
+
+ SwPosition* ppPos[2] = { nullptr, nullptr };
+ Point paPt [2] = { rPt, aEndPt };
+ bool pbRow[2] = { false, false };
+ bool pbCol[2] = { false, false };
+
+ // pEnd is set during dragging.
+ for ( sal_uInt16 i = 0; i < ( pEnd ? 2 : 1 ); ++i )
+ {
+ const SwCellFrame* pFrame =
+ static_cast<const SwCellFrame*>(GetBox( paPt[i], &pbRow[i], &pbCol[i] ) );
+
+ if( pFrame )
+ {
+ while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() )
+ pFrame = static_cast<const SwCellFrame*>( static_cast<const SwLayoutFrame*>( pFrame->Lower() )->Lower() );
+ if( pFrame && pFrame->GetTabBox()->GetSttNd() &&
+ pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() )
+ pFrame = nullptr;
+ }
+
+ if ( pFrame )
+ {
+ const SwContentFrame* pContent = ::GetCellContent( *pFrame );
+
+ if ( pContent && pContent->IsTextFrame() )
+ {
+
+ ppPos[i] = new SwPosition(static_cast<SwTextFrame const*>(pContent)->MapViewToModelPos(TextFrameIndex(0)));
+
+ // paPt[i] will not be used any longer, now we use it to store
+ // a position inside the content frame
+ paPt[i] = pContent->getFrameArea().Center();
+ }
+ }
+
+ // no calculation of end frame if start frame has not been found.
+ if ( 1 == i || !ppPos[0] || !pEnd || !pFrame )
+ break;
+
+ // find 'closest' table frame to pEnd:
+ const SwTabFrame* pCurrentTab = pFrame->FindTabFrame();
+ if ( pCurrentTab->IsFollow() )
+ pCurrentTab = pCurrentTab->FindMaster( true );
+
+ const Point aProjection = lcl_ProjectOntoClosestTableFrame( *pCurrentTab, *pEnd, bRowDrag );
+ paPt[1] = aProjection;
+ }
+
+ if ( ppPos[0] )
+ {
+ SwShellCursor* pCursor = GetCursor_();
+ SwCursorSaveState aSaveState( *pCursor );
+ SwPosition aOldPos( *pCursor->GetPoint() );
+
+ pCursor->DeleteMark();
+ *pCursor->GetPoint() = *ppPos[0];
+ pCursor->GetPtPos() = paPt[0];
+
+ if ( !pCursor->IsInProtectTable() )
+ {
+ bool bNewSelection = true;
+
+ if ( ppPos[1] )
+ {
+ if ( ppPos[1]->nNode.GetNode().StartOfSectionNode() !=
+ aOldPos.nNode.GetNode().StartOfSectionNode() )
+ {
+ pCursor->SetMark();
+ SwCursorSaveState aSaveState2( *pCursor );
+ *pCursor->GetPoint() = *ppPos[1];
+ pCursor->GetPtPos() = paPt[1];
+
+ if ( pCursor->IsInProtectTable( false, false ) )
+ {
+ pCursor->RestoreSavePos();
+ bNewSelection = false;
+ }
+ }
+ else
+ {
+ pCursor->RestoreSavePos();
+ bNewSelection = false;
+ }
+ }
+
+ if ( bNewSelection )
+ {
+ // #i35543# SelTableRowCol should remove any existing
+ // table cursor:
+ if ( IsTableMode() )
+ TableCursorToCursor();
+
+ if ( pbRow[0] && pbCol[0] )
+ bRet = SwCursorShell::SelTable();
+ else if ( pbRow[0] )
+ bRet = SwCursorShell::SelTableRowOrCol( true, true );
+ else if ( pbCol[0] )
+ bRet = SwCursorShell::SelTableRowOrCol( false, true );
+ }
+ else
+ bRet = true;
+ }
+
+ delete ppPos[0];
+ delete ppPos[1];
+ }
+
+ return bRet;
+}
+
+SwTab SwFEShell::WhichMouseTabCol( const Point &rPt ) const
+{
+ SwTab nRet = SwTab::COL_NONE;
+ bool bRow = false;
+ bool bCol = false;
+ bool bSelect = false;
+
+ // First try: Do we get the row/col move cursor?
+ const SwCellFrame* pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow ));
+
+ if ( !pFrame )
+ {
+ // Second try: Do we get the row/col/tab selection cursor?
+ pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow, &bCol ));
+ bSelect = true;
+ }
+
+ if( pFrame )
+ {
+ while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() )
+ pFrame = static_cast<const SwCellFrame*>(static_cast<const SwLayoutFrame*>(pFrame->Lower())->Lower());
+ if( pFrame && pFrame->GetTabBox()->GetSttNd() &&
+ pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() )
+ pFrame = nullptr;
+ }
+
+ if( pFrame )
+ {
+ if ( !bSelect )
+ {
+ if ( pFrame->IsVertical() )
+ nRet = bRow ? SwTab::COL_VERT : SwTab::ROW_VERT;
+ else
+ nRet = bRow ? SwTab::ROW_HORI : SwTab::COL_HORI;
+ }
+ else
+ {
+ const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
+ if ( pTabFrame->IsVertical() )
+ {
+ if ( bRow && bCol )
+ {
+ nRet = SwTab::SEL_VERT;
+ }
+ else if ( bRow )
+ {
+ nRet = SwTab::ROWSEL_VERT;
+ }
+ else if ( bCol )
+ {
+ nRet = SwTab::COLSEL_VERT;
+ }
+ }
+ else
+ {
+ if ( bRow && bCol )
+ {
+ nRet = pTabFrame->IsRightToLeft() ?
+ SwTab::SEL_HORI_RTL :
+ SwTab::SEL_HORI;
+ }
+ else if ( bRow )
+ {
+ nRet = pTabFrame->IsRightToLeft() ?
+ SwTab::ROWSEL_HORI_RTL :
+ SwTab::ROWSEL_HORI;
+ }
+ else if ( bCol )
+ {
+ nRet = SwTab::COLSEL_HORI;
+ }
+ }
+ }
+ }
+
+ return nRet;
+}
+
+// -> #i23726#
+SwTextNode * SwFEShell::GetNumRuleNodeAtPos( const Point &rPt)
+{
+ SwTextNode * pResult = nullptr;
+
+ SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel);
+
+ if( GetContentAtPos(rPt, aContentAtPos) && aContentAtPos.aFnd.pNode)
+ pResult = aContentAtPos.aFnd.pNode->GetTextNode();
+
+ return pResult;
+}
+
+bool SwFEShell::IsNumLabel( const Point &rPt, int nMaxOffset )
+{
+ bool bResult = false;
+
+ SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel);
+
+ if( GetContentAtPos(rPt, aContentAtPos))
+ {
+ if ((nMaxOffset >= 0 && aContentAtPos.nDist <= nMaxOffset) ||
+ (nMaxOffset < 0))
+ bResult = true;
+ }
+
+ return bResult;
+}
+// <- #i23726#
+
+// #i42921#
+bool SwFEShell::IsVerticalModeAtNdAndPos( const SwTextNode& _rTextNode,
+ const Point& _rDocPos )
+{
+ bool bRet( false );
+
+ const SvxFrameDirection nTextDir =
+ _rTextNode.GetTextDirection( SwPosition(_rTextNode), &_rDocPos );
+ switch ( nTextDir )
+ {
+ case SvxFrameDirection::Unknown:
+ case SvxFrameDirection::Horizontal_RL_TB:
+ case SvxFrameDirection::Horizontal_LR_TB:
+ {
+ bRet = false;
+ }
+ break;
+ case SvxFrameDirection::Vertical_LR_TB:
+ case SvxFrameDirection::Vertical_RL_TB:
+ {
+ bRet = true;
+ }
+ break;
+ default: break;
+ }
+
+ return bRet;
+}
+
+void SwFEShell::GetMouseTabCols( SwTabCols &rToFill, const Point &rPt ) const
+{
+ const SwFrame *pBox = GetBox( rPt );
+ if ( pBox )
+ GetTabCols_( rToFill, pBox );
+}
+
+void SwFEShell::SetMouseTabCols( const SwTabCols &rNew, bool bCurRowOnly,
+ const Point &rPt )
+{
+ const SwFrame *pBox = GetBox( rPt );
+ if( pBox )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+ GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<const SwCellFrame*>(pBox) );
+ EndAllActionAndCall();
+ }
+}
+
+sal_uInt16 SwFEShell::GetCurMouseColNum( const Point &rPt ) const
+{
+ return GetCurColNum_( GetBox( rPt ), nullptr );
+}
+
+size_t SwFEShell::GetCurMouseTabColNum( const Point &rPt ) const
+{
+ //!!!GetCurTabColNum() mitpflegen!!!!
+ size_t nRet = 0;
+
+ const SwFrame *pFrame = GetBox( rPt );
+ OSL_ENSURE( pFrame, "Table not found" );
+ if( pFrame )
+ {
+ const tools::Long nX = pFrame->getFrameArea().Left();
+
+ // get TabCols, only via these we get the position
+ SwTabCols aTabCols;
+ GetMouseTabCols( aTabCols, rPt );
+
+ const tools::Long nLeft = aTabCols.GetLeftMin();
+
+ if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) )
+ {
+ for ( size_t i = 0; i < aTabCols.Count(); ++i )
+ if ( ::IsSame( nX, nLeft + aTabCols[i] ) )
+ {
+ nRet = i + 1;
+ break;
+ }
+ }
+ }
+ return nRet;
+}
+
+void ClearFEShellTabCols(SwDoc & rDoc, SwTabFrame const*const pFrame)
+{
+ auto const pShell(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
+ if (pShell)
+ {
+ for (SwViewShell& rCurrentShell : pShell->GetRingContainer())
+ {
+ if (auto const pFE = dynamic_cast<SwFEShell *>(&rCurrentShell))
+ {
+ pFE->ClearColumnRowCache(pFrame);
+ }
+ }
+ }
+}
+
+void SwFEShell::ClearColumnRowCache(SwTabFrame const*const pFrame)
+{
+ if (m_pColumnCache)
+ {
+ if (pFrame == nullptr || pFrame == m_pColumnCache->pLastTabFrame)
+ {
+ m_pColumnCache.reset();
+ }
+ }
+ if (m_pRowCache)
+ {
+ if (pFrame == nullptr || pFrame == m_pRowCache->pLastTabFrame)
+ {
+ m_pRowCache.reset();
+ }
+ }
+}
+
+void SwFEShell::GetTableAttr( SfxItemSet &rSet ) const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if( pFrame && pFrame->IsInTab() )
+ rSet.Put( pFrame->ImplFindTabFrame()->GetFormat()->GetAttrSet() );
+}
+
+void SwFEShell::SetTableAttr( const SfxItemSet &rNew )
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if( pFrame && pFrame->IsInTab() )
+ {
+ CurrShell aCurr( this );
+ StartAllAction();
+ SwTabFrame *pTab = pFrame->FindTabFrame();
+ pTab->GetTable()->SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
+ GetDoc()->SetAttr( rNew, *pTab->GetFormat() );
+ GetDoc()->getIDocumentState().SetModified();
+ EndAllActionAndCall();
+ }
+}
+
+// change a cell width/cell height/column width/row height
+void SwFEShell::SetColRowWidthHeight( TableChgWidthHeightType eType, sal_uInt16 nDiff )
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if( !pFrame || !pFrame->IsInTab() )
+ return;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+
+ do {
+ pFrame = pFrame->GetUpper();
+ } while( !pFrame->IsCellFrame() );
+
+ SwTabFrame *pTab = pFrame->ImplFindTabFrame();
+
+ // if the table is in relative values (USHRT_MAX)
+ // then it should be recalculated to absolute values now
+ const SwFormatFrameSize& rTableFrameSz = pTab->GetFormat()->GetFrameSize();
+ SwRectFnSet aRectFnSet(pTab);
+ tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ TableChgWidthHeightType eTypePos = extractPosition(eType);
+ if( TableChgMode::VarWidthChangeAbs == pTab->GetTable()->GetTableChgMode() &&
+ ( eTypePos == TableChgWidthHeightType::ColLeft || eTypePos == TableChgWidthHeightType::ColRight ) &&
+ text::HoriOrientation::NONE == pTab->GetFormat()->GetHoriOrient().GetHoriOrient() &&
+ nPrtWidth != rTableFrameSz.GetWidth() )
+ {
+ SwFormatFrameSize aSz( rTableFrameSz );
+ aSz.SetWidth( pTab->getFramePrintArea().Width() );
+ pTab->GetFormat()->SetFormatAttr( aSz );
+ }
+
+ SwTwips nLogDiff = nDiff;
+ nLogDiff *= pTab->GetFormat()->GetFrameSize().GetWidth();
+ nLogDiff /= nPrtWidth;
+
+ /** The cells are destroyed in here */
+ GetDoc()->SetColRowWidthHeight(
+ *const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()),
+ eType, nDiff, nLogDiff );
+
+ ClearFEShellTabCols(*GetDoc(), nullptr);
+ EndAllActionAndCall();
+}
+
+static bool lcl_IsFormulaSelBoxes( const SwTable& rTable, const SwTableBoxFormula& rFormula,
+ SwCellFrames& rCells )
+{
+ SwTableBoxFormula aTmp( rFormula );
+ SwSelBoxes aBoxes;
+ aTmp.GetBoxesOfFormula(rTable, aBoxes);
+ for (size_t nSelBoxes = aBoxes.size(); nSelBoxes; )
+ {
+ SwTableBox* pBox = aBoxes[ --nSelBoxes ];
+
+ if( std::none_of(rCells.begin(), rCells.end(), [&pBox](SwCellFrame* pFrame) { return pFrame->GetTabBox() == pBox; }) )
+ return false;
+ }
+
+ return true;
+}
+
+ // ask formula for auto-sum
+void SwFEShell::GetAutoSum( OUString& rFormula ) const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ SwTabFrame *pTab = pFrame ? pFrame->ImplFindTabFrame() : nullptr;
+ if( !pTab )
+ return;
+
+ SwCellFrames aCells;
+ OUString sFields;
+ if( ::GetAutoSumSel( *this, aCells ))
+ {
+ sal_uInt16 nW = 0;
+ for( size_t n = aCells.size(); n; )
+ {
+ SwCellFrame* pCFrame = aCells[ --n ];
+ sal_uInt16 nBoxW = pCFrame->GetTabBox()->IsFormulaOrValueBox();
+ if( !nBoxW )
+ break;
+
+ if( !nW )
+ {
+ if( USHRT_MAX == nBoxW )
+ continue; // skip space at beginning
+
+ // formula only if box is contained
+ if( RES_BOXATR_FORMULA == nBoxW &&
+ !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
+ GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells))
+ {
+ nW = RES_BOXATR_VALUE;
+ // restore previous spaces!
+ for( size_t i = aCells.size(); n+1 < i; )
+ {
+ sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">"
+ + sFields;
+ }
+ }
+ else
+ nW = nBoxW;
+ }
+ else if( RES_BOXATR_VALUE == nW )
+ {
+ // search for values, Value/Formula/Text found -> include
+ if( RES_BOXATR_FORMULA == nBoxW &&
+ ::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
+ GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells ))
+ break;
+ else if( USHRT_MAX != nBoxW )
+ sFields = OUStringChar(cListDelim) + sFields;
+ else
+ break;
+ }
+ else if( RES_BOXATR_FORMULA == nW )
+ {
+ // only continue search when the current formula points to
+ // all boxes contained in the selection
+ if( RES_BOXATR_FORMULA == nBoxW )
+ {
+ if( !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
+ GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells ))
+ {
+ // redo only for values!
+
+ nW = RES_BOXATR_VALUE;
+ sFields.clear();
+ // restore previous spaces!
+ for( size_t i = aCells.size(); n+1 < i; )
+ {
+ sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">"
+ + sFields;
+ }
+ }
+ else
+ sFields = OUStringChar(cListDelim) + sFields;
+ }
+ else if( USHRT_MAX == nBoxW )
+ break;
+ else
+ continue; // ignore this box
+ }
+ else
+ // all other stuff terminates the loop
+ // possibly allow texts??
+ break;
+
+ sFields = "<" + pCFrame->GetTabBox()->GetName() + ">" + sFields;
+ }
+ }
+
+ rFormula = OUString::createFromAscii( sCalc_Sum );
+ if (!sFields.isEmpty())
+ {
+ rFormula += "(" + sFields + ")";
+ }
+}
+
+bool SwFEShell::IsTableRightToLeft() const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr;
+ if (!pTab)
+ return false;
+ return pTab->IsRightToLeft();
+}
+
+bool SwFEShell::IsMouseTableRightToLeft(const Point &rPt) const
+{
+ SwFrame *pFrame = const_cast<SwFrame *>(GetBox( rPt ));
+ const SwTabFrame* pTabFrame = pFrame ? pFrame->ImplFindTabFrame() : nullptr;
+ OSL_ENSURE( pTabFrame, "Table not found" );
+ return pTabFrame && pTabFrame->IsRightToLeft();
+}
+
+bool SwFEShell::IsTableVertical() const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr;
+ if (!pTab)
+ return false;
+ return pTab->IsVertical();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/fews.cxx b/sw/source/core/frmedt/fews.cxx
new file mode 100644
index 000000000..0e9a0a2ca
--- /dev/null
+++ b/sw/source/core/frmedt/fews.cxx
@@ -0,0 +1,1332 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <svx/svdobj.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+#include <init.hxx>
+#include <fesh.hxx>
+#include <tabcol.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <doc.hxx>
+#include <frmtool.hxx>
+#include <swtable.hxx>
+#include <viewimp.hxx>
+#include <dview.hxx>
+#include <flyfrm.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <sectfrm.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtfsize.hxx>
+#include <tabfrm.hxx>
+#include <flyfrms.hxx>
+#include <txtfrm.hxx>
+#include <mdiexp.hxx>
+#include <pagedesc.hxx>
+#include <fmtanchr.hxx>
+#include <environmentofanchoredobject.hxx>
+#include <ndtxt.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <UndoInsert.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/**
+ * This mutex is only used for the paste listeners, where the solar mutex can't
+ * be used.
+ */
+osl::Mutex& GetPasteMutex()
+{
+ static osl::Mutex aMutex;
+ return aMutex;
+}
+}
+
+void SwFEShell::EndAllActionAndCall()
+{
+ for(SwViewShell& rCurrentShell : GetRingContainer())
+ {
+ if( dynamic_cast<const SwCursorShell*>( &rCurrentShell) != nullptr )
+ {
+ static_cast<SwFEShell*>(&rCurrentShell)->EndAction();
+ static_cast<SwFEShell*>(&rCurrentShell)->CallChgLnk();
+ }
+ else
+ rCurrentShell.EndAction();
+ }
+}
+
+// Determine the Content's nearest to the point
+Point SwFEShell::GetContentPos( const Point& rPoint, bool bNext ) const
+{
+ CurrShell aCurr( const_cast<SwFEShell*>(this) );
+ return GetLayout()->GetNextPrevContentPos( rPoint, bNext );
+}
+
+const SwRect& SwFEShell::GetAnyCurRect( CurRectType eType, const Point* pPt,
+ const uno::Reference < embed::XEmbeddedObject >& xObj ) const
+{
+ const SwFrame *pFrame = Imp()->HasDrawView()
+ ? ::GetFlyFromMarked( &Imp()->GetDrawView()->GetMarkedObjectList(),
+ const_cast<SwFEShell*>(this))
+ : nullptr;
+
+ if( !pFrame )
+ {
+ if( pPt )
+ {
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( *pPt );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
+ SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(*pPt, true);
+ pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ }
+ else
+ {
+ const bool bOldCallbackActionEnabled = GetLayout()->IsCallbackActionEnabled();
+ if( bOldCallbackActionEnabled )
+ GetLayout()->SetCallbackActionEnabled( false );
+ pFrame = GetCurrFrame();
+ if( bOldCallbackActionEnabled )
+ GetLayout()->SetCallbackActionEnabled( true );
+ }
+ }
+
+ if( !pFrame )
+ return GetLayout()->getFrameArea();
+
+ bool bFrame = true;
+ switch ( eType )
+ {
+ case CurRectType::PagePrt: bFrame = false;
+ [[fallthrough]];
+ case CurRectType::Page : pFrame = pFrame->FindPageFrame();
+ break;
+
+ case CurRectType::PageCalc:
+ {
+ DisableCallbackAction a(const_cast<SwRootFrame&>(*pFrame->getRootFrame()));
+ pFrame->Calc(Imp()->GetShell()->GetOut());
+ pFrame = pFrame->FindPageFrame();
+ pFrame->Calc(Imp()->GetShell()->GetOut());
+ }
+ break;
+
+ case CurRectType::FlyEmbeddedPrt:
+ bFrame = false;
+ [[fallthrough]];
+ case CurRectType::FlyEmbedded:
+ {
+ const SwFrame *pFlyFrame = xObj.is() ? FindFlyFrame(xObj) : nullptr;
+ pFrame = pFlyFrame ? pFlyFrame
+ : pFrame->IsFlyFrame()
+ ? pFrame
+ : pFrame->FindFlyFrame();
+ break;
+ }
+ case CurRectType::SectionOutsideTable :
+ if( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+ else {
+ OSL_FAIL( "Missing Table" );
+ }
+ [[fallthrough]];
+ case CurRectType::SectionPrt:
+ case CurRectType::Section:
+ if( pFrame->IsInSct() )
+ pFrame = pFrame->FindSctFrame();
+ else {
+ OSL_FAIL( "Missing section" );
+ }
+
+ if( CurRectType::SectionPrt == eType )
+ bFrame = false;
+ break;
+
+ case CurRectType::HeaderFooter:
+ pFrame = pFrame->FindFooterOrHeader();
+ if( nullptr == pFrame )
+ return GetLayout()->getFrameArea();
+ break;
+
+ case CurRectType::PagesArea:
+ return GetLayout()->GetPagesArea();
+
+ default: break;
+ }
+ return bFrame ? pFrame->getFrameArea() : pFrame->getFramePrintArea();
+}
+
+sal_uInt16 SwFEShell::GetPageNumber( const Point &rPoint ) const
+{
+ const SwFrame *pPage = GetLayout()->Lower();
+ while ( pPage && !pPage->getFrameArea().Contains( rPoint ) )
+ pPage = pPage->GetNext();
+ if ( pPage )
+ return static_cast<const SwPageFrame*>(pPage)->GetPhyPageNum();
+ else
+ return 0;
+}
+
+bool SwFEShell::GetPageNumber( tools::Long nYPos, bool bAtCursorPos, sal_uInt16& rPhyNum, sal_uInt16& rVirtNum, OUString &rDisplay) const
+{
+ const SwFrame *pPage;
+
+ if ( bAtCursorPos ) // get page of Cursor
+ {
+ pPage = GetCurrFrame( false );
+ if ( pPage )
+ pPage = pPage->FindPageFrame();
+ }
+ else if ( nYPos > -1 ) // determine page via the position
+ {
+ pPage = GetLayout()->Lower();
+ while( pPage && (pPage->getFrameArea().Bottom() < nYPos ||
+ nYPos < pPage->getFrameArea().Top() ) )
+ pPage = pPage->GetNext();
+ }
+ else // first visible page
+ {
+ pPage = Imp()->GetFirstVisPage(GetOut());
+ if ( pPage && static_cast<const SwPageFrame*>(pPage)->IsEmptyPage() )
+ pPage = pPage->GetNext();
+ }
+
+ if( pPage )
+ {
+ rPhyNum = static_cast<const SwPageFrame*>(pPage)->GetPhyPageNum();
+ rVirtNum = static_cast<const SwPageFrame*>(pPage)->GetVirtPageNum();
+ const SvxNumberType& rNum = static_cast<const SwPageFrame*>(pPage)->GetPageDesc()->GetNumType();
+ rDisplay = rNum.GetNumStr( rVirtNum );
+ }
+
+ return nullptr != pPage;
+}
+
+bool SwFEShell::IsDirectlyInSection() const
+{
+ SwFrame* pFrame = GetCurrFrame( false );
+ return pFrame && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame();
+}
+
+FrameTypeFlags SwFEShell::GetFrameType( const Point *pPt, bool bStopAtFly ) const
+{
+ FrameTypeFlags nReturn = FrameTypeFlags::NONE;
+ const SwFrame *pFrame;
+ if ( pPt )
+ {
+ SwPosition aPos( *GetCursor()->GetPoint() );
+ Point aPt( *pPt );
+ GetLayout()->GetModelPositionForViewPoint( &aPos, aPt );
+ SwContentNode *pNd = aPos.nNode.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(*pPt, true);
+ pFrame = pNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
+ }
+ else
+ pFrame = GetCurrFrame( false );
+ while ( pFrame )
+ {
+ switch ( pFrame->GetType() )
+ {
+ case SwFrameType::Column:
+ if( pFrame->GetUpper()->IsSctFrame() )
+ {
+ // Check, if isn't not only a single column
+ // from a section with footnotes at the end.
+ if( pFrame->GetNext() || pFrame->GetPrev() )
+ // Sectioncolumns
+ nReturn |= ( nReturn & FrameTypeFlags::TABLE ) ?
+ FrameTypeFlags::COLSECTOUTTAB : FrameTypeFlags::COLSECT;
+ }
+ else // only pages and frame columns
+ nReturn |= FrameTypeFlags::COLUMN;
+ break;
+ case SwFrameType::Page:
+ nReturn |= FrameTypeFlags::PAGE;
+ if( static_cast<const SwPageFrame*>(pFrame)->IsFootnotePage() )
+ nReturn |= FrameTypeFlags::FTNPAGE;
+ break;
+ case SwFrameType::Header: nReturn |= FrameTypeFlags::HEADER; break;
+ case SwFrameType::Footer: nReturn |= FrameTypeFlags::FOOTER; break;
+ case SwFrameType::Body:
+ if( pFrame->GetUpper()->IsPageFrame() ) // not for ColumnFrames
+ nReturn |= FrameTypeFlags::BODY;
+ break;
+ case SwFrameType::Ftn: nReturn |= FrameTypeFlags::FOOTNOTE; break;
+ case SwFrameType::Fly:
+ if( static_cast<const SwFlyFrame*>(pFrame)->IsFlyLayFrame() )
+ nReturn |= FrameTypeFlags::FLY_FREE;
+ else if ( static_cast<const SwFlyFrame*>(pFrame)->IsFlyAtContentFrame() )
+ nReturn |= FrameTypeFlags::FLY_ATCNT;
+ else
+ {
+ OSL_ENSURE( static_cast<const SwFlyFrame*>(pFrame)->IsFlyInContentFrame(),
+ "New frametype?" );
+ nReturn |= FrameTypeFlags::FLY_INCNT;
+ }
+ nReturn |= FrameTypeFlags::FLY_ANY;
+ if( bStopAtFly )
+ return nReturn;
+ break;
+ case SwFrameType::Tab:
+ case SwFrameType::Row:
+ case SwFrameType::Cell: nReturn |= FrameTypeFlags::TABLE; break;
+ default: /* do nothing */ break;
+ }
+ if ( pFrame->IsFlyFrame() )
+ pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ else
+ pFrame = pFrame->GetUpper();
+ }
+ return nReturn;
+}
+
+void SwFEShell::ShellGetFocus()
+{
+ ::SetShell( this );
+ SwCursorShell::ShellGetFocus();
+
+ if ( HasDrawView() )
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ Imp()->GetDrawView()->showMarkHandles();
+ if ( Imp()->GetDrawView()->AreObjectsMarked() )
+ FrameNotify( this, FLY_DRAG_START );
+ }
+}
+
+void SwFEShell::ShellLoseFocus()
+{
+ SwCursorShell::ShellLoseFocus();
+
+ if ( HasDrawView() && Imp()->GetDrawView()->AreObjectsMarked() )
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ Imp()->GetDrawView()->hideMarkHandles();
+ FrameNotify( this, FLY_DRAG_END );
+ }
+}
+
+sal_uInt16 SwFEShell::GetPhyPageNum() const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if ( pFrame )
+ return pFrame->GetPhyPageNum();
+ return 0;
+}
+
+sal_uInt16 SwFEShell::GetVirtPageNum() const
+{
+ SwFrame *pFrame = GetCurrFrame();
+ if ( pFrame )
+ return pFrame->GetVirtPageNum();
+ return 0;
+}
+
+static void lcl_SetAPageOffset( sal_uInt16 nOffset, SwPageFrame* pPage, SwFEShell* pThis )
+{
+ pThis->StartAllAction();
+ OSL_ENSURE( pPage->FindFirstBodyContent(),
+ "SwFEShell _SetAPageOffset() without ContentFrame" );
+
+ SwFormatPageDesc aDesc( pPage->GetPageDesc() );
+ aDesc.SetNumOffset( nOffset );
+
+ SwFrame *pFrame = pThis->GetCurrFrame( false );
+ if ( pFrame->IsInTab() )
+ pThis->GetDoc()->SetAttr( aDesc, *pFrame->FindTabFrame()->GetFormat() );
+ else
+ {
+ pThis->GetDoc()->getIDocumentContentOperations().InsertPoolItem(
+ *pThis->GetCursor(), aDesc, SetAttrMode::DEFAULT, pThis->GetLayout());
+ }
+
+ pThis->EndAllAction();
+}
+
+void SwFEShell::SetNewPageOffset( sal_uInt16 nOffset )
+{
+ GetLayout()->SetVirtPageNum( true );
+ const SwPageFrame *pPage = GetCurrFrame( false )->FindPageFrame();
+ lcl_SetAPageOffset( nOffset, const_cast<SwPageFrame*>(pPage), this );
+}
+
+void SwFEShell::SetPageOffset( sal_uInt16 nOffset )
+{
+ const SwPageFrame *pPage = GetCurrFrame( false )->FindPageFrame();
+ const SwRootFrame* pDocLayout = GetLayout();
+ while ( pPage )
+ {
+ const SwFrame *pFlow = pPage->FindFirstBodyContent();
+ if ( pFlow )
+ {
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
+ if ( rPgDesc.GetNumOffset() )
+ {
+ pDocLayout->SetVirtPageNum( true );
+ lcl_SetAPageOffset( nOffset, const_cast<SwPageFrame*>(pPage), this );
+ break;
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
+ }
+}
+
+sal_uInt16 SwFEShell::GetPageOffset() const
+{
+ const SwPageFrame *pPage = GetCurrFrame()->FindPageFrame();
+ while ( pPage )
+ {
+ const SwFrame *pFlow = pPage->FindFirstBodyContent();
+ if ( pFlow )
+ {
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ ::std::optional<sal_uInt16> oNumOffset = pFlow->GetPageDescItem().GetNumOffset();
+ if ( oNumOffset )
+ return *oNumOffset;
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
+ }
+ return 0;
+}
+
+void SwFEShell::InsertLabel( const SwLabelType eType, const OUString &rText, const OUString& rSeparator,
+ const OUString& rNumberSeparator,
+ const bool bBefore, const sal_uInt16 nId,
+ const OUString& rCharacterStyle,
+ const bool bCpyBrd )
+{
+ // get node index of cursor position, SwDoc can do everything else itself
+ SwContentFrame *pCnt = SwLabelType::Draw==eType ? nullptr : GetCurrFrame( false );
+ if( SwLabelType::Draw!=eType && !pCnt )
+ return;
+
+ StartAllAction();
+ SwRewriter aRewriter(SwUndoInsertLabel::CreateRewriter(rText));
+ StartUndo(SwUndoId::INSERTLABEL, &aRewriter);
+
+ SwNodeOffset nIdx(0);
+ bool bInnerCntIsFly = false;
+ SwFlyFrameFormat* pFlyFormat = nullptr;
+ switch( eType )
+ {
+ case SwLabelType::Object:
+ case SwLabelType::Fly:
+ bInnerCntIsFly = pCnt->IsInFly();
+ if (bInnerCntIsFly)
+ {
+ // pass down index to the startnode for flys
+ nIdx = pCnt->FindFlyFrame()->
+ GetFormat()->GetContent().GetContentIdx()->GetIndex();
+ }
+ break;
+ case SwLabelType::Table:
+ if( pCnt->IsInTab() )
+ {
+ // pass down index to the TableNode for tables
+ const SwTable& rTable = *pCnt->FindTabFrame()->GetTable();
+ nIdx = rTable.GetTabSortBoxes()[ 0 ]
+ ->GetSttNd()->FindTableNode()->GetIndex();
+ }
+ break;
+ case SwLabelType::Draw:
+ if( Imp()->GetDrawView() )
+ {
+ SwDrawView *pDView = Imp()->GetDrawView();
+ const SdrMarkList& rMrkList = pDView->GetMarkedObjectList();
+
+ // copy marked drawing objects to
+ // local list to perform the corresponding action for each object
+ std::vector<SdrObject*> aDrawObjs;
+ {
+ for ( size_t i = 0; i < rMrkList.GetMarkCount(); ++i )
+ {
+ SdrObject* pDrawObj = rMrkList.GetMark(i)->GetMarkedSdrObj();
+ if( pDrawObj )
+ aDrawObjs.push_back( pDrawObj );
+ }
+ }
+ // loop on marked drawing objects
+ while ( !aDrawObjs.empty() )
+ {
+ SdrObject* pDrawObj = aDrawObjs.back();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pDrawObj) == nullptr &&
+ dynamic_cast<const SwFlyDrawObj*>( pDrawObj) == nullptr )
+ {
+ SwFlyFrameFormat *pFormat =
+ GetDoc()->InsertDrawLabel( rText, rSeparator, rNumberSeparator, nId, rCharacterStyle, *pDrawObj );
+ if( !pFlyFormat )
+ pFlyFormat = pFormat;
+ }
+
+ aDrawObjs.pop_back();
+ }
+
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "Cursor neither in table nor in fly." );
+ }
+
+ if( nIdx )
+ {
+ pFlyFormat = GetDoc()->InsertLabel(eType, rText, rSeparator,
+ rNumberSeparator, bBefore, nId,
+ nIdx, rCharacterStyle, bCpyBrd);
+ }
+
+ if (pFlyFormat)
+ {
+ const Point aPt(GetCursorDocPos());
+ if (SwFlyFrame* pFrame = pFlyFormat->GetFrame(&aPt))
+ SelectFlyFrame(*pFrame);
+ }
+ EndUndo();
+ EndAllActionAndCall();
+
+}
+
+bool SwFEShell::Sort(const SwSortOptions& rOpt)
+{
+ if( !HasSelection() )
+ return false;
+
+ CurrShell aCurr( this );
+ bool bRet = false;
+ StartAllAction();
+ if(IsTableMode())
+ {
+ // Sort table
+ // check if Point/Mark of current Cursor are in one table
+ SwFrame *pFrame = GetCurrFrame( false );
+ OSL_ENSURE( pFrame->FindTabFrame(), "Cursor not in table." );
+
+ // search boxes via the layout
+ SwSelBoxes aBoxes;
+ GetTableSel(*this, aBoxes);
+
+ // The Cursor should be removed from the deletion area.
+ // Always put them behind/on the table; via the
+ // document position they will always be set to the old position
+ while( !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+ {
+ /* ParkCursor->ParkCursorTab */
+ ParkCursorInTab();
+ }
+
+ // call sorting on document
+ bRet = mxDoc->SortTable(aBoxes, rOpt);
+ }
+ else
+ {
+ // Sort text nothing else
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ SwPaM* pPam = &rPaM;
+
+ SwPosition* pStart = pPam->Start();
+ SwPosition* pEnd = pPam->End();
+
+ SwNodeIndex aPrevIdx( pStart->nNode, -1 );
+ SwNodeOffset nOffset = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex();
+ const sal_Int32 nCntStt = pStart->nContent.GetIndex();
+
+ // Sorting
+ bRet = mxDoc->SortText(*pPam, rOpt);
+
+ // put selection again
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 );
+ SwContentNode* pCNd = pPam->GetContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if( nLen > nCntStt )
+ nLen = nCntStt;
+ pPam->GetPoint()->nContent.Assign(pCNd, nLen );
+ pPam->SetMark();
+
+ pPam->GetPoint()->nNode += nOffset;
+ pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+ }
+ }
+
+ EndAllAction();
+ return bRet;
+}
+
+bool SwFEShell::IsColRightToLeft() const
+{
+ SwFrame* pFrame = GetCurrFrame();
+ while (pFrame)
+ {
+ pFrame = pFrame->GetUpper();
+ if (pFrame && pFrame->IsColumnFrame())
+ {
+ return pFrame->IsRightToLeft();
+ }
+ }
+ return false;
+}
+
+sal_uInt16 SwFEShell::GetCurColNum_( const SwFrame *pFrame,
+ SwGetCurColNumPara* pPara )
+{
+ sal_uInt16 nRet = 0;
+ while ( pFrame )
+ {
+ pFrame = pFrame->GetUpper();
+ if( pFrame && pFrame->IsColumnFrame() )
+ {
+ const SwFrame *pCurFrame = pFrame;
+ do {
+ ++nRet;
+ pFrame = pFrame->GetPrev();
+ } while ( pFrame );
+
+ if( pPara )
+ {
+ // now search the format, determining the columness
+ pFrame = pCurFrame->GetUpper();
+ while( pFrame )
+ {
+ if( ( SwFrameType::Page | SwFrameType::Fly | SwFrameType::Section ) & pFrame->GetType() )
+ {
+ pPara->pFrameFormat = static_cast<const SwLayoutFrame*>(pFrame)->GetFormat();
+ pPara->pPrtRect = &pFrame->getFramePrintArea();
+ break;
+ }
+ pFrame = pFrame->GetUpper();
+ }
+ if( !pFrame )
+ {
+ pPara->pFrameFormat = nullptr;
+ pPara->pPrtRect = nullptr;
+ }
+ }
+ break;
+ }
+ }
+ return nRet;
+}
+
+sal_uInt16 SwFEShell::GetCurColNum( SwGetCurColNumPara* pPara ) const
+{
+ OSL_ENSURE( GetCurrFrame(), "Cursor parked?" );
+ return GetCurColNum_( GetCurrFrame(), pPara );
+}
+
+sal_uInt16 SwFEShell::GetCurOutColNum() const
+{
+ sal_uInt16 nRet = 0;
+ SwFrame* pFrame = GetCurrFrame();
+ OSL_ENSURE( pFrame, "Cursor parked?" );
+ if( pFrame )
+ {
+ pFrame = pFrame->IsInTab() ? static_cast<SwFrame*>(pFrame->FindTabFrame())
+ : static_cast<SwFrame*>(pFrame->FindSctFrame());
+ OSL_ENSURE( pFrame, "No Tab, no Sect" );
+ if( pFrame )
+ nRet = GetCurColNum_( pFrame, nullptr );
+ }
+ return nRet;
+}
+
+SwFEShell::SwFEShell( SwDoc& rDoc, vcl::Window *pWindow, const SwViewOption *pOptions )
+ : SwEditShell( rDoc, pWindow, pOptions )
+ , m_bCheckForOLEInCaption(false)
+ , m_aPasteListeners(GetPasteMutex())
+ , m_eTableInsertMode(SwTable::SEARCH_NONE)
+ , m_bTableCopied(false)
+{
+}
+
+SwFEShell::SwFEShell( SwEditShell& rShell, vcl::Window *pWindow )
+ : SwEditShell( rShell, pWindow )
+ , m_bCheckForOLEInCaption(false)
+ , m_aPasteListeners(GetPasteMutex())
+ , m_eTableInsertMode(SwTable::SEARCH_NONE)
+ , m_bTableCopied(false)
+{
+}
+
+SwFEShell::~SwFEShell()
+{
+}
+
+// #i17567# - adjustments for allowing
+// negative vertical positions for fly frames anchored to paragraph/to character.
+// #i22305# - adjustments for option 'Follow text flow'
+// for to frame anchored objects.
+// #i22341# - adjustments for vertical alignment at top of line
+// for to character anchored objects.
+void SwFEShell::CalcBoundRect( SwRect& _orRect,
+ const RndStdIds _nAnchorId,
+ const sal_Int16 _eHoriRelOrient,
+ const sal_Int16 _eVertRelOrient,
+ const SwPosition* _pToCharContentPos,
+ const bool _bFollowTextFlow,
+ bool _bMirror,
+ Point* _opRef,
+ Size* _opPercent,
+ const SwFormatFrameSize* pFormatFrameSize) const
+{
+ const SwFrame* pFrame;
+ const SwFlyFrame* pFly;
+ if( _opRef )
+ {
+ pFrame = GetCurrFrame();
+ pFly = pFrame->FindFlyFrame();
+ if( nullptr != pFly )
+ pFrame = pFly->GetAnchorFrame();
+ }
+ else
+ {
+ pFly = GetSelectedFlyFrame();
+ pFrame = pFly ? pFly->GetAnchorFrame() : GetCurrFrame();
+ }
+
+ bool bWrapThrough = false;
+ if ( pFly )
+ {
+ SwFlyFrameFormat* pFormat = const_cast<SwFlyFrameFormat*>(pFly->GetFormat());
+ const SwFormatSurround& rSurround = pFormat->GetSurround();
+ bWrapThrough = rSurround.GetSurround() == css::text::WrapTextMode_THROUGH;
+ }
+
+ const SwPageFrame* pPage = pFrame->FindPageFrame();
+ _bMirror = _bMirror && !pPage->OnRightPage();
+
+ Point aPos;
+ bool bVertic = false;
+ bool bRTL = false;
+ bool bVerticalL2R = false;
+
+ if ((RndStdIds::FLY_AT_PAGE == _nAnchorId) || (RndStdIds::FLY_AT_FLY == _nAnchorId)) // LAYER_IMPL
+ {
+ const SwFrame* pTmp = pFrame;
+ // #i22305#
+ if ((RndStdIds::FLY_AT_PAGE == _nAnchorId) ||
+ ((RndStdIds::FLY_AT_FLY == _nAnchorId) && !_bFollowTextFlow))
+ {
+ pFrame = pPage;
+ }
+ else
+ {
+ pFrame = pFrame->FindFlyFrame();
+ }
+ if ( !pFrame )
+ pFrame = pTmp;
+ _orRect = pFrame->getFrameArea();
+ SwRectFnSet aRectFnSet(pFrame);
+ bRTL = pFrame->IsRightToLeft();
+ if ( bRTL )
+ aPos = pFrame->getFrameArea().TopRight();
+ else
+ aPos = aRectFnSet.GetPos(pFrame->getFrameArea());
+
+ if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ {
+ bVertic = aRectFnSet.IsVert();
+ bVerticalL2R = aRectFnSet.IsVertL2R();
+ _bMirror = false; // no mirroring in vertical environment
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::PAGE_RIGHT:
+ case text::RelOrientation::FRAME_RIGHT: aPos.AdjustY(pFrame->getFramePrintArea().Height() );
+ [[fallthrough]];
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustY(pFrame->getFramePrintArea().Top() ); break;
+ default: break;
+ }
+ }
+ else if ( _bMirror )
+ {
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustX(pFrame->getFramePrintArea().Width() );
+ [[fallthrough]];
+ case text::RelOrientation::PAGE_RIGHT:
+ case text::RelOrientation::FRAME_RIGHT: aPos.AdjustX(pFrame->getFramePrintArea().Left() ); break;
+ default: aPos.AdjustX(pFrame->getFrameArea().Width() );
+ }
+ }
+ else if ( bRTL )
+ {
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustX(pFrame->getFramePrintArea().Width() );
+ [[fallthrough]];
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::FRAME_LEFT: aPos.AdjustX(pFrame->getFramePrintArea().Left() -
+ pFrame->getFrameArea().Width() ); break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::PAGE_RIGHT:
+ case text::RelOrientation::FRAME_RIGHT: aPos.AdjustX(pFrame->getFramePrintArea().Width() );
+ [[fallthrough]];
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA: aPos.AdjustX(pFrame->getFramePrintArea().Left() ); break;
+ default:break;
+ }
+ }
+
+ if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ switch ( _eVertRelOrient )
+ {
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ aPos.AdjustX( -(pFrame->GetRightMargin()) );
+ }
+ break;
+ }
+ }
+ else if ( aRectFnSet.IsVertL2R() )
+ {
+ switch ( _eVertRelOrient )
+ {
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ aPos.AdjustX(pFrame->GetLeftMargin() );
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch ( _eVertRelOrient )
+ {
+ case text::RelOrientation::PRINT_AREA:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ if ( pFrame->IsPageFrame() )
+ {
+ aPos.setY(
+ static_cast<const SwPageFrame*>(pFrame)->PrtWithoutHeaderAndFooter().Top() );
+ }
+ else
+ {
+ aPos.AdjustY(pFrame->getFramePrintArea().Top() );
+ }
+ }
+ break;
+ }
+ }
+ if ( _opPercent )
+ *_opPercent = pFrame->getFramePrintArea().SSize();
+ }
+ else
+ {
+ const SwFrame* pUpper = ( pFrame->IsPageFrame() || pFrame->IsFlyFrame() ) ?
+ pFrame : pFrame->GetUpper();
+ SwRectFnSet aRectFnSet(pUpper);
+ if ( _opPercent )
+ {
+ // If the size is relative from page, then full size should be counted from the page frame.
+ if (pFormatFrameSize && pFormatFrameSize->GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME)
+ _opPercent->setWidth(pPage->getFrameArea().Width());
+ else
+ _opPercent->setWidth(pUpper->getFramePrintArea().Width());
+
+ if (pFormatFrameSize && pFormatFrameSize->GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME)
+ // If the size is relative from page, then full size should be counted from the page frame.
+ _opPercent->setHeight(pPage->getFrameArea().Height());
+ else
+ _opPercent->setHeight(pUpper->getFramePrintArea().Height());
+ }
+
+ bRTL = pFrame->IsRightToLeft();
+ if ( bRTL )
+ aPos = pFrame->getFrameArea().TopRight();
+ else
+ aPos = aRectFnSet.GetPos(pFrame->getFrameArea());
+ // #i17567# - allow negative positions
+ // for fly frames anchor to paragraph/to character.
+ if ((_nAnchorId == RndStdIds::FLY_AT_PARA) || (_nAnchorId == RndStdIds::FLY_AT_CHAR))
+ {
+ // The rectangle, the fly frame can be positioned in, is determined
+ // horizontally by the frame area of the horizontal environment
+ // and vertically by the printing area of the vertical environment,
+ // if the object follows the text flow, or by the frame area of the
+ // vertical environment, if the object doesn't follow the text flow.
+ // new class <SwEnvironmentOfAnchoredObject>
+ objectpositioning::SwEnvironmentOfAnchoredObject aEnvOfObj(
+ _bFollowTextFlow );
+ const SwLayoutFrame& rHoriEnvironLayFrame =
+ aEnvOfObj.GetHoriEnvironmentLayoutFrame( *pFrame );
+ const SwLayoutFrame& rVertEnvironLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pFrame );
+ const SwRect& aHoriEnvironRect( rHoriEnvironLayFrame.getFrameArea() );
+ SwRect aVertEnvironRect;
+ if ( _bFollowTextFlow )
+ {
+ aVertEnvironRect = rVertEnvironLayFrame.getFramePrintArea();
+ aVertEnvironRect.Pos() += rVertEnvironLayFrame.getFrameArea().Pos();
+ // #i18732# - adjust vertical 'virtual' anchor position
+ // (<aPos.Y()> respectively <aPos.X()>), if object is vertical aligned
+ // to page areas.
+ if ( _eVertRelOrient == text::RelOrientation::PAGE_FRAME || _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ aPos.setX( aVertEnvironRect.Right() );
+ }
+ else if ( aRectFnSet.IsVertL2R() )
+ {
+ aPos.setX( aVertEnvironRect.Left() );
+ }
+ else
+ {
+ aPos.setY( aVertEnvironRect.Top() );
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( rVertEnvironLayFrame.IsPageFrame(),
+ "<SwFEShell::CalcBoundRect(..)> - not following text flow, but vertical environment *not* page!" );
+ aVertEnvironRect = rVertEnvironLayFrame.getFrameArea();
+ // #i18732# - adjustment vertical 'virtual' anchor position
+ // (<aPos.Y()> respectively <aPos.X()>), if object is vertical aligned
+ // to page areas.
+ if (_eVertRelOrient == text::RelOrientation::PAGE_FRAME
+ || _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA
+ || _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ {
+ if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ aPos.setX( aVertEnvironRect.Right() );
+ if ( _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ aPos.setX(aPos.getX() - rVertEnvironLayFrame.GetRightMargin());
+ }
+ }
+ else if ( aRectFnSet.IsVertL2R() )
+ {
+ aPos.setX( aVertEnvironRect.Left() );
+ if ( _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ aPos.setX(aPos.getX() + rVertEnvironLayFrame.GetLeftMargin());
+ }
+ }
+ else
+ {
+ aPos.setY( aVertEnvironRect.Top() );
+ if ( _eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ aPos.setY(aPos.getY() + rVertEnvironLayFrame.GetTopMargin());
+ // add height of page header
+ const SwFrame* pTmpFrame = rVertEnvironLayFrame.Lower();
+ if ( pTmpFrame->IsHeaderFrame() )
+ {
+ aPos.setY(aPos.getY() + pTmpFrame->getFrameArea().Height());
+ }
+ }
+ else if (_eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ {
+ if (rVertEnvironLayFrame.IsPageFrame())
+ {
+ auto& rPageFrame = static_cast<const SwPageFrame&>(rVertEnvironLayFrame);
+ aPos.setY(rPageFrame.PrtWithoutHeaderAndFooter().Bottom());
+ }
+ else
+ {
+ aPos.AdjustY(rVertEnvironLayFrame.getFramePrintArea().Bottom());
+ }
+ }
+ }
+ }
+ }
+
+ // #i22341# - adjust vertical 'virtual' anchor position
+ // (<aPos.Y()> respectively <aPos.X()>), if object is anchored to
+ // character and vertical aligned at character or top of line
+ // <pFrame>, which is the anchor frame or the proposed anchor frame,
+ // doesn't have to be a text frame (e.g. edit a to-page anchored
+ // fly frame). Thus, assure this.
+ const SwTextFrame* pTextFrame = pFrame->DynCastTextFrame();
+ if ( pTextFrame &&
+ (_nAnchorId == RndStdIds::FLY_AT_CHAR) &&
+ ( _eVertRelOrient == text::RelOrientation::CHAR ||
+ _eVertRelOrient == text::RelOrientation::TEXT_LINE ) )
+ {
+ SwTwips nTop = 0;
+ if ( _eVertRelOrient == text::RelOrientation::CHAR )
+ {
+ SwRect aChRect;
+ if ( _pToCharContentPos )
+ {
+ pTextFrame->GetAutoPos( aChRect, *_pToCharContentPos );
+ }
+ else
+ {
+ // No content position provided. Thus, use a default one.
+ SwPosition aDefaultContentPos(*(pTextFrame->GetTextNodeFirst()));
+ pTextFrame->GetAutoPos( aChRect, aDefaultContentPos );
+ }
+ nTop = aRectFnSet.GetBottom(aChRect);
+ }
+ else
+ {
+ if ( _pToCharContentPos )
+ {
+ pTextFrame->GetTopOfLine( nTop, *_pToCharContentPos );
+ }
+ else
+ {
+ // No content position provided. Thus, use a default one.
+ SwPosition aDefaultContentPos(*(pTextFrame->GetTextNodeFirst()));
+ pTextFrame->GetTopOfLine( nTop, aDefaultContentPos );
+ }
+ }
+ if ( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ {
+ aPos.setX(nTop);
+ }
+ else
+ {
+ aPos.setY(nTop);
+ }
+ }
+
+ // #i26945# - adjust horizontal 'virtual' anchor
+ // position (<aPos.X()> respectively <aPos.Y()>), if object is
+ // anchored to character and horizontal aligned at character.
+ if ( pTextFrame &&
+ (_nAnchorId == RndStdIds::FLY_AT_CHAR) &&
+ _eHoriRelOrient == text::RelOrientation::CHAR )
+ {
+ SwTwips nLeft = 0;
+ SwRect aChRect;
+ if ( _pToCharContentPos )
+ {
+ pTextFrame->GetAutoPos( aChRect, *_pToCharContentPos );
+ }
+ else
+ {
+ // No content position provided. Thus, use a default one.
+ SwPosition aDefaultContentPos(*(pTextFrame->GetTextNodeFirst()));
+ pTextFrame->GetAutoPos( aChRect, aDefaultContentPos );
+ }
+ nLeft = aRectFnSet.GetLeft(aChRect);
+ if ( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ {
+ aPos.setY(nLeft);
+ }
+ else
+ {
+ aPos.setX(nLeft);
+ }
+ }
+ if ( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ {
+ _orRect = SwRect( aVertEnvironRect.Left(),
+ aHoriEnvironRect.Top(),
+ aVertEnvironRect.Width(),
+ aHoriEnvironRect.Height() );
+ }
+ else
+ {
+ _orRect = SwRect( aHoriEnvironRect.Left(),
+ aVertEnvironRect.Top(),
+ aHoriEnvironRect.Width(),
+ aVertEnvironRect.Height() );
+ }
+ }
+ else
+ {
+ if( _opRef && pFly && pFly->IsFlyInContentFrame() )
+ *_opRef = static_cast<const SwFlyInContentFrame*>( pFly )->GetRefPoint();
+
+ _orRect = pUpper->getFrameArea();
+ if( !pUpper->IsBodyFrame() )
+ {
+ _orRect += pUpper->getFramePrintArea().Pos();
+ _orRect.SSize( pUpper->getFramePrintArea().SSize() );
+ if ( pUpper->IsCellFrame() )//MA_FLY_HEIGHT
+ {
+ const SwFrame* pTab = pUpper->FindTabFrame();
+ tools::Long nBottom = aRectFnSet.GetPrtBottom(*pTab->GetUpper());
+ aRectFnSet.SetBottom( _orRect, nBottom );
+ }
+ }
+ // only use 90% of height for character bound
+ {
+ if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ _orRect.Width( (_orRect.Width()*9)/10 );
+ else
+ _orRect.Height( (_orRect.Height()*9)/10 );
+ }
+ }
+
+ const SwTwips nBaseOfstForFly = ( pFrame->IsTextFrame() && pFly ) ?
+ static_cast<const SwTextFrame*>(pFrame)->GetBaseOffsetForFly( !bWrapThrough ) :
+ 0;
+ if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ {
+ bVertic = aRectFnSet.IsVert();
+ bVerticalL2R = aRectFnSet.IsVertL2R();
+ _bMirror = false;
+
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::FRAME_RIGHT:
+ {
+ aPos.setY(aPos.getY() + pFrame->getFramePrintArea().Height());
+ aPos += aRectFnSet.GetPos(pFrame->getFramePrintArea());
+ break;
+ }
+ case text::RelOrientation::PRINT_AREA:
+ {
+ aPos += aRectFnSet.GetPos(pFrame->getFramePrintArea());
+ aPos.setY(aPos.getY() + nBaseOfstForFly);
+ break;
+ }
+ case text::RelOrientation::PAGE_RIGHT:
+ {
+ aPos.setY(pPage->getFrameArea().Top() + pPage->getFramePrintArea().Bottom());
+ break;
+ }
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ aPos.setY(pPage->getFrameArea().Top() + pPage->getFramePrintArea().Top());
+ break;
+ }
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::PAGE_FRAME:
+ {
+ aPos.setY(pPage->getFrameArea().Top());
+ break;
+ }
+ case text::RelOrientation::FRAME:
+ {
+ aPos.setY(aPos.getY() + nBaseOfstForFly);
+ break;
+ }
+ default: break;
+ }
+ }
+ else if( _bMirror )
+ {
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::FRAME_RIGHT: aPos.setX(aPos.getX() + pFrame->getFramePrintArea().Left()); break;
+ case text::RelOrientation::FRAME:
+ case text::RelOrientation::FRAME_LEFT: aPos.setX(aPos.getX() + pFrame->getFrameArea().Width()); break;
+ case text::RelOrientation::PRINT_AREA: aPos.setX(aPos.getX() + pFrame->getFramePrintArea().Right()); break;
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::PAGE_FRAME: aPos.setX(pPage->getFrameArea().Right()); break;
+ case text::RelOrientation::PAGE_PRINT_AREA: aPos.setX(pPage->getFrameArea().Left()
+ + pPage->getFramePrintArea().Left()); break;
+ default: break;
+ }
+ }
+ else if ( bRTL )
+ {
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::FRAME_LEFT:
+ aPos.setX(pFrame->getFrameArea().Left() +
+ pFrame->getFramePrintArea().Left());
+ break;
+
+ case text::RelOrientation::PRINT_AREA:
+ aPos.setX(pFrame->getFrameArea().Left() + pFrame->getFramePrintArea().Left() +
+ pFrame->getFramePrintArea().Width());
+ aPos.setX(aPos.getX() + nBaseOfstForFly);
+ break;
+
+ case text::RelOrientation::PAGE_LEFT:
+ aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Left());
+ break;
+
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Left() +
+ pPage->getFramePrintArea().Width());
+ break;
+
+ case text::RelOrientation::PAGE_RIGHT:
+ case text::RelOrientation::PAGE_FRAME:
+ aPos.setX(pPage->getFrameArea().Right());
+ break;
+
+ case text::RelOrientation::FRAME:
+ aPos.setX(aPos.getX() + nBaseOfstForFly);
+ break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch ( _eHoriRelOrient )
+ {
+ case text::RelOrientation::FRAME_RIGHT:
+ aPos.AdjustX(pFrame->getFramePrintArea().Width() );
+ aPos += pFrame->getFramePrintArea().Pos();
+ break;
+ case text::RelOrientation::PRINT_AREA:
+ aPos += pFrame->getFramePrintArea().Pos();
+ aPos.setX(aPos.getX() + nBaseOfstForFly);
+ break;
+ case text::RelOrientation::PAGE_RIGHT:
+ aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Right());
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ aPos.setX(pPage->getFrameArea().Left() + pPage->getFramePrintArea().Left());
+ break;
+ case text::RelOrientation::PAGE_LEFT:
+ case text::RelOrientation::PAGE_FRAME:
+ aPos.setX(pPage->getFrameArea().Left());
+ break;
+ case text::RelOrientation::FRAME:
+ aPos.setX(aPos.getX() + nBaseOfstForFly);
+ break;
+ default: break;
+ }
+ }
+
+ }
+ if( _opRef )
+ return;
+
+ if( bVertic && !bVerticalL2R )
+ _orRect.Pos( aPos.getX() - _orRect.Width() - _orRect.Left(), _orRect.Top() - aPos.getY() );
+ else if( bVerticalL2R )
+ _orRect.Pos( _orRect.Left() - aPos.getX(), _orRect.Top() - aPos.getY() );
+ else if ( bRTL )
+ _orRect.Pos( - ( _orRect.Right() - aPos.getX() ), _orRect.Top() - aPos.getY() );
+ else
+ _orRect.Pos( _orRect.Left() - aPos.getX(), _orRect.Top() - aPos.getY() );
+ if( _bMirror )
+ _orRect.Pos( -_orRect.Right(), _orRect.Top() );
+}
+
+Size SwFEShell::GetGraphicDefaultSize() const
+{
+ Size aRet;
+ SwFlyFrame *pFly = GetSelectedFlyFrame();
+ if ( pFly )
+ {
+ // #i32951# - due to issue #i28701# no format of a
+ // newly inserted Writer fly frame or its anchor frame is performed
+ // any more. Thus, it could be possible (e.g. on insert of a horizontal
+ // line) that the anchor frame isn't formatted and its printing area
+ // size is (0,0). If this is the case the printing area of the upper
+ // of the anchor frame is taken.
+ const SwFrame* pAnchorFrame = pFly->GetAnchorFrame();
+ aRet = pAnchorFrame->getFramePrintArea().SSize();
+ if ( aRet.IsEmpty() && pAnchorFrame->GetUpper() )
+ {
+ aRet = pAnchorFrame->GetUpper()->getFramePrintArea().SSize();
+ }
+
+ SwRect aBound;
+ CalcBoundRect( aBound, pFly->GetFormat()->GetAnchor().GetAnchorId());
+ if ( pFly->GetAnchorFrame()->IsVertical() )
+ aRet.setWidth( aBound.Width() );
+ else
+ aRet.setHeight( aBound.Height() );
+ }
+ return aRet;
+}
+
+bool SwFEShell::IsFrameVertical(const bool bEnvironment, bool& bRTL, bool& bVertL2R) const
+{
+ bool bVert = false;
+ bRTL = false;
+ bVertL2R = false;
+
+ if ( Imp()->HasDrawView() )
+ {
+ const SdrMarkList &rMrkList = Imp()->GetDrawView()->GetMarkedObjectList();
+ if( rMrkList.GetMarkCount() != 1 )
+ return bVert;
+
+ SdrObject* pObj = rMrkList.GetMark( 0 )->GetMarkedSdrObj();
+ if ( !pObj )
+ {
+ OSL_FAIL( "<SwFEShell::IsFrameVertical(..)> - missing SdrObject instance in marked object list -> This is a serious situation" );
+ return bVert;
+ }
+ // #i26791#
+ SwContact* pContact = GetUserCall( pObj );
+ if ( !pContact )
+ {
+ OSL_FAIL( "<SwFEShell::IsFrameVertical(..)> - missing SwContact instance at marked object -> This is a serious situation" );
+ return bVert;
+ }
+ const SwFrame* pRef = pContact->GetAnchoredObj( pObj )->GetAnchorFrame();
+ if ( !pRef )
+ {
+ OSL_FAIL( "<SwFEShell::IsFrameVertical(..)> - missing anchor frame at marked object -> This is a serious situation" );
+ return bVert;
+ }
+
+ if ( !bEnvironment )
+ if ( auto pVirtFly = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) )
+ pRef = pVirtFly->GetFlyFrame();
+
+ bVert = pRef->IsVertical();
+ bRTL = pRef->IsRightToLeft();
+ bVertL2R = pRef->IsVertLR();
+ }
+
+ return bVert;
+}
+
+void SwFEShell::MoveObjectIfActive( svt::EmbeddedObjectRef&, const Point& )
+{
+ // does not do anything, only avoids crash if the method is used for wrong shell
+}
+
+void SwFEShell::ToggleHeaderFooterEdit()
+{
+ // Clear objects selection
+ if ( Imp()->GetDrawView()->AreObjectsMarked() )
+ {
+ Imp()->GetDrawView()->UnmarkAll();
+ ClearMark();
+ }
+
+ SwCursorShell::ToggleHeaderFooterEdit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/frmedt/tblsel.cxx b/sw/source/core/frmedt/tblsel.cxx
new file mode 100644
index 000000000..45fc820b2
--- /dev/null
+++ b/sw/source/core/frmedt/tblsel.cxx
@@ -0,0 +1,2614 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/boxitem.hxx>
+#include <editeng/protitem.hxx>
+#include <osl/diagnose.h>
+
+#include <hintids.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <frmatr.hxx>
+#include <tblsel.hxx>
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <cntfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <rootfrm.hxx>
+#include <viscrs.hxx>
+#include <swtblfmt.hxx>
+#include <UndoTable.hxx>
+#include <sectfrm.hxx>
+#include <frmtool.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+#include <deque>
+#include <memory>
+
+// see also swtable.cxx
+#define COLFUZZY 20L
+
+// macros, determining how table boxes are merged:
+// - 1. remove empty lines, all boxes separated with blanks,
+// all lines separated with ParaBreak
+// - 2. remove all empty lines and remove all empty boxes at beginning and end,
+// all boxes separated with Blank,
+// all lines separated with ParaBreak
+// - 3. remove all empty boxes, all boxes separated with blanks,
+// all lines separated with ParaBreak
+
+#undef DEL_ONLY_EMPTY_LINES
+#undef DEL_EMPTY_BOXES_AT_START_AND_END
+
+namespace {
+
+struct CmpLPt
+{
+ Point aPos;
+ const SwTableBox* pSelBox;
+ bool bVert;
+
+ CmpLPt( const Point& rPt, const SwTableBox* pBox, bool bVertical );
+
+ bool operator<( const CmpLPt& rCmp ) const
+ {
+ if ( bVert )
+ return X() > rCmp.X() || ( X() == rCmp.X() && Y() < rCmp.Y() );
+ else
+ return Y() < rCmp.Y() || ( Y() == rCmp.Y() && X() < rCmp.X() );
+ }
+
+ tools::Long X() const { return aPos.X(); }
+ tools::Long Y() const { return aPos.Y(); }
+};
+
+}
+
+typedef o3tl::sorted_vector<CmpLPt> MergePos;
+
+namespace {
+
+struct Sort_CellFrame
+{
+ const SwCellFrame* pFrame;
+
+ explicit Sort_CellFrame( const SwCellFrame& rCFrame )
+ : pFrame( &rCFrame ) {}
+};
+
+}
+
+static const SwLayoutFrame *lcl_FindCellFrame( const SwLayoutFrame *pLay )
+{
+ while ( pLay && !pLay->IsCellFrame() )
+ pLay = pLay->GetUpper();
+ return pLay;
+}
+
+static const SwLayoutFrame *lcl_FindNextCellFrame( const SwLayoutFrame *pLay )
+{
+ // ensure we leave the cell (sections)
+ const SwLayoutFrame *pTmp = pLay;
+ do {
+ pTmp = pTmp->GetNextLayoutLeaf();
+ } while( pLay->IsAnLower( pTmp ) );
+
+ while( pTmp && !pTmp->IsCellFrame() )
+ pTmp = pTmp->GetUpper();
+ return pTmp;
+}
+
+void GetTableSelCrs( const SwCursorShell &rShell, SwSelBoxes& rBoxes )
+{
+ rBoxes.clear();
+ if( rShell.IsTableMode() && const_cast<SwCursorShell&>(rShell).UpdateTableSelBoxes())
+ {
+ rBoxes.insert(rShell.GetTableCursor()->GetSelectedBoxes());
+ }
+}
+
+void GetTableSelCrs( const SwTableCursor& rTableCursor, SwSelBoxes& rBoxes )
+{
+ rBoxes.clear();
+
+ if (rTableCursor.IsChgd() || !rTableCursor.GetSelectedBoxesCount())
+ {
+ SwTableCursor* pTCursor = const_cast<SwTableCursor*>(&rTableCursor);
+ pTCursor->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout()->MakeTableCursors( *pTCursor );
+ }
+
+ if (rTableCursor.GetSelectedBoxesCount())
+ {
+ rBoxes.insert(rTableCursor.GetSelectedBoxes());
+ }
+}
+
+void GetTableSel( const SwCursorShell& rShell, SwSelBoxes& rBoxes,
+ const SwTableSearchType eSearchType )
+{
+ // get start and end cell
+ if ( !rShell.IsTableMode() )
+ rShell.GetCursor();
+
+ GetTableSel( *rShell.getShellCursor(false), rBoxes, eSearchType );
+}
+
+void GetTableSel( const SwCursor& rCursor, SwSelBoxes& rBoxes,
+ const SwTableSearchType eSearchType )
+{
+ // get start and end cell
+ OSL_ENSURE( rCursor.GetContentNode() && rCursor.GetContentNode( false ),
+ "Tabselection not on Cnt." );
+
+ // Row-selection:
+ // Check for complex tables. If Yes, search selected boxes via
+ // the layout. Otherwise via the table structure (for macros !!)
+ const SwContentNode* pContentNd = rCursor.GetNode().GetContentNode();
+ const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr;
+ if( pTableNd && pTableNd->GetTable().IsNewModel() )
+ {
+ SwTable::SearchType eSearch;
+ switch( SwTableSearchType::Col & eSearchType )
+ {
+ case SwTableSearchType::Row: eSearch = SwTable::SEARCH_ROW; break;
+ case SwTableSearchType::Col: eSearch = SwTable::SEARCH_COL; break;
+ default: eSearch = SwTable::SEARCH_NONE; break;
+ }
+ const bool bChkP( SwTableSearchType::Protect & eSearchType );
+ pTableNd->GetTable().CreateSelection( rCursor, rBoxes, eSearch, bChkP );
+ return;
+ }
+ if( SwTableSearchType::Row == ((~SwTableSearchType::Protect ) & eSearchType ) &&
+ pTableNd && !pTableNd->GetTable().IsTableComplex() )
+ {
+ const SwTable& rTable = pTableNd->GetTable();
+ const SwTableLines& rLines = rTable.GetTabLines();
+
+ const SwNode& rMarkNode = rCursor.GetNode( false );
+ const SwNodeOffset nMarkSectionStart = rMarkNode.StartOfSectionIndex();
+ const SwTableBox* pMarkBox = rTable.GetTableBox( nMarkSectionStart );
+
+ OSL_ENSURE( pMarkBox, "Point in table, mark outside?" );
+
+ const SwTableLine* pLine = pMarkBox ? pMarkBox->GetUpper() : nullptr;
+ sal_uInt16 nSttPos = rLines.GetPos( pLine );
+ OSL_ENSURE( USHRT_MAX != nSttPos, "Where is my row in the table?" );
+ pLine = rTable.GetTableBox( rCursor.GetNode().StartOfSectionIndex() )->GetUpper();
+ sal_uInt16 nEndPos = rLines.GetPos( pLine );
+ OSL_ENSURE( USHRT_MAX != nEndPos, "Where is my row in the table?" );
+ // pb: #i20193# if tableintable then nSttPos == nEndPos == USHRT_MAX
+ if ( nSttPos != USHRT_MAX && nEndPos != USHRT_MAX )
+ {
+ if( nEndPos < nSttPos ) // exchange
+ {
+ sal_uInt16 nTmp = nSttPos; nSttPos = nEndPos; nEndPos = nTmp;
+ }
+
+ bool bChkProtected( SwTableSearchType::Protect & eSearchType );
+ for( ; nSttPos <= nEndPos; ++nSttPos )
+ {
+ pLine = rLines[ nSttPos ];
+ for( auto n = pLine->GetTabBoxes().size(); n ; )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[ --n ];
+ // check for cell protection??
+ if( !bChkProtected ||
+ !pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ rBoxes.insert( pBox );
+ }
+ }
+ }
+ }
+ else
+ {
+ Point aPtPos, aMkPos;
+ const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor);
+ if( pShCursor )
+ {
+ aPtPos = pShCursor->GetPtPos();
+ aMkPos = pShCursor->GetMkPos();
+ }
+ const SwContentNode *pCntNd = rCursor.GetContentNode();
+ std::pair<Point, bool> tmp(aPtPos, true);
+ const SwLayoutFrame *pStart = pCntNd ?
+ pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr;
+ pCntNd = rCursor.GetContentNode(false);
+ tmp.first = aMkPos;
+ const SwLayoutFrame *pEnd = pCntNd ?
+ pCntNd->getLayoutFrame(pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, &tmp)->GetUpper() : nullptr;
+ if( pStart && pEnd )
+ GetTableSel( pStart, pEnd, rBoxes, nullptr, eSearchType );
+ }
+}
+
+void GetTableSel( const SwLayoutFrame* pStart, const SwLayoutFrame* pEnd,
+ SwSelBoxes& rBoxes, SwCellFrames* pCells,
+ const SwTableSearchType eSearchType )
+{
+ const SwTabFrame* pStartTab = pStart->FindTabFrame();
+ if ( !pStartTab )
+ {
+ OSL_FAIL( "GetTableSel without start table" );
+ return;
+ }
+
+ bool bChkProtected( SwTableSearchType::Protect & eSearchType );
+
+ // #i55421# Reduced value 10
+ int nLoopMax = 10;
+
+ do {
+ bool bTableIsValid = true;
+
+ // First, compute tables and rectangles
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd, eSearchType );
+
+ Point aCurrentTopLeft( LONG_MAX, LONG_MAX );
+ Point aCurrentTopRight( 0, LONG_MAX );
+ Point aCurrentBottomLeft( LONG_MAX, 0 );
+ Point aCurrentBottomRight( 0, 0 );
+ const SwCellFrame* pCurrentTopLeftFrame = nullptr;
+ const SwCellFrame* pCurrentTopRightFrame = nullptr;
+ const SwCellFrame* pCurrentBottomLeftFrame = nullptr;
+ const SwCellFrame* pCurrentBottomRightFrame = nullptr;
+
+ // Now find boxes for each entry and emit
+ for (size_t i = 0; i < aUnions.size() && bTableIsValid; ++i)
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ const SwTabFrame *pTable = pUnion->GetTable();
+
+ if( !pTable->isFrameAreaDefinitionValid() && nLoopMax )
+ {
+ bTableIsValid = false;
+ break;
+ }
+
+ // Skip any repeated headlines in the follow:
+ const SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTable->Lower());
+
+ while( pRow && bTableIsValid )
+ {
+ if( !pRow->isFrameAreaDefinitionValid() && nLoopMax )
+ {
+ bTableIsValid = false;
+ break;
+ }
+
+ if ( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while (pCell && pRow->IsAnLower(pCell))
+ {
+ if( !pCell->isFrameAreaDefinitionValid() && nLoopMax )
+ {
+ bTableIsValid = false;
+ break;
+ }
+
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without Cell" );
+ if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) )
+ {
+ SwTableBox* pBox = const_cast<SwTableBox*>(
+ static_cast<const SwCellFrame*>(pCell)->GetTabBox());
+ // check for cell protection??
+ if( !bChkProtected ||
+ !pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ rBoxes.insert( pBox );
+
+ if ( pCells )
+ {
+ const Point aTopLeft( pCell->getFrameArea().TopLeft() );
+ const Point aTopRight( pCell->getFrameArea().TopRight() );
+ const Point aBottomLeft( pCell->getFrameArea().BottomLeft() );
+ const Point aBottomRight( pCell->getFrameArea().BottomRight() );
+
+ if ( aTopLeft.getY() < aCurrentTopLeft.getY() ||
+ ( aTopLeft.getY() == aCurrentTopLeft.getY() &&
+ aTopLeft.getX() < aCurrentTopLeft.getX() ) )
+ {
+ aCurrentTopLeft = aTopLeft;
+ pCurrentTopLeftFrame = static_cast<const SwCellFrame*>( pCell );
+ }
+
+ if ( aTopRight.getY() < aCurrentTopRight.getY() ||
+ ( aTopRight.getY() == aCurrentTopRight.getY() &&
+ aTopRight.getX() > aCurrentTopRight.getX() ) )
+ {
+ aCurrentTopRight = aTopRight;
+ pCurrentTopRightFrame = static_cast<const SwCellFrame*>( pCell );
+ }
+
+ if ( aBottomLeft.getY() > aCurrentBottomLeft.getY() ||
+ ( aBottomLeft.getY() == aCurrentBottomLeft.getY() &&
+ aBottomLeft.getX() < aCurrentBottomLeft.getX() ) )
+ {
+ aCurrentBottomLeft = aBottomLeft;
+ pCurrentBottomLeftFrame = static_cast<const SwCellFrame*>( pCell );
+ }
+
+ if ( aBottomRight.getY() > aCurrentBottomRight.getY() ||
+ ( aBottomRight.getY() == aCurrentBottomRight.getY() &&
+ aBottomRight.getX() > aCurrentBottomRight.getX() ) )
+ {
+ aCurrentBottomRight = aBottomRight;
+ pCurrentBottomRightFrame = static_cast<const SwCellFrame*>( pCell );
+ }
+
+ }
+ }
+ if ( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ pCell = ::lcl_FindNextCellFrame( pCell );
+ }
+ }
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+
+ if ( pCells )
+ {
+ pCells->clear();
+ pCells->push_back( const_cast< SwCellFrame* >(pCurrentTopLeftFrame) );
+ pCells->push_back( const_cast< SwCellFrame* >(pCurrentTopRightFrame) );
+ pCells->push_back( const_cast< SwCellFrame* >(pCurrentBottomLeftFrame) );
+ pCells->push_back( const_cast< SwCellFrame* >(pCurrentBottomRightFrame) );
+ }
+
+ if( bTableIsValid )
+ break;
+
+ SwDeletionChecker aDelCheck( pStart );
+
+ // otherwise quickly "calculate" the table layout and start over
+ SwTabFrame *pTable = aUnions.front().GetTable();
+ while( pTable )
+ {
+ if( pTable->isFrameAreaDefinitionValid() )
+ {
+ pTable->InvalidatePos();
+ }
+
+ pTable->SetONECalcLowers();
+ pTable->Calc(pTable->getRootFrame()->GetCurrShell()->GetOut());
+ pTable->SetCompletePaint();
+
+ pTable = pTable->GetFollow();
+ if( nullptr == pTable )
+ break;
+ }
+
+ // --> Make code robust, check if pStart has
+ // been deleted due to the formatting of the table:
+ if ( aDelCheck.HasBeenDeleted() )
+ {
+ OSL_FAIL( "Current box has been deleted during GetTableSel()" );
+ break;
+ }
+
+ rBoxes.clear();
+ --nLoopMax;
+
+ } while( true );
+ OSL_ENSURE( nLoopMax, "Table layout is still invalid!" );
+}
+
+bool ChkChartSel( const SwNode& rSttNd, const SwNode& rEndNd )
+{
+ const SwTableNode* pTNd = rSttNd.FindTableNode();
+ if( !pTNd )
+ return false;
+
+ Point aNullPos;
+ SwNodeIndex aIdx( rSttNd );
+ const SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNextSection( &aIdx, false, false );
+
+ // if table is invisible, return
+ // (layout needed for forming table selection further down, so we can't
+ // continue with invisible tables)
+ // #i22135# - Also the content of the table could be
+ // invisible - e.g. in a hidden section
+ // Robust: check, if content was found (e.g. empty table cells)
+ if ( !pCNd || pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) == nullptr )
+ return false;
+
+ std::pair<Point, bool> tmp(aNullPos, true);
+ const SwLayoutFrame *const pStart = pCNd->getLayoutFrame(
+ pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ OSL_ENSURE( pStart, "without frame nothing works" );
+
+ aIdx = rEndNd;
+ pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNextSection( &aIdx, false, false );
+
+ // #i22135# - Robust: check, if content was found and if it's visible
+ if ( !pCNd || pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) == nullptr )
+ {
+ return false;
+ }
+
+ const SwLayoutFrame *const pEnd = pCNd->getLayoutFrame(
+ pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ OSL_ENSURE( pEnd, "without frame nothing works" );
+
+ bool bValidChartSel;
+ // #i55421# Reduced value 10
+ int nLoopMax = 10; //JP 28.06.99: max 100 loops - Bug 67292
+
+ do {
+ bool bTableIsValid = true;
+ bValidChartSel = true;
+
+ sal_uInt16 nRowCells = USHRT_MAX;
+
+ // First, compute tables and rectangles
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::NoUnionCorrect );
+
+ // find boxes for each entry and emit
+ for( auto & rSelUnion : aUnions )
+ {
+ if (!bTableIsValid || !bValidChartSel)
+ break;
+
+ SwSelUnion *pUnion = &rSelUnion;
+ const SwTabFrame *pTable = pUnion->GetTable();
+
+ SwRectFnSet aRectFnSet(pTable);
+ bool bRTL = pTable->IsRightToLeft();
+
+ if( !pTable->isFrameAreaDefinitionValid() && nLoopMax )
+ {
+ bTableIsValid = false;
+ break;
+ }
+
+ std::deque< Sort_CellFrame > aCellFrames;
+
+ // Skip any repeated headlines in the follow:
+ const SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTable->Lower());
+
+ while( pRow && bTableIsValid && bValidChartSel )
+ {
+ if( !pRow->isFrameAreaDefinitionValid() && nLoopMax )
+ {
+ bTableIsValid = false;
+ break;
+ }
+
+ if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while (pCell && pRow->IsAnLower(pCell))
+ {
+ if( !pCell->isFrameAreaDefinitionValid() && nLoopMax )
+ {
+ bTableIsValid = false;
+ break;
+ }
+
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without Cell" );
+ const SwRect& rUnion = pUnion->GetUnion(),
+ & rFrameRect = pCell->getFrameArea();
+
+ const tools::Long nUnionRight = rUnion.Right();
+ const tools::Long nUnionBottom = rUnion.Bottom();
+ const tools::Long nFrameRight = rFrameRect.Right();
+ const tools::Long nFrameBottom = rFrameRect.Bottom();
+
+ // ignore if FrameRect is outside the union
+
+ const tools::Long nXFuzzy = aRectFnSet.IsVert() ? 0 : 20;
+ const tools::Long nYFuzzy = aRectFnSet.IsVert() ? 20 : 0;
+
+ if( !( rUnion.Top() + nYFuzzy > nFrameBottom ||
+ nUnionBottom < rFrameRect.Top() + nYFuzzy ||
+ rUnion.Left() + nXFuzzy > nFrameRight ||
+ nUnionRight < rFrameRect.Left() + nXFuzzy ))
+ {
+ // ok, rUnion is _not_ completely outside of rFrameRect
+
+ // if not completely inside the union, then
+ // for Chart it is an invalid selection
+ if( rUnion.Left() <= rFrameRect.Left() + nXFuzzy &&
+ rFrameRect.Left() <= nUnionRight &&
+ rUnion.Left() <= nFrameRight &&
+ nFrameRight <= nUnionRight + nXFuzzy &&
+ rUnion.Top() <= rFrameRect.Top() + nYFuzzy &&
+ rFrameRect.Top() <= nUnionBottom &&
+ rUnion.Top() <= nFrameBottom &&
+ nFrameBottom <= nUnionBottom+ nYFuzzy )
+
+ aCellFrames.emplace_back( *static_cast<const SwCellFrame*>(pCell) );
+ else
+ {
+ bValidChartSel = false;
+ break;
+ }
+ }
+ if ( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ pCell = ::lcl_FindNextCellFrame( pCell );
+ }
+ }
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+ }
+
+ if( !bValidChartSel )
+ break;
+
+ // all cells of the (part) table together. Now check if
+ // they're all adjacent
+ size_t n;
+ sal_uInt16 nCellCnt = 0;
+ tools::Long nYPos = LONG_MAX;
+ tools::Long nXPos = 0;
+ tools::Long nHeight = 0;
+
+ for( n = 0 ; n < aCellFrames.size(); ++n )
+ {
+ const Sort_CellFrame& rCF = aCellFrames[ n ];
+ if( aRectFnSet.GetTop(rCF.pFrame->getFrameArea()) != nYPos )
+ {
+ // new row
+ if( n )
+ {
+ if( USHRT_MAX == nRowCells ) // 1. row change
+ nRowCells = nCellCnt;
+ else if( nRowCells != nCellCnt )
+ {
+ bValidChartSel = false;
+ break;
+ }
+ }
+ nCellCnt = 1;
+ nYPos = aRectFnSet.GetTop(rCF.pFrame->getFrameArea());
+ nHeight = aRectFnSet.GetHeight(rCF.pFrame->getFrameArea());
+
+ nXPos = bRTL ?
+ aRectFnSet.GetLeft(rCF.pFrame->getFrameArea()) :
+ aRectFnSet.GetRight(rCF.pFrame->getFrameArea());
+ }
+ else if( nXPos == ( bRTL ?
+ aRectFnSet.GetRight(rCF.pFrame->getFrameArea()) :
+ aRectFnSet.GetLeft(rCF.pFrame->getFrameArea()) ) &&
+ nHeight == aRectFnSet.GetHeight(rCF.pFrame->getFrameArea()) )
+ {
+ nXPos += ( bRTL ? -1 : 1 ) *
+ aRectFnSet.GetWidth(rCF.pFrame->getFrameArea());
+ ++nCellCnt;
+ }
+ else
+ {
+ bValidChartSel = false;
+ break;
+ }
+ }
+ if( bValidChartSel )
+ {
+ if( USHRT_MAX == nRowCells )
+ nRowCells = nCellCnt;
+ else if( nRowCells != nCellCnt )
+ bValidChartSel = false;
+ }
+ }
+
+ if( bTableIsValid )
+ break;
+
+ // otherwise quickly "calculate" table layout and start over
+ SwTabFrame *pTable = aUnions.front().GetTable();
+
+ for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ if( pTable->isFrameAreaDefinitionValid() )
+ {
+ pTable->InvalidatePos();
+ }
+
+ pTable->SetONECalcLowers();
+ pTable->Calc(pTable->getRootFrame()->GetCurrShell()->GetOut());
+ pTable->SetCompletePaint();
+
+ pTable = pTable->GetFollow();
+ if( nullptr == pTable )
+ break;
+ }
+ --nLoopMax;
+ } while( true );
+
+ OSL_ENSURE( nLoopMax, "table layout is still invalid!" );
+
+ return bValidChartSel;
+}
+
+bool IsFrameInTableSel( const SwRect& rUnion, const SwFrame* pCell )
+{
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without Gazelle" );
+
+ if( pCell->FindTabFrame()->IsVertical() )
+ return rUnion.Right() >= pCell->getFrameArea().Right() &&
+ rUnion.Left() <= pCell->getFrameArea().Left() &&
+ (( rUnion.Top() <= pCell->getFrameArea().Top()+20 &&
+ rUnion.Bottom() > pCell->getFrameArea().Top() ) ||
+ ( rUnion.Top() >= pCell->getFrameArea().Top() &&
+ rUnion.Bottom() < pCell->getFrameArea().Bottom() ));
+
+ return
+ rUnion.Top() <= pCell->getFrameArea().Top() &&
+ rUnion.Bottom() >= pCell->getFrameArea().Bottom() &&
+
+ (( rUnion.Left() <= pCell->getFrameArea().Left()+20 &&
+ rUnion.Right() > pCell->getFrameArea().Left() ) ||
+
+ ( rUnion.Left() >= pCell->getFrameArea().Left() &&
+ rUnion.Right() < pCell->getFrameArea().Right() ));
+}
+
+bool GetAutoSumSel( const SwCursorShell& rShell, SwCellFrames& rBoxes )
+{
+ SwShellCursor* pCursor = rShell.m_pCurrentCursor;
+ if ( rShell.IsTableMode() )
+ pCursor = rShell.m_pTableCursor;
+
+ std::pair<Point, bool> tmp(pCursor->GetPtPos(), true);
+ const SwLayoutFrame *const pStart = pCursor->GetContentNode()->getLayoutFrame(
+ rShell.GetLayout(), nullptr, &tmp)->GetUpper();
+ tmp.first = pCursor->GetMkPos();
+ const SwLayoutFrame *const pEnd = pCursor->GetContentNode(false)->getLayoutFrame(
+ rShell.GetLayout(), nullptr, &tmp)->GetUpper();
+
+ const SwLayoutFrame* pSttCell = pStart;
+ while( pSttCell && !pSttCell->IsCellFrame() )
+ pSttCell = pSttCell->GetUpper();
+
+ // First, compute tables and rectangles
+ SwSelUnions aUnions;
+
+ // by default, first test above and then to the left
+ ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::Col );
+
+ bool bTstRow = true, bFound = false;
+
+ // 1. check if box above contains value/formula
+ for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ const SwTabFrame *pTable = pUnion->GetTable();
+
+ // Skip any repeated headlines in the follow:
+ const SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTable->Lower());
+
+ while( pRow )
+ {
+ if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) )
+ {
+ const SwCellFrame* pUpperCell = nullptr;
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while( pCell && pRow->IsAnLower( pCell ) )
+ {
+ if( pCell == pSttCell )
+ {
+ sal_uInt16 nWhichId = 0;
+ for( size_t n = rBoxes.size(); n; )
+ {
+ nWhichId = rBoxes[ --n ]->GetTabBox()->IsFormulaOrValueBox();
+ if( USHRT_MAX != nWhichId )
+ break;
+ }
+
+ // all boxes together, do not check the
+ // row, if a formula or value was found
+ bTstRow = 0 == nWhichId || USHRT_MAX == nWhichId;
+ bFound = true;
+ break;
+ }
+
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
+ if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) )
+ pUpperCell = static_cast<const SwCellFrame*>(pCell);
+
+ if( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ pCell = ::lcl_FindNextCellFrame( pCell );
+ }
+
+ if( pUpperCell )
+ rBoxes.push_back( const_cast< SwCellFrame* >(pUpperCell) );
+ }
+ if( bFound )
+ {
+ i = aUnions.size();
+ break;
+ }
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+
+ // 2. check if box on left contains value/formula
+ if( bTstRow )
+ {
+ bFound = false;
+
+ rBoxes.clear();
+ aUnions.clear();
+ ::MakeSelUnions( aUnions, pStart, pEnd, SwTableSearchType::Row );
+
+ for( SwSelUnions::size_type i = 0; i < aUnions.size(); ++i )
+ {
+ SwSelUnion *pUnion = &aUnions[i];
+ const SwTabFrame *pTable = pUnion->GetTable();
+
+ // Skip any repeated headlines in the follow:
+ const SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTable->Lower());
+
+ while( pRow )
+ {
+ if( pRow->getFrameArea().Overlaps( pUnion->GetUnion() ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while( pCell && pRow->IsAnLower( pCell ) )
+ {
+ if( pCell == pSttCell )
+ {
+ sal_uInt16 nWhichId = 0;
+ for( size_t n = rBoxes.size(); n; )
+ {
+ nWhichId = rBoxes[ --n ]
+ ->GetTabBox()->IsFormulaOrValueBox();
+ if( USHRT_MAX != nWhichId )
+ break;
+ }
+
+ // all boxes together, do not check the
+ // row if a formula or value was found
+ bFound = 0 != nWhichId && USHRT_MAX != nWhichId;
+ bTstRow = false;
+ break;
+ }
+
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
+ if( ::IsFrameInTableSel( pUnion->GetUnion(), pCell ) )
+ {
+ SwCellFrame* pC = const_cast<SwCellFrame*>(static_cast<const SwCellFrame*>(pCell));
+ rBoxes.push_back( pC );
+ }
+ if( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ pCell = ::lcl_FindNextCellFrame( pCell );
+ }
+ }
+ if( !bTstRow )
+ {
+ i = aUnions.size();
+ break;
+ }
+
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+ }
+
+ return bFound;
+}
+
+bool HasProtectedCells( const SwSelBoxes& rBoxes )
+{
+ bool bRet = false;
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ if( rBoxes[ n ]->GetFrameFormat()->GetProtect().IsContentProtected() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ return bRet;
+}
+
+CmpLPt::CmpLPt( const Point& rPt, const SwTableBox* pBox, bool bVertical )
+ : aPos( rPt ), pSelBox( pBox ), bVert( bVertical )
+{}
+
+static void lcl_InsTableBox( SwTableNode* pTableNd, SwDoc* pDoc, SwTableBox* pBox,
+ sal_uInt16 nInsPos, sal_uInt16 nCnt = 1 )
+{
+ OSL_ENSURE( pBox->GetSttNd(), "Box without Start-Node" );
+ SwContentNode* pCNd = pDoc->GetNodes()[ pBox->GetSttIdx() + 1 ]
+ ->GetContentNode();
+ if( pCNd && pCNd->IsTextNode() )
+ pDoc->GetNodes().InsBoxen( pTableNd, pBox->GetUpper(),
+ static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()),
+ static_cast<SwTextNode*>(pCNd)->GetTextColl(),
+ pCNd->GetpSwAttrSet(),
+ nInsPos, nCnt );
+ else
+ pDoc->GetNodes().InsBoxen( pTableNd, pBox->GetUpper(),
+ static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat()),
+ pDoc->GetDfltTextFormatColl(), nullptr,
+ nInsPos, nCnt );
+}
+
+bool IsEmptyBox( const SwTableBox& rBox, SwPaM& rPam )
+{
+ rPam.GetPoint()->nNode = *rBox.GetSttNd()->EndOfSectionNode();
+ rPam.Move( fnMoveBackward, GoInContent );
+ rPam.SetMark();
+ rPam.GetPoint()->nNode = *rBox.GetSttNd();
+ rPam.Move( fnMoveForward, GoInContent );
+ bool bRet = *rPam.GetMark() == *rPam.GetPoint()
+ && ( rBox.GetSttNd()->GetIndex() + 1 == rPam.GetPoint()->nNode.GetIndex() );
+
+ if( bRet )
+ {
+ // now check for paragraph bound flies
+ const SwFrameFormats& rFormats = *rPam.GetDoc().GetSpzFrameFormats();
+ SwNodeOffset nSttIdx = rPam.GetPoint()->nNode.GetIndex(),
+ nEndIdx = rBox.GetSttNd()->EndOfSectionIndex(),
+ nIdx;
+
+ for( auto pFormat : rFormats )
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ const SwPosition* pAPos = rAnchor.GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) &&
+ nSttIdx <= ( nIdx = pAPos->nNode.GetIndex() ) &&
+ nIdx < nEndIdx )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+void GetMergeSel( const SwPaM& rPam, SwSelBoxes& rBoxes,
+ SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo )
+{
+ rBoxes.clear();
+
+ OSL_ENSURE( rPam.GetContentNode() && rPam.GetContentNode( false ),
+ "Tabselection not on Cnt." );
+
+//JP 24.09.96: Merge with repeating TableHeadLines does not work properly.
+// Why not use point 0,0? Then it is assured the first
+// headline is contained.
+ Point aPt( 0, 0 );
+
+ const SwContentNode* pCntNd = rPam.GetContentNode();
+ std::pair<Point, bool> const tmp(aPt, true);
+ const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ pCntNd = rPam.GetContentNode(false);
+ const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+
+ // First, compute tables and rectangles
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+ if( aUnions.empty() )
+ return;
+
+ const SwTable *pTable = aUnions.front().GetTable()->GetTable();
+ SwDoc* pDoc = const_cast<SwDoc*>(pStart->GetFormat()->GetDoc());
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(pTable->GetTabSortBoxes()[ 0 ]->
+ GetSttNd()->FindTableNode());
+
+ MergePos aPosArr; // Sort-Array with the frame positions
+ tools::Long nWidth;
+ SwTableBox* pLastBox = nullptr;
+
+ SwRectFnSet aRectFnSet(pStart->GetUpper());
+
+ for ( auto & rSelUnion : aUnions )
+ {
+ const SwTabFrame *pTabFrame = rSelUnion.GetTable();
+
+ SwRect &rUnion = rSelUnion.GetUnion();
+
+ // Skip any repeated headlines in the follow:
+ const SwLayoutFrame* pRow = pTabFrame->IsFollow() ?
+ pTabFrame->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTabFrame->Lower());
+
+ while ( pRow )
+ {
+ if ( pRow->getFrameArea().Overlaps( rUnion ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while ( pCell && pRow->IsAnLower( pCell ) )
+ {
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
+ // overlap in full width?
+ if( rUnion.Top() <= pCell->getFrameArea().Top() &&
+ rUnion.Bottom() >= pCell->getFrameArea().Bottom() )
+ {
+ SwTableBox* pBox = const_cast<SwTableBox*>(static_cast<const SwCellFrame*>(pCell)->GetTabBox());
+
+ // only overlap to the right?
+ if( ( rUnion.Left() - COLFUZZY ) <= pCell->getFrameArea().Left() &&
+ ( rUnion.Right() - COLFUZZY ) > pCell->getFrameArea().Left() )
+ {
+ if( ( rUnion.Right() + COLFUZZY ) < pCell->getFrameArea().Right() )
+ {
+ sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1;
+ lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos );
+ pBox->ClaimFrameFormat();
+ SwFormatFrameSize aNew(
+ pBox->GetFrameFormat()->GetFrameSize() );
+ nWidth = rUnion.Right() - pCell->getFrameArea().Left();
+ nWidth = nWidth * aNew.GetWidth() /
+ pCell->getFrameArea().Width();
+ tools::Long nTmpWidth = aNew.GetWidth() - nWidth;
+ aNew.SetWidth( nWidth );
+ pBox->GetFrameFormat()->SetFormatAttr( aNew );
+ // this box is selected
+ pLastBox = pBox;
+ rBoxes.insert( pBox );
+ aPosArr.insert(
+ CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()),
+ pBox, aRectFnSet.IsVert() ) );
+
+ pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ];
+ aNew.SetWidth( nTmpWidth );
+ pBox->ClaimFrameFormat();
+ pBox->GetFrameFormat()->SetFormatAttr( aNew );
+
+ if( pUndo )
+ pUndo->AddNewBox( pBox->GetSttIdx() );
+ }
+ else
+ {
+ // this box is selected
+ pLastBox = pBox;
+ rBoxes.insert( pBox );
+ aPosArr.insert(
+ CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()),
+ pBox, aRectFnSet.IsVert() ) );
+ }
+ }
+ // overlapping on left- or right-side
+ else if( ( rUnion.Left() - COLFUZZY ) >= pCell->getFrameArea().Left() &&
+ ( rUnion.Right() + COLFUZZY ) < pCell->getFrameArea().Right() )
+ {
+ sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1;
+ lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos, 2 );
+ pBox->ClaimFrameFormat();
+ SwFormatFrameSize aNew(
+ pBox->GetFrameFormat()->GetFrameSize() );
+ tools::Long nLeft = rUnion.Left() - pCell->getFrameArea().Left();
+ nLeft = nLeft * aNew.GetWidth() /
+ pCell->getFrameArea().Width();
+ tools::Long nRight = pCell->getFrameArea().Right() - rUnion.Right();
+ nRight = nRight * aNew.GetWidth() /
+ pCell->getFrameArea().Width();
+ nWidth = aNew.GetWidth() - nLeft - nRight;
+
+ aNew.SetWidth( nLeft );
+ pBox->GetFrameFormat()->SetFormatAttr( aNew );
+
+ if( const SvxBoxItem* pItem = pBox->GetFrameFormat()->GetAttrSet()
+ .GetItemIfSet( RES_BOX, false ))
+ {
+ SvxBoxItem aBox( *pItem );
+ aBox.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ pBox->GetFrameFormat()->SetFormatAttr( aBox );
+ }
+
+ pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ];
+ aNew.SetWidth( nWidth );
+ pBox->ClaimFrameFormat();
+ pBox->GetFrameFormat()->SetFormatAttr( aNew );
+
+ if( pUndo )
+ pUndo->AddNewBox( pBox->GetSttIdx() );
+
+ // this box is selected
+ pLastBox = pBox;
+ rBoxes.insert( pBox );
+ aPosArr.insert(
+ CmpLPt( aRectFnSet.GetPos(pCell->getFrameArea()),
+ pBox, aRectFnSet.IsVert() ) );
+
+ pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos+1 ];
+ aNew.SetWidth( nRight );
+ pBox->ClaimFrameFormat();
+ pBox->GetFrameFormat()->SetFormatAttr( aNew );
+
+ if( pUndo )
+ pUndo->AddNewBox( pBox->GetSttIdx() );
+ }
+ // is right side of box part of the selected area?
+ else if( ( pCell->getFrameArea().Right() - COLFUZZY ) < rUnion.Right() &&
+ ( pCell->getFrameArea().Right() - COLFUZZY ) > rUnion.Left() &&
+ ( pCell->getFrameArea().Left() + COLFUZZY ) < rUnion.Left() )
+ {
+ // then we should insert a new box and adjust the widths
+ sal_uInt16 nInsPos = pBox->GetUpper()->GetBoxPos( pBox )+1;
+ lcl_InsTableBox( pTableNd, pDoc, pBox, nInsPos );
+
+ SwFormatFrameSize aNew(pBox->GetFrameFormat()->GetFrameSize() );
+ tools::Long nLeft = rUnion.Left() - pCell->getFrameArea().Left(),
+ nRight = pCell->getFrameArea().Right() - rUnion.Left();
+
+ nLeft = nLeft * aNew.GetWidth() /
+ pCell->getFrameArea().Width();
+ nRight = nRight * aNew.GetWidth() /
+ pCell->getFrameArea().Width();
+
+ aNew.SetWidth( nLeft );
+ pBox->ClaimFrameFormat()->SetFormatAttr( aNew );
+
+ // this box is selected
+ pBox = pBox->GetUpper()->GetTabBoxes()[ nInsPos ];
+ aNew.SetWidth( nRight );
+ pBox->ClaimFrameFormat();
+ pBox->GetFrameFormat()->SetFormatAttr( aNew );
+
+ pLastBox = pBox;
+ rBoxes.insert( pBox );
+ aPosArr.insert( CmpLPt( Point( rUnion.Left(),
+ pCell->getFrameArea().Top()), pBox, aRectFnSet.IsVert() ));
+
+ if( pUndo )
+ pUndo->AddNewBox( pBox->GetSttIdx() );
+ }
+ }
+ if ( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ // --> Check if table cell is not empty
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ pCell = ::lcl_FindNextCellFrame( pCell );
+ }
+ }
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+
+ // no SSelection / no boxes found
+ if( 1 >= rBoxes.size() )
+ return;
+
+ // now search all horizontally adjacent boxes and connect
+ // their contents with blanks. All vertically adjacent will be tied
+ // together as paragraphs
+
+ // 1. Solution: map array and all on same Y-level
+ // are separated with blanks
+ // all others are separated with paragraphs
+ bool bCalcWidth = true;
+ const SwTableBox* pFirstBox = aPosArr[ 0 ].pSelBox;
+
+ // JP 27.03.98: Optimise - if boxes on one row are empty,
+ // then do not insert blanks or carriage returns
+ //Block to assure SwPaM, SwPosition are deleted from stack
+ {
+ SwPaM aPam( pDoc->GetNodes() );
+
+#if defined( DEL_ONLY_EMPTY_LINES )
+ nWidth = pFirstBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ bool bEmptyLine = sal_True;
+ sal_uInt16 n, nSttPos = 0;
+
+ for( n = 0; n < aPosArr.Count(); ++n )
+ {
+ const CmpLPt& rPt = aPosArr[ n ];
+ if( n && aPosArr[ n - 1 ].Y() == rPt.Y() ) // same Y level?
+ {
+ if( bEmptyLine && !IsEmptyBox( *rPt.pSelBox, aPam ))
+ bEmptyLine = sal_False;
+ if( bCalcWidth )
+ nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ else
+ {
+ if( bCalcWidth && n )
+ bCalcWidth = false; // one line is ready
+
+ if( bEmptyLine && nSttPos < n )
+ {
+ // now complete line is empty and should not
+ // be filled with blanks and be inserted as paragraph
+ if( pUndo )
+ for( sal_uInt16 i = nSttPos; i < n; ++i )
+ pUndo->SaveCollection( *aPosArr[ i ].pSelBox );
+
+ aPosArr.Remove( nSttPos, n - nSttPos );
+ n = nSttPos;
+ }
+ else
+ nSttPos = n;
+
+ bEmptyLine = IsEmptyBox( *aPosArr[n].pSelBox, aPam );
+ }
+ }
+ if( bEmptyLine && nSttPos < n )
+ {
+ if( pUndo )
+ for( sal_uInt16 i = nSttPos; i < n; ++i )
+ pUndo->SaveCollection( *aPosArr[ i ].pSelBox );
+ aPosArr.Remove( nSttPos, n - nSttPos );
+ }
+#elif defined( DEL_EMPTY_BOXES_AT_START_AND_END )
+
+ nWidth = pFirstBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ sal_uInt16 n, nSttPos = 0, nSEndPos = 0, nESttPos = 0;
+
+ for( n = 0; n < aPosArr.Count(); ++n )
+ {
+ const CmpLPt& rPt = aPosArr[ n ];
+ if( n && aPosArr[ n - 1 ].Y() == rPt.Y() ) // same Y level?
+ {
+ bool bEmptyBox = IsEmptyBox( *rPt.pSelBox, aPam );
+ if( bEmptyBox )
+ {
+ if( nSEndPos == n ) // beginning is empty
+ nESttPos = ++nSEndPos;
+ }
+ else // end could be empty
+ nESttPos = n+1;
+
+ if( bCalcWidth )
+ nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ else
+ {
+ if( bCalcWidth && n )
+ bCalcWidth = false; // one line ready
+
+ // first those at the beginning
+ if( nSttPos < nSEndPos )
+ {
+ // now the beginning of the line is empty and should
+ // not be filled with blanks
+ if( pUndo )
+ for( sal_uInt16 i = nSttPos; i < nSEndPos; ++i )
+ pUndo->SaveCollection( *aPosArr[ i ].pSelBox );
+
+ sal_uInt16 nCnt = nSEndPos - nSttPos;
+ aPosArr.Remove( nSttPos, nCnt );
+ nESttPos -= nCnt;
+ n -= nCnt;
+ }
+
+ if( nESttPos < n )
+ {
+ // now the beginning of the line is empty and should
+ // not be filled with blanks
+ if( pUndo )
+ for( sal_uInt16 i = nESttPos; i < n; ++i )
+ pUndo->SaveCollection( *aPosArr[ i ].pSelBox );
+
+ sal_uInt16 nCnt = n - nESttPos;
+ aPosArr.Remove( nESttPos, nCnt );
+ n -= nCnt;
+ }
+
+ nSttPos = nSEndPos = nESttPos = n;
+ if( IsEmptyBox( *aPosArr[n].pSelBox, aPam ))
+ ++nSEndPos;
+ else
+ ++nESttPos;
+ }
+ }
+
+ // first those at the beginning
+ if( nSttPos < nSEndPos )
+ {
+ // now the beginning of the line is empty and should
+ // not be filled with blanks
+ if( pUndo )
+ for( sal_uInt16 i = nSttPos; i < nSEndPos; ++i )
+ pUndo->SaveCollection( *aPosArr[ i ].pSelBox );
+
+ sal_uInt16 nCnt = nSEndPos - nSttPos;
+ aPosArr.Remove( nSttPos, nCnt );
+ nESttPos -= nCnt;
+ n -= nCnt;
+ }
+ if( nESttPos < n )
+ {
+ // now the beginning of the line is empty and should
+ // not be filled with blanks
+ if( pUndo )
+ for( sal_uInt16 i = nESttPos; i < n; ++i )
+ pUndo->SaveCollection( *aPosArr[ i ].pSelBox );
+
+ sal_uInt16 nCnt = n - nESttPos;
+ aPosArr.Remove( nESttPos, nCnt );
+ }
+#else
+// DEL_ALL_EMPTY_BOXES
+
+ nWidth = 0;
+ tools::Long nY = !aPosArr.empty() ?
+ ( aRectFnSet.IsVert() ?
+ aPosArr[ 0 ].X() :
+ aPosArr[ 0 ].Y() ) :
+ 0;
+
+ for( MergePos::size_type n = 0; n < aPosArr.size(); ++n )
+ {
+ const CmpLPt& rPt = aPosArr[ n ];
+ if( bCalcWidth )
+ {
+ if( nY == ( aRectFnSet.IsVert() ? rPt.X() : rPt.Y() ) ) // same Y level?
+ nWidth += rPt.pSelBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ else
+ bCalcWidth = false; // one line ready
+ }
+
+ if( IsEmptyBox( *rPt.pSelBox, aPam ) )
+ {
+ if( pUndo )
+ pUndo->SaveCollection( *rPt.pSelBox );
+
+ aPosArr.erase( aPosArr.begin() + n );
+ --n;
+ }
+ }
+#endif
+ }
+
+ // first create new box
+ {
+ SwTableBox* pTmpBox = rBoxes[0];
+ SwTableLine* pInsLine = pTmpBox->GetUpper();
+ sal_uInt16 nInsPos = pInsLine->GetBoxPos( pTmpBox );
+
+ lcl_InsTableBox( pTableNd, pDoc, pTmpBox, nInsPos );
+ (*ppMergeBox) = pInsLine->GetTabBoxes()[ nInsPos ];
+ pInsLine->GetTabBoxes().erase( pInsLine->GetTabBoxes().begin() + nInsPos ); // remove again
+ (*ppMergeBox)->SetUpper( nullptr );
+ (*ppMergeBox)->ClaimFrameFormat();
+
+ // define the border: the upper/left side of the first box,
+ // the lower/right side of the last box:
+ if( pLastBox && pFirstBox )
+ {
+ SvxBoxItem aBox( pFirstBox->GetFrameFormat()->GetBox() );
+ const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox();
+ aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
+ aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
+ if( aBox.GetLeft() || aBox.GetTop() ||
+ aBox.GetRight() || aBox.GetBottom() )
+ (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox );
+ }
+ }
+
+ //Block to delete SwPaM, SwPosition from stack
+ if( !aPosArr.empty() )
+ {
+ SwPosition aInsPos( *(*ppMergeBox)->GetSttNd() );
+ SwNodeIndex& rInsPosNd = aInsPos.nNode;
+
+ SwPaM aPam( aInsPos );
+
+ for( const auto &rPt : aPosArr )
+ {
+ aPam.GetPoint()->nNode.Assign( *rPt.pSelBox->GetSttNd()->
+ EndOfSectionNode(), SwNodeOffset(-1) );
+ SwContentNode* pCNd = aPam.GetContentNode();
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
+
+ SwNodeIndex aSttNdIdx( *rPt.pSelBox->GetSttNd(), 1 );
+ // one node should be kept in the box (otherwise the
+ // section would be deleted during a move)
+ bool const bUndo(pDoc->GetIDocumentUndoRedo().DoesUndo());
+ if( pUndo )
+ {
+ pDoc->GetIDocumentUndoRedo().DoUndo(false);
+ }
+ pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() );
+ if( pUndo )
+ {
+ pDoc->GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+ SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode );
+ ++rInsPosNd;
+ if( pUndo )
+ pUndo->MoveBoxContent( *pDoc, aRg, rInsPosNd );
+ else
+ {
+ pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd,
+ SwMoveFlags::DEFAULT );
+ }
+ // where is now aInsPos ??
+
+ if( bCalcWidth )
+ bCalcWidth = false; // one line is ready
+
+ // skip the first TextNode
+ rInsPosNd.Assign( pDoc->GetNodes(),
+ rInsPosNd.GetNode().EndOfSectionIndex() - 2 );
+ SwTextNode* pTextNd = rInsPosNd.GetNode().GetTextNode();
+ if( pTextNd )
+ aInsPos.nContent.Assign(pTextNd, pTextNd->GetText().getLength());
+ }
+
+ // the MergeBox should contain the complete text
+ // now erase the initial TextNode
+ OSL_ENSURE( (*ppMergeBox)->GetSttIdx()+2 <
+ (*ppMergeBox)->GetSttNd()->EndOfSectionIndex(),
+ "empty box" );
+ SwNodeIndex aIdx( *(*ppMergeBox)->GetSttNd()->EndOfSectionNode(), -1 );
+ pDoc->GetNodes().Delete( aIdx );
+ }
+
+ // set width of the box
+ (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nWidth, 0 ));
+ if( pUndo )
+ pUndo->AddNewBox( (*ppMergeBox)->GetSttIdx() );
+}
+
+static bool lcl_CheckCol(FndBox_ const&, bool* pPara);
+
+static bool lcl_CheckRow( const FndLine_& rFndLine, bool* pPara )
+{
+ for (auto const& it : rFndLine.GetBoxes())
+ {
+ lcl_CheckCol(*it, pPara);
+ }
+ return *pPara;
+}
+
+static bool lcl_CheckCol( FndBox_ const& rFndBox, bool* pPara )
+{
+ if (!rFndBox.GetBox()->GetSttNd())
+ {
+ if (rFndBox.GetLines().size() !=
+ rFndBox.GetBox()->GetTabLines().size())
+ {
+ *pPara = false;
+ }
+ else
+ {
+ for (auto const& rpFndLine : rFndBox.GetLines())
+ {
+ lcl_CheckRow( *rpFndLine, pPara );
+ }
+ }
+ }
+ // is box protected ??
+ else if (rFndBox.GetBox()->GetFrameFormat()->GetProtect().IsContentProtected())
+ *pPara = false;
+ return *pPara;
+}
+
+TableMergeErr CheckMergeSel( const SwPaM& rPam )
+{
+ SwSelBoxes aBoxes;
+//JP 24.09.96: Merge with repeating TableHeadLines does not work properly.
+// Why not use point 0,0? Then it is assured the first
+// headline is contained.
+
+ Point aPt;
+ const SwContentNode* pCntNd = rPam.GetContentNode();
+ std::pair<Point, bool> tmp(aPt, true);
+ const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ pCntNd = rPam.GetContentNode(false);
+ const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ GetTableSel( pStart, pEnd, aBoxes, nullptr );
+ return CheckMergeSel( aBoxes );
+}
+
+TableMergeErr CheckMergeSel( const SwSelBoxes& rBoxes )
+{
+ TableMergeErr eRet = TableMergeErr::NoSelection;
+ if( !rBoxes.empty() )
+ {
+ eRet = TableMergeErr::Ok;
+
+ FndBox_ aFndBox( nullptr, nullptr );
+ FndPara aPara( rBoxes, &aFndBox );
+ const SwTableNode* pTableNd = aPara.rBoxes[0]->GetSttNd()->FindTableNode();
+ ForEach_FndLineCopyCol( const_cast<SwTableLines&>(pTableNd->GetTable().GetTabLines()), &aPara );
+ if( !aFndBox.GetLines().empty() )
+ {
+ bool bMergeSelOk = true;
+ FndBox_* pFndBox = &aFndBox;
+ FndLine_* pFndLine = nullptr;
+ while( pFndBox && 1 == pFndBox->GetLines().size() )
+ {
+ pFndLine = pFndBox->GetLines().front().get();
+ if( 1 == pFndLine->GetBoxes().size() )
+ pFndBox = pFndLine->GetBoxes().front().get();
+ else
+ pFndBox = nullptr;
+ }
+ if( pFndBox )
+ {
+ for (auto const& it : pFndBox->GetLines())
+ {
+ lcl_CheckRow(*it, &bMergeSelOk);
+ }
+ }
+ else if( pFndLine )
+ {
+ for (auto const& it : pFndLine->GetBoxes())
+ {
+ lcl_CheckCol(*it, &bMergeSelOk);
+ }
+ }
+ if( !bMergeSelOk )
+ eRet = TableMergeErr::TooComplex;
+ }
+ else
+ eRet = TableMergeErr::NoSelection;
+ }
+ return eRet;
+}
+
+static SwTwips lcl_CalcWish( const SwLayoutFrame *pCell, tools::Long nWish,
+ const tools::Long nAct )
+{
+ const SwLayoutFrame *pTmp = pCell;
+ if ( !nWish )
+ nWish = 1;
+
+ const bool bRTL = pCell->IsRightToLeft();
+ SwTwips nRet = bRTL ?
+ nAct - pCell->getFrameArea().Width() :
+ 0;
+
+ while ( pTmp )
+ {
+ while ( pTmp->GetPrev() )
+ {
+ pTmp = static_cast<const SwLayoutFrame*>(pTmp->GetPrev());
+ sal_Int64 nTmp = pTmp->GetFormat()->GetFrameSize().GetWidth();
+ // multiply in 64-bit to avoid overflow here!
+ nRet += ( bRTL ? -1 : 1 ) * nTmp * nAct / nWish;
+ }
+ pTmp = pTmp->GetUpper()->GetUpper();
+ if ( pTmp && !pTmp->IsCellFrame() )
+ pTmp = nullptr;
+ }
+ return nRet;
+}
+
+static void lcl_FindStartEndRow( const SwLayoutFrame *&rpStart,
+ const SwLayoutFrame *&rpEnd,
+ const bool bChkProtected )
+{
+ // Put Start at beginning of a row.
+ // Put End at the end of its row.
+ rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetUpper()->Lower());
+ while ( rpEnd->GetNext() )
+ rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetNext());
+
+ std::deque<const SwLayoutFrame *> aSttArr, aEndArr;
+ const SwLayoutFrame *pTmp;
+ for( pTmp = rpStart; (SwFrameType::Cell|SwFrameType::Row) & pTmp->GetType();
+ pTmp = pTmp->GetUpper() )
+ {
+ aSttArr.push_front( pTmp );
+ }
+ for( pTmp = rpEnd; (SwFrameType::Cell|SwFrameType::Row) & pTmp->GetType();
+ pTmp = pTmp->GetUpper() )
+ {
+ aEndArr.push_front( pTmp );
+ }
+
+ for( std::deque<const SwLayoutFrame *>::size_type n = 0; n < aEndArr.size() && n < aSttArr.size(); ++n )
+ if( aSttArr[ n ] != aEndArr[ n ] )
+ {
+ // first unequal line or box - all odds are
+ if( n & 1 ) // 1, 3, 5, ... are boxes
+ {
+ rpStart = aSttArr[ n ];
+ rpEnd = aEndArr[ n ];
+ }
+ else // 0, 2, 4, ... are lines
+ {
+ // check if start & end line are the first & last Line of the
+ // box. If not return these cells.
+ // Else the whole line with all Boxes has to be deleted.
+ rpStart = aSttArr[ n+1 ];
+ rpEnd = aEndArr[ n+1 ];
+ if( n )
+ {
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(aSttArr[ n-1 ]);
+ const SwTableLines& rLns = pCellFrame->
+ GetTabBox()->GetTabLines();
+ if( rLns[ 0 ] == static_cast<const SwRowFrame*>(aSttArr[ n ])->GetTabLine() &&
+ rLns[ rLns.size() - 1 ] ==
+ static_cast<const SwRowFrame*>(aEndArr[ n ])->GetTabLine() )
+ {
+ rpStart = rpEnd = pCellFrame;
+ while ( rpStart->GetPrev() )
+ rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetPrev());
+ while ( rpEnd->GetNext() )
+ rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetNext());
+ }
+ }
+ }
+ break;
+ }
+
+ if( !bChkProtected ) // protected cell ?
+ return;
+
+ // Beginning and end should not be in protected cells
+ while ( rpStart->GetFormat()->GetProtect().IsContentProtected() )
+ rpStart = static_cast<const SwLayoutFrame*>(rpStart->GetNext());
+ while ( rpEnd->GetFormat()->GetProtect().IsContentProtected() )
+ rpEnd = static_cast<const SwLayoutFrame*>(rpEnd->GetPrev());
+}
+
+static void lcl_FindStartEndCol( const SwLayoutFrame *&rpStart,
+ const SwLayoutFrame *&rpEnd,
+ const bool bChkProtected )
+{
+ // Beginning and end vertical till the border of the table;
+ // Consider the whole table, including master and follows.
+ // In order to start we need the mother-tableFrame
+ if( !rpStart )
+ return;
+ const SwTabFrame *pOrg = rpStart->FindTabFrame();
+ const SwTabFrame *pTab = pOrg;
+
+ SwRectFnSet aRectFnSet(pTab);
+
+ bool bRTL = pTab->IsRightToLeft();
+ const tools::Long nTmpWish = pOrg->GetFormat()->GetFrameSize().GetWidth();
+ const tools::Long nWish = ( nTmpWish > 0 ) ? nTmpWish : 1;
+
+ while ( pTab->IsFollow() )
+ {
+ const SwFrame *pTmp = pTab->FindPrev();
+ OSL_ENSURE( pTmp->IsTabFrame(), "Predecessor of Follow is not Master." );
+ pTab = static_cast<const SwTabFrame*>(pTmp);
+ }
+
+ SwTwips nSX = 0;
+ SwTwips nSX2 = 0;
+
+ if ( pTab->GetTable()->IsNewModel() )
+ {
+ nSX = aRectFnSet.GetLeft(rpStart->getFrameArea());
+ nSX2 = aRectFnSet.GetRight(rpStart->getFrameArea());
+ }
+ else
+ {
+ const SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ nSX = ::lcl_CalcWish( rpStart, nWish, nPrtWidth ) + aRectFnSet.GetPrtLeft(*pTab);
+ nSX2 = nSX + (rpStart->GetFormat()->GetFrameSize().GetWidth() * nPrtWidth / nWish);
+ }
+
+ const SwLayoutFrame *pTmp = pTab->FirstCell();
+
+ while ( pTmp &&
+ (!pTmp->IsCellFrame() ||
+ ( ( ! bRTL && aRectFnSet.GetLeft(pTmp->getFrameArea()) < nSX &&
+ aRectFnSet.GetRight(pTmp->getFrameArea())< nSX2 ) ||
+ ( bRTL && aRectFnSet.GetLeft(pTmp->getFrameArea()) > nSX &&
+ aRectFnSet.GetRight(pTmp->getFrameArea())> nSX2 ) ) ) )
+ pTmp = pTmp->GetNextLayoutLeaf();
+
+ if ( pTmp )
+ rpStart = pTmp;
+
+ pTab = pOrg;
+
+ const SwTabFrame* pLastValidTab = pTab;
+ while ( pTab->GetFollow() )
+ {
+
+ // Check if pTab->GetFollow() is a valid follow table:
+ // Only follow tables with at least on non-FollowFlowLine
+ // should be considered.
+
+ if ( pTab->HasFollowFlowLine() )
+ {
+ pTab = pTab->GetFollow();
+ const SwFrame* pTmpRow = pTab->GetFirstNonHeadlineRow();
+ if ( pTmpRow && pTmpRow->GetNext() )
+ pLastValidTab = pTab;
+ }
+ else
+ pLastValidTab = pTab = pTab->GetFollow();
+ }
+ pTab = pLastValidTab;
+
+ SwTwips nEX = 0;
+
+ if ( pTab->GetTable()->IsNewModel() )
+ {
+ nEX = aRectFnSet.GetLeft(rpEnd->getFrameArea());
+ }
+ else
+ {
+ const SwTwips nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ nEX = ::lcl_CalcWish( rpEnd, nWish, nPrtWidth ) + aRectFnSet.GetPrtLeft(*pTab);
+ }
+
+ SwFrame const*const pLastContent = pTab->FindLastContentOrTable();
+ rpEnd = pLastContent ? pLastContent->GetUpper() : nullptr;
+ // --> Made code robust. If pTab does not have a lower,
+ // we would crash here.
+ if ( !pLastContent ) return;
+
+ while( !rpEnd->IsCellFrame() )
+ rpEnd = rpEnd->GetUpper();
+
+ while ( ( bRTL && aRectFnSet.GetLeft(rpEnd->getFrameArea()) < nEX ) ||
+ ( ! bRTL && aRectFnSet.GetLeft(rpEnd->getFrameArea()) > nEX ) )
+ {
+ const SwLayoutFrame* pTmpLeaf = rpEnd->GetPrevLayoutLeaf();
+ if( !pTmpLeaf || !pTab->IsAnLower( pTmpLeaf ) )
+ break;
+ rpEnd = pTmpLeaf;
+ }
+
+ if( !bChkProtected ) // check for protected cell ?
+ return;
+
+ // Beginning and end should not be in protected cells.
+ // If necessary we should search backwards again
+ while ( rpStart->GetFormat()->GetProtect().IsContentProtected() )
+ {
+ const SwLayoutFrame *pTmpLeaf = rpStart->GetNextLayoutLeaf();
+ while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) > nEX ) // first skip line
+ pTmpLeaf = pTmpLeaf->GetNextLayoutLeaf();
+ while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) < nSX &&
+ aRectFnSet.GetRight(pTmpLeaf->getFrameArea())< nSX2 )
+ pTmpLeaf = pTmpLeaf->GetNextLayoutLeaf();
+ const SwTabFrame *pTmpTab = rpStart->FindTabFrame();
+ if ( !pTmpTab->IsAnLower( pTmpLeaf ) )
+ {
+ pTmpTab = pTmpTab->GetFollow();
+ rpStart = pTmpTab->FirstCell();
+ while ( rpStart &&
+ aRectFnSet.GetLeft(rpStart->getFrameArea()) < nSX &&
+ aRectFnSet.GetRight(rpStart->getFrameArea())< nSX2 )
+ rpStart = rpStart->GetNextLayoutLeaf();
+ }
+ else
+ rpStart = pTmpLeaf;
+ }
+ while ( rpEnd->GetFormat()->GetProtect().IsContentProtected() )
+ {
+ const SwLayoutFrame *pTmpLeaf = rpEnd->GetPrevLayoutLeaf();
+ while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) < nEX ) // skip the line for now
+ pTmpLeaf = pTmpLeaf->GetPrevLayoutLeaf();
+ while ( pTmpLeaf && aRectFnSet.GetLeft(pTmpLeaf->getFrameArea()) > nEX )
+ pTmpLeaf = pTmpLeaf->GetPrevLayoutLeaf();
+ const SwTabFrame *pTmpTab = rpEnd->FindTabFrame();
+ if ( !pTmpLeaf || !pTmpTab->IsAnLower( pTmpLeaf ) )
+ {
+ pTmpTab = static_cast<const SwTabFrame*>(pTmpTab->FindPrev());
+ OSL_ENSURE( pTmpTab->IsTabFrame(), "Predecessor of Follow not Master.");
+ rpEnd = pTmpTab->FindLastContentOrTable()->GetUpper();
+ while( !rpEnd->IsCellFrame() )
+ rpEnd = rpEnd->GetUpper();
+ while ( aRectFnSet.GetLeft(rpEnd->getFrameArea()) > nEX )
+ rpEnd = rpEnd->GetPrevLayoutLeaf();
+ }
+ else
+ rpEnd = pTmpLeaf;
+ }
+}
+
+void MakeSelUnions( SwSelUnions& rUnions, const SwLayoutFrame *pStart,
+ const SwLayoutFrame *pEnd, const SwTableSearchType eSearchType )
+{
+ while ( pStart && !pStart->IsCellFrame() )
+ pStart = pStart->GetUpper();
+ while ( pEnd && !pEnd->IsCellFrame() )
+ pEnd = pEnd->GetUpper();
+
+ if ( !pStart || !pEnd )
+ {
+ OSL_FAIL( "MakeSelUnions with pStart or pEnd not in CellFrame" );
+ return;
+ }
+
+ const SwTabFrame *pTable = pStart->FindTabFrame();
+ const SwTabFrame *pEndTable = pEnd->FindTabFrame();
+ if( !pTable || !pEndTable )
+ return;
+ bool bExchange = false;
+
+ if ( pTable != pEndTable )
+ {
+ if ( !pTable->IsAnFollow( pEndTable ) )
+ {
+ OSL_ENSURE( pEndTable->IsAnFollow( pTable ), "Tabchain in knots." );
+ bExchange = true;
+ }
+ }
+ else
+ {
+ SwRectFnSet aRectFnSet(pTable);
+ tools::Long nSttTop = aRectFnSet.GetTop(pStart->getFrameArea());
+ tools::Long nEndTop = aRectFnSet.GetTop(pEnd->getFrameArea());
+ if( nSttTop == nEndTop )
+ {
+ if( aRectFnSet.GetLeft(pStart->getFrameArea()) >
+ aRectFnSet.GetLeft(pEnd->getFrameArea()) )
+ bExchange = true;
+ }
+ else if( aRectFnSet.IsVert() == ( nSttTop < nEndTop ) )
+ bExchange = true;
+ }
+ if ( bExchange )
+ {
+ const SwLayoutFrame *pTmp = pStart;
+ pStart = pEnd;
+ pEnd = pTmp;
+ // do no resort pTable and pEndTable, set new below
+ // MA: 28. Dec. 93 Bug: 5190
+ }
+
+ // Beginning and end now nicely sorted, if required we
+ // should move them
+ if( SwTableSearchType::Row == ((~SwTableSearchType::Protect ) & eSearchType ) )
+ ::lcl_FindStartEndRow( pStart, pEnd, bool(SwTableSearchType::Protect & eSearchType) );
+ else if( SwTableSearchType::Col == ((~SwTableSearchType::Protect ) & eSearchType ) )
+ ::lcl_FindStartEndCol( pStart, pEnd, bool(SwTableSearchType::Protect & eSearchType) );
+
+ if ( !pEnd || !pStart ) return; // Made code robust.
+
+ // retrieve again, as they have been moved
+ pTable = pStart->FindTabFrame();
+ pEndTable = pEnd->FindTabFrame();
+
+ const tools::Long nStSz = pStart->GetFormat()->GetFrameSize().GetWidth();
+ const tools::Long nEdSz = pEnd->GetFormat()->GetFrameSize().GetWidth();
+ const tools::Long nWish = std::max( tools::Long(1), pTable->GetFormat()->GetFrameSize().GetWidth() );
+ while ( pTable )
+ {
+ SwRectFnSet aRectFnSet(pTable);
+ const tools::Long nOfst = aRectFnSet.GetPrtLeft(*pTable);
+ const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTable->getFramePrintArea());
+ tools::Long nSt1 = ::lcl_CalcWish( pStart, nWish, nPrtWidth ) + nOfst;
+ tools::Long nEd1 = ::lcl_CalcWish( pEnd, nWish, nPrtWidth ) + nOfst;
+
+ if ( nSt1 <= nEd1 )
+ nEd1 += static_cast<tools::Long>((nEdSz * nPrtWidth) / nWish) - 1;
+ else
+ nSt1 += static_cast<tools::Long>((nStSz * nPrtWidth) / nWish) - 1;
+
+ tools::Long nSt2;
+ tools::Long nEd2;
+ if( pTable->IsAnLower( pStart ) )
+ nSt2 = aRectFnSet.GetTop(pStart->getFrameArea());
+ else
+ nSt2 = aRectFnSet.GetTop(pTable->getFrameArea());
+ if( pTable->IsAnLower( pEnd ) )
+ nEd2 = aRectFnSet.GetBottom(pEnd->getFrameArea());
+ else
+ nEd2 = aRectFnSet.GetBottom(pTable->getFrameArea());
+ Point aSt, aEd;
+ if( nSt1 > nEd1 )
+ {
+ tools::Long nTmp = nSt1;
+ nSt1 = nEd1;
+ nEd1 = nTmp;
+ }
+ if( nSt2 > nEd2 )
+ {
+ tools::Long nTmp = nSt2;
+ nSt2 = nEd2;
+ nEd2 = nTmp;
+ }
+ if( aRectFnSet.IsVert() )
+ {
+ aSt = Point( nSt2, nSt1 );
+ aEd = Point( nEd2, nEd1 );
+ }
+ else
+ {
+ aSt = Point( nSt1, nSt2 );
+ aEd = Point( nEd1, nEd2 );
+ }
+
+ const Point aDiff( aEd - aSt );
+ SwRect aUnion( aSt, Size( aDiff.X(), aDiff.Y() ) );
+ aUnion.Justify();
+
+ if( !(SwTableSearchType::NoUnionCorrect & eSearchType ))
+ {
+ // Unfortunately the union contains rounding errors now, therefore
+ // erroneous results could occur during split/merge.
+ // To prevent these we will determine the first and last row
+ // within the union and use their values for a new union
+ const SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTable->Lower());
+
+ while ( pRow && !pRow->getFrameArea().Overlaps( aUnion ) )
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+
+ // #i31976#
+ // A follow flow row may contain empty cells. These are not
+ // considered by FirstCell(). Therefore we have to find
+ // the first cell manually:
+ const SwFrame* pTmpCell = nullptr;
+ if ( pTable->IsFollow() && pRow && pRow->IsInFollowFlowRow() )
+ {
+ const SwFrame* pTmpRow = pRow;
+ while ( pTmpRow && pTmpRow->IsRowFrame() )
+ {
+ pTmpCell = static_cast<const SwRowFrame*>(pTmpRow)->Lower();
+ pTmpRow = static_cast<const SwCellFrame*>(pTmpCell)->Lower();
+ }
+ OSL_ENSURE( !pTmpCell || pTmpCell->IsCellFrame(), "Lower of rowframe != cellframe?!" );
+ }
+
+ const SwLayoutFrame* pFirst = pTmpCell ?
+ static_cast<const SwLayoutFrame*>(pTmpCell) :
+ pRow ?
+ pRow->FirstCell() :
+ nullptr;
+
+ while ( pFirst && !::IsFrameInTableSel( aUnion, pFirst ) )
+ {
+ if ( pFirst->GetNext() )
+ {
+ pFirst = static_cast<const SwLayoutFrame*>(pFirst->GetNext());
+ if ( pFirst->Lower() && pFirst->Lower()->IsRowFrame() )
+ pFirst = pFirst->FirstCell();
+ }
+ else
+ pFirst = ::lcl_FindNextCellFrame( pFirst );
+ }
+ const SwLayoutFrame* pLast = nullptr;
+ SwFrame const*const pLastContent = pTable->FindLastContentOrTable();
+ if ( pLastContent )
+ pLast = ::lcl_FindCellFrame( pLastContent->GetUpper() );
+
+ while ( pLast && !::IsFrameInTableSel( aUnion, pLast ) )
+ pLast = ::lcl_FindCellFrame( pLast->GetPrevLayoutLeaf() );
+
+ if ( pFirst && pLast ) //Robust
+ {
+ aUnion = pFirst->getFrameArea();
+ aUnion.Union( pLast->getFrameArea() );
+ }
+ else
+ aUnion.Width( 0 );
+ }
+
+ if( aRectFnSet.GetWidth(aUnion) )
+ {
+ rUnions.emplace_back(aUnion, const_cast<SwTabFrame*>(pTable));
+ }
+
+ pTable = pTable->GetFollow();
+ if ( pTable != pEndTable && pEndTable->IsAnFollow( pTable ) )
+ pTable = nullptr;
+ }
+}
+
+bool CheckSplitCells( const SwCursorShell& rShell, sal_uInt16 nDiv,
+ const SwTableSearchType eSearchType )
+{
+ if( !rShell.IsTableMode() )
+ rShell.GetCursor();
+
+ return CheckSplitCells( *rShell.getShellCursor(false), nDiv, eSearchType );
+}
+
+bool CheckSplitCells( const SwCursor& rCursor, sal_uInt16 nDiv,
+ const SwTableSearchType eSearchType )
+{
+ if( 1 >= nDiv )
+ return false;
+
+ sal_uInt16 nMinValue = nDiv * MINLAY;
+
+ // Get start and end cell
+ Point aPtPos, aMkPos;
+ const SwShellCursor* pShCursor = dynamic_cast<const SwShellCursor*>(&rCursor);
+ if( pShCursor )
+ {
+ aPtPos = pShCursor->GetPtPos();
+ aMkPos = pShCursor->GetMkPos();
+ }
+
+ const SwContentNode* pCntNd = rCursor.GetContentNode();
+ std::pair<Point, bool> tmp(aPtPos, true);
+ const SwLayoutFrame *const pStart = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+ pCntNd = rCursor.GetContentNode(false);
+ tmp.first = aMkPos;
+ const SwLayoutFrame *const pEnd = pCntNd->getLayoutFrame(
+ pCntNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp)->GetUpper();
+
+ SwRectFnSet aRectFnSet(pStart->GetUpper());
+
+ // First, compute tables and rectangles
+ SwSelUnions aUnions;
+
+ ::MakeSelUnions( aUnions, pStart, pEnd, eSearchType );
+
+ // now search boxes for each entry and emit
+ for ( const auto& rSelUnion : aUnions )
+ {
+ const SwTabFrame *pTable = rSelUnion.GetTable();
+
+ // Skip any repeated headlines in the follow:
+ const SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ static_cast<const SwLayoutFrame*>(pTable->Lower());
+
+ while ( pRow )
+ {
+ if ( pRow->getFrameArea().Overlaps( rSelUnion.GetUnion() ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while ( pCell && pRow->IsAnLower( pCell ) )
+ {
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
+ if( ::IsFrameInTableSel( rSelUnion.GetUnion(), pCell ) )
+ {
+ if( aRectFnSet.GetWidth(pCell->getFrameArea()) < nMinValue )
+ return false;
+ }
+
+ if ( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ pCell = ::lcl_FindNextCellFrame( pCell );
+ }
+ }
+ pRow = static_cast<const SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+ return true;
+}
+
+// These Classes copy the current table selections (rBoxes),
+// into a new structure, retaining the table structure
+// new: SS for targeted erasing/restoring of the layout
+
+static void lcl_InsertRow( SwTableLine const &rLine, SwLayoutFrame *pUpper, SwFrame *pSibling )
+{
+ SwRowFrame *pRow = new SwRowFrame( rLine, pUpper );
+ if ( pUpper->IsTabFrame() && static_cast<SwTabFrame*>(pUpper)->IsFollow() )
+ {
+ SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pUpper);
+ pTabFrame->FindMaster()->InvalidatePos(); //can absorb the line
+
+ if ( pSibling && pTabFrame->IsInHeadline( *pSibling ) )
+ {
+ // Skip any repeated headlines in the follow:
+ pSibling = pTabFrame->GetFirstNonHeadlineRow();
+ }
+ }
+ pRow->Paste( pUpper, pSibling );
+ pRow->RegistFlys();
+}
+
+static void FndBoxCopyCol( SwTableBox* pBox, FndPara* pFndPara )
+{
+ std::unique_ptr<FndBox_> pFndBox(new FndBox_( pBox, pFndPara->pFndLine ));
+ if( !pBox->GetTabLines().empty() )
+ {
+ FndPara aPara( *pFndPara, pFndBox.get() );
+ ForEach_FndLineCopyCol( pFndBox->GetBox()->GetTabLines(), &aPara );
+ if( pFndBox->GetLines().empty() )
+ {
+ return;
+ }
+ }
+ else
+ {
+ if( pFndPara->rBoxes.find( pBox ) == pFndPara->rBoxes.end())
+ {
+ return;
+ }
+ }
+ pFndPara->pFndLine->GetBoxes().push_back( std::move(pFndBox) );
+}
+
+static void FndLineCopyCol( SwTableLine* pLine, FndPara* pFndPara )
+{
+ std::unique_ptr<FndLine_> pFndLine(new FndLine_(pLine, pFndPara->pFndBox));
+ FndPara aPara(*pFndPara, pFndLine.get());
+ for( auto& rpBox : pFndLine->GetLine()->GetTabBoxes() )
+ FndBoxCopyCol(rpBox, &aPara );
+ if( !pFndLine->GetBoxes().empty() )
+ {
+ pFndPara->pFndBox->GetLines().push_back( std::move(pFndLine) );
+ }
+}
+
+void ForEach_FndLineCopyCol(SwTableLines& rLines, FndPara* pFndPara )
+{
+ for( SwTableLines::iterator it = rLines.begin(); it != rLines.end(); ++it )
+ FndLineCopyCol( *it, pFndPara );
+}
+
+void FndBox_::SetTableLines( const SwSelBoxes &rBoxes, const SwTable &rTable )
+{
+ // Set pointers to lines before and after the area to process.
+ // If the first/last lines are contained in the area, then the pointers
+ // are 0. We first search for the positions of the first/last affected
+ // lines in array of the SwTable. In order to use 0 for 'no line'
+ // we adjust the positions by 1.
+
+ sal_uInt16 nStPos = USHRT_MAX;
+ sal_uInt16 nEndPos= 0;
+
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ SwTableLine *pLine = rBoxes[i]->GetUpper();
+ while ( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+ const sal_uInt16 nPos = rTable.GetTabLines().GetPos(
+ const_cast<const SwTableLine*&>(pLine) ) + 1;
+
+ OSL_ENSURE( nPos != USHRT_MAX, "TableLine not found." );
+
+ if( nStPos > nPos )
+ nStPos = nPos;
+
+ if( nEndPos < nPos )
+ nEndPos = nPos;
+ }
+ if (USHRT_MAX != nStPos && nStPos > 1)
+ m_pLineBefore = rTable.GetTabLines()[nStPos - 2];
+ if ( nEndPos < rTable.GetTabLines().size() )
+ m_pLineBehind = rTable.GetTabLines()[nEndPos];
+}
+
+void FndBox_::SetTableLines( const SwTable &rTable )
+{
+ // Set pointers to lines before and after the area to process.
+ // If the first/last lines are contained in the area, then the pointers
+ // are 0. The positions of the first/last affected lines in the array
+ // of the SwTable are in FndBox. In order to use 0 for 'no line'
+ // we adjust the positions by 1.
+
+ if( GetLines().empty() )
+ return;
+
+ SwTableLine* pTmpLine = GetLines().front()->GetLine();
+ sal_uInt16 nPos = rTable.GetTabLines().GetPos( pTmpLine );
+ OSL_ENSURE( USHRT_MAX != nPos, "Line is not in table" );
+ if( nPos )
+ m_pLineBefore = rTable.GetTabLines()[ nPos - 1 ];
+
+ pTmpLine = GetLines().back()->GetLine();
+ nPos = rTable.GetTabLines().GetPos( pTmpLine );
+ OSL_ENSURE( USHRT_MAX != nPos, "Line is not in the table" );
+ if( ++nPos < rTable.GetTabLines().size() )
+ m_pLineBehind = rTable.GetTabLines()[nPos];
+}
+
+inline void UnsetFollow( SwFlowFrame *pTab )
+{
+ pTab->m_pPrecede = nullptr;
+}
+
+void FndBox_::DelFrames( SwTable &rTable )
+{
+ // All lines between pLineBefore and pLineBehind should be cut
+ // from the layout and erased.
+ // If this creates empty Follows we should destroy these.
+ // If a master is destroyed, the follow should become master.
+ // Always a TabFrame should remain.
+
+ sal_uInt16 nStPos = 0;
+ sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1;
+ if( rTable.IsNewModel() && m_pLineBefore )
+ rTable.CheckRowSpan( m_pLineBefore, true );
+ if ( m_pLineBefore )
+ {
+ nStPos = rTable.GetTabLines().GetPos(
+ const_cast<const SwTableLine*&>(m_pLineBefore) );
+ OSL_ENSURE( nStPos != USHRT_MAX, "The fox stole the line!" );
+ ++nStPos;
+ }
+ if( rTable.IsNewModel() && m_pLineBehind )
+ rTable.CheckRowSpan( m_pLineBehind, false );
+ if ( m_pLineBehind )
+ {
+ nEndPos = rTable.GetTabLines().GetPos(
+ const_cast<const SwTableLine*&>(m_pLineBehind) );
+ OSL_ENSURE( nEndPos != USHRT_MAX, "The fox stole the line!" );
+ if (nEndPos != 0)
+ --nEndPos;
+ }
+
+ for ( sal_uInt16 i = nStPos; i <= nEndPos; ++i)
+ {
+ SwFrameFormat *pFormat = rTable.GetTabLines()[i]->GetFrameFormat();
+ SwIterator<SwRowFrame,SwFormat> aIter( *pFormat );
+ for ( SwRowFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ if ( pFrame->GetTabLine() == rTable.GetTabLines()[i] )
+ {
+ bool bDel = true;
+ SwTabFrame *pUp = !pFrame->GetPrev() && !pFrame->GetNext() ?
+ static_cast<SwTabFrame*>(pFrame->GetUpper()) : nullptr;
+ if ( !pUp )
+ {
+ const sal_uInt16 nRepeat =
+ static_cast<SwTabFrame*>(pFrame->GetUpper())->GetTable()->GetRowsToRepeat();
+ if ( nRepeat > 0 &&
+ static_cast<SwTabFrame*>(pFrame->GetUpper())->IsFollow() )
+ {
+ if ( !pFrame->GetNext() )
+ {
+ SwRowFrame* pFirstNonHeadline =
+ static_cast<SwTabFrame*>(pFrame->GetUpper())->GetFirstNonHeadlineRow();
+ if ( pFirstNonHeadline == pFrame )
+ {
+ pUp = static_cast<SwTabFrame*>(pFrame->GetUpper());
+ }
+ }
+ }
+ }
+ if ( pUp )
+ {
+ SwTabFrame *pFollow = pUp->GetFollow();
+ SwTabFrame *pPrev = pUp->IsFollow() ? pUp : nullptr;
+ if ( pPrev )
+ {
+ SwFrame *pTmp = pPrev->FindPrev();
+ OSL_ENSURE( pTmp->IsTabFrame(),
+ "Predecessor of Follow is no Master.");
+ pPrev = static_cast<SwTabFrame*>(pTmp);
+ }
+ if ( pPrev )
+ {
+ pPrev->SetFollow( pFollow );
+ // #i60340# Do not transfer the
+ // flag from pUp to pPrev. pUp may still have the
+ // flag set although there is not more follow flow
+ // line associated with pUp.
+ pPrev->SetFollowFlowLine( false );
+ }
+ else if ( pFollow )
+ ::UnsetFollow( pFollow );
+
+ // A TableFrame should always remain!
+ if ( pPrev || pFollow )
+ {
+ // OD 26.08.2003 #i18103# - if table is in a section,
+ // lock the section, to avoid its delete.
+ {
+ SwSectionFrame* pSctFrame = pUp->FindSctFrame();
+ bool bOldSectLock = false;
+ if ( pSctFrame )
+ {
+ bOldSectLock = pSctFrame->IsColLocked();
+ pSctFrame->ColLock();
+ }
+ pUp->Cut();
+ if ( pSctFrame && !bOldSectLock )
+ {
+ pSctFrame->ColUnlock();
+ }
+ }
+ SwFrame::DestroyFrame(pUp);
+ bDel = false; // Row goes to /dev/null.
+ }
+ }
+ if ( bDel )
+ {
+ SwFrame* pTabFrame = pFrame->GetUpper();
+ if ( pTabFrame->IsTabFrame() &&
+ !pFrame->GetNext() &&
+ static_cast<SwTabFrame*>(pTabFrame)->GetFollow() )
+ {
+ // We do not delete the follow flow line,
+ // this will be done automatically in the
+ // next turn.
+ static_cast<SwTabFrame*>(pTabFrame)->SetFollowFlowLine( false );
+ }
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+ }
+ }
+ }
+}
+
+static bool lcl_IsLineOfTableFrame( const SwTabFrame& rTable, const SwFrame& rChk )
+{
+ const SwTabFrame* pTableFrame = rChk.FindTabFrame();
+ if( pTableFrame->IsFollow() )
+ pTableFrame = pTableFrame->FindMaster( true );
+ return &rTable == pTableFrame;
+}
+
+static void lcl_UpdateRepeatedHeadlines( SwTabFrame& rTabFrame, bool bCalcLowers )
+{
+ OSL_ENSURE( rTabFrame.IsFollow(), "lcl_UpdateRepeatedHeadlines called for non-follow tab" );
+
+ // Delete remaining headlines:
+ SwRowFrame* pLower = nullptr;
+ while ( nullptr != ( pLower = static_cast<SwRowFrame*>(rTabFrame.Lower()) ) && pLower->IsRepeatedHeadline() )
+ {
+ pLower->Cut();
+ SwFrame::DestroyFrame(pLower);
+ }
+
+ // Insert fresh set of headlines:
+ pLower = static_cast<SwRowFrame*>(rTabFrame.Lower());
+ SwTable& rTable = *rTabFrame.GetTable();
+ const sal_uInt16 nRepeat = rTable.GetRowsToRepeat();
+ for ( sal_uInt16 nIdx = 0; nIdx < nRepeat; ++nIdx )
+ {
+ SwRowFrame* pHeadline = new SwRowFrame( *rTable.GetTabLines()[ nIdx ], &rTabFrame );
+ pHeadline->SetRepeatedHeadline( true );
+ pHeadline->Paste( &rTabFrame, pLower );
+ pHeadline->RegistFlys();
+ }
+
+ if ( bCalcLowers )
+ rTabFrame.SetCalcLowers();
+}
+
+void FndBox_::MakeFrames( SwTable &rTable )
+{
+ // All lines between pLineBefore and pLineBehind should be re-generated in layout.
+ // And this for all instances of a table (for example in header/footer).
+ sal_uInt16 nStPos = 0;
+ sal_uInt16 nEndPos= rTable.GetTabLines().size() - 1;
+ SwRootFrame* pLayout =
+ rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout();
+ bool bHideChanges = pLayout && pLayout->IsHideRedlines();
+
+ if ( m_pLineBefore )
+ {
+ nStPos = rTable.GetTabLines().GetPos(
+ const_cast<const SwTableLine*&>(m_pLineBefore) );
+ OSL_ENSURE( nStPos != USHRT_MAX, "Fox stole the line!" );
+ ++nStPos;
+
+ }
+ if ( m_pLineBehind )
+ {
+ nEndPos = rTable.GetTabLines().GetPos(
+ const_cast<const SwTableLine*&>(m_pLineBehind) );
+ OSL_ENSURE( nEndPos != USHRT_MAX, "Fox stole the line!" );
+ --nEndPos;
+ }
+ // now big insert operation for all tables.
+ SwIterator<SwTabFrame,SwFormat> aTabIter( *rTable.GetFrameFormat() );
+ for ( SwTabFrame *pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() )
+ {
+ if ( !pTable->IsFollow() )
+ {
+ SwRowFrame *pSibling = nullptr;
+ SwFrame *pUpperFrame = nullptr;
+ int i;
+ for ( i = rTable.GetTabLines().size()-1;
+ i >= 0 && !pSibling; --i )
+ {
+ SwTableLine *pLine = m_pLineBehind ? m_pLineBehind :
+ rTable.GetTabLines()[o3tl::narrowing<sal_uInt16>(i)];
+ SwIterator<SwRowFrame,SwFormat> aIter( *pLine->GetFrameFormat() );
+ pSibling = aIter.First();
+ while ( pSibling && (
+ pSibling->GetTabLine() != pLine ||
+ !lcl_IsLineOfTableFrame( *pTable, *pSibling ) ||
+ pSibling->IsRepeatedHeadline() ||
+ // #i53647# If !pLineBehind,
+ // IsInSplitTableRow() should be checked.
+ ( m_pLineBehind && pSibling->IsInFollowFlowRow() ) ||
+ (!m_pLineBehind && pSibling->IsInSplitTableRow() ) ) )
+ {
+ pSibling = aIter.Next();
+ }
+ }
+ if ( pSibling )
+ {
+ pUpperFrame = pSibling->GetUpper();
+ if ( !m_pLineBehind )
+ pSibling = nullptr;
+ }
+ else
+// ???? or is this the last Follow of the table ????
+ pUpperFrame = pTable;
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for ( sal_uInt16 j = nStPos; j <= nEndPos; ++j )
+ {
+ SwTableLine * pLine = rTable.GetTabLines()[j];
+ if ( !bHideChanges || !pLine->IsDeleted(nRedlinePos) )
+ ::lcl_InsertRow( *pLine,
+ static_cast<SwLayoutFrame*>(pUpperFrame), pSibling );
+ }
+ if ( pUpperFrame->IsTabFrame() )
+ static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers();
+ }
+ else if ( rTable.GetRowsToRepeat() > 0 )
+ {
+ // Insert new headlines:
+ lcl_UpdateRepeatedHeadlines( *pTable, true );
+ }
+ }
+}
+
+void FndBox_::MakeNewFrames( SwTable &rTable, const sal_uInt16 nNumber,
+ const bool bBehind )
+{
+ // Create Frames for newly inserted lines
+ // bBehind == true: before pLineBehind
+ // == false: after pLineBefore
+ const sal_uInt16 nBfPos = m_pLineBefore ?
+ rTable.GetTabLines().GetPos( const_cast<const SwTableLine*&>(m_pLineBefore) ) :
+ USHRT_MAX;
+ const sal_uInt16 nBhPos = m_pLineBehind ?
+ rTable.GetTabLines().GetPos( const_cast<const SwTableLine*&>(m_pLineBehind) ) :
+ USHRT_MAX;
+
+ //nNumber: how often did we insert
+ //nCnt: how many were inserted nNumber times
+
+ const sal_uInt16 nCnt =
+ ((nBhPos != USHRT_MAX ? nBhPos : rTable.GetTabLines().size()) -
+ (nBfPos != USHRT_MAX ? nBfPos + 1 : 0)) / (nNumber + 1);
+
+ // search the Master-TabFrame
+ SwIterator<SwTabFrame,SwFormat> aTabIter( *rTable.GetFrameFormat() );
+ SwTabFrame *pTable;
+ for ( pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() )
+ {
+ if( !pTable->IsFollow() )
+ {
+ SwRowFrame* pSibling = nullptr;
+ SwLayoutFrame *pUpperFrame = nullptr;
+ if ( bBehind )
+ {
+ if ( m_pLineBehind )
+ {
+ SwIterator<SwRowFrame,SwFormat> aIter( *m_pLineBehind->GetFrameFormat() );
+ pSibling = aIter.First();
+ while ( pSibling && (
+ // only consider row frames associated with pLineBehind:
+ pSibling->GetTabLine() != m_pLineBehind ||
+ // only consider row frames that are in pTables Master-Follow chain:
+ !lcl_IsLineOfTableFrame( *pTable, *pSibling ) ||
+ // only consider row frames that are not repeated headlines:
+ pSibling->IsRepeatedHeadline() ||
+ // only consider row frames that are not follow flow rows
+ pSibling->IsInFollowFlowRow() ) )
+ {
+ pSibling = aIter.Next();
+ }
+ }
+ if ( pSibling )
+ pUpperFrame = pSibling->GetUpper();
+ else
+ {
+ while( pTable->GetFollow() )
+ pTable = pTable->GetFollow();
+ pUpperFrame = pTable;
+ }
+ const sal_uInt16 nMax = nBhPos != USHRT_MAX ?
+ nBhPos : rTable.GetTabLines().size();
+
+ sal_uInt16 i = nBfPos != USHRT_MAX ? nBfPos + 1 + nCnt : nCnt;
+
+ for ( ; i < nMax; ++i )
+ ::lcl_InsertRow( *rTable.GetTabLines()[i], pUpperFrame, pSibling );
+ if ( pUpperFrame->IsTabFrame() )
+ static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers();
+ }
+ else // insert before
+ {
+ sal_uInt16 i;
+
+ // We are looking for the frame that is behind the row frame
+ // that should be inserted.
+ for ( i = 0; !pSibling; ++i )
+ {
+ SwTableLine* pLine = m_pLineBefore ? m_pLineBefore : rTable.GetTabLines()[i];
+
+ SwIterator<SwRowFrame,SwFormat> aIter( *pLine->GetFrameFormat() );
+ pSibling = aIter.First();
+
+ while ( pSibling && (
+ // only consider row frames associated with pLineBefore:
+ pSibling->GetTabLine() != pLine ||
+ // only consider row frames that are in pTables Master-Follow chain:
+ !lcl_IsLineOfTableFrame( *pTable, *pSibling ) ||
+ // only consider row frames that are not repeated headlines:
+ pSibling->IsRepeatedHeadline() ||
+ // 1. case: pLineBefore == 0:
+ // only consider row frames that are not follow flow rows
+ // 2. case: pLineBefore != 0:
+ // only consider row frames that are not split table rows
+ // #i37476# If !pLineBefore,
+ // check IsInFollowFlowRow instead of IsInSplitTableRow.
+ ( ( !m_pLineBefore && pSibling->IsInFollowFlowRow() ) ||
+ ( m_pLineBefore && pSibling->IsInSplitTableRow() ) ) ) )
+ {
+ pSibling = aIter.Next();
+ }
+ }
+
+ pUpperFrame = pSibling->GetUpper();
+ if ( m_pLineBefore )
+ pSibling = static_cast<SwRowFrame*>( pSibling->GetNext() );
+
+ sal_uInt16 nMax = nBhPos != USHRT_MAX ?
+ nBhPos - nCnt :
+ rTable.GetTabLines().size() - nCnt;
+
+ i = nBfPos != USHRT_MAX ? nBfPos + 1 : 0;
+ for ( ; i < nMax; ++i )
+ ::lcl_InsertRow( *rTable.GetTabLines()[i],
+ pUpperFrame, pSibling );
+ if ( pUpperFrame->IsTabFrame() )
+ static_cast<SwTabFrame*>(pUpperFrame)->SetCalcLowers();
+ }
+ }
+ }
+
+ // If necessary headlines should be processed. In order to
+ // not to fragment good code, we iterate once more.
+ const sal_uInt16 nRowsToRepeat = rTable.GetRowsToRepeat();
+ if ( !(nRowsToRepeat > 0 &&
+ ( ( !bBehind && ( nBfPos == USHRT_MAX || nBfPos + 1 < nRowsToRepeat ) ) ||
+ ( bBehind && ( ( nBfPos == USHRT_MAX && nRowsToRepeat > 1 ) || nBfPos + 2 < nRowsToRepeat ) ) )) )
+ return;
+
+ for ( pTable = aTabIter.First(); pTable; pTable = aTabIter.Next() )
+ {
+ if ( pTable->Lower() )
+ {
+ if ( pTable->IsFollow() )
+ {
+ lcl_UpdateRepeatedHeadlines( *pTable, true );
+ }
+
+ OSL_ENSURE( static_cast<SwRowFrame*>(pTable->Lower())->GetTabLine() ==
+ rTable.GetTabLines()[0], "MakeNewFrames: Table corruption!" );
+ }
+ }
+}
+
+bool FndBox_::AreLinesToRestore( const SwTable &rTable ) const
+{
+ // Should we call MakeFrames here?
+
+ if ( !m_pLineBefore && !m_pLineBehind && !rTable.GetTabLines().empty() )
+ return true;
+
+ sal_uInt16 nBfPos;
+ if(m_pLineBefore)
+ {
+ const SwTableLine* rLBefore = const_cast<const SwTableLine*>(m_pLineBefore);
+ nBfPos = rTable.GetTabLines().GetPos( rLBefore );
+ }
+ else
+ nBfPos = USHRT_MAX;
+
+ sal_uInt16 nBhPos;
+ if(m_pLineBehind)
+ {
+ const SwTableLine* rLBehind = const_cast<const SwTableLine*>(m_pLineBehind);
+ nBhPos = rTable.GetTabLines().GetPos( rLBehind );
+ }
+ else
+ nBhPos = USHRT_MAX;
+
+ if ( nBfPos == nBhPos ) // Should never occur.
+ {
+ OSL_FAIL( "Table, erase but not on any area !?!" );
+ return false;
+ }
+
+ if ( rTable.GetRowsToRepeat() > 0 )
+ {
+ // oops: should the repeated headline have been deleted??
+ SwIterator<SwTabFrame,SwFormat> aIter( *rTable.GetFrameFormat() );
+ for( SwTabFrame* pTable = aIter.First(); pTable; pTable = aIter.Next() )
+ {
+ if( pTable->IsFollow() )
+ {
+ // Insert new headlines:
+ lcl_UpdateRepeatedHeadlines( *pTable, false );
+ }
+ }
+ }
+
+ // Some adjacent lines at the beginning of the table have been deleted:
+ if ( nBfPos == USHRT_MAX && nBhPos == 0 )
+ return false;
+
+ // Some adjacent lines at the end of the table have been deleted:
+ if ( nBhPos == USHRT_MAX && nBfPos == (rTable.GetTabLines().size() - 1) )
+ return false;
+
+ // Some adjacent lines in the middle of the table have been deleted:
+ if ( nBfPos != USHRT_MAX && nBhPos != USHRT_MAX && (nBfPos + 1) == nBhPos )
+ return false;
+
+ // The structure of the deleted lines is more complex due to split lines.
+ // A call of MakeFrames() is necessary.
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/graphic/GraphicSizeCheck.cxx b/sw/source/core/graphic/GraphicSizeCheck.cxx
new file mode 100644
index 000000000..9fe2de879
--- /dev/null
+++ b/sw/source/core/graphic/GraphicSizeCheck.cxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <GraphicSizeCheck.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdobj.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+
+#include <ModelTraverser.hxx>
+#include <ndgrf.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <fmtfsize.hxx>
+#include <view.hxx>
+#include <wrtsh.hxx>
+#include <cmdid.h>
+
+using namespace css;
+
+namespace sw
+{
+GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, const SwGrfNode* pGraphicNode)
+ : m_pGraphicNode(pGraphicNode)
+{
+ constexpr double fLowPercentage = 110;
+ constexpr double fHighPercentage = 50;
+
+ m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI);
+ m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI);
+}
+
+bool GraphicSizeViolation::check()
+{
+ auto pFrameFormat = m_pGraphicNode->GetFlyFormat();
+ Graphic aGraphic = m_pGraphicNode->GetGraphic();
+ Size aSizePixel = aGraphic.GetSizePixel();
+ Size aFrameSize(pFrameFormat->GetFrameSize().GetSize());
+
+ double nSizeXInch
+ = o3tl::convert(double(aFrameSize.Width()), o3tl::Length::twip, o3tl::Length::in);
+ double nSizeYInch
+ = o3tl::convert(double(aFrameSize.Height()), o3tl::Length::twip, o3tl::Length::in);
+
+ m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch);
+ m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch);
+
+ return isDPITooLow() || isDPITooHigh();
+}
+
+const OUString& GraphicSizeViolation::getGraphicName()
+{
+ return m_pGraphicNode->GetFlyFormat()->GetName();
+}
+
+namespace
+{
+class GraphicSizeCheckHandler : public ModelTraverseHandler
+{
+private:
+ sal_Int32 m_nDPI;
+ std::vector<std::unique_ptr<GraphicSizeViolation>>& m_rGraphicSizeViolationList;
+
+public:
+ GraphicSizeCheckHandler(
+ sal_Int32 nDPI,
+ std::vector<std::unique_ptr<GraphicSizeViolation>>& rGraphicSizeViolationList)
+ : m_nDPI(nDPI)
+ , m_rGraphicSizeViolationList(rGraphicSizeViolationList)
+ {
+ }
+
+ void handleNode(SwNode* pNode) override
+ {
+ if (!pNode->IsGrfNode())
+ return;
+
+ auto pEntry = std::make_unique<GraphicSizeViolation>(m_nDPI, pNode->GetGrfNode());
+ if (pEntry->check())
+ {
+ m_rGraphicSizeViolationList.push_back(std::move(pEntry));
+ }
+ }
+
+ void handleSdrObject(SdrObject* /*pObject*/) override {}
+};
+
+} // end anonymous namespace
+
+void GraphicSizeCheck::check()
+{
+ sal_Int32 nDPI = m_pDocument->getIDocumentSettingAccess().getImagePreferredDPI();
+ if (nDPI == 0)
+ return;
+
+ auto pHandler = std::make_shared<GraphicSizeCheckHandler>(nDPI, m_aGraphicSizeViolationList);
+ ModelTraverser aModelTraverser(m_pDocument);
+ aModelTraverser.addNodeHandler(pHandler);
+ aModelTraverser.traverse();
+}
+
+OUString GraphicSizeCheckGUIEntry::getText()
+{
+ OUString sText;
+
+ if (m_pViolation->isDPITooLow())
+ {
+ sText = SwResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW);
+ }
+ else if (m_pViolation->isDPITooHigh())
+ {
+ sText = SwResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH);
+ }
+
+ sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName());
+ sText = sText.replaceAll("%DPIX%", OUString::number(m_pViolation->getDPIX()));
+ sText = sText.replaceAll("%DPIY%", OUString::number(m_pViolation->getDPIY()));
+
+ return sText;
+}
+
+void GraphicSizeCheckGUIEntry::markObject()
+{
+ SwWrtShell* pWrtShell = m_pDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->GotoFly(m_pViolation->getGraphicName(), FLYCNTTYPE_ALL, true);
+}
+
+void GraphicSizeCheckGUIEntry::runProperties()
+{
+ markObject();
+ SwWrtShell* pWrtShell = m_pDocument->GetDocShell()->GetWrtShell();
+ pWrtShell->GetView().GetViewFrame()->GetDispatcher()->Execute(FN_FORMAT_GRAFIC_DLG,
+ SfxCallMode::SYNCHRON);
+}
+
+GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SwDoc* pDocument)
+{
+ GraphicSizeCheck aCheck(pDocument);
+ aCheck.check();
+
+ auto& rCollection = getCollection();
+ for (auto& rpViolation : aCheck.getViolationList())
+ {
+ auto rGUIEntry
+ = std::make_unique<GraphicSizeCheckGUIEntry>(pDocument, std::move(rpViolation));
+ rCollection.push_back(std::move(rGUIEntry));
+ }
+}
+
+OUString GraphicSizeCheckGUIResult::getTitle()
+{
+ return SwResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE);
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/graphic/grfatr.cxx b/sw/source/core/graphic/grfatr.cxx
new file mode 100644
index 000000000..50a84c12f
--- /dev/null
+++ b/sw/source/core/graphic/grfatr.cxx
@@ -0,0 +1,341 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/drawing/ColorMode.hpp>
+#include <o3tl/any.hxx>
+#include <vcl/GraphicAttributes.hxx>
+#include <grfatr.hxx>
+#include <swunohelper.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <unomid.h>
+
+using namespace ::com::sun::star;
+
+SwMirrorGrf* SwMirrorGrf::Clone( SfxItemPool* ) const
+{
+ return new SwMirrorGrf( *this );
+}
+
+sal_uInt16 SwMirrorGrf::GetValueCount() const
+{
+ return 4;
+}
+
+bool SwMirrorGrf::operator==( const SfxPoolItem& rItem) const
+{
+ return SfxEnumItem::operator==(rItem) &&
+ static_cast<const SwMirrorGrf&>(rItem).IsGrfToggle() == IsGrfToggle();
+}
+
+static bool lcl_IsHoriOnEvenPages(MirrorGraph nEnum, bool bToggle)
+{
+ bool bEnum = nEnum == MirrorGraph::Vertical ||
+ nEnum == MirrorGraph::Both;
+ return bEnum != bToggle;
+}
+
+static bool lcl_IsHoriOnOddPages(MirrorGraph nEnum)
+{
+ bool bEnum = nEnum == MirrorGraph::Vertical ||
+ nEnum == MirrorGraph::Both;
+ return bEnum;
+}
+
+bool SwMirrorGrf::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+ bool bVal = false;
+ // vertical and horizontal were swapped at some point
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_MIRROR_HORZ_EVEN_PAGES:
+ bVal = lcl_IsHoriOnEvenPages(GetValue(), IsGrfToggle());
+ break;
+ case MID_MIRROR_HORZ_ODD_PAGES:
+ bVal = lcl_IsHoriOnOddPages(GetValue());
+ break;
+ case MID_MIRROR_VERT:
+ bVal = GetValue() == MirrorGraph::Horizontal ||
+ GetValue() == MirrorGraph::Both;
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ rVal <<= bVal;
+ return bRet;
+}
+
+bool SwMirrorGrf::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ // vertical and horizontal were swapped at some point
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_MIRROR_HORZ_EVEN_PAGES:
+ case MID_MIRROR_HORZ_ODD_PAGES:
+ {
+ bool bIsVert = GetValue() == MirrorGraph::Horizontal ||
+ GetValue() == MirrorGraph::Both;
+ bool bOnOddPages = nMemberId == MID_MIRROR_HORZ_EVEN_PAGES ?
+ lcl_IsHoriOnOddPages(GetValue()) : bVal;
+ bool bOnEvenPages = nMemberId == MID_MIRROR_HORZ_ODD_PAGES ?
+ lcl_IsHoriOnEvenPages(GetValue(), IsGrfToggle()) : bVal;
+ MirrorGraph nEnum = bOnOddPages ?
+ bIsVert ? MirrorGraph::Both : MirrorGraph::Vertical :
+ bIsVert ? MirrorGraph::Horizontal : MirrorGraph::Dont;
+ bool bToggle = bOnOddPages != bOnEvenPages;
+ SetValue(nEnum);
+ SetGrfToggle( bToggle );
+ }
+ break;
+ case MID_MIRROR_VERT:
+ if ( bVal )
+ {
+ if ( GetValue() == MirrorGraph::Vertical )
+ SetValue( MirrorGraph::Both );
+ else if ( GetValue() != MirrorGraph::Both )
+ SetValue( MirrorGraph::Horizontal );
+ }
+ else
+ {
+ if ( GetValue() == MirrorGraph::Both )
+ SetValue( MirrorGraph::Vertical );
+ else if ( GetValue() == MirrorGraph::Horizontal )
+ SetValue( MirrorGraph::Dont );
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwCropGrf::SwCropGrf()
+ : SvxGrfCrop( RES_GRFATR_CROPGRF )
+{}
+
+SwCropGrf::SwCropGrf(sal_Int32 nL, sal_Int32 nR, sal_Int32 nT, sal_Int32 nB )
+ : SvxGrfCrop( nL, nR, nT, nB, RES_GRFATR_CROPGRF )
+{}
+
+SwCropGrf* SwCropGrf::Clone( SfxItemPool* ) const
+{
+ return new SwCropGrf( *this );
+}
+
+Degree10 SwRotationGrf::checkAndCorrectValue(Degree10 nValue)
+{
+ if (nValue < 0_deg10)
+ {
+ // smaller zero, modulo (will keep negative) and add one range
+ DBG_ASSERT(false, "SwRotationGrf: Value is in 10th degree and *has* to be in [0 .. 3600[ (!)");
+ return 3600_deg10 + nValue % 3600_deg10;
+ }
+ else if (nValue >= 3600_deg10)
+ {
+ // bigger range, use modulo
+ DBG_ASSERT(false, "SwRotationGrf: Value is in 10th degree and *has* to be in [0 .. 3600[ (!)");
+ return nValue % 3600_deg10;
+ }
+
+ return nValue;
+}
+
+SwRotationGrf::SwRotationGrf( Degree10 nVal, const Size& rSz )
+ // tdf#115529 check and evtl. correct value
+: SfxUInt16Item( RES_GRFATR_ROTATION, checkAndCorrectValue(nVal).get() ),
+ m_aUnrotatedSize( rSz )
+{
+}
+
+SwRotationGrf* SwRotationGrf::Clone( SfxItemPool * ) const
+{
+ return new SwRotationGrf( *this );
+}
+
+bool SwRotationGrf::operator==( const SfxPoolItem& rCmp ) const
+{
+ return SfxUInt16Item::operator==( rCmp ) &&
+ GetUnrotatedSize() == static_cast<const SwRotationGrf&>(rCmp).GetUnrotatedSize();
+}
+
+bool SwRotationGrf::QueryValue( uno::Any& rVal, sal_uInt8 ) const
+{
+ // SfxUInt16Item::QueryValue returns sal_Int32 in Any now... (srx642w)
+ // where we still want this to be a sal_Int16
+ rVal <<= static_cast<sal_Int16>(GetValue());
+ return true;
+}
+
+bool SwRotationGrf::PutValue( const uno::Any& rVal, sal_uInt8 )
+{
+ // SfxUInt16Item::QueryValue returns sal_Int32 in Any now... (srx642w)
+ // where we still want this to be a sal_Int16
+ sal_Int16 nValue = 0;
+ if (rVal >>= nValue)
+ {
+ // sal_uInt16 argument needed
+ // tdf#115529 check and evtl. correct value
+ SetValue(checkAndCorrectValue(Degree10(nValue)));
+ return true;
+ }
+
+ OSL_FAIL( "SwRotationGrf::PutValue - Wrong type!" );
+ return false;
+}
+
+// Sw___Grf::Clone(..)
+
+SwLuminanceGrf* SwLuminanceGrf::Clone( SfxItemPool * ) const
+{
+ return new SwLuminanceGrf( *this );
+}
+
+SwContrastGrf* SwContrastGrf::Clone( SfxItemPool * ) const
+{
+ return new SwContrastGrf( *this );
+}
+
+SwChannelRGrf* SwChannelRGrf::Clone( SfxItemPool * ) const
+{
+ return new SwChannelRGrf( *this );
+}
+
+SwChannelGGrf* SwChannelGGrf::Clone( SfxItemPool * ) const
+{
+ return new SwChannelGGrf( *this );
+}
+
+SwChannelBGrf* SwChannelBGrf::Clone( SfxItemPool * ) const
+{
+ return new SwChannelBGrf( *this );
+}
+
+SwGammaGrf* SwGammaGrf::Clone( SfxItemPool * ) const
+{
+ return new SwGammaGrf( *this );
+}
+
+// SwGammaGrf
+
+bool SwGammaGrf::operator==( const SfxPoolItem& rCmp ) const
+{
+ return SfxPoolItem::operator==( rCmp ) &&
+ m_nValue == static_cast<const SwGammaGrf&>(rCmp).GetValue();
+}
+
+bool SwGammaGrf::QueryValue( uno::Any& rVal, sal_uInt8 ) const
+{
+ rVal <<= m_nValue;
+ return true;
+}
+
+bool SwGammaGrf::PutValue( const uno::Any& rVal, sal_uInt8 )
+{
+ return rVal >>= m_nValue;
+}
+
+// Sw___Grf::Clone(..) cont'd
+
+SwInvertGrf* SwInvertGrf::Clone( SfxItemPool * ) const
+{
+ return new SwInvertGrf( *this );
+}
+
+SwTransparencyGrf* SwTransparencyGrf::Clone( SfxItemPool * ) const
+{
+ return new SwTransparencyGrf( *this );
+}
+
+// SwTransparencyGrf
+
+bool SwTransparencyGrf::QueryValue( uno::Any& rVal,
+ sal_uInt8 ) const
+{
+ //OSL_ENSURE(dynamic_cast<const SfxByteItem*>( this ) != nullptr,"Put/QueryValue should be removed!");
+ SAL_WARN("sw", "Put/QueryValue should be removed!");
+ sal_Int16 nRet = GetValue();
+ OSL_ENSURE( 0 <= nRet && nRet <= 100, "value out of range" );
+ rVal <<= nRet;
+ return true;
+}
+
+bool SwTransparencyGrf::PutValue( const uno::Any& rVal,
+ sal_uInt8 )
+{
+ //temporary conversion until this is a SfxInt16Item!
+ //OSL_ENSURE(dynamic_cast<const SfxByteItem*>( this ) != nullptr,"Put/QueryValue should be removed!");
+ SAL_WARN("sw", "Put/QueryValue should be removed!");
+ sal_Int16 nVal = 0;
+ if(!(rVal >>= nVal) || nVal < -100 || nVal > 100)
+ return false;
+ if(nVal < 0)
+ {
+ // for compatibility with old documents
+ // introduce rounding as for SO 6.0 PP2
+ nVal = ( ( nVal * 128 ) - (99/2) ) / 100;
+ nVal += 128;
+ }
+ OSL_ENSURE( 0 <= nVal && nVal <= 100, "value out of range" );
+ SetValue(static_cast<sal_uInt8>(nVal));
+ return true;
+}
+
+// Sw___Grf::Clone(..) cont'd
+
+SwDrawModeGrf* SwDrawModeGrf::Clone( SfxItemPool * ) const
+{
+ return new SwDrawModeGrf( *this );
+}
+
+// SwDrawModeGrf
+
+sal_uInt16 SwDrawModeGrf::GetValueCount() const
+{
+ return sal_uInt16(GraphicDrawMode::Watermark) + 1;
+}
+
+bool SwDrawModeGrf::QueryValue( uno::Any& rVal,
+ sal_uInt8 ) const
+{
+ drawing::ColorMode eRet = static_cast<drawing::ColorMode>(GetEnumValue());
+ rVal <<= eRet;
+ return true;
+}
+
+bool SwDrawModeGrf::PutValue( const uno::Any& rVal,
+ sal_uInt8 )
+{
+ sal_Int32 eVal = SWUnoHelper::GetEnumAsInt32( rVal );
+ if(eVal >= 0 && eVal <= sal_uInt16(GraphicDrawMode::Watermark))
+ {
+ SetEnumValue(o3tl::narrowing<sal_uInt16>(eVal));
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/graphic/ndgrf.cxx b/sw/source/core/graphic/ndgrf.cxx
new file mode 100644
index 000000000..14a898cf7
--- /dev/null
+++ b/sw/source/core/graphic/ndgrf.cxx
@@ -0,0 +1,905 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <hintids.hxx>
+#include <tools/helpers.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/fract.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svl/fstathelper.hxx>
+#include <vcl/imap.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <editeng/boxitem.hxx>
+#include <sot/formats.hxx>
+#include <fmtfsize.hxx>
+#include <fmturl.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <frmatr.hxx>
+#include <grfatr.hxx>
+#include <swtypes.hxx>
+#include <ndgrf.hxx>
+#include <fmtcol.hxx>
+#include <hints.hxx>
+#include <swbaslnk.hxx>
+#include <pagefrm.hxx>
+
+#include <rtl/ustring.hxx>
+#include <o3tl/deleter.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+#include <retrieveinputstreamconsumer.hxx>
+#include <drawinglayer/processor2d/objectinfoextractor2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+
+using namespace com::sun::star;
+
+SwGrfNode::SwGrfNode(
+ const SwNodeIndex & rWhere,
+ const OUString& rGrfName,
+ const OUString& rFltName,
+ const Graphic* pGraphic,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Grf, pGrfColl, pAutoAttr ),
+ mbInBaseLinkSwapIn(true),
+ // #i73788#
+ mbLinkedInputStreamReady( false ),
+ mbIsStreamReadOnly( false )
+{
+ mbInSwapIn = mbChangeTwipSize =
+ mbFrameInPaint = mbScaleImageMap = false;
+
+ ReRead(rGrfName, rFltName, pGraphic, false);
+}
+
+SwGrfNode::SwGrfNode( const SwNodeIndex & rWhere,
+ const GraphicObject& rGrfObj,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Grf, pGrfColl, pAutoAttr ),
+ maGrfObj(rGrfObj),
+ mbInBaseLinkSwapIn(true),
+ // #i73788#
+ mbLinkedInputStreamReady( false ),
+ mbIsStreamReadOnly( false )
+{
+ mbInSwapIn = mbChangeTwipSize =
+ mbFrameInPaint = mbScaleImageMap = false;
+}
+
+/** Create new SW/G reader.
+ *
+ * Use this ctor if you want to read a linked graphic.
+ *
+ * @note Does not read/open the image itself!
+ */
+SwGrfNode::SwGrfNode( const SwNodeIndex & rWhere,
+ std::u16string_view rGrfName,
+ const OUString& rFltName,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Grf, pGrfColl, pAutoAttr ),
+ mbInBaseLinkSwapIn(true),
+ // #i73788#
+ mbLinkedInputStreamReady( false ),
+ mbIsStreamReadOnly( false )
+{
+ Graphic aGrf; aGrf.SetDefaultType();
+ maGrfObj.SetGraphic( aGrf );
+
+ mbInSwapIn = mbChangeTwipSize =
+ mbFrameInPaint = mbScaleImageMap = false;
+
+ InsertLink( rGrfName, rFltName );
+ if( IsLinkedFile() )
+ {
+ INetURLObject aUrl( rGrfName );
+ if( INetProtocol::File == aUrl.GetProtocol() &&
+ FStatHelper::IsDocument( aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ))
+ {
+ // file exists, so create connection without an update
+ static_cast<SwBaseLink*>( mxLink.get() )->Connect();
+ }
+ }
+}
+
+bool SwGrfNode::ReRead(
+ const OUString& rGrfName, const OUString& rFltName,
+ const Graphic* pGraphic,
+ bool bNewGrf )
+{
+ bool bReadGrf = false;
+ bool bSetTwipSize = true;
+ mpReplacementGraphic.reset();
+
+ OSL_ENSURE( pGraphic || !rGrfName.isEmpty(),
+ "GraphicNode without a name, Graphic or GraphicObject" );
+
+ OUString sURLLink;
+ if (pGraphic)
+ {
+ Graphic aGraphic(*pGraphic);
+
+ sURLLink = aGraphic.getOriginURL();
+ if (sURLLink.isEmpty() && !rGrfName.isEmpty())
+ {
+ sURLLink = rGrfName;
+ aGraphic.setOriginURL(sURLLink);
+ }
+ }
+ else
+ {
+ sURLLink = rGrfName;
+ }
+
+ // with name
+ if( mxLink.is() )
+ {
+ OSL_ENSURE( !mbInSwapIn, "ReRead: I am still in SwapIn" );
+
+ if( !sURLLink.isEmpty() )
+ {
+ // Note: if there is DDE in the FltName, then it is a DDE-linked graphic
+ OUString sCmd( sURLLink );
+ if( !rFltName.isEmpty() )
+ {
+ sfx2::SvBaseLinkObjectType nNewType;
+ if( rFltName == "DDE" )
+ nNewType = sfx2::SvBaseLinkObjectType::ClientDde;
+ else
+ {
+ sfx2::MakeLnkName( sCmd, nullptr, sURLLink, std::u16string_view(), &rFltName );
+ nNewType = sfx2::SvBaseLinkObjectType::ClientGraphic;
+ }
+
+ if( nNewType != mxLink->GetObjType() )
+ {
+ mxLink->Disconnect();
+ static_cast<SwBaseLink*>( mxLink.get() )->SetObjType( nNewType );
+ }
+ }
+
+ mxLink->SetLinkSourceName( sCmd );
+ }
+ else // no name anymore, so remove link
+ {
+ GetDoc().getIDocumentLinksAdministration().GetLinkManager().Remove( mxLink.get() );
+ mxLink.clear();
+ }
+
+ if( pGraphic )
+ {
+ maGrfObj.SetGraphic( *pGraphic );
+ onGraphicChanged();
+ bReadGrf = true;
+ }
+ else
+ {
+ // reset data of the old graphic so that the correct placeholder is
+ // shown in case the new link could not be loaded
+ Graphic aGrf; aGrf.SetDefaultType();
+ maGrfObj.SetGraphic( aGrf );
+
+ if( mxLink.is() )
+ {
+ if( getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) )
+ {
+ CallSwClientNotify(sw::GrfRereadAndInCacheHint());
+ }
+ else if ( bNewGrf )
+ {
+ //TODO refLink->setInputStream(getInputStream());
+ static_cast<SwBaseLink*>( mxLink.get() )->SwapIn();
+ }
+ }
+ onGraphicChanged();
+ bSetTwipSize = false;
+ }
+ }
+ else if( pGraphic && sURLLink.isEmpty() )
+ {
+ maGrfObj.SetGraphic( *pGraphic );
+ onGraphicChanged();
+ bReadGrf = true;
+ }
+ // Was the graphic already loaded?
+ else if( !bNewGrf && GraphicType::NONE != maGrfObj.GetType() )
+ return true;
+ else
+ {
+ // create new link for the graphic object
+ InsertLink( sURLLink, rFltName );
+
+ if( GetNodes().IsDocNodes() )
+ {
+ if( pGraphic )
+ {
+ maGrfObj.SetGraphic( *pGraphic );
+ onGraphicChanged();
+ bReadGrf = true;
+ // create connection without update, as we have the graphic
+ static_cast<SwBaseLink*>( mxLink.get() )->Connect();
+ }
+ else
+ {
+ Graphic aGrf;
+ aGrf.SetDefaultType();
+ maGrfObj.SetGraphic( aGrf );
+ onGraphicChanged();
+ if ( bNewGrf )
+ {
+ static_cast<SwBaseLink*>( mxLink.get() )->SwapIn();
+ }
+ }
+ }
+ }
+
+ // Bug 39281: Do not delete Size immediately - Events on ImageMaps should have
+ // something to work with when swapping
+ if( bSetTwipSize )
+ SetTwipSize( ::GetGraphicSizeTwip( maGrfObj.GetGraphic(), nullptr ) );
+
+ // create an updates for the frames
+ if( bReadGrf && bNewGrf )
+ {
+ const SwUpdateAttr aHint(0,0,0);
+ CallSwClientNotify(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+
+ return bReadGrf;
+}
+
+SwGrfNode::~SwGrfNode()
+{
+ mpReplacementGraphic.reset();
+
+ // #i73788#
+ mpThreadConsumer.reset();
+
+ SwDoc& rDoc = GetDoc();
+ if( mxLink.is() )
+ {
+ OSL_ENSURE( !mbInSwapIn, "DTOR: I am still in SwapIn" );
+ rDoc.getIDocumentLinksAdministration().GetLinkManager().Remove( mxLink.get() );
+ mxLink->Disconnect();
+ }
+ else
+ {
+ // #i40014# - A graphic node, which is in a linked
+ // section, whose link is another section in the document, doesn't
+ // have to remove the stream from the storage.
+ // Because it's hard to detect this case here and it would only fix
+ // one problem with shared graphic files - there are also problems,
+ // a certain graphic file is referenced by two independent graphic nodes,
+ // brush item or drawing objects, the stream isn't no longer removed here.
+ // To do this stuff correctly, a reference counting on shared streams
+ // inside one document has to be implemented.
+ }
+ //#39289# delete frames already here since the Frames' dtor needs the graphic for its StopAnimation
+ if( HasWriterListeners() )
+ DelFrames(nullptr);
+ ResetAttr(RES_PAGEDESC);
+}
+
+/// allow reaction on change of content of GraphicObject
+void SwGrfNode::onGraphicChanged()
+{
+ // try to access SwFlyFrameFormat; since title/desc/name are set there, there is no
+ // use to continue if it is not yet set. If not yet set, call onGraphicChanged()
+ // when it is set.
+ SwFlyFrameFormat* pFlyFormat = dynamic_cast< SwFlyFrameFormat* >(GetFlyFormat());
+
+ if(!pFlyFormat)
+ return;
+
+ OUString aName;
+ OUString aTitle;
+ OUString aDesc;
+ auto const & rVectorGraphicDataPtr = GetGrf().getVectorGraphicData();
+
+ if (rVectorGraphicDataPtr)
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence(rVectorGraphicDataPtr->getPrimitive2DSequence());
+
+ if(!aSequence.empty())
+ {
+ drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ drawinglayer::processor2d::ObjectInfoPrimitiveExtractor2D aProcessor(aViewInformation2D);
+
+ aProcessor.process(aSequence);
+
+ const drawinglayer::primitive2d::ObjectInfoPrimitive2D* pResult = aProcessor.getResult();
+
+ if(pResult)
+ {
+ aName = pResult->getName();
+ aTitle = pResult->getTitle();
+ aDesc = pResult->getDesc();
+ }
+ }
+ }
+
+ if(!aTitle.isEmpty())
+ {
+ SetTitle(aTitle);
+ }
+ else if (!aName.isEmpty())
+ {
+ SetTitle(aName);
+ }
+
+ if(!aDesc.isEmpty())
+ {
+ SetDescription(aDesc);
+ }
+}
+
+void SwGrfNode::SetGraphic(const Graphic& rGraphic)
+{
+ maGrfObj.SetGraphic(rGraphic);
+ onGraphicChanged();
+}
+
+void SwGrfNode::TriggerGraphicArrived()
+{
+ CallSwClientNotify(sw::PreGraphicArrivedHint());
+ CallSwClientNotify(sw::PostGraphicArrivedHint());
+}
+
+const Graphic& SwGrfNode::GetGrf(bool bWait) const
+{
+ const_cast<SwGrfNode*>(this)->SwapIn(bWait);
+ return maGrfObj.GetGraphic();
+}
+
+const GraphicObject& SwGrfNode::GetGrfObj(bool bWait) const
+{
+ const_cast<SwGrfNode*>(this)->SwapIn(bWait);
+ return maGrfObj;
+}
+
+const GraphicObject* SwGrfNode::GetReplacementGrfObj() const
+{
+ if(!mpReplacementGraphic)
+ {
+ auto const & rVectorGraphicDataPtr = GetGrfObj().GetGraphic().getVectorGraphicData();
+
+ if (rVectorGraphicDataPtr)
+ {
+ const_cast< SwGrfNode* >(this)->mpReplacementGraphic.reset( new GraphicObject(rVectorGraphicDataPtr->getReplacement()) );
+ }
+ else if (GetGrfObj().GetGraphic().GetType() == GraphicType::GdiMetafile)
+ {
+ // Replacement graphic for PDF and metafiles is just the bitmap.
+ const_cast<SwGrfNode*>(this)->mpReplacementGraphic.reset( new GraphicObject(GetGrfObj().GetGraphic().GetBitmapEx()) );
+ }
+ }
+
+ return mpReplacementGraphic.get();
+}
+
+SwGrfNode * SwNodes::MakeGrfNode( const SwNodeIndex & rWhere,
+ const OUString& rGrfName,
+ const OUString& rFltName,
+ const Graphic* pGraphic,
+ SwGrfFormatColl* pGrfColl,
+ SwAttrSet const * pAutoAttr )
+{
+ OSL_ENSURE( pGrfColl, "MakeGrfNode: Formatpointer is 0." );
+ SwGrfNode *pNode;
+ // create object delayed, only from a SW/G-reader
+ if( !pGraphic )
+ pNode = new SwGrfNode( rWhere, rGrfName,
+ rFltName, pGrfColl, pAutoAttr );
+ else
+ pNode = new SwGrfNode( rWhere, rGrfName,
+ rFltName, pGraphic, pGrfColl, pAutoAttr );
+ return pNode;
+}
+
+SwGrfNode * SwNodes::MakeGrfNode( const SwNodeIndex & rWhere,
+ const GraphicObject& rGrfObj,
+ SwGrfFormatColl* pGrfColl )
+{
+ OSL_ENSURE( pGrfColl, "MakeGrfNode: Formatpointer is 0." );
+ return new SwGrfNode( rWhere, rGrfObj, pGrfColl, nullptr );
+}
+
+Size SwGrfNode::GetTwipSize() const
+{
+ if( !mnGrfSize.Width() && !mnGrfSize.Height() )
+ {
+ const_cast<SwGrfNode*>(this)->SwapIn();
+ }
+ return mnGrfSize;
+}
+
+/**
+ * @return true if ReRead or reading successful,
+ * false if not loaded
+ */
+bool SwGrfNode::SwapIn(bool bWaitForData)
+{
+ if(mbInSwapIn) // not recursively!
+ return true;
+
+ bool bRet = false;
+ mbInSwapIn = true;
+ SwBaseLink* pLink = static_cast<SwBaseLink*>( mxLink.get() );
+
+ if( pLink )
+ {
+ if( (GraphicType::NONE == maGrfObj.GetType() ||
+ GraphicType::Default == maGrfObj.GetType()) &&
+ mbInBaseLinkSwapIn)
+ {
+ // link was not loaded yet
+ if( pLink->SwapIn( bWaitForData ) )
+ {
+ bRet = true;
+ mbInBaseLinkSwapIn = false;
+ }
+ else if( GraphicType::Default == maGrfObj.GetType() )
+ {
+ // no default bitmap anymore, thus re-paint
+ mpReplacementGraphic.reset();
+
+ maGrfObj.SetGraphic( Graphic() );
+ onGraphicChanged();
+ SwMsgPoolItem aMsgHint( RES_GRAPHIC_PIECE_ARRIVED );
+ CallSwClientNotify(sw::LegacyModifyHint(&aMsgHint, &aMsgHint));
+ }
+ }
+ else
+ {
+ bRet = true;
+ }
+ }
+ else
+ bRet = true;
+
+ if (bRet)
+ {
+ if( !mnGrfSize.Width() && !mnGrfSize.Height() )
+ SetTwipSize( ::GetGraphicSizeTwip( maGrfObj.GetGraphic(), nullptr ) );
+ }
+ mbInSwapIn = false;
+ return bRet;
+}
+
+bool SwGrfNode::GetFileFilterNms( OUString* pFileNm, OUString* pFilterNm ) const
+{
+ bool bRet = false;
+ if( mxLink.is() && mxLink->GetLinkManager() )
+ {
+ sfx2::SvBaseLinkObjectType nType = mxLink->GetObjType();
+ if( sfx2::SvBaseLinkObjectType::ClientGraphic == nType )
+ bRet = sfx2::LinkManager::GetDisplayNames(
+ mxLink.get(), nullptr, pFileNm, nullptr, pFilterNm );
+ else if( sfx2::SvBaseLinkObjectType::ClientDde == nType && pFileNm && pFilterNm )
+ {
+ OUString sApp;
+ OUString sTopic;
+ OUString sItem;
+ if( sfx2::LinkManager::GetDisplayNames(
+ mxLink.get(), &sApp, &sTopic, &sItem ) )
+ {
+ *pFileNm = sApp + OUStringChar(sfx2::cTokenSeparator)
+ + sTopic + OUStringChar(sfx2::cTokenSeparator)
+ + sItem;
+ *pFilterNm = "DDE";
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+/** Make a graphic object ready for UNDO.
+ *
+ * If it is already in storage, it needs to be loaded.
+ */
+bool SwGrfNode::SavePersistentData()
+{
+ if( mxLink.is() )
+ {
+ OSL_ENSURE( !mbInSwapIn, "SavePersistentData: I am still in SwapIn" );
+ GetDoc().getIDocumentLinksAdministration().GetLinkManager().Remove( mxLink.get() );
+ return true;
+ }
+
+ // swap in first if in storage
+ if( HasEmbeddedStreamName() && !SwapIn() )
+ return false;
+
+ // #i44367#
+ // Do not delete graphic file in storage, because the graphic file could
+ // be referenced by other graphic nodes.
+ // Because it's hard to detect this case here and it would only fix
+ // one problem with shared graphic files - there are also problems, if
+ // a certain graphic file is referenced by two independent graphic nodes,
+ // brush item or drawing objects, the stream isn't no longer removed here.
+ // To do this stuff correct, a reference counting on shared streams
+ // inside one document has to be implemented.
+ // Important note: see also fix for #i40014#
+
+ // swap out into temp file
+ return true;
+}
+
+bool SwGrfNode::RestorePersistentData()
+{
+ if( mxLink.is() )
+ {
+ IDocumentLinksAdministration& rIDLA = getIDocumentLinksAdministration();
+ mxLink->SetVisible( rIDLA.IsVisibleLinks() );
+ rIDLA.GetLinkManager().InsertDDELink( mxLink.get() );
+ if( getIDocumentLayoutAccess().GetCurrentLayout() )
+ mxLink->Update();
+ }
+ return true;
+}
+
+void SwGrfNode::InsertLink( std::u16string_view rGrfName, const OUString& rFltName )
+{
+ mxLink = new SwBaseLink( SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::GDIMETAFILE, this );
+
+ IDocumentLinksAdministration& rIDLA = getIDocumentLinksAdministration();
+ if( !GetNodes().IsDocNodes() )
+ return;
+
+ mxLink->SetVisible( rIDLA.IsVisibleLinks() );
+ if( rFltName == "DDE" )
+ {
+ sal_Int32 nTmp = 0;
+ const OUString sApp{ o3tl::getToken(rGrfName, 0, sfx2::cTokenSeparator, nTmp ) };
+ const std::u16string_view sTopic{ o3tl::getToken(rGrfName, 0, sfx2::cTokenSeparator, nTmp ) };
+ const std::u16string_view sItem{ rGrfName.substr( nTmp ) };
+ rIDLA.GetLinkManager().InsertDDELink( mxLink.get(), sApp, sTopic, sItem );
+ }
+ else
+ {
+ const bool bSync = rFltName == "SYNCHRON";
+ mxLink->SetSynchron( bSync );
+ mxLink->SetContentType( SotClipboardFormatId::SVXB );
+
+ rIDLA.GetLinkManager().InsertFileLink( *mxLink,
+ sfx2::SvBaseLinkObjectType::ClientGraphic, rGrfName,
+ (!bSync && !rFltName.isEmpty() ? &rFltName : nullptr) );
+ }
+}
+
+void SwGrfNode::ReleaseLink()
+{
+ if( !mxLink.is() )
+ return;
+
+ Graphic aLocalGraphic(maGrfObj.GetGraphic());
+ const bool bHasOriginalData(aLocalGraphic.IsGfxLink());
+
+ {
+ mbInSwapIn = true;
+ SwBaseLink* pLink = static_cast<SwBaseLink*>( mxLink.get() );
+ pLink->SwapIn( true, true );
+ mbInSwapIn = false;
+ }
+
+ getIDocumentLinksAdministration().GetLinkManager().Remove( mxLink.get() );
+ mxLink.clear();
+ aLocalGraphic.setOriginURL("");
+
+ // #i15508# added extra processing after getting rid of the link. Use whatever is
+ // known from the formerly linked graphic to get to a state as close to a directly
+ // unlinked inserted graphic as possible. Goal is to have a valid GfxLink at the
+ // ImplGraphic (see there) that holds temporary data to the original data and type
+ // information about the original data. Only when this is given will
+ // SvXMLGraphicHelper::ImplInsertGraphicURL which is used at export use that type
+ // and use the original graphic at export for the ODF, without evtl. recoding
+ // of the bitmap graphic data to something without loss (e.g. PNG) but bigger
+ if(bHasOriginalData)
+ {
+ // #i15508# if we have the original data at the Graphic, let it survive
+ // by using that Graphic again, this time at a GraphicObject without link.
+ // This happens e.g. when inserting a linked graphic and breaking the link
+ maGrfObj.SetGraphic(aLocalGraphic);
+ }
+}
+
+void SwGrfNode::SetTwipSize( const Size& rSz )
+{
+ mnGrfSize = rSz;
+ if( IsScaleImageMap() && mnGrfSize.Width() && mnGrfSize.Height() )
+ {
+ // resize Image-Map to size of the graphic
+ ScaleImageMap();
+
+ // do not re-scale Image-Map
+ SetScaleImageMap( false );
+ }
+}
+
+void SwGrfNode::ScaleImageMap()
+{
+ if( !mnGrfSize.Width() || !mnGrfSize.Height() )
+ return;
+
+ // re-scale Image-Map
+ SwFrameFormat* pFormat = GetFlyFormat();
+
+ if( !pFormat )
+ return;
+
+ SwFormatURL aURL( pFormat->GetURL() );
+ if ( !aURL.GetMap() )
+ return;
+
+ bool bScale = false;
+ Fraction aScaleX( 1, 1 );
+ Fraction aScaleY( 1, 1 );
+
+ const SwFormatFrameSize& rFrameSize = pFormat->GetFrameSize();
+ const SvxBoxItem& rBox = pFormat->GetBox();
+
+ if( !rFrameSize.GetWidthPercent() )
+ {
+ SwTwips nWidth = rFrameSize.GetWidth();
+
+ nWidth -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) +
+ rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
+
+ OSL_ENSURE( nWidth>0, "Do any 0 twip wide graphics exist!?" );
+
+ if( mnGrfSize.Width() != nWidth )
+ {
+ aScaleX = Fraction( mnGrfSize.Width(), nWidth );
+ bScale = true;
+ }
+ }
+ if( !rFrameSize.GetHeightPercent() )
+ {
+ SwTwips nHeight = rFrameSize.GetHeight();
+
+ nHeight -= rBox.CalcLineSpace(SvxBoxItemLine::TOP) +
+ rBox.CalcLineSpace(SvxBoxItemLine::BOTTOM);
+
+ OSL_ENSURE( nHeight>0, "Do any 0 twip high graphics exist!?" );
+
+ if( mnGrfSize.Height() != nHeight )
+ {
+ aScaleY = Fraction( mnGrfSize.Height(), nHeight );
+ bScale = true;
+ }
+ }
+
+ if( bScale )
+ {
+ aURL.GetMap()->Scale( aScaleX, aScaleY );
+ pFormat->SetFormatAttr( aURL );
+ }
+}
+
+SwContentNode* SwGrfNode::MakeCopy(SwDoc& rDoc, const SwNodeIndex& rIdx, bool) const
+{
+ // copy formats into the other document
+ SwGrfFormatColl* pColl = rDoc.CopyGrfColl( *GetGrfColl() );
+
+ Graphic aTmpGrf = GetGrf();
+
+ OUString sFile, sFilter;
+ if( IsLinkedFile() )
+ sfx2::LinkManager::GetDisplayNames( mxLink.get(), nullptr, &sFile, nullptr, &sFilter );
+ else if( IsLinkedDDE() )
+ {
+ OUString sTmp1, sTmp2;
+ sfx2::LinkManager::GetDisplayNames( mxLink.get(), &sTmp1, &sTmp2, &sFilter );
+ sfx2::MakeLnkName( sFile, &sTmp1, sTmp2, sFilter );
+ sFilter = "DDE";
+ }
+
+ SwGrfNode* pGrfNd = SwNodes::MakeGrfNode( rIdx, sFile, sFilter,
+ &aTmpGrf, pColl,
+ GetpSwAttrSet() );
+ pGrfNd->SetTitle( GetTitle() );
+ pGrfNd->SetDescription( GetDescription() );
+ pGrfNd->SetContour( HasContour(), HasAutomaticContour() );
+ return pGrfNd;
+}
+
+/// returns the Graphic-Attr-Structure filled with our graphic attributes
+GraphicAttr& SwGrfNode::GetGraphicAttr( GraphicAttr& rGA,
+ const SwFrame* pFrame ) const
+{
+ const SwAttrSet& rSet = GetSwAttrSet();
+
+ rGA.SetDrawMode( rSet.GetDrawModeGrf().GetValue() );
+
+ const SwMirrorGrf & rMirror = rSet.GetMirrorGrf();
+ BmpMirrorFlags nMirror = BmpMirrorFlags::NONE;
+ if( rMirror.IsGrfToggle() && pFrame && !pFrame->FindPageFrame()->OnRightPage() )
+ {
+ switch( rMirror.GetValue() )
+ {
+ case MirrorGraph::Dont:
+ nMirror = BmpMirrorFlags::Horizontal;
+ break;
+ case MirrorGraph::Vertical:
+ nMirror = BmpMirrorFlags::NONE;
+ break;
+ case MirrorGraph::Horizontal:
+ nMirror = BmpMirrorFlags::Horizontal|BmpMirrorFlags::Vertical;
+ break;
+ default:
+ nMirror = BmpMirrorFlags::Vertical;
+ break;
+ }
+ }
+ else
+ switch( rMirror.GetValue() )
+ {
+ case MirrorGraph::Both:
+ nMirror = BmpMirrorFlags::Horizontal|BmpMirrorFlags::Vertical;
+ break;
+ case MirrorGraph::Vertical:
+ nMirror = BmpMirrorFlags::Horizontal;
+ break;
+ case MirrorGraph::Horizontal:
+ nMirror = BmpMirrorFlags::Vertical;
+ break;
+ default: break;
+ }
+
+ rGA.SetMirrorFlags( nMirror );
+
+ const SwCropGrf& rCrop = rSet.GetCropGrf();
+
+ tools::Long nCropLeft = rCrop.GetLeft();
+ tools::Long nCropTop = rCrop.GetTop();
+ tools::Long nCropRight = rCrop.GetRight();
+ tools::Long nCropBottom = rCrop.GetBottom();
+
+ // take mirroring of crop values into consideration
+ // while cropping a flipped image. otherwise,
+ // cropping will crop the opposite side of the image.
+ if (rGA.GetMirrorFlags() & BmpMirrorFlags::Vertical)
+ {
+ nCropTop = rCrop.GetBottom();
+ nCropBottom = rCrop.GetTop();
+ }
+
+ if (rGA.GetMirrorFlags() & BmpMirrorFlags::Horizontal)
+ {
+ nCropLeft = rCrop.GetRight();
+ nCropRight = rCrop.GetLeft();
+ }
+
+ rGA.SetCrop( convertTwipToMm100( nCropLeft ),
+ convertTwipToMm100( nCropTop ),
+ convertTwipToMm100( nCropRight ),
+ convertTwipToMm100( nCropBottom ));
+
+ const SwRotationGrf& rRotation = rSet.GetRotationGrf();
+ rGA.SetRotation( rRotation.GetValue() );
+
+ rGA.SetLuminance( rSet.GetLuminanceGrf().GetValue() );
+ rGA.SetContrast( rSet.GetContrastGrf().GetValue() );
+ rGA.SetChannelR( rSet.GetChannelRGrf().GetValue() );
+ rGA.SetChannelG( rSet.GetChannelGGrf().GetValue() );
+ rGA.SetChannelB( rSet.GetChannelBGrf().GetValue() );
+ rGA.SetGamma( rSet.GetGammaGrf().GetValue() );
+ rGA.SetInvert( rSet.GetInvertGrf().GetValue() );
+
+ const sal_uInt16 nTrans = rSet.GetTransparencyGrf().GetValue();
+ rGA.SetAlpha( 255 - static_cast<sal_uInt8>(FRound(
+ std::min( nTrans, sal_uInt16(100) ) * 2.55 )) );
+
+ return rGA;
+}
+
+bool SwGrfNode::IsTransparent() const
+{
+ return maGrfObj.IsTransparent() ||
+ GetSwAttrSet().GetTransparencyGrf().GetValue() != 0;
+}
+
+void SwGrfNode::TriggerAsyncRetrieveInputStream()
+{
+ if ( !IsLinkedFile() )
+ {
+ OSL_FAIL( "<SwGrfNode::TriggerAsyncLoad()> - Method is misused. Method call is only valid for graphic nodes, which refer a linked graphic file" );
+ return;
+ }
+
+ if (mpThreadConsumer != nullptr)
+ return;
+
+ mpThreadConsumer.reset(new SwAsyncRetrieveInputStreamThreadConsumer(*this), o3tl::default_delete<SwAsyncRetrieveInputStreamThreadConsumer>());
+
+ OUString sGrfNm;
+ sfx2::LinkManager::GetDisplayNames( mxLink.get(), nullptr, &sGrfNm );
+ OUString sReferer;
+ SfxObjectShell * sh = GetDoc().GetPersist();
+ if (sh != nullptr && sh->HasName())
+ {
+ sReferer = sh->GetMedium()->GetName();
+ }
+ mpThreadConsumer->CreateThread( sGrfNm, sReferer );
+}
+
+
+void SwGrfNode::ApplyInputStream(
+ const css::uno::Reference<css::io::XInputStream>& xInputStream,
+ const bool bIsStreamReadOnly )
+{
+ if ( IsLinkedFile() )
+ {
+ if ( xInputStream.is() )
+ {
+ mxInputStream = xInputStream;
+ mbIsStreamReadOnly = bIsStreamReadOnly;
+ mbLinkedInputStreamReady = true;
+ SwMsgPoolItem aMsgHint( RES_LINKED_GRAPHIC_STREAM_ARRIVED );
+ CallSwClientNotify(sw::LegacyModifyHint(&aMsgHint, &aMsgHint));
+ }
+ }
+}
+
+void SwGrfNode::UpdateLinkWithInputStream()
+{
+ // do not work on link, if a <SwapIn> has been triggered.
+ if ( mbInSwapIn || !IsLinkedFile() )
+ return;
+
+ GetLink()->setStreamToLoadFrom( mxInputStream, mbIsStreamReadOnly );
+ GetLink()->Update();
+ TriggerGraphicArrived();
+
+ // #i88291#
+ mxInputStream.clear();
+ GetLink()->clearStreamToLoadFrom();
+ mbLinkedInputStreamReady = false;
+ mpThreadConsumer.reset();
+}
+
+// #i90395#
+bool SwGrfNode::IsAsyncRetrieveInputStreamPossible() const
+{
+ bool bRet = false;
+
+ if ( IsLinkedFile() )
+ {
+ OUString sGrfNm;
+ sfx2::LinkManager::GetDisplayNames( mxLink.get(), nullptr, &sGrfNm );
+ if ( !sGrfNm.startsWith( "vnd.sun.star.pkg:" ) )
+ {
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/AccessibilityCheck.hxx b/sw/source/core/inc/AccessibilityCheck.hxx
new file mode 100644
index 000000000..4bcc56000
--- /dev/null
+++ b/sw/source/core/inc/AccessibilityCheck.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESSIBILITYCHECK_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESSIBILITYCHECK_HXX
+
+#include <sfx2/AccessibilityCheck.hxx>
+#include <doc.hxx>
+
+namespace sw
+{
+class SW_DLLPUBLIC AccessibilityCheck final : public sfx::AccessibilityCheck
+{
+private:
+ SwDoc* m_pDoc;
+
+public:
+ AccessibilityCheck(SwDoc* pDoc)
+ : m_pDoc(pDoc)
+ {
+ }
+
+ void check() override;
+ void checkObject(SdrObject* pObject);
+};
+
+} // end sw namespace
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/AccessibilityIssue.hxx b/sw/source/core/inc/AccessibilityIssue.hxx
new file mode 100644
index 000000000..882330305
--- /dev/null
+++ b/sw/source/core/inc/AccessibilityIssue.hxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_ACCESSIBILITYISSUE_HXX
+#define INCLUDED_SW_SOURCE_CORE_ACCESSIBILITYISSUE_HXX
+
+#include <sfx2/AccessibilityIssue.hxx>
+#include <doc.hxx>
+
+namespace sw
+{
+enum class IssueObject
+{
+ UNKNOWN,
+ GRAPHIC,
+ OLE,
+ TABLE,
+ TEXT,
+};
+
+class AccessibilityIssue final : public sfx::AccessibilityIssue
+{
+private:
+ IssueObject m_eIssueObject;
+ SwDoc* m_pDoc;
+ OUString m_sObjectID;
+ std::vector<OUString> m_aIssueAdditionalInfo;
+ SwNode* m_pNode;
+
+ sal_Int32 m_nStart;
+ sal_Int32 m_nEnd;
+
+public:
+ AccessibilityIssue(sfx::AccessibilityIssueID eIssueID = sfx::AccessibilityIssueID::UNSPECIFIED);
+
+ void setIssueObject(IssueObject eIssueObject);
+ void setDoc(SwDoc& rDoc);
+ void setObjectID(OUString const& rID);
+ void setNode(SwNode* pNode) { m_pNode = pNode; }
+
+ void setStart(sal_Int32 nStart) { m_nStart = nStart; }
+
+ void setEnd(sal_Int32 nEnd) { m_nEnd = nEnd; }
+
+ std::vector<OUString> const& getAdditionalInfo() const { return m_aIssueAdditionalInfo; }
+
+ void setAdditionalInfo(std::vector<OUString>&& rIssueAdditionalInfo)
+ {
+ m_aIssueAdditionalInfo = std::move(rIssueAdditionalInfo);
+ }
+
+ bool canGotoIssue() const override;
+ void gotoIssue() const override;
+};
+
+} // end sw namespace
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DateFormFieldButton.hxx b/sw/source/core/inc/DateFormFieldButton.hxx
new file mode 100644
index 000000000..3d74706a8
--- /dev/null
+++ b/sw/source/core/inc/DateFormFieldButton.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_TEXT_DATEFORMEFIELDBUTTO_HXX
+#define INCLUDED_SW_SOURCE_CORE_TEXT_DATEFORMEFIELDBUTTO_HXX
+
+#include "FormFieldButton.hxx"
+
+class SwEditWin;
+class SvNumberFormatter;
+namespace sw::mark
+{
+class DateFieldmark;
+}
+
+/**
+ * This button is shown when the cursor is on a date form field.
+ * The user can select a date from a date picker while filling in a form.
+ */
+class DateFormFieldButton final : public FormFieldButton
+{
+private:
+ SvNumberFormatter* m_pNumberFormatter;
+ sw::mark::DateFieldmark* m_pDateFieldmark;
+
+ std::unique_ptr<weld::Calendar> m_xCalendar;
+
+ DECL_LINK(ImplSelectHdl, weld::Calendar&, void);
+
+public:
+ DateFormFieldButton(SwEditWin* pEditWin, sw::mark::DateFieldmark& rFieldMark,
+ SvNumberFormatter* pNumberFormatter);
+ virtual ~DateFormFieldButton() override;
+
+ virtual void LaunchPopup() override;
+ virtual void DestroyPopup() override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/inc/DocumentChartDataProviderManager.hxx b/sw/source/core/inc/DocumentChartDataProviderManager.hxx
new file mode 100644
index 000000000..f887702bb
--- /dev/null
+++ b/sw/source/core/inc/DocumentChartDataProviderManager.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTCHARTDATAPROVIDEMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTCHARTDATAPROVIDEMANAGER_HXX
+
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <rtl/ref.hxx>
+#include <memory>
+
+class SwTable;
+class SwChartDataProvider;
+class SwChartLockController_Helper;
+class SwDoc;
+
+
+namespace sw {
+
+class DocumentChartDataProviderManager final : public IDocumentChartDataProviderAccess
+{
+
+public:
+
+ DocumentChartDataProviderManager( SwDoc& i_rSwdoc );
+
+ SwChartDataProvider * GetChartDataProvider( bool bCreate = false ) const override;
+
+ void CreateChartInternalDataProviders( const SwTable *pTable ) override;
+
+ SwChartLockController_Helper & GetChartControllerHelper() override;
+
+ virtual ~DocumentChartDataProviderManager() override;
+
+private:
+
+ DocumentChartDataProviderManager(DocumentChartDataProviderManager const&) = delete;
+ DocumentChartDataProviderManager& operator=(DocumentChartDataProviderManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ mutable rtl::Reference<SwChartDataProvider> maChartDataProviderImplRef;
+ std::unique_ptr<SwChartLockController_Helper> mpChartControllerHelper;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentContentOperationsManager.hxx b/sw/source/core/inc/DocumentContentOperationsManager.hxx
new file mode 100644
index 000000000..ce6dc7788
--- /dev/null
+++ b/sw/source/core/inc/DocumentContentOperationsManager.hxx
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <IDocumentContentOperations.hxx>
+#include <ndarr.hxx> //Only for lcl_RstTxtAttr
+
+class SwDoc;
+class SwNoTextNode;
+class SwFormatColl;
+class SwHistory;
+
+namespace sw
+{
+
+class DocumentContentOperationsManager final : public IDocumentContentOperations
+{
+public:
+ DocumentContentOperationsManager( SwDoc& i_rSwdoc );
+
+ //Interface methods:
+ bool CopyRange(SwPaM&, SwPosition&, SwCopyFlags) const override;
+
+ void DeleteSection(SwNode* pNode) override;
+
+ void DeleteRange(SwPaM&) override;
+
+ bool DelFullPara(SwPaM&) override;
+
+ bool DeleteAndJoin(SwPaM&, SwDeleteFlags flags = SwDeleteFlags::Default) override;
+
+ bool MoveRange(SwPaM&, SwPosition&, SwMoveFlags) override;
+
+ bool MoveNodeRange(SwNodeRange&, SwNodeIndex&, SwMoveFlags) override;
+
+ void MoveAndJoin(SwPaM&, SwPosition&) override;
+
+ bool Overwrite(const SwPaM &rRg, const OUString& rStr) override;
+
+ bool InsertString(const SwPaM &rRg, const OUString&,
+ const SwInsertFlags nInsertMode = SwInsertFlags::EMPTYEXPAND ) override;
+
+ void SetIME(bool bIME) override;
+
+ bool GetIME() const override;
+
+ void TransliterateText(const SwPaM& rPaM, utl::TransliterationWrapper&) override;
+
+ SwFlyFrameFormat* InsertGraphic(const SwPaM &rRg, const OUString& rGrfName, const OUString& rFltName, const Graphic* pGraphic,
+ const SfxItemSet* pFlyAttrSet, const SfxItemSet* pGrfAttrSet, SwFrameFormat*) override;
+
+ SwFlyFrameFormat* InsertGraphicObject(const SwPaM& rRg, const GraphicObject& rGrfObj, const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet) override;
+
+ void ReRead(SwPaM&, const OUString& rGrfName, const OUString& rFltName, const Graphic* pGraphic) override;
+
+ SwDrawFrameFormat* InsertDrawObj( const SwPaM &rRg, SdrObject& rDrawObj, const SfxItemSet& rFlyAttrSet ) override;
+
+ SwFlyFrameFormat* InsertEmbObject(const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj, SfxItemSet* pFlyAttrSet) override;
+
+ SwFlyFrameFormat* InsertOLE(const SwPaM &rRg, const OUString& rObjName, sal_Int64 nAspect, const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet) override;
+
+ bool SplitNode(const SwPosition &rPos, bool bChkTableStart) override;
+
+ bool AppendTextNode(SwPosition& rPos) override;
+
+ bool ReplaceRange(SwPaM& rPam, const OUString& rNewStr,
+ const bool bRegExReplace) override;
+
+ // Add a para for the char attribute exp...
+ bool InsertPoolItem(const SwPaM &rRg, const SfxPoolItem&,
+ const SetAttrMode nFlags = SetAttrMode::DEFAULT,
+ SwRootFrame const* pLayout = nullptr,
+ SwTextAttr **ppNewTextAttr = nullptr) override;
+
+ void InsertItemSet (const SwPaM &rRg, const SfxItemSet&,
+ const SetAttrMode nFlags = SetAttrMode::DEFAULT,
+ SwRootFrame const* pLayout = nullptr) override;
+
+ void RemoveLeadingWhiteSpace(const SwPosition & rPos ) override;
+
+
+ //Non-Interface methods
+
+ void DeleteDummyChar(SwPosition const& rPos, sal_Unicode cDummy);
+
+ void CopyWithFlyInFly( const SwNodeRange& rRg,
+ const SwNodeIndex& rInsPos,
+ const std::pair<const SwPaM&, const SwPosition&> * pCopiedPaM = nullptr,
+ bool bMakeNewFrames = true,
+ bool bDelRedlines = true,
+ bool bCopyFlyAtFly = false,
+ SwCopyFlags flags = SwCopyFlags::Default) const;
+ void CopyFlyInFlyImpl( const SwNodeRange& rRg,
+ SwPaM const*const pCopiedPaM,
+ const SwNodeIndex& rStartIdx,
+ const bool bCopyFlyAtFly = false,
+ SwCopyFlags flags = SwCopyFlags::Default) const;
+
+ /// Parameters for _Rst and lcl_SetTextFormatColl
+ //originallyfrom docfmt.cxx
+ struct ParaRstFormat
+ {
+ SwFormatColl* pFormatColl;
+ SwHistory* pHistory;
+ const SwPosition *pSttNd, *pEndNd;
+ const SfxItemSet* pDelSet;
+ SwRootFrame const*const pLayout;
+ sal_uInt16 nWhich;
+ bool bReset;
+ bool bResetListAttrs; // #i62575#
+ bool bResetAll;
+ bool bInclRefToxMark;
+ /// From the attributes included in the range, delete only the ones which have exactly same range. Don't delete the ones which are simply included in the range.
+ bool bExactRange;
+
+ ParaRstFormat(const SwPosition* pStt, const SwPosition* pEnd,
+ SwHistory* pHst, const SfxItemSet* pSet = nullptr,
+ SwRootFrame const*const pLay = nullptr)
+ : pFormatColl(nullptr)
+ , pHistory(pHst)
+ , pSttNd(pStt)
+ , pEndNd(pEnd)
+ , pDelSet(pSet)
+ , pLayout(pLay)
+ , nWhich(0)
+ , bReset(false) // #i62675#
+ , bResetListAttrs(false)
+ , bResetAll(true)
+ , bInclRefToxMark(false)
+ , bExactRange(false)
+ {
+ }
+ };
+ static bool lcl_RstTextAttr( SwNode* pNd, void* pArgs ); //originally from docfmt.cxx
+
+
+ virtual ~DocumentContentOperationsManager() override;
+
+private:
+ SwDoc& m_rDoc;
+
+ bool m_bIME = false;
+
+ bool DeleteAndJoinImpl(SwPaM &, SwDeleteFlags);
+ bool DeleteAndJoinWithRedlineImpl(SwPaM &, SwDeleteFlags);
+ bool DeleteRangeImpl(SwPaM &, SwDeleteFlags);
+ bool DeleteRangeImplImpl(SwPaM &, SwDeleteFlags);
+ bool ReplaceRangeImpl(SwPaM&, OUString const&, const bool);
+ SwFlyFrameFormat* InsNoTextNode( const SwPosition&rPos, SwNoTextNode*,
+ const SfxItemSet* pFlyAttrSet,
+ const SfxItemSet* pGrfAttrSet,
+ SwFrameFormat* );
+ /* Copy a range within the same or to another document.
+ Position may not lie within range! */
+ bool CopyImpl( SwPaM&, SwPosition&,
+ SwCopyFlags flags, SwPaM *const pCpyRng /*= 0*/) const;
+ bool CopyImplImpl(SwPaM&, SwPosition&,
+ SwCopyFlags flags, SwPaM *const pCpyRng /*= 0*/) const;
+
+ DocumentContentOperationsManager(DocumentContentOperationsManager const&) = delete;
+ DocumentContentOperationsManager& operator=(DocumentContentOperationsManager const&) = delete;
+};
+
+
+void CopyBookmarks(const SwPaM& rPam, const SwPosition& rTarget);
+
+void CalcBreaks(std::vector<std::pair<SwNodeOffset, sal_Int32>> & rBreaks,
+ SwPaM const & rPam, bool const isOnlyFieldmarks = false);
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentDeviceManager.hxx b/sw/source/core/inc/DocumentDeviceManager.hxx
new file mode 100644
index 000000000..e99c54c10
--- /dev/null
+++ b/sw/source/core/inc/DocumentDeviceManager.hxx
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTDEVICEMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTDEVICEMANAGER_HXX
+
+#include <IDocumentDeviceAccess.hxx>
+#include <vcl/vclptr.hxx>
+#include <memory>
+
+class SwDoc;
+class SfxPrinter;
+class VirtualDevice;
+class OutputDevice;
+class JobSetup;
+class SwPrintData;
+
+namespace sw
+{
+class DocumentDeviceManager final : public IDocumentDeviceAccess
+{
+public:
+ DocumentDeviceManager(SwDoc& i_rSwdoc);
+
+ SfxPrinter* getPrinter(/*[in]*/ bool bCreate) const override;
+
+ void setPrinter(/*[in]*/ SfxPrinter* pP, /*[in]*/ bool bDeleteOld,
+ /*[in]*/ bool bCallPrtDataChanged) override;
+
+ VirtualDevice* getVirtualDevice(/*[in]*/ bool bCreate) const override;
+
+ void setVirtualDevice(/*[in]*/ VirtualDevice* pVd) override;
+
+ OutputDevice* getReferenceDevice(/*[in]*/ bool bCreate) const override;
+
+ void setReferenceDeviceType(/*[in]*/ bool bNewVirtual, /*[in]*/ bool bNewHiRes) override;
+
+ const JobSetup* getJobsetup() const override;
+
+ void setJobsetup(/*[in]*/ const JobSetup& rJobSetup) override;
+
+ const SwPrintData& getPrintData() const override;
+
+ void setPrintData(/*[in]*/ const SwPrintData& rPrtData) override;
+
+ virtual ~DocumentDeviceManager() override;
+
+private:
+ DocumentDeviceManager(DocumentDeviceManager const&) = delete;
+ DocumentDeviceManager& operator=(DocumentDeviceManager const&) = delete;
+
+ VirtualDevice& CreateVirtualDevice_() const;
+ SfxPrinter& CreatePrinter_() const;
+ void PrtDataChanged(); /**< Printer or JobSetup altered.
+ Care has to be taken of the necessary
+ invalidations and notifications. */
+
+ SwDoc& m_rDoc;
+ VclPtr<SfxPrinter> mpPrt;
+ VclPtr<VirtualDevice> mpVirDev;
+ std::unique_ptr<SwPrintData> mpPrtData;
+};
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentDrawModelManager.hxx b/sw/source/core/inc/DocumentDrawModelManager.hxx
new file mode 100644
index 000000000..26fd603a6
--- /dev/null
+++ b/sw/source/core/inc/DocumentDrawModelManager.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTDRAWMODELMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTDRAWMODELMANAGER_HXX
+
+#include <IDocumentDrawModelAccess.hxx>
+#include <svx/svdtypes.hxx>
+#include <memory>
+#include <drawdoc.hxx>
+
+class SdrPageView;
+class SwDoc;
+
+namespace sw
+{
+class DocumentDrawModelManager final : public IDocumentDrawModelAccess
+{
+public:
+ DocumentDrawModelManager(SwDoc& i_rSwdoc);
+
+ void InitDrawModel();
+ void ReleaseDrawModel();
+ void DrawNotifyUndoHdl();
+
+ //IDocumentDrawModelAccess
+ virtual const SwDrawModel* GetDrawModel() const override;
+ virtual SwDrawModel* GetDrawModel() override;
+ virtual SwDrawModel* MakeDrawModel_() override;
+ virtual SwDrawModel* GetOrCreateDrawModel() override;
+ virtual SdrLayerID GetHeavenId() const override;
+ virtual SdrLayerID GetHellId() const override;
+ virtual SdrLayerID GetControlsId() const override;
+ virtual SdrLayerID GetInvisibleHeavenId() const override;
+ virtual SdrLayerID GetInvisibleHellId() const override;
+ virtual SdrLayerID GetInvisibleControlsId() const override;
+
+ virtual void NotifyInvisibleLayers(SdrPageView& _rSdrPageView) override;
+
+ virtual bool IsVisibleLayerId(SdrLayerID _nLayerId) const override;
+
+ virtual SdrLayerID GetInvisibleLayerIdByVisibleOne(SdrLayerID _nVisibleLayerId) override;
+
+ virtual bool Search(const SwPaM& rPaM, const SvxSearchItem& rSearchItem) override;
+
+private:
+ DocumentDrawModelManager(DocumentDrawModelManager const&) = delete;
+ DocumentDrawModelManager& operator=(DocumentDrawModelManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ std::unique_ptr<SwDrawModel> mpDrawModel;
+
+ /** Draw Model Layer IDs
+ * LayerIds, Heaven == above document
+ * Hell == below document
+ * Controls == at the very top
+ */
+ SdrLayerID mnHeaven;
+ SdrLayerID mnHell;
+ SdrLayerID mnControls;
+ SdrLayerID mnInvisibleHeaven;
+ SdrLayerID mnInvisibleHell;
+ SdrLayerID mnInvisibleControls;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentExternalDataManager.hxx b/sw/source/core/inc/DocumentExternalDataManager.hxx
new file mode 100644
index 000000000..bc80ac429
--- /dev/null
+++ b/sw/source/core/inc/DocumentExternalDataManager.hxx
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTEXTERNALDATAMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTEXTERNALDATAMANAGER_HXX
+
+#include <IDocumentExternalData.hxx>
+
+namespace sw
+{
+class DocumentExternalDataManager final : public IDocumentExternalData
+{
+private:
+ DocumentExternalDataManager(DocumentExternalDataManager const&) = delete;
+ DocumentExternalDataManager& operator=(DocumentExternalDataManager const&) = delete;
+
+public:
+ DocumentExternalDataManager() = default;
+
+ void setExternalData(::sw::tExternalDataType eType,
+ ::sw::tExternalDataPointer pPayload) override;
+ ::sw::tExternalDataPointer getExternalData(::sw::tExternalDataType eType) override;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentFieldsManager.hxx b/sw/source/core/inc/DocumentFieldsManager.hxx
new file mode 100644
index 000000000..43b15e31f
--- /dev/null
+++ b/sw/source/core/inc/DocumentFieldsManager.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTFIELDSMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTFIELDSMANAGER_HXX
+
+#include <IDocumentFieldsAccess.hxx>
+#include <sal/types.h>
+#include <memory>
+
+class SwDoc;
+class SwDBNameInfField;
+
+namespace sw {
+
+class DocumentFieldsManager final : public IDocumentFieldsAccess
+{
+
+public:
+
+ DocumentFieldsManager( SwDoc& i_rSwdoc );
+
+ virtual const SwFieldTypes *GetFieldTypes() const override;
+ virtual SwFieldType *InsertFieldType(const SwFieldType &) override;
+ virtual SwFieldType *GetSysFieldType( const SwFieldIds eWhich ) const override;
+ virtual SwFieldType* GetFieldType(SwFieldIds nResId, const OUString& rName, bool bDbFieldMatching) const override;
+ virtual void RemoveFieldType(size_t nField) override;
+ virtual void UpdateFields(bool bCloseDB) override;
+ virtual void InsDeletedFieldType(SwFieldType &) override;
+ virtual void PutValueToField(const SwPosition & rPos, const css::uno::Any& rVal, sal_uInt16 nWhich) override;
+ virtual bool UpdateField(SwTextField * rDstFormatField, SwField & rSrcField, SwMsgPoolItem * pMsgHint, bool bUpdateTableFields) override;
+ virtual void UpdateRefFields() override;
+ virtual void UpdateTableFields(SfxPoolItem* pHt) override;
+ virtual void UpdateExpFields(SwTextField* pField, bool bUpdateRefFields) override;
+ virtual void UpdateUsrFields() override;
+ virtual void UpdatePageFields(SfxPoolItem*) override;
+ virtual void LockExpFields() override;
+ virtual void UnlockExpFields() override;
+ virtual bool IsExpFieldsLocked() const override;
+ virtual SwDocUpdateField& GetUpdateFields() const override;
+ virtual bool SetFieldsDirty(bool b, const SwNode* pChk, SwNodeOffset nLen) override;
+ virtual void SetFixFields(const DateTime* pNewDateTime) override;
+ virtual void FieldsToCalc(SwCalc& rCalc, SwNodeOffset nLastNd, sal_Int32 nLastCnt) override;
+ virtual void FieldsToCalc(SwCalc& rCalc, const SetGetExpField& rToThisField, SwRootFrame const* pLayout) override;
+ virtual void FieldsToExpand(SwHashTable<HashStr>& rTable, const SetGetExpField& rToThisField, SwRootFrame const& rLayout) override;
+ virtual bool IsNewFieldLst() const override;
+ virtual void SetNewFieldLst( bool bFlag) override;
+ virtual void InsDelFieldInFieldLst(bool bIns, const SwTextField& rField) override;
+ virtual sal_Int32 GetRecordsPerDocument() const override;
+
+ //Non Interface methods
+
+ /** Returns the field at a certain position.
+ @param rPos position to search at
+ @return pointer to field at the given position or NULL in case no field is found
+ */
+ static SwField* GetFieldAtPos(const SwPosition& rPos);
+
+ /** Returns the field at a certain position.
+ @param rPos position to search at
+ @return pointer to field at the given position or NULL in case no field is found
+ */
+ static SwTextField* GetTextFieldAtPos(const SwPosition& rPos);
+
+ bool containsUpdatableFields();
+
+ // Delete all unreferenced field types.
+ void GCFieldTypes();
+
+ void InitFieldTypes();
+
+ void ClearFieldTypes();
+
+ void UpdateDBNumFields( SwDBNameInfField& rDBField, SwCalc& rCalc );
+
+ virtual ~DocumentFieldsManager() override;
+
+private:
+
+ DocumentFieldsManager(DocumentFieldsManager const&) = delete;
+ DocumentFieldsManager& operator=(DocumentFieldsManager const&) = delete;
+
+ void UpdateExpFieldsImpl(SwTextField* pField, SwRootFrame const* pLayout);
+
+ SwDoc& m_rDoc;
+
+ bool mbNewFieldLst; //< TRUE: Rebuild field-list.
+ std::unique_ptr<SwDocUpdateField> mpUpdateFields; //< Struct for updating fields
+ std::unique_ptr<SwFieldTypes> mpFieldTypes;
+ sal_Int8 mnLockExpField; //< If != 0 UpdateExpFields() has no effect!
+};
+
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentLayoutManager.hxx b/sw/source/core/inc/DocumentLayoutManager.hxx
new file mode 100644
index 000000000..8db5cbe32
--- /dev/null
+++ b/sw/source/core/inc/DocumentLayoutManager.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLAYOUTMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLAYOUTMANAGER_HXX
+
+#include <IDocumentLayoutAccess.hxx>
+#include <memory>
+
+class SwDoc;
+class SwViewShell;
+class SwLayouter;
+
+namespace sw {
+
+class DocumentLayoutManager final : public IDocumentLayoutAccess
+{
+
+public:
+
+ DocumentLayoutManager( SwDoc& i_rSwdoc );
+
+ virtual const SwViewShell *GetCurrentViewShell() const override;
+ virtual SwViewShell *GetCurrentViewShell() override; //< It must be able to communicate to a SwViewShell.This is going to be removed later.
+ virtual void SetCurrentViewShell( SwViewShell* pNew ) override;
+
+ virtual const SwRootFrame *GetCurrentLayout() const override;
+ virtual SwRootFrame *GetCurrentLayout() override;
+ virtual bool HasLayout() const override;
+
+ virtual const SwLayouter* GetLayouter() const override;
+ virtual SwLayouter* GetLayouter() override;
+ virtual void SetLayouter( SwLayouter* pNew ) override;
+
+ virtual SwFrameFormat* MakeLayoutFormat( RndStdIds eRequest, const SfxItemSet* pSet ) override;
+ virtual void DelLayoutFormat( SwFrameFormat *pFormat ) override;
+ virtual SwFrameFormat* CopyLayoutFormat( const SwFrameFormat& rSrc, const SwFormatAnchor& rNewAnchor, bool bSetTextFlyAtt, bool bMakeFrames ) override;
+
+ //Non Interface methods
+ void ClearSwLayouterEntries();
+
+ virtual ~DocumentLayoutManager() override;
+
+private:
+
+ DocumentLayoutManager(DocumentLayoutManager const&) = delete;
+ DocumentLayoutManager& operator=(DocumentLayoutManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ SwViewShell *mpCurrentView; //< SwDoc should get a new member mpCurrentView
+ std::unique_ptr<SwLayouter> mpLayouter; /**< css::frame::Controller for complex layout formatting
+ like footnote/endnote in sections */
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentLinksAdministrationManager.hxx b/sw/source/core/inc/DocumentLinksAdministrationManager.hxx
new file mode 100644
index 000000000..e03d969d2
--- /dev/null
+++ b/sw/source/core/inc/DocumentLinksAdministrationManager.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLINKSADMINISTRATIONMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLINKSADMINISTRATIONMANAGER_HXX
+
+#include <IDocumentLinksAdministration.hxx>
+
+#include <memory>
+#include <string_view>
+
+namespace sfx2 { class LinkManager; }
+class SwDoc;
+class SwPaM;
+class SwNodeRange;
+
+namespace sw
+{
+
+class DocumentLinksAdministrationManager final : public IDocumentLinksAdministration
+{
+public:
+
+ DocumentLinksAdministrationManager( SwDoc& i_rSwdoc );
+
+ bool IsVisibleLinks() const override;
+
+ void SetVisibleLinks(bool bFlag) override;
+
+ sfx2::LinkManager& GetLinkManager() override;
+
+ const sfx2::LinkManager& GetLinkManager() const override;
+
+ void UpdateLinks() override;
+
+ bool GetData(const OUString& rItem, const OUString& rMimeType, css::uno::Any& rValue) const override;
+
+ void SetData(const OUString& rItem) override;
+
+ ::sfx2::SvLinkSource* CreateLinkSource(const OUString& rItem) override;
+
+ bool EmbedAllLinks() override;
+
+ void SetLinksUpdated(const bool bNewLinksUpdated) override;
+
+ bool LinksUpdated() const override;
+
+ //Non-Interface method
+ bool SelectServerObj( std::u16string_view rStr, SwPaM*& rpPam, std::unique_ptr<SwNodeRange>& rpRange ) const;
+
+ virtual ~DocumentLinksAdministrationManager() override;
+
+private:
+
+ DocumentLinksAdministrationManager(DocumentLinksAdministrationManager const&) = delete;
+ DocumentLinksAdministrationManager& operator=(DocumentLinksAdministrationManager const&) = delete;
+
+ bool mbVisibleLinks; //< TRUE: Links are inserted visibly.
+ bool mbLinksUpdated; //< #i38810# flag indicating, that the links have been updated.
+ std::unique_ptr<sfx2::LinkManager> m_pLinkMgr; //< List of linked stuff (graphics/DDE/OLE).
+
+ SwDoc& m_rDoc;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentListItemsManager.hxx b/sw/source/core/inc/DocumentListItemsManager.hxx
new file mode 100644
index 000000000..eb31e4e25
--- /dev/null
+++ b/sw/source/core/inc/DocumentListItemsManager.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTITEMSMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTITEMSMANAGER_HXX
+
+#include <IDocumentListItems.hxx>
+#include <memory>
+#include <set>
+
+namespace sw
+{
+
+class DocumentListItemsManager final : public IDocumentListItems
+{
+public:
+
+ DocumentListItemsManager();
+
+ void addListItem( const SwNodeNum& rNodeNum ) override;
+ void removeListItem( const SwNodeNum& rNodeNum ) override;
+
+ OUString getListItemText(const SwNodeNum& rNodeNum,
+ SwRootFrame const& rLayout) const override;
+
+ bool isNumberedInLayout(SwNodeNum const& rNodeNum,
+ SwRootFrame const& rLayout) const override;
+
+ void getNumItems( IDocumentListItems::tSortedNodeNumList& orNodeNumList ) const override;
+
+ virtual ~DocumentListItemsManager() override;
+
+
+ //Non Interface
+ struct lessThanNodeNum
+ {
+ bool operator()( const SwNodeNum* pNodeNumOne,
+ const SwNodeNum* pNodeNumTwo ) const;
+ };
+
+ typedef std::set< const SwNodeNum*, lessThanNodeNum > tImplSortedNodeNumList;
+
+private:
+
+ DocumentListItemsManager(DocumentListItemsManager const&) = delete;
+ DocumentListItemsManager& operator=(DocumentListItemsManager const&) = delete;
+
+ std::unique_ptr<tImplSortedNodeNumList> mpListItemsList;
+};
+
+}
+
+ #endif // INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTITEMSMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentListsManager.hxx b/sw/source/core/inc/DocumentListsManager.hxx
new file mode 100644
index 000000000..caab21e35
--- /dev/null
+++ b/sw/source/core/inc/DocumentListsManager.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTSMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTSMANAGER_HXX
+
+#include <IDocumentListsAccess.hxx>
+#include <o3tl/deleter.hxx>
+#include <memory>
+#include <unordered_map>
+
+class SwList;
+class SwDoc;
+
+namespace sw
+{
+
+class DocumentListsManager final : public IDocumentListsAccess
+{
+ public:
+
+ DocumentListsManager( SwDoc& i_rSwdoc );
+
+ SwList* createList( const OUString& rListId,
+ const OUString& rDefaultListStyleName ) override;
+ SwList* getListByName( const OUString& rListId ) const override;
+
+ void createListForListStyle( const OUString& rListStyleName ) override;
+ SwList* getListForListStyle( const OUString& rListStyleName ) const override;
+ void deleteListForListStyle( const OUString& rListStyleName ) override;
+ void deleteListsByDefaultListStyle( const OUString& rListStyleName ) override;
+ // #i91400#
+ void trackChangeOfListStyleName( const OUString& rListStyleName,
+ const OUString& rNewListStyleName ) override;
+ virtual ~DocumentListsManager() override;
+
+ private:
+
+ DocumentListsManager(DocumentListsManager const&) = delete;
+ DocumentListsManager& operator=(DocumentListsManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ typedef std::unique_ptr<SwList, o3tl::default_delete<SwList>> SwListPtr;
+ // container to hold the lists of the text document
+ std::unordered_map<OUString, SwListPtr> maLists;
+ // relation between list style and its default list
+ std::unordered_map<OUString, SwList*> maListStyleLists;
+
+ OUString CreateUniqueListId();
+ OUString MakeListIdUnique( const OUString& aSuggestedUniqueListId );
+};
+
+}
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTLISTSMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentOutlineNodesManager.hxx b/sw/source/core/inc/DocumentOutlineNodesManager.hxx
new file mode 100644
index 000000000..2489d2397
--- /dev/null
+++ b/sw/source/core/inc/DocumentOutlineNodesManager.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTOUTLINENODESMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTOUTLINENODESMANAGER_HXX
+
+#include <IDocumentOutlineNodes.hxx>
+
+class SwDoc;
+
+namespace sw
+{
+
+class DocumentOutlineNodesManager final : public IDocumentOutlineNodes
+{
+public:
+
+ DocumentOutlineNodesManager( SwDoc& i_rSwdoc );
+
+ //typedef std::vector< const SwTextNode* > tSortedOutlineNodeList;
+
+ tSortedOutlineNodeList::size_type getOutlineNodesCount() const override;
+
+ int getOutlineLevel( const tSortedOutlineNodeList::size_type nIdx ) const override;
+ OUString getOutlineText( const tSortedOutlineNodeList::size_type nIdx,
+ SwRootFrame const* pLayout,
+ const bool bWithNumber = true,
+ const bool bWithSpacesForLevel = false,
+ const bool bWithFootnote = true ) const override;
+ SwTextNode* getOutlineNode( const tSortedOutlineNodeList::size_type nIdx ) const override;
+ bool isOutlineInLayout(tSortedOutlineNodeList::size_type nIdx,
+ SwRootFrame const& rLayout) const override;
+ void getOutlineNodes( IDocumentOutlineNodes::tSortedOutlineNodeList& orOutlineNodeList ) const override;
+
+ virtual ~DocumentOutlineNodesManager() override;
+
+private:
+
+ DocumentOutlineNodesManager(DocumentOutlineNodesManager const&) = delete;
+ DocumentOutlineNodesManager& operator=(DocumentOutlineNodesManager const&) = delete;
+
+ SwDoc& m_rDoc;
+};
+
+}
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTOUTLINENODESMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentRedlineManager.hxx b/sw/source/core/inc/DocumentRedlineManager.hxx
new file mode 100644
index 000000000..478332bbd
--- /dev/null
+++ b/sw/source/core/inc/DocumentRedlineManager.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTREDLINEMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTREDLINEMANAGER_HXX
+
+#include <IDocumentRedlineAccess.hxx>
+
+class SwDoc;
+
+namespace sw
+{
+
+class SAL_DLLPUBLIC_RTTI DocumentRedlineManager final : public IDocumentRedlineAccess
+{
+public:
+ DocumentRedlineManager( SwDoc& i_rSwdoc );
+
+ /**
+ * Replaced by SwRootFrame::IsHideRedlines() (this is model-level redline
+ * hiding).
+ */
+ virtual RedlineFlags GetRedlineFlags() const override;
+
+ virtual void SetRedlineFlags_intern(/*[in]*/RedlineFlags eMode) override;
+
+ virtual void SetRedlineFlags(/*[in]*/RedlineFlags eMode) override;
+
+ virtual bool IsRedlineOn() const override;
+
+ virtual bool IsIgnoreRedline() const override;
+
+ virtual const SwRedlineTable& GetRedlineTable() const override;
+ virtual SwRedlineTable& GetRedlineTable() override;
+ virtual const SwExtraRedlineTable& GetExtraRedlineTable() const override;
+ virtual SwExtraRedlineTable& GetExtraRedlineTable() override;
+
+ virtual bool IsInRedlines(const SwNode& rNode) const override;
+
+ virtual AppendResult AppendRedline(/*[in]*/SwRangeRedline* pPtr, /*[in]*/bool bCallDelete) override;
+
+ virtual bool AppendTableRowRedline(/*[in]*/SwTableRowRedline* pPtr) override;
+ virtual bool AppendTableCellRedline(/*[in]*/SwTableCellRedline* pPtr) override;
+
+ virtual bool SplitRedline(/*[in]*/const SwPaM& rPam) override;
+
+ virtual bool DeleteRedline(
+ /*[in]*/const SwPaM& rPam,
+ /*[in]*/bool bSaveInUndo,
+ /*[in]*/RedlineType nDelType) override;
+
+ virtual bool DeleteRedline(
+ /*[in]*/const SwStartNode& rSection,
+ /*[in]*/bool bSaveInUndo,
+ /*[in]*/RedlineType nDelType) override;
+
+ virtual SwRedlineTable::size_type GetRedlinePos(
+ /*[in]*/const SwNode& rNode,
+ /*[in]*/RedlineType nType) const override;
+
+ virtual bool HasRedline(
+ /*[in]*/const SwPaM& rPam,
+ /*[in]*/RedlineType nType,
+ /*[in]*/bool bStartOrEndInRange) const override;
+
+ virtual void CompressRedlines() override;
+
+ virtual const SwRangeRedline* GetRedline(
+ /*[in]*/const SwPosition& rPos,
+ /*[in]*/SwRedlineTable::size_type* pFndPos) const override;
+
+ virtual bool IsRedlineMove() const override;
+
+ virtual void SetRedlineMove(/*[in]*/bool bFlag) override;
+
+ virtual bool AcceptRedline(/*[in]*/SwRedlineTable::size_type nPos, /*[in]*/bool bCallDelete) override;
+
+ virtual bool AcceptRedline(/*[in]*/const SwPaM& rPam, /*[in]*/bool bCallDelete) override;
+
+ virtual void AcceptRedlineParagraphFormatting(/*[in]*/const SwPaM& rPam) override;
+
+ virtual bool RejectRedline(/*[in]*/SwRedlineTable::size_type nPos, /*[in]*/bool bCallDelete) override;
+
+ virtual bool RejectRedline(/*[in]*/const SwPaM& rPam, /*[in]*/bool bCallDelete) override;
+
+ virtual void AcceptAllRedline(/*[in]*/bool bAcceptReject) override;
+
+ virtual const SwRangeRedline* SelNextRedline(/*[in]*/SwPaM& rPam) const override;
+
+ virtual const SwRangeRedline* SelPrevRedline(/*[in]*/SwPaM& rPam) const override;
+
+ virtual void UpdateRedlineAttr() override;
+
+ virtual std::size_t GetRedlineAuthor() override;
+
+ virtual std::size_t InsertRedlineAuthor(const OUString& rAuthor) override;
+
+ virtual bool SetRedlineComment(
+ /*[in]*/const SwPaM& rPam,
+ /*[in]*/const OUString& rComment) override;
+
+ virtual const css::uno::Sequence <sal_Int8>& GetRedlinePassword() const override;
+
+ virtual void SetRedlinePassword(
+ /*[in]*/const css::uno::Sequence <sal_Int8>& rNewPassword) override;
+
+ //Non Interface methods;
+
+ /** Set comment-text for Redline. It then comes in via AppendRedLine.
+ Used by AutoFormat. 0-pointer resets mode.
+ Sequence number is for conjoining of Redlines by the UI. */
+ void SetAutoFormatRedlineComment( const OUString* pText, sal_uInt16 nSeqNo = 0 );
+
+ bool IsHideRedlines() const { return m_bHideRedlines; }
+ void SetHideRedlines(bool const bHideRedlines) { m_bHideRedlines = bHideRedlines; }
+
+ void HideAll(bool bDeletion);
+ void ShowAll();
+
+ virtual ~DocumentRedlineManager() override;
+
+private:
+
+ DocumentRedlineManager(DocumentRedlineManager const&) = delete;
+ DocumentRedlineManager& operator=(DocumentRedlineManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ RedlineFlags meRedlineFlags; //< Current Redline Mode.
+ SwRedlineTable maRedlineTable; //< List of all Ranged Redlines.
+ SwExtraRedlineTable maExtraRedlineTable; //< List of all Extra Redlines.
+ std::optional<OUString> moAutoFormatRedlnComment; //< Comment for Redlines inserted via AutoFormat.
+ bool mbIsRedlineMove; //< true: Redlines are moved into to / out of the section.
+ sal_uInt16 mnAutoFormatRedlnCommentNo; /**< SeqNo for conjoining of AutoFormat-Redlines.
+ by the UI. Managed by SwAutoFormat! */
+ css::uno::Sequence <sal_Int8 > maRedlinePasswd;
+
+ /// this flag is necessary for file import because the ViewShell/layout is
+ /// created "too late" and the ShowRedlineChanges item is not below "Views"
+ bool m_bHideRedlines = false;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentSettingManager.hxx b/sw/source/core/inc/DocumentSettingManager.hxx
new file mode 100644
index 000000000..f37696df1
--- /dev/null
+++ b/sw/source/core/inc/DocumentSettingManager.hxx
@@ -0,0 +1,223 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSETTINGMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSETTINGMANAGER_HXX
+
+#include <IDocumentSettingAccess.hxx>
+class SwDoc;
+typedef struct _xmlTextWriter* xmlTextWriterPtr;
+
+namespace sw {
+class DocumentSettingManager final :
+ public IDocumentSettingAccess
+{
+ std::shared_ptr<SvxForbiddenCharactersTable> mxForbiddenCharsTable;
+ SwDoc &m_rDoc;
+
+ sal_uInt16 mnLinkUpdMode; //< UpdateMode for links.
+
+ SwFieldUpdateFlags meFieldUpdMode;//< Automatically Update Mode for fields/charts.
+ CharCompressType meChrCmprType;//< for ASIAN: compress punctuation/kana
+
+ sal_uInt32 mn32DummyCompatibilityOptions1;
+ sal_uInt32 mn32DummyCompatibilityOptions2;
+
+ // COMPATIBILITY FLAGS START
+ //
+ //
+ // HISTORY OF THE COMPATIBILITY FLAGS:
+ //
+ // SO5:
+ // mbParaSpaceMax def = false, true since SO8
+ // mbParaSpaceMaxAtPages def = false, true since SO8
+ //
+ // SO6:
+ // mbTabCompat def = false, true since SO8
+ //
+ // SO7:
+ // mbUseVirtualDevice def = true
+ // mbAddFlyOffsets def = false, hidden
+ //
+ // SO7pp1:
+ // bOldNumbering def = false, hidden
+ //
+ // SO8:
+ // mbAddExternalLeading def = true
+ // mbUseHiResolutionVirtualDevice def = true, hidden
+ // mbOldLineSpacing def = false
+ // mbAddParaSpacingToTableCells def = true
+ // mbUseFormerObjectPos def = false
+ // mbUseFormerTextWrapping def = false
+ // mbConsiderWrapOnObjPos def = false
+ //
+ // SO8pp1:
+ // mbIgnoreFirstLineIndentInNumbering def = false, hidden
+ // mbDoNotJustifyLinesWithManualBreak def = false, hidden
+ // mbDoNotResetParaAttrsForNumFont def = false, hidden
+ //
+ // SO8pp3
+ // mbDoNotCaptureDrawObjsOnPage def = false, hidden
+ // - Relevant for drawing objects, which don't follow the text flow, but
+ // whose position is outside the page area:
+ // false: Such drawing objects are captured on the page area of its anchor.
+ // true: Such drawing objects can leave the page area, they aren't captured.
+ // mbTableRowKeep def = false, hidden
+ // mbIgnoreTabsAndBlanksForLineCalculation def = false, hidden
+ // mbClipAsCharacterAnchoredWriterFlyFrame def = false, hidden
+ // - Introduced in order to re-activate clipping of as-character anchored
+ // Writer fly frames in method <SwFlyInContentFrame::MakeAll()> for documents,
+ // which are created with version prior SO8/OOo 2.0
+ //
+ // SO8pp4
+ // mbUnixForceZeroExtLeading def = false, hidden
+ //
+ // SO8pu8
+ //
+ // SO9
+ // #i24363# tab stops relative to indent
+ // mbTabRelativeToIndent def = true, hidden
+ // #i89181# suppress tab stop at left indent for paragraphs in lists, whose
+ // list level position and space mode equals LABEL_ALIGNMENT and whose list
+ // label is followed by a tab character.
+ // mbTabAtLeftIndentForParagraphsInList def = false, hidden
+
+ bool mbHTMLMode : 1; //< true: Document is in HTMLMode.
+ bool mbIsGlobalDoc : 1; //< true: It's a global document.
+ bool mbGlblDocSaveLinks : 1; //< true: Save sections linked in global document.
+ bool mbIsLabelDoc : 1; //< true: It's a label document.
+ bool mbPurgeOLE : 1; //< true: Purge OLE-Objects
+ bool mbKernAsianPunctuation : 1; //< true: kerning also for ASIAN punctuation
+
+ bool mbParaSpaceMax : 1;
+ bool mbParaSpaceMaxAtPages : 1;
+ bool mbTabCompat : 1;
+ bool mbUseVirtualDevice : 1;
+ bool mbAddFlyOffsets : 1;
+ bool mbAddVerticalFlyOffsets : 1;
+ bool mbAddExternalLeading : 1;
+ bool mbUseHiResolutionVirtualDevice : 1;
+ bool mbOldLineSpacing : 1; // #i11859#
+ bool mbAddParaSpacingToTableCells : 1;
+ bool mbUseFormerObjectPos : 1; // #i11860#
+ bool mbUseFormerTextWrapping : 1;
+ bool mbConsiderWrapOnObjPos : 1; // #i28701#
+ // true: object positioning algorithm has consider the wrapping style of // the floating screen objects as given by its attribute 'WrapInfluenceOnObjPos'
+ // floating screen objects as given by its
+ // attribute 'WrapInfluenceOnObjPos'.
+ bool mbMathBaselineAlignment : 1; // TL 2010-10-29 #i972#
+ bool mbStylesNoDefault : 1;
+ bool mbFloattableNomargins : 1; //< If paragraph margins next to a floating table should be ignored.
+ bool mEmbedFonts : 1; //< Whether to embed fonts when saving.
+ bool mEmbedUsedFonts : 1; //< Whether to embed fonts that are used by the document when saving.
+ bool mEmbedLatinScriptFonts : 1; //< Whether to embed latin script fonts when saving.
+ bool mEmbedAsianScriptFonts : 1; //< Whether to embed asian script fonts when saving.
+ bool mEmbedComplexScriptFonts : 1; //< Whether to embed complex script fonts when saving.
+ bool mEmbedSystemFonts : 1; //< Whether to embed also system fonts.
+
+ // non-ui-compatibility flags:
+ bool mbOldNumbering : 1;
+ bool mbIgnoreFirstLineIndentInNumbering : 1; // #i47448#
+ bool mbDoNotJustifyLinesWithManualBreak : 1; // #i49277#
+ bool mbDoNotResetParaAttrsForNumFont : 1; // #i53199#
+ bool mbTableRowKeep : 1;
+ bool mbIgnoreTabsAndBlanksForLineCalculation : 1; // #i3952#
+ bool mbDoNotCaptureDrawObjsOnPage : 1; // #i62875#
+ bool mbClipAsCharacterAnchoredWriterFlyFrames : 1;
+ bool mbUnixForceZeroExtLeading : 1; // #i60945#
+ bool mbTabRelativeToIndent : 1; // #i24363# tab stops relative to indent
+ bool mbProtectForm : 1;
+ bool mbMsWordCompTrailingBlanks : 1; // tdf#104349 tdf#104668
+ bool mbMsWordCompMinLineHeightByFly : 1;
+ bool mbInvertBorderSpacing : 1;
+ bool mbCollapseEmptyCellPara : 1;
+ bool mbTabAtLeftIndentForParagraphsInList; // #i89181# - see above
+ bool mbSmallCapsPercentage66;
+ bool mbTabOverflow;
+ bool mbUnbreakableNumberings;
+ bool mbClippedPictures;
+ bool mbBackgroundParaOverDrawings;
+ bool mbTabOverMargin;
+ bool mbTabOverSpacing;
+ bool mbTreatSingleColumnBreakAsPageBreak; // tdf#76349
+ bool mbSurroundTextWrapSmall;
+ bool mbPropLineSpacingShrinksFirstLine; // fdo#79602
+ bool mbSubtractFlys; // tdf#86578
+ bool mApplyParagraphMarkFormatToNumbering;
+ bool mbAddParaLineSpacingToTableCells; // tdf#125300 tdf#134782
+
+ bool mbLastBrowseMode : 1;
+ bool mbDisableOffPagePositioning; // tdf#112443
+ bool mbEmptyDbFieldHidesPara;
+ bool mbContinuousEndnotes = false;
+ bool mbProtectBookmarks;
+ bool mbProtectFields;
+ bool mbHeaderSpacingBelowLastPara;
+ bool mbFrameAutowidthWithMorePara; //tdf#124423
+ /// Gutter position: false means left (not a compatibility setting).
+ bool mbGutterAtTop;
+ bool mbFootnoteInColumnToPageEnd;
+ sal_Int32 mnImagePreferredDPI;
+ bool mbAutoFirstLineIndentDisregardLineSpace;
+ // If this is on as_char flys wrapping will be handled the same like in Word
+ bool mbWrapAsCharFlysLikeInOOXML;
+ bool mbNoNumberingShowFollowBy;
+
+public:
+
+ DocumentSettingManager(SwDoc &rDoc);
+ virtual ~DocumentSettingManager() override;
+
+ // IDocumentSettingAccess
+ virtual bool get(/*[in]*/ DocumentSettingId id) const override;
+ virtual void set(/*[in]*/ DocumentSettingId id, /*[in]*/ bool value) override;
+ virtual const css::i18n::ForbiddenCharacters* getForbiddenCharacters(/*[in]*/ LanguageType nLang, /*[in]*/ bool bLocaleData ) const override;
+ virtual void setForbiddenCharacters(/*[in]*/ LanguageType nLang, /*[in]*/ const css::i18n::ForbiddenCharacters& rForbiddenCharacters ) override;
+ virtual std::shared_ptr<SvxForbiddenCharactersTable>& getForbiddenCharacterTable() override;
+ virtual const std::shared_ptr<SvxForbiddenCharactersTable>& getForbiddenCharacterTable() const override;
+ virtual sal_uInt16 getLinkUpdateMode( /*[in]*/bool bGlobalSettings ) const override;
+ virtual void setLinkUpdateMode( /*[in]*/ sal_uInt16 nMode ) override;
+ virtual SwFieldUpdateFlags getFieldUpdateFlags( /*[in]*/bool bGlobalSettings ) const override;
+ virtual void setFieldUpdateFlags( /*[in]*/ SwFieldUpdateFlags eMode ) override;
+ virtual CharCompressType getCharacterCompressionType() const override;
+ virtual void setCharacterCompressionType( /*[in]*/CharCompressType nType ) override;
+
+ sal_Int32 getImagePreferredDPI() override
+ {
+ return mnImagePreferredDPI;
+ }
+ void setImagePreferredDPI(sal_Int32 nValue) override
+ {
+ mnImagePreferredDPI = nValue;
+ }
+
+// Replace all compatibility options with those from rSource.
+ void ReplaceCompatibilityOptions(const DocumentSettingManager& rSource);
+
+ sal_uInt32 Getn32DummyCompatibilityOptions1() const override;
+ void Setn32DummyCompatibilityOptions1( const sal_uInt32 CompatibilityOptions1 ) override;
+ sal_uInt32 Getn32DummyCompatibilityOptions2() const override;
+ void Setn32DummyCompatibilityOptions2( const sal_uInt32 CompatibilityOptions2 ) override;
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+};
+
+}
+
+#endif //_DOCSETTING_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentStateManager.hxx b/sw/source/core/inc/DocumentStateManager.hxx
new file mode 100644
index 000000000..60c8c4923
--- /dev/null
+++ b/sw/source/core/inc/DocumentStateManager.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <IDocumentState.hxx>
+
+class SwDoc;
+
+
+namespace sw {
+
+class DocumentStateManager final : public IDocumentState
+{
+
+public:
+ DocumentStateManager( SwDoc& i_rSwdoc );
+
+ void SetModified() override;
+ void ResetModified() override;
+ bool IsModified() const override;
+ bool IsEnableSetModified() const override;
+ void SetEnableSetModified(bool bEnableSetModified) override;
+ bool IsInCallModified() const override;
+ bool IsUpdateExpField() const override;
+ bool IsNewDoc() const override;
+ void SetNewDoc(bool b) override;
+ void SetUpdateExpFieldStat(bool b) override;
+
+private:
+
+ DocumentStateManager(DocumentStateManager const&) = delete;
+ DocumentStateManager& operator=(DocumentStateManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ bool mbEnableSetModified; //< FALSE: changing document modification status (temporarily) locked
+ bool mbModified ; //< TRUE: document has changed.
+ bool mbUpdateExpField; //< TRUE: Update expression fields.
+ bool mbNewDoc ; //< TRUE: new Doc.
+ bool mbInCallModified; //< TRUE: in Set/Reset-Modified link.
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentStatisticsManager.hxx b/sw/source/core/inc/DocumentStatisticsManager.hxx
new file mode 100644
index 000000000..430fcff7c
--- /dev/null
+++ b/sw/source/core/inc/DocumentStatisticsManager.hxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTATISTICSMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTATISTICSMANAGER_HXX
+
+#include <IDocumentStatistics.hxx>
+#include <SwDocIdle.hxx>
+#include <tools/long.hxx>
+#include <memory>
+
+class SwDoc;
+struct SwDocStat;
+
+namespace sw
+{
+class DocumentStatisticsManager final : public IDocumentStatistics
+{
+public:
+ DocumentStatisticsManager(SwDoc& i_rSwdoc);
+
+ void DocInfoChgd(bool isEnableSetModified) override;
+ const SwDocStat& GetDocStat() const override;
+ void SetDocStatModified(bool bSet);
+ const SwDocStat& GetUpdatedDocStat(bool bCompleteAsync, bool bFields) override;
+ void SetDocStat(const SwDocStat& rStat) override;
+ void UpdateDocStat(bool bCompleteAsync, bool bFields) override;
+ virtual ~DocumentStatisticsManager() override;
+
+private:
+ DocumentStatisticsManager(DocumentStatisticsManager const&) = delete;
+ DocumentStatisticsManager& operator=(DocumentStatisticsManager const&) = delete;
+
+ SwDoc& m_rDoc;
+
+ /** continue computing a chunk of document statistics
+ * \param nChars number of characters to count before exiting
+ * \param bFields if stat. fields should be updated
+ *
+ * returns false when there is no more to calculate
+ */
+ bool IncrementalDocStatCalculate(tools::Long nChars, bool bFields = true);
+
+ // Our own 'StatsUpdateTimer' calls the following method
+ DECL_LINK(DoIdleStatsUpdate, Timer*, void);
+
+ std::unique_ptr<SwDocStat> mpDocStat; //< Statistics information
+ bool mbInitialized; //< allow first time update
+ SwDocIdle maStatsUpdateIdle; //< Idle for asynchronous stats calculation
+};
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DocumentStylePoolManager.hxx b/sw/source/core/inc/DocumentStylePoolManager.hxx
new file mode 100644
index 000000000..f410d0d09
--- /dev/null
+++ b/sw/source/core/inc/DocumentStylePoolManager.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTYLEPOOLMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTSTYLEPOOLMANAGER_HXX
+
+#include <IDocumentStylePoolAccess.hxx>
+
+class SwDoc;
+
+namespace sw {
+
+class DocumentStylePoolManager final : public IDocumentStylePoolAccess
+{
+
+public:
+
+ DocumentStylePoolManager( SwDoc& i_rSwdoc );
+
+ virtual SwTextFormatColl* GetTextCollFromPool( sal_uInt16 nId, bool bRegardLanguage = true ) override;
+ virtual SwFormat* GetFormatFromPool( sal_uInt16 nId ) override;
+ virtual SwFrameFormat* GetFrameFormatFromPool( sal_uInt16 nId ) override;
+ virtual SwCharFormat* GetCharFormatFromPool( sal_uInt16 nId ) override;
+ virtual SwPageDesc* GetPageDescFromPool( sal_uInt16 nId, bool bRegardLanguage = true ) override;
+ virtual SwNumRule* GetNumRuleFromPool( sal_uInt16 nId ) override;
+ virtual bool IsPoolTextCollUsed( sal_uInt16 nId ) const override;
+ virtual bool IsPoolFormatUsed( sal_uInt16 nId ) const override;
+ virtual bool IsPoolPageDescUsed( sal_uInt16 nId ) const override;
+
+ virtual ~DocumentStylePoolManager() override;
+
+private:
+
+ DocumentStylePoolManager(DocumentStylePoolManager const&) = delete;
+ DocumentStylePoolManager& operator=(DocumentStylePoolManager const&) = delete;
+
+ SwDoc& m_rDoc;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/sw/source/core/inc/DocumentTimerManager.hxx b/sw/source/core/inc/DocumentTimerManager.hxx
new file mode 100644
index 000000000..895aa4b86
--- /dev/null
+++ b/sw/source/core/inc/DocumentTimerManager.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTTIMERMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCUMENTTIMERMANAGER_HXX
+
+#include <IDocumentTimerAccess.hxx>
+#include <SwDocIdle.hxx>
+
+#include <sal/types.h>
+#include <tools/link.hxx>
+
+class SwDoc;
+
+namespace sw
+{
+class DocumentTimerManager final : public IDocumentTimerAccess
+{
+public:
+ enum class IdleJob
+ {
+ None, ///< document has no idle jobs to do
+ Busy, ///< document is busy and idle jobs are postponed
+ Grammar,
+ Layout,
+ Fields,
+ };
+
+ DocumentTimerManager(SwDoc& i_rSwdoc);
+ virtual ~DocumentTimerManager() override;
+
+ void StartIdling() override;
+
+ void StopIdling() override;
+
+ void BlockIdling() override;
+
+ void UnblockIdling() override;
+
+ bool IsDocIdle() const override;
+
+private:
+ DocumentTimerManager(DocumentTimerManager const&) = delete;
+ DocumentTimerManager& operator=(DocumentTimerManager const&) = delete;
+
+ /// Delay starting idle jobs to allow for post-load activity.
+ /// Used by LOK only.
+ DECL_LINK(FireIdleJobsTimeout, Timer*, void);
+
+ DECL_LINK(DoIdleJobs, Timer*, void);
+
+ IdleJob GetNextIdleJob() const;
+
+ SwDoc& m_rDoc;
+
+ sal_uInt32 m_nIdleBlockCount; ///< Don't run the Idle, if > 0
+ bool m_bStartOnUnblock; ///< true, if the last unblock should start the timer
+ SwDocIdle m_aDocIdle;
+ Timer m_aFireIdleJobsTimer;
+ bool m_bWaitForLokInit; ///< true if we waited for LOK to initialize already.
+};
+
+inline bool DocumentTimerManager::IsDocIdle() const
+{
+ return ((0 == m_nIdleBlockCount) && (GetNextIdleJob() != IdleJob::Busy));
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/DropDownFormFieldButton.hxx b/sw/source/core/inc/DropDownFormFieldButton.hxx
new file mode 100644
index 000000000..6f8aa42ed
--- /dev/null
+++ b/sw/source/core/inc/DropDownFormFieldButton.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "FormFieldButton.hxx"
+
+class SwEditWin;
+namespace sw::mark
+{
+class DropDownFieldmark;
+}
+
+/**
+ * This button is shown when the cursor is on a drop-down form field.
+ * The user can select an item of the field using this button while filling in a form.
+ */
+class DropDownFormFieldButton final : public FormFieldButton
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+
+ DECL_LINK(MyListBoxHandler, weld::TreeView&, bool);
+
+ void InitDropdown();
+
+public:
+ DropDownFormFieldButton(SwEditWin* pEditWin, sw::mark::DropDownFieldmark& rFieldMark);
+ virtual ~DropDownFormFieldButton() override;
+
+ virtual void LaunchPopup() override;
+ virtual void DestroyPopup() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/inc/FormFieldButton.hxx b/sw/source/core/inc/FormFieldButton.hxx
new file mode 100644
index 000000000..22a9b3e8c
--- /dev/null
+++ b/sw/source/core/inc/FormFieldButton.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/ctrl.hxx>
+#include <vcl/weld.hxx>
+#include <swrect.hxx>
+
+class SwEditWin;
+namespace sw::mark
+{
+class Fieldmark;
+}
+
+/**
+ * This button is shown when the cursor is on a form field with drop-down capability.
+ */
+class FormFieldButton : public Control
+{
+public:
+ FormFieldButton(SwEditWin* pEditWin, sw::mark::Fieldmark& rFieldMark);
+ virtual ~FormFieldButton() override;
+ virtual void dispose() override;
+
+ void CalcPosAndSize(const SwRect& rPortionPaintArea);
+
+ virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
+ DECL_LINK(FieldPopupModeEndHdl, weld::Popover&, void);
+
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual WindowHitTest ImplHitTest(const Point& rFramePos) override;
+
+ virtual void LaunchPopup();
+ virtual void DestroyPopup();
+
+private:
+ tools::Rectangle m_aFieldFramePixel;
+
+protected:
+ sw::mark::Fieldmark& m_rFieldmark;
+ std::unique_ptr<weld::Builder> m_xFieldPopupBuilder;
+ std::unique_ptr<weld::Popover> m_xFieldPopup;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/inc/GraphicSizeCheck.hxx b/sw/source/core/inc/GraphicSizeCheck.hxx
new file mode 100644
index 000000000..a70b1c756
--- /dev/null
+++ b/sw/source/core/inc/GraphicSizeCheck.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <doc.hxx>
+#include <svx/GenericCheckDialog.hxx>
+
+namespace sw
+{
+/**
+ * Class responsible to check if a graphic object violates the size
+ * constraints and store the results.
+ */
+class GraphicSizeViolation final
+{
+private:
+ const SwGrfNode* m_pGraphicNode;
+
+ sal_Int32 m_nLowDPILimit = 0;
+ sal_Int32 m_nHighDPILimit = 0;
+
+ sal_Int32 m_nDPIX = 0;
+ sal_Int32 m_nDPIY = 0;
+
+public:
+ GraphicSizeViolation(sal_Int32 nDPI, const SwGrfNode* pGraphicNode);
+ bool check();
+
+ const OUString& getGraphicName();
+
+ bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < m_nLowDPILimit; }
+
+ bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > m_nHighDPILimit; }
+
+ sal_Int32 getDPIX() { return m_nDPIX; }
+
+ sal_Int32 getDPIY() { return m_nDPIY; }
+};
+
+/**
+ * Run the graphic size checks for all the graphic objects in the DOM
+ * and store a list of violations.
+ */
+class GraphicSizeCheck final
+{
+private:
+ SwDoc* m_pDocument;
+ std::vector<std::unique_ptr<GraphicSizeViolation>> m_aGraphicSizeViolationList;
+
+public:
+ GraphicSizeCheck(SwDoc* pDocument)
+ : m_pDocument(pDocument)
+ {
+ }
+
+ void check();
+
+ std::vector<std::unique_ptr<GraphicSizeViolation>>& getViolationList()
+ {
+ return m_aGraphicSizeViolationList;
+ }
+};
+
+/** The UI part of the GraphicSizeViolation used by GenericCheckDialog */
+class GraphicSizeCheckGUIEntry : public svx::CheckData
+{
+private:
+ SwDoc* m_pDocument;
+ std::unique_ptr<GraphicSizeViolation> m_pViolation;
+
+public:
+ GraphicSizeCheckGUIEntry(SwDoc* pDocument, std::unique_ptr<GraphicSizeViolation>&& pViolation)
+ : m_pDocument(pDocument)
+ , m_pViolation(std::move(pViolation))
+ {
+ }
+
+ OUString getText() override;
+
+ bool canMarkObject() override { return true; }
+
+ void markObject() override;
+
+ bool hasProperties() override { return true; }
+
+ void runProperties() override;
+};
+
+/**
+ * The UI part presenting the graphic size check results, which is
+ * used by GenericCheckDialog
+ */
+class GraphicSizeCheckGUIResult : public svx::CheckDataCollection
+{
+public:
+ GraphicSizeCheckGUIResult(SwDoc* pDocument);
+
+ OUString getTitle() override;
+};
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/MarkManager.hxx b/sw/source/core/inc/MarkManager.hxx
new file mode 100644
index 000000000..0cf40191e
--- /dev/null
+++ b/sw/source/core/inc/MarkManager.hxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_MARKMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_MARKMANAGER_HXX
+
+#include <IMark.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <unordered_map>
+#include <memory>
+
+class SwCursorShell;
+class SfxViewShell;
+
+namespace sw::mark {
+ typedef std::unordered_map<OUString, sal_Int32> MarkBasenameMapUniqueOffset_t;
+
+ class FieldmarkWithDropDownButton;
+
+ class MarkManager final
+ : virtual public IDocumentMarkAccess
+ {
+ public:
+ MarkManager(/*[in/out]*/ SwDoc& rDoc);
+ // IDocumentMarkAccess
+ virtual ::sw::mark::IMark* makeMark(const SwPaM& rPaM,
+ const OUString& rName, IDocumentMarkAccess::MarkType eMark,
+ sw::mark::InsertMode eMode,
+ SwPosition const* pSepPos = nullptr) override;
+
+ virtual sw::mark::IFieldmark* makeFieldBookmark( const SwPaM& rPaM,
+ const OUString& rName,
+ const OUString& rType,
+ SwPosition const* pSepPos = nullptr) override;
+ virtual sw::mark::IFieldmark* makeNoTextFieldBookmark( const SwPaM& rPaM,
+ const OUString& rName,
+ const OUString& rType) override;
+
+ virtual ::sw::mark::IMark* getMarkForTextNode(const SwTextNode& rTextNode, IDocumentMarkAccess::MarkType eMark) override;
+
+ virtual sw::mark::IMark* makeAnnotationMark(
+ const SwPaM& rPaM,
+ const OUString& rName ) override;
+
+ virtual void repositionMark(::sw::mark::IMark* io_pMark, const SwPaM& rPaM) override;
+ virtual bool renameMark(::sw::mark::IMark* io_pMark, const OUString& rNewName) override;
+ virtual void correctMarksAbsolute(const SwNodeIndex& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset) override;
+ virtual void correctMarksRelative(const SwNodeIndex& rOldNode, const SwPosition& rNewPos, const sal_Int32 nOffset) override;
+
+ virtual void deleteMarks(const SwNodeIndex& rStt, const SwNodeIndex& rEnd, std::vector< ::sw::mark::SaveBookmark>* pSaveBkmk, const SwIndex* pSttIdx, const SwIndex* pEndIdx) override;
+
+ // deleters
+ virtual std::unique_ptr<ILazyDeleter>
+ deleteMark(const const_iterator_t& ppMark, bool isMoveNodes) override;
+ virtual void deleteMark(const ::sw::mark::IMark* const pMark) override;
+ virtual void clearAllMarks() override;
+
+ // marks
+ virtual const_iterator_t getAllMarksBegin() const override;
+ virtual const_iterator_t getAllMarksEnd() const override;
+ virtual sal_Int32 getAllMarksCount() const override;
+ virtual const_iterator_t findMark(const OUString& rName) const override;
+
+ // bookmarks
+ virtual bool isBookmarkDeleted(SwPaM const& rPaM) const override;
+ virtual const_iterator_t getBookmarksBegin() const override;
+ virtual const_iterator_t getBookmarksEnd() const override;
+ virtual sal_Int32 getBookmarksCount() const override;
+ virtual const_iterator_t findBookmark(const OUString& rName) const override;
+ virtual const_iterator_t findFirstBookmarkStartsAfter(const SwPosition& rPos) const override;
+
+ // Fieldmarks
+ virtual const_iterator_t getFieldmarksBegin() const override;
+ virtual const_iterator_t getFieldmarksEnd() const override;
+ virtual ::sw::mark::IFieldmark* getFieldmarkAt(const SwPosition& rPos) const override;
+ virtual ::sw::mark::IFieldmark* getFieldmarkFor(const SwPosition& rPos) const override;
+ virtual ::sw::mark::IFieldmark* getFieldmarkBefore(const SwPosition& rPos) const override;
+ virtual ::sw::mark::IFieldmark* getFieldmarkAfter(const SwPosition& rPos) const override;
+
+ virtual ::sw::mark::IFieldmark* getDropDownFor(const SwPosition &rPos) const override;
+ virtual std::vector<::sw::mark::IFieldmark*> getNoTextFieldmarksIn(const SwPaM &rPaM) const override;
+
+ virtual void deleteFieldmarkAt(const SwPosition& rPos) override;
+ virtual ::sw::mark::IFieldmark* changeFormFieldmarkType(::sw::mark::IFieldmark* pFieldmark, const OUString& rNewType) override;
+
+ virtual void NotifyCursorUpdate(const SwCursorShell& rCursorShell) override;
+ virtual void ClearFieldActivation() override;
+ void LOKUpdateActiveField(const SfxViewShell* pViewShell);
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ // Annotation Marks
+ virtual const_iterator_t getAnnotationMarksBegin() const override;
+ virtual const_iterator_t getAnnotationMarksEnd() const override;
+ virtual sal_Int32 getAnnotationMarksCount() const override;
+ virtual const_iterator_t findAnnotationMark( const OUString& rName ) const override;
+ virtual sw::mark::IMark* getAnnotationMarkFor(const SwPosition& rPos) const override;
+ virtual const_iterator_t findFirstAnnotationStartsAfter(const SwPosition& rPos) const override;
+
+ virtual void assureSortedMarkContainers() const override;
+
+ typedef std::vector<sw::mark::MarkBase*> container_t;
+
+ // helper bookmark to store annotation range of redlines
+ virtual ::sw::mark::IMark* makeAnnotationBookmark(const SwPaM& rPaM,
+ const OUString& rName, IDocumentMarkAccess::MarkType eMark,
+ sw::mark::InsertMode eMode,
+ SwPosition const* pSepPos = nullptr) override;
+ virtual const_iterator_t findAnnotationBookmark( const OUString& rName ) const override;
+ virtual void restoreAnnotationMarks(bool bDelete = true) override;
+
+ private:
+
+ MarkManager(MarkManager const&) = delete;
+ MarkManager& operator=(MarkManager const&) = delete;
+
+ // make names
+ OUString getUniqueMarkName(const OUString& rName) const;
+
+ void sortSubsetMarks();
+ void sortMarks();
+
+ // container for all marks, this container owns the objects it points to
+ container_t m_vAllMarks;
+
+ // additional container for bookmarks
+ container_t m_vBookmarks;
+ // additional container for fieldmarks
+ container_t m_vFieldmarks;
+
+ mutable MarkBasenameMapUniqueOffset_t m_aMarkBasenameMapUniqueOffset;
+
+ // container for annotation marks
+ container_t m_vAnnotationMarks;
+
+ SwDoc& m_rDoc;
+
+ sw::mark::FieldmarkWithDropDownButton* m_pLastActiveFieldmark;
+ };
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/ModelTraverser.hxx b/sw/source/core/inc/ModelTraverser.hxx
new file mode 100644
index 000000000..f3c6acb9c
--- /dev/null
+++ b/sw/source/core/inc/ModelTraverser.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <doc.hxx>
+
+class SwNode;
+class SdrObject;
+
+namespace sw
+{
+class SW_DLLPUBLIC ModelTraverseHandler
+{
+public:
+ virtual ~ModelTraverseHandler() {}
+
+ virtual void handleNode(SwNode* pNode) = 0;
+ virtual void handleSdrObject(SdrObject* pObject) = 0;
+};
+
+class ModelTraverser
+{
+private:
+ std::vector<std::shared_ptr<ModelTraverseHandler>> mpNodeHandler;
+ SwDoc* m_pDoc;
+
+public:
+ ModelTraverser(SwDoc* pDoc)
+ : m_pDoc(pDoc)
+ {
+ }
+
+ void traverse();
+
+ void addNodeHandler(std::shared_ptr<ModelTraverseHandler> pHandler)
+ {
+ mpNodeHandler.push_back(pHandler);
+ }
+};
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SearchResultLocator.hxx b/sw/source/core/inc/SearchResultLocator.hxx
new file mode 100644
index 000000000..f8c30b77d
--- /dev/null
+++ b/sw/source/core/inc/SearchResultLocator.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <swdllapi.h>
+#include <doc.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+namespace sw::search
+{
+enum class NodeType
+{
+ Undefined = 0,
+ WriterNode = 1,
+ CommonNode = 2 // node in a SdrObject
+};
+
+struct SearchIndexData
+{
+ NodeType meType = NodeType::Undefined;
+ SwNodeOffset mnNodeIndex{ 0 };
+ OUString maObjectName;
+
+ SearchIndexData() {}
+
+ SearchIndexData(NodeType eType, SwNodeOffset nNodeIndex,
+ OUString const& aObjectName = OUString())
+ : meType(eType)
+ , mnNodeIndex(nNodeIndex)
+ , maObjectName(aObjectName)
+ {
+ }
+};
+
+struct LocationResult
+{
+ bool mbFound = false;
+ std::vector<basegfx::B2DRange> maRectangles;
+};
+
+class SW_DLLPUBLIC SearchResultLocator
+{
+ SwDoc* mpDocument;
+
+ void findOne(LocationResult& rResult, SearchIndexData const& rSearchIndexData);
+ static bool tryParseJSON(const char* pPayload,
+ std::vector<sw::search::SearchIndexData>& rDataVector);
+ static bool tryParseXML(const char* pPayload,
+ std::vector<sw::search::SearchIndexData>& rDataVector);
+
+public:
+ SearchResultLocator(SwDoc* pDoc)
+ : mpDocument(pDoc)
+ {
+ }
+
+ LocationResult find(std::vector<SearchIndexData> const& rSearchIndexDataVector);
+ LocationResult findForPayload(const char* pPayload);
+};
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwGrammarMarkUp.hxx b/sw/source/core/inc/SwGrammarMarkUp.hxx
new file mode 100644
index 000000000..efefadf83
--- /dev/null
+++ b/sw/source/core/inc/SwGrammarMarkUp.hxx
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWGRAMMARMARKUP_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWGRAMMARMARKUP_HXX
+
+#include "wrong.hxx"
+#include <vector>
+
+/* SwGrammarMarkUp extends the functionality of a "normal" SwWrongList by memorizing
+ the start positions of sentences in the paragraph
+
+ The whole class is only a temporary solution without usage of virtual functions.
+ At the end the whole SwWrongList stuff should be reworked and replaced by interfaces
+ to deal with all the different wronglists like
+ spell, grammar, smarttag, sentence...
+ "MarkUpList" would be a better name than WrongList.
+*/
+
+class SwGrammarMarkUp final : public SwWrongList
+{
+ std::vector<sal_Int32> maSentence;
+
+public:
+ SwGrammarMarkUp()
+ : SwWrongList(WRONGLIST_GRAMMAR)
+ {
+ }
+
+ virtual ~SwGrammarMarkUp() override;
+ virtual SwWrongList* Clone() override;
+ virtual void CopyFrom(const SwWrongList& rCopy) override;
+
+ /* SwWrongList::Move() + handling of maSentence */
+ void MoveGrammar(sal_Int32 nPos, sal_Int32 nDiff);
+ /* SwWrongList::SplitList() + handling of maSentence */
+ std::unique_ptr<SwGrammarMarkUp> SplitGrammarList(sal_Int32 nSplitPos);
+ /* SwWrongList::JoinList() + handling of maSentence */
+ void JoinGrammarList(SwGrammarMarkUp* pNext, sal_Int32 nInsertPos);
+ /* SwWrongList::ClearList() + handling of maSentence */
+ void ClearGrammarList(sal_Int32 nSentenceEnd = COMPLETE_STRING);
+ /* setSentence to define the start position of a sentence,
+ at the moment the end position is given by the next start position */
+ void setSentence(sal_Int32 nStart);
+ /* getSentenceStart returns the last start position of a sentence
+ which is lower or equal to the given parameter */
+ sal_Int32 getSentenceStart(sal_Int32 nPos);
+ /* getSentenceEnd returns the first start position of a sentence
+ which is greater than the given parameter */
+ sal_Int32 getSentenceEnd(sal_Int32 nPos);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwPortionHandler.hxx b/sw/source/core/inc/SwPortionHandler.hxx
new file mode 100644
index 000000000..c183781d8
--- /dev/null
+++ b/sw/source/core/inc/SwPortionHandler.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWPORTIONHANDLER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWPORTIONHANDLER_HXX
+
+#include <swdllapi.h>
+#include <rtl/ustring.hxx>
+#include "TextFrameIndex.hxx"
+
+class SwFont;
+enum class PortionType;
+
+/** The SwPortionHandler interface implements a visitor for the layout
+ * engine's text portions. This can be used to gather information of
+ * the on-screen representation of a single paragraph.
+ *
+ * For each text portion, one of the methods text(...) or special(...)
+ * is called, depending on whether it is a 'normal' run of text, or
+ * any other portion. Additionally, the linebreak() method is called
+ * once at the end of every on-screen line.
+ *
+ * All parameters relate to the 'view string', which is the text string
+ * held by the sequence of all corresponding SwTextFrames.
+ *
+ * The SwPortionHandler can be used with the
+ * SwTextFrame::VisitPortions(...) method.
+ */
+class SW_DLLPUBLIC SwPortionHandler
+{
+public:
+
+ virtual ~SwPortionHandler() {} /// (empty) destructor
+
+ /** text portion. A run of nLength characters from the view
+ * string, that contains no special characters like embedded
+ * fields, etc. Thus, the on-screen text of this portion
+ * corresponds exactly to the corresponding characters in the
+ * view string.
+ */
+ virtual void Text(
+ TextFrameIndex nLength, ///< length of this portion in the view string
+ PortionType nType, /// type of this portion
+ sal_Int32 nHeight = 0, /// height of this portion
+ sal_Int32 nWidth = 0 /// width of this portion
+ ) = 0;
+
+ /** special portion. This method is called for every non-text
+ * portion. The parameters describe the length of the
+ * corresponding characters in the view string (often 0 or 1),
+ * the text which is displayed, and the type of the portion.
+ */
+ virtual void Special(
+ TextFrameIndex nLength, ///< length of this portion in the view string
+ const OUString& rText, /// text which is painted on-screen
+ PortionType nType, /// type of this portion
+ sal_Int32 nHeight = 0, /// font height of the painted text
+ sal_Int32 nWidth = 0, /// width of this portion
+ const SwFont* pFont = nullptr /// font of this portion
+ ) = 0;
+
+ /** line break. This method is called whenever a line break in the
+ * layout occurs.
+ */
+ virtual void LineBreak(sal_Int32 nWidth) = 0;
+
+ /** skip characters. The SwTextFrame may only display partially
+ * display a certain paragraph (e.g. when the paragraph is split
+ * across multiple pages). In this case, the Skip() method must be
+ * called to inform the portion handler to ignore a certain run of
+ * characters in the 'view string'. Skip(), if used at all, must
+ * be called before any of the other methods is called. Calling
+ * Skip() between portions is not allowed.
+ */
+ virtual void Skip(
+ TextFrameIndex nLength /// number of 'view string' characters to be skipped
+ ) = 0;
+
+ /** end of paragraph. This method is to be called when all the
+ * paragraph's portions have been processed.
+ */
+ virtual void Finish() = 0;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwUndoFmt.hxx b/sw/source/core/inc/SwUndoFmt.hxx
new file mode 100644
index 000000000..499a6fcd6
--- /dev/null
+++ b/sw/source/core/inc/SwUndoFmt.hxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWUNDOFMT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWUNDOFMT_HXX
+
+#include <undobj.hxx>
+#include <swundo.hxx>
+#include <numrule.hxx>
+#include <memory>
+
+class SwDoc;
+class SwTextFormatColl;
+class SwConditionTextFormatColl;
+class SwRewriter;
+
+class SwUndoFormatCreate : public SwUndo
+{
+protected:
+ SwFormat * m_pNew;
+ OUString m_sDerivedFrom;
+ SwDoc& m_rDoc;
+ mutable OUString m_sNewName;
+ std::unique_ptr<SfxItemSet> m_pNewSet;
+ sal_uInt16 m_nId; // FormatId related
+ bool m_bAuto;
+
+public:
+ SwUndoFormatCreate(SwUndoId nUndoId, SwFormat * pNew, SwFormat const * pDerivedFrom,
+ SwDoc& rDoc);
+ virtual ~SwUndoFormatCreate() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) = 0;
+ virtual void Delete() = 0;
+ virtual SwFormat * Find(const OUString & rName) const = 0;
+};
+
+class SwUndoFormatDelete : public SwUndo
+{
+protected:
+ OUString m_sDerivedFrom;
+ SwDoc& m_rDoc;
+ OUString m_sOldName;
+ SfxItemSet m_aOldSet;
+ sal_uInt16 m_nId; // FormatId related
+ bool m_bAuto;
+
+public:
+ SwUndoFormatDelete(SwUndoId nUndoId, SwFormat const * pOld, SwDoc& rDoc);
+ virtual ~SwUndoFormatDelete() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) = 0;
+ virtual void Delete(SwFormat * pFormat) = 0;
+ virtual SwFormat * Find(const OUString & rName) const = 0;
+};
+
+class SwUndoRenameFormat : public SwUndo
+{
+protected:
+ OUString m_sOldName, m_sNewName;
+ SwDoc& m_rDoc;
+
+public:
+ SwUndoRenameFormat(SwUndoId nUndoId, const OUString & sOldName,
+ const OUString & sNewName,
+ SwDoc& rDoc);
+ virtual ~SwUndoRenameFormat() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ SwRewriter GetRewriter() const override;
+
+ virtual SwFormat * Find(const OUString & rName) const = 0;
+};
+
+class SwUndoTextFormatCollCreate : public SwUndoFormatCreate
+{
+public:
+ SwUndoTextFormatCollCreate(SwTextFormatColl * pNew, SwTextFormatColl const * pDerivedFrom,
+ SwDoc& rDoc);
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+ virtual void Delete() override;
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoTextFormatCollDelete : public SwUndoFormatDelete
+{
+public:
+ SwUndoTextFormatCollDelete(SwTextFormatColl const * pOld, SwDoc& rDoc);
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+ virtual void Delete(SwFormat * pFormat) override;
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoCondTextFormatCollCreate final : public SwUndoTextFormatCollCreate
+{
+public:
+ SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl * pNew, SwTextFormatColl const * pDerivedFrom, SwDoc& rDoc);
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+};
+
+class SwUndoCondTextFormatCollDelete final : public SwUndoTextFormatCollDelete
+{
+public:
+ SwUndoCondTextFormatCollDelete(SwTextFormatColl const * pOld, SwDoc& rDoc);
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+};
+
+class SwUndoRenameFormatColl final : public SwUndoRenameFormat
+{
+public:
+ SwUndoRenameFormatColl(const OUString & sOldName,
+ const OUString & sNewName,
+ SwDoc& rDoc);
+
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoCharFormatCreate final : public SwUndoFormatCreate
+{
+public:
+ SwUndoCharFormatCreate(SwCharFormat * pNew, SwCharFormat const * pDerivedFrom,
+ SwDoc& rDoc);
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+ virtual void Delete() override;
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoCharFormatDelete final : public SwUndoFormatDelete
+{
+public:
+ SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc& rDoc);
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+ virtual void Delete(SwFormat * pFormat) override;
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoRenameCharFormat final : public SwUndoRenameFormat
+{
+public:
+ SwUndoRenameCharFormat(const OUString & sOldName,
+ const OUString & sNewName,
+ SwDoc& rDoc);
+
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoFrameFormatCreate final : public SwUndoFormatCreate
+{
+public:
+ SwUndoFrameFormatCreate(SwFrameFormat * pNew, SwFrameFormat const * pDerivedFrom,
+ SwDoc& rDoc);
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+ virtual void Delete() override;
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoFrameFormatDelete final : public SwUndoFormatDelete
+{
+public:
+ SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc& rDoc);
+
+ virtual SwFormat * Create(SwFormat * pDerivedFrom) override;
+ virtual void Delete(SwFormat * pFormat) override;
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoRenameFrameFormat final : public SwUndoRenameFormat
+{
+public:
+ SwUndoRenameFrameFormat(const OUString & sOldName,
+ const OUString & sNewName,
+ SwDoc& rDoc);
+
+ virtual SwFormat * Find(const OUString & rName) const override;
+};
+
+class SwUndoNumruleCreate final : public SwUndo
+{
+ const SwNumRule * m_pNew;
+ mutable SwNumRule m_aNew;
+ SwDoc& m_rDoc;
+ mutable bool m_bInitialized;
+
+public:
+ SwUndoNumruleCreate(const SwNumRule * pNew, SwDoc& rDoc);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ SwRewriter GetRewriter() const override;
+};
+
+class SwUndoNumruleDelete final : public SwUndo
+{
+ SwNumRule m_aOld;
+ SwDoc& m_rDoc;
+
+public:
+ SwUndoNumruleDelete(const SwNumRule& rRule, SwDoc& rDoc);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ SwRewriter GetRewriter() const override;
+};
+
+class SwUndoNumruleRename final : public SwUndo
+{
+ OUString m_aOldName, m_aNewName;
+ SwDoc& m_rDoc;
+
+ public:
+ SwUndoNumruleRename(const OUString & aOldName, const OUString & aNewName,
+ SwDoc& rDoc);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ SwRewriter GetRewriter() const override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_SWUNDOFMT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwUndoPageDesc.hxx b/sw/source/core/inc/SwUndoPageDesc.hxx
new file mode 100644
index 000000000..ab90a3a65
--- /dev/null
+++ b/sw/source/core/inc/SwUndoPageDesc.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWUNDOPAGEDESC_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWUNDOPAGEDESC_HXX
+
+#include <undobj.hxx>
+#include <pagedesc.hxx>
+
+class SwDoc;
+
+class SwUndoPageDesc final : public SwUndo
+{
+ SwPageDescExt m_aOld, m_aNew;
+ SwDoc * m_pDoc;
+ bool m_bExchange;
+
+ // To avoid duplication of (header/footer)content nodes for simple page desc changes
+ void ExchangeContentNodes( SwPageDesc& rSource, SwPageDesc &rDest );
+
+ // tdf#153220 use to Exit HeaderFooter EditMode
+ void ExitHeaderFooterEdit();
+public:
+ SwUndoPageDesc(const SwPageDesc & aOld, const SwPageDesc & aNew,
+ SwDoc * pDoc);
+ virtual ~SwUndoPageDesc() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoPageDescCreate final : public SwUndo
+{
+ const SwPageDesc * m_pDesc;
+ SwPageDescExt m_aNew;
+ SwDoc * m_pDoc;
+
+ void DoImpl();
+
+public:
+ SwUndoPageDescCreate(const SwPageDesc * pNew, SwDoc * pDoc);
+ virtual ~SwUndoPageDescCreate() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoPageDescDelete final : public SwUndo
+{
+ SwPageDescExt m_aOld;
+ SwDoc * m_pDoc;
+
+ void DoImpl();
+
+public:
+ SwUndoPageDescDelete(const SwPageDesc & aOld, SwDoc * pDoc);
+ virtual ~SwUndoPageDescDelete() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+#endif // _SW_UNDO_PAGE_DESC_CHANGE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwUndoTOXChange.hxx b/sw/source/core/inc/SwUndoTOXChange.hxx
new file mode 100644
index 000000000..a7aabf41e
--- /dev/null
+++ b/sw/source/core/inc/SwUndoTOXChange.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <undobj.hxx>
+#include <tox.hxx>
+
+class SwDoc;
+class SwTOXBaseSection;
+
+class SwUndoTOXChange final : public SwUndo
+{
+private:
+ SwTOXBase m_Old;
+ SwTOXBase m_New;
+
+ SwNodeOffset const m_nNodeIndex;
+
+public:
+ SwUndoTOXChange(const SwDoc& rDoc, SwTOXBaseSection const& rTOX, const SwTOXBase& rNew);
+ virtual ~SwUndoTOXChange() override;
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+ virtual void RepeatImpl(::sw::RepeatContext&) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwXMLBlockExport.hxx b/sw/source/core/inc/SwXMLBlockExport.hxx
new file mode 100644
index 000000000..90f8c5959
--- /dev/null
+++ b/sw/source/core/inc/SwXMLBlockExport.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <xmloff/xmlexp.hxx>
+
+class SwXMLTextBlocks;
+
+class SwXMLBlockListExport final : public SvXMLExport
+{
+private:
+ SwXMLTextBlocks &m_rBlockList;
+
+public:
+ SwXMLBlockListExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ SwXMLTextBlocks & rBlocks,
+ const OUString &rFileName,
+ css::uno::Reference< css::xml::sax::XDocumentHandler> const &rHandler);
+
+ ErrCode exportDoc( enum ::xmloff::token::XMLTokenEnum eClass = ::xmloff::token::XML_TOKEN_INVALID ) override;
+ void ExportAutoStyles_() override {}
+ void ExportMasterStyles_ () override {}
+ void ExportContent_() override {}
+};
+
+class SwXMLTextBlockExport final : public SvXMLExport
+{
+private:
+ SwXMLTextBlocks &m_rBlockList;
+
+public:
+ SwXMLTextBlockExport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ SwXMLTextBlocks & rBlocks,
+ const OUString &rFileName,
+ css::uno::Reference< css::xml::sax::XDocumentHandler> const &rHandler);
+
+ ErrCode exportDoc(enum ::xmloff::token::XMLTokenEnum /*eClass*/) override { return ERRCODE_NONE; }
+ void exportDoc(std::u16string_view rText);
+ void ExportAutoStyles_() override {}
+ void ExportMasterStyles_ () override {}
+ void ExportContent_() override {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwXMLBlockImport.hxx b/sw/source/core/inc/SwXMLBlockImport.hxx
new file mode 100644
index 000000000..821033c1e
--- /dev/null
+++ b/sw/source/core/inc/SwXMLBlockImport.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <xmloff/xmlimp.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <com/sun/star/xml/sax/FastToken.hpp>
+#include <sax/fastattribs.hxx>
+
+namespace com::sun::star::xml::sax { class XFastTokenHandler; }
+
+using namespace css::xml::sax;
+using namespace xmloff::token;
+
+class SwXMLTextBlocks;
+class SwXMLBlockListImport final : public SvXMLImport
+{
+private:
+ SwXMLTextBlocks &m_rBlockList;
+
+ // This method is called after the namespace map has been updated, but
+ // before a context for the current element has been pushed.
+ virtual SvXMLImportContext* CreateFastContext( sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+public:
+ SwXMLBlockListImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ SwXMLTextBlocks &rBlocks );
+
+ SwXMLTextBlocks& getBlockList()
+ {
+ return m_rBlockList;
+ }
+ virtual ~SwXMLBlockListImport()
+ noexcept override;
+};
+
+class SwXMLTextBlockImport final : public SvXMLImport
+{
+ // This method is called after the namespace map has been updated, but
+ // before a context for the current element has been pushed.
+ virtual SvXMLImportContext* CreateFastContext( sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+
+public:
+ bool m_bTextOnly;
+ OUString &m_rText;
+ SwXMLTextBlockImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ OUString &rNewText, bool bNewTextOnly );
+
+ virtual ~SwXMLTextBlockImport()
+ noexcept override;
+ virtual void SAL_CALL endDocument() override;
+};
+
+enum SwXMLTextBlockToken : sal_Int32
+{
+ OFFICE_BODY = FastToken::NAMESPACE | XML_NAMESPACE_OFFICE | XML_BODY,
+ OFFICE_TEXT = FastToken::NAMESPACE | XML_NAMESPACE_OFFICE | XML_TEXT,
+ OFFICE_DOCUMENT = FastToken::NAMESPACE | XML_NAMESPACE_OFFICE | XML_DOCUMENT,
+ OFFICE_DOCUMENT_CONTENT = FastToken::NAMESPACE | XML_NAMESPACE_OFFICE | XML_DOCUMENT_CONTENT,
+ TEXT_P = FastToken::NAMESPACE | XML_NAMESPACE_TEXT | XML_P
+};
+
+class SwXMLTextBlockTokenHandler final :
+ public sax_fastparser::FastTokenHandlerBase
+{
+public:
+ SwXMLTextBlockTokenHandler();
+ virtual ~SwXMLTextBlockTokenHandler() override;
+
+ //XFastTokenHandler
+ sal_Int32 SAL_CALL getTokenFromUTF8( const css::uno::Sequence< sal_Int8 >& Identifier ) override;
+ css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( sal_Int32 Token ) override;
+
+ //Much fast direct C++ shortcut to the method that matters
+ virtual sal_Int32 getTokenDirect( const char *pTag, sal_Int32 nLength ) const override;
+};
+
+enum SwXMLBlockListToken : sal_Int32
+{
+ ABBREVIATED_NAME = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_ABBREVIATED_NAME,
+ BLOCK = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_BLOCK,
+ BLOCK_LIST = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_BLOCK_LIST,
+ LIST_NAME = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_LIST_NAME,
+ NAME = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_NAME,
+ PACKAGE_NAME = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_PACKAGE_NAME,
+ UNFORMATTED_TEXT = FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST | XML_UNFORMATTED_TEXT
+};
+
+class SwXMLBlockListTokenHandler final :
+ public sax_fastparser::FastTokenHandlerBase
+{
+public:
+ SwXMLBlockListTokenHandler();
+ virtual ~SwXMLBlockListTokenHandler() override;
+
+ //XFastTokenHandler
+ sal_Int32 SAL_CALL getTokenFromUTF8( const css::uno::Sequence< sal_Int8 >& Identifier ) override;
+ css::uno::Sequence< sal_Int8 > SAL_CALL getUTF8Identifier( sal_Int32 Token ) override;
+
+ //Much fast direct C++ shortcut to the method that matters
+ virtual sal_Int32 getTokenDirect( const char *pTag, sal_Int32 nLength ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwXMLTextBlocks.hxx b/sw/source/core/inc/SwXMLTextBlocks.hxx
new file mode 100644
index 000000000..9132b5993
--- /dev/null
+++ b/sw/source/core/inc/SwXMLTextBlocks.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <sfx2/objsh.hxx>
+#include "swblocks.hxx"
+#include <o3tl/typed_flags_set.hxx>
+
+class SwDoc;
+class SvxMacroTableDtor;
+
+enum class SwXmlFlags {
+ NONE = 0x0000,
+ NoRootCommit = 0x0002,
+};
+namespace o3tl {
+ template<> struct typed_flags<SwXmlFlags> : is_typed_flags<SwXmlFlags, 0x0002> {};
+}
+
+class SwXMLTextBlocks final : public SwImpBlocks
+{
+ SfxObjectShellRef m_xDocShellRef;
+ SwXmlFlags m_nFlags;
+ OUString m_aPackageName;
+ tools::SvRef<SfxMedium> m_xMedium;
+
+ void ReadInfo();
+ void WriteInfo();
+ void InitBlockMode ( const css::uno::Reference < css::embed::XStorage >& rStorage );
+ void ResetBlockMode();
+
+public:
+ css::uno::Reference < css::embed::XStorage > m_xBlkRoot;
+ css::uno::Reference < css::embed::XStorage > m_xRoot;
+ SwXMLTextBlocks( const OUString& rFile );
+ SwXMLTextBlocks( const css::uno::Reference < css::embed::XStorage >&, const OUString& rFile );
+ void AddName( const OUString&, const OUString&, const OUString&, bool bOnlyText );
+ virtual void AddName( const OUString&, const OUString&, bool bOnlyText = false ) override;
+ static OUString GeneratePackageName ( std::u16string_view rShort );
+ virtual ~SwXMLTextBlocks() override;
+ virtual ErrCode Delete( sal_uInt16 ) override;
+ virtual ErrCode Rename( sal_uInt16, const OUString& ) override;
+ virtual ErrCode CopyBlock( SwImpBlocks& rImp, OUString& rShort, const OUString& rLong) override;
+ virtual void ClearDoc() override;
+ virtual ErrCode GetDoc( sal_uInt16 ) override;
+ virtual ErrCode BeginPutDoc( const OUString&, const OUString& ) override;
+ virtual ErrCode PutDoc() override;
+ virtual ErrCode PutText( const OUString&, const OUString&, const OUString& ) override;
+ virtual ErrCode MakeBlockList() override;
+
+ virtual ErrCode OpenFile( bool bReadOnly = true ) override;
+ virtual void CloseFile() override;
+
+ static bool IsFileUCBStorage( const OUString & rFileName);
+
+ // Methods for the new Autocorrecter
+ ErrCode GetText( std::u16string_view rShort, OUString& );
+
+ virtual bool IsOnlyTextBlock( const OUString& rShort ) const override;
+ bool IsOnlyTextBlock( sal_uInt16 nIdx ) const;
+ void SetIsTextOnly( const OUString& rShort, bool bNewValue );
+
+ virtual ErrCode GetMacroTable( sal_uInt16, SvxMacroTableDtor& rMacroTable ) override;
+ virtual ErrCode SetMacroTable( sal_uInt16 nIdx,
+ const SvxMacroTableDtor& rMacroTable ) override;
+ virtual bool PutMuchEntries( bool bOn ) override;
+
+ SwDoc* GetDoc() const { return m_xDoc.get(); }
+ //void SetDoc( SwDoc * pNewDoc);
+ ErrCode StartPutBlock( const OUString& rShort, const OUString& rPackageName );
+ ErrCode PutBlock();
+ ErrCode GetBlockText( std::u16string_view rShort, OUString& rText );
+ ErrCode PutBlockText( const OUString& rShort, std::u16string_view rText, const OUString& rPackageName );
+ void MakeBlockText( std::u16string_view rText );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/SwXTextDefaults.hxx b/sw/source/core/inc/SwXTextDefaults.hxx
new file mode 100644
index 000000000..0acab2a7d
--- /dev/null
+++ b/sw/source/core/inc/SwXTextDefaults.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+class SwDoc;
+class SfxItemPropertySet;
+
+class SwXTextDefaults final : public cppu::WeakImplHelper
+ <
+ css::beans::XPropertyState,
+ css::beans::XPropertySet,
+ css::lang::XServiceInfo
+ >
+{
+ const SfxItemPropertySet* m_pPropSet;
+ SwDoc * m_pDoc;
+
+public:
+ SwXTextDefaults ( SwDoc * pNewDoc );
+ virtual ~SwXTextDefaults () override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& rPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& rPropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& rPropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ // XPropertyState
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& rPropertyName ) override;
+ virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( const css::uno::Sequence< OUString >& rPropertyNames ) override;
+ virtual void SAL_CALL setPropertyToDefault( const OUString& rPropertyName ) override;
+ virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& rPropertyName ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/TextFrameIndex.hxx b/sw/source/core/inc/TextFrameIndex.hxx
new file mode 100644
index 000000000..3987bd590
--- /dev/null
+++ b/sw/source/core/inc/TextFrameIndex.hxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TEXTFRAMEINDEX_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TEXTFRAMEINDEX_HXX
+
+#include <sal/types.h>
+#include <o3tl/strong_int.hxx>
+
+/**
+ * Denotes a character index in a text frame at a layout level, after extent
+ * mapping from a text node at a document model level.
+ *
+ * @see SwTextFrame::MapViewToModelPos().
+ */
+typedef o3tl::strong_int<sal_Int32, struct Tag_TextFrameIndex> TextFrameIndex;
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_TEXTFRAMEINDEX_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/inc/UndoAttribute.hxx b/sw/source/core/inc/UndoAttribute.hxx
new file mode 100644
index 000000000..ffe4c4f32
--- /dev/null
+++ b/sw/source/core/inc/UndoAttribute.hxx
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOATTRIBUTE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOATTRIBUTE_HXX
+
+#include <undobj.hxx>
+#include <memory>
+#include <rtl/ustring.hxx>
+#include <svl/itemset.hxx>
+#include <swtypes.hxx>
+#include <calbck.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+class SvxTabStopItem;
+class SwFormat;
+class SwFootnoteInfo;
+class SwEndNoteInfo;
+class SwDoc;
+
+class SwUndoAttr final : public SwUndo, private SwUndRng
+{
+ SfxItemSet m_AttrSet; // attributes for Redo
+ const std::unique_ptr<SwHistory> m_pHistory; // History for Undo
+ std::unique_ptr<SwRedlineData> m_pRedlineData; // Redlining
+ std::unique_ptr<SwRedlineSaveDatas> m_pRedlineSaveData;
+ SwNodeOffset m_nNodeIndex; // Offset: for Redlining
+ const SetAttrMode m_nInsertFlags; // insert flags
+ OUString m_aChrFormatName;
+
+ void RemoveIdx( SwDoc& rDoc );
+
+public:
+ SwUndoAttr( const SwPaM&, const SfxItemSet &, const SetAttrMode nFlags );
+ SwUndoAttr( const SwPaM&, const SfxPoolItem&, const SetAttrMode nFlags );
+
+ virtual ~SwUndoAttr() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SaveRedlineData( const SwPaM& rPam, bool bInsContent );
+
+ SwHistory& GetHistory() { return *m_pHistory; }
+};
+
+class SwUndoResetAttr final : public SwUndo, private SwUndRng
+{
+ const std::unique_ptr<SwHistory> m_pHistory;
+ o3tl::sorted_vector<sal_uInt16> m_Ids;
+ const sal_uInt16 m_nFormatId; // Format-Id for Redo
+
+public:
+ SwUndoResetAttr( const SwPaM&, sal_uInt16 nFormatId );
+ SwUndoResetAttr( const SwPosition&, sal_uInt16 nFormatId );
+
+ virtual ~SwUndoResetAttr() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetAttrs( o3tl::sorted_vector<sal_uInt16> && rAttrs );
+
+ SwHistory& GetHistory() { return *m_pHistory; }
+};
+
+class SwUndoFormatAttr final : public SwUndo
+{
+ friend class SwUndoDefaultAttr;
+ OUString m_sFormatName;
+ std::optional<SfxItemSet> m_oOldSet; // old attributes
+ SwNodeOffset m_nNodeIndex;
+ const sal_uInt16 m_nFormatWhich;
+ const bool m_bSaveDrawPt;
+
+ void SaveFlyAnchor( const SwFormat * pFormat, bool bSaveDrawPt = false );
+ // #i35443# - Add return value, type <bool>.
+ // Return value indicates, if anchor attribute is restored.
+ // Notes: - If anchor attribute is restored, all other existing attributes
+ // are also restored.
+ // - Anchor attribute isn't restored successfully, if it contains
+ // an invalid anchor position and all other existing attributes
+ // aren't restored.
+ // This situation occurs for undo of styles.
+ bool RestoreFlyAnchor(::sw::UndoRedoContext & rContext);
+ // --> OD 2008-02-27 #refactorlists# - removed <rAffectedItemSet>
+ void Init( const SwFormat & rFormat );
+
+public:
+ // register at the Format and save old attributes
+ // --> OD 2008-02-27 #refactorlists# - removed <rNewSet>
+ SwUndoFormatAttr( SfxItemSet&& rOldSet,
+ SwFormat& rFormat,
+ bool bSaveDrawPt );
+ SwUndoFormatAttr( const SfxPoolItem& rItem,
+ SwFormat& rFormat,
+ bool bSaveDrawPt );
+
+ virtual ~SwUndoFormatAttr() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+
+ void PutAttr( const SfxPoolItem& rItem, const SwDoc& rDoc );
+ SwFormat* GetFormat( const SwDoc& rDoc ); // checks if it is still in the Doc!
+};
+
+// --> OD 2008-02-12 #newlistlevelattrs#
+class SwUndoFormatResetAttr final : public SwUndo
+{
+ public:
+ SwUndoFormatResetAttr( SwFormat& rChangedFormat,
+ const sal_uInt16 nWhichId );
+ virtual ~SwUndoFormatResetAttr() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ private:
+ // format at which a certain attribute is reset.
+ SwFormat * const m_pChangedFormat;
+ // which ID of the reset attribute
+ const sal_uInt16 m_nWhichId;
+ // old attribute which has been reset - needed for undo.
+ std::unique_ptr<SfxPoolItem> m_pOldItem;
+};
+
+class SwUndoDontExpandFormat final : public SwUndo
+{
+ const SwNodeOffset m_nNodeIndex;
+ const sal_Int32 m_nContentIndex;
+
+public:
+ SwUndoDontExpandFormat( const SwPosition& rPos );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+// helper class to receive changed attribute sets
+class SwUndoFormatAttrHelper final : public SwClient
+{
+ SwFormat& m_rFormat;
+ std::unique_ptr<SwUndoFormatAttr> m_pUndo;
+ const bool m_bSaveDrawPt;
+
+public:
+ SwUndoFormatAttrHelper(SwFormat& rFormat, bool bSaveDrawPt = true);
+
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+ SwUndoFormatAttr* GetUndo() const { return m_pUndo.get(); }
+ // release the undo object (so it is not deleted here), and return it
+ std::unique_ptr<SwUndoFormatAttr> ReleaseUndo() { return std::move(m_pUndo); }
+};
+
+class SwUndoMoveLeftMargin final : public SwUndo, private SwUndRng
+{
+ const std::unique_ptr<SwHistory> m_pHistory;
+ const bool m_bModulus;
+
+public:
+ SwUndoMoveLeftMargin( const SwPaM&, bool bRight, bool bModulus );
+
+ virtual ~SwUndoMoveLeftMargin() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ SwHistory& GetHistory() { return *m_pHistory; }
+
+};
+
+class SwUndoDefaultAttr final : public SwUndo
+{
+ std::optional<SfxItemSet> m_oOldSet; // the old attributes
+ std::unique_ptr<SvxTabStopItem> m_pTabStop;
+
+public:
+ // registers at the format and saves old attributes
+ SwUndoDefaultAttr( const SfxItemSet& rOldSet, const SwDoc& rDoc );
+
+ virtual ~SwUndoDefaultAttr() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+class SwUndoChangeFootNote final : public SwUndo, private SwUndRng
+{
+ const std::unique_ptr<SwHistory> m_pHistory;
+ const OUString m_Text;
+ const bool m_bEndNote;
+
+public:
+ SwUndoChangeFootNote( const SwPaM& rRange, const OUString& rText,
+ bool bIsEndNote );
+ virtual ~SwUndoChangeFootNote() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ SwHistory& GetHistory() { return *m_pHistory; }
+};
+
+class SwUndoFootNoteInfo final : public SwUndo
+{
+ std::unique_ptr<SwFootnoteInfo> m_pFootNoteInfo;
+
+public:
+ SwUndoFootNoteInfo( const SwFootnoteInfo &rInfo, const SwDoc& rDoc );
+
+ virtual ~SwUndoFootNoteInfo() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+class SwUndoEndNoteInfo final : public SwUndo
+{
+ std::unique_ptr<SwEndNoteInfo> m_pEndNoteInfo;
+
+public:
+ SwUndoEndNoteInfo( const SwEndNoteInfo &rInfo, const SwDoc& rDoc );
+
+ virtual ~SwUndoEndNoteInfo() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOATTRIBUTE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoBookmark.hxx b/sw/source/core/inc/UndoBookmark.hxx
new file mode 100644
index 000000000..dc57ad300
--- /dev/null
+++ b/sw/source/core/inc/UndoBookmark.hxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOBOOKMARK_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOBOOKMARK_HXX
+
+#include <memory>
+#include <undobj.hxx>
+#include "rolbck.hxx"
+
+class SwHistoryBookmark;
+class SwHistoryNoTextFieldmark;
+class SwHistoryTextFieldmark;
+
+namespace sw::mark
+{
+class IMark;
+class IFieldmark;
+}
+
+class SwDoc;
+
+class SwUndoBookmark : public SwUndo
+{
+ const std::unique_ptr<SwHistoryBookmark> m_pHistoryBookmark;
+
+protected:
+ SwUndoBookmark(SwUndoId nUndoId, const ::sw::mark::IMark&);
+
+ void SetInDoc(SwDoc*);
+ void ResetInDoc(SwDoc&);
+
+public:
+ virtual ~SwUndoBookmark() override;
+
+ /**
+ Returns the rewriter for this undo object.
+
+ The rewriter contains the following rule:
+
+ $1 -> <name of bookmark>
+
+ <name of bookmark> is the name of the bookmark whose
+ insertion/deletion is recorded by this undo object.
+
+ @return the rewriter for this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoInsBookmark final : public SwUndoBookmark
+{
+public:
+ SwUndoInsBookmark(const ::sw::mark::IMark&);
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+class SwUndoDeleteBookmark final : public SwUndoBookmark
+{
+public:
+ SwUndoDeleteBookmark(const ::sw::mark::IMark&);
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+class SwUndoRenameBookmark final : public SwUndo
+{
+ const OUString m_sOldName;
+ const OUString m_sNewName;
+
+public:
+ SwUndoRenameBookmark(const OUString& rOldName, const OUString& rNewName, const SwDoc& rDoc);
+ virtual ~SwUndoRenameBookmark() override;
+
+private:
+ virtual SwRewriter GetRewriter() const override;
+ static void Rename(::sw::UndoRedoContext const&, const OUString& sFrom, const OUString& sTo);
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+/// Handling undo / redo of checkbox and drop-down form field insertion
+class SwUndoInsNoTextFieldmark final : public SwUndo
+{
+private:
+ const std::unique_ptr<SwHistoryNoTextFieldmark> m_pHistoryNoTextFieldmark;
+
+public:
+ SwUndoInsNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark);
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+/// Handling undo / redo of checkbox and drop-down form field deletion
+class SwUndoDelNoTextFieldmark final : public SwUndo
+{
+private:
+ const std::unique_ptr<SwHistoryNoTextFieldmark> m_pHistoryNoTextFieldmark;
+
+public:
+ SwUndoDelNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark);
+ ~SwUndoDelNoTextFieldmark();
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+/// Handling undo / redo of text form field insertion
+class SwUndoInsTextFieldmark final : public SwUndo
+{
+private:
+ const std::unique_ptr<SwHistoryTextFieldmark> m_pHistoryTextFieldmark;
+
+public:
+ SwUndoInsTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark);
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+/// Handling undo / redo of text form field deletion
+class SwUndoDelTextFieldmark final : public SwUndo
+{
+private:
+ const std::unique_ptr<SwHistoryTextFieldmark> m_pHistoryTextFieldmark;
+
+public:
+ SwUndoDelTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark);
+ ~SwUndoDelTextFieldmark();
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOBOOKMARK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoCore.hxx b/sw/source/core/inc/UndoCore.hxx
new file mode 100644
index 000000000..c1c66b702
--- /dev/null
+++ b/sw/source/core/inc/UndoCore.hxx
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOCORE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOCORE_HXX
+
+#include <undobj.hxx>
+#include <calbck.hxx>
+#include <rtl/ustring.hxx>
+#include <redline.hxx>
+#include <numrule.hxx>
+
+#include <memory>
+#include <vector>
+
+class SfxItemSet;
+class SwFormatColl;
+class SwFormatAnchor;
+class SdrMarkList;
+class SwUndoDelete;
+
+namespace sw {
+ class UndoManager;
+ class IShellCursorSupplier;
+}
+
+class SwRedlineSaveData final : public SwUndRng, public SwRedlineData, private SwUndoSaveSection
+{
+public:
+ SwRedlineSaveData(
+ SwComparePosition eCmpPos,
+ const SwPosition& rSttPos,
+ const SwPosition& rEndPos,
+ SwRangeRedline& rRedl,
+ bool bCopyNext );
+
+ ~SwRedlineSaveData();
+
+ void RedlineToDoc( SwPaM const & rPam );
+
+ SwNodeIndex* GetMvSttIdx() const
+ {
+ return SwUndoSaveSection::GetMvSttIdx();
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt16 m_nRedlineCount;
+ bool m_bRedlineMoved;
+#endif
+};
+
+class SwRedlineSaveDatas {
+private:
+ std::vector<std::unique_ptr<SwRedlineSaveData>> m_Data;
+
+public:
+ SwRedlineSaveDatas() : m_Data() {}
+
+ void clear() { m_Data.clear(); }
+ bool empty() const { return m_Data.empty(); }
+ size_t size() const { return m_Data.size(); }
+ void push_back(std::unique_ptr<SwRedlineSaveData> pNew) { m_Data.push_back(std::move(pNew)); }
+ const SwRedlineSaveData& operator[](size_t const nIdx) const { return *m_Data[ nIdx ]; }
+ SwRedlineSaveData& operator[](size_t const nIdx) { return *m_Data[ nIdx ]; }
+};
+
+namespace sw {
+class UndoRedoContext final
+ : public SfxUndoContext
+{
+public:
+ UndoRedoContext(SwDoc & rDoc, IShellCursorSupplier & rCursorSupplier)
+ : m_rDoc(rDoc)
+ , m_rCursorSupplier(rCursorSupplier)
+ , m_pSelFormat(nullptr)
+ , m_pMarkList(nullptr)
+ { }
+
+ SwDoc & GetDoc() const { return m_rDoc; }
+
+ IShellCursorSupplier & GetCursorSupplier() { return m_rCursorSupplier; }
+
+ void SetSelections(SwFrameFormat *const pSelFormat, SdrMarkList *const pMarkList)
+ {
+ m_pSelFormat = pSelFormat;
+ m_pMarkList = pMarkList;
+ }
+ void GetSelections(SwFrameFormat *& o_rpSelFormat, SdrMarkList *& o_rpMarkList)
+ {
+ o_rpSelFormat = m_pSelFormat;
+ o_rpMarkList = m_pMarkList;
+ }
+
+ void SetUndoOffset(size_t nUndoOffset) { m_nUndoOffset = nUndoOffset; }
+
+ size_t GetUndoOffset() override { return m_nUndoOffset; }
+
+private:
+ SwDoc & m_rDoc;
+ IShellCursorSupplier & m_rCursorSupplier;
+ SwFrameFormat * m_pSelFormat;
+ SdrMarkList * m_pMarkList;
+ size_t m_nUndoOffset = 0;
+};
+
+class RepeatContext final
+ : public SfxRepeatTarget
+{
+public:
+ RepeatContext(SwDoc & rDoc, SwPaM & rPaM)
+ : m_rDoc(rDoc)
+ , m_pCurrentPaM(& rPaM)
+ , m_bDeleteRepeated(false)
+ { }
+
+ SwDoc & GetDoc() const { return m_rDoc; }
+
+ SwPaM & GetRepeatPaM()
+ {
+ return *m_pCurrentPaM;
+ }
+
+private:
+ friend class ::sw::UndoManager;
+ friend class ::SwUndoDelete;
+
+ SwDoc & m_rDoc;
+ SwPaM * m_pCurrentPaM;
+ bool m_bDeleteRepeated; /// has a delete action been repeated?
+};
+
+} // namespace sw
+
+class SwUndoFormatColl final : public SwUndo, private SwUndRng
+{
+ OUString maFormatName;
+ std::unique_ptr<SwHistory> mpHistory;
+ // for correct <ReDo(..)> and <Repeat(..)>
+ // boolean, which indicates that the attributes are reset at the nodes
+ // before the format has been applied.
+ const bool mbReset;
+ // boolean, which indicates that the list attributes had been reset at
+ // the nodes before the format has been applied.
+ const bool mbResetListAttrs;
+
+ void DoSetFormatColl(SwDoc & rDoc, SwPaM const & rPaM);
+
+public:
+ SwUndoFormatColl( const SwPaM&, const SwFormatColl*,
+ const bool bReset,
+ const bool bResetListAttrs );
+ virtual ~SwUndoFormatColl() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ /**
+ Returns the rewriter for this undo object.
+
+ The rewriter contains one rule:
+
+ $1 -> <name of format collection>
+
+ <name of format collection> is the name of the format
+ collection that is applied by the action recorded by this undo
+ object.
+
+ @return the rewriter for this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+
+ SwHistory* GetHistory() { return mpHistory.get(); }
+
+};
+
+class SwUndoSetFlyFormat final : public SwUndo, public SwClient
+{
+ SwFrameFormat* m_pFrameFormat; // saved FlyFormat
+ const OUString m_DerivedFromFormatName;
+ const OUString m_NewFormatName;
+ std::optional<SfxItemSet> m_oItemSet; // the re-/ set attributes
+ SwNodeOffset m_nOldNode, m_nNewNode;
+ sal_Int32 m_nOldContent, m_nNewContent;
+ RndStdIds m_nOldAnchorType, m_nNewAnchorType;
+ bool m_bAnchorChanged;
+
+ void PutAttr( sal_uInt16 nWhich, const SfxPoolItem* pItem );
+ void SwClientNotify(const SwModify&, const SfxHint&) override;
+ void GetAnchor( SwFormatAnchor& rAnhor, SwNodeOffset nNode, sal_Int32 nContent );
+
+public:
+ SwUndoSetFlyFormat( SwFrameFormat& rFlyFormat, const SwFrameFormat& rNewFrameFormat );
+ virtual ~SwUndoSetFlyFormat() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoOutlineLeftRight final : public SwUndo, private SwUndRng
+{
+ short m_nOffset;
+
+public:
+ SwUndoOutlineLeftRight( const SwPaM& rPam, short nOffset );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+class SwUndoOutlineEdit final : public SwUndo, private SwUndRng
+{
+ SwNumRule m_aNewNumRule;
+ SwNumRule m_aOldNumRule;
+
+public:
+ SwUndoOutlineEdit(const SwNumRule& rOldRule, const SwNumRule& rNewRule, const SwDoc& rDoc);
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+ virtual void RepeatImpl(::sw::RepeatContext&) override;
+};
+
+const int nUndoStringLength = 20;
+
+/**
+ Shortens a string to a maximum length.
+
+ @param rStr the string to be shortened
+ @param nLength the maximum length for rStr
+ @param rFillStr string to replace cut out characters with
+
+ If rStr has less than nLength characters it will be returned unaltered.
+
+ If rStr has more than nLength characters the following algorithm
+ generates the shortened string:
+
+ frontLength = (nLength - length(rFillStr)) / 2
+ rearLength = nLength - length(rFillStr) - frontLength
+ shortenedString = concat(<first frontLength characters of rStr,
+ rFillStr,
+ <last rearLength characters of rStr>)
+
+ Preconditions:
+ - nLength - length(rFillStr) >= 2
+
+ @return the shortened string
+ */
+OUString
+ShortenString(const OUString & rStr, sal_Int32 nLength, const OUString & rFillStr);
+/**
+ Denotes special characters in a string.
+
+ The rStr is split into parts containing special characters and
+ parts not containing special characters. In a part containing
+ special characters all characters are equal. These parts are
+ maximal.
+
+ @param rStr the string to denote in
+ @param bQuoted add quotation marks to the text
+
+ The resulting string is generated by concatenating the found
+ parts. The parts without special characters are surrounded by
+ "'". The parts containing special characters are denoted as "n x",
+ where n is the length of the part and x is the representation of
+ the special character (i. e. "tab(s)").
+
+ @return the denoted string
+*/
+OUString DenoteSpecialCharacters(const OUString & rStr, bool bQuoted = true);
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOCORE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoDelete.hxx b/sw/source/core/inc/UndoDelete.hxx
new file mode 100644
index 000000000..2491af658
--- /dev/null
+++ b/sw/source/core/inc/UndoDelete.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDODELETE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDODELETE_HXX
+
+#include <undobj.hxx>
+#include <rtl/ustring.hxx>
+#include <memory>
+#include <optional>
+
+class SwRedlineSaveDatas;
+class SwTextNode;
+typedef struct _xmlTextWriter* xmlTextWriterPtr;
+enum class SwDeleteFlags;
+
+namespace sfx2 {
+ class MetadatableUndo;
+}
+
+class SwUndoDelete final
+ : public SwUndo
+ , private SwUndRng
+ , private SwUndoSaveContent
+{
+ std::unique_ptr<SwNodeIndex> m_pMvStt; // Position of Nodes in UndoNodes-Array
+ std::optional<OUString> m_aSttStr, m_aEndStr;
+ std::unique_ptr<SwRedlineSaveDatas> m_pRedlSaveData;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;
+
+ OUString m_sTableName;
+
+ SwNodeOffset m_nNode;
+ SwNodeOffset m_nNdDiff; // difference of Nodes before/after Delete
+ SwNodeOffset m_nSectDiff; // diff. of Nodes before/after Move w/ SectionNodes
+ SwNodeOffset m_nReplaceDummy; // diff. to a temporary dummy object
+ sal_uInt16 m_nSetPos;
+
+ bool m_bGroup : 1; // TRUE: is already Grouped; see CanGrouping()
+ bool m_bBackSp : 1; // TRUE: if Grouped and preceding content deleted
+ bool m_bJoinNext: 1; // TRUE: if range is selected forwards
+ bool m_bTableDelLastNd : 1; // TRUE: TextNode following Table inserted/deleted
+ bool m_bDelFullPara : 1; // TRUE: entire Nodes were deleted
+ bool m_bResetPgDesc : 1; // TRUE: reset PgDsc on following node
+ bool m_bResetPgBrk : 1; // TRUE: reset PgBreak on following node
+ bool m_bFromTableCopy : 1; // TRUE: called by SwUndoTableCpyTable
+ SwDeleteFlags m_DeleteFlags;
+
+ bool SaveContent( const SwPosition* pStt, const SwPosition* pEnd,
+ SwTextNode* pSttTextNd, SwTextNode* pEndTextNd );
+
+public:
+ SwUndoDelete(
+ SwPaM&,
+ SwDeleteFlags flags,
+ bool bFullPara = false,
+ bool bCalledByTableCpy = false );
+ virtual ~SwUndoDelete() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+ /**
+ Returns rewriter for this undo object.
+
+ The rewriter consists of the following rule:
+
+ $1 -> '<deleted text>'
+
+ <deleted text> is shortened to nUndoStringLength characters.
+
+ @return rewriter for this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+
+ bool CanGrouping( SwDoc&, const SwPaM& );
+
+ void SetTableDelLastNd() { m_bTableDelLastNd = true; }
+
+ // for PageDesc/PageBreak Attributes of a table
+ void SetPgBrkFlags( bool bPageBreak, bool bPageDesc )
+ { m_bResetPgDesc = bPageDesc; m_bResetPgBrk = bPageBreak; }
+
+ void SetTableName(const OUString & rName);
+
+ // SwUndoTableCpyTable needs this information:
+ bool IsDelFullPara() const { return m_bDelFullPara; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDODELETE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoDraw.hxx b/sw/source/core/inc/UndoDraw.hxx
new file mode 100644
index 000000000..951902817
--- /dev/null
+++ b/sw/source/core/inc/UndoDraw.hxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDODRAW_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDODRAW_HXX
+
+#include <undobj.hxx>
+#include <memory>
+
+struct SwUndoGroupObjImpl;
+class SdrMark;
+class SdrMarkList;
+class SdrObject;
+class SdrObjGroup;
+class SdrUndoAction;
+class SwDrawFrameFormat;
+class SwDoc;
+
+// Undo for Draw Objects
+class SwSdrUndo final : public SwUndo
+{
+ std::unique_ptr<SdrUndoAction> m_pSdrUndo;
+ std::unique_ptr<SdrMarkList> m_pMarkList; // MarkList for all selected SdrObjects
+
+public:
+ SwSdrUndo(std::unique_ptr<SdrUndoAction>, const SdrMarkList* pMarkList, const SwDoc& rDoc);
+
+ virtual ~SwSdrUndo() override;
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+
+ virtual OUString GetComment() const override;
+};
+
+class SwUndoDrawGroup final : public SwUndo
+{
+ std::unique_ptr<SwUndoGroupObjImpl[]> m_pObjArray;
+ sal_uInt16 m_nSize;
+ bool m_bDeleteFormat;
+
+public:
+ SwUndoDrawGroup(sal_uInt16 nCnt, const SwDoc& rDoc);
+
+ virtual ~SwUndoDrawGroup() override;
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+
+ void AddObj(sal_uInt16 nPos, SwDrawFrameFormat*, SdrObject*);
+ void SetGroupFormat(SwDrawFrameFormat*);
+};
+
+// Action "ungroup drawing object" is now split into three parts - see
+// method <SwDoc::UnGroupSelection(..)>:
+// - creation for <SwDrawFrameFormat> instances for the group members of the
+// selected group objects
+// - intrinsic ungroup of the selected group objects
+// - creation of <SwDrawContact> instances for the former group members and
+// connection to the Writer layout.
+// Thus, two undo actions (instances of <SwUndo>) are needed:
+// - Existing class <SwUndoDrawUnGroup> takes over the part for the formats.
+// - New class <SwUndoDrawUnGroupConnectToLayout> takes over the part for
+// contact object.
+class SwUndoDrawUnGroup final : public SwUndo
+{
+ std::unique_ptr<SwUndoGroupObjImpl[]> m_pObjArray;
+ sal_uInt16 m_nSize;
+ bool m_bDeleteFormat;
+
+public:
+ SwUndoDrawUnGroup(SdrObjGroup*, const SwDoc& rDoc);
+
+ virtual ~SwUndoDrawUnGroup() override;
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+
+ void AddObj(sal_uInt16 nPos, SwDrawFrameFormat*);
+};
+
+class SwUndoDrawUnGroupConnectToLayout final : public SwUndo
+{
+private:
+ std::vector<std::pair<SwDrawFrameFormat*, SdrObject*>> m_aDrawFormatsAndObjs;
+
+public:
+ SwUndoDrawUnGroupConnectToLayout(const SwDoc& rDoc);
+
+ virtual ~SwUndoDrawUnGroupConnectToLayout() override;
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+
+ void AddFormatAndObj(SwDrawFrameFormat* pDrawFrameFormat, SdrObject* pDrawObject);
+};
+
+class SwUndoDrawDelete final : public SwUndo
+{
+ std::unique_ptr<SwUndoGroupObjImpl[]> m_pObjArray;
+ std::unique_ptr<SdrMarkList> m_pMarkList; // MarkList for all selected SdrObjects
+ bool m_bDeleteFormat;
+
+public:
+ SwUndoDrawDelete(sal_uInt16 nCnt, const SwDoc& rDoc);
+
+ virtual ~SwUndoDrawDelete() override;
+
+ virtual void UndoImpl(::sw::UndoRedoContext&) override;
+ virtual void RedoImpl(::sw::UndoRedoContext&) override;
+
+ void AddObj(SwDrawFrameFormat*, const SdrMark&);
+ void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDODRAW_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoInsert.hxx b/sw/source/core/inc/UndoInsert.hxx
new file mode 100644
index 000000000..ae7b6c02d
--- /dev/null
+++ b/sw/source/core/inc/UndoInsert.hxx
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOINSERT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOINSERT_HXX
+
+#include <memory>
+
+#include <undobj.hxx>
+#include <svx/svdtypes.hxx>
+#include <rtl/ustring.hxx>
+#include <swtypes.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <optional>
+
+class Graphic;
+class SwGrfNode;
+class SwUndoDelete;
+class SwUndoFormatAttr;
+class SwDoc;
+namespace sw { class DocumentContentOperationsManager; }
+enum class MirrorGraph;
+
+/// Typing one or more characters to a single paragraph.
+class SwUndoInsert final : public SwUndo, private SwUndoSaveContent
+{
+ /// start of Content in UndoNodes for Redo
+ std::unique_ptr<SwNodeIndex> m_pUndoNodeIndex;
+ std::optional<OUString> maText;
+ std::optional<OUString> maUndoText;
+ std::unique_ptr<SwRedlineData> m_pRedlData;
+ SwNodeOffset m_nNode;
+ sal_Int32 m_nContent, m_nLen;
+ bool m_bIsWordDelim : 1;
+ bool m_bIsAppend : 1;
+ bool m_bWithRsid : 1;
+
+ const SwInsertFlags m_nInsertFlags;
+
+ friend class ::sw::DocumentContentOperationsManager; // actually only DocumentContentOperationsManager::InsertString, because it uses CanGrouping
+ bool CanGrouping( sal_Unicode cIns );
+ bool CanGrouping( const SwPosition& rPos );
+
+ SwDoc * m_pDoc;
+
+ void Init(const SwNodeIndex & rNode);
+ std::optional<OUString> GetTextFromDoc() const;
+
+public:
+ SwUndoInsert( const SwNodeIndex& rNode, sal_Int32 nContent, sal_Int32 nLen,
+ const SwInsertFlags nInsertFlags,
+ bool bWDelim = true );
+ SwUndoInsert( const SwNodeIndex& rNode );
+ virtual ~SwUndoInsert() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ /**
+ Returns rewriter for this undo object.
+
+ The returned rewriter has the following rule:
+
+ $1 -> '<inserted text>'
+
+ <inserted text> is shortened to a length of nUndoStringLength.
+
+ @return rewriter for this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+
+ void SetWithRsid() { m_bWithRsid = true; }
+
+ bool IsIndependent(const SwUndoInsert& rOther) const;
+};
+
+SwRewriter
+MakeUndoReplaceRewriter(sal_uLong const occurrences,
+ OUString const& sOld, OUString const& sNew);
+
+class SwUndoReplace final
+ : public SwUndo
+{
+public:
+ SwUndoReplace(SwPaM const& rPam,
+ OUString const& rInsert, bool const bRegExp);
+
+ virtual ~SwUndoReplace() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ /**
+ Returns the rewriter of this undo object.
+
+ If this undo object represents several replacements the
+ rewriter contains the following rules:
+
+ $1 -> <number of replacements>
+ $2 -> occurrences of
+ $3 -> <replaced text>
+
+ If this undo object represents one replacement the rewriter
+ contains these rules:
+
+ $1 -> <replaced text>
+ $2 -> "->" (STR_YIELDS)
+ $3 -> <replacing text>
+
+ @return the rewriter of this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+
+ void SetEnd( const SwPaM& rPam );
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> m_pImpl;
+};
+
+class SwUndoReRead final : public SwUndo
+{
+ std::unique_ptr<Graphic> mpGraphic;
+ std::optional<OUString> maNm;
+ std::optional<OUString> maFltr;
+ SwNodeOffset mnPosition;
+ MirrorGraph mnMirror;
+
+ void SaveGraphicData( const SwGrfNode& );
+ void SetAndSave( ::sw::UndoRedoContext & );
+
+public:
+ SwUndoReRead( const SwPaM& rPam, const SwGrfNode& pGrfNd );
+
+ virtual ~SwUndoReRead() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+class SwUndoInsertLabel final : public SwUndo
+{
+ union {
+ struct {
+ // for NoTextFrames
+ SwUndoInsLayFormat* pUndoFly;
+ SwUndoFormatAttr* pUndoAttr;
+ } OBJECT;
+ struct {
+ // for tables or TextFrames
+ SwUndoDelete* pUndoInsNd;
+ SwNodeOffset nNode;
+ } NODE;
+ };
+
+ OUString m_sText;
+ // #i39983# the separator is drawn with a character style
+ OUString m_sSeparator;
+ OUString m_sNumberSeparator;
+ OUString m_sCharacterStyle;
+ // #i26791# - re-store of drawing object position no longer needed
+ sal_uInt16 m_nFieldId;
+ SwLabelType m_eType;
+ SdrLayerID m_nLayerId; // for character objects
+ bool m_bBefore :1;
+ bool m_bUndoKeep :1;
+ bool m_bCopyBorder :1;
+
+public:
+ SwUndoInsertLabel( const SwLabelType eTyp, const OUString &rText,
+ // #i39983# the separator is drawn with a character style
+ const OUString& rSeparator,
+ const OUString& rNumberSeparator, //#i61007# order of captions
+ const bool bBefore, const sal_uInt16 nId,
+ const OUString& rCharacterStyle,
+ const bool bCpyBrd,
+ const SwDoc* pDoc );
+ virtual ~SwUndoInsertLabel() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ /**
+ Returns the rewriter of this undo object.
+
+ The rewriter contains this rule:
+
+ $1 -> '<text of inserted label>'
+
+ <text of inserted label> is shortened to nUndoStringLength
+ characters.
+
+ @return the rewriter of this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+ static SwRewriter CreateRewriter(const OUString &rStr);
+
+ void SetNodePos( SwNodeOffset nNd )
+ { if( SwLabelType::Object != m_eType ) NODE.nNode = nNd; }
+
+ void SetUndoKeep() { m_bUndoKeep = true; }
+ void SetFlys( SwFrameFormat& rOldFly, SfxItemSet const & rChgSet, SwFrameFormat& rNewFly );
+ void SetDrawObj( SdrLayerID nLayerId );
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOINSERT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoManager.hxx b/sw/source/core/inc/UndoManager.hxx
new file mode 100644
index 000000000..2db896f36
--- /dev/null
+++ b/sw/source/core/inc/UndoManager.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOMANAGER_HXX
+
+#include <IDocumentUndoRedo.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <ndarr.hxx>
+#include <memory>
+
+class IDocumentDrawModelAccess;
+class IDocumentRedlineAccess;
+class IDocumentState;
+class SwDocShell;
+class SwView;
+
+namespace sw {
+
+class SW_DLLPUBLIC UndoManager final
+ : public IDocumentUndoRedo
+ , public SdrUndoManager
+{
+public:
+ UndoManager(std::shared_ptr<SwNodes> const & pUndoNodes,
+ IDocumentDrawModelAccess & rDrawModelAccess,
+ IDocumentRedlineAccess & rRedlineAccess,
+ IDocumentState & rState);
+
+ /** IDocumentUndoRedo */
+ virtual void DoUndo(bool const bDoUndo) override;
+ virtual bool DoesUndo() const override;
+ virtual void DoGroupUndo(bool const bDoUndo) override;
+ virtual bool DoesGroupUndo() const override;
+ virtual void DoDrawUndo(bool const bDoUndo) override;
+ virtual bool DoesDrawUndo() const override;
+ void DoRepair(bool bRepair) override;
+ bool DoesRepair() const override;
+ virtual void SetUndoNoModifiedPosition() override;
+ virtual void LockUndoNoModifiedPosition() override;
+ virtual void UnLockUndoNoModifiedPosition() override;
+ virtual void SetUndoNoResetModified() override;
+ virtual bool IsUndoNoResetModified() const override;
+
+ virtual SwUndoId StartUndo(SwUndoId const eUndoId,
+ SwRewriter const*const pRewriter) override;
+ virtual SwUndoId EndUndo(SwUndoId const eUndoId,
+ SwRewriter const*const pRewriter) override;
+ virtual void DelAllUndoObj() override;
+ virtual bool GetLastUndoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView = nullptr) const override;
+ virtual SwUndoComments_t GetUndoComments() const override;
+ virtual bool GetFirstRedoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView = nullptr) const override;
+ virtual SwUndoComments_t GetRedoComments() const override;
+ virtual bool Repeat(::sw::RepeatContext & rContext,
+ sal_uInt16 const nRepeatCnt) override;
+ virtual SwUndoId GetRepeatInfo(OUString *const o_pStr) const override;
+ virtual void AppendUndo(std::unique_ptr<SwUndo> pUndo) override;
+ virtual void ClearRedo() override;
+ virtual bool IsUndoNodes(SwNodes const& rNodes) const override;
+ virtual size_t GetUndoActionCount(const bool bCurrentLevel = true) const override;
+ size_t GetRedoActionCount(const bool bCurrentLevel = true) const override;
+ void SetView(SwView* pView) override;
+ bool UndoWithOffset(size_t nUndoOffset) override;
+
+ // SfxUndoManager
+ virtual void AddUndoAction(std::unique_ptr<SfxUndoAction> pAction,
+ bool bTryMerg = false) override;
+ virtual bool Undo() override;
+ virtual bool Redo() override;
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ SwUndo * RemoveLastUndo();
+ SwUndo * GetLastUndo();
+
+ SwNodes const& GetUndoNodes() const;
+ SwNodes & GetUndoNodes();
+ void SetDocShell(SwDocShell* pDocShell);
+
+ /**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action. Sets rOffset to the offset of that independent undo action on success.
+ */
+ bool IsViewUndoActionIndependent(const SwView* pView, sal_uInt16& rOffset) const;
+
+private:
+ virtual void EmptyActionsChanged() override;
+
+ IDocumentDrawModelAccess & m_rDrawModelAccess;
+ IDocumentRedlineAccess & m_rRedlineAccess;
+ IDocumentState & m_rState;
+
+ /// Undo nodes array: content not currently in document
+ std::shared_ptr<SwNodes> m_xUndoNodes;
+
+ bool m_bGroupUndo : 1; // TRUE: Undo grouping enabled
+ bool m_bDrawUndo : 1; // TRUE: Draw Undo enabled
+ /// If true, then repair mode is enabled.
+ bool m_bRepair;
+ bool m_bLockUndoNoModifiedPosition : 1;
+ /// set the IgnoreRepeat flag on every added action
+ bool m_isAddWithIgnoreRepeat;
+ /// position in Undo-Array at which Doc was saved (and is not modified)
+ UndoStackMark m_UndoSaveMark;
+ SwDocShell* m_pDocShell;
+ SwView* m_pView;
+
+ enum class UndoOrRedoType { Undo, Redo };
+ bool impl_DoUndoRedo(UndoOrRedoType undoOrRedo, size_t nUndoOffset);
+
+ // UGLY: should not be called
+ using SdrUndoManager::Repeat;
+};
+
+} // namespace sw
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoNumbering.hxx b/sw/source/core/inc/UndoNumbering.hxx
new file mode 100644
index 000000000..c5914fbd0
--- /dev/null
+++ b/sw/source/core/inc/UndoNumbering.hxx
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDONUMBERING_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDONUMBERING_HXX
+
+#include <memory>
+#include <vector>
+#include <undobj.hxx>
+#include <rtl/ustring.hxx>
+#include <numrule.hxx>
+
+class SwUndoInsNum final : public SwUndo, private SwUndRng
+{
+ SwNumRule m_aNumRule;
+ std::unique_ptr<SwHistory> m_pHistory;
+ std::unique_ptr<SwNumRule> m_pOldNumRule;
+ OUString m_sReplaceRule;
+ sal_uInt16 m_nLRSavePos;
+
+public:
+ SwUndoInsNum( const SwPaM& rPam, const SwNumRule& rRule );
+ SwUndoInsNum( const SwNumRule& rOldRule, const SwNumRule& rNewRule,
+ const SwDoc& rDoc, SwUndoId nUndoId = SwUndoId::INSFMTATTR );
+ SwUndoInsNum( const SwPosition& rPos, const SwNumRule& rRule,
+ const OUString& rReplaceRule );
+
+ virtual ~SwUndoInsNum() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+
+ SwHistory* GetHistory(); // will be created if necessary
+ void SaveOldNumRule( const SwNumRule& rOld );
+
+ void SetLRSpaceEndPos();
+
+};
+
+class SwUndoDelNum final : public SwUndo, private SwUndRng
+{
+ struct NodeLevel
+ {
+ SwNodeOffset index;
+ int level;
+ NodeLevel(SwNodeOffset idx, int lvl) : index(idx), level(lvl) {};
+ };
+ std::vector<NodeLevel> m_aNodes;
+ std::unique_ptr<SwHistory> m_pHistory;
+
+public:
+ SwUndoDelNum( const SwPaM& rPam );
+
+ virtual ~SwUndoDelNum() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void AddNode( const SwTextNode& rNd );
+ SwHistory* GetHistory() { return m_pHistory.get(); }
+};
+
+class SwUndoMoveNum final : public SwUndo, private SwUndRng
+{
+ SwNodeOffset m_nNewStart;
+ SwNodeOffset m_nOffset;
+
+public:
+ SwUndoMoveNum( const SwPaM& rPam, SwNodeOffset nOffset, bool bIsOutlMv );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetStartNode( SwNodeOffset nValue ) { m_nNewStart = nValue; }
+};
+
+class SwUndoNumUpDown final : public SwUndo, private SwUndRng
+{
+ short m_nOffset;
+
+public:
+ SwUndoNumUpDown( const SwPaM& rPam, short nOffset );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+class SwUndoNumOrNoNum final : public SwUndo
+{
+ SwNodeOffset m_nIndex;
+ bool mbNewNum, mbOldNum;
+
+public:
+ SwUndoNumOrNoNum( const SwNodeIndex& rIdx, bool mbOldNum,
+ bool mbNewNum );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+class SwUndoNumRuleStart final : public SwUndo
+{
+ SwNodeOffset m_nIndex;
+ sal_uInt16 m_nOldStart, m_nNewStart;
+ bool m_bSetStartValue : 1;
+ bool m_bFlag : 1;
+
+public:
+ SwUndoNumRuleStart( const SwPosition& rPos, bool bDelete );
+ SwUndoNumRuleStart( const SwPosition& rPos, sal_uInt16 nStt );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDONUMBERING_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoOverwrite.hxx b/sw/source/core/inc/UndoOverwrite.hxx
new file mode 100644
index 000000000..ddbf23262
--- /dev/null
+++ b/sw/source/core/inc/UndoOverwrite.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOOVERWRITE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOOVERWRITE_HXX
+
+#include <memory>
+#include <rtl/ustring.hxx>
+#include <undobj.hxx>
+
+class SwRedlineSaveDatas;
+class SwTextNode;
+enum class TransliterationFlags;
+namespace utl {
+ class TransliterationWrapper;
+}
+
+class SwUndoOverwrite final : public SwUndo, private SwUndoSaveContent
+{
+ OUString m_aDelStr, m_aInsStr;
+ std::unique_ptr<SwRedlineSaveDatas> m_pRedlSaveData;
+ SwNodeOffset m_nStartNode;
+ sal_Int32 m_nStartContent;
+ bool m_bInsChar : 1; // no Overwrite, but Insert
+ bool m_bGroup : 1; // TRUE: is already grouped; evaluated in CanGrouping()
+
+public:
+ SwUndoOverwrite( SwDoc&, SwPosition&, sal_Unicode cIns );
+
+ virtual ~SwUndoOverwrite() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ /**
+ Returns the rewriter of this undo object.
+
+ The rewriter contains the following rule:
+
+ $1 -> '<overwritten text>'
+
+ <overwritten text> is shortened to nUndoStringLength characters.
+
+ @return the rewriter of this undo object
+ */
+ virtual SwRewriter GetRewriter() const override;
+
+ bool CanGrouping( SwDoc&, SwPosition&, sal_Unicode cIns );
+};
+
+struct UndoTransliterate_Data;
+class SwUndoTransliterate final : public SwUndo, public SwUndRng
+{
+ std::vector< std::unique_ptr<UndoTransliterate_Data> > m_aChanges;
+ TransliterationFlags m_nType;
+
+ void DoTransliterate(SwDoc & rDoc, SwPaM const & rPam);
+
+public:
+ SwUndoTransliterate( const SwPaM& rPam,
+ const utl::TransliterationWrapper& rTrans );
+
+ virtual ~SwUndoTransliterate() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void AddChanges( SwTextNode& rTNd, sal_Int32 nStart, sal_Int32 nLen,
+ css::uno::Sequence <sal_Int32> const & rOffsets );
+ bool HasData() const { return m_aChanges.size() > 0; }
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOOVERWRITE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoRedline.hxx b/sw/source/core/inc/UndoRedline.hxx
new file mode 100644
index 000000000..7c815fca6
--- /dev/null
+++ b/sw/source/core/inc/UndoRedline.hxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOREDLINE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOREDLINE_HXX
+
+#include <memory>
+#include <undobj.hxx>
+#include <IDocumentContentOperations.hxx>
+
+struct SwSortOptions;
+class SwRangeRedline;
+class SwRedlineSaveDatas;
+class SwUndoDelete;
+
+class SwUndoRedline : public SwUndo, public SwUndRng
+{
+protected:
+ std::unique_ptr<SwRedlineData> mpRedlData;
+ std::unique_ptr<SwRedlineSaveDatas> mpRedlSaveData;
+ SwUndoId mnUserId;
+ bool mbHiddenRedlines;
+
+ virtual void UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam);
+ virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam);
+
+public:
+ SwUndoRedline( SwUndoId nUserId, const SwPaM& rRange );
+
+ virtual ~SwUndoRedline() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ sal_uInt16 GetRedlSaveCount() const;
+};
+
+class SwUndoRedlineDelete final : public SwUndoRedline
+{
+private:
+ std::unique_ptr<SwHistory> m_pHistory; ///< for moved fly anchors
+
+ bool m_bCanGroup : 1;
+ bool m_bIsDelim : 1;
+ bool m_bIsBackspace : 1;
+
+ OUString m_sRedlineText;
+
+ void InitHistory(SwPaM const& rRange);
+
+ virtual void UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override;
+ virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override;
+
+public:
+ SwUndoRedlineDelete(const SwPaM& rRange, SwUndoId nUserId, SwDeleteFlags flags = SwDeleteFlags::Default);
+ virtual SwRewriter GetRewriter() const override;
+
+ bool CanGrouping( const SwUndoRedlineDelete& rPrev );
+
+ // SwUndoTableCpyTable needs this information:
+ SwNodeOffset NodeDiff() const { return m_nSttNode - m_nEndNode; }
+ sal_Int32 ContentStart() const { return m_nSttContent; }
+
+ void SetRedlineText(const OUString & rText);
+};
+
+class SwUndoRedlineSort final : public SwUndoRedline
+{
+ std::unique_ptr<SwSortOptions> m_pOpt;
+ SwNodeOffset m_nSaveEndNode;
+ sal_Int32 m_nSaveEndContent;
+
+ virtual void UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override;
+ virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override;
+
+public:
+ SwUndoRedlineSort( const SwPaM& rRange, const SwSortOptions& rOpt );
+
+ virtual ~SwUndoRedlineSort() override;
+
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetSaveRange( const SwPaM& rRange );
+};
+
+class SwUndoAcceptRedline final : public SwUndoRedline
+{
+private:
+ virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override;
+
+public:
+ SwUndoAcceptRedline( const SwPaM& rRange );
+
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+class SwUndoRejectRedline final : public SwUndoRedline
+{
+private:
+ virtual void RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam) override;
+
+public:
+ SwUndoRejectRedline( const SwPaM& rRange );
+
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+class SwUndoCompDoc final : public SwUndo, public SwUndRng
+{
+ std::unique_ptr<SwRedlineData> m_pRedlineData;
+ std::unique_ptr<SwUndoDelete> m_pUndoDelete, m_pUndoDelete2;
+ std::unique_ptr<SwRedlineSaveDatas> m_pRedlineSaveDatas;
+ bool m_bInsert;
+
+public:
+ SwUndoCompDoc( const SwPaM& rRg, bool bIns );
+ SwUndoCompDoc( const SwRangeRedline& rRedl );
+
+ virtual ~SwUndoCompDoc() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOREDLINE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoSection.hxx b/sw/source/core/inc/UndoSection.hxx
new file mode 100644
index 000000000..85b15a8b7
--- /dev/null
+++ b/sw/source/core/inc/UndoSection.hxx
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOSECTION_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOSECTION_HXX
+
+#include <o3tl/deleter.hxx>
+#include <undobj.hxx>
+#include <tuple>
+#include <memory>
+#include <optional>
+
+class SfxItemSet;
+class SwTextNode;
+class SwSectionData;
+class SwSectionFormat;
+class SwTOXBase;
+
+namespace sw {
+ enum class RedlineMode;
+ enum class FieldmarkMode;
+};
+
+class SwUndoInsSection final : public SwUndo, private SwUndRng
+{
+private:
+ const std::unique_ptr<SwSectionData> m_pSectionData;
+ std::optional<std::tuple<std::unique_ptr<SwTOXBase>, sw::RedlineMode, sw::FieldmarkMode>> m_xTOXBase; /// set iff section is TOX
+ const std::unique_ptr<SfxItemSet> m_pAttrSet;
+ std::unique_ptr<SwHistory> m_pHistory;
+ std::unique_ptr<SwRedlineData> m_pRedlData;
+ std::unique_ptr<SwRedlineSaveDatas> m_pRedlineSaveData;
+ SwNodeOffset m_nSectionNodePos;
+ bool m_bSplitAtStart : 1;
+ bool m_bSplitAtEnd : 1;
+ bool m_bUpdateFootnote : 1;
+
+ void Join( SwDoc& rDoc, SwNodeOffset nNode );
+
+public:
+ SwUndoInsSection(SwPaM const&, SwSectionData const&,
+ SfxItemSet const* pSet,
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode> const* pTOXBase);
+
+ virtual ~SwUndoInsSection() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetSectNdPos(SwNodeOffset const nPos) { m_nSectionNodePos = nPos; }
+ void SaveSplitNode(SwTextNode *const pTextNd, bool const bAtStart);
+ void SetUpdateFootnoteFlag(bool const bFlag) { m_bUpdateFootnote = bFlag; }
+};
+
+std::unique_ptr<SwUndo> MakeUndoDelSection(SwSectionFormat const&);
+
+std::unique_ptr<SwUndo> MakeUndoUpdateSection(SwSectionFormat const&, bool const);
+
+
+class SwTOXBaseSection;
+class SwUndoDelSection;
+
+class SwUndoUpdateIndex final : public SwUndo
+{
+private:
+ std::unique_ptr<SwUndoDelSection> m_pTitleSectionUpdated;
+ std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> const m_pSaveSectionOriginal;
+ std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> const m_pSaveSectionUpdated;
+ SwNodeOffset const m_nStartIndex;
+
+public:
+ SwUndoUpdateIndex(SwTOXBaseSection &);
+ virtual ~SwUndoUpdateIndex() override;
+
+ void TitleSectionInserted(SwSectionFormat & rSectionFormat);
+
+ virtual void UndoImpl(::sw::UndoRedoContext &) override;
+ virtual void RedoImpl(::sw::UndoRedoContext &) override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOSECTION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoSort.hxx b/sw/source/core/inc/UndoSort.hxx
new file mode 100644
index 000000000..81cb33c60
--- /dev/null
+++ b/sw/source/core/inc/UndoSort.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOSORT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOSORT_HXX
+
+#include <undobj.hxx>
+
+#include <rtl/ustring.hxx>
+#include <tools/solar.h>
+
+#include <memory>
+#include <vector>
+
+struct SwSortOptions;
+class SwTableNode;
+class SwUndoAttrTable;
+
+struct SwSortUndoElement
+{
+ union {
+ struct {
+ sal_uLong nID;
+ // we cannot store these as SwNodeOffset (even though they are)
+ // because SwNodeOffset has no default constructor
+ sal_Int32 nSource, nTarget;
+ } TXT;
+ struct {
+ OUString *pSource, *pTarget;
+ } TBL;
+ } SORT_TXT_TBL;
+
+ SwSortUndoElement( const OUString& aS, const OUString& aT )
+ {
+ SORT_TXT_TBL.TBL.pSource = new OUString( aS );
+ SORT_TXT_TBL.TBL.pTarget = new OUString( aT );
+ }
+ SwSortUndoElement( SwNodeOffset nS, SwNodeOffset nT )
+ {
+ SORT_TXT_TBL.TXT.nSource = sal_Int32(nS);
+ SORT_TXT_TBL.TXT.nTarget = sal_Int32(nT);
+ SORT_TXT_TBL.TXT.nID = 0xffffffff;
+ }
+ ~SwSortUndoElement();
+};
+
+class SwUndoSort final : public SwUndo, private SwUndRng
+{
+ std::unique_ptr<SwSortOptions> m_pSortOptions;
+ std::vector<std::unique_ptr<SwSortUndoElement>> m_SortList;
+ std::unique_ptr<SwUndoAttrTable> m_pUndoAttrTable;
+ SwNodeOffset m_nTableNode;
+
+public:
+ SwUndoSort( const SwPaM&, const SwSortOptions& );
+ SwUndoSort( SwNodeOffset nStt, SwNodeOffset nEnd, const SwTableNode&,
+ const SwSortOptions&, bool bSaveTable );
+
+ virtual ~SwUndoSort() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void Insert( const OUString& rOrgPos, const OUString& rNewPos );
+ void Insert( SwNodeOffset nOrgPos, SwNodeOffset nNewPos );
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOSORT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoSplitMove.hxx b/sw/source/core/inc/UndoSplitMove.hxx
new file mode 100644
index 000000000..9cf2fda25
--- /dev/null
+++ b/sw/source/core/inc/UndoSplitMove.hxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOSPLITMOVE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOSPLITMOVE_HXX
+
+#include <undobj.hxx>
+
+class SwUndoSplitNode final : public SwUndo
+{
+ std::unique_ptr<SwHistory> m_pHistory;
+ std::unique_ptr<SwRedlineData> m_pRedlineData;
+ SwNodeOffset m_nNode;
+ sal_Int32 m_nContent;
+ bool m_bTableFlag : 1;
+ bool m_bCheckTableStart : 1;
+ sal_uInt32 m_nParRsid;
+
+public:
+ SwUndoSplitNode( SwDoc& rDoc, const SwPosition& rPos, bool bChkTable );
+
+ virtual ~SwUndoSplitNode() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetTableFlag() { m_bTableFlag = true; }
+};
+
+class SwUndoMove final : public SwUndo, private SwUndRng, private SwUndoSaveContent
+{
+ // nDest... - destination range of move (after move!)
+ // nIns... - source Position of move (after move!)
+ // nMv... - destination position of move (before move!); for REDO
+ SwNodeOffset m_nDestStartNode, m_nDestEndNode, m_nInsPosNode, m_nMoveDestNode;
+ sal_Int32 m_nDestStartContent, m_nDestEndContent, m_nInsPosContent, m_nMoveDestContent;
+
+ sal_uInt16 m_nFootnoteStart; // StartPos of Footnotes in History
+
+ bool m_bJoinNext : 1,
+ m_bMoveRange : 1;
+
+ bool m_bMoveRedlines; // use DOC_MOVEREDLINES when calling SwDoc::Move
+
+ void DelFootnote( const SwPaM& );
+
+public:
+ SwUndoMove( const SwPaM&, const SwPosition& );
+ SwUndoMove( SwDoc& rDoc, const SwNodeRange&, const SwNodeIndex& );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ /// set the destination range after the move
+ void SetDestRange( const SwNodeIndex& rStt, const SwNodeIndex& rEnd,
+ const SwNodeIndex& rInsPos );
+
+ bool IsMoveRange() const { return m_bMoveRange; }
+ SwNodeOffset GetEndNode() const { return m_nEndNode; }
+ SwNodeOffset GetDestSttNode() const { return m_nDestStartNode; }
+ sal_Int32 GetDestSttContent() const { return m_nDestStartContent; }
+
+ void SetMoveRedlines( bool b ) { m_bMoveRedlines = b; }
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOSPLITMOVE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/UndoTable.hxx b/sw/source/core/inc/UndoTable.hxx
new file mode 100644
index 000000000..dc6a8f4cd
--- /dev/null
+++ b/sw/source/core/inc/UndoTable.hxx
@@ -0,0 +1,429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOTABLE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOTABLE_HXX
+
+#include <o3tl/deleter.hxx>
+#include <tools/long.hxx>
+#include <tools/solar.h>
+#include <ndarr.hxx>
+#include <undobj.hxx>
+#include <set>
+#include <itabenum.hxx>
+#include <tblenum.hxx>
+#include <memory>
+#include <vector>
+
+class SfxItemSet;
+
+struct SwSaveRowSpan;
+class SaveTable;
+class SwDDEFieldType;
+class SwUndoDelete;
+class SwSelBoxes;
+class SwTable;
+class SwTableBox;
+class SwStartNode;
+class SwTableNode;
+class SwTableAutoFormat;
+class SwTableSortBoxes;
+class SwContentNode;
+class SwCursorShell;
+
+namespace sw {
+
+void NotifyTableCollapsedParagraph(const SwContentNode* pNode, SwCursorShell *const pShell);
+
+}
+
+class SwUndoInsTable final : public SwUndo
+{
+ OUString m_sTableName;
+ SwInsertTableOptions m_aInsTableOptions;
+ std::unique_ptr<SwDDEFieldType> m_pDDEFieldType;
+ std::unique_ptr<std::vector<sal_uInt16>> m_pColumnWidth;
+ std::unique_ptr<SwRedlineData> m_pRedlineData;
+ std::unique_ptr<SwTableAutoFormat> m_pAutoFormat;
+ SwNodeOffset m_nStartNode;
+ sal_uInt16 m_nRows, m_nColumns;
+ sal_uInt16 const m_nAdjust;
+
+public:
+ SwUndoInsTable( const SwPosition&, sal_uInt16 nCols, sal_uInt16 nRows,
+ sal_uInt16 eAdjust, const SwInsertTableOptions& rInsTableOpts,
+ const SwTableAutoFormat* pTAFormat, const std::vector<sal_uInt16> *pColArr,
+ const OUString & rName);
+
+ virtual ~SwUndoInsTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoTextToTable final : public SwUndo, public SwUndRng
+{
+ OUString m_sTableName;
+ SwInsertTableOptions m_aInsertTableOpts;
+ std::vector<SwNodeOffset> mvDelBoxes;
+ std::unique_ptr<SwTableAutoFormat> m_pAutoFormat;
+ SwHistory* m_pHistory;
+ sal_Unicode m_cSeparator;
+ sal_uInt16 m_nAdjust;
+ bool m_bSplitEnd : 1;
+
+public:
+ SwUndoTextToTable( const SwPaM&, const SwInsertTableOptions&, sal_Unicode,
+ sal_uInt16,
+ const SwTableAutoFormat* pAFormat );
+
+ virtual ~SwUndoTextToTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ SwHistory& GetHistory(); // will be created if necessary
+ void AddFillBox( const SwTableBox& rBox );
+};
+
+class SwUndoTableToText final : public SwUndo
+{
+ OUString m_sTableName;
+ std::unique_ptr<SwDDEFieldType> m_pDDEFieldType;
+ std::unique_ptr<SaveTable> m_pTableSave;
+ SwTableToTextSaves m_vBoxSaves;
+ std::unique_ptr<SwHistory> m_pHistory;
+ SwNodeOffset m_nStartNode, m_nEndNode;
+ sal_Unicode m_cSeparator;
+ sal_uInt16 m_nHeadlineRepeat;
+ bool m_bCheckNumFormat : 1;
+
+public:
+ SwUndoTableToText( const SwTable& rTable, sal_Unicode cCh );
+
+ virtual ~SwUndoTableToText() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetRange( const SwNodeRange& );
+ void AddBoxPos( SwDoc& rDoc, SwNodeOffset nNdIdx, SwNodeOffset nEndIdx,
+ sal_Int32 nContentIdx = SAL_MAX_INT32);
+};
+
+class SwUndoAttrTable final : public SwUndo
+{
+ SwNodeOffset m_nStartNode;
+ std::unique_ptr<SaveTable> m_pSaveTable;
+ bool m_bClearTableCol : 1;
+
+public:
+ SwUndoAttrTable( const SwTableNode& rTableNd, bool bClearTabCols = false );
+
+ virtual ~SwUndoAttrTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+class SwUndoTableNumFormat;
+
+class SwUndoTableAutoFormat final : public SwUndo
+{
+ OUString m_TableStyleName;
+ SwNodeOffset m_nStartNode;
+ std::unique_ptr<SaveTable> m_pSaveTable;
+ std::vector< std::shared_ptr<SwUndoTableNumFormat> > m_Undos;
+ bool m_bSaveContentAttr;
+ sal_uInt16 m_nRepeatHeading;
+
+ void UndoRedo(bool const bUndo, ::sw::UndoRedoContext & rContext);
+
+public:
+ SwUndoTableAutoFormat( const SwTableNode& rTableNd, const SwTableAutoFormat& );
+
+ virtual ~SwUndoTableAutoFormat() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ void SaveBoxContent( const SwTableBox& rBox );
+};
+
+using SwUndoSaveSections = std::vector<std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>>>;
+
+class SwUndoTableNdsChg final : public SwUndo
+{
+ std::unique_ptr<SaveTable> m_pSaveTable;
+ std::set<SwNodeOffset> m_Boxes;
+ struct BoxMove
+ {
+ SwNodeOffset index; ///< Index of this box.
+ bool hasMoved; ///< Has this box been moved already.
+ BoxMove(SwNodeOffset idx, bool moved=false) : index(idx), hasMoved(moved) {};
+ bool operator<(const BoxMove& other) const { return index < other.index; };
+ };
+ std::optional< std::set<BoxMove> > m_xNewSttNds;
+ std::unique_ptr<SwUndoSaveSections> m_pDelSects;
+ tools::Long m_nMin, m_nMax; // for redo of delete column
+ SwNodeOffset m_nSttNode;
+ sal_uInt16 m_nCount;
+ bool m_bFlag;
+ bool m_bSameHeight; // only used for SplitRow
+
+ SwUndoTableNdsChg(SwUndoTableNdsChg const&) = delete;
+ SwUndoTableNdsChg& operator=(SwUndoTableNdsChg const&) = delete;
+
+public:
+ SwUndoTableNdsChg( SwUndoId UndoId,
+ const SwSelBoxes& rBoxes,
+ const SwTableNode& rTableNd,
+ tools::Long nMn, tools::Long nMx,
+ sal_uInt16 nCnt, bool bFlg, bool bSameHeight );
+
+ virtual ~SwUndoTableNdsChg() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ void SaveNewBoxes( const SwTableNode& rTableNd, const SwTableSortBoxes& rOld );
+ void SaveNewBoxes( const SwTableNode& rTableNd, const SwTableSortBoxes& rOld,
+ const SwSelBoxes& rBoxes, const std::vector<SwNodeOffset> &rNodeCnts );
+ void SaveSection( SwStartNode* pSttNd );
+ void ReNewBoxes( const SwSelBoxes& rBoxes );
+
+};
+
+class SwUndoMove;
+
+class SwUndoTableMerge final : public SwUndo, private SwUndRng
+{
+ SwNodeOffset m_nTableNode;
+ std::unique_ptr<SaveTable> m_pSaveTable;
+ std::set<SwNodeOffset> m_Boxes;
+ std::vector<SwNodeOffset> m_aNewStartNodes;
+ std::vector<std::unique_ptr<SwUndoMove>> m_vMoves;
+ std::unique_ptr<SwHistory> m_pHistory;
+
+public:
+ SwUndoTableMerge( const SwPaM& rTableSel );
+
+ virtual ~SwUndoTableMerge() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ void MoveBoxContent( SwDoc& rDoc, SwNodeRange& rRg, SwNodeIndex& rPos );
+
+ void SetSelBoxes( const SwSelBoxes& rBoxes );
+
+ void AddNewBox( SwNodeOffset nSttNdIdx )
+ { m_aNewStartNodes.push_back( nSttNdIdx ); }
+
+ void SaveCollection( const SwTableBox& rBox );
+};
+
+class SwUndoTableNumFormat final : public SwUndo
+{
+ std::unique_ptr<SfxItemSet> m_pBoxSet;
+ std::unique_ptr<SwHistory> m_pHistory;
+ OUString m_aStr, m_aNewFormula;
+
+ sal_uLong m_nFormatIdx, m_nNewFormatIdx;
+ double m_fNum, m_fNewNum;
+ SwNodeOffset m_nNode;
+ SwNodeOffset m_nNodePos;
+
+ bool m_bNewFormat : 1;
+ bool m_bNewFormula : 1;
+ bool m_bNewValue : 1;
+
+public:
+ SwUndoTableNumFormat( const SwTableBox& rBox, const SfxItemSet* pNewSet = nullptr );
+
+ virtual ~SwUndoTableNumFormat() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ void SetNumFormat( sal_uLong nNewNumFormatIdx, const double& rNewNumber )
+ { m_nFormatIdx = nNewNumFormatIdx; m_fNum = rNewNumber; }
+ void SetBox( const SwTableBox& rBox );
+};
+
+struct UndoTableCpyTable_Entry;
+
+class SwUndoTableCpyTable final : public SwUndo
+{
+ std::vector<std::unique_ptr<UndoTableCpyTable_Entry>> m_vArr;
+ std::unique_ptr<SwUndoTableNdsChg> m_pInsRowUndo;
+
+ //b6341295: When redlining is active, PrepareRedline has to create the
+ //redlining attributes for the new and the old table cell content
+ static std::unique_ptr<SwUndo> PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox,
+ SwPosition& rPos, bool& rJoin, bool bRedo );
+
+public:
+ SwUndoTableCpyTable(const SwDoc& rDoc);
+
+ virtual ~SwUndoTableCpyTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ void AddBoxBefore( const SwTableBox& rBox, bool bDelContent );
+ void AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex& rIdx,
+ bool bDelContent );
+
+ bool IsEmpty() const;
+ bool InsertRow( SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt );
+ void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+};
+
+class SwUndoCpyTable final : public SwUndo
+{
+ std::unique_ptr<SwUndoDelete> m_pDelete;
+ SwNodeOffset m_nTableNode;
+
+public:
+ SwUndoCpyTable(const SwDoc& rDoc);
+
+ virtual ~SwUndoCpyTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ void SetTableSttIdx( SwNodeOffset nIdx ) { m_nTableNode = nIdx; }
+};
+
+class SwUndoSplitTable final : public SwUndo
+{
+ SwNodeOffset m_nTableNode, m_nOffset;
+ std::unique_ptr<SwSaveRowSpan> mpSaveRowSpan; // stores row span values at the splitting row
+ std::unique_ptr<SaveTable> m_pSavedTable;
+ std::unique_ptr<SwHistory> m_pHistory;
+ SplitTable_HeadlineOption const m_nMode;
+ sal_uInt16 m_nFormulaEnd;
+ bool m_bCalcNewSize;
+
+public:
+ SwUndoSplitTable( const SwTableNode& rTableNd, std::unique_ptr<SwSaveRowSpan> pRowSp,
+ SplitTable_HeadlineOption nMode, bool bCalcNewSize );
+
+ virtual ~SwUndoSplitTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SetTableNodeOffset( SwNodeOffset nIdx ) { m_nOffset = nIdx - m_nTableNode; }
+ SwHistory* GetHistory() { return m_pHistory.get(); }
+ void SaveFormula( SwHistory& rHistory );
+};
+
+class SwUndoMergeTable final : public SwUndo
+{
+ OUString m_aName;
+ SwNodeOffset m_nTableNode;
+ std::unique_ptr<SaveTable> m_pSaveTable, m_pSaveHdl;
+ std::unique_ptr<SwHistory> m_pHistory;
+ sal_uInt16 m_nMode;
+ bool m_bWithPrev;
+
+public:
+ SwUndoMergeTable( const SwTableNode& rTableNd, const SwTableNode& rDelTableNd,
+ bool bWithPrev, sal_uInt16 nMode );
+
+ virtual ~SwUndoMergeTable() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+
+ void SaveFormula( SwHistory& rHistory );
+};
+
+class SwUndoTableHeadline final : public SwUndo
+{
+ SwNodeOffset m_nTableNode;
+ sal_uInt16 m_nOldHeadline;
+ sal_uInt16 m_nNewHeadline;
+
+public:
+ SwUndoTableHeadline( const SwTable&, sal_uInt16 nOldHdl, sal_uInt16 nNewHdl );
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RepeatImpl( ::sw::RepeatContext & ) override;
+};
+
+void InsertSort( std::vector<sal_uInt16>& rArr, sal_uInt16 nIdx );
+
+class SwUndoTableStyleMake final : public SwUndo
+{
+ OUString m_sName;
+ std::unique_ptr<SwTableAutoFormat> m_pAutoFormat;
+public:
+ SwUndoTableStyleMake(const OUString& rName, const SwDoc& rDoc);
+
+ virtual ~SwUndoTableStyleMake() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoTableStyleDelete final : public SwUndo
+{
+ std::unique_ptr<SwTableAutoFormat> m_pAutoFormat;
+ std::vector<SwTable*> m_rAffectedTables;
+public:
+ SwUndoTableStyleDelete(std::unique_ptr<SwTableAutoFormat> pAutoFormat, std::vector<SwTable*>&& rAffectedTables, const SwDoc& rDoc);
+
+ virtual ~SwUndoTableStyleDelete() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+class SwUndoTableStyleUpdate final : public SwUndo
+{
+ std::unique_ptr<SwTableAutoFormat> m_pOldFormat, m_pNewFormat;
+public:
+ SwUndoTableStyleUpdate(const SwTableAutoFormat& rNewFormat, const SwTableAutoFormat& rOldFormat, const SwDoc& rDoc);
+
+ virtual ~SwUndoTableStyleUpdate() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOTABLE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/acorrect.hxx b/sw/source/core/inc/acorrect.hxx
new file mode 100644
index 000000000..884e749e0
--- /dev/null
+++ b/sw/source/core/inc/acorrect.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ACORRECT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ACORRECT_HXX
+
+#include <memory>
+
+#include <svl/itemset.hxx>
+#include <editeng/svxacorr.hxx>
+#include <nodeoffset.hxx>
+
+class SwEditShell;
+class SwPaM;
+class SwNodeIndex;
+struct SwPosition;
+class SfxItemSet;
+
+class SwDontExpandItem
+{
+ std::unique_ptr<SfxItemSet> m_pDontExpandItems;
+
+public:
+ SwDontExpandItem() {}
+ ~SwDontExpandItem();
+
+ void SaveDontExpandItems( const SwPosition& rPos );
+ void RestoreDontExpandItems( const SwPosition& rPos );
+
+};
+
+class SwAutoCorrDoc final : public SvxAutoCorrDoc
+{
+ SwEditShell& m_rEditSh;
+ SwPaM& m_rCursor;
+ std::unique_ptr<SwNodeIndex> m_pIndex;
+ int m_nEndUndoCounter;
+ bool m_bUndoIdInitialized;
+
+ void DeleteSel( SwPaM& rDelPam );
+ void DeleteSelImpl(SwPaM & rDelPam);
+
+public:
+ SwAutoCorrDoc( SwEditShell& rEditShell, SwPaM& rPam, sal_Unicode cIns = 0 );
+ virtual ~SwAutoCorrDoc() override;
+
+ virtual bool Delete( sal_Int32 nStt, sal_Int32 nEnd ) override;
+ virtual bool Insert( sal_Int32 nPos, const OUString& rText ) override;
+ virtual bool Replace( sal_Int32 nPos, const OUString& rText ) override;
+ virtual bool ReplaceRange( sal_Int32 nPos, sal_Int32 nLen, const OUString& rText ) override;
+
+ virtual void SetAttr( sal_Int32 nStt, sal_Int32 nEnd, sal_uInt16 nSlotId,
+ SfxPoolItem& ) override;
+
+ virtual bool SetINetAttr( sal_Int32 nStt, sal_Int32 nEnd, const OUString& rURL ) override;
+
+ // return text of a previous paragraph
+ // If it does not exist or if there is nothing before, return blank.
+ // - true: paragraph before "normal" insertion position
+ // - false: paragraph in that the corrected word was inserted
+ // (does not need to be the same paragraph)
+ virtual OUString const* GetPrevPara(bool bAtNormalPos) override;
+
+ virtual bool ChgAutoCorrWord( sal_Int32& rSttPos, sal_Int32 nEndPos,
+ SvxAutoCorrect& rACorrect,
+ OUString* pPara ) override;
+ virtual bool TransliterateRTLWord( sal_Int32& rSttPos, sal_Int32 nEndPos,
+ bool bApply = false ) override;
+
+ // Will be called after swapping characters by the functions
+ // - FnCapitalStartWord and
+ // - FnCapitalStartSentence.
+ // Afterwards the words can be added into exception list if needed.
+ virtual void SaveCpltSttWord( ACFlags nFlag, sal_Int32 nPos,
+ const OUString& rExceptWord, sal_Unicode cChar ) override;
+ virtual LanguageType GetLanguage( sal_Int32 nPos ) const override;
+};
+
+class SwAutoCorrExceptWord
+{
+ OUString m_sWord;
+ SwNodeOffset m_nNode;
+ ACFlags m_nFlags;
+ sal_Int32 m_nContent;
+ sal_Unicode m_cChar;
+ LanguageType m_eLanguage;
+ bool m_bDeleted;
+
+public:
+ SwAutoCorrExceptWord(ACFlags nAFlags, SwNodeOffset nNd, sal_Int32 nContent,
+ const OUString& rWord, sal_Unicode cChr,
+ LanguageType eLang)
+ : m_sWord(rWord), m_nNode(nNd), m_nFlags(nAFlags), m_nContent(nContent),
+ m_cChar(cChr), m_eLanguage(eLang), m_bDeleted(false)
+ {}
+
+ bool IsDeleted() const { return m_bDeleted; }
+ void CheckChar(const SwPosition& rPos, sal_Unicode cChar);
+ bool CheckDelChar(const SwPosition& rPos);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/anchoredobjectposition.hxx b/sw/source/core/inc/anchoredobjectposition.hxx
new file mode 100644
index 000000000..a36683300
--- /dev/null
+++ b/sw/source/core/inc/anchoredobjectposition.hxx
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ANCHOREDOBJECTPOSITION_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ANCHOREDOBJECTPOSITION_HXX
+
+#include <swtypes.hxx>
+#include "frame.hxx"
+#include <nodeoffset.hxx>
+
+class SdrObject;
+class SwFlyFrame;
+class SwContact;
+class SwFrameFormat;
+class SwRect;
+class SvxLRSpaceItem;
+class SvxULSpaceItem;
+class SwFormatHoriOrient;
+class SwAnchoredObject;
+
+namespace objectpositioning
+{
+ class SwEnvironmentOfAnchoredObject;
+
+ class SwAnchoredObjectPosition
+ {
+ private:
+ // object to be positioned
+ SdrObject& mrDrawObj;
+ // #i26791# - anchored object the object belongs to;
+ SwAnchoredObject* mpAnchoredObj;
+ // frame the object is anchored at
+ SwFrame* mpAnchorFrame;
+ // contact object
+ SwContact* mpContact;
+ // frame format
+ const SwFrameFormat* mpFrameFormat;
+ // does the object represents a Writer fly frame
+ bool mbIsObjFly;
+ // #i62875#
+ bool mbFollowTextFlow;
+ // #i62875#
+ // for compatibility option <DoNotCaptureDrawObjsOnPage>
+ bool mbDoNotCaptureAnchoredObj;
+
+ /** determine information about object
+
+ member <mbIsObjFly>, <mpAnchoredObj>, <mpAnchorFrame>, <mpContact>
+ and <mpFrameFormat> are set
+ */
+ void GetInfoAboutObj();
+
+ // #i62875#
+ // --> OD 2009-09-01 #mongolianlayout# - add parameter <bVertL2R>
+ SwTwips ImplAdjustVertRelPos( const SwTwips nTopOfAnch,
+ const bool bVert,
+ const bool bVertL2R,
+ const SwFrame& rPageAlignLayFrame,
+ const SwTwips nProposedRelPosY,
+ const bool bFollowTextFlow,
+ const bool bCheckBottom ) const;
+ SwTwips ImplAdjustHoriRelPos( const SwFrame& _rPageAlignLayFrame,
+ const SwTwips _nProposedRelPosX ) const;
+
+ protected:
+ SwAnchoredObjectPosition( SdrObject& _rDrawObj );
+ virtual ~SwAnchoredObjectPosition();
+
+ // accessors for object and its corresponding data/information
+ SdrObject& GetObject() const
+ {
+ return mrDrawObj;
+ }
+ bool IsObjFly() const
+ {
+ return mbIsObjFly;
+ }
+ SwAnchoredObject& GetAnchoredObj() const
+ {
+ return *mpAnchoredObj;
+ }
+ SwFrame& GetAnchorFrame() const
+ {
+ return *mpAnchorFrame;
+ }
+ const SwFrameFormat& GetFrameFormat() const
+ {
+ return *mpFrameFormat;
+ }
+ // #i62875#
+ bool DoesObjFollowsTextFlow() const
+ {
+ return mbFollowTextFlow;
+ }
+
+ // virtual methods providing data for to character anchored objects.
+ virtual bool IsAnchoredToChar() const;
+ virtual const SwFrame* ToCharOrientFrame() const;
+ virtual const SwRect* ToCharRect() const;
+ // #i22341#
+ virtual SwTwips ToCharTopOfLine() const;
+
+ /** helper method to determine top of a frame for the vertical object
+ positioning
+
+ #i11860#
+ */
+ SwTwips GetTopForObjPos( const SwFrame& _rFrame,
+ const SwRectFn& _fnRect,
+ const bool _bVert ) const;
+
+ void GetVertAlignmentValues( const SwFrame& _rVertOrientFrame,
+ const SwFrame& _rPageAlignLayFrame,
+ const sal_Int16 _eRelOrient,
+ SwTwips& _orAlignAreaHeight,
+ SwTwips& _orAlignAreaOffset ) const;
+
+ // #i26791# - add output parameter <_roVertOffsetToFrameAnchorPos>
+ SwTwips GetVertRelPos( const SwFrame& _rVertOrientFrame,
+ const SwFrame& _rPageAlignLayFrame,
+ const sal_Int16 _eVertOrient,
+ const sal_Int16 _eRelOrient,
+ const SwTwips _nVertPos,
+ const SvxLRSpaceItem& _rLRSpacing,
+ const SvxULSpaceItem& _rULSpacing,
+ SwTwips& _roVertOffsetToFrameAnchorPos ) const;
+
+ /** adjust calculated vertical in order to keep object inside
+ 'page' alignment layout frame.
+
+ #i31805# - add parameter <_bCheckBottom>
+ #i26945# - add parameter <_bFollowTextFlow>
+ #i62875# - made inline, intrinsic actions moved
+ to private method <ImplAdjustVertRelPos>, which is only
+ called, if <mbDoNotCaptureAnchoredObj> not set.
+ OD 2009-09-01 #mongolianlayout# - add parameter <bVertL2R>
+
+ @param nTopOfAnch
+ input parameter - 'vertical' position, at which the relative
+ position of the object is calculated from.
+
+ @param bVert
+ input parameter - boolean, indicating, if object is in vertical
+ layout.
+
+ @param bVertL2R
+ input parameter - boolean, indicating, if object is in mongolian
+ layout (vertical left-to-right layout).
+
+ @param rPageAlignLayFrame
+ input parameter - layout frame, which determines the 'page area'
+ the object has to be vertical positioned in.
+
+ @param nProposedRelPosY
+ input parameter - proposed relative vertical position, which
+ will be adjusted.
+
+ @param bFollowTextFlow
+ input parameter - value of attribute 'Follow text flow' of the
+ anchored object.
+
+ @param bCheckBottom
+ input parameter - boolean indicating, if bottom of anchored
+ object has to be checked and thus, (if needed) the proposed
+ relative position has to be adjusted. default value <true>
+ */
+ SwTwips AdjustVertRelPos( const SwTwips nTopOfAnch,
+ const bool bVert,
+ const bool bVertL2R,
+ const SwFrame& rPageAlignLayFrame,
+ const SwTwips nProposedRelPosY,
+ const bool bFollowTextFlow,
+ const bool bCheckBottom = true ) const
+ {
+ return !mbDoNotCaptureAnchoredObj
+ ? ImplAdjustVertRelPos( nTopOfAnch, bVert, bVertL2R,
+ rPageAlignLayFrame,
+ nProposedRelPosY,
+ bFollowTextFlow,
+ bCheckBottom )
+ : nProposedRelPosY;
+ }
+
+ /** calculate relative horizontal position
+
+ #i26791# - add output parameter
+ <_roHoriOffsetToFrameAnchorPos>
+
+ @param _rHoriOrientFrame
+ input parameter - frame the horizontal position of the object
+ is oriented at.
+
+ @param _rEnvOfObj
+ input parameter - object instance to retrieve environment
+ information about the object
+
+ @param _rHoriOrient
+ input parameter - horizontal positioning and alignment, for which
+ the relative position is calculated.
+
+ @param _rLRSpacing
+ input parameter - left and right spacing of the object to the text
+
+ @param _rULSpacing
+ input parameter - upper and lower spacing of the object to the text
+
+ @param _bObjWrapThrough
+ input parameter - boolean indicating, if object has wrap mode
+ 'wrap through'.
+
+ @param _nRelPosY
+ input parameter - relative vertical position
+
+ @param _roHoriOffsetToFrameAnchorPos
+ output parameter - 'horizontal' offset to frame anchor position
+ according to the alignment
+
+ @return relative horizontal position in SwTwips
+ */
+ SwTwips CalcRelPosX( const SwFrame& _rHoriOrientFrame,
+ const SwEnvironmentOfAnchoredObject& _rEnvOfObj,
+ const SwFormatHoriOrient& _rHoriOrient,
+ const SvxLRSpaceItem& _rLRSpacing,
+ const SvxULSpaceItem& _rULSpacing,
+ const bool _bObjWrapThrough,
+ const SwTwips _nRelPosY,
+ SwTwips& _roHoriOffsetToFrameAnchorPos
+ ) const;
+
+ /** adjust calculated horizontal in order to keep object inside
+ 'page' alignment layout frame for object type position TO_CNTNT
+
+ #i62875# - made inline, intrinsic actions moved
+ to private method <ImplAdjustHoriRelPos>, which is only
+ called, if <mbDoNotCaptureAnchoredObj> not set.
+
+ @param _rPageAlignLayFrame
+ input parameter - layout frame, which determines the 'page area'
+ the object has to be horizontal positioned in.
+
+ @param _nProposedRelPosX
+ input parameter - proposed relative horizontal position, which
+ will be adjusted.
+
+ @return adjusted relative horizontal position in SwTwips.
+ */
+ SwTwips AdjustHoriRelPos( const SwFrame& _rPageAlignLayFrame,
+ const SwTwips _nProposedRelPosX ) const
+ {
+ return !mbDoNotCaptureAnchoredObj
+ ? ImplAdjustHoriRelPos( _rPageAlignLayFrame, _nProposedRelPosX )
+ : _nProposedRelPosX;
+ }
+
+ /** toggle given horizontal orientation and relative alignment
+
+ @param _bToggleLeftRight
+ input parameter - boolean indicating, if horizontal orientation
+ and relative alignment has to be toggled.
+
+ @param _ioeHoriOrient
+ input/output parameter - horizontal orientation, that is toggled,
+ if needed.
+
+ @param _iopeRelOrient
+ optional input/output parameter (default value NULL)
+ - if set, relative alignment, that is toggled, if needed.
+ */
+ static void ToggleHoriOrientAndAlign( const bool _bToggleLeftRight,
+ sal_Int16& _ioeHoriOrient,
+ sal_Int16& _iopeRelOrient
+ );
+
+ /** determine alignment values for horizontal position of object
+
+ @param _rHoriOrientFrame
+ input parameter - frame the horizontal position of the object
+ is oriented at.
+
+ @param _rPageAlignLayFrame
+ input parameter - layout frame, which determines the 'page area'
+ the object has to be horizontal positioned in.
+
+ @param _eRelOrient
+ input parameter - horizontal relative alignment, for which
+ the relative position is calculated.
+
+ @param _bToggleLeftRight
+ input parameter - boolean indicating, if left/right alignments
+ have to be toggled.
+
+ @param _bObjWrapThrough
+ input parameter - boolean indicating, if object has wrap mode
+ 'wrap through'.
+ important note: value is only relevant, if _rHoriOrientFrame is
+ a text frame.
+
+ @param _orAlignAreaWidth
+ output parameter - width in SwTwips of the area the horizontal
+ position is aligned to.
+
+ @param _orAlignAreaOffset
+ output parameter - offset in SwTwips of the area the horizontal
+ position is aligned to. offset is given to the 'left' of the
+ anchor position.
+
+ @param _obAlignedRelToPage
+ output parameter - boolean indicating, that object is aligned
+ to 'page area'.
+ */
+ void GetHoriAlignmentValues( const SwFrame& _rHoriOrientFrame,
+ const SwFrame& _rPageAlignLayFrame,
+ const sal_Int16 _eRelOrient,
+ const bool _bObjWrapThrough,
+ SwTwips& _orAlignAreaWidth,
+ SwTwips& _orAlignAreaOffset,
+ bool& _obAlignedRelToPage ) const;
+
+ /** adjust calculated horizontal position in order to draw object
+ aside other objects with same positioning
+
+ @param _rHoriOrientFrame
+ input parameter - frame the horizontal position of the object
+ is oriented at.
+
+ @param _nProposedRelPosX
+ input parameter - proposed relative horizontal position, which
+ will be adjusted.
+
+ @param _nRelPosY
+ input parameter - relative vertical position
+
+ @param _eHoriOrient
+ input parameter - horizontal position of object
+
+ @param _eRelOrient
+ input parameter - alignment of object
+
+ @param _rLRSpacing
+ input parameter - left and right spacing of the object to the text
+
+ @param _rULSpacing
+ input parameter - upper and lower spacing of the object to the text
+
+ @param _bEvenPage
+ input parameter - boolean indicating, if object is on an even page.
+
+ @return adjusted relative horizontal position in SwTwips
+ */
+ SwTwips AdjustHoriRelPosForDrawAside( const SwFrame& _rHoriOrientFrame,
+ const SwTwips _nProposedRelPosX,
+ const SwTwips _nRelPosY,
+ const sal_Int16 _eHoriOrient,
+ const sal_Int16 _eRelOrient,
+ const SvxLRSpaceItem& _rLRSpacing,
+ const SvxULSpaceItem& _rULSpacing,
+ const bool _bEvenPage
+ ) const;
+
+ /** determine, if object has to draw aside given fly frame
+
+ method used by <AdjustHoriRelPosForDrawAside(..)>
+
+ @param _pFly
+ input parameter - fly frame the draw aside check is done for.
+
+ @param _rObjRect
+ input parameter - proposed object rectangle
+
+ @param _pObjContext
+ input parameter - context of the object
+
+ @param _nObjIndex
+ input parameter - index of the anchor frame of the object
+
+ @param _bEvenPage
+ input parameter - boolean indicating, if object is on an even page.
+
+ @param _eHoriOrient
+ input parameter - horizontal position of object
+
+ @param _eRelOrient
+ input parameter - alignment of object
+
+ @return boolean indicating, if object has to be drawn aside
+ given fly frame.
+ */
+ bool DrawAsideFly( const SwFlyFrame* _pFly,
+ const SwRect& _rObjRect,
+ const SwFrame* _pObjContext,
+ const SwNodeOffset _nObjIndex,
+ const bool _bEvenPage,
+ const sal_Int16 _eHoriOrient,
+ const sal_Int16 _eRelOrient
+ ) const;
+
+ /** determine, if object has to draw aside another object
+
+ the different alignments of the objects determines, if one has
+ to draw aside another one. Thus, the given alignment are checked
+ against each other, which one has to be drawn aside the other one.
+ depending on parameter _bLeft check is done for left or right
+ positioning.
+ method used by <DrawAsideFly(..)>
+
+ @param _eRelOrient1
+ input parameter - alignment 1
+
+ @param _eRelOrient2
+ input parameter - alignment 2
+
+ @param _bLeft
+ input parameter - boolean indicating, if check is done for left
+ or for right positioning.
+
+ @return boolean indicating, if an object with an alignment
+ <_eRelOrient1> has to be drawn aside an object with an
+ alignment <_eRelOrient2>
+ */
+ static bool Minor_( sal_Int16 _eRelOrient1,
+ sal_Int16 _eRelOrient2,
+ bool _bLeft );
+
+ public:
+ virtual void CalcPosition() = 0;
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/annotationmark.hxx b/sw/source/core/inc/annotationmark.hxx
new file mode 100644
index 000000000..c4c2bc128
--- /dev/null
+++ b/sw/source/core/inc/annotationmark.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ANNOTATIONMARK_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ANNOTATIONMARK_HXX
+
+#include "bookmark.hxx"
+#include <rtl/ustring.hxx>
+
+class SwFormatField;
+
+namespace sw::mark
+{
+ class AnnotationMark final : public MarkBase
+ {
+ public:
+ AnnotationMark(
+ const SwPaM& rPaM,
+ const OUString& rName );
+
+ virtual ~AnnotationMark() override;
+
+ virtual void InitDoc(SwDoc& io_Doc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override;
+
+ const SwFormatField* GetAnnotationFormatField() const;
+ };
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/ascharanchoredobjectposition.hxx b/sw/source/core/inc/ascharanchoredobjectposition.hxx
new file mode 100644
index 000000000..34f42e364
--- /dev/null
+++ b/sw/source/core/inc/ascharanchoredobjectposition.hxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ASCHARANCHOREDOBJECTPOSITION_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ASCHARANCHOREDOBJECTPOSITION_HXX
+
+#include "anchoredobjectposition.hxx"
+#include <swtypes.hxx>
+#include <swrect.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+class SwTextFrame;
+class SwFormatVertOrient;
+
+// flags for positioning algorithm of as-character-anchored objects
+enum class AsCharFlags {
+ None = 0x00,
+ Quick = 0x01,
+ UlSpace = 0x02,
+ Init = 0x04,
+ Rotate = 0x08,
+ Reverse = 0x10,
+ Bidi = 0x20,
+};
+namespace o3tl {
+ template<> struct typed_flags<AsCharFlags> : is_typed_flags<AsCharFlags, 0x3f> {};
+};
+
+namespace sw
+{
+ // TODO: merge/migrate this to com::sun::star::VertOrientation instead of duplicating?
+ enum class LineAlign
+ {
+ NONE,
+ TOP,
+ CENTER,
+ BOTTOM
+ };
+};
+namespace objectpositioning
+{
+ class SwAsCharAnchoredObjectPosition final : public SwAnchoredObjectPosition
+ {
+ private:
+ // data to calculate object position
+ // Proposed anchor position, starting point for the calculation
+ // of the object position.
+ const Point& mrProposedAnchorPos;
+ // flags that influences the calculation of the anchor position
+ // AsCharFlags::Quick : quick formatting - calculated position not set at object
+ // AsCharFlags::UlSpace : consider upper/lower spacing - adjustment of anchor position
+ // AsCharFlags::Init : initial calculation
+ // AsCharFlags::Rotate : object is rotated by 90 degrees
+ // AsCharFlags::Reverse : object is reversed (rotated by 270 degrees)
+ // AsCharFlags::Bidi : object belongs to a BIDI-multi-portion
+ const AsCharFlags mnFlags;
+ // needed line values for the different alignments.
+ const SwTwips mnLineAscent;
+ const SwTwips mnLineDescent;
+ const SwTwips mnLineAscentInclObjs;
+ const SwTwips mnLineDescentInclObjs;
+
+ // calculated data for object position
+ Point maAnchorPos;
+ SwTwips mnRelPos;
+ SwRect maObjBoundRect;
+ // line alignment relative to line height
+ sw::LineAlign mnLineAlignment;
+
+ // method to cast <SwAnchoredObjectPosition::GetAnchorFrame()>
+ const SwTextFrame& GetAnchorTextFrame() const;
+
+ /** determine the relative position to base line for object position
+
+ @param _ObjBoundHeight
+ height including corresponding spacing of the object, for which
+ the Y-position has to be calculated.
+
+ @param _rVert
+ given vertical positioning and alignment
+
+ @return relative position to the base line
+ */
+ SwTwips GetRelPosToBase( const SwTwips _nObjBoundHeight,
+ const SwFormatVertOrient& _rVert );
+
+ public:
+ /** constructor; provided object to be positioned and needed data
+ for calculation of the object position
+
+ @param _rDrawObj
+ input parameter - object, that is be positioned.
+
+ @param _rProposedAnchorPos
+ proposed anchor position; starting point for the calculation
+ of the anchor position
+
+ @param _nFlags
+ flags that influences the calculation of the anchor position
+ AsCharFlags::Quick : quick formatting - calculated position not set at object
+ AsCharFlags::UlSpace : consider upper/lower spacing - adjustment of anchor position
+ AsCharFlags::Init : initial calculation
+ AsCharFlags::Rotate : object is rotated by 90 degrees
+ AsCharFlags::Reverse : object is reversed (rotated by 270 degrees)
+ AsCharFlags::Bidi : object belongs to a BIDI-multi-portion
+
+ @param _nLineAscent, _nLineDescent, _nLineAscentInclObjs,
+ _nLineDescentInclObjs - needed line values for the different
+ alignments.
+ */
+ SwAsCharAnchoredObjectPosition( SdrObject& _rDrawObj,
+ const Point& _rProposedAnchorPos,
+ const AsCharFlags _nFlags,
+ const SwTwips _nLineAscent,
+ const SwTwips _nLineDescent,
+ const SwTwips _nLineAscentInclObjs,
+ const SwTwips _nLineDescentInclObjs );
+ virtual ~SwAsCharAnchoredObjectPosition() override;
+
+ /** calculate position for object position
+
+ members <maAnchorPos>, <mnRelPos>, <maObjBoundRect> and
+ <mnLineAlignment> are calculated.
+ calculated position is set at the given object.
+ */
+ virtual void CalcPosition() override;
+
+ // calculated anchored position for object position type AS_CHAR
+ const Point& GetAnchorPos() const { return maAnchorPos;}
+
+ // calculated relative position to base line for object position type AS_CHAR
+ SwTwips GetRelPosY() const { return mnRelPos;}
+
+ // determined object rectangle including spacing for object position type AS_CHAR
+ const SwRect& GetObjBoundRectInclSpacing() const { return maObjBoundRect;}
+
+ // determined line alignment relative to line height
+ sw::LineAlign GetLineAlignment() const { return mnLineAlignment;}
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/attrhint.hxx b/sw/source/core/inc/attrhint.hxx
new file mode 100644
index 000000000..e098b2695
--- /dev/null
+++ b/sw/source/core/inc/attrhint.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ATTRHINT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ATTRHINT_HXX
+
+#include <svl/hint.hxx>
+
+class SwAttrHint final : public SfxHint
+{
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/bodyfrm.hxx b/sw/source/core/inc/bodyfrm.hxx
new file mode 100644
index 000000000..436b8fbb4
--- /dev/null
+++ b/sw/source/core/inc/bodyfrm.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_BODYFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_BODYFRM_HXX
+
+#include "layfrm.hxx"
+
+class SwBorderAttrs;
+
+/// Container of body content (i.e. not header or footer). Typical parent is an
+/// SwPageFrame, typical lower is an SwTextFrame.
+class SAL_DLLPUBLIC_RTTI SwBodyFrame final : public SwLayoutFrame
+{
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+
+public:
+ SwBodyFrame( SwFrameFormat*, SwFrame* );
+
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/bookmark.hxx b/sw/source/core/inc/bookmark.hxx
new file mode 100644
index 000000000..a9c807063
--- /dev/null
+++ b/sw/source/core/inc/bookmark.hxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/weakref.hxx>
+#include <sfx2/Metadatable.hxx>
+#include <vcl/keycod.hxx>
+#include <memory>
+#include <string_view>
+#include <com/sun/star/text/XTextContent.hpp>
+
+#include <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+#include <tools/ref.hxx>
+#include <IMark.hxx>
+#include <swrect.hxx>
+#include "FormFieldButton.hxx"
+
+class SwDoc;
+class SwEditWin;
+class SwServerObject;
+class SvNumberFormatter;
+class SfxViewShell;
+
+namespace sw::mark {
+ class MarkBase
+ : virtual public IMark
+ {
+ public:
+ //getters
+ virtual SwPosition& GetMarkPos() const override
+ { return *m_pPos1; }
+ virtual const OUString& GetName() const override
+ { return m_aName; }
+ virtual SwPosition& GetOtherMarkPos() const override
+ {
+ OSL_PRECOND(IsExpanded(), "<SwPosition::GetOtherMarkPos(..)> - I have no other Pos set." );
+ return *m_pPos2;
+ }
+ virtual SwPosition& GetMarkStart() const override
+ {
+ if( !IsExpanded() ) return GetMarkPos( );
+ if ( GetMarkPos( ) < GetOtherMarkPos( ) )
+ return GetMarkPos();
+ else
+ return GetOtherMarkPos( );
+ }
+ virtual SwPosition& GetMarkEnd() const override
+ {
+ if( !IsExpanded() ) return GetMarkPos();
+ if ( GetMarkPos( ) >= GetOtherMarkPos( ) )
+ return GetMarkPos( );
+ else
+ return GetOtherMarkPos( );
+ }
+
+ virtual bool IsCoveringPosition(const SwPosition& rPos) const override;
+ virtual bool IsExpanded() const override
+ { return static_cast< bool >(m_pPos2); }
+
+ void SetName(const OUString& rName)
+ { m_aName = rName; }
+ virtual void SetMarkPos(const SwPosition& rNewPos);
+ virtual void SetOtherMarkPos(const SwPosition& rNewPos);
+ virtual void ClearOtherMarkPos()
+ { m_pPos2.reset(); }
+
+ virtual auto InvalidateFrames() -> void;
+
+ virtual OUString ToString( ) const override;
+ virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+
+ void Swap()
+ {
+ if(m_pPos2)
+ m_pPos1.swap(m_pPos2);
+ }
+
+ virtual void InitDoc(SwDoc&, sw::mark::InsertMode, SwPosition const*)
+ {
+ }
+
+ virtual ~MarkBase() override;
+
+ const css::uno::WeakReference< css::text::XTextContent> & GetXBookmark() const
+ { return m_wXBookmark; }
+ void SetXBookmark(css::uno::Reference< css::text::XTextContent> const& xBkmk)
+ { m_wXBookmark = xBkmk; }
+
+ protected:
+ // SwClient
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+ MarkBase(const SwPaM& rPaM, const OUString& rName);
+ std::unique_ptr<SwPosition> m_pPos1;
+ std::unique_ptr<SwPosition> m_pPos2;
+ OUString m_aName;
+ static OUString GenerateNewName(std::u16string_view rPrefix);
+
+ css::uno::WeakReference< css::text::XTextContent> m_wXBookmark;
+ };
+
+ class NavigatorReminder final
+ : public MarkBase
+ {
+ public:
+ NavigatorReminder(const SwPaM& rPaM);
+ };
+
+ class UnoMark final
+ : public MarkBase
+ {
+ public:
+ UnoMark(const SwPaM& rPaM);
+ };
+
+ class DdeBookmark
+ : public MarkBase
+ {
+ public:
+ DdeBookmark(const SwPaM& rPaM);
+
+ const SwServerObject* GetRefObject() const { return m_aRefObj.get(); }
+ SwServerObject* GetRefObject() { return m_aRefObj.get(); }
+
+ bool IsServer() const { return m_aRefObj.is(); }
+
+ void SetRefObject( SwServerObject* pObj );
+
+ virtual void DeregisterFromDoc(SwDoc& rDoc);
+ virtual ~DdeBookmark() override;
+
+ private:
+ tools::SvRef<SwServerObject> m_aRefObj;
+ };
+
+ class Bookmark
+ : virtual public IBookmark
+ , public DdeBookmark
+ , public ::sfx2::Metadatable
+ {
+ public:
+ Bookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName);
+ virtual void InitDoc(SwDoc& io_Doc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override;
+
+ virtual void DeregisterFromDoc(SwDoc& io_rDoc) override;
+
+ virtual auto InvalidateFrames() -> void override;
+
+ virtual const OUString& GetShortName() const override
+ { return m_sShortName; }
+ virtual const vcl::KeyCode& GetKeyCode() const override
+ { return m_aCode; }
+ virtual void SetShortName(const OUString& rShortName) override
+ { m_sShortName = rShortName; }
+ virtual void SetKeyCode(const vcl::KeyCode& rCode) override
+ { m_aCode = rCode; }
+ virtual bool IsHidden() const override
+ { return m_bHidden; }
+ virtual const OUString& GetHideCondition() const override
+ { return m_sHideCondition; }
+ virtual void Hide(bool rHide) override;
+ virtual void SetHideCondition(const OUString& rHideCondition) override;
+
+ // ::sfx2::Metadatable
+ virtual ::sfx2::IXmlIdRegistry& GetRegistry() override;
+ virtual bool IsInClipboard() const override;
+ virtual bool IsInUndo() const override;
+ virtual bool IsInContent() const override;
+ virtual css::uno::Reference< css::rdf::XMetadatable > MakeUnoObject() override;
+
+ private:
+ vcl::KeyCode m_aCode;
+ OUString m_sShortName;
+ bool m_bHidden;
+ OUString m_sHideCondition;
+ };
+
+ class Fieldmark
+ : virtual public IFieldmark
+ , public MarkBase
+ {
+ public:
+ Fieldmark(const SwPaM& rPaM);
+
+ virtual OUString GetFieldname() const override
+ { return m_aFieldname; }
+ virtual OUString GetFieldHelptext() const override
+ { return m_aFieldHelptext; }
+
+ virtual IFieldmark::parameter_map_t* GetParameters() override
+ { return &m_vParams; }
+
+ virtual const IFieldmark::parameter_map_t* GetParameters() const override
+ { return &m_vParams; }
+
+ virtual void SetFieldname(const OUString& aFieldname) override
+ { m_aFieldname = aFieldname; }
+ virtual void SetFieldHelptext(const OUString& aFieldHelptext) override
+ { m_aFieldHelptext = aFieldHelptext; }
+
+ virtual void ReleaseDoc(SwDoc&) = 0;
+
+ void SetMarkStartPos( const SwPosition& rNewStartPos );
+
+ virtual void Invalidate() override;
+ virtual OUString ToString() const override;
+ virtual void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+
+ private:
+ OUString m_aFieldname;
+ OUString m_aFieldHelptext;
+ IFieldmark::parameter_map_t m_vParams;
+ };
+
+ class TextFieldmark final
+ : public Fieldmark
+ {
+ public:
+ TextFieldmark(const SwPaM& rPaM, const OUString& rName);
+ virtual void InitDoc(SwDoc& io_rDoc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override;
+ virtual void ReleaseDoc(SwDoc& rDoc) override;
+ };
+
+ // Non text fieldmarks have no content between the start and end marks.
+ class NonTextFieldmark
+ : public Fieldmark
+ {
+ public:
+ NonTextFieldmark(const SwPaM& rPaM);
+ virtual void InitDoc(SwDoc& io_rDoc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override;
+ virtual void ReleaseDoc(SwDoc& rDoc) override;
+ };
+
+ /// Fieldmark representing a checkbox form field.
+ class CheckboxFieldmark final
+ : virtual public ICheckboxFieldmark
+ , public NonTextFieldmark
+ {
+ public:
+ CheckboxFieldmark(const SwPaM& rPaM);
+ bool IsChecked() const override;
+ void SetChecked(bool checked) override;
+ };
+
+ /// Fieldmark with a drop down button (e.g. this button opens the date picker for a date field)
+ class FieldmarkWithDropDownButton
+ : public NonTextFieldmark
+ {
+ public:
+ FieldmarkWithDropDownButton(const SwPaM& rPaM);
+ virtual ~FieldmarkWithDropDownButton() override;
+
+ virtual void ShowButton(SwEditWin* pEditWin) = 0;
+ virtual void RemoveButton();
+
+ protected:
+ VclPtr<FormFieldButton> m_pButton;
+ };
+
+ /// Fieldmark representing a drop-down form field.
+ class DropDownFieldmark final
+ : public FieldmarkWithDropDownButton
+ {
+ public:
+ DropDownFieldmark(const SwPaM& rPaM);
+ virtual ~DropDownFieldmark() override;
+
+ virtual void ShowButton(SwEditWin* pEditWin) override;
+ virtual void RemoveButton() override;
+
+ // This method should be called only by the portion so we can now the portion's painting area
+ void SetPortionPaintArea(const SwRect& rPortionPaintArea);
+
+ void SendLOKShowMessage(const SfxViewShell* pViewShell);
+ static void SendLOKHideMessage(const SfxViewShell* pViewShell);
+
+ private:
+ SwRect m_aPortionPaintArea;
+ };
+
+ /// Fieldmark representing a date form field.
+ /// TODO: this was an SDT in DOCX, which is modelled suboptimally here
+ /// as a fieldmark; as it cannot contain paragraph breaks, must be
+ /// well-formed XML element, and does not have field separator, it
+ /// should be a nesting text attribute similar to SwTextMeta.
+ class DateFieldmark final
+ : virtual public IDateFieldmark
+ , public FieldmarkWithDropDownButton
+ {
+ public:
+ DateFieldmark(const SwPaM& rPaM);
+ virtual ~DateFieldmark() override;
+
+ virtual void InitDoc(SwDoc& io_rDoc, sw::mark::InsertMode eMode, SwPosition const* pSepPos) override;
+ virtual void ReleaseDoc(SwDoc& rDoc) override;
+
+ virtual void ShowButton(SwEditWin* pEditWin) override;
+
+ void SetPortionPaintAreaStart(const SwRect& rPortionPaintArea);
+ void SetPortionPaintAreaEnd(const SwRect& rPortionPaintArea);
+
+ virtual OUString GetContent() const override;
+ virtual void ReplaceContent(const OUString& sNewContent) override;
+
+ virtual std::pair<bool, double> GetCurrentDate() const override;
+ virtual void SetCurrentDate(double fDate) override;
+ virtual OUString GetDateInStandardDateFormat(double fDate) const override;
+
+ private:
+ OUString GetDateInCurrentDateFormat(double fDate) const;
+ std::pair<bool, double> ParseCurrentDateParam() const;
+ void InvalidateCurrentDateParam();
+
+ SvNumberFormatter* m_pNumberFormatter;
+ sw::DocumentContentOperationsManager* m_pDocumentContentOperationsManager;
+ SwRect m_aPaintAreaStart;
+ SwRect m_aPaintAreaEnd;
+ };
+
+ /// return position of the CH_TXT_ATR_FIELDSEP for rMark
+ SwPosition FindFieldSep(IFieldmark const& rMark);
+
+ /// check if rPaM is valid range of new fieldmark
+ bool IsFieldmarkOverlap(SwPaM const& rPaM);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/cellfrm.hxx b/sw/source/core/inc/cellfrm.hxx
new file mode 100644
index 000000000..2e34b8a2f
--- /dev/null
+++ b/sw/source/core/inc/cellfrm.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_CELLFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_CELLFRM_HXX
+
+#include "layfrm.hxx"
+
+class SwTableBox;
+struct SwCursorMoveState;
+class SwBorderAttrs;
+
+/// SwCellFrame is one table cell in the document layout.
+class SwCellFrame final : public SwLayoutFrame
+{
+ const SwTableBox* m_pTabBox;
+
+ virtual void DestroyImpl() override;
+ virtual ~SwCellFrame() override;
+
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+ virtual const SwCellFrame* DynCastCellFrame() const override { return this; }
+
+public:
+ SwCellFrame( const SwTableBox &, SwFrame*, bool bInsertContent );
+
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&, SwCursorMoveState* = nullptr, bool bTestBackground = false ) const override;
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ virtual void CheckDirection( bool bVert ) override;
+
+ // #i103961#
+ virtual void Cut() override;
+
+ const SwTableBox *GetTabBox() const { return m_pTabBox; }
+
+ // used for breaking table rows:
+ SwCellFrame* GetFollowCell() const;
+ SwCellFrame* GetPreviousCell() const;
+
+ virtual bool IsLeaveUpperAllowed() const override;
+ virtual bool IsCoveredCell() const override;
+
+ // used for rowspan stuff:
+ const SwCellFrame& FindStartEndOfRowSpanCell( bool bStart ) const;
+ tools::Long GetLayoutRowSpan() const;
+
+ /// If this is a vertically merged cell, then looks up its covered cell in rRow.
+ const SwCellFrame* GetCoveredCellInRow(const SwRowFrame& rRow) const;
+
+ /// If this is a vertically merged cell, then looks up its covered cells.
+ std::vector<const SwCellFrame*> GetCoveredCells() const;
+
+ void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/cntfrm.hxx b/sw/source/core/inc/cntfrm.hxx
new file mode 100644
index 000000000..b972a7aaa
--- /dev/null
+++ b/sw/source/core/inc/cntfrm.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_CNTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_CNTFRM_HXX
+
+#include "frame.hxx"
+#include "flowfrm.hxx"
+#include <cshtyp.hxx>
+
+class SwLayoutFrame;
+class SwContentNode;
+class SwBorderAttrs;
+class SwAttrSetChg;
+class SwTextFrame;
+
+// implemented in cntfrm.cxx, used in cntfrm.cxx and crsrsh.cxx
+extern bool GetFrameInPage( const SwContentFrame*, SwWhichPage, SwPosPage, SwPaM* );
+
+enum class SwContentFrameInvFlags : sal_uInt8
+{
+ NONE = 0x00,
+ SetCompletePaint = 0x01,
+ InvalidatePos = 0x02,
+ InvalidateSize = 0x04,
+ InvalidateSectPrt = 0x08,
+ InvalidateNextPrt = 0x10,
+ InvalidatePrevPrt = 0x20,
+ InvalidateNextPos = 0x40,
+ SetNextCompletePaint = 0x80,
+};
+
+namespace o3tl {
+ template<> struct typed_flags<SwContentFrameInvFlags> : is_typed_flags<SwContentFrameInvFlags, 0xff> {};
+}
+
+/**
+ * SwContentFrame is the layout for content nodes: a common base class for text (paragraph) and
+ * non-text (e.g. graphic) frames.
+ */
+class SAL_DLLPUBLIC_RTTI SwContentFrame: public SwFrame, public SwFlowFrame
+{
+ friend void MakeNxt( SwFrame *pFrame, SwFrame *pNxt ); // calls MakePrtArea
+
+ // parameter <bObjsInNewUpper> indicates that objects exist in remaining
+ // area of new upper
+ bool WouldFit_( SwTwips nSpace,
+ SwLayoutFrame *pNewUpper,
+ bool bTstMove,
+ const bool bObjsInNewUpper );
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+
+ void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, SwContentFrameInvFlags &,
+ SwAttrSetChg *pa = nullptr, SwAttrSetChg *pb = nullptr );
+
+ virtual bool ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool& ) override;
+
+ const SwContentFrame* ImplGetNextContentFrame( bool bFwd ) const;
+
+protected:
+ void MakePrtArea( const SwBorderAttrs & );
+
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+ virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = false ) override;
+ virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) override;
+
+ SwContentFrame( SwContentNode * const, SwFrame* );
+
+ virtual void DestroyImpl() override;
+ virtual ~SwContentFrame() override;
+
+public:
+
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+
+ inline const SwContentFrame *GetFollow() const;
+ inline SwContentFrame *GetFollow();
+ SwTextFrame* FindMaster() const;
+
+ // layout dependent cursor travelling
+ virtual bool LeftMargin(SwPaM *) const = 0;
+ virtual bool RightMargin(SwPaM *, bool bAPI = false) const = 0;
+ virtual bool UnitUp( SwPaM *, const SwTwips nOffset,
+ bool bSetInReadOnly ) const;
+ virtual bool UnitDown( SwPaM *, const SwTwips nOffset,
+ bool bSetInReadOnly ) const;
+
+ // nMaxHeight is the required height
+ // bSplit indicates that the paragraph has to be split
+ // bTst indicates that we are currently doing a test formatting
+ virtual bool WouldFit(SwTwips &nMaxHeight, bool &bSplit, bool bTst, bool);
+
+ bool MoveFootnoteCntFwd( bool, SwFootnoteBossFrame* ); // called by MoveFwd if content
+
+ inline SwContentFrame* GetNextContentFrame() const;
+ inline SwContentFrame* GetPrevContentFrame() const;
+ static bool CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave,
+ tools::Long nBottom, bool bSkipRowSpanCells);
+};
+
+inline SwContentFrame* SwContentFrame::GetNextContentFrame() const
+{
+ if ( GetNext() && GetNext()->IsContentFrame() )
+ return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetNext()));
+ else
+ return const_cast<SwContentFrame*>(ImplGetNextContentFrame( true ));
+}
+
+inline SwContentFrame* SwContentFrame::GetPrevContentFrame() const
+{
+ if ( GetPrev() && GetPrev()->IsContentFrame() )
+ return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetPrev()));
+ else
+ return const_cast<SwContentFrame*>(ImplGetNextContentFrame( false ));
+}
+
+inline const SwContentFrame *SwContentFrame::GetFollow() const
+{
+ return static_cast<const SwContentFrame*>(SwFlowFrame::GetFollow());
+}
+inline SwContentFrame *SwContentFrame::GetFollow()
+{
+ return static_cast<SwContentFrame*>(SwFlowFrame::GetFollow());
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/colfrm.hxx b/sw/source/core/inc/colfrm.hxx
new file mode 100644
index 000000000..b481897f0
--- /dev/null
+++ b/sw/source/core/inc/colfrm.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_COLFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_COLFRM_HXX
+
+#include "ftnboss.hxx"
+
+class SwColumnFrame final : public SwFootnoteBossFrame
+{
+private:
+ virtual void DestroyImpl() override;
+ virtual ~SwColumnFrame() override;
+
+public:
+ SwColumnFrame(SwFrameFormat*, SwFrame*);
+
+ virtual void PaintBreak() const override;
+ virtual void PaintSubsidiaryLines(const SwPageFrame*, const SwRect&) const override;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_COLFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/contentcontrolbutton.hxx b/sw/source/core/inc/contentcontrolbutton.hxx
new file mode 100644
index 000000000..cd63bddd4
--- /dev/null
+++ b/sw/source/core/inc/contentcontrolbutton.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <vcl/ctrl.hxx>
+#include <vcl/weld.hxx>
+#include <swrect.hxx>
+
+class SwEditWin;
+class SwContentControl;
+
+/// This button is shown when the cursor is inside a content control with drop-down capability.
+class SwContentControlButton : public Control
+{
+public:
+ SwContentControlButton(SwEditWin* pEditWin,
+ const std::shared_ptr<SwContentControl>& pContentControl);
+ virtual ~SwContentControlButton() override;
+ virtual void dispose() override;
+
+ void CalcPosAndSize(const SwRect& rPortionPaintArea);
+
+ virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
+ DECL_LINK(PopupModeEndHdl, weld::Popover&, void);
+ /// Shared MouseButtonDown() and KeyInput() code.
+ void StartPopup();
+
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual WindowHitTest ImplHitTest(const Point& rFramePos) override;
+
+ virtual void LaunchPopup();
+ virtual void DestroyPopup();
+
+ const std::shared_ptr<SwContentControl>& GetContentControl() const { return m_pContentControl; }
+
+private:
+ tools::Rectangle m_aFramePixel;
+
+protected:
+ std::shared_ptr<SwContentControl> m_pContentControl;
+ std::unique_ptr<weld::Builder> m_xPopupBuilder;
+ std::unique_ptr<weld::Popover> m_xPopup;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/crossrefbookmark.hxx b/sw/source/core/inc/crossrefbookmark.hxx
new file mode 100644
index 000000000..2540170c9
--- /dev/null
+++ b/sw/source/core/inc/crossrefbookmark.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_CROSSREFBOOKMARK_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_CROSSREFBOOKMARK_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "bookmark.hxx"
+#include <rtl/ustring.hxx>
+#include <vcl/keycod.hxx>
+
+namespace sw::mark {
+ class CrossRefBookmark
+ : public Bookmark
+ {
+ public:
+ CrossRefBookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName,
+ std::u16string_view rPrefix);
+
+ // getters
+ virtual SwPosition& GetOtherMarkPos() const override;
+ virtual SwPosition& GetMarkStart() const override
+ { return *m_pPos1; }
+ virtual SwPosition& GetMarkEnd() const override
+ { return *m_pPos1; }
+ virtual bool IsExpanded() const override
+ { return false; }
+
+ virtual void SetMarkPos(const SwPosition& rNewPos) override;
+ virtual void SetOtherMarkPos(const SwPosition&) override
+ {
+ assert(false &&
+ "<CrossRefBookmark::SetOtherMarkPos(..)>"
+ " - misusage of CrossRefBookmark: other bookmark position isn't allowed to be set." );
+ }
+ virtual void ClearOtherMarkPos() override
+ {
+ assert(false &&
+ "<SwCrossRefBookmark::ClearOtherMarkPos(..)>"
+ " - misusage of CrossRefBookmark: other bookmark position isn't allowed to be set or cleared." );
+ }
+ };
+
+ class CrossRefHeadingBookmark final
+ : public CrossRefBookmark
+ {
+ public:
+ CrossRefHeadingBookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName);
+ static bool IsLegalName(std::u16string_view rName);
+ };
+
+ class CrossRefNumItemBookmark final
+ : public CrossRefBookmark
+ {
+ public:
+ CrossRefNumItemBookmark(const SwPaM& rPaM,
+ const vcl::KeyCode& rCode,
+ const OUString& rName);
+ static bool IsLegalName(std::u16string_view rName);
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/datecontentcontrolbutton.hxx b/sw/source/core/inc/datecontentcontrolbutton.hxx
new file mode 100644
index 000000000..48b08f2db
--- /dev/null
+++ b/sw/source/core/inc/datecontentcontrolbutton.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "contentcontrolbutton.hxx"
+
+class SwEditWin;
+class SvNumberFormatter;
+class SwContentControl;
+
+/**
+ * This button is shown when the cursor is on a date content control. The user can select a date
+ * from a date picker.
+ */
+class SwDateContentControlButton final : public SwContentControlButton
+{
+private:
+ SvNumberFormatter* m_pNumberFormatter;
+
+ std::unique_ptr<weld::Calendar> m_xCalendar;
+
+ DECL_LINK(SelectHandler, weld::Calendar&, void);
+
+public:
+ SwDateContentControlButton(SwEditWin* pEditWin,
+ const std::shared_ptr<SwContentControl>& pContentControl,
+ SvNumberFormatter* pNumberFormatter);
+ ~SwDateContentControlButton() override;
+
+ void LaunchPopup() override;
+ void DestroyPopup() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/dbg_lay.hxx b/sw/source/core/inc/dbg_lay.hxx
new file mode 100644
index 000000000..d340153ab
--- /dev/null
+++ b/sw/source/core/inc/dbg_lay.hxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DBG_LAY_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DBG_LAY_HXX
+
+#include <o3tl/typed_flags_set.hxx>
+#include <memory>
+
+enum class PROT {
+ FileInit = 0x00000000,
+ Init = 0x00000001,
+ MakeAll = 0x00000002,
+ MoveFwd = 0x00000004,
+ MoveBack = 0x00000008,
+ Grow = 0x00000010,
+ Shrink = 0x00000020,
+ GrowTest = 0x00000040,
+ ShrinkTest = 0x00000080,
+ Size = 0x00000100,
+ PrintArea = 0x00000200,
+ AdjustN = 0x00000800,
+ Section = 0x00001000,
+ Cut = 0x00002000,
+ Paste = 0x00004000,
+ Leaf = 0x00008000,
+ TestFormat = 0x00010000,
+ FrmChanges = 0x00020000,
+};
+namespace o3tl {
+ template<> struct typed_flags<PROT> : is_typed_flags<PROT, 0x0003fbff> {};
+}
+
+enum class DbgAction {
+ NONE,
+ Start, End,
+ CreateMaster, CreateFollow,
+ DelMaster, DelFollow,
+ Merge,
+ NextSect, PrevSect
+};
+
+#ifdef DBG_UTIL
+
+class SwImplProtocol;
+class SwFrame;
+class SwImplEnterLeave;
+
+class SwProtocol
+{
+ static PROT s_nRecord;
+ static SwImplProtocol* s_pImpl;
+ static bool Start() { return bool( PROT::Init & s_nRecord ); }
+
+public:
+ static PROT Record() { return s_nRecord; }
+ static void SetRecord( PROT nNew ) { s_nRecord = nNew; }
+ static bool Record( PROT nFunc ) { return bool(( nFunc | PROT::Init ) & s_nRecord); }
+ static void Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAction, void* pParam );
+ static void Init();
+ static void Stop();
+};
+
+class SwEnterLeave
+{
+ std::unique_ptr<SwImplEnterLeave> pImpl;
+public:
+ SwEnterLeave( const SwFrame* pFrame, PROT nFunc, DbgAction nAct, void* pPar );
+ ~SwEnterLeave();
+};
+
+#define PROTOCOL( pFrame, nFunc, nAct, pPar ) { if( SwProtocol::Record( nFunc ) )\
+ SwProtocol::Record( pFrame, nFunc, nAct, pPar ); }
+#define PROTOCOL_INIT SwProtocol::Init();
+#define PROTOCOL_STOP SwProtocol::Stop();
+#define PROTOCOL_ENTER( pFrame, nFunc, nAct, pPar ) SwEnterLeave aEnter( pFrame, nFunc, nAct, pPar );
+
+#else
+
+#define PROTOCOL( pFrame, nFunc, nAct, pPar )
+#define PROTOCOL_INIT
+#define PROTOCOL_STOP
+#define PROTOCOL_ENTER( pFrame, nFunc, nAct, pPar )
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/dflyobj.hxx b/sw/source/core/inc/dflyobj.hxx
new file mode 100644
index 000000000..682a77e84
--- /dev/null
+++ b/sw/source/core/inc/dflyobj.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DFLYOBJ_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DFLYOBJ_HXX
+
+#include <svx/svdovirt.hxx>
+#include <svx/svdobj.hxx>
+
+namespace drawinglayer::geometry { class ViewInformation2D; }
+
+class SwFlyFrame;
+class SwFrameFormat;
+
+// DrawObjects for Flys
+class SwFlyDrawObj final : public SdrObject
+{
+private:
+ virtual std::unique_ptr<sdr::properties::BaseProperties> CreateObjectSpecificProperties() override;
+ bool mbIsTextBox;
+
+ // #i95264# SwFlyDrawObj needs an own VC since createViewIndependentPrimitive2DSequence()
+ // is called when RecalcBoundRect() is used
+ virtual std::unique_ptr<sdr::contact::ViewContact> CreateObjectSpecificViewContact() override;
+
+ // protected destructor
+ virtual ~SwFlyDrawObj() override;
+
+public:
+ SwFlyDrawObj(SdrModel& rSdrModel);
+
+ // for instantiation of this class while loading (via factory)
+ virtual SdrInventor GetObjInventor() const override;
+ virtual SdrObjKind GetObjIdentifier() const override;
+ virtual bool IsTextBox() const override { return mbIsTextBox; }
+ void SetTextBox(bool bIsTextBox) { mbIsTextBox = bIsTextBox; }
+};
+
+// virtual objects for Flys
+// Flys will always be shown with virtual objects. By doing that, they can be
+// shown multiple times if needed (header/footer).
+// For example, if an SwFlyFrameFormat is anchored in a header, then all pages will have a separate
+// SwVirtFlyDrawObj in their headers.
+class SwVirtFlyDrawObj final : public SdrVirtObj
+{
+private:
+ SwFlyFrame *m_pFlyFrame;
+
+ // RotGrfFlyFrame: Helper to access the rotation angle (in 10th degrees, left-handed)
+ // of a GraphicFrame
+ Degree10 getPossibleRotationFromFraphicFrame(Size& rSize) const;
+
+ // AW: Need own sdr::contact::ViewContact since AnchorPos from parent is
+ // not used but something own (top left of new SnapRect minus top left
+ // of original SnapRect)
+ virtual std::unique_ptr<sdr::contact::ViewContact> CreateObjectSpecificViewContact() override;
+
+ // protected destructor
+ virtual ~SwVirtFlyDrawObj() override;
+
+public:
+ // for paints triggered form ExecutePrimitive
+ void wrap_DoPaintObject(
+ drawinglayer::geometry::ViewInformation2D const&) const;
+
+ // for simple access to inner and outer bounds
+ basegfx::B2DRange getOuterBound() const;
+ basegfx::B2DRange getInnerBound() const;
+
+ // RotGrfFlyFrame: Check if this is a SwGrfNode
+ bool ContainsSwGrfNode() const;
+
+ SwVirtFlyDrawObj(
+ SdrModel& rSdrModel,
+ SdrObject& rNew,
+ SwFlyFrame* pFly);
+
+ // override method of base class SdrVirtObj
+ virtual void TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const override;
+
+ // we treat the size calculation completely on ourself here
+ virtual const tools::Rectangle& GetCurrentBoundRect() const override;
+ virtual const tools::Rectangle& GetLastBoundRect() const override;
+ virtual Degree100 GetRotateAngle() const override;
+ virtual void RecalcBoundRect() override;
+ virtual void RecalcSnapRect() override;
+ virtual const tools::Rectangle& GetSnapRect() const override;
+ virtual void SetSnapRect(const tools::Rectangle& rRect) override;
+ virtual void NbcSetSnapRect(const tools::Rectangle& rRect) override;
+ virtual const tools::Rectangle& GetLogicRect() const override;
+ virtual void SetLogicRect(const tools::Rectangle& rRect) override;
+ virtual void NbcSetLogicRect(const tools::Rectangle& rRect) override;
+ virtual ::basegfx::B2DPolyPolygon TakeXorPoly() const override;
+ virtual void NbcMove (const Size& rSiz) override;
+ virtual void NbcResize(const Point& rRef, const Fraction& xFact,
+ const Fraction& yFact) override;
+ virtual void NbcCrop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact) override;
+ virtual void Move (const Size& rSiz) override;
+ virtual void Resize(const Point& rRef, const Fraction& xFact,
+ const Fraction& yFact, bool bUnsetRelative = true) override;
+ virtual void Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact) override;
+ virtual void addCropHandles(SdrHdlList& rTarget) const override;
+ virtual void Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs) override;
+
+ // FullDrag support
+ virtual SdrObjectUniquePtr getFullDragClone() const override;
+
+ const SwFrameFormat *GetFormat() const;
+ SwFrameFormat *GetFormat();
+
+ // methods to get pointers for the Fly
+ SwFlyFrame* GetFlyFrame() { return m_pFlyFrame; }
+ const SwFlyFrame* GetFlyFrame() const { return m_pFlyFrame; }
+
+ void SetRect() const;
+
+ // if a URL is attached to a graphic than this is a macro object
+ virtual bool HasMacro() const override;
+ virtual SdrObject* CheckMacroHit (const SdrObjMacroHitRec& rRec) const override;
+ virtual PointerStyle GetMacroPointer (const SdrObjMacroHitRec& rRec) const override;
+
+ // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation.
+ virtual bool HasLimitedRotation() const override;
+
+ virtual bool IsTextBox() const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/dialoghelp.hxx b/sw/source/core/inc/dialoghelp.hxx
new file mode 100644
index 000000000..a54952365
--- /dev/null
+++ b/sw/source/core/inc/dialoghelp.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DIALOGHELP_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DIALOGHELP_HXX
+
+class SwDoc;
+class SwDocShell;
+class SfxFrame;
+class SfxMedium;
+
+namespace weld
+{
+class Window;
+}
+
+weld::Window* GetFrameWeld(const SfxFrame* pFrame);
+weld::Window* GetFrameWeld(const SfxMedium* pMedium);
+weld::Window* GetFrameWeld(SwDocShell* pDocSh);
+weld::Window* GetFrameWeld(SwDoc* pDoc);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/inc/docedt.hxx b/sw/source/core/inc/docedt.hxx
new file mode 100644
index 000000000..036d516d9
--- /dev/null
+++ b/sw/source/core/inc/docedt.hxx
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCEDT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCEDT_HXX
+
+class SwPaM;
+
+bool sw_JoinText(SwPaM& rPam, bool bJoinPrev);
+
+void sw_GetJoinFlags(SwPaM& rPam, bool& rJoinText, bool& rJoinPrev);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/docfld.hxx b/sw/source/core/inc/docfld.hxx
new file mode 100644
index 000000000..08fb08e33
--- /dev/null
+++ b/sw/source/core/inc/docfld.hxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCFLD_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCFLD_HXX
+
+#include <calc.hxx>
+#include <doc.hxx>
+#include <IDocumentTimerAccess.hxx>
+#include <IMark.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <memory>
+
+class SwTextField;
+class SwIndex;
+class SwNodeIndex;
+class SwContentFrame;
+class SwSectionNode;
+class SwSection;
+class SwTextTOXMark;
+class SwTableBox;
+class SwTextINetFormat;
+class SwFlyFrameFormat;
+class SwNode;
+struct SwPosition;
+enum class SwFieldIds : sal_uInt16;
+
+// Update expression fields
+class SetGetExpField
+{
+ // TODO: in case of multiple layouts, storing this only once isn't going to work (although already a problem for cached field value)
+ sal_uInt16 m_nPageNumber = 0;
+ SwNodeOffset m_nNode;
+ union {
+ const SwTextField* pTextField;
+ const SwSection* pSection;
+ const SwPosition* pPos;
+ const SwTextTOXMark* pTextTOX;
+ const SwTableBox* pTBox;
+ const SwTextINetFormat* pTextINet;
+ const SwFlyFrameFormat* pFlyFormat;
+ ::sw::mark::IBookmark const* pBookmark;
+ } m_CNTNT;
+ sal_Int32 m_nContent;
+ enum SetGetExpFieldType
+ {
+ TEXTFIELD, TEXTTOXMARK, SECTIONNODE, BOOKMARK, CRSRPOS, TABLEBOX,
+ TEXTINET, FLYFRAME
+ } m_eSetGetExpFieldType;
+
+public:
+ SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextField* pField = nullptr,
+ const SwIndex* pIdx = nullptr,
+ sal_uInt16 nPageNumber = 0);
+
+ SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextINetFormat& rINet );
+
+ SetGetExpField( const SwSectionNode& rSectNode,
+ const SwPosition* pPos = nullptr,
+ sal_uInt16 nPageNumber = 0);
+
+ SetGetExpField( ::sw::mark::IBookmark const& rBookmark,
+ SwPosition const* pPos = nullptr,
+ sal_uInt16 nPageNumber = 0);
+
+ SetGetExpField( const SwTableBox& rTableBox );
+
+ SetGetExpField( const SwNodeIndex& rNdIdx, const SwTextTOXMark& rTOX );
+
+ SetGetExpField( const SwPosition& rPos );
+
+ SetGetExpField( const SwFlyFrameFormat& rFlyFormat, const SwPosition* pPos );
+
+ bool operator==( const SetGetExpField& rField ) const;
+ bool operator<( const SetGetExpField& rField ) const;
+
+ const SwTextField* GetTextField() const
+ { return TEXTFIELD == m_eSetGetExpFieldType ? m_CNTNT.pTextField : nullptr; }
+ const SwSection* GetSection() const
+ { return SECTIONNODE == m_eSetGetExpFieldType ? m_CNTNT.pSection : nullptr; }
+ ::sw::mark::IBookmark const* GetBookmark() const
+ { return BOOKMARK == m_eSetGetExpFieldType ? m_CNTNT.pBookmark : nullptr; }
+ const SwTextINetFormat* GetINetFormat() const
+ { return TEXTINET == m_eSetGetExpFieldType ? m_CNTNT.pTextINet : nullptr; }
+ const SwFlyFrameFormat* GetFlyFormat() const
+ { return FLYFRAME == m_eSetGetExpFieldType ? m_CNTNT.pFlyFormat : nullptr; }
+
+ SwNodeOffset GetNode() const { return m_nNode; }
+ sal_Int32 GetContent() const { return m_nContent; }
+ const void* GetPointer() const { return m_CNTNT.pTextField; }
+
+ void GetPosOfContent( SwPosition& rPos ) const;
+
+ const SwNode* GetNodeFromContent() const;
+ sal_Int32 GetCntPosFromContent() const;
+
+ void SetBodyPos( const SwContentFrame& rFrame );
+};
+
+class SetGetExpFields : public o3tl::sorted_vector<std::unique_ptr<SetGetExpField>, o3tl::less_uniqueptr_to<SetGetExpField> >
+{
+};
+
+// struct for saving strings from the SetExp's string fields
+struct HashStr final : public SwHash
+{
+ OUString aSetStr;
+ HashStr( const OUString& rName, const OUString& rText, HashStr* );
+};
+
+struct SwCalcFieldType final : public SwHash
+{
+ const SwFieldType* pFieldType;
+
+ SwCalcFieldType( const OUString& rStr, const SwFieldType* pFieldTyp )
+ : SwHash( rStr ), pFieldType( pFieldTyp )
+ {}
+};
+
+// search for the string that was saved under rName in the hash table
+OUString LookString( SwHashTable<HashStr> const & rTable, const OUString& rName );
+
+const int GETFLD_ALL = 3; // combine flags via OR
+const int GETFLD_CALC = 1;
+const int GETFLD_EXPAND = 2;
+
+class SwDocUpdateField
+{
+ std::unique_ptr<SetGetExpFields> m_pFieldSortList; ///< current field list for calculation
+ SwHashTable<SwCalcFieldType> m_FieldTypeTable;
+
+ SwNodeOffset m_nNodes; ///< to check if the node count changed
+ int m_nFieldListGetMode;
+ SwDoc& m_rDoc;
+
+ bool m_bInUpdateFields : 1; ///< currently in an UpdateFields call
+ bool m_bFieldsDirty : 1; ///< some fields are invalid
+
+ void MakeFieldList_( SwDoc& pDoc, int eGetMode );
+ void GetBodyNode( const SwTextField& , SwFieldIds nFieldWhich );
+ template<typename T>
+ void GetBodyNodeGeneric(SwNode const& rNode, T const&);
+
+public:
+ SwDocUpdateField(SwDoc& rDocument);
+ ~SwDocUpdateField();
+
+ const SetGetExpFields* GetSortList() const { return m_pFieldSortList.get(); }
+
+ void MakeFieldList( SwDoc& rDoc, bool bAll, int eGetMode );
+
+ void InsDelFieldInFieldLst( bool bIns, const SwTextField& rField );
+
+ void InsertFieldType( const SwFieldType& rType );
+ void RemoveFieldType( const SwFieldType& rType );
+
+ bool IsInUpdateFields() const { return m_bInUpdateFields; }
+ void SetInUpdateFields( bool b ) { m_bInUpdateFields = b; }
+
+ bool IsFieldsDirty() const { return m_bFieldsDirty; }
+ void SetFieldsDirty( bool b )
+ {
+ m_bFieldsDirty = b;
+
+ if (b)
+ {
+ m_rDoc.getIDocumentTimerAccess().StartIdling();
+ }
+ }
+
+ SwHashTable<SwCalcFieldType> const& GetFieldTypeTable() const { return m_FieldTypeTable; }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/docredln.hxx b/sw/source/core/inc/docredln.hxx
new file mode 100644
index 000000000..f60cb930d
--- /dev/null
+++ b/sw/source/core/inc/docredln.hxx
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCREDLN_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCREDLN_HXX
+
+#include <sal/config.h>
+
+#if defined DBG_UTIL
+
+class SwDoc;
+
+void sw_DebugRedline(const SwDoc* pDoc);
+
+#endif
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/docsort.hxx b/sw/source/core/inc/docsort.hxx
new file mode 100644
index 000000000..0aad16da4
--- /dev/null
+++ b/sw/source/core/inc/docsort.hxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCSORT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCSORT_HXX
+
+#include <ndindex.hxx>
+
+#include <memory>
+#include <vector>
+
+class SwDoc;
+class SwTableBox;
+class SwUndoSort;
+class FlatFndBox;
+struct SwSortOptions;
+class FndBox_;
+class FndLine_;
+class CollatorWrapper;
+class LocaleDataWrapper;
+namespace com::sun::star::lang { struct Locale; }
+
+class SwMovedBoxes
+{
+private:
+ std::vector<const SwTableBox*> mBoxes;
+
+public:
+ void push_back(const SwTableBox* &rpTableBox) {mBoxes.push_back(rpTableBox);}
+
+ sal_uInt16 GetPos(const SwTableBox* pTableBox) const;
+};
+
+// Functions for moving boxes
+void MoveCol(SwDoc* pDoc, const FlatFndBox& rBox,
+ sal_uInt16 nS, sal_uInt16 nT, SwMovedBoxes& rMovedList, SwUndoSort* pUD);
+void MoveRow(SwDoc* pDoc, const FlatFndBox& rBox,
+ sal_uInt16 nS, sal_uInt16 nT, SwMovedBoxes& rMovedList, SwUndoSort* pUD);
+void MoveCell(SwDoc* pDoc, const SwTableBox* pSource,
+ const SwTableBox* pTar, bool bMovedBefore, SwUndoSort* pUD=nullptr);
+
+// Elements for sorting text and table content
+struct SwSortElement
+{
+ static SwSortOptions* pOptions;
+ static SwDoc* pDoc;
+ static const FlatFndBox* pBox;
+ static CollatorWrapper* pSortCollator;
+ static css::lang::Locale* pLocale;
+ static std::optional<OUString> xLastAlgorithm;
+ static LocaleDataWrapper* pLclData;
+
+ static void Init( SwDoc*, const SwSortOptions& rOpt, FlatFndBox const * = nullptr );
+ static void Finit();
+
+ SwSortElement() = default;
+ SwSortElement(SwSortElement const &) = default;
+ SwSortElement(SwSortElement &&) = default;
+ SwSortElement & operator =(SwSortElement const &) = default;
+ SwSortElement & operator =(SwSortElement &&) = default;
+
+ virtual ~SwSortElement();
+
+ virtual OUString GetKey(sal_uInt16 nKey ) const = 0;
+ virtual double GetValue(sal_uInt16 nKey ) const;
+
+ bool operator<(const SwSortElement& ) const;
+
+ static double StrToDouble(std::u16string_view rStr);
+private:
+ int keycompare(const SwSortElement& rCmp, sal_uInt16 nKey) const;
+};
+
+// sort text
+struct SwSortTextElement final : public SwSortElement
+{
+ SwNodeOffset nOrg;
+ SwNodeIndex aPos;
+
+ SwSortTextElement( const SwNodeIndex& rPos );
+
+ virtual OUString GetKey( sal_uInt16 nKey ) const override;
+};
+
+// sort table
+struct SwSortBoxElement final : public SwSortElement
+{
+ sal_uInt16 nRow;
+
+ SwSortBoxElement( sal_uInt16 nRC );
+
+ virtual OUString GetKey( sal_uInt16 nKey ) const override;
+ virtual double GetValue( sal_uInt16 nKey ) const override;
+};
+
+// two-dimensional array of FndBoxes
+class FlatFndBox
+{
+public:
+ FlatFndBox(SwDoc* pDocPtr, const FndBox_& rBox);
+ ~FlatFndBox();
+
+ bool IsSymmetric() const { return m_bSym; }
+ sal_uInt16 GetRows() const { return m_nRows; }
+ sal_uInt16 GetCols() const { return m_nCols; }
+
+ const FndBox_* GetBox(sal_uInt16 nCol, sal_uInt16 nRow) const;
+
+ inline bool HasItemSets() const;
+ const SfxItemSet* GetItemSet(sal_uInt16 nCol, sal_uInt16 nRow) const;
+
+private:
+ bool CheckLineSymmetry(const FndBox_& rBox);
+ bool CheckBoxSymmetry(const FndLine_& rLn);
+ sal_uInt16 GetColCount(const FndBox_& rBox);
+ sal_uInt16 GetRowCount(const FndBox_& rBox);
+ void FillFlat(const FndBox_&, bool bLastBox=false);
+
+ SwDoc* m_pDoc;
+ std::unique_ptr<FndBox_ const *[]> m_pArr;
+ /// using optional because SfxItemSet has no default constructor
+ std::vector<std::optional<SfxItemSet>> m_vItemSets;
+
+ sal_uInt16 m_nRows;
+ sal_uInt16 m_nCols;
+ sal_uInt16 m_nRow;
+ sal_uInt16 m_nCol;
+
+ bool m_bSym;
+};
+
+inline bool FlatFndBox::HasItemSets() const { return !m_vItemSets.empty(); }
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/doctxm.hxx b/sw/source/core/inc/doctxm.hxx
new file mode 100644
index 000000000..b2cfe4a55
--- /dev/null
+++ b/sw/source/core/inc/doctxm.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DOCTXM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DOCTXM_HXX
+
+#include <tools/gen.hxx>
+#include <hints.hxx>
+#include <tox.hxx>
+#include <section.hxx>
+
+class SwTOXInternational;
+class SwPageDesc;
+class SwTextNode;
+class SwTextFormatColl;
+struct SwPosition;
+struct SwTOXSortTabBase;
+
+class SwTOXBaseSection final : public SwTOXBase, public SwSection
+{
+ std::vector<std::unique_ptr<SwTOXSortTabBase>> m_aSortArr;
+
+ void UpdateMarks( const SwTOXInternational& rIntl,
+ const SwTextNode* pOwnChapterNode,
+ SwRootFrame const* pLayout );
+ void UpdateOutline( const SwTextNode* pOwnChapterNode,
+ SwRootFrame const* pLayout );
+ void UpdateTemplate( const SwTextNode* pOwnChapterNode,
+ SwRootFrame const* pLayout );
+ void UpdateContent( SwTOXElement eType,
+ const SwTextNode* pOwnChapterNode,
+ SwRootFrame const* pLayout );
+ void UpdateTable( const SwTextNode* pOwnChapterNode,
+ SwRootFrame const* pLayout );
+ void UpdateSequence( const SwTextNode* pOwnChapterNode,
+ SwRootFrame const* pLayout );
+ void UpdateAuthorities( const SwTOXInternational& rIntl,
+ SwRootFrame const* pLayout );
+
+ // insert sorted into array for creation
+ void InsertSorted(std::unique_ptr<SwTOXSortTabBase> pBase);
+
+ // insert alpha delimiter at creation
+ void InsertAlphaDelimiter( const SwTOXInternational& rIntl );
+
+ // replace page num placeholder with actual page number
+ void UpdatePageNum_( SwTextNode* pNd,
+ const std::vector<sal_uInt16>& rNums,
+ const std::vector<SwPageDesc*>& rDescs,
+ const std::vector<sal_uInt16>* pMainEntryNums,
+ const SwTOXInternational& rIntl );
+
+ // get section for entering keywords
+ Range GetKeyRange( const OUString& rStr, const OUString& rStrReading,
+ const SwTOXSortTabBase& rNew, sal_uInt16 nLevel,
+ const Range& rRange );
+
+ // return text collection via name/ from format pool
+ SwTextFormatColl* GetTextFormatColl( sal_uInt16 nLevel );
+ virtual void SwClientNotify(const SwModify& rModify, const SfxHint& rHint) override;
+
+public:
+ SwTOXBaseSection(SwTOXBase const& rBase, SwSectionFormat & rFormat);
+ virtual ~SwTOXBaseSection() override;
+
+ // <_bNewTOX> : distinguish between the creation of a new table-of-content
+ // (true) or an update of a table-of-content (false)
+ void Update( const SfxItemSet* pAttr = nullptr,
+ SwRootFrame const* pLayout = nullptr,
+ const bool _bNewTOX = false );
+ void UpdatePageNum(); // insert page numbering
+
+ bool SetPosAtStartEnd( SwPosition& rPos ) const;
+ bool IsVisible() const override
+ {
+ SwPtrMsgPoolItem aInfo(RES_CONTENT_VISIBLE, nullptr);
+ return GetFormat() && !GetFormat()->GetInfo(aInfo) && aInfo.pObject;
+ }
+};
+
+struct SwDefTOXBase_Impl
+{
+ std::unique_ptr<SwTOXBase> pContBase;
+ std::unique_ptr<SwTOXBase> pIdxBase;
+ std::unique_ptr<SwTOXBase> pUserBase;
+ std::unique_ptr<SwTOXBase> pTableBase;
+ std::unique_ptr<SwTOXBase> pObjBase;
+ std::unique_ptr<SwTOXBase> pIllBase;
+ std::unique_ptr<SwTOXBase> pAuthBase;
+ std::unique_ptr<SwTOXBase> pBiblioBase;
+
+ SwDefTOXBase_Impl()
+ {
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/drawfont.hxx b/sw/source/core/inc/drawfont.hxx
new file mode 100644
index 000000000..c40ff1044
--- /dev/null
+++ b/sw/source/core/inc/drawfont.hxx
@@ -0,0 +1,622 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DRAWFONT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DRAWFONT_HXX
+
+#include <osl/diagnose.h>
+#include <vcl/vclptr.hxx>
+#include <vcl/outdev.hxx>
+#include <swtypes.hxx>
+#include "TextFrameIndex.hxx"
+#include <swdllapi.h>
+
+class SwTextFrame;
+class SwViewShell;
+class SwScriptInfo;
+namespace sw { class WrongListIterator; }
+class SwFont;
+namespace vcl {
+ class Font;
+ namespace text {
+ class TextLayoutCache;
+ }
+ typedef OutputDevice RenderContext;
+}
+class SwUnderlineFont;
+
+// encapsulates information for drawing text
+class SW_DLLPUBLIC SwDrawTextInfo
+{
+ const SwTextFrame* m_pFrame;
+ VclPtr<OutputDevice> m_pOut;
+ SwViewShell const * m_pSh;
+ const SwScriptInfo* m_pScriptInfo;
+ Point m_aPos;
+ vcl::text::TextLayoutCache const* m_pCachedVclData;
+ OUString m_aText;
+ sw::WrongListIterator* m_pWrong;
+ sw::WrongListIterator* m_pGrammarCheck;
+ sw::WrongListIterator* m_pSmartTags;
+ Size m_aSize;
+ SwFont *m_pFnt;
+ SwUnderlineFont* m_pUnderFnt;
+ TextFrameIndex* m_pHyphPos;
+ tools::Long m_nKanaDiff;
+ TextFrameIndex m_nIdx;
+ TextFrameIndex m_nLen;
+ /// this is not a string index
+ sal_Int32 m_nOfst;
+ sal_uInt16 m_nWidth;
+ sal_uInt16 m_nAscent;
+ sal_uInt16 m_nCompress;
+ tools::Long m_nCharacterSpacing;
+ tools::Long m_nSpace;
+ tools::Long m_nKern;
+ TextFrameIndex m_nNumberOfBlanks;
+ sal_uInt8 m_nCursorBidiLevel;
+ bool m_bBullet : 1;
+ bool m_bUpper : 1; // for small caps: upper case flag
+ bool m_bDrawSpace : 1; // for small caps: underline/ line through
+ bool m_bGreyWave : 1; // grey wave line for extended text input
+ // For underlining we need to know, if a section is right in front of a
+ // whole block or a fix margin section.
+ bool m_bSpaceStop : 1;
+ bool m_bSnapToGrid : 1; // Does paragraph snap to grid?
+ // Paint text as if text has LTR direction, used for line numbering
+ bool m_bIgnoreFrameRTL : 1;
+ // GetModelPositionForViewPoint should not return the next position if screen position is
+ // inside second half of bound rect, used for Accessibility
+ bool m_bPosMatchesBounds :1;
+
+public:
+
+#ifdef DBG_UTIL
+ // These flags should control that the appropriate Set-function has been
+ // called before calling the Get-function of a member
+ bool m_bPos : 1;
+ bool m_bWrong : 1;
+ bool m_bGrammarCheck : 1;
+ bool m_bSize : 1;
+ bool m_bFnt : 1;
+ bool m_bHyph : 1;
+ bool m_bKana : 1;
+ bool m_bOfst : 1;
+ bool m_bAscent: 1;
+ bool m_bCharacterSpacing : 1;
+ bool m_bSpace : 1;
+ bool m_bNumberOfBlanks : 1;
+ bool m_bUppr : 1;
+ bool m_bDrawSp: 1;
+#endif
+
+ /// constructor for simple strings
+ SwDrawTextInfo( SwViewShell const *pSh, OutputDevice &rOut,
+ const OUString &rText, sal_Int32 const nIdx, sal_Int32 const nLen,
+ sal_uInt16 nWidth = 0, bool bBullet = false)
+ : SwDrawTextInfo(pSh, rOut, nullptr, rText, TextFrameIndex(nIdx), TextFrameIndex(nLen), nWidth, bBullet)
+ {}
+ /// constructor for text frame contents
+ SwDrawTextInfo( SwViewShell const *pSh, OutputDevice &rOut, const SwScriptInfo* pSI,
+ const OUString &rText, TextFrameIndex const nIdx, TextFrameIndex const nLen,
+ sal_uInt16 nWidth = 0, bool bBullet = false,
+ vcl::text::TextLayoutCache const*const pCachedVclData = nullptr)
+ : m_pCachedVclData(pCachedVclData)
+ {
+ assert( (nLen == TextFrameIndex(COMPLETE_STRING)) ? (nIdx.get() < rText.getLength()) : (nIdx + nLen).get() <= rText.getLength() );
+ m_pFrame = nullptr;
+ m_pSh = pSh;
+ m_pOut = &rOut;
+ m_pScriptInfo = pSI;
+ m_aText = rText;
+ m_nIdx = nIdx;
+ m_nLen = nLen;
+ m_nKern = 0;
+ m_nCompress = 0;
+ m_nWidth = nWidth;
+ m_nNumberOfBlanks = TextFrameIndex(0);
+ m_nCursorBidiLevel = 0;
+ m_bBullet = bBullet;
+ m_pUnderFnt = nullptr;
+ m_bGreyWave = false;
+ m_bSpaceStop = false;
+ m_bSnapToGrid = false;
+ m_bIgnoreFrameRTL = false;
+ m_bPosMatchesBounds = false;
+
+ // These values are initialized but have to be set explicitly via their
+ // Set-function before they may be accessed by their Get-function:
+ m_pWrong = nullptr;
+ m_pGrammarCheck = nullptr;
+ m_pSmartTags = nullptr;
+ m_pFnt = nullptr;
+ m_pHyphPos = nullptr;
+ m_nKanaDiff = 0;
+ m_nOfst = 0;
+ m_nAscent = 0;
+ m_nCharacterSpacing = 0;
+ m_nSpace = 0;
+ m_bUpper = false;
+ m_bDrawSpace = false;
+
+#ifdef DBG_UTIL
+ // these flags control whether the matching member variables have been
+ // set by using the Set-function before they may be accessed by their
+ // Get-function:
+ m_bPos = m_bWrong = m_bGrammarCheck = m_bSize = m_bFnt = m_bAscent =
+ m_bSpace = m_bNumberOfBlanks = m_bUppr =
+ m_bDrawSp = m_bKana = m_bOfst = m_bHyph =
+ m_bCharacterSpacing = false;
+#endif
+ }
+
+ const SwTextFrame* GetFrame() const
+ {
+ return m_pFrame;
+ }
+
+ void SetFrame( const SwTextFrame* pNewFrame )
+ {
+ m_pFrame = pNewFrame;
+ }
+
+ SwViewShell const *GetShell() const
+ {
+ return m_pSh;
+ }
+
+ vcl::RenderContext& GetOut() const
+ {
+ return *m_pOut;
+ }
+
+ vcl::RenderContext *GetpOut() const
+ {
+ return m_pOut;
+ }
+
+ const SwScriptInfo* GetScriptInfo() const
+ {
+ return m_pScriptInfo;
+ }
+
+ const Point &GetPos() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bPos, "DrawTextInfo: Undefined Position" );
+#endif
+ return m_aPos;
+ }
+
+ TextFrameIndex *GetHyphPos() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bHyph, "DrawTextInfo: Undefined Hyph Position" );
+#endif
+ return m_pHyphPos;
+ }
+
+ vcl::text::TextLayoutCache const* GetVclCache() const
+ {
+ return m_pCachedVclData;
+ }
+
+ const OUString &GetText() const
+ {
+ return m_aText;
+ }
+
+ sw::WrongListIterator* GetWrong() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bWrong, "DrawTextInfo: Undefined WrongList" );
+#endif
+ return m_pWrong;
+ }
+
+ sw::WrongListIterator* GetGrammarCheck() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bGrammarCheck, "DrawTextInfo: Undefined GrammarCheck List" );
+#endif
+ return m_pGrammarCheck;
+ }
+
+ sw::WrongListIterator* GetSmartTags() const
+ {
+ return m_pSmartTags;
+ }
+
+ const Size &GetSize() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bSize, "DrawTextInfo: Undefined Size" );
+#endif
+ return m_aSize;
+ }
+
+ SwFont* GetFont() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bFnt, "DrawTextInfo: Undefined Font" );
+#endif
+ return m_pFnt;
+ }
+
+ SwUnderlineFont* GetUnderFnt() const
+ {
+ return m_pUnderFnt;
+ }
+
+ TextFrameIndex GetIdx() const
+ {
+ return m_nIdx;
+ }
+
+ TextFrameIndex GetLen() const
+ {
+ return m_nLen;
+ }
+
+ sal_Int32 GetOffset() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bOfst, "DrawTextInfo: Undefined Offset" );
+#endif
+ return m_nOfst;
+ }
+
+ TextFrameIndex GetEnd() const
+ {
+ return m_nIdx + m_nLen;
+ }
+
+ tools::Long GetKanaDiff() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bKana, "DrawTextInfo: Undefined kana difference" );
+#endif
+ return m_nKanaDiff;
+ }
+
+ sal_uInt16 GetWidth() const
+ {
+ return m_nWidth;
+ }
+
+ sal_uInt16 GetAscent() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bAscent, "DrawTextInfo: Undefined Ascent" );
+#endif
+ return m_nAscent;
+ }
+
+ sal_uInt16 GetKanaComp() const
+ {
+ return m_nCompress;
+ }
+
+ tools::Long GetCharacterSpacing() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bCharacterSpacing, "DrawTextInfo: Undefined CharacterSpacing" );
+#endif
+ return m_nCharacterSpacing;
+ }
+
+ tools::Long GetKern() const
+ {
+ return m_nKern;
+ }
+
+ tools::Long GetSpace() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bSpace, "DrawTextInfo: Undefined Spacing" );
+#endif
+ return m_nSpace;
+ }
+
+ TextFrameIndex GetNumberOfBlanks() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bNumberOfBlanks, "DrawTextInfo::Undefined NumberOfBlanks" );
+#endif
+ return m_nNumberOfBlanks;
+ }
+
+ sal_uInt8 GetCursorBidiLevel() const
+ {
+ return m_nCursorBidiLevel;
+ }
+
+ bool GetBullet() const
+ {
+ return m_bBullet;
+ }
+
+ bool GetUpper() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bUppr, "DrawTextInfo: Undefined Upperflag" );
+#endif
+ return m_bUpper;
+ }
+
+ bool GetDrawSpace() const
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bDrawSp, "DrawTextInfo: Undefined DrawSpaceflag" );
+#endif
+ return m_bDrawSpace;
+ }
+
+ bool GetGreyWave() const
+ {
+ return m_bGreyWave;
+ }
+
+ bool IsSpaceStop() const
+ {
+ return m_bSpaceStop;
+ }
+
+ bool SnapToGrid() const
+ {
+ return m_bSnapToGrid;
+ }
+
+ bool IsIgnoreFrameRTL() const
+ {
+ return m_bIgnoreFrameRTL;
+ }
+
+ bool IsPosMatchesBounds() const
+ {
+ return m_bPosMatchesBounds;
+ }
+
+ void SetOut( OutputDevice &rNew )
+ {
+ m_pOut = &rNew;
+ }
+
+ void SetPos( const Point &rNew )
+ {
+ m_aPos = rNew;
+#ifdef DBG_UTIL
+ m_bPos = true;
+#endif
+ }
+
+ void SetHyphPos(TextFrameIndex *const pNew)
+ {
+ m_pHyphPos = pNew;
+#ifdef DBG_UTIL
+ m_bHyph = true;
+#endif
+ }
+
+ void SetText( const OUString &rNew )
+ {
+ assert( (m_nLen == TextFrameIndex(COMPLETE_STRING)) ? (m_nIdx.get() < rNew.getLength()) : (m_nIdx + m_nLen).get() <= rNew.getLength() );
+ m_aText = rNew;
+ m_pCachedVclData = nullptr; // would any case benefit from save/restore?
+ }
+
+ // These methods are here so we can set all the related fields together to preserve the invariants that we assert
+ void SetTextIdxLen( const OUString &rNewStr, TextFrameIndex const nNewIdx, TextFrameIndex const nNewLen )
+ {
+ assert( (nNewLen == TextFrameIndex(COMPLETE_STRING)) ? (nNewIdx.get() < rNewStr.getLength()) : (nNewIdx + nNewLen).get() <= rNewStr.getLength() );
+ m_aText = rNewStr;
+ m_nIdx = nNewIdx;
+ m_nLen = nNewLen;
+ m_pCachedVclData = nullptr; // would any case benefit from save/restore?
+ }
+
+ // These methods are here so we can set all the related fields together to preserve the invariants that we assert
+ void SetIdxLen( TextFrameIndex const nNewIdx, TextFrameIndex const nNewLen )
+ {
+ assert( (nNewLen == TextFrameIndex(COMPLETE_STRING)) ? (nNewIdx.get() < m_aText.getLength()) : (nNewIdx + nNewLen).get() <= m_aText.getLength() );
+ m_nIdx = nNewIdx;
+ m_nLen = nNewLen;
+ }
+
+ void SetWrong(sw::WrongListIterator *const pNew)
+ {
+ m_pWrong = pNew;
+#ifdef DBG_UTIL
+ m_bWrong = true;
+#endif
+ }
+
+ void SetGrammarCheck(sw::WrongListIterator *const pNew)
+ {
+ m_pGrammarCheck = pNew;
+#ifdef DBG_UTIL
+ m_bGrammarCheck = true;
+#endif
+ }
+
+ void SetSmartTags(sw::WrongListIterator *const pNew)
+ {
+ m_pSmartTags = pNew;
+ }
+
+ void SetSize( const Size &rNew )
+ {
+ m_aSize = rNew;
+#ifdef DBG_UTIL
+ m_bSize = true;
+#endif
+ }
+
+ void SetFont( SwFont* pNew )
+ {
+ m_pFnt = pNew;
+#ifdef DBG_UTIL
+ m_bFnt = true;
+#endif
+ }
+
+ void SetIdx(TextFrameIndex const nNew)
+ {
+ assert( (m_nLen == TextFrameIndex(COMPLETE_STRING)) ? (nNew.get() < m_aText.getLength()) : (nNew + m_nLen).get() <= m_aText.getLength() );
+ m_nIdx = nNew;
+ }
+
+ void SetLen(TextFrameIndex const nNew)
+ {
+ assert( (nNew == TextFrameIndex(COMPLETE_STRING)) ? (m_nIdx.get() < m_aText.getLength()) : (m_nIdx + nNew).get() <= m_aText.getLength() );
+ m_nLen = nNew;
+ }
+
+ void SetOffset( sal_Int32 nNew )
+ {
+ m_nOfst = nNew;
+#ifdef DBG_UTIL
+ m_bOfst = true;
+#endif
+ }
+
+ void SetKanaDiff( tools::Long nNew )
+ {
+ m_nKanaDiff = nNew;
+#ifdef DBG_UTIL
+ m_bKana = true;
+#endif
+ }
+
+ void SetWidth( sal_uInt16 nNew )
+ {
+ m_nWidth = nNew;
+ }
+
+ void SetAscent( sal_uInt16 nNew )
+ {
+ m_nAscent = nNew;
+#ifdef DBG_UTIL
+ m_bAscent = true;
+#endif
+ }
+
+ void SetKern( tools::Long nNew )
+ {
+ m_nKern = nNew;
+ }
+
+ void SetSpace( tools::Long nNew )
+ {
+ if( nNew < 0 )
+ {
+ m_nCharacterSpacing = -nNew;
+ m_nSpace = 0;
+ }
+ else
+ {
+ m_nSpace = nNew;
+ m_nCharacterSpacing = 0;
+ }
+#ifdef DBG_UTIL
+ m_bSpace = true;
+ m_bCharacterSpacing = true;
+#endif
+ }
+
+ void SetNumberOfBlanks( TextFrameIndex const nNew )
+ {
+#ifdef DBG_UTIL
+ m_bNumberOfBlanks = true;
+#endif
+ m_nNumberOfBlanks = nNew;
+ }
+
+ void SetCursorBidiLevel( sal_uInt8 nNew )
+ {
+ m_nCursorBidiLevel = nNew;
+ }
+
+ void SetKanaComp( short nNew )
+ {
+ m_nCompress = nNew;
+ }
+
+ void SetBullet( bool bNew )
+ {
+ m_bBullet = bNew;
+ }
+
+ void SetUnderFnt( SwUnderlineFont* pULFnt )
+ {
+ m_pUnderFnt = pULFnt;
+ }
+
+ void SetUpper( bool bNew )
+ {
+ m_bUpper = bNew;
+#ifdef DBG_UTIL
+ m_bUppr = true;
+#endif
+ }
+
+ void SetDrawSpace( bool bNew )
+ {
+ m_bDrawSpace = bNew;
+#ifdef DBG_UTIL
+ m_bDrawSp = true;
+#endif
+ }
+
+ void SetGreyWave( bool bNew )
+ {
+ m_bGreyWave = bNew;
+ }
+
+ void SetSpaceStop( bool bNew )
+ {
+ m_bSpaceStop = bNew;
+ }
+
+ void SetSnapToGrid( bool bNew )
+ {
+ m_bSnapToGrid = bNew;
+ }
+
+ void SetIgnoreFrameRTL( bool bNew )
+ {
+ m_bIgnoreFrameRTL = bNew;
+ }
+
+ void SetPosMatchesBounds( bool bNew )
+ {
+ m_bPosMatchesBounds = bNew;
+ }
+
+ void Shift( Degree10 nDir );
+
+ // sets a new color at the output device if necessary if a font is passed
+ // as argument, the change if made to the font otherwise the font at the
+ // output device is changed returns if the font has been changed
+ bool ApplyAutoColor( vcl::Font* pFnt = nullptr );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/dropdowncontentcontrolbutton.hxx b/sw/source/core/inc/dropdowncontentcontrolbutton.hxx
new file mode 100644
index 000000000..6ca32974e
--- /dev/null
+++ b/sw/source/core/inc/dropdowncontentcontrolbutton.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "contentcontrolbutton.hxx"
+
+class SwEditWin;
+class SwContentControl;
+
+/**
+ * This button is shown when the cursor is inside a drop-down content control.
+ * The user can select a list item using this button while filling in a form.
+ */
+class SwDropDownContentControlButton final : public SwContentControlButton
+{
+private:
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+
+ DECL_LINK(ListBoxHandler, weld::TreeView&, bool);
+
+ void InitDropdown();
+
+public:
+ SwDropDownContentControlButton(SwEditWin* pEditWin,
+ const std::shared_ptr<SwContentControl>& pContentControl);
+ virtual ~SwDropDownContentControlButton() override;
+
+ virtual void LaunchPopup() override;
+ virtual void DestroyPopup() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/dumpfilter.hxx b/sw/source/core/inc/dumpfilter.hxx
new file mode 100644
index 000000000..6ebc6e8c9
--- /dev/null
+++ b/sw/source/core/inc/dumpfilter.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DUMPFILTER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DUMPFILTER_HXX
+
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+
+namespace sw {
+
+ /** Implementation of UNO export service to dump the layout of the
+ document as XML. This filter should be mostly be used for testing
+ purpose.
+ */
+ class LayoutDumpFilter final : public cppu::WeakImplHelper
+ <
+ css::document::XFilter,
+ css::document::XExporter,
+ css::lang::XInitialization,
+ css::lang::XServiceInfo
+ >
+ {
+ css::uno::Reference< css::lang::XComponent > m_xSrcDoc;
+
+ public:
+ LayoutDumpFilter();
+ virtual ~LayoutDumpFilter() override;
+
+ // XFilter
+ virtual sal_Bool SAL_CALL filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor ) override;
+ virtual void SAL_CALL cancel( ) override;
+
+ // XExporter
+ virtual void SAL_CALL setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ };
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/dview.hxx b/sw/source/core/inc/dview.hxx
new file mode 100644
index 000000000..cdcf3f265
--- /dev/null
+++ b/sw/source/core/inc/dview.hxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_DVIEW_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_DVIEW_HXX
+
+#include <svx/fmview.hxx>
+
+class FmFormModel;
+class OutputDevice;
+class SwViewShellImp;
+class SwFrame;
+class SwFlyFrame;
+class SwAnchoredObject;
+class SdrUndoManager;
+
+class SwDrawView final : public FmFormView
+{
+ Point m_aAnchorPoint; // anchor position
+ SwViewShellImp &m_rImp; // a view is always part of a shell
+
+ const SwFrame *CalcAnchor();
+
+ /** determine maximal order number for a 'child' object of given 'parent' object
+
+ The maximal order number will be determined on the current object
+ order hierarchy. It's the order number of the 'child' object with the
+ highest order number. The calculation can be influenced by parameter
+ <_pExclChildObj> - this 'child' object won't be considered.
+
+ @param <_rParentObj>
+ input parameter - 'parent' object, for which the maximal order number
+ for its 'children' will be determined.
+
+ @param <_pExclChildObj>
+ optional input parameter - 'child' object, which will not be considered
+ on the calculation of the maximal order number
+ */
+ static sal_uInt32 GetMaxChildOrdNum( const SwFlyFrame& _rParentObj,
+ const SdrObject* _pExclChildObj = nullptr );
+
+ /** method to move 'repeated' objects of the given moved object to the
+ according level
+
+ @param <_rMovedAnchoredObj>
+ input parameter - moved object, for which the 'repeated' ones have also
+ to be moved.
+
+ @param <_rMovedChildrenObjs>
+ input parameter - data collection of moved 'child' objects - the 'repeated'
+ ones of these 'children' will also been moved.
+ */
+ void MoveRepeatedObjs( const SwAnchoredObject& _rMovedAnchoredObj,
+ const std::vector<SdrObject*>& _rMovedChildObjs ) const;
+
+ // add custom handles (used by other apps, e.g. AnchorPos)
+ virtual void AddCustomHdl() override;
+
+ // override to allow extra handling when picking SwVirtFlyDrawObj's
+ using FmFormView::CheckSingleSdrObjectHit;
+ virtual SdrObject* CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const override;
+
+ // Create a local UndoManager
+ std::unique_ptr<SdrUndoManager> createLocalTextUndoManager() override;
+
+public:
+ SwDrawView(
+ SwViewShellImp &rI,
+ FmFormModel& rFmFormModel,
+ OutputDevice* pOutDev);
+
+ // from base class
+ virtual SdrObject* GetMaxToTopObj(SdrObject* pObj) const override;
+ virtual SdrObject* GetMaxToBtmObj(SdrObject* pObj) const override;
+ virtual void MarkListHasChanged() override;
+
+ // #i7672#
+ // Override to reuse edit background color in active text edit view (OutlinerView)
+ virtual void ModelHasChanged() override;
+
+ virtual void ObjOrderChanged( SdrObject* pObj, size_t nOldPos,
+ size_t nNewPos ) override;
+ virtual bool TakeDragLimit(SdrDragMode eMode, tools::Rectangle& rRect) const override;
+ virtual void MakeVisible( const tools::Rectangle&, vcl::Window &rWin ) override;
+ virtual void CheckPossibilities() override;
+
+ const SwViewShellImp &Imp() const { return m_rImp; }
+ SwViewShellImp &Imp() { return m_rImp; }
+
+ // anchor and Xor for dragging
+ void ShowDragAnchor();
+
+ virtual void DeleteMarked() override;
+
+ void ValidateMarkList() { FlushComeBackTimer(); }
+
+ // #i99665#
+ static bool IsAntiAliasing();
+
+ // method to replace marked/selected <SwDrawVirtObj>
+ // by its reference object for delete of selection and group selection
+ static void ReplaceMarkedDrawVirtObjs( SdrMarkView& _rMarkView );
+
+ /// See SdrMarkView::GetSfxViewShell().
+ SfxViewShell* GetSfxViewShell() const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/environmentofanchoredobject.hxx b/sw/source/core/inc/environmentofanchoredobject.hxx
new file mode 100644
index 000000000..610bd934f
--- /dev/null
+++ b/sw/source/core/inc/environmentofanchoredobject.hxx
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ENVIRONMENTOFANCHOREDOBJECT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ENVIRONMENTOFANCHOREDOBJECT_HXX
+
+class SwFrame;
+class SwLayoutFrame;
+
+namespace objectpositioning
+{
+class SwEnvironmentOfAnchoredObject
+{
+private:
+ const bool mbFollowTextFlow;
+
+public:
+ /** constructor
+
+ @param _bFollowTextFlow
+ input parameter - indicates, if the anchored object, for which
+ this environment is instantiated, follow the text flow or not
+ */
+ SwEnvironmentOfAnchoredObject(const bool _bFollowTextFlow);
+
+ /** determine environment layout frame for possible horizontal object
+ positions respectively for alignment to 'page areas'
+
+ this is, if object has to follow the text flow:
+ - cell frame, if anchored inside a cell
+ - fly frame, if anchored inside a fly frame
+ otherwise it's the page frame
+
+ this is, if object hasn't to follow the text flow:
+ - page frame.
+ - no exception any more. Thus remove
+ parameter <_bForPageAlignment>
+
+ @param _rHoriOrientFrame
+ input parameter - frame, at which the horizontal position is
+ oriented at (typically it's the anchor frame).
+ starting point for the search of the layout frame.
+
+ @return reference to the layout frame, which determines the
+ horizontal environment the object has to be positioned in.
+ */
+ const SwLayoutFrame& GetHoriEnvironmentLayoutFrame(const SwFrame& _rHoriOrientFrame) const;
+
+ /** determine environment layout frame for possible vertical object
+ positions respectively for alignments to 'page areas'
+
+ this is, if object has to follow the text flow:
+ - cell frame, if anchored inside a cell
+ - fly frame, if anchored inside a fly frame
+ - header/footer frame, if anchored inside page header/footer
+ - footnote frame, if anchored inside footnote
+ otherwise it's the document body frame
+
+ this is, if object hasn't to follow the text flow:
+ - page frame.
+ - no exception any more. Thus remove
+ parameter <_bForPageAlignment>
+
+ @param _rVertOrientFrame
+ input parameter - frame, at which the vertical position is
+ oriented at (typically it's the anchor frame).
+ starting point for the search of the layout frame.
+
+ @return reference to the layout frame, which determines the
+ vertical environment the object has to be positioned in.
+ */
+ const SwLayoutFrame& GetVertEnvironmentLayoutFrame(const SwFrame& _rVertOrientFrame) const;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/fefly.hxx b/sw/source/core/inc/fefly.hxx
new file mode 100644
index 000000000..411bf7586
--- /dev/null
+++ b/sw/source/core/inc/fefly.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FEFLY_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FEFLY_HXX
+
+class SwFlyFrame;
+class SfxItemSet;
+
+bool sw_ChkAndSetNewAnchor(const SwFlyFrame& rFly, SfxItemSet& rSet);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/fieldhint.hxx b/sw/source/core/inc/fieldhint.hxx
new file mode 100644
index 000000000..f71970f89
--- /dev/null
+++ b/sw/source/core/inc/fieldhint.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FIELDHINT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FIELDHINT_HXX
+
+#include <svl/hint.hxx>
+
+class SwPaM;
+class SwRootFrame;
+
+class SwFieldHint final : public SfxHint
+{
+public:
+ SwPaM* m_pPaM;
+ SwRootFrame const* m_pLayout;
+
+ SwFieldHint(SwPaM* const pPaM, SwRootFrame const* const pLayout)
+ : m_pPaM(pPaM)
+ , m_pLayout(pLayout)
+ {
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/flowfrm.hxx b/sw/source/core/inc/flowfrm.hxx
new file mode 100644
index 000000000..965c0b8cf
--- /dev/null
+++ b/sw/source/core/inc/flowfrm.hxx
@@ -0,0 +1,279 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FLOWFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FLOWFRM_HXX
+
+#include "frame.hxx"
+#include "layfrm.hxx"
+#include <swtypes.hxx>
+
+class SvxFormatKeepItem;
+class SvxFormatBreakItem;
+class SwPageFrame;
+class SwRect;
+class SwBorderAttrs;
+class SwDoc;
+class SwNodeIndex;
+
+/** Base class that provides the general functionalities for frames that are
+ allowed at page breaks (flow) and shall continue on the next page (can be
+ split), e.g. paragraphs (ContentFrame) or tables (TabFrame).
+
+ Some parts of these functionalities are implemented in FlowFrame while the
+ specific ones are done in the corresponding Frame classes. The FlowFrame has to
+ be seen as a base class. As such it is no Frame by itself and thus no direct
+ instances of FlowFrame can exist.
+
+ Actually it is not even a real Frame. The obvious implementation would be a
+ FlowFrame that is virtually inherited from SwFrame and that works with its own
+ member data. Further classes would need to inherit from FlowFrame and (via
+ multiple base classes since the class tree splits exactly at the branch
+ from SwFrame to SwContentFrame and SwLayoutFrame) also virtually from SwFrame as
+ well. Unfortunately, this leads - besides problems with compilers and
+ debugging programs - to high additional costs, that we IMHO are not able to
+ afford nowadays.
+
+ Hence, we use another technique: A FlowFrame keeps a reference to a SwFrame
+ - which it is actually itself - and they are friends. As a result, the
+ FlowFrame can work with the reference to the SwFrame instead of working with
+ its own this-pointer.
+ */
+class SwFlowFrame
+{
+ // PrepareMake is allowed to lock/unlock (robustness)
+ friend inline void PrepareLock ( SwFlowFrame * );
+ friend inline void PrepareUnlock( SwFlowFrame * );
+ friend inline void TableSplitRecalcLock( SwFlowFrame * );
+ friend inline void TableSplitRecalcUnlock( SwFlowFrame * );
+ // #i44049#
+ friend class SwObjectFormatterTextFrame;
+ friend class FlowFrameJoinLockGuard;
+
+ // TableSel is allowed to reset the follow-bit
+ friend inline void UnsetFollow( SwFlowFrame *pFlow );
+
+ friend void MakeFrames( SwDoc *, const SwNodeIndex &, const SwNodeIndex & );
+
+ friend class SwNode2LayImpl;
+
+ SwFrame& m_rThis;
+
+ // helper methods for MoveSubTree()
+ static SwLayoutFrame *CutTree( SwFrame* );
+ static bool PasteTree( SwFrame *, SwLayoutFrame *, SwFrame *, SwFrame* );
+
+ /** indicates that a backward move was done over multiple pages
+
+ Needed for the interaction of _GetPrevxxx and MoveBwd so that multiple
+ pages can be skipped at the same time. In addition, it is evaluated by
+ the MoveBwd() method in TabFrame.
+ */
+ static bool s_bMoveBwdJump;
+
+ /** helper method to determine previous frame for calculation of the
+ upper space
+
+ #i11860#
+
+ @param _pProposedPrevFrame
+ optional input parameter - pointer to frame, which should be used
+ instead of the direct previous frame.
+ */
+ const SwFrame* GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame = nullptr ) const;
+
+ /** method to determine the upper space amount, which is considered for
+ the previous frame
+
+ #i11860#
+ */
+ SwTwips GetUpperSpaceAmountConsideredForPrevFrame() const;
+
+ /** method to determine the upper space amount, which is considered for
+ the page grid
+
+ #i11860#
+ */
+ SwTwips GetUpperSpaceAmountConsideredForPageGrid_(
+ const SwTwips _nUpperSpaceWithoutGrid ) const;
+
+protected:
+ SwFlowFrame *m_pFollow;
+ SwFlowFrame *m_pPrecede;
+
+ bool m_bLockJoin :1; // if true than joins (and thus deletes) are prohibited!
+ bool m_bUndersized:1; // I am smaller than needed
+ bool m_bFlyLock :1; // stop positioning of at-character flyframes
+
+ // checks if forward flow makes sense to prevent infinite moves
+ inline bool IsFwdMoveAllowed() const;
+ // #i44049# - method <CalcContent(..)> has to check this property.
+ friend void CalcContent( SwLayoutFrame *pLay, bool bNoColl );
+ bool IsKeepFwdMoveAllowed( bool bIgnoreMyOwnKeepValue = false ); // like above, forward flow for Keep.
+
+ /** method to determine overlapping of an object that requests floating
+
+ 0: no overlapping
+ 1: objects that are anchored at the FlowFrame overlap
+ 2: objects that are anchored somewhere else overlap
+ 3: both types of objects overlap
+ */
+ sal_uInt8 BwdMoveNecessary( const SwPageFrame *pPage, const SwRect &rRect );
+
+ void LockJoin() { m_bLockJoin = true; }
+ void UnlockJoin() { m_bLockJoin = false; }
+
+ bool CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue );
+ bool MoveFwd( bool bMakePage, bool bPageBreak, bool bMoveAlways = false );
+ bool MoveBwd( bool &rbReformat );
+ virtual bool ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )=0;
+
+public:
+ SwFlowFrame( SwFrame &rFrame );
+ virtual ~SwFlowFrame();
+
+ const SwFrame& GetFrame() const { return m_rThis; }
+ SwFrame& GetFrame() { return m_rThis; }
+
+ static bool IsMoveBwdJump() { return s_bMoveBwdJump; }
+ static void SetMoveBwdJump( bool bNew ){ s_bMoveBwdJump = bNew; }
+
+ void SetUndersized( const bool bNew ) { m_bUndersized = bNew; }
+ bool IsUndersized() const { return m_bUndersized; }
+
+ bool IsPrevObjMove() const;
+
+ /** hook tree onto new parent with minimal operations and notifications */
+ void MoveSubTree( SwLayoutFrame* pParent, SwFrame* pSibling = nullptr );
+
+ bool HasFollow() const { return m_pFollow != nullptr; }
+ bool IsFollow() const { return nullptr != m_pPrecede; }
+ bool IsAnFollow( const SwFlowFrame *pFlow ) const;
+ const SwFlowFrame *GetFollow() const { return m_pFollow; }
+ SwFlowFrame *GetFollow() { return m_pFollow; }
+ void SetFollow(SwFlowFrame *const pFollow);
+
+ const SwFlowFrame *GetPrecede() const { return m_pPrecede; }
+ SwFlowFrame *GetPrecede() { return m_pPrecede; }
+
+ bool IsJoinLocked() const { return m_bLockJoin; }
+ bool IsAnyJoinLocked() const { return m_bLockJoin || HasLockedFollow(); }
+
+ bool IsPageBreak( bool bAct ) const;
+ bool IsColBreak( bool bAct ) const;
+
+ /** method to determine if a Keep needs to be considered (Breaks!) */
+ bool IsKeep(SvxFormatKeepItem const& rKeep,
+ SvxFormatBreakItem const& rBreak,
+ bool bBreakCheck = false ) const;
+
+ bool HasLockedFollow() const;
+
+ bool HasParaSpaceAtPages( bool bSct ) const;
+
+ /** method to determine the upper space hold by the frame
+
+ #i11860#
+
+ @param _bConsiderGrid
+ optional input parameter - consider the page grid while calculating?
+ */
+ SwTwips CalcUpperSpace( const SwBorderAttrs *pAttrs = nullptr,
+ const SwFrame* pPr = nullptr,
+ const bool _bConsiderGrid = true ) const;
+
+ /** method to determine the upper space amount, which is considered for
+ the previous frame and the page grid, if option 'Use former object
+ positioning' is OFF
+
+ #i11860#
+ */
+ SwTwips GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() const;
+
+ /** calculation of lower space */
+ SwTwips CalcLowerSpace( const SwBorderAttrs* _pAttrs = nullptr ) const;
+
+ /** calculation of the additional space to be considered, if flow frame
+ is the last inside a table cell
+
+ #i26250
+
+ @param _pAttrs
+ optional input parameter - border attributes of the flow frame.
+ Used for optimization, if caller has already determined the border
+ attributes.
+
+ @return SwTwips
+ */
+ SwTwips CalcAddLowerSpaceAsLastInTableCell(
+ const SwBorderAttrs* _pAttrs = nullptr ) const;
+
+ void CheckKeep();
+
+ void SetFlyLock( bool bNew ){ m_bFlyLock = bNew; }
+ bool IsFlyLock() const { return m_bFlyLock; }
+
+ bool ForbiddenForFootnoteCntFwd() const;
+
+ // Casting of a Frame into a FlowFrame (if it is one, otherwise 0)
+ // These methods need to be customized in subclasses!
+ static SwFlowFrame *CastFlowFrame( SwFrame *pFrame );
+ static const SwFlowFrame *CastFlowFrame( const SwFrame *pFrame );
+};
+
+inline bool SwFlowFrame::IsFwdMoveAllowed() const
+{
+ return m_rThis.GetIndPrev() != nullptr;
+}
+
+//use this to protect a SwLayoutFrame for a given scope from getting merged with
+//its neighbour and thus deleted
+class FlowFrameJoinLockGuard
+{
+private:
+ SwFlowFrame *m_pFlow;
+ bool m_bOldJoinLocked;
+public:
+ //JoinLock pParent for the lifetime of the Cut/Paste call, etc. to avoid
+ //SwSectionFrame::MergeNext removing the pParent we're trying to reparent
+ //into
+ FlowFrameJoinLockGuard(SwLayoutFrame* pFrame)
+ {
+ m_pFlow = SwFlowFrame::CastFlowFrame(pFrame);
+ if (m_pFlow)
+ {
+ m_bOldJoinLocked = m_pFlow->IsJoinLocked();
+ m_pFlow->LockJoin();
+ }
+ else
+ {
+ m_bOldJoinLocked = false;
+ }
+ }
+
+ ~FlowFrameJoinLockGuard()
+ {
+ if (m_pFlow && !m_bOldJoinLocked)
+ m_pFlow->UnlockJoin();
+ }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/flyfrm.hxx b/sw/source/core/inc/flyfrm.hxx
new file mode 100644
index 000000000..467407d16
--- /dev/null
+++ b/sw/source/core/inc/flyfrm.hxx
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FLYFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FLYFRM_HXX
+
+#include "layfrm.hxx"
+#include <vector>
+#include <frmfmt.hxx>
+#include <anchoredobject.hxx>
+#include <swdllapi.h>
+
+class SwFormatAnchor;
+class SwPageFrame;
+class SwFormatFrameSize;
+struct SwCursorMoveState;
+class SwBorderAttrs;
+class SwVirtFlyDrawObj;
+class SwAttrSetChg;
+namespace tools { class PolyPolygon; }
+class SwFormat;
+class SwViewShell;
+class SwFEShell;
+class SwWrtShell;
+
+
+/** search an anchor for paragraph bound frames starting from pOldAnch
+
+ needed for dragging of objects bound to a paragraph for showing an anchor
+ indicator as well as for changing the anchor.
+
+ implemented in layout/flycnt.cxx
+ */
+const SwContentFrame *FindAnchor( const SwFrame *pOldAnch, const Point &rNew,
+ const bool bBody = false );
+
+/** calculate rectangle in that the object can be moved or rather be resized */
+bool CalcClipRect( const SdrObject *pSdrObj, SwRect &rRect, bool bMove = true );
+
+enum class SwFlyFrameInvFlags : sal_uInt8
+{
+ NONE = 0x00,
+ InvalidatePos = 0x01,
+ InvalidateSize = 0x02,
+ InvalidatePrt = 0x04,
+ SetNotifyBack = 0x08,
+ SetCompletePaint = 0x10,
+ InvalidateBrowseWidth = 0x20,
+ ClearContourCache = 0x40,
+ UpdateObjInSortedList = 0x80,
+};
+
+namespace o3tl {
+ template<> struct typed_flags<SwFlyFrameInvFlags> : is_typed_flags<SwFlyFrameInvFlags, 0x00ff> {};
+}
+
+
+/** general base class for all free-flowing frames
+
+ #i26791# - inherit also from <SwAnchoredFlyFrame>
+*/
+class SW_DLLPUBLIC SwFlyFrame : public SwLayoutFrame, public SwAnchoredObject
+{
+ // is allowed to lock, implemented in frmtool.cxx
+ friend void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *const pFormat, const SwFormatAnchor & rAnch);
+ friend void Notify( SwFlyFrame *, SwPageFrame *pOld, const SwRect &rOld,
+ const SwRect* pOldPrt );
+
+ void InitDrawObj(SwFrame const&); // these to methods are called in the
+ void FinitDrawObj(); // constructors
+
+ void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, SwFlyFrameInvFlags &,
+ SwAttrSetChg *pa = nullptr, SwAttrSetChg *pb = nullptr );
+
+ using SwLayoutFrame::CalcRel;
+
+protected:
+ // Predecessor/Successor for chaining with text flow
+ SwFlyFrame *m_pPrevLink, *m_pNextLink;
+ static const SwFormatAnchor* GetAnchorFromPoolItem(const SfxPoolItem& rItem);
+
+private:
+ // It must be possible to block Content-bound flys so that they will be not
+ // formatted; in this case MakeAll() returns immediately. This is necessary
+ // for page changes during formatting. In addition, it is needed during
+ // the constructor call of the root object since otherwise the anchor will
+ // be formatted before the root is anchored correctly to a shell and
+ // because too much would be formatted as a result.
+ bool m_bLocked :1;
+ // true if the background of NotifyDTor needs to be notified at the end
+ // of a MakeAll() call.
+ bool m_bNotifyBack :1;
+
+protected:
+ // Pos, PrtArea or SSize have been invalidated - they will be evaluated
+ // again immediately because they have to be valid _at all time_.
+ // The invalidation is tracked here so that LayAction knows about it and
+ // can handle it properly. Exceptions prove the rule.
+ bool m_bInvalid :1;
+
+ // true if the proposed height of an attribute is a minimal height
+ // (this means that the frame can grow higher if needed)
+ bool m_bMinHeight :1;
+ // true if the fly frame could not format position/size based on its
+ // attributes, e.g. because there was not enough space.
+ bool m_bHeightClipped :1;
+ bool m_bWidthClipped :1;
+ // If true then call only the format after adjusting the width (CheckClip);
+ // but the width will not be re-evaluated based on the attributes.
+ bool m_bFormatHeightOnly :1;
+
+ bool m_bInCnt :1; ///< RndStdIds::FLY_AS_CHAR, anchored as character
+ bool m_bAtCnt :1; ///< RndStdIds::FLY_AT_PARA, anchored at paragraph
+ ///< or RndStdIds::FLY_AT_CHAR
+ bool m_bLayout :1; ///< RndStdIds::FLY_AT_PAGE, RndStdIds::FLY_AT_FLY, at page or at frame
+ bool m_bAutoPosition :1; ///< RndStdIds::FLY_AT_CHAR, anchored at character
+ bool m_bDeleted :1; ///< Anchored to a tracked deletion
+ size_t m_nAuthor; ///< Redline author index for colored crossing out
+
+ friend class SwNoTextFrame; // is allowed to call NotifyBackground
+
+ Point m_aContentPos; // content area's position relatively to Frame
+ bool m_bValidContentPos;
+
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ void MakePrtArea( const SwBorderAttrs &rAttrs );
+ void MakeContentPos( const SwBorderAttrs &rAttrs );
+
+ void Lock() { m_bLocked = true; }
+ void Unlock() { m_bLocked = false; }
+
+ Size CalcRel( const SwFormatFrameSize &rSz ) const;
+
+ SwFlyFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor );
+
+ virtual void DestroyImpl() override;
+ virtual ~SwFlyFrame() override;
+
+ /** method to assure that anchored object is registered at the correct
+ page frame
+ */
+ virtual void RegisterAtCorrectPage() override;
+ virtual void RegisterAtPage(SwPageFrame &) override;
+
+ virtual bool SetObjTop_( const SwTwips _nTop ) override;
+ virtual bool SetObjLeft_( const SwTwips _nLeft ) override;
+
+ virtual SwRect GetObjBoundRect() const override;
+ virtual void SwClientNotify(const SwModify& rMod, const SfxHint& rHint) override;
+
+ virtual const IDocumentDrawModelAccess& getIDocumentDrawModelAccess( ) override;
+
+ SwTwips CalcContentHeight(const SwBorderAttrs *pAttrs, const SwTwips nMinHeight, const SwTwips nUL);
+
+public:
+ // #i26791#
+
+ // get client information
+ virtual bool GetInfo( SfxPoolItem& ) const override;
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ virtual Size ChgSize( const Size& aNewSize ) override;
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false ) const override;
+
+ virtual void CheckDirection( bool bVert ) override;
+ virtual void Cut() override;
+#ifdef DBG_UTIL
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+#endif
+
+ SwTwips Shrink_( SwTwips, bool bTst );
+ SwTwips Grow_ ( SwTwips, bool bTst );
+ void Invalidate_( SwPageFrame const *pPage = nullptr );
+
+ bool FrameSizeChg( const SwFormatFrameSize & );
+
+ SwFlyFrame *GetPrevLink() const { return m_pPrevLink; }
+ SwFlyFrame *GetNextLink() const { return m_pNextLink; }
+
+ static void ChainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow );
+ static void UnchainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow );
+
+ SwFlyFrame *FindChainNeighbour( SwFrameFormat const &rFormat, SwFrame *pAnch = nullptr );
+
+ // #i26791#
+ const SwVirtFlyDrawObj* GetVirtDrawObj() const;
+ SwVirtFlyDrawObj *GetVirtDrawObj();
+ void NotifyDrawObj();
+
+ void ChgRelPos( const Point &rAbsPos );
+ bool IsInvalid() const { return m_bInvalid; }
+ void Invalidate() const { const_cast<SwFlyFrame*>(this)->m_bInvalid = true; }
+ void Validate() const { const_cast<SwFlyFrame*>(this)->m_bInvalid = false; }
+
+ bool IsMinHeight() const { return m_bMinHeight; }
+ bool IsLocked() const { return m_bLocked; }
+ bool IsAutoPos() const { return m_bAutoPosition; }
+ bool IsFlyInContentFrame() const { return m_bInCnt; }
+ bool IsFlyFreeFrame() const { return m_bAtCnt || m_bLayout; }
+ bool IsFlyLayFrame() const { return m_bLayout; }
+ bool IsFlyAtContentFrame() const { return m_bAtCnt; }
+ bool IsDeleted() const { return m_bDeleted; }
+ void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
+ void SetAuthor( size_t nAuthor ) { m_nAuthor = nAuthor; }
+ size_t GetAuthor() const { return m_nAuthor; }
+
+ bool IsNotifyBack() const { return m_bNotifyBack; }
+ void SetNotifyBack() { m_bNotifyBack = true; }
+ void ResetNotifyBack() { m_bNotifyBack = false; }
+
+ bool IsClipped() const { return m_bHeightClipped || m_bWidthClipped; }
+ bool IsHeightClipped() const { return m_bHeightClipped; }
+
+ bool IsLowerOf( const SwLayoutFrame* pUpper ) const;
+ bool IsUpperOf( const SwFlyFrame& _rLower ) const
+ {
+ return _rLower.IsLowerOf( this );
+ }
+
+ SwFrame *FindLastLower();
+
+ // #i13147# - add parameter <_bForPaint> to avoid load of
+ // the graphic during paint. Default value: false
+ bool GetContour( tools::PolyPolygon& rContour,
+ const bool _bForPaint = false ) const;
+
+ // Paint on this shell (consider Preview, print flag, etc. recursively)?
+ static bool IsPaint( SdrObject *pObj, const SwViewShell *pSh );
+
+ /** SwFlyFrame::IsBackgroundTransparent
+
+ determines if background of fly frame has to be drawn transparently
+
+ definition found in /core/layout/paintfrm.cxx
+
+ @return true, if background color is transparent or an existing background
+ graphic is transparent.
+ */
+ bool IsBackgroundTransparent() const;
+
+ void Chain( SwFrame* _pAnchor );
+ void Unchain();
+ void InsertCnt();
+ void DeleteCnt();
+ void InsertColumns();
+
+ // #i26791# - pure virtual methods of base class <SwAnchoredObject>
+ virtual void MakeObjPos() override;
+ virtual void InvalidateObjPos() override;
+
+ virtual SwFrameFormat& GetFrameFormat() override;
+ virtual const SwFrameFormat& GetFrameFormat() const override;
+
+ virtual SwRect GetObjRect() const override;
+
+ /** method to determine if a format on the Writer fly frame is possible
+
+ #i28701#
+ refine 'IsFormatPossible'-conditions of method
+ <SwAnchoredObject::IsFormatPossible()> by:
+ format isn't possible, if Writer fly frame is locked resp. col-locked.
+ */
+ virtual bool IsFormatPossible() const override;
+ static void GetAnchoredObjects( std::vector<SwAnchoredObject*>&, const SwFormat& rFormat );
+
+ // overwriting "SwFrameFormat *SwLayoutFrame::GetFormat" to provide the correct derived return type.
+ // (This is in order to skip on the otherwise necessary casting of the result to
+ // 'SwFlyFrameFormat *' after calls to this function. The casting is now done in this function.)
+ virtual const SwFlyFrameFormat *GetFormat() const override;
+ virtual SwFlyFrameFormat *GetFormat() override;
+
+ virtual void dumpAsXml( xmlTextWriterPtr writer ) const override { SwLayoutFrame::dumpAsXml( writer ); };
+
+ virtual void Calc(vcl::RenderContext* pRenderContext) const override;
+
+ const Point& ContentPos() const { return m_aContentPos; }
+ Point& ContentPos() { return m_aContentPos; }
+
+ void InvalidateContentPos();
+
+ void SelectionHasChanged(SwFEShell* pShell);
+ bool IsShowUnfloatButton(SwWrtShell* pWrtSh) const;
+
+ // For testing only (see uiwriter)
+ void ActiveUnfloatButton(SwWrtShell* pWrtSh);
+
+ virtual const SwFlyFrame* DynCastFlyFrame() const override;
+ virtual SwFlyFrame* DynCastFlyFrame() override;
+
+private:
+ void UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const;
+ void PaintDecorators() const;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/flyfrms.hxx b/sw/source/core/inc/flyfrms.hxx
new file mode 100644
index 000000000..cf79bf2fe
--- /dev/null
+++ b/sw/source/core/inc/flyfrms.hxx
@@ -0,0 +1,240 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FLYFRMS_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FLYFRMS_HXX
+
+#include <sal/config.h>
+
+#include "flyfrm.hxx"
+
+class SwNoTextFrame;
+
+double getLocalFrameRotation_from_SwNoTextFrame(const SwNoTextFrame& rNoTextFrame);
+
+// Base class for those Flys that can "move freely" or better that are not
+// bound in Content.
+class SwFlyFreeFrame : public SwFlyFrame
+{
+private:
+ // #i34753# - flag for at-page anchored Writer fly frames
+ // to prevent a positioning - call of method <MakeObjPos()> -, if Writer
+ // fly frame is already clipped during its format by the object formatter.
+ bool mbNoMakePos : 1;
+
+ // #i37068# - flag to prevent move in method <CheckClip(..)>
+ bool mbNoMoveOnCheckClip : 1;
+
+ SwRect maUnclippedFrame;
+
+ // RotateFlyFrame3 add TransformableSwFrame
+ std::unique_ptr< TransformableSwFrame > mpTransformableSwFrame;
+
+ void CheckClip( const SwFormatFrameSize &rSz ); //'Emergency' Clipping.
+
+ /** determines, if direct environment of fly frame has 'auto' size
+
+ #i17297#
+ start with anchor frame and search for a header, footer, row or fly frame
+ stopping at page frame.
+ return <true>, if such a frame is found and it has 'auto' size.
+ otherwise <false> is returned.
+
+ @return boolean indicating, that direct environment has 'auto' size
+ */
+ bool HasEnvironmentAutoSize() const;
+
+ // RotateFlyFrame3 - Support for outer Frame of a SwGrfNode
+ // Only for local data extraction. To uniquely access information
+ // for local transformation, use getFrameArea(Print)Transformation
+ double getLocalFrameRotation() const;
+
+protected:
+ // #i28701# - new friend class <SwFlyNotify> for access to
+ // method <NotifyBackground>
+ friend class SwFlyNotify;
+ virtual void NotifyBackground( SwPageFrame *pPage,
+ const SwRect& rRect, PrepareHint eHint) override;
+ SwFlyFreeFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor );
+
+ virtual void DestroyImpl() override;
+ virtual ~SwFlyFreeFrame() override;
+
+public:
+ // #i28701#
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+
+ // #i37068# - accessors for member <mbNoMoveOnCheckClip>
+ void SetNoMoveOnCheckClip( const bool _bNewNoMoveOnCheckClip )
+ {
+ mbNoMoveOnCheckClip = _bNewNoMoveOnCheckClip;
+ }
+ bool IsNoMoveOnCheckClip() const
+ {
+ return mbNoMoveOnCheckClip;
+ }
+ // #i34753# - accessors for member <mbNoMakePos>
+ void SetNoMakePos( const bool _bNoMakePos )
+ {
+ if ( IsFlyLayFrame() )
+ {
+ mbNoMakePos = _bNoMakePos;
+ }
+ }
+ bool IsNoMakePos() const
+ {
+ if ( IsFlyLayFrame() )
+ {
+ return mbNoMakePos;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ const SwRect& GetUnclippedFrame( ) const
+ {
+ if ( maUnclippedFrame.HasArea( ) )
+ return maUnclippedFrame;
+ else
+ return getFrameArea();
+ }
+
+ /** method to determine, if a format on the Writer fly frame is possible
+
+ #i28701#
+ refine 'IsFormatPossible'-conditions of method
+ <SwFlyFrame::IsFormatPossible()> by:
+ format isn't possible, if Writer fly frame isn't registered at a page frame
+ and its anchor frame isn't inside another Writer fly frame.
+ */
+ virtual bool IsFormatPossible() const override;
+
+ // RotateFlyFrame3 - Support for Transformations
+ bool isTransformableSwFrame() const { return bool(mpTransformableSwFrame); }
+ TransformableSwFrame* getTransformableSwFrame() { return mpTransformableSwFrame.get(); }
+ const TransformableSwFrame* getTransformableSwFrame() const { return mpTransformableSwFrame.get(); }
+
+ // RotateFlyFrame3 - Support for AutoContour
+ bool supportsAutoContour() const;
+
+ // RotateFlyFrame3 - Support for Transformations
+ virtual basegfx::B2DHomMatrix getFrameAreaTransformation() const override;
+ virtual basegfx::B2DHomMatrix getFramePrintAreaTransformation() const override;
+
+ // RotateFlyFrame3 - Support for Transformations
+ virtual void transform_translate(const Point& rOffset) override;
+};
+
+// Flys that are bound to LayoutFrames and not to Content
+class SwFlyLayFrame final: public SwFlyFreeFrame
+{
+public:
+ // #i28701#
+
+ SwFlyLayFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor );
+
+ virtual void RegisterAtPage(SwPageFrame &) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+};
+
+// Flys that are bound to Content but not in Content
+class SwFlyAtContentFrame final: public SwFlyFreeFrame
+{
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+
+ // #i28701#
+ virtual bool InvalidationAllowed( const InvalidationType _nInvalid ) const override;
+
+ /** method to assure that anchored object is registered at the correct
+ page frame
+
+ #i28701#
+ */
+ virtual void RegisterAtCorrectPage() override;
+ virtual void RegisterAtPage(SwPageFrame &) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+public:
+ // #i28701#
+
+ SwFlyAtContentFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor );
+
+ void SetAbsPos( const Point &rNew );
+
+ // #i26791#
+ virtual void MakeObjPos() override;
+
+ /** method to determine, if a format on the Writer fly frame is possible
+
+ #i28701#
+ refine 'IsFormatPossible'-conditions of method
+ <SwFlyFreeFrame::IsFormatPossible()> by:
+ format isn't possible, if method <MakeAll()> is already in progress.
+ */
+ virtual bool IsFormatPossible() const override;
+};
+
+// Flys that are bound to a character in Content
+class SwFlyInContentFrame final: public SwFlyFrame
+{
+ Point m_aRef; // relative to this point AbsPos is being calculated
+
+ virtual void DestroyImpl() override;
+ virtual ~SwFlyInContentFrame() override;
+
+ virtual void NotifyBackground(SwPageFrame *pPage, const SwRect& rRect, PrepareHint eHint) override;
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+public:
+ // #i28701#
+
+ SwFlyInContentFrame( SwFlyFrameFormat*, SwFrame*, SwFrame *pAnchor );
+
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+
+ void SetRefPoint( const Point& rPoint, const Point &rRelAttr,
+ const Point &rRelPos );
+ const Point &GetRefPoint() const { return m_aRef; }
+ Point const & GetRelPos() const;
+
+ // (26.11.93, see tabfrm.hxx, but might also be valid for others)
+ // For creation of a Fly after a FlyCnt was created _and_ inserted.
+ // Must be called by creator because can be pasted only after creation.
+ // Sometimes the page for registering the Flys is not visible until then
+ // as well.
+ void RegistFlys();
+
+ //see layact.cxx
+ void AddRefOfst( tools::Long nOfst ) { m_aRef.AdjustY( nOfst ); }
+ void AddRefOfst(Point const& rOfst) { m_aRef += rOfst; }
+
+ // #i26791#
+ virtual void MakeObjPos() override;
+
+ // invalidate anchor frame on invalidation of the position, because the
+ // position is calculated during the format of the anchor frame
+ virtual void ActionOnInvalidation( const InvalidationType _nInvalid ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/fntcache.hxx b/sw/source/core/inc/fntcache.hxx
new file mode 100644
index 000000000..bea6663e1
--- /dev/null
+++ b/sw/source/core/inc/fntcache.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FNTCACHE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FNTCACHE_HXX
+
+#include <sal/config.h>
+
+#include <cstdint>
+#include <unordered_map>
+
+#include <vcl/font.hxx>
+#include <vcl/glyphitem.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/outdev.hxx>
+#include "swcache.hxx"
+#include "TextFrameIndex.hxx"
+
+class FontMetric;
+class SwFntObj;
+class SwDrawTextInfo;
+class SwViewShell;
+class SwSubFont;
+class MapMode;
+
+class SwFntCache : public SwCache
+{
+public:
+ SwFntCache() : SwCache(50
+#ifdef DBG_UTIL
+ , OString(RTL_CONSTASCII_STRINGPARAM("Global Font-Cache pFntCache"))
+#endif
+ ) {}
+
+ inline SwFntObj *First( );
+ static inline SwFntObj *Next( SwFntObj *pFntObj);
+ void Flush();
+};
+
+// Font cache, global variable, created/destroyed in txtinit.cxx
+extern SwFntCache *pFntCache;
+extern SwFntObj *pLastFont;
+
+class SwFntObj final : public SwCacheObj
+{
+ friend class SwFntAccess;
+ friend void InitCore();
+ friend void FinitCore();
+
+ vcl::Font m_aFont;
+ vcl::Font *m_pScrFont;
+ vcl::Font *m_pPrtFont;
+ VclPtr<OutputDevice> m_pPrinter;
+ sal_uInt16 m_nGuessedLeading;
+ sal_uInt16 m_nExtLeading;
+ sal_uInt16 m_nScrAscent;
+ sal_uInt16 m_nPrtAscent;
+ sal_uInt16 m_nScrHeight;
+ sal_uInt16 m_nPrtHeight;
+ sal_uInt16 m_nPropWidth;
+ sal_uInt16 m_nScrHangingBaseline;
+ sal_uInt16 m_nPrtHangingBaseline;
+ sal_uInt16 m_nZoom;
+ bool m_bSymbol : 1;
+ bool m_bPaintBlank : 1;
+
+ static tools::Long s_nPixWidth;
+ static MapMode *s_pPixMap;
+
+public:
+ SwFntObj( const SwSubFont &rFont, std::uintptr_t nFontCacheId,
+ SwViewShell const *pSh );
+
+ virtual ~SwFntObj() override;
+
+ vcl::Font *GetScrFont() { return m_pScrFont; }
+ vcl::Font& GetFont() { return m_aFont; }
+ const vcl::Font& GetFont() const { return m_aFont; }
+
+ sal_uInt16 GetGuessedLeading() const { return m_nGuessedLeading; }
+ sal_uInt16 GetExternalLeading() const { return m_nExtLeading; }
+
+ sal_uInt16 GetFontAscent( const SwViewShell *pSh, const OutputDevice& rOut );
+ sal_uInt16 GetFontHeight( const SwViewShell *pSh, const OutputDevice& rOut );
+ sal_uInt16 GetFontLeading( const SwViewShell *pSh, const OutputDevice& rOut );
+ sal_uInt16 GetFontHangingBaseline( const SwViewShell *pSh, const OutputDevice& rOut );
+
+ void GuessLeading( const SwViewShell& rSh, const FontMetric& rMet );
+
+ void SetDevFont( const SwViewShell *pSh, OutputDevice& rOut );
+ OutputDevice* GetPrt() const { return m_pPrinter; }
+ sal_uInt16 GetZoom() const { return m_nZoom; }
+ sal_uInt16 GetPropWidth() const { return m_nPropWidth; }
+ bool IsSymbol() const { return m_bSymbol; }
+
+ void DrawText( SwDrawTextInfo &rInf );
+ /// determine the TextSize (of the printer)
+ Size GetTextSize( SwDrawTextInfo &rInf );
+ TextFrameIndex GetModelPositionForViewPoint(SwDrawTextInfo &rInf);
+
+ void CreateScrFont( const SwViewShell& rSh, const OutputDevice& rOut );
+ void CreatePrtFont( const OutputDevice& rOut );
+};
+
+SwFntObj *SwFntCache::First( )
+{
+ return static_cast<SwFntObj *>(SwCache::First());
+}
+
+SwFntObj *SwFntCache::Next( SwFntObj *pFntObj)
+{
+ return static_cast<SwFntObj *>(SwCache::Next( pFntObj ));
+}
+
+class SwFntAccess final : public SwCacheAccess
+{
+ SwViewShell const *m_pShell;
+
+ virtual SwCacheObj *NewObj( ) override;
+
+public:
+ SwFntAccess( const void*& rnFontCacheId, sal_uInt16 &rIndex, const void *pOwner,
+ SwViewShell const *pShell,
+ bool bCheck = false );
+ SwFntObj* Get() { return static_cast<SwFntObj*>( SwCacheAccess::Get() ); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/fntcap.hxx b/sw/source/core/inc/fntcap.hxx
new file mode 100644
index 000000000..0b1cee67c
--- /dev/null
+++ b/sw/source/core/inc/fntcap.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FNTCAP_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FNTCAP_HXX
+
+#include <rtl/ustring.hxx>
+#include "TextFrameIndex.hxx"
+
+class SwFont;
+
+TextFrameIndex sw_CalcCaseMap( const SwFont& rFnt,
+ const OUString& rOrigString,
+ TextFrameIndex nOfst,
+ TextFrameIndex nLen,
+ TextFrameIndex nIdx );
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/frame.hxx b/sw/source/core/inc/frame.hxx
new file mode 100644
index 000000000..7cb9dffc2
--- /dev/null
+++ b/sw/source/core/inc/frame.hxx
@@ -0,0 +1,1438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FRAME_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FRAME_HXX
+
+#include <drawinglayer/primitive2d/Primitive2DContainer.hxx>
+#include <editeng/borderline.hxx>
+#include <svl/poolitem.hxx>
+#include <swtypes.hxx>
+#include <swrect.hxx>
+#include <calbck.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <com/sun/star/style/TabStop.hpp>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vcl/outdev.hxx>
+
+#include <memory>
+#include <optional>
+
+namespace drawinglayer::processor2d { class BaseProcessor2D; }
+
+class SwLayoutFrame;
+class SwRootFrame;
+class SwPageFrame;
+class SwBodyFrame;
+class SwFlyFrame;
+class SwSectionFrame;
+class SwFootnoteFrame;
+class SwFootnoteBossFrame;
+class SwTabFrame;
+class SwRowFrame;
+class SwContentFrame;
+class SwAttrSet;
+class Color;
+class SwBorderAttrs;
+class SwCache;
+class SvxBrushItem;
+class SvxFormatBreakItem;
+class SwFormatPageDesc;
+class SwSelectionList;
+struct SwPosition;
+struct SwCursorMoveState;
+class SwFormat;
+class SwPrintData;
+class SwSortedObjs;
+class SwAnchoredObject;
+enum class SvxFrameDirection;
+class IDocumentDrawModelAccess;
+class SwTextFrame;
+
+// Each FrameType is represented here as a bit.
+// The bits must be set in a way that it can be determined with masking of
+// which kind of FrameType an instance is _and_ from what classes it was derived.
+// Each frame has in its base class a member that must be set by the
+// constructors accordingly.
+enum class SwFrameType
+{
+ None = 0x0000,
+ Root = 0x0001,
+ Page = 0x0002,
+ Column = 0x0004,
+ Header = 0x0008,
+ Footer = 0x0010,
+ FtnCont = 0x0020,
+ Ftn = 0x0040,
+ Body = 0x0080,
+ Fly = 0x0100,
+ Section = 0x0200,
+// UNUSED 0x0400
+ Tab = 0x0800,
+ Row = 0x1000,
+ Cell = 0x2000,
+ Txt = 0x4000,
+ NoTxt = 0x8000,
+};
+
+namespace o3tl
+{
+ template<> struct typed_flags<SwFrameType> : is_typed_flags<SwFrameType, 0xfbff> {};
+};
+
+// for internal use some common combinations
+#define FRM_LAYOUT SwFrameType(0x3bFF)
+#define FRM_ALL SwFrameType(0xfbff)
+#define FRM_CNTNT (SwFrameType::Txt | SwFrameType::NoTxt)
+#define FRM_FTNBOSS (SwFrameType::Page | SwFrameType::Column)
+#define FRM_ACCESSIBLE (SwFrameType::Root | SwFrameType::Page | SwFrameType::Header | SwFrameType::Footer | SwFrameType::Ftn | SwFrameType::Fly | SwFrameType::Tab | SwFrameType::Cell | SwFrameType::Txt)
+#define FRM_NEIGHBOUR (SwFrameType::Column | SwFrameType::Cell)
+#define FRM_NOTE_VERT (SwFrameType::FtnCont | SwFrameType::Ftn | SwFrameType::Section | SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell | SwFrameType::Txt)
+#define FRM_HEADFOOT (SwFrameType::Header | SwFrameType::Footer)
+#define FRM_BODYFTNC (SwFrameType::FtnCont | SwFrameType::Body)
+
+// for GetNextLeaf/GetPrevLeaf.
+enum MakePageType
+{
+ MAKEPAGE_NONE, // do not create page/footnote
+ MAKEPAGE_APPEND, // only append page if needed
+ MAKEPAGE_INSERT, // add or append page if needed
+ MAKEPAGE_FTN, // add footnote if needed
+ MAKEPAGE_NOSECTION // Don't create section frames
+};
+
+namespace drawinglayer::attribute {
+ class SdrAllFillAttributesHelper;
+ typedef std::shared_ptr< SdrAllFillAttributesHelper > SdrAllFillAttributesHelperPtr;
+}
+
+/// Helper class to isolate geometry-defining members of SwFrame
+/// and to control their accesses. Moved to own class to have no
+/// hidden accesses to 'private' members anymore.
+///
+/// Added most important flags about the state of this geometric
+/// information and if it is valid or not
+class SAL_DLLPUBLIC_RTTI SwFrameAreaDefinition
+{
+private:
+ friend void FriendHackInvalidateRowFrame(SwFrameAreaDefinition &);
+
+ // The absolute position and size of the SwFrame in the document.
+ // This values are set by the layouter implementations
+ SwRect maFrameArea;
+
+ // The 'inner' Frame Area defined by a SwRect relative to FrameArea:
+ // When identical to FrameArea, Pos() will be (0, 0) and Size identical.
+ SwRect maFramePrintArea;
+
+ // bitfield
+ bool mbFrameAreaPositionValid : 1;
+ bool mbFrameAreaSizeValid : 1;
+ bool mbFramePrintAreaValid : 1;
+
+ // #i65250#
+ // frame ID is now in general available - used for layout loop control
+ static sal_uInt32 snLastFrameId;
+ const sal_uInt32 mnFrameId;
+
+protected:
+ // write access to mb*Valid flags
+ void setFrameAreaPositionValid(bool bNew);
+ void setFrameAreaSizeValid(bool bNew);
+ void setFramePrintAreaValid(bool bNew);
+
+public:
+ SwFrameAreaDefinition();
+ virtual ~SwFrameAreaDefinition();
+
+ // read access to mb*Valid flags
+ bool isFrameAreaPositionValid() const { return mbFrameAreaPositionValid; }
+ bool isFrameAreaSizeValid() const { return mbFrameAreaSizeValid; }
+ bool isFramePrintAreaValid() const { return mbFramePrintAreaValid; }
+
+ // syntactic sugar: test whole FrameAreaDefinition
+ bool isFrameAreaDefinitionValid() const { return isFrameAreaPositionValid() && isFrameAreaSizeValid() && isFramePrintAreaValid(); }
+
+ // #i65250#
+ sal_uInt32 GetFrameId() const { return mnFrameId; }
+
+ // read accesses to FrameArea definitions - only const access allowed.
+ // Do *not* const_cast results, it is necessary to track changes. use
+ // the below offered WriteAccess helper classes instead
+ const SwRect& getFrameArea() const { return maFrameArea; }
+ const SwRect& getFramePrintArea() const { return maFramePrintArea; }
+
+ // helper class(es) for FrameArea manipulation. These
+ // have to be used to apply changes to FrameAreas. They hold a copy of the
+ // SwRect for manipulation. It gets written back at destruction. Thus, this
+ // mechanism depends on scope usage, take care. It prevents errors using
+ // different instances of SwFrame in get/set methods which is more safe
+ class FrameAreaWriteAccess : public SwRect
+ {
+ private:
+ SwFrameAreaDefinition& mrTarget;
+
+ FrameAreaWriteAccess(const FrameAreaWriteAccess&) = delete;
+ FrameAreaWriteAccess& operator=(const FrameAreaWriteAccess&) = delete;
+
+ public:
+ FrameAreaWriteAccess(SwFrameAreaDefinition& rTarget) : SwRect(rTarget.getFrameArea()), mrTarget(rTarget) {}
+ ~FrameAreaWriteAccess();
+ void setSwRect(const SwRect& rNew) { *reinterpret_cast< SwRect* >(this) = rNew; }
+ };
+
+ // same helper for FramePrintArea
+ class FramePrintAreaWriteAccess : public SwRect
+ {
+ private:
+ SwFrameAreaDefinition& mrTarget;
+
+ FramePrintAreaWriteAccess(const FramePrintAreaWriteAccess&) = delete;
+ FramePrintAreaWriteAccess& operator=(const FramePrintAreaWriteAccess&) = delete;
+
+ public:
+ FramePrintAreaWriteAccess(SwFrameAreaDefinition& rTarget) : SwRect(rTarget.getFramePrintArea()), mrTarget(rTarget) {}
+ ~FramePrintAreaWriteAccess();
+ void setSwRect(const SwRect& rNew) { *reinterpret_cast< SwRect* >(this) = rNew; }
+ };
+
+ // RotateFlyFrame3 - Support for Transformations
+ // Hand out the Transformations for the current FrameAreaDefinition
+ // for the FrameArea and FramePrintArea.
+ // FramePrintArea is not relative to FrameArea in this
+ // transformation representation (to make it easier to use and understand).
+ // There is no 'set' method since SwFrame is a layout object. For
+ // some cases rotation will be included (used for SwGrfNode in inner
+ // SwFrame of a SwFlyFrame)
+ virtual basegfx::B2DHomMatrix getFrameAreaTransformation() const;
+ virtual basegfx::B2DHomMatrix getFramePrintAreaTransformation() const;
+
+ // RotateFlyFrame3 - Support for Transformations
+ // Modify current transformations by applying given translation
+ virtual void transform_translate(const Point& rOffset);
+};
+
+/// RotateFlyFrame3: Helper class when you want to make your SwFrame derivate
+/// transformable. It provides some tooling to do so. To use, add as member
+/// (see e.g. SwFlyFreeFrame which uses 'std::unique_ptr< TransformableSwFrame >')
+class TransformableSwFrame
+{
+private:
+ // The SwFrameAreaDefinition to work on
+ SwFrameAreaDefinition& mrSwFrameAreaDefinition;
+
+ // FrameAreaTransformation and FramePrintAreaTransformation
+ // !identity when needed (translate/scale is used (e.g. rotation))
+ basegfx::B2DHomMatrix maFrameAreaTransformation;
+ basegfx::B2DHomMatrix maFramePrintAreaTransformation;
+
+public:
+ TransformableSwFrame(SwFrameAreaDefinition& rSwFrameAreaDefinition)
+ : mrSwFrameAreaDefinition(rSwFrameAreaDefinition),
+ maFrameAreaTransformation(),
+ maFramePrintAreaTransformation()
+ {
+ }
+
+ // get SwFrameArea in transformation form
+ const basegfx::B2DHomMatrix& getLocalFrameAreaTransformation() const
+ {
+ return maFrameAreaTransformation;
+ }
+
+ // get SwFramePrintArea in transformation form
+ const basegfx::B2DHomMatrix& getLocalFramePrintAreaTransformation() const
+ {
+ return maFramePrintAreaTransformation;
+ }
+
+ // Helpers to re-create the untransformed SwRect(s) originally
+ // in the SwFrameAreaDefinition, based on the current Transformations.
+ SwRect getUntransformedFrameArea() const;
+ SwRect getUntransformedFramePrintArea() const;
+
+ // Helper method to re-create FrameAreaTransformations based on the
+ // current FrameAreaDefinition transformed by given rotation and Center
+ void createFrameAreaTransformations(
+ double fRotation,
+ const basegfx::B2DPoint& rCenter);
+
+ // Tooling method to reset the SwRect(s) in the current
+ // SwFrameAreaDefinition which are already adapted to
+ // Transformation back to the untransformed state, using
+ // the getUntransformedFrame*Area calls above when needed.
+ // Only the SwRect(s) are changed back, not the transformations.
+ void restoreFrameAreas();
+
+ // Re-Creates the SwRect(s) as BoundAreas based on the current
+ // set Transformations.
+ void adaptFrameAreasToTransformations();
+
+ // Modify current definitions by applying the given transformation
+ void transform(const basegfx::B2DHomMatrix& aTransform);
+};
+
+enum class SwFrameInvFlags : sal_uInt8
+{
+ NONE = 0x00,
+ InvalidatePrt = 0x01,
+ InvalidateSize = 0x02,
+ InvalidatePos = 0x04,
+ SetCompletePaint = 0x08,
+ NextInvalidatePos = 0x10,
+ NextSetCompletePaint = 0x20,
+};
+
+namespace o3tl {
+ template<> struct typed_flags<SwFrameInvFlags> : is_typed_flags<SwFrameInvFlags, 0x003f> {};
+}
+
+/**
+ * Base class of the Writer layout elements.
+ *
+ * This includes not only fly frames, but everything down to the paragraph
+ * level: pages, headers, footers, etc. (Inside a paragraph SwLinePortion
+ * instances are used.)
+ */
+class SW_DLLPUBLIC SwFrame : public SwFrameAreaDefinition, public SwClient, public SfxBroadcaster
+{
+ // the hidden Frame
+ friend class SwFlowFrame;
+ friend class SwLayoutFrame;
+ friend class SwLooping;
+ friend class SwDeletionChecker; // for GetDep()
+
+ // voids lower during creation of a column
+ friend SwFrame *SaveContent( SwLayoutFrame *, SwFrame* pStart );
+ friend void RestoreContent( SwFrame *, SwLayoutFrame *, SwFrame *pSibling );
+
+ // for validating a mistakenly invalidated one in SwContentFrame::MakeAll
+ friend void ValidateSz( SwFrame *pFrame );
+ // implemented in text/txtftn.cxx, prevents Footnote oscillation
+ friend void ValidateText( SwFrame *pFrame );
+
+ friend void MakeNxt( SwFrame *pFrame, SwFrame *pNxt );
+
+ // cache for (border) attributes
+ static SwCache *spCache;
+
+ SwRootFrame *mpRoot;
+ SwLayoutFrame *mpUpper;
+ SwFrame *mpNext;
+ SwFrame *mpPrev;
+
+ // sw_redlinehide: hide these dangerous SwClient functions
+ using SwClient::GetRegisteredInNonConst;
+ using SwClient::GetRegisteredIn;
+
+ SwFrame *FindNext_();
+ SwFrame *FindPrev_();
+
+ /** method to determine next content frame in the same environment
+ for a flow frame (content frame, table frame, section frame)
+
+ #i27138# - adding documentation:
+ Travelling downwards through the layout to determine the next content
+ frame in the same environment. There are several environments in a
+ document, which form a closed context regarding this function. These
+ environments are:
+ - Each page header
+ - Each page footer
+ - Each unlinked fly frame
+ - Each group of linked fly frames
+ - All footnotes
+ - All document body frames
+ #i27138# - adding parameter <_bInSameFootnote>
+ Its default value is <false>. If its value is <true>, the environment
+ 'All footnotes' is no longer treated. Instead each footnote is treated
+ as an own environment.
+
+ @param _bInSameFootnote
+ input parameter - boolean indicating, that the found next content
+ frame has to be in the same footnote frame. This parameter is only
+ relevant for flow frames in footnotes.
+
+ @return SwContentFrame*
+ pointer to the found next content frame. It's NULL, if none exists.
+ */
+ SwContentFrame* FindNextCnt_( const bool _bInSameFootnote );
+
+ /** method to determine previous content frame in the same environment
+ for a flow frame (content frame, table frame, section frame)
+
+ #i27138#
+ Travelling upwards through the layout to determine the previous content
+ frame in the same environment. There are several environments in a
+ document, which form a closed context regarding this function. These
+ environments are:
+ - Each page header
+ - Each page footer
+ - Each unlinked fly frame
+ - Each group of linked fly frames
+ - All footnotes
+ - All document body frames
+ #i27138# - adding parameter <_bInSameFootnote>
+ Its default value is <false>. If its value is <true>, the environment
+ 'All footnotes' is no longer treated. Instead each footnote is treated
+ as an own environment.
+
+ The found previous content frame has to be in the same footnote frame. This is only
+ relevant for flow frames in footnotes.
+
+ @return SwContentFrame*
+ pointer to the found previous content frame. It's NULL, if none exists.
+ */
+ SwContentFrame* FindPrevCnt_();
+
+ void UpdateAttrFrame( const SfxPoolItem*, const SfxPoolItem*, SwFrameInvFlags & );
+ SwFrame* GetIndNext_();
+ void SetDirFlags( bool bVert );
+
+ const SwLayoutFrame* ImplGetNextLayoutLeaf( bool bFwd ) const;
+
+ SwPageFrame* ImplFindPageFrame();
+
+protected:
+ std::unique_ptr<SwSortedObjs> m_pDrawObjs; // draw objects, can be null
+ SwFrameType mnFrameType; //Who am I?
+
+ bool mbInDtor : 1;
+ bool mbInvalidR2L : 1;
+ bool mbDerivedR2L : 1;
+ bool mbRightToLeft : 1;
+ bool mbInvalidVert : 1;
+ bool mbDerivedVert : 1;
+ bool mbVertical : 1;
+
+ bool mbVertLR : 1;
+ bool mbVertLRBT : 1;
+
+ bool mbValidLineNum : 1;
+ bool mbFixSize : 1;
+
+ // if true, frame will be painted completely even content was changed
+ // only partially. For ContentFrames a border (from Action) will exclusively
+ // painted if <mbCompletePaint> is true.
+ bool mbCompletePaint : 1;
+
+ bool mbRetouche : 1; // frame is responsible for retouching
+
+ bool mbInfInvalid : 1; // InfoFlags are invalid
+ bool mbInfBody : 1; // Frame is in document body
+ bool mbInfTab : 1; // Frame is in a table
+ bool mbInfFly : 1; // Frame is in a Fly
+ bool mbInfFootnote : 1; // Frame is in a footnote
+ bool mbInfSct : 1; // Frame is in a section
+ bool mbColLocked : 1; // lock Grow/Shrink for column-wise section
+ // or fly frames, will be set in Format
+ bool m_isInDestroy : 1;
+ int mnForbidDelete;
+
+ void ColLock() { mbColLocked = true; }
+ void ColUnlock() { mbColLocked = false; }
+
+ virtual void DestroyImpl();
+ virtual ~SwFrame() override;
+
+ // Only used by SwRootFrame Ctor to get 'this' into mpRoot...
+ void setRootFrame( SwRootFrame* pRoot ) { mpRoot = pRoot; }
+
+ SwPageFrame *InsertPage( SwPageFrame *pSibling, bool bFootnote );
+ void PrepareMake(vcl::RenderContext* pRenderContext);
+ void OptPrepareMake();
+ virtual void MakePos();
+ // Format next frame of table frame to assure keeping attributes.
+ // In case of nested tables method <SwFrame::MakeAll()> is called to
+ // avoid formatting of superior table frame.
+ friend SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame );
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) = 0;
+ // adjust frames of a page
+ SwTwips AdjustNeighbourhood( SwTwips nDiff, bool bTst = false );
+
+ // change only frame size not the size of PrtArea
+ virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = false ) = 0;
+ virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) = 0;
+
+ /// use these so we can grep for SwFrame's GetRegisteredIn accesses
+ /// beware that SwTextFrame may return sw::WriterMultiListener
+ sw::BroadcastingModify* GetDep()
+ { return static_cast<sw::BroadcastingModify*>(GetRegisteredInNonConst()); }
+ const sw::BroadcastingModify* GetDep() const
+ { return static_cast<const sw::BroadcastingModify*>(GetRegisteredIn()); }
+
+ SwFrame( sw::BroadcastingModify*, SwFrame* );
+
+ void CheckDir( SvxFrameDirection nDir, bool bVert, bool bOnlyBiDi, bool bBrowse );
+
+ /** enumeration for the different invalidations
+ #i28701#
+ */
+ enum InvalidationType
+ {
+ INVALID_SIZE, INVALID_PRTAREA, INVALID_POS, INVALID_LINENUM, INVALID_ALL
+ };
+
+ /** method to determine, if an invalidation is allowed.
+ #i28701
+ */
+ virtual bool InvalidationAllowed( const InvalidationType _nInvalid ) const;
+
+ /** method to perform additional actions on an invalidation
+
+ #i28701#
+ Method has *only* to contain actions, which has to be performed on
+ *every* assignment of the corresponding flag to <false>.
+ */
+ virtual void ActionOnInvalidation( const InvalidationType _nInvalid );
+
+ // draw shadow and borders
+ void PaintShadow( const SwRect&, SwRect&, const SwBorderAttrs& ) const;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+ virtual const IDocumentDrawModelAccess& getIDocumentDrawModelAccess( );
+
+public:
+ virtual css::uno::Sequence< css::style::TabStop > GetTabStopInfo( SwTwips )
+ {
+ return css::uno::Sequence< css::style::TabStop >();
+ }
+
+
+ SwFrameType GetType() const { return mnFrameType; }
+
+ static SwCache &GetCache() { return *spCache; }
+ static SwCache *GetCachePtr() { return spCache; }
+ static void SetCache( SwCache *pNew ) { spCache = pNew; }
+
+ // change PrtArea size and FrameSize
+ SwTwips Shrink( SwTwips, bool bTst = false, bool bInfo = false );
+ SwTwips Grow ( SwTwips, bool bTst = false, bool bInfo = false );
+
+ // different methods for inserting in layout tree (for performance reasons)
+
+ // insert before pBehind or at the end of the chain below mpUpper
+ void InsertBefore( SwLayoutFrame* pParent, SwFrame* pBehind );
+ // insert after pBefore or at the beginning of the chain below mpUpper
+ void InsertBehind( SwLayoutFrame *pParent, SwFrame *pBefore );
+ // insert before pBehind or at the end of the chain while considering
+ // the siblings of pSct
+ bool InsertGroupBefore( SwFrame* pParent, SwFrame* pWhere, SwFrame* pSct );
+ void RemoveFromLayout();
+
+ // For internal use only - who ignores this will be put in a sack and has
+ // to stay there for two days
+ // Does special treatment for Get_[Next|Prev]Leaf() (for tables).
+ SwLayoutFrame *GetLeaf( MakePageType eMakePage, bool bFwd );
+ SwLayoutFrame *GetNextLeaf ( MakePageType eMakePage );
+ SwLayoutFrame *GetNextFootnoteLeaf( MakePageType eMakePage );
+ SwLayoutFrame *GetNextSctLeaf( MakePageType eMakePage );
+ SwLayoutFrame *GetNextCellLeaf();
+ SwLayoutFrame *GetPrevLeaf ();
+ SwLayoutFrame *GetPrevFootnoteLeaf( MakePageType eMakeFootnote );
+ SwLayoutFrame *GetPrevSctLeaf();
+ SwLayoutFrame *GetPrevCellLeaf();
+ const SwLayoutFrame *GetLeaf ( MakePageType eMakePage, bool bFwd,
+ const SwFrame *pAnch ) const;
+
+ bool WrongPageDesc( SwPageFrame* pNew );
+
+ //#i28701# - new methods to append/remove drawing objects
+ void AppendDrawObj( SwAnchoredObject& _rNewObj );
+ void RemoveDrawObj( SwAnchoredObject& _rToRemoveObj );
+
+ // work with chain of FlyFrames
+ void AppendFly( SwFlyFrame *pNew );
+ void RemoveFly( SwFlyFrame *pToRemove );
+ const SwSortedObjs *GetDrawObjs() const { return m_pDrawObjs.get(); }
+ SwSortedObjs *GetDrawObjs() { return m_pDrawObjs.get(); }
+ void SetDrawObjsAsDeleted( bool bDeleted ); // change tracking of objects anchored to character
+ // #i28701# - change purpose of method and adjust its name
+ void InvalidateObjs( const bool _bNoInvaOfAsCharAnchoredObjs = true );
+
+ virtual void PaintSwFrameShadowAndBorder(
+ const SwRect&,
+ const SwPageFrame* pPage,
+ const SwBorderAttrs&) const;
+ void PaintBaBo( const SwRect&, const SwPageFrame *pPage,
+ const bool bOnlyTextBackground = false) const;
+ void PaintSwFrameBackground( const SwRect&, const SwPageFrame *pPage,
+ const SwBorderAttrs &,
+ const bool bLowerMode = false,
+ const bool bLowerBorder = false,
+ const bool bOnlyTextBackground = false ) const;
+ void PaintBorderLine( const SwRect&, const SwRect&, const SwPageFrame*,
+ const Color *pColor,
+ const SvxBorderLineStyle = SvxBorderLineStyle::SOLID ) const;
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> CreateProcessor2D( ) const;
+ void ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const;
+
+ // retouch, not in the area of the given Rect!
+ void Retouch( const SwPageFrame *pPage, const SwRect &rRect ) const;
+
+ bool GetBackgroundBrush(
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const SvxBrushItem*& rpBrush,
+ std::optional<Color>& rxColor,
+ SwRect &rOrigRect,
+ bool bLowerMode,
+ bool bConsiderTextBox ) const;
+
+ inline void SetCompletePaint() const;
+ inline void ResetCompletePaint() const;
+ bool IsCompletePaint() const { return mbCompletePaint; }
+
+ inline void SetRetouche() const;
+ inline void ResetRetouche() const;
+ bool IsRetouche() const { return mbRetouche; }
+
+ void SetInfFlags();
+ void InvalidateInfFlags() { mbInfInvalid = true; }
+ inline bool IsInDocBody() const; // use InfoFlags, determine flags
+ inline bool IsInFootnote() const; // if necessary
+ inline bool IsInTab() const;
+ inline bool IsInFly() const;
+ inline bool IsInSct() const;
+
+ // If frame is inside a split table row, this function returns
+ // the corresponding row frame in the follow table.
+ const SwRowFrame* IsInSplitTableRow() const;
+
+ // If frame is inside a follow flow row, this function returns
+ // the corresponding row frame master table
+ const SwRowFrame* IsInFollowFlowRow() const;
+
+ bool IsInBalancedSection() const;
+
+ inline bool IsVertical() const;
+ inline bool IsVertLR() const;
+ inline bool IsVertLRBT() const;
+
+ void SetDerivedVert( bool bNew ){ mbDerivedVert = bNew; }
+ void SetInvalidVert( bool bNew) { mbInvalidVert = bNew; }
+ inline bool IsRightToLeft() const;
+ void SetDerivedR2L( bool bNew ) { mbDerivedR2L = bNew; }
+
+ void CheckDirChange();
+ // returns upper left frame position for LTR and
+ // upper right frame position for Asian / RTL frames
+ Point GetFrameAnchorPos( bool bIgnoreFlysAnchoredAtThisFrame ) const;
+
+ /** determine, if frame is moveable in given environment
+
+ method replaced 'old' method <bool IsMoveable() const>.
+ Determines, if frame is moveable in given environment. if no environment
+ is given (parameter _pLayoutFrame == 0), the movability in the actual
+ environment (<GetUpper()) is checked.
+
+ @param _pLayoutFrame
+ input parameter - given environment (layout frame), in which the movability
+ will be checked. If not set ( == 0 ), actual environment is taken.
+
+ @return boolean, indicating, if frame is moveable in given environment
+ */
+ bool IsMoveable( const SwLayoutFrame* _pLayoutFrame = nullptr ) const;
+
+ // Is it permitted for the (Text)Frame to add a footnote in the current
+ // environment (not e.g. for repeating table headlines)
+ bool IsFootnoteAllowed() const;
+
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr );
+
+ virtual void CheckDirection( bool bVert );
+
+ void ReinitializeFrameSizeAttrFlags();
+
+ /// WARNING: this may not return correct RES_PAGEDESC/RES_BREAK items for
+ /// SwTextFrame, use GetBreakItem()/GetPageDescItem() instead
+ const SwAttrSet *GetAttrSet() const;
+ virtual const SvxFormatBreakItem& GetBreakItem() const;
+ virtual const SwFormatPageDesc& GetPageDescItem() const;
+
+ bool HasFixSize() const { return mbFixSize; }
+
+ // check all pages (starting from the given) and correct them if needed
+ static void CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields = true, SwPageFrame** ppPrev = nullptr);
+
+ // might return 0, with and without const
+ SwFrame *GetNext() { return mpNext; }
+ SwFrame *GetPrev() { return mpPrev; }
+ SwLayoutFrame *GetUpper() { return mpUpper; }
+ SwRootFrame *getRootFrame(){ return mpRoot; }
+ SwPageFrame *FindPageFrame() { return IsPageFrame() ? reinterpret_cast<SwPageFrame*>(this) : ImplFindPageFrame(); }
+ SwFrame *FindColFrame();
+ SwRowFrame *FindRowFrame();
+ SwFootnoteBossFrame *FindFootnoteBossFrame( bool bFootnotes = false );
+ SwTabFrame *ImplFindTabFrame();
+ SwFootnoteFrame *ImplFindFootnoteFrame();
+ SwFlyFrame *ImplFindFlyFrame();
+ SwSectionFrame *ImplFindSctFrame();
+ const SwBodyFrame *ImplFindBodyFrame() const;
+ SwFrame *FindFooterOrHeader();
+ SwFrame *GetLower();
+ const SwFrame *GetNext() const { return mpNext; }
+ const SwFrame *GetPrev() const { return mpPrev; }
+ const SwLayoutFrame *GetUpper() const { return mpUpper; }
+ const SwRootFrame *getRootFrame() const { return mpRoot; }
+ inline SwTabFrame *FindTabFrame();
+ inline SwFootnoteFrame *FindFootnoteFrame();
+ inline SwFlyFrame *FindFlyFrame();
+ inline SwSectionFrame *FindSctFrame();
+ inline SwFrame *FindNext();
+ // #i27138# - add parameter <_bInSameFootnote>
+ SwContentFrame* FindNextCnt( const bool _bInSameFootnote = false );
+ inline SwFrame *FindPrev();
+ inline const SwPageFrame *FindPageFrame() const;
+ inline const SwFootnoteBossFrame *FindFootnoteBossFrame( bool bFootnote = false ) const;
+ inline const SwFrame *FindColFrame() const;
+ inline const SwFrame *FindFooterOrHeader() const;
+ inline const SwTabFrame *FindTabFrame() const;
+ inline const SwFootnoteFrame *FindFootnoteFrame() const;
+ inline const SwFlyFrame *FindFlyFrame() const;
+ inline const SwSectionFrame *FindSctFrame() const;
+ inline const SwBodyFrame *FindBodyFrame() const;
+ inline const SwFrame *FindNext() const;
+ // #i27138# - add parameter <_bInSameFootnote>
+ const SwContentFrame* FindNextCnt( const bool _bInSameFootnote = false ) const;
+ inline const SwFrame *FindPrev() const;
+ const SwFrame *GetLower() const;
+
+ SwContentFrame* FindPrevCnt();
+
+ const SwContentFrame* FindPrevCnt() const;
+
+ // #i79774#
+ SwFrame* GetIndPrev_() const;
+ SwFrame* GetIndPrev() const
+ { return ( mpPrev || !IsInSct() ) ? mpPrev : GetIndPrev_(); }
+
+ SwFrame* GetIndNext()
+ { return ( mpNext || !IsInSct() ) ? mpNext : GetIndNext_(); }
+ const SwFrame* GetIndNext() const { return const_cast<SwFrame*>(this)->GetIndNext(); }
+
+ sal_uInt16 GetPhyPageNum() const; // page number without offset
+ sal_uInt16 GetVirtPageNum() const; // page number with offset
+ bool OnRightPage() const { return 0 != GetPhyPageNum() % 2; };
+ bool WannaRightPage() const;
+ bool OnFirstPage() const;
+
+ inline const SwLayoutFrame *GetPrevLayoutLeaf() const;
+ inline const SwLayoutFrame *GetNextLayoutLeaf() const;
+ inline SwLayoutFrame *GetPrevLayoutLeaf();
+ inline SwLayoutFrame *GetNextLayoutLeaf();
+
+ virtual void Calc(vcl::RenderContext* pRenderContext) const; // here might be "formatted"
+ inline void OptCalc() const; // here we assume (for optimization) that
+ // the predecessors are already formatted
+ Point GetRelPos() const;
+
+ // PaintArea is the area where content might be displayed.
+ // The margin of a page or the space between columns belongs to it.
+ SwRect GetPaintArea() const;
+
+ // UnionFrame is the union of Frame- and PrtArea, normally identical
+ // to the FrameArea except in case of negative Prt margins.
+ SwRect UnionFrame( bool bBorder = false ) const;
+
+ virtual Size ChgSize( const Size& aNewSize );
+
+ virtual void Cut() = 0;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) = 0;
+
+ void ValidateLineNum() { mbValidLineNum = true; }
+
+ bool GetValidLineNumFlag()const { return mbValidLineNum; }
+
+ // Only invalidate Frame
+ // #i28701# - add call to method <ActionOnInvalidation(..)>
+ // for all invalidation methods.
+ // #i28701# - use method <InvalidationAllowed(..)> to
+ // decide, if invalidation will to be performed or not.
+ // #i26945# - no additional invalidation, if it's already
+ // invalidate.
+ void InvalidateSize_()
+ {
+ if ( isFrameAreaSizeValid() && InvalidationAllowed( INVALID_SIZE ) )
+ {
+ setFrameAreaSizeValid(false);
+ ActionOnInvalidation( INVALID_SIZE );
+ }
+ }
+ void InvalidatePrt_()
+ {
+ if ( isFramePrintAreaValid() && InvalidationAllowed( INVALID_PRTAREA ) )
+ {
+ setFramePrintAreaValid(false);
+ ActionOnInvalidation( INVALID_PRTAREA );
+ }
+ }
+ void InvalidatePos_()
+ {
+ if ( isFrameAreaPositionValid() && InvalidationAllowed( INVALID_POS ) )
+ {
+ setFrameAreaPositionValid(false);
+ ActionOnInvalidation( INVALID_POS );
+ }
+ }
+ void InvalidateLineNum_()
+ {
+ if ( mbValidLineNum && InvalidationAllowed( INVALID_LINENUM ) )
+ {
+ mbValidLineNum = false;
+ ActionOnInvalidation( INVALID_LINENUM );
+ }
+ }
+ void InvalidateAll_()
+ {
+ if ( ( isFrameAreaSizeValid() || isFramePrintAreaValid() || isFrameAreaPositionValid() ) && InvalidationAllowed( INVALID_ALL ) )
+ {
+ setFrameAreaSizeValid(false);
+ setFrameAreaPositionValid(false);
+ setFramePrintAreaValid(false);
+ ActionOnInvalidation( INVALID_ALL );
+ }
+ }
+ // also notify page at the same time
+ inline void InvalidateSize();
+ inline void InvalidatePrt();
+ inline void InvalidatePos();
+ inline void InvalidateLineNum();
+ inline void InvalidateAll();
+ void ImplInvalidateSize();
+ void ImplInvalidatePrt();
+ void ImplInvalidatePos();
+ void ImplInvalidateLineNum();
+
+ inline void InvalidateNextPos( bool bNoFootnote = false );
+ void ImplInvalidateNextPos( bool bNoFootnote );
+
+ /** method to invalidate printing area of next frame
+ #i11859#
+ */
+ void InvalidateNextPrtArea();
+
+ void InvalidatePage( const SwPageFrame *pPage = nullptr ) const;
+
+ virtual bool FillSelection( SwSelectionList& rList, const SwRect& rRect ) const;
+
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false ) const;
+ virtual bool GetCharRect( SwRect &, const SwPosition&,
+ SwCursorMoveState* = nullptr, bool bAllowFarAway = true ) const;
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const;
+
+ // HACK: shortcut between frame and formatting
+ // It's your own fault if you cast void* incorrectly! In any case check
+ // the void* for 0.
+ virtual bool Prepare( const PrepareHint ePrep = PrepareHint::Clear,
+ const void *pVoid = nullptr, bool bNotify = true );
+
+ // true if it is the correct class, false otherwise
+ inline bool IsLayoutFrame() const;
+ inline bool IsRootFrame() const;
+ inline bool IsPageFrame() const;
+ inline bool IsColumnFrame() const;
+ inline bool IsFootnoteBossFrame() const; // footnote bosses might be PageFrames or ColumnFrames
+ inline bool IsHeaderFrame() const;
+ inline bool IsFooterFrame() const;
+ inline bool IsFootnoteContFrame() const;
+ inline bool IsFootnoteFrame() const;
+ inline bool IsBodyFrame() const;
+ inline bool IsColBodyFrame() const; // implemented in layfrm.hxx, BodyFrame above ColumnFrame
+ inline bool IsPageBodyFrame() const; // implemented in layfrm.hxx, BodyFrame above PageFrame
+ inline bool IsFlyFrame() const;
+ inline bool IsSctFrame() const;
+ inline bool IsTabFrame() const;
+ inline bool IsRowFrame() const;
+ inline bool IsCellFrame() const;
+ inline bool IsContentFrame() const;
+ inline bool IsTextFrame() const;
+ SwTextFrame* DynCastTextFrame();
+ const SwTextFrame* DynCastTextFrame() const;
+ inline bool IsNoTextFrame() const;
+ // Frames where its PrtArea depends on their neighbors and that are
+ // positioned in the content flow
+ inline bool IsFlowFrame() const;
+ // Frames that are capable of retouching or that might need to retouch behind
+ // themselves
+ inline bool IsRetoucheFrame() const;
+ inline bool IsAccessibleFrame() const;
+
+ void PrepareCursor(); // CursorShell is allowed to call this
+
+ // Is the Frame (or the section containing it) protected? Same for Fly in
+ // Fly in ... and footnotes
+ bool IsProtected() const;
+
+ bool IsColLocked() const { return mbColLocked; }
+ virtual bool IsDeleteForbidden() const { return mnForbidDelete > 0; }
+
+ /// this is the only way to delete a SwFrame instance
+ static void DestroyFrame(SwFrame *const pFrame);
+
+ bool IsInDtor() const { return mbInDtor; }
+
+ // No inline cause we need the function pointers
+ tools::Long GetTopMargin() const;
+ tools::Long GetBottomMargin() const;
+ tools::Long GetLeftMargin() const;
+ tools::Long GetRightMargin() const;
+ void SetTopBottomMargins( tools::Long, tools::Long );
+ void SetLeftRightMargins( tools::Long, tools::Long );
+ void SetRightLeftMargins( tools::Long, tools::Long );
+ tools::Long GetPrtLeft() const;
+ tools::Long GetPrtBottom() const;
+ tools::Long GetPrtRight() const;
+ tools::Long GetPrtTop() const;
+ bool SetMinLeft( tools::Long );
+ bool SetMaxBottom( tools::Long );
+ bool SetMaxRight( tools::Long );
+ void MakeBelowPos( const SwFrame*, const SwFrame*, bool );
+ void MakeLeftPos( const SwFrame*, const SwFrame*, bool );
+ void MakeRightPos( const SwFrame*, const SwFrame*, bool );
+ bool IsNeighbourFrame() const
+ { return bool(GetType() & FRM_NEIGHBOUR); }
+
+ // NEW TABLES
+ // Some functions for covered/covering table cells. This way unnecessary
+ // includes can be avoided
+ virtual bool IsLeaveUpperAllowed() const;
+ virtual bool IsCoveredCell() const;
+ bool IsInCoveredCell() const;
+
+ // #i81146# new loop control
+ bool KnowsFormat( const SwFormat& rFormat ) const;
+ void RegisterToFormat( SwFormat& rFormat );
+ void ValidateThisAndAllLowers( const sal_uInt16 nStage );
+
+ void ForbidDelete() { ++mnForbidDelete; }
+ void AllowDelete() { assert(mnForbidDelete > 0); --mnForbidDelete; }
+
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr getSdrAllFillAttributesHelper() const;
+ bool supportsFullDrawingLayerFillAttributeSet() const;
+
+public:
+ // if writer is NULL, dumps the layout structure as XML in layout.xml
+ virtual void dumpAsXml(xmlTextWriterPtr writer = nullptr) const;
+ void dumpTopMostAsXml(xmlTextWriterPtr writer = nullptr) const;
+ void dumpInfosAsXml(xmlTextWriterPtr writer) const;
+ virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const;
+ void dumpChildrenAsXml(xmlTextWriterPtr writer) const;
+ bool IsCollapse() const;
+};
+
+inline bool SwFrame::IsInDocBody() const
+{
+ if ( mbInfInvalid )
+ const_cast<SwFrame*>(this)->SetInfFlags();
+ return mbInfBody;
+}
+inline bool SwFrame::IsInFootnote() const
+{
+ if ( mbInfInvalid )
+ const_cast<SwFrame*>(this)->SetInfFlags();
+ return mbInfFootnote;
+}
+inline bool SwFrame::IsInTab() const
+{
+ if ( mbInfInvalid )
+ const_cast<SwFrame*>(this)->SetInfFlags();
+ return mbInfTab;
+}
+inline bool SwFrame::IsInFly() const
+{
+ if ( mbInfInvalid )
+ const_cast<SwFrame*>(this)->SetInfFlags();
+ return mbInfFly;
+}
+inline bool SwFrame::IsInSct() const
+{
+ if ( mbInfInvalid )
+ const_cast<SwFrame*>(this)->SetInfFlags();
+ return mbInfSct;
+}
+bool SwFrame::IsVertical() const
+{
+ if( mbInvalidVert )
+ const_cast<SwFrame*>(this)->SetDirFlags( true );
+ return mbVertical;
+}
+inline bool SwFrame::IsVertLR() const
+{
+ return mbVertLR;
+}
+inline bool SwFrame::IsVertLRBT() const
+{
+ return mbVertLRBT;
+}
+inline bool SwFrame::IsRightToLeft() const
+{
+ if( mbInvalidR2L )
+ const_cast<SwFrame*>(this)->SetDirFlags( false );
+ return mbRightToLeft;
+}
+
+inline void SwFrame::SetCompletePaint() const
+{
+ const_cast<SwFrame*>(this)->mbCompletePaint = true;
+}
+inline void SwFrame::ResetCompletePaint() const
+{
+ const_cast<SwFrame*>(this)->mbCompletePaint = false;
+}
+
+inline void SwFrame::SetRetouche() const
+{
+ const_cast<SwFrame*>(this)->mbRetouche = true;
+}
+inline void SwFrame::ResetRetouche() const
+{
+ const_cast<SwFrame*>(this)->mbRetouche = false;
+}
+
+inline SwLayoutFrame *SwFrame::GetNextLayoutLeaf()
+{
+ return const_cast<SwLayoutFrame*>(static_cast<const SwFrame*>(this)->GetNextLayoutLeaf());
+}
+inline SwLayoutFrame *SwFrame::GetPrevLayoutLeaf()
+{
+ return const_cast<SwLayoutFrame*>(static_cast<const SwFrame*>(this)->GetPrevLayoutLeaf());
+}
+inline const SwLayoutFrame *SwFrame::GetNextLayoutLeaf() const
+{
+ return ImplGetNextLayoutLeaf( true );
+}
+inline const SwLayoutFrame *SwFrame::GetPrevLayoutLeaf() const
+{
+ return ImplGetNextLayoutLeaf( false );
+}
+
+inline void SwFrame::InvalidateSize()
+{
+ if ( isFrameAreaSizeValid() )
+ {
+ ImplInvalidateSize();
+ }
+}
+inline void SwFrame::InvalidatePrt()
+{
+ if ( isFramePrintAreaValid() )
+ {
+ ImplInvalidatePrt();
+ }
+}
+inline void SwFrame::InvalidatePos()
+{
+ if ( isFrameAreaPositionValid() )
+ {
+ ImplInvalidatePos();
+ }
+}
+inline void SwFrame::InvalidateLineNum()
+{
+ if ( mbValidLineNum )
+ ImplInvalidateLineNum();
+}
+inline void SwFrame::InvalidateAll()
+{
+ if ( InvalidationAllowed( INVALID_ALL ) )
+ {
+ if ( isFrameAreaDefinitionValid() )
+ {
+ ImplInvalidatePos();
+ }
+
+ setFrameAreaSizeValid(false);
+ setFrameAreaPositionValid(false);
+ setFramePrintAreaValid(false);
+
+ // #i28701#
+ ActionOnInvalidation( INVALID_ALL );
+ }
+}
+inline void SwFrame::InvalidateNextPos( bool bNoFootnote )
+{
+ if ( mpNext && !mpNext->IsSctFrame() )
+ mpNext->InvalidatePos();
+ else
+ ImplInvalidateNextPos( bNoFootnote );
+}
+
+inline void SwFrame::OptCalc() const
+{
+ if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() )
+ {
+ const_cast<SwFrame*>(this)->OptPrepareMake();
+ }
+}
+inline const SwPageFrame *SwFrame::FindPageFrame() const
+{
+ return const_cast<SwFrame*>(this)->FindPageFrame();
+}
+inline const SwFrame *SwFrame::FindColFrame() const
+{
+ return const_cast<SwFrame*>(this)->FindColFrame();
+}
+inline const SwFrame *SwFrame::FindFooterOrHeader() const
+{
+ return const_cast<SwFrame*>(this)->FindFooterOrHeader();
+}
+inline SwTabFrame *SwFrame::FindTabFrame()
+{
+ return IsInTab() ? ImplFindTabFrame() : nullptr;
+}
+inline const SwFootnoteBossFrame *SwFrame::FindFootnoteBossFrame( bool bFootnote ) const
+{
+ return const_cast<SwFrame*>(this)->FindFootnoteBossFrame( bFootnote );
+}
+inline SwFootnoteFrame *SwFrame::FindFootnoteFrame()
+{
+ return IsInFootnote() ? ImplFindFootnoteFrame() : nullptr;
+}
+inline SwFlyFrame *SwFrame::FindFlyFrame()
+{
+ return IsInFly() ? ImplFindFlyFrame() : nullptr;
+}
+inline SwSectionFrame *SwFrame::FindSctFrame()
+{
+ return IsInSct() ? ImplFindSctFrame() : nullptr;
+}
+
+inline const SwBodyFrame *SwFrame::FindBodyFrame() const
+{
+ return IsInDocBody() ? ImplFindBodyFrame() : nullptr;
+}
+
+inline const SwTabFrame *SwFrame::FindTabFrame() const
+{
+ return IsInTab() ? const_cast<SwFrame*>(this)->ImplFindTabFrame() : nullptr;
+}
+inline const SwFootnoteFrame *SwFrame::FindFootnoteFrame() const
+{
+ return IsInFootnote() ? const_cast<SwFrame*>(this)->ImplFindFootnoteFrame() : nullptr;
+}
+inline const SwFlyFrame *SwFrame::FindFlyFrame() const
+{
+ return IsInFly() ? const_cast<SwFrame*>(this)->ImplFindFlyFrame() : nullptr;
+}
+inline const SwSectionFrame *SwFrame::FindSctFrame() const
+{
+ return IsInSct() ? const_cast<SwFrame*>(this)->ImplFindSctFrame() : nullptr;
+}
+inline SwFrame *SwFrame::FindNext()
+{
+ if ( mpNext )
+ return mpNext;
+ else
+ return FindNext_();
+}
+inline const SwFrame *SwFrame::FindNext() const
+{
+ if ( mpNext )
+ return mpNext;
+ else
+ return const_cast<SwFrame*>(this)->FindNext_();
+}
+inline SwFrame *SwFrame::FindPrev()
+{
+ if ( mpPrev && !mpPrev->IsSctFrame() )
+ return mpPrev;
+ else
+ return FindPrev_();
+}
+inline const SwFrame *SwFrame::FindPrev() const
+{
+ if ( mpPrev && !mpPrev->IsSctFrame() )
+ return mpPrev;
+ else
+ return const_cast<SwFrame*>(this)->FindPrev_();
+}
+
+inline bool SwFrame::IsLayoutFrame() const
+{
+ return bool(GetType() & FRM_LAYOUT);
+}
+inline bool SwFrame::IsRootFrame() const
+{
+ return mnFrameType == SwFrameType::Root;
+}
+inline bool SwFrame::IsPageFrame() const
+{
+ return mnFrameType == SwFrameType::Page;
+}
+inline bool SwFrame::IsColumnFrame() const
+{
+ return mnFrameType == SwFrameType::Column;
+}
+inline bool SwFrame::IsFootnoteBossFrame() const
+{
+ return bool(GetType() & FRM_FTNBOSS);
+}
+inline bool SwFrame::IsHeaderFrame() const
+{
+ return mnFrameType == SwFrameType::Header;
+}
+inline bool SwFrame::IsFooterFrame() const
+{
+ return mnFrameType == SwFrameType::Footer;
+}
+inline bool SwFrame::IsFootnoteContFrame() const
+{
+ return mnFrameType == SwFrameType::FtnCont;
+}
+inline bool SwFrame::IsFootnoteFrame() const
+{
+ return mnFrameType == SwFrameType::Ftn;
+}
+inline bool SwFrame::IsBodyFrame() const
+{
+ return mnFrameType == SwFrameType::Body;
+}
+inline bool SwFrame::IsFlyFrame() const
+{
+ return mnFrameType == SwFrameType::Fly;
+}
+inline bool SwFrame::IsSctFrame() const
+{
+ return mnFrameType == SwFrameType::Section;
+}
+inline bool SwFrame::IsTabFrame() const
+{
+ return mnFrameType == SwFrameType::Tab;
+}
+inline bool SwFrame::IsRowFrame() const
+{
+ return mnFrameType == SwFrameType::Row;
+}
+inline bool SwFrame::IsCellFrame() const
+{
+ return mnFrameType == SwFrameType::Cell;
+}
+inline bool SwFrame::IsContentFrame() const
+{
+ return bool(GetType() & FRM_CNTNT);
+}
+inline bool SwFrame::IsTextFrame() const
+{
+ return mnFrameType == SwFrameType::Txt;
+}
+inline bool SwFrame::IsNoTextFrame() const
+{
+ return mnFrameType == SwFrameType::NoTxt;
+}
+inline bool SwFrame::IsFlowFrame() const
+{
+ return bool(GetType() & (FRM_CNTNT|SwFrameType::Tab|SwFrameType::Section));
+}
+inline bool SwFrame::IsRetoucheFrame() const
+{
+ return bool(GetType() & (FRM_CNTNT|SwFrameType::Tab|SwFrameType::Section|SwFrameType::Ftn));
+}
+inline bool SwFrame::IsAccessibleFrame() const
+{
+ return bool(GetType() & FRM_ACCESSIBLE);
+}
+
+//use this to protect a SwFrame for a given scope from getting deleted
+class SwFrameDeleteGuard
+{
+private:
+ SwFrame *m_pForbidFrame;
+public:
+ //Flag pFrame for SwFrameDeleteGuard lifetime that we shouldn't delete
+ //it in e.g. SwSectionFrame::MergeNext etc because we will need it
+ //again after the SwFrameDeleteGuard dtor
+ explicit SwFrameDeleteGuard(SwFrame* pFrame)
+ : m_pForbidFrame(pFrame)
+ {
+ if (m_pForbidFrame)
+ m_pForbidFrame->ForbidDelete();
+ }
+
+ SwFrameDeleteGuard(const SwFrameDeleteGuard&) =delete;
+
+ ~SwFrameDeleteGuard()
+ {
+ if (m_pForbidFrame)
+ m_pForbidFrame->AllowDelete();
+ }
+
+ SwFrameDeleteGuard& operator=(const SwFrameDeleteGuard&) =delete;
+};
+
+typedef tools::Long (SwFrame::*SwFrameGet)() const;
+typedef bool (SwFrame::*SwFrameMax)( tools::Long );
+typedef void (SwFrame::*SwFrameMakePos)( const SwFrame*, const SwFrame*, bool );
+typedef tools::Long (*SwOperator)( tools::Long, tools::Long );
+typedef void (SwFrame::*SwFrameSet)( tools::Long, tools::Long );
+
+struct SwRectFnCollection
+{
+ SwRectGet fnGetTop;
+ SwRectGet fnGetBottom;
+ SwRectGet fnGetLeft;
+ SwRectGet fnGetRight;
+ SwRectGet fnGetWidth;
+ SwRectGet fnGetHeight;
+ SwRectPoint fnGetPos;
+ SwRectSize fnGetSize;
+
+ SwRectSet fnSetTop;
+ SwRectSet fnSetBottom;
+ SwRectSet fnSetLeft;
+ SwRectSet fnSetRight;
+ SwRectSet fnSetWidth;
+ SwRectSet fnSetHeight;
+
+ SwRectSet fnSubTop;
+ SwRectSet fnAddBottom;
+ SwRectSet fnSubLeft;
+ SwRectSet fnAddRight;
+ SwRectSet fnAddWidth;
+ SwRectSet fnAddHeight;
+
+ SwRectSet fnSetPosX;
+ SwRectSet fnSetPosY;
+
+ SwFrameGet fnGetTopMargin;
+ SwFrameGet fnGetBottomMargin;
+ SwFrameGet fnGetLeftMargin;
+ SwFrameGet fnGetRightMargin;
+ SwFrameSet fnSetXMargins;
+ SwFrameSet fnSetYMargins;
+ SwFrameGet fnGetPrtTop;
+ SwFrameGet fnGetPrtBottom;
+ SwFrameGet fnGetPrtLeft;
+ SwFrameGet fnGetPrtRight;
+ SwRectDist fnTopDist;
+ SwRectDist fnBottomDist;
+ SwRectDist fnLeftDist;
+ SwRectDist fnRightDist;
+ SwFrameMax fnSetLimit;
+ SwRectMax fnOverStep;
+
+ SwRectSetPos fnSetPos;
+ SwFrameMakePos fnMakePos;
+ SwOperator fnXDiff;
+ SwOperator fnYDiff;
+ SwOperator fnXInc;
+ SwOperator fnYInc;
+
+ SwRectSetTwice fnSetLeftAndWidth;
+ SwRectSetTwice fnSetTopAndHeight;
+};
+
+typedef SwRectFnCollection* SwRectFn;
+
+// This class allows to use proper methods regardless of orientation (LTR/RTL, horizontal or vertical)
+extern SwRectFn fnRectHori, fnRectVert, fnRectVertL2R, fnRectVertL2RB2T;
+class SwRectFnSet {
+public:
+ explicit SwRectFnSet(const SwFrame *pFrame)
+ : m_bVert(pFrame->IsVertical())
+ , m_bVertL2R(pFrame->IsVertLR())
+ , m_bVertL2RB2T(pFrame->IsVertLRBT())
+ {
+ m_fnRect = m_bVert ? (m_bVertL2R ? (m_bVertL2RB2T ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert) : fnRectHori;
+ }
+
+ void Refresh(const SwFrame *pFrame)
+ {
+ m_bVert = pFrame->IsVertical();
+ m_bVertL2R = pFrame->IsVertLR();
+ m_bVertL2RB2T = pFrame->IsVertLRBT();
+ m_fnRect = m_bVert ? (m_bVertL2R ? (m_bVertL2RB2T ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert) : fnRectHori;
+ }
+
+ bool IsVert() const { return m_bVert; }
+ bool IsVertL2R() const { return m_bVertL2R; }
+ SwRectFn FnRect() const { return m_fnRect; }
+
+ bool PosDiff(const SwRect &rRect1, const SwRect &rRect2) const
+ {
+ return ((rRect1.*m_fnRect->fnGetTop)() != (rRect2.*m_fnRect->fnGetTop)()
+ || (rRect1.*m_fnRect->fnGetLeft)() != (rRect2.*m_fnRect->fnGetLeft)());
+ }
+
+ tools::Long GetTop (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetTop) (); }
+ tools::Long GetBottom(const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetBottom)(); }
+ tools::Long GetLeft (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetLeft) (); }
+ tools::Long GetRight (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetRight) (); }
+ tools::Long GetWidth (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetWidth) (); }
+ tools::Long GetHeight(const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetHeight)(); }
+ Point GetPos (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetPos) (); }
+ Size GetSize (const SwRect& rRect) const { return (rRect.*m_fnRect->fnGetSize) (); }
+
+ void SetTop (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetTop) (nNew); }
+ void SetBottom(SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetBottom)(nNew); }
+ void SetLeft (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetLeft) (nNew); }
+ void SetRight (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetRight) (nNew); }
+ void SetWidth (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetWidth) (nNew); }
+ void SetHeight(SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetHeight)(nNew); }
+
+ void SubTop (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSubTop) (nNew); }
+ void AddBottom(SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnAddBottom)(nNew); }
+ void SubLeft (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSubLeft) (nNew); }
+ void AddRight (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnAddRight) (nNew); }
+ void AddWidth (SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnAddWidth) (nNew); }
+ void AddHeight(SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnAddHeight)(nNew); }
+
+ void SetPosX(SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetPosX)(nNew); }
+ void SetPosY(SwRect& rRect, tools::Long nNew) const { (rRect.*m_fnRect->fnSetPosY)(nNew); }
+
+ tools::Long GetTopMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetTopMargin) (); }
+ tools::Long GetBottomMargin(const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetBottomMargin)(); }
+ tools::Long GetLeftMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetLeftMargin) (); }
+ tools::Long GetRightMargin (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetRightMargin) (); }
+ void SetXMargins(SwFrame& rFrame, tools::Long nLeft, tools::Long nRight) const { (rFrame.*m_fnRect->fnSetXMargins)(nLeft, nRight); }
+ void SetYMargins(SwFrame& rFrame, tools::Long nTop, tools::Long nBottom) const { (rFrame.*m_fnRect->fnSetYMargins)(nTop, nBottom); }
+ tools::Long GetPrtTop (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtTop) (); }
+ tools::Long GetPrtBottom (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtBottom) (); }
+ tools::Long GetPrtLeft (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtLeft) (); }
+ tools::Long GetPrtRight (const SwFrame& rFrame) const { return (rFrame.*m_fnRect->fnGetPrtRight) (); }
+ tools::Long TopDist (const SwRect& rRect, tools::Long nPos) const { return (rRect.*m_fnRect->fnTopDist) (nPos); }
+ tools::Long BottomDist(const SwRect& rRect, tools::Long nPos) const { return (rRect.*m_fnRect->fnBottomDist) (nPos); }
+ tools::Long LeftDist (const SwRect& rRect, tools::Long nPos) const { return (rRect.*m_fnRect->fnLeftDist) (nPos); }
+ tools::Long RightDist (const SwRect& rRect, tools::Long nPos) const { return (rRect.*m_fnRect->fnRightDist) (nPos); }
+ void SetLimit (SwFrame& rFrame, tools::Long nNew) const { (rFrame.*m_fnRect->fnSetLimit) (nNew); }
+ bool OverStep (const SwRect& rRect, tools::Long nPos) const { return (rRect.*m_fnRect->fnOverStep) (nPos); }
+
+ void SetPos(SwRect& rRect, const Point& rNew) const { (rRect.*m_fnRect->fnSetPos)(rNew); }
+ void MakePos(SwFrame& rFrame, const SwFrame* pUp, const SwFrame* pPrv, bool bNotify) const { (rFrame.*m_fnRect->fnMakePos)(pUp, pPrv, bNotify); }
+ tools::Long XDiff(tools::Long n1, tools::Long n2) const { return (m_fnRect->fnXDiff) (n1, n2); }
+ tools::Long YDiff(tools::Long n1, tools::Long n2) const { return (m_fnRect->fnYDiff) (n1, n2); }
+ tools::Long XInc (tools::Long n1, tools::Long n2) const { return (m_fnRect->fnXInc) (n1, n2); }
+ tools::Long YInc (tools::Long n1, tools::Long n2) const { return (m_fnRect->fnYInc) (n1, n2); }
+
+ void SetLeftAndWidth(SwRect& rRect, tools::Long nLeft, tools::Long nWidth) const { (rRect.*m_fnRect->fnSetLeftAndWidth)(nLeft, nWidth); }
+ void SetTopAndHeight(SwRect& rRect, tools::Long nTop, tools::Long nHeight) const { (rRect.*m_fnRect->fnSetTopAndHeight)(nTop, nHeight); }
+
+private:
+ bool m_bVert;
+ bool m_bVertL2R;
+ bool m_bVertL2RB2T;
+ SwRectFn m_fnRect;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/frminf.hxx b/sw/source/core/inc/frminf.hxx
new file mode 100644
index 000000000..1e1d433c4
--- /dev/null
+++ b/sw/source/core/inc/frminf.hxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FRMINF_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FRMINF_HXX
+
+#include <swtypes.hxx>
+
+#include "TextFrameIndex.hxx"
+
+#include <vector>
+
+class SwTextFrame;
+class SwTextCursor;
+
+class SwTextFrameInfo
+{
+ const SwTextFrame *m_pFrame;
+
+ // Where does the text (w/o whitespaces) start (document is global!)?
+ static SwTwips GetLineStart( const SwTextCursor &rLine );
+
+public:
+ SwTextFrameInfo( const SwTextFrame *pTextFrame ) : m_pFrame(pTextFrame) { }
+
+ // Does the paragraph fit into a single line?
+ bool IsOneLine() const;
+
+ // Is the line filled to X%?
+ bool IsFilled( const sal_uInt8 nPercent ) const;
+
+ // Where does the text (w/o whitespaces) start (rel. in frame)?
+ SwTwips GetLineStart() const;
+
+ // return center position of the next character
+ SwTwips GetCharPos(TextFrameIndex nChar, bool bCenter = true) const;
+
+ // collect all whitespaces at the beginning and end of a line in Pam
+ void GetSpaces(std::vector<std::pair<TextFrameIndex, TextFrameIndex>> &,
+ bool bWithLineBreak) const;
+
+ // Is a bullet point/symbol/etc. at the first text position?
+ bool IsBullet(TextFrameIndex nTextPos) const;
+
+ // determine indentation for first line
+ SwTwips GetFirstIndent() const;
+
+ const SwTextFrame* GetFrame() const { return m_pFrame; }
+ SwTextFrameInfo& SetFrame( const SwTextFrame* pNew )
+ { m_pFrame = pNew; return *this; }
+
+ // Is it a comparison? Returns position in frame.
+ sal_Int32 GetBigIndent( TextFrameIndex & rFndPos,
+ const SwTextFrame *pNextFrame ) const;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/frmtool.hxx b/sw/source/core/inc/frmtool.hxx
new file mode 100644
index 000000000..c7177379a
--- /dev/null
+++ b/sw/source/core/inc/frmtool.hxx
@@ -0,0 +1,624 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FRMTOOL_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FRMTOOL_HXX
+
+#include <swtypes.hxx>
+#include <BorderCacheOwner.hxx>
+#include "frame.hxx"
+#include "txtfrm.hxx"
+#include "swcache.hxx"
+#include <swatrset.hxx>
+
+class SwLayoutFrame;
+class SwFont;
+class SwTextFrame;
+class SwFormatAnchor;
+class SwViewShell;
+class SwPageFrame;
+class SwFlyFrame;
+class SwContentFrame;
+class SwRootFrame;
+class SwDoc;
+class SdrObject;
+class SvxBrushItem;
+class SdrMarkList;
+class SwNodeIndex;
+class GraphicObject;
+class GraphicAttr;
+class SwPageDesc;
+class SwFrameFormats;
+class SwRegionRects;
+class SwTextNode;
+namespace sw { struct Extent; }
+namespace basegfx::utils { class B2DClipState; }
+
+#define FAR_AWAY (SAL_MAX_INT32 - 20000) // initial position of a Fly
+constexpr tools::Long BROWSE_HEIGHT = 56700 * 10; // 10 Meters
+#define GRFNUM_NO 0
+#define GRFNUM_YES 1
+#define GRFNUM_REPLACE 2
+
+void AppendObjs( const SwFrameFormats *pTable, SwNodeOffset nIndex,
+ SwFrame *pFrame, SwPageFrame *pPage, SwDoc* doc );
+
+void AppendObjsOfNode(SwFrameFormats const* pTable, SwNodeOffset nIndex,
+ SwFrame * pFrame, SwPageFrame * pPage, SwDoc * pDoc,
+ std::vector<sw::Extent>::const_iterator const* pIter,
+ std::vector<sw::Extent>::const_iterator const* pEnd,
+ SwTextNode const* pFirstNode, SwTextNode const* pLastNode);
+
+void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
+ std::vector<sw::Extent>::const_iterator const* pIter,
+ std::vector<sw::Extent>::const_iterator const* pEnd,
+ SwTextNode const* pFirstNode, SwTextNode const* pLastNode);
+
+bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor);
+
+void AppendAllObjs(const SwFrameFormats* pTable, const SwFrame* pSib);
+
+// draw background with brush or graphics
+// The 6th parameter indicates that the method should consider background
+// transparency, saved in the color of the brush item.
+void DrawGraphic(
+ const SvxBrushItem *,
+ vcl::RenderContext &,
+ const SwRect &rOrg,
+ const SwRect &rOut,
+ const sal_uInt8 nGrfNum = GRFNUM_NO,
+ const bool bConsiderBackgroundTransparency = false );
+bool DrawFillAttributes(
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const SwRect& rOriginalLayoutRect,
+ const SwRegionRects& rPaintRegion,
+ const basegfx::utils::B2DClipState& rClipState,
+ vcl::RenderContext& rOut);
+
+// RotGrfFlyFrame: Adapted to rotation
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ GraphicObject const& rGraphicObj,
+ GraphicAttr const& rGraphicAttr,
+ const basegfx::B2DHomMatrix& rGraphicTransform,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDescription);
+
+// MM02 new VOC and primitive-based version
+void paintGraphicUsingPrimitivesHelper(
+ vcl::RenderContext & rOutputDevice,
+ drawinglayer::primitive2d::Primitive2DContainer& rContent,
+ const basegfx::B2DHomMatrix& rGraphicTransform,
+ const OUString& rName,
+ const OUString& rTitle,
+ const OUString& rDescription);
+
+// method to align rectangle.
+// Created declaration here to avoid <extern> declarations
+void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext );
+
+// method to align graphic rectangle
+// Created declaration here to avoid <extern> declarations
+void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut );
+
+/**
+ * Paint border around a run of characters using frame painting code.
+ *
+ * @param[in] rFont font object of actual text, which specify the border
+ * @param[in] rPaintArea rectangle area in which line portion takes place
+ * @param[in] bVerticalLayout corresponding text frame verticality
+ * @param[in] bVerticalLayoutLRBT corresponding text frame verticality (LRBT subset)
+ * @param[in] bJoinWithPrev leave border with which actual border joins to the previous portion
+ * @param[in] bJoinWithNext leave border with which actual border joins to the next portion
+**/
+void PaintCharacterBorder(const SwFont& rFont, const SwRect& rPaintArea, const bool bVerticalLayout,
+ const bool bVerticalLayoutLRBT, const bool bJoinWithPrev,
+ const bool bJoinWithNext);
+
+// get Fly, if no List is given use the current shell
+// Implementation in feshview.cxx
+SwFlyFrame *GetFlyFromMarked( const SdrMarkList *pLst, SwViewShell *pSh );
+
+SwFrame *SaveContent( SwLayoutFrame *pLay, SwFrame *pStart = nullptr );
+void RestoreContent( SwFrame *pSav, SwLayoutFrame *pParent, SwFrame *pSibling );
+
+// Get ContentNodes, create ContentFrames, and add them to LayFrame.
+void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc, SwNodeOffset nIndex,
+ bool bPages = false, SwNodeOffset nEndIndex = SwNodeOffset(0),
+ SwFrame *pPrv = nullptr, sw::FrameMode eMode = sw::FrameMode::New);
+
+// Creation of frames for a specific section (uses InsertCnt_)
+void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx,
+ const SwNodeIndex &rEndIdx );
+
+extern bool bObjsDirect;
+
+// for FlyCnts, see SwFlyAtContentFrame::MakeAll()
+extern bool bSetCompletePaintOnInvalidate;
+
+// for table settings via keyboard
+SwTwips CalcRowRstHeight( SwLayoutFrame *pRow );
+tools::Long CalcHeightWithFlys( const SwFrame *pFrame );
+
+namespace sw {
+ bool IsRightPageByNumber(SwRootFrame const& rLayout, sal_uInt16 nPageNum);
+ class FlyCreationSuppressor
+ {
+ const bool m_wasAlreadySuppressed;
+ public:
+ FlyCreationSuppressor(bool isAlreadySuppressedAllowed = true);
+ ~FlyCreationSuppressor();
+ };
+} // namespace sw
+
+SwPageFrame *InsertNewPage( SwPageDesc &rDesc, SwFrame *pUpper,
+ bool isRightPage, bool bFirst, bool bInsertEmpty, bool bFootnote,
+ SwFrame *pSibling, bool bVeryFirstPage = false );
+
+// connect Flys with page
+void RegistFlys( SwPageFrame*, const SwLayoutFrame* );
+
+// notification of Fly's background if needed
+void Notify( SwFlyFrame *pFly, SwPageFrame *pOld, const SwRect &rOld,
+ const SwRect* pOldRect = nullptr );
+
+void Notify_Background( const SdrObject* pObj,
+ SwPageFrame* pPage,
+ const SwRect& rRect,
+ const PrepareHint eHint,
+ const bool bInva );
+
+const SwFrame* GetVirtualUpper( const SwFrame* pFrame, const Point& rPos );
+
+bool Is_Lower_Of( const SwFrame *pCurrFrame, const SdrObject* pObj );
+
+// FIXME: EasyHack (refactoring): rename method and parameter name in all files
+const SwFrame *FindContext( const SwFrame *pFrame, SwFrameType nAdditionalContextTyp );
+
+bool IsFrameInSameContext( const SwFrame *pInnerFrame, const SwFrame *pFrame );
+
+const SwFrame * FindPage( const SwRect &rRect, const SwFrame *pPage );
+
+/** @see SwContentNode::getLayoutFrame()
+ @param pPos
+ Document model position; for a text frame, the returned frame will be
+ one containing this position.
+ @param pViewPosAndCalcFrame
+ First is a point in the document view; the returned frame will be the one
+ with the minimal distance to this point. To get the first frame in the
+ document, pass in a default-initialized Point with coordinates 0,0.
+ Second indicates whether the frames should be formatted before retrieving
+ their position for the test; this cannot be done by every caller so use
+ with care!
+ */
+SwFrame* GetFrameOfModify( const SwRootFrame* pLayout,
+ sw::BroadcastingModify const&,
+ SwFrameType const nFrameType,
+ const SwPosition *pPos = nullptr,
+ std::pair<Point, bool> const* pViewPosAndCalcFrame = nullptr);
+
+// Should extra data (redline stroke, line numbers) be painted?
+bool IsExtraData( const SwDoc *pDoc );
+
+// #i11760# - method declaration <CalcContent(..)>
+void CalcContent( SwLayoutFrame *pLay, bool bNoColl = false );
+
+// Notify classes memorize the current sizes in their constructor and do
+// the necessary notifications in their destructor if needed
+class SwFrameNotify
+{
+private:
+ void ImplDestroy();
+
+protected:
+ SwFrame *mpFrame;
+ const SwRect maFrame;
+ const SwRect maPrt;
+ SwTwips mnFlyAnchorOfst;
+ SwTwips mnFlyAnchorOfstNoWrap;
+ bool mbHadFollow;
+ bool mbInvaKeep;
+ bool mbValidSize;
+
+public:
+ SwFrameNotify( SwFrame *pFrame );
+ ~SwFrameNotify();
+
+ const SwRect &getFrameArea() const { return maFrame; }
+ void SetInvaKeep() { mbInvaKeep = true; }
+};
+
+class SwLayNotify : public SwFrameNotify
+{
+ bool m_bLowersComplete;
+
+ void ImplDestroy();
+
+public:
+ SwLayNotify( SwLayoutFrame *pLayFrame );
+ ~SwLayNotify();
+
+ void SetLowersComplete( bool b ) { m_bLowersComplete = b; }
+ bool IsLowersComplete() const { return m_bLowersComplete; }
+};
+
+class SwFlyNotify : public SwLayNotify
+{
+ SwPageFrame *m_pOldPage;
+ const SwRect m_aFrameAndSpace;
+
+ void ImplDestroy();
+
+public:
+ SwFlyNotify( SwFlyFrame *pFlyFrame );
+ ~SwFlyNotify();
+};
+
+class SwContentNotify : public SwFrameNotify
+{
+private:
+ // #i11859#
+ bool mbChkHeightOfLastLine;
+ SwTwips mnHeightOfLastLine;
+
+ // #i25029#
+ bool mbInvalidatePrevPrtArea;
+ bool mbBordersJoinedWithPrev;
+
+ void ImplDestroy();
+
+public:
+ SwContentNotify( SwContentFrame *pContentFrame );
+ ~SwContentNotify();
+
+ // #i25029#
+ void SetInvalidatePrevPrtArea()
+ {
+ mbInvalidatePrevPrtArea = true;
+ }
+
+ void SetBordersJoinedWithPrev()
+ {
+ mbBordersJoinedWithPrev = true;
+ }
+};
+
+// SwBorderAttrs encapsulates the calculation for margin attributes including
+// border. The whole class is cached.
+
+// WARNING! If more attributes should be cached also adjust the method
+// Modify::Modify!
+class SwBorderAttrs final : public SwCacheObj
+{
+ const SwAttrSet &m_rAttrSet;
+ const SvxULSpaceItem &m_rUL;
+ // #i96772#
+ std::shared_ptr<SvxLRSpaceItem> m_rLR;
+ const SvxBoxItem &m_rBox;
+ const SvxShadowItem &m_rShadow;
+ const Size m_aFrameSize;
+
+ // the following bool values set the cached values to INVALID - until they
+ // are calculated for the first time
+ bool m_bTopLine : 1;
+ bool m_bBottomLine : 1;
+ bool m_bLeftLine : 1;
+ bool m_bRightLine : 1;
+ bool m_bTop : 1;
+ bool m_bBottom : 1;
+ bool m_bLine : 1;
+ bool m_bLineSpacing : 1;
+
+ bool m_bIsLine : 1; // border on at least one side?
+
+ bool m_bCacheGetLine : 1; // cache GetTopLine(), GetBottomLine()?
+ bool m_bCachedGetTopLine : 1; // is GetTopLine() cached?
+ bool m_bCachedGetBottomLine : 1; // is GetBottomLine() cached?
+ // Booleans indicate that <m_bJoinedWithPrev> and <m_bJoinedWithNext> are
+ // cached and valid.
+ // Caching depends on value of <m_bCacheGetLine>.
+ mutable bool m_bCachedJoinedWithPrev : 1;
+ mutable bool m_bCachedJoinedWithNext : 1;
+ // Booleans indicate that borders are joined with previous/next frame.
+ bool m_bJoinedWithPrev :1;
+ bool m_bJoinedWithNext :1;
+
+ // The cached values (un-defined until calculated for the first time)
+ sal_uInt16 m_nTopLine,
+ m_nBottomLine,
+ m_nLeftLine,
+ m_nRightLine,
+ m_nTop,
+ m_nBottom,
+ m_nGetTopLine,
+ m_nGetBottomLine,
+ m_nLineSpacing;
+
+ // only calculate lines and shadow
+ void CalcTopLine_();
+ void CalcBottomLine_();
+ void CalcLeftLine_();
+ void CalcRightLine_();
+
+ // lines + shadow + margin
+ void CalcTop_();
+ void CalcBottom_();
+
+ void IsLine_();
+
+ // #i25029# - If <_pPrevFrame> is set, its value is taken for testing, if
+ // borders/shadow have to be joined with previous frame.
+ void GetTopLine_ ( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame );
+ void GetBottomLine_( const SwFrame& _rFrame );
+
+ // calculate cached values <m_bJoinedWithPrev> and <m_bJoinedWithNext>
+ // #i25029# - If <_pPrevFrame> is set, its value is taken for testing, if
+ // borders/shadow have to be joined with previous frame.
+ void CalcJoinedWithPrev( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame );
+ void CalcJoinedWithNext( const SwFrame& _rFrame );
+
+ // internal helper method for CalcJoinedWithPrev and CalcJoinedWithNext
+ bool JoinWithCmp( const SwFrame& _rCallerFrame,
+ const SwFrame& _rCmpFrame ) const;
+
+ // Are the left and right line and the LRSpace equal?
+ bool CmpLeftRight( const SwBorderAttrs &rCmpAttrs,
+ const SwFrame *pCaller,
+ const SwFrame *pCmp ) const;
+
+ // tdf#125300 line spacing before cell border
+ void CalcLineSpacing_();
+
+public:
+ SwBorderAttrs( const sw::BorderCacheOwner* pOwner, const SwFrame *pConstructor );
+ virtual ~SwBorderAttrs() override;
+
+ const SwAttrSet &GetAttrSet() const { return m_rAttrSet; }
+ const SvxULSpaceItem &GetULSpace() const { return m_rUL; }
+ const SvxBoxItem &GetBox() const { return m_rBox; }
+ const SvxShadowItem &GetShadow() const { return m_rShadow; }
+
+ inline sal_uInt16 CalcTopLine() const;
+ inline sal_uInt16 CalcBottomLine() const;
+ inline sal_uInt16 CalcLeftLine() const;
+ inline sal_uInt16 CalcRightLine() const;
+ inline sal_uInt16 CalcTop() const;
+ inline sal_uInt16 CalcBottom() const;
+ inline sal_uInt16 CalcLineSpacing() const;
+ tools::Long CalcLeft( const SwFrame *pCaller ) const;
+ tools::Long CalcRight( const SwFrame *pCaller ) const;
+
+ inline bool IsLine() const;
+
+ const Size &GetSize() const { return m_aFrameSize; }
+
+ // Should upper (or lower) border be evaluated for this frame?
+ // #i25029# - If <_pPrevFrame> is set, its value is taken for testing, if
+ // borders/shadow have to be joined with previous frame.
+ inline sal_uInt16 GetTopLine ( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame = nullptr ) const;
+ inline sal_uInt16 GetBottomLine( const SwFrame& _rFrame ) const;
+ inline void SetGetCacheLine( bool bNew ) const;
+
+ // Accessors for cached values <m_bJoinedWithPrev> and <m_bJoinedWithNext>
+ // #i25029# - If <_pPrevFrame> is set, its value is taken for testing, if
+ // borders/shadow have to be joined with previous frame.
+ bool JoinedWithPrev( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame = nullptr ) const;
+ bool JoinedWithNext( const SwFrame& _rFrame ) const;
+};
+
+class SwBorderAttrAccess final : public SwCacheAccess
+{
+ const SwFrame *m_pConstructor; //opt: for passing on to SwBorderAttrs
+
+ virtual SwCacheObj *NewObj() override;
+
+public:
+ SwBorderAttrAccess( SwCache &rCache, const SwFrame *pOwner );
+
+ SwBorderAttrs *Get();
+};
+
+// Iterator for draw objects of a page. The objects will be iterated sorted by
+// their Z-order. Iterating is not cheap since for each operation the _whole_
+// SortArray needs to be traversed.
+class SwOrderIter
+{
+ const SwPageFrame *m_pPage;
+ const SdrObject *m_pCurrent;
+
+public:
+ SwOrderIter( const SwPageFrame *pPage );
+
+ void Current( const SdrObject *pNew ) { m_pCurrent = pNew; }
+ const SdrObject *operator()() const { return m_pCurrent; }
+ void Top();
+ const SdrObject *Bottom();
+ const SdrObject *Next();
+ void Prev();
+};
+
+class StackHack
+{
+ static sal_uInt8 s_nCnt;
+ static bool s_bLocked;
+
+public:
+ StackHack()
+ {
+ if ( ++StackHack::s_nCnt > 50 )
+ StackHack::s_bLocked = true;
+ }
+ ~StackHack()
+ {
+ if ( --StackHack::s_nCnt < 5 )
+ StackHack::s_bLocked = false;
+ }
+
+ static bool IsLocked() { return StackHack::s_bLocked; }
+ static sal_uInt8 Count() { return StackHack::s_nCnt; }
+};
+
+// Should upper (or lower) border be evaluated for this frame?
+// #i25029# - If <_pPrevFrame> is set, its value is taken for testing, if
+// borders/shadow have to be joined with previous frame.
+inline sal_uInt16 SwBorderAttrs::GetTopLine ( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame ) const
+{
+ if ( !m_bCachedGetTopLine || _pPrevFrame )
+ {
+ const_cast<SwBorderAttrs*>(this)->GetTopLine_( _rFrame, _pPrevFrame );
+ }
+ return m_nGetTopLine;
+}
+inline sal_uInt16 SwBorderAttrs::GetBottomLine( const SwFrame& _rFrame ) const
+{
+ if ( !m_bCachedGetBottomLine )
+ const_cast<SwBorderAttrs*>(this)->GetBottomLine_( _rFrame );
+ return m_nGetBottomLine;
+}
+inline void SwBorderAttrs::SetGetCacheLine( bool bNew ) const
+{
+ const_cast<SwBorderAttrs*>(this)->m_bCacheGetLine = bNew;
+ const_cast<SwBorderAttrs*>(this)->m_bCachedGetBottomLine =
+ const_cast<SwBorderAttrs*>(this)->m_bCachedGetTopLine = false;
+ // invalidate cache for values <m_bJoinedWithPrev> and <m_bJoinedWithNext>
+ m_bCachedJoinedWithPrev = false;
+ m_bCachedJoinedWithNext = false;
+}
+
+inline sal_uInt16 SwBorderAttrs::CalcTopLine() const
+{
+ if ( m_bTopLine )
+ const_cast<SwBorderAttrs*>(this)->CalcTopLine_();
+ return m_nTopLine;
+}
+inline sal_uInt16 SwBorderAttrs::CalcBottomLine() const
+{
+ if ( m_bBottomLine )
+ const_cast<SwBorderAttrs*>(this)->CalcBottomLine_();
+ return m_nBottomLine;
+}
+inline sal_uInt16 SwBorderAttrs::CalcLeftLine() const
+{
+ if ( m_bLeftLine )
+ const_cast<SwBorderAttrs*>(this)->CalcLeftLine_();
+ return m_nLeftLine;
+}
+inline sal_uInt16 SwBorderAttrs::CalcRightLine() const
+{
+ if ( m_bRightLine )
+ const_cast<SwBorderAttrs*>(this)->CalcRightLine_();
+ return m_nRightLine;
+}
+inline sal_uInt16 SwBorderAttrs::CalcTop() const
+{
+ if ( m_bTop )
+ const_cast<SwBorderAttrs*>(this)->CalcTop_();
+ return m_nTop;
+}
+inline sal_uInt16 SwBorderAttrs::CalcBottom() const
+{
+ if ( m_bBottom )
+ const_cast<SwBorderAttrs*>(this)->CalcBottom_();
+ return m_nBottom;
+}
+inline sal_uInt16 SwBorderAttrs::CalcLineSpacing() const
+{
+ if ( m_bLineSpacing )
+ const_cast<SwBorderAttrs*>(this)->CalcLineSpacing_();
+ return m_nLineSpacing;
+}
+inline bool SwBorderAttrs::IsLine() const
+{
+ if ( m_bLine )
+ const_cast<SwBorderAttrs*>(this)->IsLine_();
+ return m_bIsLine;
+}
+
+/** method to determine the spacing values of a frame
+
+ #i28701#
+ Values only provided for flow frames (table, section or text frames)
+ Note: line spacing value is only determined for text frames
+ #i102458#
+ Add output parameter <obIsLineSpacingProportional>
+
+ @param rFrame
+ input parameter - frame, for which the spacing values are determined.
+
+ @param onPrevLowerSpacing
+ output parameter - lower spacing of the frame in SwTwips
+
+ @param onPrevLineSpacing
+ output parameter - line spacing of the frame in SwTwips
+
+ @param obIsLineSpacingProportional
+
+ @param bIdenticalStyles true if the styles of the actual and the next paragraphs (text-frames) are the same
+*/
+void GetSpacingValuesOfFrame( const SwFrame& rFrame,
+ SwTwips& onLowerSpacing,
+ SwTwips& onLineSpacing,
+ bool& obIsLineSpacingProportional,
+ bool bIdenticalStyles );
+
+/** method to get the content of the table cell
+
+ Content from any nested tables will be omitted.
+ Note: line spacing value is only determined for text frames
+
+ @param rCell_
+ input parameter - the cell which should be searched for content.
+
+ return
+ pointer to the found content frame or 0
+*/
+
+const SwContentFrame* GetCellContent( const SwLayoutFrame& rCell_ );
+
+/** helper class to check if a frame has been deleted during an operation
+ * WARNING! This should only be used as a last and desperate means to make the
+ * code robust.
+ */
+
+class SwDeletionChecker
+{
+private:
+ const SwFrame* mpFrame;
+ const sw::BroadcastingModify* mpRegIn;
+
+public:
+ SwDeletionChecker(const SwFrame* pFrame);
+
+ /**
+ * return
+ * true if mpFrame != 0 and mpFrame is not client of pRegIn
+ * false otherwise
+ */
+ bool HasBeenDeleted() const;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/ftnboss.hxx b/sw/source/core/inc/ftnboss.hxx
new file mode 100644
index 000000000..e60ff97de
--- /dev/null
+++ b/sw/source/core/inc/ftnboss.hxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FTNBOSS_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FTNBOSS_HXX
+
+#include "layfrm.hxx"
+
+class SwFootnoteBossFrame;
+class SwFootnoteContFrame;
+class SwFootnoteFrame;
+class SwTextFootnote;
+
+// Set max. footnote area.
+// Restoration of the old value in DTor. Implementation in ftnfrm.cxx
+class SwSaveFootnoteHeight
+{
+ SwFrameDeleteGuard aGuard;
+ SwFootnoteBossFrame *pBoss;
+ const SwTwips nOldHeight;
+ SwTwips nNewHeight;
+public:
+ SwSaveFootnoteHeight( SwFootnoteBossFrame *pBs, const SwTwips nDeadLine );
+ ~SwSaveFootnoteHeight();
+};
+
+enum class SwNeighbourAdjust {
+ OnlyAdjust, GrowShrink, GrowAdjust, AdjustGrow
+};
+
+typedef std::vector<SwFootnoteFrame*> SwFootnoteFrames;
+
+class SAL_DLLPUBLIC_RTTI SwFootnoteBossFrame: public SwLayoutFrame
+{
+ // for private footnote operations
+ friend class SwFrame;
+ friend class SwSaveFootnoteHeight;
+ friend class SwPageFrame; // for setting of MaxFootnoteHeight
+
+ // max. height of the footnote container on this page
+ SwTwips m_nMaxFootnoteHeight;
+
+ SwFootnoteContFrame *MakeFootnoteCont();
+ SwFootnoteFrame *FindFirstFootnote();
+ SwNeighbourAdjust NeighbourhoodAdjustment_() const;
+
+ static void CollectFootnotes_(const SwContentFrame*, SwFootnoteFrame*,
+ SwFootnoteFrames&, const SwFootnoteBossFrame*);
+
+protected:
+ void InsertFootnote( SwFootnoteFrame * );
+ static void ResetFootnote( const SwFootnoteFrame *pAssumed );
+
+public:
+ SwFootnoteBossFrame( SwFrameFormat* pFormat, SwFrame* pSib )
+ : SwLayoutFrame( pFormat, pSib )
+ , m_nMaxFootnoteHeight(0)
+ {}
+
+ SwLayoutFrame *FindBodyCont();
+ inline const SwLayoutFrame *FindBodyCont() const;
+ void SetMaxFootnoteHeight( const SwTwips nNewMax ) { m_nMaxFootnoteHeight = nNewMax; }
+
+ // footnote interface
+ void AppendFootnote( SwContentFrame *, SwTextFootnote * );
+ bool RemoveFootnote(const SwContentFrame *, const SwTextFootnote *, bool bPrep = true);
+ static SwFootnoteFrame *FindFootnote( const SwContentFrame *, const SwTextFootnote * );
+ SwFootnoteContFrame *FindFootnoteCont();
+ inline const SwFootnoteContFrame *FindFootnoteCont() const;
+ const SwFootnoteFrame *FindFirstFootnote( SwContentFrame const * ) const;
+ SwFootnoteContFrame *FindNearestFootnoteCont( bool bDontLeave = false );
+
+ static void ChangeFootnoteRef( const SwContentFrame *pOld, const SwTextFootnote *,
+ SwContentFrame *pNew );
+ void RearrangeFootnotes( const SwTwips nDeadLine, const bool bLock,
+ const SwTextFootnote *pAttr = nullptr );
+
+ // Set DeadLine (in document coordinates) so that the text formatter can
+ // temporarily limit footnote height.
+ void SetFootnoteDeadLine( const SwTwips nDeadLine );
+ SwTwips GetMaxFootnoteHeight() const { return m_nMaxFootnoteHeight; }
+
+ // returns value for remaining space until the body reaches minimal height
+ SwTwips GetVarSpace() const;
+
+ // methods needed for layouting
+ // The parameter <_bCollectOnlyPreviousFootnotes> controls if only footnotes
+ // that are positioned before the this footnote boss-frame have to be
+ // collected.
+ void CollectFootnotes( const SwContentFrame* _pRef,
+ SwFootnoteBossFrame* _pOld,
+ SwFootnoteFrames& _rFootnoteArr,
+ const bool _bCollectOnlyPreviousFootnotes = false );
+ void MoveFootnotes_( SwFootnoteFrames &rFootnoteArr, bool bCalc = false );
+ void MoveFootnotes( const SwContentFrame *pSrc, SwContentFrame *pDest,
+ SwTextFootnote const *pAttr );
+
+ // should AdjustNeighbourhood be called (or Grow/Shrink)?
+ SwNeighbourAdjust NeighbourhoodAdjustment() const
+ { return IsPageFrame() ? SwNeighbourAdjust::OnlyAdjust : NeighbourhoodAdjustment_(); }
+};
+
+inline const SwLayoutFrame *SwFootnoteBossFrame::FindBodyCont() const
+{
+ return const_cast<SwFootnoteBossFrame*>(this)->FindBodyCont();
+}
+
+inline const SwFootnoteContFrame *SwFootnoteBossFrame::FindFootnoteCont() const
+{
+ return const_cast<SwFootnoteBossFrame*>(this)->FindFootnoteCont();
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/ftnfrm.hxx b/sw/source/core/inc/ftnfrm.hxx
new file mode 100644
index 000000000..1d56901bd
--- /dev/null
+++ b/sw/source/core/inc/ftnfrm.hxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_FTNFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_FTNFRM_HXX
+
+#include "layfrm.hxx"
+
+class SwContentFrame;
+class SwRootFrame;
+class SwTextNode;
+class SwTextFootnote;
+class SwBorderAttrs;
+class SwFootnoteFrame;
+
+void sw_RemoveFootnotes( SwFootnoteBossFrame* pBoss, bool bPageOnly, bool bEndNotes );
+
+namespace sw {
+
+void RemoveFootnotesForNode(
+ SwRootFrame const& rLayout, SwTextNode const& rTextNode,
+ std::vector<std::pair<sal_Int32, sal_Int32>> const*const pExtents);
+
+}
+
+// There exists a special container frame on a page for footnotes. It's called
+// SwFootnoteContFrame. Each footnote is separated by a SwFootnoteFrame which contains
+// the text frames of a footnote. SwFootnoteFrame can be split and will then
+// continue on another page.
+class SwFootnoteContFrame final : public SwLayoutFrame
+{
+ static SwFootnoteFrame* AddChained(bool bAppend, SwFrame *pNewUpper, bool bDefaultFormat);
+
+public:
+ SwFootnoteContFrame( SwFrameFormat*, SwFrame* );
+
+ const SwFootnoteFrame* FindFootNote() const;
+
+ static inline SwFootnoteFrame* AppendChained(SwFrame* pThis, bool bDefaultFormat);
+ static inline SwFootnoteFrame* PrependChained(SwFrame* pThis, bool bDefaultFormat);
+
+ virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = false ) override;
+ virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) override;
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual void PaintSwFrameShadowAndBorder(
+ const SwRect&,
+ const SwPageFrame* pPage,
+ const SwBorderAttrs&) const override;
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const override;
+ void PaintLine( const SwRect &, const SwPageFrame * ) const;
+};
+
+inline SwFootnoteFrame* SwFootnoteContFrame::AppendChained(SwFrame* pThis, bool bDefaultFormat)
+{
+ return AddChained(true, pThis, bDefaultFormat);
+}
+
+inline SwFootnoteFrame* SwFootnoteContFrame::PrependChained(SwFrame* pThis, bool bDefaultFormat)
+{
+ return AddChained(false, pThis, bDefaultFormat);
+}
+
+class SwFootnoteFrame final : public SwLayoutFrame
+{
+ // Pointer to FootnoteFrame in which the footnote will be continued:
+ // - 0 no following existent
+ // - this for the last one
+ // - otherwise the following FootnoteFrame
+ SwFootnoteFrame *mpFollow;
+ SwFootnoteFrame *mpMaster; // FootnoteFrame from which I am the following
+ SwContentFrame *mpReference; // in this ContentFrame is the footnote reference
+ SwTextFootnote *mpAttribute; // footnote attribute (for recognition)
+
+ // if true paragraphs in this footnote are NOT permitted to flow backwards
+ bool mbBackMoveLocked : 1;
+ // #i49383# - control unlock of position of lower anchored objects.
+ bool mbUnlockPosOfLowerObjs : 1;
+
+public:
+ SwFootnoteFrame( SwFrameFormat*, SwFrame*, SwContentFrame*, SwTextFootnote* );
+
+ virtual bool IsDeleteForbidden() const override;
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const override;
+
+ bool operator<( const SwTextFootnote* pTextFootnote ) const;
+
+#ifdef DBG_UTIL
+ const SwContentFrame *GetRef() const;
+ SwContentFrame *GetRef();
+#else
+ const SwContentFrame *GetRef() const { return mpReference; }
+ SwContentFrame *GetRef() { return mpReference; }
+#endif
+ const SwContentFrame *GetRefFromAttr() const;
+ SwContentFrame *GetRefFromAttr();
+
+ const SwFootnoteFrame *GetFollow() const { return mpFollow; }
+ SwFootnoteFrame *GetFollow() { return mpFollow; }
+
+ const SwFootnoteFrame *GetMaster() const { return mpMaster; }
+ SwFootnoteFrame *GetMaster() { return mpMaster; }
+
+ const SwTextFootnote *GetAttr() const { return mpAttribute; }
+ SwTextFootnote *GetAttr() { return mpAttribute; }
+
+ void SetFollow( SwFootnoteFrame *pNew ) { mpFollow = pNew; }
+ void SetMaster( SwFootnoteFrame *pNew ) { mpMaster = pNew; }
+ void SetRef ( SwContentFrame *pNew ) { mpReference = pNew; }
+
+ void InvalidateNxtFootnoteCnts( SwPageFrame const * pPage );
+
+ void LockBackMove() { mbBackMoveLocked = true; }
+ void UnlockBackMove() { mbBackMoveLocked = false;}
+ bool IsBackMoveLocked() const { return mbBackMoveLocked; }
+
+ // prevents that the last content deletes the SwFootnoteFrame as well (Cut())
+ void ColLock() { mbColLocked = true; }
+ void ColUnlock() { mbColLocked = false; }
+
+ // #i49383#
+ void UnlockPosOfLowerObjs()
+ {
+ mbUnlockPosOfLowerObjs = true;
+ }
+ void KeepLockPosOfLowerObjs()
+ {
+ mbUnlockPosOfLowerObjs = false;
+ }
+ bool IsUnlockPosOfLowerObjs() const
+ {
+ return mbUnlockPosOfLowerObjs;
+ }
+
+ /** search for last content in the current footnote frame
+
+ OD 2005-12-02 #i27138#
+
+ @return SwContentFrame*
+ pointer to found last content frame. NULL, if none is found.
+ */
+ SwContentFrame* FindLastContent();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/hffrm.hxx b/sw/source/core/inc/hffrm.hxx
new file mode 100644
index 000000000..310e57d7e
--- /dev/null
+++ b/sw/source/core/inc/hffrm.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_HFFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_HFFRM_HXX
+
+#include "layfrm.hxx"
+
+class SwHeadFootFrame : public SwLayoutFrame
+{
+protected:
+ void FormatSize(SwTwips nUL, const SwBorderAttrs * pAttrs);
+ void FormatPrt(SwTwips & nUL, const SwBorderAttrs * pAttrs);
+ inline bool GetEatSpacing() const; // in hffrm.cxx
+
+public:
+ SwHeadFootFrame(SwFrameFormat * pFrame, SwFrame*, SwFrameType aType);
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual SwTwips GrowFrame( SwTwips,
+ bool bTst = false, bool bInfo = false ) override;
+ virtual SwTwips ShrinkFrame( SwTwips,
+ bool bTst = false, bool bInfo = false ) override;
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const override;
+};
+
+/// Header in the document layout, inside a page.
+class SwHeaderFrame final : public SwHeadFootFrame
+{
+public:
+ SwHeaderFrame( SwFrameFormat* pFrame, SwFrame* pSib ) : SwHeadFootFrame(pFrame, pSib, SwFrameType::Header) {};
+};
+
+/// Footer in the document layout, inside a page.
+class SwFooterFrame final : public SwHeadFootFrame
+{
+public:
+ SwFooterFrame( SwFrameFormat* pFrame, SwFrame* pSib ) : SwHeadFootFrame(pFrame, pSib, SwFrameType::Footer) {};
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/ifinishedthreadlistener.hxx b/sw/source/core/inc/ifinishedthreadlistener.hxx
new file mode 100644
index 000000000..2ff8197d2
--- /dev/null
+++ b/sw/source/core/inc/ifinishedthreadlistener.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_IFINISHEDTHREADLISTENER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_IFINISHEDTHREADLISTENER_HXX
+
+#include <osl/interlck.h>
+
+/** interface class to listen on the finish of a thread
+
+ OD 2007-03-30 #i73788#
+ Note: The thread provides its ThreadID on the finish notification
+*/
+class IFinishedThreadListener
+{
+public:
+ virtual ~IFinishedThreadListener()
+ {
+ };
+
+ virtual void NotifyAboutFinishedThread( const oslInterlockedCount nThreadID ) = 0;
+
+protected:
+ IFinishedThreadListener()
+ {
+ };
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/layact.hxx b/sw/source/core/inc/layact.hxx
new file mode 100644
index 000000000..6e8a109fb
--- /dev/null
+++ b/sw/source/core/inc/layact.hxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_LAYACT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_LAYACT_HXX
+
+#include <sal/config.h>
+
+#include <vcl/inputtypes.hxx>
+#include <tools/color.hxx>
+
+#include <ctime>
+#include <memory>
+#include <vector>
+
+#include <swrect.hxx>
+
+class OutputDevice;
+class SwFrame;
+class SwFrameDeleteGuard;
+class SwRootFrame;
+class SwLayoutFrame;
+class SwPageFrame;
+class SwFlyFrame;
+class SwContentFrame;
+class SwTabFrame;
+class SwViewShellImp;
+class SwContentNode;
+class SwWait;
+
+/**
+ * The usage of LayAction is always the same:
+ *
+ * 1. Generation of the LayAction object.
+ * 2. Specifying the wanted behaviour via the Set-methods
+ * 3. Calling Action()
+ * 4. Soon after that the destruction of the object
+ *
+ * The object registers at the SwViewShellImp in the ctor and deregisters not until
+ * the dtor!
+ * It's a typical stack object.
+ */
+class SwLayAction
+{
+ SwRootFrame *m_pRoot;
+ SwViewShellImp *m_pImp; // here the action logs in and off
+
+ // For the sake of optimization, so that the tables stick a bit better to
+ // the Cursor when hitting return/backspace in front of one.
+ // The first TabFrame that paints itself (per page) adds itself to the pointer.
+ // The ContentFrames beneath the page do not need to deregister at the Shell for
+ // painting.
+ const SwTabFrame *m_pOptTab;
+
+ std::unique_ptr<SwWait> m_pWait;
+
+ std::vector<SwFrame*> m_aFrameStack;
+ std::vector<std::unique_ptr<SwFrameDeleteGuard>> m_aFrameDeleteGuards;
+
+ // If a paragraph (or anything else) moved more than one page when
+ // formatting, it adds its new page number here.
+ // The InternalAction can then take the appropriate steps.
+ sal_uInt16 m_nPreInvaPage;
+
+ std::clock_t m_nStartTicks; // The Action's starting time; if too much time passes the
+ // WaitCursor can be enabled via CheckWaitCursor()
+
+ VclInputFlags m_nInputType; // Which input should terminate processing
+ sal_uInt16 m_nEndPage; // StatBar control
+ sal_uInt16 m_nCheckPageNum; // CheckPageDesc() was delayed if != USHRT_MAX
+ // check from this page onwards
+
+ bool m_bPaint; // painting or only formatting?
+ bool m_bComplete; // Format everything or just the visible Area?
+ bool m_bCalcLayout; // Complete reformatting?
+ bool m_bAgain; // For the automatically repeated Action if Pages are deleted
+ bool m_bNextCycle; // Reset on the first invalid Page
+ bool m_bInterrupt; // For terminating processing on interrupt
+ bool m_bIdle; // True if the LayAction was triggered by the Idler
+ bool m_bReschedule; // Call Reschedule depending on Progress?
+ bool m_bCheckPages; // Run CheckPageDescs() or delay it
+ bool m_bUpdateExpFields; // Is set if, after Formatting, we need to do another round for ExpField
+ bool m_bBrowseActionStop; // Terminate Action early (as per bInput) and leave the rest to the Idler
+ bool m_bWaitAllowed; // Waitcursor allowed?
+ bool m_bPaintExtraData; // Painting line numbers (or similar) enabled?
+ bool m_bActionInProgress; // Is set in Action() at the beginning and deleted at the end
+
+ // OD 14.04.2003 #106346# - new flag for content formatting on interrupt.
+ bool mbFormatContentOnInterrupt;
+
+ void PaintContent( const SwContentFrame *, const SwPageFrame *,
+ const SwRect &rOldRect, tools::Long nOldBottom );
+ bool PaintWithoutFlys( const SwRect &, const SwContentFrame *,
+ const SwPageFrame * );
+ inline bool PaintContent_( const SwContentFrame *, const SwPageFrame *,
+ const SwRect & );
+
+ bool FormatLayout( OutputDevice* pRenderContext, SwLayoutFrame *, bool bAddRect = true );
+ bool FormatLayoutTab( SwTabFrame *, bool bAddRect );
+ bool FormatContent(SwPageFrame * pPage);
+ void FormatContent_( const SwContentFrame* pContent,
+ const SwPageFrame* pPage );
+ bool IsShortCut( SwPageFrame *& );
+
+ bool TurboAction();
+ bool TurboAction_( const SwContentFrame * );
+ void InternalAction(OutputDevice* pRenderContext);
+
+ static SwPageFrame *CheckFirstVisPage( SwPageFrame *pPage );
+
+ bool RemoveEmptyBrowserPages();
+
+ void PushFormatLayout(SwFrame* pLow);
+ void PopFormatLayout();
+
+ inline void CheckIdleEnd();
+
+public:
+ SwLayAction( SwRootFrame *pRt, SwViewShellImp *pImp );
+ ~SwLayAction();
+
+ void SetIdle ( bool bNew ) { m_bIdle = bNew; }
+ void SetCheckPages ( bool bNew ) { m_bCheckPages = bNew; }
+ void SetBrowseActionStop( bool bNew ) { m_bBrowseActionStop = bNew; }
+ void SetNextCycle ( bool bNew ) { m_bNextCycle = bNew; }
+
+ bool IsWaitAllowed() const { return m_bWaitAllowed; }
+ bool IsNextCycle() const { return m_bNextCycle; }
+ bool IsPaint() const { return m_bPaint; }
+ bool IsIdle() const { return m_bIdle; }
+ bool IsReschedule() const { return m_bReschedule; }
+ bool IsPaintExtraData() const { return m_bPaintExtraData;}
+ bool IsInterrupt() const { return m_bInterrupt; }
+
+ VclInputFlags GetInputType() const { return m_nInputType; }
+
+ // adjusting Action to the wanted behaviour
+ void SetPaint ( bool bNew ) { m_bPaint = bNew; }
+ void SetComplete ( bool bNew ) { m_bComplete = bNew; }
+ void SetStatBar ( bool bNew );
+ void SetInputType ( VclInputFlags nNew ) { m_nInputType = nNew; }
+ void SetCalcLayout ( bool bNew ) { m_bCalcLayout = bNew; }
+ void SetReschedule ( bool bNew ) { m_bReschedule = bNew; }
+ void SetWaitAllowed ( bool bNew ) { m_bWaitAllowed = bNew; }
+
+ void SetAgain(bool bAgain);
+ void SetUpdateExpFields() {m_bUpdateExpFields = true; }
+
+ inline void SetCheckPageNum( sal_uInt16 nNew );
+ void SetCheckPageNumDirect( sal_uInt16 nNew ) { m_nCheckPageNum = nNew; }
+
+ void Action(OutputDevice* pRenderContext); // here it begins
+ void Reset(); // back to CTor-defaults
+
+ bool IsAgain() const { return m_bAgain; }
+ bool IsComplete() const { return m_bComplete; }
+ bool IsExpFields() const { return m_bUpdateExpFields; }
+ bool IsCalcLayout() const { return m_bCalcLayout; }
+ bool IsCheckPages() const { return m_bCheckPages; }
+ bool IsBrowseActionStop() const { return m_bBrowseActionStop; }
+ bool IsActionInProgress() const { return m_bActionInProgress; }
+
+ sal_uInt16 GetCheckPageNum() const { return m_nCheckPageNum; }
+
+ // others should be able to activate the WaitCursor, too
+ void CheckWaitCursor();
+
+ // #i28701# - method is now public;
+ // delete 2nd parameter, because it's not used;
+ void FormatLayoutFly( SwFlyFrame * );
+ // #i28701# - method is now public
+ void FormatFlyContent( const SwFlyFrame * );
+
+};
+
+class SwLayIdle
+{
+ SwRootFrame *m_pRoot;
+ SwViewShellImp *m_pImp; // The Idler registers and deregisters here
+ SwContentNode *m_pContentNode; // The current cursor position is saved here
+ sal_Int32 m_nTextPos;
+ bool m_bPageValid; // Were we able to evaluate everything on the whole page?
+#ifdef DBG_UTIL
+ bool m_bIndicator;
+
+ void ShowIdle( Color eName );
+#endif
+
+ enum IdleJobType{ ONLINE_SPELLING, AUTOCOMPLETE_WORDS, WORD_COUNT, SMART_TAGS };
+ bool DoIdleJob_( const SwContentFrame*, IdleJobType );
+ bool DoIdleJob( IdleJobType, bool bVisAreaOnly );
+
+public:
+ SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pImp );
+ ~SwLayIdle();
+};
+
+inline void SwLayAction::SetCheckPageNum( sal_uInt16 nNew )
+{
+ if ( nNew < m_nCheckPageNum )
+ m_nCheckPageNum = nNew;
+}
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_LAYACT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/laycache.hxx b/sw/source/core/inc/laycache.hxx
new file mode 100644
index 000000000..27f36a96f
--- /dev/null
+++ b/sw/source/core/inc/laycache.hxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_LAYCACHE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_LAYCACHE_HXX
+
+#include <sal/types.h>
+#include <memory>
+
+class SwDoc;
+class SwLayCacheImpl;
+class SvStream;
+
+/*
+ * This class allows to save layout information in the file and it contains
+ * this information after loading of a file.
+ * Call Write(..) with a stream and the document to save and the page break
+ * information of the document will be written.
+ * Call Read(..) with a stream and the member pLayCacheImpl will
+ * read the information from the stream and store it in an internal structure.
+ * There's a simple locking mechanism at these classes,
+ * if somebody reads the information, he increments the lock count by 1,
+ * during the Read(..) function the lock count will set to $8000.
+ */
+class SwLayoutCache
+{
+ std::unique_ptr<SwLayCacheImpl> m_pImpl;
+ sal_uInt16 m_nLockCount;
+
+public:
+ SwLayoutCache();
+ ~SwLayoutCache();
+
+ void Read( SvStream &rStream );
+ static void Write( SvStream &rStream, const SwDoc& rDoc );
+
+ void ClearImpl();
+ bool IsLocked() const { return m_nLockCount > 0; }
+ sal_uInt16& GetLockCount() { return m_nLockCount; }
+ SwLayCacheImpl *LockImpl()
+ { if( m_nLockCount & 0x8000 ) return nullptr;
+ if ( m_pImpl )
+ ++m_nLockCount;
+ return m_pImpl.get(); }
+ void UnlockImpl() { --m_nLockCount; }
+
+#ifdef DBG_UTIL
+ bool CompareLayout( const SwDoc& rDoc ) const;
+#endif
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/layfrm.hxx b/sw/source/core/inc/layfrm.hxx
new file mode 100644
index 000000000..b0f981477
--- /dev/null
+++ b/sw/source/core/inc/layfrm.hxx
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_LAYFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_LAYFRM_HXX
+
+#include "frame.hxx"
+#include <swdllapi.h>
+
+class SwAnchoredObject;
+class SwContentFrame;
+class SwFormatCol;
+struct SwCursorMoveState;
+class SwFrameFormat;
+class SwBorderAttrs;
+class SwFormatFrameSize;
+class SwCellFrame;
+
+/// A layout frame is a frame that contains other frames (m_pLower), e.g. SwPageFrame or SwTabFrame.
+class SW_DLLPUBLIC SwLayoutFrame: public SwFrame
+{
+ // The SwFrame in disguise
+ friend class SwFlowFrame;
+ friend class SwFrame;
+
+ // Releases the Lower while restructuring columns
+ friend SwFrame* SaveContent( SwLayoutFrame *, SwFrame * );
+ friend void RestoreContent( SwFrame *, SwLayoutFrame *, SwFrame *pSibling );
+
+protected:
+
+ virtual void DestroyImpl() override;
+ virtual ~SwLayoutFrame() override;
+
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+
+ SwFrame * m_pLower;
+ std::vector<SwAnchoredObject*> m_VertPosOrientFramesFor;
+
+ virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = false ) override;
+ virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) override;
+
+ tools::Long CalcRel( const SwFormatFrameSize &rSz ) const;
+
+public:
+ // --> #i28701#
+
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const;
+ void RefreshLaySubsidiary( const SwPageFrame*, const SwRect& ) const;
+ void RefreshExtraData( const SwRect & ) const;
+
+ /// Change size of lowers proportionally
+ void ChgLowersProp( const Size& rOldSize );
+
+ void AdjustColumns( const SwFormatCol *pCol, bool bAdjustAttributes );
+
+ void ChgColumns( const SwFormatCol &rOld, const SwFormatCol &rNew,
+ const bool bChgFootnote = false );
+
+ /// Paints the column separation line for the inner columns
+ void PaintColLines( const SwRect &, const SwFormatCol &,
+ const SwPageFrame * ) const;
+
+ virtual bool FillSelection( SwSelectionList& rList, const SwRect& rRect ) const override;
+
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false ) const override;
+
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+
+ /**
+ * Finds the closest Content for the SPoint
+ * Is used for Pages, Flys and Cells if GetModelPositionForViewPoint failed
+ */
+ const SwContentFrame* GetContentPos( Point &rPoint, const bool bDontLeave,
+ const bool bBodyOnly = false,
+ SwCursorMoveState *pCMS = nullptr,
+ const bool bDefaultExpand = true ) const;
+
+ SwLayoutFrame( SwFrameFormat*, SwFrame* );
+
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ const SwFrame *Lower() const { return m_pLower; }
+ SwFrame *Lower() { return m_pLower; }
+ bool ContainsDeleteForbiddenLayFrame() const;
+ const SwContentFrame *ContainsContent() const;
+ inline SwContentFrame *ContainsContent();
+ const SwCellFrame *FirstCell() const;
+ inline SwCellFrame *FirstCell();
+
+ /**
+ * Method <ContainsAny()> doesn't investigate content of footnotes by default.
+ * But under certain circumstances this investigation is intended.
+ * Thus, introduce new optional parameter <_bInvestigateFootnoteForSections>.
+ * It's default is <false>, still indicating that content of footnotes isn't
+ * investigated for sections.
+ */
+ const SwFrame *ContainsAny( const bool _bInvestigateFootnoteForSections = false ) const;
+ inline SwFrame *ContainsAny( const bool _bInvestigateFootnoteForSections = false );
+ bool IsAnLower( const SwFrame * ) const;
+
+ virtual const SwFrameFormat *GetFormat() const;
+ virtual SwFrameFormat *GetFormat();
+ void SetFrameFormat( SwFrameFormat* );
+
+ /**
+ * Moving the Footnotes of all Lowers - starting from StartContent
+ *
+ * @returns true if at least one Footnote was moved
+ * Calls the page number update if bFootnoteNums is set
+ */
+ bool MoveLowerFootnotes( SwContentFrame *pStart, SwFootnoteBossFrame *pOldBoss,
+ SwFootnoteBossFrame *pNewBoss, const bool bFootnoteNums );
+
+ // --> #i28701# - change purpose of method and its name
+ // --> #i44016# - add parameter <_bUnlockPosOfObjs> to
+ /// force an unlockposition call for the lower objects.
+ void NotifyLowerObjs( const bool _bUnlockPosOfObjs = false );
+
+ /**
+ * Invalidates the inner Frames, whose width and/or height are
+ * calculated using percentages.
+ * Frames that are anchored to this or inner Frames, are also invalidated.
+ */
+ void InvaPercentLowers( SwTwips nDiff = 0 );
+
+ /// Called by Format for Frames and Areas with columns
+ void FormatWidthCols( const SwBorderAttrs &, const SwTwips nBorder,
+ const SwTwips nMinHeight );
+
+ /**
+ * InnerHeight returns the height of the content and may be bigger or
+ * less than the PrtArea-Height of the layoutframe himself
+ */
+ SwTwips InnerHeight() const;
+
+ /** method to check relative position of layout frame to
+ a given layout frame.
+
+ refactoring of pseudo-local method <lcl_Apres(..)> in
+ <txtftn.cxx> for #104840#.
+
+ @param _aCheckRefLayFrame
+ constant reference of an instance of class <SwLayoutFrame> which
+ is used as the reference for the relative position check.
+
+ @return true, if <this> is positioned before the layout frame <p>
+ */
+ bool IsBefore( const SwLayoutFrame* _pCheckRefLayFrame ) const;
+
+ const SwFrame* GetLastLower() const;
+ inline SwFrame* GetLastLower();
+
+ virtual void PaintBreak() const;
+
+ void SetVertPosOrientFrameFor(SwAnchoredObject *pObj)
+ {
+ m_VertPosOrientFramesFor.push_back(pObj);
+ }
+
+ void ClearVertPosOrientFrameFor(SwAnchoredObject *pObj)
+ {
+ m_VertPosOrientFramesFor.erase(
+ std::remove(m_VertPosOrientFramesFor.begin(),
+ m_VertPosOrientFramesFor.end(), pObj),
+ m_VertPosOrientFramesFor.end());
+ }
+};
+
+/**
+ * In order to save us from duplicating implementations, we cast here
+ * a little.
+ */
+inline SwContentFrame* SwLayoutFrame::ContainsContent()
+{
+ return const_cast<SwContentFrame*>(static_cast<const SwLayoutFrame*>(this)->ContainsContent());
+}
+
+inline SwCellFrame* SwLayoutFrame::FirstCell()
+{
+ return const_cast<SwCellFrame*>(static_cast<const SwLayoutFrame*>(this)->FirstCell());
+}
+
+inline SwFrame* SwLayoutFrame::ContainsAny( const bool _bInvestigateFootnoteForSections )
+{
+ return const_cast<SwFrame*>(static_cast<const SwLayoutFrame*>(this)->ContainsAny( _bInvestigateFootnoteForSections ));
+}
+
+/**
+ * These SwFrame inlines are here, so that frame.hxx does not need to include layfrm.hxx
+ */
+inline bool SwFrame::IsColBodyFrame() const
+{
+ return mnFrameType == SwFrameType::Body && GetUpper()->IsColumnFrame();
+}
+
+inline bool SwFrame::IsPageBodyFrame() const
+{
+ return mnFrameType == SwFrameType::Body && GetUpper()->IsPageFrame();
+}
+
+inline SwFrame* SwLayoutFrame::GetLastLower()
+{
+ return const_cast<SwFrame*>(static_cast<const SwLayoutFrame*>(this)->GetLastLower());
+}
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_LAYFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/layouter.hxx b/sw/source/core/inc/layouter.hxx
new file mode 100644
index 000000000..a3979cf46
--- /dev/null
+++ b/sw/source/core/inc/layouter.hxx
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_LAYOUTER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_LAYOUTER_HXX
+
+#include <swtypes.hxx>
+#include <unordered_map>
+#include <memory>
+
+class SwEndnoter;
+class SwDoc;
+class SwSectionFrame;
+class SwFootnoteFrame;
+class SwPageFrame;
+class SwLooping;
+
+class SwMovedFwdFramesByObjPos;
+class SwTextFrame;
+class SwRowFrame;
+class SwObjsMarkedAsTmpConsiderWrapInfluence;
+class SwAnchoredObject;
+class SwFlowFrame;
+class SwLayoutFrame;
+
+#define LOOP_PAGE 1
+
+class SwLayouter
+{
+ std::unique_ptr<SwEndnoter> mpEndnoter;
+ std::unique_ptr<SwLooping> mpLooping;
+ void CollectEndnotes_( SwSectionFrame* pSect );
+ bool StartLooping( SwPageFrame const * pPage );
+
+ // --> #i28701#
+ std::unique_ptr<SwMovedFwdFramesByObjPos> mpMovedFwdFrames;
+ // --> #i35911#
+ std::unique_ptr<SwObjsMarkedAsTmpConsiderWrapInfluence> mpObjsTmpConsiderWrapInfl;
+
+public:
+ // --> #i65250#
+ // - data structure to collect moving backward layout information
+ struct tMoveBwdLayoutInfoKey
+ {
+ // frame ID of flow frame
+ sal_uInt32 mnFrameId;
+ // position of new upper frame
+ SwTwips mnNewUpperPosX;
+ SwTwips mnNewUpperPosY;
+ // size of new upper frame
+ SwTwips mnNewUpperWidth;
+ SwTwips mnNewUpperHeight;
+ // free space in new upper frame
+ SwTwips mnFreeSpaceInNewUpper;
+
+ };
+private:
+ struct fMoveBwdLayoutInfoKeyHash
+ {
+ size_t operator()( const tMoveBwdLayoutInfoKey& p_key ) const
+ {
+ return p_key.mnFrameId;
+ }
+ };
+ struct fMoveBwdLayoutInfoKeyEq
+ {
+ bool operator()( const tMoveBwdLayoutInfoKey& p_key1,
+ const tMoveBwdLayoutInfoKey& p_key2 ) const
+ {
+ return p_key1.mnFrameId == p_key2.mnFrameId &&
+ p_key1.mnNewUpperPosX == p_key2.mnNewUpperPosX &&
+ p_key1.mnNewUpperPosY == p_key2.mnNewUpperPosY &&
+ p_key1.mnNewUpperWidth == p_key2.mnNewUpperWidth &&
+ p_key1.mnNewUpperHeight == p_key2.mnNewUpperHeight &&
+ p_key1.mnFreeSpaceInNewUpper == p_key2.mnFreeSpaceInNewUpper;
+ }
+ };
+ std::unordered_map< tMoveBwdLayoutInfoKey, sal_uInt16,
+ fMoveBwdLayoutInfoKeyHash,
+ fMoveBwdLayoutInfoKeyEq > maMoveBwdLayoutInfo;
+public:
+ SwLayouter();
+ ~SwLayouter();
+ void InsertEndnotes( SwSectionFrame const * pSect );
+ void CollectEndnote( SwFootnoteFrame* pFootnote );
+ bool HasEndnotes() const;
+
+ void LoopControl( SwPageFrame* pPage );
+ void EndLoopControl();
+ void LoopingLouieLight( const SwDoc& rDoc, const SwTextFrame& rFrame );
+
+ static void CollectEndnotes( SwDoc* pDoc, SwSectionFrame* pSect );
+ static bool Collecting( SwDoc* pDoc, SwSectionFrame const * pSect, SwFootnoteFrame* pFootnote );
+ static bool StartLoopControl( SwDoc* pDoc, SwPageFrame const *pPage );
+
+ // --> #i28701#
+ static void ClearMovedFwdFrames( const SwDoc& _rDoc );
+ static void InsertMovedFwdFrame( const SwDoc& _rDoc,
+ const SwTextFrame& _rMovedFwdFrameByObjPos,
+ const sal_uInt32 _nToPageNum );
+ static bool FrameMovedFwdByObjPos( const SwDoc& _rDoc,
+ const SwTextFrame& _rTextFrame,
+ sal_uInt32& _ornToPageNum );
+ // --> #i40155# - unmark given frame as to be moved forward.
+ static void RemoveMovedFwdFrame( const SwDoc& _rDoc,
+ const SwTextFrame& _rTextFrame );
+ // --> #i26945#
+ static bool DoesRowContainMovedFwdFrame( const SwDoc& _rDoc,
+ const SwRowFrame& _rRowFrame );
+
+ // --> #i35911#
+ static void ClearObjsTmpConsiderWrapInfluence( const SwDoc& _rDoc );
+ static void InsertObjForTmpConsiderWrapInfluence(
+ const SwDoc& _rDoc,
+ SwAnchoredObject& _rAnchoredObj );
+ static void RemoveObjForTmpConsiderWrapInfluence(
+ const SwDoc& _rDoc,
+ SwAnchoredObject& _rAnchoredObj );
+
+ // --> #i65250#
+ static bool MoveBwdSuppressed( const SwDoc& p_rDoc,
+ const SwFlowFrame& p_rFlowFrame,
+ const SwLayoutFrame& p_rNewUpperFrame );
+ static void ClearMoveBwdLayoutInfo( const SwDoc& p_rDoc );
+};
+
+extern void LOOPING_LOUIE_LIGHT( bool bCondition, const SwTextFrame& rTextFrame );
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_LAYOUTER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx b/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx
new file mode 100644
index 000000000..c42944060
--- /dev/null
+++ b/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_MOVEDFWDFRMSBYOBJPOS_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_MOVEDFWDFRMSBYOBJPOS_HXX
+
+#include <map>
+#include <sal/types.h>
+
+class SwTextNode;
+class SwTextFrame;
+// --> #i26945#
+class SwRowFrame;
+
+typedef std::map< const SwTextNode*, const sal_uInt32 > NodeMap;
+
+class SwMovedFwdFramesByObjPos
+{
+ private:
+ NodeMap maMovedFwdFrames;
+
+ public:
+ SwMovedFwdFramesByObjPos();
+ ~SwMovedFwdFramesByObjPos();
+
+ void Insert( const SwTextFrame& _rMovedFwdFrameByObjPos,
+ const sal_uInt32 _nToPageNum );
+
+ // --> #i40155#
+ void Remove( const SwTextFrame& _rTextFrame );
+
+ bool FrameMovedFwdByObjPos( const SwTextFrame& _rTextFrame,
+ sal_uInt32& _ornToPageNum ) const;
+
+ // --> #i26945#
+ bool DoesRowContainMovedFwdFrame( const SwRowFrame& _rRowFrame ) const;
+
+ void Clear() { maMovedFwdFrames.clear(); };
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/mvsave.hxx b/sw/source/core/inc/mvsave.hxx
new file mode 100644
index 000000000..2e0511a16
--- /dev/null
+++ b/sw/source/core/inc/mvsave.hxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_MVSAVE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_MVSAVE_HXX
+
+#include <vcl/keycod.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <vector>
+#include <deque>
+#include <o3tl/typed_flags_set.hxx>
+
+namespace sfx2 {
+ class MetadatableUndo;
+}
+
+class SvNumberFormatter;
+class SwDoc;
+class SwFormatAnchor;
+class SwFrameFormat;
+class SwIndex;
+class SwNodeIndex;
+class SwNodeRange;
+class SwPaM;
+class SwNode;
+struct SwPosition;
+
+namespace sw::mark
+{
+ class IMark;
+
+ class SaveBookmark
+ {
+ public:
+ SaveBookmark(
+ const ::sw::mark::IMark& rBkmk,
+ const SwNodeIndex& rMvPos,
+ const SwIndex* pIdx);
+ void SetInDoc(SwDoc* pDoc,
+ const SwNodeIndex&,
+ const SwIndex* pIdx =nullptr);
+
+ private:
+ OUString m_aName;
+ OUString m_aShortName;
+ bool m_bHidden;
+ OUString m_aHideCondition;
+ vcl::KeyCode m_aCode;
+ IDocumentMarkAccess::MarkType m_eOrigBkmType;
+ SwNodeOffset m_nNode1;
+ SwNodeOffset m_nNode2;
+ sal_Int32 m_nContent1;
+ sal_Int32 m_nContent2;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndo;
+ };
+
+ enum class RestoreMode { Flys = 1, NonFlys = 2, All = 3 };
+
+ /// Takes care of storing relevant attributes of an SwTextNode before split, then restore them on the new node.
+ class ContentIdxStore
+ {
+ public:
+
+ virtual void Clear() =0;
+ virtual bool Empty() =0;
+ virtual void Save(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nContent, bool bSaveFlySplit=false) =0;
+ virtual void Restore(SwDoc& rDoc, SwNodeOffset nNode, sal_Int32 nOffset=0, bool bAuto = false, bool bAtStart = false, RestoreMode = RestoreMode::All) =0;
+ virtual void Restore(SwNode& rNd, sal_Int32 nLen, sal_Int32 nCorrLen, RestoreMode = RestoreMode::All) =0;
+ virtual ~ContentIdxStore() {};
+ static std::shared_ptr<ContentIdxStore> Create();
+ };
+}
+
+namespace o3tl {
+ template<> struct typed_flags<sw::mark::RestoreMode> : is_typed_flags<sw::mark::RestoreMode, 3> {};
+}
+
+void DelBookmarks(const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd,
+ std::vector< ::sw::mark::SaveBookmark> * SaveBkmk =nullptr,
+ const SwIndex* pSttIdx =nullptr,
+ const SwIndex* pEndIdx =nullptr);
+
+/** data structure to temporarily hold fly anchor positions relative to some
+ * location. */
+struct SaveFly
+{
+ SwFrameFormat* pFrameFormat; /// the fly's frame format
+ SwNodeOffset nNdDiff; /// relative node difference
+ sal_Int32 nContentIndex; ///< index in node
+ bool isAtInsertNode; ///< if true, anchor _at_ insert node index
+
+ SaveFly( SwNodeOffset nNodeDiff, sal_Int32 const nCntntIdx, SwFrameFormat* pFormat, bool bInsert )
+ : pFrameFormat(pFormat)
+ , nNdDiff(nNodeDiff)
+ , nContentIndex(nCntntIdx)
+ , isAtInsertNode(bInsert)
+ { }
+};
+
+typedef std::deque< SaveFly > SaveFlyArr;
+
+void RestFlyInRange( SaveFlyArr& rArr, const SwPosition& rSttIdx,
+ const SwNodeIndex* pInsPos, bool isForceToStartPos = false);
+void SaveFlyInRange( const SwNodeRange& rRg, SaveFlyArr& rArr );
+void SaveFlyInRange( const SwPaM& rPam, const SwPosition& rInsPos,
+ SaveFlyArr& rArr, bool bMoveAllFlys, SwHistory * pHistory = nullptr);
+
+void DelFlyInRange( const SwNodeIndex& rMkNdIdx,
+ const SwNodeIndex& rPtNdIdx,
+ SwIndex const* pMkIdx = nullptr,
+ SwIndex const* pPtIdx = nullptr);
+
+class SwDataChanged
+{
+ const SwPaM* m_pPam;
+ const SwPosition* m_pPos;
+ SwDoc& m_rDoc;
+ sal_Int32 m_nContent;
+
+public:
+ SwDataChanged( const SwPaM& rPam );
+ SwDataChanged( SwDoc& rDoc, const SwPosition& rPos );
+ ~SwDataChanged();
+
+ sal_Int32 GetContent() const { return m_nContent; }
+};
+
+/**
+ * Function declarations so that everything below the CursorShell can
+ * move the Cursor once in a while.
+ * These functions do not call the SwDoc::Corr methods!
+ */
+void PaMCorrAbs( const SwPaM& rRange,
+ const SwPosition& rNewPos );
+
+/// Sets all PaMs in OldNode to relative Pos
+void PaMCorrRel( const SwNodeIndex &rOldNode,
+ const SwPosition &rNewPos,
+ const sal_Int32 nOffset = 0 );
+
+/**
+ * Helper to copy paragraph-bound Flys.
+ * By sorting by order number, we try to retain the layout.
+ */
+class ZSortFly
+{
+ const SwFrameFormat* m_pFormat;
+ const SwFormatAnchor* m_pAnchor;
+ sal_uInt32 m_nOrdNum;
+
+public:
+ ZSortFly( const SwFrameFormat* pFrameFormat, const SwFormatAnchor* pFlyAnchor,
+ sal_uInt32 nArrOrdNum );
+
+ bool operator==( const ZSortFly& ) const { return false; }
+ bool operator<( const ZSortFly& rCmp ) const
+ { return m_nOrdNum < rCmp.m_nOrdNum; }
+
+ const SwFrameFormat* GetFormat() const { return m_pFormat; }
+ const SwFormatAnchor* GetAnchor() const { return m_pAnchor; }
+};
+
+class SwTableNumFormatMerge
+{
+ SvNumberFormatter* pNFormat;
+public:
+ SwTableNumFormatMerge( const SwDoc& rSrc, SwDoc& rDest );
+ ~SwTableNumFormatMerge();
+};
+
+class SaveRedlEndPosForRestore
+{
+ std::vector<SwPosition*> mvSavArr;
+ std::unique_ptr<SwNodeIndex> mpSaveIndex;
+ sal_Int32 mnSaveContent;
+
+public:
+ SaveRedlEndPosForRestore( const SwNodeIndex& rInsIdx, sal_Int32 nContent );
+ ~SaveRedlEndPosForRestore();
+ void Restore();
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_MVSAVE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/node2lay.hxx b/sw/source/core/inc/node2lay.hxx
new file mode 100644
index 000000000..0ba712f19
--- /dev/null
+++ b/sw/source/core/inc/node2lay.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_NODE2LAY_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_NODE2LAY_HXX
+
+#include <nodeoffset.hxx>
+
+#include <memory>
+
+/**
+ * This class connects the Nodes with the Layouts.
+ * It provides an intelligent iterator over Frames belonging to the Node or
+ * Node Range. Depending on the purpose of iterating (e.g. to insert other
+ * Frames before or after the Frames) Master/Follows are recognized and only
+ * the relevant ones are returned. Repeated table headers are also taken
+ * into account.
+ * It's possible to iterate over SectionNodes that are either not directly
+ * assigned to a SectionFrame or to multiple ones due to nesting.
+ *
+ * This class is an interface between the method and a SwClientIter: it
+ * chooses the right sw::BroadcastingModify depending on the task, creates a SwClientIter
+ * and filters its iterations depending on the task.
+ * The task is determined by the choice of class.
+ *
+ * 1. Collecting the UpperFrames (so that later RestoreUpperFrames can be called)
+ * is called by MakeFrames, if there's no PrevNext (before/after we can insert
+ * the Frames).
+ * 2. Inserting the Frames before/after which the new Frames of a Node need to
+ * be inserted, is also called by MakeFrames.
+ */
+
+class SwNode2LayImpl;
+class SwFrame;
+class SwLayoutFrame;
+class SwNode;
+class SwNodes;
+class Point;
+
+class SwNode2Layout
+{
+ std::unique_ptr<SwNode2LayImpl> m_pImpl;
+
+public:
+ /// Use this ctor for inserting before/after rNd
+ /// @param nIdx is the index of the to-be-inserted Node
+ SwNode2Layout(const SwNode& rNd, SwNodeOffset nIdx);
+ ~SwNode2Layout();
+ SwFrame* NextFrame();
+ SwLayoutFrame* UpperFrame(SwFrame*& rpFrame, const SwNode& rNode);
+
+ SwFrame* GetFrame(const Point* pDocPos) const;
+};
+
+class SwNode2LayoutSaveUpperFrames
+{
+ std::unique_ptr<SwNode2LayImpl> m_pImpl;
+
+public:
+ /// Use this ctor for collecting the UpperFrames
+ SwNode2LayoutSaveUpperFrames(const SwNode& rNd);
+ ~SwNode2LayoutSaveUpperFrames();
+
+ void RestoreUpperFrames(SwNodes& rNds, SwNodeOffset nStt, SwNodeOffset nEnd);
+};
+
+namespace sw
+{
+SwFrame const* FindNeighbourFrameForNode(SwNode const& rNode);
+
+} // namespace sw
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/noteurl.hxx b/sw/source/core/inc/noteurl.hxx
new file mode 100644
index 000000000..1e5775d3c
--- /dev/null
+++ b/sw/source/core/inc/noteurl.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_NOTEURL_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_NOTEURL_HXX
+
+class SwNoteURL
+{
+};
+
+// globale Variable, in NoteURL.Cxx angelegt
+extern SwNoteURL* pNoteURL;
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/notxtfrm.hxx b/sw/source/core/inc/notxtfrm.hxx
new file mode 100644
index 000000000..e6af4c132
--- /dev/null
+++ b/sw/source/core/inc/notxtfrm.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_NOTXTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_NOTXTFRM_HXX
+
+#include "cntfrm.hxx"
+#include <node.hxx>
+// MM02
+#include <svx/sdr/contact/viewcontact.hxx>
+
+class SwNoTextNode;
+class OutputDevice;
+class SwBorderAttrs;
+struct SwCursorMoveState;
+
+class SwNoTextFrame final : public SwContentFrame
+{
+private:
+ friend void FrameFinit();
+ const Size& GetSize() const;
+
+ void Format ( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ void PaintPicture( vcl::RenderContext*, const SwRect& ) const;
+
+ virtual void DestroyImpl() override;
+ virtual ~SwNoTextFrame() override;
+
+ // RotateFlyFrame3 add TransformableSwFrame
+ std::unique_ptr< TransformableSwFrame > mpTransformableSwFrame;
+
+ // RotateFlyFrame3 - Support for inner frame of a SwGrfNode.
+ // Only for local data extraction. To uniquely access information
+ // for local transformation, use getFrameArea(Print)Transformation.
+ friend double getLocalFrameRotation_from_SwNoTextFrame(const SwNoTextFrame& rNoTextFrame);
+ double getLocalFrameRotation() const;
+
+ void ClearCache();
+
+ // MM02
+ std::unique_ptr<sdr::contact::ViewContact> mpViewContact;
+ sdr::contact::ViewContact& GetViewContact() const;
+ void OnGraphicArrived();
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+public:
+ SwNoTextFrame( SwNoTextNode * const, SwFrame* );
+
+ const SwContentNode *GetNode() const
+ { return static_cast<SwContentNode const*>(GetDep()); }
+ SwContentNode *GetNode()
+ { return static_cast<SwContentNode *>(GetDep()); }
+
+ virtual bool LeftMargin(SwPaM *) const override;
+ virtual bool RightMargin(SwPaM *, bool bAPI = false) const override;
+
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ virtual bool GetCharRect( SwRect &, const SwPosition&,
+ SwCursorMoveState* = nullptr, bool bAllowFarAway = true ) const override;
+ virtual bool GetModelPositionForViewPoint(SwPosition* pPos, Point& aPoint,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false) const override;
+
+ void GetGrfArea( SwRect &rRect, SwRect * ) const;
+
+ bool IsTransparent() const;
+
+ void StopAnimation( const OutputDevice* = nullptr ) const;
+ bool HasAnimation() const;
+
+ // RotateFlyFrame3 - Support for Transformations
+ bool isTransformableSwFrame() const { return bool(mpTransformableSwFrame); }
+ TransformableSwFrame* getTransformableSwFrame() { return mpTransformableSwFrame.get(); }
+ const TransformableSwFrame* getTransformableSwFrame() const { return mpTransformableSwFrame.get(); }
+
+ // RotateFlyFrame3 - Support for Transformations
+ virtual basegfx::B2DHomMatrix getFrameAreaTransformation() const override;
+ virtual basegfx::B2DHomMatrix getFramePrintAreaTransformation() const override;
+
+ // RotateFlyFrame3 - Support for Transformations
+ virtual void transform_translate(const Point& rOffset) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/objectformatter.hxx b/sw/source/core/inc/objectformatter.hxx
new file mode 100644
index 000000000..99d8644f2
--- /dev/null
+++ b/sw/source/core/inc/objectformatter.hxx
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_OBJECTFORMATTER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_OBJECTFORMATTER_HXX
+
+#include <sal/types.h>
+#include <memory>
+
+class SwFrame;
+// #i26945#
+class SwTextFrame;
+class SwLayoutFrame;
+class SwPageFrame;
+class SwAnchoredObject;
+class SwLayAction;
+// OD 2004-10-04 #i26945#
+class SwPageNumAndTypeOfAnchors;
+
+// #i28701#
+// Format floating screen objects, which are anchored at the given anchor frame
+// and registered at the given page frame.
+
+class SwObjectFormatter
+{
+ private:
+ // page frame, at which the floating screen objects are registered.
+ const SwPageFrame& mrPageFrame;
+
+ // value of document compatibility option 'Consider wrapping style on
+ // object positioning'
+ const bool mbConsiderWrapOnObjPos;
+
+ // layout action calling the format of the floating screen objects
+ SwLayAction* mpLayAction;
+
+ // data structure to collect page number of object's 'anchor'
+ // #i26945#
+ std::unique_ptr<SwPageNumAndTypeOfAnchors> mpPgNumAndTypeOfAnchors;
+
+ /** helper method for method <FormatObj_(..)> - performs the intrinsic
+ format of the layout of the given layout frame and all its lower
+ layout frames.
+
+ #i28701#
+ IMPORTANT NOTE:
+ Method corresponds to methods <SwLayAction::FormatLayoutFly(..)> and
+ <SwLayAction::FormatLayout(..)>. Thus, its code for the formatting have
+ to be synchronised.
+ */
+ void FormatLayout_( SwLayoutFrame& _rLayoutFrame );
+
+ /** helper method for method <FormatObj_(..)> - performs the intrinsic
+ format of the content of the given floating screen object.
+
+ #i28701#
+ */
+ void FormatObjContent( SwAnchoredObject& _rAnchoredObj );
+
+ protected:
+ SwObjectFormatter( const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction,
+ const bool _bCollectPgNumOfAnchors = false );
+
+ static std::unique_ptr<SwObjectFormatter> CreateObjFormatter( SwFrame& _rAnchorFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+
+ virtual SwFrame& GetAnchorFrame() = 0;
+
+ const SwPageFrame& GetPageFrame() const
+ {
+ return mrPageFrame;
+ }
+
+ bool ConsiderWrapOnObjPos() const
+ {
+ return mbConsiderWrapOnObjPos;
+ }
+
+ SwLayAction* GetLayAction()
+ {
+ return mpLayAction;
+ }
+
+ /** performs the intrinsic format of a given floating screen object and its content.
+ */
+ void FormatObj_( SwAnchoredObject& _rAnchoredObj );
+
+ /** invokes the intrinsic format method for all floating screen objects,
+ anchored at anchor frame on the given page frame
+
+ for format of floating screen objects for
+ follow text frames, the 'master' text frame is passed to the method.
+ Thus, the objects, whose anchor character is inside the follow text
+ frame can be formatted.
+
+ @param _pMasterTextFrame
+ input parameter - pointer to 'master' text frame. default value: NULL
+ */
+ bool FormatObjsAtFrame_( SwTextFrame* _pMasterTextFrame = nullptr );
+
+ /** accessor to collected anchored object
+ */
+ SwAnchoredObject* GetCollectedObj( const sal_uInt32 _nIndex );
+
+ /** accessor to 'anchor' page number of collected anchored object
+ */
+ sal_uInt32 GetPgNumOfCollected( const sal_uInt32 _nIndex );
+
+ /** accessor to 'anchor' type of collected anchored object
+ */
+ bool IsCollectedAnchoredAtMaster( const sal_uInt32 _nIndex );
+
+ /** accessor to total number of collected anchored objects
+ */
+ sal_uInt32 CountOfCollected();
+
+ public:
+ virtual ~SwObjectFormatter();
+
+ /** intrinsic method to format a certain floating screen object
+
+ #i40147# - add parameter <_bCheckForMovedFwd>
+
+ @param _rAnchoredObj
+ input parameter - anchored object, which have to be formatted.
+
+ @param _bCheckForMovedFwd
+ input parameter - boolean indicating, that after a successful
+ format of the anchored object the anchor frame has to be checked,
+ if it would moved forward due to the positioning of the anchored object.
+ default value: false
+ value only considered, if wrapping style influence has to be
+ considered for the positioning of the anchored object.
+ */
+ virtual bool DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd = false ) = 0;
+
+ /** intrinsic method to format all floating screen objects
+ */
+ virtual bool DoFormatObjs() = 0;
+
+ /** method to format all floating screen objects at the given anchor frame
+ */
+ static bool FormatObjsAtFrame( SwFrame& _rAnchorFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction = nullptr );
+
+ /** method to format a given floating screen object
+ */
+ static bool FormatObj( SwAnchoredObject& _rAnchoredObj,
+ SwFrame* _pAnchorFrame = nullptr,
+ const SwPageFrame* _pPageFrame = nullptr );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/observablethread.hxx b/sw/source/core/inc/observablethread.hxx
new file mode 100644
index 000000000..3d10a7f38
--- /dev/null
+++ b/sw/source/core/inc/observablethread.hxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_OBSERVABLETHREAD_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_OBSERVABLETHREAD_HXX
+
+#include <osl/thread.hxx>
+#include <osl/interlck.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <memory>
+
+class IFinishedThreadListener;
+
+/** class for an observable thread
+
+ OD 2007-01-29 #i73788#
+ Note: A thread is ref-counted. Thus, an instance of a derived class has to
+ to be hold via a reference. The thread itself assures during its execution,
+ that the ref-count is increased. Its execution starts with its <run()> method
+ and ends with its <onTerminated()> method.
+ Note: A thread can be only observed by one or none thread observer in order
+ to notify, that the thread has finished its work.
+*/
+class ObservableThread : public osl::Thread,
+ public salhelper::SimpleReferenceObject
+{
+ public:
+
+ virtual ~ObservableThread() override;
+
+ void SetListener( std::weak_ptr< IFinishedThreadListener > const & pThreadListener,
+ const oslInterlockedCount nThreadID );
+
+ // resolve ambiguity
+ using SimpleReferenceObject::operator new;
+ using SimpleReferenceObject::operator delete;
+
+ protected:
+
+ ObservableThread();
+
+ /** intrinsic function of the thread
+
+ Important note:
+ Do not override this method again. Instead override <threadFunction()>.
+ Otherwise, it's not guaranteed, that its ref-count is increased
+ during the execution of the thread.
+ */
+ virtual void SAL_CALL run() override;
+
+ virtual void threadFunction() = 0;
+
+ /** method called, when thread has finished its work
+
+ Important note:
+ Do not override this method again. Instead override <threadFinished()>.
+ Otherwise, it's not guaranteed, that the ref-count is decreased at
+ the end of its execution and that the observer is notified, that
+ the thread has finished its work.
+ */
+ virtual void SAL_CALL onTerminated() override;
+
+ private:
+
+ oslInterlockedCount mnThreadID;
+
+ std::weak_ptr< IFinishedThreadListener > mpThreadListener;
+
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/pagedeschint.hxx b/sw/source/core/inc/pagedeschint.hxx
new file mode 100644
index 000000000..2dbfec18d
--- /dev/null
+++ b/sw/source/core/inc/pagedeschint.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_PAGEDESCHINT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_PAGEDESCHINT_HXX
+
+#include <svl/hint.hxx>
+
+class SwPageDesc;
+
+class SwPageDescHint final : public SfxHint
+{
+ SwPageDesc* m_pPageDesc;
+public:
+ SwPageDescHint( SwPageDesc* p )
+ : m_pPageDesc(p)
+ {}
+
+ SwPageDesc* GetPageDesc() const { return m_pPageDesc; }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/pagefrm.hxx b/sw/source/core/inc/pagefrm.hxx
new file mode 100644
index 000000000..73d81a689
--- /dev/null
+++ b/sw/source/core/inc/pagefrm.hxx
@@ -0,0 +1,460 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_PAGEFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_PAGEFRM_HXX
+
+#include <viewsh.hxx>
+#include "ftnboss.hxx"
+#include "hffrm.hxx"
+
+#include <SidebarWindowsTypes.hxx>
+
+class SwFlyFrame;
+class SwFlyFrameFormat;
+class SwPageDesc;
+class SwContentFrame;
+struct SwPosition;
+struct SwCursorMoveState;
+class SwAttrSetChg;
+namespace vcl { class Font; }
+class SwSortedObjs;
+class SwAnchoredObject;
+
+enum class SwPageFrameInvFlags : sal_uInt8
+{
+ NONE = 0x00,
+ InvalidatePrt = 0x01,
+ SetCompletePaint = 0x02,
+ InvalidateNextPos = 0x04,
+ PrepareHeader = 0x08,
+ PrepareFooter = 0x10,
+ CheckGrid = 0x20,
+ InvalidateGrid = 0x40,
+};
+
+namespace o3tl {
+ template<> struct typed_flags<SwPageFrameInvFlags> : is_typed_flags<SwPageFrameInvFlags, 0x007f> {};
+}
+
+/// A page of the document layout. Upper frame is expected to be an SwRootFrame
+/// instance. At least an SwBodyFrame lower is expected.
+class SAL_DLLPUBLIC_RTTI SwPageFrame final: public SwFootnoteBossFrame
+{
+ friend class SwFrame;
+
+ std::unique_ptr<SwSortedObjs> m_pSortedObjs;
+
+ SwPageDesc *m_pDesc; //PageDesc that describes the Page
+
+ /// Physical page number: index into list of SwRootFrame lowers
+ sal_uInt16 m_nPhyPageNum;
+
+ bool m_bInvalidContent :1;
+ bool m_bInvalidLayout :1;
+ bool m_bInvalidFlyContent :1;
+ bool m_bInvalidFlyLayout :1;
+ bool m_bInvalidFlyInCnt :1;
+ bool m_bFootnotePage :1; // This Page is for document end footnotes
+ bool m_bEmptyPage :1; // This Page is an explicitly empty page
+ bool m_bEndNotePage :1; // 'Footnote page' for end notes
+ bool m_bInvalidSpelling :1; // We need online spelling
+ bool m_bInvalidSmartTags :1; // We need checking for smarttags
+ bool m_bInvalidAutoCmplWrds :1; // Update auto complete word list
+ bool m_bInvalidWordCount :1;
+ bool m_bHasGrid :1; // Grid for Asian layout
+
+ static const sal_Int8 snShadowPxWidth;
+
+ void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, SwPageFrameInvFlags &,
+ SwAttrSetChg *pa = nullptr, SwAttrSetChg *pb = nullptr );
+
+ /// Adapt the max. footnote height in each single column
+ void SetColMaxFootnoteHeight();
+
+ /** determine rectangle for horizontal page shadow
+
+ #i9719#
+
+ @param _rPageRect
+ input parameter - constant instance reference of the page rectangle.
+ Generally, it's the frame area of the page, but for empty pages in print
+ preview, this parameter is useful.
+
+ @param _pViewShell
+ input parameter - instance of the view shell, for which the rectangle
+ has to be generated.
+
+ @param _orBottomShadowRect
+ output parameter - instance reference of the bottom shadow rectangle for
+ the given page rectangle
+ */
+
+ static void GetHorizontalShadowRect( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ OutputDevice const * pRenderContext,
+ SwRect& _orBottomShadowRect,
+ bool bPaintLeftShadow,
+ bool bPaintRightShadow,
+ bool bRightSidebar );
+
+ virtual void DestroyImpl() override;
+ virtual ~SwPageFrame() override;
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+
+ /// Calculate the content height of a page (without columns).
+ size_t GetContentHeight(const tools::Long nTop, const tools::Long nBottom) const;
+
+public:
+ SwPageFrame( SwFrameFormat*, SwFrame*, SwPageDesc* );
+
+ /// Make this public, so that the SwViewShell can access it when switching from browse mode
+ /// Add/remove header/footer
+ void PrepareHeader();
+ void PrepareFooter();
+
+ const SwSortedObjs *GetSortedObjs() const { return m_pSortedObjs.get(); }
+ SwSortedObjs *GetSortedObjs() { return m_pSortedObjs.get(); }
+
+ void AppendDrawObjToPage( SwAnchoredObject& _rNewObj );
+ void RemoveDrawObjFromPage( SwAnchoredObject& _rToRemoveObj );
+
+ void AppendFlyToPage( SwFlyFrame *pNew );
+ void RemoveFlyFromPage( SwFlyFrame *pToRemove );
+ void MoveFly( SwFlyFrame *pToMove, SwPageFrame *pDest ); // Optimized Remove/Append
+
+ void SetPageDesc( SwPageDesc *, SwFrameFormat * );
+ SwPageDesc *GetPageDesc() { return m_pDesc; }
+ const SwPageDesc *GetPageDesc() const { return m_pDesc; }
+ SwPageDesc *FindPageDesc();
+
+ SwContentFrame *FindLastBodyContent();
+ inline SwContentFrame *FindFirstBodyContent();
+ inline const SwContentFrame *FindFirstBodyContent() const;
+ inline const SwContentFrame *FindLastBodyContent() const;
+
+ SwRect GetBoundRect(OutputDevice const * pOutputDevice) const;
+
+ // Specialized GetContentPos() for Field in Frames
+ void GetContentPosition( const Point &rPt, SwPosition &rPos ) const;
+
+ bool IsEmptyPage() const { return m_bEmptyPage; } // Explicitly empty page
+
+ void UpdateFootnoteNum();
+
+ /// Always call after Paste
+ /// Creates the page-bound frames and formats the generic content
+ void PreparePage( bool bFootnote );
+
+ // Sends a Prepare() to all ContentFrames caused by a changed register template
+ void PrepareRegisterChg();
+
+ // Appends a fly frame - the given one or a new one - at the page frame.
+ // Needed for <Modify> and <MakeFrames>
+ // - return value not needed any more
+ // - second parameter is of type <SwFlyFrameFormat*>
+ // - third parameter only needed for assertion, but calling method assures
+ // this assertion. Thus, delete it.
+ void PlaceFly( SwFlyFrame* pFly, SwFlyFrameFormat* pFormat );
+
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false ) const override;
+ /// Get info from Client
+ virtual bool GetInfo( SfxPoolItem& ) const override;
+
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+ virtual void CheckDirection( bool bVert ) override;
+ void CheckGrid( bool bInvalidate );
+ void PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const;
+ bool HasGrid() const { return m_bHasGrid; }
+
+ void PaintDecorators( ) const;
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const override;
+ virtual void PaintBreak() const override;
+
+ /// Paint line number etc.
+ void RefreshExtraData( const SwRect & ) const;
+
+ /// Paint helper lines
+ void RefreshSubsidiary( const SwRect& ) const;
+
+ /// Foot note interface
+ bool IsFootnotePage() const { return m_bFootnotePage; }
+ bool IsEndNotePage() const { return m_bEndNotePage; }
+ void SetFootnotePage( bool b ) { m_bFootnotePage = b; }
+ void SetEndNotePage( bool b ) { m_bEndNotePage = b; }
+
+ sal_uInt16 GetPhyPageNum() const { return m_nPhyPageNum;}
+ void SetPhyPageNum( sal_uInt16 nNum ) { m_nPhyPageNum = nNum;}
+
+ /// Validate, invalidate and query the Page status
+ /// Layout/Content and Fly/non-Fly respectively are inspected separately
+ inline void InvalidateFlyLayout() const;
+ inline void InvalidateFlyContent() const;
+ inline void InvalidateFlyInCnt() const;
+ inline void InvalidateLayout() const;
+ inline void InvalidateContent() const;
+ inline void InvalidateSpelling() const;
+ inline void InvalidateSmartTags() const;
+ inline void InvalidateAutoCompleteWords() const;
+ inline void InvalidateWordCount() const;
+ inline void ValidateFlyLayout() const;
+ inline void ValidateFlyContent() const;
+ inline void ValidateFlyInCnt() const;
+ inline void ValidateLayout() const;
+ inline void ValidateContent() const;
+ inline void ValidateSpelling() const;
+ inline void ValidateSmartTags() const;
+ inline void ValidateAutoCompleteWords() const;
+ inline void ValidateWordCount() const;
+ inline bool IsInvalid() const;
+ inline bool IsInvalidFly() const;
+ bool IsRightShadowNeeded() const;
+ bool IsLeftShadowNeeded() const;
+ bool IsInvalidFlyLayout() const { return m_bInvalidFlyLayout; }
+ bool IsInvalidFlyContent() const { return m_bInvalidFlyContent; }
+ bool IsInvalidFlyInCnt() const { return m_bInvalidFlyInCnt; }
+ bool IsInvalidLayout() const { return m_bInvalidLayout; }
+ bool IsInvalidContent() const { return (m_bInvalidContent || m_bInvalidFlyInCnt); }
+ bool IsInvalidSpelling() const { return m_bInvalidSpelling; }
+ bool IsInvalidSmartTags() const { return m_bInvalidSmartTags; }
+ bool IsInvalidAutoCompleteWords() const { return m_bInvalidAutoCmplWrds; }
+ bool IsInvalidWordCount() const { return m_bInvalidWordCount; }
+
+ /** SwPageFrame::GetDrawBackgroundColor
+
+ determine the color, that is respectively will be drawn as background
+ for the page frame.
+
+ @return reference to an instance of class Color
+ */
+ Color GetDrawBackgroundColor() const;
+
+ /** paint margin area of a page
+
+ implement paint of margin area; margin area will be painted for a
+ view shell with a window and if the document is not in online layout.
+
+ @param _rOutputRect
+ input parameter - constant instance reference of the rectangle, for
+ which an output has to be generated.
+
+ @param _pViewShell
+ input parameter - instance of the view shell, on which the output
+ has to be generated.
+ */
+ void PaintMarginArea( const SwRect& _rOutputRect,
+ SwViewShell const * _pViewShell ) const;
+
+ /** paint page border and shadow
+
+ @param _rPageRect
+ input parameter - constant instance reference of the page rectangle.
+ Generally, it's the frame area of the page, but for empty pages in print
+ preview, this parameter is useful.
+
+ @param _pViewShell
+ input parameter - instance of the view shell, on which the output
+ has to be generated.
+
+ @param bPaintRightShadow
+ Should we paint shadow on the right or not (used in book mode)
+
+ @param bFullBottomShadow
+ Should we have a bottom shadow of the same size as the pages or
+ not (for right pages in book mode in a LTR environment).
+
+ @param bRightSidebar
+ Is the note sidebar on the right or not (used to adjust the
+ shadow with & position).
+ */
+ static void PaintBorderAndShadow( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ bool bPaintLeftShadow,
+ bool bPaintRightShadow,
+ bool bRightSidebar );
+
+ /** get bound rectangle of border and shadow for repaints
+
+ @param _rPageRect
+ input parameter - constant instance reference of the page rectangle.
+ Generally, it's the frame area of the page, but for empty pages in print
+ preview, this parameter is useful.
+
+ @param _pViewShell
+ input parameter - instance of the view shell, for which the rectangle
+ has to be generated.
+
+ @param _orBorderAndShadowBoundRect
+ output parameter - instance reference of the bounded border and shadow
+ rectangle for the given page rectangle
+ */
+ static void GetBorderAndShadowBoundRect( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ OutputDevice const * pRenderContext,
+ SwRect& _orBorderAndShadowBoundRect,
+ const bool bLeftShadow,
+ const bool bRightShadow,
+ const bool bRightSidebar
+ );
+
+ static void PaintNotesSidebar(const SwRect& _rPageRect, SwViewShell* _pViewShell, sal_uInt16 nPageNum, bool bRight);
+ static void PaintNotesSidebarArrows(const Point &rMiddleFirst, const Point &rMiddleSecond, SwViewShell const * _pViewShell, const Color& rColorUp, const Color& rColorDown);
+ /**
+ asks the page on which side a margin should be shown, e.g for notes
+ returns true for left side, false for right side
+ */
+ sw::sidebarwindows::SidebarPosition SidebarPosition() const;
+
+ virtual bool FillSelection( SwSelectionList& rList, const SwRect& rRect ) const override;
+
+ SwRect PrtWithoutHeaderAndFooter() const;
+
+ // in case this is an empty page, this function returns the 'reference' page
+ const SwPageFrame& GetFormatPage() const;
+
+ /// If in header or footer area, it also indicates the exact area in rControl.
+ /// Header or footer must be active, otherwise returns false.
+ bool IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const;
+
+ // return font used to paint the "empty page" string
+ static const vcl::Font& GetEmptyPageFont();
+
+ static SwTwips GetSidebarBorderWidth( const SwViewShell* );
+
+ /// Is bottom-of-page-frame - bottom-of-text-frame difference valid in case whitespace is hidden?
+ /// If false is returned, then the caller should handle negative difference as (at least) zero difference instead.
+ bool CheckPageHeightValidForHideWhitespace(SwTwips nDiff);
+
+ const SwHeaderFrame* GetHeaderFrame() const;
+ const SwFooterFrame* GetFooterFrame() const;
+};
+
+inline SwContentFrame *SwPageFrame::FindFirstBodyContent()
+{
+ SwLayoutFrame *pBody = FindBodyCont();
+ return pBody ? pBody->ContainsContent() : nullptr;
+}
+inline const SwContentFrame *SwPageFrame::FindFirstBodyContent() const
+{
+ const SwLayoutFrame *pBody = FindBodyCont();
+ return pBody ? pBody->ContainsContent() : nullptr;
+}
+inline const SwContentFrame *SwPageFrame::FindLastBodyContent() const
+{
+ return const_cast<SwPageFrame*>(this)->FindLastBodyContent();
+}
+inline void SwPageFrame::InvalidateFlyLayout() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidFlyLayout = true;
+}
+inline void SwPageFrame::InvalidateFlyContent() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidFlyContent = true;
+}
+inline void SwPageFrame::InvalidateFlyInCnt() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidFlyInCnt = true;
+}
+inline void SwPageFrame::InvalidateLayout() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidLayout = true;
+}
+inline void SwPageFrame::InvalidateContent() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidContent = true;
+}
+inline void SwPageFrame::InvalidateSpelling() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidSpelling = true;
+}
+
+inline void SwPageFrame::InvalidateSmartTags() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidSmartTags = true;
+}
+inline void SwPageFrame::InvalidateAutoCompleteWords() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidAutoCmplWrds = true;
+}
+inline void SwPageFrame::InvalidateWordCount() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidWordCount = true;
+}
+inline void SwPageFrame::ValidateFlyLayout() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidFlyLayout = false;
+}
+inline void SwPageFrame::ValidateFlyContent() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidFlyContent = false;
+}
+inline void SwPageFrame::ValidateFlyInCnt() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidFlyInCnt = false;
+}
+inline void SwPageFrame::ValidateLayout() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidLayout = false;
+}
+inline void SwPageFrame::ValidateContent() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidContent = false;
+}
+inline void SwPageFrame::ValidateSpelling() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidSpelling = false;
+}
+
+inline void SwPageFrame::ValidateSmartTags() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidSmartTags = false;
+}
+inline void SwPageFrame::ValidateAutoCompleteWords() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidAutoCmplWrds = false;
+}
+inline void SwPageFrame::ValidateWordCount() const
+{
+ const_cast<SwPageFrame*>(this)->m_bInvalidWordCount = false;
+}
+
+inline bool SwPageFrame::IsInvalid() const
+{
+ return (m_bInvalidContent || m_bInvalidLayout || m_bInvalidFlyInCnt);
+}
+inline bool SwPageFrame::IsInvalidFly() const
+{
+ return m_bInvalidFlyLayout || m_bInvalidFlyContent;
+}
+
+
+class SwTextGridItem;
+
+SwTextGridItem const* GetGridItem(SwPageFrame const*const);
+
+sal_uInt16 GetGridWidth(SwTextGridItem const&, SwDoc const&);
+
+namespace sw { bool IsPageFrameEmpty(SwPageFrame const& rPage); }
+
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_PAGEFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/paintfrm.hxx b/sw/source/core/inc/paintfrm.hxx
new file mode 100644
index 000000000..dca4a3871
--- /dev/null
+++ b/sw/source/core/inc/paintfrm.hxx
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_PAINTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_PAINTFRM_HXX
+
+#include <tools/color.hxx>
+
+extern Color aGlobalRetoucheColor;
+
+class OutputDevice;
+namespace vcl
+{
+typedef OutputDevice RenderContext;
+};
+
+void SwCalcPixStatics(vcl::RenderContext const* pOut);
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_PAINTFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/pamtyp.hxx b/sw/source/core/inc/pamtyp.hxx
new file mode 100644
index 000000000..3e99dd878
--- /dev/null
+++ b/sw/source/core/inc/pamtyp.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_PAMTYP_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_PAMTYP_HXX
+
+#include <unotools/textsearch.hxx>
+
+#include <memory>
+
+class SwpHints;
+struct SwPosition;
+class SwPaM;
+class SwTextAttr;
+class SwFormat;
+class SfxPoolItem;
+class SwRootFrame;
+class SwNode;
+class SwNodeIndex;
+class SwContentNode;
+class SwIndex;
+class SvxSearchItem;
+
+namespace i18nutil {
+ struct SearchOptions2;
+}
+
+// function prototypes for the move/find methods of SwPaM
+
+void GoStartDoc( SwPosition*);
+void GoEndDoc( SwPosition*);
+void GoStartSection( SwPosition*);
+void GoEndSection( SwPosition*);
+const SwTextAttr* GetFrwrdTextHint( const SwpHints&, size_t&, sal_Int32 );
+const SwTextAttr* GetBkwrdTextHint( const SwpHints&, size_t&, sal_Int32 );
+
+bool GoNext(SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode );
+bool GoPrevious(SwNode* pNd, SwIndex * pIdx, sal_uInt16 nMode );
+SwContentNode* GoNextNds( SwNodeIndex * pIdx, bool );
+SwContentNode* GoPreviousNds( SwNodeIndex * pIdx, bool );
+
+// type definitions of functions
+typedef bool (*GoNd)( SwNode*, SwIndex*, sal_uInt16 );
+typedef SwContentNode* (*GoNds)( SwNodeIndex*, bool );
+typedef void (*GoDoc)( SwPosition* );
+typedef void (*GoSection)( SwPosition* );
+typedef bool (SwPosition::*CmpOp)( const SwPosition& ) const;
+typedef const SwTextAttr* (*GetHint)( const SwpHints&, size_t&, sal_Int32 );
+typedef bool (utl::TextSearch::*SearchText)( const OUString&, sal_Int32*,
+ sal_Int32*, css::util::SearchResult* );
+typedef void (*MvSection)( SwNodeIndex * );
+
+struct SwMoveFnCollection
+{
+ GoNd fnNd;
+ GoNds fnNds;
+ GoDoc fnDoc;
+ GoSection fnSections;
+ CmpOp fnCmpOp;
+ GetHint fnGetHint;
+ SearchText fnSearch;
+ MvSection fnSection;
+};
+
+// function prototype for searching
+SwContentNode* GetNode(SwPaM&, bool&, SwMoveFnCollection const &,
+ bool bInReadOnly = false, SwRootFrame const* pLayout = nullptr);
+
+namespace sw {
+
+ std::unique_ptr<SwPaM> MakeRegion(SwMoveFnCollection const & fnMove,
+ const SwPaM & rOrigRg);
+
+ /// Search.
+ bool FindTextImpl(SwPaM & rSearchPam,
+ const i18nutil::SearchOptions2& rSearchOpt,
+ bool bSearchInNotes,
+ utl::TextSearch& rSText,
+ SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly,
+ SwRootFrame const* pLayout,
+ std::unique_ptr<SvxSearchItem>& xSearchItem);
+ bool FindFormatImpl(SwPaM & rSearchPam,
+ const SwFormat& rFormat,
+ SwMoveFnCollection const & fnMove,
+ const SwPaM & rRegion, bool bInReadOnly,
+ SwRootFrame const* pLayout);
+ bool FindAttrImpl(SwPaM & rSearchPam,
+ const SfxPoolItem& rAttr,
+ SwMoveFnCollection const & fnMove,
+ const SwPaM & rPam, bool bInReadOnly,
+ SwRootFrame const* pLayout);
+
+} // namespace sw
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/prevwpage.hxx b/sw/source/core/inc/prevwpage.hxx
new file mode 100644
index 000000000..d9c8d1df0
--- /dev/null
+++ b/sw/source/core/inc/prevwpage.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_PREVWPAGE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_PREVWPAGE_HXX
+
+// classes <Point>, <Size> and <Rectangle>
+#include <tools/gen.hxx>
+
+class SwPageFrame;
+
+/** data structure for a preview page in the current preview layout
+*/
+struct PreviewPage
+{
+ const SwPageFrame* pPage;
+ bool bVisible;
+ Size aPageSize;
+ Point aPreviewWinPos;
+ Point aLogicPos;
+ Point aMapOffset;
+
+ inline PreviewPage();
+};
+
+inline PreviewPage::PreviewPage()
+ : pPage( nullptr ),
+ bVisible( false ),
+ aPageSize( Size(0,0) ),
+ aPreviewWinPos( Point(0,0) ),
+ aLogicPos( Point(0,0) ),
+ aMapOffset( Point(0,0) )
+{};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/ptqueue.hxx b/sw/source/core/inc/ptqueue.hxx
new file mode 100644
index 000000000..a5e40f2e3
--- /dev/null
+++ b/sw/source/core/inc/ptqueue.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_PTQUEUE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_PTQUEUE_HXX
+
+/**
+ * Unfortunately we have some problems with processing more than one Paint()
+ * at a time. This happens especially often during printing.
+ *
+ * SwRootFrame::PaintSwFrame() determines that it's called a second time and adds the
+ * rectangle and the corresponding Shell to the PaintCollector.
+ * The call sites that are causing the double Paint() only need to process the
+ * collected Paint()s at the right point in time.
+ * Doing this during printing (after having printed one page) is very suitable
+ * for doing that.
+ *
+ * Invalidating windows directly from the RootFrame::PaintSwFrame was not a successful
+ * approach, because the Paint()s arrive at a very unfavourable point in time.
+ * Triggering an update for all windows after printing each page does not seem
+ * appropriate either: on the one hand we don't have direct access to the edit
+ * windows and on the other hand the updates can become very costly on some
+ * platforms.
+ */
+
+class SwQueuedPaint;
+class SwViewShell;
+class SwRect;
+
+class SwPaintQueue
+{
+public:
+ static SwQueuedPaint* s_pPaintQueue;
+
+ static void Add(SwViewShell* pSh, const SwRect& rNew);
+ static void Remove(SwViewShell const* pSh);
+ static void Repaint();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/retrievedinputstreamdata.hxx b/sw/source/core/inc/retrievedinputstreamdata.hxx
new file mode 100644
index 000000000..4a958b31b
--- /dev/null
+++ b/sw/source/core/inc/retrievedinputstreamdata.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_RETRIEVEDINPUTSTREAMDATA_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_RETRIEVEDINPUTSTREAMDATA_HXX
+
+#include <tools/link.hxx>
+#include <sal/types.h>
+#include <mutex>
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <map>
+#include <memory>
+
+namespace com::sun::star::io { class XInputStream; }
+
+class SwAsyncRetrieveInputStreamThreadConsumer;
+
+/** Singleton class to manage retrieved input stream data in Writer
+
+ OD 2007-01-29 #i73788#
+ The instance of this class provides data container for retrieved input
+ stream data. The data container is accessed via a key, which the data
+ manager provides on creation of the data container.
+ When a certain data container is filled with data, an user event is submitted
+ to trigger the processing of with data.
+*/
+class SwRetrievedInputStreamDataManager
+{
+ public:
+
+ typedef sal_uInt64 tDataKey;
+
+ struct tData
+ {
+ std::weak_ptr< SwAsyncRetrieveInputStreamThreadConsumer > mpThreadConsumer;
+ css::uno::Reference<css::io::XInputStream> mxInputStream;
+ bool mbIsStreamReadOnly;
+
+ tData()
+ : mpThreadConsumer(),
+ mbIsStreamReadOnly( false )
+ {};
+
+ tData( std::weak_ptr< SwAsyncRetrieveInputStreamThreadConsumer > pThreadConsumer )
+ : mpThreadConsumer( pThreadConsumer ),
+ mbIsStreamReadOnly( false )
+ {};
+ };
+
+ static SwRetrievedInputStreamDataManager& GetManager();
+
+ tDataKey ReserveData( std::weak_ptr< SwAsyncRetrieveInputStreamThreadConsumer > const & pThreadConsumer );
+
+ void PushData( const tDataKey nDataKey,
+ css::uno::Reference<css::io::XInputStream> const & xInputStream,
+ const bool bIsStreamReadOnly );
+
+ bool PopData( const tDataKey nDataKey,
+ tData& rData );
+
+ DECL_STATIC_LINK( SwRetrievedInputStreamDataManager, LinkedInputStreamReady, void*, void );
+
+ private:
+
+ static tDataKey snNextKeyValue;
+
+ std::mutex maMutex;
+
+ std::map< tDataKey, tData > maInputStreamData;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/retrieveinputstream.hxx b/sw/source/core/inc/retrieveinputstream.hxx
new file mode 100644
index 000000000..5f8f3df3b
--- /dev/null
+++ b/sw/source/core/inc/retrieveinputstream.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_RETRIEVEINPUTSTREAM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_RETRIEVEINPUTSTREAM_HXX
+
+#include "observablethread.hxx"
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include "retrievedinputstreamdata.hxx"
+
+/** class for a thread to retrieve an input stream given by a URL
+
+ OD 2007-01-29 #i73788#
+*/
+class SwAsyncRetrieveInputStreamThread final : public ObservableThread
+{
+ public:
+
+ static ::rtl::Reference< ObservableThread > createThread(
+ const SwRetrievedInputStreamDataManager::tDataKey nDataKey,
+ const OUString& rLinkedURL, const OUString& rReferer );
+
+ virtual ~SwAsyncRetrieveInputStreamThread() override;
+
+ private:
+
+ virtual void threadFunction() override;
+
+ SwAsyncRetrieveInputStreamThread( const SwRetrievedInputStreamDataManager::tDataKey nDataKey,
+ const OUString& rLinkedURL,
+ const OUString& rReferer );
+
+ const SwRetrievedInputStreamDataManager::tDataKey mnDataKey;
+ const OUString mrLinkedURL;
+ const OUString mrReferer;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/retrieveinputstreamconsumer.hxx b/sw/source/core/inc/retrieveinputstreamconsumer.hxx
new file mode 100644
index 000000000..ba6424e9a
--- /dev/null
+++ b/sw/source/core/inc/retrieveinputstreamconsumer.hxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_RETRIEVEINPUTSTREAMCONSUMER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_RETRIEVEINPUTSTREAMCONSUMER_HXX
+
+#include <osl/interlck.h>
+#include <rtl/ustring.hxx>
+
+namespace com::sun::star::io { class XInputStream; }
+namespace com::sun::star::uno { template <typename > class Reference; }
+
+class SwGrfNode;
+
+/** class to provide creation of a thread to retrieve an input stream given by
+ a URL and to consume the retrieved input stream.
+
+ #i73788#
+*/
+class SwAsyncRetrieveInputStreamThreadConsumer
+{
+ public:
+
+ SwAsyncRetrieveInputStreamThreadConsumer( SwGrfNode& rGrfNode );
+ ~SwAsyncRetrieveInputStreamThreadConsumer() COVERITY_NOEXCEPT_FALSE;
+
+ /** method to create thread
+ */
+ void CreateThread( const OUString& rURL, const OUString& rReferer );
+
+ /** method called to provide the retrieved input stream to the thread Consumer
+ */
+ void ApplyInputStream(
+ css::uno::Reference<css::io::XInputStream> const & xInputStream,
+ const bool bIsStreamReadOnly );
+
+ private:
+
+ SwGrfNode& mrGrfNode;
+ oslInterlockedCount mnThreadID;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/rolbck.hxx b/sw/source/core/inc/rolbck.hxx
new file mode 100644
index 000000000..d92328344
--- /dev/null
+++ b/sw/source/core/inc/rolbck.hxx
@@ -0,0 +1,440 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ROLBCK_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ROLBCK_HXX
+
+#include <o3tl/deleter.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/keycod.hxx>
+#include <tox.hxx>
+
+#include <IDocumentMarkAccess.hxx>
+
+#include <memory>
+#include <vector>
+
+namespace sfx2 {
+ class MetadatableUndo;
+}
+
+class SwDoc;
+class SwFormatColl;
+class SwTextAttr;
+class SfxPoolItem;
+class SwUndoSaveSection;
+class SwTextFootnote;
+class SwUndoDelLayFormat;
+class SwFlyFrameFormat;
+class SwFormatField;
+class SwTextField;
+class SwFieldType;
+class SwTextTOXMark;
+class SwTextRefMark;
+class SwFrameFormat;
+class SwpHints;
+class SwFormatChain;
+class SwNode;
+class SwCharFormat;
+enum class SwFieldIds : sal_uInt16;
+typedef struct _xmlTextWriter* xmlTextWriterPtr;
+
+enum HISTORY_HINT {
+ HSTRY_SETFMTHNT,
+ HSTRY_RESETFMTHNT,
+ HSTRY_SETTXTHNT,
+ HSTRY_SETTXTFLDHNT,
+ HSTRY_SETREFMARKHNT,
+ HSTRY_SETTOXMARKHNT,
+ HSTRY_RESETTXTHNT,
+ HSTRY_SETFTNHNT,
+ HSTRY_CHGFMTCOLL,
+ HSTRY_FLYCNT,
+ HSTRY_BOOKMARK,
+ HSTRY_SETATTRSET,
+ HSTRY_CHGFLYANCHOR,
+ HSTRY_CHGFLYCHAIN,
+ HSTRY_CHGCHARFMT,
+ HSTRY_NOTEXTFIELDMARK,
+ HSTRY_TEXTFIELDMARK,
+};
+
+class SwHistoryHint
+{
+ const HISTORY_HINT m_eWhichId;
+
+public:
+ SwHistoryHint( HISTORY_HINT eWhich ) : m_eWhichId( eWhich ) {}
+ virtual ~SwHistoryHint() {}
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) = 0;
+ HISTORY_HINT Which() const { return m_eWhichId; }
+ virtual OUString GetDescription() const;
+ virtual void dumpAsXml(xmlTextWriterPtr pWriter) const;
+};
+
+class SwHistorySetFormat final : public SwHistoryHint
+{
+ std::unique_ptr<SfxPoolItem> m_pAttr;
+ const SwNodeOffset m_nNodeIndex;
+
+public:
+ SwHistorySetFormat( const SfxPoolItem* pFormatHt, SwNodeOffset nNode );
+ virtual ~SwHistorySetFormat() override;
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+ virtual OUString GetDescription() const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+};
+
+class SwHistoryResetFormat final : public SwHistoryHint
+{
+ const SwNodeOffset m_nNodeIndex;
+ const sal_uInt16 m_nWhich;
+
+public:
+ SwHistoryResetFormat( const SfxPoolItem* pFormatHt, SwNodeOffset nNodeIdx );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+};
+
+class SwHistorySetText final : public SwHistoryHint
+{
+ std::unique_ptr<SfxPoolItem> m_pAttr;
+ const SwNodeOffset m_nNodeIndex;
+ const sal_Int32 m_nStart;
+ const sal_Int32 m_nEnd;
+ bool m_bFormatIgnoreStart : 1;
+ bool m_bFormatIgnoreEnd : 1;
+
+public:
+ SwHistorySetText( SwTextAttr* pTextHt, SwNodeOffset nNode );
+ virtual ~SwHistorySetText() override;
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+};
+
+class SwHistorySetTextField final : public SwHistoryHint
+{
+ //!! beware of the order for the declaration of the unique_ptrs.
+ //!! If they get destroyed in the wrong order sw may crash (namely mail-merge as well)
+ std::unique_ptr<SwFieldType> m_pFieldType;
+ const std::unique_ptr<SwFormatField> m_pField;
+
+ SwNodeOffset m_nNodeIndex;
+ sal_Int32 m_nPos;
+ SwFieldIds m_nFieldWhich;
+
+public:
+ SwHistorySetTextField( const SwTextField* pTextField, SwNodeOffset nNode );
+ virtual ~SwHistorySetTextField() override;
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+ virtual OUString GetDescription() const override;
+
+};
+
+class SwHistorySetRefMark final : public SwHistoryHint
+{
+ const OUString m_RefName;
+ const SwNodeOffset m_nNodeIndex;
+ const sal_Int32 m_nStart;
+ const sal_Int32 m_nEnd;
+
+public:
+ SwHistorySetRefMark( const SwTextRefMark* pTextHt, SwNodeOffset nNode );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+};
+
+class SwHistorySetTOXMark final : public SwHistoryHint
+{
+ SwTOXMark m_TOXMark;
+ const OUString m_TOXName;
+ const TOXTypes m_eTOXTypes;
+ const SwNodeOffset m_nNodeIndex;
+ const sal_Int32 m_nStart;
+ const sal_Int32 m_nEnd;
+
+public:
+ SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, SwNodeOffset nNode );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+ bool IsEqual( const SwTOXMark& rCmp ) const;
+
+ static SwTOXType* GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName);
+};
+
+class SwHistoryResetText final : public SwHistoryHint
+{
+ const SwNodeOffset m_nNodeIndex;
+ const sal_Int32 m_nStart;
+ const sal_Int32 m_nEnd;
+ const sal_uInt16 m_nAttr;
+
+public:
+ SwHistoryResetText( sal_uInt16 nWhich, sal_Int32 nStt, sal_Int32 nEnd,
+ SwNodeOffset nNode );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+ sal_uInt16 GetWhich() const { return m_nAttr; }
+ SwNodeOffset GetNode() const { return m_nNodeIndex; }
+ sal_Int32 GetContent() const { return m_nStart; }
+
+};
+
+class SwHistorySetFootnote final : public SwHistoryHint
+{
+ const std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> m_pUndo;
+ const OUString m_FootnoteNumber;
+ SwNodeOffset m_nNodeIndex;
+ const sal_Int32 m_nStart;
+ const bool m_bEndNote;
+
+public:
+ SwHistorySetFootnote( SwTextFootnote* pTextFootnote, SwNodeOffset nNode );
+ SwHistorySetFootnote( const SwTextFootnote& );
+ virtual ~SwHistorySetFootnote() override;
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+ virtual OUString GetDescription() const override;
+
+};
+
+class SwHistoryChangeFormatColl final : public SwHistoryHint
+{
+ SwFormatColl * const m_pColl;
+ const SwNodeOffset m_nNodeIndex;
+ const SwNodeType m_nNodeType;
+
+public:
+ SwHistoryChangeFormatColl( SwFormatColl* pColl, SwNodeOffset nNode, SwNodeType nNodeWhich );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+};
+
+class SwHistoryTextFlyCnt final : public SwHistoryHint
+{
+ std::unique_ptr<SwUndoDelLayFormat> m_pUndo;
+
+public:
+ SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat );
+ virtual ~SwHistoryTextFlyCnt() override;
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+ SwUndoDelLayFormat* GetUDelLFormat() { return m_pUndo.get(); }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const override;
+};
+
+class SwHistoryBookmark final : public SwHistoryHint
+{
+ public:
+ SwHistoryBookmark(const ::sw::mark::IMark& rBkmk,
+ bool bSavePos, bool bSaveOtherPos);
+ virtual void SetInDoc(SwDoc * pDoc, bool) override;
+
+ bool IsEqualBookmark(const ::sw::mark::IMark& rBkmk);
+ const OUString& GetName() const { return m_aName;}
+
+ private:
+ const OUString m_aName;
+ OUString m_aShortName;
+ bool m_bHidden;
+ OUString m_aHideCondition;
+ vcl::KeyCode m_aKeycode;
+ const SwNodeOffset m_nNode;
+ const SwNodeOffset m_nOtherNode;
+ const sal_Int32 m_nContent;
+ const sal_Int32 m_nOtherContent;
+ const bool m_bSavePos;
+ const bool m_bSaveOtherPos;
+ const bool m_bHadOtherPos;
+ const IDocumentMarkAccess::MarkType m_eBkmkType;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndo;
+};
+
+/// History object containing all information used during undo / redo
+/// of checkbox and drop-down form field insertion.
+class SwHistoryNoTextFieldmark final : public SwHistoryHint
+{
+ public:
+ SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark);
+ virtual void SetInDoc(SwDoc* pDoc, bool) override;
+ void ResetInDoc(SwDoc& rDoc);
+
+ private:
+ const OUString m_sType;
+ const SwNodeOffset m_nNode;
+ const sal_Int32 m_nContent;
+};
+
+/// History object containing all information used during undo / redo
+/// of text form field insertion.
+class SwHistoryTextFieldmark final : public SwHistoryHint
+{
+ public:
+ SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark);
+ virtual void SetInDoc(SwDoc* pDoc, bool) override;
+ void ResetInDoc(SwDoc& rDoc);
+
+ private:
+ const OUString m_sName;
+ const OUString m_sType;
+ const SwNodeOffset m_nStartNode;
+ const sal_Int32 m_nStartContent;
+ const SwNodeOffset m_nEndNode;
+ const sal_Int32 m_nEndContent;
+ /*const*/ SwNodeOffset m_nSepNode;
+ /*const*/ sal_Int32 m_nSepContent;
+};
+
+class SwHistorySetAttrSet final : public SwHistoryHint
+{
+ SfxItemSet m_OldSet;
+ std::vector<sal_uInt16> m_ResetArray;
+ const SwNodeOffset m_nNodeIndex;
+
+public:
+ SwHistorySetAttrSet( const SfxItemSet& rSet, SwNodeOffset nNode,
+ const o3tl::sorted_vector<sal_uInt16> &rSetArr );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+};
+
+class SwHistoryChangeFlyAnchor final : public SwHistoryHint
+{
+ SwFrameFormat & m_rFormat;
+ const SwNodeOffset m_nOldNodeIndex;
+ const sal_Int32 m_nOldContentIndex;
+
+public:
+ SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+};
+
+class SwHistoryChangeFlyChain final : public SwHistoryHint
+{
+ SwFlyFrameFormat * const m_pPrevFormat;
+ SwFlyFrameFormat * const m_pNextFormat;
+ SwFlyFrameFormat * const m_pFlyFormat;
+
+public:
+ SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat, const SwFormatChain& rAttr );
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+};
+
+class SwHistoryChangeCharFormat final : public SwHistoryHint
+{
+ const SfxItemSet m_OldSet;
+ const OUString m_Format;
+
+public:
+ SwHistoryChangeCharFormat( const SfxItemSet& rSet, const OUString & sFormat);
+ virtual void SetInDoc( SwDoc* pDoc, bool bTmpSet ) override;
+
+};
+
+class SwHistory
+{
+ friend class SwDoc; // actually only SwDoc::DelUndoObj may access
+ friend class SwRegHistory; // for inserting History attributes
+
+ std::vector<std::unique_ptr<SwHistoryHint>> m_SwpHstry;
+ sal_uInt16 m_nEndDiff;
+
+public:
+ SwHistory();
+ ~SwHistory();
+
+ // call and delete all objects between nStart and array end
+ bool Rollback( SwDoc* pDoc, sal_uInt16 nStart = 0 );
+ // call all objects between nStart and TmpEnd; store nStart as TmpEnd
+ bool TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool ToFirst = true );
+
+ void Add( const SfxPoolItem* pOldValue, const SfxPoolItem* pNewValue,
+ SwNodeOffset nNodeIdx );
+ void Add( SwTextAttr* pTextHt, SwNodeOffset nNodeIdx, bool bNewAttr );
+ void Add( SwFormatColl*, SwNodeOffset nNodeIdx, SwNodeType nWhichNd );
+ void Add( const ::sw::mark::IMark&, bool bSavePos, bool bSaveOtherPos );
+ void AddChangeFlyAnchor( SwFrameFormat& rFormat );
+ void AddDeleteFly( SwFrameFormat&, sal_uInt16& rSetPos );
+ void Add( const SwTextFootnote& );
+ void Add( const SfxItemSet & rSet, const SwCharFormat & rCharFormat);
+
+ sal_uInt16 Count() const { return m_SwpHstry.size(); }
+ sal_uInt16 GetTmpEnd() const { return m_SwpHstry.size() - m_nEndDiff; }
+ sal_uInt16 SetTmpEnd( sal_uInt16 nTmpEnd ); // return previous value
+ SwHistoryHint * operator[]( sal_uInt16 nPos ) { return m_SwpHstry[nPos].get(); }
+ SwHistoryHint const* operator[]( sal_uInt16 nPos ) const
+ { return m_SwpHstry[nPos].get(); }
+
+ // for SwUndoDelete::Undo/Redo
+ void Move( sal_uInt16 nPos, SwHistory *pIns,
+ sal_uInt16 const nStart = 0)
+ {
+ auto itSourceBegin = pIns->m_SwpHstry.begin() + nStart;
+ auto itSourceEnd = pIns->m_SwpHstry.end();
+ if (itSourceBegin == itSourceEnd)
+ return;
+ m_SwpHstry.insert(m_SwpHstry.begin() + nPos, std::make_move_iterator(itSourceBegin), std::make_move_iterator(itSourceEnd));
+ pIns->m_SwpHstry.erase( itSourceBegin, itSourceEnd );
+ }
+
+ // helper methods for recording attribute in History
+ // used by Undo classes (Delete/Overwrite/Inserts)
+ void CopyAttr(
+ SwpHints const * pHts,
+ const SwNodeOffset nNodeIdx,
+ const sal_Int32 nStart,
+ const sal_Int32 nEnd,
+ const bool bCopyFields );
+
+ void CopyFormatAttr( const SfxItemSet& rSet, SwNodeOffset nNodeIdx );
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+};
+
+class SwRegHistory final: public SwClient
+{
+private:
+ o3tl::sorted_vector<sal_uInt16> m_WhichIdSet;
+ SwHistory * const m_pHistory;
+ SwNodeOffset m_nNodeIndex;
+
+ void MakeSetWhichIds();
+
+public:
+ SwRegHistory( SwHistory* pHst );
+ SwRegHistory( const SwNode& rNd, SwHistory* pHst );
+ SwRegHistory( sw::BroadcastingModify* pRegIn, const SwNode& rNd, SwHistory* pHst );
+ virtual void SwClientNotify(const SwModify&, const SfxHint& rHint) override;
+
+ /// @return true if at least 1 item was inserted
+ bool InsertItems( const SfxItemSet& rSet,
+ sal_Int32 const nStart, sal_Int32 const nEnd,
+ SetAttrMode const nFlags,
+ SwTextAttr **ppNewTextAttr );
+
+ void AddHint( SwTextAttr* pHt, const bool bNew );
+
+ void RegisterInModify( sw::BroadcastingModify* pRegIn, const SwNode& rNd );
+ void ChangeNodeIndex( SwNodeOffset nNew ) { m_nNodeIndex = nNew; }
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_ROLBCK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/rootfrm.hxx b/sw/source/core/inc/rootfrm.hxx
new file mode 100644
index 000000000..3d00fe1cc
--- /dev/null
+++ b/sw/source/core/inc/rootfrm.hxx
@@ -0,0 +1,472 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ROOTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ROOTFRM_HXX
+
+#include "layfrm.hxx"
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <IDocumentTimerAccess.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <set>
+#include <vector>
+
+class SwContentFrame;
+class SdrPage;
+class SwFrameFormat;
+class SwPaM;
+class SwCursor;
+class SwShellCursor;
+class SwTableCursor;
+class SwLayVout;
+class SwViewOption;
+class SwSelectionList;
+struct SwPosition;
+struct SwCursorMoveState;
+
+namespace sw {
+ enum class RedlineMode
+ {
+ Shown, Hidden
+ };
+
+ enum class FieldmarkMode { ShowCommand = 1, ShowResult = 2, ShowBoth = 3 };
+};
+
+enum class SwInvalidateFlags
+{
+ Size = 0x01,
+ PrtArea = 0x02,
+ Pos = 0x04,
+ Table = 0x08,
+ Section = 0x10,
+ LineNum = 0x20,
+ Direction = 0x40,
+};
+
+namespace o3tl
+{
+ template<> struct typed_flags<SwInvalidateFlags> : is_typed_flags<SwInvalidateFlags, 0x7f> {};
+};
+
+enum class SwRemoveResult
+{
+ Next,
+ Prev
+};
+
+using SwCurrShells = std::set<CurrShell*>;
+
+class SwSectionFrame;
+using SwDestroyList = o3tl::sorted_vector<SwSectionFrame*>;
+
+/// The root element of a Writer document layout. Lower frames are expected to
+/// be SwPageFrame instances.
+class SW_DLLPUBLIC SwRootFrame final : public SwLayoutFrame
+{
+ // Needs to disable the Superfluous temporarily
+ friend void AdjustSizeChgNotify( SwRootFrame *pRoot );
+
+ // Maintains the mpLastPage (Cut() and Paste() of SwPageFrame
+ friend inline void SetLastPage( SwPageFrame* );
+
+ // For creating and destroying of the virtual output device manager
+ friend void FrameInit(); // Creates s_pVout
+ friend void FrameFinit(); // Destroys s_pVout
+
+ std::vector<SwRect> maPageRects;// returns the current rectangle for each page frame
+ // the rectangle is extended to the top/bottom/left/right
+ // for pages located at the outer margins
+ SwRect maPagesArea; // the area covered by the pages
+ tools::Long mnViewWidth; // the current page layout bases on this view width
+ sal_uInt16 mnColumns; // the current page layout bases on this number of columns
+ bool mbBookMode; // the current page layout is in book view
+ bool mbSidebarChanged; // the notes sidebar state has changed
+
+ bool mbNeedGrammarCheck; // true when sth needs to be checked (not necessarily started yet!)
+
+ static SwLayVout *s_pVout;
+ static bool s_isInPaint; // Protection against double Paints
+ static bool s_isNoVirDev;// No virt. Device for SystemPaints
+
+ /// Width of the HTML / Web document if not defined otherwise: 20cm.
+ static constexpr sal_Int64 MIN_BROWSE_WIDTH = o3tl::toTwips(20000, o3tl::Length::mm100);
+
+ bool mbCheckSuperfluous :1; // Search for empty Pages?
+ bool mbIdleFormat :1; // Trigger Idle Formatter?
+ bool mbBrowseWidthValid :1; // Is mnBrowseWidth valid?
+ bool mbTurboAllowed :1;
+ bool mbAssertFlyPages :1; // Insert more Pages for Flys if needed?
+ bool mbTableUpdateInProgress : 1; // tdf#139426 to allow suppression of AssertFlyPages during TableUpdate
+ bool mbIsVirtPageNum :1; // Do we have a virtual pagenumber?
+ bool mbIsNewLayout :1; // Layout loaded or newly created
+ bool mbCallbackActionEnabled:1; // No Action in Notification desired
+ // @see dcontact.cxx, ::Changed()
+ bool mbLayoutFreezed;
+ bool mbHideRedlines;
+ sw::FieldmarkMode m_FieldmarkMode;
+
+ /**
+ * For BrowseMode
+ * mnBrowseWidth is the outer margin of the object most to the right.
+ * The page's right edge should not be smaller than this value.
+ */
+ tools::Long mnBrowseWidth;
+
+ /// If we only have to format one ContentFrame, its in mpTurbo
+ const SwContentFrame *mpTurbo;
+
+ /// We should not need to always struggle to find the last page, so store it here
+ SwPageFrame *mpLastPage;
+
+ /** [ Comment from the original StarOffice checkin ]:
+ * The root takes care of the shell access. Via the document
+ * it should be possible to get at the root frame, and thus always
+ * have access to the shell.
+ * the pointer mpCurrShell is the pointer to any of the shells for
+ * the document.
+ * Because sometimes it matters which shell is used, it is necessary to
+ * know the active shell.
+ * this is approximated by setting the pointer mpCurrShell when a
+ * shell gets the focus (FEShell). Additionally the pointer will be
+ * set temporarily by SwCurrShell typically via CurrShell
+ * The class can be found in the SwViewShell. These object can
+ * be created nested (also for different kinds of Shells). They are
+ * collected into the Array mpCurrShells.
+ * Furthermore it can happen that a shell is activated while a curshell
+ * object is still 'active'. This one will be entered into mpWaitingCurrShell
+ * and will be activated by the last d'tor of CurrShell.
+ * One other problem is the destruction of a shell while it is active.
+ * The pointer mpCurrShell is then reset to an arbitrary other shell.
+ * If at the time of the destruction of a shell, which is still referenced
+ * by a curshell object, that will be cleaned up as well.
+ */
+ friend class CurrShell;
+ friend void SetShell( SwViewShell *pSh );
+ friend void InitCurrShells( SwRootFrame *pRoot );
+ SwViewShell *mpCurrShell;
+ SwViewShell *mpWaitingCurrShell;
+ std::unique_ptr<SwCurrShells> mpCurrShells;
+
+ /// One Page per DrawModel per Document; is always the size of the Root
+ SdrPage *mpDrawPage;
+
+ std::unique_ptr<SwDestroyList> mpDestroy;
+
+ sal_uInt16 mnPhyPageNums; /// Page count
+ sal_uInt16 mnAccessibleShells; // Number of accessible shells
+
+ void ImplCalcBrowseWidth();
+ void ImplInvalidateBrowseWidth();
+
+ void DeleteEmptySct_(); // Destroys the registered SectionFrames
+ void RemoveFromList_( SwSectionFrame* pSct ); // Removes SectionFrames from the Delete List
+
+ virtual void DestroyImpl() override;
+ virtual ~SwRootFrame() override;
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+
+public:
+
+ /// Remove MasterObjects from the Page (called by the ctors)
+ static void RemoveMasterObjs( SdrPage *pPg );
+
+ void AllCheckPageDescs() const;
+ void AllInvalidateAutoCompleteWords() const;
+ void AllAddPaintRect() const;
+ void AllRemoveFootnotes() ;
+ void AllInvalidateSmartTagsOrSpelling(bool bSmartTags) const;
+
+ /// Output virtual Device (e.g. for animations)
+ static bool FlushVout();
+
+ /// Save Clipping if exactly the ClipRect is outputted
+ static bool HasSameRect( const SwRect& rRect );
+
+ SwRootFrame( SwFrameFormat*, SwViewShell* );
+ void Init(SwFrameFormat*);
+
+ SwViewShell *GetCurrShell() const { return mpCurrShell; }
+ void DeRegisterShell( SwViewShell *pSh );
+
+ /**
+ * Set up Start-/EndAction for all Shells on an as high as possible
+ * (Shell section) level.
+ * For the StarONE binding, which does not know the Shells directly.
+ * The ChangeLinkd of the CursorShell (UI notifications) is called
+ * automatically in the EndAllAction.
+ */
+ void StartAllAction();
+ void EndAllAction( bool bVirDev = false );
+
+ /**
+ * Certain UNO Actions (e.g. table cursor) require that all Actions are reset temporarily
+ * In order for that to work, every SwViewShell needs to remember its old Action counter
+ */
+ void UnoRemoveAllActions();
+ void UnoRestoreAllActions();
+
+ const SdrPage* GetDrawPage() const { return mpDrawPage; }
+ SdrPage* GetDrawPage() { return mpDrawPage; }
+ void SetDrawPage( SdrPage* pNew ){ mpDrawPage = pNew; }
+
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false ) const override;
+
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ virtual SwTwips ShrinkFrame( SwTwips, bool bTst = false, bool bInfo = false ) override;
+ virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) override;
+#ifdef DBG_UTIL
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+#endif
+
+ virtual bool FillSelection( SwSelectionList& rList, const SwRect& rRect ) const override;
+
+ Point GetNextPrevContentPos( const Point &rPoint, bool bNext ) const;
+
+ virtual Size ChgSize( const Size& aNewSize ) override;
+
+ void SetIdleFlags()
+ {
+ mbIdleFormat = true;
+
+ SwViewShell* pCurrShell = GetCurrShell();
+ // May be NULL if called from SfxBaseModel::dispose
+ // (this happens in the build test 'rtfexport').
+ if (pCurrShell != nullptr)
+ pCurrShell->GetDoc()->getIDocumentTimerAccess().StartIdling();
+ }
+ bool IsIdleFormat() const { return mbIdleFormat; }
+ void ResetIdleFormat() { mbIdleFormat = false; }
+
+ bool IsNeedGrammarCheck() const { return mbNeedGrammarCheck; }
+ void SetNeedGrammarCheck( bool bVal )
+ {
+ mbNeedGrammarCheck = bVal;
+
+ if ( bVal )
+ {
+ SwViewShell* pCurrShell = GetCurrShell();
+ // May be NULL if called from SfxBaseModel::dispose
+ // (this happens in the build test 'rtfexport').
+ if (pCurrShell != nullptr)
+ pCurrShell->GetDoc()->getIDocumentTimerAccess().StartIdling();
+ }
+ }
+
+ /// Makes sure that all requested page-bound Flys find a Page
+ void SetAssertFlyPages() { mbAssertFlyPages = true; }
+ void AssertFlyPages();
+ bool IsAssertFlyPages() const { return mbAssertFlyPages; }
+
+ void SetTableUpdateInProgress(bool bValue) { mbTableUpdateInProgress = bValue; }
+ bool IsTableUpdateInProgress() const { return mbTableUpdateInProgress; }
+
+ /**
+ * Makes sure that, starting from the passed Page, all page-bound Frames
+ * are on the right Page (pagenumber).
+ */
+ static void AssertPageFlys( SwPageFrame * );
+
+ /// Invalidate all Content, Size or PrtArea
+ void InvalidateAllContent( SwInvalidateFlags nInvalidate );
+
+ /**
+ * Invalidate/re-calculate the position of all floating
+ * screen objects (Writer fly frames and drawing objects), which are
+ * anchored to paragraph or to character.
+ */
+ void InvalidateAllObjPos();
+
+ /// Remove superfluous Pages
+ void SetSuperfluous() { mbCheckSuperfluous = true; }
+ bool IsSuperfluous() const { return mbCheckSuperfluous; }
+ void RemoveSuperfluous();
+
+ /**
+ * Query/set the current Page and the collective Page count
+ * We'll format as much as necessary
+ */
+ sal_uInt16 GetCurrPage( const SwPaM* ) const;
+ sal_uInt16 SetCurrPage( SwCursor*, sal_uInt16 nPageNum );
+ Point GetPagePos( sal_uInt16 nPageNum ) const;
+ sal_uInt16 GetPageNum() const { return mnPhyPageNums; }
+ void DecrPhyPageNums() { --mnPhyPageNums; }
+ void IncrPhyPageNums() { ++mnPhyPageNums; }
+ bool IsVirtPageNum() const { return mbIsVirtPageNum; }
+ inline void SetVirtPageNum( const bool bOf ) const;
+ bool IsDummyPage( sal_uInt16 nPageNum ) const;
+
+ /**
+ * Point rPt: The point that should be used to find the page
+ * Size pSize: If given, we return the (first) page that overlaps with the
+ * rectangle defined by rPt and pSize
+ * bool bExtend: Extend each page to the left/right/top/bottom up to the
+ * next page margin
+ */
+ const SwPageFrame* GetPageAtPos( const Point& rPt, const Size* pSize = nullptr, bool bExtend = false ) const;
+
+ /**
+ * Point rPt: The point to test
+ * @returns true: if rPt is between top/bottom margins of two pages
+ * in hide-whitespace, rPt can be near the gap, but
+ * not strictly between pages (in a page) as gap is small.
+ * @returns false: if rPt is in a page or not strictly between two pages
+ */
+ bool IsBetweenPages(const Point& rPt) const;
+
+ void CalcFrameRects( SwShellCursor& );
+
+ /**
+ * Calculates the cells included from the current selection
+ *
+ * @returns false: There was no result because of an invalid layout
+ * @returns true: Everything worked fine.
+ */
+ bool MakeTableCursors( SwTableCursor& );
+
+ void DisallowTurbo() const { const_cast<SwRootFrame*>(this)->mbTurboAllowed = false; }
+ void ResetTurboFlag() const { const_cast<SwRootFrame*>(this)->mbTurboAllowed = true; }
+ bool IsTurboAllowed() const { return mbTurboAllowed; }
+ void SetTurbo( const SwContentFrame *pContent ) { mpTurbo = pContent; }
+ void ResetTurbo() { mpTurbo = nullptr; }
+ const SwContentFrame *GetTurbo() const { return mpTurbo; }
+
+ /// Update the footnote numbers of all Pages
+ void UpdateFootnoteNums(); // Only for page by page numbering!
+
+ /// Remove all footnotes (but no references)
+ void RemoveFootnotes( SwPageFrame *pPage = nullptr, bool bPageOnly = false,
+ bool bEndNotes = false );
+ void CheckFootnotePageDescs( bool bEndNote );
+
+ const SwPageFrame *GetLastPage() const { return mpLastPage; }
+ SwPageFrame *GetLastPage() { return mpLastPage; }
+
+ static bool IsInPaint() { return s_isInPaint; }
+
+ static void SetNoVirDev(const bool bNew) { s_isNoVirDev = bNew; }
+
+ inline tools::Long GetBrowseWidth() const;
+ inline void InvalidateBrowseWidth();
+
+ bool IsNewLayout() const { return mbIsNewLayout; }
+ void ResetNewLayout() { mbIsNewLayout = false;}
+
+ /**
+ * Empty SwSectionFrames are registered here for deletion and
+ * destroyed later on or deregistered.
+ */
+ void InsertEmptySct( SwSectionFrame* pDel );
+ void DeleteEmptySct() { if( mpDestroy ) DeleteEmptySct_(); }
+ void RemoveFromList( SwSectionFrame* pSct ) { if( mpDestroy ) RemoveFromList_( pSct ); }
+#ifdef DBG_UTIL
+ bool IsInDelList( SwSectionFrame* pSct ) const;
+#endif
+
+ void SetCallbackActionEnabled( bool b ) { mbCallbackActionEnabled = b; }
+ bool IsCallbackActionEnabled() const { return mbCallbackActionEnabled; }
+
+ bool IsAnyShellAccessible() const { return mnAccessibleShells > 0; }
+ void AddAccessibleShell() { ++mnAccessibleShells; }
+ void RemoveAccessibleShell() { --mnAccessibleShells; }
+
+ /**
+ * Get page frame by physical page number
+ * looping through the lowers, which are page frame, in order to find the
+ * page frame with the given physical page number.
+ * if no page frame is found, 0 is returned.
+ * Note: Empty page frames are also returned.
+ *
+ * @param _nPageNum: physical page number of page frame to be searched and
+ * returned.
+ *
+ * @return pointer to the page frame with the given physical page number
+ */
+ SwPageFrame* GetPageByPageNum( sal_uInt16 _nPageNum ) const;
+
+ void CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea );
+ bool IsLeftToRightViewLayout() const;
+ const SwRect& GetPagesArea() const { return maPagesArea; }
+ void SetSidebarChanged() { mbSidebarChanged = true; }
+
+ bool IsLayoutFreezed() const { return mbLayoutFreezed; }
+ void FreezeLayout( bool freeze ) { mbLayoutFreezed = freeze; }
+
+ void RemovePage( SwPageFrame **pDel, SwRemoveResult eResult );
+
+ /**
+ * Replacement for sw::DocumentRedlineManager::GetRedlineFlags()
+ * (this is layout-level redline hiding).
+ */
+ bool IsHideRedlines() const { return mbHideRedlines; }
+ void SetHideRedlines(bool);
+ sw::FieldmarkMode GetFieldmarkMode() const { return m_FieldmarkMode; }
+ void SetFieldmarkMode(sw::FieldmarkMode);
+ bool HasMergedParas() const {
+ return IsHideRedlines() || GetFieldmarkMode() != sw::FieldmarkMode::ShowBoth;
+ }
+};
+
+inline tools::Long SwRootFrame::GetBrowseWidth() const
+{
+ if ( !mbBrowseWidthValid )
+ const_cast<SwRootFrame*>(this)->ImplCalcBrowseWidth();
+ return mnBrowseWidth;
+}
+
+inline void SwRootFrame::InvalidateBrowseWidth()
+{
+ if ( mbBrowseWidthValid )
+ ImplInvalidateBrowseWidth();
+}
+
+inline void SwRootFrame::SetVirtPageNum( const bool bOf) const
+{
+ const_cast<SwRootFrame*>(this)->mbIsVirtPageNum = bOf;
+}
+
+/// helper class to disable creation of an action by a callback event
+/// in particular, change event from a drawing object (SwDrawContact::Changed())
+class DisableCallbackAction
+{
+ private:
+ SwRootFrame & m_rRootFrame;
+ bool m_bOldCallbackActionState;
+
+ public:
+ explicit DisableCallbackAction(SwRootFrame & rRootFrame)
+ : m_rRootFrame(rRootFrame)
+ , m_bOldCallbackActionState(rRootFrame.IsCallbackActionEnabled())
+ {
+ m_rRootFrame.SetCallbackActionEnabled(false);
+ }
+
+ ~DisableCallbackAction()
+ {
+ m_rRootFrame.SetCallbackActionEnabled(m_bOldCallbackActionState);
+ }
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_ROOTFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/rowfrm.hxx b/sw/source/core/inc/rowfrm.hxx
new file mode 100644
index 000000000..ebaae2e1d
--- /dev/null
+++ b/sw/source/core/inc/rowfrm.hxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_ROWFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_ROWFRM_HXX
+
+#include "layfrm.hxx"
+
+class SwTableLine;
+class SwBorderAttrs;
+
+/// SwRowFrame is one table row in the document layout.
+class SwRowFrame final : public SwLayoutFrame
+{
+ virtual void Format(vcl::RenderContext* pRenderContext,
+ const SwBorderAttrs* pAttrs = nullptr) override;
+ /// Only change the Frame size, not the PrtArea SSize
+ virtual SwTwips ShrinkFrame(SwTwips, bool bTst = false, bool bInfo = false) override;
+ virtual SwTwips GrowFrame(SwTwips, bool bTst = false, bool bInfo = false) override;
+
+ const SwTableLine* m_pTabLine;
+ SwRowFrame* m_pFollowRow; ///< note: this is *only* set on old-style tables!
+ // #i29550#
+ sal_uInt16 mnTopMarginForLowers;
+ sal_uInt16 mnBottomMarginForLowers;
+ sal_uInt16 mnBottomLineSize;
+ // <-- collapsing
+ bool m_bIsFollowFlowRow; ///< note: this is *only* set on old-style tables!
+ bool m_bIsRepeatedHeadline;
+ bool m_bIsRowSpanLine;
+
+ bool m_bForceRowSplitAllowed;
+ bool m_bIsInSplit;
+
+ virtual void DestroyImpl() override;
+ virtual ~SwRowFrame() override;
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+ virtual const SwRowFrame* DynCastRowFrame() const override { return this; }
+
+public:
+ SwRowFrame(const SwTableLine&, SwFrame*, bool bInsertContent = true);
+
+ virtual void Cut() override;
+
+ /**
+ * Register Flys after a line was created _AND_ inserted
+ * Must be called by the creator; the Fly is inserted _after_ it has
+ * been created; the same holds true for the Page at which the Flys
+ * are to be registered at.
+ */
+ void RegistFlys(SwPageFrame* pPage = nullptr);
+
+ const SwTableLine* GetTabLine() const { return m_pTabLine; }
+
+ /**
+ * Adapts the Cells to the current height; invalidates the Cells if
+ * the Direction does not match the height
+ */
+ void AdjustCells(const SwTwips nHeight, const bool bHeight);
+
+ SwRowFrame* GetFollowRow() const { return m_pFollowRow; }
+ void SetFollowRow(SwRowFrame* pNew) { m_pFollowRow = pNew; }
+
+ // #i29550#
+ sal_uInt16 GetTopMarginForLowers() const { return mnTopMarginForLowers; }
+ void SetTopMarginForLowers(sal_uInt16 nNew) { mnTopMarginForLowers = nNew; }
+ sal_uInt16 GetBottomMarginForLowers() const { return mnBottomMarginForLowers; }
+ void SetBottomMarginForLowers(sal_uInt16 nNew) { mnBottomMarginForLowers = nNew; }
+ sal_uInt16 GetBottomLineSize() const { return mnBottomLineSize; }
+ void SetBottomLineSize(sal_uInt16 nNew) { mnBottomLineSize = nNew; }
+ // <-- collapsing
+
+ bool IsRepeatedHeadline() const { return m_bIsRepeatedHeadline; }
+ void SetRepeatedHeadline(bool bNew) { m_bIsRepeatedHeadline = bNew; }
+
+ // --> split table rows
+ bool IsRowSplitAllowed() const;
+ bool IsForceRowSplitAllowed() const { return m_bForceRowSplitAllowed; }
+ void SetForceRowSplitAllowed(bool bNew) { m_bForceRowSplitAllowed = bNew; };
+ bool IsFollowFlowRow() const { return m_bIsFollowFlowRow; }
+ void SetFollowFlowRow(bool bNew) { m_bIsFollowFlowRow = bNew; }
+ // <-- split table rows
+
+ // #131283# Table row keep feature
+ bool ShouldRowKeepWithNext(const bool bCheckParents = true) const;
+
+ // #i4032# NEW TABLES
+ bool IsRowSpanLine() const { return m_bIsRowSpanLine; }
+ void SetRowSpanLine(bool bNew) { m_bIsRowSpanLine = bNew; }
+
+ // A row may only be split if the minimum height of the row frame
+ // fits into the vertical space left.
+ // The minimum height is found as maximum of two values: minimal
+ // contents of the row (e.g., height of first line of text, or an
+ // object, or lower table cell), and the minimum height setting.
+ // As the minimum height setting should not prevent the row to
+ // flow, (it only should ensure that *total* height is no less), we
+ // should not consider the setting when the split is performed
+ // (we should be able to keep on first page as little as required).
+ // When IsInSplit is true, lcl_CalcMinRowHeight will ignore the
+ // minimum height setting. It is set in lcl_RecalcSplitLine around
+ // lcl_RecalcRow and SwRowFrame::Calc that decide if it's possible
+ // to keep part of row's content on first page, and update table's
+ // height to fit the rest of space.
+ bool IsInSplit() const { return m_bIsInSplit; }
+ void SetInSplit(bool bNew = true) { m_bIsInSplit = bNew; }
+ void OnFrameSize(const SfxPoolItem&);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/scriptinfo.hxx b/sw/source/core/inc/scriptinfo.hxx
new file mode 100644
index 000000000..cfe9ef3e5
--- /dev/null
+++ b/sw/source/core/inc/scriptinfo.hxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SCRIPTINFO_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SCRIPTINFO_HXX
+
+#include <vector>
+#include <deque>
+#include <unordered_set>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <i18nlangtag/lang.h>
+#include <tools/long.hxx>
+#include "TextFrameIndex.hxx"
+
+class SwTextNode;
+class SwTextFrame;
+class Point;
+class MultiSelection;
+enum class SwFontScript;
+namespace sw { struct MergedPara; }
+namespace sw::mark { class IBookmark; }
+
+#define SPACING_PRECISION_FACTOR 100
+
+// encapsulates information about script changes
+class SwScriptInfo
+{
+public:
+ enum CompType { KANA, SPECIAL_LEFT, SPECIAL_RIGHT, NONE, SPECIAL_MIDDLE};
+ enum class MarkKind { Start = (1<<0), End = (1<<1), Point = (1<<2) };
+
+private:
+ //! Records a single change in script type.
+ struct ScriptChangeInfo
+ {
+ TextFrameIndex position; //!< Character position at which we change script
+ sal_uInt8 type; //!< Script type (Latin/Asian/Complex) that we change to.
+ ScriptChangeInfo(TextFrameIndex pos, sal_uInt8 typ) : position(pos), type(typ) {};
+ };
+ //TODO - This is sorted, so should probably be a std::set rather than vector.
+ // But we also use random access (probably unnecessarily).
+ std::vector<ScriptChangeInfo> m_ScriptChanges;
+ //! Records a single change in direction.
+ struct DirectionChangeInfo
+ {
+ TextFrameIndex position; //!< Character position at which we change direction.
+ sal_uInt8 type; //!< Direction that we change to.
+ DirectionChangeInfo(TextFrameIndex pos, sal_uInt8 typ) : position(pos), type(typ) {};
+ };
+ std::vector<DirectionChangeInfo> m_DirectionChanges;
+ std::deque<TextFrameIndex> m_Kashida;
+ /// indexes into m_Kashida
+ std::unordered_set<size_t> m_KashidaInvalid;
+ std::deque<TextFrameIndex> m_NoKashidaLine;
+ std::deque<TextFrameIndex> m_NoKashidaLineEnd;
+ std::vector<TextFrameIndex> m_HiddenChg;
+ std::vector<std::pair<TextFrameIndex, MarkKind>> m_Bookmarks;
+ //! Records a single change in compression.
+ struct CompressionChangeInfo
+ {
+ TextFrameIndex position; //!< Character position where the change occurs.
+ TextFrameIndex length; //!< Length of the segment.
+ CompType type; //!< Type of compression that we change to.
+ CompressionChangeInfo(TextFrameIndex pos, TextFrameIndex len, CompType typ) : position(pos), length(len), type(typ) {};
+ };
+ std::vector<CompressionChangeInfo> m_CompressionChanges;
+#ifdef DBG_UTIL
+ CompType DbgCompType(const TextFrameIndex nPos) const;
+#endif
+
+ TextFrameIndex m_nInvalidityPos;
+ sal_uInt8 m_nDefaultDir;
+
+ void UpdateBidiInfo( const OUString& rText );
+ bool IsKashidaValid(size_t nKashPos) const;
+ // returns true if nKashPos is newly marked invalid
+ bool MarkKashidaInvalid(size_t nKashPos);
+ void ClearKashidaInvalid(size_t nKashPos);
+ bool MarkOrClearKashidaInvalid(TextFrameIndex nStt, TextFrameIndex nLen,
+ bool bMark, sal_Int32 nMarkCount);
+ bool IsKashidaLine(TextFrameIndex nCharIdx) const;
+ // examines the range [ nStart, nStart + nEnd ] if there are kanas
+ // returns start index of kana entry in array, otherwise SAL_MAX_SIZE
+ size_t HasKana(TextFrameIndex nStart, TextFrameIndex nEnd) const;
+
+public:
+
+ SwScriptInfo();
+ ~SwScriptInfo();
+
+ // determines script changes
+ void InitScriptInfo(const SwTextNode& rNode, sw::MergedPara const* pMerged, bool bRTL);
+ void InitScriptInfo(const SwTextNode& rNode, sw::MergedPara const* pMerged);
+
+ // set/get position from which data is invalid
+ void SetInvalidityA(const TextFrameIndex nPos)
+ {
+ if (nPos < m_nInvalidityPos)
+ m_nInvalidityPos = nPos;
+ }
+ TextFrameIndex GetInvalidityA() const
+ {
+ return m_nInvalidityPos;
+ }
+
+ // get default direction for paragraph
+ sal_uInt8 GetDefaultDir() const { return m_nDefaultDir; };
+
+ // array operations, nCnt refers to array position
+ size_t CountScriptChg() const { return m_ScriptChanges.size(); }
+ TextFrameIndex GetScriptChg(const size_t nCnt) const
+ {
+ assert(nCnt < m_ScriptChanges.size());
+ return m_ScriptChanges[nCnt].position;
+ }
+ sal_uInt8 GetScriptType( const size_t nCnt ) const
+ {
+ assert( nCnt < m_ScriptChanges.size());
+ return m_ScriptChanges[nCnt].type;
+ }
+
+ size_t CountDirChg() const { return m_DirectionChanges.size(); }
+ TextFrameIndex GetDirChg(const size_t nCnt) const
+ {
+ assert(nCnt < m_DirectionChanges.size());
+ return m_DirectionChanges[ nCnt ].position;
+ }
+ sal_uInt8 GetDirType( const size_t nCnt ) const
+ {
+ assert(nCnt < m_DirectionChanges.size());
+ return m_DirectionChanges[ nCnt ].type;
+ }
+
+ size_t CountKashida() const
+ {
+ return m_Kashida.size();
+ }
+
+ TextFrameIndex GetKashida(const size_t nCnt) const
+ {
+ assert(nCnt < m_Kashida.size());
+ return m_Kashida[nCnt];
+ }
+
+ size_t CountCompChg() const { return m_CompressionChanges.size(); };
+ TextFrameIndex GetCompStart(const size_t nCnt) const
+ {
+ assert(nCnt < m_CompressionChanges.size());
+ return m_CompressionChanges[ nCnt ].position;
+ }
+ TextFrameIndex GetCompLen(const size_t nCnt) const
+ {
+ assert(nCnt < m_CompressionChanges.size());
+ return m_CompressionChanges[ nCnt ].length;
+ }
+ CompType GetCompType( const size_t nCnt ) const
+ {
+ assert(nCnt < m_CompressionChanges.size());
+ return m_CompressionChanges[ nCnt ].type;
+ }
+
+ size_t CountHiddenChg() const { return m_HiddenChg.size(); };
+ TextFrameIndex GetHiddenChg(const size_t nCnt) const
+ {
+ assert(nCnt < m_HiddenChg.size());
+ return m_HiddenChg[ nCnt ];
+ }
+ TextFrameIndex NextHiddenChg(TextFrameIndex nPos) const;
+ TextFrameIndex NextBookmark(TextFrameIndex nPos) const;
+ MarkKind GetBookmark(TextFrameIndex nPos) const;
+ static void CalcHiddenRanges(const SwTextNode& rNode,
+ MultiSelection& rHiddenMulti,
+ std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> * pBookmarks);
+ static void selectHiddenTextProperty(const SwTextNode& rNode,
+ MultiSelection &rHiddenMulti,
+ std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> * pBookmarks);
+ static void selectRedLineDeleted(const SwTextNode& rNode, MultiSelection &rHiddenMulti, bool bSelect=true);
+
+ // "high" level operations, nPos refers to string position
+ TextFrameIndex NextScriptChg(TextFrameIndex nPos) const;
+ sal_Int16 ScriptType(const TextFrameIndex nPos) const;
+
+ // Returns the position of the next direction level change.
+ // If bLevel is set, the position of the next level which is smaller
+ // than the level at position nPos is returned. This is required to
+ // obtain the end of a SwBidiPortion
+ TextFrameIndex NextDirChg(const TextFrameIndex nPos,
+ const sal_uInt8* pLevel = nullptr) const;
+ sal_uInt8 DirType(const TextFrameIndex nPos) const;
+
+ // HIDDEN TEXT STUFF START
+
+/** Hidden text range information - static and non-version
+
+ @descr Determines if a given position is inside a hidden text range. The
+ static version tries to obtain a valid SwScriptInfo object
+ via the SwTextNode, otherwise it calculates the values from scratch.
+ The non-static version uses the internally cached information
+ for the calculation.
+
+ @param rNode
+ The text node.
+ @param nPos
+ The given position that should be checked.
+ @param rnStartPos
+ Return parameter for the start position of the hidden range.
+ COMPLETE_STRING if nPos is not inside a hidden range.
+ @param rnEndPos
+ Return parameter for the end position of the hidden range.
+ 0 if nPos is not inside a hidden range.
+ @param rnEndPos
+ Return parameter that contains all the hidden text ranges. Optional.
+ @return
+ returns true if there are any hidden characters in this paragraph.
+
+*/
+ static bool GetBoundsOfHiddenRange( const SwTextNode& rNode, sal_Int32 nPos,
+ sal_Int32& rnStartPos, sal_Int32& rnEndPos,
+ std::vector<sal_Int32>* pList = nullptr );
+ bool GetBoundsOfHiddenRange(TextFrameIndex nPos, TextFrameIndex & rnStartPos,
+ TextFrameIndex & rnEndPos) const;
+
+ static bool IsInHiddenRange( const SwTextNode& rNode, sal_Int32 nPos );
+
+/** Hidden text attribute handling
+
+ @descr Takes a string and either deletes the hidden ranges or sets
+ a given character in place of the hidden characters.
+
+ @param rNode
+ The text node.
+ @param rText
+ The string to modify.
+ @param cChar
+ The character that should replace the hidden characters.
+ @param bDel
+ If set, the hidden ranges will be deleted from the text node.
+ */
+ static sal_Int32 MaskHiddenRanges(
+ const SwTextNode& rNode, OUStringBuffer& rText,
+ const sal_Int32 nStt, const sal_Int32 nEnd,
+ const sal_Unicode cChar );
+
+/** Hidden text attribute handling
+
+ @descr Takes a SwTextNode and deletes the hidden ranges from the node.
+
+ @param rNode
+ The text node.
+ */
+ static void DeleteHiddenRanges( SwTextNode& rNode );
+
+ // HIDDEN TEXT STUFF END
+
+ // modifies the kerning array according to a given compress value
+ tools::Long Compress( sal_Int32* pKernArray, TextFrameIndex nIdx, TextFrameIndex nLen,
+ const sal_uInt16 nCompress, const sal_uInt16 nFontHeight,
+ const bool bCentered,
+ Point* pPoint = nullptr ) const;
+
+/** Performs a kashida justification on the kerning array
+
+ @descr Add some extra space for kashida justification to the
+ positions in the kerning array.
+ @param pKernArray
+ The printers kerning array. Optional.
+ @param nStt
+ Start referring to the paragraph.
+ @param nLen
+ The number of characters to be considered.
+ @param nSpaceAdd
+ The value which has to be added to a kashida opportunity.
+ @return The number of kashida opportunities in the given range
+*/
+ sal_Int32 KashidaJustify( sal_Int32* pKernArray,
+ TextFrameIndex nStt, TextFrameIndex nLen, tools::Long nSpaceAdd = 0) const;
+
+/** Clears array of kashidas marked as invalid
+ */
+ void ClearKashidaInvalid(TextFrameIndex const nStt, TextFrameIndex const nLen)
+ {
+ MarkOrClearKashidaInvalid(nStt, nLen, false, 0);
+ }
+
+/** Marks nCnt kashida positions as invalid
+ pKashidaPositions: array of char indices relative to the paragraph
+*/
+ void MarkKashidasInvalid(sal_Int32 nCnt, const TextFrameIndex* pKashidaPositions);
+
+/** Marks nCnt kashida positions as invalid
+ in the given text range
+ */
+ bool MarkKashidasInvalid(sal_Int32 const nCnt,
+ TextFrameIndex const nStt, TextFrameIndex const nLen)
+ {
+ return MarkOrClearKashidaInvalid(nStt, nLen, true, nCnt);
+ }
+
+/** retrieves kashida opportunities for a given text range.
+
+ rKashidaPositions: buffer to receive the char indices of the
+ kashida opportunities relative to the paragraph
+*/
+ void GetKashidaPositions(TextFrameIndex nStt, TextFrameIndex nLen,
+ std::vector<TextFrameIndex>& rKashidaPosition);
+
+/** Use regular blank justification instead of kashdida justification for the given line of text.
+ nStt Start char index of the line referring to the paragraph.
+ nLen Number of characters in the line
+*/
+ void SetNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen);
+
+/** Clear forced blank justification for a given line.
+ nStt Start char index of the line referring to the paragraph.
+ nLen Number of characters in the line
+*/
+ void ClearNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen);
+
+/** Checks if text is Arabic text.
+
+ @descr Checks if text is Arabic text.
+ @param rText
+ The text to check
+ @param nStt
+ Start index of the text
+ @return Returns if the language is an Arabic language
+ */
+ static bool IsArabicText(const OUString& rText, TextFrameIndex nStt, TextFrameIndex nLen);
+
+/** Performs a thai justification on the kerning array
+
+ @descr Add some extra space for thai justification to the
+ positions in the kerning array.
+ @param rText
+ The String
+ @param pKernArray
+ The printers kerning array. Optional.
+ @param nIdx
+ Start referring to the paragraph.
+ @param nLen
+ The number of characters to be considered.
+ @param nSpaceAdd
+ The value which has to be added to the cells.
+ @return The number of extra spaces in the given range
+*/
+ static TextFrameIndex ThaiJustify( const OUString& rText, sal_Int32* pKernArray,
+ TextFrameIndex nIdx,
+ TextFrameIndex nLen,
+ TextFrameIndex nNumberOfBlanks = TextFrameIndex(0),
+ tools::Long nSpaceAdd = 0 );
+
+ static TextFrameIndex CountCJKCharacters(const OUString &rText,
+ TextFrameIndex nPos, TextFrameIndex nEnd, LanguageType aLang);
+
+ static void CJKJustify( const OUString& rText, sal_Int32* pKernArray,
+ TextFrameIndex nStt,
+ TextFrameIndex nLen, LanguageType aLang,
+ tools::Long nSpaceAdd, bool bIsSpaceStop );
+
+ /// return a frame for the node, ScriptInfo is its member...
+ /// (many clients need both frame and SI, and both have to match)
+ static SwScriptInfo* GetScriptInfo( const SwTextNode& rNode,
+ SwTextFrame const** o_pFrame = nullptr,
+ bool bAllowInvalid = false);
+
+ SwFontScript WhichFont(TextFrameIndex nIdx) const;
+ static SwFontScript WhichFont(sal_Int32 nIdx, OUString const & rText);
+};
+
+namespace o3tl
+{
+
+template<> struct typed_flags<SwScriptInfo::MarkKind> : is_typed_flags<SwScriptInfo::MarkKind, 0x07> {};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/sectfrm.hxx b/sw/source/core/inc/sectfrm.hxx
new file mode 100644
index 000000000..09c742f8d
--- /dev/null
+++ b/sw/source/core/inc/sectfrm.hxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SECTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SECTFRM_HXX
+
+#include "layfrm.hxx"
+#include "flowfrm.hxx"
+
+#include <svl/listener.hxx>
+
+class SwSection;
+class SwSectionFormat;
+class SwAttrSetChg;
+class SwFootnoteContFrame;
+class SwLayouter;
+
+enum class SwFindMode
+{
+ None = 0, EndNote = 1, LastCnt = 2, MyLast = 4
+};
+
+enum class SwSectionFrameInvFlags : sal_uInt8
+{
+ NONE = 0x00,
+ InvalidateSize = 0x01,
+ SetCompletePaint = 0x10,
+};
+
+namespace o3tl {
+ template<> struct typed_flags<SwSectionFrameInvFlags> : is_typed_flags<SwSectionFrameInvFlags, 0x0011> {};
+}
+
+class SwSectionFrame final: public SwLayoutFrame, public SwFlowFrame
+ , public SvtListener // TODO?
+{
+ SwSection* m_pSection;
+ bool m_bFootnoteAtEnd; // footnotes at the end of section
+ bool m_bEndnAtEnd; // endnotes at the end of section
+ bool m_bContentLock; // content locked
+ bool m_bOwnFootnoteNum; // special numbering of footnotes
+ bool m_bFootnoteLock; // ftn, don't leave this section bwd
+
+ void UpdateAttr_( const SfxPoolItem*, const SfxPoolItem*, SwSectionFrameInvFlags &,
+ SwAttrSetChg *pa = nullptr, SwAttrSetChg *pb = nullptr );
+ void Cut_( bool bRemove );
+ // Is there a FootnoteContainer?
+ // An empty sectionfrm without FootnoteCont is superfluous
+ bool IsSuperfluous() const { return !ContainsAny() && !ContainsFootnoteCont(); }
+ void CalcFootnoteAtEndFlag();
+ void CalcEndAtEndFlag();
+ const SwSectionFormat* GetEndSectFormat_() const;
+ bool IsEndnoteAtMyEnd() const;
+
+ virtual void DestroyImpl() override;
+ virtual ~SwSectionFrame() override;
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+ virtual bool ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat ) override;
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual void Notify(SfxHint const& rHint) override;
+ virtual void SwClientNotify( const SwModify&, const SfxHint& ) override;
+
+public:
+ SwSectionFrame( SwSection &, SwFrame* ); // Content is not created!
+ SwSectionFrame( SwSectionFrame &, bool bMaster ); // _ONLY_ for creating Master/Follows!
+
+ void Init();
+ virtual void CheckDirection( bool bVert ) override;
+
+ virtual void PaintSubsidiaryLines( const SwPageFrame*, const SwRect& ) const override;
+
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+
+ inline const SwSectionFrame *GetFollow() const;
+ inline SwSectionFrame *GetFollow();
+ SwSectionFrame* FindMaster() const;
+
+ SwContentFrame *FindLastContent( SwFindMode nMode = SwFindMode::None );
+ inline const SwContentFrame *FindLastContent() const;
+ SwSection* GetSection() { return m_pSection; }
+ const SwSection* GetSection() const { return m_pSection; }
+ void ColLock() { mbColLocked = true; }
+ void ColUnlock() { mbColLocked = false; }
+
+ void CalcFootnoteContent();
+ void SimpleFormat();
+ bool IsDescendantFrom( const SwSectionFormat* pSect ) const;
+ bool HasToBreak( const SwFrame* pFrame ) const;
+ void MergeNext( SwSectionFrame* pNxt );
+
+ /**
+ * Splits the SectionFrame surrounding the pFrame up in two parts:
+ * pFrame and the start of the 2nd part
+ */
+ bool SplitSect( SwFrame* pFrame, bool bApres );
+ void DelEmpty( bool bRemove ); // Like Cut(), except for that Follow chaining is maintained
+ SwFootnoteContFrame* ContainsFootnoteCont( const SwFootnoteContFrame* pCont = nullptr ) const;
+ bool Growable() const;
+ SwTwips Shrink_( SwTwips, bool bTst );
+ SwTwips Grow_ ( SwTwips, bool bTst );
+
+ /**
+ * A sectionfrm has to maximize, if he has a follow or a ftncontainer at
+ * the end of the page. A superfluous follow will be ignored,
+ * if bCheckFollow is set.
+ */
+ bool ToMaximize( bool bCheckFollow ) const;
+ bool ToMaximize_() const {
+ if( !m_pSection ) return false;
+ return ToMaximize( false );
+ }
+ bool MoveAllowed( const SwFrame* ) const;
+ bool CalcMinDiff( SwTwips& rMinDiff ) const;
+
+ /**
+ * Returns the size delta that the section would like to be
+ * greater if it has undersized TextFrames in it.
+ *
+ * The return value is > 0 for undersized Frames, or 0 otherwise.
+ *
+ * If necessary the undersized-flag is corrected.
+ * We need this in the FormatWidthCols to "deflate" columns there.
+ */
+ SwTwips Undersize();
+ SwTwips CalcUndersize() const;
+
+ /// Adapt size to surroundings
+ void CheckClipping( bool bGrow, bool bMaximize );
+
+ void InvalidateFootnotePos();
+ void CollectEndnotes( SwLayouter* pLayouter );
+ const SwSectionFormat* GetEndSectFormat() const {
+ if( IsEndnAtEnd() ) return GetEndSectFormat_();
+ return nullptr;
+ }
+
+ static void MoveContentAndDelete( SwSectionFrame* pDel, bool bSave );
+
+ bool IsBalancedSection() const;
+
+ virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override;
+
+ bool IsFootnoteAtEnd() const { return m_bFootnoteAtEnd; }
+ bool IsEndnAtEnd() const { return m_bEndnAtEnd; }
+ bool IsAnyNoteAtEnd() const { return m_bFootnoteAtEnd || m_bEndnAtEnd; }
+
+ void SetContentLock( bool bNew ) { m_bContentLock = bNew; }
+ bool IsContentLocked() const { return m_bContentLock; }
+
+ bool IsOwnFootnoteNum() const { return m_bOwnFootnoteNum; }
+
+ void SetFootnoteLock( bool bNew ) { m_bFootnoteLock = bNew; }
+ bool IsFootnoteLock() const { return m_bFootnoteLock; }
+};
+
+inline const SwSectionFrame *SwSectionFrame::GetFollow() const
+{
+ return static_cast<const SwSectionFrame*>(SwFlowFrame::GetFollow());
+}
+inline SwSectionFrame *SwSectionFrame::GetFollow()
+{
+ return static_cast<SwSectionFrame*>(SwFlowFrame::GetFollow());
+}
+inline const SwContentFrame *SwSectionFrame::FindLastContent() const
+{
+ return const_cast<SwSectionFrame*>(this)->FindLastContent();
+}
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_SECTFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/sortedobjs.hxx b/sw/source/core/inc/sortedobjs.hxx
new file mode 100644
index 000000000..6d524f492
--- /dev/null
+++ b/sw/source/core/inc/sortedobjs.hxx
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SORTEDOBJS_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SORTEDOBJS_HXX
+
+#include <vector>
+#include <swdllapi.h>
+
+class SwAnchoredObject;
+
+/** class for collecting anchored objects
+
+ for #i28701#
+ Anchored objects can be inserted and deleted. The entries can be directly
+ accessed via index.
+ An anchored object is inserted sorted. The sort criteria are:
+ - anchor type
+ - order 1: to-page, 2: to-fly, 3: to-paragraph|to-character|as-character
+ - anchor node
+ - wrapping style (inclusive layer)
+ - order 1: wrapping style != css::text::WrapTextMode_THROUGH and not in hell layer,
+ 2: wrapping style = css::text::WrapTextMode_THROUGH or in hell layer
+ - wrapping style influence
+ - order 1: NONE_SUCCESSIVE_POSITIONED, 2: NONE_CONCURRENT_POSITIONED
+ - again anchor type
+ - order 1: to-paragraph, 2: to-character, 3: as-character
+ - anchor node position
+ - internal anchor order number
+ If one of the sort criteria attributes of an anchored object changes,
+ the sorting has to be updated - use method <Update(..)>
+*/
+class SW_DLLPUBLIC SwSortedObjs
+{
+ private:
+ std::vector< SwAnchoredObject* > maSortedObjLst;
+
+ public:
+ typedef std::vector<SwAnchoredObject*>::const_iterator const_iterator;
+ SwSortedObjs();
+ ~SwSortedObjs();
+
+ size_t size() const;
+
+ /** direct access to the entries
+
+ @param _nIndex
+ input parameter - index of entry, valid value range [0..size()-1]
+ */
+ SwAnchoredObject* operator[]( size_t _nIndex ) const;
+ const_iterator begin() const
+ { return maSortedObjLst.cbegin(); };
+ const_iterator end() const
+ { return maSortedObjLst.cend(); };
+
+ bool Insert( SwAnchoredObject& _rAnchoredObj );
+
+ void Remove( SwAnchoredObject& _rAnchoredObj );
+
+ bool Contains( const SwAnchoredObject& _rAnchoredObj ) const;
+
+ /** method to update the position of the given anchored object in the
+ sorted list
+
+ @return boolean, indicating success of the update.
+ */
+ void Update(SwAnchoredObject& _rAnchoredObj);
+ void UpdateAll();
+
+ /** Position of object <_rAnchoredObj> in sorted list
+
+ Returns the number of the list position of object <_rAnchoredObj>.
+ Returns <size()>, if object isn't contained in list.
+
+ @return size_t
+ Number of the list position of object <_rAnchoredObj>
+ */
+ size_t ListPosOf( const SwAnchoredObject& _rAnchoredObj ) const;
+
+ bool is_sorted() const;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swblocks.hxx b/sw/source/core/inc/swblocks.hxx
new file mode 100644
index 000000000..01d8e2e4f
--- /dev/null
+++ b/sw/source/core/inc/swblocks.hxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWBLOCKS_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWBLOCKS_HXX
+
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <vcl/errcode.hxx>
+#include <rtl/ref.hxx>
+
+class SwPaM;
+class SwDoc;
+class SvxMacroTableDtor;
+
+// Name of a text block:
+
+class SwBlockName
+{
+ friend class SwImpBlocks;
+ sal_uInt16 m_nHashS, m_nHashL; // Hash codes for testing
+public:
+ OUString m_aShort; /// Shortname
+ OUString m_aLong; /// Longname
+ OUString m_aPackageName; /// Package name
+ bool m_bIsOnlyTextFlagInit : 1; /// Is the Flag valid?
+ bool m_bIsOnlyText : 1; /// Unformatted text
+
+ SwBlockName( const OUString& rShort, const OUString& rLong );
+ SwBlockName( const OUString& rShort, const OUString& rLong, const OUString& rPackageName );
+
+ /// For sorting in the array
+ bool operator< ( const SwBlockName& r ) const { return m_aShort < r.m_aShort; }
+};
+
+class SwBlockNames : public o3tl::sorted_vector<std::unique_ptr<SwBlockName>, o3tl::less_uniqueptr_to<SwBlockName> > {};
+
+class SwImpBlocks
+{
+ friend class SwTextBlocks;
+protected:
+ OUString m_aFile; // Physical file name
+ OUString m_aName; // Logical file name
+ OUString m_aCurrentText; // Current text
+ OUString m_aShort, m_aLong; // Short- and longname (PutDoc)
+ OUString m_sBaseURL; // Base URL - has to be set at the Readers and Writers
+ SwBlockNames m_aNames; // List of all Blocks
+ Date m_aDateModified; // For aligning the Actions
+ tools::Time m_aTimeModified;
+ rtl::Reference<SwDoc> m_xDoc; // Document to be switched
+ sal_uInt16 m_nCurrentIndex; // Current Index
+ bool m_bReadOnly : 1;
+ bool m_bInPutMuchBlocks : 1; // Put several block entries
+ bool m_bInfoChanged : 1; // Whether any info of TextBlock changed
+
+ explicit SwImpBlocks( const OUString& );
+
+ enum class FileType {
+ NoFile, // Not present
+ None, // No TB file
+ XML // XML Block List
+ };
+ static FileType GetFileType( const OUString& );
+
+ virtual void ClearDoc(); // Delete Doc content
+ std::unique_ptr<SwPaM> MakePaM(); // Span PaM over Doc
+ virtual void AddName( const OUString&, const OUString&, bool bOnlyText = false );
+ bool IsFileChanged() const;
+ void Touch();
+
+public:
+ virtual ~SwImpBlocks();
+
+ static sal_uInt16 Hash( const OUString& ); /// Hashcode for Block names
+ sal_uInt16 GetCount() const; /// Get count of Text Blocks
+ sal_uInt16 GetIndex( const OUString& ) const; /// Index for shortnames
+ sal_uInt16 GetLongIndex( const OUString& ) const; /// Index for longnames
+ OUString GetShortName( sal_uInt16 ) const; /// Return shortname for index
+ OUString GetLongName( sal_uInt16 ) const; /// Return longname for index
+ OUString GetPackageName( sal_uInt16 ) const; /// Return packagename for index
+
+ const OUString& GetFileName() const {return m_aFile;} /// Return physical file name
+ void SetName( const OUString& rName ) /// Logic name
+ { m_aName = rName; m_bInfoChanged = true; }
+ const OUString& GetName() const
+ { return m_aName; }
+
+ const OUString& GetBaseURL() const { return m_sBaseURL;}
+ void SetBaseURL( const OUString& rURL ) { m_sBaseURL = rURL; }
+
+ virtual ErrCode Delete( sal_uInt16 ) = 0;
+ virtual ErrCode Rename( sal_uInt16, const OUString& ) = 0;
+ virtual ErrCode CopyBlock( SwImpBlocks& rImp, OUString& rShort, const OUString& rLong) = 0;
+ virtual ErrCode GetDoc( sal_uInt16 ) = 0;
+ virtual ErrCode BeginPutDoc( const OUString&, const OUString& ) = 0;
+ virtual ErrCode PutDoc() = 0;
+ virtual ErrCode PutText( const OUString&, const OUString&, const OUString& ) = 0;
+ virtual ErrCode MakeBlockList() = 0;
+
+ virtual ErrCode OpenFile( bool bReadOnly = true ) = 0;
+ virtual void CloseFile() = 0;
+
+ virtual bool IsOnlyTextBlock( const OUString& rShort ) const;
+
+ virtual ErrCode GetMacroTable( sal_uInt16 nIdx, SvxMacroTableDtor& rMacroTable );
+ virtual ErrCode SetMacroTable( sal_uInt16 nIdx,
+ const SvxMacroTableDtor& rMacroTable );
+ virtual bool PutMuchEntries( bool bOn );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swcache.hxx b/sw/source/core/inc/swcache.hxx
new file mode 100644
index 000000000..fba72413c
--- /dev/null
+++ b/sw/source/core/inc/swcache.hxx
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWCACHE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWCACHE_HXX
+
+/**
+ * Here, we manage pointers in a simple PtrArray to objects.
+ * These objects are created (using new) in cache access classes; they are
+ * destroyed by the cache.
+ *
+ * One can access these objects by array index or by searching in the array.
+ * If you access it by index, managing the index is the responsibility of
+ * the cache user.
+ *
+ * The cached objects are derived from the base class SwCacheObj.
+ * In it, the cache objects are doubly-linked which allows for the use of
+ * an LRU algorithm.
+ *
+ * The LRU algorithm can be changed in the base class, by setting a virtual
+ * First Pointer. It can be set to the first real one plus an offset.
+ * By doing so we can protect the start area of the cache and make sure we
+ * don't mess up the cache during some special operations.
+ * E.g.: the Idle Handler should not destroy the cache for the visible area.
+ *
+ * The cache can be grown and shrunk in size.
+ * E.g.: The cache for FormatInfo is grown for every new Shell and shrunk
+ * when destroying them.
+ */
+
+#include <memory>
+#include <vector>
+
+#include <rtl/string.hxx>
+#include <tools/long.hxx>
+
+class SwCacheObj;
+
+class SwCache
+{
+ std::vector<std::unique_ptr<SwCacheObj>> m_aCacheObjects;
+ std::vector<sal_uInt16> m_aFreePositions; /// Free positions for the Insert if the maximum has not been reached
+ /// Every time an object is deregistered, its position is added here
+ SwCacheObj *m_pRealFirst; /// _ALWAYS_ the real first LRU
+ SwCacheObj *m_pFirst; /// The virtual first
+ SwCacheObj *m_pLast;
+
+ sal_uInt16 m_nCurMax; // Maximum of accepted objects
+
+ void DeleteObj( SwCacheObj *pObj );
+
+#ifdef DBG_UTIL
+ OString m_aName;
+ tools::Long m_nAppend; /// number of entries appended
+ tools::Long m_nInsertFree; /// number of entries inserted on freed position
+ tools::Long m_nReplace; /// number of LRU replacements
+ tools::Long m_nGetSuccess;
+ tools::Long m_nGetFail;
+ tools::Long m_nToTop; /// number of reordering (LRU)
+ tools::Long m_nDelete; /// number of explicit deletes
+ tools::Long m_nGetSeek; /// number of gets without index
+ tools::Long m_nAverageSeekCnt; /// number of seeks for all gets without index
+ tools::Long m_nFlushCnt; /// number of flush calls
+ tools::Long m_nFlushedObjects;
+ tools::Long m_nIncreaseMax; /// number of cache size increases
+ tools::Long m_nDecreaseMax; /// number of cache size decreases
+
+ void Check();
+#endif
+
+public:
+
+// Only add sal_uInt8!!!
+#ifdef DBG_UTIL
+ SwCache( const sal_uInt16 nInitSize, const OString &rNm );
+#else
+ SwCache( const sal_uInt16 nInitSize );
+#endif
+ /// The dtor will free all objects still in the vector
+ ~SwCache();
+
+ void Flush();
+
+ //bToTop == false -> No LRU resorting!
+ SwCacheObj *Get( const void *pOwner, const bool bToTop = true );
+ SwCacheObj *Get( const void *pOwner, const sal_uInt16 nIndex,
+ const bool bToTop = true );
+ void ToTop( SwCacheObj *pObj );
+
+ bool Insert(SwCacheObj *pNew, bool isDuplicateOwnerAllowed);
+ void Delete(const void * pOwner, sal_uInt16 nIndex);
+ void Delete( const void *pOwner );
+
+ void SetLRUOfst( const sal_uInt16 nOfst ); /// nOfst determines how many are not to be touched
+ void ResetLRUOfst() { m_pFirst = m_pRealFirst; }
+
+ void IncreaseMax( const sal_uInt16 nAdd );
+ void DecreaseMax( const sal_uInt16 nSub );
+ sal_uInt16 GetCurMax() const { return m_nCurMax; }
+ SwCacheObj *First() { return m_pRealFirst; }
+ static inline SwCacheObj *Next( SwCacheObj *pCacheObj);
+ SwCacheObj* operator[](sal_uInt16 nIndex) { return m_aCacheObjects[nIndex].get(); }
+ sal_uInt16 size() { return m_aCacheObjects.size(); }
+};
+
+/// Try to prevent visible SwParaPortions from being deleted.
+class SwSaveSetLRUOfst
+{
+public:
+ SwSaveSetLRUOfst();
+ ~SwSaveSetLRUOfst();
+};
+
+/**
+ * The Cache object base class
+ * Users of the Cache must derive a class from the SwCacheObj and store
+ * their payload there
+ */
+class SwCacheObj
+{
+ friend class SwCache; /// Can do everything
+
+ SwCacheObj *m_pNext; /// For the LRU chaining
+ SwCacheObj *m_pPrev;
+
+ sal_uInt16 m_nCachePos; /// Position in the Cache array
+
+ sal_uInt8 m_nLock;
+
+ SwCacheObj *GetNext() { return m_pNext; }
+ SwCacheObj *GetPrev() { return m_pPrev; }
+ void SetNext( SwCacheObj *pNew ) { m_pNext = pNew; }
+ void SetPrev( SwCacheObj *pNew ) { m_pPrev = pNew; }
+
+ void SetCachePos(const sal_uInt16 nNew)
+ {
+ if (m_nCachePos != nNew)
+ {
+ m_nCachePos = nNew;
+ UpdateCachePos();
+ }
+ }
+ virtual void UpdateCachePos() { }
+
+protected:
+ const void *m_pOwner;
+
+public:
+
+ SwCacheObj( const void *pOwner );
+ virtual ~SwCacheObj();
+
+ const void *GetOwner() const { return m_pOwner; }
+ inline bool IsOwner( const void *pNew ) const;
+
+ sal_uInt16 GetCachePos() const { return m_nCachePos; }
+
+ bool IsLocked() const { return 0 != m_nLock; }
+
+#ifdef DBG_UTIL
+ void Lock();
+ void Unlock();
+#else
+ void Lock() { ++m_nLock; }
+ void Unlock() { --m_nLock; }
+#endif
+};
+
+/**
+ * Access class for the Cache
+ *
+ * The Cache object is created in the ctor.
+ * If the Cache does not return one, the member is set to 0 and one is
+ * created on the Get() and added to the Cache (if possible).
+ * Cache users must derive a class from SwCacheAccess in order to
+ * guarantee type safety. The base class should always be called for the
+ * Get(). A derived Get() should only ever guarantee type safety.
+ * Cache objects are always locked for the instance's life time.
+ */
+class SwCacheAccess
+{
+ SwCache &m_rCache;
+
+ void Get_(bool isDuplicateOwnerAllowed);
+
+protected:
+ SwCacheObj *m_pObj;
+ const void *m_pOwner; /// Can be use in NewObj
+
+ virtual SwCacheObj *NewObj() = 0;
+
+ inline SwCacheObj *Get(bool isDuplicateOwnerAllowed);
+
+ inline SwCacheAccess( SwCache &rCache, const void *pOwner, bool bSeek );
+ inline SwCacheAccess( SwCache &rCache, const void* nCacheId, const sal_uInt16 nIndex );
+
+public:
+ virtual ~SwCacheAccess();
+};
+
+
+inline bool SwCacheObj::IsOwner( const void *pNew ) const
+{
+ return m_pOwner && m_pOwner == pNew;
+}
+
+inline SwCacheObj *SwCache::Next( SwCacheObj *pCacheObj)
+{
+ if ( pCacheObj )
+ return pCacheObj->GetNext();
+ else
+ return nullptr;
+}
+
+inline SwCacheAccess::SwCacheAccess( SwCache &rC, const void *pOwn, bool bSeek ) :
+ m_rCache( rC ),
+ m_pObj( nullptr ),
+ m_pOwner( pOwn )
+{
+ if ( bSeek && m_pOwner )
+ {
+ m_pObj = m_rCache.Get( m_pOwner );
+ if (m_pObj)
+ m_pObj->Lock();
+ }
+}
+
+inline SwCacheAccess::SwCacheAccess( SwCache &rC, const void* nCacheId,
+ const sal_uInt16 nIndex ) :
+ m_rCache( rC ),
+ m_pObj( nullptr ),
+ m_pOwner( nCacheId )
+{
+ if ( m_pOwner )
+ {
+ m_pObj = m_rCache.Get( m_pOwner, nIndex );
+ if (m_pObj)
+ m_pObj->Lock();
+ }
+}
+
+inline SwCacheObj *SwCacheAccess::Get(bool const isDuplicateOwnerAllowed = true)
+{
+ if ( !m_pObj )
+ Get_(isDuplicateOwnerAllowed);
+ return m_pObj;
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swfntcch.hxx b/sw/source/core/inc/swfntcch.hxx
new file mode 100644
index 000000000..7e25d4d09
--- /dev/null
+++ b/sw/source/core/inc/swfntcch.hxx
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWFNTCCH_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWFNTCCH_HXX
+
+#define NUM_DEFAULT_VALUES 39
+
+#include "swcache.hxx"
+#include "swfont.hxx"
+
+class SwViewShell;
+class SfxPoolItem;
+
+class SwFontCache : public SwCache
+{
+public:
+
+ SwFontCache() : SwCache(50
+#ifdef DBG_UTIL
+ , "Global AttributeSet/Font-Cache pSwFontCache"
+#endif
+ ) {}
+
+};
+
+// AttributeSet/Font-Cache, globale Variable, in FontCache.Cxx angelegt
+extern SwFontCache *pSwFontCache;
+
+class SwFontObj final : public SwCacheObj
+{
+ friend class SwFontAccess;
+
+private:
+ SwFont m_aSwFont;
+ const SfxPoolItem* m_pDefaultArray[ NUM_DEFAULT_VALUES ];
+
+public:
+ SwFontObj( const void* pOwner, SwViewShell *pSh );
+
+ virtual ~SwFontObj() override;
+
+ SwFont& GetFont() { return m_aSwFont; }
+ const SwFont& GetFont() const { return m_aSwFont; }
+ const SfxPoolItem** GetDefault() { return m_pDefaultArray; }
+};
+
+class SwFontAccess final : public SwCacheAccess
+{
+ SwViewShell *m_pShell;
+
+ virtual SwCacheObj *NewObj( ) override;
+
+public:
+ SwFontAccess( const void *pOwner, SwViewShell *pSh );
+ SwFontObj *Get();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swfont.hxx b/sw/source/core/inc/swfont.hxx
new file mode 100644
index 000000000..38936574a
--- /dev/null
+++ b/sw/source/core/inc/swfont.hxx
@@ -0,0 +1,1007 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWFONT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWFONT_HXX
+
+#include <memory>
+#include <i18nlangtag/lang.h>
+#include <tools/color.hxx>
+#include <tools/gen.hxx>
+#include <svl/poolitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <swtypes.hxx>
+#include "drawfont.hxx"
+#include <editeng/borderline.hxx>
+#include <optional>
+#include <o3tl/enumarray.hxx>
+
+class SfxItemSet;
+class SwAttrSet;
+class SwDoCapitals; // DoCapitals
+class SwScriptInfo; // GetTextSize_
+class SwViewShell;
+class IDocumentSettingAccess;
+enum class SvxShadowItemSide;
+
+const sal_Unicode CH_BLANK = ' '; // ' ' blank spaces
+const sal_Unicode CH_BREAK = 0x0A;
+const sal_Unicode CH_TAB = '\t'; // \t
+constexpr OUStringChar CH_PAR = u'\u00B6'; // paragraph
+const sal_Unicode CH_BULLET = 0xB7; // centered dot
+const sal_Unicode CH_FULL_BLANK = 0x3000;
+const sal_Unicode CH_NB_SPACE = 0xA0;
+const sal_Unicode CH_SIX_PER_EM = 0x2006; // six-per-em space
+
+Degree10 UnMapDirection( Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT );
+
+class SwSubFont final : public SvxFont
+{
+ friend class SwFont;
+ const void* m_nFontCacheId; // "MagicNumber" within the font cache
+ Size m_aSize; // foreigners only see this size
+ sal_uInt16 m_nFontIndex; // index in the font cache
+ sal_uInt16 m_nOrgHeight; // height including escapement/proportion
+ sal_uInt16 m_nOrgAscent; // ascent including escapement/proportion
+ sal_uInt16 m_nProportionalWidth; // proportional width
+ bool m_bSmallCapsPercentage66;
+
+ sal_uInt16 CalcEscAscent( const sal_uInt16 nOldAscent ) const;
+ sal_uInt16 CalcEscHeight( const sal_uInt16 nOldHeight,
+ const sal_uInt16 nOldAscent ) const;
+ void CalcEsc( SwDrawTextInfo const & rInf, Point& rPos );
+
+ short CheckKerning_( );
+
+ bool ChgFnt( SwViewShell const *pSh, OutputDevice& rOut );
+ bool IsSymbol( SwViewShell const *pSh );
+ sal_uInt16 GetAscent( SwViewShell const *pSh, const OutputDevice& rOut );
+ sal_uInt16 GetHeight( SwViewShell const *pSh, const OutputDevice& rOut );
+ sal_uInt16 GetHangingBaseline( SwViewShell const *pSh, const OutputDevice& rOut );
+ Size GetTextSize_( SwDrawTextInfo& rInf );
+ Size GetCapitalSize( SwDrawTextInfo& rInf );
+ void DrawText_( SwDrawTextInfo &rInf, const bool bGrey );
+ void DrawCapital( SwDrawTextInfo &rInf );
+ void DrawStretchCapital( SwDrawTextInfo &rInf );
+ void DoOnCapitals( SwDoCapitals &rDo );
+ void DrawStretchText_( SwDrawTextInfo &rInf );
+ TextFrameIndex GetModelPositionForViewPoint_( SwDrawTextInfo& rInf );
+ TextFrameIndex GetCapitalCursorOfst( SwDrawTextInfo& rInf );
+
+ inline void SetColor( const Color& rColor );
+ inline void SetFillColor( const Color& rColor );
+ inline void SetCharSet( const rtl_TextEncoding eCharSet );
+ inline void SetPitch( const FontPitch ePitch );
+ inline void SetAlign( const TextAlign eAlign );
+ inline void SetUnderline( const FontLineStyle eUnderline );
+ inline void SetOverline( const FontLineStyle eOverline );
+ inline void SetStrikeout( const FontStrikeout eStrikeout );
+ inline void SetItalic( const FontItalic eItalic );
+ inline void SetOutline( const bool bOutline );
+ inline void SetVertical( const Degree10 nDir, const bool bVertFormat );
+ inline void SetShadow( const bool bShadow );
+ inline void SetAutoKern( FontKerning nAutoKern );
+ inline void SetWordLineMode( const bool bWordLineMode );
+ inline void SetEmphasisMark( const FontEmphasisMark eValue );
+ inline void SetRelief( const FontRelief eNew );
+
+ // methods for sub-/superscript
+ inline void SetEscapement( const short nNewEsc );
+ inline void SetProportion( const sal_uInt8 nNewPropr );
+
+ inline void SetFamily( const FontFamily eFamily );
+ inline void SetName( const OUString& rName );
+ inline void SetStyleName( const OUString& rStyleName );
+ inline void SetSize( const Size& rSize );
+ inline void SetWeight( const FontWeight eWeight );
+ inline void SetLanguage( LanguageType eNewLang );
+ short CheckKerning()
+ { return GetFixKerning() >= 0 ? GetFixKerning() : CheckKerning_( ); }
+ void SetPropWidth( const sal_uInt16 nNew )
+ { m_nFontCacheId = nullptr; m_nProportionalWidth = nNew; }
+public:
+ SwSubFont() : m_aSize(0,0)
+ { m_nFontCacheId = nullptr; m_nFontIndex = m_nOrgHeight = m_nOrgAscent = 0; m_nProportionalWidth =100; m_bSmallCapsPercentage66 = false; }
+ sal_uInt16 GetPropWidth() const { return m_nProportionalWidth; }
+};
+
+enum class SwFontScript
+{
+ Latin, CJK, CTL, LAST = CTL
+};
+
+// mostly used as a "unknown script" marker
+#define SW_SCRIPTS (SwFontScript(int(SwFontScript::LAST)+1))
+
+class SwFont
+{
+ // CJK == Chinese, Japanese, Korean
+ // CTL == Complex text layout ( Hebrew, Arabic )
+ o3tl::enumarray<SwFontScript, SwSubFont> m_aSub; // Latin-, CJK- and CTL-font
+
+ std::optional<Color>
+ mxBackColor; // background color (i.e. at character styles)
+ Color m_aHighlightColor; // highlight color
+ Color m_aUnderColor; // color of the underlining
+ Color m_aOverColor; // color of the overlining
+
+ // character borders
+ std::optional<editeng::SvxBorderLine> m_aTopBorder;
+ std::optional<editeng::SvxBorderLine> m_aBottomBorder;
+ std::optional<editeng::SvxBorderLine> m_aRightBorder;
+ std::optional<editeng::SvxBorderLine> m_aLeftBorder;
+
+ // border distance
+ sal_uInt16 m_nTopBorderDist;
+ sal_uInt16 m_nBottomBorderDist;
+ sal_uInt16 m_nRightBorderDist;
+ sal_uInt16 m_nLeftBorderDist;
+
+ Color m_aShadowColor;
+ sal_uInt16 m_nShadowWidth;
+ SvxShadowLocation m_aShadowLocation;
+
+ sal_uInt8 m_nToxCount; // counts the nesting depth of the Tox
+ sal_uInt8 m_nRefCount; // counts the nesting depth of the Refs
+ sal_uInt8 m_nMetaCount; // count META/METAFIELD
+ sal_uInt8 m_nContentControlCount; // count CONTENTCONTROL
+ sal_uInt8 m_nInputFieldCount; // count INPUTFIELD
+
+ SwFontScript m_nActual; // actual font (Latin, CJK or CTL)
+
+ // switch for the font-extensions
+ bool m_bPaintBlank :1; // blanks not with DrawRect
+ bool m_bFontChg :1;
+ bool m_bOrgChg :1; // nOrgHeight/Ascent are invalid
+ bool m_bGreyWave :1; // for the extended TextInput: gray waveline
+
+public:
+ SwFont( const SwAttrSet* pSet, const IDocumentSettingAccess* pIDocumentSettingAccess );
+ SwFont( const SwFont& rFont );
+
+ void ChgFnt( SwViewShell const *pSh, OutputDevice& rOut )
+ { m_bPaintBlank = m_aSub[m_nActual].ChgFnt( pSh, rOut ); }
+
+ ~SwFont();
+
+ SwFont& operator=( const SwFont &rFont );
+
+ SwFontScript GetActual() const { return m_nActual; }
+ inline void SetActual( SwFontScript nNew );
+ const SvxFont& GetActualFont() const { return m_aSub[m_nActual]; }
+
+ // gets a font cache id via SwFntAccess
+ void AllocFontCacheId( SwViewShell const *pSh, SwFontScript nWhich );
+ // set background color
+ void SetBackColor( std::optional<Color> xNewColor );
+ const std::optional<Color>& GetBackColor() const{ return mxBackColor; }
+ void SetHighlightColor( const Color& aNewColor );
+ const Color& GetHighlightColor() const { return m_aHighlightColor; }
+
+ void CheckFontCacheId( SwViewShell const *pSh, SwFontScript nWhich )
+ { if( !m_aSub[ nWhich ].m_nFontCacheId ) AllocFontCacheId( pSh, nWhich ); }
+ void GetFontCacheId( const void* &rnFontCacheId, sal_uInt16 &rIdx, SwFontScript nWhich )
+ { rnFontCacheId = m_aSub[nWhich].m_nFontCacheId; rIdx = m_aSub[nWhich].m_nFontIndex; }
+ void SetFontCacheId( const void* nNewFontCacheId, const sal_uInt16 nIdx, SwFontScript nWhich )
+ { m_aSub[nWhich].m_nFontCacheId = nNewFontCacheId; m_aSub[nWhich].m_nFontIndex = nIdx; }
+ bool DifferentFontCacheId( const SwFont* pFnt, SwFontScript nWhich )
+ { return m_aSub[nWhich].m_nFontCacheId != pFnt->m_aSub[nWhich].m_nFontCacheId ||
+ !m_aSub[nWhich].m_nFontCacheId || !pFnt->m_aSub[nWhich].m_nFontCacheId; }
+
+ const Size &GetSize( SwFontScript nWhich ) const
+ { return m_aSub[nWhich].m_aSize; }
+ bool IsFntChg() const { return m_bFontChg; }
+ void SetFntChg( const bool bNew ) { m_bFontChg = bNew; }
+
+ // the encapsulated SV-Font-methods (set bFntChg to true)
+ inline void SetColor( const Color& rColor );
+ inline void SetFillColor( const Color& rColor );
+ inline void SetAlign( const TextAlign eAlign );
+ inline void SetUnderline( const FontLineStyle eUnderline );
+ void SetUnderColor( const Color &rColor ) { m_aUnderColor = rColor; }
+ inline void SetOverline( const FontLineStyle eOverline );
+ void SetOverColor( const Color &rColor ) { m_aOverColor = rColor; }
+ inline void SetStrikeout( const FontStrikeout eStrikeout );
+ inline void SetOutline( const bool bOutline );
+ void SetVertical(Degree10 nDir, const bool bVertLayout = false,
+ const bool bVertLayoutLRBT = false);
+ inline void SetShadow( const bool bShadow );
+ inline void SetAutoKern( FontKerning nAutoKern );
+ inline void SetTransparent( const bool bTrans );
+ inline void SetWordLineMode( const bool bWordLineMode );
+ inline void SetFixKerning( const short nNewKern );
+ inline void SetCaseMap( const SvxCaseMap eNew );
+ inline void SetEmphasisMark( const FontEmphasisMark eValue );
+
+ // methods for sub-/superscript
+ inline void SetEscapement( const short nNewEsc );
+ inline void SetProportion( const sal_uInt8 nNewPropr );
+
+ inline void SetPropWidth( const sal_uInt16 nNew );
+
+ inline void SetFamily( const FontFamily eFamily, const SwFontScript nWhich );
+ inline void SetName( const OUString& rName, const SwFontScript nWhich );
+ inline void SetStyleName( const OUString& rStyleName, const SwFontScript nWhich );
+ inline void SetSize( const Size& rSize, const SwFontScript nWhich );
+ inline void SetWeight( const FontWeight eWeight, const SwFontScript nWhich );
+ inline void SetItalic( const FontItalic eItalic, const SwFontScript nWhich );
+ inline void SetLanguage( LanguageType eNewLang, const SwFontScript nWhich );
+ inline void SetCharSet( const rtl_TextEncoding eCharSet, const SwFontScript nWhich );
+ inline void SetPitch( const FontPitch ePitch, const SwFontScript nWhich );
+ inline void SetRelief( const FontRelief eNew );
+
+ // Get/Set-methods for the current setting
+ sal_uInt8 &GetTox() { return m_nToxCount; }
+ bool IsTox() const { return ( 0 != m_nToxCount ); }
+ sal_uInt8 &GetRef() { return m_nRefCount; }
+ bool IsRef() const { return ( 0 != m_nRefCount ); }
+ sal_uInt8 &GetMeta() { return m_nMetaCount; }
+ bool IsMeta() const { return (0 != m_nMetaCount); }
+ sal_uInt8& GetContentControl() { return m_nContentControlCount; }
+ bool IsContentControl() const { return m_nContentControlCount != 0; }
+ sal_uInt8 &GetInputField() { return m_nInputFieldCount; }
+ bool IsInputField() const { return (0 != m_nInputFieldCount); }
+ inline void SetGreyWave( const bool bNew );
+ bool IsGreyWave() const { return m_bGreyWave; }
+ bool IsPaintBlank() const { return m_bPaintBlank; }
+
+ // setting of the base class font for SwTextCharFormat
+ void SetDiffFnt( const SfxItemSet* pSet,
+ const IDocumentSettingAccess* pIDocumentSettingAccess );
+
+ const SvxFont &GetFnt( const SwFontScript nWhich ) const
+ { return m_aSub[nWhich]; };
+
+ bool IsSymbol( SwViewShell const *pSh )
+ { return m_aSub[m_nActual].IsSymbol( pSh ); }
+ FontLineStyle GetUnderline() const { return m_aSub[m_nActual].GetUnderline(); }
+ const Color& GetUnderColor() const { return m_aUnderColor; }
+ FontLineStyle GetOverline() const { return m_aSub[m_nActual].GetOverline(); }
+ const Color& GetOverColor() const { return m_aOverColor; }
+ FontStrikeout GetStrikeout() const { return m_aSub[m_nActual].GetStrikeout(); }
+ const Color& GetColor() const { return m_aSub[m_nActual].GetColor(); }
+ bool IsWordLineMode() const { return m_aSub[m_nActual].IsWordLineMode(); }
+ short GetEscapement() const { return m_aSub[m_nActual].GetEscapement(); }
+ SvxCaseMap GetCaseMap() const { return m_aSub[m_nActual].GetCaseMap(); }
+ sal_uInt8 GetPropr() const { return m_aSub[m_nActual].GetPropr(); }
+ FontItalic GetItalic() const { return m_aSub[m_nActual].GetItalic(); }
+ LanguageType GetLanguage() const { return m_aSub[m_nActual].GetLanguage(); }
+ tools::Long GetHeight() const { return m_aSub[m_nActual].GetFontSize().Height(); }
+ FontWeight GetWeight() const { return m_aSub[m_nActual].GetWeight(); }
+ FontEmphasisMark GetEmphasisMark() const
+ { return m_aSub[m_nActual].GetEmphasisMark(); }
+ Degree10 GetOrientation(const bool bVertLayout = false,
+ const bool bVertFormatLRBT = false) const;
+
+ const OUString& GetName( const SwFontScript nWhich ) const
+ { return m_aSub[nWhich].GetFamilyName(); }
+ LanguageType GetLanguage( const SwFontScript nWhich ) const
+ { return m_aSub[nWhich].GetLanguage(); }
+ rtl_TextEncoding GetCharSet( const SwFontScript nWhich ) const
+ { return m_aSub[nWhich].GetCharSet(); }
+ tools::Long GetHeight( const SwFontScript nWhich ) const
+ { return m_aSub[nWhich].GetFontSize().Height(); }
+
+ // makes the logical font be effective in the OutputDevice
+ void ChgPhysFnt( SwViewShell const *pSh, OutputDevice& rOut );
+
+ TextFrameIndex GetCapitalBreak( SwViewShell const* pSh, const OutputDevice* pOut,
+ const SwScriptInfo* pScript, const OUString& rText,
+ tools::Long nTextWidth, TextFrameIndex nIdx, TextFrameIndex nLen);
+
+ void DoOnCapitals( SwDoCapitals &rDo )
+ { m_aSub[m_nActual].DoOnCapitals( rDo ); }
+
+ Size GetTextSize_( SwDrawTextInfo& rInf )
+ { rInf.SetFont( this ); return m_aSub[m_nActual].GetTextSize_( rInf ); }
+
+ TextFrameIndex GetTextBreak( SwDrawTextInfo const & rInf, tools::Long nTextWidth );
+
+ TextFrameIndex GetModelPositionForViewPoint_( SwDrawTextInfo& rInf )
+ { return m_aSub[m_nActual].GetModelPositionForViewPoint_( rInf ); }
+
+ void DrawText_( SwDrawTextInfo &rInf )
+ { m_aSub[m_nActual].DrawText_( rInf, IsGreyWave() ); }
+
+ void DrawStretchText_( SwDrawTextInfo &rInf )
+ { m_aSub[m_nActual].DrawStretchText_( rInf ); }
+
+ short CheckKerning()
+ { return m_aSub[m_nActual].CheckKerning(); }
+
+ sal_uInt16 GetAscent( SwViewShell const *pSh, const OutputDevice& rOut )
+ { return m_aSub[m_nActual].GetAscent( pSh, rOut ); }
+ sal_uInt16 GetHeight( SwViewShell const *pSh, const OutputDevice& rOut )
+ { return m_aSub[m_nActual].GetHeight( pSh, rOut ); }
+
+ sal_uInt16 GetHangingBaseline( SwViewShell const *pSh, const OutputDevice& rOut )
+ { return m_nActual == SwFontScript::CTL ? m_aSub[m_nActual].GetHangingBaseline( pSh, rOut ) : 0; }
+
+ void Invalidate()
+ { m_bFontChg = m_bOrgChg = true; }
+
+ void SetTopBorder( const editeng::SvxBorderLine* pTopBorder );
+ void SetBottomBorder( const editeng::SvxBorderLine* pBottomBorder );
+ void SetRightBorder( const editeng::SvxBorderLine* pRightBorder );
+ void SetLeftBorder( const editeng::SvxBorderLine* pLeftBorder );
+
+ const std::optional<editeng::SvxBorderLine>& GetTopBorder() const { return m_aTopBorder; }
+ const std::optional<editeng::SvxBorderLine>& GetBottomBorder() const { return m_aBottomBorder; }
+ const std::optional<editeng::SvxBorderLine>& GetRightBorder() const { return m_aRightBorder; }
+ const std::optional<editeng::SvxBorderLine>& GetLeftBorder() const { return m_aLeftBorder; }
+
+ // Get absolute border correspond to the layout verticality and orientation.
+ const std::optional<editeng::SvxBorderLine>&
+ GetAbsTopBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const;
+ const std::optional<editeng::SvxBorderLine>&
+ GetAbsBottomBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const;
+ const std::optional<editeng::SvxBorderLine>&
+ GetAbsRightBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const;
+ const std::optional<editeng::SvxBorderLine>&
+ GetAbsLeftBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const;
+
+ void SetTopBorderDist( const sal_uInt16 nTopDist );
+ void SetBottomBorderDist( const sal_uInt16 nBottomDist );
+ void SetRightBorderDist( const sal_uInt16 nRightDist );
+ void SetLeftBorderDist( const sal_uInt16 nLeftDist );
+
+ sal_uInt16 GetTopBorderDist() const { return m_nTopBorderDist; }
+ sal_uInt16 GetBottomBorderDist() const { return m_nBottomBorderDist; }
+ sal_uInt16 GetRightBorderDist() const { return m_nRightBorderDist; }
+ sal_uInt16 GetLeftBorderDist() const { return m_nLeftBorderDist; }
+
+ // Return with the whole space which border holed (border width, spacing and shadow width)
+ sal_uInt16 GetTopBorderSpace() const;
+ sal_uInt16 GetBottomBorderSpace() const;
+ sal_uInt16 GetRightBorderSpace() const;
+ sal_uInt16 GetLeftBorderSpace() const;
+
+ /// Check whether font has any border on any side
+ bool HasBorder() const;
+
+ void SetShadowColor( const Color& rColor );
+ void SetShadowWidth( const sal_uInt16 nWidth );
+ void SetShadowLocation( const SvxShadowLocation aLocation );
+
+ const Color& GetShadowColor() const { return m_aShadowColor; }
+ sal_uInt16 GetShadowWidth() const { return m_nShadowWidth; }
+ SvxShadowLocation GetShadowLocation() const { return m_aShadowLocation; }
+
+ /**
+ * Get the absolute shadow location dependent from orientation.
+ *
+ * @param[in] bVertLayout true, if the container layout is vertical
+ * false, otherwise
+ * @param[in] bVertLayoutLRBT true if the container layout is vertical
+ * (bottom to top, left to right), false otherwise
+ * @return absolute location
+ **/
+ SvxShadowLocation GetAbsShadowLocation(const bool bVertLayout,
+ const bool bVertLayoutLRBT) const;
+
+ /**
+ * Calculate the shadow space on the specified side dependent from
+ * the orientation and connection with neighbours.
+ * @see shaditem.hxx for integer constants of sides
+ *
+ * @param[in] nShadow specify the side
+ * @param[in] bVertLayout true, if the container layout is vertical
+ * false, otherwise
+ * @param[in] bVertLayoutLRBT true if the container layout is vertical
+ * (bottom to top, left to right), false otherwise
+ * @param[in] bSkipLeft relative left shadow space is skipped
+ * @param[in] bSkipRight relative right shadow space is skipped
+ * @return the shadow space
+ **/
+ sal_uInt16 CalcShadowSpace(
+ const SvxShadowItemSide nShadow, const bool bVertLayout, const bool bVertLayoutLRBT,
+ const bool bSkipLeft, const bool bSkipRight ) const;
+
+ void dumpAsXml( xmlTextWriterPtr writer ) const;
+};
+
+inline void SwFont::SetColor( const Color& rColor )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetColor( rColor );
+ m_aSub[SwFontScript::CJK].SetColor( rColor );
+ m_aSub[SwFontScript::CTL].SetColor( rColor );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetColor( const Color& rColor )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetColor( rColor );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetFillColor( const Color& rColor )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetFillColor( rColor );
+}
+
+inline void SwFont::SetFillColor( const Color& rColor )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetFillColor( rColor );
+ m_aSub[SwFontScript::CJK].SetFillColor( rColor );
+ m_aSub[SwFontScript::CTL].SetFillColor( rColor );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetFamily( const FontFamily eFamily )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetFamily( eFamily );
+}
+
+inline void SwFont::SetFamily( const FontFamily eFamily, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetFamily( eFamily );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetName( const OUString& rName )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetFamilyName( rName );
+}
+
+inline void SwFont::SetName( const OUString& rName, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetName( rName );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetStyleName( const OUString& rStyleName )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetStyleName( rStyleName );
+}
+
+inline void SwFont::SetStyleName( const OUString& rStyle, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetStyleName( rStyle );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetCharSet( const rtl_TextEncoding eCharSet )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetCharSet( eCharSet );
+}
+
+inline void SwFont::SetCharSet( const rtl_TextEncoding eCharSet, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetCharSet( eCharSet );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetPitch( const FontPitch ePitch )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetPitch( ePitch );
+}
+
+// encapsulated SV-Font-method
+inline void SwFont::SetPitch( const FontPitch ePitch, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetPitch( ePitch );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetAlign( const TextAlign eAlign )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetAlignment( eAlign );
+}
+
+inline void SwFont::SetAlign( const TextAlign eAlign )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetAlign( eAlign );
+ m_aSub[SwFontScript::CJK].SetAlign( eAlign );
+ m_aSub[SwFontScript::CTL].SetAlign( eAlign );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetWeight( const FontWeight eWeight )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetWeight( eWeight );
+}
+
+inline void SwFont::SetWeight( const FontWeight eWeight, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetWeight( eWeight );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetUnderline( const FontLineStyle eUnderline )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetUnderline( eUnderline );
+}
+
+inline void SwFont::SetUnderline( const FontLineStyle eUnderline )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetUnderline( eUnderline );
+ m_aSub[SwFontScript::CJK].SetUnderline( eUnderline );
+ m_aSub[SwFontScript::CTL].SetUnderline( eUnderline );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetOverline( const FontLineStyle eOverline )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetOverline( eOverline );
+}
+
+inline void SwFont::SetOverline( const FontLineStyle eOverline )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetOverline( eOverline );
+ m_aSub[SwFontScript::CJK].SetOverline( eOverline );
+ m_aSub[SwFontScript::CTL].SetOverline( eOverline );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetStrikeout( const FontStrikeout eStrikeout )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetStrikeout( eStrikeout );
+}
+
+inline void SwFont::SetStrikeout( const FontStrikeout eStrikeout )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetStrikeout( eStrikeout );
+ m_aSub[SwFontScript::CJK].SetStrikeout( eStrikeout );
+ m_aSub[SwFontScript::CTL].SetStrikeout( eStrikeout );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetItalic( const FontItalic eItalic )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetItalic( eItalic );
+}
+
+inline void SwFont::SetItalic( const FontItalic eItalic, const SwFontScript nWhich )
+{
+ m_bFontChg = true;
+ m_aSub[nWhich].SetItalic( eItalic );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetOutline( const bool bOutline )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetOutline( bOutline );
+}
+
+inline void SwFont::SetOutline( const bool bOutline )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetOutline( bOutline );
+ m_aSub[SwFontScript::CJK].SetOutline( bOutline );
+ m_aSub[SwFontScript::CTL].SetOutline( bOutline );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetShadow( const bool bShadow )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetShadow( bShadow );
+}
+
+inline void SwFont::SetShadow( const bool bShadow )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetShadow( bShadow );
+ m_aSub[SwFontScript::CJK].SetShadow( bShadow );
+ m_aSub[SwFontScript::CTL].SetShadow( bShadow );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetAutoKern( FontKerning nAutoKern )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetKerning( nAutoKern );
+}
+
+inline void SwFont::SetAutoKern( FontKerning nAutoKern )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::CJK].SetAutoKern( nAutoKern );
+ if( nAutoKern != FontKerning::NONE )
+ nAutoKern = FontKerning::FontSpecific;
+ m_aSub[SwFontScript::Latin].SetAutoKern( nAutoKern );
+ m_aSub[SwFontScript::CTL].SetAutoKern( nAutoKern );
+}
+
+inline void SwFont::SetTransparent( const bool bTrans )
+{
+ m_aSub[SwFontScript::Latin].SetTransparent( bTrans );
+ m_aSub[SwFontScript::CJK].SetTransparent( bTrans );
+ m_aSub[SwFontScript::CTL].SetTransparent( bTrans );
+}
+
+inline void SwFont::SetFixKerning( const short nNewKern )
+{
+ m_aSub[SwFontScript::Latin].SetFixKerning( nNewKern );
+ m_aSub[SwFontScript::CJK].SetFixKerning( nNewKern );
+ m_aSub[SwFontScript::CTL].SetFixKerning( nNewKern );
+}
+
+inline void SwFont::SetCaseMap( const SvxCaseMap eNew )
+{
+ m_aSub[SwFontScript::Latin].SetCaseMap( eNew );
+ m_aSub[SwFontScript::CJK].SetCaseMap( eNew );
+ m_aSub[SwFontScript::CTL].SetCaseMap( eNew );
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetWordLineMode( const bool bWordLineMode )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetWordLineMode( bWordLineMode );
+}
+
+inline void SwFont::SetWordLineMode( const bool bWordLineMode )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetWordLineMode( bWordLineMode );
+ m_aSub[SwFontScript::CJK].SetWordLineMode( bWordLineMode );
+ m_aSub[SwFontScript::CTL].SetWordLineMode( bWordLineMode );
+}
+// encapsulated SV-Font-method
+inline void SwSubFont::SetEmphasisMark( const FontEmphasisMark eValue )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetEmphasisMark( eValue );
+}
+
+inline void SwFont::SetEmphasisMark( const FontEmphasisMark eValue )
+{
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetEmphasisMark( eValue );
+ m_aSub[SwFontScript::CJK].SetEmphasisMark( eValue );
+ m_aSub[SwFontScript::CTL].SetEmphasisMark( eValue );
+}
+
+inline void SwFont::SetPropWidth( const sal_uInt16 nNew )
+{
+ if( nNew != m_aSub[SwFontScript::Latin].GetPropWidth() )
+ {
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetPropWidth( nNew );
+ m_aSub[SwFontScript::CJK].SetPropWidth( nNew );
+ m_aSub[SwFontScript::CTL].SetPropWidth( nNew );
+ }
+}
+
+// encapsulated SV-Font-method
+inline void SwSubFont::SetRelief( const FontRelief eNew )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetRelief( eNew );
+}
+
+inline void SwFont::SetRelief( const FontRelief eNew )
+{
+ if( eNew != m_aSub[SwFontScript::Latin].GetRelief() )
+ {
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].SetRelief( eNew );
+ m_aSub[SwFontScript::CJK].SetRelief( eNew );
+ m_aSub[SwFontScript::CTL].SetRelief( eNew );
+ }
+}
+
+// overloaded font-method
+inline void SwSubFont::SetSize( const Size& rSize )
+{
+ m_aSize = rSize;
+ if ( GetPropr() == 100 )
+ Font::SetFontSize( m_aSize );
+ else
+ {
+ Font::SetFontSize( Size(
+ m_aSize.Width() * GetPropr() / 100,
+ m_aSize.Height() * GetPropr() / 100 ) );
+ }
+ m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetSize( const Size& rSize, const SwFontScript nWhich )
+{
+ if( m_aSub[nWhich].m_aSize != rSize )
+ {
+ m_aSub[nWhich].SetSize( rSize );
+ m_bFontChg = true;
+ m_bOrgChg = true;
+ }
+}
+
+inline void SwFont::SetActual( SwFontScript nNew )
+{
+ if ( m_nActual != nNew )
+ {
+ m_bFontChg = true;
+ m_bOrgChg = true;
+ m_nActual = nNew;
+ }
+}
+
+inline void SwSubFont::SetProportion( const sal_uInt8 nNewPropr )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetFontSize( Size( m_aSize.Width() * nNewPropr / 100,
+ m_aSize.Height() * nNewPropr / 100 ) );
+ SvxFont::SetPropr( nNewPropr );
+}
+
+inline void SwFont::SetProportion( const sal_uInt8 nNewPropr )
+{
+ if( nNewPropr != m_aSub[SwFontScript::Latin].GetPropr() )
+ {
+ m_bFontChg = true;
+ m_bOrgChg = true;
+
+ m_aSub[SwFontScript::Latin].SetProportion( nNewPropr );
+ m_aSub[SwFontScript::CJK].SetProportion( nNewPropr );
+ m_aSub[SwFontScript::CTL].SetProportion( nNewPropr );
+ }
+}
+
+inline void SwSubFont::SetEscapement( const short nNewEsc )
+{
+ m_nFontCacheId = nullptr;
+ SvxFont::SetEscapement( nNewEsc );
+}
+
+inline void SwFont::SetEscapement( const short nNewEsc )
+{
+ if( nNewEsc != m_aSub[SwFontScript::Latin].GetEscapement() )
+ {
+ // these have to be set, otherwise nOrgHeight and nOrgAscent will not
+ // be calculated
+ m_bFontChg = true;
+ m_bOrgChg = true;
+
+ m_aSub[SwFontScript::Latin].SetEscapement( nNewEsc );
+ m_aSub[SwFontScript::CJK].SetEscapement( nNewEsc );
+ m_aSub[SwFontScript::CTL].SetEscapement( nNewEsc );
+ }
+}
+
+inline void SwSubFont::SetLanguage( LanguageType eNewLang )
+{
+ m_nFontCacheId = nullptr;
+ if( eNewLang == LANGUAGE_SYSTEM )
+ eNewLang = GetAppLanguage();
+ SvxFont::SetLanguage( eNewLang );
+}
+
+inline void SwFont::SetLanguage( const LanguageType eNewLang, const SwFontScript nWhich )
+{
+ m_aSub[nWhich].SetLanguage( eNewLang );
+ if( SwFontScript::CJK == nWhich )
+ {
+ m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
+ m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
+ m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
+ }
+}
+
+inline void SwFont::SetGreyWave( const bool bNew )
+{
+ m_bGreyWave = bNew;
+}
+
+inline void SwSubFont::SetVertical( const Degree10 nDir, const bool bVertFormat )
+{
+ m_nFontCacheId = nullptr;
+ Font::SetVertical( bVertFormat );
+ Font::SetOrientation( nDir );
+}
+
+inline void SwFont::SetTopBorderDist( const sal_uInt16 nTopDist )
+{
+ m_nTopBorderDist = nTopDist;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetBottomBorderDist( const sal_uInt16 nBottomDist )
+{
+ m_nBottomBorderDist = nBottomDist;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetRightBorderDist( const sal_uInt16 nRightDist )
+{
+ m_nRightBorderDist = nRightDist;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetLeftBorderDist( const sal_uInt16 nLeftDist )
+{
+ m_nLeftBorderDist = nLeftDist;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline sal_uInt16 SwFont::GetTopBorderSpace() const
+{
+ sal_uInt16 nRet = 0;
+ if( m_aTopBorder )
+ {
+ nRet += m_aTopBorder->GetScaledWidth() + m_nTopBorderDist;
+ }
+ if( m_aShadowLocation == SvxShadowLocation::TopLeft ||
+ m_aShadowLocation == SvxShadowLocation::TopRight )
+ {
+ nRet += m_nShadowWidth;
+ }
+ return nRet;
+}
+
+inline sal_uInt16 SwFont::GetBottomBorderSpace() const
+{
+ sal_uInt16 nRet = 0;
+ if( m_aBottomBorder )
+ {
+ nRet += m_aBottomBorder->GetScaledWidth() + m_nBottomBorderDist;
+ }
+ if( m_aShadowLocation == SvxShadowLocation::BottomLeft ||
+ m_aShadowLocation == SvxShadowLocation::BottomRight )
+ {
+ nRet += m_nShadowWidth;
+ }
+ return nRet;
+}
+
+inline sal_uInt16 SwFont::GetRightBorderSpace() const
+{
+ sal_uInt16 nRet = 0;
+ if( m_aRightBorder )
+ {
+ nRet += m_aRightBorder->GetScaledWidth() + m_nRightBorderDist;
+ }
+ if( m_aShadowLocation == SvxShadowLocation::TopRight ||
+ m_aShadowLocation == SvxShadowLocation::BottomRight )
+ {
+ nRet += m_nShadowWidth;
+ }
+ return nRet;
+}
+
+inline sal_uInt16 SwFont::GetLeftBorderSpace() const
+{
+ sal_uInt16 nRet = 0;
+ if( m_aLeftBorder )
+ {
+ nRet += m_aLeftBorder->GetScaledWidth() + m_nLeftBorderDist;
+ }
+ if( m_aShadowLocation == SvxShadowLocation::TopLeft ||
+ m_aShadowLocation == SvxShadowLocation::BottomLeft )
+ {
+ nRet += m_nShadowWidth;
+ }
+ return nRet;
+}
+
+inline bool SwFont::HasBorder() const
+{
+ return m_aTopBorder || m_aBottomBorder || m_aLeftBorder || m_aRightBorder;
+}
+
+inline void SwFont::SetShadowColor( const Color& rColor )
+{
+ m_aShadowColor = rColor;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetShadowWidth( const sal_uInt16 nWidth )
+{
+ m_nShadowWidth = nWidth;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetShadowLocation( const SvxShadowLocation aLocation )
+{
+ m_aShadowLocation = aLocation;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+inline void SwFont::SetHighlightColor( const Color& aNewColor )
+{
+ m_aHighlightColor = aNewColor;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+// Used for the "continuous underline" feature.
+class SwUnderlineFont
+{
+ Point m_aPos;
+ TextFrameIndex m_nEnd;
+ std::unique_ptr<SwFont> m_pFont;
+
+public:
+ // sets the font which should paint the common baseline,
+ // index where continuous underline ends,
+ // and the starting point of the common baseline
+ SwUnderlineFont(SwFont& rFnt, TextFrameIndex nEnd, const Point& rPoint);
+ ~SwUnderlineFont();
+
+ SwFont& GetFont()
+ {
+ OSL_ENSURE( m_pFont, "No underline font" );
+ return *m_pFont;
+ }
+ const Point& GetPos() const { return m_aPos; }
+ TextFrameIndex GetEnd() const { return m_nEnd; }
+ // the x coordinate of the starting point has to be set for each portion
+ void SetPos( const Point& rPoint ) { m_aPos = rPoint; }
+};
+
+#ifdef DBG_UTIL
+
+class SvStatistics
+{
+public:
+ sal_uInt16 nGetTextSize;
+ sal_uInt16 nDrawText;
+ sal_uInt16 nGetStretchTextSize;
+ sal_uInt16 nDrawStretchText;
+ sal_uInt16 nChangeFont;
+
+ SvStatistics()
+ { nGetTextSize = nDrawText = nGetStretchTextSize = nDrawStretchText = nChangeFont = 0; }
+};
+
+// global variable, implemented in swfont.cxx
+extern SvStatistics g_SvStat;
+
+#define SV_STAT(nWhich) ++(g_SvStat.nWhich);
+
+
+#else
+#define SV_STAT(nWhich)
+#endif /* DBG_UTIL */
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swselectionlist.hxx b/sw/source/core/inc/swselectionlist.hxx
new file mode 100644
index 000000000..9eb390321
--- /dev/null
+++ b/sw/source/core/inc/swselectionlist.hxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWSELECTIONLIST_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWSELECTIONLIST_HXX
+
+#include <list>
+
+class SwPaM;
+class SwFrame;
+
+/** This class is used as parameter for creation of a block cursor selection
+
+ This class will be created by a block cursor. Its responsibility is
+ to collect a group of selected text portions which are part of a common
+ context.
+ Definition of context:
+ A page header is a context.
+ A page footer is a context.
+ A footnote is a context.
+ Every fly frame builds a context together with its linked colleagues.
+ The content of the page bodies builds a context.
+*/
+
+class SwSelectionList
+{
+ std::list<SwPaM*> m_aList; // container for the selected text portions
+ const SwFrame* m_pContext; // the context of these text portions
+public:
+ /** Ctor to create an empty list for a given context
+
+ @param pInitCxt
+ The frame (normally a SwTextFrame) where the block cursor selection starts,
+ it will be used to get the allowed context for the text selections.
+ */
+ explicit SwSelectionList(const SwFrame* pInitCxt);
+
+ /** Start of the container for the selected text portions
+ */
+ std::list<SwPaM*>::iterator getStart() { return m_aList.begin(); }
+
+ /** End of the container for the selected text portions
+ */
+ std::list<SwPaM*>::iterator getEnd() { return m_aList.end(); }
+
+ /** Adds a text portion to the selection list
+
+ @param pPam
+ represents a text portion to select
+ */
+ void insertPaM(SwPaM* pPam) { m_aList.push_front(pPam); }
+
+ /** Reports if the list does not contain any text portion
+
+ @return true, if list is empty
+ */
+ bool isEmpty() const { return m_aList.empty(); }
+
+ /** Checks if the context of the list is equal to the context of the frame
+
+ If the list does not have already a context, the context of the frame
+ will define the list's context.
+ If the list has already a context, it will be compared to the context of
+ the given frame.
+
+ @param pCheck
+ The frame to check
+
+ @return true, if the context of the frame is equal to the one of the list
+ */
+ bool checkContext(const SwFrame* pCheck);
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_SWSELECTIONLIST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swthreadjoiner.hxx b/sw/source/core/inc/swthreadjoiner.hxx
new file mode 100644
index 000000000..18e1e1193
--- /dev/null
+++ b/sw/source/core/inc/swthreadjoiner.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWTHREADJOINER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWTHREADJOINER_HXX
+
+#include <sal/types.h>
+
+namespace com::sun::star::uno
+{
+template <class interface_type> class Reference;
+}
+namespace com::sun::star::util
+{
+class XJobManager;
+}
+
+/** Testing */
+namespace SwThreadJoiner
+{
+css::uno::Reference<css::util::XJobManager>& GetThreadJoiner();
+
+void ReleaseThreadJoiner();
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/swthreadmanager.hxx b/sw/source/core/inc/swthreadmanager.hxx
new file mode 100644
index 000000000..6c478aec8
--- /dev/null
+++ b/sw/source/core/inc/swthreadmanager.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_SWTHREADMANAGER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_SWTHREADMANAGER_HXX
+
+#include <osl/interlck.h>
+
+#include <memory>
+
+namespace rtl { template <class reference_type> class Reference; }
+
+class ObservableThread;
+class ThreadManager;
+
+/** class to manage threads in Writer using a <ThreadManager> instance
+
+ #i73788#
+ Conforms the singleton pattern
+*/
+class SwThreadManager
+{
+ public:
+
+ static SwThreadManager& GetThreadManager();
+
+ static bool ExistsThreadManager();
+
+ // private: don't call!
+ SwThreadManager();
+ // private: don't call!
+ ~SwThreadManager();
+
+ oslInterlockedCount AddThread( const rtl::Reference< ObservableThread >& rThread );
+
+ void RemoveThread( const oslInterlockedCount nThreadID );
+
+ /** suspend the starting of threads
+
+ Suspending the starting of further threads is sensible during the
+ destruction of a Writer document.
+ */
+ void SuspendStartingOfThreads();
+
+ /** continues the starting of threads after it has been suspended
+ */
+ void ResumeStartingOfThreads();
+
+ bool StartingOfThreadsSuspended();
+
+ private:
+
+ SwThreadManager(SwThreadManager const&) = delete;
+ SwThreadManager& operator=(SwThreadManager const&) = delete;
+
+ static bool sbThreadManagerInstantiated;
+
+ std::unique_ptr<ThreadManager> mpThreadManagerImpl;
+
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/tabfrm.hxx b/sw/source/core/inc/tabfrm.hxx
new file mode 100644
index 000000000..9df5aac42
--- /dev/null
+++ b/sw/source/core/inc/tabfrm.hxx
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TABFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TABFRM_HXX
+
+#include "layfrm.hxx"
+#include "flowfrm.hxx"
+
+class SwTable;
+class SwBorderAttrs;
+class SwAttrSetChg;
+
+enum class SwTabFrameInvFlags : sal_uInt8
+{
+ NONE = 0x00,
+ InvalidatePrt = 0x02,
+ InvalidateIndNextPrt = 0x04,
+ InvalidatePrevPrt = 0x08,
+ SetIndNextCompletePaint = 0x10,
+ InvalidateBrowseWidth = 0x20,
+ InvalidatePos = 0x40,
+ InvalidateNextPos = 0x80,
+};
+
+namespace o3tl {
+ template<> struct typed_flags<SwTabFrameInvFlags> : is_typed_flags<SwTabFrameInvFlags, 0xfe> {};
+}
+
+/// SwTabFrame is one table in the document layout, containing rows (which contain cells).
+class SwTabFrame final: public SwLayoutFrame, public SwFlowFrame
+{
+ friend void CalcContent( SwLayoutFrame *pLay, bool bNoColl );
+
+ // does the special treatment for Get_[Next|Prev]Leaf()
+ using SwFrame::GetLeaf;
+
+ SwTable * m_pTable;
+
+ bool m_bComplete :1; /// Set entries for Repaint without needing to
+ /// set the base class' CompletePaint
+ /// With that we would want to avoid unnecessary
+ /// table repaints
+ bool m_bCalcLowers :1; /// For stability of the content in MakeAll
+ bool m_bLowersFormatted :1; /// Communication between MakeAll and Layact
+ bool m_bLockBackMove :1; /// The Master took care of the BackMove test
+ bool m_bResizeHTMLTable :1; /// Call the Resize of the HTMLTableLayout in the MakeAll
+ /// This is an optimization, so that we don't have to call
+ /// it in ContentFrame::Grow; there it might be called for
+ /// _every_ Cell
+
+ bool m_bONECalcLowers :1; /// Primarily for the StarONE SS
+ /// The Contents are formatted via Calc() on MakeAll in any
+ /// case. There are no further invalidations and that path can
+ /// hardly give any guarantees
+
+ bool m_bHasFollowFlowLine :1; /// Means that the first line in the follow
+ /// is indented to contain content from a broken
+ /// cell
+ bool m_bIsRebuildLastLine :1; /// Means that currently the last line of the
+ /// TabFrame is rebuilt. In this case we do not
+ // want any notification to the master table
+
+ bool m_bRestrictTableGrowth :1; // Usually, the table may grow infinitely,
+ // as the table can be split in SwTabFrame::MakeAll
+ // In MakeAll, this flag is set to indicate that
+ // the table may only grow inside its upper. This
+ // is necessary, in order to let the text flow into
+ // the FollowFlowLine
+
+ bool m_bRemoveFollowFlowLinePending :1;
+
+ // #i26945#
+ bool m_bConsiderObjsForMinCellHeight :1; // Usually, the floating screen objects
+ // are considered during the calculation
+ // for the minimal cell height.
+ // For the splitting table rows algorithm
+ // we need not to consider floating
+ // screen object for the preparation
+ // of the re-calculation of the
+ // last table row.
+ // #i26945#
+ bool m_bObjsDoesFit :1; // For splitting table rows algorithm, this boolean
+ // indicates, if the floating screen objects fits
+
+ bool m_bInRecalcLowerRow : 1;
+
+ /**
+ * Split() splits the Frame at the specified position: a Follow is
+ * created and constructed and inserted directly after this.
+ * Join() gets the Follow's content and destroys it.
+ */
+ bool Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowKeep );
+ void Join();
+
+ void UpdateAttr_(
+ const SfxPoolItem*,
+ const SfxPoolItem*, SwTabFrameInvFlags &,
+ SwAttrSetChg *pa = nullptr,
+ SwAttrSetChg *pb = nullptr );
+
+ virtual bool ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat ) override;
+
+ virtual void DestroyImpl() override;
+ virtual ~SwTabFrame() override;
+
+ virtual void MakeAll(vcl::RenderContext* pRenderContext) override;
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual void SwClientNotify(const SwModify&, const SfxHint&) override;
+ // only changes the Framesize, not the PrtArea size
+ virtual SwTwips GrowFrame ( SwTwips, bool bTst = false, bool bInfo = false ) override;
+ virtual const SwTabFrame* DynCastTabFrame() const override { return this; }
+
+public:
+ SwTabFrame( SwTable &, SwFrame* ); // calling RegistFlys always after creation _and_pasting!
+ SwTabFrame( SwTabFrame & ); // _only_ for the creation of follows
+
+ void JoinAndDelFollows(); // for DelFrames of the TableNodes!
+
+ // calls thr RegistFlys of the rows
+ void RegistFlys();
+
+ inline const SwTabFrame *GetFollow() const;
+ inline SwTabFrame *GetFollow();
+ SwTabFrame* FindMaster( bool bFirstMaster = false ) const;
+
+ virtual bool GetInfo( SfxPoolItem &rHint ) const override;
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ virtual void CheckDirection( bool bVert ) override;
+
+ virtual void Cut() override;
+ virtual void Paste( SwFrame* pParent, SwFrame* pSibling = nullptr ) override;
+
+ virtual bool Prepare( const PrepareHint ePrep = PrepareHint::Clear,
+ const void *pVoid = nullptr, bool bNotify = true ) override;
+
+ SwFrame *FindLastContentOrTable();
+ inline const SwFrame *FindLastContentOrTable() const;
+ SwContentFrame *FindLastContent();
+ inline const SwContentFrame *FindLastContent() const;
+
+ const SwTable *GetTable() const { return m_pTable; }
+ SwTable *GetTable() { return m_pTable; }
+
+ bool IsComplete() const { return m_bComplete; }
+ void SetComplete() { m_bComplete = true; }
+ void ResetComplete() { m_bComplete = false; }
+
+ bool IsLowersFormatted() const { return m_bLowersFormatted; }
+ void SetLowersFormatted(bool b) { m_bLowersFormatted = b; }
+
+ void SetCalcLowers() { m_bCalcLowers = true; } // use rarely
+ void SetResizeHTMLTable() { m_bResizeHTMLTable = true; } // same
+ void SetONECalcLowers() { m_bONECalcLowers = true; }
+
+ // Start: New stuff for breaking table rows
+
+ bool HasFollowFlowLine() const { return m_bHasFollowFlowLine; }
+ void SetFollowFlowLine(bool bNew) { m_bHasFollowFlowLine = bNew; }
+
+ bool IsRebuildLastLine() const { return m_bIsRebuildLastLine; }
+ void SetRebuildLastLine(bool bNew) { m_bIsRebuildLastLine = bNew; }
+
+ bool IsRestrictTableGrowth() const { return m_bRestrictTableGrowth; }
+ void SetRestrictTableGrowth( bool bNew ) { m_bRestrictTableGrowth = bNew; }
+
+ bool IsRemoveFollowFlowLinePending() const { return m_bRemoveFollowFlowLinePending; }
+ void SetRemoveFollowFlowLinePending(bool bNew) { m_bRemoveFollowFlowLinePending = bNew; }
+
+ bool IsInRecalcLowerRow() const
+ {
+ return m_bInRecalcLowerRow;
+ }
+ void SetInRecalcLowerRow( bool bNew )
+ {
+ m_bInRecalcLowerRow = bNew;
+ }
+
+ // #i26945#
+ bool IsConsiderObjsForMinCellHeight() const
+ {
+ return m_bConsiderObjsForMinCellHeight;
+ }
+ void SetConsiderObjsForMinCellHeight(bool const bConsiderObjsForMinCellHeight)
+ {
+ m_bConsiderObjsForMinCellHeight = bConsiderObjsForMinCellHeight;
+ }
+
+ // #i26945#
+ bool DoesObjsFit() const
+ {
+ return m_bObjsDoesFit;
+ }
+ void SetDoesObjsFit(bool const bObjsDoesFit)
+ {
+ m_bObjsDoesFit = bObjsDoesFit;
+ }
+
+ bool RemoveFollowFlowLine();
+
+ // End: New stuff for breaking table rows
+
+ bool CalcFlyOffsets(
+ SwTwips& rUpper,
+ tools::Long& rLeftOffset,
+ tools::Long& rRightOffset,
+ SwTwips * pSpaceBelowBottom) const;
+
+ SwTwips CalcHeightOfFirstContentLine() const;
+
+ bool IsInHeadline( const SwFrame& rFrame ) const;
+ SwRowFrame* GetFirstNonHeadlineRow() const;
+
+ bool IsLayoutSplitAllowed() const;
+
+ // #i29550#
+ bool IsCollapsingBorders() const;
+
+ sal_uInt16 GetBottomLineSize() const;
+
+ virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override;
+};
+
+inline const SwFrame *SwTabFrame::FindLastContentOrTable() const
+{
+ return const_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+}
+
+inline const SwContentFrame *SwTabFrame::FindLastContent() const
+{
+ return const_cast<SwTabFrame*>(this)->FindLastContent();
+}
+
+inline const SwTabFrame *SwTabFrame::GetFollow() const
+{
+ return static_cast<const SwTabFrame*>(SwFlowFrame::GetFollow());
+}
+inline SwTabFrame *SwTabFrame::GetFollow()
+{
+ return static_cast<SwTabFrame*>(SwFlowFrame::GetFollow());
+}
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_TABFRM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/tblrwcl.hxx b/sw/source/core/inc/tblrwcl.hxx
new file mode 100644
index 000000000..9cd6fa3eb
--- /dev/null
+++ b/sw/source/core/inc/tblrwcl.hxx
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TBLRWCL_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TBLRWCL_HXX
+
+#include <cstddef>
+#include <vector>
+
+#include <swtypes.hxx>
+#include <swtable.hxx>
+
+namespace editeng { class SvxBorderLine; }
+
+class SwDoc;
+class SwTableNode;
+class SwTableBoxFormat;
+class SwHistory;
+class SwContentNode;
+class SfxPoolItem;
+class SwShareBoxFormats;
+class SwFormatFrameSize;
+
+void sw_LineSetHeadCondColl( const SwTableLine* pLine );
+
+#ifdef DBG_UTIL
+void CheckBoxWidth( const SwTableLine& rLine, SwTwips nSize );
+#endif
+
+void InsTableBox( SwDoc& rDoc, SwTableNode* pTableNd,
+ SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat,
+ SwTableBox* pBox, sal_uInt16 nInsPos, sal_uInt16 nCnt = 1 );
+
+void DeleteBox_( SwTable& rTable, SwTableBox* pBox, SwUndo* pUndo,
+ bool bCalcNewSize, const bool bCorrBorder,
+ SwShareBoxFormats* pShareFormats = nullptr );
+
+/**
+ * Class for SplitTable
+ * Collects the uppermost or lowermost Lines of a Box from a Line in an array.
+ * We also store their positions.
+ *
+ * @see implementation in ndtbl.cxx
+ */
+class SwCollectTableLineBoxes
+{
+ std::vector<sal_uInt16> m_aPositionArr;
+ std::vector<SwTableBox*> m_Boxes;
+ SwHistory* m_pHistory;
+ SplitTable_HeadlineOption m_nMode;
+ sal_uInt16 m_nWidth;
+ bool m_bGetFromTop : 1;
+ bool m_bGetValues : 1;
+
+public:
+ SwCollectTableLineBoxes( bool bTop, SplitTable_HeadlineOption nMd = SplitTable_HeadlineOption::NONE, SwHistory* pHist=nullptr )
+ :
+ m_pHistory( pHist ), m_nMode( nMd ), m_nWidth( 0 ),
+ m_bGetFromTop( bTop ), m_bGetValues( true )
+
+ {}
+
+ void AddBox( const SwTableBox& rBox );
+ const SwTableBox* GetBoxOfPos( const SwTableBox& rBox );
+ void AddToUndoHistory( const SwContentNode& rNd );
+
+ size_t Count() const { return m_Boxes.size(); }
+ const SwTableBox& GetBox( std::size_t nPos, sal_uInt16* pWidth = nullptr ) const
+ {
+ // We need the EndPos of the column here!
+ if( pWidth )
+ *pWidth = (nPos+1 == m_aPositionArr.size()) ? m_nWidth
+ : m_aPositionArr[ nPos+1 ];
+ return *m_Boxes[ nPos ];
+ }
+
+ bool IsGetFromTop() const { return m_bGetFromTop; }
+ bool IsGetValues() const { return m_bGetValues; }
+
+ SplitTable_HeadlineOption GetMode() const { return m_nMode; }
+ void SetValues( bool bFlag ) { m_bGetValues = false; m_nWidth = 0;
+ m_bGetFromTop = bFlag; }
+ bool Resize( sal_uInt16 nOffset, sal_uInt16 nWidth );
+};
+
+void sw_Box_CollectBox( const SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara );
+bool sw_Line_CollectBox( const SwTableLine*& rpLine, void* pPara );
+
+void sw_BoxSetSplitBoxFormats( SwTableBox* pBox, SwCollectTableLineBoxes* pSplPara );
+
+/**
+ * This structure is needed by Undo to restore row span attributes
+ * when a table has been split into two tables
+ */
+struct SwSaveRowSpan
+{
+ sal_uInt16 mnSplitLine; // the line number where the table has been split
+ std::vector< tools::Long > mnRowSpans; // the row span attributes in this line
+ SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn );
+};
+
+struct SwGCLineBorder
+{
+ const SwTableLines* pLines;
+ SwShareBoxFormats* pShareFormats;
+ sal_uInt16 nLinePos;
+
+ SwGCLineBorder( const SwTable& rTable )
+ : pLines( &rTable.GetTabLines() ), pShareFormats(nullptr), nLinePos( 0 ) {}
+
+ SwGCLineBorder( const SwTableBox& rBox )
+ : pLines( &rBox.GetTabLines() ), pShareFormats(nullptr), nLinePos( 0 ) {}
+ bool IsLastLine() const { return nLinePos + 1 >= o3tl::narrowing<sal_uInt16>(pLines->size()); }
+};
+
+class SwGCBorder_BoxBrd
+{
+ const editeng::SvxBorderLine* m_pBorderLine;
+ bool m_bAnyBorderFind;
+public:
+ SwGCBorder_BoxBrd() : m_pBorderLine( nullptr ), m_bAnyBorderFind( false ) {}
+
+ void SetBorder( const editeng::SvxBorderLine& rBorderLine )
+ { m_pBorderLine = &rBorderLine; m_bAnyBorderFind = false; }
+
+ /**
+ * Check whether the left Border is the same as the set one
+ * @returns false if no Border was set
+ */
+ bool CheckLeftBorderOfFormat( const SwFrameFormat& rFormat );
+
+ bool IsAnyBorderFound() const { return m_bAnyBorderFind; }
+};
+
+void sw_GC_Line_Border( const SwTableLine* pLine, SwGCLineBorder* pGCPara );
+
+class SwShareBoxFormat
+{
+ const SwFrameFormat* m_pOldFormat;
+ std::vector<SwFrameFormat*> m_aNewFormats;
+
+public:
+ SwShareBoxFormat( const SwFrameFormat& rFormat )
+ : m_pOldFormat( &rFormat )
+ {}
+
+ const SwFrameFormat& GetOldFormat() const { return *m_pOldFormat; }
+
+ SwFrameFormat* GetFormat( tools::Long nWidth ) const;
+ SwFrameFormat* GetFormat( const SfxPoolItem& rItem ) const;
+ void AddFormat( SwFrameFormat& rFormat );
+ /// @returns true, if we can delete
+ bool RemoveFormat( const SwFrameFormat& rFormat );
+};
+
+class SwShareBoxFormats
+{
+ std::vector<SwShareBoxFormat> m_ShareArr;
+
+ bool Seek_Entry( const SwFrameFormat& rFormat, sal_uInt16* pPos ) const;
+
+ void ChangeFrameFormat( SwTableBox* pBox, SwTableLine* pLn, SwFrameFormat& rFormat );
+
+public:
+ SwShareBoxFormats() {}
+ ~SwShareBoxFormats();
+
+ SwFrameFormat* GetFormat( const SwFrameFormat& rFormat, tools::Long nWidth ) const;
+ SwFrameFormat* GetFormat( const SwFrameFormat& rFormat, const SfxPoolItem& ) const;
+
+ void AddFormat( const SwFrameFormat& rOld, SwFrameFormat& rNew );
+
+ void SetSize( SwTableBox& rBox, const SwFormatFrameSize& rSz );
+ void SetAttr( SwTableBox& rBox, const SfxPoolItem& rItem );
+ void SetAttr( SwTableLine& rLine, const SfxPoolItem& rItem );
+
+ void RemoveFormat( const SwFrameFormat& rFormat );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/textapi.hxx b/sw/source/core/inc/textapi.hxx
new file mode 100644
index 000000000..48586b5fa
--- /dev/null
+++ b/sw/source/core/inc/textapi.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TEXTAPI_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TEXTAPI_HXX
+
+#include <editeng/unoedsrc.hxx>
+#include <editeng/unotext.hxx>
+#include <editeng/outlobj.hxx>
+
+class SwDoc;
+
+struct SwTextAPIEditSource_Impl;
+class SwTextAPIEditSource final : public SvxEditSource
+{
+ SwTextAPIEditSource_Impl* m_pImpl;
+
+ virtual std::unique_ptr<SvxEditSource> Clone() const override;
+ virtual SvxTextForwarder* GetTextForwarder() override;
+ virtual void UpdateData() override;
+ explicit SwTextAPIEditSource( const SwTextAPIEditSource& rSource );
+
+public:
+ SwTextAPIEditSource(SwDoc* pDoc);
+ virtual ~SwTextAPIEditSource() override;
+
+ void Dispose();
+ void SetText( OutlinerParaObject const & rText );
+ void SetString( const OUString& rText );
+ std::optional<OutlinerParaObject> CreateText();
+ OUString GetText() const;
+};
+
+class SwTextAPIObject final : public SvxUnoText
+{
+ std::unique_ptr<SwTextAPIEditSource> m_pSource;
+public:
+ SwTextAPIObject( std::unique_ptr<SwTextAPIEditSource> p);
+ virtual ~SwTextAPIObject() noexcept override;
+ void DisposeEditSource() { m_pSource->Dispose(); }
+ std::optional<OutlinerParaObject> CreateText() { return m_pSource->CreateText(); }
+ void SetString( const OUString& rText ) { m_pSource->SetString( rText ); }
+ void SetText( OutlinerParaObject const & rText ) { m_pSource->SetText( rText ); }
+ OUString GetText() const { return m_pSource->GetText(); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/threadlistener.hxx b/sw/source/core/inc/threadlistener.hxx
new file mode 100644
index 000000000..071de465c
--- /dev/null
+++ b/sw/source/core/inc/threadlistener.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_THREADLISTENER_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_THREADLISTENER_HXX
+
+#include "ifinishedthreadlistener.hxx"
+
+class ThreadManager;
+class ObservableThread;
+
+/** helper class to observe threads
+
+ OD 2007-03-30 #i73788#
+ An instance of this class can be used to observe a thread in order to
+ be notified, if the thread has finished its work. The notification is
+ directly forward to its owner - an instance of ThreadManager
+ Note:
+ - A thread can only have one or none listener.
+ - The notification is performed via the ThreadID
+*/
+class ThreadListener final : public IFinishedThreadListener
+{
+ public:
+
+ ThreadListener( ThreadManager& rThreadListenerOwner );
+ virtual ~ThreadListener() override;
+
+ void ListenToThread( const oslInterlockedCount nThreadID,
+ ObservableThread& rThread );
+
+ virtual void NotifyAboutFinishedThread( const oslInterlockedCount nThreadID ) override;
+
+ private:
+
+ ThreadManager& mrThreadListenerOwner;
+};
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/tocntntanchoredobjectposition.hxx b/sw/source/core/inc/tocntntanchoredobjectposition.hxx
new file mode 100644
index 000000000..54fdecc58
--- /dev/null
+++ b/sw/source/core/inc/tocntntanchoredobjectposition.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TOCNTNTANCHOREDOBJECTPOSITION_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TOCNTNTANCHOREDOBJECTPOSITION_HXX
+#include "anchoredobjectposition.hxx"
+
+class SwFrame;
+class SwTextFrame;
+class SwLayoutFrame;
+class SwRect;
+
+namespace objectpositioning
+{
+ class SwToContentAnchoredObjectPosition final : public SwAnchoredObjectPosition
+ {
+ private:
+ // calculated data for object position
+ const SwLayoutFrame* mpVertPosOrientFrame;
+ // #i26791#
+ // determine offset to frame anchor position according to the
+ // positioning alignments
+ Point maOffsetToFrameAnchorPos;
+
+ // data for calculation of position
+ bool mbAnchorToChar;
+ const SwFrame* mpToCharOrientFrame;
+ const SwRect* mpToCharRect;
+ SwTwips mnToCharTopOfLine;
+
+ virtual bool IsAnchoredToChar() const override;
+ virtual const SwFrame* ToCharOrientFrame() const override;
+ virtual const SwRect* ToCharRect() const override;
+ // #i22341#
+ virtual SwTwips ToCharTopOfLine() const override;
+
+ // method to cast <SwAnchoredObjectPosition::GetAnchorFrame()> to
+ // the needed type
+ SwTextFrame& GetAnchorTextFrame() const;
+
+ /** determine frame for horizontal position
+
+ if the given proposed frame is a content frame, the proposed
+ frame is returned.
+ otherwise (given proposed frame is a layout frame),
+ the lower content frames of the proposed frame are checked
+ for the first, that the anchor or a follow of the anchor.
+ If none is found, the proposed frame is returned.
+
+ @param _pProposedFrame
+ input parameter - proposed frame for horizontal position
+
+ @return constant reference to <SwFrame> object, at which the
+ horizontal position is determined.
+ */
+ const SwFrame& GetHoriVirtualAnchor( const SwLayoutFrame& _pProposedFrame ) const;
+
+ public:
+ SwToContentAnchoredObjectPosition( SdrObject& _rDrawObj );
+ virtual ~SwToContentAnchoredObjectPosition() override;
+
+ /** calculate position of object
+ */
+ virtual void CalcPosition() override;
+
+ /** frame, at which the vertical position is oriented at
+ */
+ const SwLayoutFrame& GetVertPosOrientFrame() const { return *mpVertPosOrientFrame;}
+
+ /// In case overlap is not allowed, re-position the current object.
+ void CalcOverlap(const SwTextFrame* pAnchorFrameForVertPos, Point& rRelPos,
+ const SwTwips nTopOfAnch);
+ };
+} // namespace objectpositioning
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/tolayoutanchoredobjectposition.hxx b/sw/source/core/inc/tolayoutanchoredobjectposition.hxx
new file mode 100644
index 000000000..256e7378d
--- /dev/null
+++ b/sw/source/core/inc/tolayoutanchoredobjectposition.hxx
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TOLAYOUTANCHOREDOBJECTPOSITION_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TOLAYOUTANCHOREDOBJECTPOSITION_HXX
+#include "anchoredobjectposition.hxx"
+
+namespace objectpositioning
+{
+ class SwToLayoutAnchoredObjectPosition final : public SwAnchoredObjectPosition
+ {
+ private:
+ // calculated data for object position type TO_LAYOUT
+ Point maRelPos;
+
+ // #i26791#
+ // determine offset to frame anchor position according to the
+ // positioning alignments
+ Point maOffsetToFrameAnchorPos;
+
+ public:
+ SwToLayoutAnchoredObjectPosition( SdrObject& _rDrawObj );
+ virtual ~SwToLayoutAnchoredObjectPosition() override;
+
+ /** calculate position for object
+ */
+ virtual void CalcPosition() override;
+
+ /** calculated relative position for object
+ */
+ const Point& GetRelPos() const { return maRelPos;}
+ };
+} // namespace objectpositioning
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/txmsrt.hxx b/sw/source/core/inc/txmsrt.hxx
new file mode 100644
index 000000000..bd968f87f
--- /dev/null
+++ b/sw/source/core/inc/txmsrt.hxx
@@ -0,0 +1,308 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TXMSRT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TXMSRT_HXX
+
+#include <i18nlangtag/lang.h>
+#include <nodeoffset.hxx>
+#include <tox.hxx>
+
+#include <com/sun/star/lang/Locale.hpp>
+
+class CharClass;
+class SwContentNode;
+class SwTextNode;
+class SwTextTOXMark;
+class SwIndex;
+class SwFormatField;
+class SwRootFrame;
+class IndexEntrySupplierWrapper;
+
+enum TOXSortType
+{
+ TOX_SORT_INDEX,
+ TOX_SORT_CUSTOM,
+ TOX_SORT_CONTENT,
+ TOX_SORT_PARA,
+ TOX_SORT_TABLE,
+ TOX_SORT_AUTHORITY
+};
+
+struct SwTOXSource
+{
+ const SwContentNode* pNd;
+ sal_Int32 nPos;
+ bool bMainEntry;
+
+ SwTOXSource( const SwContentNode* pNode, sal_Int32 n, bool bMain )
+ : pNd(pNode), nPos(n), bMainEntry(bMain)
+ {
+ }
+};
+
+struct TextAndReading
+{
+ OUString sText;
+ OUString sReading;
+
+ TextAndReading() {}
+
+ TextAndReading(const OUString& rText, const OUString& rReading)
+ : sText(rText)
+ , sReading(rReading)
+ {}
+};
+
+class SwTOXInternational
+{
+ std::unique_ptr<IndexEntrySupplierWrapper> m_pIndexWrapper;
+ std::unique_ptr<CharClass> m_pCharClass;
+ LanguageType m_eLang;
+ OUString m_sSortAlgorithm;
+ SwTOIOptions m_nOptions;
+
+ void Init();
+
+public:
+ SwTOXInternational( LanguageType nLang, SwTOIOptions nOptions,
+ const OUString& rSortAlgorithm );
+ SwTOXInternational( const SwTOXInternational& );
+ ~SwTOXInternational();
+
+ sal_Int32 Compare( const TextAndReading& rTaR1,
+ const css::lang::Locale& rLocale1,
+ const TextAndReading& rTaR2,
+ const css::lang::Locale& rLocale2 ) const;
+
+ bool IsEqual( const TextAndReading& rTaR1,
+ const css::lang::Locale& rLocale1,
+ const TextAndReading& rTaR2,
+ const css::lang::Locale& rLocale2 ) const
+ {
+ return 0 == Compare( rTaR1, rLocale1, rTaR2, rLocale2 );
+ }
+
+ bool IsLess( const TextAndReading& rTaR1,
+ const css::lang::Locale& rLocale1,
+ const TextAndReading& rTaR2,
+ const css::lang::Locale& rLocale2 ) const
+ {
+ return -1 == Compare( rTaR1, rLocale1, rTaR2, rLocale2 );
+ }
+
+ OUString GetIndexKey( const TextAndReading& rTaR,
+ const css::lang::Locale& rLcl ) const;
+
+ OUString GetFollowingText( bool bMorePages ) const;
+
+ OUString ToUpper( const OUString& rStr, sal_Int32 nPos ) const;
+ inline bool IsNumeric( const OUString& rStr ) const;
+};
+
+/**
+ * Class for sorting directories
+ */
+struct SwTOXSortTabBase
+{
+ bool operator==(const SwTOXSortTabBase&) const = delete;
+ bool operator<(const SwTOXSortTabBase&) const = delete;
+
+ std::vector<SwTOXSource> aTOXSources;
+ css::lang::Locale aLocale;
+ const SwTextNode* pTOXNd;
+ const SwTextTOXMark* pTextMark;
+ const SwTOXInternational* pTOXIntl;
+ SwNodeOffset nPos;
+ sal_Int32 nCntPos;
+ sal_uInt16 nType;
+ static SwTOIOptions nOpt;
+
+ SwTOXSortTabBase( TOXSortType nType,
+ const SwContentNode* pTOXSrc,
+ const SwTextTOXMark* pTextMark,
+ const SwTOXInternational* pIntl,
+ const css::lang::Locale* pLocale = nullptr );
+ virtual ~SwTOXSortTabBase() {}
+
+ sal_uInt16 GetType() const { return nType; }
+ static SwTOIOptions GetOptions() { return nOpt; }
+
+ virtual void FillText(SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16 nAuthField, SwRootFrame const* pLayout) const;
+ virtual sal_uInt16 GetLevel() const = 0;
+ virtual bool equivalent( const SwTOXSortTabBase& );
+ virtual bool sort_lt( const SwTOXSortTabBase& );
+
+ virtual std::pair<OUString, bool> GetURL(SwRootFrame const*const pLayout) const;
+
+ virtual bool IsFullPara() const;
+
+ // must be called
+ inline void InitText(SwRootFrame const*const pLayout);
+ inline TextAndReading const & GetText() const;
+ inline const css::lang::Locale& GetLocale() const;
+
+private:
+ bool m_bValidText;
+ TextAndReading m_aSort;
+
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const = 0;
+};
+
+inline void SwTOXSortTabBase::InitText(SwRootFrame const*const pLayout)
+{
+ // 'this' is 'SwTOXSortTabBase const*', so the virtual
+ // mechanism will call the derived class' GetText_Impl
+ assert(!m_bValidText);
+ m_aSort = GetText_Impl(pLayout);
+ m_bValidText = true;
+}
+
+inline TextAndReading const & SwTOXSortTabBase::GetText() const
+{
+ assert(m_bValidText);
+ return m_aSort;
+}
+
+inline const css::lang::Locale& SwTOXSortTabBase::GetLocale() const
+{
+ return aLocale;
+}
+
+/**
+ * For sorting by text
+ */
+struct SwTOXIndex final : public SwTOXSortTabBase
+{
+ SwTOXIndex( const SwTextNode&, const SwTextTOXMark*, SwTOIOptions nOptions, sal_uInt8 nKeyLevel,
+ const SwTOXInternational& rIntl,
+ const css::lang::Locale& rLocale );
+
+ virtual void FillText(SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16 nAuthField, SwRootFrame const* pLayout) const override;
+ virtual sal_uInt16 GetLevel() const override;
+ virtual bool equivalent( const SwTOXSortTabBase& ) override;
+ virtual bool sort_lt( const SwTOXSortTabBase& ) override;
+
+private:
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override;
+
+ sal_uInt8 nKeyLevel;
+};
+
+struct SwTOXCustom final : public SwTOXSortTabBase
+{
+ SwTOXCustom( const TextAndReading& rKey, sal_uInt16 nLevel,
+ const SwTOXInternational& rIntl,
+ const css::lang::Locale& rLocale );
+
+ virtual sal_uInt16 GetLevel() const override;
+ virtual bool equivalent( const SwTOXSortTabBase& ) override;
+ virtual bool sort_lt( const SwTOXSortTabBase& ) override;
+
+private:
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override;
+
+ TextAndReading m_aKey;
+ sal_uInt16 nLev;
+};
+
+/**
+ * For sorting by position
+ */
+struct SwTOXContent final : public SwTOXSortTabBase
+{
+ SwTOXContent( const SwTextNode&, const SwTextTOXMark*,
+ const SwTOXInternational& rIntl );
+
+ virtual void FillText(SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16 nAuthField, SwRootFrame const* pLayout) const override;
+ virtual sal_uInt16 GetLevel() const override;
+private:
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override;
+
+};
+
+struct SwTOXPara final : public SwTOXSortTabBase
+{
+ SwTOXPara(SwContentNode&, SwTOXElement,
+ sal_uInt16 nLevel = FORM_ALPHA_DELIMITER,
+ const OUString& sSeqName = OUString());
+
+ void SetStartIndex(sal_Int32 nSet) { nStartIndex = nSet; }
+ void SetEndIndex(sal_Int32 nSet) { nEndIndex = nSet; }
+
+ virtual void FillText(SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16 nAuthField, SwRootFrame const* pLayout) const override;
+ virtual sal_uInt16 GetLevel() const override;
+
+ virtual std::pair<OUString, bool> GetURL(SwRootFrame const*const pLayout) const override;
+ virtual bool IsFullPara() const override;
+private:
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override;
+
+ SwTOXElement eType;
+ sal_uInt16 m_nLevel;
+ sal_Int32 nStartIndex;
+ sal_Int32 nEndIndex;
+ OUString m_sSequenceName;
+};
+
+struct SwTOXTable final : public SwTOXSortTabBase
+{
+ SwTOXTable( const SwContentNode& rNd );
+
+ void SetLevel(sal_uInt16 nSet){nLevel = nSet;}
+
+ virtual sal_uInt16 GetLevel() const override;
+
+ virtual std::pair<OUString, bool> GetURL(SwRootFrame const*const pLayout) const override;
+
+private:
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override;
+
+ sal_uInt16 nLevel;
+};
+
+/// Represents one row in the bibliography table.
+struct SwTOXAuthority final : public SwTOXSortTabBase
+{
+private:
+ SwFormatField& m_rField;
+ virtual void FillText(SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16 nAuthField, SwRootFrame const* pLayout) const override;
+ virtual TextAndReading GetText_Impl(SwRootFrame const* pLayout) const override;
+
+public:
+ SwTOXAuthority( const SwContentNode& rNd, SwFormatField& rField, const SwTOXInternational& rIntl );
+
+ SwFormatField& GetFieldFormat() {return m_rField;}
+
+ virtual bool equivalent( const SwTOXSortTabBase& ) override;
+ virtual bool sort_lt( const SwTOXSortTabBase& ) override;
+ virtual sal_uInt16 GetLevel() const override;
+ OUString GetText(sal_uInt16 nAuthField, const SwRootFrame* pLayout) const;
+
+ /// Gets the URL of the underlying SwAuthEntry, ignoring its page number.
+ static OUString GetSourceURL(const OUString& rText);
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_TXMSRT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/txtfly.hxx b/sw/source/core/inc/txtfly.hxx
new file mode 100644
index 000000000..37d78e3c8
--- /dev/null
+++ b/sw/source/core/inc/txtfly.hxx
@@ -0,0 +1,382 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <editeng/txtrange.hxx>
+#include <swtypes.hxx>
+#include <swrect.hxx>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <nodeoffset.hxx>
+#include <memory>
+#include <vector>
+
+class OutputDevice;
+class SwPageFrame;
+class SdrObject;
+class SwFormat;
+class SwAnchoredObject;
+class SwTextFrame;
+class SwDrawTextInfo;
+class SwContourCache;
+class SwBreakPortion;
+class SwTextFormatInfo;
+
+typedef std::vector< SwAnchoredObject* > SwAnchoredObjList;
+
+/** Contour-cache global variable, initialized/destroyed in txtinit.cxx
+ and needed in txtfly.cxx by text wrapping.
+ */
+extern SwContourCache *pContourCache;
+
+#define POLY_CNT 20
+#define POLY_MIN 5
+#define POLY_MAX 4000
+
+void ClrContourCache( const SdrObject *pObj );
+
+class SwContourCache
+{
+ friend void ClrContourCache();
+ struct CacheItem
+ {
+ const SdrObject *mpSdrObj;
+ std::unique_ptr<TextRanger> mxTextRanger;
+ };
+ std::vector<CacheItem> mvItems;
+ tools::Long mnPointCount;
+ SwRect ContourRect( const SwFormat* pFormat, const SdrObject* pObj,
+ const SwTextFrame* pFrame, const SwRect &rLine, const tools::Long nXPos,
+ const bool bRight );
+
+public:
+ SwContourCache();
+ ~SwContourCache();
+ const SdrObject* GetObject( sal_uInt16 nPos ) const{ return mvItems[ nPos ].mpSdrObj; }
+ sal_uInt16 GetCount() const { return mvItems.size(); }
+ void ClrObject( sal_uInt16 nPos );
+
+ /**
+ Computes the rectangle that will cover the object in the given line.
+
+ For _non_ contour-flow objects, this is simply the overlap area of
+ BoundRect (including spacing), and the line, for contour-flow,
+ the tools::PolyPolygon of the object gets traversed
+ */
+ static SwRect CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
+ const SwRect &rLine,
+ const SwTextFrame* pFrame,
+ const tools::Long nXPos,
+ const bool bRight );
+};
+
+/**
+ The purpose of this class is to be the universal interface between
+ formatting/text output and the possibly overlapping free-flying frames.
+ During formatting the formatter gets the information from SwTextFly, whether
+ a certain area is present by the attributes of an overlapping frame.
+ Such areas are represented by dummy portions.
+
+ The whole text output and touch-up is, again, forwarded to a SwTextFly.
+ This one decides, whether parts of the text need to be clipped and splits
+ the areas for e.g. a DrawRect.
+
+ Please note that all free-flying frames are located in a PtrArray, sorted
+ by TopLeft.
+
+ Internally we always use document-global values. The IN and OUT parameters
+ are, however, adjusted to the needs of the LineIter most of the time. That
+ is: they are converted to frame- and window-local coordinates.
+ If multiple frames with wrap attributes are located on the same line, we get
+ the following settings for the text flow:
+
+ L/R P L R N
+ P -P-P- -P-L -P R- -P N
+ L -L P- -L L -L R- -L N
+ R R-P- R-L R R- R N
+ N N P- N L N R- N N
+
+ (P=parallel, L=left, R=right, N=no wrap)
+
+ We can describe the behaviour as follows:
+ Every frame can push away text, with the restriction that it only has influence
+ until the next frame.
+ */
+class SwTextFly
+{
+ const SwPageFrame * m_pPage;
+ const SwAnchoredObject * mpCurrAnchoredObj;
+ const SwTextFrame * m_pCurrFrame;
+ const SwTextFrame * m_pMaster;
+ std::unique_ptr<SwAnchoredObjList> mpAnchoredObjList;
+
+ tools::Long m_nMinBottom;
+ tools::Long m_nNextTop; /// Stores the upper edge of the "next" frame
+ SwNodeOffset m_nCurrFrameNodeIndex;
+
+ bool m_bOn : 1;
+ bool m_bTopRule: 1;
+ bool mbIgnoreCurrentFrame: 1;
+ bool mbIgnoreContour: 1;
+
+ /** boolean, indicating if objects in page header|footer are considered for
+ text frames not in page header|footer.
+ */
+ bool mbIgnoreObjsInHeaderFooter: 1;
+
+ /**
+ This method will be called during the LineIter formatting
+ \li to compute the position of the next \c FlyPortion
+ \li remember new overlappings after a change of the line height.
+
+ \param[in] rPortion
+ Scope: document global.
+ */
+ SwRect GetFrame_( const SwRect &rPortion ) const;
+
+ SwAnchoredObjList* InitAnchoredObjList();
+
+ SwAnchoredObjList* GetAnchoredObjList() const;
+
+ /**
+ Look for the first object which overlaps with the rectangle.
+ Iterates over the anchored object list mpAnchoredObjList.
+ */
+ bool ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const;
+
+ /**
+ \li There is less than 2cm space on both sides for the text:
+ no surround (css::text::WrapTextMode_NONE)
+
+ \li There is more than 2cm space on only one side:
+ surround on that side (css::text::WrapTextMode_LEFT or css::text::WrapTextMode_RIGHT)
+
+ \li There is more than 2cm space on both sides, the object is
+ larger than 1.5cm: surround on the wider side
+ (css::text::WrapTextMode_LEFT or css::text::WrapTextMode_RIGHT)
+
+ \li There is more than 2cm space on both sides and the object
+ width is less than 1.5cm: both sides surround (css::text::WrapTextMode_PARALLEL)
+ */
+ css::text::WrapTextMode GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const;
+
+ /**
+ The right margin is the right margin or it is determined by the
+ next object standing on the line.
+ */
+ void CalcRightMargin( SwRect &rFly,
+ SwAnchoredObjList::size_type nPos,
+ const SwRect &rLine ) const;
+
+ /**
+ The left margin is the left margin of the current PrintArea or
+ it is determined by the last FlyFrame, which stands on the line.
+ */
+ void CalcLeftMargin( SwRect &rFly,
+ SwAnchoredObjList::size_type nPos,
+ const SwRect &rLine ) const;
+
+ /**
+ \return the position in sorted array
+ */
+ SwAnchoredObjList::size_type GetPos( const SwAnchoredObject* pAnchoredObj ) const;
+
+ bool GetTop( const SwAnchoredObject* _pAnchoredObj,
+ const bool bInFootnote,
+ const bool bInFooterOrHeader );
+
+ SwTwips CalcMinBottom() const;
+
+ const SwTextFrame* GetMaster_();
+
+public:
+
+ SwTextFly();
+ SwTextFly( const SwTextFrame *pFrame );
+ SwTextFly( const SwTextFly& rTextFly );
+ ~SwTextFly();
+
+ void CtorInitTextFly( const SwTextFrame *pFrame );
+
+ void SetTopRule();
+
+ SwRect GetFrame( const SwRect &rPortion ) const;
+ bool IsOn() const;
+
+ /**
+ If there is no flying object frame standing in rRect (usually the current row),
+ then we are turning ourself off.
+
+ \param rRect is global to the document!
+ */
+ bool Relax( const SwRect &rRect );
+ bool Relax();
+
+ SwTwips GetMinBottom() const;
+
+ /// Gets the maximum of the fly frame bottoms.
+ SwTwips GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const;
+
+ const SwTextFrame* GetMaster() const;
+
+ // This temporary variable needs to be manipulated in const methods
+ tools::Long GetNextTop() const;
+ void SetNextTop( tools::Long nNew ) const;
+
+ /**
+ Determines the demanded rectangle for an anchored object,
+ considering its surround for text wrapping.
+
+ \param pAnchoredObj the object for which to get the bounds
+ \param rLine the bounds of the line to format
+
+ \return the flying object bounds
+ */
+ SwRect AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj,
+ const SwRect& rRect ) const;
+
+ /**
+ This method is called by DrawText().
+
+ Ensures that the overlapping frames (except the transparent frames) won't
+ be scribbled by setting clip regions so that only the portions that are not
+ in the area of FlyFrames that are opaque and above the current frame will
+ be output.
+
+ DrawText() takes over the on optimization!
+ */
+ void DrawTextOpaque( SwDrawTextInfo &rInf );
+
+ /**
+ Two subtleties needs to be mentioned:
+ \li DrawRect() is allowed over the ClipRects
+ \li FlyToRect() returns bigger values than the frame data
+
+ Ensure that the overlapping frames (except the transparent frames)
+ won't be scribbled
+ */
+ void DrawFlyRect( OutputDevice* pOut, const SwRect &rRect );
+
+ /**
+ Used to switch off the SwTextFly when there is no overlapping object (Relax).
+
+ \param[in] the line area
+ \return whether the line will be overlapped by a frame
+ */
+ bool IsAnyFrame( const SwRect &rLine ) const;
+
+ /**
+ Same as IsAnyFrame(const SwRect&), but uses the current frame print
+ area
+ */
+ bool IsAnyFrame() const;
+
+ /**
+ true when a frame or DrawObj must be taken in account. The optimizations
+ like Paint/FormatEmpty for empty sentences or the virtual OutputDevice can
+ be used only when false is returned.
+
+ \param rRect
+ The rectangle can be empty, the current frame is then used. The value is
+ global to the document.
+ */
+ bool IsAnyObj( const SwRect& rRect ) const;
+
+ void SetIgnoreCurrentFrame( bool bNew );
+ void SetIgnoreContour( bool bNew );
+
+ void SetIgnoreObjsInHeaderFooter( const bool bNew );
+};
+
+inline SwAnchoredObjList* SwTextFly::GetAnchoredObjList() const
+{
+ return mpAnchoredObjList
+ ? mpAnchoredObjList.get()
+ : const_cast<SwTextFly*>(this)->InitAnchoredObjList();
+}
+
+inline void SwTextFly::SetTopRule()
+{
+ m_bTopRule = false;
+}
+
+inline bool SwTextFly::IsOn() const
+{
+ return m_bOn;
+}
+
+inline bool SwTextFly::Relax( const SwRect &rRect )
+{
+ if (m_bOn)
+ {
+ m_bOn = IsAnyFrame( rRect );
+ }
+ return m_bOn;
+}
+
+inline bool SwTextFly::Relax()
+{
+ if (m_bOn)
+ {
+ m_bOn = IsAnyFrame();
+ }
+ return m_bOn;
+}
+
+inline SwTwips SwTextFly::GetMinBottom() const
+{
+ return mpAnchoredObjList ? m_nMinBottom : CalcMinBottom();
+}
+
+inline const SwTextFrame* SwTextFly::GetMaster() const
+{
+ return m_pMaster ? m_pMaster : const_cast<SwTextFly*>(this)->GetMaster_();
+}
+
+inline tools::Long SwTextFly::GetNextTop() const
+{
+ return m_nNextTop;
+}
+
+inline void SwTextFly::SetNextTop( tools::Long nNew ) const
+{
+ const_cast<SwTextFly*>(this)->m_nNextTop = nNew;
+}
+
+inline SwRect SwTextFly::GetFrame( const SwRect &rRect ) const
+{
+ return m_bOn ? GetFrame_( rRect ) : SwRect();
+}
+
+inline void SwTextFly::SetIgnoreCurrentFrame( bool bNew )
+{
+ mbIgnoreCurrentFrame = bNew;
+}
+
+inline void SwTextFly::SetIgnoreContour( bool bNew )
+{
+ mbIgnoreContour = bNew;
+}
+
+inline void SwTextFly::SetIgnoreObjsInHeaderFooter( const bool bNew )
+{
+ mbIgnoreObjsInHeaderFooter = bNew;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/txtfrm.hxx b/sw/source/core/inc/txtfrm.hxx
new file mode 100644
index 000000000..0ff2321bc
--- /dev/null
+++ b/sw/source/core/inc/txtfrm.hxx
@@ -0,0 +1,1032 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_TXTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_TXTFRM_HXX
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include "cntfrm.hxx"
+#include "TextFrameIndex.hxx"
+#include <nodeoffset.hxx>
+
+#include <set>
+
+namespace com::sun::star::linguistic2 { class XHyphenatedWord; }
+
+namespace sw::mark { class IMark; }
+class SwCharRange;
+class SwInsText;
+class SwTextNode;
+class SwTextAttrEnd;
+class SwTextFormatter;
+class SwTextFormatInfo;
+class SwParaPortion;
+class WidowsAndOrphans;
+class SwTextFootnote;
+class SwInterHyphInfo; // Hyphenate()
+class SwCache;
+class SwBorderAttrs;
+class SwFrameFormat;
+struct SwCursorMoveState;
+struct SwFillData;
+class SwPortionHandler;
+class SwScriptInfo;
+enum class ExpandMode;
+class SwTextAttr;
+class SwWrtShell;
+
+#define NON_PRINTING_CHARACTER_COLOR Color(0x26, 0x8b, 0xd2)
+
+/// a clone of SwInterHyphInfo, but with TextFrameIndex instead of node index
+class SwInterHyphInfoTextFrame
+{
+private:
+ /// output: hyphenated word
+ css::uno::Reference<css::linguistic2::XHyphenatedWord> m_xHyphWord;
+public:
+ /// input: requested range to hyphenate
+ TextFrameIndex m_nStart;
+ TextFrameIndex m_nEnd;
+ /// output: found word
+ TextFrameIndex m_nWordStart;
+ TextFrameIndex m_nWordLen;
+
+ SwInterHyphInfoTextFrame(SwTextFrame const& rFrame,
+ SwTextNode const& rNode, SwInterHyphInfo const& rHyphInfo);
+ void UpdateTextNodeHyphInfo(SwTextFrame const& rFrame,
+ SwTextNode const& rNode, SwInterHyphInfo & o_rHyphInfo);
+
+ void SetHyphWord(const css::uno::Reference<css::linguistic2::XHyphenatedWord> &xHW)
+ {
+ m_xHyphWord = xHW;
+ }
+};
+
+namespace sw {
+
+/**
+ * Describes a part of a single text node, which will be part of a text frame,
+ * even when redlines are hidden at a layout level.
+ */
+struct Extent
+{
+ SwTextNode * /*const logically, but need assignment for std::vector*/ pNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ Extent(SwTextNode *const p, sal_Int32 const s, sal_Int32 const e)
+ : pNode(p), nStart(s), nEnd(e)
+ {
+ assert(pNode);
+ assert(nStart != nEnd);
+ }
+};
+
+struct MergedPara;
+
+std::pair<SwTextNode*, sal_Int32> MapViewToModel(MergedPara const&, TextFrameIndex nIndex);
+TextFrameIndex MapModelToView(MergedPara const&, SwTextNode const* pNode, sal_Int32 nIndex);
+
+// warning: Existing must be used only once; a second use would delete the frame created by the first one...
+enum class FrameMode { New, Existing };
+std::unique_ptr<sw::MergedPara> CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode, FrameMode eMode);
+SwTextFrame * MakeTextFrame(SwTextNode & rNode, SwFrame *, sw::FrameMode eMode);
+
+bool FrameContainsNode(SwContentFrame const& rFrame, SwNodeOffset nNodeIndex);
+bool IsParaPropsNode(SwRootFrame const& rLayout, SwTextNode const& rNode);
+SwTextNode * GetParaPropsNode(SwRootFrame const& rLayout, SwNodeIndex const& rNode);
+SwPosition GetParaPropsPos(SwRootFrame const& rLayout, SwPosition const& rPos);
+std::pair<SwTextNode *, SwTextNode *>
+GetFirstAndLastNode(SwRootFrame const& rLayout, SwNodeIndex const& rPos);
+
+SwTextNode const& GetAttrMerged(SfxItemSet & rFormatSet,
+ SwTextNode const& rNode, SwRootFrame const* pLayout);
+
+void GotoPrevLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const* pLayout);
+void GotoNextLayoutTextFrame(SwNodeIndex & rIndex, SwRootFrame const* pLayout);
+
+TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
+ bool isRealDelete,
+ SwTextNode const& rNode, sal_Int32 nIndex, sal_Int32 nLen);
+
+void MoveMergedFlysAndFootnotes(std::vector<SwTextFrame*> const& rFrames,
+ SwTextNode const& rFirstNode, SwTextNode & rSecondNode, bool);
+
+void MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev, SwTextNode & rNode);
+enum class Recreate { No, ThisNode, Predecessor };
+void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate eRecreateMerged);
+
+void UpdateFramesForAddDeleteRedline(SwDoc & rDoc, SwPaM const& rPam);
+void UpdateFramesForRemoveDeleteRedline(SwDoc & rDoc, SwPaM const& rPam);
+
+void AddRemoveFlysAnchoredToFrameStartingAtNode(
+ SwTextFrame & rFrame, SwTextNode & rTextNode,
+ std::set<SwNodeOffset> *pSkipped);
+
+OUString GetExpandTextMerged(SwRootFrame const* pLayout,
+ SwTextNode const& rNode, bool bWithNumber,
+ bool bWithSpacesForLevel, ExpandMode i_mode);
+
+bool IsMarkHidden(SwRootFrame const& rLayout, ::sw::mark::IMark const& rMark);
+bool IsMarkHintHidden(SwRootFrame const& rLayout,
+ SwTextNode const& rNode, SwTextAttrEnd const& rHint);
+
+void RecreateStartTextFrames(SwTextNode & rNode);
+
+auto MakeSwInsText(SwTextNode & rNode, sal_Int32 nPos, sal_Int32 nLen) -> SwInsText;
+
+/**
+ * Decides if rTextNode has a numbering which has layout-level values (e.g. Arabic, but not
+ * none or bullets).
+ */
+bool HasNumberingWhichNeedsLayoutUpdate(const SwTextNode& rTextNode);
+
+} // namespace sw
+
+/// Represents the visualization of a paragraph. Typical upper is an
+/// SwBodyFrame. The first text portion of the first line is az SwParaPortion.
+class SW_DLLPUBLIC SwTextFrame final : public SwContentFrame
+{
+ friend class SwTextIter;
+ friend class SwTestFormat;
+ friend class WidowsAndOrphans;
+ friend class TextFrameLockGuard; // May Lock()/Unlock()
+ friend bool sw_ChangeOffset(SwTextFrame* pFrame, TextFrameIndex nNew);
+
+ /// SwLineLayout cache: the lines are not actually owned by the SwTextFrame
+ /// but by this SwCache, so they will be deleted in large documents
+ /// if there are too many of them, but the "valid" flags of the frame
+ /// will still be set; GetFormatted() is the function that forces
+ /// recreation of the SwLineLayout by Format() if necessary.
+ static SwCache *s_pTextCache;
+ static constexpr tools::Long nMinPrtLine = 0; // This Line must not be underrun when printing
+ // Hack for table cells stretching multiple pages
+
+ sal_uInt32 mnAllLines :24; // Line count for the Paint (including nThisLines)
+ sal_uInt32 mnThisLines :8; // Count of Lines of the Frame
+
+ // The x position for flys anchored at this paragraph.
+ // These values are calculated in SwTextFrame::CalcBaseOfstForFly()
+ SwTwips mnFlyAnchorOfst;
+ // The x position for wrap-through flys anchored at this paragraph.
+ SwTwips mnFlyAnchorOfstNoWrap;
+ /// The y position for wrap-through flys anchored at this paragraph.
+ SwTwips mnFlyAnchorVertOfstNoWrap;
+ SwTwips mnFootnoteLine;
+ // OD 2004-03-17 #i11860# - re-factoring of #i11859#
+ // member for height of last line (value needed for proportional line spacing)
+ SwTwips mnHeightOfLastLine;
+ // member for the additional first line offset, which is caused by the list
+ // label alignment for list level position and space mode LABEL_ALIGNMENT.
+ // This additional first line offset is used for the text formatting.
+ // It is NOT used for the determination of printing area.
+ SwTwips mnAdditionalFirstLineOffset;
+
+ /// redline merge data
+ std::unique_ptr<sw::MergedPara> m_pMergedPara;
+
+ TextFrameIndex mnOffset; // Is the offset in the Content (character count)
+
+ sal_uInt16 mnCacheIndex; // Index into the cache, USHRT_MAX if there's definitely no fitting object in the cache
+
+ // Separates the Master and creates a Follow or adjusts the data in the Follow
+ void AdjustFollow_( SwTextFormatter &rLine, TextFrameIndex nOffset,
+ TextFrameIndex nStrEnd, const sal_uInt8 nMode );
+
+ // Iterates all Lines and sets the line spacing using the attribute
+ void CalcLineSpace();
+
+ // Only called in Format
+ void AdjustFrame( const SwTwips nChgHeight, bool bHasToFit = false );
+
+ // Evaluates the Preps in Format()
+ bool CalcPreps();
+ void PrepWidows( const sal_uInt16 nNeed, bool bNotify );
+ void InvalidateRange_( const SwCharRange &, const tools::Long = 0);
+ inline void InvalidateRange( const SwCharRange &, const tools::Long = 0);
+
+ // WidowsAndOrphans, AdjustFrame, AdjustFollow
+ void FormatAdjust( SwTextFormatter &rLine, WidowsAndOrphans &rFrameBreak,
+ TextFrameIndex nStrLen, const bool bDummy );
+ void ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew );
+
+ bool mbLocked : 1; // In the Format?
+ bool mbWidow : 1; // Is our follow a Widow?
+ bool mbJustWidow : 1; // Did we just request Widow flag on master?
+ bool mbEmpty : 1; // Are we an empty paragraph?
+ bool mbInFootnoteConnect : 1; // Is in Connect at the moment
+ bool mbFootnote : 1; // Has at least one footnote
+ bool mbRepaint : 1; // TextFrame: Repaint is ready to be fetched
+ /// Contains rotated portions.
+ bool mbHasRotatedPortions : 1;
+ bool mbFieldFollow : 1; // Start with Field rest of the Master
+ bool mbHasAnimation : 1; // Contains animated SwGrfNumPortion
+ bool mbIsSwapped : 1; // during text formatting we swap the
+ // width and height for vertical formatting
+ // OD 14.03.2003 #i11760# - flag to control, if follow is formatted in
+ // method <CalcFollow(..)>.
+ // E.g., avoid formatting of follow, if method <SwLayoutFrame::FormatWidthCols(..)>
+ // is running.
+ bool mbFollowFormatAllowed : 1;
+
+ void ResetPreps();
+ void Lock() { mbLocked = true; }
+ void Unlock() { mbLocked = false; }
+ void SetWidow( const bool bNew ) { mbWidow = bNew; }
+ void SetJustWidow( const bool bNew ) { mbJustWidow = bNew; }
+ void SetEmpty( const bool bNew ) { mbEmpty = bNew; }
+ void SetFieldFollow( const bool bNew ) { mbFieldFollow = bNew; }
+
+ bool IsIdxInside(TextFrameIndex nPos, TextFrameIndex nLen) const;
+
+ // Changes the Frame or not (cf. FlyCnt)
+ bool GetModelPositionForViewPoint_(SwPosition *pPos, const Point &rPoint,
+ const bool bChgFrame, SwCursorMoveState* = nullptr ) const;
+ void FillCursorPos( SwFillData &rFill ) const;
+
+ // Format exactly one Line
+ bool FormatLine( SwTextFormatter &rLine, const bool bPrev );
+
+ // In order to safe stack space, we split this method:
+ // Format_ calls Format_ with parameters
+ void Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara );
+ void Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
+ const bool bAdjust = false );
+ void FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf );
+
+ // Formats the Follow and ensures disposing on orphans
+ bool CalcFollow(TextFrameIndex nTextOfst);
+
+ virtual void MakePos() override;
+
+ // Corrects the position from which we need to format
+ static TextFrameIndex FindBrk(const OUString &rText, TextFrameIndex nStart,
+ TextFrameIndex nEnd);
+
+ // inline branch
+ SwTwips GetFootnoteFrameHeight_() const;
+
+ // Outsourced to CalcPreps
+ bool CalcPrepFootnoteAdjust();
+
+ // For Footnote and WidOrp: Forced validation
+ void ValidateFrame();
+ void ValidateBodyFrame();
+
+ bool GetDropRect_( SwRect &rRect ) const;
+
+ void SetPara( SwParaPortion *pNew, bool bDelete = true );
+
+ bool IsFootnoteNumFrame_() const;
+
+ // Refresh formatting information
+ bool FormatQuick( bool bForceQuickFormat );
+
+ // Opt: Format empty paragraphs
+ bool FormatEmpty();
+ SwTwips EmptyHeight() const;
+
+ // Opt: Paint empty paragraphs
+ bool PaintEmpty( const SwRect &, bool bCheck ) const;
+
+ void ChgThisLines(); // Must always be called if the Line count could have changed
+
+ // required for 'new' relative anchor position
+ void CalcBaseOfstForFly();
+
+ /** method to determine height of last line, needed for proportional line spacing
+
+ OD 2004-03-17 #i11860#
+ OD 2005-05-20 #i47162# - introduce new optional parameter <_bUseFont>
+ in order to force the usage of the former algorithm to determine the
+ height of the last line, which uses the font.
+
+ @param _bUseFont
+ optional input parameter - boolean indicating, if the font has to be
+ used to determine the height of the last line. default value: false
+ */
+ void CalcHeightOfLastLine( const bool _bUseFont = false );
+
+ virtual void DestroyImpl() override;
+ virtual ~SwTextFrame() override;
+
+ void UpdateOutlineContentVisibilityButton(SwWrtShell* pWrtSh) const;
+ void PaintOutlineContentVisibilityButton() const;
+
+ virtual void SwClientNotify(SwModify const& rModify, SfxHint const& rHint) override;
+
+public:
+
+ virtual const SvxFormatBreakItem& GetBreakItem() const override;
+ virtual const SwFormatPageDesc& GetPageDescItem() const override;
+
+ css::uno::Sequence< css::style::TabStop > GetTabStopInfo( SwTwips CurrentPos ) override;
+
+ /**
+ * This is public, as it needs to be called by some methods in order to save the Prepare
+ * USE WITH CAUTION!
+ */
+ void Init();
+
+ /// Is called by DoIdleJob_(), ExecSpellPopup() and UpDown()
+ SwRect AutoSpell_(SwTextNode &, sal_Int32);
+
+ /// Is called by DoIdleJob_()
+ SwRect SmartTagScan(SwTextNode &);
+
+ /// Is called by DoIdleJob_()
+ void CollectAutoCmplWrds(SwTextNode &, sal_Int32);
+
+ /**
+ * Returns the view rectangle for the rPos model position. The values are relative to the upper
+ * left position of the page frame.
+ * Additional information can be obtained by passing an SwCursorMoveState object.
+ * Returns false if rPos > number of character is string
+ */
+ virtual bool GetCharRect( SwRect& rRect, const SwPosition& rPos,
+ SwCursorMoveState* pCMS = nullptr, bool bAllowFarAway = true ) const override;
+
+ /// A slimmer version of GetCharRect for autopositioning Frames
+ bool GetAutoPos( SwRect &, const SwPosition& ) const;
+
+ /**
+ * Determine top of line for given position in the text frame
+ *
+ * OD 11.11.2003 #i22341#
+ * Assumption: given position exists in the text frame or in a follow of it
+ * OD 2004-02-02 - adjustment
+ * Top of first paragraph line is the top of the paragraph.
+ * OD 2004-03-18 #i11860# - Consider upper space amount considered for
+ * previous frame and the page grid.
+ *
+ * @param _onTopOfLine
+ * output parameter - top of line, if the given position is found in the
+ * text frame.
+ *
+ * @param _rPos
+ * input parameter - reference to the position in the text frame
+ *
+ * @return boolean indicating, if the top of line for the given position
+ * has been determined or not.
+ */
+ bool GetTopOfLine( SwTwips& _onTopOfLine,
+ const SwPosition& _rPos ) const;
+
+ virtual bool FillSelection( SwSelectionList& rList, const SwRect& rRect ) const override;
+
+ /**
+ * In nOffset returns the offset of the char within the set
+ * text buffer, which is closest to the position provided by
+ * aPoint within the layout's SSize.
+ *
+ * @returns false if the SPoint is outside of the SSize else
+ * returns true
+ */
+ virtual bool GetModelPositionForViewPoint( SwPosition *, Point&,
+ SwCursorMoveState* = nullptr, bool bTestBackground = false ) const override;
+
+ /**
+ * Makes sure that the Frame is not switched (e.g. switched for a
+ * character-bound Frame)
+ */
+ bool GetKeyCursorOfst(SwPosition *pPos, const Point &rPoint ) const
+ { return GetModelPositionForViewPoint_( pPos, rPoint, false ); }
+
+ void PaintExtraData( const SwRect & rRect ) const; /// Page number etc.
+ SwRect GetPaintSwRect();
+ virtual void PaintSwFrame( vcl::RenderContext& rRenderContext, SwRect const&,
+ SwPrintData const*const pPrintData = nullptr ) const override;
+ virtual bool GetInfo( SfxPoolItem & ) const override;
+
+ /**
+ * Layout oriented cursor travelling:
+ * Left border, right border, previous Line, following Line,
+ * same horizontal position
+ */
+ virtual bool LeftMargin(SwPaM *) const override;
+ virtual bool RightMargin(SwPaM *, bool bAPI = false) const override;
+
+ virtual bool UnitUp(SwPaM *, const SwTwips nOffset,
+ bool bSetInReadOnly ) const override;
+ virtual bool UnitDown(SwPaM *, const SwTwips nOffset,
+ bool bSetInReadOnly ) const override;
+ bool UnitUp_(SwPaM *, const SwTwips nOffset,
+ bool bSetInReadOnly ) const;
+ bool UnitDown_(SwPaM *, const SwTwips nOffset,
+ bool bSetInReadOnly ) const;
+
+ /**
+ * Prepares the cursor position for a visual cursor move (BiDi).
+ * The behaviour is different for insert and overwrite cursors
+ */
+ void PrepareVisualMove( TextFrameIndex& nPos, sal_uInt8& nCursorLevel,
+ bool& bRight, bool bInsertCursor );
+
+ /// Methods to manage the FollowFrame
+ void SplitFrame(TextFrameIndex nTextPos);
+ SwContentFrame *JoinFrame();
+ TextFrameIndex GetOffset() const { return mnOffset; }
+ void SetOffset_(TextFrameIndex nNewOfst);
+ inline void SetOffset (TextFrameIndex nNewOfst);
+ void ManipOfst(TextFrameIndex const nNewOfst) { mnOffset = nNewOfst; }
+ SwTextFrame *GetFrameAtPos ( const SwPosition &rPos);
+ inline const SwTextFrame *GetFrameAtPos ( const SwPosition &rPos) const;
+ SwTextFrame& GetFrameAtOfst(TextFrameIndex nOfst);
+ /// If there's a Follow and we don't contain text ourselves
+ bool IsEmptyMaster() const
+ { return GetFollow() && !GetFollow()->GetOffset(); }
+
+ void SetMergedPara(std::unique_ptr<sw::MergedPara> p);
+ sw::MergedPara * GetMergedPara() { return m_pMergedPara.get(); }
+ sw::MergedPara const* GetMergedPara() const { return m_pMergedPara.get(); }
+
+ /// Returns the text portion we want to edit (for inline see underneath)
+ const OUString& GetText() const;
+ SwTextNode const* GetTextNodeForParaProps() const;
+ SwTextNode const* GetTextNodeForFirstText() const;
+ SwTextNode * GetTextNodeFirst()
+ { return const_cast<SwTextNode*>(const_cast<SwTextFrame const*>(this)->GetTextNodeFirst()); };
+ SwTextNode const* GetTextNodeFirst() const;
+ SwDoc & GetDoc()
+ { return const_cast<SwDoc &>(const_cast<SwTextFrame const*>(this)->GetDoc()); }
+ SwDoc const& GetDoc() const;
+
+ SwTextFrame(SwTextNode * const, SwFrame*, sw::FrameMode eMode);
+
+ /**
+ * SwContentFrame: the shortcut for the Frames
+ * If the void* casts wrongly, it's its own fault!
+ * The void* must be checked for 0 in any case!
+ *
+ * return true if the Portion associated with this SwTextFrame was
+ * potentially destroyed and replaced by Prepare
+ */
+ virtual bool Prepare( const PrepareHint ePrep = PrepareHint::Clear,
+ const void *pVoid = nullptr, bool bNotify = true ) override;
+
+ /**
+ * nMaxHeight is the required height
+ * bSplit indicates, that the paragraph has to be split
+ * bTst indicates, that we are currently doing a test formatting
+ */
+ virtual bool WouldFit(SwTwips &nMaxHeight, bool &bSplit, bool bTst, bool bMoveBwd) override;
+
+ /**
+ * The WouldFit equivalent for temporarily rewired TextFrames
+ * nMaxHeight returns the required size here too and bSplit
+ * determines whether the paragraph needs to be split.
+ * We pass the potential predecessor for the distance calculation
+ */
+ bool TestFormat( const SwFrame* pPrv, SwTwips &nMaxHeight, bool &bSplit );
+
+ /**
+ * We format a Line for interactive hyphenation
+ * @return found
+ */
+ bool Hyphenate(SwInterHyphInfoTextFrame & rInf);
+
+ /// Test grow
+ inline SwTwips GrowTst( const SwTwips nGrow );
+
+ SwParaPortion *GetPara();
+ inline const SwParaPortion *GetPara() const;
+ inline bool HasPara() const;
+ bool HasPara_() const;
+
+ /// map position in potentially merged text frame to SwPosition
+ std::pair<SwTextNode*, sal_Int32> MapViewToModel(TextFrameIndex nIndex) const;
+ SwPosition MapViewToModelPos(TextFrameIndex nIndex) const;
+ TextFrameIndex MapModelToView(SwTextNode const* pNode, sal_Int32 nIndex) const;
+ TextFrameIndex MapModelToViewPos(SwPosition const& rPos) const;
+
+ // If there are any hanging punctuation portions in the margin
+ // the offset will be returned.
+ SwTwips HangingMargin() const;
+
+ // Locking
+ bool IsLocked() const { return mbLocked; }
+
+ bool IsWidow() const { return mbWidow; }
+ bool IsJustWidow() const { return mbJustWidow; }
+ bool IsEmpty() const { return mbEmpty; }
+ bool HasFootnote() const { return mbFootnote; }
+ bool IsInFootnoteConnect()const { return mbInFootnoteConnect;}
+ bool IsFieldFollow() const { return mbFieldFollow;}
+
+ inline void SetRepaint() const;
+ inline void ResetRepaint() const;
+ bool HasRepaint() const { return mbRepaint; }
+ void SetHasRotatedPortions(bool bHasRotatedPortions);
+ bool GetHasRotatedPortions() const { return mbHasRotatedPortions; }
+ void SetAnimation() const
+ { const_cast<SwTextFrame*>(this)->mbHasAnimation = true; }
+ bool HasAnimation() const { return mbHasAnimation; }
+
+ bool IsSwapped() const { return mbIsSwapped; }
+
+ /// Does the Frame have a local footnote (in this Frame or Follow)?
+#ifdef DBG_UTIL
+ void CalcFootnoteFlag(TextFrameIndex nStop = TextFrameIndex(COMPLETE_STRING)); //For testing SplitFrame
+#else
+ void CalcFootnoteFlag();
+#endif
+
+ /// Hidden
+ bool IsHiddenNow() const; // bHidden && pOut == pPrt
+ void HideHidden(); // Remove appendage if Hidden
+ void HideFootnotes(TextFrameIndex nStart, TextFrameIndex nEnd);
+
+ /**
+ * Hides respectively shows objects, which are anchored at paragraph,
+ * at/as a character of the paragraph, corresponding to the paragraph and
+ * paragraph portion visibility.
+ */
+ void HideAndShowObjects();
+
+ /// Footnote
+ void RemoveFootnote(TextFrameIndex nStart,
+ TextFrameIndex nLen = TextFrameIndex(COMPLETE_STRING));
+ inline SwTwips GetFootnoteFrameHeight() const;
+ SwTextFrame *FindFootnoteRef( const SwTextFootnote *pFootnote );
+ const SwTextFrame *FindFootnoteRef( const SwTextFootnote *pFootnote ) const
+ { return const_cast<SwTextFrame *>(this)->FindFootnoteRef( pFootnote ); }
+ void ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDeadLine );
+
+ /**
+ * If we're a Footnote that grows towards its reference ...
+ * public, because it's needed by SwContentFrame::MakeAll
+ */
+ SwTwips GetFootnoteLine( const SwTextFootnote *pFootnote ) const;
+
+ TextFrameIndex GetDropLen(TextFrameIndex nWishLen) const;
+
+ LanguageType GetLangOfChar(TextFrameIndex nIndex, sal_uInt16 nScript,
+ bool bNoChar = false) const;
+
+ virtual void Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs = nullptr ) override;
+ virtual void CheckDirection( bool bVert ) override;
+
+ /// Returns the sum of line height in pLine
+ SwTwips GetParHeight() const;
+
+ inline SwTextFrame *GetFollow();
+ inline const SwTextFrame *GetFollow() const;
+
+ /// Find the page number of ErgoSum and QuoVadis
+ SwTextFrame *FindQuoVadisFrame();
+
+ /**
+ * In case the SwLineLayout was cleared out of the s_pTextCache, recreate it
+ *
+ * #i29062# GetFormatted() can trigger a full formatting
+ * of the paragraph, causing other layout frames to become invalid. This
+ * has to be avoided during painting. Therefore we need to pass the
+ * information that we are currently in the paint process.
+ */
+ SwTextFrame* GetFormatted( bool bForceQuickFormat = false );
+
+ /// Will be moved soon
+ void SetFootnote( const bool bNew ) { mbFootnote = bNew; }
+
+ /// Respect the Follows
+ inline bool IsInside(TextFrameIndex nPos) const;
+
+ /// DropCaps and selections
+ bool GetDropRect( SwRect &rRect ) const
+ { return HasPara() && GetDropRect_( rRect ); }
+
+ static SwCache *GetTextCache() { return s_pTextCache; }
+ static void SetTextCache( SwCache *pNew ) { s_pTextCache = pNew; }
+
+ static tools::Long GetMinPrtLine() { return nMinPrtLine; }
+
+ sal_uInt16 GetCacheIdx() const { return mnCacheIndex; }
+ void SetCacheIdx( const sal_uInt16 nNew ) { mnCacheIndex = nNew; }
+
+ /// Removes the Line information from the Cache but retains the entry itself
+ void ClearPara();
+ /// Removes this frame completely from the Cache
+ void RemoveFromCache();
+
+ /// Am I a FootnoteFrame, with a number at the start of the paragraph?
+ bool IsFootnoteNumFrame() const
+ { return IsInFootnote() && !GetIndPrev() && IsFootnoteNumFrame_(); }
+
+ /**
+ * Simulates a formatting as if there were not right margin or Flys or other
+ * obstacles and returns the width
+ */
+ SwTwips CalcFitToContent();
+
+ /**
+ * Simulate format for a list item paragraph, whose list level attributes
+ * are in LABEL_ALIGNMENT mode, in order to determine additional first
+ * line offset for the real text formatting due to the value of label
+ * adjustment attribute of the list level.
+ */
+ void CalcAdditionalFirstLineOffset();
+
+ SwTwips GetAdditionalFirstLineOffset() const
+ {
+ return mnAdditionalFirstLineOffset;
+ }
+
+ /**
+ * Returns the additional line spacing for the next paragraph
+ * @param _bNoPropLineSpacing: control, whether the value of a
+ * proportional line spacing is returned or not
+ */
+ tools::Long GetLineSpace( const bool _bNoPropLineSpacing = false ) const;
+
+ /// Returns the first line height
+ sal_uInt16 FirstLineHeight() const;
+
+ /// Rewires FlyInContentFrame, if nEnd > Index >= nStart
+ void MoveFlyInCnt(SwTextFrame *pNew, TextFrameIndex nStart, TextFrameIndex nEnd);
+
+ /// Calculates the position of FlyInContentFrames
+ TextFrameIndex CalcFlyPos( SwFrameFormat const * pSearch );
+
+ /// Determines the start position and step size of the register
+ bool FillRegister( SwTwips& rRegStart, sal_uInt16& rRegDiff );
+
+ /// Determines the line count
+ sal_uInt16 GetLineCount(TextFrameIndex nPos);
+
+ /// For displaying the line numbers
+ sal_uLong GetAllLines() const { return mnAllLines; }
+ sal_uLong GetThisLines() const { return mnThisLines;}
+ void RecalcAllLines();
+
+ /// Stops the animations within numberings
+ void StopAnimation( const OutputDevice *pOut );
+
+ /// Visit all portions for Accessibility
+ void VisitPortions( SwPortionHandler& rPH ) const;
+
+ /// Returns the script info stored at the paraportion
+ const SwScriptInfo* GetScriptInfo() const;
+
+ /// Swaps width and height of the text frame
+ void SwapWidthAndHeight();
+
+ /**
+ * Calculates the coordinates of a rectangle when switching from
+ * horizontal to vertical layout
+ */
+ void SwitchHorizontalToVertical( SwRect& rRect ) const;
+
+ /**
+ * Calculates the coordinates of a point when switching from
+ * horizontal to vertical layout
+ */
+ void SwitchHorizontalToVertical( Point& rPoint ) const;
+
+ /**
+ * Calculates the limit value when switching from
+ * horizontal to vertical layout
+ */
+ tools::Long SwitchHorizontalToVertical( tools::Long nLimit ) const;
+
+ /**
+ * Calculates the coordinates of a rectangle when switching from
+ * vertical to horizontal layout
+ */
+ void SwitchVerticalToHorizontal( SwRect& rRect ) const;
+
+ /**
+ * Calculates the coordinates of a point when switching from
+ * vertical to horizontal layout
+ */
+ void SwitchVerticalToHorizontal( Point& rPoint ) const;
+
+ /**
+ * Calculates the a limit value when switching from
+ * vertical to horizontal layout
+ */
+ tools::Long SwitchVerticalToHorizontal( tools::Long nLimit ) const;
+
+ /**
+ * Calculates the coordinates of a rectangle when switching from
+ * LTR to RTL layout
+ */
+ void SwitchLTRtoRTL( SwRect& rRect ) const;
+
+ /**
+ * Calculates the coordinates of a point when switching from
+ * LTR to RTL layout
+ */
+ void SwitchLTRtoRTL( Point& rPoint ) const;
+
+ /**
+ * Calculates the coordinates of a rectangle when switching from
+ * RTL to LTR layout
+ */
+ void SwitchRTLtoLTR( SwRect& rRect ) const { SwitchLTRtoRTL( rRect ); }
+
+ /**
+ * Calculates the coordinates of a point when switching from
+ * RTL to LTR layout
+ */
+ void SwitchRTLtoLTR( Point& rPoint ) const { SwitchLTRtoRTL( rPoint ); };
+
+ bool FollowFormatAllowed() const
+ {
+ return mbFollowFormatAllowed;
+ }
+
+ void AllowFollowFormat()
+ {
+ mbFollowFormatAllowed = true;
+ }
+
+ void ForbidFollowFormat()
+ {
+ mbFollowFormatAllowed = false;
+ }
+
+ SwTwips GetBaseOffsetForFly( bool bIgnoreFlysAnchoredAtThisFrame ) const
+ {
+ return ( bIgnoreFlysAnchoredAtThisFrame ?
+ mnFlyAnchorOfst :
+ mnFlyAnchorOfstNoWrap );
+ }
+
+ SwTwips GetBaseVertOffsetForFly(bool bIgnoreFlysAnchoredAtThisFrame) const;
+
+ SwTwips GetHeightOfLastLine() const
+ {
+ return mnHeightOfLastLine;
+ }
+
+ static void repaintTextFrames( const SwTextNode& rNode );
+
+ void RegisterToNode(SwTextNode &, bool isForceNodeAsFirst = false);
+
+ bool IsSymbolAt(TextFrameIndex) const;
+ OUString GetCurWord(SwPosition const&) const;
+ sal_uInt16 GetScalingOfSelectedText(TextFrameIndex nStt, TextFrameIndex nEnd);
+
+ virtual void dumpAsXmlAttributes(xmlTextWriterPtr writer) const override;
+};
+
+//use this to protect a SwTextFrame for a given scope from getting merged with
+//its neighbour and thus deleted
+class TextFrameLockGuard
+{
+private:
+ SwTextFrame *m_pTextFrame;
+ bool m_bOldLocked;
+public:
+ //Lock pFrame for the lifetime of the Cut/Paste call, etc. to avoid
+ //SwTextFrame::AdjustFollow_ removing the pFrame we're trying to Make
+ TextFrameLockGuard(SwFrame* pFrame)
+ {
+ m_pTextFrame = pFrame->IsTextFrame() ? static_cast<SwTextFrame*>(pFrame) : nullptr;
+ if (m_pTextFrame)
+ {
+ m_bOldLocked = m_pTextFrame->IsLocked();
+ m_pTextFrame->Lock();
+ }
+ else
+ {
+ m_bOldLocked = false;
+ }
+ }
+
+ ~TextFrameLockGuard()
+ {
+ if (m_pTextFrame && !m_bOldLocked)
+ m_pTextFrame->Unlock();
+ }
+};
+
+inline const SwParaPortion *SwTextFrame::GetPara() const
+{
+ return const_cast<SwTextFrame*>(this)->GetPara();
+}
+
+inline bool SwTextFrame::HasPara() const
+{
+ return mnCacheIndex!=USHRT_MAX && HasPara_();
+}
+
+inline SwTwips SwTextFrame::GrowTst( const SwTwips nGrow )
+{
+ return Grow( nGrow, true );
+}
+
+inline bool SwTextFrame::IsInside(TextFrameIndex const nPos) const
+{
+ bool bRet = true;
+ if( nPos < GetOffset() )
+ bRet = false;
+ else
+ {
+ const SwTextFrame *pFoll = GetFollow();
+ if( pFoll && nPos >= pFoll->GetOffset() )
+ bRet = false;
+ }
+ return bRet;
+}
+
+inline SwTwips SwTextFrame::GetFootnoteFrameHeight() const
+{
+ if( !IsFollow() && IsInFootnote() && HasPara() )
+ return GetFootnoteFrameHeight_();
+ else
+ return 0;
+}
+
+inline const SwTextFrame *SwTextFrame::GetFollow() const
+{
+ return static_cast<const SwTextFrame*>(SwContentFrame::GetFollow());
+}
+inline SwTextFrame *SwTextFrame::GetFollow()
+{
+ return static_cast<SwTextFrame*>(SwContentFrame::GetFollow());
+}
+
+inline const SwTextFrame *SwTextFrame::GetFrameAtPos( const SwPosition &rPos) const
+{
+ return const_cast<SwTextFrame*>(this)->GetFrameAtPos( rPos );
+}
+
+inline void SwTextFrame::SetOffset(TextFrameIndex const nNewOfst)
+{
+ if ( mnOffset != nNewOfst )
+ SetOffset_( nNewOfst );
+}
+
+inline void SwTextFrame::SetRepaint() const
+{
+ const_cast<SwTextFrame*>(this)->mbRepaint = true;
+}
+inline void SwTextFrame::ResetRepaint() const
+{
+ const_cast<SwTextFrame*>(this)->mbRepaint = false;
+}
+
+class TemporarySwap {
+protected:
+ explicit TemporarySwap(SwTextFrame * frame, bool swap):
+ m_frame(frame), m_undo(false)
+ {
+ if (m_frame->IsVertical() && swap) {
+ m_undo = true;
+ m_frame->SwapWidthAndHeight();
+ }
+ }
+
+ ~TemporarySwap() {
+ if (m_undo) {
+ m_frame->SwapWidthAndHeight();
+ }
+ }
+
+private:
+ TemporarySwap(TemporarySwap const &) = delete;
+ void operator =(TemporarySwap const &) = delete;
+
+ SwTextFrame * m_frame;
+ bool m_undo;
+};
+
+class SwSwapIfSwapped: private TemporarySwap {
+public:
+ explicit SwSwapIfSwapped(SwTextFrame* frame):
+ TemporarySwap(frame, frame->IsSwapped()) {}
+};
+
+class SwSwapIfNotSwapped: private TemporarySwap {
+public:
+ explicit SwSwapIfNotSwapped(SwTextFrame* frame):
+ TemporarySwap(frame, !frame->IsSwapped()) {}
+};
+
+/**
+ * Helper class which can be used instead of the macros if a function
+ * has too many returns
+ */
+class SwFrameSwapper
+{
+ const SwTextFrame* pFrame;
+ bool bUndo;
+public:
+ SwFrameSwapper( const SwTextFrame* pFrame, bool bSwapIfNotSwapped );
+ ~SwFrameSwapper();
+};
+
+class SwLayoutModeModifier
+{
+ const OutputDevice& m_rOut;
+ vcl::text::ComplexTextLayoutFlags m_nOldLayoutMode;
+public:
+ SwLayoutModeModifier( const OutputDevice& rOutp );
+ ~SwLayoutModeModifier();
+ void Modify( bool bChgToRTL );
+ void SetAuto();
+};
+
+class SwDigitModeModifier
+{
+ const OutputDevice& rOut;
+ LanguageType nOldLanguageType;
+public:
+ SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang );
+ ~SwDigitModeModifier();
+};
+
+namespace sw {
+
+/**
+ * Describes parts of multiple text nodes, which will form a text frame, even
+ * when redlines are hidden at a layout level.
+ */
+struct MergedPara
+{
+ sw::WriterMultiListener listener;
+ std::vector<Extent> extents;
+ /// note: cannot be const currently to avoid UB because SwTextGuess::Guess
+ /// const_casts it and modifies it (also, Update will modify it)
+ OUString mergedText;
+ /// most paragraph properties are taken from the first non-empty node
+ SwTextNode * pParaPropsNode;
+ /// except break attributes, those are taken from the first node
+ SwTextNode *const pFirstNode;
+ /// mainly for sanity checks
+ SwTextNode const* pLastNode;
+ MergedPara(SwTextFrame & rFrame, std::vector<Extent>&& rExtents,
+ OUString const& rText,
+ SwTextNode *const pProps, SwTextNode *const pFirst,
+ SwTextNode const*const pLast)
+ : listener(rFrame), extents(std::move(rExtents)), mergedText(rText)
+ , pParaPropsNode(pProps), pFirstNode(pFirst), pLastNode(pLast)
+ {
+ assert(pParaPropsNode);
+ assert(pFirstNode);
+ assert(pLastNode);
+ }
+};
+
+/// iterate SwTextAttr in potentially merged text frame
+class MergedAttrIterBase
+{
+protected:
+ sw::MergedPara const*const m_pMerged;
+ SwTextNode const*const m_pNode;
+ size_t m_CurrentExtent;
+ size_t m_CurrentHint;
+ MergedAttrIterBase(SwTextFrame const& rFrame);
+};
+
+class MergedAttrIter
+ : public MergedAttrIterBase
+{
+public:
+ MergedAttrIter(SwTextFrame const& rFrame) : MergedAttrIterBase(rFrame) {}
+ SwTextAttr const* NextAttr(SwTextNode const** ppNode = nullptr);
+};
+
+class MergedAttrIterByEnd
+{
+private:
+ std::vector<std::pair<SwTextNode const*, SwTextAttr const*>> m_Hints;
+ SwTextNode const*const m_pNode;
+ size_t m_CurrentHint;
+public:
+ MergedAttrIterByEnd(SwTextFrame const& rFrame);
+ SwTextAttr const* NextAttr(SwTextNode const*& rpNode);
+ void PrevAttr();
+};
+
+class MergedAttrIterReverse
+ : public MergedAttrIterBase
+{
+public:
+ MergedAttrIterReverse(SwTextFrame const& rFrame);
+ SwTextAttr const* PrevAttr(SwTextNode const** ppNode = nullptr);
+};
+
+
+const SwTwips WIDOW_MAGIC = (SAL_MAX_INT32 - 1)/2;
+
+} // namespace sw
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/txttypes.hxx b/sw/source/core/inc/txttypes.hxx
new file mode 100644
index 000000000..6b3124fb8
--- /dev/null
+++ b/sw/source/core/inc/txttypes.hxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+/// @see PORGRP_* masks in porlin.hxx for meaning of bits!
+enum class PortionType
+{
+ NONE = 0x0000,
+ FlyCnt = 0x0001,
+
+ Hole = 0x0080,
+ TempEnd = 0x0081,
+ Break = 0x0082,
+ Kern = 0x0083,
+ Arrow = 0x0084,
+ Multi = 0x0085,
+ HiddenText = 0x0086,
+ ControlChar = 0x0087,
+ Bookmark = 0x0088,
+
+ Text = 0x8000,
+ Lay = 0x8001,
+ Para = 0x8002,
+ Hanging = 0x8004,
+ InputField = 0x8005,
+ FieldMark = 0x8006,
+ FieldFormCheckbox = 0x8007,
+
+ Drop = 0x8080,
+ Tox = 0x8089,
+ IsoTox = 0x808a,
+ Ref = 0x808b,
+ IsoRef = 0x808c,
+ Meta = 0x808d,
+ ContentControl = 0x808e,
+
+ Expand = 0xc080,
+ Blank = 0xc081,
+ PostIts = 0xc082,
+
+ Hyphen = 0xd080,
+ HyphenStr = 0xd081,
+ SoftHyphen = 0xd082,
+ SoftHyphenStr = 0xd083,
+ SoftHyphenComp = 0xd084,
+
+ Field = 0xe080,
+ Hidden = 0xe081,
+ QuoVadis = 0xe082,
+ ErgoSum = 0xe083,
+ Combined = 0xe084,
+ Footnote = 0xe085,
+
+ FootnoteNum = 0xe880,
+ Number = 0xe881,
+ Bullet = 0xe882,
+ GrfNum = 0xe883,
+
+ Glue = 0x0480,
+
+ Margin = 0x04c0,
+
+ Fix = 0x06c0,
+ Fly = 0x06c1,
+
+ // Tabulator, not table
+ Tab = 0x0750,
+
+ TabRight = 0x07d0,
+ TabCenter = 0x07d1,
+ TabDecimal = 0x07d2,
+
+ TabLeft = 0x0740,
+};
+
+namespace sw
+{
+const char* PortionTypeToString(PortionType nType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/undoflystrattr.hxx b/sw/source/core/inc/undoflystrattr.hxx
new file mode 100644
index 000000000..5032c26c1
--- /dev/null
+++ b/sw/source/core/inc/undoflystrattr.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNDOFLYSTRATTR_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNDOFLYSTRATTR_HXX
+
+#include <undobj.hxx>
+#include <swundo.hxx>
+
+class SwFlyFrameFormat;
+
+class SwUndoFlyStrAttr final : public SwUndo
+{
+ public:
+ SwUndoFlyStrAttr( SwFlyFrameFormat& rFlyFrameFormat,
+ const SwUndoId eUndoId,
+ const OUString& sOldStr,
+ const OUString& sNewStr );
+ virtual ~SwUndoFlyStrAttr() override;
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+
+ virtual SwRewriter GetRewriter() const override;
+
+ private:
+ SwFlyFrameFormat& mrFlyFrameFormat;
+ const OUString msOldStr;
+ const OUString msNewStr;
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNDOFLYSTRATTR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unobookmark.hxx b/sw/source/core/inc/unobookmark.hxx
new file mode 100644
index 000000000..09ccb69e6
--- /dev/null
+++ b/sw/source/core/inc/unobookmark.hxx
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/text/XFormField.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <svl/listener.hxx>
+#include <sfx2/Metadatable.hxx>
+
+#include <unobaseclass.hxx>
+#include <IDocumentMarkAccess.hxx>
+
+class SwDoc;
+
+typedef ::cppu::ImplInheritanceHelper
+< ::sfx2::MetadatableMixin
+, css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::container::XNamed
+, css::text::XTextContent
+> SwXBookmark_Base;
+
+/// UNO API wrapper around an internal sw::mark::IMark.
+class SwXBookmark
+ : public SwXBookmark_Base
+{
+
+private:
+
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+protected:
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void attachToRangeEx(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange,
+ IDocumentMarkAccess::MarkType eType,
+ bool isFieldmarkSeparatorAtStart = false);
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ virtual void attachToRange(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange);
+
+ ::sw::mark::IMark* GetBookmark() const;
+
+ IDocumentMarkAccess* GetIDocumentMarkAccess();
+
+ SwDoc * GetDoc();
+
+ void registerInMark( SwXBookmark& rXMark, ::sw::mark::IMark* const pMarkBase );
+
+ virtual ~SwXBookmark() override;
+
+ SwXBookmark(SwDoc *const pDoc);
+
+ /// descriptor
+ SwXBookmark();
+
+public:
+
+ static css::uno::Reference< css::text::XTextContent>
+ CreateXBookmark(SwDoc & rDoc, ::sw::mark::IMark * pBookmark);
+
+ /// @return IMark for this, but only if it lives in pDoc
+ static ::sw::mark::IMark const* GetBookmarkInDoc(SwDoc const*const pDoc,
+ const css::uno::Reference<css::lang::XUnoTunnel> & xUT);
+
+ // MetadatableMixin
+ virtual ::sfx2::Metadatable* GetCoreObject() override;
+ virtual css::uno::Reference< css::frame::XModel > GetModel() override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<
+ css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& rName) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+};
+
+class SwXFieldmarkParameters final
+ : public ::cppu::WeakImplHelper< css::container::XNameContainer>
+ , public SvtListener
+{
+ private:
+ ::sw::mark::IFieldmark* m_pFieldmark;
+ /// @throws css::uno::RuntimeException
+ ::sw::mark::IFieldmark::parameter_map_t* getCoreParameters();
+ public:
+ SwXFieldmarkParameters(::sw::mark::IFieldmark* const pFieldmark)
+ : m_pFieldmark(pFieldmark)
+ {
+ StartListening(pFieldmark->GetNotifier());
+ }
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override;
+ // XNameAccess
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+
+ virtual void Notify( const SfxHint& rHint ) override;
+};
+
+typedef cppu::ImplInheritanceHelper< SwXBookmark,
+ css::text::XFormField,
+ css::text::XTextField
+ > SwXFieldmark_Base;
+
+/// UNO wrapper around an sw::mark::IFieldmark.
+class SwXFieldmark final
+ : public SwXFieldmark_Base
+{
+ ::sw::mark::ICheckboxFieldmark* getCheckboxFieldmark();
+ bool const m_bReplacementObject;
+ bool m_isFieldmarkSeparatorAtStart = false;
+
+ css::uno::Reference<css::text::XTextRange>
+ GetCommand(::sw::mark::IFieldmark const& rMark);
+ css::uno::Reference<css::text::XTextRange>
+ GetResult(::sw::mark::IFieldmark const& rMark);
+
+ SwXFieldmark(bool isReplacementObject, SwDoc* pDoc);
+
+public:
+ static css::uno::Reference<css::text::XTextContent>
+ CreateXFieldmark(SwDoc & rDoc, ::sw::mark::IMark * pMark,
+ bool isReplacementObject = false);
+
+ virtual void attachToRange(
+ const css::uno::Reference<css::text::XTextRange > & xTextRange) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference<css::lang::XEventListener> & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference<css::lang::XEventListener> & xListener) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference<css::text::XTextRange> & xTextRange) override;
+ virtual css::uno::Reference<css::text::XTextRange> SAL_CALL getAnchor() override;
+
+ // XTextField
+ virtual OUString SAL_CALL getPresentation(sal_Bool bShowCommand) override;
+
+ // XFormField
+ virtual OUString SAL_CALL getFieldType() override;
+ virtual void SAL_CALL setFieldType(const OUString& description) override;
+ virtual css::uno::Reference<css::container::XNameContainer> SAL_CALL getParameters() override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unocontentcontrol.hxx b/sw/source/core/inc/unocontentcontrol.hxx
new file mode 100644
index 000000000..c34f57b4e
--- /dev/null
+++ b/sw/source/core/inc/unocontentcontrol.hxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <memory>
+#include <deque>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <unobaseclass.hxx>
+
+typedef std::deque<css::uno::Reference<css::text::XTextRange>> TextRangeList_t;
+
+class SwPaM;
+class SwTextNode;
+class SwContentControl;
+
+/**
+ * UNO API wrapper around an SwContentControl, exposed as the com.sun.star.text.ContentControl
+ * service.
+ */
+class SwXContentControl
+ : public cppu::WeakImplHelper<css::lang::XUnoTunnel, css::lang::XServiceInfo,
+ css::container::XEnumerationAccess, css::text::XTextContent,
+ css::text::XText, css::beans::XPropertySet>
+{
+public:
+ class Impl;
+
+protected:
+ sw::UnoImplPtr<Impl> m_pImpl;
+
+ void AttachImpl(const css::uno::Reference<css::text::XTextRange>& xTextRange,
+ sal_uInt16 nWhich);
+
+ ~SwXContentControl() override;
+
+ SwXContentControl(const SwXContentControl&) = delete;
+ SwXContentControl& operator=(const SwXContentControl&) = delete;
+
+ SwXContentControl(SwDoc* pDoc, SwContentControl* pContentControl,
+ const css::uno::Reference<css::text::XText>& xParentText,
+ std::unique_ptr<const TextRangeList_t> pPortions);
+
+ SwXContentControl(SwDoc* pDoc);
+
+public:
+ static css::uno::Reference<css::text::XTextContent>
+ CreateXContentControl(SwContentControl& rContentControl,
+ const css::uno::Reference<css::text::XText>& xParentText = nullptr,
+ std::unique_ptr<const TextRangeList_t>&& pPortions
+ = std::unique_ptr<const TextRangeList_t>());
+
+ static css::uno::Reference<css::text::XTextContent> CreateXContentControl(SwDoc& rDoc);
+
+ /// Initializes params with position of the attribute content (without CH_TXTATR).
+ bool SetContentRange(SwTextNode*& rpNode, sal_Int32& rStart, sal_Int32& rEnd) const;
+ const css::uno::Reference<css::text::XText>& GetParentText() const;
+
+ static const css::uno::Sequence<sal_Int8>& getUnoTunnelId();
+
+ // XUnoTunnel
+ sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8>& Identifier) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XComponent
+ void SAL_CALL dispose() override;
+ void SAL_CALL
+ addEventListener(const css::uno::Reference<css::lang::XEventListener>& xListener) override;
+ void SAL_CALL
+ removeEventListener(const css::uno::Reference<css::lang::XEventListener>& xListener) override;
+
+ // XElementAccess
+ css::uno::Type SAL_CALL getElementType() override;
+ sal_Bool SAL_CALL hasElements() override;
+
+ // XEnumerationAccess
+ css::uno::Reference<css::container::XEnumeration> SAL_CALL createEnumeration() override;
+
+ // XTextContent
+ void SAL_CALL attach(const css::uno::Reference<css::text::XTextRange>& xTextRange) override;
+ css::uno::Reference<css::text::XTextRange> SAL_CALL getAnchor() override;
+
+ // XTextRange
+ css::uno::Reference<css::text::XText> SAL_CALL getText() override;
+ css::uno::Reference<css::text::XTextRange> SAL_CALL getStart() override;
+ css::uno::Reference<css::text::XTextRange> SAL_CALL getEnd() override;
+ OUString SAL_CALL getString() override;
+ void SAL_CALL setString(const OUString& rString) override;
+
+ // XSimpleText
+ css::uno::Reference<css::text::XTextCursor> SAL_CALL createTextCursor() override;
+ css::uno::Reference<css::text::XTextCursor> SAL_CALL createTextCursorByRange(
+ const css::uno::Reference<css::text::XTextRange>& xTextPosition) override;
+ void SAL_CALL insertString(const css::uno::Reference<css::text::XTextRange>& xRange,
+ const OUString& aString, sal_Bool bAbsorb) override;
+ void SAL_CALL insertControlCharacter(const css::uno::Reference<css::text::XTextRange>& xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb) override;
+
+ // XText
+ void SAL_CALL insertTextContent(const css::uno::Reference<css::text::XTextRange>& xRange,
+ const css::uno::Reference<css::text::XTextContent>& xContent,
+ sal_Bool bAbsorb) override;
+ void SAL_CALL
+ removeTextContent(const css::uno::Reference<css::text::XTextContent>& xContent) override;
+
+ // XPropertySet
+ css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ void SAL_CALL setPropertyValue(const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ css::uno::Any SAL_CALL getPropertyValue(const OUString& rPropertyName) override;
+ void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& xListener) override;
+ void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& xListener) override;
+ void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& xListener) override;
+ void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& xListener) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unoevent.hxx b/sw/source/core/inc/unoevent.hxx
new file mode 100644
index 000000000..4906f447f
--- /dev/null
+++ b/sw/source/core/inc/unoevent.hxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOEVENT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOEVENT_HXX
+
+#include <svtools/unoevent.hxx>
+
+class SvxMacroItem;
+class SwXFrame;
+class SwXTextFrame;
+class SwXTextGraphicObject;
+class SwXTextEmbeddedObject;
+class SwFormatINetFormat;
+namespace sw
+{
+class ICoreFrameStyle;
+}
+
+class SwHyperlinkEventDescriptor final : public SvDetachedEventDescriptor
+{
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+
+ virtual ~SwHyperlinkEventDescriptor() override;
+
+public:
+ SwHyperlinkEventDescriptor();
+
+ void copyMacrosFromINetFormat(const SwFormatINetFormat& aFormat);
+ void copyMacrosIntoINetFormat(SwFormatINetFormat& aFormat);
+
+ void
+ copyMacrosFromNameReplace(css::uno::Reference<css::container::XNameReplace> const& xReplace);
+};
+
+// SwEventDescriptor for
+// 1) SwXTextFrame
+// 2) SwXGraphicObject
+// 3) SwXEmbeddedObject
+// All these objects are an SwXFrame, so they can use a common implementation
+class SwFrameEventDescriptor final : public SvEventDescriptor
+{
+ SwXFrame& m_rFrame;
+
+public:
+ SwFrameEventDescriptor(SwXTextFrame& rFrameRef);
+ SwFrameEventDescriptor(SwXTextGraphicObject& rGraphicRef);
+ SwFrameEventDescriptor(SwXTextEmbeddedObject& rObjectRef);
+
+ virtual ~SwFrameEventDescriptor() override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+private:
+ virtual void setMacroItem(const SvxMacroItem& rItem) override;
+ virtual const SvxMacroItem& getMacroItem() override;
+ virtual sal_uInt16 getMacroItemWhich() const override;
+};
+
+class SwFrameStyleEventDescriptor final : public SvEventDescriptor
+{
+ sw::ICoreFrameStyle& m_rStyle;
+
+public:
+ SwFrameStyleEventDescriptor(sw::ICoreFrameStyle& rStyle);
+
+ virtual ~SwFrameStyleEventDescriptor() override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+
+private:
+ virtual void setMacroItem(const SvxMacroItem& rItem) override;
+ virtual const SvxMacroItem& getMacroItem() override;
+ virtual sal_uInt16 getMacroItemWhich() const override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unofield.hxx b/sw/source/core/inc/unofield.hxx
new file mode 100644
index 000000000..b6b281cec
--- /dev/null
+++ b/sw/source/core/inc/unofield.hxx
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOFIELD_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOFIELD_HXX
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/text/XDependentTextField.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <unobaseclass.hxx>
+#include <unocoll.hxx>
+#include <fldbas.hxx>
+
+class SwDoc;
+class SwFormatField;
+class SwSetExpField;
+
+typedef ::cppu::WeakImplHelper
+< css::beans::XPropertySet
+, css::lang::XServiceInfo
+, css::lang::XUnoTunnel
+, css::lang::XComponent
+> SwXFieldMaster_Base;
+
+class SwXFieldMaster final
+ : public SwXFieldMaster_Base
+{
+
+private:
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual ~SwXFieldMaster() override;
+
+ SwXFieldMaster(SwFieldType& rType, SwDoc * pDoc);
+
+ /// descriptor
+ SwXFieldMaster(SwDoc* pDoc, SwFieldIds nResId);
+
+public:
+
+ static css::uno::Reference<css::beans::XPropertySet>
+ CreateXFieldMaster(SwDoc * pDoc, SwFieldType * pType,
+ SwFieldIds nResId = SwFieldIds::Unknown);
+
+ static OUString GetProgrammaticName(const SwFieldType& rType, SwDoc& rDoc);
+ static OUString LocalizeFormula(const SwSetExpField& rField, const OUString& rFormula, bool bQuery);
+
+ SwFieldType* GetFieldType(bool bDontCreate = false) const;
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+};
+
+typedef ::cppu::WeakImplHelper
+< css::text::XDependentTextField
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::lang::XUnoTunnel
+, css::util::XUpdatable
+> SwXTextField_Base;
+
+/**
+ * UNO wrapper around an SwFormatField, i.e. a Writer field that the user creates via Insert ->
+ * Field.
+ */
+class SwXTextField final
+ : public SwXTextField_Base
+{
+
+private:
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual ~SwXTextField() override;
+
+ SwXTextField(SwFormatField& rFormat, SwDoc & rDoc);
+
+ /// descriptor
+ SwXTextField(SwServiceType nServiceId, SwDoc* pDoc);
+
+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,
+ SwServiceType nServiceId = SwServiceType::Invalid);
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XTextField
+ virtual OUString SAL_CALL getPresentation(sal_Bool bShowCommand) override;
+
+ // XDependentTextField
+ virtual void SAL_CALL attachTextFieldMaster(
+ const css::uno::Reference< css::beans::XPropertySet > & xFieldMaster) override;
+ virtual css::uno::Reference< css::beans::XPropertySet> SAL_CALL getTextFieldMaster() override;
+
+};
+
+typedef ::cppu::WeakImplHelper
+< css::container::XEnumeration
+, css::lang::XServiceInfo
+> SwXFieldEnumeration_Base;
+
+class SwXFieldEnumeration final
+ : public SwXFieldEnumeration_Base
+{
+
+private:
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual ~SwXFieldEnumeration() override;
+
+public:
+ explicit SwXFieldEnumeration(SwDoc & rDoc);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual css::uno::Any SAL_CALL nextElement() override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unoflatpara.hxx b/sw/source/core/inc/unoflatpara.hxx
new file mode 100644
index 000000000..4c53f7fd6
--- /dev/null
+++ b/sw/source/core/inc/unoflatpara.hxx
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOFLATPARA_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOFLATPARA_HXX
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/text/XFlatParagraph.hpp>
+#include <com/sun/star/text/XFlatParagraphIterator.hpp>
+#include <svl/listener.hxx>
+#include "unotextmarkup.hxx"
+#include <nodeoffset.hxx>
+
+#include <set>
+
+namespace com::sun::star::container { class XStringKeyMap; }
+namespace com::sun::star::text { class XTextRange; }
+class SwTextNode;
+class SwDoc;
+class ModelToViewHelper;
+
+typedef ::cppu::ImplInheritanceHelper
+< SwXTextMarkup
+, css::beans::XPropertySet
+, css::text::XFlatParagraph
+, css::lang::XUnoTunnel
+> SwXFlatParagraph_Base;
+
+class SwXFlatParagraph final
+ : public SwXFlatParagraph_Base
+{
+public:
+ SwXFlatParagraph( SwTextNode& rTextNode, const OUString& aExpandText, const ModelToViewHelper& rConversionMap );
+ virtual ~SwXFlatParagraph() override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // text::XTextMarkup:
+ virtual css::uno::Reference< css::container::XStringKeyMap > SAL_CALL getMarkupInfoContainer() override;
+
+ virtual void SAL_CALL commitStringMarkup(::sal_Int32 nType, const OUString & aIdentifier, ::sal_Int32 nStart, ::sal_Int32 nLength,
+ const css::uno::Reference< css::container::XStringKeyMap > & xMarkupInfoContainer) override;
+
+ virtual void SAL_CALL commitTextRangeMarkup(::sal_Int32 nType, const OUString & aIdentifier, const css::uno::Reference< css::text::XTextRange> & xRange,
+ const css::uno::Reference< css::container::XStringKeyMap > & xMarkupInfoContainer) override;
+
+ // text::XFlatParagraph:
+ virtual OUString SAL_CALL getText() override;
+ virtual sal_Bool SAL_CALL isModified() override;
+ virtual void SAL_CALL setChecked(::sal_Int32 nType, sal_Bool bVal) override;
+ virtual sal_Bool SAL_CALL isChecked(::sal_Int32 nType) override;
+ virtual css::lang::Locale SAL_CALL getLanguageOfText(::sal_Int32 nPos, ::sal_Int32 nLen) override;
+ virtual css::lang::Locale SAL_CALL getPrimaryLanguageOfText(::sal_Int32 nPos, ::sal_Int32 nLen) override;
+ virtual void SAL_CALL changeText(::sal_Int32 nPos, ::sal_Int32 nLen, const OUString & aNewText, const css::uno::Sequence< css::beans::PropertyValue > & aAttributes) override;
+ virtual void SAL_CALL changeAttributes(::sal_Int32 nPos, ::sal_Int32 nLen, const css::uno::Sequence< css::beans::PropertyValue > & aAttributes) override;
+ virtual css::uno::Sequence< ::sal_Int32 > SAL_CALL getLanguagePortions() override;
+
+ using SwXTextMarkup::GetTextNode;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 >& rId) override;
+
+private:
+ SwXFlatParagraph( const SwXFlatParagraph & ) = delete;
+ SwXFlatParagraph & operator = ( const SwXFlatParagraph & ) = delete;
+
+ OUString maExpandText;
+};
+
+class SwXFlatParagraphIterator final :
+ public ::cppu::WeakImplHelper
+ <
+ css::text::XFlatParagraphIterator
+ >,
+ public SvtListener
+{
+public:
+ SwXFlatParagraphIterator( SwDoc& rDoc, sal_Int32 nType, bool bAutomatic );
+ virtual ~SwXFlatParagraphIterator() override;
+
+ // text::XFlatParagraphIterator:
+ virtual css::uno::Reference< css::text::XFlatParagraph > SAL_CALL getFirstPara() override;
+ virtual css::uno::Reference< css::text::XFlatParagraph > SAL_CALL getNextPara() override;
+ virtual css::uno::Reference< css::text::XFlatParagraph > SAL_CALL getLastPara() override;
+ virtual css::uno::Reference< css::text::XFlatParagraph > SAL_CALL getParaBefore(const css::uno::Reference< css::text::XFlatParagraph > & xPara) override;
+ virtual css::uno::Reference< css::text::XFlatParagraph > SAL_CALL getParaAfter(const css::uno::Reference< css::text::XFlatParagraph > & xPara) override;
+
+ virtual void Notify( const SfxHint& ) override;
+
+private:
+ SwXFlatParagraphIterator( const SwXFlatParagraphIterator & ) = delete;
+ SwXFlatParagraphIterator & operator =(const SwXFlatParagraphIterator & ) = delete;
+
+ // container to hold the 'hard' references as long as necessary and valid
+ std::set< css::uno::Reference< css::text::XFlatParagraph > > m_aFlatParaList;
+
+ SwDoc* mpDoc;
+ const sal_Int32 mnType;
+ const bool mbAutomatic;
+
+ SwNodeOffset mnCurrentNode; // used for non-automatic mode
+ SwNodeOffset mnEndNode; // used for non-automatic mode
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unofldmid.h b/sw/source/core/inc/unofldmid.h
new file mode 100644
index 000000000..e6146fe01
--- /dev/null
+++ b/sw/source/core/inc/unofldmid.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOFLDMID_H
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOFLDMID_H
+
+// mapping of the properties on the descriptor
+#define FIELD_PROP_PAR1 10
+#define FIELD_PROP_PAR2 11
+#define FIELD_PROP_PAR3 12
+#define FIELD_PROP_FORMAT 13
+#define FIELD_PROP_SUBTYPE 14
+#define FIELD_PROP_BOOL1 15
+#define FIELD_PROP_BOOL2 16
+#define FIELD_PROP_DATE 17
+#define FIELD_PROP_USHORT1 18
+#define FIELD_PROP_USHORT2 19
+#define FIELD_PROP_BYTE1 20
+#define FIELD_PROP_DOUBLE 21
+#define FIELD_PROP_BOOL3 22
+#define FIELD_PROP_PAR4 23
+#define FIELD_PROP_SHORT1 24
+#define FIELD_PROP_DATE_TIME 25
+#define FIELD_PROP_PROP_SEQ 26
+#define FIELD_PROP_LOCALE 27
+#define FIELD_PROP_BOOL4 28
+#define FIELD_PROP_STRINGS 29
+#define FIELD_PROP_PAR5 30
+#define FIELD_PROP_GRABBAG 31
+
+#define FIELD_PROP_IS_FIELD_USED 32
+#define FIELD_PROP_IS_FIELD_DISPLAYED 33
+
+#define FIELD_PROP_TEXT 34
+#define FIELD_PROP_TITLE 35
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unofootnote.hxx b/sw/source/core/inc/unofootnote.hxx
new file mode 100644
index 000000000..b30c89d8b
--- /dev/null
+++ b/sw/source/core/inc/unofootnote.hxx
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOFOOTNOTE_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOFOOTNOTE_HXX
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/text/XFootnote.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <unotext.hxx>
+
+class SwDoc;
+class SwFormatFootnote;
+
+typedef ::cppu::WeakImplHelper
+< css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::container::XEnumerationAccess
+, css::text::XFootnote
+> SwXFootnote_Base;
+
+class SwXFootnote final
+ : public SwXFootnote_Base
+ , public SwXText
+{
+ friend class SwXFootnotes;
+
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual const SwStartNode *GetStartNode() const override;
+
+ virtual css::uno::Reference< css::text::XTextCursor > CreateCursor() override;
+
+ virtual ~SwXFootnote() override;
+
+ SwXFootnote(SwDoc & rDoc, SwFormatFootnote & rFormat);
+ SwXFootnote(const bool bEndnote);
+
+public:
+
+ static css::uno::Reference<css::text::XFootnote>
+ CreateXFootnote(SwDoc & rDoc, SwFormatFootnote * pFootnoteFormat,
+ bool isEndnote = false);
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface(
+ const css::uno::Type& rType) override;
+ virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); }
+ virtual void SAL_CALL release() noexcept override { OWeakObject::release(); }
+
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type >
+ SAL_CALL getTypes() override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL
+ createEnumeration() override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XFootnote
+ virtual OUString SAL_CALL getLabel() override;
+ virtual void SAL_CALL setLabel(const OUString& rLabel) override;
+
+ // XSimpleText
+ virtual css::uno::Reference< css::text::XTextCursor > SAL_CALL
+ createTextCursor() override;
+ virtual css::uno::Reference< css::text::XTextCursor > SAL_CALL
+ createTextCursorByRange(
+ const css::uno::Reference< css::text::XTextRange > & xTextPosition) override;
+
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOFOOTNOTE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unoidx.hxx b/sw/source/core/inc/unoidx.hxx
new file mode 100644
index 000000000..d7db05373
--- /dev/null
+++ b/sw/source/core/inc/unoidx.hxx
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOIDX_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOIDX_HXX
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/util/XRefreshable.hpp>
+#include <com/sun/star/text/XDocumentIndexMark.hpp>
+#include <com/sun/star/text/XDocumentIndex.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <sfx2/Metadatable.hxx>
+
+#include <toxe.hxx>
+#include <unobaseclass.hxx>
+
+class SwDoc;
+class SwTOXBaseSection;
+class SwTOXMark;
+class SwTOXType;
+
+typedef ::cppu::ImplInheritanceHelper
+< ::sfx2::MetadatableMixin
+, css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::container::XNamed
+, css::util::XRefreshable
+, css::text::XDocumentIndex
+> SwXDocumentIndex_Base;
+
+class SwXDocumentIndex final
+ : public SwXDocumentIndex_Base
+{
+
+private:
+
+ class StyleAccess_Impl;
+ class TokenAccess_Impl;
+
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual ~SwXDocumentIndex() override;
+
+ SwXDocumentIndex(SwTOXBaseSection &, SwDoc &);
+
+ /// descriptor
+ SwXDocumentIndex(const TOXTypes eToxType, SwDoc& rDoc);
+
+public:
+
+ static css::uno::Reference< css::text::XDocumentIndex>
+ CreateXDocumentIndex(SwDoc & rDoc, SwTOXBaseSection * pSection,
+ TOXTypes eTypes = TOX_INDEX);
+
+ // MetadatableMixin
+ virtual ::sfx2::Metadatable* GetCoreObject() override;
+ virtual css::uno::Reference< css::frame::XModel >
+ GetModel() override;
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& rName) override;
+
+ // XRefreshable
+ virtual void SAL_CALL refresh() override;
+ virtual void SAL_CALL addRefreshListener(
+ const css::uno::Reference< css::util::XRefreshListener>& xListener) override;
+ virtual void SAL_CALL removeRefreshListener(
+ const css::uno::Reference< css::util::XRefreshListener>& xListener) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XDocumentIndex
+ virtual OUString SAL_CALL getServiceName() override;
+ virtual void SAL_CALL update() override;
+
+};
+
+typedef ::cppu::WeakImplHelper
+< css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::text::XDocumentIndexMark
+> SwXDocumentIndexMark_Base;
+
+class SwXDocumentIndexMark final
+ : public SwXDocumentIndexMark_Base
+{
+
+private:
+
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual ~SwXDocumentIndexMark() override;
+
+ SwXDocumentIndexMark(SwDoc & rDoc,
+ const SwTOXType & rType, const SwTOXMark & rMark);
+
+ /// descriptor
+ SwXDocumentIndexMark(const TOXTypes eToxType);
+
+public:
+
+ static css::uno::Reference< css::text::XDocumentIndexMark>
+ CreateXDocumentIndexMark(SwDoc & rDoc,
+ SwTOXMark * pMark, TOXTypes eType = TOX_INDEX);
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XDocumentIndexMark
+ virtual OUString SAL_CALL getMarkEntry() override;
+ virtual void SAL_CALL setMarkEntry(const OUString& rIndexEntry) override;
+
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unolinebreak.hxx b/sw/source/core/inc/unolinebreak.hxx
new file mode 100644
index 000000000..469685566
--- /dev/null
+++ b/sw/source/core/inc/unolinebreak.hxx
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOLINEBREAK_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOLINEBREAK_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+
+#include <unobaseclass.hxx>
+
+class SwDoc;
+class SwFormatLineBreak;
+
+/// UNO API wrapper around an SwFormatLineBreak, exposed as the com.sun.star.text.LineBreak service.
+class SwXLineBreak final
+ : public cppu::WeakImplHelper<css::beans::XPropertySet, css::lang::XServiceInfo,
+ css::text::XTextContent, css::lang::XUnoTunnel>
+{
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ SwXLineBreak(SwFormatLineBreak& rFormat);
+ SwXLineBreak();
+
+ ~SwXLineBreak() override;
+
+public:
+ static css::uno::Reference<css::text::XTextContent>
+ CreateXLineBreak(SwFormatLineBreak* pLineBreakFormat);
+
+ // XPropertySet
+ css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ void SAL_CALL setPropertyValue(const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ css::uno::Any SAL_CALL getPropertyValue(const OUString& rPropertyName) override;
+ void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& xListener) override;
+ void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& xListener) override;
+ void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& xListener) override;
+ void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& xListener) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XTextContent
+ void SAL_CALL attach(const css::uno::Reference<css::text::XTextRange>& xTextRange) override;
+ css::uno::Reference<css::text::XTextRange> SAL_CALL getAnchor() override;
+
+ // XComponent, via XTextContent
+ void SAL_CALL dispose() override;
+ void SAL_CALL
+ addEventListener(const css::uno::Reference<css::lang::XEventListener>& xListener) override;
+ void SAL_CALL
+ removeEventListener(const css::uno::Reference<css::lang::XEventListener>& xListener) override;
+
+ // XUnoTunnel
+ sal_Int64 SAL_CALL getSomething(const css::uno::Sequence<sal_Int8>& rIdentifier) override;
+
+ static const css::uno::Sequence<sal_Int8>& getUnoTunnelId();
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOLINEBREAK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unometa.hxx b/sw/source/core/inc/unometa.hxx
new file mode 100644
index 000000000..ca298fcd8
--- /dev/null
+++ b/sw/source/core/inc/unometa.hxx
@@ -0,0 +1,270 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOMETA_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOMETA_HXX
+
+#include <memory>
+#include <deque>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <sfx2/Metadatable.hxx>
+
+#include <unobaseclass.hxx>
+
+typedef std::deque<
+ css::uno::Reference< css::text::XTextRange > >
+ TextRangeList_t;
+
+class SwPaM;
+class SwTextNode;
+
+namespace sw {
+ class Meta;
+}
+
+typedef ::cppu::ImplInheritanceHelper
+< ::sfx2::MetadatableMixin
+, css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::container::XChild
+, css::container::XEnumerationAccess
+, css::text::XTextContent
+, css::text::XText
+> SwXMeta_Base;
+
+class SwXMeta
+ : public SwXMeta_Base
+{
+
+public:
+
+ class Impl;
+
+protected:
+
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ void AttachImpl(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange,
+ const sal_uInt16 nWhich);
+
+ virtual ~SwXMeta() override;
+
+ SwXMeta(SwXMeta const&) = delete;
+ SwXMeta& operator=(SwXMeta const&) = delete;
+
+ /// @param pDoc and pMeta != 0, but not & because of ImplInheritanceHelper
+ SwXMeta(SwDoc *const pDoc, ::sw::Meta *const pMeta,
+ css::uno::Reference< css::text::XText> const& xParentText,
+ std::unique_ptr<TextRangeList_t const> pPortions);
+
+ SwXMeta(SwDoc *const pDoc);
+
+public:
+
+ static css::uno::Reference< css::rdf::XMetadatable >
+ CreateXMeta(
+ ::sw::Meta & rMeta,
+ css::uno::Reference< css::text::XText> const& xParentText = nullptr,
+ std::unique_ptr<TextRangeList_t const> && pPortions = std::unique_ptr<TextRangeList_t const>());
+
+ static css::uno::Reference<css::rdf::XMetadatable>
+ CreateXMeta(SwDoc & rDoc, bool isField);
+
+ /// init params with position of the attribute content (w/out CH_TXTATR)
+ bool SetContentRange( SwTextNode *& rpNode, sal_Int32 & rStart, sal_Int32 & rEnd) const;
+ css::uno::Reference< css::text::XText > const & GetParentText() const;
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb);
+
+ // MetadatableMixin
+ virtual ::sfx2::Metadatable * GetCoreObject() override;
+ virtual css::uno::Reference< css::frame::XModel >
+ GetModel() override;
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& Identifier ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XChild
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL
+ getParent() override;
+ virtual void SAL_CALL setParent(
+ css::uno::Reference< css::uno::XInterface> const& xParent) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL
+ createEnumeration() override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XTextRange
+ virtual css::uno::Reference< css::text::XText >
+ SAL_CALL getText() override;
+ virtual css::uno::Reference<
+ css::text::XTextRange > SAL_CALL getStart() override;
+ virtual css::uno::Reference<
+ css::text::XTextRange > SAL_CALL getEnd() override;
+ virtual OUString SAL_CALL getString() override;
+ virtual void SAL_CALL setString(const OUString& rString) override;
+
+ // XSimpleText
+ virtual css::uno::Reference< css::text::XTextCursor > SAL_CALL
+ createTextCursor() override;
+ virtual css::uno::Reference< css::text::XTextCursor > SAL_CALL
+ createTextCursorByRange(
+ const css::uno::Reference< css::text::XTextRange > & xTextPosition) override;
+ virtual void SAL_CALL insertString(
+ const css::uno::Reference< css::text::XTextRange > & xRange,
+ const OUString& aString, sal_Bool bAbsorb) override;
+ virtual void SAL_CALL insertControlCharacter(
+ const css::uno::Reference< css::text::XTextRange > & xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb) override;
+
+ // XText
+ virtual void SAL_CALL insertTextContent(
+ const css::uno::Reference< css::text::XTextRange > & xRange,
+ const css::uno::Reference< css::text::XTextContent > & xContent,
+ sal_Bool bAbsorb) override;
+ virtual void SAL_CALL removeTextContent(
+ const css::uno::Reference< css::text::XTextContent > & xContent) override;
+
+};
+
+typedef ::cppu::ImplInheritanceHelper
+< SwXMeta
+, css::beans::XPropertySet
+, css::text::XTextField
+> SwXMetaField_Base;
+
+class SwXMetaField final
+ : public SwXMetaField_Base
+{
+
+private:
+
+ virtual ~SwXMetaField() override;
+
+ friend css::uno::Reference< css::rdf::XMetadatable >
+ SwXMeta::CreateXMeta(::sw::Meta &,
+ css::uno::Reference< css::text::XText> const&,
+ std::unique_ptr<TextRangeList_t const> && pPortions);
+
+ SwXMetaField(SwDoc *const pDoc, ::sw::Meta *const pMeta,
+ css::uno::Reference< css::text::XText> const& xParentText,
+ std::unique_ptr<TextRangeList_t const> pPortions);
+
+ friend css::uno::Reference<css::rdf::XMetadatable>
+ SwXMeta::CreateXMeta(SwDoc &, bool);
+
+ SwXMetaField(SwDoc *const pDoc);
+
+public:
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& ServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames( ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL
+ getPropertyValue(const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XTextField
+ virtual OUString SAL_CALL getPresentation(sal_Bool bShowCommand) override;
+
+};
+
+/// get prefix/suffix from the RDF repository. @throws RuntimeException
+void getPrefixAndSuffix(
+ const css::uno::Reference< css::frame::XModel>& xModel,
+ const css::uno::Reference< css::rdf::XMetadatable>& xMetaField,
+ OUString *const o_pPrefix, OUString *const o_pSuffix, OUString *const o_pShadowColor);
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOMETA_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unoparaframeenum.hxx b/sw/source/core/inc/unoparaframeenum.hxx
new file mode 100644
index 000000000..017232c5a
--- /dev/null
+++ b/sw/source/core/inc/unoparaframeenum.hxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOPARAFRAMEENUM_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOPARAFRAMEENUM_HXX
+
+#include <deque>
+
+#include <calbck.hxx>
+#include <unobaseclass.hxx>
+
+
+class SwNodeIndex;
+class SwPaM;
+class SwFrameFormat;
+
+namespace sw
+{
+ struct FrameClient final : public SwClient
+ {
+ FrameClient(sw::BroadcastingModify* pModify) : SwClient(pModify) {};
+ };
+}
+struct FrameClientSortListEntry
+{
+ sal_Int32 nIndex;
+ sal_uInt32 nOrder;
+ std::shared_ptr<sw::FrameClient> pFrameClient;
+
+ FrameClientSortListEntry (sal_Int32 const i_nIndex,
+ sal_uInt32 const i_nOrder, std::shared_ptr<sw::FrameClient> i_pClient)
+ : nIndex(i_nIndex), nOrder(i_nOrder), pFrameClient(std::move(i_pClient)) { }
+};
+
+typedef std::deque< FrameClientSortListEntry >
+ FrameClientSortList_t;
+
+typedef std::deque< std::shared_ptr<sw::FrameClient> >
+ FrameClientList_t;
+
+// #i28701# - adjust 4th parameter
+void CollectFrameAtNode( const SwNodeIndex& rIdx,
+ FrameClientSortList_t& rFrames,
+ const bool bAtCharAnchoredObjs );
+
+enum ParaFrameMode
+{
+ PARAFRAME_PORTION_PARAGRAPH,
+ PARAFRAME_PORTION_CHAR,
+ PARAFRAME_PORTION_TEXTRANGE,
+};
+
+struct SwXParaFrameEnumeration
+ : public SwSimpleEnumeration_Base
+{
+ static rtl::Reference<SwXParaFrameEnumeration> Create(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat = nullptr);
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOPARAFRAMEENUM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unoport.hxx b/sw/source/core/inc/unoport.hxx
new file mode 100644
index 000000000..4c0098be8
--- /dev/null
+++ b/sw/source/core/inc/unoport.hxx
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOPORT_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOPORT_HXX
+
+#include <memory>
+#include <deque>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XTolerantMultiPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <svl/itemprop.hxx>
+#include <svl/listener.hxx>
+
+#include <unocrsr.hxx>
+
+namespace com::sun::star::beans { struct PropertyValue; }
+namespace com::sun::star::text { class XTextField; }
+namespace com::sun::star::text { class XFootnote; }
+
+class SwFrameFormat;
+class SwRangeRedline;
+class SwTextRuby;
+
+typedef std::deque<
+ css::uno::Reference< css::text::XTextRange > >
+ TextRangeList_t;
+
+enum SwTextPortionType
+{
+ PORTION_TEXT,
+ PORTION_FIELD,
+ PORTION_FRAME,
+ PORTION_FOOTNOTE,
+ PORTION_REFMARK_START,
+ PORTION_REFMARK_END,
+ PORTION_TOXMARK_START,
+ PORTION_TOXMARK_END,
+ PORTION_BOOKMARK_START,
+ PORTION_BOOKMARK_END,
+ PORTION_REDLINE_START,
+ PORTION_REDLINE_END,
+ PORTION_RUBY_START,
+ PORTION_RUBY_END,
+ PORTION_SOFT_PAGEBREAK,
+ PORTION_META,
+ PORTION_FIELD_START,
+ PORTION_FIELD_SEP,
+ PORTION_FIELD_END,
+ PORTION_FIELD_START_END,
+ PORTION_ANNOTATION,
+ PORTION_ANNOTATION_END,
+ PORTION_LINEBREAK,
+ PORTION_CONTENT_CONTROL,
+ PORTION_LIST_AUTOFMT
+};
+
+class SwXTextPortion : public cppu::WeakImplHelper
+<
+ css::beans::XTolerantMultiPropertySet,
+ css::beans::XMultiPropertySet,
+ css::beans::XPropertySet,
+ css::text::XTextRange,
+ css::beans::XPropertyState,
+ css::container::XContentEnumerationAccess,
+ css::lang::XUnoTunnel,
+ css::lang::XServiceInfo
+>,
+ public SvtListener
+{
+private:
+
+ const SfxItemPropertySet * m_pPropSet;
+ const css::uno::Reference< css::text::XText >
+ m_xParentText;
+ css::uno::Reference< css::text::XTextContent >
+ m_xRefMark;
+ css::uno::Reference< css::text::XTextContent >
+ m_xTOXMark;
+ css::uno::Reference< css::text::XTextContent >
+ m_xBookmark;
+ css::uno::Reference< css::text::XFootnote >
+ m_xFootnote;
+ css::uno::Reference< css::text::XTextField >
+ m_xTextField;
+ css::uno::Reference< css::text::XTextContent >
+ m_xMeta;
+ css::uno::Reference<css::text::XTextContent> m_xLineBreak;
+ css::uno::Reference<css::text::XTextContent> m_xContentControl;
+ std::unique_ptr< css::uno::Any > m_pRubyText;
+ std::unique_ptr< css::uno::Any > m_pRubyStyle;
+ std::unique_ptr< css::uno::Any > m_pRubyAdjust;
+ std::unique_ptr< css::uno::Any > m_pRubyIsAbove;
+ std::unique_ptr< css::uno::Any > m_pRubyPosition;
+ sw::UnoCursorPointer m_pUnoCursor;
+
+ SwFrameFormat* m_pFrameFormat;
+ const SwTextPortionType m_ePortionType;
+
+ bool m_bIsCollapsed;
+
+ /// Expose the paragraph's RES_PARATR_LIST_AUTOFMT, not the char props of the underlying (empty)
+ /// text.
+ bool m_bIsListAutoFormat;
+
+ void init(const SwUnoCursor* pPortionCursor);
+
+protected:
+ /// @throws css::beans::UnknownPropertyException
+ /// @throws css::beans::PropertyVetoException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::lang::WrappedTargetException
+ /// @throws css::uno::RuntimeException
+ void SetPropertyValues_Impl(
+ const css::uno::Sequence< OUString >& aPropertyNames,
+ const css::uno::Sequence< css::uno::Any >& aValues );
+ /// @throws css::beans::UnknownPropertyException
+ /// @throws css::lang::WrappedTargetException
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< css::uno::Any > GetPropertyValues_Impl(
+ const css::uno::Sequence< OUString >& aPropertyNames );
+
+ void GetPropertyValue( css::uno::Any &rVal,
+ const SfxItemPropertyMapEntry& rEntry, SwUnoCursor *pUnoCursor, std::unique_ptr<SfxItemSet> &pSet );
+
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence<css::beans::GetDirectPropertyTolerantResult> GetPropertyValuesTolerant_Impl(
+ const css::uno::Sequence< OUString >& rPropertyNames, bool bDirectValuesOnly );
+
+ virtual ~SwXTextPortion() override;
+
+ virtual void Notify(const SfxHint& rHint) override;
+
+public:
+ SwXTextPortion(const SwUnoCursor* pPortionCursor, css::uno::Reference< css::text::XText > const& rParent, SwTextPortionType eType );
+ SwXTextPortion(const SwUnoCursor* pPortionCursor, css::uno::Reference< css::text::XText > const& rParent, SwFrameFormat& rFormat );
+
+ // for Ruby
+ SwXTextPortion(const SwUnoCursor* pPortionCursor,
+ SwTextRuby const& rAttr,
+ css::uno::Reference< css::text::XText > const& xParent,
+ bool bIsEnd );
+
+ //XTextRange
+ virtual css::uno::Reference< css::text::XText > SAL_CALL getText() override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getStart() override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getEnd() override;
+ virtual OUString SAL_CALL getString() override;
+ virtual void SAL_CALL setString(const OUString& aString) override;
+
+ //XTolerantMultiPropertySet
+ virtual css::uno::Sequence< css::beans::SetPropertyTolerantFailed > SAL_CALL setPropertyValuesTolerant( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+ virtual css::uno::Sequence< css::beans::GetPropertyTolerantResult > SAL_CALL getPropertyValuesTolerant( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ virtual css::uno::Sequence< css::beans::GetDirectPropertyTolerantResult > SAL_CALL getDirectPropertyValuesTolerant( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+
+ //XMultiPropertySet
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+ virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+
+ //XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ //XPropertyState
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override;
+ virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( const css::uno::Sequence< OUString >& aPropertyName ) override;
+ virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override;
+ virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override;
+
+ //XUnoTunnel
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ //XContentEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames() override;
+
+ void SetRefMark( css::uno::Reference< css::text::XTextContent > const & xMark)
+ { m_xRefMark = xMark; }
+
+ void SetTOXMark( css::uno::Reference< css::text::XTextContent > const & xMark)
+ { m_xTOXMark = xMark; }
+
+ void SetBookmark( css::uno::Reference< css::text::XTextContent > const & xMark)
+ { m_xBookmark = xMark; }
+
+ void SetFootnote( css::uno::Reference< css::text::XFootnote > const & xNote)
+ { m_xFootnote = xNote; }
+
+ void SetTextField( css::uno::Reference< css::text::XTextField> const & xField)
+ { m_xTextField = xField; }
+
+ void SetMeta( css::uno::Reference< css::text::XTextContent > const & xMeta)
+ { m_xMeta = xMeta; }
+
+ void SetLineBreak(css::uno::Reference<css::text::XTextContent> const& xLineBreak)
+ {
+ m_xLineBreak = xLineBreak;
+ }
+
+ void SetContentControl(const css::uno::Reference<css::text::XTextContent>& xContentControl)
+ {
+ m_xContentControl = xContentControl;
+ }
+
+ void SetCollapsed(bool bSet) { m_bIsCollapsed = bSet;}
+
+ SwTextPortionType GetTextPortionType() const { return m_ePortionType; }
+
+ SwUnoCursor& GetCursor() const
+ { return *m_pUnoCursor; }
+};
+
+class SwXTextPortionEnumeration final
+ : public ::cppu::WeakImplHelper
+ < css::container::XEnumeration
+ , css::lang::XServiceInfo
+ , css::lang::XUnoTunnel
+ >
+{
+ TextRangeList_t m_Portions; // contains all portions, filled by ctor
+ sw::UnoCursorPointer m_pUnoCursor;
+
+ virtual ~SwXTextPortionEnumeration() override;
+
+public:
+ SwXTextPortionEnumeration(SwPaM& rParaCursor,
+ css::uno::Reference< css::text::XText > const & xParent,
+ const sal_Int32 nStart, const sal_Int32 nEnd );
+
+ SwXTextPortionEnumeration(SwPaM& rParaCursor,
+ TextRangeList_t && rPortions );
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ //XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ //XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual css::uno::Any SAL_CALL nextElement() override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+};
+
+class SwXRedlinePortion final : public SwXTextPortion
+{
+private:
+ SwRangeRedline const& m_rRedline;
+
+ bool Validate();
+
+ using SwXTextPortion::GetPropertyValue;
+
+ virtual ~SwXRedlinePortion() override;
+
+public:
+ SwXRedlinePortion(
+ SwRangeRedline const& rRedline,
+ SwUnoCursor const* pPortionCursor,
+ css::uno::Reference< css::text::XText > const& xParent,
+ bool const bIsStart);
+
+ /// @throws std::exception
+ static css::uno::Any GetPropertyValue(
+ std::u16string_view PropertyName, SwRangeRedline const& rRedline);
+ /// @throws std::exception
+ static css::uno::Sequence< css::beans::PropertyValue > CreateRedlineProperties(
+ SwRangeRedline const& rRedline, bool const bIsStart);
+
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ // XPropertySet
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unorefmark.hxx b/sw/source/core/inc/unorefmark.hxx
new file mode 100644
index 000000000..94d81dd28
--- /dev/null
+++ b/sw/source/core/inc/unorefmark.hxx
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOREFMARK_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOREFMARK_HXX
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <unobaseclass.hxx>
+
+class SwDoc;
+class SwFormatRefMark;
+
+typedef ::cppu::WeakImplHelper
+< css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::container::XNamed
+, css::text::XTextContent
+> SwXReferenceMark_Base;
+
+class SwXReferenceMark final
+ : public SwXReferenceMark_Base
+{
+
+private:
+
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ virtual ~SwXReferenceMark() override;
+
+ SwXReferenceMark(SwDoc *const pDoc, SwFormatRefMark *const pMark);
+
+public:
+
+ static css::uno::Reference<css::text::XTextContent>
+ CreateXReferenceMark(SwDoc & rDoc, SwFormatRefMark * pMarkFormat);
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& rName) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOREFMARK_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unosection.hxx b/sw/source/core/inc/unosection.hxx
new file mode 100644
index 000000000..2b12b7364
--- /dev/null
+++ b/sw/source/core/inc/unosection.hxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOSECTION_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOSECTION_HXX
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <sfx2/Metadatable.hxx>
+
+#include <unobaseclass.hxx>
+
+class SwSectionFormat;
+
+typedef ::cppu::ImplInheritanceHelper
+< ::sfx2::MetadatableMixin
+, css::lang::XUnoTunnel
+, css::lang::XServiceInfo
+, css::beans::XPropertySet
+, css::beans::XPropertyState
+, css::beans::XMultiPropertySet
+, css::container::XNamed
+, css::text::XTextSection
+> SwXTextSection_Base;
+
+class SwXTextSection final
+ : public SwXTextSection_Base
+{
+
+private:
+
+ class Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+ SwXTextSection(SwSectionFormat *const pFormat, const bool bIndexHeader);
+
+ virtual ~SwXTextSection() override;
+
+public:
+
+ SwSectionFormat* GetFormat() const;
+
+ static css::uno::Reference< css::text::XTextSection >
+ CreateXTextSection(SwSectionFormat *const pFormat,
+ const bool bIndexHeader = false);
+
+ // MetadatableMixin
+ virtual ::sfx2::Metadatable* GetCoreObject() override;
+ virtual css::uno::Reference< css::frame::XModel >
+ GetModel() override;
+
+ static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId();
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething(
+ const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(
+ const OUString& rServiceName) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+ virtual void SAL_CALL removeEventListener(
+ const css::uno::Reference< css::lang::XEventListener > & xListener) override;
+
+ // XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL
+ getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue(
+ const OUString& rPropertyName,
+ const css::uno::Any& rValue) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue(
+ const OUString& rPropertyName) override;
+ virtual void SAL_CALL addPropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertyChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener) override;
+ virtual void SAL_CALL addVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+ virtual void SAL_CALL removeVetoableChangeListener(
+ const OUString& rPropertyName,
+ const css::uno::Reference< css::beans::XVetoableChangeListener >& xListener) override;
+
+ // XPropertyState
+ virtual css::beans::PropertyState SAL_CALL
+ getPropertyState(const OUString& rPropertyName) override;
+ virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL
+ getPropertyStates(
+ const css::uno::Sequence< OUString >& rPropertyNames) override;
+ virtual void SAL_CALL setPropertyToDefault(
+ const OUString& rPropertyName) override;
+ virtual css::uno::Any SAL_CALL getPropertyDefault(
+ const OUString& rPropertyName) override;
+
+ // XMultiPropertySet
+ virtual void SAL_CALL setPropertyValues(
+ const css::uno::Sequence< OUString >& rPropertyNames,
+ const css::uno::Sequence< css::uno::Any >& rValues) override;
+ virtual css::uno::Sequence< css::uno::Any >
+ SAL_CALL getPropertyValues(
+ const css::uno::Sequence< OUString >& rPropertyNames) override;
+ virtual void SAL_CALL addPropertiesChangeListener(
+ const css::uno::Sequence< OUString >& rPropertyNames,
+ const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener) override;
+ virtual void SAL_CALL removePropertiesChangeListener(
+ const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener) override;
+ virtual void SAL_CALL firePropertiesChangeEvent(
+ const css::uno::Sequence< OUString >& rPropertyNames,
+ const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& rName) override;
+
+ // XTextContent
+ virtual void SAL_CALL attach(
+ const css::uno::Reference< css::text::XTextRange > & xTextRange) override;
+ virtual css::uno::Reference< css::text::XTextRange > SAL_CALL getAnchor() override;
+
+ // XTextSection
+ virtual css::uno::Reference< css::text::XTextSection > SAL_CALL
+ getParentSection() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::text::XTextSection > > SAL_CALL
+ getChildSections() override;
+
+};
+
+#endif // INCLUDED_SW_SOURCE_CORE_INC_UNOSECTION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/unotextmarkup.hxx b/sw/source/core/inc/unotextmarkup.hxx
new file mode 100644
index 000000000..4475bafda
--- /dev/null
+++ b/sw/source/core/inc/unotextmarkup.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_UNOTEXTMARKUP_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_UNOTEXTMARKUP_HXX
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/text/XTextMarkup.hpp>
+#include <com/sun/star/text/XMultiTextMarkup.hpp>
+
+#include <unobaseclass.hxx>
+
+#include <map>
+
+namespace com::sun::star::container { class XStringKeyMap; }
+namespace com::sun::star::text { class XTextRange; }
+class SwTextNode;
+class ModelToViewHelper;
+
+/** Implementation of the css::text::XTextMarkup interface
+ */
+class SwXTextMarkup
+ : public ::cppu::WeakImplHelper
+ < css::text::XTextMarkup
+ , css::text::XMultiTextMarkup
+ >
+{
+public:
+ SwXTextMarkup(SwTextNode *const rTextNode,
+ const ModelToViewHelper& rConversionMap);
+ virtual ~SwXTextMarkup() override;
+
+ // css::text::XTextMarkup:
+ virtual css::uno::Reference< css::container::XStringKeyMap > SAL_CALL getMarkupInfoContainer() override;
+
+ virtual void SAL_CALL commitStringMarkup(::sal_Int32 nType, const OUString & aIdentifier, ::sal_Int32 nStart, ::sal_Int32 nLength,
+ const css::uno::Reference< css::container::XStringKeyMap > & xMarkupInfoContainer) override;
+
+ virtual void SAL_CALL commitTextRangeMarkup(::sal_Int32 nType, const OUString & aIdentifier, const css::uno::Reference< css::text::XTextRange> & xRange,
+ const css::uno::Reference< css::container::XStringKeyMap > & xMarkupInfoContainer) override;
+
+ // css::text::XMultiTextMarkup:
+ virtual void SAL_CALL commitMultiTextMarkup( const css::uno::Sequence< css::text::TextMarkupDescriptor >& aMarkups ) override;
+
+private:
+ SwXTextMarkup( const SwXTextMarkup & ) = delete;
+ SwXTextMarkup & operator =( const SwXTextMarkup & ) = delete;
+
+ struct Impl;
+ ::sw::UnoImplPtr<Impl> m_pImpl;
+
+protected:
+ SwTextNode* GetTextNode();
+ void ClearTextNode();
+ const ModelToViewHelper& GetConversionMap() const;
+};
+
+/** Implementation of the css::container::XStringKeyMap interface
+ */
+class SwXStringKeyMap final :
+ public ::cppu::WeakImplHelper<
+ css::container::XStringKeyMap>
+{
+public:
+ SwXStringKeyMap();
+
+ // css::container::XStringKeyMap:
+ virtual css::uno::Any SAL_CALL getValue(const OUString & aKey) override;
+ virtual sal_Bool SAL_CALL hasValue(const OUString & aKey) override;
+ virtual void SAL_CALL insertValue(const OUString & aKey, const css::uno::Any & aValue) override;
+ virtual ::sal_Int32 SAL_CALL getCount() override;
+ virtual OUString SAL_CALL getKeyByIndex(::sal_Int32 nIndex) override;
+ virtual css::uno::Any SAL_CALL getValueByIndex(::sal_Int32 nIndex) override;
+
+private:
+ SwXStringKeyMap(SwXStringKeyMap const &) = delete;
+ void operator =(SwXStringKeyMap const &) = delete;
+
+ virtual ~SwXStringKeyMap() override {}
+
+ std::map< OUString, css::uno::Any > maMap;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/viewimp.hxx b/sw/source/core/inc/viewimp.hxx
new file mode 100644
index 000000000..fdd0ed330
--- /dev/null
+++ b/sw/source/core/inc/viewimp.hxx
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_VIEWIMP_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_VIEWIMP_HXX
+
+#include <tools/color.hxx>
+#include <svx/svdtypes.hxx>
+#include <swrect.hxx>
+#include <swregion.hxx>
+#include <vector>
+#include <memory>
+
+class OutputDevice;
+class SwViewShell;
+class SwFlyFrame;
+class SwViewOption;
+class SwFrame;
+class SwLayAction;
+class SwLayIdle;
+class SwDrawView;
+class SdrPageView;
+class SwPageFrame;
+class SwAccessibleMap;
+class SdrObject;
+class Fraction;
+class SwPrintData;
+class SwPagePreviewLayout;
+struct PreviewPage;
+class SwTextFrame;
+// --> OD #i76669#
+namespace sdr::contact { class ViewObjectContactRedirector; }
+// <--
+
+class SwViewShellImp
+{
+ friend class SwViewShell;
+
+ friend class SwLayAction; // Lay- and IdleAction register and deregister
+ friend class SwLayIdle;
+
+ // for paint of page preview
+ friend class SwPagePreviewLayout;
+
+ SwViewShell *m_pShell; // If someone passes an Imp, but needs a SwViewShell, we
+ // keep a backlink here
+
+ std::unique_ptr<SwDrawView> m_pDrawView; // Our DrawView
+ SdrPageView *m_pSdrPageView; // Exactly one Page for our DrawView
+
+ SwPageFrame *m_pFirstVisiblePage; // Always points to the first visible Page
+ std::unique_ptr<SwRegionRects> m_pPaintRegion; // Collector of Paintrects from the LayAction
+
+ std::vector<SwRect> m_pendingLOKInvalidations;
+
+ SwLayAction *m_pLayAction; // Is set if an Action object exists
+ // Is registered by the SwLayAction ctor and deregistered by the dtor
+ SwLayIdle *m_pIdleAct; // The same as SwLayAction for SwLayIdle
+
+ /// note: the map is *uniquely* owned here - the shared_ptr is only
+ /// used so that SwAccessibleContext can check via weak_ptr that it's alive
+ std::shared_ptr<SwAccessibleMap> m_pAccessibleMap;
+
+ bool m_bFirstPageInvalid : 1; // Pointer to the first Page invalid?
+ bool m_bResetHdlHiddenPaint : 1; // Ditto
+ bool m_bSmoothUpdate : 1; // For SmoothScroll
+ bool m_bStopSmooth : 1;
+
+ sal_uInt16 m_nRestoreActions ; // Count for the Action that need to be restored (UNO)
+ SwRect m_aSmoothRect;
+
+ std::unique_ptr<SwPagePreviewLayout> m_pPagePreviewLayout;
+
+ void SetFirstVisPage(OutputDevice const * pRenderContext); // Recalculate the first visible Page
+
+ void StartAction(); // Show handle and hide
+ void EndAction(); // Called by SwViewShell::ImplXXXAction
+ void LockPaint(); // Ditto; called by SwViewShell::ImplLockPaint
+ void UnlockPaint();
+
+private:
+
+ SwAccessibleMap *CreateAccessibleMap();
+
+ /** invalidate CONTENT_FLOWS_FROM/_TO relation for paragraphs
+
+ #i27138#
+ implementation for wrapper method
+ <SwViewShell::InvalidateAccessibleParaFlowRelation(..)>
+
+ @param _pFromTextFrame
+ input parameter - paragraph frame, for which the relation CONTENT_FLOWS_FROM
+ has to be invalidated.
+ If NULL, no CONTENT_FLOWS_FROM relation has to be invalidated
+
+ @param _pToTextFrame
+ input parameter - paragraph frame, for which the relation CONTENT_FLOWS_TO
+ has to be invalidated.
+ If NULL, no CONTENT_FLOWS_TO relation has to be invalidated
+ */
+ void InvalidateAccessibleParaFlowRelation_( const SwTextFrame* _pFromTextFrame,
+ const SwTextFrame* _pToTextFrame );
+
+ /** invalidate text selection for paragraphs
+
+ #i27301#
+ implementation for wrapper method
+ <SwViewShell::InvalidateAccessibleParaTextSelection(..)>
+ */
+ void InvalidateAccessibleParaTextSelection_();
+
+ /** invalidate attributes for paragraphs and paragraph's characters
+
+ #i88069#
+ implementation for wrapper method
+ <SwViewShell::InvalidateAccessibleParaAttrs(..)>
+ */
+ void InvalidateAccessibleParaAttrs_( const SwTextFrame& rTextFrame );
+
+public:
+ SwViewShellImp( SwViewShell * );
+ ~SwViewShellImp();
+ void Init( const SwViewOption * ); /// Only for SwViewShell::Init()
+
+ const SwViewShell *GetShell() const { return m_pShell; }
+ SwViewShell *GetShell() { return m_pShell; }
+
+ Color GetRetoucheColor() const;
+
+ /// Management of the first visible Page
+ const SwPageFrame *GetFirstVisPage(OutputDevice const * pRenderContext) const;
+ SwPageFrame *GetFirstVisPage(OutputDevice const * pRenderContext);
+ void SetFirstVisPageInvalid() { m_bFirstPageInvalid = true; }
+
+ bool AddPaintRect( const SwRect &rRect );
+ bool HasPaintRegion() { return static_cast<bool>(m_pPaintRegion); }
+ std::unique_ptr<SwRegionRects> TakePaintRegion() { return std::move(m_pPaintRegion); }
+ const SwRegionRects* GetPaintRegion() { return m_pPaintRegion.get(); }
+ void DeletePaintRegion() { m_pPaintRegion.reset(); }
+
+ void AddPendingLOKInvalidation( const SwRect& rRect );
+ std::vector<SwRect> TakePendingLOKInvalidations();
+
+ /// New Interface for StarView Drawing
+ bool HasDrawView() const { return nullptr != m_pDrawView; }
+ SwDrawView* GetDrawView() { return m_pDrawView.get(); }
+ const SwDrawView* GetDrawView() const { return m_pDrawView.get(); }
+ SdrPageView*GetPageView() { return m_pSdrPageView; }
+ const SdrPageView*GetPageView() const { return m_pSdrPageView; }
+ void MakeDrawView();
+
+ /**
+ * @param _pPageBackgrdColor for setting this color as the background color
+ * at the outliner of the draw view for painting layers "hell" and "heaven"
+ *
+ * @param _bIsPageRightToLeft for the horizontal text direction of the page
+ * in order to set the default horizontal text direction at the outliner of
+ * the draw view for painting layers "hell" and "heaven"
+ */
+ void PaintLayer( const SdrLayerID _nLayerID,
+ SwPrintData const*const pPrintData,
+ SwPageFrame const& rPageFrame,
+ const SwRect& _rRect,
+ const Color* _pPageBackgrdColor,
+ const bool _bIsPageRightToLeft,
+ sdr::contact::ViewObjectContactRedirector* pRedirector );
+
+ /**
+ * Is passed to the DrawEngine as a Link and decides what is painted
+ * or not and in what way
+ */
+
+ // Interface Drawing
+ bool IsDragPossible( const Point &rPoint );
+ void NotifySizeChg( const Size &rNewSz );
+
+ /// SS for the Lay-/IdleAction and relatives
+ bool IsAction() const { return m_pLayAction != nullptr; }
+ bool IsIdleAction() const { return m_pIdleAct != nullptr; }
+ SwLayAction &GetLayAction() { return *m_pLayAction; }
+ const SwLayAction &GetLayAction() const { return *m_pLayAction; }
+
+ /**
+ * If an Action is running we ask it to check whether it's time
+ * to enable the WaitCursor
+ */
+ void CheckWaitCursor();
+
+ /// Asks the LayAction if present
+ bool IsCalcLayoutProgress() const;
+
+ /**
+ * @returns true if a LayAction is running
+ *
+ * There we also set the Flag for ExpressionFields
+ */
+ bool IsUpdateExpFields();
+
+ void SetRestoreActions(sal_uInt16 nSet){m_nRestoreActions = nSet;}
+ sal_uInt16 GetRestoreActions() const{return m_nRestoreActions;}
+
+ void InitPagePreviewLayout();
+
+ SwPagePreviewLayout* PagePreviewLayout()
+ {
+ return m_pPagePreviewLayout.get();
+ }
+
+ /// Is this view accessible?
+ bool IsAccessible() const { return m_pAccessibleMap != nullptr; }
+
+ inline SwAccessibleMap& GetAccessibleMap();
+
+ /// Update (this) accessible view
+ void UpdateAccessible();
+
+ /// Remove a frame from the accessible view
+ void DisposeAccessible( const SwFrame *pFrame, const SdrObject *pObj,
+ bool bRecursive, bool bCanSkipInvisible );
+ inline void DisposeAccessibleFrame( const SwFrame *pFrame,
+ bool bRecursive = false );
+ inline void DisposeAccessibleObj( const SdrObject *pObj, bool bCanSkipInvisible );
+
+ /// Move a frame's position in the accessible view
+ void MoveAccessible( const SwFrame *pFrame, const SdrObject *pObj,
+ const SwRect& rOldFrame );
+ inline void MoveAccessibleFrame( const SwFrame *pFrame, const SwRect& rOldFrame );
+
+ /// Add a frame in the accessible view
+ inline void AddAccessibleFrame( const SwFrame *pFrame );
+
+ inline void AddAccessibleObj( const SdrObject *pObj );
+
+ /// Invalidate accessible frame's content
+ void InvalidateAccessibleFrameContent( const SwFrame *pFrame );
+
+ /// Invalidate accessible frame's cursor position
+ void InvalidateAccessibleCursorPosition( const SwFrame *pFrame );
+
+ /// Invalidate editable state for all accessible frames
+ void InvalidateAccessibleEditableState( bool bAllShells,
+ const SwFrame *pFrame=nullptr );
+
+ /// Invalidate frame's relation set (for chained frames)
+ void InvalidateAccessibleRelationSet( const SwFlyFrame *pMaster,
+ const SwFlyFrame *pFollow );
+
+ /// update data for accessible preview
+ /// change method signature due to new page preview functionality
+ void UpdateAccessiblePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize );
+
+ void InvalidateAccessiblePreviewSelection( sal_uInt16 nSelPage );
+
+ /// Fire all accessible events that have been collected so far
+ void FireAccessibleEvents();
+};
+
+inline SwAccessibleMap& SwViewShellImp::GetAccessibleMap()
+{
+ if( !m_pAccessibleMap )
+ CreateAccessibleMap();
+
+ return *m_pAccessibleMap;
+}
+
+inline void SwViewShellImp::DisposeAccessibleFrame( const SwFrame *pFrame,
+ bool bRecursive )
+{
+ DisposeAccessible( pFrame, nullptr, bRecursive, true );
+}
+
+inline void SwViewShellImp::DisposeAccessibleObj( const SdrObject *pObj, bool bCanSkipInvisible )
+{
+ DisposeAccessible( nullptr, pObj, false, bCanSkipInvisible );
+}
+
+inline void SwViewShellImp::MoveAccessibleFrame( const SwFrame *pFrame,
+ const SwRect& rOldFrame )
+{
+ MoveAccessible( pFrame, nullptr, rOldFrame );
+}
+
+inline void SwViewShellImp::AddAccessibleFrame( const SwFrame *pFrame )
+{
+ SwRect aEmptyRect;
+ MoveAccessible( pFrame, nullptr, aEmptyRect );
+}
+
+inline void SwViewShellImp::AddAccessibleObj( const SdrObject *pObj )
+{
+ SwRect aEmptyRect;
+ MoveAccessible( nullptr, pObj, aEmptyRect );
+}
+#endif // INCLUDED_SW_SOURCE_CORE_INC_VIEWIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/visiturl.hxx b/sw/source/core/inc/visiturl.hxx
new file mode 100644
index 000000000..f273f33bb
--- /dev/null
+++ b/sw/source/core/inc/visiturl.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_VISITURL_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_VISITURL_HXX
+
+#include <svl/lstner.hxx>
+
+class SwDoc;
+
+class SwURLStateChanged final : public SfxListener
+{
+ SwDoc& m_rDoc;
+
+public:
+ SwURLStateChanged(SwDoc& rD);
+ virtual ~SwURLStateChanged() override;
+
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/inc/wrong.hxx b/sw/source/core/inc/wrong.hxx
new file mode 100644
index 000000000..b05fe074c
--- /dev/null
+++ b/sw/source/core/inc/wrong.hxx
@@ -0,0 +1,413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_INC_WRONG_HXX
+#define INCLUDED_SW_SOURCE_CORE_INC_WRONG_HXX
+
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/container/XStringKeyMap.hpp>
+
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <vector>
+#include <memory>
+#include <optional>
+
+#include <tools/color.hxx>
+#include <swtypes.hxx>
+#include <viewopt.hxx>
+#include "TextFrameIndex.hxx"
+
+#if defined _MSC_VER
+// For MSVC (without /vmg) SwTextNode must consistently be defined for
+// WrongListIterator::m_pGetWrongList of pointer-to-SwTextNode-member type to consistently have the
+// same size in all translation units that include this file:
+#include <ndtxt.hxx>
+#endif
+
+class SwWrongList;
+
+enum WrongAreaLineType
+{
+ WRONGAREA_NONE,
+ WRONGAREA_WAVE,
+ WRONGAREA_BOLDWAVE,
+ WRONGAREA_BOLD,
+ WRONGAREA_DASHED
+};
+
+enum WrongListType
+{
+ WRONGLIST_SPELL,
+ WRONGLIST_GRAMMAR,
+ WRONGLIST_SMARTTAG,
+ WRONGLIST_CHANGETRACKING
+};
+
+// ST2
+class SwWrongArea
+{
+public:
+ OUString maType;
+ css::uno::Reference< css::container::XStringKeyMap > mxPropertyBag;
+ sal_Int32 mnPos;
+ sal_Int32 mnLen;
+ SwWrongList* mpSubList;
+
+ Color mColor;
+ WrongAreaLineType mLineType;
+
+ SwWrongArea( const OUString& rType,
+ WrongListType listType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nPos,
+ sal_Int32 nLen);
+
+ SwWrongArea( const OUString& rType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nPos,
+ sal_Int32 nLen,
+ SwWrongList* pSubList);
+private:
+
+ static Color getGrammarColor ( css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag)
+ {
+ try
+ {
+ if (xPropertyBag.is())
+ {
+ css::uno::Any aLineColor = xPropertyBag->getValue("LineColor");
+ ::Color lineColor;
+
+ if (aLineColor >>= lineColor)
+ {
+ return lineColor;
+ }
+ }
+ }
+ catch(const css::container::NoSuchElementException&)
+ {
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ }
+
+ return COL_LIGHTBLUE;
+ }
+
+ static WrongAreaLineType getGrammarLineType( css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag )
+ {
+ try
+ {
+ if (xPropertyBag.is())
+ {
+ css::uno::Any aLineType = xPropertyBag->getValue("LineType");
+ ::sal_Int16 lineType = 0;
+
+ if (!(aLineType >>= lineType))
+ {
+ return WRONGAREA_WAVE;
+ }
+ if (css::awt::FontUnderline::BOLDWAVE == lineType)
+ {
+ return WRONGAREA_BOLDWAVE;
+ }
+ if (css::awt::FontUnderline::BOLD == lineType)
+ {
+ return WRONGAREA_BOLD;
+ }
+ if (css::awt::FontUnderline::DASH == lineType)
+ {
+ return WRONGAREA_DASHED;
+ }
+ if (css::awt::FontUnderline::SMALLWAVE == lineType)
+ {
+ return WRONGAREA_WAVE; //Code draws wave height based on space that fits.
+ }
+ }
+ }
+ catch(const css::container::NoSuchElementException&)
+ {
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ }
+
+ return WRONGAREA_WAVE;
+ }
+
+ static Color getSmartColor ( css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag)
+ {
+ try
+ {
+ if (xPropertyBag.is())
+ {
+ css::uno::Any aLineColor = xPropertyBag->getValue("LineColor");
+ ::Color lineColor;
+
+ if (aLineColor >>= lineColor)
+ {
+ return lineColor;
+ }
+ }
+ }
+ catch(const css::container::NoSuchElementException&)
+ {
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ }
+
+ return SwViewOption::GetSmarttagColor( );
+ }
+
+ static WrongAreaLineType getSmartLineType( css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag )
+ {
+ try
+ {
+ if (xPropertyBag.is())
+ {
+ css::uno::Any aLineType = xPropertyBag->getValue("LineType");
+ ::sal_Int16 lineType = 0;
+
+ if (!(aLineType >>= lineType))
+ {
+ return WRONGAREA_DASHED;
+ }
+ if (css::awt::FontUnderline::WAVE == lineType)
+ {
+ return WRONGAREA_WAVE;
+ }
+ if (css::awt::FontUnderline::BOLDWAVE == lineType)
+ {
+ return WRONGAREA_BOLDWAVE;
+ }
+ if (css::awt::FontUnderline::BOLD == lineType)
+ {
+ return WRONGAREA_BOLD;
+ }
+ if (css::awt::FontUnderline::SMALLWAVE == lineType)
+ {
+ return WRONGAREA_WAVE; //Code draws wave height based on space that fits.
+ }
+ }
+ }
+ catch(const css::container::NoSuchElementException&)
+ {
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ }
+
+ return WRONGAREA_DASHED;
+ }
+
+ static Color getWrongAreaColor(WrongListType listType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag )
+ {
+ if (WRONGLIST_SPELL == listType)
+ {
+ return SwViewOption::GetSpellColor();
+ }
+ else if (WRONGLIST_GRAMMAR == listType)
+ {
+ return getGrammarColor(xPropertyBag);
+ }
+ else if (WRONGLIST_SMARTTAG == listType)
+ {
+ return getSmartColor(xPropertyBag);
+ }
+
+ return SwViewOption::GetSpellColor();
+ }
+
+ static WrongAreaLineType getWrongAreaLineType(WrongListType listType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag )
+ {
+ if (WRONGLIST_SPELL == listType)
+ {
+ return WRONGAREA_WAVE;
+ }
+ else if (WRONGLIST_GRAMMAR == listType)
+ {
+ return getGrammarLineType(xPropertyBag);
+ }
+ else if (WRONGLIST_SMARTTAG == listType)
+ {
+ return getSmartLineType(xPropertyBag);
+ }
+
+ return WRONGAREA_WAVE;
+ }
+
+};
+
+class SwWrongList
+{
+ std::vector<SwWrongArea> maList;
+ WrongListType meType;
+
+ sal_Int32 mnBeginInvalid; // Start of the invalid range
+ sal_Int32 mnEndInvalid; // End of the invalid range
+
+ static void ShiftLeft( sal_Int32 &rPos, sal_Int32 nStart, sal_Int32 nEnd )
+ { if( rPos > nStart ) rPos = rPos > nEnd ? rPos - nEnd + nStart : nStart; }
+ void Invalidate_( sal_Int32 nBegin, sal_Int32 nEnd );
+
+ void Insert(sal_uInt16 nWhere, std::vector<SwWrongArea>::iterator startPos, std::vector<SwWrongArea>::iterator const & endPos);
+ void Remove( sal_uInt16 nIdx, sal_uInt16 nLen );
+
+ SwWrongList& operator= (const SwWrongList &) = delete;
+ SwWrongList( const SwWrongList& rCpy ) = delete;
+
+public:
+ SwWrongList( WrongListType eType );
+
+ virtual ~SwWrongList();
+ virtual SwWrongList* Clone();
+ virtual void CopyFrom( const SwWrongList& rCopy );
+
+ WrongListType GetWrongListType() const { return meType; }
+ sal_Int32 GetBeginInv() const { return mnBeginInvalid; }
+ sal_Int32 GetEndInv() const { return mnEndInvalid; }
+ void SetInvalid( sal_Int32 nBegin, sal_Int32 nEnd );
+ void Validate(){ mnBeginInvalid = mnEndInvalid = COMPLETE_STRING; }
+ void Invalidate( sal_Int32 nBegin, sal_Int32 nEnd );
+ bool InvalidateWrong();
+ enum class FreshState { FRESH, CURSOR, NOTHING };
+ FreshState Fresh( sal_Int32 &rStart, sal_Int32 &rEnd, sal_Int32 nPos,
+ sal_Int32 nLen, sal_uInt16 nIndex, sal_Int32 nCursorPos );
+ sal_uInt16 GetWrongPos( sal_Int32 nValue ) const;
+
+ bool Check( sal_Int32 &rChk, sal_Int32 &rLn ) const;
+ bool InWrongWord( sal_Int32 &rChk, sal_Int32 &rLn ) const;
+ sal_Int32 NextWrong( sal_Int32 nChk ) const;
+
+ void Move( sal_Int32 nPos, sal_Int32 nDiff );
+ void ClearList();
+
+ // Divide the list into two part, the wrong words until nSplitPos will be
+ // removed and transferred to a new SwWrongList.
+ std::unique_ptr<SwWrongList> SplitList( sal_Int32 nSplitPos );
+ // Join the next SwWrongList, nInsertPos is my own text length, where
+ // the other wrong list has to be inserted.
+ void JoinList( SwWrongList* pNext, sal_Int32 nInsertPos );
+
+ sal_Int32 Len( sal_uInt16 nIdx ) const
+ {
+ return nIdx < maList.size() ? maList[nIdx].mnLen : 0;
+ }
+
+ sal_Int32 Pos( sal_uInt16 nIdx ) const
+ {
+ return nIdx < maList.size() ? maList[nIdx].mnPos : 0;
+ }
+
+ sal_uInt16 Count() const { return o3tl::narrowing<sal_uInt16>(maList.size()); }
+
+ void Insert( const OUString& rType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nNewPos, sal_Int32 nNewLen, sal_uInt16 nWhere )
+ {
+ std::vector<SwWrongArea>::iterator i = maList.begin();
+ if ( nWhere >= maList.size() )
+ i = maList.end(); // robust
+ else
+ i += nWhere;
+
+ maList.insert(i, SwWrongArea( rType, meType, xPropertyBag, nNewPos, nNewLen) );
+ }
+
+ void Insert( const OUString& rType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nNewPos, sal_Int32 nNewLen );
+
+ SwWrongList* SubList( sal_uInt16 nIdx ) const
+ {
+ return nIdx < maList.size() ? maList[nIdx].mpSubList : nullptr;
+ }
+
+ void InsertSubList( sal_Int32 nNewPos, sal_Int32 nNewLen, sal_uInt16 nWhere, SwWrongList* pSubList );
+
+ const SwWrongArea* GetElement( sal_uInt16 nIdx ) const
+ {
+ return nIdx < maList.size() ? &maList[nIdx] : nullptr;
+ }
+ void RemoveEntry( sal_Int32 nBegin, sal_Int32 nEnd );
+ bool LookForEntry( sal_Int32 nBegin, sal_Int32 nEnd );
+};
+
+class SwTextNode;
+class SwTextFrame;
+
+namespace sw {
+
+struct MergedPara;
+
+class WrongListIteratorBase
+{
+protected:
+ SwWrongList const* (SwTextNode::*const m_pGetWrongList)() const;
+ sw::MergedPara const*const m_pMergedPara;
+ size_t m_CurrentExtent;
+ TextFrameIndex m_CurrentIndex;
+ SwWrongList const*const m_pWrongList;
+
+public:
+ /// for the text frame
+ WrongListIteratorBase(SwTextFrame const& rFrame,
+ SwWrongList const* (SwTextNode::*pGetWrongList)() const);
+ /// for SwTextSlot
+ WrongListIteratorBase(SwWrongList const& rWrongList);
+};
+
+class WrongListIterator
+ : public WrongListIteratorBase
+{
+public:
+ /// for the text frame
+ WrongListIterator(SwTextFrame const& rFrame,
+ SwWrongList const* (SwTextNode::*pGetWrongList)() const);
+ /// for SwTextSlot
+ WrongListIterator(SwWrongList const& rWrongList);
+
+ bool Check(TextFrameIndex &rStart, TextFrameIndex &rLen);
+ const SwWrongArea* GetWrongElement(TextFrameIndex nStart);
+
+ bool LooksUseful() { return m_pMergedPara || m_pWrongList; }
+};
+
+class WrongListIteratorCounter
+ : public WrongListIteratorBase
+{
+public:
+ WrongListIteratorCounter(SwTextFrame const& rFrame,
+ SwWrongList const* (SwTextNode::*pGetWrongList)() const);
+ WrongListIteratorCounter(SwWrongList const& rWrongList);
+
+ sal_uInt16 GetElementCount();
+ std::optional<std::pair<TextFrameIndex, TextFrameIndex>> GetElementAt(sal_uInt16 nIndex);
+};
+
+} // namespace sw
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/anchoreddrawobject.cxx b/sw/source/core/layout/anchoreddrawobject.cxx
new file mode 100644
index 000000000..491e86001
--- /dev/null
+++ b/sw/source/core/layout/anchoreddrawobject.cxx
@@ -0,0 +1,939 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <dcontact.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <tocntntanchoredobjectposition.hxx>
+#include <tolayoutanchoredobjectposition.hxx>
+#include <frmtool.hxx>
+#include <fmtornt.hxx>
+#include <txtfrm.hxx>
+#include <vector>
+#include <svx/svdogrp.hxx>
+#include <tools/fract.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentState.hxx>
+#include <txtfly.hxx>
+#include <viewimp.hxx>
+#include <textboxhelper.hxx>
+#include <unomid.h>
+#include <svx/svdoashp.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+/// helper class for correct notification due to the positioning of
+/// the anchored drawing object
+class SwPosNotify
+{
+ private:
+ SwAnchoredDrawObject* mpAnchoredDrawObj;
+ SwRect maOldObjRect;
+ SwPageFrame* mpOldPageFrame;
+
+ public:
+ explicit SwPosNotify( SwAnchoredDrawObject* _pAnchoredDrawObj );
+ ~SwPosNotify() COVERITY_NOEXCEPT_FALSE;
+ // #i32795#
+ Point const & LastObjPos() const;
+};
+
+}
+
+SwPosNotify::SwPosNotify( SwAnchoredDrawObject* _pAnchoredDrawObj ) :
+ mpAnchoredDrawObj( _pAnchoredDrawObj )
+{
+ maOldObjRect = mpAnchoredDrawObj->GetObjRect();
+ // --> #i35640# - determine correct page frame
+ mpOldPageFrame = mpAnchoredDrawObj->GetPageFrame();
+}
+
+SwPosNotify::~SwPosNotify() COVERITY_NOEXCEPT_FALSE
+{
+ if ( maOldObjRect != mpAnchoredDrawObj->GetObjRect() )
+ {
+ if( maOldObjRect.HasArea() && mpOldPageFrame )
+ {
+ mpAnchoredDrawObj->NotifyBackground( mpOldPageFrame, maOldObjRect,
+ PrepareHint::FlyFrameLeave );
+ }
+ SwRect aNewObjRect( mpAnchoredDrawObj->GetObjRect() );
+ if( aNewObjRect.HasArea() )
+ {
+ // --> #i35640# - determine correct page frame
+ SwPageFrame* pNewPageFrame = mpAnchoredDrawObj->GetPageFrame();
+ if( pNewPageFrame )
+ mpAnchoredDrawObj->NotifyBackground( pNewPageFrame, aNewObjRect,
+ PrepareHint::FlyFrameArrive );
+ }
+
+ ::ClrContourCache( mpAnchoredDrawObj->GetDrawObj() );
+
+ // --> #i35640# - additional notify anchor text frame
+ // Needed for negative positioned drawing objects
+ // --> #i43255# - refine condition to avoid unneeded
+ // invalidations: anchored object had to be on the page of its anchor
+ // text frame.
+ if ( mpAnchoredDrawObj->GetAnchorFrame()->IsTextFrame() &&
+ mpOldPageFrame == mpAnchoredDrawObj->GetAnchorFrame()->FindPageFrame() )
+ {
+ mpAnchoredDrawObj->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
+ }
+
+ // indicate a restart of the layout process
+ mpAnchoredDrawObj->SetRestartLayoutProcess( true );
+ }
+ else
+ {
+ // lock position
+ mpAnchoredDrawObj->LockPosition();
+
+ if ( !mpAnchoredDrawObj->ConsiderForTextWrap() )
+ {
+ // indicate that object has to be considered for text wrap
+ mpAnchoredDrawObj->SetConsiderForTextWrap( true );
+ // invalidate 'background' in order to allow its 'background'
+ // to wrap around it.
+ mpAnchoredDrawObj->NotifyBackground( mpAnchoredDrawObj->GetPageFrame(),
+ mpAnchoredDrawObj->GetObjRectWithSpaces(),
+ PrepareHint::FlyFrameArrive );
+ // invalidate position of anchor frame in order to force
+ // a re-format of the anchor frame, which also causes a
+ // re-format of the invalid previous frames of the anchor frame.
+ mpAnchoredDrawObj->AnchorFrame()->InvalidatePos();
+ }
+ }
+ // tdf#101464 notify SwAccessibleMap about new drawing object position
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (mpOldPageFrame && mpOldPageFrame->getRootFrame()->IsAnyShellAccessible())
+ {
+ mpOldPageFrame->getRootFrame()->GetCurrShell()->Imp()->MoveAccessible(
+ nullptr, mpAnchoredDrawObj->GetDrawObj(), maOldObjRect);
+ }
+#endif
+}
+
+// --> #i32795#
+Point const & SwPosNotify::LastObjPos() const
+{
+ return maOldObjRect.Pos();
+}
+
+namespace {
+
+// #i32795#
+/// helper class for oscillation control on object positioning
+class SwObjPosOscillationControl
+{
+ private:
+ const SwAnchoredDrawObject* mpAnchoredDrawObj;
+
+ std::vector<Point> maObjPositions;
+
+ public:
+ explicit SwObjPosOscillationControl( const SwAnchoredDrawObject& _rAnchoredDrawObj );
+
+ bool OscillationDetected();
+};
+
+}
+
+SwObjPosOscillationControl::SwObjPosOscillationControl(
+ const SwAnchoredDrawObject& _rAnchoredDrawObj )
+ : mpAnchoredDrawObj( &_rAnchoredDrawObj )
+{
+}
+
+bool SwObjPosOscillationControl::OscillationDetected()
+{
+ bool bOscillationDetected = false;
+
+ if ( maObjPositions.size() == 20 )
+ {
+ // position stack is full -> oscillation
+ bOscillationDetected = true;
+ }
+ else
+ {
+ Point aNewObjPos = mpAnchoredDrawObj->GetObjRect().Pos();
+ for ( auto const & pt : maObjPositions )
+ {
+ if ( aNewObjPos == pt )
+ {
+ // position already occurred -> oscillation
+ bOscillationDetected = true;
+ break;
+ }
+ }
+ if ( !bOscillationDetected )
+ {
+ maObjPositions.push_back( aNewObjPos );
+ }
+ }
+
+ return bOscillationDetected;
+}
+
+
+SwAnchoredDrawObject::SwAnchoredDrawObject() :
+ mbValidPos( false ),
+ mbNotYetAttachedToAnchorFrame( true ),
+ // --> #i28749#
+ mbNotYetPositioned( true ),
+ // --> #i62875#
+ mbCaptureAfterLayoutDirChange( false )
+{
+}
+
+SwAnchoredDrawObject::~SwAnchoredDrawObject()
+{
+}
+
+// --> #i62875#
+void SwAnchoredDrawObject::UpdateLayoutDir()
+{
+ SwFrameFormat::tLayoutDir nOldLayoutDir( GetFrameFormat().GetLayoutDir() );
+
+ SwAnchoredObject::UpdateLayoutDir();
+
+ if ( !NotYetPositioned() &&
+ GetFrameFormat().GetLayoutDir() != nOldLayoutDir &&
+ GetFrameFormat().GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE) &&
+ !IsOutsidePage() )
+ {
+ mbCaptureAfterLayoutDirChange = true;
+ }
+}
+
+// --> #i62875#
+bool SwAnchoredDrawObject::IsOutsidePage() const
+{
+ bool bOutsidePage( false );
+
+ if ( !NotYetPositioned() && GetPageFrame() )
+ {
+ SwRect aTmpRect( GetObjRect() );
+ bOutsidePage =
+ ( aTmpRect.Intersection( GetPageFrame()->getFrameArea() ) != GetObjRect() );
+ }
+
+ return bOutsidePage;
+}
+
+void SwAnchoredDrawObject::MakeObjPos()
+{
+ if ( IsPositioningInProgress() )
+ {
+ // nothing to do - positioning already in progress
+ return;
+ }
+
+ if ( mbValidPos )
+ {
+ // nothing to do - position is valid
+ return;
+ }
+
+ // --> #i28749# - anchored drawing object has to be attached
+ // to anchor frame
+ if ( mbNotYetAttachedToAnchorFrame )
+ {
+ OSL_FAIL( "<SwAnchoredDrawObject::MakeObjPos() - drawing object not yet attached to anchor frame -> no positioning" );
+ return;
+ }
+
+ SwDrawContact* pDrawContact =
+ static_cast<SwDrawContact*>(::GetUserCall( GetDrawObj() ));
+
+ // --> #i28749# - if anchored drawing object hasn't been yet
+ // positioned, convert its positioning attributes, if its positioning
+ // attributes are given in horizontal left-to-right layout.
+ // --> #i36010# - Note: horizontal left-to-right layout is made
+ // the default layout direction for <SwDrawFrameFormat> instances. Thus, it has
+ // to be adjusted manually, if no adjustment of the positioning attributes
+ // have to be performed here.
+ // --> #i35635# - additionally move drawing object to the visible layer.
+ if ( mbNotYetPositioned )
+ {
+ // --> #i35635#
+ pDrawContact->MoveObjToVisibleLayer( DrawObj() );
+ // --> perform conversion of positioning
+ // attributes only for 'master' drawing objects
+ // #i44334#, #i44681# - check, if positioning
+ // attributes already have been set.
+ if ( dynamic_cast< const SwDrawVirtObj* >(GetDrawObj()) == nullptr &&
+ !static_cast<SwDrawFrameFormat&>(GetFrameFormat()).IsPosAttrSet() )
+ {
+ SetPositioningAttr();
+ }
+ // -->
+ // - reset internal flag after all needed actions are performed to
+ // avoid callbacks from drawing layer
+ mbNotYetPositioned = false;
+ }
+
+ // indicate that positioning is in progress
+ {
+ SwObjPositioningInProgress aObjPosInProgress( *this );
+
+ // determine relative position of drawing object and set it
+ switch ( pDrawContact->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ {
+ // indicate that position will be valid after positioning is performed
+ mbValidPos = true;
+ // nothing to do, because as-character anchored objects are positioned
+ // during the format of its anchor frame - see <SwFlyCntPortion::SetBase(..)>
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ // --> #i32795# - move intrinsic positioning to
+ // helper method <MakeObjPosAnchoredAtPara()>
+ MakeObjPosAnchoredAtPara();
+ }
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ case RndStdIds::FLY_AT_FLY:
+ {
+ // --> #i32795# - move intrinsic positioning to
+ // helper method <MakeObjPosAnchoredAtLayout()>
+ MakeObjPosAnchoredAtLayout();
+ }
+ break;
+ default:
+ {
+ assert(!"<SwAnchoredDrawObject::MakeObjPos()> - unknown anchor type.");
+ }
+ }
+
+ // keep, current object rectangle
+ // --> #i34748# - use new method <SetLastObjRect(..)>
+ SetLastObjRect( GetObjRect().SVRect() );
+
+ // Assure for 'master' drawing object, that it's registered at the correct page.
+ // Perform check not for as-character anchored drawing objects and only if
+ // the anchor frame is valid.
+ if ( dynamic_cast< const SwDrawVirtObj* >(GetDrawObj()) == nullptr &&
+ !pDrawContact->ObjAnchoredAsChar() &&
+ GetAnchorFrame()->isFrameAreaDefinitionValid() )
+ {
+ pDrawContact->ChkPage();
+ }
+ }
+
+ // --> #i62875#
+ if ( !(mbCaptureAfterLayoutDirChange &&
+ GetPageFrame()) )
+ return;
+
+ SwRect aPageRect( GetPageFrame()->getFrameArea() );
+ SwRect aObjRect( GetObjRect() );
+ if ( aObjRect.Right() >= aPageRect.Right() + 10 )
+ {
+ Size aSize( aPageRect.Right() - aObjRect.Right(), 0 );
+ DrawObj()->Move( aSize );
+ aObjRect = GetObjRect();
+ }
+
+ if ( aObjRect.Left() + 10 <= aPageRect.Left() )
+ {
+ Size aSize( aPageRect.Left() - aObjRect.Left(), 0 );
+ DrawObj()->Move( aSize );
+ }
+
+ mbCaptureAfterLayoutDirChange = false;
+}
+
+/** method for the intrinsic positioning of an at-paragraph|at-character
+ anchored drawing object
+
+ #i32795# - helper method for method <MakeObjPos>
+*/
+void SwAnchoredDrawObject::MakeObjPosAnchoredAtPara()
+{
+ // --> #i32795# - adopt positioning algorithm from Writer
+ // fly frames, which are anchored at paragraph|at character
+
+ // Determine, if anchor frame can/has to be formatted.
+ // If yes, after each object positioning the anchor frame is formatted.
+ // If after the anchor frame format the object position isn't valid, the
+ // object is positioned again.
+ // --> #i43255# - refine condition: anchor frame format not
+ // allowed, if another anchored object, has to be consider its wrap influence
+ // --> #i50356# - format anchor frame containing the anchor
+ // position. E.g., for at-character anchored object this can be the follow
+ // frame of the anchor frame, which contains the anchor character.
+ bool bJoinLocked
+ = static_cast<const SwTextFrame*>(GetAnchorFrameContainingAnchPos())->IsAnyJoinLocked();
+ const bool bFormatAnchor = !bJoinLocked && !ConsiderObjWrapInfluenceOnObjPos()
+ && !ConsiderObjWrapInfluenceOfOtherObjs();
+
+ // Format of anchor is needed for (vertical) fly offsets, otherwise the
+ // lack of fly portions will result in an incorrect 0 offset.
+ bool bAddVerticalFlyOffsets = GetFrameFormat().getIDocumentSettingAccess().get(
+ DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
+ bool bFormatAnchorOnce = !bJoinLocked && bAddVerticalFlyOffsets;
+
+ if (bFormatAnchor || bFormatAnchorOnce)
+ {
+ // --> #i50356#
+ GetAnchorFrameContainingAnchPos()->Calc(GetAnchorFrameContainingAnchPos()->getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ bool bOscillationDetected = false;
+ SwObjPosOscillationControl aObjPosOscCtrl( *this );
+ // --> #i3317# - boolean, to apply temporarily the
+ // 'straightforward positioning process' for the frame due to its
+ // overlapping with a previous column.
+ bool bConsiderWrapInfluenceDueToOverlapPrevCol( false );
+ do {
+ // indicate that position will be valid after positioning is performed
+ mbValidPos = true;
+
+ // --> #i35640# - correct scope for <SwPosNotify> instance
+ {
+ // create instance of <SwPosNotify> for correct notification
+ SwPosNotify aPosNotify( this );
+
+ // determine and set position
+ objectpositioning::SwToContentAnchoredObjectPosition
+ aObjPositioning( *DrawObj() );
+ aObjPositioning.CalcPosition();
+
+ // get further needed results of the positioning algorithm
+ SetVertPosOrientFrame ( aObjPositioning.GetVertPosOrientFrame() );
+ SetDrawObjAnchor();
+
+ // check for object position oscillation, if position has changed.
+ if ( GetObjRect().Pos() != aPosNotify.LastObjPos() )
+ {
+ bOscillationDetected = aObjPosOscCtrl.OscillationDetected();
+ }
+ }
+ // format anchor frame, if requested.
+ // Note: the format of the anchor frame can cause the object position
+ // to be invalid.
+ if ( bFormatAnchor )
+ {
+ // --> #i50356#
+ GetAnchorFrameContainingAnchPos()->Calc(GetAnchorFrameContainingAnchPos()->getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ // --> #i3317#
+ if ( !ConsiderObjWrapInfluenceOnObjPos() &&
+ OverlapsPrevColumn() )
+ {
+ bConsiderWrapInfluenceDueToOverlapPrevCol = true;
+ }
+ } while ( !mbValidPos && !bOscillationDetected &&
+ !bConsiderWrapInfluenceDueToOverlapPrevCol );
+
+ // --> #i3317# - consider a detected oscillation and overlapping
+ // with previous column.
+ // temporarily consider the anchored objects wrapping style influence
+ if ( bOscillationDetected || bConsiderWrapInfluenceDueToOverlapPrevCol )
+ {
+ SetTmpConsiderWrapInfluence( true );
+ SetRestartLayoutProcess( true );
+ }
+}
+
+/** method for the intrinsic positioning of an at-page|at-frame anchored
+ drawing object
+
+ #i32795# - helper method for method <MakeObjPos>
+*/
+void SwAnchoredDrawObject::MakeObjPosAnchoredAtLayout()
+{
+ // indicate that position will be valid after positioning is performed
+ mbValidPos = true;
+
+ // create instance of <SwPosNotify> for correct notification
+ SwPosNotify aPosNotify( this );
+
+ // determine position
+ objectpositioning::SwToLayoutAnchoredObjectPosition
+ aObjPositioning( *DrawObj() );
+ aObjPositioning.CalcPosition();
+
+ // set position
+
+ // --> #i31698#
+ // --> #i34995# - setting anchor position needed for filters,
+ // especially for the xml-filter to the OpenOffice.org file format
+ {
+ const Point aNewAnchorPos =
+ GetAnchorFrame()->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
+ DrawObj()->SetAnchorPos( aNewAnchorPos );
+ // --> #i70122# - missing invalidation
+ InvalidateObjRectWithSpaces();
+ }
+ SetCurrRelPos( aObjPositioning.GetRelPos() );
+ const SwFrame* pAnchorFrame = GetAnchorFrame();
+ SwRectFnSet aRectFnSet(pAnchorFrame);
+ const Point aAnchPos( aRectFnSet.GetPos(pAnchorFrame->getFrameArea()) );
+ SetObjLeft( aAnchPos.X() + GetCurrRelPos().X() );
+ SetObjTop( aAnchPos.Y() + GetCurrRelPos().Y() );
+}
+
+void SwAnchoredDrawObject::SetDrawObjAnchor()
+{
+ // new anchor position
+ // --> #i31698# -
+ Point aNewAnchorPos =
+ GetAnchorFrame()->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
+ Point aCurrAnchorPos = GetDrawObj()->GetAnchorPos();
+ if ( aNewAnchorPos != aCurrAnchorPos )
+ {
+ // determine movement to be applied after setting the new anchor position
+ Size aMove( aCurrAnchorPos.getX() - aNewAnchorPos.getX(),
+ aCurrAnchorPos.getY() - aNewAnchorPos.getY() );
+ // set new anchor position
+ DrawObj()->SetAnchorPos( aNewAnchorPos );
+ // correct object position, caused by setting new anchor position
+ DrawObj()->Move( aMove );
+ // --> #i70122# - missing invalidation
+ InvalidateObjRectWithSpaces();
+ }
+}
+
+/** method to invalidate the given page frame
+
+ #i28701#
+*/
+void SwAnchoredDrawObject::InvalidatePage_( SwPageFrame* _pPageFrame )
+{
+ if ( !_pPageFrame || _pPageFrame->GetFormat()->GetDoc()->IsInDtor() )
+ return;
+
+ if ( !_pPageFrame->GetUpper() )
+ return;
+
+ // --> #i35007# - correct invalidation for as-character
+ // anchored objects.
+ if ( GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR )
+ {
+ _pPageFrame->InvalidateFlyInCnt();
+ }
+ else
+ {
+ _pPageFrame->InvalidateFlyLayout();
+ }
+
+ SwRootFrame* pRootFrame = static_cast<SwRootFrame*>(_pPageFrame->GetUpper());
+ pRootFrame->DisallowTurbo();
+ if ( pRootFrame->GetTurbo() )
+ {
+ const SwContentFrame* pTmpFrame = pRootFrame->GetTurbo();
+ pRootFrame->ResetTurbo();
+ pTmpFrame->InvalidatePage();
+ }
+ pRootFrame->SetIdleFlags();
+}
+
+void SwAnchoredDrawObject::InvalidateObjPos()
+{
+ // --> #i28701# - check, if invalidation is allowed
+ if ( !(mbValidPos &&
+ InvalidationOfPosAllowed()) )
+ return;
+
+ mbValidPos = false;
+ // --> #i68520#
+ InvalidateObjRectWithSpaces();
+
+ // --> #i44339# - check, if anchor frame exists.
+ if ( !GetAnchorFrame() )
+ return;
+
+ // --> #118547# - notify anchor frame of as-character
+ // anchored object, because its positioned by the format of its anchor frame.
+ // --> #i44559# - assure, that text hint is already
+ // existing in the text frame
+ if ( GetAnchorFrame()->DynCastTextFrame() != nullptr &&
+ (GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR) )
+ {
+ SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) );
+ if (pAnchorTextFrame->CalcFlyPos(&GetFrameFormat()) != TextFrameIndex(COMPLETE_STRING))
+ {
+ AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged, &GetFrameFormat() );
+ }
+ }
+
+ SwPageFrame* pPageFrame = AnchorFrame()->FindPageFrame();
+ InvalidatePage_( pPageFrame );
+
+ // --> #i32270# - also invalidate page frame, at which the
+ // drawing object is registered at.
+ SwPageFrame* pPageFrameRegisteredAt = GetPageFrame();
+ if ( pPageFrameRegisteredAt &&
+ pPageFrameRegisteredAt != pPageFrame )
+ {
+ InvalidatePage_( pPageFrameRegisteredAt );
+ }
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor &&
+ pPageFrameOfAnchor != pPageFrame &&
+ pPageFrameOfAnchor != pPageFrameRegisteredAt )
+ {
+ InvalidatePage_( pPageFrameOfAnchor );
+ }
+}
+
+SwFrameFormat& SwAnchoredDrawObject::GetFrameFormat()
+{
+ assert(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+ return *(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+}
+const SwFrameFormat& SwAnchoredDrawObject::GetFrameFormat() const
+{
+ assert(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+ return *(static_cast<SwDrawContact*>(GetUserCall(GetDrawObj()))->GetFormat());
+}
+
+SwRect SwAnchoredDrawObject::GetObjRect() const
+{
+ // use geometry of drawing object
+ //return GetDrawObj()->GetCurrentBoundRect();
+ return SwRect(GetDrawObj()->GetSnapRect());
+}
+
+namespace
+{
+ // Imagine an open book, inside margin is the one that is at the inner side of the pages, at the center of the book,
+ // outside margin is at the two opposite edges of the book.
+ // outside --text-- inside | inside --text-- outside
+ // With mirrored margins, when relating the size of an object from the inside margin for example, on the
+ // first page we calculate the new size of the object using the size of the right margin,
+ // on second page the left margin, third page right margin, etc.
+ tools::Long getInsideOutsideRelativeWidth(bool isOutside, const SwPageFrame* const pPageFrame)
+ {
+ // Alternating between the only two possible cases: inside and outside.
+ // Inside = false, Outside = true.
+ auto nPageNum = pPageFrame->GetPhyPageNum();
+ if (nPageNum % 2 == (isOutside ? 0 : 1))
+ return pPageFrame->GetRightMargin();
+ else
+ return pPageFrame->GetLeftMargin();
+ }
+}
+
+// --> #i70122#
+SwRect SwAnchoredDrawObject::GetObjBoundRect() const
+{
+ bool bGroupShape = dynamic_cast<const SdrObjGroup*>( GetDrawObj() );
+ // Resize objects with relative width or height
+ if ( !bGroupShape && GetPageFrame( ) && ( GetDrawObj( )->GetRelativeWidth( ) || GetDrawObj()->GetRelativeHeight( ) ) )
+ {
+ tools::Rectangle aCurrObjRect = GetDrawObj()->GetCurrentBoundRect();
+
+ tools::Long nTargetWidth = aCurrObjRect.GetWidth( );
+ if ( GetDrawObj( )->GetRelativeWidth( ) )
+ {
+ tools::Long nWidth = 0;
+ if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::FRAME)
+ // Exclude margins.
+ nWidth = GetPageFrame()->getFramePrintArea().SVRect().GetWidth();
+ // Here we handle the relative size of the width of some shape.
+ // The size of the shape's width is going to be relative to the size of the left margin.
+ // E.g.: (left margin = 8 && relative size = 150%) -> width of some shape = 12.
+ else if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::PAGE_LEFT)
+ {
+ if (GetPageFrame()->GetPageDesc()->GetUseOn() == UseOnPage::Mirror)
+ // We want to get the width of whatever is going through here using the size of the
+ // outside margin.
+ nWidth = getInsideOutsideRelativeWidth(true, GetPageFrame());
+ else
+ nWidth = GetPageFrame()->GetLeftMargin();
+ }
+ // Same as the left margin above.
+ else if (GetDrawObj()->GetRelativeWidthRelation() == text::RelOrientation::PAGE_RIGHT)
+ if (GetPageFrame()->GetPageDesc()->GetUseOn() == UseOnPage::Mirror)
+ // We want to get the width of whatever is going through here using the size of the
+ // inside margin.
+ nWidth = getInsideOutsideRelativeWidth(false, GetPageFrame());
+ else
+ nWidth = GetPageFrame()->GetRightMargin();
+ else
+ nWidth = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetWidth();
+ nTargetWidth = nWidth * (*GetDrawObj( )->GetRelativeWidth());
+ }
+
+ bool bCheck = GetDrawObj()->GetRelativeHeight();
+ if (bCheck)
+ {
+ auto pObjCustomShape = dynamic_cast<const SdrObjCustomShape*>(GetDrawObj());
+ bCheck = !pObjCustomShape || !pObjCustomShape->IsAutoGrowHeight();
+ }
+
+ tools::Long nTargetHeight = aCurrObjRect.GetHeight();
+ if (bCheck)
+ {
+ tools::Long nHeight = 0;
+ if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::FRAME)
+ // Exclude margins.
+ nHeight = GetPageFrame()->getFramePrintArea().SVRect().GetHeight();
+ else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA)
+ {
+ // count required height: print area top = top margin + header
+ SwRect aHeaderRect;
+ const SwHeaderFrame* pHeaderFrame = GetPageFrame()->GetHeaderFrame();
+ if (pHeaderFrame)
+ aHeaderRect = pHeaderFrame->GetPaintArea();
+ nHeight = GetPageFrame()->GetTopMargin() + aHeaderRect.Height();
+ }
+ else if (GetDrawObj()->GetRelativeHeightRelation() == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ {
+ // count required height: print area bottom = bottom margin + footer
+ SwRect aFooterRect;
+ auto pFooterFrame = GetPageFrame()->GetFooterFrame();
+ if (pFooterFrame)
+ aFooterRect = pFooterFrame->GetPaintArea();
+ nHeight = GetPageFrame()->GetBottomMargin() + aFooterRect.Height();
+ }
+ else
+ nHeight = GetPageFrame( )->GetBoundRect( GetPageFrame()->getRootFrame()->GetCurrShell()->GetOut() ).SVRect().GetHeight();
+ nTargetHeight = nHeight * (*GetDrawObj()->GetRelativeHeight());
+ }
+
+ if ( nTargetWidth != aCurrObjRect.GetWidth( ) || nTargetHeight != aCurrObjRect.GetHeight( ) )
+ {
+ SwDoc* pDoc = const_cast<SwDoc*>(GetPageFrame()->GetFormat()->GetDoc());
+
+ bool bEnableSetModified = pDoc->getIDocumentState().IsEnableSetModified();
+ pDoc->getIDocumentState().SetEnableSetModified(false);
+ auto pObject = const_cast<SdrObject*>(GetDrawObj());
+ pObject->Resize( aCurrObjRect.TopLeft(),
+ Fraction( nTargetWidth, aCurrObjRect.GetWidth() ),
+ Fraction( nTargetHeight, aCurrObjRect.GetHeight() ), false );
+
+ if (SwFrameFormat* pFrameFormat = FindFrameFormat(pObject))
+ {
+ if (SwTextBoxHelper::isTextBox(pFrameFormat, RES_DRAWFRMFMT))
+ {
+ // Shape has relative size and also a textbox, update its text area as well.
+ uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
+ SwTextBoxHelper::syncProperty(pFrameFormat, RES_FRM_SIZE, MID_FRMSIZE_SIZE,
+ uno::Any(xShape->getSize()));
+ }
+ }
+
+ pDoc->getIDocumentState().SetEnableSetModified(bEnableSetModified);
+ }
+ }
+ return SwRect(GetDrawObj()->GetCurrentBoundRect());
+}
+
+// --> #i68520#
+bool SwAnchoredDrawObject::SetObjTop_( const SwTwips _nTop )
+{
+ SwTwips nDiff = _nTop - GetObjRect().Top();
+ DrawObj()->Move( Size( 0, nDiff ) );
+
+ return nDiff != 0;
+}
+bool SwAnchoredDrawObject::SetObjLeft_( const SwTwips _nLeft )
+{
+ SwTwips nDiff = _nLeft - GetObjRect().Left();
+ DrawObj()->Move( Size( nDiff, 0 ) );
+
+ return nDiff != 0;
+}
+
+/** adjust positioning and alignment attributes for new anchor frame
+
+ #i33313# - add second optional parameter <_pNewObjRect>
+*/
+void SwAnchoredDrawObject::AdjustPositioningAttr( const SwFrame* _pNewAnchorFrame,
+ const SwRect* _pNewObjRect )
+{
+ SwTwips nHoriRelPos = 0;
+ SwTwips nVertRelPos = 0;
+ const Point aAnchorPos = _pNewAnchorFrame->GetFrameAnchorPos( ::HasWrap( GetDrawObj() ) );
+ // --> #i33313#
+ const SwRect aObjRect( _pNewObjRect ? *_pNewObjRect : GetObjRect() );
+ const bool bVert = _pNewAnchorFrame->IsVertical();
+ const bool bR2L = _pNewAnchorFrame->IsRightToLeft();
+ if ( bVert )
+ {
+ nHoriRelPos = aObjRect.Top() - aAnchorPos.Y();
+ nVertRelPos = aAnchorPos.X() - aObjRect.Right();
+ }
+ else if ( bR2L )
+ {
+ nHoriRelPos = aAnchorPos.X() - aObjRect.Right();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.Y();
+ }
+ else
+ {
+ nHoriRelPos = aObjRect.Left() - aAnchorPos.X();
+ nVertRelPos = aObjRect.Top() - aAnchorPos.Y();
+ }
+
+ SwFormatHoriOrient hori(nHoriRelPos, text::HoriOrientation::NONE, text::RelOrientation::FRAME);
+ SwFormatVertOrient vert(nVertRelPos, text::VertOrientation::NONE, text::RelOrientation::FRAME);
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> items(GetFrameFormat().GetDoc()->GetAttrPool());
+ items.Put(hori);
+ items.Put(vert);
+ GetFrameFormat().GetDoc()->SetAttr(items, GetFrameFormat());
+}
+
+// --> #i34748# - change return type.
+// If member <mpLastObjRect> is NULL, create one.
+void SwAnchoredDrawObject::SetLastObjRect( const tools::Rectangle& _rNewLastRect )
+{
+ maLastObjRect = _rNewLastRect;
+}
+
+void SwAnchoredDrawObject::ObjectAttachedToAnchorFrame()
+{
+ // --> #i31698#
+ SwAnchoredObject::ObjectAttachedToAnchorFrame();
+
+ if ( mbNotYetAttachedToAnchorFrame )
+ {
+ mbNotYetAttachedToAnchorFrame = false;
+ }
+}
+
+/** method to set positioning attributes
+
+ #i35798#
+ During load the positioning attributes aren't set.
+ Thus, the positioning attributes are set by the current object geometry.
+ This method is also used for the conversion for drawing objects
+ (not anchored as-character) imported from OpenOffice.org file format
+ once and directly before the first positioning.
+*/
+void SwAnchoredDrawObject::SetPositioningAttr()
+{
+ SwDrawContact* pDrawContact =
+ static_cast<SwDrawContact*>(GetUserCall( GetDrawObj() ));
+
+ if ( !pDrawContact->ObjAnchoredAsChar() )
+ {
+ SwRect aObjRect( GetObjRect() );
+
+ SwTwips nHoriPos = aObjRect.Left();
+ SwTwips nVertPos = aObjRect.Top();
+ // #i44334#, #i44681#
+ // perform conversion only if position is in horizontal-left-to-right-layout.
+ if ( GetFrameFormat().GetPositionLayoutDir() ==
+ text::PositionLayoutDir::PositionInHoriL2R )
+ {
+ SwFrameFormat::tLayoutDir eLayoutDir = GetFrameFormat().GetLayoutDir();
+ switch ( eLayoutDir )
+ {
+ case SwFrameFormat::HORI_L2R:
+ {
+ // nothing to do
+ }
+ break;
+ case SwFrameFormat::HORI_R2L:
+ {
+ nHoriPos = -aObjRect.Left() - aObjRect.Width();
+ }
+ break;
+ case SwFrameFormat::VERT_R2L:
+ {
+ nHoriPos = aObjRect.Top();
+ nVertPos = -aObjRect.Left() - aObjRect.Width();
+ }
+ break;
+ default:
+ {
+ assert(!"<SwAnchoredDrawObject::SetPositioningAttr()> - unsupported layout direction");
+ }
+ }
+ }
+
+ // --> #i71182#
+ // only change position - do not lose other attributes
+
+ SwFormatHoriOrient aHori( GetFrameFormat().GetHoriOrient() );
+ if (nHoriPos != aHori.GetPos()) {
+ aHori.SetPos( nHoriPos );
+ InvalidateObjRectWithSpaces();
+ GetFrameFormat().SetFormatAttr( aHori );
+ }
+
+ SwFormatVertOrient aVert( GetFrameFormat().GetVertOrient() );
+ if (nVertPos != aVert.GetPos()) {
+ aVert.SetPos( nVertPos );
+ InvalidateObjRectWithSpaces();
+ GetFrameFormat().SetFormatAttr( aVert );
+ }
+
+ // --> #i36010# - set layout direction of the position
+ GetFrameFormat().SetPositionLayoutDir(
+ text::PositionLayoutDir::PositionInLayoutDirOfAnchor );
+ }
+ // --> #i65798# - also for as-character anchored objects
+ // --> #i45952# - indicate that position
+ // attributes are set now.
+ static_cast<SwDrawFrameFormat&>(GetFrameFormat()).PosAttrSet();
+}
+
+void SwAnchoredDrawObject::NotifyBackground( SwPageFrame* _pPageFrame,
+ const SwRect& _rRect,
+ PrepareHint _eHint )
+{
+ ::Notify_Background( GetDrawObj(), _pPageFrame, _rRect, _eHint, true );
+}
+
+/** method to assure that anchored object is registered at the correct
+ page frame
+
+ #i28701#
+*/
+void SwAnchoredDrawObject::RegisterAtCorrectPage()
+{
+ SwPageFrame* pPageFrame( nullptr );
+ if ( GetVertPosOrientFrame() )
+ {
+ pPageFrame = const_cast<SwPageFrame*>(GetVertPosOrientFrame()->FindPageFrame());
+ }
+ if ( pPageFrame && GetPageFrame() != pPageFrame )
+ {
+ RegisterAtPage(*pPageFrame);
+ }
+}
+
+void SwAnchoredDrawObject::RegisterAtPage(SwPageFrame & rPageFrame)
+{
+ assert(GetPageFrame() != &rPageFrame);
+ if (GetPageFrame())
+ {
+ GetPageFrame()->RemoveDrawObjFromPage( *this );
+ }
+ rPageFrame.AppendDrawObjToPage( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/anchoredobject.cxx b/sw/source/core/layout/anchoredobject.cxx
new file mode 100644
index 000000000..c38cfcb03
--- /dev/null
+++ b/sw/source/core/layout/anchoredobject.cxx
@@ -0,0 +1,916 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <txtfrm.hxx>
+#include <frmatr.hxx>
+#include <fmtornt.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <fmtsrnd.hxx>
+#include <dcontact.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <sortedobjs.hxx>
+#include <pagefrm.hxx>
+#include <layouter.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+// --> #i28701# -
+// implementation of helper class <SwObjPositioningInProgress>
+
+SwObjPositioningInProgress::SwObjPositioningInProgress( SdrObject& _rSdrObj ) :
+ mpAnchoredObj( nullptr ),
+ // --> #i52904#
+ mbOldObjPositioningInProgress( false )
+{
+ mpAnchoredObj = ::GetUserCall( &_rSdrObj )->GetAnchoredObj( &_rSdrObj );
+ // --> #i52904#
+ mbOldObjPositioningInProgress = mpAnchoredObj->IsPositioningInProgress();
+ mpAnchoredObj->SetPositioningInProgress( true );
+}
+SwObjPositioningInProgress::SwObjPositioningInProgress( SwAnchoredObject& _rAnchoredObj ) :
+ mpAnchoredObj( &_rAnchoredObj ),
+ // --> #i52904#
+ mbOldObjPositioningInProgress( false )
+{
+ // --> #i52904#
+ mbOldObjPositioningInProgress = mpAnchoredObj->IsPositioningInProgress();
+ mpAnchoredObj->SetPositioningInProgress( true );
+}
+
+SwObjPositioningInProgress::~SwObjPositioningInProgress()
+{
+ if ( mpAnchoredObj )
+ {
+ // --> #i52904#
+ mpAnchoredObj->SetPositioningInProgress( mbOldObjPositioningInProgress );
+ }
+}
+
+
+SwAnchoredObject::SwAnchoredObject() :
+ mpDrawObj( nullptr ),
+ mpAnchorFrame( nullptr ),
+ // --> #i28701#
+ mpPageFrame( nullptr ),
+ mnLastTopOfLine( 0 ),
+ mpVertPosOrientFrame( nullptr ),
+ // --> #i28701#
+ mbPositioningInProgress( false ),
+ mbConsiderForTextWrap( false ),
+ mbPositionLocked( false ),
+ // --> #i40147#
+ mbKeepPositionLockedForSection( false ),
+ mbRestartLayoutProcess( false ),
+ // --> #i35911#
+ mbClearedEnvironment( false ),
+ // --> #i3317#
+ mbTmpConsiderWrapInfluence( false ),
+ mbObjRectWithSpacesValid( false )
+{
+}
+
+void SwAnchoredObject::ClearVertPosOrientFrame()
+{
+ if (mpVertPosOrientFrame)
+ {
+ const_cast<SwLayoutFrame*>(mpVertPosOrientFrame)->ClearVertPosOrientFrameFor(this);
+ mpVertPosOrientFrame = nullptr;
+ }
+}
+
+SwAnchoredObject::~SwAnchoredObject()
+{
+ ClearVertPosOrientFrame();
+}
+
+void SwAnchoredObject::SetDrawObj( SdrObject& _rDrawObj )
+{
+ mpDrawObj = &_rDrawObj;
+}
+
+
+void SwAnchoredObject::ChgAnchorFrame( SwFrame* _pNewAnchorFrame )
+{
+ mpAnchorFrame = _pNewAnchorFrame;
+
+ if ( mpAnchorFrame )
+ {
+ ObjectAttachedToAnchorFrame();
+ }
+}
+
+/** determine anchor frame containing the anchor position
+
+ #i26945#
+ the anchor frame, which is determined, is <mpAnchorFrame>
+ for an at-page, at-frame or at-paragraph anchored object
+ and the anchor character frame for an at-character and as-character
+ anchored object.
+*/
+SwFrame* SwAnchoredObject::GetAnchorFrameContainingAnchPos()
+{
+ SwFrame* pAnchorFrameContainingAnchPos = FindAnchorCharFrame();
+ if ( !pAnchorFrameContainingAnchPos )
+ {
+ pAnchorFrameContainingAnchPos = AnchorFrame();
+ }
+
+ return pAnchorFrameContainingAnchPos;
+}
+
+
+void SwAnchoredObject::SetPageFrame( SwPageFrame* _pNewPageFrame )
+{
+ if ( mpPageFrame == _pNewPageFrame )
+ return;
+
+ // clear member, which denotes the layout frame at which the vertical
+ // position is oriented at, if it doesn't fit to the new page frame.
+ if ( GetVertPosOrientFrame() &&
+ ( !_pNewPageFrame ||
+ _pNewPageFrame != GetVertPosOrientFrame()->FindPageFrame() ) )
+ {
+ ClearVertPosOrientFrame();
+ }
+
+ // assign new page frame
+ mpPageFrame = _pNewPageFrame;
+}
+
+
+SwTwips SwAnchoredObject::GetRelCharX( const SwFrame* pFrame ) const
+{
+ return maLastCharRect.Left() - pFrame->getFrameArea().Left();
+}
+
+SwTwips SwAnchoredObject::GetRelCharY( const SwFrame* pFrame ) const
+{
+ return maLastCharRect.Bottom() - pFrame->getFrameArea().Top();
+}
+
+void SwAnchoredObject::AddLastCharY( tools::Long nDiff )
+{
+ maLastCharRect.Pos().AdjustY(nDiff );
+}
+
+void SwAnchoredObject::ResetLastCharRectHeight()
+{
+ maLastCharRect.Height( 0 );
+}
+
+void SwAnchoredObject::SetVertPosOrientFrame( const SwLayoutFrame& _rVertPosOrientFrame )
+{
+ ClearVertPosOrientFrame();
+
+ mpVertPosOrientFrame = &_rVertPosOrientFrame;
+ const_cast<SwLayoutFrame*>(mpVertPosOrientFrame)->SetVertPosOrientFrameFor(this);
+
+ // #i28701# - take over functionality of deleted method
+ // <SwFlyAtContentFrame::AssertPage()>: assure for at-paragraph and at-character
+ // an anchored object, that it is registered at the correct page frame
+ RegisterAtCorrectPage();
+}
+
+
+// #i28701# - follow-up of #i22341#
+void SwAnchoredObject::AddLastTopOfLineY( SwTwips _nDiff )
+{
+ mnLastTopOfLine += _nDiff;
+}
+
+/** check anchor character rectangle and top of line
+
+ #i26791
+ For to-character anchored Writer fly frames the members <maLastCharRect>
+ and <maLastTopOfLine> are updated. These are checked for change and
+ depending on the applied positioning, it's decided, if the Writer fly
+ frame has to be invalidated.
+
+ add parameter <_bCheckForParaPorInf>, default value <true>
+*/
+void SwAnchoredObject::CheckCharRectAndTopOfLine(
+ const bool _bCheckForParaPorInf )
+{
+ if ( !(GetAnchorFrame() &&
+ GetAnchorFrame()->IsTextFrame()) )
+ return;
+
+ const SwFormatAnchor& rAnch = GetFrameFormat().GetAnchor();
+ if ( !((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) &&
+ rAnch.GetContentAnchor()) )
+ return;
+
+ // --> if requested, assure that anchor frame,
+ // which contains the anchor character, has a paragraph portion information.
+ // The paragraph portion information is needed to determine the
+ // anchor character rectangle respectively the top of the line.
+ // Thus, a format of this frame is avoided to determine the
+ // paragraph portion information.
+ // --> #i26945# - use new method <FindAnchorCharFrame()>
+ const SwTextFrame& aAnchorCharFrame = *(FindAnchorCharFrame());
+ if ( !_bCheckForParaPorInf || aAnchorCharFrame.HasPara() )
+ {
+ CheckCharRect( rAnch, aAnchorCharFrame );
+ CheckTopOfLine( rAnch, aAnchorCharFrame );
+ }
+}
+
+/** check anchor character rectangle
+
+ #i22341#
+ helper method for method <CheckCharRectAndTopOfLine()>
+ For to-character anchored Writer fly frames the member <maLastCharRect>
+ is updated. This is checked for change and depending on the applied
+ positioning, it's decided, if the Writer fly frame has to be invalidated.
+
+ improvement - add second parameter <_rAnchorCharFrame>
+*/
+void SwAnchoredObject::CheckCharRect( const SwFormatAnchor& _rAnch,
+ const SwTextFrame& _rAnchorCharFrame )
+{
+ // determine rectangle of anchor character. If not exist, abort operation
+ SwRect aCharRect;
+ if ( !_rAnchorCharFrame.GetAutoPos( aCharRect, *_rAnch.GetContentAnchor() ) )
+ {
+ return;
+ }
+ // check, if anchor character rectangle has changed
+ if ( aCharRect == maLastCharRect )
+ return;
+
+ // check positioning and alignment for invalidation of position
+ {
+ SwRectFnSet aRectFnSet(&_rAnchorCharFrame);
+ // determine positioning and alignment
+ SwFormatVertOrient aVert( GetFrameFormat().GetVertOrient() );
+ SwFormatHoriOrient aHori( GetFrameFormat().GetHoriOrient() );
+ // check for anchor character rectangle changes for certain
+ // positionings and alignments
+ // add condition to invalidate position,
+ // if vertical aligned at frame/page area and vertical position
+ // of anchor character has changed.
+ const sal_Int16 eVertRelOrient = aVert.GetRelationOrient();
+ if ( ( aHori.GetRelationOrient() == text::RelOrientation::CHAR &&
+ aRectFnSet.GetLeft(aCharRect) != aRectFnSet.GetLeft(maLastCharRect) ) ||
+ ( eVertRelOrient == text::RelOrientation::CHAR &&
+ ( aRectFnSet.GetTop(aCharRect) != aRectFnSet.GetTop(maLastCharRect) ||
+ aRectFnSet.GetHeight(aCharRect) != aRectFnSet.GetHeight(maLastCharRect) ) ) ||
+ ( ( ( eVertRelOrient == text::RelOrientation::FRAME ) ||
+ ( eVertRelOrient == text::RelOrientation::PRINT_AREA ) ||
+ ( eVertRelOrient == text::RelOrientation::PAGE_FRAME ) ||
+ ( eVertRelOrient == text::RelOrientation::PAGE_PRINT_AREA ) ) &&
+ ( aRectFnSet.GetTop(aCharRect) != aRectFnSet.GetTop(maLastCharRect) ) ) )
+ {
+ // #i26945#, #i35911# - unlock position of
+ // anchored object, if it isn't registered at the page,
+ // where its anchor character frame is on.
+ if ( GetPageFrame() != _rAnchorCharFrame.FindPageFrame() )
+ {
+ UnlockPosition();
+ }
+ InvalidateObjPos();
+ }
+ }
+ // keep new anchor character rectangle
+ maLastCharRect = aCharRect;
+}
+
+/** check top of line
+
+ #i22341#
+ helper method for method <CheckCharRectAndTopOfLine()>
+ For to-character anchored Writer fly frames the member <mnLastTopOfLine>
+ is updated. This is checked for change and depending on the applied
+ positioning, it's decided, if the Writer fly frame has to be invalidated.
+
+ improvement - add second parameter <_rAnchorCharFrame>
+*/
+void SwAnchoredObject::CheckTopOfLine( const SwFormatAnchor& _rAnch,
+ const SwTextFrame& _rAnchorCharFrame )
+{
+ SwTwips nTopOfLine = 0;
+ if ( !_rAnchorCharFrame.GetTopOfLine( nTopOfLine, *_rAnch.GetContentAnchor() ) )
+ return;
+
+ if ( nTopOfLine == mnLastTopOfLine )
+ return;
+
+ // check alignment for invalidation of position
+ if ( GetFrameFormat().GetVertOrient().GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ // #i26945#, #i35911# - unlock position of
+ // anchored object, if it isn't registered at the page,
+ // where its anchor character frame is on.
+ if ( GetPageFrame() != _rAnchorCharFrame.FindPageFrame() )
+ {
+ UnlockPosition();
+ }
+ InvalidateObjPos();
+ }
+ // keep new top of line value
+ mnLastTopOfLine = nTopOfLine;
+}
+
+void SwAnchoredObject::ClearCharRectAndTopOfLine()
+{
+ maLastCharRect.Clear();
+ mnLastTopOfLine = 0;
+}
+
+void SwAnchoredObject::SetCurrRelPos( Point _aRelPos )
+{
+ maRelPos = _aRelPos;
+}
+
+void SwAnchoredObject::ObjectAttachedToAnchorFrame()
+{
+ // default behaviour:
+ // update layout direction, the anchored object is assigned to
+ UpdateLayoutDir();
+}
+
+/** method update layout direction the layout direction, the anchored
+ object is in
+
+ #i31698#
+ method has typically to be called, if the anchored object gets its
+ anchor frame assigned.
+*/
+void SwAnchoredObject::UpdateLayoutDir()
+{
+ SwFrameFormat::tLayoutDir nLayoutDir = SwFrameFormat::HORI_L2R;
+ const SwFrame* pAnchorFrame = GetAnchorFrame();
+ if ( pAnchorFrame )
+ {
+ const bool bVert = pAnchorFrame->IsVertical();
+ const bool bR2L = pAnchorFrame->IsRightToLeft();
+ if ( bVert )
+ {
+ nLayoutDir = SwFrameFormat::VERT_R2L;
+ }
+ else if ( bR2L )
+ {
+ nLayoutDir = SwFrameFormat::HORI_R2L;
+ }
+ }
+ GetFrameFormat().SetLayoutDir( nLayoutDir );
+}
+
+/** method to perform necessary invalidations for the positioning of
+ objects, for whose the wrapping style influence has to be considered
+ on the object positioning.
+
+ #i28701#
+*/
+void SwAnchoredObject::InvalidateObjPosForConsiderWrapInfluence()
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ {
+ // indicate that object has not to be considered for text wrap
+ SetConsiderForTextWrap( false );
+ // unlock position
+ UnlockPosition();
+ // invalidate position
+ InvalidateObjPos();
+ // invalidate 'background'
+ NotifyBackground( GetPageFrame(), GetObjRectWithSpaces(), PrepareHint::FlyFrameLeave );
+ }
+}
+
+/** method to determine, if wrapping style influence of the anchored
+ object has to be considered on the object positioning
+
+ #i28701#
+ Note: result of this method also decides, if the booleans for the
+ layout process are of relevance.
+*/
+bool SwAnchoredObject::ConsiderObjWrapInfluenceOnObjPos() const
+{
+ bool bRet( false );
+
+ const SwFrameFormat& rObjFormat = GetFrameFormat();
+
+ // --> #i3317# - add condition <IsTmpConsiderWrapInfluence()>
+ // --> #i55204#
+ // - correction: wrapping style influence has been considered, if condition
+ // <IsTmpConsiderWrapInfluence()> is hold, regardless of its anchor type
+ // or its wrapping style.
+ if ( IsTmpConsiderWrapInfluence() )
+ {
+ bRet = true;
+ }
+ else if ( rObjFormat.getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
+ if ( ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)) &&
+ rObjFormat.GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
+ {
+ // --> #i34520# - text also wraps around anchored
+ // objects in the layer Hell - see the text formatting.
+ // Thus, it hasn't to be checked here.
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+/** method to determine, if other anchored objects, also attached at
+ to the anchor frame, have to consider its wrap influence.
+
+ // --> #i43255#
+*/
+bool SwAnchoredObject::ConsiderObjWrapInfluenceOfOtherObjs() const
+{
+ bool bRet( false );
+
+ const SwSortedObjs* pObjs = GetAnchorFrame()->GetDrawObjs();
+ if ( pObjs->size() > 1 )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj != this &&
+ pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool SwAnchoredObject::ConsiderForTextWrap() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbConsiderForTextWrap;
+ else
+ return true;
+}
+
+void SwAnchoredObject::SetConsiderForTextWrap( const bool _bConsiderForTextWrap )
+{
+ mbConsiderForTextWrap = _bConsiderForTextWrap;
+}
+
+bool SwAnchoredObject::PositionLocked() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbPositionLocked;
+ else
+ return false;
+}
+
+bool SwAnchoredObject::RestartLayoutProcess() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbRestartLayoutProcess;
+ else
+ return false;
+}
+
+void SwAnchoredObject::SetRestartLayoutProcess( const bool _bRestartLayoutProcess )
+{
+ mbRestartLayoutProcess = _bRestartLayoutProcess;
+}
+
+// --> #i35911#
+bool SwAnchoredObject::ClearedEnvironment() const
+{
+ if ( ConsiderObjWrapInfluenceOnObjPos() )
+ return mbClearedEnvironment;
+ else
+ return false;
+}
+void SwAnchoredObject::SetClearedEnvironment( const bool _bClearedEnvironment )
+{
+ mbClearedEnvironment = _bClearedEnvironment;
+}
+
+/** method to determine, if due to anchored object size and wrapping
+ style, its layout environment is cleared.
+
+ #i35911#
+*/
+bool SwAnchoredObject::HasClearedEnvironment() const
+{
+ bool bHasClearedEnvironment( false );
+
+ // --> #i43913# - layout frame, vertical position is orient at, has to be set.
+ OSL_ENSURE( GetVertPosOrientFrame(),
+ "<SwAnchoredObject::HasClearedEnvironment()> - layout frame missing, at which the vertical position is oriented at." );
+ if ( GetVertPosOrientFrame() &&
+ GetAnchorFrame()->IsTextFrame() &&
+ !static_cast<const SwTextFrame*>(GetAnchorFrame())->IsFollow() &&
+ static_cast<const SwTextFrame*>(GetAnchorFrame())->FindPageFrame()->GetPhyPageNum() >=
+ GetPageFrame()->GetPhyPageNum() )
+ {
+ const SwFrame* pTmpFrame = GetVertPosOrientFrame()->Lower();
+ while ( pTmpFrame && pTmpFrame->IsLayoutFrame() && !pTmpFrame->IsTabFrame() )
+ {
+ pTmpFrame = static_cast<const SwLayoutFrame*>(pTmpFrame)->Lower();
+ }
+ if ( !pTmpFrame )
+ {
+ bHasClearedEnvironment = true;
+ }
+ else if ( pTmpFrame->IsTextFrame() && !pTmpFrame->GetNext() )
+ {
+ const SwTextFrame* pTmpTextFrame = static_cast<const SwTextFrame*>(pTmpFrame);
+ if ( pTmpTextFrame->IsUndersized() ||
+ ( pTmpTextFrame->GetFollow() &&
+ pTmpTextFrame->GetFollow()->GetOffset() == TextFrameIndex(0)))
+ {
+ bHasClearedEnvironment = true;
+ }
+ }
+ }
+
+ return bHasClearedEnvironment;
+}
+
+/** method to add spacing to object area
+
+ #i28701#
+ #i68520# - return constant reference and use cache
+*/
+const SwRect& SwAnchoredObject::GetObjRectWithSpaces() const
+{
+ if ( mbObjRectWithSpacesValid &&
+ maLastObjRect != GetObjRect() )
+ {
+ OSL_FAIL( "<SwAnchoredObject::GetObjRectWithSpaces> - cache for object rectangle inclusive spaces marked as valid, but it couldn't be. Missing invalidation of cache." );
+ InvalidateObjRectWithSpaces();
+ }
+ if ( !mbObjRectWithSpacesValid )
+ {
+ maObjRectWithSpaces = GetObjBoundRect();
+ const SwFrameFormat& rFormat = GetFrameFormat();
+ const SvxULSpaceItem& rUL = rFormat.GetULSpace();
+ const SvxLRSpaceItem& rLR = rFormat.GetLRSpace();
+ {
+ maObjRectWithSpaces.Top ( std::max( maObjRectWithSpaces.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ));
+ maObjRectWithSpaces.Left( std::max( maObjRectWithSpaces.Left()- rLR.GetLeft(), tools::Long(0) ));
+ maObjRectWithSpaces.AddHeight(rUL.GetLower() );
+ maObjRectWithSpaces.AddWidth(rLR.GetRight() );
+ }
+
+ mbObjRectWithSpacesValid = true;
+ maLastObjRect = GetObjRect();
+ }
+
+ return maObjRectWithSpaces;
+}
+
+// --> #i68520#
+void SwAnchoredObject::SetObjTop( const SwTwips _nTop)
+{
+ const bool bTopChanged( SetObjTop_( _nTop ) );
+ if ( bTopChanged )
+ {
+ mbObjRectWithSpacesValid = false;
+ }
+}
+
+void SwAnchoredObject::SetObjLeft( const SwTwips _nLeft)
+{
+ const bool bLeftChanged( SetObjLeft_( _nLeft ) );
+ if ( bLeftChanged )
+ {
+ mbObjRectWithSpacesValid = false;
+ }
+}
+
+/** method to update anchored object in the <SwSortedObjs> lists
+
+ #i28701#
+ If document compatibility option 'Consider wrapping style influence
+ on object positioning' is ON, additionally all anchored objects
+ at the anchor frame and all following anchored objects on the page
+ frame are invalidated.
+*/
+
+void SwAnchoredObject::UpdateObjInSortedList()
+{
+ if(!GetAnchorFrame())
+ return;
+
+ if ( GetFrameFormat().getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ // invalidate position of all anchored objects at anchor frame
+ if ( GetAnchorFrame()->GetDrawObjs() )
+ {
+ const SwSortedObjs* pObjs = GetAnchorFrame()->GetDrawObjs();
+ for(auto it = pObjs->begin(); it != pObjs->end(); ++it)
+ {
+ SwAnchoredObject* pAnchoredObj = *it;
+ if(pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos())
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ else
+ pAnchoredObj->InvalidateObjPos();
+ }
+ }
+ // invalidate all following anchored objects on the page frame
+ if ( GetPageFrame() && GetPageFrame()->GetSortedObjs() )
+ {
+ const SwSortedObjs* pObjs = GetPageFrame()->GetSortedObjs();
+ // determine start index
+ for ( size_t i = pObjs->ListPosOf( *this ) + 1; i < pObjs->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ else
+ pAnchoredObj->InvalidateObjPos();
+ }
+ }
+ }
+ // update its position in the sorted object list of its anchor frame
+ AnchorFrame()->GetDrawObjs()->Update( *this );
+ // update its position in the sorted object list of its page frame
+ // note: as-character anchored object aren't registered at a page frame
+ if ( GetPageFrame() && GetPageFrame()->GetSortedObjs() &&
+ GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR )
+ {
+ GetPageFrame()->GetSortedObjs()->Update( *this );
+ }
+}
+
+/** method to determine, if invalidation of position is allowed
+
+ #i28701#
+*/
+bool SwAnchoredObject::InvalidationOfPosAllowed() const
+{
+ // --> Check, if page frame layout is in progress,
+ // isn't needed, because of anchored object, whose are moved forward.
+ return !PositionLocked();
+}
+
+/** method to determine the page frame, on which the 'anchor' of
+ the given anchored object is.
+
+ #i28701#
+ #i33751#, #i34060#
+ Adjust meaning of method and thus its name: If the anchored object
+ or its anchor isn't correctly inserted in the layout, no page frame
+ can be found. Thus, the return type changed to be a pointer and can
+ be NULL.
+*/
+SwPageFrame* SwAnchoredObject::FindPageFrameOfAnchor()
+{
+ SwPageFrame* pRetPageFrame = nullptr;
+
+ // --> #i44339# - check, if anchor frame exists.
+ if ( mpAnchorFrame )
+ {
+ // --> #i26945# - use new method <GetAnchorFrameContainingAnchPos()>
+ pRetPageFrame = GetAnchorFrameContainingAnchPos()->FindPageFrame();
+ }
+
+ return pRetPageFrame;
+}
+
+/** get frame, which contains the anchor character, if the object
+ is anchored at-character or as-character.
+
+ #i26945#
+
+ @return SwTextFrame*
+ text frame containing the anchor character. It's NULL, if the object
+ isn't anchored at-character resp. as-character.
+*/
+SwTextFrame* SwAnchoredObject::FindAnchorCharFrame()
+{
+ SwTextFrame* pAnchorCharFrame( nullptr );
+
+ // --> #i44339# - check, if anchor frame exists.
+ if ( mpAnchorFrame )
+ {
+ const SwFormatAnchor& rAnch = GetFrameFormat().GetAnchor();
+ if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR))
+ {
+ SwTextFrame *const pFrame(static_cast<SwTextFrame*>(AnchorFrame()));
+ TextFrameIndex const nOffset(pFrame->MapModelToViewPos(*rAnch.GetContentAnchor()));
+ pAnchorCharFrame = &pFrame->GetFrameAtOfst(nOffset);
+ }
+ }
+
+ return pAnchorCharFrame;
+}
+
+/** method to determine, if a format on the anchored object is possible
+
+ #i28701#
+ A format is possible, if anchored object is in an invisible layer.
+ Note: method is virtual to refine the conditions for the sub-classes.
+*/
+bool SwAnchoredObject::IsFormatPossible() const
+{
+ return GetFrameFormat().GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetDrawObj()->GetLayer() );
+}
+
+bool SwAnchoredObject::IsDraggingOffPageAllowed(const SwFrameFormat* pFrameFormat)
+{
+ OSL_ASSERT(pFrameFormat);
+ const bool bDisablePositioning = pFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ const bool bIsWrapThrough = pFrameFormat->GetSurround().GetSurround() == text::WrapTextMode::WrapTextMode_THROUGH;
+
+ return bDisablePositioning && bIsWrapThrough;
+}
+
+// --> #i3317#
+void SwAnchoredObject::SetTmpConsiderWrapInfluence( const bool _bTmpConsiderWrapInfluence )
+{
+ mbTmpConsiderWrapInfluence = _bTmpConsiderWrapInfluence;
+ // --> #i35911#
+ if ( mbTmpConsiderWrapInfluence )
+ {
+ SwLayouter::InsertObjForTmpConsiderWrapInfluence( *(GetFrameFormat().GetDoc()),
+ *this );
+ }
+}
+
+void SwAnchoredObject::ClearTmpConsiderWrapInfluence()
+{
+ mbTmpConsiderWrapInfluence = false;
+ mbClearedEnvironment = false;
+ SetClearedEnvironment( false );
+ SwLayouter::RemoveObjForTmpConsiderWrapInfluence( *(GetFrameFormat().GetDoc()),
+ *this );
+}
+void SwAnchoredObject::SetTmpConsiderWrapInfluenceOfOtherObjs()
+{
+ const SwSortedObjs* pObjs = GetAnchorFrame()->GetDrawObjs();
+ if ( pObjs->size() > 1 )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj != this )
+ {
+ pAnchoredObj->SetTmpConsiderWrapInfluence( true/*bTmpConsiderWrapInfluence*/ );
+ }
+ }
+ }
+}
+
+/** method to determine, if the anchored object is overlapping with a
+ previous column
+
+ #i3317#
+ overlapping with a previous column means, that the object overlaps
+ with a column, which is a previous one of the column its anchor
+ frame is in.
+ Only applied for at-paragraph and at-character anchored objects.
+*/
+bool SwAnchoredObject::OverlapsPrevColumn() const
+{
+ bool bOverlapsPrevColumn( false );
+
+ if ( mpAnchorFrame && mpAnchorFrame->IsTextFrame() )
+ {
+ const SwFrame* pColFrame = mpAnchorFrame->FindColFrame();
+ if ( pColFrame && pColFrame->GetPrev() )
+ {
+ const SwFrame* pTmpColFrame = pColFrame->GetPrev();
+ SwRect aChkRect;
+ while ( pTmpColFrame )
+ {
+ aChkRect.Union( pTmpColFrame->getFrameArea() );
+ pTmpColFrame = pTmpColFrame->GetPrev();
+ }
+ bOverlapsPrevColumn = GetObjRect().Overlaps( aChkRect );
+ }
+ }
+
+ return bOverlapsPrevColumn;
+}
+
+/** method to determine position of anchored object relative to
+ anchor frame
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+*/
+Point SwAnchoredObject::GetRelPosToAnchorFrame() const
+{
+ Point aRelPos;
+
+ assert(GetAnchorFrame());
+ aRelPos = GetObjRect().Pos();
+ aRelPos -= GetAnchorFrame()->getFrameArea().Pos();
+
+ return aRelPos;
+}
+
+/** method to determine position of anchored object relative to
+ page frame
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+ #i33818# - add parameters <_bFollowTextFlow> and
+ <_obRelToTableCell>
+ If <_bFollowTextFlow> is set and object is anchored inside table,
+ the position relative to the table cell is determined. Output
+ parameter <_obRelToTableCell> reflects this situation
+*/
+Point SwAnchoredObject::GetRelPosToPageFrame( const bool _bFollowTextFlow,
+ bool& _obRelToTableCell ) const
+{
+ Point aRelPos;
+ _obRelToTableCell = false;
+
+ assert(GetAnchorFrame());
+ assert(GetAnchorFrame()->FindPageFrame());
+
+ aRelPos = GetObjRect().Pos();
+ // --> #i33818# - search for cell frame, if object has to
+ // follow the text flow.
+ const SwFrame* pFrame( nullptr );
+ if ( _bFollowTextFlow && !GetAnchorFrame()->IsPageFrame() )
+ {
+ pFrame = GetAnchorFrame()->GetUpper();
+ while ( !pFrame->IsCellFrame() && !pFrame->IsPageFrame() )
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ }
+ else
+ {
+ pFrame = GetAnchorFrame()->FindPageFrame();
+ }
+ if ( pFrame->IsCellFrame() )
+ {
+ aRelPos -= pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
+ _obRelToTableCell = true;
+ }
+ else
+ {
+ aRelPos -= pFrame->getFrameArea().Pos();
+ }
+
+ return aRelPos;
+}
+
+/** method to determine position of anchored object relative to
+ anchor character
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+*/
+Point SwAnchoredObject::GetRelPosToChar() const
+{
+ Point aRelPos = GetObjRect().Pos();
+ aRelPos -= GetLastCharRect().Pos();
+
+ return aRelPos;
+}
+
+/** method to determine position of anchored object relative to
+ top of line
+
+ #i30669#
+ Usage: Needed layout information for WW8 export
+*/
+Point SwAnchoredObject::GetRelPosToLine() const
+{
+ Point aRelPos = GetObjRect().Pos();
+ aRelPos.AdjustY( -(GetLastTopOfLine()) );
+
+ return aRelPos;
+}
+
+const SwFlyFrame* SwAnchoredObject::DynCastFlyFrame() const
+{
+ return nullptr;
+}
+
+SwFlyFrame* SwAnchoredObject::DynCastFlyFrame()
+{
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/atrfrm.cxx b/sw/source/core/layout/atrfrm.cxx
new file mode 100644
index 000000000..df2d8e974
--- /dev/null
+++ b/sw/source/core/layout/atrfrm.cxx
@@ -0,0 +1,3711 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/text/ColumnSeparatorStyle.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/text/TextGridMode.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <sal/log.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svtools/unoimap.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/imap.hxx>
+#include <vcl/imapobj.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <frmfmt.hxx>
+#include <unocoll.hxx>
+#include <fmtclds.hxx>
+#include <fmtornt.hxx>
+#include <fmthdft.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtfordr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtftntx.hxx>
+#include <fmteiro.hxx>
+#include <fmturl.hxx>
+#include <fmtcnct.hxx>
+#include <section.hxx>
+#include <fmtline.hxx>
+#include <tgrditem.hxx>
+#include <hfspacingitem.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <notxtfrm.hxx>
+#include <txtfrm.hxx>
+#include <crsrsh.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <frmtool.hxx>
+#include <flyfrms.hxx>
+#include <pagedesc.hxx>
+#include <grfatr.hxx>
+#include <ndnotxt.hxx>
+#include <node2lay.hxx>
+#include <fmtclbl.hxx>
+#include <swunohelper.hxx>
+#include <unoframe.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <editeng/brushitem.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <unomid.h>
+#include <strings.hrc>
+#include <svx/svdundo.hxx>
+#include <svx/SvxXTextColumns.hxx>
+#include <sortedobjs.hxx>
+#include <HandleAnchorNodeChg.hxx>
+#include <calbck.hxx>
+#include <pagedeschint.hxx>
+#include <drawdoc.hxx>
+#include <hints.hxx>
+#include <frameformats.hxx>
+#include <unoprnms.hxx>
+
+#include <ndtxt.hxx>
+
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <svl/itemiter.hxx>
+#include <wrtsh.hxx>
+#include <txtfld.hxx>
+#include <cellatr.hxx>
+
+using namespace ::com::sun::star;
+
+namespace sw {
+
+bool GetAtPageRelOrientation(sal_Int16 & rOrientation, bool const isIgnorePrintArea)
+{
+ switch (rOrientation)
+ {
+ case text::RelOrientation::CHAR:
+ case text::RelOrientation::FRAME:
+ rOrientation = text::RelOrientation::PAGE_FRAME;
+ return true;
+ case text::RelOrientation::PRINT_AREA:
+ if (isIgnorePrintArea)
+ {
+ return false;
+ }
+ else
+ {
+ rOrientation = text::RelOrientation::PAGE_PRINT_AREA;
+ return true;
+ }
+ case text::RelOrientation::FRAME_LEFT:
+ rOrientation = text::RelOrientation::PAGE_LEFT;
+ return true;
+ case text::RelOrientation::FRAME_RIGHT:
+ rOrientation = text::RelOrientation::PAGE_RIGHT;
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace sw
+
+SfxPoolItem* SwFormatLineNumber::CreateDefault() { return new SwFormatLineNumber; }
+
+static sal_Int16 lcl_IntToRelation(const uno::Any& rVal)
+{
+ sal_Int16 nVal = text::RelOrientation::FRAME;
+ if (!(rVal >>= nVal))
+ SAL_WARN("sw.core", "lcl_IntToRelation: read from Any failed!");
+ return nVal;
+}
+
+static void lcl_DelHFFormat( SwClient *pToRemove, SwFrameFormat *pFormat )
+{
+ //If the client is the last one who uses this format, then we have to delete
+ //it - before this is done, we may need to delete the content-section.
+ SwDoc* pDoc = pFormat->GetDoc();
+ pFormat->Remove( pToRemove );
+ if( pDoc->IsInDtor() )
+ {
+ delete pFormat;
+ return;
+ }
+
+ // Anything other than frames registered?
+ bool bDel = true;
+ {
+ // nested scope because DTOR of SwClientIter resets the flag bTreeChg.
+ // It's suboptimal if the format is deleted beforehand.
+ SwIterator<SwClient,SwFrameFormat> aIter(*pFormat);
+ for(SwClient* pLast = aIter.First(); bDel && pLast; pLast = aIter.Next())
+ if (dynamic_cast<const SwFrame*>(pLast) == nullptr)
+ bDel = false;
+ }
+
+ if ( !bDel )
+ return;
+
+ // If there is a Cursor registered in one of the nodes, we need to call the
+ // ParkCursor in an (arbitrary) shell.
+ SwFormatContent& rCnt = const_cast<SwFormatContent&>(pFormat->GetContent());
+ if ( rCnt.GetContentIdx() )
+ {
+ SwNode *pNode = nullptr;
+ {
+ // #i92993#
+ // Begin with start node of page header/footer to assure that
+ // complete content is checked for cursors and the complete content
+ // is deleted on below made method call <pDoc->getIDocumentContentOperations().DeleteSection(pNode)>
+ SwNodeIndex aIdx( *rCnt.GetContentIdx(), 0 );
+ // If there is a Cursor registered in one of the nodes, we need to call the
+ // ParkCursor in an (arbitrary) shell.
+ pNode = & aIdx.GetNode();
+ SwNodeOffset nEnd = pNode->EndOfSectionIndex();
+ while ( aIdx < nEnd )
+ {
+ if ( pNode->IsContentNode() &&
+ static_cast<SwContentNode*>(pNode)->HasWriterListeners() )
+ {
+ SwCursorShell *pShell = SwIterator<SwCursorShell,SwContentNode>( *static_cast<SwContentNode*>(pNode) ).First();
+ if( pShell )
+ {
+ pShell->ParkCursor( aIdx );
+ aIdx = nEnd-1;
+ }
+ }
+ ++aIdx;
+ pNode = & aIdx.GetNode();
+ }
+ }
+ rCnt.SetNewContentIdx( nullptr );
+
+ // When deleting a header/footer-format, we ALWAYS need to disable
+ // the undo function (Bug 31069)
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ OSL_ENSURE( pNode, "A big problem." );
+ pDoc->getIDocumentContentOperations().DeleteSection( pNode );
+ }
+ delete pFormat;
+}
+
+void SwFormatFrameSize::ScaleMetrics(tools::Long lMult, tools::Long lDiv) {
+ // Don't inherit the SvxSizeItem override (might or might not be relevant; added "just in case"
+ // when changing SwFormatFrameSize to derive from SvxSizeItem instead of directly from
+ // SfxPoolItem):
+ return SfxPoolItem::ScaleMetrics(lMult, lDiv);
+}
+
+bool SwFormatFrameSize::HasMetrics() const {
+ // Don't inherit the SvxSizeItem override (might or might not be relevant; added "just in case"
+ // when changing SwFormatFrameSize to derive from SvxSizeItem instead of directly from
+ // SfxPoolItem):
+ return SfxPoolItem::HasMetrics();
+}
+
+// Partially implemented inline in hxx
+SwFormatFrameSize::SwFormatFrameSize( SwFrameSize eSize, SwTwips nWidth, SwTwips nHeight )
+ : SvxSizeItem( RES_FRM_SIZE, {nWidth, nHeight} ),
+ m_eFrameHeightType( eSize ),
+ m_eFrameWidthType( SwFrameSize::Fixed )
+{
+ m_nWidthPercent = m_eWidthPercentRelation = m_nHeightPercent = m_eHeightPercentRelation = 0;
+}
+
+bool SwFormatFrameSize::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return( m_eFrameHeightType == static_cast<const SwFormatFrameSize&>(rAttr).m_eFrameHeightType &&
+ m_eFrameWidthType == static_cast<const SwFormatFrameSize&>(rAttr).m_eFrameWidthType &&
+ SvxSizeItem::operator==(rAttr)&&
+ m_nWidthPercent == static_cast<const SwFormatFrameSize&>(rAttr).GetWidthPercent() &&
+ m_eWidthPercentRelation == static_cast<const SwFormatFrameSize&>(rAttr).GetWidthPercentRelation() &&
+ m_nHeightPercent == static_cast<const SwFormatFrameSize&>(rAttr).GetHeightPercent() &&
+ m_eHeightPercentRelation == static_cast<const SwFormatFrameSize&>(rAttr).GetHeightPercentRelation() );
+}
+
+SwFormatFrameSize* SwFormatFrameSize::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFrameSize( *this );
+}
+
+bool SwFormatFrameSize::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_FRMSIZE_SIZE:
+ {
+ awt::Size aTmp;
+ aTmp.Height = convertTwipToMm100(GetHeight());
+ aTmp.Width = convertTwipToMm100(GetWidth());
+ rVal <<= aTmp;
+ }
+ break;
+ case MID_FRMSIZE_REL_HEIGHT:
+ rVal <<= static_cast<sal_Int16>(GetHeightPercent() != SwFormatFrameSize::SYNCED ? GetHeightPercent() : 0);
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ rVal <<= GetHeightPercentRelation();
+ break;
+ case MID_FRMSIZE_REL_WIDTH:
+ rVal <<= static_cast<sal_Int16>(GetWidthPercent() != SwFormatFrameSize::SYNCED ? GetWidthPercent() : 0);
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ rVal <<= GetWidthPercentRelation();
+ break;
+ case MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH:
+ rVal <<= SwFormatFrameSize::SYNCED == GetHeightPercent();
+ break;
+ case MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT:
+ rVal <<= SwFormatFrameSize::SYNCED == GetWidthPercent();
+ break;
+ case MID_FRMSIZE_WIDTH :
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetWidth()));
+ break;
+ case MID_FRMSIZE_HEIGHT:
+ // #95848# returned size should never be zero.
+ // (there was a bug that allowed for setting height to 0.
+ // Thus there some documents existing with that not allowed
+ // attribute value which may cause problems on import.)
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetHeight() < MINLAY ? MINLAY : GetHeight() ));
+ break;
+ case MID_FRMSIZE_SIZE_TYPE:
+ rVal <<= static_cast<sal_Int16>(GetHeightSizeType());
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ rVal <<= SwFrameSize::Fixed != GetHeightSizeType();
+ break;
+ case MID_FRMSIZE_WIDTH_TYPE:
+ rVal <<= static_cast<sal_Int16>(GetWidthSizeType());
+ break;
+ }
+ return true;
+}
+
+bool SwFormatFrameSize::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_FRMSIZE_SIZE:
+ {
+ awt::Size aVal;
+ if(!(rVal >>= aVal))
+ bRet = false;
+ else
+ {
+ Size aTmp(aVal.Width, aVal.Height);
+ if(bConvert)
+ {
+ aTmp.setHeight(o3tl::toTwips(aTmp.Height(), o3tl::Length::mm100));
+ aTmp.setWidth(o3tl::toTwips(aTmp.Width(), o3tl::Length::mm100));
+ }
+ SetSize(aTmp);
+ }
+ }
+ break;
+ case MID_FRMSIZE_REL_HEIGHT:
+ {
+ sal_Int16 nSet = 0;
+ rVal >>= nSet;
+ if(nSet >= 0 && nSet < SwFormatFrameSize::SYNCED)
+ SetHeightPercent(static_cast<sal_uInt8>(nSet));
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ {
+ sal_Int16 eSet = 0;
+ rVal >>= eSet;
+ SetHeightPercentRelation(eSet);
+ }
+ break;
+ case MID_FRMSIZE_REL_WIDTH:
+ {
+ sal_Int16 nSet = 0;
+ rVal >>= nSet;
+ if(nSet >= 0 && nSet < SwFormatFrameSize::SYNCED)
+ SetWidthPercent(static_cast<sal_uInt8>(nSet));
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ {
+ sal_Int16 eSet = 0;
+ rVal >>= eSet;
+ SetWidthPercentRelation(eSet);
+ }
+ break;
+ case MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH:
+ {
+ bool bSet = *o3tl::doAccess<bool>(rVal);
+ if(bSet)
+ SetHeightPercent(SwFormatFrameSize::SYNCED);
+ else if( SwFormatFrameSize::SYNCED == GetHeightPercent() )
+ SetHeightPercent( 0 );
+ }
+ break;
+ case MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT:
+ {
+ bool bSet = *o3tl::doAccess<bool>(rVal);
+ if(bSet)
+ SetWidthPercent(SwFormatFrameSize::SYNCED);
+ else if( SwFormatFrameSize::SYNCED == GetWidthPercent() )
+ SetWidthPercent(0);
+ }
+ break;
+ case MID_FRMSIZE_WIDTH :
+ {
+ sal_Int32 nWd = 0;
+ if(rVal >>= nWd)
+ {
+ if(bConvert)
+ nWd = o3tl::toTwips(nWd, o3tl::Length::mm100);
+ if(nWd < MINLAY)
+ nWd = MINLAY;
+ SetWidth(nWd);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_HEIGHT:
+ {
+ sal_Int32 nHg = 0;
+ if(rVal >>= nHg)
+ {
+ if(bConvert)
+ nHg = o3tl::toTwips(nHg, o3tl::Length::mm100);
+ if(nHg < MINLAY)
+ nHg = MINLAY;
+ SetHeight(nHg);
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_SIZE_TYPE:
+ {
+ sal_Int16 nType = 0;
+ if((rVal >>= nType) && nType >= 0 && nType <= static_cast<int>(SwFrameSize::Minimum) )
+ {
+ SetHeightSizeType(static_cast<SwFrameSize>(nType));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_FRMSIZE_IS_AUTO_HEIGHT:
+ {
+ bool bSet = *o3tl::doAccess<bool>(rVal);
+ SetHeightSizeType(bSet ? SwFrameSize::Variable : SwFrameSize::Fixed);
+ }
+ break;
+ case MID_FRMSIZE_WIDTH_TYPE:
+ {
+ sal_Int16 nType = 0;
+ if((rVal >>= nType) && nType >= 0 && nType <= static_cast<int>(SwFrameSize::Minimum) )
+ {
+ SetWidthSizeType(static_cast<SwFrameSize>(nType));
+ }
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatFrameSize::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatFrameSize"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ std::stringstream aSize;
+ aSize << GetSize();
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(aSize.str().c_str()));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eFrameHeightType"), BAD_CAST(OString::number(static_cast<int>(m_eFrameHeightType)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eFrameWidthType"), BAD_CAST(OString::number(static_cast<int>(m_eFrameWidthType)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidthPercent"), BAD_CAST(OString::number(m_nWidthPercent).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eWidthPercentRelation"), BAD_CAST(OString::number(m_eWidthPercentRelation).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nHeightPercent"), BAD_CAST(OString::number(m_nHeightPercent).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eHeightPercentRelation"), BAD_CAST(OString::number(m_eHeightPercentRelation).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatFillOrder::SwFormatFillOrder( SwFillOrder nFO )
+ : SfxEnumItem( RES_FILL_ORDER, nFO )
+{}
+
+SwFormatFillOrder* SwFormatFillOrder::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFillOrder( GetValue() );
+}
+
+sal_uInt16 SwFormatFillOrder::GetValueCount() const
+{
+ return SW_FILL_ORDER_END - SW_FILL_ORDER_BEGIN;
+}
+
+// Partially implemented inline in hxx
+SwFormatHeader::SwFormatHeader( SwFrameFormat *pHeaderFormat )
+ : SfxPoolItem( RES_HEADER ),
+ SwClient( pHeaderFormat ),
+ m_bActive( pHeaderFormat )
+{
+}
+
+SwFormatHeader::SwFormatHeader( const SwFormatHeader &rCpy )
+ : SfxPoolItem( RES_HEADER ),
+ SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCpy.GetRegisteredIn())) ),
+ m_bActive( rCpy.IsActive() )
+{
+}
+
+SwFormatHeader::SwFormatHeader( bool bOn )
+ : SfxPoolItem( RES_HEADER ),
+ SwClient( nullptr ),
+ m_bActive( bOn )
+{
+}
+
+ SwFormatHeader::~SwFormatHeader()
+{
+ if ( GetHeaderFormat() )
+ lcl_DelHFFormat( this, GetHeaderFormat() );
+}
+
+bool SwFormatHeader::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( GetRegisteredIn() == static_cast<const SwFormatHeader&>(rAttr).GetRegisteredIn() &&
+ m_bActive == static_cast<const SwFormatHeader&>(rAttr).IsActive() );
+}
+
+SwFormatHeader* SwFormatHeader::Clone( SfxItemPool* ) const
+{
+ return new SwFormatHeader( *this );
+}
+
+void SwFormatHeader::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add(this);
+}
+
+// Partially implemented inline in hxx
+SwFormatFooter::SwFormatFooter( SwFrameFormat *pFooterFormat )
+ : SfxPoolItem( RES_FOOTER ),
+ SwClient( pFooterFormat ),
+ m_bActive( pFooterFormat )
+{
+}
+
+SwFormatFooter::SwFormatFooter( const SwFormatFooter &rCpy )
+ : SfxPoolItem( RES_FOOTER ),
+ SwClient( const_cast<sw::BroadcastingModify*>(static_cast<const sw::BroadcastingModify*>(rCpy.GetRegisteredIn())) ),
+ m_bActive( rCpy.IsActive() )
+{
+}
+
+SwFormatFooter::SwFormatFooter( bool bOn )
+ : SfxPoolItem( RES_FOOTER ),
+ SwClient( nullptr ),
+ m_bActive( bOn )
+{
+}
+
+ SwFormatFooter::~SwFormatFooter()
+{
+ if ( GetFooterFormat() )
+ lcl_DelHFFormat( this, GetFooterFormat() );
+}
+
+void SwFormatFooter::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add(this);
+}
+
+bool SwFormatFooter::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( GetRegisteredIn() == static_cast<const SwFormatFooter&>(rAttr).GetRegisteredIn() &&
+ m_bActive == static_cast<const SwFormatFooter&>(rAttr).IsActive() );
+}
+
+SwFormatFooter* SwFormatFooter::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFooter( *this );
+}
+
+// Partially implemented inline in hxx
+SwFormatContent::SwFormatContent( const SwFormatContent &rCpy )
+ : SfxPoolItem( RES_CNTNT )
+{
+ m_pStartNode.reset( rCpy.GetContentIdx() ?
+ new SwNodeIndex( *rCpy.GetContentIdx() ) : nullptr);
+}
+
+SwFormatContent::SwFormatContent( const SwStartNode *pStartNd )
+ : SfxPoolItem( RES_CNTNT )
+{
+ m_pStartNode.reset( pStartNd ? new SwNodeIndex( *pStartNd ) : nullptr);
+}
+
+SwFormatContent::~SwFormatContent()
+{
+}
+
+void SwFormatContent::SetNewContentIdx( const SwNodeIndex *pIdx )
+{
+ m_pStartNode.reset( pIdx ? new SwNodeIndex( *pIdx ) : nullptr );
+}
+
+bool SwFormatContent::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ if( static_cast<bool>(m_pStartNode) != static_cast<bool>(static_cast<const SwFormatContent&>(rAttr).m_pStartNode) )
+ return false;
+ if( m_pStartNode )
+ return ( *m_pStartNode == *static_cast<const SwFormatContent&>(rAttr).GetContentIdx() );
+ return true;
+}
+
+SwFormatContent* SwFormatContent::Clone( SfxItemPool* ) const
+{
+ return new SwFormatContent( *this );
+}
+
+void SwFormatContent::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatContent"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ if (m_pStartNode)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("startNode"),
+ BAD_CAST(OString::number(sal_Int32(m_pStartNode->GetNode().GetIndex())).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("startNodePtr"), "%p",
+ &m_pStartNode->GetNode());
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatPageDesc::SwFormatPageDesc( const SwFormatPageDesc &rCpy )
+ : SfxPoolItem( RES_PAGEDESC ),
+ SwClient( const_cast<SwPageDesc*>(rCpy.GetPageDesc()) ),
+ m_oNumOffset( rCpy.m_oNumOffset ),
+ m_pDefinedIn( nullptr )
+{
+}
+
+SwFormatPageDesc::SwFormatPageDesc( const SwPageDesc *pDesc )
+ : SfxPoolItem( RES_PAGEDESC ),
+ SwClient( const_cast<SwPageDesc*>(pDesc) ),
+ m_pDefinedIn( nullptr )
+{
+}
+
+SwFormatPageDesc &SwFormatPageDesc::operator=(const SwFormatPageDesc &rCpy)
+{
+ if(this == &rCpy)
+ return *this;
+
+ if (rCpy.GetPageDesc())
+ RegisterToPageDesc(*const_cast<SwPageDesc*>(rCpy.GetPageDesc()));
+ m_oNumOffset = rCpy.m_oNumOffset;
+ m_pDefinedIn = nullptr;
+
+ return *this;
+}
+
+ SwFormatPageDesc::~SwFormatPageDesc() {}
+
+bool SwFormatPageDesc::KnowsPageDesc() const
+{
+ return (GetRegisteredIn() != nullptr);
+}
+
+bool SwFormatPageDesc::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_pDefinedIn == static_cast<const SwFormatPageDesc&>(rAttr).m_pDefinedIn ) &&
+ ( m_oNumOffset == static_cast<const SwFormatPageDesc&>(rAttr).m_oNumOffset ) &&
+ ( GetPageDesc() == static_cast<const SwFormatPageDesc&>(rAttr).GetPageDesc() );
+}
+
+SwFormatPageDesc* SwFormatPageDesc::Clone( SfxItemPool* ) const
+{
+ return new SwFormatPageDesc( *this );
+}
+
+void SwFormatPageDesc::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (const SwPageDescHint* pHint = dynamic_cast<const SwPageDescHint*>(&rHint))
+ {
+ // mba: shouldn't that be broadcasted also?
+ SwFormatPageDesc aDfltDesc(pHint->GetPageDesc());
+ SwPageDesc* pDesc = pHint->GetPageDesc();
+ const sw::BroadcastingModify* pMod = GetDefinedIn();
+ if(pMod)
+ {
+ if(auto pContentNode = dynamic_cast<const SwContentNode*>(pMod))
+ const_cast<SwContentNode*>(pContentNode)->SetAttr(aDfltDesc);
+ else if(auto pFormat = dynamic_cast<const SwFormat*>(pMod))
+ const_cast<SwFormat*>(pFormat)->SetFormatAttr( aDfltDesc );
+ else
+ {
+ SAL_WARN("sw.core", "SwFormatPageDesc registered at " << typeid(pMod).name() << ".");
+ RegisterToPageDesc(*pDesc);
+ }
+ }
+ else
+ // there could be an Undo-copy
+ RegisterToPageDesc(*pDesc);
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(RES_OBJECTDYING == pLegacy->GetWhich())
+ {
+ m_pDefinedIn = nullptr;
+ EndListeningAll();
+ }
+ }
+}
+
+void SwFormatPageDesc::RegisterToPageDesc( SwPageDesc& rDesc )
+{
+ rDesc.Add( this );
+}
+
+bool SwFormatPageDesc::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_PAGEDESC_PAGENUMOFFSET:
+ {
+ ::std::optional<sal_uInt16> oOffset = GetNumOffset();
+ if (oOffset)
+ {
+ rVal <<= static_cast<sal_Int16>(*oOffset);
+ }
+ else
+ {
+ rVal.clear();
+ }
+ }
+ break;
+
+ case MID_PAGEDESC_PAGEDESCNAME:
+ {
+ const SwPageDesc* pDesc = GetPageDesc();
+ if( pDesc )
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName(pDesc->GetName(), aString, SwGetPoolIdFromName::PageDesc);
+ rVal <<= aString;
+ }
+ else
+ rVal.clear();
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatPageDesc::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_PAGEDESC_PAGENUMOFFSET:
+ {
+ sal_Int16 nOffset = 0;
+ if (!rVal.hasValue())
+ {
+ SetNumOffset(std::nullopt);
+ }
+ else if (rVal >>= nOffset)
+ SetNumOffset( nOffset );
+ else
+ bRet = false;
+ }
+ break;
+
+ case MID_PAGEDESC_PAGEDESCNAME:
+ /* Doesn't work, because the attribute doesn't need the name but a
+ * pointer to the PageDesc (it's a client of it). The pointer can
+ * only be requested from the document using the name.
+ */
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatPageDesc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatPageDesc"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ if (m_oNumOffset)
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("oNumOffset"), BAD_CAST(OString::number(*m_oNumOffset).getStr()));
+ else
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("oNumOffset"), BAD_CAST("none"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("pPageDesc"), "%p", GetPageDesc());
+ if (const SwPageDesc* pPageDesc = GetPageDesc())
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(pPageDesc->GetName().toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SwFormatCol
+// Partially implemented inline in hxx
+
+SwColumn::SwColumn() :
+ m_nWish ( 0 ),
+ m_nLeft ( 0 ),
+ m_nRight( 0 )
+{
+}
+
+bool SwColumn::operator==( const SwColumn &rCmp ) const
+{
+ return m_nWish == rCmp.GetWishWidth() &&
+ GetLeft() == rCmp.GetLeft() &&
+ GetRight() == rCmp.GetRight();
+}
+
+void SwColumn::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwColumn"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWish"), BAD_CAST(OString::number(m_nWish).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nUpper"), BAD_CAST(OString::number(0).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLower"), BAD_CAST(OString::number(0).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLeft"), BAD_CAST(OString::number(m_nLeft).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nRight"), BAD_CAST(OString::number(m_nRight).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwFormatCol::SwFormatCol( const SwFormatCol& rCpy )
+ : SfxPoolItem( RES_COL ),
+ m_eLineStyle( rCpy.m_eLineStyle ),
+ m_nLineWidth( rCpy.m_nLineWidth),
+ m_aLineColor( rCpy.m_aLineColor),
+ m_nLineHeight( rCpy.GetLineHeight() ),
+ m_eAdj( rCpy.GetLineAdj() ),
+ m_nWidth( rCpy.GetWishWidth() ),
+ m_aWidthAdjustValue( rCpy.m_aWidthAdjustValue ),
+ m_bOrtho( rCpy.IsOrtho() )
+{
+ m_aColumns.reserve(rCpy.GetNumCols());
+ for ( sal_uInt16 i = 0; i < rCpy.GetNumCols(); ++i )
+ {
+ m_aColumns.emplace_back(rCpy.GetColumns()[i] );
+ }
+}
+
+SwFormatCol::~SwFormatCol() {}
+
+SwFormatCol& SwFormatCol::operator=( const SwFormatCol& rCpy )
+{
+ if (this != &rCpy)
+ {
+ m_eLineStyle = rCpy.m_eLineStyle;
+ m_nLineWidth = rCpy.m_nLineWidth;
+ m_aLineColor = rCpy.m_aLineColor;
+ m_nLineHeight = rCpy.GetLineHeight();
+ m_eAdj = rCpy.GetLineAdj();
+ m_nWidth = rCpy.GetWishWidth();
+ m_aWidthAdjustValue = rCpy.m_aWidthAdjustValue;
+ m_bOrtho = rCpy.IsOrtho();
+
+ m_aColumns.clear();
+ for ( sal_uInt16 i = 0; i < rCpy.GetNumCols(); ++i )
+ {
+ m_aColumns.emplace_back(rCpy.GetColumns()[i] );
+ }
+ }
+ return *this;
+}
+
+SwFormatCol::SwFormatCol()
+ : SfxPoolItem( RES_COL )
+ , m_eLineStyle( SvxBorderLineStyle::NONE)
+ ,
+ m_nLineWidth(0),
+ m_nLineHeight( 100 ),
+ m_eAdj( COLADJ_NONE ),
+ m_nWidth( USHRT_MAX ),
+ m_aWidthAdjustValue( 0 ),
+ m_bOrtho( true )
+{
+}
+
+bool SwFormatCol::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SwFormatCol &rCmp = static_cast<const SwFormatCol&>(rAttr);
+ if( !(m_eLineStyle == rCmp.m_eLineStyle &&
+ m_nLineWidth == rCmp.m_nLineWidth &&
+ m_aLineColor == rCmp.m_aLineColor &&
+ m_nLineHeight == rCmp.GetLineHeight() &&
+ m_eAdj == rCmp.GetLineAdj() &&
+ m_nWidth == rCmp.GetWishWidth() &&
+ m_bOrtho == rCmp.IsOrtho() &&
+ m_aColumns.size() == rCmp.GetNumCols() &&
+ m_aWidthAdjustValue == rCmp.GetAdjustValue()
+ ) )
+ return false;
+
+ for ( size_t i = 0; i < m_aColumns.size(); ++i )
+ if ( !(m_aColumns[i] == rCmp.GetColumns()[i]) )
+ return false;
+
+ return true;
+}
+
+SwFormatCol* SwFormatCol::Clone( SfxItemPool* ) const
+{
+ return new SwFormatCol( *this );
+}
+
+sal_uInt16 SwFormatCol::GetGutterWidth( bool bMin ) const
+{
+ sal_uInt16 nRet = 0;
+ if ( m_aColumns.size() == 2 )
+ nRet = m_aColumns[0].GetRight() + m_aColumns[1].GetLeft();
+ else if ( m_aColumns.size() > 2 )
+ {
+ bool bSet = false;
+ for ( size_t i = 1; i+1 < m_aColumns.size(); ++i )
+ {
+ const sal_uInt16 nTmp = m_aColumns[i].GetRight() + m_aColumns[i+1].GetLeft();
+ if ( bSet )
+ {
+ if ( nTmp != nRet )
+ {
+ if ( !bMin )
+ return USHRT_MAX;
+ if ( nRet > nTmp )
+ nRet = nTmp;
+ }
+ }
+ else
+ {
+ bSet = true;
+ nRet = nTmp;
+ }
+ }
+ }
+ return nRet;
+}
+
+void SwFormatCol::SetGutterWidth( sal_uInt16 nNew, sal_uInt16 nAct )
+{
+ if ( m_bOrtho )
+ Calc( nNew, nAct );
+ else
+ {
+ sal_uInt16 nHalf = nNew / 2;
+ for (size_t i = 0; i < m_aColumns.size(); ++i)
+ {
+ SwColumn &rCol = m_aColumns[i];
+ rCol.SetLeft(nHalf);
+ rCol.SetRight(nHalf);
+ if ( i == 0 )
+ rCol.SetLeft(0);
+ else if ( i+1 == m_aColumns.size() )
+ rCol.SetRight(0);
+ }
+ }
+}
+
+void SwFormatCol::Init( sal_uInt16 nNumCols, sal_uInt16 nGutterWidth, sal_uInt16 nAct )
+{
+ // Deleting seems to be a bit radical on the first sight; but otherwise we
+ // have to initialize all values of the remaining SwColumns.
+ m_aColumns.clear();
+ for ( sal_uInt16 i = 0; i < nNumCols; ++i )
+ {
+ m_aColumns.emplace_back( );
+ }
+ m_bOrtho = true;
+ m_nWidth = USHRT_MAX;
+ if( nNumCols )
+ Calc( nGutterWidth, nAct );
+}
+
+void SwFormatCol::SetOrtho( bool bNew, sal_uInt16 nGutterWidth, sal_uInt16 nAct )
+{
+ m_bOrtho = bNew;
+ if ( bNew && !m_aColumns.empty() )
+ Calc( nGutterWidth, nAct );
+}
+
+sal_uInt16 SwFormatCol::CalcColWidth( sal_uInt16 nCol, sal_uInt16 nAct ) const
+{
+ assert(nCol < m_aColumns.size());
+ if ( m_nWidth != nAct )
+ {
+ tools::Long nW = m_aColumns[nCol].GetWishWidth();
+ nW *= nAct;
+ nW /= m_nWidth;
+ return sal_uInt16(nW);
+ }
+ else
+ return m_aColumns[nCol].GetWishWidth();
+}
+
+sal_uInt16 SwFormatCol::CalcPrtColWidth( sal_uInt16 nCol, sal_uInt16 nAct ) const
+{
+ assert(nCol < m_aColumns.size());
+ sal_uInt16 nRet = CalcColWidth( nCol, nAct );
+ const SwColumn *pCol = &m_aColumns[nCol];
+ nRet = nRet - pCol->GetLeft();
+ nRet = nRet - pCol->GetRight();
+ return nRet;
+}
+
+void SwFormatCol::Calc( sal_uInt16 nGutterWidth, sal_uInt16 nAct )
+{
+ if (!GetNumCols())
+ return;
+
+ //First set the column widths with the current width, then calculate the
+ //column's requested width using the requested total width.
+ const sal_uInt16 nGutterHalf = nGutterWidth ? nGutterWidth / 2 : 0;
+
+ //Width of PrtAreas is totalwidth - spacings / count
+ sal_uInt16 nSpacings;
+ bool bFail = o3tl::checked_multiply<sal_uInt16>(GetNumCols() - 1, nGutterWidth, nSpacings);
+ if (bFail)
+ {
+ SAL_WARN("sw.core", "SwFormatVertOrient::Calc: overflow");
+ return;
+ }
+
+ const sal_uInt16 nPrtWidth = (nAct - nSpacings) / GetNumCols();
+ sal_uInt16 nAvail = nAct;
+
+ //The first column is PrtWidth + (gap width / 2)
+ const sal_uInt16 nLeftWidth = nPrtWidth + nGutterHalf;
+ SwColumn &rFirstCol = m_aColumns.front();
+ rFirstCol.SetWishWidth(nLeftWidth);
+ rFirstCol.SetRight(nGutterHalf);
+ rFirstCol.SetLeft(0);
+ nAvail = nAvail - nLeftWidth;
+
+ //Column 2 to n-1 is PrtWidth + gap width
+ const sal_uInt16 nMidWidth = nPrtWidth + nGutterWidth;
+
+ for (sal_uInt16 i = 1; i < GetNumCols()-1; ++i)
+ {
+ SwColumn &rCol = m_aColumns[i];
+ rCol.SetWishWidth(nMidWidth);
+ rCol.SetLeft(nGutterHalf);
+ rCol.SetRight(nGutterHalf);
+ nAvail = nAvail - nMidWidth;
+ }
+
+ //The last column is equivalent to the first one - to compensate rounding
+ //errors we add the remaining space of the other columns to the last one.
+ SwColumn &rLastCol = m_aColumns.back();
+ rLastCol.SetWishWidth(nAvail);
+ rLastCol.SetLeft(nGutterHalf);
+ rLastCol.SetRight(0);
+
+ assert(nAct != 0);
+ //Convert the current width to the requested width.
+ for (SwColumn &rCol: m_aColumns)
+ {
+ tools::Long nTmp = rCol.GetWishWidth();
+ nTmp *= GetWishWidth();
+ nTmp = nAct == 0 ? nTmp : nTmp / nAct;
+ rCol.SetWishWidth(sal_uInt16(nTmp));
+ }
+}
+
+bool SwFormatCol::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ if(MID_COLUMN_SEPARATOR_LINE == nMemberId)
+ {
+ OSL_FAIL("not implemented");
+ }
+ else
+ {
+ uno::Reference<text::XTextColumns> xCols(SvxXTextColumns_createInstance(),
+ css::uno::UNO_QUERY_THROW);
+ uno::Reference<beans::XPropertySet> xProps(xCols, css::uno::UNO_QUERY_THROW);
+
+ if (GetNumCols() > 0)
+ {
+ xCols->setColumnCount(GetNumCols());
+ const sal_uInt16 nItemGutterWidth = GetGutterWidth();
+ sal_Int32 nAutoDistance = IsOrtho() ? USHRT_MAX == nItemGutterWidth
+ ? DEF_GUTTER_WIDTH
+ : static_cast<sal_Int32>(nItemGutterWidth)
+ : 0;
+ nAutoDistance = convertTwipToMm100(nAutoDistance);
+ xProps->setPropertyValue(UNO_NAME_AUTOMATIC_DISTANCE, uno::Any(nAutoDistance));
+
+ if (!IsOrtho())
+ {
+ auto aTextColumns = xCols->getColumns();
+ text::TextColumn* pColumns = aTextColumns.getArray();
+ const SwColumns& rCols = GetColumns();
+ for (sal_Int32 i = 0; i < aTextColumns.getLength(); ++i)
+ {
+ const SwColumn* pCol = &rCols[i];
+
+ pColumns[i].Width = pCol->GetWishWidth();
+ pColumns[i].LeftMargin = convertTwipToMm100(pCol->GetLeft());
+ pColumns[i].RightMargin = convertTwipToMm100(pCol->GetRight());
+ }
+ xCols->setColumns(aTextColumns); // sets "IsAutomatic" property to false
+ }
+ }
+ uno::Any aVal;
+ aVal <<= o3tl::narrowing<sal_Int32>(
+ o3tl::convert(GetLineWidth(), o3tl::Length::twip, o3tl::Length::mm100));
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_WIDTH, aVal);
+ aVal <<= GetLineColor();
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_COLOR, aVal);
+ aVal <<= static_cast<sal_Int32>(GetLineHeight());
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_RELATIVE_HEIGHT, aVal);
+ aVal <<= GetLineAdj() != COLADJ_NONE;
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_IS_ON, aVal);
+ sal_Int16 nStyle;
+ switch (GetLineStyle())
+ {
+ case SvxBorderLineStyle::SOLID:
+ nStyle = css::text::ColumnSeparatorStyle::SOLID;
+ break;
+ case SvxBorderLineStyle::DOTTED:
+ nStyle = css::text::ColumnSeparatorStyle::DOTTED;
+ break;
+ case SvxBorderLineStyle::DASHED:
+ nStyle = css::text::ColumnSeparatorStyle::DASHED;
+ break;
+ case SvxBorderLineStyle::NONE:
+ default:
+ nStyle = css::text::ColumnSeparatorStyle::NONE;
+ break;
+ }
+ aVal <<= nStyle;
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_STYLE, aVal);
+ style::VerticalAlignment eAlignment;
+ switch (GetLineAdj())
+ {
+ case COLADJ_TOP:
+ eAlignment = style::VerticalAlignment_TOP;
+ break;
+ case COLADJ_BOTTOM:
+ eAlignment = style::VerticalAlignment_BOTTOM;
+ break;
+ case COLADJ_CENTER:
+ case COLADJ_NONE:
+ default:
+ eAlignment = style::VerticalAlignment_MIDDLE;
+ }
+ aVal <<= eAlignment;
+ xProps->setPropertyValue(UNO_NAME_SEPARATOR_LINE_VERTIVAL_ALIGNMENT, aVal);
+ rVal <<= xCols;
+ }
+ return true;
+}
+
+bool SwFormatCol::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = false;
+ if(MID_COLUMN_SEPARATOR_LINE == nMemberId)
+ {
+ OSL_FAIL("not implemented");
+ }
+ else
+ {
+ uno::Reference< text::XTextColumns > xCols;
+ rVal >>= xCols;
+ if(xCols.is())
+ {
+ uno::Sequence<text::TextColumn> aSetColumns = xCols->getColumns();
+ const text::TextColumn* pArray = aSetColumns.getConstArray();
+ m_aColumns.clear();
+ //max count is 64k here - this is something the array can't do
+ sal_uInt16 nCount = std::min( o3tl::narrowing<sal_uInt16>(aSetColumns.getLength()),
+ sal_uInt16(0x3fff) );
+ sal_uInt16 nWidthSum = 0;
+ // #101224# one column is no column
+
+ if(nCount > 1)
+ for(sal_uInt16 i = 0; i < nCount; i++)
+ {
+ SwColumn aCol;
+ aCol.SetWishWidth(pArray[i].Width );
+ nWidthSum = nWidthSum + pArray[i].Width;
+ aCol.SetLeft (o3tl::toTwips(pArray[i].LeftMargin, o3tl::Length::mm100));
+ aCol.SetRight(o3tl::toTwips(pArray[i].RightMargin, o3tl::Length::mm100));
+ m_aColumns.insert(m_aColumns.begin() + i, aCol);
+ }
+ bRet = true;
+ m_nWidth = nWidthSum;
+ m_bOrtho = false;
+
+ if (uno::Reference<beans::XPropertySet> xProps{ xCols, css::uno::UNO_QUERY })
+ {
+ xProps->getPropertyValue(UNO_NAME_IS_AUTOMATIC) >>= m_bOrtho;
+ xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_WIDTH) >>= m_nLineWidth;
+ m_nLineWidth = o3tl::toTwips(m_nLineWidth, o3tl::Length::mm100);
+ xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_COLOR) >>= m_aLineColor;
+ if (sal_Int32 nHeight;
+ xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_RELATIVE_HEIGHT) >>= nHeight)
+ m_nLineHeight = nHeight;
+ switch (xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_STYLE).get<sal_Int16>())
+ {
+ default:
+ case css::text::ColumnSeparatorStyle::NONE:
+ m_eLineStyle = SvxBorderLineStyle::NONE;
+ break;
+ case css::text::ColumnSeparatorStyle::SOLID:
+ m_eLineStyle = SvxBorderLineStyle::SOLID;
+ break;
+ case css::text::ColumnSeparatorStyle::DOTTED:
+ m_eLineStyle = SvxBorderLineStyle::DOTTED;
+ break;
+ case css::text::ColumnSeparatorStyle::DASHED:
+ m_eLineStyle = SvxBorderLineStyle::DASHED;
+ break;
+ }
+ if (!xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_IS_ON).get<bool>())
+ m_eAdj = COLADJ_NONE;
+ else switch (xProps->getPropertyValue(UNO_NAME_SEPARATOR_LINE_VERTIVAL_ALIGNMENT).get<style::VerticalAlignment>())
+ {
+ case style::VerticalAlignment_TOP: m_eAdj = COLADJ_TOP; break;
+ case style::VerticalAlignment_MIDDLE: m_eAdj = COLADJ_CENTER; break;
+ case style::VerticalAlignment_BOTTOM: m_eAdj = COLADJ_BOTTOM; break;
+ default: OSL_ENSURE( false, "unknown alignment" ); break;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+void SwFormatCol::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatCol"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eLineStyle"), BAD_CAST(OString::number(static_cast<sal_Int16>(m_eLineStyle)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLineWidth"), BAD_CAST(OString::number(m_nLineWidth).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aLineColor"), BAD_CAST(m_aLineColor.AsRGBHexString().toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nLineHeight"), BAD_CAST(OString::number(m_nLineHeight).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eAdj"), BAD_CAST(OString::number(m_eAdj).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidth"), BAD_CAST(OString::number(m_nWidth).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nWidthAdjustValue"), BAD_CAST(OString::number(m_aWidthAdjustValue).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bOrtho"), BAD_CAST(OString::boolean(m_bOrtho).getStr()));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("aColumns"));
+ for (const SwColumn& rColumn : m_aColumns)
+ rColumn.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatSurround::SwFormatSurround( css::text::WrapTextMode eFly ) :
+ SfxEnumItem( RES_SURROUND, eFly )
+{
+ m_bAnchorOnly = m_bContour = m_bOutside = false;
+}
+
+bool SwFormatSurround::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( GetValue() == static_cast<const SwFormatSurround&>(rAttr).GetValue() &&
+ m_bAnchorOnly== static_cast<const SwFormatSurround&>(rAttr).m_bAnchorOnly &&
+ m_bContour== static_cast<const SwFormatSurround&>(rAttr).m_bContour &&
+ m_bOutside== static_cast<const SwFormatSurround&>(rAttr).m_bOutside );
+}
+
+SwFormatSurround* SwFormatSurround::Clone( SfxItemPool* ) const
+{
+ return new SwFormatSurround( *this );
+}
+
+sal_uInt16 SwFormatSurround::GetValueCount() const
+{
+ return 6;
+}
+
+bool SwFormatSurround::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_SURROUND_SURROUNDTYPE:
+ rVal <<= GetSurround();
+ break;
+ case MID_SURROUND_ANCHORONLY:
+ rVal <<= IsAnchorOnly();
+ break;
+ case MID_SURROUND_CONTOUR:
+ rVal <<= IsContour();
+ break;
+ case MID_SURROUND_CONTOUROUTSIDE:
+ rVal <<= IsOutside();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatSurround::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_SURROUND_SURROUNDTYPE:
+ {
+ css::text::WrapTextMode eVal = static_cast<css::text::WrapTextMode>(SWUnoHelper::GetEnumAsInt32( rVal ));
+ if( eVal >= css::text::WrapTextMode_NONE && eVal <= css::text::WrapTextMode_RIGHT )
+ SetValue( eVal );
+ else {
+ //exception
+ ;
+ }
+ }
+ break;
+
+ case MID_SURROUND_ANCHORONLY:
+ SetAnchorOnly( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_SURROUND_CONTOUR:
+ SetContour( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_SURROUND_CONTOUROUTSIDE:
+ SetOutside( *o3tl::doAccess<bool>(rVal) );
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatSurround::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatSurround"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(static_cast<sal_Int32>(GetValue())).getStr()));
+
+ OUString aPresentation;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ GetPresentation(SfxItemPresentation::Nameless, MapUnit::Map100thMM, MapUnit::Map100thMM, aPresentation, aIntlWrapper);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr()));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bAnchorOnly"), BAD_CAST(OString::boolean(m_bAnchorOnly).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bContour"), BAD_CAST(OString::boolean(m_bContour).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bOutside"), BAD_CAST(OString::boolean(m_bOutside).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatVertOrient::SwFormatVertOrient( SwTwips nY, sal_Int16 eVert,
+ sal_Int16 eRel )
+ : SfxPoolItem( RES_VERT_ORIENT ),
+ m_nYPos( nY ),
+ m_eOrient( eVert ),
+ m_eRelation( eRel )
+{}
+
+bool SwFormatVertOrient::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_nYPos == static_cast<const SwFormatVertOrient&>(rAttr).m_nYPos &&
+ m_eOrient == static_cast<const SwFormatVertOrient&>(rAttr).m_eOrient &&
+ m_eRelation == static_cast<const SwFormatVertOrient&>(rAttr).m_eRelation );
+}
+
+SwFormatVertOrient* SwFormatVertOrient::Clone( SfxItemPool* ) const
+{
+ return new SwFormatVertOrient( *this );
+}
+
+bool SwFormatVertOrient::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_VERTORIENT_ORIENT:
+ {
+ rVal <<= m_eOrient;
+ }
+ break;
+ case MID_VERTORIENT_RELATION:
+ rVal <<= m_eRelation;
+ break;
+ case MID_VERTORIENT_POSITION:
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetPos()));
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatVertOrient::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_VERTORIENT_ORIENT:
+ {
+ sal_uInt16 nVal = text::VertOrientation::NONE;
+ rVal >>= nVal;
+ m_eOrient = nVal;
+ }
+ break;
+ case MID_VERTORIENT_RELATION:
+ {
+ m_eRelation = lcl_IntToRelation(rVal);
+ }
+ break;
+ case MID_VERTORIENT_POSITION:
+ {
+ sal_Int32 nVal = 0;
+ rVal >>= nVal;
+ if(bConvert)
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetPos( nVal );
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatVertOrient::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatVertOrient"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nYPos"), BAD_CAST(OString::number(m_nYPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eOrient"), BAD_CAST(OString::number(m_eOrient).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eRelation"), BAD_CAST(OString::number(m_eRelation).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatHoriOrient::SwFormatHoriOrient( SwTwips nX, sal_Int16 eHori,
+ sal_Int16 eRel, bool bPos )
+ : SfxPoolItem( RES_HORI_ORIENT ),
+ m_nXPos( nX ),
+ m_eOrient( eHori ),
+ m_eRelation( eRel ),
+ m_bPosToggle( bPos )
+{}
+
+bool SwFormatHoriOrient::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_nXPos == static_cast<const SwFormatHoriOrient&>(rAttr).m_nXPos &&
+ m_eOrient == static_cast<const SwFormatHoriOrient&>(rAttr).m_eOrient &&
+ m_eRelation == static_cast<const SwFormatHoriOrient&>(rAttr).m_eRelation &&
+ m_bPosToggle == static_cast<const SwFormatHoriOrient&>(rAttr).m_bPosToggle );
+}
+
+SwFormatHoriOrient* SwFormatHoriOrient::Clone( SfxItemPool* ) const
+{
+ return new SwFormatHoriOrient( *this );
+}
+
+bool SwFormatHoriOrient::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_HORIORIENT_ORIENT:
+ {
+ rVal <<= m_eOrient;
+ }
+ break;
+ case MID_HORIORIENT_RELATION:
+ rVal <<= m_eRelation;
+ break;
+ case MID_HORIORIENT_POSITION:
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(GetPos()));
+ break;
+ case MID_HORIORIENT_PAGETOGGLE:
+ rVal <<= IsPosToggle();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatHoriOrient::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0 != (nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_HORIORIENT_ORIENT:
+ {
+ sal_Int16 nVal = text::HoriOrientation::NONE;
+ rVal >>= nVal;
+ m_eOrient = nVal;
+ }
+ break;
+ case MID_HORIORIENT_RELATION:
+ {
+ m_eRelation = lcl_IntToRelation(rVal);
+ }
+ break;
+ case MID_HORIORIENT_POSITION:
+ {
+ sal_Int32 nVal = 0;
+ if(!(rVal >>= nVal))
+ bRet = false;
+ if(bConvert)
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ SetPos( nVal );
+ }
+ break;
+ case MID_HORIORIENT_PAGETOGGLE:
+ SetPosToggle( *o3tl::doAccess<bool>(rVal));
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatHoriOrient::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatHoriOrient"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nXPos"), BAD_CAST(OString::number(m_nXPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eOrient"), BAD_CAST(OString::number(m_eOrient).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("eRelation"), BAD_CAST(OString::number(m_eRelation).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("bPosToggle"), BAD_CAST(OString::boolean(m_bPosToggle).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatAnchor::SwFormatAnchor( RndStdIds nRnd, sal_uInt16 nPage )
+ : SfxPoolItem( RES_ANCHOR ),
+ m_eAnchorId( nRnd ),
+ m_nPageNumber( nPage ),
+ // OD 2004-05-05 #i28701# - get always new increased order number
+ m_nOrder( ++s_nOrderCounter )
+{}
+
+SwFormatAnchor::SwFormatAnchor( const SwFormatAnchor &rCpy )
+ : SfxPoolItem( RES_ANCHOR )
+ , m_pContentAnchor( (rCpy.GetContentAnchor())
+ ? new SwPosition( *rCpy.GetContentAnchor() ) : nullptr )
+ , m_eAnchorId( rCpy.GetAnchorId() )
+ , m_nPageNumber( rCpy.GetPageNum() )
+ // OD 2004-05-05 #i28701# - get always new increased order number
+ , m_nOrder( ++s_nOrderCounter )
+{
+}
+
+SwFormatAnchor::~SwFormatAnchor()
+{
+}
+
+void SwFormatAnchor::SetAnchor( const SwPosition *pPos )
+{
+ // anchor only to paragraphs, or start nodes in case of RndStdIds::FLY_AT_FLY
+ // also allow table node, this is used when a table is selected and is converted to a frame by the UI
+ assert(!pPos
+ || ((RndStdIds::FLY_AT_FLY == m_eAnchorId) &&
+ dynamic_cast<SwStartNode*>(&pPos->nNode.GetNode()))
+ || (RndStdIds::FLY_AT_PARA == m_eAnchorId && dynamic_cast<SwTableNode*>(&pPos->nNode.GetNode()))
+ || dynamic_cast<SwTextNode*>(&pPos->nNode.GetNode()));
+ m_pContentAnchor .reset( pPos ? new SwPosition( *pPos ) : nullptr );
+ // Flys anchored AT paragraph should not point into the paragraph content
+ if (m_pContentAnchor &&
+ ((RndStdIds::FLY_AT_PARA == m_eAnchorId) || (RndStdIds::FLY_AT_FLY == m_eAnchorId)))
+ {
+ m_pContentAnchor->nContent.Assign( nullptr, 0 );
+ }
+}
+
+SwFormatAnchor& SwFormatAnchor::operator=(const SwFormatAnchor& rAnchor)
+{
+ if (this != &rAnchor)
+ {
+ m_eAnchorId = rAnchor.GetAnchorId();
+ m_nPageNumber = rAnchor.GetPageNum();
+ // OD 2004-05-05 #i28701# - get always new increased order number
+ m_nOrder = ++s_nOrderCounter;
+
+ m_pContentAnchor.reset( (rAnchor.GetContentAnchor())
+ ? new SwPosition(*(rAnchor.GetContentAnchor()))
+ : nullptr );
+ }
+ return *this;
+}
+
+bool SwFormatAnchor::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ SwFormatAnchor const& rFormatAnchor(static_cast<SwFormatAnchor const&>(rAttr));
+ // OD 2004-05-05 #i28701# - Note: <mnOrder> hasn't to be considered.
+ return ( m_eAnchorId == rFormatAnchor.GetAnchorId() &&
+ m_nPageNumber == rFormatAnchor.GetPageNum() &&
+ // compare anchor: either both do not point into a textnode or
+ // both do (valid m_pContentAnchor) and the positions are equal
+ ((m_pContentAnchor.get() == rFormatAnchor.m_pContentAnchor.get()) ||
+ (m_pContentAnchor && rFormatAnchor.GetContentAnchor() &&
+ (*m_pContentAnchor == *rFormatAnchor.GetContentAnchor()))));
+}
+
+SwFormatAnchor* SwFormatAnchor::Clone( SfxItemPool* ) const
+{
+ return new SwFormatAnchor( *this );
+}
+
+// OD 2004-05-05 #i28701#
+sal_uInt32 SwFormatAnchor::s_nOrderCounter = 0;
+
+// OD 2004-05-05 #i28701#
+
+bool SwFormatAnchor::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+
+ text::TextContentAnchorType eRet;
+ switch (GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_CHAR:
+ eRet = text::TextContentAnchorType_AT_CHARACTER;
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ eRet = text::TextContentAnchorType_AT_PAGE;
+ break;
+ case RndStdIds::FLY_AT_FLY:
+ eRet = text::TextContentAnchorType_AT_FRAME;
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ eRet = text::TextContentAnchorType_AS_CHARACTER;
+ break;
+ //case RndStdIds::FLY_AT_PARA:
+ default:
+ eRet = text::TextContentAnchorType_AT_PARAGRAPH;
+ }
+ rVal <<= eRet;
+ break;
+ case MID_ANCHOR_PAGENUM:
+ rVal <<= static_cast<sal_Int16>(GetPageNum());
+ break;
+ case MID_ANCHOR_ANCHORFRAME:
+ {
+ if (m_pContentAnchor && RndStdIds::FLY_AT_FLY == m_eAnchorId)
+ {
+ SwFrameFormat* pFormat = m_pContentAnchor->nNode.GetNode().GetFlyFormat();
+ if(pFormat)
+ {
+ uno::Reference<text::XTextFrame> const xRet(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat));
+ rVal <<= xRet;
+ }
+ }
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatAnchor::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_ANCHOR_ANCHORTYPE:
+ {
+ RndStdIds eAnchor;
+ switch( static_cast<text::TextContentAnchorType>(SWUnoHelper::GetEnumAsInt32( rVal )) )
+ {
+ case text::TextContentAnchorType_AS_CHARACTER:
+ eAnchor = RndStdIds::FLY_AS_CHAR;
+ break;
+ case text::TextContentAnchorType_AT_PAGE:
+ eAnchor = RndStdIds::FLY_AT_PAGE;
+ if( GetPageNum() > 0 )
+ {
+ // If the anchor type is page and a valid page number
+ // has been set, the content position isn't required
+ // any longer.
+ m_pContentAnchor.reset();
+ }
+ break;
+ case text::TextContentAnchorType_AT_FRAME:
+ eAnchor = RndStdIds::FLY_AT_FLY;
+ break;
+ case text::TextContentAnchorType_AT_CHARACTER:
+ eAnchor = RndStdIds::FLY_AT_CHAR;
+ break;
+ //case text::TextContentAnchorType_AT_PARAGRAPH:
+ default:
+ eAnchor = RndStdIds::FLY_AT_PARA;
+ break;
+ }
+ SetType( eAnchor );
+ }
+ break;
+ case MID_ANCHOR_PAGENUM:
+ {
+ sal_Int16 nVal = 0;
+ if((rVal >>= nVal) && nVal > 0)
+ {
+ SetPageNum( nVal );
+ if (RndStdIds::FLY_AT_PAGE == GetAnchorId())
+ {
+ // If the anchor type is page and a valid page number
+ // is set, the content position has to be deleted to not
+ // confuse the layout (frmtool.cxx). However, if the
+ // anchor type is not page, any content position will
+ // be kept.
+ m_pContentAnchor.reset();
+ }
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_ANCHOR_ANCHORFRAME:
+ //no break here!;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+void SwFormatAnchor::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatAnchor"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ if (m_pContentAnchor)
+ {
+ std::stringstream aContentAnchor;
+ aContentAnchor << *m_pContentAnchor;
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_pContentAnchor"), BAD_CAST(aContentAnchor.str().c_str()));
+ }
+ else
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pContentAnchor"), "%p", m_pContentAnchor.get());
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eAnchorType"), BAD_CAST(OString::number(static_cast<int>(m_eAnchorId)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nPageNumber"), BAD_CAST(OString::number(m_nPageNumber).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nOrder"), BAD_CAST(OString::number(m_nOrder).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("s_nOrderCounter"), BAD_CAST(OString::number(s_nOrderCounter).getStr()));
+ OUString aPresentation;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ GetPresentation(SfxItemPresentation::Nameless, MapUnit::Map100thMM, MapUnit::Map100thMM, aPresentation, aIntlWrapper);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// Partially implemented inline in hxx
+SwFormatURL::SwFormatURL() :
+ SfxPoolItem( RES_URL ),
+ m_bIsServerMap( false )
+{
+}
+
+SwFormatURL::SwFormatURL( const SwFormatURL &rURL) :
+ SfxPoolItem( RES_URL ),
+ m_sTargetFrameName( rURL.GetTargetFrameName() ),
+ m_sURL( rURL.GetURL() ),
+ m_sName( rURL.GetName() ),
+ m_bIsServerMap( rURL.IsServerMap() )
+{
+ if (rURL.GetMap())
+ m_pMap.reset( new ImageMap( *rURL.GetMap() ) );
+}
+
+SwFormatURL::~SwFormatURL()
+{
+}
+
+bool SwFormatURL::operator==( const SfxPoolItem &rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SwFormatURL &rCmp = static_cast<const SwFormatURL&>(rAttr);
+ bool bRet = m_bIsServerMap == rCmp.IsServerMap() &&
+ m_sURL == rCmp.GetURL() &&
+ m_sTargetFrameName == rCmp.GetTargetFrameName() &&
+ m_sName == rCmp.GetName();
+ if ( bRet )
+ {
+ if ( m_pMap && rCmp.GetMap() )
+ bRet = *m_pMap == *rCmp.GetMap();
+ else
+ bRet = m_pMap.get() == rCmp.GetMap();
+ }
+ return bRet;
+}
+
+SwFormatURL* SwFormatURL::Clone( SfxItemPool* ) const
+{
+ return new SwFormatURL( *this );
+}
+
+void SwFormatURL::SetURL(const OUString &rURL, bool bServerMap)
+{
+ m_sURL = rURL;
+ m_bIsServerMap = bServerMap;
+}
+
+void SwFormatURL::SetMap( const ImageMap *pM )
+{
+ m_pMap.reset( pM ? new ImageMap( *pM ) : nullptr);
+}
+
+bool SwFormatURL::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_URL_URL:
+ rVal <<= GetURL();
+ break;
+ case MID_URL_TARGET:
+ rVal <<= GetTargetFrameName();
+ break;
+ case MID_URL_HYPERLINKNAME:
+ rVal <<= GetName();
+ break;
+ case MID_URL_CLIENTMAP:
+ {
+ uno::Reference< uno::XInterface > xInt;
+ if(m_pMap)
+ {
+ xInt = SvUnoImageMap_createInstance( *m_pMap, sw_GetSupportedMacroItems() );
+ }
+ else
+ {
+ ImageMap aEmptyMap;
+ xInt = SvUnoImageMap_createInstance( aEmptyMap, sw_GetSupportedMacroItems() );
+ }
+ uno::Reference< container::XIndexContainer > xCont(xInt, uno::UNO_QUERY);
+ rVal <<= xCont;
+ }
+ break;
+ case MID_URL_SERVERMAP:
+ rVal <<= IsServerMap();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatURL::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_URL_URL:
+ {
+ OUString sTmp;
+ rVal >>= sTmp;
+ SetURL( sTmp, m_bIsServerMap );
+ }
+ break;
+ case MID_URL_TARGET:
+ {
+ OUString sTmp;
+ rVal >>= sTmp;
+ SetTargetFrameName( sTmp );
+ }
+ break;
+ case MID_URL_HYPERLINKNAME:
+ {
+ OUString sTmp;
+ rVal >>= sTmp;
+ SetName( sTmp );
+ }
+ break;
+ case MID_URL_CLIENTMAP:
+ {
+ uno::Reference<container::XIndexContainer> xCont;
+ if(!rVal.hasValue())
+ m_pMap.reset();
+ else if(rVal >>= xCont)
+ {
+ if(!m_pMap)
+ m_pMap.reset(new ImageMap);
+ bRet = SvUnoImageMap_fillImageMap( xCont, *m_pMap );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_URL_SERVERMAP:
+ m_bIsServerMap = *o3tl::doAccess<bool>(rVal);
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwFormatEditInReadonly* SwFormatEditInReadonly::Clone( SfxItemPool* ) const
+{
+ return new SwFormatEditInReadonly( *this );
+}
+
+SwFormatLayoutSplit* SwFormatLayoutSplit::Clone( SfxItemPool* ) const
+{
+ return new SwFormatLayoutSplit( *this );
+}
+
+SwFormatRowSplit* SwFormatRowSplit::Clone( SfxItemPool* ) const
+{
+ return new SwFormatRowSplit( *this );
+}
+
+SwFormatNoBalancedColumns* SwFormatNoBalancedColumns::Clone( SfxItemPool* ) const
+{
+ return new SwFormatNoBalancedColumns( *this );
+}
+
+void SwFormatNoBalancedColumns::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatNoBalancedColumns"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SwFormatFootnoteEndAtTextEnd
+
+sal_uInt16 SwFormatFootnoteEndAtTextEnd::GetValueCount() const
+{
+ return sal_uInt16( FTNEND_ATTXTEND_END );
+}
+
+SwFormatFootnoteEndAtTextEnd& SwFormatFootnoteEndAtTextEnd::operator=(
+ const SwFormatFootnoteEndAtTextEnd& rAttr )
+{
+ SfxEnumItem::SetValue( rAttr.GetValue() );
+ m_aFormat = rAttr.m_aFormat;
+ m_nOffset = rAttr.m_nOffset;
+ m_sPrefix = rAttr.m_sPrefix;
+ m_sSuffix = rAttr.m_sSuffix;
+ return *this;
+}
+
+bool SwFormatFootnoteEndAtTextEnd::operator==( const SfxPoolItem& rItem ) const
+{
+ const SwFormatFootnoteEndAtTextEnd& rAttr = static_cast<const SwFormatFootnoteEndAtTextEnd&>(rItem);
+ return SfxEnumItem::operator==( rItem ) &&
+ m_aFormat.GetNumberingType() == rAttr.m_aFormat.GetNumberingType() &&
+ m_nOffset == rAttr.m_nOffset &&
+ m_sPrefix == rAttr.m_sPrefix &&
+ m_sSuffix == rAttr.m_sSuffix;
+}
+
+bool SwFormatFootnoteEndAtTextEnd::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_COLLECT :
+ rVal <<= GetValue() >= FTNEND_ATTXTEND;
+ break;
+ case MID_RESTART_NUM :
+ rVal <<= GetValue() >= FTNEND_ATTXTEND_OWNNUMSEQ;
+ break;
+ case MID_NUM_START_AT: rVal <<= static_cast<sal_Int16>(m_nOffset); break;
+ case MID_OWN_NUM :
+ rVal <<= GetValue() >= FTNEND_ATTXTEND_OWNNUMANDFMT;
+ break;
+ case MID_NUM_TYPE : rVal <<= static_cast<sal_Int16>(m_aFormat.GetNumberingType()); break;
+ case MID_PREFIX : rVal <<= m_sPrefix; break;
+ case MID_SUFFIX : rVal <<= m_sSuffix; break;
+ default: return false;
+ }
+ return true;
+}
+
+bool SwFormatFootnoteEndAtTextEnd::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_COLLECT :
+ {
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ if(!bVal && GetValue() >= FTNEND_ATTXTEND)
+ SetValue(FTNEND_ATPGORDOCEND);
+ else if(bVal && GetValue() < FTNEND_ATTXTEND)
+ SetValue(FTNEND_ATTXTEND);
+ }
+ break;
+ case MID_RESTART_NUM :
+ {
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ if(!bVal && GetValue() >= FTNEND_ATTXTEND_OWNNUMSEQ)
+ SetValue(FTNEND_ATTXTEND);
+ else if(bVal && GetValue() < FTNEND_ATTXTEND_OWNNUMSEQ)
+ SetValue(FTNEND_ATTXTEND_OWNNUMSEQ);
+ }
+ break;
+ case MID_NUM_START_AT:
+ {
+ sal_Int16 nVal = 0;
+ rVal >>= nVal;
+ if(nVal >= 0)
+ m_nOffset = nVal;
+ else
+ bRet = false;
+ }
+ break;
+ case MID_OWN_NUM :
+ {
+ bool bVal = *o3tl::doAccess<bool>(rVal);
+ if(!bVal && GetValue() >= FTNEND_ATTXTEND_OWNNUMANDFMT)
+ SetValue(FTNEND_ATTXTEND_OWNNUMSEQ);
+ else if(bVal && GetValue() < FTNEND_ATTXTEND_OWNNUMANDFMT)
+ SetValue(FTNEND_ATTXTEND_OWNNUMANDFMT);
+ }
+ break;
+ case MID_NUM_TYPE :
+ {
+ sal_Int16 nVal = 0;
+ rVal >>= nVal;
+ if(nVal >= 0 &&
+ (nVal <= SVX_NUM_ARABIC ||
+ SVX_NUM_CHARS_UPPER_LETTER_N == nVal ||
+ SVX_NUM_CHARS_LOWER_LETTER_N == nVal ))
+ m_aFormat.SetNumberingType(static_cast<SvxNumType>(nVal));
+ else
+ bRet = false;
+ }
+ break;
+ case MID_PREFIX :
+ {
+ OUString sVal; rVal >>= sVal;
+ m_sPrefix = sVal;
+ }
+ break;
+ case MID_SUFFIX :
+ {
+ OUString sVal; rVal >>= sVal;
+ m_sSuffix = sVal;
+ }
+ break;
+ default: bRet = false;
+ }
+ return bRet;
+}
+
+// class SwFormatFootnoteAtTextEnd
+
+SwFormatFootnoteAtTextEnd* SwFormatFootnoteAtTextEnd::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFootnoteAtTextEnd(*this);
+}
+
+// class SwFormatEndAtTextEnd
+
+SwFormatEndAtTextEnd* SwFormatEndAtTextEnd::Clone( SfxItemPool* ) const
+{
+ return new SwFormatEndAtTextEnd(*this);
+}
+
+//class SwFormatChain
+
+bool SwFormatChain::operator==( const SfxPoolItem &rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return GetPrev() == static_cast<const SwFormatChain&>(rAttr).GetPrev() &&
+ GetNext() == static_cast<const SwFormatChain&>(rAttr).GetNext();
+}
+
+SwFormatChain::SwFormatChain( const SwFormatChain &rCpy ) :
+ SfxPoolItem( RES_CHAIN )
+{
+ SetPrev( rCpy.GetPrev() );
+ SetNext( rCpy.GetNext() );
+}
+
+SwFormatChain* SwFormatChain::Clone( SfxItemPool* ) const
+{
+ SwFormatChain *pRet = new SwFormatChain;
+ pRet->SetPrev( GetPrev() );
+ pRet->SetNext( GetNext() );
+ return pRet;
+}
+
+void SwFormatChain::SetPrev( SwFlyFrameFormat *pFormat )
+{
+ if ( pFormat )
+ pFormat->Add( &m_aPrev );
+ else
+ m_aPrev.EndListeningAll();
+}
+
+void SwFormatChain::SetNext( SwFlyFrameFormat *pFormat )
+{
+ if ( pFormat )
+ pFormat->Add( &m_aNext );
+ else
+ m_aNext.EndListeningAll();
+}
+
+bool SwFormatChain::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ OUString aRet;
+ switch ( nMemberId )
+ {
+ case MID_CHAIN_PREVNAME:
+ if ( GetPrev() )
+ aRet = GetPrev()->GetName();
+ break;
+ case MID_CHAIN_NEXTNAME:
+ if ( GetNext() )
+ aRet = GetNext()->GetName();
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ rVal <<= aRet;
+ return bRet;
+}
+
+SwFormatLineNumber::SwFormatLineNumber() :
+ SfxPoolItem( RES_LINENUMBER )
+{
+ m_nStartValue = 0;
+ m_bCountLines = true;
+}
+
+SwFormatLineNumber::~SwFormatLineNumber()
+{
+}
+
+bool SwFormatLineNumber::operator==( const SfxPoolItem &rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return m_nStartValue == static_cast<const SwFormatLineNumber&>(rAttr).GetStartValue() &&
+ m_bCountLines == static_cast<const SwFormatLineNumber&>(rAttr).IsCount();
+}
+
+SwFormatLineNumber* SwFormatLineNumber::Clone( SfxItemPool* ) const
+{
+ return new SwFormatLineNumber( *this );
+}
+
+bool SwFormatLineNumber::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_LINENUMBER_COUNT:
+ rVal <<= IsCount();
+ break;
+ case MID_LINENUMBER_STARTVALUE:
+ rVal <<= static_cast<sal_Int32>(GetStartValue());
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool SwFormatLineNumber::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ // here we convert always!
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = true;
+ switch ( nMemberId )
+ {
+ case MID_LINENUMBER_COUNT:
+ SetCountLines( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_LINENUMBER_STARTVALUE:
+ {
+ sal_Int32 nVal = 0;
+ if(rVal >>= nVal)
+ SetStartValue( nVal );
+ else
+ bRet = false;
+ }
+ break;
+ default:
+ OSL_ENSURE( false, "unknown MemberId" );
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwTextGridItem::SwTextGridItem()
+ : SfxPoolItem( RES_TEXTGRID ), m_aColor( COL_LIGHTGRAY ), m_nLines( 20 )
+ , m_nBaseHeight( 400 ), m_nRubyHeight( 200 ), m_eGridType( GRID_NONE )
+ , m_bRubyTextBelow( false ), m_bPrintGrid( true ), m_bDisplayGrid( true )
+ , m_nBaseWidth(400), m_bSnapToChars( true ), m_bSquaredMode(true)
+{
+}
+
+SwTextGridItem::~SwTextGridItem()
+{
+}
+
+bool SwTextGridItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ SwTextGridItem const& rOther(static_cast<SwTextGridItem const&>(rAttr));
+ return m_eGridType == rOther.GetGridType()
+ && m_nLines == rOther.GetLines()
+ && m_nBaseHeight == rOther.GetBaseHeight()
+ && m_nRubyHeight == rOther.GetRubyHeight()
+ && m_bRubyTextBelow == rOther.GetRubyTextBelow()
+ && m_bDisplayGrid == rOther.GetDisplayGrid()
+ && m_bPrintGrid == rOther.GetPrintGrid()
+ && m_aColor == rOther.GetColor()
+ && m_nBaseWidth == rOther.GetBaseWidth()
+ && m_bSnapToChars == rOther.GetSnapToChars()
+ && m_bSquaredMode == rOther.GetSquaredMode();
+}
+
+SwTextGridItem* SwTextGridItem::Clone( SfxItemPool* ) const
+{
+ return new SwTextGridItem( *this );
+}
+
+bool SwTextGridItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+
+ switch( nMemberId & ~CONVERT_TWIPS )
+ {
+ case MID_GRID_COLOR:
+ rVal <<= GetColor();
+ break;
+ case MID_GRID_LINES:
+ rVal <<= GetLines();
+ break;
+ case MID_GRID_RUBY_BELOW:
+ rVal <<= m_bRubyTextBelow;
+ break;
+ case MID_GRID_PRINT:
+ rVal <<= m_bPrintGrid;
+ break;
+ case MID_GRID_DISPLAY:
+ rVal <<= m_bDisplayGrid;
+ break;
+ case MID_GRID_BASEHEIGHT:
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_nBaseHeight));
+ break;
+ case MID_GRID_BASEWIDTH:
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_nBaseWidth));
+ break;
+ case MID_GRID_RUBYHEIGHT:
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ rVal <<= static_cast<sal_Int32>(convertTwipToMm100(m_nRubyHeight));
+ break;
+ case MID_GRID_TYPE:
+ switch( GetGridType() )
+ {
+ case GRID_NONE:
+ rVal <<= text::TextGridMode::NONE;
+ break;
+ case GRID_LINES_ONLY:
+ rVal <<= text::TextGridMode::LINES;
+ break;
+ case GRID_LINES_CHARS:
+ rVal <<= text::TextGridMode::LINES_AND_CHARS;
+ break;
+ default:
+ OSL_FAIL("unknown SwTextGrid value");
+ bRet = false;
+ break;
+ }
+ break;
+ case MID_GRID_SNAPTOCHARS:
+ rVal <<= m_bSnapToChars;
+ break;
+ case MID_GRID_STANDARD_MODE:
+ rVal <<= !m_bSquaredMode;
+ break;
+ default:
+ OSL_FAIL("Unknown SwTextGridItem member");
+ bRet = false;
+ break;
+ }
+
+ return bRet;
+}
+
+bool SwTextGridItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ switch( nMemberId & ~CONVERT_TWIPS )
+ {
+ case MID_GRID_COLOR:
+ {
+ Color nTmp;
+ bRet = (rVal >>= nTmp);
+ if( bRet )
+ SetColor( nTmp );
+ }
+ break;
+ case MID_GRID_LINES:
+ {
+ sal_Int16 nTmp = 0;
+ bRet = (rVal >>= nTmp);
+ if( bRet && (nTmp >= 0) )
+ SetLines( o3tl::narrowing<sal_uInt16>(nTmp) );
+ else
+ bRet = false;
+ }
+ break;
+ case MID_GRID_RUBY_BELOW:
+ SetRubyTextBelow( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_PRINT:
+ SetPrintGrid( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_DISPLAY:
+ SetDisplayGrid( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_BASEHEIGHT:
+ case MID_GRID_BASEWIDTH:
+ case MID_GRID_RUBYHEIGHT:
+ {
+ OSL_ENSURE( (nMemberId & CONVERT_TWIPS) != 0,
+ "This value needs TWIPS-MM100 conversion" );
+ sal_Int32 nTmp = 0;
+ bRet = (rVal >>= nTmp);
+ nTmp = o3tl::toTwips(nTmp, o3tl::Length::mm100);
+ if( bRet && (nTmp >= 0) && ( nTmp <= SAL_MAX_UINT16) )
+ {
+ // rhbz#1043551 round up to 5pt -- 0 causes divide-by-zero
+ // in layout; 1pt ties the painting code up in knots for
+ // minutes with bazillion lines...
+#define MIN_TEXTGRID_SIZE 100
+ if( (nMemberId & ~CONVERT_TWIPS) == MID_GRID_BASEHEIGHT )
+ {
+ nTmp = std::max<sal_Int32>(nTmp, MIN_TEXTGRID_SIZE);
+ SetBaseHeight( o3tl::narrowing<sal_uInt16>(nTmp) );
+ }
+ else if( (nMemberId & ~CONVERT_TWIPS) == MID_GRID_BASEWIDTH )
+ {
+ nTmp = std::max<sal_Int32>(nTmp, MIN_TEXTGRID_SIZE);
+ SetBaseWidth( o3tl::narrowing<sal_uInt16>(nTmp) );
+ }
+ else
+ SetRubyHeight( o3tl::narrowing<sal_uInt16>(nTmp) );
+ }
+ else
+ bRet = false;
+ }
+ break;
+ case MID_GRID_TYPE:
+ {
+ sal_Int16 nTmp = 0;
+ bRet = (rVal >>= nTmp);
+ if( bRet )
+ {
+ switch( nTmp )
+ {
+ case text::TextGridMode::NONE:
+ SetGridType( GRID_NONE );
+ break;
+ case text::TextGridMode::LINES:
+ SetGridType( GRID_LINES_ONLY );
+ break;
+ case text::TextGridMode::LINES_AND_CHARS:
+ SetGridType( GRID_LINES_CHARS );
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+ }
+ break;
+ }
+ case MID_GRID_SNAPTOCHARS:
+ SetSnapToChars( *o3tl::doAccess<bool>(rVal) );
+ break;
+ case MID_GRID_STANDARD_MODE:
+ {
+ bool bStandard = *o3tl::doAccess<bool>(rVal);
+ SetSquaredMode( !bStandard );
+ break;
+ }
+ default:
+ OSL_FAIL("Unknown SwTextGridItem member");
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+void SwTextGridItem::SwitchPaperMode(bool bNew)
+{
+ if (bNew == m_bSquaredMode)
+ {
+ //same paper mode, not switch
+ return;
+ }
+
+ // use default value when grid is disable
+ if (m_eGridType == GRID_NONE)
+ {
+ m_bSquaredMode = bNew;
+ Init();
+ return;
+ }
+
+ if (m_bSquaredMode)
+ {
+ //switch from "squared mode" to "standard mode"
+ m_nBaseWidth = m_nBaseHeight;
+ m_nBaseHeight = m_nBaseHeight + m_nRubyHeight;
+ m_nRubyHeight = 0;
+ }
+ else
+ {
+ //switch from "standard mode" to "squared mode"
+ m_nRubyHeight = m_nBaseHeight/3;
+ m_nBaseHeight = m_nBaseHeight - m_nRubyHeight;
+ m_nBaseWidth = m_nBaseHeight;
+ }
+ m_bSquaredMode = !m_bSquaredMode;
+}
+
+void SwTextGridItem::Init()
+{
+ if (m_bSquaredMode)
+ {
+ m_nLines = 20;
+ m_nBaseHeight = 400;
+ m_nRubyHeight = 200;
+ m_eGridType = GRID_NONE;
+ m_bRubyTextBelow = false;
+ m_bPrintGrid = true;
+ m_bDisplayGrid = true;
+ m_bSnapToChars = true;
+ m_nBaseWidth = 400;
+ }
+ else
+ {
+ m_nLines = 44;
+ m_nBaseHeight = 312;
+ m_nRubyHeight = 0;
+ m_eGridType = GRID_NONE;
+ m_bRubyTextBelow = false;
+ m_bPrintGrid = true;
+ m_bDisplayGrid = true;
+ m_nBaseWidth = 210;
+ m_bSnapToChars = true;
+ }
+}
+
+SwHeaderAndFooterEatSpacingItem* SwHeaderAndFooterEatSpacingItem::Clone( SfxItemPool* ) const
+{
+ return new SwHeaderAndFooterEatSpacingItem( Which(), GetValue() );
+}
+
+SwFrameFormat::SwFrameFormat(
+ SwAttrPool& rPool,
+ const char* pFormatNm,
+ SwFrameFormat *pDrvdFrame,
+ sal_uInt16 nFormatWhich,
+ const WhichRangesContainer& pWhichRange)
+: SwFormat(rPool, pFormatNm, pWhichRange, pDrvdFrame, nFormatWhich),
+ m_ffList(nullptr)
+{
+}
+
+SwFrameFormat::SwFrameFormat(
+ SwAttrPool& rPool,
+ const OUString &rFormatNm,
+ SwFrameFormat *pDrvdFrame,
+ sal_uInt16 nFormatWhich,
+ const WhichRangesContainer& pWhichRange)
+: SwFormat(rPool, rFormatNm, pWhichRange, pDrvdFrame, nFormatWhich),
+ m_ffList(nullptr)
+{
+}
+
+SwFrameFormat::~SwFrameFormat()
+{
+ if( !GetDoc()->IsInDtor())
+ {
+ const SwFormatAnchor& rAnchor = GetAnchor();
+ if (rAnchor.GetContentAnchor() != nullptr)
+ {
+ rAnchor.GetContentAnchor()->nNode.GetNode().RemoveAnchoredFly(this);
+ }
+ }
+
+ // Check if there any textboxes attached to this format.
+ if( nullptr == m_pOtherTextBoxFormats )
+ return;
+
+ // This is a fly-frame-format just delete this
+ // textbox entry from the textbox collection.
+ // Note: Do not delete it from the doc, as that
+ // is already in progress.
+ if (Which() == RES_FLYFRMFMT)
+ m_pOtherTextBoxFormats->DelTextBox(this);
+
+ // This is a draw-frame-format what belongs to
+ // a shape with textbox(es). Delete all of them.
+ if (Which() == RES_DRAWFRMFMT)
+ m_pOtherTextBoxFormats->ClearAll();
+
+ // Release the pointer.
+ m_pOtherTextBoxFormats.reset();
+}
+
+void SwFrameFormat::SetName( const OUString& rNewName, bool bBroadcast )
+{
+ if (m_ffList != nullptr) {
+ SwFrameFormats::iterator it = m_ffList->find( this );
+ assert( m_ffList->end() != it );
+ SAL_INFO_IF(m_aFormatName == rNewName, "sw.core", "SwFrmFmt not really renamed, as both names are equal");
+
+ const SwStringMsgPoolItem aOld( RES_NAME_CHANGED, m_aFormatName );
+ // As it's a non-unique list, rename should never fail!
+ bool const renamed =
+ m_ffList->m_PosIndex.modify( it,
+ change_name( rNewName ), change_name( m_aFormatName ) );
+ assert(renamed);
+ (void)renamed; // unused in NDEBUG
+ if (bBroadcast) {
+ const SwStringMsgPoolItem aNew( RES_NAME_CHANGED, rNewName );
+ GetNotifier().Broadcast(sw::LegacyModifyHint( &aOld, &aNew ));
+ }
+ }
+ else
+ SwFormat::SetName( rNewName, bBroadcast );
+}
+
+bool SwFrameFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return true;
+}
+
+void SwFrameFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nNewWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
+ const SwAttrSetChg* pNewAttrSetChg = nullptr;
+ const SwFormatHeader* pH = nullptr;
+ const SwFormatFooter* pF = nullptr;
+ const SwPosition* pNewAnchorPosition = nullptr;
+ switch(nNewWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ pNewAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ pH = pNewAttrSetChg->GetChgSet()->GetItem(RES_HEADER, false);
+ pF = pNewAttrSetChg->GetChgSet()->GetItem(RES_FOOTER, false);
+
+ // reset fill information
+ if(maFillAttributes && supportsFullDrawingLayerFillAttributeSet())
+ {
+ SfxItemIter aIter(*pNewAttrSetChg->GetChgSet());
+ for(const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if(!IsInvalidItem(pItem) && pItem->Which() >= XATTR_FILL_FIRST && pItem->Which() <= XATTR_FILL_LAST)
+ {
+ maFillAttributes.reset();
+ break;
+ }
+ }
+ }
+ const SwFormatAnchor* pAnchor = pNewAttrSetChg->GetChgSet()->GetItem(RES_ANCHOR, false);
+ if(pAnchor)
+ {
+ pNewAnchorPosition = pAnchor->GetContentAnchor();
+ assert(pNewAnchorPosition == nullptr || // style's set must not contain position!
+ pNewAttrSetChg->GetTheChgdSet() == &m_aSet);
+ }
+ break;
+ }
+ case RES_FMT_CHG:
+ {
+ // reset fill information on format change (e.g. style changed)
+ if(maFillAttributes && supportsFullDrawingLayerFillAttributeSet())
+ maFillAttributes.reset();
+ break;
+ }
+ case RES_HEADER:
+ pH = static_cast<const SwFormatHeader*>(pLegacy->m_pNew);
+ break;
+ case RES_FOOTER:
+ pF = static_cast<const SwFormatFooter*>(pLegacy->m_pNew);
+ break;
+ case RES_ANCHOR:
+ pNewAnchorPosition = static_cast<const SwFormatAnchor*>(pLegacy->m_pNew)->GetContentAnchor();
+ break;
+ }
+ const sal_uInt16 nOldWhich = pLegacy->m_pOld ? pLegacy->m_pOld->Which() : 0;
+ const SwPosition* pOldAnchorPosition = nullptr;
+ switch(nOldWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ const SwAttrSetChg* pOldAttrSetChg = nullptr;
+ pOldAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ const SwFormatAnchor* pAnchor = pOldAttrSetChg->GetChgSet()->GetItem(RES_ANCHOR, false);
+ if(pAnchor)
+ {
+ pOldAnchorPosition = pAnchor->GetContentAnchor();
+ assert(pOldAnchorPosition == nullptr || // style's set must not contain position!
+ pOldAttrSetChg->GetTheChgdSet() == &m_aSet);
+ }
+ break;
+ }
+ case RES_ANCHOR:
+ pOldAnchorPosition = static_cast<const SwFormatAnchor*>(pLegacy->m_pOld)->GetContentAnchor();
+ break;
+ case RES_REMOVE_UNO_OBJECT:
+ SetXObject(uno::Reference<uno::XInterface>(nullptr));
+ break;
+ }
+
+ assert(nOldWhich == nNewWhich || !nOldWhich || !nNewWhich);
+ if(pH && pH->IsActive() && !pH->GetHeaderFormat())
+ { //If he doesn't have one, I'll add one
+ SwFrameFormat* pFormat = GetDoc()->getIDocumentLayoutAccess().MakeLayoutFormat(RndStdIds::HEADER, nullptr);
+ const_cast<SwFormatHeader*>(pH)->RegisterToFormat(*pFormat);
+ }
+ if(pF && pF->IsActive() && !pF->GetFooterFormat())
+ { //If he doesn't have one, I'll add one
+ SwFrameFormat* pFormat = GetDoc()->getIDocumentLayoutAccess().MakeLayoutFormat(RndStdIds::FOOTER, nullptr);
+ const_cast<SwFormatFooter*>(pF)->RegisterToFormat(*pFormat);
+ }
+ SwFormat::SwClientNotify(rMod, rHint);
+ if(pOldAnchorPosition != nullptr && (pNewAnchorPosition == nullptr || pOldAnchorPosition->nNode.GetIndex() != pNewAnchorPosition->nNode.GetIndex()))
+ pOldAnchorPosition->nNode.GetNode().RemoveAnchoredFly(this);
+ if(pNewAnchorPosition != nullptr && (pOldAnchorPosition == nullptr || pOldAnchorPosition->nNode.GetIndex() != pNewAnchorPosition->nNode.GetIndex()))
+ pNewAnchorPosition->nNode.GetNode().AddAnchoredFly(this);
+}
+
+void SwFrameFormat::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+/// Delete all Frames that are registered in aDepend.
+void SwFrameFormat::DelFrames()
+{
+ SwIterator<SwFrame,SwFormat> aIter( *this );
+ SwFrame * pLast = aIter.First();
+ if( pLast )
+ do {
+ pLast->Cut();
+ SwFrame::DestroyFrame(pLast);
+ } while( nullptr != ( pLast = aIter.Next() ));
+}
+
+void SwFrameFormat::MakeFrames()
+{
+ assert(false); // unimplemented in base class
+}
+
+SwRect SwFrameFormat::FindLayoutRect( const bool bPrtArea, const Point* pPoint ) const
+{
+ SwRect aRet;
+ SwFrame *pFrame = nullptr;
+ if( auto pSectionFormat = dynamic_cast<const SwSectionFormat*>( this ))
+ {
+ // get the Frame using Node2Layout
+ const SwSectionNode* pSectNd = pSectionFormat->GetSectionNode();
+ if( pSectNd )
+ {
+ SwNode2Layout aTmp( *pSectNd, pSectNd->GetIndex() - 1 );
+ pFrame = aTmp.NextFrame();
+
+ if( pFrame && !pFrame->KnowsFormat(*this) )
+ {
+ // the Section doesn't have his own Frame, so if someone
+ // needs the real size, we have to implement this by requesting
+ // the matching Frame from the end.
+ // PROBLEM: what happens if SectionFrames overlaps multiple
+ // pages?
+ if( bPrtArea )
+ aRet = pFrame->getFramePrintArea();
+ else
+ {
+ aRet = pFrame->getFrameArea();
+ aRet.Pos().AdjustY( -1 );
+ }
+ pFrame = nullptr; // the rect is finished by now
+ }
+ }
+ }
+ else
+ {
+ const SwFrameType nFrameType = RES_FLYFRMFMT == Which() ? SwFrameType::Fly : FRM_ALL;
+ std::pair<Point, bool> tmp;
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = false;
+ }
+ pFrame = ::GetFrameOfModify(nullptr, *this, nFrameType, nullptr, pPoint ? &tmp : nullptr);
+ }
+
+ if( pFrame )
+ {
+ if( bPrtArea )
+ aRet = pFrame->getFramePrintArea();
+ else
+ aRet = pFrame->getFrameArea();
+ }
+ return aRet;
+}
+
+SdrObject* SwFrameFormat::FindRealSdrObject()
+{
+ if( RES_FLYFRMFMT == Which() )
+ {
+ Point aNullPt;
+ std::pair<Point, bool> const tmp(aNullPt, false);
+ SwFlyFrame* pFly = static_cast<SwFlyFrame*>(::GetFrameOfModify( nullptr, *this, SwFrameType::Fly,
+ nullptr, &tmp));
+ return pFly ? pFly->GetVirtDrawObj() : nullptr;
+ }
+ return FindSdrObject();
+}
+
+bool SwFrameFormat::IsLowerOf( const SwFrameFormat& rFormat ) const
+{
+ //Also linking from inside to outside or from outside to inside is not
+ //allowed.
+ SwFlyFrame *pSFly = SwIterator<SwFlyFrame,SwFormat>(*this).First();
+ if( pSFly )
+ {
+ SwFlyFrame *pAskFly = SwIterator<SwFlyFrame,SwFormat>(rFormat).First();
+ if( pAskFly )
+ return pSFly->IsLowerOf( pAskFly );
+ }
+
+ // let's try it using the node positions
+ const SwFormatAnchor* pAnchor = &rFormat.GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE != pAnchor->GetAnchorId()) && pAnchor->GetContentAnchor())
+ {
+ const SwFrameFormats& rFormats = *GetDoc()->GetSpzFrameFormats();
+ const SwNode* pFlyNd = pAnchor->GetContentAnchor()->nNode.GetNode().
+ FindFlyStartNode();
+ while( pFlyNd )
+ {
+ // then we walk up using the anchor
+ size_t n;
+ for( n = 0; n < rFormats.size(); ++n )
+ {
+ const SwFrameFormat* pFormat = rFormats[ n ];
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if( pIdx && pFlyNd == &pIdx->GetNode() )
+ {
+ if( pFormat == this )
+ return true;
+
+ pAnchor = &pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PAGE == pAnchor->GetAnchorId()) ||
+ !pAnchor->GetContentAnchor() )
+ {
+ return false;
+ }
+
+ pFlyNd = pAnchor->GetContentAnchor()->nNode.GetNode().
+ FindFlyStartNode();
+ break;
+ }
+ }
+ if( n >= rFormats.size() )
+ {
+ OSL_ENSURE( false, "Fly section but no format found" );
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+// #i31698#
+SwFrameFormat::tLayoutDir SwFrameFormat::GetLayoutDir() const
+{
+ return SwFrameFormat::HORI_L2R;
+}
+
+void SwFrameFormat::SetLayoutDir( const SwFrameFormat::tLayoutDir )
+{
+ // empty body, because default implementation does nothing
+}
+
+// #i28749#
+sal_Int16 SwFrameFormat::GetPositionLayoutDir() const
+{
+ return text::PositionLayoutDir::PositionInLayoutDirOfAnchor;
+}
+void SwFrameFormat::SetPositionLayoutDir( const sal_Int16 )
+{
+ // empty body, because default implementation does nothing
+}
+
+OUString SwFrameFormat::GetDescription() const
+{
+ return SwResId(STR_FRAME);
+}
+
+void SwFrameFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFrameFormat"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("whichId"), "%d", Which());
+
+ const char* pWhich = nullptr;
+ switch (Which())
+ {
+ case RES_FLYFRMFMT:
+ pWhich = "fly frame format";
+ break;
+ case RES_DRAWFRMFMT:
+ pWhich = "draw frame format";
+ break;
+ }
+ if (pWhich)
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("which"), BAD_CAST(pWhich));
+
+ if (m_pOtherTextBoxFormats)
+ {
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("OtherTextBoxFormat"), "%p", m_pOtherTextBoxFormats.get());
+ }
+
+ GetAttrSet().dumpAsXml(pWriter);
+
+ if (const SdrObject* pSdrObject = FindSdrObject())
+ pSdrObject->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwFrameFormats::dumpAsXml(xmlTextWriterPtr pWriter, const char* pName) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST(pName));
+ for (const SwFrameFormat *pFormat : m_PosIndex)
+ pFormat->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+SwFlyFrameFormat::SwFlyFrameFormat( SwAttrPool& rPool, const OUString &rFormatNm, SwFrameFormat *pDrvdFrame )
+ : SwFrameFormat( rPool, rFormatNm, pDrvdFrame, RES_FLYFRMFMT )
+{}
+
+SwFlyFrameFormat::~SwFlyFrameFormat()
+{
+ SwIterator<SwFlyFrame,SwFormat> aIter( *this );
+ SwFlyFrame * pLast = aIter.First();
+ if( pLast )
+ do
+ {
+ SwFrame::DestroyFrame(pLast);
+ } while( nullptr != ( pLast = aIter.Next() ));
+
+}
+
+SwFlyDrawContact* SwFlyFrameFormat::GetOrCreateContact()
+{
+ if(!m_pContact)
+ {
+ SwDrawModel* pDrawModel(GetDoc()->getIDocumentDrawModelAccess().GetOrCreateDrawModel());
+ m_pContact.reset(new SwFlyDrawContact(this, *pDrawModel));
+ }
+
+ return m_pContact.get();
+}
+
+/// Creates the Frames if the format describes a paragraph-bound frame.
+/// MA: 1994-02-14: creates the Frames also for frames anchored at page.
+void SwFlyFrameFormat::MakeFrames()
+{
+ // is there a layout?
+ if( !GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ return;
+
+ sw::BroadcastingModify *pModify = nullptr;
+ // OD 24.07.2003 #111032# - create local copy of anchor attribute for possible changes.
+ SwFormatAnchor aAnchorAttr( GetAnchor() );
+ switch( aAnchorAttr.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ if( aAnchorAttr.GetContentAnchor() )
+ {
+ pModify = aAnchorAttr.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY:
+ if( aAnchorAttr.GetContentAnchor() )
+ {
+ //First search in the content because this is O(1)
+ //This can go wrong for linked frames because in this case it's
+ //possible, that no Frame exists for this content.
+ //In such a situation we also need to search from StartNode to
+ //FrameFormat.
+ SwNodeIndex aIdx( aAnchorAttr.GetContentAnchor()->nNode );
+ SwContentNode *pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
+ // #i105535#
+ if ( pCNd == nullptr )
+ {
+ pCNd = aAnchorAttr.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ }
+ if ( pCNd )
+ {
+ if (SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti>(*pCNd).First())
+ {
+ pModify = pCNd;
+ }
+ }
+ // #i105535#
+ if ( pModify == nullptr )
+ {
+ const SwNodeIndex &rIdx = aAnchorAttr.GetContentAnchor()->nNode;
+ SwFrameFormats& rFormats = *GetDoc()->GetSpzFrameFormats();
+ for( size_t i = 0; i < rFormats.size(); ++i )
+ {
+ SwFrameFormat* pFlyFormat = rFormats[i];
+ if( pFlyFormat->GetContent().GetContentIdx() &&
+ rIdx == *pFlyFormat->GetContent().GetContentIdx() )
+ {
+ pModify = pFlyFormat;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ sal_uInt16 nPgNum = aAnchorAttr.GetPageNum();
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->Lower());
+ if( nPgNum == 0 && aAnchorAttr.GetContentAnchor() )
+ {
+ SwContentNode *pCNd = aAnchorAttr.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pCNd);
+ for ( SwFrame* pFrame = aIter.First(); pFrame != nullptr; pFrame = aIter.Next() )
+ {
+ pPage = pFrame->FindPageFrame();
+ if( pPage )
+ {
+ nPgNum = pPage->GetPhyPageNum();
+ aAnchorAttr.SetPageNum( nPgNum );
+ aAnchorAttr.SetAnchor( nullptr );
+ SetFormatAttr( aAnchorAttr );
+ break;
+ }
+ }
+ }
+ while ( pPage )
+ {
+ if ( pPage->GetPhyPageNum() == nPgNum )
+ {
+ // #i50432# - adjust synopsis of <PlaceFly(..)>
+ pPage->PlaceFly( nullptr, this );
+ break;
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if( !pModify )
+ return;
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*pModify);
+ for( SwFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ bool bAdd = !pFrame->IsContentFrame() ||
+ !static_cast<SwContentFrame*>(pFrame)->IsFollow();
+
+ if ( RndStdIds::FLY_AT_FLY == aAnchorAttr.GetAnchorId() && !pFrame->IsFlyFrame() )
+ {
+ SwFrame* pFlyFrame = pFrame->FindFlyFrame();
+ if ( pFlyFrame )
+ {
+ pFrame = pFlyFrame;
+ }
+ else
+ {
+ aAnchorAttr.SetType( RndStdIds::FLY_AT_PARA );
+ SetFormatAttr( aAnchorAttr );
+ MakeFrames();
+ return;
+ }
+ }
+
+ if (bAdd)
+ {
+ switch (aAnchorAttr.GetAnchorId())
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ assert(pFrame->IsTextFrame());
+ bAdd = IsAnchoredObjShown(*static_cast<SwTextFrame*>(pFrame), aAnchorAttr);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (bAdd && pFrame->GetDrawObjs())
+ {
+ // #i28701# - new type <SwSortedObjs>
+ SwSortedObjs &rObjs = *pFrame->GetDrawObjs();
+ for(SwAnchoredObject* pObj : rObjs)
+ {
+ // #i28701# - consider changed type of
+ // <SwSortedObjs> entries.
+ if( pObj->DynCastFlyFrame() != nullptr &&
+ (&pObj->GetFrameFormat()) == this )
+ {
+ bAdd = false;
+ break;
+ }
+ }
+ }
+
+ if( bAdd )
+ {
+ SwFlyFrame *pFly = nullptr; // avoid warnings
+ switch( aAnchorAttr.GetAnchorId() )
+ {
+ case RndStdIds::FLY_AT_FLY:
+ pFly = new SwFlyLayFrame( this, pFrame, pFrame );
+ break;
+
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ pFly = new SwFlyAtContentFrame( this, pFrame, pFrame );
+ break;
+
+ case RndStdIds::FLY_AS_CHAR:
+ pFly = new SwFlyInContentFrame( this, pFrame, pFrame );
+ break;
+
+ default:
+ assert(false && "New anchor type" );
+ }
+ pFrame->AppendFly( pFly );
+ pFly->GetFormat()->SetObjTitle(GetObjTitle());
+ pFly->GetFormat()->SetObjDescription(GetObjDescription());
+ SwPageFrame *pPage = pFly->FindPageFrame();
+ if( pPage )
+ ::RegistFlys( pPage, pFly );
+ }
+ }
+}
+
+SwFlyFrame* SwFlyFrameFormat::GetFrame( const Point* pPoint ) const
+{
+ std::pair<Point, bool> tmp;
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = false;
+ }
+ return static_cast<SwFlyFrame*>(::GetFrameOfModify( nullptr, *this, SwFrameType::Fly,
+ nullptr, &tmp));
+}
+
+SwAnchoredObject* SwFlyFrameFormat::GetAnchoredObj() const
+{
+ SwFlyFrame* pFlyFrame( GetFrame() );
+ if ( pFlyFrame )
+ {
+ return pFlyFrame;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+bool SwFlyFrameFormat::GetInfo( SfxPoolItem& rInfo ) const
+{
+ bool bRet = true;
+ switch( rInfo.Which() )
+ {
+ case RES_CONTENT_VISIBLE:
+ {
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject = SwIterator<SwFrame,SwFormat>( *this ).First();
+ }
+ bRet = false;
+ break;
+
+ default:
+ bRet = SwFrameFormat::GetInfo( rInfo );
+ break;
+ }
+ return bRet;
+}
+
+// #i73249#
+void SwFlyFrameFormat::SetObjTitle( const OUString& rTitle, bool bBroadcast )
+{
+ SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::SetObjTitle(..)> - missing <SdrObject> instance" );
+ msTitle = rTitle;
+ if ( !pMasterObject )
+ {
+ return;
+ }
+
+ const SwStringMsgPoolItem aOld(RES_TITLE_CHANGED, pMasterObject->GetTitle());
+ pMasterObject->SetTitle(rTitle);
+ if(bBroadcast)
+ {
+ const SwStringMsgPoolItem aNew(RES_TITLE_CHANGED, rTitle);
+ GetNotifier().Broadcast(sw::LegacyModifyHint(&aOld, &aNew));
+ }
+}
+
+OUString SwFlyFrameFormat::GetObjTitle() const
+{
+ const SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::GetObjTitle(..)> - missing <SdrObject> instance" );
+ if ( !pMasterObject )
+ {
+ return msTitle;
+ }
+ if (!pMasterObject->GetTitle().isEmpty())
+ return pMasterObject->GetTitle();
+ else
+ return msTitle;
+}
+
+void SwFlyFrameFormat::SetObjTooltip(const OUString& rTooltip)
+{
+ msTooltip = rTooltip;
+}
+
+const OUString & SwFlyFrameFormat::GetObjTooltip() const
+{
+ return msTooltip;
+}
+
+void SwFlyFrameFormat::SetObjDescription( const OUString& rDescription, bool bBroadcast )
+{
+ SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::SetDescription(..)> - missing <SdrObject> instance" );
+ msDesc = rDescription;
+ if ( !pMasterObject )
+ {
+ return;
+ }
+
+ const SwStringMsgPoolItem aOld( RES_DESCRIPTION_CHANGED, pMasterObject->GetDescription() );
+ pMasterObject->SetDescription( rDescription );
+ if(bBroadcast)
+ {
+ const SwStringMsgPoolItem aNew( RES_DESCRIPTION_CHANGED, rDescription );
+ GetNotifier().Broadcast(sw::LegacyModifyHint(&aOld, &aNew));
+ }
+}
+
+OUString SwFlyFrameFormat::GetObjDescription() const
+{
+ const SdrObject* pMasterObject = FindSdrObject();
+ OSL_ENSURE( pMasterObject, "<SwFlyFrameFormat::GetDescription(..)> - missing <SdrObject> instance" );
+ if ( !pMasterObject )
+ {
+ return msDesc;
+ }
+ if (!pMasterObject->GetDescription().isEmpty())
+ return pMasterObject->GetDescription();
+ else
+ return msDesc;
+}
+
+/** SwFlyFrameFormat::IsBackgroundTransparent - for #99657#
+
+ OD 22.08.2002 - overriding virtual method and its default implementation,
+ because format of fly frame provides transparent backgrounds.
+ Method determines, if background of fly frame is transparent.
+
+ @return true, if background color is transparent, but not "no fill"
+ or the transparency of an existing background graphic is set.
+*/
+bool SwFlyFrameFormat::IsBackgroundTransparent() const
+{
+ if (supportsFullDrawingLayerFillAttributeSet() && getSdrAllFillAttributesHelper())
+ {
+ return getSdrAllFillAttributesHelper()->isTransparent();
+ }
+
+ // NOTE: If background color is "no fill"/"auto fill" (COL_TRANSPARENT)
+ // and there is no background graphic, it "inherites" the background
+ // from its anchor.
+ std::unique_ptr<SvxBrushItem> aBackground(makeBackgroundBrushItem());
+ if ( aBackground->GetColor().IsTransparent() &&
+ aBackground->GetColor() != COL_TRANSPARENT
+ )
+ {
+ return true;
+ }
+ else
+ {
+ const GraphicObject *pTmpGrf = aBackground->GetGraphicObject();
+ if ( pTmpGrf &&
+ pTmpGrf->GetAttr().IsTransparent()
+ )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** SwFlyFrameFormat::IsBackgroundBrushInherited - for #103898#
+
+ OD 08.10.2002 - method to determine, if the brush for drawing the
+ background is "inherited" from its parent/grandparent.
+ This is the case, if no background graphic is set and the background
+ color is "no fill"/"auto fill"
+ NOTE: condition is "copied" from method <SwFrame::GetBackgroundBrush(..).
+
+ @return true, if background brush is "inherited" from parent/grandparent
+*/
+bool SwFlyFrameFormat::IsBackgroundBrushInherited() const
+{
+ if (supportsFullDrawingLayerFillAttributeSet() && getSdrAllFillAttributesHelper())
+ {
+ return !getSdrAllFillAttributesHelper()->isUsed();
+ }
+ else
+ {
+ std::unique_ptr<SvxBrushItem> aBackground(makeBackgroundBrushItem());
+ if ( (aBackground->GetColor() == COL_TRANSPARENT) &&
+ !(aBackground->GetGraphicObject()) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SwHandleAnchorNodeChg::SwHandleAnchorNodeChg( SwFlyFrameFormat& _rFlyFrameFormat,
+ const SwFormatAnchor& _rNewAnchorFormat,
+ SwFlyFrame const * _pKeepThisFlyFrame )
+ : mrFlyFrameFormat( _rFlyFrameFormat ),
+ mbAnchorNodeChanged( false ),
+ mpWrtShell(nullptr)
+{
+ const SwFormatAnchor& aOldAnchorFormat(_rFlyFrameFormat.GetAnchor());
+ const RndStdIds nNewAnchorType( _rNewAnchorFormat.GetAnchorId() );
+ if ( ((nNewAnchorType == RndStdIds::FLY_AT_PARA) ||
+ (nNewAnchorType == RndStdIds::FLY_AT_CHAR)) &&
+ _rNewAnchorFormat.GetContentAnchor() &&
+ _rNewAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode() )
+ {
+ if ( aOldAnchorFormat.GetAnchorId() == nNewAnchorType &&
+ aOldAnchorFormat.GetContentAnchor() &&
+ aOldAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode() &&
+ aOldAnchorFormat.GetContentAnchor()->nNode !=
+ _rNewAnchorFormat.GetContentAnchor()->nNode )
+ {
+ // determine 'old' number of anchor frames
+ sal_uInt32 nOldNumOfAnchFrame( 0 );
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aOldIter(
+ *(aOldAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode()) );
+ for( SwFrame* pOld = aOldIter.First(); pOld; pOld = aOldIter.Next() )
+ {
+ ++nOldNumOfAnchFrame;
+ }
+ // determine 'new' number of anchor frames
+ sal_uInt32 nNewNumOfAnchFrame( 0 );
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aNewIter(
+ *(_rNewAnchorFormat.GetContentAnchor()->nNode.GetNode().GetContentNode()) );
+ for( SwFrame* pNew = aNewIter.First(); pNew; pNew = aNewIter.Next() )
+ {
+ ++nNewNumOfAnchFrame;
+ }
+ if ( nOldNumOfAnchFrame != nNewNumOfAnchFrame )
+ {
+ // delete existing fly frames except <_pKeepThisFlyFrame>
+ SwIterator<SwFrame,SwFormat> aIter( mrFlyFrameFormat );
+ SwFrame* pFrame = aIter.First();
+ if ( pFrame )
+ {
+ do {
+ if ( pFrame != _pKeepThisFlyFrame )
+ {
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+ } while( nullptr != ( pFrame = aIter.Next() ));
+ }
+ // indicate, that re-creation of fly frames necessary
+ mbAnchorNodeChanged = true;
+ }
+ }
+ }
+
+ if (aOldAnchorFormat.GetContentAnchor()
+ && aOldAnchorFormat.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ mpCommentAnchor.reset(new SwPosition(*aOldAnchorFormat.GetContentAnchor()));
+ }
+
+ if (_pKeepThisFlyFrame)
+ {
+ SwViewShell* pViewShell = _pKeepThisFlyFrame->getRootFrame()->GetCurrShell();
+ mpWrtShell = dynamic_cast<SwWrtShell*>(pViewShell);
+ }
+}
+
+void SwHandleAnchorNodeChg::ImplDestroy()
+{
+ if ( mbAnchorNodeChanged )
+ {
+ mrFlyFrameFormat.MakeFrames();
+ }
+
+ // See if the fly frame had a comment: if so, move it to the new anchor as well.
+ if (!mpCommentAnchor)
+ {
+ return;
+ }
+
+ SwTextNode* pTextNode = mpCommentAnchor->nNode.GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return;
+ }
+
+ const SwTextField* pField = pTextNode->GetFieldTextAttrAt(mpCommentAnchor->nContent.GetIndex());
+ if (!pField || pField->GetFormatField().GetField()->GetTyp()->Which() != SwFieldIds::Postit)
+ {
+ return;
+ }
+
+ if (!mpWrtShell)
+ {
+ return;
+ }
+
+ // Save current cursor position, so we can restore it later.
+ mpWrtShell->Push();
+
+ // Set up the source of the move: the old comment anchor.
+ {
+ SwPaM& rCursor = mpWrtShell->GetCurrentShellCursor();
+ *rCursor.GetPoint() = *mpCommentAnchor;
+ rCursor.SetMark();
+ *rCursor.GetMark() = *mpCommentAnchor;
+ ++rCursor.GetMark()->nContent;
+ }
+
+ // Set up the target of the move: the new comment anchor.
+ const SwFormatAnchor& rNewAnchorFormat = mrFlyFrameFormat.GetAnchor();
+ mpWrtShell->CreateCursor();
+ *mpWrtShell->GetCurrentShellCursor().GetPoint() = *rNewAnchorFormat.GetContentAnchor();
+
+ // Move by copying and deleting.
+ mpWrtShell->SwEditShell::Copy(*mpWrtShell);
+ mpWrtShell->DestroyCursor();
+
+ mpWrtShell->Delete(false);
+
+ mpWrtShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+}
+
+SwHandleAnchorNodeChg::~SwHandleAnchorNodeChg()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+namespace sw
+{
+ DrawFrameFormatHint::~DrawFrameFormatHint() {}
+ CheckDrawFrameFormatLayerHint::~CheckDrawFrameFormatLayerHint() {}
+ ContactChangedHint::~ContactChangedHint() {}
+ DrawFormatLayoutCopyHint::~DrawFormatLayoutCopyHint() {}
+ WW8AnchorConvHint::~WW8AnchorConvHint() {}
+ RestoreFlyAnchorHint::~RestoreFlyAnchorHint() {}
+ CreatePortionHint::~CreatePortionHint() {}
+ FindSdrObjectHint::~FindSdrObjectHint() {}
+ CollectTextObjectsHint::~CollectTextObjectsHint() {}
+ GetZOrderHint::~GetZOrderHint() {}
+ GetObjectConnectedHint::~GetObjectConnectedHint() {}
+}
+
+SwDrawFrameFormat::~SwDrawFrameFormat()
+{
+ CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::DYING));
+}
+
+void SwDrawFrameFormat::MakeFrames()
+{
+ CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::MAKE_FRAMES));
+}
+
+void SwDrawFrameFormat::DelFrames()
+{
+ CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::DELETE_FRAMES));
+}
+
+// #i31698#
+SwFrameFormat::tLayoutDir SwDrawFrameFormat::GetLayoutDir() const
+{
+ return meLayoutDir;
+}
+
+void SwDrawFrameFormat::SetLayoutDir( const SwFrameFormat::tLayoutDir _eLayoutDir )
+{
+ meLayoutDir = _eLayoutDir;
+}
+
+// #i28749#
+sal_Int16 SwDrawFrameFormat::GetPositionLayoutDir() const
+{
+ return mnPositionLayoutDir;
+}
+void SwDrawFrameFormat::SetPositionLayoutDir( const sal_Int16 _nPositionLayoutDir )
+{
+ switch ( _nPositionLayoutDir )
+ {
+ case text::PositionLayoutDir::PositionInHoriL2R:
+ case text::PositionLayoutDir::PositionInLayoutDirOfAnchor:
+ {
+ mnPositionLayoutDir = _nPositionLayoutDir;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwDrawFrameFormat::SetPositionLayoutDir(..)> - invalid attribute value." );
+ }
+ }
+}
+
+OUString SwDrawFrameFormat::GetDescription() const
+{
+ OUString aResult;
+ const SdrObject * pSdrObj = FindSdrObject();
+
+ if (pSdrObj)
+ {
+ if (pSdrObj != m_pSdrObjectCached)
+ {
+ m_sSdrObjectCachedComment = SdrUndoNewObj::GetComment(*pSdrObj);
+ m_pSdrObjectCached = pSdrObj;
+ }
+
+ aResult = m_sSdrObjectCachedComment;
+ }
+ else
+ aResult = SwResId(STR_GRAPHIC);
+
+ return aResult;
+}
+
+IMapObject* SwFrameFormat::GetIMapObject( const Point& rPoint,
+ const SwFlyFrame *pFly ) const
+{
+ const SwFormatURL &rURL = GetURL();
+ if( !rURL.GetMap() )
+ return nullptr;
+
+ if( !pFly )
+ {
+ pFly = SwIterator<SwFlyFrame,SwFormat>( *this ).First();
+ if( !pFly )
+ return nullptr;
+ }
+
+ //Original size for OLE and graphic is TwipSize, otherwise the size of
+ //FrameFormat of the Fly.
+ const SwFrame *pRef;
+ const SwNoTextNode *pNd = nullptr;
+ Size aOrigSz;
+ if( pFly->Lower() && pFly->Lower()->IsNoTextFrame() )
+ {
+ pRef = pFly->Lower();
+ pNd = static_cast<const SwNoTextFrame*>(pRef)->GetNode()->GetNoTextNode();
+ aOrigSz = pNd->GetTwipSize();
+ }
+ else
+ {
+ pRef = pFly;
+ aOrigSz = pFly->GetFormat()->GetFrameSize().GetSize();
+ }
+
+ if( !aOrigSz.IsEmpty() )
+ {
+ Point aPos( rPoint );
+ Size aActSz ( pRef == pFly ? pFly->getFrameArea().SSize() : pRef->getFramePrintArea().SSize() );
+ const o3tl::Length aSrc ( o3tl::Length::twip );
+ const o3tl::Length aDest( o3tl::Length::mm100 );
+ aOrigSz = o3tl::convert( aOrigSz, aSrc, aDest );
+ aActSz = o3tl::convert( aActSz, aSrc, aDest );
+ aPos -= pRef->getFrameArea().Pos();
+ aPos -= pRef->getFramePrintArea().Pos();
+ aPos = o3tl::convert( aPos, aSrc, aDest );
+ sal_uInt32 nFlags = 0;
+ if ( pFly != pRef && pNd->IsGrfNode() )
+ {
+ const MirrorGraph nMirror = pNd->GetSwAttrSet().
+ GetMirrorGrf().GetValue();
+ if ( MirrorGraph::Both == nMirror )
+ nFlags = IMAP_MIRROR_HORZ | IMAP_MIRROR_VERT;
+ else if ( MirrorGraph::Vertical == nMirror )
+ nFlags = IMAP_MIRROR_VERT;
+ else if ( MirrorGraph::Horizontal == nMirror )
+ nFlags = IMAP_MIRROR_HORZ;
+
+ }
+ return const_cast<ImageMap*>(rURL.GetMap())->GetHitIMapObject( aOrigSz,
+ aActSz, aPos, nFlags );
+ }
+
+ return nullptr;
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwFrameFormat::getSdrAllFillAttributesHelper() const
+{
+ if (supportsFullDrawingLayerFillAttributeSet())
+ {
+ // create FillAttributes on demand
+ if(!maFillAttributes)
+ {
+ const_cast< SwFrameFormat* >(this)->maFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(GetAttrSet());
+ }
+ }
+ else
+ {
+ // FALLBACKBREAKHERE assert wrong usage
+ OSL_ENSURE(false, "getSdrAllFillAttributesHelper() call only valid for RES_FLYFRMFMT and RES_FRMFMT (!)");
+ }
+
+ return maFillAttributes;
+}
+
+void SwFrameFormat::MoveTableBox(SwTableBox& rTableBox, const SwFrameFormat* pOldFormat)
+{
+ Add(&rTableBox);
+ if(!pOldFormat)
+ return;
+ const auto& rOld = pOldFormat->GetFormatAttr(RES_BOXATR_FORMAT);
+ const auto& rNew = GetFormatAttr(RES_BOXATR_FORMAT);
+ if(rOld != rNew)
+ SwClientNotify(*this, sw::LegacyModifyHint(&rOld, &rNew));
+}
+
+
+namespace sw {
+
+bool IsFlyFrameFormatInHeader(const SwFrameFormat& rFormat)
+{
+ const SwFlyFrameFormat* pFlyFrameFormat = dynamic_cast<const SwFlyFrameFormat*>(&rFormat);
+ if (!pFlyFrameFormat)
+ return false;
+ SwFlyFrame* pFlyFrame = pFlyFrameFormat->GetFrame();
+ if (!pFlyFrame) // fdo#54648: "hidden" drawing object has no layout frame
+ {
+ return false;
+ }
+ SwPageFrame* pPageFrame = pFlyFrame->FindPageFrameOfAnchor();
+ SwFrame* pHeader = pPageFrame->Lower();
+ if (pHeader->GetType() == SwFrameType::Header)
+ {
+ const SwFrame* pFrame = pFlyFrame->GetAnchorFrame();
+ while (pFrame)
+ {
+ if (pFrame == pHeader)
+ return true;
+ pFrame = pFrame->GetUpper();
+ }
+ }
+ return false;
+}
+
+void CheckAnchoredFlyConsistency(SwDoc const& rDoc)
+{
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ SwNodes const& rNodes(rDoc.GetNodes());
+ SwNodeOffset const count(rNodes.Count());
+ for (SwNodeOffset i(0); i != count; ++i)
+ {
+ SwNode const*const pNode(rNodes[i]);
+ std::vector<SwFrameFormat*> const & rFlys(pNode->GetAnchoredFlys());
+ for (const auto& rpFly : rFlys)
+ {
+ SwFormatAnchor const& rAnchor((*rpFly).GetAnchor(false));
+ assert(&rAnchor.GetContentAnchor()->nNode.GetNode() == pNode);
+ }
+ }
+ SwFrameFormats const*const pSpzFrameFormats(rDoc.GetSpzFrameFormats());
+ if (!pSpzFrameFormats)
+ return;
+
+ for (auto it = pSpzFrameFormats->begin(); it != pSpzFrameFormats->end(); ++it)
+ {
+ SwFormatAnchor const& rAnchor((**it).GetAnchor(false));
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ {
+ assert(!rAnchor.GetContentAnchor()
+ // for invalid documents that lack text:anchor-page-number
+ // it may have an anchor before MakeFrames() is called
+ || (!SwIterator<SwFrame, SwFrameFormat>(**it).First()));
+ }
+ else
+ {
+ SwNode & rNode(rAnchor.GetContentAnchor()->nNode.GetNode());
+ std::vector<SwFrameFormat*> const& rFlys(rNode.GetAnchoredFlys());
+ assert(std::find(rFlys.begin(), rFlys.end(), *it) != rFlys.end());
+ switch (rAnchor.GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_FLY:
+ assert(rNode.IsStartNode());
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ assert(rNode.IsTextNode() || rNode.IsTableNode());
+ break;
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ assert(rNode.IsTextNode());
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ }
+ }
+#else
+ (void) rDoc;
+#endif
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx
new file mode 100644
index 000000000..184373585
--- /dev/null
+++ b/sw/source/core/layout/calcmove.cxx
@@ -0,0 +1,2215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <ndtxt.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <osl/diagnose.h>
+#include <svx/sdtaitm.hxx>
+
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <fmtclbl.hxx>
+
+#include <tabfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <dbg_lay.hxx>
+
+#include <sortedobjs.hxx>
+#include <layouter.hxx>
+#include <flyfrms.hxx>
+
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+// Move methods
+
+/// Return value tells whether the Frame should be moved.
+bool SwContentFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool & )
+{
+ if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
+ {
+ // Floating back a frm uses a bit of time unfortunately.
+ // The most common case is the following: The Frame wants to float to
+ // somewhere where the FixSize is the same that the Frame itself has already.
+ // In that case it's pretty easy to check if the Frame has enough space
+ // for its VarSize. If this is NOT the case, we already know that
+ // we don't need to move.
+ // The Frame checks itself whether it has enough space - respecting the fact
+ // that it could possibly split itself if needed.
+ // If, however, the FixSize differs from the Frame or Flys are involved
+ // (either in the old or the new position), checking is pointless,
+ // and we have to move the Frame just to see what happens - if there's
+ // some space available to do it, that is.
+
+ // The FixSize of the containers of Contents is always the width.
+
+ // If we moved more than one sheet back (for example jumping over empty
+ // pages), we have to move either way. Otherwise, if the Frame doesn't fit
+ // into the page, empty pages wouldn't be respected anymore.
+ sal_uInt8 nMoveAnyway = 0;
+ SwPageFrame * const pNewPage = pNewUpper->FindPageFrame();
+ SwPageFrame *pOldPage = FindPageFrame();
+
+ if ( SwFlowFrame::IsMoveBwdJump() )
+ return true;
+
+ if( IsInFootnote() && IsInSct() )
+ {
+ SwFootnoteFrame* pFootnote = FindFootnoteFrame();
+ SwSectionFrame* pMySect = pFootnote->FindSctFrame();
+ if( pMySect && pMySect->IsFootnoteLock() )
+ {
+ SwSectionFrame *pSect = pNewUpper->FindSctFrame();
+ while( pSect && pSect->IsInFootnote() )
+ pSect = pSect->GetUpper()->FindSctFrame();
+ OSL_ENSURE( pSect, "Escaping footnote" );
+ if( pSect != pMySect )
+ return false;
+ }
+ }
+ SwRectFnSet aRectFnSet(this);
+ SwRectFnSet fnRectX(pNewUpper);
+ if( std::abs( fnRectX.GetWidth(pNewUpper->getFramePrintArea()) -
+ aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) ) > 1 ) {
+ // In this case, only a WouldFit_ with test move is possible
+ nMoveAnyway = 2;
+ }
+
+ // Do *not* move backward, if <nMoveAnyway> equals 3 and no space is left in new upper.
+ nMoveAnyway |= BwdMoveNecessary( pOldPage, getFrameArea() );
+ {
+ const IDocumentSettingAccess& rIDSA = pNewPage->GetFormat()->getIDocumentSettingAccess();
+ SwTwips nSpace = 0;
+ SwRect aRect( pNewUpper->getFramePrintArea() );
+ aRect.Pos() += pNewUpper->getFrameArea().Pos();
+ const SwFrame *pPrevFrame = pNewUpper->Lower();
+ while ( pPrevFrame )
+ {
+ SwTwips nNewTop = fnRectX.GetBottom(pPrevFrame->getFrameArea());
+ // Consider lower spacing of last frame in a table cell
+ {
+ // Check if last frame is inside table and if it includes its lower spacing.
+ if ( !pPrevFrame->GetNext() && pPrevFrame->IsInTab() &&
+ rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) )
+ {
+ const SwFrame* pLastFrame = pPrevFrame;
+ // if last frame is a section, take its last content
+ if ( pPrevFrame->IsSctFrame() )
+ {
+ pLastFrame = static_cast<const SwSectionFrame*>(pPrevFrame)->FindLastContent();
+ if ( pLastFrame &&
+ pLastFrame->FindTabFrame() != pPrevFrame->FindTabFrame() )
+ {
+ pLastFrame = pLastFrame->FindTabFrame();
+ }
+ }
+
+ if ( pLastFrame )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pLastFrame );
+ const SwBorderAttrs& rAttrs = *aAccess.Get();
+ nNewTop -= rAttrs.GetULSpace().GetLower();
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
+ {
+ nNewTop -= rAttrs.CalcLineSpacing();
+ }
+ }
+ }
+ }
+ fnRectX.SetTop( aRect, nNewTop );
+
+ pPrevFrame = pPrevFrame->GetNext();
+ }
+
+ nMoveAnyway |= BwdMoveNecessary( pNewPage, aRect);
+
+ //determine space left in new upper frame
+ nSpace = fnRectX.GetHeight(aRect);
+ const SwViewShell *pSh = pNewUpper->getRootFrame()->GetCurrShell();
+ if ( IsInFootnote() ||
+ (pSh && pSh->GetViewOptions()->getBrowseMode()) ||
+ pNewUpper->IsCellFrame() ||
+ ( pNewUpper->IsInSct() && ( pNewUpper->IsSctFrame() ||
+ ( pNewUpper->IsColBodyFrame() &&
+ !pNewUpper->GetUpper()->GetPrev() &&
+ !pNewUpper->GetUpper()->GetNext() ) ) ) )
+ nSpace += pNewUpper->Grow( LONG_MAX, true );
+
+ if ( nMoveAnyway < 3 )
+ {
+ if ( nSpace )
+ {
+ // Do not notify footnotes which are stuck to the paragraph:
+ // This would require extremely confusing code, taking into
+ // account the widths
+ // and Flys, that in turn influence the footnotes, ...
+
+ // WouldFit_ can only be used if the width is the same and
+ // ONLY self-anchored Flys are present.
+
+ // WouldFit_ can also be used if ONLY Flys anchored
+ // somewhere else are present.
+ // In this case, the width doesn't even matter,
+ // because we're running a TestFormat in the new upper.
+ const sal_uInt8 nBwdMoveNecessaryResult =
+ BwdMoveNecessary( pNewPage, aRect);
+ const bool bObjsInNewUpper( nBwdMoveNecessaryResult == 2 ||
+ nBwdMoveNecessaryResult == 3 );
+
+ return WouldFit_( nSpace, pNewUpper, nMoveAnyway == 2,
+ bObjsInNewUpper );
+ }
+ // It's impossible for WouldFit_ to return a usable result if
+ // we have a fresh multi-column section - so we really have to
+ // float back unless there is no space.
+ return pNewUpper->IsInSct() && pNewUpper->IsColBodyFrame() &&
+ !fnRectX.GetWidth(pNewUpper->getFramePrintArea()) &&
+ ( pNewUpper->GetUpper()->GetPrev() ||
+ pNewUpper->GetUpper()->GetNext() );
+ }
+
+ // Check for space left in new upper
+ return nSpace != 0;
+ }
+ }
+ return false;
+}
+
+// Calc methods
+
+// Two little friendships form a secret society
+inline void PrepareLock( SwFlowFrame *pTab )
+{
+ pTab->LockJoin();
+}
+inline void PrepareUnlock( SwFlowFrame *pTab )
+{
+ pTab->UnlockJoin();
+
+}
+
+// hopefully, one day this function simply will return 'false'
+static bool lcl_IsCalcUpperAllowed( const SwFrame& rFrame )
+{
+ return !rFrame.GetUpper()->IsSctFrame() &&
+ !rFrame.GetUpper()->IsFooterFrame() &&
+ // No format of upper Writer fly frame
+ !rFrame.GetUpper()->IsFlyFrame() &&
+ !( rFrame.GetUpper()->IsTabFrame() && rFrame.GetUpper()->GetUpper()->IsInTab() ) &&
+ !( rFrame.IsTabFrame() && rFrame.GetUpper()->IsInTab() );
+}
+
+/** Prepares the Frame for "formatting" (MakeAll()).
+ *
+ * This method serves to save stack space: To calculate the position of the Frame
+ * we have to make sure that the positions of Upper and Prev respectively are
+ * valid. This may require a recursive call (a loop would be quite expensive,
+ * as it's not required very often).
+ *
+ * Every call of MakeAll requires around 500 bytes on the stack - you easily
+ * see where this leads to. This method requires only a little bit of stack
+ * space, so the recursive call should not be a problem here.
+ *
+ * Another advantage is that one nice day, this method and with it the
+ * formatting of predecessors could be avoided. Then it could probably be
+ * possible to jump "quickly" to the document's end.
+ *
+ * @see MakeAll()
+ */
+void SwFrame::PrepareMake(vcl::RenderContext* pRenderContext)
+{
+ StackHack aHack;
+ if ( GetUpper() )
+ {
+ SwFrameDeleteGuard aDeleteGuard(this);
+ if ( lcl_IsCalcUpperAllowed( *this ) )
+ GetUpper()->Calc(pRenderContext);
+ OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
+ if ( !GetUpper() )
+ return;
+
+ const bool bCnt = IsContentFrame();
+ const bool bTab = IsTabFrame();
+ bool bNoSect = IsInSct();
+ bool bOldTabLock = false, bFoll = false;
+ SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
+
+ if ( bTab )
+ {
+ pThis = static_cast<SwTabFrame*>(this);
+ bOldTabLock = static_cast<SwTabFrame*>(this)->IsJoinLocked();
+ ::PrepareLock( static_cast<SwTabFrame*>(this) );
+ bFoll = pThis->IsFollow();
+ }
+ else if( IsSctFrame() )
+ {
+ pThis = static_cast<SwSectionFrame*>(this);
+ bFoll = pThis->IsFollow();
+ bNoSect = false;
+ }
+ else if ( bCnt )
+ {
+ bFoll = pThis->IsFollow();
+ if ( bFoll && GetPrev() )
+ {
+ // Do not follow the chain when we need only one instance
+ const SwTextFrame* pMaster = static_cast<SwContentFrame*>(this)->FindMaster();
+ if ( pMaster && pMaster->IsLocked() )
+ {
+ MakeAll(pRenderContext);
+ return;
+ }
+ }
+ }
+
+ // There is no format of previous frame, if current frame is a table
+ // frame and its previous frame wants to keep with it.
+ const bool bFormatPrev = !bTab ||
+ !GetPrev() ||
+ !GetPrev()->GetAttrSet()->GetKeep().GetValue();
+ if ( bFormatPrev )
+ {
+ SwFrame *pFrame = GetUpper()->Lower();
+ while ( pFrame != this )
+ {
+ OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
+ if ( !pFrame )
+ return; //Oioioioi ...
+
+ if ( !pFrame->isFrameAreaDefinitionValid() )
+ {
+ // A small interference that hopefully improves on the stability:
+ // If I'm Follow AND neighbor of a Frame before me, it would delete
+ // me when formatting. This as you can see could easily become a
+ // confusing situation that we want to avoid.
+ if ( bFoll && pFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
+ break;
+
+ bool const isLast(pFrame->GetNext() == this);
+ // note: this seems obvious but does *not* hold, a MakeAll()
+ // could move more than 1 frame backwards!
+ // that's why FindNext() is used below
+ // assert(pFrame->GetUpper() == GetUpper());
+ pFrame->MakeAll(pRenderContext);
+ if( IsSctFrame() && !static_cast<SwSectionFrame*>(this)->GetSection() )
+ break;
+ if (isLast && pFrame->GetUpper() != GetUpper())
+ {
+ assert(GetUpper()->Lower() == this
+ // empty section frames are created all the time...
+ || GetUpper()->Lower()->IsSctFrame()
+ // tab frame/section frame may split multiple times
+ || ( SwFlowFrame::CastFlowFrame(pFrame)
+ && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
+ && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
+ SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
+ && (GetUpper()->Lower()->GetNext() == this
+ // if it's more than 10 pages long...
+ || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
+ == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
+ && GetUpper()->Lower()->GetNext()->GetNext() == this)
+ // pre-existing empty section frames may end up between them...
+ || GetUpper()->Lower()->GetNext()->IsSctFrame())));
+ break; // tdf#119109 frame was moved backward, prevent
+ // FindNext() returning a frame inside this if
+ } // this is a table!
+ }
+ // With ContentFrames, the chain may be broken while walking through
+ // it. Therefore we have to figure out the next frame in a bit more
+ // complicated way. However, I'll HAVE to get back to myself
+ // sometime again.
+ pFrame = pFrame->FindNext();
+
+ // If we started out in a SectionFrame, it might have happened that
+ // we landed in a Section Follow via the MakeAll calls.
+ // FindNext only gives us the SectionFrame, not it's content - we
+ // won't find ourselves anymore!
+ if( bNoSect && pFrame && pFrame->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pCnt )
+ pFrame = pCnt;
+ }
+ }
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
+ if ( !GetUpper() )
+ return;
+
+ if ( lcl_IsCalcUpperAllowed( *this ) )
+ GetUpper()->Calc(pRenderContext);
+
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
+ }
+
+ if ( bTab && !bOldTabLock )
+ ::PrepareUnlock( static_cast<SwTabFrame*>(this) );
+ }
+ MakeAll(pRenderContext);
+}
+
+void SwFrame::OptPrepareMake()
+{
+ // #i23129#, #i36347# - no format of upper Writer fly frame
+ if ( GetUpper() && !GetUpper()->IsFooterFrame() &&
+ !GetUpper()->IsFlyFrame() )
+ {
+ {
+ SwFrameDeleteGuard aDeleteGuard(this);
+ GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+ }
+ OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
+ if ( !GetUpper() )
+ return;
+ }
+ if ( GetPrev() && !GetPrev()->isFrameAreaDefinitionValid() )
+ {
+ PrepareMake(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+ }
+ else
+ {
+ StackHack aHack;
+ MakeAll(IsRootFrame() ? nullptr : getRootFrame()->GetCurrShell()->GetOut());
+ }
+}
+
+void SwFrame::PrepareCursor()
+{
+ StackHack aHack;
+ if( GetUpper() && !GetUpper()->IsSctFrame() )
+ {
+ const bool bCnt = IsContentFrame();
+ const bool bTab = IsTabFrame();
+ bool bNoSect = IsInSct();
+
+ std::optional<FlowFrameJoinLockGuard> tabGuard;
+ std::optional<SwFrameDeleteGuard> rowGuard;
+ SwFlowFrame* pThis = bCnt ? static_cast<SwContentFrame*>(this) : nullptr;
+
+ if ( bTab )
+ {
+ tabGuard.emplace(static_cast<SwTabFrame*>(this)); // tdf#125741
+ pThis = static_cast<SwTabFrame*>(this);
+ }
+ else if (IsRowFrame())
+ {
+ rowGuard.emplace(this); // tdf#125741 keep this alive
+ }
+ else if( IsSctFrame() )
+ {
+ pThis = static_cast<SwSectionFrame*>(this);
+ bNoSect = false;
+ }
+
+ GetUpper()->PrepareCursor();
+ GetUpper()->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+
+ OSL_ENSURE( GetUpper(), ":-( Layout unstable (Upper gone)." );
+ if ( !GetUpper() )
+ return;
+
+ bool const bFoll = pThis && pThis->IsFollow();
+
+ SwFrame *pFrame = GetUpper()->Lower();
+ while ( pFrame != this )
+ {
+ OSL_ENSURE( pFrame, ":-( Layout unstable (this not found)." );
+ if ( !pFrame )
+ return; //Oioioioi ...
+
+ if ( !pFrame->isFrameAreaDefinitionValid() )
+ {
+ // A small interference that hopefully improves on the stability:
+ // If I'm Follow AND neighbor of a Frame before me, it would delete
+ // me when formatting. This as you can see could easily become a
+ // confusing situation that we want to avoid.
+ if ( bFoll && pFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow( pThis ) )
+ break;
+
+ bool const isLast(pFrame->GetNext() == this);
+ pFrame->MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ if (isLast && pFrame->GetUpper() != GetUpper())
+ {
+ assert(GetUpper()->Lower() == this
+ // empty section frames are created all the time...
+ || GetUpper()->Lower()->IsSctFrame()
+ // tab frame/section frame may split multiple times
+ || ( SwFlowFrame::CastFlowFrame(pFrame)
+ && SwFlowFrame::CastFlowFrame(GetUpper()->Lower())
+ && SwFlowFrame::CastFlowFrame(pFrame)->IsAnFollow(
+ SwFlowFrame::CastFlowFrame(GetUpper()->Lower()))
+ && (GetUpper()->Lower()->GetNext() == this
+ // if it's more than 10 pages long...
+ || (SwFlowFrame::CastFlowFrame(GetUpper()->Lower())->GetFollow()
+ == SwFlowFrame::CastFlowFrame(GetUpper()->Lower()->GetNext())
+ && GetUpper()->Lower()->GetNext()->GetNext() == this)
+ // pre-existing empty section frames may end up between them...
+ || GetUpper()->Lower()->GetNext()->IsSctFrame())));
+ break; // tdf#119109 frame was moved backward, prevent
+ // FindNext() returning a frame inside this if
+ } // this is a table!
+ }
+ // With ContentFrames, the chain may be broken while walking through
+ // it. Therefore we have to figure out the next frame in a bit more
+ // complicated way. However, I'll HAVE to get back to myself
+ // sometime again.
+ pFrame = pFrame->FindNext();
+ if( bNoSect && pFrame && pFrame->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pCnt )
+ pFrame = pCnt;
+ }
+ }
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone II)." );
+ if ( !GetUpper() )
+ return;
+
+ GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ OSL_ENSURE( GetUpper(), "Layout unstable (Upper gone III)." );
+ }
+ Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr);
+}
+
+// Here we return GetPrev(); however we will ignore empty SectionFrames
+static SwFrame* lcl_Prev( SwFrame* pFrame, bool bSectPrv = true )
+{
+ SwFrame* pRet = pFrame->GetPrev();
+ if( !pRet && pFrame->GetUpper() && pFrame->GetUpper()->IsSctFrame() &&
+ bSectPrv && !pFrame->IsColumnFrame() )
+ pRet = pFrame->GetUpper()->GetPrev();
+ while( pRet && pRet->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pRet)->GetSection() )
+ pRet = pRet->GetPrev();
+ return pRet;
+}
+
+static SwFrame* lcl_NotHiddenPrev( SwFrame* pFrame )
+{
+ SwFrame *pRet = pFrame;
+ do
+ {
+ pRet = lcl_Prev( pRet );
+ } while ( pRet && pRet->IsTextFrame() && static_cast<SwTextFrame*>(pRet)->IsHiddenNow() );
+ return pRet;
+}
+
+void SwFrame::MakePos()
+{
+ if ( isFrameAreaPositionValid() )
+ return;
+
+ setFrameAreaPositionValid(true);
+ bool bUseUpper = false;
+ SwFrame* pPrv = lcl_Prev( this );
+ if ( pPrv &&
+ ( !pPrv->IsContentFrame() ||
+ ( static_cast<SwContentFrame*>(pPrv)->GetFollow() != this ) )
+ )
+ {
+ if ( !StackHack::IsLocked() &&
+ ( !IsInSct() || IsSctFrame() ) &&
+ !pPrv->IsSctFrame() &&
+ !pPrv->GetAttrSet()->GetKeep().GetValue()
+ )
+ {
+ pPrv->Calc(getRootFrame()->GetCurrShell() ? getRootFrame()->GetCurrShell()->GetOut() : nullptr); // This may cause Prev to vanish!
+ }
+ else if ( pPrv->getFrameArea().Top() == 0 )
+ {
+ bUseUpper = true;
+ }
+ }
+
+ pPrv = lcl_Prev( this, false );
+ const SwFrameType nMyType = GetType();
+ SwRectFnSet aRectFnSet((IsCellFrame() && GetUpper() ? GetUpper() : this));
+ if ( !bUseUpper && pPrv )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+
+ if( FRM_NEIGHBOUR & nMyType )
+ {
+ const bool bR2L = IsRightToLeft();
+
+ if( bR2L )
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
+ }
+ else
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
+ }
+
+ // cells may now leave their uppers
+ if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
+ }
+ }
+ else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
+ {
+ if ( aRectFnSet.IsVertL2R() )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() + pPrv->getFrameArea().Width());
+ }
+ else
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
+ }
+ }
+ else
+ {
+ aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
+ }
+ }
+ else if ( GetUpper() )
+ {
+ // If parent frame is a footer frame and its <ColLocked()>, then
+ // do *not* calculate it.
+ // NOTE: Footer frame is <ColLocked()> during its
+ // <FormatSize(..)>, which is called from <Format(..)>, which
+ // is called from <MakeAll()>, which is called from <Calc()>.
+ // #i56850#
+ // - no format of upper Writer fly frame, which is anchored
+ // at-paragraph or at-character.
+ if ( !GetUpper()->IsTabFrame() &&
+ !( IsTabFrame() && GetUpper()->IsInTab() ) &&
+ !GetUpper()->IsSctFrame() &&
+ !dynamic_cast<SwFlyAtContentFrame*>(GetUpper()) &&
+ !( GetUpper()->IsFooterFrame() &&
+ GetUpper()->IsColLocked() )
+ )
+ {
+ GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ pPrv = lcl_Prev( this, false );
+ if ( !bUseUpper && pPrv )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+
+ if( FRM_NEIGHBOUR & nMyType )
+ {
+ const bool bR2L = IsRightToLeft();
+
+ if( bR2L )
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) - aRectFnSet.GetWidth(aFrm) );
+ }
+ else
+ {
+ aRectFnSet.SetPosX( aFrm, aRectFnSet.GetLeft(aFrm) + aRectFnSet.GetWidth(pPrv->getFrameArea()) );
+ }
+
+ // cells may now leave their uppers
+ if( aRectFnSet.IsVert() && SwFrameType::Cell & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + pPrv->getFrameArea().Width());
+ }
+ }
+ else if( aRectFnSet.IsVert() && FRM_NOTE_VERT & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width());
+ }
+ else
+ {
+ aFrm.Pos().setY(aFrm.Pos().getY() + pPrv->getFrameArea().Height());
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( GetUpper()->getFrameArea().Pos() );
+
+ if( GetUpper()->IsFlyFrame() )
+ {
+ aFrm.Pos() += static_cast<SwFlyFrame*>(GetUpper())->ContentPos();
+ }
+ else
+ {
+ aFrm.Pos() += GetUpper()->getFramePrintArea().Pos();
+ }
+
+ if( FRM_NEIGHBOUR & nMyType && IsRightToLeft() )
+ {
+ if( aRectFnSet.IsVert() )
+ {
+ aFrm.Pos().setY(aFrm.Pos().getY() + GetUpper()->getFramePrintArea().Height() - aFrm.Height());
+ }
+ else
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
+ }
+ }
+ else if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && FRM_NOTE_VERT & nMyType )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() - aFrm.Width() + GetUpper()->getFramePrintArea().Width());
+ }
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(0);
+ aFrm.Pos().setY(0);
+ }
+
+ if( IsBodyFrame() && aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() && GetUpper() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(aFrm.Pos().getX() + GetUpper()->getFramePrintArea().Width() - aFrm.Width());
+ }
+
+ setFrameAreaPositionValid(true);
+}
+
+// #i28701# - new type <SwSortedObjs>
+static void lcl_CheckObjects(SwSortedObjs& rSortedObjs, const SwFrame* pFrame, tools::Long& rBot)
+{
+ // And then there can be paragraph anchored frames that sit below their paragraph.
+ tools::Long nMax = 0;
+ for (SwAnchoredObject* pObj : rSortedObjs)
+ {
+ // #i28701# - consider changed type of <SwSortedObjs>
+ // entries.
+ tools::Long nTmp = 0;
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if( pFly->getFrameArea().Top() != FAR_AWAY &&
+ ( pFrame->IsPageFrame() ? pFly->IsFlyLayFrame() :
+ ( pFly->IsFlyAtContentFrame() &&
+ ( pFrame->IsBodyFrame() ? pFly->GetAnchorFrame()->IsInDocBody() :
+ pFly->GetAnchorFrame()->IsInFootnote() ) ) ) )
+ {
+ nTmp = pFly->getFrameArea().Bottom();
+ }
+ }
+ else
+ nTmp = pObj->GetObjRect().Bottom();
+ nMax = std::max( nTmp, nMax );
+ }
+ ++nMax; // Lower edge vs. height!
+ rBot = std::max( rBot, nMax );
+}
+
+size_t SwPageFrame::GetContentHeight(const tools::Long nTop, const tools::Long nBottom) const
+{
+ OSL_ENSURE(!(FindBodyCont() && FindBodyCont()->Lower() && FindBodyCont()->Lower()->IsColumnFrame()),
+ "SwPageFrame::GetContentHeight(): No support for columns.");
+
+ // In pages without columns, the content defines the size.
+ tools::Long nBot = getFrameArea().Top() + nTop;
+ const SwFrame *pFrame = Lower();
+ while (pFrame)
+ {
+ tools::Long nTmp = 0;
+ const SwFrame *pCnt = static_cast<const SwLayoutFrame*>(pFrame)->ContainsAny();
+ while (pCnt && (pCnt->GetUpper() == pFrame ||
+ static_cast<const SwLayoutFrame*>(pFrame)->IsAnLower(pCnt)))
+ {
+ nTmp += pCnt->getFrameArea().Height();
+ if (pCnt->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pCnt)->IsUndersized())
+ {
+ // This TextFrame would like to be a bit bigger.
+ nTmp += static_cast<const SwTextFrame*>(pCnt)->GetParHeight()
+ - pCnt->getFramePrintArea().Height();
+ }
+ else if (pCnt->IsSctFrame())
+ {
+ // Grow if undersized, but don't shrink if oversized.
+ const auto delta = static_cast<const SwSectionFrame*>(pCnt)->CalcUndersize();
+ if (delta > 0)
+ nTmp += delta;
+ }
+
+ pCnt = pCnt->FindNext();
+ }
+ // Consider invalid body frame properties
+ if (pFrame->IsBodyFrame() &&
+ (!pFrame->isFrameAreaSizeValid() ||
+ !pFrame->isFramePrintAreaValid()) &&
+ (pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height())
+ )
+ {
+ nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
+ }
+ else
+ {
+ // Assert invalid lower property
+ OSL_ENSURE(!(pFrame->getFrameArea().Height() < pFrame->getFramePrintArea().Height()),
+ "SwPageFrame::GetContentHeight(): Lower with frame height < printing height");
+ nTmp += pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
+ }
+ if (!pFrame->IsBodyFrame())
+ nTmp = std::min(nTmp, pFrame->getFrameArea().Height());
+ nBot += nTmp;
+ // Here we check whether paragraph anchored objects
+ // protrude outside the Body/FootnoteCont.
+ if (m_pSortedObjs && !pFrame->IsHeaderFrame() &&
+ !pFrame->IsFooterFrame())
+ lcl_CheckObjects(*m_pSortedObjs, pFrame, nBot);
+ pFrame = pFrame->GetNext();
+ }
+ nBot += nBottom;
+ // And the page anchored ones
+ if (m_pSortedObjs)
+ lcl_CheckObjects(*m_pSortedObjs, this, nBot);
+ nBot -= getFrameArea().Top();
+
+ return nBot;
+}
+
+void SwPageFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ const SwRect aOldRect( getFrameArea() ); // Adjust root size
+ const SwLayNotify aNotify( this ); // takes care of the notification in the dtor
+ std::optional<SwBorderAttrAccess> oAccess;
+ const SwBorderAttrs*pAttrs = nullptr;
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( !isFrameAreaPositionValid() )
+ {
+ setFrameAreaPositionValid(true); // positioning of the pages is taken care of by the root frame
+ }
+
+ if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( IsEmptyPage() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( 0 );
+ aFrm.Height( 0 );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( 0 );
+ aPrt.Height( 0 );
+ aPrt.Left( 0 );
+ aPrt.Top( 0 );
+
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ else
+ {
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ assert(pAttrs);
+
+ SwRootFrame* pRootFrame = getRootFrame();
+ SwViewShell* pSh = pRootFrame->GetCurrShell();
+ if (pSh && pSh->GetViewOptions()->getBrowseMode())
+ {
+ // In BrowseView, we use fixed settings
+ const Size aBorder = pRenderContext->PixelToLogic( pSh->GetBrowseBorder() );
+ const tools::Long nTop = pAttrs->CalcTopLine() + aBorder.Height();
+ const tools::Long nBottom = pAttrs->CalcBottomLine()+ aBorder.Height();
+
+ tools::Long nWidth = GetUpper() ? static_cast<SwRootFrame*>(GetUpper())->GetBrowseWidth() : 0;
+ const auto nDefWidth = pSh->GetBrowseWidth();
+ if (nWidth < nDefWidth)
+ nWidth = nDefWidth;
+ nWidth += + 2 * aBorder.Width();
+
+ constexpr tools::Long constTwips_2cm = o3tl::toTwips(2, o3tl::Length::cm);
+ nWidth = std::max(nWidth, 2L * aBorder.Width() + constTwips_2cm);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( nWidth );
+
+ SwLayoutFrame *pBody = FindBodyCont();
+ if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ // Columns have a fixed height
+ aFrm.Height( pAttrs->GetSize().Height() );
+ }
+ else
+ {
+ // In pages without columns, the content defines the size.
+ tools::Long nBot = GetContentHeight(nTop, nBottom);
+
+ // #i35143# - If second page frame
+ // exists, the first page doesn't have to fulfill the
+ // visible area.
+ if ( !GetPrev() && !GetNext() )
+ {
+ nBot = std::max( nBot, pSh->VisArea().Height() );
+ }
+ // #i35143# - Assure, that the page
+ // doesn't exceed the defined browse height.
+ aFrm.Height( std::min( nBot, BROWSE_HEIGHT ) );
+ }
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left ( pAttrs->CalcLeftLine() + aBorder.Width() );
+ aPrt.Top ( nTop );
+ aPrt.Width( getFrameArea().Width() - ( aPrt.Left() + pAttrs->CalcRightLine() + aBorder.Width() ) );
+ aPrt.Height( getFrameArea().Height() - (nTop + nBottom) );
+ }
+
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ continue;
+ }
+ else if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden() && pRootFrame->GetLastPage() != this)
+ {
+ tools::Long height = 0;
+ SwLayoutFrame *pBody = FindBodyCont();
+ if ( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ // Columns have a fixed height
+ height = pAttrs->GetSize().Height();
+ }
+ else
+ {
+ // No need for borders.
+ height = GetContentHeight(0, 0);
+ }
+
+ if (height > 0)
+ {
+ ChgSize(Size(getFrameArea().Width(), height));
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top(0);
+ aPrt.Height(height);
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ continue;
+ }
+
+ // Fallback to default formatting. Especially relevant
+ // when loading a doc when Hide Whitespace is enabled.
+ // Heights are zero initially.
+ }
+
+ // Set FixSize. For pages, this is not done from Upper, but from
+ // the attribute.
+ //FIXME: This resets the size when (isFrameAreaSizeValid() && !isFramePrintAreaValid()).
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.SSize( pAttrs->GetSize() );
+ }
+ Format( pRenderContext, pAttrs );
+ }
+ }
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+
+ if ( getFrameArea() != aOldRect && GetUpper() )
+ static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
+
+ OSL_ENSURE( !GetUpper() || GetUpper()->getFramePrintArea().Width() >= getFrameArea().Width(),
+ "Upper (Root) must be wide enough to contain the widest page");
+}
+
+void SwLayoutFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ // takes care of the notification in the dtor
+ const SwLayNotify aNotify( this );
+ bool bVert = IsVertical();
+
+ SwRectFn fnRect = ( IsNeighbourFrame() == bVert )? fnRectHori : ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
+
+ std::optional<SwBorderAttrAccess> oAccess;
+ const SwBorderAttrs*pAttrs = nullptr;
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( !isFrameAreaPositionValid() )
+ MakePos();
+
+ if ( GetUpper() )
+ {
+ // NEW TABLES
+ if ( IsLeaveUpperAllowed() )
+ {
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFramePrintAreaValid(false);
+ }
+ }
+ else
+ {
+ if ( !isFrameAreaSizeValid() )
+ {
+ // Set FixSize; VarSize is set by Format() after calculating the PrtArea
+ setFramePrintAreaValid(false);
+
+ SwTwips nPrtWidth = (GetUpper()->getFramePrintArea().*fnRect->fnGetWidth)();
+ if( bVert && ( IsBodyFrame() || IsFootnoteContFrame() ) )
+ {
+ SwFrame* pNxt = GetPrev();
+ while( pNxt && !pNxt->IsHeaderFrame() )
+ pNxt = pNxt->GetPrev();
+ if( pNxt )
+ nPrtWidth -= pNxt->getFrameArea().Height();
+ pNxt = GetNext();
+ while( pNxt && !pNxt->IsFooterFrame() )
+ pNxt = pNxt->GetNext();
+ if( pNxt )
+ nPrtWidth -= pNxt->getFrameArea().Height();
+ }
+
+ const tools::Long nDiff = nPrtWidth - (getFrameArea().*fnRect->fnGetWidth)();
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ // SwRectFn switched between horizontal and vertical when bVert == IsNeighbourFrame().
+ // We pick fnSubLeft or fnAddRight that is correspondant to SwRectFn->fnAddBottom
+ if( ( IsCellFrame() && IsRightToLeft() ) || ( IsColumnFrame() && bVert && !IsVertLR() ) )
+ {
+ (aFrm.*fnRect->fnSubLeft)( nDiff );
+ }
+ else
+ {
+ (aFrm.*fnRect->fnAddRight)( nDiff );
+ }
+ }
+ else
+ {
+ // Don't leave your upper
+ const SwTwips nDeadLine = (GetUpper()->*fnRect->fnGetPrtBottom)();
+ if( (getFrameArea().*fnRect->fnOverStep)( nDeadLine ) )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+ }
+ }
+
+ if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ if ( !oAccess )
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
+ }
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+}
+
+bool SwTextNode::IsCollapse() const
+{
+ if (GetDoc().GetDocumentSettingManager().get( DocumentSettingId::COLLAPSE_EMPTY_CELL_PARA )
+ && GetText().isEmpty())
+ {
+ SwNodeOffset nIdx=GetIndex();
+ const SwEndNode *pNdBefore=GetNodes()[nIdx-1]->GetEndNode();
+ const SwEndNode *pNdAfter=GetNodes()[nIdx+1]->GetEndNode();
+
+ // The paragraph is collapsed only if the NdAfter is the end of a cell
+ bool bInTable = FindTableNode( ) != nullptr;
+
+ SwSortedObjs* pObjs = getLayoutFrame( GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() )->GetDrawObjs( );
+ const size_t nObjs = ( pObjs != nullptr ) ? pObjs->size( ) : 0;
+
+ return pNdBefore!=nullptr && pNdAfter!=nullptr && nObjs == 0 && bInTable;
+ }
+
+ return false;
+}
+
+bool SwFrame::IsCollapse() const
+{
+ if (!IsTextFrame())
+ return false;
+
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame*>(this);
+ const SwTextNode *pTextNode = pTextFrame->GetTextNodeForParaProps();
+ // TODO this SwTextNode function is pointless and should be merged in here
+ return pTextFrame->GetText().isEmpty() && pTextNode && pTextNode->IsCollapse();
+}
+
+void SwContentFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
+{
+ if ( isFramePrintAreaValid() )
+ return;
+
+ setFramePrintAreaValid(true);
+ SwRectFnSet aRectFnSet(this);
+ const bool bTextFrame = IsTextFrame();
+ SwTwips nUpper = 0;
+ if ( bTextFrame && static_cast<SwTextFrame*>(this)->IsHiddenNow() )
+ {
+ if ( static_cast<SwTextFrame*>(this)->HasFollow() )
+ static_cast<SwTextFrame*>(this)->JoinFrame();
+
+ if( aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ static_cast<SwTextFrame*>(this)->HideHidden();
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Pos().setX(0);
+ aPrt.Pos().setY(0);
+ aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ nUpper = -( aRectFnSet.GetHeight(getFrameArea()) );
+ }
+ else
+ {
+ // Simplification: ContentFrames are always variable in height!
+
+ // At the FixSize, the surrounding Frame enforces the size;
+ // the borders are simply subtracted.
+ const tools::Long nLeft = rAttrs.CalcLeft( this );
+ const tools::Long nRight = rAttrs.CalcRight( this );
+ aRectFnSet.SetXMargins( *this, nLeft, nRight );
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwTwips nWidthArea;
+ if( pSh && 0!=(nWidthArea=aRectFnSet.GetWidth(pSh->VisArea())) &&
+ GetUpper()->IsPageBodyFrame() && // but not for BodyFrames in Columns
+ pSh->GetViewOptions()->getBrowseMode() )
+ {
+ // Do not protrude the edge of the visible area. The page may be
+ // wider, because there may be objects with excess width
+ // (RootFrame::ImplCalcBrowseWidth())
+ tools::Long nMinWidth = 0;
+
+ for (size_t i = 0; GetDrawObjs() && i < GetDrawObjs()->size(); ++i)
+ {
+ // #i28701# - consider changed type of
+ // <SwSortedObjs> entries
+ SwAnchoredObject* pObj = (*GetDrawObjs())[i];
+ const SwFrameFormat& rFormat = pObj->GetFrameFormat();
+ const bool bFly = pObj->DynCastFlyFrame() != nullptr;
+ if ((bFly && (FAR_AWAY == pObj->GetObjRect().Width()))
+ || rFormat.GetFrameSize().GetWidthPercent())
+ {
+ continue;
+ }
+
+ if ( RndStdIds::FLY_AS_CHAR == rFormat.GetAnchor().GetAnchorId() )
+ {
+ nMinWidth = std::max( nMinWidth,
+ bFly ? rFormat.GetFrameSize().GetWidth()
+ : pObj->GetObjRect().Width() );
+ }
+ }
+
+ const Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
+ tools::Long nWidth = nWidthArea - 2 * ( IsVertical() ? aBorder.Height() : aBorder.Width() );
+ nWidth -= aRectFnSet.GetLeft(getFramePrintArea());
+ nWidth -= rAttrs.CalcRightLine();
+ nWidth = std::max( nMinWidth, nWidth );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, std::min( nWidth, aRectFnSet.GetWidth(aPrt) ) );
+ }
+
+ if ( aRectFnSet.GetWidth(getFramePrintArea()) <= MINLAY )
+ {
+ // The PrtArea should already be at least MINLAY wide, matching the
+ // minimal values of the UI
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, std::min( tools::Long(MINLAY), aRectFnSet.GetWidth(getFrameArea()) ) );
+ SwTwips nTmp = aRectFnSet.GetWidth(getFrameArea()) - aRectFnSet.GetWidth(aPrt);
+
+ if( aRectFnSet.GetLeft(aPrt) > nTmp )
+ {
+ aRectFnSet.SetLeft( aPrt, nTmp );
+ }
+ }
+
+ // The following rules apply for VarSize:
+ // 1. The first entry of a chain has no top border
+ // 2. There is never a bottom border
+ // 3. The top border is the maximum of the distance
+ // of Prev downwards and our own distance upwards
+ // Those three rules apply when calculating spacings
+ // that are given by UL- and LRSpace. There might be a spacing
+ // in all directions however; this may be caused by borders
+ // and / or shadows.
+ // 4. The spacing for TextFrames corresponds to the interline lead,
+ // at a minimum.
+
+ nUpper = CalcUpperSpace( &rAttrs );
+
+ SwTwips nLower = CalcLowerSpace( &rAttrs );
+ if (IsCollapse()) {
+ nUpper=0;
+ nLower=0;
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetPosY( aPrt, !aRectFnSet.IsVert() ? nUpper : nLower);
+ }
+
+ nUpper += nLower;
+ nUpper -= aRectFnSet.GetHeight(getFrameArea()) - aRectFnSet.GetHeight(getFramePrintArea());
+ }
+ // If there's a difference between old and new size, call Grow() or
+ // Shrink() respectively.
+ if ( nUpper )
+ {
+ if ( nUpper > 0 )
+ GrowFrame( nUpper );
+ else
+ ShrinkFrame( -nUpper );
+ }
+}
+
+#define STOP_FLY_FORMAT 10
+// - loop prevention
+const int cnStopFormat = 15;
+
+inline void ValidateSz( SwFrame *pFrame )
+{
+ if ( pFrame )
+ {
+ pFrame->setFrameAreaSizeValid(true);
+ pFrame->setFramePrintAreaValid(true);
+ }
+}
+
+void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ OSL_ENSURE( GetUpper(), "no Upper?" );
+ OSL_ENSURE( IsTextFrame(), "MakeAll(), NoText" );
+
+ if ( !IsFollow() && StackHack::IsLocked() )
+ return;
+
+ if ( IsJoinLocked() )
+ return;
+
+ OSL_ENSURE( !static_cast<SwTextFrame*>(this)->IsSwapped(), "Calculation of a swapped frame" );
+
+ StackHack aHack;
+
+ if ( static_cast<SwTextFrame*>(this)->IsLocked() )
+ {
+ OSL_FAIL( "Format for locked TextFrame." );
+ return;
+ }
+
+ std::optional<SwFrameDeleteGuard> oDeleteGuard(std::in_place, this);
+ LockJoin();
+ tools::Long nFormatCount = 0;
+ // - loop prevention
+ int nConsecutiveFormatsWithoutChange = 0;
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ // takes care of the notification in the dtor
+ std::optional<SwContentNotify> oNotify( std::in_place, this );
+
+ // as long as bMakePage is true, a new page can be created (exactly once)
+ bool bMakePage = true;
+ // bMovedBwd gets set to true when the frame flows backwards
+ bool bMovedBwd = false;
+ // as long as bMovedFwd is false, the Frame may flow backwards (until
+ // it has been moved forward once)
+ bool bMovedFwd = false;
+ sal_Bool bFormatted = false; // For the widow/orphan rules, we encourage the
+ // last ContentFrame of a chain to format. This only
+ // needs to happen once. Every time the Frame is
+ // moved, the flag will have to be reset.
+ bool bMustFit = false; // Once the emergency brake is pulled,
+ // no other prepares will be triggered
+ bool bFitPromise = false; // If a paragraph didn't fit, but promises
+ // with WouldFit that it would adjust accordingly,
+ // this flag is set. If it turns out that it
+ // didn't keep it's promise, we can act in a
+ // controlled fashion.
+ const bool bFly = IsInFly();
+ const bool bTab = IsInTab();
+ const bool bFootnote = IsInFootnote();
+ const bool bSct = IsInSct();
+ Point aOldFramePos; // This is so we can compare with the last pos
+ Point aOldPrtPos; // and determine whether it makes sense to Prepare
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ if ( !IsFollow() && rAttrs.JoinedWithPrev( *(this) ) )
+ {
+ oNotify->SetBordersJoinedWithPrev();
+ }
+
+ const bool bKeep = IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem());
+
+ std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote;
+ if ( bFootnote )
+ {
+ SwFootnoteFrame *pFootnote = FindFootnoteFrame();
+ SwSectionFrame* pSct = pFootnote->FindSctFrame();
+ if ( !static_cast<SwTextFrame*>(pFootnote->GetRef())->IsLocked() )
+ {
+ SwFootnoteBossFrame* pBoss = pFootnote->GetRef()->FindFootnoteBossFrame(
+ pFootnote->GetAttr()->GetFootnote().IsEndNote() );
+ if( !pSct || pSct->IsColLocked() || !pSct->Growable() )
+ pSaveFootnote.reset( new SwSaveFootnoteHeight( pBoss,
+ static_cast<SwTextFrame*>(pFootnote->GetRef())->GetFootnoteLine( pFootnote->GetAttr() ) ) );
+ }
+ }
+
+ if ( GetUpper()->IsSctFrame() &&
+ HasFollow() && !GetFollow()->IsDeleteForbidden() &&
+ &GetFollow()->GetFrame() == GetNext() )
+ {
+ static_cast<SwTextFrame&>(*this).JoinFrame();
+ }
+
+ // #i28701# - move master forward, if it has to move,
+ // because of its object positioning.
+ if ( !static_cast<SwTextFrame*>(this)->IsFollow() )
+ {
+ sal_uInt32 nToPageNum = 0;
+ const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
+ *(GetAttrSet()->GetDoc()),
+ *static_cast<SwTextFrame*>(this),
+ nToPageNum );
+ // #i58182#
+ // Also move a paragraph forward, which is the first one inside a table cell.
+ if ( bMoveFwdByObjPos &&
+ FindPageFrame()->GetPhyPageNum() < nToPageNum &&
+ ( lcl_Prev( this ) ||
+ GetUpper()->IsCellFrame() ||
+ ( GetUpper()->IsSctFrame() &&
+ GetUpper()->GetUpper()->IsCellFrame() ) ) &&
+ IsMoveable() )
+ {
+ bMovedFwd = true;
+ MoveFwd( bMakePage, false );
+ }
+ }
+
+ // If a Follow sits next to its Master and doesn't fit, we know it can
+ // be moved right now.
+ if ( lcl_Prev( this ) && static_cast<SwTextFrame*>(this)->IsFollow() && IsMoveable() )
+ {
+ bMovedFwd = true;
+ // If follow frame is in table, its master will be the last in the
+ // current table cell. Thus, invalidate the printing area of the master.
+ if ( IsInTab() )
+ {
+ lcl_Prev( this )->InvalidatePrt();
+ }
+ MoveFwd( bMakePage, false );
+ }
+
+ // Check footnote content for forward move.
+ // If a content of a footnote is on a prior page/column as its invalid
+ // reference, it can be moved forward.
+ if ( bFootnote && !isFrameAreaPositionValid() )
+ {
+ SwFootnoteFrame* pFootnote = FindFootnoteFrame();
+ SwContentFrame* pRefCnt = pFootnote ? pFootnote->GetRef() : nullptr;
+
+ if ( pRefCnt && !pRefCnt->isFrameAreaDefinitionValid() )
+ {
+ SwFootnoteBossFrame* pFootnoteBossOfFootnote = pFootnote->FindFootnoteBossFrame();
+ SwFootnoteBossFrame* pFootnoteBossOfRef = pRefCnt->FindFootnoteBossFrame();
+ //<loop of movefwd until condition held or no move>
+ if ( pFootnoteBossOfFootnote && pFootnoteBossOfRef &&
+ pFootnoteBossOfFootnote != pFootnoteBossOfRef &&
+ pFootnoteBossOfFootnote->IsBefore( pFootnoteBossOfRef ) )
+ {
+ bMovedFwd = true;
+ MoveFwd( bMakePage, false );
+ }
+ }
+ }
+
+ SwRectFnSet aRectFnSet(this);
+
+ SwFrame const* pMoveBwdPre(nullptr);
+ bool isMoveBwdPreValid(false);
+
+ SwRect aOldFrame_StopFormat, aOldFrame_StopFormat2;
+ SwRect aOldPrt_StopFormat, aOldPrt_StopFormat2;
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ // - loop prevention
+ aOldFrame_StopFormat2 = aOldFrame_StopFormat;
+ aOldPrt_StopFormat2 = aOldPrt_StopFormat;
+ aOldFrame_StopFormat = getFrameArea();
+ aOldPrt_StopFormat = getFramePrintArea();
+
+ bool bMoveable = IsMoveable();
+ if (bMoveable)
+ {
+ SwFrame *pPre = GetIndPrev();
+ if ( CheckMoveFwd( bMakePage, bKeep, bMovedBwd ) )
+ {
+ aRectFnSet.Refresh(this);
+ bMovedFwd = true;
+ if ( bMovedBwd )
+ {
+ // While flowing back, the Upper was encouraged to
+ // completely re-paint itself. We can skip this now after
+ // flowing back and forth.
+ GetUpper()->ResetCompletePaint();
+ // The predecessor was invalidated, so this is obsolete as well now.
+ assert(pPre);
+ if ((pPre == pMoveBwdPre && isMoveBwdPreValid) && !pPre->IsSctFrame())
+ ::ValidateSz( pPre );
+ }
+ bMoveable = IsMoveable();
+ }
+ }
+
+ aOldFramePos = aRectFnSet.GetPos(getFrameArea());
+ aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
+
+ if ( !isFrameAreaPositionValid() )
+ MakePos();
+
+ //Set FixSize. VarSize is being adjusted by Format().
+ if ( !isFrameAreaSizeValid() )
+ {
+ // invalidate printing area flag, if the following conditions are hold:
+ // - current frame width is 0.
+ // - current printing area width is 0.
+ // - frame width is adjusted to a value greater than 0.
+ // - printing area flag is true.
+ // Thus, it's assured that the printing area is adjusted, if the
+ // frame area width changes its width from 0 to something greater
+ // than 0.
+ // Note: A text frame can be in such a situation, if the format is
+ // triggered by method call <SwCursorShell::SetCursor()> after
+ // loading the document.
+ const SwTwips nNewFrameWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+
+ if ( isFramePrintAreaValid() &&
+ nNewFrameWidth > 0 &&
+ aRectFnSet.GetWidth(getFrameArea()) == 0 &&
+ aRectFnSet.GetWidth(getFramePrintArea()) == 0 )
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, nNewFrameWidth );
+ }
+
+ // When a lower of a vertically aligned fly frame changes its size we need to recalculate content pos.
+ if( GetUpper() && GetUpper()->IsFlyFrame() &&
+ GetUpper()->GetFormat()->GetTextVertAdjust().GetValue() != SDRTEXTVERTADJUST_TOP )
+ {
+ static_cast<SwFlyFrame*>(GetUpper())->InvalidateContentPos();
+ GetUpper()->SetCompletePaint();
+ }
+ }
+ if ( !isFramePrintAreaValid() )
+ {
+ const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
+ // #i34730# - keep current frame height
+ const SwTwips nOldH = aRectFnSet.GetHeight(getFrameArea());
+ MakePrtArea( rAttrs );
+ if ( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
+ Prepare( PrepareHint::FixSizeChanged );
+ // #i34730# - check, if frame height has changed.
+ // If yes, send a PrepareHint::AdjustSizeWithoutFormatting and invalidate the size flag to
+ // force a format. The format will check in its method
+ // <SwTextFrame::CalcPreps()>, if the already formatted lines still
+ // fit and if not, performs necessary actions.
+ // #i40150# - no check, if frame is undersized.
+ if ( isFrameAreaSizeValid() && !IsUndersized() && nOldH != aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ // #115759# - no PrepareHint::AdjustSizeWithoutFormatting and size
+ // invalidation, if height decreases only by the additional
+ // lower space as last content of a table cell and an existing
+ // follow containing one line exists.
+ const SwTwips nHDiff = nOldH - aRectFnSet.GetHeight(getFrameArea());
+ const bool bNoPrepAdjustFrame =
+ nHDiff > 0 && IsInTab() && GetFollow() &&
+ (1 == static_cast<SwTextFrame*>(GetFollow())->GetLineCount(TextFrameIndex(COMPLETE_STRING))
+ || aRectFnSet.GetWidth(static_cast<SwTextFrame*>(GetFollow())->getFrameArea()) < 0) &&
+ GetFollow()->CalcAddLowerSpaceAsLastInTableCell() == nHDiff;
+ if ( !bNoPrepAdjustFrame )
+ {
+ Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ setFrameAreaSizeValid(false);
+ }
+ }
+ }
+
+ // To make the widow and orphan rules work, we need to notify the ContentFrame.
+ // Criteria:
+ // - It needs to be movable (otherwise, splitting doesn't make sense)
+ // - It needs to overlap with the lower edge of the PrtArea of the Upper
+ if ( !bMustFit )
+ {
+ bool bWidow = true;
+ const SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ if( bMoveable && !bFormatted &&
+ ( GetFollow() || aRectFnSet.OverStep( getFrameArea(), nDeadLine ) ) )
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ setFrameAreaSizeValid(false);
+ bWidow = false;
+ }
+ if( aRectFnSet.GetPos(getFrameArea()) != aOldFramePos ||
+ aRectFnSet.GetPos(getFramePrintArea()) != aOldPrtPos )
+ {
+ // In this Prepare, an InvalidateSize_() might happen.
+ // isFrameAreaSizeValid() becomes false and Format() gets called.
+ Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
+ if ( bWidow && GetFollow() )
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ setFrameAreaSizeValid(false);
+ }
+ }
+ }
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+ bFormatted = true;
+ ++nFormatCount;
+ if( nFormatCount > STOP_FLY_FORMAT )
+ SetFlyLock( true );
+ // - loop prevention
+ // No format any longer, if <cnStopFormat> consecutive formats
+ // without change occur.
+ if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
+ {
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "debug assertion: <SwContentFrame::MakeAll()> - format of text frame suppressed by fix b6448963" );
+ }
+#endif
+ }
+
+ // If this is the first one in a chain, check if this can flow
+ // backwards (if this is movable at all).
+ // To prevent oscillations/loops, check that this has not just
+ // flowed forwards.
+ bool bDummy;
+ auto const pTemp(GetIndPrev());
+ auto const bTemp(pTemp && pTemp->isFrameAreaSizeValid()
+ && pTemp->isFramePrintAreaValid());
+ if ( !lcl_Prev( this ) &&
+ !bMovedFwd &&
+ ( bMoveable || ( bFly && !bTab ) ) &&
+ ( !bFootnote || !GetUpper()->FindFootnoteFrame()->GetPrev() )
+ && MoveBwd( bDummy ) )
+ {
+ aRectFnSet.Refresh(this);
+ pMoveBwdPre = pTemp;
+ isMoveBwdPreValid = bTemp;
+ bMovedBwd = true;
+ bFormatted = false;
+ if ( bKeep && bMoveable )
+ {
+ if( CheckMoveFwd( bMakePage, false, bMovedBwd ) )
+ {
+ bMovedFwd = true;
+ bMoveable = IsMoveable();
+ aRectFnSet.Refresh(this);
+ }
+ Point aOldPos = aRectFnSet.GetPos(getFrameArea());
+ MakePos();
+ if( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
+ {
+ Prepare( PrepareHint::FramePositionChanged, static_cast<const void*>(&bFormatted), false );
+ if ( !isFrameAreaSizeValid() )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) );
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ const tools::Long nOldW = aRectFnSet.GetWidth(getFramePrintArea());
+ MakePrtArea( rAttrs );
+ if( nOldW != aRectFnSet.GetWidth(getFramePrintArea()) )
+ Prepare( PrepareHint::FixSizeChanged, nullptr, false );
+ }
+ if( GetFollow() )
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ }
+
+ setFrameAreaSizeValid(true);
+ bFormatted = true;
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ }
+ SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
+ while( pNxt && pNxt->IsSctFrame() )
+ { // Leave empty sections out, go into the other ones.
+ if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ if( pTmp )
+ {
+ pNxt = pTmp;
+ break;
+ }
+ }
+ pNxt = pNxt->FindNext();
+ }
+ if ( pNxt )
+ {
+ pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ if( isFrameAreaPositionValid() && !GetIndNext() )
+ {
+ SwSectionFrame *pSct = FindSctFrame();
+ if( pSct && !pSct->isFrameAreaSizeValid() )
+ {
+ SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
+ if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+ else
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+ }
+ }
+ }
+
+ // In footnotes, the TextFrame may validate itself, which can lead to the
+ // situation that it's position is wrong despite being "valid".
+ if ( isFrameAreaPositionValid() )
+ {
+ // #i59341#
+ // Workaround for inadequate layout algorithm:
+ // suppress invalidation and calculation of position, if paragraph
+ // has formatted itself at least STOP_FLY_FORMAT times and
+ // has anchored objects.
+ // Thus, the anchored objects get the possibility to format itself
+ // and this probably solve the layout loop.
+ if ( bFootnote &&
+ nFormatCount <= STOP_FLY_FORMAT &&
+ !GetDrawObjs() )
+ {
+ setFrameAreaPositionValid(false);
+ MakePos();
+ aOldFramePos = aRectFnSet.GetPos(getFrameArea());
+ aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
+ }
+ }
+
+ // - loop prevention
+ {
+ if ( (aOldFrame_StopFormat == getFrameArea() || aOldFrame_StopFormat2 == getFrameArea() ) &&
+ (aOldPrt_StopFormat == getFramePrintArea() || aOldPrt_StopFormat2 == getFramePrintArea()))
+ {
+ ++nConsecutiveFormatsWithoutChange;
+ }
+ else
+ {
+ nConsecutiveFormatsWithoutChange = 0;
+ }
+ }
+
+ // Yet again an invalid value? Repeat from the start...
+ if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ continue;
+
+ // Done?
+ // Attention: because height == 0, it's better to use Top()+Height() instead of
+ // Bottom(). This might happen with undersized TextFrames on the lower edge of a
+ // multi-column section
+ const tools::Long nPrtBottom = aRectFnSet.GetPrtBottom(*GetUpper());
+ tools::Long nBottomDist = aRectFnSet.BottomDist(getFrameArea(), nPrtBottom);
+
+ // Hide whitespace may require not to insert a new page.
+ SwPageFrame* pPageFrame = FindPageFrame();
+ const bool bHeightValid = pPageFrame->CheckPageHeightValidForHideWhitespace(nBottomDist);
+ if (!bHeightValid)
+ {
+ pPageFrame->InvalidateSize();
+ nBottomDist = 0;
+ }
+
+ if( nBottomDist >= 0 )
+ {
+ if ( bKeep && bMoveable )
+ {
+ // We make sure the successor will be formatted the same.
+ // This way, we keep control until (almost) everything is stable,
+ // allowing us to avoid endless loops caused by ever repeating
+ // retries.
+
+ // bMoveFwdInvalid is required for #38407#. This was originally solved
+ // in flowfrm.cxx rev 1.38, but broke the above schema and
+ // preferred to play towers of hanoi (#43669#).
+ SwFrame *pNxt = HasFollow() ? nullptr : FindNext();
+ // For sections we prefer the content, because it can change
+ // the page if required.
+ while( pNxt && pNxt->IsSctFrame() )
+ {
+ if( static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ break;
+ }
+ pNxt = pNxt->FindNext();
+ }
+ if ( pNxt )
+ {
+ const bool bMoveFwdInvalid = nullptr != GetIndNext();
+ const bool bNxtNew =
+ ( 0 == aRectFnSet.GetHeight(pNxt->getFramePrintArea()) ) &&
+ (!pNxt->IsTextFrame() ||!static_cast<SwTextFrame*>(pNxt)->IsHiddenNow());
+
+ pNxt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ if ( !bMovedBwd &&
+ ((bMoveFwdInvalid && !GetIndNext()) ||
+ bNxtNew) )
+ {
+ if( bMovedFwd )
+ oNotify->SetInvaKeep();
+ bMovedFwd = false;
+ }
+ }
+ }
+ continue;
+ }
+
+ // I don't fit into my parents, so it's time to make changes
+ // as constructively as possible.
+
+ //If I'm NOT allowed to leave the parent Frame, I've got a problem.
+ // Following Arthur Dent, we do the only thing that you can do with
+ // an unsolvable problem: We ignore it with all our power.
+ if ( !bMoveable || IsUndersized() )
+ {
+ break;
+ }
+
+ // If there's no way I can make myself fit into my Upper, the situation
+ // could still probably be mitigated by splitting up.
+ // This situation arises with freshly created Follows that had been moved
+ // to the next page but is still too big for it - ie. needs to be split
+ // as well.
+
+ // If I'm unable to split (WouldFit()) and can't be fitted, I'm going
+ // to tell my TextFrame part that, if possible, we still need to split despite
+ // the "don't split" attribute.
+ bool bMoveOrFit = false;
+ bool bDontMoveMe = !GetIndPrev();
+ if( bDontMoveMe && IsInSct() )
+ {
+ SwFootnoteBossFrame* pBoss = FindFootnoteBossFrame();
+ bDontMoveMe = !pBoss->IsInSct() ||
+ ( !pBoss->Lower()->GetNext() && !pBoss->GetPrev() );
+ }
+
+ // Finally, we are able to split table rows. Therefore, bDontMoveMe
+ // can be set to false:
+ if( bDontMoveMe && IsInTab() &&
+ nullptr != GetNextCellLeaf() )
+ bDontMoveMe = false;
+
+ assert(bMoveable);
+
+ if ( bDontMoveMe && aRectFnSet.GetHeight(getFrameArea()) >
+ aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) )
+ {
+ if ( !bFitPromise )
+ {
+ SwTwips nTmp = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -
+ aRectFnSet.GetTop(getFramePrintArea());
+ bool bSplit = !IsFwdMoveAllowed();
+ if (nTmp > 0 && WouldFit(nTmp, bSplit, false, false))
+ {
+ Prepare( PrepareHint::WidowsOrphans, nullptr, false );
+ setFrameAreaSizeValid(false);
+ bFitPromise = true;
+ continue;
+ }
+ /*
+ * In earlier days, we never tried to fit TextFrames in
+ * frames and sections using bMoveOrFit by ignoring
+ * its attributes (Widows, Keep).
+ * This should have been done at least for column frames;
+ * as it must be tried anyway with linked frames and sections.
+ * Exception: If we sit in FormatWidthCols, we must not ignore
+ * the attributes.
+ */
+ else if ( !bFootnote &&
+ ( !bFly || !FindFlyFrame()->IsColLocked() ) &&
+ ( !bSct || !FindSctFrame()->IsColLocked() ) )
+ bMoveOrFit = true;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "+TextFrame didn't respect WouldFit promise." );
+ }
+#endif
+ }
+
+ // Let's see if I can find some space somewhere...
+ // footnotes in the neighbourhood are moved into _MoveFootnoteCntFwd
+ SwFrame *pPre = GetIndPrev();
+ SwFrame *pOldUp = GetUpper();
+
+/* MA 13. Oct. 98: What is this supposed to be!?
+ * AMA 14. Dec 98: If a column section can't find any space for its first ContentFrame, it should be
+ * moved not only to the next column, but probably even to the next page, creating
+ * a section-follow there.
+ */
+ if( IsInSct() && bMovedFwd && bMakePage && pOldUp->IsColBodyFrame() &&
+ pOldUp->GetUpper()->GetUpper()->IsSctFrame() &&
+ ( pPre || pOldUp->GetUpper()->GetPrev() ) &&
+ static_cast<SwSectionFrame*>(pOldUp->GetUpper()->GetUpper())->MoveAllowed(this) )
+ {
+ bMovedFwd = false;
+ }
+
+ const bool bCheckForGrownBody = pOldUp->IsBodyFrame();
+ const tools::Long nOldBodyHeight = aRectFnSet.GetHeight(pOldUp->getFrameArea());
+
+ if ( !bMovedFwd && !MoveFwd( bMakePage, false ) )
+ bMakePage = false;
+ aRectFnSet.Refresh(this);
+ if (!bMovedFwd && bFootnote && GetIndPrev() != pPre)
+ { // SwFlowFrame::CutTree() could have formatted and deleted pPre
+ auto const pPrevFootnoteFrame(static_cast<SwFootnoteFrame const*>(
+ FindFootnoteFrame())->GetMaster());
+ bool bReset = true;
+ if (pPrevFootnoteFrame)
+ { // use GetIndNext() in case there are sections
+ for (auto p = pPrevFootnoteFrame->Lower(); p; p = p->GetIndNext())
+ {
+ if (p == pPre)
+ {
+ bReset = false;
+ break;
+ }
+ }
+ }
+ if (bReset)
+ {
+ pPre = nullptr;
+ }
+ }
+
+ // If MoveFwd moves the paragraph to the next page, a following
+ // paragraph, which contains footnotes can cause the old upper
+ // frame to grow. In this case we explicitly allow a new check
+ // for MoveBwd. Robust: We also check the bMovedBwd flag again.
+ // If pOldUp was a footnote frame, it has been deleted inside MoveFwd.
+ // Therefore we only check for growing body frames.
+ bMovedFwd = !bCheckForGrownBody || bMovedBwd || pOldUp == GetUpper() ||
+ aRectFnSet.GetHeight(pOldUp->getFrameArea()) <= nOldBodyHeight;
+
+ bFormatted = false;
+ if ( bMoveOrFit && GetUpper() == pOldUp )
+ {
+ // FME 2007-08-30 #i81146# new loop control
+ if ( nConsecutiveFormatsWithoutChange <= cnStopFormat )
+ {
+ Prepare( PrepareHint::MustFit, nullptr, false );
+ setFrameAreaSizeValid(false);
+ bMustFit = true;
+ continue;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_FAIL( "LoopControl in SwContentFrame::MakeAll" );
+#endif
+ }
+ if ( bMovedBwd && GetUpper() )
+ { // Retire invalidations that have become useless.
+ GetUpper()->ResetCompletePaint();
+ if( pPre && !pPre->IsSctFrame() )
+ ::ValidateSz( pPre );
+ }
+
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+
+ // NEW: Looping Louie (Light). Should not be applied in balanced sections.
+ // Should only be applied if there is no better solution!
+ LOOPING_LOUIE_LIGHT( bMovedFwd && bMovedBwd && !IsInBalancedSection() &&
+ (
+
+ ( bFootnote && !FindFootnoteFrame()->GetRef()->IsInSct() ) ||
+
+ // #i33887#
+ ( IsInSct() && bKeep )
+
+ // ... add your conditions here ...
+
+ ),
+ static_cast<SwTextFrame&>(*this) );
+
+ pSaveFootnote.reset();
+
+ UnlockJoin();
+ oDeleteGuard.reset();
+ if ( bMovedFwd || bMovedBwd )
+ oNotify->SetInvaKeep();
+ if ( bMovedFwd )
+ {
+ oNotify->SetInvalidatePrevPrtArea();
+ }
+ oNotify.reset();
+ SetFlyLock( false );
+}
+
+void MakeNxt( SwFrame *pFrame, SwFrame *pNxt )
+{
+ // fix(25455): Validate, otherwise this leads to a recursion.
+ // The first try, cancelling with pFrame = 0 if !Valid, leads to a problem, as
+ // the Keep may not be considered properly anymore (27417).
+ const bool bOldPos = pFrame->isFrameAreaPositionValid();
+ const bool bOldSz = pFrame->isFrameAreaSizeValid();
+ const bool bOldPrt = pFrame->isFramePrintAreaValid();
+ pFrame->setFrameAreaPositionValid(true);
+ pFrame->setFrameAreaSizeValid(true);
+ pFrame->setFramePrintAreaValid(true);
+
+ // fix(29272): Don't call MakeAll - there, pFrame might be invalidated again, and
+ // we recursively end up in here again.
+ if ( pNxt->IsContentFrame() )
+ {
+ SwContentNotify aNotify( static_cast<SwContentFrame*>(pNxt) );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ if ( !pNxt->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
+
+ if( pNxt->IsVertical() )
+ {
+ aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
+ }
+ else
+ {
+ aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
+ }
+ }
+ static_cast<SwContentFrame*>(pNxt)->MakePrtArea( rAttrs );
+ pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ }
+ else
+ {
+ SwLayNotify aNotify( static_cast<SwLayoutFrame*>(pNxt) );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNxt );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ if ( !pNxt->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNxt);
+
+ if( pNxt->IsVertical() )
+ {
+ aFrm.Height( pNxt->GetUpper()->getFramePrintArea().Height() );
+ }
+ else
+ {
+ aFrm.Width( pNxt->GetUpper()->getFramePrintArea().Width() );
+ }
+ }
+ pNxt->Format( pNxt->getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ }
+
+ pFrame->setFrameAreaPositionValid(bOldPos);
+ pFrame->setFrameAreaSizeValid(bOldSz);
+ pFrame->setFramePrintAreaValid(bOldPrt);
+}
+
+/// This routine checks whether there are no other FootnoteBosses
+/// between the pFrame's FootnoteBoss and the pNxt's FootnoteBoss.
+static bool lcl_IsNextFootnoteBoss( const SwFrame *pFrame, const SwFrame* pNxt )
+{
+ assert(pFrame && pNxt && "lcl_IsNextFootnoteBoss: No Frames?");
+ pFrame = pFrame->FindFootnoteBossFrame();
+ pNxt = pNxt->FindFootnoteBossFrame();
+ // If pFrame is a last column, we use the page instead.
+ while( pFrame && pFrame->IsColumnFrame() && !pFrame->GetNext() )
+ pFrame = pFrame->GetUpper()->FindFootnoteBossFrame();
+ // If pNxt is a first column, we use the page instead.
+ while( pNxt && pNxt->IsColumnFrame() && !pNxt->GetPrev() )
+ pNxt = pNxt->GetUpper()->FindFootnoteBossFrame();
+ // So... now pFrame and pNxt are either two adjacent pages or columns.
+ return pFrame && pNxt && pFrame->GetNext() == pNxt;
+}
+
+bool SwContentFrame::WouldFit_( SwTwips nSpace,
+ SwLayoutFrame *pNewUpper,
+ bool bTstMove,
+ const bool bObjsInNewUpper )
+{
+ // To have the footnote select its place carefully, it needs
+ // to be moved in any case if there is at least one page/column
+ // between the footnote and the new Upper.
+ SwFootnoteFrame* pFootnoteFrame = nullptr;
+ if ( IsInFootnote() )
+ {
+ if( !lcl_IsNextFootnoteBoss( pNewUpper, this ) )
+ return true;
+ pFootnoteFrame = FindFootnoteFrame();
+ }
+
+ bool bRet;
+ bool bSplit = !pNewUpper->Lower();
+ SwContentFrame *pFrame = this;
+ const SwFrame *pTmpPrev = pNewUpper->Lower();
+ if( pTmpPrev && pTmpPrev->IsFootnoteFrame() )
+ pTmpPrev = static_cast<const SwFootnoteFrame*>(pTmpPrev)->Lower();
+ while ( pTmpPrev && pTmpPrev->GetNext() )
+ pTmpPrev = pTmpPrev->GetNext();
+ do
+ {
+ // #i46181#
+ SwTwips nSecondCheck = 0;
+ SwTwips nOldSpace = nSpace;
+ bool bOldSplit = bSplit;
+
+ if ( bTstMove || IsInFly() || ( IsInSct() &&
+ ( pFrame->GetUpper()->IsColBodyFrame() || ( pFootnoteFrame &&
+ pFootnoteFrame->GetUpper()->GetUpper()->IsColumnFrame() ) ) ) )
+ {
+ // This is going to get a bit insidious now. If you're faint of heart,
+ // you'd better look away here. If a Fly contains columns, then the Contents
+ // are movable, except ones in the last column (see SwFrame::IsMoveable()).
+ // Of course they're allowed to float back. WouldFit() only returns a usable
+ // value if the Frame is movable. To fool WouldFit() into believing there's
+ // a movable Frame, I'm just going to hang it somewhere else for the time.
+ // The same procedure applies for column sections to make SwSectionFrame::Growable()
+ // return the proper value.
+ // Within footnotes, we may even need to put the SwFootnoteFrame somewhere else, if
+ // there's no SwFootnoteFrame there.
+ SwFrame* pTmpFrame = pFrame->IsInFootnote() && !pNewUpper->FindFootnoteFrame() ?
+ static_cast<SwFrame*>(pFrame->FindFootnoteFrame()) : pFrame;
+ SwLayoutFrame *pUp = pTmpFrame->GetUpper();
+ SwFrame *pOldNext = pTmpFrame->GetNext();
+ pTmpFrame->RemoveFromLayout();
+ pTmpFrame->InsertBefore( pNewUpper, nullptr );
+ // tdf#107126 for a section in a footnote, we have only inserted
+ // the SwTextFrame but no SwSectionFrame - reset mbInfSct flag
+ // to avoid crashing (but perhaps we should create a temp
+ // SwSectionFrame here because WidowsAndOrphans checks for that?)
+ pTmpFrame->InvalidateInfFlags();
+ if ( pFrame->IsTextFrame() &&
+ ( bTstMove ||
+ static_cast<SwTextFrame*>(pFrame)->HasFollow() ||
+ ( !static_cast<SwTextFrame*>(pFrame)->HasPara() &&
+ !static_cast<SwTextFrame*>(pFrame)->IsEmpty()
+ )
+ )
+ )
+ {
+ bTstMove = true;
+ bRet = static_cast<SwTextFrame*>(pFrame)->TestFormat( pTmpPrev, nSpace, bSplit );
+ }
+ else
+ bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
+
+ pTmpFrame->RemoveFromLayout();
+ pTmpFrame->InsertBefore( pUp, pOldNext );
+ pTmpFrame->InvalidateInfFlags(); // restore flags
+ }
+ else
+ {
+ bRet = pFrame->WouldFit(nSpace, bSplit, false, true);
+ nSecondCheck = !bSplit ? 1 : 0;
+ }
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // Sad but true: We need to consider the spacing in our calculation.
+ // This already happened in TestFormat.
+ if ( bRet && !bTstMove )
+ {
+ SwTwips nUpper;
+
+ if ( pTmpPrev )
+ {
+ nUpper = CalcUpperSpace( nullptr, pTmpPrev );
+
+ // in balanced columned section frames we do not want the
+ // common border
+ bool bCommonBorder = true;
+ if ( pFrame->IsInSct() && pFrame->GetUpper()->IsColBodyFrame() )
+ {
+ const SwSectionFrame* pSct = pFrame->FindSctFrame();
+ bCommonBorder = pSct->GetFormat()->GetBalancedColumns().GetValue();
+ }
+
+ // #i46181#
+ nSecondCheck = ( 1 == nSecondCheck &&
+ pFrame == this &&
+ IsTextFrame() &&
+ bCommonBorder &&
+ !static_cast<const SwTextFrame*>(this)->IsEmpty() ) ?
+ nUpper :
+ 0;
+
+ nUpper += bCommonBorder ?
+ rAttrs.GetBottomLine( *pFrame ) :
+ rAttrs.CalcBottomLine();
+
+ }
+ else
+ {
+ // #i46181#
+ nSecondCheck = 0;
+
+ if( pFrame->IsVertical() )
+ nUpper = pFrame->getFrameArea().Width() - pFrame->getFramePrintArea().Width();
+ else
+ nUpper = pFrame->getFrameArea().Height() - pFrame->getFramePrintArea().Height();
+ }
+
+ nSpace -= nUpper;
+
+ if ( nSpace < 0 )
+ {
+ bRet = false;
+
+ // #i46181#
+ if ( nSecondCheck > 0 )
+ {
+ // The following code is intended to solve a (rare) problem
+ // causing some frames not to move backward:
+ // SwTextFrame::WouldFit() claims that the whole paragraph
+ // fits into the given space and subtracts the height of
+ // all lines from nSpace. nSpace - nUpper is not a valid
+ // indicator if the frame should be allowed to move backward.
+ // We do a second check with the original remaining space
+ // reduced by the required upper space:
+ nOldSpace -= nSecondCheck;
+ const bool bSecondRet = nOldSpace >= 0 && pFrame->WouldFit(nOldSpace, bOldSplit, false, true);
+ if ( bSecondRet && bOldSplit && nOldSpace >= 0 )
+ {
+ bRet = true;
+ bSplit = true;
+ }
+ }
+ }
+ }
+
+ // Also consider lower spacing in table cells
+ IDocumentSettingAccess const& rIDSA(pNewUpper->GetFormat()->getIDocumentSettingAccess());
+ if ( bRet && IsInTab() &&
+ rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
+ {
+ nSpace -= rAttrs.GetULSpace().GetLower();
+
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
+ {
+ nSpace -= rAttrs.CalcLineSpacing();
+ }
+ if ( nSpace < 0 )
+ {
+ bRet = false;
+ }
+ }
+
+ if (bRet && !bSplit && pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem()))
+ {
+ if( bTstMove )
+ {
+ while( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->HasFollow() )
+ {
+ pFrame = static_cast<SwTextFrame*>(pFrame)->GetFollow();
+ }
+ // If last follow frame of <this> text frame isn't valid,
+ // a formatting of the next content frame doesn't makes sense.
+ // Thus, return true.
+ if ( IsAnFollow( pFrame ) && !pFrame->isFrameAreaDefinitionValid() )
+ {
+ OSL_FAIL( "Only a warning for task 108824:/n<SwContentFrame::WouldFit_(..) - follow not valid!" );
+ return true;
+ }
+ }
+ SwFrame *pNxt;
+ if( nullptr != (pNxt = pFrame->FindNext()) && pNxt->IsContentFrame() &&
+ ( !pFootnoteFrame || ( pNxt->IsInFootnote() &&
+ pNxt->FindFootnoteFrame()->GetAttr() == pFootnoteFrame->GetAttr() ) ) )
+ {
+ // TestFormat(?) does not like paragraph- or character anchored objects.
+
+ // current solution for the test formatting doesn't work, if
+ // objects are present in the remaining area of the new upper
+ if ( bTstMove &&
+ ( pNxt->GetDrawObjs() || bObjsInNewUpper ) )
+ {
+ return true;
+ }
+
+ if ( !pNxt->isFrameAreaDefinitionValid() )
+ {
+ MakeNxt( pFrame, pNxt );
+ }
+
+ // Little trick: if the next has a predecessor, then the paragraph
+ // spacing has been calculated already, and we don't need to re-calculate
+ // it in an expensive way.
+ if( lcl_NotHiddenPrev( pNxt ) )
+ pTmpPrev = nullptr;
+ else
+ {
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow() )
+ pTmpPrev = lcl_NotHiddenPrev( pFrame );
+ else
+ pTmpPrev = pFrame;
+ }
+ pFrame = static_cast<SwContentFrame*>(pNxt);
+ }
+ else
+ pFrame = nullptr;
+ }
+ else
+ pFrame = nullptr;
+
+ } while ( bRet && pFrame );
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/colfrm.cxx b/sw/source/core/layout/colfrm.cxx
new file mode 100644
index 000000000..4b13576a7
--- /dev/null
+++ b/sw/source/core/layout/colfrm.cxx
@@ -0,0 +1,446 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/ulspitem.hxx>
+#include <osl/diagnose.h>
+#include <fmtclds.hxx>
+#include <fmtfordr.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <colfrm.hxx>
+#include <pagefrm.hxx>
+#include <bodyfrm.hxx>
+#include <rootfrm.hxx>
+#include <sectfrm.hxx>
+#include <calbck.hxx>
+#include <ftnfrm.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+
+SwColumnFrame::SwColumnFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
+ SwFootnoteBossFrame( pFormat, pSib )
+{
+ mnFrameType = SwFrameType::Column;
+ SwBodyFrame* pColBody = new SwBodyFrame( pFormat->GetDoc()->GetDfltFrameFormat(), pSib );
+ pColBody->InsertBehind( this, nullptr ); // ColumnFrames now with BodyFrame
+ SetMaxFootnoteHeight( LONG_MAX );
+}
+
+void SwColumnFrame::DestroyImpl()
+{
+ SwFrameFormat *pFormat = GetFormat();
+ SwDoc *pDoc;
+ if ( !(pDoc = pFormat->GetDoc())->IsInDtor() && pFormat->HasOnlyOneListener() )
+ {
+ //I'm the only one, delete the format.
+ //Get default format before, so the base class can cope with it.
+ pDoc->GetDfltFrameFormat()->Add( this );
+ // tdf#134009, like #i32968# avoid SwUndoFrameFormatDelete creation,
+ // the format is owned by the SwColumnFrame, see lcl_AddColumns()
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ pDoc->DelFrameFormat( pFormat );
+ }
+
+ SwFootnoteBossFrame::DestroyImpl();
+}
+
+SwColumnFrame::~SwColumnFrame()
+{
+}
+
+static void lcl_RemoveColumns( SwLayoutFrame *pCont, sal_uInt16 nCnt )
+{
+ OSL_ENSURE( pCont && pCont->Lower() && pCont->Lower()->IsColumnFrame(),
+ "no columns to remove." );
+
+ SwColumnFrame *pColumn = static_cast<SwColumnFrame*>(pCont->Lower());
+ sw_RemoveFootnotes( pColumn, true, true );
+ while ( pColumn->GetNext() )
+ {
+ OSL_ENSURE( pColumn->GetNext()->IsColumnFrame(),
+ "neighbor of ColumnFrame is no ColumnFrame." );
+ pColumn = static_cast<SwColumnFrame*>(pColumn->GetNext());
+ }
+ for ( sal_uInt16 i = 0; i < nCnt; ++i )
+ {
+ SwColumnFrame *pTmp = static_cast<SwColumnFrame*>(pColumn->GetPrev());
+ pColumn->Cut();
+ SwFrame::DestroyFrame(pColumn); //format is going to be destroyed in the DTor if needed.
+ pColumn = pTmp;
+ }
+}
+
+static SwLayoutFrame * lcl_FindColumns( SwLayoutFrame *pLay, sal_uInt16 nCount )
+{
+ SwFrame *pCol = pLay->Lower();
+ if ( pLay->IsPageFrame() )
+ pCol = static_cast<SwPageFrame*>(pLay)->FindBodyCont()->Lower();
+
+ if ( pCol && pCol->IsColumnFrame() )
+ {
+ SwFrame *pTmp = pCol;
+ sal_uInt16 i;
+ for ( i = 0; pTmp; pTmp = pTmp->GetNext(), ++i )
+ /* do nothing */;
+ return i == nCount ? static_cast<SwLayoutFrame*>(pCol) : nullptr;
+ }
+ return nullptr;
+}
+
+static bool lcl_AddColumns( SwLayoutFrame *pCont, sal_uInt16 nCount )
+{
+ SwDoc *pDoc = pCont->GetFormat()->GetDoc();
+ const bool bMod = pDoc->getIDocumentState().IsModified();
+
+ //Formats should be shared whenever possible. If a neighbour already has
+ //the same column settings we can add them to the same format.
+ //The neighbour can be searched using the format, however the owner of the
+ //attribute depends on the frame type.
+ SwLayoutFrame *pAttrOwner = pCont;
+ if ( pCont->IsBodyFrame() )
+ pAttrOwner = pCont->FindPageFrame();
+ SwLayoutFrame *pNeighbourCol = nullptr;
+ SwIterator<SwLayoutFrame,SwFormat> aIter( *pAttrOwner->GetFormat() );
+ SwLayoutFrame *pNeighbour = aIter.First();
+
+ sal_uInt16 nAdd = 0;
+ SwFrame *pCol = pCont->Lower();
+ if ( pCol && pCol->IsColumnFrame() )
+ for ( nAdd = 1; pCol; pCol = pCol->GetNext(), ++nAdd )
+ /* do nothing */;
+ while ( pNeighbour )
+ {
+ if ( nullptr != (pNeighbourCol = lcl_FindColumns( pNeighbour, nCount+nAdd )) &&
+ pNeighbourCol != pCont )
+ break;
+ pNeighbourCol = nullptr;
+ pNeighbour = aIter.Next();
+ }
+
+ bool bRet;
+ SwTwips nMax = pCont->IsPageBodyFrame() ?
+ pCont->FindPageFrame()->GetMaxFootnoteHeight() : LONG_MAX;
+ if ( pNeighbourCol )
+ {
+ bRet = false;
+ SwFrame *pTmp = pCont->Lower();
+ while ( pTmp )
+ {
+ pTmp = pTmp->GetNext();
+ pNeighbourCol = static_cast<SwLayoutFrame*>(pNeighbourCol->GetNext());
+ }
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ {
+ SwColumnFrame *pTmpCol = new SwColumnFrame( pNeighbourCol->GetFormat(), pCont );
+ pTmpCol->SetMaxFootnoteHeight( nMax );
+ pTmpCol->InsertBefore( pCont, nullptr );
+ pNeighbourCol = static_cast<SwLayoutFrame*>(pNeighbourCol->GetNext());
+ }
+ }
+ else
+ {
+ bRet = true;
+ // tdf#103359, like #i32968# Inserting columns in the section causes MakeFrameFormat to put
+ // nCount objects of type SwUndoFrameFormat on the undo stack. We don't want them.
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ {
+ SwFrameFormat *pFormat = pDoc->MakeFrameFormat(OUString(), pDoc->GetDfltFrameFormat());
+ SwColumnFrame *pTmp = new SwColumnFrame( pFormat, pCont );
+ pTmp->SetMaxFootnoteHeight( nMax );
+ pTmp->Paste( pCont );
+ }
+ }
+
+ if ( !bMod )
+ pDoc->getIDocumentState().ResetModified();
+ return bRet;
+}
+
+/** add or remove columns from a layoutframe.
+ *
+ * Normally, a layoutframe with a column attribute of 1 or 0 columns contains
+ * no columnframe. However, a sectionframe with "footnotes at the end" needs
+ * a columnframe.
+ *
+ * @param rOld
+ * @param rNew
+ * @param bChgFootnote if true, the columnframe will be inserted or removed, if necessary.
+ */
+void SwLayoutFrame::ChgColumns( const SwFormatCol &rOld, const SwFormatCol &rNew,
+ const bool bChgFootnote )
+{
+ if ( rOld.GetNumCols() <= 1 && rNew.GetNumCols() <= 1 && !bChgFootnote )
+ return;
+ // #i97379#
+ // If current lower is a no text frame, then columns are not allowed
+ if ( Lower() && Lower()->IsNoTextFrame() &&
+ rNew.GetNumCols() > 1 )
+ {
+ return;
+ }
+
+ sal_uInt16 nNewNum, nOldNum = 1;
+ if( Lower() && Lower()->IsColumnFrame() )
+ {
+ SwFrame* pCol = Lower();
+ while( nullptr != (pCol=pCol->GetNext()) )
+ ++nOldNum;
+ }
+ nNewNum = rNew.GetNumCols();
+ if( !nNewNum )
+ ++nNewNum;
+ bool bAtEnd;
+ if( IsSctFrame() )
+ bAtEnd = static_cast<SwSectionFrame*>(this)->IsAnyNoteAtEnd();
+ else
+ bAtEnd = false;
+
+ //Setting the column width is only needed for new formats.
+ bool bAdjustAttributes = nOldNum != rOld.GetNumCols();
+
+ //The content is saved and restored if the column count is different.
+ SwFrame *pSave = nullptr;
+ if( nOldNum != nNewNum || bChgFootnote )
+ {
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ OSL_ENSURE( pDoc, "FrameFormat doesn't return a document." );
+ // SaveContent would also suck up the content of the footnote container
+ // and store it within the normal text flow.
+ if( IsPageBodyFrame() )
+ pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->RemoveFootnotes( static_cast<SwPageFrame*>(GetUpper()) );
+ pSave = ::SaveContent( this );
+
+ //If columns exist, they get deleted if a column count of 0 or 1 is requested.
+ if ( nNewNum == 1 && !bAtEnd )
+ {
+ ::lcl_RemoveColumns( this, nOldNum );
+ if ( IsBodyFrame() )
+ SetFrameFormat( pDoc->GetDfltFrameFormat() );
+ else
+ GetFormat()->SetFormatAttr( SwFormatFillOrder() );
+ if ( pSave )
+ ::RestoreContent( pSave, this, nullptr );
+ return;
+ }
+ if ( nOldNum == 1 )
+ {
+ if ( IsBodyFrame() )
+ SetFrameFormat( pDoc->GetColumnContFormat() );
+ else
+ GetFormat()->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT ) );
+ if( !Lower() || !Lower()->IsColumnFrame() )
+ --nOldNum;
+ }
+ if ( nOldNum > nNewNum )
+ {
+ ::lcl_RemoveColumns( this, nOldNum - nNewNum );
+ bAdjustAttributes = true;
+ }
+ else if( nOldNum < nNewNum )
+ {
+ sal_uInt16 nAdd = nNewNum - nOldNum;
+ bAdjustAttributes = lcl_AddColumns( this, nAdd );
+ }
+ }
+
+ if ( !bAdjustAttributes )
+ {
+ if ( rOld.GetLineWidth() != rNew.GetLineWidth() ||
+ rOld.GetWishWidth() != rNew.GetWishWidth() ||
+ rOld.IsOrtho() != rNew.IsOrtho() )
+ bAdjustAttributes = true;
+ else
+ {
+ const size_t nCount = std::min( rNew.GetColumns().size(), rOld.GetColumns().size() );
+ for ( size_t i = 0; i < nCount; ++i )
+ if ( !(rOld.GetColumns()[i] == rNew.GetColumns()[i]) )
+ {
+ bAdjustAttributes = true;
+ break;
+ }
+ }
+ }
+
+ //The columns can now be easily adjusted.
+ AdjustColumns( &rNew, bAdjustAttributes );
+
+ //Don't restore the content before. An earlier restore would trigger useless
+ //actions during setup.
+ if ( pSave )
+ {
+ OSL_ENSURE( Lower() && Lower()->IsLayoutFrame() &&
+ static_cast<SwLayoutFrame*>(Lower())->Lower() &&
+ static_cast<SwLayoutFrame*>(Lower())->Lower()->IsLayoutFrame(),
+ "no column body." ); // ColumnFrames contain BodyFrames
+ ::RestoreContent( pSave, static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()), nullptr );
+ }
+}
+
+void SwLayoutFrame::AdjustColumns( const SwFormatCol *pAttr, bool bAdjustAttributes )
+{
+ if( !Lower()->GetNext() )
+ {
+ Lower()->ChgSize( getFramePrintArea().SSize() );
+ return;
+ }
+
+ const bool bVert = IsVertical();
+
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ //If we have a pointer or we have to configure an attribute, we set the
+ //column widths in any case. Otherwise we check if a configuration is needed.
+ if ( !pAttr )
+ {
+ pAttr = &GetFormat()->GetCol();
+ if ( !bAdjustAttributes )
+ {
+ tools::Long nAvail = (getFramePrintArea().*fnRect->fnGetWidth)();
+ for ( SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(Lower());
+ pCol;
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext()) )
+ nAvail -= (pCol->getFrameArea().*fnRect->fnGetWidth)();
+ if ( !nAvail )
+ return;
+ }
+ }
+
+ //The columns can now be easily adjusted.
+ //The widths get counted so we can give the reminder to the last one.
+ SwTwips nAvail = (getFramePrintArea().*fnRect->fnGetWidth)();
+ const bool bLine = pAttr->GetLineAdj() != COLADJ_NONE;
+ const sal_uInt16 nMin = bLine ? sal_uInt16( 20 + ( pAttr->GetLineWidth() / 2) ) : 0;
+
+ const bool bR2L = IsRightToLeft();
+ SwFrame *pCol = bR2L ? GetLastLower() : Lower();
+
+ // #i27399#
+ // bOrtho means we have to adjust the column frames manually. Otherwise
+ // we may use the values returned by CalcColWidth:
+ const bool bOrtho = pAttr->IsOrtho() && pAttr->GetNumCols() > 0;
+ tools::Long nGutter = 0;
+
+ for ( sal_uInt16 i = 0; i < pAttr->GetNumCols() && pCol; ++i ) //i118878, value returned by GetNumCols() can't be trusted
+ {
+ if( !bOrtho )
+ {
+ const SwTwips nWidth = i == (pAttr->GetNumCols() - 1) ?
+ nAvail :
+ pAttr->CalcColWidth( i, sal_uInt16( (getFramePrintArea().*fnRect->fnGetWidth)() ) );
+
+ const Size aColSz = bVert ?
+ Size( getFramePrintArea().Width(), nWidth ) :
+ Size( nWidth, getFramePrintArea().Height() );
+
+ pCol->ChgSize( aColSz );
+
+ // With this, the ColumnBodyFrames from page columns gets adjusted and
+ // their bFixHeight flag is set so they won't shrink/grow.
+ // Don't use the flag with frame columns because BodyFrames in frame
+ // columns can grow/shrink.
+ if( IsBodyFrame() )
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->ChgSize( aColSz );
+
+ nAvail -= nWidth;
+ }
+
+ if ( bOrtho || bAdjustAttributes )
+ {
+ const SwColumn *pC = &pAttr->GetColumns()[i];
+ const SwAttrSet* pSet = pCol->GetAttrSet();
+ SvxLRSpaceItem aLR( pSet->GetLRSpace() );
+
+ //In order to have enough space for the separation lines, we have to
+ //take them into account here. Every time two columns meet we
+ //calculate a clearance of 20 + half the pen width on the left or
+ //right side, respectively.
+ const sal_uInt16 nLeft = pC->GetLeft();
+ const sal_uInt16 nRight = pC->GetRight();
+
+ aLR.SetLeft ( nLeft );
+ aLR.SetRight( nRight );
+
+ if ( bLine )
+ {
+ if ( i == 0 )
+ {
+ aLR.SetRight( std::max( nRight, nMin ) );
+ }
+ else if ( i == pAttr->GetNumCols() - 1 )
+ {
+ aLR.SetLeft ( std::max( nLeft, nMin ) );
+ }
+ else
+ {
+ aLR.SetLeft ( std::max( nLeft, nMin ) );
+ aLR.SetRight( std::max( nRight, nMin ) );
+ }
+ }
+
+ if ( bAdjustAttributes )
+ {
+ SvxULSpaceItem aUL( pSet->GetULSpace() );
+ aUL.SetUpper(0);
+ aUL.SetLower(0);
+
+ static_cast<SwLayoutFrame*>(pCol)->GetFormat()->SetFormatAttr( aLR );
+ static_cast<SwLayoutFrame*>(pCol)->GetFormat()->SetFormatAttr( aUL );
+ }
+
+ nGutter += aLR.GetLeft() + aLR.GetRight();
+ }
+
+ pCol = bR2L ? pCol->GetPrev() : pCol->GetNext();
+ }
+
+ if( !bOrtho )
+ return;
+
+ tools::Long nInnerWidth = ( nAvail - nGutter ) / pAttr->GetNumCols();
+ pCol = Lower();
+ for( sal_uInt16 i = 0; i < pAttr->GetNumCols() && pCol; pCol = pCol->GetNext(), ++i ) //i118878, value returned by GetNumCols() can't be trusted
+ {
+ SwTwips nWidth;
+ if ( i == pAttr->GetNumCols() - 1 )
+ nWidth = nAvail;
+ else
+ {
+ SvxLRSpaceItem aLR( pCol->GetAttrSet()->GetLRSpace() );
+ nWidth = nInnerWidth + aLR.GetLeft() + aLR.GetRight();
+ }
+ if( nWidth < 0 )
+ nWidth = 0;
+
+ const Size aColSz = bVert ?
+ Size( getFramePrintArea().Width(), nWidth ) :
+ Size( nWidth, getFramePrintArea().Height() );
+
+ pCol->ChgSize( aColSz );
+
+ if( IsBodyFrame() )
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->ChgSize( aColSz );
+
+ nAvail -= nWidth;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/dbg_lay.cxx b/sw/source/core/layout/dbg_lay.cxx
new file mode 100644
index 000000000..d1d012c64
--- /dev/null
+++ b/sw/source/core/layout/dbg_lay.cxx
@@ -0,0 +1,953 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifdef DBG_UTIL
+
+/*
+ * And here's the description:
+ *
+ * The PROTOCOL macros allow you to log events in frame methods. In places where
+ * logging is useful either one of the PROTOCOL(...) or PROTOCOL_ENTER(...) can
+ * be used. PROTOCOL_ENTER(...) additionally logs the leaving of a method.
+ *
+ * The PROTOCOL macros accept the following parameters:
+ * 1. A pointer to an SwFrame (usually "this" or "rThis")
+ * 2. The function group i.e. PROT::MakeAll. This is used to decide (inline)
+ * whether this event shall be logged at the current time.
+ * 3. The action, usually 0. For example DbgAction::Start indents output in the log
+ * file and DbgAction::End stops the indentation. This allows for example
+ * PROTOCOL_ENTER to indent at the beginning of a method and stop indenting
+ * when leaving the method.
+ * 4. The fourth parameter is a void pointer which allows to pass anything
+ * which can be used in the log. A good example is PROT::Grow: this requires
+ * a pointer to the value which defines how much to grow.
+ *
+ * The log file is called "dbg_lay.out", which is saved in the current (BIN-)
+ * directory. The file contains lines with FrameId, function group and additional
+ * information.
+ *
+ * What exactly is going to be logged, can be defined as follows:
+ * 1. The static variable SwProtocol::nRecord contains the function groups
+ * which shall be logged.
+ * A value of i.e. PROT::Grow causes calls to SwFrame::Grow to be
+ * logged; PROT::MakeAll logs the calls to xxx::MakeAll.
+ * The PROT_XY values can be combined using binary OR, the default value
+ * is null - no method calls are logged.
+ * 2. The SwImplProtocol class contains a filter for frame types, only method
+ * call of frame types which are defined there are logged.
+ * The member nTypes can be set to values like SwFrameType::Page or SwFrameType::Section and
+ * may be combined using binary OR. The default values is 0xFFFF - meaning
+ * all frame types.
+ * 3. The SwImplProtocol class contains an ArrayPointer to FrameIds which need to be
+ * tracked. If the pointer is null, all frames will be logged; otherwise
+ * only frames of linked from the array will be logged.
+ *
+ * Code changes are needed to start logging; either change the default of nRecord
+ * in SwProtocol::Init() or change the debugger. There are several possible
+ * places in the debugger:
+ * 1. Set a breakpoint in SwProtocol::Init() and manipulate nRecord there, set
+ * FrameIds accordingly then start logging during program start.
+ * 2. Set a breakpoint before any PROTOCOL or PROTOCOL_ENTER macro during
+ * program execution, then set the lowest bit (PROT::Init) of
+ * SwProtocol::nRecord. This activates the function group of the following
+ * macro and causes it to be logged in the future.
+ * 3. There's a special case for 2: If one uses 2. in SwRootFrame::PaintSwFrame(..),
+ * the log settings are taken from the file "dbg_lay.ini"!
+ * In this INI-file you can have comment lines starting with a '#'.
+ * The sections "[frmid]", "[frmtype]" and "[record]" are relevant.
+ * In the [frmid] section, you can put FrameIds of the Frames to be logged.
+ * If there are no entries in this section, all Frames will be logged.
+ * In the [frmtype] section, the frame types which should be logged are
+ * listed; default is USHRT_MAX which means that all types are logged.
+ * It's possible to remove types from the list using '!' in front of a
+ * value. The value !0xC000 would for example exclude SwContentFrames from
+ * logging.
+ * In the [record] section the functions group which should be logged are
+ * listed; default is 0 which means that none are logged. It's also
+ * possible to remove functions using '!'.
+ * An example INI file:
+ * #Functions: all(0x0007ffff), except PrintArea (0x200)
+ * [record] 524287 !512
+ * [frmid]
+ * #the following FrameIds:
+ * 1 2 12 13 14 15
+ * #no layout frames, except ColumnFrames
+ * [frmtype] !0x3FFF 0x4
+ *
+ * As soon as the logging is in process, one can manipulate many things in
+ * SwImplProtocol::Record_(...) using a debugger, especially concerning
+ * frame types and FrameIds.
+ */
+
+#include <dbg_lay.hxx>
+
+#include <txtfrm.hxx>
+#include <fntcache.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <layfrm.hxx>
+#include <frame.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+
+PROT SwProtocol::s_nRecord = PROT::FileInit;
+SwImplProtocol* SwProtocol::s_pImpl = nullptr;
+
+static sal_uLong lcl_GetFrameId( const SwFrame* pFrame )
+{
+#if OSL_DEBUG_LEVEL > 1
+ static bool bFrameId = false;
+ if( bFrameId )
+ return pFrame->GetFrameId();
+#endif
+ if( pFrame )
+ return pFrame->GetFrameId();
+ return 0;
+}
+
+class SwImplProtocol
+{
+ std::unique_ptr<SvFileStream> m_pStream; // output stream
+ std::unique_ptr<std::set<sal_uInt16>>
+ m_pFrameIds; // which FrameIds shall be logged ( NULL == all)
+ std::vector<tools::Long> m_aVars; // variables
+ OStringBuffer m_aLayer; // indentation of output (" " per start/end)
+ SwFrameType m_nTypes; // which types shall be logged
+ sal_uInt16 m_nLineCount; // printed lines
+ sal_uInt16 m_nMaxLines; // max lines to be printed
+ sal_uInt8 m_nInitFile; // range (FrameId,FrameType,Record) during reading of the INI file
+ sal_uInt8
+ m_nTestMode; // special for test formatting, logging may only be done in test formatting.
+ void Record_( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam );
+ bool NewStream();
+ void CheckLine( OString& rLine );
+ static void SectFunc( OStringBuffer& rOut, DbgAction nAct, void const * pParam );
+public:
+ SwImplProtocol();
+ ~SwImplProtocol();
+ // logging
+ void Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
+ {
+ if (m_pStream)
+ Record_(pFrame, nFunction, nAct, pParam);
+ }
+ void InsertFrame( sal_uInt16 nFrameId ); // take FrameId for logging
+ void DeleteFrame( sal_uInt16 nFrameId ); // remove FrameId; don't log him anymore
+ void FileInit(); // read the INI file
+ void ChkStream() {
+ if (!m_pStream)
+ NewStream();
+ }
+};
+
+/* Through the PROTOCOL_ENTER macro a SwEnterLeave object gets created. If the
+ * current function should be logged as SwImplEnterLeace object gets created.
+ * The funny thing here is, that the Ctor of the Impl object is automatically
+ * called at the beginning of the function and the Dtor is automatically called
+ * when leaving the function. In the base implementation the Ctor calls only
+ * PROTOCOL(..) with DbgAction::Start and in the Dtor a PROTOCOL(..) with DbgAction::End.
+ * It's possible to derive from this class, for example to be able to document
+ * frame resize while leaving a function. To do this, one only needs to add the
+ * desired SwImplEnterLeave class in SwEnterLeave::Ctor().
+ */
+
+class SwImplEnterLeave
+{
+protected:
+ const SwFrame* m_pFrame; // the frame
+ PROT m_nFunction; // the function
+ DbgAction m_nAction; // the action if needed
+ void* m_pParam; // further parameter
+public:
+ SwImplEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : m_pFrame(pF)
+ , m_nFunction(nFunct)
+ , m_nAction(nAct)
+ , m_pParam(pPar)
+ {
+ }
+ virtual ~SwImplEnterLeave() {}
+ virtual void Enter(); // message when entering
+ virtual void Leave(); // message when leaving
+};
+
+namespace {
+
+class SwSizeEnterLeave : public SwImplEnterLeave
+{
+ tools::Long m_nFrameHeight;
+
+public:
+ SwSizeEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : SwImplEnterLeave(pF, nFunct, nAct, pPar)
+ , m_nFrameHeight(pF->getFrameArea().Height())
+ {
+ }
+
+ virtual void Leave() override; // resize message
+};
+
+class SwUpperEnterLeave : public SwImplEnterLeave
+{
+ sal_uInt16 m_nFrameId;
+
+public:
+ SwUpperEnterLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : SwImplEnterLeave(pF, nFunct, nAct, pPar)
+ , m_nFrameId(0)
+ {
+ }
+
+ virtual void Enter() override; // message
+ virtual void Leave() override; // message of FrameId from upper
+};
+
+class SwFrameChangesLeave : public SwImplEnterLeave
+{
+ SwRect m_aFrame;
+
+public:
+ SwFrameChangesLeave(const SwFrame* pF, PROT nFunct, DbgAction nAct, void* pPar)
+ : SwImplEnterLeave(pF, nFunct, nAct, pPar)
+ , m_aFrame(pF->getFrameArea())
+ {
+ }
+
+ virtual void Enter() override; // no message
+ virtual void Leave() override; // message when resizing the Frame area
+};
+
+}
+
+void SwProtocol::Record( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
+{
+ if( Start() )
+ { // We reach this point if SwProtocol::nRecord is binary OR'd with PROT::Init(0x1) using the debugger
+ bool bFinit = false; // This gives the possibility to stop logging of this action in the debugger
+ if( bFinit )
+ {
+ s_nRecord &= ~nFunction; // Don't log this function any longer
+ s_nRecord &= ~PROT::Init; // Always reset PROT::Init
+ return;
+ }
+ s_nRecord |= nFunction; // Activate logging of this function
+ s_nRecord &= ~PROT::Init; // Always reset PROT::Init
+ if( s_pImpl )
+ s_pImpl->ChkStream();
+ }
+ if( !s_pImpl ) // Create Impl object if needed
+ s_pImpl = new SwImplProtocol();
+ s_pImpl->Record( pFrame, nFunction, nAct, pParam ); // ...and start logging
+}
+
+// The following function gets called when pulling in the writer DLL through
+// TextInit(..) and gives the possibility to release functions
+// and/or FrameIds to the debugger
+
+void SwProtocol::Init()
+{
+ s_nRecord = PROT::FileInit;
+ SvFileStream aStream( "dbg_lay.go", StreamMode::READ );
+ if( aStream.IsOpen() )
+ {
+ s_pImpl = new SwImplProtocol();
+ s_pImpl->FileInit();
+ }
+ aStream.Close();
+}
+
+// End of logging
+
+void SwProtocol::Stop()
+{
+ if( s_pImpl )
+ {
+ delete s_pImpl;
+ s_pImpl = nullptr;
+ if( pFntCache )
+ pFntCache->Flush();
+ }
+ s_nRecord = PROT::FileInit;
+}
+
+SwImplProtocol::SwImplProtocol()
+ : m_nTypes(FRM_ALL)
+ , m_nLineCount(0)
+ , m_nMaxLines(USHRT_MAX)
+ , m_nTestMode(0)
+{
+ NewStream();
+}
+
+bool SwImplProtocol::NewStream()
+{
+ m_nLineCount = 0;
+ m_pStream.reset(new SvFileStream("dbg_lay.out", StreamMode::WRITE | StreamMode::TRUNC));
+ if (m_pStream->GetError())
+ {
+ m_pStream.reset();
+ }
+ return nullptr != m_pStream;
+}
+
+SwImplProtocol::~SwImplProtocol()
+{
+ if (m_pStream)
+ {
+ m_pStream->Close();
+ m_pStream.reset();
+ }
+ m_pFrameIds.reset();
+ m_aVars.clear();
+}
+
+/// analyze a line in the INI file
+void SwImplProtocol::CheckLine( OString& rLine )
+{
+ rLine = rLine.toAsciiLowerCase(); // upper/lower case is the same
+ rLine = rLine.replace( '\t', ' ' );
+ if( '#' == rLine[0] ) // comments start with '#'
+ return;
+ if( '[' == rLine[0] ) // section: FrameIds, type or function
+ {
+ std::string_view aTmp = o3tl::getToken(rLine, 0, ']');
+ if (aTmp == "[frmid") // section FrameIds
+ {
+ m_nInitFile = 1;
+ m_pFrameIds.reset(); // default: log all frames
+ }
+ else if (aTmp == "[frmtype")// section types
+ {
+ m_nInitFile = 2;
+ m_nTypes = FRM_ALL; // default: log all frame types
+ }
+ else if (aTmp == "[record")// section functions
+ {
+ m_nInitFile = 3;
+ SwProtocol::SetRecord( PROT::FileInit );// default: don't log any function
+ }
+ else if (aTmp == "[test")// section functions
+ {
+ m_nInitFile = 4; // default:
+ m_nTestMode = 0; // log outside of test formatting
+ }
+ else if (aTmp == "[max")// Max number of lines
+ {
+ m_nInitFile = 5; // default:
+ m_nMaxLines = USHRT_MAX;
+ }
+ else if (aTmp == "[var")// variables
+ {
+ m_nInitFile = 6;
+ }
+ else
+ m_nInitFile = 0; // oops: unknown section?
+ rLine = rLine.copy(aTmp.size() + 1);
+ }
+
+ // spaces (or tabs) are the delimiter
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::string_view aTok = o3tl::getToken(rLine, 0, ' ', nIndex );
+ bool bNo = false;
+ if( !aTok.empty() && '!' == aTok[0] )
+ {
+ bNo = true; // remove this function/type
+ aTok = aTok.substr(1);
+ }
+ if( !aTok.empty() )
+ {
+ sal_Int64 nVal = o3tl::toInt64(aTok);
+ switch (m_nInitFile)
+ {
+ case 1: InsertFrame( sal_uInt16( nVal ) ); // add FrameId
+ break;
+ case 2: {
+ SwFrameType nNew = static_cast<SwFrameType>(nVal);
+ if( bNo )
+ m_nTypes &= ~nNew; // remove type
+ else
+ m_nTypes |= nNew; // add type
+ }
+ break;
+ case 3: {
+ PROT nOld = SwProtocol::Record();
+ if( bNo )
+ nOld &= ~PROT(nVal & o3tl::typed_flags<PROT>::mask); // remove function
+ else
+ nOld |= PROT(nVal & o3tl::typed_flags<PROT>::mask); // remove function
+ SwProtocol::SetRecord( nOld );
+ }
+ break;
+ case 4: {
+ sal_uInt8 nNew = static_cast<sal_uInt8>(nVal);
+ if( bNo )
+ m_nTestMode &= ~nNew; // reset test mode
+ else
+ m_nTestMode |= nNew; // set test mode
+ }
+ break;
+ case 5:
+ m_nMaxLines = o3tl::narrowing<sal_uInt16>(nVal);
+ break;
+ case 6:
+ m_aVars.push_back(nVal);
+ break;
+ }
+ }
+ }
+ while ( nIndex >= 0 );
+}
+
+/// read the file "dbg_lay.ini" in the current directory and evaluate it.
+void SwImplProtocol::FileInit()
+{
+ SvFileStream aStream( "dbg_lay.ini", StreamMode::READ );
+ if( aStream.IsOpen() )
+ {
+ OString aLine;
+ m_nInitFile = 0;
+ while( aStream.good() )
+ {
+ char c;
+ aStream.ReadChar( c );
+ if( '\n' == c || '\r' == c ) // line ending
+ {
+ aLine = aLine.trim();
+ if( !aLine.isEmpty() )
+ CheckLine( aLine ); // evaluate line
+ aLine.clear();
+ }
+ else
+ aLine += OStringChar(c);
+ }
+ if( !aLine.isEmpty() )
+ CheckLine( aLine ); // evaluate last line
+ }
+ aStream.Close();
+}
+
+/// enable indentation by two spaces during DbgAction::Start and disable it again at DbgAction::End.
+static void lcl_Start(OStringBuffer& rOut, OStringBuffer& rLay, DbgAction nAction)
+{
+ if( nAction == DbgAction::Start )
+ {
+ rLay.append(" ");
+ rOut.append(" On");
+ }
+ else if( nAction == DbgAction::End )
+ {
+ if( rLay.getLength() > 1 )
+ {
+ rLay.remove(rLay.getLength() - 2, rLay.getLength());
+ rOut.remove(0, 2);
+ }
+ rOut.append(" Off");
+ }
+}
+
+/// output the ValidSize-, ValidPos- and ValidPrtArea-Flag ("Sz","Ps","PA")
+/// of the frame; "+" stands for valid, "-" stands for invalid.
+static void lcl_Flags(OStringBuffer& rOut, const SwFrame* pFrame)
+{
+ rOut.append(" ValidSize");
+ rOut.append(pFrame->isFrameAreaSizeValid() ? '+' : '-');
+ rOut.append(" ValidPos");
+ rOut.append(pFrame->isFrameAreaPositionValid() ? '+' : '-');
+ rOut.append(" ValidPrtArea");
+ rOut.append(pFrame->isFramePrintAreaValid() ? '+' : '-');
+}
+
+static void lcl_Padded(OStringBuffer& rOut, const OString& s, size_t length)
+{
+ if (length < o3tl::make_unsigned(s.getLength()))
+ length = s.getLength();
+ rOut.append(s);
+ for (size_t i = 0; i < length - s.getLength(); i++)
+ {
+ rOut.append(" ");
+ }
+}
+
+static void lcl_Padded(OStringBuffer& rOut, const tools::Long n, size_t length = 5)
+{
+ char sz[RTL_STR_MAX_VALUEOFINT64];
+ rtl_str_valueOfInt64(sz, n, 10);
+ OString s(sz);
+ lcl_Padded(rOut, s, length);
+}
+
+/// output the frame as plain text.
+static void lcl_FrameRect(OStringBuffer& rOut, const char* hint, const SwRect& rect)
+{
+ rOut.append("[");
+ rOut.append(hint);
+ rOut.append(":X:");
+ lcl_Padded(rOut, rect.Pos().X());
+ rOut.append(", Y:");
+ lcl_Padded(rOut, rect.Pos().Y());
+ rOut.append(", Width:");
+ lcl_Padded(rOut, rect.SSize().Width());
+ rOut.append(", Height:");
+ lcl_Padded(rOut, rect.SSize().Height());
+ rOut.append("] ");
+}
+
+static OString lcl_TableInfo(const SwTabFrame* pTabFrame)
+{
+ const SwTable* pTable = pTabFrame->GetTable();
+ const SwFormat* pFormat = static_cast<const SwFormat*>(pTable->GetRegisteredIn());
+ const OUString& text = pFormat->GetName();
+ return OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
+}
+
+static OString lcl_RowInfo(const SwRowFrame* pFrame)
+{
+ // dummy, needs actual functionality...
+ if (pFrame == nullptr)
+ return "";
+ const SwTableLine* pTabLine = pFrame->GetTabLine();
+ if (pTabLine == nullptr)
+ return "";
+
+ return "RowInfo";
+}
+
+static OUString lcl_CellText(const SwCellFrame* pFrame)
+{
+ OUString result;
+ int n = 0;
+
+ const SwStartNode* pStartNode = pFrame->GetTabBox()->GetSttNd();
+ const SwEndNode* pEndNode = pStartNode->EndOfSectionNode();
+ const SwNodes& nodes = pStartNode->GetNodes();
+
+ for (SwNodeOffset i = pStartNode->GetIndex(); i < nodes.Count(); i++)
+ {
+ SwNode* pNode = nodes[i];
+
+ if (pNode->IsEndNode())
+ {
+ if (pNode->EndOfSectionNode() == pEndNode)
+ break;
+ }
+ else if (pNode->IsTextNode())
+ {
+ n++;
+ result += "Para:" + OUString::number(10) + " " +
+ pNode->GetTextNode()->GetText();
+ }
+ }
+
+ return OUString::number(n) + " para(s):" + result;
+}
+
+static OString lcl_CellInfo(const SwCellFrame* pFrame)
+{
+ const OUString text = "CellInfo: " + pFrame->GetTabBox()->GetName() + " Text: " + lcl_CellText(pFrame);
+ return OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
+}
+
+/// output the type of the frame as plain text.
+static void lcl_FrameType( OStringBuffer& rOut, const SwFrame* pFrame )
+{
+ if( pFrame->IsTextFrame() )
+ rOut.append("SwTextFrame ");
+ else if( pFrame->IsLayoutFrame() )
+ {
+ if( pFrame->IsPageFrame() )
+ rOut.append("SwPageFrame ");
+ else if( pFrame->IsColumnFrame() )
+ rOut.append("SwColumnFrame ");
+ else if( pFrame->IsBodyFrame() )
+ {
+ if( pFrame->GetUpper() && pFrame->IsColBodyFrame() )
+ rOut.append("(Col)");
+ rOut.append("SwBodyFrame ");
+ }
+ else if( pFrame->IsRootFrame() )
+ rOut.append("SwRootFrame ");
+ else if( pFrame->IsCellFrame() )
+ rOut.append("SwCellFrame ");
+ else if( pFrame->IsTabFrame() )
+ rOut.append("SwTabFrame ");
+ else if( pFrame->IsRowFrame() )
+ rOut.append("SwRowFrame ");
+ else if( pFrame->IsSctFrame() )
+ rOut.append("SwSectionFrame ");
+ else if( pFrame->IsHeaderFrame() )
+ rOut.append("SwHeaderFrame ");
+ else if( pFrame->IsFooterFrame() )
+ rOut.append("SwFooterFrame ");
+ else if( pFrame->IsFootnoteFrame() )
+ rOut.append("SwFootnoteFrame ");
+ else if( pFrame->IsFootnoteContFrame() )
+ rOut.append("SwFootnoteContFrame ");
+ else if( pFrame->IsFlyFrame() )
+ rOut.append("SwFlyFrame ");
+ else
+ rOut.append("SwLayoutFrame ");
+ }
+ else if( pFrame->IsNoTextFrame() )
+ rOut.append("SwNoTextFrame");
+ else
+ rOut.append("Not impl. ");
+}
+
+/**
+ * Is only called if the PROTOCOL macro finds out,
+ * that this function should be recorded ( @see{SwProtocol::nRecord} ).
+ *
+ * In this method we also check if FrameId and frame type should be logged.
+ */
+void SwImplProtocol::Record_( const SwFrame* pFrame, PROT nFunction, DbgAction nAct, void* pParam )
+{
+ sal_uInt16 nSpecial = 0;
+ if( nSpecial ) // the possible debugger manipulations
+ {
+ sal_uInt16 nId = sal_uInt16(lcl_GetFrameId( pFrame ));
+ switch ( nSpecial )
+ {
+ case 1: InsertFrame( nId ); break;
+ case 2: DeleteFrame( nId ); break;
+ case 3:
+ m_pFrameIds.reset();
+ break;
+ case 4:
+ m_pStream.reset();
+ break;
+ }
+ return;
+ }
+ if (!m_pStream && !NewStream())
+ return; // still no stream
+
+ if (m_pFrameIds && !m_pFrameIds->count(sal_uInt16(lcl_GetFrameId(pFrame))))
+ return; // doesn't belong to the wished FrameIds
+
+ if (!(pFrame->GetType() & m_nTypes))
+ return; // the type is unwanted
+
+ if (1 == m_nTestMode && nFunction != PROT::TestFormat)
+ return; // we may only log inside a test formatting
+ bool bTmp = false;
+ OStringBuffer aOut(m_aLayer);
+ aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(pFrame)));
+ aOut.append(' ');
+ lcl_FrameType( aOut, pFrame ); // then the frame type
+ switch ( nFunction ) // and the function
+ {
+ case PROT::MakeAll: aOut.append("SwFrame::MakeAll");
+ lcl_Start(aOut, m_aLayer, nAct);
+ if (nAct == DbgAction::Start)
+ lcl_Flags(aOut, pFrame);
+ break;
+ case PROT::MoveFwd: bTmp = true;
+ [[fallthrough]];
+ case PROT::MoveBack:
+ if (nFunction == (bTmp ? PROT::Init : PROT::FileInit))
+ aOut.append("SwFlowFrame::MoveFwd");
+ else
+ aOut.append("SwFlowFrame::MoveBwd");
+ lcl_Start(aOut, m_aLayer, nAct);
+ if( pParam )
+ {
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int32>(*static_cast<sal_uInt16*>(pParam)));
+ }
+ break;
+ case PROT::GrowTest:
+ aOut.append("SwFrame::Grow (test)");
+ lcl_Start(aOut, m_aLayer, nAct);
+ break;
+ case PROT::ShrinkTest:
+ aOut.append("SwFrame::Shrink (test)");
+ lcl_Start(aOut, m_aLayer, nAct);
+ break;
+ case PROT::AdjustN :
+ case PROT::Shrink: bTmp = true;
+ [[fallthrough]];
+ case PROT::Grow:
+ if (!bTmp)
+ aOut.append("SwFrame::Grow");
+ else
+ {
+ if (nFunction == PROT::Shrink)
+ aOut.append("SwFrame::Shrink");
+ else
+ aOut.append("SwFrame::AdjustNeighbourhood");
+ }
+ lcl_Start(aOut, m_aLayer, nAct);
+ if( pParam )
+ {
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int64>(*static_cast<tools::Long*>(pParam)));
+ }
+ break;
+ case PROT::PrintArea: aOut.append("PROT::PrintArea");
+ lcl_Start(aOut, m_aLayer, nAct);
+ break;
+ case PROT::Size: aOut.append("PROT::Size");
+ lcl_Start(aOut, m_aLayer, nAct);
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Height()));
+ break;
+ case PROT::Leaf: aOut.append("SwFrame::GetPrev/NextSctLeaf");
+ lcl_Start(aOut, m_aLayer, nAct);
+ aOut.append(' ');
+ if (pParam)
+ {
+ aOut.append(' ');
+ aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame*>(pParam))));
+ }
+ break;
+ case PROT::FileInit: FileInit();
+ aOut.append("Initialize");
+ break;
+ case PROT::Section: SectFunc(aOut, nAct, pParam);
+ break;
+ case PROT::Cut: bTmp = true;
+ [[fallthrough]];
+ case PROT::Paste:
+ if (bTmp)
+ aOut.append("PROT::Cut from ");
+ else
+ aOut.append("PROT::Paste to ");
+ aOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame*>(pParam))));
+ break;
+ case PROT::TestFormat:
+ aOut.append("SwTextFrame::TestFormat");
+ lcl_Start(aOut, m_aLayer, nAct);
+ if( DbgAction::Start == nAct )
+ m_nTestMode |= 2;
+ else
+ m_nTestMode &= ~2;
+ break;
+ case PROT::FrmChanges:
+ {
+ SwRect& rFrame = *static_cast<SwRect*>(pParam);
+ if( pFrame->getFrameArea().Pos() != rFrame.Pos() )
+ {
+ aOut.append("PosChg: (");
+ aOut.append(static_cast<sal_Int64>(rFrame.Left()));
+ aOut.append(", ");
+ aOut.append(static_cast<sal_Int64>(rFrame.Top()));
+ aOut.append(") -> (");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Left()));
+ aOut.append(", ");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Top()));
+ aOut.append(") ");
+ }
+ if( pFrame->getFrameArea().Height() != rFrame.Height() )
+ {
+ aOut.append("Height: ");
+ aOut.append(static_cast<sal_Int64>(rFrame.Height()));
+ aOut.append(" -> ");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Height()));
+ aOut.append(" ");
+ }
+ if( pFrame->getFrameArea().Width() != rFrame.Width() )
+ {
+ aOut.append("Width: ");
+ aOut.append(static_cast<sal_Int64>(rFrame.Width()));
+ aOut.append(" -> ");
+ aOut.append(static_cast<sal_Int64>(pFrame->getFrameArea().Width()));
+ aOut.append(' ');
+ }
+ break;
+ }
+ default: break;
+ }
+
+ aOut.append(" ");
+ while (aOut.getLength() < 40) aOut.append(" ");
+ lcl_FrameRect(aOut, "SwFrame", pFrame->getFrameArea());
+
+ aOut.append(" ");
+ while (aOut.getLength() < 90) aOut.append(" ");
+ lcl_FrameRect(aOut, "SwPrint", pFrame->getFramePrintArea());
+
+ if (pFrame->IsTextFrame())
+ {
+ aOut.append(" ");
+ while (aOut.getLength() < 140) aOut.append(" ");
+ const OUString& text = static_cast<const SwTextFrame*>(pFrame)->GetText();
+ OString o = OUStringToOString(text, RTL_TEXTENCODING_ASCII_US);
+ aOut.append(o);
+ }
+ else if (pFrame->IsTabFrame())
+ {
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pFrame);
+ aOut.append(lcl_TableInfo(pTabFrame));
+ }
+ else if (pFrame->IsRowFrame())
+ {
+ const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pFrame);
+ aOut.append(lcl_RowInfo(pRowFrame));
+
+ }
+ else if (pFrame->IsCellFrame())
+ {
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFrame);
+ aOut.append(lcl_CellInfo(pCellFrame));
+ }
+
+ SAL_INFO("sw.layout.debug", aOut.getStr());
+ m_pStream->WriteOString(aOut.makeStringAndClear());
+ (*m_pStream) << endl; // output
+ m_pStream->Flush(); // to the disk, so we can read it immediately
+ if (++m_nLineCount >= m_nMaxLines) // max number of lines reached?
+ {
+ SAL_WARN("sw.layout.debug", "max number of lines reached");
+ SwProtocol::SetRecord( PROT::FileInit ); // => end f logging
+ }
+}
+
+/// Handle the output of the SectionFrames.
+void SwImplProtocol::SectFunc(OStringBuffer &rOut, DbgAction nAct, void const * pParam)
+{
+ bool bTmp = false;
+ switch( nAct )
+ {
+ case DbgAction::Merge: rOut.append("Merge Section ");
+ rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
+ break;
+ case DbgAction::CreateMaster: bTmp = true;
+ [[fallthrough]];
+ case DbgAction::CreateFollow: rOut.append("Create Section ");
+ if (bTmp)
+ rOut.append("Master to ");
+ else
+ rOut.append("Follow from ");
+ rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
+ break;
+ case DbgAction::DelMaster: bTmp = true;
+ [[fallthrough]];
+ case DbgAction::DelFollow: rOut.append("Delete Section ");
+ if (bTmp)
+ rOut.append("Master to ");
+ else
+ rOut.append("Follow from ");
+ rOut.append(static_cast<sal_Int64>(lcl_GetFrameId(static_cast<SwFrame const *>(pParam))));
+ break;
+ default: break;
+ }
+}
+
+/**
+ * if pFrameIds==NULL all Frames will be logged. But as soon as pFrameIds are
+ * set, only the added FrameIds are being logged.
+ *
+ * @param nId new FrameId for logging
+ * @return TRUE if newly added, FALSE if FrameId is already under control
+ */
+void SwImplProtocol::InsertFrame( sal_uInt16 nId )
+{
+ if (!m_pFrameIds)
+ m_pFrameIds.reset(new std::set<sal_uInt16>);
+ if (m_pFrameIds->count(nId))
+ return;
+ m_pFrameIds->insert(nId);
+}
+
+/// Removes a FrameId from the pFrameIds array, so that it won't be logged anymore.
+void SwImplProtocol::DeleteFrame( sal_uInt16 nId )
+{
+ if (!m_pFrameIds)
+ return;
+ m_pFrameIds->erase(nId);
+}
+
+/*
+ * The task here is to find the right SwImplEnterLeave object based on the
+ * function; everything else is then done in his Ctor/Dtor.
+ */
+SwEnterLeave::SwEnterLeave( const SwFrame* pFrame, PROT nFunc, DbgAction nAct, void* pPar )
+{
+ if( !SwProtocol::Record( nFunc ) )
+ return;
+ switch( nFunc )
+ {
+ case PROT::AdjustN :
+ case PROT::Grow:
+ case PROT::Shrink : pImpl.reset( new SwSizeEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ case PROT::MoveFwd:
+ case PROT::MoveBack : pImpl.reset( new SwUpperEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ case PROT::FrmChanges : pImpl.reset( new SwFrameChangesLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ default: pImpl.reset( new SwImplEnterLeave( pFrame, nFunc, nAct, pPar ) ); break;
+ }
+ pImpl->Enter();
+}
+
+/* This is not inline because we don't want the SwImplEnterLeave definition inside
+ * dbg_lay.hxx.
+ */
+SwEnterLeave::~SwEnterLeave()
+{
+ if (pImpl)
+ pImpl->Leave();
+}
+
+void SwImplEnterLeave::Enter()
+{
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::Start, m_pParam);
+}
+
+void SwImplEnterLeave::Leave() {
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, m_pParam);
+}
+
+void SwSizeEnterLeave::Leave()
+{
+ m_nFrameHeight = m_pFrame->getFrameArea().Height() - m_nFrameHeight;
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, &m_nFrameHeight);
+}
+
+void SwUpperEnterLeave::Enter()
+{
+ m_nFrameId = m_pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId(m_pFrame->GetUpper())) : 0;
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::Start, &m_nFrameId);
+}
+
+void SwUpperEnterLeave::Leave()
+{
+ m_nFrameId = m_pFrame->GetUpper() ? sal_uInt16(lcl_GetFrameId(m_pFrame->GetUpper())) : 0;
+ SwProtocol::Record(m_pFrame, m_nFunction, DbgAction::End, &m_nFrameId);
+}
+
+void SwFrameChangesLeave::Enter()
+{
+}
+
+void SwFrameChangesLeave::Leave()
+{
+ if (m_pFrame->getFrameArea() != m_aFrame)
+ SwProtocol::Record(m_pFrame, PROT::FrmChanges, DbgAction::NONE, &m_aFrame);
+}
+
+#endif // DBG_UTIL
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/dumpfilter.cxx b/sw/source/core/layout/dumpfilter.cxx
new file mode 100644
index 000000000..21d1ec354
--- /dev/null
+++ b/sw/source/core/layout/dumpfilter.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <dumpfilter.hxx>
+
+#include <wrtsh.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <docsh.hxx>
+#include <rootfrm.hxx>
+#include <unotxdoc.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ int writeCallback( void* pContext, const char* sBuffer, int nLen )
+ {
+ int written = nLen;
+
+ // Actually write bytes to XOutputSream
+ try
+ {
+ uno::XInterface* pObj = static_cast<uno::XInterface*>(pContext);
+ uno::Reference< io::XOutputStream > xOut( pObj, uno::UNO_QUERY_THROW );
+
+ // Don't output the terminating \0 to the xml or the file will be invalid
+ uno::Sequence< sal_Int8 > seq( nLen );
+ strncpy( reinterpret_cast<char *>(seq.getArray()), sBuffer, nLen );
+ xOut->writeBytes( seq );
+ }
+ catch (const uno::Exception&)
+ {
+ written = -1;
+ }
+
+ return written;
+ }
+
+ int closeCallback( void* pContext )
+ {
+ int result = 0;
+ try
+ {
+ uno::XInterface* pObj = static_cast<uno::XInterface*>(pContext);
+ uno::Reference< io::XOutputStream > xOut( pObj, uno::UNO_QUERY_THROW );
+ xOut->closeOutput( );
+ }
+ catch (const uno::Exception&)
+ {
+ result = -1;
+ }
+ return result;
+ }
+}
+
+namespace sw
+{
+
+ LayoutDumpFilter::LayoutDumpFilter( )
+ {
+ }
+
+ LayoutDumpFilter::~LayoutDumpFilter( )
+ {
+ }
+
+ // XFilter
+ sal_Bool LayoutDumpFilter::filter( const uno::Sequence< beans::PropertyValue >& aDescriptor )
+ {
+ bool bRet = false;
+
+ utl::MediaDescriptor aMediaDesc = aDescriptor;
+
+ // Get the output stream
+ uno::Reference< io::XOutputStream > xOut = aMediaDesc.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_OUTPUTSTREAM,
+ uno::Reference< io::XOutputStream >() );
+
+ // Actually get the SwRootFrame to call dumpAsXml
+ auto pXDoc = comphelper::getFromUnoTunnel<SwXTextDocument>(m_xSrcDoc);
+ if ( pXDoc )
+ {
+ SwRootFrame* pLayout = pXDoc->GetDocShell()->GetWrtShell()->GetLayout();
+
+ // Get sure that the whole layout is processed: set a visible area
+ // even though there isn't any need of it
+ pXDoc->GetDocShell()->GetWrtShell()->StartAction();
+ tools::Rectangle aRect( 0, 0, 26000, 21000 );
+ pXDoc->GetDocShell()->SetVisArea( aRect );
+ pLayout->InvalidateAllContent( SwInvalidateFlags::Size );
+ pXDoc->GetDocShell()->GetWrtShell()->EndAction();
+
+ // Dump the layout XML into the XOutputStream
+ xmlOutputBufferPtr outBuffer = xmlOutputBufferCreateIO(
+ writeCallback, closeCallback, static_cast<void*>(xOut.get()), nullptr );
+
+ xmlTextWriterPtr writer = xmlNewTextWriter( outBuffer );
+ xmlTextWriterSetIndent(writer, 1);
+ (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
+
+ // TODO This doesn't export the whole XML file, whereas dumpAsXML() does it nicely
+ pLayout->dumpAsXml( writer );
+
+ (void)xmlTextWriterEndDocument( writer );
+ xmlFreeTextWriter( writer );
+
+ bRet = true;
+ }
+
+ return bRet;
+ }
+
+ void LayoutDumpFilter::cancel( )
+ {
+ }
+
+ // XExporter
+ void LayoutDumpFilter::setSourceDocument( const uno::Reference< lang::XComponent >& xDoc )
+ {
+ m_xSrcDoc = xDoc;
+ }
+
+ // XInitialization
+ void LayoutDumpFilter::initialize( const uno::Sequence< uno::Any >& )
+ {
+ }
+
+ // XServiceInfo
+ OUString LayoutDumpFilter::getImplementationName( )
+ {
+ return "com.sun.star.comp.Writer.LayoutDump";
+ }
+
+ sal_Bool LayoutDumpFilter::supportsService( const OUString& rServiceName )
+ {
+ return cppu::supportsService(this, rServiceName);
+ }
+
+ uno::Sequence< OUString > LayoutDumpFilter::getSupportedServiceNames()
+ {
+ return { "com.sun.star.document.ExportFilter" };
+ }
+
+} // Namespace sw
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_Writer_LayoutDump_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new sw::LayoutDumpFilter());
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/findfrm.cxx b/sw/source/core/layout/findfrm.cxx
new file mode 100644
index 000000000..3d92f2a9c
--- /dev/null
+++ b/sw/source/core/layout/findfrm.cxx
@@ -0,0 +1,1905 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cellfrm.hxx>
+#include <rowfrm.hxx>
+#include <swtable.hxx>
+#include <notxtfrm.hxx>
+#include <tabfrm.hxx>
+#include <sectfrm.hxx>
+#include <frmatr.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtclbl.hxx>
+#include <txtfrm.hxx>
+#include <bodyfrm.hxx>
+#include <calbck.hxx>
+#include <viewopt.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+
+/// Searches the first ContentFrame in BodyText below the page.
+SwLayoutFrame *SwFootnoteBossFrame::FindBodyCont()
+{
+ SwFrame *pLay = Lower();
+ while ( pLay && !pLay->IsBodyFrame() )
+ pLay = pLay->GetNext();
+ return static_cast<SwLayoutFrame*>(pLay);
+}
+
+/// Searches the last ContentFrame in BodyText below the page.
+SwContentFrame *SwPageFrame::FindLastBodyContent()
+{
+ SwContentFrame *pRet = FindFirstBodyContent();
+ SwContentFrame *pNxt = pRet;
+ while ( pNxt && pNxt->IsInDocBody() && IsAnLower( pNxt ) )
+ { pRet = pNxt;
+ pNxt = pNxt->FindNextCnt();
+ }
+ return pRet;
+}
+
+/**
+ * Checks if the frame contains one or more ContentFrame's anywhere in his
+ * subsidiary structure; if so the first found ContentFrame is returned.
+ */
+const SwContentFrame *SwLayoutFrame::ContainsContent() const
+{
+ //Search downwards the layout leaf and if there is no content, jump to the
+ //next leaf until content is found or we leave "this".
+ //Sections: Content next to sections would not be found this way (empty
+ //sections directly next to ContentFrame) therefore we need to recursively
+ //search for them even if it's more complex.
+
+ const SwLayoutFrame *pLayLeaf = this;
+ do
+ {
+ while ( (!pLayLeaf->IsSctFrame() || pLayLeaf == this ) &&
+ pLayLeaf->Lower() && pLayLeaf->Lower()->IsLayoutFrame() )
+ pLayLeaf = static_cast<const SwLayoutFrame*>(pLayLeaf->Lower());
+
+ if( pLayLeaf->IsSctFrame() && pLayLeaf != this )
+ {
+ const SwContentFrame *pCnt = pLayLeaf->ContainsContent();
+ if( pCnt )
+ return pCnt;
+ if( pLayLeaf->GetNext() )
+ {
+ if( pLayLeaf->GetNext()->IsLayoutFrame() )
+ {
+ pLayLeaf = static_cast<const SwLayoutFrame*>(pLayLeaf->GetNext());
+ continue;
+ }
+ else
+ return static_cast<const SwContentFrame*>(pLayLeaf->GetNext());
+ }
+ }
+ else if ( pLayLeaf->Lower() )
+ return static_cast<const SwContentFrame*>(pLayLeaf->Lower());
+
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ if( !IsAnLower( pLayLeaf) )
+ return nullptr;
+ } while( pLayLeaf );
+ return nullptr;
+}
+
+/**
+ * Calls ContainsAny first to reach the innermost cell. From there we walk back
+ * up to the first SwCellFrame. Since we use SectionFrames, ContainsContent()->GetUpper()
+ * is not enough anymore.
+ */
+const SwCellFrame *SwLayoutFrame::FirstCell() const
+{
+ const SwFrame* pCnt = ContainsAny();
+ while( pCnt && !pCnt->IsCellFrame() )
+ pCnt = pCnt->GetUpper();
+ return static_cast<const SwCellFrame*>(pCnt);
+}
+
+/** return ContentFrames, sections, and tables.
+ *
+ * @param _bInvestigateFootnoteForSections controls investigation of content of footnotes for sections.
+ * @see ContainsContent
+ */
+const SwFrame *SwLayoutFrame::ContainsAny( const bool _bInvestigateFootnoteForSections ) const
+{
+ //Search downwards the layout leaf and if there is no content, jump to the
+ //next leaf until content is found, we leave "this" or until we found
+ //a SectionFrame or a TabFrame.
+
+ const SwLayoutFrame *pLayLeaf = this;
+ const bool bNoFootnote = IsSctFrame() && !_bInvestigateFootnoteForSections;
+ do
+ {
+ while ( ( (!pLayLeaf->IsSctFrame() && !pLayLeaf->IsTabFrame())
+ || pLayLeaf == this ) &&
+ pLayLeaf->Lower() && pLayLeaf->Lower()->IsLayoutFrame() )
+ pLayLeaf = static_cast<const SwLayoutFrame*>(pLayLeaf->Lower());
+
+ if( ( pLayLeaf->IsTabFrame() || pLayLeaf->IsSctFrame() )
+ && pLayLeaf != this )
+ {
+ // Now we also return "deleted" SectionFrames so they can be
+ // maintained on SaveContent and RestoreContent
+ return pLayLeaf;
+ }
+ else if ( pLayLeaf->Lower() )
+ return static_cast<const SwContentFrame*>(pLayLeaf->Lower());
+
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ if( bNoFootnote && pLayLeaf && pLayLeaf->IsInFootnote() )
+ {
+ do
+ {
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ } while( pLayLeaf && pLayLeaf->IsInFootnote() );
+ }
+ if( !IsAnLower( pLayLeaf) )
+ return nullptr;
+ } while( pLayLeaf );
+ return nullptr;
+}
+
+bool SwLayoutFrame::ContainsDeleteForbiddenLayFrame() const
+{
+ if (IsDeleteForbidden())
+ {
+ return true;
+ }
+ for (SwFrame const* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ if (!pFrame->IsLayoutFrame())
+ {
+ continue;
+ }
+ SwLayoutFrame const*const pLay(static_cast<SwLayoutFrame const*>(pFrame));
+ if (pLay->ContainsDeleteForbiddenLayFrame())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+const SwFrame* SwFrame::GetLower() const
+{
+ return IsLayoutFrame() ? static_cast<const SwLayoutFrame*>(this)->Lower() : nullptr;
+}
+
+SwFrame* SwFrame::GetLower()
+{
+ return IsLayoutFrame() ? static_cast<SwLayoutFrame*>(this)->Lower() : nullptr;
+}
+
+SwContentFrame* SwFrame::FindPrevCnt( )
+{
+ if ( GetPrev() && GetPrev()->IsContentFrame() )
+ return static_cast<SwContentFrame*>(GetPrev());
+ else
+ return FindPrevCnt_();
+}
+
+const SwContentFrame* SwFrame::FindPrevCnt() const
+{
+ if ( GetPrev() && GetPrev()->IsContentFrame() )
+ return static_cast<const SwContentFrame*>(GetPrev());
+ else
+ return const_cast<SwFrame*>(this)->FindPrevCnt_();
+}
+
+SwContentFrame *SwFrame::FindNextCnt( const bool _bInSameFootnote )
+{
+ if ( mpNext && mpNext->IsContentFrame() )
+ return static_cast<SwContentFrame*>(mpNext);
+ else
+ return FindNextCnt_( _bInSameFootnote );
+}
+
+const SwContentFrame *SwFrame::FindNextCnt( const bool _bInSameFootnote ) const
+{
+ if ( mpNext && mpNext->IsContentFrame() )
+ return static_cast<SwContentFrame*>(mpNext);
+ else
+ return const_cast<SwFrame*>(this)->FindNextCnt_( _bInSameFootnote );
+}
+
+bool SwLayoutFrame::IsAnLower( const SwFrame *pAssumed ) const
+{
+ const SwFrame *pUp = pAssumed;
+ while ( pUp )
+ {
+ if ( pUp == this )
+ return true;
+ if ( pUp->IsFlyFrame() )
+ pUp = static_cast<const SwFlyFrame*>(pUp)->GetAnchorFrame();
+ else
+ pUp = pUp->GetUpper();
+ }
+ return false;
+}
+
+/** method to check relative position of layout frame to
+ a given layout frame.
+
+ OD 08.11.2002 - refactoring of pseudo-local method <lcl_Apres(..)> in
+ <txtftn.cxx> for #104840#.
+
+ @param _aCheckRefLayFrame
+ constant reference of an instance of class <SwLayoutFrame> which
+ is used as the reference for the relative position check.
+
+ @return true, if <this> is positioned before the layout frame <p>
+*/
+bool SwLayoutFrame::IsBefore( const SwLayoutFrame* _pCheckRefLayFrame ) const
+{
+ OSL_ENSURE( !IsRootFrame() , "<IsBefore> called at a <SwRootFrame>.");
+ OSL_ENSURE( !_pCheckRefLayFrame->IsRootFrame() , "<IsBefore> called with a <SwRootFrame>.");
+
+ bool bReturn;
+
+ // check, if on different pages
+ const SwPageFrame *pMyPage = FindPageFrame();
+ const SwPageFrame *pCheckRefPage = _pCheckRefLayFrame->FindPageFrame();
+ if( pMyPage != pCheckRefPage )
+ {
+ // being on different page as check reference
+ bReturn = pMyPage->GetPhyPageNum() < pCheckRefPage->GetPhyPageNum();
+ }
+ else
+ {
+ // being on same page as check reference
+ // --> search my supreme parent <pUp>, which doesn't contain check reference.
+ const SwLayoutFrame* pUp = this;
+ while ( pUp->GetUpper() &&
+ !pUp->GetUpper()->IsAnLower( _pCheckRefLayFrame )
+ )
+ pUp = pUp->GetUpper();
+ if( !pUp->GetUpper() )
+ {
+ // can occur, if <this> is a fly frm
+ bReturn = false;
+ }
+ else
+ {
+ // travel through the next's of <pUp> and check if one of these
+ // contain the check reference.
+ const SwLayoutFrame* pUpNext = static_cast<const SwLayoutFrame*>(pUp->GetNext());
+ while ( pUpNext &&
+ !pUpNext->IsAnLower( _pCheckRefLayFrame ) )
+ {
+ pUpNext = static_cast<const SwLayoutFrame*>(pUpNext->GetNext());
+ }
+ bReturn = pUpNext != nullptr;
+ }
+ }
+
+ return bReturn;
+}
+
+// Local helper functions for GetNextLayoutLeaf
+
+static const SwFrame* lcl_FindLayoutFrame( const SwFrame* pFrame, bool bNext )
+{
+ const SwFrame* pRet = nullptr;
+ if ( pFrame->IsFlyFrame() )
+ pRet = bNext ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink();
+ else
+ pRet = bNext ? pFrame->GetNext() : pFrame->GetPrev();
+
+ return pRet;
+}
+
+static const SwFrame* lcl_GetLower( const SwFrame* pFrame, bool bFwd )
+{
+ if ( !pFrame->IsLayoutFrame() )
+ return nullptr;
+
+ return bFwd ?
+ static_cast<const SwLayoutFrame*>(pFrame)->Lower() :
+ static_cast<const SwLayoutFrame*>(pFrame)->GetLastLower();
+}
+
+/**
+ * Finds the next layout leaf. This is a layout frame, which does not
+ * have a lower which is a LayoutFrame. That means, pLower can be 0 or a
+ * content frame.
+ *
+ * However, pLower may be a TabFrame
+ */
+const SwLayoutFrame *SwFrame::ImplGetNextLayoutLeaf( bool bFwd ) const
+{
+ const SwFrame *pFrame = this;
+ const SwLayoutFrame *pLayoutFrame = nullptr;
+ const SwFrame *p = nullptr;
+ bool bGoingUp = !bFwd; // false for forward, true for backward
+ do {
+
+ bool bGoingFwdOrBwd = false;
+
+ bool bGoingDown = !bGoingUp;
+ if (bGoingDown)
+ {
+ p = lcl_GetLower( pFrame, bFwd );
+ bGoingDown = nullptr != p;
+ }
+ if ( !bGoingDown )
+ {
+ // I cannot go down, because either I'm currently going up or
+ // because the is no lower.
+ // I'll try to go forward:
+ p = lcl_FindLayoutFrame( pFrame, bFwd );
+ bGoingFwdOrBwd = nullptr != p;
+ if ( !bGoingFwdOrBwd )
+ {
+ // I cannot go forward, because there is no next frame.
+ // I'll try to go up:
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ {
+ // I cannot go up, because there is no upper frame.
+ return nullptr;
+ }
+ }
+ }
+
+ // If I could not go down or forward, I'll have to go up
+ bGoingUp = !bGoingFwdOrBwd && !bGoingDown;
+
+ pFrame = p;
+ p = lcl_GetLower( pFrame, true );
+
+ } while( ( p && !p->IsFlowFrame() ) ||
+ pFrame == this ||
+ nullptr == ( pLayoutFrame = pFrame->IsLayoutFrame() ? static_cast<const SwLayoutFrame*>(pFrame) : nullptr ) ||
+ pLayoutFrame->IsAnLower( this ) );
+
+ return pLayoutFrame;
+}
+
+/**
+ * Walk back inside the tree: grab the subordinate Frame if one exists and the
+ * last step was not moving up a level (this would lead to an infinite up/down
+ * loop!). With this we ensure that during walking back we search through all
+ * sub trees. If we walked downwards we have to go to the end of the chain first
+ * because we go backwards from the last Frame inside another Frame. Walking
+ * forward works the same.
+ *
+ * @warning fixes here may also need to be applied to the @{lcl_NextFrame} method above
+ */
+const SwContentFrame* SwContentFrame::ImplGetNextContentFrame( bool bFwd ) const
+{
+ const SwFrame *pFrame = this;
+ const SwContentFrame *pContentFrame = nullptr;
+ bool bGoingUp = false;
+ do {
+ const SwFrame *p = nullptr;
+ bool bGoingFwdOrBwd = false;
+
+ bool bGoingDown = !bGoingUp;
+ if (bGoingDown)
+ {
+ p = lcl_GetLower( pFrame, true ) ;
+ bGoingDown = nullptr != p;
+ }
+ if ( !bGoingDown )
+ {
+ p = lcl_FindLayoutFrame( pFrame, bFwd );
+ bGoingFwdOrBwd = nullptr != p;
+ if ( !bGoingFwdOrBwd )
+ {
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ {
+ return nullptr;
+ }
+ }
+ }
+
+ bGoingUp = !(bGoingFwdOrBwd || bGoingDown);
+ assert(p);
+ if (!bFwd && bGoingDown)
+ {
+ while ( p->GetNext() )
+ p = p->GetNext();
+ }
+
+ pFrame = p;
+ } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? static_cast<const SwContentFrame*>(pFrame) : nullptr) ));
+
+ return pContentFrame;
+}
+
+SwPageFrame* SwFrame::ImplFindPageFrame()
+{
+ SwFrame *pRet = this;
+ while ( pRet && !pRet->IsPageFrame() )
+ {
+ if ( pRet->GetUpper() )
+ pRet = pRet->GetUpper();
+ else if ( pRet->IsFlyFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ const auto pFly(static_cast<SwFlyFrame*>(pRet));
+ pRet = pFly->GetPageFrame();
+ if (pRet == nullptr)
+ pRet = pFly->AnchorFrame();
+ }
+ else
+ return nullptr;
+ }
+ return static_cast<SwPageFrame*>(pRet);
+}
+
+SwFootnoteBossFrame* SwFrame::FindFootnoteBossFrame( bool bFootnotes )
+{
+ SwFrame *pRet = this;
+ // Footnote bosses can't exist inside a table; also sections with columns
+ // don't contain footnote texts there
+ if( pRet->IsInTab() )
+ pRet = pRet->FindTabFrame();
+
+ // tdf139336: put the footnotes into the page frame (instead of a column frame)
+ // to avoid maximizing the section to the full page.... if:
+ // - it is in a section
+ // - collect footnotes at section end (FootnoteAtEnd) is not set
+ // - columns are evenly distributed (=balanced) is not set
+ // Note 1: at page end, the footnotes have no multi-column-capability,
+ // so this fix is used only where there is better chance to help
+ // Note 2: If balanced is not set, there is a higher chance that the 1. column will reach
+ // the end of the page... if that happens the section will be maximized anyway.
+ // Note 3: The user will be able to easily choose the old layout (with multi-column footnotes)
+ // with this setting.
+ // similar case can be reached with a page break + FootnoteAtEnd setting
+ SwSectionFrame* pSectframe = pRet->FindSctFrame();
+ bool bMoveToPageFrame = false;
+ // tdf146704: only if it is really a footnote, not an endnote.
+ // tdf54465: compatibility flag to make old odt files keep these full page sections.
+ if (bFootnotes && pSectframe
+ && pSectframe->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::FOOTNOTE_IN_COLUMN_TO_PAGEEND))
+ {
+ SwSection* pSect = pSectframe->GetSection();
+ if (pSect) {
+ bool bNoBalance = pSect->GetFormat()->GetBalancedColumns().GetValue();
+ bool bFAtEnd = pSectframe->IsFootnoteAtEnd();
+ bMoveToPageFrame = !bFAtEnd && !bNoBalance;
+ }
+ }
+ while (pRet
+ && ((!bMoveToPageFrame && !pRet->IsFootnoteBossFrame())
+ || (bMoveToPageFrame && !pRet->IsPageFrame())))
+ {
+ if ( pRet->GetUpper() )
+ pRet = pRet->GetUpper();
+ else if ( pRet->IsFlyFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ if ( static_cast<SwFlyFrame*>(pRet)->GetPageFrame() )
+ pRet = static_cast<SwFlyFrame*>(pRet)->GetPageFrame();
+ else
+ pRet = static_cast<SwFlyFrame*>(pRet)->AnchorFrame();
+ }
+ else
+ return nullptr;
+ }
+ if( bFootnotes && pRet && pRet->IsColumnFrame() &&
+ !pRet->GetNext() && !pRet->GetPrev() )
+ {
+ SwSectionFrame* pSct = pRet->FindSctFrame();
+ OSL_ENSURE( pSct, "FindFootnoteBossFrame: Single column outside section?" );
+ if( !pSct->IsFootnoteAtEnd() )
+ return pSct->FindFootnoteBossFrame( true );
+ }
+ return static_cast<SwFootnoteBossFrame*>(pRet);
+}
+
+SwTabFrame* SwFrame::ImplFindTabFrame()
+{
+ SwFrame *pRet = this;
+ while ( !pRet->IsTabFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<SwTabFrame*>(pRet);
+}
+
+SwSectionFrame* SwFrame::ImplFindSctFrame()
+{
+ SwFrame *pRet = this;
+ while ( !pRet->IsSctFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<SwSectionFrame*>(pRet);
+}
+
+const SwBodyFrame* SwFrame::ImplFindBodyFrame() const
+{
+ const SwFrame *pRet = this;
+ while ( !pRet->IsBodyFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<const SwBodyFrame*>(pRet);
+}
+
+SwFootnoteFrame *SwFrame::ImplFindFootnoteFrame()
+{
+ SwFrame *pRet = this;
+ while ( !pRet->IsFootnoteFrame() )
+ {
+ pRet = pRet->GetUpper();
+ if ( !pRet )
+ return nullptr;
+ }
+ return static_cast<SwFootnoteFrame*>(pRet);
+}
+
+SwFlyFrame *SwFrame::ImplFindFlyFrame()
+{
+ SwFrame *pRet = this;
+ do
+ {
+ if ( pRet->IsFlyFrame() )
+ return static_cast<SwFlyFrame*>(pRet);
+ else
+ pRet = pRet->GetUpper();
+ } while ( pRet );
+ return nullptr;
+}
+
+SwFrame *SwFrame::FindColFrame()
+{
+ SwFrame *pFrame = this;
+ do
+ { pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsColumnFrame() );
+ return pFrame;
+}
+
+SwRowFrame *SwFrame::FindRowFrame()
+{
+ SwFrame *pFrame = this;
+ do
+ { pFrame = pFrame->GetUpper();
+ } while ( pFrame && !pFrame->IsRowFrame() );
+ return dynamic_cast< SwRowFrame* >( pFrame );
+}
+
+SwFrame* SwFrame::FindFooterOrHeader()
+{
+ SwFrame* pRet = this;
+ do
+ {
+ if (pRet->GetType() & FRM_HEADFOOT) //header and footer
+ return pRet;
+ else if ( pRet->GetUpper() )
+ pRet = pRet->GetUpper();
+ else if ( pRet->IsFlyFrame() )
+ pRet = static_cast<SwFlyFrame*>(pRet)->AnchorFrame();
+ else
+ return nullptr;
+ } while ( pRet );
+ return pRet;
+}
+
+const SwFootnoteFrame* SwFootnoteContFrame::FindFootNote() const
+{
+ const SwFootnoteFrame* pRet = static_cast<const SwFootnoteFrame*>(Lower());
+ if( pRet && !pRet->GetAttr()->GetFootnote().IsEndNote() )
+ return pRet;
+ return nullptr;
+}
+
+const SwPageFrame* SwRootFrame::GetPageAtPos( const Point& rPt, const Size* pSize, bool bExtend ) const
+{
+ const SwPageFrame* pRet = nullptr;
+
+ SwRect aRect;
+ if ( pSize )
+ {
+ aRect.Pos() = rPt;
+ aRect.SSize( *pSize );
+ }
+
+ const SwFrame* pPage = Lower();
+
+ if ( !bExtend )
+ {
+ if( !getFrameArea().Contains( rPt ) )
+ return nullptr;
+
+ // skip pages above point:
+ while( pPage && rPt.Y() > pPage->getFrameArea().Bottom() )
+ pPage = pPage->GetNext();
+ }
+
+ OSL_ENSURE( GetPageNum() <= maPageRects.size(), "number of pages differs from page rect array size" );
+ size_t nPageIdx = 0;
+
+ while ( pPage && !pRet )
+ {
+ const SwRect& rBoundRect = bExtend ? maPageRects[ nPageIdx++ ] : pPage->getFrameArea();
+
+ if ( (!pSize && rBoundRect.Contains(rPt)) ||
+ (pSize && rBoundRect.Overlaps(aRect)) )
+ {
+ pRet = static_cast<const SwPageFrame*>(pPage);
+ }
+
+ pPage = pPage->GetNext();
+ }
+
+ return pRet;
+}
+
+bool SwRootFrame::IsBetweenPages(const Point& rPt) const
+{
+ if (!getFrameArea().Contains(rPt))
+ return false;
+
+ // top visible page
+ const SwFrame* pPage = Lower();
+ if (pPage == nullptr)
+ return false;
+
+ // skip pages above point:
+ while (pPage && rPt.Y() > pPage->getFrameArea().Bottom())
+ pPage = pPage->GetNext();
+
+ if (pPage &&
+ rPt.X() >= pPage->getFrameArea().Left() &&
+ rPt.X() <= pPage->getFrameArea().Right())
+ {
+ // Trivial case when we're right in between.
+ if (!pPage->getFrameArea().Contains(rPt))
+ return true;
+
+ // In normal mode the gap is large enough and
+ // header/footer mouse interaction competes with
+ // handling hide-whitespace within them.
+ // In hide-whitespace, however, the gap is too small
+ // for convenience and there are no headers/footers.
+ const SwViewShell *pSh = GetCurrShell();
+ if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
+ {
+ constexpr SwTwips constMargin = o3tl::convert(tools::Long(2), o3tl::Length::mm, o3tl::Length::twip);
+
+ // If we are really close to the bottom or top of a page.
+ const auto toEdge = std::min(std::abs(pPage->getFrameArea().Top() - rPt.Y()),
+ std::abs(pPage->getFrameArea().Bottom() - rPt.Y()));
+ return toEdge <= constMargin;
+ }
+ }
+
+ return false;
+}
+
+const SvxFormatBreakItem& SwFrame::GetBreakItem() const
+{
+ return GetAttrSet()->GetBreak();
+}
+
+const SwFormatPageDesc& SwFrame::GetPageDescItem() const
+{
+ return GetAttrSet()->GetPageDesc();
+}
+
+const SvxFormatBreakItem& SwTextFrame::GetBreakItem() const
+{
+ return GetTextNodeFirst()->GetSwAttrSet().GetBreak();
+}
+
+const SwFormatPageDesc& SwTextFrame::GetPageDescItem() const
+{
+ return GetTextNodeFirst()->GetSwAttrSet().GetPageDesc();
+}
+
+const SwAttrSet* SwFrame::GetAttrSet() const
+{
+ if (IsTextFrame())
+ {
+ return &static_cast<const SwTextFrame*>(this)->GetTextNodeForParaProps()->GetSwAttrSet();
+ }
+ else if (IsNoTextFrame())
+ {
+ return &static_cast<const SwNoTextFrame*>(this)->GetNode()->GetSwAttrSet();
+ }
+ else
+ {
+ assert(IsLayoutFrame());
+ return &static_cast<const SwLayoutFrame*>(this)->GetFormat()->GetAttrSet();
+ }
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwFrame::getSdrAllFillAttributesHelper() const
+{
+ if (IsTextFrame())
+ {
+ return static_cast<const SwTextFrame*>(this)->GetTextNodeForParaProps()->getSdrAllFillAttributesHelper();
+ }
+ else if (IsNoTextFrame())
+ {
+ return static_cast<const SwNoTextFrame*>(this)->GetNode()->getSdrAllFillAttributesHelper();
+ }
+ else
+ {
+ return static_cast< const SwLayoutFrame* >(this)->GetFormat()->getSdrAllFillAttributesHelper();
+ }
+}
+
+bool SwFrame::supportsFullDrawingLayerFillAttributeSet() const
+{
+ if (IsContentFrame())
+ {
+ return true;
+ }
+ else
+ {
+ return static_cast< const SwLayoutFrame* >(this)->GetFormat()->supportsFullDrawingLayerFillAttributeSet();
+ }
+}
+
+/*
+ * SwFrame::FindNext_(), FindPrev_(), InvalidateNextPos()
+ * FindNextCnt_() visits tables and sections and only returns SwContentFrames.
+ *
+ * Description Invalidates the position of the next frame.
+ * This is the direct successor or in case of ContentFrames the next
+ * ContentFrame which sits in the same flow as I do:
+ * - body,
+ * - footnote,
+ * - in headers/footers the notification only needs to be forwarded
+ * inside the section
+ * - same for Flys
+ * - Contents in tabs remain only inside their cell
+ * - in principle tables behave exactly like the Contents
+ * - sections also
+ */
+// This helper function is an equivalent to the ImplGetNextContentFrame() method,
+// besides ContentFrames this function also returns TabFrames and SectionFrames.
+static SwFrame* lcl_NextFrame( SwFrame* pFrame )
+{
+ SwFrame *pRet = nullptr;
+ bool bGoingUp = false;
+ do {
+ SwFrame *p = nullptr;
+
+ bool bGoingFwd = false;
+ bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame();
+ if (bGoingDown)
+ {
+ p = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ bGoingDown = nullptr != p;
+ }
+ if( !bGoingDown )
+ {
+ p = pFrame->IsFlyFrame() ? static_cast<SwFlyFrame*>(pFrame)->GetNextLink() : pFrame->GetNext();
+ bGoingFwd = nullptr != p;
+ if ( !bGoingFwd )
+ {
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ {
+ return nullptr;
+ }
+ }
+ }
+ bGoingUp = !(bGoingFwd || bGoingDown);
+ pFrame = p;
+ } while ( nullptr == (pRet = ( ( pFrame->IsContentFrame() || ( !bGoingUp &&
+ ( pFrame->IsTabFrame() || pFrame->IsSctFrame() ) ) )? pFrame : nullptr ) ) );
+ return pRet;
+}
+
+SwFrame *SwFrame::FindNext_()
+{
+ bool bIgnoreTab = false;
+ SwFrame *pThis = this;
+
+ if ( IsTabFrame() )
+ {
+ //The last Content of the table gets picked up and his follower is
+ //returned. To be able to deactivate the special case for tables
+ //(see below) bIgnoreTab will be set.
+ if ( static_cast<SwTabFrame*>(this)->GetFollow() )
+ return static_cast<SwTabFrame*>(this)->GetFollow();
+
+ pThis = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ if ( !pThis )
+ pThis = this;
+ bIgnoreTab = true;
+ }
+ else if ( IsSctFrame() )
+ {
+ //The last Content of the section gets picked and his follower is returned.
+ if ( static_cast<SwSectionFrame*>(this)->GetFollow() )
+ return static_cast<SwSectionFrame*>(this)->GetFollow();
+
+ pThis = static_cast<SwSectionFrame*>(this)->FindLastContent();
+ if ( !pThis )
+ pThis = this;
+ }
+ else if ( IsContentFrame() )
+ {
+ if( static_cast<SwContentFrame*>(this)->GetFollow() )
+ return static_cast<SwContentFrame*>(this)->GetFollow();
+ }
+ else if ( IsRowFrame() )
+ {
+ SwFrame* pMyUpper = GetUpper();
+ if ( pMyUpper->IsTabFrame() && static_cast<SwTabFrame*>(pMyUpper)->GetFollow() )
+ return static_cast<SwTabFrame*>(pMyUpper)->GetFollow()->GetLower();
+ else return nullptr;
+ }
+ else
+ return nullptr;
+
+ SwFrame* pRet = nullptr;
+ const bool bFootnote = pThis->IsInFootnote();
+ if ( !bIgnoreTab && pThis->IsInTab() )
+ {
+ SwLayoutFrame *pUp = pThis->GetUpper();
+ while (pUp && !pUp->IsCellFrame())
+ pUp = pUp->GetUpper();
+ assert(pUp && "Content flag says it's in table but it's not in cell.");
+ SwFrame* pNxt = pUp ? static_cast<SwCellFrame*>(pUp)->GetFollowCell() : nullptr;
+ if ( pNxt )
+ pNxt = static_cast<SwCellFrame*>(pNxt)->ContainsContent();
+ if ( !pNxt )
+ {
+ pNxt = lcl_NextFrame( pThis );
+ if (pUp && pUp->IsAnLower(pNxt))
+ pRet = pNxt;
+ }
+ else
+ pRet = pNxt;
+ }
+ else
+ {
+ const bool bBody = pThis->IsInDocBody();
+ SwFrame *pNxtCnt = lcl_NextFrame( pThis );
+ if ( pNxtCnt )
+ {
+ if ( bBody || bFootnote )
+ {
+ while ( pNxtCnt )
+ {
+ // OD 02.04.2003 #108446# - check for endnote, only if found
+ // next content isn't contained in a section, that collect its
+ // endnotes at its end.
+ bool bEndn = IsInSct() && !IsSctFrame() &&
+ ( !pNxtCnt->IsInSct() ||
+ !pNxtCnt->FindSctFrame()->IsEndnAtEnd()
+ );
+ if ( ( bBody && pNxtCnt->IsInDocBody() ) ||
+ ( pNxtCnt->IsInFootnote() &&
+ ( bFootnote ||
+ ( bEndn && pNxtCnt->FindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
+ )
+ )
+ )
+ {
+ pRet = pNxtCnt->IsInTab() ? pNxtCnt->FindTabFrame()
+ : pNxtCnt;
+ break;
+ }
+ pNxtCnt = lcl_NextFrame( pNxtCnt );
+ }
+ }
+ else if ( pThis->IsInFly() )
+ {
+ pRet = pNxtCnt->IsInTab() ? pNxtCnt->FindTabFrame()
+ : pNxtCnt;
+ }
+ else //footer-/or header section
+ {
+ const SwFrame *pUp = pThis->GetUpper();
+ const SwFrame *pCntUp = pNxtCnt->GetUpper();
+ while ( pUp && pUp->GetUpper() &&
+ !pUp->IsHeaderFrame() && !pUp->IsFooterFrame() )
+ pUp = pUp->GetUpper();
+ while ( pCntUp && pCntUp->GetUpper() &&
+ !pCntUp->IsHeaderFrame() && !pCntUp->IsFooterFrame() )
+ pCntUp = pCntUp->GetUpper();
+ if ( pCntUp == pUp )
+ {
+ pRet = pNxtCnt->IsInTab() ? pNxtCnt->FindTabFrame()
+ : pNxtCnt;
+ }
+ }
+ }
+ }
+ if( pRet && pRet->IsInSct() )
+ {
+ SwSectionFrame* pSct = pRet->FindSctFrame();
+ //Footnotes in frames with columns must not return the section which
+ //contains the footnote
+ if( !pSct->IsAnLower( this ) &&
+ (!bFootnote || pSct->IsInFootnote() ) )
+ return pSct;
+ }
+ return pRet;
+}
+
+// #i27138# - add parameter <_bInSameFootnote>
+SwContentFrame *SwFrame::FindNextCnt_( const bool _bInSameFootnote )
+{
+ SwFrame *pThis = this;
+
+ if ( IsTabFrame() )
+ {
+ if ( static_cast<SwTabFrame*>(this)->GetFollow() )
+ {
+ pThis = static_cast<SwTabFrame*>(this)->GetFollow()->ContainsContent();
+ if( pThis )
+ return static_cast<SwContentFrame*>(pThis);
+ }
+ pThis = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ if ( !pThis )
+ return nullptr;
+ }
+ else if ( IsSctFrame() )
+ {
+ if ( static_cast<SwSectionFrame*>(this)->GetFollow() )
+ {
+ pThis = static_cast<SwSectionFrame*>(this)->GetFollow()->ContainsContent();
+ if( pThis )
+ return static_cast<SwContentFrame*>(pThis);
+ }
+ pThis = static_cast<SwSectionFrame*>(this)->FindLastContent();
+ if ( !pThis )
+ return nullptr;
+ }
+ else if ( IsContentFrame() && static_cast<SwContentFrame*>(this)->GetFollow() )
+ return static_cast<SwContentFrame*>(this)->GetFollow();
+
+ if ( pThis->IsContentFrame() )
+ {
+ const bool bBody = pThis->IsInDocBody();
+ const bool bFootnote = pThis->IsInFootnote();
+ SwContentFrame *pNxtCnt = static_cast<SwContentFrame*>(pThis)->GetNextContentFrame();
+ if ( pNxtCnt )
+ {
+ // #i27138#
+ if ( bBody || ( bFootnote && !_bInSameFootnote ) )
+ {
+ // handling for environments 'footnotes' and 'document body frames':
+ while ( pNxtCnt )
+ {
+ if ( (bBody && pNxtCnt->IsInDocBody()) ||
+ (bFootnote && pNxtCnt->IsInFootnote()) )
+ return pNxtCnt;
+ pNxtCnt = pNxtCnt->GetNextContentFrame();
+ }
+ }
+ // #i27138#
+ else if ( bFootnote && _bInSameFootnote )
+ {
+ // handling for environments 'each footnote':
+ // Assure that found next content frame belongs to the same footnotes
+ const SwFootnoteFrame* pFootnoteFrameOfNext( pNxtCnt->FindFootnoteFrame() );
+ const SwFootnoteFrame* pFootnoteFrameOfCurr( pThis->FindFootnoteFrame() );
+ OSL_ENSURE( pFootnoteFrameOfCurr,
+ "<SwFrame::FindNextCnt_() - unknown layout situation: current frame has to have an upper footnote frame." );
+ if ( pFootnoteFrameOfNext == pFootnoteFrameOfCurr )
+ {
+ return pNxtCnt;
+ }
+ else if ( pFootnoteFrameOfCurr->GetFollow() )
+ {
+ // next content frame has to be the first content frame
+ // in the follow footnote, which contains a content frame.
+ SwFootnoteFrame* pFollowFootnoteFrameOfCurr(
+ const_cast<SwFootnoteFrame*>(pFootnoteFrameOfCurr) );
+ pNxtCnt = nullptr;
+ do {
+ pFollowFootnoteFrameOfCurr = pFollowFootnoteFrameOfCurr->GetFollow();
+ pNxtCnt = pFollowFootnoteFrameOfCurr->ContainsContent();
+ } while ( !pNxtCnt && pFollowFootnoteFrameOfCurr->GetFollow() );
+ return pNxtCnt;
+ }
+ else
+ {
+ // current content frame is the last content frame in the
+ // footnote - no next content frame exists.
+ return nullptr;
+ }
+ }
+ else if ( pThis->IsInFly() )
+ // handling for environments 'unlinked fly frame' and
+ // 'group of linked fly frames':
+ return pNxtCnt;
+ else
+ {
+ // handling for environments 'page header' and 'page footer':
+ const SwFrame *pUp = pThis->GetUpper();
+ const SwFrame *pCntUp = pNxtCnt->GetUpper();
+ while ( pUp && pUp->GetUpper() &&
+ !pUp->IsHeaderFrame() && !pUp->IsFooterFrame() )
+ pUp = pUp->GetUpper();
+ while ( pCntUp && pCntUp->GetUpper() &&
+ !pCntUp->IsHeaderFrame() && !pCntUp->IsFooterFrame() )
+ pCntUp = pCntUp->GetUpper();
+ if ( pCntUp == pUp )
+ return pNxtCnt;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/** method to determine previous content frame in the same environment
+ for a flow frame (content frame, table frame, section frame)
+
+ OD 2005-11-30 #i27138#
+*/
+SwContentFrame* SwFrame::FindPrevCnt_()
+{
+ if ( !IsFlowFrame() )
+ {
+ // nothing to do, if current frame isn't a flow frame.
+ return nullptr;
+ }
+
+ SwContentFrame* pPrevContentFrame( nullptr );
+
+ // Because method <SwContentFrame::GetPrevContentFrame()> is used to travel
+ // through the layout, a content frame, at which the travel starts, is needed.
+ SwContentFrame* pCurrContentFrame = dynamic_cast<SwContentFrame*>(this);
+
+ // perform shortcut, if current frame is a follow, and
+ // determine <pCurrContentFrame>, if current frame is a table or section frame
+ if ( pCurrContentFrame && pCurrContentFrame->IsFollow() )
+ {
+ // previous content frame is its master content frame
+ pPrevContentFrame = pCurrContentFrame->FindMaster();
+ }
+ else if ( IsTabFrame() )
+ {
+ SwTabFrame* pTabFrame( static_cast<SwTabFrame*>(this) );
+ if ( pTabFrame->IsFollow() )
+ {
+ // previous content frame is the last content of its master table frame
+ pPrevContentFrame = pTabFrame->FindMaster()->FindLastContent();
+ }
+ else
+ {
+ // start content frame for the search is the first content frame of
+ // the table frame.
+ pCurrContentFrame = pTabFrame->ContainsContent();
+ }
+ }
+ else if ( IsSctFrame() )
+ {
+ SwSectionFrame* pSectFrame( static_cast<SwSectionFrame*>(this) );
+ if ( pSectFrame->IsFollow() )
+ {
+ // previous content frame is the last content of its master section frame
+ pPrevContentFrame = pSectFrame->FindMaster()->FindLastContent();
+ }
+ else
+ {
+ // start content frame for the search is the first content frame of
+ // the section frame.
+ pCurrContentFrame = pSectFrame->ContainsContent();
+ }
+ }
+
+ // search for next content frame, depending on the environment, in which
+ // the current frame is in.
+ if ( !pPrevContentFrame && pCurrContentFrame )
+ {
+ pPrevContentFrame = pCurrContentFrame->GetPrevContentFrame();
+ if ( pPrevContentFrame )
+ {
+ if ( pCurrContentFrame->IsInFly() )
+ {
+ // handling for environments 'unlinked fly frame' and
+ // 'group of linked fly frames':
+ // Nothing to do, <pPrevContentFrame> is the one
+ }
+ else
+ {
+ const bool bInDocBody = pCurrContentFrame->IsInDocBody();
+ const bool bInFootnote = pCurrContentFrame->IsInFootnote();
+ if ( bInDocBody )
+ {
+ // handling for environments 'footnotes' and 'document body frames':
+ // Assure that found previous frame is also in one of these
+ // environments. Otherwise, travel further
+ while ( pPrevContentFrame )
+ {
+ if ( ( bInDocBody && pPrevContentFrame->IsInDocBody() ) ||
+ ( bInFootnote && pPrevContentFrame->IsInFootnote() ) )
+ {
+ break;
+ }
+ pPrevContentFrame = pPrevContentFrame->GetPrevContentFrame();
+ }
+ }
+ else if ( bInFootnote )
+ {
+ // handling for environments 'each footnote':
+ // Assure that found next content frame belongs to the same footnotes
+ const SwFootnoteFrame* pFootnoteFrameOfPrev( pPrevContentFrame->FindFootnoteFrame() );
+ const SwFootnoteFrame* pFootnoteFrameOfCurr( pCurrContentFrame->FindFootnoteFrame() );
+ if ( pFootnoteFrameOfPrev != pFootnoteFrameOfCurr )
+ {
+ if ( pFootnoteFrameOfCurr->GetMaster() )
+ {
+ SwFootnoteFrame* pMasterFootnoteFrameOfCurr(
+ const_cast<SwFootnoteFrame*>(pFootnoteFrameOfCurr) );
+ pPrevContentFrame = nullptr;
+ // correct wrong loop-condition
+ do {
+ pMasterFootnoteFrameOfCurr = pMasterFootnoteFrameOfCurr->GetMaster();
+ pPrevContentFrame = pMasterFootnoteFrameOfCurr->FindLastContent();
+ } while ( !pPrevContentFrame &&
+ pMasterFootnoteFrameOfCurr->GetMaster() );
+ }
+ else
+ {
+ // current content frame is the first content in the
+ // footnote - no previous content exists.
+ pPrevContentFrame = nullptr;
+ }
+ }
+ }
+ else
+ {
+ // handling for environments 'page header' and 'page footer':
+ // Assure that found previous frame is also in the same
+ // page header respectively page footer as <pCurrContentFrame>
+ // Note: At this point it's clear that <pCurrContentFrame> has
+ // to be inside a page header or page footer and that
+ // neither <pCurrContentFrame> nor <pPrevContentFrame> are
+ // inside a fly frame.
+ // Thus, method <FindFooterOrHeader()> can be used.
+ OSL_ENSURE( pCurrContentFrame->FindFooterOrHeader(),
+ "<SwFrame::FindPrevCnt_()> - unknown layout situation: current frame should be in page header or page footer" );
+ OSL_ENSURE( !pPrevContentFrame->IsInFly(),
+ "<SwFrame::FindPrevCnt_()> - unknown layout situation: found previous frame should *not* be inside a fly frame." );
+ if ( pPrevContentFrame->FindFooterOrHeader() !=
+ pCurrContentFrame->FindFooterOrHeader() )
+ {
+ pPrevContentFrame = nullptr;
+ }
+ }
+ }
+ }
+ }
+
+ return pPrevContentFrame;
+}
+
+SwFrame *SwFrame::FindPrev_()
+{
+ bool bIgnoreTab = false;
+ SwFrame *pThis = this;
+
+ if ( IsTabFrame() )
+ {
+ //The first Content of the table gets picked up and his predecessor is
+ //returned. To be able to deactivate the special case for tables
+ //(see below) bIgnoreTab will be set.
+ if ( static_cast<SwTabFrame*>(this)->IsFollow() )
+ return static_cast<SwTabFrame*>(this)->FindMaster();
+ else
+ pThis = static_cast<SwTabFrame*>(this)->ContainsContent();
+ bIgnoreTab = true;
+ }
+
+ if ( pThis && pThis->IsContentFrame() )
+ {
+ SwContentFrame *pPrvCnt = static_cast<SwContentFrame*>(pThis)->GetPrevContentFrame();
+ if( !pPrvCnt )
+ return nullptr;
+ if ( !bIgnoreTab && pThis->IsInTab() )
+ {
+ SwLayoutFrame *pUp = pThis->GetUpper();
+ while (pUp && !pUp->IsCellFrame())
+ pUp = pUp->GetUpper();
+ assert(pUp && "Content flag says it's in table but it's not in cell.");
+ if (pUp && pUp->IsAnLower(pPrvCnt))
+ return pPrvCnt;
+ }
+ else
+ {
+ SwFrame* pRet;
+ const bool bBody = pThis->IsInDocBody();
+ const bool bFootnote = !bBody && pThis->IsInFootnote();
+ if ( bBody || bFootnote )
+ {
+ while ( pPrvCnt )
+ {
+ if ( (bBody && pPrvCnt->IsInDocBody()) ||
+ (bFootnote && pPrvCnt->IsInFootnote()) )
+ {
+ pRet = pPrvCnt->IsInTab() ? pPrvCnt->FindTabFrame()
+ : static_cast<SwFrame*>(pPrvCnt);
+ return pRet;
+ }
+ pPrvCnt = pPrvCnt->GetPrevContentFrame();
+ }
+ }
+ else if ( pThis->IsInFly() )
+ {
+ pRet = pPrvCnt->IsInTab() ? pPrvCnt->FindTabFrame()
+ : static_cast<SwFrame*>(pPrvCnt);
+ return pRet;
+ }
+ else // footer or header or Fly
+ {
+ const SwFrame *pUp = pThis->GetUpper();
+ const SwFrame *pCntUp = pPrvCnt->GetUpper();
+ while ( pUp && pUp->GetUpper() &&
+ !pUp->IsHeaderFrame() && !pUp->IsFooterFrame() )
+ pUp = pUp->GetUpper();
+ while ( pCntUp && pCntUp->GetUpper() )
+ pCntUp = pCntUp->GetUpper();
+ if ( pCntUp == pUp )
+ {
+ pRet = pPrvCnt->IsInTab() ? pPrvCnt->FindTabFrame()
+ : static_cast<SwFrame*>(pPrvCnt);
+ return pRet;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SwFrame::ImplInvalidateNextPos( bool bNoFootnote )
+{
+ SwFrame *pFrame = FindNext_();
+ if ( nullptr == pFrame )
+ return;
+
+ if( pFrame->IsSctFrame() )
+ {
+ while( pFrame && pFrame->IsSctFrame() )
+ {
+ if( static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePos();
+ else if( !bNoFootnote )
+ static_cast<SwSectionFrame*>(pFrame)->InvalidateFootnotePos();
+ if( !IsInSct() || FindSctFrame()->GetFollow() != pFrame )
+ pFrame->InvalidatePos();
+ return;
+ }
+ pFrame = pFrame->FindNext();
+ }
+ if( pFrame )
+ {
+ if ( pFrame->IsSctFrame())
+ {
+ // We need to invalidate the section's content so it gets
+ // the chance to flow to a different page.
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePos();
+ if( !IsInSct() || FindSctFrame()->GetFollow() != pFrame )
+ pFrame->InvalidatePos();
+ }
+ else
+ pFrame->InvalidatePos();
+ }
+ }
+ else
+ pFrame->InvalidatePos();
+}
+
+/** method to invalidate printing area of next frame
+
+ OD 09.01.2004 #i11859#
+
+ FME 2004-04-19 #i27145# Moved function from SwTextFrame to SwFrame
+*/
+void SwFrame::InvalidateNextPrtArea()
+{
+ // determine next frame
+ SwFrame* pNextFrame = FindNext();
+ // skip empty section frames and hidden text frames
+ {
+ while ( pNextFrame &&
+ ( ( pNextFrame->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pNextFrame)->GetSection() ) ||
+ ( pNextFrame->IsTextFrame() &&
+ static_cast<SwTextFrame*>(pNextFrame)->IsHiddenNow() ) ) )
+ {
+ pNextFrame = pNextFrame->FindNext();
+ }
+ }
+
+ // Invalidate printing area of found next frame
+ if ( !pNextFrame )
+ return;
+
+ if ( pNextFrame->IsSctFrame() )
+ {
+ // Invalidate printing area of found section frame, if
+ // (1) this text frame isn't in a section OR
+ // (2) found section frame isn't a follow of the section frame this
+ // text frame is in.
+ if ( !IsInSct() || FindSctFrame()->GetFollow() != pNextFrame )
+ {
+ pNextFrame->InvalidatePrt();
+ }
+
+ // Invalidate printing area of first content in found section.
+ SwFrame* pFstContentOfSctFrame =
+ static_cast<SwSectionFrame*>(pNextFrame)->ContainsAny();
+ if ( pFstContentOfSctFrame )
+ {
+ pFstContentOfSctFrame->InvalidatePrt();
+ }
+ }
+ else
+ {
+ pNextFrame->InvalidatePrt();
+ }
+}
+
+/// @returns true if the frame _directly_ sits in a section
+/// but not if it sits in a table which itself sits in a section.
+static bool lcl_IsInSectionDirectly( const SwFrame *pUp )
+{
+ bool bSeenColumn = false;
+
+ while( pUp )
+ {
+ if( pUp->IsColumnFrame() )
+ bSeenColumn = true;
+ else if( pUp->IsSctFrame() )
+ {
+ auto pSection = static_cast<const SwSectionFrame*>(pUp);
+ const SwFrame* pHeaderFooter = pSection->FindFooterOrHeader();
+ // When the section frame is not in header/footer:
+ // Allow move of frame in case our only column is not growable.
+ // Also allow if there is a previous section frame (to move back).
+ bool bAllowOutsideHeaderFooter = !pSection->Growable() || pSection->GetPrecede();
+ return bSeenColumn || (!pHeaderFooter && bAllowOutsideHeaderFooter);
+ }
+ else if( pUp->IsTabFrame() )
+ return false;
+ pUp = pUp->GetUpper();
+ }
+ return false;
+}
+
+/** determine, if frame is moveable in given environment
+
+ OD 08.08.2003 #110978#
+ method replaced 'old' method <sal_Bool IsMoveable() const>.
+ Determines, if frame is moveable in given environment. if no environment
+ is given (parameter _pLayoutFrame == 0), the movability in the actual
+ environment (<GetUpper()) is checked.
+*/
+bool SwFrame::IsMoveable( const SwLayoutFrame* _pLayoutFrame ) const
+{
+ bool bRetVal = false;
+
+ if ( !_pLayoutFrame )
+ {
+ _pLayoutFrame = GetUpper();
+ }
+
+ if ( _pLayoutFrame && IsFlowFrame() )
+ {
+ if ( _pLayoutFrame->IsInSct() && lcl_IsInSectionDirectly( _pLayoutFrame ) )
+ {
+ bRetVal = true;
+ }
+ else if ( _pLayoutFrame->IsInFly() ||
+ _pLayoutFrame->IsInDocBody() ||
+ _pLayoutFrame->IsInFootnote() )
+ {
+ // If IsMovable() is called before a MoveFwd() the method
+ // may return false if there is no NextCellLeaf. If
+ // IsMovable() is called before a MoveBwd() the method may
+ // return false if there is no PrevCellLeaf.
+ if ( _pLayoutFrame->IsInTab() && !IsTabFrame() &&
+ ( !IsContentFrame() || (!const_cast<SwFrame*>(this)->GetNextCellLeaf()
+ && !const_cast<SwFrame*>(this)->GetPrevCellLeaf()) )
+ )
+ {
+ bRetVal = false;
+ }
+ else
+ {
+ if ( _pLayoutFrame->IsInFly() )
+ {
+ // if fly frame has a follow (next linked fly frame),
+ // frame is moveable.
+ if ( const_cast<SwLayoutFrame*>(_pLayoutFrame)->FindFlyFrame()->GetNextLink() )
+ {
+ bRetVal = true;
+ }
+ else
+ {
+ // if environment is columned, frame is moveable, if
+ // it isn't in last column.
+ // search for column frame
+ const SwFrame* pCol = _pLayoutFrame;
+ while ( pCol && !pCol->IsColumnFrame() )
+ {
+ pCol = pCol->GetUpper();
+ }
+ // frame is moveable, if found column frame isn't last one.
+ if ( pCol && pCol->GetNext() )
+ {
+ bRetVal = true;
+ }
+ }
+ }
+ else if (!(_pLayoutFrame->IsInFootnote() && (IsTabFrame() || IsInTab())))
+ {
+ bRetVal = true;
+ }
+ }
+ }
+ }
+
+ return bRetVal;
+}
+
+void SwFrame::SetInfFlags()
+{
+ if ( !IsFlyFrame() && !GetUpper() ) //not yet pasted, no information available
+ return;
+
+ mbInfInvalid = mbInfBody = mbInfTab = mbInfFly = mbInfFootnote = mbInfSct = false;
+
+ SwFrame *pFrame = this;
+ if( IsFootnoteContFrame() )
+ mbInfFootnote = true;
+ do
+ {
+ // mbInfBody is only set in the page body, but not in the column body
+ if ( pFrame->IsBodyFrame() && !mbInfFootnote && pFrame->GetUpper()
+ && pFrame->GetUpper()->IsPageFrame() )
+ mbInfBody = true;
+ else if ( pFrame->IsTabFrame() || pFrame->IsCellFrame() )
+ {
+ mbInfTab = true;
+ }
+ else if ( pFrame->IsFlyFrame() )
+ mbInfFly = true;
+ else if ( pFrame->IsSctFrame() )
+ mbInfSct = true;
+ else if ( pFrame->IsFootnoteFrame() )
+ mbInfFootnote = true;
+
+ pFrame = pFrame->GetUpper();
+
+ } while ( pFrame && !pFrame->IsPageFrame() ); //there is nothing above the page
+}
+
+/** Updates the vertical or the righttoleft-flags.
+ *
+ * If the property is derived, it's from the upper or (for fly frames) from
+ * the anchor. Otherwise we've to call a virtual method to check the property.
+ */
+void SwFrame::SetDirFlags( bool bVert )
+{
+ if( bVert )
+ {
+ // OD 2004-01-21 #114969# - if derived, valid vertical flag only if
+ // vertical flag of upper/anchor is valid.
+ if( mbDerivedVert )
+ {
+ const SwFrame* pAsk = IsFlyFrame() ?
+ static_cast<SwFlyFrame*>(this)->GetAnchorFrame() : GetUpper();
+
+ OSL_ENSURE( pAsk != this, "Autsch! Stack overflow is about to happen" );
+
+ if( pAsk )
+ {
+ mbVertical = pAsk->IsVertical();
+ mbVertLR = pAsk->IsVertLR();
+ mbVertLRBT = pAsk->IsVertLRBT();
+
+ if ( !pAsk->mbInvalidVert )
+ mbInvalidVert = false;
+
+ if ( IsCellFrame() )
+ {
+ SwCellFrame* pPrv = static_cast<SwCellFrame*>(this)->GetPreviousCell();
+ if ( pPrv && !mbVertical && pPrv->IsVertical() )
+ {
+ mbVertical = pPrv->IsVertical();
+ mbVertLR = pPrv->IsVertLR();
+ mbVertLRBT = pPrv->IsVertLRBT();
+ }
+ }
+ }
+ }
+ else
+ CheckDirection( bVert );
+ }
+ else
+ {
+ bool bInv = false;
+ if( !mbDerivedR2L ) // CheckDirection is able to set bDerivedR2L!
+ CheckDirection( bVert );
+ if( mbDerivedR2L )
+ {
+ const SwFrame* pAsk = IsFlyFrame() ?
+ static_cast<SwFlyFrame*>(this)->GetAnchorFrame() : GetUpper();
+
+ OSL_ENSURE( pAsk != this, "Oops! Stack overflow is about to happen" );
+
+ if( pAsk )
+ mbRightToLeft = pAsk->IsRightToLeft();
+ if( !pAsk || pAsk->mbInvalidR2L )
+ bInv = mbInvalidR2L;
+ }
+ mbInvalidR2L = bInv;
+ }
+}
+
+SwLayoutFrame* SwFrame::GetNextCellLeaf()
+{
+ SwFrame* pTmpFrame = this;
+ while (pTmpFrame && !pTmpFrame->IsCellFrame())
+ pTmpFrame = pTmpFrame->GetUpper();
+
+ SAL_WARN_IF(!pTmpFrame, "sw.core", "SwFrame::GetNextCellLeaf() without cell");
+ return pTmpFrame ? static_cast<SwCellFrame*>(pTmpFrame)->GetFollowCell() : nullptr;
+}
+
+SwLayoutFrame* SwFrame::GetPrevCellLeaf()
+{
+ SwFrame* pTmpFrame = this;
+ while (pTmpFrame && !pTmpFrame->IsCellFrame())
+ pTmpFrame = pTmpFrame->GetUpper();
+
+ SAL_WARN_IF(!pTmpFrame, "sw.core", "SwFrame::GetNextPreviousLeaf() without cell");
+ return pTmpFrame ? static_cast<SwCellFrame*>(pTmpFrame)->GetPreviousCell() : nullptr;
+}
+
+static SwCellFrame* lcl_FindCorrespondingCellFrame( const SwRowFrame& rOrigRow,
+ const SwCellFrame& rOrigCell,
+ const SwRowFrame& rCorrRow,
+ bool bInFollow )
+{
+ SwCellFrame* pRet = nullptr;
+ const SwCellFrame* pCell = static_cast<const SwCellFrame*>(rOrigRow.Lower());
+ SwCellFrame* pCorrCell = const_cast<SwCellFrame*>(static_cast<const SwCellFrame*>(rCorrRow.Lower()));
+
+ while ( pCell != &rOrigCell && !pCell->IsAnLower( &rOrigCell ) )
+ {
+ pCell = static_cast<const SwCellFrame*>(pCell->GetNext());
+ pCorrCell = static_cast<SwCellFrame*>(pCorrCell->GetNext());
+ }
+
+ assert(pCell && pCorrCell && "lcl_FindCorrespondingCellFrame does not work");
+
+ if ( pCell != &rOrigCell )
+ {
+ // rOrigCell must be a lower of pCell. We need to recurse into the rows:
+ assert(pCell->Lower() && pCell->Lower()->IsRowFrame() &&
+ "lcl_FindCorrespondingCellFrame does not work");
+
+ const SwRowFrame* pRow = static_cast<const SwRowFrame*>(pCell->Lower());
+ while ( !pRow->IsAnLower( &rOrigCell ) )
+ pRow = static_cast<const SwRowFrame*>(pRow->GetNext());
+
+ SwRowFrame* pCorrRow = nullptr;
+ if ( bInFollow )
+ pCorrRow = pRow->GetFollowRow();
+ else
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pCorrCell->GetLastLower());
+
+ if ( pTmpRow && pTmpRow->GetFollowRow() == pRow )
+ pCorrRow = pTmpRow;
+ }
+
+ if ( pCorrRow )
+ pRet = lcl_FindCorrespondingCellFrame( *pRow, rOrigCell, *pCorrRow, bInFollow );
+ }
+ else
+ pRet = pCorrCell;
+
+ return pRet;
+}
+
+// VERSION OF GetFollowCell() that assumes that we always have a follow flow line:
+SwCellFrame* SwCellFrame::GetFollowCell() const
+{
+ SwCellFrame* pRet = nullptr;
+
+ // NEW TABLES
+ // Covered cells do not have follow cells!
+ const tools::Long nRowSpan = GetLayoutRowSpan();
+ if ( nRowSpan < 1 )
+ return nullptr;
+
+ // find most upper row frame
+ const SwFrame* pRow = GetUpper();
+
+ while (pRow && (!pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame()))
+ pRow = pRow->GetUpper();
+
+ if (!pRow)
+ return nullptr;
+
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
+ if (!pTabFrame || !pTabFrame->GetFollow() || !pTabFrame->HasFollowFlowLine())
+ return nullptr;
+
+ const SwCellFrame* pThisCell = this;
+
+ // Get last cell of the current table frame that belongs to the rowspan:
+ if ( nRowSpan > 1 )
+ {
+ // optimization: Will end of row span be in last row or exceed row?
+ tools::Long nMax = 0;
+ while ( pRow->GetNext() && ++nMax < nRowSpan )
+ pRow = pRow->GetNext();
+
+ if ( !pRow->GetNext() )
+ {
+ pThisCell = &pThisCell->FindStartEndOfRowSpanCell( false );
+ pRow = pThisCell->GetUpper();
+ }
+ }
+
+ const SwRowFrame* pFollowRow = nullptr;
+ if ( !pRow->GetNext() &&
+ nullptr != ( pFollowRow = pRow->IsInSplitTableRow() ) &&
+ ( !pFollowRow->IsRowSpanLine() || nRowSpan > 1 ) )
+ pRet = lcl_FindCorrespondingCellFrame( *static_cast<const SwRowFrame*>(pRow), *pThisCell, *pFollowRow, true );
+
+ return pRet;
+}
+
+// VERSION OF GetPreviousCell() THAT ASSUMES THAT WE ALWAYS HAVE A FFL
+SwCellFrame* SwCellFrame::GetPreviousCell() const
+{
+ SwCellFrame* pRet = nullptr;
+
+ // NEW TABLES
+ // Covered cells do not have previous cells!
+ if ( GetLayoutRowSpan() < 1 )
+ return nullptr;
+
+ // find most upper row frame
+ const SwFrame* pRow = GetUpper();
+ while( !pRow->IsRowFrame() || (pRow->GetUpper() && !pRow->GetUpper()->IsTabFrame()) )
+ pRow = pRow->GetUpper();
+
+ OSL_ENSURE( pRow->GetUpper() && pRow->GetUpper()->IsTabFrame(), "GetPreviousCell without Table" );
+
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ if ( pTab && pTab->IsFollow() )
+ {
+ const SwFrame* pTmp = pTab->GetFirstNonHeadlineRow();
+ const bool bIsInFirstLine = ( pTmp == pRow );
+
+ if ( bIsInFirstLine )
+ {
+ SwTabFrame *pMaster = pTab->FindMaster();
+ if ( pMaster && pMaster->HasFollowFlowLine() )
+ {
+ SwRowFrame* pMasterRow = static_cast<SwRowFrame*>(pMaster->GetLastLower());
+ if ( pMasterRow )
+ pRet = lcl_FindCorrespondingCellFrame( *static_cast<const SwRowFrame*>(pRow), *this, *pMasterRow, false );
+ if ( pRet && pRet->GetTabBox()->getRowSpan() < 1 )
+ pRet = &const_cast<SwCellFrame&>(pRet->FindStartEndOfRowSpanCell( true ));
+ }
+ }
+ }
+
+ return pRet;
+}
+
+// --> NEW TABLES
+const SwCellFrame& SwCellFrame::FindStartEndOfRowSpanCell( bool bStart ) const
+{
+ const SwTabFrame* pTableFrame = dynamic_cast<const SwTabFrame*>(GetUpper()->GetUpper());
+
+ if ( !bStart && pTableFrame && pTableFrame->IsFollow() && pTableFrame->IsInHeadline( *this ) )
+ return *this;
+
+ OSL_ENSURE( pTableFrame &&
+ ( (bStart && GetTabBox()->getRowSpan() < 1) ||
+ (!bStart && GetLayoutRowSpan() > 1) ),
+ "SwCellFrame::FindStartRowSpanCell: No rowspan, no table, no cookies" );
+
+ if ( pTableFrame )
+ {
+ const SwTable* pTable = pTableFrame->GetTable();
+
+ sal_uInt16 nMax = USHRT_MAX;
+ const SwFrame* pCurrentRow = GetUpper();
+ const bool bDoNotEnterHeadline = bStart && pTableFrame->IsFollow() &&
+ !pTableFrame->IsInHeadline( *pCurrentRow );
+
+ // check how many rows we are allowed to go up or down until we reach the end of
+ // the current table frame:
+ nMax = 0;
+ while ( bStart ? pCurrentRow->GetPrev() : pCurrentRow->GetNext() )
+ {
+ if ( bStart )
+ {
+ // do not enter a repeated headline:
+ if ( bDoNotEnterHeadline && pTableFrame->IsFollow() &&
+ pTableFrame->IsInHeadline( *pCurrentRow->GetPrev() ) )
+ break;
+
+ pCurrentRow = pCurrentRow->GetPrev();
+ }
+ else
+ pCurrentRow = pCurrentRow->GetNext();
+
+ ++nMax;
+ }
+
+ // By passing the nMax value for Find*OfRowSpan (in case of bCurrentTableOnly
+ // is set) we assure that we find a rMasterBox that has a SwCellFrame in
+ // the current table frame:
+ const SwTableBox& rMasterBox = bStart ?
+ GetTabBox()->FindStartOfRowSpan( *pTable, nMax ) :
+ GetTabBox()->FindEndOfRowSpan( *pTable, nMax );
+
+ SwIterator<SwCellFrame,SwFormat> aIter( *rMasterBox.GetFrameFormat() );
+
+ for ( SwCellFrame* pMasterCell = aIter.First(); pMasterCell; pMasterCell = aIter.Next() )
+ {
+ if ( pMasterCell->GetTabBox() == &rMasterBox )
+ {
+ const SwTabFrame* pMasterTable = static_cast<const SwTabFrame*>(pMasterCell->GetUpper()->GetUpper());
+
+ if ( pMasterTable == pTableFrame )
+ {
+ return *pMasterCell;
+ }
+ }
+ }
+ }
+
+ SAL_WARN("sw.core", "SwCellFrame::FindStartRowSpanCell: No result");
+
+ return *this;
+}
+
+// <-- NEW TABLES
+
+const SwRowFrame* SwFrame::IsInSplitTableRow() const
+{
+ OSL_ENSURE( IsInTab(), "IsInSplitTableRow should only be called for frames in tables" );
+
+ const SwFrame* pRow = this;
+
+ // find most upper row frame
+ while( pRow && ( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() ) )
+ pRow = pRow->GetUpper();
+
+ if ( !pRow ) return nullptr;
+
+ OSL_ENSURE( pRow->GetUpper()->IsTabFrame(), "Confusion in table layout" );
+
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ // If most upper row frame is a headline row, the current frame
+ // can't be in a split table row. Thus, add corresponding condition.
+ if ( pRow->GetNext() ||
+ pTab->GetTable()->IsHeadline(
+ *(static_cast<const SwRowFrame*>(pRow)->GetTabLine()) ) ||
+ !pTab->HasFollowFlowLine() ||
+ !pTab->GetFollow() )
+ return nullptr;
+
+ // skip headline
+ const SwRowFrame* pFollowRow = pTab->GetFollow()->GetFirstNonHeadlineRow();
+
+ OSL_ENSURE( pFollowRow, "SwFrame::IsInSplitTableRow() does not work" );
+
+ return pFollowRow;
+}
+
+const SwRowFrame* SwFrame::IsInFollowFlowRow() const
+{
+ OSL_ENSURE( IsInTab(), "IsInSplitTableRow should only be called for frames in tables" );
+
+ // find most upper row frame
+ const SwFrame* pRow = this;
+ while( pRow && ( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() ) )
+ pRow = pRow->GetUpper();
+
+ if ( !pRow ) return nullptr;
+
+ OSL_ENSURE( pRow->GetUpper()->IsTabFrame(), "Confusion in table layout" );
+
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ const SwTabFrame* pMaster = pTab->IsFollow() ? pTab->FindMaster() : nullptr;
+
+ if ( !pMaster || !pMaster->HasFollowFlowLine() )
+ return nullptr;
+
+ const SwFrame* pTmp = pTab->GetFirstNonHeadlineRow();
+ const bool bIsInFirstLine = ( pTmp == pRow );
+
+ if ( !bIsInFirstLine )
+ return nullptr;
+
+ const SwRowFrame* pMasterRow = static_cast<const SwRowFrame*>(pMaster->GetLastLower());
+ return pMasterRow;
+}
+
+bool SwFrame::IsInBalancedSection() const
+{
+ bool bRet = false;
+
+ if ( IsInSct() )
+ {
+ const SwSectionFrame* pSectionFrame = FindSctFrame();
+ if ( pSectionFrame )
+ bRet = pSectionFrame->IsBalancedSection();
+ }
+ return bRet;
+}
+
+const SwFrame* SwLayoutFrame::GetLastLower() const
+{
+ const SwFrame* pRet = Lower();
+ if ( !pRet )
+ return nullptr;
+ while ( pRet->GetNext() )
+ pRet = pRet->GetNext();
+ return pRet;
+}
+
+SwTextFrame* SwFrame::DynCastTextFrame()
+{
+ return IsTextFrame() ? static_cast<SwTextFrame*>(this) : nullptr;
+}
+
+const SwTextFrame* SwFrame::DynCastTextFrame() const
+{
+ return IsTextFrame() ? static_cast<const SwTextFrame*>(this) : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flowfrm.cxx b/sw/source/core/layout/flowfrm.cxx
new file mode 100644
index 000000000..78c3a34ca
--- /dev/null
+++ b/sw/source/core/layout/flowfrm.cxx
@@ -0,0 +1,2736 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/svdobj.hxx>
+
+#include <anchoredobject.hxx>
+#include <bodyfrm.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtpdsc.hxx>
+#include <editeng/ulspitem.hxx>
+#include <tgrditem.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <editeng/pgrditem.hxx>
+#include <paratr.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <pagedesc.hxx>
+#include <layact.hxx>
+#include <flyfrm.hxx>
+#include <sectfrm.hxx>
+#include <section.hxx>
+#include <dbg_lay.hxx>
+#include <lineinfo.hxx>
+#include <fmtclbl.hxx>
+#include <sortedobjs.hxx>
+#include <layouter.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <calbck.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+
+bool SwFlowFrame::s_bMoveBwdJump = false;
+
+SwFlowFrame::SwFlowFrame( SwFrame &rFrame ) :
+ m_rThis( rFrame ),
+ m_pFollow( nullptr ),
+ m_pPrecede( nullptr ),
+ m_bLockJoin( false ),
+ m_bUndersized( false ),
+ m_bFlyLock( false )
+{}
+
+SwFlowFrame::~SwFlowFrame()
+{
+ if (m_pFollow)
+ {
+ m_pFollow->m_pPrecede = nullptr;
+ }
+ if (m_pPrecede)
+ {
+ m_pPrecede->m_pFollow = nullptr;
+ }
+}
+
+void SwFlowFrame::SetFollow(SwFlowFrame *const pFollow)
+{
+ if (m_pFollow)
+ {
+ assert(this == m_pFollow->m_pPrecede);
+ m_pFollow->m_pPrecede = nullptr;
+ }
+ m_pFollow = pFollow;
+ if (m_pFollow != nullptr)
+ {
+ if (m_pFollow->m_pPrecede) // re-chaining pFollow?
+ {
+ assert(m_pFollow == m_pFollow->m_pPrecede->m_pFollow);
+ m_pFollow->m_pPrecede->m_pFollow = nullptr;
+ }
+ m_pFollow->m_pPrecede = this;
+ }
+}
+
+/// @return true if any follow has the JoinLocked flag
+bool SwFlowFrame::HasLockedFollow() const
+{
+ const SwFlowFrame* pFrame = GetFollow();
+ while( pFrame )
+ {
+ if( pFrame->IsJoinLocked() )
+ return true;
+ pFrame = pFrame->GetFollow();
+ }
+ return false;
+}
+
+bool SwFlowFrame::IsKeepFwdMoveAllowed( bool bIgnoreMyOwnKeepValue )
+{
+ // If all the predecessors up to the first of the chain have
+ // the 'keep' attribute set, and the first of the chain's
+ // IsFwdMoveAllowed returns false, then we're not allowed to move.
+ SwFrame *pFrame = &m_rThis;
+ if ( !pFrame->IsInFootnote() ) {
+ if ( bIgnoreMyOwnKeepValue && pFrame->GetIndPrev() )
+ pFrame = pFrame->GetIndPrev();
+ do
+ { if ( pFrame->GetAttrSet()->GetKeep().GetValue() )
+ pFrame = pFrame->GetIndPrev();
+ else
+ return true;
+ } while ( pFrame );
+ }
+ //See IsFwdMoveAllowed()
+ bool bRet = false;
+ if ( pFrame && pFrame->GetIndPrev() )
+ bRet = true;
+ return bRet;
+}
+
+void SwFlowFrame::CheckKeep()
+{
+ // Kick off the "last" predecessor with a 'keep' attribute, because
+ // it's possible for the whole troop to move back.
+ SwFrame *pPre = m_rThis.GetIndPrev();
+ assert(pPre);
+ if( pPre->IsSctFrame() )
+ {
+ SwFrame *pLast = static_cast<SwSectionFrame*>(pPre)->FindLastContent();
+ if( pLast && pLast->FindSctFrame() == pPre )
+ pPre = pLast;
+ else
+ return;
+ }
+ SwFrame* pTmp;
+ bool bKeep;
+ while ( (bKeep = pPre->GetAttrSet()->GetKeep().GetValue()) &&
+ nullptr != ( pTmp = pPre->GetIndPrev() ) )
+ {
+ if( pTmp->IsSctFrame() )
+ {
+ SwFrame *pLast = static_cast<SwSectionFrame*>(pTmp)->FindLastContent();
+ if( pLast && pLast->FindSctFrame() == pTmp )
+ pTmp = pLast;
+ else
+ break;
+ }
+ pPre = pTmp;
+ }
+ if ( bKeep )
+ pPre->InvalidatePos();
+}
+
+namespace
+{
+/**
+ * Determines if the next content frame after rThis will require the full area of the parent body
+ * frame.
+ */
+bool IsNextContentFullPage(const SwFrame& rThis)
+{
+ const SwFrame* pNext = rThis.FindNextCnt();
+ if (!pNext)
+ {
+ return false;
+ }
+
+ const SwSortedObjs* pNextDrawObjs = pNext->GetDrawObjs();
+ if (!pNextDrawObjs || !pNextDrawObjs->size())
+ {
+ return false;
+ }
+
+ for (const auto& pDrawObj : *pNextDrawObjs)
+ {
+ if (!pDrawObj)
+ {
+ continue;
+ }
+
+ SwTwips nDrawObjHeight = pDrawObj->GetObjRectWithSpaces().Height();
+ const SwPageFrame* pPageFrame = pDrawObj->GetPageFrame();
+ if (!pPageFrame)
+ {
+ continue;
+ }
+
+ SwTwips nBodyHeight = pPageFrame->GetLower()->getFrameArea().Height();
+ if (nDrawObjHeight < nBodyHeight)
+ {
+ continue;
+ }
+
+ const SwFormatSurround& rSurround = pDrawObj->GetFrameFormat().GetSurround();
+ if (rSurround.GetSurround() != text::WrapTextMode_NONE)
+ {
+ continue;
+ }
+
+ // At this point the height of the draw object will use all the vertical available space,
+ // and also no wrapping will be performed, so all horizontal space will be taken as well.
+ return true;
+ }
+
+ return false;
+}
+}
+
+bool SwFlowFrame::IsKeep(SvxFormatKeepItem const& rKeep,
+ SvxFormatBreakItem const& rBreak,
+ bool const bCheckIfLastRowShouldKeep) const
+{
+ // 1. The keep attribute is ignored inside footnotes
+ // 2. For compatibility reasons, the keep attribute is
+ // ignored for frames inside table cells
+ // 3. If bBreakCheck is set to true, this function only checks
+ // if there are any break after attributes set at rAttrs
+ // or break before attributes set for the next content (or next table)
+ // 4. Keep is ignored if the next frame will require its own page.
+ bool bKeep = bCheckIfLastRowShouldKeep ||
+ ( !m_rThis.IsInFootnote() &&
+ ( !m_rThis.IsInTab() || m_rThis.IsTabFrame() ) &&
+ rKeep.GetValue() && !IsNextContentFullPage(m_rThis));
+
+ OSL_ENSURE( !bCheckIfLastRowShouldKeep || m_rThis.IsTabFrame(),
+ "IsKeep with bCheckIfLastRowShouldKeep should only be used for tabfrms" );
+
+ // Ignore keep attribute if there are break situations:
+ if ( bKeep )
+ {
+ switch (rBreak.GetBreak())
+ {
+ case SvxBreak::ColumnAfter:
+ case SvxBreak::ColumnBoth:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ {
+ bKeep = false;
+ break;
+ }
+ default: break;
+ }
+ if ( bKeep )
+ {
+ SwFrame *pNxt;
+ if( nullptr != (pNxt = m_rThis.FindNextCnt()) &&
+ (!m_pFollow || pNxt != &m_pFollow->GetFrame()))
+ {
+ // The last row of a table only keeps with the next content
+ // it they are in the same section:
+ if ( bCheckIfLastRowShouldKeep )
+ {
+ const SwSection* pThisSection = nullptr;
+ const SwSection* pNextSection = nullptr;
+ const SwSectionFrame* pThisSectionFrame = m_rThis.FindSctFrame();
+ const SwSectionFrame* pNextSectionFrame = pNxt->FindSctFrame();
+
+ if ( pThisSectionFrame )
+ pThisSection = pThisSectionFrame->GetSection();
+
+ if ( pNextSectionFrame )
+ pNextSection = pNextSectionFrame->GetSection();
+
+ if ( pThisSection != pNextSection )
+ bKeep = false;
+ }
+
+ if ( bKeep )
+ {
+ SvxFormatBreakItem const* pBreak;
+ SwFormatPageDesc const* pPageDesc;
+ SwTabFrame* pTab = pNxt->IsInTab() ? pNxt->FindTabFrame() : nullptr;
+ if (pTab && (!m_rThis.IsInTab() || m_rThis.FindTabFrame() != pTab))
+ {
+ const SwAttrSet *const pSet = &pTab->GetFormat()->GetAttrSet();
+ pBreak = &pSet->GetBreak();
+ pPageDesc = &pSet->GetPageDesc();
+ }
+ else
+ {
+ pBreak = &pNxt->GetBreakItem();
+ pPageDesc = &pNxt->GetPageDescItem();
+ }
+
+ if (pPageDesc->GetPageDesc())
+ bKeep = false;
+ else switch (pBreak->GetBreak())
+ {
+ case SvxBreak::ColumnBefore:
+ case SvxBreak::ColumnBoth:
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageBoth:
+ bKeep = false;
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+ }
+ return bKeep;
+}
+
+sal_uInt8 SwFlowFrame::BwdMoveNecessary( const SwPageFrame *pPage, const SwRect &rRect )
+{
+ // The return value helps deciding whether we need to flow back (3),
+ // or whether we can use the good old WouldFit (0, 1), or if
+ // it's reasonable to relocate and test-format (2).
+
+ // Bit 1 in this case means that there are objects anchored to myself,
+ // bit 2 means that I have to evade other objects.
+
+ // If a SurroundObj that desires to be wrapped around overlaps with the
+ // Rect, it's required to flow (because we can't guess the relationships).
+ // However it's possible for a test formatting to happen.
+ // If the SurroundObj is a Fly and I'm a Lower, or the Fly is a Lower of
+ // mine, then it doesn't matter.
+ // If the SurroundObj is anchored in a character bound Fly, and I'm not
+ // a Lower of that character bound Fly myself, then the Fly doesn't matter.
+
+ // If the object is anchored with me, i can ignore it, because
+ // it's likely that it will follow me with the flow. A test formatting is
+ // not allowed in that case, however!
+ sal_uInt8 nRet = 0;
+ SwFlowFrame *pTmp = this;
+ do
+ { // If there are objects hanging either on me or on a follow, we can't
+ // do a test formatting, because paragraph bound objects wouldn't
+ // be properly considered, and character bound objects shouldn't
+ // be test formatted at all.
+ if( pTmp->GetFrame().GetDrawObjs() )
+ nRet = 1;
+ pTmp = pTmp->GetFollow();
+ } while ( !nRet && pTmp );
+ const SwSortedObjs *pObjs = pPage ? pPage->GetSortedObjs() : nullptr;
+ if (pObjs)
+ {
+
+ const SwSortedObjs &rObjs = *pObjs;
+ SwNodeOffset nIndex = NODE_OFFSET_MAX;
+ for ( size_t i = 0; nRet < 3 && i < rObjs.size(); ++i )
+ {
+
+ SwAnchoredObject* pObj = rObjs[i];
+ const SwFrameFormat& rFormat = pObj->GetFrameFormat();
+ const SwRect aRect( pObj->GetObjRect() );
+ if ( aRect.Overlaps( rRect ) &&
+ rFormat.GetSurround().GetSurround() != css::text::WrapTextMode_THROUGH )
+ {
+ if( m_rThis.IsLayoutFrame() && //Fly Lower of This?
+ Is_Lower_Of( &m_rThis, pObj->GetDrawObj() ) )
+ continue;
+ if( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsAnLower( &m_rThis ) )//This Lower of Fly?
+ continue;
+ }
+
+ const SwFrame* pAnchor = pObj->GetAnchorFrame();
+ if ( pAnchor == &m_rThis )
+ {
+ nRet |= 1;
+ continue;
+ }
+
+ // Don't do this if the object is anchored behind me in the text
+ // flow, because then I wouldn't evade it.
+ if ( ::IsFrameInSameContext( pAnchor, &m_rThis ) )
+ {
+ if ( rFormat.GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA )
+ {
+ // The index of the other one can be retrieved using the anchor attribute.
+ SwNodeOffset nTmpIndex = rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex();
+ // Now we're going to check whether the current paragraph before
+ // the anchor of the displacing object sits in the text. If this
+ // is the case, we don't try to evade it.
+ // The index is being determined via SwFormatAnchor, because it's
+ // getting quite expensive otherwise.
+ if( NODE_OFFSET_MAX == nIndex )
+ {
+ const SwNode *pNode;
+ if (m_rThis.IsTextFrame())
+ pNode = static_cast<SwTextFrame&>(m_rThis).GetTextNodeFirst();
+ else if (m_rThis.IsNoTextFrame())
+ pNode = static_cast<SwNoTextFrame&>(m_rThis).GetNode();
+ else if( m_rThis.IsSctFrame() )
+ pNode = static_cast<SwSectionFormat*>(static_cast<SwSectionFrame&>(m_rThis).
+ GetFormat())->GetSectionNode();
+ else
+ {
+ assert(!m_rThis.IsContentFrame());
+ OSL_ENSURE( m_rThis.IsTabFrame(), "new FowFrame?" );
+ pNode = static_cast<SwTabFrame&>(m_rThis).GetTable()->
+ GetTabSortBoxes()[0]->GetSttNd()->FindTableNode();
+ }
+ nIndex = pNode->GetIndex();
+ }
+ if (nIndex < nTmpIndex &&
+ (!m_rThis.IsTextFrame() ||
+ !FrameContainsNode(static_cast<SwTextFrame&>(m_rThis), nTmpIndex)))
+ {
+ continue;
+ }
+ }
+ }
+ else
+ continue;
+
+ nRet |= 2;
+ }
+ }
+ }
+ return nRet;
+}
+
+/// A specialized form of Cut(), which relocates a whole chain (this and the following,
+/// in particular). During this process, only the minimum operations and notifications are done.
+SwLayoutFrame *SwFlowFrame::CutTree( SwFrame *pStart )
+{
+ // Cut the Start and all the neighbours; they are chained together and
+ // a handle to the first one is returned. Residuals are invalidated
+ // as appropriate.
+
+ SwLayoutFrame *pLay = pStart->GetUpper();
+ if ( pLay->IsInFootnote() )
+ pLay = pLay->FindFootnoteFrame();
+
+ // i#58846
+ // <pPrepare( PrepareHint::QuoVadis )> only for frames in footnotes
+ if( pStart->IsInFootnote() )
+ {
+ SwFrame* pTmp = pStart->GetIndPrev();
+ if( pTmp )
+ pTmp->Prepare( PrepareHint::QuoVadis );
+ }
+
+ // Just cut quickly and take care that we don't cause problems with the
+ // left-behinds. The pointers of the chain being cut can point who-knows where.
+ if ( pStart == pStart->GetUpper()->Lower() )
+ pStart->GetUpper()->m_pLower = nullptr;
+ if ( pStart->GetPrev() )
+ {
+ pStart->GetPrev()->mpNext = nullptr;
+ pStart->mpPrev = nullptr;
+ }
+
+ if ( pLay->IsFootnoteFrame() )
+ {
+ if ( !pLay->Lower() && !pLay->IsColLocked() &&
+ !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked() )
+ {
+ // tdf#101821 don't delete it while iterating over it
+ if (!pLay->IsDeleteForbidden())
+ {
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+ // else: assume there is code on the stack to clean up empty
+ // footnote frames
+ // (don't go into the else branch below, it produces a disconnected
+ // footnote with null upper that can be returned by
+ // SwFootnoteBossFrame::FindFootnote() causing null pointer deref
+ // in SwTextFrame::ConnectFootnote()
+ }
+ else
+ {
+ bool bUnlock = !static_cast<SwFootnoteFrame*>(pLay)->IsBackMoveLocked();
+ static_cast<SwFootnoteFrame*>(pLay)->LockBackMove();
+ pLay->InvalidateSize();
+ pLay->Calc(pLay->getRootFrame()->GetCurrShell()->GetOut());
+ SwContentFrame *pCnt = pLay->ContainsContent();
+ while ( pCnt && pLay->IsAnLower( pCnt ) )
+ {
+ // It's possible for the ContentFrame to be locked, and we don't want
+ // to end up in an endless page migration, so we're not even
+ // going to call Calc!
+ OSL_ENSURE( pCnt->IsTextFrame(), "The Graphic has landed." );
+ if ( static_cast<SwTextFrame*>(pCnt)->IsLocked() ||
+ static_cast<SwTextFrame*>(pCnt)->GetFollow() == pStart )
+ break;
+ pCnt->Calc(pCnt->getRootFrame()->GetCurrShell()->GetOut());
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ if( bUnlock )
+ static_cast<SwFootnoteFrame*>(pLay)->UnlockBackMove();
+ }
+ pLay = nullptr;
+ }
+ return pLay;
+}
+
+/// A specialized form of Paste(), which relocates a whole chain (this and the following,
+/// in particular). During this process, only the minimum operations and notifications are done.
+bool SwFlowFrame::PasteTree( SwFrame *pStart, SwLayoutFrame *pParent, SwFrame *pSibling,
+ SwFrame *pOldParent )
+{
+ // returns true if there's a LayoutFrame in the chain.
+ bool bRet = false;
+
+ // The chain beginning with pStart is inserted before pSibling
+ // under the parent. We take care to invalidate as required.
+
+ // I'm receiving a finished chain. We need to update the pointers for
+ // the beginning of the chain, then all the uppers and finally the end.
+ // On the way there, we invalidate as required.
+ if ( pSibling )
+ {
+ pStart->mpPrev = pSibling->GetPrev();
+ if ( nullptr != pStart->mpPrev )
+ pStart->GetPrev()->mpNext = pStart;
+ else
+ pParent->m_pLower = pStart;
+ pSibling->InvalidatePos_();
+ pSibling->InvalidatePrt_();
+ }
+ else
+ {
+ pStart->mpPrev = pParent->Lower();
+ if ( nullptr == pStart->mpPrev )
+ pParent->m_pLower = pStart;
+ else
+ //i#100782
+ //If the pParent has more than 1 child nodes, former design will
+ //ignore them directly without any collection work. It will make some
+ //dangling pointers. This lead the crash...
+ //The new design will find the last child of pParent in loop way, and
+ //add the pStart after the last child.
+ // pParent->Lower()->pNext = pStart;
+ {
+ SwFrame* pTemp = pParent->m_pLower;
+ while (pTemp)
+ {
+ if (pTemp->mpNext)
+ pTemp = pTemp->mpNext;
+ else
+ {
+ pStart->mpPrev = pTemp;
+ pTemp->mpNext = pStart;
+ break;
+ }
+ }
+ }
+
+
+ // i#27145
+ if ( pParent->IsSctFrame() )
+ {
+ // We have no sibling because pParent is a section frame and
+ // has just been created to contain some content. The printing
+ // area of the frame behind pParent has to be invalidated, so
+ // that the correct distance between pParent and the next frame
+ // can be calculated.
+ pParent->InvalidateNextPrtArea();
+ }
+ }
+ SwFrame *pFloat = pStart;
+ SwFrame *pLst = nullptr;
+ SwRectFnSet aRectFnSet(pParent);
+ SwTwips nGrowVal = 0;
+ do
+ { pFloat->mpUpper = pParent;
+ pFloat->InvalidateAll_();
+ pFloat->CheckDirChange();
+
+ // I'm a friend of the TextFrame and thus am allowed to do many things.
+ // The CacheIdx idea seems to be a bit risky!
+ if ( pFloat->IsTextFrame() )
+ {
+ if ( static_cast<SwTextFrame*>(pFloat)->GetCacheIdx() != USHRT_MAX )
+ static_cast<SwTextFrame*>(pFloat)->Init(); // I'm his friend.
+ }
+ else
+ bRet = true;
+
+ nGrowVal += aRectFnSet.GetHeight(pFloat->getFrameArea());
+ if ( pFloat->GetNext() )
+ pFloat = pFloat->GetNext();
+ else
+ {
+ pLst = pFloat;
+ pFloat = nullptr;
+ }
+ } while ( pFloat );
+
+ if ( pSibling )
+ {
+ pLst->mpNext = pSibling;
+ pSibling->mpPrev = pLst;
+ if( pSibling->IsInFootnote() )
+ {
+ if( pSibling->IsSctFrame() )
+ pSibling = static_cast<SwSectionFrame*>(pSibling)->ContainsAny();
+ if( pSibling )
+ pSibling->Prepare( PrepareHint::ErgoSum );
+ }
+ }
+ if ( nGrowVal )
+ {
+ if ( pOldParent && pOldParent->IsBodyFrame() ) // For variable page height while browsing
+ pOldParent->Shrink( nGrowVal );
+ pParent->Grow( nGrowVal );
+ }
+
+ if ( pParent->IsFootnoteFrame() )
+ static_cast<SwFootnoteFrame*>(pParent)->InvalidateNxtFootnoteCnts( pParent->FindPageFrame() );
+ return bRet;
+}
+
+void SwFlowFrame::MoveSubTree( SwLayoutFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "No parent given." );
+ OSL_ENSURE( m_rThis.GetUpper(), "Where are we coming from?" );
+
+ // Be economical with notifications if an action is running.
+ SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ const SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ const bool bComplete = pImp && pImp->IsAction() && pImp->GetLayAction().IsComplete();
+
+ if ( !bComplete )
+ {
+ SwFrame *pPre = m_rThis.GetIndPrev();
+ if ( pPre )
+ {
+ pPre->SetRetouche();
+ // follow-up of i#26250
+ // invalidate printing area of previous frame, if it's in a table
+ if ( pPre->GetUpper()->IsInTab() )
+ {
+ pPre->InvalidatePrt_();
+ }
+ pPre->InvalidatePage();
+ }
+ else
+ {
+ m_rThis.GetUpper()->SetCompletePaint();
+ m_rThis.GetUpper()->InvalidatePage();
+ }
+ }
+
+ SwPageFrame *pOldPage = m_rThis.FindPageFrame();
+
+ SwLayoutFrame *pOldParent;
+ bool bInvaLay;
+
+ {
+ //JoinLock pParent for the lifetime of the Cut/Paste call to avoid
+ //SwSectionFrame::MergeNext removing the pParent we're trying to reparent
+ //into
+ FlowFrameJoinLockGuard aJoinGuard(pParent);
+ SwFrameDeleteGuard aDeleteGuard(pParent);
+ pOldParent = CutTree( &m_rThis );
+ bInvaLay = PasteTree( &m_rThis, pParent, pSibling, pOldParent );
+ }
+
+ // If, by cutting & pasting, an empty SectionFrame came into existence, it should
+ // disappear automatically.
+ SwSectionFrame *pSct;
+
+ if ( pOldParent && !pOldParent->Lower() &&
+ ( pOldParent->IsInSct() &&
+ !(pSct = pOldParent->FindSctFrame())->ContainsContent() &&
+ !pSct->ContainsAny( true ) ) )
+ {
+ pSct->DelEmpty( false );
+ }
+
+ // If we're in a column section, we'd rather not call Calc "from below"
+ if( !m_rThis.IsInSct() &&
+ ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) )
+ m_rThis.GetUpper()->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ else if( m_rThis.GetUpper()->IsSctFrame() )
+ {
+ SwSectionFrame* pTmpSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
+ bool bOld = pTmpSct->IsContentLocked();
+ pTmpSct->SetContentLock( true );
+ pTmpSct->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ if( !bOld )
+ pTmpSct->SetContentLock( false );
+ }
+ SwPageFrame *pPage = m_rThis.FindPageFrame();
+
+ if ( pOldPage != pPage )
+ {
+ m_rThis.InvalidatePage( pPage );
+ if ( m_rThis.IsLayoutFrame() )
+ {
+ SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(&m_rThis)->ContainsContent();
+ if ( pCnt )
+ pCnt->InvalidatePage( pPage );
+ }
+ else if ( pSh && pSh->GetDoc()->GetLineNumberInfo().IsRestartEachPage()
+ && pPage->FindFirstBodyContent() == &m_rThis )
+ {
+ m_rThis.InvalidateLineNum_();
+ }
+ }
+ if ( bInvaLay || (pSibling && pSibling->IsLayoutFrame()) )
+ m_rThis.GetUpper()->InvalidatePage( pPage );
+}
+
+bool SwFlowFrame::IsAnFollow( const SwFlowFrame *pAssumed ) const
+{
+ const SwFlowFrame *pFoll = this;
+ do
+ { if ( pAssumed == pFoll )
+ return true;
+ pFoll = pFoll->GetFollow();
+ } while ( pFoll );
+ return false;
+}
+
+SwTextFrame* SwContentFrame::FindMaster() const
+{
+ OSL_ENSURE( IsFollow(), "SwContentFrame::FindMaster(): !IsFollow" );
+
+ const SwContentFrame* pPrec = static_cast<const SwContentFrame*>(SwFlowFrame::GetPrecede());
+
+ if ( pPrec && pPrec->HasFollow() && pPrec->GetFollow() == this )
+ {
+ OSL_ENSURE( pPrec->IsTextFrame(), "NoTextFrame with follow found" );
+ return const_cast<SwTextFrame*>(static_cast< const SwTextFrame* >(pPrec));
+ }
+
+ OSL_FAIL( "Follow is lost in Space." );
+ return nullptr;
+}
+
+SwSectionFrame* SwSectionFrame::FindMaster() const
+{
+ OSL_ENSURE( IsFollow(), "SwSectionFrame::FindMaster(): !IsFollow" );
+
+ if (!m_pSection)
+ return nullptr;
+
+ SwIterator<SwSectionFrame,SwFormat> aIter( *m_pSection->GetFormat() );
+ SwSectionFrame* pSect = aIter.First();
+ while ( pSect )
+ {
+ if (pSect->GetFollow() == this)
+ return pSect;
+ pSect = aIter.Next();
+ }
+
+ OSL_FAIL( "Follow is lost in Space." );
+ return nullptr;
+}
+
+SwTabFrame* SwTabFrame::FindMaster( bool bFirstMaster ) const
+{
+ OSL_ENSURE( IsFollow(), "SwTabFrame::FindMaster(): !IsFollow" );
+
+ SwIterator<SwTabFrame,SwFormat> aIter( *GetTable()->GetFrameFormat() );
+ SwTabFrame* pTab = aIter.First();
+ while ( pTab )
+ {
+ if ( bFirstMaster )
+ {
+ // Optimization. This makes code like this obsolete:
+ // while ( pTab->IsFollow() )
+ // pTab = pTab->FindMaster();
+
+ if ( !pTab->IsFollow() )
+ {
+ SwTabFrame* pNxt = pTab;
+ while ( pNxt )
+ {
+ if ( pNxt->GetFollow() == this )
+ return pTab;
+ pNxt = pNxt->GetFollow();
+ }
+ }
+ }
+ else
+ {
+ if ( pTab->GetFollow() == this )
+ return pTab;
+ }
+
+ pTab = aIter.Next();
+ }
+
+ OSL_FAIL( "Follow is lost in Space." );
+ return nullptr;
+}
+
+/**
+ * Returns the next/previous Layout leaf that's NOT below this (or even is this itself).
+ * Also, that leaf must be in the same text flow as the pAnch origin frame (Body, Footnote)
+ */
+const SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd,
+ const SwFrame *pAnch ) const
+{
+ // No flow, no joy...
+ if ( !(IsInDocBody() || IsInFootnote() || IsInFly()) )
+ return nullptr;
+
+ const SwFrame *pLeaf = this;
+ bool bFound = false;
+
+ do
+ { pLeaf = const_cast<SwFrame*>(pLeaf)->GetLeaf( eMakePage, bFwd );
+
+ if ( pLeaf &&
+ (!IsLayoutFrame() || !static_cast<const SwLayoutFrame*>(this)->IsAnLower( pLeaf )))
+ {
+ if ( pAnch->IsInDocBody() == pLeaf->IsInDocBody() &&
+ pAnch->IsInFootnote() == pLeaf->IsInFootnote() )
+ {
+ bFound = true;
+ }
+ }
+ } while ( !bFound && pLeaf );
+
+ return static_cast<const SwLayoutFrame*>(pLeaf);
+}
+
+SwLayoutFrame *SwFrame::GetLeaf( MakePageType eMakePage, bool bFwd )
+{
+ if ( IsInFootnote() )
+ return bFwd ? GetNextFootnoteLeaf( eMakePage ) : GetPrevFootnoteLeaf( eMakePage );
+
+ // i#53323
+ // A frame could be inside a table AND inside a section.
+ // Thus, it has to be determined, which is the first parent.
+ bool bInTab( IsInTab() );
+ bool bInSct( IsInSct() );
+ if ( bInTab && bInSct )
+ {
+ const SwFrame* pUpperFrame( GetUpper() );
+ while ( pUpperFrame )
+ {
+ if ( pUpperFrame->IsTabFrame() )
+ {
+ // the table is the first.
+ bInSct = false;
+ break;
+ }
+ else if ( pUpperFrame->IsSctFrame() )
+ {
+ // the section is the first.
+ bInTab = false;
+ break;
+ }
+
+ pUpperFrame = pUpperFrame->GetUpper();
+ }
+ }
+
+ if ( bInTab && ( !IsTabFrame() || GetUpper()->IsCellFrame() ) ) // TABLE IN TABLE
+ return bFwd ? GetNextCellLeaf() : GetPrevCellLeaf();
+
+ if ( bInSct )
+ return bFwd ? GetNextSctLeaf( eMakePage ) : GetPrevSctLeaf();
+
+ return bFwd ? GetNextLeaf( eMakePage ) : GetPrevLeaf();
+}
+
+bool SwFrame::WrongPageDesc( SwPageFrame* pNew )
+{
+ // Now it's getting a bit complicated:
+
+ // Maybe I'm bringing a Pagedesc myself; in that case,
+ // the pagedesc of the next page needs to correspond.
+ // Otherwise, I'll have to dig a bit deeper to see where
+ // the following Pagedesc is coming from.
+ // If the following page itself tells me that it's pagedesc
+ // is wrong, I can happily exchange it.
+ // If the page however thinks that it's pagedesc is correct,
+ // this doesn't mean it's useful to me:
+ // If the first BodyContent asks for a PageDesc or a PageBreak,
+ // I'll have to insert a new page - except the desired page is
+ // the correct one.
+ // If I inserted a new page, the problems only get started:
+ // because then it's likely for the next page to have been
+ // wrong and having been swapped because of that.
+ // This in turn means that I have a new (and correct) page,
+ // but the conditions to swap still apply.
+ // Way out of the situation: Try to preliminarily insert a
+ // new page once (empty pages are already inserted by InsertPage()
+ // if required)
+
+ //My Pagedesc doesn't count if I'm a follow!
+ const SwPageDesc *pDesc = nullptr;
+ std::optional<sal_uInt16> oTmp;
+ SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( this );
+ if ( !pFlow || !pFlow->IsFollow() )
+ {
+ const SwFormatPageDesc &rFormatDesc = GetPageDescItem();
+ pDesc = rFormatDesc.GetPageDesc();
+ if( pDesc )
+ {
+ if( !pDesc->GetRightFormat() )
+ oTmp = 2;
+ else if( !pDesc->GetLeftFormat() )
+ oTmp = 1;
+ else if( rFormatDesc.GetNumOffset() )
+ oTmp = rFormatDesc.GetNumOffset();
+ }
+ }
+
+ // Does the Content bring a Pagedesc or do we need the
+ // virtual page number of the new layout leaf?
+ // PageDesc isn't allowed with Follows
+ const bool isRightPage = oTmp ? sw::IsRightPageByNumber(*mpRoot, *oTmp) : pNew->OnRightPage();
+ if ( !pDesc )
+ pDesc = pNew->FindPageDesc();
+
+ bool bFirst = pNew->OnFirstPage();
+
+ const SwFlowFrame *pNewFlow = pNew->FindFirstBodyContent();
+ // Did we find ourselves?
+ if( pNewFlow == pFlow )
+ pNewFlow = nullptr;
+ if ( pNewFlow && pNewFlow->GetFrame().IsInTab() )
+ pNewFlow = pNewFlow->GetFrame().FindTabFrame();
+ const SwPageDesc *pNewDesc= ( pNewFlow && !pNewFlow->IsFollow() )
+ ? pNewFlow->GetFrame().GetPageDescItem().GetPageDesc()
+ : nullptr;
+
+ SAL_INFO( "sw.pageframe", "WrongPageDesc p: " << pNew << " phys: " << pNew->GetPhyPageNum() );
+ SAL_INFO( "sw.pageframe", "WrongPageDesc " << pNew->GetPageDesc() << " " << pDesc );
+ SAL_INFO( "sw.pageframe", "WrongPageDesc right: " << isRightPage
+ << " first: " << bFirst << " " << pNew->GetFormat() << " == "
+ << (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)) << " "
+ << (isRightPage ? pDesc->GetLeftFormat(bFirst) : pDesc->GetRightFormat(bFirst)) );
+
+ return (pNew->GetPageDesc() != pDesc) // own desc ?
+ || (pNew->GetFormat() !=
+ (isRightPage ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst)))
+ || (pNewDesc && pNewDesc == pDesc);
+}
+
+/// Returns the next layout leaf in which we can move the frame.
+SwLayoutFrame *SwFrame::GetNextLeaf( MakePageType eMakePage )
+{
+ OSL_ENSURE( !IsInFootnote(), "GetNextLeaf(), don't call me for Footnote." );
+ OSL_ENSURE( !IsInSct(), "GetNextLeaf(), don't call me for Sections." );
+
+ const bool bBody = IsInDocBody(); // If I'm coming from the DocBody,
+ // I want to end up in the body.
+
+ // It doesn't make sense to insert pages, as we only want to search the
+ // chain.
+ if( IsInFly() )
+ eMakePage = MAKEPAGE_NONE;
+
+ // For tables, we just take the big leap. A simple GetNext would
+ // iterate through the first cells and, in turn, all other cells.
+ SwLayoutFrame *pLayLeaf = nullptr;
+ if ( IsTabFrame() )
+ {
+ SwFrame *const pTmp = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ if ( pTmp )
+ pLayLeaf = pTmp->GetUpper();
+ }
+ if ( !pLayLeaf )
+ pLayLeaf = GetNextLayoutLeaf();
+
+ SwLayoutFrame *pOldLayLeaf = nullptr; // Make sure that we don't have to
+ // start searching from top when we
+ // have a freshly created page.
+ bool bNewPg = false; // Only insert a new page once.
+
+ while ( true )
+ {
+ if ( pLayLeaf )
+ {
+ // There's yet another LayoutFrame. Let's see if it's ready to host
+ // me as well.
+ // It only needs to be of the same kind like my starting point
+ // (DocBody or Footnote respectively)
+ if ( pLayLeaf->FindPageFrame()->IsFootnotePage() )
+ { // If I ended up at the end note pages, we're done.
+ pLayLeaf = nullptr;
+ continue;
+ }
+ if ( (bBody && !pLayLeaf->IsInDocBody()) || pLayLeaf->IsInTab()
+ || pLayLeaf->IsInSct() )
+ {
+ // They don't want me! Try again
+ pOldLayLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ continue;
+ }
+
+ // I'm wanted, therefore I'm done. However, it may still be that,
+ // during a page break, the page type isn't the desired one. In that
+ // case we have to insert a page of the correct type.
+
+ if( !IsFlowFrame() && ( eMakePage == MAKEPAGE_NONE ||
+ eMakePage==MAKEPAGE_APPEND || eMakePage==MAKEPAGE_NOSECTION ) )
+ return pLayLeaf;
+
+ SwPageFrame *pNew = pLayLeaf->FindPageFrame();
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ // The pagedesc check does not make sense for frames in fly frames
+ if ( pNew != FindPageFrame() && !bNewPg && !IsInFly() &&
+ // i#46683
+ // Do not consider page descriptions in browse mode (since
+ // MoveBwd ignored them)
+ !(pSh && pSh->GetViewOptions()->getBrowseMode() ) )
+ {
+ if( WrongPageDesc( pNew ) )
+ {
+ SwFootnoteContFrame *pCont = pNew->FindFootnoteCont();
+ if( pCont )
+ {
+ // If the reference of the first footnote of this page
+ // lies before the page, we'd rather not insert a new page.
+
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ if( pFootnote && pFootnote->GetRef() )
+ {
+ const sal_uInt16 nRefNum = pNew->GetPhyPageNum();
+ if( pFootnote->GetRef()->GetPhyPageNum() < nRefNum )
+ break;
+ }
+ }
+ //Gotcha! The following page is wrong, therefore we need to
+ //insert a new one.
+ if ( eMakePage == MAKEPAGE_INSERT )
+ {
+ bNewPg = true;
+
+ SwPageFrame *pPg = pOldLayLeaf ?
+ pOldLayLeaf->FindPageFrame() : nullptr;
+ if ( pPg && pPg->IsEmptyPage() )
+ // Don't insert behind. Insert before the EmptyPage.
+ pPg = static_cast<SwPageFrame*>(pPg->GetPrev());
+
+ if ( !pPg || pPg == pNew )
+ pPg = FindPageFrame();
+
+ InsertPage( pPg, false );
+ pLayLeaf = GetNextLayoutLeaf();
+ pOldLayLeaf = nullptr;
+ continue;
+ }
+ else
+ pLayLeaf = nullptr;
+ }
+ }
+ break;
+ }
+ else
+ {
+ // There's no other matching LayoutFrame, so we have to insert
+ // a new page.
+ if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
+ {
+ InsertPage(
+ pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
+ false );
+
+ // And again from the start.
+ pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
+ }
+ else
+ break;
+ }
+ }
+ return pLayLeaf;
+}
+
+/// Returns the previous layout leaf where we can move the frame.
+SwLayoutFrame *SwFrame::GetPrevLeaf()
+{
+ OSL_ENSURE( !IsInFootnote(), "GetPrevLeaf(), don't call me for Footnote." );
+
+ const bool bBody = IsInDocBody(); // If I'm coming from the DocBody,
+ // I want to end up in the body.
+ const bool bFly = IsInFly();
+
+ SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
+ SwLayoutFrame *pPrevLeaf = nullptr;
+
+ while ( pLayLeaf )
+ {
+ if ( pLayLeaf->IsInTab() || // Never go into tables.
+ pLayLeaf->IsInSct() ) // Same goes for sections!
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ else if ( bBody && pLayLeaf->IsInDocBody() )
+ {
+ if ( pLayLeaf->Lower() )
+ break;
+ pPrevLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ if ( pLayLeaf )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ else if ( bFly )
+ break; //Contents in Flys should accept any layout leaf.
+ else
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ }
+ return pLayLeaf ? pLayLeaf : pPrevLeaf;
+}
+
+bool SwFlowFrame::IsPrevObjMove() const
+{
+ // true: The FlowFrame must respect the a border of the predecessor, also needs
+ // to insert a break if required.
+
+ //!!!!!!!!!!!Hack!!!!!!!!!!!
+ const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ return false;
+
+ SwFrame *pPre = m_rThis.FindPrev();
+
+ if ( pPre && pPre->GetDrawObjs() )
+ {
+ OSL_ENSURE( SwFlowFrame::CastFlowFrame( pPre ), "new flowfrm?" );
+ if( SwFlowFrame::CastFlowFrame( pPre )->IsAnFollow( this ) )
+ return false;
+ if (SwFlowFrame::CastFlowFrame(pPre)->IsJoinLocked())
+ {
+ SwBorderAttrAccess baa(SwFrame::GetCache(), pPre);
+ SwBorderAttrs const& rAttrs(*baa.Get());
+ if (SwFlowFrame::CastFlowFrame(pPre)->IsKeep(rAttrs.GetAttrSet().GetKeep(), pPre->GetBreakItem()))
+ { // pPre is currently being formatted - maybe it moved back but
+ // its objects still have the old page's body as
+ // mpVertPosOrientFrame and SwContentFrame::MakeAll() is calling
+ // pNxt->Calc() in this case so allow this frame to move back
+ return false; // too, else pPre is forced to move forward again.
+ }
+ }
+ SwLayoutFrame* pPreUp = pPre->GetUpper();
+ // If the upper is a SectionFrame, or a column of a SectionFrame, we're
+ // allowed to protrude out of it. However, we need to respect the
+ // Upper of the SectionFrame.
+ if( pPreUp->IsInSct() )
+ {
+ if( pPreUp->IsSctFrame() )
+ pPreUp = pPreUp->GetUpper();
+ else if( pPreUp->IsColBodyFrame() &&
+ pPreUp->GetUpper()->GetUpper()->IsSctFrame() )
+ pPreUp = pPreUp->GetUpper()->GetUpper()->GetUpper();
+ }
+ // i#26945 - re-factoring
+ // use <GetVertPosOrientFrame()> to determine, if object has followed the
+ // text flow to the next layout frame
+ for (SwAnchoredObject* pObj : *pPre->GetDrawObjs())
+ {
+
+ // Do not consider hidden objects
+ // i#26945 - do not consider object, which
+ // doesn't follow the text flow.
+ if ( pObj->GetFrameFormat().GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pObj->GetDrawObj()->GetLayer() ) &&
+ pObj->GetFrameFormat().GetFollowTextFlow().GetValue() )
+ {
+ const SwLayoutFrame* pVertPosOrientFrame = pObj->GetVertPosOrientFrame();
+ if ( pVertPosOrientFrame &&
+ pPreUp != pVertPosOrientFrame &&
+ !pPreUp->IsAnLower( pVertPosOrientFrame ) )
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+|* If there's a hard page break before the Frame AND there's a
+|* predecessor on the same page, true is returned (we need to create a
+|* new PageBreak). Otherwise, returns false.
+|* If bAct is set to true, this function returns true if
+|* there's a PageBreak.
+|* Of course, we don't evaluate the hard page break for follows.
+|* The page break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
+|* predecessor (AFTER). If there's no predecessor on the page, we don't
+|* need to think further.
+|* Also, a page break (or the need for one) is also present if
+|* the FrameFormat contains a PageDesc.
+|* The implementation works only on ContentFrames! - the definition
+|* of the predecessor is not clear for LayoutFrames.
+|*/
+bool SwFlowFrame::IsPageBreak( bool bAct ) const
+{
+ if ( !IsFollow() && m_rThis.IsInDocBody() &&
+ ( !m_rThis.IsInTab() || ( m_rThis.IsTabFrame() && !m_rThis.GetUpper()->IsInTab() ) ) ) // i66968
+ {
+ const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ return false;
+
+ // Determine predecessor
+ const SwFrame *pPrev = m_rThis.FindPrev();
+ while ( pPrev && ( !pPrev->IsInDocBody() ||
+ ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) )
+ pPrev = pPrev->FindPrev();
+
+ if ( pPrev )
+ {
+ OSL_ENSURE( pPrev->IsInDocBody(), "IsPageBreak: Not in DocBody?" );
+ if ( bAct )
+ { if ( m_rThis.FindPageFrame() == pPrev->FindPageFrame() )
+ return false;
+ }
+ else
+ { if ( m_rThis.FindPageFrame() != pPrev->FindPageFrame() )
+ return false;
+ }
+
+ //for compatibility, also break at column break if no columns exist
+ const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ const bool bTreatSingleColumnBreakAsPageBreak = rIDSA.get(DocumentSettingId::TREAT_SINGLE_COLUMN_BREAK_AS_PAGE_BREAK);
+ const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
+ if ( eBreak == SvxBreak::PageBefore ||
+ eBreak == SvxBreak::PageBoth ||
+ ( bTreatSingleColumnBreakAsPageBreak && eBreak == SvxBreak::ColumnBefore && !m_rThis.FindColFrame() ))
+ return true;
+ else
+ {
+ const SvxBreak &ePrB = pPrev->GetBreakItem().GetBreak();
+ if ( ePrB == SvxBreak::PageAfter ||
+ ePrB == SvxBreak::PageBoth ||
+ m_rThis.GetPageDescItem().GetPageDesc())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+|* If there's a hard column break before the Frame AND there is
+|* a predecessor in the same column, we return true (we need to create
+|* a ColBreak). Otherwise, we return false.
+|* If bAct is set to true, we return true if there's a ColBreak.
+|* Of course, we don't evaluate the hard column break for follows.
+|*
+|* The column break is in its own FrameFormat (BEFORE) or in the FrameFormat of the
+|* predecessor (AFTER). If there's no predecessor in the column, we don't
+|* need to think further.
+|* The implementation works only on ContentFrames! - the definition
+|* of the predecessor is not clear for LayoutFrames.
+|*/
+bool SwFlowFrame::IsColBreak( bool bAct ) const
+{
+ if ( !IsFollow() && (m_rThis.IsMoveable() || bAct) )
+ {
+ const SwFrame *pCol = m_rThis.FindColFrame();
+ if ( pCol )
+ {
+ // Determine predecessor
+ const SwFrame *pPrev = m_rThis.FindPrev();
+ while( pPrev && ( ( !pPrev->IsInDocBody() && !m_rThis.IsInFly() && !m_rThis.FindFooterOrHeader() ) ||
+ ( pPrev->IsTextFrame() && static_cast<const SwTextFrame*>(pPrev)->IsHiddenNow() ) ) )
+ pPrev = pPrev->FindPrev();
+
+ if ( pPrev )
+ {
+ if ( bAct )
+ { if ( pCol == pPrev->FindColFrame() )
+ return false;
+ }
+ else
+ { if ( pCol != pPrev->FindColFrame() )
+ return false;
+ }
+
+ const SvxBreak eBreak = m_rThis.GetBreakItem().GetBreak();
+ if ( eBreak == SvxBreak::ColumnBefore ||
+ eBreak == SvxBreak::ColumnBoth )
+ return true;
+ else
+ {
+ const SvxBreak &ePrB = pPrev->GetBreakItem().GetBreak();
+ if ( ePrB == SvxBreak::ColumnAfter ||
+ ePrB == SvxBreak::ColumnBoth )
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SwFlowFrame::HasParaSpaceAtPages( bool bSct ) const
+{
+ if( m_rThis.IsInSct() )
+ {
+ const SwFrame* pTmp = m_rThis.GetUpper();
+ while( pTmp )
+ {
+ if( pTmp->IsCellFrame() || pTmp->IsFlyFrame() ||
+ pTmp->IsFooterFrame() || pTmp->IsHeaderFrame() ||
+ ( pTmp->IsFootnoteFrame() && !static_cast<const SwFootnoteFrame*>(pTmp)->GetMaster() ) )
+ return true;
+ if( pTmp->IsPageFrame() )
+ return !pTmp->GetPrev() || IsPageBreak(true);
+ if( pTmp->IsColumnFrame() && pTmp->GetPrev() )
+ return IsColBreak( true );
+ if( pTmp->IsSctFrame() && ( !bSct || pTmp->GetPrev() ) )
+ return false;
+ pTmp = pTmp->GetUpper();
+ }
+ OSL_FAIL( "HasParaSpaceAtPages: Where's my page?" );
+ return false;
+ }
+ if( !m_rThis.IsInDocBody() || ( m_rThis.IsInTab() && !m_rThis.IsTabFrame()) ||
+ IsPageBreak( true ) || ( m_rThis.FindColFrame() && IsColBreak( true ) ) )
+ return true;
+ const SwFrame* pTmp = m_rThis.FindColFrame();
+ if( pTmp )
+ {
+ if( pTmp->GetPrev() )
+ return false;
+ }
+ else
+ pTmp = &m_rThis;
+ pTmp = pTmp->FindPageFrame();
+ return pTmp && !pTmp->GetPrev();
+}
+
+/** helper method to determine previous frame for calculation of the
+ upper space
+
+ i#11860
+*/
+const SwFrame* SwFlowFrame::GetPrevFrameForUpperSpaceCalc_( const SwFrame* _pProposedPrevFrame ) const
+{
+ const SwFrame* pPrevFrame = _pProposedPrevFrame
+ ? _pProposedPrevFrame
+ : m_rThis.GetPrev();
+
+ // Skip hidden paragraphs and empty sections
+ while ( pPrevFrame &&
+ ( ( pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ||
+ ( pPrevFrame->IsSctFrame() &&
+ !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+
+ // Special case: no direct previous frame is found but frame is in footnote
+ // Search for a previous frame in previous footnote,
+ // if frame isn't in a section, which is also in the footnote
+ if ( !pPrevFrame && m_rThis.IsInFootnote() &&
+ ( m_rThis.IsSctFrame() ||
+ !m_rThis.IsInSct() || !m_rThis.FindSctFrame()->IsInFootnote() ) )
+ {
+ const SwFootnoteFrame* pPrevFootnoteFrame =
+ static_cast<const SwFootnoteFrame*>(m_rThis.FindFootnoteFrame()->GetPrev());
+ if ( pPrevFootnoteFrame )
+ {
+ pPrevFrame = pPrevFootnoteFrame->GetLastLower();
+
+ // Skip hidden paragraphs and empty sections
+ while ( pPrevFrame &&
+ ( ( pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ||
+ ( pPrevFrame->IsSctFrame() &&
+ !static_cast<const SwSectionFrame*>(pPrevFrame)->GetSection() ) ) )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+ }
+ }
+ // Special case: found previous frame is a section
+ // Search for the last content in the section
+ if( pPrevFrame && pPrevFrame->IsSctFrame() )
+ {
+ const SwSectionFrame* pPrevSectFrame =
+ static_cast<const SwSectionFrame*>(pPrevFrame);
+ pPrevFrame = pPrevSectFrame->FindLastContent();
+ // If the last content is in a table _inside_ the section,
+ // take the table herself.
+ // Correction: Check directly, if table is inside table, instead of indirectly
+ // by checking, if section isn't inside a table
+ if ( pPrevFrame && pPrevFrame->IsInTab() )
+ {
+ const SwTabFrame* pTableFrame = pPrevFrame->FindTabFrame();
+ if ( pPrevSectFrame->IsAnLower( pTableFrame ) )
+ {
+ pPrevFrame = pTableFrame;
+ }
+ }
+ // Correction: skip hidden text frames
+ while ( pPrevFrame &&
+ pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+ }
+
+ return pPrevFrame;
+}
+
+/// Compare styles attached to these text frames.
+static bool lcl_IdenticalStyles(const SwFrame* pPrevFrame, const SwFrame* pFrame)
+{
+ SwTextFormatColl *pPrevFormatColl = nullptr;
+ if (pPrevFrame && pPrevFrame->IsTextFrame())
+ {
+ const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pPrevFrame );
+ pPrevFormatColl = dynamic_cast<SwTextFormatColl*>(
+ pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
+ }
+
+ bool bIdenticalStyles = false;
+ if (pFrame && pFrame->IsTextFrame())
+ {
+ const SwTextFrame *pTextFrame = static_cast< const SwTextFrame * >( pFrame );
+ SwTextFormatColl *const pFormatColl = dynamic_cast<SwTextFormatColl*>(
+ pTextFrame->GetTextNodeForParaProps()->GetFormatColl());
+ bIdenticalStyles = pPrevFormatColl == pFormatColl;
+ }
+ return bIdenticalStyles;
+}
+
+static bool lcl_getContextualSpacing(const SwFrame* pPrevFrame)
+{
+ bool bRet;
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), pPrevFrame);
+ const SwBorderAttrs *pAttrs = aAccess.Get();
+
+ bRet = pAttrs->GetULSpace().GetContext();
+
+ return bRet;
+}
+
+
+SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs,
+ const SwFrame* pPr,
+ const bool _bConsiderGrid ) const
+{
+ const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_( pPr );
+
+ std::optional<SwBorderAttrAccess> oAccess;
+ SwFrame* pOwn;
+ if( !pAttrs )
+ {
+ if( m_rThis.IsSctFrame() )
+ {
+ SwSectionFrame* pFoll = &static_cast<SwSectionFrame&>(m_rThis);
+ do
+ pOwn = pFoll->ContainsAny();
+ while( !pOwn && nullptr != ( pFoll = pFoll->GetFollow() ) );
+ if( !pOwn )
+ return 0;
+ }
+ else
+ pOwn = &m_rThis;
+ oAccess.emplace(SwFrame::GetCache(), pOwn);
+ pAttrs = oAccess->Get();
+ }
+ else
+ {
+ pOwn = &m_rThis;
+ }
+ SwTwips nUpper = 0;
+
+ {
+ const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ if( pPrevFrame )
+ {
+ const bool bUseFormerLineSpacing = rIDSA.get(DocumentSettingId::OLD_LINE_SPACING);
+ const bool bContextualSpacingThis = pAttrs->GetULSpace().GetContext();
+ const bool bContextualSpacingPrev = lcl_getContextualSpacing(pPrevFrame);
+ bool bIdenticalStyles = lcl_IdenticalStyles(pPrevFrame, &m_rThis);
+
+ const bool bContextualSpacing = bContextualSpacingThis
+ && bContextualSpacingPrev
+ && bIdenticalStyles;
+
+ // tdf#125893 always ignore own top margin setting of the actual paragraph
+ // with contextual spacing, if the previous paragraph is identical
+ const bool bHalfContextualSpacing = !bContextualSpacing
+ && bContextualSpacingThis
+ && !bContextualSpacingPrev
+ && bIdenticalStyles;
+
+ // tdf#134463 always ignore own bottom margin setting of the previous paragraph
+ // with contextual spacing, if the actual paragraph is identical
+ const bool bHalfContextualSpacingPrev = !bContextualSpacing
+ && !bContextualSpacingThis
+ && bContextualSpacingPrev
+ && bIdenticalStyles;
+
+ // i#11860 - use new method to determine needed spacing
+ // values of found previous frame and use these values.
+ SwTwips nPrevLowerSpace = 0;
+ SwTwips nPrevLineSpacing = 0;
+ // i#102458
+ bool bPrevLineSpacingProportional = false;
+ GetSpacingValuesOfFrame( (*pPrevFrame),
+ nPrevLowerSpace, nPrevLineSpacing,
+ bPrevLineSpacingProportional,
+ bIdenticalStyles);
+ if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) )
+ {
+ // FIXME: apply bHalfContextualSpacing for better portability?
+ nUpper = bContextualSpacing ? 0 : nPrevLowerSpace + pAttrs->GetULSpace().GetUpper();
+ SwTwips nAdd = nPrevLineSpacing;
+ // i#11859 - consideration of the line spacing
+ // for the upper spacing of a text frame
+ if ( bUseFormerLineSpacing )
+ {
+ // former consideration
+ if ( pOwn->IsTextFrame() )
+ {
+ nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
+ }
+ nUpper += nAdd;
+ }
+ else
+ {
+ // new consideration:
+ // Only the proportional line spacing of the previous
+ // text frame is considered for the upper spacing and
+ // the line spacing values are add up instead of
+ // building its maximum.
+ if ( pOwn->IsTextFrame() )
+ {
+ // i#102458
+ // Correction:
+ // A proportional line spacing of the previous text frame
+ // is added up to an own leading line spacing.
+ // Otherwise, the maximum of the leading line spacing
+ // of the previous text frame and the own leading line
+ // spacing is built.
+ if ( bPrevLineSpacingProportional )
+ {
+ nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
+ }
+ else
+ {
+ nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
+ }
+ }
+ nUpper += nAdd;
+ }
+ }
+ else
+ {
+ nUpper = bContextualSpacing ? 0 : std::max(
+ bHalfContextualSpacingPrev ? 0 : static_cast<tools::Long>(nPrevLowerSpace),
+ bHalfContextualSpacing ? 0 : static_cast<tools::Long>(pAttrs->GetULSpace().GetUpper()) );
+
+ // i#11859 - consideration of the line spacing
+ // for the upper spacing of a text frame
+ if ( bUseFormerLineSpacing )
+ {
+ // former consideration
+ if ( pOwn->IsTextFrame() )
+ nUpper = std::max( nUpper, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace()) );
+ if ( nPrevLineSpacing != 0 )
+ {
+ nUpper = std::max( nUpper, nPrevLineSpacing );
+ }
+ }
+ else
+ {
+ // new consideration:
+ // Only the proportional line spacing of the previous
+ // text frame is considered for the upper spacing and
+ // the line spacing values are add up and added to
+ // the paragraph spacing instead of building the
+ // maximum of the line spacings and the paragraph spacing.
+ SwTwips nAdd = nPrevLineSpacing;
+ if ( pOwn->IsTextFrame() )
+ {
+ // i#102458
+ // Correction:
+ // A proportional line spacing of the previous text frame
+ // is added up to an own leading line spacing.
+ // Otherwise, the maximum of the leading line spacing
+ // of the previous text frame and the own leading line
+ // spacing is built.
+ if ( bPrevLineSpacingProportional )
+ {
+ nAdd += static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true );
+ }
+ else
+ {
+ nAdd = std::max( nAdd, SwTwips(static_cast<SwTextFrame*>(pOwn)->GetLineSpace( true )) );
+ }
+ }
+ nUpper += nAdd;
+ }
+ }
+ }
+ else if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) &&
+ CastFlowFrame( pOwn )->HasParaSpaceAtPages( m_rThis.IsSctFrame() ) )
+ {
+ nUpper = pAttrs->GetULSpace().GetUpper();
+ }
+ }
+
+ // i#25029 - pass previous frame <pPrevFrame>
+ // to method <GetTopLine(..)>, if parameter <pPr> is set.
+ // Note: parameter <pPr> is set, if method is called from <SwTextFrame::WouldFit(..)>
+ nUpper += pAttrs->GetTopLine( m_rThis, (pPr ? pPrevFrame : nullptr) );
+
+ // i#11860 - consider value of new parameter <_bConsiderGrid>
+ // and use new method <GetUpperSpaceAmountConsideredForPageGrid(..)>
+
+ //consider grid in square page mode
+ if ( _bConsiderGrid && m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode() )
+ {
+ nUpper += GetUpperSpaceAmountConsideredForPageGrid_( nUpper );
+ }
+ return nUpper;
+}
+
+/** method to determine the upper space amount, which is considered for
+ the page grid
+
+ i#11860
+ Precondition: Position of frame is valid.
+*/
+SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid_(
+ const SwTwips _nUpperSpaceWithoutGrid ) const
+{
+ SwTwips nUpperSpaceAmountConsideredForPageGrid = 0;
+
+ if ( m_rThis.IsInDocBody() && m_rThis.GetAttrSet()->GetParaGrid().GetValue() )
+ {
+ const SwPageFrame* pPageFrame = m_rThis.FindPageFrame();
+ SwTextGridItem const*const pGrid(GetGridItem(pPageFrame));
+ if( pGrid )
+ {
+ const SwFrame* pBodyFrame = pPageFrame->FindBodyCont();
+ if ( pBodyFrame )
+ {
+ const tools::Long nGridLineHeight =
+ pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
+
+ SwRectFnSet aRectFnSet(&m_rThis);
+ const SwTwips nBodyPrtTop = aRectFnSet.GetPrtTop(*pBodyFrame);
+ const SwTwips nProposedPrtTop =
+ aRectFnSet.YInc( aRectFnSet.GetTop(m_rThis.getFrameArea()),
+ _nUpperSpaceWithoutGrid );
+
+ const SwTwips nSpaceAbovePrtTop =
+ aRectFnSet.YDiff( nProposedPrtTop, nBodyPrtTop );
+ const SwTwips nSpaceOfCompleteLinesAbove =
+ nGridLineHeight * ( nSpaceAbovePrtTop / nGridLineHeight );
+ SwTwips nNewPrtTop =
+ aRectFnSet.YInc( nBodyPrtTop, nSpaceOfCompleteLinesAbove );
+ if ( aRectFnSet.YDiff( nProposedPrtTop, nNewPrtTop ) > 0 )
+ {
+ nNewPrtTop = aRectFnSet.YInc( nNewPrtTop, nGridLineHeight );
+ }
+
+ const SwTwips nNewUpperSpace =
+ aRectFnSet.YDiff( nNewPrtTop,
+ aRectFnSet.GetTop(m_rThis.getFrameArea()) );
+
+ nUpperSpaceAmountConsideredForPageGrid =
+ nNewUpperSpace - _nUpperSpaceWithoutGrid;
+
+ OSL_ENSURE( nUpperSpaceAmountConsideredForPageGrid >= 0,
+ "<SwFlowFrame::GetUpperSpaceAmountConsideredForPageGrid(..)> - negative space considered for page grid!" );
+ }
+ }
+ }
+ return nUpperSpaceAmountConsideredForPageGrid;
+}
+
+/** method to determine the upper space amount, which is considered for
+ the previous frame
+
+ i#11860
+*/
+SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrame() const
+{
+ SwTwips nUpperSpaceAmountOfPrevFrame = 0;
+
+ const SwFrame* pPrevFrame = GetPrevFrameForUpperSpaceCalc_();
+ if ( pPrevFrame )
+ {
+ SwTwips nPrevLowerSpace = 0;
+ SwTwips nPrevLineSpacing = 0;
+ // i#102458
+ bool bDummy = false;
+ GetSpacingValuesOfFrame( (*pPrevFrame), nPrevLowerSpace, nPrevLineSpacing, bDummy, lcl_IdenticalStyles(pPrevFrame, &m_rThis));
+ if ( nPrevLowerSpace > 0 || nPrevLineSpacing > 0 )
+ {
+ const IDocumentSettingAccess& rIDSA = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
+ !rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) )
+ {
+ nUpperSpaceAmountOfPrevFrame = nPrevLowerSpace + nPrevLineSpacing;
+ }
+ else
+ {
+ nUpperSpaceAmountOfPrevFrame = std::max( nPrevLowerSpace, nPrevLineSpacing );
+ }
+ }
+ }
+
+ return nUpperSpaceAmountOfPrevFrame;
+}
+
+/** method to determine the upper space amount, which is considered for
+ the previous frame and the page grid, if option 'Use former object
+ positioning' is OFF
+
+ i#11860
+*/
+SwTwips SwFlowFrame::GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid() const
+{
+ SwTwips nUpperSpaceAmountConsideredForPrevFrameAndPageGrid = 0;
+
+ if (!m_rThis.GetUpper() || !m_rThis.GetUpper()->GetFormat())
+ {
+ return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
+ }
+
+ if ( !m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
+ {
+ nUpperSpaceAmountConsideredForPrevFrameAndPageGrid =
+ GetUpperSpaceAmountConsideredForPrevFrame() +
+ ( m_rThis.GetUpper()->GetFormat()->GetDoc()->IsSquaredPageMode()
+ ? GetUpperSpaceAmountConsideredForPageGrid_( CalcUpperSpace( nullptr, nullptr, false ) )
+ : 0 );
+ }
+
+ return nUpperSpaceAmountConsideredForPrevFrameAndPageGrid;
+}
+
+// Calculation of lower space
+
+SwTwips SwFlowFrame::CalcLowerSpace( const SwBorderAttrs* _pAttrs ) const
+{
+ SwTwips nLowerSpace = 0;
+
+ std::optional<SwBorderAttrAccess> oAttrAccess;
+ if ( !_pAttrs )
+ {
+ oAttrAccess.emplace(SwFrame::GetCache(), &m_rThis);
+ _pAttrs = oAttrAccess->Get();
+ }
+
+ bool bCommonBorder = true;
+ if ( m_rThis.IsInSct() && m_rThis.GetUpper()->IsColBodyFrame() )
+ {
+ const SwSectionFrame* pSectFrame = m_rThis.FindSctFrame();
+ bCommonBorder = pSectFrame->GetFormat()->GetBalancedColumns().GetValue();
+ }
+ nLowerSpace = bCommonBorder ?
+ _pAttrs->GetBottomLine( m_rThis ) :
+ _pAttrs->CalcBottomLine();
+
+ // i#26250
+ // - correct consideration of table frames
+ // - use new method <CalcAddLowerSpaceAsLastInTableCell(..)>
+ if ( ( ( m_rThis.IsTabFrame() && m_rThis.GetUpper()->IsInTab() ) ||
+ // No lower spacing, if frame has a follow
+ ( m_rThis.IsInTab() && !GetFollow() ) ) &&
+ !m_rThis.GetIndNext() )
+ {
+ nLowerSpace += CalcAddLowerSpaceAsLastInTableCell( _pAttrs );
+ }
+
+ // tdf#128195 Consider para spacing below last paragraph in header
+ bool bHasSpacingBelowPara = m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::HEADER_SPACING_BELOW_LAST_PARA);
+ if (bHasSpacingBelowPara && !m_rThis.IsInFly() && m_rThis.FindFooterOrHeader() && !GetFollow()
+ && !m_rThis.GetIndNext())
+ nLowerSpace += _pAttrs->GetULSpace().GetLower() + _pAttrs->CalcLineSpacing();
+
+ return nLowerSpace;
+}
+
+/** calculation of the additional space to be considered, if flow frame
+ is the last inside a table cell
+
+ i#26250
+*/
+SwTwips SwFlowFrame::CalcAddLowerSpaceAsLastInTableCell(
+ const SwBorderAttrs* _pAttrs ) const
+{
+ SwTwips nAdditionalLowerSpace = 0;
+
+ IDocumentSettingAccess const& rIDSA(m_rThis.GetUpper()->GetFormat()->getIDocumentSettingAccess());
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS))
+ {
+ const SwFrame* pFrame = &m_rThis;
+ if ( pFrame->IsSctFrame() )
+ {
+ const SwSectionFrame* pSectFrame = static_cast<const SwSectionFrame*>(pFrame);
+ pFrame = pSectFrame->FindLastContent();
+ if ( pFrame && pFrame->IsInTab() )
+ {
+ const SwTabFrame* pTableFrame = pFrame->FindTabFrame();
+ if ( pSectFrame->IsAnLower( pTableFrame ) )
+ {
+ pFrame = pTableFrame;
+ }
+ }
+ }
+
+ std::optional<SwBorderAttrAccess> oAttrAccess;
+ if (pFrame && (!_pAttrs || pFrame != &m_rThis))
+ {
+ oAttrAccess.emplace(SwFrame::GetCache(), pFrame);
+ _pAttrs = oAttrAccess->Get();
+ }
+
+ if (_pAttrs)
+ {
+ nAdditionalLowerSpace += _pAttrs->GetULSpace().GetLower();
+
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS))
+ {
+ nAdditionalLowerSpace += _pAttrs->CalcLineSpacing();
+ }
+ }
+ }
+
+ return nAdditionalLowerSpace;
+}
+
+/// Moves the Frame forward if it seems necessary regarding the current conditions and attributes.
+bool SwFlowFrame::CheckMoveFwd( bool& rbMakePage, bool bKeep, bool bIgnoreMyOwnKeepValue )
+{
+ const SwFrame* pNxt = m_rThis.GetIndNext();
+
+ if ( bKeep && //!bMovedBwd &&
+ ( !pNxt || ( pNxt->IsTextFrame() && static_cast<const SwTextFrame*>(pNxt)->IsEmptyMaster() ) ) &&
+ ( nullptr != (pNxt = m_rThis.FindNext()) ) && IsKeepFwdMoveAllowed(bIgnoreMyOwnKeepValue) )
+ {
+ if( pNxt->IsSctFrame() )
+ { // Don't get fooled by empty SectionFrames
+ const SwFrame* pTmp = nullptr;
+ while( pNxt && pNxt->IsSctFrame() &&
+ ( !static_cast<const SwSectionFrame*>(pNxt)->GetSection() ||
+ nullptr == ( pTmp = static_cast<const SwSectionFrame*>(pNxt)->ContainsAny() ) ) )
+ {
+ pNxt = pNxt->FindNext();
+ pTmp = nullptr;
+ }
+ if( pTmp )
+ pNxt = pTmp; // the content of the next notempty sectionfrm
+ }
+ if( pNxt && pNxt->isFrameAreaPositionValid() )
+ {
+ bool bMove = false;
+ const SwSectionFrame *pSct = m_rThis.FindSctFrame();
+ if( pSct && !pSct->isFrameAreaSizeValid() )
+ {
+ const SwSectionFrame* pNxtSct = pNxt->FindSctFrame();
+ if( pNxtSct && pSct->IsAnFollow( pNxtSct ) )
+ bMove = true;
+ }
+ else
+ bMove = true;
+ if( bMove )
+ {
+ //Keep together with the following frame
+ MoveFwd( rbMakePage, false );
+ return true;
+ }
+ }
+ }
+
+ bool bMovedFwd = false;
+
+ if ( m_rThis.GetIndPrev() )
+ {
+ if ( IsPrevObjMove() ) // Should we care about objects of the Prev?
+ {
+ bMovedFwd = true;
+ if ( !MoveFwd( rbMakePage, false ) )
+ rbMakePage = false;
+ }
+ else
+ {
+ if ( IsPageBreak( false ) )
+ {
+ while ( MoveFwd( rbMakePage, true ) )
+ /* do nothing */;
+ rbMakePage = false;
+ bMovedFwd = true;
+ }
+ else if ( IsColBreak ( false ) )
+ {
+ const SwPageFrame *pPage = m_rThis.FindPageFrame();
+ SwFrame *pCol = m_rThis.FindColFrame();
+ do
+ { MoveFwd( rbMakePage, false );
+ SwFrame *pTmp = m_rThis.FindColFrame();
+ if( pTmp != pCol )
+ {
+ bMovedFwd = true;
+ pCol = pTmp;
+ }
+ else
+ break;
+ } while ( IsColBreak( false ) );
+ if ( pPage != m_rThis.FindPageFrame() )
+ rbMakePage = false;
+ }
+ }
+ }
+ return bMovedFwd;
+}
+
+bool SwFlowFrame::ForbiddenForFootnoteCntFwd() const
+{
+ return m_rThis.IsTabFrame() || m_rThis.IsInTab();
+}
+
+/// Return value guarantees that a new page was not created,
+/// although false does not NECESSARILY indicate that a new page was created.
+/// Either false or true(MoveFootnoteCntFwd) can be returned if no changes were made
+bool SwFlowFrame::MoveFwd( bool bMakePage, bool bPageBreak, bool bMoveAlways )
+{
+//!!!!MoveFootnoteCntFwd might need to be updated as well.
+ SwFootnoteBossFrame *pOldBoss = m_rThis.FindFootnoteBossFrame();
+ if (m_rThis.IsInFootnote())
+ {
+ assert(!ForbiddenForFootnoteCntFwd()); // prevented by IsMoveable()
+ if (!m_rThis.IsContentFrame() || !pOldBoss)
+ {
+ SAL_WARN("sw.core", "Tables in footnotes are not truly supported");
+ return false;
+ }
+ return static_cast<SwContentFrame&>(m_rThis).MoveFootnoteCntFwd( bMakePage, pOldBoss );
+ }
+
+ if( !IsFwdMoveAllowed() && !bMoveAlways )
+ {
+ bool bNoFwd = true;
+ if( m_rThis.IsInSct() )
+ {
+ SwFootnoteBossFrame* pBoss = m_rThis.FindFootnoteBossFrame();
+ bNoFwd = !pBoss->IsInSct() || ( !pBoss->Lower()->GetNext() &&
+ !pBoss->GetPrev() );
+ }
+
+ // Allow the MoveFwd even if we do not have an IndPrev in these cases:
+ if ( m_rThis.IsInTab() &&
+ ( !m_rThis.IsTabFrame() ||
+ m_rThis.GetUpper()->IsInTab() ) &&
+ nullptr != m_rThis.GetNextCellLeaf() )
+ {
+ bNoFwd = false;
+ }
+
+ if( bNoFwd )
+ {
+ // It's allowed to move PageBreaks if the Frame isn't the first
+ // one on the page.
+ if ( !bPageBreak )
+ return false;
+
+ const SwFrame *pCol = m_rThis.FindColFrame();
+ if ( !pCol || !pCol->GetPrev() )
+ return false;
+ }
+ }
+
+ std::optional<SwFrameDeleteGuard> oDeleteGuard;
+ if (bMakePage)
+ oDeleteGuard.emplace(pOldBoss);
+
+ bool bSamePage = true;
+ SwLayoutFrame *pNewUpper =
+ m_rThis.GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
+
+ if ( pNewUpper )
+ {
+ PROTOCOL_ENTER( &m_rThis, PROT::MoveFwd, DbgAction::NONE, nullptr );
+ SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
+ // We move ourself and all the direct successors before the
+ // first ContentFrame below the new Upper.
+
+ // If our NewUpper lies in a SectionFrame, we need to make sure
+ // that it won't destroy itself in Calc.
+ SwSectionFrame* pSect = pNewUpper->FindSctFrame();
+ if( pSect )
+ {
+ // If we only switch column within our SectionFrame, we better don't
+ // call Calc, as this would format the SectionFrame, which in turn would
+ // call us again, etc.
+ if( pSect != m_rThis.FindSctFrame() )
+ {
+ bool bUnlock = !pSect->IsColLocked();
+ pSect->ColLock();
+ pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ if( bUnlock )
+ pSect->ColUnlock();
+ }
+ }
+ // Do not calculate split cell frames.
+ else if ( !pNewUpper->IsCellFrame() || pNewUpper->Lower() )
+ pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+
+ SwFootnoteBossFrame *pNewBoss = pNewUpper->FindFootnoteBossFrame();
+ bool bBossChg = pNewBoss != pOldBoss;
+ pNewBoss = pNewBoss->FindFootnoteBossFrame( true );
+ pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
+ SwPageFrame* pNewPage = pOldPage;
+
+ oDeleteGuard.reset();
+
+ // First, we move the footnotes.
+ bool bFootnoteMoved = false;
+
+ // i#26831
+ // If pSect has just been created, the printing area of pSect has
+ // been calculated based on the first content of its follow.
+ // In this case we prefer to call a SimpleFormat for this new
+ // section after we inserted the contents. Otherwise the section
+ // frame will invalidate its lowers, if its printing area changes
+ // in SwSectionFrame::Format, which can cause loops.
+ const bool bForceSimpleFormat = pSect && pSect->HasFollow() &&
+ !pSect->ContainsAny();
+
+ if ( pNewBoss != pOldBoss )
+ {
+ pNewPage = pNewBoss->FindPageFrame();
+ bSamePage = pNewPage == pOldPage;
+ // Set deadline, so the footnotes don't think up
+ // silly things...
+ SwRectFnSet aRectFnSet(pOldBoss);
+ SwSaveFootnoteHeight aHeight( pOldBoss,
+ aRectFnSet.GetBottom(pOldBoss->getFrameArea()) );
+ SwContentFrame* pStart = m_rThis.IsContentFrame() ?
+ static_cast<SwContentFrame*>(&m_rThis) : static_cast<SwLayoutFrame&>(m_rThis).ContainsContent();
+ OSL_ENSURE( pStart || ( m_rThis.IsTabFrame() && !static_cast<SwTabFrame&>(m_rThis).Lower() ),
+ "MoveFwd: Missing Content" );
+ SwLayoutFrame* pBody = pStart ? ( pStart->IsTextFrame() ?
+ const_cast<SwBodyFrame *>(static_cast<SwTextFrame*>(pStart)->FindBodyFrame()) : nullptr ) : nullptr;
+ if( pBody )
+ bFootnoteMoved = pBody->MoveLowerFootnotes( pStart, pOldBoss, pNewBoss,
+ false);
+ }
+ // It's possible when dealing with SectionFrames that we have been moved
+ // by pNewUpper->Calc(), for instance into the pNewUpper.
+ // MoveSubTree or PasteTree respectively is not prepared to handle such a
+ // situation.
+ if( pNewUpper != m_rThis.GetUpper() )
+ {
+ // i#27145
+ SwSectionFrame* pOldSct = nullptr;
+ if ( m_rThis.GetUpper()->IsSctFrame() )
+ {
+ pOldSct = static_cast<SwSectionFrame*>(m_rThis.GetUpper());
+ }
+
+ MoveSubTree( pNewUpper, pNewUpper->Lower() );
+
+ // i#27145
+ if ( pOldSct && pOldSct->GetSection() )
+ {
+ // Prevent loops by setting the new height at
+ // the section frame if footnotes have been moved.
+ // Otherwise the call of SwLayNotify::~SwLayNotify() for
+ // the (invalid) section frame will invalidate the first
+ // lower of its follow, because it grows due to the removed
+ // footnotes.
+ // Note: If pOldSct has become empty during MoveSubTree, it
+ // has already been scheduled for removal. No SimpleFormat
+ // for these.
+ pOldSct->SimpleFormat();
+ }
+
+ // i#26831
+ if ( bForceSimpleFormat )
+ {
+ pSect->SimpleFormat();
+ }
+
+ if ( bFootnoteMoved && !bSamePage )
+ {
+ pOldPage->UpdateFootnoteNum();
+ pNewPage->UpdateFootnoteNum();
+ }
+
+ if( bBossChg )
+ {
+ m_rThis.Prepare( PrepareHint::BossChanged, nullptr, false );
+ if( !bSamePage )
+ {
+ SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
+ pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true); // Will be done by CalcLayout() later on!
+
+ pNewPage->InvalidateSpelling();
+ pNewPage->InvalidateSmartTags();
+ pNewPage->InvalidateAutoCompleteWords();
+ pNewPage->InvalidateWordCount();
+ }
+ }
+ }
+ // No <CheckPageDesc(..)> in online layout
+ const SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+
+ if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
+ {
+ // i#106452
+ // check page description not only in situation with sections.
+ if ( !bSamePage &&
+ ( m_rThis.GetPageDescItem().GetPageDesc() ||
+ pOldPage->GetPageDesc()->GetFollow() != pNewPage->GetPageDesc() ) )
+ {
+ SwFrame::CheckPageDescs( pNewPage, false );
+ }
+ }
+ }
+ return bSamePage;
+}
+
+/** Return value tells whether any changes have been made.
+ * If true, the frame has moved backwards to an earlier column/section/frame/page etc.
+ *
+ * @note This should be called by derived classes.
+ * @note The actual moving must be implemented in the subclasses via Cut()/Paste().
+ */
+bool SwFlowFrame::MoveBwd( bool &rbReformat )
+{
+ SwFlowFrame::SetMoveBwdJump( false );
+
+ SwFootnoteFrame* pFootnote = m_rThis.FindFootnoteFrame();
+ if ( pFootnote && pFootnote->IsBackMoveLocked() )
+ return false;
+
+ // Text frames, which are directly inside
+ // tables aren't allowed to move backward.
+ if ( m_rThis.IsTextFrame() && m_rThis.IsInTab() )
+ {
+ const SwLayoutFrame* pUpperFrame = m_rThis.GetUpper();
+ while ( pUpperFrame )
+ {
+ if ( pUpperFrame->IsTabFrame() || pUpperFrame->IsRowFrame() )
+ {
+ return false;
+ }
+ // If the text frame is a follow-section-in-table, that can move
+ // backward as well.
+ bool bIsFollowSection = pUpperFrame->IsSctFrame() && static_cast<const SwSectionFrame*>(pUpperFrame)->GetPrecede();
+
+ // If the text frame is a follow-in-table, that can move
+ // backward as well.
+ bool bIsFollow = const_cast<SwLayoutFrame*>(pUpperFrame)->GetPrevCellLeaf();
+
+ if ( ( pUpperFrame->IsColumnFrame() && pUpperFrame->IsInSct() ) || bIsFollowSection || bIsFollow )
+ {
+ break;
+ }
+ pUpperFrame = pUpperFrame->GetUpper();
+ }
+ }
+
+ SwFootnoteBossFrame * pOldBoss = m_rThis.FindFootnoteBossFrame();
+ if (!pOldBoss)
+ return false;
+
+ SwPageFrame * const pOldPage = pOldBoss->FindPageFrame();
+ SwLayoutFrame *pNewUpper = nullptr;
+ bool bCheckPageDescs = false;
+ bool bCheckPageDescOfNextPage = false;
+
+ if ( pFootnote )
+ {
+ // If the footnote already sits on the same page/column as the reference,
+ // we can't flow back. The breaks don't need to be checked for footnotes.
+
+ // i#37084 FindLastContent does not necessarily
+ // have to have a result != 0
+ SwFrame* pRef = nullptr;
+ const bool bEndnote = pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ const IDocumentSettingAccess& rSettings
+ = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
+ if( bEndnote && pFootnote->IsInSct() )
+ {
+ SwSectionFrame* pSect = pFootnote->FindSctFrame();
+ if( pSect->IsEndnAtEnd() )
+ // Endnotes at the end of the section.
+ pRef = pSect->FindLastContent( SwFindMode::LastCnt );
+ }
+ else if (bEndnote && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
+ {
+ // Endnotes at the end of the document.
+ SwPageFrame* pPage = m_rThis.getRootFrame()->GetLastPage();
+ pRef = pPage->FindLastBodyContent();
+ }
+ if( !pRef )
+ // Endnotes on a separate page.
+ pRef = pFootnote->GetRef();
+
+ OSL_ENSURE( pRef, "MoveBwd: Endnote for an empty section?" );
+
+ if( !bEndnote )
+ pOldBoss = pOldBoss->FindFootnoteBossFrame( true );
+ SwFootnoteBossFrame *pRefBoss = pRef->FindFootnoteBossFrame( !bEndnote );
+ if ( pOldBoss != pRefBoss &&
+
+ ( !bEndnote ||
+ pRefBoss->IsBefore( pOldBoss ) )
+ )
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_FTN, false );
+ }
+ else if ( IsPageBreak( true ) ) // Do we have to respect a PageBreak?
+ {
+ // If the previous page doesn't have a Frame in the body,
+ // flowing back makes sense despite the PageBreak (otherwise,
+ // we'd get an empty page).
+ // Of course we need to overlook empty pages!
+ const SwFrame *pFlow = &m_rThis;
+ do
+ {
+ pFlow = pFlow->FindPrev();
+ } while ( pFlow &&
+ ( pFlow->FindPageFrame() == pOldPage ||
+ !pFlow->IsInDocBody() ) );
+ if ( pFlow )
+ {
+ tools::Long nDiff = pOldPage->GetPhyPageNum() - pFlow->GetPhyPageNum();
+ if ( nDiff > 1 )
+ {
+ if ( static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage() )
+ nDiff -= 1;
+ if ( nDiff > 1 )
+ {
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+ // i#53139
+ // Now <pNewUpper> is a previous layout frame, which contains
+ // content. But the new upper layout frame has to be the next one.
+ // Thus, hack for issue i14206 no longer needed, but fix for issue 114442
+ // Correct fix for i53139
+ // Check for wrong page description before using next new upper.
+ // i#66051 - further correction of fix for i53139
+ // Check for correct type of new next upper layout frame
+ // Another correction of fix for i53139
+ // Assumption, that in all cases <pNewUpper> is a previous
+ // layout frame, which contains content, is wrong.
+ // Another correction of fix for i53139
+ // Beside type check, check also, if proposed new next upper
+ // frame is inside the same frame types.
+ // i#73194 - and yet another correction
+ // of fix for i53139:
+ // Assure that the new next upper layout frame doesn't
+ // equal the current one.
+ // E.g.: content is on page 3, on page 2 is only a 'ghost'
+ // section and on page 1 is normal content. Method <FindPrev(..)>
+ // will find the last content of page 1, but <GetLeaf(..)>
+ // returns new upper on page 2.
+ if (pNewUpper && pNewUpper->Lower())
+ {
+ SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
+ if ( pNewNextUpper &&
+ pNewNextUpper != m_rThis.GetUpper() &&
+ pNewNextUpper->GetType() == pNewUpper->GetType() &&
+ pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
+ pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
+ pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
+ pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
+ !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
+ {
+ pNewUpper = pNewNextUpper;
+ bCheckPageDescOfNextPage = true;
+ }
+ }
+
+ bCheckPageDescs = true;
+ }
+ }
+ }
+ }
+ else if ( IsColBreak( true ) )
+ {
+ // If the previous column doesn't contain a ContentFrame, flowing back
+ // makes sense despite the ColumnBreak, as otherwise we'd get
+ // an empty column.
+ if( m_rThis.IsInSct() )
+ {
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+ if( pNewUpper && !SwFlowFrame::IsMoveBwdJump() &&
+ ( pNewUpper->ContainsContent() ||
+ ( ( !pNewUpper->IsColBodyFrame() ||
+ !pNewUpper->GetUpper()->GetPrev() ) &&
+ !pNewUpper->FindSctFrame()->GetPrev() ) ) )
+ {
+ pNewUpper = nullptr;
+ }
+ // i#53139
+ // i#69409 - check <pNewUpper>
+ // i#71065 - check <SwFlowFrame::IsMoveBwdJump()>
+ else if ( pNewUpper && !SwFlowFrame::IsMoveBwdJump() )
+ {
+ // Now <pNewUpper> is a previous layout frame, which
+ // contains content. But the new upper layout frame
+ // has to be the next one.
+ // Correct fix for i53139
+ // Check for wrong page description before using next new upper.
+ // i#66051 - further correction of fix for i53139
+ // Check for correct type of new next upper layout frame
+ // Another correction of fix for i53139
+ // Beside type check, check also, if proposed new next upper
+ // frame is inside the same frame types.
+ SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NOSECTION, true );
+ if ( pNewNextUpper &&
+ pNewNextUpper->GetType() == pNewUpper->GetType() &&
+ pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
+ pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
+ pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
+ pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
+ !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
+ {
+ pNewUpper = pNewNextUpper;
+ }
+ }
+ }
+ else
+ {
+ const SwFrame *pCol = m_rThis.FindColFrame();
+ assert(pCol);
+ bool bGoOn = true;
+ bool bJump = false;
+ do
+ {
+ if ( pCol->GetPrev() )
+ pCol = pCol->GetPrev();
+ else
+ {
+ bGoOn = false;
+ pCol = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+ }
+ if ( pCol )
+ {
+ // ColumnFrames now with BodyFrame
+ SwLayoutFrame* pColBody = pCol->IsColumnFrame() ?
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())) :
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol));
+ if ( pColBody->ContainsContent() )
+ {
+ bGoOn = false; // We have content here! we accept this
+ // only if GetLeaf() has set the MoveBwdJump.
+ if( SwFlowFrame::IsMoveBwdJump() )
+ {
+ pNewUpper = pColBody;
+ // i#53139
+ // Now <pNewUpper> is a previous layout frame, which
+ // contains content. But the new upper layout frame
+ // has to be the next one.
+ // Correct fix for i53139
+ // Check for wrong page description before using next new upper.
+ // i#66051 - further correction of fix for i53139
+ // Check for correct type of new next upper layout frame
+ // Another correction of fix for i53139
+ // Beside type check, check also, if proposed new next upper
+ // frame is inside the same frame types.
+ // i#71065
+ // Check that the proposed new next upper layout
+ // frame isn't the current one.
+ SwLayoutFrame* pNewNextUpper = pNewUpper->GetLeaf( MAKEPAGE_NONE, true );
+ if ( pNewNextUpper &&
+ pNewNextUpper != m_rThis.GetUpper() &&
+ pNewNextUpper->GetType() == pNewUpper->GetType() &&
+ pNewNextUpper->IsInDocBody() == pNewUpper->IsInDocBody() &&
+ pNewNextUpper->IsInFootnote() == pNewUpper->IsInFootnote() &&
+ pNewNextUpper->IsInTab() == pNewUpper->IsInTab() &&
+ pNewNextUpper->IsInSct() == pNewUpper->IsInSct() &&
+ !m_rThis.WrongPageDesc( pNewNextUpper->FindPageFrame() ) )
+ {
+ pNewUpper = pNewNextUpper;
+ }
+ }
+ }
+ else
+ {
+ if( pNewUpper ) // We already had an empty column, in other
+ bJump = true; // words we skipped one.
+ pNewUpper = pColBody; // this empty column could be considered,
+ // but we continue searching nevertheless.
+ }
+ }
+ } while( bGoOn );
+ if( bJump )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ }
+ else // No breaks - we can flow back.
+ pNewUpper = m_rThis.GetLeaf( MAKEPAGE_NONE, false );
+
+ // i#27801 - no move backward of 'master' text frame,
+ // if - due to its object positioning - it isn't allowed to be on the new page frame
+ // i#44049 - add another condition for not moving backward
+ // If one of its objects has restarted the layout process, moving backward
+ // isn't sensible either.
+ // i#47697 - refine condition made for issue i44049
+ // - allow move backward as long as the anchored object is only temporarily
+ // positions considering its wrapping style.
+ if ( pNewUpper &&
+ m_rThis.IsTextFrame() && !IsFollow() )
+ {
+ sal_uInt32 nToPageNum( 0 );
+ const bool bMoveFwdByObjPos = SwLayouter::FrameMovedFwdByObjPos(
+ *(pOldPage->GetFormat()->GetDoc()),
+ static_cast<SwTextFrame&>(m_rThis),
+ nToPageNum );
+ if ( bMoveFwdByObjPos &&
+ pNewUpper->FindPageFrame()->GetPhyPageNum() < nToPageNum )
+ {
+ pNewUpper = nullptr;
+ }
+ // i#44049 - check, if one of its anchored objects
+ // has restarted the layout process.
+ else if ( m_rThis.GetDrawObjs() )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *m_rThis.GetDrawObjs())
+ {
+ // i#47697 - refine condition - see above
+ if ( pAnchoredObj->RestartLayoutProcess() &&
+ !pAnchoredObj->IsTmpConsiderWrapInfluence() )
+ {
+ pNewUpper = nullptr;
+ break;
+ }
+ }
+ }
+ }
+
+ // With Follows, it's only allowed to flow back if there's no neighbor
+ // in the new environment (because that would be the Master).
+ // (6677) If however we skipped empty pages, we still have to move.
+ if ( pNewUpper && IsFollow() && pNewUpper->Lower() )
+ {
+ // i#79774
+ // neglect empty sections in proposed new upper frame
+ bool bProposedNewUpperContainsOnlyEmptySections( true );
+ {
+ const SwFrame* pLower( pNewUpper->Lower() );
+ while ( pLower )
+ {
+ if ( pLower->IsSctFrame() &&
+ !dynamic_cast<const SwSectionFrame&>(*pLower).GetSection() )
+ {
+ pLower = pLower->GetNext();
+ continue;
+ }
+ else
+ {
+ bProposedNewUpperContainsOnlyEmptySections = false;
+ break;
+ }
+ }
+ }
+ if ( !bProposedNewUpperContainsOnlyEmptySections )
+ {
+ if ( SwFlowFrame::IsMoveBwdJump() )
+ {
+ // Don't move after the Master, but into the next empty page.
+ SwFrame *pFrame = pNewUpper->Lower();
+ while ( pFrame->GetNext() )
+ pFrame = pFrame->GetNext();
+ pNewUpper = pFrame->GetLeaf( MAKEPAGE_INSERT, true );
+ if( pNewUpper == m_rThis.GetUpper() ) // Did we end up in the same place?
+ pNewUpper = nullptr; // If so, moving is not needed.
+ }
+ else
+ pNewUpper = nullptr;
+ }
+ }
+ if ( pNewUpper && !ShouldBwdMoved( pNewUpper, rbReformat ) )
+ {
+ if( !pNewUpper->Lower() )
+ {
+ if( pNewUpper->IsFootnoteContFrame() )
+ {
+ pNewUpper->Cut();
+ SwFrame::DestroyFrame(pNewUpper);
+ }
+ else
+ {
+ SwSectionFrame* pSectFrame = pNewUpper->FindSctFrame();
+
+ if ( pSectFrame && !pSectFrame->IsColLocked() &&
+ !pSectFrame->ContainsContent() && !pSectFrame->ContainsAny( true ) )
+ {
+ pSectFrame->DelEmpty( true );
+ SwFrame::DestroyFrame(pSectFrame);
+ m_rThis.setFrameAreaPositionValid(true);
+ }
+ }
+ }
+ pNewUpper = nullptr;
+ }
+
+ // i#21478 - don't move backward, if flow frame wants to
+ // keep with next frame and next frame is locked.
+ // i#38232 - If next frame is a table, do *not* check,
+ // if it's locked.
+ if ( pNewUpper && !IsFollow() &&
+ m_rThis.GetAttrSet()->GetKeep().GetValue() && m_rThis.GetIndNext() )
+ {
+ SwFrame* pIndNext = m_rThis.GetIndNext();
+ // i#38232
+ if ( !pIndNext->IsTabFrame() )
+ {
+ // get first content of section, while empty sections are skipped
+ while ( pIndNext && pIndNext->IsSctFrame() )
+ {
+ if( static_cast<SwSectionFrame*>(pIndNext)->GetSection() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pIndNext)->ContainsAny();
+ if ( pTmp )
+ {
+ pIndNext = pTmp;
+ break;
+ }
+ }
+ pIndNext = pIndNext->GetIndNext();
+ }
+ OSL_ENSURE( !pIndNext || dynamic_cast<const SwTextFrame*>( pIndNext) != nullptr,
+ "<SwFlowFrame::MovedBwd(..)> - incorrect next found." );
+ if ( pIndNext && pIndNext->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame(pIndNext)->IsJoinLocked() )
+ {
+ pNewUpper = nullptr;
+ }
+ }
+ }
+
+ // i#65250
+ // layout loop control for flowing content again and again moving
+ // backward under the same layout condition.
+ if ( pNewUpper && !IsFollow() &&
+ pNewUpper != m_rThis.GetUpper() &&
+ SwLayouter::MoveBwdSuppressed( *(pOldPage->GetFormat()->GetDoc()),
+ *this, *pNewUpper ) )
+ {
+ SwLayoutFrame* pNextNewUpper = pNewUpper->GetLeaf(
+ ( !m_rThis.IsSctFrame() && m_rThis.IsInSct() )
+ ? MAKEPAGE_NOSECTION
+ : MAKEPAGE_NONE,
+ true );
+ // i#73194 - make code robust
+ OSL_ENSURE( pNextNewUpper, "<SwFlowFrame::MoveBwd(..)> - missing next new upper" );
+ if ( pNextNewUpper &&
+ ( pNextNewUpper == m_rThis.GetUpper() ||
+ pNextNewUpper->GetType() != m_rThis.GetUpper()->GetType() ) )
+ {
+ // tdf#107398 do not leave empty footnote container around
+ if (!pNewUpper->Lower() && pNewUpper->IsFootnoteContFrame())
+ {
+ pNewUpper->Cut();
+ SwFrame::DestroyFrame(pNewUpper);
+ }
+ pNewUpper = nullptr;
+ OSL_FAIL( "<SwFlowFrame::MoveBwd(..)> - layout loop control for layout action <Move Backward> applied!" );
+ }
+ }
+
+ OSL_ENSURE( pNewUpper != m_rThis.GetUpper(),
+ "<SwFlowFrame::MoveBwd(..)> - moving backward to the current upper frame!?" );
+ if ( pNewUpper )
+ {
+ PROTOCOL_ENTER( &m_rThis, PROT::MoveBack, DbgAction::NONE, nullptr );
+ if ( pNewUpper->IsFootnoteContFrame() )
+ {
+ // I may have gotten a Container
+ SwFootnoteFrame *pNew = SwFootnoteContFrame::PrependChained(&m_rThis, false);
+ pNew->Paste( pNewUpper );
+ pNewUpper = pNew;
+ }
+ if( pNewUpper->IsFootnoteFrame() && m_rThis.IsInSct() )
+ {
+ SwSectionFrame* pSct = m_rThis.FindSctFrame();
+ // If we're in a section of a footnote, we may need to create
+ // a SwSectionFrame in the new upper
+ if( pSct->IsInFootnote() )
+ {
+ SwFrame* pTmp = pNewUpper->Lower();
+ if( pTmp )
+ {
+ while( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ if( !pTmp->IsSctFrame() ||
+ static_cast<SwSectionFrame*>(pTmp)->GetFollow() != pSct )
+ pTmp = nullptr;
+ }
+ if( pTmp )
+ pNewUpper = static_cast<SwSectionFrame*>(pTmp);
+ else
+ {
+ pSct = new SwSectionFrame( *pSct, true );
+ pSct->Paste( pNewUpper );
+ pSct->Init();
+ pNewUpper = pSct;
+ pSct->SimpleFormat();
+ }
+ }
+ }
+ bool bUnlock = false;
+ bool bFollow = false;
+ // Lock section. Otherwise, it could get destroyed if the only Content
+ // moves e.g. from the second into the first column.
+ SwSectionFrame* pSect = pNewUpper->FindSctFrame();
+ if( pSect )
+ {
+ bUnlock = !pSect->IsColLocked();
+ pSect->ColLock();
+ bFollow = pSect->HasFollow();
+ }
+
+ {
+ auto const pOld = m_rThis.GetUpper();
+ ::std::optional<SwFrameDeleteGuard> g;
+ if (m_rThis.GetUpper()->IsCellFrame())
+ {
+ // note: IsFollowFlowRow() is never set for new-style tables
+ SwTabFrame const*const pTabFrame(m_rThis.FindTabFrame());
+ if ( pTabFrame->IsFollow()
+ && static_cast<SwTabFrame const*>(pTabFrame->GetPrecede())->HasFollowFlowLine()
+ && pTabFrame->GetFirstNonHeadlineRow() == m_rThis.GetUpper()->GetUpper())
+ {
+ // lock follow-flow-row (similar to sections above)
+ g.emplace(m_rThis.GetUpper()->GetUpper());
+ assert(m_rThis.GetUpper()->GetUpper()->IsDeleteForbidden());
+ }
+ }
+ pNewUpper->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+ SAL_WARN_IF(pOld != m_rThis.GetUpper(), "sw.core",
+ "MoveBwd(): pNewUpper->Calc() moved this frame?");
+ }
+
+ m_rThis.Cut();
+
+ // optimization: format section, if its size is invalidated and if it's
+ // the new parent of moved backward frame.
+ bool bFormatSect( false );
+ if( bUnlock )
+ {
+ pSect->ColUnlock();
+ if( pSect->HasFollow() != bFollow )
+ {
+ pSect->InvalidateSize();
+ // - optimization
+ if ( pSect == pNewUpper )
+ bFormatSect = true;
+ }
+ }
+
+ m_rThis.Paste( pNewUpper );
+ // - optimization
+ if ( bFormatSect )
+ pSect->Calc(m_rThis.getRootFrame()->GetCurrShell()->GetOut());
+
+ SwPageFrame *pNewPage = m_rThis.FindPageFrame();
+ if( pNewPage != pOldPage )
+ {
+ m_rThis.Prepare( PrepareHint::BossChanged, static_cast<const void*>(pOldPage), false );
+ SwViewShell *pSh = m_rThis.getRootFrame()->GetCurrShell();
+ if ( pSh && !pSh->Imp()->IsUpdateExpFields() )
+ pSh->GetDoc()->getIDocumentFieldsAccess().SetNewFieldLst(true); // Will be done by CalcLayout() later on
+
+ pNewPage->InvalidateSpelling();
+ pNewPage->InvalidateSmartTags();
+ pNewPage->InvalidateAutoCompleteWords();
+ pNewPage->InvalidateWordCount();
+
+ // No <CheckPageDesc(..)> in online layout
+ if ( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) )
+ {
+ if ( bCheckPageDescs && pNewPage->GetNext() )
+ {
+ SwPageFrame* pStartPage = bCheckPageDescOfNextPage ?
+ pNewPage :
+ static_cast<SwPageFrame*>(pNewPage->GetNext());
+ SwFrame::CheckPageDescs( pStartPage, false);
+ }
+ else if (m_rThis.GetPageDescItem().GetPageDesc())
+ {
+ // First page could get empty for example by disabling
+ // a section
+ SwFrame::CheckPageDescs( pNewPage, false);
+ }
+ }
+ }
+ }
+ return pNewUpper != nullptr;
+}
+
+SwFlowFrame *SwFlowFrame::CastFlowFrame( SwFrame *pFrame )
+{
+ if ( pFrame->IsContentFrame() )
+ return static_cast<SwContentFrame*>(pFrame);
+ if ( pFrame->IsTabFrame() )
+ return static_cast<SwTabFrame*>(pFrame);
+ if ( pFrame->IsSctFrame() )
+ return static_cast<SwSectionFrame*>(pFrame);
+ return nullptr;
+}
+
+const SwFlowFrame *SwFlowFrame::CastFlowFrame( const SwFrame *pFrame )
+{
+ if ( pFrame->IsContentFrame() )
+ return static_cast<const SwContentFrame*>(pFrame);
+ if ( pFrame->IsTabFrame() )
+ return static_cast<const SwTabFrame*>(pFrame);
+ if ( pFrame->IsSctFrame() )
+ return static_cast<const SwSectionFrame*>(pFrame);
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
new file mode 100644
index 000000000..99f976eab
--- /dev/null
+++ b/sw/source/core/layout/fly.cxx
@@ -0,0 +1,2997 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <svl/itemiter.hxx>
+#include <vcl/imap.hxx>
+#include <tools/helpers.hxx>
+#include <editeng/protitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fmtfsize.hxx>
+#include <fmtclds.hxx>
+#include <fmtcntnt.hxx>
+#include <fmturl.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtornt.hxx>
+#include <fmtcnct.hxx>
+#include <ndgrf.hxx>
+#include <tolayoutanchoredobjectposition.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <ndole.hxx>
+#include <swtable.hxx>
+#include <svx/svdoashp.hxx>
+#include <layouter.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <dcontact.hxx>
+#include <dflyobj.hxx>
+#include <dview.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <hints.hxx>
+#include <tabfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrms.hxx>
+#include <sectfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <calbck.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <textboxhelper.hxx>
+#include <txtfly.hxx>
+#include <ndindex.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <osl/diagnose.h>
+
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <bodyfrm.hxx>
+#include <FrameControlsManager.hxx>
+#include <ndtxt.hxx>
+
+using namespace ::com::sun::star;
+
+static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame );
+
+SwFlyFrame::SwFlyFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwLayoutFrame( pFormat, pSib ),
+ // #i26791#
+ m_pPrevLink( nullptr ),
+ m_pNextLink( nullptr ),
+ m_bInCnt( false ),
+ m_bAtCnt( false ),
+ m_bLayout( false ),
+ m_bAutoPosition( false ),
+ m_bDeleted( false ),
+ m_nAuthor( std::string::npos ),
+ m_bValidContentPos( false )
+{
+ mnFrameType = SwFrameType::Fly;
+
+ m_bInvalid = m_bNotifyBack = true;
+ m_bLocked = m_bMinHeight =
+ m_bHeightClipped = m_bWidthClipped = m_bFormatHeightOnly = false;
+
+ // Size setting: Fixed size is always the width
+ const SwFormatFrameSize &rFrameSize = pFormat->GetFrameSize();
+ const SvxFrameDirection nDir = pFormat->GetFormatAttr( RES_FRAMEDIR ).GetValue();
+ if( SvxFrameDirection::Environment == nDir )
+ {
+ mbDerivedVert = true;
+ mbDerivedR2L = true;
+ }
+ else
+ {
+ mbInvalidVert = false;
+ mbDerivedVert = false;
+ mbDerivedR2L = false;
+ if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ mbVertical = true;
+
+ if ( SvxFrameDirection::Vertical_LR_TB == nDir )
+ mbVertLR = true;
+ else if (nDir == SvxFrameDirection::Vertical_LR_BT)
+ {
+ mbVertLR = true;
+ mbVertLRBT = true;
+ }
+ else
+ mbVertLR = false;
+ }
+ }
+
+ mbInvalidR2L = false;
+ if( SvxFrameDirection::Horizontal_RL_TB == nDir )
+ mbRightToLeft = true;
+ else
+ mbRightToLeft = false;
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( rFrameSize.GetWidth() );
+ aFrm.Height( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable ? MINFLY : rFrameSize.GetHeight() );
+ }
+
+ // Fixed or variable Height?
+ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
+ m_bMinHeight = true;
+ else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ mbFixSize = true;
+
+ // insert columns, if necessary
+ InsertColumns();
+
+ // First the Init, then the Content:
+ // This is due to the fact that the Content may have Objects/Frames,
+ // which are then registered
+ InitDrawObj(*pAnch);
+
+ Chain( pAnch );
+
+ InsertCnt();
+
+ // Put it somewhere outside so that out document is not formatted unnecessarily often
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(FAR_AWAY);
+ aFrm.Pos().setY(FAR_AWAY);
+}
+
+void SwFlyFrame::Chain( SwFrame* _pAnch )
+{
+ // Connect to chain neighbours.
+ // No problem, if a neighbor doesn't exist - the construction of the
+ // neighbor will make the connection
+ const SwFormatChain& rChain = GetFormat()->GetChain();
+ if ( !(rChain.GetPrev() || rChain.GetNext()) )
+ return;
+
+ if ( rChain.GetNext() )
+ {
+ SwFlyFrame* pFollow = FindChainNeighbour( *rChain.GetNext(), _pAnch );
+ if ( pFollow )
+ {
+ OSL_ENSURE( !pFollow->GetPrevLink(), "wrong chain detected" );
+ if ( !pFollow->GetPrevLink() )
+ SwFlyFrame::ChainFrames( this, pFollow );
+ }
+ }
+ if ( rChain.GetPrev() )
+ {
+ SwFlyFrame *pMaster = FindChainNeighbour( *rChain.GetPrev(), _pAnch );
+ if ( pMaster )
+ {
+ OSL_ENSURE( !pMaster->GetNextLink(), "wrong chain detected" );
+ if ( !pMaster->GetNextLink() )
+ SwFlyFrame::ChainFrames( pMaster, this );
+ }
+ }
+}
+
+void SwFlyFrame::InsertCnt()
+{
+ if ( GetPrevLink() )
+ return;
+
+ const SwFormatContent& rContent = GetFormat()->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), ":-( no content prepared." );
+ SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
+ // Lower() means SwColumnFrame; the Content then needs to be inserted into the (Column)BodyFrame
+ ::InsertCnt_( Lower() ? static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(Lower())->Lower()) : static_cast<SwLayoutFrame*>(this),
+ GetFormat()->GetDoc(), nIndex );
+
+ // NoText always have a fixed height.
+ if ( Lower() && Lower()->IsNoTextFrame() )
+ {
+ mbFixSize = true;
+ m_bMinHeight = false;
+ }
+}
+
+void SwFlyFrame::InsertColumns()
+{
+ // #i97379#
+ // Check, if column are allowed.
+ // Columns are not allowed for fly frames, which represent graphics or embedded objects.
+ const SwFormatContent& rContent = GetFormat()->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "<SwFlyFrame::InsertColumns()> - no content prepared." );
+ SwNodeIndex aFirstContent( *(rContent.GetContentIdx()), 1 );
+ if ( aFirstContent.GetNode().IsNoTextNode() )
+ {
+ return;
+ }
+
+ const SwFormatCol &rCol = GetFormat()->GetCol();
+ if ( rCol.GetNumCols() <= 1 )
+ return;
+
+ // Start off PrtArea to be as large as Frame, so that we can put in the columns
+ // properly. It'll adjust later on.
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( getFrameArea().Width() );
+ aPrt.Height( getFrameArea().Height() );
+ }
+
+ const SwFormatCol aOld; // ChgColumns() also needs an old value passed
+ ChgColumns( aOld, rCol );
+}
+
+void SwFlyFrame::DestroyImpl()
+{
+ // Accessible objects for fly frames will be destroyed in this destructor.
+ // For frames bound as char or frames that don't have an anchor we have
+ // to do that ourselves. For any other frame the call RemoveFly at the
+ // anchor will do that.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( IsAccessibleFrame() && GetFormat() && (IsFlyInContentFrame() || !GetAnchorFrame()) )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell *pVSh = pRootFrame->GetCurrShell();
+ if( pVSh && pVSh->Imp() )
+ {
+ // Lowers aren't disposed already, so we have to do a recursive
+ // dispose
+ pVSh->Imp()->DisposeAccessibleFrame( this, true );
+ }
+ }
+ }
+#endif
+
+ if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
+ {
+ ClearTmpConsiderWrapInfluence(); // remove this from SwLayouter
+
+ Unchain();
+
+ DeleteCnt();
+
+ if ( GetAnchorFrame() )
+ AnchorFrame()->RemoveFly( this );
+ }
+
+ FinitDrawObj();
+
+ SwLayoutFrame::DestroyImpl();
+
+ SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(getRootFrame()->GetCurrShell());
+ UpdateUnfloatButton(pWrtSh, false);
+}
+
+SwFlyFrame::~SwFlyFrame()
+{
+}
+
+const IDocumentDrawModelAccess& SwFlyFrame::getIDocumentDrawModelAccess()
+{
+ return GetFormat()->getIDocumentDrawModelAccess();
+}
+
+void SwFlyFrame::Unchain()
+{
+ if ( GetPrevLink() )
+ UnchainFrames( GetPrevLink(), this );
+ if ( GetNextLink() )
+ UnchainFrames( this, GetNextLink() );
+}
+
+void SwFlyFrame::DeleteCnt()
+{
+ SwFrame* pFrame = m_pLower;
+ while ( pFrame )
+ {
+ while ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() )
+ {
+ SwAnchoredObject *pAnchoredObj = (*pFrame->GetDrawObjs())[0];
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pAnchoredObj) != nullptr )
+ {
+ // consider 'virtual' drawing objects
+ SdrObject* pObj = pAnchoredObj->DrawObj();
+ if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj*>( pObj) )
+ {
+ pDrawVirtObj->RemoveFromWriterLayout();
+ pDrawVirtObj->RemoveFromDrawingPage();
+ }
+ else
+ {
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(::GetUserCall( pObj ));
+ if ( pContact )
+ {
+ pContact->DisconnectFromLayout();
+ }
+ }
+ }
+ }
+
+ pFrame->RemoveFromLayout();
+ SwFrame::DestroyFrame(pFrame);
+ pFrame = m_pLower;
+ }
+
+ InvalidatePage();
+}
+
+void SwFlyFrame::InitDrawObj(SwFrame const& rAnchorFrame)
+{
+ SetDrawObj(*SwFlyDrawContact::CreateNewRef(this, GetFormat(), rAnchorFrame));
+
+ // Set the right Layer
+ IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+ SdrLayerID nHeavenId = rIDDMA.GetHeavenId();
+ SdrLayerID nHellId = rIDDMA.GetHellId();
+ GetVirtDrawObj()->SetLayer( GetFormat()->GetOpaque().GetValue()
+ ? nHeavenId
+ : nHellId );
+}
+
+static SwPosition ResolveFlyAnchor(SwFrameFormat const& rFlyFrame)
+{
+ SwFormatAnchor const& rAnch(rFlyFrame.GetAnchor());
+ if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ { // arbitrarily pick last node
+ return SwPosition(SwNodeIndex(rFlyFrame.GetDoc()->GetNodes().GetEndOfContent(), -1));
+ }
+ else
+ {
+ SwPosition const*const pPos(rAnch.GetContentAnchor());
+ assert(pPos);
+ if (SwFrameFormat const*const pParent = pPos->nNode.GetNode().GetFlyFormat())
+ {
+ return ResolveFlyAnchor(*pParent);
+ }
+ else if (pPos->nContent.GetIdxReg())
+ {
+ return *pPos;
+ }
+ else
+ {
+ return SwPosition(*pPos->nNode.GetNode().GetContentNode(), 0);
+ }
+ }
+}
+
+void SwFlyFrame::FinitDrawObj()
+{
+ if(!GetVirtDrawObj() )
+ return;
+ SwFormat* pFormat = GetFormat();
+ // Deregister from SdrPageViews if the Objects is still selected there.
+ if(!pFormat->GetDoc()->IsInDtor())
+ {
+ SwViewShell* p1St = getRootFrame()->GetCurrShell();
+ if(p1St)
+ {
+ for(SwViewShell& rCurrentShell : p1St->GetRingContainer())
+ { // At the moment the Drawing can do just do an Unmark on everything,
+ // as the Object was already removed
+ if (rCurrentShell.HasDrawView() &&
+ rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())
+ {
+ SwFlyFrame const*const pOldSelFly = ::GetFlyFromMarked(nullptr, &rCurrentShell);
+ if (pOldSelFly == this)
+ {
+ assert(rCurrentShell.Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() == 1);
+ if (SwFEShell *const pFEShell = dynamic_cast<SwFEShell*>(&rCurrentShell))
+ { // tdf#131679 move any cursor out of fly
+ rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
+ if (pOldSelFly)
+ {
+ SwPosition const pos(ResolveFlyAnchor(*pOldSelFly->GetFormat()));
+ SwPaM const temp(pos);
+ pFEShell->SetSelection(temp);
+ // could also call SetCursor() like SwFEShell::SelectObj()
+ // does, but that would access layout a bit much...
+ }
+ }
+ else
+ {
+ rCurrentShell.Imp()->GetDrawView()->UnmarkAll();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Else calls delete of the ContactObj
+ GetVirtDrawObj()->SetUserCall(nullptr);
+
+ // Deregisters itself at the Master
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(GetVirtDrawObj());
+ SdrObject::Free(pTemp);
+}
+
+void SwFlyFrame::ChainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
+{
+ OSL_ENSURE( pMaster && pFollow, "incomplete chain" );
+ OSL_ENSURE( !pMaster->GetNextLink(), "link can not be changed" );
+ OSL_ENSURE( !pFollow->GetPrevLink(), "link can not be changed" );
+
+ pMaster->m_pNextLink = pFollow;
+ pFollow->m_pPrevLink = pMaster;
+
+ if ( pMaster->ContainsContent() )
+ {
+ // To get a text flow we need to invalidate
+ SwFrame *pInva = pMaster->FindLastLower();
+ SwRectFnSet aRectFnSet(pMaster);
+ const tools::Long nBottom = aRectFnSet.GetPrtBottom(*pMaster);
+ while ( pInva )
+ {
+ if( aRectFnSet.BottomDist( pInva->getFrameArea(), nBottom ) <= 0 )
+ {
+ pInva->InvalidateSize();
+ pInva->Prepare();
+ pInva = pInva->FindPrev();
+ }
+ else
+ pInva = nullptr;
+ }
+ }
+
+ if ( pFollow->ContainsContent() )
+ {
+ // There's only the content from the Masters left; the content from the Follow
+ // does not have any Frames left (should always be exactly one empty TextNode).
+ SwFrame *pFrame = pFollow->ContainsContent();
+ OSL_ENSURE( !pFrame->IsTabFrame() && !pFrame->FindNext(), "follow in chain contains content" );
+ pFrame->Cut();
+ SwFrame::DestroyFrame(pFrame);
+ }
+
+ // invalidate accessible relation set (accessibility wrapper)
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = pMaster->getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
+ }
+#endif
+}
+
+void SwFlyFrame::UnchainFrames( SwFlyFrame *pMaster, SwFlyFrame *pFollow )
+{
+ pMaster->m_pNextLink = nullptr;
+ pFollow->m_pPrevLink = nullptr;
+
+ if ( pFollow->ContainsContent() )
+ {
+ // The Master sucks up the content of the Follow
+ SwLayoutFrame *pUpper = pMaster;
+ if ( pUpper->Lower()->IsColumnFrame() )
+ {
+ pUpper = static_cast<SwLayoutFrame*>(pUpper->GetLastLower());
+ pUpper = static_cast<SwLayoutFrame*>(pUpper->Lower()); // The (Column)BodyFrame
+ OSL_ENSURE( pUpper && pUpper->IsColBodyFrame(), "Missing ColumnBody" );
+ }
+ SwFlyFrame *pFoll = pFollow;
+ while ( pFoll )
+ {
+ SwFrame *pTmp = ::SaveContent( pFoll );
+ if ( pTmp )
+ ::RestoreContent( pTmp, pUpper, pMaster->FindLastLower() );
+ pFoll->SetCompletePaint();
+ pFoll->InvalidateSize();
+ pFoll = pFoll->GetNextLink();
+ }
+ }
+
+ // The Follow needs his own content to be served
+ const SwFormatContent &rContent = pFollow->GetFormat()->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), ":-( No content prepared." );
+ SwNodeOffset nIndex = rContent.GetContentIdx()->GetIndex();
+ // Lower() means SwColumnFrame: this one contains another SwBodyFrame
+ ::InsertCnt_( pFollow->Lower() ? const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFollow->Lower())->Lower()))
+ : static_cast<SwLayoutFrame*>(pFollow),
+ pFollow->GetFormat()->GetDoc(), ++nIndex );
+
+ // invalidate accessible relation set (accessibility wrapper)
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = pMaster->getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = pMaster->getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleRelationSet( pMaster, pFollow );
+ }
+#endif
+}
+
+SwFlyFrame *SwFlyFrame::FindChainNeighbour( SwFrameFormat const &rChain, SwFrame *pAnch )
+{
+ // We look for the Fly that's in the same Area.
+ // Areas can for now only be Head/Footer or Flys.
+
+ if ( !pAnch ) // If an Anchor was passed along, that one counts (ctor!)
+ pAnch = AnchorFrame();
+
+ SwLayoutFrame *pLay;
+ if ( pAnch->IsInFly() )
+ pLay = pAnch->FindFlyFrame();
+ else
+ {
+ // FindFooterOrHeader is not appropriate here, as we may not have a
+ // connection to the Anchor yet.
+ pLay = pAnch->GetUpper();
+ while ( pLay && !(pLay->GetType() & (SwFrameType::Header|SwFrameType::Footer)) )
+ pLay = pLay->GetUpper();
+ }
+
+ SwIterator<SwFlyFrame,SwFormat> aIter( rChain );
+ SwFlyFrame *pFly = aIter.First();
+ if ( pLay )
+ {
+ while ( pFly )
+ {
+ if ( pFly->GetAnchorFrame() )
+ {
+ if ( pFly->GetAnchorFrame()->IsInFly() )
+ {
+ if ( pFly->AnchorFrame()->FindFlyFrame() == pLay )
+ break;
+ }
+ else if ( pLay == pFly->FindFooterOrHeader() )
+ break;
+ }
+ pFly = aIter.Next();
+ }
+ }
+ else if ( pFly )
+ {
+ OSL_ENSURE( !aIter.Next(), "chain with more than one instance" );
+ }
+ return pFly;
+}
+
+SwFrame *SwFlyFrame::FindLastLower()
+{
+ SwFrame *pRet = ContainsAny();
+ if ( pRet && pRet->IsInTab() )
+ pRet = pRet->FindTabFrame();
+ SwFrame *pNxt = pRet;
+ while ( pNxt && IsAnLower( pNxt ) )
+ { pRet = pNxt;
+ pNxt = pNxt->FindNext();
+ }
+ return pRet;
+}
+
+bool SwFlyFrame::FrameSizeChg( const SwFormatFrameSize &rFrameSize )
+{
+ bool bRet = false;
+ SwTwips nDiffHeight = getFrameArea().Height();
+ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Variable )
+ mbFixSize = m_bMinHeight = false;
+ else
+ {
+ if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ {
+ mbFixSize = true;
+ m_bMinHeight = false;
+ }
+ else if ( rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum )
+ {
+ mbFixSize = false;
+ m_bMinHeight = true;
+ }
+ nDiffHeight -= rFrameSize.GetHeight();
+ }
+ // If the Fly contains columns, we already need to set the Fly
+ // and the Columns to the required value or else we run into problems.
+ if ( Lower() )
+ {
+ if ( Lower()->IsColumnFrame() )
+ {
+ const SwRect aOld( GetObjRectWithSpaces() );
+ const Size aOldSz( getFramePrintArea().SSize() );
+ const SwTwips nDiffWidth = getFrameArea().Width() - rFrameSize.GetWidth();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( aFrm.Height() - nDiffHeight );
+ aFrm.Width ( aFrm.Width() - nDiffWidth );
+ }
+
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( aPrt.Height() - nDiffHeight );
+ aPrt.Width ( aPrt.Width() - nDiffWidth );
+ }
+
+ ChgLowersProp( aOldSz );
+ ::Notify( this, FindPageFrame(), aOld );
+ setFrameAreaPositionValid(false);
+ bRet = true;
+ }
+ else if ( Lower()->IsNoTextFrame() )
+ {
+ mbFixSize = true;
+ m_bMinHeight = false;
+ }
+ }
+ return bRet;
+}
+
+void SwFlyFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwFlyFrameInvFlags eInvFlags = SwFlyFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && pLegacy->m_pOld && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ SfxItemIter aNIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet());
+ SfxItemIter aOIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ SwAttrSetChg aOldSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld));
+ SwAttrSetChg aNewSet(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew));
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwFlyFrameInvFlags::NONE)
+ return;
+
+ Invalidate_();
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidatePos)
+ {
+ InvalidatePos_();
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ }
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidateSize)
+ {
+ InvalidateSize_();
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ }
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidatePrt)
+ InvalidatePrt_();
+ if(eInvFlags & SwFlyFrameInvFlags::SetNotifyBack)
+ SetNotifyBack();
+ if(eInvFlags & SwFlyFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ if((eInvFlags & SwFlyFrameInvFlags::ClearContourCache) && Lower() && Lower()->IsNoTextFrame())
+ ClrContourCache( GetVirtDrawObj() );
+ SwRootFrame *pRoot;
+ if(eInvFlags & SwFlyFrameInvFlags::InvalidateBrowseWidth && nullptr != (pRoot = getRootFrame()))
+ pRoot->InvalidateBrowseWidth();
+ // #i28701#
+ if(eInvFlags & SwFlyFrameInvFlags::UpdateObjInSortedList)
+ {
+ // update sorted object lists, the Writer fly frame is registered at.
+ UpdateObjInSortedList();
+ }
+
+ // #i87645# - reset flags for the layout process (only if something has been invalidated)
+ ResetLayoutProcessBools();
+ }
+ else if (auto pGetZOrdnerHint = dynamic_cast<const sw::GetZOrderHint*>(&rHint))
+ {
+ const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
+ if (rFormat.Which() == RES_FLYFRMFMT && rFormat.getIDocumentLayoutAccess().GetCurrentViewShell()) // #i11176#
+ pGetZOrdnerHint->m_rnZOrder = GetVirtDrawObj()->GetOrdNum();
+ }
+ else if (auto pConnectedHint = dynamic_cast<const sw::GetObjectConnectedHint*>(&rHint))
+ {
+ const auto& rFormat(dynamic_cast<const SwFrameFormat&>(rMod));
+ if (!pConnectedHint->m_risConnected && rFormat.Which() == RES_FLYFRMFMT && (!pConnectedHint->m_pRoot || pConnectedHint->m_pRoot == getRootFrame()))
+ pConnectedHint->m_risConnected = true;
+ }
+}
+
+void SwFlyFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwFlyFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ switch( nWhich )
+ {
+ case RES_VERT_ORIENT:
+ case RES_HORI_ORIENT:
+ // #i18732# - consider new option 'follow text flow'
+ case RES_FOLLOW_TEXT_FLOW:
+ {
+ // ATTENTION: Always also change Action in ChgRePos()!
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
+ }
+ break;
+ // #i28701# - consider new option 'wrap influence on position'
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ {
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack
+ | SwFlyFrameInvFlags::UpdateObjInSortedList;
+ }
+ break;
+ case RES_SURROUND:
+ {
+ //#i28701# - invalidate position on change of
+ // wrapping style.
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
+ // The background needs to be messaged and invalidated
+ const SwRect aTmp( GetObjRectWithSpaces() );
+ NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameAttributesChanged );
+
+ // By changing the flow of frame-bound Frames, a vertical alignment
+ // can be activated/deactivated => MakeFlyPos
+ if( RndStdIds::FLY_AT_FLY == GetFormat()->GetAnchor().GetAnchorId() )
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::SetNotifyBack;
+
+ // Delete contour in the Node if necessary
+ if ( Lower() && Lower()->IsNoTextFrame() &&
+ !GetFormat()->GetSurround().IsContour() )
+ {
+ SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
+ if ( pNd->HasContour() )
+ pNd->SetContour( nullptr );
+ }
+ // #i28701# - perform reorder of object lists
+ // at anchor frame and at page frame.
+ rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
+ }
+ break;
+
+ case RES_PROTECT:
+ if (pNew)
+ {
+ const SvxProtectItem *pP = static_cast<const SvxProtectItem*>(pNew);
+ GetVirtDrawObj()->SetMoveProtect( pP->IsPosProtected() );
+ GetVirtDrawObj()->SetResizeProtect( pP->IsSizeProtected() );
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleEditableState( true, this );
+ }
+#endif
+ }
+ break;
+ case RES_COL:
+ if (pOld && pNew)
+ {
+ ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
+ const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
+ if ( FrameSizeChg( rNew ) )
+ NotifyDrawObj();
+ rInvFlags |= SwFlyFrameInvFlags::InvalidateSize | SwFlyFrameInvFlags::SetNotifyBack
+ | SwFlyFrameInvFlags::SetCompletePaint;
+ }
+ break;
+
+ case RES_FRM_SIZE:
+ case RES_FMT_CHG:
+ {
+ const SwFormatFrameSize &rNew = GetFormat()->GetFrameSize();
+ if ( FrameSizeChg( rNew ) )
+ NotifyDrawObj();
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
+ | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetNotifyBack
+ | SwFlyFrameInvFlags::SetCompletePaint
+ | SwFlyFrameInvFlags::InvalidateBrowseWidth
+ | SwFlyFrameInvFlags::ClearContourCache;
+ if (pOld && RES_FMT_CHG == nWhich)
+ {
+ SwRect aNew( GetObjRectWithSpaces() );
+ SwRect aOld( getFrameArea() );
+ const SvxULSpaceItem &rUL = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetULSpace();
+ aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
+ aOld.AddHeight(rUL.GetLower() );
+ const SvxLRSpaceItem &rLR = static_cast<const SwFormatChg*>(pOld)->pChangedFormat->GetLRSpace();
+ aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
+ aOld.AddWidth(rLR.GetRight() );
+ aNew.Union( aOld );
+ NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
+
+ // Special case:
+ // When assigning a template we cannot rely on the old column
+ // attribute. As there need to be at least enough for ChgColumns,
+ // we need to create a temporary attribute.
+ SwFormatCol aCol;
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ sal_uInt16 nCol = 0;
+ SwFrame *pTmp = Lower();
+ do
+ { ++nCol;
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ aCol.Init( nCol, 0, 1000 );
+ }
+ ChgColumns( aCol, GetFormat()->GetCol() );
+ }
+
+ SwFormatURL aURL( GetFormat()->GetURL() );
+
+ SwFormatFrameSize *pNewFormatFrameSize = nullptr;
+ SwFormatChg *pOldFormatChg = nullptr;
+ if (nWhich == RES_FRM_SIZE)
+ pNewFormatFrameSize = const_cast<SwFormatFrameSize*>(static_cast<const SwFormatFrameSize*>(pNew));
+ else
+ pOldFormatChg = const_cast<SwFormatChg*>(static_cast<const SwFormatChg*>(pOld));
+
+ if (aURL.GetMap() && (pNewFormatFrameSize || pOldFormatChg))
+ {
+ const SwFormatFrameSize &rOld = pNewFormatFrameSize ?
+ *pNewFormatFrameSize :
+ pOldFormatChg->pChangedFormat->GetFrameSize();
+ //#35091# Can be "times zero", when loading the template
+ if ( rOld.GetWidth() && rOld.GetHeight() )
+ {
+
+ Fraction aScaleX( rOld.GetWidth(), rNew.GetWidth() );
+ Fraction aScaleY( rOld.GetHeight(), rOld.GetHeight() );
+ aURL.GetMap()->Scale( aScaleX, aScaleY );
+ SwFrameFormat *pFormat = GetFormat();
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aURL );
+ pFormat->UnlockModify();
+ }
+ }
+ const SvxProtectItem &rP = GetFormat()->GetProtect();
+ GetVirtDrawObj()->SetMoveProtect( rP.IsPosProtected() );
+ GetVirtDrawObj()->SetResizeProtect( rP.IsSizeProtected() );
+
+ if ( pSh )
+ pSh->InvalidateWindows( getFrameArea() );
+ const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+ const SdrLayerID nId = GetFormat()->GetOpaque().GetValue() ?
+ rIDDMA.GetHeavenId() :
+ rIDDMA.GetHellId();
+ GetVirtDrawObj()->SetLayer( nId );
+
+ if ( Lower() )
+ {
+ // Delete contour in the Node if necessary
+ if( Lower()->IsNoTextFrame() &&
+ !GetFormat()->GetSurround().IsContour() )
+ {
+ SwNoTextNode *pNd = static_cast<SwNoTextNode*>(static_cast<SwNoTextFrame*>(Lower())->GetNode());
+ if ( pNd->HasContour() )
+ pNd->SetContour( nullptr );
+ }
+ else if( !Lower()->IsColumnFrame() )
+ {
+ SwFrame* pFrame = GetLastLower();
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ }
+
+ // #i28701# - perform reorder of object lists
+ // at anchor frame and at page frame.
+ rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
+
+ break;
+ }
+ case RES_UL_SPACE:
+ case RES_LR_SPACE:
+ {
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::ClearContourCache;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ getRootFrame()->InvalidateBrowseWidth();
+ SwRect aNew( GetObjRectWithSpaces() );
+ SwRect aOld( getFrameArea() );
+ if (pNew)
+ {
+ if ( RES_UL_SPACE == nWhich )
+ {
+ const SvxULSpaceItem &rUL = *static_cast<const SvxULSpaceItem*>(pNew);
+ aOld.Top( std::max( aOld.Top() - tools::Long(rUL.GetUpper()), tools::Long(0) ) );
+ aOld.AddHeight(rUL.GetLower() );
+ }
+ else
+ {
+ const SvxLRSpaceItem &rLR = *static_cast<const SvxLRSpaceItem*>(pNew);
+ aOld.Left ( std::max( aOld.Left() - rLR.GetLeft(), tools::Long(0) ) );
+ aOld.AddWidth(rLR.GetRight() );
+ }
+ }
+ aNew.Union( aOld );
+ NotifyBackground( FindPageFrame(), aNew, PrepareHint::Clear );
+ }
+ break;
+
+ case RES_TEXT_VERT_ADJUST:
+ {
+ InvalidateContentPos();
+ rInvFlags |= SwFlyFrameInvFlags::SetCompletePaint;
+ }
+ break;
+
+ case RES_BOX:
+ case RES_SHADOW:
+ rInvFlags |= SwFlyFrameInvFlags::InvalidatePos | SwFlyFrameInvFlags::InvalidateSize
+ | SwFlyFrameInvFlags::InvalidatePrt | SwFlyFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_FRAMEDIR :
+ SetDerivedVert( false );
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+
+ case RES_OPAQUE:
+ if (pNew)
+ {
+ if ( pSh )
+ pSh->InvalidateWindows( getFrameArea() );
+
+ const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+ const SdrLayerID nId = static_cast<const SvxOpaqueItem*>(pNew)->GetValue() ?
+ rIDDMA.GetHeavenId() :
+ rIDDMA.GetHellId();
+ GetVirtDrawObj()->SetLayer( nId );
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ {
+ pSh->Imp()->DisposeAccessibleFrame( this );
+ pSh->Imp()->AddAccessibleFrame( this );
+ }
+ }
+#endif
+ // #i28701# - perform reorder of object lists
+ // at anchor frame and at page frame.
+ rInvFlags |= SwFlyFrameInvFlags::UpdateObjInSortedList;
+ }
+ break;
+
+ case RES_URL:
+ // The interface changes the frame size when interacting with text frames,
+ // the Map, however, needs to be relative to FrameSize().
+ if ( (!Lower() || !Lower()->IsNoTextFrame()) && pNew && pOld &&
+ static_cast<const SwFormatURL*>(pNew)->GetMap() && static_cast<const SwFormatURL*>(pOld)->GetMap() )
+ {
+ const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
+ if ( rSz.GetHeight() != getFrameArea().Height() ||
+ rSz.GetWidth() != getFrameArea().Width() )
+ {
+ SwFormatURL aURL( GetFormat()->GetURL() );
+ Fraction aScaleX( getFrameArea().Width(), rSz.GetWidth() );
+ Fraction aScaleY( getFrameArea().Height(), rSz.GetHeight() );
+ aURL.GetMap()->Scale( aScaleX, aScaleY );
+ SwFrameFormat *pFormat = GetFormat();
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aURL );
+ pFormat->UnlockModify();
+ }
+ }
+ // No invalidation necessary
+ break;
+
+ case RES_CHAIN:
+ if (pNew)
+ {
+ const SwFormatChain *pChain = static_cast<const SwFormatChain*>(pNew);
+ if ( pChain->GetNext() )
+ {
+ SwFlyFrame *pFollow = FindChainNeighbour( *pChain->GetNext() );
+ if ( GetNextLink() && pFollow != GetNextLink() )
+ SwFlyFrame::UnchainFrames( this, GetNextLink());
+ if ( pFollow )
+ {
+ if ( pFollow->GetPrevLink() &&
+ pFollow->GetPrevLink() != this )
+ SwFlyFrame::UnchainFrames( pFollow->GetPrevLink(),
+ pFollow );
+ if ( !GetNextLink() )
+ SwFlyFrame::ChainFrames( this, pFollow );
+ }
+ }
+ else if ( GetNextLink() )
+ SwFlyFrame::UnchainFrames( this, GetNextLink() );
+ if ( pChain->GetPrev() )
+ {
+ SwFlyFrame *pMaster = FindChainNeighbour( *pChain->GetPrev() );
+ if ( GetPrevLink() && pMaster != GetPrevLink() )
+ SwFlyFrame::UnchainFrames( GetPrevLink(), this );
+ if ( pMaster )
+ {
+ if ( pMaster->GetNextLink() &&
+ pMaster->GetNextLink() != this )
+ SwFlyFrame::UnchainFrames( pMaster,
+ pMaster->GetNextLink() );
+ if ( !GetPrevLink() )
+ SwFlyFrame::ChainFrames( pMaster, this );
+ }
+ }
+ else if ( GetPrevLink() )
+ SwFlyFrame::UnchainFrames( GetPrevLink(), this );
+ }
+ [[fallthrough]];
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+/// Gets information from the Modify
+bool SwFlyFrame::GetInfo( SfxPoolItem & rInfo ) const
+{
+ if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
+ return false; // There's a FlyFrame, so use it
+ return true; // Continue searching
+}
+
+void SwFlyFrame::Invalidate_( SwPageFrame const *pPage )
+{
+ InvalidatePage( pPage );
+ m_bNotifyBack = m_bInvalid = true;
+
+ SwFlyFrame *pFrame;
+ if ( GetAnchorFrame() && nullptr != (pFrame = AnchorFrame()->FindFlyFrame()) )
+ {
+ // Very bad case: If the Fly is bound within another Fly which
+ // contains columns, the Format should be from that one.
+ if ( !pFrame->IsLocked() && !pFrame->IsColLocked() &&
+ pFrame->Lower() && pFrame->Lower()->IsColumnFrame() )
+ pFrame->InvalidateSize();
+ }
+
+ // #i85216#
+ // if vertical position is oriented at a layout frame inside a ghost section,
+ // assure that the position is invalidated and that the information about
+ // the vertical position oriented frame is cleared
+ if ( GetVertPosOrientFrame() && GetVertPosOrientFrame()->IsLayoutFrame() )
+ {
+ const SwSectionFrame* pSectFrame( GetVertPosOrientFrame()->FindSctFrame() );
+ if ( pSectFrame && pSectFrame->GetSection() == nullptr )
+ {
+ InvalidatePos();
+ ClearVertPosOrientFrame();
+ }
+ }
+}
+
+/** Change the relative position
+ *
+ * The position will be Fix automatically and the attribute is changed accordingly.
+ */
+void SwFlyFrame::ChgRelPos( const Point &rNewPos )
+{
+ if ( GetCurrRelPos() == rNewPos )
+ return;
+
+ SwFrameFormat *pFormat = GetFormat();
+ const bool bVert = GetAnchorFrame()->IsVertical();
+ const SwTwips nNewY = bVert ? rNewPos.X() : rNewPos.Y();
+ SwTwips nTmpY = nNewY == LONG_MAX ? 0 : nNewY;
+ if( bVert )
+ nTmpY = -nTmpY;
+ SfxItemSetFixed<RES_VERT_ORIENT, RES_HORI_ORIENT> aSet( pFormat->GetDoc()->GetAttrPool() );
+
+ SwFormatVertOrient aVert( pFormat->GetVertOrient() );
+ const SwTextFrame *pAutoFrame = nullptr;
+ // #i34948# - handle also at-page and at-fly anchored
+ // Writer fly frames
+ const RndStdIds eAnchorType = GetFrameFormat().GetAnchor().GetAnchorId();
+ if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ aVert.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
+ }
+ else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ aVert.SetRelationOrient( text::RelOrientation::FRAME );
+ }
+ else if ( IsFlyAtContentFrame() || text::VertOrientation::NONE != aVert.GetVertOrient() )
+ {
+ if( text::RelOrientation::CHAR == aVert.GetRelationOrient() && IsAutoPos() )
+ {
+ if( LONG_MAX != nNewY )
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ assert(GetAnchorFrame()->IsTextFrame());
+ pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
+ TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
+ *pFormat->GetAnchor().GetContentAnchor()));
+ while( pAutoFrame->GetFollow() &&
+ pAutoFrame->GetFollow()->GetOffset() <= nOfs )
+ {
+ if( pAutoFrame == GetAnchorFrame() )
+ nTmpY += pAutoFrame->GetRelPos().Y();
+ nTmpY -= pAutoFrame->GetUpper()->getFramePrintArea().Height();
+ pAutoFrame = pAutoFrame->GetFollow();
+ }
+ nTmpY = static_cast<SwFlyAtContentFrame*>(this)->GetRelCharY(pAutoFrame)-nTmpY;
+ }
+ else
+ aVert.SetVertOrient( text::VertOrientation::CHAR_BOTTOM );
+ }
+ else
+ {
+ aVert.SetVertOrient( text::VertOrientation::NONE );
+ aVert.SetRelationOrient( text::RelOrientation::FRAME );
+ }
+ }
+ aVert.SetPos( nTmpY );
+ aSet.Put( aVert );
+
+ // For Flys in the Cnt, the horizontal orientation is of no interest,
+ // as it's always 0
+ if ( !IsFlyInContentFrame() )
+ {
+ const SwTwips nNewX = bVert ? rNewPos.Y() : rNewPos.X();
+ SwTwips nTmpX = nNewX == LONG_MAX ? 0 : nNewX;
+ SwFormatHoriOrient aHori( pFormat->GetHoriOrient() );
+ // #i34948# - handle also at-page and at-fly anchored
+ // Writer fly frames
+ if ( eAnchorType == RndStdIds::FLY_AT_PAGE )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::NONE );
+ aHori.SetRelationOrient( text::RelOrientation::PAGE_FRAME );
+ aHori.SetPosToggle( false );
+ }
+ else if ( eAnchorType == RndStdIds::FLY_AT_FLY )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::NONE );
+ aHori.SetRelationOrient( text::RelOrientation::FRAME );
+ aHori.SetPosToggle( false );
+ }
+ else if ( IsFlyAtContentFrame() || text::HoriOrientation::NONE != aHori.GetHoriOrient() )
+ {
+ aHori.SetHoriOrient( text::HoriOrientation::NONE );
+ if( text::RelOrientation::CHAR == aHori.GetRelationOrient() && IsAutoPos() )
+ {
+ if( LONG_MAX != nNewX )
+ {
+ if( !pAutoFrame )
+ {
+ assert(GetAnchorFrame()->IsTextFrame());
+ pAutoFrame = static_cast<const SwTextFrame*>(GetAnchorFrame());
+ TextFrameIndex const nOfs(pAutoFrame->MapModelToViewPos(
+ *pFormat->GetAnchor().GetContentAnchor()));
+ while( pAutoFrame->GetFollow() &&
+ pAutoFrame->GetFollow()->GetOffset() <= nOfs )
+ pAutoFrame = pAutoFrame->GetFollow();
+ }
+ nTmpX -= static_cast<SwFlyAtContentFrame*>(this)->GetRelCharX(pAutoFrame);
+ }
+ }
+ else
+ aHori.SetRelationOrient( text::RelOrientation::FRAME );
+ aHori.SetPosToggle( false );
+ }
+ aHori.SetPos( nTmpX );
+ aSet.Put( aHori );
+ }
+ SetCurrRelPos( rNewPos );
+ pFormat->GetDoc()->SetAttr( aSet, *pFormat );
+
+}
+
+/** "Formats" the Frame; Frame and PrtArea.
+ *
+ * The FixSize is not inserted here.
+ */
+void SwFlyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "FlyFrame::Format, pAttrs is 0." );
+
+ ColLock();
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ if ( getFrameArea().Top() == FAR_AWAY && getFrameArea().Left() == FAR_AWAY )
+ {
+ // Remove safety switch (see SwFrame::CTor)
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(0);
+ aFrm.Pos().setY(0);
+ }
+
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ }
+
+ // Check column width and set it if needed
+ if ( Lower() && Lower()->IsColumnFrame() )
+ AdjustColumns( nullptr, false );
+
+ setFrameAreaSizeValid(true);
+
+ const SwTwips nUL = pAttrs->CalcTopLine() + pAttrs->CalcBottomLine();
+ const SwTwips nLR = pAttrs->CalcLeftLine() + pAttrs->CalcRightLine();
+ const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
+ Size aRelSize( CalcRel( rFrameSz ) );
+
+ OSL_ENSURE( pAttrs->GetSize().Height() != 0 || rFrameSz.GetHeightPercent(), "FrameAttr height is 0." );
+ OSL_ENSURE( pAttrs->GetSize().Width() != 0 || rFrameSz.GetWidthPercent(), "FrameAttr width is 0." );
+
+ SwRectFnSet aRectFnSet(this);
+ if( !HasFixSize() )
+ {
+ tools::Long nMinHeight = 0;
+ if( IsMinHeight() )
+ nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
+
+ SwTwips nRemaining = CalcContentHeight(pAttrs, nMinHeight, nUL);
+ if( IsMinHeight() && (nRemaining + nUL) < nMinHeight )
+ nRemaining = nMinHeight - nUL;
+ // Because the Grow/Shrink of the Flys does not directly
+ // set the size - only indirectly by triggering a Format()
+ // via Invalidate() - the sizes need to be set here.
+ // Notification is running along already.
+ // As we already got a lot of zeros per attribute, we block them
+ // from now on.
+
+ if ( nRemaining < MINFLY )
+ nRemaining = MINFLY;
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nRemaining );
+ }
+
+ nRemaining -= aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nRemaining + nUL );
+ }
+
+ // #i68520#
+ if ( nRemaining + nUL != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+
+ setFrameAreaSizeValid(true);
+
+ if (SwFrameFormat* pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT))
+ {
+ // This fly is a textbox of a draw shape.
+ SdrObject* pShape = pShapeFormat->FindSdrObject();
+ if (SdrObjCustomShape* pCustomShape = dynamic_cast<SdrObjCustomShape*>( pShape) )
+ {
+ // The shape is a customshape: then inform it about the calculated fly size.
+ Size aSize(getFrameArea().Width(), getFrameArea().Height());
+ pCustomShape->SuggestTextFrameSize(aSize);
+ // Do the calculations normally done after touching editeng text of the shape.
+ pCustomShape->NbcSetOutlinerParaObjectForText(std::nullopt, nullptr);
+ }
+ }
+ }
+ else
+ {
+ // Fixed Frames do not Format itself
+ setFrameAreaSizeValid(true);
+
+ // Flys set their size using the attr
+ SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
+ nNewSize -= nUL;
+ if( nNewSize < MINFLY )
+ nNewSize = MINFLY;
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nNewSize );
+ }
+
+ nNewSize += nUL - aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nNewSize );
+ }
+
+ // #i68520#
+ if ( nNewSize != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+ }
+
+ if ( !m_bFormatHeightOnly )
+ {
+ OSL_ENSURE( aRelSize == CalcRel( rFrameSz ), "SwFlyFrame::Format CalcRel problem" );
+ SwTwips nNewSize = aRectFnSet.IsVert() ? aRelSize.Height() : aRelSize.Width();
+
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ // #i9046# Autowidth for fly frames
+ const SwTwips nAutoWidth = lcl_CalcAutoWidth( *this );
+ if ( nAutoWidth )
+ {
+ if( SwFrameSize::Minimum == rFrameSz.GetWidthSizeType() )
+ nNewSize = std::max( nNewSize - nLR, nAutoWidth );
+ else
+ nNewSize = nAutoWidth;
+ }
+ }
+ else
+ nNewSize -= nLR;
+
+ if( nNewSize < MINFLY )
+ nNewSize = MINFLY;
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, nNewSize );
+ }
+
+ nNewSize += nLR - aRectFnSet.GetWidth(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddRight( aFrm, nNewSize );
+ }
+
+ // #i68520#
+ if ( nNewSize != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+ }
+ }
+ ColUnlock();
+}
+
+// #i11760# - change parameter <bNoColl>: type <bool>;
+// add new parameter <bNoCalcFollow> with
+// new parameter <bNoCalcFollow> was used by method
+// <FormatWidthCols(..)> to avoid follow formatting
+// for text frames. But, unformatted follows causes
+// problems in method <SwContentFrame::WouldFit_(..)>,
+// which assumes that the follows are formatted.
+// Thus, <bNoCalcFollow> no longer used by <FormatWidthCols(..)>.
+void CalcContent( SwLayoutFrame *pLay, bool bNoColl )
+{
+ vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
+ SwSectionFrame* pSect;
+ bool bCollect = false;
+ if( pLay->IsSctFrame() )
+ {
+ pSect = static_cast<SwSectionFrame*>(pLay);
+ if( pSect->IsEndnAtEnd() && !bNoColl )
+ {
+ bCollect = true;
+ SwLayouter::CollectEndnotes( pLay->GetFormat()->GetDoc(), pSect );
+ }
+ pSect->CalcFootnoteContent();
+ }
+ else
+ pSect = nullptr;
+ SwFrame *pFrame = pLay->ContainsAny();
+ if ( !pFrame )
+ {
+ if( pSect )
+ {
+ if( pSect->HasFollow() )
+ pFrame = pSect->GetFollow()->ContainsAny();
+ if( !pFrame )
+ {
+ if( pSect->IsEndnAtEnd() )
+ {
+ if( bCollect )
+ pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->
+ InsertEndnotes( pSect );
+ bool bLock = pSect->IsFootnoteLock();
+ pSect->SetFootnoteLock( true );
+ pSect->CalcFootnoteContent();
+ pSect->CalcFootnoteContent();
+ pSect->SetFootnoteLock( bLock );
+ }
+ return;
+ }
+ pFrame->InvalidatePos_();
+ }
+ else
+ return;
+ }
+ pFrame->InvalidatePage();
+
+ do
+ {
+ // local variables to avoid loops caused by anchored object positioning
+ SwAnchoredObject* pAgainObj1 = nullptr;
+ SwAnchoredObject* pAgainObj2 = nullptr;
+
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns = 0;
+ // tdf#152106 loop control for multi-column sections
+ int nLoopControlRunsInMultiCol = 0;
+ const int nLoopControlMax = 20;
+ const SwFrame* pLoopControlCond = nullptr;
+
+ SwFrame* pLast;
+ do
+ {
+ pLast = pFrame;
+ bool const wasFrameLowerOfLay(pLay->IsAnLower(pFrame));
+ if( pFrame->IsVertical() ?
+ ( pFrame->GetUpper()->getFramePrintArea().Height() != pFrame->getFrameArea().Height() )
+ : ( pFrame->GetUpper()->getFramePrintArea().Width() != pFrame->getFrameArea().Width() ) )
+ {
+ pFrame->Prepare( PrepareHint::FixSizeChanged );
+ pFrame->InvalidateSize_();
+ }
+
+ if ( pFrame->IsTabFrame() )
+ {
+ static_cast<SwTabFrame*>(pFrame)->m_bCalcLowers = true;
+ // #i18103# - lock move backward of follow table,
+ // if no section content is formatted or follow table belongs
+ // to the section, which content is formatted.
+ if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() &&
+ ( !pSect || pSect == pFrame->FindSctFrame() ) )
+ {
+ static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = true;
+ }
+ }
+
+ {
+ SwFrameDeleteGuard aDeletePageGuard(pSect ? pSect->FindPageFrame() : nullptr);
+ SwFrameDeleteGuard aDeleteGuard(pSect);
+ pFrame->Calc(pRenderContext);
+ }
+
+ // #i11760# - reset control flag for follow format.
+ if ( pFrame->IsTextFrame() )
+ {
+ static_cast<SwTextFrame*>(pFrame)->AllowFollowFormat();
+ }
+
+ // The keep-attribute can cause the position
+ // of the prev to be invalid:
+ // Do not consider invalid previous frame
+ // due to its keep-attribute, if current frame is a follow or is locked.
+ // #i44049# - do not consider invalid previous
+ // frame due to its keep-attribute, if it can't move forward.
+ // #i57765# - do not consider invalid previous
+ // frame, if current frame has a column/page break before attribute.
+ SwFrame* pTmpPrev = pFrame->FindPrev();
+ SwFlowFrame* pTmpPrevFlowFrame = pTmpPrev && pTmpPrev->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pTmpPrev) : nullptr;
+ SwFlowFrame* pTmpFlowFrame = pFrame->IsFlowFrame() ? SwFlowFrame::CastFlowFrame(pFrame) : nullptr;
+
+ bool bPrevInvalid = pTmpPrevFlowFrame && pTmpFlowFrame &&
+ !pTmpFlowFrame->IsFollow() &&
+ !StackHack::IsLocked() && // #i76382#
+ !pTmpFlowFrame->IsJoinLocked() &&
+ !pTmpPrev->isFrameAreaPositionValid() &&
+ pLay->IsAnLower( pTmpPrev ) &&
+ pTmpPrevFlowFrame->IsKeep(pTmpPrev->GetAttrSet()->GetKeep(), pTmpPrev->GetBreakItem()) &&
+ pTmpPrevFlowFrame->IsKeepFwdMoveAllowed();
+
+ // format floating screen objects anchored to the frame.
+ if ( !bPrevInvalid && pFrame->GetDrawObjs() && pLay->IsAnLower( pFrame ) )
+ {
+ bool bAgain = false;
+ bool bRestartLayoutProcess = false;
+ size_t nCnt = pFrame->GetDrawObjs()->size();
+ size_t i = 0;
+ while ( i < nCnt )
+ {
+ // pFrame can move to a different page in FormatObj()
+ SwPageFrame *const pPageFrame = pFrame->FindPageFrame();
+
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ assert(pAnchoredObj);
+
+ // determine if anchored object has to be
+ // formatted and, in case, format it
+ if ( !pAnchoredObj->PositionLocked() && pAnchoredObj->IsFormatPossible() )
+ {
+ // #i43737# - no invalidation of
+ // anchored object needed - causes loops for as-character
+ // anchored objects.
+ //pAnchoredObj->InvalidateObjPos();
+ SwRect aRect( pAnchoredObj->GetObjRect() );
+ if ( !SwObjectFormatter::FormatObj( *pAnchoredObj, pFrame, pPageFrame ) )
+ {
+ bRestartLayoutProcess = true;
+ break;
+ }
+ // #i3317# - restart layout process,
+ // if the position of the anchored object is locked now.
+ if ( pAnchoredObj->PositionLocked() )
+ {
+ bRestartLayoutProcess = true;
+ break;
+ }
+
+ if ( aRect != pAnchoredObj->GetObjRect() )
+ {
+ bAgain = true;
+ if ( pAgainObj2 == pAnchoredObj )
+ {
+ OSL_FAIL( "::CalcContent(..) - loop detected, perform attribute changes to avoid the loop" );
+ // Prevent oscillation
+ SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ SwFormatSurround aAttr( rFormat.GetSurround() );
+ if( css::text::WrapTextMode_THROUGH != aAttr.GetSurround() )
+ {
+ // When on auto position, we can only set it to
+ // flow through
+ if ((rFormat.GetAnchor().GetAnchorId() ==
+ RndStdIds::FLY_AT_CHAR) &&
+ (css::text::WrapTextMode_PARALLEL ==
+ aAttr.GetSurround()))
+ {
+ aAttr.SetSurround( css::text::WrapTextMode_THROUGH );
+ }
+ else
+ {
+ aAttr.SetSurround( css::text::WrapTextMode_PARALLEL );
+ }
+ rFormat.LockModify();
+ rFormat.SetFormatAttr( aAttr );
+ rFormat.UnlockModify();
+ }
+ }
+ else
+ {
+ if ( pAgainObj1 == pAnchoredObj )
+ pAgainObj2 = pAnchoredObj;
+ pAgainObj1 = pAnchoredObj;
+ }
+ }
+
+ if ( !pFrame->GetDrawObjs() )
+ break;
+ if ( pFrame->GetDrawObjs()->size() < nCnt )
+ {
+ --nCnt;
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+
+ // #i28701# - restart layout process, if
+ // requested by floating screen object formatting
+ if (bRestartLayoutProcess
+ // tdf#152106 loop control in multi-column sections to avoid of freezing
+ && nLoopControlRunsInMultiCol < nLoopControlMax
+ // tdf#142080 if it was already on next page, and still is,
+ // ignore restart, as restart could cause infinite loop
+ && (wasFrameLowerOfLay || pLay->IsAnLower(pFrame)))
+ {
+ bool bIsMultiColumn = pSect && pSect->GetSection() && pSect->Lower() &&
+ pSect->Lower()->IsColumnFrame() && pSect->Lower()->GetNext();
+ if ( bIsMultiColumn )
+ ++nLoopControlRunsInMultiCol;
+ pFrame = pLay->ContainsAny();
+ pAgainObj1 = nullptr;
+ pAgainObj2 = nullptr;
+ continue;
+ }
+
+ // #i28701# - format anchor frame after its objects
+ // are formatted, if the wrapping style influence has to be considered.
+ if ( pLay->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ pFrame->Calc(pRenderContext);
+ }
+
+ if ( bAgain )
+ {
+ pFrame = pLay->ContainsContent();
+ if ( pFrame && pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+ if( pFrame && pFrame->IsInSct() )
+ {
+ SwSectionFrame* pTmp = pFrame->FindSctFrame();
+ if( pTmp != pLay && pLay->IsAnLower( pTmp ) )
+ pFrame = pTmp;
+ }
+
+ if ( pFrame == pLoopControlCond )
+ ++nLoopControlRuns;
+ else
+ {
+ nLoopControlRuns = 0;
+ pLoopControlCond = pFrame;
+ }
+
+ if ( nLoopControlRuns < nLoopControlMax )
+ continue;
+
+ OSL_FAIL( "LoopControl in CalcContent" );
+ }
+ }
+ if ( pFrame->IsTabFrame() )
+ {
+ if ( static_cast<SwTabFrame*>(pFrame)->IsFollow() )
+ static_cast<SwTabFrame*>(pFrame)->m_bLockBackMove = false;
+ }
+
+ pFrame = bPrevInvalid ? pTmpPrev : pFrame->FindNext();
+ if( !bPrevInvalid && pFrame && pFrame->IsSctFrame() && pSect )
+ {
+ // Empty SectionFrames could be present here
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ pFrame = pFrame->FindNext();
+
+ // If FindNext returns the Follow of the original Area, we want to
+ // continue with this content as long as it flows back.
+ if( pFrame && pFrame->IsSctFrame() && ( pFrame == pSect->GetFollow() ||
+ static_cast<SwSectionFrame*>(pFrame)->IsAnFollow( pSect ) ) )
+ {
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->InvalidatePos_();
+ }
+ }
+ // Stay in the pLay.
+ // Except for SectionFrames with Follow: the first ContentFrame of the
+ // Follow will be formatted, so that it gets a chance to move back
+ // into the pLay. Continue as long as these Frames land in pLay.
+ } while ( pFrame &&
+ ( pLay->IsAnLower( pFrame ) ||
+ ( pSect &&
+ ( ( pSect->HasFollow() &&
+ ( pLay->IsAnLower( pLast ) ||
+ ( pLast->IsInSct() &&
+ pLast->FindSctFrame()->IsAnFollow(pSect) ) ) &&
+ pSect->GetFollow()->IsAnLower( pFrame ) ) ||
+ ( pFrame->IsInSct() &&
+ pFrame->FindSctFrame()->IsAnFollow( pSect ) ) ) ) ) );
+ if( pSect )
+ {
+ if( bCollect )
+ {
+ pLay->GetFormat()->GetDoc()->getIDocumentLayoutAccess().GetLayouter()->InsertEndnotes(pSect);
+ pSect->CalcFootnoteContent();
+ }
+ if( pSect->HasFollow() )
+ {
+ SwSectionFrame* pNxt = pSect->GetFollow();
+ while( pNxt && !pNxt->ContainsContent() )
+ pNxt = pNxt->GetFollow();
+ if( pNxt )
+ pNxt->CalcFootnoteContent();
+ }
+ if( bCollect )
+ {
+ pFrame = pLay->ContainsAny();
+ bCollect = false;
+ if( pFrame )
+ continue;
+ }
+ }
+ break;
+ }
+ while( true );
+}
+
+void SwFlyFrame::MakeObjPos()
+{
+ if ( isFrameAreaPositionValid() )
+ return;
+
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ setFrameAreaPositionValid(true);
+
+ // use new class to position object
+ GetAnchorFrame()->Calc(pRenderContext);
+ objectpositioning::SwToLayoutAnchoredObjectPosition
+ aObjPositioning( *GetVirtDrawObj() );
+ aObjPositioning.CalcPosition();
+
+ // #i58280#
+ // update relative position
+ SetCurrRelPos( aObjPositioning.GetRelPos() );
+
+ {
+ SwRectFnSet aRectFnSet(GetAnchorFrame());
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos( aObjPositioning.GetRelPos() );
+ aFrm.Pos() += aRectFnSet.GetPos(GetAnchorFrame()->getFrameArea());
+ }
+
+ // #i69335#
+ InvalidateObjRectWithSpaces();
+}
+
+void SwFlyFrame::MakePrtArea( const SwBorderAttrs &rAttrs )
+{
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+
+ // consider vertical layout
+ SwRectFnSet aRectFnSet(this);
+ aRectFnSet.SetXMargins( *this, rAttrs.CalcLeftLine(),
+ rAttrs.CalcRightLine() );
+ aRectFnSet.SetYMargins( *this, rAttrs.CalcTopLine(),
+ rAttrs.CalcBottomLine() );
+ }
+}
+
+void SwFlyFrame::MakeContentPos( const SwBorderAttrs &rAttrs )
+{
+ if ( m_bValidContentPos )
+ return;
+
+ m_bValidContentPos = true;
+
+ const SwTwips nUL = rAttrs.CalcTopLine() + rAttrs.CalcBottomLine();
+ Size aRelSize( CalcRel( GetFormat()->GetFrameSize() ) );
+
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nMinHeight = 0;
+ if( IsMinHeight() )
+ nMinHeight = aRectFnSet.IsVert() ? aRelSize.Width() : aRelSize.Height();
+
+ Point aNewContentPos = getFramePrintArea().Pos();
+ const SdrTextVertAdjust nAdjust = GetFormat()->GetTextVertAdjust().GetValue();
+
+ if( nAdjust != SDRTEXTVERTADJUST_TOP )
+ {
+ const SwTwips nContentHeight = CalcContentHeight(&rAttrs, nMinHeight, nUL);
+ SwTwips nDiff = 0;
+
+ if( nContentHeight != 0)
+ nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nContentHeight;
+
+ if( nDiff > 0 )
+ {
+ if( nAdjust == SDRTEXTVERTADJUST_CENTER )
+ {
+ if( aRectFnSet.IsVertL2R() )
+ aNewContentPos.setX(aNewContentPos.getX() + nDiff/2);
+ else if( aRectFnSet.IsVert() )
+ aNewContentPos.setX(aNewContentPos.getX() - nDiff/2);
+ else
+ aNewContentPos.setY(aNewContentPos.getY() + nDiff/2);
+ }
+ else if( nAdjust == SDRTEXTVERTADJUST_BOTTOM )
+ {
+ if( aRectFnSet.IsVertL2R() )
+ aNewContentPos.setX(aNewContentPos.getX() + nDiff);
+ else if( aRectFnSet.IsVert() )
+ aNewContentPos.setX(aNewContentPos.getX() - nDiff);
+ else
+ aNewContentPos.setY(aNewContentPos.getY() + nDiff);
+ }
+ }
+ }
+ if( aNewContentPos != ContentPos() )
+ {
+ ContentPos() = aNewContentPos;
+ for( SwFrame *pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ pFrame->InvalidatePos();
+ }
+ }
+
+}
+
+void SwFlyFrame::InvalidateContentPos()
+{
+ m_bValidContentPos = false;
+ Invalidate_();
+}
+
+void SwFlyFrame::SelectionHasChanged(SwFEShell* pShell)
+{
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >(pShell);
+ if (pWrtSh == nullptr)
+ return;
+
+ UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
+}
+
+bool SwFlyFrame::IsShowUnfloatButton(SwWrtShell* pWrtSh) const
+{
+ if (pWrtSh == nullptr)
+ return false;
+
+ // In read only mode we don't allow unfloat operation
+ if (pWrtSh->GetViewOptions()->IsReadonly())
+ return false;
+
+ const SdrObject *pObj = GetFrameFormat().FindRealSdrObject();
+ if (pObj == nullptr)
+ return false;
+
+ // SwFlyFrame itself can mean images, ole objects, etc, but we interested in actual text frames
+ if (SwFEShell::GetObjCntType(*pObj) != OBJCNT_FLY)
+ return false;
+
+ // We show the button only for the selected text frame
+ SwDrawView *pView = pWrtSh->Imp()->GetDrawView();
+ if (pView == nullptr)
+ return false;
+
+ // Fly frame can be selected only alone
+ if (pView->GetMarkedObjectList().GetMarkCount() != 1)
+ return false;
+
+ if(!pView->IsObjMarked(pObj))
+ return false;
+
+ // A frame is a floating table if there is only one table (and maybe some whitespaces) inside it
+ int nTableCount = 0;
+ const SwFrame* pLower = GetLower();
+ const SwTabFrame* pTable = nullptr;
+ while (pLower)
+ {
+ if (pLower->IsTabFrame())
+ {
+ pTable = static_cast<const SwTabFrame*>(pLower);
+ ++nTableCount;
+ if (nTableCount > 1 || pTable == nullptr)
+ return false;
+ }
+
+ if (pLower->IsTextFrame())
+ {
+ const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pLower);
+ if (!pTextFrame->GetText().trim().isEmpty())
+ return false;
+ }
+ pLower = pLower->GetNext();
+ }
+
+ if (nTableCount != 1 || pTable == nullptr)
+ return false;
+
+ // Show the unfold button only for multipage tables
+ const SwBodyFrame *pBody = GetAnchorFrame()->FindBodyFrame();
+ if (pBody == nullptr)
+ return false;
+
+ tools::Long nBodyHeight = pBody->getFrameArea().Height();
+ tools::Long nTableHeight = pTable->getFrameArea().Height();
+ tools::Long nFrameOffset = std::abs(GetAnchorFrame()->getFrameArea().Top() - pBody->getFrameArea().Top());
+
+ return nBodyHeight < nTableHeight + nFrameOffset;
+}
+
+void SwFlyFrame::ActiveUnfloatButton(SwWrtShell* pWrtSh)
+{
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ SwFrameControlPtr pControl = rMngr.GetControl(FrameControlType::FloatingTable, this);
+ if (pControl && pControl->GetWindow())
+ {
+ pControl->GetWindow()->MouseButtonDown(MouseEvent());
+ }
+}
+
+void SwFlyFrame::UpdateUnfloatButton(SwWrtShell* pWrtSh, bool bShow) const
+{
+ if (pWrtSh == nullptr)
+ return;
+
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ Point aTopRightPixel = rEditWin.LogicToPixel( getFrameArea().TopRight() );
+ rMngr.SetUnfloatTableButton(this, bShow, aTopRightPixel);
+}
+
+SwTwips SwFlyFrame::Grow_( SwTwips nDist, bool bTst )
+{
+ SwRectFnSet aRectFnSet(this);
+ if ( Lower() && !IsColLocked() && !HasFixSize() )
+ {
+ SwTwips nSize = aRectFnSet.GetHeight(getFrameArea());
+ if( nSize > 0 && nDist > ( LONG_MAX - nSize ) )
+ nDist = LONG_MAX - nSize;
+
+ if ( nDist <= 0 )
+ return 0;
+
+ if ( Lower()->IsColumnFrame() )
+ { // If it's a Column Frame, the Format takes control of the
+ // resizing (due to the adjustment).
+ if ( !bTst )
+ {
+ // #i28701# - unlock position of Writer fly frame
+ UnlockPosition();
+ InvalidatePos_();
+ InvalidateSize();
+ }
+ return 0;
+ }
+
+ if ( !bTst )
+ {
+ const SwRect aOld( GetObjRectWithSpaces() );
+ InvalidateSize_();
+ const bool bOldLock = m_bLocked;
+ Unlock();
+ if ( IsFlyFreeFrame() )
+ {
+ // #i37068# - no format of position here
+ // and prevent move in method <CheckClip(..)>.
+ // This is needed to prevent layout loop caused by nested
+ // Writer fly frames - inner Writer fly frames format its
+ // anchor, which grows/shrinks the outer Writer fly frame.
+ // Note: position will be invalidated below.
+ setFrameAreaPositionValid(true);
+
+ // #i55416#
+ // Suppress format of width for autowidth frame, because the
+ // format of the width would call <SwTextFrame::CalcFitToContent()>
+ // for the lower frame, which initiated this grow.
+ const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
+ const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = true;
+ }
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if (pSh)
+ {
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
+ static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(pSh->GetOut());
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
+ }
+ // #i55416#
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = bOldFormatHeightOnly;
+ }
+ }
+ else
+ MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ InvalidateSize_();
+ InvalidatePos();
+ if ( bOldLock )
+ Lock();
+ const SwRect aNew( GetObjRectWithSpaces() );
+ if ( aOld != aNew )
+ ::Notify( this, FindPageFrame(), aOld );
+ return aRectFnSet.GetHeight(aNew)-aRectFnSet.GetHeight(aOld);
+ }
+ return nDist;
+ }
+ return 0;
+}
+
+SwTwips SwFlyFrame::Shrink_( SwTwips nDist, bool bTst )
+{
+ if( Lower() && !IsColLocked() && !HasFixSize() )
+ {
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nDist > nHeight )
+ nDist = nHeight;
+
+ SwTwips nVal = nDist;
+ if ( IsMinHeight() )
+ {
+ const SwFormatFrameSize& rFormatSize = GetFormat()->GetFrameSize();
+ SwTwips nFormatHeight = aRectFnSet.IsVert() ? rFormatSize.GetWidth() : rFormatSize.GetHeight();
+
+ nVal = std::min( nDist, nHeight - nFormatHeight );
+ }
+
+ if ( nVal <= 0 )
+ return 0;
+
+ if ( Lower()->IsColumnFrame() )
+ { // If it's a Column Frame, the Format takes control of the
+ // resizing (due to the adjustment).
+ if ( !bTst )
+ {
+ SwRect aOld( GetObjRectWithSpaces() );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nHeight - nVal );
+ }
+
+ // #i68520#
+ if ( nHeight - nVal != 0 )
+ {
+ InvalidateObjRectWithSpaces();
+ }
+
+ nHeight = aRectFnSet.GetHeight(getFramePrintArea());
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nHeight - nVal );
+ }
+
+ InvalidatePos_();
+ InvalidateSize();
+ ::Notify( this, FindPageFrame(), aOld );
+ NotifyDrawObj();
+ if ( GetAnchorFrame()->IsInFly() )
+ AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
+ }
+ return 0;
+ }
+
+ if ( !bTst )
+ {
+ const SwRect aOld( GetObjRectWithSpaces() );
+ InvalidateSize_();
+ const bool bOldLocked = m_bLocked;
+ Unlock();
+ if ( IsFlyFreeFrame() )
+ {
+ // #i37068# - no format of position here
+ // and prevent move in method <CheckClip(..)>.
+ // This is needed to prevent layout loop caused by nested
+ // Writer fly frames - inner Writer fly frames format its
+ // anchor, which grows/shrinks the outer Writer fly frame.
+ // Note: position will be invalidated below.
+ setFrameAreaPositionValid(true);
+
+ // #i55416#
+ // Suppress format of width for autowidth frame, because the
+ // format of the width would call <SwTextFrame::CalcFitToContent()>
+ // for the lower frame, which initiated this shrink.
+ const bool bOldFormatHeightOnly = m_bFormatHeightOnly;
+ const SwFormatFrameSize& rFrameSz = GetFormat()->GetFrameSize();
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = true;
+ }
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( true );
+ static_cast<SwFlyFreeFrame*>(this)->SwFlyFreeFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ static_cast<SwFlyFreeFrame*>(this)->SetNoMoveOnCheckClip( false );
+ // #i55416#
+ if ( rFrameSz.GetWidthSizeType() != SwFrameSize::Fixed )
+ {
+ m_bFormatHeightOnly = bOldFormatHeightOnly;
+ }
+ }
+ else
+ MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+ InvalidateSize_();
+ InvalidatePos();
+ if ( bOldLocked )
+ Lock();
+ const SwRect aNew( GetObjRectWithSpaces() );
+ if ( aOld != aNew )
+ {
+ ::Notify( this, FindPageFrame(), aOld );
+ if ( GetAnchorFrame()->IsInFly() )
+ AnchorFrame()->FindFlyFrame()->Shrink( nDist, bTst );
+ }
+ return aRectFnSet.GetHeight(aOld) -
+ aRectFnSet.GetHeight(aNew);
+ }
+ return nVal;
+ }
+ return 0;
+}
+
+Size SwFlyFrame::ChgSize( const Size& aNewSize )
+{
+ // #i53298#
+ // If the fly frame anchored at-paragraph or at-character contains an OLE
+ // object, assure that the new size fits into the current clipping area
+ // of the fly frame
+ Size aAdjustedNewSize( aNewSize );
+ {
+ if ( dynamic_cast<SwFlyAtContentFrame*>(this) &&
+ Lower() && dynamic_cast<SwNoTextFrame*>(Lower()) &&
+ static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() )
+ {
+ SwRect aClipRect;
+ ::CalcClipRect( GetVirtDrawObj(), aClipRect, false );
+ if ( aAdjustedNewSize.Width() > aClipRect.Width() )
+ {
+ aAdjustedNewSize.setWidth( aClipRect.Width() );
+ }
+ if ( aAdjustedNewSize.Height() > aClipRect.Height() )
+ {
+ aAdjustedNewSize.setWidth( aClipRect.Height() );
+ }
+ }
+ }
+
+ if ( aAdjustedNewSize != getFrameArea().SSize() )
+ {
+ SwFrameFormat *pFormat = GetFormat();
+ SwFormatFrameSize aSz( pFormat->GetFrameSize() );
+ aSz.SetWidth( aAdjustedNewSize.Width() );
+ aSz.SetHeight( aAdjustedNewSize.Height() );
+ // go via the Doc for UNDO
+ pFormat->GetDoc()->SetAttr( aSz, *pFormat );
+ return aSz.GetSize();
+ }
+ else
+ return getFrameArea().SSize();
+}
+
+bool SwFlyFrame::IsLowerOf( const SwLayoutFrame* pUpperFrame ) const
+{
+ OSL_ENSURE( GetAnchorFrame(), "8-( Fly is lost in Space." );
+ const SwFrame* pFrame = GetAnchorFrame();
+ do
+ {
+ if ( pFrame == pUpperFrame )
+ return true;
+ pFrame = pFrame->IsFlyFrame()
+ ? static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame()
+ : pFrame->GetUpper();
+ } while ( pFrame );
+ return false;
+}
+
+void SwFlyFrame::Cut()
+{
+}
+
+void SwFrame::AppendFly( SwFlyFrame *pNew )
+{
+ if (!m_pDrawObjs)
+ {
+ m_pDrawObjs.reset(new SwSortedObjs());
+ }
+ m_pDrawObjs->Insert( *pNew );
+ pNew->ChgAnchorFrame( this );
+
+ // Register at the page
+ // If there's none present, register via SwPageFrame::PreparePage
+ SwPageFrame* pPage = FindPageFrame();
+ if ( pPage != nullptr )
+ {
+ pPage->AppendFlyToPage( pNew );
+ }
+}
+
+void SwFrame::RemoveFly( SwFlyFrame *pToRemove )
+{
+ // Deregister from the page
+ // Could already have happened, if the page was already destructed
+ SwPageFrame *pPage = pToRemove->FindPageFrame();
+ if ( pPage && pPage->GetSortedObjs() )
+ {
+ pPage->RemoveFlyFromPage( pToRemove );
+ }
+ // #i73201#
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ else
+ {
+ if ( pToRemove->IsAccessibleFrame() &&
+ pToRemove->GetFormat() &&
+ !pToRemove->IsFlyInContentFrame() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell *pVSh = pRootFrame->GetCurrShell();
+ if( pVSh && pVSh->Imp() )
+ {
+ pVSh->Imp()->DisposeAccessibleFrame( pToRemove );
+ }
+ }
+ }
+ }
+#endif
+
+ m_pDrawObjs->Remove(*pToRemove);
+ if (!m_pDrawObjs->size())
+ {
+ m_pDrawObjs.reset();
+ }
+
+ pToRemove->ChgAnchorFrame( nullptr );
+
+ if ( !pToRemove->IsFlyInContentFrame() && GetUpper() && IsInTab() )//MA_FLY_HEIGHT
+ GetUpper()->InvalidateSize();
+}
+
+void SwFrame::AppendDrawObj( SwAnchoredObject& _rNewObj )
+{
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+
+ if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rNewObj) == nullptr )
+ {
+ OSL_FAIL( "SwFrame::AppendDrawObj(..) - anchored object of unexpected type -> object not appended" );
+ return;
+ }
+
+ if ( dynamic_cast<const SwDrawVirtObj*>(_rNewObj.GetDrawObj()) == nullptr &&
+ _rNewObj.GetAnchorFrame() && _rNewObj.GetAnchorFrame() != this )
+ {
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+ // perform disconnect from layout, if 'master' drawing object is appended
+ // to a new frame.
+ static_cast<SwDrawContact*>(::GetUserCall( _rNewObj.GetDrawObj() ))->
+ DisconnectFromLayout( false );
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+ }
+
+ if ( _rNewObj.GetAnchorFrame() != this )
+ {
+ if (!m_pDrawObjs)
+ {
+ m_pDrawObjs.reset(new SwSortedObjs());
+ }
+ m_pDrawObjs->Insert(_rNewObj);
+ _rNewObj.ChgAnchorFrame( this );
+ }
+
+ // #i113730#
+ // Assure the control objects and group objects containing controls are on the control layer
+ if ( ::CheckControlLayer( _rNewObj.DrawObj() ) )
+ {
+ const IDocumentDrawModelAccess& rIDDMA = getIDocumentDrawModelAccess();
+ const SdrLayerID aCurrentLayer(_rNewObj.DrawObj()->GetLayer());
+ const SdrLayerID aControlLayerID(rIDDMA.GetControlsId());
+ const SdrLayerID aInvisibleControlLayerID(rIDDMA.GetInvisibleControlsId());
+
+ if(aCurrentLayer != aControlLayerID && aCurrentLayer != aInvisibleControlLayerID)
+ {
+ if ( aCurrentLayer == rIDDMA.GetInvisibleHellId() ||
+ aCurrentLayer == rIDDMA.GetInvisibleHeavenId() )
+ {
+ _rNewObj.DrawObj()->SetLayer(aInvisibleControlLayerID);
+ }
+ else
+ {
+ _rNewObj.DrawObj()->SetLayer(aControlLayerID);
+ }
+ //The layer is part of the key used to sort the obj, so update
+ //its position if the layer changed.
+ m_pDrawObjs->Update(_rNewObj);
+ }
+ }
+
+ // no direct positioning needed, but invalidate the drawing object position
+ _rNewObj.InvalidateObjPos();
+
+ // register at page frame
+ SwPageFrame* pPage = FindPageFrame();
+ if ( pPage )
+ {
+ pPage->AppendDrawObjToPage( _rNewObj );
+ }
+
+ // Notify accessible layout.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if( pLayout && pLayout->IsAnyShellAccessible() )
+ {
+ pSh->Imp()->AddAccessibleObj( _rNewObj.GetDrawObj() );
+ }
+ }
+#endif
+
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+}
+
+void SwFrame::RemoveDrawObj( SwAnchoredObject& _rToRemoveObj )
+{
+ // Notify accessible layout.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if( pSh )
+ {
+ SwRootFrame* pLayout = getRootFrame();
+ if (pLayout && pLayout->IsAnyShellAccessible())
+ pSh->Imp()->DisposeAccessibleObj(_rToRemoveObj.GetDrawObj(), false);
+ }
+#endif
+
+ // deregister from page frame
+ SwPageFrame* pPage = _rToRemoveObj.GetPageFrame();
+ if ( pPage && pPage->GetSortedObjs() )
+ pPage->RemoveDrawObjFromPage( _rToRemoveObj );
+
+ m_pDrawObjs->Remove(_rToRemoveObj);
+ if (!m_pDrawObjs->size())
+ {
+ m_pDrawObjs.reset();
+ }
+ _rToRemoveObj.ChgAnchorFrame( nullptr );
+
+ assert(!m_pDrawObjs || m_pDrawObjs->is_sorted());
+}
+
+void SwFrame::InvalidateObjs( const bool _bNoInvaOfAsCharAnchoredObjs )
+{
+ if ( !GetDrawObjs() )
+ return;
+
+ // #i26945# - determine page the frame is on,
+ // in order to check, if anchored object is registered at the same
+ // page.
+ const SwPageFrame* pPageFrame = FindPageFrame();
+ // #i28701# - re-factoring
+ for (SwAnchoredObject* pAnchoredObj : *GetDrawObjs())
+ {
+ if ( _bNoInvaOfAsCharAnchoredObjs &&
+ (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ == RndStdIds::FLY_AS_CHAR) )
+ {
+ continue;
+ }
+ // #i26945# - no invalidation, if anchored object
+ // isn't registered at the same page and instead is registered at
+ // the page, where its anchor character text frame is on.
+ if ( pAnchoredObj->GetPageFrame() &&
+ pAnchoredObj->GetPageFrame() != pPageFrame )
+ {
+ SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
+ if ( pAnchorCharFrame &&
+ pAnchoredObj->GetPageFrame() == pAnchorCharFrame->FindPageFrame() )
+ {
+ continue;
+ }
+ // #115759# - unlock its position, if anchored
+ // object isn't registered at the page, where its anchor
+ // character text frame is on, respectively if it has no
+ // anchor character text frame.
+ else
+ {
+ pAnchoredObj->UnlockPosition();
+ }
+ }
+ // #i51474# - reset flag, that anchored object
+ // has cleared environment, and unlock its position, if the anchored
+ // object is registered at the same page as the anchor frame is on.
+ if ( pAnchoredObj->ClearedEnvironment() &&
+ pAnchoredObj->GetPageFrame() &&
+ pAnchoredObj->GetPageFrame() == pPageFrame )
+ {
+ pAnchoredObj->UnlockPosition();
+ pAnchoredObj->SetClearedEnvironment( false );
+ }
+ // distinguish between writer fly frames and drawing objects
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ pFly->Invalidate_();
+ pFly->InvalidatePos_();
+ }
+ else
+ {
+ pAnchoredObj->InvalidateObjPos();
+ }
+ } // end of loop on objects, which are connected to the frame
+}
+
+// #i26945# - correct check, if anchored object is a lower
+// of the layout frame. E.g., anchor character text frame can be a follow text
+// frame.
+// #i44016# - add parameter <_bUnlockPosOfObjs> to
+// force an unlockposition call for the lower objects.
+void SwLayoutFrame::NotifyLowerObjs( const bool _bUnlockPosOfObjs )
+{
+ // invalidate lower floating screen objects
+ SwPageFrame* pPageFrame = FindPageFrame();
+ if ( !(pPageFrame && pPageFrame->GetSortedObjs()) )
+ return;
+
+ SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ // #i26945# - check if anchored object is a lower
+ // of the layout frame is changed to check, if its anchor frame
+ // is a lower of the layout frame.
+ // Determine the anchor frame - usually it's the anchor frame,
+ // for at-character/as-character anchored objects the anchor character
+ // text frame is taken.
+ const SwFrame* pAnchorFrame = pObj->GetAnchorFrameContainingAnchPos();
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->getFrameArea().Left() == FAR_AWAY )
+ continue;
+
+ if ( pFly->IsAnLower( this ) )
+ continue;
+
+ // #i26945# - use <pAnchorFrame> to check, if
+ // fly frame is lower of layout frame resp. if fly frame is
+ // at a different page registered as its anchor frame is on.
+ const bool bLow = IsAnLower( pAnchorFrame );
+ if ( bLow || pAnchorFrame->FindPageFrame() != pPageFrame )
+ {
+ pFly->Invalidate_( pPageFrame );
+ if ( !bLow || pFly->IsFlyAtContentFrame() )
+ {
+ // #i44016#
+ if ( _bUnlockPosOfObjs )
+ {
+ pFly->UnlockPosition();
+ }
+ pFly->InvalidatePos_();
+ }
+ else
+ pFly->InvalidatePrt_();
+ }
+ }
+ else
+ {
+ assert( dynamic_cast<const SwAnchoredDrawObject*>( pObj) &&
+ "<SwLayoutFrame::NotifyFlys() - anchored object of unexpected type" );
+ // #i26945# - use <pAnchorFrame> to check, if
+ // fly frame is lower of layout frame resp. if fly frame is
+ // at a different page registered as its anchor frame is on.
+ if ( IsAnLower( pAnchorFrame ) ||
+ pAnchorFrame->FindPageFrame() != pPageFrame )
+ {
+ // #i44016#
+ if ( _bUnlockPosOfObjs )
+ {
+ pObj->UnlockPosition();
+ }
+ pObj->InvalidateObjPos();
+ }
+ }
+ }
+}
+
+void SwFlyFrame::NotifyDrawObj()
+{
+ SwVirtFlyDrawObj* pObj = GetVirtDrawObj();
+ pObj->SetRect();
+ pObj->SetBoundAndSnapRectsDirty();
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+
+ if ( GetFormat()->GetSurround().IsContour() )
+ {
+ ClrContourCache( pObj );
+ }
+ else if(IsFlyFreeFrame() && static_cast< const SwFlyFreeFrame* >(this)->supportsAutoContour())
+ {
+ // RotateFlyFrame3: Also need to clear when changes happen
+ // Caution: isTransformableSwFrame is already reset when resetting rotation, so
+ // *additionally* reset in SwFlyFreeFrame::MakeAll when no more rotation
+ ClrContourCache( pObj );
+ }
+}
+
+Size SwFlyFrame::CalcRel( const SwFormatFrameSize &rSz ) const
+{
+ Size aRet( rSz.GetSize() );
+
+ const SwFrame *pRel = IsFlyLayFrame() ? GetAnchorFrame() : GetAnchorFrame()->GetUpper();
+ if( pRel ) // LAYER_IMPL
+ {
+ tools::Long nRelWidth = LONG_MAX, nRelHeight = LONG_MAX;
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( ( pRel->IsBodyFrame() || pRel->IsPageFrame() ) &&
+ pSh && pSh->GetViewOptions()->getBrowseMode() &&
+ pSh->VisArea().HasArea() )
+ {
+ nRelWidth = pSh->GetBrowseWidth();
+ nRelHeight = pSh->VisArea().Height();
+ Size aBorder = pSh->GetOut()->PixelToLogic( pSh->GetBrowseBorder() );
+ nRelWidth = std::min( nRelWidth, pRel->getFramePrintArea().Width() );
+ nRelHeight -= 2*aBorder.Height();
+ nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
+ }
+
+ // At the moment only the "== PAGE_FRAME" and "!= PAGE_FRAME" cases are handled.
+ // When size is a relative to page size, ignore size of SwBodyFrame.
+ if (rSz.GetWidthPercentRelation() != text::RelOrientation::PAGE_FRAME)
+ nRelWidth = std::min( nRelWidth, pRel->getFramePrintArea().Width() );
+ else if ( pRel->IsPageFrame() )
+ nRelWidth = std::min( nRelWidth, pRel->getFrameArea().Width() );
+
+ if (rSz.GetHeightPercentRelation() != text::RelOrientation::PAGE_FRAME)
+ nRelHeight = std::min( nRelHeight, pRel->getFramePrintArea().Height() );
+ else if ( pRel->IsPageFrame() )
+ nRelHeight = std::min( nRelHeight, pRel->getFrameArea().Height() );
+
+ if( !pRel->IsPageFrame() )
+ {
+ const SwPageFrame* pPage = FindPageFrame();
+ if( pPage )
+ {
+ if (rSz.GetWidthPercentRelation() == text::RelOrientation::PAGE_FRAME)
+ // Ignore margins of pPage.
+ nRelWidth = std::min( nRelWidth, pPage->getFrameArea().Width() );
+ else
+ nRelWidth = std::min( nRelWidth, pPage->getFramePrintArea().Width() );
+ if (rSz.GetHeightPercentRelation() == text::RelOrientation::PAGE_FRAME)
+ // Ignore margins of pPage.
+ nRelHeight = std::min( nRelHeight, pPage->getFrameArea().Height() );
+ else
+ nRelHeight = std::min( nRelHeight, pPage->getFramePrintArea().Height() );
+ }
+ }
+
+ if ( rSz.GetWidthPercent() && rSz.GetWidthPercent() != SwFormatFrameSize::SYNCED )
+ aRet.setWidth( nRelWidth * rSz.GetWidthPercent() / 100 );
+ if ( rSz.GetHeightPercent() && rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED )
+ aRet.setHeight( nRelHeight * rSz.GetHeightPercent() / 100 );
+
+ if ( rSz.GetHeight() && rSz.GetWidthPercent() == SwFormatFrameSize::SYNCED )
+ {
+ aRet.setWidth( aRet.Width() * ( aRet.Height()) );
+ aRet.setWidth( aRet.Width() / ( rSz.GetHeight()) );
+ }
+ else if ( rSz.GetWidth() && rSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
+ {
+ aRet.setHeight( aRet.Height() * ( aRet.Width()) );
+ aRet.setHeight( aRet.Height() / ( rSz.GetWidth()) );
+ }
+ }
+ return aRet;
+}
+
+static SwTwips lcl_CalcAutoWidth( const SwLayoutFrame& rFrame )
+{
+ SwTwips nRet = 0;
+ SwTwips nMin = 0;
+ const SwFrame* pFrame = rFrame.Lower();
+
+ // No autowidth defined for columned frames
+ if ( !pFrame || pFrame->IsColumnFrame() )
+ return nRet;
+
+ int nParagraphCount = 0;
+ while ( pFrame )
+ {
+ nParagraphCount++;
+ if ( pFrame->IsSctFrame() )
+ {
+ nMin = lcl_CalcAutoWidth( *static_cast<const SwSectionFrame*>(pFrame) );
+ }
+ if ( pFrame->IsTextFrame() )
+ {
+ nMin = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pFrame))->CalcFitToContent();
+ const SvxLRSpaceItem &rSpace =
+ static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace();
+ if (!static_cast<const SwTextFrame*>(pFrame)->IsLocked())
+ nMin += rSpace.GetRight() + rSpace.GetTextLeft() + rSpace.GetTextFirstLineOffset();
+ }
+ else if ( pFrame->IsTabFrame() )
+ {
+ const SwFormatFrameSize& rTableFormatSz = static_cast<const SwTabFrame*>(pFrame)->GetTable()->GetFrameFormat()->GetFrameSize();
+ if ( USHRT_MAX == rTableFormatSz.GetSize().Width() ||
+ text::HoriOrientation::NONE == static_cast<const SwTabFrame*>(pFrame)->GetFormat()->GetHoriOrient().GetHoriOrient() )
+ {
+ const SwPageFrame* pPage = rFrame.FindPageFrame();
+ // auto width table
+ nMin = pFrame->GetUpper()->IsVertical() ?
+ pPage->getFramePrintArea().Height() :
+ pPage->getFramePrintArea().Width();
+ }
+ else
+ {
+ nMin = rTableFormatSz.GetSize().Width();
+ }
+ }
+
+ if ( nMin > nRet )
+ nRet = nMin;
+
+ pFrame = pFrame->GetNext();
+ }
+
+ // tdf#124423 In Microsoft compatibility mode: widen the frame to max (PrintArea of the frame it anchored to) if it contains at least 2 paragraphs,
+ // or 1 paragraph wider than its parent area.
+ if (rFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::FRAME_AUTOWIDTH_WITH_MORE_PARA))
+ {
+ const SwFrame* pFrameRect = rFrame.IsFlyFrame() ? static_cast<const SwFlyFrame*>(&rFrame)->GetAnchorFrame() : rFrame.Lower()->FindPageFrame();
+ SwTwips nParentWidth = rFrame.IsVertical() ? pFrameRect->getFramePrintArea().Height() : pFrameRect->getFramePrintArea().Width();
+ if (nParagraphCount > 1 || nRet > nParentWidth)
+ {
+ return nParentWidth;
+ }
+ }
+
+ return nRet;
+}
+
+/// #i13147# - If called for paint and the <SwNoTextFrame> contains
+/// a graphic, load of intrinsic graphic has to be avoided.
+bool SwFlyFrame::GetContour( tools::PolyPolygon& rContour,
+ const bool _bForPaint ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ bool bRet = false;
+ const bool bIsCandidate(Lower() && Lower()->IsNoTextFrame());
+
+ if(bIsCandidate)
+ {
+ if(GetFormat()->GetSurround().IsContour())
+ {
+ SwNoTextNode *pNd = const_cast<SwNoTextNode*>(static_cast<const SwNoTextNode*>(static_cast<const SwNoTextFrame*>(Lower())->GetNode()));
+ // #i13147# - determine <GraphicObject> instead of <Graphic>
+ // in order to avoid load of graphic, if <SwNoTextNode> contains a graphic
+ // node and method is called for paint.
+ std::unique_ptr<GraphicObject> xTmpGrfObj;
+ const GraphicObject* pGrfObj = nullptr;
+ const SwGrfNode* pGrfNd = pNd->GetGrfNode();
+ if ( pGrfNd && _bForPaint )
+ {
+ pGrfObj = &(pGrfNd->GetGrfObj());
+ }
+ else
+ {
+ xTmpGrfObj.reset(new GraphicObject(pNd->GetGraphic()));
+ pGrfObj = xTmpGrfObj.get();
+ }
+ assert(pGrfObj && "SwFlyFrame::GetContour() - No Graphic/GraphicObject found at <SwNoTextNode>.");
+ if (pGrfObj->GetType() != GraphicType::NONE)
+ {
+ if( !pNd->HasContour() )
+ {
+ //#i13147# - no <CreateContour> for a graphic
+ // during paint. Thus, return (value of <bRet> should be <false>).
+ if ( pGrfNd && _bForPaint )
+ {
+ OSL_FAIL( "SwFlyFrame::GetContour() - No Contour found at <SwNoTextNode> during paint." );
+ return bRet;
+ }
+ pNd->CreateContour();
+ }
+ pNd->GetContour( rContour );
+ // The Node holds the Polygon matching the original size of the graphic
+ // We need to include the scaling here
+ SwRect aClip;
+ SwRect aOrig;
+ Lower()->Calc(pRenderContext);
+ static_cast<const SwNoTextFrame*>(Lower())->GetGrfArea( aClip, &aOrig );
+ // #i13147# - copy method code <SvxContourDlg::ScaleContour(..)>
+ // in order to avoid that graphic has to be loaded for contour scale.
+ //SvxContourDlg::ScaleContour( rContour, aGrf, MapUnit::MapTwip, aOrig.SSize() );
+ {
+ OutputDevice* pOutDev = Application::GetDefaultDevice();
+ const MapMode aDispMap( MapUnit::MapTwip );
+ const MapMode aGrfMap( pGrfObj->GetPrefMapMode() );
+ const Size aGrfSize( pGrfObj->GetPrefSize() );
+ Size aOrgSize;
+ Point aNewPoint;
+ bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+
+ if ( bPixelMap )
+ aOrgSize = pOutDev->PixelToLogic( aGrfSize, aDispMap );
+ else
+ aOrgSize = OutputDevice::LogicToLogic( aGrfSize, aGrfMap, aDispMap );
+
+ if ( aOrgSize.Width() && aOrgSize.Height() )
+ {
+ double fScaleX = static_cast<double>(aOrig.Width()) / aOrgSize.Width();
+ double fScaleY = static_cast<double>(aOrig.Height()) / aOrgSize.Height();
+
+ for ( sal_uInt16 j = 0, nPolyCount = rContour.Count(); j < nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = rContour[ j ];
+
+ for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ )
+ {
+ if ( bPixelMap )
+ aNewPoint = pOutDev->PixelToLogic( rPoly[ i ], aDispMap );
+ else
+ aNewPoint = OutputDevice::LogicToLogic( rPoly[ i ], aGrfMap, aDispMap );
+
+ rPoly[ i ] = Point( FRound( aNewPoint.getX() * fScaleX ), FRound( aNewPoint.getY() * fScaleY ) );
+ }
+ }
+ }
+ }
+ // destroy created <GraphicObject>.
+ xTmpGrfObj.reset();
+ rContour.Move( aOrig.Left(), aOrig.Top() );
+ if( !aClip.Width() )
+ aClip.Width( 1 );
+ if( !aClip.Height() )
+ aClip.Height( 1 );
+ rContour.Clip( aClip.SVRect() );
+ rContour.Optimize(PolyOptimizeFlags::CLOSE);
+ bRet = true;
+ }
+ }
+ else if (IsFlyFreeFrame())
+ {
+ const SwFlyFreeFrame* pSwFlyFreeFrame(static_cast< const SwFlyFreeFrame* >(this));
+
+ if(nullptr != pSwFlyFreeFrame &&
+ pSwFlyFreeFrame->supportsAutoContour() &&
+ // isTransformableSwFrame already used in supportsAutoContour(), but
+ // better check twice when it may get changed there...
+ pSwFlyFreeFrame->isTransformableSwFrame())
+ {
+ // RotateFlyFrame: use untransformed SwFrame to allow text floating around.
+ // Will be transformed below
+ const TransformableSwFrame* pTransformableSwFrame(pSwFlyFreeFrame->getTransformableSwFrame());
+ const SwRect aFrameArea(pTransformableSwFrame->getUntransformedFrameArea());
+ rContour = tools::PolyPolygon(tools::Polygon(aFrameArea.SVRect()));
+ bRet = (0 != rContour.Count());
+ }
+ }
+
+ if(bRet && 0 != rContour.Count())
+ {
+ if (IsFlyFreeFrame() &&
+ static_cast< const SwFlyFreeFrame* >(this)->isTransformableSwFrame())
+ {
+ // Need to adapt contour to transformation
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getFrameAreaTransformation().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ basegfx::B2DPolyPolygon aSource(rContour.getB2DPolyPolygon());
+ const basegfx::B2DPoint aCenter(getFrameAreaTransformation() * basegfx::B2DPoint(0.5, 0.5));
+ const basegfx::B2DHomMatrix aRotateAroundCenter(
+ basegfx::utils::createRotateAroundPoint(
+ aCenter.getX(),
+ aCenter.getY(),
+ fRotate));
+ aSource.transform(aRotateAroundCenter);
+ rContour = tools::PolyPolygon(aSource);
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+
+const SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj() const
+{
+ return static_cast<const SwVirtFlyDrawObj*>(GetDrawObj());
+}
+SwVirtFlyDrawObj* SwFlyFrame::GetVirtDrawObj()
+{
+ return static_cast<SwVirtFlyDrawObj*>(DrawObj());
+}
+
+// implementation of pure virtual method declared in
+// base class <SwAnchoredObject>
+
+void SwFlyFrame::InvalidateObjPos()
+{
+ InvalidatePos();
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+}
+
+SwFrameFormat& SwFlyFrame::GetFrameFormat()
+{
+ OSL_ENSURE( GetFormat(),
+ "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
+ return *GetFormat();
+}
+const SwFrameFormat& SwFlyFrame::GetFrameFormat() const
+{
+ OSL_ENSURE( GetFormat(),
+ "<SwFlyFrame::GetFrameFormat()> - missing frame format -> crash." );
+ return *GetFormat();
+}
+
+SwRect SwFlyFrame::GetObjRect() const
+{
+ return getFrameArea();
+}
+
+// #i70122#
+// for Writer fly frames the bounding rectangle equals the object rectangles
+SwRect SwFlyFrame::GetObjBoundRect() const
+{
+ return GetObjRect();
+}
+
+// #i68520#
+bool SwFlyFrame::SetObjTop_( const SwTwips _nTop )
+{
+ const bool bChanged( getFrameArea().Pos().getY() != _nTop );
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setY(_nTop);
+
+ return bChanged;
+}
+bool SwFlyFrame::SetObjLeft_( const SwTwips _nLeft )
+{
+ const bool bChanged( getFrameArea().Pos().getX() != _nLeft );
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(_nLeft);
+
+ return bChanged;
+}
+
+/** method to assure that anchored object is registered at the correct
+ page frame
+
+ OD 2004-07-02 #i28701#
+*/
+void SwFlyFrame::RegisterAtCorrectPage()
+{
+ // default behaviour is to do nothing.
+}
+
+void SwFlyFrame::RegisterAtPage(SwPageFrame &)
+{
+ // default behaviour is to do nothing.
+}
+
+/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
+
+ OD 2004-05-11 #i28701#
+*/
+bool SwFlyFrame::IsFormatPossible() const
+{
+ return SwAnchoredObject::IsFormatPossible() &&
+ !IsLocked() && !IsColLocked();
+}
+
+void SwFlyFrame::GetAnchoredObjects( std::vector<SwAnchoredObject*>& aVector, const SwFormat& rFormat )
+{
+ SwIterator<SwFlyFrame,SwFormat> aIter( rFormat );
+ for( SwFlyFrame* pFlyFrame = aIter.First(); pFlyFrame; pFlyFrame = aIter.Next() )
+ aVector.push_back( pFlyFrame );
+}
+
+const SwFlyFrameFormat * SwFlyFrame::GetFormat() const
+{
+ return static_cast< const SwFlyFrameFormat * >( GetDep() );
+}
+
+SwFlyFrameFormat * SwFlyFrame::GetFormat()
+{
+ return static_cast< SwFlyFrameFormat * >( GetDep() );
+}
+
+void SwFlyFrame::Calc(vcl::RenderContext* pRenderContext) const
+{
+ if ( !m_bValidContentPos )
+ const_cast<SwFlyFrame*>(this)->PrepareMake(pRenderContext);
+ else
+ SwLayoutFrame::Calc(pRenderContext);
+}
+
+SwTwips SwFlyFrame::CalcContentHeight(const SwBorderAttrs *pAttrs, const SwTwips nMinHeight, const SwTwips nUL)
+{
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nHeight = 0;
+ if ( Lower() )
+ {
+ if ( Lower()->IsColumnFrame() )
+ {
+ FormatWidthCols( *pAttrs, nUL, nMinHeight );
+ nHeight = aRectFnSet.GetHeight(Lower()->getFrameArea());
+ }
+ else
+ {
+ SwFrame *pFrame = Lower();
+ while ( pFrame )
+ {
+ nHeight += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ // This TextFrame would like to be a bit larger
+ nHeight += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - aRectFnSet.GetHeight(pFrame->getFramePrintArea());
+ else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ nHeight += static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ pFrame = pFrame->GetNext();
+ }
+ }
+ if ( GetDrawObjs() )
+ {
+ const size_t nCnt = GetDrawObjs()->size();
+ SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
+ SwTwips nBorder = aRectFnSet.GetHeight(getFrameArea()) -
+ aRectFnSet.GetHeight(getFramePrintArea());
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ // consider only Writer fly frames, which follow the text flow.
+ if ( pFly->IsFlyLayFrame() &&
+ pFly->getFrameArea().Top() != FAR_AWAY &&
+ pFly->GetFormat()->GetFollowTextFlow().GetValue() )
+ {
+ SwTwips nDist = -aRectFnSet.BottomDist( pFly->getFrameArea(), nTop );
+ if( nDist > nBorder + nHeight )
+ nHeight = nDist - nBorder;
+ }
+ }
+ }
+ }
+ }
+ return nHeight;
+}
+
+const SwFormatAnchor* SwFlyFrame::GetAnchorFromPoolItem(const SfxPoolItem& rItem)
+{
+ switch(rItem.Which())
+ {
+ case RES_ATTRSET_CHG:
+ return rItem.StaticWhichCast(RES_ATTRSET_CHG).GetChgSet()->GetItem(RES_ANCHOR, false);
+ case RES_ANCHOR:
+ return static_cast<const SwFormatAnchor*>(&rItem);
+ default:
+ return nullptr;
+ }
+}
+
+const SwFlyFrame* SwFlyFrame::DynCastFlyFrame() const
+{
+ return this;
+}
+
+SwFlyFrame* SwFlyFrame::DynCastFlyFrame()
+{
+ return this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx
new file mode 100644
index 000000000..f7611392d
--- /dev/null
+++ b/sw/source/core/layout/flycnt.cxx
@@ -0,0 +1,1522 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/swframetypes.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <frmtool.hxx>
+#include <dflyobj.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <txatbase.hxx>
+
+#include <tabfrm.hxx>
+#include <flyfrms.hxx>
+#include <crstate.hxx>
+#include <sectfrm.hxx>
+
+#include <tocntntanchoredobjectposition.hxx>
+#include <sortedobjs.hxx>
+#include <layouter.hxx>
+#include "objectformattertxtfrm.hxx"
+#include <HandleAnchorNodeChg.hxx>
+#include <ndtxt.hxx>
+#include <textboxhelper.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <unoprnms.hxx>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+
+SwTwips lcl_GetTopForObjPos(const SwContentFrame* pCnt, const bool bVert, const bool bVertL2R)
+{
+ if ( bVert )
+ {
+ SwTwips aResult = pCnt->getFrameArea().Left();
+ if ( bVertL2R )
+ aResult += pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ else
+ aResult += pCnt->getFrameArea().Width() - pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ return aResult;
+ }
+ else
+ return pCnt->getFrameArea().Top() + pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+}
+
+}
+
+SwFlyAtContentFrame::SwFlyAtContentFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwFlyFreeFrame( pFormat, pSib, pAnch )
+{
+ m_bAtCnt = true;
+ m_bAutoPosition = (RndStdIds::FLY_AT_CHAR == pFormat->GetAnchor().GetAnchorId());
+}
+
+// #i28701#
+
+void SwFlyAtContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ {
+ SwFlyFrame::SwClientNotify(rMod, rHint);
+ return;
+ }
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const SwFormatAnchor* pAnch = pLegacy->m_pNew ? GetAnchorFromPoolItem(*pLegacy->m_pNew) : nullptr;
+ if(!pAnch)
+ {
+ SwFlyFrame::SwClientNotify(rMod, rHint);
+ return;
+ }
+ OSL_ENSURE(pAnch->GetAnchorId() == GetFormat()->GetAnchor().GetAnchorId(),
+ "Illegal change of anchor type.");
+
+ //Unregister, get hold of a new anchor and attach it
+ SwRect aOld(GetObjRectWithSpaces());
+ SwPageFrame* pOldPage = FindPageFrame();
+ const SwFrame* pOldAnchor = GetAnchorFrame();
+ SwContentFrame* pContent = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetAnchorFrame()));
+ AnchorFrame()->RemoveFly(this);
+
+ const bool bBodyFootnote = (pContent->IsInDocBody() || pContent->IsInFootnote());
+
+ // Search the new anchor using the NodeIdx; the relation between old
+ // and new NodeIdx determines the search direction
+ const SwNodeIndex aNewIdx(pAnch->GetContentAnchor()->nNode);
+ SwNodeIndex aOldIdx(pContent->IsTextFrame()
+ // sw_redlinehide: can pick any node here, the compare with
+ // FrameContainsNode should catch it
+ ? *static_cast<SwTextFrame *>(pContent)->GetTextNodeFirst()
+ : *static_cast<SwNoTextFrame *>(pContent)->GetNode());
+
+ //fix: depending on which index was smaller, searching in the do-while
+ //loop previously was done forward or backwards respectively. This however
+ //could lead to an infinite loop. To at least avoid the loop, searching
+ //is now done in only one direction. Getting hold of a frame from the node
+ //is still possible if the new anchor could not be found. Chances are
+ //good that this will be the correct one.
+ // consider the case that at found anchor frame candidate already a
+ // fly frame of the given fly format is registered.
+ // consider, that <pContent> is the already
+ // the new anchor frame.
+ bool bFound(FrameContainsNode(*pContent, aNewIdx.GetIndex()));
+ const bool bNext = !bFound && aOldIdx < aNewIdx;
+ while(pContent && !bFound)
+ {
+ do
+ {
+ if(bNext)
+ pContent = pContent->GetNextContentFrame();
+ else
+ pContent = pContent->GetPrevContentFrame();
+ } while(pContent &&
+ (bBodyFootnote != (pContent->IsInDocBody() || pContent->IsInFootnote())));
+ if(pContent)
+ bFound = FrameContainsNode(*pContent, aNewIdx.GetIndex());
+
+ // check, if at found anchor frame candidate already a fly frame
+ // of the given fly frame format is registered.
+ if(bFound && pContent && pContent->GetDrawObjs())
+ {
+ SwFrameFormat* pMyFlyFrameFormat(&GetFrameFormat());
+ SwSortedObjs &rObjs = *pContent->GetDrawObjs();
+ for(SwAnchoredObject* rObj : rObjs)
+ {
+ SwFlyFrame* pFlyFrame = rObj->DynCastFlyFrame();
+ if (pFlyFrame &&
+ &(pFlyFrame->GetFrameFormat()) == pMyFlyFrameFormat)
+ {
+ bFound = false;
+ break;
+ }
+ }
+ }
+ }
+ if(!pContent)
+ {
+ SwContentNode *pNode = aNewIdx.GetNode().GetContentNode();
+ std::pair<Point, bool> const tmp(pOldAnchor->getFrameArea().Pos(), false);
+ pContent = pNode->getLayoutFrame(getRootFrame(), nullptr, &tmp);
+ OSL_ENSURE(pContent, "New anchor not found");
+ }
+ //Flys are never attached to a follow, but always on the master which
+ //we are going to search now.
+ SwContentFrame* pFlow = pContent;
+ while(pFlow->IsFollow())
+ pFlow = pFlow->FindMaster();
+ pContent = pFlow;
+
+ //and *puff* it's attached...
+ pContent->AppendFly( this );
+ if(pOldPage && pOldPage != FindPageFrame())
+ NotifyBackground(pOldPage, aOld, PrepareHint::FlyFrameLeave);
+
+ //Fix(3495)
+ InvalidatePos_();
+ InvalidatePage();
+ SetNotifyBack();
+ // #i28701# - reset member <maLastCharRect> and
+ // <mnLastTopOfLine> for to-character anchored objects.
+ ClearCharRectAndTopOfLine();
+}
+
+//We need some helper classes to monitor the oscillation and a few functions
+//to not get lost.
+
+namespace {
+
+// #i3317# - re-factoring of the position stack
+class SwOszControl
+{
+ static const SwFlyFrame* s_pStack1;
+ static const SwFlyFrame* s_pStack2;
+ static const SwFlyFrame* s_pStack3;
+ static const SwFlyFrame* s_pStack4;
+ static const SwFlyFrame* s_pStack5;
+
+ const SwFlyFrame* m_pFly;
+ std::vector<Point> maObjPositions;
+
+public:
+ explicit SwOszControl( const SwFlyFrame *pFrame );
+ ~SwOszControl();
+ bool ChkOsz();
+ static bool IsInProgress( const SwFlyFrame *pFly );
+};
+
+}
+
+const SwFlyFrame* SwOszControl::s_pStack1 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack2 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack3 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack4 = nullptr;
+const SwFlyFrame* SwOszControl::s_pStack5 = nullptr;
+
+SwOszControl::SwOszControl(const SwFlyFrame* pFrame)
+ : m_pFly(pFrame)
+{
+ if (!SwOszControl::s_pStack1)
+ SwOszControl::s_pStack1 = m_pFly;
+ else if (!SwOszControl::s_pStack2)
+ SwOszControl::s_pStack2 = m_pFly;
+ else if (!SwOszControl::s_pStack3)
+ SwOszControl::s_pStack3 = m_pFly;
+ else if (!SwOszControl::s_pStack4)
+ SwOszControl::s_pStack4 = m_pFly;
+ else if (!SwOszControl::s_pStack5)
+ SwOszControl::s_pStack5 = m_pFly;
+}
+
+SwOszControl::~SwOszControl()
+{
+ if (SwOszControl::s_pStack1 == m_pFly)
+ SwOszControl::s_pStack1 = nullptr;
+ else if (SwOszControl::s_pStack2 == m_pFly)
+ SwOszControl::s_pStack2 = nullptr;
+ else if (SwOszControl::s_pStack3 == m_pFly)
+ SwOszControl::s_pStack3 = nullptr;
+ else if (SwOszControl::s_pStack4 == m_pFly)
+ SwOszControl::s_pStack4 = nullptr;
+ else if (SwOszControl::s_pStack5 == m_pFly)
+ SwOszControl::s_pStack5 = nullptr;
+ // #i3317#
+ maObjPositions.clear();
+}
+
+bool SwOszControl::IsInProgress( const SwFlyFrame *pFly )
+{
+ if (SwOszControl::s_pStack1 && !pFly->IsLowerOf(SwOszControl::s_pStack1))
+ return true;
+ if (SwOszControl::s_pStack2 && !pFly->IsLowerOf(SwOszControl::s_pStack2))
+ return true;
+ if (SwOszControl::s_pStack3 && !pFly->IsLowerOf(SwOszControl::s_pStack3))
+ return true;
+ if (SwOszControl::s_pStack4 && !pFly->IsLowerOf(SwOszControl::s_pStack4))
+ return true;
+ if (SwOszControl::s_pStack5 && !pFly->IsLowerOf(SwOszControl::s_pStack5))
+ return true;
+ return false;
+}
+
+bool SwOszControl::ChkOsz()
+{
+ bool bOscillationDetected = false;
+
+ if ( maObjPositions.size() == 20 )
+ {
+ // #i3317# position stack is full -> oscillation
+ bOscillationDetected = true;
+ }
+ else
+ {
+ Point aNewObjPos = m_pFly->GetObjRect().Pos();
+ for ( auto const & pt : maObjPositions )
+ {
+ if ( aNewObjPos == pt )
+ {
+ // position already occurred -> oscillation
+ bOscillationDetected = true;
+ break;
+ }
+ }
+ if ( !bOscillationDetected )
+ {
+ maObjPositions.push_back( aNewObjPos );
+ }
+ }
+
+ return bOscillationDetected;
+}
+
+/**
+|* With a paragraph-anchored fly it's absolutely possible that
+|* the anchor reacts to changes of the fly. To this reaction the fly must
+|* certainly react too. Sadly this can lead to oscillations; for example the
+|* fly wants to go down therefore the content can go up - this leads to a
+|* smaller TextFrame thus the fly needs to go up again whereby the text will
+|* get pushed down...
+|* To avoid such oscillations, a small position stack is built. If the fly
+|* reaches a position which it already had once, the action is stopped.
+|* To not run into problems, the stack is designed to hold five positions.
+|* If the stack flows over, the action is stopped too.
+|* Cancellation leads to the situation that the fly has a bad position in
+|* the end. In case of cancellation, the frame is set to automatic top
+|* alignment to not trigger a 'big oscillation' when calling from outside
+|* again.
+|*/
+void SwFlyAtContentFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
+ {
+ return;
+ }
+
+ if ( SwOszControl::IsInProgress( this ) || IsLocked() || IsColLocked() )
+ return;
+
+ // #i28701# - use new method <GetPageFrame()>
+ if( !GetPageFrame() && GetAnchorFrame() && GetAnchorFrame()->IsInFly() )
+ {
+ SwFlyFrame* pFly = AnchorFrame()->FindFlyFrame();
+ SwPageFrame *pTmpPage = pFly ? pFly->FindPageFrame() : nullptr;
+ if( pTmpPage )
+ pTmpPage->AppendFlyToPage( this );
+ }
+ // #i28701# - use new method <GetPageFrame()>
+ if( !GetPageFrame() )
+ return;
+
+ bSetCompletePaintOnInvalidate = true;
+ {
+ SwFlyFrameFormat *pFormat = GetFormat();
+ const SwFormatFrameSize &rFrameSz = GetFormat()->GetFrameSize();
+ if( rFrameSz.GetHeightPercent() != SwFormatFrameSize::SYNCED &&
+ rFrameSz.GetHeightPercent() >= 100 )
+ {
+ pFormat->LockModify();
+ SwFormatSurround aMain( pFormat->GetSurround() );
+ if ( aMain.GetSurround() == css::text::WrapTextMode_NONE )
+ {
+ aMain.SetSurround( css::text::WrapTextMode_THROUGH );
+ pFormat->SetFormatAttr( aMain );
+ }
+ pFormat->UnlockModify();
+ }
+ }
+
+ SwOszControl aOszCntrl( this );
+
+ // #i43255#
+ // #i50356# - format the anchor frame, which
+ // contains the anchor position. E.g., for at-character anchored
+ // object this can be the follow frame of the anchor frame.
+ const bool bFormatAnchor =
+ !static_cast<const SwTextFrame*>( GetAnchorFrameContainingAnchPos() )->IsAnyJoinLocked() &&
+ !ConsiderObjWrapInfluenceOnObjPos() &&
+ !ConsiderObjWrapInfluenceOfOtherObjs();
+
+ const SwFrame* pFooter = GetAnchorFrame()->FindFooterOrHeader();
+ if( pFooter && !pFooter->IsFooterFrame() )
+ pFooter = nullptr;
+ bool bOsz = false;
+ bool bExtra = Lower() && Lower()->IsColumnFrame();
+ // #i3317# - boolean, to apply temporarily the
+ // 'straightforward positioning process' for the frame due to its
+ // overlapping with a previous column.
+ bool bConsiderWrapInfluenceDueToOverlapPrevCol( false );
+ // #i35911# - boolean, to apply temporarily the
+ // 'straightforward positioning process' for the frame due to fact
+ // that it causes the complete content of its layout environment
+ // to move forward.
+ // #i40444# - extend usage of this boolean:
+ // apply temporarily the 'straightforward positioning process' for
+ // the frame due to the fact that the frame clears the area for
+ // the anchor frame, thus it has to move forward.
+ bool bConsiderWrapInfluenceDueToMovedFwdAnchor( false );
+ do {
+ SwRectFnSet aRectFnSet(this);
+ Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
+ SwFlyFreeFrame::MakeAll(pRenderContext);
+
+ const bool bPosChgDueToOwnFormat =
+ aOldPos != aRectFnSet.GetPos(getFrameArea());
+ // #i3317#
+ if ( !ConsiderObjWrapInfluenceOnObjPos() &&
+ OverlapsPrevColumn() )
+ {
+ bConsiderWrapInfluenceDueToOverlapPrevCol = true;
+ }
+ // #i28701# - no format of anchor frame, if
+ // wrapping style influence is considered on object positioning
+ if ( bFormatAnchor )
+ {
+ SwTextFrame& rAnchPosAnchorFrame =
+ *GetAnchorFrameContainingAnchPos()->DynCastTextFrame();
+ // #i58182# - For the usage of new method
+ // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)>
+ // to check move forward of anchor frame due to the object
+ // positioning it's needed to know, if the object is anchored
+ // at the master frame before the anchor frame is formatted.
+ const bool bAnchoredAtMaster(!rAnchPosAnchorFrame.IsFollow());
+
+ // #i56300#
+ // perform complete format of anchor text frame and its
+ // previous frames, which have become invalid due to the
+ // fly frame format.
+ SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( rAnchPosAnchorFrame );
+ // #i35911#
+ // #i40444#
+ // #i58182# - usage of new method
+ // <SwObjectFormatterTextFrame::CheckMovedFwdCondition(..)>
+ sal_uInt32 nToPageNum( 0 );
+ bool bDummy( false );
+ bool bPageHasFlysAnchoredBelowThis(false);
+ if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition(
+// TODO: what if this fly moved bc it's in table? does sth prevent that?
+ *this, *GetPageFrame(),
+ bAnchoredAtMaster, nToPageNum, bDummy,
+ bPageHasFlysAnchoredBelowThis) )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ bConsiderWrapInfluenceDueToMovedFwdAnchor = true;
+ }
+ // mark anchor text frame
+ // directly, that it is moved forward by object positioning.
+ SwTextFrame* pAnchorTextFrame( static_cast<SwTextFrame*>(AnchorFrame()) );
+ bool bInsert( true );
+ sal_uInt32 nAnchorFrameToPageNum( 0 );
+ const SwDoc& rDoc = *(GetFrameFormat().GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, *pAnchorTextFrame, nAnchorFrameToPageNum ) )
+ {
+ if ( nAnchorFrameToPageNum < nToPageNum )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::RemoveMovedFwdFrame(rDoc, *pAnchorTextFrame);
+ }
+ }
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::InsertMovedFwdFrame(rDoc, *pAnchorTextFrame,
+ nToPageNum);
+ }
+ }
+ }
+ }
+
+ if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) ||
+ ( !isFrameAreaPositionValid() &&
+ ( pFooter || bPosChgDueToOwnFormat ) ) )
+ {
+ bOsz = aOszCntrl.ChkOsz();
+
+ // special loop prevention for dedicated document:
+ if ( bOsz &&
+ HasFixSize() && IsClipped() &&
+ GetAnchorFrame()->GetUpper()->IsCellFrame() )
+ {
+ SwFrameFormat* pFormat = GetFormat();
+ const SwFormatFrameSize& rFrameSz = pFormat->GetFrameSize();
+ if ( rFrameSz.GetWidthPercent() &&
+ rFrameSz.GetHeightPercent() == SwFormatFrameSize::SYNCED )
+ {
+ SwFormatSurround aSurround( pFormat->GetSurround() );
+ if ( aSurround.GetSurround() == css::text::WrapTextMode_NONE )
+ {
+ pFormat->LockModify();
+ aSurround.SetSurround( css::text::WrapTextMode_THROUGH );
+ pFormat->SetFormatAttr( aSurround );
+ pFormat->UnlockModify();
+ bOsz = false;
+ OSL_FAIL( "<SwFlyAtContentFrame::MakeAll()> - special loop prevention for dedicated document of b6403541 applied" );
+ }
+ }
+ }
+ }
+
+ if ( bExtra && Lower() && !Lower()->isFrameAreaPositionValid() )
+ {
+ // If a multi column frame leaves invalid columns because of
+ // a position change, we loop once more and format
+ // our content using FormatWidthCols again.
+ InvalidateSize_();
+ bExtra = false; // Ensure only one additional loop run
+ }
+ } while ( !isFrameAreaDefinitionValid() && !bOsz &&
+ // #i3317#
+ !bConsiderWrapInfluenceDueToOverlapPrevCol &&
+ // #i40444#
+ !bConsiderWrapInfluenceDueToMovedFwdAnchor &&
+ GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) );
+
+ // #i3317# - instead of attribute change apply
+ // temporarily the 'straightforward positioning process'.
+ // #i80924#
+ // handle special case during splitting of table rows
+ if ( bConsiderWrapInfluenceDueToMovedFwdAnchor &&
+ GetAnchorFrame()->IsInTab() &&
+ GetAnchorFrame()->IsInFollowFlowRow() )
+ {
+ const SwFrame* pCellFrame = GetAnchorFrame();
+ while ( pCellFrame && !pCellFrame->IsCellFrame() )
+ {
+ pCellFrame = pCellFrame->GetUpper();
+ }
+ if ( pCellFrame )
+ {
+ SwRectFnSet aRectFnSet(pCellFrame);
+ if ( aRectFnSet.GetTop(pCellFrame->getFrameArea()) == 0 &&
+ aRectFnSet.GetHeight(pCellFrame->getFrameArea()) == 0 )
+ {
+ bConsiderWrapInfluenceDueToMovedFwdAnchor = false;
+ }
+ }
+ }
+ // tdf#137803: Fix the position of the shape during autoSize
+ SwFrameFormat* pShapeFormat
+ = SwTextBoxHelper::getOtherTextBoxFormat(GetFormat(), RES_FLYFRMFMT);
+ // FIXME: According to tdf37153, ignore FollowTextFlow objs, because
+ // wrong position will applied in that case. FollowTextFlow needs fix.
+ if (pShapeFormat && !pShapeFormat->GetFollowTextFlow().GetValue() &&
+ SwTextBoxHelper::getProperty(pShapeFormat,
+ UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).hasValue() &&
+ SwTextBoxHelper::getProperty(pShapeFormat,
+ UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT).get<bool>() )
+ {
+ // get the text area of the shape
+ const tools::Rectangle aTextRectangle
+ = SwTextBoxHelper::getRelativeTextRectangle(pShapeFormat->FindRealSdrObject());
+ // get the original textframe position
+ SwFormatHoriOrient aHOri = pShapeFormat->GetHoriOrient();
+ SwFormatVertOrient aVOri = pShapeFormat->GetVertOrient();
+ // calc the right position of the shape depending on text area
+ aHOri.SetPos(aHOri.GetPos() + aTextRectangle.Left());
+ aVOri.SetPos(aVOri.GetPos() + aTextRectangle.Top());
+ // save the new position for the shape
+ auto pFormat = GetFormat();
+ const bool bLocked = pFormat->IsModifyLocked();
+ if (!bLocked)
+ pFormat->LockModify();
+ pFormat->SetFormatAttr(aHOri);
+ pFormat->SetFormatAttr(aVOri);
+ if (!bLocked)
+ pFormat->UnlockModify();
+ }
+ if ( bOsz || bConsiderWrapInfluenceDueToOverlapPrevCol ||
+ // #i40444#
+ bConsiderWrapInfluenceDueToMovedFwdAnchor )
+ {
+ SetTmpConsiderWrapInfluence( true );
+ SetRestartLayoutProcess( true );
+ SetTmpConsiderWrapInfluenceOfOtherObjs();
+ }
+ bSetCompletePaintOnInvalidate = false;
+}
+
+/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
+
+ #i28701#
+*/
+bool SwFlyAtContentFrame::IsFormatPossible() const
+{
+ return SwFlyFreeFrame::IsFormatPossible() &&
+ !SwOszControl::IsInProgress( this );
+}
+
+namespace {
+
+class SwDistance
+{
+public:
+ SwTwips m_nMain, m_nSub;
+ SwDistance()
+ : m_nMain(0)
+ , m_nSub(0)
+ {
+ }
+ bool operator<( const SwDistance& rTwo ) const
+ {
+ return m_nMain < rTwo.m_nMain
+ || (m_nMain == rTwo.m_nMain && m_nSub && rTwo.m_nSub && m_nSub < rTwo.m_nSub);
+ }
+ bool operator<=( const SwDistance& rTwo ) const
+ {
+ return m_nMain < rTwo.m_nMain
+ || (m_nMain == rTwo.m_nMain
+ && (!m_nSub || !rTwo.m_nSub || m_nSub <= rTwo.m_nSub));
+ }
+};
+
+}
+
+static const SwFrame * lcl_CalcDownDist( SwDistance &rRet,
+ const Point &rPt,
+ const SwContentFrame *pCnt )
+{
+ rRet.m_nSub = 0;
+ //If the point stays inside the Cnt everything is clear already; the Content
+ //automatically has a distance of 0.
+ if ( pCnt->getFrameArea().Contains( rPt ) )
+ {
+ rRet.m_nMain = 0;
+ return pCnt;
+ }
+ else
+ {
+ const SwLayoutFrame *pUp = pCnt->IsInTab() ? pCnt->FindTabFrame()->GetUpper() : pCnt->GetUpper();
+ // single column sections need to interconnect to their upper
+ while( pUp->IsSctFrame() )
+ pUp = pUp->GetUpper();
+ const bool bVert = pUp->IsVertical();
+
+ const bool bVertL2R = pUp->IsVertLR();
+
+ //Follow the text flow.
+ // #i70582#
+ // --> OD 2009-03-05 - adopted for Support for Classical Mongolian Script
+ const SwTwips nTopForObjPos = lcl_GetTopForObjPos(pCnt, bVert, bVertL2R);
+ if ( pUp->getFrameArea().Contains( rPt ) )
+ {
+ // <rPt> point is inside environment of given content frame
+ // #i70582#
+ if( bVert )
+ {
+ if ( bVertL2R )
+ rRet.m_nMain = rPt.X() - nTopForObjPos;
+ else
+ rRet.m_nMain = nTopForObjPos - rPt.X();
+ }
+ else
+ rRet.m_nMain = rPt.Y() - nTopForObjPos;
+ return pCnt;
+ }
+ else if ( rPt.Y() <= pUp->getFrameArea().Top() )
+ {
+ // <rPt> point is above environment of given content frame
+ // correct for vertical layout?
+ rRet.m_nMain = LONG_MAX;
+ }
+ else if( rPt.X() < pUp->getFrameArea().Left() &&
+ rPt.Y() <= ( bVert ? pUp->getFrameArea().Top() : pUp->getFrameArea().Bottom() ) )
+ {
+ // <rPt> point is left of environment of given content frame
+ // seems not to be correct for vertical layout!?
+ const SwFrame *pLay = pUp->GetLeaf( MAKEPAGE_NONE, false, pCnt );
+ if( !pLay ||
+ (bVert && (pLay->getFrameArea().Top() + pLay->getFramePrintArea().Bottom()) <rPt.Y())||
+ (!bVert && (pLay->getFrameArea().Left() + pLay->getFramePrintArea().Right())<rPt.X()) )
+ {
+ // <rPt> point is in left border of environment
+ // #i70582#
+ if( bVert )
+ {
+ if ( bVertL2R )
+ rRet.m_nMain = rPt.X() - nTopForObjPos;
+ else
+ rRet.m_nMain = nTopForObjPos - rPt.X();
+ }
+ else
+ rRet.m_nMain = rPt.Y() - nTopForObjPos;
+ return pCnt;
+ }
+ else
+ rRet.m_nMain = LONG_MAX;
+ }
+ else
+ {
+ rRet.m_nMain
+ = bVert ? (bVertL2R
+ ? ((pUp->getFrameArea().Left() + pUp->getFramePrintArea().Right())
+ - nTopForObjPos)
+ : (nTopForObjPos
+ - (pUp->getFrameArea().Left() + pUp->getFramePrintArea().Left())))
+ : ((pUp->getFrameArea().Top() + pUp->getFramePrintArea().Bottom())
+ - nTopForObjPos);
+
+ const SwFrame *pPre = pCnt;
+ const SwFrame *pLay = pUp->GetLeaf( MAKEPAGE_NONE, true, pCnt );
+ SwTwips nFrameTop = 0;
+ SwTwips nPrtHeight = 0;
+ bool bSct = false;
+ const SwSectionFrame *pSect = pUp->FindSctFrame();
+ if( pSect )
+ {
+ rRet.m_nSub = rRet.m_nMain;
+ rRet.m_nMain = 0;
+ }
+ if( pSect && !pSect->IsAnLower( pLay ) )
+ {
+ bSct = false;
+ const SwSectionFrame* pNxtSect = pLay ? pLay->FindSctFrame() : nullptr;
+ if (pSect->IsAnFollow(pNxtSect) && pLay)
+ {
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ nFrameTop = pLay->getFrameArea().Left();
+ else
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ pSect = pNxtSect;
+ }
+ else
+ {
+ pLay = pSect->GetUpper();
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pSect->getFrameArea().Right();
+ nPrtHeight = pLay->getFrameArea().Left() + pLay->getFramePrintArea().Left()
+ + pLay->getFramePrintArea().Width() - pSect->getFrameArea().Left()
+ - pSect->getFrameArea().Width();
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Left();
+ nPrtHeight = pSect->getFrameArea().Left() - pLay->getFrameArea().Left()
+ - pLay->getFramePrintArea().Left();
+ }
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Bottom();
+ nPrtHeight = pLay->getFrameArea().Top() + pLay->getFramePrintArea().Top()
+ + pLay->getFramePrintArea().Height() - pSect->getFrameArea().Top()
+ - pSect->getFrameArea().Height();
+ }
+ pSect = nullptr;
+ }
+ }
+ else if( pLay )
+ {
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pLay->getFrameArea().Left();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ bSct = nullptr != pSect;
+ }
+ while ( pLay && !pLay->getFrameArea().Contains( rPt ) &&
+ ( pLay->getFrameArea().Top() <= rPt.Y() || pLay->IsInFly() ||
+ ( pLay->IsInSct() &&
+ pLay->FindSctFrame()->GetUpper()->getFrameArea().Top() <= rPt.Y())) )
+ {
+ if ( pLay->IsFootnoteContFrame() )
+ {
+ if ( !static_cast<const SwLayoutFrame*>(pLay)->Lower() )
+ {
+ SwFrame *pDel = const_cast<SwFrame*>(pLay);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ return pPre;
+ }
+ return nullptr;
+ }
+ else
+ {
+ if( bSct || pSect )
+ rRet.m_nSub += nPrtHeight;
+ else
+ rRet.m_nMain += nPrtHeight;
+ pPre = pLay;
+ pLay = pLay->GetLeaf( MAKEPAGE_NONE, true, pCnt );
+ if( pSect && !pSect->IsAnLower( pLay ) )
+ { // If we're leaving a SwSectionFrame, the next Leaf-Frame
+ // is the part of the upper below the SectionFrame.
+ const SwSectionFrame* pNxtSect = pLay ?
+ pLay->FindSctFrame() : nullptr;
+ bSct = false;
+ if (pLay && pSect->IsAnFollow(pNxtSect))
+ {
+ pSect = pNxtSect;
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pLay->getFrameArea().Left();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ }
+ else
+ {
+ pLay = pSect->GetUpper();
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pSect->getFrameArea().Right();
+ nPrtHeight = pLay->getFrameArea().Left()+pLay->getFramePrintArea().Left()
+ + pLay->getFramePrintArea().Width() - pSect->getFrameArea().Left()
+ - pSect->getFrameArea().Width();
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Left();
+ nPrtHeight = pSect->getFrameArea().Left() -
+ pLay->getFrameArea().Left() - pLay->getFramePrintArea().Left();
+ }
+ }
+ else
+ {
+ nFrameTop = pSect->getFrameArea().Bottom();
+ nPrtHeight = pLay->getFrameArea().Top()+pLay->getFramePrintArea().Top()
+ + pLay->getFramePrintArea().Height() - pSect->getFrameArea().Top()
+ - pSect->getFrameArea().Height();
+ }
+ pSect = nullptr;
+ }
+ }
+ else if( pLay )
+ {
+ if( pLay->IsVertical() )
+ {
+ if ( pLay->IsVertLR() )
+ {
+ nFrameTop = pLay->getFrameArea().Left();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Left() + pLay->getFrameArea().Width();
+ nPrtHeight = pLay->getFramePrintArea().Width();
+ }
+ }
+ else
+ {
+ nFrameTop = pLay->getFrameArea().Top();
+ nPrtHeight = pLay->getFramePrintArea().Height();
+ }
+ bSct = nullptr != pSect;
+ }
+ }
+ }
+ if ( pLay )
+ {
+ if ( pLay->getFrameArea().Contains( rPt ) )
+ {
+ SwTwips nDiff = pLay->IsVertical() ? ( pLay->IsVertLR() ? ( rPt.X() - nFrameTop ) : ( nFrameTop - rPt.X() ) )
+ : ( rPt.Y() - nFrameTop );
+ if( bSct || pSect )
+ rRet.m_nSub += nDiff;
+ else
+ rRet.m_nMain += nDiff;
+ }
+ if ( pLay->IsFootnoteContFrame() && !static_cast<const SwLayoutFrame*>(pLay)->Lower() )
+ {
+ SwFrame *pDel = const_cast<SwFrame*>(pLay);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ return nullptr;
+ }
+ return pLay;
+ }
+ else
+ rRet.m_nMain = LONG_MAX;
+ }
+ }
+ return nullptr;
+}
+
+static sal_uInt64 lcl_FindCntDiff( const Point &rPt, const SwLayoutFrame *pLay,
+ const SwContentFrame *& rpCnt,
+ const bool bBody, const bool bFootnote )
+{
+ // Searches below pLay the nearest Cnt to the point. The reference point of
+ //the Contents is always the left upper corner.
+ //The Cnt should preferably be above the point.
+
+ rpCnt = nullptr;
+ sal_uInt64 nDistance = SAL_MAX_UINT64;
+ sal_uInt64 nNearest = SAL_MAX_UINT64;
+ const SwContentFrame *pCnt = pLay ? pLay->ContainsContent() : nullptr;
+
+ while ( pCnt && (bBody != pCnt->IsInDocBody() || bFootnote != pCnt->IsInFootnote()))
+ {
+ pCnt = pCnt->GetNextContentFrame();
+ if ( !pLay->IsAnLower( pCnt ) )
+ pCnt = nullptr;
+ }
+ const SwContentFrame *pNearest = pCnt;
+ if ( pCnt )
+ {
+ do
+ {
+ //Calculate the distance between those two points.
+ //'delta' X^2 + 'delta' Y^2 = 'distance'^2
+ sal_uInt64 dX = std::max( pCnt->getFrameArea().Left(), rPt.X() ) -
+ std::min( pCnt->getFrameArea().Left(), rPt.X() ),
+ dY = std::max( pCnt->getFrameArea().Top(), rPt.Y() ) -
+ std::min( pCnt->getFrameArea().Top(), rPt.Y() );
+ // square of the difference will do fine here
+ const sal_uInt64 nDiff = (dX * dX) + (dY * dY);
+ if ( pCnt->getFrameArea().Top() <= rPt.Y() )
+ {
+ if ( nDiff < nDistance )
+ {
+ //This one is the nearer one
+ nDistance = nNearest = nDiff;
+ rpCnt = pNearest = pCnt;
+ }
+ }
+ else if ( nDiff < nNearest )
+ {
+ nNearest = nDiff;
+ pNearest = pCnt;
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ while ( pCnt &&
+ (bBody != pCnt->IsInDocBody() || bFootnote != pCnt->IsInFootnote()))
+ pCnt = pCnt->GetNextContentFrame();
+
+ } while ( pCnt && pLay->IsAnLower( pCnt ) );
+ }
+ if (nDistance == SAL_MAX_UINT64)
+ { rpCnt = pNearest;
+ return nNearest;
+ }
+ return nDistance;
+}
+
+static const SwContentFrame * lcl_FindCnt( const Point &rPt, const SwContentFrame *pCnt,
+ const bool bBody, const bool bFootnote )
+{
+ //Starting from pCnt searches the ContentFrame whose left upper corner is the
+ //nearest to the point.
+ //Always returns a ContentFrame.
+
+ //First the nearest Content inside the page which contains the Content is
+ //searched. Starting from this page the pages in both directions need to
+ //be considered. If possible a Content is returned whose Y-position is
+ //above the point.
+ const SwContentFrame *pRet, *pNew;
+ const SwLayoutFrame *pLay = pCnt->FindPageFrame();
+ sal_uInt64 nDist; // not sure if a sal_Int32 would be enough?
+
+ nDist = ::lcl_FindCntDiff( rPt, pLay, pNew, bBody, bFootnote );
+ if ( pNew )
+ pRet = pNew;
+ else
+ { pRet = pCnt;
+ nDist = SAL_MAX_UINT64;
+ }
+ const SwContentFrame *pNearest = pRet;
+ sal_uInt64 nNearest = nDist;
+
+ if ( pLay )
+ {
+ const SwLayoutFrame *pPge = pLay;
+ sal_uInt64 nOldNew = SAL_MAX_UINT64;
+ for ( int i = 0; pPge->GetPrev() && (i < 3); ++i )
+ {
+ pPge = static_cast<const SwLayoutFrame*>(pPge->GetPrev());
+ const sal_uInt64 nNew = ::lcl_FindCntDiff( rPt, pPge, pNew, bBody, bFootnote );
+ if ( nNew < nDist )
+ {
+ if ( pNew->getFrameArea().Top() <= rPt.Y() )
+ {
+ pRet = pNearest = pNew;
+ nDist = nNearest = nNew;
+ }
+ else if ( nNew < nNearest )
+ {
+ pNearest = pNew;
+ nNearest = nNew;
+ }
+ }
+ else if (nOldNew != SAL_MAX_UINT64 && nNew > nOldNew)
+ break;
+ else
+ nOldNew = nNew;
+
+ }
+ pPge = pLay;
+ nOldNew = SAL_MAX_UINT64;
+ for ( int j = 0; pPge->GetNext() && (j < 3); ++j )
+ {
+ pPge = static_cast<const SwLayoutFrame*>(pPge->GetNext());
+ const sal_uInt64 nNew = ::lcl_FindCntDiff( rPt, pPge, pNew, bBody, bFootnote );
+ if ( nNew < nDist )
+ {
+ if ( pNew->getFrameArea().Top() <= rPt.Y() )
+ {
+ pRet = pNearest = pNew;
+ nDist = nNearest = nNew;
+ }
+ else if ( nNew < nNearest )
+ {
+ pNearest = pNew;
+ nNearest = nNew;
+ }
+ }
+ else if (nOldNew != SAL_MAX_UINT64 && nNew > nOldNew)
+ break;
+ else
+ nOldNew = nNew;
+ }
+ }
+ if ( pRet->getFrameArea().Top() > rPt.Y() )
+ return pNearest;
+ else
+ return pRet;
+}
+
+static void lcl_PointToPrt( Point &rPoint, const SwFrame *pFrame )
+{
+ SwRect aTmp( pFrame->getFramePrintArea() );
+ aTmp += pFrame->getFrameArea().Pos();
+ if ( rPoint.getX() < aTmp.Left() )
+ rPoint.setX(aTmp.Left());
+ else if ( rPoint.getX() > aTmp.Right() )
+ rPoint.setX(aTmp.Right());
+ if ( rPoint.getY() < aTmp.Top() )
+ rPoint.setY(aTmp.Top());
+ else if ( rPoint.getY() > aTmp.Bottom() )
+ rPoint.setY(aTmp.Bottom());
+
+}
+
+/** Searches an anchor for paragraph bound objects starting from pOldAnch.
+ *
+ * This is used to show anchors as well as changing anchors
+ * when dragging paragraph bound objects.
+ */
+const SwContentFrame *FindAnchor( const SwFrame *pOldAnch, const Point &rNew,
+ const bool bBodyOnly )
+{
+ //Search the nearest Cnt around the given document position in the text
+ //flow. The given anchor is the starting Frame.
+ const SwContentFrame* pCnt;
+ if ( pOldAnch->IsContentFrame() )
+ {
+ pCnt = static_cast<const SwContentFrame*>(pOldAnch);
+ }
+ else
+ {
+ Point aTmp( rNew );
+ const SwLayoutFrame *pTmpLay = static_cast<const SwLayoutFrame*>(pOldAnch);
+ if( pTmpLay->IsRootFrame() )
+ {
+ SwRect aTmpRect( aTmp, Size(0,0) );
+ pTmpLay = static_cast<const SwLayoutFrame*>(::FindPage( aTmpRect, pTmpLay->Lower() ));
+ }
+ pCnt = pTmpLay->GetContentPos( aTmp, false, bBodyOnly );
+ }
+
+ //Take care to use meaningful ranges during search. This means to not enter
+ //or leave header/footer in this case.
+ const bool bBody = pCnt->IsInDocBody() || bBodyOnly;
+ const bool bFootnote = !bBodyOnly && pCnt->IsInFootnote();
+
+ Point aNew( rNew );
+ if ( bBody )
+ {
+ //#38848 drag from page margin into the body.
+ const SwFrame *pPage = pCnt->FindPageFrame();
+ ::lcl_PointToPrt( aNew, pPage->GetUpper() );
+ SwRect aTmp( aNew, Size( 0, 0 ) );
+ pPage = ::FindPage( aTmp, pPage );
+ ::lcl_PointToPrt( aNew, pPage );
+ }
+
+ if ( pCnt->IsInDocBody() == bBody && pCnt->getFrameArea().Contains( aNew ) )
+ return pCnt;
+ else if ( pOldAnch->IsInDocBody() || pOldAnch->IsPageFrame() )
+ {
+ // Maybe the selected anchor is on the same page as the current anchor.
+ // With this we won't run into problems with the columns.
+ Point aTmp( aNew );
+ const SwContentFrame *pTmp = pCnt->FindPageFrame()->
+ GetContentPos( aTmp, false, true );
+ if ( pTmp && pTmp->getFrameArea().Contains( aNew ) )
+ return pTmp;
+ }
+
+ //Starting from the anchor we now search in both directions until we found
+ //the nearest one respectively.
+ //Not the direct distance is relevant but the distance which needs to be
+ //traveled through the text flow.
+ const SwContentFrame *pUpLst;
+ const SwContentFrame *pUpFrame = pCnt;
+ SwDistance nUp, nUpLst;
+ ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
+ SwDistance nDown = nUp;
+ bool bNegAllowed = true;// Make it possible to leave the negative section once.
+ do
+ {
+ pUpLst = pUpFrame; nUpLst = nUp;
+ pUpFrame = pUpLst->GetPrevContentFrame();
+ while ( pUpFrame &&
+ (bBody != pUpFrame->IsInDocBody() || bFootnote != pUpFrame->IsInFootnote()))
+ pUpFrame = pUpFrame->GetPrevContentFrame();
+ if ( pUpFrame )
+ {
+ ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
+ //It makes sense to search further, if the distance grows inside
+ //a table.
+ if ( pUpLst->IsInTab() && pUpFrame->IsInTab() )
+ {
+ while ( pUpFrame && ((nUpLst < nUp && pUpFrame->IsInTab()) ||
+ bBody != pUpFrame->IsInDocBody()) )
+ {
+ pUpFrame = pUpFrame->GetPrevContentFrame();
+ if ( pUpFrame )
+ ::lcl_CalcDownDist( nUp, aNew, pUpFrame );
+ }
+ }
+ }
+ if ( !pUpFrame )
+ nUp.m_nMain = LONG_MAX;
+ if (nUp.m_nMain >= 0 && LONG_MAX != nUp.m_nMain)
+ {
+ bNegAllowed = false;
+ if (nUpLst.m_nMain < 0) //don't take the wrong one, if the value
+ //just changed from negative to positive.
+ { pUpLst = pUpFrame;
+ nUpLst = nUp;
+ }
+ }
+ } while (pUpFrame && ((bNegAllowed && nUp.m_nMain < 0) || (nUp <= nUpLst)));
+
+ const SwContentFrame *pDownLst;
+ const SwContentFrame *pDownFrame = pCnt;
+ SwDistance nDownLst;
+ if (nDown.m_nMain < 0)
+ nDown.m_nMain = LONG_MAX;
+ do
+ {
+ pDownLst = pDownFrame; nDownLst = nDown;
+ pDownFrame = pDownLst->GetNextContentFrame();
+ while ( pDownFrame &&
+ (bBody != pDownFrame->IsInDocBody() || bFootnote != pDownFrame->IsInFootnote()))
+ pDownFrame = pDownFrame->GetNextContentFrame();
+ if ( pDownFrame )
+ {
+ ::lcl_CalcDownDist( nDown, aNew, pDownFrame );
+ if (nDown.m_nMain < 0)
+ nDown.m_nMain = LONG_MAX;
+ //It makes sense to search further, if the distance grows inside
+ //a table.
+ if ( pDownLst->IsInTab() && pDownFrame->IsInTab() )
+ {
+ while (pDownFrame
+ && ((nDown.m_nMain != LONG_MAX && pDownFrame->IsInTab())
+ || bBody != pDownFrame->IsInDocBody()))
+ {
+ pDownFrame = pDownFrame->GetNextContentFrame();
+ if ( pDownFrame )
+ ::lcl_CalcDownDist( nDown, aNew, pDownFrame );
+ if (nDown.m_nMain < 0)
+ nDown.m_nMain = LONG_MAX;
+ }
+ }
+ }
+ if ( !pDownFrame )
+ nDown.m_nMain = LONG_MAX;
+
+ } while (pDownFrame && nDown <= nDownLst && nDown.m_nMain != LONG_MAX
+ && nDownLst.m_nMain != LONG_MAX);
+
+ //If we couldn't find one in both directions, we'll search the Content whose
+ //left upper corner is the nearest to the point. Such a situation may
+ //happen, if the point doesn't lay in the text flow but in any margin.
+ if (nDownLst.m_nMain == LONG_MAX && nUpLst.m_nMain == LONG_MAX)
+ {
+ // If an OLE objects, which is contained in a fly frame
+ // is resized in inplace mode and the new Position is outside the
+ // fly frame, we do not want to leave our fly frame.
+ if ( pCnt->IsInFly() )
+ return pCnt;
+
+ return ::lcl_FindCnt( aNew, pCnt, bBody, bFootnote );
+ }
+ else
+ return nDownLst < nUpLst ? pDownLst : pUpLst;
+}
+
+void SwFlyAtContentFrame::SetAbsPos( const Point &rNew )
+{
+ SwPageFrame *pOldPage = FindPageFrame();
+ const SwRect aOld( GetObjRectWithSpaces() );
+ Point aNew( rNew );
+
+ if( ( GetAnchorFrame()->IsVertical() && !GetAnchorFrame()->IsVertLR() ) || GetAnchorFrame()->IsRightToLeft() )
+ aNew.setX(aNew.getX() + getFrameArea().Width());
+ SwContentFrame *pCnt = const_cast<SwContentFrame*>(::FindAnchor( GetAnchorFrame(), aNew ));
+ if( pCnt->IsProtected() )
+ pCnt = const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(GetAnchorFrame()));
+
+ SwPageFrame *pTmpPage = nullptr;
+ const bool bVert = pCnt->IsVertical();
+
+ const bool bVertL2R = pCnt->IsVertLR();
+ const bool bRTL = pCnt->IsRightToLeft();
+
+ if( ( !bVert != !GetAnchorFrame()->IsVertical() ) ||
+ ( !bRTL != !GetAnchorFrame()->IsRightToLeft() ) )
+ {
+ if( bVert || bRTL )
+ aNew.setX(aNew.getX() + getFrameArea().Width());
+ else
+ aNew.setX(aNew.getX() - getFrameArea().Width());
+ }
+
+ if ( pCnt->IsInDocBody() )
+ {
+ //#38848 drag from page margin into the body.
+ pTmpPage = pCnt->FindPageFrame();
+ ::lcl_PointToPrt( aNew, pTmpPage->GetUpper() );
+ SwRect aTmp( aNew, Size( 0, 0 ) );
+ pTmpPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(::FindPage( aTmp, pTmpPage )));
+ ::lcl_PointToPrt( aNew, pTmpPage );
+ }
+
+ //Setup RelPos, only invalidate if requested.
+ //rNew is an absolute position. We need to calculate the distance from rNew
+ //to the anchor inside the text flow to correctly set RelPos.
+//!!!!!We can optimize here: FindAnchor could also return RelPos!
+ const SwFrame *pFrame = nullptr;
+ SwTwips nY;
+ if ( pCnt->getFrameArea().Contains( aNew ) )
+ {
+ // #i70582#
+ if ( bVert )
+ {
+ nY = pCnt->getFrameArea().Left() - rNew.X();
+ if ( bVertL2R )
+ nY = -nY;
+ else
+ nY += pCnt->getFrameArea().Width() - getFrameArea().Width();
+ nY -= pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ }
+ else
+ nY = rNew.Y() - pCnt->getFrameArea().Top() - pCnt->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ }
+ else
+ {
+ SwDistance aDist;
+ pFrame = ::lcl_CalcDownDist( aDist, aNew, pCnt );
+ nY = aDist.m_nMain + aDist.m_nSub;
+ }
+
+ SwTwips nX = 0;
+
+ if ( pCnt->IsFollow() )
+ {
+ // Flys are never attached to the follow but always to the master,
+ // which we're going to search now.
+ const SwContentFrame *pOriginal = pCnt;
+ const SwContentFrame *pFollow = pCnt;
+ while ( pCnt->IsFollow() )
+ {
+ do
+ {
+ SwContentFrame* pPrev = pCnt->GetPrevContentFrame();
+ if (!pPrev)
+ {
+ SAL_WARN("sw.core", "very unexpected missing PrevContentFrame");
+ break;
+ }
+ pCnt = pPrev;
+ }
+ while ( pCnt->GetFollow() != pFollow );
+ pFollow = pCnt;
+ }
+ SwTwips nDiff = 0;
+ do
+ { const SwFrame *pUp = pFollow->GetUpper();
+ if( pUp->IsVertical() )
+ {
+ if ( pUp->IsVertLR() )
+ nDiff += pUp->getFramePrintArea().Width() - pFollow->GetRelPos().getX();
+ else
+ nDiff += pFollow->getFrameArea().Left() + pFollow->getFrameArea().Width()
+ - pUp->getFrameArea().Left() - pUp->getFramePrintArea().Left();
+ }
+ else
+ nDiff += pUp->getFramePrintArea().Height() - pFollow->GetRelPos().Y();
+ pFollow = pFollow->GetFollow();
+ } while ( pFollow != pOriginal );
+ nY += nDiff;
+ if( bVert )
+ nX = pCnt->getFrameArea().Top() - pOriginal->getFrameArea().Top();
+ else
+ nX = pCnt->getFrameArea().Left() - pOriginal->getFrameArea().Left();
+ }
+
+ if ( nY == LONG_MAX )
+ {
+ // #i70582#
+ const SwTwips nTopForObjPos = lcl_GetTopForObjPos(pCnt, bVert, bVertL2R);
+ if( bVert )
+ {
+ if ( bVertL2R )
+ nY = rNew.X() - nTopForObjPos;
+ else
+ nY = nTopForObjPos - rNew.X();
+ }
+ else
+ {
+ nY = rNew.Y() - nTopForObjPos;
+ }
+ }
+
+ SwFlyFrameFormat *pFormat = GetFormat();
+
+ if( bVert )
+ {
+ if( !pFrame )
+ nX += rNew.Y() - pCnt->getFrameArea().Top();
+ else
+ nX = rNew.Y() - pFrame->getFrameArea().Top();
+ }
+ else
+ {
+ if( !pFrame )
+ {
+ if ( pCnt->IsRightToLeft() )
+ nX += pCnt->getFrameArea().Right() - rNew.X() - getFrameArea().Width();
+ else
+ nX += rNew.X() - pCnt->getFrameArea().Left();
+ }
+ else
+ {
+ if ( pFrame->IsRightToLeft() )
+ nX += pFrame->getFrameArea().Right() - rNew.X() - getFrameArea().Width();
+ else
+ nX = rNew.X() - pFrame->getFrameArea().Left();
+ }
+ }
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+
+ if( pCnt != GetAnchorFrame() || ( IsAutoPos() && pCnt->IsTextFrame() &&
+ GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)) )
+ {
+ //Set the anchor attribute according to the new Cnt.
+ SwFormatAnchor aAnch( pFormat->GetAnchor() );
+ SwPosition pos = *aAnch.GetContentAnchor();
+ if( IsAutoPos() && pCnt->IsTextFrame() )
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
+ SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
+ Point aPt( rNew );
+ if( pCnt->GetModelPositionForViewPoint( &pos, aPt, &eTmpState )
+ && FrameContainsNode(*pTextFrame, pos.nNode.GetIndex()))
+ {
+ const SwTextAttr *const pTextInputField =
+ pos.nNode.GetNode().GetTextNode()->GetTextAttrAt(
+ pos.nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT );
+ if (pTextInputField != nullptr)
+ {
+ pos.nContent = pTextInputField->GetStart();
+ }
+ ResetLastCharRectHeight();
+ if( text::RelOrientation::CHAR == pFormat->GetVertOrient().GetRelationOrient() )
+ nY = LONG_MAX;
+ if( text::RelOrientation::CHAR == pFormat->GetHoriOrient().GetRelationOrient() )
+ nX = LONG_MAX;
+ }
+ else
+ {
+ pos = pTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ }
+ else if (pCnt->IsTextFrame())
+ {
+ pos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else // is that even possible? maybe if there was a change of anchor type from AT_FLY or something?
+ {
+ assert(pCnt->IsNoTextFrame());
+ pos.nNode = *static_cast<SwNoTextFrame*>(pCnt)->GetNode();
+ pos.nContent.Assign(static_cast<SwNoTextFrame*>(pCnt)->GetNode(), 0);
+ }
+ aAnch.SetAnchor( &pos );
+
+ // handle change of anchor node:
+ // if count of the anchor frame also change, the fly frames have to be
+ // re-created. Thus, delete all fly frames except the <this> before the
+ // anchor attribute is change and re-create them afterwards.
+ {
+ SwHandleAnchorNodeChg aHandleAnchorNodeChg( *pFormat, aAnch, this );
+ pFormat->GetDoc()->SetAttr( aAnch, *pFormat );
+ }
+ }
+ else if ( pTmpPage && pTmpPage != GetPageFrame() )
+ GetPageFrame()->MoveFly( this, pTmpPage );
+
+ const Point aRelPos = bVert ? Point( -nY, nX ) : Point( nX, nY );
+ ChgRelPos( aRelPos );
+ GetFormat()->GetDoc()->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+
+ if ( pOldPage != FindPageFrame() )
+ ::Notify_Background( GetVirtDrawObj(), pOldPage, aOld, PrepareHint::FlyFrameLeave, false );
+}
+
+/** method to assure that anchored object is registered at the correct
+ page frame
+
+ #i28701#
+ takes over functionality of deleted method <SwFlyAtContentFrame::AssertPage()>
+*/
+void SwFlyAtContentFrame::RegisterAtCorrectPage()
+{
+ SwPageFrame* pPageFrame( nullptr );
+ if ( GetVertPosOrientFrame() )
+ {
+ pPageFrame = const_cast<SwPageFrame*>(GetVertPosOrientFrame()->FindPageFrame());
+ }
+ if ( pPageFrame && GetPageFrame() != pPageFrame )
+ {
+ RegisterAtPage(*pPageFrame);
+ }
+}
+
+void SwFlyAtContentFrame::RegisterAtPage(SwPageFrame & rPageFrame)
+{
+ assert(GetPageFrame() != &rPageFrame);
+ if (GetPageFrame())
+ {
+ GetPageFrame()->MoveFly( this, &rPageFrame );
+ }
+ else
+ {
+ rPageFrame.AppendFlyToPage( this );
+ }
+}
+
+// #i26791#
+void SwFlyAtContentFrame::MakeObjPos()
+{
+ // if fly frame position is valid, nothing is to do. Thus, return
+ if ( isFrameAreaPositionValid() )
+ {
+ return;
+ }
+
+ // #i26791# - validate position flag here.
+ setFrameAreaPositionValid(true);
+
+ // #i35911# - no calculation of new position, if
+ // anchored object is marked that it clears its environment and its
+ // environment is already cleared.
+ // before checking for cleared environment
+ // check, if member <mpVertPosOrientFrame> is set.
+ if ( GetVertPosOrientFrame() &&
+ ClearedEnvironment() && HasClearedEnvironment() )
+ {
+ return;
+ }
+
+ // use new class to position object
+ objectpositioning::SwToContentAnchoredObjectPosition
+ aObjPositioning( *GetVirtDrawObj() );
+ aObjPositioning.CalcPosition();
+
+ SetVertPosOrientFrame ( aObjPositioning.GetVertPosOrientFrame() );
+}
+
+// #i28701#
+bool SwFlyAtContentFrame::InvalidationAllowed( const InvalidationType _nInvalid ) const
+{
+ bool bAllowed( SwFlyFreeFrame::InvalidationAllowed( _nInvalid ) );
+
+ // forbiddance of base instance can't be over ruled.
+ if ( bAllowed )
+ {
+ if ( _nInvalid == INVALID_POS ||
+ _nInvalid == INVALID_ALL )
+ {
+ bAllowed = InvalidationOfPosAllowed();
+ }
+ }
+
+ return bAllowed;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flyincnt.cxx b/sw/source/core/layout/flyincnt.cxx
new file mode 100644
index 000000000..2455f7a6d
--- /dev/null
+++ b/sw/source/core/layout/flyincnt.cxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <frmtool.hxx>
+#include <hints.hxx>
+#include <fmtornt.hxx>
+#include <rootfrm.hxx>
+#include <flyfrms.hxx>
+#include <dflyobj.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/deleter.hxx>
+
+SwFlyInContentFrame::SwFlyInContentFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwFlyFrame( pFormat, pSib, pAnch )
+{
+ m_bInCnt = true;
+ SwTwips nRel = pFormat->GetVertOrient().GetPos();
+ // OD 2004-05-27 #i26791# - member <aRelPos> moved to <SwAnchoredObject>
+ Point aRelPos;
+ if( pAnch && pAnch->IsVertical() )
+ aRelPos.setX(-nRel);
+ else
+ aRelPos.setY(nRel);
+ SetCurrRelPos( aRelPos );
+}
+
+void SwFlyInContentFrame::DestroyImpl()
+{
+ if ( !GetFormat()->GetDoc()->IsInDtor() && GetAnchorFrame() )
+ {
+ SwRect aTmp( GetObjRectWithSpaces() );
+ SwFlyInContentFrame::NotifyBackground( FindPageFrame(), aTmp, PrepareHint::FlyFrameLeave );
+ }
+
+ SwFlyFrame::DestroyImpl();
+}
+
+SwFlyInContentFrame::~SwFlyInContentFrame()
+{
+}
+
+// #i28701#
+
+void SwFlyInContentFrame::SetRefPoint( const Point& rPoint,
+ const Point& rRelAttr,
+ const Point& rRelPos )
+{
+ // OD 2004-05-27 #i26791# - member <aRelPos> moved to <SwAnchoredObject>
+ OSL_ENSURE( rPoint != m_aRef || rRelAttr != GetCurrRelPos(), "SetRefPoint: no change" );
+ std::unique_ptr<SwFlyNotify, o3tl::default_delete<SwFlyNotify>> xNotify;
+ // No notify at a locked fly frame, if a fly frame is locked, there's
+ // already a SwFlyNotify object on the stack (MakeAll).
+ if( !IsLocked() )
+ xNotify.reset(new SwFlyNotify( this ));
+ m_aRef = rPoint;
+ SetCurrRelPos( rRelAttr );
+ SwRectFnSet aRectFnSet(GetAnchorFrame());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetPos( aFrm, rPoint + rRelPos );
+ }
+
+ // #i68520#
+ InvalidateObjRectWithSpaces();
+ if (xNotify)
+ {
+ InvalidatePage();
+ setFrameAreaPositionValid(false);
+ m_bInvalid = true;
+ Calc(getRootFrame()->GetCurrShell()->GetOut());
+ xNotify.reset();
+ }
+}
+
+void SwFlyInContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ std::pair<std::unique_ptr<SwAttrSetChg>, std::unique_ptr<SwAttrSetChg>> aTweakedChgs;
+ std::pair<const SfxPoolItem*, const SfxPoolItem*> aSuperArgs(nullptr, nullptr);
+ switch(pLegacy->GetWhich())
+ {
+ case RES_ATTRSET_CHG:
+ {
+ auto pOldAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto pNewAttrSetChg = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ if(pOldAttrSetChg
+ && pNewAttrSetChg
+ && ((SfxItemState::SET == pNewAttrSetChg->GetChgSet()->GetItemState(RES_SURROUND, false))
+ || (SfxItemState::SET == pNewAttrSetChg->GetChgSet()->GetItemState(RES_FRMMACRO, false))))
+ {
+ aTweakedChgs.second = std::make_unique<SwAttrSetChg>(*pOldAttrSetChg);
+ aTweakedChgs.second->ClearItem(RES_SURROUND);
+ aTweakedChgs.second->ClearItem(RES_FRMMACRO);
+ if(aTweakedChgs.second->Count())
+ {
+ aTweakedChgs.first = std::make_unique<SwAttrSetChg>(*pOldAttrSetChg);
+ aTweakedChgs.first->ClearItem(RES_SURROUND);
+ aTweakedChgs.first->ClearItem(RES_FRMMACRO);
+ aSuperArgs = std::pair<const SfxPoolItem*, const SfxPoolItem*>(aTweakedChgs.first.get(), aTweakedChgs.second.get());
+ }
+ } else if (pNewAttrSetChg && pNewAttrSetChg->GetChgSet()->Count())
+ aSuperArgs = std::pair<const SfxPoolItem*, const SfxPoolItem*>(pLegacy->m_pOld, pLegacy->m_pNew);
+ break;
+ }
+ case RES_SURROUND:
+ case RES_FRMMACRO:
+ break;
+ default:
+ aSuperArgs = std::pair<const SfxPoolItem*, const SfxPoolItem*>(pLegacy->m_pOld, pLegacy->m_pNew);
+ }
+ if(aSuperArgs.second)
+ {
+ SwFlyFrame::SwClientNotify(rMod, sw::LegacyModifyHint(aSuperArgs.first, aSuperArgs.second));
+ if(GetAnchorFrame())
+ AnchorFrame()->Prepare(PrepareHint::FlyFrameAttributesChanged, GetFormat());
+ }
+}
+
+/// Here the content gets formatted initially.
+void SwFlyInContentFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttrs )
+{
+ if ( !getFrameArea().Height() )
+ {
+ Lock(); //don't format the anchor on the crook.
+ SwContentFrame *pContent = ContainsContent();
+ while ( pContent )
+ { pContent->Calc(pRenderContext);
+ pContent = pContent->GetNextContentFrame();
+ }
+ Unlock();
+ }
+ SwFlyFrame::Format( pRenderContext, pAttrs );
+}
+
+/** Calculate object position
+ *
+ * @note: In contrast to other Frames, we only calculate the relative position
+ * here. The absolute position is only calculated using SetAbsPos.
+ **/
+void SwFlyInContentFrame::MakeObjPos()
+{
+ if ( isFrameAreaPositionValid() )
+ return;
+
+ setFrameAreaPositionValid(true);
+ SwFlyFrameFormat *pFormat = GetFormat();
+ const SwFormatVertOrient &rVert = pFormat->GetVertOrient();
+ //Update the current values in the format if needed, during this we of
+ //course must not send any Modify.
+ const bool bVert = GetAnchorFrame()->IsVertical();
+ SwTwips nOld = rVert.GetPos();
+ SwTwips nAct = bVert ? -GetCurrRelPos().X() : GetCurrRelPos().Y();
+ if( nAct != nOld )
+ {
+ SwFormatVertOrient aVert( rVert );
+ aVert.SetPos( nAct );
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aVert );
+ pFormat->UnlockModify();
+ }
+}
+
+void SwFlyInContentFrame::ActionOnInvalidation( const InvalidationType _nInvalid )
+{
+ if ( INVALID_POS == _nInvalid || INVALID_ALL == _nInvalid )
+ AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged, &GetFrameFormat() );
+}
+
+void SwFlyInContentFrame::NotifyBackground( SwPageFrame *, const SwRect& rRect,
+ PrepareHint eHint)
+{
+ if ( eHint == PrepareHint::FlyFrameAttributesChanged )
+ AnchorFrame()->Prepare( PrepareHint::FlyFrameAttributesChanged );
+ else
+ AnchorFrame()->Prepare( eHint, static_cast<void const *>(&rRect) );
+}
+
+Point const & SwFlyInContentFrame::GetRelPos() const
+{
+ Calc(getRootFrame()->GetCurrShell()->GetOut());
+ return GetCurrRelPos();
+}
+
+/// @see SwRowFrame::RegistFlys()
+void SwFlyInContentFrame::RegistFlys()
+{
+ SwPageFrame *pPage = FindPageFrame();
+ OSL_ENSURE( pPage, "Register Flys without pages?" );
+ ::RegistFlys( pPage, this );
+}
+
+void SwFlyInContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ // OD 2004-01-19 #110582#
+ if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
+ {
+ return;
+ }
+
+ if ( !GetAnchorFrame() || IsLocked() || IsColLocked() || !FindPageFrame() )
+ return;
+
+ Lock(); // The curtain falls
+
+ //does the notification in the DTor
+ const SwFlyNotify aNotify( this );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ if ( IsClipped() )
+ {
+ setFrameAreaSizeValid(false);
+ m_bHeightClipped = m_bWidthClipped = false;
+ }
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() || !m_bValidContentPos )
+ {
+ //Only stop, if the flag is set!!
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ MakePrtArea( rAttrs );
+ m_bValidContentPos = false;
+ }
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ Format( getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ }
+
+ if ( !isFrameAreaPositionValid() )
+ {
+ MakeObjPos();
+ }
+
+ if ( !m_bValidContentPos )
+ MakeContentPos( rAttrs );
+
+ // re-activate clipping of as-character anchored Writer fly frames
+ // depending on compatibility option <ClipAsCharacterAnchoredWriterFlyFrames>
+ if ( isFrameAreaPositionValid() &&
+ isFrameAreaSizeValid() &&
+ GetFormat()->getIDocumentSettingAccess().get( DocumentSettingId::CLIP_AS_CHARACTER_ANCHORED_WRITER_FLY_FRAME ) )
+ {
+ SwFrame* pFrame = AnchorFrame();
+ if ( getFrameArea().Left() == (pFrame->getFrameArea().Left()+pFrame->getFramePrintArea().Left()) &&
+ getFrameArea().Width() > pFrame->getFramePrintArea().Width() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( pFrame->getFramePrintArea().Width() );
+ setFramePrintAreaValid(false);
+ m_bWidthClipped = true;
+ }
+ }
+ }
+ Unlock();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flylay.cxx b/sw/source/core/layout/flylay.cxx
new file mode 100644
index 000000000..209ca1fcf
--- /dev/null
+++ b/sw/source/core/layout/flylay.cxx
@@ -0,0 +1,1505 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <ftnfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <sectfrm.hxx>
+#include <notxtfrm.hxx>
+#include <txtfly.hxx>
+
+#include <svx/svdpage.hxx>
+#include <editeng/ulspitem.hxx>
+#include <fmtornt.hxx>
+#include <fmtfsize.hxx>
+#include <ndole.hxx>
+#include <tabfrm.hxx>
+#include <flyfrms.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <environmentofanchoredobject.hxx>
+#include <sortedobjs.hxx>
+#include <viewimp.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <pam.hxx>
+#include <ndindex.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+SwFlyFreeFrame::SwFlyFreeFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch )
+: SwFlyFrame( pFormat, pSib, pAnch ),
+ // #i34753#
+ mbNoMakePos( false ),
+ // #i37068#
+ mbNoMoveOnCheckClip( false )
+{
+}
+
+void SwFlyFreeFrame::DestroyImpl()
+{
+ // #i28701# - use new method <GetPageFrame()>
+ if( GetPageFrame() )
+ {
+ if( GetFormat()->GetDoc()->IsInDtor() )
+ {
+ // #i29879# - remove also to-frame anchored Writer
+ // fly frame from page.
+ const bool bRemoveFromPage =
+ GetPageFrame()->GetSortedObjs() &&
+ ( IsFlyAtContentFrame() ||
+ ( GetAnchorFrame() && GetAnchorFrame()->IsFlyFrame() ) );
+ if ( bRemoveFromPage )
+ {
+ GetPageFrame()->GetSortedObjs()->Remove( *this );
+ }
+ }
+ else
+ {
+ SwRect aTmp( GetObjRectWithSpaces() );
+ SwFlyFreeFrame::NotifyBackground( GetPageFrame(), aTmp, PrepareHint::FlyFrameLeave );
+ }
+ }
+
+ SwFlyFrame::DestroyImpl();
+}
+
+SwFlyFreeFrame::~SwFlyFreeFrame()
+{
+#if 0
+ // we are possibly in ContourCache, make sure we vanish
+ ::ClrContourCache(GetVirtDrawObj());
+#endif
+}
+
+// #i28701#
+/** Notifies the background (all ContentFrames that currently are overlapping).
+ *
+ * Additionally, the window is also directly invalidated (especially where
+ * there are no overlapping ContentFrames).
+ * This also takes ContentFrames within other Flys into account.
+ */
+void SwFlyFreeFrame::NotifyBackground( SwPageFrame *pPageFrame,
+ const SwRect& rRect, PrepareHint eHint )
+{
+ ::Notify_Background( GetVirtDrawObj(), pPageFrame, rRect, eHint, true );
+}
+
+void SwFlyFreeFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ if ( !GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( GetVirtDrawObj()->GetLayer() ) )
+ {
+ return;
+ }
+
+ if ( !GetAnchorFrame() || IsLocked() || IsColLocked() )
+ {
+ return;
+ }
+
+ // #i28701# - use new method <GetPageFrame()>
+ if( !GetPageFrame() && GetAnchorFrame()->IsInFly() )
+ {
+ SwFlyFrame* pFly = AnchorFrame()->FindFlyFrame();
+ SwPageFrame *pPageFrame = pFly ? pFly->FindPageFrame() : nullptr;
+ if( pPageFrame )
+ pPageFrame->AppendFlyToPage( this );
+ }
+
+ if( !GetPageFrame() )
+ {
+ return;
+ }
+
+ Lock(); // The curtain drops
+
+ // takes care of the notification in the dtor
+ const SwFlyNotify aNotify( this );
+
+ if ( IsClipped() )
+ {
+ setFrameAreaSizeValid(false);
+ m_bHeightClipped = m_bWidthClipped = false;
+ // no invalidation of position,
+ // if anchored object is anchored inside a Writer fly frame,
+ // its position is already locked, and it follows the text flow.
+ // #i34753# - add condition:
+ // no invalidation of position, if no direct move is requested in <CheckClip(..)>
+ if ( !IsNoMoveOnCheckClip() &&
+ !( PositionLocked() &&
+ GetAnchorFrame()->IsInFly() &&
+ GetFrameFormat().GetFollowTextFlow().GetValue() ) )
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+
+ // #i81146# new loop control
+ int nLoopControlRuns = 0;
+ const int nLoopControlMax = 10;
+
+ // RotateFlyFrame3 - outer frame
+ const double fRotation(getLocalFrameRotation());
+ const bool bRotated(!basegfx::fTools::equalZero(fRotation));
+
+ if(bRotated)
+ {
+ // Re-layout may be partially (see all isFrameAreaDefinitionValid() flags),
+ // so resetting the local SwFrame(s) in the local SwFrameAreaDefinition is
+ // needed. Reset to BoundAreas will be done below automatically
+ if(isTransformableSwFrame())
+ {
+ getTransformableSwFrame()->restoreFrameAreas();
+ }
+ }
+
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() || m_bFormatHeightOnly || !m_bValidContentPos )
+ {
+ SwRectFnSet aRectFnSet(this);
+ const SwFormatFrameSize *pSz;
+ { // Additional scope, so aAccess will be destroyed before the check!
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ pSz = &rAttrs.GetAttrSet().GetFrameSize();
+
+ // Only set when the flag is set!
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ MakePrtArea( rAttrs );
+ m_bValidContentPos = false;
+ }
+
+ if ( !isFrameAreaSizeValid() || m_bFormatHeightOnly )
+ {
+ setFrameAreaSizeValid(false);
+ Format( getRootFrame()->GetCurrShell()->GetOut(), &rAttrs );
+ m_bFormatHeightOnly = false;
+ }
+ }
+
+ if ( !isFrameAreaPositionValid() )
+ {
+ const Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
+ // #i26791# - use new method <MakeObjPos()>
+ // #i34753# - no positioning, if requested.
+ if ( IsNoMakePos() )
+ {
+ setFrameAreaPositionValid(true);
+ }
+ else
+ // #i26791# - use new method <MakeObjPos()>
+ MakeObjPos();
+ if( aOldPos == aRectFnSet.GetPos(getFrameArea()) )
+ {
+ if( !isFrameAreaPositionValid() && GetAnchorFrame()->IsInSct() &&
+ !GetAnchorFrame()->FindSctFrame()->isFrameAreaDefinitionValid() )
+ {
+ setFrameAreaPositionValid(true);
+ }
+ }
+ else
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+
+ if ( !m_bValidContentPos )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ MakeContentPos( rAttrs );
+ }
+
+ if ( isFrameAreaPositionValid() && isFrameAreaSizeValid() )
+ {
+ ++nLoopControlRuns;
+
+ OSL_ENSURE( nLoopControlRuns < nLoopControlMax, "LoopControl in SwFlyFreeFrame::MakeAll" );
+
+ if ( nLoopControlRuns < nLoopControlMax )
+ CheckClip( *pSz );
+ }
+ else
+ nLoopControlRuns = 0;
+ }
+
+ // RotateFlyFrame3 - outer frame
+ // Do not refresh transforms/Areas self here, this will be done
+ // when inner and outer frame are layouted, in SwNoTextFrame::MakeAll
+ if(bRotated)
+ {
+ // RotateFlyFrame3: Safe changes locally
+ // get center from outer frame (layout frame) to be on the safe side
+ const Point aCenter(getFrameArea().Center());
+ const basegfx::B2DPoint aB2DCenter(aCenter.X(), aCenter.Y());
+
+ if(!mpTransformableSwFrame)
+ {
+ mpTransformableSwFrame.reset(new TransformableSwFrame(*this));
+ }
+
+ getTransformableSwFrame()->createFrameAreaTransformations(
+ fRotation,
+ aB2DCenter);
+ getTransformableSwFrame()->adaptFrameAreasToTransformations();
+ }
+ else
+ {
+ // RotateFlyFrame3: Also need to clear ContourCache (if used),
+ // usually done in SwFlyFrame::NotifyDrawObj, but there relies on
+ // being in transform mode which is already reset then
+ if(isTransformableSwFrame())
+ {
+ ::ClrContourCache(GetVirtDrawObj());
+ }
+
+ // reset transformations to show that they are not used
+ mpTransformableSwFrame.reset();
+ }
+
+ Unlock();
+
+#if OSL_DEBUG_LEVEL > 0
+ SwRectFnSet aRectFnSet(this);
+ OSL_ENSURE( m_bHeightClipped || ( aRectFnSet.GetHeight(getFrameArea()) > 0 &&
+ aRectFnSet.GetHeight(getFramePrintArea()) > 0),
+ "SwFlyFreeFrame::Format(), flipping Fly." );
+
+#endif
+}
+
+bool SwFlyFreeFrame::supportsAutoContour() const
+{
+ static bool bOverrideHandleContourToAlwaysOff(true); // loplugin:constvars:ignore
+
+ // RotateFlyFrameFix: For LO6.0 we need to deactivate the AutoContour feature again, it is simply
+ // not clear how/if to use and save/load it in ODF. This has to be discussed.
+ // The reason not to remove is that this may be used as-is now, using a new switch.
+ // Even when not, the detection if it is possible will be needed in any case later.
+ if(bOverrideHandleContourToAlwaysOff)
+ {
+ return false;
+ }
+
+ if(!isTransformableSwFrame())
+ {
+ // support only when transformed, else there is no free space
+ return false;
+ }
+
+ // Check for Borders. If we have Borders, do (currently) not support,
+ // since borders do not transform with the object.
+ // (Will need to be enhanced to take into account if we have Borders and if these
+ // transform with the object)
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), this);
+ const SwBorderAttrs &rAttrs(*aAccess.Get());
+
+ if(rAttrs.IsLine())
+ {
+ return false;
+ }
+
+ // Check for Padding. Do not support when padding is used, this will
+ // produce a covered space around the object (filled with fill defines)
+ const SvxBoxItem* pBoxItem(nullptr);
+
+ if(GetFormat() && (pBoxItem = GetFormat()->GetItemIfSet(RES_BOX, false)))
+ {
+ if(pBoxItem->HasBorder(/*bTreatPaddingAsBorder*/true))
+ {
+ return false;
+ }
+ }
+
+ // check for Fill - if we have fill, it will fill the gaps and we will not
+ // support AutoContour
+ if(GetFormat() && GetFormat()->supportsFullDrawingLayerFillAttributeSet())
+ {
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(GetFormat()->getSdrAllFillAttributesHelper());
+
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ return false;
+ }
+ }
+ else
+ {
+ const std::unique_ptr<SvxBrushItem> aBack(GetFormat()->makeBackgroundBrushItem());
+
+ if(aBack->isUsed())
+ {
+ return false;
+ }
+ }
+
+ // else, support
+ return true;
+}
+
+// RotateFlyFrame3 - Support for Transformations - outer frame
+basegfx::B2DHomMatrix SwFlyFreeFrame::getFrameAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFrameAreaTransformation();
+ }
+
+ // call parent
+ return SwFlyFrame::getFrameAreaTransformation();
+}
+
+basegfx::B2DHomMatrix SwFlyFreeFrame::getFramePrintAreaTransformation() const
+{
+ if(isTransformableSwFrame())
+ {
+ // use pre-created transformation
+ return getTransformableSwFrame()->getLocalFramePrintAreaTransformation();
+ }
+
+ // call parent
+ return SwFlyFrame::getFramePrintAreaTransformation();
+}
+
+// RotateFlyFrame3 - Support for Transformations
+void SwFlyFreeFrame::transform_translate(const Point& rOffset)
+{
+ // call parent - this will do the basic transform for SwRect(s)
+ // in the SwFrameAreaDefinition
+ SwFlyFrame::transform_translate(rOffset);
+
+ // check if the Transformations need to be adapted
+ if(isTransformableSwFrame())
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ rOffset.X(), rOffset.Y()));
+
+ // transform using TransformableSwFrame
+ getTransformableSwFrame()->transform(aTransform);
+ }
+}
+
+// RotateFlyFrame3 - outer frame
+double getLocalFrameRotation_from_SwNoTextFrame(const SwNoTextFrame& rNoTextFrame)
+{
+ return rNoTextFrame.getLocalFrameRotation();
+}
+
+double SwFlyFreeFrame::getLocalFrameRotation() const
+{
+ // SwLayoutFrame::Lower() != SwFrame::GetLower(), but SwFrame::GetLower()
+ // calls SwLayoutFrame::Lower() when it's a SwLayoutFrame - so use GetLower()
+ const SwNoTextFrame* pSwNoTextFrame(dynamic_cast< const SwNoTextFrame* >(GetLower()));
+
+ if(nullptr != pSwNoTextFrame)
+ {
+ return getLocalFrameRotation_from_SwNoTextFrame(*pSwNoTextFrame);
+ }
+
+ // no rotation
+ return 0.0;
+}
+
+/** determines, if direct environment of fly frame has 'auto' size
+
+ #i17297#
+ start with anchor frame and search via <GetUpper()> for a header, footer,
+ row or fly frame stopping at page frame.
+ return <true>, if such a frame is found and it has 'auto' size.
+ otherwise <false> is returned.
+
+ @return boolean indicating, that direct environment has 'auto' size
+*/
+bool SwFlyFreeFrame::HasEnvironmentAutoSize() const
+{
+ bool bRetVal = false;
+
+ const SwFrame* pToBeCheckedFrame = GetAnchorFrame();
+ while ( pToBeCheckedFrame &&
+ !pToBeCheckedFrame->IsPageFrame() )
+ {
+ if ( pToBeCheckedFrame->IsHeaderFrame() ||
+ pToBeCheckedFrame->IsFooterFrame() ||
+ pToBeCheckedFrame->IsRowFrame() ||
+ pToBeCheckedFrame->IsFlyFrame() )
+ {
+ bRetVal = SwFrameSize::Fixed !=
+ pToBeCheckedFrame->GetAttrSet()->GetFrameSize().GetHeightSizeType();
+ break;
+ }
+ else
+ {
+ pToBeCheckedFrame = pToBeCheckedFrame->GetUpper();
+ }
+ }
+
+ return bRetVal;
+}
+
+void SwFlyFreeFrame::CheckClip( const SwFormatFrameSize &rSz )
+{
+ // It's probably time now to take appropriate measures, if the Fly
+ // doesn't fit into its surrounding.
+ // First, the Fly gives up its position, then it's formatted.
+ // Only if it still doesn't fit after giving up its position, the
+ // width or height are given up as well. The frame will be squeezed
+ // as much as needed.
+
+ const SwVirtFlyDrawObj *pObj = GetVirtDrawObj();
+ SwRect aClip, aTmpStretch;
+ ::CalcClipRect( pObj, aClip );
+ ::CalcClipRect( pObj, aTmpStretch, false );
+ aClip.Intersection_( aTmpStretch );
+
+ const tools::Long nBot = getFrameArea().Top() + getFrameArea().Height();
+ const tools::Long nRig = getFrameArea().Left() + getFrameArea().Width();
+ const tools::Long nClipBot = aClip.Top() + aClip.Height();
+ const tools::Long nClipRig = aClip.Left() + aClip.Width();
+
+ const bool bBot = nBot > nClipBot;
+ const bool bRig = nRig > nClipRig;
+ if (( bBot || bRig ) && !IsDraggingOffPageAllowed(FindFrameFormat(GetDrawObj())))
+ {
+ bool bAgain = false;
+ // #i37068# - no move, if it's requested
+ if ( bBot && !IsNoMoveOnCheckClip() &&
+ !GetDrawObjs() && !GetAnchorFrame()->IsInTab() )
+ {
+ SwFrame* pHeader = FindFooterOrHeader();
+ // In a header, correction of the position is no good idea.
+ // If the fly moves, some paragraphs have to be formatted, this
+ // could cause a change of the height of the headerframe,
+ // now the flyframe can change its position and so on ...
+ if ( !pHeader || !pHeader->IsHeaderFrame() )
+ {
+ const tools::Long nOld = getFrameArea().Top();
+
+ // tdf#112443 disable positioning if content is completely off page
+ bool bDisableOffPagePositioning = GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ if ( !bDisableOffPagePositioning || nOld <= nClipBot)
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setY( std::max( aClip.Top(), nClipBot - aFrm.Height() ) );
+ }
+
+ if ( getFrameArea().Top() != nOld )
+ {
+ bAgain = true;
+ }
+
+ m_bHeightClipped = true;
+ }
+ }
+ if ( bRig )
+ {
+ const tools::Long nOld = getFrameArea().Left();
+
+ // tdf#112443 disable positioning if content is completely off page
+ bool bDisableOffPagePositioning = GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ if ( !bDisableOffPagePositioning || nOld <= nClipRig )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX( std::max( aClip.Left(), nClipRig - aFrm.Width() ) );
+ }
+
+ if ( getFrameArea().Left() != nOld )
+ {
+ const SwFormatHoriOrient &rH = GetFormat()->GetHoriOrient();
+ // Left-aligned ones may not be moved to the left when they
+ // are avoiding another one.
+ if( rH.GetHoriOrient() == text::HoriOrientation::LEFT )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX( nOld );
+ }
+ else
+ {
+ bAgain = true;
+ }
+ }
+ m_bWidthClipped = true;
+ }
+ if ( bAgain )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ else
+ {
+ // If we reach this branch, the Frame protrudes into forbidden
+ // areas, and correcting the position is not allowed or not
+ // possible or not required.
+
+ // For Flys with OLE objects as lower, we make sure that
+ // we always resize proportionally
+ Size aOldSize( getFrameArea().SSize() );
+
+ // First, setup the FrameRect, then transfer it to the Frame.
+ SwRect aFrameRect( getFrameArea() );
+
+ if ( bBot )
+ {
+ tools::Long nDiff = nClipBot;
+ nDiff -= aFrameRect.Top(); // nDiff represents the available distance
+ nDiff = aFrameRect.Height() - nDiff;
+ aFrameRect.Height( aFrameRect.Height() - nDiff );
+ m_bHeightClipped = true;
+ }
+ if ( bRig )
+ {
+ tools::Long nDiff = nClipRig;
+ nDiff -= aFrameRect.Left();// nDiff represents the available distance
+ nDiff = aFrameRect.Width() - nDiff;
+ aFrameRect.Width( aFrameRect.Width() - nDiff );
+ m_bWidthClipped = true;
+ }
+
+ // #i17297# - no proportional
+ // scaling of graphics in environments, which determines its size
+ // by its content ('auto' size). Otherwise layout loops can occur and
+ // layout sizes of the environment can be incorrect.
+ // Such environment are:
+ // (1) header and footer frames with 'auto' size
+ // (2) table row frames with 'auto' size
+ // (3) fly frames with 'auto' size
+ // Note: section frames seems to be not critical - didn't found
+ // any critical layout situation so far.
+ if ( Lower() && Lower()->IsNoTextFrame() &&
+ (static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() ||
+ !HasEnvironmentAutoSize() ) )
+ {
+ // If width and height got adjusted, then the bigger
+ // change is relevant.
+ if ( aFrameRect.Width() != aOldSize.Width() &&
+ aFrameRect.Height()!= aOldSize.Height() )
+ {
+ if ( (aOldSize.Width() - aFrameRect.Width()) >
+ (aOldSize.Height()- aFrameRect.Height()) )
+ aFrameRect.Height( aOldSize.Height() );
+ else
+ aFrameRect.Width( aOldSize.Width() );
+ }
+
+ // Adjusted the width? change height proportionally
+ if( aFrameRect.Width() != aOldSize.Width() )
+ {
+ aFrameRect.Height( aFrameRect.Width() * aOldSize.Height() /
+ aOldSize.Width() );
+ m_bHeightClipped = true;
+ }
+ // Adjusted the height? change width proportionally
+ else if( aFrameRect.Height() != aOldSize.Height() )
+ {
+ aFrameRect.Width( aFrameRect.Height() * aOldSize.Width() /
+ aOldSize.Height() );
+ m_bWidthClipped = true;
+ }
+
+ // #i17297# - reactivate change
+ // of size attribute for fly frames containing an ole object.
+
+ // Added the aFrameRect.HasArea() hack, because
+ // the environment of the ole object does not have to be valid
+ // at this moment, or even worse, it does not have to have a
+ // reasonable size. In this case we do not want to change to
+ // attributes permanently. Maybe one day somebody dares to remove
+ // this code.
+ if ( aFrameRect.HasArea() &&
+ static_cast<SwNoTextFrame*>(Lower())->GetNode()->GetOLENode() &&
+ ( m_bWidthClipped || m_bHeightClipped ) )
+ {
+ SwFlyFrameFormat *pFormat = GetFormat();
+ pFormat->LockModify();
+ SwFormatFrameSize aFrameSize( rSz );
+ aFrameSize.SetWidth( aFrameRect.Width() );
+ aFrameSize.SetHeight( aFrameRect.Height() );
+ pFormat->SetFormatAttr( aFrameSize );
+ pFormat->UnlockModify();
+ }
+ }
+
+ // Now change the Frame; for columns, we put the new values into the attributes,
+ // otherwise we'll end up with unwanted side-effects/oscillations
+ const tools::Long nPrtHeightDiff = getFrameArea().Height() - getFramePrintArea().Height();
+ const tools::Long nPrtWidthDiff = getFrameArea().Width() - getFramePrintArea().Width();
+ maUnclippedFrame = getFrameArea();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( aFrameRect.Height() );
+ aFrm.Width ( std::max( tools::Long(MINLAY), aFrameRect.Width() ) );
+ }
+
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ ColLock(); //lock grow/shrink
+ const Size aTmpOldSize( getFramePrintArea().SSize() );
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( getFrameArea().Height() - nPrtHeightDiff );
+ aPrt.Width ( getFrameArea().Width() - nPrtWidthDiff );
+ }
+
+ ChgLowersProp( aTmpOldSize );
+ SwFrame *pLow = Lower();
+ do
+ {
+ pLow->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // also calculate the (Column)BodyFrame
+ static_cast<SwLayoutFrame*>(pLow)->Lower()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pLow = pLow->GetNext();
+ } while ( pLow );
+ ::CalcContent( this );
+ ColUnlock();
+
+ if ( !isFrameAreaSizeValid() && !m_bWidthClipped )
+ {
+ setFrameAreaSizeValid(true);
+ m_bFormatHeightOnly = true;
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( getFrameArea().Height() - nPrtHeightDiff );
+ aPrt.Width ( getFrameArea().Width() - nPrtWidthDiff );
+ }
+ }
+ }
+
+ // #i26945#
+ OSL_ENSURE( getFrameArea().Height() >= 0,
+ "<SwFlyFreeFrame::CheckClip(..)> - fly frame has negative height now." );
+}
+
+/** method to determine, if a <MakeAll()> on the Writer fly frame is possible
+ #i43771#
+*/
+bool SwFlyFreeFrame::IsFormatPossible() const
+{
+ return SwFlyFrame::IsFormatPossible() &&
+ ( GetPageFrame() ||
+ ( GetAnchorFrame() && GetAnchorFrame()->IsInFly() ) );
+}
+
+SwFlyLayFrame::SwFlyLayFrame( SwFlyFrameFormat *pFormat, SwFrame* pSib, SwFrame *pAnch ) :
+ SwFlyFreeFrame( pFormat, pSib, pAnch )
+{
+ m_bLayout = true;
+}
+
+void SwFlyLayFrame::RegisterAtPage(SwPageFrame & rPageFrame)
+{
+ assert(GetPageFrame() != &rPageFrame);
+ if (GetPageFrame())
+ {
+ GetPageFrame()->MoveFly( this, &rPageFrame );
+ }
+ else
+ {
+ rPageFrame.AppendFlyToPage( this );
+ }
+}
+
+// #i28701#
+
+void SwFlyLayFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pNew)
+ return;
+ const auto pAnch = GetAnchorFromPoolItem(*pLegacy->m_pNew);
+
+ if(!pAnch)
+ {
+ SwFlyFrame::SwClientNotify(rMod, rHint);
+ return;
+ }
+ SAL_WARN_IF(pAnch->GetAnchorId() == GetFormat()->GetAnchor().GetAnchorId(), "sw.core", "Invalid change of anchor type.");
+
+ // Unregister, get hold of the page, attach to the corresponding LayoutFrame.
+ SwRect aOld(GetObjRectWithSpaces());
+ // #i28701# - use new method <GetPageFrame()>
+ SwPageFrame* pOldPage = GetPageFrame();
+ AnchorFrame()->RemoveFly(this);
+
+ if(RndStdIds::FLY_AT_PAGE == pAnch->GetAnchorId())
+ {
+ SwRootFrame* pRoot = getRootFrame();
+ SwPageFrame* pTmpPage = static_cast<SwPageFrame*>(pRoot->Lower());
+ sal_uInt16 nPagesToFlip = pAnch->GetPageNum()-1;
+ while(pTmpPage && nPagesToFlip)
+ {
+ pTmpPage = static_cast<SwPageFrame*>(pTmpPage->GetNext());
+ --nPagesToFlip;
+ }
+ if(pTmpPage && !nPagesToFlip)
+ {
+ // #i50432# - adjust synopsis of <PlaceFly(..)>
+ pTmpPage->PlaceFly(this, nullptr);
+ }
+ if(!pTmpPage)
+ {
+ pRoot->SetAssertFlyPages();
+ pRoot->AssertFlyPages();
+ }
+ }
+ else
+ {
+ SwNodeIndex aIdx(pAnch->GetContentAnchor()->nNode);
+ SwContentFrame* pContent = GetFormat()->GetDoc()->GetNodes().GoNext(&aIdx)->
+ GetContentNode()->getLayoutFrame(getRootFrame(), nullptr, nullptr);
+ if(pContent)
+ {
+ SwFlyFrame *pTmp = pContent->FindFlyFrame();
+ if(pTmp)
+ pTmp->AppendFly(this);
+ }
+ }
+ // #i28701# - use new method <GetPageFrame()>
+ if ( pOldPage && pOldPage != GetPageFrame() )
+ NotifyBackground( pOldPage, aOld, PrepareHint::FlyFrameLeave );
+ SetCompletePaint();
+ InvalidateAll();
+ SetNotifyBack();
+}
+
+void SwPageFrame::AppendFlyToPage( SwFlyFrame *pNew )
+{
+ if ( !pNew->GetVirtDrawObj()->IsInserted() )
+ getRootFrame()->GetDrawPage()->InsertObject(
+ static_cast<SdrObject*>(pNew->GetVirtDrawObj()),
+ pNew->GetVirtDrawObj()->GetReferencedObj().GetOrdNumDirect() );
+
+ InvalidateSpelling();
+ InvalidateSmartTags();
+ InvalidateAutoCompleteWords();
+ InvalidateWordCount();
+
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->SetIdleFlags();
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+
+ SdrObject* pObj = pNew->GetVirtDrawObj();
+ OSL_ENSURE( pNew->GetAnchorFrame(), "Fly without Anchor" );
+ SwFlyFrame* pFly = const_cast<SwFlyFrame*>(pNew->GetAnchorFrame()->FindFlyFrame());
+ if ( pFly && pObj->GetOrdNum() < pFly->GetVirtDrawObj()->GetOrdNum() )
+ {
+ //#i119945# set pFly's OrdNum to _rNewObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
+ sal_uInt32 nNewNum = pObj->GetOrdNumDirect();
+ SdrObject* pDrawObj = nullptr;
+ if (auto pFormat = pFly->GetFormat())
+ if (auto pShapeFormat = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_FLYFRMFMT))
+ pDrawObj = pShapeFormat->FindRealSdrObject();
+
+ if (pDrawObj)
+ {
+ if (auto pPage = pDrawObj->getSdrPageFromSdrObject())
+ pPage->SetObjectOrdNum(pDrawObj->GetOrdNumDirect(), nNewNum);
+ else
+ pDrawObj->SetOrdNum(nNewNum);
+ }
+
+ if ( pObj->getSdrPageFromSdrObject() )
+ pObj->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(), nNewNum + (pDrawObj ? 1 : 0) );
+ else
+ pFly->GetVirtDrawObj()->SetOrdNum( nNewNum + (pDrawObj ? 1 : 0));
+ }
+
+ // Don't look further at Flys that sit inside the Content.
+ if ( pNew->IsFlyInContentFrame() )
+ InvalidateFlyInCnt();
+ else
+ {
+ InvalidateFlyContent();
+
+ if ( !m_pSortedObjs )
+ {
+ m_pSortedObjs.reset(new SwSortedObjs());
+ }
+
+ const bool bSuccessInserted = m_pSortedObjs->Insert( *pNew );
+ OSL_ENSURE( bSuccessInserted, "Fly not inserted in Sorted." );
+
+ // #i87493#
+ OSL_ENSURE( pNew->GetPageFrame() == nullptr || pNew->GetPageFrame() == this,
+ "<SwPageFrame::AppendFlyToPage(..)> - anchored fly frame seems to be registered at another page frame. Serious defect." );
+ // #i28701# - use new method <SetPageFrame(..)>
+ pNew->SetPageFrame( this );
+ pNew->InvalidatePage( this );
+ // #i28701#
+ pNew->UnlockPosition();
+ // needed to reposition at-page anchored flys moved from different page
+ pNew->InvalidateObjPos();
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->AddAccessibleFrame( pNew );
+ }
+#endif
+ }
+
+ // #i28701# - correction: consider also drawing objects
+ if ( !pNew->GetDrawObjs() )
+ return;
+
+ SwSortedObjs &rObjs = *pNew->GetDrawObjs();
+ for (SwAnchoredObject* pTmpObj : rObjs)
+ {
+ if ( auto pTmpFly = pTmpObj->DynCastFlyFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ if ( pTmpFly->IsFlyFreeFrame() && !pTmpFly->GetPageFrame() )
+ AppendFlyToPage( pTmpFly );
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pTmpObj) != nullptr )
+ {
+ // #i87493#
+ if ( pTmpObj->GetPageFrame() != this )
+ {
+ if ( pTmpObj->GetPageFrame() != nullptr )
+ {
+ pTmpObj->GetPageFrame()->RemoveDrawObjFromPage( *pTmpObj );
+ }
+ AppendDrawObjToPage( *pTmpObj );
+ }
+ }
+ }
+}
+
+void SwPageFrame::RemoveFlyFromPage( SwFlyFrame *pToRemove )
+{
+ const sal_uInt32 nOrdNum = pToRemove->GetVirtDrawObj()->GetOrdNum();
+ getRootFrame()->GetDrawPage()->RemoveObject( nOrdNum );
+ pToRemove->GetVirtDrawObj()->ReferencedObj().SetOrdNum( nOrdNum );
+
+ if ( GetUpper() )
+ {
+ if ( !pToRemove->IsFlyInContentFrame() )
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+
+ // Don't look further at Flys that sit inside the Content.
+ if ( pToRemove->IsFlyInContentFrame() )
+ return;
+
+ // Don't delete collections just yet. This will happen at the end of the
+ // action in the RemoveSuperfluous of the page, kicked off by a method of
+ // the same name in the root.
+ // The FlyColl might be gone already, because the page's dtor is being
+ // executed.
+ // Remove it _before_ disposing accessible frames to avoid accesses to
+ // the Frame from event handlers.
+ if (m_pSortedObjs)
+ {
+ m_pSortedObjs->Remove(*pToRemove);
+ if (!m_pSortedObjs->size())
+ {
+ m_pSortedObjs.reset();
+ }
+ }
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->DisposeAccessibleFrame( pToRemove, true );
+ }
+#endif
+
+ // #i28701# - use new method <SetPageFrame(..)>
+ pToRemove->SetPageFrame( nullptr );
+}
+
+void SwPageFrame::MoveFly( SwFlyFrame *pToMove, SwPageFrame *pDest )
+{
+ // Invalidations
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->SetIdleFlags();
+ if ( !pToMove->IsFlyInContentFrame() && pDest->GetPhyPageNum() < GetPhyPageNum() )
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ }
+
+ pDest->InvalidateSpelling();
+ pDest->InvalidateSmartTags();
+ pDest->InvalidateAutoCompleteWords();
+ pDest->InvalidateWordCount();
+
+ if ( pToMove->IsFlyInContentFrame() )
+ {
+ pDest->InvalidateFlyInCnt();
+ return;
+ }
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->DisposeAccessibleFrame( pToMove, true );
+ }
+#endif
+
+ // The FlyColl might be gone already, because the page's dtor is being executed.
+ if ( m_pSortedObjs )
+ {
+ m_pSortedObjs->Remove( *pToMove );
+ if ( !m_pSortedObjs->size() )
+ {
+ m_pSortedObjs.reset();
+ }
+ }
+
+ // Register
+ if ( !pDest->GetSortedObjs() )
+ pDest->m_pSortedObjs.reset(new SwSortedObjs());
+
+ const bool bSuccessInserted = pDest->GetSortedObjs()->Insert( *pToMove );
+ OSL_ENSURE( bSuccessInserted, "Fly not inserted in Sorted." );
+
+ // #i28701# - use new method <SetPageFrame(..)>
+ pToMove->SetPageFrame( pDest );
+ pToMove->InvalidatePage( pDest );
+ pToMove->SetNotifyBack();
+ pDest->InvalidateFlyContent();
+ // #i28701#
+ pToMove->UnlockPosition();
+
+ // Notify accessible layout. That's required at this place for
+ // frames only where the anchor is moved. Creation of new frames
+ // is additionally handled by the SwFrameNotify class.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( GetUpper() &&
+ static_cast< SwRootFrame * >( GetUpper() )->IsAnyShellAccessible() &&
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell() )
+ {
+ static_cast< SwRootFrame * >( GetUpper() )->GetCurrShell()->Imp()
+ ->AddAccessibleFrame( pToMove );
+ }
+#endif
+
+ // #i28701# - correction: move lowers of Writer fly frame
+ if ( !pToMove->GetDrawObjs() )
+ return;
+
+ SwSortedObjs &rObjs = *pToMove->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyFreeFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ SwPageFrame* pPageFrame = pFly->GetPageFrame();
+ if ( pPageFrame )
+ pPageFrame->MoveFly( pFly, pDest );
+ else
+ pDest->AppendFlyToPage( pFly );
+ }
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
+ {
+ RemoveDrawObjFromPage( *pObj );
+ pDest->AppendDrawObjToPage( *pObj );
+ }
+ }
+}
+
+void SwPageFrame::AppendDrawObjToPage( SwAnchoredObject& _rNewObj )
+{
+ if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rNewObj) == nullptr )
+ {
+ OSL_FAIL( "SwPageFrame::AppendDrawObjToPage(..) - anchored object of unexpected type -> object not appended" );
+ return;
+ }
+
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+
+ assert(_rNewObj.GetAnchorFrame());
+ SwFlyFrame* pFlyFrame = const_cast<SwFlyFrame*>(_rNewObj.GetAnchorFrame()->FindFlyFrame());
+ if ( pFlyFrame &&
+ _rNewObj.GetDrawObj()->GetOrdNum() < pFlyFrame->GetVirtDrawObj()->GetOrdNum() )
+ {
+ //#i119945# set pFly's OrdNum to _rNewObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
+ sal_uInt32 nNewNum = _rNewObj.GetDrawObj()->GetOrdNumDirect();
+ if ( _rNewObj.GetDrawObj()->getSdrPageFromSdrObject() )
+ _rNewObj.DrawObj()->getSdrPageFromSdrObject()->SetObjectOrdNum( pFlyFrame->GetVirtDrawObj()->GetOrdNumDirect(), nNewNum );
+ else
+ pFlyFrame->GetVirtDrawObj()->SetOrdNum( nNewNum );
+ }
+
+ if ( RndStdIds::FLY_AS_CHAR == _rNewObj.GetFrameFormat().GetAnchor().GetAnchorId() )
+ {
+ return;
+ }
+
+ if ( !m_pSortedObjs )
+ {
+ m_pSortedObjs.reset(new SwSortedObjs());
+ }
+ if ( !m_pSortedObjs->Insert( _rNewObj ) )
+ {
+ OSL_ENSURE( m_pSortedObjs->Contains( _rNewObj ),
+ "Drawing object not appended into list <pSortedObjs>." );
+ }
+ // #i87493#
+ OSL_ENSURE( _rNewObj.GetPageFrame() == nullptr || _rNewObj.GetPageFrame() == this,
+ "<SwPageFrame::AppendDrawObjToPage(..)> - anchored draw object seems to be registered at another page frame. Serious defect." );
+ _rNewObj.SetPageFrame( this );
+
+ // invalidate page in order to force a reformat of object layout of the page.
+ InvalidateFlyLayout();
+}
+
+void SwPageFrame::RemoveDrawObjFromPage( SwAnchoredObject& _rToRemoveObj )
+{
+ if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rToRemoveObj) == nullptr )
+ {
+ OSL_FAIL( "SwPageFrame::RemoveDrawObjFromPage(..) - anchored object of unexpected type -> object not removed" );
+ return;
+ }
+
+ if ( m_pSortedObjs )
+ {
+ m_pSortedObjs->Remove( _rToRemoveObj );
+ if ( !m_pSortedObjs->size() )
+ {
+ m_pSortedObjs.reset();
+ }
+ if ( GetUpper() )
+ {
+ if (RndStdIds::FLY_AS_CHAR !=
+ _rToRemoveObj.GetFrameFormat().GetAnchor().GetAnchorId())
+ {
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ InvalidatePage();
+ }
+ static_cast<SwRootFrame*>(GetUpper())->InvalidateBrowseWidth();
+ }
+ }
+ _rToRemoveObj.SetPageFrame( nullptr );
+}
+
+// #i50432# - adjust method description and synopsis.
+void SwPageFrame::PlaceFly( SwFlyFrame* pFly, SwFlyFrameFormat* pFormat )
+{
+ // #i50432# - consider the case that page is an empty page:
+ // In this case append the fly frame at the next page
+ OSL_ENSURE( !IsEmptyPage() || GetNext(),
+ "<SwPageFrame::PlaceFly(..)> - empty page with no next page! -> fly frame appended at empty page" );
+ if ( IsEmptyPage() && GetNext() )
+ {
+ static_cast<SwPageFrame*>(GetNext())->PlaceFly( pFly, pFormat );
+ }
+ else
+ {
+ // If we received a Fly, we use that one. Otherwise, create a new
+ // one using the Format.
+ if ( pFly )
+ AppendFly( pFly );
+ else
+ {
+ OSL_ENSURE( pFormat, ":-( No Format given for Fly." );
+ pFly = new SwFlyLayFrame( pFormat, this, this );
+ AppendFly( pFly );
+ ::RegistFlys( this, pFly );
+ }
+ }
+}
+
+// #i18732# - adjustments for following text flow or not
+// AND alignment at 'page areas' for to paragraph/to character anchored objects
+// #i22305# - adjustment for following text flow for to frame anchored objects
+// #i29778# - Because calculating the floating screen object's position
+// (Writer fly frame or drawing object) doesn't perform a calculation on its
+// upper frames and its anchor frame, a calculation of the upper frames in this
+// method is no longer sensible.
+// #i28701# - if document compatibility option 'Consider wrapping style influence
+// on object positioning' is ON, the clip area corresponds to the one as the
+// object doesn't follow the text flow.
+bool CalcClipRect( const SdrObject *pSdrObj, SwRect &rRect, bool bMove )
+{
+ bool bRet = true;
+ if ( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(pSdrObj) )
+ {
+ const SwFlyFrame* pFly = pVirtFlyDrawObj->GetFlyFrame();
+ const bool bFollowTextFlow = pFly->GetFormat()->GetFollowTextFlow().GetValue();
+ // #i28701#
+ const bool bConsiderWrapOnObjPos =
+ pFly->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
+ const SwFormatVertOrient &rV = pFly->GetFormat()->GetVertOrient();
+ if( pFly->IsFlyLayFrame() )
+ {
+ const SwFrame* pClip;
+ // #i22305#
+ // #i28701#
+ if ( !bFollowTextFlow || bConsiderWrapOnObjPos )
+ {
+ pClip = pFly->GetAnchorFrame()->FindPageFrame();
+ }
+ else
+ {
+ pClip = pFly->GetAnchorFrame();
+ }
+
+ rRect = pClip->getFrameArea();
+ SwRectFnSet aRectFnSet(pClip);
+
+ // vertical clipping: Top and Bottom, also to PrtArea if necessary
+ if( rV.GetVertOrient() != text::VertOrientation::NONE &&
+ rV.GetRelationOrient() == text::RelOrientation::PRINT_AREA )
+ {
+ aRectFnSet.SetTop( rRect, aRectFnSet.GetPrtTop(*pClip) );
+ aRectFnSet.SetBottom( rRect, aRectFnSet.GetPrtBottom(*pClip) );
+ }
+ // horizontal clipping: Top and Bottom, also to PrtArea if necessary
+ const SwFormatHoriOrient &rH = pFly->GetFormat()->GetHoriOrient();
+ if( rH.GetHoriOrient() != text::HoriOrientation::NONE &&
+ rH.GetRelationOrient() == text::RelOrientation::PRINT_AREA )
+ {
+ aRectFnSet.SetLeft( rRect, aRectFnSet.GetPrtLeft(*pClip) );
+ aRectFnSet.SetRight(rRect, aRectFnSet.GetPrtRight(*pClip));
+ }
+ }
+ else if( pFly->IsFlyAtContentFrame() )
+ {
+ // #i18732# - consider following text flow or not
+ // AND alignment at 'page areas'
+ const SwFrame* pVertPosOrientFrame = pFly->GetVertPosOrientFrame();
+ if ( !pVertPosOrientFrame )
+ {
+ OSL_FAIL( "::CalcClipRect(..) - frame, vertical position is oriented at, is missing .");
+ pVertPosOrientFrame = pFly->GetAnchorFrame();
+ }
+
+ if ( !bFollowTextFlow || bConsiderWrapOnObjPos )
+ {
+ const SwLayoutFrame* pClipFrame = pVertPosOrientFrame->FindPageFrame();
+ if (!pClipFrame)
+ {
+ OSL_FAIL("!pClipFrame: "
+ "if you can reproduce this please file a bug");
+ return false;
+ }
+ rRect = bMove ? pClipFrame->GetUpper()->getFrameArea()
+ : pClipFrame->getFrameArea();
+ // #i26945# - consider that a table, during
+ // its format, can exceed its upper printing area bottom.
+ // Thus, enlarge the clip rectangle, if such a case occurred
+ if ( pFly->GetAnchorFrame()->IsInTab() )
+ {
+ const SwTabFrame* pTabFrame = const_cast<SwFlyFrame*>(pFly)
+ ->GetAnchorFrameContainingAnchPos()->FindTabFrame();
+ SwRect aTmp( pTabFrame->getFramePrintArea() );
+ aTmp += pTabFrame->getFrameArea().Pos();
+ rRect.Union( aTmp );
+ // #i43913# - consider also the cell frame
+ const SwFrame* pCellFrame = const_cast<SwFlyFrame*>(pFly)
+ ->GetAnchorFrameContainingAnchPos()->GetUpper();
+ while ( pCellFrame && !pCellFrame->IsCellFrame() )
+ {
+ pCellFrame = pCellFrame->GetUpper();
+ }
+ if ( pCellFrame )
+ {
+ aTmp = pCellFrame->getFramePrintArea();
+ aTmp += pCellFrame->getFrameArea().Pos();
+ rRect.Union( aTmp );
+ }
+ }
+ }
+ else if ( rV.GetRelationOrient() == text::RelOrientation::PAGE_FRAME ||
+ rV.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ // new class <SwEnvironmentOfAnchoredObject>
+ objectpositioning::SwEnvironmentOfAnchoredObject
+ aEnvOfObj( bFollowTextFlow );
+ const SwLayoutFrame& rVertClipFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pVertPosOrientFrame );
+ if ( rV.GetRelationOrient() == text::RelOrientation::PAGE_FRAME )
+ {
+ rRect = rVertClipFrame.getFrameArea();
+ }
+ else if ( rV.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ if ( rVertClipFrame.IsPageFrame() )
+ {
+ rRect = static_cast<const SwPageFrame&>(rVertClipFrame).PrtWithoutHeaderAndFooter();
+ }
+ else
+ {
+ rRect = rVertClipFrame.getFrameArea();
+ }
+ }
+ const SwLayoutFrame* pHoriClipFrame =
+ pFly->GetAnchorFrame()->FindPageFrame()->GetUpper();
+ SwRectFnSet aRectFnSet(pFly->GetAnchorFrame());
+ aRectFnSet.SetLeft( rRect, aRectFnSet.GetLeft(pHoriClipFrame->getFrameArea()) );
+ aRectFnSet.SetRight(rRect, aRectFnSet.GetRight(pHoriClipFrame->getFrameArea()));
+ }
+ else
+ {
+ // #i26945#
+ const SwFrame *pClip =
+ const_cast<SwFlyFrame*>(pFly)->GetAnchorFrameContainingAnchPos();
+ SwRectFnSet aRectFnSet(pClip);
+ const SwLayoutFrame *pUp = pClip->GetUpper();
+ const SwFrame *pCell = pUp->IsCellFrame() ? pUp : nullptr;
+ const SwFrameType nType = bMove
+ ? SwFrameType::Root | SwFrameType::Fly | SwFrameType::Header |
+ SwFrameType::Footer | SwFrameType::Ftn
+ : SwFrameType::Body | SwFrameType::Fly | SwFrameType::Header |
+ SwFrameType::Footer | SwFrameType::Cell| SwFrameType::Ftn;
+
+ while ( !(pUp->GetType() & nType) || pUp->IsColBodyFrame() )
+ {
+ pUp = pUp->GetUpper();
+ if ( !pCell && pUp->IsCellFrame() )
+ pCell = pUp;
+ }
+ if ( bMove && pUp->IsRootFrame() )
+ {
+ rRect = pUp->getFramePrintArea();
+ rRect += pUp->getFrameArea().Pos();
+ pUp = nullptr;
+ }
+ if ( pUp )
+ {
+ if ( pUp->GetType() & SwFrameType::Body )
+ {
+ const SwPageFrame *pPg;
+ if ( pUp->GetUpper() != (pPg = pFly->FindPageFrame()) )
+ pUp = pPg->FindBodyCont();
+ if (pUp)
+ {
+ rRect = pUp->GetUpper()->getFrameArea();
+ aRectFnSet.SetTop( rRect, aRectFnSet.GetPrtTop(*pUp) );
+ aRectFnSet.SetBottom(rRect, aRectFnSet.GetPrtBottom(*pUp));
+ }
+ }
+ else
+ {
+ if( ( pUp->GetType() & (SwFrameType::Fly | SwFrameType::Ftn ) ) &&
+ !pUp->getFrameArea().Contains( pFly->getFrameArea().Pos() ) )
+ {
+ if( pUp->IsFlyFrame() )
+ {
+ const SwFlyFrame *pTmpFly = static_cast<const SwFlyFrame*>(pUp);
+ while( pTmpFly->GetNextLink() )
+ {
+ pTmpFly = pTmpFly->GetNextLink();
+ if( pTmpFly->getFrameArea().Contains( pFly->getFrameArea().Pos() ) )
+ break;
+ }
+ pUp = pTmpFly;
+ }
+ else if( pUp->IsInFootnote() )
+ {
+ const SwFootnoteFrame *pTmp = pUp->FindFootnoteFrame();
+ while( pTmp->GetFollow() )
+ {
+ pTmp = pTmp->GetFollow();
+ if( pTmp->getFrameArea().Contains( pFly->getFrameArea().Pos() ) )
+ break;
+ }
+ pUp = pTmp;
+ }
+ }
+ rRect = pUp->getFramePrintArea();
+ rRect.Pos() += pUp->getFrameArea().Pos();
+ if ( pUp->GetType() & (SwFrameType::Header | SwFrameType::Footer) )
+ {
+ rRect.Left ( pUp->GetUpper()->getFrameArea().Left() );
+ rRect.Width( pUp->GetUpper()->getFrameArea().Width());
+ }
+ else if ( pUp->IsCellFrame() ) //MA_FLY_HEIGHT
+ {
+ const SwFrame *pTab = pUp->FindTabFrame();
+ aRectFnSet.SetBottom( rRect, aRectFnSet.GetPrtBottom(*pTab->GetUpper()) );
+ // expand to left and right cell border
+ rRect.Left ( pUp->getFrameArea().Left() );
+ rRect.Width( pUp->getFrameArea().Width() );
+ }
+ }
+ }
+ if ( pCell )
+ {
+ // CellFrames might also sit in unallowed areas. In this case,
+ // the Fly is allowed to do so as well
+ SwRect aTmp( pCell->getFramePrintArea() );
+ aTmp += pCell->getFrameArea().Pos();
+ rRect.Union( aTmp );
+ }
+ }
+ }
+ else
+ {
+ const SwFrame *pUp = pFly->GetAnchorFrame()->GetUpper();
+ SwRectFnSet aRectFnSet(pFly->GetAnchorFrame());
+ while( pUp->IsColumnFrame() || pUp->IsSctFrame() || pUp->IsColBodyFrame())
+ pUp = pUp->GetUpper();
+ rRect = pUp->getFrameArea();
+ if( !pUp->IsBodyFrame() )
+ {
+ rRect += pUp->getFramePrintArea().Pos();
+ rRect.SSize( pUp->getFramePrintArea().SSize() );
+ if ( pUp->IsCellFrame() )
+ {
+ const SwFrame *pTab = pUp->FindTabFrame();
+ aRectFnSet.SetBottom( rRect, aRectFnSet.GetPrtBottom(*pTab->GetUpper()) );
+ }
+ }
+ else if ( pUp->GetUpper()->IsPageFrame() )
+ {
+ // Objects anchored as character may exceed right margin
+ // of body frame:
+ aRectFnSet.SetRight( rRect, aRectFnSet.GetRight(pUp->GetUpper()->getFrameArea()) );
+ }
+ tools::Long nHeight = (9*aRectFnSet.GetHeight(rRect))/10;
+ tools::Long nTop;
+ const SwFormat *pFormat = GetUserCall(pSdrObj)->GetFormat();
+ const SvxULSpaceItem &rUL = pFormat->GetULSpace();
+ if( bMove )
+ {
+ nTop = aRectFnSet.IsVert() ? static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().X() :
+ static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().Y();
+ nTop = aRectFnSet.YInc( nTop, -nHeight );
+ tools::Long nWidth = aRectFnSet.GetWidth(pFly->getFrameArea());
+ aRectFnSet.SetLeftAndWidth( rRect, aRectFnSet.IsVert() ?
+ static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().Y() :
+ static_cast<const SwFlyInContentFrame*>(pFly)->GetRefPoint().X(), nWidth );
+ nHeight = 2*nHeight - rUL.GetLower() - rUL.GetUpper();
+ }
+ else
+ {
+ nTop = aRectFnSet.YInc( aRectFnSet.GetBottom(pFly->getFrameArea()),
+ rUL.GetLower() - nHeight );
+ nHeight = 2*nHeight - aRectFnSet.GetHeight(pFly->getFrameArea())
+ - rUL.GetLower() - rUL.GetUpper();
+ }
+ aRectFnSet.SetTopAndHeight( rRect, nTop, nHeight );
+ }
+ }
+ else
+ {
+ const SwDrawContact *pC = static_cast<const SwDrawContact*>(GetUserCall(pSdrObj));
+ const SwFrameFormat *pFormat = pC->GetFormat();
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( RndStdIds::FLY_AS_CHAR == rAnch.GetAnchorId() )
+ {
+ const SwFrame* pAnchorFrame = pC->GetAnchorFrame( pSdrObj );
+ if( !pAnchorFrame )
+ {
+ OSL_FAIL( "<::CalcClipRect(..)> - missing anchor frame." );
+ const_cast<SwDrawContact*>(pC)->ConnectToLayout();
+ pAnchorFrame = pC->GetAnchorFrame();
+ }
+ const SwFrame* pUp = pAnchorFrame->GetUpper();
+ rRect = pUp->getFramePrintArea();
+ rRect += pUp->getFrameArea().Pos();
+ SwRectFnSet aRectFnSet(pAnchorFrame);
+ tools::Long nHeight = (9*aRectFnSet.GetHeight(rRect))/10;
+ tools::Long nTop;
+ const SvxULSpaceItem &rUL = pFormat->GetULSpace();
+ SwRect aSnapRect( pSdrObj->GetSnapRect() );
+ tools::Long nTmpH = 0;
+ if( bMove )
+ {
+ nTop = aRectFnSet.YInc( aRectFnSet.IsVert() ? pSdrObj->GetAnchorPos().X() :
+ pSdrObj->GetAnchorPos().Y(), -nHeight );
+ tools::Long nWidth = aRectFnSet.GetWidth(aSnapRect);
+ aRectFnSet.SetLeftAndWidth( rRect, aRectFnSet.IsVert() ?
+ pSdrObj->GetAnchorPos().Y() :
+ pSdrObj->GetAnchorPos().X(), nWidth );
+ }
+ else
+ {
+ // #i26791# - value of <nTmpH> is needed to
+ // calculate value of <nTop>.
+ nTmpH = aRectFnSet.IsVert() ? pSdrObj->GetCurrentBoundRect().GetWidth() :
+ pSdrObj->GetCurrentBoundRect().GetHeight();
+ nTop = aRectFnSet.YInc( aRectFnSet.GetTop(aSnapRect),
+ rUL.GetLower() + nTmpH - nHeight );
+ }
+ nHeight = 2*nHeight - nTmpH - rUL.GetLower() - rUL.GetUpper();
+ aRectFnSet.SetTopAndHeight( rRect, nTop, nHeight );
+ }
+ else
+ {
+ // restrict clip rectangle for drawing
+ // objects in header/footer to the page frame.
+ // #i26791#
+ const SwFrame* pAnchorFrame = pC->GetAnchorFrame( pSdrObj );
+ if ( pAnchorFrame && pAnchorFrame->FindFooterOrHeader() )
+ {
+ // clip frame is the page frame the header/footer is on.
+ const SwFrame* pClipFrame = pAnchorFrame->FindPageFrame();
+ rRect = pClipFrame->getFrameArea();
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/flypos.cxx b/sw/source/core/layout/flypos.cxx
new file mode 100644
index 000000000..0a7ab2dca
--- /dev/null
+++ b/sw/source/core/layout/flypos.cxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fmtanchr.hxx>
+#include <ndindex.hxx>
+#include <frameformats.hxx>
+#include <svx/swframetypes.hxx>
+
+bool SwPosFlyFrameCmp::operator()(const SwPosFlyFramePtr& rA, const SwPosFlyFramePtr& rB) const
+{
+ if (rA->GetNdIndex() == rB->GetNdIndex())
+ {
+ // In this case, the order number decides!
+ return rA->GetOrdNum() < rB->GetOrdNum();
+ }
+
+ return rA->GetNdIndex() < rB->GetNdIndex();
+}
+
+SwPosFlyFrame::SwPosFlyFrame(const SwNodeIndex& rIdx, const SwFrameFormat* pFormat,
+ sal_uInt16 nArrPos)
+ : m_pFrameFormat(pFormat)
+ , m_pNodeIndex(const_cast<SwNodeIndex*>(&rIdx))
+ , m_nOrdNum(SAL_MAX_UINT32)
+{
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ m_pNodeIndex = new SwNodeIndex(rIdx);
+ else if (pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell())
+ pFormat->CallSwClientNotify(sw::GetZOrderHint(m_nOrdNum));
+ if (m_nOrdNum == SAL_MAX_UINT32)
+ {
+ m_nOrdNum = pFormat->GetDoc()->GetSpzFrameFormats()->size();
+ m_nOrdNum += nArrPos;
+ }
+}
+
+SwPosFlyFrame::~SwPosFlyFrame()
+{
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId())
+ {
+ delete m_pNodeIndex;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/frmtool.cxx b/sw/source/core/layout/frmtool.cxx
new file mode 100644
index 000000000..5a23118be
--- /dev/null
+++ b/sw/source/core/layout/frmtool.cxx
@@ -0,0 +1,4040 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <svx/svdpage.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <sal/log.hxx>
+#include <o3tl/deleter.hxx>
+#include <osl/diagnose.h>
+
+#include <drawdoc.hxx>
+#include <fmtornt.hxx>
+#include <fmthdft.hxx>
+#include <fmtfsize.hxx>
+#include <fmtsrnd.hxx>
+#include <docary.hxx>
+#include <lineinfo.hxx>
+#include <swmodule.hxx>
+#include <pagefrm.hxx>
+#include <colfrm.hxx>
+#include <fesh.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrms.hxx>
+#include <layact.hxx>
+#include <pagedesc.hxx>
+#include <section.hxx>
+#include <sectfrm.hxx>
+#include <node2lay.hxx>
+#include <ndole.hxx>
+#include <hints.hxx>
+#include "layhelp.hxx"
+#include <laycache.hxx>
+#include <rootfrm.hxx>
+#include <paratr.hxx>
+#include <redline.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <calbck.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentTimerAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <frameformats.hxx>
+#include <boost/circular_buffer.hpp>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+ // FIXME: would likely better be a member of SwRootFrame instead of a global flag
+ bool isFlyCreationSuppressed = false;
+}
+namespace sw {
+ FlyCreationSuppressor::FlyCreationSuppressor(bool wasAlreadySuppressedAllowed)
+ : m_wasAlreadySuppressed(isFlyCreationSuppressed)
+ {
+ (void)wasAlreadySuppressedAllowed;
+ assert(wasAlreadySuppressedAllowed || !isFlyCreationSuppressed);
+ isFlyCreationSuppressed = true;
+ }
+ FlyCreationSuppressor::~FlyCreationSuppressor()
+ {
+ isFlyCreationSuppressed = m_wasAlreadySuppressed;
+ }
+}
+
+bool bObjsDirect = true;
+bool bSetCompletePaintOnInvalidate = false;
+
+sal_uInt8 StackHack::s_nCnt = 0;
+bool StackHack::s_bLocked = false;
+
+SwFrameNotify::SwFrameNotify( SwFrame *pF ) :
+ mpFrame( pF ),
+ maFrame( pF->getFrameArea() ),
+ maPrt( pF->getFramePrintArea() ),
+ mbInvaKeep( false ),
+ mbValidSize( pF->isFrameAreaSizeValid() )
+{
+ if ( pF->IsTextFrame() )
+ {
+ mnFlyAnchorOfst = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( true );
+ mnFlyAnchorOfstNoWrap = static_cast<SwTextFrame*>(pF)->GetBaseOffsetForFly( false );
+ }
+ else
+ {
+ mnFlyAnchorOfst = 0;
+ mnFlyAnchorOfstNoWrap = 0;
+ }
+
+ mbHadFollow = pF->IsContentFrame() && static_cast<SwContentFrame*>(pF)->GetFollow();
+}
+
+SwFrameNotify::~SwFrameNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+void SwFrameNotify::ImplDestroy()
+{
+ SwRectFnSet aRectFnSet(mpFrame);
+ const bool bAbsP = aRectFnSet.PosDiff(maFrame, mpFrame->getFrameArea());
+ const bool bChgWidth =
+ aRectFnSet.GetWidth(maFrame) != aRectFnSet.GetWidth(mpFrame->getFrameArea());
+ const bool bChgHeight =
+ aRectFnSet.GetHeight(maFrame)!=aRectFnSet.GetHeight(mpFrame->getFrameArea());
+ const bool bChgFlyBasePos = mpFrame->IsTextFrame() &&
+ ( ( mnFlyAnchorOfst != static_cast<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( true ) ) ||
+ ( mnFlyAnchorOfstNoWrap != static_cast<SwTextFrame*>(mpFrame)->GetBaseOffsetForFly( false ) ) );
+
+ if ( mpFrame->IsFlowFrame() && !mpFrame->IsInFootnote() )
+ {
+ SwFlowFrame *pFlow = SwFlowFrame::CastFlowFrame( mpFrame );
+
+ if ( !pFlow->IsFollow() )
+ {
+ if ( !mpFrame->GetIndPrev() )
+ {
+ if ( mbInvaKeep )
+ {
+ SwFrame *pPre = mpFrame->FindPrev();
+ if ( pPre && pPre->IsFlowFrame() )
+ {
+ // 1. pPre wants to keep with me:
+ bool bInvalidPrePos = SwFlowFrame::CastFlowFrame(pPre)->IsKeep(pPre->GetAttrSet()->GetKeep(), pPre->GetBreakItem())
+ && pPre->GetIndPrev();
+
+ // 2. pPre is a table and the last row wants to keep with me:
+ if ( !bInvalidPrePos && pPre->IsTabFrame() )
+ {
+ SwTabFrame* pPreTab = static_cast<SwTabFrame*>(pPre);
+ if ( pPreTab->GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
+ {
+ SwRowFrame* pLastRow = static_cast<SwRowFrame*>(pPreTab->GetLastLower());
+ if ( pLastRow && pLastRow->ShouldRowKeepWithNext() )
+ bInvalidPrePos = true;
+ }
+ }
+
+ if ( bInvalidPrePos )
+ pPre->InvalidatePos();
+ }
+ }
+ }
+ else if ( !pFlow->HasFollow() )
+ {
+ tools::Long nOldHeight = aRectFnSet.GetHeight(maFrame);
+ tools::Long nNewHeight = aRectFnSet.GetHeight(mpFrame->getFrameArea());
+ if( (nOldHeight > nNewHeight) || (!nOldHeight && nNewHeight) )
+ pFlow->CheckKeep();
+ }
+ }
+ }
+
+ if ( bAbsP )
+ {
+ mpFrame->SetCompletePaint();
+
+ SwFrame* pNxt = mpFrame->GetIndNext();
+ // #121888# - skip empty section frames
+ while ( pNxt &&
+ pNxt->IsSctFrame() && !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ pNxt = pNxt->GetIndNext();
+ }
+
+ if ( pNxt )
+ pNxt->InvalidatePos();
+ else
+ {
+ // #104100# - correct condition for setting retouche
+ // flag for vertical layout.
+ if( mpFrame->IsRetoucheFrame() &&
+ aRectFnSet.TopDist( maFrame, aRectFnSet.GetTop(mpFrame->getFrameArea()) ) > 0 )
+ {
+ mpFrame->SetRetouche();
+ }
+
+ // A fresh follow frame does not have to be invalidated, because
+ // it is already formatted:
+ if ( mbHadFollow || !mpFrame->IsContentFrame() || !static_cast<SwContentFrame*>(mpFrame)->GetFollow() )
+ {
+ if ( !mpFrame->IsTabFrame() || !static_cast<SwTabFrame*>(mpFrame)->GetFollow() )
+ mpFrame->InvalidateNextPos();
+ }
+ }
+ }
+
+ //For each resize of the background graphics is a repaint necessary.
+ const bool bPrtWidth =
+ aRectFnSet.GetWidth(maPrt) != aRectFnSet.GetWidth(mpFrame->getFramePrintArea());
+ const bool bPrtHeight =
+ aRectFnSet.GetHeight(maPrt)!=aRectFnSet.GetHeight(mpFrame->getFramePrintArea());
+ if ( bPrtWidth || bPrtHeight )
+ {
+ bool bUseNewFillProperties(false);
+ if (mpFrame->supportsFullDrawingLayerFillAttributeSet())
+ {
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(mpFrame->getSdrAllFillAttributesHelper());
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ bUseNewFillProperties = true;
+ // use SetCompletePaint if needed
+ if(aFillAttributes->needCompleteRepaint())
+ {
+ mpFrame->SetCompletePaint();
+ }
+ }
+ }
+ if (!bUseNewFillProperties)
+ {
+ const SvxGraphicPosition ePos = mpFrame->GetAttrSet()->GetBackground().GetGraphicPos();
+ if(GPOS_NONE != ePos && GPOS_TILED != ePos)
+ mpFrame->SetCompletePaint();
+ }
+ }
+ else
+ {
+ // #97597# - consider case that *only* margins between
+ // frame and printing area has changed. Then, frame has to be repainted,
+ // in order to force paint of the margin areas.
+ if ( !bAbsP && (bChgWidth || bChgHeight) )
+ {
+ mpFrame->SetCompletePaint();
+ }
+ }
+
+ const bool bPrtP = aRectFnSet.PosDiff( maPrt, mpFrame->getFramePrintArea() );
+ if ( bAbsP || bPrtP || bChgWidth || bChgHeight ||
+ bPrtWidth || bPrtHeight || bChgFlyBasePos )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( mpFrame->IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = mpFrame->getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( mpFrame, maFrame );
+ }
+ }
+#endif
+
+ // Notification of anchored objects
+ if ( mpFrame->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *mpFrame->GetDrawObjs();
+ SwPageFrame* pPageFrame = nullptr;
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ // OD 2004-03-31 #i26791# - no general distinction between
+ // Writer fly frames and drawing objects
+ bool bNotify = false;
+ bool bNotifySize = false;
+ SwContact* pContact = ::GetUserCall( pObj->GetDrawObj() );
+ const bool bAnchoredAsChar = pContact->ObjAnchoredAsChar();
+ if ( !bAnchoredAsChar )
+ {
+ // Notify object, which aren't anchored as-character:
+
+ // always notify objects, if frame position has changed
+ // or if the object is to-page|to-fly anchored.
+ if ( bAbsP ||
+ pContact->ObjAnchoredAtPage() ||
+ pContact->ObjAnchoredAtFly() )
+ {
+ bNotify = true;
+
+ // assure that to-fly anchored Writer fly frames are
+ // registered at the correct page frame, if frame
+ // position has changed.
+ if ( bAbsP && pContact->ObjAnchoredAtFly() &&
+ pObj->DynCastFlyFrame() != nullptr )
+ {
+ // determine to-fly anchored Writer fly frame
+ SwFlyFrame* pFlyFrame = static_cast<SwFlyFrame*>(pObj);
+ // determine page frame of to-fly anchored
+ // Writer fly frame
+ SwPageFrame* pFlyPageFrame = pFlyFrame->FindPageFrame();
+ // determine page frame, if needed.
+ if ( !pPageFrame )
+ {
+ pPageFrame = mpFrame->FindPageFrame();
+ }
+ if ( pPageFrame != pFlyPageFrame )
+ {
+ OSL_ENSURE( pFlyPageFrame, "~SwFrameNotify: Fly from Nowhere" );
+ if( pFlyPageFrame )
+ pFlyPageFrame->MoveFly( pFlyFrame, pPageFrame );
+ else
+ pPageFrame->AppendFlyToPage( pFlyFrame );
+ }
+ }
+ }
+ // otherwise the objects are notified in dependence to
+ // its positioning and alignment
+ else
+ {
+ const SwFormatVertOrient& rVert =
+ pContact->GetFormat()->GetVertOrient();
+ if ( ( rVert.GetVertOrient() == text::VertOrientation::CENTER ||
+ rVert.GetVertOrient() == text::VertOrientation::BOTTOM ||
+ rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) &&
+ ( bChgHeight || bPrtHeight ) )
+ {
+ bNotify = true;
+ }
+ if ( !bNotify )
+ {
+ const SwFormatHoriOrient& rHori =
+ pContact->GetFormat()->GetHoriOrient();
+ if ( ( rHori.GetHoriOrient() != text::HoriOrientation::NONE ||
+ rHori.GetRelationOrient()== text::RelOrientation::PRINT_AREA ||
+ rHori.GetRelationOrient()== text::RelOrientation::FRAME ) &&
+ ( bChgWidth || bPrtWidth || bChgFlyBasePos ) )
+ {
+ bNotify = true;
+ }
+ }
+ }
+ }
+ else if ( bPrtWidth )
+ {
+ // Notify as-character anchored objects, if printing area
+ // width has changed.
+ bNotify = true;
+ bNotifySize = true;
+ }
+
+ // perform notification via the corresponding invalidations
+ if ( bNotify )
+ {
+ if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ if ( bNotifySize )
+ pFlyFrame->InvalidateSize_();
+ // #115759# - no invalidation of
+ // position for as-character anchored objects.
+ if ( !bAnchoredAsChar )
+ {
+ pFlyFrame->InvalidatePos_();
+ }
+ pFlyFrame->Invalidate_();
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
+ {
+ // #115759# - no invalidation of
+ // position for as-character anchored objects.
+ if ( !bAnchoredAsChar )
+ {
+ pObj->InvalidateObjPos();
+ }
+ }
+ else
+ {
+ OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - unknown anchored object type." );
+ }
+ }
+ }
+ }
+ }
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ else if( mpFrame->IsTextFrame() && mbValidSize != mpFrame->isFrameAreaSizeValid() )
+ {
+ SwRootFrame *pRootFrame = mpFrame->getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->InvalidateAccessibleFrameContent( mpFrame );
+ }
+ }
+#endif
+
+ // #i9046# Automatic frame width
+ SwFlyFrame* pFly = nullptr;
+ // #i35879# Do not trust the inf flags. pFrame does not
+ // necessarily have to have an upper!
+ if ( mpFrame->IsFlyFrame() || nullptr == ( pFly = mpFrame->ImplFindFlyFrame() ))
+ return;
+
+ // #i61999#
+ // no invalidation of columned Writer fly frames, because automatic
+ // width doesn't make sense for such Writer fly frames.
+ if ( !pFly->Lower() || pFly->Lower()->IsColumnFrame() )
+ return;
+
+ const SwFormatFrameSize &rFrameSz = pFly->GetFormat()->GetFrameSize();
+
+ // This could be optimized. Basically the fly frame only has to
+ // be invalidated, if the first line of pFrame (if pFrame is a content
+ // frame, for other frame types it's the print area) has changed its
+ // size and pFrame was responsible for the current width of pFly. On
+ // the other hand, this is only rarely used and re-calculation of
+ // the fly frame does not cause too much trouble. So we keep it this
+ // way:
+ if ( SwFrameSize::Fixed != rFrameSz.GetWidthSizeType() )
+ {
+ // #i50668#, #i50998# - invalidation of position
+ // of as-character anchored fly frames not needed and can cause
+ // layout loops
+ if ( dynamic_cast<const SwFlyInContentFrame*>( pFly) == nullptr )
+ {
+ pFly->InvalidatePos();
+ }
+ pFly->InvalidateSize();
+ }
+}
+
+SwLayNotify::SwLayNotify( SwLayoutFrame *pLayFrame ) :
+ SwFrameNotify( pLayFrame ),
+ m_bLowersComplete( false )
+{
+}
+
+// OD 2004-05-11 #i28701# - local method to invalidate the position of all
+// frames inclusive its floating screen objects, which are lowers of the given
+// layout frame
+static void lcl_InvalidatePosOfLowers( SwLayoutFrame& _rLayoutFrame )
+{
+ if( _rLayoutFrame.IsFlyFrame() && _rLayoutFrame.GetDrawObjs() )
+ {
+ _rLayoutFrame.InvalidateObjs( false );
+ }
+
+ SwFrame* pLowerFrame = _rLayoutFrame.Lower();
+ while ( pLowerFrame )
+ {
+ pLowerFrame->InvalidatePos();
+ if ( pLowerFrame->IsTextFrame() )
+ {
+ static_cast<SwTextFrame*>(pLowerFrame)->Prepare( PrepareHint::FramePositionChanged );
+ }
+ else if ( pLowerFrame->IsTabFrame() )
+ {
+ pLowerFrame->InvalidatePrt();
+ }
+
+ pLowerFrame->InvalidateObjs( false );
+
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+}
+
+void SwLayNotify::ImplDestroy()
+{
+ SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(mpFrame);
+ SwRectFnSet aRectFnSet(pLay);
+ bool bNotify = false;
+ if ( pLay->getFramePrintArea().SSize() != maPrt.SSize() )
+ {
+ if ( !IsLowersComplete() )
+ {
+ bool bInvaPercent;
+
+ if ( pLay->IsRowFrame() )
+ {
+ bInvaPercent = true;
+ tools::Long nNew = aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ if( nNew != aRectFnSet.GetHeight(maPrt) )
+ static_cast<SwRowFrame*>(pLay)->AdjustCells( nNew, true);
+ if( aRectFnSet.GetWidth(pLay->getFramePrintArea())
+ != aRectFnSet.GetWidth(maPrt) )
+ static_cast<SwRowFrame*>(pLay)->AdjustCells( 0, false );
+ }
+ else
+ {
+ //Proportional adoption of the internal.
+ //1. If the formatted is no Fly
+ //2. If he contains no columns
+ //3. If the Fly has a fixed height and the columns
+ // are next to be.
+ // Hoehe danebenliegen.
+ //4. Never at SectionFrames.
+ bool bLow;
+ if( pLay->IsFlyFrame() )
+ {
+ if ( pLay->Lower() )
+ {
+ bLow = !pLay->Lower()->IsColumnFrame() ||
+ aRectFnSet.GetHeight(pLay->Lower()->getFrameArea())
+ != aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ }
+ else
+ bLow = false;
+ }
+ else if( pLay->IsSctFrame() )
+ {
+ if ( pLay->Lower() )
+ {
+ if( pLay->Lower()->IsColumnFrame() && pLay->Lower()->GetNext() )
+ bLow = pLay->Lower()->getFrameArea().Height() != pLay->getFramePrintArea().Height();
+ else
+ bLow = pLay->getFramePrintArea().Width() != maPrt.Width();
+ }
+ else
+ bLow = false;
+ }
+ else if( pLay->IsFooterFrame() && !pLay->HasFixSize() )
+ bLow = pLay->getFramePrintArea().Width() != maPrt.Width();
+ else
+ bLow = true;
+ bInvaPercent = bLow;
+ if ( bLow )
+ {
+ pLay->ChgLowersProp( maPrt.SSize() );
+ }
+ // If the PrtArea has been extended, it might be possible that the chain of parts
+ // can take another frame. As a result, the "possible right one" needs to be
+ // invalidated. This only pays off if this or its Uppers are moveable sections.
+ // A PrtArea has been extended if width or height are larger than before.
+ if ( (pLay->getFramePrintArea().Height() > maPrt.Height() ||
+ pLay->getFramePrintArea().Width() > maPrt.Width()) &&
+ (pLay->IsMoveable() || pLay->IsFlyFrame()) )
+ {
+ SwFrame *pTmpFrame = pLay->Lower();
+ if ( pTmpFrame && pTmpFrame->IsFlowFrame() )
+ {
+ while ( pTmpFrame->GetNext() )
+ pTmpFrame = pTmpFrame->GetNext();
+ pTmpFrame->InvalidateNextPos();
+ }
+ }
+ }
+ bNotify = true;
+ //EXPENSIVE!! But how we do it more elegant?
+ if( bInvaPercent )
+ pLay->InvaPercentLowers( pLay->getFramePrintArea().Height() - maPrt.Height() );
+ }
+ if ( pLay->IsTabFrame() )
+ //So that _only_ the shadow is drawn while resizing.
+ static_cast<SwTabFrame*>(pLay)->SetComplete();
+ else
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ if( !( pSh && pSh->GetViewOptions()->getBrowseMode() ) ||
+ !(pLay->GetType() & (SwFrameType::Body | SwFrameType::Page)) )
+ //Thereby the subordinates are retouched clean.
+ //Example problem: Take the Flys with the handles and downsize.
+ //Not for body and page, otherwise it flickers when loading HTML.
+ pLay->SetCompletePaint();
+ }
+ }
+ //Notify Lower if the position has changed.
+ const bool bPrtPos = aRectFnSet.PosDiff( maPrt, pLay->getFramePrintArea() );
+ const bool bPos = bPrtPos || aRectFnSet.PosDiff( maFrame, pLay->getFrameArea() );
+ const bool bSize = pLay->getFrameArea().SSize() != maFrame.SSize();
+
+ if ( bPos && pLay->Lower() && !IsLowersComplete() )
+ {
+ pLay->Lower()->InvalidatePos();
+ SwFootnoteFrame* pFtnFrame = pLay->Lower()->IsFootnoteFrame() ?
+ static_cast<SwFootnoteFrame*>(pLay->Lower()) : nullptr;
+ SwFrame* pFtnLower = pFtnFrame ? pFtnFrame->Lower() : nullptr;
+ if (pFtnLower)
+ pFtnLower->InvalidatePos();
+ }
+
+ if ( bPrtPos )
+ pLay->SetCompletePaint();
+
+ //Inform the Follower if the SSize has changed.
+ if ( bSize )
+ {
+ if( pLay->GetNext() )
+ {
+ if ( pLay->GetNext()->IsLayoutFrame() )
+ pLay->GetNext()->InvalidatePos_();
+ else
+ pLay->GetNext()->InvalidatePos();
+ }
+ else if( pLay->IsSctFrame() )
+ pLay->InvalidateNextPos();
+ }
+ if ( !IsLowersComplete() &&
+ !(pLay->GetType()&(SwFrameType::Fly|SwFrameType::Section) &&
+ pLay->Lower() && pLay->Lower()->IsColumnFrame()) &&
+ (bPos || bNotify) &&
+ !(pLay->GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
+ {
+ // #i44016# - force unlock of position of lower objects.
+ // #i43913# - no unlock of position of objects,
+ // if <pLay> is a cell frame, and its table frame resp. its parent table
+ // frame is locked.
+ // #i47458# - force unlock of position of lower objects,
+ // only if position of layout frame has changed.
+ bool bUnlockPosOfObjs( bPos );
+ if ( bUnlockPosOfObjs && pLay->IsCellFrame() )
+ {
+ SwTabFrame* pTabFrame( pLay->FindTabFrame() );
+ if ( pTabFrame &&
+ ( pTabFrame->IsJoinLocked() ||
+ ( pTabFrame->IsFollow() &&
+ pTabFrame->FindMaster()->IsJoinLocked() ) ) )
+ {
+ bUnlockPosOfObjs = false;
+ }
+ }
+ // #i49383# - check for footnote frame, if unlock
+ // of position of lower objects is allowed.
+ else if ( bUnlockPosOfObjs && pLay->IsFootnoteFrame() )
+ {
+ bUnlockPosOfObjs = static_cast<SwFootnoteFrame*>(pLay)->IsUnlockPosOfLowerObjs();
+ }
+ // #i51303# - no unlock of object positions for sections
+ else if ( bUnlockPosOfObjs && pLay->IsSctFrame() )
+ {
+ bUnlockPosOfObjs = false;
+ }
+ pLay->NotifyLowerObjs( bUnlockPosOfObjs );
+ }
+ if ( bPos && pLay->IsFootnoteFrame() && pLay->Lower() )
+ {
+ // OD 2004-05-11 #i28701#
+ ::lcl_InvalidatePosOfLowers( *pLay );
+ }
+ if( ( bPos || bSize ) && pLay->IsFlyFrame() && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()
+ && static_cast<SwFlyFrame*>(pLay)->GetAnchorFrame()->IsFlyFrame() )
+ static_cast<SwFlyFrame*>(pLay)->AnchorFrame()->InvalidateSize();
+}
+
+SwLayNotify::~SwLayNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+SwFlyNotify::SwFlyNotify( SwFlyFrame *pFlyFrame ) :
+ SwLayNotify( pFlyFrame ),
+ // #115759# - keep correct page frame - the page frame
+ // the Writer fly frame is currently registered at.
+ m_pOldPage( pFlyFrame->GetPageFrame() ),
+ m_aFrameAndSpace( pFlyFrame->GetObjRectWithSpaces() )
+{
+}
+
+void SwFlyNotify::ImplDestroy()
+{
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(mpFrame);
+ if ( pFly->IsNotifyBack() )
+ {
+ SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ if ( !pImp || !pImp->IsAction() || !pImp->GetLayAction().IsAgain() )
+ {
+ //If in the LayAction the IsAgain is set it can be
+ //that the old page is destroyed in the meantime!
+ ::Notify( pFly, m_pOldPage, m_aFrameAndSpace, &maPrt );
+ // #i35640# - additional notify anchor text frame,
+ // if Writer fly frame has changed its page
+ if ( pFly->GetAnchorFrame()->IsTextFrame() &&
+ pFly->GetPageFrame() != m_pOldPage )
+ {
+ pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
+ }
+ }
+ pFly->ResetNotifyBack();
+ }
+
+ //Have the size or the position changed,
+ //so should the view know this.
+ SwRectFnSet aRectFnSet(pFly);
+ const bool bPosChgd = aRectFnSet.PosDiff( maFrame, pFly->getFrameArea() );
+ const bool bFrameChgd = pFly->getFrameArea().SSize() != maFrame.SSize();
+ const bool bPrtChgd = maPrt != pFly->getFramePrintArea();
+ if ( bPosChgd || bFrameChgd || bPrtChgd )
+ {
+ pFly->NotifyDrawObj();
+ }
+ if ( bPosChgd && maFrame.Pos().X() != FAR_AWAY )
+ {
+ // OD 2004-05-10 #i28701# - no direct move of lower Writer fly frames.
+ // reason: New positioning and alignment (e.g. to-paragraph anchored,
+ // but aligned at page) are introduced.
+ // <SwLayNotify::~SwLayNotify()> takes care of invalidation of lower
+ // floating screen objects by calling method <SwLayoutFrame::NotifyLowerObjs()>.
+
+ if ( pFly->IsFlyAtContentFrame() )
+ {
+ SwFrame *pNxt = pFly->AnchorFrame()->FindNext();
+ while (pNxt)
+ {
+ pNxt->InvalidatePos();
+ if (!pNxt->IsSctFrame())
+ {
+ break;
+ }
+ // invalidating pos of a section frame doesn't have much
+ // effect, so try again with its lower
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->Lower();
+ }
+ }
+
+ // #i26945# - notify anchor.
+ // Needed for negative positioned Writer fly frames
+ if ( pFly->GetAnchorFrame()->IsTextFrame() )
+ {
+ pFly->AnchorFrame()->Prepare( PrepareHint::FlyFrameLeave );
+ }
+ }
+
+ // OD 2004-05-13 #i28701#
+ // #i45180# - no adjustment of layout process flags and
+ // further notifications/invalidations, if format is called by grow/shrink
+ if ( !pFly->ConsiderObjWrapInfluenceOnObjPos() )
+ return;
+ if (pFly->IsFlyFreeFrame())
+ {
+ if (static_cast<SwFlyFreeFrame*>(pFly)->IsNoMoveOnCheckClip())
+ return;
+ }
+
+ // #i54138# - suppress restart of the layout process
+ // on changed frame height.
+ // Note: It doesn't seem to be necessary and can cause layout loops.
+ if ( bPosChgd )
+ {
+ // indicate a restart of the layout process
+ pFly->SetRestartLayoutProcess( true );
+ }
+ else
+ {
+ // lock position
+ pFly->LockPosition();
+ }
+
+ if ( pFly->ConsiderForTextWrap() )
+ return;
+
+ // indicate that object has to be considered for text wrap
+ pFly->SetConsiderForTextWrap( true );
+ // invalidate 'background' in order to allow its 'background'
+ // to wrap around it.
+ pFly->NotifyBackground( pFly->GetPageFrame(),
+ pFly->GetObjRectWithSpaces(),
+ PrepareHint::FlyFrameArrive );
+ // invalidate position of anchor frame in order to force
+ // a re-format of the anchor frame, which also causes a
+ // re-format of the invalid previous frames of the anchor frame.
+ pFly->AnchorFrame()->InvalidatePos();
+}
+
+SwFlyNotify::~SwFlyNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+SwContentNotify::SwContentNotify( SwContentFrame *pContentFrame ) :
+ SwFrameNotify( pContentFrame ),
+ // OD 08.01.2004 #i11859#
+ mbChkHeightOfLastLine( false ),
+ mnHeightOfLastLine( 0 ),
+ // OD 2004-02-26 #i25029#
+ mbInvalidatePrevPrtArea( false ),
+ mbBordersJoinedWithPrev( false )
+{
+ // OD 08.01.2004 #i11859#
+ if ( !pContentFrame->IsTextFrame() )
+ return;
+
+ SwTextFrame* pTextFrame = static_cast<SwTextFrame*>(pContentFrame);
+ if (!pTextFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::OLD_LINE_SPACING))
+ {
+ const SvxLineSpacingItem &rSpace = pTextFrame->GetAttrSet()->GetLineSpacing();
+ if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
+ {
+ mbChkHeightOfLastLine = true;
+ mnHeightOfLastLine = pTextFrame->GetHeightOfLastLine();
+ }
+ }
+}
+
+void SwContentNotify::ImplDestroy()
+{
+ SwContentFrame *pCnt = static_cast<SwContentFrame*>(mpFrame);
+ if ( bSetCompletePaintOnInvalidate )
+ pCnt->SetCompletePaint();
+
+ SwRectFnSet aRectFnSet(pCnt);
+ if ( pCnt->IsInTab() && ( aRectFnSet.PosDiff( pCnt->getFrameArea(), maFrame ) ||
+ pCnt->getFrameArea().SSize() != maFrame.SSize()))
+ {
+ SwLayoutFrame* pCell = pCnt->GetUpper();
+ while( !pCell->IsCellFrame() && pCell->GetUpper() )
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell->IsCellFrame(), "Where's my cell?" );
+ if ( text::VertOrientation::NONE != pCell->GetFormat()->GetVertOrient().GetVertOrient() )
+ pCell->InvalidatePrt(); //for the vertical align.
+ }
+
+ // OD 2004-02-26 #i25029#
+ if ( mbInvalidatePrevPrtArea && mbBordersJoinedWithPrev &&
+ pCnt->IsTextFrame() &&
+ !pCnt->IsFollow() && !pCnt->GetIndPrev() )
+ {
+ // determine previous frame
+ SwFrame* pPrevFrame = pCnt->FindPrev();
+ // skip empty section frames and hidden text frames
+ {
+ while ( pPrevFrame &&
+ ( ( pPrevFrame->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pPrevFrame)->GetSection() ) ||
+ ( pPrevFrame->IsTextFrame() &&
+ static_cast<SwTextFrame*>(pPrevFrame)->IsHiddenNow() ) ) )
+ {
+ pPrevFrame = pPrevFrame->FindPrev();
+ }
+ }
+
+ // Invalidate printing area of found previous frame
+ if ( pPrevFrame )
+ {
+ if ( pPrevFrame->IsSctFrame() )
+ {
+ if ( pCnt->IsInSct() )
+ {
+ // Note: found previous frame is a section frame and
+ // <pCnt> is also inside a section.
+ // Thus due to <mbBordersJoinedWithPrev>,
+ // <pCnt> had joined its borders/shadow with the
+ // last content of the found section.
+ // Invalidate printing area of last content in found section.
+ SwFrame* pLstContentOfSctFrame =
+ static_cast<SwSectionFrame*>(pPrevFrame)->FindLastContent();
+ if ( pLstContentOfSctFrame )
+ {
+ pLstContentOfSctFrame->InvalidatePrt();
+ }
+ }
+ }
+ else
+ {
+ pPrevFrame->InvalidatePrt();
+ }
+ }
+ }
+
+ const bool bFirst = aRectFnSet.GetWidth(maFrame) == 0;
+
+ if ( pCnt->IsNoTextFrame() )
+ {
+ //Active PlugIn's or OLE-Objects should know something of the change
+ //thereby they move their window appropriate.
+ SwViewShell *pSh = pCnt->getRootFrame()->GetCurrShell();
+ if ( pSh )
+ {
+ SwOLENode *const pNd(static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetOLENode());
+ if (nullptr != pNd &&
+ (pNd->GetOLEObj().IsOleRef() ||
+ pNd->IsOLESizeInvalid()) )
+ {
+ const bool bNoTextFramePrtAreaChanged =
+ ( maPrt.SSize().Width() != 0 &&
+ maPrt.SSize().Height() != 0 ) &&
+ maPrt.SSize() != pCnt->getFramePrintArea().SSize();
+ OSL_ENSURE( pCnt->IsInFly(), "OLE not in FlyFrame" );
+ SwFlyFrame *pFly = pCnt->FindFlyFrame();
+ svt::EmbeddedObjectRef& xObj = pNd->GetOLEObj().GetObject();
+ SwFEShell *pFESh = nullptr;
+ for(SwViewShell& rCurrentShell : pSh->GetRingContainer())
+ { if ( dynamic_cast<const SwCursorShell*>( &rCurrentShell) != nullptr )
+ {
+ pFESh = static_cast<SwFEShell*>(&rCurrentShell);
+ // #108369#: Here used to be the condition if (!bFirst).
+ // I think this should mean "do not call CalcAndSetScale"
+ // if the frame is formatted for the first time.
+ // Unfortunately this is not valid anymore since the
+ // SwNoTextFrame already gets a width during CalcLowerPreps.
+ // Nevertheless, the indention of !bFirst seemed to be
+ // to assure that the OLE objects have already been notified
+ // if necessary before calling CalcAndSetScale.
+ // So I replaced !bFirst by !IsOLESizeInvalid. There is
+ // one additional problem specific to the word import:
+ // The layout is calculated _before_ calling PrtOLENotify,
+ // and the OLE objects are not invalidated during import.
+ // Therefore I added the condition !IsUpdateExpField,
+ // have a look at the occurrence of CalcLayout in
+ // uiview/view.cxx.
+ if ( !pNd->IsOLESizeInvalid() &&
+ !pSh->GetDoc()->getIDocumentState().IsUpdateExpField() )
+ pFESh->CalcAndSetScale( xObj,
+ &pFly->getFramePrintArea(), &pFly->getFrameArea(),
+ bNoTextFramePrtAreaChanged );
+ }
+ }
+
+ if ( pFESh && pNd->IsOLESizeInvalid() )
+ {
+ pNd->SetOLESizeInvalid( false );
+ pFESh->CalcAndSetScale( xObj ); // create client
+ }
+ }
+ // ditto animated graphics
+ if ( getFrameArea().HasArea() && static_cast<SwNoTextFrame*>(pCnt)->HasAnimation() )
+ {
+ static_cast<SwNoTextFrame*>(pCnt)->StopAnimation();
+ pSh->InvalidateWindows( getFrameArea() );
+ }
+ }
+ }
+
+ if ( bFirst )
+ {
+ pCnt->SetRetouche(); //fix(13870)
+
+ SwDoc& rDoc = pCnt->IsTextFrame()
+ ? static_cast<SwTextFrame*>(pCnt)->GetDoc()
+ : static_cast<SwNoTextFrame*>(pCnt)->GetNode()->GetDoc();
+ if ( !rDoc.GetSpzFrameFormats()->empty() &&
+ rDoc.DoesContainAtPageObjWithContentAnchor() && !rDoc.getIDocumentState().IsNewDoc() )
+ {
+ // If certain import filters for foreign file format import
+ // AT_PAGE anchored objects, the corresponding page number is
+ // typically not known. In this case the content position is
+ // stored at which the anchored object is found in the
+ // imported document.
+ // When this content is formatted it is the time at which
+ // the page is known. Thus, this data can be corrected now.
+
+ const SwPageFrame *pPage = nullptr;
+ SwFrameFormats *pTable = rDoc.GetSpzFrameFormats();
+
+ for ( size_t i = 0; i < pTable->size(); ++i )
+ {
+ SwFrameFormat *pFormat = (*pTable)[i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId() ||
+ rAnch.GetContentAnchor() == nullptr )
+ {
+ continue;
+ }
+
+ if (FrameContainsNode(*pCnt, rAnch.GetContentAnchor()->nNode.GetIndex()))
+ {
+ OSL_FAIL( "<SwContentNotify::~SwContentNotify()> - to page anchored object with content position." );
+ if ( !pPage )
+ {
+ pPage = pCnt->FindPageFrame();
+ }
+ SwFormatAnchor aAnch( rAnch );
+ aAnch.SetAnchor( nullptr );
+ aAnch.SetPageNum( pPage->GetPhyPageNum() );
+ pFormat->SetFormatAttr( aAnch );
+ if ( RES_DRAWFRMFMT != pFormat->Which() )
+ {
+ pFormat->MakeFrames();
+ }
+ }
+ }
+ }
+ }
+
+ // OD 12.01.2004 #i11859# - invalidate printing area of following frame,
+ // if height of last line has changed.
+ if ( pCnt->IsTextFrame() && mbChkHeightOfLastLine )
+ {
+ if ( mnHeightOfLastLine != static_cast<SwTextFrame*>(pCnt)->GetHeightOfLastLine() )
+ {
+ pCnt->InvalidateNextPrtArea();
+ }
+ }
+
+ // #i44049#
+ if ( pCnt->IsTextFrame() && aRectFnSet.PosDiff( maFrame, pCnt->getFrameArea() ) )
+ {
+ pCnt->InvalidateObjs();
+ }
+
+ // #i43255# - move code to invalidate at-character
+ // anchored objects due to a change of its anchor character from
+ // method <SwTextFrame::Format(..)>.
+ if ( !pCnt->IsTextFrame() )
+ return;
+
+ SwTextFrame* pMasterFrame = pCnt->IsFollow()
+ ? static_cast<SwTextFrame*>(pCnt)->FindMaster()
+ : static_cast<SwTextFrame*>(pCnt);
+ if ( pMasterFrame && !pMasterFrame->IsFlyLock() &&
+ pMasterFrame->GetDrawObjs() )
+ {
+ SwSortedObjs* pObjs = pMasterFrame->GetDrawObjs();
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ == RndStdIds::FLY_AT_CHAR )
+ {
+ pAnchoredObj->CheckCharRectAndTopOfLine( !pMasterFrame->IsEmpty() );
+ }
+ }
+ }
+}
+
+SwContentNotify::~SwContentNotify()
+{
+ suppress_fun_call_w_exception(ImplDestroy());
+}
+
+// note this *cannot* be static because it's a friend
+void AppendObj(SwFrame *const pFrame, SwPageFrame *const pPage, SwFrameFormat *const pFormat, const SwFormatAnchor & rAnch)
+{
+ const bool bFlyAtFly = rAnch.GetAnchorId() == RndStdIds::FLY_AT_FLY; // LAYER_IMPL
+ //Is a frame or a SdrObject described?
+ const bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
+ // OD 23.06.2003 #108784# - append also drawing objects anchored
+ // as character.
+ const bool bDrawObjInContent = bSdrObj &&
+ (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
+
+ if( !(bFlyAtFly ||
+ (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA) ||
+ (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ bDrawObjInContent) )
+ return;
+
+ SdrObject* pSdrObj = nullptr;
+ if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
+ {
+ OSL_ENSURE( !bSdrObj, "DrawObject not found." );
+ pFormat->GetDoc()->DelFrameFormat( pFormat );
+ return;
+ }
+ if ( pSdrObj )
+ {
+ if ( !pSdrObj->getSdrPageFromSdrObject() )
+ {
+ pFormat->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0)->
+ InsertObject(pSdrObj, pSdrObj->GetOrdNumDirect());
+ }
+
+ SwDrawContact* pNew =
+ static_cast<SwDrawContact*>(GetUserCall( pSdrObj ));
+ if ( !pNew->GetAnchorFrame() )
+ {
+ pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( nullptr )) );
+ }
+ // OD 19.06.2003 #108784# - add 'virtual' drawing object,
+ // if necessary. But control objects have to be excluded.
+ else if ( !::CheckControlLayer( pSdrObj ) &&
+ pNew->GetAnchorFrame() != pFrame &&
+ !pNew->GetDrawObjectByAnchorFrame( *pFrame ) )
+ {
+ SwDrawVirtObj* pDrawVirtObj = pNew->AddVirtObj(*pFrame);
+ pFrame->AppendDrawObj( *(pNew->GetAnchoredObj( pDrawVirtObj )) );
+
+ pDrawVirtObj->ActionChanged();
+ }
+ }
+ else
+ {
+ SwFlyFrame *pFly;
+ if( bFlyAtFly )
+ pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
+ else
+ pFly = new SwFlyAtContentFrame( static_cast<SwFlyFrameFormat*>(pFormat), pFrame, pFrame );
+ pFly->Lock();
+ pFrame->AppendFly( pFly );
+ pFly->Unlock();
+ if ( pPage )
+ ::RegistFlys( pPage, pFly );
+ }
+}
+
+static bool IsShown(SwNodeOffset const nIndex,
+ const SwFormatAnchor & rAnch,
+ std::vector<sw::Extent>::const_iterator const*const pIter,
+ std::vector<sw::Extent>::const_iterator const*const pEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+ assert(!pIter || *pIter == *pEnd || (*pIter)->pNode->GetIndex() == nIndex);
+ SwPosition const& rAnchor(*rAnch.GetContentAnchor());
+ if (rAnchor.nNode.GetIndex() != nIndex)
+ {
+ return false;
+ }
+ if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ return pIter == nullptr // not merged
+ || pIter != pEnd // at least one char visible in node
+ || !IsSelectFrameAnchoredAtPara(rAnchor,
+ SwPosition(const_cast<SwTextNode&>(*pFirstNode), 0),
+ SwPosition(const_cast<SwTextNode&>(*pLastNode), pLastNode->Len()));
+ }
+ if (pIter)
+ {
+ // note: frames are not sorted by anchor position.
+ assert(pEnd);
+ assert(pFirstNode);
+ assert(pLastNode);
+ assert(rAnch.GetAnchorId() != RndStdIds::FLY_AT_FLY);
+ if (*pIter == *pEnd && rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ { // tdf#149595 special case - it *could* be shown if first == last
+ return !IsDestroyFrameAnchoredAtChar(*rAnch.GetContentAnchor(),
+ SwPosition(const_cast<SwTextNode&>(*pFirstNode), 0),
+ SwPosition(const_cast<SwTextNode&>(*pLastNode), pLastNode->Len()));
+ }
+ for (auto iter = *pIter; iter != *pEnd; ++iter)
+ {
+ assert(iter->nStart != iter->nEnd); // TODO possible?
+ assert(iter->pNode->GetIndex() == nIndex);
+ if (rAnchor.nContent.GetIndex() < iter->nStart)
+ {
+ return false;
+ }
+ if (rAnch.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ // if there is an extent then obviously the node was not
+ // deleted fully...
+ // show if start <= pos <= end
+ // *or* if first-node/0 *and* not StartOfSection
+ // *or* if last-node/Len *and* not EndOfSection
+
+ // first determine the extent to compare to, then
+ // construct start/end positions for the deletion *before* the
+ // extent and compare once.
+ // the interesting corner cases are on the edge of the extent!
+ // no need to check for > the last extent because those
+ // are never visible.
+ if (rAnchor.nContent.GetIndex() <= iter->nEnd)
+ {
+ if (iter->nStart == 0)
+ {
+ return true;
+ }
+ else
+ {
+ SwPosition const start(
+ const_cast<SwTextNode&>(
+ iter == *pIter
+ ? *pFirstNode // simplification
+ : *iter->pNode),
+ iter == *pIter // first extent?
+ ? iter->pNode == pFirstNode
+ ? 0 // at start of 1st node
+ : pFirstNode->Len() // previous node; simplification but should get right result
+ : (iter-1)->nEnd); // previous extent
+ SwPosition const end(*iter->pNode, iter->nStart);
+ return !IsDestroyFrameAnchoredAtChar(rAnchor, start, end);
+ }
+ }
+ else if (iter == *pEnd - 1) // special case: after last extent
+ {
+ if (iter->nEnd == iter->pNode->Len())
+ {
+ return true; // special case: end of node
+ }
+ else
+ {
+ SwPosition const start(*iter->pNode, iter->nEnd);
+ SwPosition const end(
+ const_cast<SwTextNode&>(*pLastNode), // simplification
+ iter->pNode == pLastNode
+ ? iter->pNode->Len()
+ : 0);
+ return !IsDestroyFrameAnchoredAtChar(rAnchor, start, end);
+ }
+ }
+ }
+ else
+ {
+ assert(rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
+ // for AS_CHAR obviously must be <
+ if (rAnchor.nContent.GetIndex() < iter->nEnd)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void RemoveHiddenObjsOfNode(SwTextNode const& rNode,
+ std::vector<sw::Extent>::const_iterator const*const pIter,
+ std::vector<sw::Extent>::const_iterator const*const pEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+ std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
+ for (SwFrameFormat * pFrameFormat : rFlys)
+ {
+ SwFormatAnchor const& rAnchor = pFrameFormat->GetAnchor();
+ if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ || rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ assert(rAnchor.GetContentAnchor()->nNode.GetIndex() == rNode.GetIndex());
+ if (!IsShown(rNode.GetIndex(), rAnchor, pIter, pEnd, pFirstNode, pLastNode))
+ {
+ pFrameFormat->DelFrames();
+ }
+ }
+ }
+}
+
+void AppendObjsOfNode(SwFrameFormats const*const pTable, SwNodeOffset const nIndex,
+ SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc,
+ std::vector<sw::Extent>::const_iterator const*const pIter,
+ std::vector<sw::Extent>::const_iterator const*const pEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+#if OSL_DEBUG_LEVEL > 0
+ std::vector<SwFrameFormat*> checkFormats;
+ for ( size_t i = 0; i < pTable->size(); ++i )
+ {
+ SwFrameFormat *pFormat = (*pTable)[i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( rAnch.GetContentAnchor() &&
+ IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
+ {
+ checkFormats.push_back( pFormat );
+ }
+ }
+#else
+ (void)pTable;
+#endif
+
+ SwNode const& rNode(*pDoc->GetNodes()[nIndex]);
+ std::vector<SwFrameFormat*> const & rFlys(rNode.GetAnchoredFlys());
+ for (size_t it = 0; it != rFlys.size(); )
+ {
+ SwFrameFormat *const pFormat = rFlys[it];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( rAnch.GetContentAnchor() &&
+ IsShown(nIndex, rAnch, pIter, pEnd, pFirstNode, pLastNode))
+ {
+#if OSL_DEBUG_LEVEL > 0
+ std::vector<SwFrameFormat*>::iterator checkPos = std::find( checkFormats.begin(), checkFormats.end(), pFormat );
+ assert( checkPos != checkFormats.end());
+ checkFormats.erase( checkPos );
+#endif
+ AppendObj(pFrame, pPage, pFormat, rAnch);
+ }
+ ++it;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ assert( checkFormats.empty());
+#endif
+}
+
+
+void AppendObjs(const SwFrameFormats *const pTable, SwNodeOffset const nIndex,
+ SwFrame *const pFrame, SwPageFrame *const pPage, SwDoc *const pDoc)
+{
+ if (pFrame->IsTextFrame())
+ {
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame));
+ if (sw::MergedPara const*const pMerged = pTextFrame->GetMergedPara())
+ {
+ std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
+ std::vector<sw::Extent>::const_iterator iter(iterFirst);
+ SwTextNode const* pNode(pMerged->pFirstNode);
+ for ( ; ; ++iter)
+ {
+ if (iter == pMerged->extents.end()
+ || iter->pNode != pNode)
+ {
+ AppendObjsOfNode(pTable, pNode->GetIndex(), pFrame, pPage, pDoc,
+ &iterFirst, &iter, pMerged->pFirstNode, pMerged->pLastNode);
+ SwNodeOffset const until = iter == pMerged->extents.end()
+ ? pMerged->pLastNode->GetIndex() + 1
+ : iter->pNode->GetIndex();
+ for (SwNodeOffset i = pNode->GetIndex() + 1; i < until; ++i)
+ {
+ // let's show at-para flys on nodes that contain start/end of
+ // redline too, even if there's no text there
+ SwNode const*const pTmp(pNode->GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+ AppendObjsOfNode(pTable, pTmp->GetIndex(), pFrame, pPage, pDoc, &iter, &iter, pMerged->pFirstNode, pMerged->pLastNode);
+ }
+ }
+ if (iter == pMerged->extents.end())
+ {
+ break;
+ }
+ pNode = iter->pNode;
+ iterFirst = iter;
+ }
+ }
+ }
+ else
+ {
+ return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr);
+ }
+ }
+ else
+ {
+ return AppendObjsOfNode(pTable, nIndex, pFrame, pPage, pDoc, nullptr, nullptr, nullptr, nullptr);
+ }
+}
+
+bool IsAnchoredObjShown(SwTextFrame const& rFrame, SwFormatAnchor const& rAnchor)
+{
+ assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA ||
+ rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR ||
+ rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR);
+ bool ret(true);
+ if (auto const pMergedPara = rFrame.GetMergedPara())
+ {
+ ret = false;
+ auto const pAnchor(rAnchor.GetContentAnchor());
+ auto iterFirst(pMergedPara->extents.cbegin());
+ if (iterFirst == pMergedPara->extents.end()
+ && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR))
+ {
+ ret = (&pAnchor->nNode.GetNode() == pMergedPara->pFirstNode
+ && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ || pAnchor->nContent == 0))
+ || (&pAnchor->nNode.GetNode() == pMergedPara->pLastNode
+ && (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ || pAnchor->nContent == pMergedPara->pLastNode->Len()));
+ }
+ auto iter(iterFirst);
+ SwTextNode const* pNode(pMergedPara->pFirstNode);
+ for ( ; ; ++iter)
+ {
+ if (iter == pMergedPara->extents.end()
+ || iter->pNode != pNode)
+ {
+ assert(pNode->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
+ if (pNode == &pAnchor->nNode.GetNode())
+ {
+ ret = IsShown(pNode->GetIndex(), rAnchor, &iterFirst, &iter,
+ pMergedPara->pFirstNode, pMergedPara->pLastNode);
+ break;
+ }
+ if (iter == pMergedPara->extents.end())
+ {
+ break;
+ }
+ pNode = iter->pNode;
+ if (pAnchor->nNode.GetIndex() < pNode->GetIndex())
+ {
+ break;
+ }
+ iterFirst = iter;
+ }
+ }
+ }
+ return ret;
+}
+
+void AppendAllObjs(const SwFrameFormats* pTable, const SwFrame* pSib)
+{
+ //Connecting of all Objects, which are described in the SpzTable with the
+ //layout.
+
+ boost::circular_buffer<SwFrameFormat*> vFormatsToConnect(pTable->size());
+ for(const auto& pFormat : *pTable)
+ {
+ const auto& rAnch = pFormat->GetAnchor();
+ // Formats can still remain, because we neither use character bound
+ // frames nor objects which are anchored to character bounds.
+ if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PAGE) && (rAnch.GetAnchorId() != RndStdIds::FLY_AS_CHAR))
+ {
+ auto pContentAnchor = rAnch.GetContentAnchor();
+ // formats in header/footer have no dependencies
+ if(pContentAnchor && pFormat->GetDoc()->IsInHeaderFooter(pContentAnchor->nNode))
+ pFormat->MakeFrames();
+ else
+ vFormatsToConnect.push_back(pFormat);
+ }
+ }
+ const SwRootFrame* pRoot = pSib ? pSib->getRootFrame() : nullptr;
+ const SwFrameFormat* pFirstRequeued(nullptr);
+ while(!vFormatsToConnect.empty())
+ {
+ auto& pFormat = vFormatsToConnect.front();
+ bool isConnected(false);
+ pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot));
+ if(!isConnected)
+ {
+ pFormat->MakeFrames();
+ pFormat->CallSwClientNotify(sw::GetObjectConnectedHint(isConnected, pRoot));
+ }
+ // do this *before* push_back! the circular_buffer can be "full"!
+ vFormatsToConnect.pop_front();
+ if (!isConnected)
+ {
+ if(pFirstRequeued == pFormat)
+ // If nothing happens anymore we can stop.
+ break;
+ if(!pFirstRequeued)
+ pFirstRequeued = pFormat;
+ assert(!vFormatsToConnect.full());
+ vFormatsToConnect.push_back(pFormat);
+ }
+ else
+ {
+ pFirstRequeued = nullptr;
+ }
+ }
+}
+
+namespace sw {
+
+void RecreateStartTextFrames(SwTextNode & rNode)
+{
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ }
+ auto eMode(sw::FrameMode::Existing);
+ for (SwTextFrame * pFrame : frames)
+ {
+ // SplitNode could have moved the original frame to the start node
+ // & created a new one on end, or could have created new frame on
+ // start node... grab start node's frame and recreate MergedPara.
+ SwTextNode & rFirstNode(pFrame->GetMergedPara()
+ ? *pFrame->GetMergedPara()->pFirstNode
+ : rNode);
+ assert(rFirstNode.GetIndex() <= rNode.GetIndex());
+ // clear old one first to avoid DelFrames confusing updates & asserts...
+ pFrame->SetMergedPara(nullptr);
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ // note: this may or may not delete frames on the end node
+ }
+}
+
+} // namespace sw
+
+/** local method to set 'working' position for newly inserted frames
+
+ OD 12.08.2003 #i17969#
+*/
+static void lcl_SetPos( SwFrame& _rNewFrame,
+ const SwLayoutFrame& _rLayFrame )
+{
+ SwRectFnSet aRectFnSet(&_rLayFrame);
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(_rNewFrame);
+ aRectFnSet.SetPos( aFrm, aRectFnSet.GetPos(_rLayFrame.getFrameArea()) );
+
+ // move position by one SwTwip in text flow direction in order to get
+ // notifications for a new calculated position after its formatting.
+ if ( aRectFnSet.IsVert() )
+ {
+ aFrm.Pos().AdjustX( -1 );
+ }
+ else
+ {
+ aFrm.Pos().AdjustY(1 );
+ }
+}
+
+void InsertCnt_( SwLayoutFrame *pLay, SwDoc *pDoc,
+ SwNodeOffset nIndex, bool bPages, SwNodeOffset nEndIndex,
+ SwFrame *pPrv, sw::FrameMode const eMode )
+{
+ pDoc->getIDocumentTimerAccess().BlockIdling();
+ SwRootFrame* pLayout = pLay->getRootFrame();
+ const bool bOldCallbackActionEnabled = pLayout && pLayout->IsCallbackActionEnabled();
+ if( bOldCallbackActionEnabled )
+ pLayout->SetCallbackActionEnabled( false );
+
+ //In the generation of the Layout bPages=true will be handed over.
+ //Then will be new pages generated all x paragraphs already times in advance.
+ //On breaks and/or pagedescriptorchanges the corresponding will be generated
+ //immediately.
+ //The advantage is, that on one hand already a nearly realistic number of
+ //pages are created, but above all there are no almost endless long chain
+ //of paragraphs, which must be moved expensively until it reaches a tolerable
+ //reduced level.
+ //We'd like to think that 20 Paragraphs fit on one page.
+ //So that it does not become in extreme situations so violent we calculate depending
+ //on the node something to it.
+ //If in the DocStatistic a usable given pagenumber
+ //(Will be cared for while writing), so it will be presumed that this will be
+ //number of pages.
+ const bool bStartPercent = bPages && !nEndIndex;
+
+ SwPageFrame *pPage = pLay->FindPageFrame();
+ const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats();
+ SwFrame *pFrame = nullptr;
+ std::unique_ptr<SwActualSection> pActualSection;
+ std::unique_ptr<SwLayHelper> pPageMaker;
+
+ //If the layout will be created (bPages == true) we do head on the progress
+ //Flys and DrawObjects are not connected immediately, this
+ //happens only at the end of the function.
+ if ( bPages )
+ {
+ // Attention: the SwLayHelper class uses references to the content-,
+ // page-, layout-frame etc. and may change them!
+ pPageMaker.reset(new SwLayHelper( pDoc, pFrame, pPrv, pPage, pLay,
+ pActualSection, nIndex, SwNodeOffset(0) == nEndIndex ));
+ if( bStartPercent )
+ {
+ const sal_uLong nPageCount = pPageMaker->CalcPageCount();
+ if( nPageCount )
+ bObjsDirect = false;
+ }
+ }
+ else
+ pPageMaker = nullptr;
+
+ if( pLay->IsInSct() &&
+ ( pLay->IsSctFrame() || pLay->GetUpper() ) ) // Hereby will newbies
+ // be intercepted, of which flags could not determined yet,
+ // for e.g. while inserting a table
+ {
+ SwSectionFrame* pSct = pLay->FindSctFrame();
+ // If content will be inserted in a footnote, which in a column area,
+ // the column area it is not allowed to be broken up.
+ // Only if in the inner of the footnote lies an area, is this a candidate
+ // for pActualSection.
+ // The same applies for areas in tables, if inside the table will be
+ // something inserted, it's only allowed to break up areas, which
+ // lies in the inside also.
+ if( ( !pLay->IsInFootnote() || pSct->IsInFootnote() ) &&
+ ( !pLay->IsInTab() || pSct->IsInTab() ) )
+ {
+ pActualSection.reset(new SwActualSection(nullptr, pSct, pSct->GetSection()->GetFormat()->GetSectionNode()));
+ // tdf#132236 for SwUndoDelete: find outer sections whose start
+ // nodes aren't contained in the range but whose end nodes are,
+ // because section frames may need to be created for them
+ SwActualSection * pUpperSection(pActualSection.get());
+ while (pUpperSection->GetSectionNode()->EndOfSectionIndex() < nEndIndex)
+ {
+ SwStartNode *const pStart(pUpperSection->GetSectionNode()->StartOfSectionNode());
+ if (!pStart->IsSectionNode())
+ {
+ break;
+ }
+ // note: these don't have a section frame, check it in EndNode case!
+ auto const pTmp(new SwActualSection(nullptr, nullptr, static_cast<SwSectionNode*>(pStart)));
+ pUpperSection->SetUpper(pTmp);
+ pUpperSection = pTmp;
+ }
+ OSL_ENSURE( !pLay->Lower() || !pLay->Lower()->IsColumnFrame(),
+ "InsertCnt_: Wrong Call" );
+ }
+ }
+
+ //If a section is "open", the pActualSection points to an SwActualSection.
+ //If the page breaks, for "open" sections a follow will created.
+ //For nested sections (which have, however, not a nested layout),
+ //the SwActualSection class has a member, which points to an upper(section).
+ //When the "inner" section finishes, the upper will used instead.
+
+ // Do not consider the end node. The caller (Section/MakeFrames()) has to
+ // ensure that the end of this range is positioned before EndIndex!
+ for ( ; nEndIndex == SwNodeOffset(0) || nIndex < nEndIndex; ++nIndex)
+ {
+ SwNode *pNd = pDoc->GetNodes()[nIndex];
+ if ( pNd->IsContentNode() )
+ {
+ SwContentNode* pNode = static_cast<SwContentNode*>(pNd);
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ if (pNd->IsTextNode()
+ && pNd->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ { // must have a frame already
+ assert(static_cast<SwTextFrame*>(pNode->getLayoutFrame(pLayout))->GetMergedPara());
+ }
+ continue; // skip it
+ }
+ pFrame = pNode->IsTextNode()
+ ? sw::MakeTextFrame(*pNode->GetTextNode(), pLay, eMode)
+ : pNode->MakeFrame(pLay);
+ if( pPageMaker )
+ pPageMaker->CheckInsert( nIndex );
+
+ pFrame->InsertBehind( pLay, pPrv );
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( pFrame->IsTextFrame() )
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ // no notification, if <SwViewShell> is in construction
+ if ( pViewShell && !pViewShell->IsInConstructor() &&
+ pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() &&
+ pFrame->FindPageFrame() != nullptr)
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ // #i68958#
+ // The information flags of the text frame are validated
+ // in methods <FindNextCnt(..)> and <FindPrevCnt(..)>.
+ // The information flags have to be invalidated, because
+ // it is possible, that the one of its upper frames
+ // isn't inserted into the layout.
+ pFrame->InvalidateInfFlags();
+ }
+ }
+#endif
+ // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
+ // for setting position at newly inserted frame
+ lcl_SetPos( *pFrame, *pLay );
+ pPrv = pFrame;
+
+ if ( !pTable->empty() && bObjsDirect && !isFlyCreationSuppressed )
+ AppendObjs( pTable, nIndex, pFrame, pPage, pDoc );
+ }
+ else if ( pNd->IsTableNode() )
+ { //Should we have encountered a table?
+ SwTableNode *pTableNode = static_cast<SwTableNode*>(pNd);
+ if (pLayout->IsHideRedlines())
+ {
+ // in the problematic case, there can be only 1 redline...
+ SwPosition const tmp(*pNd);
+ SwRangeRedline const*const pRedline(
+ pDoc->getIDocumentRedlineAccess().GetRedline(tmp, nullptr));
+ // pathology: redline that starts on a TableNode; cannot
+ // be created in UI but by import filters...
+ if (pRedline
+ && pRedline->GetType() == RedlineType::Delete
+ && &pRedline->Start()->nNode.GetNode() == pNd)
+ {
+ SAL_WARN("sw.pageframe", "skipping table frame creation on bizarre redline");
+ while (true)
+ {
+ pTableNode->GetNodes()[nIndex]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ if (nIndex == pTableNode->EndOfSectionIndex())
+ {
+ break;
+ }
+ ++nIndex;
+ }
+ continue;
+ }
+ }
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ nIndex = pTableNode->EndOfSectionIndex();
+ continue; // skip it
+ }
+
+ pFrame = pTableNode->MakeFrame( pLay );
+
+ // skip tables deleted with track changes
+ if ( !static_cast<SwTabFrame*>(pFrame)->Lower() )
+ {
+ nIndex = pTableNode->EndOfSectionIndex();
+ continue; // skip it
+ }
+
+ // #108116# loading may produce table structures that GCLines
+ // needs to clean up. To keep table formulas correct, change
+ // all table formulas to internal (BOXPTR) representation.
+ SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ pTableNode->GetTable().GCLines();
+
+ if( pPageMaker )
+ pPageMaker->CheckInsert( nIndex );
+
+ pFrame->InsertBehind( pLay, pPrv );
+ if (pPage) // would null in SwCellFrame ctor
+ { // tdf#134931 call ResetTurbo(); not sure if Paste() would be
+ pFrame->InvalidatePage(pPage); // better than InsertBehind()?
+ }
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ // no notification, if <SwViewShell> is in construction
+ if ( pViewShell && !pViewShell->IsInConstructor() &&
+ pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() &&
+ pFrame->FindPageFrame() != nullptr)
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ if ( bObjsDirect && !pTable->empty() )
+ static_cast<SwTabFrame*>(pFrame)->RegistFlys();
+ // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
+ // for setting position at newly inserted frame
+ lcl_SetPos( *pFrame, *pLay );
+
+ pPrv = pFrame;
+ //Set the index to the endnode of the table section.
+ nIndex = pTableNode->EndOfSectionIndex();
+
+ SwTabFrame* pTmpFrame = static_cast<SwTabFrame*>(pFrame);
+ while ( pTmpFrame )
+ {
+ pTmpFrame->CheckDirChange();
+ pTmpFrame = pTmpFrame->IsFollow() ? pTmpFrame->FindMaster() : nullptr;
+ }
+
+ }
+ else if ( pNd->IsSectionNode() )
+ {
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ continue; // skip it
+ }
+ SwSectionNode *pNode = static_cast<SwSectionNode*>(pNd);
+ if( pNode->GetSection().CalcHiddenFlag() )
+ // is hidden, skip the area
+ nIndex = pNode->EndOfSectionIndex();
+ else
+ {
+ pFrame = pNode->MakeFrame( pLay );
+ pActualSection.reset( new SwActualSection( pActualSection.release(),
+ static_cast<SwSectionFrame*>(pFrame), pNode ) );
+ if ( pActualSection->GetUpper() )
+ {
+ //Insert behind the Upper, the "Follow" of the Upper will be
+ //generated at the EndNode.
+ SwSectionFrame *pTmp = pActualSection->GetUpper()->GetSectionFrame();
+ pFrame->InsertBehind( pTmp->GetUpper(), pTmp );
+ // OD 25.03.2003 #108339# - direct initialization of section
+ // after insertion in the layout
+ static_cast<SwSectionFrame*>(pFrame)->Init();
+ }
+ else
+ {
+ pFrame->InsertBehind( pLay, pPrv );
+ // OD 25.03.2003 #108339# - direct initialization of section
+ // after insertion in the layout
+ static_cast<SwSectionFrame*>(pFrame)->Init();
+
+ // #i33963#
+ // Do not trust the IsInFootnote flag. If we are currently
+ // building up a table, the upper of pPrv may be a cell
+ // frame, but the cell frame does not have an upper yet.
+ if( pPrv && nullptr != pPrv->ImplFindFootnoteFrame() )
+ {
+ if( pPrv->IsSctFrame() )
+ pPrv = static_cast<SwSectionFrame*>(pPrv)->ContainsContent();
+ if( pPrv && pPrv->IsTextFrame() )
+ static_cast<SwTextFrame*>(pPrv)->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ }
+ }
+
+ if (nIndex + 1 == nEndIndex
+ // tdf#136452 may also be needed at end of section
+ || pNode->EndOfSectionIndex() - 1 == nEndIndex)
+ { // tdf#131684 tdf#132236 fix upper of frame moved in
+ // SwUndoDelete; can't be done there unfortunately
+ // because empty section frames are deleted here
+ SwFrame *const pNext(
+ // if there's a parent section, it has been split
+ // into 2 SwSectionFrame already :(
+ ( pFrame->GetNext()
+ && pFrame->GetNext()->IsSctFrame()
+ && pActualSection->GetUpper()
+ && pActualSection->GetUpper()->GetSectionNode() ==
+ static_cast<SwSectionFrame const*>(pFrame->GetNext())->GetSection()->GetFormat()->GetSectionNode())
+ ? static_cast<SwSectionFrame *>(pFrame->GetNext())->ContainsContent()
+ : pFrame->GetNext());
+ if (pNext
+ && pNext->IsTextFrame()
+ && static_cast<SwTextFrame*>(pNext)->GetTextNodeFirst() == pDoc->GetNodes()[nEndIndex]
+ && (pNext->GetUpper() == pFrame->GetUpper()
+ || pFrame->GetNext()->IsSctFrame())) // checked above
+ {
+ pNext->Cut();
+ pNext->InvalidateInfFlags(); // mbInfSct changed
+ // could have columns
+ SwSectionFrame *const pSection(static_cast<SwSectionFrame*>(pFrame));
+ assert(!pSection->Lower() || pSection->Lower()->IsLayoutFrame());
+ SwLayoutFrame *const pParent(pSection->Lower() ? pSection->GetNextLayoutLeaf() : pSection);
+ assert(!pParent->Lower());
+ // paste invalidates, section could have indent...
+ pNext->Paste(pParent, nullptr);
+ }
+ }
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for next paragraph will change
+ // and relation CONTENT_FLOWS_TO for previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFrame->getRootFrame()->GetCurrShell() );
+ // no notification, if <SwViewShell> is in construction
+ if ( pViewShell && !pViewShell->IsInConstructor() &&
+ pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() &&
+ pFrame->FindPageFrame() != nullptr)
+ {
+ auto pNext = pFrame->FindNextCnt( true );
+ auto pPrev = pFrame->FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ pFrame->CheckDirChange();
+
+ // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
+ // for setting position at newly inserted frame
+ lcl_SetPos( *pFrame, *pLay );
+
+ // OD 20.11.2002 #105405# - no page, no invalidate.
+ if ( pPage )
+ {
+ // OD 18.09.2002 #100522#
+ // invalidate page in order to force format and paint of
+ // inserted section frame
+ pFrame->InvalidatePage( pPage );
+
+ // FME 10.11.2003 #112243#
+ // Invalidate fly content flag:
+ if ( pFrame->IsInFly() )
+ pPage->InvalidateFlyContent();
+
+ // OD 14.11.2002 #104684# - invalidate page content in order to
+ // force format and paint of section content.
+ pPage->InvalidateContent();
+ }
+
+ pLay = static_cast<SwLayoutFrame*>(pFrame);
+ if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
+ pLay = pLay->GetNextLayoutLeaf();
+ pPrv = nullptr;
+ }
+ }
+ else if ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsSectionNode() )
+ {
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ continue; // skip it
+ }
+ if (pLayout->HasMergedParas() && !pNd->StartOfSectionNode()->IsCreateFrameWhenHidingRedlines())
+ { // tdf#135014 section break in fieldmark (start inside, end outside)
+ assert(pNd->StartOfSectionNode()->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ continue; // skip it
+ }
+ assert(pActualSection && "Section end without section start?");
+ assert(pActualSection->GetSectionNode() == pNd->StartOfSectionNode());
+
+ //Close the section, where appropriate activate the surrounding
+ //section again.
+ pActualSection.reset(pActualSection->GetUpper());
+ pLay = pLay->FindSctFrame();
+ if ( pActualSection )
+ {
+ //Could be, that the last SectionFrame remains empty.
+ //Then now is the time to remove them.
+ if ( !pLay->ContainsContent() )
+ {
+ SwFrame *pTmpFrame = pLay;
+ pLay = pTmpFrame->GetUpper();
+ pPrv = pTmpFrame->GetPrev();
+ pTmpFrame->RemoveFromLayout();
+ SwFrame::DestroyFrame(pTmpFrame);
+ }
+ else
+ {
+ pPrv = pLay;
+ pLay = pLay->GetUpper();
+ }
+
+ // new section frame
+ pFrame = pActualSection->GetSectionNode()->MakeFrame( pLay );
+ pFrame->InsertBehind( pLay, pPrv );
+ static_cast<SwSectionFrame*>(pFrame)->Init();
+
+ // OD 12.08.2003 #i17969# - consider horizontal/vertical layout
+ // for setting position at newly inserted frame
+ lcl_SetPos( *pFrame, *pLay );
+
+ SwSectionFrame* pOuterSectionFrame = pActualSection->GetSectionFrame();
+
+ // a follow has to be appended to the new section frame
+ SwSectionFrame* pFollow = pOuterSectionFrame ? pOuterSectionFrame->GetFollow() : nullptr;
+ if ( pFollow )
+ {
+ pOuterSectionFrame->SetFollow( nullptr );
+ pOuterSectionFrame->InvalidateSize();
+ static_cast<SwSectionFrame*>(pFrame)->SetFollow( pFollow );
+ }
+
+ // We don't want to leave empty parts back.
+ if (pOuterSectionFrame &&
+ ! pOuterSectionFrame->IsColLocked() &&
+ ! pOuterSectionFrame->ContainsContent() )
+ {
+ pOuterSectionFrame->DelEmpty( true );
+ SwFrame::DestroyFrame(pOuterSectionFrame);
+ }
+ pActualSection->SetSectionFrame( static_cast<SwSectionFrame*>(pFrame) );
+
+ pLay = static_cast<SwLayoutFrame*>(pFrame);
+ if ( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
+ pLay = pLay->GetNextLayoutLeaf();
+ pPrv = nullptr;
+ }
+ else
+ {
+ //Nothing more with sections, it goes on right behind
+ //the SectionFrame.
+ pPrv = pLay;
+ pLay = pLay->GetUpper();
+ }
+ }
+ else if( pNd->IsStartNode() &&
+ SwFlyStartNode == static_cast<SwStartNode*>(pNd)->GetStartNodeType() )
+ {
+ if (pLayout->HasMergedParas() && !pNd->IsCreateFrameWhenHidingRedlines())
+ {
+ assert(pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden);
+ assert(false); // actually a fly-section can't be deleted?
+ continue; // skip it
+ }
+ if ( !pTable->empty() && bObjsDirect && !isFlyCreationSuppressed )
+ {
+ SwFlyFrame* pFly = pLay->FindFlyFrame();
+ if( pFly )
+ AppendObjs( pTable, nIndex, pFly, pPage, pDoc );
+ }
+ }
+ else
+ {
+ assert(!pLayout->HasMergedParas()
+ || pNd->GetRedlineMergeFlag() != SwNode::Merge::Hidden);
+ // Neither Content nor table nor section, so we are done.
+ break;
+ }
+ }
+
+ if ( pActualSection )
+ {
+ // Might happen that an empty (Follow-)Section is left over.
+ if ( !(pLay = pActualSection->GetSectionFrame())->ContainsContent() )
+ {
+ pLay->RemoveFromLayout();
+ SwFrame::DestroyFrame(pLay);
+ }
+ pActualSection.reset();
+ }
+
+ if ( bPages ) // let the Flys connect to each other
+ {
+ if ( !isFlyCreationSuppressed )
+ AppendAllObjs( pTable, pLayout );
+ bObjsDirect = true;
+ }
+
+ if( pPageMaker )
+ {
+ pPageMaker->CheckFlyCache( pPage );
+ pPageMaker.reset();
+ if( pDoc->GetLayoutCache() )
+ {
+#ifdef DBG_UTIL
+ pDoc->GetLayoutCache()->CompareLayout( *pDoc );
+#endif
+ pDoc->GetLayoutCache()->ClearImpl();
+ }
+ }
+
+ pDoc->getIDocumentTimerAccess().UnblockIdling();
+ if( bOldCallbackActionEnabled )
+ pLayout->SetCallbackActionEnabled( bOldCallbackActionEnabled );
+}
+
+void MakeFrames( SwDoc *pDoc, const SwNodeIndex &rSttIdx,
+ const SwNodeIndex &rEndIdx )
+{
+ bObjsDirect = false;
+
+ SwNodeIndex aTmp( rSttIdx );
+ SwNodeOffset nEndIdx = rEndIdx.GetIndex();
+ // TODO for multiple layouts there should be a loop here
+ SwNode* pNd = pDoc->GetNodes().FindPrvNxtFrameNode( aTmp,
+ pDoc->GetNodes()[ nEndIdx-1 ],
+ pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ if ( pNd )
+ {
+ bool bApres = aTmp < rSttIdx;
+ SwNode2Layout aNode2Layout( *pNd, rSttIdx.GetIndex() );
+ sw::FrameMode eMode = sw::FrameMode::Existing;
+ ::std::vector<SwFrame*> frames;
+ while (SwFrame* pFrame = aNode2Layout.NextFrame())
+ { // tdf#150500 new frames may be created that end up merged on pNd
+ // so copy the currently existing ones; they shouldn't get deleted
+ frames.push_back(pFrame);
+ }
+ for (SwFrame *const pFrame : frames)
+ {
+ SwLayoutFrame *pUpper = pFrame->GetUpper();
+ SwFootnoteFrame* pFootnoteFrame = pUpper->FindFootnoteFrame();
+ bool bOldLock, bOldFootnote;
+ if( pFootnoteFrame )
+ {
+ bOldFootnote = pFootnoteFrame->IsColLocked();
+ pFootnoteFrame->ColLock();
+ }
+ else
+ bOldFootnote = true;
+ SwSectionFrame* pSct = pUpper->FindSctFrame();
+ // Inside of footnotes only those areas are interesting that are inside of them. But
+ // not the ones (e.g. column areas) in which are the footnote containers positioned.
+ // #109767# Table frame is in section, insert section in cell frame.
+ if( pSct && ((pFootnoteFrame && !pSct->IsInFootnote()) || pUpper->IsCellFrame()) )
+ pSct = nullptr;
+ if( pSct )
+ { // to prevent pTmp->MoveFwd from destroying the SectionFrame
+ bOldLock = pSct->IsColLocked();
+ pSct->ColLock();
+ }
+ else
+ bOldLock = true;
+
+ // If pFrame cannot be moved, it is not possible to move it to the next page. This applies
+ // also for frames (in the first column of a frame pFrame is moveable) and column
+ // sections of tables (also here pFrame is moveable).
+ bool bMoveNext = nEndIdx - rSttIdx.GetIndex() > SwNodeOffset(120);
+ bool bAllowMove = !pFrame->IsInFly() && pFrame->IsMoveable() &&
+ (!pFrame->IsInTab() || pFrame->IsTabFrame() );
+ if ( bMoveNext && bAllowMove )
+ {
+ SwFrame *pMove = pFrame;
+ SwFrame *pPrev = pFrame->GetPrev();
+ SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pMove );
+ assert(pTmp);
+
+ if ( bApres )
+ {
+ // The rest of this page should be empty. Thus, the following one has to move to
+ // the next page (it might also be located in the following column).
+ assert(!pTmp->HasFollow() && "prev. node's frame is not last");
+ pPrev = pFrame;
+ // If the surrounding SectionFrame has a "next" one,
+ // so this one needs to be moved as well.
+ pMove = pFrame->GetIndNext();
+ SwColumnFrame* pCol = static_cast<SwColumnFrame*>(pFrame->FindColFrame());
+ if( pCol )
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ do
+ {
+ if( pCol && !pMove )
+ { // No successor so far, look into the next column
+ pMove = pCol->ContainsAny();
+ if( pCol->GetNext() )
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ else if( pCol->IsInSct() )
+ { // If there is no following column but we are in a column frame,
+ // there might be (page) columns outside of it.
+ pCol = static_cast<SwColumnFrame*>(pCol->FindSctFrame()->FindColFrame());
+ if( pCol )
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ }
+ else
+ pCol = nullptr;
+ }
+ // skip invalid SectionFrames
+ while( pMove && pMove->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pMove)->GetSection() )
+ pMove = pMove->GetNext();
+ } while( !pMove && pCol );
+
+ if( pMove )
+ {
+ if ( pMove->IsContentFrame() )
+ pTmp = static_cast<SwContentFrame*>(pMove);
+ else if ( pMove->IsTabFrame() )
+ pTmp = static_cast<SwTabFrame*>(pMove);
+ else if ( pMove->IsSctFrame() )
+ {
+ pMove = static_cast<SwSectionFrame*>(pMove)->ContainsAny();
+ if( pMove )
+ pTmp = SwFlowFrame::CastFlowFrame( pMove );
+ else
+ pTmp = nullptr;
+ }
+ }
+ else
+ pTmp = nullptr;
+ }
+ else
+ {
+ assert(!pTmp->IsFollow() && "next node's frame is not master");
+ // move the _content_ of a section frame
+ if( pMove->IsSctFrame() )
+ {
+ while( pMove && pMove->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pMove)->GetSection() )
+ pMove = pMove->GetNext();
+ if( pMove && pMove->IsSctFrame() )
+ pMove = static_cast<SwSectionFrame*>(pMove)->ContainsAny();
+ if( pMove )
+ pTmp = SwFlowFrame::CastFlowFrame( pMove );
+ else
+ pTmp = nullptr;
+ }
+ }
+
+ if( pTmp )
+ {
+ SwFrame* pOldUp = pTmp->GetFrame().GetUpper();
+ // MoveFwd==true means that we are still on the same page.
+ // But since we want to move if possible!
+ bool bTmpOldLock = pTmp->IsJoinLocked();
+ pTmp->LockJoin();
+ while( pTmp->MoveFwd( true, false, true ) )
+ {
+ if( pOldUp == pTmp->GetFrame().GetUpper() )
+ break;
+ pOldUp = pTmp->GetFrame().GetUpper();
+ }
+ if( !bTmpOldLock )
+ pTmp->UnlockJoin();
+ }
+ ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(),
+ pFrame->IsInDocBody(), nEndIdx, pPrev, eMode );
+ }
+ else
+ {
+ bool bSplit;
+ SwFrame* pPrv = bApres ? pFrame : pFrame->GetPrev();
+ // If the section frame is inserted into another one, it must be split.
+ if( pSct && rSttIdx.GetNode().IsSectionNode() )
+ {
+ bSplit = pSct->SplitSect( pFrame, bApres );
+ if( !bSplit && !bApres )
+ {
+ pUpper = pSct->GetUpper();
+ pPrv = pSct->GetPrev();
+ }
+ }
+ else
+ bSplit = false;
+
+ ::InsertCnt_( pUpper, pDoc, rSttIdx.GetIndex(), false,
+ nEndIdx, pPrv, eMode );
+ // OD 23.06.2003 #108784# - correction: append objects doesn't
+ // depend on value of <bAllowMove>
+ if( !isFlyCreationSuppressed )
+ {
+ const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats();
+ if( !pTable->empty() )
+ AppendAllObjs( pTable, pUpper );
+ }
+
+ // If nothing was added (e.g. a hidden section), the split must be reversed.
+ if( bSplit && pSct && pSct->GetNext()
+ && pSct->GetNext()->IsSctFrame() )
+ pSct->MergeNext( static_cast<SwSectionFrame*>(pSct->GetNext()) );
+ if( pFrame->IsInFly() )
+ pFrame->FindFlyFrame()->Invalidate_();
+ if( pFrame->IsInTab() )
+ pFrame->InvalidateSize();
+ }
+
+ SwPageFrame *pPage = pUpper->FindPageFrame();
+ SwFrame::CheckPageDescs( pPage, false );
+ if( !bOldFootnote )
+ pFootnoteFrame->ColUnlock();
+ if( !bOldLock )
+ {
+ pSct->ColUnlock();
+ // pSct might be empty (e.g. when inserting linked section containing further
+ // sections) and can be destroyed in such cases.
+ if( !pSct->ContainsContent() )
+ {
+ pSct->DelEmpty( true );
+ pUpper->getRootFrame()->RemoveFromList( pSct );
+ SwFrame::DestroyFrame(pSct);
+ }
+ }
+ eMode = sw::FrameMode::New; // use Existing only once!
+ }
+ }
+
+ bObjsDirect = true;
+}
+
+SwBorderAttrs::SwBorderAttrs(const sw::BorderCacheOwner* pOwner, const SwFrame* pConstructor)
+ : SwCacheObj(pOwner)
+ , m_rAttrSet(pConstructor->IsContentFrame()
+ ? pConstructor->IsTextFrame()
+ ? static_cast<const SwTextFrame*>(pConstructor)->GetTextNodeForParaProps()->GetSwAttrSet()
+ : static_cast<const SwNoTextFrame*>(pConstructor)->GetNode()->GetSwAttrSet()
+ : static_cast<const SwLayoutFrame*>(pConstructor)->GetFormat()->GetAttrSet())
+ , m_rUL(m_rAttrSet.GetULSpace())
+ // #i96772#
+ // LRSpaceItem is copied due to the possibility that it is adjusted - see below
+ , m_rLR(m_rAttrSet.GetLRSpace().Clone())
+ , m_rBox(m_rAttrSet.GetBox())
+ , m_rShadow(m_rAttrSet.GetShadow())
+ , m_aFrameSize(m_rAttrSet.GetFrameSize().GetSize())
+ , m_bIsLine(false)
+ , m_bJoinedWithPrev(false)
+ , m_bJoinedWithNext(false)
+ , m_nTopLine(0)
+ , m_nBottomLine(0)
+ , m_nLeftLine(0)
+ , m_nRightLine(0)
+ , m_nTop(0)
+ , m_nBottom(0)
+ , m_nGetTopLine(0)
+ , m_nGetBottomLine(0)
+ , m_nLineSpacing(0)
+{
+ // #i96772#
+ const SwTextFrame* pTextFrame = pConstructor->DynCastTextFrame();
+ if ( pTextFrame )
+ {
+ pTextFrame->GetTextNodeForParaProps()->ClearLRSpaceItemDueToListLevelIndents( m_rLR );
+ }
+ else if ( pConstructor->IsNoTextFrame() )
+ {
+ m_rLR = std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE);
+ }
+
+ // Caution: The USHORTs for the cached values are not initialized by intention!
+
+ // everything needs to be calculated at least once:
+ m_bTopLine = m_bBottomLine = m_bLeftLine = m_bRightLine =
+ m_bTop = m_bBottom = m_bLine = true;
+
+ // except this one: calculate line spacing before cell border only for text frames
+ m_bLineSpacing = bool(pTextFrame);
+
+ m_bCacheGetLine = m_bCachedGetTopLine = m_bCachedGetBottomLine = false;
+ // OD 21.05.2003 #108789# - init cache status for values <m_bJoinedWithPrev>
+ // and <m_bJoinedWithNext>, which aren't initialized by default.
+ m_bCachedJoinedWithPrev = false;
+ m_bCachedJoinedWithNext = false;
+}
+
+SwBorderAttrs::~SwBorderAttrs()
+{
+ const_cast<sw::BorderCacheOwner*>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = false;
+}
+
+/* All calc methods calculate a safety distance in addition to the values given by the attributes.
+ * This safety distance is only added when working with borders and/or shadows to prevent that
+ * e.g. borders are painted over.
+ */
+
+void SwBorderAttrs::CalcTop_()
+{
+ m_nTop = CalcTopLine() + m_rUL.GetUpper();
+
+ if (m_rLR)
+ {
+ bool bGutterAtTop = m_rAttrSet.GetDoc()->getIDocumentSettingAccess().get(
+ DocumentSettingId::GUTTER_AT_TOP);
+ if (bGutterAtTop)
+ {
+ // Decrease the print area: the top space is the sum of top and gutter margins.
+ m_nTop += m_rLR->GetGutterMargin();
+ }
+ }
+
+ m_bTop = false;
+}
+
+void SwBorderAttrs::CalcBottom_()
+{
+ m_nBottom = CalcBottomLine() + m_rUL.GetLower();
+ m_bBottom = false;
+}
+
+tools::Long SwBorderAttrs::CalcRight( const SwFrame* pCaller ) const
+{
+ tools::Long nRight=0;
+
+ if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING)) {
+ // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left
+ // and right border are painted on the right respectively left.
+ if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() )
+ nRight = CalcLeftLine();
+ else
+ nRight = CalcRightLine();
+
+ }
+ // for paragraphs, "left" is "before text" and "right" is "after text"
+ if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() )
+ nRight += m_rLR->GetLeft();
+ else
+ nRight += m_rLR->GetRight();
+
+ // correction: retrieve left margin for numbering in R2L-layout
+ if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() )
+ {
+ nRight += static_cast<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ }
+
+ if (pCaller->IsPageFrame() && m_rLR)
+ {
+ const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
+ bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::GUTTER_AT_TOP);
+ if (!bGutterAtTop)
+ {
+ bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
+ tools::Long nGutterMargin = bRtlGutter ? m_rLR->GetGutterMargin() : m_rLR->GetRightGutterMargin();
+ // Decrease the print area: the right space is the sum of right and right gutter
+ // margins.
+ nRight += nGutterMargin;
+ }
+ }
+
+ return nRight;
+}
+
+/// Tries to detect if this paragraph has a floating table attached.
+static bool lcl_hasTabFrame(const SwTextFrame* pTextFrame)
+{
+ if (pTextFrame->GetDrawObjs())
+ {
+ const SwSortedObjs* pSortedObjs = pTextFrame->GetDrawObjs();
+ if (pSortedObjs->size() > 0)
+ {
+ SwAnchoredObject* pObject = (*pSortedObjs)[0];
+ if (auto pFly = pObject->DynCastFlyFrame())
+ {
+ if (pFly->Lower() && pFly->Lower()->IsTabFrame())
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+tools::Long SwBorderAttrs::CalcLeft( const SwFrame *pCaller ) const
+{
+ tools::Long nLeft=0;
+
+ if (!pCaller->IsTextFrame() || !static_cast<const SwTextFrame*>(pCaller)->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::INVERT_BORDER_SPACING))
+ {
+ // OD 23.01.2003 #106895# - for cell frame in R2L text direction the left
+ // and right border are painted on the right respectively left.
+ if ( pCaller->IsCellFrame() && pCaller->IsRightToLeft() )
+ nLeft = CalcRightLine();
+ else
+ nLeft = CalcLeftLine();
+ }
+
+ // for paragraphs, "left" is "before text" and "right" is "after text"
+ if ( pCaller->IsTextFrame() && pCaller->IsRightToLeft() )
+ nLeft += m_rLR->GetRight();
+ else
+ {
+ bool bIgnoreMargin = false;
+ if (pCaller->IsTextFrame())
+ {
+ const SwTextFrame* pTextFrame = static_cast<const SwTextFrame*>(pCaller);
+ if (pTextFrame->GetDoc().GetDocumentSettingManager().get(DocumentSettingId::FLOATTABLE_NOMARGINS))
+ {
+ // If this is explicitly requested, ignore the margins next to the floating table.
+ if (lcl_hasTabFrame(pTextFrame))
+ bIgnoreMargin = true;
+ // TODO here we only handle the first two paragraphs, would be nice to generalize this.
+ else if (pTextFrame->FindPrev() && pTextFrame->FindPrev()->IsTextFrame() && lcl_hasTabFrame(static_cast<const SwTextFrame*>(pTextFrame->FindPrev())))
+ bIgnoreMargin = true;
+ }
+ }
+ if (!bIgnoreMargin)
+ nLeft += m_rLR->GetLeft();
+ }
+
+ // correction: do not retrieve left margin for numbering in R2L-layout
+ if ( pCaller->IsTextFrame() && !pCaller->IsRightToLeft() )
+ {
+ nLeft += static_cast<const SwTextFrame*>(pCaller)->GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ }
+
+ if (pCaller->IsPageFrame() && m_rLR)
+ {
+ const auto pPageFrame = static_cast<const SwPageFrame*>(pCaller);
+ bool bGutterAtTop = pPageFrame->GetFormat()->getIDocumentSettingAccess().get(
+ DocumentSettingId::GUTTER_AT_TOP);
+ if (!bGutterAtTop)
+ {
+ bool bRtlGutter = pPageFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_RTL_GUTTER)->GetValue();
+ tools::Long nGutterMargin = bRtlGutter ? m_rLR->GetRightGutterMargin() : m_rLR->GetGutterMargin();
+ // Decrease the print area: the left space is the sum of left and gutter margins.
+ nLeft += nGutterMargin;
+ }
+ }
+
+ return nLeft;
+}
+
+/* Calculated values for borders and shadows.
+ * It might be that a distance is wanted even without lines. This will be
+ * considered here and not by the attribute (e.g. bBorderDist for cells).
+ */
+
+void SwBorderAttrs::CalcTopLine_()
+{
+ m_nTopLine = m_rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine*/true );
+ m_nTopLine = m_nTopLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::TOP);
+ m_bTopLine = false;
+}
+
+void SwBorderAttrs::CalcBottomLine_()
+{
+ m_nBottomLine = m_rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM, true );
+ m_nBottomLine = m_nBottomLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::BOTTOM);
+ m_bBottomLine = false;
+}
+
+void SwBorderAttrs::CalcLeftLine_()
+{
+ m_nLeftLine = m_rBox.CalcLineSpace( SvxBoxItemLine::LEFT, true);
+ m_nLeftLine = m_nLeftLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT);
+ m_bLeftLine = false;
+}
+
+void SwBorderAttrs::CalcRightLine_()
+{
+ m_nRightLine = m_rBox.CalcLineSpace( SvxBoxItemLine::RIGHT, true );
+ m_nRightLine = m_nRightLine + m_rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT);
+ m_bRightLine = false;
+}
+
+void SwBorderAttrs::IsLine_()
+{
+ m_bIsLine = m_rBox.GetTop() || m_rBox.GetBottom() ||
+ m_rBox.GetLeft()|| m_rBox.GetRight();
+ m_bLine = false;
+}
+
+/* The borders of neighboring paragraphs are condensed by following algorithm:
+ *
+ * 1. No top border if the predecessor has the same top border and (3) applies.
+ * In addition, the paragraph needs to have a border at least one side (left/right/bottom).
+ * 2. No bottom border if the successor has the same bottom border and (3) applies.
+ * In addition, the paragraph needs to have a border at least one side (left/right/top).
+ * 3. The borders on the left and right side are identical between the current and the
+ * pre-/succeeding paragraph.
+ */
+
+static bool CmpLines( const editeng::SvxBorderLine *pL1, const editeng::SvxBorderLine *pL2 )
+{
+ return ( ((pL1 && pL2) && (*pL1 == *pL2)) || (!pL1 && !pL2) );
+}
+
+// OD 21.05.2003 #108789# - change name of 1st parameter - "rAttrs" -> "rCmpAttrs"
+// OD 21.05.2003 #108789# - compare <CalcRight()> and <rCmpAttrs.CalcRight()>
+// instead of only the right LR-spacing, because R2L-layout has to be
+// considered.
+bool SwBorderAttrs::CmpLeftRight( const SwBorderAttrs &rCmpAttrs,
+ const SwFrame *pCaller,
+ const SwFrame *pCmp ) const
+{
+ return ( CmpLines( rCmpAttrs.GetBox().GetLeft(), GetBox().GetLeft() ) &&
+ CmpLines( rCmpAttrs.GetBox().GetRight(),GetBox().GetRight() ) &&
+ CalcLeft( pCaller ) == rCmpAttrs.CalcLeft( pCmp ) &&
+ // OD 21.05.2003 #108789# - compare <CalcRight> with <rCmpAttrs.CalcRight>.
+ CalcRight( pCaller ) == rCmpAttrs.CalcRight( pCmp ) );
+}
+
+bool SwBorderAttrs::JoinWithCmp( const SwFrame& _rCallerFrame,
+ const SwFrame& _rCmpFrame ) const
+{
+ bool bReturnVal = false;
+
+ SwBorderAttrAccess aCmpAccess( SwFrame::GetCache(), &_rCmpFrame );
+ const SwBorderAttrs &rCmpAttrs = *aCmpAccess.Get();
+ if ( m_rShadow == rCmpAttrs.GetShadow() &&
+ CmpLines( m_rBox.GetTop(), rCmpAttrs.GetBox().GetTop() ) &&
+ CmpLines( m_rBox.GetBottom(), rCmpAttrs.GetBox().GetBottom() ) &&
+ CmpLeftRight( rCmpAttrs, &_rCallerFrame, &_rCmpFrame )
+ )
+ {
+ bReturnVal = true;
+ }
+
+ return bReturnVal;
+}
+
+// OD 21.05.2003 #108789# - method to determine, if borders are joined with
+// previous frame. Calculated value saved in cached value <m_bJoinedWithPrev>
+// OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>
+void SwBorderAttrs::CalcJoinedWithPrev( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame )
+{
+ // set default
+ m_bJoinedWithPrev = false;
+
+ if ( _rFrame.IsTextFrame() )
+ {
+ // text frame can potentially join with previous text frame, if
+ // corresponding attribute set is set at previous text frame.
+ // OD 2004-02-26 #i25029# - If parameter <_pPrevFrame> is set, take this
+ // one as previous frame.
+ const SwFrame* pPrevFrame = _pPrevFrame ? _pPrevFrame : _rFrame.GetPrev();
+ // OD 2004-02-13 #i25029# - skip hidden text frames.
+ while ( pPrevFrame && pPrevFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pPrevFrame)->IsHiddenNow() )
+ {
+ pPrevFrame = pPrevFrame->GetPrev();
+ }
+ if ( pPrevFrame && pPrevFrame->IsTextFrame() &&
+ pPrevFrame->GetAttrSet()->GetParaConnectBorder().GetValue()
+ )
+ {
+ m_bJoinedWithPrev = JoinWithCmp( _rFrame, *pPrevFrame );
+ }
+ }
+
+ // valid cache status, if demanded
+ // OD 2004-02-26 #i25029# - Do not validate cache, if parameter <_pPrevFrame>
+ // is set.
+ m_bCachedJoinedWithPrev = m_bCacheGetLine && !_pPrevFrame;
+}
+
+// OD 21.05.2003 #108789# - method to determine, if borders are joined with
+// next frame. Calculated value saved in cached value <m_bJoinedWithNext>
+void SwBorderAttrs::CalcJoinedWithNext( const SwFrame& _rFrame )
+{
+ // set default
+ m_bJoinedWithNext = false;
+
+ if ( _rFrame.IsTextFrame() )
+ {
+ // text frame can potentially join with next text frame, if
+ // corresponding attribute set is set at current text frame.
+ // OD 2004-02-13 #i25029# - get next frame, but skip hidden text frames.
+ const SwFrame* pNextFrame = _rFrame.GetNext();
+ while ( pNextFrame && pNextFrame->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pNextFrame)->IsHiddenNow() )
+ {
+ pNextFrame = pNextFrame->GetNext();
+ }
+ if ( pNextFrame && pNextFrame->IsTextFrame() &&
+ _rFrame.GetAttrSet()->GetParaConnectBorder().GetValue()
+ )
+ {
+ m_bJoinedWithNext = JoinWithCmp( _rFrame, *pNextFrame );
+ }
+ }
+
+ // valid cache status, if demanded
+ m_bCachedJoinedWithNext = m_bCacheGetLine;
+}
+
+// OD 21.05.2003 #108789# - accessor for cached values <m_bJoinedWithPrev>
+// OD 2004-02-26 #i25029# - add 2nd parameter <_pPrevFrame>, which is passed to
+// method <_CalcJoindWithPrev(..)>.
+bool SwBorderAttrs::JoinedWithPrev( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame ) const
+{
+ if ( !m_bCachedJoinedWithPrev || _pPrevFrame )
+ {
+ // OD 2004-02-26 #i25029# - pass <_pPrevFrame> as 2nd parameter
+ const_cast<SwBorderAttrs*>(this)->CalcJoinedWithPrev( _rFrame, _pPrevFrame );
+ }
+
+ return m_bJoinedWithPrev;
+}
+
+bool SwBorderAttrs::JoinedWithNext( const SwFrame& _rFrame ) const
+{
+ if ( !m_bCachedJoinedWithNext )
+ {
+ const_cast<SwBorderAttrs*>(this)->CalcJoinedWithNext( _rFrame );
+ }
+
+ return m_bJoinedWithNext;
+}
+
+// OD 2004-02-26 #i25029# - added 2nd parameter <_pPrevFrame>, which is passed to
+// method <JoinedWithPrev>
+void SwBorderAttrs::GetTopLine_( const SwFrame& _rFrame,
+ const SwFrame* _pPrevFrame )
+{
+ sal_uInt16 nRet = CalcTopLine();
+
+ // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
+ // OD 2004-02-26 #i25029# - add 2nd parameter
+ if ( JoinedWithPrev( _rFrame, _pPrevFrame ) )
+ {
+ nRet = 0;
+ }
+
+ m_bCachedGetTopLine = m_bCacheGetLine;
+
+ m_nGetTopLine = nRet;
+}
+
+void SwBorderAttrs::GetBottomLine_( const SwFrame& _rFrame )
+{
+ sal_uInt16 nRet = CalcBottomLine();
+
+ // OD 21.05.2003 #108789# - use new method <JoinWithPrev()>
+ if ( JoinedWithNext( _rFrame ) )
+ {
+ nRet = 0;
+ }
+
+ m_bCachedGetBottomLine = m_bCacheGetLine;
+
+ m_nGetBottomLine = nRet;
+}
+
+void SwBorderAttrs::CalcLineSpacing_()
+{
+ // tdf#125300 compatibility option AddParaLineSpacingToTableCells needs also line spacing
+ const SvxLineSpacingItem &rSpace = m_rAttrSet.GetLineSpacing();
+ if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop && rSpace.GetPropLineSpace() > 100 )
+ {
+ sal_Int32 nFontSize = m_rAttrSet.Get(RES_CHRATR_FONTSIZE).GetHeight();
+ m_nLineSpacing = nFontSize * (rSpace.GetPropLineSpace() - 100) * 1.15 / 100;
+ }
+ m_bLineSpacing = false;
+}
+
+static sw::BorderCacheOwner const* GetBorderCacheOwner(SwFrame const& rFrame)
+{
+ return rFrame.IsContentFrame()
+ ? static_cast<sw::BorderCacheOwner const*>(rFrame.IsTextFrame()
+ // sw_redlinehide: presumably this caches the border attrs at the model level and can be shared across different layouts so we want the ParaProps node here
+ ? static_cast<const SwTextFrame&>(rFrame).GetTextNodeForParaProps()
+ : static_cast<const SwNoTextFrame&>(rFrame).GetNode())
+ : static_cast<sw::BorderCacheOwner const*>(static_cast<const SwLayoutFrame&>(rFrame).GetFormat());
+}
+
+SwBorderAttrAccess::SwBorderAttrAccess( SwCache &rCach, const SwFrame *pFrame ) :
+ SwCacheAccess( rCach,
+ static_cast<void const *>(GetBorderCacheOwner(*pFrame)),
+ GetBorderCacheOwner(*pFrame)->IsInCache()),
+ m_pConstructor( pFrame )
+{
+}
+
+SwCacheObj *SwBorderAttrAccess::NewObj()
+{
+ const_cast<sw::BorderCacheOwner *>(static_cast<sw::BorderCacheOwner const *>(m_pOwner))->m_bInCache = true;
+ return new SwBorderAttrs( static_cast<sw::BorderCacheOwner const *>(m_pOwner), m_pConstructor );
+}
+
+SwBorderAttrs *SwBorderAttrAccess::Get()
+{
+ return static_cast<SwBorderAttrs*>(SwCacheAccess::Get());
+}
+
+SwOrderIter::SwOrderIter( const SwPageFrame *pPg ) :
+ m_pPage( pPg ),
+ m_pCurrent( nullptr )
+{
+}
+
+void SwOrderIter::Top()
+{
+ m_pCurrent = nullptr;
+ if ( !m_pPage->GetSortedObjs() )
+ return;
+
+ const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
+ if ( !pObjs->size() )
+ return;
+
+ sal_uInt32 nTopOrd = 0;
+ (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
+ for (SwAnchoredObject* i : *pObjs)
+ {
+ const SdrObject* pObj = i->GetDrawObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ continue;
+ sal_uInt32 nTmp = pObj->GetOrdNumDirect();
+ if ( nTmp >= nTopOrd )
+ {
+ nTopOrd = nTmp;
+ m_pCurrent = pObj;
+ }
+ }
+}
+
+const SdrObject *SwOrderIter::Bottom()
+{
+ m_pCurrent = nullptr;
+ if ( m_pPage->GetSortedObjs() )
+ {
+ sal_uInt32 nBotOrd = USHRT_MAX;
+ const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
+ if ( pObjs->size() )
+ {
+ (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
+ for (SwAnchoredObject* i : *pObjs)
+ {
+ const SdrObject* pObj = i->GetDrawObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ continue;
+ sal_uInt32 nTmp = pObj->GetOrdNumDirect();
+ if ( nTmp < nBotOrd )
+ {
+ nBotOrd = nTmp;
+ m_pCurrent = pObj;
+ }
+ }
+ }
+ }
+ return m_pCurrent;
+}
+
+const SdrObject *SwOrderIter::Next()
+{
+ const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0;
+ m_pCurrent = nullptr;
+ if ( m_pPage->GetSortedObjs() )
+ {
+ sal_uInt32 nOrd = USHRT_MAX;
+ const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
+ if ( pObjs->size() )
+ {
+ (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
+ for (SwAnchoredObject* i : *pObjs)
+ {
+ const SdrObject* pObj = i->GetDrawObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ continue;
+ sal_uInt32 nTmp = pObj->GetOrdNumDirect();
+ if ( nTmp > nCurOrd && nTmp < nOrd )
+ {
+ nOrd = nTmp;
+ m_pCurrent = pObj;
+ }
+ }
+ }
+ }
+ return m_pCurrent;
+}
+
+void SwOrderIter::Prev()
+{
+ const sal_uInt32 nCurOrd = m_pCurrent ? m_pCurrent->GetOrdNumDirect() : 0;
+ m_pCurrent = nullptr;
+ if ( !m_pPage->GetSortedObjs() )
+ return;
+
+ const SwSortedObjs *pObjs = m_pPage->GetSortedObjs();
+ if ( !pObjs->size() )
+ return;
+
+ sal_uInt32 nOrd = 0;
+ (*pObjs)[0]->GetDrawObj()->GetOrdNum(); // force updating
+ for (SwAnchoredObject* i : *pObjs)
+ {
+ const SdrObject* pObj = i->GetDrawObj();
+ if ( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ continue;
+ sal_uInt32 nTmp = pObj->GetOrdNumDirect();
+ if ( nTmp < nCurOrd && nTmp >= nOrd )
+ {
+ nOrd = nTmp;
+ m_pCurrent = pObj;
+ }
+ }
+}
+
+/// Keep and restore the substructure of a layout frame for an action.
+// New algorithm:
+// Do not look at each neighbor one by one to set all pointers correctly.
+// It is sufficient to detach a part of a chain and check if another chain needs to be added
+// when attaching it again. Only the pointers necessary for the chain connection need to be
+// adjusted. The correction happens in RestoreContent(). In between all access is restricted.
+// During this action, the Flys are detached from the page.
+
+// #115759# - 'remove' also drawing object from page and
+// at-fly anchored objects from page
+static void lcl_RemoveObjsFromPage( SwFrame* _pFrame )
+{
+ OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_RemoveObjsFromPage." );
+ SwSortedObjs &rObjs = *_pFrame->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ // #115759# - reset member, at which the anchored
+ // object orients its vertical position
+ pObj->ClearVertPosOrientFrame();
+ // #i43913#
+ pObj->ResetLayoutProcessBools();
+ // #115759# - remove also lower objects of as-character
+ // anchored Writer fly frames from page
+ if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ // #115759# - remove also direct lowers of Writer
+ // fly frame from page
+ if ( pFlyFrame->GetDrawObjs() )
+ {
+ ::lcl_RemoveObjsFromPage( pFlyFrame );
+ }
+
+ SwContentFrame* pCnt = pFlyFrame->ContainsContent();
+ while ( pCnt )
+ {
+ if ( pCnt->GetDrawObjs() )
+ ::lcl_RemoveObjsFromPage( pCnt );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ if ( pFlyFrame->IsFlyFreeFrame() )
+ {
+ // #i28701# - use new method <GetPageFrame()>
+ if (SwPageFrame *pPg = pFlyFrame->GetPageFrame())
+ pPg->RemoveFlyFromPage(pFlyFrame);
+ }
+ }
+ // #115759# - remove also drawing objects from page
+ else if ( auto pDrawObj = dynamic_cast<SwAnchoredDrawObject*>( pObj) )
+ {
+ if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ if (SwPageFrame *pPg = pObj->GetPageFrame())
+ pPg->RemoveDrawObjFromPage( *pDrawObj );
+ }
+ }
+ }
+}
+
+SwFrame *SaveContent( SwLayoutFrame *pLay, SwFrame *pStart )
+{
+ if( pLay->IsSctFrame() && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
+ sw_RemoveFootnotes( static_cast<SwColumnFrame*>(pLay->Lower()), true, true );
+
+ SwFrame *pSav = pLay->ContainsAny();
+ if ( nullptr == pSav )
+ return nullptr;
+
+ if( pSav->IsInFootnote() && !pLay->IsInFootnote() )
+ {
+ do
+ pSav = pSav->FindNext();
+ while( pSav && pSav->IsInFootnote() );
+ if( !pSav || !pLay->IsAnLower( pSav ) )
+ return nullptr;
+ }
+
+ // Tables should be saved as a whole, exception:
+ // The contents of a section or a cell inside a table should be saved
+ if ( pSav->IsInTab() && !( ( pLay->IsSctFrame() || pLay->IsCellFrame() ) && pLay->IsInTab() ) )
+ while ( !pSav->IsTabFrame() )
+ pSav = pSav->GetUpper();
+
+ if( pSav->IsInSct() )
+ { // search the upmost section inside of pLay
+ SwFrame* pSect = pLay->FindSctFrame();
+ SwFrame *pTmp = pSav;
+ do
+ {
+ pSav = pTmp;
+ pTmp = (pSav && pSav->GetUpper()) ? pSav->GetUpper()->FindSctFrame() : nullptr;
+ } while ( pTmp != pSect );
+ }
+
+ SwFrame *pFloat = pSav;
+ if( !pStart )
+ pStart = pSav;
+ bool bGo = pStart == pSav;
+ do
+ {
+ if( bGo )
+ pFloat->GetUpper()->m_pLower = nullptr; // detach the chain part
+
+ // search the end of the chain part, remove Flys on the way
+ do
+ {
+ if( bGo )
+ {
+ if ( pFloat->IsContentFrame() )
+ {
+ if ( pFloat->GetDrawObjs() )
+ ::lcl_RemoveObjsFromPage( static_cast<SwContentFrame*>(pFloat) );
+ }
+ else if ( pFloat->IsTabFrame() || pFloat->IsSctFrame() )
+ {
+ SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(pFloat)->ContainsContent();
+ if( pCnt )
+ {
+ do
+ { if ( pCnt->GetDrawObjs() )
+ ::lcl_RemoveObjsFromPage( pCnt );
+ pCnt = pCnt->GetNextContentFrame();
+ } while ( pCnt && static_cast<SwLayoutFrame*>(pFloat)->IsAnLower( pCnt ) );
+ }
+ }
+ else {
+ OSL_ENSURE( !pFloat, "new FloatFrame?" );
+ }
+ }
+ if ( pFloat->GetNext() )
+ {
+ if( bGo )
+ pFloat->mpUpper = nullptr;
+ pFloat = pFloat->GetNext();
+ if( !bGo && pFloat == pStart )
+ {
+ bGo = true;
+ pFloat->mpPrev->mpNext = nullptr;
+ pFloat->mpPrev = nullptr;
+ }
+ }
+ else
+ break;
+
+ } while ( pFloat );
+
+ // search next chain part and connect both chains
+ SwFrame *pTmp = pFloat->FindNext();
+ if( bGo )
+ pFloat->mpUpper = nullptr;
+
+ if( !pLay->IsInFootnote() )
+ while( pTmp && pTmp->IsInFootnote() )
+ pTmp = pTmp->FindNext();
+
+ if ( !pLay->IsAnLower( pTmp ) )
+ pTmp = nullptr;
+
+ if ( pTmp && bGo )
+ {
+ pFloat->mpNext = pTmp; // connect both chains
+ pFloat->mpNext->mpPrev = pFloat;
+ }
+ pFloat = pTmp;
+ bGo = bGo || ( pStart == pFloat );
+ } while ( pFloat );
+
+ return bGo ? pStart : nullptr;
+}
+
+// #115759# - add also drawing objects to page and at-fly
+// anchored objects to page
+static void lcl_AddObjsToPage( SwFrame* _pFrame, SwPageFrame* _pPage )
+{
+ OSL_ENSURE( _pFrame->GetDrawObjs(), "no DrawObjs in lcl_AddObjsToPage." );
+ SwSortedObjs &rObjs = *_pFrame->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ // #115759# - unlock position of anchored object
+ // in order to get the object's position calculated.
+ pObj->UnlockPosition();
+ // #115759# - add also lower objects of as-character
+ // anchored Writer fly frames from page
+ if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
+ {
+ if (pFlyFrame->IsFlyFreeFrame())
+ {
+ _pPage->AppendFlyToPage( pFlyFrame );
+ }
+ pFlyFrame->InvalidatePos_();
+ pFlyFrame->InvalidateSize_();
+ pFlyFrame->InvalidatePage( _pPage );
+
+ // #115759# - add also at-fly anchored objects
+ // to page
+ if ( pFlyFrame->GetDrawObjs() )
+ {
+ ::lcl_AddObjsToPage( pFlyFrame, _pPage );
+ }
+
+ SwContentFrame *pCnt = pFlyFrame->ContainsContent();
+ while ( pCnt )
+ {
+ if ( pCnt->GetDrawObjs() )
+ ::lcl_AddObjsToPage( pCnt, _pPage );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ // #115759# - remove also drawing objects from page
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( pObj) != nullptr )
+ {
+ if (pObj->GetFrameFormat().GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR)
+ {
+ pObj->InvalidateObjPos();
+ _pPage->AppendDrawObjToPage(
+ *static_cast<SwAnchoredDrawObject*>(pObj) );
+ }
+ }
+ }
+}
+
+void RestoreContent( SwFrame *pSav, SwLayoutFrame *pParent, SwFrame *pSibling )
+{
+ OSL_ENSURE( pSav && pParent, "no Save or Parent provided for RestoreContent." );
+ SwRectFnSet aRectFnSet(pParent);
+
+ // If there are already FlowFrames below the new parent, so add the chain (starting with pSav)
+ // after the last one. The parts are inserted and invalidated if needed.
+ // On the way, the Flys of the ContentFrames are registered at the page.
+
+ SwPageFrame *pPage = pParent->FindPageFrame();
+
+ if ( pPage )
+ pPage->InvalidatePage( pPage );
+
+ // determine predecessor and establish connection or initialize
+ pSav->mpPrev = pSibling;
+ SwFrame* pNxt;
+ if ( pSibling )
+ {
+ pNxt = pSibling->mpNext;
+ pSibling->mpNext = pSav;
+ pSibling->InvalidatePrt_();
+ pSibling->InvalidatePage( pPage );
+ SwFlowFrame *pFlowFrame = dynamic_cast<SwFlowFrame*>(pSibling);
+ if (pFlowFrame && pFlowFrame->GetFollow())
+ pSibling->Prepare( PrepareHint::Clear, nullptr, false );
+ }
+ else
+ { pNxt = pParent->m_pLower;
+ pParent->m_pLower = pSav;
+ pSav->mpUpper = pParent; // set here already, so that it is explicit when invalidating
+
+ if ( pSav->IsContentFrame() )
+ static_cast<SwContentFrame*>(pSav)->InvalidatePage( pPage );
+ else
+ { // pSav might be an empty SectFrame
+ SwContentFrame* pCnt = pParent->ContainsContent();
+ if( pCnt )
+ pCnt->InvalidatePage( pPage );
+ }
+ }
+
+ // the parent needs to grow appropriately
+ SwTwips nGrowVal = 0;
+ SwFrame* pLast;
+ do
+ { pSav->mpUpper = pParent;
+ nGrowVal += aRectFnSet.GetHeight(pSav->getFrameArea());
+ pSav->InvalidateAll_();
+
+ // register Flys, if TextFrames than also invalidate appropriately
+ if ( pSav->IsContentFrame() )
+ {
+ if ( pSav->IsTextFrame() &&
+ static_cast<SwTextFrame*>(pSav)->GetCacheIdx() != USHRT_MAX )
+ static_cast<SwTextFrame*>(pSav)->Init(); // I am its friend
+
+ if ( pPage && pSav->GetDrawObjs() )
+ ::lcl_AddObjsToPage( static_cast<SwContentFrame*>(pSav), pPage );
+ }
+ else
+ { SwContentFrame *pBlub = static_cast<SwLayoutFrame*>(pSav)->ContainsContent();
+ if( pBlub )
+ {
+ do
+ { if ( pPage && pBlub->GetDrawObjs() )
+ ::lcl_AddObjsToPage( pBlub, pPage );
+ if( pBlub->IsTextFrame() && static_cast<SwTextFrame*>(pBlub)->HasFootnote() &&
+ static_cast<SwTextFrame*>(pBlub)->GetCacheIdx() != USHRT_MAX )
+ static_cast<SwTextFrame*>(pBlub)->Init(); // I am its friend
+ pBlub = pBlub->GetNextContentFrame();
+ } while ( pBlub && static_cast<SwLayoutFrame*>(pSav)->IsAnLower( pBlub ));
+ }
+ }
+ pLast = pSav;
+ pSav = pSav->GetNext();
+
+ } while ( pSav );
+
+ if( pNxt )
+ {
+ pLast->mpNext = pNxt;
+ pNxt->mpPrev = pLast;
+ }
+
+ pParent->Grow( nGrowVal );
+}
+
+namespace sw {
+
+bool IsRightPageByNumber(SwRootFrame const& rLayout, sal_uInt16 const nPageNum)
+{
+ assert(rLayout.GetLower());
+ // unfortunately can only get SwPageDesc, not SwFormatPageDesc here...
+ auto const nFirstVirtPageNum(rLayout.GetLower()->GetVirtPageNum());
+ bool const isFirstPageOfLayoutOdd(nFirstVirtPageNum % 2 == 1);
+ return ((nPageNum % 2) == 1) == isFirstPageOfLayoutOdd;
+}
+
+} // namespace sw
+
+SwPageFrame * InsertNewPage( SwPageDesc &rDesc, SwFrame *pUpper,
+ bool const isRightPage, bool const bFirst, bool bInsertEmpty,
+ bool const bFootnote,
+ SwFrame *pSibling,
+ bool const bVeryFirstPage )
+{
+ assert(pUpper);
+ assert(pUpper->IsRootFrame());
+ assert(!pSibling || static_cast<SwLayoutFrame const*>(pUpper)->Lower() != pSibling); // currently no insert before 1st page
+ SwPageFrame *pRet;
+ SwDoc *pDoc = static_cast<SwLayoutFrame*>(pUpper)->GetFormat()->GetDoc();
+ if (bFirst)
+ {
+ if (rDesc.IsFirstShared())
+ {
+ // We need to fallback to left or right page format, decide it now.
+ // FIXME: is this still needed?
+ if (isRightPage)
+ {
+ rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetHeader() );
+ rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetFooter() );
+ // fdo#60250 copy margins for mirrored pages
+ rDesc.GetFirstMaster().SetFormatAttr( rDesc.GetMaster().GetLRSpace() );
+ }
+ else
+ {
+ rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetHeader() );
+ rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetFooter() );
+ rDesc.GetFirstLeft().SetFormatAttr( rDesc.GetLeft().GetLRSpace() );
+ }
+ }
+ }
+ SwFrameFormat *pFormat(isRightPage ? rDesc.GetRightFormat(bFirst) : rDesc.GetLeftFormat(bFirst));
+ // If there is no FrameFormat for this page, add an empty page
+ if ( !pFormat )
+ {
+ pFormat = isRightPage ? rDesc.GetLeftFormat(bVeryFirstPage) : rDesc.GetRightFormat(bVeryFirstPage);
+ OSL_ENSURE( pFormat, "Descriptor without any format?!" );
+ bInsertEmpty = !bInsertEmpty;
+ }
+ if( bInsertEmpty )
+ {
+ SwPageDesc *pTmpDesc = pSibling && pSibling->GetPrev() ?
+ static_cast<SwPageFrame*>(pSibling->GetPrev())->GetPageDesc() : &rDesc;
+ pRet = new SwPageFrame( pDoc->GetEmptyPageFormat(), pUpper, pTmpDesc );
+ SAL_INFO( "sw.pageframe", "InsertNewPage - insert empty p: " << pRet << " d: " << pTmpDesc );
+ pRet->Paste( pUpper, pSibling );
+ pRet->PreparePage( bFootnote );
+ }
+ pRet = new SwPageFrame( pFormat, pUpper, &rDesc );
+ SAL_INFO( "sw.pageframe", "InsertNewPage p: " << pRet << " d: " << &rDesc << " f: " << pFormat );
+ pRet->Paste( pUpper, pSibling );
+ pRet->PreparePage( bFootnote );
+ if ( pRet->GetNext() )
+ SwRootFrame::AssertPageFlys( pRet );
+ return pRet;
+}
+
+/* The following two methods search the layout structure recursively and
+ * register all Flys at the page that have a Frame in this structure as an anchor.
+ */
+
+static void lcl_Regist( SwPageFrame *pPage, const SwFrame *pAnch )
+{
+ SwSortedObjs *pObjs = const_cast<SwSortedObjs*>(pAnch->GetDrawObjs());
+ for (SwAnchoredObject* pObj : *pObjs)
+ {
+ if (SwFlyFrame* pFly = pObj->DynCastFlyFrame())
+ {
+ // register (not if already known)
+ // #i28701# - use new method <GetPageFrame()>
+ SwPageFrame *pPg = pFly->IsFlyFreeFrame()
+ ? pFly->GetPageFrame() : pFly->FindPageFrame();
+ if ( pPg != pPage )
+ {
+ if ( pPg )
+ pPg->RemoveFlyFromPage( pFly );
+ pPage->AppendFlyToPage( pFly );
+ }
+ ::RegistFlys( pPage, pFly );
+ }
+ else
+ {
+ // #i87493#
+ if ( pPage != pObj->GetPageFrame() )
+ {
+ // #i28701#
+ if (SwPageFrame *pPg = pObj->GetPageFrame())
+ pPg->RemoveDrawObjFromPage( *pObj );
+ pPage->AppendDrawObjToPage( *pObj );
+ }
+ }
+
+ const SwFlyFrame* pFly = pAnch->FindFlyFrame();
+ if ( pFly &&
+ pObj->GetDrawObj()->GetOrdNum() < pFly->GetVirtDrawObj()->GetOrdNum() &&
+ pObj->GetDrawObj()->getSdrPageFromSdrObject() )
+ {
+ //#i119945# set pFly's OrdNum to pObj's. So when pFly is removed by Undo, the original OrdNum will not be changed.
+ pObj->DrawObj()->getSdrPageFromSdrObject()->SetObjectOrdNum( pFly->GetVirtDrawObj()->GetOrdNumDirect(),
+ pObj->GetDrawObj()->GetOrdNumDirect() );
+ }
+ }
+}
+
+void RegistFlys( SwPageFrame *pPage, const SwLayoutFrame *pLay )
+{
+ if ( pLay->GetDrawObjs() )
+ ::lcl_Regist( pPage, pLay );
+ const SwFrame *pFrame = pLay->Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ ::RegistFlys( pPage, static_cast<const SwLayoutFrame*>(pFrame) );
+ else if ( pFrame->GetDrawObjs() )
+ ::lcl_Regist( pPage, pFrame );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+/// Notify the background based on the difference between old and new rectangle
+void Notify( SwFlyFrame *pFly, SwPageFrame *pOld, const SwRect &rOld,
+ const SwRect* pOldPrt )
+{
+ const SwRect aFrame( pFly->GetObjRectWithSpaces() );
+ if ( rOld.Pos() != aFrame.Pos() )
+ { // changed position, invalidate old and new area
+ if ( rOld.HasArea() &&
+ rOld.Left()+pFly->GetFormat()->GetLRSpace().GetLeft() < FAR_AWAY )
+ {
+ pFly->NotifyBackground( pOld, rOld, PrepareHint::FlyFrameLeave );
+ }
+ pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive );
+ }
+ else if ( rOld.SSize() != aFrame.SSize() )
+ { // changed size, invalidate the area that was left or is now overlapped
+ // For simplicity, we purposely invalidate a Twip even if not needed.
+
+ SwViewShell *pSh = pFly->getRootFrame()->GetCurrShell();
+ if( pSh && rOld.HasArea() )
+ pSh->InvalidateWindows( rOld );
+
+ // #i51941# - consider case that fly frame isn't
+ // registered at the old page <pOld>
+ SwPageFrame* pPageFrame = pFly->FindPageFrame();
+ if ( pOld != pPageFrame )
+ {
+ pFly->NotifyBackground( pPageFrame, aFrame, PrepareHint::FlyFrameArrive );
+ }
+
+ if ( rOld.Left() != aFrame.Left() )
+ {
+ SwRect aTmp( rOld );
+ aTmp.Union( aFrame );
+ aTmp.Left( std::min(aFrame.Left(), rOld.Left()) );
+ aTmp.Right( std::max(aFrame.Left(), rOld.Left()) );
+ pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
+ }
+ SwTwips nOld = rOld.Right();
+ SwTwips nNew = aFrame.Right();
+ if ( nOld != nNew )
+ {
+ SwRect aTmp( rOld );
+ aTmp.Union( aFrame );
+ aTmp.Left( std::min(nNew, nOld) );
+ aTmp.Right( std::max(nNew, nOld) );
+ pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
+ }
+ if ( rOld.Top() != aFrame.Top() )
+ {
+ SwRect aTmp( rOld );
+ aTmp.Union( aFrame );
+ aTmp.Top( std::min(aFrame.Top(), rOld.Top()) );
+ aTmp.Bottom( std::max(aFrame.Top(), rOld.Top()) );
+ pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
+ }
+ nOld = rOld.Bottom();
+ nNew = aFrame.Bottom();
+ if ( nOld != nNew )
+ {
+ SwRect aTmp( rOld );
+ aTmp.Union( aFrame );
+ aTmp.Top( std::min(nNew, nOld) );
+ aTmp.Bottom( std::max(nNew, nOld) );
+ pFly->NotifyBackground( pOld, aTmp, PrepareHint::FlyFrameSizeChanged );
+ }
+ }
+ else if(pOldPrt && *pOldPrt != pFly->getFramePrintArea())
+ {
+ bool bNotifyBackground(pFly->GetFormat()->GetSurround().IsContour());
+
+ if(!bNotifyBackground &&
+ pFly->IsFlyFreeFrame() &&
+ static_cast< const SwFlyFreeFrame* >(pFly)->supportsAutoContour())
+ {
+ // RotateFlyFrame3: Also notify for FlyFrames which allow AutoContour
+ bNotifyBackground = true;
+ }
+
+ if(bNotifyBackground)
+ {
+ // #i24097#
+ pFly->NotifyBackground( pFly->FindPageFrame(), aFrame, PrepareHint::FlyFrameArrive );
+ }
+ }
+}
+
+static void lcl_CheckFlowBack( SwFrame* pFrame, const SwRect &rRect )
+{
+ SwTwips nBottom = rRect.Bottom();
+ while( pFrame )
+ {
+ if( pFrame->IsLayoutFrame() )
+ {
+ if( rRect.Overlaps( pFrame->getFrameArea() ) )
+ lcl_CheckFlowBack( static_cast<SwLayoutFrame*>(pFrame)->Lower(), rRect );
+ }
+ else if( !pFrame->GetNext() && nBottom > pFrame->getFrameArea().Bottom() )
+ {
+ if( pFrame->IsContentFrame() && static_cast<SwContentFrame*>(pFrame)->HasFollow() )
+ pFrame->InvalidateSize();
+ else
+ pFrame->InvalidateNextPos();
+ }
+ pFrame = pFrame->GetNext();
+ }
+}
+
+static void lcl_NotifyContent( const SdrObject *pThis, SwContentFrame *pCnt,
+ const SwRect &rRect, const PrepareHint eHint )
+{
+ if ( !pCnt->IsTextFrame() )
+ return;
+
+ SwRect aCntPrt( pCnt->getFramePrintArea() );
+ aCntPrt.Pos() += pCnt->getFrameArea().Pos();
+ if ( eHint == PrepareHint::FlyFrameAttributesChanged )
+ {
+ // #i35640# - use given rectangle <rRect> instead
+ // of current bound rectangle
+ if ( aCntPrt.Overlaps( rRect ) )
+ pCnt->Prepare( PrepareHint::FlyFrameAttributesChanged );
+ }
+ // #i23129# - only invalidate, if the text frame
+ // printing area overlaps with the given rectangle.
+ else if ( aCntPrt.Overlaps( rRect ) )
+ pCnt->Prepare( eHint, static_cast<void*>(&aCntPrt.Intersection_( rRect )) );
+ if ( !pCnt->GetDrawObjs() )
+ return;
+
+ const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ SwContentFrame *pContent = pFly->ContainsContent();
+ while ( pContent )
+ {
+ ::lcl_NotifyContent( pThis, pContent, rRect, eHint );
+ pContent = pContent->GetNextContentFrame();
+ }
+ }
+ }
+ }
+}
+
+void Notify_Background( const SdrObject* pObj,
+ SwPageFrame* pPage,
+ const SwRect& rRect,
+ const PrepareHint eHint,
+ const bool bInva )
+{
+ // If the frame was positioned correctly for the first time, do not inform the old area
+ if ( eHint == PrepareHint::FlyFrameLeave && rRect.Top() == FAR_AWAY )
+ return;
+
+ SwLayoutFrame* pArea;
+ SwFlyFrame *pFlyFrame = nullptr;
+ SwFrame* pAnchor;
+ if( auto pVirtFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>( pObj) )
+ {
+ pFlyFrame = const_cast<SwVirtFlyDrawObj*>(pVirtFlyDrawObj)->GetFlyFrame();
+ pAnchor = pFlyFrame->AnchorFrame();
+ }
+ else
+ {
+ pFlyFrame = nullptr;
+ pAnchor = const_cast<SwFrame*>(
+ GetUserCall(pObj)->GetAnchoredObj( pObj )->GetAnchorFrame() );
+ }
+ if( PrepareHint::FlyFrameLeave != eHint && pAnchor->IsInFly() )
+ pArea = pAnchor->FindFlyFrame();
+ else
+ pArea = pPage;
+ SwContentFrame *pCnt = nullptr;
+ if ( pArea )
+ {
+ if( PrepareHint::FlyFrameArrive != eHint )
+ lcl_CheckFlowBack( pArea, rRect );
+
+ // Only the Flys following this anchor are reacting. Thus, those do not
+ // need to be processed.
+ // An exception is LEAVE, since the Fly might come "from above".
+ // If the anchor is positioned on the previous page, the whole page
+ // needs to be processed (47722).
+ // OD 2004-05-13 #i28701# - If the wrapping style has to be considered
+ // on the object positioning, the complete area has to be processed,
+ // because content frames before the anchor frame also have to consider
+ // the object for the text wrapping.
+ // #i3317# - The complete area has always been
+ // processed.
+ {
+ pCnt = pArea->ContainsContent();
+ }
+ }
+ SwFrame *pLastTab = nullptr;
+
+ bool isValidTableBeforeAnchor(false);
+ while ( pCnt && pArea && pArea->IsAnLower( pCnt ) )
+ {
+ ::lcl_NotifyContent( pObj, pCnt, rRect, eHint );
+ if ( pCnt->IsInTab() )
+ {
+ SwTabFrame *pTab = pCnt->FindTabFrame();
+ if ( pTab != pLastTab )
+ {
+ pLastTab = pTab;
+ isValidTableBeforeAnchor = false;
+ if (PrepareHint::FlyFrameArrive == eHint
+ && pFlyFrame // TODO: do it for draw objects too?
+ && pTab->IsFollow() // table starts on previous page?
+ // "through" means they will actually overlap anyway
+ && css::text::WrapTextMode_THROUGH != pFlyFrame->GetFormat()->GetSurround().GetSurround()
+ // if it's anchored in footer it can't move to other page
+ && !pAnchor->FindFooterOrHeader())
+ {
+ SwFrame * pTmp(pAnchor->GetPrev());
+ while (pTmp)
+ {
+ if (pTmp == pTab)
+ {
+ // tdf#99460 the table shouldn't be moved by the fly
+ isValidTableBeforeAnchor = true;
+ break;
+ }
+ pTmp = pTmp->GetPrev();
+ }
+ }
+ // #i40606# - use <GetLastBoundRect()>
+ // instead of <GetCurrentBoundRect()>, because a recalculation
+ // of the bounding rectangle isn't intended here.
+ if (!isValidTableBeforeAnchor
+ && (pTab->getFrameArea().Overlaps(SwRect(pObj->GetLastBoundRect())) ||
+ pTab->getFrameArea().Overlaps(rRect)))
+ {
+ if ( !pFlyFrame || !pFlyFrame->IsLowerOf( pTab ) )
+ pTab->InvalidatePrt();
+ }
+ }
+ SwLayoutFrame* pCell = pCnt->GetUpper();
+ // #i40606# - use <GetLastBoundRect()>
+ // instead of <GetCurrentBoundRect()>, because a recalculation
+ // of the bounding rectangle isn't intended here.
+ if (!isValidTableBeforeAnchor && pCell->IsCellFrame() &&
+ ( pCell->getFrameArea().Overlaps( SwRect(pObj->GetLastBoundRect()) ) ||
+ pCell->getFrameArea().Overlaps( rRect ) ) )
+ {
+ const SwFormatVertOrient &rOri = pCell->GetFormat()->GetVertOrient();
+ if ( text::VertOrientation::NONE != rOri.GetVertOrient() )
+ pCell->InvalidatePrt();
+ }
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ // #128702# - make code robust
+ if ( pPage && pPage->GetSortedObjs() )
+ {
+ pObj->GetOrdNum();
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( pAnchoredObj->DynCastFlyFrame() != nullptr )
+ {
+ if( pAnchoredObj->GetDrawObj() == pObj )
+ continue;
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pAnchoredObj);
+ if ( pFly->getFrameArea().Top() == FAR_AWAY )
+ continue;
+
+ if ( !pFlyFrame ||
+ (!pFly->IsLowerOf( pFlyFrame ) &&
+ pFly->GetVirtDrawObj()->GetOrdNumDirect() < pObj->GetOrdNumDirect()))
+ {
+ pCnt = pFly->ContainsContent();
+ while ( pCnt )
+ {
+ ::lcl_NotifyContent( pObj, pCnt, rRect, eHint );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ if( pFly->IsFlyLayFrame() )
+ {
+ if( pFly->Lower() && pFly->Lower()->IsColumnFrame() &&
+ pFly->getFrameArea().Bottom() >= rRect.Top() &&
+ pFly->getFrameArea().Top() <= rRect.Bottom() &&
+ pFly->getFrameArea().Right() >= rRect.Left() &&
+ pFly->getFrameArea().Left() <= rRect.Right() )
+ {
+ pFly->InvalidateSize();
+ }
+ }
+ // Flys above myself might sidestep if they have an automatic
+ // alignment. This happens independently of my attributes since
+ // this might have been changed as well.
+ else if ( pFly->IsFlyAtContentFrame() &&
+ pObj->GetOrdNumDirect() <
+ pFly->GetVirtDrawObj()->GetOrdNumDirect() &&
+ pFlyFrame && !pFly->IsLowerOf( pFlyFrame ) )
+ {
+ const SwFormatHoriOrient &rH = pFly->GetFormat()->GetHoriOrient();
+ if ( text::HoriOrientation::NONE != rH.GetHoriOrient() &&
+ text::HoriOrientation::CENTER != rH.GetHoriOrient() &&
+ ( !pFly->IsAutoPos() || text::RelOrientation::CHAR != rH.GetRelationOrient() ) &&
+ (pFly->getFrameArea().Bottom() >= rRect.Top() &&
+ pFly->getFrameArea().Top() <= rRect.Bottom()) )
+ pFly->InvalidatePos();
+ }
+ }
+ }
+ }
+ if ( pFlyFrame && pAnchor->GetUpper() && pAnchor->IsInTab() )//MA_FLY_HEIGHT
+ pAnchor->GetUpper()->InvalidateSize();
+
+ // #i82258# - make code robust
+ SwViewShell* pSh = nullptr;
+ if ( bInva && pPage &&
+ nullptr != (pSh = pPage->getRootFrame()->GetCurrShell()) )
+ {
+ pSh->InvalidateWindows( rRect );
+ }
+}
+
+/// Provides the Upper of an anchor in paragraph-bound objects. If the latter
+/// is a chained border or a footnote, the "virtual" Upper might be returned.
+const SwFrame* GetVirtualUpper( const SwFrame* pFrame, const Point& rPos )
+{
+ if( pFrame->IsTextFrame() )
+ {
+ pFrame = pFrame->GetUpper();
+ if( !pFrame->getFrameArea().Contains( rPos ) )
+ {
+ if( pFrame->IsFootnoteFrame() )
+ {
+ const SwFootnoteFrame* pTmp = static_cast<const SwFootnoteFrame*>(pFrame)->GetFollow();
+ while( pTmp )
+ {
+ if( pTmp->getFrameArea().Contains( rPos ) )
+ return pTmp;
+ pTmp = pTmp->GetFollow();
+ }
+ }
+ else
+ {
+ SwFlyFrame* pTmp = const_cast<SwFlyFrame*>(pFrame->FindFlyFrame());
+ while( pTmp )
+ {
+ if( pTmp->getFrameArea().Contains( rPos ) )
+ return pTmp;
+ pTmp = pTmp->GetNextLink();
+ }
+ }
+ }
+ }
+ return pFrame;
+}
+
+bool Is_Lower_Of(const SwFrame *pCurrFrame, const SdrObject* pObj)
+{
+ Point aPos;
+ const SwFrame* pFrame;
+ if (const SwVirtFlyDrawObj *pFlyDrawObj = dynamic_cast<const SwVirtFlyDrawObj*>(pObj))
+ {
+ const SwFlyFrame* pFly = pFlyDrawObj->GetFlyFrame();
+ pFrame = pFly->GetAnchorFrame();
+ aPos = pFly->getFrameArea().Pos();
+ }
+ else
+ {
+ pFrame = static_cast<SwDrawContact*>(GetUserCall(pObj))->GetAnchorFrame(pObj);
+ aPos = pObj->GetCurrentBoundRect().TopLeft();
+ }
+ OSL_ENSURE( pFrame, "8-( Fly is lost in Space." );
+ pFrame = GetVirtualUpper( pFrame, aPos );
+ do
+ { if ( pFrame == pCurrFrame )
+ return true;
+ if( pFrame->IsFlyFrame() )
+ {
+ aPos = pFrame->getFrameArea().Pos();
+ pFrame = GetVirtualUpper( static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(), aPos );
+ }
+ else
+ pFrame = pFrame->GetUpper();
+ } while ( pFrame );
+ return false;
+}
+
+/// provides the area of a frame in that no Fly from another area can overlap
+const SwFrame *FindContext( const SwFrame *pFrame, SwFrameType nAdditionalContextType )
+{
+ const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont |
+ SwFrameType::Ftn | SwFrameType::Fly |
+ SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell |
+ nAdditionalContextType;
+ do
+ { if ( pFrame->GetType() & nTyp )
+ break;
+ pFrame = pFrame->GetUpper();
+ } while( pFrame );
+ return pFrame;
+}
+
+bool IsFrameInSameContext( const SwFrame *pInnerFrame, const SwFrame *pFrame )
+{
+ const SwFrame *pContext = FindContext( pInnerFrame, SwFrameType::None );
+
+ const SwFrameType nTyp = SwFrameType::Root | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont |
+ SwFrameType::Ftn | SwFrameType::Fly |
+ SwFrameType::Tab | SwFrameType::Row | SwFrameType::Cell;
+ do
+ { if ( pFrame->GetType() & nTyp )
+ {
+ if( pFrame == pContext )
+ return true;
+ if( pFrame->IsCellFrame() )
+ return false;
+ }
+ if( pFrame->IsFlyFrame() )
+ {
+ Point aPos( pFrame->getFrameArea().Pos() );
+ pFrame = GetVirtualUpper( static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame(), aPos );
+ }
+ else
+ pFrame = pFrame->GetUpper();
+ } while( pFrame );
+
+ return false;
+}
+
+static SwTwips lcl_CalcCellRstHeight( SwLayoutFrame *pCell )
+{
+ SwFrame *pLow = pCell->Lower();
+ if ( pLow && (pLow->IsContentFrame() || pLow->IsSctFrame()) )
+ {
+ tools::Long nHeight = 0, nFlyAdd = 0;
+ do
+ {
+ tools::Long nLow = pLow->getFrameArea().Height();
+ if( pLow->IsTextFrame() && static_cast<SwTextFrame*>(pLow)->IsUndersized() )
+ nLow += static_cast<SwTextFrame*>(pLow)->GetParHeight()-pLow->getFramePrintArea().Height();
+ else if( pLow->IsSctFrame() && static_cast<SwSectionFrame*>(pLow)->IsUndersized() )
+ nLow += static_cast<SwSectionFrame*>(pLow)->Undersize();
+ nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLow );
+ nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
+ nHeight += nLow;
+ pLow = pLow->GetNext();
+ } while ( pLow );
+ if ( nFlyAdd )
+ nHeight += nFlyAdd;
+
+ // The border cannot be calculated based on PrtArea and Frame, since both can be invalid.
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
+
+ return pCell->getFrameArea().Height() - nHeight;
+ }
+ else
+ {
+ tools::Long nRstHeight = 0;
+ while (pLow && pLow->IsLayoutFrame())
+ {
+ nRstHeight += ::CalcRowRstHeight(static_cast<SwLayoutFrame*>(pLow));
+ pLow = pLow->GetNext();
+ }
+ return nRstHeight;
+ }
+}
+
+SwTwips CalcRowRstHeight( SwLayoutFrame *pRow )
+{
+ SwFrame *pLow = pRow->Lower();
+ if (!(pLow && pLow->IsLayoutFrame()))
+ {
+ return 0;
+ }
+ SwTwips nRstHeight = LONG_MAX;
+ while (pLow && pLow->IsLayoutFrame())
+ {
+ nRstHeight = std::min(nRstHeight, ::lcl_CalcCellRstHeight(static_cast<SwLayoutFrame*>(pLow)));
+ pLow = pLow->GetNext();
+ }
+ return nRstHeight;
+}
+
+const SwFrame* FindPage( const SwRect &rRect, const SwFrame *pPage )
+{
+ if ( !rRect.Overlaps( pPage->getFrameArea() ) )
+ {
+ const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(pPage->GetUpper());
+ const SwFrame* pTmpPage = pRootFrame ? pRootFrame->GetPageAtPos( rRect.TopLeft(), &rRect.SSize(), true ) : nullptr;
+ if ( pTmpPage )
+ pPage = pTmpPage;
+ }
+
+ return pPage;
+}
+
+namespace {
+
+class SwFrameHolder : private SfxListener
+{
+ SwFrame* m_pFrame;
+ bool m_bSet;
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+public:
+ SwFrameHolder()
+ : m_pFrame(nullptr)
+ , m_bSet(false)
+ {
+ }
+ void SetFrame( SwFrame* pHold );
+ SwFrame* GetFrame() { return m_pFrame; }
+ void Reset();
+ bool IsSet() const { return m_bSet; }
+};
+
+}
+
+void SwFrameHolder::SetFrame( SwFrame* pHold )
+{
+ m_bSet = true;
+ if (m_pFrame != pHold)
+ {
+ if (m_pFrame)
+ EndListening(*m_pFrame);
+ StartListening(*pHold);
+ m_pFrame = pHold;
+ }
+}
+
+void SwFrameHolder::Reset()
+{
+ if (m_pFrame)
+ EndListening(*m_pFrame);
+ m_bSet = false;
+ m_pFrame = nullptr;
+}
+
+void SwFrameHolder::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::Dying && &rBC == m_pFrame)
+ {
+ m_pFrame = nullptr;
+ }
+}
+
+SwFrame* GetFrameOfModify(SwRootFrame const*const pLayout, sw::BroadcastingModify const& rMod,
+ SwFrameType const nFrameType, SwPosition const*const pPos,
+ std::pair<Point, bool> const*const pViewPosAndCalcFrame)
+{
+ SwFrame *pMinFrame = nullptr, *pTmpFrame;
+ SwFrameHolder aHolder;
+ SwRect aCalcRect;
+ bool bClientIterChanged = false;
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(rMod);
+ do {
+ pMinFrame = nullptr;
+ aHolder.Reset();
+ sal_uInt64 nMinDist = 0;
+ bClientIterChanged = false;
+
+ for( pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
+ {
+ if( pTmpFrame->GetType() & nFrameType &&
+ ( !pLayout || pLayout == pTmpFrame->getRootFrame() ) &&
+ (!pTmpFrame->IsFlowFrame() ||
+ !SwFlowFrame::CastFlowFrame( pTmpFrame )->IsFollow() ))
+ {
+ if (pViewPosAndCalcFrame)
+ {
+ // watch for Frame being deleted
+ if ( pMinFrame )
+ aHolder.SetFrame( pMinFrame );
+ else
+ aHolder.Reset();
+
+ if (pViewPosAndCalcFrame->second)
+ {
+ // tdf#108118 prevent recursion
+ DisableCallbackAction a(*pTmpFrame->getRootFrame());
+ // - format parent Writer
+ // fly frame, if it isn't been formatted yet.
+ // Note: The Writer fly frame could be the frame itself.
+ SwFlyFrame* pFlyFrame( pTmpFrame->FindFlyFrame() );
+ if ( pFlyFrame &&
+ pFlyFrame->getFrameArea().Pos().X() == FAR_AWAY &&
+ pFlyFrame->getFrameArea().Pos().Y() == FAR_AWAY )
+ {
+ SwObjectFormatter::FormatObj( *pFlyFrame );
+ }
+ pTmpFrame->Calc(pLayout ? pLayout->GetCurrShell()->GetOut() : nullptr);
+ }
+
+ // aIter.IsChanged checks if the current pTmpFrame has been deleted while
+ // it is the current iterator
+ // FrameHolder watches for deletion of the current pMinFrame
+ if( aIter.IsChanged() || ( aHolder.IsSet() && !aHolder.GetFrame() ) )
+ {
+ // restart iteration
+ bClientIterChanged = true;
+ break;
+ }
+
+ // for Flys go via the parent if the Fly is not yet "formatted"
+ if (!pViewPosAndCalcFrame->second &&
+ pTmpFrame->GetType() & SwFrameType::Fly &&
+ static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame() &&
+ FAR_AWAY == pTmpFrame->getFrameArea().Pos().getX() &&
+ FAR_AWAY == pTmpFrame->getFrameArea().Pos().getY() )
+ aCalcRect = static_cast<SwFlyFrame*>(pTmpFrame)->GetAnchorFrame()->getFrameArea();
+ else
+ aCalcRect = pTmpFrame->getFrameArea();
+
+ if (aCalcRect.Contains(pViewPosAndCalcFrame->first))
+ {
+ pMinFrame = pTmpFrame;
+ break;
+ }
+
+ // Point not in rectangle. Compare distances:
+ const Point aCalcRectCenter = aCalcRect.Center();
+ const Point aDiff = aCalcRectCenter - pViewPosAndCalcFrame->first;
+ const sal_uInt64 nCurrentDist = sal_Int64(aDiff.getX()) * sal_Int64(aDiff.getX()) + sal_Int64(aDiff.getY()) * sal_Int64(aDiff.getY()); // opt: no sqrt
+ if ( !pMinFrame || nCurrentDist < nMinDist )
+ {
+ pMinFrame = pTmpFrame;
+ nMinDist = nCurrentDist;
+ }
+ }
+ else
+ {
+ // if no pViewPosAndCalcFrame is provided, take the first one
+ pMinFrame = pTmpFrame;
+ break;
+ }
+ }
+ }
+ } while( bClientIterChanged );
+
+ if( pPos && pMinFrame && pMinFrame->IsTextFrame() )
+ return static_cast<SwTextFrame*>(pMinFrame)->GetFrameAtPos( *pPos );
+
+ return pMinFrame;
+}
+
+bool IsExtraData( const SwDoc *pDoc )
+{
+ const SwLineNumberInfo &rInf = pDoc->GetLineNumberInfo();
+ if (rInf.IsPaintLineNumbers() ||
+ rInf.IsCountInFlys() ||
+ (static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE &&
+ !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty()))
+ {
+ return true;
+ }
+
+ const SwEditShell* pSh = pDoc->GetEditShell();
+ const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr;
+ return pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton();
+}
+
+// OD 22.09.2003 #110978#
+SwRect SwPageFrame::PrtWithoutHeaderAndFooter() const
+{
+ SwRect aPrtWithoutHeaderFooter( getFramePrintArea() );
+ aPrtWithoutHeaderFooter.Pos() += getFrameArea().Pos();
+
+ const SwFrame* pLowerFrame = Lower();
+ while ( pLowerFrame )
+ {
+ // Note: independent on text direction page header and page footer are
+ // always at top respectively at bottom of the page frame.
+ if ( pLowerFrame->IsHeaderFrame() )
+ {
+ aPrtWithoutHeaderFooter.AddTop( pLowerFrame->getFrameArea().Height() );
+ }
+ if ( pLowerFrame->IsFooterFrame() )
+ {
+ aPrtWithoutHeaderFooter.AddBottom( - pLowerFrame->getFrameArea().Height() );
+ }
+
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+
+ return aPrtWithoutHeaderFooter;
+}
+
+/** method to determine the spacing values of a frame
+
+ OD 2004-03-10 #i28701#
+ OD 2009-08-28 #i102458#
+ Add output parameter <obIsLineSpacingProportional>
+*/
+void GetSpacingValuesOfFrame( const SwFrame& rFrame,
+ SwTwips& onLowerSpacing,
+ SwTwips& onLineSpacing,
+ bool& obIsLineSpacingProportional,
+ bool bIdenticalStyles )
+{
+ if ( !rFrame.IsFlowFrame() )
+ {
+ onLowerSpacing = 0;
+ onLineSpacing = 0;
+ }
+ else
+ {
+ const SvxULSpaceItem& rULSpace = rFrame.GetAttrSet()->GetULSpace();
+ // check contextual spacing if the style of actual and next paragraphs are identical
+ if (bIdenticalStyles)
+ onLowerSpacing = (rULSpace.GetContext() ? 0 : rULSpace.GetLower());
+ else
+ onLowerSpacing = rULSpace.GetLower();
+
+ onLineSpacing = 0;
+ obIsLineSpacingProportional = false;
+ if ( rFrame.IsTextFrame() )
+ {
+ onLineSpacing = static_cast<const SwTextFrame&>(rFrame).GetLineSpace();
+ obIsLineSpacingProportional =
+ onLineSpacing != 0 &&
+ static_cast<const SwTextFrame&>(rFrame).GetLineSpace( true ) == 0;
+ }
+
+ OSL_ENSURE( onLowerSpacing >= 0 && onLineSpacing >= 0,
+ "<GetSpacingValuesOfFrame(..)> - spacing values aren't positive!" );
+ }
+}
+
+/// get the content of the table cell, skipping content from nested tables
+const SwContentFrame* GetCellContent( const SwLayoutFrame& rCell )
+{
+ const SwContentFrame* pContent = rCell.ContainsContent();
+ const SwTabFrame* pTab = rCell.FindTabFrame();
+
+ while ( pContent && rCell.IsAnLower( pContent ) )
+ {
+ const SwTabFrame* pTmpTab = pContent->FindTabFrame();
+ if ( pTmpTab != pTab )
+ {
+ SwFrame const*const pTmp = pTmpTab->FindLastContentOrTable();
+ if (pTmp)
+ {
+ pContent = pTmp->FindNextCnt();
+ }
+ else
+ {
+ pContent = nullptr;
+ }
+ }
+ else
+ break;
+ }
+ return pContent;
+}
+
+SwDeletionChecker::SwDeletionChecker(const SwFrame* pFrame)
+ : mpFrame( pFrame )
+ , mpRegIn( pFrame
+ ? pFrame->IsTextFrame()
+ // sw_redlinehide: GetDep() may be a member of SwTextFrame!
+ ? static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()
+ : const_cast<SwFrame*>(pFrame)->GetDep()
+ : nullptr )
+{
+}
+
+/// Can be used to check if a frame has been deleted
+bool SwDeletionChecker::HasBeenDeleted() const
+{
+ if ( !mpFrame || !mpRegIn )
+ return false;
+
+ SwIterator<SwFrame, sw::BroadcastingModify, sw::IteratorMode::UnwrapMulti> aIter(*mpRegIn);
+ SwFrame* pLast = aIter.First();
+ while ( pLast )
+ {
+ if ( pLast == mpFrame )
+ return false;
+ pLast = aIter.Next();
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/ftnfrm.cxx b/sw/source/core/layout/ftnfrm.cxx
new file mode 100644
index 000000000..652436eb4
--- /dev/null
+++ b/sw/source/core/layout/ftnfrm.cxx
@@ -0,0 +1,2987 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <ftnidx.hxx>
+#include <pagefrm.hxx>
+#include <colfrm.hxx>
+#include <rootfrm.hxx>
+#include <frmtool.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <tabfrm.hxx>
+#include <pagedesc.hxx>
+#include <ftninfo.hxx>
+#include <sectfrm.hxx>
+#include <objectformatter.hxx>
+#include <viewopt.hxx>
+#include <calbck.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+#define ENDNOTE 0x80000000
+
+/// Search the position of an attribute in the FootnoteArray at the document,
+/// because all footnotes are located there, ordered by their index.
+static sal_uLong lcl_FindFootnotePos( const SwDoc *pDoc, const SwTextFootnote *pAttr )
+{
+ const SwFootnoteIdxs &rFootnoteIdxs = pDoc->GetFootnoteIdxs();
+
+ SwTextFootnote* pBla = const_cast<SwTextFootnote*>(pAttr);
+ SwFootnoteIdxs::const_iterator it = rFootnoteIdxs.find( pBla );
+ if ( it != rFootnoteIdxs.end() )
+ {
+ sal_uLong nRet = it - rFootnoteIdxs.begin();
+ if( pAttr->GetFootnote().IsEndNote() )
+ return nRet + ENDNOTE;
+ return nRet;
+ }
+ OSL_ENSURE( !pDoc, "FootnotePos not found." );
+ return 0;
+}
+
+bool SwFootnoteFrame::operator<( const SwTextFootnote* pTextFootnote ) const
+{
+ const SwDoc* pDoc = GetFormat()->GetDoc();
+ OSL_ENSURE( pDoc, "SwFootnoteFrame: Missing doc!" );
+ return lcl_FindFootnotePos( pDoc, GetAttr() ) <
+ lcl_FindFootnotePos( pDoc, pTextFootnote );
+}
+
+/*
+|*
+|* bool lcl_NextFootnoteBoss( SwFootnoteBossFrame* pBoss, SwPageFrame* pPage)
+|* sets pBoss on the next SwFootnoteBossFrame, which can either be a column
+|* or a page (without columns). If the page changes meanwhile,
+|* pPage contains the new page and this function returns true.
+|*
+|*/
+
+static bool lcl_NextFootnoteBoss( SwFootnoteBossFrame* &rpBoss, SwPageFrame* &rpPage,
+ bool bDontLeave )
+{
+ if( rpBoss->IsColumnFrame() )
+ {
+ if( rpBoss->GetNext() )
+ {
+ rpBoss = static_cast<SwFootnoteBossFrame*>(rpBoss->GetNext()); //next column
+ return false;
+ }
+ if( rpBoss->IsInSct() )
+ {
+ SwSectionFrame* pSct = rpBoss->FindSctFrame()->GetFollow();
+ if( pSct )
+ {
+ OSL_ENSURE( pSct->Lower() && pSct->Lower()->IsColumnFrame(),
+ "Where's the column?" );
+ rpBoss = static_cast<SwColumnFrame*>(pSct->Lower());
+ SwPageFrame* pOld = rpPage;
+ rpPage = pSct->FindPageFrame();
+ return pOld != rpPage;
+ }
+ else if( bDontLeave )
+ {
+ rpPage = nullptr;
+ rpBoss = nullptr;
+ return false;
+ }
+ }
+ }
+ rpPage = static_cast<SwPageFrame*>(rpPage->GetNext()); // next page
+ rpBoss = rpPage;
+ if( rpPage )
+ {
+ SwLayoutFrame* pBody = rpPage->FindBodyCont();
+ if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ rpBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower()); // first column
+ }
+ return true;
+}
+
+/// @returns column number if pBoss is a column, otherwise 0.
+static sal_uInt16 lcl_ColumnNum( const SwFrame* pBoss )
+{
+ sal_uInt16 nRet = 0;
+ if( !pBoss->IsColumnFrame() )
+ return 0;
+ const SwFrame* pCol;
+ if( pBoss->IsInSct() )
+ {
+ pCol = pBoss->GetUpper()->FindColFrame();
+ if( pBoss->GetNext() || pBoss->GetPrev() )
+ {
+ while( pBoss )
+ {
+ ++nRet; // Section columns
+ pBoss = pBoss->GetPrev();
+ }
+ }
+ }
+ else
+ pCol = pBoss;
+ while( pCol )
+ {
+ nRet += 256; // Page columns
+ pCol = pCol->GetPrev();
+ }
+ return nRet;
+}
+
+SwFootnoteContFrame::SwFootnoteContFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
+ SwLayoutFrame( pFormat, pSib )
+{
+ mnFrameType = SwFrameType::FtnCont;
+}
+
+SwFootnoteFrame* SwFootnoteContFrame::AddChained(bool bAppend, SwFrame* pThis, bool bDefaultFormat)
+{
+ SwFootnoteFrame *pOld = pThis->FindFootnoteFrame();
+ SwFrameFormat *pFormat = pOld->GetFormat();
+ if (bDefaultFormat)
+ pFormat = pFormat->GetDoc()->GetDfltFrameFormat();
+
+ SwFootnoteFrame *pNew = new SwFootnoteFrame(pFormat, pOld, pOld->GetRef(), pOld->GetAttr());
+
+ if (bAppend)
+ {
+ if (pOld->GetFollow())
+ {
+ pNew->SetFollow(pOld->GetFollow());
+ pOld->GetFollow()->SetMaster(pNew);
+ }
+ pOld->SetFollow(pNew);
+ pNew->SetMaster(pOld);
+ }
+ else
+ {
+ if (pOld->GetMaster())
+ {
+ pNew->SetMaster(pOld->GetMaster());
+ pOld->GetMaster()->SetFollow(pNew);
+ }
+ pNew->SetFollow(pOld);
+ pOld->SetMaster(pNew);
+ }
+
+ return pNew;
+}
+
+// lcl_Undersize(..) walks over a SwFrame and its contents
+// and returns the sum of all requested TextFrame magnifications.
+
+static tools::Long lcl_Undersize( const SwFrame* pFrame )
+{
+ tools::Long nRet = 0;
+ SwRectFnSet aRectFnSet(pFrame);
+ if( pFrame->IsTextFrame() )
+ {
+ if( static_cast<const SwTextFrame*>(pFrame)->IsUndersized() )
+ {
+ // Does this TextFrame would like to be a little bit bigger?
+ nRet = static_cast<const SwTextFrame*>(pFrame)->GetParHeight() -
+ aRectFnSet.GetHeight(pFrame->getFramePrintArea());
+ if( nRet < 0 )
+ nRet = 0;
+ }
+ }
+ else if( pFrame->IsLayoutFrame() )
+ {
+ const SwFrame* pNxt = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
+ while( pNxt )
+ {
+ nRet += lcl_Undersize( pNxt );
+ pNxt = pNxt->GetNext();
+ }
+ }
+ return nRet;
+}
+
+namespace sw {
+
+SwTwips FootnoteSeparatorHeight(SwPageFootnoteInfo const& rInf)
+{
+ return rInf.GetTopDist() + rInf.GetBottomDist() + rInf.GetLineWidth();
+}
+
+} // namespace sw
+
+/// "format" the frame (Fixsize is not set here).
+void SwFootnoteContFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ // calculate total border, only one distance to the top
+ const SwPageFrame* pPage = FindPageFrame();
+ const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
+ const SwTwips nBorder = sw::FootnoteSeparatorHeight(rInf);
+ SwRectFnSet aRectFnSet(this);
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+
+ aRectFnSet.SetTop( aPrt, nBorder );
+ aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(getFrameArea()) );
+ aRectFnSet.SetHeight(aPrt, aRectFnSet.GetHeight(getFrameArea()) - nBorder );
+
+ if( aRectFnSet.GetHeight(aPrt) < 0 && !pPage->IsFootnotePage() )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ bool bGrow = pPage->IsFootnotePage();
+ if( bGrow )
+ {
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ bGrow = false;
+ }
+ if( bGrow )
+ Grow( LONG_MAX );
+ else
+ {
+ // VarSize is determined based on the content plus the borders
+ SwTwips nRemaining = 0;
+ SwFrame *pFrame = m_pLower;
+ while ( pFrame )
+ { // lcl_Undersize(..) respects (recursively) TextFrames, which
+ // would like to be bigger. They are created especially in
+ // columnized borders, if these do not have their maximum
+ // size yet.
+ nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea()) + lcl_Undersize( pFrame );
+ pFrame = pFrame->GetNext();
+ }
+ // add the own border
+ nRemaining += nBorder;
+
+ SwTwips nDiff;
+ if( IsInSct() )
+ {
+ nDiff = -aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
+ if( nDiff > 0 )
+ {
+ if( nDiff > aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ nDiff = aRectFnSet.GetHeight(getFrameArea());
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, -nDiff );
+ aRectFnSet.AddHeight( aFrm, -nDiff );
+ }
+ }
+ nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
+ if ( nDiff > 0 )
+ Shrink( nDiff );
+ else if ( nDiff < 0 )
+ {
+ Grow( -nDiff );
+ // It may happen that there is less space available,
+ // than what the border needs - the size of the PrtArea
+ // will then be negative.
+ SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( nPrtHeight < 0 )
+ {
+ const SwTwips nTmpDiff = std::max( SwTwips(aRectFnSet.GetTop(getFramePrintArea())), -nPrtHeight );
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SubTop( aPrt, nTmpDiff );
+ }
+ }
+ }
+
+ setFrameAreaSizeValid(true);
+}
+
+SwTwips SwFootnoteContFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
+{
+ // No check if FixSize since FootnoteContainer are variable up to their max. height.
+ // If the max. height is LONG_MAX, take as much space as needed.
+ // If the page is a special footnote page, take also as much as possible.
+ assert(GetUpper() && GetUpper()->IsFootnoteBossFrame());
+
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.GetHeight(getFrameArea()) > 0 &&
+ nDist > ( LONG_MAX - aRectFnSet.GetHeight(getFrameArea()) ) )
+ nDist = LONG_MAX - aRectFnSet.GetHeight(getFrameArea());
+
+ SwFootnoteBossFrame *pBoss = static_cast<SwFootnoteBossFrame*>(GetUpper());
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSect = FindSctFrame();
+ OSL_ENSURE( pSect, "GrowFrame: Missing SectFrame" );
+ // In a section, which has to maximize, a footnotecontainer is allowed
+ // to grow, when the section can't grow anymore.
+ if( !bTst && !pSect->IsColLocked() &&
+ pSect->ToMaximize( false ) && pSect->Growable() )
+ {
+ pSect->InvalidateSize();
+ return 0;
+ }
+ }
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwPageFrame *pPage = pBoss->FindPageFrame();
+ if ( bBrowseMode || !pPage->IsFootnotePage() )
+ {
+ if ( pBoss->GetMaxFootnoteHeight() != LONG_MAX )
+ {
+ nDist = std::min( nDist,
+ SwTwips(pBoss->GetMaxFootnoteHeight() - aRectFnSet.GetHeight(getFrameArea())) );
+ if ( nDist <= 0 )
+ return 0;
+ }
+ // FootnoteBoss also influences the max value
+ if( !IsInSct() )
+ {
+ const SwTwips nMax = pBoss->GetVarSpace();
+ if ( nDist > nMax )
+ nDist = nMax;
+ if ( nDist <= 0 )
+ return 0;
+ }
+ }
+ else if( nDist > aRectFnSet.GetHeight(GetPrev()->getFrameArea()) )
+ // do not use more space than the body has
+ nDist = aRectFnSet.GetHeight(GetPrev()->getFrameArea());
+
+ tools::Long nAvail = 0;
+ if ( bBrowseMode )
+ {
+ nAvail = GetUpper()->getFramePrintArea().Height();
+ const SwFrame *pAvail = GetUpper()->Lower();
+ do
+ { nAvail -= pAvail->getFrameArea().Height();
+ pAvail = pAvail->GetNext();
+ } while ( pAvail );
+ if ( nAvail > nDist )
+ nAvail = nDist;
+ }
+
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+ }
+ tools::Long nGrow = nDist - nAvail,
+ nReal = 0;
+ if ( nGrow > 0 )
+ {
+ SwNeighbourAdjust nAdjust = pBoss->NeighbourhoodAdjustment();
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ nReal = AdjustNeighbourhood( nGrow, bTst );
+ else
+ {
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust )
+ {
+ SwFrame* pFootnote = Lower();
+ if( pFootnote )
+ {
+ while( pFootnote->GetNext() )
+ pFootnote = pFootnote->GetNext();
+ if( static_cast<SwFootnoteFrame*>(pFootnote)->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ nReal = AdjustNeighbourhood( nGrow, bTst );
+ nAdjust = SwNeighbourAdjust::GrowShrink; // no more AdjustNeighbourhood
+ }
+ }
+ }
+ nReal += pBoss->Grow( nGrow - nReal, bTst );
+ if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || SwNeighbourAdjust::AdjustGrow == nAdjust )
+ && nReal < nGrow )
+ nReal += AdjustNeighbourhood( nGrow - nReal, bTst );
+ }
+ }
+
+ nReal += nAvail;
+
+ if ( !bTst )
+ {
+ if ( nReal != nDist )
+ {
+ nDist -= nReal;
+
+ // We can only respect the boundless wish so much
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.AddHeight( -nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nDist );
+ }
+ }
+
+ // growing happens upwards, so successors to not need to be invalidated
+ if( nReal )
+ {
+ InvalidateSize_();
+ InvalidatePos_();
+ InvalidatePage( pPage );
+ }
+ }
+ return nReal;
+}
+
+SwTwips SwFootnoteContFrame::ShrinkFrame( SwTwips nDiff, bool bTst, bool bInfo )
+{
+ SwPageFrame *pPage = FindPageFrame();
+ bool bShrink = false;
+ if ( pPage )
+ {
+ if( !pPage->IsFootnotePage() )
+ bShrink = true;
+ else
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ bShrink = true;
+ }
+ }
+ if( bShrink )
+ {
+ SwTwips nRet = SwLayoutFrame::ShrinkFrame( nDiff, bTst, bInfo );
+ if( IsInSct() && !bTst )
+ FindSctFrame()->InvalidateNextPos();
+ if ( !bTst && nRet )
+ {
+ InvalidatePos_();
+ InvalidatePage( pPage );
+ }
+ return nRet;
+ }
+ return 0;
+}
+
+SwFootnoteFrame::SwFootnoteFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwContentFrame *pCnt, SwTextFootnote *pAt ):
+ SwLayoutFrame( pFormat, pSib ),
+ mpFollow( nullptr ),
+ mpMaster( nullptr ),
+ mpReference( pCnt ),
+ mpAttribute( pAt ),
+ mbBackMoveLocked( false ),
+ // #i49383#
+ mbUnlockPosOfLowerObjs( true )
+{
+ mnFrameType = SwFrameType::Ftn;
+}
+
+void SwFootnoteFrame::InvalidateNxtFootnoteCnts( SwPageFrame const *pPage )
+{
+ if ( !GetNext() )
+ return;
+
+ SwFrame *pCnt = static_cast<SwLayoutFrame*>(GetNext())->ContainsAny();
+ if( !pCnt )
+ return;
+
+ pCnt->InvalidatePage( pPage );
+ pCnt->InvalidatePrt_();
+ do
+ { pCnt->InvalidatePos_();
+ if( pCnt->IsSctFrame() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePos_();
+ }
+ pCnt->GetUpper()->InvalidateSize_();
+ pCnt = pCnt->FindNext();
+ } while ( pCnt && GetUpper()->IsAnLower( pCnt ) );
+}
+
+bool SwFootnoteFrame::IsDeleteForbidden() const
+{
+ if (SwLayoutFrame::IsDeleteForbidden())
+ return true;
+ // needs to be in sync with the ::Cut logic
+ const SwLayoutFrame *pUp = GetUpper();
+ if (pUp)
+ {
+ if (GetPrev())
+ return false;
+
+ // The last footnote takes its container along if it
+ // is deleted. Cut would put pUp->Lower() to the value
+ // of GetNext(), so if there is no GetNext then
+ // Cut would delete pUp. If that condition is true
+ // here then check if the container is delete-forbidden
+ return !GetNext() && pUp->IsDeleteForbidden();
+ }
+ return false;
+}
+
+void SwFootnoteFrame::Cut()
+{
+ if ( GetNext() )
+ GetNext()->InvalidatePos();
+ else if ( GetPrev() )
+ GetPrev()->SetRetouche();
+
+ // first move then shrink Upper
+ SwLayoutFrame *pUp = GetUpper();
+
+ // correct chaining
+ SwFootnoteFrame *pFootnote = this;
+ if ( pFootnote->GetFollow() )
+ pFootnote->GetFollow()->SetMaster( pFootnote->GetMaster() );
+ if ( pFootnote->GetMaster() )
+ pFootnote->GetMaster()->SetFollow( pFootnote->GetFollow() );
+ pFootnote->SetFollow( nullptr );
+ pFootnote->SetMaster( nullptr );
+
+ // cut all connections
+ RemoveFromLayout();
+
+ if ( !pUp )
+ return;
+
+ // The last footnote takes its container along
+ if (!pUp->Lower())
+ {
+ SwPageFrame *pPage = pUp->FindPageFrame();
+ if ( pPage )
+ {
+ SwLayoutFrame *pBody = pPage->FindBodyCont();
+ if( pBody && !pBody->ContainsContent() )
+ pPage->getRootFrame()->SetSuperfluous();
+ }
+ SwSectionFrame* pSect = pUp->FindSctFrame();
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ // If the last footnote container was removed from a column
+ // section without a Follow, then this section can be shrunk.
+ if( pSect && !pSect->ToMaximize( false ) && !pSect->IsColLocked() )
+ pSect->InvalidateSize_();
+ }
+ else
+ { if ( getFrameArea().Height() )
+ pUp->Shrink( getFrameArea().Height() );
+ pUp->SetCompletePaint();
+ pUp->InvalidatePage();
+ }
+}
+
+void SwFootnoteFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "no parent in Paste." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I am my own parent." );
+ OSL_ENSURE( pSibling != this, "I am my own sibling." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I am still somewhere registered." );
+
+ // insert into tree structure
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.GetWidth(getFrameArea())!=aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
+ InvalidateSize_();
+ InvalidatePos_();
+ if (SwFrame *const pContent = ContainsContent())
+ { // tdf#139687 invalidate possibly stale top margin (computed from previous frame)
+ pContent->InvalidatePrt_();
+ }
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ if (SwFootnoteFrame *const pNext = static_cast<SwFootnoteFrame *>(GetNext()))
+ {
+ pNext->InvalidatePos_();
+ if (SwFrame *const pContent = pNext->ContainsContent())
+ { // tdf#139687 invalidate possibly stale top margin (computed from previous frame)
+ pContent->InvalidatePrt_();
+ }
+ }
+ if( aRectFnSet.GetHeight(getFrameArea()) )
+ pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
+
+ // If the predecessor is the master and/or the successor is the Follow,
+ // then take their content and destroy them.
+ if ( GetPrev() && GetPrev() == GetMaster() )
+ {
+ OSL_ENSURE( SwFlowFrame::CastFlowFrame( GetPrev()->GetLower() ),
+ "Footnote without content?" );
+ SwFlowFrame::CastFlowFrame( GetPrev()->GetLower())->
+ MoveSubTree( this, GetLower() );
+ SwFrame *pDel = GetPrev();
+ assert(pDel != this);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ if ( GetNext() && GetNext() == GetFollow() )
+ {
+ OSL_ENSURE( SwFlowFrame::CastFlowFrame( GetNext()->GetLower() ),
+ "Footnote without content?" );
+ SwFlowFrame::CastFlowFrame( GetNext()->GetLower() )->MoveSubTree( this );
+ SwFrame *pDel = GetNext();
+ assert(pDel != this);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ if ( GetPrev() )
+ {
+ OSL_ENSURE( lcl_FindFootnotePos( pDoc, static_cast<SwFootnoteFrame*>(GetPrev())->GetAttr() ) <=
+ lcl_FindFootnotePos( pDoc, GetAttr() ), "Prev is not FootnotePrev" );
+ }
+ if ( GetNext() )
+ {
+ OSL_ENSURE( lcl_FindFootnotePos( pDoc, GetAttr() ) <=
+ lcl_FindFootnotePos( pDoc, static_cast<SwFootnoteFrame*>(GetNext())->GetAttr() ),
+ "Next is not FootnoteNext" );
+ }
+#endif
+ InvalidateNxtFootnoteCnts( pPage );
+}
+
+/// Return the next layout leaf in that the frame can be moved.
+/// New pages will only be created if specified by the parameter.
+SwLayoutFrame *SwFrame::GetNextFootnoteLeaf( MakePageType eMakePage )
+{
+ SwFootnoteBossFrame *pOldBoss = FindFootnoteBossFrame();
+ SwPageFrame* pOldPage = pOldBoss->FindPageFrame();
+ SwPageFrame* pPage;
+ SwFootnoteBossFrame *pBoss = pOldBoss->IsColumnFrame() ?
+ static_cast<SwFootnoteBossFrame*>(pOldBoss->GetNext()) : nullptr; // next column, if existing
+ if( pBoss )
+ pPage = nullptr;
+ else
+ {
+ if( pOldBoss->GetUpper()->IsSctFrame() )
+ { // this can only be in a column area
+ SwLayoutFrame* pNxt = pOldBoss->GetNextSctLeaf( eMakePage );
+ if( pNxt )
+ {
+ OSL_ENSURE( pNxt->IsColBodyFrame(), "GetNextFootnoteLeaf: Funny Leaf" );
+ pBoss = static_cast<SwFootnoteBossFrame*>(pNxt->GetUpper());
+ pPage = pBoss->FindPageFrame();
+ }
+ else
+ return nullptr;
+ }
+ else
+ {
+ // next page
+ pPage = static_cast<SwPageFrame*>(pOldPage->GetNext());
+ // skip empty pages
+ if( pPage && pPage->IsEmptyPage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pBoss = pPage;
+ }
+ }
+ // What do we have until here?
+ // pBoss != NULL, pPage==NULL => pBoss is the next column on the same page
+ // pBoss != NULL, pPage!=NULL => pBoss and pPage are the following page (empty pages skipped)
+ // pBoss == NULL => pPage == NULL, so there are no following pages
+
+ // If the footnote has already a Follow we do not need to search.
+ // However, if there are unwanted empty columns/pages between Footnote and Follow,
+ // create another Follow on the next best column/page and the rest will sort itself out.
+ SwFootnoteFrame *pFootnote = FindFootnoteFrame();
+ if ( pFootnote && pFootnote->GetFollow() )
+ {
+ SwFootnoteBossFrame* pTmpBoss = pFootnote->GetFollow()->FindFootnoteBossFrame();
+ // Following cases will be handled:
+ // 1. both "FootnoteBoss"es are neighboring columns/pages
+ // 2. the new one is the first column of a neighboring page
+ // 3. the new one is the first column in a section of the next page
+ while( pTmpBoss != pBoss && pTmpBoss && !pTmpBoss->GetPrev() )
+ pTmpBoss = pTmpBoss->GetUpper()->FindFootnoteBossFrame();
+ if( pTmpBoss == pBoss )
+ return pFootnote->GetFollow();
+ }
+
+ // If no pBoss could be found or it is a "wrong" page, we need a new page.
+ if ( !pBoss || ( pPage && pPage->IsEndNotePage() && !pOldPage->IsEndNotePage() ) )
+ {
+ if ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT )
+ {
+ pBoss = InsertPage( pOldPage, pOldPage->IsFootnotePage() );
+ static_cast<SwPageFrame*>(pBoss)->SetEndNotePage( pOldPage->IsEndNotePage() );
+ }
+ else
+ return nullptr;
+ }
+ if( pBoss->IsPageFrame() )
+ {
+ // If this page has columns, then go to the first one
+ SwLayoutFrame* pLay = pBoss->FindBodyCont();
+ if( pLay && pLay->Lower() && pLay->Lower()->IsColumnFrame() )
+ pBoss = static_cast<SwFootnoteBossFrame*>(pLay->Lower());
+ }
+ // found column/page - add myself
+ SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
+ if ( !pCont && pBoss->GetMaxFootnoteHeight() &&
+ ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
+ pCont = pBoss->MakeFootnoteCont();
+ return pCont;
+}
+
+/// Get the preceding layout leaf in that the frame can be moved.
+SwLayoutFrame *SwFrame::GetPrevFootnoteLeaf( MakePageType eMakeFootnote )
+{
+ // The predecessor of a footnote is (if possible)
+ // the master of the chain of the footnote.
+ SwFootnoteFrame *pFootnote = FindFootnoteFrame();
+ SwLayoutFrame *pRet = pFootnote->GetMaster();
+
+ SwFootnoteBossFrame* pOldBoss = FindFootnoteBossFrame();
+ SwPageFrame *pOldPage = pOldBoss->FindPageFrame();
+
+ if ( !pOldBoss->GetPrev() && !pOldPage->GetPrev() )
+ return pRet; // there is neither a predecessor column nor page
+
+ if ( !pRet )
+ {
+ bool bEndn = pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ SwFrame* pTmpRef = nullptr;
+ const IDocumentSettingAccess& rSettings
+ = pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
+ if( bEndn && pFootnote->IsInSct() )
+ {
+ SwSectionFrame* pSect = pFootnote->FindSctFrame();
+ if( pSect->IsEndnAtEnd() )
+ // Endnotes at the end of the section.
+ pTmpRef = pSect->FindLastContent( SwFindMode::LastCnt );
+ }
+ else if (bEndn && rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
+ {
+ // Endnotes at the end of the document.
+ SwPageFrame* pPage = getRootFrame()->GetLastPage();
+ assert(pPage);
+ SwFrame* pPrevPage = pPage->GetPrev();
+ if (pPrevPage)
+ {
+ // Have a last but one page, use that since we try to get a preceding frame.
+ assert(pPrevPage->IsPageFrame());
+ pPage = static_cast<SwPageFrame*>(pPrevPage);
+ }
+ pTmpRef = pPage->FindLastBodyContent();
+ }
+ if( !pTmpRef )
+ // Endnotes on a separate page.
+ pTmpRef = pFootnote->GetRef();
+ SwFootnoteBossFrame* pStop = pTmpRef->FindFootnoteBossFrame( !bEndn );
+
+ const sal_uInt16 nNum = pStop->GetPhyPageNum();
+
+ // Do not leave the corresponding page if the footnote should
+ // be shown at the document ending or the footnote is an endnote.
+ const bool bEndNote = pOldPage->IsEndNotePage();
+ const bool bFootnoteEndDoc = pOldPage->IsFootnotePage();
+ SwFootnoteBossFrame* pNxtBoss = pOldBoss;
+ SwSectionFrame *pSect = pNxtBoss->GetUpper()->IsSctFrame() ?
+ static_cast<SwSectionFrame*>(pNxtBoss->GetUpper()) : nullptr;
+
+ do
+ {
+ if( pNxtBoss->IsColumnFrame() && pNxtBoss->GetPrev() )
+ pNxtBoss = static_cast<SwFootnoteBossFrame*>(pNxtBoss->GetPrev()); // one column backwards
+ else // one page backwards
+ {
+ SwLayoutFrame* pBody = nullptr;
+ if( pSect )
+ {
+ if( pSect->IsFootnoteLock() )
+ {
+ if( pNxtBoss == pOldBoss )
+ return nullptr;
+ pStop = pNxtBoss;
+ }
+ else
+ {
+ pSect = pSect->FindMaster();
+ if( !pSect || !pSect->Lower() )
+ return nullptr;
+ OSL_ENSURE( pSect->Lower()->IsColumnFrame(),
+ "GetPrevFootnoteLeaf: Where's the column?" );
+ pNxtBoss = static_cast<SwFootnoteBossFrame*>(pSect->Lower());
+ pBody = pSect;
+ }
+ }
+ else
+ {
+ SwPageFrame* pPage = static_cast<SwPageFrame*>(pNxtBoss->FindPageFrame()->GetPrev());
+ if( !pPage || pPage->GetPhyPageNum() < nNum ||
+ bEndNote != pPage->IsEndNotePage() || bFootnoteEndDoc != pPage->IsFootnotePage() )
+ return nullptr; // no further pages found
+ pNxtBoss = pPage;
+ pBody = pPage->FindBodyCont();
+ }
+ // We have the previous page, we might need to find the last column of it
+ if( pBody )
+ {
+ if ( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ pNxtBoss = static_cast<SwFootnoteBossFrame*>(pBody->GetLastLower());
+ }
+ }
+ }
+ SwFootnoteContFrame *pCont = pNxtBoss->FindFootnoteCont();
+ if ( pCont )
+ {
+ pRet = pCont;
+ break;
+ }
+ if ( pStop == pNxtBoss )
+ {
+ // Reached the column/page of the reference.
+ // Try to add a container and paste our content.
+ if ( eMakeFootnote == MAKEPAGE_FTN && pNxtBoss->GetMaxFootnoteHeight() )
+ pRet = pNxtBoss->MakeFootnoteCont();
+ break;
+ }
+ } while( !pRet );
+ }
+ if ( pRet )
+ {
+ const SwFootnoteBossFrame* pNewBoss = pRet->FindFootnoteBossFrame();
+ bool bJump = false;
+ if( pOldBoss->IsColumnFrame() && pOldBoss->GetPrev() ) // a previous column exists
+ bJump = pOldBoss->GetPrev() != static_cast<SwFrame const *>(pNewBoss); // did we chose it?
+ else if( pNewBoss->IsColumnFrame() && pNewBoss->GetNext() )
+ bJump = true; // there is another column after the boss (not the old boss)
+ else
+ {
+ // Will be reached only if old and new boss are both either pages or the last (new)
+ // or first (old) column of a page. In this case, check if pages were skipped.
+ const sal_uInt16 nDiff = pOldPage->GetPhyPageNum() - pRet->FindPageFrame()->GetPhyPageNum();
+ if ( nDiff > 2 ||
+ (nDiff > 1 && !static_cast<SwPageFrame*>(pOldPage->GetPrev())->IsEmptyPage()) )
+ bJump = true;
+ }
+ if( bJump )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ return pRet;
+}
+
+bool SwFrame::IsFootnoteAllowed() const
+{
+ if ( !IsInDocBody() )
+ return false;
+
+ if ( IsInTab() )
+ {
+ // no footnotes in repeated headlines
+ const SwTabFrame *pTab = const_cast<SwFrame*>(this)->ImplFindTabFrame();
+ assert(pTab);
+ if ( pTab->IsFollow() )
+ return !pTab->IsInHeadline( *this );
+ }
+ return true;
+}
+
+void SwRootFrame::UpdateFootnoteNums()
+{
+ // page numbering only if set at the document
+ if ( GetFormat()->GetDoc()->GetFootnoteInfo().m_eNum == FTNNUM_PAGE )
+ {
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && !pPage->IsFootnotePage() )
+ {
+ pPage->UpdateFootnoteNum();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ }
+}
+
+/// remove all footnotes (not the references) and all footnote pages
+void sw_RemoveFootnotes( SwFootnoteBossFrame* pBoss, bool bPageOnly, bool bEndNotes )
+{
+ do
+ {
+ SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
+ if ( pCont )
+ {
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ assert(pFootnote);
+ if ( bPageOnly )
+ while ( pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ do
+ {
+ SwFootnoteFrame *pNxt = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
+ if ( !pFootnote->GetAttr()->GetFootnote().IsEndNote() ||
+ bEndNotes )
+ {
+ pFootnote->GetRef()->Prepare( PrepareHint::FootnoteInvalidation, static_cast<void*>(pFootnote->GetAttr()) );
+ if ( bPageOnly && !pNxt )
+ pNxt = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ }
+ pFootnote = pNxt;
+
+ } while ( pFootnote );
+ }
+ if( !pBoss->IsInSct() )
+ {
+ // A sectionframe with the Footnote/EndnAtEnd-flags may contain
+ // foot/endnotes. If the last lower frame of the bodyframe is
+ // a multicolumned sectionframe, it may contain footnotes, too.
+ SwLayoutFrame* pBody = pBoss->FindBodyCont();
+ if( pBody && pBody->Lower() )
+ {
+ SwFrame* pLow = pBody->Lower();
+ while (pLow)
+ {
+ if( pLow->IsSctFrame() && ( !pLow->GetNext() ||
+ static_cast<SwSectionFrame*>(pLow)->IsAnyNoteAtEnd() ) &&
+ static_cast<SwSectionFrame*>(pLow)->Lower() &&
+ static_cast<SwSectionFrame*>(pLow)->Lower()->IsColumnFrame() )
+ sw_RemoveFootnotes( static_cast<SwColumnFrame*>(static_cast<SwSectionFrame*>(pLow)->Lower()),
+ bPageOnly, bEndNotes );
+ pLow = pLow->GetNext();
+ }
+ }
+ }
+ // is there another column?
+ pBoss = pBoss->IsColumnFrame() ? static_cast<SwColumnFrame*>(pBoss->GetNext()) : nullptr;
+ } while( pBoss );
+}
+
+void SwRootFrame::RemoveFootnotes( SwPageFrame *pPage, bool bPageOnly, bool bEndNotes )
+{
+ if ( !pPage )
+ pPage = static_cast<SwPageFrame*>(Lower());
+
+ do
+ { // On columned pages we have to clean up in all columns
+ SwFootnoteBossFrame* pBoss;
+ SwLayoutFrame* pBody = pPage->FindBodyCont();
+ if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ pBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower()); // the first column
+ else
+ pBoss = pPage; // no columns
+ sw_RemoveFootnotes( pBoss, bPageOnly, bEndNotes );
+ if ( !bPageOnly )
+ {
+ if ( pPage->IsFootnotePage() &&
+ (!pPage->IsEndNotePage() || bEndNotes) )
+ {
+ SwFrame *pDel = pPage;
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ else
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ else
+ break;
+
+ } while ( pPage );
+}
+
+/// Change the page template of the footnote pages
+void SwRootFrame::CheckFootnotePageDescs( bool bEndNote )
+{
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && !pPage->IsFootnotePage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ while ( pPage && pPage->IsEndNotePage() != bEndNote )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ if ( pPage )
+ SwFrame::CheckPageDescs( pPage, false );
+}
+
+/** Insert a footnote container
+ *
+ * A footnote container is always placed directly behind the body text.
+ *
+ * The frame format (FrameFormat) is always the default frame format.
+ *
+ * @return footnote container frame
+ */
+SwFootnoteContFrame *SwFootnoteBossFrame::MakeFootnoteCont()
+{
+ SAL_WARN_IF(FindFootnoteCont(), "sw.core", "footnote container exists already");
+
+ SwFootnoteContFrame *pNew = new SwFootnoteContFrame( GetFormat()->GetDoc()->GetDfltFrameFormat(), this );
+ SwLayoutFrame *pLay = FindBodyCont();
+ pNew->Paste( this, pLay->GetNext() );
+ return pNew;
+}
+
+SwFootnoteContFrame *SwFootnoteBossFrame::FindFootnoteCont()
+{
+ SwFrame *pFrame = Lower();
+ while( pFrame && !pFrame->IsFootnoteContFrame() )
+ pFrame = pFrame->GetNext();
+
+#if OSL_DEBUG_LEVEL > 0
+ if ( pFrame )
+ {
+ SwFrame *pFootnote = pFrame->GetLower();
+ assert(pFootnote);
+ while ( pFootnote )
+ {
+ assert(pFootnote->IsFootnoteFrame() && "Neighbor of footnote must be a footnote");
+ pFootnote = pFootnote->GetNext();
+ }
+ }
+#endif
+
+ return static_cast<SwFootnoteContFrame*>(pFrame);
+}
+
+/// Search the next available footnote container.
+SwFootnoteContFrame *SwFootnoteBossFrame::FindNearestFootnoteCont( bool bDontLeave )
+{
+ SwFootnoteContFrame *pCont = nullptr;
+ if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ {
+ pCont = FindFootnoteCont();
+ if ( !pCont )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ SwFootnoteBossFrame* pBoss = this;
+ bool bEndNote = pPage->IsEndNotePage();
+ do
+ {
+ bool bChgPage = lcl_NextFootnoteBoss( pBoss, pPage, bDontLeave );
+ // Found another boss? When changing pages, also the endnote flag must match.
+ if( pBoss && ( !bChgPage || pPage->IsEndNotePage() == bEndNote ) )
+ pCont = pBoss->FindFootnoteCont();
+ } while ( !pCont && pPage );
+ }
+ }
+ return pCont;
+}
+
+SwFootnoteFrame *SwFootnoteBossFrame::FindFirstFootnote()
+{
+ // search for the nearest footnote container
+ SwFootnoteContFrame *pCont = FindNearestFootnoteCont();
+ if ( !pCont )
+ return nullptr;
+
+ // Starting from the first footnote, search the first
+ // footnote that is referenced by the current column/page
+
+ SwFootnoteFrame *pRet = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ const sal_uInt16 nRefNum = FindPageFrame()->GetPhyPageNum();
+ const sal_uInt16 nRefCol = lcl_ColumnNum( this );
+ sal_uInt16 nPgNum, nColNum; // page number, column number
+ SwFootnoteBossFrame* pBoss;
+ SwPageFrame* pPage;
+ if( pRet )
+ {
+ pBoss = pRet->GetRef()->FindFootnoteBossFrame();
+ OSL_ENSURE( pBoss, "FindFirstFootnote: No boss found" );
+ if( !pBoss )
+ return nullptr; // ?There must be a bug, but no GPF
+ pPage = pBoss->FindPageFrame();
+ nPgNum = pPage->GetPhyPageNum();
+ if ( nPgNum == nRefNum )
+ {
+ nColNum = lcl_ColumnNum( pBoss );
+ if( nColNum == nRefCol )
+ return pRet; // found
+ else if( nColNum > nRefCol )
+ return nullptr; // at least one column too far
+ }
+ else if ( nPgNum > nRefNum )
+ return nullptr; // at least one column too far
+ }
+ else
+ return nullptr;
+ // Done if Ref is on a subsequent page or on the same page in a subsequent column
+
+ do
+ {
+ while ( pRet->GetFollow() )
+ pRet = pRet->GetFollow();
+
+ SwFootnoteFrame *pNxt = static_cast<SwFootnoteFrame*>(pRet->GetNext());
+ if ( !pNxt )
+ {
+ pBoss = pRet->FindFootnoteBossFrame();
+ pPage = pBoss->FindPageFrame();
+ lcl_NextFootnoteBoss( pBoss, pPage, false ); // next FootnoteBoss
+ pCont = pBoss ? pBoss->FindNearestFootnoteCont() : nullptr;
+ if ( pCont )
+ pNxt = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ }
+ if ( pNxt )
+ {
+ pRet = pNxt;
+ pBoss = pRet->GetRef()->FindFootnoteBossFrame();
+ pPage = pBoss->FindPageFrame();
+ nPgNum = pPage->GetPhyPageNum();
+ if ( nPgNum == nRefNum )
+ {
+ nColNum = lcl_ColumnNum( pBoss );
+ if( nColNum == nRefCol )
+ break; // found
+ else if( nColNum > nRefCol )
+ pRet = nullptr; // at least one column too far
+ }
+ else if ( nPgNum > nRefNum )
+ pRet = nullptr; // at least a page too far
+ }
+ else
+ pRet = nullptr; // there is none
+ } while( pRet );
+ return pRet;
+}
+
+/// Get the first footnote of a given content
+const SwFootnoteFrame *SwFootnoteBossFrame::FindFirstFootnote( SwContentFrame const *pCnt ) const
+{
+ const SwFootnoteFrame *pRet = const_cast<SwFootnoteBossFrame*>(this)->FindFirstFootnote();
+ if ( pRet )
+ {
+ const sal_uInt16 nColNum = lcl_ColumnNum( this );
+ const sal_uInt16 nPageNum = GetPhyPageNum();
+ while ( pRet && (pRet->GetRef() != pCnt) )
+ {
+ while ( pRet->GetFollow() )
+ pRet = pRet->GetFollow();
+
+ if ( pRet->GetNext() )
+ pRet = static_cast<const SwFootnoteFrame*>(pRet->GetNext());
+ else
+ { SwFootnoteBossFrame *pBoss = const_cast<SwFootnoteBossFrame*>(pRet->FindFootnoteBossFrame());
+ SwPageFrame *pPage = pBoss->FindPageFrame();
+ lcl_NextFootnoteBoss( pBoss, pPage, false ); // next FootnoteBoss
+ SwFootnoteContFrame *pCont = pBoss ? pBoss->FindNearestFootnoteCont() : nullptr;
+ pRet = pCont ? static_cast<SwFootnoteFrame*>(pCont->Lower()) : nullptr;
+ }
+ if ( pRet )
+ {
+ const SwFootnoteBossFrame* pBoss = pRet->GetRef()->FindFootnoteBossFrame();
+ if( pBoss->GetPhyPageNum() != nPageNum ||
+ nColNum != lcl_ColumnNum( pBoss ) )
+ pRet = nullptr;
+ }
+ }
+ }
+ return pRet;
+}
+
+void SwFootnoteBossFrame::ResetFootnote( const SwFootnoteFrame *pCheck )
+{
+ // Destroy the incarnations of footnotes to an attribute, if they don't
+ // belong to pAssumed
+ OSL_ENSURE( !pCheck->GetMaster(), "given master is not a Master." );
+
+ SwNodeIndex aIdx( *pCheck->GetAttr()->GetStartNode(), 1 );
+ SwContentNode *pNd = aIdx.GetNode().GetContentNode();
+ if ( !pNd )
+ pNd = pCheck->GetFormat()->GetDoc()->
+ GetNodes().GoNextSection( &aIdx, true, false );
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
+ SwFrame* pFrame = aIter.First();
+ while( pFrame )
+ {
+ if( pFrame->getRootFrame() == pCheck->getRootFrame() )
+ {
+ SwFrame *pTmp = pFrame->GetUpper();
+ while ( pTmp && !pTmp->IsFootnoteFrame() )
+ pTmp = pTmp->GetUpper();
+
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pTmp);
+ while ( pFootnote && pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ if ( pFootnote != pCheck )
+ {
+ while (pFootnote && !pFootnote->IsDeleteForbidden())
+ {
+ SwFootnoteFrame *pNxt = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ pFootnote = pNxt;
+ }
+ }
+ }
+
+ pFrame = aIter.Next();
+ }
+}
+
+void SwFootnoteBossFrame::InsertFootnote( SwFootnoteFrame* pNew )
+{
+ // Place the footnote in front of the footnote whose attribute
+ // is in front of the new one (get position via the Doc).
+ // If there is no footnote in this footnote-boss yet, create a new container.
+ // If there is a container but no footnote for this footnote-boss yet, place
+ // the footnote behind the last footnote of the closest previous column/page.
+
+ ResetFootnote( pNew );
+ SwFootnoteFrame *pSibling = FindFirstFootnote();
+ bool bDontLeave = false;
+
+ // Ok, a sibling has been found, but is the sibling in an acceptable
+ // environment?
+ if( IsInSct() )
+ {
+ SwSectionFrame* pMySect = ImplFindSctFrame();
+ bool bEndnt = pNew->GetAttr()->GetFootnote().IsEndNote();
+ if( bEndnt )
+ {
+ const SwSectionFormat* pEndFormat = pMySect->GetEndSectFormat();
+ bDontLeave = nullptr != pEndFormat;
+ if( pSibling )
+ {
+ if( pEndFormat )
+ {
+ if( !pSibling->IsInSct() ||
+ !pSibling->ImplFindSctFrame()->IsDescendantFrom( pEndFormat ) )
+ pSibling = nullptr;
+ }
+ else if( pSibling->IsInSct() )
+ pSibling = nullptr;
+ }
+ }
+ else
+ {
+ bDontLeave = pMySect->IsFootnoteAtEnd();
+ if( pSibling )
+ {
+ if( pMySect->IsFootnoteAtEnd() )
+ {
+ if( !pSibling->IsInSct() ||
+ !pMySect->IsAnFollow( pSibling->ImplFindSctFrame() ) )
+ pSibling = nullptr;
+ }
+ else if( pSibling->IsInSct() )
+ pSibling = nullptr;
+ }
+ }
+ }
+
+ if( pSibling && pSibling->FindPageFrame()->IsEndNotePage() !=
+ FindPageFrame()->IsEndNotePage() )
+ pSibling = nullptr;
+
+ // use the Doc to find out the position
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ const sal_uLong nStPos = ::lcl_FindFootnotePos( pDoc, pNew->GetAttr() );
+
+ sal_uLong nCmpPos = 0;
+ sal_uLong nLastPos = 0;
+ SwFootnoteContFrame *pParent = nullptr;
+ if( pSibling )
+ {
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ if( nCmpPos > nStPos )
+ pSibling = nullptr;
+ }
+
+ if ( !pSibling )
+ { pParent = FindFootnoteCont();
+ if ( !pParent )
+ {
+ // There is no footnote container yet. Before creating one, keep in mind that
+ // there might exist another following footnote that must be placed before the
+ // new inserted one e.g. because it was divided over multiple pages etc.
+ pParent = FindNearestFootnoteCont( bDontLeave );
+ if ( pParent )
+ {
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pParent->Lower());
+ if ( pFootnote )
+ {
+
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pFootnote->GetAttr() );
+ if ( nCmpPos > nStPos )
+ pParent = nullptr;
+ }
+ else
+ pParent = nullptr;
+ }
+ }
+ if ( !pParent )
+ // here, we are sure that we can create a footnote container
+ pParent = MakeFootnoteCont();
+ else
+ {
+ // Based on the first footnote below the Parent, search for the first footnote whose
+ // index is after the index of the newly inserted, to place the new one correctly
+ pSibling = static_cast<SwFootnoteFrame*>(pParent->Lower());
+ if ( !pSibling )
+ {
+ OSL_ENSURE( false, "Could not find space for footnote.");
+ return;
+ }
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+
+ SwFootnoteBossFrame *pNxtB; // remember the last one to not
+ SwFootnoteFrame *pLastSib = nullptr; // go too far.
+
+ while ( pSibling && nCmpPos <= nStPos )
+ {
+ pLastSib = pSibling; // potential candidate
+ nLastPos = nCmpPos;
+
+ while ( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+
+ if ( pSibling->GetNext() )
+ {
+ pSibling = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
+ OSL_ENSURE( !pSibling->GetMaster() || ( ENDNOTE > nStPos &&
+ pSibling->GetAttr()->GetFootnote().IsEndNote() ),
+ "InsertFootnote: Master expected I" );
+ }
+ else
+ {
+ pNxtB = pSibling->FindFootnoteBossFrame();
+ SwPageFrame *pSibPage = pNxtB->FindPageFrame();
+ bool bEndNote = pSibPage->IsEndNotePage();
+ bool bChgPage = lcl_NextFootnoteBoss( pNxtB, pSibPage, bDontLeave );
+ // When changing pages, also the endnote flag must match.
+ SwFootnoteContFrame *pCont = pNxtB && ( !bChgPage ||
+ pSibPage->IsEndNotePage() == bEndNote )
+ ? pNxtB->FindNearestFootnoteCont( bDontLeave ) : nullptr;
+ if( pCont )
+ pSibling = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ else // no further FootnoteContainer, insert after pSibling
+ break;
+ }
+ if ( pSibling )
+ {
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ OSL_ENSURE( nCmpPos > nLastPos, "InsertFootnote: Order of FootnoteFrame's buggy" );
+ }
+ }
+ // pLastSib is the last footnote before the new one and
+ // pSibling is empty or the first one after the new one
+ if ( pSibling && pLastSib && (pSibling != pLastSib) )
+ {
+ // too far?
+ if ( nCmpPos > nStPos )
+ pSibling = pLastSib;
+ }
+ else if ( !pSibling )
+ {
+ // Last chance: Take the last footnote of the parent.
+ // Special case that happens e.g. when moving paragraphs with multiple footnotes.
+ // To keep the order, use the parent of the last inspected footnote.
+ pSibling = pLastSib;
+ while( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+ OSL_ENSURE( !pSibling->GetNext(), "InsertFootnote: Who's that guy?" );
+ }
+ }
+ }
+ else
+ {
+ // First footnote of the column/page found. Now search from there for the first one on the
+ // same column/page whose index is after the given one. The last one found is the predecessor.
+ SwFootnoteBossFrame* pBoss = pNew->GetRef()->FindFootnoteBossFrame(
+ !pNew->GetAttr()->GetFootnote().IsEndNote() );
+ sal_uInt16 nRefNum = pBoss->GetPhyPageNum(); // page number of the new footnote
+ sal_uInt16 nRefCol = lcl_ColumnNum( pBoss ); // column number of the new footnote
+ bool bEnd = false;
+ SwFootnoteFrame *pLastSib = nullptr;
+ while ( pSibling && !bEnd && (nCmpPos <= nStPos) )
+ {
+ pLastSib = pSibling;
+ nLastPos = nCmpPos;
+
+ while ( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+
+ SwFootnoteFrame *pFoll = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
+ if ( pFoll )
+ {
+ pBoss = pSibling->GetRef()->FindFootnoteBossFrame( !pSibling->
+ GetAttr()->GetFootnote().IsEndNote() );
+ sal_uInt16 nTmpRef;
+ if( nStPos >= ENDNOTE ||
+ (nTmpRef = pBoss->GetPhyPageNum()) < nRefNum ||
+ ( nTmpRef == nRefNum && lcl_ColumnNum( pBoss ) <= nRefCol ))
+ pSibling = pFoll;
+ else
+ bEnd = true;
+ }
+ else
+ {
+ SwFootnoteBossFrame* pNxtB = pSibling->FindFootnoteBossFrame();
+ SwPageFrame *pSibPage = pNxtB->FindPageFrame();
+ bool bEndNote = pSibPage->IsEndNotePage();
+ bool bChgPage = lcl_NextFootnoteBoss( pNxtB, pSibPage, bDontLeave );
+ // When changing pages, also the endnote flag must match.
+ SwFootnoteContFrame *pCont = pNxtB && ( !bChgPage ||
+ pSibPage->IsEndNotePage() == bEndNote )
+ ? pNxtB->FindNearestFootnoteCont( bDontLeave ) : nullptr;
+ if ( pCont )
+ pSibling = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ else
+ bEnd = true;
+ }
+ if ( !bEnd && pSibling )
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ if (pSibling && (pSibling != pLastSib))
+ {
+ // too far?
+ if ( (nLastPos < nCmpPos) && (nCmpPos > nStPos) )
+ {
+ pSibling = pLastSib;
+ bEnd = true;
+ }
+ }
+ }
+ }
+ if ( pSibling )
+ {
+ nCmpPos = ::lcl_FindFootnotePos( pDoc, pSibling->GetAttr() );
+ if ( nCmpPos < nStPos )
+ {
+ while ( pSibling->GetFollow() )
+ pSibling = pSibling->GetFollow();
+ pParent = static_cast<SwFootnoteContFrame*>(pSibling->GetUpper());
+ pSibling = static_cast<SwFootnoteFrame*>(pSibling->GetNext());
+ }
+ else
+ {
+ if( pSibling->GetMaster() )
+ {
+ if( ENDNOTE > nCmpPos || nStPos >= ENDNOTE )
+ {
+ OSL_FAIL( "InsertFootnote: Master expected II" );
+ do
+ pSibling = pSibling->GetMaster();
+ while ( pSibling->GetMaster() );
+ }
+ }
+ pParent = static_cast<SwFootnoteContFrame*>(pSibling->GetUpper());
+ }
+ }
+ OSL_ENSURE( pParent, "paste in space?" );
+ pNew->Paste( pParent, pSibling );
+}
+
+static SwPageFrame* lcl_GetApproximateFootnotePage(const bool bEnd, const SwPageFrame* pPage,
+ const SwDoc *pDoc, const SwTextFootnote *pAttr)
+{
+ // We can at least search the approximately correct page
+ // to ensure that we will finish in finite time even if
+ // hundreds of footnotes exist.
+ const SwPageFrame *pNxt = static_cast<const SwPageFrame*>(pPage->GetNext());
+ const sal_uLong nStPos = ::lcl_FindFootnotePos(pDoc, pAttr);
+ while (pNxt && (bEnd ? pNxt->IsEndNotePage() : pNxt->IsFootnotePage() && !pNxt->IsEndNotePage()))
+ {
+ const SwFootnoteContFrame *pCont = pNxt->FindFootnoteCont();
+ if (pCont && pCont->Lower())
+ {
+ OSL_ENSURE( pCont->Lower()->IsFootnoteFrame(), "no footnote in the container" );
+ if (nStPos > ::lcl_FindFootnotePos(pDoc,
+ static_cast<const SwFootnoteFrame*>(pCont->Lower())->GetAttr()))
+ {
+ pPage = pNxt;
+ pNxt = static_cast<const SwPageFrame*>(pPage->GetNext());
+ continue;
+ }
+ }
+ break;
+ }
+ return const_cast<SwPageFrame*>(pPage);
+}
+
+void SwFootnoteBossFrame::AppendFootnote( SwContentFrame *pRef, SwTextFootnote *pAttr )
+{
+ // If the footnote already exists, do nothing.
+ if ( FindFootnote( pRef, pAttr ) )
+ return;
+
+ // If footnotes are inserted at the end of the document,
+ // we only need to search from the relevant page on.
+ // If there is none yet, we need to create one.
+ // If it is an Endnote, we need to search for or create an
+ // Endnote page.
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ SwFootnoteBossFrame *pBoss = this;
+ SwPageFrame *pPage = FindPageFrame();
+ SwPageFrame *pMyPage = pPage;
+ bool bChgPage = false;
+ const bool bEnd = pAttr->GetFootnote().IsEndNote();
+ if (bEnd)
+ {
+ const IDocumentSettingAccess& rSettings = *pAttr->GetTextNode().getIDocumentSettingAccess();
+ if( GetUpper()->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(GetUpper())->IsEndnAtEnd() )
+ {
+ // Endnotes at the end of the section.
+ SwFrame* pLast =
+ static_cast<SwSectionFrame*>(GetUpper())->FindLastContent( SwFindMode::EndNote );
+ if( pLast )
+ {
+ pBoss = pLast->FindFootnoteBossFrame();
+ pPage = pBoss->FindPageFrame();
+ }
+ }
+ else if (rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES))
+ {
+ // Endnotes at the end of the document.
+ pBoss = getRootFrame()->GetLastPage();
+ pPage = pBoss->FindPageFrame();
+ }
+ else
+ {
+ // Endnotes on a separate page.
+ while ( pPage->GetNext() && !pPage->IsEndNotePage() )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ bChgPage = true;
+ }
+ if ( !pPage->IsEndNotePage() )
+ {
+ SwPageDesc *pDesc = pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
+ pPage = ::InsertNewPage( *pDesc, pPage->GetUpper(),
+ !pPage->OnRightPage(), false, false, true, nullptr );
+ pPage->SetEndNotePage( true );
+ bChgPage = true;
+ }
+ else
+ pPage = lcl_GetApproximateFootnotePage(true, pPage, pDoc, pAttr);
+ }
+ }
+ else if( FTNPOS_CHAPTER == pDoc->GetFootnoteInfo().m_ePos && ( !GetUpper()->
+ IsSctFrame() || !static_cast<SwSectionFrame*>(GetUpper())->IsFootnoteAtEnd() ) )
+ {
+ while ( pPage->GetNext() && !pPage->IsFootnotePage() &&
+ !static_cast<SwPageFrame*>(pPage->GetNext())->IsEndNotePage() )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ bChgPage = true;
+ }
+
+ if ( !pPage->IsFootnotePage() )
+ {
+ SwPageDesc *pDesc = pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
+ pPage = ::InsertNewPage( *pDesc, pPage->GetUpper(),
+ !pPage->OnRightPage(), false, false, true, pPage->GetNext() );
+ bChgPage = true;
+ }
+ else
+ pPage = lcl_GetApproximateFootnotePage(false, pPage, pDoc, pAttr);
+ }
+
+ // For now, create a footnote and the corresponding content frames
+ if ( !pAttr->GetStartNode() )
+ {
+ OSL_ENSURE( false, "no footnote content." );
+ return;
+ }
+
+ // If there is already a footnote content on the column/page,
+ // another one cannot be created in a column area.
+ if( pBoss->IsInSct() && pBoss->IsColumnFrame() && !pPage->IsFootnotePage() )
+ {
+ SwSectionFrame* pSct = pBoss->FindSctFrame();
+ if( bEnd ? !pSct->IsEndnAtEnd() : !pSct->IsFootnoteAtEnd() )
+ {
+ SwFootnoteContFrame* pFootnoteCont = pSct->FindFootnoteBossFrame(!bEnd)->FindFootnoteCont();
+ if( pFootnoteCont )
+ {
+ SwFootnoteFrame* pTmp = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
+ if( bEnd )
+ while( pTmp && !pTmp->GetAttr()->GetFootnote().IsEndNote() )
+ pTmp = static_cast<SwFootnoteFrame*>(pTmp->GetNext());
+ if( pTmp && *pTmp < pAttr )
+ return;
+ }
+ }
+ }
+
+ SwFootnoteFrame *pNew = new SwFootnoteFrame( pDoc->GetDfltFrameFormat(), this, pRef, pAttr );
+ {
+ SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
+ ::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
+ }
+ // If the page was changed or newly created,
+ // we need to place ourselves in the first column
+ if( bChgPage )
+ {
+ SwLayoutFrame* pBody = pPage->FindBodyCont();
+ OSL_ENSURE( pBody, "AppendFootnote: NoPageBody?" );
+ if( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ pBoss = static_cast<SwFootnoteBossFrame*>(pBody->Lower());
+ else
+ pBoss = pPage; // page if no columns exist
+ }
+ pBoss->InsertFootnote( pNew );
+ if ( pNew->GetUpper() ) // inserted or not?
+ {
+ ::RegistFlys( pNew->FindPageFrame(), pNew );
+ SwSectionFrame* pSect = FindSctFrame();
+ // The content of a FootnoteContainer in a (column) section only need to be calculated
+ // if the section stretches already to the bottom edge of the Upper.
+ if( pSect && !pSect->IsJoinLocked() && ( bEnd ? !pSect->IsEndnAtEnd() :
+ !pSect->IsFootnoteAtEnd() ) && pSect->Growable() )
+ pSect->InvalidateSize();
+ else
+ {
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote content.
+ const bool bOldFootnoteFrameLocked( pNew->IsColLocked() );
+ pNew->ColLock();
+ pNew->KeepLockPosOfLowerObjs();
+ // #i57914# - adjust fix #i49383#
+ SwContentFrame *pCnt = pNew->ContainsContent();
+ while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
+ {
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pNew->ContainsContent();
+ continue;
+ }
+ }
+ pCnt = pCnt->FindNextCnt();
+ }
+ // #i49383#
+ if ( !bOldFootnoteFrameLocked )
+ {
+ pNew->ColUnlock();
+ }
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pNew->UnlockPosOfLowerObjs();
+ pNew->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i57914# - adjust fix #i49383#
+ if ( !bOldFootnoteFrameLocked && !pNew->GetLower() &&
+ !pNew->IsColLocked() && !pNew->IsBackMoveLocked() &&
+ !pNew->IsDeleteForbidden() )
+ {
+ pNew->Cut();
+ SwFrame::DestroyFrame(pNew);
+ }
+ }
+ pMyPage->UpdateFootnoteNum();
+ }
+ else
+ SwFrame::DestroyFrame(pNew);
+}
+
+SwFootnoteFrame *SwFootnoteBossFrame::FindFootnote( const SwContentFrame *pRef, const SwTextFootnote *pAttr )
+{
+ // the easiest and savest way goes via the attribute
+ OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
+ SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
+ SwContentNode *pNd = aIdx.GetNode().GetContentNode();
+ if ( !pNd )
+ pNd = pRef->GetAttrSet()->GetDoc()->
+ GetNodes().GoNextSection( &aIdx, true, false );
+ if ( !pNd )
+ return nullptr;
+ SwIterator<SwFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
+ SwFrame* pFrame = aIter.First();
+ if( pFrame )
+ do
+ {
+ pFrame = pFrame->GetUpper();
+ // #i28500#, #i27243# Due to the endnode collector, there are
+ // SwFootnoteFrames, which are not in the layout. Therefore the
+ // bInfFootnote flags are not set correctly, and a cell of FindFootnoteFrame
+ // would return 0. Therefore we better call ImplFindFootnoteFrame().
+ SwFootnoteFrame *pFootnote = pFrame->ImplFindFootnoteFrame();
+ if ( pFootnote && pFootnote->GetRef() == pRef )
+ {
+ // The following condition becomes true, if the whole
+ // footnotecontent is a section. While no frames exist,
+ // the HiddenFlag of the section is set, this causes
+ // the GoNextSection-function leaves the footnote.
+ if( pFootnote->GetAttr() != pAttr )
+ return nullptr;
+ while ( pFootnote && pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ return pFootnote;
+ }
+
+ } while ( nullptr != (pFrame = aIter.Next()) );
+
+ return nullptr;
+}
+
+bool SwFootnoteBossFrame::RemoveFootnote(
+ const SwContentFrame *const pRef, const SwTextFootnote *const pAttr,
+ bool bPrep )
+{
+ bool ret(false);
+ SwFootnoteFrame *pFootnote = FindFootnote( pRef, pAttr );
+ if( pFootnote )
+ {
+ ret = true;
+ do
+ {
+ SwFootnoteFrame *pFoll = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ pFootnote = pFoll;
+ } while ( pFootnote );
+ if( bPrep && pRef->IsFollow() )
+ {
+ OSL_ENSURE( pRef->IsTextFrame(), "NoTextFrame has Footnote?" );
+ SwTextFrame* pMaster = pRef->FindMaster();
+ if( !pMaster->IsLocked() )
+ pMaster->Prepare( PrepareHint::FootnoteInvalidationGone );
+ }
+ }
+ FindPageFrame()->UpdateFootnoteNum();
+ return ret;
+}
+
+void SwFootnoteBossFrame::ChangeFootnoteRef( const SwContentFrame *pOld, const SwTextFootnote *pAttr,
+ SwContentFrame *pNew )
+{
+ SwFootnoteFrame *pFootnote = FindFootnote( pOld, pAttr );
+ while ( pFootnote )
+ {
+ pFootnote->SetRef( pNew );
+ pFootnote = pFootnote->GetFollow();
+ }
+}
+
+/// OD 03.04.2003 #108446# - add parameter <_bCollectOnlyPreviousFootnotes> in
+/// order to control, if only footnotes, which are positioned before the
+/// footnote boss frame <this> have to be collected.
+void SwFootnoteBossFrame::CollectFootnotes( const SwContentFrame* _pRef,
+ SwFootnoteBossFrame* _pOld,
+ SwFootnoteFrames& _rFootnoteArr,
+ const bool _bCollectOnlyPreviousFootnotes )
+{
+ SwFootnoteFrame *pFootnote = _pOld->FindFirstFootnote();
+ while( !pFootnote )
+ {
+ if( _pOld->IsColumnFrame() )
+ {
+ // visit columns
+ while ( !pFootnote && _pOld->GetPrev() )
+ {
+ // Still no problem if no footnote was found yet. The loop is needed to pick up
+ // following rows in tables. In all other cases it might correct bad contexts.
+ _pOld = static_cast<SwFootnoteBossFrame*>(_pOld->GetPrev());
+ pFootnote = _pOld->FindFirstFootnote();
+ }
+ }
+ if( !pFootnote )
+ {
+ // previous page
+ SwPageFrame* pPg;
+ for ( SwFrame* pTmp = _pOld;
+ nullptr != ( pPg = static_cast<SwPageFrame*>(pTmp->FindPageFrame()->GetPrev()))
+ && pPg->IsEmptyPage() ;
+ )
+ {
+ pTmp = pPg;
+ }
+ if( !pPg )
+ return;
+
+ SwLayoutFrame* pBody = pPg->FindBodyCont();
+ if( pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ // multiple columns on one page => search last column
+ _pOld = static_cast<SwFootnoteBossFrame*>(pBody->GetLastLower());
+ }
+ else
+ _pOld = pPg; // single column page
+ pFootnote = _pOld->FindFirstFootnote();
+ }
+ }
+
+ CollectFootnotes_(_pRef, pFootnote, _rFootnoteArr, _bCollectOnlyPreviousFootnotes ? this : nullptr);
+}
+
+static void FootnoteInArr( SwFootnoteFrames& rFootnoteArr, SwFootnoteFrame* pFootnote )
+{
+ if ( rFootnoteArr.end() == std::find( rFootnoteArr.begin(), rFootnoteArr.end(), pFootnote ) )
+ rFootnoteArr.push_back( pFootnote );
+}
+
+void SwFootnoteBossFrame::CollectFootnotes_( const SwContentFrame* _pRef,
+ SwFootnoteFrame* _pFootnote,
+ SwFootnoteFrames& _rFootnoteArr,
+ const SwFootnoteBossFrame* _pRefFootnoteBossFrame)
+{
+ // Collect all footnotes referenced by pRef (attribute by attribute), combine them
+ // (the content might be divided over multiple pages) and cut them.
+
+ // For robustness, we do not log the corresponding footnotes here. If a footnote
+ // is touched twice, there might be a crash. This allows this function here to
+ // also handle corrupt layouts in some degrees (without loops or even crashes).
+ SwFootnoteFrames aNotFootnoteArr;
+
+ // here we have a footnote placed in front of the first one of the reference
+ OSL_ENSURE( !_pFootnote->GetMaster() || _pFootnote->GetRef() != _pRef, "move FollowFootnote?" );
+ while ( _pFootnote->GetMaster() )
+ _pFootnote = _pFootnote->GetMaster();
+
+ bool bFound = false;
+
+ do
+ {
+ // Search for the next footnote in this column/page so that
+ // we do not start from zero again after cutting one footnote.
+ SwFootnoteFrame *pNxtFootnote = _pFootnote;
+ while ( pNxtFootnote->GetFollow() )
+ pNxtFootnote = pNxtFootnote->GetFollow();
+ pNxtFootnote = static_cast<SwFootnoteFrame*>(pNxtFootnote->GetNext());
+
+ if ( !pNxtFootnote )
+ {
+ SwFootnoteBossFrame* pBoss = _pFootnote->FindFootnoteBossFrame();
+ SwPageFrame* pPage = pBoss->FindPageFrame();
+ do
+ {
+ lcl_NextFootnoteBoss( pBoss, pPage, false );
+ if( pBoss )
+ {
+ SwLayoutFrame* pCont = pBoss->FindFootnoteCont();
+ if( pCont )
+ {
+ pNxtFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ if( pNxtFootnote )
+ {
+ while( pNxtFootnote->GetMaster() )
+ pNxtFootnote = pNxtFootnote->GetMaster();
+ if( pNxtFootnote == _pFootnote )
+ pNxtFootnote = nullptr;
+ }
+ }
+ }
+ } while( !pNxtFootnote && pBoss );
+ }
+ else if( !pNxtFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ OSL_ENSURE( !pNxtFootnote->GetMaster(), "_CollectFootnote: Master expected" );
+ while ( pNxtFootnote->GetMaster() )
+ pNxtFootnote = pNxtFootnote->GetMaster();
+ }
+ if ( pNxtFootnote == _pFootnote )
+ {
+ OSL_FAIL( "_CollectFootnote: Vicious circle" );
+ pNxtFootnote = nullptr;
+ }
+
+ // OD 03.04.2003 #108446# - determine, if found footnote has to be collected.
+ bool bCollectFoundFootnote = false;
+ // Ignore endnotes which are on a separate endnote page.
+ bool bEndNote = _pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ const IDocumentSettingAccess& rSettings
+ = _pFootnote->GetAttrSet()->GetDoc()->getIDocumentSettingAccess();
+ bool bContinuousEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
+ if (_pFootnote->GetRef() == _pRef && (!bEndNote || bContinuousEndnotes))
+ {
+ if (_pRefFootnoteBossFrame)
+ {
+ SwFootnoteBossFrame* pBossOfFoundFootnote = _pFootnote->FindFootnoteBossFrame( true );
+ OSL_ENSURE( pBossOfFoundFootnote,
+ "<SwFootnoteBossFrame::CollectFootnotes_(..)> - footnote boss frame of found footnote frame missing.\nWrong layout!" );
+ if ( !pBossOfFoundFootnote || // don't crash, if no footnote boss is found.
+ pBossOfFoundFootnote->IsBefore( _pRefFootnoteBossFrame )
+ )
+ {
+ bCollectFoundFootnote = true;
+ }
+ }
+ else
+ {
+ bCollectFoundFootnote = true;
+ }
+ }
+
+ if ( bCollectFoundFootnote )
+ {
+ OSL_ENSURE( !_pFootnote->GetMaster(), "move FollowFootnote?" );
+ SwFootnoteFrame *pNxt = _pFootnote->GetFollow();
+ while ( pNxt )
+ {
+ SwFrame *pCnt = pNxt->ContainsAny();
+ if ( pCnt )
+ {
+ // destroy the follow on the way as it is empty
+ do
+ { SwFrame *pNxtCnt = pCnt->GetNext();
+ pCnt->Cut();
+ pCnt->Paste( _pFootnote );
+ pCnt = pNxtCnt;
+ } while ( pCnt );
+ }
+ else
+ {
+ OSL_ENSURE( !pNxt, "footnote without content?" );
+ pNxt->Cut();
+ SwFrame::DestroyFrame(pNxt);
+ }
+ pNxt = _pFootnote->GetFollow();
+ }
+ _pFootnote->Cut();
+ FootnoteInArr( _rFootnoteArr, _pFootnote );
+ bFound = true;
+ }
+ else
+ {
+ FootnoteInArr( aNotFootnoteArr, _pFootnote );
+ if( bFound )
+ break;
+ }
+ if ( pNxtFootnote &&
+ _rFootnoteArr.end() == std::find( _rFootnoteArr.begin(), _rFootnoteArr.end(), pNxtFootnote ) &&
+ aNotFootnoteArr.end() == std::find( aNotFootnoteArr.begin(), aNotFootnoteArr.end(), pNxtFootnote ) )
+ _pFootnote = pNxtFootnote;
+ else
+ break;
+ }
+ while ( _pFootnote );
+}
+
+void SwFootnoteBossFrame::MoveFootnotes_( SwFootnoteFrames &rFootnoteArr, bool bCalc )
+{
+ // All footnotes referenced by pRef need to be moved
+ // to a new position (based on the new column/page)
+ const sal_uInt16 nMyNum = FindPageFrame()->GetPhyPageNum();
+ const sal_uInt16 nMyCol = lcl_ColumnNum( this );
+ SwRectFnSet aRectFnSet(this);
+
+ // #i21478# - keep last inserted footnote in order to
+ // format the content of the following one.
+ SwFootnoteFrame* pLastInsertedFootnote = nullptr;
+ for (SwFootnoteFrame* pFootnote : rFootnoteArr)
+ {
+ SwFootnoteBossFrame* pRefBoss(pFootnote->GetRef()->FindFootnoteBossFrame(
+ !pFootnote->GetAttr()->GetFootnote().IsEndNote()));
+ if( pRefBoss != this )
+ {
+ const sal_uInt16 nRefNum = pRefBoss->FindPageFrame()->GetPhyPageNum();
+ const sal_uInt16 nRefCol = lcl_ColumnNum( this );
+ if( nRefNum < nMyNum || ( nRefNum == nMyNum && nRefCol <= nMyCol ) )
+ pRefBoss = this;
+ }
+ pRefBoss->InsertFootnote( pFootnote );
+
+ if ( pFootnote->GetUpper() ) // robust, e.g. with duplicates
+ {
+ // First condense the content so that footnote frames that do not fit on the page
+ // do not do too much harm (Loop 66312). So, the footnote content first grows as
+ // soon as the content gets formatted and it is sure that it fits on the page.
+ SwFrame *pCnt = pFootnote->ContainsAny();
+ while( pCnt )
+ {
+ if( pCnt->IsLayoutFrame() )
+ {
+ SwFrame* pTmp = static_cast<SwLayoutFrame*>(pCnt)->ContainsAny();
+ while( pTmp && static_cast<SwLayoutFrame*>(pCnt)->IsAnLower( pTmp ) )
+ {
+ pTmp->Prepare( PrepareHint::FootnoteMove );
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
+ aRectFnSet.SetHeight(aPrt, 0);
+
+ pTmp = pTmp->FindNext();
+ }
+ }
+ else
+ {
+ pCnt->Prepare( PrepareHint::FootnoteMove );
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCnt);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCnt);
+ aRectFnSet.SetHeight(aPrt, 0);
+
+ pCnt = pCnt->GetNext();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFootnote);
+ aRectFnSet.SetHeight(aFrm, 0);
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFootnote);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+
+ pFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pFootnote->GetUpper()->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ if( bCalc )
+ {
+ SwTextFootnote *pAttr = pFootnote->GetAttr();
+ pCnt = pFootnote->ContainsAny();
+ bool bUnlock = !pFootnote->IsBackMoveLocked();
+ pFootnote->LockBackMove();
+
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote content.
+ pFootnote->KeepLockPosOfLowerObjs();
+ // #i57914# - adjust fix #i49383#
+
+ while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
+ {
+ pCnt->InvalidatePos_();
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pFootnote->ContainsAny();
+ continue;
+ }
+ }
+ if( pCnt->IsSctFrame() )
+ {
+ // If the area is not empty, iterate also over the content
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ pCnt = pTmp;
+ else
+ pCnt = pCnt->FindNext();
+ }
+ else
+ pCnt = pCnt->FindNext();
+ }
+ if( bUnlock )
+ {
+ pFootnote->UnlockBackMove();
+ if( !pFootnote->ContainsAny() && !pFootnote->IsColLocked() )
+ {
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ // #i21478#
+ pFootnote = nullptr;
+ }
+ }
+ // #i49383#
+ if ( pFootnote )
+ {
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pFootnote->UnlockPosOfLowerObjs();
+ pFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( !pFootnote->GetMaster() && !pFootnote->GetFollow(),
+ "DelFootnote and Master/Follow?" );
+ SwFrame::DestroyFrame(pFootnote);
+ // #i21478#
+ pFootnote = nullptr;
+ }
+
+ // #i21478#
+ if ( pFootnote )
+ {
+ pLastInsertedFootnote = pFootnote;
+ }
+ }
+
+ // #i21478# - format content of footnote following
+ // the new inserted ones.
+ if ( !(bCalc && pLastInsertedFootnote) )
+ return;
+
+ if ( !pLastInsertedFootnote->GetNext() )
+ return;
+
+ SwFootnoteFrame* pNextFootnote = static_cast<SwFootnoteFrame*>(pLastInsertedFootnote->GetNext());
+ SwTextFootnote* pAttr = pNextFootnote->GetAttr();
+ SwFrame* pCnt = pNextFootnote->ContainsAny();
+
+ bool bUnlock = !pNextFootnote->IsBackMoveLocked();
+ pNextFootnote->LockBackMove();
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote content.
+ pNextFootnote->KeepLockPosOfLowerObjs();
+ // #i57914# - adjust fix #i49383#
+
+ while ( pCnt && pCnt->FindFootnoteFrame()->GetAttr() == pAttr )
+ {
+ pCnt->InvalidatePos_();
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pNextFootnote->ContainsAny();
+ continue;
+ }
+ }
+ if( pCnt->IsSctFrame() )
+ {
+ // If the area is not empty, iterate also over the content
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ pCnt = pTmp;
+ else
+ pCnt = pCnt->FindNext();
+ }
+ else
+ pCnt = pCnt->FindNext();
+ }
+ if( bUnlock )
+ {
+ pNextFootnote->UnlockBackMove();
+ }
+ // #i49383#
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pNextFootnote->UnlockPosOfLowerObjs();
+ pNextFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+}
+
+void SwFootnoteBossFrame::MoveFootnotes( const SwContentFrame *pSrc, SwContentFrame *pDest,
+ SwTextFootnote const *pAttr )
+{
+ if( ( GetFormat()->GetDoc()->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER &&
+ (!GetUpper()->IsSctFrame() || !static_cast<SwSectionFrame*>(GetUpper())->IsFootnoteAtEnd()))
+ || pAttr->GetFootnote().IsEndNote() )
+ return;
+
+ OSL_ENSURE( this == pSrc->FindFootnoteBossFrame( true ),
+ "SwPageFrame::MoveFootnotes: source frame isn't on that FootnoteBoss" );
+
+ SwFootnoteFrame *pFootnote = FindFirstFootnote();
+ if( !pFootnote )
+ return;
+
+ ChangeFootnoteRef( pSrc, pAttr, pDest );
+ SwFootnoteBossFrame *pDestBoss = pDest->FindFootnoteBossFrame( true );
+ OSL_ENSURE( pDestBoss, "+SwPageFrame::MoveFootnotes: no destination boss" );
+ if( !pDestBoss ) // robust
+ return;
+
+ SwFootnoteFrames aFootnoteArr;
+ SwFootnoteBossFrame::CollectFootnotes_(pDest, pFootnote, aFootnoteArr, nullptr);
+ if ( aFootnoteArr.empty() )
+ return;
+
+ pDestBoss->MoveFootnotes_( aFootnoteArr, true );
+ SwPageFrame* pSrcPage = FindPageFrame();
+ SwPageFrame* pDestPage = pDestBoss->FindPageFrame();
+ // update FootnoteNum only at page change
+ if( pSrcPage != pDestPage )
+ {
+ if( pSrcPage->GetPhyPageNum() > pDestPage->GetPhyPageNum() )
+ pSrcPage->UpdateFootnoteNum();
+ pDestPage->UpdateFootnoteNum();
+ }
+}
+
+void SwFootnoteBossFrame::RearrangeFootnotes( const SwTwips nDeadLine, const bool bLock,
+ const SwTextFootnote *pAttr )
+{
+ // Format all footnotes of a column/page so that they might change the column/page.
+
+ SwSaveFootnoteHeight aSave( this, nDeadLine );
+ SwFootnoteFrame *pFootnote = FindFirstFootnote();
+ if( pFootnote && pFootnote->GetPrev() && bLock )
+ {
+ SwFootnoteFrame* pFirst = static_cast<SwFootnoteFrame*>(pFootnote->GetUpper()->Lower());
+ SwFrame* pContent = pFirst->ContainsAny();
+ if( pContent )
+ {
+ bool bUnlock = !pFirst->IsBackMoveLocked();
+ pFirst->LockBackMove();
+ pFirst->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pContent->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pContent->IsTextFrame() && pContent->isFrameAreaDefinitionValid() )
+ {
+ SwObjectFormatter::FormatObjsAtFrame( *pContent,
+ *(pContent->FindPageFrame()) );
+ }
+ if( bUnlock )
+ pFirst->UnlockBackMove();
+ }
+ pFootnote = FindFirstFootnote();
+ }
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ const sal_uLong nFootnotePos = pAttr ? ::lcl_FindFootnotePos( pDoc, pAttr ) : 0;
+ SwFrame *pCnt = pFootnote ? pFootnote->ContainsAny() : nullptr;
+ if ( !pCnt )
+ return;
+
+ bool bMore = true;
+ bool bStart = pAttr == nullptr; // If no attribute is given, process all
+ // #i49383# - disable unlock of position of
+ // lower objects during format of footnote and footnote content.
+ SwFootnoteFrame* pLastFootnoteFrame( nullptr );
+ // footnote frame needs to be locked, if <bLock> isn't set.
+ bool bUnlockLastFootnoteFrame( false );
+ do
+ {
+ if( !bStart )
+ bStart = ::lcl_FindFootnotePos( pDoc, pCnt->FindFootnoteFrame()->GetAttr() )
+ == nFootnotePos;
+ if( bStart )
+ {
+ pCnt->InvalidatePos_();
+ pCnt->InvalidateSize_();
+ pCnt->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ SwFootnoteFrame* pFootnoteFrame = pCnt->FindFootnoteFrame();
+ // #i49383#
+ if ( pFootnoteFrame != pLastFootnoteFrame )
+ {
+ if ( pLastFootnoteFrame )
+ {
+ if ( !bLock && bUnlockLastFootnoteFrame )
+ {
+ pLastFootnoteFrame->ColUnlock();
+ }
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pLastFootnoteFrame->UnlockPosOfLowerObjs();
+ pLastFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ if ( !bLock && bUnlockLastFootnoteFrame &&
+ !pLastFootnoteFrame->GetLower() &&
+ !pLastFootnoteFrame->IsColLocked() &&
+ !pLastFootnoteFrame->IsBackMoveLocked() &&
+ !pLastFootnoteFrame->IsDeleteForbidden() )
+ {
+ pLastFootnoteFrame->Cut();
+ SwFrame::DestroyFrame(pLastFootnoteFrame);
+ pLastFootnoteFrame = nullptr;
+ }
+ }
+ if ( !bLock )
+ {
+ bUnlockLastFootnoteFrame = !pFootnoteFrame->IsColLocked();
+ pFootnoteFrame->ColLock();
+ }
+ pFootnoteFrame->KeepLockPosOfLowerObjs();
+ pLastFootnoteFrame = pFootnoteFrame;
+ }
+ // OD 30.10.2002 #97265# - invalidate position of footnote
+ // frame, if it's below its footnote container, in order to
+ // assure its correct position, probably calculating its previous
+ // footnote frames.
+ {
+ SwRectFnSet aRectFnSet(this);
+ SwFrame* pFootnoteContFrame = pFootnoteFrame->GetUpper();
+ if ( aRectFnSet.TopDist(pFootnoteFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pFootnoteContFrame)) > 0 )
+ {
+ pFootnoteFrame->InvalidatePos_();
+ }
+ }
+ if ( bLock )
+ {
+ bool bUnlock = !pFootnoteFrame->IsBackMoveLocked();
+ pFootnoteFrame->LockBackMove();
+ pFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ SwFrameDeleteGuard aDeleteGuard(pFootnote);
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pFootnote ? pFootnote->ContainsAny() : nullptr;
+ if (!pCnt)
+ bMore = false;
+ continue;
+ }
+ }
+ if( bUnlock )
+ {
+ pFootnoteFrame->UnlockBackMove();
+ if( !pFootnoteFrame->Lower() &&
+ !pFootnoteFrame->IsColLocked() )
+ {
+ // #i49383#
+ OSL_ENSURE( pLastFootnoteFrame == pFootnoteFrame,
+ "<SwFootnoteBossFrame::RearrangeFootnotes(..)> - <pLastFootnoteFrame> != <pFootnoteFrame>" );
+ pLastFootnoteFrame = nullptr;
+ pFootnoteFrame->Cut();
+ SwFrame::DestroyFrame(pFootnoteFrame);
+ if (pFootnote == pFootnoteFrame)
+ pFootnote = nullptr;
+ }
+ }
+ }
+ else
+ {
+ pFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ pCnt->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i49383# - format anchored objects
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ // restart format with first content
+ pCnt = pFootnote->ContainsAny();
+ continue;
+ }
+ }
+ }
+ }
+ SwSectionFrame *pDel = nullptr;
+ if( pCnt->IsSctFrame() )
+ {
+ SwFrame* pTmp = static_cast<SwSectionFrame*>(pCnt)->ContainsAny();
+ if( pTmp )
+ {
+ pCnt = pTmp;
+ continue;
+ }
+ pDel = static_cast<SwSectionFrame*>(pCnt);
+ }
+ if ( pCnt->GetNext() )
+ pCnt = pCnt->GetNext();
+ else
+ {
+ pCnt = pCnt->FindNext();
+ if ( pCnt )
+ {
+ SwFootnoteFrame* pFootnoteFrame = pCnt->FindFootnoteFrame();
+ if( pFootnoteFrame->GetRef()->FindFootnoteBossFrame(
+ pFootnoteFrame->GetAttr()->GetFootnote().IsEndNote() ) != this )
+ bMore = false;
+ }
+ else
+ bMore = false;
+ }
+ if( pDel )
+ {
+ bool bUnlockLastFootnoteFrameGuard = pLastFootnoteFrame && !pLastFootnoteFrame->IsColLocked();
+ if (bUnlockLastFootnoteFrameGuard)
+ pLastFootnoteFrame->ColLock();
+ pDel->Cut();
+ if (bUnlockLastFootnoteFrameGuard)
+ pLastFootnoteFrame->ColUnlock();
+ SwFrame::DestroyFrame(pDel);
+ }
+ if ( bMore )
+ {
+ // Go not further than to the provided footnote (if given)
+ if ( pAttr &&
+ (::lcl_FindFootnotePos( pDoc,
+ pCnt->FindFootnoteFrame()->GetAttr()) > nFootnotePos ) )
+ bMore = false;
+ }
+ } while ( bMore );
+ // #i49383#
+ if ( !pLastFootnoteFrame )
+ return;
+
+ if ( !bLock && bUnlockLastFootnoteFrame )
+ {
+ pLastFootnoteFrame->ColUnlock();
+ }
+ // #i57914# - adjust fix #i49383#
+ // enable lock of lower object position before format of footnote frame.
+ pLastFootnoteFrame->UnlockPosOfLowerObjs();
+ pLastFootnoteFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ if ( !bLock && bUnlockLastFootnoteFrame &&
+ !pLastFootnoteFrame->GetLower() &&
+ !pLastFootnoteFrame->IsColLocked() &&
+ !pLastFootnoteFrame->IsBackMoveLocked() &&
+ !pLastFootnoteFrame->IsDeleteForbidden() )
+ {
+ pLastFootnoteFrame->Cut();
+ SwFrame::DestroyFrame(pLastFootnoteFrame);
+ }
+}
+
+void SwPageFrame::UpdateFootnoteNum()
+{
+ // page numbering only if set at the document
+ if ( GetFormat()->GetDoc()->GetFootnoteInfo().m_eNum != FTNNUM_PAGE )
+ return;
+
+ SwLayoutFrame* pBody = FindBodyCont();
+ if( !pBody || !pBody->Lower() )
+ return;
+
+ SwContentFrame* pContent = pBody->ContainsContent();
+ sal_uInt16 nNum = 0;
+
+ while( pContent && pContent->FindPageFrame() == this )
+ {
+ if( static_cast<SwTextFrame*>(pContent)->HasFootnote() )
+ {
+ SwFootnoteBossFrame* pBoss = pContent->FindFootnoteBossFrame( true );
+ if( pBoss->GetUpper()->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pBoss->GetUpper())->IsOwnFootnoteNum() )
+ pContent = static_cast<SwSectionFrame*>(pBoss->GetUpper())->FindLastContent();
+ else
+ {
+ SwFootnoteFrame* pFootnote = const_cast<SwFootnoteFrame*>(pBoss->FindFirstFootnote( pContent ));
+ while( pFootnote )
+ {
+ SwTextFootnote* pTextFootnote = pFootnote->GetAttr();
+ if( !pTextFootnote->GetFootnote().IsEndNote() &&
+ pTextFootnote->GetFootnote().GetNumStr().isEmpty() &&
+ !pFootnote->GetMaster())
+ {
+ // sw_redlinehide: the layout can only keep one number
+ // up to date; depending on its setting, this is either
+ // the non-hidden or the hidden number; the other
+ // number will simply be preserved as-is (so in case
+ // there are 2 layouts, maybe both can be updated...)
+ ++nNum;
+ sal_uInt16 const nOldNum(pTextFootnote->GetFootnote().GetNumber());
+ sal_uInt16 const nOldNumRLHidden(pTextFootnote->GetFootnote().GetNumberRLHidden());
+ if (getRootFrame()->IsHideRedlines())
+ {
+ if (nNum != nOldNumRLHidden)
+ {
+ pTextFootnote->SetNumber(nOldNum, nNum, OUString());
+ }
+ }
+ else
+ {
+ if (nNum != nOldNum)
+ {
+ pTextFootnote->SetNumber(nNum, nOldNumRLHidden, OUString());
+ }
+ }
+ }
+ if ( pFootnote->GetNext() )
+ pFootnote = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
+ else
+ {
+ SwFootnoteBossFrame* pTmpBoss = pFootnote->FindFootnoteBossFrame( true );
+ if( pTmpBoss )
+ {
+ SwPageFrame* pPage = pTmpBoss->FindPageFrame();
+ pFootnote = nullptr;
+ lcl_NextFootnoteBoss( pTmpBoss, pPage, false );
+ SwFootnoteContFrame *pCont = pTmpBoss ? pTmpBoss->FindNearestFootnoteCont() : nullptr;
+ if ( pCont )
+ pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ }
+ }
+ if( pFootnote && pFootnote->GetRef() != pContent )
+ pFootnote = nullptr;
+ }
+ }
+ }
+ pContent = pContent->FindNextCnt();
+ }
+}
+
+void SwFootnoteBossFrame::SetFootnoteDeadLine( const SwTwips nDeadLine )
+{
+ SwFrame *pBody = FindBodyCont();
+ pBody->Calc(getRootFrame()->GetCurrShell()->GetOut());
+
+ SwFrame *pCont = FindFootnoteCont();
+ const SwTwips nMax = m_nMaxFootnoteHeight;// current should exceed MaxHeight
+ SwRectFnSet aRectFnSet(this);
+ if ( pCont )
+ {
+ pCont->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ m_nMaxFootnoteHeight = -aRectFnSet.BottomDist( pCont->getFrameArea(), nDeadLine );
+ }
+ else
+ m_nMaxFootnoteHeight = -aRectFnSet.BottomDist( pBody->getFrameArea(), nDeadLine );
+
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ m_nMaxFootnoteHeight += pBody->Grow( LONG_MAX, true );
+ if ( IsInSct() )
+ m_nMaxFootnoteHeight += FindSctFrame()->Grow( LONG_MAX, true );
+
+ if ( m_nMaxFootnoteHeight < 0 )
+ m_nMaxFootnoteHeight = 0;
+ if ( nMax != LONG_MAX && m_nMaxFootnoteHeight > nMax )
+ m_nMaxFootnoteHeight = nMax;
+}
+
+SwTwips SwFootnoteBossFrame::GetVarSpace() const
+{
+ // To not fall below 20% of the page height
+ // (in contrast to MSOffice where footnotes can fill a whole column/page)
+
+ const SwPageFrame* pPg = FindPageFrame();
+ OSL_ENSURE( pPg || IsInSct(), "Footnote lost page" );
+
+ const SwFrame *pBody = FindBodyCont();
+ SwTwips nRet;
+ if( pBody )
+ {
+ SwRectFnSet aRectFnSet(this);
+ if( IsInSct() )
+ {
+ nRet = 0;
+ SwTwips nTmp = aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pBody),
+ aRectFnSet.GetTop(getFrameArea()) );
+ const SwSectionFrame* pSect = FindSctFrame();
+ // Endnotes in a ftncontainer causes a deadline:
+ // the bottom of the last contentfrm
+ if( pSect->IsEndnAtEnd() ) // endnotes allowed?
+ {
+ OSL_ENSURE( !Lower() || !Lower()->GetNext() || Lower()->GetNext()->
+ IsFootnoteContFrame(), "FootnoteContainer expected" );
+ const SwFootnoteContFrame* pCont = Lower() ?
+ static_cast<const SwFootnoteContFrame*>(Lower()->GetNext()) : nullptr;
+ if( pCont )
+ {
+ const SwFootnoteFrame* pFootnote = static_cast<const SwFootnoteFrame*>(pCont->Lower());
+ while( pFootnote)
+ {
+ if( pFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ { // endnote found
+ const SwFrame* pFrame = static_cast<const SwLayoutFrame*>(Lower())->Lower();
+ if( pFrame )
+ {
+ while( pFrame->GetNext() )
+ pFrame = pFrame->GetNext(); // last cntntfrm
+ nTmp += aRectFnSet.YDiff(
+ aRectFnSet.GetTop(getFrameArea()),
+ aRectFnSet.GetBottom(pFrame->getFrameArea()) );
+ }
+ break;
+ }
+ pFootnote = static_cast<const SwFootnoteFrame*>(pFootnote->GetNext());
+ }
+ }
+ }
+ if( nTmp < nRet )
+ nRet = nTmp;
+ }
+ else
+ nRet = - aRectFnSet.GetHeight(pPg->getFramePrintArea())/5;
+ nRet += aRectFnSet.GetHeight(pBody->getFrameArea());
+ if( nRet < 0 )
+ nRet = 0;
+ }
+ else
+ nRet = 0;
+ if ( IsPageFrame() )
+ {
+ const SwViewShell *pSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ nRet += BROWSE_HEIGHT - getFrameArea().Height();
+ }
+ return nRet;
+}
+
+/** Obtain if pFrame's size adjustment should be processed
+ *
+ * For a page frame of columns directly below the page AdjustNeighbourhood() needs
+ * to be called, or Grow()/ Shrink() for frame columns respectively.
+ *
+ * A column section is special, since if there is a footnote container in a column
+ * and those footnotes are not collected, it is handled like a page frame.
+ *
+ * @see AdjustNeighbourhood()
+ * @see Grow()
+ * @see Shrink()
+ */
+SwNeighbourAdjust SwFootnoteBossFrame::NeighbourhoodAdjustment_() const
+{
+ SwNeighbourAdjust nRet = SwNeighbourAdjust::OnlyAdjust;
+ if( GetUpper() && !GetUpper()->IsPageBodyFrame() )
+ {
+ // column sections need grow/shrink
+ if( GetUpper()->IsFlyFrame() )
+ nRet = SwNeighbourAdjust::GrowShrink;
+ else
+ {
+ OSL_ENSURE( GetUpper()->IsSctFrame(), "NeighbourhoodAdjustment: Unexpected Upper" );
+ if( !GetNext() && !GetPrev() )
+ nRet = SwNeighbourAdjust::GrowAdjust; // section with a single column (FootnoteAtEnd)
+ else
+ {
+ const SwFrame* pTmp = Lower();
+ OSL_ENSURE( pTmp, "NeighbourhoodAdjustment: Missing Lower()" );
+ if( !pTmp->GetNext() )
+ nRet = SwNeighbourAdjust::GrowShrink;
+ else if( !GetUpper()->IsColLocked() )
+ nRet = SwNeighbourAdjust::AdjustGrow;
+ OSL_ENSURE( !pTmp->GetNext() || pTmp->GetNext()->IsFootnoteContFrame(),
+ "NeighbourhoodAdjustment: Who's that guy?" );
+ }
+ }
+ }
+ return nRet;
+}
+
+void SwPageFrame::SetColMaxFootnoteHeight()
+{
+ SwLayoutFrame *pBody = FindBodyCont();
+ if( pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame() )
+ {
+ SwColumnFrame* pCol = static_cast<SwColumnFrame*>(pBody->Lower());
+ do
+ {
+ pCol->SetMaxFootnoteHeight( GetMaxFootnoteHeight() );
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ } while ( pCol );
+ }
+}
+
+bool SwLayoutFrame::MoveLowerFootnotes( SwContentFrame *pStart, SwFootnoteBossFrame *pOldBoss,
+ SwFootnoteBossFrame *pNewBoss, const bool bFootnoteNums )
+{
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ if ( pDoc->GetFootnoteIdxs().empty() )
+ return false;
+ if( pDoc->GetFootnoteInfo().m_ePos == FTNPOS_CHAPTER &&
+ ( !IsInSct() || !FindSctFrame()->IsFootnoteAtEnd() ) )
+ return true;
+
+ if ( !pNewBoss )
+ pNewBoss = FindFootnoteBossFrame( true );
+ if ( pNewBoss == pOldBoss )
+ return false;
+
+ bool bMoved = false;
+ if( !pStart )
+ pStart = ContainsContent();
+
+ SwFootnoteFrames aFootnoteArr;
+
+ while ( IsAnLower( pStart ) )
+ {
+ if ( static_cast<SwTextFrame*>(pStart)->HasFootnote() )
+ {
+ // OD 03.04.2003 #108446# - To avoid unnecessary moves of footnotes
+ // use new parameter <_bCollectOnlyPreviousFootnote> (4th parameter of
+ // method <SwFootnoteBossFrame::CollectFootnote(..)>) to control, that only
+ // footnotes have to be collected, that are positioned before the
+ // new dedicated footnote boss frame.
+ pNewBoss->CollectFootnotes( pStart, pOldBoss, aFootnoteArr, true );
+ }
+ pStart = pStart->GetNextContentFrame();
+ }
+
+ OSL_ENSURE( pOldBoss->IsInSct() == pNewBoss->IsInSct(),
+ "MoveLowerFootnotes: Section confusion" );
+ std::unique_ptr<SwFootnoteFrames> pFootnoteArr;
+ SwLayoutFrame* pNewChief = nullptr;
+ SwLayoutFrame* pOldChief = nullptr;
+
+ bool bFoundCandidate = false;
+ if (pStart && pOldBoss->IsInSct())
+ {
+ pOldChief = pOldBoss->FindSctFrame();
+ pNewChief = pNewBoss->FindSctFrame();
+ bFoundCandidate = pOldChief != pNewChief;
+ }
+
+ if (bFoundCandidate)
+ {
+ pFootnoteArr.reset(new SwFootnoteFrames);
+ pOldChief = pOldBoss->FindFootnoteBossFrame( true );
+ pNewChief = pNewBoss->FindFootnoteBossFrame( true );
+ while( pOldChief->IsAnLower( pStart ) )
+ {
+ if ( static_cast<SwTextFrame*>(pStart)->HasFootnote() )
+ static_cast<SwFootnoteBossFrame*>(pNewChief)->CollectFootnotes( pStart,
+ pOldBoss, *pFootnoteArr );
+ pStart = pStart->GetNextContentFrame();
+ }
+ if( pFootnoteArr->empty() )
+ {
+ pFootnoteArr.reset();
+ }
+ }
+ else
+ pFootnoteArr = nullptr;
+
+ if ( !aFootnoteArr.empty() || pFootnoteArr )
+ {
+ if( !aFootnoteArr.empty() )
+ pNewBoss->MoveFootnotes_( aFootnoteArr, true );
+ if( pFootnoteArr )
+ {
+ assert(pNewChief);
+ static_cast<SwFootnoteBossFrame*>(pNewChief)->MoveFootnotes_( *pFootnoteArr, true );
+ pFootnoteArr.reset();
+ }
+ bMoved = true;
+
+ // update FootnoteNum only at page change
+ if ( bFootnoteNums )
+ {
+ SwPageFrame* pOldPage = pOldBoss->FindPageFrame();
+ SwPageFrame* pNewPage =pNewBoss->FindPageFrame();
+ if( pOldPage != pNewPage )
+ {
+ pOldPage->UpdateFootnoteNum();
+ pNewPage->UpdateFootnoteNum();
+ }
+ }
+ }
+ return bMoved;
+}
+
+/// Return value guarantees that a new page was not created. See SwFlowFrame::MoveFwd.
+bool SwContentFrame::MoveFootnoteCntFwd( bool bMakePage, SwFootnoteBossFrame *pOldBoss )
+{
+ OSL_ENSURE( IsInFootnote(), "no footnote." );
+ SwLayoutFrame *pFootnote = FindFootnoteFrame();
+
+ // The first paragraph in the first footnote in the first column in the
+ // sectionfrm at the top of the page has not to move forward, if the
+ // columnbody is empty.
+ if( pOldBoss->IsInSct() && !pOldBoss->GetIndPrev() && !GetIndPrev() &&
+ !pFootnote->GetPrev() )
+ {
+ SwLayoutFrame* pBody = pOldBoss->FindBodyCont();
+ if( !pBody || !pBody->Lower() )
+ return true;
+ }
+
+ //fix(9538): if the footnote has neighbors behind itself, remove them temporarily
+ SwLayoutFrame *pNxt = static_cast<SwLayoutFrame*>(pFootnote->GetNext());
+ SwLayoutFrame *pLst = nullptr;
+ while ( pNxt )
+ {
+ while ( pNxt->GetNext() )
+ pNxt = static_cast<SwLayoutFrame*>(pNxt->GetNext());
+ if ( pNxt == pLst )
+ pNxt = nullptr;
+ else
+ { pLst = pNxt;
+ SwContentFrame *pCnt = pNxt->ContainsContent();
+ if( pCnt )
+ pCnt->MoveFootnoteCntFwd( true, pOldBoss );
+ pNxt = static_cast<SwLayoutFrame*>(pFootnote->GetNext());
+ }
+ }
+
+ bool bSamePage = true;
+ SwLayoutFrame *pNewUpper =
+ GetLeaf( bMakePage ? MAKEPAGE_INSERT : MAKEPAGE_NONE, true );
+
+ if ( pNewUpper )
+ {
+ SwFootnoteBossFrame * const pNewBoss = pNewUpper->FindFootnoteBossFrame();
+ // Are we changing the column/page?
+ bool bSameBoss = pNewBoss == pOldBoss;
+ if ( !bSameBoss )
+ {
+ bSamePage = pOldBoss->FindPageFrame() == pNewBoss->FindPageFrame(); // page change?
+ pNewUpper->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ // The layout leaf of the footnote is either a footnote container or a footnote.
+ // If it is a footnote and it has the same footnote reference like the old Upper,
+ // then move the content inside of it.
+ // If it is a container or the reference differs, create a new footnote and add
+ // it into the container.
+ // Create also a SectionFrame if currently in an area inside a footnote.
+ SwFootnoteFrame* pTmpFootnote = pNewUpper->IsFootnoteFrame() ? static_cast<SwFootnoteFrame*>(pNewUpper) : nullptr;
+ if (!pTmpFootnote)
+ {
+ assert(pNewUpper->IsFootnoteContFrame() && "New Upper not a FootnoteCont");
+ SwFootnoteContFrame *pCont = static_cast<SwFootnoteContFrame*>(pNewUpper);
+ pTmpFootnote = SwFootnoteContFrame::AppendChained(this, true);
+ SwFrame* pNx = pCont->Lower();
+ if( pNx && pTmpFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ while(pNx && !static_cast<SwFootnoteFrame*>(pNx)->GetAttr()->GetFootnote().IsEndNote())
+ pNx = pNx->GetNext();
+ pTmpFootnote->Paste( pCont, pNx );
+ pTmpFootnote->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ }
+ OSL_ENSURE( pTmpFootnote->GetAttr() == FindFootnoteFrame()->GetAttr(), "Wrong Footnote!" );
+ // areas inside of footnotes get a special treatment
+ SwLayoutFrame *pNewUp = pTmpFootnote;
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSect = FindSctFrame();
+ // area inside of a footnote (or only footnote in an area)?
+ if( pSect->IsInFootnote() )
+ {
+ if( pTmpFootnote->Lower() && pTmpFootnote->Lower()->IsSctFrame() &&
+ pSect->GetFollow() == static_cast<SwSectionFrame*>(pTmpFootnote->Lower()) )
+ pNewUp = static_cast<SwSectionFrame*>(pTmpFootnote->Lower());
+ else
+ {
+ pNewUp = new SwSectionFrame( *pSect, false );
+ pNewUp->InsertBefore( pTmpFootnote, pTmpFootnote->Lower() );
+ static_cast<SwSectionFrame*>(pNewUp)->Init();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pNewUp);
+ aFrm.Pos() = pTmpFootnote->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 ); // for notifications
+ }
+
+ // If the section frame has a successor then the latter needs
+ // to be moved behind the new Follow of the section frame.
+ SwFrame* pTmp = pSect->GetNext();
+ if( pTmp )
+ {
+ SwFlowFrame* pTmpNxt;
+ if( pTmp->IsContentFrame() )
+ pTmpNxt = static_cast<SwContentFrame*>(pTmp);
+ else if( pTmp->IsSctFrame() )
+ pTmpNxt = static_cast<SwSectionFrame*>(pTmp);
+ else
+ {
+ OSL_ENSURE( pTmp->IsTabFrame(), "GetNextSctLeaf: Wrong Type" );
+ pTmpNxt = static_cast<SwTabFrame*>(pTmp);
+ }
+ // we will dereference pNewUp in the following MoveSubTree call
+ // so it certainly should not be deleted before that
+ SwFrameDeleteGuard aDeleteGuard(pNewUp);
+ pTmpNxt->MoveSubTree( pTmpFootnote, pNewUp->GetNext() );
+ }
+ }
+ }
+ }
+
+ MoveSubTree( pNewUp, pNewUp->Lower() );
+
+ if( !bSameBoss )
+ Prepare( PrepareHint::BossChanged );
+ }
+ return bSamePage;
+}
+
+SwSaveFootnoteHeight::SwSaveFootnoteHeight( SwFootnoteBossFrame *pBs, const SwTwips nDeadLine ) :
+ aGuard(pBs),
+ pBoss( pBs ),
+ nOldHeight( pBs->GetMaxFootnoteHeight() )
+{
+ pBoss->SetFootnoteDeadLine( nDeadLine );
+ nNewHeight = pBoss->GetMaxFootnoteHeight();
+}
+
+SwSaveFootnoteHeight::~SwSaveFootnoteHeight()
+{
+ // If somebody tweaked the deadline meanwhile, we let it happen
+ if ( nNewHeight == pBoss->GetMaxFootnoteHeight() )
+ pBoss->m_nMaxFootnoteHeight = nOldHeight;
+}
+
+#ifdef DBG_UTIL
+//JP 15.10.2001: in a non pro version test if the attribute has the same
+// meaning which his reference is
+
+// Normally, the pRef member and the GetRefFromAttr() result has to be
+// identically. Sometimes footnote will be moved from a master to its follow,
+// but the GetRef() is called first, so we have to ignore a master/follow
+// mismatch.
+
+const SwContentFrame* SwFootnoteFrame::GetRef() const
+{
+ const SwContentFrame* pRefAttr = GetRefFromAttr();
+ // check consistency: access to deleted frame?
+ assert(mpReference == pRefAttr || mpReference->IsAnFollow(pRefAttr)
+ || pRefAttr->IsAnFollow(mpReference));
+ (void) pRefAttr;
+ return mpReference;
+}
+
+SwContentFrame* SwFootnoteFrame::GetRef()
+{
+ const SwContentFrame* pRefAttr = GetRefFromAttr();
+ // check consistency: access to deleted frame?
+ assert(mpReference == pRefAttr || mpReference->IsAnFollow(pRefAttr)
+ || pRefAttr->IsAnFollow(mpReference));
+ (void) pRefAttr;
+ return mpReference;
+}
+#endif
+
+const SwContentFrame* SwFootnoteFrame::GetRefFromAttr() const
+{
+ SwFootnoteFrame* pThis = const_cast<SwFootnoteFrame*>(this);
+ return pThis->GetRefFromAttr();
+}
+
+SwContentFrame* SwFootnoteFrame::GetRefFromAttr()
+{
+ assert(mpAttribute && "invalid Attribute");
+ SwTextNode& rTNd = const_cast<SwTextNode&>(mpAttribute->GetTextNode());
+ SwPosition aPos( rTNd, SwIndex( &rTNd, mpAttribute->GetStart() ));
+ SwContentFrame* pCFrame = rTNd.getLayoutFrame(getRootFrame(), &aPos);
+ return pCFrame;
+}
+
+/** search for last content in the current footnote frame
+
+ OD 2005-12-02 #i27138#
+*/
+SwContentFrame* SwFootnoteFrame::FindLastContent()
+{
+ SwContentFrame* pLastContentFrame( nullptr );
+
+ // find last lower, which is a content frame or contains content.
+ // hidden text frames, empty sections and empty tables have to be skipped.
+ SwFrame* pLastLowerOfFootnote( GetLower() );
+ SwFrame* pTmpLastLower( pLastLowerOfFootnote );
+ while ( pTmpLastLower && pTmpLastLower->GetNext() )
+ {
+ pTmpLastLower = pTmpLastLower->GetNext();
+ if ( ( pTmpLastLower->IsTextFrame() &&
+ !static_cast<SwTextFrame*>(pTmpLastLower)->IsHiddenNow() ) ||
+ ( pTmpLastLower->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pTmpLastLower)->GetSection() &&
+ static_cast<SwSectionFrame*>(pTmpLastLower)->ContainsContent() ) ||
+ ( pTmpLastLower->IsTabFrame() &&
+ static_cast<SwTabFrame*>(pTmpLastLower)->ContainsContent() ) )
+ {
+ pLastLowerOfFootnote = pTmpLastLower;
+ }
+ }
+
+ // determine last content frame depending on type of found last lower.
+ if ( pLastLowerOfFootnote && pLastLowerOfFootnote->IsTabFrame() )
+ {
+ pLastContentFrame = static_cast<SwTabFrame*>(pLastLowerOfFootnote)->FindLastContent();
+ }
+ else if ( pLastLowerOfFootnote && pLastLowerOfFootnote->IsSctFrame() )
+ {
+ pLastContentFrame = static_cast<SwSectionFrame*>(pLastLowerOfFootnote)->FindLastContent();
+ }
+ else
+ {
+ pLastContentFrame = dynamic_cast<SwContentFrame*>(pLastLowerOfFootnote);
+ }
+
+ return pLastContentFrame;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/hffrm.cxx b/sw/source/core/layout/hffrm.cxx
new file mode 100644
index 000000000..a8e9bfa5a
--- /dev/null
+++ b/sw/source/core/layout/hffrm.cxx
@@ -0,0 +1,768 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <pagefrm.hxx>
+#include <fmtcntnt.hxx>
+#include <fmthdft.hxx>
+#include <fmtfsize.hxx>
+#include <viewopt.hxx>
+#include <hffrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <sectfrm.hxx>
+#include <flyfrm.hxx>
+#include <frmtool.hxx>
+#include <hfspacingitem.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <ndindex.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+static SwTwips lcl_GetFrameMinHeight(const SwLayoutFrame & rFrame)
+{
+ const SwFormatFrameSize &rSz = rFrame.GetFormat()->GetFrameSize();
+ SwTwips nMinHeight;
+
+ switch (rSz.GetHeightSizeType())
+ {
+ case SwFrameSize::Minimum:
+ nMinHeight = rSz.GetHeight();
+
+ break;
+
+ default:
+ nMinHeight = 0;
+ }
+
+ return nMinHeight;
+}
+
+static SwTwips lcl_CalcContentHeight(SwLayoutFrame & frm)
+{
+ SwTwips nRemaining = 0;
+ SwFrame* pFrame = frm.Lower();
+
+ while ( pFrame )
+ {
+ SwTwips nTmp;
+
+ nTmp = pFrame->getFrameArea().Height();
+ nRemaining += nTmp;
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ {
+ nTmp = static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - pFrame->getFramePrintArea().Height();
+ // This TextFrame would like to be a bit bigger
+ nRemaining += nTmp;
+ }
+ else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ {
+ nTmp = static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ nRemaining += nTmp;
+ }
+ pFrame = pFrame->GetNext();
+ }
+
+ return nRemaining;
+}
+
+static void lcl_LayoutFrameEnsureMinHeight(SwLayoutFrame & rFrame)
+{
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(rFrame);
+
+ if (rFrame.getFrameArea().Height() < nMinHeight)
+ {
+ rFrame.Grow(nMinHeight - rFrame.getFrameArea().Height());
+ }
+}
+
+SwHeadFootFrame::SwHeadFootFrame( SwFrameFormat * pFormat, SwFrame* pSib, SwFrameType nTypeIn)
+ : SwLayoutFrame( pFormat, pSib )
+{
+ mnFrameType = nTypeIn;
+ SetDerivedVert( false );
+
+ const SwFormatContent &rCnt = pFormat->GetContent();
+
+ OSL_ENSURE( rCnt.GetContentIdx(), "No content for Header." );
+
+ // Have the objects created right now for header and footer
+ bool bOld = bObjsDirect;
+ bObjsDirect = true;
+ SwNodeOffset nIndex = rCnt.GetContentIdx()->GetIndex();
+ ::InsertCnt_( this, pFormat->GetDoc(), ++nIndex );
+ bObjsDirect = bOld;
+}
+
+void SwHeadFootFrame::FormatPrt(SwTwips & nUL, const SwBorderAttrs * pAttrs)
+{
+ if (GetEatSpacing())
+ {
+ /* The minimal height of the print area is the minimal height of the
+ frame without the height needed for borders and shadow. */
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+
+ nMinHeight -= pAttrs->CalcTop();
+ nMinHeight -= pAttrs->CalcBottom();
+
+ /* If the minimal height of the print area is negative, try to
+ compensate by overlapping */
+ SwTwips nOverlap = 0;
+ if (nMinHeight < 0)
+ {
+ nOverlap = -nMinHeight;
+ nMinHeight = 0;
+ }
+
+ /* Calculate desired height of content. The minimal height has to be
+ adhered. */
+ SwTwips nHeight;
+
+ if ( ! HasFixSize() )
+ nHeight = lcl_CalcContentHeight(*this);
+ else
+ nHeight = nMinHeight;
+
+ if (nHeight < nMinHeight)
+ nHeight = nMinHeight;
+
+ /* calculate initial spacing/line space */
+ SwTwips nSpace, nLine;
+
+ if (IsHeaderFrame())
+ {
+ nSpace = pAttrs->CalcBottom();
+ nLine = pAttrs->CalcBottomLine();
+ }
+ else
+ {
+ nSpace = pAttrs->CalcTop();
+ nLine = pAttrs->CalcTopLine();
+ }
+
+ /* calculate overlap and correct spacing */
+ nOverlap += nHeight - nMinHeight;
+ if (nOverlap < nSpace - nLine)
+ nSpace -= nOverlap;
+ else
+ nSpace = nLine;
+
+ /* calculate real vertical space between frame and print area */
+ if (IsHeaderFrame())
+ nUL = pAttrs->CalcTop() + nSpace;
+ else
+ nUL = pAttrs->CalcBottom() + nSpace;
+
+ /* set print area */
+ SwTwips nLR = pAttrs->CalcLeft( this ) + pAttrs->CalcRight( this );
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+
+ aPrt.Left(pAttrs->CalcLeft(this));
+
+ if (IsHeaderFrame())
+ {
+ aPrt.Top(pAttrs->CalcTop());
+ }
+ else
+ {
+ aPrt.Top(nSpace);
+ }
+
+ aPrt.Width(getFrameArea().Width() - nLR);
+
+ SwTwips nNewHeight;
+
+ if (nUL < getFrameArea().Height())
+ {
+ nNewHeight = getFrameArea().Height() - nUL;
+ }
+ else
+ {
+ nNewHeight = 0;
+ }
+
+ aPrt.Height(nNewHeight);
+ }
+ else
+ {
+ // Set position
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( pAttrs->CalcLeft( this ) );
+ aPrt.Top ( pAttrs->CalcTop() );
+
+ // Set sizes - the sizes are given by the surrounding Frame, just
+ // subtract the borders.
+ SwTwips nLR = pAttrs->CalcLeft( this ) + pAttrs->CalcRight( this );
+ aPrt.Width ( getFrameArea().Width() - nLR );
+ aPrt.Height( getFrameArea().Height()- nUL );
+ }
+
+ setFramePrintAreaValid(true);
+}
+
+void SwHeadFootFrame::FormatSize(SwTwips nUL, const SwBorderAttrs * pAttrs)
+{
+ if ( !HasFixSize() )
+ {
+ if( !IsColLocked() )
+ {
+ setFramePrintAreaValid(true);
+ setFrameAreaSizeValid(true);
+
+ const SwTwips nBorder = nUL;
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+ nMinHeight -= pAttrs->CalcTop();
+ nMinHeight -= pAttrs->CalcBottom();
+
+ if (nMinHeight < 0)
+ nMinHeight = 0;
+
+ ColLock();
+
+ SwTwips nMaxHeight = LONG_MAX;
+ SwTwips nRemaining, nOldHeight;
+ // #i64301#
+ // use the position of the footer printing area to control invalidation
+ // of the first footer content.
+ Point aOldFooterPrtPos;
+
+ do
+ {
+ nOldHeight = getFramePrintArea().Height();
+ SwFrame* pFrame = Lower();
+ // #i64301#
+ if ( pFrame &&
+ aOldFooterPrtPos != ( getFrameArea().Pos() + getFramePrintArea().Pos() ) )
+ {
+ pFrame->InvalidatePos_();
+ aOldFooterPrtPos = getFrameArea().Pos() + getFramePrintArea().Pos();
+ }
+ int nLoopControl = 0;
+ while( pFrame )
+ {
+ pFrame->Calc(getRootFrame()->GetCurrShell()->GetOut());
+ // #i43771# - format also object anchored
+ // at the frame
+ // #i46941# - frame has to be valid.
+ // Note: frame could be invalid after calling its format,
+ // if it's locked
+ OSL_ENSURE( StackHack::IsLocked() || !pFrame->IsTextFrame() ||
+ pFrame->isFrameAreaDefinitionValid() ||
+ static_cast<SwTextFrame*>(pFrame)->IsJoinLocked(),
+ "<SwHeadFootFrame::FormatSize(..)> - text frame invalid and not locked." );
+
+ if ( pFrame->IsTextFrame() && pFrame->isFrameAreaDefinitionValid() )
+ {
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pFrame,
+ *(pFrame->FindPageFrame()) ) )
+ {
+ if (nLoopControl++ < 20)
+ {
+ // restart format with first content
+ pFrame = Lower();
+ continue;
+ }
+ else
+ SAL_WARN("sw", "SwHeadFootFrame::FormatSize: loop detection triggered");
+ }
+ }
+ pFrame = pFrame->GetNext();
+ }
+ nRemaining = 0;
+ pFrame = Lower();
+
+ while ( pFrame )
+ {
+ nRemaining += pFrame->getFrameArea().Height();
+
+ if( pFrame->IsTextFrame() &&
+ static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ // This TextFrame would like to be a bit bigger
+ nRemaining += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - pFrame->getFramePrintArea().Height();
+ else if( pFrame->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ nRemaining += static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ pFrame = pFrame->GetNext();
+ }
+ if ( nRemaining < nMinHeight )
+ nRemaining = nMinHeight;
+
+ SwTwips nDiff = nRemaining - nOldHeight;
+
+ if( !nDiff )
+ break;
+ if( nDiff < 0 )
+ {
+ nMaxHeight = nOldHeight;
+
+ if( nRemaining <= nMinHeight )
+ nRemaining = ( nMaxHeight + nMinHeight + 1 ) / 2;
+ }
+ else
+ {
+ if (nOldHeight > nMinHeight)
+ nMinHeight = nOldHeight;
+
+ if( nRemaining >= nMaxHeight )
+ nRemaining = ( nMaxHeight + nMinHeight + 1 ) / 2;
+ }
+
+ nDiff = nRemaining - nOldHeight;
+
+ if ( nDiff )
+ {
+ ColUnlock();
+ if ( nDiff > 0 )
+ {
+ if ( Grow( nDiff ) )
+ {
+ pFrame = Lower();
+
+ while ( pFrame )
+ {
+ if( pFrame->IsTextFrame())
+ {
+ SwTextFrame * pTmpFrame = static_cast<SwTextFrame*>(pFrame);
+ if (pTmpFrame->IsUndersized() )
+ {
+ pTmpFrame->InvalidateSize();
+ pTmpFrame->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
+ }
+ }
+ /* #i3568# Undersized sections need to be
+ invalidated too. */
+ else if (pFrame->IsSctFrame())
+ {
+ SwSectionFrame * pTmpFrame =
+ static_cast<SwSectionFrame*>(pFrame);
+ if (pTmpFrame->IsUndersized() )
+ {
+ pTmpFrame->InvalidateSize();
+ pTmpFrame->Prepare(PrepareHint::AdjustSizeWithoutFormatting);
+ }
+ }
+ pFrame = pFrame->GetNext();
+ }
+ }
+ }
+ else
+ Shrink( -nDiff );
+ // Quickly update the position
+
+ MakePos();
+ ColLock();
+ }
+ else
+ break;
+ // Don't overwrite the lower edge of the upper
+ if ( GetUpper() && getFrameArea().Height() )
+ {
+ const SwTwips nDeadLine = GetUpper()->getFrameArea().Top() + GetUpper()->getFramePrintArea().Bottom();
+ const SwTwips nBot = getFrameArea().Bottom();
+
+ if ( nBot > nDeadLine )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Bottom( nDeadLine );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( getFrameArea().Height() - nBorder );
+ }
+ }
+
+ setFramePrintAreaValid(true);
+ setFrameAreaSizeValid(true);
+ } while( nRemaining<=nMaxHeight && nOldHeight!=getFramePrintArea().Height() );
+ ColUnlock();
+ }
+
+ setFramePrintAreaValid(true);
+ setFrameAreaSizeValid(true);
+ }
+ else //if (GetType() & FRM_HEADFOOT)
+ {
+ do
+ {
+ if ( getFrameArea().Height() != pAttrs->GetSize().Height() )
+ {
+ ChgSize( Size( getFrameArea().Width(), pAttrs->GetSize().Height()));
+ }
+
+ setFrameAreaSizeValid(true);
+ MakePos();
+ } while ( !isFrameAreaSizeValid() );
+ }
+}
+
+void SwHeadFootFrame::Format(vcl::RenderContext* pRenderContext, const SwBorderAttrs * pAttrs)
+{
+ OSL_ENSURE( pAttrs, "SwFooterFrame::Format, pAttrs is 0." );
+
+ if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
+ return;
+
+ if ( ! GetEatSpacing() && IsHeaderFrame())
+ {
+ SwLayoutFrame::Format(pRenderContext, pAttrs);
+ }
+ else
+ {
+ lcl_LayoutFrameEnsureMinHeight(*this);
+
+ SwTwips nUL = pAttrs->CalcTop() + pAttrs->CalcBottom();
+
+ if ( !isFramePrintAreaValid() )
+ FormatPrt(nUL, pAttrs);
+
+ if ( !isFrameAreaSizeValid() )
+ FormatSize(nUL, pAttrs);
+ }
+}
+
+SwTwips SwHeadFootFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwTwips nResult;
+
+ if ( IsColLocked() )
+ {
+ nResult = 0;
+ }
+ else if (!GetEatSpacing())
+ {
+ nResult = SwLayoutFrame::GrowFrame(nDist, bTst, bInfo);
+ }
+ else
+ {
+ nResult = 0;
+
+ std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
+ OSL_ENSURE(oAccess, "no border attributes");
+
+ SwBorderAttrs * pAttrs = oAccess->Get();
+
+ /* First assume the whole amount to grow can be provided by eating
+ spacing. */
+ SwTwips nEat = nDist;
+ SwTwips nMaxEat;
+
+ /* calculate maximum eatable spacing */
+ if (IsHeaderFrame())
+ nMaxEat = getFrameArea().Height() - getFramePrintArea().Top() - getFramePrintArea().Height() - pAttrs->CalcBottomLine();
+ else
+ nMaxEat = getFramePrintArea().Top() - pAttrs->CalcTopLine();
+
+ if (nMaxEat < 0)
+ nMaxEat = 0;
+
+ /* If the frame is too small, eat less spacing thus letting the frame
+ grow more. */
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+ SwTwips nFrameTooSmall = nMinHeight - getFrameArea().Height();
+
+ if (nFrameTooSmall > 0)
+ nEat -= nFrameTooSmall;
+
+ /* No negative eating, not eating more than allowed. */
+ if (nEat < 0)
+ nEat = 0;
+ else if (nEat > nMaxEat)
+ nEat = nMaxEat;
+
+ // Notify fly frame, if header frame
+ // grows. Consider, that 'normal' grow of layout frame already notifies
+ // the fly frames.
+ bool bNotifyFlys = false;
+ if (nEat > 0)
+ {
+ if ( ! bTst)
+ {
+ if (! IsHeaderFrame())
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top(aPrt.Top() - nEat);
+ aPrt.Height(aPrt.Height() - nEat);
+ }
+
+ InvalidateAll();
+ }
+
+ nResult += nEat;
+ // trigger fly frame notify.
+ if ( IsHeaderFrame() )
+ {
+ bNotifyFlys = true;
+ }
+ }
+
+ if (nDist - nEat > 0)
+ {
+ const SwTwips nFrameGrow =
+ SwLayoutFrame::GrowFrame( nDist - nEat, bTst, bInfo );
+
+ nResult += nFrameGrow;
+ if ( nFrameGrow > 0 )
+ {
+ bNotifyFlys = false;
+ }
+ }
+
+ // notify fly frames, if necessary and triggered.
+ if ( ( nResult > 0 ) && bNotifyFlys )
+ {
+ NotifyLowerObjs();
+ }
+ }
+
+ if ( nResult && !bTst )
+ SetCompletePaint();
+
+ return nResult;
+}
+
+SwTwips SwHeadFootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwTwips nResult;
+
+ if ( IsColLocked() )
+ {
+ nResult = 0;
+ }
+ else if (! GetEatSpacing())
+ {
+ nResult = SwLayoutFrame::ShrinkFrame(nDist, bTst, bInfo);
+ }
+ else
+ {
+ nResult = 0;
+
+ SwTwips nMinHeight = lcl_GetFrameMinHeight(*this);
+ SwTwips nOldHeight = getFrameArea().Height();
+ SwTwips nRest = 0; // Amount to shrink by spitting out spacing
+
+ if ( nOldHeight >= nMinHeight )
+ {
+ /* If the frame's height is bigger than its minimum height, shrink
+ the frame towards its minimum height. If this is not sufficient
+ to provide the shrinking requested provide the rest by spitting
+ out spacing. */
+
+ SwTwips nBiggerThanMin = nOldHeight - nMinHeight;
+
+ if (nBiggerThanMin < nDist)
+ {
+ nRest = nDist - nBiggerThanMin;
+ }
+ /* info: declaration of nRest -> else nRest = 0 */
+ }
+ else
+ /* The frame cannot shrink. Provide shrinking by spitting out
+ spacing. */
+ nRest = nDist;
+
+ // Notify fly frame, if header/footer frame shrinks.
+ // Consider, that 'normal' shrink of layout frame already notifies the fly frames.
+ bool bNotifyFlys = false;
+ if (nRest > 0)
+ {
+ std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
+ OSL_ENSURE(oAccess, "no border attributes");
+
+ SwBorderAttrs * pAttrs = oAccess->Get();
+
+ /* minimal height of print area */
+ SwTwips nMinPrtHeight = nMinHeight
+ - pAttrs->CalcTop()
+ - pAttrs->CalcBottom();
+
+ if (nMinPrtHeight < 0)
+ nMinPrtHeight = 0;
+
+ /* assume all shrinking can be provided */
+ SwTwips nShrink = nRest;
+
+ /* calculate maximum shrinking */
+ SwTwips nMaxShrink = getFramePrintArea().Height() - nMinPrtHeight;
+
+ /* shrink no more than maximum shrinking */
+ if (nShrink > nMaxShrink)
+ {
+ //nRest -= nShrink - nMaxShrink;
+ nShrink = nMaxShrink;
+ }
+
+ if (!bTst)
+ {
+ if (! IsHeaderFrame() )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top(aPrt.Top() + nShrink);
+ aPrt.Height(aPrt.Height() - nShrink);
+ }
+
+ InvalidateAll();
+ }
+ nResult += nShrink;
+ // Trigger fly frame notify.
+ if ( IsHeaderFrame() )
+ {
+ bNotifyFlys = true;
+ }
+ }
+
+ /* The shrinking not providable by spitting out spacing has to be done
+ by the frame. */
+ if (nDist - nRest > 0)
+ {
+ SwTwips nShrinkAmount = SwLayoutFrame::ShrinkFrame( nDist - nRest, bTst, bInfo );
+ nResult += nShrinkAmount;
+ if ( nShrinkAmount > 0 )
+ {
+ bNotifyFlys = false;
+ }
+ }
+
+ // Notify fly frames, if necessary.
+ if ( ( nResult > 0 ) && bNotifyFlys )
+ {
+ NotifyLowerObjs();
+ }
+ }
+
+ return nResult;
+}
+
+bool SwHeadFootFrame::GetEatSpacing() const
+{
+ const SwFrameFormat * pFormat = GetFormat();
+ OSL_ENSURE(pFormat, "SwHeadFootFrame: no format?");
+
+ return pFormat->GetHeaderAndFooterEatSpacing().GetValue();
+}
+
+static void DelFlys( const SwLayoutFrame& rFrame, SwPageFrame &rPage)
+{
+ size_t i = 0;
+ while ( rPage.GetSortedObjs() &&
+ rPage.GetSortedObjs()->size() &&
+ i < rPage.GetSortedObjs()->size() )
+ {
+ SwAnchoredObject* pObj = (*rPage.GetSortedObjs())[i];
+ if (SwFlyFrame* pFlyFrame = pObj->DynCastFlyFrame())
+ {
+ if (rFrame.IsAnLower(pFlyFrame))
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+}
+
+/// Creates or removes headers
+void SwPageFrame::PrepareHeader()
+{
+ SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(Lower());
+ if ( !pLay )
+ return;
+
+ const SwFormatHeader &rH = static_cast<SwFrameFormat*>(GetDep())->GetHeader();
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bOn = !(pSh && (pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsWhitespaceHidden()));
+
+ if ( bOn && rH.IsActive() )
+ { //Implant header, but remove first, if already present
+ OSL_ENSURE( rH.GetHeaderFormat(), "FrameFormat for Header not found." );
+
+ if ( pLay->GetFormat() == rH.GetHeaderFormat() )
+ return; // Header is already the correct one.
+
+ if ( pLay->IsHeaderFrame() )
+ { SwLayoutFrame *pDel = pLay;
+ pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
+ ::DelFlys(*pDel, *this);
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ OSL_ENSURE( pLay, "Where to with the Header?" );
+ SwHeaderFrame *pH = new SwHeaderFrame( const_cast<SwFrameFormat*>(rH.GetHeaderFormat()), this );
+ pH->Paste( this, pLay );
+ if ( GetUpper() )
+ ::RegistFlys( this, pH );
+ }
+ else if (pLay->IsHeaderFrame())
+ { // Remove header if present.
+ ::DelFlys(*pLay, *this);
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+}
+
+/// Creates or removes footer
+void SwPageFrame::PrepareFooter()
+{
+ SwLayoutFrame *pLay = static_cast<SwLayoutFrame*>(Lower());
+ if ( !pLay )
+ return;
+
+ const SwFormatFooter &rF = static_cast<SwFrameFormat*>(GetDep())->GetFooter();
+ while ( pLay->GetNext() )
+ pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bOn = !(pSh && (pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsWhitespaceHidden()));
+
+ if ( bOn && rF.IsActive() )
+ { //Implant footer, but remove first, if already present
+ OSL_ENSURE( rF.GetFooterFormat(), "FrameFormat for Footer not found." );
+
+ if ( pLay->GetFormat() == rF.GetFooterFormat() )
+ return; // Footer is already the correct one.
+
+ if ( pLay->IsFooterFrame() )
+ {
+ ::DelFlys(*pLay, *this);
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+ SwFooterFrame *pF = new SwFooterFrame( const_cast<SwFrameFormat*>(rF.GetFooterFormat()), this );
+ pF->Paste( this );
+ if ( GetUpper() )
+ ::RegistFlys( this, pF );
+ }
+ else if ( pLay->IsFooterFrame() )
+ {
+ // Remove footer if already present
+ ::DelFlys(*pLay, *this);
+ SwViewShell *pShell;
+ if ( pLay->GetPrev() && nullptr != (pShell = getRootFrame()->GetCurrShell()) &&
+ pShell->VisArea().HasArea() )
+ pShell->InvalidateWindows( pShell->VisArea() );
+ pLay->Cut();
+ SwFrame::DestroyFrame(pLay);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
new file mode 100644
index 000000000..6cdfcfce0
--- /dev/null
+++ b/sw/source/core/layout/layact.cxx
@@ -0,0 +1,2419 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_feature_desktop.h>
+#include <config_wasm_strip.h>
+
+#include <ctime>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <crsrsh.hxx>
+#include <dflyobj.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <dbg_lay.hxx>
+#include <layouter.hxx>
+#include <docstat.hxx>
+#include <swevent.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <sfx2/event.hxx>
+
+#include <ftnidx.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/opaqitem.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <sal/log.hxx>
+
+#include <layact.hxx>
+#include <swwait.hxx>
+#include <fmtsrnd.hxx>
+#include <docsh.hxx>
+
+#include <anchoreddrawobject.hxx>
+#include <ndtxt.hxx>
+#include <tabfrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <flyfrms.hxx>
+#include <mdiexp.hxx>
+#include <sectfrm.hxx>
+#include <acmplwrd.hxx>
+#include <deletelistener.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <fntcache.hxx>
+#include <fmtanchr.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <vector>
+#include <tools/diagnose_ex.h>
+
+void SwLayAction::CheckWaitCursor()
+{
+ if (IsReschedule())
+ {
+ ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
+ }
+ if ( !m_pWait && IsWaitAllowed() && IsPaint() &&
+ ((std::clock() - m_nStartTicks) * 1000 / CLOCKS_PER_SEC >= CLOCKS_PER_SEC/2) )
+ {
+ m_pWait.reset( new SwWait( *m_pRoot->GetFormat()->GetDoc()->GetDocShell(), true ) );
+ }
+}
+
+// Time over already?
+inline void SwLayAction::CheckIdleEnd()
+{
+ if (!IsInterrupt())
+ m_bInterrupt = bool(GetInputType()) && Application::AnyInput(GetInputType());
+}
+
+void SwLayAction::SetStatBar( bool bNew )
+{
+ if ( bNew )
+ {
+ m_nEndPage = m_pRoot->GetPageNum();
+ m_nEndPage += m_nEndPage * 10 / 100;
+ }
+ else
+ m_nEndPage = USHRT_MAX;
+}
+
+bool SwLayAction::PaintWithoutFlys( const SwRect &rRect, const SwContentFrame *pCnt,
+ const SwPageFrame *pPage )
+{
+ SwRegionRects aTmp( rRect );
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ const SwFlyFrame *pSelfFly = pCnt->FindFlyFrame();
+
+ for ( size_t i = 0; i < rObjs.size() && !aTmp.empty(); ++i )
+ {
+ SwVirtFlyDrawObj *pVirtFly = dynamic_cast<SwVirtFlyDrawObj*>(rObjs[i]->DrawObj());
+ if ( !pVirtFly )
+ continue;
+
+ // do not consider invisible objects
+ const IDocumentDrawModelAccess& rIDDMA = pPage->GetFormat()->getIDocumentDrawModelAccess();
+ if ( !rIDDMA.IsVisibleLayerId( pVirtFly->GetLayer() ) )
+ {
+ continue;
+ }
+
+ SwFlyFrame *pFly = pVirtFly->GetFlyFrame();
+
+ if ( pFly == pSelfFly || !rRect.Overlaps( pFly->getFrameArea() ) )
+ continue;
+
+ if ( pSelfFly && pSelfFly->IsLowerOf( pFly ) )
+ continue;
+
+ if ( pFly->GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() )
+ continue;
+
+ if ( pSelfFly )
+ {
+ const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
+ if ( pVirtFly->GetLayer() == pTmp->GetLayer() )
+ {
+ if ( pVirtFly->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
+ // Only look at things above us, if inside the same layer
+ continue;
+ }
+ else
+ {
+ const bool bLowerOfSelf = pFly->IsLowerOf( pSelfFly );
+ if ( !bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue() )
+ // Things from other layers are only interesting to us if
+ // they're not transparent or lie inwards
+ continue;
+ }
+ }
+
+ // Fly frame without a lower have to be subtracted from paint region.
+ // For checking, if fly frame contains transparent graphic or
+ // has surrounded contour, assure that fly frame has a lower
+ if ( pFly->Lower() &&
+ pFly->Lower()->IsNoTextFrame() &&
+ ( static_cast<SwNoTextFrame*>(pFly->Lower())->IsTransparent() ||
+ pFly->GetFormat()->GetSurround().IsContour() )
+ )
+ {
+ continue;
+ }
+
+ // vcl::Region of a fly frame with transparent background or a transparent
+ // shadow have not to be subtracted from paint region
+ if ( pFly->IsBackgroundTransparent() )
+ {
+ continue;
+ }
+
+ aTmp -= pFly->getFrameArea();
+ }
+
+ bool bRetPaint = false;
+ for ( const auto& rRegionRect : aTmp )
+ bRetPaint |= m_pImp->GetShell()->AddPaintRect( rRegionRect );
+ return bRetPaint;
+}
+
+inline bool SwLayAction::PaintContent_( const SwContentFrame *pContent,
+ const SwPageFrame *pPage,
+ const SwRect &rRect )
+{
+ if ( rRect.HasArea() )
+ {
+ if ( pPage->GetSortedObjs() )
+ return PaintWithoutFlys( rRect, pContent, pPage );
+ else
+ return m_pImp->GetShell()->AddPaintRect( rRect );
+ }
+ return false;
+}
+
+/**
+ * Depending of the type, the Content is output according to its changes, or the area
+ * to be outputted is registered with the region, respectively.
+ */
+void SwLayAction::PaintContent( const SwContentFrame *pCnt,
+ const SwPageFrame *pPage,
+ const SwRect &rOldRect,
+ tools::Long nOldBottom )
+{
+ SwRectFnSet aRectFnSet(pCnt);
+
+ if ( pCnt->IsCompletePaint() || !pCnt->IsTextFrame() )
+ {
+ SwRect aPaint( pCnt->GetPaintArea() );
+ if ( !PaintContent_( pCnt, pPage, aPaint ) )
+ pCnt->ResetCompletePaint();
+ }
+ else
+ {
+ // paint the area between printing bottom and frame bottom and
+ // the area left and right beside the frame, if its height changed.
+ tools::Long nOldHeight = aRectFnSet.GetHeight(rOldRect);
+ tools::Long nNewHeight = aRectFnSet.GetHeight(pCnt->getFrameArea());
+ const bool bHeightDiff = nOldHeight != nNewHeight;
+ if( bHeightDiff )
+ {
+ // consider whole potential paint area.
+ SwRect aDrawRect( pCnt->GetPaintArea() );
+ if( nOldHeight > nNewHeight )
+ nOldBottom = aRectFnSet.GetPrtBottom(*pCnt);
+ aRectFnSet.SetTop( aDrawRect, nOldBottom );
+ PaintContent_( pCnt, pPage, aDrawRect );
+ }
+ // paint content area
+ SwRect aPaintRect = static_cast<SwTextFrame*>(const_cast<SwContentFrame*>(pCnt))->GetPaintSwRect();
+ PaintContent_( pCnt, pPage, aPaintRect );
+ }
+
+ if ( !pCnt->IsRetouche() || pCnt->GetNext() )
+ return;
+
+ const SwFrame *pTmp = pCnt;
+ if( pCnt->IsInSct() )
+ {
+ const SwSectionFrame* pSct = pCnt->FindSctFrame();
+ if( pSct->IsRetouche() && !pSct->GetNext() )
+ pTmp = pSct;
+ }
+ SwRect aRect( pTmp->GetUpper()->GetPaintArea() );
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTmp) );
+ if ( !PaintContent_( pCnt, pPage, aRect ) )
+ pCnt->ResetRetouche();
+}
+
+SwLayAction::SwLayAction( SwRootFrame *pRt, SwViewShellImp *pI ) :
+ m_pRoot( pRt ),
+ m_pImp( pI ),
+ m_pOptTab( nullptr ),
+ m_nPreInvaPage( USHRT_MAX ),
+ m_nStartTicks( std::clock() ),
+ m_nInputType( VclInputFlags::NONE ),
+ m_nEndPage( USHRT_MAX ),
+ m_nCheckPageNum( USHRT_MAX )
+{
+ m_bPaintExtraData = ::IsExtraData( m_pImp->GetShell()->GetDoc() );
+ m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
+ m_bInterrupt = m_bAgain = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
+ m_bUpdateExpFields = m_bBrowseActionStop = m_bActionInProgress = false;
+ // init new flag <mbFormatContentOnInterrupt>.
+ mbFormatContentOnInterrupt = false;
+
+ assert(!m_pImp->m_pLayAction); // there can be only one SwLayAction
+ m_pImp->m_pLayAction = this; // register there
+}
+
+SwLayAction::~SwLayAction()
+{
+ OSL_ENSURE( !m_pWait, "Wait object not destroyed" );
+ m_pImp->m_pLayAction = nullptr; // unregister
+}
+
+void SwLayAction::Reset()
+{
+ SetAgain(false);
+ m_pOptTab = nullptr;
+ m_nStartTicks = std::clock();
+ m_nInputType = VclInputFlags::NONE;
+ m_nEndPage = m_nPreInvaPage = m_nCheckPageNum = USHRT_MAX;
+ m_bPaint = m_bComplete = m_bWaitAllowed = m_bCheckPages = true;
+ m_bInterrupt = m_bNextCycle = m_bCalcLayout = m_bIdle = m_bReschedule =
+ m_bUpdateExpFields = m_bBrowseActionStop = false;
+}
+
+bool SwLayAction::RemoveEmptyBrowserPages()
+{
+ // switching from the normal to the browser mode, empty pages may be
+ // retained for an annoyingly long time, so delete them here
+ bool bRet = false;
+ const SwViewShell *pSh = m_pRoot->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ do
+ {
+ if ( (pPage->GetSortedObjs() && pPage->GetSortedObjs()->size()) ||
+ pPage->ContainsContent() ||
+ pPage->FindFootnoteCont() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ else
+ {
+ bRet = true;
+ SwPageFrame *pDel = pPage;
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ pDel->Cut();
+ SwFrame::DestroyFrame(pDel);
+ }
+ } while ( pPage );
+ }
+ return bRet;
+}
+
+void SwLayAction::SetAgain(bool bAgain)
+{
+ if (bAgain == m_bAgain)
+ return;
+
+ m_bAgain = bAgain;
+
+ assert(m_aFrameStack.size() == m_aFrameDeleteGuards.size());
+ size_t nCount = m_aFrameStack.size();
+ if (m_bAgain)
+ {
+ // LayAction::FormatLayout is now flagged to exit early and will avoid
+ // dereferencing any SwFrames in the stack of FormatLayouts so allow
+ // their deletion
+ for (size_t i = 0; i < nCount; ++i)
+ m_aFrameDeleteGuards[i].reset();
+ }
+ else
+ {
+ // LayAction::FormatLayout is now continue normally and will
+ // dereference the top SwFrame in the stack of m_aFrameStack as each
+ // FormatLevel returns so disallow their deletion
+ for (size_t i = 0; i < nCount; ++i)
+ m_aFrameDeleteGuards[i] = std::make_unique<SwFrameDeleteGuard>(m_aFrameStack[i]);
+ }
+}
+
+void SwLayAction::PushFormatLayout(SwFrame* pLow)
+{
+ /* Workaround crash seen in crashtesting with fdo53985-1.docx
+
+ Lock pLow against getting deleted when it will be dereferenced
+ after FormatLayout
+
+ If SetAgain is called to make SwLayAction exit early to avoid that
+ dereference, then it clears these guards
+ */
+ m_aFrameStack.push_back(pLow);
+ m_aFrameDeleteGuards.push_back(std::make_unique<SwFrameDeleteGuard>(pLow));
+}
+
+void SwLayAction::PopFormatLayout()
+{
+ m_aFrameDeleteGuards.pop_back();
+ m_aFrameStack.pop_back();
+}
+
+void SwLayAction::Action(OutputDevice* pRenderContext)
+{
+ m_bActionInProgress = true;
+
+ //TurboMode? Hands-off during idle-format
+ if ( IsPaint() && !IsIdle() && TurboAction() )
+ {
+ m_pWait.reset();
+ m_pRoot->ResetTurboFlag();
+ m_bActionInProgress = false;
+ m_pRoot->DeleteEmptySct();
+ return;
+ }
+ else if ( m_pRoot->GetTurbo() )
+ {
+ m_pRoot->DisallowTurbo();
+ const SwFrame *pFrame = m_pRoot->GetTurbo();
+ m_pRoot->ResetTurbo();
+ pFrame->InvalidatePage();
+ }
+ m_pRoot->DisallowTurbo();
+
+ if ( IsCalcLayout() )
+ SetCheckPages( false );
+
+ InternalAction(pRenderContext);
+ if (RemoveEmptyBrowserPages())
+ SetAgain(true);
+ while ( IsAgain() )
+ {
+ SetAgain(false);
+ m_bNextCycle = false;
+ InternalAction(pRenderContext);
+ if (RemoveEmptyBrowserPages())
+ SetAgain(true);
+ }
+ m_pRoot->DeleteEmptySct();
+
+ m_pWait.reset();
+
+ //Turbo-Action permitted again for all cases.
+ m_pRoot->ResetTurboFlag();
+ m_pRoot->ResetTurbo();
+
+ SetCheckPages( true );
+
+ m_bActionInProgress = false;
+}
+
+SwPageFrame* SwLayAction::CheckFirstVisPage( SwPageFrame *pPage )
+{
+ SwContentFrame *pCnt = pPage->FindFirstBodyContent();
+ SwContentFrame *pChk = pCnt;
+ bool bPageChgd = false;
+ while ( pCnt && pCnt->IsFollow() )
+ pCnt = pCnt->FindMaster();
+ if ( pCnt && pChk != pCnt )
+ { bPageChgd = true;
+ pPage = pCnt->FindPageFrame();
+ }
+
+ if ( !pPage->GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ {
+ SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
+ if ( pCont )
+ {
+ pCnt = pCont->ContainsContent();
+ pChk = pCnt;
+ while ( pCnt && pCnt->IsFollow() )
+ pCnt = static_cast<SwContentFrame*>(pCnt->FindPrev());
+ if ( pCnt && pCnt != pChk )
+ {
+ if ( bPageChgd )
+ {
+ // Use the 'topmost' page
+ SwPageFrame *pTmp = pCnt->FindPageFrame();
+ if ( pPage->GetPhyPageNum() > pTmp->GetPhyPageNum() )
+ pPage = pTmp;
+ }
+ else
+ pPage = pCnt->FindPageFrame();
+ }
+ }
+ }
+ return pPage;
+}
+
+// unlock position on start and end of page
+// layout process.
+static void unlockPositionOfObjects( SwPageFrame *pPageFrame )
+{
+ assert( pPageFrame );
+
+ SwSortedObjs* pObjs = pPageFrame->GetSortedObjs();
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pObj : *pObjs)
+ {
+ pObj->UnlockPosition();
+ }
+ }
+}
+
+void SwLayAction::InternalAction(OutputDevice* pRenderContext)
+{
+ OSL_ENSURE( m_pRoot->Lower()->IsPageFrame(), ":-( No page below the root.");
+
+ m_pRoot->Calc(pRenderContext);
+
+ // Figure out the first invalid page or the first one to be formatted,
+ // respectively. A complete-action means the first invalid page.
+ // However, the first page to be formatted might be the one having the
+ // number 1. If we're doing a fake formatting, the number of the first
+ // page is the number of the first visible page.
+ SwPageFrame *pPage = IsComplete() ? static_cast<SwPageFrame*>(m_pRoot->Lower()) :
+ m_pImp->GetFirstVisPage(pRenderContext);
+ if ( !pPage )
+ pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+
+ // If there's a first-flow-Content in the first visible page that's also a Follow,
+ // we switch the page back to the original master of that Content.
+ if ( !IsComplete() )
+ pPage = CheckFirstVisPage( pPage );
+ sal_uInt16 nFirstPageNum = pPage->GetPhyPageNum();
+
+ while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ IDocumentLayoutAccess& rLayoutAccess = m_pRoot->GetFormat()->getIDocumentLayoutAccess();
+ const bool bNoLoop = pPage && SwLayouter::StartLoopControl(m_pRoot->GetFormat()->GetDoc(), pPage);
+ sal_uInt16 nPercentPageNum = 0;
+
+ auto lcl_isLayoutLooping = [&]()
+ {
+ const bool bAgain = this->IsAgain();
+ if (bAgain && bNoLoop)
+ rLayoutAccess.GetLayouter()->EndLoopControl();
+ return bAgain;
+ };
+
+ while ( (pPage && !IsInterrupt()) || m_nCheckPageNum != USHRT_MAX )
+ {
+ // note: this is the only place that consumes and resets m_nCheckPageNum
+ if ((IsInterrupt() || !pPage) && m_nCheckPageNum != USHRT_MAX)
+ {
+ if (!pPage || m_nCheckPageNum < pPage->GetPhyPageNum())
+ {
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ while (pPg && pPg->GetPhyPageNum() < m_nCheckPageNum)
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ if (pPg)
+ pPage = pPg;
+ if (!pPage)
+ break;
+ }
+
+ SwPageFrame *pTmp = pPage->GetPrev() ?
+ static_cast<SwPageFrame*>(pPage->GetPrev()) : pPage;
+ SetCheckPages( true );
+ SwFrame::CheckPageDescs( pPage, true, &pTmp );
+ SetCheckPages( false );
+ m_nCheckPageNum = USHRT_MAX;
+ pPage = pTmp;
+ continue;
+ }
+
+ if ( m_nEndPage != USHRT_MAX && pPage->GetPhyPageNum() > nPercentPageNum )
+ {
+ nPercentPageNum = pPage->GetPhyPageNum();
+ ::SetProgressState( nPercentPageNum, m_pImp->GetShell()->GetDoc()->GetDocShell());
+ }
+ m_pOptTab = nullptr;
+
+ // No Shortcut for Idle or CalcLayout
+ const bool bTakeShortcut = !IsIdle() && !IsComplete() && IsShortCut(pPage);
+
+ m_pRoot->DeleteEmptySct();
+ if (lcl_isLayoutLooping()) return;
+
+ if (!bTakeShortcut)
+ {
+ while ( !IsInterrupt() && !IsNextCycle() &&
+ ((pPage->GetSortedObjs() && pPage->IsInvalidFly()) || pPage->IsInvalid()) )
+ {
+ unlockPositionOfObjects( pPage );
+
+ SwObjectFormatter::FormatObjsAtFrame( *pPage, *pPage, this );
+ if ( !pPage->GetSortedObjs() )
+ {
+ // If there are no (more) Flys, the flags are superfluous.
+ pPage->ValidateFlyLayout();
+ pPage->ValidateFlyContent();
+ }
+ // change condition
+ while ( !IsInterrupt() && !IsNextCycle() &&
+ ( pPage->IsInvalid() ||
+ (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
+ {
+ PROTOCOL( pPage, PROT::FileInit, DbgAction::NONE, nullptr)
+ if (lcl_isLayoutLooping()) return;
+
+ // new loop control
+ int nLoopControlRuns_1 = 0;
+ const int nLoopControlMax = 20;
+
+ while ( !IsNextCycle() && pPage->IsInvalidLayout() )
+ {
+ pPage->ValidateLayout();
+
+ if ( ++nLoopControlRuns_1 > nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl_1 in SwLayAction::InternalAction" );
+ break;
+ }
+
+ FormatLayout( pRenderContext, pPage );
+ if (lcl_isLayoutLooping()) return;
+ }
+ // change condition
+ if ( !IsNextCycle() &&
+ ( pPage->IsInvalidContent() ||
+ (pPage->GetSortedObjs() && pPage->IsInvalidFly()) ) )
+ {
+ pPage->ValidateFlyInCnt();
+ pPage->ValidateContent();
+ pPage->ValidateFlyLayout();
+ pPage->ValidateFlyContent();
+ if ( !FormatContent( pPage ) )
+ {
+ if (lcl_isLayoutLooping()) return;
+ pPage->InvalidateContent();
+ pPage->InvalidateFlyInCnt();
+ pPage->InvalidateFlyLayout();
+ pPage->InvalidateFlyContent();
+ if ( IsBrowseActionStop() )
+ m_bInterrupt = true;
+ }
+ }
+ if( bNoLoop )
+ rLayoutAccess.GetLayouter()->LoopControl( pPage );
+ }
+
+ unlockPositionOfObjects( pPage );
+ }
+
+ // A previous page may be invalid again.
+ if (lcl_isLayoutLooping()) return;
+ if ( !pPage->GetSortedObjs() )
+ {
+ // If there are no (more) Flys, the flags are superfluous.
+ pPage->ValidateFlyLayout();
+ pPage->ValidateFlyContent();
+ }
+ if ( !IsInterrupt() )
+ {
+ SetNextCycle( false );
+
+ if ( m_nPreInvaPage != USHRT_MAX )
+ {
+ if( !IsComplete() && m_nPreInvaPage + 2 < nFirstPageNum )
+ {
+ m_pImp->SetFirstVisPageInvalid();
+ SwPageFrame *pTmpPage = m_pImp->GetFirstVisPage(pRenderContext);
+ nFirstPageNum = pTmpPage->GetPhyPageNum();
+ if( m_nPreInvaPage < nFirstPageNum )
+ {
+ m_nPreInvaPage = nFirstPageNum;
+ pPage = pTmpPage;
+ }
+ }
+ while ( pPage->GetPrev() && pPage->GetPhyPageNum() > m_nPreInvaPage )
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ m_nPreInvaPage = USHRT_MAX;
+ }
+
+ while ( pPage->GetPrev() &&
+ ( static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalid() ||
+ ( static_cast<SwPageFrame*>(pPage->GetPrev())->GetSortedObjs() &&
+ static_cast<SwPageFrame*>(pPage->GetPrev())->IsInvalidFly())) &&
+ (static_cast<SwPageFrame*>(pPage->GetPrev())->GetPhyPageNum() >=
+ nFirstPageNum) )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ }
+
+ // Continue to the next invalid page
+ while ( pPage && !pPage->IsInvalid() &&
+ (!pPage->GetSortedObjs() || !pPage->IsInvalidFly()) )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ if( bNoLoop )
+ rLayoutAccess.GetLayouter()->LoopControl( pPage );
+ }
+ CheckIdleEnd();
+ }
+
+ if ((bTakeShortcut || !pPage) && !IsInterrupt() &&
+ (m_pRoot->IsSuperfluous() || m_pRoot->IsAssertFlyPages()) )
+ {
+ // tdf#139426 allow suppression of AssertFlyPages
+ if ( m_pRoot->IsAssertFlyPages() && !m_pRoot->IsTableUpdateInProgress())
+ {
+ m_pRoot->AssertFlyPages();
+ }
+ if ( m_pRoot->IsSuperfluous() )
+ {
+ bool bOld = IsAgain();
+ m_pRoot->RemoveSuperfluous();
+ SetAgain(bOld);
+ }
+ if (lcl_isLayoutLooping()) return;
+ pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ while ( pPage && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ while ( pPage && pPage->GetNext() &&
+ pPage->GetPhyPageNum() < nFirstPageNum )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ else if (bTakeShortcut)
+ break;
+ }
+ if ( IsInterrupt() && pPage )
+ {
+ // If we have input, we don't want to format content anymore, but
+ // we still should clean the layout.
+ // Otherwise, the following situation might arise:
+ // The user enters some text at the end of the paragraph of the last
+ // page, causing the paragraph to create a Follow for the next page.
+ // Meanwhile the user continues typing, so we have input while
+ // still formatting.
+ // The paragraph on the new page has already been partially formatted,
+ // and the new page has been fully formatted and is set to CompletePaint,
+ // but hasn't added itself to the area to be output. Then we paint,
+ // the CompletePaint of the page is reset because the new paragraph
+ // already added itself, but the borders of the page haven't been painted
+ // yet.
+ // Oh well, with the inevitable following LayAction, the page doesn't
+ // register itself, because it's (LayoutFrame) flags have been reset
+ // already - the border of the page will never be painted.
+ SwPageFrame *pPg = pPage;
+ if (lcl_isLayoutLooping()) return;
+ const SwRect &rVis = m_pImp->GetShell()->VisArea();
+
+ while( pPg && pPg->getFrameArea().Bottom() < rVis.Top() )
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ if( pPg != pPage )
+ pPg = pPg ? static_cast<SwPageFrame*>(pPg->GetPrev()) : pPage;
+
+ // set flag for interrupt content formatting
+ mbFormatContentOnInterrupt = true;
+ tools::Long nBottom = rVis.Bottom();
+ // #i42586# - format current page, if idle action is active
+ // This is an optimization for the case that the interrupt is created by
+ // the move of a form control object, which is represented by a window.
+ while ( pPg && ( pPg->getFrameArea().Top() < nBottom ||
+ ( IsIdle() && pPg == pPage ) ) )
+ {
+ unlockPositionOfObjects( pPg );
+
+ if (lcl_isLayoutLooping()) return;
+
+ // new loop control
+ int nLoopControlRuns_2 = 0;
+ const int nLoopControlMax = 20;
+
+ // special case: interrupt content formatting
+ // conditions are incorrect and are too strict.
+ // adjust interrupt formatting to normal page formatting - see above.
+ while ( ( mbFormatContentOnInterrupt &&
+ ( pPg->IsInvalid() ||
+ ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) ) ||
+ ( !mbFormatContentOnInterrupt && pPg->IsInvalidLayout() ) )
+ {
+ if (lcl_isLayoutLooping()) return;
+ // format also at-page anchored objects
+ SwObjectFormatter::FormatObjsAtFrame( *pPg, *pPg, this );
+ if ( !pPg->GetSortedObjs() )
+ {
+ pPg->ValidateFlyLayout();
+ pPg->ValidateFlyContent();
+ }
+
+ // new loop control
+ int nLoopControlRuns_3 = 0;
+
+ while ( pPg->IsInvalidLayout() )
+ {
+ pPg->ValidateLayout();
+
+ if ( ++nLoopControlRuns_3 > nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl_3 in Interrupt formatting in SwLayAction::InternalAction" );
+ break;
+ }
+
+ FormatLayout( pRenderContext, pPg );
+ if (lcl_isLayoutLooping()) return;
+ }
+
+ if ( mbFormatContentOnInterrupt &&
+ ( pPg->IsInvalidContent() ||
+ ( pPg->GetSortedObjs() && pPg->IsInvalidFly() ) ) )
+ {
+ pPg->ValidateFlyInCnt();
+ pPg->ValidateContent();
+ pPg->ValidateFlyLayout();
+ pPg->ValidateFlyContent();
+
+ if ( ++nLoopControlRuns_2 > nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl_2 in Interrupt formatting in SwLayAction::InternalAction" );
+ break;
+ }
+
+ if ( !FormatContent( pPg ) )
+ {
+ if (lcl_isLayoutLooping()) return;
+ pPg->InvalidateContent();
+ pPg->InvalidateFlyInCnt();
+ pPg->InvalidateFlyLayout();
+ pPg->InvalidateFlyContent();
+ }
+ // we are satisfied if the content is formatted once complete.
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ unlockPositionOfObjects( pPg );
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+ // reset flag for special interrupt content formatting.
+ mbFormatContentOnInterrupt = false;
+ }
+ m_pOptTab = nullptr;
+ if( bNoLoop )
+ rLayoutAccess.GetLayouter()->EndLoopControl();
+}
+
+bool SwLayAction::TurboAction_( const SwContentFrame *pCnt )
+{
+
+ const SwPageFrame *pPage = nullptr;
+ if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() || pCnt->IsRetouche() )
+ {
+ const SwRect aOldRect( pCnt->UnionFrame( true ) );
+ const tools::Long nOldBottom = pCnt->getFrameArea().Top() + pCnt->getFramePrintArea().Bottom();
+ pCnt->Calc(m_pImp->GetShell()->GetOut());
+ if ( pCnt->getFrameArea().Bottom() < aOldRect.Bottom() )
+ pCnt->SetRetouche();
+
+ pPage = pCnt->FindPageFrame();
+ PaintContent( pCnt, pPage, aOldRect, nOldBottom );
+
+ if ( !pCnt->GetValidLineNumFlag() && pCnt->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pCnt)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCnt))->RecalcAllLines();
+ if ( nAllLines != static_cast<const SwTextFrame*>(pCnt)->GetAllLines() )
+ {
+ if ( IsPaintExtraData() )
+ m_pImp->GetShell()->AddPaintRect( pCnt->getFrameArea() );
+ // This is to calculate the remaining LineNums on the page,
+ // and we don't stop processing here. To perform this inside RecalcAllLines
+ // would be expensive, because we would have to notify the page even
+ // in unnecessary cases (normal actions).
+ const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
+ while ( pNxt &&
+ (pNxt->IsInTab() || pNxt->IsInDocBody() != pCnt->IsInDocBody()) )
+ pNxt = pNxt->GetNextContentFrame();
+ if ( pNxt )
+ pNxt->InvalidatePage();
+ }
+ return false;
+ }
+
+ if ( pPage->IsInvalidLayout() || (pPage->GetSortedObjs() && pPage->IsInvalidFly()) )
+ return false;
+ }
+ if ( !pPage )
+ pPage = pCnt->FindPageFrame();
+
+ // format floating screen objects at content frame.
+ if ( pCnt->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pCnt),
+ *pPage, this ) )
+ {
+ return false;
+ }
+
+ if ( pPage->IsInvalidContent() )
+ return false;
+ return true;
+}
+
+bool SwLayAction::TurboAction()
+{
+ bool bRet = true;
+
+ if ( m_pRoot->GetTurbo() )
+ {
+ if ( !TurboAction_( m_pRoot->GetTurbo() ) )
+ {
+ CheckIdleEnd();
+ bRet = false;
+ }
+ m_pRoot->ResetTurbo();
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+static bool lcl_IsInvaLay( const SwFrame *pFrame, tools::Long nBottom )
+{
+ return !pFrame->isFrameAreaDefinitionValid() ||
+ (pFrame->IsCompletePaint() && ( pFrame->getFrameArea().Top() < nBottom ) );
+}
+
+static const SwFrame *lcl_FindFirstInvaLay( const SwFrame *pFrame, tools::Long nBottom )
+{
+ OSL_ENSURE( pFrame->IsLayoutFrame(), "FindFirstInvaLay, no LayFrame" );
+
+ if (lcl_IsInvaLay(pFrame, nBottom))
+ return pFrame;
+ pFrame = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ {
+ if (lcl_IsInvaLay(pFrame, nBottom))
+ return pFrame;
+ const SwFrame *pTmp = lcl_FindFirstInvaLay( pFrame, nBottom );
+ if ( nullptr != pTmp )
+ return pTmp;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ return nullptr;
+}
+
+static const SwFrame *lcl_FindFirstInvaContent( const SwLayoutFrame *pLay, tools::Long nBottom,
+ const SwContentFrame *pFirst )
+{
+ const SwContentFrame *pCnt = pFirst ? pFirst->GetNextContentFrame() :
+ pLay->ContainsContent();
+ while ( pCnt )
+ {
+ if ( !pCnt->isFrameAreaDefinitionValid() || pCnt->IsCompletePaint() )
+ {
+ if ( pCnt->getFrameArea().Top() <= nBottom )
+ return pCnt;
+ }
+
+ if ( pCnt->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ if ( static_cast<const SwFlyInContentFrame*>(pFly)->IsInvalid() ||
+ pFly->IsCompletePaint() )
+ {
+ if ( pFly->getFrameArea().Top() <= nBottom )
+ return pFly;
+ }
+ const SwFrame *pFrame = lcl_FindFirstInvaContent( pFly, nBottom, nullptr );
+ if ( pFrame && pFrame->getFrameArea().Bottom() <= nBottom )
+ return pFrame;
+ }
+ }
+ }
+ }
+ if ( pCnt->getFrameArea().Top() > nBottom && !pCnt->IsInTab() )
+ return nullptr;
+ pCnt = pCnt->GetNextContentFrame();
+ if ( !pLay->IsAnLower( pCnt ) )
+ break;
+ }
+ return nullptr;
+}
+
+// consider drawing objects
+static const SwAnchoredObject* lcl_FindFirstInvaObj( const SwPageFrame* _pPage,
+ tools::Long _nBottom )
+{
+ OSL_ENSURE( _pPage->GetSortedObjs(), "FindFirstInvaObj, no Objs" );
+
+ for (SwAnchoredObject* pObj : *_pPage->GetSortedObjs())
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->getFrameArea().Top() <= _nBottom )
+ {
+ if ( pFly->IsInvalid() || pFly->IsCompletePaint() )
+ return pFly;
+
+ const SwFrame* pTmp;
+ if ( nullptr != (pTmp = lcl_FindFirstInvaContent( pFly, _nBottom, nullptr )) &&
+ pTmp->getFrameArea().Top() <= _nBottom )
+ return pFly;
+ }
+ }
+ else if ( auto pDrawObject = dynamic_cast< const SwAnchoredDrawObject *>( pObj ) )
+ {
+ if ( !pDrawObject->IsValidPos() )
+ {
+ return pObj;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/* Returns True if the page lies directly below or right of the visible area.
+ *
+ * It's possible for things to change in such a way that the processing
+ * (of the caller!) has to continue with the predecessor of the passed page.
+ * The parameter might therefore get modified!
+ * For BrowseMode, you may even activate the ShortCut if the invalid content
+ * of the page lies below the visible area.
+ */
+bool SwLayAction::IsShortCut( SwPageFrame *&prPage )
+{
+ vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
+ bool bRet = false;
+ const SwViewShell *pSh = m_pRoot->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+
+ // If the page is not valid, we quickly format it, otherwise
+ // there's gonna be no end of trouble
+ if ( !prPage->isFrameAreaDefinitionValid() )
+ {
+ if ( bBrowse )
+ {
+ // format complete page
+ // Thus, loop on all lowers of the page <prPage>, instead of only
+ // format its first lower.
+ // NOTE: In online layout (bBrowse == true) a page can contain
+ // a header frame and/or a footer frame beside the body frame.
+ prPage->Calc(pRenderContext);
+ SwFrame* pPageLowerFrame = prPage->Lower();
+ while ( pPageLowerFrame )
+ {
+ pPageLowerFrame->Calc(pRenderContext);
+ pPageLowerFrame = pPageLowerFrame->GetNext();
+ }
+ }
+ else
+ FormatLayout( pSh ? pSh->GetOut() : nullptr, prPage );
+ if ( IsAgain() )
+ return false;
+ }
+
+ const SwRect &rVis = m_pImp->GetShell()->VisArea();
+ if ( (prPage->getFrameArea().Top() >= rVis.Bottom()) ||
+ (prPage->getFrameArea().Left()>= rVis.Right()) )
+ {
+ bRet = true;
+
+ // This is going to be a bit nasty: The first ContentFrame of this
+ // page in the Body text needs formatting; if it changes the page during
+ // that process, I need to start over a page further back, because we
+ // have been processing a PageBreak.
+ // Even more uncomfortable: The next ContentFrame must be formatted,
+ // because it's possible for empty pages to exist temporarily (for example
+ // a paragraph across multiple pages gets deleted or reduced in size).
+
+ // This is irrelevant for the browser, if the last Cnt above it
+ // isn't visible anymore.
+
+ const SwPageFrame *p2ndPage = prPage;
+ const SwContentFrame *pContent;
+ const SwLayoutFrame* pBody = p2ndPage->FindBodyCont();
+ if( p2ndPage->IsFootnotePage() && pBody )
+ pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
+ pContent = pBody ? pBody->ContainsContent() : nullptr;
+ while ( p2ndPage && !pContent )
+ {
+ p2ndPage = static_cast<const SwPageFrame*>(p2ndPage->GetNext());
+ if( p2ndPage )
+ {
+ pBody = p2ndPage->FindBodyCont();
+ if( p2ndPage->IsFootnotePage() && pBody )
+ pBody = static_cast<const SwLayoutFrame*>(pBody->GetNext());
+ pContent = pBody ? pBody->ContainsContent() : nullptr;
+ }
+ }
+ if ( pContent )
+ {
+ bool bTstCnt = true;
+ if ( bBrowse )
+ {
+ // Is the Cnt before already invisible?
+ const SwFrame *pLst = pContent;
+ if ( pLst->IsInTab() )
+ pLst = pContent->FindTabFrame();
+ if ( pLst->IsInSct() )
+ pLst = pContent->FindSctFrame();
+ pLst = pLst->FindPrev();
+ if ( pLst &&
+ (pLst->getFrameArea().Top() >= rVis.Bottom() ||
+ pLst->getFrameArea().Left()>= rVis.Right()) )
+ {
+ bTstCnt = false;
+ }
+ }
+
+ if ( bTstCnt )
+ {
+ // check after each frame calculation,
+ // if the content frame has changed the page. If yes, no other
+ // frame calculation is performed
+ bool bPageChg = false;
+
+ if ( pContent->IsInSct() )
+ {
+ const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
+ if ( !pSct->isFrameAreaDefinitionValid() )
+ {
+ pSct->Calc(pRenderContext);
+ pSct->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+ }
+
+ if ( !bPageChg && !pContent->isFrameAreaDefinitionValid() )
+ {
+ pContent->Calc(pRenderContext);
+ pContent->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+
+ if ( !bPageChg && pContent->IsInTab() )
+ {
+ const SwTabFrame *pTab = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindTabFrame();
+ if ( !pTab->isFrameAreaDefinitionValid() )
+ {
+ pTab->Calc(pRenderContext);
+ pTab->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+ }
+
+ if ( !bPageChg && pContent->IsInSct() )
+ {
+ const SwSectionFrame *pSct = const_cast<SwFrame*>(static_cast<SwFrame const *>(pContent))->ImplFindSctFrame();
+ if ( !pSct->isFrameAreaDefinitionValid() )
+ {
+ pSct->Calc(pRenderContext);
+ pSct->SetCompletePaint();
+ if ( IsAgain() )
+ return false;
+
+ bPageChg = pContent->FindPageFrame() != p2ndPage &&
+ prPage->GetPrev();
+ }
+ }
+
+ if ( bPageChg )
+ {
+ bRet = false;
+ const SwPageFrame* pTmp = pContent->FindPageFrame();
+ if ( pTmp->GetPhyPageNum() < prPage->GetPhyPageNum() &&
+ pTmp->IsInvalid() )
+ {
+ prPage = const_cast<SwPageFrame*>(pTmp);
+ }
+ else
+ {
+ prPage = static_cast<SwPageFrame*>(prPage->GetPrev());
+ }
+ }
+ // no shortcut, if at previous page
+ // an anchored object is registered, whose anchor is <pContent>.
+ else if ( prPage->GetPrev() )
+ {
+ SwSortedObjs* pObjs =
+ static_cast<SwPageFrame*>(prPage->GetPrev())->GetSortedObjs();
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pObj : *pObjs)
+ {
+ if ( pObj->GetAnchorFrameContainingAnchPos() == pContent )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bRet && bBrowse )
+ {
+ const tools::Long nBottom = rVis.Bottom();
+ const SwAnchoredObject* pObj( nullptr );
+ if ( prPage->GetSortedObjs() &&
+ (prPage->IsInvalidFlyLayout() || prPage->IsInvalidFlyContent()) &&
+ nullptr != (pObj = lcl_FindFirstInvaObj( prPage, nBottom )) &&
+ pObj->GetObjRect().Top() <= nBottom )
+ {
+ return false;
+ }
+ const SwFrame* pFrame( nullptr );
+ if ( prPage->IsInvalidLayout() &&
+ nullptr != (pFrame = lcl_FindFirstInvaLay( prPage, nBottom )) &&
+ pFrame->getFrameArea().Top() <= nBottom )
+ {
+ return false;
+ }
+ if ( (prPage->IsInvalidContent() || prPage->IsInvalidFlyInCnt()) &&
+ nullptr != (pFrame = lcl_FindFirstInvaContent( prPage, nBottom, nullptr )) &&
+ pFrame->getFrameArea().Top() <= nBottom )
+ {
+ return false;
+ }
+ bRet = true;
+ }
+ return bRet;
+}
+
+// introduce support for vertical layout
+bool SwLayAction::FormatLayout( OutputDevice *pRenderContext, SwLayoutFrame *pLay, bool bAddRect )
+{
+ OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
+ if ( IsAgain() )
+ return false;
+
+ bool bChanged = false;
+ bool bAlreadyPainted = false;
+ // remember frame at complete paint
+ SwRect aFrameAtCompletePaint;
+
+ if ( !pLay->isFrameAreaDefinitionValid() || pLay->IsCompletePaint() )
+ {
+ if ( pLay->GetPrev() && !pLay->GetPrev()->isFrameAreaDefinitionValid() )
+ pLay->GetPrev()->SetCompletePaint();
+
+ SwRect aOldFrame( pLay->getFrameArea() );
+ SwRect aOldRect( aOldFrame );
+ if( pLay->IsPageFrame() )
+ {
+ aOldRect = static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext);
+ }
+
+ {
+ SwFrameDeleteGuard aDeleteGuard(pLay);
+ pLay->Calc(pRenderContext);
+ }
+
+ if ( aOldFrame != pLay->getFrameArea() )
+ bChanged = true;
+
+ bool bNoPaint = false;
+ if ( pLay->IsPageBodyFrame() &&
+ pLay->getFrameArea().Pos() == aOldRect.Pos() &&
+ pLay->Lower() )
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ // Limitations because of headers / footers
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() &&
+ !( pLay->IsCompletePaint() && pLay->FindPageFrame()->FindFootnoteCont() ) )
+ bNoPaint = true;
+ }
+
+ if ( !bNoPaint && IsPaint() && bAddRect && (pLay->IsCompletePaint() || bChanged) )
+ {
+ SwRect aPaint( pLay->getFrameArea() );
+ // consider border and shadow for
+ // page frames -> enlarge paint rectangle correspondingly.
+ if ( pLay->IsPageFrame() )
+ {
+ SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
+ aPaint = pPageFrame->GetBoundRect(pRenderContext);
+ }
+
+ bool bPageInBrowseMode = pLay->IsPageFrame();
+ if( bPageInBrowseMode )
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ if( !pSh || !pSh->GetViewOptions()->getBrowseMode() )
+ bPageInBrowseMode = false;
+ }
+ if( bPageInBrowseMode )
+ {
+ // NOTE: no vertical layout in online layout
+ // Is the change even visible?
+ if ( pLay->IsCompletePaint() )
+ {
+ m_pImp->GetShell()->AddPaintRect( aPaint );
+ bAddRect = false;
+ }
+ else
+ {
+ SwRegionRects aRegion( aOldRect );
+ aRegion -= aPaint;
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ m_pImp->GetShell()->AddPaintRect( aRegion[i] );
+ aRegion.ChangeOrigin( aPaint );
+ aRegion.clear();
+ aRegion.push_back( aPaint );
+ aRegion -= aOldRect;
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ m_pImp->GetShell()->AddPaintRect( aRegion[i] );
+ }
+ }
+ else
+ {
+ m_pImp->GetShell()->AddPaintRect( aPaint );
+ bAlreadyPainted = true;
+ // remember frame at complete paint
+ aFrameAtCompletePaint = pLay->getFrameArea();
+ }
+
+ // provide paint of spacing
+ // between pages (not only for in online mode).
+ if ( pLay->IsPageFrame() )
+ {
+ const SwViewShell *pSh = pLay->getRootFrame()->GetCurrShell();
+ const SwTwips nHalfDocBorder = pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
+ : SwViewOption::defGapBetweenPages;
+ const bool bLeftToRightViewLayout = m_pRoot->IsLeftToRightViewLayout();
+ const bool bPrev = bLeftToRightViewLayout ? pLay->GetPrev() : pLay->GetNext();
+ const bool bNext = bLeftToRightViewLayout ? pLay->GetNext() : pLay->GetPrev();
+ SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(pLay);
+ SwRect aPageRect( pLay->getFrameArea() );
+
+ if(pSh)
+ {
+ SwPageFrame::GetBorderAndShadowBoundRect(aPageRect, pSh,
+ pRenderContext,
+ aPageRect, pPageFrame->IsLeftShadowNeeded(), pPageFrame->IsRightShadowNeeded(),
+ pPageFrame->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
+ }
+
+ if ( bPrev )
+ {
+ // top
+ SwRect aSpaceToPrevPage( aPageRect );
+ aSpaceToPrevPage.Top( aSpaceToPrevPage.Top() - nHalfDocBorder );
+ aSpaceToPrevPage.Bottom( pLay->getFrameArea().Top() );
+ if(!aSpaceToPrevPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
+
+ // left
+ aSpaceToPrevPage = aPageRect;
+ aSpaceToPrevPage.Left( aSpaceToPrevPage.Left() - nHalfDocBorder );
+ aSpaceToPrevPage.Right( pLay->getFrameArea().Left() );
+ if(!aSpaceToPrevPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToPrevPage );
+ }
+ if ( bNext )
+ {
+ // bottom
+ SwRect aSpaceToNextPage( aPageRect );
+ aSpaceToNextPage.Bottom( aSpaceToNextPage.Bottom() + nHalfDocBorder );
+ aSpaceToNextPage.Top( pLay->getFrameArea().Bottom() );
+ if(!aSpaceToNextPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
+
+ // right
+ aSpaceToNextPage = aPageRect;
+ aSpaceToNextPage.Right( aSpaceToNextPage.Right() + nHalfDocBorder );
+ aSpaceToNextPage.Left( pLay->getFrameArea().Right() );
+ if(!aSpaceToNextPage.IsEmpty())
+ m_pImp->GetShell()->AddPaintRect( aSpaceToNextPage );
+ }
+ }
+ }
+ pLay->ResetCompletePaint();
+ }
+
+ if ( IsPaint() && bAddRect &&
+ !pLay->GetNext() && pLay->IsRetoucheFrame() && pLay->IsRetouche() )
+ {
+ // vertical layout support
+ SwRectFnSet aRectFnSet(pLay);
+ SwRect aRect( pLay->GetUpper()->GetPaintArea() );
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pLay) );
+ if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
+ pLay->ResetRetouche();
+ }
+
+ if( bAlreadyPainted )
+ bAddRect = false;
+
+ CheckWaitCursor();
+
+ if ( IsAgain() )
+ return false;
+
+ // Now, deal with the lowers that are LayoutFrames
+
+ if ( pLay->IsFootnoteFrame() ) // no LayFrames as Lower
+ return bChanged;
+
+ SwFrame *pLow = pLay->Lower();
+ bool bTabChanged = false;
+ while ( pLow && pLow->GetUpper() == pLay )
+ {
+ SwFrame* pNext = nullptr;
+ if ( pLow->IsLayoutFrame() )
+ {
+ if ( pLow->IsTabFrame() )
+ {
+ // Remember what was the next of the lower. Formatting may move it to the previous
+ // page, in which case it looses its next.
+ pNext = pLow->GetNext();
+
+ if (pNext && pNext->IsTabFrame())
+ {
+ auto pTab = static_cast<SwTabFrame*>(pNext);
+ if (pTab->IsFollow())
+ {
+ // The next frame is a follow of the previous frame, SwTabFrame::Join() will
+ // delete this one as part of formatting, so forget about it.
+ pNext = nullptr;
+ }
+ }
+
+ bTabChanged |= FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
+ }
+ // Skip the ones already registered for deletion
+ else if( !pLow->IsSctFrame() || static_cast<SwSectionFrame*>(pLow)->GetSection() )
+ {
+ PushFormatLayout(pLow);
+ bChanged |= FormatLayout( pRenderContext, static_cast<SwLayoutFrame*>(pLow), bAddRect );
+ PopFormatLayout();
+ }
+ }
+ else if ( m_pImp->GetShell()->IsPaintLocked() )
+ // Shortcut to minimize the cycles. With Lock, the
+ // paint is coming either way (primarily for browse)
+ pLow->OptCalc();
+
+ if ( IsAgain() )
+ return false;
+ if (!pNext)
+ {
+ pNext = pLow->GetNext();
+ }
+ pLow = pNext;
+ }
+ // add complete frame area as paint area, if frame
+ // area has been already added and after formatting its lowers the frame area
+ // is enlarged.
+ SwRect aBoundRect(pLay->IsPageFrame() ? static_cast<SwPageFrame*>(pLay)->GetBoundRect(pRenderContext) : pLay->getFrameArea() );
+
+ if ( bAlreadyPainted &&
+ ( aBoundRect.Width() > aFrameAtCompletePaint.Width() ||
+ aBoundRect.Height() > aFrameAtCompletePaint.Height() )
+ )
+ {
+ m_pImp->GetShell()->AddPaintRect( aBoundRect );
+ }
+ return bChanged || bTabChanged;
+}
+
+void SwLayAction::FormatLayoutFly( SwFlyFrame* pFly )
+{
+ vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
+ OSL_ENSURE( !IsAgain(), "Attention to the invalid page." );
+ if ( IsAgain() )
+ return;
+
+ bool bChanged = false;
+ bool bAddRect = true;
+
+ if ( !pFly->isFrameAreaDefinitionValid() || pFly->IsCompletePaint() || pFly->IsInvalid() )
+ {
+ // The Frame has changed, now it's getting formatted.
+ const SwRect aOldRect( pFly->getFrameArea() );
+ pFly->Calc(pRenderContext);
+ bChanged = aOldRect != pFly->getFrameArea();
+
+ if ( IsPaint() && (pFly->IsCompletePaint() || bChanged) &&
+ pFly->getFrameArea().Top() > 0 && pFly->getFrameArea().Left() > 0 )
+ m_pImp->GetShell()->AddPaintRect( pFly->getFrameArea() );
+
+ if ( bChanged )
+ pFly->Invalidate();
+ else
+ pFly->Validate();
+
+ bAddRect = false;
+ pFly->ResetCompletePaint();
+ }
+
+ if ( IsAgain() )
+ return;
+
+ // Now, deal with the lowers that are LayoutFrames
+ SwFrame *pLow = pFly->Lower();
+ while ( pLow )
+ {
+ if ( pLow->IsLayoutFrame() )
+ {
+ if ( pLow->IsTabFrame() )
+ FormatLayoutTab( static_cast<SwTabFrame*>(pLow), bAddRect );
+ else
+ FormatLayout( m_pImp->GetShell()->GetOut(), static_cast<SwLayoutFrame*>(pLow), bAddRect );
+ }
+ pLow = pLow->GetNext();
+ }
+}
+
+// Implement vertical layout support
+bool SwLayAction::FormatLayoutTab( SwTabFrame *pTab, bool bAddRect )
+{
+ OSL_ENSURE( !IsAgain(), "8-) Attention to the invalid page." );
+ if ( IsAgain() || !pTab->Lower() )
+ return false;
+
+ vcl::RenderContext* pRenderContext = m_pImp->GetShell()->GetOut();
+ IDocumentTimerAccess& rTimerAccess = m_pRoot->GetFormat()->getIDocumentTimerAccess();
+ rTimerAccess.BlockIdling();
+
+ bool bChanged = false;
+ bool bPainted = false;
+
+ const SwPageFrame *pOldPage = pTab->FindPageFrame();
+
+ // vertical layout support
+ SwRectFnSet aRectFnSet(pTab);
+
+ if ( !pTab->isFrameAreaDefinitionValid() || pTab->IsCompletePaint() || pTab->IsComplete() )
+ {
+ if ( pTab->GetPrev() && !pTab->GetPrev()->isFrameAreaDefinitionValid() )
+ {
+ pTab->GetPrev()->SetCompletePaint();
+ }
+
+ const SwRect aOldRect( pTab->getFrameArea() );
+ pTab->SetLowersFormatted( false );
+ pTab->Calc(pRenderContext);
+ if ( aOldRect != pTab->getFrameArea() )
+ {
+ bChanged = true;
+ }
+ const SwRect aPaintFrame = pTab->GetPaintArea();
+
+ if ( IsPaint() && bAddRect )
+ {
+ // add condition <pTab->getFrameArea().HasArea()>
+ if ( !pTab->IsCompletePaint() &&
+ pTab->IsComplete() &&
+ ( pTab->getFrameArea().SSize() != pTab->getFramePrintArea().SSize() ||
+ // vertical layout support
+ aRectFnSet.GetLeftMargin(*pTab) ) &&
+ pTab->getFrameArea().HasArea()
+ )
+ {
+ // re-implement calculation of margin rectangles.
+ SwRect aMarginRect;
+
+ SwTwips nLeftMargin = aRectFnSet.GetLeftMargin(*pTab);
+ if ( nLeftMargin > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetWidth( aMarginRect, nLeftMargin );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+
+ if ( aRectFnSet.GetRightMargin(*pTab) > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetLeft( aMarginRect, aRectFnSet.GetPrtRight(*pTab) );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+
+ SwTwips nTopMargin = aRectFnSet.GetTopMargin(*pTab);
+ if ( nTopMargin > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetHeight( aMarginRect, nTopMargin );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+
+ if ( aRectFnSet.GetBottomMargin(*pTab) > 0)
+ {
+ aMarginRect = pTab->getFrameArea();
+ aRectFnSet.SetTop( aMarginRect, aRectFnSet.GetPrtBottom(*pTab) );
+ m_pImp->GetShell()->AddPaintRect( aMarginRect );
+ }
+ }
+ else if ( pTab->IsCompletePaint() )
+ {
+ m_pImp->GetShell()->AddPaintRect( aPaintFrame );
+ bAddRect = false;
+ bPainted = true;
+ }
+
+ if ( pTab->IsRetouche() && !pTab->GetNext() )
+ {
+ SwRect aRect( pTab->GetUpper()->GetPaintArea() );
+ // vertical layout support
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
+ if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
+ pTab->ResetRetouche();
+ }
+ }
+ else
+ bAddRect = false;
+
+ if ( pTab->IsCompletePaint() && !m_pOptTab )
+ m_pOptTab = pTab;
+ pTab->ResetCompletePaint();
+ }
+ if ( IsPaint() && bAddRect && pTab->IsRetouche() && !pTab->GetNext() )
+ {
+ // set correct rectangle for retouche: area between bottom of table frame
+ // and bottom of paint area of the upper frame.
+ SwRect aRect( pTab->GetUpper()->GetPaintArea() );
+ // vertical layout support
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetPrtBottom(*pTab) );
+ if ( !m_pImp->GetShell()->AddPaintRect( aRect ) )
+ pTab->ResetRetouche();
+ }
+
+ CheckWaitCursor();
+
+ rTimerAccess.UnblockIdling();
+
+ // Ugly shortcut!
+ if ( pTab->IsLowersFormatted() &&
+ (bPainted || !m_pImp->GetShell()->VisArea().Overlaps( pTab->getFrameArea())) )
+ return false;
+
+ // Now, deal with the lowers
+ if ( IsAgain() )
+ return false;
+
+ // for safety reasons:
+ // check page number before formatting lowers.
+ if ( pOldPage->GetPhyPageNum() > (pTab->FindPageFrame()->GetPhyPageNum() + 1) )
+ SetNextCycle( true );
+
+ // format lowers, only if table frame is valid
+ if ( pTab->isFrameAreaDefinitionValid() )
+ {
+ FlowFrameJoinLockGuard tabG(pTab); // tdf#124675 prevent Join() if pTab becomes empty
+ SwLayoutFrame *pLow = static_cast<SwLayoutFrame*>(pTab->Lower());
+ while ( pLow )
+ {
+ SwFrameDeleteGuard rowG(pLow); // tdf#124675 prevent RemoveFollowFlowLine()
+ bChanged |= FormatLayout( m_pImp->GetShell()->GetOut(), pLow, bAddRect );
+ if ( IsAgain() )
+ return false;
+ pLow = static_cast<SwLayoutFrame*>(pLow->GetNext());
+ }
+ }
+
+ return bChanged;
+}
+
+bool SwLayAction::FormatContent(SwPageFrame *const pPage)
+{
+ ::comphelper::ScopeGuard g([this, pPage]() {
+ if (IsAgain())
+ {
+ return; // pPage probably deleted
+ }
+ if (auto const* pObjs = pPage->GetSortedObjs())
+ {
+ std::vector<std::pair<SwAnchoredObject*, SwPageFrame*>> moved;
+ for (auto const pObj : *pObjs)
+ {
+ assert(!pObj->AnchorFrame()->IsTextFrame()
+ || !static_cast<SwTextFrame const*>(pObj->AnchorFrame())->IsFollow());
+ SwPageFrame *const pAnchorPage(pObj->AnchorFrame()->FindPageFrame());
+ assert(pAnchorPage);
+ if (pAnchorPage != pPage
+ && pPage->GetPhyPageNum() < pAnchorPage->GetPhyPageNum()
+ && pObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ != RndStdIds::FLY_AS_CHAR)
+ {
+ moved.emplace_back(pObj, pAnchorPage);
+ }
+ }
+ for (auto const& [pObj, pAnchorPage] : moved)
+ {
+ SAL_INFO("sw.layout", "SwLayAction::FormatContent: move anchored " << pObj << " from " << pPage->GetPhyPageNum() << " to " << pAnchorPage->GetPhyPageNum());
+ pObj->RegisterAtPage(*pAnchorPage);
+ // tdf#143239 if the position remains valid, it may not be
+ // positioned again so would remain on the wrong page!
+ pObj->InvalidateObjPos();
+ ::Notify_Background(pObj->GetDrawObj(), pPage,
+ pObj->GetObjRect(), PrepareHint::FlyFrameLeave, false);
+ }
+ if (!moved.empty())
+ {
+ pPage->InvalidateFlyLayout();
+ if (auto *const pContent = pPage->FindLastBodyContent())
+ {
+ pContent->InvalidateSize();
+ }
+ }
+ }
+ });
+
+ const SwContentFrame *pContent = pPage->ContainsContent();
+ const SwViewShell *pSh = m_pRoot->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+
+ while ( pContent && pPage->IsAnLower( pContent ) )
+ {
+ // If the content didn't change, we can use a few shortcuts.
+ const bool bFull = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint() ||
+ pContent->IsRetouche() || pContent->GetDrawObjs();
+ if ( bFull )
+ {
+ // We do this so we don't have to search later on.
+ const bool bNxtCnt = IsCalcLayout() && !pContent->GetFollow();
+ const SwContentFrame *pContentNext = bNxtCnt ? pContent->GetNextContentFrame() : nullptr;
+ SwContentFrame* const pContentPrev = pContent->GetPrev() ? pContent->GetPrevContentFrame() : nullptr;
+ std::optional<SfxDeleteListener> oPrevDeleteListener;
+ if (pContentPrev)
+ oPrevDeleteListener.emplace(*pContentPrev);
+
+ const SwLayoutFrame*pOldUpper = pContent->GetUpper();
+ const SwTabFrame *pTab = pContent->FindTabFrame();
+ const bool bInValid = !pContent->isFrameAreaDefinitionValid() || pContent->IsCompletePaint();
+ const bool bOldPaint = IsPaint();
+ m_bPaint = bOldPaint && !(pTab && pTab == m_pOptTab);
+ FormatContent_( pContent, pPage );
+ // reset <bPaint> before format objects
+ m_bPaint = bOldPaint;
+
+ // format floating screen object at content frame.
+ // No format, if action flag <bAgain> is set or action is interrupted.
+ // allow format on interruption of action, if
+ // it's the format for this interrupt
+ // pass correct page frame
+ // to the object formatter.
+ if ( !IsAgain() &&
+ ( !IsInterrupt() || mbFormatContentOnInterrupt ) &&
+ pContent->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame( *const_cast<SwContentFrame*>(pContent),
+ *(pContent->FindPageFrame()), this ) )
+ {
+ return false;
+ }
+
+ if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
+ if ( IsPaintExtraData() && IsPaint() &&
+ nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
+ m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
+ }
+
+ if ( IsAgain() )
+ return false;
+
+ // Temporarily interrupt processing if layout or Flys become invalid again.
+ // However not for the BrowseView: The layout is getting invalid
+ // all the time because the page height gets adjusted.
+ // The same applies if the user wants to continue working and at least one
+ // paragraph has been processed.
+ if (!pTab || !bInValid)
+ {
+ CheckIdleEnd();
+ // consider interrupt formatting.
+ if ( ( IsInterrupt() && !mbFormatContentOnInterrupt ) ||
+ ( !bBrowse && pPage->IsInvalidLayout() ) ||
+ // consider interrupt formatting
+ ( pPage->GetSortedObjs() && pPage->IsInvalidFly() && !mbFormatContentOnInterrupt )
+ )
+ return false;
+ }
+ if ( pOldUpper != pContent->GetUpper() )
+ {
+ const sal_uInt16 nCurNum = pContent->FindPageFrame()->GetPhyPageNum();
+ if ( nCurNum < pPage->GetPhyPageNum() )
+ m_nPreInvaPage = nCurNum;
+
+ // If the frame flowed backwards more than one page, we need to
+ // start over again from the beginning, so nothing gets left out.
+ if ( !IsCalcLayout() && pPage->GetPhyPageNum() > nCurNum+1 )
+ {
+ SetNextCycle( true );
+ // consider interrupt formatting
+ if ( !mbFormatContentOnInterrupt )
+ {
+ return false;
+ }
+ }
+ }
+ // If the frame moved forwards to the next page, we re-run through
+ // the predecessor.
+ // This way, we catch predecessors which are now responsible for
+ // retouching, but the footers will be touched also.
+ bool bSetContent = true;
+ if ( pContentPrev )
+ {
+ if (oPrevDeleteListener->WasDeleted())
+ {
+ SAL_WARN("sw", "ContentPrev was deleted");
+ return false;
+ }
+
+ if ( !pContentPrev->isFrameAreaDefinitionValid() && pPage->IsAnLower( pContentPrev ) )
+ {
+ pPage->InvalidateContent();
+ }
+
+ if ( pOldUpper != pContent->GetUpper() &&
+ pPage->GetPhyPageNum() < pContent->FindPageFrame()->GetPhyPageNum() )
+ {
+ pContent = pContentPrev;
+ bSetContent = false;
+ }
+ }
+ if ( bSetContent )
+ {
+ if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
+ pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
+ {
+ const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
+ const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
+ nBottom, pContent );
+ if ( !pTmp )
+ {
+ if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
+ !lcl_FindFirstInvaObj( pPage, nBottom )) &&
+ (!pPage->IsInvalidLayout() ||
+ !lcl_FindFirstInvaLay( pPage, nBottom )))
+ SetBrowseActionStop( true );
+ // consider interrupt formatting.
+ if ( !mbFormatContentOnInterrupt )
+ {
+ return false;
+ }
+ }
+ }
+ pContent = bNxtCnt ? pContentNext : pContent->GetNextContentFrame();
+ }
+
+ if (IsReschedule())
+ {
+ ::RescheduleProgress(m_pImp->GetShell()->GetDoc()->GetDocShell());
+ }
+ }
+ else
+ {
+ if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
+ if ( IsPaintExtraData() && IsPaint() &&
+ nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
+ m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
+ }
+
+ // Do this if the frame has been formatted before.
+ if ( pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() &&
+ IsPaint() )
+ PaintContent( pContent, pPage, pContent->getFrameArea(), pContent->getFrameArea().Bottom());
+ if ( IsIdle() )
+ {
+ CheckIdleEnd();
+ // consider interrupt formatting.
+ if ( IsInterrupt() && !mbFormatContentOnInterrupt )
+ return false;
+ }
+ if ( bBrowse && !IsIdle() && !IsCalcLayout() && !IsComplete() &&
+ pContent->getFrameArea().Top() > m_pImp->GetShell()->VisArea().Bottom())
+ {
+ const tools::Long nBottom = m_pImp->GetShell()->VisArea().Bottom();
+ const SwFrame *pTmp = lcl_FindFirstInvaContent( pPage,
+ nBottom, pContent );
+ if ( !pTmp )
+ {
+ if ( (!(pPage->GetSortedObjs() && pPage->IsInvalidFly()) ||
+ !lcl_FindFirstInvaObj( pPage, nBottom )) &&
+ (!pPage->IsInvalidLayout() ||
+ !lcl_FindFirstInvaLay( pPage, nBottom )))
+ SetBrowseActionStop( true );
+ // consider interrupt formatting.
+ if ( !mbFormatContentOnInterrupt )
+ {
+ return false;
+ }
+ }
+ }
+ pContent = pContent->GetNextContentFrame();
+ }
+ }
+ CheckWaitCursor();
+ // consider interrupt formatting.
+ return !IsInterrupt() || mbFormatContentOnInterrupt;
+}
+
+void SwLayAction::FormatContent_( const SwContentFrame *pContent, const SwPageFrame *pPage )
+{
+ // We probably only ended up here because the Content holds DrawObjects.
+ const bool bDrawObjsOnly = pContent->isFrameAreaDefinitionValid() && !pContent->IsCompletePaint() && !pContent->IsRetouche();
+ SwRectFnSet aRectFnSet(pContent);
+ if ( !bDrawObjsOnly && IsPaint() )
+ {
+ const SwRect aOldRect( pContent->UnionFrame() );
+ const tools::Long nOldBottom = aRectFnSet.GetPrtBottom(*pContent);
+ pContent->OptCalc();
+ if( IsAgain() )
+ return;
+ if( aRectFnSet.YDiff( aRectFnSet.GetBottom(pContent->getFrameArea()),
+ aRectFnSet.GetBottom(aOldRect) ) < 0 )
+ {
+ pContent->SetRetouche();
+ }
+ PaintContent( pContent, pContent->FindPageFrame(), aOldRect, nOldBottom);
+ }
+ else
+ {
+ if ( IsPaint() && pContent->IsTextFrame() && static_cast<const SwTextFrame*>(pContent)->HasRepaint() )
+ PaintContent( pContent, pPage, pContent->getFrameArea(),
+ aRectFnSet.GetBottom(pContent->getFrameArea()) );
+ pContent->OptCalc();
+ }
+}
+
+void SwLayAction::FormatFlyContent( const SwFlyFrame *pFly )
+{
+ const SwContentFrame *pContent = pFly->ContainsContent();
+
+ while ( pContent )
+ {
+ FormatContent_( pContent, pContent->FindPageFrame() );
+
+ // format floating screen objects at content text frame
+ // pass correct page frame to the object formatter.
+ if ( pContent->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame(
+ *const_cast<SwContentFrame*>(pContent),
+ *(pContent->FindPageFrame()), this ) )
+ {
+ // restart format with first content
+ pContent = pFly->ContainsContent();
+ continue;
+ }
+
+ if ( !pContent->GetValidLineNumFlag() && pContent->IsTextFrame() )
+ {
+ const sal_uLong nAllLines = static_cast<const SwTextFrame*>(pContent)->GetAllLines();
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pContent))->RecalcAllLines();
+ if ( IsPaintExtraData() && IsPaint() &&
+ nAllLines != static_cast<const SwTextFrame*>(pContent)->GetAllLines() )
+ m_pImp->GetShell()->AddPaintRect( pContent->getFrameArea() );
+ }
+
+ if ( IsAgain() )
+ return;
+
+ // If there's input, we interrupt processing.
+ if ( !pFly->IsFlyInContentFrame() )
+ {
+ CheckIdleEnd();
+ // consider interrupt formatting.
+ if ( IsInterrupt() && !mbFormatContentOnInterrupt )
+ return;
+ }
+ pContent = pContent->GetNextContentFrame();
+ }
+ CheckWaitCursor();
+}
+
+bool SwLayIdle::DoIdleJob_( const SwContentFrame *pCnt, IdleJobType eJob )
+{
+ OSL_ENSURE( pCnt->IsTextFrame(), "NoText neighbour of Text" );
+ // robust against misuse by e.g. #i52542#
+ if( !pCnt->IsTextFrame() )
+ return false;
+
+ SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pCnt));
+ // sw_redlinehide: spell check only the nodes with visible content?
+ SwTextNode* pTextNode = const_cast<SwTextNode*>(pTextFrame->GetTextNodeForParaProps());
+
+ bool bProcess = false;
+ for (size_t i = 0; pTextNode; )
+ {
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING :
+ bProcess = pTextNode->IsWrongDirty(); break;
+ case AUTOCOMPLETE_WORDS :
+ bProcess = pTextNode->IsAutoCompleteWordDirty(); break;
+ case WORD_COUNT :
+ bProcess = pTextNode->IsWordCountDirty(); break;
+ case SMART_TAGS :
+ bProcess = pTextNode->IsSmartTagDirty(); break;
+ }
+ if (bProcess)
+ {
+ break;
+ }
+ if (sw::MergedPara const* pMerged = pTextFrame->GetMergedPara())
+ {
+ while (true)
+ {
+ ++i;
+ if (i < pMerged->extents.size())
+ {
+ if (pMerged->extents[i].pNode != pTextNode)
+ {
+ pTextNode = pMerged->extents[i].pNode;
+ break;
+ }
+ }
+ else
+ {
+ pTextNode = nullptr;
+ break;
+ }
+ }
+ }
+ else
+ pTextNode = nullptr;
+ }
+
+ if( bProcess )
+ {
+ assert(pTextNode);
+ SwViewShell *pSh = m_pImp->GetShell();
+ if( COMPLETE_STRING == m_nTextPos )
+ {
+ --m_nTextPos;
+ if( auto pCursorShell = dynamic_cast<SwCursorShell *>( pSh ) )
+ if( !pCursorShell->IsTableMode() )
+ {
+ SwPaM *pCursor = pCursorShell->GetCursor();
+ if( !pCursor->HasMark() && !pCursor->IsMultiSelection() )
+ {
+ m_pContentNode = pCursor->GetContentNode();
+ m_nTextPos = pCursor->GetPoint()->nContent.GetIndex();
+ }
+ }
+ }
+ sal_Int32 const nPos((m_pContentNode && pTextNode == m_pContentNode)
+ ? m_nTextPos
+ : COMPLETE_STRING);
+
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING :
+ {
+ SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->AutoSpell_(*pTextNode, nPos) );
+ // PENDING should stop idle spell checking
+ m_bPageValid = m_bPageValid && (SwTextNode::WrongState::TODO != pTextNode->GetWrongDirty());
+ if ( aRepaint.HasArea() )
+ m_pImp->GetShell()->InvalidateWindows( aRepaint );
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return true;
+ break;
+ }
+ case AUTOCOMPLETE_WORDS :
+ const_cast<SwTextFrame*>(pTextFrame)->CollectAutoCmplWrds(*pTextNode, nPos);
+ // note: bPageValid remains true here even if the cursor
+ // position is skipped, so no PENDING state needed currently
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return true;
+ break;
+ case WORD_COUNT :
+ {
+ const sal_Int32 nEnd = pTextNode->GetText().getLength();
+ SwDocStat aStat;
+ pTextNode->CountWords( aStat, 0, nEnd );
+ if ( Application::AnyInput() )
+ return true;
+ break;
+ }
+ case SMART_TAGS :
+ {
+ try {
+ const SwRect aRepaint( const_cast<SwTextFrame*>(pTextFrame)->SmartTagScan(*pTextNode) );
+ m_bPageValid = m_bPageValid && !pTextNode->IsSmartTagDirty();
+ if ( aRepaint.HasArea() )
+ m_pImp->GetShell()->InvalidateWindows( aRepaint );
+ } catch( const css::uno::RuntimeException&) {
+ // handle smarttag problems gracefully and provide diagnostics
+ TOOLS_WARN_EXCEPTION( "sw.core", "SMART_TAGS");
+ }
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return true;
+ break;
+ }
+ }
+ }
+
+ // The Flys that are anchored to the paragraph need to be considered too.
+ if ( pCnt->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pObj : rObjs)
+ {
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ const SwContentFrame *pC = pFly->ContainsContent();
+ while( pC )
+ {
+ if ( pC->IsTextFrame() )
+ {
+ if ( DoIdleJob_( pC, eJob ) )
+ return true;
+ }
+ pC = pC->GetNextContentFrame();
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SwLayIdle::DoIdleJob( IdleJobType eJob, bool bVisAreaOnly )
+{
+ // Spellcheck all contents of the pages. Either only the
+ // visible ones or all of them.
+ const SwViewShell* pViewShell = m_pImp->GetShell();
+ const SwViewOption* pViewOptions = pViewShell->GetViewOptions();
+ const SwDoc* pDoc = pViewShell->GetDoc();
+
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING :
+ if( !pViewOptions->IsOnlineSpell() )
+ return false;
+ break;
+ case AUTOCOMPLETE_WORDS :
+ if( !SwViewOption::IsAutoCompleteWords() ||
+ SwDoc::GetAutoCompleteWords().IsLockWordLstLocked())
+ return false;
+ break;
+ case WORD_COUNT :
+ if ( !pViewShell->getIDocumentStatistics().GetDocStat().bModified )
+ return false;
+ break;
+ case SMART_TAGS :
+ if ( pDoc->GetDocShell()->IsHelpDocument() ||
+ pDoc->isXForms() ||
+ !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
+ return false;
+ break;
+ default: OSL_FAIL( "Unknown idle job type" );
+ }
+
+ SwPageFrame *pPage;
+ if ( bVisAreaOnly )
+ pPage = m_pImp->GetFirstVisPage(pViewShell->GetOut());
+ else
+ pPage = static_cast<SwPageFrame*>(m_pRoot->Lower());
+
+ m_pContentNode = nullptr;
+ m_nTextPos = COMPLETE_STRING;
+
+ while ( pPage )
+ {
+ m_bPageValid = true;
+ const SwContentFrame *pCnt = pPage->ContainsContent();
+ while( pCnt && pPage->IsAnLower( pCnt ) )
+ {
+ if ( DoIdleJob_( pCnt, eJob ) )
+ {
+ SAL_INFO("sw.idle", "DoIdleJob " << eJob << " interrupted on page " << pPage->GetPhyPageNum());
+ return true;
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ if ( pPage->GetSortedObjs() )
+ {
+ for ( size_t i = 0; pPage->GetSortedObjs() &&
+ i < pPage->GetSortedObjs()->size(); ++i )
+ {
+ const SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
+ if ( auto pFly = pObj->DynCastFlyFrame() )
+ {
+ const SwContentFrame *pC = pFly->ContainsContent();
+ while( pC )
+ {
+ if ( pC->IsTextFrame() )
+ {
+ if ( DoIdleJob_( pC, eJob ) )
+ {
+ SAL_INFO("sw.idle", "DoIdleJob " << eJob << " interrupted on page " << pPage->GetPhyPageNum());
+ return true;
+ }
+ }
+ pC = pC->GetNextContentFrame();
+ }
+ }
+ }
+ }
+
+ if( m_bPageValid )
+ {
+ switch ( eJob )
+ {
+ case ONLINE_SPELLING : pPage->ValidateSpelling(); break;
+ case AUTOCOMPLETE_WORDS : pPage->ValidateAutoCompleteWords(); break;
+ case WORD_COUNT : pPage->ValidateWordCount(); break;
+ case SMART_TAGS : pPage->ValidateSmartTags(); break;
+ }
+ }
+
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ if ( pPage && bVisAreaOnly &&
+ !pPage->getFrameArea().Overlaps( m_pImp->GetShell()->VisArea()))
+ break;
+ }
+ return false;
+}
+
+#if HAVE_FEATURE_DESKTOP && defined DBG_UTIL
+void SwLayIdle::ShowIdle( Color eColor )
+{
+ if ( m_bIndicator )
+ return;
+
+ m_bIndicator = true;
+ vcl::Window *pWin = m_pImp->GetShell()->GetWin();
+ if (pWin && !pWin->SupportsDoubleBuffering()) // FIXME make this work with double-buffering
+ {
+ tools::Rectangle aRect( 0, 0, 5, 5 );
+ aRect = pWin->PixelToLogic( aRect );
+ // Depending on if idle layout is in progress or not, draw a "red square" or a "green square".
+ pWin->GetOutDev()->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pWin->GetOutDev()->SetFillColor( eColor );
+ pWin->GetOutDev()->SetLineColor();
+ pWin->GetOutDev()->DrawRect( aRect );
+ pWin->GetOutDev()->Pop();
+ }
+}
+#define SHOW_IDLE( Color ) ShowIdle( Color )
+#else
+#define SHOW_IDLE( Color )
+#endif // DBG_UTIL
+
+SwLayIdle::SwLayIdle( SwRootFrame *pRt, SwViewShellImp *pI ) :
+ m_pRoot( pRt ),
+ m_pImp( pI )
+#ifdef DBG_UTIL
+ , m_bIndicator( false )
+#endif
+{
+ SAL_INFO("sw.idle", "SwLayIdle() entry");
+
+ m_pImp->m_pIdleAct = this;
+
+ SHOW_IDLE( COL_LIGHTRED );
+
+ m_pImp->GetShell()->EnableSmooth( false );
+
+ // First, spellcheck the visible area. Only if there's nothing
+ // to do there, we trigger the IdleFormat.
+ if ( !DoIdleJob( SMART_TAGS, true ) &&
+ !DoIdleJob( ONLINE_SPELLING, true ) &&
+ !DoIdleJob( AUTOCOMPLETE_WORDS, true ) )
+ {
+ // Format, then register repaint rectangles with the SwViewShell if necessary.
+ // This requires running artificial actions, so we don't get undesired
+ // effects when for instance the page count gets changed.
+ // We remember the shells where the cursor is visible, so we can make
+ // it visible again if needed after a document change.
+ std::vector<bool> aBools;
+ for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
+ {
+ ++rSh.mnStartAction;
+ bool bVis = false;
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ {
+ bVis = pCursorShell->GetCharRect().Overlaps(rSh.VisArea());
+ }
+ aBools.push_back( bVis );
+ }
+
+ bool bInterrupt(false);
+ {
+ SwLayAction aAction( m_pRoot, m_pImp );
+ aAction.SetInputType( VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER) );
+ aAction.SetIdle( true );
+ aAction.SetWaitAllowed( false );
+ aAction.Action(m_pImp->GetShell()->GetOut());
+ bInterrupt = aAction.IsInterrupt();
+ }
+
+ // Further start/end actions only happen if there were paints started
+ // somewhere or if the visibility of the CharRects has changed.
+ bool bActions = false;
+ size_t nBoolIdx = 0;
+ for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
+ {
+ --rSh.mnStartAction;
+
+ if ( rSh.Imp()->HasPaintRegion() )
+ bActions = true;
+ else
+ {
+ SwRect aTmp( rSh.VisArea() );
+ rSh.UISizeNotify();
+
+ // Are we supposed to crash if rSh isn't a cursor shell?!
+ // bActions |= aTmp != rSh.VisArea() ||
+ // aBools[nBoolIdx] != ((SwCursorShell*)&rSh)->GetCharRect().IsOver( rSh.VisArea() );
+
+ // aBools[ i ] is true, if the i-th shell is a cursor shell (!!!)
+ // and the cursor is visible.
+ bActions |= aTmp != rSh.VisArea();
+ if ( aTmp == rSh.VisArea() )
+ if ( auto pCursorShell = dynamic_cast< SwCursorShell*>( &rSh) )
+ bActions |= aBools[nBoolIdx] != pCursorShell->GetCharRect().Overlaps( rSh.VisArea() );
+ }
+
+ ++nBoolIdx;
+ }
+
+ if ( bActions )
+ {
+ // Prepare start/end actions via CursorShell, so the cursor, selection
+ // and VisArea can be set correctly.
+ nBoolIdx = 0;
+ for(SwViewShell& rSh : m_pImp->GetShell()->GetRingContainer())
+ {
+ SwCursorShell* pCursorShell = dynamic_cast<SwCursorShell*>( &rSh);
+
+ if ( pCursorShell )
+ pCursorShell->SttCursorMove();
+
+ // If there are accrued paints, it's best to simply invalidate
+ // the whole window. Otherwise there would arise paint problems whose
+ // solution would be disproportionally expensive.
+ SwViewShellImp *pViewImp = rSh.Imp();
+ bool bUnlock = false;
+ if ( pViewImp->HasPaintRegion() )
+ {
+ pViewImp->DeletePaintRegion();
+
+ // Cause a repaint with virtual device.
+ rSh.LockPaint();
+ bUnlock = true;
+ }
+
+ if ( pCursorShell )
+ // If the Cursor was visible, we need to make it visible again.
+ // Otherwise, EndCursorMove with true for IdleEnd
+ pCursorShell->EndCursorMove( !aBools[nBoolIdx] );
+ if( bUnlock )
+ {
+ if( pCursorShell )
+ {
+ // UnlockPaint overwrite the selection from the
+ // CursorShell and calls the virtual method paint
+ // to fill the virtual device. This fill don't have
+ // paint the selection! -> Set the focus flag at
+ // CursorShell and it doesn't paint the selection.
+ pCursorShell->ShellLoseFocus();
+ pCursorShell->UnlockPaint( true );
+ pCursorShell->ShellGetFocus();
+ }
+ else
+ rSh.UnlockPaint( true );
+ }
+ ++nBoolIdx;
+
+ }
+ }
+
+ if (!bInterrupt)
+ {
+ if ( !DoIdleJob( WORD_COUNT, false ) )
+ if ( !DoIdleJob( SMART_TAGS, false ) )
+ if ( !DoIdleJob( ONLINE_SPELLING, false ) )
+ DoIdleJob( AUTOCOMPLETE_WORDS, false );
+ }
+
+ bool bInValid = false;
+ const SwViewOption& rVOpt = *m_pImp->GetShell()->GetViewOptions();
+ const SwViewShell* pViewShell = m_pImp->GetShell();
+ // See conditions in DoIdleJob()
+ const bool bSpell = rVOpt.IsOnlineSpell();
+ const bool bACmplWrd = SwViewOption::IsAutoCompleteWords();
+ const bool bWordCount = pViewShell->getIDocumentStatistics().GetDocStat().bModified;
+ const bool bSmartTags = !pViewShell->GetDoc()->GetDocShell()->IsHelpDocument() &&
+ !pViewShell->GetDoc()->isXForms() &&
+ SwSmartTagMgr::Get().IsSmartTagsEnabled();
+
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(m_pRoot->Lower());
+ do
+ {
+ bInValid = pPg->IsInvalidContent() || pPg->IsInvalidLayout() ||
+ pPg->IsInvalidFlyContent() || pPg->IsInvalidFlyLayout() ||
+ pPg->IsInvalidFlyInCnt() ||
+ (bSpell && pPg->IsInvalidSpelling()) ||
+ (bACmplWrd && pPg->IsInvalidAutoCompleteWords()) ||
+ (bWordCount && pPg->IsInvalidWordCount()) ||
+ (bSmartTags && pPg->IsInvalidSmartTags());
+
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+
+ } while ( pPg && !bInValid );
+
+ if ( !bInValid )
+ {
+ m_pRoot->ResetIdleFormat();
+ SfxObjectShell* pDocShell = m_pImp->GetShell()->GetDoc()->GetDocShell();
+ pDocShell->Broadcast( SfxEventHint( SfxEventHintId::SwEventLayoutFinished, SwDocShell::GetEventName(STR_SW_EVENT_LAYOUT_FINISHED), pDocShell ) );
+ }
+ }
+
+ m_pImp->GetShell()->EnableSmooth( true );
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( m_pImp->IsAccessible() )
+ m_pImp->FireAccessibleEvents();
+#endif
+
+ SAL_INFO("sw.idle", "SwLayIdle() return");
+
+#ifdef DBG_UTIL
+ if ( m_bIndicator && m_pImp->GetShell()->GetWin() )
+ {
+ // Do not invalidate indicator, this may cause an endless loop. Instead, just repaint it
+ // This should be replaced by an overlay object in the future, anyways. Since it's only for debug
+ // purposes, it is not urgent.
+ m_bIndicator = false; SHOW_IDLE( COL_LIGHTGREEN );
+ }
+#endif
+}
+
+SwLayIdle::~SwLayIdle()
+{
+ m_pImp->m_pIdleAct = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/laycache.cxx b/sw/source/core/layout/laycache.cxx
new file mode 100644
index 000000000..e5b1dc341
--- /dev/null
+++ b/sw/source/core/layout/laycache.cxx
@@ -0,0 +1,1200 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/formatbreakitem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/stream.hxx>
+#include <doc.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docstat.hxx>
+#include <fmtpdsc.hxx>
+#include <laycache.hxx>
+#include "layhelp.hxx"
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <swtable.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <sectfrm.hxx>
+#include <fmtcntnt.hxx>
+#include <pagedesc.hxx>
+#include <frmtool.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <viewopt.hxx>
+#include <flyfrm.hxx>
+#include <sortedobjs.hxx>
+#include <ndindex.hxx>
+#include <node.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+
+#include <limits>
+
+using namespace ::com::sun::star;
+
+SwLayoutCache::SwLayoutCache() : m_nLockCount( 0 ) {}
+
+/*
+ * Reading and writing of the layout cache.
+ * The layout cache is not necessary, but it improves
+ * the performance and reduces the text flow during
+ * the formatting.
+ * The layout cache contains the index of the paragraphs/tables
+ * at the top of every page, so it's possible to create
+ * the right count of pages and to distribute the document content
+ * to this pages before the formatting starts.
+ */
+
+void SwLayoutCache::Read( SvStream &rStream )
+{
+ if( !m_pImpl )
+ {
+ m_pImpl.reset( new SwLayCacheImpl );
+ if( !m_pImpl->Read( rStream ) )
+ {
+ m_pImpl.reset();
+ }
+ }
+}
+
+void SwLayCacheImpl::Insert( sal_uInt16 nType, SwNodeOffset nIndex, sal_Int32 nOffset )
+{
+ m_aType.push_back( nType );
+ mIndices.push_back( nIndex );
+ m_aOffset.push_back( nOffset );
+}
+
+bool SwLayCacheImpl::Read( SvStream& rStream )
+{
+ SwLayCacheIoImpl aIo( rStream, false );
+ if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
+ return false;
+
+ // Due to an evil bug in the layout cache (#102759#), we cannot trust the
+ // sizes of fly frames which have been written using the "old" layout cache.
+ // This flag should indicate that we do not want to trust the width and
+ // height of fly frames
+ m_bUseFlyCache = aIo.GetMinorVersion() >= 1;
+
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
+ aIo.OpenFlagRec();
+ aIo.CloseFlagRec();
+ while( aIo.BytesLeft() && !aIo.HasError() )
+ {
+ sal_uInt32 nIndex(0), nOffset(0);
+
+ switch( aIo.Peek() )
+ {
+ case SW_LAYCACHE_IO_REC_PARA:
+ {
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
+ sal_uInt8 cFlags = aIo.OpenFlagRec();
+ aIo.GetStream().ReadUInt32( nIndex );
+ if( (cFlags & 0x01) != 0 )
+ aIo.GetStream().ReadUInt32( nOffset );
+ else
+ nOffset = COMPLETE_STRING;
+ aIo.CloseFlagRec();
+ Insert( SW_LAYCACHE_IO_REC_PARA, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
+ aIo.CloseRec();
+ break;
+ }
+ case SW_LAYCACHE_IO_REC_TABLE:
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
+ aIo.OpenFlagRec();
+ aIo.GetStream().ReadUInt32( nIndex )
+ .ReadUInt32( nOffset );
+ Insert( SW_LAYCACHE_IO_REC_TABLE, SwNodeOffset(nIndex), static_cast<sal_Int32>(nOffset) );
+ aIo.CloseFlagRec();
+ aIo.CloseRec();
+ break;
+ case SW_LAYCACHE_IO_REC_FLY:
+ {
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
+ aIo.OpenFlagRec();
+ aIo.CloseFlagRec();
+ sal_Int32 nX(0), nY(0), nW(0), nH(0);
+ sal_uInt16 nPgNum(0);
+ aIo.GetStream().ReadUInt16( nPgNum ).ReadUInt32( nIndex )
+ .ReadInt32( nX ).ReadInt32( nY ).ReadInt32( nW ).ReadInt32( nH );
+ m_FlyCache.emplace_back( nPgNum, nIndex, nX, nY, nW, nH );
+ aIo.CloseRec();
+ break;
+ }
+ default:
+ aIo.SkipRec();
+ break;
+ }
+ }
+ aIo.CloseRec();
+
+ return !aIo.HasError();
+}
+
+/** writes the index (more precise: the difference between
+ * the index and the first index of the document content)
+ * of the first paragraph/table at the top of every page.
+ * If at the top of a page is the rest of a paragraph/table
+ * from the bottom of the previous page, the character/row
+ * number is stored, too.
+ * The position, size and page number of the text frames
+ * are stored, too
+ */
+void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
+{
+ if( !rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) // the layout itself ..
+ return;
+
+ SwLayCacheIoImpl aIo( rStream, true );
+ // We want to save the relative index, so we need the index
+ // of the first content
+ SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
+ StartOfSectionNode()->GetIndex();
+ // The first page...
+ SwPageFrame* pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(rDoc.getIDocumentLayoutAccess().GetCurrentLayout()->Lower()));
+
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
+ aIo.OpenFlagRec( 0, 0 );
+ aIo.CloseFlagRec();
+ while( pPage )
+ {
+ if( pPage->GetPrev() )
+ {
+ SwLayoutFrame* pLay = pPage->FindBodyCont();
+ SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
+ // We are only interested in paragraph or table frames,
+ // a section frames contains paragraphs/tables.
+ if( pTmp && pTmp->IsSctFrame() )
+ pTmp = static_cast<SwSectionFrame*>(pTmp)->ContainsAny();
+
+ if( pTmp ) // any content
+ {
+ if( pTmp->IsTextFrame() )
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
+ assert(!pFrame->GetMergedPara());
+ SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ /* Open Paragraph Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
+ bool bFollow = static_cast<SwTextFrame*>(pTmp)->IsFollow();
+ aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
+ bFollow ? 8 : 4 );
+ nNdIdx -= nStartOfContent;
+ aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) );
+ if( bFollow )
+ aIo.GetStream().WriteUInt32( sal_Int32(static_cast<SwTextFrame*>(pTmp)->GetOffset()) );
+ aIo.CloseFlagRec();
+ /* Close Paragraph Record */
+ aIo.CloseRec();
+ }
+ }
+ else if( pTmp->IsTabFrame() )
+ {
+ SwTabFrame* pTab = static_cast<SwTabFrame*>(pTmp);
+ sal_uLong nOfst = COMPLETE_STRING;
+ if( pTab->IsFollow() )
+ {
+ // If the table is a follow, we have to look for the
+ // master and to count all rows to get the row number
+ nOfst = 0;
+ if( pTab->IsFollow() )
+ pTab = pTab->FindMaster( true );
+ while( pTab != pTmp )
+ {
+ SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ assert(pTab && "Table follow without master");
+ }
+ }
+ while (true)
+ {
+ SwNodeOffset nNdIdx =
+ pTab->GetTable()->GetTableNode()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ /* Open Table Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
+ aIo.OpenFlagRec( 0, 8 );
+ nNdIdx -= nStartOfContent;
+ aIo.GetStream().WriteUInt32( sal_Int32(nNdIdx) )
+ .WriteUInt32( nOfst );
+ aIo.CloseFlagRec();
+ /* Close Table Record */
+ aIo.CloseRec();
+ }
+ // If the table has a follow on the next page,
+ // we know already the row number and store this
+ // immediately.
+ if( pTab->GetFollow() )
+ {
+ if( nOfst == sal_uLong(COMPLETE_STRING) )
+ nOfst = 0;
+ do
+ {
+ SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ SwPageFrame *pTabPage = pTab->FindPageFrame();
+ if( pTabPage != pPage )
+ {
+ OSL_ENSURE( pPage->GetPhyPageNum() <
+ pTabPage->GetPhyPageNum(),
+ "Looping Tableframes" );
+ pPage = pTabPage;
+ break;
+ }
+ } while ( pTab->GetFollow() );
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ if( pPage->GetSortedObjs() )
+ {
+ SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame())
+ {
+ if( pFly->getFrameArea().Left() != FAR_AWAY &&
+ !pFly->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ const SwContact *pC =
+ ::GetUserCall(pAnchoredObj->GetDrawObj());
+ if( pC )
+ {
+ sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
+ sal_uInt16 nPageNum = pPage->GetPhyPageNum();
+ /* Open Fly Record */
+ aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
+ aIo.OpenFlagRec( 0, 0 );
+ aIo.CloseFlagRec();
+ const SwRect& rRct = pFly->getFrameArea();
+ sal_Int32 nX = rRct.Left() - pPage->getFrameArea().Left();
+ sal_Int32 nY = rRct.Top() - pPage->getFrameArea().Top();
+ aIo.GetStream().WriteUInt16( nPageNum ).WriteUInt32( nOrdNum )
+ .WriteInt32( nX ).WriteInt32( nY )
+ .WriteInt32( rRct.Width() )
+ .WriteInt32( rRct.Height() );
+ /* Close Fly Record */
+ aIo.CloseRec();
+ }
+ }
+ }
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ aIo.CloseRec();
+}
+
+#ifdef DBG_UTIL
+bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
+{
+ if( !m_pImpl )
+ return true;
+ const SwRootFrame *pRootFrame = rDoc.getIDocumentLayoutAccess().GetCurrentLayout();
+ if( pRootFrame )
+ {
+ size_t nIndex = 0;
+ SwNodeOffset nStartOfContent = rDoc.GetNodes().GetEndOfContent().
+ StartOfSectionNode()->GetIndex();
+ const SwPageFrame* pPage = static_cast<const SwPageFrame*>(pRootFrame->Lower());
+ if( pPage )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ while( pPage )
+ {
+ if( nIndex >= m_pImpl->size() )
+ return false;
+
+ const SwLayoutFrame* pLay = pPage->FindBodyCont();
+ const SwFrame* pTmp = pLay ? pLay->ContainsAny() : nullptr;
+ if( pTmp && pTmp->IsSctFrame() )
+ pTmp = static_cast<const SwSectionFrame*>(pTmp)->ContainsAny();
+ if( pTmp )
+ {
+ if( pTmp->IsTextFrame() )
+ {
+
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTmp));
+ assert(!pFrame->GetMergedPara());
+ SwNodeOffset nNdIdx = pFrame->GetTextNodeFirst()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ bool bFollow = static_cast<const SwTextFrame*>(pTmp)->IsFollow();
+ nNdIdx -= nStartOfContent;
+ if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
+ SW_LAYCACHE_IO_REC_PARA !=
+ m_pImpl->GetBreakType( nIndex ) ||
+ (bFollow
+ ? sal_Int32(static_cast<const SwTextFrame*>(pTmp)->GetOffset())
+ : COMPLETE_STRING) != m_pImpl->GetBreakOfst(nIndex))
+ {
+ return false;
+ }
+ ++nIndex;
+ }
+ }
+ else if( pTmp->IsTabFrame() )
+ {
+ const SwTabFrame* pTab = static_cast<const SwTabFrame*>(pTmp);
+ sal_Int32 nOfst = COMPLETE_STRING;
+ if( pTab->IsFollow() )
+ {
+ nOfst = 0;
+ if( pTab->IsFollow() )
+ pTab = pTab->FindMaster( true );
+ while( pTab != pTmp )
+ {
+ const SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ }
+ }
+ do
+ {
+ SwNodeOffset nNdIdx =
+ pTab->GetTable()->GetTableNode()->GetIndex();
+ if( nNdIdx > nStartOfContent )
+ {
+ nNdIdx -= nStartOfContent;
+ if( m_pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
+ SW_LAYCACHE_IO_REC_TABLE !=
+ m_pImpl->GetBreakType( nIndex ) ||
+ nOfst != m_pImpl->GetBreakOfst( nIndex ) )
+ {
+ return false;
+ }
+ ++nIndex;
+ }
+ if( pTab->GetFollow() )
+ {
+ if( nOfst == COMPLETE_STRING )
+ nOfst = 0;
+ do
+ {
+ const SwFrame* pSub = pTab->Lower();
+ while( pSub )
+ {
+ ++nOfst;
+ pSub = pSub->GetNext();
+ }
+ pTab = pTab->GetFollow();
+ const SwPageFrame *pTabPage = pTab->FindPageFrame();
+ if( pTabPage != pPage )
+ {
+ pPage = pTabPage;
+ break;
+ }
+ } while ( pTab->GetFollow() );
+ }
+ else
+ break;
+ } while( pTab );
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ return true;
+}
+#endif
+
+void SwLayoutCache::ClearImpl()
+{
+ if( !IsLocked() )
+ {
+ m_pImpl.reset();
+ }
+}
+
+SwLayoutCache::~SwLayoutCache()
+{
+ OSL_ENSURE( !m_nLockCount, "Deleting a locked SwLayoutCache!?" );
+}
+
+/// helper class to create not nested section frames for nested sections.
+SwActualSection::SwActualSection( SwActualSection *pUp,
+ SwSectionFrame *pSect,
+ SwSectionNode *pNd ) :
+ m_pUpper( pUp ),
+ m_pSectFrame( pSect ),
+ m_pSectNode( pNd )
+{
+ if ( !m_pSectNode )
+ {
+ const SwNodeIndex *pIndex = pSect->GetFormat()->GetContent().GetContentIdx();
+ m_pSectNode = pIndex->GetNode().FindSectionNode();
+ }
+}
+
+namespace {
+
+bool sanityCheckLayoutCache(SwLayCacheImpl const& rCache,
+ SwNodes const& rNodes, SwNodeOffset nNodeIndex)
+{
+ auto const nStartOfContent(rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex());
+ nNodeIndex -= nStartOfContent;
+ auto const nMaxIndex(rNodes.GetEndOfContent().GetIndex() - nStartOfContent);
+ for (size_t nIndex = 0; nIndex < rCache.size(); ++nIndex)
+ {
+ auto const nBreakIndex(rCache.GetBreakIndex(nIndex));
+ if (nBreakIndex < nNodeIndex || nMaxIndex <= nBreakIndex)
+ {
+ SAL_WARN("sw.layout",
+ "invalid node index in layout-cache: " << nBreakIndex);
+ return false;
+ }
+ auto const nBreakType(rCache.GetBreakType(nIndex));
+ switch (nBreakType)
+ {
+ case SW_LAYCACHE_IO_REC_PARA:
+ if (!rNodes[nBreakIndex + nStartOfContent]->IsTextNode())
+ {
+ SAL_WARN("sw.layout",
+ "invalid node of type 'P' in layout-cache");
+ return false;
+ }
+ break;
+ case SW_LAYCACHE_IO_REC_TABLE:
+ if (!rNodes[nBreakIndex + nStartOfContent]->IsTableNode())
+ {
+ SAL_WARN("sw.layout",
+ "invalid node of type 'T' in layout-cache");
+ return false;
+ }
+ break;
+ default:
+ assert(false); // Read shouldn't have inserted that
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+/** helper class, which utilizes the layout cache information
+ * to distribute the document content to the right pages.
+ * It's used by the InsertCnt_(..)-function.
+ * If there's no layout cache, the distribution to the pages is more
+ * a guess, but a guess with statistical background.
+ */
+SwLayHelper::SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
+ SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
+ SwNodeOffset nNodeIndex, bool bCache )
+ : mrpFrame( rpF )
+ , mrpPrv( rpP )
+ , mrpPage( rpPg )
+ , mrpLay( rpL )
+ , mrpActualSection( rpA )
+ , mbBreakAfter(false)
+ , mpDoc(pD)
+ , mnMaxParaPerPage( 25 )
+ , mnParagraphCnt( bCache ? 0 : USHRT_MAX )
+ , mnFlyIdx( 0 )
+ , mbFirst( bCache )
+{
+ mpImpl = mpDoc->GetLayoutCache() ? mpDoc->GetLayoutCache()->LockImpl() : nullptr;
+ if( mpImpl )
+ {
+ SwNodes const& rNodes(mpDoc->GetNodes());
+ if (sanityCheckLayoutCache(*mpImpl, rNodes, nNodeIndex))
+ {
+ mnIndex = 0;
+ mnStartOfContent = rNodes.GetEndOfContent().StartOfSectionNode()->GetIndex();
+ mnMaxParaPerPage = 1000;
+ }
+ else
+ {
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ mpImpl = nullptr;
+ mnIndex = std::numeric_limits<size_t>::max();
+ mnStartOfContent = SwNodeOffset(USHRT_MAX);
+ }
+ }
+ else
+ {
+ mnIndex = std::numeric_limits<size_t>::max();
+ mnStartOfContent = NODE_OFFSET_MAX;
+ }
+}
+
+SwLayHelper::~SwLayHelper()
+{
+ if( mpImpl )
+ {
+ OSL_ENSURE( mpDoc && mpDoc->GetLayoutCache(), "Missing layoutcache" );
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ }
+}
+
+/** Does NOT really calculate the page count,
+ * it returns the page count value from the layout cache, if available,
+ * otherwise it estimates the page count.
+ */
+sal_uLong SwLayHelper::CalcPageCount()
+{
+ sal_uLong nPgCount;
+ SwLayCacheImpl *pCache = mpDoc->GetLayoutCache() ?
+ mpDoc->GetLayoutCache()->LockImpl() : nullptr;
+ if( pCache )
+ {
+ nPgCount = pCache->size() + 1;
+ mpDoc->GetLayoutCache()->UnlockImpl();
+ }
+ else
+ {
+ nPgCount = mpDoc->getIDocumentStatistics().GetDocStat().nPage;
+ if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
+ nPgCount = 0;
+ sal_Int32 nNdCount = mpDoc->getIDocumentStatistics().GetDocStat().nPara;
+ if ( nNdCount <= 1 )
+ {
+ //Estimates the number of paragraphs.
+ SwNodeOffset nTmp = mpDoc->GetNodes().GetEndOfContent().GetIndex() -
+ mpDoc->GetNodes().GetEndOfExtras().GetIndex();
+ //Tables have a little overhead...
+ nTmp -= SwNodeOffset(mpDoc->GetTableFrameFormats()->size() * 25);
+ //Fly frames, too ..
+ nTmp -= (mpDoc->GetNodes().GetEndOfAutotext().GetIndex() -
+ mpDoc->GetNodes().GetEndOfInserts().GetIndex()) / SwNodeOffset(3 * 5);
+ if ( nTmp > SwNodeOffset(0) )
+ nNdCount = sal_Int32(nTmp);
+ }
+ if ( nNdCount > 100 ) // no estimation below this value
+ {
+ if ( nPgCount > 0 )
+ { // tdf#129529 avoid 0...
+ mnMaxParaPerPage = std::max<sal_uLong>(3, nNdCount / nPgCount);
+ }
+ else
+ {
+ mnMaxParaPerPage = std::max( sal_uLong(20),
+ sal_uLong(20 + nNdCount / 1000 * 3) );
+ const sal_uLong nMax = 53;
+ mnMaxParaPerPage = std::min( mnMaxParaPerPage, nMax );
+ nPgCount = nNdCount / mnMaxParaPerPage;
+ }
+ if ( nNdCount < 1000 )
+ nPgCount = 0;// no progress bar for small documents
+ SwViewShell *pSh = nullptr;
+ if( mrpLay && mrpLay->getRootFrame() )
+ pSh = mrpLay->getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ mnMaxParaPerPage *= 6;
+ }
+ }
+ return nPgCount;
+}
+
+/**
+ * inserts a page and return true, if
+ * - the break after flag is set
+ * - the actual content wants a break before
+ * - the maximum count of paragraph/rows is reached
+ *
+ * The break after flag is set, if the actual content
+ * wants a break after.
+ */
+bool SwLayHelper::CheckInsertPage()
+{
+ bool bEnd = nullptr == mrpPage->GetNext();
+ const SvxFormatBreakItem& rBrk = mrpFrame->GetBreakItem();
+ const SwFormatPageDesc& rDesc = mrpFrame->GetPageDescItem();
+ // #118195# Do not evaluate page description if frame
+ // is a follow frame!
+ const SwPageDesc* pDesc = mrpFrame->IsFlowFrame() &&
+ SwFlowFrame::CastFlowFrame( mrpFrame )->IsFollow() ?
+ nullptr :
+ rDesc.GetPageDesc();
+
+ bool bBrk = mnParagraphCnt > mnMaxParaPerPage || mbBreakAfter;
+ mbBreakAfter = rBrk.GetBreak() == SvxBreak::PageAfter ||
+ rBrk.GetBreak() == SvxBreak::PageBoth;
+ if ( !bBrk )
+ bBrk = rBrk.GetBreak() == SvxBreak::PageBefore ||
+ rBrk.GetBreak() == SvxBreak::PageBoth;
+
+ if ( bBrk || pDesc )
+ {
+ ::std::optional<sal_uInt16> oPgNum;
+ if ( !pDesc )
+ {
+ pDesc = mrpPage->GetPageDesc()->GetFollow();
+ }
+ else
+ {
+ oPgNum = rDesc.GetNumOffset();
+ if ( oPgNum )
+ static_cast<SwRootFrame*>(mrpPage->GetUpper())->SetVirtPageNum(true);
+ }
+ bool bNextPageRight = !mrpPage->OnRightPage();
+ bool bInsertEmpty = false;
+ assert(mrpPage->GetUpper()->GetLower());
+ if (oPgNum && bNextPageRight != IsRightPageByNumber(
+ *static_cast<SwRootFrame*>(mrpPage->GetUpper()), *oPgNum))
+ {
+ bNextPageRight = !bNextPageRight;
+ bInsertEmpty = true;
+ }
+ // If the page style is changing, we'll have a first page.
+ bool bNextPageFirst = pDesc != mrpPage->GetPageDesc();
+ ::InsertNewPage( const_cast<SwPageDesc&>(*pDesc), mrpPage->GetUpper(),
+ bNextPageRight, bNextPageFirst, bInsertEmpty, false, mrpPage->GetNext());
+ if ( bEnd )
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ do
+ { mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ } while ( mrpPage->GetNext() );
+ }
+ else
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ if ( mrpPage->IsEmptyPage() )
+ {
+ OSL_ENSURE( mrpPage->GetNext(), "No new page?" );
+ mrpPage = static_cast<SwPageFrame*>(mrpPage->GetNext());
+ }
+ }
+ mrpLay = mrpPage->FindBodyCont();
+ while( mrpLay->Lower() )
+ mrpLay = static_cast<SwLayoutFrame*>(mrpLay->Lower());
+ return true;
+ }
+ return false;
+}
+
+/** entry point for the InsertCnt_-function.
+ * The document content index is checked either it is
+ * in the layout cache either it's time to insert a page
+ * cause the maximal estimation of content per page is reached.
+ * A really big table or long paragraph may contains more than
+ * one page, in this case the needed count of pages will inserted.
+ */
+bool SwLayHelper::CheckInsert( SwNodeOffset nNodeIndex )
+{
+ bool bRet = false;
+ bool bLongTab = false;
+ sal_uLong nMaxRowPerPage( 0 );
+ nNodeIndex -= mnStartOfContent;
+ sal_uInt16 nRows( 0 );
+ if( mrpFrame->IsTabFrame() )
+ {
+ //Inside a table counts every row as a paragraph
+ SwFrame *pLow = static_cast<SwTabFrame*>(mrpFrame)->Lower();
+ nRows = 0;
+ do
+ {
+ ++nRows;
+ pLow = pLow->GetNext();
+ } while ( pLow );
+ mnParagraphCnt += nRows;
+ if( !mpImpl && mnParagraphCnt > mnMaxParaPerPage + 10 )
+ {
+ // OD 09.04.2003 #108698# - improve heuristics:
+ // Assume that a table, which has more than three times the quantity
+ // of maximal paragraphs per page rows, consists of rows, which have
+ // the height of a normal paragraph. Thus, allow as much rows per page
+ // as much paragraphs are allowed.
+ if ( nRows > ( 3*mnMaxParaPerPage ) )
+ {
+ nMaxRowPerPage = mnMaxParaPerPage;
+ }
+ else
+ {
+ SwFrame *pTmp = static_cast<SwTabFrame*>(mrpFrame)->Lower();
+ if( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ pTmp = static_cast<SwRowFrame*>(pTmp)->Lower();
+ sal_uInt16 nCnt = 0;
+ do
+ {
+ ++nCnt;
+ pTmp = pTmp->GetNext();
+ } while( pTmp );
+ nMaxRowPerPage = std::max( sal_uLong(2), mnMaxParaPerPage / nCnt );
+ }
+ bLongTab = true;
+ }
+ }
+ else
+ ++mnParagraphCnt;
+ if( mbFirst && mpImpl && mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex &&
+ ( mpImpl->GetBreakOfst( mnIndex ) < COMPLETE_STRING ||
+ ( ++mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) ) )
+ mbFirst = false;
+ // OD 09.04.2003 #108698# - always split a big tables.
+ if ( !mbFirst ||
+ ( mrpFrame->IsTabFrame() && bLongTab )
+ )
+ {
+ sal_Int32 nRowCount = 0;
+ do
+ {
+ if( mpImpl || bLongTab )
+ {
+ sal_Int32 nOfst = COMPLETE_STRING;
+ sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
+ if( bLongTab )
+ {
+ mbBreakAfter = true;
+ nOfst = static_cast<sal_Int32>(nRowCount + nMaxRowPerPage);
+ }
+ else
+ {
+ while( mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex(mnIndex) < nNodeIndex)
+ ++mnIndex;
+ if( mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex(mnIndex) == nNodeIndex )
+ {
+ nType = mpImpl->GetBreakType( mnIndex );
+ nOfst = mpImpl->GetBreakOfst( mnIndex++ );
+ mbBreakAfter = true;
+ }
+ }
+
+ if( nOfst < COMPLETE_STRING )
+ {
+ bool bSplit = false;
+ sal_uInt16 nRepeat( 0 );
+ if( !bLongTab && mrpFrame->IsTextFrame() &&
+ SW_LAYCACHE_IO_REC_PARA == nType &&
+ nOfst < static_cast<SwTextFrame*>(mrpFrame)->GetText().getLength())
+ bSplit = true;
+ else if( mrpFrame->IsTabFrame() && nRowCount < nOfst &&
+ ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
+ {
+ nRepeat = static_cast<SwTabFrame*>(mrpFrame)->
+ GetTable()->GetRowsToRepeat();
+ bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
+ bLongTab = bLongTab && bSplit;
+ }
+ if( bSplit )
+ {
+ mrpFrame->InsertBehind( mrpLay, mrpPrv );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpFrame);
+ aFrm.Pos() = mrpLay->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 );
+ }
+
+ mrpPrv = mrpFrame;
+ if( mrpFrame->IsTabFrame() )
+ {
+ SwTabFrame* pTab = static_cast<SwTabFrame*>(mrpFrame);
+ // #i33629#, #i29955#
+ ::RegistFlys( pTab->FindPageFrame(), pTab );
+ SwFrame *pRow = pTab->Lower();
+ SwTabFrame *pFoll = new SwTabFrame( *pTab );
+
+ SwFrame *pPrv;
+ if( nRepeat > 0 )
+ {
+ sw::FlyCreationSuppressor aSuppressor;
+ // Insert new headlines:
+ sal_uInt16 nRowIdx = 0;
+ SwRowFrame* pHeadline = nullptr;
+ while( nRowIdx < nRepeat )
+ {
+ OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table without rows?" );
+ pHeadline =
+ new SwRowFrame( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
+ pHeadline->SetRepeatedHeadline( true );
+ pHeadline->InsertBefore( pFoll, nullptr );
+ pHeadline->RegistFlys();
+
+ ++nRowIdx;
+ }
+ pPrv = pHeadline;
+ nRows = nRows + nRepeat;
+ }
+ else
+ pPrv = nullptr;
+ while( pRow && nRowCount < nOfst )
+ {
+ pRow = pRow->GetNext();
+ ++nRowCount;
+ }
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( pFoll, pPrv );
+ pPrv = pRow;
+ pRow = pNxt;
+ }
+ mrpFrame = pFoll;
+ }
+ else
+ {
+ SwTextFrame *const pNew = static_cast<SwTextFrame*>(
+ static_cast<SwTextFrame*>(mrpFrame)
+ ->GetTextNodeFirst()->MakeFrame(mrpFrame));
+ pNew->ManipOfst( TextFrameIndex(nOfst) );
+ pNew->SetFollow( static_cast<SwTextFrame*>(mrpFrame)->GetFollow() );
+ static_cast<SwTextFrame*>(mrpFrame)->SetFollow( pNew );
+ mrpFrame = pNew;
+ }
+ }
+ }
+ }
+
+ SwPageFrame* pLastPage = mrpPage;
+ if( CheckInsertPage() )
+ {
+ CheckFlyCache_( pLastPage );
+ if( mrpPrv && mrpPrv->IsTextFrame() && !mrpPrv->isFrameAreaSizeValid() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mrpPrv);
+ aFrm.Height( mrpPrv->GetUpper()->getFramePrintArea().Height() );
+ }
+
+ bRet = true;
+ mrpPrv = nullptr;
+ mnParagraphCnt = 0;
+
+ if ( mrpActualSection )
+ {
+ //Did the SectionFrame even have a content? If not, we can
+ //directly put it somewhere else
+ SwSectionFrame *pSct;
+ bool bInit = false;
+ if ( !mrpActualSection->GetSectionFrame()->ContainsContent())
+ {
+ pSct = mrpActualSection->GetSectionFrame();
+ pSct->RemoveFromLayout();
+ }
+ else
+ {
+ pSct = new SwSectionFrame(
+ *mrpActualSection->GetSectionFrame(), false );
+ mrpActualSection->GetSectionFrame()->SimpleFormat();
+ bInit = true;
+ }
+ mrpActualSection->SetSectionFrame( pSct );
+ pSct->InsertBehind( mrpLay, nullptr );
+
+ if( bInit )
+ {
+ pSct->Init();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pSct);
+ aFrm.Pos() = mrpLay->getFrameArea().Pos();
+ aFrm.Pos().AdjustY(1 ); //because of the notifications
+ }
+
+ mrpLay = pSct;
+ if ( mrpLay->Lower() && mrpLay->Lower()->IsLayoutFrame() )
+ mrpLay = mrpLay->GetNextLayoutLeaf();
+ }
+ }
+ } while( bLongTab || ( mpImpl && mnIndex < mpImpl->size() &&
+ mpImpl->GetBreakIndex( mnIndex ) == nNodeIndex ) );
+ }
+ mbFirst = false;
+ return bRet;
+}
+
+namespace {
+
+struct SdrObjectCompare
+{
+ bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
+ {
+ return pF1->GetOrdNum() < pF2->GetOrdNum();
+ }
+};
+
+struct FlyCacheCompare
+{
+ bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
+ {
+ return pC1->nOrdNum < pC2->nOrdNum;
+ }
+};
+
+}
+
+/**
+ * If a new page is inserted, the last page is analysed.
+ * If there are text frames with default position, the fly cache
+ * is checked, if these frames are stored in the cache.
+ */
+void SwLayHelper::CheckFlyCache_( SwPageFrame* pPage )
+{
+ if( !mpImpl || !pPage )
+ return;
+ const size_t nFlyCount = mpImpl->GetFlyCount();
+ // Any text frames at the page, fly cache available?
+ if( !(pPage->GetSortedObjs() && mnFlyIdx < nFlyCount) )
+ return;
+
+ SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ sal_uInt16 nPgNum = pPage->GetPhyPageNum();
+
+ // NOTE: Here we do not use the absolute ordnums but
+ // relative ordnums for the objects on this page.
+
+ // skip fly frames from pages before the current page
+ while( mnFlyIdx < nFlyCount &&
+ mpImpl->GetFlyCache(mnFlyIdx).nPageNum < nPgNum )
+ ++mnFlyIdx;
+
+ // sort cached objects on this page by ordnum
+ o3tl::sorted_vector< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
+ size_t nIdx = mnFlyIdx;
+
+ SwFlyCache* pFlyC;
+ while( nIdx < nFlyCount &&
+ ( pFlyC = &mpImpl->GetFlyCache( nIdx ) )->nPageNum == nPgNum )
+ {
+ aFlyCacheSet.insert( pFlyC );
+ ++nIdx;
+ }
+
+ // sort objects on this page by ordnum
+ o3tl::sorted_vector< const SdrObject*, SdrObjectCompare > aFlySet;
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if (SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame()) // a text frame?
+ {
+ if( pFly->GetAnchorFrame() &&
+ !pFly->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
+ if( pC )
+ {
+ aFlySet.insert( pAnchoredObj->GetDrawObj() );
+ }
+ }
+ }
+ }
+
+ if ( aFlyCacheSet.size() != aFlySet.size() )
+ return;
+
+ auto aFlySetIt = aFlySet.begin();
+
+ for ( const SwFlyCache* pFlyCache : aFlyCacheSet )
+ {
+ SwFlyFrame* pFly = const_cast<SwVirtFlyDrawObj*>(static_cast<const SwVirtFlyDrawObj*>(*aFlySetIt))->GetFlyFrame();
+
+ if ( pFly->getFrameArea().Left() == FAR_AWAY )
+ {
+ // we get the stored information
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
+ aFrm.Pos().setX( pFlyCache->Left() + pPage->getFrameArea().Left() );
+ aFrm.Pos().setY( pFlyCache->Top() + pPage->getFrameArea().Top() );
+
+ if ( mpImpl->IsUseFlyCache() )
+ {
+ aFrm.Width( pFlyCache->Width() );
+ aFrm.Height( pFlyCache->Height() );
+ }
+ }
+
+ ++aFlySetIt;
+ }
+}
+
+SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
+ m_pStream( &rStrm ),
+ m_nFlagRecEnd ( 0 ),
+ m_nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
+ m_nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
+ m_bWriteMode( bWrtMd ),
+ m_bError( false )
+{
+ if( m_bWriteMode )
+ m_pStream->WriteUInt16( m_nMajorVersion )
+ .WriteUInt16( m_nMinorVersion );
+
+ else
+ m_pStream->ReadUInt16( m_nMajorVersion )
+ .ReadUInt16( m_nMinorVersion );
+}
+
+void SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
+{
+ sal_uInt32 nPos = m_pStream->Tell();
+ if( m_bWriteMode )
+ {
+ m_aRecords.emplace_back(cType, nPos );
+ m_pStream->WriteUInt32( 0 );
+ }
+ else
+ {
+ sal_uInt32 nVal(0);
+ m_pStream->ReadUInt32( nVal );
+ sal_uInt8 cRecTyp = static_cast<sal_uInt8>(nVal);
+ if (!nVal || cRecTyp != cType || !m_pStream->good())
+ {
+ OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
+ OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
+ m_aRecords.emplace_back(0, m_pStream->Tell() );
+ m_bError = true;
+ }
+ else
+ {
+ sal_uInt32 nSize = nVal >> 8;
+ m_aRecords.emplace_back(cRecTyp, nPos+nSize );
+ }
+ }
+}
+
+// Close record
+void SwLayCacheIoImpl::CloseRec()
+{
+ bool bRes = true;
+ OSL_ENSURE( !m_aRecords.empty(), "CloseRec: no levels" );
+ if( !m_aRecords.empty() )
+ {
+ sal_uInt32 nPos = m_pStream->Tell();
+ if( m_bWriteMode )
+ {
+ sal_uInt32 nBgn = m_aRecords.back().size;
+ m_pStream->Seek( nBgn );
+ sal_uInt32 nSize = nPos - nBgn;
+ sal_uInt32 nVal = ( nSize << 8 ) | m_aRecords.back().type;
+ m_pStream->WriteUInt32( nVal );
+ m_pStream->Seek( nPos );
+ if( m_pStream->GetError() != ERRCODE_NONE )
+ bRes = false;
+ }
+ else
+ {
+ sal_uInt32 n = m_aRecords.back().size;
+ OSL_ENSURE( n >= nPos, "CloseRec: too much data read" );
+ if( n != nPos )
+ {
+ m_pStream->Seek( n );
+ if( n < nPos )
+ bRes = false;
+ }
+ if( m_pStream->GetErrorCode() != ERRCODE_NONE )
+ bRes = false;
+ }
+ m_aRecords.pop_back();
+ }
+
+ if( !bRes )
+ m_bError = true;
+}
+
+sal_uInt32 SwLayCacheIoImpl::BytesLeft()
+{
+ sal_uInt32 n = 0;
+ if( !m_bError && !m_aRecords.empty() )
+ {
+ sal_uInt32 nEndPos = m_aRecords.back().size;
+ sal_uInt32 nPos = m_pStream->Tell();
+ if( nEndPos > nPos )
+ n = nEndPos - nPos;
+ }
+ return n;
+}
+
+sal_uInt8 SwLayCacheIoImpl::Peek()
+{
+ sal_uInt8 c(0);
+ if( !m_bError )
+ {
+ sal_uInt32 nPos = m_pStream->Tell();
+ m_pStream->ReadUChar( c );
+ m_pStream->Seek( nPos );
+ if( m_pStream->GetErrorCode() != ERRCODE_NONE )
+ {
+ c = 0;
+ m_bError = true;
+ }
+ }
+ return c;
+}
+
+void SwLayCacheIoImpl::SkipRec()
+{
+ sal_uInt8 c = Peek();
+ OpenRec( c );
+ m_pStream->Seek( m_aRecords.back().size );
+ CloseRec();
+}
+
+sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
+{
+ OSL_ENSURE( !m_bWriteMode, "OpenFlagRec illegal in write mode" );
+ sal_uInt8 cFlags(0);
+ m_pStream->ReadUChar( cFlags );
+ m_nFlagRecEnd = m_pStream->Tell() + ( cFlags & 0x0F );
+ return (cFlags >> 4);
+}
+
+void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
+{
+ OSL_ENSURE( m_bWriteMode, "OpenFlagRec illegal in read mode" );
+ OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
+ OSL_ENSURE( nLen < 16, "wrong flag record length" );
+ sal_uInt8 cFlags = (nFlags << 4) + nLen;
+ m_pStream->WriteUChar( cFlags );
+ m_nFlagRecEnd = m_pStream->Tell() + nLen;
+}
+
+void SwLayCacheIoImpl::CloseFlagRec()
+{
+ if( m_bWriteMode )
+ {
+ OSL_ENSURE( m_pStream->Tell() == m_nFlagRecEnd, "Wrong amount of data written" );
+ }
+ else
+ {
+ OSL_ENSURE( m_pStream->Tell() <= m_nFlagRecEnd, "Too many data read" );
+ if( m_pStream->Tell() != m_nFlagRecEnd )
+ m_pStream->Seek( m_nFlagRecEnd );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layhelp.hxx b/sw/source/core/layout/layhelp.hxx
new file mode 100644
index 000000000..17c5cc324
--- /dev/null
+++ b/sw/source/core/layout/layhelp.hxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_LAYHELP_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_LAYHELP_HXX
+
+#include <swrect.hxx>
+#include <nodeoffset.hxx>
+
+#include <tools/solar.h>
+
+#include <memory>
+#include <vector>
+#include <deque>
+
+class SwDoc;
+class SwFrame;
+class SwLayoutFrame;
+class SwPageFrame;
+class SwSectionFrame;
+class SwSectionNode;
+class SvStream;
+
+/*
+ * Contains the page break information and the text frame positions
+ * of the document (after loading)
+ * and is used inside the constructor of the layout rootframe to
+ * insert content and text frames at the right pages.
+ * For every page of the main text (body content, no footnotes, text frames etc.)
+ * we have the nodeindex of the first content at the page,
+ * the type of content ( table or paragraph )
+ * and if it's not the first part of the table/paragraph,
+ * the row/character-offset inside the table/paragraph.
+ * The text frame positions are stored in the SwPageFlyCache array.
+ */
+
+class SwFlyCache;
+typedef std::vector<SwFlyCache> SwPageFlyCache;
+
+class SwLayCacheImpl
+{
+ std::vector<SwNodeOffset> mIndices;
+ /// either a textframe character offset, or a row index inside a table
+ std::deque<sal_Int32> m_aOffset;
+ std::vector<sal_uInt16> m_aType;
+ SwPageFlyCache m_FlyCache;
+ bool m_bUseFlyCache;
+ void Insert( sal_uInt16 nType, SwNodeOffset nIndex, sal_Int32 nOffset );
+
+public:
+ inline SwLayCacheImpl();
+
+ size_t size() const { return mIndices.size(); }
+
+ bool Read( SvStream& rStream );
+
+ SwNodeOffset GetBreakIndex( size_t nIdx ) const { return mIndices[ nIdx ]; }
+ sal_Int32 GetBreakOfst( size_t nIdx ) const { return m_aOffset[ nIdx ]; }
+ sal_uInt16 GetBreakType( size_t nIdx ) const { return m_aType[ nIdx ]; }
+
+ inline size_t GetFlyCount() const;
+ inline SwFlyCache& GetFlyCache( size_t nIdx );
+
+ bool IsUseFlyCache() const { return m_bUseFlyCache; }
+};
+
+// Helps to create the sectionframes during the InsertCnt_-function
+// by controlling nested sections.
+class SwActualSection
+{
+ SwActualSection *m_pUpper;
+ SwSectionFrame *m_pSectFrame;
+ SwSectionNode *m_pSectNode;
+public:
+ SwActualSection( SwActualSection *pUpper,
+ SwSectionFrame *pSect,
+ SwSectionNode *pNd );
+
+ SwSectionFrame *GetSectionFrame() { return m_pSectFrame; }
+ void SetSectionFrame( SwSectionFrame *p ) { m_pSectFrame = p; }
+ SwSectionNode *GetSectionNode() { return m_pSectNode;}
+ void SetUpper(SwActualSection *p) { m_pUpper = p; }
+ SwActualSection *GetUpper() { return m_pUpper; }
+};
+
+/// Helps during the InsertCnt_ function to create new pages.
+/// If there's a layout cache available, this information is used.
+class SwLayHelper
+{
+ SwFrame* &mrpFrame;
+ SwFrame* &mrpPrv;
+ SwPageFrame* &mrpPage;
+ SwLayoutFrame* &mrpLay;
+ std::unique_ptr<SwActualSection> &mrpActualSection;
+ bool mbBreakAfter;
+ SwDoc* mpDoc;
+ SwLayCacheImpl* mpImpl;
+ sal_uLong mnMaxParaPerPage;
+ sal_uLong mnParagraphCnt;
+ SwNodeOffset mnStartOfContent;
+ size_t mnIndex; ///< the index in the page break array
+ size_t mnFlyIdx; ///< the index in the fly cache array
+ bool mbFirst : 1;
+ void CheckFlyCache_( SwPageFrame* pPage );
+public:
+ SwLayHelper( SwDoc *pD, SwFrame* &rpF, SwFrame* &rpP, SwPageFrame* &rpPg,
+ SwLayoutFrame* &rpL, std::unique_ptr<SwActualSection> &rpA,
+ SwNodeOffset nNodeIndex, bool bCache );
+ ~SwLayHelper();
+ sal_uLong CalcPageCount();
+ bool CheckInsert( SwNodeOffset nNodeIndex );
+
+ bool CheckInsertPage();
+
+ /// Look for fresh text frames at this (new) page and set them to the right
+ /// position, if they are in the fly cache.
+ void CheckFlyCache( SwPageFrame* pPage )
+ { if( mpImpl && mnFlyIdx < mpImpl->GetFlyCount() ) CheckFlyCache_( pPage ); }
+};
+
+// Contains the data structures that are required to read and write a layout cache.
+#define SW_LAYCACHE_IO_REC_PAGES 'p'
+#define SW_LAYCACHE_IO_REC_PARA 'P'
+#define SW_LAYCACHE_IO_REC_TABLE 'T'
+#define SW_LAYCACHE_IO_REC_FLY 'F'
+
+#define SW_LAYCACHE_IO_VERSION_MAJOR 1
+#define SW_LAYCACHE_IO_VERSION_MINOR 1
+
+class SwLayCacheIoImpl
+{
+private:
+ struct RecTypeSize {
+ sal_uInt8 type;
+ sal_uLong size;
+ RecTypeSize(sal_uInt8 typ, sal_uLong siz) : type(typ), size(siz) {}
+ };
+ std::vector<RecTypeSize> m_aRecords;
+
+ SvStream *m_pStream;
+
+ sal_uLong m_nFlagRecEnd;
+
+ sal_uInt16 m_nMajorVersion;
+ sal_uInt16 m_nMinorVersion;
+
+ bool m_bWriteMode : 1;
+ bool m_bError : 1;
+
+public:
+ SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd );
+
+ /// Get input or output stream
+ SvStream& GetStream() const { return *m_pStream; }
+
+ /// Open a record of type "nType"
+ void OpenRec( sal_uInt8 nType );
+
+ /// Close a record. This skips any unread data that
+ /// remains in the record.
+ void CloseRec();
+
+ /// Return the number of bytes contained in the current record that
+ /// haven't been read by now.
+ sal_uInt32 BytesLeft();
+
+ /// Return the current record's type
+ sal_uInt8 Peek();
+
+ /// Skip the current record
+ void SkipRec();
+
+ /// Open a flag record for reading. The uppermost four bits are flags,
+ /// while the lowermost are the flag record's size. Flag records cannot
+ /// be nested.
+ sal_uInt8 OpenFlagRec();
+
+ /// Open flag record for writing;
+ void OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen );
+
+ /// Close a flag record. Any bytes left are skipped.
+ void CloseFlagRec();
+
+ bool HasError() const { return m_bError; }
+
+ sal_uInt16 GetMajorVersion() const { return m_nMajorVersion; }
+ sal_uInt16 GetMinorVersion() const { return m_nMinorVersion; }
+};
+
+// Stored information about text frames:
+class SwFlyCache : public SwRect // position and size
+{
+public:
+ sal_uLong nOrdNum; ///< Id to recognize text frames
+ sal_uInt16 nPageNum; ///< page number
+ SwFlyCache( sal_uInt16 nP, sal_uLong nO, tools::Long nXL, tools::Long nYL, tools::Long nWL, tools::Long nHL ) :
+ SwRect( nXL, nYL, nWL, nHL ), nOrdNum( nO ), nPageNum( nP ){}
+};
+
+SwLayCacheImpl::SwLayCacheImpl() : m_bUseFlyCache(false) {}
+
+size_t SwLayCacheImpl::GetFlyCount() const { return m_FlyCache.size(); }
+
+SwFlyCache& SwLayCacheImpl::GetFlyCache( size_t nIdx ) { return m_FlyCache[ nIdx ]; }
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/layouter.cxx b/sw/source/core/layout/layouter.cxx
new file mode 100644
index 000000000..57f2ee0ae
--- /dev/null
+++ b/sw/source/core/layout/layouter.cxx
@@ -0,0 +1,483 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <layouter.hxx>
+#include <doc.hxx>
+#include <sectfrm.hxx>
+#include <pagefrm.hxx>
+#include <ftnfrm.hxx>
+#include <txtfrm.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <movedfwdfrmsbyobjpos.hxx>
+#include "objstmpconsiderwrapinfl.hxx"
+#include <osl/diagnose.h>
+
+#define LOOP_DETECT 250
+
+class SwLooping
+{
+ sal_uInt16 mnMinPage;
+ sal_uInt16 mnMaxPage;
+ sal_uInt16 mnCount;
+ sal_uInt16 mnLoopControlStage;
+public:
+ explicit SwLooping( SwPageFrame const * pPage );
+ void Control( SwPageFrame* pPage );
+ void Drastic( SwFrame* pFrame );
+ bool IsLoopingLouieLight() const { return mnCount > LOOP_DETECT - 30; };
+};
+
+class SwEndnoter
+{
+ SwLayouter* m_pMaster;
+ SwSectionFrame* m_pSect;
+ std::unique_ptr<SwFootnoteFrames> m_pEndArr;
+public:
+ explicit SwEndnoter( SwLayouter* pLay )
+ : m_pMaster( pLay ), m_pSect( nullptr ) {}
+ void CollectEndnotes( SwSectionFrame* pSct );
+ void CollectEndnote( SwFootnoteFrame* pFootnote );
+ const SwSectionFrame* GetSect() const { return m_pSect; }
+ void InsertEndnotes();
+ bool HasEndnotes() const { return m_pEndArr && !m_pEndArr->empty(); }
+};
+
+void SwEndnoter::CollectEndnotes( SwSectionFrame* pSct )
+{
+ OSL_ENSURE( pSct, "CollectEndnotes: Which section?" );
+ if( !m_pSect )
+ m_pSect = pSct;
+ else if( pSct != m_pSect )
+ return;
+ m_pSect->CollectEndnotes( m_pMaster );
+}
+
+void SwEndnoter::CollectEndnote( SwFootnoteFrame* pFootnote )
+{
+ if( m_pEndArr && m_pEndArr->end() != std::find( m_pEndArr->begin(), m_pEndArr->end(), pFootnote ) )
+ return;
+
+ if( pFootnote->GetUpper() )
+ {
+ // pFootnote is the master, he incorporates its follows
+ SwFootnoteFrame *pNxt = pFootnote->GetFollow();
+ while ( pNxt )
+ {
+ SwFrame *pCnt = pNxt->ContainsAny();
+ if ( pCnt )
+ {
+ do
+ { SwFrame *pNxtCnt = pCnt->GetNext();
+ pCnt->Cut();
+ pCnt->Paste( pFootnote );
+ pCnt = pNxtCnt;
+ } while ( pCnt );
+ }
+ else
+ {
+ OSL_ENSURE( pNxt->Lower() && pNxt->Lower()->IsSctFrame(),
+ "Endnote without content?" );
+ pNxt->Cut();
+ SwFrame::DestroyFrame(pNxt);
+ }
+ pNxt = pFootnote->GetFollow();
+ }
+ if( pFootnote->GetMaster() )
+ return;
+ pFootnote->Cut();
+ }
+ else if( m_pEndArr )
+ {
+ for (SwFootnoteFrame* pEndFootnote : *m_pEndArr)
+ {
+ if( pEndFootnote->GetAttr() == pFootnote->GetAttr() )
+ {
+ SwFrame::DestroyFrame(pFootnote);
+ return;
+ }
+ }
+ }
+ if( !m_pEndArr )
+ m_pEndArr.reset( new SwFootnoteFrames ); // deleted from the SwLayouter
+ m_pEndArr->push_back( pFootnote );
+}
+
+void SwEndnoter::InsertEndnotes()
+{
+ if( !m_pSect )
+ return;
+ if( !m_pEndArr || m_pEndArr->empty() )
+ {
+ m_pSect = nullptr;
+ return;
+ }
+ OSL_ENSURE( m_pSect->Lower() && m_pSect->Lower()->IsFootnoteBossFrame(),
+ "InsertEndnotes: Where's my column?" );
+ SwFrame* pRef = m_pSect->FindLastContent( SwFindMode::MyLast );
+ SwFootnoteBossFrame *pBoss = pRef ? pRef->FindFootnoteBossFrame()
+ : static_cast<SwFootnoteBossFrame*>(m_pSect->Lower());
+ pBoss->MoveFootnotes_( *m_pEndArr );
+ m_pEndArr.reset();
+ m_pSect = nullptr;
+}
+
+SwLooping::SwLooping( SwPageFrame const * pPage )
+{
+ OSL_ENSURE( pPage, "Where's my page?" );
+ mnMinPage = pPage->GetPhyPageNum();
+ mnMaxPage = mnMinPage;
+ mnCount = 0;
+ mnLoopControlStage = 0;
+}
+
+void SwLooping::Drastic( SwFrame* pFrame )
+{
+ while( pFrame )
+ {
+ pFrame->ValidateThisAndAllLowers( mnLoopControlStage );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+void SwLooping::Control( SwPageFrame* pPage )
+{
+ if( !pPage )
+ return;
+ const sal_uInt16 nNew = pPage->GetPhyPageNum();
+ if (nNew > mnMaxPage)
+ mnMaxPage = nNew;
+ if (nNew < mnMinPage)
+ {
+ mnMinPage = nNew;
+ mnMaxPage = nNew;
+ mnCount = 0;
+ mnLoopControlStage = 0;
+ }
+ else if (nNew > mnMinPage + 2)
+ {
+ mnMinPage = nNew - 2;
+ mnMaxPage = nNew;
+ mnCount = 0;
+ mnLoopControlStage = 0;
+ }
+ else if (++mnCount > LOOP_DETECT)
+ {
+#if OSL_DEBUG_LEVEL > 1
+ static bool bNoLouie = false;
+ if( bNoLouie )
+ return;
+
+ // FME 2007-08-30 #i81146# new loop control
+ OSL_ENSURE( 0 != mnLoopControlStage, "Looping Louie: Stage 1!" );
+ OSL_ENSURE( 1 != mnLoopControlStage, "Looping Louie: Stage 2!!" );
+ OSL_ENSURE( 2 > mnLoopControlStage, "Looping Louie: Stage 3!!!" );
+#endif
+
+ Drastic( pPage->Lower() );
+ if (nNew > mnMinPage && pPage->GetPrev())
+ Drastic( static_cast<SwPageFrame*>(pPage->GetPrev())->Lower() );
+ if (nNew < mnMaxPage && pPage->GetNext())
+ Drastic( static_cast<SwPageFrame*>(pPage->GetNext())->Lower() );
+
+ ++mnLoopControlStage;
+ mnCount = 0;
+ }
+}
+
+SwLayouter::SwLayouter()
+{
+}
+
+SwLayouter::~SwLayouter()
+{
+}
+
+void SwLayouter::CollectEndnotes_( SwSectionFrame* pSect )
+{
+ if( !mpEndnoter )
+ mpEndnoter.reset(new SwEndnoter( this ));
+ mpEndnoter->CollectEndnotes( pSect );
+}
+
+bool SwLayouter::HasEndnotes() const
+{
+ return mpEndnoter->HasEndnotes();
+}
+
+void SwLayouter::CollectEndnote( SwFootnoteFrame* pFootnote )
+{
+ mpEndnoter->CollectEndnote( pFootnote );
+}
+
+void SwLayouter::InsertEndnotes( SwSectionFrame const * pSect )
+{
+ if( !mpEndnoter || mpEndnoter->GetSect() != pSect )
+ return;
+ mpEndnoter->InsertEndnotes();
+}
+
+void SwLayouter::LoopControl( SwPageFrame* pPage )
+{
+ OSL_ENSURE( mpLooping, "Looping: Lost control" );
+ mpLooping->Control( pPage );
+}
+
+void SwLayouter::LoopingLouieLight( const SwDoc& rDoc, const SwTextFrame& rFrame )
+{
+ if ( mpLooping && mpLooping->IsLoopingLouieLight() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ OSL_FAIL( "Looping Louie (Light): Fixating fractious frame" );
+#endif
+ SwLayouter::InsertMovedFwdFrame( rDoc, rFrame, rFrame.FindPageFrame()->GetPhyPageNum() );
+ }
+}
+
+bool SwLayouter::StartLooping( SwPageFrame const * pPage )
+{
+ if( mpLooping )
+ return false;
+ mpLooping.reset(new SwLooping( pPage ));
+ return true;
+}
+
+void SwLayouter::EndLoopControl()
+{
+ mpLooping.reset();
+}
+
+void SwLayouter::CollectEndnotes( SwDoc* pDoc, SwSectionFrame* pSect )
+{
+ assert(pDoc && "No doc, no fun");
+ if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
+ pDoc->getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ pDoc->getIDocumentLayoutAccess().GetLayouter()->CollectEndnotes_( pSect );
+}
+
+bool SwLayouter::Collecting( SwDoc* pDoc, SwSectionFrame const * pSect, SwFootnoteFrame* pFootnote )
+{
+ if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
+ return false;
+ SwLayouter *pLayouter = pDoc->getIDocumentLayoutAccess().GetLayouter();
+ if( pLayouter->mpEndnoter && pLayouter->mpEndnoter->GetSect() && pSect &&
+ ( pLayouter->mpEndnoter->GetSect()->IsAnFollow( pSect ) ||
+ pSect->IsAnFollow( pLayouter->mpEndnoter->GetSect() ) ) )
+ {
+ if( pFootnote )
+ pLayouter->CollectEndnote( pFootnote );
+ return true;
+ }
+ return false;
+}
+
+bool SwLayouter::StartLoopControl( SwDoc* pDoc, SwPageFrame const *pPage )
+{
+ OSL_ENSURE( pDoc, "No doc, no fun" );
+ if( !pDoc->getIDocumentLayoutAccess().GetLayouter() )
+ pDoc->getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ return !pDoc->getIDocumentLayoutAccess().GetLayouter()->mpLooping &&
+ pDoc->getIDocumentLayoutAccess().GetLayouter()->StartLooping( pPage );
+}
+
+// #i28701#
+// methods to manage text frames, which are moved forward by the positioning
+// of its anchored objects
+void SwLayouter::ClearMovedFwdFrames( const SwDoc& _rDoc )
+{
+ if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() &&
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Clear();
+ }
+}
+
+void SwLayouter::InsertMovedFwdFrame( const SwDoc& _rDoc,
+ const SwTextFrame& _rMovedFwdFrameByObjPos,
+ const sal_uInt32 _nToPageNum )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ }
+
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames.reset(
+ new SwMovedFwdFramesByObjPos());
+ }
+
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Insert( _rMovedFwdFrameByObjPos,
+ _nToPageNum );
+}
+
+// #i40155#
+void SwLayouter::RemoveMovedFwdFrame( const SwDoc& _rDoc,
+ const SwTextFrame& _rTextFrame )
+{
+ sal_uInt32 nDummy;
+ if ( SwLayouter::FrameMovedFwdByObjPos( _rDoc, _rTextFrame, nDummy ) )
+ {
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Remove( _rTextFrame );
+ }
+}
+
+bool SwLayouter::FrameMovedFwdByObjPos( const SwDoc& _rDoc,
+ const SwTextFrame& _rTextFrame,
+ sal_uInt32& _ornToPageNum )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ _ornToPageNum = 0;
+ return false;
+ }
+ else if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ _ornToPageNum = 0;
+ return false;
+ }
+ else
+ {
+ return _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->
+ FrameMovedFwdByObjPos( _rTextFrame, _ornToPageNum );
+ }
+}
+
+// #i26945#
+bool SwLayouter::DoesRowContainMovedFwdFrame( const SwDoc& _rDoc,
+ const SwRowFrame& _rRowFrame )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ return false;
+ }
+ else if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames )
+ {
+ return false;
+ }
+ else
+ {
+ return _rDoc.getIDocumentLayoutAccess().GetLayouter()->
+ mpMovedFwdFrames->DoesRowContainMovedFwdFrame( _rRowFrame );
+ }
+}
+
+// #i35911#
+void SwLayouter::ClearObjsTmpConsiderWrapInfluence( const SwDoc& _rDoc )
+{
+ if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() &&
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
+ {
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Clear();
+ }
+}
+
+void SwLayouter::InsertObjForTmpConsiderWrapInfluence(
+ const SwDoc& _rDoc,
+ SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ }
+
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
+ {
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl.reset(
+ new SwObjsMarkedAsTmpConsiderWrapInfluence());
+ }
+
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Insert( _rAnchoredObj );
+}
+
+void SwLayouter::RemoveObjForTmpConsiderWrapInfluence(
+ const SwDoc& _rDoc,
+ SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ return;
+
+ if ( !_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl )
+ return;
+
+ _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpObjsTmpConsiderWrapInfl->Remove( _rAnchoredObj );
+}
+
+
+void LOOPING_LOUIE_LIGHT( bool bCondition, const SwTextFrame& rTextFrame )
+{
+ if ( bCondition )
+ {
+ const SwDoc& rDoc = *rTextFrame.GetAttrSet()->GetDoc();
+ if ( rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(rDoc).getIDocumentLayoutAccess().GetLayouter()->LoopingLouieLight( rDoc, rTextFrame );
+ }
+ }
+}
+
+// #i65250#
+bool SwLayouter::MoveBwdSuppressed( const SwDoc& p_rDoc,
+ const SwFlowFrame& p_rFlowFrame,
+ const SwLayoutFrame& p_rNewUpperFrame )
+{
+ bool bMoveBwdSuppressed( false );
+
+ if ( !p_rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ {
+ const_cast<SwDoc&>(p_rDoc).getIDocumentLayoutAccess().SetLayouter( new SwLayouter() );
+ }
+
+ // create hash map key
+ tMoveBwdLayoutInfoKey aMoveBwdLayoutInfo;
+ aMoveBwdLayoutInfo.mnFrameId = p_rFlowFrame.GetFrame().GetFrameId();
+ aMoveBwdLayoutInfo.mnNewUpperPosX = p_rNewUpperFrame.getFrameArea().Pos().X();
+ aMoveBwdLayoutInfo.mnNewUpperPosY = p_rNewUpperFrame.getFrameArea().Pos().Y();
+ aMoveBwdLayoutInfo.mnNewUpperWidth = p_rNewUpperFrame.getFrameArea().Width();
+ aMoveBwdLayoutInfo.mnNewUpperHeight = p_rNewUpperFrame.getFrameArea().Height();
+ SwRectFnSet aRectFnSet(&p_rNewUpperFrame);
+ const SwFrame* pLastLower( p_rNewUpperFrame.Lower() );
+ while ( pLastLower && pLastLower->GetNext() )
+ {
+ pLastLower = pLastLower->GetNext();
+ }
+ aMoveBwdLayoutInfo.mnFreeSpaceInNewUpper =
+ pLastLower
+ ? aRectFnSet.BottomDist( pLastLower->getFrameArea(), aRectFnSet.GetPrtBottom(p_rNewUpperFrame) )
+ : aRectFnSet.GetHeight(p_rNewUpperFrame.getFrameArea());
+
+ // check for moving backward suppress threshold
+ const sal_uInt16 cMoveBwdCountSuppressThreshold = 20;
+ if ( ++const_cast<SwDoc&>(p_rDoc).getIDocumentLayoutAccess().GetLayouter()->maMoveBwdLayoutInfo[ aMoveBwdLayoutInfo ] >
+ cMoveBwdCountSuppressThreshold )
+ {
+ bMoveBwdSuppressed = true;
+ }
+
+ return bMoveBwdSuppressed;
+}
+
+void SwLayouter::ClearMoveBwdLayoutInfo( const SwDoc& _rDoc )
+{
+ if ( _rDoc.getIDocumentLayoutAccess().GetLayouter() )
+ const_cast<SwDoc&>(_rDoc).getIDocumentLayoutAccess().GetLayouter()->maMoveBwdLayoutInfo.clear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/legacyitem.cxx b/sw/source/core/layout/legacyitem.cxx
new file mode 100644
index 000000000..281b71d83
--- /dev/null
+++ b/sw/source/core/layout/legacyitem.cxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <legacyitem.hxx>
+#include <tools/stream.hxx>
+#include <sal/log.hxx>
+#include <fmtornt.hxx>
+
+namespace legacy::SwFormatVert
+{
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SwFormatVertOrient& rItem, SvStream& rStrm, sal_uInt16 nVersionAbusedAsSize)
+ {
+ SwTwips yPos(0);
+ sal_Int16 orient(0);
+ sal_Int16 relation(0);
+
+ switch (nVersionAbusedAsSize)
+ {
+ // compatibility hack for Table Auto Format: SwTwips is "long" :(
+ // (this means that the file format is platform dependent)
+ case 14:
+ {
+ sal_Int64 n(0);
+ rStrm.ReadInt64(n);
+ yPos = n;
+ break;
+ }
+ case 10:
+ {
+ sal_Int32 n(0);
+ rStrm.ReadInt32(n);
+ yPos = n;
+ break;
+ }
+ default:
+ SAL_WARN("sw.core", "SwFormatVertOrient::Create: unknown size");
+ }
+
+ rStrm.ReadInt16( orient ).ReadInt16( relation );
+
+ rItem.SetPos(yPos);
+ rItem.SetVertOrient(orient);
+ rItem.SetRelationOrient(relation);
+ }
+
+ SvStream& Store(const SwFormatVertOrient& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ if constexpr(sizeof(rItem.GetPos()) >= 8)
+ rStrm.WriteInt64(rItem.GetPos());
+ else
+ rStrm.WriteInt32(rItem.GetPos());
+ rStrm.WriteInt16(rItem.GetVertOrient()).WriteInt16(rItem.GetRelationOrient());
+ return rStrm;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx b/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
new file mode 100644
index 000000000..7aba4b74a
--- /dev/null
+++ b/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <movedfwdfrmsbyobjpos.hxx>
+#include <txtfrm.hxx>
+#include <rowfrm.hxx>
+#include <pagefrm.hxx>
+#include <calbck.hxx>
+#include <ndtxt.hxx>
+
+SwMovedFwdFramesByObjPos::SwMovedFwdFramesByObjPos()
+{
+}
+
+SwMovedFwdFramesByObjPos::~SwMovedFwdFramesByObjPos()
+{
+ Clear();
+}
+
+void SwMovedFwdFramesByObjPos::Insert( const SwTextFrame& _rMovedFwdFrameByObjPos,
+ const sal_uInt32 _nToPageNum )
+{
+ maMovedFwdFrames.emplace(_rMovedFwdFrameByObjPos.GetTextNodeFirst(), _nToPageNum);
+}
+
+void SwMovedFwdFramesByObjPos::Remove( const SwTextFrame& _rTextFrame )
+{
+ maMovedFwdFrames.erase(_rTextFrame.GetTextNodeFirst());
+}
+
+bool SwMovedFwdFramesByObjPos::FrameMovedFwdByObjPos( const SwTextFrame& _rTextFrame,
+ sal_uInt32& _ornToPageNum ) const
+{
+ // sw_redlinehide: assumption: this wants to uniquely identify all
+ // SwTextFrame belonging to the same paragraph, so just use first one as key
+ auto aIter = maMovedFwdFrames.find( _rTextFrame.GetTextNodeFirst() );
+ if ( maMovedFwdFrames.end() != aIter )
+ {
+ _ornToPageNum = (*aIter).second;
+ return true;
+ }
+
+ return false;
+}
+
+// #i26945#
+bool SwMovedFwdFramesByObjPos::DoesRowContainMovedFwdFrame( const SwRowFrame& _rRowFrame ) const
+{
+ bool bDoesRowContainMovedFwdFrame( false );
+
+ const sal_uInt32 nPageNumOfRow = _rRowFrame.FindPageFrame()->GetPhyPageNum();
+
+ for ( const auto & rEntry : maMovedFwdFrames )
+ {
+ if ( rEntry.second >= nPageNumOfRow )
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aFrameIter(*rEntry.first);
+ for( SwTextFrame* pTextFrame = aFrameIter.First(); pTextFrame; pTextFrame = aFrameIter.Next() )
+ {
+ // #115759# - assure that found text frame
+ // is the first one.
+ if ( _rRowFrame.IsAnLower( pTextFrame ) && !pTextFrame->GetIndPrev() )
+ {
+ bDoesRowContainMovedFwdFrame = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return bDoesRowContainMovedFwdFrame;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/newfrm.cxx b/sw/source/core/layout/newfrm.cxx
new file mode 100644
index 000000000..c09c55779
--- /dev/null
+++ b/sw/source/core/layout/newfrm.cxx
@@ -0,0 +1,618 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+#include <drawdoc.hxx>
+#include <fmtpdsc.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <dflyobj.hxx>
+#include <frmtool.hxx>
+#include "virtoutp.hxx"
+#include <notxtfrm.hxx>
+#include <pagedesc.hxx>
+#include <viewimp.hxx>
+#include <hints.hxx>
+#include <viewopt.hxx>
+#include <set>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <DocumentLayoutManager.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <ndindex.hxx>
+
+SwLayVout *SwRootFrame::s_pVout = nullptr;
+bool SwRootFrame::s_isInPaint = false;
+bool SwRootFrame::s_isNoVirDev = false;
+
+SwCache *SwFrame::spCache = nullptr;
+
+static tools::Long FirstMinusSecond( tools::Long nFirst, tools::Long nSecond )
+ { return nFirst - nSecond; }
+static tools::Long SecondMinusFirst( tools::Long nFirst, tools::Long nSecond )
+ { return nSecond - nFirst; }
+static tools::Long SwIncrement( tools::Long nA, tools::Long nAdd )
+ { return nA + nAdd; }
+static tools::Long SwDecrement( tools::Long nA, tools::Long nSub )
+ { return nA - nSub; }
+
+static SwRectFnCollection aHorizontal = {
+ /*.fnGetTop =*/&SwRect::Top_,
+ /*.fnGetBottom =*/&SwRect::Bottom_,
+ /*.fnGetLeft =*/&SwRect::Left_,
+ /*.fnGetRight =*/&SwRect::Right_,
+ /*.fnGetWidth =*/&SwRect::Width_,
+ /*.fnGetHeight =*/&SwRect::Height_,
+ /*.fnGetPos =*/&SwRect::TopLeft,
+ /*.fnGetSize =*/&SwRect::Size_,
+
+ /*.fnSetTop =*/&SwRect::Top_,
+ /*.fnSetBottom =*/&SwRect::Bottom_,
+ /*.fnSetLeft =*/&SwRect::Left_,
+ /*.fnSetRight =*/&SwRect::Right_,
+ /*.fnSetWidth =*/&SwRect::Width_,
+ /*.fnSetHeight =*/&SwRect::Height_,
+
+ /*.fnSubTop =*/&SwRect::SubTop,
+ /*.fnAddBottom =*/&SwRect::AddBottom,
+ /*.fnSubLeft =*/&SwRect::SubLeft,
+ /*.fnAddRight =*/&SwRect::AddRight,
+ /*.fnAddWidth =*/&SwRect::AddWidth,
+ /*.fnAddHeight =*/&SwRect::AddHeight,
+
+ /*.fnSetPosX =*/&SwRect::SetPosX,
+ /*.fnSetPosY =*/&SwRect::SetPosY,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetTopMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetRightMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetLeftRightMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtTop,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtBottom,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtRight,
+ /*.fnTopDist =*/&SwRect::GetTopDistance,
+ /*.fnBottomDist =*/&SwRect::GetBottomDistance,
+ /*.fnLeftDist =*/&SwRect::GetLeftDistance,
+ /*.fnRightDist =*/&SwRect::GetRightDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMaxBottom,
+ /*.fnOverStep =*/&SwRect::OverStepBottom,
+
+ /*.fnSetPos =*/&SwRect::SetUpperLeftCorner,
+ /*.fnMakePos =*/&SwFrame::MakeBelowPos,
+ /*.fnXDiff =*/&FirstMinusSecond,
+ /*.fnYDiff =*/&FirstMinusSecond,
+ /*.fnXInc =*/&SwIncrement,
+ /*.fnYInc =*/&o3tl::saturating_add<tools::Long>,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetLeftAndWidth,
+ /*.fnSetTopAndHeight =*/&SwRect::SetTopAndHeight
+};
+
+static SwRectFnCollection aVertical = {
+ /*.fnGetTop =*/&SwRect::Right_,
+ /*.fnGetBottom =*/&SwRect::Left_,
+ /*.fnGetLeft =*/&SwRect::Top_,
+ /*.fnGetRight =*/&SwRect::Bottom_,
+ /*.fnGetWidth =*/&SwRect::Height_,
+ /*.fnGetHeight =*/&SwRect::Width_,
+ /*.fnGetPos =*/&SwRect::TopRight,
+ /*.fnGetSize =*/&SwRect::SwappedSize,
+
+ /*.fnSetTop =*/&SwRect::Right_,
+ /*.fnSetBottom =*/&SwRect::Left_,
+ /*.fnSetLeft =*/&SwRect::Top_,
+ /*.fnSetRight =*/&SwRect::Bottom_,
+ /*.fnSetWidth =*/&SwRect::Height_,
+ /*.fnSetHeight =*/&SwRect::Width_,
+
+ /*.fnSubTop =*/&SwRect::AddRight,
+ /*.fnAddBottom =*/&SwRect::SubLeft,
+ /*.fnSubLeft =*/&SwRect::SubTop,
+ /*.fnAddRight =*/&SwRect::AddBottom,
+ /*.fnAddWidth =*/&SwRect::AddHeight,
+ /*.fnAddHeight =*/&SwRect::AddWidth,
+
+ /*.fnSetPosX =*/&SwRect::SetPosY,
+ /*.fnSetPosY =*/&SwRect::SetPosX,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetRightMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetTopMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetRightLeftMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtRight,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtTop,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtBottom,
+ /*.fnTopDist =*/&SwRect::GetRightDistance,
+ /*.fnBottomDist =*/&SwRect::GetLeftDistance,
+ /*.fnLeftDist =*/&SwRect::GetTopDistance,
+ /*.fnRightDist =*/&SwRect::GetBottomDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMinLeft,
+ /*.fnOverStep =*/&SwRect::OverStepLeft,
+
+ /*.fnSetPos =*/&SwRect::SetUpperRightCorner,
+ /*.fnMakePos =*/&SwFrame::MakeLeftPos,
+ /*.fnXDiff =*/&FirstMinusSecond,
+ /*.fnYDiff =*/&SecondMinusFirst,
+ /*.fnXInc =*/&SwIncrement,
+ /*.fnYInc =*/&SwDecrement,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetTopAndHeight,
+ /*.fnSetTopAndHeight =*/&SwRect::SetRightAndWidth
+};
+
+static SwRectFnCollection aVerticalLeftToRight = {
+ /*.fnGetTop =*/&SwRect::Left_,
+ /*.fnGetBottom =*/&SwRect::Right_,
+ /*.fnGetLeft =*/&SwRect::Top_,
+ /*.fnGetRight =*/&SwRect::Bottom_,
+ /*.fnGetWidth =*/&SwRect::Height_,
+ /*.fnGetHeight =*/&SwRect::Width_,
+ /*.fnGetPos =*/&SwRect::TopLeft,
+ /*.fnGetSize =*/&SwRect::SwappedSize,
+
+ /*.fnSetTop =*/&SwRect::Left_,
+ /*.fnSetBottom =*/&SwRect::Right_,
+ /*.fnSetLeft =*/&SwRect::Top_,
+ /*.fnSetRight =*/&SwRect::Bottom_,
+ /*.fnSetWidth =*/&SwRect::Height_,
+ /*.fnSetHeight =*/&SwRect::Width_,
+
+ /*.fnSubTop =*/&SwRect::SubLeft,
+ /*.fnAddBottom =*/&SwRect::AddRight,
+ /*.fnSubLeft =*/&SwRect::SubTop,
+ /*.fnAddRight =*/&SwRect::AddBottom,
+ /*.fnAddWidth =*/&SwRect::AddHeight,
+ /*.fnAddHeight =*/&SwRect::AddWidth,
+
+ /*.fnSetPosX =*/&SwRect::SetPosY,
+ /*.fnSetPosY =*/&SwRect::SetPosX,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetRightMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetTopMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetLeftRightMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtRight,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtTop,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtBottom,
+ /*.fnTopDist =*/&SwRect::GetLeftDistance,
+ /*.fnBottomDist =*/&SwRect::GetRightDistance,
+ /*.fnLeftDist =*/&SwRect::GetTopDistance,
+ /*.fnRightDist =*/&SwRect::GetBottomDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMaxRight,
+ /*.fnOverStep =*/&SwRect::OverStepRight,
+
+ /*.fnSetPos =*/&SwRect::SetUpperLeftCorner,
+ /*.fnMakePos =*/&SwFrame::MakeRightPos,
+ /*.fnXDiff =*/&FirstMinusSecond,
+ /*.fnYDiff =*/&FirstMinusSecond,
+ /*.fnXInc =*/&SwIncrement,
+ /*.fnYInc =*/&SwIncrement,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetTopAndHeight,
+ /*.fnSetTopAndHeight =*/&SwRect::SetLeftAndWidth
+};
+
+/**
+ * This is the same as horizontal, but rotated counter-clockwise by 90 degrees.
+ * This means logical top is physical left, bottom is right, left is bottom,
+ * finally right is top. Values map from logical to physical.
+ */
+static SwRectFnCollection aVerticalLeftToRightBottomToTop = {
+ /*.fnGetTop =*/&SwRect::Left_,
+ /*.fnGetBottom =*/&SwRect::Right_,
+ /*.fnGetLeft =*/&SwRect::Bottom_,
+ /*.fnGetRight =*/&SwRect::Top_,
+ /*.fnGetWidth =*/&SwRect::Height_,
+ /*.fnGetHeight =*/&SwRect::Width_,
+ /*.fnGetPos =*/&SwRect::BottomLeft,
+ /*.fnGetSize =*/&SwRect::SwappedSize,
+
+ /*.fnSetTop =*/&SwRect::Left_,
+ /*.fnSetBottom =*/&SwRect::Right_,
+ /*.fnSetLeft =*/&SwRect::Bottom_,
+ /*.fnSetRight =*/&SwRect::Top_,
+ /*.fnSetWidth =*/&SwRect::Height_,
+ /*.fnSetHeight =*/&SwRect::Width_,
+
+ /*.fnSubTop =*/&SwRect::SubLeft,
+ /*.fnAddBottom =*/&SwRect::AddRight,
+ /*.fnSubLeft =*/&SwRect::AddBottom,
+ /*.fnAddRight =*/&SwRect::SubTop,
+ /*.fnAddWidth =*/&SwRect::AddHeight,
+ /*.fnAddHeight =*/&SwRect::AddWidth,
+
+ /*.fnSetPosX =*/&SwRect::SetPosY,
+ /*.fnSetPosY =*/&SwRect::SetPosX,
+
+ /*.fnGetTopMargin =*/&SwFrame::GetLeftMargin,
+ /*.fnGetBottomMargin =*/&SwFrame::GetRightMargin,
+ /*.fnGetLeftMargin =*/&SwFrame::GetBottomMargin,
+ /*.fnGetRightMargin =*/&SwFrame::GetTopMargin,
+ /*.fnSetXMargins =*/&SwFrame::SetTopBottomMargins,
+ /*.fnSetYMargins =*/&SwFrame::SetLeftRightMargins,
+ /*.fnGetPrtTop =*/&SwFrame::GetPrtLeft,
+ /*.fnGetPrtBottom =*/&SwFrame::GetPrtRight,
+ /*.fnGetPrtLeft =*/&SwFrame::GetPrtBottom,
+ /*.fnGetPrtRight =*/&SwFrame::GetPrtTop,
+ /*.fnTopDist =*/&SwRect::GetLeftDistance,
+ /*.fnBottomDist =*/&SwRect::GetRightDistance,
+ /*.fnLeftDist =*/&SwRect::GetBottomDistance,
+ /*.fnRightDist =*/&SwRect::GetTopDistance,
+ /*.fnSetLimit =*/&SwFrame::SetMaxRight,
+ /*.fnOverStep =*/&SwRect::OverStepRight,
+
+ /*.fnSetPos =*/&SwRect::SetLowerLeftCorner,
+ /*.fnMakePos =*/&SwFrame::MakeRightPos,
+ /*.fnXDiff =*/&SecondMinusFirst,
+ /*.fnYDiff =*/&FirstMinusSecond,
+ /*.fnXInc =*/&SwDecrement,
+ /*.fnYInc =*/&SwIncrement,
+
+ /*.fnSetLeftAndWidth =*/&SwRect::SetBottomAndHeight,
+ /*.fnSetTopAndHeight =*/&SwRect::SetLeftAndWidth
+};
+
+SwRectFn fnRectHori = &aHorizontal;
+SwRectFn fnRectVert = &aVertical;
+SwRectFn fnRectVertL2R = &aVerticalLeftToRight;
+SwRectFn fnRectVertL2RB2T = &aVerticalLeftToRightBottomToTop;
+
+// #i65250#
+sal_uInt32 SwFrameAreaDefinition::snLastFrameId=0;
+
+
+void FrameInit()
+{
+ SwRootFrame::s_pVout = new SwLayVout();
+ SwCache *pNew = new SwCache( 100
+#ifdef DBG_UTIL
+ , "static SwBorderAttrs::pCache"
+#endif
+ );
+ SwFrame::SetCache( pNew );
+}
+
+void FrameFinit()
+{
+#if OSL_DEBUG_LEVEL > 0
+ // The cache may only contain null pointers at this time.
+ for( size_t n = SwFrame::GetCachePtr()->size(); n; )
+ if( (*SwFrame::GetCachePtr())[ --n ] )
+ {
+ SwCacheObj* pObj = (*SwFrame::GetCachePtr())[ n ];
+ OSL_ENSURE( !pObj, "Who didn't deregister?");
+ }
+#endif
+ delete SwRootFrame::s_pVout;
+ delete SwFrame::GetCachePtr();
+}
+
+// RootFrame::Everything that belongs to CurrShell
+
+CurrShell::CurrShell( SwViewShell *pNew )
+{
+ OSL_ENSURE( pNew, "insert 0-Shell?" );
+ pRoot = pNew->GetLayout();
+ if ( pRoot )
+ {
+ pPrev = pRoot->mpCurrShell;
+ pRoot->mpCurrShell = pNew;
+ pRoot->mpCurrShells->insert( this );
+ }
+ else
+ pPrev = nullptr;
+}
+
+CurrShell::~CurrShell()
+{
+ if ( pRoot )
+ {
+ pRoot->mpCurrShells->erase( this );
+ if ( pPrev )
+ pRoot->mpCurrShell = pPrev;
+ if ( pRoot->mpCurrShells->empty() && pRoot->mpWaitingCurrShell )
+ {
+ pRoot->mpCurrShell = pRoot->mpWaitingCurrShell;
+ pRoot->mpWaitingCurrShell = nullptr;
+ }
+ }
+}
+
+void SetShell( SwViewShell *pSh )
+{
+ SwRootFrame *pRoot = pSh->GetLayout();
+ if ( pRoot->mpCurrShells->empty() )
+ pRoot->mpCurrShell = pSh;
+ else
+ pRoot->mpWaitingCurrShell = pSh;
+}
+
+void SwRootFrame::DeRegisterShell( SwViewShell *pSh )
+{
+ // Activate some shell if possible
+ if ( mpCurrShell == pSh )
+ {
+ mpCurrShell = nullptr;
+ for(SwViewShell& rShell : pSh->GetRingContainer())
+ {
+ if(&rShell != pSh)
+ {
+ mpCurrShell = &rShell;
+ break;
+ }
+ }
+ }
+
+ // Doesn't matter anymore
+ if ( mpWaitingCurrShell == pSh )
+ mpWaitingCurrShell = nullptr;
+
+ // Remove references
+ for ( CurrShell *pC : *mpCurrShells )
+ {
+ if (pC->pPrev == pSh)
+ pC->pPrev = nullptr;
+ }
+}
+
+void InitCurrShells( SwRootFrame *pRoot )
+{
+ pRoot->mpCurrShells.reset( new SwCurrShells );
+}
+
+/*
+|* The RootFrame requests an own FrameFormat from the document, which it is
+|* going to delete again in the dtor. The own FrameFormat is derived from
+|* the passed FrameFormat.
+|*/
+SwRootFrame::SwRootFrame( SwFrameFormat *pFormat, SwViewShell * pSh ) :
+ SwLayoutFrame( pFormat->GetDoc()->MakeFrameFormat(
+ "Root", pFormat ), nullptr ),
+ mnViewWidth( -1 ),
+ mnColumns( 0 ),
+ mbBookMode( false ),
+ mbSidebarChanged( false ),
+ mbNeedGrammarCheck( false ),
+ mbCheckSuperfluous( false ),
+ mbIdleFormat( true ),
+ mbBrowseWidthValid( false ),
+ mbTurboAllowed( true ),
+ mbAssertFlyPages( true ),
+ mbTableUpdateInProgress( false ),
+ mbIsVirtPageNum( false ),
+ mbIsNewLayout( true ),
+ mbCallbackActionEnabled ( false ),
+ mbLayoutFreezed ( false ),
+ mbHideRedlines(pFormat->GetDoc()->GetDocumentRedlineManager().IsHideRedlines()),
+ m_FieldmarkMode(pSh->GetViewOptions()->IsFieldName()
+ ? sw::FieldmarkMode::ShowCommand
+ : sw::FieldmarkMode::ShowResult),
+ mnBrowseWidth(MIN_BROWSE_WIDTH),
+ mpTurbo( nullptr ),
+ mpLastPage( nullptr ),
+ mpCurrShell( pSh ),
+ mpWaitingCurrShell( nullptr ),
+ mpDrawPage( nullptr ),
+ mnPhyPageNums( 0 ),
+ mnAccessibleShells( 0 )
+{
+ mnFrameType = SwFrameType::Root;
+ setRootFrame( this );
+}
+
+void SwRootFrame::Init( SwFrameFormat* pFormat )
+{
+ InitCurrShells( this );
+
+ IDocumentTimerAccess& rTimerAccess = pFormat->getIDocumentTimerAccess();
+ IDocumentLayoutAccess& rLayoutAccess = pFormat->getIDocumentLayoutAccess();
+ IDocumentFieldsAccess& rFieldsAccess = pFormat->getIDocumentFieldsAccess();
+ const IDocumentSettingAccess& rSettingAccess = pFormat->getIDocumentSettingAccess();
+ rTimerAccess.StopIdling();
+ // For creating the Flys by MakeFrames()
+ rLayoutAccess.SetCurrentViewShell( GetCurrShell() );
+ mbCallbackActionEnabled = false; // needs to be set to true before leaving!
+
+ SwDrawModel* pMd = pFormat->getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pMd )
+ {
+ // Disable "multiple layout"
+ mpDrawPage = pMd->GetPage(0);
+
+ mpDrawPage->SetSize( getFrameArea().SSize() );
+ }
+
+ // Initialize the layout: create pages, link content with Content etc.
+ // First, initialize some stuff, then get hold of the first
+ // node (which will be needed for the PageDesc).
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwNodeIndex aIndex( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode() );
+ SwContentNode *pNode = pDoc->GetNodes().GoNextSection( &aIndex, true, false );
+ // #123067# pNode = 0 can really happen
+ SwTableNode *pTableNd= pNode ? pNode->FindTableNode() : nullptr;
+
+ // Get hold of PageDesc (either via FrameFormat of the first node or the initial one).
+ SwPageDesc *pDesc = nullptr;
+ ::std::optional<sal_uInt16> oPgNum;
+
+ if ( pTableNd )
+ {
+ const SwFormatPageDesc &rDesc = pTableNd->GetTable().GetFrameFormat()->GetPageDesc();
+ pDesc = const_cast<SwPageDesc*>(rDesc.GetPageDesc());
+ //#19104# respect the page number offset!!
+ oPgNum = rDesc.GetNumOffset();
+ if (oPgNum)
+ mbIsVirtPageNum = true;
+ }
+ else if ( pNode )
+ {
+ const SwFormatPageDesc &rDesc = pNode->GetSwAttrSet().GetPageDesc();
+ pDesc = const_cast<SwPageDesc*>(rDesc.GetPageDesc());
+ //#19104# respect the page number offset!!
+ oPgNum = rDesc.GetNumOffset();
+ if (oPgNum)
+ mbIsVirtPageNum = true;
+ }
+ else
+ mbIsVirtPageNum = false;
+ if ( !pDesc )
+ pDesc = &pDoc->GetPageDesc( 0 );
+
+ // Create a page and put it in the layout
+ // The first page is always a right-page and always a first-page
+ SwPageFrame* pPage = ::InsertNewPage(
+ *pDesc, /*pUpper=*/this, /*isRightPage=*/true, /*bFirst=*/true, /*bInsertEmpty=*/false,
+ /*bFootnote=*/false, /*pSibling=*/nullptr, /*bVeryFirstPage=*/true);
+
+ // Find the first page in the Bodytext section.
+ SwLayoutFrame *pLay = pPage->FindBodyCont();
+ while( pLay->Lower() )
+ pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
+
+ SwNodeIndex aTmp( *pDoc->GetNodes().GetEndOfContent().StartOfSectionNode(), 1 );
+ ::InsertCnt_( pLay, pDoc, aTmp.GetIndex(), true );
+
+ if( rSettingAccess.get(DocumentSettingId::GLOBAL_DOCUMENT) )
+ rFieldsAccess.UpdateRefFields();
+ //b6433357: Update page fields after loading
+ if ( !mpCurrShell || !mpCurrShell->Imp()->IsUpdateExpFields() )
+ {
+ SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
+ rFieldsAccess.UpdatePageFields( &aMsgHint );
+ }
+
+ rTimerAccess.StartIdling();
+ mbCallbackActionEnabled = true;
+
+ SwViewShell *pViewSh = GetCurrShell();
+ if (pViewSh)
+ mbNeedGrammarCheck = pViewSh->GetViewOptions()->IsOnlineSpell();
+}
+
+void SwRootFrame::DestroyImpl()
+{
+ mbTurboAllowed = false;
+ mpTurbo = nullptr;
+
+ SwFrameFormat *pRegisteredInNonConst = static_cast<SwFrameFormat*>(GetDep());
+ if ( pRegisteredInNonConst )
+ {
+ SwDoc *pDoc = pRegisteredInNonConst->GetDoc();
+ pDoc->DelFrameFormat( pRegisteredInNonConst );
+ // do this before calling RemoveFootnotes() because footnotes
+ // can contain anchored objects
+ pDoc->GetDocumentLayoutManager().ClearSwLayouterEntries();
+ }
+
+ mpDestroy.reset();
+
+ // Remove references
+ for ( auto& rpCurrShell : *mpCurrShells )
+ rpCurrShell->pRoot = nullptr;
+
+ mpCurrShells.reset();
+
+ // Some accessible shells are left => problems on second SwFrame::Destroy call
+ assert(0 == mnAccessibleShells);
+
+ // fdo#39510 crash on document close with footnotes
+ // Object ownership in writer and esp. in layout are a mess: Before the
+ // document/layout split SwDoc and SwRootFrame were essentially one object
+ // and magically/uncleanly worked around their common destruction by call
+ // to SwDoc::IsInDtor() -- even from the layout. As of now destruction of
+ // the layout proceeds forward through the frames. Since SwTextFootnote::DelFrames
+ // also searches backwards to find the master of footnotes, they must be
+ // considered to be owned by the SwRootFrame and also be destroyed here,
+ // before tearing down the (now footnote free) rest of the layout.
+ RemoveFootnotes(nullptr, false, true);
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwRootFrame::~SwRootFrame()
+{
+}
+
+void SwRootFrame::RemoveMasterObjs( SdrPage *pPg )
+{
+ // Remove all master objects from the Page. But don't delete!
+ for( size_t i = pPg ? pPg->GetObjCount() : 0; i; )
+ {
+ SdrObject* pObj = pPg->GetObj( --i );
+ if( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr )
+ pPg->RemoveObject( i );
+ }
+}
+
+void SwRootFrame::AllCheckPageDescs() const
+{
+ if ( !IsLayoutFreezed() )
+ CheckPageDescs( const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower())) );
+}
+
+void SwRootFrame::AllInvalidateAutoCompleteWords() const
+{
+ SwPageFrame *pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower()));
+ while ( pPage )
+ {
+ pPage->InvalidateAutoCompleteWords();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+}
+
+void SwRootFrame::AllAddPaintRect() const
+{
+ GetCurrShell()->AddPaintRect( getFrameArea() );
+}
+
+void SwRootFrame::AllRemoveFootnotes()
+{
+ RemoveFootnotes();
+}
+
+void SwRootFrame::AllInvalidateSmartTagsOrSpelling(bool bSmartTags) const
+{
+ SwPageFrame *pPage = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(Lower()));
+ while ( pPage )
+ {
+ if ( bSmartTags )
+ pPage->InvalidateSmartTags();
+
+ pPage->InvalidateSpelling();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformatter.cxx b/sw/source/core/layout/objectformatter.cxx
new file mode 100644
index 000000000..15ca544a2
--- /dev/null
+++ b/sw/source/core/layout/objectformatter.cxx
@@ -0,0 +1,478 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "objectformattertxtfrm.hxx"
+#include "objectformatterlayfrm.hxx"
+#include <anchoreddrawobject.hxx>
+#include <sortedobjs.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <flyfrms.hxx>
+#include <txtfrm.hxx>
+#include <layact.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <osl/diagnose.h>
+
+#include <vector>
+
+// --> #i26945# - Additionally the type of the anchor text frame
+// is collected - by type is meant 'master' or 'follow'.
+class SwPageNumAndTypeOfAnchors
+{
+ private:
+ struct tEntry
+ {
+ SwAnchoredObject* mpAnchoredObj;
+ sal_uInt32 mnPageNumOfAnchor;
+ bool mbAnchoredAtMaster;
+ };
+
+ std::vector< tEntry > maObjList;
+
+ public:
+ SwPageNumAndTypeOfAnchors()
+ {
+ }
+
+ void Collect( SwAnchoredObject& _rAnchoredObj )
+ {
+ tEntry aNewEntry;
+ aNewEntry.mpAnchoredObj = &_rAnchoredObj;
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = _rAnchoredObj.FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor )
+ {
+ aNewEntry.mnPageNumOfAnchor = pPageFrameOfAnchor->GetPhyPageNum();
+ }
+ else
+ {
+ aNewEntry.mnPageNumOfAnchor = 0;
+ }
+ // --> #i26945# - collect type of anchor
+ SwTextFrame* pAnchorCharFrame = _rAnchoredObj.FindAnchorCharFrame();
+ if ( pAnchorCharFrame )
+ {
+ aNewEntry.mbAnchoredAtMaster = !pAnchorCharFrame->IsFollow();
+ }
+ else
+ {
+ aNewEntry.mbAnchoredAtMaster = true;
+ }
+ maObjList.push_back( aNewEntry );
+ }
+
+ SwAnchoredObject* operator[]( sal_uInt32 _nIndex )
+ {
+ return maObjList[_nIndex].mpAnchoredObj;
+ }
+
+ sal_uInt32 GetPageNum( sal_uInt32 _nIndex ) const
+ {
+ return maObjList[_nIndex].mnPageNumOfAnchor;
+ }
+
+ // --> #i26945#
+ bool AnchoredAtMaster( sal_uInt32 _nIndex )
+ {
+ return maObjList[_nIndex].mbAnchoredAtMaster;
+ }
+
+ sal_uInt32 Count() const
+ {
+ return maObjList.size();
+ }
+};
+
+SwObjectFormatter::SwObjectFormatter( const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction,
+ const bool _bCollectPgNumOfAnchors )
+ : mrPageFrame( _rPageFrame ),
+ mbConsiderWrapOnObjPos( _rPageFrame.GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) ),
+ mpLayAction( _pLayAction ),
+ // --> #i26945#
+ mpPgNumAndTypeOfAnchors( _bCollectPgNumOfAnchors ? new SwPageNumAndTypeOfAnchors() : nullptr )
+{
+}
+
+SwObjectFormatter::~SwObjectFormatter()
+{
+}
+
+std::unique_ptr<SwObjectFormatter> SwObjectFormatter::CreateObjFormatter(
+ SwFrame& _rAnchorFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ std::unique_ptr<SwObjectFormatter> pObjFormatter;
+ if ( _rAnchorFrame.IsTextFrame() )
+ {
+ pObjFormatter = SwObjectFormatterTextFrame::CreateObjFormatter(
+ static_cast<SwTextFrame&>(_rAnchorFrame),
+ _rPageFrame, _pLayAction );
+ }
+ else if ( _rAnchorFrame.IsLayoutFrame() )
+ {
+ pObjFormatter = SwObjectFormatterLayFrame::CreateObjFormatter(
+ static_cast<SwLayoutFrame&>(_rAnchorFrame),
+ _rPageFrame, _pLayAction );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatter::CreateObjFormatter(..)> - unexpected type of anchor frame" );
+ }
+
+ return pObjFormatter;
+}
+
+/** method to format all floating screen objects at the given anchor frame
+*/
+bool SwObjectFormatter::FormatObjsAtFrame( SwFrame& _rAnchorFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ bool bSuccess( true );
+
+ // create corresponding object formatter
+ std::unique_ptr<SwObjectFormatter> pObjFormatter =
+ SwObjectFormatter::CreateObjFormatter( _rAnchorFrame, _rPageFrame, _pLayAction );
+
+ if ( pObjFormatter )
+ {
+ // format anchored floating screen objects
+ bSuccess = pObjFormatter->DoFormatObjs();
+ }
+
+ return bSuccess;
+}
+
+/** method to format a given floating screen object
+*/
+bool SwObjectFormatter::FormatObj( SwAnchoredObject& _rAnchoredObj,
+ SwFrame* _pAnchorFrame,
+ const SwPageFrame* _pPageFrame )
+{
+ bool bSuccess( true );
+
+ OSL_ENSURE( _pAnchorFrame || _rAnchoredObj.GetAnchorFrame(),
+ "<SwObjectFormatter::FormatObj(..)> - missing anchor frame" );
+ SwFrame& rAnchorFrame = _pAnchorFrame ? *_pAnchorFrame : *(_rAnchoredObj.AnchorFrame());
+
+ OSL_ENSURE( _pPageFrame || rAnchorFrame.FindPageFrame(),
+ "<SwObjectFormatter::FormatObj(..)> - missing page frame" );
+ const SwPageFrame& rPageFrame = _pPageFrame ? *_pPageFrame : *(rAnchorFrame.FindPageFrame());
+
+ // create corresponding object formatter
+ std::unique_ptr<SwObjectFormatter> pObjFormatter =
+ SwObjectFormatter::CreateObjFormatter( rAnchorFrame, rPageFrame, nullptr/*_pLayAction*/ );
+
+ if ( pObjFormatter )
+ {
+ // format given floating screen object
+ // --> #i40147# - check for moved forward anchor frame
+ bSuccess = pObjFormatter->DoFormatObj( _rAnchoredObj, true );
+ }
+
+ return bSuccess;
+}
+
+/** helper method for method <FormatObj_(..)> - performs the intrinsic format
+ of the layout of the given layout frame and all its lower layout frames.
+
+ #i28701#
+ IMPORTANT NOTE:
+ Method corresponds to methods <SwLayAction::FormatLayoutFly(..)> and
+ <SwLayAction::FormatLayout(..)>. Thus, its code for the formatting have
+ to be synchronised.
+*/
+void SwObjectFormatter::FormatLayout_( SwLayoutFrame& _rLayoutFrame )
+{
+ _rLayoutFrame.Calc(_rLayoutFrame.getRootFrame()->GetCurrShell()->GetOut());
+
+ SwFrame* pLowerFrame = _rLayoutFrame.Lower();
+ while ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsLayoutFrame() )
+ {
+ FormatLayout_( *static_cast<SwLayoutFrame*>(pLowerFrame) );
+ }
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+}
+
+/** helper method for method <FormatObj_(..)> - performs the intrinsic
+ format of the content of the given floating screen object.
+
+ #i28701#
+*/
+void SwObjectFormatter::FormatObjContent( SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !_rAnchoredObj.DynCastFlyFrame() )
+ {
+ // only Writer fly frames have content
+ return;
+ }
+
+ SwFlyFrame& rFlyFrame = static_cast<SwFlyFrame&>(_rAnchoredObj);
+ SwContentFrame* pContent = rFlyFrame.ContainsContent();
+
+ while ( pContent )
+ {
+ // format content
+ pContent->OptCalc();
+
+ // format floating screen objects at content text frame
+ // #i23129#, #i36347# - pass correct page frame to
+ // the object formatter
+ if ( pContent->IsTextFrame() &&
+ !SwObjectFormatter::FormatObjsAtFrame( *pContent,
+ *(pContent->FindPageFrame()),
+ GetLayAction() ) )
+ {
+ // restart format with first content
+ pContent = rFlyFrame.ContainsContent();
+ continue;
+ }
+
+ // continue with next content
+ pContent = pContent->GetNextContentFrame();
+ }
+}
+
+/** performs the intrinsic format of a given floating screen object and its content.
+
+ #i28701#
+*/
+void SwObjectFormatter::FormatObj_( SwAnchoredObject& _rAnchoredObj )
+{
+ // collect anchor object and its 'anchor' page number, if requested
+ if ( mpPgNumAndTypeOfAnchors )
+ {
+ mpPgNumAndTypeOfAnchors->Collect( _rAnchoredObj );
+ }
+
+ if ( auto pFlyFrame = _rAnchoredObj.DynCastFlyFrame() )
+ {
+ // --> #i34753# - reset flag, which prevents a positioning
+ if ( pFlyFrame->IsFlyLayFrame() )
+ {
+ static_cast<SwFlyLayFrame*>(pFlyFrame)->SetNoMakePos( false );
+ }
+
+ // #i81146# new loop control
+ int nLoopControlRuns = 0;
+ const int nLoopControlMax = 15;
+
+ do {
+ if ( mpLayAction )
+ {
+ mpLayAction->FormatLayoutFly( pFlyFrame );
+ // --> consider, if the layout action
+ // has to be restarted due to a delete of a page frame.
+ if ( mpLayAction->IsAgain() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ FormatLayout_( *pFlyFrame );
+ }
+ // --> #i34753# - prevent further positioning, if
+ // to-page|to-fly anchored Writer fly frame is already clipped.
+ if ( pFlyFrame->IsFlyLayFrame() && pFlyFrame->IsClipped() )
+ {
+ static_cast<SwFlyLayFrame*>(pFlyFrame)->SetNoMakePos( true );
+ }
+ // #i23129#, #i36347# - pass correct page frame
+ // to the object formatter
+ SwObjectFormatter::FormatObjsAtFrame( *pFlyFrame,
+ *(pFlyFrame->FindPageFrame()),
+ mpLayAction );
+ if ( mpLayAction )
+ {
+ mpLayAction->FormatFlyContent( pFlyFrame );
+ // --> consider, if the layout action
+ // has to be restarted due to a delete of a page frame.
+ if ( mpLayAction->IsAgain() )
+ {
+ break;
+ }
+ }
+ else
+ {
+ FormatObjContent( *pFlyFrame );
+ }
+
+ if ( ++nLoopControlRuns >= nLoopControlMax )
+ {
+ OSL_FAIL( "LoopControl in SwObjectFormatter::FormatObj_: Stage 3!!!" );
+ pFlyFrame->ValidateThisAndAllLowers( 2 );
+ nLoopControlRuns = 0;
+ }
+
+ // --> #i57917#
+ // stop formatting of anchored object, if restart of layout process is requested.
+ } while ( !pFlyFrame->isFrameAreaDefinitionValid() &&
+ !_rAnchoredObj.RestartLayoutProcess() &&
+ pFlyFrame->GetAnchorFrame() == &GetAnchorFrame() );
+ }
+ else if ( dynamic_cast<const SwAnchoredDrawObject*>( &_rAnchoredObj) != nullptr )
+ {
+ _rAnchoredObj.MakeObjPos();
+ }
+}
+
+/** invokes the intrinsic format method for all floating screen objects,
+ anchored at anchor frame on the given page frame
+
+ #i28701#
+ #i26945# - for format of floating screen objects for
+ follow text frames, the 'master' text frame is passed to the method.
+ Thus, the objects, whose anchor character is inside the follow text
+ frame can be formatted.
+*/
+bool SwObjectFormatter::FormatObjsAtFrame_( SwTextFrame* _pMasterTextFrame )
+{
+ // --> #i26945#
+ SwFrame* pAnchorFrame( nullptr );
+ if ( GetAnchorFrame().IsTextFrame() &&
+ static_cast<SwTextFrame&>(GetAnchorFrame()).IsFollow() &&
+ _pMasterTextFrame )
+ {
+ pAnchorFrame = _pMasterTextFrame;
+ }
+ else
+ {
+ pAnchorFrame = &GetAnchorFrame();
+ }
+ if ( !pAnchorFrame->GetDrawObjs() )
+ {
+ // nothing to do, if no floating screen object is registered at the anchor frame.
+ return true;
+ }
+
+ bool bSuccess( true );
+
+ for ( size_t i = 0; i < pAnchorFrame->GetDrawObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pAnchorFrame->GetDrawObjs())[i];
+
+ // check, if object's anchor is on the given page frame or
+ // object is registered at the given page frame.
+ // --> #i26945# - check, if the anchor character of the
+ // anchored object is located in a follow text frame. If this anchor
+ // follow text frame differs from the given anchor frame, the given
+ // anchor frame is a 'master' text frame of the anchor follow text frame.
+ // If the anchor follow text frame is in the same body as its 'master'
+ // text frame, do not format the anchored object.
+ // E.g., this situation can occur during the table row splitting algorithm.
+ SwTextFrame* pAnchorCharFrame = pAnchoredObj->FindAnchorCharFrame();
+ const bool bAnchoredAtFollowInSameBodyAsMaster =
+ pAnchorCharFrame && pAnchorCharFrame->IsFollow() &&
+ pAnchorCharFrame != pAnchoredObj->GetAnchorFrame() &&
+ pAnchorCharFrame->FindBodyFrame() ==
+ static_cast<SwTextFrame*>(pAnchoredObj->AnchorFrame())->FindBodyFrame();
+ if ( bAnchoredAtFollowInSameBodyAsMaster )
+ {
+ continue;
+ }
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = pAnchoredObj->FindPageFrameOfAnchor();
+ OSL_ENSURE( pPageFrameOfAnchor,
+ "<SwObjectFormatter::FormatObjsAtFrame_()> - missing page frame." );
+ // --> #i26945#
+ if ( pPageFrameOfAnchor && pPageFrameOfAnchor == &mrPageFrame )
+ {
+ // if format of object fails, stop formatting and pass fail to
+ // calling method via the return value.
+ if ( !DoFormatObj( *pAnchoredObj ) )
+ {
+ bSuccess = false;
+ break;
+ }
+
+ // considering changes at <pAnchorFrame->GetDrawObjs()> during
+ // format of the object.
+ if ( !pAnchorFrame->GetDrawObjs() ||
+ i > pAnchorFrame->GetDrawObjs()->size() )
+ {
+ break;
+ }
+ else
+ {
+ const size_t nActPosOfObj =
+ pAnchorFrame->GetDrawObjs()->ListPosOf( *pAnchoredObj );
+ if ( nActPosOfObj == pAnchorFrame->GetDrawObjs()->size() ||
+ nActPosOfObj > i )
+ {
+ --i;
+ }
+ else if ( nActPosOfObj < i )
+ {
+ i = nActPosOfObj;
+ }
+ }
+ }
+ } // end of loop on <pAnchorFrame->.GetDrawObjs()>
+
+ return bSuccess;
+}
+
+/** accessor to collected anchored object
+
+ #i28701#
+*/
+SwAnchoredObject* SwObjectFormatter::GetCollectedObj( const sal_uInt32 _nIndex )
+{
+ return mpPgNumAndTypeOfAnchors ? (*mpPgNumAndTypeOfAnchors)[_nIndex] : nullptr;
+}
+
+/** accessor to 'anchor' page number of collected anchored object
+
+ #i28701#
+*/
+sal_uInt32 SwObjectFormatter::GetPgNumOfCollected( const sal_uInt32 _nIndex )
+{
+ return mpPgNumAndTypeOfAnchors ? mpPgNumAndTypeOfAnchors->GetPageNum(_nIndex) : 0;
+}
+
+/** accessor to 'anchor' type of collected anchored object
+
+ #i26945#
+*/
+bool SwObjectFormatter::IsCollectedAnchoredAtMaster( const sal_uInt32 _nIndex )
+{
+ return mpPgNumAndTypeOfAnchors == nullptr
+ || mpPgNumAndTypeOfAnchors->AnchoredAtMaster(_nIndex);
+}
+
+/** accessor to total number of collected anchored objects
+
+ #i28701#
+*/
+sal_uInt32 SwObjectFormatter::CountOfCollected()
+{
+ return mpPgNumAndTypeOfAnchors ? mpPgNumAndTypeOfAnchors->Count() : 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformatterlayfrm.cxx b/sw/source/core/layout/objectformatterlayfrm.cxx
new file mode 100644
index 000000000..a9b5201b3
--- /dev/null
+++ b/sw/source/core/layout/objectformatterlayfrm.cxx
@@ -0,0 +1,188 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "objectformatterlayfrm.hxx"
+#include <anchoredobject.hxx>
+#include <sortedobjs.hxx>
+#include <pagefrm.hxx>
+
+#include <layact.hxx>
+#include <osl/diagnose.h>
+
+SwObjectFormatterLayFrame::SwObjectFormatterLayFrame( SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+ : SwObjectFormatter( _rPageFrame, _pLayAction ),
+ mrAnchorLayFrame( _rAnchorLayFrame )
+{
+}
+
+SwObjectFormatterLayFrame::~SwObjectFormatterLayFrame()
+{
+}
+
+std::unique_ptr<SwObjectFormatterLayFrame> SwObjectFormatterLayFrame::CreateObjFormatter(
+ SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ if ( !_rAnchorLayFrame.IsPageFrame() &&
+ !_rAnchorLayFrame.IsFlyFrame() )
+ {
+ OSL_FAIL( "<SwObjectFormatterLayFrame::CreateObjFormatter(..)> - unexpected type of anchor frame " );
+ return nullptr;
+ }
+
+ std::unique_ptr<SwObjectFormatterLayFrame> pObjFormatter;
+
+ // create object formatter, if floating screen objects are registered at
+ // given anchor layout frame.
+ if ( _rAnchorLayFrame.GetDrawObjs() ||
+ ( _rAnchorLayFrame.IsPageFrame() &&
+ static_cast<SwPageFrame&>(_rAnchorLayFrame).GetSortedObjs() ) )
+ {
+ pObjFormatter.reset(
+ new SwObjectFormatterLayFrame( _rAnchorLayFrame, _rPageFrame, _pLayAction ));
+ }
+
+ return pObjFormatter;
+}
+
+SwFrame& SwObjectFormatterLayFrame::GetAnchorFrame()
+{
+ return mrAnchorLayFrame;
+}
+
+// #i40147# - add parameter <_bCheckForMovedFwd>.
+// Not relevant for objects anchored at layout frame.
+bool SwObjectFormatterLayFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool )
+{
+ FormatObj_( _rAnchoredObj );
+
+ // #124218# - consider that the layout action has to be
+ // restarted due to a deleted page frame.
+ return GetLayAction() == nullptr || !GetLayAction()->IsAgain();
+}
+
+bool SwObjectFormatterLayFrame::DoFormatObjs()
+{
+ bool bSuccess = FormatObjsAtFrame_();
+
+ if ( bSuccess && GetAnchorFrame().IsPageFrame() )
+ {
+ // anchor layout frame is a page frame.
+ // Thus, format also all anchored objects, which are registered at
+ // this page frame, whose 'anchor' isn't on this page frame and whose
+ // anchor frame is valid.
+ bSuccess = AdditionalFormatObjsOnPage();
+ }
+
+ return bSuccess;
+}
+
+/** method to format all anchored objects, which are registered at
+ the page frame, whose 'anchor' isn't on this page frame and whose
+ anchor frame is valid.
+
+ OD 2004-07-02 #i28701#
+*/
+bool SwObjectFormatterLayFrame::AdditionalFormatObjsOnPage()
+{
+ if ( !GetAnchorFrame().IsPageFrame() )
+ {
+ OSL_FAIL( "<SwObjectFormatterLayFrame::AdditionalFormatObjsOnPage()> - mis-usage of method, call only for anchor frames of type page frame" );
+ return true;
+ }
+
+ // #124218# - consider, if the layout action
+ // has to be restarted due to a delete of a page frame.
+ if ( GetLayAction() && GetLayAction()->IsAgain() )
+ {
+ return false;
+ }
+
+ SwPageFrame& rPageFrame = static_cast<SwPageFrame&>(GetAnchorFrame());
+
+ if ( !rPageFrame.GetSortedObjs() )
+ {
+ // nothing to do, if no floating screen object is registered at the anchor frame.
+ return true;
+ }
+
+ bool bSuccess( true );
+
+ for ( size_t i = 0; i < rPageFrame.GetSortedObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*rPageFrame.GetSortedObjs())[i];
+
+ // #i51941# - do not format object, which are anchored
+ // inside or at fly frame.
+ if ( pAnchoredObj->GetAnchorFrame()->FindFlyFrame() )
+ {
+ continue;
+ }
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor = pAnchoredObj->FindPageFrameOfAnchor();
+ // #i26945# - check, if the page frame of the
+ // object's anchor frame isn't the given page frame
+ OSL_ENSURE( pPageFrameOfAnchor,
+ "<SwObjectFormatterLayFrame::AdditionalFormatObjsOnPage()> - missing page frame" );
+ if ( pPageFrameOfAnchor &&
+ // #i35911#
+ pPageFrameOfAnchor->GetPhyPageNum() < rPageFrame.GetPhyPageNum() )
+ {
+ // if format of object fails, stop formatting and pass fail to
+ // calling method via the return value.
+ if ( !DoFormatObj( *pAnchoredObj ) )
+ {
+ bSuccess = false;
+ break;
+ }
+
+ // considering changes at <GetAnchorFrame().GetDrawObjs()> during
+ // format of the object.
+ if ( !rPageFrame.GetSortedObjs() ||
+ i > rPageFrame.GetSortedObjs()->size() )
+ {
+ break;
+ }
+ else
+ {
+ const size_t nActPosOfObj =
+ rPageFrame.GetSortedObjs()->ListPosOf( *pAnchoredObj );
+ if ( nActPosOfObj == rPageFrame.GetSortedObjs()->size() ||
+ nActPosOfObj > i )
+ {
+ --i;
+ }
+ else if ( nActPosOfObj < i )
+ {
+ i = nActPosOfObj;
+ }
+ }
+ }
+ } // end of loop on <rPageFrame.GetSortedObjs()>
+
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformatterlayfrm.hxx b/sw/source/core/layout/objectformatterlayfrm.hxx
new file mode 100644
index 000000000..9ece6f281
--- /dev/null
+++ b/sw/source/core/layout/objectformatterlayfrm.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERLAYFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERLAYFRM_HXX
+
+#include <objectformatter.hxx>
+
+class SwLayoutFrame;
+
+// Format floating screen objects, which are anchored at a given anchor text frame
+// and registered at the given page frame.
+class SwObjectFormatterLayFrame : public SwObjectFormatter
+{
+ private:
+ // anchor layout frame
+ SwLayoutFrame& mrAnchorLayFrame;
+
+ SwObjectFormatterLayFrame( SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+
+ /** method to format all anchored objects, which are registered at
+ the page frame, whose 'anchor' isn't on this page frame and whose
+ anchor frame is valid.
+
+ OD 2004-07-02 #i28701#
+
+ @return boolean
+ indicates, if format was successful
+ */
+ bool AdditionalFormatObjsOnPage();
+
+ protected:
+
+ virtual SwFrame& GetAnchorFrame() override;
+
+ public:
+ virtual ~SwObjectFormatterLayFrame() override;
+
+ // #i40147# - add parameter <_bCheckForMovedFwd>.
+ // Not relevant for objects anchored at layout frame.
+ virtual bool DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd = false ) override;
+ virtual bool DoFormatObjs() override;
+
+ static std::unique_ptr<SwObjectFormatterLayFrame> CreateObjFormatter(
+ SwLayoutFrame& _rAnchorLayFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformattertxtfrm.cxx b/sw/source/core/layout/objectformattertxtfrm.cxx
new file mode 100644
index 000000000..6b2503d40
--- /dev/null
+++ b/sw/source/core/layout/objectformattertxtfrm.cxx
@@ -0,0 +1,971 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "objectformattertxtfrm.hxx"
+#include <sortedobjs.hxx>
+#include <rootfrm.hxx>
+#include <anchoredobject.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <rowfrm.hxx>
+#include <layouter.hxx>
+#include <fmtanchr.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <layact.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+// little helper class to forbid follow formatting for the given text frame
+class SwForbidFollowFormat
+{
+private:
+ SwTextFrame& mrTextFrame;
+ const bool bOldFollowFormatAllowed;
+
+public:
+ explicit SwForbidFollowFormat( SwTextFrame& _rTextFrame )
+ : mrTextFrame( _rTextFrame ),
+ bOldFollowFormatAllowed( _rTextFrame.FollowFormatAllowed() )
+ {
+ mrTextFrame.ForbidFollowFormat();
+ }
+
+ ~SwForbidFollowFormat()
+ {
+ if ( bOldFollowFormatAllowed )
+ {
+ mrTextFrame.AllowFollowFormat();
+ }
+ }
+};
+
+}
+
+SwObjectFormatterTextFrame::SwObjectFormatterTextFrame( SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwTextFrame* _pMasterAnchorTextFrame,
+ SwLayAction* _pLayAction )
+ : SwObjectFormatter( _rPageFrame, _pLayAction, true ),
+ mrAnchorTextFrame( _rAnchorTextFrame ),
+ mpMasterAnchorTextFrame( _pMasterAnchorTextFrame )
+{
+}
+
+SwObjectFormatterTextFrame::~SwObjectFormatterTextFrame()
+{
+}
+
+std::unique_ptr<SwObjectFormatterTextFrame> SwObjectFormatterTextFrame::CreateObjFormatter(
+ SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction )
+{
+ std::unique_ptr<SwObjectFormatterTextFrame> pObjFormatter;
+
+ // determine 'master' of <_rAnchorTextFrame>, if anchor frame is a follow text frame.
+ SwTextFrame* pMasterOfAnchorFrame = nullptr;
+ if ( _rAnchorTextFrame.IsFollow() )
+ {
+ pMasterOfAnchorFrame = _rAnchorTextFrame.FindMaster();
+ while ( pMasterOfAnchorFrame && pMasterOfAnchorFrame->IsFollow() )
+ {
+ pMasterOfAnchorFrame = pMasterOfAnchorFrame->FindMaster();
+ }
+ }
+
+ // create object formatter, if floating screen objects are registered
+ // at anchor frame (or at 'master' anchor frame)
+ if ( _rAnchorTextFrame.GetDrawObjs() ||
+ ( pMasterOfAnchorFrame && pMasterOfAnchorFrame->GetDrawObjs() ) )
+ {
+ pObjFormatter.reset(
+ new SwObjectFormatterTextFrame( _rAnchorTextFrame, _rPageFrame,
+ pMasterOfAnchorFrame, _pLayAction ));
+ }
+
+ return pObjFormatter;
+}
+
+SwFrame& SwObjectFormatterTextFrame::GetAnchorFrame()
+{
+ return mrAnchorTextFrame;
+}
+
+// #i40147# - add parameter <_bCheckForMovedFwd>.
+bool SwObjectFormatterTextFrame::DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd )
+{
+ // consider, if the layout action has to be
+ // restarted due to a delete of a page frame.
+ if ( GetLayAction() && GetLayAction()->IsAgain() )
+ {
+ return false;
+ }
+
+ bool bSuccess( true );
+
+ if ( _rAnchoredObj.IsFormatPossible() )
+ {
+ _rAnchoredObj.SetRestartLayoutProcess( false );
+
+ FormatObj_( _rAnchoredObj );
+ // consider, if the layout action has to be
+ // restarted due to a delete of a page frame.
+ if ( GetLayAction() && GetLayAction()->IsAgain() )
+ {
+ return false;
+ }
+
+ // check, if layout process has to be restarted.
+ // if yes, perform needed invalidations.
+
+ // no restart of layout process,
+ // if anchored object is anchored inside a Writer fly frame,
+ // its position is already locked, and it follows the text flow.
+ const bool bRestart =
+ _rAnchoredObj.RestartLayoutProcess() &&
+ !( _rAnchoredObj.PositionLocked() &&
+ _rAnchoredObj.GetAnchorFrame()->IsInFly() &&
+ _rAnchoredObj.GetFrameFormat().GetFollowTextFlow().GetValue() );
+ if ( bRestart )
+ {
+ bSuccess = false;
+ InvalidatePrevObjs( _rAnchoredObj );
+ InvalidateFollowObjs( _rAnchoredObj );
+ }
+
+ // format anchor text frame, if wrapping style influence of the object
+ // has to be considered and it's <NONE_SUCCESSIVE_POSITIONED>
+ // #i3317# - consider also anchored objects, whose
+ // wrapping style influence is temporarily considered.
+ // #i40147# - consider also anchored objects, for
+ // whose the check of a moved forward anchor frame is requested.
+ // revise decision made for i3317:
+ // anchored objects, whose wrapping style influence is temporarily considered,
+ // have to be considered in method <SwObjectFormatterTextFrame::DoFormatObjs()>
+ if ( bSuccess &&
+ _rAnchoredObj.ConsiderObjWrapInfluenceOnObjPos() &&
+ ( _bCheckForMovedFwd ||
+ _rAnchoredObj.GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) ==
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ) )
+ {
+ // #i26945# - check conditions for move forward of
+ // anchor text frame
+ // determine, if anchor text frame has previous frame
+ const bool bDoesAnchorHadPrev = ( mrAnchorTextFrame.GetIndPrev() != nullptr );
+
+ // #i40141# - use new method - it also formats the
+ // section the anchor frame is in.
+ FormatAnchorFrameForCheckMoveFwd();
+
+ // #i35911#
+ if ( _rAnchoredObj.HasClearedEnvironment() )
+ {
+ _rAnchoredObj.SetClearedEnvironment( true );
+ // #i44049# - consider, that anchor frame
+ // could already been marked to move forward.
+ SwPageFrame* pAnchorPageFrame( mrAnchorTextFrame.FindPageFrame() );
+ if ( pAnchorPageFrame != _rAnchoredObj.GetPageFrame() )
+ {
+ bool bInsert( true );
+ sal_uInt32 nToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nToPageNum ) )
+ {
+ if ( nToPageNum < pAnchorPageFrame->GetPhyPageNum() )
+ SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame );
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ SwLayouter::InsertMovedFwdFrame( rDoc, mrAnchorTextFrame,
+ pAnchorPageFrame->GetPhyPageNum() );
+ mrAnchorTextFrame.InvalidatePos();
+ bSuccess = false;
+ InvalidatePrevObjs( _rAnchoredObj );
+ InvalidateFollowObjs( _rAnchoredObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObj(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ }
+ else if ( !mrAnchorTextFrame.IsFollow() && bDoesAnchorHadPrev )
+ {
+ // index of anchored object in collection of page numbers and
+ // anchor types
+ sal_uInt32 nIdx( CountOfCollected() );
+ OSL_ENSURE( nIdx > 0,
+ "<SwObjectFormatterTextFrame::DoFormatObj(..)> - anchored object not collected!?" );
+ --nIdx;
+
+ sal_uInt32 nToPageNum( 0 );
+ // #i43913#
+ bool bDummy( false );
+ bool bPageHasFlysAnchoredBelowThis(false);
+ // see how SwObjectFormatter::FormatObjsAtFrame_() checks
+ // "pPageFrameOfAnchor == &mrPageFrame" - only caller relevant for
+ // this subclass
+ assert(GetPageFrame().GetPhyPageNum() == GetPgNumOfCollected(nIdx));
+ if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( nIdx ),
+ GetPageFrame(),
+ IsCollectedAnchoredAtMaster( nIdx ),
+ nToPageNum, bDummy,
+ bPageHasFlysAnchoredBelowThis))
+ {
+ // #i49987# - consider, that anchor frame
+ // could already been marked to move forward.
+ bool bInsert( true );
+ sal_uInt32 nMovedFwdToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nMovedFwdToPageNum ) )
+ {
+ if ( nMovedFwdToPageNum < nToPageNum )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::RemoveMovedFwdFrame(rDoc, mrAnchorTextFrame);
+ }
+ }
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ // Indicate that anchor text frame has to move forward and
+ // invalidate its position to force a re-format.
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::InsertMovedFwdFrame(rDoc,
+ mrAnchorTextFrame, nToPageNum);
+ }
+ mrAnchorTextFrame.InvalidatePos();
+
+ // Indicate restart of the layout process
+ bSuccess = false;
+
+ // If needed, invalidate previous objects anchored at same anchor
+ // text frame.
+ InvalidatePrevObjs( _rAnchoredObj );
+
+ // Invalidate object and following objects for the restart of the
+ // layout process
+ InvalidateFollowObjs( _rAnchoredObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObj(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ }
+ // i40155# - mark anchor frame not to wrap around
+ // objects under the condition, that its follow contains all its text.
+ else if ( !mrAnchorTextFrame.IsFollow() &&
+ mrAnchorTextFrame.GetFollow() &&
+ mrAnchorTextFrame.GetFollow()->GetOffset() == TextFrameIndex(0))
+ {
+ SwLayouter::RemoveMovedFwdFrame(
+ *(mrAnchorTextFrame.FindPageFrame()->GetFormat()->GetDoc()),
+ mrAnchorTextFrame );
+ }
+ }
+ }
+
+ return bSuccess;
+}
+
+bool SwObjectFormatterTextFrame::DoFormatObjs()
+{
+ if ( !mrAnchorTextFrame.isFrameAreaDefinitionValid() )
+ {
+ if ( GetLayAction() &&
+ mrAnchorTextFrame.FindPageFrame() != &GetPageFrame() )
+ {
+ // notify layout action, thus is can restart the layout process on
+ // a previous page.
+ GetLayAction()->SetAgain(true);
+ }
+ else
+ {
+ // the anchor text frame has to be valid, thus assert.
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObjs()> called for invalidate anchor text frame." );
+ }
+
+ return false;
+ }
+
+ bool bSuccess( true );
+
+ if ( mrAnchorTextFrame.IsFollow() )
+ {
+ // Only floating screen objects anchored as-character are directly
+ // registered at a follow text frame. The other floating screen objects
+ // are registered at the 'master' anchor text frame.
+ // Thus, format the other floating screen objects through the 'master'
+ // anchor text frame
+ OSL_ENSURE( mpMasterAnchorTextFrame,
+ "SwObjectFormatterTextFrame::DoFormatObjs() - missing 'master' anchor text frame" );
+ bSuccess = FormatObjsAtFrame_( mpMasterAnchorTextFrame );
+
+ if ( bSuccess )
+ {
+ // format of as-character anchored floating screen objects - no failure
+ // expected on the format of these objects.
+ bSuccess = FormatObjsAtFrame_();
+ }
+ }
+ else
+ {
+ bSuccess = FormatObjsAtFrame_();
+ }
+
+ // consider anchored objects, whose wrapping style influence are temporarily
+ // considered.
+ if ( bSuccess &&
+ ( ConsiderWrapOnObjPos() ||
+ ( !mrAnchorTextFrame.IsFollow() &&
+ AtLeastOneObjIsTmpConsiderWrapInfluence() ) ) )
+ {
+ const bool bDoesAnchorHadPrev = ( mrAnchorTextFrame.GetIndPrev() != nullptr );
+
+ // Format anchor text frame after its objects are formatted.
+ // Note: The format of the anchor frame also formats the invalid
+ // previous frames of the anchor frame. The format of the previous
+ // frames is needed to get a correct result of format of the
+ // anchor frame for the following check for moved forward anchors
+ // #i40141# - use new method - it also formats the
+ // section the anchor frame is in.
+ FormatAnchorFrameForCheckMoveFwd();
+
+ sal_uInt32 nToPageNum( 0 );
+ // #i43913#
+ bool bInFollow( false );
+ bool bPageHasFlysAnchoredBelowThis(false);
+ SwAnchoredObject* pObj = nullptr;
+ if ( !mrAnchorTextFrame.IsFollow() )
+ {
+ pObj = GetFirstObjWithMovedFwdAnchor(
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT,
+ nToPageNum, bInFollow, bPageHasFlysAnchoredBelowThis );
+ }
+ // #i35911#
+ if ( pObj && pObj->HasClearedEnvironment() )
+ {
+ pObj->SetClearedEnvironment( true );
+ // #i44049# - consider, that anchor frame
+ // could already been marked to move forward.
+ SwPageFrame* pAnchorPageFrame( mrAnchorTextFrame.FindPageFrame() );
+ // #i43913# - consider, that anchor frame
+ // is a follow or is in a follow row, which will move forward.
+ if ( pAnchorPageFrame != pObj->GetPageFrame() ||
+ bInFollow )
+ {
+ bool bInsert( true );
+ sal_uInt32 nTmpToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nTmpToPageNum ) )
+ {
+ if ( nTmpToPageNum < pAnchorPageFrame->GetPhyPageNum() )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::RemoveMovedFwdFrame(rDoc, mrAnchorTextFrame);
+ }
+ }
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ if (!bPageHasFlysAnchoredBelowThis)
+ {
+ SwLayouter::InsertMovedFwdFrame(rDoc, mrAnchorTextFrame,
+ pAnchorPageFrame->GetPhyPageNum());
+ }
+ mrAnchorTextFrame.InvalidatePos();
+ bSuccess = false;
+ InvalidatePrevObjs( *pObj );
+ InvalidateFollowObjs( *pObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObjs(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ }
+ else if ( pObj && bDoesAnchorHadPrev )
+ {
+ // Object found, whose anchor is moved forward
+
+ // #i49987# - consider, that anchor frame
+ // could already been marked to move forward.
+ bool bInsert( true );
+ sal_uInt32 nMovedFwdToPageNum( 0 );
+ const SwDoc& rDoc = *(GetPageFrame().GetFormat()->GetDoc());
+ if ( SwLayouter::FrameMovedFwdByObjPos(
+ rDoc, mrAnchorTextFrame, nMovedFwdToPageNum ) )
+ {
+ if ( nMovedFwdToPageNum < nToPageNum )
+ SwLayouter::RemoveMovedFwdFrame( rDoc, mrAnchorTextFrame );
+ else
+ bInsert = false;
+ }
+ if ( bInsert )
+ {
+ // Indicate that anchor text frame has to move forward and
+ // invalidate its position to force a re-format.
+ SwLayouter::InsertMovedFwdFrame( rDoc, mrAnchorTextFrame, nToPageNum );
+ mrAnchorTextFrame.InvalidatePos();
+
+ // Indicate restart of the layout process
+ bSuccess = false;
+
+ // If needed, invalidate previous objects anchored at same anchor
+ // text frame.
+ InvalidatePrevObjs( *pObj );
+
+ // Invalidate object and following objects for the restart of the
+ // layout process
+ InvalidateFollowObjs( *pObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwObjectFormatterTextFrame::DoFormatObjs(..)> - anchor frame not marked to move forward" );
+ }
+ }
+ // #i40155# - mark anchor frame not to wrap around
+ // objects under the condition, that its follow contains all its text.
+ else if ( !mrAnchorTextFrame.IsFollow() &&
+ mrAnchorTextFrame.GetFollow() &&
+ mrAnchorTextFrame.GetFollow()->GetOffset() == TextFrameIndex(0))
+ {
+ SwLayouter::RemoveMovedFwdFrame(
+ *(mrAnchorTextFrame.FindPageFrame()->GetFormat()->GetDoc()),
+ mrAnchorTextFrame );
+ }
+ }
+
+ return bSuccess;
+}
+
+void SwObjectFormatterTextFrame::InvalidatePrevObjs( SwAnchoredObject& _rAnchoredObj )
+{
+ // invalidate all previous objects, whose wrapping influence on the object
+ // positioning is <NONE_CONCURRENT_POSITIONED>.
+ // Note: list of objects at anchor frame is sorted by this property.
+ if ( _rAnchoredObj.GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) !=
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT )
+ return;
+
+ const SwSortedObjs* pObjs = GetAnchorFrame().GetDrawObjs();
+ if ( !pObjs )
+ return;
+
+ // determine start index
+ size_t i = pObjs->ListPosOf( _rAnchoredObj );
+ while (i > 0)
+ {
+ --i;
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if ( pAnchoredObj->GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) ==
+ // #i35017# - constant name has changed
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT )
+ {
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ }
+ }
+}
+
+void SwObjectFormatterTextFrame::InvalidateFollowObjs( SwAnchoredObject& _rAnchoredObj )
+{
+ _rAnchoredObj.InvalidateObjPosForConsiderWrapInfluence();
+
+ const SwSortedObjs* pObjs = GetPageFrame().GetSortedObjs();
+ if ( pObjs )
+ {
+ // determine start index
+ for ( size_t i = pObjs->ListPosOf( _rAnchoredObj ) + 1; i < pObjs->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ }
+ }
+}
+
+SwAnchoredObject* SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor(
+ const sal_Int16 _nWrapInfluenceOnPosition,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis)
+{
+ // #i35017# - constant names have changed
+ OSL_ENSURE( _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE ||
+ _nWrapInfluenceOnPosition == text::WrapInfluenceOnPosition::ONCE_CONCURRENT,
+ "<SwObjectFormatterTextFrame::GetFirstObjWithMovedFwdAnchor(..)> - invalid value for parameter <_nWrapInfluenceOnPosition>" );
+
+ SwAnchoredObject* pRetAnchoredObj = nullptr;
+
+ sal_uInt32 i = 0;
+ for ( ; i < CountOfCollected(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = GetCollectedObj(i);
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() &&
+ pAnchoredObj->GetFrameFormat().GetWrapInfluenceOnObjPos().
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ GetWrapInfluenceOnObjPos( true ) == _nWrapInfluenceOnPosition )
+ {
+ // see how SwObjectFormatter::FormatObjsAtFrame_() checks
+ // "pPageFrameOfAnchor == &mrPageFrame" - only caller relevant for
+ // this subclass
+ assert(GetPageFrame().GetPhyPageNum() == GetPgNumOfCollected(i));
+ // #i26945# - use new method <_CheckMovedFwdCondition(..)>
+ // #i43913#
+ if ( SwObjectFormatterTextFrame::CheckMovedFwdCondition( *GetCollectedObj( i ),
+ GetPageFrame(),
+ IsCollectedAnchoredAtMaster( i ),
+ _noToPageNum, _boInFollow,
+ o_rbPageHasFlysAnchoredBelowThis) )
+ {
+ pRetAnchoredObj = pAnchoredObj;
+ break;
+ }
+ }
+ }
+
+ return pRetAnchoredObj;
+}
+
+static SwRowFrame const* FindTopLevelRowFrame(SwFrame const*const pFrame)
+{
+ SwRowFrame * pRow = const_cast<SwFrame*>(pFrame)->FindRowFrame();
+ // looks like SwTabFrame has mbInfTab = true so go up 2 levels
+ while (pRow->GetUpper()->GetUpper()->IsInTab())
+ {
+ pRow = pRow->GetUpper()->GetUpper()->FindRowFrame();
+ }
+ return pRow;
+}
+
+static SwContentFrame const* FindFrameInBody(SwAnchoredObject const& rAnchored)
+{
+ SwFrame const*const pAnchor(rAnchored.GetAnchorFrame());
+ assert(pAnchor);
+ if (pAnchor->IsPageFrame() || pAnchor->FindFooterOrHeader())
+ {
+ return nullptr;
+ }
+ if (pAnchor->IsInFly())
+ {
+ return FindFrameInBody(*pAnchor->FindFlyFrame());
+ }
+ if (pAnchor->IsInFootnote())
+ {
+ return pAnchor->FindFootnoteFrame()->GetRef();
+ }
+ assert(pAnchor->IsInDocBody());
+ assert(pAnchor->IsContentFrame());
+ return static_cast<SwContentFrame const*>(pAnchor);
+}
+
+// #i58182#
+// - replace private method by corresponding static public method
+bool SwObjectFormatterTextFrame::CheckMovedFwdCondition(
+ SwAnchoredObject& _rAnchoredObj,
+ SwPageFrame const& rFromPageFrame,
+ const bool _bAnchoredAtMasterBeforeFormatAnchor,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis)
+{
+ const sal_uInt32 _nFromPageNum(rFromPageFrame.GetPhyPageNum());
+ bool bAnchorIsMovedForward( false );
+
+ SwPageFrame* pPageFrameOfAnchor = _rAnchoredObj.FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor )
+ {
+ const sal_uInt32 nPageNum = pPageFrameOfAnchor->GetPhyPageNum();
+ if ( nPageNum > _nFromPageNum )
+ {
+ _noToPageNum = nPageNum;
+ // Handling of special case:
+ // If anchor frame is move forward into a follow flow row,
+ // <_noToPageNum> is set to <_nFromPageNum + 1>, because it is
+ // possible that the anchor page frame isn't valid, because the
+ // page distance between master row and follow flow row is greater
+ // than 1.
+ if ( _noToPageNum > (_nFromPageNum + 1) )
+ {
+ SwFrame* pAnchorFrame = _rAnchoredObj.GetAnchorFrameContainingAnchPos();
+ if ( pAnchorFrame->IsInTab() &&
+ pAnchorFrame->IsInFollowFlowRow() )
+ {
+ _noToPageNum = _nFromPageNum + 1;
+ }
+ }
+ bAnchorIsMovedForward = true;
+ }
+ }
+ // #i26945# - check, if an at-paragraph|at-character
+ // anchored object is now anchored at a follow text frame, which will be
+ // on the next page. Also check, if an at-character anchored object
+ // is now anchored at a text frame, which is in a follow flow row,
+ // which will be on the next page.
+ if ( !bAnchorIsMovedForward &&
+ _bAnchoredAtMasterBeforeFormatAnchor &&
+ ((_rAnchoredObj.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_CHAR) ||
+ (_rAnchoredObj.GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AT_PARA)))
+ {
+ SwFrame* pAnchorFrame = _rAnchoredObj.GetAnchorFrameContainingAnchPos();
+ OSL_ENSURE( pAnchorFrame->IsTextFrame(),
+ "<SwObjectFormatterTextFrame::CheckMovedFwdCondition(..) - wrong type of anchor frame>" );
+ SwTextFrame* pAnchorTextFrame = static_cast<SwTextFrame*>(pAnchorFrame);
+ bool bCheck( false );
+ if ( pAnchorTextFrame->IsFollow() )
+ {
+ bCheck = true;
+ }
+ else if( pAnchorTextFrame->IsInTab() )
+ {
+ const SwRowFrame* pMasterRow = pAnchorTextFrame->IsInFollowFlowRow();
+ if ( pMasterRow &&
+ pMasterRow->FindPageFrame() == pPageFrameOfAnchor )
+ {
+ bCheck = true;
+ }
+ }
+ if ( bCheck )
+ {
+ // check, if found text frame will be on the next page
+ // by checking, if it's in a column, which has no next.
+ SwFrame* pColFrame = pAnchorTextFrame->FindColFrame();
+ while ( pColFrame && !pColFrame->GetNext() )
+ {
+ pColFrame = pColFrame->FindColFrame();
+ }
+ if ( !pColFrame || !pColFrame->GetNext() )
+ {
+ _noToPageNum = _nFromPageNum + 1;
+ bAnchorIsMovedForward = true;
+ // #i43913#
+ _boInFollow = true;
+ }
+ }
+ }
+
+ if (bAnchorIsMovedForward)
+ {
+ // tdf#138518 try to determine if there is a fly on page rFromPageFrame
+ // which is anchored in a frame that is "below" the anchor frame
+ // of _rAnchoredObj, such that it should move to the next page before
+ // _rAnchoredObj does
+ if (auto * pObjs = rFromPageFrame.GetSortedObjs())
+ {
+ for (SwAnchoredObject *const pObj : *pObjs)
+ {
+ SwPageFrame const*const pObjAnchorPage(pObj->FindPageFrameOfAnchor());
+ assert(pObjAnchorPage);
+ if ((pObjAnchorPage == &rFromPageFrame
+ ? _boInFollow // same-page but will move forward
+ : rFromPageFrame.GetPhyPageNum() < pObjAnchorPage->GetPhyPageNum())
+ && pObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ != RndStdIds::FLY_AS_CHAR)
+ {
+ if (pPageFrameOfAnchor->GetPhyPageNum() < pObjAnchorPage->GetPhyPageNum())
+ {
+ SAL_INFO("sw.layout", "SwObjectFormatterTextFrame::CheckMovedFwdCondition(): o_rbPageHasFlysAnchoredBelowThis because next page");
+ o_rbPageHasFlysAnchoredBelowThis = true;
+ break;
+ }
+ // on same page: check if it's in next-chain in the document body
+ // (in case both are in the same fly the flag must not be
+ // set because the whole fly moves at once)
+ SwContentFrame const*const pInBodyFrameObj(FindFrameInBody(*pObj));
+ SwContentFrame const*const pInBodyFrameAnchoredObj(FindFrameInBody(_rAnchoredObj));
+ if (pInBodyFrameObj && pInBodyFrameAnchoredObj)
+ {
+ bool isBreakMore(false);
+ // currently this ignores index of at-char flys
+ for (SwContentFrame const* pContentFrame = pInBodyFrameAnchoredObj->FindNextCnt();
+ pContentFrame;
+ pContentFrame = pContentFrame->FindNextCnt())
+ {
+ if (pInBodyFrameObj == pContentFrame)
+ {
+ // subsequent cells in a row are not automatically
+ // "below" and the row could potentially be split
+ // TODO refine check if needed
+ if (!pInBodyFrameAnchoredObj->IsInTab()
+ || FindTopLevelRowFrame(pInBodyFrameAnchoredObj)
+ != FindTopLevelRowFrame(pInBodyFrameAnchoredObj))
+ { // anchored in next chain on same page
+ SAL_INFO("sw.layout", "SwObjectFormatterTextFrame::CheckMovedFwdCondition(): o_rbPageHasFlysAnchoredBelowThis because next chain on same page");
+ o_rbPageHasFlysAnchoredBelowThis = true;
+ isBreakMore = true;
+ }
+ break;
+ }
+ }
+ if (isBreakMore)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bAnchorIsMovedForward;
+}
+
+static void CleanupEmptyFootnoteFrame(SwFrame* pLowerFrame)
+{
+ // Calc on a SwTextFrame in a footnote can move it to the next page -
+ // deletion of the SwFootnoteFrame was disabled with SwFrameDeleteGuard
+ // but now we have to clean up empty footnote frames to prevent crashes.
+ // Note: check it at this level, not lower: both container and footnote
+ // can be deleted at the same time!
+ if (!pLowerFrame->IsFootnoteContFrame())
+ return;
+
+ for (SwFrame * pFootnote = pLowerFrame->GetLower(); pFootnote; )
+ {
+ assert(pFootnote->IsFootnoteFrame());
+ SwFrame *const pNextNote = pFootnote->GetNext();
+ if (!pFootnote->IsDeleteForbidden() && !pFootnote->GetLower() && !pFootnote->IsColLocked() &&
+ !static_cast<SwFootnoteFrame*>(pFootnote)->IsBackMoveLocked())
+ {
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ }
+ pFootnote = pNextNote;
+ }
+}
+
+// #i40140# - helper method to format layout frames used by
+// method <SwObjectFormatterTextFrame::FormatAnchorFrameForCheckMoveFwd()>
+// #i44049# - format till a certain lower frame, if provided.
+static void lcl_FormatContentOfLayoutFrame( SwLayoutFrame* pLayFrame,
+ SwFrame* pLastLowerFrame = nullptr )
+{
+ SwFrame* pLowerFrame = pLayFrame->GetLower();
+ while ( pLowerFrame )
+ {
+ // #i44049#
+ if ( pLastLowerFrame && pLowerFrame == pLastLowerFrame )
+ {
+ break;
+ }
+ if ( pLowerFrame->IsLayoutFrame() )
+ {
+ SwFrameDeleteGuard aCrudeHack(pLowerFrame); // ??? any issue setting this for non-footnote frames?
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pLowerFrame),
+ pLastLowerFrame );
+ }
+ else
+ pLowerFrame->Calc(pLowerFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ // Calc on a SwTextFrame in a footnote can move it to the next page -
+ // deletion of the SwFootnoteFrame was disabled with SwFrameDeleteGuard
+ // but now we have to clean up empty footnote frames to prevent crashes.
+ // Note: check it at this level, not lower: both container and footnote
+ // can be deleted at the same time!
+ SwFrame *const pNext = pLowerFrame->GetNext();
+ CleanupEmptyFootnoteFrame(pLowerFrame);
+ pLowerFrame = pNext;
+ }
+}
+
+/** method to format given anchor text frame and its previous frames
+
+ #i56300#
+ Usage: Needed to check, if the anchor text frame is moved forward
+ due to the positioning and wrapping of its anchored objects, and
+ to format the frames, which have become invalid due to the anchored
+ object formatting in the iterative object positioning algorithm
+*/
+void SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAnchorTextFrame )
+{
+ // #i47014# - no format of section and previous columns
+ // for follow text frames.
+ if ( !_rAnchorTextFrame.IsFollow() )
+ {
+ // In case the anchor frame is in a column or section, format its
+ // previous frames first - but don't jump out of the current layout
+ // environment, e.g. from footnotes into the footnote boss.
+ SwFrame * pSectFrame(nullptr);
+ SwFrame * pColFrameOfAnchor(nullptr);
+ for (SwFrame* pUpper = _rAnchorTextFrame.GetUpper();
+ pUpper != nullptr; pUpper = pUpper->GetUpper())
+ {
+ if (pUpper->IsCellFrame())
+ {
+ break; // apparently nothing to be done?
+ }
+ if (pUpper->IsFootnoteFrame())
+ {
+ SAL_INFO_IF(pColFrameOfAnchor == nullptr && pUpper->FindColFrame(),
+ "sw.layout", "tdf#122894 skipping column for footnote in column");
+ break; // stop: prevent crash in case footnotes are being moved
+ }
+ if (pUpper->IsSctFrame())
+ {
+ pColFrameOfAnchor = nullptr;
+ pSectFrame = pUpper;
+ break;
+ }
+ if (pColFrameOfAnchor != nullptr)
+ { // parent of column not a section frame => column not in section
+ break;
+ }
+ if (pUpper->IsColumnFrame())
+ {
+ pColFrameOfAnchor = pUpper;
+ }
+ }
+
+ // if anchor frame is directly inside a section, format this section and
+ // its previous frames.
+ // Note: It's a very simple format without formatting objects.
+ if (pSectFrame)
+ {
+ assert(pSectFrame->IsSctFrame());
+ {
+ SwFrameDeleteGuard aDeleteGuard(&_rAnchorTextFrame);
+ // #i44049#
+ _rAnchorTextFrame.LockJoin();
+ SwFrame* pFrame = pSectFrame->GetUpper()->GetLower();
+ // #i49605# - section frame could move forward
+ // by the format of its previous frame.
+ // Thus, check for valid <pFrame>.
+ while ( pFrame && pFrame != pSectFrame )
+ {
+ SwFrameDeleteGuard aDeleteFrameGuard(pFrame);
+
+ if ( pFrame->IsLayoutFrame() )
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pFrame) );
+ else
+ pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ pFrame = pFrame->GetNext();
+ }
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pSectFrame),
+ &_rAnchorTextFrame );
+ // #i44049#
+ _rAnchorTextFrame.UnlockJoin();
+ }
+ }
+
+ // #i40140# - if anchor frame is inside a column,
+ // format the content of the previous columns.
+ // Note: It's a very simple format without formatting objects.
+ if (pColFrameOfAnchor)
+ {
+ assert(pColFrameOfAnchor->IsColumnFrame());
+ // #i44049#
+ _rAnchorTextFrame.LockJoin();
+ SwFrameDeleteGuard aDeleteGuard(&_rAnchorTextFrame);
+ SwFrame* pColFrame = pColFrameOfAnchor->GetUpper()->GetLower();
+ while ( pColFrame != pColFrameOfAnchor )
+ {
+ SwFrame* pFrame = pColFrame->GetLower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ lcl_FormatContentOfLayoutFrame( static_cast<SwLayoutFrame*>(pFrame) );
+ else
+ pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ pFrame = pFrame->GetNext();
+ }
+
+ pColFrame = pColFrame->GetNext();
+ }
+ // #i44049#
+ _rAnchorTextFrame.UnlockJoin();
+ }
+ }
+
+ // format anchor frame - format of its follow not needed
+ // #i43255# - forbid follow format, only if anchor text
+ // frame is in table
+ if ( _rAnchorTextFrame.IsInTab() )
+ {
+ SwForbidFollowFormat aForbidFollowFormat( _rAnchorTextFrame );
+ _rAnchorTextFrame.Calc(_rAnchorTextFrame.getRootFrame()->GetCurrShell()->GetOut());
+ }
+ else
+ {
+ _rAnchorTextFrame.Calc(_rAnchorTextFrame.getRootFrame()->GetCurrShell()->GetOut());
+ }
+}
+
+/** method to format the anchor frame for checking of the move forward condition
+
+ #i40141#
+*/
+void SwObjectFormatterTextFrame::FormatAnchorFrameForCheckMoveFwd()
+{
+ SwObjectFormatterTextFrame::FormatAnchorFrameAndItsPrevs( mrAnchorTextFrame );
+}
+
+/** method to determine if at least one anchored object has state
+ <temporarily consider wrapping style influence> set.
+*/
+bool SwObjectFormatterTextFrame::AtLeastOneObjIsTmpConsiderWrapInfluence()
+{
+ bool bRet( false );
+
+ const SwSortedObjs* pObjs = GetAnchorFrame().GetDrawObjs();
+ if ( pObjs && pObjs->size() > 1 )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objectformattertxtfrm.hxx b/sw/source/core/layout/objectformattertxtfrm.hxx
new file mode 100644
index 000000000..25a7a7e92
--- /dev/null
+++ b/sw/source/core/layout/objectformattertxtfrm.hxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERTXTFRM_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJECTFORMATTERTXTFRM_HXX
+
+#include <objectformatter.hxx>
+#include <sal/types.h>
+
+class SwTextFrame;
+
+// #i28701#
+// Format floating screen objects, which are anchored at a given anchor text frame
+// and registered at the given page frame.
+class SwObjectFormatterTextFrame : public SwObjectFormatter
+{
+ private:
+ // anchor text frame
+ SwTextFrame& mrAnchorTextFrame;
+
+ // 'master' anchor text frame
+ SwTextFrame* mpMasterAnchorTextFrame;
+
+ SwObjectFormatterTextFrame( SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwTextFrame* _pMasterAnchorTextFrame,
+ SwLayAction* _pLayAction );
+
+ /** method to invalidate objects, anchored previous to given object at
+ the anchor text frame
+
+ @param _rAnchoredObj
+ reference to anchored object - objects, anchored previous to
+ this one will be invalidated.
+ */
+ void InvalidatePrevObjs( SwAnchoredObject& _rAnchoredObj );
+
+ /** method to invalidate objects, anchored after the given object at
+ the page frame
+
+ @param _rAnchoredObj
+ reference to anchored object - objects, anchored after this one will
+ be invalidated.
+ */
+ void InvalidateFollowObjs( SwAnchoredObject& _rAnchoredObj );
+
+ /** method to determine first anchored object, whose 'anchor is moved
+ forward'.
+
+ 'anchor (of an object) is moved forward', if the anchor frame
+ respectively the anchor character of the object isn't on the
+ proposed page frame. Instead its on a following page
+
+ #i26945# - For at-character anchored objects,
+ it has also to be checked, if the anchor character is in a follow
+ text frame, which would move to the next page.
+
+ #i43913# - add output parameter <_boInFollow>
+
+ @param _nWrapInfluenceOnPosition
+ input parameter - only object with this given wrapping style
+ influence are investigated.
+
+ @param _nFromPageNum
+ input parameter - number of page frame, the 'anchor' should be
+
+ @param _noToPageNum
+ output parameter - number of page frame, the 'anchor' of the returned
+ anchored object is.
+
+ @param _boInFollow
+ output parameter - boolean, indicating that anchor text frame is
+ currently on the same page, but it's a follow of in a follow row,
+ which will move forward. value only relevant, if method returns
+ an anchored object
+
+ @return SwAnchoredObject*
+ anchored object with a 'moved forward anchor'. If NULL, no such
+ anchored object is found.
+ */
+ SwAnchoredObject* GetFirstObjWithMovedFwdAnchor(
+ const sal_Int16 _nWrapInfluenceOnPosition,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis);
+
+ /** method to format the anchor frame for checking of the move forward condition
+
+ #i40141#
+ */
+ void FormatAnchorFrameForCheckMoveFwd();
+
+ /** method to determine if at least one anchored object has state
+ <temporarily consider wrapping style influence> set.
+ */
+ bool AtLeastOneObjIsTmpConsiderWrapInfluence();
+
+ protected:
+
+ virtual SwFrame& GetAnchorFrame() override;
+
+ public:
+ virtual ~SwObjectFormatterTextFrame() override;
+
+ // #i40147# - add parameter <_bCheckForMovedFwd>.
+ virtual bool DoFormatObj( SwAnchoredObject& _rAnchoredObj,
+ const bool _bCheckForMovedFwd = false ) override;
+ virtual bool DoFormatObjs() override;
+
+ /** method to create an instance of <SwObjectFormatterTextFrame> is
+ necessary.
+ */
+ static std::unique_ptr<SwObjectFormatterTextFrame> CreateObjFormatter(
+ SwTextFrame& _rAnchorTextFrame,
+ const SwPageFrame& _rPageFrame,
+ SwLayAction* _pLayAction );
+
+ /** method to format given anchor text frame and its previous frames
+
+ #i56300#
+ Usage: Needed to check, if the anchor text frame is moved forward
+ due to the positioning and wrapping of its anchored objects, and
+ to format the frames, which have become invalid due to the anchored
+ object formatting in the iterative object positioning algorithm
+
+ @param _rAnchorTextFrame
+ input parameter - reference to anchor text frame, which has to be
+ formatted including its previous frames of the page.
+ */
+ static void FormatAnchorFrameAndItsPrevs( SwTextFrame& _rAnchorTextFrame );
+
+ /** method to check the conditions, if 'anchor is moved forward'
+
+ #i26945#
+ #i43913# - add output parameter <_boInFollow>
+ #i58182# - replace method by a corresponding static
+ method, because it's needed for the iterative positioning algorithm.
+
+ @param _rAnchoredObj
+ input parameter - anchored object, for which the condition has to checked.
+
+ @param _nFromPageNum
+ input parameter - number of the page, on which the check is performed
+
+ @param _bAnchoredAtMasterBeforeFormatAnchor
+ input parameter - boolean indicating, that the given anchored object
+ was anchored at the master frame before the anchor frame has been
+ formatted.
+
+ @param _noToPageNum
+ output parameter - number of page frame, the 'anchor' of the returned
+ anchored object is.
+
+ @param _boInFollow
+ output parameter - boolean, indicating that anchor text frame is
+ currently on the same page, but it's a follow of in a follow row,
+ which will move forward. value only relevant, if method return <true>.
+ @param o_rbPageHasFlysAnchoredBelowThis
+ output parameter - indicates that the page has flys anchored
+ somewhere below the anchor of the passed _rAnchoredObj
+
+ @return boolean
+ indicating, if 'anchor is moved forward'
+ */
+ static bool CheckMovedFwdCondition( SwAnchoredObject& _rAnchoredObj,
+ SwPageFrame const& rFromPageFrame,
+ const bool _bAnchoredAtMasterBeforeFormatAnchor,
+ sal_uInt32& _noToPageNum,
+ bool& _boInFollow,
+ bool& o_rbPageHasFlysAnchoredBelowThis);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objstmpconsiderwrapinfl.cxx b/sw/source/core/layout/objstmpconsiderwrapinfl.cxx
new file mode 100644
index 000000000..e35336b9f
--- /dev/null
+++ b/sw/source/core/layout/objstmpconsiderwrapinfl.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "objstmpconsiderwrapinfl.hxx"
+#include <anchoredobject.hxx>
+
+SwObjsMarkedAsTmpConsiderWrapInfluence::SwObjsMarkedAsTmpConsiderWrapInfluence()
+{
+}
+
+SwObjsMarkedAsTmpConsiderWrapInfluence::~SwObjsMarkedAsTmpConsiderWrapInfluence()
+{
+ Clear();
+}
+
+void SwObjsMarkedAsTmpConsiderWrapInfluence::Insert( SwAnchoredObject& _rAnchoredObj )
+{
+ auto it = std::find(maObjsTmpConsiderWrapInfl.begin(), maObjsTmpConsiderWrapInfl.end(), &_rAnchoredObj);
+ if (it != maObjsTmpConsiderWrapInfl.end())
+ return;
+ maObjsTmpConsiderWrapInfl.push_back( &_rAnchoredObj );
+}
+
+void SwObjsMarkedAsTmpConsiderWrapInfluence::Remove( SwAnchoredObject& _rAnchoredObj )
+{
+ auto it = std::find(maObjsTmpConsiderWrapInfl.begin(), maObjsTmpConsiderWrapInfl.end(), &_rAnchoredObj);
+ if (it == maObjsTmpConsiderWrapInfl.end())
+ return;
+ maObjsTmpConsiderWrapInfl.erase(it);
+}
+
+void SwObjsMarkedAsTmpConsiderWrapInfluence::Clear()
+{
+ while ( !maObjsTmpConsiderWrapInfl.empty() )
+ {
+ SwAnchoredObject* pAnchoredObj = maObjsTmpConsiderWrapInfl.back();
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ pAnchoredObj->SetClearedEnvironment( false );
+
+ maObjsTmpConsiderWrapInfl.pop_back();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/objstmpconsiderwrapinfl.hxx b/sw/source/core/layout/objstmpconsiderwrapinfl.hxx
new file mode 100644
index 000000000..28b6acf9d
--- /dev/null
+++ b/sw/source/core/layout/objstmpconsiderwrapinfl.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJSTMPCONSIDERWRAPINFL_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_OBJSTMPCONSIDERWRAPINFL_HXX
+
+#include <vector>
+
+class SwAnchoredObject;
+
+class SwObjsMarkedAsTmpConsiderWrapInfluence
+{
+ private:
+ std::vector< SwAnchoredObject* > maObjsTmpConsiderWrapInfl;
+
+ public:
+ SwObjsMarkedAsTmpConsiderWrapInfluence();
+ ~SwObjsMarkedAsTmpConsiderWrapInfluence();
+
+ void Insert( SwAnchoredObject& _rAnchoredObj );
+ void Remove( SwAnchoredObject& _rAnchoredObj );
+ void Clear();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/pagechg.cxx b/sw/source/core/layout/pagechg.cxx
new file mode 100644
index 000000000..42720ee19
--- /dev/null
+++ b/sw/source/core/layout/pagechg.cxx
@@ -0,0 +1,2614 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <comphelper/lok.hxx>
+#include <ndole.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemiter.hxx>
+#include <fmtfsize.hxx>
+#include <fmthdft.hxx>
+#include <fmtclds.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <ftninfo.hxx>
+#include <frmtool.hxx>
+#include <tgrditem.hxx>
+#include <viewopt.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <frameformats.hxx>
+
+#include <viewimp.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <dcontact.hxx>
+#include <hints.hxx>
+#include <FrameControlsManager.hxx>
+
+#include <ftnidx.hxx>
+#include <bodyfrm.hxx>
+#include <ftnfrm.hxx>
+#include <tabfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <layact.hxx>
+#include <flyfrms.hxx>
+#include <htmltbl.hxx>
+#include <pagedesc.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sortedobjs.hxx>
+#include <calbck.hxx>
+#include <txtfly.hxx>
+
+using namespace ::com::sun::star;
+
+SwBodyFrame::SwBodyFrame( SwFrameFormat *pFormat, SwFrame* pSib ):
+ SwLayoutFrame( pFormat, pSib )
+{
+ mnFrameType = SwFrameType::Body;
+}
+
+void SwBodyFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ // Formatting of the body is too simple, thus, it gets its own format method.
+ // Borders etc. are not taken into account here.
+ // Width is taken from the PrtArea of the Upper. Height is the height of the
+ // PrtArea of the Upper minus any neighbors (for robustness).
+ // The PrtArea has always the size of the frame.
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ SwTwips nHeight = GetUpper()->getFramePrintArea().Height();
+ SwTwips nWidth = GetUpper()->getFramePrintArea().Width();
+ const SwFrame *pFrame = GetUpper()->Lower();
+ do
+ {
+ if ( pFrame != this )
+ {
+ if( pFrame->IsVertical() )
+ nWidth -= pFrame->getFrameArea().Width();
+ else
+ nHeight -= pFrame->getFrameArea().Height();
+ }
+ pFrame = pFrame->GetNext();
+ } while ( pFrame );
+
+ if ( nHeight < 0 )
+ {
+ nHeight = 0;
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( nHeight );
+
+ if( IsVertical() && !IsVertLR() && nWidth != aFrm.Width() )
+ {
+ aFrm.Pos().setX(aFrm.Pos().getX() + aFrm.Width() - nWidth);
+ }
+
+ aFrm.Width( nWidth );
+ }
+
+ bool bNoGrid = true;
+ if( GetUpper()->IsPageFrame() && static_cast<SwPageFrame*>(GetUpper())->HasGrid() )
+ {
+ SwTextGridItem const*const pGrid(
+ GetGridItem(static_cast<SwPageFrame*>(GetUpper())));
+ if( pGrid )
+ {
+ bNoGrid = false;
+ tools::Long nSum = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nSize = aRectFnSet.GetWidth(getFrameArea());
+ tools::Long nBorder = 0;
+ if( GRID_LINES_CHARS == pGrid->GetGridType() )
+ {
+ //for textgrid refactor
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ nBorder = nSize % (GetGridWidth(*pGrid, *pDoc));
+ nSize -= nBorder;
+ nBorder /= 2;
+ }
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetPosX( aPrt, nBorder );
+ aRectFnSet.SetWidth( aPrt, nSize );
+
+ // Height of body frame:
+ nBorder = aRectFnSet.GetHeight(getFrameArea());
+
+ // Number of possible lines in area of body frame:
+ tools::Long nNumberOfLines = nBorder / nSum;
+ if( nNumberOfLines > pGrid->GetLines() )
+ nNumberOfLines = pGrid->GetLines();
+
+ // Space required for nNumberOfLines lines:
+ nSize = nNumberOfLines * nSum;
+ nBorder -= nSize;
+ nBorder /= 2;
+
+ // #i21774# Footnotes and centering the grid does not work together:
+ const bool bAdjust = static_cast<SwPageFrame*>(GetUpper())->GetFormat()->GetDoc()->
+ GetFootnoteIdxs().empty();
+
+ aRectFnSet.SetPosY( aPrt, bAdjust ? nBorder : 0 );
+ aRectFnSet.SetHeight( aPrt, nSize );
+ }
+ }
+
+ if( bNoGrid )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Pos().setX(0);
+ aPrt.Pos().setY(0);
+ aPrt.Height( getFrameArea().Height() );
+ aPrt.Width( getFrameArea().Width() );
+ }
+
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+}
+
+SwPageFrame::SwPageFrame( SwFrameFormat *pFormat, SwFrame* pSib, SwPageDesc *pPgDsc ) :
+ SwFootnoteBossFrame( pFormat, pSib ),
+ m_pDesc( pPgDsc ),
+ m_nPhyPageNum( 0 )
+{
+ SetDerivedVert( false );
+ SetDerivedR2L( false );
+ if( m_pDesc )
+ {
+ m_bHasGrid = true;
+ SwTextGridItem const*const pGrid(GetGridItem(this));
+ if( !pGrid )
+ m_bHasGrid = false;
+ }
+ else
+ m_bHasGrid = false;
+ SetMaxFootnoteHeight( pPgDsc->GetFootnoteInfo().GetHeight() ?
+ pPgDsc->GetFootnoteInfo().GetHeight() : LONG_MAX );
+ mnFrameType = SwFrameType::Page;
+ m_bInvalidLayout = m_bInvalidContent = m_bInvalidSpelling = m_bInvalidSmartTags = m_bInvalidAutoCmplWrds = m_bInvalidWordCount = true;
+ m_bInvalidFlyLayout = m_bInvalidFlyContent = m_bInvalidFlyInCnt = m_bFootnotePage = m_bEndNotePage = false;
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if ( bBrowseMode )
+ {
+ aFrm.Height( 0 );
+ tools::Long nWidth = pSh->VisArea().Width();
+
+ if ( !nWidth )
+ {
+ nWidth = 5000; // changes anyway
+ }
+
+ aFrm.Width ( nWidth );
+ }
+ else
+ {
+ aFrm.SSize( pFormat->GetFrameSize().GetSize() );
+ }
+ }
+
+ // create and insert body area if it is not a blank page
+ SwDoc* pDoc(pFormat->GetDoc());
+ m_bEmptyPage = (pFormat == pDoc->GetEmptyPageFormat());
+
+ if(m_bEmptyPage)
+ {
+ return;
+ }
+
+ Calc(pRenderContext); // so that the PrtArea is correct
+ SwBodyFrame *pBodyFrame = new SwBodyFrame( pDoc->GetDfltFrameFormat(), this );
+ pBodyFrame->ChgSize( getFramePrintArea().SSize() );
+ pBodyFrame->Paste( this );
+ pBodyFrame->Calc(pRenderContext); // so that the columns can be inserted correctly
+ pBodyFrame->InvalidatePos();
+
+ if ( bBrowseMode )
+ InvalidateSize_();
+
+ // insert header/footer,, but only if active.
+ if ( pFormat->GetHeader().IsActive() )
+ PrepareHeader();
+ if ( pFormat->GetFooter().IsActive() )
+ PrepareFooter();
+
+ const SwFormatCol &rCol = pFormat->GetCol();
+ if ( rCol.GetNumCols() > 1 )
+ {
+ const SwFormatCol aOld; //ChgColumns() needs an old value
+ pBodyFrame->ChgColumns( aOld, rCol );
+ }
+
+}
+
+void SwPageFrame::DestroyImpl()
+{
+ // Cleanup the header-footer controls in all SwEditWins
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if (pSh)
+ {
+ for (SwViewShell& rSh : pSh->GetRingContainer())
+ {
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( &rSh );
+ if ( pWrtSh )
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ rEditWin.GetFrameControlsManager( ).RemoveControls( this );
+ }
+ }
+ }
+
+ // empty FlyContainer, deletion of the Flys is done by the anchor (in base class SwFrame)
+ if (m_pSortedObjs)
+ {
+ // Objects can be anchored at pages that are before their anchors (why ever...).
+ // In such cases, we would access already freed memory.
+ for (SwAnchoredObject* pAnchoredObj : *m_pSortedObjs)
+ {
+ pAnchoredObj->SetPageFrame( nullptr );
+ }
+ m_pSortedObjs.reset(); // reset to zero to prevent problems when detaching the Flys
+ }
+
+ // prevent access to destroyed pages
+ SwDoc *pDoc = GetFormat() ? GetFormat()->GetDoc() : nullptr;
+ if( pDoc && !pDoc->IsInDtor() )
+ {
+ if ( pSh )
+ {
+ SwViewShellImp *pImp = pSh->Imp();
+ pImp->SetFirstVisPageInvalid();
+ if ( pImp->IsAction() )
+ pImp->GetLayAction().SetAgain(true);
+ // #i9719# - retouche area of page
+ // including border and shadow area.
+ const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
+ SwRect aRetoucheRect;
+ SwPageFrame::GetBorderAndShadowBoundRect( getFrameArea(), pSh, pSh->GetOut(), aRetoucheRect, IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
+ pSh->AddPaintRect( aRetoucheRect );
+ }
+ }
+
+ SwFootnoteBossFrame::DestroyImpl();
+}
+
+SwPageFrame::~SwPageFrame()
+{
+}
+
+void SwPageFrame::CheckGrid( bool bInvalidate )
+{
+ bool bOld = m_bHasGrid;
+ m_bHasGrid = true;
+ SwTextGridItem const*const pGrid(GetGridItem(this));
+ m_bHasGrid = nullptr != pGrid;
+ if( !(bInvalidate || bOld != m_bHasGrid) )
+ return;
+
+ SwLayoutFrame* pBody = FindBodyCont();
+ if( pBody )
+ {
+ pBody->InvalidatePrt();
+ SwContentFrame* pFrame = pBody->ContainsContent();
+ while( pBody->IsAnLower( pFrame ) )
+ {
+ static_cast<SwTextFrame*>(pFrame)->Prepare();
+ pFrame = pFrame->GetNextContentFrame();
+ }
+ }
+ SetCompletePaint();
+}
+
+void SwPageFrame::CheckDirection( bool bVert )
+{
+ SvxFrameDirection nDir = GetFormat()->GetFormatAttr( RES_FRAMEDIR ).GetValue();
+ if( bVert )
+ {
+ if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ mbVertLR = false;
+ mbVertical = false;
+ }
+ else
+ {
+ mbVertical = true;
+
+ if(SvxFrameDirection::Vertical_RL_TB == nDir)
+ mbVertLR = false;
+ else if(SvxFrameDirection::Vertical_LR_TB==nDir)
+ mbVertLR = true;
+ }
+ }
+
+ mbInvalidVert = false;
+ }
+ else
+ {
+ if( SvxFrameDirection::Horizontal_RL_TB == nDir )
+ mbRightToLeft = true;
+ else
+ mbRightToLeft = false;
+ mbInvalidR2L = false;
+ }
+}
+
+/// create specific Flys for this page and format generic content
+static void lcl_FormatLay( SwLayoutFrame *pLay )
+{
+ vcl::RenderContext* pRenderContext = pLay->getRootFrame()->GetCurrShell()->GetOut();
+ // format all LayoutFrames - no tables, Flys etc.
+
+ SwFrame *pTmp = pLay->Lower();
+ // first the low-level ones
+ while ( pTmp )
+ {
+ const SwFrameType nTypes = SwFrameType::Root | SwFrameType::Page | SwFrameType::Column
+ | SwFrameType::Header | SwFrameType::Footer | SwFrameType::FtnCont
+ | SwFrameType::Ftn | SwFrameType::Body;
+ if ( pTmp->GetType() & nTypes )
+ ::lcl_FormatLay( static_cast<SwLayoutFrame*>(pTmp) );
+ pTmp = pTmp->GetNext();
+ }
+ pLay->Calc(pRenderContext);
+}
+
+/// Create Flys or register draw objects
+static void lcl_MakeObjs( const SwFrameFormats &rTable, SwPageFrame *pPage )
+{
+ // formats are in the special table of the document
+
+ for ( size_t i = 0; i < rTable.size(); ++i )
+ {
+ SwFrameFormat *pFormat = rTable[i];
+ const SwFormatAnchor &rAnch = pFormat->GetAnchor();
+ if ( rAnch.GetPageNum() == pPage->GetPhyPageNum() )
+ {
+ if( rAnch.GetContentAnchor() )
+ {
+ if (RndStdIds::FLY_AT_PAGE == rAnch.GetAnchorId())
+ {
+ SwFormatAnchor aAnch( rAnch );
+ aAnch.SetAnchor( nullptr );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ else
+ continue;
+ }
+
+ // is it a border or a SdrObject?
+ bool bSdrObj = RES_DRAWFRMFMT == pFormat->Which();
+ SdrObject *pSdrObj = nullptr;
+ if ( bSdrObj && nullptr == (pSdrObj = pFormat->FindSdrObject()) )
+ {
+ OSL_FAIL( "DrawObject not found." );
+ pFormat->GetDoc()->DelFrameFormat( pFormat );
+ --i;
+ continue;
+ }
+ // The object might be anchored to another page, e.g. when inserting
+ // a new page due to a page descriptor change. In such cases, the
+ // object needs to be moved.
+ // In some cases the object is already anchored to the correct page.
+ // This will be handled here and does not need to be coded extra.
+ SwPageFrame *pPg = pPage->IsEmptyPage() ? static_cast<SwPageFrame*>(pPage->GetNext()) : pPage;
+ if ( bSdrObj )
+ {
+ // OD 23.06.2003 #108784# - consider 'virtual' drawing objects
+ SwDrawContact *pContact =
+ static_cast<SwDrawContact*>(::GetUserCall(pSdrObj));
+ if ( auto pDrawVirtObj = dynamic_cast<SwDrawVirtObj *>( pSdrObj ) )
+ {
+ if ( pContact )
+ {
+ pDrawVirtObj->RemoveFromWriterLayout();
+ pDrawVirtObj->RemoveFromDrawingPage();
+ pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pDrawVirtObj )) );
+ }
+ }
+ else
+ {
+ if ( pContact->GetAnchorFrame() )
+ pContact->DisconnectFromLayout( false );
+ pPg->AppendDrawObj( *(pContact->GetAnchoredObj( pSdrObj )) );
+ }
+ }
+ else
+ {
+ SwIterator<SwFlyFrame,SwFormat> aIter( *pFormat );
+ SwFlyFrame *pFly = aIter.First();
+ if ( pFly)
+ {
+ if( pFly->GetAnchorFrame() )
+ pFly->AnchorFrame()->RemoveFly( pFly );
+ }
+ else
+ pFly = new SwFlyLayFrame( static_cast<SwFlyFrameFormat*>(pFormat), pPg, pPg );
+ pPg->AppendFly( pFly );
+ ::RegistFlys( pPg, pFly );
+ }
+ }
+ }
+}
+
+void SwPageFrame::PreparePage( bool bFootnote )
+{
+ SetFootnotePage( bFootnote );
+
+ // #i82258#
+ // Due to made change on OOo 2.0 code line, method <::lcl_FormatLay(..)> has
+ // the side effect, that the content of page header and footer are formatted.
+ // For this formatting it is needed that the anchored objects are registered
+ // at the <SwPageFrame> instance.
+ // Thus, first calling <::RegistFlys(..)>, then call <::lcl_FormatLay(..)>
+ ::RegistFlys( this, this );
+
+ if ( Lower() )
+ {
+ ::lcl_FormatLay( this );
+ }
+
+ // Flys and draw objects that are still attached to the document.
+ // Footnote pages do not have page-bound Flys!
+ // There might be Flys or draw objects that want to be placed on
+ // empty pages, however, the empty pages ignore that and the following
+ // pages take care of them.
+ if ( !bFootnote && !IsEmptyPage() )
+ {
+ SwDoc *pDoc = GetFormat()->GetDoc();
+
+ if ( GetPrev() && static_cast<SwPageFrame*>(GetPrev())->IsEmptyPage() )
+ lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), static_cast<SwPageFrame*>(GetPrev()) );
+ lcl_MakeObjs( *pDoc->GetSpzFrameFormats(), this );
+ }
+}
+
+void SwPageFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if(typeid(sw::PageFootnoteHint) == typeid(rHint))
+ {
+ // currently the savest way:
+ static_cast<SwRootFrame*>(GetUpper())->SetSuperfluous();
+ SetMaxFootnoteHeight(m_pDesc->GetFootnoteInfo().GetHeight());
+ if(!GetMaxFootnoteHeight())
+ SetMaxFootnoteHeight(LONG_MAX);
+ SetColMaxFootnoteHeight();
+ // here, the page might be destroyed:
+ static_cast<SwRootFrame*>(GetUpper())->RemoveFootnotes(nullptr, false, true);
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(auto pSh = getRootFrame()->GetCurrShell())
+ pSh->SetFirstVisPageInvalid();
+
+ SwPageFrameInvFlags eInvFlags = SwPageFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pOItem = aOIter.NextItem();
+ pNItem = aNIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if (eInvFlags == SwPageFrameInvFlags::NONE)
+ return;
+
+ InvalidatePage( this );
+ if(eInvFlags & SwPageFrameInvFlags::InvalidatePrt)
+ InvalidatePrt_();
+ if(eInvFlags & SwPageFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ if(eInvFlags & SwPageFrameInvFlags::InvalidateNextPos && GetNext() )
+ GetNext()->InvalidatePos();
+ if(eInvFlags & SwPageFrameInvFlags::PrepareHeader)
+ PrepareHeader();
+ if(eInvFlags & SwPageFrameInvFlags::PrepareFooter)
+ PrepareFooter();
+ if(eInvFlags & SwPageFrameInvFlags::CheckGrid)
+ CheckGrid(bool(eInvFlags & SwPageFrameInvFlags::InvalidateGrid));
+ } else
+ SwFrame::SwClientNotify(rModify, rHint);
+}
+
+void SwPageFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwPageFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ {
+ case RES_FMT_CHG:
+ {
+ // state of m_bEmptyPage needs to be determined newly
+ const bool bNewState(GetFormat() == GetFormat()->GetDoc()->GetEmptyPageFormat());
+
+ if(m_bEmptyPage != bNewState)
+ {
+ // copy new state
+ m_bEmptyPage = bNewState;
+
+ if(nullptr == GetLower())
+ {
+ // if we were an empty page before there is not yet a BodyArea in the
+ // form of a SwBodyFrame, see constructor
+ SwViewShell* pSh(getRootFrame()->GetCurrShell());
+ vcl::RenderContext* pRenderContext(pSh ? pSh->GetOut() : nullptr);
+ Calc(pRenderContext); // so that the PrtArea is correct
+ SwBodyFrame* pBodyFrame = new SwBodyFrame(GetFormat(), this);
+ pBodyFrame->ChgSize(getFramePrintArea().SSize());
+ pBodyFrame->Paste(this);
+ pBodyFrame->InvalidatePos();
+ }
+ }
+
+ // If the frame format is changed, several things might also change:
+ // 1. columns:
+ assert(pOld && pNew); //FMT_CHG Missing Format
+ const SwFormat *const pOldFormat = static_cast<const SwFormatChg*>(pOld)->pChangedFormat;
+ const SwFormat *const pNewFormat = static_cast<const SwFormatChg*>(pNew)->pChangedFormat;
+ assert(pOldFormat && pNewFormat); //FMT_CHG Missing Format
+ const SwFormatCol &rOldCol = pOldFormat->GetCol();
+ const SwFormatCol &rNewCol = pNewFormat->GetCol();
+ if( rOldCol != rNewCol )
+ {
+ SwLayoutFrame *pB = FindBodyCont();
+ assert(pB && "Page without Body.");
+ pB->ChgColumns( rOldCol, rNewCol );
+ rInvFlags |= SwPageFrameInvFlags::CheckGrid;
+ }
+
+ // 2. header and footer:
+ const SwFormatHeader &rOldH = pOldFormat->GetHeader();
+ const SwFormatHeader &rNewH = pNewFormat->GetHeader();
+ if( rOldH != rNewH )
+ rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
+
+ const SwFormatFooter &rOldF = pOldFormat->GetFooter();
+ const SwFormatFooter &rNewF = pNewFormat->GetFooter();
+ if( rOldF != rNewF )
+ rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
+ CheckDirChange();
+
+ [[fallthrough]];
+ }
+ case RES_FRM_SIZE:
+ {
+ const SwRect aOldPageFrameRect( getFrameArea() );
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ setFrameAreaSizeValid(false);
+ // OD 28.10.2002 #97265# - Don't call <SwPageFrame::MakeAll()>
+ // Calculation of the page is not necessary, because its size is
+ // invalidated here and further invalidation is done in the
+ // calling method <SwPageFrame::Modify(..)> and probably by calling
+ // <SwLayoutFrame::SwClientNotify(..)> at the end.
+ // It can also causes inconsistences, because the lowers are
+ // adjusted, but not calculated, and a <SwPageFrame::MakeAll()> of
+ // a next page is called. This is performed on the switch to the
+ // online layout.
+ //MakeAll();
+ }
+ else if (pNew)
+ {
+ const SwFormatFrameSize &rSz = nWhich == RES_FMT_CHG ?
+ static_cast<const SwFormatChg*>(pNew)->pChangedFormat->GetFrameSize() :
+ static_cast<const SwFormatFrameSize&>(*pNew);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( std::max( rSz.GetHeight(), tools::Long(MINLAY) ) );
+ aFrm.Width ( std::max( rSz.GetWidth(), tools::Long(MINLAY) ) );
+ }
+
+ if ( GetUpper() )
+ {
+ static_cast<SwRootFrame*>(GetUpper())->CheckViewLayout( nullptr, nullptr );
+ }
+ }
+ // cleanup Window
+ if( pSh && pSh->GetWin() && aOldPageFrameRect.HasArea() )
+ {
+ // #i9719# - consider border and shadow of
+ // page frame for determine 'old' rectangle - it's used for invalidating.
+ const bool bRightSidebar = (SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT);
+ SwRect aOldRectWithBorderAndShadow;
+ SwPageFrame::GetBorderAndShadowBoundRect( aOldPageFrameRect, pSh, pSh->GetOut(), aOldRectWithBorderAndShadow,
+ IsLeftShadowNeeded(), IsRightShadowNeeded(), bRightSidebar );
+ pSh->InvalidateWindows( aOldRectWithBorderAndShadow );
+ }
+ rInvFlags |= SwPageFrameInvFlags::InvalidatePrt | SwPageFrameInvFlags::SetCompletePaint;
+ if ( aOldPageFrameRect.Height() != getFrameArea().Height() )
+ rInvFlags |= SwPageFrameInvFlags::InvalidateNextPos;
+ }
+ break;
+
+ case RES_COL:
+ assert(pOld && pNew); //COL Missing Format
+ if (pOld && pNew)
+ {
+ SwLayoutFrame *pB = FindBodyCont();
+ assert(pB); //page without body
+ pB->ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
+ rInvFlags |= SwPageFrameInvFlags::SetCompletePaint | SwPageFrameInvFlags::CheckGrid;
+ }
+ break;
+
+ case RES_HEADER:
+ rInvFlags |= SwPageFrameInvFlags::PrepareHeader;
+ break;
+
+ case RES_FOOTER:
+ rInvFlags |= SwPageFrameInvFlags::PrepareFooter;
+ break;
+ case RES_TEXTGRID:
+ rInvFlags |= SwPageFrameInvFlags::CheckGrid | SwPageFrameInvFlags::InvalidateGrid;
+ break;
+ case RES_FRAMEDIR :
+ CheckDirChange();
+ break;
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+/// get information from Modify
+bool SwPageFrame::GetInfo( SfxPoolItem & rInfo ) const
+{
+ if( RES_AUTOFMT_DOCNODE == rInfo.Which() )
+ {
+ // a page frame exists, so use this one
+ return false;
+ }
+ return true; // continue searching
+}
+
+void SwPageFrame::SetPageDesc( SwPageDesc *pNew, SwFrameFormat *pFormat )
+{
+ m_pDesc = pNew;
+ if ( pFormat )
+ SetFrameFormat( pFormat );
+}
+
+/* determine the right PageDesc:
+ * 0. from the document for footnote and endnote pages
+ * 1. from the first BodyContent below a page
+ * 2. from PageDesc of the predecessor page
+ * 3. from PageDesc of the previous page if blank page
+ * 3.1 from PageDesc of the next page if no predecessor exists
+ * 4. default PageDesc
+ * 5. In BrowseMode use the first paragraph or default PageDesc.
+ */
+SwPageDesc *SwPageFrame::FindPageDesc()
+{
+ // 0.
+ if ( IsFootnotePage() )
+ {
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ if ( IsEndNotePage() )
+ return pDoc->GetEndNoteInfo().GetPageDesc( *pDoc );
+ else
+ return pDoc->GetFootnoteInfo().GetPageDesc( *pDoc );
+ }
+
+ SwPageDesc *pRet = nullptr;
+
+ //5.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ SwContentFrame *pFrame = GetUpper()->ContainsContent();
+ while (pFrame && !pFrame->IsInDocBody())
+ pFrame = pFrame->GetNextContentFrame();
+ if (pFrame)
+ {
+ SwFrame *pFlow = pFrame;
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
+ }
+ if ( !pRet )
+ pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
+ return pRet;
+ }
+
+ SwFrame *pFlow = FindFirstBodyContent();
+ if ( pFlow && pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+
+ //1.
+ if ( pFlow )
+ {
+ SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
+ if ( !pTmp->IsFollow() )
+ pRet = const_cast<SwPageDesc*>(pFlow->GetPageDescItem().GetPageDesc());
+ }
+
+ //3. and 3.1
+ if ( !pRet && IsEmptyPage() )
+ // FME 2008-03-03 #i81544# lijian/fme: an empty page should have
+ // the same page description as its prev, just like after construction
+ // of the empty page.
+ pRet = GetPrev() ? static_cast<SwPageFrame*>(GetPrev())->GetPageDesc() :
+ GetNext() ? static_cast<SwPageFrame*>(GetNext())->GetPageDesc() : nullptr;
+
+ //2.
+ if ( !pRet )
+ pRet = GetPrev() ?
+ static_cast<SwPageFrame*>(GetPrev())->GetPageDesc()->GetFollow() : nullptr;
+
+ //4.
+ if ( !pRet )
+ pRet = &GetFormat()->GetDoc()->GetPageDesc( 0 );
+
+ OSL_ENSURE( pRet, "could not find page descriptor." );
+ return pRet;
+}
+
+// Notify if the RootFrame changes its size
+void AdjustSizeChgNotify( SwRootFrame *pRoot )
+{
+ const bool bOld = pRoot->IsSuperfluous();
+ pRoot->mbCheckSuperfluous = false;
+ if ( pRoot->GetCurrShell() )
+ {
+ for(SwViewShell& rSh : pRoot->GetCurrShell()->GetRingContainer())
+ {
+ if( pRoot == rSh.GetLayout() )
+ {
+ rSh.SizeChgNotify();
+ if ( rSh.Imp() )
+ rSh.Imp()->NotifySizeChg( pRoot->getFrameArea().SSize() );
+ }
+ }
+ }
+ pRoot->mbCheckSuperfluous = bOld;
+}
+
+inline void SetLastPage( SwPageFrame *pPage )
+{
+ static_cast<SwRootFrame*>(pPage->GetUpper())->mpLastPage = pPage;
+}
+
+void SwPageFrame::Cut()
+{
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( !IsEmptyPage() )
+ {
+ if ( GetNext() )
+ GetNext()->InvalidatePos();
+
+ // move Flys whose anchor is on a different page (draw objects are not relevant here)
+ if ( GetSortedObjs() )
+ {
+ size_t i = 0;
+ while ( GetSortedObjs() && i < GetSortedObjs()->size() )
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*GetSortedObjs())[i];
+
+ if ( auto pFly = dynamic_cast<SwFlyAtContentFrame *>( pAnchoredObj ) )
+ {
+ SwPageFrame *pAnchPage = pFly->GetAnchorFrame() ?
+ pFly->AnchorFrame()->FindPageFrame() : nullptr;
+ if ( pAnchPage && (pAnchPage != this) )
+ {
+ MoveFly( pFly, pAnchPage );
+ pFly->InvalidateSize();
+ pFly->InvalidatePos_();
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+ }
+ // cleanup Window
+ if ( pSh && pSh->GetWin() )
+ pSh->InvalidateWindows( getFrameArea() );
+ }
+
+ // decrease the root's page number
+ static_cast<SwRootFrame*>(GetUpper())->DecrPhyPageNums();
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
+ if ( pPg )
+ {
+ while ( pPg )
+ {
+ --pPg->m_nPhyPageNum;
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+ }
+ else
+ ::SetLastPage( static_cast<SwPageFrame*>(GetPrev()) );
+
+ SwFrame* pRootFrame = GetUpper();
+
+ // cut all connections
+ RemoveFromLayout();
+
+ if ( pRootFrame )
+ static_cast<SwRootFrame*>(pRootFrame)->CheckViewLayout( nullptr, nullptr );
+}
+
+void SwPageFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent->IsRootFrame(), "Parent is no Root." );
+ OSL_ENSURE( pParent, "No parent for Paste()." );
+ OSL_ENSURE( pParent != this, "I'm my own parent." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I am still registered somewhere." );
+
+ // insert into tree structure
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ // increase the root's page number
+ static_cast<SwRootFrame*>(GetUpper())->IncrPhyPageNums();
+ if( GetPrev() )
+ SetPhyPageNum( static_cast<SwPageFrame*>(GetPrev())->GetPhyPageNum() + 1 );
+ else
+ SetPhyPageNum( 1 );
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(GetNext());
+ if ( pPg )
+ {
+ while ( pPg )
+ {
+ ++pPg->m_nPhyPageNum;
+ pPg->InvalidatePos_();
+ pPg->InvalidateLayout();
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+ }
+ else
+ ::SetLastPage( this );
+
+ if( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
+ InvalidateSize_();
+
+ InvalidatePos();
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( pSh )
+ pSh->SetFirstVisPageInvalid();
+
+ getRootFrame()->CheckViewLayout( nullptr, nullptr );
+}
+
+static void lcl_PrepFlyInCntRegister( SwContentFrame *pFrame )
+{
+ pFrame->Prepare( PrepareHint::Register );
+ if( !pFrame->GetDrawObjs() )
+ return;
+
+ for(SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
+ {
+ // #i28701#
+ if ( auto pFly = dynamic_cast<SwFlyInContentFrame *>( pAnchoredObj ) )
+ {
+ SwContentFrame *pCnt = pFly->ContainsContent();
+ while ( pCnt )
+ {
+ lcl_PrepFlyInCntRegister( pCnt );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ }
+}
+
+void SwPageFrame::PrepareRegisterChg()
+{
+ SwContentFrame *pFrame = FindFirstBodyContent();
+ while( pFrame )
+ {
+ lcl_PrepFlyInCntRegister( pFrame );
+ pFrame = pFrame->GetNextContentFrame();
+ if( !IsAnLower( pFrame ) )
+ break;
+ }
+ if( !GetSortedObjs() )
+ return;
+
+ for(SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
+ {
+ // #i28701#
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ pFrame = pFly->ContainsContent();
+ while ( pFrame )
+ {
+ ::lcl_PrepFlyInCntRegister( pFrame );
+ pFrame = pFrame->GetNextContentFrame();
+ }
+ }
+ }
+}
+
+namespace sw {
+
+/// check if there's content on the page that requires it to exist
+bool IsPageFrameEmpty(SwPageFrame const& rPage)
+{
+ bool bExistEssentialObjs = (nullptr != rPage.GetSortedObjs());
+ if (bExistEssentialObjs)
+ {
+ // Only because the page has Flys does not mean that it is needed. If all Flys are
+ // attached to generic content it is also superfluous (checking DocBody should be enough)
+ // OD 19.06.2003 - consider that drawing objects in
+ // header/footer are supported now.
+ bool bOnlySuperfluousObjs = true;
+ SwSortedObjs const& rObjs = *rPage.GetSortedObjs();
+ for (size_t i = 0; bOnlySuperfluousObjs && i < rObjs.size(); ++i)
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = rObjs[i];
+ // do not consider hidden objects
+ if ( rPage.GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pAnchoredObj->GetDrawObj()->GetLayer() ) &&
+ !pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() )
+ {
+ bOnlySuperfluousObjs = false;
+ }
+ }
+ bExistEssentialObjs = !bOnlySuperfluousObjs;
+ }
+
+ // optimization: check first if essential objects exist.
+ const SwLayoutFrame* pBody = nullptr;
+ if ( bExistEssentialObjs ||
+ rPage.FindFootnoteCont() ||
+ (nullptr != (pBody = rPage.FindBodyCont()) &&
+ ( pBody->ContainsContent() ||
+ // check for section frames that are being formatted on the stack
+ rPage.ContainsDeleteForbiddenLayFrame() ||
+ // #i47580#
+ // Do not delete page if there's an empty tabframe
+ // left. I think it might be correct to use ContainsAny()
+ // instead of ContainsContent() to cover the empty-table-case,
+ // but I'm not fully sure, since ContainsAny() also returns
+ // SectionFrames. Therefore I prefer to do it the safe way:
+ ( pBody->Lower() && pBody->Lower()->IsTabFrame() ) ) ) )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+} // namespace sw
+
+//FIXME: provide missing documentation
+/** Check all pages (starting from the given one) if they use the appropriate frame format.
+ *
+ * If "wrong" pages are found, try to fix this as simple as possible.
+ *
+ * Also delete pages that don't have content on them.
+ *
+ * @param pStart the page from where to start searching
+ * @param bNotifyFields
+ * @param ppPrev
+ */
+void SwFrame::CheckPageDescs( SwPageFrame *pStart, bool bNotifyFields, SwPageFrame** ppPrev )
+{
+ SAL_INFO( "sw.pageframe", "(CheckPageDescs in phy: " << pStart->GetPhyPageNum() );
+ assert(pStart && "no starting page.");
+
+ SwViewShell *pSh = pStart->getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+
+ if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
+ {
+ pImp->GetLayAction().SetCheckPageNum( pStart->GetPhyPageNum() );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs out fast - via SetCheckPageNum: "
+ << pStart->GetPhyPageNum() << ")" );
+ return;
+ }
+
+ // For the update of page numbering fields, nDocPos provides
+ // the page position from where invalidation should start.
+ SwTwips nDocPos = LONG_MAX;
+
+ SwRootFrame *pRoot = static_cast<SwRootFrame*>(pStart->GetUpper());
+ SwDoc* pDoc = pStart->GetFormat()->GetDoc();
+ const bool bFootnotes = !pDoc->GetFootnoteIdxs().empty();
+
+ SwPageFrame *pPage = pStart;
+ if( pPage->GetPrev() && static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ while ( pPage )
+ {
+ SwPageFrame *pPrevPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ SwPageFrame *pNextPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ SwPageDesc *pDesc = pPage->FindPageDesc();
+ /// page is intentionally empty page
+ bool bIsEmpty = pPage->IsEmptyPage();
+ // false for intentionally empty pages, they need additional check
+ bool isPageFrameEmpty(!bIsEmpty && sw::IsPageFrameEmpty(*pPage));
+ bool bIsOdd = pPage->OnRightPage();
+ bool bWantOdd = pPage->WannaRightPage();
+ bool bFirst = pPage->OnFirstPage();
+ SwFrameFormat *pFormatWish = bWantOdd
+ ? pDesc->GetRightFormat(bFirst) : pDesc->GetLeftFormat(bFirst);
+
+ if ( bIsOdd != bWantOdd ||
+ pDesc != pPage->GetPageDesc() || // wrong Desc
+ ( pFormatWish != pPage->GetFormat() && // wrong format and
+ ( !bIsEmpty || pFormatWish ) // not blank /empty
+ )
+ )
+ {
+ // Updating a page might take a while, so check the WaitCursor
+ if( pImp )
+ pImp->CheckWaitCursor();
+
+ // invalidate the field, starting from here
+ if ( nDocPos == LONG_MAX )
+ nDocPos = pPrevPage ? pPrevPage->getFrameArea().Top() : pPage->getFrameArea().Top();
+
+ // Cases:
+ // 1. Empty page should be "normal" page -> remove empty page and take next one
+ // 2. Empty page should have different descriptor -> change
+ // 3. Normal page should be empty -> insert empty page if previous page
+ // is not empty, otherwise see (6).
+ // 4. Normal page should have different descriptor -> change
+ // 5. Normal page should have different format -> change
+ // 6. No "wish" format provided -> take the "other" format (left/right) of the PageDesc
+
+ if ( bIsEmpty && ( pFormatWish || //1.
+ ( !bWantOdd && !pPrevPage ) ) )
+ {
+ // Check all cases for the next page, so we don't oscillate empty pages
+ // Skip case 1 and 2, as we require a non-empty next page to save the empty page
+ // Case 3 is the one we actually want to predict and skip
+ // We can skip the empty check of case 3, as we just work on an existing next page
+ bool bNextWantOdd;
+ SwPageDesc *pNextDesc;
+ if ( pNextPage && !pNextPage->IsEmptyPage() && //3.
+ pNextPage->OnRightPage() == (bNextWantOdd = pNextPage->WannaRightPage()) &&
+ pNextPage->GetPageDesc() == (pNextDesc = pNextPage->FindPageDesc()) ) //4.
+ {
+ bool bNextFirst = pNextPage->OnFirstPage();
+ SwFrameFormat *pNextFormatWish = bNextWantOdd ? //5.
+ pNextDesc->GetRightFormat(bNextFirst) : pNextDesc->GetLeftFormat(bNextFirst);
+ if ( !pNextFormatWish ) // 6.
+ pNextFormatWish = bNextWantOdd ? pNextDesc->GetLeftFormat() : pNextDesc->GetRightFormat();
+ if ( pNextFormatWish && pNextPage->GetFormat() == pNextFormatWish )
+ {
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 1+3 - skip next page of p: " << pPage );
+ if (pPrevPage && pPage->GetPageDesc() != pPrevPage->GetPageDesc())
+ pPage->SetPageDesc( pPrevPage->GetPageDesc(), nullptr );
+ // We can skip the next page, as all checks were already done!
+ pPage = static_cast<SwPageFrame*>(pNextPage->GetNext());
+ continue;
+ }
+ }
+
+ pPage->Cut();
+ bool bUpdatePrev = false;
+ if (ppPrev && *ppPrev == pPage)
+ bUpdatePrev = true;
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 1 - destroy p: " << pPage );
+ SwFrame::DestroyFrame(pPage);
+ if ( pStart == pPage )
+ pStart = pNextPage;
+ pPage = pNextPage;
+ if (bUpdatePrev)
+ *ppPrev = pNextPage;
+ continue;
+ }
+ else if ( bIsEmpty && !pFormatWish && //2.
+ pDesc != pPage->GetPageDesc() )
+ {
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 2 - set desc p: " << pPage << " d: " << pDesc );
+ pPage->SetPageDesc( pDesc, nullptr );
+ }
+ else if ( !bIsEmpty && //3.
+ bIsOdd != bWantOdd &&
+ ( ( !pPrevPage && !bWantOdd ) ||
+ ( pPrevPage && !pPrevPage->IsEmptyPage() )
+ )
+ )
+ {
+ if ( pPrevPage )
+ pDesc = pPrevPage->GetPageDesc();
+ SwPageFrame *pTmp = new SwPageFrame( pDoc->GetEmptyPageFormat(), pRoot, pDesc );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 3 - insert empty p: " << pTmp << " d: " << pDesc );
+ pTmp->Paste( pRoot, pPage );
+ pTmp->PreparePage( false );
+ pPage = pTmp;
+ isPageFrameEmpty = false; // don't delete it right away!
+ }
+ else if ( pPage->GetPageDesc() != pDesc ) //4.
+ {
+ SwPageDesc *pOld = pPage->GetPageDesc();
+ pPage->SetPageDesc( pDesc, pFormatWish );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 4 - set desc + format p: " << pPage
+ << " d: " << pDesc << " f: " << pFormatWish );
+ if ( bFootnotes )
+ {
+ // If specific values of the FootnoteInfo are changed, something has to happen.
+ // We try to limit the damage...
+ // If the page has no FootnoteCont it might be problematic.
+ // Let's hope that invalidation is enough.
+ SwFootnoteContFrame *pCont = pPage->FindFootnoteCont();
+ if ( pCont && !(pOld->GetFootnoteInfo() == pDesc->GetFootnoteInfo()) )
+ pCont->InvalidateAll_();
+ }
+ }
+ else if ( pFormatWish && pPage->GetFormat() != pFormatWish ) //5.
+ {
+ pPage->SetFrameFormat( pFormatWish );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 5 - set format p: " << pPage << " f: " << pFormatWish );
+ }
+ else if ( !pFormatWish ) //6.
+ {
+ // get format with inverted logic
+ pFormatWish = bWantOdd ? pDesc->GetLeftFormat() : pDesc->GetRightFormat();
+ if ( pFormatWish && pPage->GetFormat() != pFormatWish )
+ {
+ pPage->SetFrameFormat( pFormatWish );
+ SAL_INFO( "sw.pageframe", "CheckPageDescs phys: " << pPage->GetPhyPageNum()
+ << " c: 6 - set format p: " << pPage << " f: " << pFormatWish );
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "CheckPageDescs, missing solution" );
+ }
+#endif
+ }
+ assert(!bIsEmpty || !isPageFrameEmpty);
+ const bool bWantRemovePage = bIsEmpty || isPageFrameEmpty;
+ if (bWantRemovePage && !pPage->IsDeleteForbidden())
+ {
+ // It also might be that an empty page is not needed at all.
+ // However, the algorithm above cannot determine that. It is not needed if the following
+ // page can live without it. Do obtain that information, we need to dig deeper...
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(pPage->GetNext());
+ if (isPageFrameEmpty || !pPg || pPage->OnRightPage() == pPg->WannaRightPage())
+ {
+ // The following page can find a FrameFormat or has no successor -> empty page not needed
+ SwPageFrame *pTmp = static_cast<SwPageFrame*>(pPage->GetNext());
+ if (isPageFrameEmpty && pPage->GetPrev())
+ { // check previous *again* vs. its new next! see "ooo321_stylepagenumber.odt"
+ pTmp = static_cast<SwPageFrame*>(pPage->GetPrev());
+ }
+ pPage->Cut();
+ bool bUpdatePrev = false;
+ if (ppPrev && *ppPrev == pPage)
+ bUpdatePrev = true;
+ SwFrame::DestroyFrame(pPage);
+ SAL_INFO( "sw.pageframe", "CheckPageDescs - handle bIsEmpty - destroy p: " << pPage );
+ if ( pStart == pPage )
+ pStart = pTmp;
+ pPage = pTmp;
+ if (bUpdatePrev)
+ *ppPrev = pTmp;
+ continue;
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ pRoot->SetAssertFlyPages();
+ SwRootFrame::AssertPageFlys( pStart );
+
+ if ( bNotifyFields && (!pImp || !pImp->IsUpdateExpFields()) )
+ {
+ SwDocPosUpdate aMsgHint( nDocPos );
+ pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ //1. check if two empty pages are behind one another
+ bool bEmpty = false;
+ SwPageFrame *pPg = pStart;
+ while ( pPg )
+ {
+ if ( pPg->IsEmptyPage() )
+ {
+ if ( bEmpty )
+ {
+ OSL_FAIL( "double empty pages." );
+ break; // once is enough
+ }
+ bEmpty = true;
+ }
+ else
+ bEmpty = false;
+
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ }
+#endif
+ SAL_INFO( "sw.pageframe", "CheckPageDescs out)" );
+}
+
+namespace
+{
+ bool isDeleteForbidden(const SwPageFrame *pDel)
+ {
+ if (pDel->IsDeleteForbidden())
+ return true;
+ const SwLayoutFrame* pBody = pDel->FindBodyCont();
+ const SwFrame* pBodyContent = pBody ? pBody->Lower() : nullptr;
+ return pBodyContent && pBodyContent->IsDeleteForbidden();
+ }
+
+ bool doInsertPage( SwRootFrame *pRoot, SwPageFrame **pRefSibling,
+ SwFrameFormat *pFormat, SwPageDesc *pDesc,
+ bool bFootnote, SwPageFrame **pRefPage )
+ {
+ SwPageFrame *pPage = new SwPageFrame(pFormat, pRoot, pDesc);
+ SwPageFrame *pSibling = *pRefSibling;
+ if ( pRefPage )
+ {
+ *pRefPage = pPage;
+ SAL_INFO( "sw.pageframe", "doInsertPage p: " << pPage
+ << " d: " << pDesc << " f: " << pFormat );
+ }
+ else
+ SAL_INFO( "sw.pageframe", "doInsertPage - insert empty p: "
+ << pPage << " d: " << pDesc );
+ pPage->Paste( pRoot, pSibling );
+
+ SwViewShell* pViewShell = pRoot->GetCurrShell();
+ if (pViewShell && pViewShell->GetViewOptions()->IsHideWhitespaceMode())
+ {
+ // Hide-whitespace mode does not shrink the last page, so resize the page that used to
+ // be the last one.
+ if (SwFrame* pPrevPage = pPage->GetPrev())
+ {
+ pPrevPage->InvalidateSize();
+ }
+ }
+
+ pPage->PreparePage( bFootnote );
+ // If the sibling has no body text, destroy it as long as it is no footnote page.
+ if (!pSibling)
+ return true;
+ if (pSibling->IsFootnotePage())
+ return true;
+ if (pSibling->FindFirstBodyContent())
+ return true;
+
+ if (!pRefPage || !isDeleteForbidden(pSibling))
+ {
+ pRoot->RemovePage( pRefSibling, SwRemoveResult::Next ) ;
+ return false;
+ }
+
+ return true;
+ }
+}
+
+SwPageFrame *SwFrame::InsertPage( SwPageFrame *pPrevPage, bool bFootnote )
+{
+ SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPrevPage->GetUpper());
+ SwPageFrame *pSibling = static_cast<SwPageFrame*>(pPrevPage->GetNext());
+ SwPageDesc *pDesc = nullptr;
+
+ // insert right (odd) or left (even) page?
+ bool bNextRightPage = !pPrevPage->OnRightPage();
+ bool bWishedRightPage = bNextRightPage;
+
+ // Which PageDesc is relevant?
+ // For ContentFrame take the one from format if provided,
+ // otherwise from the Follow of the PrevPage
+ if ( IsFlowFrame() && !SwFlowFrame::CastFlowFrame( this )->IsFollow() )
+ {
+ SwFormatPageDesc &rDesc = const_cast<SwFormatPageDesc&>(GetPageDescItem());
+ pDesc = rDesc.GetPageDesc();
+ if ( rDesc.GetNumOffset() )
+ {
+ ::std::optional<sal_uInt16> oNumOffset = rDesc.GetNumOffset();
+ bWishedRightPage = sw::IsRightPageByNumber(*pRoot, *oNumOffset);
+ // use the opportunity to set the flag at root
+ pRoot->SetVirtPageNum( true );
+ }
+ }
+ if ( !pDesc )
+ pDesc = pPrevPage->GetPageDesc()->GetFollow();
+
+ assert(pDesc && "Missing PageDesc");
+ if( !(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()) )
+ bWishedRightPage = !bWishedRightPage;
+ bool const bWishedFirst = pDesc != pPrevPage->GetPageDesc();
+
+ SwDoc *pDoc = pPrevPage->GetFormat()->GetDoc();
+ bool bCheckPages = false;
+ // If there is no FrameFormat for this page, create an empty page.
+ if (bWishedRightPage != bNextRightPage)
+ {
+ if( doInsertPage( pRoot, &pSibling, pDoc->GetEmptyPageFormat(),
+ pPrevPage->GetPageDesc(), bFootnote, nullptr ) )
+ bCheckPages = true;
+ }
+ SwFrameFormat *const pFormat( bWishedRightPage
+ ? pDesc->GetRightFormat(bWishedFirst)
+ : pDesc->GetLeftFormat(bWishedFirst) );
+ assert(pFormat);
+ SwPageFrame *pPage = nullptr;
+ if( doInsertPage( pRoot, &pSibling, pFormat, pDesc, bFootnote, &pPage ) )
+ bCheckPages = true;
+
+ if ( pSibling )
+ {
+ if ( bCheckPages )
+ {
+ CheckPageDescs( pSibling, false );
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ if ( pImp && pImp->IsAction() && !pImp->GetLayAction().IsCheckPages() )
+ {
+ const sal_uInt16 nNum = pImp->GetLayAction().GetCheckPageNum();
+ if ( nNum == pPrevPage->GetPhyPageNum() + 1 )
+ {
+ pImp->GetLayAction().SetCheckPageNumDirect(
+ pSibling->GetPhyPageNum() );
+ SAL_INFO( "sw.pageframe", "InsertPage - SetCheckPageNumDirect: "
+ << pSibling->GetPhyPageNum() );
+ }
+ return pPage;
+ }
+ }
+ else
+ SwRootFrame::AssertPageFlys( pSibling );
+ }
+
+ // For the update of page numbering fields, nDocPos provides
+ // the page position from where invalidation should start.
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( !pSh || !pSh->Imp()->IsUpdateExpFields() )
+ {
+ SwDocPosUpdate aMsgHint( pPrevPage->getFrameArea().Top() );
+ pDoc->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+ return pPage;
+}
+
+sw::sidebarwindows::SidebarPosition SwPageFrame::SidebarPosition() const
+{
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( !pSh || pSh->GetViewOptions()->getBrowseMode() )
+ {
+ return sw::sidebarwindows::SidebarPosition::RIGHT;
+ }
+ else
+ {
+ const bool bLTR = getRootFrame()->IsLeftToRightViewLayout();
+ const bool bBookMode = pSh->GetViewOptions()->IsViewLayoutBookMode();
+ const bool bRightSidebar = bLTR ? (!bBookMode || OnRightPage()) : (bBookMode && !OnRightPage());
+
+ return bRightSidebar
+ ? sw::sidebarwindows::SidebarPosition::RIGHT
+ : sw::sidebarwindows::SidebarPosition::LEFT;
+ }
+}
+
+SwTwips SwRootFrame::GrowFrame( SwTwips nDist, bool bTst, bool )
+{
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.AddHeight(nDist );
+ }
+
+ return nDist;
+}
+
+SwTwips SwRootFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool )
+{
+ OSL_ENSURE( nDist >= 0, "nDist < 0." );
+ OSL_ENSURE( nDist <= getFrameArea().Height(), "nDist greater than current size." );
+
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.AddHeight( -nDist );
+ }
+
+ return nDist;
+}
+
+void SwRootFrame::RemovePage( SwPageFrame **pDelRef, SwRemoveResult eResult )
+{
+ SwPageFrame *pDel = *pDelRef;
+ (*pDelRef) = static_cast<SwPageFrame*>(
+ eResult == SwRemoveResult::Next ? pDel->GetNext() : pDel->GetPrev() );
+ if ( !GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ RemoveFootnotes( pDel, true );
+ pDel->Cut();
+ SwFrame::DestroyFrame( pDel );
+}
+
+/// remove pages that are not needed at all
+void SwRootFrame::RemoveSuperfluous()
+{
+ // A page is empty if the body text area has no ContentFrame, but not if there
+ // is at least one Fly or one footnote attached to the page. Two runs are
+ // needed: one for endnote pages and one for the pages of the body text.
+
+ if ( !IsSuperfluous() )
+ return;
+ mbCheckSuperfluous = false;
+
+ SwPageFrame *pPage = GetLastPage();
+ tools::Long nDocPos = LONG_MAX;
+
+ // Check the corresponding last page if it is empty and stop loop at the last non-empty page.
+ do
+ {
+ if (!sw::IsPageFrameEmpty(*pPage))
+ {
+ if ( pPage->IsFootnotePage() )
+ {
+ while ( pPage->IsFootnotePage() )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetPrev());
+ OSL_ENSURE( pPage, "only endnote pages remain." );
+ }
+ continue;
+ }
+ else
+ pPage = nullptr;
+ }
+
+ if ( pPage )
+ {
+ SAL_INFO( "sw.pageframe", "RemoveSuperfluous - DestroyFrm p: " << pPage );
+ RemovePage( &pPage, SwRemoveResult::Prev );
+ nDocPos = pPage ? pPage->getFrameArea().Top() : 0;
+ }
+ } while ( pPage );
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( nDocPos != LONG_MAX &&
+ (!pSh || !pSh->Imp()->IsUpdateExpFields()) )
+ {
+ SwDocPosUpdate aMsgHint( nDocPos );
+ GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+}
+
+/// Ensures that enough pages exist, so that all page bound frames and draw objects can be placed
+void SwRootFrame::AssertFlyPages()
+{
+ if ( !IsAssertFlyPages() )
+ return;
+ mbAssertFlyPages = false;
+
+ SwDoc *pDoc = GetFormat()->GetDoc();
+ const SwFrameFormats *pTable = pDoc->GetSpzFrameFormats();
+
+ // what page targets the "last" Fly?
+ // note the needed pages in a set
+ sal_uInt16 nMaxPg(0);
+ o3tl::sorted_vector< sal_uInt16 > neededPages;
+ neededPages.reserve(pTable->size());
+
+ for ( size_t i = 0; i < pTable->size(); ++i )
+ {
+ const SwFormatAnchor &rAnch = (*pTable)[i]->GetAnchor();
+ if(!rAnch.GetContentAnchor())
+ {
+ const sal_uInt16 nPageNum(rAnch.GetPageNum());
+
+ // calc MaxPage (as before)
+ nMaxPg = std::max(nMaxPg, nPageNum);
+
+ // note as needed page
+ neededPages.insert(nPageNum);
+ }
+ }
+
+ // How many pages exist at the moment?
+ // And are there EmptyPages that are needed?
+ SwPageFrame* pPage(static_cast<SwPageFrame*>(Lower()));
+ SwPageFrame* pPrevPage(nullptr);
+ SwPageFrame* pFirstRevivedEmptyPage(nullptr);
+
+ while(pPage) // moved two while-conditions to break-statements (see below)
+ {
+ const sal_uInt16 nPageNum(pPage->GetPhyPageNum());
+
+ if(pPage->IsEmptyPage() &&
+ nullptr != pPrevPage &&
+ neededPages.find(nPageNum) != neededPages.end())
+ {
+ // This is an empty page, but it *is* needed since a SwFrame
+ // is anchored at it directly. Initially these SwFrames are
+ // not fully initialized. Need to change the format of this SwFrame
+ // and let the ::Notify mechanism newly evaluate
+ // m_bEmptyPage (see SwPageFrame::UpdateAttr_). Code is taken and
+ // adapted from ::InsertPage (used below), this needs previous page
+ bool bWishedRightPage(!pPrevPage->OnRightPage());
+ SwPageDesc* pDesc(pPrevPage->GetPageDesc()->GetFollow());
+ assert(pDesc && "Missing PageDesc");
+
+ if (!(bWishedRightPage ? pDesc->GetRightFormat() : pDesc->GetLeftFormat()))
+ {
+ bWishedRightPage = !bWishedRightPage;
+ }
+
+ bool const bWishedFirst(pDesc != pPrevPage->GetPageDesc());
+ SwFrameFormat* pFormat(bWishedRightPage ? pDesc->GetRightFormat(bWishedFirst) : pDesc->GetLeftFormat(bWishedFirst));
+
+ // set SwFrameFormat, this will trigger SwPageFrame::UpdateAttr_ and re-evaluate
+ // m_bEmptyPage, too
+ pPage->SetFrameFormat(pFormat);
+
+ if(nullptr == pFirstRevivedEmptyPage)
+ {
+ // remember first (lowest) SwPageFrame which needed correction
+ pFirstRevivedEmptyPage = pPage;
+ }
+ }
+
+ // original while-condition II
+ if(nullptr == pPage->GetNext())
+ {
+ break;
+ }
+
+ // original while-condition III
+ if(static_cast< SwPageFrame* >(pPage->GetNext())->IsFootnotePage())
+ {
+ break;
+ }
+
+ pPrevPage = pPage;
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ if ( nMaxPg > pPage->GetPhyPageNum() )
+ {
+ for ( sal_uInt16 i = pPage->GetPhyPageNum(); i < nMaxPg; ++i )
+ pPage = InsertPage( pPage, false );
+
+ // If the endnote pages are now corrupt, destroy them.
+ if ( !pDoc->GetFootnoteIdxs().empty() )
+ {
+ pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && !pPage->IsFootnotePage() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+
+ if ( pPage )
+ {
+ SwPageDesc *pTmpDesc = pPage->FindPageDesc();
+ bool isRightPage = pPage->OnRightPage();
+ if ( pPage->GetFormat() !=
+ (isRightPage ? pTmpDesc->GetRightFormat() : pTmpDesc->GetLeftFormat()) )
+ RemoveFootnotes( pPage, false, true );
+ }
+ }
+ }
+
+ // if we corrected SwFrameFormat and changed one (or more) m_bEmptyPage
+ // flags, we need to correct evtl. currently wrong positioned SwFrame(s)
+ // which did think until now that these Page(s) are empty.
+ // After trying to correct myself I found SwRootFrame::AssertPageFlys
+ // directly below that already does that, so use it.
+ if(nullptr != pFirstRevivedEmptyPage)
+ {
+ AssertPageFlys(pFirstRevivedEmptyPage);
+ }
+
+ //Remove masters that haven't been replaced yet from the list.
+ RemoveMasterObjs( mpDrawPage );
+
+#if OSL_DEBUG_LEVEL > 0
+ pPage = static_cast<SwPageFrame*>(Lower());
+ while ( pPage && pPage->GetNext() &&
+ !static_cast<SwPageFrame*>(pPage->GetNext())->IsFootnotePage() )
+ {
+ SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
+ << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
+ << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ SAL_INFO( "sw.pageframe", "AssertFlyPages p: " << pPage << " d: " << pPage->GetPageDesc()
+ << " f: " << pPage->GetFormat() << " virt: " << pPage->GetVirtPageNum()
+ << " phys: " << pPage->GetPhyPageNum() << " empty: " << pPage->IsEmptyPage() );
+#endif
+}
+
+/// Ensure that after the given page all page-bound objects are located on the correct page
+void SwRootFrame::AssertPageFlys( SwPageFrame *pPage )
+{
+ SAL_INFO( "sw.pageframe", "(AssertPageFlys in" );
+ while ( pPage )
+ {
+ if (pPage->GetSortedObjs())
+ {
+ size_t i = 0;
+ while ( pPage->GetSortedObjs() && i< pPage->GetSortedObjs()->size() )
+ {
+ // #i28701#
+ SwFrameFormat& rFormat = (*pPage->GetSortedObjs())[i]->GetFrameFormat();
+ const SwFormatAnchor &rAnch = rFormat.GetAnchor();
+ const sal_uInt16 nPg = rAnch.GetPageNum();
+ if ((rAnch.GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
+ nPg != pPage->GetPhyPageNum() )
+ {
+ SAL_INFO( "sw.pageframe", nPg << " " << pPage->GetPhyPageNum() );
+ // If on the wrong page, check if previous page is empty
+ if( nPg && !(pPage->GetPhyPageNum()-1 == nPg &&
+ static_cast<SwPageFrame*>(pPage->GetPrev())->IsEmptyPage()) )
+ {
+ // It can move by itself. Just send a modify to its anchor attribute.
+#if OSL_DEBUG_LEVEL > 1
+ const size_t nCnt = pPage->GetSortedObjs()->size();
+ rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
+ OSL_ENSURE( !pPage->GetSortedObjs() ||
+ nCnt != pPage->GetSortedObjs()->size(),
+ "Object couldn't be reattached!" );
+#else
+ rFormat.CallSwClientNotify(sw::LegacyModifyHint(nullptr, &rAnch));
+#endif
+ // Do not increment index, in this case
+ continue;
+ }
+ }
+ ++i;
+ }
+ }
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+ SAL_INFO( "sw.pageframe", "AssertPageFlys out)" );
+}
+
+Size SwRootFrame::ChgSize( const Size& aNewSize )
+{
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.SSize(aNewSize);
+ }
+
+ InvalidatePrt_();
+ mbFixSize = false;
+ return getFrameArea().SSize();
+}
+
+void SwRootFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/)
+{
+ if ( !isFrameAreaPositionValid() )
+ {
+ setFrameAreaPositionValid(true);
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().setX(DOCUMENTBORDER);
+ aFrm.Pos().setY(DOCUMENTBORDER);
+ }
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Pos().setX(0);
+ aPrt.Pos().setY(0);
+ aPrt.SSize( getFrameArea().SSize() );
+ }
+
+ if ( !isFrameAreaSizeValid() )
+ {
+ // SSize is set by the pages (Cut/Paste).
+ setFrameAreaSizeValid(true);
+ }
+}
+
+void SwRootFrame::ImplInvalidateBrowseWidth()
+{
+ mbBrowseWidthValid = false;
+ SwFrame *pPg = Lower();
+ while ( pPg )
+ {
+ pPg->InvalidateSize();
+ pPg = pPg->GetNext();
+ }
+}
+
+void SwRootFrame::ImplCalcBrowseWidth()
+{
+ OSL_ENSURE( GetCurrShell() && GetCurrShell()->GetViewOptions()->getBrowseMode(),
+ "CalcBrowseWidth and not in BrowseView" );
+
+ // The (minimal) with is determined from borders, tables and paint objects.
+ // It is calculated based on the attributes. Thus, it is not relevant how wide they are
+ // currently but only how wide they want to be.
+ // Frames and paint objects inside other objects (frames, tables) do not count.
+ // Borders and columns are not taken into account.
+
+ SwFrame *pFrame = ContainsContent();
+ while ( pFrame && !pFrame->IsInDocBody() )
+ pFrame = static_cast<SwContentFrame*>(pFrame)->GetNextContentFrame();
+ if ( !pFrame )
+ return;
+
+ mbBrowseWidthValid = true;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ mnBrowseWidth = (!comphelper::LibreOfficeKit::isActive() && pSh)? MINLAY + 2 * pSh->GetOut()-> PixelToLogic( pSh->GetBrowseBorder() ).Width(): MIN_BROWSE_WIDTH;
+
+ do
+ {
+ if ( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+
+ if ( pFrame->IsTabFrame() &&
+ !static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize().GetWidthPercent() )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ const SwFormatHoriOrient &rHori = rAttrs.GetAttrSet().GetHoriOrient();
+ tools::Long nWidth = rAttrs.GetSize().Width();
+ if ( nWidth < int(USHRT_MAX)-2000 && //-2k, because USHRT_MAX gets missing while trying to resize! (and cast to int to avoid -Wsign-compare due to broken USHRT_MAX on Android)
+ text::HoriOrientation::FULL != rHori.GetHoriOrient() )
+ {
+ const SwHTMLTableLayout *pLayoutInfo =
+ static_cast<const SwTabFrame *>(pFrame)->GetTable()
+ ->GetHTMLTableLayout();
+ if ( pLayoutInfo )
+ nWidth = std::min( nWidth, pLayoutInfo->GetBrowseWidthMin() );
+
+ switch ( rHori.GetHoriOrient() )
+ {
+ case text::HoriOrientation::NONE:
+ // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
+ nWidth += rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame );
+ break;
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ nWidth += rAttrs.CalcLeft( pFrame );
+ break;
+ default:
+ break;
+ }
+ mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
+ }
+ }
+ else if ( pFrame->GetDrawObjs() )
+ {
+ for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
+ {
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ const bool bFly = pAnchoredObj->DynCastFlyFrame() != nullptr;
+ if ((bFly && (FAR_AWAY == pAnchoredObj->GetObjRect().Width()))
+ || rFormat.GetFrameSize().GetWidthPercent())
+ {
+ continue;
+ }
+
+ tools::Long nWidth = 0;
+ switch ( rFormat.GetAnchor().GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ nWidth = bFly ? rFormat.GetFrameSize().GetWidth() :
+ pAnchoredObj->GetObjRect().Width();
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ {
+ // #i33170#
+ // Reactivated old code because
+ // nWidth = pAnchoredObj->GetObjRect().Right()
+ // gives wrong results for objects that are still
+ // at position FAR_AWAY.
+ if ( bFly )
+ {
+ nWidth = rFormat.GetFrameSize().GetWidth();
+ const SwFormatHoriOrient &rHori = rFormat.GetHoriOrient();
+ switch ( rHori.GetHoriOrient() )
+ {
+ case text::HoriOrientation::NONE:
+ nWidth += rHori.GetPos();
+ break;
+ case text::HoriOrientation::INSIDE:
+ case text::HoriOrientation::LEFT:
+ if ( text::RelOrientation::PRINT_AREA == rHori.GetRelationOrient() )
+ nWidth += pFrame->getFramePrintArea().Left();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ // Paint objects to not have attributes and
+ // are defined by their current size
+ nWidth = pAnchoredObj->GetObjRect().Right() -
+ pAnchoredObj->GetDrawObj()->GetAnchorPos().X();
+ }
+ break;
+ default: /* do nothing */;
+ }
+ mnBrowseWidth = std::max( mnBrowseWidth, nWidth );
+ }
+ }
+ pFrame = pFrame->FindNextCnt();
+ } while ( pFrame );
+}
+
+void SwRootFrame::StartAllAction()
+{
+ if ( GetCurrShell() )
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ pCursorShell->StartAction();
+ else
+ rSh.StartAction();
+ }
+}
+
+void SwRootFrame::EndAllAction( bool bVirDev )
+{
+ if ( !GetCurrShell() )
+ return;
+
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ const bool bOldEndActionByVirDev = rSh.IsEndActionByVirDev();
+ rSh.SetEndActionByVirDev( bVirDev );
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ {
+ pCursorShell->EndAction();
+ pCursorShell->CallChgLnk();
+ if ( auto pFEShell = dynamic_cast<SwFEShell*>( &rSh) )
+ pFEShell->SetChainMarker();
+ }
+ else
+ rSh.EndAction();
+ rSh.SetEndActionByVirDev( bOldEndActionByVirDev );
+ }
+}
+
+void SwRootFrame::UnoRemoveAllActions()
+{
+ if ( !GetCurrShell() )
+ return;
+
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ // #i84729#
+ // No end action, if <SwViewShell> instance is currently in its end action.
+ // Recursive calls to <::EndAction()> are not allowed.
+ if ( !rSh.IsInEndAction() )
+ {
+ OSL_ENSURE(!rSh.GetRestoreActions(), "Restore action count is already set!");
+ bool bCursor = dynamic_cast<const SwCursorShell*>( &rSh) != nullptr;
+ bool bFE = dynamic_cast<const SwFEShell*>( &rSh) != nullptr;
+ sal_uInt16 nRestore = 0;
+ while( rSh.ActionCount() )
+ {
+ if( bCursor )
+ {
+ static_cast<SwCursorShell*>(&rSh)->EndAction();
+ static_cast<SwCursorShell*>(&rSh)->CallChgLnk();
+ if ( bFE )
+ static_cast<SwFEShell*>(&rSh)->SetChainMarker();
+ }
+ else
+ rSh.EndAction();
+ nRestore++;
+ }
+ rSh.SetRestoreActions(nRestore);
+ }
+ rSh.LockView(true);
+ }
+}
+
+void SwRootFrame::UnoRestoreAllActions()
+{
+ if ( !GetCurrShell() )
+ return;
+
+ for(SwViewShell& rSh : GetCurrShell()->GetRingContainer())
+ {
+ sal_uInt16 nActions = rSh.GetRestoreActions();
+ while( nActions-- )
+ {
+ if ( auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh) )
+ pCursorShell->StartAction();
+ else
+ rSh.StartAction();
+ }
+ rSh.SetRestoreActions(0);
+ rSh.LockView(false);
+ }
+}
+
+// Helper functions for SwRootFrame::CheckViewLayout
+static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset );
+
+static void lcl_MoveAllLowerObjs( SwFrame* pFrame, const Point& rOffset )
+{
+ const bool bPage = pFrame->IsPageFrame();
+ const SwSortedObjs* pSortedObj = bPage
+ ? static_cast<SwPageFrame*>(pFrame)->GetSortedObjs()
+ : pFrame->GetDrawObjs();
+ if (pSortedObj == nullptr)
+ return;
+
+ // note: pSortedObj elements may be removed and inserted from
+ // MoveObjectIfActive(), invalidating iterators
+ // DO NOT CONVERT THIS TO A C++11 FOR LOOP, IT DID NOT WORK THE LAST 2 TIMES
+ for (size_t i = 0; i < pSortedObj->size(); ++i)
+ {
+ SwAnchoredObject *const pAnchoredObj = (*pSortedObj)[i];
+ const SwFrameFormat& rObjFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor& rAnchor = rObjFormat.GetAnchor();
+
+ // all except from the as character anchored objects are moved
+ // when processing the page frame:
+ if ( !bPage && (rAnchor.GetAnchorId() != RndStdIds::FLY_AS_CHAR) )
+ continue;
+
+ SwObjPositioningInProgress aPosInProgress( *pAnchoredObj );
+
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ lcl_MoveAllLowers( pFlyFrame, rOffset );
+ // tdf#138785 update position specific to as-char flys
+ if (pFlyFrame->IsFlyInContentFrame())
+ {
+ static_cast<SwFlyInContentFrame*>(pFlyFrame)->AddRefOfst(rOffset);
+ }
+ pFlyFrame->NotifyDrawObj();
+ // --> let the active embedded object be moved
+ SwFrame* pLower = pFlyFrame->Lower();
+ if ( pLower && pLower->IsNoTextFrame() )
+ {
+ SwRootFrame* pRoot = pLower->getRootFrame();
+ SwViewShell *pSh = pRoot ? pRoot->GetCurrShell() : nullptr;
+ if ( pSh )
+ {
+ SwNoTextFrame *const pContentFrame = static_cast<SwNoTextFrame*>(pLower);
+ SwOLENode* pNode = pContentFrame->GetNode()->GetOLENode();
+ if ( pNode )
+ {
+ svt::EmbeddedObjectRef& xObj = pNode->GetOLEObj().GetObject();
+ if ( xObj.is() )
+ {
+ for(SwViewShell& rSh : pSh->GetRingContainer())
+ {
+ SwFEShell* pFEShell = dynamic_cast< SwFEShell* >( &rSh );
+ if ( pFEShell )
+ pFEShell->MoveObjectIfActive( xObj, rOffset );
+ }
+ }
+ }
+ }
+ }
+ }
+ else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchoredObj ) )
+ {
+ // don't touch objects that are not yet positioned:
+ if ( pAnchoredDrawObj->NotYetPositioned() )
+ continue;
+
+ const Point& aCurrAnchorPos = pAnchoredDrawObj->GetDrawObj()->GetAnchorPos();
+ const Point aNewAnchorPos( aCurrAnchorPos + rOffset );
+ pAnchoredDrawObj->DrawObj()->SetAnchorPos( aNewAnchorPos );
+ pAnchoredDrawObj->SetLastObjRect( pAnchoredDrawObj->GetObjRect().SVRect() );
+
+ // clear contour cache
+ if ( pAnchoredDrawObj->GetFrameFormat().GetSurround().IsContour() )
+ ClrContourCache( pAnchoredDrawObj->GetDrawObj() );
+ }
+ // #i92511#
+ // cache for object rectangle inclusive spaces has to be invalidated.
+ pAnchoredObj->InvalidateObjRectWithSpaces();
+ }
+}
+
+static void lcl_MoveAllLowers( SwFrame* pFrame, const Point& rOffset )
+{
+ // first move the current frame
+ // RotateFlyFrame3: moved to transform_translate instead of
+ // direct modification to allow the SwFrame evtl. needed own reactions
+ pFrame->transform_translate(rOffset);
+
+ // Don't forget accessibility:
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( pFrame->IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = pFrame->getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ const SwRect aFrame( pFrame->getFrameArea() );
+
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pFrame, aFrame );
+ }
+ }
+#endif
+
+ // the move any objects
+ lcl_MoveAllLowerObjs( pFrame, rOffset );
+
+ // finally, for layout frames we have to call this function recursively:
+ if (pFrame->IsLayoutFrame())
+ {
+ SwFrame* pLowerFrame = pFrame->GetLower();
+ while ( pLowerFrame )
+ {
+ lcl_MoveAllLowers( pLowerFrame, rOffset );
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ }
+}
+
+// Calculate how the pages have to be positioned
+void SwRootFrame::CheckViewLayout( const SwViewOption* pViewOpt, const SwRect* pVisArea )
+{
+ SwViewShell* pSh = GetCurrShell();
+ vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
+ // #i91432#
+ // No calculation of page positions, if only an empty page is present.
+ // This situation occurs when <SwRootFrame> instance is in construction
+ // and the document contains only left pages.
+ if ( Lower()->GetNext() == nullptr &&
+ static_cast<SwPageFrame*>(Lower())->IsEmptyPage() )
+ {
+ return;
+ }
+
+ if ( !pVisArea )
+ {
+ // no early return for bNewPage
+ if ( mnViewWidth < 0 )
+ mnViewWidth = 0;
+ }
+ else
+ {
+ assert(pViewOpt && "CheckViewLayout required ViewOptions");
+
+ const sal_uInt16 nColumns = pViewOpt->GetViewLayoutColumns();
+ const bool bBookMode = pViewOpt->IsViewLayoutBookMode();
+
+ if ( nColumns == mnColumns && bBookMode == mbBookMode && pVisArea->Width() == mnViewWidth && !mbSidebarChanged )
+ return;
+
+ mnColumns = nColumns;
+ mbBookMode = bBookMode;
+ mnViewWidth = pVisArea->Width();
+ mbSidebarChanged = false;
+ }
+
+ if( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE ) )
+ {
+ mnColumns = 1;
+ mbBookMode = false;
+ }
+
+ Calc(pRenderContext);
+
+ const bool bOldCallbackActionEnabled = IsCallbackActionEnabled();
+ SetCallbackActionEnabled( false );
+
+ maPageRects.clear();
+
+ const tools::Long nBorder = getFrameArea().Pos().getX();
+ const tools::Long nVisWidth = mnViewWidth - 2 * nBorder;
+ const tools::Long nGapBetweenPages = pViewOpt ? pViewOpt->GetGapBetweenPages()
+ : (pSh ? pSh->GetViewOptions()->GetGapBetweenPages()
+ : SwViewOption::defGapBetweenPages);
+
+ // check how many pages fit into the first page layout row:
+ SwPageFrame* pPageFrame = static_cast<SwPageFrame*>(Lower());
+
+ // will contain the number of pages per row. 0 means that
+ // the page does not fit.
+ tools::Long nWidthRemain = nVisWidth;
+
+ // after one row has been processed, these variables contain
+ // the width of the row and the maximum of the page heights
+ tools::Long nCurrentRowHeight = 0;
+ tools::Long nCurrentRowWidth = 0;
+
+ // these variables are used to finally set the size of the
+ // root frame
+ tools::Long nSumRowHeight = 0;
+ SwTwips nMinPageLeft = TWIPS_MAX;
+ SwTwips nMaxPageRight = 0;
+ SwPageFrame* pStartOfRow = pPageFrame;
+ sal_uInt16 nNumberOfPagesInRow = mbBookMode ? 1 : 0; // in book view, start with right page
+ bool bFirstRow = true;
+
+ bool bPageChanged = false;
+ const bool bRTL = !IsLeftToRightViewLayout();
+ const SwTwips nSidebarWidth = SwPageFrame::GetSidebarBorderWidth( pSh );
+
+ while ( pPageFrame )
+ {
+ // we consider the current page to be "start of row" if
+ // 1. it is the first page in the current row or
+ // 2. it is the second page in the row and the first page is an empty page in non-book view:
+ const bool bStartOfRow = pPageFrame == pStartOfRow ||
+ ( pStartOfRow->IsEmptyPage() && pPageFrame == pStartOfRow->GetNext() && !mbBookMode );
+
+ const bool bEmptyPage = pPageFrame->IsEmptyPage() && !mbBookMode;
+
+ // no half doc border space for first page in each row and
+ tools::Long nPageWidth = 0;
+ tools::Long nPageHeight = 0;
+
+ if ( mbBookMode )
+ {
+ const SwFrame& rFormatPage = pPageFrame->GetFormatPage();
+
+ nPageWidth = rFormatPage.getFrameArea().Width() + nSidebarWidth + ((bStartOfRow || 1 == (pPageFrame->GetPhyPageNum()%2)) ? 0 : nGapBetweenPages);
+ nPageHeight = rFormatPage.getFrameArea().Height() + nGapBetweenPages;
+ }
+ else
+ {
+ if ( !pPageFrame->IsEmptyPage() )
+ {
+ nPageWidth = pPageFrame->getFrameArea().Width() + nSidebarWidth + (bStartOfRow ? 0 : nGapBetweenPages);
+ nPageHeight = pPageFrame->getFrameArea().Height() + nGapBetweenPages;
+ }
+ }
+
+ if ( !bEmptyPage )
+ ++nNumberOfPagesInRow;
+
+ // finish current row if
+ // 1. in dynamic mode the current page does not fit anymore or
+ // 2. the current page exceeds the maximum number of columns
+ bool bRowFinished = (0 == mnColumns && nWidthRemain < nPageWidth ) ||
+ (0 != mnColumns && mnColumns < nNumberOfPagesInRow);
+
+ // make sure that at least one page goes to the current row:
+ if ( !bRowFinished || bStartOfRow )
+ {
+ // current page is allowed to be in current row
+ nWidthRemain = nWidthRemain - nPageWidth;
+
+ nCurrentRowWidth = nCurrentRowWidth + nPageWidth;
+ nCurrentRowHeight = std::max( nCurrentRowHeight, nPageHeight );
+
+ pPageFrame = static_cast<SwPageFrame*>(pPageFrame->GetNext());
+
+ if ( !pPageFrame )
+ bRowFinished = true;
+ }
+
+ if ( bRowFinished )
+ {
+ // pPageFrame now points to the first page in the new row or null
+ // pStartOfRow points to the first page in the current row
+
+ // special centering for last row. pretend to fill the last row with virtual copies of the last page before centering:
+ if ( !pPageFrame && nWidthRemain > 0 )
+ {
+ // find last page in current row:
+ const SwPageFrame* pLastPageInCurrentRow = pStartOfRow;
+ while( pLastPageInCurrentRow->GetNext() )
+ pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetNext());
+
+ if ( pLastPageInCurrentRow->IsEmptyPage() )
+ pLastPageInCurrentRow = static_cast<const SwPageFrame*>(pLastPageInCurrentRow->GetPrev());
+
+ // check how many times the last page would still fit into the remaining space:
+ sal_uInt16 nNumberOfVirtualPages = 0;
+ const sal_uInt16 nMaxNumberOfVirtualPages = mnColumns > 0 ? mnColumns - nNumberOfPagesInRow : USHRT_MAX;
+ SwTwips nRemain = nWidthRemain;
+ SwTwips nVirtualPagesWidth = 0;
+ SwTwips nLastPageWidth = pLastPageInCurrentRow->getFrameArea().Width() + nSidebarWidth;
+
+ while ( ( mnColumns > 0 || nRemain > 0 ) && nNumberOfVirtualPages < nMaxNumberOfVirtualPages )
+ {
+ SwTwips nLastPageWidthWithGap = nLastPageWidth;
+ if ( !mbBookMode || ( 0 == (nNumberOfVirtualPages + nNumberOfPagesInRow) %2) )
+ nLastPageWidthWithGap += nGapBetweenPages;
+
+ if ( mnColumns > 0 || nLastPageWidthWithGap < nRemain )
+ {
+ ++nNumberOfVirtualPages;
+ nVirtualPagesWidth += nLastPageWidthWithGap;
+ }
+ nRemain = nRemain - nLastPageWidthWithGap;
+ }
+
+ nCurrentRowWidth = nCurrentRowWidth + nVirtualPagesWidth;
+ }
+
+ // first page in book mode is always special:
+ if ( bFirstRow && mbBookMode )
+ {
+ // #i88036#
+ nCurrentRowWidth +=
+ pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
+ }
+
+ // center page if possible
+ tools::Long nSizeDiff = 0;
+ if (nVisWidth > nCurrentRowWidth && !comphelper::LibreOfficeKit::isActive())
+ nSizeDiff = ( nVisWidth - nCurrentRowWidth ) / 2;
+
+ // adjust positions of pages in current row
+ tools::Long nX = nSizeDiff;
+
+ const tools::Long nRowStart = nBorder + nSizeDiff;
+ const tools::Long nRowEnd = nRowStart + nCurrentRowWidth;
+
+ if ( bFirstRow && mbBookMode )
+ {
+ // #i88036#
+ nX += pStartOfRow->GetFormatPage().getFrameArea().Width() + nSidebarWidth;
+ }
+
+ SwPageFrame* pEndOfRow = pPageFrame;
+ SwPageFrame* pPageToAdjust = pStartOfRow;
+
+ do
+ {
+ const SwPageFrame* pFormatPage = pPageToAdjust;
+ if ( mbBookMode )
+ pFormatPage = &pPageToAdjust->GetFormatPage();
+
+ const SwTwips nCurrentPageWidth = pFormatPage->getFrameArea().Width() + (pFormatPage->IsEmptyPage() ? 0 : nSidebarWidth);
+ const Point aOldPagePos = pPageToAdjust->getFrameArea().Pos();
+ const bool bLeftSidebar = pPageToAdjust->SidebarPosition() == sw::sidebarwindows::SidebarPosition::LEFT;
+ const SwTwips nLeftPageAddOffset = bLeftSidebar ?
+ nSidebarWidth :
+ 0;
+
+ Point aNewPagePos( nBorder + nX, nBorder + nSumRowHeight );
+ Point aNewPagePosWithLeftOffset( nBorder + nX + nLeftPageAddOffset, nBorder + nSumRowHeight );
+
+ // RTL view layout: Calculate mirrored page position
+ if ( bRTL )
+ {
+ const tools::Long nXOffsetInRow = aNewPagePos.getX() - nRowStart;
+ aNewPagePos.setX(nRowEnd - nXOffsetInRow - nCurrentPageWidth);
+ aNewPagePosWithLeftOffset = aNewPagePos;
+ aNewPagePosWithLeftOffset.setX(aNewPagePosWithLeftOffset.getX() + nLeftPageAddOffset);
+ }
+
+ if ( aNewPagePosWithLeftOffset != aOldPagePos )
+ {
+ lcl_MoveAllLowers( pPageToAdjust, aNewPagePosWithLeftOffset - aOldPagePos );
+ pPageToAdjust->SetCompletePaint();
+ bPageChanged = true;
+ }
+
+ // calculate area covered by the current page and store to
+ // maPageRects. This is used e.g., for cursor setting
+ const bool bFirstColumn = pPageToAdjust == pStartOfRow;
+ const bool bLastColumn = pPageToAdjust->GetNext() == pEndOfRow;
+ const bool bLastRow = !pEndOfRow;
+
+ nMinPageLeft = std::min( nMinPageLeft, SwTwips(aNewPagePos.getX()) );
+ nMaxPageRight = std::max( nMaxPageRight, SwTwips(aNewPagePos.getX() + nCurrentPageWidth));
+
+ // border of nGapBetweenPages around the current page:
+ SwRect aPageRectWithBorders( aNewPagePos.getX() - nGapBetweenPages,
+ aNewPagePos.getY(),
+ pPageToAdjust->getFrameArea().SSize().Width() + nGapBetweenPages + nSidebarWidth,
+ nCurrentRowHeight );
+
+ static const tools::Long nOuterClickDiff = 1000000;
+
+ // adjust borders for these special cases:
+ if ( (bFirstColumn && !bRTL) || (bLastColumn && bRTL) )
+ aPageRectWithBorders.SubLeft( nOuterClickDiff );
+ if ( (bLastColumn && !bRTL) || (bFirstColumn && bRTL) )
+ aPageRectWithBorders.AddRight( nOuterClickDiff );
+ if ( bFirstRow )
+ aPageRectWithBorders.SubTop( nOuterClickDiff );
+ if ( bLastRow )
+ aPageRectWithBorders.AddBottom( nOuterClickDiff );
+
+ maPageRects.push_back( aPageRectWithBorders );
+
+ nX = nX + nCurrentPageWidth;
+ pPageToAdjust = static_cast<SwPageFrame*>(pPageToAdjust->GetNext());
+
+ // distance to next page
+ if ( pPageToAdjust && pPageToAdjust != pEndOfRow )
+ {
+ // in book view, we add the x gap before left (even) pages:
+ if ( mbBookMode )
+ {
+ if ( 0 == (pPageToAdjust->GetPhyPageNum()%2) )
+ nX = nX + nGapBetweenPages;
+ }
+ else
+ {
+ // in non-book view, don't add x gap before
+ // 1. the last empty page in a row
+ // 2. after an empty page
+ const bool bDontAddGap = ( pPageToAdjust->IsEmptyPage() && pPageToAdjust->GetNext() == pEndOfRow ) ||
+ ( static_cast<SwPageFrame*>(pPageToAdjust->GetPrev())->IsEmptyPage() );
+
+ if ( !bDontAddGap )
+ nX = nX + nGapBetweenPages;
+ }
+ }
+ }
+ while (pPageToAdjust && pPageToAdjust != pEndOfRow);
+
+ // adjust values for root frame size
+ nSumRowHeight = nSumRowHeight + nCurrentRowHeight;
+
+ // start new row:
+ nCurrentRowHeight = 0;
+ nCurrentRowWidth = 0;
+ pStartOfRow = pEndOfRow;
+ nWidthRemain = nVisWidth;
+ nNumberOfPagesInRow = 0;
+ bFirstRow = false;
+ } // end row finished
+ } // end while
+
+ // set size of root frame:
+ const Size aOldSize( getFrameArea().SSize() );
+ const Size aNewSize( nMaxPageRight - nBorder, nSumRowHeight - nGapBetweenPages );
+
+ if ( bPageChanged || aNewSize != aOldSize )
+ {
+ ChgSize( aNewSize );
+ ::AdjustSizeChgNotify( this );
+ Calc(pRenderContext);
+
+ if ( pSh && pSh->GetDoc()->GetDocShell() )
+ {
+ pSh->SetFirstVisPageInvalid();
+ if (bOldCallbackActionEnabled)
+ {
+ pSh->InvalidateWindows( SwRect( 0, 0, SAL_MAX_INT32, SAL_MAX_INT32 ) );
+ pSh->GetDoc()->GetDocShell()->Broadcast(SfxHint(SfxHintId::DocChanged));
+ }
+ }
+ }
+
+ maPagesArea.Pos( getFrameArea().Pos() );
+ maPagesArea.SSize( aNewSize );
+ if ( TWIPS_MAX != nMinPageLeft )
+ maPagesArea.Left_( nMinPageLeft );
+
+ SetCallbackActionEnabled( bOldCallbackActionEnabled );
+}
+
+bool SwRootFrame::IsLeftToRightViewLayout() const
+{
+ // Layout direction determined by layout direction of the first page.
+ // #i88036#
+ // Only ask a non-empty page frame for its layout direction
+ assert(dynamic_cast<const SwPageFrame *>(Lower()) != nullptr);
+ const SwPageFrame& rPage = static_cast<const SwPageFrame&>(*Lower()).GetFormatPage();
+ return !rPage.IsRightToLeft() && !rPage.IsVertical();
+}
+
+const SwPageFrame& SwPageFrame::GetFormatPage() const
+{
+ const SwPageFrame* pRet = this;
+ if ( IsEmptyPage() )
+ {
+ pRet = static_cast<const SwPageFrame*>( OnRightPage() ? GetNext() : GetPrev() );
+ // #i88035#
+ // Typically a right empty page frame has a next non-empty page frame and
+ // a left empty page frame has a previous non-empty page frame.
+ // But under certain circumstances this assumption is not true -
+ // e.g. during insertion of a left page at the end of the document right
+ // after a left page in an intermediate state a right empty page does not
+ // have a next page frame.
+ if ( pRet == nullptr )
+ {
+ if ( OnRightPage() )
+ {
+ pRet = static_cast<const SwPageFrame*>( GetPrev() );
+ }
+ else
+ {
+ pRet = static_cast<const SwPageFrame*>( GetNext() );
+ }
+ }
+ assert(pRet &&
+ "<SwPageFrame::GetFormatPage()> - inconsistent layout: empty page without previous and next page frame --> crash.");
+ }
+ return *pRet;
+}
+
+bool SwPageFrame::IsOverHeaderFooterArea( const Point& rPt, FrameControlType &rControl ) const
+{
+ tools::Long nUpperLimit = 0;
+ tools::Long nLowerLimit = 0;
+ const SwFrame* pFrame = Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsBodyFrame() )
+ {
+ nUpperLimit = pFrame->getFrameArea().Top();
+ nLowerLimit = pFrame->getFrameArea().Bottom();
+ }
+ else if ( pFrame->IsFootnoteContFrame() )
+ nLowerLimit = pFrame->getFrameArea().Bottom();
+
+ pFrame = pFrame->GetNext();
+ }
+
+ SwRect aHeaderArea( getFrameArea().TopLeft(),
+ Size( getFrameArea().Width(), nUpperLimit - getFrameArea().Top() ) );
+
+ SwViewShell* pViewShell = getRootFrame()->GetCurrShell();
+ const bool bHideWhitespaceMode = pViewShell->GetViewOptions()->IsHideWhitespaceMode();
+ if ( aHeaderArea.Contains( rPt ) )
+ {
+ if (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetHeader().IsActive())
+ {
+ rControl = FrameControlType::Header;
+ return true;
+ }
+ }
+ else
+ {
+ SwRect aFooterArea( Point( getFrameArea().Left(), nLowerLimit ),
+ Size( getFrameArea().Width(), getFrameArea().Bottom() - nLowerLimit ) );
+
+ if ( aFooterArea.Contains( rPt ) &&
+ (!bHideWhitespaceMode || static_cast<const SwFrameFormat*>(GetDep())->GetFooter().IsActive()) )
+ {
+ rControl = FrameControlType::Footer;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SwPageFrame::CheckPageHeightValidForHideWhitespace(SwTwips nDiff)
+{
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+ if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
+ {
+ // When whitespace is hidden, the page frame has two heights: the
+ // nominal (defined by the frame format), and the actual (which is
+ // at most the nominal height, but can be smaller in case there is
+ // no content for the whole page).
+ // The layout size is the actual one, but we want to move the
+ // content frame to a new page only in case it doesn't fit the
+ // nominal size.
+ if (nDiff < 0)
+ {
+ // Content frame doesn't fit the actual size, check if it fits the nominal one.
+ const SwFrameFormat* pPageFormat = static_cast<const SwFrameFormat*>(GetDep());
+ const Size& rPageSize = pPageFormat->GetFrameSize().GetSize();
+ tools::Long nWhitespace = rPageSize.getHeight() - getFrameArea().Height();
+ if (nWhitespace > -nDiff)
+ {
+ // It does: don't move it and invalidate our page frame so
+ // that it gets a larger height.
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+const SwHeaderFrame* SwPageFrame::GetHeaderFrame() const
+{
+ const SwFrame* pLowerFrame = Lower();
+ while (pLowerFrame)
+ {
+ if (pLowerFrame->IsHeaderFrame())
+ return dynamic_cast<const SwHeaderFrame*>(pLowerFrame);
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ return nullptr;
+}
+
+const SwFooterFrame* SwPageFrame::GetFooterFrame() const
+{
+ const SwFrame* pLowerFrame = Lower();
+ while (pLowerFrame)
+ {
+ if (pLowerFrame->IsFooterFrame())
+ return dynamic_cast<const SwFooterFrame*>(pLowerFrame);
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ return nullptr;
+}
+
+SwTextGridItem const* GetGridItem(SwPageFrame const*const pPage)
+{
+ if (pPage && pPage->HasGrid())
+ {
+ SwTextGridItem const& rGridItem(
+ pPage->GetPageDesc()->GetMaster().GetTextGrid());
+ if (GRID_NONE != rGridItem.GetGridType())
+ {
+ return &rGridItem;
+ }
+ }
+ return nullptr;
+}
+
+sal_uInt16 GetGridWidth(SwTextGridItem const& rG, SwDoc const& rDoc)
+{
+ return (rDoc.IsSquaredPageMode()) ? rG.GetBaseHeight() : rG.GetBaseWidth();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/pagedesc.cxx b/sw/source/core/layout/pagedesc.cxx
new file mode 100644
index 000000000..d93b47517
--- /dev/null
+++ b/sw/source/core/layout/pagedesc.cxx
@@ -0,0 +1,803 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <editeng/pbinitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <fmtclds.hxx>
+#include <fmtfsize.hxx>
+#include <pagefrm.hxx>
+#include <pagedesc.hxx>
+#include <swtable.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <doc.hxx>
+#include <node.hxx>
+#include <strings.hrc>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
+#include <calbck.hxx>
+
+SwPageDesc::SwPageDesc(const OUString& rName, SwFrameFormat *pFormat, SwDoc *const pDoc)
+ : sw::BroadcastingModify()
+ , m_StyleName( rName )
+ , m_Master( pDoc->GetAttrPool(), rName, pFormat )
+ , m_Left( pDoc->GetAttrPool(), rName, pFormat )
+ , m_FirstMaster( pDoc->GetAttrPool(), rName, pFormat )
+ , m_FirstLeft( pDoc->GetAttrPool(), rName, pFormat )
+ , m_aStashedHeader()
+ , m_aStashedFooter()
+ , m_aDepends(*this)
+ , m_pTextFormatColl(nullptr)
+ , m_pFollow( this )
+ , m_nRegHeight( 0 )
+ , m_nRegAscent( 0 )
+ , m_nVerticalAdjustment( drawing::TextVerticalAdjust_TOP )
+ , m_eUse( UseOnPage::All | UseOnPage::HeaderShare | UseOnPage::FooterShare | UseOnPage::FirstShare )
+ , m_IsLandscape( false )
+ , m_IsHidden( false )
+ , m_pdList( nullptr )
+{
+}
+
+SwPageDesc::SwPageDesc( const SwPageDesc &rCpy )
+ : sw::BroadcastingModify()
+ , m_StyleName( rCpy.GetName() )
+ , m_NumType( rCpy.GetNumType() )
+ , m_Master( rCpy.GetMaster() )
+ , m_Left( rCpy.GetLeft() )
+ , m_FirstMaster( rCpy.GetFirstMaster() )
+ , m_FirstLeft( rCpy.GetFirstLeft() )
+ , m_aDepends(*this)
+ , m_pTextFormatColl(nullptr)
+ , m_pFollow( rCpy.m_pFollow )
+ , m_nRegHeight( rCpy.GetRegHeight() )
+ , m_nRegAscent( rCpy.GetRegAscent() )
+ , m_nVerticalAdjustment( rCpy.GetVerticalAdjustment() )
+ , m_eUse( rCpy.ReadUseOn() )
+ , m_IsLandscape( rCpy.GetLandscape() )
+ , m_IsHidden( rCpy.IsHidden() )
+ , m_FootnoteInfo( rCpy.GetFootnoteInfo() )
+ , m_pdList( nullptr )
+{
+ m_aStashedHeader.m_pStashedFirst = rCpy.m_aStashedHeader.m_pStashedFirst;
+ m_aStashedHeader.m_pStashedLeft = rCpy.m_aStashedHeader.m_pStashedLeft;
+ m_aStashedHeader.m_pStashedFirstLeft = rCpy.m_aStashedHeader.m_pStashedFirstLeft;
+
+ m_aStashedFooter.m_pStashedFirst = rCpy.m_aStashedFooter.m_pStashedFirst;
+ m_aStashedFooter.m_pStashedLeft = rCpy.m_aStashedFooter.m_pStashedLeft;
+ m_aStashedFooter.m_pStashedFirstLeft = rCpy.m_aStashedFooter.m_pStashedFirstLeft;
+
+ if (rCpy.m_pTextFormatColl && rCpy.m_aDepends.IsListeningTo(rCpy.m_pTextFormatColl))
+ {
+ m_pTextFormatColl = rCpy.m_pTextFormatColl;
+ m_aDepends.StartListening(const_cast<SwTextFormatColl*>(m_pTextFormatColl));
+ }
+}
+
+SwPageDesc & SwPageDesc::operator = (const SwPageDesc & rSrc)
+{
+ if(this == &rSrc)
+ return *this;
+
+ m_StyleName = rSrc.m_StyleName;
+ m_NumType = rSrc.m_NumType;
+ m_Master = rSrc.m_Master;
+ m_Left = rSrc.m_Left;
+ m_FirstMaster = rSrc.m_FirstMaster;
+ m_FirstLeft = rSrc.m_FirstLeft;
+
+ m_aStashedHeader.m_pStashedFirst = rSrc.m_aStashedHeader.m_pStashedFirst;
+ m_aStashedHeader.m_pStashedLeft = rSrc.m_aStashedHeader.m_pStashedLeft;
+ m_aStashedHeader.m_pStashedFirstLeft = rSrc.m_aStashedHeader.m_pStashedFirstLeft;
+
+ m_aStashedFooter.m_pStashedFirst = rSrc.m_aStashedFooter.m_pStashedFirst;
+ m_aStashedFooter.m_pStashedLeft = rSrc.m_aStashedFooter.m_pStashedLeft;
+ m_aStashedFooter.m_pStashedFirstLeft = rSrc.m_aStashedFooter.m_pStashedFirstLeft;
+
+ m_aDepends.EndListeningAll();
+ if (rSrc.m_pTextFormatColl && rSrc.m_aDepends.IsListeningTo(rSrc.m_pTextFormatColl))
+ {
+ m_pTextFormatColl = rSrc.m_pTextFormatColl;
+ m_aDepends.StartListening(const_cast<SwTextFormatColl*>(m_pTextFormatColl));
+ }
+ else
+ m_pTextFormatColl = nullptr;
+
+ if (rSrc.m_pFollow == &rSrc)
+ m_pFollow = this;
+ else
+ m_pFollow = rSrc.m_pFollow;
+
+ m_nRegHeight = rSrc.m_nRegHeight;
+ m_nRegAscent = rSrc.m_nRegAscent;
+ m_nVerticalAdjustment = rSrc.m_nVerticalAdjustment;
+ m_eUse = rSrc.m_eUse;
+ m_IsLandscape = rSrc.m_IsLandscape;
+ return *this;
+}
+
+SwPageDesc::~SwPageDesc()
+{
+}
+
+bool SwPageDesc::SetName( const OUString& rNewName )
+{
+ bool renamed = true;
+ if (m_pdList) {
+ SwPageDescs::iterator it = m_pdList->find_( m_StyleName );
+ if( m_pdList->end() == it ) {
+ SAL_WARN( "sw", "SwPageDesc not found in expected m_pdList" );
+ return false;
+ }
+ renamed = m_pdList->m_PosIndex.modify( it,
+ change_name( rNewName ), change_name( m_StyleName ) );
+ }
+ else
+ m_StyleName = rNewName;
+ return renamed;
+}
+
+/// Only the margin is mirrored.
+/// Attributes like borders and so on are copied 1:1.
+void SwPageDesc::Mirror()
+{
+ //Only the margins are mirrored, all other values are just copied.
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ const SvxLRSpaceItem &rLR = m_Master.GetLRSpace();
+ aLR.SetLeft( rLR.GetRight() );
+ aLR.SetRight( rLR.GetLeft() );
+ aLR.SetRightGutterMargin(rLR.GetGutterMargin());
+
+ SfxItemSet aSet( *m_Master.GetAttrSet().GetPool(),
+ m_Master.GetAttrSet().GetRanges() );
+ aSet.Put( aLR );
+ aSet.Put( m_Master.GetFrameSize() );
+ aSet.Put( m_Master.GetPaperBin() );
+ aSet.Put( m_Master.GetULSpace() );
+ aSet.Put( m_Master.GetBox() );
+ aSet.Put( m_Master.makeBackgroundBrushItem() );
+ aSet.Put( m_Master.GetShadow() );
+ aSet.Put( m_Master.GetCol() );
+ aSet.Put( m_Master.GetFrameDir() );
+ m_Left.SetFormatAttr( aSet );
+}
+
+void SwPageDesc::ResetAllAttr()
+{
+ SwFrameFormat& rFormat = GetMaster();
+
+ // #i73790# - method renamed
+ rFormat.ResetAllFormatAttr();
+ rFormat.SetFormatAttr( SvxFrameDirectionItem(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR) );
+}
+
+// gets information from Modify
+bool SwPageDesc::GetInfo( SfxPoolItem & rInfo ) const
+{
+ if (!m_Master.GetInfo(rInfo))
+ return false; // found
+ if (!m_Left.GetInfo(rInfo))
+ return false ;
+ if ( !m_FirstMaster.GetInfo( rInfo ) )
+ return false;
+ return m_FirstLeft.GetInfo( rInfo );
+}
+
+/// set the style for the grid alignment
+void SwPageDesc::SetRegisterFormatColl(const SwTextFormatColl* pFormat)
+{
+ if(pFormat != m_pTextFormatColl)
+ {
+ m_aDepends.EndListeningAll();
+ m_pTextFormatColl = pFormat;
+ m_aDepends.StartListening(const_cast<SwTextFormatColl*>(m_pTextFormatColl));
+ RegisterChange();
+ }
+}
+
+/// retrieve the style for the grid alignment
+const SwTextFormatColl* SwPageDesc::GetRegisterFormatColl() const
+{
+ if (!m_aDepends.IsListeningTo(m_pTextFormatColl))
+ m_pTextFormatColl = nullptr;
+ return m_pTextFormatColl;
+}
+
+/// notify all affected page frames
+void SwPageDesc::RegisterChange()
+{
+ // #117072# - During destruction of the document <SwDoc>
+ // the page description is modified. Thus, do nothing, if the document
+ // is in destruction respectively if no viewshell exists.
+ SwDoc* pDoc = GetMaster().GetDoc();
+ if ( !pDoc || pDoc->IsInDtor() )
+ {
+ return;
+ }
+ SwViewShell* pSh = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if ( !pSh )
+ {
+ return;
+ }
+
+ m_nRegHeight = 0;
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetMaster() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetLeft() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetFirstMaster() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+ {
+ SwIterator<SwFrame,SwFormat> aIter( GetFirstLeft() );
+ for( SwFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->IsPageFrame() )
+ static_cast<SwPageFrame*>(pLast)->PrepareRegisterChg();
+ }
+ }
+}
+
+/// special handling if the style of the grid alignment changes
+void SwPageDesc::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const sal_uInt16 nWhich = pLegacyHint->m_pOld
+ ? pLegacyHint->m_pOld->Which()
+ : pLegacyHint->m_pNew
+ ? pLegacyHint->m_pNew->Which()
+ : 0;
+ CallSwClientNotify(rHint);
+ if((RES_ATTRSET_CHG == nWhich)
+ || (RES_FMT_CHG == nWhich)
+ || isCHRATR(nWhich)
+ || (RES_PARATR_LINESPACING == nWhich))
+ RegisterChange();
+ }
+ else if (auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ if(m_pTextFormatColl == &rModify)
+ m_pTextFormatColl = static_cast<const SwTextFormatColl*>(pModifyChangedHint->m_pNew);
+ else
+ assert(false);
+ }
+}
+
+static const SwFrame* lcl_GetFrameOfNode( const SwNode& rNd )
+{
+ const sw::BroadcastingModify* pMod;
+ SwFrameType nFrameType = FRM_CNTNT;
+
+ if( rNd.IsContentNode() )
+ {
+ pMod = &static_cast<const SwContentNode&>(rNd);
+ }
+ else if( rNd.IsTableNode() )
+ {
+ pMod = static_cast<const SwTableNode&>(rNd).GetTable().GetFrameFormat();
+ nFrameType = SwFrameType::Tab;
+ }
+ else
+ pMod = nullptr;
+
+ Point aNullPt;
+ std::pair<Point, bool> const tmp(aNullPt, false);
+ return pMod ? ::GetFrameOfModify(nullptr, *pMod, nFrameType, nullptr, &tmp)
+ : nullptr;
+}
+
+const SwPageDesc* SwPageDesc::GetPageDescOfNode(const SwNode& rNd)
+{
+ const SwPageDesc* pRet = nullptr;
+ const SwFrame* pChkFrame = lcl_GetFrameOfNode( rNd );
+ if (pChkFrame && nullptr != (pChkFrame = pChkFrame->FindPageFrame()))
+ pRet = static_cast<const SwPageFrame*>(pChkFrame)->GetPageDesc();
+ return pRet;
+}
+
+const SwFrameFormat* SwPageDesc::GetPageFormatOfNode( const SwNode& rNd,
+ bool bCheckForThisPgDc ) const
+{
+ // which PageDescFormat is valid for this node?
+ const SwFrameFormat* pRet;
+ const SwFrame* pChkFrame = lcl_GetFrameOfNode( rNd );
+
+ if( pChkFrame && nullptr != ( pChkFrame = pChkFrame->FindPageFrame() ))
+ {
+ const SwPageDesc* pPd = bCheckForThisPgDc ? this :
+ static_cast<const SwPageFrame*>(pChkFrame)->GetPageDesc();
+ pRet = &pPd->GetMaster();
+ OSL_ENSURE( static_cast<const SwPageFrame*>(pChkFrame)->GetPageDesc() == pPd, "Wrong node for detection of page format!" );
+ // this page is assigned to which format?
+ if( !pChkFrame->KnowsFormat(*pRet) )
+ {
+ pRet = &pPd->GetLeft();
+ OSL_ENSURE( pChkFrame->KnowsFormat(*pRet), "Wrong node for detection of page format!" );
+ }
+ }
+ else
+ pRet = &GetMaster();
+ return pRet;
+}
+
+bool SwPageDesc::IsFollowNextPageOfNode( const SwNode& rNd ) const
+{
+ bool bRet = false;
+ if( GetFollow() && this != GetFollow() )
+ {
+ const SwFrame* pChkFrame = lcl_GetFrameOfNode( rNd );
+ if( pChkFrame && nullptr != ( pChkFrame = pChkFrame->FindPageFrame() ) &&
+ pChkFrame->IsPageFrame() &&
+ ( !pChkFrame->GetNext() || GetFollow() ==
+ static_cast<const SwPageFrame*>(pChkFrame->GetNext())->GetPageDesc() ))
+ // the page on which the follow points was found
+ bRet = true;
+ }
+ return bRet;
+}
+
+SwFrameFormat *SwPageDesc::GetLeftFormat(bool const bFirst)
+{
+ return (UseOnPage::Left & m_eUse)
+ ? (bFirst ? &m_FirstLeft : &m_Left)
+ : nullptr;
+}
+
+SwFrameFormat *SwPageDesc::GetRightFormat(bool const bFirst)
+{
+ return (UseOnPage::Right & m_eUse)
+ ? (bFirst ? &m_FirstMaster : &m_Master)
+ : nullptr;
+}
+
+bool SwPageDesc::IsFirstShared() const
+{
+ return bool(m_eUse & UseOnPage::FirstShare);
+}
+
+void SwPageDesc::ChgFirstShare( bool bNew )
+{
+ if ( bNew )
+ m_eUse |= UseOnPage::FirstShare;
+ else
+ m_eUse &= UseOnPage::NoFirstShare;
+}
+
+void SwPageDesc::StashFrameFormat(const SwFrameFormat& rFormat, bool bHeader, bool bLeft, bool bFirst)
+{
+ assert(rFormat.GetRegisteredIn());
+ std::shared_ptr<SwFrameFormat>* pFormat = nullptr;
+
+ if (bHeader)
+ {
+ if (bLeft && !bFirst)
+ pFormat = &m_aStashedHeader.m_pStashedLeft;
+ else if (!bLeft && bFirst)
+ pFormat = &m_aStashedHeader.m_pStashedFirst;
+ else if (bLeft && bFirst)
+ pFormat = &m_aStashedHeader.m_pStashedFirstLeft;
+ }
+ else
+ {
+ if (bLeft && !bFirst)
+ pFormat = &m_aStashedFooter.m_pStashedLeft;
+ else if (!bLeft && bFirst)
+ pFormat = &m_aStashedFooter.m_pStashedFirst;
+ else if (bLeft && bFirst)
+ pFormat = &m_aStashedFooter.m_pStashedFirstLeft;
+ }
+
+ if (pFormat)
+ {
+ *pFormat = std::make_shared<SwFrameFormat>(rFormat);
+ }
+ else
+ {
+ SAL_WARN(
+ "sw",
+ "SwPageDesc::StashFrameFormat: Stashing the right page header/footer is pointless.");
+ }
+}
+
+const SwFrameFormat* SwPageDesc::GetStashedFrameFormat(bool bHeader, bool bLeft, bool bFirst) const
+{
+ std::shared_ptr<SwFrameFormat>* pFormat = nullptr;
+
+ if (bLeft && !bFirst)
+ {
+ pFormat = bHeader ? &m_aStashedHeader.m_pStashedLeft : &m_aStashedFooter.m_pStashedLeft;
+ }
+ else if (!bLeft && bFirst)
+ {
+ pFormat = bHeader ? &m_aStashedHeader.m_pStashedFirst : &m_aStashedFooter.m_pStashedFirst;
+ }
+ else if (bLeft && bFirst)
+ {
+ pFormat = bHeader ? &m_aStashedHeader.m_pStashedFirstLeft : &m_aStashedFooter.m_pStashedFirstLeft;
+ }
+
+ if (pFormat)
+ {
+ return pFormat->get();
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::GetStashedFrameFormat: Right page format is never stashed.");
+ return nullptr;
+ }
+}
+
+bool SwPageDesc::HasStashedFormat(bool bHeader, bool bLeft, bool bFirst)
+{
+ if (bHeader)
+ {
+ if (bLeft && !bFirst)
+ {
+ return m_aStashedHeader.m_pStashedLeft != nullptr;
+ }
+ else if (!bLeft && bFirst)
+ {
+ return m_aStashedHeader.m_pStashedFirst != nullptr;
+ }
+ else if (bLeft && bFirst)
+ {
+ return m_aStashedHeader.m_pStashedFirstLeft != nullptr;
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::HasStashedFormat: Right page format is never stashed.");
+ return false;
+ }
+ }
+ else
+ {
+ if (bLeft && !bFirst)
+ {
+ return m_aStashedFooter.m_pStashedLeft != nullptr;
+ }
+ else if (!bLeft && bFirst)
+ {
+ return m_aStashedFooter.m_pStashedFirst != nullptr;
+ }
+ else if (bLeft && bFirst)
+ {
+ return m_aStashedFooter.m_pStashedFirstLeft != nullptr;
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::HasStashedFormat: Right page format is never stashed.");
+ return false;
+ }
+ }
+}
+
+void SwPageDesc::RemoveStashedFormat(bool bHeader, bool bLeft, bool bFirst)
+{
+ if (bHeader)
+ {
+ if (bLeft && !bFirst)
+ {
+ m_aStashedHeader.m_pStashedLeft.reset();
+ }
+ else if (!bLeft && bFirst)
+ {
+ m_aStashedHeader.m_pStashedFirst.reset();
+ }
+ else if (bLeft && bFirst)
+ {
+ m_aStashedHeader.m_pStashedFirstLeft.reset();
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::RemoveStashedFormat: Right page format is never stashed.");
+ }
+ }
+ else
+ {
+ if (bLeft && !bFirst)
+ {
+ m_aStashedFooter.m_pStashedLeft.reset();
+ }
+ else if (!bLeft && bFirst)
+ {
+ m_aStashedFooter.m_pStashedFirst.reset();
+ }
+ else if (bLeft && bFirst)
+ {
+ m_aStashedFooter.m_pStashedFirstLeft.reset();
+ }
+ else
+ {
+ SAL_WARN("sw", "SwPageDesc::RemoveStashedFormat: Right page format is never stashed.");
+ }
+ }
+}
+
+// Page styles
+const TranslateId STR_POOLPAGE[] =
+{
+ STR_POOLPAGE_STANDARD,
+ STR_POOLPAGE_FIRST,
+ STR_POOLPAGE_LEFT,
+ STR_POOLPAGE_RIGHT,
+ STR_POOLPAGE_ENVELOPE,
+ STR_POOLPAGE_REGISTER,
+ STR_POOLPAGE_HTML,
+ STR_POOLPAGE_FOOTNOTE,
+ STR_POOLPAGE_ENDNOTE,
+ STR_POOLPAGE_LANDSCAPE
+};
+
+SwPageDesc* SwPageDesc::GetByName(SwDoc& rDoc, std::u16string_view rName)
+{
+ const size_t nDCount = rDoc.GetPageDescCnt();
+
+ for( size_t i = 0; i < nDCount; i++ )
+ {
+ SwPageDesc* pDsc = &rDoc.GetPageDesc( i );
+ if(pDsc->GetName() == rName)
+ {
+ return pDsc;
+ }
+ }
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(STR_POOLPAGE); ++i)
+ {
+ if (rName == SwResId(STR_POOLPAGE[i]))
+ {
+ return rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool( static_cast< sal_uInt16 >(
+ i + RES_POOLPAGE_BEGIN) );
+ }
+ }
+
+ return nullptr;
+}
+
+void SwPageDesc::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPageDesc"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_StyleName"), "%s",
+ BAD_CAST(m_StyleName.toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pFollow"), "%p", m_pFollow);
+ (void)xmlTextWriterWriteFormatAttribute(
+ pWriter, BAD_CAST("m_eUse"), "0x%s",
+ BAD_CAST(OString::number(static_cast<int>(m_eUse), 16).getStr()));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Master"));
+ m_Master.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Left"));
+ m_Left.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_FirstMaster"));
+ m_FirstMaster.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_FirstLeft"));
+ m_FirstLeft.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwPageFootnoteInfo::SwPageFootnoteInfo()
+ : m_nMaxHeight( 0 )
+ , m_nLineWidth(10)
+ , m_eLineStyle( SvxBorderLineStyle::SOLID )
+ , m_Width( 25, 100 )
+ , m_nTopDist( 57 ) //1mm
+ , m_nBottomDist( 57 )
+{
+ m_eAdjust = SvxFrameDirection::Horizontal_RL_TB == GetDefaultFrameDirection(GetAppLanguage()) ?
+ css::text::HorizontalAdjust_RIGHT :
+ css::text::HorizontalAdjust_LEFT;
+}
+
+SwPageFootnoteInfo::SwPageFootnoteInfo( const SwPageFootnoteInfo &rCpy )
+ : m_nMaxHeight(rCpy.GetHeight())
+ , m_nLineWidth(rCpy.m_nLineWidth)
+ , m_eLineStyle(rCpy.m_eLineStyle)
+ , m_LineColor(rCpy.m_LineColor)
+ , m_Width(rCpy.GetWidth())
+ , m_eAdjust(rCpy.GetAdj())
+ , m_nTopDist(rCpy.GetTopDist())
+ , m_nBottomDist(rCpy.GetBottomDist())
+{
+}
+
+SwPageFootnoteInfo &SwPageFootnoteInfo::operator=( const SwPageFootnoteInfo& rCpy )
+{
+ m_nMaxHeight = rCpy.GetHeight();
+ m_nLineWidth = rCpy.m_nLineWidth;
+ m_eLineStyle = rCpy.m_eLineStyle;
+ m_LineColor = rCpy.m_LineColor;
+ m_Width = rCpy.GetWidth();
+ m_eAdjust = rCpy.GetAdj();
+ m_nTopDist = rCpy.GetTopDist();
+ m_nBottomDist = rCpy.GetBottomDist();
+ return *this;
+}
+
+bool SwPageFootnoteInfo::operator==( const SwPageFootnoteInfo& rCmp ) const
+{
+ return m_nMaxHeight == rCmp.GetHeight()
+ && m_nLineWidth == rCmp.m_nLineWidth
+ && m_eLineStyle == rCmp.m_eLineStyle
+ && m_LineColor == rCmp.m_LineColor
+ && m_Width == rCmp.GetWidth()
+ && m_eAdjust == rCmp.GetAdj()
+ && m_nTopDist == rCmp.GetTopDist()
+ && m_nBottomDist== rCmp.GetBottomDist();
+}
+
+SwPageDescExt::SwPageDescExt(const SwPageDesc & rPageDesc, SwDoc *const pDoc)
+ : m_PageDesc(rPageDesc)
+ , m_pDoc(pDoc)
+{
+ SetPageDesc(rPageDesc);
+}
+
+SwPageDescExt::SwPageDescExt(const SwPageDescExt & rSrc)
+ : m_PageDesc(rSrc.m_PageDesc)
+ , m_pDoc(rSrc.m_pDoc)
+{
+ SetPageDesc(rSrc.m_PageDesc);
+}
+
+SwPageDescExt::~SwPageDescExt()
+{
+}
+
+OUString const & SwPageDescExt::GetName() const
+{
+ return m_PageDesc.GetName();
+}
+
+void SwPageDescExt::SetPageDesc(const SwPageDesc & rPageDesc)
+{
+ m_PageDesc = rPageDesc;
+
+ if (m_PageDesc.GetFollow())
+ m_sFollow = m_PageDesc.GetFollow()->GetName();
+}
+
+SwPageDescExt & SwPageDescExt::operator = (const SwPageDesc & rSrc)
+{
+ SetPageDesc(rSrc);
+
+ return *this;
+}
+
+SwPageDescExt & SwPageDescExt::operator = (const SwPageDescExt & rSrc)
+{
+ operator=(rSrc.m_PageDesc);
+ return *this;
+}
+
+SwPageDescExt::operator SwPageDesc() const
+{
+ SwPageDesc aResult(m_PageDesc);
+
+ SwPageDesc * pPageDesc = m_pDoc->FindPageDesc(m_sFollow);
+
+ if ( nullptr != pPageDesc )
+ aResult.SetFollow(pPageDesc);
+
+ return aResult;
+}
+
+SwPageDescs::SwPageDescs()
+ : m_PosIndex( m_Array.get<0>() )
+ , m_NameIndex( m_Array.get<1>() )
+{
+}
+
+SwPageDescs::~SwPageDescs()
+{
+ for(const_iterator it = begin(); it != end(); ++it)
+ delete *it;
+}
+
+SwPageDescs::iterator SwPageDescs::find_(const OUString &name) const
+{
+ ByName::iterator it = m_NameIndex.find( name );
+ return m_Array.iterator_to( *it );
+}
+
+std::pair<SwPageDescs::const_iterator,bool> SwPageDescs::push_back( const value_type& x )
+{
+ // SwPageDesc is not already in a SwPageDescs list!
+ assert( x->m_pdList == nullptr );
+
+ std::pair<iterator,bool> res = m_PosIndex.push_back( x );
+ if( res.second )
+ x->m_pdList = this;
+ return res;
+}
+
+void SwPageDescs::erase( const value_type& x )
+{
+ // SwPageDesc is not in this SwPageDescs list!
+ assert( x->m_pdList == this );
+
+ iterator const ret = find_( x->GetName() );
+ if (ret != end())
+ m_PosIndex.erase( ret );
+ else
+ SAL_WARN( "sw", "SwPageDesc is not in SwPageDescs m_pdList!" );
+ x->m_pdList = nullptr;
+}
+
+void SwPageDescs::erase( const_iterator const& position )
+{
+ // SwPageDesc is not in this SwPageDescs list!
+ assert( (*position)->m_pdList == this );
+
+ (*position)->m_pdList = nullptr;
+ m_PosIndex.erase( position );
+}
+
+void SwPageDescs::erase( size_type index_ )
+{
+ erase( begin() + index_ );
+}
+
+void SwPageDescs::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPageDescs"));
+
+ for (const auto& pPageDesc : m_PosIndex)
+ {
+ pPageDesc->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/paintfrm.cxx b/sw/source/core/layout/paintfrm.cxx
new file mode 100644
index 000000000..f168cbd5a
--- /dev/null
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -0,0 +1,7721 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/lazydelete.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/progress.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <svx/ctredlin.hxx>
+#include <svx/framelink.hxx>
+#include <drawdoc.hxx>
+#include <tgrditem.hxx>
+#include <calbck.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtclds.hxx>
+#include <strings.hrc>
+#include <swmodule.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <section.hxx>
+#include <sectfrm.hxx>
+#include <viewimp.hxx>
+#include <dflyobj.hxx>
+#include <flyfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <dview.hxx>
+#include <dcontact.hxx>
+#include <txtfrm.hxx>
+#include <ftnfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <notxtfrm.hxx>
+#include <layact.hxx>
+#include <pagedesc.hxx>
+#include <ptqueue.hxx>
+#include <noteurl.hxx>
+#include "virtoutp.hxx"
+#include <lineinfo.hxx>
+#include <dbg_lay.hxx>
+#include <docsh.hxx>
+#include <svx/svdogrp.hxx>
+#include <sortedobjs.hxx>
+#include <EnhancedPDFExportHelper.hxx>
+#include <bodyfrm.hxx>
+#include <hffrm.hxx>
+#include <colfrm.hxx>
+#include <sw_primitivetypes2d.hxx>
+#include <swfont.hxx>
+
+#include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
+#include <svx/sdr/contact/viewobjectcontactredirector.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+
+#include <ndole.hxx>
+#include <PostItMgr.hxx>
+#include <FrameControlsManager.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+
+#include <svtools/borderhelper.hxx>
+
+#include <bitmaps.hlst>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processorfromoutputdevice.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/xfillit0.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/color/bcolortools.hxx>
+#include <basegfx/utils/b2dclipstate.hxx>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <wrtsh.hxx>
+#include <edtwin.hxx>
+#include <view.hxx>
+#include <paintfrm.hxx>
+#include <textboxhelper.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+#include <vcl/BitmapTools.hxx>
+#include <comphelper/lok.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+using namespace ::editeng;
+using namespace ::com::sun::star;
+
+namespace {
+
+struct SwPaintProperties;
+
+//Class declaration; here because they are only used in this file
+enum class SubColFlags {
+ Page = 0x01, //Helplines of the page
+ Tab = 0x08, //Helplines inside tables
+ Fly = 0x10, //Helplines inside fly frames
+ Sect = 0x20, //Helplines inside sections
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<SubColFlags> : is_typed_flags<SubColFlags, 0x39> {};
+}
+
+namespace {
+
+// Classes collecting the border lines and help lines
+class SwLineRect : public SwRect
+{
+ Color m_aColor;
+ SvxBorderLineStyle m_nStyle;
+ const SwTabFrame* m_pTabFrame;
+ SubColFlags m_nSubColor; //colorize subsidiary lines
+ bool m_bPainted; //already painted?
+ sal_uInt8 m_nLock; //To distinguish the line and the hell layer.
+public:
+ SwLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
+ const SwTabFrame *pT , const SubColFlags nSCol );
+
+ const Color& GetColor() const { return m_aColor; }
+ SvxBorderLineStyle GetStyle() const { return m_nStyle; }
+ const SwTabFrame* GetTab() const { return m_pTabFrame; }
+ void SetPainted() { m_bPainted = true; }
+ void Lock(bool bLock)
+ {
+ if (bLock)
+ ++m_nLock;
+ else if (m_nLock)
+ --m_nLock;
+ }
+ bool IsPainted() const { return m_bPainted; }
+ bool IsLocked() const { return m_nLock != 0; }
+ SubColFlags GetSubColor() const { return m_nSubColor; }
+
+ bool MakeUnion(const SwRect& rRect, SwPaintProperties const& properties);
+};
+
+}
+
+#ifdef IOS
+static void dummy_function()
+{
+ pid_t pid = getpid();
+ (void) pid;
+}
+#endif
+
+namespace {
+
+class SwLineRects
+{
+public:
+ std::vector<SwLineRect> m_aLineRects;
+ typedef std::vector< SwLineRect >::const_iterator const_iterator;
+ typedef std::vector< SwLineRect >::iterator iterator;
+ typedef std::vector< SwLineRect >::reverse_iterator reverse_iterator;
+ typedef std::vector< SwLineRect >::size_type size_type;
+ size_t m_nLastCount; //avoid unnecessary cycles in PaintLines
+ SwLineRects()
+ : m_nLastCount(0)
+ {
+#ifdef IOS
+ // Work around what is either a compiler bug in Xcode 5.1.1,
+ // or some unknown problem in this file. If I ifdef out this
+ // call, I get a crash in SwSubsRects::PaintSubsidiary: the
+ // address of the rLi reference variable is claimed to be
+ // 0x4000000!
+ dummy_function();
+#endif
+ }
+ void AddLineRect( const SwRect& rRect, const Color *pColor, const SvxBorderLineStyle nStyle,
+ const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const &properties );
+ void ConnectEdges( OutputDevice const *pOut, SwPaintProperties const &properties );
+ void PaintLines ( OutputDevice *pOut, SwPaintProperties const &properties );
+ void LockLines( bool bLock );
+
+ //Limit lines to 100
+ bool isFull() const { return m_aLineRects.size() > 100; }
+};
+
+class SwSubsRects : public SwLineRects
+{
+ void RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const &properties );
+public:
+ void PaintSubsidiary( OutputDevice *pOut, const SwLineRects *pRects, SwPaintProperties const &properties );
+};
+
+class BorderLines
+{
+ drawinglayer::primitive2d::Primitive2DContainer m_Lines;
+public:
+ void AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer);
+ drawinglayer::primitive2d::Primitive2DContainer GetBorderLines_Clear()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer lines;
+ lines.swap(m_Lines);
+ return lines;
+ }
+};
+
+}
+
+// Default zoom factor
+const double aEdgeScale = 0.5;
+
+//To optimize the expensive RetouchColor determination
+Color aGlobalRetoucheColor;
+
+namespace sw
+{
+Color* GetActiveRetoucheColor()
+{
+ return &aGlobalRetoucheColor;
+}
+}
+
+namespace {
+
+/**
+ * Container for static properties
+ */
+struct SwPaintProperties {
+ // Only repaint the Fly content as well as the background of the Fly content if
+ // a metafile is taken of the Fly.
+ bool bSFlyMetafile;
+ VclPtr<OutputDevice> pSFlyMetafileOut;
+ SwViewShell *pSGlobalShell;
+
+ // Retouch for transparent Flys is done by the background of the Flys.
+ // The Fly itself should certainly not be spared out. See PaintSwFrameBackground and
+ // lcl_SubtractFlys()
+ SwFlyFrame *pSRetoucheFly;
+ SwFlyFrame *pSRetoucheFly2;
+ SwFlyFrame *pSFlyOnlyDraw;
+
+ // The borders will be collected in pSLines during the Paint and later
+ // possibly merge them.
+ // The help lines will be collected and merged in gProp.pSSubsLines. These will
+ // be compared with pSLines before the work in order to avoid help lines
+ // to hide borders.
+ std::unique_ptr<BorderLines> pBLines;
+ std::unique_ptr<SwLineRects> pSLines;
+ std::unique_ptr<SwSubsRects> pSSubsLines;
+
+ // global variable for sub-lines of body, header, footer, section and footnote frames.
+ std::unique_ptr<SwSubsRects> pSSpecSubsLines;
+ SfxProgress *pSProgress;
+
+ // Sizes of a pixel and the corresponding halves. Will be reset when
+ // entering SwRootFrame::PaintSwFrame
+ tools::Long nSPixelSzW;
+ tools::Long nSPixelSzH;
+ tools::Long nSHalfPixelSzW;
+ tools::Long nSHalfPixelSzH;
+ tools::Long nSMinDistPixelW;
+ tools::Long nSMinDistPixelH;
+
+ Color aSGlobalRetoucheColor;
+
+ // Current zoom factor
+ double aSScaleX;
+ double aSScaleY;
+
+ SwPaintProperties()
+ : bSFlyMetafile(false)
+ , pSFlyMetafileOut(nullptr)
+ , pSGlobalShell(nullptr)
+ , pSRetoucheFly(nullptr)
+ , pSRetoucheFly2(nullptr)
+ , pSFlyOnlyDraw(nullptr)
+ , pSProgress(nullptr)
+ , nSPixelSzW(0)
+ , nSPixelSzH(0)
+ , nSHalfPixelSzW(0)
+ , nSHalfPixelSzH(0)
+ , nSMinDistPixelW(0)
+ , nSMinDistPixelH(0)
+ , aSScaleX(1)
+ , aSScaleY(1)
+ {
+ }
+
+};
+
+}
+
+static SwPaintProperties gProp;
+
+static bool isSubsidiaryLinesFlysEnabled()
+{
+ return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
+ SwViewOption::IsObjectBoundaries();
+}
+//other subsidiary lines enabled?
+static bool isSubsidiaryLinesEnabled()
+{
+ return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsWhitespaceHidden() &&
+ SwViewOption::IsDocBoundaries();
+}
+//subsidiary lines for sections
+static bool isSubsidiaryLinesForSectionsEnabled()
+{
+ return !gProp.pSGlobalShell->GetViewOptions()->IsPagePreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsFormView() &&
+ SwViewOption::IsSectionBoundaries();
+}
+
+
+namespace {
+
+bool isTableBoundariesEnabled()
+{
+ if (!gProp.pSGlobalShell->GetViewOptions()->IsTable())
+ return false;
+
+ if (gProp.pSGlobalShell->GetViewOptions()->IsPagePreview())
+ return false;
+
+ if (gProp.pSGlobalShell->GetViewOptions()->IsReadonly())
+ return false;
+
+ if (gProp.pSGlobalShell->GetViewOptions()->IsFormView())
+ return false;
+
+ return SwViewOption::IsTableBoundaries();
+}
+
+}
+
+/**
+ * Set borders alignment statics
+ * Adjustment for 'small' twip-to-pixel relations:
+ * For 'small' twip-to-pixel relations (less than 2:1)
+ * values of <gProp.nSHalfPixelSzW> and <gProp.nSHalfPixelSzH> are set to ZERO
+ */
+void SwCalcPixStatics( vcl::RenderContext const *pOut )
+{
+ // determine 'small' twip-to-pixel relation
+ bool bSmallTwipToPxRelW = false;
+ bool bSmallTwipToPxRelH = false;
+ {
+ Size aCheckTwipToPxRelSz( pOut->PixelToLogic( Size( 100, 100 )) );
+ if ( (aCheckTwipToPxRelSz.Width()/100.0) < 2.0 )
+ {
+ bSmallTwipToPxRelW = true;
+ }
+ if ( (aCheckTwipToPxRelSz.Height()/100.0) < 2.0 )
+ {
+ bSmallTwipToPxRelH = true;
+ }
+ }
+
+ Size aSz( pOut->PixelToLogic( Size( 1,1 )) );
+
+ gProp.nSPixelSzW = aSz.Width();
+ if( !gProp.nSPixelSzW )
+ gProp.nSPixelSzW = 1;
+ gProp.nSPixelSzH = aSz.Height();
+ if( !gProp.nSPixelSzH )
+ gProp.nSPixelSzH = 1;
+
+ // consider 'small' twip-to-pixel relations
+ if ( !bSmallTwipToPxRelW )
+ {
+ gProp.nSHalfPixelSzW = gProp.nSPixelSzW / 2 + 1;
+ }
+ else
+ {
+ gProp.nSHalfPixelSzW = 0;
+ }
+ // consider 'small' twip-to-pixel relations
+ if ( !bSmallTwipToPxRelH )
+ {
+ gProp.nSHalfPixelSzH = gProp.nSPixelSzH / 2 + 1;
+ }
+ else
+ {
+ gProp.nSHalfPixelSzH = 0;
+ }
+
+ gProp.nSMinDistPixelW = gProp.nSPixelSzW * 2 + 1;
+ gProp.nSMinDistPixelH = gProp.nSPixelSzH * 2 + 1;
+
+ const MapMode &rMap = pOut->GetMapMode();
+ gProp.aSScaleX = double(rMap.GetScaleX());
+ gProp.aSScaleY = double(rMap.GetScaleY());
+}
+
+namespace {
+
+/**
+ * To be able to save the statics so the paint is more or less reentrant
+ */
+class SwSavePaintStatics : public SwPaintProperties
+{
+public:
+ SwSavePaintStatics();
+ ~SwSavePaintStatics();
+};
+
+}
+
+SwSavePaintStatics::SwSavePaintStatics()
+{
+ // Saving globales
+ bSFlyMetafile = gProp.bSFlyMetafile;
+ pSGlobalShell = gProp.pSGlobalShell;
+ pSFlyMetafileOut = gProp.pSFlyMetafileOut;
+ pSRetoucheFly = gProp.pSRetoucheFly;
+ pSRetoucheFly2 = gProp.pSRetoucheFly2;
+ pSFlyOnlyDraw = gProp.pSFlyOnlyDraw;
+ pBLines = std::move(gProp.pBLines);
+ pSLines = std::move(gProp.pSLines);
+ pSSubsLines = std::move(gProp.pSSubsLines);
+ pSSpecSubsLines = std::move(gProp.pSSpecSubsLines);
+ pSProgress = gProp.pSProgress;
+ nSPixelSzW = gProp.nSPixelSzW;
+ nSPixelSzH = gProp.nSPixelSzH;
+ nSHalfPixelSzW = gProp.nSHalfPixelSzW;
+ nSHalfPixelSzH = gProp.nSHalfPixelSzH;
+ nSMinDistPixelW = gProp.nSMinDistPixelW;
+ nSMinDistPixelH = gProp.nSMinDistPixelH ;
+ aSGlobalRetoucheColor = aGlobalRetoucheColor;
+ aSScaleX = gProp.aSScaleX;
+ aSScaleY = gProp.aSScaleY;
+
+ // Restoring globales to default
+ gProp.bSFlyMetafile = false;
+ gProp.pSFlyMetafileOut = nullptr;
+ gProp.pSRetoucheFly = nullptr;
+ gProp.pSRetoucheFly2 = nullptr;
+ gProp.nSPixelSzW = gProp.nSPixelSzH =
+ gProp.nSHalfPixelSzW = gProp.nSHalfPixelSzH =
+ gProp.nSMinDistPixelW = gProp.nSMinDistPixelH = 0;
+ gProp.aSScaleX = gProp.aSScaleY = 1.0;
+ gProp.pSProgress = nullptr;
+}
+
+SwSavePaintStatics::~SwSavePaintStatics()
+{
+ // Restoring globales to saved one
+ gProp.pSGlobalShell = pSGlobalShell;
+ gProp.bSFlyMetafile = bSFlyMetafile;
+ gProp.pSFlyMetafileOut = pSFlyMetafileOut;
+ gProp.pSRetoucheFly = pSRetoucheFly;
+ gProp.pSRetoucheFly2 = pSRetoucheFly2;
+ gProp.pSFlyOnlyDraw = pSFlyOnlyDraw;
+ gProp.pBLines = std::move(pBLines);
+ gProp.pSLines = std::move(pSLines);
+ gProp.pSSubsLines = std::move(pSSubsLines);
+ gProp.pSSpecSubsLines = std::move(pSSpecSubsLines);
+ gProp.pSProgress = pSProgress;
+ gProp.nSPixelSzW = nSPixelSzW;
+ gProp.nSPixelSzH = nSPixelSzH;
+ gProp.nSHalfPixelSzW = nSHalfPixelSzW;
+ gProp.nSHalfPixelSzH = nSHalfPixelSzH;
+ gProp.nSMinDistPixelW = nSMinDistPixelW;
+ gProp.nSMinDistPixelH = nSMinDistPixelH;
+ aGlobalRetoucheColor = aSGlobalRetoucheColor;
+ gProp.aSScaleX = aSScaleX;
+ gProp.aSScaleY = aSScaleY;
+}
+
+void BorderLines::AddBorderLines(drawinglayer::primitive2d::Primitive2DContainer&& rContainer)
+{
+ if(!rContainer.empty())
+ {
+ m_Lines.append(std::move(rContainer));
+ }
+}
+
+SwLineRect::SwLineRect(const SwRect& rRect, const Color* pCol, const SvxBorderLineStyle nStyl,
+ const SwTabFrame* pT, const SubColFlags nSCol)
+ : SwRect(rRect)
+ , m_nStyle(nStyl)
+ , m_pTabFrame(pT)
+ , m_nSubColor(nSCol)
+ , m_bPainted(false)
+ , m_nLock(0)
+{
+ if ( pCol != nullptr )
+ m_aColor = *pCol;
+}
+
+bool SwLineRect::MakeUnion( const SwRect &rRect, SwPaintProperties const & properties)
+{
+ // It has already been tested outside, whether the rectangles have
+ // the same orientation (horizontal or vertical), color, etc.
+ if ( Height() > Width() ) //Vertical line
+ {
+ if ( Left() == rRect.Left() && Width() == rRect.Width() )
+ {
+ // Merge when there is no gap between the lines
+ const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
+ if ( Bottom() + nAdd >= rRect.Top() &&
+ Top() - nAdd <= rRect.Bottom() )
+ {
+ Bottom( std::max( Bottom(), rRect.Bottom() ) );
+ Top ( std::min( Top(), rRect.Top() ) );
+ return true;
+ }
+ }
+ }
+ else
+ {
+ if ( Top() == rRect.Top() && Height() == rRect.Height() )
+ {
+ // Merge when there is no gap between the lines
+ const tools::Long nAdd = properties.nSPixelSzW + properties.nSHalfPixelSzW;
+ if ( Right() + nAdd >= rRect.Left() &&
+ Left() - nAdd <= rRect.Right() )
+ {
+ Right( std::max( Right(), rRect.Right() ) );
+ Left ( std::min( Left(), rRect.Left() ) );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void SwLineRects::AddLineRect( const SwRect &rRect, const Color *pCol, const SvxBorderLineStyle nStyle,
+ const SwTabFrame *pTab, const SubColFlags nSCol, SwPaintProperties const & properties )
+{
+ // Loop backwards because lines which can be combined, can usually be painted
+ // in the same context
+ for (reverse_iterator it = m_aLineRects.rbegin(); it != m_aLineRects.rend(); ++it)
+ {
+ SwLineRect &rLRect = *it;
+ // Test for the orientation, color, table
+ if ( rLRect.GetTab() == pTab &&
+ !rLRect.IsPainted() && rLRect.GetSubColor() == nSCol &&
+ (rLRect.Height() > rLRect.Width()) == (rRect.Height() > rRect.Width()) &&
+ (pCol && rLRect.GetColor() == *pCol) )
+ {
+ if ( rLRect.MakeUnion( rRect, properties ) )
+ return;
+ }
+ }
+ m_aLineRects.emplace_back(rRect, pCol, nStyle, pTab, nSCol);
+}
+
+void SwLineRects::ConnectEdges( OutputDevice const *pOut, SwPaintProperties const & properties )
+{
+ if ( pOut->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ // I'm not doing anything for a too small zoom
+ if ( properties.aSScaleX < aEdgeScale || properties.aSScaleY < aEdgeScale )
+ return;
+ }
+
+ static const tools::Long nAdd = 20;
+
+ std::vector<SwLineRect*> aCheck;
+
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rL1 = m_aLineRects[i];
+ if ( !rL1.GetTab() || rL1.IsPainted() || rL1.IsLocked() )
+ continue;
+
+ aCheck.clear();
+
+ const bool bVert = rL1.Height() > rL1.Width();
+ tools::Long nL1a, nL1b, nL1c, nL1d;
+
+ if ( bVert )
+ {
+ nL1a = rL1.Top(); nL1b = rL1.Left();
+ nL1c = rL1.Right(); nL1d = rL1.Bottom();
+ }
+ else
+ {
+ nL1a = rL1.Left(); nL1b = rL1.Top();
+ nL1c = rL1.Bottom(); nL1d = rL1.Right();
+ }
+
+ // Collect all lines to possibly link with i1
+ for (iterator it2 = m_aLineRects.begin(); it2 != m_aLineRects.end(); ++it2)
+ {
+ SwLineRect &rL2 = *it2;
+ if ( rL2.GetTab() != rL1.GetTab() ||
+ rL2.IsPainted() ||
+ rL2.IsLocked() ||
+ (bVert == (rL2.Height() > rL2.Width())) )
+ continue;
+
+ tools::Long nL2a, nL2b, nL2c, nL2d;
+ if ( bVert )
+ {
+ nL2a = rL2.Top(); nL2b = rL2.Left();
+ nL2c = rL2.Right(); nL2d = rL2.Bottom();
+ }
+ else
+ {
+ nL2a = rL2.Left(); nL2b = rL2.Top();
+ nL2c = rL2.Bottom(); nL2d = rL2.Right();
+ }
+
+ if ( (nL1a - nAdd < nL2d && nL1d + nAdd > nL2a) &&
+ ((nL1b > nL2b && nL1c < nL2c) ||
+ (nL1c >= nL2c && nL1b - nAdd < nL2c) ||
+ (nL1b <= nL2b && nL1c + nAdd > nL2b)) )
+ {
+ aCheck.push_back( &rL2 );
+ }
+ }
+ if ( aCheck.size() < 2 )
+ continue;
+
+ bool bRemove = false;
+
+ // For each line test all following ones.
+ for ( size_t k = 0; !bRemove && k < aCheck.size(); ++k )
+ {
+ SwLineRect &rR1 = *aCheck[k];
+
+ for ( size_t k2 = k+1; !bRemove && k2 < aCheck.size(); ++k2 )
+ {
+ SwLineRect &rR2 = *aCheck[k2];
+ if ( bVert )
+ {
+ SwLineRect *pLA = nullptr;
+ SwLineRect *pLB = nullptr;
+ if ( rR1.Top() < rR2.Top() )
+ {
+ pLA = &rR1; pLB = &rR2;
+ }
+ else if ( rR1.Top() > rR2.Top() )
+ {
+ pLA = &rR2; pLB = &rR1;
+ }
+ // are k1 and k2 describing a double line?
+ if ( pLA && pLA->Bottom() + 60 > pLB->Top() )
+ {
+ if ( rL1.Top() < pLA->Top() )
+ {
+ if ( rL1.Bottom() == pLA->Bottom() )
+ continue; //Small mistake (where?)
+
+ SwRect aIns( rL1 );
+ aIns.Bottom( pLA->Bottom() );
+ if ( !rL1.Contains( aIns ) )
+ continue;
+ m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
+ SvxBorderLineStyle::SOLID, rL1.GetTab(),
+ SubColFlags::Tab);
+ if ( isFull() )
+ {
+ --i;
+ k = aCheck.size();
+ break;
+ }
+ }
+
+ if ( rL1.Bottom() > pLB->Bottom() )
+ rL1.Top( pLB->Top() ); // extend i1 on the top
+ else
+ bRemove = true; //stopping, remove i1
+ }
+ }
+ else
+ {
+ SwLineRect *pLA = nullptr;
+ SwLineRect *pLB = nullptr;
+ if ( rR1.Left() < rR2.Left() )
+ {
+ pLA = &rR1; pLB = &rR2;
+ }
+ else if ( rR1.Left() > rR2.Left() )
+ {
+ pLA = &rR2; pLB = &rR1;
+ }
+ // Is it double line?
+ if ( pLA && pLA->Right() + 60 > pLB->Left() )
+ {
+ if ( rL1.Left() < pLA->Left() )
+ {
+ if ( rL1.Right() == pLA->Right() )
+ continue; //small error
+
+ SwRect aIns( rL1 );
+ aIns.Right( pLA->Right() );
+ if ( !rL1.Contains( aIns ) )
+ continue;
+ m_aLineRects.emplace_back(aIns, &rL1.GetColor(),
+ SvxBorderLineStyle::SOLID, rL1.GetTab(),
+ SubColFlags::Tab);
+ if ( isFull() )
+ {
+ --i;
+ k = aCheck.size();
+ break;
+ }
+ }
+ if ( rL1.Right() > pLB->Right() )
+ rL1.Left( pLB->Left() );
+ else
+ bRemove = true;
+ }
+ }
+ }
+ }
+ if ( bRemove )
+ {
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ --i;
+ }
+ }
+}
+
+void SwSubsRects::RemoveSuperfluousSubsidiaryLines( const SwLineRects &rRects, SwPaintProperties const & properties )
+{
+ // All help lines that are covered by any border will be removed or split
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ // get a copy instead of a reference, because an <insert> may destroy
+ // the object due to a necessary array resize.
+ const SwLineRect aSubsLineRect(m_aLineRects[i]);
+
+ // add condition <aSubsLineRect.IsLocked()> in order to consider only
+ // border lines, which are *not* locked.
+ if ( aSubsLineRect.IsPainted() ||
+ aSubsLineRect.IsLocked() )
+ continue;
+
+ const bool bVerticalSubs = aSubsLineRect.Height() > aSubsLineRect.Width();
+ SwRect aSubsRect( aSubsLineRect );
+ if ( bVerticalSubs )
+ {
+ aSubsRect.AddLeft ( - (properties.nSPixelSzW+properties.nSHalfPixelSzW) );
+ aSubsRect.AddRight ( properties.nSPixelSzW+properties.nSHalfPixelSzW );
+ }
+ else
+ {
+ aSubsRect.AddTop ( - (properties.nSPixelSzH+properties.nSHalfPixelSzH) );
+ aSubsRect.AddBottom( properties.nSPixelSzH+properties.nSHalfPixelSzH );
+ }
+ for (const_iterator itK = rRects.m_aLineRects.begin(); itK != rRects.m_aLineRects.end();
+ ++itK)
+ {
+ const SwLineRect &rLine = *itK;
+
+ // do *not* consider painted or locked border lines.
+ // #i1837# - locked border lines have to be considered.
+ if ( rLine.IsLocked () )
+ continue;
+
+ if ( !bVerticalSubs == ( rLine.Height() > rLine.Width() ) ) //same direction?
+ continue;
+
+ if ( aSubsRect.Overlaps( rLine ) )
+ {
+ if ( bVerticalSubs ) // Vertical?
+ {
+ if ( aSubsRect.Left() <= rLine.Right() &&
+ aSubsRect.Right() >= rLine.Left() )
+ {
+ tools::Long nTmp = rLine.Top()-(properties.nSPixelSzH+1);
+ if ( aSubsLineRect.Top() < nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Bottom( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ nTmp = rLine.Bottom()+properties.nSPixelSzH+1;
+ if ( aSubsLineRect.Bottom() > nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Top( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ --i;
+ break;
+ }
+ }
+ else // Horizontal
+ {
+ if ( aSubsRect.Top() <= rLine.Bottom() &&
+ aSubsRect.Bottom() >= rLine.Top() )
+ {
+ tools::Long nTmp = rLine.Left()-(properties.nSPixelSzW+1);
+ if ( aSubsLineRect.Left() < nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Right( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ nTmp = rLine.Right()+properties.nSPixelSzW+1;
+ if ( aSubsLineRect.Right() > nTmp )
+ {
+ SwRect aNewSubsRect( aSubsLineRect );
+ aNewSubsRect.Left( nTmp );
+ m_aLineRects.emplace_back(aNewSubsRect, nullptr,
+ aSubsLineRect.GetStyle(), nullptr,
+ aSubsLineRect.GetSubColor());
+ }
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ --i;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void SwLineRects::LockLines( bool bLock )
+{
+ for (SwLineRect& rLRect : m_aLineRects)
+ rLRect.Lock(bLock);
+}
+
+static void lcl_DrawDashedRect( OutputDevice * pOut, SwLineRect const & rLRect )
+{
+ tools::Long startX = rLRect.Left( ), endX;
+ tools::Long startY = rLRect.Top( ), endY;
+
+ // Discriminate vertically stretched rect from horizontally stretched
+ // and restrict minimum nHalfLWidth to 1
+ tools::Long nHalfLWidth = std::max( std::min( rLRect.Width( ), rLRect.Height( ) ) / 2, tools::Long(1) );
+
+ if ( rLRect.Height( ) > rLRect.Width( ) )
+ {
+ startX += nHalfLWidth;
+ endX = startX;
+ endY = startY + rLRect.Height( );
+ }
+ else
+ {
+ startY += nHalfLWidth;
+ endY = startY;
+ endX = startX + rLRect.Width( );
+ }
+
+ svtools::DrawLine( *pOut, Point( startX, startY ), Point( endX, endY ),
+ sal_uInt32( nHalfLWidth * 2 ), rLRect.GetStyle( ) );
+}
+
+void SwLineRects::PaintLines( OutputDevice *pOut, SwPaintProperties const &properties )
+{
+ // Paint the borders. Sadly two passes are needed.
+ // Once for the inside and once for the outside edges of tables
+ if (m_aLineRects.size() == m_nLastCount)
+ return;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
+
+ pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pOut->SetFillColor();
+ pOut->SetLineColor();
+ ConnectEdges( pOut, properties );
+ const Color *pLast = nullptr;
+
+ bool bPaint2nd = false;
+ size_t nMinCount = m_aLineRects.size();
+
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rLRect = m_aLineRects[i];
+
+ if ( rLRect.IsPainted() )
+ continue;
+
+ if ( rLRect.IsLocked() )
+ {
+ nMinCount = std::min( nMinCount, i );
+ continue;
+ }
+
+ // Paint it now or in the second pass?
+ bool bPaint = true;
+ if ( rLRect.GetTab() )
+ {
+ if ( rLRect.Height() > rLRect.Width() )
+ {
+ // Vertical edge, overlapping with the table edge?
+ SwTwips nLLeft = rLRect.Left() - 30,
+ nLRight = rLRect.Right() + 30,
+ nTLeft = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Left(),
+ nTRight = rLRect.GetTab()->getFrameArea().Left() + rLRect.GetTab()->getFramePrintArea().Right();
+ if ( (nTLeft >= nLLeft && nTLeft <= nLRight) ||
+ (nTRight>= nLLeft && nTRight<= nLRight) )
+ bPaint = false;
+ }
+ else
+ {
+ // Horizontal edge, overlapping with the table edge?
+ SwTwips nLTop = rLRect.Top() - 30,
+ nLBottom = rLRect.Bottom() + 30,
+ nTTop = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Top(),
+ nTBottom = rLRect.GetTab()->getFrameArea().Top() + rLRect.GetTab()->getFramePrintArea().Bottom();
+ if ( (nTTop >= nLTop && nTTop <= nLBottom) ||
+ (nTBottom >= nLTop && nTBottom <= nLBottom) )
+ bPaint = false;
+ }
+ }
+ if ( bPaint )
+ {
+ if ( !pLast || *pLast != rLRect.GetColor() )
+ {
+ pLast = &rLRect.GetColor();
+
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ if( properties.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ pOut->SetDrawMode( DrawModeFlags::Default );
+
+ pOut->SetLineColor( *pLast );
+ pOut->SetFillColor( *pLast );
+ pOut->SetDrawMode( nOldDrawMode );
+ }
+
+ if( !rLRect.IsEmpty() )
+ lcl_DrawDashedRect( pOut, rLRect );
+ rLRect.SetPainted();
+ }
+ else
+ bPaint2nd = true;
+ }
+ if ( bPaint2nd )
+ {
+ for (size_t i = 0; i < m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rLRect = m_aLineRects[i];
+ if ( rLRect.IsPainted() )
+ continue;
+
+ if ( rLRect.IsLocked() )
+ {
+ nMinCount = std::min( nMinCount, i );
+ continue;
+ }
+
+ if ( !pLast || *pLast != rLRect.GetColor() )
+ {
+ pLast = &rLRect.GetColor();
+
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ if( properties.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pOut->SetDrawMode( DrawModeFlags::Default );
+ }
+
+ pOut->SetFillColor( *pLast );
+ pOut->SetDrawMode( nOldDrawMode );
+ }
+ if( !rLRect.IsEmpty() )
+ lcl_DrawDashedRect( pOut, rLRect );
+ rLRect.SetPainted();
+ }
+ }
+ m_nLastCount = nMinCount;
+ pOut->Pop();
+
+}
+
+void SwSubsRects::PaintSubsidiary( OutputDevice *pOut,
+ const SwLineRects *pRects,
+ SwPaintProperties const & properties )
+{
+ if (m_aLineRects.empty())
+ return;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
+
+ // Remove all help line that are almost covered (tables)
+ for (size_type i = 0; i != m_aLineRects.size(); ++i)
+ {
+ SwLineRect& rLi = m_aLineRects[i];
+ const bool bVerticalSubs = rLi.Height() > rLi.Width();
+
+ for (size_type k = i + 1; k != m_aLineRects.size(); ++k)
+ {
+ SwLineRect& rLk = m_aLineRects[k];
+ if ( rLi.SSize() == rLk.SSize() )
+ {
+ if ( bVerticalSubs == ( rLk.Height() > rLk.Width() ) )
+ {
+ if ( bVerticalSubs )
+ {
+ tools::Long nLi = rLi.Right();
+ tools::Long nLk = rLk.Right();
+ if ( rLi.Top() == rLk.Top() &&
+ ((nLi < rLk.Left() && nLi+21 > rLk.Left()) ||
+ (nLk < rLi.Left() && nLk+21 > rLi.Left())))
+ {
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ // don't continue with inner loop any more:
+ // the array may shrink!
+ --i;
+ break;
+ }
+ }
+ else
+ {
+ tools::Long nLi = rLi.Bottom();
+ tools::Long nLk = rLk.Bottom();
+ if ( rLi.Left() == rLk.Left() &&
+ ((nLi < rLk.Top() && nLi+21 > rLk.Top()) ||
+ (nLk < rLi.Top() && nLk+21 > rLi.Top())))
+ {
+ m_aLineRects.erase(m_aLineRects.begin() + i);
+ // don't continue with inner loop any more:
+ // the array may shrink!
+ --i;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (pRects && (!pRects->m_aLineRects.empty()))
+ RemoveSuperfluousSubsidiaryLines( *pRects, properties );
+
+ if (m_aLineRects.empty())
+ return;
+
+ pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pOut->SetLineColor();
+
+ // Reset draw mode in high contrast mode in order to get fill color
+ // set at output device. Recover draw mode after draw of lines.
+ // Necessary for the subsidiary lines painted by the fly frames.
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ if( gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pOut->SetDrawMode( DrawModeFlags::Default );
+ }
+
+ for (SwLineRect& rLRect : m_aLineRects)
+ {
+ // Add condition <!rLRect.IsLocked()> to prevent paint of locked subsidiary lines.
+ if ( !rLRect.IsPainted() &&
+ !rLRect.IsLocked() )
+ {
+ const Color *pCol = nullptr;
+ switch ( rLRect.GetSubColor() )
+ {
+ case SubColFlags::Page: pCol = &SwViewOption::GetDocBoundariesColor(); break;
+ case SubColFlags::Fly: pCol = &SwViewOption::GetObjectBoundariesColor(); break;
+ case SubColFlags::Tab: pCol = &SwViewOption::GetTableBoundariesColor(); break;
+ case SubColFlags::Sect: pCol = &SwViewOption::GetSectionBoundColor(); break;
+ }
+
+ if (pCol && pOut->GetFillColor() != *pCol)
+ pOut->SetFillColor( *pCol );
+ pOut->DrawRect( rLRect.SVRect() );
+
+ rLRect.SetPainted();
+ }
+ }
+
+ pOut->SetDrawMode( nOldDrawMode );
+
+ pOut->Pop();
+}
+
+// Various functions that are use in this file.
+
+/**
+ * Function <SwAlignRect(..)> is also used outside this file
+ *
+ * Correction: adjust rectangle on pixel level in order to make sure,
+ * that the border "leaves its original pixel", if it has to
+ * No prior adjustments for odd relation between pixel and twip
+ */
+void SwAlignRect( SwRect &rRect, const SwViewShell *pSh, const vcl::RenderContext* pRenderContext )
+{
+ if( !rRect.HasArea() )
+ return;
+
+ // Make sure that view shell (parameter <pSh>) exists, if the output device
+ // is taken from this view shell --> no output device, no alignment
+ // Output device taken from view shell <pSh>, if <gProp.bSFlyMetafile> not set
+ if ( !gProp.bSFlyMetafile && !pSh )
+ {
+ return;
+ }
+
+ const vcl::RenderContext *pOut = gProp.bSFlyMetafile ?
+ gProp.pSFlyMetafileOut.get() : pRenderContext;
+
+ // Hold original rectangle in pixel
+ const tools::Rectangle aOrgPxRect = pOut->LogicToPixel( rRect.SVRect() );
+ // Determine pixel-center rectangle in twip
+ const SwRect aPxCenterRect( pOut->PixelToLogic( aOrgPxRect ) );
+
+ // Perform adjustments on pixel level.
+ SwRect aAlignedPxRect( aOrgPxRect );
+ if ( rRect.Top() > aPxCenterRect.Top() )
+ {
+ // 'leave pixel overlapping on top'
+ aAlignedPxRect.AddTop( 1 );
+ }
+
+ if ( rRect.Bottom() < aPxCenterRect.Bottom() )
+ {
+ // 'leave pixel overlapping on bottom'
+ aAlignedPxRect.AddBottom( - 1 );
+ }
+
+ if ( rRect.Left() > aPxCenterRect.Left() )
+ {
+ // 'leave pixel overlapping on left'
+ aAlignedPxRect.AddLeft( 1 );
+ }
+
+ if ( rRect.Right() < aPxCenterRect.Right() )
+ {
+ // 'leave pixel overlapping on right'
+ aAlignedPxRect.AddRight( - 1 );
+ }
+
+ // Consider negative width/height check, if aligned SwRect has negative width/height.
+ // If Yes, adjust it to width/height = 0 twip.
+ // NOTE: A SwRect with negative width/height can occur, if the width/height
+ // of the given SwRect in twip was less than a pixel in twip and that
+ // the alignment calculates that the aligned SwRect should not contain
+ // the pixels the width/height is on.
+ if ( aAlignedPxRect.Width() < 0 )
+ {
+ aAlignedPxRect.Width(0);
+ }
+ if ( aAlignedPxRect.Height() < 0 )
+ {
+ aAlignedPxRect.Height(0);
+ }
+ // Consider zero width/height for converting a rectangle from
+ // pixel to logic it needs a width/height. Thus, set width/height
+ // to one, if it's zero and correct this on the twip level after the conversion.
+ bool bZeroWidth = false;
+ if ( aAlignedPxRect.Width() == 0 )
+ {
+ aAlignedPxRect.Width(1);
+ bZeroWidth = true;
+ }
+ bool bZeroHeight = false;
+ if ( aAlignedPxRect.Height() == 0 )
+ {
+ aAlignedPxRect.Height(1);
+ bZeroHeight = true;
+ }
+
+ rRect = SwRect(pOut->PixelToLogic( aAlignedPxRect.SVRect() ));
+
+ // Consider zero width/height and adjust calculated aligned twip rectangle.
+ // Reset width/height to zero; previous negative width/height haven't to be considered.
+ if ( bZeroWidth )
+ {
+ rRect.Width(0);
+ }
+ if ( bZeroHeight )
+ {
+ rRect.Height(0);
+ }
+}
+
+/**
+ * Method to pixel-align rectangle for drawing graphic object
+ *
+ * Because we are drawing graphics from the left-top-corner in conjunction
+ * with size coordinates, these coordinates have to be calculated at a pixel
+ * level.
+ * Thus, we convert the rectangle to pixel and then convert to left-top-corner
+ * and then get size of pixel rectangle back to logic.
+ * This calculation is necessary, because there's a different between
+ * the conversion from logic to pixel of a normal rectangle with its left-top-
+ * and right-bottom-corner and the same conversion of the same rectangle
+ * with left-top-corner and size.
+ *
+ * NOTE: Call this method before each <GraphicObject.Draw(...)>
+*/
+void SwAlignGrfRect( SwRect *pGrfRect, const vcl::RenderContext &rOut )
+{
+ tools::Rectangle aPxRect = rOut.LogicToPixel( pGrfRect->SVRect() );
+ pGrfRect->Pos( rOut.PixelToLogic( aPxRect.TopLeft() ) );
+ pGrfRect->SSize( rOut.PixelToLogic( aPxRect.GetSize() ) );
+}
+
+static tools::Long lcl_AlignWidth( const tools::Long nWidth, SwPaintProperties const & properties )
+{
+ if ( nWidth )
+ {
+ const tools::Long nW = nWidth % properties.nSPixelSzW;
+
+ if ( !nW || nW > properties.nSHalfPixelSzW )
+ return std::max(tools::Long(1), nWidth - properties.nSHalfPixelSzW);
+ }
+ return nWidth;
+}
+
+static tools::Long lcl_AlignHeight( const tools::Long nHeight, SwPaintProperties const & properties )
+{
+ if ( nHeight )
+ {
+ const tools::Long nH = nHeight % properties.nSPixelSzH;
+
+ if ( !nH || nH > properties.nSHalfPixelSzH )
+ return std::max(tools::Long(1), nHeight - properties.nSHalfPixelSzH);
+ }
+ return nHeight;
+}
+
+/**
+ * Calculate PrtArea plus surrounding plus shadow
+ */
+static void lcl_CalcBorderRect( SwRect &rRect, const SwFrame *pFrame,
+ const SwBorderAttrs &rAttrs,
+ const bool bShadow,
+ SwPaintProperties const & properties)
+{
+ // Special handling for cell frames.
+ // The printing area of a cell frame is completely enclosed in the frame area
+ // and a cell frame has no shadow. Thus, for cell frames the calculated
+ // area equals the frame area.
+ // Notes: Borders of cell frames in R2L text direction will switch its side
+ // - left border is painted on the right; right border on the left.
+ // See <lcl_PaintLeftLine> and <lcl_PaintRightLine>.
+ if( pFrame->IsSctFrame() )
+ {
+ rRect = pFrame->getFramePrintArea();
+ rRect.Pos() += pFrame->getFrameArea().Pos();
+ }
+ else if ( pFrame->IsCellFrame() )
+ rRect = pFrame->getFrameArea();
+ else
+ {
+ rRect = pFrame->getFramePrintArea();
+ rRect.Pos() += pFrame->getFrameArea().Pos();
+
+ SwRectFn fnRect = pFrame->IsVertical() ? ( pFrame->IsVertLR() ? (pFrame->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ const SvxBoxItem &rBox = rAttrs.GetBox();
+ const bool bTop = 0 != (pFrame->*fnRect->fnGetTopMargin)();
+ if ( bTop || rBox.GetTop() )
+ {
+ SwTwips nDiff = rBox.GetTop() ?
+ rBox.CalcLineSpace( SvxBoxItemLine::TOP, /*bEvenIfNoLine=*/false, /*bAllowNegative=*/true ) :
+ rBox.GetDistance( SvxBoxItemLine::TOP );
+ if( nDiff )
+ (rRect.*fnRect->fnSubTop)( nDiff );
+ }
+
+ const bool bBottom = 0 != (pFrame->*fnRect->fnGetBottomMargin)();
+ if ( bBottom )
+ {
+ SwTwips nDiff = 0;
+ // #i29550#
+ if ( pFrame->IsTabFrame() &&
+ static_cast<const SwTabFrame*>(pFrame)->IsCollapsingBorders() )
+ {
+ // For collapsing borders, we have to add the height of
+ // the height of the last line
+ nDiff = static_cast<const SwTabFrame*>(pFrame)->GetBottomLineSize();
+ }
+ else
+ {
+ nDiff = rBox.GetBottom() ?
+ rBox.CalcLineSpace( SvxBoxItemLine::BOTTOM ) :
+ rBox.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ if( nDiff )
+ (rRect.*fnRect->fnAddBottom)( nDiff );
+ }
+
+ if ( rBox.GetLeft() )
+ (rRect.*fnRect->fnSubLeft)( rBox.CalcLineSpace( SvxBoxItemLine::LEFT ) );
+ else
+ (rRect.*fnRect->fnSubLeft)( rBox.GetDistance( SvxBoxItemLine::LEFT ) );
+
+ if ( rBox.GetRight() )
+ (rRect.*fnRect->fnAddRight)( rBox.CalcLineSpace( SvxBoxItemLine::RIGHT ) );
+ else
+ (rRect.*fnRect->fnAddRight)( rBox.GetDistance( SvxBoxItemLine::RIGHT ) );
+
+ if ( bShadow && rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
+ {
+ const SvxShadowItem &rShadow = rAttrs.GetShadow();
+ if ( bTop )
+ (rRect.*fnRect->fnSubTop)(rShadow.CalcShadowSpace(SvxShadowItemSide::TOP));
+ (rRect.*fnRect->fnSubLeft)(rShadow.CalcShadowSpace(SvxShadowItemSide::LEFT));
+ if ( bBottom )
+ (rRect.*fnRect->fnAddBottom)
+ (rShadow.CalcShadowSpace( SvxShadowItemSide::BOTTOM ));
+ (rRect.*fnRect->fnAddRight)(rShadow.CalcShadowSpace(SvxShadowItemSide::RIGHT));
+ }
+ }
+
+ ::SwAlignRect( rRect, properties.pSGlobalShell, properties.pSGlobalShell ? properties.pSGlobalShell->GetOut() : nullptr );
+}
+
+/**
+ * Extend left/right border/shadow rectangle to bottom of previous frame/to
+ * top of next frame, if border/shadow is joined with previous/next frame
+ */
+static void lcl_ExtendLeftAndRight( SwRect& _rRect,
+ const SwFrame& _rFrame,
+ const SwBorderAttrs& _rAttrs,
+ const SwRectFn& _rRectFn )
+{
+ if ( _rAttrs.JoinedWithPrev( _rFrame ) )
+ {
+ const SwFrame* pPrevFrame = _rFrame.GetPrev();
+ (_rRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
+ }
+ if ( _rAttrs.JoinedWithNext( _rFrame ) )
+ {
+ const SwFrame* pNextFrame = _rFrame.GetNext();
+ (_rRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
+ }
+}
+
+/// Returns a range suitable for subtraction when lcl_SubtractFlys() is used.
+/// Otherwise DrawFillAttributes() expands the clip path itself.
+static basegfx::B2DRange lcl_ShrinkFly(const SwRect& rRect)
+{
+ static MapMode aMapMode(MapUnit::MapTwip);
+ static const Size aSingleUnit = Application::GetDefaultDevice()->PixelToLogic(Size(1, 1), aMapMode);
+
+ double x1 = rRect.Left() + aSingleUnit.getWidth();
+ double y1 = rRect.Top() + aSingleUnit.getHeight();
+ double x2 = rRect.Right() - aSingleUnit.getWidth();
+ double y2 = rRect.Bottom() - aSingleUnit.getHeight();
+
+ return basegfx::B2DRange(x1, y1, x2, y2);
+}
+
+static void lcl_SubtractFlys( const SwFrame *pFrame, const SwPageFrame *pPage,
+ const SwRect &rRect, SwRegionRects &rRegion, basegfx::utils::B2DClipState& rClipState, SwPaintProperties const & rProperties)
+{
+ const SwSortedObjs& rObjs = *pPage->GetSortedObjs();
+ const SwFlyFrame* pSelfFly = pFrame->IsInFly() ? pFrame->FindFlyFrame() : gProp.pSRetoucheFly2;
+ if (!gProp.pSRetoucheFly)
+ gProp.pSRetoucheFly = gProp.pSRetoucheFly2;
+
+ for (size_t j = 0; (j < rObjs.size()) && !rRegion.empty(); ++j)
+ {
+ const SwAnchoredObject* pAnchoredObj = rObjs[j];
+ const SdrObject* pSdrObj = pAnchoredObj->GetDrawObj();
+
+ // Do not consider invisible objects
+ if (!pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(pSdrObj->GetLayer()))
+ continue;
+
+ const SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
+ if (!pFly)
+ continue;
+
+ if (pSelfFly == pFly || gProp.pSRetoucheFly == pFly || !rRect.Overlaps(pFly->getFrameArea()))
+ continue;
+
+ if (!pFly->GetFormat()->GetPrint().GetValue() &&
+ (OUTDEV_PRINTER == gProp.pSGlobalShell->GetOut()->GetOutDevType() ||
+ gProp.pSGlobalShell->IsPreview()))
+ continue;
+
+ const bool bLowerOfSelf = pSelfFly && pFly->IsLowerOf( pSelfFly );
+
+ //For character bound Flys only examine those Flys in which it is not
+ //anchored itself.
+ //Why only for character bound ones you may ask? It never makes sense to
+ //subtract frames in which it is anchored itself right?
+ if (pSelfFly && pSelfFly->IsLowerOf(pFly))
+ continue;
+
+ //Any why does it not apply for the RetoucheFly too?
+ if (gProp.pSRetoucheFly && gProp.pSRetoucheFly->IsLowerOf(pFly))
+ continue;
+
+#if OSL_DEBUG_LEVEL > 0
+ //Flys who are anchored inside their own one, must have a bigger OrdNum
+ //or be character bound.
+ if (pSelfFly && bLowerOfSelf)
+ {
+ OSL_ENSURE( pFly->IsFlyInContentFrame() ||
+ pSdrObj->GetOrdNumDirect() > pSelfFly->GetVirtDrawObj()->GetOrdNumDirect(),
+ "Fly with wrong z-Order" );
+ }
+#endif
+
+ bool bStopOnHell = true;
+ if (pSelfFly)
+ {
+ const SdrObject *pTmp = pSelfFly->GetVirtDrawObj();
+ if (pSdrObj->GetLayer() == pTmp->GetLayer())
+ {
+ if (pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect())
+ //In the same layer we only observe those that are above.
+ continue;
+ }
+ else
+ {
+ if (!bLowerOfSelf && !pFly->GetFormat()->GetOpaque().GetValue())
+ //From other layers we are only interested in non
+ //transparent ones or those that are internal
+ continue;
+ bStopOnHell = false;
+ }
+ }
+ if (gProp.pSRetoucheFly)
+ {
+ const SdrObject *pTmp = gProp.pSRetoucheFly->GetVirtDrawObj();
+ if ( pSdrObj->GetLayer() == pTmp->GetLayer() )
+ {
+ if ( pSdrObj->GetOrdNumDirect() < pTmp->GetOrdNumDirect() )
+ //In the same layer we only observe those that are above.
+ continue;
+ }
+ else
+ {
+ if (!pFly->IsLowerOf( gProp.pSRetoucheFly ) && !pFly->GetFormat()->GetOpaque().GetValue())
+ //From other layers we are only interested in non
+ //transparent ones or those that are internal
+ continue;
+ bStopOnHell = false;
+ }
+ }
+
+ //If the content of the Fly is transparent, we subtract it only if it's
+ //contained in the hell layer.
+ const IDocumentDrawModelAccess& rIDDMA = pFly->GetFormat()->getIDocumentDrawModelAccess();
+ bool bHell = pSdrObj->GetLayer() == rIDDMA.GetHellId();
+ if ( (bStopOnHell && bHell) ||
+ /// Change internal order of condition
+ /// first check "!bHell", then "..->Lower()" and "..->IsNoTextFrame()"
+ /// have not to be performed, if frame is in "Hell"
+ ( !bHell && pFly->Lower() && pFly->Lower()->IsNoTextFrame() &&
+ (static_cast<SwNoTextFrame const*>(pFly->Lower())->IsTransparent() ||
+ static_cast<SwNoTextFrame const*>(pFly->Lower())->HasAnimation() ||
+ pFly->GetFormat()->GetSurround().IsContour()
+ )
+ )
+ )
+ continue;
+
+ // Own if-statements for transparent background/shadow of fly frames
+ // in order to handle special conditions.
+ if (pFly->IsBackgroundTransparent())
+ {
+ // Background <pFly> is transparent drawn. Thus normally, its region
+ // have not to be subtracted from given region.
+ // But, if method is called for a fly frame and
+ // <pFly> is a direct lower of this fly frame and
+ // <pFly> inherites its transparent background brush from its parent,
+ // then <pFly> frame area have to be subtracted from given region.
+ // NOTE: Because in Status Quo transparent backgrounds can only be
+ // assigned to fly frames, the handle of this special case
+ // avoids drawing of transparent areas more than once, if
+ // a fly frame inherites a transparent background from its
+ // parent fly frame.
+ if (pFrame->IsFlyFrame() &&
+ (pFly->GetAnchorFrame()->FindFlyFrame() == pFrame) &&
+ pFly->GetFormat()->IsBackgroundBrushInherited()
+ )
+ {
+ SwRect aRect;
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
+ rRegion -= aRect;
+ rClipState.subtractRange(lcl_ShrinkFly(aRect));
+ continue;
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ if (bHell && pFly->GetAnchorFrame()->IsInFly())
+ {
+ //So the border won't get dismantled by the background of the other
+ //Fly.
+ SwRect aRect;
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(pFly) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ ::lcl_CalcBorderRect( aRect, pFly, rAttrs, true, rProperties );
+ rRegion -= aRect;
+ rClipState.subtractRange(lcl_ShrinkFly(aRect));
+ }
+ else
+ {
+ SwRect aRect( pFly->getFramePrintArea() );
+ aRect += pFly->getFrameArea().Pos();
+ rRegion -= aRect;
+ rClipState.subtractRange(lcl_ShrinkFly(aRect));
+ }
+ }
+ if (gProp.pSRetoucheFly == gProp.pSRetoucheFly2)
+ gProp.pSRetoucheFly = nullptr;
+}
+
+static void lcl_implDrawGraphicBackground(const SvxBrushItem& _rBackgrdBrush,
+ vcl::RenderContext& _rOut,
+ const SwRect& _rAlignedPaintRect,
+ const GraphicObject& _rGraphicObj,
+ SwPaintProperties const & properties)
+{
+ /// determine color of background
+ /// If color of background brush is not "no fill"/"auto fill" or
+ /// <SwPaintProperties.bSFlyMetafile> is set, use color of background brush, otherwise
+ /// use global retouche color.
+ const Color aColor( ( (_rBackgrdBrush.GetColor() != COL_TRANSPARENT) || properties.bSFlyMetafile )
+ ? _rBackgrdBrush.GetColor()
+ : aGlobalRetoucheColor );
+
+ /// determine, if background color have to be drawn transparent
+ /// and calculate transparency percent value
+ sal_Int8 nTransparencyPercent = 0;
+ bool bDrawTransparent = false;
+ if ( aColor.IsTransparent() )
+ /// background color is transparent --> draw transparent.
+ {
+ bDrawTransparent = true;
+ nTransparencyPercent = ((255 - aColor.GetAlpha())*100 + 0x7F)/0xFF;
+ }
+ else if ( (_rGraphicObj.GetAttr().IsTransparent()) &&
+ (_rBackgrdBrush.GetColor() == COL_TRANSPARENT) )
+ /// graphic is drawn transparent and background color is
+ /// "no fill"/"auto fill" --> draw transparent
+ {
+ bDrawTransparent = true;
+ nTransparencyPercent = 100 - (_rGraphicObj.GetAttr().GetAlpha() * 100 + 127) / 255;
+ }
+
+ if ( bDrawTransparent )
+ {
+ /// draw background transparent
+ if( _rOut.GetFillColor() != aColor.GetRGBColor() )
+ _rOut.SetFillColor( aColor.GetRGBColor() );
+ tools::PolyPolygon aPoly( _rAlignedPaintRect.SVRect() );
+ _rOut.DrawTransparent( aPoly, nTransparencyPercent );
+ }
+ else
+ {
+ /// draw background opaque
+ if ( _rOut.GetFillColor() != aColor )
+ _rOut.SetFillColor( aColor );
+ _rOut.DrawRect( _rAlignedPaintRect.SVRect() );
+ }
+}
+
+/**
+ * This is a local help method to draw a background for a graphic
+ *
+ * Under certain circumstances we have to draw a background for a graphic.
+ * This method takes care of the conditions and draws the background with the
+ * corresponding color.
+ * Method introduced for bug fix #103876# in order to optimize drawing tiled
+ * background graphics. Previously, this code was integrated in method
+ * <lcl_DrawGraphic>.
+ * Method implemented as an inline, checking the conditions and calling method
+ * method <lcl_implDrawGraphicBackground(..)> for the intrinsic drawing.
+ *
+ * @param _rBackgrdBrush
+ * background brush contain the color the background has to be drawn.
+ *
+ * @param _rOut
+ * output device the background has to be drawn in.
+ *
+ * @param _rAlignedPaintRect
+ * paint rectangle in the output device, which has to be drawn with the background.
+ * rectangle have to be aligned by method ::SwAlignRect
+ *
+ * @param _rGraphicObj
+ * graphic object, for which the background has to be drawn. Used for checking
+ * the transparency of its bitmap, its type and if the graphic is drawn transparent
+ *
+ * @param _bNumberingGraphic
+ * boolean indicating that graphic is used as a numbering.
+ *
+ * @param _bBackgrdAlreadyDrawn
+ * boolean (optional; default: false) indicating, if the background is already drawn.
+*/
+static void lcl_DrawGraphicBackground( const SvxBrushItem& _rBackgrdBrush,
+ OutputDevice& _rOut,
+ const SwRect& _rAlignedPaintRect,
+ const GraphicObject& _rGraphicObj,
+ bool _bNumberingGraphic,
+ SwPaintProperties const & properties,
+ bool _bBackgrdAlreadyDrawn = false)
+{
+ // draw background with background color, if
+ // (1) graphic is not used as a numbering AND
+ // (2) background is not already drawn AND
+ // (3) intrinsic graphic is transparent OR intrinsic graphic doesn't exists
+ if ( !_bNumberingGraphic &&
+ !_bBackgrdAlreadyDrawn &&
+ ( _rGraphicObj.IsTransparent() || _rGraphicObj.GetType() == GraphicType::NONE )
+ )
+ {
+ lcl_implDrawGraphicBackground( _rBackgrdBrush, _rOut, _rAlignedPaintRect, _rGraphicObj, properties );
+ }
+}
+
+/**
+ * NNOTE: the transparency of the background graphic is saved in
+ * SvxBrushItem.GetGraphicObject(<shell>).GetAttr().Set/GetTransparency()
+ * and is considered in the drawing of the graphic
+ *
+ * Thus, to provide transparent background graphic for text frames nothing
+ * has to be coded
+ *
+ * Use align rectangle for drawing graphic Pixel-align coordinates for
+ * drawing graphic
+ * Outsource code for drawing background of the graphic
+ * with a background color in method <lcl_DrawGraphicBackground>
+ *
+ * Also, change type of <bGrfNum> and <bClip> from <bool> to <bool>
+ */
+static void lcl_DrawGraphic( const SvxBrushItem& rBrush, vcl::RenderContext &rOutDev,
+ const SwViewShell &rSh, const SwRect &rGrf, const SwRect &rOut,
+ bool bGrfNum,
+ SwPaintProperties const & properties,
+ bool bBackgrdAlreadyDrawn )
+ // add parameter <bBackgrdAlreadyDrawn> to indicate
+ // that the background is already drawn.
+{
+ // Calculate align rectangle from parameter <rGrf> and use aligned
+ // rectangle <aAlignedGrfRect> in the following code
+ SwRect aAlignedGrfRect = rGrf;
+ ::SwAlignRect( aAlignedGrfRect, &rSh, &rOutDev );
+
+ // Change type from <bool> to <bool>.
+ const bool bNotInside = !rOut.Contains( aAlignedGrfRect );
+ if ( bNotInside )
+ {
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( rOut.SVRect() );
+ }
+
+ GraphicObject *pGrf = const_cast<GraphicObject*>(rBrush.GetGraphicObject());
+
+ OUString aOriginURL = pGrf->GetGraphic().getOriginURL();
+ if (pGrf->GetGraphic().GetType() == GraphicType::Default && !aOriginURL.isEmpty())
+ {
+ Graphic aGraphic = vcl::graphic::loadFromURL(aOriginURL);
+ pGrf->SetGraphic(aGraphic);
+ }
+
+ // Outsource drawing of background with a background color
+ ::lcl_DrawGraphicBackground( rBrush, rOutDev, aAlignedGrfRect, *pGrf, bGrfNum, properties, bBackgrdAlreadyDrawn );
+
+ // Because for drawing a graphic left-top-corner and size coordinates are
+ // used, these coordinates have to be determined on pixel level.
+ ::SwAlignGrfRect( &aAlignedGrfRect, rOutDev );
+
+ const basegfx::B2DHomMatrix aGraphicTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aAlignedGrfRect.Width(), aAlignedGrfRect.Height(),
+ aAlignedGrfRect.Left(), aAlignedGrfRect.Top()));
+
+ paintGraphicUsingPrimitivesHelper(
+ rOutDev,
+ *pGrf,
+ pGrf->GetAttr(),
+ aGraphicTransform,
+ OUString(),
+ OUString(),
+ OUString());
+
+ if ( bNotInside )
+ rOutDev.Pop();
+}
+
+bool DrawFillAttributes(
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const SwRect& rOriginalLayoutRect,
+ const SwRegionRects& rPaintRegion,
+ const basegfx::utils::B2DClipState& rClipState,
+ vcl::RenderContext& rOut)
+{
+ if(rFillAttributes && rFillAttributes->isUsed())
+ {
+ basegfx::B2DRange aPaintRange(
+ rPaintRegion.GetOrigin().Left(),
+ rPaintRegion.GetOrigin().Top(),
+ rPaintRegion.GetOrigin().Right(),
+ rPaintRegion.GetOrigin().Bottom());
+
+ if (!aPaintRange.isEmpty() &&
+ !rPaintRegion.empty() &&
+ !basegfx::fTools::equalZero(aPaintRange.getWidth()) &&
+ !basegfx::fTools::equalZero(aPaintRange.getHeight()))
+ {
+ // need to expand for correct AAed and non-AAed visualization as primitive.
+ // This must probably be removed again when we will be able to get all Writer visualization
+ // as primitives and Writer prepares all it's stuff in high precision coordinates (also
+ // needs to avoid moving boundaries around to better show overlapping stuff...)
+ if(SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // if AAed in principle expand by 0.5 in all directions. Since painting edges of
+ // AAed regions does not add to no transparence (0.5 opacity covered by 0.5 opacity
+ // is not full opacity but 0.75 opacity) we need some overlap here to avoid paint
+ // artifacts. Checked experimentally - a little bit more in Y is needed, probably
+ // due to still existing integer alignment and crunching in writer.
+ static const double fExpandX = 0.55;
+ static const double fExpandY = 0.70;
+ const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(fExpandX, fExpandY));
+
+ aPaintRange.expand(aPaintRange.getMinimum() - aSingleUnit);
+ aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
+ }
+ else
+ {
+ // if not AAed expand by one unit to bottom right due to the missing unit
+ // from SwRect/Rectangle integer handling
+ const basegfx::B2DVector aSingleUnit(rOut.GetInverseViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+
+ aPaintRange.expand(aPaintRange.getMaximum() + aSingleUnit);
+ }
+
+ const basegfx::B2DRange aDefineRange(
+ rOriginalLayoutRect.Left(),
+ rOriginalLayoutRect.Top(),
+ rOriginalLayoutRect.Right(),
+ rOriginalLayoutRect.Bottom());
+
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rFillAttributes->getPrimitive2DSequence(
+ aPaintRange,
+ aDefineRange);
+
+ if(rSequence.size())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer const*
+ pPrimitives(&rSequence);
+ drawinglayer::primitive2d::Primitive2DContainer primitives;
+ // tdf#86578 the awful lcl_SubtractFlys hack
+ if (rPaintRegion.size() > 1 || rPaintRegion[0] != rPaintRegion.GetOrigin())
+ {
+ basegfx::B2DPolyPolygon const& maskRegion(rClipState.getClipPoly());
+ primitives.resize(1);
+ primitives[0] = new drawinglayer::primitive2d::MaskPrimitive2D(
+ maskRegion, drawinglayer::primitive2d::Primitive2DContainer(rSequence));
+ pPrimitives = &primitives;
+ }
+ assert(pPrimitives && pPrimitives->size());
+
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
+ basegfx::B2DHomMatrix(),
+ rOut.GetViewTransformation(),
+ aPaintRange,
+ nullptr,
+ 0.0);
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ rOut,
+ aViewInformation2D) );
+ pProcessor->process(*pPrimitives);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void DrawGraphic(
+ const SvxBrushItem *pBrush,
+ vcl::RenderContext &rOutDev,
+ const SwRect &rOrg,
+ const SwRect &rOut,
+ const sal_uInt8 nGrfNum,
+ const bool bConsiderBackgroundTransparency )
+ // Add 6th parameter to indicate that method should
+ // consider background transparency, saved in the color of the brush item
+{
+ SwViewShell &rSh = *gProp.pSGlobalShell;
+ bool bReplaceGrfNum = GRFNUM_REPLACE == nGrfNum;
+ bool bGrfNum = GRFNUM_NO != nGrfNum;
+ Size aGrfSize;
+ SvxGraphicPosition ePos = GPOS_NONE;
+ if( pBrush && !bReplaceGrfNum )
+ {
+ if( rSh.GetViewOptions()->IsGraphic() )
+ {
+ OUString referer;
+ SfxObjectShell * sh = rSh.GetDoc()->GetPersist();
+ if (sh != nullptr && sh->HasName()) {
+ referer = sh->GetMedium()->GetName();
+ }
+ const Graphic* pGrf = pBrush->GetGraphic(referer);
+ if( pGrf && GraphicType::NONE != pGrf->GetType() )
+ {
+ ePos = pBrush->GetGraphicPos();
+ if( pGrf->IsSupportedGraphic() )
+ // don't the use the specific output device! Bug 94802
+ aGrfSize = ::GetGraphicSizeTwip( *pGrf, nullptr );
+ }
+ }
+ else
+ bReplaceGrfNum = bGrfNum;
+ }
+
+ SwRect aGrf;
+ aGrf.SSize( aGrfSize );
+ bool bDraw = true;
+ bool bRetouche = true;
+ switch ( ePos )
+ {
+ case GPOS_LT:
+ aGrf.Pos() = rOrg.Pos();
+ break;
+
+ case GPOS_MT:
+ aGrf.Pos().setY( rOrg.Top() );
+ aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
+ break;
+
+ case GPOS_RT:
+ aGrf.Pos().setY( rOrg.Top() );
+ aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LM:
+ aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
+ aGrf.Pos().setX( rOrg.Left() );
+ break;
+
+ case GPOS_MM:
+ aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
+ aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
+ break;
+
+ case GPOS_RM:
+ aGrf.Pos().setY( rOrg.Top() + rOrg.Height()/2 - aGrfSize.Height()/2 );
+ aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_LB:
+ aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
+ aGrf.Pos().setX( rOrg.Left() );
+ break;
+
+ case GPOS_MB:
+ aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
+ aGrf.Pos().setX( rOrg.Left() + rOrg.Width()/2 - aGrfSize.Width()/2 );
+ break;
+
+ case GPOS_RB:
+ aGrf.Pos().setY( rOrg.Bottom() - aGrfSize.Height() );
+ aGrf.Pos().setX( rOrg.Right() - aGrfSize.Width() );
+ break;
+
+ case GPOS_AREA:
+ aGrf = rOrg;
+ // Despite the fact that the background graphic has to fill the complete
+ // area, we already checked, whether the graphic will completely fill out
+ // the region the <rOut> that is to be painted. Thus, nothing has to be
+ // touched again.
+ // E.g. this is the case for a Fly Frame without a background
+ // brush positioned on the border of the page which inherited the background
+ // brush from the page.
+ bRetouche = !rOut.Contains( aGrf );
+ break;
+
+ case GPOS_TILED:
+ {
+ // draw background of tiled graphic before drawing tiled graphic in loop
+ // determine graphic object
+ GraphicObject* pGraphicObj = const_cast< GraphicObject* >(pBrush->GetGraphicObject());
+ // calculate aligned paint rectangle
+ SwRect aAlignedPaintRect = rOut;
+ ::SwAlignRect( aAlignedPaintRect, &rSh, &rOutDev );
+ // draw background color for aligned paint rectangle
+ lcl_DrawGraphicBackground( *pBrush, rOutDev, aAlignedPaintRect, *pGraphicObj, bGrfNum, gProp );
+
+ // set left-top-corner of background graphic to left-top-corner of the
+ // area, from which the background brush is determined.
+ aGrf.Pos() = rOrg.Pos();
+ // setup clipping at output device
+ rOutDev.Push( vcl::PushFlags::CLIPREGION );
+ rOutDev.IntersectClipRegion( rOut.SVRect() );
+ // use new method <GraphicObject::DrawTiled(::)>
+ {
+ // calculate paint offset
+ Point aPaintOffset( aAlignedPaintRect.Pos() - aGrf.Pos() );
+ // draw background graphic tiled for aligned paint rectangle
+ // #i42643#
+ // For PDF export, every draw operation for bitmaps takes a
+ // noticeable amount of place (~50 characters). Thus, optimize
+ // between tile bitmap size and number of drawing operations here.
+
+ // A_out
+ // n_chars = k1 * ---------- + k2 * A_bitmap
+ // A_bitmap
+
+ // minimum n_chars is obtained for (derive for A_bitmap,
+ // set to 0, take positive solution):
+ // k1
+ // A_bitmap = Sqrt( ---- A_out )
+ // k2
+
+ // where k1 is the number of chars per draw operation, and
+ // k2 is the number of chars per bitmap pixel.
+ // This is approximately 50 and 7 for current PDF writer, respectively.
+
+ const double k1( 50 );
+ const double k2( 7 );
+ const Size aSize( aAlignedPaintRect.SSize() );
+ const double Abitmap( k1/k2 * static_cast<double>(aSize.Width())*aSize.Height() );
+
+ pGraphicObj->DrawTiled( rOutDev,
+ aAlignedPaintRect.SVRect(),
+ aGrf.SSize(),
+ Size( aPaintOffset.X(), aPaintOffset.Y() ),
+ std::max( 128, static_cast<int>( sqrt(sqrt( Abitmap)) + .5 ) ) );
+ }
+ // reset clipping at output device
+ rOutDev.Pop();
+ // set <bDraw> and <bRetouche> to false, indicating that background
+ // graphic and background are already drawn.
+ bDraw = bRetouche = false;
+ }
+ break;
+
+ case GPOS_NONE:
+ bDraw = false;
+ break;
+
+ default: OSL_ENSURE( false, "new Graphic position?" );
+ }
+
+ /// init variable <bGrfBackgrdAlreadDrawn> to indicate, if background of
+ /// graphic is already drawn or not.
+ bool bGrfBackgrdAlreadyDrawn = false;
+ if ( bRetouche )
+ {
+ rOutDev.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rOutDev.SetLineColor();
+
+ // check, if an existing background graphic (not filling the complete
+ // background) is transparent drawn and the background color is
+ // "no fill" respectively "auto fill", if background transparency
+ // has to be considered.
+ // If YES, memorize transparency of background graphic.
+ // check also, if background graphic bitmap is transparent.
+ bool bTransparentGrfWithNoFillBackgrd = false;
+ sal_Int32 nGrfTransparency = 0;
+ bool bGrfIsTransparent = false;
+ if ( (ePos != GPOS_NONE) &&
+ (ePos != GPOS_TILED) && (ePos != GPOS_AREA)
+ )
+ {
+ GraphicObject *pGrf = const_cast<GraphicObject*>(pBrush->GetGraphicObject());
+ if ( bConsiderBackgroundTransparency )
+ {
+ GraphicAttr aGrfAttr = pGrf->GetAttr();
+ if ( (aGrfAttr.IsTransparent()) &&
+ (pBrush->GetColor() == COL_TRANSPARENT)
+ )
+ {
+ bTransparentGrfWithNoFillBackgrd = true;
+ nGrfTransparency = 255 - aGrfAttr.GetAlpha();
+ }
+ }
+ if ( pGrf->IsTransparent() )
+ {
+ bGrfIsTransparent = true;
+ }
+ }
+
+ // to get color of brush, check background color against COL_TRANSPARENT ("no fill"/"auto fill")
+ // instead of checking, if transparency is not set.
+ const Color aColor( pBrush &&
+ ( (pBrush->GetColor() != COL_TRANSPARENT) ||
+ gProp.bSFlyMetafile )
+ ? pBrush->GetColor()
+ : aGlobalRetoucheColor );
+
+ // determine, if background region have to be
+ // drawn transparent.
+ // background region has to be drawn transparent, if
+ // background transparency have to be considered
+ // AND
+ // ( background color is transparent OR
+ // background graphic is transparent and background color is "no fill"
+ // )
+
+ enum DrawStyle {
+ Default,
+ Transparent,
+ } eDrawStyle = Default;
+
+ if (bConsiderBackgroundTransparency &&
+ ( ( aColor.IsTransparent()) ||
+ bTransparentGrfWithNoFillBackgrd ) )
+ {
+ eDrawStyle = Transparent;
+ }
+
+ // #i75614# reset draw mode in high contrast mode in order to get fill color set
+ const DrawModeFlags nOldDrawMode = rOutDev.GetDrawMode();
+ if ( gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ rOutDev.SetDrawMode( DrawModeFlags::Default );
+ }
+
+ // If background region has to be drawn transparent, set only the RGB values of the background color as
+ // the fill color for the output device.
+ switch (eDrawStyle)
+ {
+ case Transparent:
+ {
+ if( rOutDev.GetFillColor() != aColor.GetRGBColor() )
+ rOutDev.SetFillColor( aColor.GetRGBColor() );
+ break;
+ }
+ default:
+ {
+ if( rOutDev.GetFillColor() != aColor )
+ rOutDev.SetFillColor( aColor );
+ break;
+ }
+ }
+
+ // #i75614#
+ // restore draw mode
+ rOutDev.SetDrawMode( nOldDrawMode );
+
+ switch (eDrawStyle)
+ {
+ case Transparent:
+ {
+ // background region have to be drawn transparent.
+ // Thus, create a poly-polygon from the region and draw it with
+ // the corresponding transparency percent.
+ tools::PolyPolygon aDrawPoly( rOut.SVRect() );
+ if ( aGrf.HasArea() )
+ {
+ if ( !bGrfIsTransparent )
+ {
+ // subtract area of background graphic from draw area
+ // Consider only that part of the graphic area that is overlapping with draw area.
+ SwRect aTmpGrf = aGrf;
+ aTmpGrf.Intersection( rOut );
+ if ( aTmpGrf.HasArea() )
+ {
+ tools::Polygon aGrfPoly( aTmpGrf.SVRect() );
+ aDrawPoly.Insert( aGrfPoly );
+ }
+ }
+ else
+ bGrfBackgrdAlreadyDrawn = true;
+ }
+ // calculate transparency percent:
+ // ( <transparency value[0x01..0xFF]>*100 + 0x7F ) / 0xFF
+ // If there is a background graphic with a background color "no fill"/"auto fill",
+ // the transparency value is taken from the background graphic,
+ // otherwise take the transparency value from the color.
+ sal_Int8 nTransparencyPercent = static_cast<sal_Int8>(
+ (( bTransparentGrfWithNoFillBackgrd ? nGrfTransparency : (255 - aColor.GetAlpha())
+ )*100 + 0x7F)/0xFF);
+ // draw poly-polygon transparent
+ rOutDev.DrawTransparent( aDrawPoly, nTransparencyPercent );
+
+ break;
+ }
+ case Default:
+ default:
+ {
+ SwRegionRects aRegion( rOut, 4 );
+ if ( !bGrfIsTransparent )
+ aRegion -= aGrf;
+ else
+ bGrfBackgrdAlreadyDrawn = true;
+ // loop rectangles of background region, which has to be drawn
+ for( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ rOutDev.DrawRect( aRegion[i].SVRect() );
+ }
+ }
+ }
+ rOutDev.Pop();
+ }
+
+ if( bDraw && aGrf.Overlaps( rOut ) )
+ lcl_DrawGraphic( *pBrush, rOutDev, rSh, aGrf, rOut, bGrfNum, gProp,
+ bGrfBackgrdAlreadyDrawn );
+
+ if( bReplaceGrfNum )
+ {
+ const BitmapEx& rBmp = rSh.GetReplacementBitmap(false);
+ vcl::Font aTmp( rOutDev.GetFont() );
+ Graphic::DrawEx(rOutDev, OUString(), aTmp, rBmp, rOrg.Pos(), rOrg.SSize());
+ }
+}
+
+/**
+ * Local helper for SwRootFrame::PaintSwFrame(..) - Adjust given rectangle to pixel size
+ *
+ * By OD at 27.09.2002 for #103636#
+ * In order to avoid paint errors caused by multiple alignments (e.g. ::SwAlignRect(..))
+ * and other changes to the to be painted rectangle, this method is called for the
+ * rectangle to be painted in order to adjust it to the pixel it is overlapping
+*/
+static void lcl_AdjustRectToPixelSize( SwRect& io_aSwRect, const vcl::RenderContext &aOut )
+{
+ // local constant object of class <Size> to determine number of Twips
+ // representing a pixel.
+ const Size aTwipToPxSize( aOut.PixelToLogic( Size( 1,1 )) );
+
+ // local object of class <Rectangle> in Twip coordinates
+ // calculated from given rectangle aligned to pixel centers.
+ const tools::Rectangle aPxCenterRect = aOut.PixelToLogic(
+ aOut.LogicToPixel( io_aSwRect.SVRect() ) );
+
+ // local constant object of class <Rectangle> representing given rectangle
+ // in pixel.
+ const tools::Rectangle aOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
+
+ // calculate adjusted rectangle from pixel centered rectangle.
+ // Due to rounding differences <aPxCenterRect> doesn't exactly represents
+ // the Twip-centers. Thus, adjust borders by half of pixel width/height plus 1.
+ // Afterwards, adjust calculated Twip-positions of the all borders.
+ tools::Rectangle aSizedRect = aPxCenterRect;
+ aSizedRect.AdjustLeft( -(aTwipToPxSize.Width()/2 + 1) );
+ aSizedRect.AdjustRight( aTwipToPxSize.Width()/2 + 1 );
+ aSizedRect.AdjustTop( -(aTwipToPxSize.Height()/2 + 1) );
+ aSizedRect.AdjustBottom(aTwipToPxSize.Height()/2 + 1);
+
+ // adjust left()
+ while ( aOut.LogicToPixel(aSizedRect).Left() < aOrgPxRect.Left() )
+ {
+ aSizedRect.AdjustLeft( 1 );
+ }
+ // adjust right()
+ while ( aOut.LogicToPixel(aSizedRect).Right() > aOrgPxRect.Right() )
+ {
+ aSizedRect.AdjustRight( -1 );
+ }
+ // adjust top()
+ while ( aOut.LogicToPixel(aSizedRect).Top() < aOrgPxRect.Top() )
+ {
+ aSizedRect.AdjustTop( 1 );
+ }
+ // adjust bottom()
+ while ( aOut.LogicToPixel(aSizedRect).Bottom() > aOrgPxRect.Bottom() )
+ {
+ aSizedRect.AdjustBottom( -1 );
+ }
+
+ io_aSwRect = SwRect( aSizedRect );
+
+#if OSL_DEBUG_LEVEL > 0
+ tools::Rectangle aTestOrgPxRect = aOut.LogicToPixel( io_aSwRect.SVRect() );
+ tools::Rectangle aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect == aTestNewPxRect,
+ "Error in lcl_AlignRectToPixelSize(..): Adjusted rectangle has incorrect position or size");
+ // check Left()
+ aSizedRect.AdjustLeft( -1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Left() >= (aTestNewPxRect.Left()+1),
+ "Error in lcl_AlignRectToPixelSize(..): Left() not correct adjusted");
+ aSizedRect.AdjustLeft( 1 );
+ // check Right()
+ aSizedRect.AdjustRight( 1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Right() <= (aTestNewPxRect.Right()-1),
+ "Error in lcl_AlignRectToPixelSize(..): Right() not correct adjusted");
+ aSizedRect.AdjustRight( -1 );
+ // check Top()
+ aSizedRect.AdjustTop( -1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Top() >= (aTestNewPxRect.Top()+1),
+ "Error in lcl_AlignRectToPixelSize(..): Top() not correct adjusted");
+ aSizedRect.AdjustTop( 1 );
+ // check Bottom()
+ aSizedRect.AdjustBottom( 1 );
+ aTestNewPxRect = aOut.LogicToPixel( aSizedRect );
+ OSL_ENSURE( aTestOrgPxRect.Bottom() <= (aTestNewPxRect.Bottom()-1),
+ "Error in lcl_AlignRectToPixelSize(..): Bottom() not correct adjusted");
+ aSizedRect.AdjustBottom( -1 );
+#endif
+}
+
+// FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES START
+
+namespace {
+
+struct SwLineEntry
+{
+ SwTwips mnKey;
+ SwTwips mnStartPos;
+ SwTwips mnEndPos;
+ SwTwips mnLimitedEndPos;
+ bool mbOuter;
+
+ svx::frame::Style maAttribute;
+
+ enum OverlapType { NO_OVERLAP, OVERLAP1, OVERLAP2, OVERLAP3 };
+
+ enum class VerticalType { LEFT, RIGHT };
+
+public:
+ SwLineEntry( SwTwips nKey,
+ SwTwips nStartPos,
+ SwTwips nEndPos,
+ bool bOuter,
+ const svx::frame::Style& rAttribute );
+
+ OverlapType Overlaps( const SwLineEntry& rComp ) const;
+
+ /**
+ * Assuming that this entry is for a Word-style covering cell and the border matching eType is
+ * set, limit the end position of this border in case covered cells have no borders set.
+ */
+ void LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType);
+};
+
+}
+
+SwLineEntry::SwLineEntry( SwTwips nKey,
+ SwTwips nStartPos,
+ SwTwips nEndPos,
+ bool bOuter,
+ const svx::frame::Style& rAttribute )
+ : mnKey( nKey ),
+ mnStartPos( nStartPos ),
+ mnEndPos( nEndPos ),
+ mnLimitedEndPos(0),
+ mbOuter(bOuter),
+ maAttribute( rAttribute )
+{
+}
+
+/*
+
+ 1. ---------- rOld
+ ---------- rNew
+
+ 2. ---------- rOld
+ ------------- rNew
+
+ 3. ------- rOld
+ ------------- rNew
+
+ 4. ------------- rOld
+ ---------- rNew
+
+ 5. ---------- rOld
+ ---- rNew
+
+ 6. ---------- rOld
+ ---------- rNew
+
+ 7. ------------- rOld
+ ---------- rNew
+
+ 8. ---------- rOld
+ ------------- rNew
+
+ 9. ---------- rOld
+ ---------- rNew
+*/
+
+SwLineEntry::OverlapType SwLineEntry::Overlaps( const SwLineEntry& rNew ) const
+{
+ SwLineEntry::OverlapType eRet = OVERLAP3;
+
+ if ( mnStartPos >= rNew.mnEndPos || mnEndPos <= rNew.mnStartPos )
+ eRet = NO_OVERLAP;
+
+ // 1, 2, 3
+ else if ( mnEndPos < rNew.mnEndPos )
+ eRet = OVERLAP1;
+
+ // 4, 5, 6, 7
+ else if (mnStartPos <= rNew.mnStartPos)
+ eRet = OVERLAP2;
+
+ // 8, 9
+ return eRet;
+}
+
+void SwLineEntry::LimitVerticalEndPos(const SwFrame& rFrame, VerticalType eType)
+{
+ if (!rFrame.IsCellFrame())
+ {
+ return;
+ }
+
+ const auto& rCellFrame = static_cast<const SwCellFrame&>(rFrame);
+ std::vector<const SwCellFrame*> aCoveredCells = rCellFrame.GetCoveredCells();
+ // Iterate in reverse order, so we can stop at the first cell that has a border. This can
+ // determine what is the minimal end position that is safe to use as a limit.
+ for (auto it = aCoveredCells.rbegin(); it != aCoveredCells.rend(); ++it)
+ {
+ const SwCellFrame* pCoveredCell = *it;
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCoveredCell );
+ const SwBorderAttrs& rAttrs = *aAccess.Get();
+ const SvxBoxItem& rBox = rAttrs.GetBox();
+ if (eType == VerticalType::LEFT && rBox.GetLeft())
+ {
+ break;
+ }
+
+ if (eType == VerticalType::RIGHT && rBox.GetRight())
+ {
+ break;
+ }
+
+ mnLimitedEndPos = pCoveredCell->getFrameArea().Top();
+ }
+}
+
+namespace {
+
+struct lt_SwLineEntry
+{
+ bool operator()( const SwLineEntry& e1, const SwLineEntry& e2 ) const
+ {
+ return e1.mnStartPos < e2.mnStartPos;
+ }
+};
+
+}
+
+typedef std::set< SwLineEntry, lt_SwLineEntry > SwLineEntrySet;
+typedef std::map< SwTwips, SwLineEntrySet > SwLineEntryMap;
+
+namespace {
+
+class SwTabFramePainter
+{
+ SwLineEntryMap maVertLines;
+ SwLineEntryMap maHoriLines;
+ const SwTabFrame& mrTabFrame;
+
+ void Insert( SwLineEntry&, bool bHori );
+ void Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect &rPaintArea);
+ void HandleFrame(const SwLayoutFrame& rFrame, const SwRect& rPaintArea);
+ void FindStylesForLine( Point&,
+ Point&,
+ svx::frame::Style*,
+ bool bHori,
+ bool bOuter ) const;
+
+public:
+ explicit SwTabFramePainter( const SwTabFrame& rTabFrame );
+
+ void PaintLines( OutputDevice& rDev, const SwRect& rRect ) const;
+};
+
+}
+
+SwTabFramePainter::SwTabFramePainter( const SwTabFrame& rTabFrame )
+ : mrTabFrame( rTabFrame )
+{
+ SwRect aPaintArea = rTabFrame.GetUpper()->GetPaintArea();
+ HandleFrame(rTabFrame, aPaintArea);
+}
+
+void SwTabFramePainter::HandleFrame(const SwLayoutFrame& rLayoutFrame, const SwRect& rPaintArea)
+{
+ // Add border lines of cell frames. Skip covered cells. Skip cells
+ // in special row span row, which do not have a negative row span:
+ if ( rLayoutFrame.IsCellFrame() && !rLayoutFrame.IsCoveredCell() )
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(&rLayoutFrame);
+ const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pThisCell->GetUpper());
+ const tools::Long nRowSpan = pThisCell->GetTabBox()->getRowSpan();
+ if ( !pRowFrame->IsRowSpanLine() || nRowSpan > 1 || nRowSpan < -1 )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), &rLayoutFrame );
+ const SwBorderAttrs& rAttrs = *aAccess.Get();
+ const SvxBoxItem& rBox = rAttrs.GetBox();
+ Insert(rLayoutFrame, rBox, rPaintArea);
+ }
+ }
+
+ // Recurse into lower layout frames, but do not recurse into lower tabframes.
+ const SwFrame* pLower = rLayoutFrame.Lower();
+ while ( pLower )
+ {
+ if (pLower->IsLayoutFrame() && !pLower->IsTabFrame())
+ {
+ const SwLayoutFrame* pLowerLayFrame = static_cast<const SwLayoutFrame*>(pLower);
+ HandleFrame(*pLowerLayFrame, rPaintArea);
+ }
+ pLower = pLower->GetNext();
+ }
+}
+
+void SwTabFramePainter::PaintLines(OutputDevice& rDev, const SwRect& rRect) const
+{
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, rDev );
+
+ SwLineEntryMap::const_iterator aIter = maHoriLines.begin();
+ bool bHori = true;
+
+ // color for subsidiary lines:
+ const Color& rCol( SwViewOption::GetTableBoundariesColor() );
+
+ // high contrast mode:
+ // overrides the color of non-subsidiary lines.
+ const Color* pHCColor = nullptr;
+ DrawModeFlags nOldDrawMode = rDev.GetDrawMode();
+ if( gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pHCColor = &SwViewOption::GetFontColor();
+ rDev.SetDrawMode( DrawModeFlags::Default );
+ }
+
+ const SwFrame* pUpper = mrTabFrame.GetUpper();
+ SwRect aUpper( pUpper->getFramePrintArea() );
+ aUpper.Pos() += pUpper->getFrameArea().Pos();
+ SwRect aUpperAligned( aUpper );
+ ::SwAlignRect( aUpperAligned, gProp.pSGlobalShell, &rDev );
+
+ // prepare SdrFrameBorderDataVector
+ std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
+ std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
+
+ while ( true )
+ {
+ if ( bHori && aIter == maHoriLines.end() )
+ {
+ aIter = maVertLines.begin();
+ bHori = false;
+ }
+
+ if ( !bHori && aIter == maVertLines.end() )
+ break;
+
+ const SwLineEntrySet& rEntrySet = (*aIter).second;
+ for (const SwLineEntry& rEntry : rEntrySet)
+ {
+ const svx::frame::Style& rEntryStyle( rEntry.maAttribute );
+
+ Point aStart, aEnd;
+ if ( bHori )
+ {
+ aStart.setX( rEntry.mnStartPos );
+ aStart.setY( rEntry.mnKey );
+ aEnd.setX( rEntry.mnEndPos );
+ aEnd.setY( rEntry.mnKey );
+ }
+ else
+ {
+ aStart.setX( rEntry.mnKey );
+ aStart.setY( rEntry.mnStartPos );
+ aEnd.setX( rEntry.mnKey );
+ aEnd.setY( rEntry.mnEndPos );
+ }
+
+ svx::frame::Style aStyles[ 7 ];
+ aStyles[ 0 ] = rEntryStyle;
+ FindStylesForLine(aStart, aEnd, aStyles, bHori, rEntry.mbOuter);
+
+ if (!bHori && rEntry.mnLimitedEndPos)
+ {
+ aEnd.setY(rEntry.mnLimitedEndPos);
+ }
+
+ SwRect aRepaintRect( aStart, aEnd );
+
+ // the repaint rectangle has to be moved a bit for the centered lines:
+ SwTwips nRepaintRectSize = !rEntryStyle.GetWidth() ? 1 : rEntryStyle.GetWidth();
+ if ( bHori )
+ {
+ aRepaintRect.Height( 2 * nRepaintRectSize );
+ aRepaintRect.Pos().AdjustY( -nRepaintRectSize );
+
+ // To decide on visibility it is also necessary to expand the RepaintRect
+ // to left/right according existing BorderLine overlap matchings, else there
+ // will be repaint errors when scrolling in e.t TripleLine BorderLines.
+ // aStyles[1] == aLFromT, aStyles[3] == aLFromB, aStyles[4] == aRFromT, aStyles[6] == aRFromB
+ if(aStyles[1].IsUsed() || aStyles[3].IsUsed() || aStyles[4].IsUsed() || aStyles[6].IsUsed())
+ {
+ const double fLineWidthMaxLeft(std::max(aStyles[1].GetWidth(), aStyles[3].GetWidth()));
+ const double fLineWidthMaxRight(std::max(aStyles[4].GetWidth(), aStyles[6].GetWidth()));
+ aRepaintRect.Width(aRepaintRect.Width() + (fLineWidthMaxLeft + fLineWidthMaxRight));
+ aRepaintRect.Pos().AdjustX( -fLineWidthMaxLeft );
+ }
+ }
+ else
+ {
+ aRepaintRect.Width( 2 * nRepaintRectSize );
+ aRepaintRect.Pos().AdjustX( -nRepaintRectSize );
+
+ // Accordingly to horizontal case, but for top/bottom
+ // aStyles[3] == aTFromR, aStyles[1] == aTFromL, aStyles[6] == aBFromR, aStyles[4] == aBFromL
+ if(aStyles[3].IsUsed() || aStyles[1].IsUsed() || aStyles[6].IsUsed() || aStyles[4].IsUsed())
+ {
+ const double fLineWidthMaxTop(std::max(aStyles[3].GetWidth(), aStyles[1].GetWidth()));
+ const double fLineWidthMaxBottom(std::max(aStyles[6].GetWidth(), aStyles[4].GetWidth()));
+ aRepaintRect.Height(aRepaintRect.Height() + (fLineWidthMaxTop + fLineWidthMaxBottom));
+ aRepaintRect.Pos().AdjustY( -fLineWidthMaxTop );
+ }
+ }
+
+ if (!rRect.Overlaps(aRepaintRect))
+ {
+ continue;
+ }
+
+ // subsidiary lines
+ const Color* pTmpColor = nullptr;
+ if (0 == aStyles[ 0 ].GetWidth())
+ {
+ if (isTableBoundariesEnabled() && gProp.pSGlobalShell->GetWin())
+ aStyles[ 0 ].Set( rCol, rCol, rCol, false, 1, 0, 0 );
+ else
+ aStyles[0].SetType(SvxBorderLineStyle::NONE);
+ }
+ else
+ pTmpColor = pHCColor;
+
+ // The (twip) positions will be adjusted to meet these requirements:
+ // 1. The y coordinates are located in the middle of the pixel grid
+ // 2. The x coordinated are located at the beginning of the pixel grid
+ // This is done, because the horizontal lines are painted "at
+ // beginning", whereas the vertical lines are painted "centered".
+ // By making the line sizes a multiple of one pixel size, we can
+ // assure that all lines having the same twip size have the same
+ // pixel size, independent of their position on the screen.
+ Point aPaintStart = rDev.PixelToLogic( rDev.LogicToPixel(aStart) );
+ Point aPaintEnd = rDev.PixelToLogic( rDev.LogicToPixel(aEnd) );
+
+ if (gProp.pSGlobalShell->GetWin())
+ {
+ // The table borders do not use SwAlignRect, but all the other frames do.
+ // Therefore we tweak the outer borders a bit to achieve that the outer
+ // borders match the subsidiary lines of the upper:
+ if (aStart.X() == aUpper.Left())
+ aPaintStart.setX( aUpperAligned.Left() );
+ else if (aStart.X() == aUpper.Right_())
+ aPaintStart.setX( aUpperAligned.Right_() );
+ if (aStart.Y() == aUpper.Top())
+ aPaintStart.setY( aUpperAligned.Top() );
+ else if (aStart.Y() == aUpper.Bottom_())
+ aPaintStart.setY( aUpperAligned.Bottom_() );
+
+ if (aEnd.X() == aUpper.Left())
+ aPaintEnd.setX( aUpperAligned.Left() );
+ else if (aEnd.X() == aUpper.Right_())
+ aPaintEnd.setX( aUpperAligned.Right_() );
+ if (aEnd.Y() == aUpper.Top())
+ aPaintEnd.setY( aUpperAligned.Top() );
+ else if (aEnd.Y() == aUpper.Bottom_())
+ aPaintEnd.setY( aUpperAligned.Bottom_() );
+ }
+
+ if(aStyles[0].IsUsed())
+ {
+ if (bHori)
+ {
+ const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
+ const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
+
+ if(!aX.equalZero())
+ {
+ const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
+ aData->emplace_back(
+ aOrigin,
+ aX,
+ aStyles[0],
+ pTmpColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ rInstance.addSdrConnectStyleData(true, aStyles[1], -aY, true); // aLFromT
+ rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aLFromL
+ rInstance.addSdrConnectStyleData(true, aStyles[3], aY, false); // aLFromB
+
+ rInstance.addSdrConnectStyleData(false, aStyles[4], -aY, true); // aRFromT
+ rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aRFromR
+ rInstance.addSdrConnectStyleData(false, aStyles[6], aY, false); // aRFromB
+ }
+ }
+ else // vertical
+ {
+ const basegfx::B2DPoint aOrigin(aPaintStart.X(), aPaintStart.Y());
+ const basegfx::B2DVector aX(basegfx::B2DPoint(aPaintEnd.X(), aPaintEnd.Y()) - aOrigin);
+
+ if(!aX.equalZero())
+ {
+ const basegfx::B2DVector aY(basegfx::getNormalizedPerpendicular(aX));
+ aData->emplace_back(
+ aOrigin,
+ aX,
+ aStyles[0],
+ pTmpColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ rInstance.addSdrConnectStyleData(true, aStyles[3], -aY, false); // aTFromR
+ rInstance.addSdrConnectStyleData(true, aStyles[2], -aX, true); // aTFromT
+ rInstance.addSdrConnectStyleData(true, aStyles[1], aY, true); // aTFromL
+
+ rInstance.addSdrConnectStyleData(false, aStyles[6], -aY, false); // aBFromR
+ rInstance.addSdrConnectStyleData(false, aStyles[5], aX, false); // aBFromB
+ rInstance.addSdrConnectStyleData(false, aStyles[4], aY, true); // aBFromL
+ }
+ }
+ }
+ }
+ ++aIter;
+ }
+
+ // create instance of SdrFrameBorderPrimitive2D if
+ // SdrFrameBorderDataVector is used
+ if(!aData->empty())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+ aSequence.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
+ aData,
+ true))); // force visualization to minimal one discrete unit (pixel)
+ // paint
+ mrTabFrame.ProcessPrimitives(aSequence);
+ }
+
+ // restore output device:
+ rDev.SetDrawMode( nOldDrawMode );
+}
+
+/**
+ * Finds the lines that join the line defined by (StartPoint, EndPoint) in either
+ * StartPoint or Endpoint. The styles of these lines are required for DR's magic
+ * line painting functions
+ */
+void SwTabFramePainter::FindStylesForLine( Point& rStartPoint,
+ Point& rEndPoint,
+ svx::frame::Style* pStyles,
+ bool bHori, bool bOuter ) const
+{
+ // For example, aLFromB means: this vertical line intersects my horizontal line at its left end,
+ // from bottom.
+ // pStyles[ 1 ] = bHori ? aLFromT : TFromL
+ // pStyles[ 2 ] = bHori ? aLFromL : TFromT,
+ // pStyles[ 3 ] = bHori ? aLFromB : TFromR,
+ // pStyles[ 4 ] = bHori ? aRFromT : BFromL,
+ // pStyles[ 5 ] = bHori ? aRFromR : BFromB,
+ // pStyles[ 6 ] = bHori ? aRFromB : BFromR,
+
+ bool bWordTableCell = false;
+ SwViewShell* pShell = mrTabFrame.getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+
+ SwLineEntryMap::const_iterator aMapIter = maVertLines.find( rStartPoint.X() );
+ OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rVertSet = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rVertSet )
+ {
+ if ( bHori )
+ {
+ if ( rStartPoint.Y() == rEntry.mnStartPos )
+ pStyles[ 3 ] = rEntry.maAttribute;
+ else if ( rStartPoint.Y() == rEntry.mnEndPos )
+ pStyles[ 1 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rStartPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rStartPoint.AdjustX(rEntry.maAttribute.GetWidth());
+ }
+ }
+ else
+ {
+ if ( rStartPoint.Y() == rEntry.mnEndPos )
+ pStyles[ 2 ] = rEntry.maAttribute;
+ else if ( rEndPoint.Y() == rEntry.mnStartPos )
+ pStyles[ 5 ] = rEntry.maAttribute;
+ }
+ }
+
+ aMapIter = maHoriLines.find( rStartPoint.Y() );
+ OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rHoriSet = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rHoriSet )
+ {
+ if ( bHori )
+ {
+ if ( rStartPoint.X() == rEntry.mnEndPos )
+ pStyles[ 2 ] = rEntry.maAttribute;
+ else if ( rEndPoint.X() == rEntry.mnStartPos )
+ pStyles[ 5 ] = rEntry.maAttribute;
+ }
+ else
+ {
+ if ( rStartPoint.X() == rEntry.mnEndPos )
+ pStyles[ 1 ] = rEntry.maAttribute;
+ else if ( rStartPoint.X() == rEntry.mnStartPos )
+ pStyles[ 3 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rStartPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rStartPoint.AdjustY(rEntry.maAttribute.GetWidth());
+ }
+ }
+ }
+
+ if ( bHori )
+ {
+ aMapIter = maVertLines.find( rEndPoint.X() );
+ OSL_ENSURE( aMapIter != maVertLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rVertSet2 = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rVertSet2 )
+ {
+ if ( rEndPoint.Y() == rEntry.mnStartPos )
+ pStyles[ 6 ] = rEntry.maAttribute;
+ else if ( rEndPoint.Y() == rEntry.mnEndPos )
+ pStyles[ 4 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rEndPoint.X() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rEndPoint.AdjustX(-rEntry.maAttribute.GetWidth());
+ }
+ }
+ }
+ else
+ {
+ aMapIter = maHoriLines.find( rEndPoint.Y() );
+ OSL_ENSURE( aMapIter != maHoriLines.end(), "FindStylesForLine: Error" );
+ const SwLineEntrySet& rHoriSet2 = (*aMapIter).second;
+
+ for ( const SwLineEntry& rEntry : rHoriSet2 )
+ {
+ if ( rEndPoint.X() == rEntry.mnEndPos )
+ pStyles[ 4 ] = rEntry.maAttribute;
+ else if ( rEndPoint.X() == rEntry.mnStartPos )
+ pStyles[ 6 ] = rEntry.maAttribute;
+
+ if (bWordTableCell && rEndPoint.Y() == rEntry.mnKey && !bOuter && rEntry.mbOuter)
+ {
+ rEndPoint.AdjustY(-rEntry.maAttribute.GetWidth());
+ }
+ }
+ }
+}
+
+/**
+ * Special case: #i9860#
+ * first line in follow table without repeated headlines
+ */
+static bool lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
+ SwTabFrame const& rTabFrame, SwFrame const& rFrame, SvxBoxItem const& rBoxItem)
+{
+ SwRowFrame const*const pThisRowFrame =
+ dynamic_cast<const SwRowFrame*>(rFrame.GetUpper());
+ return (pThisRowFrame
+ && (pThisRowFrame->GetUpper() == &rTabFrame)
+ && rTabFrame.IsFollow()
+ && !rTabFrame.GetTable()->GetRowsToRepeat()
+ && ( !pThisRowFrame->GetPrev()
+ || static_cast<const SwRowFrame*>(pThisRowFrame->GetPrev())
+ ->IsRowSpanLine())
+ && !rBoxItem.GetTop()
+ && rBoxItem.GetBottom());
+}
+
+void SwTabFramePainter::Insert(const SwFrame& rFrame, const SvxBoxItem& rBoxItem, const SwRect& rPaintArea)
+{
+ // build 4 line entries for the 4 borders:
+ SwRect aBorderRect = rFrame.getFrameArea();
+
+ aBorderRect.Intersection(rPaintArea);
+
+ bool const bBottomAsTop(lcl_IsFirstRowInFollowTableWithoutRepeatedHeadlines(
+ mrTabFrame, rFrame, rBoxItem));
+ bool const bVert = mrTabFrame.IsVertical();
+ bool const bR2L = mrTabFrame.IsRightToLeft();
+
+ bool bWordTableCell = false;
+ SwViewShell* pShell = rFrame.getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordTableCell = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+
+ // no scaling needed, it's all in the primitives and the target device
+ svx::frame::Style aL(rBoxItem.GetLeft(), 1.0);
+ aL.SetWordTableCell(bWordTableCell);
+ svx::frame::Style aR(rBoxItem.GetRight(), 1.0);
+ aR.SetWordTableCell(bWordTableCell);
+ svx::frame::Style aT(rBoxItem.GetTop(), 1.0);
+ aT.SetWordTableCell(bWordTableCell);
+ svx::frame::Style aB(rBoxItem.GetBottom(), 1.0);
+ aB.SetWordTableCell(bWordTableCell);
+
+ // First cell in a row.
+ bool bLeftIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetLower() == &rFrame;
+ // Last cell in a row.
+ bool bRightIsOuter = rFrame.IsCellFrame() && rFrame.GetNext() == nullptr;
+ // First row in a table.
+ bool bTopIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetUpper()->GetLower() == rFrame.GetUpper();
+ // Last row in a table.
+ bool bBottomIsOuter = rFrame.IsCellFrame() && rFrame.GetUpper()->GetNext() == nullptr;
+
+ aR.MirrorSelf();
+ if (!bWordTableCell || !bBottomIsOuter)
+ {
+ // Outer horizontal lines are never mirrored in Word.
+ aB.MirrorSelf();
+ }
+
+ const SwTwips nLeft = aBorderRect.Left_();
+ const SwTwips nRight = aBorderRect.Right_();
+ const SwTwips nTop = aBorderRect.Top_();
+ const SwTwips nBottom = aBorderRect.Bottom_();
+
+ aL.SetRefMode( svx::frame::RefMode::Centered );
+ aR.SetRefMode( svx::frame::RefMode::Centered );
+ aT.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
+ aB.SetRefMode( !bVert ? svx::frame::RefMode::Begin : svx::frame::RefMode::End );
+
+ if (bWordTableCell && bLeftIsOuter)
+ {
+ // Outer vertical lines are always mirrored in Word.
+ aL.MirrorSelf();
+ }
+
+ SwLineEntry aLeft (nLeft, nTop, nBottom, bLeftIsOuter,
+ bVert ? aB : (bR2L ? aR : aL));
+ if (bWordTableCell && rBoxItem.GetLeft())
+ {
+ aLeft.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::LEFT);
+ }
+
+ SwLineEntry aRight (nRight, nTop, nBottom, bRightIsOuter,
+ bVert ? (bBottomAsTop ? aB : aT) : (bR2L ? aL : aR));
+ if (bWordTableCell && rBoxItem.GetRight())
+ {
+ aRight.LimitVerticalEndPos(rFrame, SwLineEntry::VerticalType::RIGHT);
+ }
+
+ SwLineEntry aTop (nTop, nLeft, nRight, bTopIsOuter,
+ bVert ? aL : (bBottomAsTop ? aB : aT));
+
+ SwLineEntry aBottom(nBottom, nLeft, nRight, bBottomIsOuter,
+ bVert ? aR : aB);
+
+ Insert( aLeft, false );
+ Insert( aRight, false );
+ Insert( aTop, true );
+ Insert( aBottom, true );
+}
+
+void SwTabFramePainter::Insert( SwLineEntry& rNew, bool bHori )
+{
+ // get all lines from structure, that have key entry of pLE
+ SwLineEntryMap* pLine2 = bHori ? &maHoriLines : &maVertLines;
+ const SwTwips nKey = rNew.mnKey;
+ SwLineEntryMap::iterator aMapIter = pLine2->find( nKey );
+
+ SwLineEntrySet* pLineSet = aMapIter != pLine2->end() ? &((*aMapIter).second) : nullptr;
+ if ( !pLineSet )
+ {
+ SwLineEntrySet aNewSet;
+ (*pLine2)[ nKey ] = aNewSet;
+ pLineSet = &(*pLine2)[ nKey ];
+ }
+ SwLineEntrySet::iterator aIter = pLineSet->begin();
+
+ while ( aIter != pLineSet->end() && rNew.mnStartPos < rNew.mnEndPos )
+ {
+ const SwLineEntry& rOld = *aIter;
+
+ if (rOld.mnLimitedEndPos || rOld.mbOuter != rNew.mbOuter)
+ {
+ // Don't merge with this line entry as it ends sooner than mnEndPos.
+ ++aIter;
+ continue;
+ }
+
+ const SwLineEntry::OverlapType nOverlapType = rOld.Overlaps( rNew );
+
+ const svx::frame::Style& rOldAttr = rOld.maAttribute;
+ const svx::frame::Style& rNewAttr = rNew.maAttribute;
+ const svx::frame::Style& rCmpAttr = std::max(rNewAttr, rOldAttr);
+
+ if ( SwLineEntry::OVERLAP1 == nOverlapType )
+ {
+ OSL_ENSURE( rNew.mnStartPos >= rOld.mnStartPos, "Overlap type 3? How this?" );
+
+ // new left segment
+ const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
+
+ // new middle segment
+ const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rOld.mnEndPos, rOld.mbOuter, rCmpAttr);
+
+ // new right segment
+ rNew.mnStartPos = rOld.mnEndPos;
+
+ // update current lines set
+ pLineSet->erase( aIter );
+ if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
+ if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
+
+ aIter = pLineSet->begin();
+
+ continue; // start over
+ }
+ else if ( SwLineEntry::OVERLAP2 == nOverlapType )
+ {
+ // new left segment
+ const SwLineEntry aLeft(nKey, rOld.mnStartPos, rNew.mnStartPos, rOld.mbOuter, rOldAttr);
+
+ // new middle segment
+ const SwLineEntry aMiddle(nKey, rNew.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
+
+ // new right segment
+ const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
+
+ // update current lines set
+ pLineSet->erase( aIter );
+ if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
+ if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
+ if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
+
+ rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
+
+ break; // we are finished
+ }
+ else if ( SwLineEntry::OVERLAP3 == nOverlapType )
+ {
+ // new left segment
+ const SwLineEntry aLeft(nKey, rNew.mnStartPos, rOld.mnStartPos, rOld.mbOuter, rNewAttr);
+
+ // new middle segment
+ const SwLineEntry aMiddle(nKey, rOld.mnStartPos, rNew.mnEndPos, rOld.mbOuter, rCmpAttr);
+
+ // new right segment
+ const SwLineEntry aRight(nKey, rNew.mnEndPos, rOld.mnEndPos, rOld.mbOuter, rOldAttr);
+
+ // update current lines set
+ pLineSet->erase( aIter );
+ if ( aLeft.mnStartPos < aLeft.mnEndPos ) pLineSet->insert( aLeft );
+ if ( aMiddle.mnStartPos < aMiddle.mnEndPos ) pLineSet->insert( aMiddle );
+ if ( aRight.mnStartPos < aRight.mnEndPos ) pLineSet->insert( aRight );
+
+ rNew.mnStartPos = rNew.mnEndPos; // rNew should not be inserted!
+
+ break; // we are finished
+ }
+
+ ++aIter;
+ }
+
+ if ( rNew.mnStartPos < rNew.mnEndPos ) // insert rest
+ pLineSet->insert( rNew );
+}
+
+/**
+ * FUNCTIONS USED FOR COLLAPSING TABLE BORDER LINES END
+ * --> OD #i76669#
+ */
+namespace
+{
+ class SwViewObjectContactRedirector : public sdr::contact::ViewObjectContactRedirector
+ {
+ private:
+ const SwViewShell& mrViewShell;
+
+ public:
+ explicit SwViewObjectContactRedirector( const SwViewShell& rSh )
+ : mrViewShell( rSh )
+ {};
+
+ virtual void createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal,
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override
+ {
+ bool bPaint( true );
+
+ SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject();
+ if ( pObj )
+ {
+ bPaint = SwFlyFrame::IsPaint( pObj, &mrViewShell );
+ }
+
+ if ( !bPaint )
+ {
+ return;
+ }
+
+ sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+ rOriginal, rDisplayInfo, rVisitor );
+ }
+ };
+
+} // end of anonymous namespace
+// <--
+
+/**
+ * Paint once for every visible page which is touched by Rect
+ *
+ * 1. Paint borders and backgrounds
+ * 2. Paint the draw layer (frames and drawing objects) that is
+ * below the document (hell)
+ * 3. Paint the document content (text)
+ * 4. Paint the draw layer that is above the document
+|*/
+void SwRootFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const pPrintData) const
+{
+ OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "Lower of root is no page." );
+
+ PROTOCOL( this, PROT::FileInit, DbgAction::NONE, nullptr)
+
+ bool bResetRootPaint = false;
+ SwViewShell *pSh = mpCurrShell;
+
+ if ( pSh->GetWin() )
+ {
+ if ( pSh->GetOut() == pSh->GetWin()->GetOutDev() && !pSh->GetWin()->IsVisible() )
+ {
+ return;
+ }
+ if (SwRootFrame::s_isInPaint)
+ {
+ SwPaintQueue::Add( pSh, rRect );
+ return;
+ }
+ }
+ else
+ SwRootFrame::s_isInPaint = bResetRootPaint = true;
+
+ std::unique_ptr<SwSavePaintStatics> pStatics;
+ if ( gProp.pSGlobalShell )
+ pStatics.reset(new SwSavePaintStatics());
+ gProp.pSGlobalShell = pSh;
+
+ if( !pSh->GetWin() )
+ gProp.pSProgress = SfxProgress::GetActiveProgress( static_cast<SfxObjectShell*>(pSh->GetDoc()->GetDocShell()) );
+
+ ::SwCalcPixStatics( pSh->GetOut() );
+ aGlobalRetoucheColor = pSh->Imp()->GetRetoucheColor();
+
+ // Copy rRect; for one, rRect could become dangling during the below action, and for another it
+ // needs to be copied to aRect anyway as that is modified further down below:
+ SwRect aRect( rRect );
+
+ //Trigger an action to clear things up if needed.
+ //Using this trick we can ensure that all values are valid in all paints -
+ //no problems, no special case(s).
+ // #i92745#
+ // Extend check on certain states of the 'current' <SwViewShell> instance to
+ // all existing <SwViewShell> instances.
+ bool bPerformLayoutAction( true );
+ {
+ for(SwViewShell& rTmpViewShell : pSh->GetRingContainer())
+ {
+ if ( rTmpViewShell.IsInEndAction() ||
+ rTmpViewShell.IsPaintInProgress() ||
+ ( rTmpViewShell.Imp()->IsAction() &&
+ rTmpViewShell.Imp()->GetLayAction().IsActionInProgress() ) )
+ {
+ bPerformLayoutAction = false;
+ }
+
+ if(!bPerformLayoutAction)
+ break;
+ }
+ }
+ if ( bPerformLayoutAction )
+ {
+ const_cast<SwRootFrame*>(this)->ResetTurbo();
+ SwLayAction aAction( const_cast<SwRootFrame*>(this), pSh->Imp() );
+ aAction.SetPaint( false );
+ aAction.SetComplete( false );
+ aAction.SetReschedule( gProp.pSProgress != nullptr );
+ aAction.Action(&rRenderContext);
+ ResetTurboFlag();
+ if ( !pSh->ActionPend() )
+ pSh->Imp()->DeletePaintRegion();
+ }
+
+ aRect.Intersection( pSh->VisArea() );
+
+ const bool bExtraData = ::IsExtraData( GetFormat()->GetDoc() );
+
+ gProp.pSLines.reset(new SwLineRects); // Container for borders.
+
+ // #104289#. During painting, something (OLE) can
+ // load the linguistic, which in turn can cause a reformat
+ // of the document. Dangerous! We better set this flag to
+ // avoid the reformat.
+ const bool bOldAction = IsCallbackActionEnabled();
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
+
+ const SwPageFrame *pPage = pSh->Imp()->GetFirstVisPage(&rRenderContext);
+
+ // #126222. The positions of headers and footers of the previous
+ // pages have to be updated, else these headers and footers could
+ // get visible at a wrong position.
+ const SwPageFrame *pPageDeco = static_cast<const SwPageFrame*>(pPage->GetPrev());
+ while (pPageDeco)
+ {
+ pPageDeco->PaintDecorators();
+ OSL_ENSURE(!pPageDeco->GetPrev() || pPageDeco->GetPrev()->IsPageFrame(),
+ "Neighbour of page is not a page.");
+ pPageDeco = static_cast<const SwPageFrame*>(pPageDeco->GetPrev());
+ }
+
+ const bool bBookMode = gProp.pSGlobalShell->GetViewOptions()->IsViewLayoutBookMode();
+ if ( bBookMode && pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetPrev());
+
+ // #i68597#
+ const bool bGridPainting(pSh->GetWin() && pSh->Imp()->HasDrawView() && pSh->Imp()->GetDrawView()->IsGridVisible());
+
+ // Hide all page break controls before showing them again
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( pWrtSh )
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ const SwPageFrame* pHiddenPage = pPage;
+ while ( pHiddenPage->GetPrev() != nullptr )
+ {
+ pHiddenPage = static_cast< const SwPageFrame* >( pHiddenPage->GetPrev() );
+ SwFrameControlPtr pControl = rMngr.GetControl( FrameControlType::PageBreak, pHiddenPage );
+ if ( pControl )
+ pControl->ShowAll( false );
+ }
+ }
+
+ // #i76669#
+ SwViewObjectContactRedirector aSwRedirector( *pSh );
+
+ while ( pPage )
+ {
+ const bool bPaintRightShadow = pPage->IsRightShadowNeeded();
+ const bool bPaintLeftShadow = pPage->IsLeftShadowNeeded();
+ const bool bRightSidebar = pPage->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
+
+ if ( !pPage->IsEmptyPage() )
+ {
+ SwRect aPaintRect;
+ SwPageFrame::GetBorderAndShadowBoundRect( pPage->getFrameArea(), pSh, &rRenderContext, aPaintRect,
+ bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+
+ if ( aRect.Overlaps( aPaintRect ) )
+ {
+ if ( pSh->GetWin() )
+ {
+ gProp.pSSubsLines.reset(new SwSubsRects);
+ gProp.pSSpecSubsLines.reset(new SwSubsRects);
+ }
+ gProp.pBLines.reset(new BorderLines);
+
+ aPaintRect.Intersection_( aRect );
+
+ if ( bExtraData &&
+ pSh->GetWin() && pSh->IsInEndAction() )
+ {
+ // enlarge paint rectangle to complete page width, subtract
+ // current paint area and invalidate the resulting region.
+ SwRectFnSet aRectFnSet(pPage);
+ SwRect aPageRectTemp( aPaintRect );
+ aRectFnSet.SetLeftAndWidth( aPageRectTemp,
+ aRectFnSet.GetLeft(pPage->getFrameArea()),
+ aRectFnSet.GetWidth(pPage->getFrameArea()) );
+ aPageRectTemp.Intersection_( pSh->VisArea() );
+ vcl::Region aPageRectRegion( aPageRectTemp.SVRect() );
+ aPageRectRegion.Exclude( aPaintRect.SVRect() );
+ pSh->GetWin()->Invalidate( aPageRectRegion, InvalidateFlags::Children );
+ }
+
+ // #i80793#
+ // enlarge paint rectangle for objects overlapping the same pixel
+ // in all cases and before the DrawingLayer overlay is initialized.
+ lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
+
+ // #i68597#
+ // moved paint pre-process for DrawingLayer overlay here since the above
+ // code dependent from bExtraData may expand the PaintRect
+ {
+ // #i75172# if called from SwViewShell::ImplEndAction it should no longer
+ // really be used but handled by SwViewShell::ImplEndAction already
+ const vcl::Region aDLRegion(aPaintRect.SVRect());
+ pSh->DLPrePaint2(aDLRegion);
+ }
+
+ if(OUTDEV_WINDOW == gProp.pSGlobalShell->GetOut()->GetOutDevType())
+ {
+ // changed method SwLayVout::Enter(..)
+ // 2nd parameter is no longer <const> and will be set to the
+ // rectangle the virtual output device is calculated from <aPaintRect>,
+ // if the virtual output is used.
+ s_pVout->Enter(pSh, aPaintRect, !s_isNoVirDev);
+
+ // Adjust paint rectangle to pixel size
+ // Thus, all objects overlapping on pixel level with the unadjusted
+ // paint rectangle will be considered in the paint.
+ lcl_AdjustRectToPixelSize( aPaintRect, *(pSh->GetOut()) );
+ }
+
+ // maybe this can be put in the above scope. Since we are not sure, just leave it ATM
+ s_pVout->SetOrgRect( aPaintRect );
+
+ // determine background color of page for <PaintLayer> method
+ // calls, paint <hell> or <heaven>
+ const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
+
+ pPage->PaintBaBo( aPaintRect, pPage );
+
+ if ( pSh->Imp()->HasDrawView() )
+ {
+ gProp.pSLines->LockLines( true );
+ const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
+ pSh->Imp()->PaintLayer( rIDDMA.GetHellId(),
+ pPrintData,
+ *pPage, pPage->getFrameArea(),
+ &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
+ gProp.pSLines->LockLines( false );
+ }
+
+ if ( pSh->GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::BACKGROUND_PARA_OVER_DRAWINGS ) )
+ pPage->PaintBaBo( aPaintRect, pPage, /*bOnlyTextBackground=*/true );
+
+ if( pSh->GetWin() )
+ {
+ // collect sub-lines
+ pPage->RefreshSubsidiary( aPaintRect );
+ // paint special sub-lines
+ gProp.pSSpecSubsLines->PaintSubsidiary( pSh->GetOut(), nullptr, gProp );
+ }
+
+ pPage->PaintSwFrame( rRenderContext, aPaintRect );
+
+ // no paint of page border and shadow, if writer is in place mode.
+ if( pSh->GetWin() && pSh->GetDoc()->GetDocShell() &&
+ !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
+ {
+ SwPageFrame::PaintBorderAndShadow( pPage->getFrameArea(), pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+ SwPageFrame::PaintNotesSidebar( pPage->getFrameArea(), pSh, pPage->GetPhyPageNum(), bRightSidebar);
+ }
+
+ gProp.pSLines->PaintLines( pSh->GetOut(), gProp );
+ if ( pSh->GetWin() )
+ {
+ gProp.pSSubsLines->PaintSubsidiary( pSh->GetOut(), gProp.pSLines.get(), gProp );
+ gProp.pSSubsLines.reset();
+ gProp.pSSpecSubsLines.reset();
+ }
+ // fdo#42750: delay painting these until after subsidiary lines
+ // fdo#45562: delay painting these until after hell layer
+ // fdo#47717: but do it before heaven layer
+ ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
+
+ if ( pSh->Imp()->HasDrawView() )
+ {
+ pSh->Imp()->PaintLayer( pSh->GetDoc()->getIDocumentDrawModelAccess().GetHeavenId(),
+ pPrintData,
+ *pPage, pPage->getFrameArea(),
+ &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ }
+
+ if ( bExtraData )
+ pPage->RefreshExtraData( aPaintRect );
+
+ gProp.pBLines.reset();
+ s_pVout->Leave();
+
+ // #i68597#
+ // needed to move grid painting inside Begin/EndDrawLayer bounds and to change
+ // output rect for it accordingly
+ if(bGridPainting)
+ {
+ SdrPaintView* pPaintView = pSh->Imp()->GetDrawView();
+ SdrPageView* pPageView = pPaintView->GetSdrPageView();
+ pPageView->DrawPageViewGrid(*pSh->GetOut(), aPaintRect.SVRect(), SwViewOption::GetTextGridColor() );
+ }
+
+ // #i68597#
+ // moved paint post-process for DrawingLayer overlay here, see above
+ {
+ pSh->DLPostPaint2(true);
+ }
+ }
+
+ pPage->PaintDecorators( );
+ pPage->PaintBreak();
+ }
+ else if ( bBookMode && pSh->GetWin() && !pSh->GetDoc()->GetDocShell()->IsInPlaceActive() )
+ {
+ // paint empty page
+ SwRect aPaintRect;
+ SwRect aEmptyPageRect( pPage->getFrameArea() );
+
+ // code from vprint.cxx
+ const SwPageFrame& rFormatPage = pPage->GetFormatPage();
+ aEmptyPageRect.SSize( rFormatPage.getFrameArea().SSize() );
+
+ SwPageFrame::GetBorderAndShadowBoundRect( aEmptyPageRect, pSh, &rRenderContext, aPaintRect,
+ bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+ aPaintRect.Intersection_( aRect );
+
+ if ( aRect.Overlaps( aEmptyPageRect ) )
+ {
+ // #i75172# if called from SwViewShell::ImplEndAction it should no longer
+ // really be used but handled by SwViewShell::ImplEndAction already
+ {
+ const vcl::Region aDLRegion(aPaintRect.SVRect());
+ pSh->DLPrePaint2(aDLRegion);
+ }
+
+ if( pSh->GetOut()->GetFillColor() != aGlobalRetoucheColor )
+ pSh->GetOut()->SetFillColor( aGlobalRetoucheColor );
+ // No line color
+ pSh->GetOut()->SetLineColor();
+ // Use aligned page rectangle
+ {
+ SwRect aTmpPageRect( aEmptyPageRect );
+ ::SwAlignRect( aTmpPageRect, pSh, &rRenderContext );
+ aEmptyPageRect = aTmpPageRect;
+ }
+
+ pSh->GetOut()->DrawRect( aEmptyPageRect.SVRect() );
+
+ // paint empty page text
+ const vcl::Font& rEmptyPageFont = SwPageFrame::GetEmptyPageFont();
+ const vcl::Font aOldFont( pSh->GetOut()->GetFont() );
+
+ pSh->GetOut()->SetFont( rEmptyPageFont );
+ pSh->GetOut()->DrawText( aEmptyPageRect.SVRect(), SwResId( STR_EMPTYPAGE ),
+ DrawTextFlags::VCenter |
+ DrawTextFlags::Center |
+ DrawTextFlags::Clip );
+
+ pSh->GetOut()->SetFont( aOldFont );
+ // paint shadow and border for empty page
+ SwPageFrame::PaintBorderAndShadow( aEmptyPageRect, pSh, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+ SwPageFrame::PaintNotesSidebar( aEmptyPageRect, pSh, pPage->GetPhyPageNum(), bRightSidebar);
+
+ {
+ pSh->DLPostPaint2(true);
+ }
+ }
+ }
+
+ OSL_ENSURE( !pPage->GetNext() || pPage->GetNext()->IsPageFrame(),
+ "Neighbour of page is not a page." );
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ gProp.pSLines.reset();
+
+ if ( bResetRootPaint )
+ SwRootFrame::s_isInPaint = false;
+ if ( pStatics )
+ pStatics.reset();
+ else
+ {
+ gProp.pSProgress = nullptr;
+ gProp.pSGlobalShell = nullptr;
+ }
+
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
+}
+
+static void lcl_EmergencyFormatFootnoteCont( SwFootnoteContFrame *pCont )
+{
+ vcl::RenderContext* pRenderContext = pCont->getRootFrame()->GetCurrShell()->GetOut();
+
+ //It's possible that the Cont will get destroyed.
+ SwContentFrame *pCnt = pCont->ContainsContent();
+ while ( pCnt && pCnt->IsInFootnote() )
+ {
+ pCnt->Calc(pRenderContext);
+ pCnt = pCnt->GetNextContentFrame();
+ }
+}
+
+namespace {
+
+class SwShortCut
+{
+ SwRectDist m_fnCheck;
+ tools::Long m_nLimit;
+
+public:
+ SwShortCut( const SwFrame& rFrame, const SwRect& rRect );
+ bool Stop(const SwRect& rRect) const { return (rRect.*m_fnCheck)(m_nLimit) > 0; }
+};
+
+}
+
+SwShortCut::SwShortCut( const SwFrame& rFrame, const SwRect& rRect )
+{
+ bool bVert = rFrame.IsVertical();
+ bool bR2L = rFrame.IsRightToLeft();
+ if( rFrame.IsNeighbourFrame() && bVert == bR2L )
+ {
+ if( bVert )
+ {
+ m_fnCheck = &SwRect::GetBottomDistance;
+ m_nLimit = rRect.Top();
+ }
+ else
+ {
+ m_fnCheck = &SwRect::GetLeftDistance;
+ m_nLimit = rRect.Left() + rRect.Width();
+ }
+ }
+ else if( bVert == rFrame.IsNeighbourFrame() )
+ {
+ m_fnCheck = &SwRect::GetTopDistance;
+ m_nLimit = rRect.Top() + rRect.Height();
+ }
+ else
+ {
+ if ( rFrame.IsVertLR() )
+ {
+ m_fnCheck = &SwRect::GetLeftDistance;
+ m_nLimit = rRect.Right();
+ }
+ else
+ {
+ m_fnCheck = &SwRect::GetRightDistance;
+ m_nLimit = rRect.Left();
+ }
+ }
+}
+
+void SwLayoutFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ // #i16816# tagged pdf support
+ Frame_Info aFrameInfo( *this );
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, &aFrameInfo, nullptr, rRenderContext );
+
+ const SwFrame *pFrame = Lower();
+ if ( !pFrame )
+ return;
+
+ SwFrameDeleteGuard g(const_cast<SwLayoutFrame*>(this)); // lock because Calc() and recursion
+ SwShortCut aShortCut( *pFrame, rRect );
+ bool bCnt = pFrame->IsContentFrame();
+ if ( bCnt )
+ pFrame->Calc(&rRenderContext);
+
+ if ( pFrame->IsFootnoteContFrame() )
+ {
+ ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame)) );
+ pFrame = Lower();
+ }
+
+ const SwPageFrame *pPage = nullptr;
+ bool bWin = gProp.pSGlobalShell->GetWin() != nullptr;
+ if (comphelper::LibreOfficeKit::isTiledPainting())
+ // Tiled rendering is similar to printing in this case: painting transparently multiple
+ // times will result in darker colors: avoid that.
+ bWin = false;
+
+ while ( IsAnLower( pFrame ) )
+ {
+ SwRect aPaintRect( pFrame->GetPaintArea() );
+ if( aShortCut.Stop( aPaintRect ) )
+ break;
+ if ( bCnt && gProp.pSProgress )
+ SfxProgress::Reschedule();
+
+ //We need to retouch if a frame explicitly requests it.
+ //First do the retouch, because this could flatten the borders.
+ if ( pFrame->IsRetouche() )
+ {
+ if ( pFrame->IsRetoucheFrame() && bWin && !pFrame->GetNext() )
+ {
+ if ( !pPage )
+ pPage = FindPageFrame();
+ pFrame->Retouch( pPage, rRect );
+ }
+ pFrame->ResetRetouche();
+ }
+
+ if ( rRect.Overlaps( aPaintRect ) )
+ {
+ if ( bCnt && pFrame->IsCompletePaint() &&
+ !rRect.Contains( aPaintRect ) && Application::AnyInput( VclInputFlags::KEYBOARD ) )
+ {
+ //fix(8104): It may happen, that the processing wasn't complete
+ //but some parts of the paragraph were still repainted.
+ //This could lead to the situation, that other parts of the
+ //paragraph won't be repainted at all. The only solution seems
+ //to be an invalidation of the window.
+ //To not make it too severe the rectangle is limited by
+ //painting the desired part and only invalidating the
+ //remaining paragraph parts.
+ if ( aPaintRect.Left() == rRect.Left() &&
+ aPaintRect.Right() == rRect.Right() )
+ {
+ aPaintRect.Bottom( rRect.Top() - 1 );
+ if ( aPaintRect.Height() > 0 )
+ gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
+ aPaintRect.Top( rRect.Bottom() + 1 );
+ aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
+ if ( aPaintRect.Height() > 0 )
+ gProp.pSGlobalShell->InvalidateWindows(aPaintRect);
+ aPaintRect.Top( pFrame->getFrameArea().Top() );
+ aPaintRect.Bottom( pFrame->getFrameArea().Bottom() );
+ }
+ else
+ {
+ gProp.pSGlobalShell->InvalidateWindows( aPaintRect );
+ pFrame = pFrame->GetNext();
+ if ( pFrame )
+ {
+ bCnt = pFrame->IsContentFrame();
+ if ( bCnt )
+ pFrame->Calc(&rRenderContext);
+ }
+ continue;
+ }
+ }
+ pFrame->ResetCompletePaint();
+ aPaintRect.Intersection_( rRect );
+
+ pFrame->PaintSwFrame( rRenderContext, aPaintRect );
+
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ //Paint the column separator line if needed. The page is
+ //responsible for the page frame - not the upper.
+ const SwFrameFormat *pFormat = GetUpper() && GetUpper()->IsPageFrame()
+ ? GetUpper()->GetFormat()
+ : GetFormat();
+ const SwFormatCol &rCol = pFormat->GetCol();
+ if ( rCol.GetLineAdj() != COLADJ_NONE )
+ {
+ if ( !pPage )
+ pPage = pFrame->FindPageFrame();
+
+ PaintColLines( aPaintRect, rCol, pPage );
+ }
+ }
+ }
+ if ( !bCnt && pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame() )
+ ::lcl_EmergencyFormatFootnoteCont( const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pFrame->GetNext())) );
+
+ pFrame = pFrame->GetNext();
+
+ if ( pFrame )
+ {
+ bCnt = pFrame->IsContentFrame();
+ if ( bCnt )
+ pFrame->Calc(&rRenderContext);
+ }
+ }
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateDashedIndicatorPrimitive(
+ const basegfx::B2DPoint& rStart, const basegfx::B2DPoint& rEnd,
+ basegfx::BColor aColor )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
+
+ std::vector< double > aStrokePattern;
+ basegfx::B2DPolygon aLinePolygon;
+ aLinePolygon.append(rStart);
+ aLinePolygon.append(rEnd);
+
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ if ( rSettings.GetHighContrastMode( ) )
+ {
+ // Only a solid line in high contrast mode
+ aColor = rSettings.GetDialogTextColor().getBColor();
+ }
+ else
+ {
+ // Get a color for the contrast
+ basegfx::BColor aHslLine = basegfx::utils::rgb2hsl( aColor );
+ double nLuminance = aHslLine.getZ() * 2.5;
+ if ( nLuminance == 0 )
+ nLuminance = 0.5;
+ else if ( nLuminance >= 1.0 )
+ nLuminance = aHslLine.getZ() * 0.4;
+ aHslLine.setZ( nLuminance );
+ const basegfx::BColor aOtherColor = basegfx::utils::hsl2rgb( aHslLine );
+
+ // Compute the plain line
+ aSeq[0] =
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aLinePolygon, aOtherColor );
+
+ // Dashed line in twips
+ aStrokePattern.push_back( 40 );
+ aStrokePattern.push_back( 40 );
+
+ aSeq.resize( 2 );
+ }
+
+ // Compute the dashed line primitive
+ aSeq[ aSeq.size( ) - 1 ] =
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D (
+ basegfx::B2DPolyPolygon( aLinePolygon ),
+ drawinglayer::attribute::LineAttribute( aColor ),
+ drawinglayer::attribute::StrokeAttribute( std::move(aStrokePattern) ) );
+
+
+ return aSeq;
+}
+
+void SwPageFrame::PaintBreak( ) const
+{
+ if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
+ gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
+ gProp.pSGlobalShell->IsPreview() )
+ return;
+
+ const SwFrame* pBodyFrame = Lower();
+ while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
+ pBodyFrame = pBodyFrame->GetNext();
+
+ if ( pBodyFrame )
+ {
+ const SwLayoutFrame* pLayBody = static_cast< const SwLayoutFrame* >( pBodyFrame );
+ const SwFlowFrame *pFlowFrame = pLayBody->ContainsContent();
+
+ // Test if the first node is a table
+ const SwFrame* pFirstFrame = pLayBody->Lower();
+ if ( pFirstFrame && pFirstFrame->IsTabFrame() )
+ pFlowFrame = static_cast< const SwTabFrame* >( pFirstFrame );
+
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( pWrtSh )
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+
+ if ( pFlowFrame && pFlowFrame->IsPageBreak( true ) )
+ rMngr.SetPageBreakControl( this );
+ else
+ rMngr.RemoveControlsByType( FrameControlType::PageBreak, this );
+ }
+ }
+ SwLayoutFrame::PaintBreak( );
+}
+
+void SwColumnFrame::PaintBreak( ) const
+{
+ if ( gProp.pSGlobalShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() ||
+ gProp.pSGlobalShell->GetViewOptions()->IsReadonly() ||
+ gProp.pSGlobalShell->IsPreview() )
+ return;
+
+ const SwFrame* pBodyFrame = Lower();
+ while ( pBodyFrame && !pBodyFrame->IsBodyFrame() )
+ pBodyFrame = pBodyFrame->GetNext();
+
+ if ( !pBodyFrame )
+ return;
+
+ const SwContentFrame *pCnt = static_cast< const SwLayoutFrame* >( pBodyFrame )->ContainsContent();
+ if ( !(pCnt && pCnt->IsColBreak( true )) )
+ return;
+
+ // Paint the break only if:
+ // * Not in header footer edition, to avoid conflicts with the
+ // header/footer marker
+ // * Non-printing characters are shown, as this is more consistent
+ // with other formatting marks
+ if ( !(!gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
+ !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) &&
+ gProp.pSGlobalShell->GetViewOptions()->IsLineBreak()) )
+ return;
+
+ SwRect aRect( pCnt->getFramePrintArea() );
+ aRect.Pos() += pCnt->getFrameArea().Pos();
+
+ // Draw the line
+ basegfx::B2DPoint aStart( double( aRect.Left() ), aRect.Top() );
+ basegfx::B2DPoint aEnd( double( aRect.Right() ), aRect.Top() );
+ double nWidth = aRect.Width();
+ if ( IsVertical( ) )
+ {
+ aStart = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Top() ) );
+ aEnd = basegfx::B2DPoint( double( aRect.Right() ), double( aRect.Bottom() ) );
+ nWidth = aRect.Height();
+ }
+
+ basegfx::BColor aLineColor = SwViewOption::GetPageBreakColor().getBColor();
+
+ drawinglayer::primitive2d::Primitive2DContainer aSeq =
+ lcl_CreateDashedIndicatorPrimitive( aStart, aEnd, aLineColor );
+
+ // Add the text above
+ OUString aBreakText = SwResId(STR_COLUMN_BREAK);
+
+ basegfx::B2DVector aFontSize;
+ OutputDevice* pOut = gProp.pSGlobalShell->GetOut();
+ vcl::Font aFont = pOut->GetSettings().GetStyleSettings().GetToolFont();
+ aFont.SetFontHeight( 8 * 20 );
+ pOut->SetFont( aFont );
+ drawinglayer::attribute::FontAttribute aFontAttr = drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontSize, aFont, IsRightToLeft(), false );
+
+ tools::Rectangle aTextRect;
+ pOut->GetTextBoundRect( aTextRect, aBreakText );
+ tools::Long nTextOff = ( nWidth - aTextRect.GetWidth() ) / 2;
+
+ basegfx::B2DHomMatrix aTextMatrix( basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aFontSize.getX(), aFontSize.getY(),
+ aRect.Left() + nTextOff, aRect.Top() ) );
+ if ( IsVertical() )
+ {
+ aTextMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix (
+ aFontSize.getX(), aFontSize.getY(), 0.0, M_PI_2,
+ aRect.Right(), aRect.Top() + nTextOff );
+ }
+
+ aSeq.push_back(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aTextMatrix,
+ aBreakText, 0, aBreakText.getLength(),
+ std::vector< double >(),
+ aFontAttr,
+ lang::Locale(),
+ aLineColor ) );
+
+ ProcessPrimitives( aSeq );
+}
+
+void SwLayoutFrame::PaintBreak( ) const
+{
+ const SwFrame* pFrame = Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ static_cast< const SwLayoutFrame*>( pFrame )->PaintBreak( );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+void SwPageFrame::PaintDecorators( ) const
+{
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( !pWrtSh )
+ return;
+
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+
+ const SwLayoutFrame* pBody = FindBodyCont();
+ if ( !pBody )
+ return;
+
+ SwRect aBodyRect( pBody->getFrameArea() );
+
+ if ( !(gProp.pSGlobalShell->GetOut()->GetOutDevType() != OUTDEV_PRINTER &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsPDFExport() &&
+ !gProp.pSGlobalShell->IsPreview() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsReadonly() &&
+ !gProp.pSGlobalShell->GetViewOptions()->getBrowseMode() &&
+ ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) ||
+ gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )) )
+ return;
+
+ bool bRtl = AllSettings::GetLayoutRTL();
+ const SwRect& rVisArea = gProp.pSGlobalShell->VisArea();
+ tools::Long nXOff = std::min( aBodyRect.Right(), rVisArea.Right() );
+ if ( bRtl )
+ nXOff = std::max( aBodyRect.Left(), rVisArea.Left() );
+
+ // Header
+ if ( gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Header ) )
+ {
+ const SwFrame* pHeaderFrame = Lower();
+ if ( !pHeaderFrame->IsHeaderFrame() )
+ pHeaderFrame = nullptr;
+
+ tools::Long nHeaderYOff = aBodyRect.Top();
+ Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nHeaderYOff ) );
+ rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Header, nOutputOff );
+ }
+
+ // Footer
+ if ( !gProp.pSGlobalShell->IsShowHeaderFooterSeparator( FrameControlType::Footer ) )
+ return;
+
+ const SwFrame* pFootnoteContFrame = Lower();
+ while ( pFootnoteContFrame )
+ {
+ if ( pFootnoteContFrame->IsFootnoteContFrame() )
+ aBodyRect.AddBottom( pFootnoteContFrame->getFrameArea().Bottom() - aBodyRect.Bottom() );
+ pFootnoteContFrame = pFootnoteContFrame->GetNext();
+ }
+
+ tools::Long nFooterYOff = aBodyRect.Bottom();
+ Point nOutputOff = rEditWin.LogicToPixel( Point( nXOff, nFooterYOff ) );
+ rEditWin.GetFrameControlsManager().SetHeaderFooterControl( this, FrameControlType::Footer, nOutputOff );
+}
+
+/**
+ * For feature #99657#
+ *
+ * OD 12.08.2002
+ * determines, if background of fly frame has to be drawn transparent
+ * declaration found in /core/inc/flyfrm.cxx
+ *
+ * OD 08.10.2002 #103898# - If the background of the fly frame itself is not
+ * transparent and the background is inherited from its parent/grandparent,
+ * the background brush, used for drawing, has to be investigated for transparency.
+ *
+ * @return true, if background is transparent drawn
+*/
+bool SwFlyFrame::IsBackgroundTransparent() const
+{
+ bool bBackgroundTransparent = GetFormat()->IsBackgroundTransparent();
+ if ( !bBackgroundTransparent &&
+ GetFormat()->IsBackgroundBrushInherited() )
+ {
+ const SvxBrushItem* pBackgroundBrush = nullptr;
+ std::optional<Color> xSectionTOXColor;
+ SwRect aDummyRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( GetBackgroundBrush( aFillAttributes, pBackgroundBrush, xSectionTOXColor, aDummyRect, false, /*bConsiderTextBox=*/false) )
+ {
+ if ( xSectionTOXColor &&
+ (xSectionTOXColor->IsTransparent()) &&
+ (xSectionTOXColor != COL_TRANSPARENT) )
+ {
+ bBackgroundTransparent = true;
+ }
+ else if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ bBackgroundTransparent = aFillAttributes->isTransparent();
+ }
+ else if ( pBackgroundBrush )
+ {
+ if ( (pBackgroundBrush->GetColor().IsTransparent()) &&
+ (pBackgroundBrush->GetColor() != COL_TRANSPARENT) )
+ {
+ bBackgroundTransparent = true;
+ }
+ else
+ {
+ const GraphicObject *pTmpGrf =
+ pBackgroundBrush->GetGraphicObject();
+ if ( pTmpGrf &&
+ (pTmpGrf->GetAttr().IsTransparent())
+ )
+ {
+ bBackgroundTransparent = true;
+ }
+ }
+ }
+ }
+ }
+
+ return bBackgroundTransparent;
+};
+
+bool SwFlyFrame::IsPaint( SdrObject *pObj, const SwViewShell *pSh )
+{
+ SdrObjUserCall *pUserCall = GetUserCall(pObj);
+
+ if ( nullptr == pUserCall )
+ return true;
+
+ //Attribute dependent, don't paint for printer or Preview
+ bool bPaint = gProp.pSFlyOnlyDraw ||
+ static_cast<SwContact*>(pUserCall)->GetFormat()->GetPrint().GetValue();
+ if ( !bPaint )
+ bPaint = pSh->GetWin() && !pSh->IsPreview();
+
+ if ( bPaint )
+ {
+ //The paint may be prevented by the superior Flys.
+ SwFrame *pAnch = nullptr;
+ if ( dynamic_cast< const SwFlyDrawObj *>( pObj ) != nullptr ) // i#117962#
+ {
+ bPaint = false;
+ }
+ if ( auto pFlyDraw = dynamic_cast<SwVirtFlyDrawObj *>( pObj ) )
+ {
+ SwFlyFrame *pFly = pFlyDraw->GetFlyFrame();
+ if ( gProp.pSFlyOnlyDraw && gProp.pSFlyOnlyDraw == pFly )
+ return true;
+
+ //Try to avoid displaying the intermediate stage, Flys which don't
+ //overlap with the page on which they are anchored won't be
+ //painted.
+ //HACK: exception: printing of frames in tables, those can overlap
+ //a page once in a while when dealing with oversized tables (HTML).
+ SwPageFrame *pPage = pFly->FindPageFrame();
+ if ( pPage && pPage->getFrameArea().Overlaps( pFly->getFrameArea() ) )
+ {
+ pAnch = pFly->AnchorFrame();
+ }
+
+ }
+ else
+ {
+ // Consider 'virtual' drawing objects
+ SwDrawContact* pDrawContact = dynamic_cast<SwDrawContact*>(pUserCall);
+ pAnch = pDrawContact ? pDrawContact->GetAnchorFrame(pObj) : nullptr;
+ if ( pAnch )
+ {
+ if ( !pAnch->isFrameAreaPositionValid() )
+ pAnch = nullptr;
+ else if ( pSh->GetOut() == pSh->getIDocumentDeviceAccess().getPrinter( false ))
+ {
+ //HACK: we have to omit some of the objects for printing,
+ //otherwise they would be printed twice.
+ //The objects should get printed if the TableHack is active
+ //right now. Afterwards they must not be printed if the
+ //page over which they float position wise gets printed.
+ const SwPageFrame *pPage = pAnch->FindPageFrame();
+ if ( !pPage->getFrameArea().Overlaps( SwRect(pObj->GetCurrentBoundRect()) ) )
+ pAnch = nullptr;
+ }
+ }
+ else
+ {
+ if ( dynamic_cast< const SdrObjGroup *>( pObj ) == nullptr )
+ {
+ OSL_FAIL( "<SwFlyFrame::IsPaint(..)> - paint of drawing object without anchor frame!?" );
+ }
+ }
+ }
+ if ( pAnch )
+ {
+ if ( pAnch->IsInFly() )
+ bPaint = SwFlyFrame::IsPaint( pAnch->FindFlyFrame()->GetVirtDrawObj(),
+ pSh );
+ else if ( gProp.pSFlyOnlyDraw )
+ bPaint = false;
+ }
+ else
+ bPaint = false;
+ }
+ return bPaint;
+}
+
+void SwCellFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ if ( GetLayoutRowSpan() >= 1 )
+ SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
+}
+
+namespace {
+
+struct BorderLinesGuard
+{
+ explicit BorderLinesGuard() : m_pBorderLines(std::move(gProp.pBLines))
+ {
+ gProp.pBLines.reset(new BorderLines);
+ }
+ ~BorderLinesGuard()
+ {
+ gProp.pBLines = std::move(m_pBorderLines);
+ }
+private:
+ std::unique_ptr<BorderLines> m_pBorderLines;
+};
+
+}
+
+// set strikethrough for deleted objects anchored to character
+void SwFrame::SetDrawObjsAsDeleted( bool bDeleted )
+{
+ if ( SwSortedObjs *pObjs = GetDrawObjs() )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ pFly->SetDeleted(bDeleted);
+ }
+ }
+ }
+}
+
+void SwFlyFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ //optimize thumbnail generation and store procedure to improve odt saving performance, #i120030#
+ SwViewShell *pShell = getRootFrame()->GetCurrShell();
+ if (pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocShell())
+ {
+ bool bInGenerateThumbnail = pShell->GetDoc()->GetDocShell()->IsInGenerateAndStoreThumbnail();
+ if (bInGenerateThumbnail)
+ {
+ const SwRect& aVisRect = pShell->VisArea();
+ if (!aVisRect.Overlaps(getFrameArea()))
+ return;
+ }
+ }
+
+ //because of the overlapping of frames and drawing objects the flys have to
+ //paint their borders (and those of the internal ones) directly.
+ //e.g. #33066#
+ gProp.pSLines->LockLines(true);
+ BorderLinesGuard blg; // this should not paint borders added from PaintBaBo
+
+ SwRect aRect( rRect );
+ aRect.Intersection_( getFrameArea() );
+
+ rRenderContext.Push( vcl::PushFlags::CLIPREGION );
+ rRenderContext.SetClipRegion();
+ const SwPageFrame* pPage = FindPageFrame();
+
+ const SwNoTextFrame *pNoText = Lower() && Lower()->IsNoTextFrame()
+ ? static_cast<const SwNoTextFrame*>(Lower()) : nullptr;
+
+ bool bIsChart = false; //#i102950# don't paint additional borders for charts
+ //check whether we have a chart
+ if(pNoText)
+ {
+ const SwNoTextNode* pNoTNd = dynamic_cast<const SwNoTextNode*>(pNoText->GetNode());
+ if( pNoTNd )
+ {
+ SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTNd->GetOLENode());
+ if( pOLENd && pOLENd->GetOLEObj().GetObject().IsChart() )
+ bIsChart = true;
+ }
+ }
+
+ {
+ bool bContour = GetFormat()->GetSurround().IsContour();
+ tools::PolyPolygon aPoly;
+ if ( bContour )
+ {
+ // add 2nd parameter with value <true>
+ // to indicate that method is called for paint in order to avoid
+ // load of the intrinsic graphic.
+ bContour = GetContour( aPoly, true );
+ }
+
+ // #i47804# - distinguish complete background paint
+ // and margin paint.
+ // paint complete background for Writer text fly frames
+ bool bPaintCompleteBack( !pNoText );
+ // paint complete background for transparent graphic and contour,
+ // if own background color exists.
+ const bool bIsGraphicTransparent = pNoText && pNoText->IsTransparent();
+ if ( !bPaintCompleteBack &&
+ ( bIsGraphicTransparent|| bContour ) )
+ {
+ const SwFlyFrameFormat* pSwFrameFormat = GetFormat();
+
+ if (pSwFrameFormat && pSwFrameFormat->supportsFullDrawingLayerFillAttributeSet())
+ {
+ // check for transparency
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes(pSwFrameFormat->getSdrAllFillAttributesHelper());
+
+ // check if the new fill attributes are used
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ bPaintCompleteBack = true;
+ }
+ }
+ else
+ {
+ std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
+ // to determine, if background has to be painted, by checking, if
+ // background color is not COL_TRANSPARENT ("no fill"/"auto fill")
+ // or a background graphic exists.
+ bPaintCompleteBack =
+ aBack->GetColor() != COL_TRANSPARENT ||
+ aBack->GetGraphicPos() != GPOS_NONE;
+ }
+ }
+ // paint of margin needed.
+ const bool bPaintMarginOnly( !bPaintCompleteBack &&
+ getFramePrintArea().SSize() != getFrameArea().SSize() );
+
+ // #i47804# - paint background of parent fly frame
+ // for transparent graphics in layer Hell, if parent fly frame isn't
+ // in layer Hell. It's only painted the intersection between the
+ // parent fly frame area and the paint area <aRect>
+ const IDocumentDrawModelAccess& rIDDMA = GetFormat()->getIDocumentDrawModelAccess();
+
+ if (bIsGraphicTransparent &&
+ GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS) &&
+ GetVirtDrawObj()->GetLayer() == rIDDMA.GetHellId() &&
+ GetAnchorFrame()->FindFlyFrame() )
+ {
+ const SwFlyFrame* pParentFlyFrame = GetAnchorFrame()->FindFlyFrame();
+ if ( pParentFlyFrame->GetDrawObj()->GetLayer() !=
+ rIDDMA.GetHellId() )
+ {
+ SwFlyFrame* pOldRet = gProp.pSRetoucheFly2;
+ gProp.pSRetoucheFly2 = const_cast<SwFlyFrame*>(this);
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pParentFlyFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ SwRect aPaintRect( aRect );
+ aPaintRect.Intersection_( pParentFlyFrame->getFrameArea() );
+ pParentFlyFrame->PaintSwFrameBackground( aPaintRect, pPage, rAttrs );
+
+ gProp.pSRetoucheFly2 = pOldRet;
+ }
+ }
+
+ if ( bPaintCompleteBack || bPaintMarginOnly )
+ {
+ //#24926# JP 01.02.96, PaintBaBo is here partially so PaintSwFrameShadowAndBorder
+ //receives the original Rect but PaintSwFrameBackground only the limited
+ //one.
+
+ rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rRenderContext.SetLineColor();
+
+ pPage = FindPageFrame();
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // paint background
+ {
+ SwRegionRects aRegion( aRect );
+ // #i80822#
+ // suppress painting of background in printing area for
+ // non-transparent graphics.
+ if ( bPaintMarginOnly ||
+ ( pNoText && !bIsGraphicTransparent ) )
+ {
+ //What we actually want to paint is the small stripe between
+ //PrtArea and outer border.
+ SwRect aTmp( getFramePrintArea() ); aTmp += getFrameArea().Pos();
+ aRegion -= aTmp;
+ }
+ if ( bContour )
+ {
+ rRenderContext.Push();
+ // #i80822#
+ // apply clip region under the same conditions, which are
+ // used in <SwNoTextFrame::PaintSwFrame(..)> to set the clip region
+ // for painting the graphic/OLE. Thus, the clip region is
+ // also applied for the PDF export.
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ if ( !rRenderContext.GetConnectMetaFile() || !pSh || !pSh->GetWin() )
+ {
+ rRenderContext.SetClipRegion(vcl::Region(aPoly));
+ }
+
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
+ }
+
+ rRenderContext.Pop();
+ }
+ else
+ {
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ PaintSwFrameBackground( aRegion[i], pPage, rAttrs, false, true );
+ }
+ }
+ }
+
+ // paint border before painting background
+ PaintSwFrameShadowAndBorder(rRect, pPage, rAttrs);
+
+ rRenderContext.Pop();
+ }
+ }
+
+ // fly frame will paint it's subsidiary lines and
+ // the subsidiary lines of its lowers on its own, due to overlapping with
+ // other fly frames or other objects.
+ if( gProp.pSGlobalShell->GetWin()
+ && !bIsChart ) //#i102950# don't paint additional borders for charts
+ {
+ bool bSubsLineRectsCreated;
+ if ( gProp.pSSubsLines )
+ {
+ // Lock already existing subsidiary lines
+ gProp.pSSubsLines->LockLines( true );
+ bSubsLineRectsCreated = false;
+ }
+ else
+ {
+ // create new subsidiary lines
+ gProp.pSSubsLines.reset(new SwSubsRects);
+ bSubsLineRectsCreated = true;
+ }
+
+ bool bSpecSubsLineRectsCreated;
+ if ( gProp.pSSpecSubsLines )
+ {
+ // Lock already existing special subsidiary lines
+ gProp.pSSpecSubsLines->LockLines( true );
+ bSpecSubsLineRectsCreated = false;
+ }
+ else
+ {
+ // create new special subsidiary lines
+ gProp.pSSpecSubsLines.reset(new SwSubsRects);
+ bSpecSubsLineRectsCreated = true;
+ }
+ // Add subsidiary lines of fly frame and its lowers
+ RefreshLaySubsidiary( pPage, aRect );
+ // paint subsidiary lines of fly frame and its lowers
+ gProp.pSSpecSubsLines->PaintSubsidiary( &rRenderContext, nullptr, gProp );
+ gProp.pSSubsLines->PaintSubsidiary(&rRenderContext, gProp.pSLines.get(), gProp);
+ if ( !bSubsLineRectsCreated )
+ // unlock subsidiary lines
+ gProp.pSSubsLines->LockLines( false );
+ else
+ {
+ // delete created subsidiary lines container
+ gProp.pSSubsLines.reset();
+ }
+
+ if ( !bSpecSubsLineRectsCreated )
+ // unlock special subsidiary lines
+ gProp.pSSpecSubsLines->LockLines( false );
+ else
+ {
+ // delete created special subsidiary lines container
+ gProp.pSSpecSubsLines.reset();
+ }
+ }
+
+ SwLayoutFrame::PaintSwFrame( rRenderContext, aRect );
+
+ Validate();
+
+ {
+ SwTaggedPDFHelper tag(nullptr, nullptr, nullptr, rRenderContext);
+ // first paint lines added by fly frame paint
+ // and then unlock other lines.
+ gProp.pSLines->PaintLines( &rRenderContext, gProp );
+ gProp.pSLines->LockLines( false );
+ // have to paint frame borders added in heaven layer here...
+ ProcessPrimitives(gProp.pBLines->GetBorderLines_Clear());
+ }
+
+ PaintDecorators();
+
+ // crossing out for tracked deletion
+ if ( GetAuthor() != std::string::npos && IsDeleted() )
+ {
+ tools::Long startX = aRect.Left( ), endX = aRect.Right();
+ tools::Long startY = aRect.Top( ), endY = aRect.Bottom();
+ rRenderContext.SetLineColor( SwPostItMgr::GetColorAnchor(GetAuthor()) );
+ rRenderContext.DrawLine(Point(startX, startY), Point(endX, endY));
+ rRenderContext.DrawLine(Point(startX, endY), Point(endX, startY));
+ }
+
+ rRenderContext.Pop();
+
+ if ( gProp.pSProgress && pNoText )
+ SfxProgress::Reschedule();
+}
+
+void SwFlyFrame::PaintDecorators() const
+{
+ // Show the un-float button
+ SwWrtShell* pWrtSh = dynamic_cast< SwWrtShell* >( gProp.pSGlobalShell );
+ if ( pWrtSh )
+ {
+ UpdateUnfloatButton(pWrtSh, IsShowUnfloatButton(pWrtSh));
+ }
+}
+
+void SwTextFrame::PaintOutlineContentVisibilityButton() const
+{
+ SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(gProp.pSGlobalShell);
+ if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton())
+ UpdateOutlineContentVisibilityButton(pWrtSh);
+}
+
+
+void SwTabFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ const SwViewOption* pViewOption = gProp.pSGlobalShell->GetViewOptions();
+ if (pViewOption->IsTable())
+ {
+ // #i29550#
+ if ( IsCollapsingBorders() )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), static_cast<SwFrame const *>(this) );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // paint shadow
+ if ( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
+ {
+ SwRect aRect;
+ ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
+ PaintShadow( rRect, aRect, rAttrs );
+ }
+
+ SwTabFramePainter aHelper(*this);
+ aHelper.PaintLines(rRenderContext, rRect);
+ }
+
+ SwLayoutFrame::PaintSwFrame( rRenderContext, rRect );
+ }
+ // #i6467# - no light grey rectangle for page preview
+ else if ( gProp.pSGlobalShell->GetWin() && !gProp.pSGlobalShell->IsPreview() )
+ {
+ // #i6467# - intersect output rectangle with table frame
+ SwRect aTabRect( getFramePrintArea() );
+ aTabRect.Pos() += getFrameArea().Pos();
+ SwRect aTabOutRect( rRect );
+ aTabOutRect.Intersection( aTabRect );
+ SwViewOption::DrawRect( &rRenderContext, aTabOutRect, COL_LIGHTGRAY );
+ }
+ const_cast<SwTabFrame*>(this)->ResetComplete();
+}
+
+/**
+ * Paint border shadow
+ *
+ * @param[in] rRect aligned rect to clip the result
+ * @param[in,out] rOutRect full painting area as input
+ * painting area reduced by shadow space for border and background as output
+ * @param[in] rShadow includes shadow attributes
+ * @param[in] bDrawFullShadowRectangle paint full rect of shadow
+ * @param[in] bTop paint top part of the shadow
+ * @param[in] bBottom paint bottom part of the shadow
+ * @param[in] bLeft paint left part of the shadow
+ * @param[in] bRight paint right part of the shadow
+**/
+static void lcl_PaintShadow( const SwRect& rRect, SwRect& rOutRect,
+ const SvxShadowItem& rShadow, const bool bDrawFullShadowRectangle,
+ const bool bTop, const bool bBottom,
+ const bool bLeft, const bool bRight,
+ SwPaintProperties const & properties)
+{
+ const tools::Long nWidth = ::lcl_AlignWidth ( rShadow.GetWidth(), properties );
+ const tools::Long nHeight = ::lcl_AlignHeight( rShadow.GetWidth(), properties );
+
+ SwRects aRegion;
+ SwRect aOut( rOutRect );
+
+ switch ( rShadow.GetLocation() )
+ {
+ case SvxShadowLocation::BottomRight:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Top( rOutRect.Top() + nHeight );
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bBottom )
+ {
+ aOut.Top( rOutRect.Bottom() - nHeight );
+ if( bLeft )
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bRight )
+ {
+ aOut.Left( rOutRect.Right() - nWidth );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ else
+ aOut.Top( rOutRect.Top() );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bRight )
+ rOutRect.AddRight(- nWidth );
+ if( bBottom )
+ rOutRect.AddBottom(- nHeight );
+ }
+ break;
+ case SvxShadowLocation::TopLeft:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bTop )
+ {
+ aOut.Bottom( rOutRect.Top() + nHeight );
+ if( bRight )
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bLeft )
+ {
+ aOut.Right( rOutRect.Left() + nWidth );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ else
+ aOut.Bottom( rOutRect.Bottom() );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bLeft )
+ rOutRect.AddLeft( nWidth );
+ if( bTop )
+ rOutRect.AddTop( nHeight );
+ }
+ break;
+ case SvxShadowLocation::TopRight:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Bottom( rOutRect.Bottom() - nHeight);
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bTop )
+ {
+ aOut.Bottom( rOutRect.Top() + nHeight );
+ if( bLeft )
+ aOut.Left( rOutRect.Left() + nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bRight )
+ {
+ aOut.Left( rOutRect.Right() - nWidth );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ else
+ aOut.Bottom( rOutRect.Bottom() );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bRight )
+ rOutRect.AddRight( - nWidth );
+ if( bTop )
+ rOutRect.AddTop( nHeight );
+ }
+ break;
+ case SvxShadowLocation::BottomLeft:
+ {
+ if ( bDrawFullShadowRectangle )
+ {
+ // draw full shadow rectangle
+ aOut.Top( rOutRect.Top() + nHeight );
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ else
+ {
+ if( bBottom )
+ {
+ aOut.Top( rOutRect.Bottom()- nHeight );
+ if( bRight )
+ aOut.Right( rOutRect.Right() - nWidth );
+ aRegion.push_back( aOut );
+ }
+ if( bLeft )
+ {
+ aOut.Right( rOutRect.Left() + nWidth );
+ if( bTop )
+ aOut.Top( rOutRect.Top() + nHeight );
+ else
+ aOut.Top( rOutRect.Top() );
+ if( bBottom )
+ aOut.Bottom( rOutRect.Bottom() - nHeight );
+ aRegion.push_back( aOut );
+ }
+ }
+
+ if( bLeft )
+ rOutRect.AddLeft( nWidth );
+ if( bBottom )
+ rOutRect.AddBottom( - nHeight );
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ vcl::RenderContext *pOut = properties.pSGlobalShell->GetOut();
+
+ DrawModeFlags nOldDrawMode = pOut->GetDrawMode();
+ Color aShadowColor( rShadow.GetColor().GetRGBColor() );
+ if( !aRegion.empty() && properties.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ // In high contrast mode, the output device has already set the
+ // DrawModeFlags::SettingsFill flag. This causes the SetFillColor function
+ // to ignore the setting of a new color. Therefore we have to reset
+ // the drawing mode
+ pOut->SetDrawMode( DrawModeFlags::Default );
+ aShadowColor = SwViewOption::GetFontColor();
+ }
+
+ if ( pOut->GetFillColor() != aShadowColor )
+ pOut->SetFillColor( aShadowColor );
+
+ pOut->SetLineColor();
+
+ pOut->SetDrawMode( nOldDrawMode );
+
+ for (const SwRect & rOut : aRegion)
+ {
+ aOut = rOut;
+ if ( rRect.Overlaps( aOut ) && aOut.Height() > 0 && aOut.Width() > 0 )
+ {
+ aOut.Intersection_( rRect );
+ pOut->DrawRect( aOut.SVRect() );
+ }
+ }
+}
+
+/**
+ * Paints a shadow if the format requests so.
+ *
+ * The shadow is always painted on the outer edge of the OutRect.
+ * If needed, the OutRect is shrunk so the painting of the border can be
+ * done on it.
+ *
+ * @note: draw full shadow rectangle for frames with transparent drawn backgrounds (OD 23.08.2002 #99657#)
+ */
+void SwFrame::PaintShadow( const SwRect& rRect, SwRect& rOutRect,
+ const SwBorderAttrs &rAttrs ) const
+{
+ SvxShadowItem rShadow = rAttrs.GetShadow();
+
+ const bool bCnt = IsContentFrame();
+ const bool bTop = !bCnt || rAttrs.GetTopLine ( *(this) );
+ const bool bBottom = !bCnt || rAttrs.GetBottomLine( *(this) );
+
+ if( IsVertical() )
+ {
+ switch( rShadow.GetLocation() )
+ {
+ case SvxShadowLocation::BottomRight: rShadow.SetLocation(SvxShadowLocation::BottomLeft); break;
+ case SvxShadowLocation::TopLeft: rShadow.SetLocation(SvxShadowLocation::TopRight); break;
+ case SvxShadowLocation::TopRight: rShadow.SetLocation(SvxShadowLocation::BottomRight); break;
+ case SvxShadowLocation::BottomLeft: rShadow.SetLocation(SvxShadowLocation::TopLeft); break;
+ default: break;
+ }
+ }
+
+ // determine, if full shadow rectangle have to be drawn or only two shadow rectangles beside the frame.
+ // draw full shadow rectangle, if frame background is drawn transparent.
+ // Status Quo:
+ // SwLayoutFrame can have transparent drawn backgrounds. Thus,
+ // "asked" their frame format.
+ const bool bDrawFullShadowRectangle =
+ ( IsLayoutFrame() &&
+ static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent()
+ );
+
+ SwRectFnSet aRectFnSet(this);
+ ::lcl_ExtendLeftAndRight( rOutRect, *(this), rAttrs, aRectFnSet.FnRect() );
+
+ lcl_PaintShadow(rRect, rOutRect, rShadow, bDrawFullShadowRectangle, bTop, bBottom, true, true, gProp);
+}
+
+void SwFrame::PaintBorderLine( const SwRect& rRect,
+ const SwRect& rOutRect,
+ const SwPageFrame * pPage,
+ const Color *pColor,
+ const SvxBorderLineStyle nStyle ) const
+{
+ if ( !rOutRect.Overlaps( rRect ) )
+ return;
+
+ SwRect aOut( rOutRect );
+ aOut.Intersection_( rRect );
+
+ const SwTabFrame *pTab = IsCellFrame() ? FindTabFrame() : nullptr;
+ SubColFlags nSubCol = ( IsCellFrame() || IsRowFrame() )
+ ? SubColFlags::Tab
+ : ( IsInSct()
+ ? SubColFlags::Sect
+ : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
+ if( pColor && gProp.pSGlobalShell->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ pColor = &SwViewOption::GetFontColor();
+ }
+
+ if (pPage->GetSortedObjs() &&
+ pPage->GetFormat()->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
+ {
+ SwRegionRects aRegion( aOut, 4 );
+ basegfx::utils::B2DClipState aClipState;
+ ::lcl_SubtractFlys( this, pPage, aOut, aRegion, aClipState, gProp );
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ gProp.pSLines->AddLineRect( aRegion[i], pColor, nStyle, pTab, nSubCol, gProp );
+ }
+ else
+ gProp.pSLines->AddLineRect( aOut, pColor, nStyle, pTab, nSubCol, gProp );
+}
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class SwBorderRectanglePrimitive2D : public BufferedDecompositionPrimitive2D
+ {
+ private:
+ /// the transformation defining the geometry of this BorderRectangle
+ basegfx::B2DHomMatrix maB2DHomMatrix;
+
+ /// the four styles to be used
+ svx::frame::Style maStyleTop;
+ svx::frame::Style maStyleRight;
+ svx::frame::Style maStyleBottom;
+ svx::frame::Style maStyleLeft;
+
+ protected:
+ /// local decomposition.
+ virtual void create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+ public:
+ /// constructor
+ SwBorderRectanglePrimitive2D(
+ const basegfx::B2DHomMatrix& rB2DHomMatrix,
+ const svx::frame::Style& rStyleTop,
+ const svx::frame::Style& rStyleRight,
+ const svx::frame::Style& rStyleBottom,
+ const svx::frame::Style& rStyleLeft);
+
+ /// data read access
+ const basegfx::B2DHomMatrix& getB2DHomMatrix() const { return maB2DHomMatrix; }
+ const svx::frame::Style& getStyleTop() const { return maStyleTop; }
+ const svx::frame::Style& getStyleRight() const { return maStyleRight; }
+ const svx::frame::Style& getStyleBottom() const { return maStyleBottom; }
+ const svx::frame::Style& getStyleLeft() const { return maStyleLeft; }
+
+ /// compare operator
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ /// get range
+ virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D& rViewInformation) const override;
+
+ /// provide unique ID
+ virtual sal_uInt32 getPrimitive2DID() const override;
+ };
+
+ }
+
+ void SwBorderRectanglePrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ basegfx::B2DPoint aTopLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 0.0));
+ basegfx::B2DPoint aTopRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 0.0));
+ basegfx::B2DPoint aBottomLeft(getB2DHomMatrix() * basegfx::B2DPoint(0.0, 1.0));
+ basegfx::B2DPoint aBottomRight(getB2DHomMatrix() * basegfx::B2DPoint(1.0, 1.0));
+
+ // prepare SdrFrameBorderDataVector
+ std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
+ std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
+
+ if(getStyleTop().IsUsed())
+ {
+ // move top left/right inwards half border width
+ basegfx::B2DVector aDown(getB2DHomMatrix() * basegfx::B2DVector(0.0, 1.0));
+ aDown.setLength(getStyleTop().GetWidth() * 0.5);
+ aTopLeft += aDown;
+ aTopRight += aDown;
+ }
+
+ if(getStyleBottom().IsUsed())
+ {
+ // move bottom left/right inwards half border width
+ basegfx::B2DVector aUp(getB2DHomMatrix() * basegfx::B2DVector(0.0, -1.0));
+ aUp.setLength(getStyleBottom().GetWidth() * 0.5);
+ aBottomLeft += aUp;
+ aBottomRight += aUp;
+ }
+
+ if(getStyleLeft().IsUsed())
+ {
+ // move left top/bottom inwards half border width
+ basegfx::B2DVector aRight(getB2DHomMatrix() * basegfx::B2DVector(1.0, 0.0));
+ aRight.setLength(getStyleLeft().GetWidth() * 0.5);
+ aTopLeft += aRight;
+ aBottomLeft += aRight;
+ }
+
+ if(getStyleRight().IsUsed())
+ {
+ // move right top/bottom inwards half border width
+ basegfx::B2DVector aLeft(getB2DHomMatrix() * basegfx::B2DVector(-1.0, 0.0));
+ aLeft.setLength(getStyleRight().GetWidth() * 0.5);
+ aTopRight += aLeft;
+ aBottomRight += aLeft;
+ }
+
+ // go round-robin, from TopLeft to TopRight, down, left and back up. That
+ // way, the borders will not need to be mirrored in any way
+ if(getStyleTop().IsUsed())
+ {
+ // create BorderPrimitive(s) for top border
+ const basegfx::B2DVector aVector(aTopRight - aTopLeft);
+ aData->emplace_back(
+ aTopLeft,
+ aVector,
+ getStyleTop(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleLeft().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleLeft(), basegfx::B2DVector(aBottomLeft - aTopLeft), false);
+ }
+
+ if(getStyleRight().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleRight(), basegfx::B2DVector(aBottomRight - aTopRight), false);
+ }
+ }
+
+ if(getStyleRight().IsUsed())
+ {
+ // create BorderPrimitive(s) for right border
+ const basegfx::B2DVector aVector(aBottomRight - aTopRight);
+ aData->emplace_back(
+ aTopRight,
+ aVector,
+ getStyleRight(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleTop().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleTop(), basegfx::B2DVector(aTopLeft - aTopRight), false);
+ }
+
+ if(getStyleBottom().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleBottom(), basegfx::B2DVector(aBottomLeft - aBottomRight), false);
+ }
+ }
+
+ if(getStyleBottom().IsUsed())
+ {
+ // create BorderPrimitive(s) for bottom border
+ const basegfx::B2DVector aVector(aBottomLeft - aBottomRight);
+ aData->emplace_back(
+ aBottomRight,
+ aVector,
+ getStyleBottom(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleRight().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleRight(), basegfx::B2DVector(aTopRight - aBottomRight), false);
+ }
+
+ if(getStyleLeft().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleLeft(), basegfx::B2DVector(aTopLeft - aBottomLeft), false);
+ }
+ }
+
+ if(getStyleLeft().IsUsed())
+ {
+ // create BorderPrimitive(s) for left border
+ const basegfx::B2DVector aVector(aTopLeft - aBottomLeft);
+ aData->emplace_back(
+ aBottomLeft,
+ aVector,
+ getStyleLeft(),
+ nullptr);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(aData->back());
+
+ if(getStyleBottom().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(true, getStyleBottom(), basegfx::B2DVector(aBottomRight - aBottomLeft), false);
+ }
+
+ if(getStyleTop().IsUsed())
+ {
+ rInstance.addSdrConnectStyleData(false, getStyleTop(), basegfx::B2DVector(aTopRight - aTopLeft), false);
+ }
+ }
+
+ // create instance of SdrFrameBorderPrimitive2D if
+ // SdrFrameBorderDataVector is used
+ if(!aData->empty())
+ {
+ rContainer.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
+ aData,
+ true))); // force visualization to minimal one discrete unit (pixel)
+ }
+ }
+
+ SwBorderRectanglePrimitive2D::SwBorderRectanglePrimitive2D(
+ const basegfx::B2DHomMatrix& rB2DHomMatrix,
+ const svx::frame::Style& rStyleTop,
+ const svx::frame::Style& rStyleRight,
+ const svx::frame::Style& rStyleBottom,
+ const svx::frame::Style& rStyleLeft)
+ : maB2DHomMatrix(rB2DHomMatrix),
+ maStyleTop(rStyleTop),
+ maStyleRight(rStyleRight),
+ maStyleBottom(rStyleBottom),
+ maStyleLeft(rStyleLeft)
+ {
+ }
+
+ bool SwBorderRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const SwBorderRectanglePrimitive2D& rCompare = static_cast<const SwBorderRectanglePrimitive2D&>(rPrimitive);
+
+ return (getB2DHomMatrix() == rCompare.getB2DHomMatrix() &&
+ getStyleTop() == rCompare.getStyleTop() &&
+ getStyleRight() == rCompare.getStyleRight() &&
+ getStyleBottom() == rCompare.getStyleBottom() &&
+ getStyleLeft() == rCompare.getStyleLeft());
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SwBorderRectanglePrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ basegfx::B2DRange aRetval(0.0, 0.0, 1.0, 1.0);
+
+ aRetval.transform(getB2DHomMatrix());
+ return aRetval;
+ }
+
+ // provide unique ID
+ sal_uInt32 SwBorderRectanglePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SWBORDERRECTANGLERIMITIVE;
+ }
+
+} // end of namespace drawinglayer::primitive2d
+
+namespace {
+
+editeng::SvxBorderLine const * get_ptr(std::optional<editeng::SvxBorderLine> const & opt) {
+ return opt ? &*opt : nullptr;
+}
+
+}
+
+void PaintCharacterBorder(
+ const SwFont& rFont,
+ const SwRect& rPaintArea,
+ const bool bVerticalLayout,
+ const bool bVerticalLayoutLRBT,
+ const bool bJoinWithPrev,
+ const bool bJoinWithNext )
+{
+ SwRect aAlignedRect(rPaintArea);
+ SwAlignRect(aAlignedRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
+
+ bool bTop = true;
+ bool bBottom = true;
+ bool bLeft = true;
+ bool bRight = true;
+
+ switch (rFont.GetOrientation(bVerticalLayout, bVerticalLayoutLRBT).get())
+ {
+ case 0 :
+ bLeft = !bJoinWithPrev;
+ bRight = !bJoinWithNext;
+ break;
+ case 900 :
+ bBottom = !bJoinWithPrev;
+ bTop = !bJoinWithNext;
+ break;
+ case 1800 :
+ bRight = !bJoinWithPrev;
+ bLeft = !bJoinWithNext;
+ break;
+ case 2700 :
+ bTop = !bJoinWithPrev;
+ bBottom = !bJoinWithNext;
+ break;
+ }
+
+ // Paint shadow (reduce painting rect)
+ {
+ const SvxShadowItem aShadow(
+ 0, &rFont.GetShadowColor(), rFont.GetShadowWidth(),
+ rFont.GetAbsShadowLocation(bVerticalLayout, bVerticalLayoutLRBT));
+
+ if( aShadow.GetLocation() != SvxShadowLocation::NONE )
+ {
+ lcl_PaintShadow( rPaintArea, aAlignedRect, aShadow,
+ false, bTop, bBottom, bLeft, bRight, gProp);
+ }
+ }
+
+ const basegfx::B2DHomMatrix aBorderTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aAlignedRect.Width(), aAlignedRect.Height(),
+ aAlignedRect.Left(), aAlignedRect.Top()));
+ const svx::frame::Style aStyleTop(
+ bTop ? get_ptr(rFont.GetAbsTopBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
+ 1.0);
+ const svx::frame::Style aStyleRight(
+ bRight ? get_ptr(rFont.GetAbsRightBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
+ 1.0);
+ const svx::frame::Style aStyleBottom(
+ bBottom ? get_ptr(rFont.GetAbsBottomBorder(bVerticalLayout, bVerticalLayoutLRBT))
+ : nullptr,
+ 1.0);
+ const svx::frame::Style aStyleLeft(
+ bLeft ? get_ptr(rFont.GetAbsLeftBorder(bVerticalLayout, bVerticalLayoutLRBT)) : nullptr,
+ 1.0);
+ drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
+
+ aBorderLineTarget.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
+ aBorderTransform,
+ aStyleTop,
+ aStyleRight,
+ aStyleBottom,
+ aStyleLeft)));
+ gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
+}
+
+/// #i15844#
+static const SwFrame* lcl_HasNextCell( const SwFrame& rFrame )
+{
+ OSL_ENSURE( rFrame.IsCellFrame(),
+ "lcl_HasNextCell( const SwFrame& rFrame ) should be called with SwCellFrame" );
+
+ const SwFrame* pTmpFrame = &rFrame;
+ do
+ {
+ if ( pTmpFrame->GetNext() )
+ return pTmpFrame->GetNext();
+
+ pTmpFrame = pTmpFrame->GetUpper()->GetUpper();
+ }
+ while ( pTmpFrame->IsCellFrame() );
+
+ return nullptr;
+}
+
+/**
+ * Determine cell frame, from which the border attributes
+ * for paint of top/bottom border has to be used.
+ *
+ * OD 21.02.2003 #b4779636#, #107692#
+ *
+ * @param _pCellFrame
+ * input parameter - constant pointer to cell frame for which the cell frame
+ * for the border attributes has to be determined.
+ *
+ * @param _rCellBorderAttrs
+ * input parameter - constant reference to the border attributes of cell frame
+ * <_pCellFrame>.
+ *
+ * @param _bTop
+ * input parameter - boolean, that controls, if cell frame for top border or
+ * for bottom border has to be determined.
+ *
+ * @return constant pointer to cell frame, for which the border attributes has
+ * to be used
+ */
+static const SwFrame* lcl_GetCellFrameForBorderAttrs( const SwFrame* _pCellFrame,
+ const SwBorderAttrs& _rCellBorderAttrs,
+ const bool _bTop )
+{
+ OSL_ENSURE( _pCellFrame, "No cell frame available, dying soon" );
+
+ // determine, if cell frame is at bottom/top border of a table frame and
+ // the table frame has/is a follow.
+ const SwFrame* pTmpFrame = _pCellFrame;
+ bool bCellAtBorder = true;
+ bool bCellAtLeftBorder = !_pCellFrame->GetPrev();
+ bool bCellAtRightBorder = !_pCellFrame->GetNext();
+ while( !pTmpFrame->IsRowFrame() || !pTmpFrame->GetUpper()->IsTabFrame() )
+ {
+ pTmpFrame = pTmpFrame->GetUpper();
+ if ( pTmpFrame->IsRowFrame() &&
+ (_bTop ? pTmpFrame->GetPrev() : pTmpFrame->GetNext())
+ )
+ {
+ bCellAtBorder = false;
+ }
+ if ( pTmpFrame->IsCellFrame() )
+ {
+ if ( pTmpFrame->GetPrev() )
+ {
+ bCellAtLeftBorder = false;
+ }
+ if ( pTmpFrame->GetNext() )
+ {
+ bCellAtRightBorder = false;
+ }
+ }
+ }
+ OSL_ENSURE( pTmpFrame && pTmpFrame->IsRowFrame(), "No RowFrame available" );
+
+ const SwLayoutFrame* pParentRowFrame = static_cast<const SwLayoutFrame*>(pTmpFrame);
+ const SwTabFrame* pParentTabFrame =
+ static_cast<const SwTabFrame*>(pParentRowFrame->GetUpper());
+
+ const bool bCellNeedsAttribute = bCellAtBorder &&
+ ( _bTop ?
+ // bCellInFirstRowWithMaster
+ ( !pParentRowFrame->GetPrev() &&
+ pParentTabFrame->IsFollow() &&
+ 0 == pParentTabFrame->GetTable()->GetRowsToRepeat() ) :
+ // bCellInLastRowWithFollow
+ ( !pParentRowFrame->GetNext() &&
+ pParentTabFrame->GetFollow() )
+ );
+
+ const SwFrame* pRet = _pCellFrame;
+ if ( bCellNeedsAttribute )
+ {
+ // determine, if cell frame has no borders inside the table.
+ const SwFrame* pNextCell = nullptr;
+ bool bNoBordersInside = false;
+
+ if ( bCellAtLeftBorder && ( nullptr != ( pNextCell = lcl_HasNextCell( *_pCellFrame ) ) ) )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pNextCell );
+ const SwBorderAttrs &rBorderAttrs = *aAccess.Get();
+ const SvxBoxItem& rBorderBox = rBorderAttrs.GetBox();
+ bCellAtRightBorder = !lcl_HasNextCell( *pNextCell );
+ bNoBordersInside =
+ ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
+ !rBorderBox.GetLeft() &&
+ ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
+ ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
+ }
+ else
+ {
+ const SvxBoxItem& rBorderBox = _rCellBorderAttrs.GetBox();
+ bNoBordersInside =
+ ( !rBorderBox.GetTop() || !pParentRowFrame->GetPrev() ) &&
+ ( !rBorderBox.GetLeft() || bCellAtLeftBorder ) &&
+ ( !rBorderBox.GetRight() || bCellAtRightBorder ) &&
+ ( !rBorderBox.GetBottom() || !pParentRowFrame->GetNext() );
+ }
+
+ if ( bNoBordersInside )
+ {
+ if ( _bTop && !_rCellBorderAttrs.GetBox().GetTop() )
+ {
+ //-hack
+ // Cell frame has no top border and no border inside the table, but
+ // it is at the top border of a table frame, which is a follow.
+ // Thus, use border attributes of cell frame in first row of complete table.
+ // First, determine first table frame of complete table.
+ SwTabFrame* pMasterTabFrame = pParentTabFrame->FindMaster( true );
+ // determine first row of complete table.
+ const SwFrame* pFirstRow = pMasterTabFrame->GetLower();
+ // return first cell in first row
+ SwFrame* pLowerCell = const_cast<SwFrame*>(pFirstRow->GetLower());
+ while ( !pLowerCell->IsCellFrame() ||
+ ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
+ )
+ {
+ pLowerCell = pLowerCell->GetLower();
+ }
+ OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
+ pRet = pLowerCell;
+ }
+ else if ( !_bTop && !_rCellBorderAttrs.GetBox().GetBottom() )
+ {
+ //-hack
+ // Cell frame has no bottom border and no border inside the table,
+ // but it is at the bottom border of a table frame, which has a follow.
+ // Thus, use border attributes of cell frame in last row of complete table.
+ // First, determine last table frame of complete table.
+ SwTabFrame* pLastTabFrame = const_cast<SwTabFrame*>(pParentTabFrame->GetFollow());
+ while ( pLastTabFrame->GetFollow() )
+ {
+ pLastTabFrame = pLastTabFrame->GetFollow();
+ }
+ // determine last row of complete table.
+ SwFrame* pLastRow = pLastTabFrame->GetLastLower();
+ // return first bottom border cell in last row
+ SwFrame* pLowerCell = pLastRow->GetLower();
+ while ( !pLowerCell->IsCellFrame() ||
+ ( pLowerCell->GetLower() && pLowerCell->GetLower()->IsRowFrame() )
+ )
+ {
+ if ( pLowerCell->IsRowFrame() )
+ {
+ while ( pLowerCell->GetNext() )
+ {
+ pLowerCell = pLowerCell->GetNext();
+ }
+ }
+ pLowerCell = pLowerCell->GetLower();
+ }
+ OSL_ENSURE( pLowerCell && pLowerCell->IsCellFrame(), "No CellFrame available" );
+ pRet = pLowerCell;
+ }
+ }
+ }
+
+ return pRet;
+}
+
+std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> SwFrame::CreateProcessor2D( ) const
+{
+ basegfx::B2DRange aViewRange;
+
+ SdrPage *pDrawPage = getRootFrame()->GetCurrShell()->Imp()->GetPageView()->GetPage();
+ const drawinglayer::geometry::ViewInformation2D aNewViewInfos(
+ basegfx::B2DHomMatrix( ),
+ getRootFrame()->GetCurrShell()->GetOut()->GetViewTransformation(),
+ aViewRange,
+ GetXDrawPageForSdrPage( pDrawPage ),
+ 0.0);
+
+ return drawinglayer::processor2d::createBaseProcessor2DFromOutputDevice(
+ *getRootFrame()->GetCurrShell()->GetOut(),
+ aNewViewInfos );
+}
+
+void SwFrame::ProcessPrimitives( const drawinglayer::primitive2d::Primitive2DContainer& rSequence ) const
+{
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D = CreateProcessor2D();
+ if ( pProcessor2D )
+ {
+ pProcessor2D->process( rSequence );
+ }
+}
+
+/// Paints shadows and borders
+void SwFrame::PaintSwFrameShadowAndBorder(
+ const SwRect& rRect,
+ const SwPageFrame* /*pPage*/,
+ const SwBorderAttrs& rAttrs) const
+{
+ // There's nothing (Row,Body,Footnote,Root,Column,NoText) need to do here
+ if (GetType() & (SwFrameType::NoTxt|SwFrameType::Row|SwFrameType::Body|SwFrameType::Ftn|SwFrameType::Column|SwFrameType::Root))
+ return;
+
+ if (IsCellFrame() && !gProp.pSGlobalShell->GetViewOptions()->IsTable())
+ return;
+
+ // #i29550#
+ if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
+ {
+ const SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->IsCollapsingBorders() )
+ return;
+
+ if ( pTabFrame->GetTable()->IsNewModel() && ( !IsCellFrame() || IsCoveredCell() ) )
+ return;
+ }
+
+ const bool bLine = rAttrs.IsLine();
+ const bool bShadow = rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE;
+
+ // - flag to control,
+ //-hack has to be used.
+ const bool bb4779636HackActive = true;
+
+ const SwFrame* pCellFrameForBottomBorderAttrs = nullptr;
+ const SwFrame* pCellFrameForTopBorderAttrs = nullptr;
+ bool bFoundCellForTopOrBorderAttrs = false;
+ if ( bb4779636HackActive && IsCellFrame() )
+ {
+ pCellFrameForBottomBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, false );
+ if ( pCellFrameForBottomBorderAttrs != this )
+ bFoundCellForTopOrBorderAttrs = true;
+ pCellFrameForTopBorderAttrs = lcl_GetCellFrameForBorderAttrs( this, rAttrs, true );
+ if ( pCellFrameForTopBorderAttrs != this )
+ bFoundCellForTopOrBorderAttrs = true;
+ }
+
+ // - add condition <bFoundCellForTopOrBorderAttrs>
+ //-hack
+ if ( !(bLine || bShadow || bFoundCellForTopOrBorderAttrs) )
+ return;
+
+ //If the rectangle is completely inside the PrtArea, no border needs to
+ //be painted.
+ //For the PrtArea the aligned value needs to be used, otherwise it could
+ //happen, that some parts won't be processed.
+ SwRect aRect( getFramePrintArea() );
+ aRect += getFrameArea().Pos();
+ ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ // new local boolean variable in order to
+ // suspend border paint under special cases - see below.
+ // NOTE: This is a fix for the implementation of feature #99657#.
+ bool bDrawOnlyShadowForTransparentFrame = false;
+ if ( aRect.Contains( rRect ) )
+ {
+ // paint shadow, if background is transparent.
+ // Because of introduced transparent background for fly frame #99657#,
+ // the shadow have to be drawn if the background is transparent,
+ // in spite the fact that the paint rectangle <rRect> lies fully
+ // in the printing area.
+ // NOTE to chosen solution:
+ // On transparent background, continue processing, but suspend
+ // drawing of border by setting <bDrawOnlyShadowForTransparentFrame>
+ // to true.
+ if ( IsLayoutFrame() &&
+ static_cast<const SwLayoutFrame*>(this)->GetFormat()->IsBackgroundTransparent() )
+ {
+ bDrawOnlyShadowForTransparentFrame = true;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ ::lcl_CalcBorderRect( aRect, this, rAttrs, true, gProp );
+ rAttrs.SetGetCacheLine( true );
+
+ if(bShadow)
+ {
+ PaintShadow(rRect, aRect, rAttrs);
+ }
+
+ // suspend drawing of border
+ // add condition < NOT bDrawOnlyShadowForTransparentFrame > - see above
+ // - add condition <bFoundCellForTopOrBorderAttrs>
+ //-hack.
+ if((bLine || bFoundCellForTopOrBorderAttrs) && !bDrawOnlyShadowForTransparentFrame)
+ {
+ // define SvxBorderLine(s) to use
+ const SvxBoxItem& rBox(rAttrs.GetBox());
+ const SvxBorderLine* pLeftBorder(rBox.GetLeft());
+ const SvxBorderLine* pRightBorder(rBox.GetRight());
+ const SvxBorderLine* pTopBorder(rBox.GetTop());
+ const SvxBorderLine* pBottomBorder(rBox.GetBottom());
+
+ // if R2L, exchange Right/Left
+ const bool bR2L(IsCellFrame() && IsRightToLeft());
+
+ if(bR2L)
+ {
+ std::swap(pLeftBorder, pRightBorder);
+ }
+
+ // if ContentFrame and joined Prev/Next, reset top/bottom as needed
+ if(IsContentFrame())
+ {
+ const SwFrame* pDirRefFrame(IsCellFrame() ? FindTabFrame() : this);
+ const SwRectFnSet aRectFnSet(pDirRefFrame);
+ const SwRectFn& _rRectFn(aRectFnSet.FnRect());
+
+ if(rAttrs.JoinedWithPrev(*this))
+ {
+ // tdf#115296 re-add adaptation of vert distance to close the evtl.
+ // existing gap to previous frame
+ const SwFrame* pPrevFrame(GetPrev());
+ (aRect.*_rRectFn->fnSetTop)( (pPrevFrame->*_rRectFn->fnGetPrtBottom)() );
+
+ // ...and disable top border paint/creation
+ pTopBorder = nullptr;
+ }
+
+ if(rAttrs.JoinedWithNext(*this))
+ {
+ // tdf#115296 re-add adaptation of vert distance to close the evtl.
+ // existing gap to next frame
+ const SwFrame* pNextFrame(GetNext());
+ (aRect.*_rRectFn->fnSetBottom)( (pNextFrame->*_rRectFn->fnGetPrtTop)() );
+
+ // ...and disable bottom border paint/creation
+ pBottomBorder = nullptr;
+ }
+ }
+
+ // necessary to replace TopBorder?
+ if((!IsContentFrame() || rAttrs.GetTopLine(*this)) && IsCellFrame() && pCellFrameForTopBorderAttrs != this)
+ {
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForTopBorderAttrs);
+ pTopBorder = aAccess.Get()->GetBox().GetTop();
+ }
+
+ // necessary to replace BottomBorder?
+ if((!IsContentFrame() || rAttrs.GetBottomLine(*this)) && IsCellFrame() && pCellFrameForBottomBorderAttrs != this)
+ {
+ SwBorderAttrAccess aAccess(SwFrame::GetCache(), pCellFrameForBottomBorderAttrs);
+ pBottomBorder = aAccess.Get()->GetBox().GetBottom();
+ }
+
+ bool bWordBorder = false;
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+ if (pShell)
+ {
+ const IDocumentSettingAccess& rIDSA = pShell->GetDoc()->getIDocumentSettingAccess();
+ bWordBorder = rIDSA.get(DocumentSettingId::TABLE_ROW_KEEP);
+ }
+ bool bInWordTableCell = IsContentFrame() && GetUpper()->IsCellFrame() && bWordBorder;
+ if (bInWordTableCell)
+ {
+ // Compat mode: don't paint bottom border if we know the bottom of the content was cut
+ // off.
+ auto pContentFrame = static_cast<const SwContentFrame*>(this);
+ if (pContentFrame->IsUndersized())
+ {
+ pBottomBorder = nullptr;
+ }
+ }
+
+ if(nullptr != pLeftBorder || nullptr != pRightBorder || nullptr != pTopBorder || nullptr != pBottomBorder)
+ {
+ // now we have all SvxBorderLine(s) sorted out, create geometry
+ const basegfx::B2DHomMatrix aBorderTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aRect.Width(), aRect.Height(),
+ aRect.Left(), aRect.Top()));
+ const svx::frame::Style aStyleTop(pTopBorder, 1.0);
+ svx::frame::Style aStyleRight(pRightBorder, 1.0);
+
+ // Right/bottom page borders are always mirrored in Word.
+ if (IsPageFrame() && bWordBorder)
+ {
+ aStyleRight.MirrorSelf();
+ }
+
+ svx::frame::Style aStyleBottom(pBottomBorder, 1.0);
+
+ if (IsPageFrame() && bWordBorder)
+ {
+ aStyleBottom.MirrorSelf();
+ }
+
+ const svx::frame::Style aStyleLeft(pLeftBorder, 1.0);
+ drawinglayer::primitive2d::Primitive2DContainer aBorderLineTarget;
+
+ drawinglayer::primitive2d::Primitive2DReference aRetval(
+ new drawinglayer::primitive2d::SwBorderRectanglePrimitive2D(
+ aBorderTransform,
+ aStyleTop,
+ aStyleRight,
+ aStyleBottom,
+ aStyleLeft));
+
+ if (bInWordTableCell)
+ {
+ // Compat mode: cut off the borders which are outside of our own area.
+ const SwRect& rClip = getFrameArea();
+ basegfx::B2DRectangle aClip(rClip.Left(), rClip.Top(), rClip.Right(),
+ rClip.Bottom());
+ const basegfx::B2DPolyPolygon aPolyPolygon(
+ basegfx::utils::createPolygonFromRect(aClip));
+ const drawinglayer::primitive2d::Primitive2DReference xClipped(
+ new drawinglayer::primitive2d::MaskPrimitive2D(aPolyPolygon, { aRetval }));
+ aRetval = xClipped;
+ }
+
+ aBorderLineTarget.append(aRetval);
+ gProp.pBLines->AddBorderLines(std::move(aBorderLineTarget));
+ }
+ }
+
+ rAttrs.SetGetCacheLine( false );
+}
+
+/**
+ * Special implementation because of the footnote line
+ *
+ * Currently only the top frame needs to be taken into account
+ * Other lines and shadows are set aside
+ */
+void SwFootnoteContFrame::PaintSwFrameShadowAndBorder(
+ const SwRect& rRect,
+ const SwPageFrame* pPage,
+ const SwBorderAttrs&) const
+{
+ //If the rectangle is completely inside the PrtArea, no border needs to
+ //be painted.
+ SwRect aRect( getFramePrintArea() );
+ aRect.Pos() += getFrameArea().Pos();
+ if ( !aRect.Contains( rRect ) )
+ PaintLine( rRect, pPage );
+}
+
+/// Paint footnote lines.
+void SwFootnoteContFrame::PaintLine( const SwRect& rRect,
+ const SwPageFrame *pPage ) const
+{
+ //The length of the line is derived from the percentual indication on the
+ //PageDesc. The position is also stated on the PageDesc.
+ //The pen can directly be taken from the PageDesc.
+
+ if ( !pPage )
+ pPage = FindPageFrame();
+ const SwPageFootnoteInfo &rInf = pPage->GetPageDesc()->GetFootnoteInfo();
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
+ Fraction aFract( nPrtWidth, 1 );
+ aFract *= rInf.GetWidth();
+ const SwTwips nWidth = static_cast<tools::Long>(aFract);
+
+ SwTwips nX = aRectFnSet.GetPrtLeft(*this);
+ switch ( rInf.GetAdj() )
+ {
+ case css::text::HorizontalAdjust_CENTER:
+ nX += nPrtWidth/2 - nWidth/2; break;
+ case css::text::HorizontalAdjust_RIGHT:
+ nX += nPrtWidth - nWidth; break;
+ case css::text::HorizontalAdjust_LEFT:
+ /* do nothing */; break;
+ default:
+ SAL_WARN("sw.core", "New adjustment for footnote lines?");
+ assert(false);
+ }
+ SwTwips nLineWidth = rInf.GetLineWidth();
+ const SwRect aLineRect = aRectFnSet.IsVert() ?
+ SwRect( Point(getFrameArea().Left()+getFrameArea().Width()-rInf.GetTopDist()-nLineWidth,
+ nX), Size( nLineWidth, nWidth ) )
+ : SwRect( Point( nX, getFrameArea().Pos().Y() + rInf.GetTopDist() ),
+ Size( nWidth, rInf.GetLineWidth()));
+ if ( aLineRect.HasArea() && rInf.GetLineStyle() != SvxBorderLineStyle::NONE)
+ PaintBorderLine( rRect, aLineRect , pPage, &rInf.GetLineColor(),
+ rInf.GetLineStyle() );
+}
+
+/// Paints the separator line for inside columns
+void SwLayoutFrame::PaintColLines( const SwRect &rRect, const SwFormatCol &rFormatCol,
+ const SwPageFrame *pPage ) const
+{
+ const SwFrame *pCol = Lower();
+ if ( !pCol || !pCol->IsColumnFrame() )
+ return;
+
+ SwRectFn fnRect = pCol->IsVertical() ? ( pCol->IsVertLR() ? (pCol->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ SwRect aLineRect = getFramePrintArea();
+ aLineRect += getFrameArea().Pos();
+
+ SwTwips nTop = ((aLineRect.*fnRect->fnGetHeight)()*rFormatCol.GetLineHeight())
+ / 100 - (aLineRect.*fnRect->fnGetHeight)();
+ SwTwips nBottom = 0;
+
+ switch ( rFormatCol.GetLineAdj() )
+ {
+ case COLADJ_CENTER:
+ nBottom = nTop / 2; nTop -= nBottom; break;
+ case COLADJ_TOP:
+ nBottom = nTop; nTop = 0; break;
+ case COLADJ_BOTTOM:
+ break;
+ default:
+ OSL_ENSURE( false, "New adjustment for column lines?" );
+ }
+
+ if( nTop )
+ (aLineRect.*fnRect->fnSubTop)( nTop );
+ if( nBottom )
+ (aLineRect.*fnRect->fnAddBottom)( nBottom );
+
+ SwTwips nPenHalf = rFormatCol.GetLineWidth();
+ (aLineRect.*fnRect->fnSetWidth)( nPenHalf );
+ nPenHalf /= 2;
+
+ //We need to be a bit generous here, to not lose something.
+ SwRect aRect( rRect );
+ (aRect.*fnRect->fnSubLeft)( nPenHalf + gProp.nSPixelSzW );
+ (aRect.*fnRect->fnAddRight)( nPenHalf + gProp.nSPixelSzW );
+ SwRectGet fnGetX = IsRightToLeft() ? fnRect->fnGetLeft : fnRect->fnGetRight;
+ while ( pCol->GetNext() )
+ {
+ (aLineRect.*fnRect->fnSetPosX)
+ ( (pCol->getFrameArea().*fnGetX)() - nPenHalf );
+ if ( aRect.Overlaps( aLineRect ) )
+ PaintBorderLine( aRect, aLineRect , pPage, &rFormatCol.GetLineColor(),
+ rFormatCol.GetLineStyle() );
+ pCol = pCol->GetNext();
+ }
+}
+
+void SwPageFrame::PaintGrid( OutputDevice const * pOut, SwRect const &rRect ) const
+{
+ if( !m_bHasGrid || gProp.pSRetoucheFly || gProp.pSRetoucheFly2 )
+ return;
+ SwTextGridItem const*const pGrid(GetGridItem(this));
+ if( !(pGrid && ( OUTDEV_PRINTER != pOut->GetOutDevType() ?
+ pGrid->GetDisplayGrid() : pGrid->GetPrintGrid() )) )
+ return;
+
+ const SwLayoutFrame* pBody = FindBodyCont();
+ if( !pBody )
+ return;
+
+ SwRect aGrid( pBody->getFramePrintArea() );
+ aGrid += pBody->getFrameArea().Pos();
+
+ SwRect aInter( aGrid );
+ aInter.Intersection( rRect );
+ if( !aInter.HasArea() )
+ return;
+
+ bool bGrid = pGrid->GetRubyTextBelow();
+ bool bCell = GRID_LINES_CHARS == pGrid->GetGridType();
+ tools::Long nGrid = pGrid->GetBaseHeight();
+ const SwDoc* pDoc = GetFormat()->GetDoc();
+ tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
+ tools::Long nRuby = pGrid->GetRubyHeight();
+ tools::Long nSum = nGrid + nRuby;
+ const Color *pCol = &pGrid->GetColor();
+
+ SwTwips nRight = aInter.Left() + aInter.Width();
+ SwTwips nBottom = aInter.Top() + aInter.Height();
+ if( IsVertical() )
+ {
+ SwTwips nOrig = aGrid.Left() + aGrid.Width();
+ SwTwips nY = nOrig + nSum *
+ ( ( nOrig - aInter.Left() ) / nSum );
+ SwRect aTmp( Point( nY, aInter.Top() ),
+ Size( 1, aInter.Height() ) );
+ SwTwips nX = aGrid.Top() + nGrid *
+ ( ( aInter.Top() - aGrid.Top() )/ nGrid );
+ if( nX < aInter.Top() )
+ nX += nGrid;
+ SwTwips nGridBottom = aGrid.Top() + aGrid.Height();
+ bool bLeft = aGrid.Top() >= aInter.Top();
+ bool bRight = nGridBottom <= nBottom;
+ bool bBorder = bLeft || bRight;
+ while( nY > nRight )
+ {
+ aTmp.Pos().setX( nY );
+ if( bGrid )
+ {
+ nY -= nGrid;
+ SwTwips nPosY = std::max( SwTwips(aInter.Left()), nY );
+ SwTwips nHeight = std::min(nRight, SwTwips(aTmp.Pos().X()))-nPosY;
+ if( nHeight > 0 )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point( nPosY, nX ),
+ Size( nHeight, 1 ) );
+ while( aVert.Top() <= nBottom )
+ {
+ PaintBorderLine(rRect,aVert,this,pCol);
+ aVert.Pos().AdjustY(nGrid );
+ }
+ }
+ else if( bBorder )
+ {
+ SwRect aVert( Point( nPosY, aGrid.Top() ),
+ Size( nHeight, 1 ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY -= nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
+ SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
+ SwRect aVert( Point( nPos, aGrid.Top() ),
+ Size( nW, 1 ) );
+ if( nW > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ while( nY >= aInter.Left() )
+ {
+ aTmp.Pos().setX( nY );
+ PaintBorderLine( rRect, aTmp, this, pCol);
+ if( bGrid )
+ {
+ nY -= nGrid;
+ SwTwips nHeight = aTmp.Pos().X()
+ - std::max(SwTwips(aInter.Left()), nY );
+ if( nHeight > 0 )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point(aTmp.Pos().X()-nHeight,
+ nX ), Size( nHeight, 1 ) );
+ while( aVert.Top() <= nBottom )
+ {
+ PaintBorderLine(rRect,aVert,this,pCol);
+ aVert.Pos().AdjustY(nGrid );
+ }
+ }
+ else if( bBorder )
+ {
+ SwRect aVert( Point(aTmp.Pos().X()-nHeight,
+ aGrid.Top() ), Size( nHeight, 1 ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY -= nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max( SwTwips(aInter.Left()), nY );
+ SwTwips nW = std::min(nRight, SwTwips(aTmp.Pos().X())) - nPos;
+ SwRect aVert( Point( nPos, aGrid.Top() ),
+ Size( nW, 1 ) );
+ if( nW > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setY( nGridBottom );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ }
+ else
+ {
+ SwTwips nOrig = aGrid.Top();
+ SwTwips nY = nOrig + nSum *( (aInter.Top()-nOrig)/nSum );
+ SwRect aTmp( Point( aInter.Left(), nY ),
+ Size( aInter.Width(), 1 ) );
+ //for textgrid refactor
+ SwTwips nX = aGrid.Left() + nGridWidth *
+ ( ( aInter.Left() - aGrid.Left() )/ nGridWidth );
+ if( nX < aInter.Left() )
+ nX += nGridWidth;
+ SwTwips nGridRight = aGrid.Left() + aGrid.Width();
+ bool bLeft = aGrid.Left() >= aInter.Left();
+ bool bRight = nGridRight <= nRight;
+ bool bBorder = bLeft || bRight;
+ while( nY < aInter.Top() )
+ {
+ aTmp.Pos().setY(nY);
+ if( bGrid )
+ {
+ nY += nGrid;
+ SwTwips nPosY = std::max( aInter.Top(), aTmp.Pos().getY() );
+ SwTwips nHeight = std::min(nBottom, nY ) - nPosY;
+ if( nHeight )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point( nX, nPosY ),
+ Size( 1, nHeight ) );
+ while( aVert.Left() <= nRight )
+ {
+ PaintBorderLine(rRect,aVert,this,pCol);
+ aVert.Pos().AdjustX(nGridWidth ); //for textgrid refactor
+ }
+ }
+ else if ( bBorder )
+ {
+ SwRect aVert( Point( aGrid.Left(), nPosY ),
+ Size( 1, nHeight ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX( nGridRight );
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY += nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().getY());
+ SwTwips nH = std::min( nBottom, nY ) - nPos;
+ SwRect aVert( Point( aGrid.Left(), nPos ),
+ Size( 1, nH ) );
+ if( nH > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX(nGridRight);
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ while( nY <= nBottom )
+ {
+ aTmp.Pos().setY(nY);
+ PaintBorderLine( rRect, aTmp, this, pCol);
+ if( bGrid )
+ {
+ nY += nGrid;
+ SwTwips nHeight = std::min(nBottom, nY) - aTmp.Pos().getY();
+ if( nHeight )
+ {
+ if( bCell )
+ {
+ SwRect aVert( Point( nX, aTmp.Pos().getY() ),
+ Size( 1, nHeight ) );
+ while( aVert.Left() <= nRight )
+ {
+ PaintBorderLine( rRect, aVert, this, pCol);
+ aVert.Pos().setX(aVert.Pos().getX() + nGridWidth); //for textgrid refactor
+ }
+ }
+ else if( bBorder )
+ {
+ SwRect aVert( Point( aGrid.Left(),
+ aTmp.Pos().getY() ), Size( 1, nHeight ) );
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX(nGridRight);
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ else
+ {
+ nY += nRuby;
+ if( bBorder )
+ {
+ SwTwips nPos = std::max(aInter.Top(),aTmp.Pos().Y());
+ SwTwips nH = std::min( nBottom, nY ) - nPos;
+ SwRect aVert( Point( aGrid.Left(), nPos ),
+ Size( 1, nH ) );
+ if( nH > 0 )
+ {
+ if( bLeft )
+ PaintBorderLine(rRect,aVert,this,pCol);
+ if( bRight )
+ {
+ aVert.Pos().setX(nGridRight);
+ PaintBorderLine(rRect,aVert,this,pCol);
+ }
+ }
+ }
+ }
+ bGrid = !bGrid;
+ }
+ }
+}
+
+/**
+ * Paint margin area of a page
+ *
+ * OD 20.11.2002 for #104598#:
+ * implement paint of margin area; margin area will be painted for a
+ * view shell with a window and if the document is not in online layout.
+ *
+ * @param _rOutputRect
+ * input parameter - constant instance reference of the rectangle, for
+ * which an output has to be generated.
+ *
+ * @param _pViewShell
+ * input parameter - instance of the view shell, on which the output
+ * has to be generated.
+ */
+void SwPageFrame::PaintMarginArea( const SwRect& _rOutputRect,
+ SwViewShell const * _pViewShell ) const
+{
+ if ( !_pViewShell->GetWin() || _pViewShell->GetViewOptions()->getBrowseMode() )
+ return;
+
+ // Simplified paint with DrawingLayer FillStyle
+ SwRect aPgRect = getFrameArea();
+ aPgRect.Intersection_( _rOutputRect );
+
+ if(!aPgRect.IsEmpty())
+ {
+ OutputDevice *pOut = _pViewShell->GetOut();
+
+ if(pOut->GetFillColor() != aGlobalRetoucheColor)
+ {
+ pOut->SetFillColor(aGlobalRetoucheColor);
+ }
+
+ pOut->DrawRect(aPgRect.SVRect());
+ }
+}
+
+const sal_Int8 SwPageFrame::snShadowPxWidth = 9;
+
+bool SwPageFrame::IsRightShadowNeeded() const
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
+
+ // We paint the right shadow if we're not in book mode
+ // or if we've no sibling or are the last page of the "row"
+ return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetNext()
+ || (this == Lower()) || (bIsLTR && OnRightPage())
+ || (!bIsLTR && !OnRightPage());
+
+}
+
+bool SwPageFrame::IsLeftShadowNeeded() const
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bIsLTR = getRootFrame()->IsLeftToRightViewLayout();
+
+ // We paint the left shadow if we're not in book mode
+ // or if we've no sibling or are the last page of the "row"
+ return !pSh || (!pSh->GetViewOptions()->IsViewLayoutBookMode()) || !GetPrev()
+ || (bIsLTR && !OnRightPage())
+ || (!bIsLTR && OnRightPage());
+}
+
+/**
+ * Determine rectangle for bottom page shadow
+ * for #i9719#
+ */
+/*static*/ void SwPageFrame::GetHorizontalShadowRect( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ OutputDevice const * pRenderContext,
+ SwRect& _orHorizontalShadowRect,
+ bool bPaintLeftShadow,
+ bool bPaintRightShadow,
+ bool bRightSidebar )
+{
+ const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
+ SwRect aAlignedPageRect( _rPageRect );
+ ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
+ SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
+
+ tools::Long lShadowAdjustment = snShadowPxWidth - 1; // TODO: extract this
+
+ _orHorizontalShadowRect.Chg(
+ Point( aPagePxRect.Left() + (bPaintLeftShadow ? lShadowAdjustment : 0), 0 ),
+ Size( aPagePxRect.Width() - ( (bPaintLeftShadow ? lShadowAdjustment : 0) + (bPaintRightShadow ? lShadowAdjustment : 0) ),
+ snShadowPxWidth ) );
+
+ if(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())
+ {
+ // Notes are displayed, we've to extend borders
+ SwTwips aSidebarTotalWidth = pMgr->GetSidebarWidth(true) + pMgr->GetSidebarBorderWidth(true);
+ if(bRightSidebar)
+ _orHorizontalShadowRect.AddRight( aSidebarTotalWidth );
+ else
+ _orHorizontalShadowRect.AddLeft( - aSidebarTotalWidth );
+ }
+}
+
+namespace {
+
+enum PaintArea {LEFT, RIGHT, TOP, BOTTOM};
+
+}
+
+#define BORDER_TILE_SIZE 512
+
+/// Wrapper around pOut->DrawBitmapEx.
+static void lcl_paintBitmapExToRect(vcl::RenderContext *pOut, const Point& aPoint, const Size& aSize, const BitmapEx& rBitmapEx, PaintArea eArea)
+{
+ if(!comphelper::LibreOfficeKit::isActive())
+ {
+ // The problem is that if we get called multiple times and the color is
+ // partly transparent, then the result will get darker and darker. To avoid
+ // this, always paint the background color before doing the real paint.
+ tools::Rectangle aRect(aPoint, aSize);
+
+ if (!aRect.IsEmpty())
+ {
+ switch (eArea)
+ {
+ case LEFT: aRect.SetLeft( aRect.Right() - 1 ); break;
+ case RIGHT: aRect.SetRight( aRect.Left() + 1 ); break;
+ case TOP: aRect.SetTop( aRect.Bottom() - 1 ); break;
+ case BOTTOM: aRect.SetBottom( aRect.Top() + 1 ); break;
+ }
+ }
+
+ pOut->SetFillColor(SwViewOption::GetAppBackgroundColor());
+ pOut->SetLineColor();
+ pOut->DrawRect(pOut->PixelToLogic(aRect));
+ }
+
+ // Tiled render if necessary
+ tools::Rectangle aComplete(aPoint, aSize);
+ Size aTileSize(BORDER_TILE_SIZE, BORDER_TILE_SIZE);
+
+ tools::Long iterX = eArea != RIGHT && eArea != LEFT ? BORDER_TILE_SIZE : 0;
+ tools::Long iterY = eArea == RIGHT || eArea == LEFT ? BORDER_TILE_SIZE : 0;
+
+ for (tools::Rectangle aTile(aPoint, aTileSize); true; aTile.Move(iterX, iterY))
+ {
+ tools::Rectangle aRender = aComplete.GetIntersection(aTile);
+ if (aRender.IsEmpty())
+ break;
+ pOut->DrawBitmapEx(pOut->PixelToLogic(aRender.TopLeft()),
+ pOut->PixelToLogic(aRender.GetSize()),
+ Point(0, 0), aRender.GetSize(),
+ rBitmapEx);
+ }
+
+}
+
+/**
+ * Paint page border and shadow
+ *
+ * for #i9719#
+ * implement paint of page border and shadow
+*/
+/*static*/ void SwPageFrame::PaintBorderAndShadow( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ bool bPaintLeftShadow,
+ bool bPaintRightShadow,
+ bool bRightSidebar )
+{
+ // No shadow in prefs
+ if (!SwViewOption::IsShadow())
+ return;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *_pViewShell->GetOut() );
+
+ static vcl::DeleteOnDeinit<drawinglayer::primitive2d::DiscreteShadow> shadowMaskObj(
+ vcl::bitmap::loadFromName(BMP_PAGE_SHADOW_MASK,
+ ImageLoadFlags::IgnoreDarkTheme | ImageLoadFlags::IgnoreScalingFactor));
+
+ drawinglayer::primitive2d::DiscreteShadow& shadowMask = *shadowMaskObj.get();
+ static vcl::DeleteOnDeinit< BitmapEx > aPageTopRightShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageBottomRightShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageBottomLeftShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageBottomShadowBaseObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageRightShadowBaseObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageTopShadowBaseObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageTopLeftShadowObj {};
+ static vcl::DeleteOnDeinit< BitmapEx > aPageLeftShadowBaseObj {};
+ BitmapEx& aPageTopRightShadow = *aPageTopRightShadowObj.get();
+ BitmapEx& aPageBottomRightShadow = *aPageBottomRightShadowObj.get();
+ BitmapEx& aPageBottomLeftShadow = *aPageBottomLeftShadowObj.get();
+ BitmapEx& aPageBottomShadow = *aPageBottomShadowBaseObj.get();
+ BitmapEx& aPageRightShadow = *aPageRightShadowBaseObj.get();
+ BitmapEx& aPageTopShadow = *aPageTopShadowBaseObj.get();
+ BitmapEx& aPageTopLeftShadow = *aPageTopLeftShadowObj.get();
+ BitmapEx& aPageLeftShadow = *aPageLeftShadowBaseObj.get();
+ static Color aShadowColor( COL_AUTO );
+
+ SwRect aAlignedPageRect( _rPageRect );
+ ::SwAlignRect( aAlignedPageRect, _pViewShell, _pViewShell->GetOut() );
+ SwRect aPagePxRect(_pViewShell->GetOut()->LogicToPixel( aAlignedPageRect.SVRect() ));
+
+ if (aShadowColor != SwViewOption::GetShadowColor())
+ {
+ aShadowColor = SwViewOption::GetShadowColor();
+
+ AlphaMask aMask( shadowMask.getBottomRight().GetBitmap() );
+ Bitmap aFilledSquare(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageBottomRightShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getBottomLeft().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageBottomLeftShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getBottom().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageBottomShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getTop().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageTopShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getTopRight().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageTopRightShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getRight().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageRightShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getTopLeft().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageTopLeftShadow = BitmapEx( aFilledSquare, aMask );
+
+ aMask = AlphaMask( shadowMask.getLeft().GetBitmap() );
+ aFilledSquare = Bitmap(aMask.GetSizePixel(), vcl::PixelFormat::N24_BPP);
+ aFilledSquare.Erase( aShadowColor );
+ aPageLeftShadow = BitmapEx( aFilledSquare, aMask );
+ }
+
+ SwRect aPaintRect;
+ OutputDevice *pOut = _pViewShell->GetOut();
+
+ SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pOut, aPaintRect, bPaintLeftShadow, bPaintRightShadow, bRightSidebar );
+
+ // Right shadow & corners
+ if ( bPaintRightShadow )
+ {
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Bottom() + 1 - (aPageBottomRightShadow.GetSizePixel().Height() - snShadowPxWidth) ) ),
+ aPageBottomRightShadow );
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( aPaintRect.Right(), aPagePxRect.Top() - snShadowPxWidth ) ),
+ aPageTopRightShadow );
+
+ if (aPagePxRect.Height() > 2 * snShadowPxWidth)
+ {
+ const tools::Long nWidth = aPageRightShadow.GetSizePixel().Width();
+ const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
+ if (aPageRightShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
+ aPageRightShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(aPaintRect.Right() + snShadowPxWidth, aPagePxRect.Top() + snShadowPxWidth - 1),
+ Size(nWidth, nHeight),
+ aPageRightShadow, RIGHT);
+ }
+ }
+
+ // Left shadows and corners
+ if(bPaintLeftShadow)
+ {
+ const tools::Long lLeft = aPaintRect.Left() - aPageBottomLeftShadow.GetSizePixel().Width();
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft,
+ aPagePxRect.Bottom() + 1 + snShadowPxWidth - aPageBottomLeftShadow.GetSizePixel().Height() ) ), aPageBottomLeftShadow );
+ pOut->DrawBitmapEx( pOut->PixelToLogic( Point( lLeft, aPagePxRect.Top() - snShadowPxWidth ) ), aPageTopLeftShadow );
+ if (aPagePxRect.Height() > 2 * snShadowPxWidth)
+ {
+ const tools::Long nWidth = aPageLeftShadow.GetSizePixel().Width();
+ const tools::Long nHeight = aPagePxRect.Height() - 2 * (snShadowPxWidth - 1);
+ if (aPageLeftShadow.GetSizePixel().Height() < BORDER_TILE_SIZE)
+ aPageLeftShadow.Scale(Size(nWidth, BORDER_TILE_SIZE), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(lLeft, aPagePxRect.Top() + snShadowPxWidth - 1),
+ Size(nWidth, nHeight),
+ aPageLeftShadow, LEFT);
+ }
+ }
+
+ // Bottom shadow
+ const tools::Long nBottomHeight = aPageBottomShadow.GetSizePixel().Height();
+ if (aPageBottomShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
+ aPageBottomShadow.Scale(Size(BORDER_TILE_SIZE, nBottomHeight), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(aPaintRect.Left(), aPagePxRect.Bottom() + 2),
+ Size(aPaintRect.Width(), nBottomHeight),
+ aPageBottomShadow, BOTTOM);
+
+ // Top shadow
+ const tools::Long nTopHeight = aPageTopShadow.GetSizePixel().Height();
+ if (aPageTopShadow.GetSizePixel().Width() < BORDER_TILE_SIZE)
+ aPageTopShadow.Scale(Size(BORDER_TILE_SIZE, nTopHeight), BmpScaleFlag::Fast);
+
+ lcl_paintBitmapExToRect(pOut,
+ Point(aPaintRect.Left(), aPagePxRect.Top() - snShadowPxWidth),
+ Size(aPaintRect.Width(), nTopHeight),
+ aPageTopShadow, TOP);
+}
+
+/**
+ * mod #i6193# paint sidebar for notes
+ * IMPORTANT: if you change the rects here, also change SwPostItMgr::ScrollbarHit
+ */
+/*static*/void SwPageFrame::PaintNotesSidebar(const SwRect& _rPageRect, SwViewShell* _pViewShell, sal_uInt16 nPageNum, bool bRight)
+{
+ //TODO: cut out scrollbar area and arrows out of sidepane rect, otherwise it could flicker when pressing arrow buttons
+ if (!_pViewShell )
+ return;
+
+ SwRect aPageRect( _rPageRect );
+ SwAlignRect( aPageRect, _pViewShell, _pViewShell->GetOut() );
+
+ const SwPostItMgr *pMgr = _pViewShell->GetPostItMgr();
+ if (!(pMgr && pMgr->ShowNotes() && pMgr->HasNotes())) // do not show anything in print preview
+ return;
+
+ sal_Int32 nScrollerHeight = pMgr->GetSidebarScrollerHeight();
+ const tools::Rectangle &aVisRect = _pViewShell->VisArea().SVRect();
+ //draw border and sidepane
+ _pViewShell->GetOut()->SetLineColor();
+ if (!bRight)
+ {
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetObjectBoundariesColor());
+ _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()))) ;
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ else
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetSectionBoundColor());
+ _pViewShell->GetOut()->DrawRect(tools::Rectangle(Point(aPageRect.Left()-pMgr->GetSidebarWidth()-pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()))) ;
+ }
+ else
+ {
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetObjectBoundariesColor());
+ SwRect aSidebarBorder(aPageRect.TopRight(),Size(pMgr->GetSidebarBorderWidth(),aPageRect.Height()));
+ _pViewShell->GetOut()->DrawRect(aSidebarBorder.SVRect());
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ else
+ _pViewShell->GetOut()->SetFillColor(SwViewOption::GetSectionBoundColor());
+ SwRect aSidebar(Point(aPageRect.Right()+pMgr->GetSidebarBorderWidth(),aPageRect.Top()),Size(pMgr->GetSidebarWidth(),aPageRect.Height()));
+ _pViewShell->GetOut()->DrawRect(aSidebar.SVRect());
+ }
+ if (!pMgr->ShowScrollbar(nPageNum))
+ return;
+
+ // draw scrollbar area and arrows
+ Point aPointBottom;
+ Point aPointTop;
+ aPointBottom = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() - pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height()) :
+ Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- _pViewShell->GetOut()->PixelToLogic(Size(0,2+pMgr->GetSidebarScrollerHeight())).Height());
+ aPointTop = !bRight ? Point(aPageRect.Left() - pMgr->GetSidebarWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height()) :
+ Point(aPageRect.Right() + pMgr->GetSidebarBorderWidth() + _pViewShell->GetOut()->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + _pViewShell->GetOut()->PixelToLogic(Size(0,2)).Height());
+ Size aSize(pMgr->GetSidebarWidth() - _pViewShell->GetOut()->PixelToLogic(Size(4,0)).Width(), _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()) ;
+ tools::Rectangle aRectBottom(aPointBottom,aSize);
+ tools::Rectangle aRectTop(aPointTop,aSize);
+
+ if (aRectBottom.Overlaps(aVisRect))
+ {
+
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_WHITE);
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ }
+ else
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_BLACK);
+ _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
+ }
+ _pViewShell->GetOut()->DrawRect(aRectBottom);
+ _pViewShell->GetOut()->DrawLine(aPointBottom + Point(pMgr->GetSidebarWidth()/3,0), aPointBottom + Point(pMgr->GetSidebarWidth()/3 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
+
+ _pViewShell->GetOut()->SetLineColor();
+ Point aMiddleFirst(aPointBottom + Point(pMgr->GetSidebarWidth()/6,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ Point aMiddleSecond(aPointBottom + Point(pMgr->GetSidebarWidth()/3*2,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell,pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
+ }
+ if (!aRectTop.Overlaps(aVisRect))
+ return;
+
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode() )
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_WHITE);
+ _pViewShell->GetOut()->SetFillColor(COL_BLACK);
+ }
+ else
+ {
+ _pViewShell->GetOut()->SetLineColor(COL_BLACK);
+ _pViewShell->GetOut()->SetFillColor(COL_LIGHTGRAY);
+ }
+ _pViewShell->GetOut()->DrawRect(aRectTop);
+ _pViewShell->GetOut()->DrawLine(aPointTop + Point(pMgr->GetSidebarWidth()/3*2,0), aPointTop + Point(pMgr->GetSidebarWidth()/3*2 , _pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()));
+
+ _pViewShell->GetOut()->SetLineColor();
+ Point aMiddleFirst(aPointTop + Point(pMgr->GetSidebarWidth()/3,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ Point aMiddleSecond(aPointTop + Point(pMgr->GetSidebarWidth()/6*5,_pViewShell->GetOut()->PixelToLogic(Size(0,nScrollerHeight)).Height()/2));
+ PaintNotesSidebarArrows(aMiddleFirst,aMiddleSecond,_pViewShell, pMgr->GetArrowColor(KEY_PAGEUP,nPageNum), pMgr->GetArrowColor(KEY_PAGEDOWN,nPageNum));
+}
+
+/*static*/ void SwPageFrame::PaintNotesSidebarArrows(const Point &aMiddleFirst, const Point &aMiddleSecond, SwViewShell const * _pViewShell, const Color& rColorUp, const Color& rColorDown)
+{
+ tools::Polygon aTriangleUp(3);
+ tools::Polygon aTriangleDown(3);
+
+ aTriangleUp.SetPoint(aMiddleFirst + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
+ aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),1);
+ aTriangleUp.SetPoint(aMiddleFirst + Point(_pViewShell->GetOut()->PixelToLogic(Size(3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
+
+ aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(-3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),0);
+ aTriangleDown.SetPoint(aMiddleSecond + Point(_pViewShell->GetOut()->PixelToLogic(Size(+3,0)).Width(),_pViewShell->GetOut()->PixelToLogic(Size(0,-3)).Height()),1);
+ aTriangleDown.SetPoint(aMiddleSecond + Point(0,_pViewShell->GetOut()->PixelToLogic(Size(0,3)).Height()),2);
+
+ _pViewShell->GetOut()->SetFillColor(rColorUp);
+ _pViewShell->GetOut()->DrawPolygon(aTriangleUp);
+ _pViewShell->GetOut()->SetFillColor(rColorDown);
+ _pViewShell->GetOut()->DrawPolygon(aTriangleDown);
+}
+
+/**
+ * Get bound rectangle of border and shadow for repaints
+ *
+ * for #i9719#
+ */
+/*static*/ void SwPageFrame::GetBorderAndShadowBoundRect( const SwRect& _rPageRect,
+ const SwViewShell* _pViewShell,
+ OutputDevice const * pRenderContext,
+ SwRect& _orBorderAndShadowBoundRect,
+ bool bLeftShadow,
+ bool bRightShadow,
+ bool bRightSidebar
+ )
+{
+ SwRect aAlignedPageRect( _rPageRect );
+ ::SwAlignRect( aAlignedPageRect, _pViewShell, pRenderContext );
+ SwRect aPagePxRect(pRenderContext->LogicToPixel( aAlignedPageRect.SVRect() ));
+ aPagePxRect.AddBottom( snShadowPxWidth + 1 );
+ aPagePxRect.AddTop( - snShadowPxWidth - 1 );
+
+ SwRect aTmpRect;
+
+ // Always ask for full shadow since we want a bounding rect
+ // including at least the page frame
+ SwPageFrame::GetHorizontalShadowRect( _rPageRect, _pViewShell, pRenderContext, aTmpRect, false, false, bRightSidebar );
+
+ if(bLeftShadow) aPagePxRect.Left( aTmpRect.Left() - snShadowPxWidth - 1);
+ if(bRightShadow) aPagePxRect.Right( aTmpRect.Right() + snShadowPxWidth + 1);
+
+ _orBorderAndShadowBoundRect = SwRect(pRenderContext->PixelToLogic( aPagePxRect.SVRect() ));
+}
+
+SwRect SwPageFrame::GetBoundRect(OutputDevice const * pOutputDevice) const
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwRect aPageRect( getFrameArea() );
+ SwRect aResult;
+
+ if(!pSh) {
+ return SwRect( Point(0, 0), Size(0, 0) );
+ }
+
+ SwPageFrame::GetBorderAndShadowBoundRect( aPageRect, pSh, pOutputDevice, aResult,
+ IsLeftShadowNeeded(), IsRightShadowNeeded(), SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT );
+ return aResult;
+}
+
+/*static*/ SwTwips SwPageFrame::GetSidebarBorderWidth( const SwViewShell* _pViewShell )
+{
+ const SwPostItMgr* pPostItMgr = _pViewShell ? _pViewShell->GetPostItMgr() : nullptr;
+ const SwTwips nRet = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ? pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() : 0;
+ return nRet;
+}
+
+void SwFrame::PaintBaBo( const SwRect& rRect, const SwPageFrame *pPage,
+ const bool bOnlyTextBackground ) const
+{
+ if ( !pPage )
+ pPage = FindPageFrame();
+
+ OutputDevice *pOut = gProp.pSGlobalShell->GetOut();
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pOut );
+
+ pOut->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ pOut->SetLineColor();
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ // take care of page margin area
+ // Note: code move from <SwFrame::PaintSwFrameBackground(..)> to new method
+ // <SwPageFrame::Paintmargin(..)>.
+ if ( IsPageFrame() && !bOnlyTextBackground)
+ {
+ static_cast<const SwPageFrame*>(this)->PaintMarginArea( rRect, gProp.pSGlobalShell );
+ }
+
+ // paint background
+ {
+ PaintSwFrameBackground( rRect, pPage, rAttrs, false, true/*bLowerBorder*/, bOnlyTextBackground );
+ }
+
+ // paint border before painting background
+ // paint grid for page frame and paint border
+ if (!bOnlyTextBackground)
+ {
+ SwRect aRect( rRect );
+
+ if( IsPageFrame() )
+ {
+ static_cast<const SwPageFrame*>(this)->PaintGrid( pOut, aRect );
+ }
+
+ PaintSwFrameShadowAndBorder(aRect, pPage, rAttrs);
+ }
+
+ pOut->Pop();
+}
+
+static bool lcl_compareFillAttributes(const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pA, const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& pB)
+{
+ if (pA == pB)
+ return true;
+ if (!pA || !pB)
+ return false;
+ return pA->getFillAttribute() == pB->getFillAttribute();
+}
+
+/// Do not paint background for fly frames without a background brush by
+/// calling <PaintBaBo> at the page or at the fly frame its anchored
+void SwFrame::PaintSwFrameBackground( const SwRect &rRect, const SwPageFrame *pPage,
+ const SwBorderAttrs & rAttrs,
+ const bool bLowerMode,
+ const bool bLowerBorder,
+ const bool bOnlyTextBackground ) const
+{
+ // #i1837# - no paint of table background, if corresponding option is *not* set.
+ if( IsTabFrame() &&
+ !gProp.pSGlobalShell->GetViewOptions()->IsTable() )
+ {
+ return;
+ }
+
+ // nothing to do for covered table cells:
+ if( IsCellFrame() && IsCoveredCell() )
+ return;
+
+ SwViewShell *pSh = gProp.pSGlobalShell;
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
+
+ const SvxBrushItem* pItem;
+ // temporary background brush for a fly frame without a background brush
+ std::unique_ptr<SvxBrushItem> pTmpBackBrush;
+ std::optional<Color> pCol;
+ SwRect aOrigBackRect;
+ const bool bPageFrame = IsPageFrame();
+ bool bLowMode = true;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ bool bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, bLowerMode, /*bConsiderTextBox=*/false );
+
+ // show track changes of table row
+ if( IsRowFrame() && !getRootFrame()->IsHideRedlines() )
+ {
+ RedlineType eType = static_cast<const SwRowFrame*>(this)->GetTabLine()->GetRedlineType();
+ if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
+ {
+ pCol = RedlineType::Delete == eType ? COL_AUTHOR_TABLE_DEL : COL_AUTHOR_TABLE_INS;
+ bBack = true;
+ }
+ }
+ else if ( bBack && IsCellFrame() && !getRootFrame()->IsHideRedlines() &&
+ // skip cell background to show the row colored according to its tracked change
+ RedlineType::None != static_cast<const SwRowFrame*>(GetUpper())->GetTabLine()->GetRedlineType() )
+ {
+ return;
+ }
+
+ //- Output if a separate background is used.
+ bool bNoFlyBackground = !gProp.bSFlyMetafile && !bBack && IsFlyFrame();
+ if ( bNoFlyBackground )
+ {
+ // Fly frame has no background.
+ // Try to find background brush at parents, if previous call of
+ // <GetBackgroundBrush> disabled this option with the parameter <bLowerMode>
+ if ( bLowerMode )
+ {
+ bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/false );
+ }
+ // If still no background found for the fly frame, initialize the
+ // background brush <pItem> with global retouche color and set <bBack>
+ // to true, that fly frame will paint its background using this color.
+ if ( !bBack )
+ {
+ // #i6467# - on print output, pdf output and in embedded mode not editing color COL_WHITE is used
+ // instead of the global retouche color.
+ if ( pSh->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ pSh->GetViewOptions()->IsPDFExport() ||
+ ( pSh->GetDoc()->GetDocShell()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED &&
+ !pSh->GetDoc()->GetDocShell()->IsInPlaceActive()
+ )
+ )
+ {
+ pTmpBackBrush.reset(new SvxBrushItem( COL_WHITE, RES_BACKGROUND ));
+
+ //UUU
+ aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(COL_WHITE);
+ }
+ else
+ {
+ pTmpBackBrush.reset(new SvxBrushItem( aGlobalRetoucheColor, RES_BACKGROUND));
+
+ //UUU
+ aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aGlobalRetoucheColor);
+ }
+
+ pItem = pTmpBackBrush.get();
+ bBack = true;
+ }
+ }
+
+ SwRect aPaintRect( getFrameArea() );
+ if( IsTextFrame() || IsSctFrame() )
+ aPaintRect = UnionFrame( true );
+
+ // bOnlyTextBackground means background that's on top of background shapes,
+ // this includes both text and cell frames.
+ if ( (!bOnlyTextBackground || IsTextFrame() || IsCellFrame()) && aPaintRect.Overlaps( rRect ) )
+ {
+ if ( bBack || bPageFrame || !bLowerMode )
+ {
+ const bool bBrowse = pSh->GetViewOptions()->getBrowseMode();
+ SwRect aRect;
+ if ( (bPageFrame && bBrowse) ||
+ (IsTextFrame() && getFramePrintArea().SSize() == getFrameArea().SSize()) )
+ {
+ aRect = getFrameArea();
+ ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ }
+ else
+ {
+ if (bPageFrame && GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
+ {
+ aRect = getFrameArea();
+ ::SwAlignRect(aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut());
+ }
+ else
+ {
+ ::lcl_CalcBorderRect( aRect, this, rAttrs, false, gProp);
+ }
+
+ if ( (IsTextFrame() || IsTabFrame()) && GetPrev() )
+ {
+ if ( GetPrev()->GetAttrSet()->GetBackground() == GetAttrSet()->GetBackground() &&
+ lcl_compareFillAttributes(GetPrev()->getSdrAllFillAttributesHelper(), getSdrAllFillAttributesHelper()))
+ {
+ aRect.Top( getFrameArea().Top() );
+ }
+ }
+ }
+ aRect.Intersection( rRect );
+
+ OutputDevice *pOut = pSh->GetOut();
+
+ if ( aRect.HasArea() )
+ {
+ std::unique_ptr<SvxBrushItem> pNewItem;
+
+ if( pCol )
+ {
+ pNewItem.reset(new SvxBrushItem( *pCol, RES_BACKGROUND ));
+ pItem = pNewItem.get();
+ aFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(*pCol);
+ }
+
+ SwRegionRects aRegion( aRect );
+ basegfx::B2DPolygon aB2DPolygon{tools::Polygon(aRect.SVRect()).getB2DPolygon()};
+ basegfx::utils::B2DClipState aClipState{basegfx::B2DPolyPolygon(aB2DPolygon)};
+ if (pPage->GetSortedObjs() &&
+ pSh->GetDoc()->getIDocumentSettingAccess().get(DocumentSettingId::SUBTRACT_FLYS))
+ {
+ ::lcl_SubtractFlys( this, pPage, aRect, aRegion, aClipState, gProp );
+ }
+
+ // Determine, if background transparency
+ // have to be considered for drawing.
+ // Status Quo: background transparency have to be
+ // considered for fly frames
+ const bool bConsiderBackgroundTransparency = IsFlyFrame();
+ bool bDone(false);
+
+ // #i125189# We are also done when the new DrawingLayer FillAttributes are used
+ // or the FillStyle is set (different from drawing::FillStyle_NONE)
+ if (aFillAttributes)
+ {
+ if(aFillAttributes->isUsed())
+ {
+ // check if really something is painted
+ bDone = DrawFillAttributes(aFillAttributes, aOrigBackRect, aRegion, aClipState, *pOut);
+ }
+
+ if(!bDone)
+ {
+ // if not, still a FillStyle could be set but the transparency is at 100%,
+ // thus need to check the model data itself for FillStyle (do not rely on
+ // SdrAllFillAttributesHelper since it already contains optimized information,
+ // e.g. transparency leads to no fill)
+ const drawing::FillStyle eFillStyle(GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
+
+ if(drawing::FillStyle_NONE != eFillStyle)
+ {
+ bDone = true;
+ }
+ }
+ }
+
+ if(!bDone)
+ {
+ for (size_t i = 0; i < aRegion.size(); ++i)
+ {
+ if (1 < aRegion.size())
+ {
+ ::SwAlignRect( aRegion[i], gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ if( !aRegion[i].HasArea() )
+ continue;
+ }
+ // add 6th parameter to indicate, if background transparency have to be considered
+ // Set missing 5th parameter to the default value GRFNUM_NO
+ // - see declaration in /core/inc/frmtool.hxx.
+ ::DrawGraphic(
+ pItem,
+ *pOut,
+ aOrigBackRect,
+ aRegion[i],
+ GRFNUM_NO,
+ bConsiderBackgroundTransparency );
+ }
+ }
+ }
+ }
+ else
+ bLowMode = bLowerMode;
+ }
+
+ // delete temporary background brush.
+ pTmpBackBrush.reset();
+
+ //Now process lower and his neighbour.
+ //We end this as soon as a Frame leaves the chain and therefore is not a lower
+ //of me anymore
+ const SwFrame *pFrame = GetLower();
+ if ( !pFrame )
+ return;
+
+ SwRect aFrameRect;
+ SwRect aRect( GetPaintArea() );
+ aRect.Intersection_( rRect );
+ SwRect aBorderRect( aRect );
+ SwShortCut aShortCut( *pFrame, aBorderRect );
+ do
+ { if ( gProp.pSProgress )
+ SfxProgress::Reschedule();
+
+ aFrameRect = pFrame->GetPaintArea();
+ if ( aFrameRect.Overlaps( aBorderRect ) )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rTmpAttrs = *aAccess.Get();
+ if ( ( pFrame->IsLayoutFrame() && bLowerBorder ) || aFrameRect.Overlaps( aRect ) )
+ {
+ pFrame->PaintSwFrameBackground( aRect, pPage, rTmpAttrs, bLowMode,
+ bLowerBorder, bOnlyTextBackground );
+ }
+
+ if ( bLowerBorder )
+ {
+ pFrame->PaintSwFrameShadowAndBorder( aBorderRect, pPage, rTmpAttrs );
+ }
+ }
+ pFrame = pFrame->GetNext();
+ } while ( pFrame && pFrame->GetUpper() == this &&
+ !aShortCut.Stop( aFrameRect ) );
+}
+
+/// Refreshes all subsidiary lines of a page.
+void SwPageFrame::RefreshSubsidiary( const SwRect &rRect ) const
+{
+ if ( !(isSubsidiaryLinesEnabled() || isTableBoundariesEnabled()
+ || isSubsidiaryLinesForSectionsEnabled() || isSubsidiaryLinesFlysEnabled()) )
+ return;
+
+ if ( !rRect.HasArea() )
+ return;
+
+ //During paint using the root, the array is controlled from there.
+ //Otherwise we'll handle it for our self.
+ bool bDelSubs = false;
+ if ( !gProp.pSSubsLines )
+ {
+ gProp.pSSubsLines.reset(new SwSubsRects);
+ // create container for special subsidiary lines
+ gProp.pSSpecSubsLines.reset(new SwSubsRects);
+ bDelSubs = true;
+ }
+
+ RefreshLaySubsidiary( this, rRect );
+
+ if ( bDelSubs )
+ {
+ // paint special subsidiary lines and delete its container
+ gProp.pSSpecSubsLines->PaintSubsidiary( gProp.pSGlobalShell->GetOut(), nullptr, gProp );
+ gProp.pSSpecSubsLines.reset();
+
+ gProp.pSSubsLines->PaintSubsidiary(gProp.pSGlobalShell->GetOut(), gProp.pSLines.get(), gProp);
+ gProp.pSSubsLines.reset();
+ }
+}
+
+void SwLayoutFrame::RefreshLaySubsidiary( const SwPageFrame *pPage,
+ const SwRect &rRect ) const
+{
+ const bool bSubsOpt = isSubsidiaryLinesEnabled();
+ if ( bSubsOpt )
+ PaintSubsidiaryLines( pPage, rRect );
+
+ const SwFrame *pLow = Lower();
+ if( !pLow )
+ return;
+ SwShortCut aShortCut( *pLow, rRect );
+ while( pLow && !aShortCut.Stop( pLow->getFrameArea() ) )
+ {
+ if ( pLow->getFrameArea().Overlaps( rRect ) && pLow->getFrameArea().HasArea() )
+ {
+ if ( pLow->IsLayoutFrame() )
+ static_cast<const SwLayoutFrame*>(pLow)->RefreshLaySubsidiary( pPage, rRect);
+ else if ( pLow->GetDrawObjs() )
+ {
+ const SwSortedObjs& rObjs = *(pLow->GetDrawObjs());
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( pPage->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId(
+ pAnchoredObj->GetDrawObj()->GetLayer() ) )
+ if (auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() && pFly->getFrameArea().Overlaps( rRect ) )
+ {
+ if ( !pFly->Lower() || !pFly->Lower()->IsNoTextFrame() ||
+ !static_cast<const SwNoTextFrame*>(pFly->Lower())->HasAnimation())
+ pFly->RefreshLaySubsidiary( pPage, rRect );
+ }
+ }
+ }
+ }
+ }
+ pLow = pLow->GetNext();
+ }
+}
+
+/**
+ * Subsidiary lines to paint the PrtAreas
+ * Only the LayoutFrames which directly contain Content
+ * Paints the desired line and pays attention to not overpaint any flys
+ */
+static void lcl_RefreshLine( const SwLayoutFrame *pLay,
+ const SwPageFrame *pPage,
+ const Point &rP1,
+ const Point &rP2,
+ const SubColFlags nSubColor,
+ SwLineRects* pSubsLines )
+{
+ //In which direction do we loop? Can only be horizontal or vertical.
+ OSL_ENSURE( ((rP1.X() == rP2.X()) || (rP1.Y() == rP2.Y())),
+ "Sloped subsidiary lines are not allowed." );
+
+ const bool bHori = rP1.Y() == rP2.Y();
+
+ // use pointers to member function in order to unify flow
+ typedef tools::Long (Point::*pmfPtGet)() const;
+ typedef void (Point::*pmfPtSet)(tools::Long);
+ const pmfPtGet pDirPtX = &Point::X;
+ const pmfPtGet pDirPtY = &Point::Y;
+ const pmfPtGet pDirPt = bHori ? pDirPtX : pDirPtY;
+ const pmfPtSet pDirPtSetX = &Point::setX;
+ const pmfPtSet pDirPtSetY = &Point::setY;
+ const pmfPtSet pDirPtSet = bHori ? pDirPtSetX : pDirPtSetY;
+
+ Point aP1( rP1 );
+ Point aP2( rP2 );
+
+ while ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
+ {
+ //If the starting point lies in a fly, it is directly set behind the
+ //fly.
+ //The end point moves to the start if the end point lies in a fly or we
+ //have a fly between starting point and end point.
+ // In this way, every position is output one by one.
+
+ //If I'm a fly I'll only avoid those flys which are places 'above' me;
+ //this means those who are behind me in the array.
+ //Even if I'm inside a fly or inside a fly inside a fly a.s.o I won't
+ //avoid any of those flys.
+ SwOrderIter aIter( pPage );
+ const SwFlyFrame *pMyFly = pLay->FindFlyFrame();
+ if ( pMyFly )
+ {
+ aIter.Current( pMyFly->GetVirtDrawObj() );
+ while ( nullptr != (pMyFly = pMyFly->GetAnchorFrame()->FindFlyFrame()) )
+ {
+ if ( aIter()->GetOrdNum() > pMyFly->GetVirtDrawObj()->GetOrdNum() )
+ aIter.Current( pMyFly->GetVirtDrawObj() );
+ }
+ }
+ else
+ aIter.Bottom();
+
+ while ( aIter() )
+ {
+ const SwVirtFlyDrawObj *pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
+ const SwFlyFrame *pFly = pObj ? pObj->GetFlyFrame() : nullptr;
+
+ //I certainly won't avoid myself, even if I'm placed _inside_ the
+ //fly I won't avoid it.
+ if ( !pFly || (pFly == pLay || pFly->IsAnLower( pLay )) )
+ {
+ aIter.Next();
+ continue;
+ }
+
+ // do *not* consider fly frames with a transparent background.
+ // do *not* consider fly frame, which belongs to an invisible layer
+ if ( pFly->IsBackgroundTransparent() ||
+ !pFly->GetFormat()->GetDoc()->getIDocumentDrawModelAccess().IsVisibleLayerId( pObj->GetLayer() ) )
+ {
+ aIter.Next();
+ continue;
+ }
+
+ //Is the Obj placed on the line
+ const tools::Long nP1OthPt = !bHori ? rP1.X() : rP1.Y();
+ const tools::Rectangle &rBound = pObj->GetCurrentBoundRect();
+ const Point aDrPt( rBound.TopLeft() );
+ const tools::Long nDrOthPt = !bHori ? aDrPt.X() : aDrPt.Y();
+ const Size aDrSz( rBound.GetSize() );
+ const tools::Long nDrOthSz = !bHori ? aDrSz.Width() : aDrSz.Height();
+
+ if ( nP1OthPt >= nDrOthPt && nP1OthPt <= nDrOthPt + nDrOthSz )
+ {
+ const tools::Long nDrDirPt = bHori ? aDrPt.X() : aDrPt.Y();
+ const tools::Long nDrDirSz = bHori ? aDrSz.Width() : aDrSz.Height();
+
+ if ( (aP1.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() <= nDrDirPt + nDrDirSz )
+ (aP1.*pDirPtSet)( nDrDirPt + nDrDirSz );
+
+ if ( (aP2.*pDirPt)() >= nDrDirPt && (aP1.*pDirPt)() < (nDrDirPt - 1) )
+ (aP2.*pDirPtSet)( nDrDirPt - 1 );
+ }
+ aIter.Next();
+ }
+
+ if ( (aP1.*pDirPt)() < (aP2.*pDirPt)() )
+ {
+ SwRect aRect( aP1, aP2 );
+ // use parameter <pSubsLines> instead of global variable <gProp.pSSubsLines>.
+ pSubsLines->AddLineRect( aRect, nullptr, SvxBorderLineStyle::SOLID,
+ nullptr, nSubColor, gProp );
+ }
+ aP1 = aP2;
+ (aP1.*pDirPtSet)( (aP1.*pDirPt)() + 1 );
+ aP2 = rP2;
+ }
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreatePageAreaDelimiterPrimitives(
+ const SwRect& rRect )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
+
+ basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
+ double nLineLength = 200.0; // in Twips
+
+ Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
+ double const aXOffDirs[] = { -1.0, 1.0, 1.0, -1.0 };
+ double const aYOffDirs[] = { -1.0, -1.0, 1.0, 1.0 };
+
+ // Actually loop over the corners to create the two lines
+ for ( int i = 0; i < 4; i++ )
+ {
+ basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
+ basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
+
+ basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( aBPoint + aHorizVector * nLineLength );
+ aPolygon.append( aBPoint );
+ aPolygon.append( aBPoint + aVertVector * nLineLength );
+
+ aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aLineColor );
+ }
+
+ return aSeq;
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateRectangleDelimiterPrimitives (
+ const SwRect& rRect )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 1 );
+ basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Top() ) );
+ aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Top() ) );
+ aPolygon.append( basegfx::B2DPoint( rRect.Right(), rRect.Bottom() ) );
+ aPolygon.append( basegfx::B2DPoint( rRect.Left(), rRect.Bottom() ) );
+ aPolygon.setClosed( true );
+
+ aSeq[0] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aLineColor );
+
+ return aSeq;
+}
+
+static drawinglayer::primitive2d::Primitive2DContainer lcl_CreateColumnAreaDelimiterPrimitives(
+ const SwRect& rRect )
+{
+ drawinglayer::primitive2d::Primitive2DContainer aSeq( 4 );
+
+ basegfx::BColor aLineColor = SwViewOption::GetDocBoundariesColor().getBColor();
+ double nLineLength = 100.0; // in Twips
+
+ Point aPoints[] = { rRect.TopLeft(), rRect.TopRight(), rRect.BottomRight(), rRect.BottomLeft() };
+ double const aXOffDirs[] = { 1.0, -1.0, -1.0, 1.0 };
+ double const aYOffDirs[] = { 1.0, 1.0, -1.0, -1.0 };
+
+ // Actually loop over the corners to create the two lines
+ for ( int i = 0; i < 4; i++ )
+ {
+ basegfx::B2DVector aHorizVector( aXOffDirs[i], 0.0 );
+ basegfx::B2DVector aVertVector( 0.0, aYOffDirs[i] );
+
+ basegfx::B2DPoint aBPoint( aPoints[i].getX(), aPoints[i].getY() );
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( aBPoint + aHorizVector * nLineLength );
+ aPolygon.append( aBPoint );
+ aPolygon.append( aBPoint + aVertVector * nLineLength );
+
+ aSeq[i] = new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aPolygon, aLineColor );
+ }
+
+ return aSeq;
+}
+
+void SwPageFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+ if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
+ return;
+
+ const SwFrame* pLay = Lower();
+ const SwFrame* pFootnoteCont = nullptr;
+ const SwFrame* pPageBody = nullptr;
+ while ( pLay && !( pFootnoteCont && pPageBody ) )
+ {
+ if ( pLay->IsFootnoteContFrame( ) )
+ pFootnoteCont = pLay;
+ if ( pLay->IsBodyFrame() )
+ pPageBody = pLay;
+ pLay = pLay->GetNext();
+ }
+
+ SwRect aArea( pPageBody->getFrameArea() );
+ if ( pFootnoteCont )
+ aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
+
+ if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
+ ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
+ else
+ ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
+}
+
+void SwColumnFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+ const SwFrame* pLay = Lower();
+ const SwFrame* pFootnoteCont = nullptr;
+ const SwFrame* pColBody = nullptr;
+ while ( pLay && !( pFootnoteCont && pColBody ) )
+ {
+ if ( pLay->IsFootnoteContFrame( ) )
+ pFootnoteCont = pLay;
+ if ( pLay->IsBodyFrame() )
+ pColBody = pLay;
+ pLay = pLay->GetNext();
+ }
+
+ SwRect aArea( pColBody->getFrameArea() );
+
+ // #i3662# - enlarge top of column body frame's printing area
+ // in sections to top of section frame.
+ const bool bColInSection = GetUpper()->IsSctFrame();
+ if ( bColInSection )
+ {
+ if ( IsVertical() )
+ aArea.Right( GetUpper()->getFrameArea().Right() );
+ else
+ aArea.Top( GetUpper()->getFrameArea().Top() );
+ }
+
+ if ( pFootnoteCont )
+ aArea.AddBottom( pFootnoteCont->getFrameArea().Bottom() - aArea.Bottom() );
+
+ ::SwAlignRect( aArea, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+
+ if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
+ ProcessPrimitives( lcl_CreateColumnAreaDelimiterPrimitives( aArea ) );
+ else
+ ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
+}
+
+void SwSectionFrame::PaintSubsidiaryLines( const SwPageFrame * pPage,
+ const SwRect & rRect ) const
+{
+ const bool bNoLowerColumn = !Lower() || !Lower()->IsColumnFrame();
+ if ( bNoLowerColumn )
+ {
+ SwLayoutFrame::PaintSubsidiaryLines( pPage, rRect );
+ }
+}
+
+/**
+ * The SwBodyFrame doesn't print any subsidiary line: it's bounds are painted
+ * either by the parent page or the parent column frame.
+ */
+void SwBodyFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+}
+
+void SwHeadFootFrame::PaintSubsidiaryLines( const SwPageFrame *, const SwRect & ) const
+{
+ if ( gProp.pSGlobalShell->IsHeaderFooterEdit() )
+ {
+ SwRect aArea( getFramePrintArea() );
+ aArea.Pos() += getFrameArea().Pos();
+ if ( !gProp.pSGlobalShell->GetViewOptions()->IsViewMetaChars( ) )
+ ProcessPrimitives( lcl_CreatePageAreaDelimiterPrimitives( aArea ) );
+ else
+ ProcessPrimitives( lcl_CreateRectangleDelimiterPrimitives( aArea ) );
+ }
+}
+
+/**
+ * This method is overridden in order to have no subsidiary lines
+ * around the footnotes.
+ */
+void SwFootnoteFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+}
+
+/**
+ * This method is overridden in order to have no subsidiary lines
+ * around the footnotes containers.
+ */
+void SwFootnoteContFrame::PaintSubsidiaryLines( const SwPageFrame *,
+ const SwRect & ) const
+{
+}
+
+void SwLayoutFrame::PaintSubsidiaryLines( const SwPageFrame *pPage,
+ const SwRect &rRect ) const
+{
+ bool bNewTableModel = false;
+
+ // #i29550#
+ if ( IsTabFrame() || IsCellFrame() || IsRowFrame() )
+ {
+ const SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->IsCollapsingBorders() )
+ return;
+
+ bNewTableModel = pTabFrame->GetTable()->IsNewModel();
+ // in the new table model, we have an early return for all cell-related
+ // frames, except from non-covered table cells
+ if ( bNewTableModel )
+ if ( IsTabFrame() ||
+ IsRowFrame() ||
+ ( IsCellFrame() && IsCoveredCell() ) )
+ return;
+ }
+
+ const bool bFlys = pPage->GetSortedObjs() != nullptr;
+
+ const bool bCell = IsCellFrame();
+ // #i3662# - use frame area for cells for section use also frame area
+ const bool bUseFrameArea = bCell || IsSctFrame();
+ SwRect aOriginal( bUseFrameArea ? getFrameArea() : getFramePrintArea() );
+ if ( !bUseFrameArea )
+ aOriginal.Pos() += getFrameArea().Pos();
+
+ ::SwAlignRect( aOriginal, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+
+ if ( !aOriginal.Overlaps( rRect ) )
+ return;
+
+ SwRect aOut( aOriginal );
+ aOut.Intersection_( rRect );
+
+ const SwTwips nRight = aOut.Right();
+ const SwTwips nBottom= aOut.Bottom();
+
+ const Point aRT( nRight, aOut.Top() );
+ const Point aRB( nRight, nBottom );
+ const Point aLB( aOut.Left(), nBottom );
+
+ SubColFlags nSubColor = ( bCell || IsRowFrame() )
+ ? SubColFlags::Tab
+ : ( IsInSct()
+ ? SubColFlags::Sect
+ : ( IsInFly() ? SubColFlags::Fly : SubColFlags::Page ) );
+
+ // collect body, header, footer, footnote and section
+ // sub-lines in <pSpecSubsLine> array.
+ const bool bSpecialSublines = IsBodyFrame() || IsHeaderFrame() || IsFooterFrame() ||
+ IsFootnoteFrame() || IsSctFrame();
+ SwLineRects *const pUsedSubsLines = bSpecialSublines
+ ? gProp.pSSpecSubsLines.get() : gProp.pSSubsLines.get();
+
+ // NOTE: for cell frames only left and right (horizontal layout) respectively
+ // top and bottom (vertical layout) lines painted.
+ // NOTE2: this does not hold for the new table model!!! We paint the top border
+ // of each non-covered table cell.
+ const bool bVert = IsVertical();
+ if ( bFlys )
+ {
+ // add control for drawing left and right lines
+ if ( !bCell || bNewTableModel || !bVert )
+ {
+ if ( aOriginal.Left() == aOut.Left() )
+ ::lcl_RefreshLine( this, pPage, aOut.Pos(), aLB, nSubColor, pUsedSubsLines );
+ // in vertical layout set page/column break at right
+ if ( aOriginal.Right() == nRight )
+ ::lcl_RefreshLine( this, pPage, aRT, aRB, nSubColor, pUsedSubsLines );
+ }
+ // adjust control for drawing top and bottom lines
+ if ( !bCell || bNewTableModel || bVert )
+ {
+ if ( aOriginal.Top() == aOut.Top() )
+ // in horizontal layout set page/column break at top
+ ::lcl_RefreshLine( this, pPage, aOut.Pos(), aRT, nSubColor, pUsedSubsLines );
+ if ( aOriginal.Bottom() == nBottom )
+ ::lcl_RefreshLine( this, pPage, aLB, aRB, nSubColor,
+ pUsedSubsLines );
+ }
+ }
+ else
+ {
+ // add control for drawing left and right lines
+ if ( !bCell || bNewTableModel || !bVert )
+ {
+ if ( aOriginal.Left() == aOut.Left() )
+ {
+ const SwRect aRect( aOut.Pos(), aLB );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ // in vertical layout set page/column break at right
+ if ( aOriginal.Right() == nRight )
+ {
+ const SwRect aRect( aRT, aRB );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ }
+ // adjust control for drawing top and bottom lines
+ if ( !bCell || bNewTableModel || bVert )
+ {
+ if ( aOriginal.Top() == aOut.Top() )
+ {
+ // in horizontal layout set page/column break at top
+ const SwRect aRect( aOut.Pos(), aRT );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ if ( aOriginal.Bottom() == nBottom )
+ {
+ const SwRect aRect( aLB, aRB );
+ pUsedSubsLines->AddLineRect( aRect, nullptr,
+ SvxBorderLineStyle::SOLID, nullptr, nSubColor, gProp );
+ }
+ }
+ }
+}
+
+/**
+ * Refreshes all extra data (line breaks a.s.o) of the page. Basically only those objects
+ * are considered which horizontally overlap the Rect.
+ */
+void SwPageFrame::RefreshExtraData( const SwRect &rRect ) const
+{
+ const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
+ bool bLineInFly = (rInfo.IsPaintLineNumbers() && rInfo.IsCountInFlys())
+ || static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos()) != text::HoriOrientation::NONE;
+
+ SwRect aRect( rRect );
+ ::SwAlignRect( aRect, gProp.pSGlobalShell, gProp.pSGlobalShell->GetOut() );
+ if ( !aRect.HasArea() )
+ return;
+
+ SwLayoutFrame::RefreshExtraData( aRect );
+
+ if ( bLineInFly && GetSortedObjs() )
+ for (SwAnchoredObject* pAnchoredObj : *GetSortedObjs())
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->getFrameArea().Top() <= aRect.Bottom() &&
+ pFly->getFrameArea().Bottom() >= aRect.Top() )
+ pFly->RefreshExtraData( aRect );
+ }
+ }
+}
+
+void SwLayoutFrame::RefreshExtraData( const SwRect &rRect ) const
+{
+
+ const SwLineNumberInfo &rInfo = GetFormat()->GetDoc()->GetLineNumberInfo();
+ bool bLineInBody = rInfo.IsPaintLineNumbers(),
+ bLineInFly = bLineInBody && rInfo.IsCountInFlys(),
+ bRedLine = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos())!=text::HoriOrientation::NONE;
+
+ const SwContentFrame *pCnt = ContainsContent();
+ while ( pCnt && IsAnLower( pCnt ) )
+ {
+ if ( pCnt->IsTextFrame() && ( bRedLine ||
+ ( !pCnt->IsInTab() &&
+ ((bLineInBody && pCnt->IsInDocBody()) ||
+ (bLineInFly && pCnt->IsInFly())) ) ) &&
+ pCnt->getFrameArea().Top() <= rRect.Bottom() &&
+ pCnt->getFrameArea().Bottom() >= rRect.Top() )
+ {
+ static_cast<const SwTextFrame*>(pCnt)->PaintExtraData( rRect );
+ }
+ if ( bLineInFly && pCnt->GetDrawObjs() )
+ for (SwAnchoredObject* pAnchoredObj : *pCnt->GetDrawObjs())
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() &&
+ pFly->getFrameArea().Top() <= rRect.Bottom() &&
+ pFly->getFrameArea().Bottom() >= rRect.Top() )
+ pFly->RefreshExtraData( rRect );
+ }
+ }
+ pCnt = pCnt->GetNextContentFrame();
+ }
+}
+
+/**
+ * For #102450#
+ * Determine the color, that is respectively will be drawn as background
+ * for the page frame.
+ * Using existing method SwFrame::GetBackgroundBrush to determine the color
+ * that is set at the page frame respectively is parent. If none is found
+ * return the global retouche color
+ *
+ * @return Color
+ */
+Color SwPageFrame::GetDrawBackgroundColor() const
+{
+ const SvxBrushItem* pBrushItem;
+ std::optional<Color> xDummyColor;
+ SwRect aDummyRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if ( GetBackgroundBrush( aFillAttributes, pBrushItem, xDummyColor, aDummyRect, true, /*bConsiderTextBox=*/false) )
+ {
+ if(aFillAttributes && aFillAttributes->isUsed())
+ {
+ // let SdrAllFillAttributesHelper do the average color calculation
+ return Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
+ }
+ else if(pBrushItem)
+ {
+ OUString referer;
+ SwViewShell * sh1 = getRootFrame()->GetCurrShell();
+ if (sh1 != nullptr) {
+ SfxObjectShell * sh2 = sh1->GetDoc()->GetPersist();
+ if (sh2 != nullptr && sh2->HasName()) {
+ referer = sh2->GetMedium()->GetName();
+ }
+ }
+ const Graphic* pGraphic = pBrushItem->GetGraphic(referer);
+
+ if(pGraphic)
+ {
+ // #29105# when a graphic is set, it may be possible to calculate a single
+ // color which looks good in all places of the graphic. Since it is
+ // planned to have text edit on the overlay one day and the fallback
+ // to aGlobalRetoucheColor returns something useful, just use that
+ // for now.
+ }
+ else
+ {
+ // not a graphic, use (hopefully) initialized color
+ return pBrushItem->GetColor();
+ }
+ }
+ }
+
+ return aGlobalRetoucheColor;
+}
+
+/// create/return font used to paint the "empty page" string
+const vcl::Font& SwPageFrame::GetEmptyPageFont()
+{
+ static vcl::Font aEmptyPgFont = []()
+ {
+ vcl::Font tmp;
+ tmp.SetFontSize( Size( 0, 80 * 20 )); // == 80 pt
+ tmp.SetWeight( WEIGHT_BOLD );
+ tmp.SetStyleName(OUString());
+ tmp.SetFamilyName("Helvetica");
+ tmp.SetFamily( FAMILY_SWISS );
+ tmp.SetTransparent( true );
+ tmp.SetColor( COL_GRAY );
+ return tmp;
+ }();
+
+ return aEmptyPgFont;
+}
+
+/**
+ * Retouch for a section
+ *
+ * Retouch will only be done, if the Frame is the last one in his chain.
+ * The whole area of the upper which is located below the Frame will be
+ * cleared using PaintSwFrameBackground.
+ */
+void SwFrame::Retouch( const SwPageFrame * pPage, const SwRect &rRect ) const
+{
+ if ( gProp.bSFlyMetafile )
+ return;
+
+ OSL_ENSURE( GetUpper(), "Retouche try without Upper." );
+ OSL_ENSURE( getRootFrame()->GetCurrShell() && gProp.pSGlobalShell->GetWin(), "Retouche on a printer?" );
+
+ SwRect aRetouche( GetUpper()->GetPaintArea() );
+ aRetouche.Top( getFrameArea().Top() + getFrameArea().Height() );
+ aRetouche.Intersection( gProp.pSGlobalShell->VisArea() );
+
+ if ( aRetouche.HasArea() )
+ {
+ //Omit the passed Rect. To do this, we unfortunately need a region to
+ //cut out.
+ SwRegionRects aRegion( aRetouche );
+ aRegion -= rRect;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
+
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ const SwRect &rRetouche = aRegion[i];
+
+ GetUpper()->PaintBaBo( rRetouche, pPage );
+
+ //Hell and Heaven need to be refreshed too.
+ //To avoid recursion my retouch flag needs to be reset first!
+ ResetRetouche();
+ if ( rRetouche.HasArea() )
+ {
+ const Color aPageBackgrdColor(pPage->GetDrawBackgroundColor());
+ const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
+ // --> OD #i76669#
+ SwViewObjectContactRedirector aSwRedirector( *pSh );
+ // <--
+
+ pSh->Imp()->PaintLayer( rIDDMA.GetHellId(), nullptr,
+ *pPage, rRetouche, &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ pSh->Imp()->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
+ *pPage, rRetouche, &aPageBackgrdColor,
+ pPage->IsRightToLeft(),
+ &aSwRedirector );
+ }
+
+ SetRetouche();
+
+ //Because we leave all paint areas, we need to refresh the
+ //subsidiary lines.
+ pPage->RefreshSubsidiary( rRetouche );
+ }
+ }
+ if ( SwViewShell::IsLstEndAction() )
+ ResetRetouche();
+}
+
+/**
+ * Determine the background brush for the frame:
+ * the background brush is taken from it-self or from its parent (anchor/upper).
+ * Normally, the background brush is taken, which has no transparent color or
+ * which has a background graphic. But there are some special cases:
+ * (1) No background brush is taken from a page frame, if view option "IsPageBack"
+ * isn't set.
+ * (2) Background brush from an index section is taken under special conditions.
+ * In this case parameter <rpCol> is set to the index shading color.
+ * (3) New (OD 20.08.2002) - Background brush is taken, if on background drawing
+ * of the frame transparency is considered and its color is not "no fill"/"auto fill"
+ *
+ * Old description in German:
+ * Returns the Backgroundbrush for the area of the Frame.
+ * The Brush is defined by the Frame or by an upper, the first Brush is
+ * used. If no Brush is defined for a Frame, false is returned.
+ *
+ * @param rpBrush
+ * output parameter - constant reference pointer the found background brush
+ *
+ * @param rpFillStyle
+ * output parameter - constant reference pointer the found background fill style
+ *
+ * @param rpFillGradient
+ * output parameter - constant reference pointer the found background fill gradient
+ *
+ * @param rpCol
+ * output parameter - constant reference pointer to the color of the index shading
+ * set under special conditions, if background brush is taken from an index section.
+ *
+ * @param rOrigRect
+ * in-/output parameter - reference to the rectangle the background brush is
+ * considered for - adjusted to the frame, from which the background brush is
+ * taken.
+ *
+ * @parem bLowerMode
+ * input parameter - boolean indicating, if background brush should *not* be
+ * taken from parent.
+ *
+ * @param bConsiderTextBox
+ * consider the TextBox of this fly frame (if there is any) when determining
+ * the background color, useful for automatic font color.
+ *
+ * @return true, if a background brush for the frame is found
+ */
+bool SwFrame::GetBackgroundBrush(
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const SvxBrushItem* & rpBrush,
+ std::optional<Color>& rxCol,
+ SwRect &rOrigRect,
+ bool bLowerMode,
+ bool bConsiderTextBox ) const
+{
+ const SwFrame *pFrame = this;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const SwViewOption *pOpt = pSh->GetViewOptions();
+ rpBrush = nullptr;
+ rxCol.reset();
+ do
+ {
+ if ( pFrame->IsPageFrame() && !pOpt->IsPageBack() )
+ return false;
+
+ if (pFrame->supportsFullDrawingLayerFillAttributeSet())
+ {
+ bool bHandledTextBox = false;
+ if (pFrame->IsFlyFrame() && bConsiderTextBox)
+ {
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(pFrame);
+ SwFrameFormat* pShape
+ = SwTextBoxHelper::getOtherTextBoxFormat(pFlyFrame->GetFormat(), RES_FLYFRMFMT);
+ if (pShape)
+ {
+ SdrObject* pObject = pShape->FindRealSdrObject();
+ if (pObject)
+ {
+ // Work with the fill attributes of the shape of the fly frame.
+ rFillAttributes =
+ std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(
+ pObject->GetMergedItemSet());
+ bHandledTextBox = true;
+ }
+ }
+ }
+
+ if (!bHandledTextBox)
+ rFillAttributes = pFrame->getSdrAllFillAttributesHelper();
+ }
+ const SvxBrushItem &rBack = pFrame->GetAttrSet()->GetBackground();
+
+ if( pFrame->IsSctFrame() )
+ {
+ const SwSection* pSection = static_cast<const SwSectionFrame*>(pFrame)->GetSection();
+ // Note: If frame <pFrame> is a section of the index and
+ // it its background color is "no fill"/"auto fill" and
+ // it has no background graphic and
+ // we are not in the page preview and
+ // we are not in read-only mode and
+ // option "index shadings" is set and
+ // the output is not the printer
+ // then set <rpCol> to the color of the index shading
+ if( pSection && ( SectionType::ToxHeader == pSection->GetType() ||
+ SectionType::ToxContent == pSection->GetType() ) &&
+ (rBack.GetColor() == COL_TRANSPARENT) &&
+ rBack.GetGraphicPos() == GPOS_NONE &&
+ !pOpt->IsPagePreview() &&
+ !pOpt->IsReadonly() &&
+ // #114856# Form view
+ !pOpt->IsFormView() &&
+ SwViewOption::IsIndexShadings() &&
+ !pOpt->IsPDFExport() &&
+ pSh->GetOut()->GetOutDevType() != OUTDEV_PRINTER )
+ {
+ rxCol = SwViewOption::GetIndexShadingsColor();
+ }
+ }
+
+ // determine, if background draw of frame <pFrame> considers transparency
+ // Status Quo: background transparency have to be
+ // considered for fly frames
+ const bool bConsiderBackgroundTransparency = pFrame->IsFlyFrame();
+
+ // #i125189# Do not base the decision for using the parent's fill style for this
+ // frame when the new DrawingLayer FillAttributes are used on the SdrAllFillAttributesHelper
+ // information. There the data is already optimized to no fill in the case that the
+ // transparence is at 100% while no fill is the criteria for derivation
+ bool bNewDrawingLayerFillStyleIsUsedAndNotNoFill(false);
+
+ if(rFillAttributes)
+ {
+ // the new DrawingLayer FillStyle is used
+ if(rFillAttributes->isUsed())
+ {
+ // it's not drawing::FillStyle_NONE
+ bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
+ }
+ else
+ {
+ // maybe optimized already when 100% transparency is used somewhere, need to test
+ // XFillStyleItem directly from the model data
+ const drawing::FillStyle eFillStyle(pFrame->GetAttrSet()->Get(XATTR_FILLSTYLE).GetValue());
+
+ if(drawing::FillStyle_NONE != eFillStyle)
+ {
+ bNewDrawingLayerFillStyleIsUsedAndNotNoFill = true;
+ }
+ }
+ }
+
+ // add condition:
+ // If <bConsiderBackgroundTransparency> is set - see above -,
+ // return brush of frame <pFrame>, if its color is *not* "no fill"/"auto fill"
+ if (
+ // #i125189# Done when the new DrawingLayer FillAttributes are used and
+ // not drawing::FillStyle_NONE (see above)
+ bNewDrawingLayerFillStyleIsUsedAndNotNoFill ||
+
+ // done when SvxBrushItem is used
+ rBack.GetColor().GetAlpha() == 255 || rBack.GetGraphicPos() != GPOS_NONE ||
+
+ // done when direct color is forced
+ rxCol ||
+
+ // done when consider BG transparency and color is not completely transparent
+ (bConsiderBackgroundTransparency && (rBack.GetColor() != COL_TRANSPARENT))
+ )
+ {
+ rpBrush = &rBack;
+ if ( pFrame->IsPageFrame() && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ rOrigRect = pFrame->getFrameArea();
+ ::SwAlignRect(rOrigRect, pSh, pSh->GetOut());
+ }
+ else
+ {
+ if (pFrame->IsPageFrame()
+ && pFrame->GetAttrSet()->GetItem<SfxBoolItem>(RES_BACKGROUND_FULL_SIZE)->GetValue())
+ {
+ rOrigRect = pFrame->getFrameArea();
+ }
+ else if (pFrame->getFrameArea().SSize() != pFrame->getFramePrintArea().SSize())
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ ::lcl_CalcBorderRect( rOrigRect, pFrame, rAttrs, false, gProp );
+ }
+ else
+ {
+ rOrigRect = pFrame->getFramePrintArea();
+ rOrigRect += pFrame->getFrameArea().Pos();
+ }
+ }
+
+ return true;
+ }
+
+ if ( bLowerMode )
+ {
+ // Do not try to get background brush from parent (anchor/upper)
+ return false;
+ }
+
+ // get parent frame - anchor or upper - for next loop
+ if ( pFrame->IsFlyFrame() )
+ {
+ pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ }
+ else
+ {
+ pFrame = pFrame->GetUpper();
+ }
+ } while ( pFrame );
+
+ return false;
+}
+
+void SetOutDevAndWin( SwViewShell *pSh, OutputDevice *pO,
+ vcl::Window *pW, sal_uInt16 nZoom )
+{
+ pSh->mpOut = pO;
+ pSh->mpWin = pW;
+ pSh->mpOpt->SetZoom( nZoom );
+}
+
+Graphic SwFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
+{
+ return Graphic();
+}
+
+Graphic SwFlyFrameFormat::MakeGraphic( ImageMap* pMap, const sal_uInt32 /*nMaximumQuadraticPixels*/, const std::optional<Size>& /*rTargetDPI*/ )
+{
+ Graphic aRet;
+ //search any Fly!
+ SwIterator<SwFrame,SwFormat> aIter( *this );
+ SwFrame *pFirst = aIter.First();
+ SwViewShell *const pSh =
+ pFirst ? pFirst->getRootFrame()->GetCurrShell() : nullptr;
+ if (nullptr != pSh)
+ {
+ SwViewShell *pOldGlobal = gProp.pSGlobalShell;
+ gProp.pSGlobalShell = pSh;
+
+ bool bNoteURL = pMap &&
+ SfxItemState::SET != GetAttrSet().GetItemState( RES_URL );
+ if( bNoteURL )
+ {
+ OSL_ENSURE( !pNoteURL, "MakeGraphic: pNoteURL already used? " );
+ pNoteURL = new SwNoteURL;
+ }
+ SwFlyFrame *pFly = static_cast<SwFlyFrame*>(pFirst);
+
+ OutputDevice *pOld = pSh->GetOut();
+ ScopedVclPtrInstance< VirtualDevice > pDev( *pOld );
+ pDev->EnableOutput( false );
+
+ GDIMetaFile aMet;
+ MapMode aMap( pOld->GetMapMode().GetMapUnit() );
+ pDev->SetMapMode( aMap );
+ aMet.SetPrefMapMode( aMap );
+
+ ::SwCalcPixStatics( pSh->GetOut() );
+ aMet.SetPrefSize( pFly->getFrameArea().SSize() );
+
+ aMet.Record( pDev.get() );
+ pDev->SetLineColor();
+ pDev->SetFillColor();
+ pDev->SetFont( pOld->GetFont() );
+
+ //Enlarge the rectangle if needed, so the border is painted too.
+ SwRect aOut( pFly->getFrameArea() );
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFly );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ if ( rAttrs.CalcRightLine() )
+ aOut.AddWidth(2*gProp.nSPixelSzW );
+ if ( rAttrs.CalcBottomLine() )
+ aOut.AddHeight(2*gProp.nSPixelSzH );
+
+ // #i92711# start Pre/PostPaint encapsulation before pOut is changed to the buffering VDev
+ const vcl::Region aRepaintRegion(aOut.SVRect());
+ pSh->DLPrePaint2(aRepaintRegion);
+
+ vcl::Window *pWin = pSh->GetWin();
+ sal_uInt16 nZoom = pSh->GetViewOptions()->GetZoom();
+ ::SetOutDevAndWin( pSh, pDev, nullptr, 100 );
+ gProp.bSFlyMetafile = true;
+ gProp.pSFlyMetafileOut = pWin->GetOutDev();
+
+ SwViewShellImp *pImp = pSh->Imp();
+ gProp.pSFlyOnlyDraw = pFly;
+ gProp.pSLines.reset(new SwLineRects);
+
+ // determine page, fly frame is on
+ const SwPageFrame* pFlyPage = pFly->FindPageFrame();
+ const Color aPageBackgrdColor(pFlyPage->GetDrawBackgroundColor());
+ const IDocumentDrawModelAccess& rIDDMA = pSh->getIDocumentDrawModelAccess();
+ // --> OD #i76669#
+ SwViewObjectContactRedirector aSwRedirector( *pSh );
+ // <--
+ pImp->PaintLayer( rIDDMA.GetHellId(), nullptr,
+ *pFlyPage, aOut, &aPageBackgrdColor,
+ pFlyPage->IsRightToLeft(),
+ &aSwRedirector );
+ gProp.pSLines->PaintLines( pDev, gProp );
+ if ( pFly->IsFlyInContentFrame() )
+ pFly->PaintSwFrame( *pDev, aOut );
+ gProp.pSLines->PaintLines( pDev, gProp );
+ pImp->PaintLayer( rIDDMA.GetHeavenId(), nullptr,
+ *pFlyPage, aOut, &aPageBackgrdColor,
+ pFlyPage->IsRightToLeft(),
+ &aSwRedirector );
+ gProp.pSLines->PaintLines( pDev, gProp );
+ gProp.pSLines.reset();
+ gProp.pSFlyOnlyDraw = nullptr;
+
+ gProp.pSFlyMetafileOut = nullptr;
+ gProp.bSFlyMetafile = false;
+ ::SetOutDevAndWin( pSh, pOld, pWin, nZoom );
+
+ // #i92711# end Pre/PostPaint encapsulation when pOut is back and content is painted
+ pSh->DLPostPaint2(true);
+
+ aMet.Stop();
+ aMet.Move( -pFly->getFrameArea().Left(), -pFly->getFrameArea().Top() );
+ aRet = Graphic( aMet );
+
+ if( bNoteURL )
+ {
+ OSL_ENSURE( pNoteURL, "MakeGraphic: Good Bye, NoteURL." );
+ delete pNoteURL;
+ pNoteURL = nullptr;
+ }
+ gProp.pSGlobalShell = pOldGlobal;
+ }
+ return aRet;
+}
+
+Graphic SwDrawFrameFormat::MakeGraphic( ImageMap*, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI )
+{
+ Graphic aRet;
+ SwDrawModel* pMod = getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pMod )
+ {
+ SdrObject *pObj = FindSdrObject();
+ SdrView aView( *pMod );
+ SdrPageView *pPgView = aView.ShowSdrPage(aView.GetModel()->GetPage(0));
+ aView.MarkObj( pObj, pPgView );
+ aRet = aView.GetMarkedObjBitmapEx(/*bNoVDevIfOneBmpMarked=*/false, nMaximumQuadraticPixels, rTargetDPI);
+ aView.HideSdrPage();
+ }
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/sectfrm.cxx b/sw/source/core/layout/sectfrm.cxx
new file mode 100644
index 000000000..eb667dd51
--- /dev/null
+++ b/sw/source/core/layout/sectfrm.cxx
@@ -0,0 +1,2941 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemiter.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtclbl.hxx>
+#include <sectfrm.hxx>
+#include <cellfrm.hxx>
+#include <section.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <fmtclds.hxx>
+#include <colfrm.hxx>
+#include <tabfrm.hxx>
+#include <ftnfrm.hxx>
+#include <layouter.hxx>
+#include <dbg_lay.hxx>
+#include <viewopt.hxx>
+#include <viewimp.hxx>
+#include <editeng/brushitem.hxx>
+#include <fmtftntx.hxx>
+#include <flyfrm.hxx>
+#include <sortedobjs.hxx>
+#include <hints.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+
+namespace
+{
+/**
+ * Performs the correct type of position invalidation depending on if we're in
+ * CalcContent().
+ */
+void InvalidateFramePos(SwFrame* pFrame, bool bInCalcContent)
+{
+ if (bInCalcContent)
+ pFrame->InvalidatePos_();
+ else
+ pFrame->InvalidatePos();
+}
+}
+
+SwSectionFrame::SwSectionFrame( SwSection &rSect, SwFrame* pSib )
+ : SwLayoutFrame( rSect.GetFormat(), pSib )
+ , SwFlowFrame( static_cast<SwFrame&>(*this) )
+ , m_pSection( &rSect )
+ , m_bFootnoteAtEnd(false)
+ , m_bEndnAtEnd(false)
+ , m_bContentLock(false)
+ , m_bOwnFootnoteNum(false)
+ , m_bFootnoteLock(false)
+{
+ StartListening(rSect.GetFormat()->GetNotifier());
+
+ mnFrameType = SwFrameType::Section;
+
+ CalcFootnoteAtEndFlag();
+ CalcEndAtEndFlag();
+}
+
+SwSectionFrame::SwSectionFrame( SwSectionFrame &rSect, bool bMaster ) :
+ SwLayoutFrame( rSect.GetFormat(), rSect.getRootFrame() ),
+ SwFlowFrame( static_cast<SwFrame&>(*this) ),
+ m_pSection( rSect.GetSection() ),
+ m_bFootnoteAtEnd( rSect.IsFootnoteAtEnd() ),
+ m_bEndnAtEnd( rSect.IsEndnAtEnd() ),
+ m_bContentLock( false ),
+ m_bOwnFootnoteNum( false ),
+ m_bFootnoteLock( false )
+{
+ StartListening(rSect.GetFormat()->GetNotifier());
+
+ mnFrameType = SwFrameType::Section;
+
+ PROTOCOL( this, PROT::Section, bMaster ? DbgAction::CreateMaster : DbgAction::CreateFollow, &rSect )
+
+ if( bMaster )
+ {
+ SwSectionFrame* pMaster = rSect.IsFollow() ? rSect.FindMaster() : nullptr;
+ if (pMaster)
+ pMaster->SetFollow( this );
+ SetFollow( &rSect );
+ }
+ else
+ {
+ SetFollow( rSect.GetFollow() );
+ rSect.SetFollow( this );
+ if( !GetFollow() )
+ rSect.SimpleFormat();
+ if( !rSect.IsColLocked() )
+ rSect.InvalidateSize();
+ }
+}
+
+// NOTE: call <SwSectionFrame::Init()> directly after creation of a new section
+// frame and its insert in the layout.
+void SwSectionFrame::Init()
+{
+ assert(GetUpper() && "SwSectionFrame::Init before insertion?!");
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, nWidth );
+ aRectFnSet.SetHeight( aFrm, 0 );
+ }
+
+ // #109700# LRSpace for sections
+ const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetLeft( aPrt, rLRSpace.GetLeft() );
+ aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() );
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ const SwFormatCol &rCol = GetFormat()->GetCol();
+ if( ( rCol.GetNumCols() > 1 || IsAnyNoteAtEnd() ) && !IsInFootnote() )
+ {
+ const SwFormatCol *pOld = Lower() ? &rCol : new SwFormatCol;
+ ChgColumns( *pOld, rCol, IsAnyNoteAtEnd() );
+ if( pOld != &rCol )
+ delete pOld;
+ }
+}
+
+void SwSectionFrame::DestroyImpl()
+{
+ if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame )
+ pRootFrame->RemoveFromList( this );
+ if( IsFollow() )
+ {
+ SwSectionFrame *pMaster = FindMaster();
+ if( pMaster )
+ {
+ PROTOCOL( this, PROT::Section, DbgAction::DelFollow, pMaster )
+ pMaster->SetFollow( GetFollow() );
+ // A Master always grabs the space until the lower edge of his
+ // Upper. If he doesn't have a Follow anymore, he can
+ // release it, which is why the Size of the Master is
+ // invalidated.
+ if( !GetFollow() )
+ pMaster->InvalidateSize();
+ }
+ }
+#if defined DBG_UTIL
+ else if( HasFollow() )
+ {
+ PROTOCOL( this, PROT::Section, DbgAction::DelMaster, GetFollow() )
+ }
+#endif
+ }
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwSectionFrame::~SwSectionFrame()
+{
+}
+
+void SwSectionFrame::DelEmpty( bool bRemove )
+{
+ if( IsColLocked() )
+ {
+ OSL_ENSURE( !bRemove, "Don't delete locked SectionFrames" );
+ return;
+ }
+ SwFrame* pUp = GetUpper();
+ if( pUp )
+ {
+ // #i27138#
+ // notify accessibility paragraphs objects about changed
+ // CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = FindNextCnt( true );
+ auto pPrev = FindPrevCnt();
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ pPrev ? pPrev->DynCastTextFrame() : nullptr );
+ }
+ }
+#endif
+ Cut_( bRemove );
+ }
+ SwSectionFrame *pMaster = IsFollow() ? FindMaster() : nullptr;
+ if (pMaster)
+ {
+ pMaster->SetFollow( GetFollow() );
+ // A Master always grabs the space until the lower edge of his
+ // Upper. If he doesn't have a Follow anymore, he can
+ // release it, which is why the Size of the Master is
+ // invalidated.
+ if( !GetFollow() && !pMaster->IsColLocked() )
+ pMaster->InvalidateSize();
+ }
+ SetFollow(nullptr);
+ if( !pUp )
+ return;
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( 0 );
+ }
+
+ // If we are destroyed immediately anyway, we don't need
+ // to put us into the list
+ if( bRemove )
+ { // If we already were half dead before this DelEmpty,
+ // we are likely in the list and have to remove us from
+ // it
+ if( !m_pSection && getRootFrame() )
+ getRootFrame()->RemoveFromList( this );
+ }
+ else if( getRootFrame() )
+ {
+ getRootFrame()->InsertEmptySct( this );
+ }
+
+ m_pSection = nullptr; // like this a reanimation is virtually impossible though
+}
+
+void SwSectionFrame::Cut()
+{
+ Cut_( true );
+}
+
+void SwSectionFrame::Cut_( bool bRemove )
+{
+ OSL_ENSURE( GetUpper(), "Cut without Upper()." );
+
+ PROTOCOL( this, PROT::Cut, DbgAction::NONE, GetUpper() )
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ SwFrame *pFrame = GetNext();
+ SwFrame* pPrepFrame = nullptr;
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ pFrame = pFrame->GetNext();
+ if( pFrame )
+ { // The former successor might have calculated a gap to the predecessor
+ // which is now obsolete since he becomes the first
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if ( pFrame && pFrame->IsContentFrame() )
+ {
+ pFrame->InvalidatePage( pPage );
+ if( IsInFootnote() && !GetIndPrev() )
+ pPrepFrame = pFrame;
+ }
+ }
+ else
+ {
+ InvalidateNextPos();
+ // Someone has to take over the retouching: predecessor or Upper
+ pFrame = GetPrev();
+ if ( nullptr != pFrame )
+ {
+ pFrame->SetRetouche();
+ pFrame->Prepare( PrepareHint::WidowsOrphans );
+ if ( pFrame->IsContentFrame() )
+ pFrame->InvalidatePage( pPage );
+ }
+ // If I am (was) the only FlowFrame in my Upper, then he has to take over
+ // the retouching.
+ // Furthermore a blank page could have emerged
+ else
+ { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
+ pRoot->SetSuperfluous();
+ GetUpper()->SetCompletePaint();
+ }
+ }
+ // First remove, then shrink Upper
+ SwLayoutFrame *pUp = GetUpper();
+ if( bRemove )
+ {
+ RemoveFromLayout();
+ if( pUp && !pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
+ pUp->GetUpper() )
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ pUp = nullptr;
+ }
+ }
+ if( pPrepFrame )
+ pPrepFrame->Prepare( PrepareHint::FootnoteInvalidation );
+ if ( !pUp )
+ return;
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight <= 0 )
+ return;
+
+ if( !bRemove )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, 0 );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ pUp->Shrink( nFrameHeight );
+}
+
+void SwSectionFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "No parent for Paste()." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm my own parent." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetUpper(),
+ "I am still registered somewhere." );
+
+ PROTOCOL( this, PROT::Paste, DbgAction::NONE, GetUpper() )
+
+ // Add to the tree
+ SwSectionFrame* pSect = pParent->FindSctFrame();
+ // Assure that parent is not inside a table frame, which is inside the found section frame.
+ if ( pSect )
+ {
+ SwTabFrame* pTableFrame = pParent->FindTabFrame();
+ if ( pTableFrame &&
+ pSect->IsAnLower( pTableFrame ) )
+ {
+ pSect = nullptr;
+ }
+ }
+
+ SwRectFnSet aRectFnSet(pParent);
+ if( pSect && HasToBreak( pSect ) )
+ {
+ if( pParent->IsColBodyFrame() ) // dealing with a single-column area
+ {
+ // If we are coincidentally at the end of a column, pSibling
+ // has to point to the first frame of the next column in order
+ // for the content of the next column to be moved correctly to the
+ // newly created pSect by the InsertGroup
+ SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pParent->GetUpper());
+ while( !pSibling && nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
+ pSibling = static_cast<SwLayoutFrame*>(pCol->Lower())->Lower();
+ if( pSibling )
+ {
+ // Even worse: every following column content has to
+ // be attached to the pSibling-chain in order to be
+ // taken along
+ SwFrame *pTmp = pSibling;
+ while ( nullptr != ( pCol = static_cast<SwColumnFrame*>(pCol->GetNext()) ) )
+ {
+ while ( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ SwFrame* pSave = ::SaveContent( pCol );
+ if (pSave)
+ ::RestoreContent( pSave, pSibling->GetUpper(), pTmp );
+ }
+ }
+ }
+ pParent = pSect;
+ pSect = new SwSectionFrame( *static_cast<SwSectionFrame*>(pParent)->GetSection(), pParent );
+ // if pParent is decomposed into two parts, its Follow has to be attached
+ // to the new second part
+ pSect->SetFollow( static_cast<SwSectionFrame*>(pParent)->GetFollow() );
+ static_cast<SwSectionFrame*>(pParent)->SetFollow( nullptr );
+ if( pSect->GetFollow() )
+ pParent->InvalidateSize_();
+
+ const bool bInserted = InsertGroupBefore( pParent, pSibling, pSect );
+ if (bInserted)
+ {
+ pSect->Init();
+ aRectFnSet.MakePos( *pSect, pSect->GetUpper(), pSect->GetPrev(), true);
+ }
+ if( !static_cast<SwLayoutFrame*>(pParent)->Lower() )
+ {
+ SwSectionFrame::MoveContentAndDelete( static_cast<SwSectionFrame*>(pParent), false );
+ pParent = this;
+ }
+ }
+ else
+ InsertGroupBefore( pParent, pSibling, nullptr );
+
+ InvalidateAll_();
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+
+ if ( pSibling )
+ {
+ pSibling->InvalidatePos_();
+ pSibling->InvalidatePrt_();
+ if ( pSibling->IsContentFrame() )
+ pSibling->InvalidatePage( pPage );
+ }
+
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight )
+ pParent->Grow( nFrameHeight );
+
+ if ( GetPrev() && !IsFollow() )
+ {
+ GetPrev()->InvalidateSize();
+ if ( GetPrev()->IsContentFrame() )
+ GetPrev()->InvalidatePage( pPage );
+ }
+}
+
+/**
+|* Here it's decided whether the this-SectionFrame should break up
+|* the passed (Section)frm (or not).
+|* Initially, all superior sections are broken up. Later on that could
+|* be made configurable.
+|*/
+bool SwSectionFrame::HasToBreak( const SwFrame* pFrame ) const
+{
+ if( !pFrame->IsSctFrame() )
+ return false;
+
+ const SwSectionFormat *pTmp = static_cast<const SwSectionFormat*>(GetFormat());
+
+ const SwFrameFormat *pOtherFormat = static_cast<const SwSectionFrame*>(pFrame)->GetFormat();
+ do
+ {
+ pTmp = pTmp->GetParent();
+ if( !pTmp )
+ return false;
+ if( pTmp == pOtherFormat )
+ return true;
+ } while( true ); // ( pTmp->GetSect().GetValue() );
+}
+
+/**
+|* Merges two SectionFrames, in case it's about the same section.
+|* This can be necessary when a (sub)section is deleted that had
+|* divided another part into two.
+|*/
+void SwSectionFrame::MergeNext( SwSectionFrame* pNxt )
+{
+ if (pNxt->IsDeleteForbidden())
+ return;
+
+ if (pNxt->IsJoinLocked() || GetSection() != pNxt->GetSection())
+ return;
+
+ PROTOCOL( this, PROT::Section, DbgAction::Merge, pNxt )
+
+ SwFrame* pTmp = ::SaveContent( pNxt );
+ if( pTmp )
+ {
+ SwFrame* pLast = Lower();
+ SwLayoutFrame* pLay = this;
+ if( pLast )
+ {
+ while( pLast->GetNext() )
+ pLast = pLast->GetNext();
+ if( pLast->IsColumnFrame() )
+ { // Columns now with BodyFrame
+ pLay = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLast)->Lower());
+ pLast = pLay->Lower();
+ if( pLast )
+ while( pLast->GetNext() )
+ pLast = pLast->GetNext();
+ }
+ }
+ ::RestoreContent( pTmp, pLay, pLast );
+ }
+ SetFollow( pNxt->GetFollow() );
+ pNxt->SetFollow( nullptr );
+ pNxt->Cut();
+ SwFrame::DestroyFrame(pNxt);
+ InvalidateSize();
+}
+
+/**
+|* Divides a SectionFrame into two parts. The second one starts with the
+|* passed frame.
+|* This is required when inserting an inner section, because the MoveFwd
+|* cannot have the desired effect within a frame or a table cell.
+|*/
+bool SwSectionFrame::SplitSect( SwFrame* pFrame, bool bApres )
+{
+ assert(pFrame && "SplitSect: Why?");
+ SwFrame* pOther = bApres ? pFrame->FindNext() : pFrame->FindPrev();
+ if( !pOther )
+ return false;
+ SwSectionFrame* pSect = pOther->FindSctFrame();
+ if( pSect != this )
+ return false;
+ // Put the content aside
+ SwFrame* pSav = ::SaveContent( this, bApres ? pOther : pFrame );
+ OSL_ENSURE( pSav, "SplitSect: What's on?" );
+ if( pSav ) // be robust
+ { // Create a new SctFrame, not as a Follower/master
+ SwSectionFrame* pNew = new SwSectionFrame( *pSect->GetSection(), pSect );
+ pNew->InsertBehind( pSect->GetUpper(), pSect );
+ pNew->Init();
+ SwRectFnSet aRectFnSet(this);
+ aRectFnSet.MakePos( *pNew, nullptr, pSect, true );
+ // OD 25.03.2003 #108339# - restore content:
+ // determine layout frame for restoring content after the initialization
+ // of the section frame. In the section initialization the columns are
+ // created.
+ {
+ SwLayoutFrame* pLay = pNew;
+ // Search for last layout frame, e.g. for columned sections.
+ while( pLay->Lower() && pLay->Lower()->IsLayoutFrame() )
+ pLay = static_cast<SwLayoutFrame*>(pLay->Lower());
+ ::RestoreContent( pSav, pLay, nullptr );
+ }
+ InvalidateSize_();
+ if( HasFollow() )
+ {
+ pNew->SetFollow( GetFollow() );
+ SetFollow( nullptr );
+ }
+ return true;
+ }
+ return false;
+}
+
+/**
+|* MoveContent is called for destroying a SectionFrames, due to
+|* the cancellation or hiding of a section, to handle the content.
+|* If the SectionFrame hasn't broken up another one, then the content
+|* is moved to the Upper. Otherwise the content is moved to another
+|* SectionFrame, which has to be potentially merged.
+|*/
+// If a multi-column section is cancelled, the ContentFrames have to be
+// invalidated
+static void lcl_InvalidateInfFlags( SwFrame* pFrame, bool bInva )
+{
+ while ( pFrame )
+ {
+ pFrame->InvalidateInfFlags();
+ if( bInva )
+ {
+ pFrame->InvalidatePos_();
+ pFrame->InvalidateSize_();
+ pFrame->InvalidatePrt_();
+ }
+ if( pFrame->IsLayoutFrame() )
+ lcl_InvalidateInfFlags( static_cast<SwLayoutFrame*>(pFrame)->GetLower(), false );
+ pFrame = pFrame->GetNext();
+ }
+}
+
+// Works like SwContentFrame::ImplGetNextContentFrame, but starts with a LayoutFrame
+static SwContentFrame* lcl_GetNextContentFrame( const SwLayoutFrame* pLay, bool bFwd )
+{
+ if ( bFwd )
+ {
+ if ( pLay->GetNext() && pLay->GetNext()->IsContentFrame() )
+ return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetNext()));
+ }
+ else
+ {
+ if ( pLay->GetPrev() && pLay->GetPrev()->IsContentFrame() )
+ return const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pLay->GetPrev()));
+ }
+
+ const SwFrame* pFrame = pLay;
+ SwContentFrame *pContentFrame = nullptr;
+ bool bGoingUp = true;
+ do {
+ const SwFrame *p = nullptr;
+ bool bGoingFwdOrBwd = false;
+
+ bool bGoingDown = !bGoingUp && pFrame->IsLayoutFrame();
+ if (bGoingDown)
+ {
+ p = static_cast<const SwLayoutFrame*>(pFrame)->Lower();
+ bGoingDown = nullptr != p;
+ }
+ if ( !bGoingDown )
+ {
+ p = pFrame->IsFlyFrame() ?
+ ( bFwd ? static_cast<const SwFlyFrame*>(pFrame)->GetNextLink() : static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() ) :
+ ( bFwd ? pFrame->GetNext() :pFrame->GetPrev() );
+ bGoingFwdOrBwd = nullptr != p;
+ if ( !bGoingFwdOrBwd )
+ {
+ p = pFrame->GetUpper();
+ bGoingUp = nullptr != p;
+ if ( !bGoingUp )
+ return nullptr;
+ }
+ }
+
+ bGoingUp = !( bGoingFwdOrBwd || bGoingDown );
+ assert(p);
+ if (!bFwd && bGoingDown)
+ while ( p->GetNext() )
+ p = p->GetNext();
+
+ pFrame = p;
+ } while ( nullptr == (pContentFrame = (pFrame->IsContentFrame() ? const_cast<SwContentFrame*>(static_cast<const SwContentFrame*>(pFrame)) : nullptr) ));
+
+ return pContentFrame;
+}
+
+namespace
+{
+ SwLayoutFrame* FirstLeaf(SwSectionFrame* pLayFrame)
+ {
+ if (pLayFrame->Lower() && pLayFrame->Lower()->IsColumnFrame())
+ return pLayFrame->GetNextLayoutLeaf();
+ return pLayFrame;
+ }
+
+ /// Checks if pFrame has a parent that can contain a split section frame.
+ bool CanContainSplitSection(const SwFrame* pFrame)
+ {
+ if (!pFrame->IsInTab())
+ return true;
+
+ // The frame is in a table, see if the table is in a section.
+ bool bRet = !pFrame->FindTabFrame()->IsInSct();
+
+ if (bRet)
+ {
+ // Don't try to split if the frame itself is a section frame with
+ // multiple columns.
+ if (pFrame->IsSctFrame())
+ {
+ const SwFrame* pLower = pFrame->GetLower();
+ if (pLower && pLower->IsColumnFrame())
+ bRet = false;
+ }
+ }
+
+ return bRet;
+ }
+}
+
+void SwSectionFrame::MoveContentAndDelete( SwSectionFrame* pDel, bool bSave )
+{
+ bool bSize = pDel->Lower() && pDel->Lower()->IsColumnFrame();
+ SwFrame* pPrv = pDel->GetPrev();
+ SwLayoutFrame* pUp = pDel->GetUpper();
+ // OD 27.03.2003 #i12711# - initialize local pointer variables.
+ SwSectionFrame* pPrvSct = nullptr;
+ SwSectionFrame* pNxtSct = nullptr;
+ SwSectionFormat* pParent = static_cast<SwSectionFormat*>(pDel->GetFormat())->GetParent();
+ if( pDel->IsInTab() && pParent )
+ {
+ SwTabFrame *pTab = pDel->FindTabFrame();
+ // If we are within a table, we can only have broken up sections that
+ // are inside as well, but not a section that contains the whole table.
+ if( pTab->IsInSct() && pParent == pTab->FindSctFrame()->GetFormat() )
+ pParent = nullptr;
+ }
+ // If our Format has a parent, we have probably broken up another
+ // SectionFrame, which has to be checked. To do so we first acquire the
+ // succeeding and the preceding ContentFrame, let's see if they
+ // lay in the SectionFrames.
+ // OD 27.03.2003 #i12711# - check, if previous and next section belonging
+ // together and can be joined, *not* only if deleted section contains content.
+ if ( pParent )
+ {
+ SwFrame* pPrvContent = lcl_GetNextContentFrame( pDel, false );
+ pPrvSct = pPrvContent ? pPrvContent->FindSctFrame() : nullptr;
+ SwFrame* pNxtContent = lcl_GetNextContentFrame( pDel, true );
+ pNxtSct = pNxtContent ? pNxtContent->FindSctFrame() : nullptr;
+ }
+ else
+ {
+ pParent = nullptr;
+ pPrvSct = pNxtSct = nullptr;
+ }
+
+ // Now the content is put aside and the frame is destroyed
+ SwFrame *pSave = bSave ? ::SaveContent( pDel ) : nullptr;
+ bool bOldFootnote = true;
+ if( pSave && pUp->IsFootnoteFrame() )
+ {
+ bOldFootnote = static_cast<SwFootnoteFrame*>(pUp)->IsColLocked();
+ static_cast<SwFootnoteFrame*>(pUp)->ColLock();
+ }
+ pDel->DelEmpty( true );
+ SwFrame::DestroyFrame(pDel);
+ if( pParent )
+ { // Search for the appropriate insert position
+ if( pNxtSct && pNxtSct->GetFormat() == pParent )
+ { // Here we can insert ourselves at the beginning
+ pUp = FirstLeaf( pNxtSct );
+ pPrv = nullptr;
+ if( pPrvSct && ( pPrvSct->GetFormat() != pParent ) )
+ pPrvSct = nullptr; // In order that nothing is merged
+ }
+ else if( pPrvSct && pPrvSct->GetFormat() == pParent )
+ { // Wonderful, here we can insert ourselves at the end
+ pUp = pPrvSct;
+ if( pUp->Lower() && pUp->Lower()->IsColumnFrame() )
+ {
+ pUp = static_cast<SwLayoutFrame*>(pUp->GetLastLower());
+ // The body of the last column
+ pUp = static_cast<SwLayoutFrame*>(pUp->Lower());
+ }
+ // In order to perform the insertion after the last one
+ pPrv = pUp->GetLastLower();
+ pPrvSct = nullptr; // Such that nothing is merged
+ }
+ else
+ {
+ if( pSave )
+ { // Following situations: before and after the section-to-be
+ // deleted there is the section boundary of the enclosing
+ // section, or another (sibling) section connects subsequently,
+ // that derives from the same Parent.
+ // In that case, there's not (yet) a part of our parent available
+ // that can store the content, so we create it here.
+ pPrvSct = new SwSectionFrame( *pParent->GetSection(), pUp );
+ pPrvSct->InsertBehind( pUp, pPrv );
+ pPrvSct->Init();
+ SwRectFnSet aRectFnSet(pUp);
+ aRectFnSet.MakePos( *pPrvSct, pUp, pPrv, true );
+ pUp = FirstLeaf( pPrvSct );
+ pPrv = nullptr;
+ }
+ pPrvSct = nullptr; // Such that nothing will be merged
+ }
+ }
+ // The content is going to be inserted...
+ if( pSave )
+ {
+ lcl_InvalidateInfFlags( pSave, bSize );
+ ::RestoreContent( pSave, pUp, pPrv );
+ pUp->FindPageFrame()->InvalidateContent();
+ if( !bOldFootnote )
+ static_cast<SwFootnoteFrame*>(pUp)->ColUnlock();
+ }
+ // Now two parts of the superior section could possibly be merged
+ if( pPrvSct && !pPrvSct->IsJoinLocked() )
+ {
+ OSL_ENSURE( pNxtSct, "MoveContent: No Merge" );
+ pPrvSct->MergeNext( pNxtSct );
+ }
+}
+
+void SwSectionFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( IsJoinLocked() || IsColLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
+ return;
+ if( !m_pSection ) // Via DelEmpty
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
+#endif
+ if( !isFrameAreaPositionValid() )
+ {
+ if( GetUpper() )
+ {
+ SwRectFnSet aRectFnSet(GetUpper());
+ aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false );
+ }
+
+ if (getFrameArea().Height() == 0)
+ {
+ // SwLayoutFrame::MakeAll() is not called for to-be-deleted
+ // section frames (which would invalidate the position of the
+ // next frame via the SwLayNotify dtor), so call it manually.
+ if (SwFrame* pNext = GetNext())
+ pNext->InvalidatePos();
+ }
+ }
+
+ setFrameAreaPositionValid(true);
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ return;
+ }
+ LockJoin(); // I don't let myself to be destroyed on the way
+
+ while( GetNext() && GetNext() == GetFollow() )
+ {
+ const SwFrame* pFoll = GetFollow();
+ MergeNext( static_cast<SwSectionFrame*>(GetNext()) );
+ if( pFoll == GetFollow() )
+ break;
+ }
+
+ // OD 2004-03-15 #116561# - In online layout join the follows, if section
+ // can grow.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ // Split sections inside table cells: need to merge all follows of the
+ // section here, as later we won't attempt doing so.
+ bool bCanContainSplitSection = false;
+ if (IsInTab() && GetUpper())
+ bCanContainSplitSection = CanContainSplitSection(GetUpper());
+
+ if( pSh && (pSh->GetViewOptions()->getBrowseMode() || bCanContainSplitSection) &&
+ ( Grow( LONG_MAX, true ) > 0 ) )
+ {
+ while( GetFollow() )
+ {
+ const SwFrame* pFoll = GetFollow();
+ MergeNext( GetFollow() );
+ if( pFoll == GetFollow() )
+ break;
+ }
+ }
+
+ // A section with Follow uses all the space until the lower edge of the
+ // Upper. If it moves, its size can grow or decrease...
+ if( !isFrameAreaPositionValid() && ToMaximize( false ) )
+ {
+ setFrameAreaSizeValid(false);
+ }
+
+ SwLayoutFrame::MakeAll(getRootFrame()->GetCurrShell()->GetOut());
+
+ if (IsInTab())
+ {
+ // In case the section is in a table, then calculate the lower right
+ // now. Just setting the valid size flag of the lower to false may not
+ // be enough, as lcl_RecalcRow() can call
+ // SwFrame::ValidateThisAndAllLowers(), and then we don't attempt
+ // calculating the proper position of the lower.
+ SwFrame* pLower = Lower();
+ if (pLower && !pLower->isFrameAreaPositionValid())
+ pLower->Calc(pRenderContext);
+ }
+
+ UnlockJoin();
+ if( m_pSection && IsSuperfluous() )
+ DelEmpty( false );
+}
+
+bool SwSectionFrame::ShouldBwdMoved( SwLayoutFrame *, bool & )
+{
+ OSL_FAIL( "Oops, where is my tinfoil hat?" );
+ return false;
+}
+
+const SwSectionFormat* SwSectionFrame::GetEndSectFormat_() const
+{
+ const SwSectionFormat *pFormat = m_pSection->GetFormat();
+ while( !pFormat->GetEndAtTextEnd().IsAtEnd() )
+ {
+ if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pFormat->GetRegisteredIn()) )
+ pFormat = pNewFormat;
+ else
+ return nullptr;
+ }
+ return pFormat;
+}
+
+static void lcl_FindContentFrame( SwContentFrame* &rpContentFrame, SwFootnoteFrame* &rpFootnoteFrame,
+ SwFrame* pFrame, bool &rbChkFootnote )
+{
+ if( !pFrame )
+ return;
+
+ while( pFrame->GetNext() )
+ pFrame = pFrame->GetNext();
+ while( !rpContentFrame && pFrame )
+ {
+ if( pFrame->IsContentFrame() )
+ rpContentFrame = static_cast<SwContentFrame*>(pFrame);
+ else if( pFrame->IsLayoutFrame() )
+ {
+ if( pFrame->IsFootnoteFrame() )
+ {
+ if( rbChkFootnote )
+ {
+ rpFootnoteFrame = static_cast<SwFootnoteFrame*>(pFrame);
+ rbChkFootnote = rpFootnoteFrame->GetAttr()->GetFootnote().IsEndNote();
+ }
+ }
+ else
+ lcl_FindContentFrame( rpContentFrame, rpFootnoteFrame,
+ static_cast<SwLayoutFrame*>(pFrame)->Lower(), rbChkFootnote );
+ }
+ pFrame = pFrame->GetPrev();
+ }
+}
+
+SwContentFrame *SwSectionFrame::FindLastContent( SwFindMode nMode )
+{
+ SwContentFrame *pRet = nullptr;
+ SwFootnoteFrame *pFootnoteFrame = nullptr;
+ SwSectionFrame *pSect = this;
+ if( nMode != SwFindMode::None )
+ {
+ const SwSectionFormat *pFormat = IsEndnAtEnd() ? GetEndSectFormat() :
+ m_pSection->GetFormat();
+ do {
+ while( pSect->HasFollow() )
+ pSect = pSect->GetFollow();
+ SwFrame* pTmp = pSect->FindNext();
+ while( pTmp && pTmp->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pTmp)->GetSection() )
+ pTmp = pTmp->FindNext();
+ if( pTmp && pTmp->IsSctFrame() &&
+ static_cast<SwSectionFrame*>(pTmp)->IsDescendantFrom( pFormat ) )
+ pSect = static_cast<SwSectionFrame*>(pTmp);
+ else
+ break;
+ } while( true );
+ }
+ bool bFootnoteFound = nMode == SwFindMode::EndNote;
+ do
+ {
+ lcl_FindContentFrame( pRet, pFootnoteFrame, pSect->Lower(), bFootnoteFound );
+ if( pRet || !pSect->IsFollow() || nMode == SwFindMode::None ||
+ ( SwFindMode::MyLast == nMode && this == pSect ) )
+ break;
+ pSect = pSect->FindMaster();
+ } while( pSect );
+ if( ( nMode == SwFindMode::EndNote ) && pFootnoteFrame )
+ pRet = pFootnoteFrame->ContainsContent();
+ return pRet;
+}
+
+bool SwSectionFrame::CalcMinDiff( SwTwips& rMinDiff ) const
+{
+ if( ToMaximize( true ) )
+ {
+ SwRectFnSet aRectFnSet(this);
+ rMinDiff = aRectFnSet.GetPrtBottom(*GetUpper());
+ rMinDiff = aRectFnSet.BottomDist( getFrameArea(), rMinDiff );
+ return true;
+ }
+ return false;
+}
+
+/**
+ * CollectEndnotes looks for endnotes in the sectionfrm and his follows,
+ * the endnotes will cut off the layout and put into the array.
+ * If the first endnote is not a master-SwFootnoteFrame, the whole sectionfrm
+ * contains only endnotes and it is not necessary to collect them.
+ */
+static SwFootnoteFrame* lcl_FindEndnote( SwSectionFrame* &rpSect, bool &rbEmpty,
+ SwLayouter *pLayouter )
+{
+ // if rEmpty is set, the rpSect is already searched
+ SwSectionFrame* pSect = rbEmpty ? rpSect->GetFollow() : rpSect;
+ while( pSect )
+ {
+ OSL_ENSURE( (pSect->Lower() && pSect->Lower()->IsColumnFrame()) || pSect->GetUpper()->IsFootnoteFrame(),
+ "InsertEndnotes: Where's my column?" );
+
+ // i73332: Columned section in endnote
+ SwColumnFrame* pCol = nullptr;
+ if(pSect->Lower() && pSect->Lower()->IsColumnFrame())
+ pCol = static_cast<SwColumnFrame*>(pSect->Lower());
+
+ while( pCol ) // check all columns
+ {
+ SwFootnoteContFrame* pFootnoteCont = pCol->FindFootnoteCont();
+ if( pFootnoteCont )
+ {
+ SwFootnoteFrame* pRet = static_cast<SwFootnoteFrame*>(pFootnoteCont->Lower());
+ while( pRet ) // look for endnotes
+ {
+ /* CollectEndNode can destroy pRet so we need to get the
+ next early
+ */
+ SwFootnoteFrame* pRetNext = static_cast<SwFootnoteFrame*>(pRet->GetNext());
+ if( pRet->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ if( pRet->GetMaster() )
+ {
+ if( pLayouter )
+ pLayouter->CollectEndnote( pRet );
+ else
+ return nullptr;
+ }
+ else
+ return pRet; // Found
+ }
+ pRet = pRetNext;
+ }
+ }
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ }
+ rpSect = pSect;
+ pSect = pLayouter ? pSect->GetFollow() : nullptr;
+ rbEmpty = true;
+ }
+ return nullptr;
+}
+
+static void lcl_ColumnRefresh( SwSectionFrame* pSect, bool bFollow )
+{
+ vcl::RenderContext* pRenderContext = pSect->getRootFrame()->GetCurrShell()->GetOut();
+ while( pSect )
+ {
+ bool bOldLock = pSect->IsColLocked();
+ pSect->ColLock();
+ if( pSect->Lower() && pSect->Lower()->IsColumnFrame() )
+ {
+ SwColumnFrame *pCol = static_cast<SwColumnFrame*>(pSect->Lower());
+ do
+ { pCol->InvalidateSize_();
+ pCol->InvalidatePos_();
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->InvalidateSize_();
+ pCol->Calc(pRenderContext); // calculation of column and
+ static_cast<SwLayoutFrame*>(pCol)->Lower()->Calc(pRenderContext); // body
+ pCol = static_cast<SwColumnFrame*>(pCol->GetNext());
+ } while ( pCol );
+ }
+ if( !bOldLock )
+ pSect->ColUnlock();
+ if( bFollow )
+ pSect = pSect->GetFollow();
+ else
+ pSect = nullptr;
+ }
+}
+
+void SwSectionFrame::CollectEndnotes( SwLayouter* pLayouter )
+{
+ OSL_ENSURE( IsColLocked(), "CollectEndnotes: You love the risk?" );
+ // i73332: Section in footnode does not have columns!
+ OSL_ENSURE( (Lower() && Lower()->IsColumnFrame()) || GetUpper()->IsFootnoteFrame(), "Where's my column?" );
+
+ SwSectionFrame* pSect = this;
+ SwFootnoteFrame* pFootnote;
+ bool bEmpty = false;
+ // pSect is the last sectionfrm without endnotes or the this-pointer
+ // the first sectionfrm with endnotes may be destroyed, when the endnotes
+ // is cutted
+ while( nullptr != (pFootnote = lcl_FindEndnote( pSect, bEmpty, pLayouter )) )
+ pLayouter->CollectEndnote( pFootnote );
+ if( pLayouter->HasEndnotes() )
+ lcl_ColumnRefresh( this, true );
+}
+
+/** Fits the size to the surroundings.
+|*
+|* Those that have a Follow or foot notes, have to extend until
+|* the lower edge of a upper (bMaximize)
+|* They must not extend above the Upper, as the case may be one can
+|* try to grow its upper (bGrow)
+|* If the size had to be changed, the content is calculated.
+|*
+|* @note: perform calculation of content, only if height has changed (OD 18.09.2002 #100522#)
+|*/
+void SwSectionFrame::CheckClipping( bool bGrow, bool bMaximize )
+{
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nDiff;
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ if( bGrow && ( !IsInFly() || !GetUpper()->IsColBodyFrame() ||
+ !FindFlyFrame()->IsLocked() ) )
+ {
+ nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
+ if( !bMaximize )
+ nDiff += Undersize();
+ if( nDiff > 0 )
+ {
+ tools::Long nAdd = GetUpper()->Grow( nDiff );
+ if( aRectFnSet.IsVert() )
+ nDeadLine -= nAdd;
+ else
+ nDeadLine += nAdd;
+ }
+ }
+ nDiff = -aRectFnSet.BottomDist( getFrameArea(), nDeadLine );
+ SetUndersized( !bMaximize && nDiff >= 0 );
+ const bool bCalc = ( IsUndersized() || bMaximize ) &&
+ ( nDiff ||
+ aRectFnSet.GetTop(getFramePrintArea()) > aRectFnSet.GetHeight(getFrameArea()) );
+ // OD 03.11.2003 #i19737# - introduce local variable <bExtraCalc> to indicate
+ // that a calculation has to be done beside the value of <bCalc>.
+ bool bExtraCalc = false;
+ if( !bCalc && !bGrow && IsAnyNoteAtEnd() && !IsInFootnote() )
+ {
+ SwSectionFrame *pSect = this;
+ bool bEmpty = false;
+ SwLayoutFrame* pFootnote = IsEndnAtEnd() ?
+ lcl_FindEndnote( pSect, bEmpty, nullptr ) : nullptr;
+ if( pFootnote )
+ {
+ pFootnote = pFootnote->FindFootnoteBossFrame();
+ SwFrame* pTmp = FindLastContent( SwFindMode::LastCnt );
+ // OD 08.11.2002 #104840# - use <SwLayoutFrame::IsBefore(..)>
+ if ( pTmp && pFootnote->IsBefore( pTmp->FindFootnoteBossFrame() ) )
+ bExtraCalc = true;
+ }
+ else if( GetFollow() && !GetFollow()->ContainsAny() )
+ bExtraCalc = true;
+ }
+ if ( !(bCalc || bExtraCalc) )
+ return;
+
+ nDiff = aRectFnSet.YDiff( nDeadLine, aRectFnSet.GetTop(getFrameArea()) );
+ if( nDiff < 0 )
+ nDeadLine = aRectFnSet.GetTop(getFrameArea());
+ const Size aOldSz( getFramePrintArea().SSize() );
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetBottom( aFrm, nDeadLine );
+ }
+
+ nDiff = aRectFnSet.GetHeight(getFrameArea());
+ if( nTop > nDiff )
+ nTop = nDiff;
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+
+ // OD 18.09.2002 #100522#
+ // Determine, if height has changed.
+ // Note: In vertical layout the height equals the width value.
+ bool bHeightChanged = aRectFnSet.IsVert() ?
+ (aOldSz.Width() != getFramePrintArea().Width()) :
+ (aOldSz.Height() != getFramePrintArea().Height());
+ // Last but not least we have changed the height again, thus the inner
+ // layout (columns) is calculated and the content as well.
+ // OD 18.09.2002 #100522#
+ // calculate content, only if height has changed.
+ // OD 03.11.2003 #i19737# - restriction of content calculation too strong.
+ // If an endnote has an incorrect position or a follow section contains
+ // no content except footnotes/endnotes, the content has also been calculated.
+ if ( !(( bHeightChanged || bExtraCalc ) && Lower()) )
+ return;
+
+ if( Lower()->IsColumnFrame() )
+ {
+ lcl_ColumnRefresh( this, false );
+ ::CalcContent( this );
+ }
+ else
+ {
+ ChgLowersProp( aOldSz );
+ if( !bMaximize && !IsContentLocked() )
+ ::CalcContent( this );
+ }
+}
+
+void SwSectionFrame::SimpleFormat()
+{
+ if ( IsJoinLocked() || IsColLocked() )
+ return;
+ LockJoin();
+ SwRectFnSet aRectFnSet(this);
+ if( GetPrev() || GetUpper() )
+ {
+ // assure notifications on position changes.
+ const SwLayNotify aNotify( this );
+ aRectFnSet.MakePos( *this, GetUpper(), GetPrev(), false );
+ setFrameAreaPositionValid(true);
+ }
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ // OD 22.10.2002 #97265# - call always method <lcl_ColumnRefresh(..)>, in
+ // order to get calculated lowers, not only if there space left in its upper.
+ if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) >= 0 )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetBottom( aFrm, nDeadLine );
+ }
+
+ tools::Long nHeight = aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nTop = CalcUpperSpace();
+ if( nTop > nHeight )
+ nTop = nHeight;
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+ }
+ lcl_ColumnRefresh( this, false );
+ UnlockJoin();
+}
+
+namespace {
+
+// #i40147# - helper class to perform extra section format
+// to position anchored objects and to keep the position of whose objects locked.
+class ExtraFormatToPositionObjs
+{
+ private:
+ SwSectionFrame* mpSectFrame;
+ bool mbExtraFormatPerformed;
+
+ public:
+ explicit ExtraFormatToPositionObjs( SwSectionFrame& _rSectFrame)
+ : mpSectFrame( &_rSectFrame ),
+ mbExtraFormatPerformed( false )
+ {}
+
+ ~ExtraFormatToPositionObjs()
+ {
+ if ( !mbExtraFormatPerformed )
+ return;
+
+ // release keep locked position of lower floating screen objects
+ SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
+ SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetKeepPosLocked( false );
+ }
+ }
+ }
+ }
+
+ // #i81555#
+ void InitObjs( SwFrame& rFrame )
+ {
+ SwSortedObjs* pObjs = rFrame.GetDrawObjs();
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ pAnchoredObj->UnlockPosition();
+ pAnchoredObj->SetClearedEnvironment( false );
+ }
+ }
+ if ( rFrame.IsLayoutFrame() )
+ {
+ SwFrame* pLowerFrame = rFrame.GetLower();
+ while ( pLowerFrame != nullptr )
+ {
+ InitObjs( *pLowerFrame );
+
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+ }
+ }
+
+ void FormatSectionToPositionObjs()
+ {
+ vcl::RenderContext* pRenderContext = mpSectFrame->getRootFrame()->GetCurrShell()->GetOut();
+ // perform extra format for multi-columned section.
+ if ( !(mpSectFrame->Lower() && mpSectFrame->Lower()->IsColumnFrame() &&
+ mpSectFrame->Lower()->GetNext()) )
+ return;
+
+ // grow section till bottom of printing area of upper frame
+ SwRectFnSet aRectFnSet(mpSectFrame);
+ SwTwips nTopMargin = aRectFnSet.GetTopMargin(*mpSectFrame);
+ Size aOldSectPrtSize( mpSectFrame->getFramePrintArea().SSize() );
+ SwTwips nDiff = aRectFnSet.BottomDist( mpSectFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*mpSectFrame->GetUpper()) );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*mpSectFrame);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ }
+
+ aRectFnSet.SetYMargins( *mpSectFrame, nTopMargin, 0 );
+ // #i59789#
+ // suppress formatting, if printing area of section is too narrow
+ if ( aRectFnSet.GetHeight(mpSectFrame->getFramePrintArea()) <= 0 )
+ {
+ return;
+ }
+ mpSectFrame->ChgLowersProp( aOldSectPrtSize );
+
+ // format column frames and its body and footnote container
+ SwColumnFrame* pColFrame = static_cast<SwColumnFrame*>(mpSectFrame->Lower());
+ while ( pColFrame )
+ {
+ pColFrame->Calc(pRenderContext);
+ pColFrame->Lower()->Calc(pRenderContext);
+ if ( pColFrame->Lower()->GetNext() )
+ {
+ pColFrame->Lower()->GetNext()->Calc(pRenderContext);
+ }
+
+ pColFrame = static_cast<SwColumnFrame*>(pColFrame->GetNext());
+ }
+
+ // unlock position of lower floating screen objects for the extra format
+ // #i81555#
+ // Section frame can already have changed the page and its content
+ // can still be on the former page.
+ // Thus, initialize objects via lower-relationship
+ InitObjs( *mpSectFrame );
+
+ // format content - first with collecting its foot-/endnotes before content
+ // format, second without collecting its foot-/endnotes.
+ ::CalcContent( mpSectFrame );
+ ::CalcContent( mpSectFrame, true );
+
+ // keep locked position of lower floating screen objects
+ SwPageFrame* pPageFrame = mpSectFrame->FindPageFrame();
+ SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( mpSectFrame->IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetKeepPosLocked( true );
+ }
+ }
+ }
+
+ mbExtraFormatPerformed = true;
+
+ }
+};
+
+}
+
+/// "formats" the frame; Frame and PrtArea
+void SwSectionFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs *pAttr )
+{
+ if( !m_pSection ) // via DelEmpty
+ {
+#ifdef DBG_UTIL
+ OSL_ENSURE( getRootFrame()->IsInDelList( this ), "SectionFrame without Section" );
+#endif
+ setFrameAreaPositionValid(true);
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ return;
+ }
+
+ SwRectFnSet aRectFnSet(this);
+
+ if ( !isFramePrintAreaValid() )
+ {
+ PROTOCOL( this, PROT::PrintArea, DbgAction::NONE, nullptr )
+ setFramePrintAreaValid(true);
+ SwTwips nUpper = CalcUpperSpace();
+
+ // #109700# LRSpace for sections
+ const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
+ aRectFnSet.SetXMargins( *this, rLRSpace.GetLeft(), rLRSpace.GetRight() );
+
+ if( nUpper != aRectFnSet.GetTopMargin(*this) )
+ {
+ setFrameAreaSizeValid(false);
+ SwFrame* pOwn = ContainsAny();
+ if( pOwn )
+ pOwn->InvalidatePos_();
+ }
+ aRectFnSet.SetYMargins( *this, nUpper, 0 );
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ PROTOCOL_ENTER( this, PROT::Size, DbgAction::NONE, nullptr )
+ const tools::Long nOldHeight = aRectFnSet.GetHeight(getFrameArea());
+ bool bOldLock = IsColLocked();
+ ColLock();
+
+ setFrameAreaSizeValid(true);
+
+ // The size is only determined by the content, if the SectFrame does not have a
+ // Follow. Otherwise it fills (occupies) the Upper down to the lower edge.
+ // It is not responsible for the text flow, but the content is.
+ bool bMaximize = ToMaximize( false );
+
+ // OD 2004-05-17 #i28701# - If the wrapping style has to be considered
+ // on object positioning, an extra formatting has to be performed
+ // to determine the correct positions the floating screen objects.
+ // #i40147#
+ // use new helper class <ExtraFormatToPositionObjs>.
+ // This class additionally keep the locked position of the objects
+ // and releases this position lock keeping on destruction.
+ ExtraFormatToPositionObjs aExtraFormatToPosObjs( *this );
+ if ( !bMaximize &&
+ GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
+ !GetFormat()->GetBalancedColumns().GetValue() )
+ {
+ aExtraFormatToPosObjs.FormatSectionToPositionObjs();
+ }
+
+ // Column widths have to be adjusted before calling CheckClipping.
+ // CheckClipping can cause the formatting of the lower frames
+ // which still have a width of 0.
+ const bool bHasColumns = Lower() && Lower()->IsColumnFrame();
+ if ( bHasColumns && Lower()->GetNext() )
+ AdjustColumns( nullptr, false );
+
+ if( GetUpper() )
+ {
+ const tools::Long nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetWidth( aFrm, nWidth );
+ }
+
+ // #109700# LRSpace for sections
+ {
+ const SvxLRSpaceItem& rLRSpace = GetFormat()->GetLRSpace();
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetWidth( aPrt, nWidth - rLRSpace.GetLeft() - rLRSpace.GetRight() );
+ }
+
+ // OD 15.10.2002 #103517# - allow grow in online layout
+ // Thus, set <..IsBrowseMode()> as parameter <bGrow> on calling
+ // method <CheckClipping(..)>.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ CheckClipping( pSh && pSh->GetViewOptions()->getBrowseMode(), bMaximize );
+ bMaximize = ToMaximize( false );
+ setFrameAreaSizeValid(true);
+ }
+
+ // Check the width of the columns and adjust if necessary
+ if ( bHasColumns && ! Lower()->GetNext() && bMaximize )
+ static_cast<SwColumnFrame*>(Lower())->Lower()->Calc(pRenderContext);
+
+ if ( !bMaximize )
+ {
+ SwTwips nRemaining = aRectFnSet.GetTopMargin(*this);
+ SwFrame *pFrame = m_pLower;
+ if( pFrame )
+ {
+ if( pFrame->IsColumnFrame() && pFrame->GetNext() )
+ {
+ // #i61435#
+ // suppress formatting, if upper frame has height <= 0
+ if ( aRectFnSet.GetHeight(GetUpper()->getFrameArea()) > 0 )
+ {
+ FormatWidthCols( *pAttr, nRemaining, MINLAY );
+ }
+ // #126020# - adjust check for empty section
+ // #130797# - correct fix #126020#
+ while( HasFollow() && !GetFollow()->ContainsContent() &&
+ !GetFollow()->ContainsAny( true ) )
+ {
+ SwFrame* pOld = GetFollow();
+ GetFollow()->DelEmpty( false );
+ if( pOld == GetFollow() )
+ break;
+ }
+ bMaximize = ToMaximize( false );
+ nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ }
+ else
+ {
+ if( pFrame->IsColumnFrame() )
+ {
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ CalcFootnoteContent();
+ }
+ // If we are in a columned frame which calls a CalcContent
+ // in the FormatWidthCols, the content might need calculating
+ if( pFrame && !pFrame->isFrameAreaDefinitionValid() && IsInFly() &&
+ FindFlyFrame()->IsColLocked() )
+ ::CalcContent( this );
+ nRemaining += InnerHeight();
+ bMaximize = HasFollow();
+ }
+ }
+
+ SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
+ if( nDiff < 0)
+ {
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ {
+ tools::Long nBottom = aRectFnSet.GetBottom(getFrameArea());
+ nBottom = aRectFnSet.YInc( nBottom, -nDiff );
+ tools::Long nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
+ if( nTmpDiff > 0 )
+ {
+ nTmpDiff = GetUpper()->Grow( nTmpDiff, true );
+ nDeadLine = aRectFnSet.YInc( nDeadLine, nTmpDiff );
+ nTmpDiff = aRectFnSet.YDiff( nBottom, nDeadLine );
+ if( nTmpDiff > 0 )
+ nDiff += nTmpDiff;
+ if( nDiff > 0 )
+ nDiff = 0;
+ }
+ }
+ }
+ if( nDiff )
+ {
+ tools::Long nTmp = nRemaining - aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nTmp );
+ }
+
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+ InvalidateNextPos();
+
+ if (m_pLower && (!m_pLower->IsColumnFrame() || !m_pLower->GetNext()))
+ {
+ // If a single-column section just created the space that
+ // was requested by the "undersized" paragraphs, then they
+ // have to be invalidated and calculated, so they fully cover it
+ pFrame = m_pLower;
+ if( pFrame->IsColumnFrame() )
+ {
+ pFrame->InvalidateSize_();
+ pFrame->InvalidatePos_();
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwColumnFrame*>(pFrame)->Lower();
+ pFrame->Calc(pRenderContext);
+ pFrame = static_cast<SwLayoutFrame*>(pFrame)->Lower();
+ CalcFootnoteContent();
+ }
+ bool bUnderSz = false;
+ while( pFrame )
+ {
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ {
+ pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ bUnderSz = true;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ if( bUnderSz && !IsContentLocked() )
+ ::CalcContent( this );
+ }
+ }
+ }
+
+ // Do not exceed the lower edge of the Upper.
+ // Do not extend below the lower edge with Sections with Follows
+ if ( GetUpper() )
+ CheckClipping( true, bMaximize );
+ if( !bOldLock )
+ ColUnlock();
+ tools::Long nDiff = nOldHeight - aRectFnSet.GetHeight(getFrameArea());
+
+ if( nDiff > 0 )
+ {
+ if( !GetNext() )
+ SetRetouche(); // Take over the retouching ourselves
+ if( GetUpper() && !GetUpper()->IsFooterFrame() )
+ GetUpper()->Shrink( nDiff );
+ }
+
+ if( IsUndersized() )
+ {
+ setFramePrintAreaValid(true);
+ }
+
+}
+
+/// Returns the next layout sheet where the frame can be moved in.
+/// New pages are created only if specified by the parameter.
+SwLayoutFrame *SwFrame::GetNextSctLeaf( MakePageType eMakePage )
+{
+ // Attention: Nested sections are currently not supported
+
+ PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::NextSect, GetUpper()->FindSctFrame() )
+
+ // Shortcuts for "columned" sections, if we're not in the last column
+ // Can we slide to the next column of the section?
+ if( IsColBodyFrame() && GetUpper()->GetNext() )
+ return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetNext())->Lower());
+ if( GetUpper()->IsColBodyFrame() && GetUpper()->GetUpper()->GetNext() )
+ return static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(GetUpper()->GetUpper()->GetNext())->Lower());
+ // Inside a table-in-section, or sections of headers/footers, there can be only
+ // one column shift be made, one of the above shortcuts should have applied!
+ if( !CanContainSplitSection(GetUpper()) || FindFooterOrHeader() )
+ return nullptr;
+
+ SwSectionFrame *pSect = FindSctFrame();
+ bool bWrongPage = false;
+ assert(pSect && "GetNextSctLeaf: Missing SectionFrame");
+
+ // Shortcut for sections with Follows. That's ok,
+ // if no columns or pages (except dummy pages) lie in between.
+ // In case of linked frames and in footnotes the shortcut would get
+ // even more costly
+ if( pSect->HasFollow() && pSect->IsInDocBody() && !pSect->IsInTab() )
+ {
+ if( pSect->GetFollow() == pSect->GetNext() )
+ {
+ SwPageFrame *pPg = pSect->GetFollow()->FindPageFrame();
+ if( WrongPageDesc( pPg ) )
+ bWrongPage = true;
+ else
+ return FirstLeaf( pSect->GetFollow() );
+ }
+ else
+ {
+ SwFrame* pTmp;
+ if( !pSect->GetUpper()->IsColBodyFrame() ||
+ nullptr == ( pTmp = pSect->GetUpper()->GetUpper()->GetNext() ) )
+ pTmp = pSect->FindPageFrame()->GetNext();
+ if( pTmp ) // is now the next column or page
+ {
+ SwFrame* pTmpX = pTmp;
+ if( pTmp->IsPageFrame() && static_cast<SwPageFrame*>(pTmp)->IsEmptyPage() )
+ pTmp = pTmp->GetNext(); // skip dummy pages
+ SwFrame *pUp = pSect->GetFollow()->GetUpper();
+ // pUp becomes the next column if the Follow lies in a column
+ // that is not a "not first" one, otherwise the page
+ if( !pUp->IsColBodyFrame() ||
+ !( pUp = pUp->GetUpper() )->GetPrev() )
+ pUp = pUp->FindPageFrame();
+ // Now pUp and pTmp have to be the same page/column, otherwise
+ // pages or columns lie between Master and Follow
+ if( pUp == pTmp || pUp->GetNext() == pTmpX )
+ {
+ SwPageFrame* pNxtPg = pUp->IsPageFrame() ?
+ static_cast<SwPageFrame*>(pUp) : pUp->FindPageFrame();
+ if( WrongPageDesc( pNxtPg ) )
+ bWrongPage = true;
+ else
+ return FirstLeaf( pSect->GetFollow() );
+ }
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ std::vector<SwFrame *> parents;
+ for (SwFrame * pTmp = GetUpper(); pTmp && !pTmp->IsPageFrame(); pTmp = pTmp->GetUpper())
+ {
+ parents.push_back(pTmp);
+ }
+#endif
+
+ // Always end up in the same section: Body again inside Body etc.
+ const bool bBody = IsInDocBody();
+ const bool bFootnotePage = FindPageFrame()->IsFootnotePage();
+
+ // The "pLayLeaf is in a table" case is rejected by default, so that it
+ // can't happen that we try to move a table to one of its own cells.
+ bool bLayLeafTableAllowed = false;
+ SwLayoutFrame *pLayLeaf;
+
+ SwLayoutFrame* pCellLeaf = nullptr;
+ if (GetUpper()->IsInTab())
+ {
+ if (IsTabFrame())
+ {
+ return nullptr; // table in section in table: split disabled for now
+ }
+ // We are *in* a table (not an outermost SwTabFrame), see if there
+ // is a follow cell frame created already.
+ pCellLeaf = GetNextCellLeaf();
+ if (!pCellLeaf)
+ {
+ SAL_WARN("sw.layout", "section is in table, but the table is not split");
+ return nullptr;
+ }
+ }
+
+ // A shortcut for TabFrames such that not all cells need to be visited
+ if( bWrongPage )
+ pLayLeaf = nullptr;
+ else if( IsTabFrame() )
+ {
+ SwFrame *const pTmpCnt = static_cast<SwTabFrame*>(this)->FindLastContentOrTable();
+ pLayLeaf = pTmpCnt ? pTmpCnt->GetUpper() : nullptr;
+ }
+ else if (pCellLeaf && CanContainSplitSection(this))
+ {
+ // This frame is in a table-not-in-section, its follow should be
+ // inserted under the follow of the frame's cell.
+ pLayLeaf = pCellLeaf;
+ if (pLayLeaf->FindTabFrame() == FindTabFrame())
+ SAL_WARN("sw.layout", "my table frame and my follow's table frame is the same");
+ // In this case pLayLeaf pointing to an in-table frame is OK.
+ bLayLeafTableAllowed = true;
+ }
+ else
+ {
+ pLayLeaf = GetNextLayoutLeaf();
+ if( IsColumnFrame() )
+ {
+ while( pLayLeaf && static_cast<SwColumnFrame*>(this)->IsAnLower( pLayLeaf ) )
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ }
+ }
+
+ SwLayoutFrame *pOldLayLeaf = nullptr; // Such that in case of newly
+ // created pages, the search is
+ // not started over at the beginning
+
+ while( true )
+ {
+ if( pLayLeaf )
+ {
+ // A layout leaf was found, let's see whether it can store me or
+ // another SectionFrame can be inserted here, or we have to continue
+ // searching
+ SwPageFrame* pNxtPg = pLayLeaf->FindPageFrame();
+ if ( !bFootnotePage && pNxtPg->IsFootnotePage() )
+ { // If I reached the end note pages it's over
+ pLayLeaf = nullptr;
+ continue;
+ }
+ // Once inBody always inBody, don't step into tables-in-sections and not into other sections
+ if ( (bBody && !pLayLeaf->IsInDocBody()) ||
+ (IsInFootnote() != pLayLeaf->IsInFootnote() ) ||
+ (pLayLeaf->IsInTab() && !bLayLeafTableAllowed) ||
+ ( pLayLeaf->IsInSct() && ( !pSect->HasFollow()
+ || pSect->GetFollow() != pLayLeaf->FindSctFrame() ) ) )
+ {
+ // Rejected - try again.
+ pOldLayLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetNextLayoutLeaf();
+ continue;
+ }
+ // Page desc is never wrong in case of sections-in-tables: in that
+ // case pLayLeaf points to our section's cell's follow, which is
+ // fine to be on the same page. New page creation is handled when
+ // creating / moving the cell frame.
+ if( WrongPageDesc( pNxtPg ) && !bLayLeafTableAllowed )
+ {
+ if( bWrongPage )
+ break; // there's a column between me and my right page
+ pLayLeaf = nullptr;
+ bWrongPage = true;
+ pOldLayLeaf = nullptr;
+ continue;
+ }
+ }
+ // There is no further LayoutFrame that fits, so a new page
+ // has to be created, although new pages are worthless within a frame
+ else if( !pSect->IsInFly() &&
+ ( eMakePage == MAKEPAGE_APPEND || eMakePage == MAKEPAGE_INSERT ) )
+ {
+ InsertPage(pOldLayLeaf ? pOldLayLeaf->FindPageFrame() : FindPageFrame(),
+ false );
+ // and again the whole thing
+ if (pCellLeaf && CanContainSplitSection(this))
+ // GetNextLayoutLeaf() would refer to the next cell in the same
+ // row, avoid that. pCellLeaf points to the correct cell in the
+ // follow table, and in the next round it'll be used, as we now
+ // have a next page.
+ pLayLeaf = pCellLeaf;
+ else
+ pLayLeaf = pOldLayLeaf ? pOldLayLeaf : GetNextLayoutLeaf();
+ continue;
+ }
+ break;
+ }
+
+ if( pLayLeaf )
+ {
+ // We have found the suitable layout sheet. If there (in the sheet) is
+ // already a Follow of our section, we take its first layout sheet,
+ // otherwise it is time to create a section follow
+ SwSectionFrame* pNew = nullptr;
+
+ // This can be omitted if existing Follows were cut short
+ SwFrame* pFirst = pLayLeaf->Lower();
+ // Here SectionFrames that are to be deleted must be ignored
+ while( pFirst && pFirst->IsSctFrame() && !static_cast<SwSectionFrame*>(pFirst)->GetSection() )
+ pFirst = pFirst->GetNext();
+ if( pFirst && pFirst->IsSctFrame() && pSect->GetFollow() == pFirst )
+ pNew = pSect->GetFollow();
+ else if( MAKEPAGE_NOSECTION == eMakePage )
+ return pLayLeaf;
+ else if (pSect->GetSection())
+ {
+ pNew = new SwSectionFrame( *pSect, false );
+ pNew->InsertBefore( pLayLeaf, pLayLeaf->Lower() );
+ pNew->Init();
+ SwRectFnSet aRectFnSet(pNew);
+ aRectFnSet.MakePos( *pNew, pLayLeaf, nullptr, true );
+
+#ifndef NDEBUG
+ { // sanity check the parents of the new frame vs. the old frame
+ SwFrame * pTmp = pNew;
+ auto iter(parents.begin());
+ if (parents.size() >= 2 &&
+ parents[0]->IsBodyFrame() && parents[1]->IsColumnFrame())
+ { // this only inserts section frame - remove column
+ assert(parents[2]->IsSctFrame() || IsSctFrame());
+ if (parents[2]->IsSctFrame())
+ std::advance(iter, +2);
+ else
+ pTmp = pTmp->GetUpper();
+ }
+ else if (IsBodyFrame() && parents.size() >= 1
+ && parents[0]->IsColumnFrame())
+ { // same as above, special case: "this" is the body frame
+ assert(parents[1]->IsSctFrame());
+ std::advance(iter, +1);
+ }
+ else if (IsSctFrame()) // special case: "this" is the section
+ {
+ pTmp = pTmp->GetUpper();
+ }
+
+ for ( ; iter != parents.end(); ++iter)
+ {
+ if (pTmp->IsPageFrame())
+ {
+ if ((*iter)->IsColumnFrame() &&
+ (iter + 1) != parents.end() && (*(iter + 1))->IsBodyFrame())
+ { // page style has columns - evidently these are
+ break; // added later?
+ }
+ assert(!pTmp->IsPageFrame());
+ }
+ assert(pTmp->GetType() == (*iter)->GetType());
+ // for cell frames and table frames:
+ // 1) there may be multiple follow frames of the old one
+ // 2) the new frame may be identical to the old one
+ // (not sure if this is allowed, but it happens now
+ // for the outer table of a nested table)
+ if (pTmp->IsCellFrame())
+ {
+ SwCellFrame const*const pNewF(static_cast<SwCellFrame*>(pTmp));
+ SwCellFrame const*const pOldF(static_cast<SwCellFrame*>(*iter));
+ bool bFollowFound(false);
+ for (SwCellFrame const* pOldIter = pOldF;
+ pOldIter; pOldIter = pOldIter->GetFollowCell())
+ {
+ if (pOldIter == pNewF)
+ {
+ bFollowFound = true;
+ break;
+ }
+ }
+ assert(bFollowFound);
+ }
+ else if (pTmp->IsFlowFrame())
+ {
+ SwFlowFrame const*const pNewF(SwFlowFrame::CastFlowFrame(pTmp));
+ SwFlowFrame const*const pOldF(SwFlowFrame::CastFlowFrame(*iter));
+ bool bFollowFound(false);
+ for (SwFlowFrame const* pOldIter = pOldF;
+ pOldIter; pOldIter = pOldIter->GetFollow())
+ {
+ if (pOldIter == pNewF)
+ {
+ bFollowFound = true;
+ break;
+ }
+ }
+ assert(bFollowFound);
+ }
+ pTmp = pTmp->GetUpper();
+ }
+ assert(pTmp == nullptr /* SwFlyAtContentFrame case */
+ || pTmp->IsPageFrame() // usual case
+ // the new page has columns, but the old page did not
+ || (pTmp->IsColumnFrame() && pTmp->GetUpper()->IsBodyFrame()
+ && pTmp->GetUpper()->GetUpper()->IsPageFrame()));
+ }
+#endif
+
+ // If our section frame has a successor then that has to be
+ // moved behind the new Follow of the section frames
+ SwFrame* pTmp = pSect->GetNext();
+ if( pTmp && pTmp != pSect->GetFollow() )
+ {
+ SwFlowFrame* pNxt;
+ SwContentFrame* pNxtContent = nullptr;
+ if( pTmp->IsContentFrame() )
+ {
+ pNxt = static_cast<SwContentFrame*>(pTmp);
+ pNxtContent = static_cast<SwContentFrame*>(pTmp);
+ }
+ else
+ {
+ pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
+ if( pTmp->IsSctFrame() )
+ pNxt = static_cast<SwSectionFrame*>(pTmp);
+ else
+ {
+ assert(pTmp->IsTabFrame());
+ pNxt = static_cast<SwTabFrame*>(pTmp);
+ }
+ while( !pNxtContent && nullptr != ( pTmp = pTmp->GetNext() ) )
+ {
+ if( pTmp->IsContentFrame() )
+ pNxtContent = static_cast<SwContentFrame*>(pTmp);
+ else
+ pNxtContent = static_cast<SwLayoutFrame*>(pTmp)->ContainsContent();
+ }
+ }
+ if( pNxtContent )
+ {
+ SwFootnoteBossFrame* pOldBoss = pSect->FindFootnoteBossFrame( true );
+ if( pOldBoss == pNxtContent->FindFootnoteBossFrame( true ) )
+ {
+ SwSaveFootnoteHeight aHeight( pOldBoss,
+ pOldBoss->getFrameArea().Top() + pOldBoss->getFrameArea().Height() );
+ pSect->GetUpper()->MoveLowerFootnotes( pNxtContent, pOldBoss,
+ pLayLeaf->FindFootnoteBossFrame( true ), false );
+ }
+ }
+ pNxt->MoveSubTree( pLayLeaf, pNew->GetNext() );
+ }
+ if( pNew->GetFollow() )
+ pNew->SimpleFormat();
+ }
+ // The wanted layout sheet is now the first of the determined SctFrames:
+ pLayLeaf = pNew ? FirstLeaf(pNew) : nullptr;
+ }
+ return pLayLeaf;
+}
+
+/// Returns the preceding layout sheet where the frame can be moved into
+SwLayoutFrame *SwFrame::GetPrevSctLeaf()
+{
+ PROTOCOL_ENTER( this, PROT::Leaf, DbgAction::PrevSect, GetUpper()->FindSctFrame() )
+
+ SwLayoutFrame* pCol;
+ // ColumnFrame always contain a BodyFrame now
+ if( IsColBodyFrame() )
+ pCol = GetUpper();
+ else if( GetUpper()->IsColBodyFrame() )
+ pCol = GetUpper()->GetUpper();
+ else
+ pCol = nullptr;
+ bool bJump = false;
+ if( pCol )
+ {
+ if( pCol->GetPrev() )
+ {
+ do
+ {
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetPrev());
+ // Is there any content?
+ if( static_cast<SwLayoutFrame*>(pCol->Lower())->Lower() )
+ {
+ if( bJump ) // Did we skip a blank page?
+ SwFlowFrame::SetMoveBwdJump( true );
+ return static_cast<SwLayoutFrame*>(pCol->Lower()); // The columnm body
+ }
+ bJump = true;
+ } while( pCol->GetPrev() );
+
+ // We get here when all columns are empty, pCol is now the
+ // first column, we need the body though
+ pCol = static_cast<SwLayoutFrame*>(pCol->Lower());
+ }
+ else
+ pCol = nullptr;
+ }
+
+ if( bJump ) // Did we skip a blank page?
+ SwFlowFrame::SetMoveBwdJump( true );
+
+ SwSectionFrame *pSect = FindSctFrame();
+ if (!pCol && pSect && IsInTab() && CanContainSplitSection(this))
+ {
+ // We don't have a previous section yet, and we're in a
+ // section-in-table.
+ if (SwFlowFrame* pPrecede = pSect->GetPrecede())
+ {
+ // Our section has a precede, work with that.
+ if (pPrecede->GetFrame().IsLayoutFrame())
+ pCol = static_cast<SwLayoutFrame*>(&pPrecede->GetFrame());
+ }
+ }
+
+ // Within sections in tables or section in headers/footers there can
+ // be only one column change be made, one of the above shortcuts should
+ // have applied, also when the section has a pPrev.
+ // Now we even consider an empty column...
+ OSL_ENSURE( pSect, "GetNextSctLeaf: Missing SectionFrame" );
+ if (!pSect || (IsInTab() && !IsTabFrame()) || FindFooterOrHeader())
+ return pCol;
+
+ // === IMPORTANT ===
+ // Precondition, which needs to be hold, is that the <this> frame can be
+ // inside a table, but then the found section frame <pSect> is also inside
+ // this table.
+
+ // #i95698#
+ // A table cell containing directly a section does not break - see lcl_FindSectionsInRow(..)
+ // Thus, a table inside a section, which is inside another table can only
+ // flow backward in the columns of its section.
+ // Note: The table cell, which contains the section, can not have a master table cell.
+ if ( IsTabFrame() && pSect->IsInTab() )
+ {
+ return pCol;
+ }
+
+ {
+ if (SwFrame *pPrv = pSect->GetIndPrev())
+ {
+ // Mooching, half dead SectionFrames shouldn't confuse us
+ while( pPrv && pPrv->IsSctFrame() && !static_cast<SwSectionFrame*>(pPrv)->GetSection() )
+ pPrv = pPrv->GetPrev();
+ if( pPrv )
+ return pCol;
+ }
+ }
+
+ const bool bBody = IsInDocBody();
+ const bool bFly = IsInFly();
+
+ SwLayoutFrame *pLayLeaf = GetPrevLayoutLeaf();
+ SwLayoutFrame *pPrevLeaf = nullptr;
+
+ while ( pLayLeaf )
+ {
+ // Never step into tables or sections
+ if ( pLayLeaf->IsInTab() || pLayLeaf->IsInSct() )
+ {
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ }
+ else if ( bBody && pLayLeaf->IsInDocBody() )
+ {
+ // If there is a pLayLeaf has a lower pLayLeaf is the frame we are looking for.
+ // Exception: pLayLeaf->Lower() is a zombie section frame
+ const SwFrame* pTmp = pLayLeaf->Lower();
+ // OD 11.04.2003 #108824# - consider, that the zombie section frame
+ // can have frame below it in the found layout leaf.
+ // Thus, skipping zombie section frame, if possible.
+ while ( pTmp && pTmp->IsSctFrame() &&
+ !( static_cast<const SwSectionFrame*>(pTmp)->GetSection() ) &&
+ pTmp->GetNext()
+ )
+ {
+ pTmp = pTmp->GetNext();
+ }
+ if ( pTmp &&
+ ( !pTmp->IsSctFrame() ||
+ ( static_cast<const SwSectionFrame*>(pTmp)->GetSection() )
+ )
+ )
+ {
+ break;
+ }
+ pPrevLeaf = pLayLeaf;
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ if ( pLayLeaf )
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ else if ( bFly )
+ break; // Contents in Flys every layout sheet should be right. Why?
+ else
+ pLayLeaf = pLayLeaf->GetPrevLayoutLeaf();
+ }
+ if( !pLayLeaf )
+ {
+ if( !pPrevLeaf )
+ return pCol;
+ pLayLeaf = pPrevLeaf;
+ }
+
+ SwSectionFrame* pNew = nullptr;
+ // At first go to the end of the layout sheet
+ SwFrame *pTmp = pLayLeaf->Lower();
+ if( pTmp )
+ {
+ while( pTmp->GetNext() )
+ pTmp = pTmp->GetNext();
+ if( pTmp->IsSctFrame() )
+ {
+ // Half dead ones only interfere here
+ while( !static_cast<SwSectionFrame*>(pTmp)->GetSection() && pTmp->GetPrev() &&
+ pTmp->GetPrev()->IsSctFrame() )
+ pTmp = pTmp->GetPrev();
+ if( static_cast<SwSectionFrame*>(pTmp)->GetFollow() == pSect )
+ pNew = static_cast<SwSectionFrame*>(pTmp);
+ }
+ }
+ if( !pNew )
+ {
+ pNew = new SwSectionFrame( *pSect, true );
+ pNew->InsertBefore( pLayLeaf, nullptr );
+ pNew->Init();
+ SwRectFnSet aRectFnSet(pNew);
+ aRectFnSet.MakePos( *pNew, pLayLeaf, pNew->GetPrev(), true );
+
+ pLayLeaf = FirstLeaf( pNew );
+ if( !pNew->Lower() ) // Format single column sections
+ {
+ pNew->MakePos();
+ pLayLeaf->Format(getRootFrame()->GetCurrShell()->GetOut()); // In order that the PrtArea is correct for the MoveBwd
+ }
+ else
+ pNew->SimpleFormat();
+ }
+ else
+ {
+ pLayLeaf = FirstLeaf( pNew );
+ if( pLayLeaf->IsColBodyFrame() )
+ {
+ // In existent section columns we're looking for the last not empty
+ // column.
+ SwLayoutFrame *pTmpLay = pLayLeaf;
+ while( pLayLeaf->GetUpper()->GetNext() )
+ {
+ pLayLeaf = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pLayLeaf->GetUpper()->GetNext())->Lower());
+ if( pLayLeaf->Lower() )
+ pTmpLay = pLayLeaf;
+ }
+ // If we skipped an empty column, we've to set the jump-flag
+ if( pLayLeaf != pTmpLay )
+ {
+ pLayLeaf = pTmpLay;
+ SwFlowFrame::SetMoveBwdJump( true );
+ }
+ }
+ }
+ return pLayLeaf;
+}
+
+static SwTwips lcl_DeadLine( const SwFrame* pFrame )
+{
+ const SwLayoutFrame* pUp = pFrame->GetUpper();
+ while( pUp && pUp->IsInSct() )
+ {
+ if( pUp->IsSctFrame() )
+ pUp = pUp->GetUpper();
+ // Columns now with BodyFrame
+ else if( pUp->IsColBodyFrame() && pUp->GetUpper()->GetUpper()->IsSctFrame() )
+ pUp = pUp->GetUpper()->GetUpper();
+ else
+ break;
+ }
+ SwRectFnSet aRectFnSet(pFrame);
+ return pUp ? aRectFnSet.GetPrtBottom(*pUp) :
+ aRectFnSet.GetBottom(pFrame->getFrameArea());
+}
+
+/// checks whether the SectionFrame is still able to grow, as case may be the environment has to be asked
+bool SwSectionFrame::Growable() const
+{
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.YDiff( lcl_DeadLine( this ),
+ aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
+ return true;
+
+ return ( GetUpper() && const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->Grow( LONG_MAX, true ) );
+}
+
+SwTwips SwSectionFrame::Grow_( SwTwips nDist, bool bTst )
+{
+ if ( !IsColLocked() && !HasFixSize() )
+ {
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
+ nDist = LONG_MAX - nFrameHeight;
+
+ if ( nDist <= 0 )
+ return 0;
+
+ bool bInCalcContent = GetUpper() && IsInFly() && FindFlyFrame()->IsLocked();
+ // OD 2004-03-15 #116561# - allow grow in online layout
+ bool bGrow = !Lower() || !Lower()->IsColumnFrame() || !Lower()->GetNext();
+ if (!bGrow)
+ {
+ SwSection* pSection = GetSection();
+ bGrow = pSection && pSection->GetFormat()->GetBalancedColumns().GetValue();
+ }
+ if( !bGrow )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ bGrow = pSh && pSh->GetViewOptions()->getBrowseMode();
+ }
+ if( bGrow )
+ {
+ SwTwips nGrow;
+ if( IsInFootnote() )
+ nGrow = 0;
+ else
+ {
+ nGrow = lcl_DeadLine( this );
+ nGrow = aRectFnSet.YDiff( nGrow, aRectFnSet.GetBottom(getFrameArea()) );
+ }
+ SwTwips nSpace = nGrow;
+ if( !bInCalcContent && nGrow < nDist && GetUpper() )
+ nGrow = o3tl::saturating_add(
+ nGrow, GetUpper()->Grow( LONG_MAX, true ));
+
+ if( nGrow > nDist )
+ nGrow = nDist;
+ if( nGrow <= 0 )
+ {
+ nGrow = 0;
+ if (!bTst)
+ {
+ if( bInCalcContent )
+ InvalidateSize_();
+ else
+ InvalidateSize();
+ }
+ }
+ else if( !bTst )
+ {
+ if( bInCalcContent )
+ InvalidateSize_();
+ else if( nSpace < nGrow && nDist != nSpace + GetUpper()->
+ Grow( nGrow - nSpace ) )
+ InvalidateSize();
+ else
+ {
+ const SvxGraphicPosition ePos =
+ GetAttrSet()->GetBackground().GetGraphicPos();
+ if ( GPOS_RT < ePos && GPOS_TILED != ePos )
+ {
+ SetCompletePaint();
+ InvalidatePage();
+ }
+ if( GetUpper() && GetUpper()->IsHeaderFrame() )
+ GetUpper()->InvalidateSize();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nGrow );
+ }
+
+ {
+ const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) + nGrow;
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight );
+ }
+
+ if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
+ {
+ SwFrame* pTmp = Lower();
+ do
+ {
+ pTmp->InvalidateSize_();
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ InvalidateSize_();
+ }
+ if( GetNext() )
+ {
+ // Own height changed, need to invalidate the position of
+ // next frames.
+ SwFrame *pFrame = GetNext();
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ {
+ // Invalidate all in-between frames, otherwise position
+ // calculation (which only looks back to one relative
+ // frame) will have an incorrect result.
+ InvalidateFramePos(pFrame, bInCalcContent);
+ pFrame = pFrame->GetNext();
+ }
+ if( pFrame )
+ {
+ InvalidateFramePos(pFrame, bInCalcContent);
+ }
+ }
+ // #i28701# - Due to the new object positioning
+ // the frame on the next page/column can flow backward (e.g. it
+ // was moved forward due to the positioning of its objects ).
+ // Thus, invalivate this next frame, if document compatibility
+ // option 'Consider wrapping style influence on object positioning' is ON.
+ else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ }
+ return nGrow;
+ }
+ if ( !bTst )
+ {
+ if( bInCalcContent )
+ InvalidateSize_();
+ else
+ InvalidateSize();
+ }
+ }
+ return 0;
+}
+
+SwTwips SwSectionFrame::Shrink_( SwTwips nDist, bool bTst )
+{
+ if ( Lower() && !IsColLocked() && !HasFixSize() )
+ {
+ if( ToMaximize( false ) )
+ {
+ if( !bTst )
+ InvalidateSize();
+ }
+ else
+ {
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nDist > nFrameHeight )
+ nDist = nFrameHeight;
+
+ if ( Lower()->IsColumnFrame() && Lower()->GetNext() && // FootnoteAtEnd
+ !GetSection()->GetFormat()->GetBalancedColumns().GetValue() )
+ { // With column bases the format takes over the control of the
+ // growth (because of the balance)
+ if ( !bTst )
+ InvalidateSize();
+ return nDist;
+ }
+ else if( !bTst )
+ {
+ const SvxGraphicPosition ePos =
+ GetAttrSet()->GetBackground().GetGraphicPos();
+ if ( GPOS_RT < ePos && GPOS_TILED != ePos )
+ {
+ SetCompletePaint();
+ InvalidatePage();
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, -nDist );
+ }
+
+ {
+ const tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nDist;
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight );
+ }
+
+ // We do not allow a section frame to shrink the its upper
+ // footer frame. This is because in the calculation of a
+ // footer frame, the content of the section frame is _not_
+ // calculated. If there is a fly frame overlapping with the
+ // footer frame, the section frame is not affected by this
+ // during the calculation of the footer frame size.
+ // The footer frame does not grow in its FormatSize function
+ // but during the calculation of the content of the section
+ // frame. The section frame grows until some of its text is
+ // located on top of the fly frame. The next call of CalcContent
+ // tries to shrink the section and here it would also shrink
+ // the footer. This may not happen, because shrinking the footer
+ // would cause the top of the section frame to overlap with the
+ // fly frame again, this would result in a perfect loop.
+ if( GetUpper() && !GetUpper()->IsFooterFrame() )
+ GetUpper()->Shrink( nDist, bTst );
+
+ if( Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
+ {
+ SwFrame* pTmp = Lower();
+ do
+ {
+ pTmp->InvalidateSize_();
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ }
+ if( GetNext() )
+ {
+ SwFrame* pFrame = GetNext();
+ while( pFrame && pFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pFrame)->GetSection() )
+ pFrame = pFrame->GetNext();
+ if( pFrame )
+ pFrame->InvalidatePos();
+ else
+ SetRetouche();
+ }
+ else
+ SetRetouche();
+ return nDist;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+|* When are Frames within a SectionFrames moveable?
+|* If they are not in the last column of a SectionFrames yet,
+|* if there is no Follow,
+|* if the SectionFrame cannot grow anymore, then it gets more complicated,
+|* in that case it depends on whether the SectionFrame can find a next
+|* layout sheet. In (column based/chained) Flys this is checked via
+|* GetNextLayout, in tables and headers/footers there is none, however in the
+|* DocBody and in foot notes there is always one.
+|*
+|* This routine is used in the TextFormatter to decided whether it's allowed to
+|* create a (paragraph-)Follow or whether the paragraph has to stick together
+|*/
+bool SwSectionFrame::MoveAllowed( const SwFrame* pFrame) const
+{
+ // Is there a Follow or is the Frame not in the last column?
+ if( HasFollow() || ( pFrame->GetUpper()->IsColBodyFrame() &&
+ pFrame->GetUpper()->GetUpper()->GetNext() ) )
+ return true;
+ if( pFrame->IsInFootnote() )
+ {
+ if( IsInFootnote() )
+ {
+ if( GetUpper()->IsInSct() )
+ {
+ if( Growable() )
+ return false;
+ return GetUpper()->FindSctFrame()->MoveAllowed( this );
+ }
+ else
+ return true;
+ }
+ // The content of footnote inside a columned sectionfrm is moveable
+ // except in the last column
+ const SwLayoutFrame *pLay = pFrame->FindFootnoteFrame()->GetUpper()->GetUpper();
+ if( pLay->IsColumnFrame() && pLay->GetNext() )
+ {
+ // The first paragraph in the first footnote in the first column
+ // in the sectionfrm at the top of the page is not moveable,
+ // if the columnbody is empty.
+ bool bRet = false;
+ if( pLay->GetIndPrev() || pFrame->GetIndPrev() ||
+ pFrame->FindFootnoteFrame()->GetPrev() )
+ bRet = true;
+ else
+ {
+ const SwLayoutFrame* pBody = static_cast<const SwColumnFrame*>(pLay)->FindBodyCont();
+ if( pBody && pBody->Lower() )
+ bRet = true;
+ }
+ if( bRet && ( IsFootnoteAtEnd() || !Growable() ) )
+ return true;
+ }
+ }
+ // Or can the section still grow?
+ if( !IsColLocked() && Growable() )
+ return false;
+ // Now it has to be examined whether there is a layout sheet wherein
+ // a section Follow can be created
+ if( !CanContainSplitSection(this) || ( !IsInDocBody() && FindFooterOrHeader() ) )
+ return false; // It doesn't work in table-in-sections/nested tables/headers/footers
+ if( IsInFly() ) // In column based or chained frames
+ return nullptr != const_cast<SwFrame*>(static_cast<SwFrame const *>(GetUpper()))->GetNextLeaf( MAKEPAGE_NONE );
+ return true;
+}
+
+/** Called for a frame inside a section with no direct previous frame (or only
+ previous empty section frames) the previous frame of the outer section is
+ returned, if the frame is the first flowing content of this section.
+
+ Note: For a frame inside a table frame, which is inside a section frame,
+ NULL is returned.
+*/
+SwFrame* SwFrame::GetIndPrev_() const
+{
+ SwFrame *pRet = nullptr;
+ // #i79774#
+ // Do not assert, if the frame has a direct previous frame, because it
+ // could be an empty section frame. The caller has to assure, that the
+ // frame has no direct previous frame or only empty section frames as
+ // previous frames.
+ OSL_ENSURE( /*!pPrev &&*/ IsInSct(), "Why?" );
+ const SwFrame* pSct = GetUpper();
+ if( !pSct )
+ return nullptr;
+ if( pSct->IsSctFrame() )
+ pRet = pSct->GetIndPrev();
+ else if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
+ {
+ // Do not return the previous frame of the outer section, if in one
+ // of the previous columns is content.
+ const SwFrame* pCol = GetUpper()->GetUpper()->GetPrev();
+ while( pCol )
+ {
+ assert(pCol->IsColumnFrame());
+ assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
+ if( static_cast<const SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pCol)->Lower())->Lower() )
+ return nullptr;
+ pCol = pCol->GetPrev();
+ }
+ pRet = pSct->GetIndPrev();
+ }
+
+ // skip empty section frames
+ while( pRet && pRet->IsSctFrame() && !static_cast<SwSectionFrame*>(pRet)->GetSection() )
+ pRet = pRet->GetIndPrev();
+ return pRet;
+}
+
+SwFrame* SwFrame::GetIndNext_()
+{
+ OSL_ENSURE( !mpNext && IsInSct(), "Why?" );
+ SwFrame* pSct = GetUpper();
+ if( !pSct )
+ return nullptr;
+ if( pSct->IsSctFrame() )
+ return pSct->GetIndNext();
+ if( pSct->IsColBodyFrame() && (pSct = pSct->GetUpper()->GetUpper())->IsSctFrame() )
+ { // We can only return the successor of the SectionFrames if there is no
+ // content in the successive columns
+ SwFrame* pCol = GetUpper()->GetUpper()->GetNext();
+ while( pCol )
+ {
+ assert(pCol->IsColumnFrame());
+ assert(pCol->GetLower() && pCol->GetLower()->IsBodyFrame());
+ if( static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pCol)->Lower())->Lower() )
+ return nullptr;
+ pCol = pCol->GetNext();
+ }
+ return pSct->GetIndNext();
+ }
+ return nullptr;
+}
+
+bool SwSectionFrame::IsDescendantFrom( const SwSectionFormat* pFormat ) const
+{
+ if( !m_pSection || !pFormat )
+ return false;
+ const SwSectionFormat *pMyFormat = m_pSection->GetFormat();
+ while( pFormat != pMyFormat )
+ {
+ if( auto pNewFormat = dynamic_cast< const SwSectionFormat *>( pMyFormat->GetRegisteredIn()) )
+ pMyFormat = pNewFormat;
+ else
+ return false;
+ }
+ return true;
+}
+
+void SwSectionFrame::CalcFootnoteAtEndFlag()
+{
+ SwSectionFormat *pFormat = GetSection()->GetFormat();
+ sal_uInt16 nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
+ m_bFootnoteAtEnd = FTNEND_ATPGORDOCEND != nVal;
+ m_bOwnFootnoteNum = FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
+ while( !m_bFootnoteAtEnd && !m_bOwnFootnoteNum )
+ {
+ if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
+ pFormat = pNewFormat;
+ else
+ break;
+ nVal = pFormat->GetFootnoteAtTextEnd( false ).GetValue();
+ if( FTNEND_ATPGORDOCEND != nVal )
+ {
+ m_bFootnoteAtEnd = true;
+ m_bOwnFootnoteNum = m_bOwnFootnoteNum ||FTNEND_ATTXTEND_OWNNUMSEQ == nVal ||
+ FTNEND_ATTXTEND_OWNNUMANDFMT == nVal;
+ }
+ }
+}
+
+bool SwSectionFrame::IsEndnoteAtMyEnd() const
+{
+ return m_pSection->GetFormat()->GetEndAtTextEnd( false ).IsAtEnd();
+}
+
+void SwSectionFrame::CalcEndAtEndFlag()
+{
+ SwSectionFormat *pFormat = GetSection()->GetFormat();
+ m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
+ while( !m_bEndnAtEnd )
+ {
+ if( auto pNewFormat = dynamic_cast<SwSectionFormat *>( pFormat->GetRegisteredIn()) )
+ pFormat = pNewFormat;
+ else
+ break;
+ m_bEndnAtEnd = pFormat->GetEndAtTextEnd( false ).IsAtEnd();
+ }
+}
+
+void SwSectionFrame::Notify(SfxHint const& rHint)
+{
+ SwSectionFormat *const pFormat(GetSection()->GetFormat());
+ assert(pFormat);
+ SwClientNotify(*pFormat, rHint);
+}
+
+void SwSectionFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwSectionFrameInvFlags eInvFlags = SwSectionFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while (pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if (eInvFlags != SwSectionFrameInvFlags::NONE)
+ {
+ if(eInvFlags & SwSectionFrameInvFlags::InvalidateSize)
+ InvalidateSize();
+ if(eInvFlags & SwSectionFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ }
+ }
+ else if(const auto pHint = dynamic_cast<const SwSectionFrameMoveAndDeleteHint*>(&rHint))
+ {
+ // #i117863#
+ if(&rMod != GetDep())
+ return;
+ SwSectionFrame::MoveContentAndDelete(this, pHint->IsSaveContent());
+ }
+ else
+ SwFrame::SwClientNotify(rMod, rHint);
+}
+
+void SwSectionFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwSectionFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ { // Suppress multi columns in foot notes
+ case RES_FMT_CHG:
+ {
+ const SwFormatCol& rNewCol = GetFormat()->GetCol();
+ if( !IsInFootnote() )
+ {
+ // Nasty case. When allocating a template we can not count
+ // on the old column attribute. We're left with creating a
+ // temporary attribute here.
+ SwFormatCol aCol;
+ if ( Lower() && Lower()->IsColumnFrame() )
+ {
+ sal_uInt16 nCol = 0;
+ SwFrame *pTmp = Lower();
+ do
+ { ++nCol;
+ pTmp = pTmp->GetNext();
+ } while ( pTmp );
+ aCol.Init( nCol, 0, 1000 );
+ }
+ bool bChgFootnote = IsFootnoteAtEnd();
+ bool const bChgEndn = IsEndnAtEnd();
+ bool const bChgMyEndn = IsEndnoteAtMyEnd();
+ CalcFootnoteAtEndFlag();
+ CalcEndAtEndFlag();
+ bChgFootnote = ( bChgFootnote != IsFootnoteAtEnd() ) ||
+ ( bChgEndn != IsEndnAtEnd() ) ||
+ ( bChgMyEndn != IsEndnoteAtMyEnd() );
+ ChgColumns( aCol, rNewCol, bChgFootnote );
+ rInvFlags |= SwSectionFrameInvFlags::SetCompletePaint;
+ }
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ bClear = false;
+ }
+ break;
+
+ case RES_COL:
+ if( !IsInFootnote() )
+ {
+ assert(pOld && pNew);
+ if (pOld && pNew)
+ {
+ ChgColumns( *static_cast<const SwFormatCol*>(pOld), *static_cast<const SwFormatCol*>(pNew) );
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize | SwSectionFrameInvFlags::SetCompletePaint;
+ }
+ }
+ break;
+
+ case RES_FTN_AT_TXTEND:
+ if( !IsInFootnote() )
+ {
+ bool const bOld = IsFootnoteAtEnd();
+ CalcFootnoteAtEndFlag();
+ if (bOld != IsFootnoteAtEnd())
+ {
+ const SwFormatCol& rNewCol = GetFormat()->GetCol();
+ ChgColumns( rNewCol, rNewCol, true );
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ }
+ }
+ break;
+
+ case RES_END_AT_TXTEND:
+ if( !IsInFootnote() )
+ {
+ bool const bOld = IsEndnAtEnd();
+ bool const bMyOld = IsEndnoteAtMyEnd();
+ CalcEndAtEndFlag();
+ if (bOld != IsEndnAtEnd() || bMyOld != IsEndnoteAtMyEnd())
+ {
+ const SwFormatCol& rNewCol = GetFormat()->GetCol();
+ ChgColumns( rNewCol, rNewCol, true );
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ }
+ }
+ break;
+ case RES_COLUMNBALANCE:
+ rInvFlags |= SwSectionFrameInvFlags::InvalidateSize;
+ break;
+
+ case RES_FRAMEDIR :
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+
+ case RES_PROTECT:
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetLayout()->IsAnyShellAccessible() )
+ pSh->Imp()->InvalidateAccessibleEditableState( true, this );
+ }
+#endif
+ break;
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+/// A follow or a ftncontainer at the end of the page causes a maximal Size of the sectionframe.
+bool SwSectionFrame::ToMaximize( bool bCheckFollow ) const
+{
+ if( HasFollow() )
+ {
+ if( !bCheckFollow ) // Don't check superfluous follows
+ return true;
+ const SwSectionFrame* pFoll = GetFollow();
+ while( pFoll && pFoll->IsSuperfluous() )
+ pFoll = pFoll->GetFollow();
+ if( pFoll )
+ return true;
+ }
+ if( IsFootnoteAtEnd() )
+ return false;
+ const SwFootnoteContFrame* pCont = ContainsFootnoteCont();
+ if( !IsEndnAtEnd() )
+ return nullptr != pCont;
+ bool bRet = false;
+ while( pCont && !bRet )
+ {
+ if( pCont->FindFootNote() )
+ bRet = true;
+ else
+ pCont = ContainsFootnoteCont( pCont );
+ }
+ return bRet;
+}
+
+/// Check every Column for FootnoteContFrames.
+SwFootnoteContFrame* SwSectionFrame::ContainsFootnoteCont( const SwFootnoteContFrame* pCont ) const
+{
+ SwFootnoteContFrame* pRet = nullptr;
+ const SwLayoutFrame* pLay;
+ if( pCont )
+ {
+ pLay = pCont->FindFootnoteBossFrame();
+ OSL_ENSURE( IsAnLower( pLay ), "ContainsFootnoteCont: Wrong FootnoteContainer" );
+ pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
+ }
+ else if( Lower() && Lower()->IsColumnFrame() )
+ pLay = static_cast<const SwLayoutFrame*>(Lower());
+ else
+ pLay = nullptr;
+ while ( !pRet && pLay )
+ {
+ if( pLay->Lower() && pLay->Lower()->GetNext() )
+ {
+ OSL_ENSURE( pLay->Lower()->GetNext()->IsFootnoteContFrame(),
+ "ToMaximize: Unexpected Frame" );
+ pRet = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pLay->Lower()->GetNext()));
+ }
+ OSL_ENSURE( !pLay->GetNext() || pLay->GetNext()->IsLayoutFrame(),
+ "ToMaximize: ColFrame expected" );
+ pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
+ }
+ return pRet;
+}
+
+void SwSectionFrame::InvalidateFootnotePos()
+{
+ SwFootnoteContFrame* pCont = ContainsFootnoteCont();
+ if( pCont )
+ {
+ SwFrame *pTmp = pCont->ContainsContent();
+ if( pTmp )
+ pTmp->InvalidatePos_();
+ }
+}
+
+SwTwips SwSectionFrame::CalcUndersize() const
+{
+ SwRectFnSet aRectFnSet(this);
+ return InnerHeight() - aRectFnSet.GetHeight(getFramePrintArea());
+}
+
+SwTwips SwSectionFrame::Undersize()
+{
+ const auto nRet = CalcUndersize();
+ m_bUndersized = (nRet > 0);
+ return nRet <= 0 ? 0 : nRet;
+}
+
+void SwSectionFrame::CalcFootnoteContent()
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ SwFootnoteContFrame* pCont = ContainsFootnoteCont();
+ if( !pCont )
+ return;
+
+ SwFrame* pFrame = pCont->ContainsAny();
+ if( pFrame )
+ pCont->Calc(pRenderContext);
+ while( pFrame && IsAnLower( pFrame ) )
+ {
+ SwFootnoteFrame* pFootnote = pFrame->FindFootnoteFrame();
+ if( pFootnote )
+ pFootnote->Calc(pRenderContext);
+ pFrame->Calc(pRenderContext);
+ if( pFrame->IsSctFrame() )
+ {
+ SwFrame *pTmp = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pTmp )
+ {
+ pFrame = pTmp;
+ continue;
+ }
+ }
+ pFrame = pFrame->FindNext();
+ }
+}
+
+/*
+ * If a SectionFrame gets empty, e.g. because its content changes the page/column,
+ * it is not destroyed immediately (there could be a pointer left to it on the
+ * stack), instead it puts itself in a list at the RootFrame, which is processed
+ * later on (in Layaction::Action among others). Its size is set to Null and
+ * the pointer to its page as well. Such SectionFrames that are to be deleted
+ * must be ignored by the layout/during formatting.
+ *
+ * With InsertEmptySct the RootFrame stores a SectionFrame in the list,
+ * with RemoveFromList it can be removed from the list (Dtor),
+ * with DeleteEmptySct the list is processed and the SectionFrames are destroyed.
+ */
+void SwRootFrame::InsertEmptySct( SwSectionFrame* pDel )
+{
+ if( !mpDestroy )
+ mpDestroy.reset( new SwDestroyList );
+ mpDestroy->insert( pDel );
+}
+
+void SwRootFrame::DeleteEmptySct_()
+{
+ assert(mpDestroy);
+ while( !mpDestroy->empty() )
+ {
+ SwSectionFrame* pSect = *mpDestroy->begin();
+ mpDestroy->erase( mpDestroy->begin() );
+ OSL_ENSURE( !pSect->IsColLocked() && !pSect->IsJoinLocked(),
+ "DeleteEmptySct: Locked SectionFrame" );
+ SAL_WARN_IF(pSect->IsDeleteForbidden(), "sw.layout", "not allowed delete SwFrame");
+ if( !pSect->getFrameArea().HasArea() && !pSect->ContainsContent() && !pSect->IsDeleteForbidden() )
+ {
+ SwLayoutFrame* pUp = pSect->GetUpper();
+ pSect->RemoveFromLayout();
+ SwFrame::DestroyFrame(pSect);
+ if( pUp && !pUp->Lower() )
+ {
+ if( pUp->IsPageBodyFrame() )
+ pUp->getRootFrame()->SetSuperfluous();
+ else if( pUp->IsFootnoteFrame() && !pUp->IsColLocked() &&
+ pUp->GetUpper() )
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ }
+ }
+ }
+ else {
+ OSL_ENSURE( pSect->GetSection(), "DeleteEmptySct: Half-dead SectionFrame?!" );
+ }
+ }
+}
+
+void SwRootFrame::RemoveFromList_( SwSectionFrame* pSct )
+{
+ assert(mpDestroy && "Where's my list?");
+ mpDestroy->erase( pSct );
+}
+
+#ifdef DBG_UTIL
+bool SwRootFrame::IsInDelList( SwSectionFrame* pSct ) const
+{
+ return mpDestroy && mpDestroy->find( pSct ) != mpDestroy->end();
+}
+#endif
+
+bool SwSectionFrame::IsBalancedSection() const
+{
+ bool bRet = false;
+ if ( GetSection() && Lower() && Lower()->IsColumnFrame() && Lower()->GetNext() )
+ {
+ bRet = !GetSection()->GetFormat()->GetBalancedColumns().GetValue();
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/softpagebreak.cxx b/sw/source/core/layout/softpagebreak.cxx
new file mode 100644
index 000000000..87ba7c24e
--- /dev/null
+++ b/sw/source/core/layout/softpagebreak.cxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <swtable.hxx>
+#include <frmfmt.hxx>
+#include <rowfrm.hxx>
+#include <tabfrm.hxx>
+#include <calbck.hxx>
+#include <ndtxt.hxx>
+
+void SwTextNode::fillSoftPageBreakList( SwSoftPageBreakList& rBreak ) const
+{
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for( const SwTextFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ // No soft page break in header or footer
+ if( pFrame->FindFooterOrHeader() || pFrame->IsInFly() )
+ return;
+ // No soft page break if I'm not the first frame in my layout frame
+ if( pFrame->GetIndPrev() )
+ continue;
+ const SwPageFrame* pPage = pFrame->FindPageFrame();
+ // No soft page break at the first page
+ if( pPage && pPage->GetPrev() )
+ {
+ const SwContentFrame* pFirst2 = pPage->FindFirstBodyContent();
+ // Special handling for content frame in table frames
+ if( pFrame->IsInTab() )
+ {
+ // No soft page break if I'm in a table but the first content frame
+ // at my page is not in a table
+ if( !pFirst2 || !pFirst2->IsInTab() )
+ continue;
+ const SwLayoutFrame *pRow = pFrame->GetUpper();
+ // Looking for the "most upper" row frame,
+ // skipping sub tables and/or table in table
+ while( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() ||
+ pRow->GetUpper()->GetUpper()->IsInTab() )
+ pRow = pRow->GetUpper();
+ const SwTabFrame *pTab = pRow->FindTabFrame();
+ // For master tables the soft page break will exported at the table row,
+ // not at the content frame.
+ // If the first content is outside my table frame, no soft page break.
+ if( !pTab->IsFollow() || !pTab->IsAnLower( pFirst2 ) )
+ continue;
+ // Only content of non-heading-rows can get a soft page break
+ const SwFrame* pFirstRow = pTab->GetFirstNonHeadlineRow();
+ // If there's no follow flow line, the soft page break will be
+ // exported at the row, not at the content.
+ if( pRow == pFirstRow &&
+ pTab->FindMaster()->HasFollowFlowLine() )
+ {
+ // Now we have the row which causes a new page,
+ // this row is a follow flow line and therefore cannot get
+ // the soft page break itself.
+ // Every first content frame of every cell frame in this row
+ // will get the soft page break
+ const SwFrame* pCell = pRow->Lower();
+ while( pCell )
+ {
+ pFirst2 = static_cast<const SwLayoutFrame*>(pCell)->ContainsContent();
+ if( pFirst2 == pFrame )
+ { // Here we are: a first content inside a cell
+ // inside the split row => soft page break
+ auto const pos(pFrame->MapViewToModel(pFrame->GetOffset()));
+ if (pos.first == this)
+ {
+ rBreak.insert(pos.second);
+ }
+ break;
+ }
+ pCell = pCell->GetNext();
+ }
+ }
+ }
+ else // No soft page break if there's a "hard" page break attribute
+ if( pFirst2 == pFrame && !pFrame->IsPageBreak( true ) )
+ {
+ auto const pos(pFrame->MapViewToModel(pFrame->GetOffset()));
+ if (pos.first == this)
+ { // in the !Show case, we have to iterate over the merged
+ // SwTextFrame for every node
+ rBreak.insert(pos.second);
+ }
+ }
+ }
+ }
+}
+
+bool SwTableLine::hasSoftPageBreak() const
+{
+ // No soft page break for sub tables
+ if( GetUpper() || !GetFrameFormat() )
+ return false;
+ SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() );
+ for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->GetTabLine() == this )
+ {
+ const SwTabFrame* pTab = pLast->FindTabFrame();
+ // No soft page break for
+ // tables with prevs, i.e. if the frame is not the first in its layout frame
+ // tables in footer or header
+ // tables in flies
+ // inner tables of nested tables
+ // master table frames with "hard" page break attribute
+ if( pTab->GetIndPrev() || pTab->FindFooterOrHeader()
+ || pTab->IsInFly() || pTab->GetUpper()->IsInTab() ||
+ ( !pTab->IsFollow() && pTab->IsPageBreak( true ) ) )
+ return false;
+ const SwPageFrame* pPage = pTab->FindPageFrame();
+ // No soft page break at the first page of the document
+ if( pPage && !pPage->GetPrev() )
+ return false;
+ const SwContentFrame* pFirst = pPage ? pPage->FindFirstBodyContent() : nullptr;
+ // No soft page break for
+ // tables which does not contain the first body content of the page
+ if( !pFirst || !pTab->IsAnLower( pFirst->FindTabFrame() ) )
+ return false;
+ // The row which could get a soft page break must be either the first
+ // row of a master table frame or the first "non-headline-row" of a
+ // follow table frame...
+ const SwFrame* pRow = pTab->IsFollow() ?
+ pTab->GetFirstNonHeadlineRow() : pTab->Lower();
+ if( pRow == pLast )
+ {
+ // The last check: no soft page break for "follow" table lines
+ return !pTab->IsFollow() || !pTab->FindMaster()->HasFollowFlowLine();
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/sortedobjs.cxx b/sw/source/core/layout/sortedobjs.cxx
new file mode 100644
index 000000000..ad3759466
--- /dev/null
+++ b/sw/source/core/layout/sortedobjs.cxx
@@ -0,0 +1,293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sortedobjs.hxx>
+
+#include <algorithm>
+#include <anchoredobject.hxx>
+#include <fmtanchr.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <frmfmt.hxx>
+#include <pam.hxx>
+#include <svx/svdobj.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+SwSortedObjs::SwSortedObjs()
+{
+}
+
+SwSortedObjs::~SwSortedObjs()
+{
+}
+
+size_t SwSortedObjs::size() const
+{
+ return maSortedObjLst.size();
+}
+
+SwAnchoredObject* SwSortedObjs::operator[]( size_t _nIndex ) const
+{
+ SwAnchoredObject* pAnchoredObj = nullptr;
+
+ if ( _nIndex >= size() )
+ {
+ OSL_FAIL( "<SwSortedObjs::operator[]> - index out of range" );
+ }
+ else
+ {
+ pAnchoredObj = maSortedObjLst[ _nIndex ];
+ }
+
+ return pAnchoredObj;
+}
+
+namespace
+{
+ int GetAnchorWeight(RndStdIds eAnchor)
+ {
+ if (eAnchor == RndStdIds::FLY_AT_CHAR)
+ return 0;
+ if (eAnchor == RndStdIds::FLY_AS_CHAR)
+ return 1;
+ return 2;
+ }
+
+struct ObjAnchorOrder
+{
+ bool operator()( const SwAnchoredObject* _pListedAnchoredObj,
+ const SwAnchoredObject* _pNewAnchoredObj )
+ {
+ // get attributes of listed object
+ const SwFrameFormat& rFormatListed = _pListedAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor* pAnchorListed = &(rFormatListed.GetAnchor());
+
+ // get attributes of new object
+ const SwFrameFormat& rFormatNew = _pNewAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor* pAnchorNew = &(rFormatNew.GetAnchor());
+
+ // check for to-page anchored objects
+ if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
+ (pAnchorNew ->GetAnchorId() != RndStdIds::FLY_AT_PAGE))
+ {
+ return true;
+ }
+ else if ((pAnchorListed->GetAnchorId() != RndStdIds::FLY_AT_PAGE) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_PAGE))
+ {
+ return false;
+ }
+ else if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_PAGE) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_PAGE))
+ {
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+
+ // Both objects aren't anchored to page.
+ // Thus, check for to-fly anchored objects
+ if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_FLY) &&
+ (pAnchorNew ->GetAnchorId() != RndStdIds::FLY_AT_FLY))
+ {
+ return true;
+ }
+ else if ((pAnchorListed->GetAnchorId() != RndStdIds::FLY_AT_FLY) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_FLY))
+ {
+ return false;
+ }
+ else if ((pAnchorListed->GetAnchorId() == RndStdIds::FLY_AT_FLY) &&
+ (pAnchorNew ->GetAnchorId() == RndStdIds::FLY_AT_FLY))
+ {
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+
+ // Both objects aren't anchor to page or to fly
+ // Thus, compare content anchor nodes, if existing.
+ const SwPosition* pContentAnchorListed = pAnchorListed->GetContentAnchor();
+ const SwPosition* pContentAnchorNew = pAnchorNew->GetContentAnchor();
+ if ( pContentAnchorListed && pContentAnchorNew &&
+ pContentAnchorListed->nNode != pContentAnchorNew->nNode )
+ {
+ return pContentAnchorListed->nNode < pContentAnchorNew->nNode;
+ }
+
+ // objects anchored at the same content.
+ // --> OD 2006-11-29 #???# - objects have to be ordered by anchor node position
+ // Thus, compare content anchor node positions and anchor type,
+ // if not anchored at-paragraph
+ if (pContentAnchorListed && pContentAnchorNew)
+ {
+ sal_Int32 nListedIndex = pAnchorListed->GetAnchorId() != RndStdIds::FLY_AT_PARA ?
+ pContentAnchorListed->nContent.GetIndex() : 0;
+ sal_Int32 nNewIndex = pAnchorNew->GetAnchorId() != RndStdIds::FLY_AT_PARA ?
+ pContentAnchorNew->nContent.GetIndex() : 0;
+ if (nListedIndex != nNewIndex)
+ {
+ return nListedIndex < nNewIndex;
+ }
+ }
+
+ int nAnchorListedWeight = GetAnchorWeight(pAnchorListed->GetAnchorId());
+ int nAnchorNewWeight = GetAnchorWeight(pAnchorNew->GetAnchorId());
+ if (nAnchorListedWeight != nAnchorNewWeight)
+ {
+ return nAnchorListedWeight < nAnchorNewWeight;
+ }
+
+ // objects anchored at the same content and at the same content anchor
+ // node position with the same anchor type
+ // Thus, compare its wrapping style including its layer
+ const IDocumentDrawModelAccess& rIDDMA = rFormatListed.getIDocumentDrawModelAccess();
+ const SdrLayerID nHellId = rIDDMA.GetHellId();
+ const SdrLayerID nInvisibleHellId = rIDDMA.GetInvisibleHellId();
+ const bool bWrapThroughOrHellListed =
+ rFormatListed.GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH ||
+ _pListedAnchoredObj->GetDrawObj()->GetLayer() == nHellId ||
+ _pListedAnchoredObj->GetDrawObj()->GetLayer() == nInvisibleHellId;
+ const bool bWrapThroughOrHellNew =
+ rFormatNew.GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH ||
+ _pNewAnchoredObj->GetDrawObj()->GetLayer() == nHellId ||
+ _pNewAnchoredObj->GetDrawObj()->GetLayer() == nInvisibleHellId;
+ if ( bWrapThroughOrHellListed != bWrapThroughOrHellNew )
+ {
+ return !bWrapThroughOrHellListed;
+ }
+ else if ( bWrapThroughOrHellListed && bWrapThroughOrHellNew )
+ {
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+
+ // objects anchored at the same content with a set text wrapping
+ // Thus, compare wrap influences on object position
+ const SwFormatWrapInfluenceOnObjPos* pWrapInfluenceOnObjPosListed =
+ &(rFormatListed.GetWrapInfluenceOnObjPos());
+ const SwFormatWrapInfluenceOnObjPos* pWrapInfluenceOnObjPosNew =
+ &(rFormatNew.GetWrapInfluenceOnObjPos());
+ // #i35017# - handle ITERATIVE as ONCE_SUCCESSIVE
+ if ( pWrapInfluenceOnObjPosListed->GetWrapInfluenceOnObjPos( true ) !=
+ pWrapInfluenceOnObjPosNew->GetWrapInfluenceOnObjPos( true ) )
+ {
+ // #i35017# - constant name has changed
+ return pWrapInfluenceOnObjPosListed->GetWrapInfluenceOnObjPos( true )
+ == text::WrapInfluenceOnPosition::ONCE_SUCCESSIVE;
+ }
+
+ // objects anchored at the same content position/page/fly with same
+ // wrap influence.
+ // Thus, compare anchor order number
+ return pAnchorListed->GetOrder() < pAnchorNew->GetOrder();
+ }
+};
+
+}
+
+bool SwSortedObjs::is_sorted() const
+{
+ return std::is_sorted(maSortedObjLst.begin(), maSortedObjLst.end(), ObjAnchorOrder());
+}
+
+bool SwSortedObjs::Insert( SwAnchoredObject& _rAnchoredObj )
+{
+ // #i51941#
+ if ( Contains( _rAnchoredObj ) )
+ {
+ // list already contains object
+ OSL_FAIL( "<SwSortedObjs::Insert()> - already contains object" );
+ return true;
+ }
+
+ // find insert position
+ std::vector< SwAnchoredObject* >::iterator aInsPosIter =
+ std::lower_bound( maSortedObjLst.begin(), maSortedObjLst.end(),
+ &_rAnchoredObj, ObjAnchorOrder() );
+
+ // insert object into list
+ maSortedObjLst.insert( aInsPosIter, &_rAnchoredObj );
+
+ return Contains( _rAnchoredObj );
+}
+
+void SwSortedObjs::Remove( SwAnchoredObject& _rAnchoredObj )
+{
+ std::vector< SwAnchoredObject* >::iterator aDelPosIter =
+ std::find( maSortedObjLst.begin(), maSortedObjLst.end(), &_rAnchoredObj );
+
+ if ( aDelPosIter == maSortedObjLst.end() )
+ {
+ // object not found.
+ OSL_FAIL( "<SwSortedObjs::Remove()> - object not found" );
+ }
+ else
+ {
+ maSortedObjLst.erase( aDelPosIter );
+ }
+}
+
+bool SwSortedObjs::Contains( const SwAnchoredObject& _rAnchoredObj ) const
+{
+ std::vector< SwAnchoredObject* >::const_iterator aIter =
+ std::find( maSortedObjLst.begin(), maSortedObjLst.end(), &_rAnchoredObj );
+
+ return aIter != maSortedObjLst.end();
+}
+
+void SwSortedObjs::Update( SwAnchoredObject& _rAnchoredObj )
+{
+ if ( !Contains( _rAnchoredObj ) )
+ {
+ // given anchored object not found in list
+ OSL_FAIL( "<SwSortedObjs::Update(..) - sorted list doesn't contain given anchored object" );
+ return;
+ }
+
+ if ( size() == 1 )
+ {
+ // given anchored object is the only one in the list.
+ return;
+ }
+
+ Remove( _rAnchoredObj );
+ Insert( _rAnchoredObj );
+}
+
+void SwSortedObjs::UpdateAll()
+{
+ std::stable_sort(maSortedObjLst.begin(), maSortedObjLst.end(), ObjAnchorOrder());
+}
+
+size_t SwSortedObjs::ListPosOf( const SwAnchoredObject& _rAnchoredObj ) const
+{
+ std::vector< SwAnchoredObject* >::const_iterator aIter =
+ std::find( maSortedObjLst.begin(), maSortedObjLst.end(), &_rAnchoredObj );
+
+ if ( aIter != maSortedObjLst.end() )
+ {
+ // #i51941#
+ std::vector< SwAnchoredObject* >::difference_type nPos =
+ aIter - maSortedObjLst.begin();
+ return static_cast<size_t>( nPos );
+ }
+
+ return size();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/ssfrm.cxx b/sw/source/core/layout/ssfrm.cxx
new file mode 100644
index 000000000..c7d11b3ca
--- /dev/null
+++ b/sw/source/core/layout/ssfrm.cxx
@@ -0,0 +1,752 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <dcontact.hxx>
+#include <flyfrm.hxx>
+#include <txtfrm.hxx>
+#include <cellfrm.hxx>
+#include <swtable.hxx>
+#include <fmtfsize.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <fmtclds.hxx>
+#include <viewimp.hxx>
+#include <sortedobjs.hxx>
+#include <hints.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <osl/diagnose.h>
+
+ // No inline cause we need the function pointers
+tools::Long SwFrame::GetTopMargin() const
+ { return getFramePrintArea().Top(); }
+tools::Long SwFrame::GetBottomMargin() const
+ { return getFrameArea().Height() -getFramePrintArea().Height() -getFramePrintArea().Top(); }
+tools::Long SwFrame::GetLeftMargin() const
+ { return getFramePrintArea().Left(); }
+tools::Long SwFrame::GetRightMargin() const
+ { return getFrameArea().Width() - getFramePrintArea().Width() - getFramePrintArea().Left(); }
+tools::Long SwFrame::GetPrtLeft() const
+ { return getFrameArea().Left() + getFramePrintArea().Left(); }
+tools::Long SwFrame::GetPrtBottom() const
+ { return getFrameArea().Top() + getFramePrintArea().Height() + getFramePrintArea().Top(); }
+tools::Long SwFrame::GetPrtRight() const
+ { return getFrameArea().Left() + getFramePrintArea().Width() + getFramePrintArea().Left(); }
+tools::Long SwFrame::GetPrtTop() const
+ { return getFrameArea().Top() + getFramePrintArea().Top(); }
+
+bool SwFrame::SetMinLeft( tools::Long nDeadline )
+{
+ SwTwips nDiff = nDeadline - getFrameArea().Left();
+ if( nDiff > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Left( nDeadline );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( aPrt.Width() - nDiff );
+
+ return true;
+ }
+ return false;
+}
+
+bool SwFrame::SetMaxBottom( tools::Long nDeadline )
+{
+ SwTwips nDiff = getFrameArea().Top() + getFrameArea().Height() - nDeadline;
+ if( nDiff > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( aFrm.Height() - nDiff );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( aPrt.Height() - nDiff );
+
+ return true;
+ }
+ return false;
+}
+
+bool SwFrame::SetMaxRight( tools::Long nDeadline )
+{
+ SwTwips nDiff = getFrameArea().Left() + getFrameArea().Width() - nDeadline;
+ if( nDiff > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( aFrm.Width() - nDiff );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( aPrt.Width() - nDiff );
+
+ return true;
+ }
+ return false;
+}
+
+void SwFrame::MakeBelowPos( const SwFrame* pUp, const SwFrame* pPrv, bool bNotify )
+{
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( pPrv )
+ {
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+ aFrm.Pos().AdjustY(pPrv->getFrameArea().Height() );
+ }
+ else
+ {
+ aFrm.Pos( pUp->getFrameArea().Pos() );
+ aFrm.Pos() += pUp->getFramePrintArea().Pos();
+ }
+
+ if( bNotify )
+ {
+ aFrm.Pos().AdjustY(1 );
+ }
+}
+
+void SwFrame::MakeLeftPos( const SwFrame* pUp, const SwFrame* pPrv, bool bNotify )
+{
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( pPrv )
+ {
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+ aFrm.Pos().AdjustX( -(aFrm.Width()) );
+ }
+ else
+ {
+ aFrm.Pos( pUp->getFrameArea().Pos() );
+ aFrm.Pos() += pUp->getFramePrintArea().Pos();
+ aFrm.Pos().AdjustX(pUp->getFramePrintArea().Width() - aFrm.Width() );
+ }
+
+ if( bNotify )
+ {
+ aFrm.Pos().AdjustX( -1 );
+ }
+}
+
+void SwFrame::MakeRightPos( const SwFrame* pUp, const SwFrame* pPrv, bool bNotify )
+{
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( pPrv )
+ {
+ aFrm.Pos( pPrv->getFrameArea().Pos() );
+ aFrm.Pos().AdjustX(pPrv->getFrameArea().Width() );
+ }
+ else
+ {
+ aFrm.Pos( pUp->getFrameArea().Pos() );
+ aFrm.Pos() += pUp->getFramePrintArea().Pos();
+ }
+
+ if( bNotify )
+ {
+ aFrm.Pos().AdjustX(1 );
+ }
+}
+
+void SwFrame::SetTopBottomMargins( tools::Long nTop, tools::Long nBot )
+{
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Top( nTop );
+ aPrt.Height( getFrameArea().Height() - nTop - nBot );
+}
+
+void SwFrame::SetLeftRightMargins( tools::Long nLeft, tools::Long nRight)
+{
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( nLeft );
+ aPrt.Width( getFrameArea().Width() - nLeft - nRight );
+}
+
+void SwFrame::SetRightLeftMargins( tools::Long nRight, tools::Long nLeft)
+{
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( nLeft );
+ aPrt.Width( getFrameArea().Width() - nLeft - nRight );
+}
+
+/// checks the layout direction and invalidates the lower frames recursively, if necessary.
+void SwFrame::CheckDirChange()
+{
+ bool bOldVert = mbVertical;
+ bool bOldR2L = mbRightToLeft;
+ SetInvalidVert( true );
+ mbInvalidR2L = true;
+ bool bChg = bOldR2L != IsRightToLeft();
+ bool bOldVertL2R = IsVertLR();
+ if( !(( IsVertical() != bOldVert ) || bChg || bOldVertL2R != IsVertLR()) )
+ return;
+
+ InvalidateAll();
+ if( IsLayoutFrame() )
+ {
+ // set minimum row height for vertical cells in horizontal table:
+ if ( IsCellFrame() && GetUpper() )
+ {
+ if ( IsVertical() != GetUpper()->IsVertical() &&
+ static_cast<SwCellFrame*>(this)->GetTabBox()->getRowSpan() == 1 )
+ {
+ enum {
+ MIN_VERT_CELL_HEIGHT = 1135
+ };
+
+ SwTableLine* pLine = const_cast<SwTableLine*>(static_cast<SwCellFrame*>(this)->GetTabBox()->GetUpper());
+ SwFrameFormat* pFrameFormat = pLine->GetFrameFormat();
+ SwFormatFrameSize aNew( pFrameFormat->GetFrameSize() );
+ if ( SwFrameSize::Fixed != aNew.GetHeightSizeType() )
+ aNew.SetHeightSizeType( SwFrameSize::Minimum );
+ if ( aNew.GetHeight() < MIN_VERT_CELL_HEIGHT )
+ aNew.SetHeight( MIN_VERT_CELL_HEIGHT );
+ SwDoc* pDoc = pFrameFormat->GetDoc();
+ pDoc->SetAttr( aNew, *pLine->ClaimFrameFormat() );
+ }
+ }
+
+ SwFrame* pFrame = static_cast<SwLayoutFrame*>(this)->Lower();
+ const SwFormatCol* pCol = nullptr;
+ SwLayoutFrame* pBody = nullptr;
+ if( pFrame )
+ {
+ if( IsPageFrame() )
+ {
+ // If we're a page frame and we change our layout direction,
+ // we have to look for columns and rearrange them.
+ pBody = static_cast<SwPageFrame*>(this)->FindBodyCont();
+ if(pBody && pBody->Lower() && pBody->Lower()->IsColumnFrame())
+ pCol = &static_cast<SwPageFrame*>(this)->GetFormat()->GetCol();
+ }
+ else if( pFrame->IsColumnFrame() )
+ {
+ pBody = static_cast<SwLayoutFrame*>(this);
+ const SwFrameFormat *pFormat = pBody->GetFormat();
+ if( pFormat )
+ pCol = &pFormat->GetCol();
+ }
+ }
+ while( pFrame )
+ {
+ pFrame->CheckDirChange();
+ pFrame = pFrame->GetNext();
+ }
+ if( pCol )
+ pBody->AdjustColumns( pCol, true );
+ }
+ else if( IsTextFrame() )
+ static_cast<SwTextFrame*>(this)->Prepare();
+
+ // #i31698# - notify anchored objects also for page frames.
+ // Remove code above for special handling of page frames
+ if ( !GetDrawObjs() )
+ return;
+
+ const SwSortedObjs *pObjs = GetDrawObjs();
+ const size_t nCnt = pObjs->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ pFlyFrame->CheckDirChange();
+ else
+ {
+ // OD 2004-04-06 #i26791# - direct object
+ // positioning no longer needed. Instead
+ // invalidate
+ pAnchoredObj->InvalidateObjPos();
+ }
+ // #i31698# - update layout direction of
+ // anchored object
+ {
+ ::setContextWritingMode( pAnchoredObj->DrawObj(), pAnchoredObj->GetAnchorFrameContainingAnchPos() );
+ pAnchoredObj->UpdateLayoutDir();
+ }
+ }
+}
+
+/// returns the position for anchors based on frame direction
+// OD 2004-03-10 #i11860# - consider lower space and line spacing of
+// previous frame according to new option 'Use former object positioning'
+Point SwFrame::GetFrameAnchorPos( bool bIgnoreFlysAnchoredAtThisFrame ) const
+{
+ Point aAnchor = getFrameArea().Pos();
+
+ if ( ( IsVertical() && !IsVertLR() ) || IsRightToLeft() )
+ aAnchor.AdjustX(getFrameArea().Width() );
+
+ if ( IsTextFrame() )
+ {
+ SwTwips nBaseOfstForFly =
+ static_cast<const SwTextFrame*>(this)->GetBaseOffsetForFly( bIgnoreFlysAnchoredAtThisFrame );
+ if ( IsVertical() )
+ aAnchor.AdjustY(nBaseOfstForFly );
+ else
+ aAnchor.AdjustX(nBaseOfstForFly );
+
+ // OD 2004-03-10 #i11860# - if option 'Use former object positioning'
+ // is OFF, consider the lower space and the line spacing of the
+ // previous frame and the spacing considered for the page grid
+ const SwTextFrame* pThisTextFrame = static_cast<const SwTextFrame*>(this);
+ const SwTwips nUpperSpaceAmountConsideredForPrevFrameAndPageGrid =
+ pThisTextFrame->GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ if ( IsVertical() )
+ {
+ aAnchor.AdjustX( -nUpperSpaceAmountConsideredForPrevFrameAndPageGrid );
+ }
+ else
+ {
+ aAnchor.AdjustY(nUpperSpaceAmountConsideredForPrevFrameAndPageGrid );
+ }
+ }
+
+ return aAnchor;
+}
+
+void SwFrame::DestroyImpl()
+{
+ mbInDtor = true;
+
+ // accessible objects for fly and cell frames have been already disposed
+ // by the destructors of the derived classes.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (IsAccessibleFrame() && !(IsFlyFrame() || IsCellFrame())
+ && (GetDep() || IsTextFrame())) // sw_redlinehide: text frame may not have Dep!
+ {
+ assert(!IsTextFrame() || GetDep() || static_cast<SwTextFrame*>(this)->GetMergedPara());
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell *pVSh = pRootFrame->GetCurrShell();
+ if( pVSh && pVSh->Imp() )
+ {
+ OSL_ENSURE( !GetLower(), "Lowers should be dispose already!" );
+ pVSh->Imp()->DisposeAccessibleFrame( this );
+ }
+ }
+ }
+#endif
+
+ if (!m_pDrawObjs)
+ return;
+
+ for (size_t i = m_pDrawObjs->size(); i; )
+ {
+ SwAnchoredObject* pAnchoredObj = (*m_pDrawObjs)[--i];
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ }
+ else
+ {
+ SdrObject* pSdrObj = pAnchoredObj->DrawObj();
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(pSdrObj->GetUserCall());
+ OSL_ENSURE( pContact,
+ "<SwFrame::~SwFrame> - missing contact for drawing object" );
+ if ( pContact )
+ {
+ pContact->DisconnectObjFromLayout( pSdrObj );
+ }
+ }
+ }
+ m_pDrawObjs.reset();
+}
+
+SwFrame::~SwFrame()
+{
+ assert(m_isInDestroy); // check that only DestroySwFrame does "delete"
+ assert(!IsDeleteForbidden()); // check that it's not deleted while deletes are forbidden
+#if OSL_DEBUG_LEVEL > 0
+ // JP 15.10.2001: for detection of access to deleted frames
+ mpRoot = reinterpret_cast<SwRootFrame*>(0x33333333);
+#endif
+}
+
+void SwFrame::DestroyFrame(SwFrame *const pFrame)
+{
+ if (pFrame)
+ {
+ pFrame->m_isInDestroy = true;
+ pFrame->DestroyImpl();
+ assert(pFrame->mbInDtor); // check that nobody forgot to call base class
+ delete pFrame;
+ }
+}
+
+const SwFrameFormat * SwLayoutFrame::GetFormat() const
+{
+ return static_cast< const SwFrameFormat * >( GetDep() );
+}
+
+SwFrameFormat * SwLayoutFrame::GetFormat()
+{
+ return static_cast< SwFrameFormat * >( GetDep() );
+}
+
+void SwLayoutFrame::SetFrameFormat(SwFrameFormat* pNew)
+{
+ if(pNew == GetFormat())
+ return;
+ const SwFormatChg aOldFormat(GetFormat());
+ pNew->Add(this);
+ const SwFormatChg aNewFormat(pNew);
+ SwClientNotify(*pNew, sw::LegacyModifyHint(&aOldFormat, &aNewFormat));
+}
+
+SwContentFrame::SwContentFrame( SwContentNode * const pContent, SwFrame* pSib ) :
+ SwFrame( pContent, pSib ),
+ SwFlowFrame( static_cast<SwFrame&>(*this) )
+{
+ assert(!getRootFrame()->HasMergedParas() || pContent->IsCreateFrameWhenHidingRedlines());
+}
+
+void SwContentFrame::DestroyImpl()
+{
+ const SwContentNode* pCNd(dynamic_cast<SwContentNode*>(GetDep()));
+ if (nullptr == pCNd && IsTextFrame())
+ {
+ pCNd = static_cast<SwTextFrame*>(this)->GetTextNodeFirst();
+ }
+ // IsInDtor shouldn't be happening with ViewShell owning layout
+ assert(nullptr == pCNd || !pCNd->GetDoc().IsInDtor());
+ if (nullptr != pCNd && !pCNd->GetDoc().IsInDtor())
+ {
+ //Unregister from root if I'm still in turbo there.
+ SwRootFrame *pRoot = getRootFrame();
+ if( pRoot && pRoot->GetTurbo() == this )
+ {
+ pRoot->DisallowTurbo();
+ pRoot->ResetTurbo();
+ }
+ }
+
+ SwFrame::DestroyImpl();
+}
+
+SwContentFrame::~SwContentFrame()
+{
+}
+
+void SwTextFrame::RegisterToNode(SwTextNode & rNode, bool const isForceNodeAsFirst)
+{
+ if (isForceNodeAsFirst && m_pMergedPara)
+ { // nothing registered here, in particular no delete redlines (insert
+ // redline might end on empty node where delete rl starts, should be ok)
+ assert(m_pMergedPara->pFirstNode->GetIndex() + 1 == rNode.GetIndex());
+ assert(rNode.GetDoc().getIDocumentRedlineAccess().GetRedlinePos(
+ *m_pMergedPara->pFirstNode, RedlineType::Delete) == SwRedlineTable::npos);
+ assert(std::find_if(
+ rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarksBegin(),
+ rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarksEnd(),
+ [this](::sw::mark::IMark const*const pMark) {
+ return pMark->GetMarkStart().nNode == *m_pMergedPara->pFirstNode
+ && pMark->GetMarkEnd().nNode != *m_pMergedPara->pFirstNode;
+ }) == rNode.GetDoc().getIDocumentMarkAccess()->getFieldmarksEnd());
+ }
+ assert(&rNode != GetDep());
+ assert(!m_pMergedPara
+ || (m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
+ || (rNode.GetIndex() + 1 == m_pMergedPara->pFirstNode->GetIndex()));
+ SwTextNode & rFirstNode(
+ (!isForceNodeAsFirst && m_pMergedPara && m_pMergedPara->pFirstNode->GetIndex() < rNode.GetIndex())
+ ? *m_pMergedPara->pFirstNode
+ : rNode);
+ // sw_redlinehide: use New here, because the only caller also calls lcl_ChangeFootnoteRef
+ m_pMergedPara = sw::CheckParaRedlineMerge(*this, rFirstNode, sw::FrameMode::New);
+ if (!m_pMergedPara)
+ {
+ rNode.Add(this);
+ }
+}
+
+void SwLayoutFrame::DestroyImpl()
+{
+ while (!m_VertPosOrientFramesFor.empty())
+ {
+ SwAnchoredObject *pObj = *m_VertPosOrientFramesFor.begin();
+ pObj->ClearVertPosOrientFrame();
+ }
+
+ assert(m_VertPosOrientFramesFor.empty());
+
+ SwFrame *pFrame = m_pLower;
+
+ if( GetFormat() && !GetFormat()->GetDoc()->IsInDtor() )
+ {
+ while ( pFrame )
+ {
+ //First delete the Objs of the Frame because they can't unregister
+ //from the page after remove.
+ //We don't want to create an endless loop only because one couldn't
+ //unregister.
+
+ while ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() )
+ {
+ const size_t nCnt = pFrame->GetDrawObjs()->size();
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[0];
+ if (SwFlyFrame* pFlyFrame = pAnchoredObj->DynCastFlyFrame())
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ assert(!pFrame->GetDrawObjs() || nCnt > pFrame->GetDrawObjs()->size());
+ }
+ else
+ {
+ pAnchoredObj->ClearTmpConsiderWrapInfluence();
+ SdrObject* pSdrObj = pAnchoredObj->DrawObj();
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(pSdrObj->GetUserCall());
+ OSL_ENSURE( pContact,
+ "<SwFrame::~SwFrame> - missing contact for drawing object" );
+ if ( pContact )
+ {
+ pContact->DisconnectObjFromLayout( pSdrObj );
+ }
+
+ if ( pFrame->GetDrawObjs() &&
+ nCnt == pFrame->GetDrawObjs()->size() )
+ {
+ pFrame->GetDrawObjs()->Remove( *pAnchoredObj );
+ }
+ }
+ }
+ pFrame->RemoveFromLayout();
+ SwFrame::DestroyFrame(pFrame);
+ pFrame = m_pLower;
+ }
+ //Delete the Flys, the last one also deletes the array.
+ while ( GetDrawObjs() && GetDrawObjs()->size() )
+ {
+ const size_t nCnt = GetDrawObjs()->size();
+
+ // #i28701#
+ SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[0];
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ SwFrame::DestroyFrame(pFlyFrame);
+ assert(!GetDrawObjs() || nCnt > GetDrawObjs()->size());
+ }
+ else
+ {
+ SdrObject* pSdrObj = pAnchoredObj->DrawObj();
+ SwDrawContact* pContact =
+ static_cast<SwDrawContact*>(pSdrObj->GetUserCall());
+ OSL_ENSURE( pContact,
+ "<SwFrame::~SwFrame> - missing contact for drawing object" );
+ if ( pContact )
+ {
+ pContact->DisconnectObjFromLayout( pSdrObj );
+ }
+
+ if ( GetDrawObjs() && nCnt == GetDrawObjs()->size() )
+ {
+ GetDrawObjs()->Remove( *pAnchoredObj );
+ }
+ }
+ }
+ }
+ else
+ {
+ while( pFrame )
+ {
+ SwFrame *pNxt = pFrame->GetNext();
+ SwFrame::DestroyFrame(pFrame);
+ pFrame = pNxt;
+ }
+ }
+
+ SwFrame::DestroyImpl();
+}
+
+SwLayoutFrame::~SwLayoutFrame()
+{
+}
+
+/**
+|* The paintarea is the area, in which the content of a frame is allowed
+|* to be displayed. This region could be larger than the printarea (getFramePrintArea())
+|* of the upper, it includes e.g. often the margin of the page.
+|*/
+SwRect SwFrame::GetPaintArea() const
+{
+ // NEW TABLES
+ // Cell frames may not leave their upper:
+ SwRect aRect = IsRowFrame() ? GetUpper()->getFrameArea() : getFrameArea();
+ const bool bVert = IsVertical();
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nRight = (aRect.*fnRect->fnGetRight)();
+ tools::Long nLeft = (aRect.*fnRect->fnGetLeft)();
+ const SwFrame* pTmp = this;
+ bool bLeft = true;
+ bool bRight = true;
+ tools::Long nRowSpan = 0;
+ while( pTmp )
+ {
+ if( pTmp->IsCellFrame() && pTmp->GetUpper() &&
+ pTmp->GetUpper()->IsVertical() != pTmp->IsVertical() )
+ nRowSpan = static_cast<const SwCellFrame*>(pTmp)->GetTabBox()->getRowSpan();
+ tools::Long nTmpRight = (pTmp->getFrameArea().*fnRect->fnGetRight)();
+ tools::Long nTmpLeft = (pTmp->getFrameArea().*fnRect->fnGetLeft)();
+ if( pTmp->IsRowFrame() && nRowSpan > 1 )
+ {
+ const SwFrame* pNxt = pTmp;
+ while( --nRowSpan > 0 && pNxt->GetNext() )
+ pNxt = pNxt->GetNext();
+ if( pTmp->IsVertical() )
+ nTmpLeft = (pNxt->getFrameArea().*fnRect->fnGetLeft)();
+ else
+ {
+ // pTmp is a row frame, but it's not vertical.
+ if (IsVertLRBT())
+ {
+ // This frame cell is OK to expand towards the physical down direction.
+ // Physical down is left.
+ nTmpLeft = (pNxt->getFrameArea().*fnRect->fnGetLeft)();
+ }
+ else
+ {
+ nTmpRight = (pNxt->getFrameArea().*fnRect->fnGetRight)();
+ }
+ }
+ }
+ OSL_ENSURE( pTmp, "GetPaintArea lost in time and space" );
+ if( pTmp->IsPageFrame() || pTmp->IsFlyFrame() ||
+ pTmp->IsCellFrame() || pTmp->IsRowFrame() || //nobody leaves a table!
+ pTmp->IsRootFrame() )
+ {
+ // BTLR is OK to expand towards the physical down direction. Physical down is left.
+ if( bLeft || (aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 && !IsVertLRBT()) )
+ nLeft = nTmpLeft;
+ if( bRight || aRectFnSet.XDiff(nRight, nTmpRight) > 0 )
+ nRight = nTmpRight;
+ if( pTmp->IsPageFrame() || pTmp->IsFlyFrame() || pTmp->IsRootFrame() )
+ break;
+ bLeft = false;
+ bRight = false;
+ }
+ else if( pTmp->IsColumnFrame() ) // nobody enters neighbour columns
+ {
+ bool bR2L = pTmp->IsRightToLeft();
+ // the first column has _no_ influence to the left range
+ if( bR2L ? pTmp->GetNext() : pTmp->GetPrev() )
+ {
+ if( bLeft || aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 )
+ nLeft = nTmpLeft;
+ bLeft = false;
+ }
+ // the last column has _no_ influence to the right range
+ if( bR2L ? pTmp->GetPrev() : pTmp->GetNext() )
+ {
+ if( bRight || aRectFnSet.XDiff(nRight, nTmpRight) > 0 )
+ nRight = nTmpRight;
+ bRight = false;
+ }
+ }
+ else if( bVert && pTmp->IsBodyFrame() )
+ {
+ // Header and footer frames have always horizontal direction and
+ // limit the body frame.
+ // A previous frame of a body frame must be a header,
+ // the next frame of a body frame may be a footnotecontainer or
+ // a footer. The footnotecontainer has the same direction like
+ // the body frame.
+ if( pTmp->GetPrev() && ( bLeft || aRectFnSet.XDiff(nTmpLeft, nLeft) > 0 ) )
+ {
+ nLeft = nTmpLeft;
+ bLeft = false;
+ }
+ if( pTmp->GetNext() &&
+ ( pTmp->GetNext()->IsFooterFrame() || pTmp->GetNext()->GetNext() )
+ && ( bRight || aRectFnSet.XDiff(nRight, nTmpRight) > 0 ) )
+ {
+ nRight = nTmpRight;
+ bRight = false;
+ }
+ }
+ pTmp = pTmp->GetUpper();
+ }
+ (aRect.*fnRect->fnSetLeft)( nLeft );
+ (aRect.*fnRect->fnSetRight)( nRight );
+ return aRect;
+}
+
+/**
+|* The unionframe is the framearea (getFrameArea()) of a frame expanded by the
+|* printarea, if there's a negative margin at the left or right side.
+|*/
+SwRect SwFrame::UnionFrame( bool bBorder ) const
+{
+ bool bVert = IsVertical();
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+ tools::Long nLeft = (getFrameArea().*fnRect->fnGetLeft)();
+ tools::Long nWidth = (getFrameArea().*fnRect->fnGetWidth)();
+ tools::Long nPrtLeft = (getFramePrintArea().*fnRect->fnGetLeft)();
+ tools::Long nPrtWidth = (getFramePrintArea().*fnRect->fnGetWidth)();
+ SwRectFnSet aRectFnSet(this);
+ if (aRectFnSet.XInc(nPrtLeft, nPrtWidth) > nWidth)
+ nWidth = nPrtLeft + nPrtWidth;
+ if( nPrtLeft < 0 )
+ {
+ nLeft += nPrtLeft;
+ nWidth -= nPrtLeft;
+ }
+ SwTwips nRight = aRectFnSet.XInc(nLeft, nWidth);
+ tools::Long nAdd = 0;
+ if( bBorder )
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), this );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ const SvxBoxItem &rBox = rAttrs.GetBox();
+ if ( rBox.GetLeft() )
+ nLeft -= rBox.CalcLineSpace( SvxBoxItemLine::LEFT );
+ else
+ nLeft -= rBox.GetDistance( SvxBoxItemLine::LEFT ) + 1;
+ if ( rBox.GetRight() )
+ nAdd += rBox.CalcLineSpace( SvxBoxItemLine::RIGHT );
+ else
+ nAdd += rBox.GetDistance( SvxBoxItemLine::RIGHT ) + 1;
+ if( rAttrs.GetShadow().GetLocation() != SvxShadowLocation::NONE )
+ {
+ const SvxShadowItem &rShadow = rAttrs.GetShadow();
+ nLeft -= rShadow.CalcShadowSpace( SvxShadowItemSide::LEFT );
+ nAdd += rShadow.CalcShadowSpace( SvxShadowItemSide::RIGHT );
+ }
+ }
+ if( IsTextFrame() && static_cast<const SwTextFrame*>(this)->HasPara() )
+ {
+ tools::Long nTmp = static_cast<const SwTextFrame*>(this)->HangingMargin();
+ if( nTmp > nAdd )
+ nAdd = nTmp;
+ }
+ nWidth = aRectFnSet.XDiff(aRectFnSet.XInc(nRight, nAdd), nLeft);
+ SwRect aRet( getFrameArea() );
+ (aRet.*fnRect->fnSetLeft)(nLeft);
+ (aRet.*fnRect->fnSetWidth)( nWidth );
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/swselectionlist.cxx b/sw/source/core/layout/swselectionlist.cxx
new file mode 100644
index 000000000..b7628cbac
--- /dev/null
+++ b/sw/source/core/layout/swselectionlist.cxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swselectionlist.hxx>
+#include <flyfrm.hxx>
+#include <ftnfrm.hxx>
+
+/** This class is used as parameter for functions to create a rectangular text selection
+*/
+
+namespace {
+
+ /** Find the context of a given frame
+
+ A context is the environment where text is allowed to flow.
+ The context is represented by
+ - the SwRootFrame if the frame is part of a page body
+ - the SwHeaderFrame if the frame is part of a page header
+ - the SwFooterFrame if the frame is part of a page footer
+ - the (master) SwFootnoteFrame if the frame is part of footnote
+ - the (first) SwFlyFrame if the frame is part of a (linked) fly frame
+
+ @param pFrame
+ the given frame
+
+ @return the context of the frame, represented by a SwFrame*
+ */
+ const SwFrame* getContext( const SwFrame* pFrame )
+ {
+ while( pFrame )
+ {
+ if( pFrame->IsRootFrame() || pFrame->IsHeaderFrame() || pFrame->IsFooterFrame() )
+ break;
+ if( pFrame->IsFlyFrame() )
+ {
+ const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>( pFrame );
+ while( pFly->GetPrevLink() )
+ pFly = pFly->GetPrevLink();
+ break;
+ }
+ if( pFrame->IsFootnoteFrame() )
+ {
+ const SwFootnoteFrame* pFootnote = static_cast<const SwFootnoteFrame*>( pFrame );
+ while( pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ break;
+ }
+ pFrame = pFrame->GetUpper();
+ }
+ return pFrame;
+ }
+}
+
+SwSelectionList::SwSelectionList( const SwFrame* pInitCxt ) :
+ m_pContext( getContext( pInitCxt ) )
+{
+}
+
+bool SwSelectionList::checkContext( const SwFrame* pCheck )
+{
+ pCheck = getContext( pCheck );
+ if( !m_pContext )
+ m_pContext = pCheck;
+ return m_pContext == pCheck;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
new file mode 100644
index 000000000..8e3a1b5dc
--- /dev/null
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -0,0 +1,6170 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <viewimp.hxx>
+#include <fesh.hxx>
+#include <swtable.hxx>
+#include <deletelistener.hxx>
+#include <dflyobj.hxx>
+#include <anchoreddrawobject.hxx>
+#include <fmtanchr.hxx>
+#include <viewopt.hxx>
+#include <hints.hxx>
+#include <dbg_lay.hxx>
+#include <ftnidx.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <basegfx/range/b1drange.hxx>
+#include <fmtlsplt.hxx>
+#include <fmtrowsplt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtornt.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtfsize.hxx>
+#include <swtblfmt.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <flyfrms.hxx>
+#include <txtfrm.hxx>
+#include <ftnfrm.hxx>
+#include <notxtfrm.hxx>
+#include <htmltbl.hxx>
+#include <sectfrm.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <sortedobjs.hxx>
+#include <objectformatter.hxx>
+#include <layouter.hxx>
+#include <calbck.hxx>
+#include <DocumentSettingManager.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <frameformats.hxx>
+
+using namespace ::com::sun::star;
+
+SwTabFrame::SwTabFrame( SwTable &rTab, SwFrame* pSib )
+ : SwLayoutFrame( rTab.GetFrameFormat(), pSib )
+ , SwFlowFrame( static_cast<SwFrame&>(*this) )
+ , m_pTable( &rTab )
+ , m_bComplete(false)
+ , m_bCalcLowers(false)
+ , m_bLowersFormatted(false)
+ , m_bLockBackMove(false)
+ , m_bResizeHTMLTable(false)
+ , m_bONECalcLowers(false)
+ , m_bHasFollowFlowLine(false)
+ , m_bIsRebuildLastLine(false)
+ , m_bRestrictTableGrowth(false)
+ , m_bRemoveFollowFlowLinePending(false)
+ , m_bConsiderObjsForMinCellHeight(true)
+ , m_bObjsDoesFit(true)
+ , m_bInRecalcLowerRow(false)
+{
+ mbFixSize = false; //Don't fall for import filter again.
+ mnFrameType = SwFrameType::Tab;
+
+ //Create the lines and insert them.
+ const SwTableLines &rLines = rTab.GetTabLines();
+ SwFrame *pTmpPrev = nullptr;
+ bool bHiddenRedlines = getRootFrame()->IsHideRedlines() &&
+ !GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable().empty();
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ // skip lines deleted with track changes
+ if ( bHiddenRedlines && rLines[i]->IsDeleted(nRedlinePos) )
+ continue;
+
+ SwRowFrame *pNew = new SwRowFrame( *rLines[i], this );
+ if( pNew->Lower() )
+ {
+ pNew->InsertBehind( this, pTmpPrev );
+ pTmpPrev = pNew;
+ }
+ else
+ SwFrame::DestroyFrame(pNew);
+ }
+ OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "SwTabFrame::SwTabFrame: No rows." );
+}
+
+SwTabFrame::SwTabFrame( SwTabFrame &rTab )
+ : SwLayoutFrame( rTab.GetFormat(), &rTab )
+ , SwFlowFrame( static_cast<SwFrame&>(*this) )
+ , m_pTable( rTab.GetTable() )
+ , m_bComplete(false)
+ , m_bCalcLowers(false)
+ , m_bLowersFormatted(false)
+ , m_bLockBackMove(false)
+ , m_bResizeHTMLTable(false)
+ , m_bONECalcLowers(false)
+ , m_bHasFollowFlowLine(false)
+ , m_bIsRebuildLastLine(false)
+ , m_bRestrictTableGrowth(false)
+ , m_bRemoveFollowFlowLinePending(false)
+ , m_bConsiderObjsForMinCellHeight(true)
+ , m_bObjsDoesFit(true)
+ , m_bInRecalcLowerRow(false)
+{
+ mbFixSize = false; //Don't fall for import filter again.
+ mnFrameType = SwFrameType::Tab;
+
+ SetFollow( rTab.GetFollow() );
+ rTab.SetFollow( this );
+}
+
+void SwTabFrame::DestroyImpl()
+{
+ // There is some terrible code in fetab.cxx, that
+ // caches pointers to SwTabFrames.
+ ::ClearFEShellTabCols(*GetFormat()->GetDoc(), this);
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwTabFrame::~SwTabFrame()
+{
+}
+
+void SwTabFrame::JoinAndDelFollows()
+{
+ SwTabFrame *pFoll = GetFollow();
+ if ( pFoll->HasFollow() )
+ pFoll->JoinAndDelFollows();
+ pFoll->Cut();
+ SetFollow( pFoll->GetFollow() );
+ SwFrame::DestroyFrame(pFoll);
+}
+
+void SwTabFrame::RegistFlys()
+{
+ OSL_ENSURE( Lower() && Lower()->IsRowFrame(), "No rows." );
+
+ SwPageFrame *pPage = FindPageFrame();
+ if ( pPage )
+ {
+ SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
+ do
+ {
+ pRow->RegistFlys( pPage );
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ } while ( pRow );
+ }
+}
+
+static void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom );
+static void lcl_RecalcRow( SwRowFrame& rRow, tools::Long nBottom );
+static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva );
+// #i26945# - add parameter <_bOnlyRowsAndCells> to control
+// that only row and cell frames are formatted.
+static bool lcl_InnerCalcLayout( SwFrame *pFrame,
+ tools::Long nBottom,
+ bool _bOnlyRowsAndCells = false );
+// OD 2004-02-18 #106629# - correct type of 1st parameter
+// #i26945# - add parameter <_bConsiderObjs> in order to
+// control, if floating screen objects have to be considered for the minimal
+// cell height.
+static SwTwips lcl_CalcMinRowHeight( const SwRowFrame *pRow,
+ const bool _bConsiderObjs );
+static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame&, const SwBorderAttrs& );
+
+static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow);
+
+static SwTwips lcl_GetHeightOfRows( const SwFrame* pStart, tools::Long nCount )
+{
+ if ( !nCount || !pStart)
+ return 0;
+
+ SwTwips nRet = 0;
+ SwRectFnSet aRectFnSet(pStart);
+ while ( pStart && nCount > 0 )
+ {
+ nRet += aRectFnSet.GetHeight(pStart->getFrameArea());
+ pStart = pStart->GetNext();
+ --nCount;
+ }
+
+ return nRet;
+}
+
+// Local helper function to insert a new follow flow line
+static SwRowFrame* lcl_InsertNewFollowFlowLine( SwTabFrame& rTab, const SwFrame& rTmpRow, bool bRowSpanLine )
+{
+ OSL_ENSURE( rTmpRow.IsRowFrame(), "No row frame to copy for FollowFlowLine" );
+ const SwRowFrame& rRow = static_cast<const SwRowFrame&>(rTmpRow);
+
+ rTab.SetFollowFlowLine( true );
+ SwRowFrame *pFollowFlowLine = new SwRowFrame(*rRow.GetTabLine(), &rTab, false );
+ pFollowFlowLine->SetRowSpanLine( bRowSpanLine );
+ SwFrame* pFirstRow = rTab.GetFollow()->GetFirstNonHeadlineRow();
+ pFollowFlowLine->InsertBefore( rTab.GetFollow(), pFirstRow );
+ return pFollowFlowLine;
+}
+
+// #i26945# - local helper function to invalidate all lower
+// objects. By parameter <_bMoveObjsOutOfRange> it can be controlled, if
+// additionally the objects are moved 'out of range'.
+static void lcl_InvalidateLowerObjs( SwLayoutFrame& _rLayoutFrame,
+ const bool _bMoveObjsOutOfRange = false,
+ SwPageFrame* _pPageFrame = nullptr )
+{
+ // determine page frame, if needed
+ if ( !_pPageFrame )
+ {
+ _pPageFrame = _rLayoutFrame.FindPageFrame();
+ OSL_ENSURE( _pPageFrame,
+ "<lcl_InvalidateLowerObjs(..)> - missing page frame -> no move of lower objects out of range" );
+ if ( !_pPageFrame )
+ {
+ return;
+ }
+ }
+
+ // loop on lower frames
+ SwFrame* pLowerFrame = _rLayoutFrame.Lower();
+ while ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsLayoutFrame() )
+ {
+ ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pLowerFrame),
+ _bMoveObjsOutOfRange, _pPageFrame );
+ }
+ if ( pLowerFrame->GetDrawObjs() )
+ {
+ for (size_t i = 0, nCount = pLowerFrame->GetDrawObjs()->size(); i < nCount; ++i)
+ {
+ SwAnchoredObject* pAnchoredObj = (*pLowerFrame->GetDrawObjs())[i];
+
+ // invalidate position of anchored object
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ pAnchoredObj->SetConsiderForTextWrap( false );
+ pAnchoredObj->UnlockPosition();
+ pAnchoredObj->InvalidateObjPos();
+
+ SwFlyFrame *pFly = pAnchoredObj->DynCastFlyFrame();
+
+ // move anchored object 'out of range'
+ if ( _bMoveObjsOutOfRange )
+ {
+ // indicate, that positioning is progress to avoid
+ // modification of the anchored object resp. it's attributes
+ // due to the movement
+ SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
+ pAnchoredObj->SetObjLeft( _pPageFrame->getFrameArea().Right() );
+ // #115759# - reset character rectangle,
+ // top of line and relative position in order to assure,
+ // that anchored object is correctly positioned.
+ pAnchoredObj->ClearCharRectAndTopOfLine();
+ pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
+ if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ == RndStdIds::FLY_AS_CHAR )
+ {
+ pAnchoredObj->AnchorFrame()
+ ->Prepare( PrepareHint::FlyFrameAttributesChanged,
+ &(pAnchoredObj->GetFrameFormat()) );
+ }
+ if ( pFly != nullptr )
+ {
+ pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
+ pFly->GetVirtDrawObj()->SetChanged();
+ }
+ }
+
+ // If anchored object is a fly frame, invalidate its lower objects
+ if ( pFly != nullptr )
+ {
+ ::lcl_InvalidateLowerObjs( *pFly, _bMoveObjsOutOfRange, _pPageFrame );
+ }
+ }
+ }
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+}
+
+// Local helper function to shrink all lowers of pRow to 0 height
+static void lcl_ShrinkCellsAndAllContent( SwRowFrame& rRow )
+{
+ SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rRow.Lower());
+ SwRectFnSet aRectFnSet(pCurrMasterCell);
+
+ bool bAllCellsCollapsed = true;
+ while ( pCurrMasterCell )
+ {
+ // NEW TABLES
+ SwCellFrame& rToAdjust = pCurrMasterCell->GetTabBox()->getRowSpan() < 1 ?
+ const_cast<SwCellFrame&>(pCurrMasterCell->FindStartEndOfRowSpanCell( true )) :
+ *pCurrMasterCell;
+
+ // #i26945#
+ // all lowers should have the correct position
+ lcl_ArrangeLowers( &rToAdjust,
+ aRectFnSet.GetPrtTop(rToAdjust),
+ false );
+ // TODO: Optimize number of frames which are set to 0 height
+ // we have to start with the last lower frame, otherwise
+ // the shrink will not shrink the current cell
+ SwFrame* pTmp = rToAdjust.GetLastLower();
+ bool bAllLowersCollapsed = true;
+
+ if ( pTmp && pTmp->IsRowFrame() )
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pTmp);
+ lcl_ShrinkCellsAndAllContent( *pTmpRow );
+ }
+ else
+ {
+ // TODO: Optimize number of frames which are set to 0 height
+ while ( pTmp )
+ {
+ // the frames have to be shrunk
+ if ( pTmp->IsTabFrame() )
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(static_cast<SwTabFrame*>(pTmp)->Lower());
+ bool bAllRowsCollapsed = true;
+
+ while ( pTmpRow )
+ {
+ lcl_ShrinkCellsAndAllContent( *pTmpRow );
+
+ if (aRectFnSet.GetHeight(pTmpRow->getFrameArea()) > 0)
+ bAllRowsCollapsed = false;
+
+ pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
+ }
+
+ if (bAllRowsCollapsed)
+ {
+ // All rows of this table have 0 height -> set height of the table itself as well.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pTmp);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+ else
+ bAllLowersCollapsed = false;
+ }
+ else
+ {
+ pTmp->Shrink(aRectFnSet.GetHeight(pTmp->getFrameArea()));
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pTmp);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+
+ if (aRectFnSet.GetHeight(pTmp->getFrameArea()) > 0)
+ {
+ bAllLowersCollapsed = false;
+ }
+ }
+
+ pTmp = pTmp->GetPrev();
+ }
+
+ // all lowers should have the correct position
+ lcl_ArrangeLowers( &rToAdjust,
+ aRectFnSet.GetPrtTop(rToAdjust),
+ false );
+ }
+
+ if (bAllLowersCollapsed)
+ {
+ // All lower frame of this cell have 0 height -> set height of the cell itself as well.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCurrMasterCell);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pCurrMasterCell);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+ else
+ bAllCellsCollapsed = false;
+
+ pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
+ }
+
+ if (bAllCellsCollapsed)
+ {
+ // All cells have 0 height -> set height of row as well.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(rRow);
+ aRectFnSet.SetHeight(aFrm, 0);
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(rRow);
+ aRectFnSet.SetTop(aPrt, 0);
+ aRectFnSet.SetHeight(aPrt, 0);
+ }
+}
+
+// Local helper function to move the content from rSourceLine to rDestLine
+// The content is inserted behind the last content in the corresponding
+// cell in rDestLine.
+static void lcl_MoveRowContent( SwRowFrame& rSourceLine, SwRowFrame& rDestLine )
+{
+ SwCellFrame* pCurrDestCell = static_cast<SwCellFrame*>(rDestLine.Lower());
+ SwCellFrame* pCurrSourceCell = static_cast<SwCellFrame*>(rSourceLine.Lower());
+
+ // Move content of follow cells into master cells
+ while ( pCurrSourceCell )
+ {
+ if ( pCurrSourceCell->Lower() && pCurrSourceCell->Lower()->IsRowFrame() )
+ {
+ SwRowFrame* pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
+ while ( pTmpSourceRow )
+ {
+ // #125926# Attention! It is possible,
+ // that pTmpSourceRow->IsFollowFlowRow() but pTmpDestRow
+ // cannot be found. In this case, we have to move the complete
+ // row.
+ SwRowFrame* pTmpDestRow = static_cast<SwRowFrame*>(pCurrDestCell->Lower());
+
+ if ( pTmpSourceRow->IsFollowFlowRow() && pTmpDestRow )
+ {
+ // move content from follow flow row to pTmpDestRow:
+ while ( pTmpDestRow->GetNext() )
+ pTmpDestRow = static_cast<SwRowFrame*>(pTmpDestRow->GetNext());
+
+ assert(pTmpDestRow->GetFollowRow() == pTmpSourceRow);
+
+ lcl_MoveRowContent( *pTmpSourceRow, *pTmpDestRow );
+ pTmpDestRow->SetFollowRow( pTmpSourceRow->GetFollowRow() );
+ pTmpSourceRow->RemoveFromLayout();
+ SwFrame::DestroyFrame(pTmpSourceRow);
+ }
+ else
+ {
+ // move complete row:
+ pTmpSourceRow->RemoveFromLayout();
+ pTmpSourceRow->InsertBefore( pCurrDestCell, nullptr );
+ }
+
+ pTmpSourceRow = static_cast<SwRowFrame*>(pCurrSourceCell->Lower());
+ }
+ }
+ else
+ {
+ SwFrame *pTmp = ::SaveContent( pCurrSourceCell );
+ if ( pTmp )
+ {
+ // NEW TABLES
+ SwCellFrame* pDestCell = pCurrDestCell;
+ if ( pDestCell->GetTabBox()->getRowSpan() < 1 )
+ pDestCell = & const_cast<SwCellFrame&>(pDestCell->FindStartEndOfRowSpanCell( true ));
+
+ // Find last content
+ SwFrame* pFrame = pDestCell->GetLastLower();
+ ::RestoreContent( pTmp, pDestCell, pFrame );
+ }
+ }
+ pCurrDestCell = static_cast<SwCellFrame*>(pCurrDestCell->GetNext());
+ pCurrSourceCell = static_cast<SwCellFrame*>(pCurrSourceCell->GetNext());
+ }
+}
+
+// Local helper function to move all footnotes in rRowFrame from
+// the footnote boss of rSource to the footnote boss of rDest.
+static void lcl_MoveFootnotes( SwTabFrame& rSource, SwTabFrame& rDest, SwLayoutFrame& rRowFrame )
+{
+ if ( !rSource.GetFormat()->GetDoc()->GetFootnoteIdxs().empty() )
+ {
+ SwFootnoteBossFrame* pOldBoss = rSource.FindFootnoteBossFrame( true );
+ SwFootnoteBossFrame* pNewBoss = rDest.FindFootnoteBossFrame( true );
+ rRowFrame.MoveLowerFootnotes( nullptr, pOldBoss, pNewBoss, true );
+ }
+}
+
+// Local helper function to handle nested table cells before the split process
+static void lcl_PreprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine,
+ SwRowFrame& rFollowFlowLine, SwTwips nRemain )
+{
+ SwCellFrame* pCurrLastLineCell = static_cast<SwCellFrame*>(rLastLine.Lower());
+ SwCellFrame* pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(rFollowFlowLine.Lower());
+
+ SwRectFnSet aRectFnSet(pCurrLastLineCell);
+
+ // Move content of follow cells into master cells
+ while ( pCurrLastLineCell )
+ {
+ if ( pCurrLastLineCell->Lower() && pCurrLastLineCell->Lower()->IsRowFrame() )
+ {
+ SwTwips nTmpCut = nRemain;
+ SwRowFrame* pTmpLastLineRow = static_cast<SwRowFrame*>(pCurrLastLineCell->Lower());
+
+ // #i26945#
+ SwTwips nCurrentHeight =
+ lcl_CalcMinRowHeight( pTmpLastLineRow,
+ rTab.IsConsiderObjsForMinCellHeight() );
+ while ( pTmpLastLineRow->GetNext() && nTmpCut > nCurrentHeight )
+ {
+ nTmpCut -= nCurrentHeight;
+ pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
+ // #i26945#
+ nCurrentHeight =
+ lcl_CalcMinRowHeight( pTmpLastLineRow,
+ rTab.IsConsiderObjsForMinCellHeight() );
+ }
+
+ // pTmpLastLineRow does not fit to the line or it is the last line
+ // Check if we can move pTmpLastLineRow to the follow table,
+ // or if we have to split the line:
+ bool bTableLayoutTooComplex = false;
+ tools::Long nMinHeight = 0;
+
+ // We have to take into account:
+ // 1. The fixed height of the row
+ // 2. The borders of the cells inside the row
+ // 3. The minimum height of the row
+ if ( pTmpLastLineRow->HasFixSize() )
+ nMinHeight = aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea());
+ else
+ {
+ {
+ const SwFormatFrameSize &rSz = pTmpLastLineRow->GetFormat()->GetFrameSize();
+ if ( rSz.GetHeightSizeType() == SwFrameSize::Minimum )
+ nMinHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pTmpLastLineRow);
+ }
+
+ SwFrame* pCell = pTmpLastLineRow->Lower();
+ while ( pCell )
+ {
+ if ( static_cast<SwCellFrame*>(pCell)->Lower() &&
+ static_cast<SwCellFrame*>(pCell)->Lower()->IsRowFrame() )
+ {
+ bTableLayoutTooComplex = true;
+ break;
+ }
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nMinHeight = std::max( nMinHeight, tools::Long(lcl_CalcTopAndBottomMargin( *static_cast<SwLayoutFrame*>(pCell), rAttrs )) );
+ pCell = pCell->GetNext();
+ }
+ }
+
+ // 1. Case:
+ // The line completely fits into the master table.
+ // Nevertheless, we build a follow (otherwise painting problems
+ // with empty cell).
+
+ // 2. Case:
+ // The line has to be split, the minimum height still fits into
+ // the master table, and the table structure is not too complex.
+ if ( nTmpCut > nCurrentHeight ||
+ ( pTmpLastLineRow->IsRowSplitAllowed() &&
+ !bTableLayoutTooComplex && nMinHeight < nTmpCut ) )
+ {
+ // The line has to be split:
+ SwRowFrame* pNewRow = new SwRowFrame( *pTmpLastLineRow->GetTabLine(), &rTab, false );
+ pNewRow->SetFollowFlowRow( true );
+ pNewRow->SetFollowRow( pTmpLastLineRow->GetFollowRow() );
+ pTmpLastLineRow->SetFollowRow( pNewRow );
+ pNewRow->InsertBehind( pCurrFollowFlowLineCell, nullptr );
+ pTmpLastLineRow = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
+ }
+
+ // The following lines have to be moved:
+ while ( pTmpLastLineRow )
+ {
+ SwRowFrame* pTmp = static_cast<SwRowFrame*>(pTmpLastLineRow->GetNext());
+ lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pTmpLastLineRow );
+ pTmpLastLineRow->RemoveFromLayout();
+ pTmpLastLineRow->InsertBefore( pCurrFollowFlowLineCell, nullptr );
+ pTmpLastLineRow->Shrink( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
+ pCurrFollowFlowLineCell->Grow( aRectFnSet.GetHeight(pTmpLastLineRow->getFrameArea()) );
+ pTmpLastLineRow = pTmp;
+ }
+ }
+
+ pCurrLastLineCell = static_cast<SwCellFrame*>(pCurrLastLineCell->GetNext());
+ pCurrFollowFlowLineCell = static_cast<SwCellFrame*>(pCurrFollowFlowLineCell->GetNext());
+ }
+}
+
+// Local helper function to handle nested table cells after the split process
+static void lcl_PostprocessRowsInCells( SwTabFrame& rTab, SwRowFrame& rLastLine )
+{
+ SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
+ while ( pCurrMasterCell )
+ {
+ if ( pCurrMasterCell->Lower() &&
+ pCurrMasterCell->Lower()->IsRowFrame() )
+ {
+ SwRowFrame* pRowFrame = static_cast<SwRowFrame*>(pCurrMasterCell->GetLastLower());
+
+ if ( nullptr != pRowFrame->GetPrev() && !pRowFrame->ContainsContent() )
+ {
+ OSL_ENSURE( pRowFrame->GetFollowRow(), "Deleting row frame without follow" );
+
+ // The footnotes have to be moved:
+ lcl_MoveFootnotes( rTab, *rTab.GetFollow(), *pRowFrame );
+ pRowFrame->Cut();
+ SwRowFrame* pFollowRow = pRowFrame->GetFollowRow();
+ pRowFrame->Paste( pFollowRow->GetUpper(), pFollowRow );
+ pRowFrame->SetFollowRow( pFollowRow->GetFollowRow() );
+ lcl_MoveRowContent( *pFollowRow, *pRowFrame );
+ pFollowRow->Cut();
+ SwFrame::DestroyFrame(pFollowRow);
+ ::SwInvalidateAll( pCurrMasterCell, LONG_MAX );
+ }
+ }
+
+ pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
+ }
+}
+
+// Local helper function to re-calculate the split line.
+inline void TableSplitRecalcLock( SwFlowFrame *pTab ) { pTab->LockJoin(); }
+inline void TableSplitRecalcUnlock( SwFlowFrame *pTab ) { pTab->UnlockJoin(); }
+
+static bool lcl_RecalcSplitLine( SwRowFrame& rLastLine, SwRowFrame& rFollowLine,
+ SwTwips nRemainingSpaceForLastRow, SwTwips nAlreadyFree )
+{
+ bool bRet = true;
+
+ vcl::RenderContext* pRenderContext = rLastLine.getRootFrame()->GetCurrShell()->GetOut();
+ SwTabFrame& rTab = static_cast<SwTabFrame&>(*rLastLine.GetUpper());
+ SwRectFnSet aRectFnSet(rTab.GetUpper());
+ SwTwips nCurLastLineHeight = aRectFnSet.GetHeight(rLastLine.getFrameArea());
+
+ // If there are nested cells in rLastLine, the recalculation of the last
+ // line needs some preprocessing.
+ lcl_PreprocessRowsInCells( rTab, rLastLine, rFollowLine, nRemainingSpaceForLastRow );
+
+ // Here the recalculation process starts:
+ rTab.SetRebuildLastLine( true );
+ // #i26945#
+ rTab.SetDoesObjsFit( true );
+
+ // #i26945# - invalidate and move floating screen
+ // objects 'out of range'
+ ::lcl_InvalidateLowerObjs( rLastLine, true );
+
+ // manipulate row and cell sizes
+
+ // #i26945# - Do *not* consider floating screen objects
+ // for the minimal cell height.
+ rTab.SetConsiderObjsForMinCellHeight( false );
+ ::lcl_ShrinkCellsAndAllContent( rLastLine );
+ rTab.SetConsiderObjsForMinCellHeight( true );
+
+ // invalidate last line
+ ::SwInvalidateAll( &rLastLine, LONG_MAX );
+
+ // Shrink the table to account for the shrunk last row, as well as lower rows
+ // that had been moved to follow table in SwTabFrame::Split.
+ // It will grow later when last line will recalc its height.
+ rTab.Shrink(nAlreadyFree + nCurLastLineHeight - nRemainingSpaceForLastRow + 1);
+
+ // Lock this tab frame and its follow
+ bool bUnlockMaster = false;
+ SwFlowFrame * pFollow = nullptr;
+ SwTabFrame* pMaster = rTab.IsFollow() ? rTab.FindMaster() : nullptr;
+ if ( pMaster && !pMaster->IsJoinLocked() )
+ {
+ bUnlockMaster = true;
+ ::TableSplitRecalcLock( pMaster );
+ }
+ if ( !rTab.GetFollow()->IsJoinLocked() )
+ {
+ pFollow = rTab.GetFollow();
+ ::TableSplitRecalcLock( pFollow );
+ }
+
+ bool bInSplit = rLastLine.IsInSplit();
+ rLastLine.SetInSplit();
+
+ // Do the recalculation
+ lcl_RecalcRow( rLastLine, LONG_MAX );
+ // #115759# - force a format of the last line in order to
+ // get the correct height.
+ rLastLine.InvalidateSize();
+ rLastLine.Calc(pRenderContext);
+
+ rLastLine.SetInSplit(bInSplit);
+
+ // Unlock this tab frame and its follow
+ if ( pFollow )
+ ::TableSplitRecalcUnlock( pFollow );
+ if ( bUnlockMaster )
+ ::TableSplitRecalcUnlock( pMaster );
+
+ // If there are nested cells in rLastLine, the recalculation of the last
+ // line needs some postprocessing.
+ lcl_PostprocessRowsInCells( rTab, rLastLine );
+
+ // Do a couple of checks on the current situation.
+
+ // If we are not happy with the current situation we return false.
+ // This will start a new try to split the table, this time we do not
+ // try to split the table rows.
+
+ // 1. Check if table fits to its upper.
+ // #i26945# - include check, if objects fit
+ const SwTwips nDistanceToUpperPrtBottom =
+ aRectFnSet.BottomDist(rTab.getFrameArea(), aRectFnSet.GetPrtBottom(*rTab.GetUpper()));
+ // tdf#125685 ignore footnotes that are anchored in follow-table of this
+ // table - if split is successful they move to the next page/column anyway
+ assert(rTab.GetFollow() == rFollowLine.GetUpper());
+ SwTwips nFollowFootnotes(0);
+ // actually there should always be a boss frame, except if "this" isn't
+ // connected to a page yet; not sure if that can happen
+ if (SwFootnoteBossFrame const*const pBoss = rTab.FindFootnoteBossFrame())
+ {
+ if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
+ {
+ for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
+ pFootnote != nullptr;
+ pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
+ {
+ SwContentFrame const*const pAnchor = pFootnote->GetRef();
+ SwTabFrame const* pTab = pAnchor->FindTabFrame();
+ if (pTab)
+ {
+ while (pTab->GetUpper()->IsInTab())
+ {
+ pTab = pTab->GetUpper()->FindTabFrame();
+ }
+ // TODO currently do this only for top-level tables?
+ // otherwise would need to check rTab's follow and any upper table's follow?
+ if (pTab == rTab.GetFollow())
+ {
+ nFollowFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
+ }
+ }
+ }
+ }
+ }
+ if (nDistanceToUpperPrtBottom + nFollowFootnotes < 0 || !rTab.DoesObjsFit())
+ bRet = false;
+
+ // 2. Check if each cell in the last line has at least one content frame.
+
+ // Note: a FollowFlowRow may contains empty cells!
+ if ( bRet )
+ {
+ if ( !rLastLine.IsInFollowFlowRow() )
+ {
+ SwCellFrame* pCurrMasterCell = static_cast<SwCellFrame*>(rLastLine.Lower());
+ while ( pCurrMasterCell )
+ {
+ if ( !pCurrMasterCell->ContainsContent() && pCurrMasterCell->GetTabBox()->getRowSpan() >= 1 )
+ {
+ bRet = false;
+ break;
+ }
+ pCurrMasterCell = static_cast<SwCellFrame*>(pCurrMasterCell->GetNext());
+ }
+ }
+ }
+
+ // 3. Check if last line does not contain any content:
+ if ( bRet )
+ {
+ if ( !rLastLine.ContainsContent() )
+ {
+ bRet = false;
+ }
+ }
+
+ // 4. Check if follow flow line does not contain content:
+ if ( bRet )
+ {
+ if ( !rFollowLine.IsRowSpanLine() && !rFollowLine.ContainsContent() )
+ {
+ bRet = false;
+ }
+ }
+
+ if ( bRet )
+ {
+ // Everything looks fine. Splitting seems to be successful. We invalidate
+ // rFollowLine to force a new formatting.
+ ::SwInvalidateAll( &rFollowLine, LONG_MAX );
+ }
+ else
+ {
+ // Splitting the table row gave us an unexpected result.
+ // Everything has to be prepared for a second try to split
+ // the table, this time without splitting the row.
+ ::SwInvalidateAll( &rLastLine, LONG_MAX );
+ }
+
+ rTab.SetRebuildLastLine( false );
+ // #i26945#
+ rTab.SetDoesObjsFit( true );
+
+ return bRet;
+}
+
+// Sets the correct height for all spanned cells
+static void lcl_AdjustRowSpanCells( SwRowFrame* pRow )
+{
+ SwRectFnSet aRectFnSet(pRow);
+ SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pRow->GetLower());
+ while ( pCellFrame )
+ {
+ const tools::Long nLayoutRowSpan = pCellFrame->GetLayoutRowSpan();
+ if ( nLayoutRowSpan > 1 )
+ {
+ // calculate height of cell:
+ const tools::Long nNewCellHeight = lcl_GetHeightOfRows( pRow, nLayoutRowSpan );
+ const tools::Long nDiff = nNewCellHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
+
+ if ( nDiff )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
+ aRectFnSet.AddBottom(aFrm, nDiff);
+ }
+ }
+
+ pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
+ }
+}
+
+// Returns the maximum layout row span of the row
+// Looking for the next row that contains no covered cells:
+static tools::Long lcl_GetMaximumLayoutRowSpan( const SwRowFrame& rRow )
+{
+ tools::Long nRet = 1;
+
+ const SwRowFrame* pCurrentRowFrame = static_cast<const SwRowFrame*>(rRow.GetNext());
+ bool bNextRow = false;
+
+ while ( pCurrentRowFrame )
+ {
+ // if there is any covered cell, we proceed to the next row frame
+ const SwCellFrame* pLower = static_cast<const SwCellFrame*>( pCurrentRowFrame->Lower());
+ while ( pLower )
+ {
+ if ( pLower->GetTabBox()->getRowSpan() < 0 )
+ {
+ ++nRet;
+ bNextRow = true;
+ break;
+ }
+ pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
+ }
+ pCurrentRowFrame = bNextRow ?
+ static_cast<const SwRowFrame*>(pCurrentRowFrame->GetNext() ) :
+ nullptr;
+ }
+
+ return nRet;
+}
+
+// Function to remove the FollowFlowLine of rTab.
+// The content of the FollowFlowLine is moved to the associated line in the
+// master table.
+bool SwTabFrame::RemoveFollowFlowLine()
+{
+ // find FollowFlowLine
+ SwTabFrame *pFoll = GetFollow();
+ SwRowFrame* pFollowFlowLine = pFoll ? pFoll->GetFirstNonHeadlineRow() : nullptr;
+
+ // find last row in master
+ SwFrame* pLastLine = GetLastLower();
+
+ OSL_ENSURE( HasFollowFlowLine() &&
+ pFollowFlowLine &&
+ pLastLine, "There should be a flowline in the follow" );
+
+ // #140081# Make code robust.
+ if ( !pFollowFlowLine || !pLastLine )
+ return true;
+ if (pFollowFlowLine->IsDeleteForbidden())
+ {
+ SAL_WARN("sw.layout", "Cannot remove in-use Follow Flow Line");
+ return false;
+ }
+
+ // We have to reset the flag here, because lcl_MoveRowContent
+ // calls a GrowFrame(), which has a different behavior if
+ // this flag is set.
+ SetFollowFlowLine( false );
+
+ // Move content
+ lcl_MoveRowContent( *pFollowFlowLine, *static_cast<SwRowFrame*>(pLastLine) );
+
+ // NEW TABLES
+ // If a row span follow flow line is removed, we want to move the whole span
+ // to the master:
+ tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pFollowFlowLine );
+
+ if ( nRowsToMove > 1 )
+ {
+ SwRectFnSet aRectFnSet(this);
+ SwFrame* pRow = pFollowFlowLine->GetNext();
+ SwFrame* pInsertBehind = GetLastLower();
+ SwTwips nGrow = 0;
+
+ while ( pRow && nRowsToMove-- > 1 )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ nGrow += aRectFnSet.GetHeight(pRow->getFrameArea());
+
+ // The footnotes have to be moved:
+ lcl_MoveFootnotes( *GetFollow(), *this, static_cast<SwRowFrame&>(*pRow) );
+
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( this, pInsertBehind );
+ pRow->InvalidateAll_();
+ pRow->CheckDirChange();
+ pInsertBehind = pRow;
+ pRow = pNxt;
+ }
+
+ SwFrame* pFirstRow = Lower();
+ while ( pFirstRow )
+ {
+ lcl_AdjustRowSpanCells( static_cast<SwRowFrame*>(pFirstRow) );
+ pFirstRow = pFirstRow->GetNext();
+ }
+
+ Grow( nGrow );
+ GetFollow()->Shrink( nGrow );
+ }
+
+ bool bJoin = !pFollowFlowLine->GetNext();
+ pFollowFlowLine->Cut();
+ SwFrame::DestroyFrame(pFollowFlowLine);
+
+ return bJoin;
+}
+
+// #i26945# - Floating screen objects are no longer searched.
+static bool lcl_FindSectionsInRow( const SwRowFrame& rRow )
+{
+ bool bRet = false;
+ const SwCellFrame* pLower = static_cast<const SwCellFrame*>(rRow.Lower());
+ while ( pLower )
+ {
+ if ( pLower->IsVertical() != rRow.IsVertical() )
+ return true;
+
+ const SwFrame* pTmpFrame = pLower->Lower();
+ while ( pTmpFrame )
+ {
+ if ( pTmpFrame->IsRowFrame() )
+ {
+ bRet = lcl_FindSectionsInRow( *static_cast<const SwRowFrame*>(pTmpFrame) );
+ }
+ else
+ {
+ // #i26945# - search only for sections
+ if (pTmpFrame->IsSctFrame())
+ {
+ bRet = true;
+
+ if (!rRow.IsInSct())
+ {
+ // This row is not in a section.
+ if (const SwFrame* pSectionLower = pTmpFrame->GetLower())
+ {
+ if (!pSectionLower->IsColumnFrame())
+ {
+ // Section has a single column only, try to
+ // split that.
+ bRet = false;
+
+ for (const SwFrame* pFrame = pSectionLower; pFrame; pFrame = pFrame->GetNext())
+ {
+ if (pFrame->IsTabFrame())
+ {
+ // Section contains a table, no split in that case.
+ bRet = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( bRet )
+ return true;
+ pTmpFrame = pTmpFrame->GetNext();
+ }
+
+ pLower = static_cast<const SwCellFrame*>(pLower->GetNext());
+ }
+ return bRet;
+}
+
+bool SwTabFrame::Split( const SwTwips nCutPos, bool bTryToSplit, bool bTableRowKeep )
+{
+ bool bRet = true;
+
+ SwRectFnSet aRectFnSet(this);
+
+ // #i26745# - format row and cell frames of table
+ {
+ Lower()->InvalidatePos_();
+ // #i43913# - correction
+ // call method <lcl_InnerCalcLayout> with first lower.
+ lcl_InnerCalcLayout( Lower(), LONG_MAX, true );
+ }
+
+ //In order to be able to compare the positions of the cells with CutPos,
+ //they have to be calculated consecutively starting from the table.
+ //They can definitely be invalid because of position changes of the table.
+ SwRowFrame *pRow = static_cast<SwRowFrame*>(Lower());
+ if( !pRow )
+ return bRet;
+
+ const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+ sal_uInt16 nRowCount = 0; // pRow currently points to the first row
+
+ SwTwips nRemainingSpaceForLastRow =
+ aRectFnSet.YDiff(nCutPos, aRectFnSet.GetTop(getFrameArea()));
+ nRemainingSpaceForLastRow -= aRectFnSet.GetTopMargin(*this);
+
+ // Make pRow point to the line that does not fit anymore:
+ while( pRow->GetNext() &&
+ nRemainingSpaceForLastRow >= ( aRectFnSet.GetHeight(pRow->getFrameArea()) +
+ (IsCollapsingBorders() ?
+ pRow->GetBottomLineSize() :
+ 0 ) ) )
+ {
+ if( bTryToSplit || !pRow->IsRowSpanLine() ||
+ 0 != aRectFnSet.GetHeight(pRow->getFrameArea()) )
+ ++nRowCount;
+ nRemainingSpaceForLastRow -= aRectFnSet.GetHeight(pRow->getFrameArea());
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ }
+
+ // bSplitRowAllowed: Row may be split according to its attributes.
+ // bTryToSplit: Row will never be split if bTryToSplit = false.
+ // This can either be passed as a parameter, indicating
+ // that we are currently doing the second try to split the
+ // table, or it will be set to false under certain
+ // conditions that are not suitable for splitting
+ // the row.
+ bool bSplitRowAllowed = true;
+ if (!pRow->IsRowSplitAllowed())
+ {
+ // A row larger than the entire page ought to be allowed to split regardless of setting,
+ // otherwise it has hidden content and that makes no sense
+ if ( pRow->getFrameArea().Height() > FindPageFrame()->getFramePrintArea().Height() )
+ pRow->SetForceRowSplitAllowed( true );
+ else
+ bSplitRowAllowed = false;
+ }
+ // #i29438#
+ // #i26945# - Floating screen objects no longer forbid
+ // a splitting of the table row.
+ // Special DoNotSplit case 1:
+ // Search for sections inside pRow:
+ if ( lcl_FindSectionsInRow( *pRow ) )
+ {
+ bTryToSplit = false;
+ }
+
+ // #i29771#
+ // To avoid loops, we do some checks before actually trying to split
+ // the row. Maybe we should keep the next row in this table.
+ // Note: This is only done if we are at the beginning of our upper
+ bool bKeepNextRow = false;
+ if ( nRowCount < nRepeat )
+ {
+ // First case: One of the repeated headline does not fit to the page anymore.
+ // tdf#88496 Disable repeated headline (like for #i44910#) to avoid loops and
+ // to fix interoperability problems (very long tables only with headline)
+ // tdf#150149 except in multi-column sections, where it's possible to enlarge
+ // the height of the section frame instead of using this fallback
+ OSL_ENSURE( !GetIndPrev(), "Table is supposed to be at beginning" );
+ if ( !IsInSct() )
+ {
+ m_pTable->SetRowsToRepeat(0);
+ return false;
+ }
+ else
+ bKeepNextRow = true;
+ }
+ else if ( !GetIndPrev() && nRepeat == nRowCount )
+ {
+ // Second case: The first non-headline row does not fit to the page.
+ // If it is not allowed to be split, or it contains a sub-row that
+ // is not allowed to be split, we keep the row in this table:
+ if ( bTryToSplit && bSplitRowAllowed )
+ {
+ // Check if there are (first) rows inside this row,
+ // which are not allowed to be split.
+ SwCellFrame* pLowerCell = static_cast<SwCellFrame*>(pRow->Lower());
+ while ( pLowerCell )
+ {
+ if ( pLowerCell->Lower() && pLowerCell->Lower()->IsRowFrame() )
+ {
+ const SwRowFrame* pLowerRow = static_cast<SwRowFrame*>(pLowerCell->Lower());
+ if ( !pLowerRow->IsRowSplitAllowed() &&
+ aRectFnSet.GetHeight(pLowerRow->getFrameArea()) > nRemainingSpaceForLastRow )
+ {
+ bKeepNextRow = true;
+ break;
+ }
+ }
+ pLowerCell = static_cast<SwCellFrame*>(pLowerCell->GetNext());
+ }
+ }
+ else
+ bKeepNextRow = true;
+ }
+
+ // Better keep the next row in this table:
+ if ( bKeepNextRow )
+ {
+ pRow = GetFirstNonHeadlineRow();
+ if ( pRow && pRow->IsRowSpanLine() && 0 == aRectFnSet.GetHeight(pRow->getFrameArea()) )
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ if ( pRow )
+ {
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+ ++nRowCount;
+ }
+ }
+
+ // No more row to split or to move to follow table:
+ if ( !pRow )
+ return bRet;
+
+ // We try to split the row if
+ // - the attributes of the row are set accordingly and
+ // - we are allowed to do so
+ // - it should not be kept with the next row
+ bSplitRowAllowed = bSplitRowAllowed && bTryToSplit &&
+ ( !bTableRowKeep ||
+ !pRow->ShouldRowKeepWithNext() );
+
+ // Adjust pRow according to the keep-with-next attribute:
+ if ( !bSplitRowAllowed && bTableRowKeep )
+ {
+ SwRowFrame* pTmpRow = static_cast<SwRowFrame*>(pRow->GetPrev());
+ SwRowFrame* pOldRow = pRow;
+ while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() &&
+ nRowCount > nRepeat )
+ {
+ pRow = pTmpRow;
+ --nRowCount;
+ pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetPrev());
+ }
+
+ // loop prevention
+ if ( nRowCount == nRepeat && !GetIndPrev())
+ {
+ pRow = pOldRow;
+ }
+ }
+
+ // If we do not intend to split pRow, we check if we are
+ // allowed to move pRow to a follow. Otherwise we return
+ // false, indicating an error
+ if ( !bSplitRowAllowed )
+ {
+ SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
+ if ( pRow == pFirstNonHeadlineRow )
+ return false;
+
+ // #i91764#
+ // Ignore row span lines
+ SwRowFrame* pTmpRow = pFirstNonHeadlineRow;
+ while ( pTmpRow && pTmpRow->IsRowSpanLine() )
+ {
+ pTmpRow = static_cast<SwRowFrame*>(pTmpRow->GetNext());
+ }
+ if ( !pTmpRow || pRow == pTmpRow )
+ {
+ return false;
+ }
+ }
+
+ // Build follow table if not already done:
+ bool bNewFollow;
+ SwTabFrame *pFoll;
+ if ( GetFollow() )
+ {
+ pFoll = GetFollow();
+ bNewFollow = false;
+ }
+ else
+ {
+ bNewFollow = true;
+ pFoll = new SwTabFrame( *this );
+
+ // We give the follow table an initial width.
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFoll);
+ aRectFnSet.AddWidth(aFrm, aRectFnSet.GetWidth(getFrameArea()));
+ aRectFnSet.SetLeft(aFrm, aRectFnSet.GetLeft(getFrameArea()));
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFoll);
+ aRectFnSet.AddWidth(aPrt, aRectFnSet.GetWidth(getFramePrintArea()));
+ }
+
+ // Insert the new follow table
+ pFoll->InsertBehind( GetUpper(), this );
+
+ // Repeat the headlines.
+ auto& rLines = GetTable()->GetTabLines();
+ for ( nRowCount = 0; nRowCount < nRepeat; ++nRowCount )
+ {
+ // Insert new headlines:
+ SwRowFrame* pHeadline = new SwRowFrame(*rLines[nRowCount], this);
+ {
+ sw::FlyCreationSuppressor aSuppressor;
+ pHeadline->SetRepeatedHeadline(true);
+ }
+ pHeadline->InsertBefore( pFoll, nullptr );
+
+ SwPageFrame *pPage = pHeadline->FindPageFrame();
+ const SwFrameFormats *pTable = GetFormat()->GetDoc()->GetSpzFrameFormats();
+ if( !pTable->empty() )
+ {
+ SwNodeOffset nIndex;
+ SwContentFrame* pFrame = pHeadline->ContainsContent();
+ while( pFrame )
+ {
+ // sw_redlinehide: the implementation of AppendObjs
+ // takes care of iterating merged SwTextFrame
+ nIndex = pFrame->IsTextFrame()
+ ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->GetIndex()
+ : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->GetIndex();
+ AppendObjs( pTable, nIndex, pFrame, pPage, GetFormat()->GetDoc());
+ pFrame = pFrame->GetNextContentFrame();
+ if( !pHeadline->IsAnLower( pFrame ) )
+ break;
+ }
+ }
+ }
+ }
+
+ SwRowFrame* pLastRow = nullptr; // points to the last remaining line in master
+ SwRowFrame* pFollowRow = nullptr; // points to either the follow flow line or the
+ // first regular line in the follow
+
+ if ( bSplitRowAllowed )
+ {
+ // If the row that does not fit anymore is allowed
+ // to be split, the next row has to be moved to the follow table.
+ pLastRow = pRow;
+ pRow = static_cast<SwRowFrame*>(pRow->GetNext());
+
+ // new follow flow line for last row of master table
+ pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, false );
+ }
+ else
+ {
+ pFollowRow = pRow;
+
+ // NEW TABLES
+ // check if we will break a row span by moving pFollowRow to the follow:
+ // In this case we want to reformat the last line.
+ const SwCellFrame* pCellFrame = static_cast<const SwCellFrame*>(pFollowRow->GetLower());
+ while ( pCellFrame )
+ {
+ if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
+ {
+ pLastRow = static_cast<SwRowFrame*>(pRow->GetPrev());
+ break;
+ }
+
+ pCellFrame = static_cast<const SwCellFrame*>(pCellFrame->GetNext());
+ }
+
+ // new follow flow line for last row of master table
+ if ( pLastRow )
+ pFollowRow = lcl_InsertNewFollowFlowLine( *this, *pLastRow, true );
+ }
+
+ SwTwips nShrink = 0;
+
+ //Optimization: There is no paste needed for the new Follow and the
+ //optimized insert can be used (large numbers of rows luckily only occur in
+ //such situations).
+ if ( bNewFollow )
+ {
+ SwFrame* pInsertBehind = pFoll->GetLastLower();
+
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
+ // The footnotes do not have to be moved, this is done in the
+ // MoveFwd of the follow table!!!
+ pRow->RemoveFromLayout();
+ pRow->InsertBehind( pFoll, pInsertBehind );
+ pRow->InvalidateAll_();
+ pInsertBehind = pRow;
+ pRow = static_cast<SwRowFrame*>(pNxt);
+ }
+ }
+ else
+ {
+ SwFrame* pPasteBefore = HasFollowFlowLine() ?
+ pFollowRow->GetNext() :
+ pFoll->GetFirstNonHeadlineRow();
+
+ while ( pRow )
+ {
+ SwFrame* pNxt = pRow->GetNext();
+ nShrink += aRectFnSet.GetHeight(pRow->getFrameArea());
+
+ // The footnotes have to be moved:
+ lcl_MoveFootnotes( *this, *GetFollow(), *pRow );
+
+ pRow->RemoveFromLayout();
+ pRow->Paste( pFoll, pPasteBefore );
+
+ pRow->CheckDirChange();
+ pRow = static_cast<SwRowFrame*>(pNxt);
+ }
+ }
+
+ if ( !pLastRow )
+ Shrink( nShrink );
+ else
+ {
+ // we rebuild the last line to assure that it will be fully formatted
+ // we also don't shrink here, because we will be doing that in lcl_RecalcSplitLine
+
+ // recalculate the split line
+ bRet = lcl_RecalcSplitLine( *pLastRow, *pFollowRow, nRemainingSpaceForLastRow, nShrink );
+
+ // RecalcSplitLine did not work. In this case we conceal the split error:
+ if (!bRet && !bSplitRowAllowed)
+ {
+ bRet = true;
+ }
+
+ // NEW TABLES
+ // check if each cell in the row span line has a good height
+ if ( bRet && pFollowRow->IsRowSpanLine() )
+ lcl_AdjustRowSpanCells( pFollowRow );
+ }
+
+ return bRet;
+}
+
+namespace
+{
+ bool CanDeleteFollow(SwTabFrame *pFoll)
+ {
+ if (pFoll->IsJoinLocked())
+ return false;
+
+ if (pFoll->IsDeleteForbidden())
+ {
+ SAL_WARN("sw.layout", "Delete Forbidden");
+ return false;
+ }
+
+ return true;
+ }
+}
+
+void SwTabFrame::Join()
+{
+ OSL_ENSURE( !HasFollowFlowLine(), "Joining follow flow line" );
+
+ SwTabFrame *pFoll = GetFollow();
+
+ if (!pFoll || !CanDeleteFollow(pFoll))
+ return;
+
+ SwRectFnSet aRectFnSet(this);
+ pFoll->Cut(); //Cut out first to avoid unnecessary notifications.
+
+ SwFrame *pRow = pFoll->GetFirstNonHeadlineRow(),
+ *pNxt;
+
+ SwFrame* pPrv = GetLastLower();
+
+ SwTwips nHeight = 0; //Total height of the inserted rows as return value.
+
+ while ( pRow )
+ {
+ pNxt = pRow->GetNext();
+ nHeight += aRectFnSet.GetHeight(pRow->getFrameArea());
+ pRow->RemoveFromLayout();
+ pRow->InvalidateAll_();
+ pRow->InsertBehind( this, pPrv );
+ pRow->CheckDirChange();
+ pPrv = pRow;
+ pRow = pNxt;
+ }
+
+ SetFollow( pFoll->GetFollow() );
+ SetFollowFlowLine( pFoll->HasFollowFlowLine() );
+ SwFrame::DestroyFrame(pFoll);
+
+ Grow( nHeight );
+}
+
+static void SwInvalidatePositions( SwFrame *pFrame, tools::Long nBottom )
+{
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ SwRectFnSet aRectFnSet(pFrame);
+ do
+ { pFrame->InvalidatePos_();
+ pFrame->InvalidateSize_();
+ if( pFrame->IsLayoutFrame() )
+ {
+ if ( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
+ {
+ ::SwInvalidatePositions( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
+ // #i26945#
+ ::lcl_InvalidateLowerObjs( *static_cast<SwLayoutFrame*>(pFrame) );
+ }
+ }
+ else
+ pFrame->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pFrame = pFrame->GetNext();
+ } while ( pFrame &&
+ ( bAll ||
+ aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
+}
+
+void SwInvalidateAll( SwFrame *pFrame, tools::Long nBottom )
+{
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ SwRectFnSet aRectFnSet(pFrame);
+ do
+ {
+ pFrame->InvalidatePos_();
+ pFrame->InvalidateSize_();
+ pFrame->InvalidatePrt_();
+ if( pFrame->IsLayoutFrame() )
+ {
+ // NEW TABLES
+ SwLayoutFrame* pToInvalidate = static_cast<SwLayoutFrame*>(pFrame);
+ if (pFrame->IsCellFrame())
+ {
+ SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
+ if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
+ {
+ pToInvalidate = & const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
+ pToInvalidate->InvalidatePos_();
+ pToInvalidate->InvalidateSize_();
+ pToInvalidate->InvalidatePrt_();
+ }
+ }
+ if ( pToInvalidate->Lower() )
+ ::SwInvalidateAll( pToInvalidate->Lower(), nBottom);
+ }
+ else
+ pFrame->Prepare();
+
+ pFrame = pFrame->GetNext();
+ } while ( pFrame &&
+ ( bAll ||
+ aRectFnSet.YDiff( aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom ) < 0 ) );
+}
+
+// #i29550#
+static void lcl_InvalidateAllLowersPrt( SwLayoutFrame* pLayFrame )
+{
+ pLayFrame->InvalidatePrt_();
+ pLayFrame->InvalidateSize_();
+ pLayFrame->SetCompletePaint();
+
+ SwFrame* pFrame = pLayFrame->Lower();
+
+ while ( pFrame )
+ {
+ if ( pFrame->IsLayoutFrame() )
+ lcl_InvalidateAllLowersPrt( static_cast<SwLayoutFrame*>(pFrame) );
+ else
+ {
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidateSize_();
+ pFrame->SetCompletePaint();
+ }
+
+ pFrame = pFrame->GetNext();
+ }
+}
+
+bool SwContentFrame::CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave,
+ tools::Long nBottom, bool bSkipRowSpanCells )
+{
+ vcl::RenderContext* pRenderContext = rLay.getRootFrame()->GetCurrShell()->GetOut();
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ bool bRet = false;
+ SwContentFrame *pCnt = rLay.ContainsContent();
+ SwRectFnSet aRectFnSet(&rLay);
+
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns = 0;
+ const int nLoopControlMax = 10;
+ const sw::BroadcastingModify* pLoopControlCond = nullptr;
+
+ while (pCnt && rDontLeave.IsAnLower(pCnt))
+ {
+ // #115759# - check, if a format of content frame is
+ // possible. Thus, 'copy' conditions, found at the beginning of
+ // <SwContentFrame::MakeAll(..)>, and check these.
+ const bool bFormatPossible = !pCnt->IsJoinLocked() &&
+ ( !pCnt->IsTextFrame() ||
+ !static_cast<SwTextFrame*>(pCnt)->IsLocked() ) &&
+ ( pCnt->IsFollow() || !StackHack::IsLocked() );
+
+ // NEW TABLES
+ bool bSkipContent = false;
+ if ( bSkipRowSpanCells && pCnt->IsInTab() )
+ {
+ const SwFrame* pCell = pCnt->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ if ( pCell && 1 != static_cast<const SwCellFrame*>( pCell )->GetLayoutRowSpan() )
+ bSkipContent = true;
+ }
+
+ if ( bFormatPossible && !bSkipContent )
+ {
+ bRet |= !pCnt->isFrameAreaDefinitionValid();
+ // #i26945# - no extra invalidation of floating
+ // screen objects needed.
+ // Thus, delete call of method <SwFrame::InvalidateObjs( true )>
+ pCnt->Calc(pRenderContext);
+ // #i46941# - frame has to be valid
+ // Note: frame could be invalid after calling its format, if it's locked.
+ OSL_ENSURE( !pCnt->IsTextFrame() ||
+ pCnt->isFrameAreaDefinitionValid() ||
+ static_cast<SwTextFrame*>(pCnt)->IsJoinLocked(),
+ "<SwContentFrame::CalcLowers(..)> - text frame invalid and not locked." );
+ if ( pCnt->IsTextFrame() && pCnt->isFrameAreaDefinitionValid() )
+ {
+ // #i23129#, #i36347# - pass correct page frame to
+ // the object formatter
+ if ( !SwObjectFormatter::FormatObjsAtFrame( *pCnt,
+ *(pCnt->FindPageFrame()) ) )
+ {
+ SwTextNode const*const pTextNode(
+ static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst());
+ if (pTextNode == pLoopControlCond)
+ ++nLoopControlRuns;
+ else
+ {
+ nLoopControlRuns = 0;
+ pLoopControlCond = pTextNode;
+ }
+
+ if ( nLoopControlRuns < nLoopControlMax )
+ {
+ // restart format with first content
+ pCnt = rLay.ContainsContent();
+ continue;
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ OSL_FAIL( "LoopControl in SwContentFrame::CalcLowers" );
+#endif
+ }
+ }
+ if (!rDontLeave.IsAnLower(pCnt)) // moved backward?
+ {
+ pCnt = rLay.ContainsContent();
+ continue; // avoid formatting new upper on different page
+ }
+ pCnt->GetUpper()->Calc(pRenderContext);
+ }
+ if( ! bAll && aRectFnSet.YDiff(aRectFnSet.GetTop(pCnt->getFrameArea()), nBottom) > 0 )
+ break;
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ return bRet;
+}
+
+// #i26945# - add parameter <_bOnlyRowsAndCells> to control
+// that only row and cell frames are formatted.
+static bool lcl_InnerCalcLayout( SwFrame *pFrame,
+ tools::Long nBottom,
+ bool _bOnlyRowsAndCells )
+{
+ vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell() ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
+ // LONG_MAX == nBottom means we have to calculate all
+ bool bAll = LONG_MAX == nBottom;
+ bool bRet = false;
+ const SwFrame* pOldUp = pFrame->GetUpper();
+ SwRectFnSet aRectFnSet(pFrame);
+ do
+ {
+ // #i26945# - parameter <_bOnlyRowsAndCells> controls,
+ // if only row and cell frames are formatted.
+ if ( pFrame->IsLayoutFrame() &&
+ ( !_bOnlyRowsAndCells || pFrame->IsRowFrame() || pFrame->IsCellFrame() ) )
+ {
+ SwFrameDeleteGuard aDeleteGuard(pFrame);
+
+ // #130744# An invalid locked table frame will
+ // not be calculated => It will not become valid =>
+ // Loop in lcl_RecalcRow(). Therefore we do not consider them for bRet.
+ bRet |= !pFrame->isFrameAreaDefinitionValid() && ( !pFrame->IsTabFrame() || !static_cast<SwTabFrame*>(pFrame)->IsJoinLocked() );
+ pFrame->Calc(pRenderContext);
+ if( static_cast<SwLayoutFrame*>(pFrame)->Lower() )
+ bRet |= lcl_InnerCalcLayout( static_cast<SwLayoutFrame*>(pFrame)->Lower(), nBottom);
+
+ // NEW TABLES
+ if (pFrame->IsCellFrame())
+ {
+ SwCellFrame* pThisCell = static_cast<SwCellFrame*>(pFrame);
+ if ( pThisCell->GetTabBox()->getRowSpan() < 1 )
+ {
+ SwCellFrame& rToCalc = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( true ));
+ bRet |= !rToCalc.isFrameAreaDefinitionValid();
+ rToCalc.Calc(pRenderContext);
+ if ( rToCalc.Lower() )
+ bRet |= lcl_InnerCalcLayout( rToCalc.Lower(), nBottom);
+ }
+ }
+ }
+ pFrame = pFrame->GetNext();
+ } while( pFrame &&
+ ( bAll ||
+ aRectFnSet.YDiff(aRectFnSet.GetTop(pFrame->getFrameArea()), nBottom) < 0 )
+ && pFrame->GetUpper() == pOldUp );
+ return bRet;
+}
+
+static void lcl_RecalcRow(SwRowFrame & rRow, tools::Long const nBottom)
+{
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns_1 = 0;
+ sal_uInt16 nLoopControlStage_1 = 0;
+ const int nLoopControlMax = 10;
+
+ bool bCheck = true;
+ do
+ {
+ // FME 2007-08-30 #i81146# new loop control
+ int nLoopControlRuns_2 = 0;
+ sal_uInt16 nLoopControlStage_2 = 0;
+
+ while (lcl_InnerCalcLayout(&rRow, nBottom))
+ {
+ if ( ++nLoopControlRuns_2 > nLoopControlMax )
+ {
+ SAL_WARN_IF(nLoopControlStage_2 == 0, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 1!");
+ SAL_WARN_IF(nLoopControlStage_2 == 1, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 2!!");
+ SAL_WARN_IF(nLoopControlStage_2 >= 2, "sw.layout", "LoopControl_2 in lcl_RecalcRow: Stage 3!!!");
+ rRow.ValidateThisAndAllLowers( nLoopControlStage_2++ );
+ nLoopControlRuns_2 = 0;
+ if( nLoopControlStage_2 > 2 )
+ break;
+ }
+
+ bCheck = true;
+ }
+
+ if( bCheck )
+ {
+ // #115759# - force another format of the
+ // lowers, if at least one of it was invalid.
+ bCheck = SwContentFrame::CalcLowers(rRow, *rRow.GetUpper(), nBottom, true);
+
+ // NEW TABLES
+ // First we calculate the cells with row span of < 1, afterwards
+ // all cells with row span of > 1:
+ for ( int i = 0; i < 2; ++i )
+ {
+ SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(rRow.Lower());
+ while ( pCellFrame )
+ {
+ const bool bCalc = 0 == i ?
+ pCellFrame->GetLayoutRowSpan() < 1 :
+ pCellFrame->GetLayoutRowSpan() > 1;
+
+ if ( bCalc )
+ {
+ SwCellFrame& rToRecalc = 0 == i ?
+ const_cast<SwCellFrame&>(pCellFrame->FindStartEndOfRowSpanCell( true )) :
+ *pCellFrame;
+ bCheck |= SwContentFrame::CalcLowers(rToRecalc, rToRecalc, nBottom, false);
+ }
+
+ pCellFrame = static_cast<SwCellFrame*>(pCellFrame->GetNext());
+ }
+ }
+
+ if ( bCheck )
+ {
+ if ( ++nLoopControlRuns_1 > nLoopControlMax )
+ {
+ SAL_WARN_IF(nLoopControlStage_1 == 0, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 1!");
+ SAL_WARN_IF(nLoopControlStage_1 == 1, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 2!!");
+ SAL_WARN_IF(nLoopControlStage_1 >= 2, "sw.layout", "LoopControl_1 in lcl_RecalcRow: Stage 3!!!");
+ rRow.ValidateThisAndAllLowers( nLoopControlStage_1++ );
+ nLoopControlRuns_1 = 0;
+ if( nLoopControlStage_1 > 2 )
+ break;
+ }
+
+ continue;
+ }
+ }
+ break;
+ } while( true );
+}
+
+static void lcl_RecalcTable( SwTabFrame& rTab,
+ SwLayoutFrame *pFirstRow,
+ SwLayNotify &rNotify )
+{
+ if ( rTab.Lower() )
+ {
+ if ( !pFirstRow )
+ {
+ pFirstRow = static_cast<SwLayoutFrame*>(rTab.Lower());
+ rNotify.SetLowersComplete( true );
+ }
+ ::SwInvalidatePositions( pFirstRow, LONG_MAX );
+ lcl_RecalcRow( *static_cast<SwRowFrame*>(pFirstRow), LONG_MAX );
+ }
+}
+
+// This is a new function to check the first condition whether
+// a tab frame may move backward. It replaces the formerly used
+// GetIndPrev(), which did not work correctly for #i5947#
+static bool lcl_NoPrev( const SwFrame& rFrame )
+{
+ // #i79774#
+ // skip empty sections on investigation of direct previous frame.
+ // use information, that at least one empty section is skipped in the following code.
+ bool bSkippedDirectPrevEmptySection( false );
+ if ( rFrame.GetPrev() )
+ {
+ const SwFrame* pPrev( rFrame.GetPrev() );
+ while ( pPrev &&
+ pPrev->IsSctFrame() &&
+ !dynamic_cast<const SwSectionFrame&>(*pPrev).GetSection() )
+ {
+ pPrev = pPrev->GetPrev();
+ bSkippedDirectPrevEmptySection = true;
+ }
+ if ( pPrev )
+ {
+ return false;
+ }
+ }
+
+ if ( ( !bSkippedDirectPrevEmptySection && !rFrame.GetIndPrev() ) ||
+ ( bSkippedDirectPrevEmptySection &&
+ ( !rFrame.IsInSct() || !rFrame.GetIndPrev_() ) ) )
+ {
+ return true;
+ }
+
+ // I do not have a direct prev, but I have an indirect prev.
+ // In section frames I have to check if I'm located inside
+ // the first column:
+ if ( rFrame.IsInSct() )
+ {
+ const SwFrame* pSct = rFrame.GetUpper();
+ if ( pSct && pSct->IsColBodyFrame() &&
+ pSct->GetUpper()->GetUpper()->IsSctFrame() )
+ {
+ const SwFrame* pPrevCol = rFrame.GetUpper()->GetUpper()->GetPrev();
+ if ( pPrevCol )
+ // I'm not inside the first column and do not have a direct
+ // prev. I can try to go backward.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define KEEPTAB ( !GetFollow() && !IsFollow() )
+
+// - helper method to find next content frame of
+// a table frame and format it to assure keep attribute.
+// method return true, if a next content frame is formatted.
+// Precondition: The given table frame hasn't a follow and isn't a follow.
+SwFrame* sw_FormatNextContentForKeep( SwTabFrame* pTabFrame )
+{
+ vcl::RenderContext* pRenderContext = pTabFrame->getRootFrame()->GetCurrShell()->GetOut();
+ // find next content, table or section
+ SwFrame* pNxt = pTabFrame->FindNext();
+
+ // skip empty sections
+ while ( pNxt && pNxt->IsSctFrame() &&
+ !static_cast<SwSectionFrame*>(pNxt)->GetSection() )
+ {
+ pNxt = pNxt->FindNext();
+ }
+
+ // if found next frame is a section, get its first content.
+ if ( pNxt && pNxt->IsSctFrame() )
+ {
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ }
+
+ // format found next frame.
+ // if table frame is inside another table, method <SwFrame::MakeAll()> is
+ // called to avoid that the superior table frame is formatted.
+ if ( pNxt )
+ {
+ if ( pTabFrame->GetUpper()->IsInTab() )
+ pNxt->MakeAll(pNxt->getRootFrame()->GetCurrShell()->GetOut());
+ else
+ pNxt->Calc(pRenderContext);
+ }
+
+ return pNxt;
+}
+
+namespace {
+ bool AreAllRowsKeepWithNext( const SwRowFrame* pFirstRowFrame, const bool bCheckParents = true )
+ {
+ bool bRet = pFirstRowFrame != nullptr &&
+ pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
+
+ while ( bRet && pFirstRowFrame->GetNext() != nullptr )
+ {
+ pFirstRowFrame = dynamic_cast<const SwRowFrame*>(pFirstRowFrame->GetNext());
+ bRet = pFirstRowFrame != nullptr &&
+ pFirstRowFrame->ShouldRowKeepWithNext( bCheckParents );
+ }
+
+ return bRet;
+ }
+}
+
+// extern because static can't be friend
+void FriendHackInvalidateRowFrame(SwFrameAreaDefinition & rRowFrame)
+{
+ // hilariously static_cast<SwTabFrame*>(GetLower()) would not require friend declaration, but it's UB...
+ rRowFrame.setFrameAreaPositionValid(false);
+}
+
+static void InvalidateFramePositions(SwFrame * pFrame)
+{
+ while (pFrame)
+ {
+ if (pFrame->IsLayoutFrame())
+ {
+ InvalidateFramePositions(pFrame->GetLower());
+ }
+ else if (pFrame->IsTextFrame())
+ {
+ pFrame->Prepare(PrepareHint::FramePositionChanged);
+ }
+ pFrame = pFrame->GetNext();
+ }
+}
+
+void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( IsJoinLocked() || StackHack::IsLocked() || StackHack::Count() > 50 )
+ return;
+
+ if ( HasFollow() )
+ {
+ SwTabFrame* pFollowFrame = GetFollow();
+ OSL_ENSURE( !pFollowFrame->IsJoinLocked() || !pFollowFrame->IsRebuildLastLine(),
+ "SwTabFrame::MakeAll for master while follow is in RebuildLastLine()" );
+ if ( pFollowFrame->IsJoinLocked() && pFollowFrame->IsRebuildLastLine() )
+ return;
+ }
+
+ PROTOCOL_ENTER( this, PROT::MakeAll, DbgAction::NONE, nullptr )
+
+ LockJoin(); //I don't want to be destroyed on the way.
+ SwLayNotify aNotify( this ); //does the notification in the DTor
+ // If pos is invalid, we have to call a SetInvaKeep at aNotify.
+ // Otherwise the keep attribute would not work in front of a table.
+ const bool bOldValidPos = isFrameAreaPositionValid();
+
+ //If my neighbour is my Follow at the same time, I'll swallow it up.
+ // OD 09.04.2003 #108698# - join all follows, which are placed on the
+ // same page/column.
+ // OD 29.04.2003 #109213# - join follow, only if join for the follow
+ // is not locked. Otherwise, join will not be performed and this loop
+ // will be endless.
+ while ( GetNext() && GetNext() == GetFollow() &&
+ CanDeleteFollow(GetFollow())
+ )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+
+ // The bRemoveFollowFlowLinePending is set if the split attribute of the
+ // last line is set:
+ if ( IsRemoveFollowFlowLinePending() && HasFollowFlowLine() )
+ {
+ if ( RemoveFollowFlowLine() )
+ Join();
+ SetRemoveFollowFlowLinePending( false );
+ }
+
+ if (m_bResizeHTMLTable) //Optimized interplay with grow/shrink of the content
+ {
+ m_bResizeHTMLTable = false;
+ SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
+ if ( pLayout )
+ m_bCalcLowers = pLayout->Resize(
+ pLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+
+ // as long as bMakePage is true, a new page can be created (exactly once)
+ bool bMakePage = true;
+ // bMovedBwd gets set to true when the frame flows backwards
+ bool bMovedBwd = false;
+ // as long as bMovedFwd is false, the Frame may flow backwards (until
+ // it has been moved forward once)
+ bool bMovedFwd = false;
+ // gets set to true when the Frame is split
+ bool bSplit = false;
+ const bool bFootnotesInDoc = !GetFormat()->GetDoc()->GetFootnoteIdxs().empty();
+ const bool bFly = IsInFly();
+
+ std::optional<SwBorderAttrAccess> oAccess(std::in_place, SwFrame::GetCache(), this);
+ const SwBorderAttrs *pAttrs = oAccess->Get();
+
+ // All rows should keep together
+ const bool bDontSplit = !IsFollow() &&
+ ( !GetFormat()->GetLayoutSplit().GetValue() );
+
+ // The number of repeated headlines
+ const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+
+ // This flag indicates that we are allowed to try to split the
+ // table rows.
+ bool bTryToSplit = true;
+
+ // Indicates that two individual rows may keep together, based on the keep
+ // attribute set at the first paragraph in the first cell.
+ const bool bTableRowKeep = !bDontSplit && GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP);
+
+ // The Magic Move: Used for the table row keep feature.
+ // If only the last row of the table wants to keep (implicitly by setting
+ // keep for the first paragraph in the first cell), and this table does
+ // not have a next, the last line will be cut. Loop prevention: Only
+ // one try.
+ // WHAT IS THIS??? It "magically" hides last line (paragraph) in a table,
+ // if first is set to keep with next???
+ bool bLastRowHasToMoveToFollow = false;
+ bool bLastRowMoveNoMoreTries = false;
+
+ const bool bLargeTable = GetTable()->GetTabLines().size() > 64; //arbitrary value, virtually guaranteed to be larger than one page.
+ const bool bEmulateTableKeep = !bLargeTable && bTableRowKeep
+ && !pAttrs->GetAttrSet().GetKeep().GetValue()
+ && AreAllRowsKeepWithNext(GetFirstNonHeadlineRow(), /*bCheckParents=*/false);
+ // The beloved keep attribute
+ const bool bKeep = IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), bEmulateTableKeep);
+
+ // Join follow table, if this table is not allowed to split:
+ if ( bDontSplit )
+ {
+ while ( GetFollow() && !GetFollow()->IsJoinLocked() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+ }
+
+ // Join follow table, if this does not have enough (repeated) lines:
+ if ( nRepeat )
+ {
+ if( GetFollow() && !GetFollow()->IsJoinLocked() &&
+ nullptr == GetFirstNonHeadlineRow() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+ }
+
+ // Join follow table, if last row of this table should keep:
+ if ( bTableRowKeep && GetFollow() && !GetFollow()->IsJoinLocked() )
+ {
+ const SwRowFrame* pTmpRow = static_cast<const SwRowFrame*>(GetLastLower());
+ if ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ Join();
+ }
+ }
+
+ // a new one is moved forwards immediately
+ if ( !getFrameArea().Top() && IsFollow() )
+ {
+ SwFrame *pPre = GetPrev();
+ if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
+ {
+ // don't make the effort to move fwd if its known
+ // conditions that are known not to work
+ if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
+ bMakePage = false;
+ else if (!MoveFwd(bMakePage, false))
+ bMakePage = false;
+ bMovedFwd = true;
+ }
+ }
+
+ int nUnSplitted = 5; // Just another loop control :-(
+ int nThrowAwayValidLayoutLimit = 5; // And another one :-(
+ SwRectFnSet aRectFnSet(this);
+ while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ const bool bMoveable = IsMoveable();
+ if (bMoveable &&
+ !(bMovedFwd && bEmulateTableKeep) )
+ if ( CheckMoveFwd( bMakePage, bKeep && KEEPTAB, bEmulateTableKeep ) )
+ {
+ bMovedFwd = true;
+ m_bCalcLowers = true;
+ // #i99267#
+ // reset <bSplit> after forward move to assure that follows
+ // can be joined, if further space is available.
+ bSplit = false;
+ }
+
+ Point aOldPos( aRectFnSet.GetPos(getFrameArea()) );
+ MakePos();
+
+ if ( aOldPos != aRectFnSet.GetPos(getFrameArea()) )
+ {
+ if ( aOldPos.Y() != aRectFnSet.GetTop(getFrameArea()) )
+ {
+ SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
+ if( pLayout )
+ {
+ oAccess.reset();
+ m_bCalcLowers |= pLayout->Resize(
+ pLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+
+ setFramePrintAreaValid(false);
+ aNotify.SetLowersComplete( false );
+ }
+ SwFrame *pPre;
+ if ( bKeep || (nullptr != (pPre = FindPrev()) &&
+ pPre->GetAttrSet()->GetKeep().GetValue()) )
+ {
+ m_bCalcLowers = true;
+ }
+ if (GetLower())
+ { // it's possible that the rows already have valid pos - but it is surely wrong if the table's pos changed!
+ FriendHackInvalidateRowFrame(*GetLower());
+ // invalidate text frames to get rid of their SwFlyPortions
+ InvalidateFramePositions(GetLower());
+ }
+ }
+
+ //We need to know the height of the first row, because the master needs
+ //to be invalidated if it shrinks and then absorb the row if possible.
+ tools::Long n1StLineHeight = 0;
+ if ( IsFollow() )
+ {
+ SwFrame* pFrame = GetFirstNonHeadlineRow();
+ if ( pFrame )
+ n1StLineHeight = aRectFnSet.GetHeight(pFrame->getFrameArea());
+ }
+
+ if ( !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ {
+ const tools::Long nOldPrtWidth = aRectFnSet.GetWidth(getFramePrintArea());
+ const tools::Long nOldFrameWidth = aRectFnSet.GetWidth(getFrameArea());
+ const Point aOldPrtPos = aRectFnSet.GetPos(getFramePrintArea());
+
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
+
+ SwHTMLTableLayout *pLayout = GetTable()->GetHTMLTableLayout();
+ if ( pLayout &&
+ (aRectFnSet.GetWidth(getFramePrintArea()) != nOldPrtWidth ||
+ aRectFnSet.GetWidth(getFrameArea()) != nOldFrameWidth) )
+ {
+ oAccess.reset();
+ m_bCalcLowers |= pLayout->Resize(
+ pLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+ if ( aOldPrtPos != aRectFnSet.GetPos(getFramePrintArea()) )
+ aNotify.SetLowersComplete( false );
+ }
+
+ // If this is the first one in a chain, check if this can flow
+ // backwards (if this is movable at all).
+ // To prevent oscillations/loops, check that this has not just
+ // flowed forwards.
+ if ( !bMovedFwd && (bMoveable || bFly) && lcl_NoPrev( *this ) )
+ {
+ // for Follows notify Master.
+ // only move Follow if it has to skip empty pages.
+ if ( IsFollow() )
+ {
+ // Only if the height of the first line got smaller.
+ SwFrame *pFrame = GetFirstNonHeadlineRow();
+ if( pFrame && n1StLineHeight >aRectFnSet.GetHeight(pFrame->getFrameArea()) )
+ {
+ SwTabFrame *pMaster = FindMaster();
+ bool bDummy;
+ if ( ShouldBwdMoved( pMaster->GetUpper(), bDummy ) )
+ pMaster->InvalidatePos();
+ }
+ }
+ SwFootnoteBossFrame *pOldBoss = bFootnotesInDoc ? FindFootnoteBossFrame( true ) : nullptr;
+ bool bReformat;
+ std::optional<SfxDeleteListener> oDeleteListener;
+ if (pOldBoss)
+ oDeleteListener.emplace(*pOldBoss);
+ SwFrameDeleteGuard g(this);
+ if ( MoveBwd( bReformat ) )
+ {
+ SAL_WARN_IF(oDeleteListener && oDeleteListener->WasDeleted(), "sw.layout", "SwFootnoteBossFrame unexpectedly deleted");
+
+ aRectFnSet.Refresh(this);
+ bMovedBwd = true;
+ aNotify.SetLowersComplete( false );
+ if (bFootnotesInDoc && !oDeleteListener->WasDeleted())
+ MoveLowerFootnotes( nullptr, pOldBoss, nullptr, true );
+ if ( bReformat || bKeep )
+ {
+ tools::Long nOldTop = aRectFnSet.GetTop(getFrameArea());
+ MakePos();
+ if( nOldTop != aRectFnSet.GetTop(getFrameArea()) )
+ {
+ SwHTMLTableLayout *pHTMLLayout =
+ GetTable()->GetHTMLTableLayout();
+ if( pHTMLLayout )
+ {
+ oAccess.reset();
+ m_bCalcLowers |= pHTMLLayout->Resize(
+ pHTMLLayout->GetBrowseWidthByTabFrame( *this ) );
+ }
+
+ setFramePrintAreaValid(false);
+
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ Format( getRootFrame()->GetCurrShell()->GetOut(), pAttrs );
+ }
+
+ oAccess.reset();
+
+ lcl_RecalcTable( *this, nullptr, aNotify );
+
+ m_bLowersFormatted = true;
+ if ( bKeep && KEEPTAB )
+ {
+
+ // Consider case that table is inside another table,
+ // because it has to be avoided, that superior table
+ // is formatted.
+ // Thus, find next content, table or section
+ // and, if a section is found, get its first
+ // content.
+ if ( nullptr != sw_FormatNextContentForKeep( this ) && !GetNext() )
+ {
+ setFrameAreaPositionValid(false);
+ }
+ }
+ }
+ }
+ }
+
+ //Again an invalid value? - do it again...
+ if ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+ continue;
+
+ // check, if calculation of table frame is ready.
+
+ // Local variable <nDistanceToUpperPrtBottom>
+ // Introduce local variable and init it with the distance from the
+ // table frame bottom to the bottom of the upper printing area.
+ // Note: negative values denotes the situation that table frame doesn't fit in its upper.
+ SwTwips nDistanceToUpperPrtBottom =
+ aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+
+ /// In online layout try to grow upper of table frame, if table frame doesn't fit in its upper.
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ if ( nDistanceToUpperPrtBottom < 0 && bBrowseMode )
+ {
+ if ( GetUpper()->Grow( -nDistanceToUpperPrtBottom ) )
+ {
+ // upper is grown --> recalculate <nDistanceToUpperPrtBottom>
+ nDistanceToUpperPrtBottom = aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+ }
+ }
+
+ // If there is still some space left in the upper, we check if we
+ // can join some rows of the follow.
+ // Setting bLastRowHasToMoveToFollow to true means we want to force
+ // the table to be split! Only skip this if condition once.
+ if( nDistanceToUpperPrtBottom >= 0 && !bLastRowHasToMoveToFollow )
+ {
+ // If there is space left in the upper printing area, join as for trial
+ // at least one further row of an existing follow.
+ if ( !bSplit && GetFollow() )
+ {
+ bool bDummy;
+ if ( GetFollow()->ShouldBwdMoved( GetUpper(), bDummy ) )
+ {
+ SwFrame *pTmp = GetUpper();
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*pTmp);
+ if ( bBrowseMode )
+ nDeadLine += pTmp->Grow( LONG_MAX, true );
+ bool bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) > 0;
+ if (!bFits && aRectFnSet.GetHeight(GetFollow()->getFrameArea()) == 0)
+ // The follow should move backwards, so allow the case
+ // when the upper has no space, but the follow is
+ // empty.
+ bFits = aRectFnSet.BottomDist(getFrameArea(), nDeadLine) >= 0;
+ if (bFits)
+ {
+ // First, we remove an existing follow flow line.
+ if ( HasFollowFlowLine() )
+ {
+ SwFrame* pLastLine = GetLastLower();
+ RemoveFollowFlowLine();
+ // invalidate and rebuild last row
+ if ( pLastLine )
+ {
+ ::SwInvalidateAll( pLastLine, LONG_MAX );
+ SetRebuildLastLine( true );
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(pLastLine), LONG_MAX);
+ SetRebuildLastLine( false );
+ }
+
+ SwFrame* pRow = GetFollow()->GetFirstNonHeadlineRow();
+
+ if ( !pRow || !pRow->GetNext() )
+ // The follow became empty and hence useless
+ Join();
+
+ continue;
+ }
+
+ // If there is no follow flow line, we move the first
+ // row in the follow table to the master table.
+ SwRowFrame *pRow = GetFollow()->GetFirstNonHeadlineRow();
+
+ // The follow became empty and hence useless
+ if ( !pRow )
+ {
+ Join();
+ continue;
+ }
+
+ const SwTwips nOld = aRectFnSet.GetHeight(getFrameArea());
+ tools::Long nRowsToMove = lcl_GetMaximumLayoutRowSpan( *pRow );
+ SwFrame* pRowToMove = pRow;
+
+ while ( pRowToMove && nRowsToMove-- > 0 )
+ {
+ const bool bMoveFootnotes = bFootnotesInDoc && !GetFollow()->IsJoinLocked();
+
+ SwFootnoteBossFrame *pOldBoss = nullptr;
+ if ( bMoveFootnotes )
+ pOldBoss = pRowToMove->FindFootnoteBossFrame( true );
+
+ SwFrame* pNextRow = pRowToMove->GetNext();
+
+ if ( !pNextRow )
+ {
+ // The follow became empty and hence useless
+ Join();
+ }
+ else
+ {
+ pRowToMove->Cut();
+ pRowToMove->Paste( this );
+ }
+
+ // Move the footnotes!
+ if ( bMoveFootnotes )
+ if ( static_cast<SwLayoutFrame*>(pRowToMove)->MoveLowerFootnotes( nullptr, pOldBoss, FindFootnoteBossFrame( true ), true ) )
+ GetUpper()->Calc(pRenderContext);
+
+ pRowToMove = pNextRow;
+ }
+
+ if ( nOld != aRectFnSet.GetHeight(getFrameArea()) )
+ lcl_RecalcTable( *this, static_cast<SwLayoutFrame*>(pRow), aNotify );
+
+ continue;
+ }
+ }
+ }
+ else if ( KEEPTAB )
+ {
+ bool bFormat = false;
+ if ( bKeep )
+ bFormat = true;
+ else if ( bTableRowKeep && !bLastRowMoveNoMoreTries )
+ {
+ // We only want to give the last row one chance to move
+ // to the follow table. Set the flag as early as possible:
+ bLastRowMoveNoMoreTries = true;
+
+ // The last line of the table has to be cut off if:
+ // 1. The table does not want to keep with its next
+ // 2. The compatibility option is set and the table is allowed to split
+ // 3. We did not already cut off the last row
+ // 4. There is not break after attribute set at the table
+ // 5. There is no break before attribute set behind the table
+ // 6. There is no section change behind the table (see IsKeep)
+ // 7. The last table row wants to keep with its next.
+ const SwRowFrame* pLastRow = static_cast<const SwRowFrame*>(GetLastLower());
+ if (pLastRow)
+ {
+ if (!oAccess)
+ {
+ oAccess.emplace(SwFrame::GetCache(), this);
+ pAttrs = oAccess->Get();
+ }
+ if (IsKeep(pAttrs->GetAttrSet().GetKeep(), GetBreakItem(), true)
+ && pLastRow->ShouldRowKeepWithNext())
+ {
+ bFormat = true;
+ }
+ }
+ }
+
+ if ( bFormat )
+ {
+ oAccess.reset();
+
+ // Consider case that table is inside another table, because
+ // it has to be avoided, that superior table is formatted.
+ // Thus, find next content, table or section and, if a section
+ // is found, get its first content.
+ const SwFrame* pTmpNxt = sw_FormatNextContentForKeep( this );
+
+ // The last row wants to keep with the frame behind the table.
+ // Check if the next frame is on a different page and valid.
+ // In this case we do a magic trick:
+ if ( !bKeep && !GetNext() && pTmpNxt && pTmpNxt->isFrameAreaDefinitionValid() )
+ {
+ setFrameAreaPositionValid(false);
+ bLastRowHasToMoveToFollow = true;
+ }
+ }
+ }
+
+ if ( isFrameAreaDefinitionValid() )
+ {
+ if (m_bCalcLowers)
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ }
+ else if (m_bONECalcLowers)
+ {
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
+ m_bONECalcLowers = false;
+ }
+ }
+ continue;
+ }
+
+ // I don't fit in the upper Frame anymore, therefore it's the
+ // right moment to do some preferably constructive changes.
+
+ // If I'm NOT allowed to leave the upper Frame, I've got a problem.
+ // Following Arthur Dent, we do the only thing that you can do with
+ // an unsolvable problem: We ignore it with all our power.
+ if ( !bMoveable )
+ {
+ if (m_bCalcLowers && isFrameAreaDefinitionValid())
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ }
+ else if (m_bONECalcLowers)
+ {
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
+ m_bONECalcLowers = false;
+ }
+
+ // It does not make sense to cut off the last line if we are
+ // not moveable:
+ bLastRowHasToMoveToFollow = false;
+
+ continue;
+ }
+
+ if (m_bCalcLowers && isFrameAreaDefinitionValid())
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ if( !isFrameAreaDefinitionValid() )
+ continue;
+ }
+
+ // First try to split the table. Condition:
+ // 1. We have at least one non headline row
+ // 2. If this row wants to keep, we need an additional row
+ // 3. The table is allowed to split or we do not have a pIndPrev:
+ SwFrame* pIndPrev = GetIndPrev();
+ const SwRowFrame* pFirstNonHeadlineRow = GetFirstNonHeadlineRow();
+ // #i120016# if this row wants to keep, allow split in case that all rows want to keep with next,
+ // the table can not move forward as it is the first one and a split is in general allowed.
+ const bool bAllowSplitOfRow = bTableRowKeep && !pIndPrev && AreAllRowsKeepWithNext(pFirstNonHeadlineRow);
+ // tdf91083 MSCompat: this extends bAllowSplitOfRow (and perhaps should just replace it).
+ // If the kept-together items cannot move to a new page, a table split is in general allowed.
+ const bool bEmulateTableKeepSplitAllowed = bEmulateTableKeep && !IsKeepFwdMoveAllowed(/*IgnoreMyOwnKeepValue=*/true);
+
+ if ( pFirstNonHeadlineRow && nUnSplitted > 0 &&
+ ( bEmulateTableKeepSplitAllowed || bAllowSplitOfRow ||
+ ( ( !bTableRowKeep || pFirstNonHeadlineRow->GetNext() ||
+ !pFirstNonHeadlineRow->ShouldRowKeepWithNext()
+ ) && ( !bDontSplit || !pIndPrev )
+ ) ) )
+ {
+ // #i29438#
+ // Special DoNotSplit cases:
+ // We better avoid splitting of a row frame if we are inside a columned
+ // section which has a height of 0, because this is not growable and thus
+ // all kinds of unexpected things could happen.
+ if ( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() &&
+ 0 == aRectFnSet.GetHeight(GetUpper()->getFrameArea())
+ )
+ {
+ bTryToSplit = false;
+ }
+
+ // 1. Try: bTryToSplit = true => Try to split the row.
+ // 2. Try: bTryToSplit = false => Split the table between the rows.
+ if ( pFirstNonHeadlineRow->GetNext() || bTryToSplit )
+ {
+ SwTwips nDeadLine = aRectFnSet.GetPrtBottom(*GetUpper());
+ if( IsInSct() || GetUpper()->IsInTab() ) // TABLE IN TABLE)
+ nDeadLine = aRectFnSet.YInc( nDeadLine,
+ GetUpper()->Grow( LONG_MAX, true ) );
+
+ {
+ SwFrameDeleteGuard g(Lower()); // tdf#134965 prevent RemoveFollowFlowLine()
+ SetInRecalcLowerRow( true );
+ ::lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), nDeadLine);
+ SetInRecalcLowerRow( false );
+ }
+ m_bLowersFormatted = true;
+ aNotify.SetLowersComplete( true );
+
+ // One more check if it's really necessary to split the table.
+ // 1. The table either has to exceed the deadline or
+ // 2. We explicitly want to cut off the last row.
+ if( aRectFnSet.BottomDist( getFrameArea(), nDeadLine ) > 0 && !bLastRowHasToMoveToFollow )
+ {
+ continue;
+ }
+
+ // Set to false again as early as possible.
+ bLastRowHasToMoveToFollow = false;
+
+ // #i52781#
+ // YaSC - Yet another special case:
+ // If our upper is inside a table cell which is not allowed
+ // to split, we do not try to split:
+ if ( GetUpper()->IsInTab() )
+ {
+ const SwFrame* pTmpRow = GetUpper();
+ while ( pTmpRow && !pTmpRow->IsRowFrame() )
+ pTmpRow = pTmpRow->GetUpper();
+ if ( pTmpRow && !static_cast<const SwRowFrame*>(pTmpRow)->IsRowSplitAllowed() )
+ continue;
+ }
+
+ sal_uInt16 nMinNumOfLines = nRepeat;
+
+ if ( bTableRowKeep )
+ {
+ const SwRowFrame* pTmpRow = GetFirstNonHeadlineRow();
+ while ( pTmpRow && pTmpRow->ShouldRowKeepWithNext() )
+ {
+ ++nMinNumOfLines;
+ pTmpRow = static_cast<const SwRowFrame*>(pTmpRow->GetNext());
+ }
+ }
+
+ if ( !bTryToSplit )
+ ++nMinNumOfLines;
+
+ const SwTwips nBreakLine = aRectFnSet.YInc(
+ aRectFnSet.GetTop(getFrameArea()),
+ aRectFnSet.GetTopMargin(*this) +
+ lcl_GetHeightOfRows( GetLower(), nMinNumOfLines ) );
+
+ // Some more checks if we want to call the split algorithm or not:
+ // The repeating lines / keeping lines still fit into the upper or
+ // if we do not have an (in)direct Prev, we split anyway.
+ if( aRectFnSet.YDiff(nDeadLine, nBreakLine) >=0
+ || !pIndPrev || bEmulateTableKeepSplitAllowed )
+ {
+ aNotify.SetLowersComplete( false );
+ bSplit = true;
+
+ // An existing follow flow line has to be removed.
+ if ( HasFollowFlowLine() )
+ {
+ if (!nThrowAwayValidLayoutLimit)
+ continue;
+ const bool bInitialLoopEndCondition(isFrameAreaDefinitionValid());
+ RemoveFollowFlowLine();
+ const bool bFinalLoopEndCondition(isFrameAreaDefinitionValid());
+
+ if (bInitialLoopEndCondition && !bFinalLoopEndCondition)
+ {
+ --nThrowAwayValidLayoutLimit;
+ }
+ }
+
+ oAccess.reset();
+ const bool bSplitError = !Split( nDeadLine, bTryToSplit, ( bTableRowKeep && !(bAllowSplitOfRow || bEmulateTableKeepSplitAllowed) ) );
+
+ // tdf#130639 don't start table on a new page after the fallback "switch off repeating header"
+ if (bSplitError && nRepeat > GetTable()->GetRowsToRepeat())
+ {
+ setFrameAreaPositionValid(false);
+ break;
+ }
+
+ if (!bTryToSplit && !bSplitError)
+ {
+ --nUnSplitted;
+ }
+
+ // #i29771# Two tries to split the table
+ // If an error occurred during splitting. We start a second
+ // try, this time without splitting of table rows.
+ if ( bSplitError && HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+
+ // If splitting the table was successful or not,
+ // we do not want to have 'empty' follow tables.
+ if ( GetFollow() && !GetFollow()->GetFirstNonHeadlineRow() )
+ Join();
+
+ // We want to restore the situation before the failed
+ // split operation as good as possible. Therefore we
+ // do some more calculations. Note: Restricting this
+ // to nDeadLine may not be enough.
+ if ( bSplitError && bTryToSplit ) // no restart if we did not try to split: i72847, i79426
+ {
+ lcl_RecalcRow(*static_cast<SwRowFrame*>(Lower()), LONG_MAX);
+ setFrameAreaPositionValid(false);
+ bTryToSplit = false;
+ continue;
+ }
+
+ bTryToSplit = !bSplitError;
+
+ //To avoid oscillations the Follow must become valid now
+ if ( GetFollow() )
+ {
+ // #i80924#
+ // After a successful split assure that the first row
+ // is invalid. When graphics are present, this isn't hold.
+ // Note: defect i80924 could also be fixed, if it is
+ // assured, that <SwLayNotify::bLowersComplete> is only
+ // set, if all lower are valid *and* are correct laid out.
+ if ( !bSplitError && GetFollow()->GetLower() )
+ {
+ GetFollow()->GetLower()->InvalidatePos();
+ }
+ SwRectFnSet fnRectX(GetFollow());
+
+ static sal_uInt8 nStack = 0;
+ if ( !StackHack::IsLocked() && nStack < 4 )
+ {
+ ++nStack;
+ StackHack aHack;
+ oAccess.reset();
+
+ GetFollow()->MakeAll(pRenderContext);
+
+ GetFollow()->SetLowersFormatted(false);
+ // #i43913# - lock follow table
+ // to avoid its formatting during the format of
+ // its content.
+ const bool bOldJoinLock = GetFollow()->IsJoinLocked();
+ GetFollow()->LockJoin();
+ ::lcl_RecalcRow(*static_cast<SwRowFrame*>(GetFollow()->Lower()),
+ fnRectX.GetBottom(GetFollow()->GetUpper()->getFrameArea()) );
+ // #i43913#
+ // #i63632# Do not unlock the
+ // follow if it wasn't locked before.
+ if ( !bOldJoinLock )
+ GetFollow()->UnlockJoin();
+
+ if ( !GetFollow()->GetFollow() )
+ {
+ SwFrame* pNxt = static_cast<SwFrame*>(GetFollow())->FindNext();
+ if ( pNxt )
+ {
+ // #i18103# - no formatting of found next
+ // frame, if it's a follow section of the
+ // 'ColLocked' section, the follow table is
+ // in.
+ bool bCalcNxt = true;
+ if ( GetFollow()->IsInSct() && pNxt->IsSctFrame() )
+ {
+ SwSectionFrame* pSct = GetFollow()->FindSctFrame();
+ if ( pSct->IsColLocked() &&
+ pSct->GetFollow() == pNxt )
+ {
+ bCalcNxt = false;
+ }
+ }
+ if ( bCalcNxt )
+ {
+ // tdf#119109 follow was just formatted,
+ // don't do it again now
+ FlowFrameJoinLockGuard g(GetFollow());
+ pNxt->Calc(pRenderContext);
+ }
+ }
+ }
+
+ --nStack;
+ }
+ else if ( GetFollow() == GetNext() )
+ GetFollow()->MoveFwd( true, false );
+ }
+ continue;
+ }
+ }
+ }
+
+ // Set to false again as early as possible.
+ bLastRowHasToMoveToFollow = false;
+
+ if( IsInSct() && bMovedFwd && bMakePage && GetUpper()->IsColBodyFrame() &&
+ GetUpper()->GetUpper()->GetUpper()->IsSctFrame() &&
+ ( GetUpper()->GetUpper()->GetPrev() || GetIndPrev() ) &&
+ static_cast<SwSectionFrame*>(GetUpper()->GetUpper()->GetUpper())->MoveAllowed(this) )
+ {
+ bMovedFwd = false;
+ }
+
+ // #i29771# Reset bTryToSplit flag on change of upper
+ const SwFrame* pOldUpper = GetUpper();
+
+ //Let's see if we find some place anywhere...
+ if (!bMovedFwd)
+ {
+ // don't make the effort to move fwd if its known
+ // conditions that are known not to work
+ if (IsInFootnote() && ForbiddenForFootnoteCntFwd())
+ bMakePage = false;
+ else if (!MoveFwd(bMakePage, false))
+ bMakePage = false;
+ }
+
+ // #i29771# Reset bSplitError flag on change of upper
+ if ( GetUpper() != pOldUpper )
+ {
+ bTryToSplit = true;
+ nUnSplitted = 5;
+ }
+
+ aRectFnSet.Refresh(this);
+ m_bCalcLowers = true;
+ bMovedFwd = true;
+ aNotify.SetLowersComplete( false );
+ if ( IsFollow() )
+ {
+ // To avoid oscillations, master should not remain invalid
+ SwTabFrame *pTab = FindMaster();
+ if ( pTab->GetUpper() )
+ pTab->GetUpper()->Calc(pRenderContext);
+ pTab->Calc(pRenderContext);
+ pTab->SetLowersFormatted( false );
+ }
+
+ //If my neighbour is my Follow at the same time, I'll swallow it up.
+ if ( ( GetNext() && GetNext() == GetFollow() ) || !GetLower() )
+ {
+ if ( HasFollowFlowLine() )
+ RemoveFollowFlowLine();
+ if ( GetFollow() )
+ Join();
+ }
+
+ if ( bMovedBwd && GetUpper() )
+ {
+ //During flowing back the upper was animated to do a full repaint,
+ //we can now skip this after the whole flowing back and forth.
+ GetUpper()->ResetCompletePaint();
+ }
+
+ if (m_bCalcLowers && isFrameAreaDefinitionValid())
+ {
+ // #i44910# - format of lower frames unnecessary
+ // and can cause layout loops, if table doesn't fit and isn't
+ // allowed to split.
+ SwTwips nDistToUpperPrtBottom =
+ aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+ if ( nDistToUpperPrtBottom >= 0 || bTryToSplit )
+ {
+ lcl_RecalcTable( *this, nullptr, aNotify );
+ m_bLowersFormatted = true;
+ m_bCalcLowers = false;
+ if (!isFramePrintAreaValid())
+ m_pTable->SetRowsToRepeat(1);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "debug assertion: <SwTabFrame::MakeAll()> - format of table lowers suppressed by fix i44910" );
+ }
+#endif
+ }
+
+ } //while ( !isFrameAreaPositionValid() || !isFrameAreaSizeValid() || !isFramePrintAreaValid() )
+
+ //If my direct predecessor is my master now, it can destroy me during the
+ //next best opportunity.
+ if ( IsFollow() )
+ {
+ SwFrame *pPre = GetPrev();
+ if ( pPre && pPre->IsTabFrame() && static_cast<SwTabFrame*>(pPre)->GetFollow() == this)
+ pPre->InvalidatePos();
+ }
+
+ m_bCalcLowers = m_bONECalcLowers = false;
+ oAccess.reset();
+ UnlockJoin();
+ if ( bMovedFwd || bMovedBwd || !bOldValidPos )
+ aNotify.SetInvaKeep();
+}
+
+static bool IsNextOnSamePage(SwPageFrame const& rPage,
+ SwTabFrame const& rTabFrame, SwTextFrame const& rAnchorFrame)
+{
+ for (SwContentFrame const* pContentFrame = rTabFrame.FindNextCnt();
+ pContentFrame && pContentFrame->FindPageFrame() == &rPage;
+ pContentFrame = pContentFrame->FindNextCnt())
+ {
+ if (pContentFrame == &rAnchorFrame)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/// Calculate the offsets arising because of FlyFrames
+bool SwTabFrame::CalcFlyOffsets( SwTwips& rUpper,
+ tools::Long& rLeftOffset,
+ tools::Long& rRightOffset,
+ SwTwips *const pSpaceBelowBottom) const
+{
+ bool bInvalidatePrtArea = false;
+ const SwPageFrame *pPage = FindPageFrame();
+ const SwFlyFrame* pMyFly = FindFlyFrame();
+
+ // --> #108724# Page header/footer content doesn't have to wrap around
+ // floating screen objects
+
+ const IDocumentSettingAccess& rIDSA = GetFormat()->getIDocumentSettingAccess();
+ const bool bWrapAllowed = rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
+ ( !IsInFootnote() && nullptr == FindFooterOrHeader() );
+
+ if ( pPage->GetSortedObjs() && bWrapAllowed )
+ {
+ SwRectFnSet aRectFnSet(this);
+ const bool bConsiderWrapOnObjPos = rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION);
+ tools::Long nPrtPos = aRectFnSet.GetTop(getFrameArea());
+ nPrtPos = aRectFnSet.YInc( nPrtPos, rUpper );
+ SwRect aRect( getFrameArea() );
+ if (pSpaceBelowBottom)
+ { // set to space below table frame
+ aRectFnSet.SetTopAndHeight(aRect, aRectFnSet.GetBottom(aRect), *pSpaceBelowBottom);
+ }
+ else
+ {
+ tools::Long nYDiff = aRectFnSet.YDiff( aRectFnSet.GetTop(getFramePrintArea()), rUpper );
+ if (nYDiff > 0)
+ aRectFnSet.AddBottom( aRect, -nYDiff );
+ }
+
+ bool bAddVerticalFlyOffsets = rIDSA.get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
+
+ for ( size_t i = 0; i < pPage->GetSortedObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pPage->GetSortedObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ const SwRect aFlyRect = pFly->GetObjRectWithSpaces();
+ // #i26945# - correction of conditions,
+ // if Writer fly frame has to be considered:
+ // - no need to check, if top of Writer fly frame differs
+ // from FAR_AWAY, because it's also checked, if the Writer
+ // fly frame rectangle overlaps with <aRect>
+ // - no check, if bottom of anchor frame is prior the top of
+ // the table, because Writer fly frames can be negative positioned.
+ // - correct check, if the Writer fly frame is a lower of the
+ // table, because table lines/rows can split and an at-character
+ // anchored Writer fly frame could be positioned in the follow
+ // flow line.
+ // - add condition, that an existing anchor character text frame
+ // has to be on the same page as the table.
+ // E.g., it could happen, that the fly frame is still registered
+ // at the page frame, the table is on, but it's anchor character
+ // text frame has already changed its page.
+ const SwTextFrame* pAnchorCharFrame = pFly->FindAnchorCharFrame();
+ bool bConsiderFly =
+ // #i46807# - do not consider invalid
+ // Writer fly frames.
+ (pFly->isFrameAreaDefinitionValid() || bAddVerticalFlyOffsets) &&
+ // fly anchored at character or at paragraph
+ pFly->IsFlyAtContentFrame() &&
+ // fly overlaps with corresponding table rectangle
+ aFlyRect.Overlaps( aRect ) &&
+ // fly isn't lower of table and
+ // anchor character frame of fly isn't lower of table
+ (pSpaceBelowBottom // not if in ShouldBwdMoved
+ || (!IsAnLower( pFly ) &&
+ (!pAnchorCharFrame || !IsAnLower(pAnchorCharFrame)))) &&
+ // table isn't lower of fly
+ !pFly->IsAnLower( this ) &&
+ // fly is lower of fly, the table is in
+ // #123274# - correction
+ // assure that fly isn't a lower of a fly, the table isn't in.
+ // E.g., a table in the body doesn't wrap around a graphic,
+ // which is inside a frame.
+ ( ( !pMyFly ||
+ pMyFly->IsAnLower( pFly ) ) &&
+ pMyFly == pFly->GetAnchorFrameContainingAnchPos()->FindFlyFrame() ) &&
+ // anchor frame not on following page
+ pPage->GetPhyPageNum() >=
+ pFly->GetAnchorFrame()->FindPageFrame()->GetPhyPageNum() &&
+ // anchor character text frame on same page
+ ( !pAnchorCharFrame ||
+ pAnchorCharFrame->FindPageFrame()->GetPhyPageNum() ==
+ pPage->GetPhyPageNum() );
+
+ if ( bConsiderFly )
+ {
+ const SwFrame* pFlyHeaderFooterFrame = pFly->GetAnchorFrame()->FindFooterOrHeader();
+ const SwFrame* pThisHeaderFooterFrame = FindFooterOrHeader();
+
+ if ( pFlyHeaderFooterFrame != pThisHeaderFooterFrame &&
+ // #148493# If bConsiderWrapOnObjPos is set,
+ // we want to consider the fly if it is located in the header and
+ // the table is located in the body:
+ ( !bConsiderWrapOnObjPos || nullptr != pThisHeaderFooterFrame || !pFlyHeaderFooterFrame->IsHeaderFrame() ) )
+ bConsiderFly = false;
+ }
+
+ if ( bConsiderFly )
+ {
+ const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
+ const SwFormatHoriOrient &rHori= pFly->GetFormat()->GetHoriOrient();
+ bool bShiftDown = css::text::WrapTextMode_NONE == rSur.GetSurround();
+ if (!bShiftDown && bAddVerticalFlyOffsets)
+ {
+ if (rSur.GetSurround() == text::WrapTextMode_PARALLEL
+ && rHori.GetHoriOrient() == text::HoriOrientation::NONE)
+ {
+ // We know that wrapping was requested and the table frame overlaps with
+ // the fly frame. Check if the print area overlaps with the fly frame as
+ // well (in case the table does not use all the available width).
+ basegfx::B1DRange aTabRange(
+ aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea()),
+ aRectFnSet.GetLeft(aRect) + aRectFnSet.GetLeft(getFramePrintArea())
+ + aRectFnSet.GetWidth(getFramePrintArea()));
+
+ // Ignore spacing when determining the left/right edge of the fly, like
+ // Word does.
+ const SwRect aFlyRectWithoutSpaces = pFly->GetObjRect();
+ basegfx::B1DRange aFlyRange(aRectFnSet.GetLeft(aFlyRectWithoutSpaces),
+ aRectFnSet.GetRight(aFlyRectWithoutSpaces));
+
+ // If it does, shift the table down. Do this only in the compat case,
+ // normally an SwFlyPortion is created instead that increases the height
+ // of the first table row.
+ bShiftDown = aTabRange.overlaps(aFlyRange);
+ }
+ }
+
+ if (bShiftDown)
+ {
+ // possible cases:
+ // both in body
+ // both in same fly
+ // any comb. of body, footnote, header/footer
+ // to keep it safe, check only in doc body vs page margin for now
+ tools::Long nBottom = aRectFnSet.GetBottom(aFlyRect);
+ // tdf#138039 don't grow beyond the page body
+ // if the fly is anchored below the table; the fly
+ // must move with its anchor frame to the next page
+ SwRectFnSet fnPage(pPage);
+ if (!IsInDocBody() // TODO
+ || fnPage.YDiff(fnPage.GetBottom(aFlyRect), fnPage.GetPrtBottom(*pPage)) <= 0
+ || !IsNextOnSamePage(*pPage, *this,
+ *static_cast<SwTextFrame*>(pFly->GetAnchorFrameContainingAnchPos())))
+ {
+ if (aRectFnSet.YDiff( nPrtPos, nBottom ) < 0)
+ nPrtPos = nBottom;
+ // tdf#116501 subtract flys blocking space from below
+ // TODO this may not work ideally for multiple flys
+ if (pSpaceBelowBottom
+ && aRectFnSet.YDiff(aRectFnSet.GetBottom(aRect), nBottom) < 0)
+ {
+ if (aRectFnSet.YDiff(aRectFnSet.GetTop(aRect), aRectFnSet.GetTop(aFlyRect)) < 0)
+ {
+ aRectFnSet.SetBottom(aRect, aRectFnSet.GetTop(aFlyRect));
+ }
+ else
+ {
+ aRectFnSet.SetHeight(aRect, 0);
+ }
+ }
+ bInvalidatePrtArea = true;
+ }
+ }
+ if ( (css::text::WrapTextMode_RIGHT == rSur.GetSurround() ||
+ css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
+ text::HoriOrientation::LEFT == rHori.GetHoriOrient() )
+ {
+ const tools::Long nWidth = aRectFnSet.XDiff(
+ aRectFnSet.GetRight(aFlyRect),
+ aRectFnSet.GetLeft(pFly->GetAnchorFrame()->getFrameArea()) );
+ rLeftOffset = std::max( rLeftOffset, nWidth );
+ bInvalidatePrtArea = true;
+ }
+ if ( (css::text::WrapTextMode_LEFT == rSur.GetSurround() ||
+ css::text::WrapTextMode_PARALLEL == rSur.GetSurround())&&
+ text::HoriOrientation::RIGHT == rHori.GetHoriOrient() )
+ {
+ const tools::Long nWidth = aRectFnSet.XDiff(
+ aRectFnSet.GetRight(pFly->GetAnchorFrame()->getFrameArea()),
+ aRectFnSet.GetLeft(aFlyRect) );
+ rRightOffset = std::max( rRightOffset, nWidth );
+ bInvalidatePrtArea = true;
+ }
+ }
+ }
+ }
+ rUpper = aRectFnSet.YDiff( nPrtPos, aRectFnSet.GetTop(getFrameArea()) );
+ if (pSpaceBelowBottom)
+ {
+ *pSpaceBelowBottom = aRectFnSet.GetHeight(aRect);
+ }
+ }
+
+ return bInvalidatePrtArea;
+}
+
+/// "Formats" the frame; Frame and PrtArea.
+/// The fixed size is not adjusted here.
+void SwTabFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "TabFrame::Format, pAttrs is 0." );
+
+ SwRectFnSet aRectFnSet(this);
+ if ( !isFrameAreaSizeValid() )
+ {
+ tools::Long nDiff = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea()) -
+ aRectFnSet.GetWidth(getFrameArea());
+ if( nDiff )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddRight( aFrm, nDiff );
+ }
+ }
+
+ //VarSize is always the height.
+ //For the upper/lower margins the same rules apply as for ContentFrames (see
+ //MakePrtArea() of those).
+
+ SwTwips nUpper = CalcUpperSpace( pAttrs );
+
+ // We want to dodge the flys. Two possibilities:
+ // 1. There are flys with SurroundNone, dodge them completely
+ // 2. There are flys which only wrap on the right or the left side and
+ // those are right or left aligned, those set the minimum for the margins
+ tools::Long nTmpRight = -1000000,
+ nLeftOffset = 0;
+ if (CalcFlyOffsets(nUpper, nLeftOffset, nTmpRight, nullptr))
+ {
+ setFramePrintAreaValid(false);
+ }
+
+ tools::Long nRightOffset = std::max( tools::Long(0), nTmpRight );
+
+ SwTwips nLower = pAttrs->CalcBottomLine();
+ // #i29550#
+ if ( IsCollapsingBorders() )
+ nLower += GetBottomLineSize();
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+
+ // The width of the PrintArea is given by the FrameFormat, the margins
+ // have to be set accordingly.
+ // Minimum margins are determined depending on borders and shadows.
+ // The margins are set so that the PrintArea is aligned into the
+ // Frame according to the adjustment.
+ // If the adjustment is 0, the margins are set according to the border
+ // attributes.
+
+ const SwTwips nOldHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ const SwTwips nMax = aRectFnSet.GetWidth(getFrameArea());
+
+ // OD 14.03.2003 #i9040# - adjust variable names.
+ const SwTwips nLeftLine = pAttrs->CalcLeftLine();
+ const SwTwips nRightLine = pAttrs->CalcRightLine();
+
+ // The width possibly is a percentage value. If the table is inside
+ // something else, the value refers to the environment. If it's in the
+ // body then in the BrowseView the value refers to the screen width.
+ const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
+ // OD 14.03.2003 #i9040# - adjust variable name.
+ const SwTwips nWishedTableWidth = CalcRel( rSz );
+
+ bool bCheckBrowseWidth = false;
+
+ // OD 14.03.2003 #i9040# - insert new variables for left/right spacing.
+ SwTwips nLeftSpacing = 0;
+ SwTwips nRightSpacing = 0;
+ switch ( GetFormat()->GetHoriOrient().GetHoriOrient() )
+ {
+ case text::HoriOrientation::LEFT:
+ {
+ // left indent:
+ nLeftSpacing = nLeftLine + nLeftOffset;
+ // OD 06.03.2003 #i9040# - correct calculation of right indent:
+ // - Consider right indent given by right line attributes.
+ // - Consider negative right indent.
+ // wished right indent determined by wished table width and
+ // left offset given by surround fly frames on the left:
+ const SwTwips nWishRight = nMax - nWishedTableWidth - nLeftOffset;
+ if ( nRightOffset > 0 )
+ {
+ // surrounding fly frames on the right
+ // -> right indent is maximum of given right offset
+ // and wished right offset.
+ nRightSpacing = nRightLine + std::max( SwTwips(nRightOffset), nWishRight );
+ }
+ else
+ {
+ // no surrounding fly frames on the right
+ // If intrinsic right indent (intrinsic means not considering
+ // determined left indent) is negative,
+ // then hold this intrinsic indent,
+ // otherwise non negative wished right indent is hold.
+ nRightSpacing = nRightLine +
+ ( ( (nWishRight+nLeftOffset) < 0 ) ?
+ (nWishRight+nLeftOffset) :
+ std::max( SwTwips(0), nWishRight ) );
+ }
+ }
+ break;
+ case text::HoriOrientation::RIGHT:
+ {
+ // right indent:
+ nRightSpacing = nRightLine + nRightOffset;
+ // OD 06.03.2003 #i9040# - correct calculation of left indent:
+ // - Consider left indent given by left line attributes.
+ // - Consider negative left indent.
+ // wished left indent determined by wished table width and
+ // right offset given by surrounding fly frames on the right:
+ const SwTwips nWishLeft = nMax - nWishedTableWidth - nRightOffset;
+ if ( nLeftOffset > 0 )
+ {
+ // surrounding fly frames on the left
+ // -> right indent is maximum of given left offset
+ // and wished left offset.
+ nLeftSpacing = nLeftLine + std::max( SwTwips(nLeftOffset), nWishLeft );
+ }
+ else
+ {
+ // no surrounding fly frames on the left
+ // If intrinsic left indent (intrinsic = not considering
+ // determined right indent) is negative,
+ // then hold this intrinsic indent,
+ // otherwise non negative wished left indent is hold.
+ nLeftSpacing = nLeftLine +
+ ( ( (nWishLeft+nRightOffset) < 0 ) ?
+ (nWishLeft+nRightOffset) :
+ std::max( SwTwips(0), nWishLeft ) );
+ }
+ }
+ break;
+ case text::HoriOrientation::CENTER:
+ {
+ // OD 07.03.2003 #i9040# - consider left/right line attribute.
+ const SwTwips nCenterSpacing = ( nMax - nWishedTableWidth ) / 2;
+ nLeftSpacing = nLeftLine +
+ ( (nLeftOffset > 0) ?
+ std::max( nCenterSpacing, SwTwips(nLeftOffset) ) :
+ nCenterSpacing );
+ nRightSpacing = nRightLine +
+ ( (nRightOffset > 0) ?
+ std::max( nCenterSpacing, SwTwips(nRightOffset) ) :
+ nCenterSpacing );
+ }
+ break;
+ case text::HoriOrientation::FULL:
+ //This things grows over the whole width.
+ //Only the free space needed for the border is taken into
+ //account. The attribute values of LRSpace are ignored
+ //intentionally.
+ bCheckBrowseWidth = true;
+ nLeftSpacing = nLeftLine + nLeftOffset;
+ nRightSpacing = nRightLine + nRightOffset;
+ break;
+ case text::HoriOrientation::NONE:
+ {
+ // The margins are defined by the LRSpace attribute.
+ nLeftSpacing = pAttrs->CalcLeft( this );
+ if( nLeftOffset )
+ {
+ // OD 07.03.2003 #i9040# - surround fly frames only, if
+ // they overlap with the table.
+ // Thus, take maximum of left spacing and left offset.
+ // OD 10.03.2003 #i9040# - consider left line attribute.
+ nLeftSpacing = std::max( nLeftSpacing, SwTwips( nLeftOffset + nLeftLine ) );
+ }
+ // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
+ nRightSpacing = pAttrs->CalcRight( this );
+ if( nRightOffset )
+ {
+ // OD 07.03.2003 #i9040# - surround fly frames only, if
+ // they overlap with the table.
+ // Thus, take maximum of right spacing and right offset.
+ // OD 10.03.2003 #i9040# - consider right line attribute.
+ nRightSpacing = std::max( nRightSpacing, SwTwips( nRightOffset + nRightLine ) );
+ }
+ }
+ break;
+ case text::HoriOrientation::LEFT_AND_WIDTH:
+ {
+ // count left border and width (Word specialty)
+ // OD 10.03.2003 #i9040# - no width alignment in online mode.
+ //bCheckBrowseWidth = true;
+ nLeftSpacing = pAttrs->CalcLeft( this );
+ if( nLeftOffset )
+ {
+ // OD 10.03.2003 #i9040# - surround fly frames only, if
+ // they overlap with the table.
+ // Thus, take maximum of right spacing and right offset.
+ // OD 10.03.2003 #i9040# - consider left line attribute.
+ nLeftSpacing = std::max( nLeftSpacing, SwTwips( pAttrs->CalcLeftLine() + nLeftOffset ) );
+ }
+ // OD 10.03.2003 #i9040# - consider right and left line attribute.
+ const SwTwips nWishRight =
+ nMax - (nLeftSpacing-pAttrs->CalcLeftLine()) - nWishedTableWidth;
+ nRightSpacing = nRightLine +
+ ( (nRightOffset > 0) ?
+ std::max( nWishRight, SwTwips(nRightOffset) ) :
+ nWishRight );
+ }
+ break;
+ default:
+ OSL_FAIL( "Invalid orientation for table." );
+ }
+
+ // #i26250# - extend bottom printing area, if table
+ // is last content inside a table cell.
+ if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) &&
+ GetUpper()->IsInTab() && !GetIndNext() )
+ {
+ nLower += pAttrs->GetULSpace().GetLower();
+ }
+ aRectFnSet.SetYMargins( *this, nUpper, nLower );
+ if( (nMax - MINLAY) < (nLeftSpacing + nRightSpacing) )
+ aRectFnSet.SetXMargins( *this, 0, 0 );
+ else
+ aRectFnSet.SetXMargins( *this, nLeftSpacing, nRightSpacing );
+
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( bCheckBrowseWidth &&
+ pSh && pSh->GetViewOptions()->getBrowseMode() &&
+ GetUpper()->IsPageBodyFrame() && // only PageBodyFrames and not ColBodyFrames
+ pSh->VisArea().Width() )
+ {
+ //Don't go beyond the edge of the visible area.
+ //The page width can be bigger because objects with
+ //"over-size" are possible (RootFrame::ImplCalcBrowseWidth())
+ tools::Long nWidth = pSh->GetBrowseWidth();
+ nWidth -= getFramePrintArea().Left();
+ nWidth -= pAttrs->CalcRightLine();
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( std::min( nWidth, aPrt.Width() ) );
+ }
+
+ if ( nOldHeight != aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ setFrameAreaSizeValid(false);
+ }
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ setFrameAreaSizeValid(true);
+
+ // The size is defined by the content plus the margins.
+ SwTwips nRemaining = 0, nDiff;
+ SwFrame *pFrame = m_pLower;
+ while ( pFrame )
+ {
+ nRemaining += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+ // And now add the margins
+ nRemaining += nUpper + nLower;
+
+ nDiff = aRectFnSet.GetHeight(getFrameArea()) - nRemaining;
+ if ( nDiff > 0 )
+ Shrink( nDiff );
+ else if ( nDiff < 0 )
+ Grow( -nDiff );
+}
+
+SwTwips SwTabFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nHeight > 0 && nDist > ( LONG_MAX - nHeight ) )
+ nDist = LONG_MAX - nHeight;
+
+ if ( bTst && !IsRestrictTableGrowth() )
+ return nDist;
+
+ if ( GetUpper() )
+ {
+ //The upper only grows as far as needed. nReal provides the distance
+ //which is already available.
+ SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
+ SwFrame *pFrame = GetUpper()->Lower();
+ while ( pFrame && GetFollow() != pFrame )
+ {
+ nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+
+ if ( nReal < nDist )
+ {
+ tools::Long nTmp = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0), bTst, bInfo );
+
+ if ( IsRestrictTableGrowth() )
+ {
+ nTmp = std::min( tools::Long(nDist), nReal + nTmp );
+ nDist = nTmp < 0 ? 0 : nTmp;
+ }
+ }
+
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nDist );
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ SwRect aOldFrame( getFrameArea() );
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
+ }
+#endif
+ }
+ }
+
+ if ( !bTst && ( nDist || IsRestrictTableGrowth() ) )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ }
+ // #i28701# - Due to the new object positioning the
+ // frame on the next page/column can flow backward (e.g. it was moved
+ // forward due to the positioning of its objects ). Thus, invalivate this
+ // next frame, if document compatibility option 'Consider wrapping style
+ // influence on object positioning' is ON.
+ else if ( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ InvalidateAll_();
+ InvalidatePage( pPage );
+ SetComplete();
+
+ std::unique_ptr<SvxBrushItem> aBack = GetFormat()->makeBackgroundBrushItem();
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
+ SetCompletePaint();
+ }
+
+ return nDist;
+}
+
+void SwTabFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwTabFrameInvFlags eInvFlags = SwTabFrameInvFlags::NONE;
+ bool bAttrSetChg = pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which();
+
+ if(bAttrSetChg)
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwLayoutFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwTabFrameInvFlags::NONE)
+ return;
+
+ SwPageFrame* pPage = FindPageFrame();
+ InvalidatePage(pPage);
+ if(eInvFlags & SwTabFrameInvFlags::InvalidatePrt)
+ InvalidatePrt_();
+ if(eInvFlags & SwTabFrameInvFlags::InvalidatePos)
+ InvalidatePos_();
+ SwFrame* pTmp = GetIndNext();
+ if(nullptr != pTmp)
+ {
+ if(eInvFlags & SwTabFrameInvFlags::InvalidateIndNextPrt)
+ {
+ pTmp->InvalidatePrt_();
+ if(pTmp->IsContentFrame())
+ pTmp->InvalidatePage(pPage);
+ }
+ if(eInvFlags & SwTabFrameInvFlags::SetIndNextCompletePaint)
+ pTmp->SetCompletePaint();
+ }
+ if(eInvFlags & SwTabFrameInvFlags::InvalidatePrevPrt && nullptr != (pTmp = GetPrev()))
+ {
+ pTmp->InvalidatePrt_();
+ if(pTmp->IsContentFrame())
+ pTmp->InvalidatePage( pPage );
+ }
+ if(eInvFlags & SwTabFrameInvFlags::InvalidateBrowseWidth)
+ {
+ if(pPage && pPage->GetUpper() && !IsFollow())
+ static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
+ }
+ if(eInvFlags & SwTabFrameInvFlags::InvalidateNextPos)
+ InvalidateNextPos();
+}
+
+void SwTabFrame::UpdateAttr_( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwTabFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ const sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ {
+ case RES_TBLHEADLINECHG:
+ if ( IsFollow() )
+ {
+ // Delete remaining headlines:
+ SwRowFrame* pLowerRow = nullptr;
+ while ( nullptr != ( pLowerRow = static_cast<SwRowFrame*>(Lower()) ) && pLowerRow->IsRepeatedHeadline() )
+ {
+ pLowerRow->Cut();
+ SwFrame::DestroyFrame(pLowerRow);
+ }
+
+ // insert new headlines
+ const sal_uInt16 nNewRepeat = GetTable()->GetRowsToRepeat();
+ auto& rLines = GetTable()->GetTabLines();
+ for ( sal_uInt16 nIdx = 0; nIdx < nNewRepeat; ++nIdx )
+ {
+ SwRowFrame* pHeadline = new SwRowFrame(*rLines[nIdx], this);
+ {
+ sw::FlyCreationSuppressor aSuppressor;
+ pHeadline->SetRepeatedHeadline(true);
+ }
+ pHeadline->Paste( this, pLowerRow );
+ }
+ }
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
+ break;
+
+ case RES_FRM_SIZE:
+ case RES_HORI_ORIENT:
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePrt | SwTabFrameInvFlags::InvalidateBrowseWidth;
+ break;
+
+ case RES_PAGEDESC: //Attribute changes (on/off)
+ if ( IsInDocBody() )
+ {
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
+ SwPageFrame *pPage = FindPageFrame();
+ if (pPage)
+ {
+ if ( !GetPrev() )
+ CheckPageDescs( pPage );
+ if (GetFormat()->GetPageDesc().GetNumOffset())
+ static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
+ SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
+ GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+ }
+ break;
+
+ case RES_BREAK:
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePos | SwTabFrameInvFlags::InvalidateNextPos;
+ break;
+
+ case RES_LAYOUT_SPLIT:
+ if ( !IsFollow() )
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePos;
+ break;
+ case RES_FRAMEDIR :
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+ case RES_COLLAPSING_BORDERS :
+ rInvFlags |= SwTabFrameInvFlags::InvalidatePrt;
+ lcl_InvalidateAllLowersPrt( this );
+ break;
+ case RES_UL_SPACE:
+ rInvFlags |= SwTabFrameInvFlags::InvalidateIndNextPrt | SwTabFrameInvFlags::InvalidatePrevPrt | SwTabFrameInvFlags::SetIndNextCompletePaint;
+ [[fallthrough]];
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+bool SwTabFrame::GetInfo( SfxPoolItem &rHint ) const
+{
+ if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && !IsFollow() )
+ {
+ SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( pPage )
+ {
+ if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
+ {
+ // Should be the one (can temporarily be different, should we be
+ // concerned about this possibility?)
+ rInfo.SetInfo( pPage, this );
+ return false;
+ }
+ if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
+ (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
+ {
+ //This could be the one.
+ rInfo.SetInfo( pPage, this );
+ }
+ }
+ }
+ return true;
+}
+
+SwFrame *SwTabFrame::FindLastContentOrTable()
+{
+ SwFrame *pRet = m_pLower;
+
+ while ( pRet && !pRet->IsContentFrame() )
+ {
+ SwFrame *pOld = pRet;
+
+ SwFrame *pTmp = pRet; // To skip empty section frames
+ while ( pRet->GetNext() )
+ {
+ pRet = pRet->GetNext();
+ if( !pRet->IsSctFrame() || static_cast<SwSectionFrame*>(pRet)->GetSection() )
+ pTmp = pRet;
+ }
+ pRet = pTmp;
+
+ if ( pRet->GetLower() )
+ pRet = pRet->GetLower();
+ if ( pRet == pOld )
+ {
+ // Check all other columns if there is a column based section with
+ // an empty last column at the end of the last cell - this is done
+ // by SwSectionFrame::FindLastContent
+ if( pRet->IsColBodyFrame() )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SwSectionFrame* pSect = pRet->FindSctFrame();
+ OSL_ENSURE( pSect, "Where does this column come from?");
+ OSL_ENSURE( IsAnLower( pSect ), "Split cell?" );
+#endif
+ return pRet->FindSctFrame()->FindLastContent();
+ }
+
+ // pRet may be a cell frame without a lower (cell has been split).
+ // We have to find the last content the hard way:
+
+ OSL_ENSURE( pRet->IsCellFrame(), "SwTabFrame::FindLastContent failed" );
+ const SwFrame* pRow = pRet->GetUpper();
+ while ( pRow && !pRow->GetUpper()->IsTabFrame() )
+ pRow = pRow->GetUpper();
+ const SwContentFrame* pContentFrame = pRow ? static_cast<const SwLayoutFrame*>(pRow)->ContainsContent() : nullptr;
+ pRet = nullptr;
+
+ while ( pContentFrame && static_cast<const SwLayoutFrame*>(pRow)->IsAnLower( pContentFrame ) )
+ {
+ pRet = const_cast<SwContentFrame*>(pContentFrame);
+ pContentFrame = pContentFrame->GetNextContentFrame();
+ }
+ }
+ }
+
+ // #112929# There actually is a situation, which results in pRet = 0:
+ // Insert frame, insert table via text <-> table. This gives you a frame
+ // containing a table without any other content frames. Split the table
+ // and undo the splitting. This operation gives us a table frame without
+ // a lower.
+ if ( pRet )
+ {
+ while ( pRet->GetNext() )
+ pRet = pRet->GetNext();
+
+ if (pRet->IsSctFrame())
+ pRet = static_cast<SwSectionFrame*>(pRet)->FindLastContent();
+ }
+
+ assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet) || dynamic_cast<SwTabFrame*>(pRet));
+ return pRet;
+}
+
+SwContentFrame *SwTabFrame::FindLastContent()
+{
+ SwFrame * pRet(FindLastContentOrTable());
+
+ while (pRet && pRet->IsTabFrame()) // possibly there's only tables here!
+ { // tdf#126138 skip table, don't look inside
+ pRet = pRet->GetPrev();
+ }
+
+ assert(pRet == nullptr || dynamic_cast<SwContentFrame*>(pRet));
+ return static_cast<SwContentFrame*>(pRet);
+}
+
+/// Return value defines if the frm needs to be relocated
+bool SwTabFrame::ShouldBwdMoved( SwLayoutFrame *pNewUpper, bool &rReformat )
+{
+ rReformat = false;
+ if ( SwFlowFrame::IsMoveBwdJump() || !IsPrevObjMove() )
+ {
+ //Flowing back Frames is quite time consuming unfortunately.
+ //Most often the location where the Frame wants to flow to has the same
+ //FixSize as the Frame itself. In such a situation it's easy to check if
+ //the Frame will find enough space for its VarSize, if this is not the
+ //case, the relocation can be skipped.
+ //Checking if the Frame will find enough space is done by the Frame itself,
+ //this also takes the possibility of splitting the Frame into account.
+ //If the FixSize is different or Flys are involved (at the old or the
+ //new position) the checks are pointless, the Frame then
+ //needs to be relocated tentatively (if a bit of space is available).
+
+ //The FixSize of the environments which contain tables is always the
+ //width.
+
+ SwPageFrame *pOldPage = FindPageFrame(),
+ *pNewPage = pNewUpper->FindPageFrame();
+ bool bMoveAnyway = false;
+ SwTwips nSpace = 0;
+
+ SwRectFnSet aRectFnSet(this);
+ if ( !SwFlowFrame::IsMoveBwdJump() )
+ {
+
+ tools::Long nOldWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+ SwRectFnSet fnRectX(pNewUpper);
+ tools::Long nNewWidth = fnRectX.GetWidth(pNewUpper->getFramePrintArea());
+ if( std::abs( nNewWidth - nOldWidth ) < 2 )
+ {
+ bMoveAnyway = BwdMoveNecessary( pOldPage, getFrameArea() ) > 1;
+ if( !bMoveAnyway )
+ {
+ SwRect aRect( pNewUpper->getFramePrintArea() );
+ aRect.Pos() += pNewUpper->getFrameArea().Pos();
+ const SwFrame *pPrevFrame = pNewUpper->Lower();
+ while ( pPrevFrame && pPrevFrame != this )
+ {
+ fnRectX.SetTop( aRect, fnRectX.GetBottom(pPrevFrame->getFrameArea()) );
+ pPrevFrame = pPrevFrame->GetNext();
+ }
+ bMoveAnyway = BwdMoveNecessary( pNewPage, aRect) > 1;
+
+ // #i54861# Due to changes made in PrepareMake,
+ // the tabfrm may not have a correct position. Therefore
+ // it is possible that pNewUpper->getFramePrintArea().Height == 0. In this
+ // case the above calculation of nSpace might give wrong
+ // results and we really do not want to MoveBackward into a
+ // 0 height frame. If nTmpSpace is already <= 0, we take this
+ // value:
+ const SwTwips nTmpSpace = fnRectX.GetHeight(aRect);
+ if ( fnRectX.GetHeight(pNewUpper->getFramePrintArea()) > 0 || nTmpSpace <= 0 )
+ nSpace = nTmpSpace;
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && pSh->GetViewOptions()->getBrowseMode() )
+ nSpace += pNewUpper->Grow( LONG_MAX, true );
+ if (0 < nSpace && GetPrecede())
+ {
+ SwTwips nUpperDummy(0);
+ tools::Long nLeftOffsetDummy(0), nRightOffsetDummy(0);
+ // tdf#116501 check for no-wrap fly overlap
+ static_cast<const SwTabFrame*>(GetPrecede())->CalcFlyOffsets(
+ nUpperDummy, nLeftOffsetDummy, nRightOffsetDummy, &nSpace);
+ }
+ }
+ }
+ else if (!m_bLockBackMove)
+ bMoveAnyway = true;
+ }
+ else if (!m_bLockBackMove)
+ bMoveAnyway = true;
+
+ if ( bMoveAnyway )
+ {
+ rReformat = true;
+ return true;
+ }
+
+ bool bFits = nSpace > 0;
+ if (!bFits && aRectFnSet.GetHeight(getFrameArea()) == 0)
+ // This frame fits into pNewUpper in case it has no space, but this
+ // frame is empty.
+ bFits = nSpace >= 0;
+ if (!m_bLockBackMove && bFits)
+ {
+ // #i26945# - check, if follow flow line
+ // contains frame, which are moved forward due to its object
+ // positioning.
+ const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
+ if ( pFirstRow && pFirstRow->IsInFollowFlowRow() &&
+ SwLayouter::DoesRowContainMovedFwdFrame(
+ *(pFirstRow->GetFormat()->GetDoc()),
+ *pFirstRow ) )
+ {
+ return false;
+ }
+ SwTwips nTmpHeight = CalcHeightOfFirstContentLine();
+
+ // For some mysterious reason, I changed the good old
+ // 'return nHeight <= nSpace' to 'return nTmpHeight < nSpace'.
+ // This obviously results in problems with table frames in
+ // sections. Remember: Every twip is sacred.
+ return nTmpHeight <= nSpace;
+ }
+ }
+ return false;
+}
+
+void SwTabFrame::Cut()
+{
+ OSL_ENSURE( GetUpper(), "Cut without Upper()." );
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ SwFrame *pFrame = GetNext();
+ if( pFrame )
+ {
+ // Possibly the old follow calculated a spacing to the predecessor
+ // which is obsolete now when it becomes the first frame
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ if ( pFrame->IsContentFrame() )
+ pFrame->InvalidatePage( pPage );
+ if( IsInSct() && !GetPrev() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ }
+ else
+ {
+ InvalidateNextPos();
+ //Someone has to do the retouch: predecessor or upper
+ pFrame = GetPrev();
+ if ( nullptr != pFrame )
+ {
+ pFrame->SetRetouche();
+ pFrame->Prepare( PrepareHint::WidowsOrphans );
+ pFrame->InvalidatePos_();
+ if ( pFrame->IsContentFrame() )
+ pFrame->InvalidatePage( pPage );
+ }
+ //If I am (was) the only FlowFrame in my own upper, it has to do
+ //the retouch. Moreover a new empty page might be created.
+ else
+ { SwRootFrame *pRoot = static_cast<SwRootFrame*>(pPage->GetUpper());
+ pRoot->SetSuperfluous();
+ GetUpper()->SetCompletePaint();
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ }
+ }
+
+ //First remove, then shrink the upper.
+ SwLayoutFrame *pUp = GetUpper();
+ SwRectFnSet aRectFnSet(this);
+ RemoveFromLayout();
+ if ( pUp )
+ {
+ OSL_ENSURE( !pUp->IsFootnoteFrame(), "Table in Footnote." );
+ SwSectionFrame *pSct = nullptr;
+ // #126020# - adjust check for empty section
+ // #130797# - correct fix #126020#
+ if ( !pUp->Lower() && pUp->IsInSct() &&
+ !(pSct = pUp->FindSctFrame())->ContainsContent() &&
+ !pSct->ContainsAny( true ) )
+ {
+ if ( pUp->GetUpper() )
+ {
+ pSct->DelEmpty( false );
+ pSct->InvalidateSize_();
+ }
+ }
+ // table-in-footnote: delete empty footnote frames (like SwContentFrame::Cut)
+ else if (!pUp->Lower() && pUp->IsFootnoteFrame() && !pUp->IsColLocked())
+ {
+ if (pUp->GetNext() && !pUp->GetPrev())
+ {
+ if (SwFrame *const pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny())
+ {
+ pTmp->InvalidatePrt_();
+ }
+ }
+ if (!pUp->IsDeleteForbidden())
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ }
+ }
+ else if( aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ // OD 26.08.2003 #i18103# - *no* 'ColUnlock' of section -
+ // undo changes of fix for #104992#
+ pUp->Shrink( getFrameArea().Height() );
+ }
+ }
+
+
+ if ( pPage && !IsFollow() && pPage->GetUpper() )
+ static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
+}
+
+void SwTabFrame::Paste( SwFrame* pParent, SwFrame* pSibling )
+{
+ OSL_ENSURE( pParent, "No parent for pasting." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm the parent myself." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I'm still registered somewhere." );
+
+ //Insert in the tree.
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ InvalidateAll_();
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ GetNext()->InvalidatePrt_();
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ }
+
+ SwRectFnSet aRectFnSet(this);
+ if( aRectFnSet.GetHeight(getFrameArea()) )
+ pParent->Grow( aRectFnSet.GetHeight(getFrameArea()) );
+
+ if( aRectFnSet.GetWidth(getFrameArea()) != aRectFnSet.GetWidth(pParent->getFramePrintArea()) )
+ Prepare( PrepareHint::FixSizeChanged );
+ if ( GetPrev() )
+ {
+ if ( !IsFollow() )
+ {
+ GetPrev()->InvalidateSize();
+ if ( GetPrev()->IsContentFrame() )
+ GetPrev()->InvalidatePage( pPage );
+ }
+ }
+ else if ( GetNext() )
+ // Take the spacing into account when dealing with ContentFrames.
+ // There are two situations (both always happen at the same time):
+ // a) The Content becomes the first in a chain
+ // b) The new follower was previously the first in a chain
+ GetNext()->InvalidatePrt_();
+
+ if ( !pPage || IsFollow() )
+ return;
+
+ if ( pPage->GetUpper() )
+ static_cast<SwRootFrame*>(pPage->GetUpper())->InvalidateBrowseWidth();
+
+ if ( !GetPrev() )//At least needed for HTML with a table at the beginning.
+ {
+ const SwPageDesc *pDesc = GetFormat()->GetPageDesc().GetPageDesc();
+ if ( (pDesc && pDesc != pPage->GetPageDesc()) ||
+ (!pDesc && pPage->GetPageDesc() != &GetFormat()->GetDoc()->GetPageDesc(0)) )
+ CheckPageDescs( pPage );
+ }
+}
+
+bool SwTabFrame::Prepare( const PrepareHint eHint, const void *, bool )
+{
+ if( PrepareHint::BossChanged == eHint )
+ CheckDirChange();
+ return false;
+}
+
+SwRowFrame::SwRowFrame(const SwTableLine &rLine, SwFrame* pSib, bool bInsertContent)
+ : SwLayoutFrame( rLine.GetFrameFormat(), pSib )
+ , m_pTabLine( &rLine )
+ , m_pFollowRow( nullptr )
+ // #i29550#
+ , mnTopMarginForLowers( 0 )
+ , mnBottomMarginForLowers( 0 )
+ , mnBottomLineSize( 0 )
+ // --> split table rows
+ , m_bIsFollowFlowRow( false )
+ // <-- split table rows
+ , m_bIsRepeatedHeadline( false )
+ , m_bIsRowSpanLine( false )
+ , m_bForceRowSplitAllowed( false )
+ , m_bIsInSplit( false )
+{
+ mnFrameType = SwFrameType::Row;
+
+ //Create the boxes and insert them.
+ const SwTableBoxes &rBoxes = rLine.GetTabBoxes();
+ SwFrame *pTmpPrev = nullptr;
+ for ( size_t i = 0; i < rBoxes.size(); ++i )
+ {
+ SwCellFrame *pNew = new SwCellFrame( *rBoxes[i], this, bInsertContent );
+ pNew->InsertBehind( this, pTmpPrev );
+ pTmpPrev = pNew;
+ }
+}
+
+void SwRowFrame::DestroyImpl()
+{
+ sw::BroadcastingModify* pMod = GetFormat();
+ if( pMod )
+ {
+ pMod->Remove( this );
+ if( !pMod->HasWriterListeners() )
+ delete pMod;
+ }
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwRowFrame::~SwRowFrame()
+{
+}
+
+void SwRowFrame::RegistFlys( SwPageFrame *pPage )
+{
+ ::RegistFlys( pPage ? pPage : FindPageFrame(), this );
+}
+
+void SwRowFrame::OnFrameSize(const SfxPoolItem& rSize)
+{
+ SwTabFrame* pTab = FindTabFrame();
+ if(pTab)
+ {
+ const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
+ // #i35063#
+ // Invalidation required is pRow is last row
+ if(bInFirstNonHeadlineRow)
+ pTab = pTab->FindMaster();
+ if(bInFirstNonHeadlineRow || !GetNext())
+ pTab->InvalidatePos();
+ }
+ const sw::BroadcastingModify aMod;
+ SwLayoutFrame::SwClientNotify(aMod, sw::LegacyModifyHint(nullptr, &rSize));
+}
+
+void SwRowFrame::SwClientNotify(const SwModify& rModify, const SfxHint& rHint)
+{
+ if(auto pNewFormatHint = dynamic_cast<const sw::TableLineFormatChanged*>(&rHint))
+ {
+ if(GetTabLine() != &pNewFormatHint->m_rTabLine)
+ return;
+ RegisterToFormat(const_cast<SwTableLineFormat&>(pNewFormatHint->m_rNewFormat));
+ InvalidateSize();
+ InvalidatePrt_();
+ SetCompletePaint();
+ ReinitializeFrameSizeAttrFlags();
+
+ // #i35063#
+ // consider 'split row allowed' attribute
+ SwTabFrame* pTab = FindTabFrame();
+ bool bInFollowFlowRow = false;
+ const bool bInFirstNonHeadlineRow = pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow();
+ if(bInFirstNonHeadlineRow ||
+ !GetNext() ||
+ (bInFollowFlowRow = IsInFollowFlowRow()) ||
+ nullptr != IsInSplitTableRow() )
+ {
+ if(bInFirstNonHeadlineRow || bInFollowFlowRow)
+ pTab = pTab->FindMaster();
+
+ pTab->SetRemoveFollowFlowLinePending(true);
+ pTab->InvalidatePos();
+ }
+ }
+ else if(auto pMoveTableLineHint = dynamic_cast<const sw::MoveTableLineHint*>(&rHint))
+ {
+
+ if(GetTabLine() != &pMoveTableLineHint->m_rTableLine)
+ return;
+ const_cast<SwFrameFormat*>(&pMoveTableLineHint->m_rNewFormat)->Add(this);
+ InvalidateAll();
+ ReinitializeFrameSizeAttrFlags();
+ return;
+ }
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pNew)
+ {
+ // possibly not needed?
+ SwLayoutFrame::SwClientNotify(rModify, rHint);
+ return;
+ }
+ switch(pLegacy->m_pNew->Which())
+ {
+ case RES_ATTRSET_CHG:
+ {
+ const SwAttrSet* pChgSet = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
+ const SfxPoolItem* pItem = nullptr;
+ pChgSet->GetItemState(RES_FRM_SIZE, false, &pItem);
+ if(!pItem)
+ pChgSet->GetItemState(RES_ROW_SPLIT, false, &pItem);
+ if(pItem)
+ OnFrameSize(*pItem);
+ else
+ SwLayoutFrame::SwClientNotify(rModify, rHint); // possibly not needed?
+ return;
+ }
+ case RES_FRM_SIZE:
+ case RES_ROW_SPLIT:
+ OnFrameSize(*static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew));
+ return;
+ }
+}
+
+void SwRowFrame::MakeAll(vcl::RenderContext* pRenderContext)
+{
+ if ( !GetNext() )
+ {
+ setFrameAreaSizeValid(false);
+ }
+
+ SwLayoutFrame::MakeAll(pRenderContext);
+}
+
+tools::Long CalcHeightWithFlys( const SwFrame *pFrame )
+{
+ SwRectFnSet aRectFnSet(pFrame);
+ tools::Long nHeight = 0;
+ const SwFrame* pTmp = pFrame->IsSctFrame() ?
+ static_cast<const SwSectionFrame*>(pFrame)->ContainsContent() : pFrame;
+ while( pTmp )
+ {
+ // #i26945# - consider follow text frames
+ const SwSortedObjs* pObjs( nullptr );
+ bool bIsFollow( false );
+ if ( pTmp->IsTextFrame() && static_cast<const SwTextFrame*>(pTmp)->IsFollow() )
+ {
+ const SwFrame* pMaster;
+ // #i46450# Master does not necessarily have
+ // to exist if this function is called from JoinFrame() ->
+ // Cut() -> Shrink()
+ const SwTextFrame* pTmpFrame = static_cast<const SwTextFrame*>(pTmp);
+ if ( pTmpFrame->GetPrev() && pTmpFrame->GetPrev()->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() &&
+ static_cast<const SwTextFrame*>(pTmpFrame->GetPrev())->GetFollow() != pTmp )
+ pMaster = nullptr;
+ else
+ pMaster = pTmpFrame->FindMaster();
+
+ if ( pMaster )
+ {
+ pObjs = static_cast<const SwTextFrame*>(pTmp)->FindMaster()->GetDrawObjs();
+ bIsFollow = true;
+ }
+ }
+ else
+ {
+ pObjs = pTmp->GetDrawObjs();
+ }
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ // #i26945# - if <pTmp> is follow, the
+ // anchor character frame has to be <pTmp>.
+ if ( bIsFollow &&
+ pAnchoredObj->FindAnchorCharFrame() != pTmp )
+ {
+ continue;
+ }
+ // #i26945# - consider also drawing objects
+ {
+ // OD 30.09.2003 #i18732# - only objects, which follow
+ // the text flow have to be considered.
+ const SwFrameFormat& rFrameFormat = pAnchoredObj->GetFrameFormat();
+ bool bFollowTextFlow = rFrameFormat.GetFollowTextFlow().GetValue();
+ bool bIsFarAway = pAnchoredObj->GetObjRect().Top() != FAR_AWAY;
+ const SwPageFrame* pPageFrm = pTmp->FindPageFrame();
+ bool bIsAnchoredToTmpFrm = false;
+ if ( pPageFrm && pPageFrm->IsPageFrame() && pAnchoredObj->GetPageFrame())
+ bIsAnchoredToTmpFrm = pAnchoredObj->GetPageFrame() == pPageFrm ||
+ (pPageFrm->GetFormatPage().GetPhyPageNum() == pAnchoredObj->GetPageFrame()->GetFormatPage().GetPhyPageNum() + 1);
+ const bool bConsiderObj =
+ (rFrameFormat.GetAnchor().GetAnchorId() != RndStdIds::FLY_AS_CHAR) &&
+ bIsFarAway &&
+ bFollowTextFlow && bIsAnchoredToTmpFrm;
+ bool bWrapThrough = rFrameFormat.GetSurround().GetValue() == text::WrapTextMode_THROUGH;
+ bool bInBackground = !rFrameFormat.GetOpaque().GetValue();
+ if (pFrame->IsInTab() && bFollowTextFlow && bWrapThrough && bInBackground)
+ {
+ // Ignore wrap-through objects when determining the cell height.
+ // Normally FollowTextFlow requires a resize of the cell, but not in case of
+ // wrap-through.
+ continue;
+ }
+
+ if ( bConsiderObj )
+ {
+ const SwFormatFrameSize &rSz = rFrameFormat.GetFrameSize();
+ if( !rSz.GetHeightPercent() )
+ {
+ const SwTwips nDistOfFlyBottomToAnchorTop =
+ aRectFnSet.GetHeight(pAnchoredObj->GetObjRect()) +
+ ( aRectFnSet.IsVert() ?
+ pAnchoredObj->GetCurrRelPos().X() :
+ pAnchoredObj->GetCurrRelPos().Y() );
+
+ const SwTwips nFrameDiff =
+ aRectFnSet.YDiff(
+ aRectFnSet.GetTop(pTmp->getFrameArea()),
+ aRectFnSet.GetTop(pFrame->getFrameArea()) );
+
+ nHeight = std::max( nHeight, nDistOfFlyBottomToAnchorTop + nFrameDiff -
+ aRectFnSet.GetHeight(pFrame->getFrameArea()) );
+
+ // #i56115# The first height calculation
+ // gives wrong results if pFrame->getFramePrintArea().Y() > 0. We do
+ // a second calculation based on the actual rectangles of
+ // pFrame and pAnchoredObj, and use the maximum of the results.
+ // I do not want to remove the first calculation because
+ // if clipping has been applied, using the GetCurrRelPos
+ // might be the better option to calculate nHeight.
+ const SwTwips nDistOfFlyBottomToAnchorTop2 = aRectFnSet.YDiff(
+ aRectFnSet.GetBottom(pAnchoredObj->GetObjRect()),
+ aRectFnSet.GetBottom(pFrame->getFrameArea()) );
+
+ nHeight = std::max( nHeight, tools::Long(nDistOfFlyBottomToAnchorTop2 ));
+ }
+ }
+ }
+ }
+ }
+ if( !pFrame->IsSctFrame() )
+ break;
+ pTmp = pTmp->FindNextCnt();
+ if( !static_cast<const SwSectionFrame*>(pFrame)->IsAnLower( pTmp ) )
+ break;
+ }
+ return nHeight;
+}
+
+static SwTwips lcl_CalcTopAndBottomMargin( const SwLayoutFrame& rCell, const SwBorderAttrs& rAttrs )
+{
+ const SwTabFrame* pTab = rCell.FindTabFrame();
+ SwTwips nTopSpace = 0;
+ SwTwips nBottomSpace = 0;
+
+ // #i29550#
+ if ( pTab->IsCollapsingBorders() && rCell.Lower() && !rCell.Lower()->IsRowFrame() )
+ {
+ nTopSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetTopMarginForLowers();
+ nBottomSpace = static_cast<const SwRowFrame*>(rCell.GetUpper())->GetBottomMarginForLowers();
+ }
+ else
+ {
+ if ( pTab->IsVertical() != rCell.IsVertical() )
+ {
+ nTopSpace = rAttrs.CalcLeft( &rCell );
+ nBottomSpace = rAttrs.CalcRight( &rCell );
+ }
+ else
+ {
+ nTopSpace = rAttrs.CalcTop();
+ nBottomSpace = rAttrs.CalcBottom();
+ }
+ }
+
+ return nTopSpace + nBottomSpace;
+}
+
+// #i26945# - add parameter <_bConsiderObjs> in order to
+// control, if floating screen objects have to be considered for the minimal
+// cell height.
+static SwTwips lcl_CalcMinCellHeight( const SwLayoutFrame *_pCell,
+ const bool _bConsiderObjs,
+ const SwBorderAttrs *pAttrs = nullptr )
+{
+ SwRectFnSet aRectFnSet(_pCell);
+ SwTwips nHeight = 0;
+ const SwFrame* pLow = _pCell->Lower();
+ if ( pLow )
+ {
+ tools::Long nFlyAdd = 0;
+ while ( pLow )
+ {
+ if ( pLow->IsRowFrame() )
+ {
+ // #i26945#
+ nHeight += ::lcl_CalcMinRowHeight( static_cast<const SwRowFrame*>(pLow),
+ _bConsiderObjs );
+ }
+ else
+ {
+ tools::Long nLowHeight = aRectFnSet.GetHeight(pLow->getFrameArea());
+ nHeight += nLowHeight;
+ // #i26945#
+ if ( _bConsiderObjs )
+ {
+ nFlyAdd = std::max( tools::Long(0), nFlyAdd - nLowHeight );
+ nFlyAdd = std::max( nFlyAdd, ::CalcHeightWithFlys( pLow ) );
+ }
+ }
+
+ pLow = pLow->GetNext();
+ }
+ if ( nFlyAdd )
+ nHeight += nFlyAdd;
+ }
+ // The border/margin needs to be considered too, unfortunately it can't be
+ // calculated using PrintArea and FrameArea because any or all of those
+ // may be invalid.
+ if ( _pCell->Lower() )
+ {
+ if ( pAttrs )
+ nHeight += lcl_CalcTopAndBottomMargin( *_pCell, *pAttrs );
+ else
+ {
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), _pCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nHeight += lcl_CalcTopAndBottomMargin( *_pCell, rAttrs );
+ }
+ }
+ return nHeight;
+}
+
+// #i26945# - add parameter <_bConsiderObjs> in order to control,
+// if floating screen objects have to be considered for the minimal cell height
+static SwTwips lcl_CalcMinRowHeight( const SwRowFrame* _pRow,
+ const bool _bConsiderObjs )
+{
+ SwTwips nHeight = 0;
+ if ( !_pRow->IsRowSpanLine() )
+ {
+ const SwFormatFrameSize &rSz = _pRow->GetFormat()->GetFrameSize();
+ if ( _pRow->HasFixSize() )
+ {
+ OSL_ENSURE(SwFrameSize::Fixed == rSz.GetHeightSizeType(), "pRow claims to have fixed size");
+ return rSz.GetHeight();
+ }
+ // If this row frame is being split, then row's minimal height shouldn't restrict
+ // this frame's minimal height, because the rest will go to follow frame.
+ else if ( !_pRow->IsInSplit() && rSz.GetHeightSizeType() == SwFrameSize::Minimum )
+ {
+ nHeight = rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*_pRow);
+ }
+ }
+
+ SwRectFnSet aRectFnSet(_pRow);
+ const SwCellFrame* pLow = static_cast<const SwCellFrame*>(_pRow->Lower());
+ while ( pLow )
+ {
+ SwTwips nTmp = 0;
+ const tools::Long nRowSpan = pLow->GetLayoutRowSpan();
+ // --> NEW TABLES
+ // Consider height of
+ // 1. current cell if RowSpan == 1
+ // 2. current cell if cell is "follow" cell of a cell with RowSpan == -1
+ // 3. master cell if RowSpan == -1
+ if ( 1 == nRowSpan )
+ {
+ nTmp = ::lcl_CalcMinCellHeight( pLow, _bConsiderObjs );
+ }
+ else if ( -1 == nRowSpan )
+ {
+ // Height of the last cell of a row span is height of master cell
+ // minus the height of the other rows which are covered by the master
+ // cell:
+ const SwCellFrame& rMaster = pLow->FindStartEndOfRowSpanCell( true );
+ nTmp = ::lcl_CalcMinCellHeight( &rMaster, _bConsiderObjs );
+ const SwFrame* pMasterRow = rMaster.GetUpper();
+ while ( pMasterRow && pMasterRow != _pRow )
+ {
+ nTmp -= aRectFnSet.GetHeight(pMasterRow->getFrameArea());
+ pMasterRow = pMasterRow->GetNext();
+ }
+ }
+ // <-- NEW TABLES
+
+ // Do not consider rotated cells:
+ if ( pLow->IsVertical() == aRectFnSet.IsVert() && nTmp > nHeight )
+ nHeight = nTmp;
+
+ pLow = static_cast<const SwCellFrame*>(pLow->GetNext());
+ }
+
+ return nHeight;
+}
+
+// #i29550#
+
+// Calculate the maximum of (TopLineSize + TopLineDist) over all lowers:
+static sal_uInt16 lcl_GetTopSpace( const SwRowFrame& rRow )
+{
+ sal_uInt16 nTopSpace = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpTopSpace = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ nTmpTopSpace = lcl_GetTopSpace( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpTopSpace = rBoxItem.CalcLineSpace( SvxBoxItemLine::TOP, true );
+ }
+ nTopSpace = std::max( nTopSpace, nTmpTopSpace );
+ }
+ return nTopSpace;
+}
+
+// Calculate the maximum of TopLineDist over all lowers:
+static sal_uInt16 lcl_GetTopLineDist( const SwRowFrame& rRow )
+{
+ sal_uInt16 nTopLineDist = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpTopLineDist = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ nTmpTopLineDist = lcl_GetTopLineDist( *static_cast<const SwRowFrame*>(pCurrLower->Lower()) );
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpTopLineDist = rBoxItem.GetDistance( SvxBoxItemLine::TOP );
+ }
+ nTopLineDist = std::max( nTopLineDist, nTmpTopLineDist );
+ }
+ return nTopLineDist;
+}
+
+// Calculate the maximum of BottomLineSize over all lowers:
+static sal_uInt16 lcl_GetBottomLineSize( const SwRowFrame& rRow )
+{
+ sal_uInt16 nBottomLineSize = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpBottomLineSize = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ {
+ const SwFrame* pRow = pCurrLower->GetLastLower();
+ nTmpBottomLineSize = lcl_GetBottomLineSize( *static_cast<const SwRowFrame*>(pRow) );
+ }
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpBottomLineSize = rBoxItem.CalcLineSpace( SvxBoxItemLine::BOTTOM, true ) -
+ rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ nBottomLineSize = std::max( nBottomLineSize, nTmpBottomLineSize );
+ }
+ return nBottomLineSize;
+}
+
+// Calculate the maximum of BottomLineDist over all lowers:
+static sal_uInt16 lcl_GetBottomLineDist( const SwRowFrame& rRow )
+{
+ sal_uInt16 nBottomLineDist = 0;
+ for ( const SwCellFrame* pCurrLower = static_cast<const SwCellFrame*>(rRow.Lower()); pCurrLower;
+ pCurrLower = static_cast<const SwCellFrame*>(pCurrLower->GetNext()) )
+ {
+ sal_uInt16 nTmpBottomLineDist = 0;
+ if ( pCurrLower->Lower() && pCurrLower->Lower()->IsRowFrame() )
+ {
+ const SwFrame* pRow = pCurrLower->GetLastLower();
+ nTmpBottomLineDist = lcl_GetBottomLineDist( *static_cast<const SwRowFrame*>(pRow) );
+ }
+ else
+ {
+ const SwAttrSet& rSet = const_cast<SwCellFrame*>(pCurrLower)->GetFormat()->GetAttrSet();
+ const SvxBoxItem& rBoxItem = rSet.GetBox();
+ nTmpBottomLineDist = rBoxItem.GetDistance( SvxBoxItemLine::BOTTOM );
+ }
+ nBottomLineDist = std::max( nBottomLineDist, nTmpBottomLineDist );
+ }
+ return nBottomLineDist;
+}
+
+// tdf#104425: calculate the height of all row frames,
+// for which this frame is a follow.
+// When a row has fixed/minimum height, it may span over
+// several pages. The minimal height on this page should
+// take into account the sum of all the heights of previous
+// frames that constitute the table row on previous pages.
+// Otherwise, trying to split a too high row frame will
+// result in loop trying to create that too high row
+// on each following page
+static SwTwips lcl_calcHeightOfRowBeforeThisFrame(const SwRowFrame& rRow)
+{
+ // We don't need to account for previous instances of repeated headlines
+ if (rRow.IsRepeatedHeadline())
+ return 0;
+ SwRectFnSet aRectFnSet(&rRow);
+ const SwTableLine* pLine = rRow.GetTabLine();
+ const SwTabFrame* pTab = rRow.FindTabFrame();
+ if (!pLine || !pTab || !pTab->IsFollow())
+ return 0;
+ SwTwips nResult = 0;
+ SwIterator<SwRowFrame, SwFormat> aIter(*pLine->GetFrameFormat());
+ for (const SwRowFrame* pCurRow = aIter.First(); pCurRow; pCurRow = aIter.Next())
+ {
+ if (pCurRow != &rRow && pCurRow->GetTabLine() == pLine)
+ {
+ // We've found another row frame that is part of the same table row
+ const SwTabFrame* pCurTab = pCurRow->FindTabFrame();
+ // A row frame may not belong to a table frame, when it is being cut, e.g., in
+ // lcl_PostprocessRowsInCells().
+ // Its SwRowFrame::Cut() has been called; it in turn called SwLayoutFrame::Cut(),
+ // which nullified row's upper in RemoveFromLayout(), and then called Shrink()
+ // for its former upper.
+ // Regardless of whether it will be pasted back, or destroyed, currently it's not
+ // part of layout, and its height does not count
+ if (pCurTab && pCurTab->IsAnFollow(pTab))
+ {
+ // The found row frame belongs to a table frame that precedes
+ // (above) this one in chain. So, include it in the sum
+ nResult += aRectFnSet.GetHeight(pCurRow->getFrameArea());
+ }
+ }
+ }
+ return nResult;
+}
+
+void SwRowFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ SwRectFnSet aRectFnSet(this);
+ OSL_ENSURE( pAttrs, "SwRowFrame::Format without Attrs." );
+
+ const bool bFix = mbFixSize;
+
+ if ( !isFramePrintAreaValid() )
+ {
+ // RowFrames don't have borders/margins therefore the PrintArea always
+ // matches the FrameArea.
+ setFramePrintAreaValid(true);
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Left( 0 );
+ aPrt.Top( 0 );
+ aPrt.Width ( getFrameArea().Width() );
+ aPrt.Height( getFrameArea().Height() );
+ }
+
+ // #i29550#
+ // Here we calculate the top-printing area for the lower cell frames
+ SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->IsCollapsingBorders() )
+ {
+ const sal_uInt16 nTopSpace = lcl_GetTopSpace( *this );
+ const sal_uInt16 nTopLineDist = lcl_GetTopLineDist( *this );
+ const sal_uInt16 nBottomLineSize = lcl_GetBottomLineSize( *this );
+ const sal_uInt16 nBottomLineDist = lcl_GetBottomLineDist( *this );
+
+ const SwRowFrame* pPreviousRow = nullptr;
+
+ // #i32456#
+ // In order to calculate the top printing area for the lower cell
+ // frames, we have to find the 'previous' row frame and compare
+ // the bottom values of the 'previous' row with the 'top' values
+ // of this row. The best way to find the 'previous' row is to
+ // use the table structure:
+ const SwTable* pTable = pTabFrame->GetTable();
+ const SwTableLine* pPrevTabLine = nullptr;
+ const SwRowFrame* pTmpRow = this;
+
+ while ( pTmpRow && !pPrevTabLine )
+ {
+ size_t nIdx = 0;
+ const SwTableLines& rLines = pTmpRow->GetTabLine()->GetUpper() ?
+ pTmpRow->GetTabLine()->GetUpper()->GetTabLines() :
+ pTable->GetTabLines();
+
+ while ( rLines[ nIdx ] != pTmpRow->GetTabLine() )
+ ++nIdx;
+
+ if ( nIdx > 0 )
+ {
+ // pTmpRow has a 'previous' row in the table structure:
+ pPrevTabLine = rLines[ nIdx - 1 ];
+ }
+ else
+ {
+ // pTmpRow is a first row in the table structure.
+ // We go up in the table structure:
+ pTmpRow = pTmpRow->GetUpper()->GetUpper() &&
+ pTmpRow->GetUpper()->GetUpper()->IsRowFrame() ?
+ static_cast<const SwRowFrame*>( pTmpRow->GetUpper()->GetUpper() ) :
+ nullptr;
+ }
+ }
+
+ // If we found a 'previous' row, we look for the appropriate row frame:
+ if ( pPrevTabLine )
+ {
+ SwIterator<SwRowFrame,SwFormat> aIter( *pPrevTabLine->GetFrameFormat() );
+ for ( SwRowFrame* pRow = aIter.First(); pRow; pRow = aIter.Next() )
+ {
+ // #115759# - do *not* take repeated
+ // headlines, because during split of table it can be
+ // invalid and thus can't provide correct border values.
+ if ( pRow->GetTabLine() == pPrevTabLine &&
+ !pRow->IsRepeatedHeadline() )
+ {
+ pPreviousRow = pRow;
+ break;
+ }
+ }
+ }
+
+ sal_uInt16 nTopPrtMargin = nTopSpace;
+ if ( pPreviousRow )
+ {
+ const sal_uInt16 nTmpPrtMargin = pPreviousRow->GetBottomLineSize() + nTopLineDist;
+ if ( nTmpPrtMargin > nTopPrtMargin )
+ nTopPrtMargin = nTmpPrtMargin;
+ }
+
+ // table has to be notified if it has to change its lower
+ // margin due to changes of nBottomLineSize:
+ if ( !GetNext() && nBottomLineSize != GetBottomLineSize() )
+ pTabFrame->InvalidatePrt_();
+
+ // If there are rows nested inside this row, the nested rows
+ // may not have been calculated yet. Therefore the
+ // ::lcl_CalcMinRowHeight( this ) operation later in this
+ // function cannot consider the correct border values. We
+ // have to trigger the invalidation of the outer row frame
+ // manually:
+ // Note: If any further invalidations should be necessary, we
+ // should consider moving the invalidation stuff to the
+ // appropriate SwNotify object.
+ if ( GetUpper()->GetUpper()->IsRowFrame() &&
+ ( nBottomLineDist != GetBottomMarginForLowers() ||
+ nTopPrtMargin != GetTopMarginForLowers() ) )
+ GetUpper()->GetUpper()->InvalidateSize_();
+
+ SetBottomMarginForLowers( nBottomLineDist ); // 3.
+ SetBottomLineSize( nBottomLineSize ); // 4.
+ SetTopMarginForLowers( nTopPrtMargin ); // 5.
+
+ }
+ }
+
+ while ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+
+#if OSL_DEBUG_LEVEL > 0
+ if ( HasFixSize() )
+ {
+ const SwFormatFrameSize &rFrameSize = GetFormat()->GetFrameSize();
+ OSL_ENSURE( rFrameSize.GetSize().Height() > 0, "Has it" );
+ }
+#endif
+ const SwTwips nDiff = aRectFnSet.GetHeight(getFrameArea()) -
+ ( HasFixSize() && !IsRowSpanLine()
+ ? pAttrs->GetSize().Height()
+ // #i26945#
+ : ::lcl_CalcMinRowHeight( this,
+ FindTabFrame()->IsConsiderObjsForMinCellHeight() ) );
+ if ( nDiff )
+ {
+ mbFixSize = false;
+ if ( nDiff > 0 )
+ Shrink( nDiff, false, true );
+ else if ( nDiff < 0 )
+ Grow( -nDiff );
+ mbFixSize = bFix;
+ }
+ }
+
+ // last row will fill the space in its upper.
+ if ( GetNext() )
+ return;
+
+ //The last fills the remaining space in the upper.
+ SwTwips nDiff = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
+ SwFrame *pSibling = GetUpper()->Lower();
+ do
+ { nDiff -= aRectFnSet.GetHeight(pSibling->getFrameArea());
+ pSibling = pSibling->GetNext();
+ } while ( pSibling );
+ if ( nDiff > 0 )
+ {
+ mbFixSize = false;
+ Grow( nDiff );
+ mbFixSize = bFix;
+ setFrameAreaSizeValid(true);
+ }
+}
+
+void SwRowFrame::AdjustCells( const SwTwips nHeight, const bool bHeight )
+{
+ SwFrame *pFrame = Lower();
+ if ( bHeight )
+ {
+ SwRectFnSet aRectFnSet(this);
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRect aOldFrame;
+#endif
+
+ while ( pFrame )
+ {
+ SwFrame* pNotify = nullptr;
+
+ SwCellFrame* pCellFrame = static_cast<SwCellFrame*>(pFrame);
+
+ // NEW TABLES
+ // Which cells need to be adjusted if the current row changes
+ // its height?
+
+ // Current frame is a covered frame:
+ // Set new height for covered cell and adjust master cell:
+ if ( pCellFrame->GetTabBox()->getRowSpan() < 1 )
+ {
+ // Set height of current (covered) cell to new line height.
+ const tools::Long nDiff = nHeight - aRectFnSet.GetHeight(pCellFrame->getFrameArea());
+ if ( nDiff )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pCellFrame);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ }
+
+ pCellFrame->InvalidatePrt_();
+ }
+ }
+
+ SwCellFrame* pToAdjust = nullptr;
+ SwFrame* pToAdjustRow = nullptr;
+
+ // If current frame is covered frame, we still want to adjust the
+ // height of the cell starting the row span
+ if ( pCellFrame->GetLayoutRowSpan() < 1 )
+ {
+ pToAdjust = const_cast< SwCellFrame*>(&pCellFrame->FindStartEndOfRowSpanCell( true ));
+ pToAdjustRow = pToAdjust->GetUpper();
+ }
+ else
+ {
+ pToAdjust = pCellFrame;
+ pToAdjustRow = this;
+ }
+
+ // Set height of master cell to height of all lines spanned by this line.
+ tools::Long nRowSpan = pToAdjust->GetLayoutRowSpan();
+ SwTwips nSumRowHeight = 0;
+ while ( pToAdjustRow )
+ {
+ // Use new height for the current row:
+ nSumRowHeight += pToAdjustRow == this ?
+ nHeight :
+ aRectFnSet.GetHeight(pToAdjustRow->getFrameArea());
+
+ if ( nRowSpan-- == 1 )
+ break;
+
+ pToAdjustRow = pToAdjustRow->GetNext();
+ }
+
+ if ( pToAdjustRow && pToAdjustRow != this )
+ pToAdjustRow->InvalidateSize_();
+
+ const tools::Long nDiff = nSumRowHeight - aRectFnSet.GetHeight(pToAdjust->getFrameArea());
+ if ( nDiff )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ aOldFrame = pToAdjust->getFrameArea();
+#endif
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pToAdjust);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ pNotify = pToAdjust;
+ }
+
+ if ( pNotify )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() && pRootFrame->GetCurrShell() )
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( pNotify, aOldFrame );
+#endif
+
+ pNotify->InvalidatePrt_();
+ }
+
+ pFrame = pFrame->GetNext();
+ }
+ }
+ else
+ { while ( pFrame )
+ {
+ pFrame->InvalidateAll_();
+ pFrame = pFrame->GetNext();
+ }
+ }
+ InvalidatePage();
+}
+
+void SwRowFrame::Cut()
+{
+ SwTabFrame *pTab = FindTabFrame();
+ if ( pTab && pTab->IsFollow() && this == pTab->GetFirstNonHeadlineRow() )
+ {
+ pTab->FindMaster()->InvalidatePos();
+ }
+
+ SwLayoutFrame::Cut();
+}
+
+SwTwips SwRowFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwTwips nReal = 0;
+
+ SwTabFrame* pTab = FindTabFrame();
+ SwRectFnSet aRectFnSet(pTab);
+
+ bool bRestrictTableGrowth;
+ bool bHasFollowFlowLine = pTab->HasFollowFlowLine();
+
+ if ( GetUpper()->IsTabFrame() )
+ {
+ const SwRowFrame* pFollowFlowRow = IsInSplitTableRow();
+ bRestrictTableGrowth = pFollowFlowRow && !pFollowFlowRow->IsRowSpanLine();
+ }
+ else
+ {
+ OSL_ENSURE( GetUpper()->IsCellFrame(), "RowFrame->GetUpper neither table nor cell" );
+ bRestrictTableGrowth = GetFollowRow() && bHasFollowFlowLine;
+ OSL_ENSURE( !bRestrictTableGrowth || !GetNext(),
+ "GetFollowRow for row frame that has a Next" );
+
+ // There may still be some space left in my direct upper:
+ const SwTwips nAdditionalSpace =
+ aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()->GetUpper()) );
+ if ( bRestrictTableGrowth && nAdditionalSpace > 0 )
+ {
+ nReal = std::min( nAdditionalSpace, nDist );
+ nDist -= nReal;
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nReal );
+ }
+ }
+ }
+
+ if ( bRestrictTableGrowth )
+ pTab->SetRestrictTableGrowth( true );
+ else
+ {
+ // Ok, this looks like a hack, indeed, it is a hack.
+ // If the current row frame is inside another cell frame,
+ // and the current row frame has no follow, it should not
+ // be allowed to grow. In fact, setting bRestrictTableGrowth
+ // to 'false' does not work, because the surrounding RowFrame
+ // would set this to 'true'.
+ pTab->SetFollowFlowLine( false );
+ }
+
+ nReal += SwLayoutFrame::GrowFrame( nDist, bTst, bInfo);
+
+ pTab->SetRestrictTableGrowth( false );
+ pTab->SetFollowFlowLine( bHasFollowFlowLine );
+
+ //Update the height of the cells to the newest value.
+ if ( !bTst )
+ {
+ SwRectFnSet fnRectX(this);
+ AdjustCells( fnRectX.GetHeight(getFramePrintArea()) + nReal, true );
+ if ( nReal )
+ SetCompletePaint();
+ }
+
+ return nReal;
+}
+
+SwTwips SwRowFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+ if( HasFixSize() )
+ {
+ AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()), true );
+ return 0;
+ }
+
+ // bInfo may be set to true by SwRowFrame::Format; we need to handle this
+ // here accordingly
+ const bool bShrinkAnyway = bInfo;
+
+ //Only shrink as much as the content of the biggest cell allows.
+ SwTwips nRealDist = nDist;
+ SwFormat* pMod = GetFormat();
+ if (pMod)
+ {
+ const SwFormatFrameSize &rSz = pMod->GetFrameSize();
+ SwTwips nMinHeight = 0;
+ if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
+ nMinHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*this),
+ tools::Long(0));
+
+ // Only necessary to calculate minimal row height if height
+ // of pRow is at least nMinHeight. Otherwise nMinHeight is the
+ // minimum height.
+ if( nMinHeight < aRectFnSet.GetHeight(getFrameArea()) )
+ {
+ // #i26945#
+ OSL_ENSURE( FindTabFrame(), "<SwRowFrame::ShrinkFrame(..)> - no table frame -> crash." );
+ const bool bConsiderObjs( FindTabFrame()->IsConsiderObjsForMinCellHeight() );
+ nMinHeight = lcl_CalcMinRowHeight( this, bConsiderObjs );
+ }
+
+ if ( (aRectFnSet.GetHeight(getFrameArea()) - nRealDist) < nMinHeight )
+ nRealDist = aRectFnSet.GetHeight(getFrameArea()) - nMinHeight;
+ }
+ if ( nRealDist < 0 )
+ nRealDist = 0;
+
+ SwTwips nReal = nRealDist;
+ if ( nReal )
+ {
+ if ( !bTst )
+ {
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nHeight - nReal );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nReal );
+ }
+ }
+
+ SwLayoutFrame* pFrame = GetUpper();
+ SwTwips nTmp = pFrame ? pFrame->Shrink(nReal, bTst) : 0;
+ if ( !bShrinkAnyway && !GetNext() && nTmp != nReal )
+ {
+ //The last one gets the leftover in the upper and therefore takes
+ //care (otherwise: endless loop)
+ if ( !bTst )
+ {
+ nReal -= nTmp;
+ SwTwips nHeight = aRectFnSet.GetHeight(getFrameArea());
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nHeight + nReal );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nReal );
+ }
+ }
+ nReal = nTmp;
+ }
+ }
+
+ // Invalidate appropriately and update the height to the newest value.
+ if ( !bTst )
+ {
+ if ( nReal )
+ {
+ if ( GetNext() )
+ GetNext()->InvalidatePos_();
+ InvalidateAll_();
+ SetCompletePaint();
+
+ SwTabFrame *pTab = FindTabFrame();
+ if ( !pTab->IsRebuildLastLine()
+ && pTab->IsFollow()
+ && this == pTab->GetFirstNonHeadlineRow()
+ && !pTab->IsInRecalcLowerRow() )
+ {
+ SwTabFrame* pMasterTab = pTab->FindMaster();
+ pMasterTab->InvalidatePos();
+ }
+ }
+ AdjustCells( aRectFnSet.GetHeight(getFramePrintArea()) - nReal, true );
+ }
+ return nReal;
+}
+
+bool SwRowFrame::IsRowSplitAllowed() const
+{
+ // Fixed size rows are never allowed to split:
+ if ( HasFixSize() )
+ {
+ OSL_ENSURE( SwFrameSize::Fixed == GetFormat()->GetFrameSize().GetHeightSizeType(), "pRow claims to have fixed size" );
+ return false;
+ }
+
+ // Repeated headlines are never allowed to split:
+ const SwTabFrame* pTabFrame = FindTabFrame();
+ if ( pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
+ pTabFrame->IsInHeadline( *this ) )
+ return false;
+
+ if ( IsForceRowSplitAllowed() )
+ return true;
+
+ const SwTableLineFormat* pFrameFormat = static_cast<SwTableLineFormat*>(GetTabLine()->GetFrameFormat());
+ const SwFormatRowSplit& rLP = pFrameFormat->GetRowSplit();
+ return rLP.GetValue();
+}
+
+bool SwRowFrame::ShouldRowKeepWithNext( const bool bCheckParents ) const
+{
+ // No KeepWithNext if nested in another table
+ if ( GetUpper()->GetUpper()->IsCellFrame() )
+ return false;
+
+ const SwCellFrame* pCell = static_cast<const SwCellFrame*>(Lower());
+ const SwFrame* pText = pCell->Lower();
+
+ return pText && pText->IsTextFrame() &&
+ static_cast<const SwTextFrame*>(pText)->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep(bCheckParents).GetValue();
+}
+
+SwCellFrame::SwCellFrame(const SwTableBox &rBox, SwFrame* pSib, bool bInsertContent)
+ : SwLayoutFrame( rBox.GetFrameFormat(), pSib )
+ , m_pTabBox( &rBox )
+{
+ mnFrameType = SwFrameType::Cell;
+
+ if ( !bInsertContent )
+ return;
+
+ //If a StartIdx is available, ContentFrames are added in the cell, otherwise
+ //Rows have to be present and those are added.
+ if ( rBox.GetSttIdx() )
+ {
+ SwNodeOffset nIndex = rBox.GetSttIdx();
+ ::InsertCnt_( this, rBox.GetFrameFormat()->GetDoc(), ++nIndex );
+ }
+ else
+ {
+ const SwTableLines &rLines = rBox.GetTabLines();
+ SwFrame *pTmpPrev = nullptr;
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ SwRowFrame *pNew = new SwRowFrame( *rLines[i], this, bInsertContent );
+ pNew->InsertBehind( this, pTmpPrev );
+ pTmpPrev = pNew;
+ }
+ }
+}
+
+void SwCellFrame::DestroyImpl()
+{
+ sw::BroadcastingModify* pMod = GetFormat();
+ if( pMod )
+ {
+ // At this stage the lower frames aren't destroyed already,
+ // therefore we have to do a recursive dispose.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->DisposeAccessibleFrame( this, true );
+ }
+#endif
+
+ pMod->Remove( this );
+ if( !pMod->HasWriterListeners() )
+ delete pMod;
+ }
+
+ SwLayoutFrame::DestroyImpl();
+}
+
+SwCellFrame::~SwCellFrame()
+{
+}
+
+static bool lcl_ArrangeLowers( SwLayoutFrame *pLay, tools::Long lYStart, bool bInva )
+{
+ bool bRet = false;
+ SwFrame *pFrame = pLay->Lower();
+ SwRectFnSet aRectFnSet(pLay);
+ while ( pFrame )
+ {
+ tools::Long nFrameTop = aRectFnSet.GetTop(pFrame->getFrameArea());
+ if( nFrameTop != lYStart )
+ {
+ bRet = true;
+ const tools::Long lDiff = aRectFnSet.YDiff( lYStart, nFrameTop );
+ const tools::Long lDiffX = lYStart - nFrameTop;
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
+ aRectFnSet.SubTop( aFrm, -lDiff );
+ aRectFnSet.AddBottom( aFrm, lDiff );
+ }
+
+ pFrame->SetCompletePaint();
+
+ if ( !pFrame->GetNext() )
+ pFrame->SetRetouche();
+ if( bInva )
+ pFrame->Prepare( PrepareHint::FramePositionChanged );
+ if ( pFrame->IsLayoutFrame() && static_cast<SwLayoutFrame*>(pFrame)->Lower() )
+ lcl_ArrangeLowers( static_cast<SwLayoutFrame*>(pFrame),
+ aRectFnSet.GetTop(static_cast<SwLayoutFrame*>(pFrame)->Lower()->getFrameArea())
+ + lDiffX, bInva );
+ if ( pFrame->GetDrawObjs() )
+ {
+ for ( size_t i = 0; i < pFrame->GetDrawObjs()->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ // #i26945# - check, if anchored object
+ // is lower of layout frame by checking, if the anchor
+ // frame, which contains the anchor position, is a lower
+ // of the layout frame.
+ if ( !pLay->IsAnLower( pAnchoredObj->GetAnchorFrameContainingAnchPos() ) )
+ {
+ continue;
+ }
+ // #i52904# - distinguish between anchored
+ // objects, whose vertical position depends on its anchor
+ // frame and whose vertical position is independent
+ // from its anchor frame.
+ bool bVertPosDepOnAnchor( true );
+ {
+ SwFormatVertOrient aVert( pAnchoredObj->GetFrameFormat().GetVertOrient() );
+ switch ( aVert.GetRelationOrient() )
+ {
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ bVertPosDepOnAnchor = false;
+ break;
+ default: break;
+ }
+ }
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+
+ // OD 2004-05-18 #i28701# - no direct move of objects,
+ // which are anchored to-paragraph/to-character, if
+ // the wrapping style influence has to be considered
+ // on the object positioning.
+ // #i52904# - no direct move of objects,
+ // whose vertical position doesn't depend on anchor frame.
+ const bool bDirectMove =
+ FAR_AWAY != pFly->getFrameArea().Top() &&
+ bVertPosDepOnAnchor &&
+ !pFly->ConsiderObjWrapInfluenceOnObjPos();
+ if ( bDirectMove )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFly);
+ aRectFnSet.SubTop( aFrm, -lDiff );
+ aRectFnSet.AddBottom( aFrm, lDiff );
+ }
+
+ pFly->GetVirtDrawObj()->SetBoundAndSnapRectsDirty();
+ // --> OD 2004-08-17 - also notify view of <SdrObject>
+ // instance, which represents the Writer fly frame in
+ // the drawing layer
+ pFly->GetVirtDrawObj()->SetChanged();
+ // #i58280#
+ pFly->InvalidateObjRectWithSpaces();
+ }
+
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ static_cast<SwFlyInContentFrame*>(pFly)->AddRefOfst( lDiff );
+ // #115759# - reset current relative
+ // position to get re-positioned, if not directly moved.
+ if ( !bDirectMove )
+ {
+ pAnchoredObj->SetCurrRelPos( Point( 0, 0 ) );
+ }
+ }
+ else if( pFly->IsAutoPos() )
+ {
+ pFly->AddLastCharY( lDiff );
+ // OD 2004-05-18 #i28701# - follow-up of #i22341#
+ // <mnLastTopOfLine> has also been adjusted.
+ pFly->AddLastTopOfLineY( lDiff );
+ }
+ // #i26945# - re-registration at
+ // page frame of anchor frame, if table frame isn't
+ // a follow table and table frame isn't in its
+ // rebuild of last line.
+ const SwTabFrame* pTabFrame = pLay->FindTabFrame();
+ // - save: check, if table frame is found.
+ if ( pTabFrame &&
+ !( pTabFrame->IsFollow() &&
+ pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
+ pFly->IsFlyFreeFrame() )
+ {
+ SwPageFrame* pPageFrame = pFly->GetPageFrame();
+ SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
+ if ( pPageFrame != pPageOfAnchor )
+ {
+ pFly->InvalidatePos();
+ if ( pPageFrame )
+ pPageFrame->MoveFly( pFly, pPageOfAnchor );
+ else
+ pPageOfAnchor->AppendFlyToPage( pFly );
+ }
+ }
+ // OD 2004-05-11 #i28701# - Because of the introduction
+ // of new positionings and alignments (e.g. aligned at
+ // page area, but anchored at-character), the position
+ // of the Writer fly frame has to be invalidated.
+ pFly->InvalidatePos();
+
+ // #i26945# - follow-up of #i3317#
+ // No arrangement of lowers, if Writer fly frame isn't
+ // moved
+ if ( bDirectMove &&
+ ::lcl_ArrangeLowers( pFly,
+ aRectFnSet.GetPrtTop(*pFly),
+ bInva ) )
+ {
+ pFly->SetCompletePaint();
+ }
+ }
+ else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
+ {
+ // #i26945#
+ const SwTabFrame* pTabFrame = pLay->FindTabFrame();
+ if ( pTabFrame &&
+ !( pTabFrame->IsFollow() &&
+ pTabFrame->FindMaster()->IsRebuildLastLine() ) &&
+ (pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ != RndStdIds::FLY_AS_CHAR))
+ {
+ SwPageFrame* pPageFrame = pAnchoredObj->GetPageFrame();
+ SwPageFrame* pPageOfAnchor = pFrame->FindPageFrame();
+ if ( pPageFrame != pPageOfAnchor )
+ {
+ pAnchoredObj->InvalidateObjPos();
+ if ( pPageFrame )
+ {
+ pPageFrame->RemoveDrawObjFromPage( *pAnchoredObj );
+ }
+ pPageOfAnchor->AppendDrawObjToPage( *pAnchoredObj );
+ }
+ }
+ // #i28701# - adjust last character
+ // rectangle and last top of line.
+ pAnchoredObj->AddLastCharY( lDiff );
+ pAnchoredObj->AddLastTopOfLineY( lDiff );
+ // #i52904# - re-introduce direct move
+ // of drawing objects
+ const bool bDirectMove =
+ static_cast<const SwDrawFrameFormat&>(pAnchoredObj->GetFrameFormat()).IsPosAttrSet() &&
+ bVertPosDepOnAnchor &&
+ !pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos();
+ if ( bDirectMove )
+ {
+ SwObjPositioningInProgress aObjPosInProgress( *pAnchoredObj );
+ if ( aRectFnSet.IsVert() )
+ {
+ pAnchoredObj->DrawObj()->Move( Size( lDiff, 0 ) );
+ }
+ else
+ {
+ pAnchoredObj->DrawObj()->Move( Size( 0, lDiff ) );
+ }
+ // #i58280#
+ pAnchoredObj->InvalidateObjRectWithSpaces();
+ }
+ pAnchoredObj->InvalidateObjPos();
+ }
+ else
+ {
+ OSL_FAIL( "<lcl_ArrangeLowers(..)> - unknown type of anchored object!" );
+ }
+ }
+ }
+ }
+ // Columns and cells are ordered horizontal, not vertical
+ if( !pFrame->IsColumnFrame() && !pFrame->IsCellFrame() )
+ lYStart = aRectFnSet.YInc( lYStart,
+ aRectFnSet.GetHeight(pFrame->getFrameArea()) );
+
+ // Nowadays, the content inside a cell can flow into the follow table.
+ // Thus, the cell may only grow up to the end of the environment.
+ // So the content may have grown, but the cell could not grow.
+ // Therefore we have to trigger a formatting for the frames, which do
+ // not fit into the cell anymore:
+ SwTwips nDistanceToUpperPrtBottom =
+ aRectFnSet.BottomDist( pFrame->getFrameArea(), aRectFnSet.GetPrtBottom(*pLay) );
+ // #i56146# - Revise fix of issue #i26945#
+ // do *not* consider content inside fly frames, if it's an undersized paragraph.
+ // #i26945# - consider content inside fly frames
+ if ( nDistanceToUpperPrtBottom < 0 &&
+ ( ( pFrame->IsInFly() &&
+ ( !pFrame->IsTextFrame() ||
+ !static_cast<SwTextFrame*>(pFrame)->IsUndersized() ) ) ||
+ pFrame->IsInSplitTableRow() ) )
+ {
+ pFrame->InvalidatePos();
+ }
+
+ pFrame = pFrame->GetNext();
+ }
+ return bRet;
+}
+
+void SwCellFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "CellFrame::Format, pAttrs is 0." );
+ const SwTabFrame* pTab = FindTabFrame();
+ SwRectFnSet aRectFnSet(pTab);
+
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+
+ //Adjust position.
+ if ( Lower() )
+ {
+ SwTwips nTopSpace, nBottomSpace, nLeftSpace, nRightSpace;
+ // #i29550#
+ if ( pTab->IsCollapsingBorders() && !Lower()->IsRowFrame() )
+ {
+ const SvxBoxItem& rBoxItem = pAttrs->GetBox();
+ nLeftSpace = rBoxItem.GetDistance( SvxBoxItemLine::LEFT );
+ nRightSpace = rBoxItem.GetDistance( SvxBoxItemLine::RIGHT );
+ nTopSpace = static_cast<SwRowFrame*>(GetUpper())->GetTopMarginForLowers();
+ nBottomSpace = static_cast<SwRowFrame*>(GetUpper())->GetBottomMarginForLowers();
+ }
+ else
+ {
+ // OD 23.01.2003 #106895# - add 1st param to <SwBorderAttrs::CalcRight(..)>
+ nLeftSpace = pAttrs->CalcLeft( this );
+ nRightSpace = pAttrs->CalcRight( this );
+ nTopSpace = pAttrs->CalcTop();
+ nBottomSpace = pAttrs->CalcBottom();
+ }
+ aRectFnSet.SetXMargins( *this, nLeftSpace, nRightSpace );
+ aRectFnSet.SetYMargins( *this, nTopSpace, nBottomSpace );
+ }
+ }
+ // #i26945#
+ tools::Long nRemaining = GetTabBox()->getRowSpan() >= 1 ?
+ ::lcl_CalcMinCellHeight( this, pTab->IsConsiderObjsForMinCellHeight(), pAttrs ) :
+ 0;
+ if ( !isFrameAreaSizeValid() )
+ {
+ setFrameAreaSizeValid(true);
+
+ //The VarSize of the CellFrames is always the width.
+ //The width is not variable though, it is defined by the format.
+ //This predefined value however does not necessary match the actual
+ //width. The width is calculated based on the attribute, the value in
+ //the attribute matches the desired value of the TabFrame. Changes which
+ //were done there are taken into account here proportionately.
+ //If the cell doesn't have a neighbour anymore, it does not take the
+ //attribute into account and takes the rest of the upper instead.
+ SwTwips nWidth;
+ if ( GetNext() )
+ {
+ const SwTwips nWish = pTab->GetFormat()->GetFrameSize().GetWidth();
+ nWidth = pAttrs->GetSize().Width();
+
+ OSL_ENSURE( nWish, "Table without width?" );
+ OSL_ENSURE( nWidth <= nWish, "Width of cell larger than table." );
+ OSL_ENSURE( nWidth > 0, "Box without width" );
+
+ const tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
+ if ( nWish != nPrtWidth )
+ {
+ // Avoid rounding problems, at least for the new table model
+ if ( pTab->GetTable()->IsNewModel() )
+ {
+ // 1. sum of widths of cells up to this cell (in model)
+ const SwTableLine* pTabLine = GetTabBox()->GetUpper();
+ const SwTableBoxes& rBoxes = pTabLine->GetTabBoxes();
+ const SwTableBox* pTmpBox = nullptr;
+
+ SwTwips nSumWidth = 0;
+ size_t i = 0;
+ do
+ {
+ pTmpBox = rBoxes[ i++ ];
+ nSumWidth += pTmpBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ while ( pTmpBox != GetTabBox() );
+
+ // 2. calculate actual width of cells up to this one
+ double nTmpWidth = nSumWidth;
+ nTmpWidth *= nPrtWidth;
+ nTmpWidth /= nWish;
+ nWidth = static_cast<SwTwips>(nTmpWidth);
+
+ // 3. calculate frame widths of cells up to this one:
+ const SwFrame* pTmpCell = static_cast<const SwLayoutFrame*>(GetUpper())->Lower();
+ SwTwips nSumFrameWidths = 0;
+ while ( pTmpCell != this )
+ {
+ nSumFrameWidths += aRectFnSet.GetWidth(pTmpCell->getFrameArea());
+ pTmpCell = pTmpCell->GetNext();
+ }
+
+ nWidth = nWidth - nSumFrameWidths;
+ }
+ else
+ {
+ // #i12092# use double instead of long,
+ // otherwise this could lead to overflows
+ double nTmpWidth = nWidth;
+ nTmpWidth *= nPrtWidth;
+ nTmpWidth /= nWish;
+ nWidth = static_cast<SwTwips>(nTmpWidth);
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( pAttrs->GetSize().Width() > 0, "Box without width" );
+ nWidth = aRectFnSet.GetWidth(GetUpper()->getFramePrintArea());
+ SwFrame *pPre = GetUpper()->Lower();
+ while ( pPre != this )
+ {
+ nWidth -= aRectFnSet.GetWidth(pPre->getFrameArea());
+ pPre = pPre->GetNext();
+ }
+ }
+
+ const tools::Long nDiff = nWidth - aRectFnSet.GetWidth(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if( IsNeighbourFrame() && IsRightToLeft() )
+ {
+ aRectFnSet.SubLeft( aFrm, nDiff );
+ }
+ else
+ {
+ aRectFnSet.AddRight( aFrm, nDiff );
+ }
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.AddRight( aPrt, nDiff );
+ }
+
+ //Adjust the height, it's defined through the content and the margins.
+ const tools::Long nDiffHeight = nRemaining - aRectFnSet.GetHeight(getFrameArea());
+ if ( nDiffHeight )
+ {
+ if ( nDiffHeight > 0 )
+ {
+ //Validate again if no growth happened. Invalidation is done
+ //through AdjustCells of the row.
+ if ( !Grow( nDiffHeight ) )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ }
+ else
+ {
+ // Only keep invalidated if shrinking was actually done; the
+ // attempt can be ignored because all horizontally adjoined
+ // cells have to be the same height.
+ if ( !Shrink( -nDiffHeight ) )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ }
+ }
+ }
+ const SwFormatVertOrient &rOri = pAttrs->GetAttrSet().GetVertOrient();
+
+ if ( !Lower() )
+ return;
+
+ // From now on, all operations are related to the table cell.
+ aRectFnSet.Refresh(this);
+
+ SwPageFrame* pPg = nullptr;
+ if ( !FindTabFrame()->IsRebuildLastLine() && text::VertOrientation::NONE != rOri.GetVertOrient() &&
+ // #158225# no vertical alignment of covered cells
+ !IsCoveredCell() &&
+ (pPg = FindPageFrame())!=nullptr )
+ {
+ if ( !Lower()->IsContentFrame() && !Lower()->IsSctFrame() && !Lower()->IsTabFrame() )
+ {
+ // OSL_ENSURE(for HTML-import!
+ OSL_ENSURE( false, "VAlign to cell without content" );
+ return;
+ }
+ bool bVertDir = true;
+ // #i43913# - no vertical alignment, if wrapping
+ // style influence is considered on object positioning and
+ // an object is anchored inside the cell.
+ const bool bConsiderWrapOnObjPos( GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) );
+ // No alignment if fly with wrap overlaps the cell.
+ if ( pPg->GetSortedObjs() )
+ {
+ SwRect aRect( getFramePrintArea() ); aRect += getFrameArea().Pos();
+ for (SwAnchoredObject* pAnchoredObj : *pPg->GetSortedObjs())
+ {
+ SwRect aTmp( pAnchoredObj->GetObjRect() );
+ const SwFrame* pAnch = pAnchoredObj->GetAnchorFrame();
+ if ( (bConsiderWrapOnObjPos && IsAnLower( pAnch )) || (!bConsiderWrapOnObjPos && aTmp.Overlaps( aRect )) )
+ {
+ const SwFrameFormat& rAnchoredObjFrameFormat = pAnchoredObj->GetFrameFormat();
+ const SwFormatSurround &rSur = rAnchoredObjFrameFormat.GetSurround();
+
+ if ( bConsiderWrapOnObjPos || css::text::WrapTextMode_THROUGH != rSur.GetSurround() )
+ {
+ // frames, which the cell is a lower of, aren't relevant
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsAnLower( this ) )
+ continue;
+ }
+
+ // #i43913#
+ // #i52904# - no vertical alignment,
+ // if object, anchored inside cell, has temporarily
+ // consider its wrapping style on object positioning.
+ // #i58806# - no vertical alignment
+ // if object does not follow the text flow.
+ if ( bConsiderWrapOnObjPos ||
+ !IsAnLower( pAnch ) ||
+ pAnchoredObj->IsTmpConsiderWrapInfluence() ||
+ !rAnchoredObjFrameFormat.GetFollowTextFlow().GetValue() )
+ {
+ bVertDir = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( ( bVertDir && ( nRemaining -= lcl_CalcTopAndBottomMargin( *this, *pAttrs ) ) < nPrtHeight ) ||
+ aRectFnSet.GetTop(Lower()->getFrameArea()) != aRectFnSet.GetPrtTop(*this) )
+ {
+ tools::Long nDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nRemaining;
+ if ( nDiff >= 0 )
+ {
+ tools::Long lTopOfst = 0;
+ if ( bVertDir )
+ {
+ switch ( rOri.GetVertOrient() )
+ {
+ case text::VertOrientation::CENTER: lTopOfst = nDiff / 2; break;
+ case text::VertOrientation::BOTTOM: lTopOfst = nDiff; break;
+ default: break;
+ }
+ }
+ tools::Long nTmp = aRectFnSet.YInc(
+ aRectFnSet.GetPrtTop(*this), lTopOfst );
+ if ( lcl_ArrangeLowers( this, nTmp, !bVertDir ) )
+ SetCompletePaint();
+ }
+ }
+ }
+ else
+ {
+ //Was an old alignment taken into account?
+ if ( Lower()->IsContentFrame() )
+ {
+ const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
+ lcl_ArrangeLowers( this, lYStart, true );
+ }
+ }
+
+ // Handle rotated portions of lowers: it's possible that we have changed amount of vertical
+ // space since the last format, and this affects how many rotated portions we need. So throw
+ // away the current portions to build them using the new line width.
+ for (SwFrame* pFrame = Lower(); pFrame; pFrame = pFrame->GetNext())
+ {
+ if (!pFrame->IsTextFrame())
+ {
+ continue;
+ }
+
+ auto pTextFrame = static_cast<SwTextFrame*>(pFrame);
+ if (!pTextFrame->GetHasRotatedPortions())
+ {
+ continue;
+ }
+
+ pTextFrame->Prepare();
+ }
+}
+
+void SwCellFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if(auto pNewFormatHint = dynamic_cast<const sw::TableBoxFormatChanged*>(&rHint))
+ {
+ if(GetTabBox() != &pNewFormatHint->m_rTableBox)
+ return;
+ RegisterToFormat(const_cast<SwTableBoxFormat&>(pNewFormatHint->m_rNewFormat));
+ InvalidateSize();
+ InvalidatePrt_();
+ SetCompletePaint();
+ SetDerivedVert(false);
+ CheckDirChange();
+
+ // #i47489#
+ // make sure that the row will be formatted, in order
+ // to have the correct Get(Top|Bottom)MarginForLowers values
+ // set at the row.
+ const SwTabFrame* pTab = FindTabFrame();
+ if(pTab && pTab->IsCollapsingBorders())
+ {
+ SwFrame* pRow = GetUpper();
+ pRow->InvalidateSize_();
+ pRow->InvalidatePrt_();
+ }
+ }
+ else if(auto pMoveTableBoxHint = dynamic_cast<const sw::MoveTableBoxHint*>(&rHint))
+ {
+ if(GetTabBox() != &pMoveTableBoxHint->m_rTableBox)
+ return;
+ const_cast<SwFrameFormat*>(&pMoveTableBoxHint->m_rNewFormat)->Add(this);
+ InvalidateAll();
+ ReinitializeFrameSizeAttrFlags();
+ SetDerivedVert(false);
+ CheckDirChange();
+ return;
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const SfxPoolItem* pVertOrientItem = nullptr;
+ const SfxPoolItem* pProtectItem = nullptr;
+ const SfxPoolItem* pFrameDirItem = nullptr;
+ const SfxPoolItem* pBoxItem = nullptr;
+ const auto nWhich = pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0;
+ switch(nWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ auto& rChgSet = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
+ pVertOrientItem = rChgSet.GetItemIfSet(RES_VERT_ORIENT, false);
+ pProtectItem = rChgSet.GetItemIfSet(RES_PROTECT, false);
+ pFrameDirItem = rChgSet.GetItemIfSet(RES_FRAMEDIR, false);
+ pBoxItem = rChgSet.GetItemIfSet(RES_BOX, false);
+ break;
+ }
+ case RES_VERT_ORIENT:
+ pVertOrientItem = pLegacy->m_pNew;
+ break;
+ case RES_PROTECT:
+ pProtectItem = pLegacy->m_pNew;
+ break;
+ case RES_FRAMEDIR:
+ pFrameDirItem = pLegacy->m_pNew;
+ break;
+ case RES_BOX:
+ pBoxItem = pLegacy->m_pNew;
+ break;
+ }
+ if(pVertOrientItem)
+ {
+ bool bInva = true;
+ const auto eVertOrient = static_cast<const SwFormatVertOrient*>(pVertOrientItem)->GetVertOrient();
+ if(text::VertOrientation::NONE == eVertOrient && Lower() && Lower()->IsContentFrame())
+ {
+ SwRectFnSet aRectFnSet(this);
+ const tools::Long lYStart = aRectFnSet.GetPrtTop(*this);
+ bInva = lcl_ArrangeLowers(this, lYStart, false);
+ }
+ if (bInva)
+ {
+ SetCompletePaint();
+ InvalidatePrt();
+ }
+ }
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if(pProtectItem)
+ {
+ SwViewShell* pSh = getRootFrame()->GetCurrShell();
+ if(pSh && pSh->GetLayout()->IsAnyShellAccessible())
+ pSh->Imp()->InvalidateAccessibleEditableState(true, this);
+ }
+#endif
+ if(pFrameDirItem)
+ {
+ SetDerivedVert(false);
+ CheckDirChange();
+ }
+ // #i29550#
+ if(pBoxItem)
+ {
+ SwFrame* pTmpUpper = GetUpper();
+ while(pTmpUpper->GetUpper() && !pTmpUpper->GetUpper()->IsTabFrame())
+ pTmpUpper = pTmpUpper->GetUpper();
+
+ SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pTmpUpper->GetUpper());
+ if(pTabFrame->IsCollapsingBorders())
+ {
+ // Invalidate lowers of this and next row:
+ lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
+ pTmpUpper = pTmpUpper->GetNext();
+ if(pTmpUpper)
+ lcl_InvalidateAllLowersPrt(static_cast<SwRowFrame*>(pTmpUpper));
+ else
+ pTabFrame->InvalidatePrt();
+ }
+ }
+ SwLayoutFrame::SwClientNotify(rMod, rHint);
+ }
+}
+
+tools::Long SwCellFrame::GetLayoutRowSpan() const
+{
+ const SwTableBox *pTabBox = GetTabBox();
+ tools::Long nRet = pTabBox ? pTabBox->getRowSpan() : 0;
+ if ( nRet < 1 )
+ {
+ const SwFrame* pRow = GetUpper();
+ const SwTabFrame* pTab = pRow ? static_cast<const SwTabFrame*>(pRow->GetUpper()) : nullptr;
+
+ if ( pTab && pTab->IsFollow() && pRow == pTab->GetFirstNonHeadlineRow() )
+ nRet = -nRet;
+ }
+ return nRet;
+}
+
+const SwCellFrame* SwCellFrame::GetCoveredCellInRow(const SwRowFrame& rRow) const
+{
+ if (GetLayoutRowSpan() <= 1)
+ {
+ // Not merged vertically.
+ return nullptr;
+ }
+
+ for (const SwFrame* pCell = rRow.GetLower(); pCell; pCell = pCell->GetNext())
+ {
+ if (!pCell->IsCellFrame())
+ {
+ continue;
+ }
+
+ auto pCellFrame = static_cast<const SwCellFrame*>(pCell);
+ if (!pCellFrame->IsCoveredCell())
+ {
+ continue;
+ }
+
+ if (pCellFrame->getFrameArea().Left() != getFrameArea().Left())
+ {
+ continue;
+ }
+
+ if (pCellFrame->getFrameArea().Width() != getFrameArea().Width())
+ {
+ continue;
+ }
+
+ // pCellFrame is covered, there are only covered cell frames between "this" and pCellFrame
+ // and the horizontal position/size matches "this".
+ return pCellFrame;
+ }
+
+ return nullptr;
+}
+
+std::vector<const SwCellFrame*> SwCellFrame::GetCoveredCells() const
+{
+ std::vector<const SwCellFrame*> aRet;
+ if (GetLayoutRowSpan() <= 1)
+ {
+ return aRet;
+ }
+
+ if (!GetUpper()->IsRowFrame())
+ {
+ return aRet;
+ }
+
+ auto pFirstRowFrame = static_cast<const SwRowFrame*>(GetUpper());
+ if (!pFirstRowFrame->GetNext())
+ {
+ return aRet;
+ }
+
+ if (!pFirstRowFrame->GetNext()->IsRowFrame())
+ {
+ return aRet;
+ }
+
+ for (const SwFrame* pRow = pFirstRowFrame->GetNext(); pRow; pRow = pRow->GetNext())
+ {
+ if (!pRow->IsRowFrame())
+ {
+ continue;
+ }
+
+ auto pRowFrame = static_cast<const SwRowFrame*>(pRow);
+ const SwCellFrame* pCovered = GetCoveredCellInRow(*pRowFrame);
+ if (!pCovered)
+ {
+ continue;
+ }
+
+ // Found a cell in a next row that is covered by "this".
+ aRet.push_back(pCovered);
+ }
+
+ return aRet;
+}
+
+void SwCellFrame::dumpAsXmlAttributes(xmlTextWriterPtr pWriter) const
+{
+ SwFrame::dumpAsXmlAttributes(pWriter);
+ if (SwCellFrame* pFollow = GetFollowCell())
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFollow->GetFrameId());
+
+ if (SwCellFrame* pPrevious = GetPreviousCell())
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("precede"), "%" SAL_PRIuUINT32, pPrevious->GetFrameId());
+}
+
+// #i103961#
+void SwCellFrame::Cut()
+{
+ // notification for accessibility
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() )
+ {
+ SwViewShell* pVSh = pRootFrame->GetCurrShell();
+ if ( pVSh && pVSh->Imp() )
+ {
+ pVSh->Imp()->DisposeAccessibleFrame( this );
+ }
+ }
+ }
+#endif
+
+ SwLayoutFrame::Cut();
+}
+
+// Helper functions for repeated headlines:
+
+bool SwTabFrame::IsInHeadline( const SwFrame& rFrame ) const
+{
+ OSL_ENSURE( IsAnLower( &rFrame ) && rFrame.IsInTab(),
+ "SwTabFrame::IsInHeadline called for frame not lower of table" );
+
+ const SwFrame* pTmp = &rFrame;
+ while ( !pTmp->GetUpper()->IsTabFrame() )
+ pTmp = pTmp->GetUpper();
+
+ return GetTable()->IsHeadline( *static_cast<const SwRowFrame*>(pTmp)->GetTabLine() );
+}
+
+/*
+ * If this is a master table, we can may assume, that there are at least
+ * nRepeat lines in the table.
+ * If this is a follow table, there are intermediate states for the table
+ * layout, e.g., during deletion of rows, which makes it necessary to find
+ * the first non-headline row by evaluating the headline flag at the row frame.
+ */
+SwRowFrame* SwTabFrame::GetFirstNonHeadlineRow() const
+{
+ SwRowFrame* pRet = const_cast<SwRowFrame*>(static_cast<const SwRowFrame*>(Lower()));
+ if ( pRet )
+ {
+ if ( IsFollow() )
+ {
+ while ( pRet && pRet->IsRepeatedHeadline() )
+ pRet = static_cast<SwRowFrame*>(pRet->GetNext());
+ }
+ else
+ {
+ sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+ while ( pRet && nRepeat > 0 )
+ {
+ pRet = static_cast<SwRowFrame*>(pRet->GetNext());
+ --nRepeat;
+ }
+ }
+ }
+
+ return pRet;
+}
+
+bool SwTable::IsHeadline( const SwTableLine& rLine ) const
+{
+ for ( sal_uInt16 i = 0; i < GetRowsToRepeat(); ++i )
+ if ( GetTabLines()[ i ] == &rLine )
+ return true;
+
+ return false;
+}
+
+bool SwTabFrame::IsLayoutSplitAllowed() const
+{
+ return GetFormat()->GetLayoutSplit().GetValue();
+}
+
+// #i29550#
+
+sal_uInt16 SwTabFrame::GetBottomLineSize() const
+{
+ OSL_ENSURE( IsCollapsingBorders(),
+ "BottomLineSize only required for collapsing borders" );
+
+ OSL_ENSURE( Lower(), "Warning! Trying to prevent a crash" );
+
+ const SwFrame* pTmp = GetLastLower();
+
+ // #124755# Try to make code robust
+ if ( !pTmp ) return 0;
+
+ return static_cast<const SwRowFrame*>(pTmp)->GetBottomLineSize();
+}
+
+bool SwTabFrame::IsCollapsingBorders() const
+{
+ return GetFormat()->GetAttrSet().Get( RES_COLLAPSING_BORDERS ).GetValue();
+}
+
+/// Local helper function to calculate height of first text row
+static SwTwips lcl_CalcHeightOfFirstContentLine( const SwRowFrame& rSourceLine )
+{
+ // Find corresponding split line in master table
+ const SwTabFrame* pTab = rSourceLine.FindTabFrame();
+ SwRectFnSet aRectFnSet(pTab);
+ const SwCellFrame* pCurrSourceCell = static_cast<const SwCellFrame*>(rSourceLine.Lower());
+
+ // 1. Case: rSourceLine is a follow flow line.
+ // In this case we have to return the minimum of the heights
+ // of the first lines in rSourceLine.
+
+ // 2. Case: rSourceLine is not a follow flow line.
+ // In this case we have to return the maximum of the heights
+ // of the first lines in rSourceLine.
+ bool bIsInFollowFlowLine = rSourceLine.IsInFollowFlowRow();
+ SwTwips nHeight = bIsInFollowFlowLine ? LONG_MAX : 0;
+
+ while ( pCurrSourceCell )
+ {
+ // NEW TABLES
+ // Skip cells which are not responsible for the height of
+ // the follow flow line:
+ if ( bIsInFollowFlowLine && pCurrSourceCell->GetLayoutRowSpan() > 1 )
+ {
+ pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
+ continue;
+ }
+
+ const SwFrame *pTmp = pCurrSourceCell->Lower();
+ if ( pTmp )
+ {
+ SwTwips nTmpHeight = USHRT_MAX;
+ // #i32456# Consider lower row frames
+ if ( pTmp->IsRowFrame() )
+ {
+ const SwRowFrame* pTmpSourceRow = static_cast<const SwRowFrame*>(pCurrSourceCell->Lower());
+ nTmpHeight = lcl_CalcHeightOfFirstContentLine( *pTmpSourceRow );
+ }
+ else if (pTmp->IsTabFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTabFrame()))
+ {
+ SwTabFrame const*const pTabFrame(pTmp->IsTabFrame()
+ ? static_cast<SwTabFrame const*>(pTmp)
+ : static_cast<SwTabFrame const*>(pTmp->GetLower()));
+ nTmpHeight = pTabFrame->CalcHeightOfFirstContentLine();
+ }
+ else if (pTmp->IsTextFrame() || (pTmp->IsSctFrame() && pTmp->GetLower() && pTmp->GetLower()->IsTextFrame()))
+ {
+ // Section frames don't influence the size/position of text
+ // frames, so 'text frame' and 'text frame in section frame' is
+ // the same case.
+ SwTextFrame* pTextFrame = nullptr;
+ if (pTmp->IsTextFrame())
+ pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp));
+ else
+ pTextFrame = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp->GetLower()));
+ pTextFrame->GetFormatted();
+ nTmpHeight = pTextFrame->FirstLineHeight();
+ }
+
+ if ( USHRT_MAX != nTmpHeight )
+ {
+ const SwCellFrame* pPrevCell = pCurrSourceCell->GetPreviousCell();
+ if ( pPrevCell )
+ {
+ // If we are in a split row, there may be some space
+ // left in the cell frame of the master row.
+ // We look for the minimum of all first line heights;
+ SwTwips nReal = aRectFnSet.GetHeight(pPrevCell->getFramePrintArea());
+ const SwFrame* pFrame = pPrevCell->Lower();
+ const SwFrame* pLast = pFrame;
+ while ( pFrame )
+ {
+ nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pLast = pFrame;
+ pFrame = pFrame->GetNext();
+ }
+
+ // #i26831#, #i26520#
+ // The additional lower space of the current last.
+ // #115759# - do *not* consider the
+ // additional lower space for 'master' text frames
+ if ( pLast && pLast->IsFlowFrame() &&
+ ( !pLast->IsTextFrame() ||
+ !static_cast<const SwTextFrame*>(pLast)->GetFollow() ) )
+ {
+ nReal += SwFlowFrame::CastFlowFrame(pLast)->CalcAddLowerSpaceAsLastInTableCell();
+ }
+ // Don't forget the upper space and lower space,
+ // #115759# - do *not* consider the upper
+ // and the lower space for follow text frames.
+ if ( pTmp->IsFlowFrame() &&
+ ( !pTmp->IsTextFrame() ||
+ !static_cast<const SwTextFrame*>(pTmp)->IsFollow() ) )
+ {
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace( nullptr, pLast);
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
+ }
+ // #115759# - consider additional lower
+ // space of <pTmp>, if contains only one line.
+ // In this case it would be the new last text frame, which
+ // would have no follow and thus would add this space.
+ if ( pTmp->IsTextFrame() &&
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pTmp))
+ ->GetLineCount(TextFrameIndex(COMPLETE_STRING)) == 1)
+ {
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)
+ ->CalcAddLowerSpaceAsLastInTableCell();
+ }
+ if ( nReal > 0 )
+ nTmpHeight -= nReal;
+ }
+ else
+ {
+ // pFirstRow is not a FollowFlowRow. In this case,
+ // we look for the maximum of all first line heights:
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pCurrSourceCell );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+ nTmpHeight += rAttrs.CalcTop() + rAttrs.CalcBottom();
+ // #i26250#
+ // Don't forget the upper space and lower space,
+ if ( pTmp->IsFlowFrame() )
+ {
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcUpperSpace();
+ nTmpHeight += SwFlowFrame::CastFlowFrame(pTmp)->CalcLowerSpace();
+ }
+ }
+ }
+
+ if ( bIsInFollowFlowLine )
+ {
+ // minimum
+ if ( nTmpHeight < nHeight )
+ nHeight = nTmpHeight;
+ }
+ else
+ {
+ // maximum
+ if ( nTmpHeight > nHeight && USHRT_MAX != nTmpHeight )
+ nHeight = nTmpHeight;
+ }
+ }
+
+ pCurrSourceCell = static_cast<const SwCellFrame*>(pCurrSourceCell->GetNext());
+ }
+
+ return ( LONG_MAX == nHeight ) ? 0 : nHeight;
+}
+
+/// Function to calculate height of first text row
+SwTwips SwTabFrame::CalcHeightOfFirstContentLine() const
+{
+ SwRectFnSet aRectFnSet(this);
+
+ const bool bDontSplit = !IsFollow() && !GetFormat()->GetLayoutSplit().GetValue();
+
+ if ( bDontSplit )
+ {
+ // Table is not allowed to split: Take the whole height, that's all
+ return aRectFnSet.GetHeight(getFrameArea());
+ }
+
+ SwTwips nTmpHeight = 0;
+
+ const SwRowFrame* pFirstRow = GetFirstNonHeadlineRow();
+ OSL_ENSURE( !IsFollow() || pFirstRow, "FollowTable without Lower" );
+
+ // NEW TABLES
+ if ( pFirstRow && pFirstRow->IsRowSpanLine() && pFirstRow->GetNext() )
+ pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
+
+ // Calculate the height of the headlines:
+ const sal_uInt16 nRepeat = GetTable()->GetRowsToRepeat();
+ SwTwips nRepeatHeight = nRepeat ? lcl_GetHeightOfRows( GetLower(), nRepeat ) : 0;
+
+ // Calculate the height of the keeping lines
+ // (headlines + following keeping lines):
+ SwTwips nKeepHeight = nRepeatHeight;
+ if ( GetFormat()->GetDoc()->GetDocumentSettingManager().get(DocumentSettingId::TABLE_ROW_KEEP) )
+ {
+ sal_uInt16 nKeepRows = nRepeat;
+
+ // Check how many rows want to keep together
+ while ( pFirstRow && pFirstRow->ShouldRowKeepWithNext() )
+ {
+ ++nKeepRows;
+ pFirstRow = static_cast<const SwRowFrame*>(pFirstRow->GetNext());
+ }
+
+ if ( nKeepRows > nRepeat )
+ nKeepHeight = lcl_GetHeightOfRows( GetLower(), nKeepRows );
+ }
+
+ // For master tables, the height of the headlines + the height of the
+ // keeping lines (if any) has to be considered. For follow tables, we
+ // only consider the height of the keeping rows without the repeated lines:
+ if ( !IsFollow() )
+ {
+ nTmpHeight = nKeepHeight;
+ }
+ else
+ {
+ nTmpHeight = nKeepHeight - nRepeatHeight;
+ }
+
+ // pFirstRow row is the first non-heading row.
+ // nTmpHeight is the height of the heading row if we are a follow.
+ if ( pFirstRow )
+ {
+ const bool bSplittable = pFirstRow->IsRowSplitAllowed();
+ const SwTwips nFirstLineHeight = aRectFnSet.GetHeight(pFirstRow->getFrameArea());
+
+ if ( !bSplittable )
+ {
+ // pFirstRow is not splittable, but it is still possible that the line height of pFirstRow
+ // actually is determined by a lower cell with rowspan = -1. In this case we should not
+ // just return the height of the first line. Basically we need to get the height of the
+ // line as it would be on the last page. Since this is quite complicated to calculate,
+ // we only calculate the height of the first line.
+ SwFormatFrameSize const& rFrameSize(pFirstRow->GetAttrSet()->GetFrameSize());
+ if ( pFirstRow->GetPrev() &&
+ static_cast<const SwRowFrame*>(pFirstRow->GetPrev())->IsRowSpanLine()
+ && rFrameSize.GetHeightSizeType() != SwFrameSize::Fixed)
+ {
+ // Calculate maximum height of all cells with rowspan = 1:
+ SwTwips nMaxHeight = rFrameSize.GetHeightSizeType() == SwFrameSize::Minimum
+ ? rFrameSize.GetHeight()
+ : 0;
+ const SwCellFrame* pLower2 = static_cast<const SwCellFrame*>(pFirstRow->Lower());
+ while ( pLower2 )
+ {
+ if ( 1 == pLower2->GetTabBox()->getRowSpan() )
+ {
+ const SwTwips nCellHeight = lcl_CalcMinCellHeight( pLower2, true );
+ nMaxHeight = std::max( nCellHeight, nMaxHeight );
+ }
+ pLower2 = static_cast<const SwCellFrame*>(pLower2->GetNext());
+ }
+ nTmpHeight += nMaxHeight;
+ }
+ else
+ {
+ nTmpHeight += nFirstLineHeight;
+ }
+ }
+
+ // Optimization: lcl_CalcHeightOfFirstContentLine actually can trigger
+ // a formatting of the row frame (via the GetFormatted()). We don't
+ // want this formatting if the row does not have a height.
+ else if ( 0 != nFirstLineHeight )
+ {
+ const bool bOldJoinLock = IsJoinLocked();
+ const_cast<SwTabFrame*>(this)->LockJoin();
+ const SwTwips nHeightOfFirstContentLine = lcl_CalcHeightOfFirstContentLine( *pFirstRow );
+
+ // Consider minimum row height:
+ const SwFormatFrameSize &rSz = pFirstRow->GetFormat()->GetFrameSize();
+
+ SwTwips nMinRowHeight = 0;
+ if (rSz.GetHeightSizeType() == SwFrameSize::Minimum)
+ {
+ nMinRowHeight = std::max(rSz.GetHeight() - lcl_calcHeightOfRowBeforeThisFrame(*pFirstRow),
+ tools::Long(0));
+ }
+
+ nTmpHeight += std::max( nHeightOfFirstContentLine, nMinRowHeight );
+
+ if ( !bOldJoinLock )
+ const_cast<SwTabFrame*>(this)->UnlockJoin();
+ }
+ }
+
+ return nTmpHeight;
+}
+
+// Some more functions for covered/covering cells. This way inclusion of
+// SwCellFrame can be avoided
+
+bool SwFrame::IsLeaveUpperAllowed() const
+{
+ return false;
+}
+
+bool SwCellFrame::IsLeaveUpperAllowed() const
+{
+ return GetLayoutRowSpan() > 1;
+}
+
+bool SwFrame::IsCoveredCell() const
+{
+ return false;
+}
+
+bool SwCellFrame::IsCoveredCell() const
+{
+ return GetLayoutRowSpan() < 1;
+}
+
+bool SwFrame::IsInCoveredCell() const
+{
+ bool bRet = false;
+
+ const SwFrame* pThis = this;
+ while ( pThis && !pThis->IsCellFrame() )
+ pThis = pThis->GetUpper();
+
+ if ( pThis )
+ bRet = pThis->IsCoveredCell();
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/trvlfrm.cxx b/sw/source/core/layout/trvlfrm.cxx
new file mode 100644
index 000000000..375b9fe34
--- /dev/null
+++ b/sw/source/core/layout/trvlfrm.cxx
@@ -0,0 +1,2654 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hints.hxx>
+#include <comphelper/flagguard.hxx>
+#include <tools/line.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/protitem.hxx>
+#include <vcl/settings.hxx>
+#include <fmtpdsc.hxx>
+#include <fmtsrnd.hxx>
+#include <pagedesc.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <ftnfrm.hxx>
+#include <flyfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <viewopt.hxx>
+#include <DocumentSettingManager.hxx>
+#include <viscrs.hxx>
+#include <dflyobj.hxx>
+#include <crstate.hxx>
+#include <dcontact.hxx>
+#include <sortedobjs.hxx>
+#include <txatbase.hxx>
+#include <fmtfld.hxx>
+#include <fldbas.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <undobj.hxx>
+
+#include <swselectionlist.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+namespace {
+ bool lcl_GetModelPositionForViewPoint_Objects( const SwPageFrame* pPageFrame, bool bSearchBackground,
+ SwPosition *pPos, Point const & rPoint, SwCursorMoveState* pCMS )
+ {
+ bool bRet = false;
+ Point aPoint( rPoint );
+ SwOrderIter aIter( pPageFrame );
+ aIter.Top();
+ while ( aIter() )
+ {
+ const SwVirtFlyDrawObj* pObj =
+ static_cast<const SwVirtFlyDrawObj*>(aIter());
+ const SwAnchoredObject* pAnchoredObj = GetUserCall( aIter() )->GetAnchoredObj( aIter() );
+ const SwFormatSurround& rSurround = pAnchoredObj->GetFrameFormat().GetSurround();
+ const SvxOpaqueItem& rOpaque = pAnchoredObj->GetFrameFormat().GetOpaque();
+ bool bInBackground = ( rSurround.GetSurround() == css::text::WrapTextMode_THROUGH ) && !rOpaque.GetValue();
+
+ bool bBackgroundMatches = bInBackground == bSearchBackground;
+
+ const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
+ if ( pFly && bBackgroundMatches &&
+ ( ( pCMS && pCMS->m_bSetInReadOnly ) ||
+ !pFly->IsProtected() ) &&
+ pFly->GetModelPositionForViewPoint( pPos, aPoint, pCMS ) )
+ {
+ bRet = true;
+ break;
+ }
+
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ aIter.Prev();
+ }
+ return bRet;
+ }
+
+ double lcl_getDistance( const SwRect& rRect, const Point& rPoint )
+ {
+ double nDist = 0.0;
+
+ // If the point is inside the rectangle, then distance is 0
+ // Otherwise, compute the distance to the center of the rectangle.
+ if ( !rRect.Contains( rPoint ) )
+ {
+ tools::Line aLine( rPoint, rRect.Center( ) );
+ nDist = aLine.GetLength( );
+ }
+
+ return nDist;
+ }
+}
+
+namespace {
+
+//For SwFlyFrame::GetModelPositionForViewPoint
+class SwCursorOszControl
+{
+public:
+ // So the compiler can initialize the class already. No DTOR and member
+ // as public members
+ const SwFlyFrame* m_pEntry;
+ const SwFlyFrame* m_pStack1;
+ const SwFlyFrame* m_pStack2;
+
+ bool ChkOsz( const SwFlyFrame *pFly )
+ {
+ bool bRet = true;
+ if (pFly != m_pStack1 && pFly != m_pStack2)
+ {
+ m_pStack1 = m_pStack2;
+ m_pStack2 = pFly;
+ bRet = false;
+ }
+ return bRet;
+ }
+
+ void Entry( const SwFlyFrame *pFly )
+ {
+ if (!m_pEntry)
+ m_pEntry = m_pStack1 = pFly;
+ }
+
+ void Exit( const SwFlyFrame *pFly )
+ {
+ if (pFly == m_pEntry)
+ m_pEntry = m_pStack1 = m_pStack2 = nullptr;
+ }
+};
+
+}
+
+static SwCursorOszControl g_OszCtrl = { nullptr, nullptr, nullptr };
+
+/** Searches the ContentFrame owning the PrtArea containing the point. */
+bool SwLayoutFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ bool bRet = false;
+ const SwFrame *pFrame = Lower();
+ while ( !bRet && pFrame )
+ {
+ pFrame->Calc(pRenderContext);
+
+ // #i43742# New function
+ const bool bContentCheck = pFrame->IsTextFrame() && pCMS && pCMS->m_bContentCheck;
+ const SwRect aPaintRect( bContentCheck ?
+ pFrame->UnionFrame() :
+ pFrame->GetPaintArea() );
+
+ if ( aPaintRect.Contains( rPoint ) &&
+ ( bContentCheck || pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS ) ) )
+ bRet = true;
+ else
+ pFrame = pFrame->GetNext();
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ return bRet;
+}
+
+/** Searches the page containing the searched point. */
+
+bool SwPageFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool bTestBackground ) const
+{
+ Point aPoint( rPoint );
+
+ // check, if we have to adjust the point
+ if ( !getFrameArea().Contains( aPoint ) )
+ {
+ aPoint.setX( std::max( aPoint.X(), getFrameArea().Left() ) );
+ aPoint.setX( std::min( aPoint.X(), getFrameArea().Right() ) );
+ aPoint.setY( std::max( aPoint.Y(), getFrameArea().Top() ) );
+ aPoint.setY( std::min( aPoint.Y(), getFrameArea().Bottom() ) );
+ }
+
+ bool bRet = false;
+ //Could it be a free flying one?
+ //If his content should be protected, we can't set the Cursor in it, thus
+ //all changes should be impossible.
+ if ( GetSortedObjs() )
+ {
+ bRet = lcl_GetModelPositionForViewPoint_Objects( this, false, pPos, rPoint, pCMS );
+ }
+
+ if ( !bRet )
+ {
+ SwPosition aBackPos( *pPos );
+ SwPosition aTextPos( *pPos );
+
+ //We fix the StartPoint if no Content below the page 'answers' and then
+ //start all over again one page before the current one.
+ //However we can't use Flys in such a case.
+ if (!SwLayoutFrame::GetModelPositionForViewPoint(&aTextPos, aPoint, pCMS))
+ {
+ if ( pCMS && (pCMS->m_bStop || pCMS->m_bExactOnly) )
+ {
+ pCMS->m_bStop = true;
+ return false;
+ }
+
+ const SwContentFrame *pCnt = GetContentPos( aPoint, false, false, pCMS, false );
+ // GetContentPos may have modified pCMS
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+
+ bool bTextRet = false;
+
+ OSL_ENSURE( pCnt, "Cursor is gone to a Black hole" );
+ if( pCMS && pCMS->m_pFill && pCnt->IsTextFrame() )
+ bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, rPoint, pCMS );
+ else
+ bTextRet = pCnt->GetModelPositionForViewPoint( &aTextPos, aPoint, pCMS );
+
+ if ( !bTextRet )
+ {
+ // Set point to pCnt, delete mark
+ // this may happen, if pCnt is hidden
+ if (pCnt->IsTextFrame())
+ {
+ aTextPos = static_cast<SwTextFrame const*>(pCnt)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ assert(pCnt->IsNoTextFrame());
+ aTextPos = SwPosition( *static_cast<SwNoTextFrame const*>(pCnt)->GetNode() );
+ }
+ }
+ }
+
+ SwContentNode* pContentNode = aTextPos.nNode.GetNode().GetContentNode();
+ bool bConsiderBackground = true;
+ // If the text position is a clickable field, then that should have priority.
+ if (pContentNode && pContentNode->IsTextNode())
+ {
+ SwTextNode* pTextNd = pContentNode->GetTextNode();
+ SwTextAttr* pTextAttr = pTextNd->GetTextAttrForCharAt(aTextPos.nContent.GetIndex(), RES_TXTATR_FIELD);
+ if (pTextAttr)
+ {
+ const SwField* pField = pTextAttr->GetFormatField().GetField();
+ if (pField->IsClickable())
+ bConsiderBackground = false;
+ }
+ }
+
+ bool bBackRet = false;
+ // Check objects in the background if nothing else matched
+ if ( GetSortedObjs() )
+ {
+ bBackRet = lcl_GetModelPositionForViewPoint_Objects( this, true, &aBackPos, rPoint, pCMS );
+ }
+
+ if (bConsiderBackground && bTestBackground && bBackRet)
+ {
+ (*pPos) = aBackPos;
+ }
+ else if (!bBackRet)
+ {
+ (*pPos) = aTextPos;
+ }
+ else // bBackRet && !(bConsiderBackground && bTestBackground)
+ {
+ /* In order to provide a selection as accurate as possible when we have both
+ * text and background object, then we compute the distance between both
+ * would-be positions and the click point. The shortest distance wins.
+ */
+ double nTextDistance = 0;
+ bool bValidTextDistance = false;
+ if (pContentNode)
+ {
+ SwContentFrame* pTextFrame = pContentNode->getLayoutFrame( getRootFrame( ) );
+
+ // try this again but prefer the "previous" position
+ SwCursorMoveState aMoveState;
+ SwCursorMoveState *const pState(pCMS ? pCMS : &aMoveState);
+ comphelper::FlagRestorationGuard g(
+ pState->m_bPosMatchesBounds, true);
+ SwPosition prevTextPos(*pPos);
+ if (SwLayoutFrame::GetModelPositionForViewPoint(&prevTextPos, aPoint, pState))
+ {
+ SwRect aTextRect;
+ pTextFrame->GetCharRect(aTextRect, prevTextPos);
+
+ if (prevTextPos.nContent < pContentNode->Len())
+ {
+ // aRextRect is just a line on the left edge of the
+ // previous character; to get a better measure from
+ // lcl_getDistance, extend that to a rectangle over
+ // the entire character.
+ SwPosition const nextTextPos(prevTextPos.nNode,
+ SwIndex(prevTextPos.nContent, +1));
+ SwRect nextTextRect;
+ pTextFrame->GetCharRect(nextTextRect, nextTextPos);
+ SwRectFnSet aRectFnSet(pTextFrame);
+ if (aRectFnSet.GetTop(aTextRect) ==
+ aRectFnSet.GetTop(nextTextRect)) // same line?
+ {
+ // need to handle mixed RTL/LTR portions somehow
+ if (aRectFnSet.GetLeft(aTextRect) <
+ aRectFnSet.GetLeft(nextTextRect))
+ {
+ aRectFnSet.SetRight( aTextRect,
+ aRectFnSet.GetLeft(nextTextRect));
+ }
+ else // RTL
+ {
+ aRectFnSet.SetLeft( aTextRect,
+ aRectFnSet.GetLeft(nextTextRect));
+ }
+ }
+ }
+
+ nTextDistance = lcl_getDistance(aTextRect, rPoint);
+ bValidTextDistance = true;
+ }
+ }
+
+ double nBackDistance = 0;
+ bool bValidBackDistance = false;
+ SwContentNode* pBackNd = aBackPos.nNode.GetNode( ).GetContentNode( );
+ if ( pBackNd && bConsiderBackground)
+ {
+ // FIXME There are still cases were we don't have the proper node here.
+ SwContentFrame* pBackFrame = pBackNd->getLayoutFrame( getRootFrame( ) );
+ if (pBackFrame)
+ {
+ SwRect rBackRect;
+ pBackFrame->GetCharRect( rBackRect, aBackPos );
+
+ nBackDistance = lcl_getDistance( rBackRect, rPoint );
+ bValidBackDistance = true;
+ }
+ }
+
+ if ( bValidTextDistance && bValidBackDistance && basegfx::fTools::more( nTextDistance, nBackDistance ) )
+ {
+ (*pPos) = aBackPos;
+ }
+ else
+ {
+ (*pPos) = aTextPos;
+ }
+ }
+ }
+
+ rPoint = aPoint;
+ return true;
+}
+
+bool SwLayoutFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
+{
+ if( rRect.Overlaps(GetPaintArea()) )
+ {
+ const SwFrame* pFrame = Lower();
+ while( pFrame )
+ {
+ pFrame->FillSelection( rList, rRect );
+ pFrame = pFrame->GetNext();
+ }
+ }
+ return false;
+}
+
+bool SwPageFrame::FillSelection( SwSelectionList& rList, const SwRect& rRect ) const
+{
+ bool bRet = false;
+ if( rRect.Overlaps(GetPaintArea()) )
+ {
+ bRet = SwLayoutFrame::FillSelection( rList, rRect );
+ if( GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
+ if( !pFly )
+ continue;
+ if( pFly->FillSelection( rList, rRect ) )
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SwRootFrame::FillSelection( SwSelectionList& aSelList, const SwRect& rRect) const
+{
+ const SwFrame *pPage = Lower();
+ const tools::Long nBottom = rRect.Bottom();
+ while( pPage )
+ {
+ if( pPage->getFrameArea().Top() < nBottom )
+ {
+ if( pPage->getFrameArea().Bottom() > rRect.Top() )
+ pPage->FillSelection( aSelList, rRect );
+ pPage = pPage->GetNext();
+ }
+ else
+ pPage = nullptr;
+ }
+ return !aSelList.isEmpty();
+}
+
+/** Primary passes the call to the first page.
+ *
+ * @return false, if the passed Point gets changed
+ */
+bool SwRootFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool bTestBackground ) const
+{
+ const bool bOldAction = IsCallbackActionEnabled();
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( false );
+ OSL_ENSURE( (Lower() && Lower()->IsPageFrame()), "No PageFrame found." );
+ if( pCMS && pCMS->m_pFill )
+ pCMS->m_bFillRet = false;
+ Point aOldPoint = rPoint;
+
+ // search for page containing rPoint. The borders around the pages are considered
+ const SwPageFrame* pPage = GetPageAtPos( rPoint, nullptr, true );
+
+ // #i95626#
+ // special handling for <rPoint> beyond root frames area
+ if ( !pPage &&
+ rPoint.X() > getFrameArea().Right() &&
+ rPoint.Y() > getFrameArea().Bottom() )
+ {
+ pPage = dynamic_cast<const SwPageFrame*>(Lower());
+ while ( pPage && pPage->GetNext() )
+ {
+ pPage = dynamic_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ }
+ if ( pPage )
+ {
+ pPage->SwPageFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS, bTestBackground );
+ }
+
+ const_cast<SwRootFrame*>(this)->SetCallbackActionEnabled( bOldAction );
+ if( pCMS )
+ {
+ if( pCMS->m_bStop )
+ return false;
+ if( pCMS->m_pFill )
+ return pCMS->m_bFillRet;
+ }
+ return aOldPoint == rPoint;
+}
+
+/**
+ * If this is about a Content-carrying cell the Cursor will be force inserted into one of the ContentFrames
+ * if there are no other options.
+ *
+ * There is no entry for protected cells.
+ */
+bool SwCellFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ // cell frame does not necessarily have a lower (split table cell)
+ if ( !Lower() )
+ return false;
+
+ if ( !(pCMS && pCMS->m_bSetInReadOnly) &&
+ GetFormat()->GetProtect().IsContentProtected() )
+ return false;
+
+ if ( pCMS && pCMS->m_eState == CursorMoveState::TableSel )
+ {
+ const SwTabFrame *pTab = FindTabFrame();
+ if ( pTab->IsFollow() && pTab->IsInHeadline( *this ) )
+ {
+ pCMS->m_bStop = true;
+ return false;
+ }
+ }
+
+ if ( Lower() )
+ {
+ if ( Lower()->IsLayoutFrame() )
+ return SwLayoutFrame::GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ else
+ {
+ Calc(pRenderContext);
+ bool bRet = false;
+
+ const SwFrame *pFrame = Lower();
+ while ( pFrame && !bRet )
+ {
+ pFrame->Calc(pRenderContext);
+ if ( pFrame->getFrameArea().Contains( rPoint ) )
+ {
+ bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ if ( !bRet )
+ {
+ const bool bFill = pCMS && pCMS->m_pFill;
+ Point aPoint( rPoint );
+ const SwContentFrame *pCnt = GetContentPos( rPoint, true );
+ if( bFill && pCnt->IsTextFrame() )
+ {
+ rPoint = aPoint;
+ }
+ pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//Problem: If two Flys have the same size and share the same position then
+//they end inside each other.
+//Because we recursively check if a Point doesn't randomly lie inside another
+//fly which lies completely inside the current Fly we could trigger an endless
+//loop with the mentioned situation above.
+//Using the helper class SwCursorOszControl we prevent the recursion. During
+//a recursion GetModelPositionForViewPoint picks the one which lies on top.
+bool SwFlyFrame::GetModelPositionForViewPoint( SwPosition *pPos, Point &rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ g_OszCtrl.Entry( this );
+
+ //If the Points lies inside the Fly, we try hard to set the Cursor inside it.
+ //However if the Point sits inside a Fly which is completely located inside
+ //the current one, we call GetModelPositionForViewPoint for it.
+ Calc(pRenderContext);
+ bool bInside = getFrameArea().Contains( rPoint ) && Lower();
+ bool bRet = false;
+
+ //If a Frame contains a graphic, but only text was requested, it basically
+ //won't accept the Cursor.
+ if ( bInside && pCMS && pCMS->m_eState == CursorMoveState::SetOnlyText &&
+ (!Lower() || Lower()->IsNoTextFrame()) )
+ bInside = false;
+
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( bInside && pPage && pPage->GetSortedObjs() )
+ {
+ SwOrderIter aIter( pPage );
+ aIter.Top();
+ while ( aIter() && !bRet )
+ {
+ const SwVirtFlyDrawObj* pObj = static_cast<const SwVirtFlyDrawObj*>(aIter());
+ const SwFlyFrame* pFly = pObj ? pObj->GetFlyFrame() : nullptr;
+ if ( pFly && pFly->getFrameArea().Contains( rPoint ) &&
+ getFrameArea().Contains( pFly->getFrameArea() ) )
+ {
+ if (g_OszCtrl.ChkOsz(pFly))
+ break;
+ bRet = pFly->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ if ( bRet )
+ break;
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ aIter.Next();
+ }
+ }
+
+ while ( bInside && !bRet )
+ {
+ const SwFrame *pFrame = Lower();
+ while ( pFrame && !bRet )
+ {
+ pFrame->Calc(pRenderContext);
+ if ( pFrame->getFrameArea().Contains( rPoint ) )
+ {
+ bRet = pFrame->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ }
+ pFrame = pFrame->GetNext();
+ }
+ if ( !bRet )
+ {
+ const bool bFill = pCMS && pCMS->m_pFill;
+ Point aPoint( rPoint );
+ const SwContentFrame *pCnt = GetContentPos( rPoint, true, false, pCMS );
+ if ( pCMS && pCMS->m_bStop )
+ return false;
+ if( bFill && pCnt->IsTextFrame() )
+ {
+ rPoint = aPoint;
+ }
+ pCnt->GetModelPositionForViewPoint( pPos, rPoint, pCMS );
+ bRet = true;
+ }
+ }
+ g_OszCtrl.Exit( this );
+ return bRet;
+}
+
+/** Layout dependent cursor travelling */
+bool SwNoTextFrame::LeftMargin(SwPaM *pPam) const
+{
+ if( &pPam->GetNode() != GetNode() )
+ return false;
+ const_cast<SwContentNode*>(GetNode())->
+ MakeStartIndex(&pPam->GetPoint()->nContent);
+ return true;
+}
+
+bool SwNoTextFrame::RightMargin(SwPaM *pPam, bool) const
+{
+ if( &pPam->GetNode() != GetNode() )
+ return false;
+ const_cast<SwContentNode*>(GetNode())->
+ MakeEndIndex(&pPam->GetPoint()->nContent);
+ return true;
+}
+
+static const SwContentFrame *lcl_GetNxtCnt( const SwContentFrame* pCnt )
+{
+ return pCnt->GetNextContentFrame();
+}
+
+static const SwContentFrame *lcl_GetPrvCnt( const SwContentFrame* pCnt )
+{
+ return pCnt->GetPrevContentFrame();
+}
+
+typedef const SwContentFrame *(*GetNxtPrvCnt)( const SwContentFrame* );
+
+/// Frame in repeated headline?
+static bool lcl_IsInRepeatedHeadline( const SwFrame *pFrame,
+ const SwTabFrame** ppTFrame = nullptr )
+{
+ const SwTabFrame *pTab = pFrame->FindTabFrame();
+ if( ppTFrame )
+ *ppTFrame = pTab;
+ return pTab && pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
+}
+
+/// Skip protected table cells. Optionally also skip repeated headlines.
+//MA 1998-01-26: Chg also skip other protected areas
+//FME: Skip follow flow cells
+static const SwContentFrame * lcl_MissProtectedFrames( const SwContentFrame *pCnt,
+ GetNxtPrvCnt fnNxtPrv,
+ bool bMissHeadline,
+ bool bInReadOnly,
+ bool bMissFollowFlowLine )
+{
+ if ( pCnt && pCnt->IsInTab() )
+ {
+ bool bProtect = true;
+ while ( pCnt && bProtect )
+ {
+ const SwLayoutFrame *pCell = pCnt->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ if ( !pCell ||
+ ( ( bInReadOnly || !pCell->GetFormat()->GetProtect().IsContentProtected() ) &&
+ ( !bMissHeadline || !lcl_IsInRepeatedHeadline( pCell ) ) &&
+ ( !bMissFollowFlowLine || !pCell->IsInFollowFlowRow() ) &&
+ !pCell->IsCoveredCell() ) )
+ bProtect = false;
+ else
+ pCnt = (*fnNxtPrv)( pCnt );
+ }
+ }
+ else if ( !bInReadOnly )
+ while ( pCnt && pCnt->IsProtected() )
+ pCnt = (*fnNxtPrv)( pCnt );
+
+ return pCnt;
+}
+
+static bool lcl_UpDown( SwPaM *pPam, const SwContentFrame *pStart,
+ GetNxtPrvCnt fnNxtPrv, bool bInReadOnly )
+{
+ OSL_ENSURE( FrameContainsNode(*pStart, pPam->GetNode().GetIndex()),
+ "lcl_UpDown doesn't work for others." );
+
+ const SwContentFrame *pCnt = nullptr;
+
+ //We have to cheat a little bit during a table selection: Go to the
+ //beginning of the cell while going up and go to the end of the cell while
+ //going down.
+ bool bTableSel = false;
+ if ( pStart->IsInTab() &&
+ pPam->GetNode().StartOfSectionNode() !=
+ pPam->GetNode( false ).StartOfSectionNode() )
+ {
+ bTableSel = true;
+ const SwLayoutFrame *pCell = pStart->GetUpper();
+ while ( !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+
+ // Check, if cell has a Prev/Follow cell:
+ const bool bFwd = ( fnNxtPrv == lcl_GetNxtCnt );
+ const SwLayoutFrame* pTmpCell = bFwd ?
+ static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
+ static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
+
+ const SwContentFrame* pTmpStart = pStart;
+ while ( pTmpCell && nullptr != ( pTmpStart = pTmpCell->ContainsContent() ) )
+ {
+ pCell = pTmpCell;
+ pTmpCell = bFwd ?
+ static_cast<const SwCellFrame*>(pCell)->GetFollowCell() :
+ static_cast<const SwCellFrame*>(pCell)->GetPreviousCell();
+ }
+ const SwContentFrame *pNxt = pCnt = pTmpStart;
+
+ while ( pCell->IsAnLower( pNxt ) )
+ {
+ pCnt = pNxt;
+ pNxt = (*fnNxtPrv)( pNxt );
+ }
+ }
+
+ pCnt = (*fnNxtPrv)( pCnt ? pCnt : pStart );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+
+ const SwTabFrame *pStTab = pStart->FindTabFrame();
+ const SwTabFrame *pTable = nullptr;
+ const bool bTab = pStTab || (pCnt && pCnt->IsInTab());
+ bool bEnd = !bTab;
+
+ const SwFrame* pVertRefFrame = pStart;
+ if ( bTableSel && pStTab )
+ pVertRefFrame = pStTab;
+ SwRectFnSet aRectFnSet(pVertRefFrame);
+
+ SwTwips nX = 0;
+ if ( bTab )
+ {
+ // pStart or pCnt is inside a table. nX will be used for travelling:
+ SwRect aRect( pStart->getFrameArea() );
+ pStart->GetCharRect( aRect, *pPam->GetPoint() );
+ Point aCenter = aRect.Center();
+ nX = aRectFnSet.IsVert() ? aCenter.Y() : aCenter.X();
+
+ pTable = pCnt ? pCnt->FindTabFrame() : nullptr;
+ if ( !pTable )
+ pTable = pStTab;
+
+ if ( pStTab &&
+ !pStTab->GetUpper()->IsInTab() &&
+ !pTable->GetUpper()->IsInTab() )
+ {
+ const SwFrame *pCell = pStart->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell, "could not find the cell" );
+ nX = aRectFnSet.XInc(aRectFnSet.GetLeft(pCell->getFrameArea()),
+ aRectFnSet.GetWidth(pCell->getFrameArea()) / 2);
+
+ //The flow leads from one table to the next. The X-value needs to be
+ //corrected based on the middle of the starting cell by the amount
+ //of the offset of the tables.
+ if ( pStTab != pTable )
+ {
+ nX += aRectFnSet.GetLeft(pTable->getFrameArea()) -
+ aRectFnSet.GetLeft(pStTab->getFrameArea());
+ }
+ }
+
+ // Restrict nX to the left and right borders of pTab:
+ // (is this really necessary?)
+ if (pTable && !pTable->GetUpper()->IsInTab())
+ {
+ const bool bRTL = pTable->IsRightToLeft();
+ const tools::Long nPrtLeft = bRTL ?
+ aRectFnSet.GetPrtRight(*pTable) :
+ aRectFnSet.GetPrtLeft(*pTable);
+ if (bRTL != (aRectFnSet.XDiff(nPrtLeft, nX) > 0))
+ nX = nPrtLeft;
+ else
+ {
+ const tools::Long nPrtRight = bRTL ?
+ aRectFnSet.GetPrtLeft(*pTable) :
+ aRectFnSet.GetPrtRight(*pTable);
+ if (bRTL != (aRectFnSet.XDiff(nX, nPrtRight) > 0))
+ nX = nPrtRight;
+ }
+ }
+ }
+
+ do
+ {
+ //If I'm in the DocumentBody, I want to stay there.
+ if ( pStart->IsInDocBody() )
+ {
+ while ( pCnt && (!pCnt->IsInDocBody() ||
+ (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())))
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ //If I'm in the FootNoteArea, I try to reach the next FootNoteArea in
+ //case of necessity.
+ else if ( pStart->IsInFootnote() )
+ {
+ while ( pCnt && (!pCnt->IsInFootnote() ||
+ (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow())))
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ //In Flys we can go ahead blindly as long as we find a Content.
+ else if ( pStart->IsInFly() )
+ {
+ if ( pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow() )
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ //Otherwise I'll just refuse to leave to current area.
+ else if ( pCnt )
+ {
+ const SwFrame *pUp = pStart->GetUpper();
+ while (pUp && pUp->GetUpper() && !(pUp->GetType() & FRM_HEADFOOT))
+ pUp = pUp->GetUpper();
+ bool bSame = false;
+ const SwFrame *pCntUp = pCnt->GetUpper();
+ while ( pCntUp && !bSame )
+ {
+ if ( pUp == pCntUp )
+ bSame = true;
+ else
+ pCntUp = pCntUp->GetUpper();
+ }
+ if ( !bSame )
+ pCnt = nullptr;
+ else if (pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()) // i73332
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ if ( bTab )
+ {
+ if ( !pCnt )
+ bEnd = true;
+ else
+ {
+ const SwTabFrame *pTab = pCnt->FindTabFrame();
+ if( !pTab )
+ bEnd = true;
+ else
+ {
+ if ( pTab != pTable )
+ {
+ //The flow leads from one table to the next. The X-value
+ //needs to be corrected by the amount of the offset of
+ //the tables
+ if ( pTable &&
+ !pTab->GetUpper()->IsInTab() &&
+ !pTable->GetUpper()->IsInTab() )
+ nX += pTab->getFrameArea().Left() - pTable->getFrameArea().Left();
+ pTable = pTab;
+ }
+ const SwLayoutFrame *pCell = pCnt->GetUpper();
+ while ( pCell && !pCell->IsCellFrame() )
+ pCell = pCell->GetUpper();
+
+ Point aInsideCell;
+ Point aInsideCnt;
+ if ( pCell )
+ {
+ tools::Long nTmpTop = aRectFnSet.GetTop(pCell->getFrameArea());
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( nTmpTop )
+ nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
+
+ aInsideCell = Point( nTmpTop, nX );
+ }
+ else
+ aInsideCell = Point( nX, nTmpTop );
+ }
+
+ tools::Long nTmpTop = aRectFnSet.GetTop(pCnt->getFrameArea());
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( nTmpTop )
+ nTmpTop = aRectFnSet.XInc(nTmpTop, -1);
+
+ aInsideCnt = Point( nTmpTop, nX );
+ }
+ else
+ aInsideCnt = Point( nX, nTmpTop );
+
+ if ( pCell && pCell->getFrameArea().Contains( aInsideCell ) )
+ {
+ bEnd = true;
+ //Get the right Content out of the cell.
+ if ( !pCnt->getFrameArea().Contains( aInsideCnt ) )
+ {
+ pCnt = pCell->ContainsContent();
+ if ( fnNxtPrv == lcl_GetPrvCnt )
+ while ( pCell->IsAnLower(pCnt->GetNextContentFrame()) )
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+ else if ( pCnt->getFrameArea().Contains( aInsideCnt ) )
+ bEnd = true;
+ }
+ }
+ if ( !bEnd )
+ {
+ pCnt = (*fnNxtPrv)( pCnt );
+ pCnt = ::lcl_MissProtectedFrames( pCnt, fnNxtPrv, true, bInReadOnly, bTableSel );
+ }
+ }
+
+ } while ( !bEnd ||
+ (pCnt && pCnt->IsTextFrame() && static_cast<const SwTextFrame*>(pCnt)->IsHiddenNow()));
+
+ if (pCnt == nullptr)
+ {
+ return false;
+ }
+ if (pCnt->IsTextFrame())
+ {
+ SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pCnt));
+ *pPam->GetPoint() = pFrame->MapViewToModelPos(TextFrameIndex(
+ fnNxtPrv == lcl_GetPrvCnt
+ ? pFrame->GetText().getLength()
+ : 0));
+ }
+ else
+ { // set the Point on the Content-Node
+ assert(pCnt->IsNoTextFrame());
+ SwContentNode *const pCNd = const_cast<SwContentNode*>(static_cast<SwNoTextFrame const*>(pCnt)->GetNode());
+ pPam->GetPoint()->nNode = *pCNd;
+ if ( fnNxtPrv == lcl_GetPrvCnt )
+ pCNd->MakeEndIndex( &pPam->GetPoint()->nContent );
+ else
+ pCNd->MakeStartIndex( &pPam->GetPoint()->nContent );
+ }
+ return true;
+}
+
+bool SwContentFrame::UnitUp( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
+{
+ return ::lcl_UpDown( pPam, this, lcl_GetPrvCnt, bInReadOnly );
+}
+
+bool SwContentFrame::UnitDown( SwPaM* pPam, const SwTwips, bool bInReadOnly ) const
+{
+ return ::lcl_UpDown( pPam, this, lcl_GetNxtCnt, bInReadOnly );
+}
+
+/** Returns the number of the current page.
+ *
+ * If the method gets a PaM then the current page is the one in which the PaM sits. Otherwise the
+ * current page is the first one inside the VisibleArea. We only work on available pages!
+ */
+sal_uInt16 SwRootFrame::GetCurrPage( const SwPaM *pActualCursor ) const
+{
+ OSL_ENSURE( pActualCursor, "got no page cursor" );
+ SwFrame const*const pActFrame = pActualCursor->GetPoint()->nNode.GetNode().
+ GetContentNode()->getLayoutFrame(this,
+ pActualCursor->GetPoint());
+ return pActFrame->FindPageFrame()->GetPhyPageNum();
+}
+
+/** Returns a PaM which sits at the beginning of the requested page.
+ *
+ * Formatting is done as far as necessary.
+ * The PaM sits on the last page, if the page number was chosen too big.
+ *
+ * @return Null, if the operation was not possible.
+ */
+sal_uInt16 SwRootFrame::SetCurrPage( SwCursor* pToSet, sal_uInt16 nPageNum )
+{
+ vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
+ OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
+
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ bool bEnd =false;
+ while ( !bEnd && pPage->GetPhyPageNum() != nPageNum )
+ { if ( pPage->GetNext() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ else
+ { //Search the first ContentFrame and format until a new page is started
+ //or until the ContentFrame are all done.
+ const SwContentFrame *pContent = pPage->ContainsContent();
+ while ( pContent && pPage->IsAnLower( pContent ) )
+ {
+ pContent->Calc(pRenderContext);
+ pContent = pContent->GetNextContentFrame();
+ }
+ //Either this is a new page or we found the last page.
+ if ( pPage->GetNext() )
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ else
+ bEnd = true;
+ }
+ }
+ //pPage now points to the 'requested' page. Now we have to create the PaM
+ //on the beginning of the first ContentFrame in the body-text.
+ //If this is a footnote-page, the PaM will be set in the first footnote.
+ const SwContentFrame *pContent = pPage->ContainsContent();
+ if ( pPage->IsFootnotePage() )
+ while ( pContent && !pContent->IsInFootnote() )
+ pContent = pContent->GetNextContentFrame();
+ else
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ if ( pContent )
+ {
+ assert(pContent->IsTextFrame());
+ SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pContent));
+ *pToSet->GetPoint() = pFrame->MapViewToModelPos(pFrame->GetOffset());
+
+ SwShellCursor* pSCursor = dynamic_cast<SwShellCursor*>(pToSet);
+ if( pSCursor )
+ {
+ Point &rPt = pSCursor->GetPtPos();
+ rPt = pContent->getFrameArea().Pos();
+ rPt += pContent->getFramePrintArea().Pos();
+ }
+ return pPage->GetPhyPageNum();
+ }
+ return 0;
+}
+
+SwContentFrame *GetFirstSub( const SwLayoutFrame *pLayout )
+{
+ return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindFirstBodyContent();
+}
+
+SwContentFrame *GetLastSub( const SwLayoutFrame *pLayout )
+{
+ return const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pLayout))->FindLastBodyContent();
+}
+
+SwLayoutFrame *GetNextFrame( const SwLayoutFrame *pFrame )
+{
+ SwLayoutFrame *pNext =
+ (pFrame->GetNext() && pFrame->GetNext()->IsLayoutFrame()) ?
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetNext())) : nullptr;
+ // #i39402# in case of an empty page
+ if(pNext && !pNext->ContainsContent())
+ pNext = (pNext->GetNext() && pNext->GetNext()->IsLayoutFrame()) ?
+ static_cast<SwLayoutFrame*>(pNext->GetNext()) : nullptr;
+ return pNext;
+}
+
+SwLayoutFrame *GetThisFrame( const SwLayoutFrame *pFrame )
+{
+ return const_cast<SwLayoutFrame*>(pFrame);
+}
+
+SwLayoutFrame *GetPrevFrame( const SwLayoutFrame *pFrame )
+{
+ SwLayoutFrame *pPrev =
+ (pFrame->GetPrev() && pFrame->GetPrev()->IsLayoutFrame()) ?
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pFrame->GetPrev())) : nullptr;
+ // #i39402# in case of an empty page
+ if(pPrev && !pPrev->ContainsContent())
+ pPrev = (pPrev->GetPrev() && pPrev->GetPrev()->IsLayoutFrame()) ?
+ static_cast<SwLayoutFrame*>(pPrev->GetPrev()) : nullptr;
+ return pPrev;
+}
+
+/**
+ * Returns the first/last Contentframe (controlled using the parameter fnPosPage)
+ * of the current/previous/next page (controlled using the parameter fnWhichPage).
+ */
+bool GetFrameInPage( const SwContentFrame *pCnt, SwWhichPage fnWhichPage,
+ SwPosPage fnPosPage, SwPaM *pPam )
+{
+ //First find the requested page, at first the current, then the one which
+ //was requests through fnWichPage.
+ const SwLayoutFrame *pLayoutFrame = pCnt->FindPageFrame();
+ if ( !pLayoutFrame || (nullptr == (pLayoutFrame = (*fnWhichPage)(pLayoutFrame))) )
+ return false;
+
+ //Now the desired ContentFrame below the page
+ pCnt = (*fnPosPage)(pLayoutFrame);
+ if( nullptr == pCnt )
+ return false;
+ else
+ {
+ // repeated headlines in tables
+ if ( pCnt->IsInTab() && fnPosPage == GetFirstSub )
+ {
+ const SwTabFrame* pTab = pCnt->FindTabFrame();
+ if ( pTab->IsFollow() )
+ {
+ if ( pTab->IsInHeadline( *pCnt ) )
+ {
+ SwLayoutFrame* pRow = pTab->GetFirstNonHeadlineRow();
+ if ( pRow )
+ {
+ // We are in the first line of a follow table
+ // with repeated headings.
+ // To actually make a "real" move we take the first content
+ // of the next row
+ pCnt = pRow->ContainsContent();
+ if ( ! pCnt )
+ return false;
+ }
+ }
+ }
+ }
+
+ assert(pCnt->IsTextFrame());
+ SwTextFrame const*const pFrame(static_cast<const SwTextFrame*>(pCnt));
+ TextFrameIndex const nIdx((fnPosPage == GetFirstSub)
+ ? pFrame->GetOffset()
+ : (pFrame->GetFollow())
+ ? pFrame->GetFollow()->GetOffset() - TextFrameIndex(1)
+ : TextFrameIndex(pFrame->GetText().getLength()));
+ *pPam->GetPoint() = pFrame->MapViewToModelPos(nIdx);
+ return true;
+ }
+}
+
+static sal_uInt64 CalcDiff(const Point &rPt1, const Point &rPt2)
+{
+ //Calculate the distance between the two points.
+ //'delta' X^2 + 'delta'Y^2 = 'distance'^2
+ sal_uInt64 dX = std::max( rPt1.X(), rPt2.X() ) -
+ std::min( rPt1.X(), rPt2.X() ),
+ dY = std::max( rPt1.Y(), rPt2.Y() ) -
+ std::min( rPt1.Y(), rPt2.Y() );
+ return (dX * dX) + (dY * dY);
+}
+
+/** Check if the point lies inside the page part in which also the ContentFrame lies.
+ *
+ * In this context header, page body, footer and footnote-container count as page part.
+ * This will suit the purpose that the ContentFrame which lies in the "right" page part will be
+ * accepted instead of one which doesn't lie there although his distance to the point is shorter.
+ */
+static const SwLayoutFrame* lcl_Inside( const SwContentFrame *pCnt, Point const & rPt )
+{
+ const SwLayoutFrame* pUp = pCnt->GetUpper();
+ while( pUp )
+ {
+ if( pUp->IsPageBodyFrame() || pUp->IsFooterFrame() || pUp->IsHeaderFrame() )
+ {
+ if( rPt.Y() >= pUp->getFrameArea().Top() && rPt.Y() <= pUp->getFrameArea().Bottom() )
+ return pUp;
+ return nullptr;
+ }
+ if( pUp->IsFootnoteContFrame() )
+ return pUp->getFrameArea().Contains( rPt ) ? pUp : nullptr;
+ pUp = pUp->GetUpper();
+ }
+ return nullptr;
+}
+
+/** Search for the nearest Content to pass.
+ *
+ * Considers the previous, the current and the next page.
+ * If no content is found, the area gets expanded until one is found.
+ *
+ * @return The 'semantically correct' position inside the PrtArea of the found ContentFrame.
+ */
+const SwContentFrame *SwLayoutFrame::GetContentPos( Point& rPoint,
+ const bool bDontLeave,
+ const bool bBodyOnly,
+ SwCursorMoveState *pCMS,
+ const bool bDefaultExpand ) const
+{
+ //Determine the first ContentFrame.
+ const SwLayoutFrame *pStart = (!bDontLeave && bDefaultExpand && GetPrev()) ?
+ static_cast<const SwLayoutFrame*>(GetPrev()) : this;
+ const SwContentFrame *pContent = pStart->ContainsContent();
+
+ if ( !pContent && (GetPrev() && !bDontLeave) )
+ pContent = ContainsContent();
+
+ if ( bBodyOnly && pContent && !pContent->IsInDocBody() )
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+
+ const SwContentFrame *pActual= pContent;
+ const SwLayoutFrame *pInside = nullptr;
+ sal_uInt16 nMaxPage = GetPhyPageNum() + (bDefaultExpand ? 1 : 0);
+ Point aPoint = rPoint;
+ sal_uInt64 nDistance = SAL_MAX_UINT64;
+
+ while ( true ) //A loop to be sure we always find one.
+ {
+ while ( pContent &&
+ ((!bDontLeave || IsAnLower( pContent )) &&
+ (pContent->GetPhyPageNum() <= nMaxPage)) )
+ {
+ if ( pContent->getFrameArea().Width() &&
+ ( !bBodyOnly || pContent->IsInDocBody() ) )
+ {
+ //If the Content lies in a protected area (cell, Footnote, section),
+ //we search the next Content which is not protected.
+ const SwContentFrame *pComp = pContent;
+ pContent = ::lcl_MissProtectedFrames( pContent, lcl_GetNxtCnt, false,
+ pCMS && pCMS->m_bSetInReadOnly, false );
+ if ( pComp != pContent )
+ continue;
+
+ if ( !pContent->IsTextFrame() || !static_cast<const SwTextFrame*>(pContent)->IsHiddenNow() )
+ {
+ SwRect aContentFrame( pContent->UnionFrame() );
+ if ( aContentFrame.Contains( rPoint ) )
+ {
+ pActual = pContent;
+ aPoint = rPoint;
+ break;
+ }
+ //The distance from rPoint to the nearest Point of pContent
+ //will now be calculated.
+ Point aContentPoint( rPoint );
+
+ //First set the vertical position
+ if ( aContentFrame.Top() > aContentPoint.Y() )
+ aContentPoint.setY( aContentFrame.Top() );
+ else if ( aContentFrame.Bottom() < aContentPoint.Y() )
+ aContentPoint.setY( aContentFrame.Bottom() );
+
+ //Now the horizontal position
+ if ( aContentFrame.Left() > aContentPoint.X() )
+ aContentPoint.setX( aContentFrame.Left() );
+ else if ( aContentFrame.Right() < aContentPoint.X() )
+ aContentPoint.setX( aContentFrame.Right() );
+
+ // pInside is a page area in which the point lies. As soon
+ // as pInside != 0 only frames are accepted which are
+ // placed inside.
+ if( !pInside || ( pInside->IsAnLower( pContent ) &&
+ ( !pContent->IsInFootnote() || pInside->IsFootnoteContFrame() ) ) )
+ {
+ const sal_uInt64 nDiff = ::CalcDiff(aContentPoint, rPoint);
+ bool bBetter = nDiff < nDistance; // This one is nearer
+ if( !pInside )
+ {
+ pInside = lcl_Inside( pContent, rPoint );
+ if( pInside ) // In the "right" page area
+ bBetter = true;
+ }
+ if( bBetter )
+ {
+ aPoint = aContentPoint;
+ nDistance = nDiff;
+ pActual = pContent;
+ }
+ }
+ }
+ }
+ pContent = pContent->GetNextContentFrame();
+ if ( bBodyOnly )
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ }
+ if ( !pActual )
+ { //If we not yet found one we have to expand the searched
+ //area, sometime we will find one!
+ //MA 1997-01-09: Opt for many empty pages - if we only search inside
+ //the body, we can expand the searched area sufficiently in one step.
+ if ( bBodyOnly )
+ {
+ while ( !pContent && pStart->GetPrev() )
+ {
+ ++nMaxPage;
+ if( !pStart->GetPrev()->IsLayoutFrame() )
+ return nullptr;
+ pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
+ if( pStart->IsInDocBody() )
+ pContent = pStart->ContainsContent();
+ else
+ {
+ const SwPageFrame *pPage = pStart->FindPageFrame();
+ if( !pPage )
+ return nullptr;
+ pContent = pPage->FindFirstBodyContent();
+ }
+ }
+ if ( !pContent ) // Somewhere down the road we have to start with one!
+ {
+ const SwPageFrame *pPage = pStart->FindPageFrame();
+ if( !pPage )
+ return nullptr;
+ pContent = pPage->GetUpper()->ContainsContent();
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ if ( !pContent )
+ return nullptr; // There is no document content yet!
+ }
+ }
+ else
+ {
+ ++nMaxPage;
+ if ( pStart->GetPrev() )
+ {
+ if( !pStart->GetPrev()->IsLayoutFrame() )
+ return nullptr;
+ pStart = static_cast<const SwLayoutFrame*>(pStart->GetPrev());
+ pContent = pStart->ContainsContent();
+ }
+ else // Somewhere down the road we have to start with one!
+ {
+ const SwPageFrame *pPage = pStart->FindPageFrame();
+ if( !pPage )
+ return nullptr;
+ pContent = pPage->GetUpper()->ContainsContent();
+ }
+ }
+ pActual = pContent;
+ }
+ else
+ break;
+ }
+
+ OSL_ENSURE( pActual, "no Content found." );
+ OSL_ENSURE( !bBodyOnly || pActual->IsInDocBody(), "Content not in Body." );
+
+ //Special case for selecting tables not in repeated TableHeadlines.
+ if ( pActual->IsInTab() && pCMS && pCMS->m_eState == CursorMoveState::TableSel )
+ {
+ const SwTabFrame *pTab = pActual->FindTabFrame();
+ if ( pTab->IsFollow() && pTab->IsInHeadline( *pActual ) )
+ {
+ pCMS->m_bStop = true;
+ return nullptr;
+ }
+ }
+
+ //A small correction at the first/last
+ Size aActualSize( pActual->getFramePrintArea().SSize() );
+ if ( aActualSize.Height() > pActual->GetUpper()->getFramePrintArea().Height() )
+ aActualSize.setHeight( pActual->GetUpper()->getFramePrintArea().Height() );
+
+ SwRectFnSet aRectFnSet(pActual);
+ if ( !pActual->GetPrev() &&
+ aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pActual),
+ aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) > 0 )
+ {
+ aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Top() );
+ aPoint.setX( pActual->getFrameArea().Left() +
+ ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
+ pActual->getFramePrintArea().Right() :
+ pActual->getFramePrintArea().Left() ) );
+ }
+ else if ( !pActual->GetNext() &&
+ aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pActual),
+ aRectFnSet.IsVert() ? rPoint.X() : rPoint.Y() ) < 0 )
+ {
+ aPoint.setY( pActual->getFrameArea().Top() + pActual->getFramePrintArea().Bottom() );
+ aPoint.setX( pActual->getFrameArea().Left() +
+ ( pActual->IsRightToLeft() || aRectFnSet.IsVert() ?
+ pActual->getFramePrintArea().Left() :
+ pActual->getFramePrintArea().Right() ) );
+ }
+
+ //Bring the Point into the PrtArea
+ const SwRect aRect( pActual->getFrameArea().Pos() + pActual->getFramePrintArea().Pos(),
+ aActualSize );
+ if ( aPoint.Y() < aRect.Top() )
+ aPoint.setY( aRect.Top() );
+ else if ( aPoint.Y() > aRect.Bottom() )
+ aPoint.setY( aRect.Bottom() );
+ if ( aPoint.X() < aRect.Left() )
+ aPoint.setX( aRect.Left() );
+ else if ( aPoint.X() > aRect.Right() )
+ aPoint.setX( aRect.Right() );
+ rPoint = aPoint;
+ return pActual;
+}
+
+/** Same as SwLayoutFrame::GetContentPos(). Specialized for fields and border. */
+void SwPageFrame::GetContentPosition( const Point &rPt, SwPosition &rPos ) const
+{
+ //Determine the first ContentFrame.
+ const SwContentFrame *pContent = ContainsContent();
+ if ( pContent )
+ {
+ //Look back one more (if possible).
+ const SwContentFrame *pTmp = pContent->GetPrevContentFrame();
+ while ( pTmp && !pTmp->IsInDocBody() )
+ pTmp = pTmp->GetPrevContentFrame();
+ if ( pTmp )
+ pContent = pTmp;
+ }
+ else
+ pContent = GetUpper()->ContainsContent();
+
+ const SwContentFrame *pAct = pContent;
+ Point aAct = rPt;
+ sal_uInt64 nDist = SAL_MAX_UINT64;
+
+ while ( pContent )
+ {
+ SwRect aContentFrame( pContent->UnionFrame() );
+ if ( aContentFrame.Contains( rPt ) )
+ {
+ //This is the nearest one.
+ pAct = pContent;
+ break;
+ }
+
+ //Calculate the distance from rPt to the nearest point of pContent.
+ Point aPoint( rPt );
+
+ //Calculate the vertical position first
+ if ( aContentFrame.Top() > rPt.Y() )
+ aPoint.setY( aContentFrame.Top() );
+ else if ( aContentFrame.Bottom() < rPt.Y() )
+ aPoint.setY( aContentFrame.Bottom() );
+
+ //And now the horizontal position
+ if ( aContentFrame.Left() > rPt.X() )
+ aPoint.setX( aContentFrame.Left() );
+ else if ( aContentFrame.Right() < rPt.X() )
+ aPoint.setX( aContentFrame.Right() );
+
+ const sal_uInt64 nDiff = ::CalcDiff( aPoint, rPt );
+ if ( nDiff < nDist )
+ {
+ aAct = aPoint;
+ nDist = nDiff;
+ pAct = pContent;
+ }
+ else if ( aContentFrame.Top() > getFrameArea().Bottom() )
+ //In terms of fields, it's not possible to be closer any more!
+ break;
+
+ pContent = pContent->GetNextContentFrame();
+ while ( pContent && !pContent->IsInDocBody() )
+ pContent = pContent->GetNextContentFrame();
+ }
+
+ //Bring the point into the PrtArea.
+ const SwRect aRect( pAct->getFrameArea().Pos() + pAct->getFramePrintArea().Pos(), pAct->getFramePrintArea().SSize() );
+ if ( aAct.Y() < aRect.Top() )
+ aAct.setY( aRect.Top() );
+ else if ( aAct.Y() > aRect.Bottom() )
+ aAct.setY( aRect.Bottom() );
+ if ( aAct.X() < aRect.Left() )
+ aAct.setX( aRect.Left() );
+ else if ( aAct.X() > aRect.Right() )
+ aAct.setX( aRect.Right() );
+
+ if (!pAct->isFrameAreaDefinitionValid() ||
+ (pAct->IsTextFrame() && !static_cast<SwTextFrame const*>(pAct)->HasPara()))
+ {
+ // ContentFrame not formatted -> always on node-beginning
+ // tdf#100635 also if the SwTextFrame would require reformatting,
+ // which is unwanted in case this is called from text formatting code
+ rPos = static_cast<SwTextFrame const*>(pAct)->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
+ pAct->GetModelPositionForViewPoint( &rPos, aAct, &aTmpState );
+ }
+}
+
+/** Search the nearest Content to the passed point.
+ *
+ * Only search inside the BodyText.
+ * @note Only the nearest vertically one will be searched.
+ * @note JP 11.10.2001: only in tables we try to find the right column - Bug 72294
+ */
+Point SwRootFrame::GetNextPrevContentPos( const Point& rPoint, bool bNext ) const
+{
+ vcl::RenderContext* pRenderContext = GetCurrShell() ? GetCurrShell()->GetOut() : nullptr;
+ // #123110# - disable creation of an action by a callback
+ // event during processing of this method. Needed because formatting is
+ // triggered by this method.
+ DisableCallbackAction aDisableCallbackAction(const_cast<SwRootFrame&>(*this));
+ //Search the first ContentFrame and his successor in the body area.
+ //To be efficient (and not formatting too much) we'll start at the correct
+ //page.
+ const SwLayoutFrame *pPage = static_cast<const SwLayoutFrame*>(Lower());
+ if( pPage )
+ while( pPage->GetNext() && pPage->getFrameArea().Bottom() < rPoint.Y() )
+ pPage = static_cast<const SwLayoutFrame*>(pPage->GetNext());
+
+ const SwContentFrame *pCnt = pPage ? pPage->ContainsContent() : ContainsContent();
+ while ( pCnt && !pCnt->IsInDocBody() )
+ pCnt = pCnt->GetNextContentFrame();
+
+ if ( !pCnt )
+ return Point( 0, 0 );
+
+ pCnt->Calc(pRenderContext);
+ if( !bNext )
+ {
+ // As long as the point lies before the first ContentFrame and there are
+ // still precedent pages I'll go to the next page.
+ while ( rPoint.Y() < pCnt->getFrameArea().Top() && pPage->GetPrev() )
+ {
+ pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
+ pCnt = pPage->ContainsContent();
+ while ( !pCnt )
+ {
+ pPage = static_cast<const SwLayoutFrame*>(pPage->GetPrev());
+ if ( pPage )
+ pCnt = pPage->ContainsContent();
+ else
+ return ContainsContent()->UnionFrame().Pos();
+ }
+ pCnt->Calc(pRenderContext);
+ }
+ }
+
+ //Does the point lie above the first ContentFrame?
+ if ( rPoint.Y() < pCnt->getFrameArea().Top() && !lcl_IsInRepeatedHeadline( pCnt ) )
+ return pCnt->UnionFrame().Pos();
+
+ Point aRet(0, 0);
+ do
+ {
+ //Does the point lie in the current ContentFrame?
+ SwRect aContentFrame( pCnt->UnionFrame() );
+ if ( aContentFrame.Contains( rPoint ) && !lcl_IsInRepeatedHeadline( pCnt ))
+ {
+ aRet = rPoint;
+ break;
+ }
+
+ //Is the current one the last ContentFrame?
+ //If the next ContentFrame lies behind the point, then the current on is the
+ //one we searched.
+ const SwContentFrame *pNxt = pCnt->GetNextContentFrame();
+ while ( pNxt && !pNxt->IsInDocBody() )
+ pNxt = pNxt->GetNextContentFrame();
+
+ //Does the point lie behind the last ContentFrame?
+ if ( !pNxt )
+ {
+ aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
+ break;
+ }
+
+ //If the next ContentFrame lies behind the point then it is the one we
+ //searched.
+ const SwTabFrame* pTFrame;
+ pNxt->Calc(pRenderContext);
+ if( pNxt->getFrameArea().Top() > rPoint.Y() &&
+ !lcl_IsInRepeatedHeadline( pCnt, &pTFrame ) &&
+ ( !pTFrame || pNxt->getFrameArea().Left() > rPoint.X() ))
+ {
+ if (bNext)
+ aRet = pNxt->getFrameArea().Pos();
+ else
+ aRet = Point( aContentFrame.Right(), aContentFrame.Bottom() );
+ break;
+ }
+ pCnt = pNxt;
+ }
+ while (pCnt);
+ return aRet;
+}
+
+/** Returns the absolute document position of the desired page.
+ *
+ * Formatting is done only as far as needed and only if bFormat=true.
+ * Pos is set to the one of the last page, if the page number was chosen too big.
+ *
+ * @return Null, if the operation failed.
+ */
+Point SwRootFrame::GetPagePos( sal_uInt16 nPageNum ) const
+{
+ OSL_ENSURE( Lower() && Lower()->IsPageFrame(), "No page available." );
+
+ const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
+ while ( true )
+ {
+ if ( pPage->GetPhyPageNum() >= nPageNum || !pPage->GetNext() )
+ break;
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ return pPage->getFrameArea().Pos();
+}
+
+/** get page frame by physical page number
+ *
+ * @return pointer to the page frame with the given physical page number
+ */
+SwPageFrame* SwRootFrame::GetPageByPageNum( sal_uInt16 _nPageNum ) const
+{
+ const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>( Lower() );
+ while ( pPageFrame && pPageFrame->GetPhyPageNum() < _nPageNum )
+ {
+ pPageFrame = static_cast<const SwPageFrame*>( pPageFrame->GetNext() );
+ }
+
+ if ( pPageFrame && pPageFrame->GetPhyPageNum() == _nPageNum )
+ {
+ return const_cast<SwPageFrame*>( pPageFrame );
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+/**
+ * @return true, when the given physical pagenumber doesn't exist or this page is an empty page.
+ */
+bool SwRootFrame::IsDummyPage( sal_uInt16 nPageNum ) const
+{
+ if( !Lower() || !nPageNum || nPageNum > GetPageNum() )
+ return true;
+
+ const SwPageFrame *pPage = static_cast<const SwPageFrame*>(Lower());
+ while( pPage && nPageNum < pPage->GetPhyPageNum() )
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ return !pPage || pPage->IsEmptyPage();
+}
+
+/** Is the Frame or rather the Section in which it lies protected?
+ *
+ * Also Fly in Fly in ... and Footnotes
+ */
+bool SwFrame::IsProtected() const
+{
+ if (IsTextFrame())
+ {
+ const SwDoc *pDoc = &static_cast<const SwTextFrame*>(this)->GetDoc();
+ bool isFormProtected=pDoc->GetDocumentSettingManager().get(DocumentSettingId::PROTECT_FORM );
+ if (isFormProtected)
+ {
+ return false; // TODO a hack for now, well deal with it later, I we return true here we have a "double" locking
+ }
+ }
+ //The Frame can be protected in borders, cells or sections.
+ //Also goes up FlyFrames recursive and from footnote to anchor.
+ const SwFrame *pFrame = this;
+ do
+ {
+ if (pFrame->IsTextFrame())
+ { // sw_redlinehide: redlines can't overlap section nodes, so any node will do
+ if (static_cast<SwTextFrame const*>(pFrame)->GetTextNodeFirst()->IsInProtectSect())
+ {
+ return true;
+ }
+ }
+ else if ( pFrame->IsContentFrame() )
+ {
+ assert(pFrame->IsNoTextFrame());
+ if (static_cast<const SwNoTextFrame*>(pFrame)->GetNode() &&
+ static_cast<const SwNoTextFrame*>(pFrame)->GetNode()->IsInProtectSect())
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if ( static_cast<const SwLayoutFrame*>(pFrame)->GetFormat() &&
+ static_cast<const SwLayoutFrame*>(pFrame)->GetFormat()->
+ GetProtect().IsContentProtected() )
+ return true;
+ if ( pFrame->IsCoveredCell() )
+ return true;
+ }
+ if ( pFrame->IsFlyFrame() )
+ {
+ //In a chain the protection of the content can be specified by the
+ //master of the chain.
+ if ( static_cast<const SwFlyFrame*>(pFrame)->GetPrevLink() )
+ {
+ const SwFlyFrame *pMaster = static_cast<const SwFlyFrame*>(pFrame);
+ do
+ { pMaster = pMaster->GetPrevLink();
+ } while ( pMaster->GetPrevLink() );
+ if ( pMaster->IsProtected() )
+ return true;
+ }
+ pFrame = static_cast<const SwFlyFrame*>(pFrame)->GetAnchorFrame();
+ }
+ else if ( pFrame->IsFootnoteFrame() )
+ pFrame = static_cast<const SwFootnoteFrame*>(pFrame)->GetRef();
+ else
+ pFrame = pFrame->GetUpper();
+
+ } while ( pFrame );
+
+ return false;
+}
+
+/** @return the physical page number */
+sal_uInt16 SwFrame::GetPhyPageNum() const
+{
+ const SwPageFrame *pPage = FindPageFrame();
+ return pPage ? pPage->GetPhyPageNum() : 0;
+}
+
+/** Decides if the page want to be a right page or not.
+ *
+ * If the first content of the page has a page descriptor, we take the follow
+ * of the page descriptor of the last not empty page. If this descriptor allows
+ * only right(left) pages and the page isn't an empty page then it wants to be
+ * such right(left) page. If the descriptor allows right and left pages, we
+ * look for a number offset in the first content. If there is one, odd number
+ * results right pages (or left pages if document starts with even number),
+ * even number results left pages (or right pages if document starts with even
+ * number).
+ * If there is no number offset, we take the physical page number instead,
+ * but a previous empty page doesn't count.
+ */
+bool SwFrame::WannaRightPage() const
+{
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( !pPage || !pPage->GetUpper() )
+ return true;
+
+ const SwFrame *pFlow = pPage->FindFirstBodyContent();
+ const SwPageDesc *pDesc = nullptr;
+ ::std::optional<sal_uInt16> oPgNum;
+ if ( pFlow )
+ {
+ if ( pFlow->IsInTab() )
+ pFlow = pFlow->FindTabFrame();
+ const SwFlowFrame *pTmp = SwFlowFrame::CastFlowFrame( pFlow );
+ if ( !pTmp->IsFollow() )
+ {
+ const SwFormatPageDesc& rPgDesc = pFlow->GetPageDescItem();
+ pDesc = rPgDesc.GetPageDesc();
+ oPgNum = rPgDesc.GetNumOffset();
+ }
+ }
+ if ( !pDesc )
+ {
+ SwPageFrame *pPrv = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pPage->GetPrev()));
+ if( pPrv && pPrv->IsEmptyPage() )
+ pPrv = static_cast<SwPageFrame*>(pPrv->GetPrev());
+ if( pPrv )
+ pDesc = pPrv->GetPageDesc()->GetFollow();
+ else
+ {
+ const SwDoc* pDoc = pPage->GetFormat()->GetDoc();
+ pDesc = &pDoc->GetPageDesc( 0 );
+ }
+ }
+ OSL_ENSURE( pDesc, "No pagedescriptor" );
+ bool isRightPage;
+ if( oPgNum )
+ isRightPage = sw::IsRightPageByNumber(*mpRoot, *oPgNum);
+ else
+ {
+ isRightPage = pPage->OnRightPage();
+ if( pPage->GetPrev() && static_cast<const SwPageFrame*>(pPage->GetPrev())->IsEmptyPage() )
+ isRightPage = !isRightPage;
+ }
+ if( !pPage->IsEmptyPage() )
+ {
+ if( !pDesc->GetRightFormat() )
+ isRightPage = false;
+ else if( !pDesc->GetLeftFormat() )
+ isRightPage = true;
+ }
+ return isRightPage;
+}
+
+bool SwFrame::OnFirstPage() const
+{
+ bool bRet = false;
+ const SwPageFrame *pPage = FindPageFrame();
+
+ if (pPage)
+ {
+ const SwPageFrame* pPrevFrame = dynamic_cast<const SwPageFrame*>(pPage->GetPrev());
+ if (pPrevFrame)
+ {
+ // first page of layout may be empty page, but only if it starts with "Left Page" style
+ const SwPageDesc* pDesc = pPage->GetPageDesc();
+ bRet = pPrevFrame->GetPageDesc() != pDesc;
+ }
+ else
+ bRet = true;
+ }
+ return bRet;
+}
+
+void SwFrame::Calc(vcl::RenderContext* pRenderContext) const
+{
+ if ( !isFrameAreaPositionValid() || !isFramePrintAreaValid() || !isFrameAreaSizeValid() )
+ {
+ const_cast<SwFrame*>(this)->PrepareMake(pRenderContext);
+ }
+}
+
+Point SwFrame::GetRelPos() const
+{
+ Point aRet( getFrameArea().Pos() );
+ // here we cast since SwLayoutFrame is declared only as forwarded
+ aRet -= GetUpper()->getFramePrintArea().Pos();
+ aRet -= GetUpper()->getFrameArea().Pos();
+ return aRet;
+}
+
+/** @return the virtual page number with the offset. */
+sal_uInt16 SwFrame::GetVirtPageNum() const
+{
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( !pPage || !pPage->GetUpper() )
+ return 0;
+
+ sal_uInt16 nPhyPage = pPage->GetPhyPageNum();
+ if ( !static_cast<const SwRootFrame*>(pPage->GetUpper())->IsVirtPageNum() )
+ return nPhyPage;
+
+ //Search the nearest section using the virtual page number.
+ //Because searching backwards needs a lot of time we search specific using
+ //the dependencies. From the PageDescs we get the attributes and from the
+ //attributes we get the sections.
+ const SwPageFrame *pVirtPage = nullptr;
+ const SwFrame *pFrame = nullptr;
+ const SfxItemPool &rPool = pPage->GetFormat()->GetDoc()->GetAttrPool();
+ for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(RES_PAGEDESC))
+ {
+ const SwFormatPageDesc *pDesc = dynamic_cast<const SwFormatPageDesc*>(pItem);
+ if ( !pDesc )
+ continue;
+
+ if ( pDesc->GetNumOffset() && pDesc->GetDefinedIn() )
+ {
+ const sw::BroadcastingModify *pMod = pDesc->GetDefinedIn();
+ SwVirtPageNumInfo aInfo( pPage );
+ pMod->GetInfo( aInfo );
+ if ( aInfo.GetPage() )
+ {
+ if( !pVirtPage || aInfo.GetPage()->GetPhyPageNum() > pVirtPage->GetPhyPageNum() )
+ {
+ pVirtPage = aInfo.GetPage();
+ pFrame = aInfo.GetFrame();
+ }
+ }
+ }
+ }
+ if ( pFrame )
+ {
+ ::std::optional<sal_uInt16> oNumOffset = pFrame->GetPageDescItem().GetNumOffset();
+ if (oNumOffset)
+ {
+ return nPhyPage - pFrame->GetPhyPageNum() + *oNumOffset;
+ }
+ else
+ {
+ return nPhyPage - pFrame->GetPhyPageNum();
+ }
+ }
+ return nPhyPage;
+}
+
+/** Determines and sets those cells which are enclosed by the selection. */
+bool SwRootFrame::MakeTableCursors( SwTableCursor& rTableCursor )
+{
+ //Find Union-Rects and tables (Follows) of the selection.
+ OSL_ENSURE( rTableCursor.GetContentNode() && rTableCursor.GetContentNode( false ),
+ "Tabselection not on Cnt." );
+
+ bool bRet = false;
+
+ // For new table models there's no need to ask the layout...
+ if( rTableCursor.NewTableSelection() )
+ return true;
+
+ Point aPtPt, aMkPt;
+ {
+ SwShellCursor* pShCursor = dynamic_cast<SwShellCursor*>(&rTableCursor);
+
+ if( pShCursor )
+ {
+ aPtPt = pShCursor->GetPtPos();
+ aMkPt = pShCursor->GetMkPos();
+ }
+ }
+
+ // #151012# Made code robust here
+ const SwContentNode* pTmpStartNode = rTableCursor.GetContentNode();
+ const SwContentNode* pTmpEndNode = rTableCursor.GetContentNode(false);
+
+ std::pair<Point, bool> tmp(aPtPt, false);
+ const SwFrame *const pTmpStartFrame = pTmpStartNode ? pTmpStartNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
+ tmp.first = aMkPt;
+ const SwFrame *const pTmpEndFrame = pTmpEndNode ? pTmpEndNode->getLayoutFrame(this, nullptr, &tmp) : nullptr;
+
+ const SwLayoutFrame* pStart = pTmpStartFrame ? pTmpStartFrame->GetUpper() : nullptr;
+ const SwLayoutFrame* pEnd = pTmpEndFrame ? pTmpEndFrame->GetUpper() : nullptr;
+
+ OSL_ENSURE( pStart && pEnd, "MakeTableCursors: Good to have the code robust here!" );
+
+ /* #109590# Only change table boxes if the frames are
+ valid. Needed because otherwise the table cursor after moving
+ table cells by dnd resulted in an empty tables cursor. */
+ if ( pStart && pEnd && pStart->isFrameAreaDefinitionValid() && pEnd->isFrameAreaDefinitionValid())
+ {
+ SwSelUnions aUnions;
+ ::MakeSelUnions( aUnions, pStart, pEnd );
+
+ SwSelBoxes aNew;
+
+ const bool bReadOnlyAvailable = rTableCursor.IsReadOnlyAvailable();
+
+ for (SwSelUnion & rUnion : aUnions)
+ {
+ const SwTabFrame *pTable = rUnion.GetTable();
+
+ // Skip any repeated headlines in the follow:
+ SwLayoutFrame* pRow = pTable->IsFollow() ?
+ pTable->GetFirstNonHeadlineRow() :
+ const_cast<SwLayoutFrame*>(static_cast<const SwLayoutFrame*>(pTable->Lower()));
+
+ while ( pRow )
+ {
+ if ( pRow->getFrameArea().Overlaps( rUnion.GetUnion() ) )
+ {
+ const SwLayoutFrame *pCell = pRow->FirstCell();
+
+ while ( pCell && pRow->IsAnLower( pCell ) )
+ {
+ OSL_ENSURE( pCell->IsCellFrame(), "Frame without cell" );
+ if( IsFrameInTableSel( rUnion.GetUnion(), pCell ) &&
+ (bReadOnlyAvailable ||
+ !pCell->GetFormat()->GetProtect().IsContentProtected()))
+ {
+ SwTableBox* pInsBox = const_cast<SwTableBox*>(
+ static_cast<const SwCellFrame*>(pCell)->GetTabBox());
+ aNew.insert( pInsBox );
+ }
+ if ( pCell->GetNext() )
+ {
+ pCell = static_cast<const SwLayoutFrame*>(pCell->GetNext());
+ if ( pCell->Lower() && pCell->Lower()->IsRowFrame() )
+ pCell = pCell->FirstCell();
+ }
+ else
+ {
+ const SwLayoutFrame* pLastCell = pCell;
+ do
+ {
+ pCell = pCell->GetNextLayoutLeaf();
+ } while ( pCell && pLastCell->IsAnLower( pCell ) );
+ // For sections with columns
+ if( pCell && pCell->IsInTab() )
+ {
+ while( !pCell->IsCellFrame() )
+ {
+ pCell = pCell->GetUpper();
+ OSL_ENSURE( pCell, "Where's my cell?" );
+ }
+ }
+ }
+ }
+ }
+ pRow = static_cast<SwLayoutFrame*>(pRow->GetNext());
+ }
+ }
+
+ rTableCursor.ActualizeSelection( aNew );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+static void Sub( SwRegionRects& rRegion, const SwRect& rRect )
+{
+ if( rRect.Width() > 1 && rRect.Height() > 1 &&
+ rRect.Overlaps( rRegion.GetOrigin() ))
+ rRegion -= rRect;
+}
+
+static void Add( SwRegionRects& rRegion, const SwRect& rRect )
+{
+ if( rRect.Width() > 1 && rRect.Height() > 1 )
+ rRegion += rRect;
+}
+
+/*
+ * The following situations can happen:
+ * 1. Start and end lie in one screen-row and in the same node
+ * -> one rectangle out of start and end; and we're okay
+ * 2. Start and end lie in one frame (therefore in the same node!)
+ * -> expand start to the right, end to the left and if more than two
+ * screen-rows are involved - calculate the in-between
+ * 3. Start and end lie in different frames
+ * -> expand start to the right until frame-end, calculate Rect
+ * expand end to the left until frame-start, calculate Rect
+ * and if more than two frames are involved add the PrtArea of all
+ * frames which lie in between
+ *
+ * Big reorganization because of the FlyFrame - those need to be locked out.
+ * Exceptions: - The Fly in which the selection took place (if it took place
+ * in a Fly)
+ * - The Flys which are underrun by the text
+ * - The Flys which are anchored to somewhere inside the selection.
+ * Functioning: First a SwRegion with a root gets initialized.
+ * Out of the region the inverted sections are cut out. The
+ * section gets compressed and finally inverted and thereby the
+ * inverted rectangles are available.
+ * In the end the Flys are cut out of the section.
+ */
+void SwRootFrame::CalcFrameRects(SwShellCursor &rCursor)
+{
+ SwPosition *pStartPos = rCursor.Start(),
+ *pEndPos = rCursor.End();
+
+ SwViewShell *pSh = GetCurrShell();
+
+ bool bIgnoreVisArea = true;
+ if (pSh)
+ bIgnoreVisArea = pSh->GetViewOptions()->IsPDFExport() || comphelper::LibreOfficeKit::isActive();
+
+ // #i12836# enhanced pdf
+ SwRegionRects aRegion( !bIgnoreVisArea ?
+ pSh->VisArea() :
+ getFrameArea() );
+ if( !pStartPos->nNode.GetNode().IsContentNode() ||
+ !pStartPos->nNode.GetNode().GetContentNode()->getLayoutFrame(this) ||
+ ( pStartPos->nNode != pEndPos->nNode &&
+ ( !pEndPos->nNode.GetNode().IsContentNode() ||
+ !pEndPos->nNode.GetNode().GetContentNode()->getLayoutFrame(this) ) ) )
+ {
+ return;
+ }
+
+ DisableCallbackAction a(*this); // the GetCharRect below may format
+
+ //First obtain the ContentFrames for the start and the end - those are needed
+ //anyway.
+ std::pair<Point, bool> tmp(rCursor.GetSttPos(), true);
+ SwContentFrame* pStartFrame = pStartPos->nNode.GetNode().
+ GetContentNode()->getLayoutFrame(this, pStartPos, &tmp);
+
+ tmp.first = rCursor.GetEndPos();
+ SwContentFrame* pEndFrame = pEndPos->nNode.GetNode().
+ GetContentNode()->getLayoutFrame(this, pEndPos, &tmp);
+
+ assert(pStartFrame && pEndFrame && "No ContentFrames found.");
+ //tdf#119224 start and end are expected to exist for the scope of this function
+ SwFrameDeleteGuard aStartFrameGuard(pStartFrame), aEndFrameGuard(pEndFrame);
+
+ //Do not subtract the FlyFrames in which selected Frames lie.
+ SwSortedObjs aSortObjs;
+ if ( pStartFrame->IsInFly() )
+ {
+ const SwAnchoredObject* pObj = pStartFrame->FindFlyFrame();
+ OSL_ENSURE( pObj, "No Start Object." );
+ if (pObj) aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
+ const SwAnchoredObject* pObj2 = pEndFrame->FindFlyFrame();
+ OSL_ENSURE( pObj2, "SwRootFrame::CalcFrameRects(..) - FlyFrame missing - looks like an invalid selection" );
+ if ( pObj2 != nullptr && pObj2 != pObj )
+ {
+ aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj2) );
+ }
+ }
+
+ // if a selection which is not allowed exists, we correct what is not
+ // allowed (header/footer/table-headline) for two pages.
+ do { // middle check loop
+ const SwLayoutFrame* pSttLFrame = pStartFrame->GetUpper();
+ const SwFrameType cHdFtTableHd = SwFrameType::Header | SwFrameType::Footer | SwFrameType::Tab;
+ while( pSttLFrame &&
+ ! (cHdFtTableHd & pSttLFrame->GetType() ))
+ pSttLFrame = pSttLFrame->GetUpper();
+ if( !pSttLFrame )
+ break;
+ const SwLayoutFrame* pEndLFrame = pEndFrame->GetUpper();
+ while( pEndLFrame &&
+ ! (cHdFtTableHd & pEndLFrame->GetType() ))
+ pEndLFrame = pEndLFrame->GetUpper();
+ if( !pEndLFrame )
+ break;
+
+ OSL_ENSURE( pEndLFrame->GetType() == pSttLFrame->GetType(),
+ "Selection over different content" );
+ switch( pSttLFrame->GetType() )
+ {
+ case SwFrameType::Header:
+ case SwFrameType::Footer:
+ // On different pages? Then always on the start-page
+ if( pEndLFrame->FindPageFrame() != pSttLFrame->FindPageFrame() )
+ {
+ // Set end- to the start-ContentFrame
+ if( pStartPos == rCursor.GetPoint() )
+ pEndFrame = pStartFrame;
+ else
+ pStartFrame = pEndFrame;
+ }
+ break;
+ case SwFrameType::Tab:
+ // On different pages? Then check for table-headline
+ {
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pSttLFrame);
+ if( ( pTabFrame->GetFollow() ||
+ static_cast<const SwTabFrame*>(pEndLFrame)->GetFollow() ) &&
+ pTabFrame->GetTable()->GetRowsToRepeat() > 0 &&
+ pTabFrame->GetLower() != static_cast<const SwTabFrame*>(pEndLFrame)->GetLower() &&
+ ( lcl_IsInRepeatedHeadline( pStartFrame ) ||
+ lcl_IsInRepeatedHeadline( pEndFrame ) ) )
+ {
+ // Set end- to the start-ContentFrame
+ if( pStartPos == rCursor.GetPoint() )
+ pEndFrame = pStartFrame;
+ else
+ pStartFrame = pEndFrame;
+ }
+ }
+ break;
+ default: break;
+ }
+ } while( false );
+
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ aTmpState.m_b2Lines = true;
+ aTmpState.m_bNoScroll = true;
+ aTmpState.m_nCursorBidiLevel = pStartFrame->IsRightToLeft() ? 1 : 0;
+
+ //ContentRects to Start- and EndFrames.
+ SwRect aStRect, aEndRect;
+ pStartFrame->GetCharRect( aStRect, *pStartPos, &aTmpState );
+ std::unique_ptr<Sw2LinesPos> pSt2Pos = std::move(aTmpState.m_p2Lines);
+ aTmpState.m_nCursorBidiLevel = pEndFrame->IsRightToLeft() ? 1 : 0;
+
+ pEndFrame->GetCharRect( aEndRect, *pEndPos, &aTmpState );
+ std::unique_ptr<Sw2LinesPos> pEnd2Pos = std::move(aTmpState.m_p2Lines);
+
+ SwRect aStFrame ( pStartFrame->UnionFrame( true ) );
+ aStFrame.Intersection( pStartFrame->GetPaintArea() );
+ SwRect aEndFrame( pStartFrame == pEndFrame ? aStFrame : pEndFrame->UnionFrame( true ) );
+ if( pStartFrame != pEndFrame )
+ {
+ aEndFrame.Intersection( pEndFrame->GetPaintArea() );
+ }
+ SwRectFnSet aRectFnSet(pStartFrame);
+ const bool bR2L = pStartFrame->IsRightToLeft();
+ const bool bEndR2L = pEndFrame->IsRightToLeft();
+ const bool bB2T = pStartFrame->IsVertLRBT();
+
+ // If there's no doubleline portion involved or start and end are both
+ // in the same doubleline portion, all works fine, but otherwise
+ // we need the following...
+ if( pSt2Pos != pEnd2Pos && ( !pSt2Pos || !pEnd2Pos ||
+ pSt2Pos->aPortion != pEnd2Pos->aPortion ) )
+ {
+ // If we have a start(end) position inside a doubleline portion
+ // the surrounded part of the doubleline portion is subtracted
+ // from the region and the aStRect(aEndRect) is set to the
+ // end(start) of the doubleline portion.
+ if( pSt2Pos )
+ {
+ SwRect aTmp( aStRect );
+
+ // BiDi-Portions are swimming against the current.
+ const bool bPorR2L = ( MultiPortionType::BIDI == pSt2Pos->nMultiType ) ?
+ ! bR2L :
+ bR2L;
+
+ if( MultiPortionType::BIDI == pSt2Pos->nMultiType &&
+ aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
+ {
+ // nested bidi portion
+ tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
+ nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
+
+ aRectFnSet.SetRight( aTmp, nRightAbs );
+
+ if ( ! pEnd2Pos || pEnd2Pos->aPortion != pSt2Pos->aPortion )
+ {
+ SwRect aTmp2( pSt2Pos->aPortion );
+ aRectFnSet.SetRight( aTmp2, nLeftAbs );
+ aTmp2.Intersection( aEndFrame );
+ Sub( aRegion, aTmp2 );
+ }
+ }
+ else
+ {
+ if( bPorR2L )
+ aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
+ else
+ aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
+ }
+
+ if( MultiPortionType::ROT_90 == pSt2Pos->nMultiType ||
+ aRectFnSet.GetTop(pSt2Pos->aPortion) ==
+ aRectFnSet.GetTop(aTmp) )
+ {
+ aRectFnSet.SetTop( aTmp, aRectFnSet.GetTop(pSt2Pos->aLine) );
+ }
+
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+
+ SwTwips nTmp = aRectFnSet.GetBottom(pSt2Pos->aLine);
+ if( MultiPortionType::ROT_90 != pSt2Pos->nMultiType &&
+ aRectFnSet.BottomDist( aStRect, nTmp ) > 0 )
+ {
+ aRectFnSet.SetTop( aTmp, aRectFnSet.GetBottom(aTmp) );
+ aRectFnSet.SetBottom( aTmp, nTmp );
+ if( aRectFnSet.BottomDist( aStRect, aRectFnSet.GetBottom(pSt2Pos->aPortion) ) > 0 )
+ {
+ if( bPorR2L )
+ aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(pSt2Pos->aPortion) );
+ else
+ aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(pSt2Pos->aPortion) );
+ }
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+ }
+
+ aStRect = pSt2Pos->aLine;
+ aRectFnSet.SetLeft( aStRect, bR2L ?
+ aRectFnSet.GetLeft(pSt2Pos->aPortion) :
+ aRectFnSet.GetRight(pSt2Pos->aPortion) );
+ aRectFnSet.SetWidth( aStRect, 1 );
+ }
+
+ if( pEnd2Pos )
+ {
+ SwRectFnSet fnRectX(pEndFrame);
+ SwRect aTmp( aEndRect );
+
+ // BiDi-Portions are swimming against the current.
+ const bool bPorR2L = ( MultiPortionType::BIDI == pEnd2Pos->nMultiType ) ?
+ ! bEndR2L :
+ bEndR2L;
+
+ if( MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
+ fnRectX.GetWidth(pEnd2Pos->aPortion2) )
+ {
+ // nested bidi portion
+ tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
+ nRightAbs = nRightAbs - fnRectX.GetLeft(pEnd2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
+
+ fnRectX.SetLeft( aTmp, nLeftAbs );
+
+ if ( ! pSt2Pos || pSt2Pos->aPortion != pEnd2Pos->aPortion )
+ {
+ SwRect aTmp2( pEnd2Pos->aPortion );
+ fnRectX.SetLeft( aTmp2, nRightAbs );
+ aTmp2.Intersection( aEndFrame );
+ Sub( aRegion, aTmp2 );
+ }
+ }
+ else
+ {
+ if ( bPorR2L )
+ fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
+ else
+ fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
+ }
+
+ if( MultiPortionType::ROT_90 == pEnd2Pos->nMultiType ||
+ fnRectX.GetBottom(pEnd2Pos->aPortion) ==
+ fnRectX.GetBottom(aEndRect) )
+ {
+ fnRectX.SetBottom( aTmp, fnRectX.GetBottom(pEnd2Pos->aLine) );
+ }
+
+ aTmp.Intersection( aEndFrame );
+ Sub( aRegion, aTmp );
+
+ // The next statement means neither ruby nor rotate(90):
+ if( MultiPortionType::RUBY != pEnd2Pos->nMultiType && MultiPortionType::ROT_90 != pEnd2Pos->nMultiType )
+ {
+ SwTwips nTmp = fnRectX.GetTop(pEnd2Pos->aLine);
+ if( fnRectX.GetTop(aEndRect) != nTmp )
+ {
+ fnRectX.SetBottom( aTmp, fnRectX.GetTop(aTmp) );
+ fnRectX.SetTop( aTmp, nTmp );
+ if( fnRectX.GetTop(aEndRect) !=
+ fnRectX.GetTop(pEnd2Pos->aPortion) )
+ {
+ if( bPorR2L )
+ fnRectX.SetLeft( aTmp, fnRectX.GetLeft(pEnd2Pos->aPortion) );
+ else
+ fnRectX.SetRight( aTmp, fnRectX.GetRight(pEnd2Pos->aPortion) );
+ }
+ aTmp.Intersection( aEndFrame );
+ Sub( aRegion, aTmp );
+ }
+ }
+
+ aEndRect = pEnd2Pos->aLine;
+ fnRectX.SetLeft( aEndRect, bEndR2L ?
+ fnRectX.GetRight(pEnd2Pos->aPortion) :
+ fnRectX.GetLeft(pEnd2Pos->aPortion) );
+ fnRectX.SetWidth( aEndRect, 1 );
+ }
+ }
+ else if( pSt2Pos && pEnd2Pos &&
+ MultiPortionType::BIDI == pSt2Pos->nMultiType &&
+ MultiPortionType::BIDI == pEnd2Pos->nMultiType &&
+ pSt2Pos->aPortion == pEnd2Pos->aPortion &&
+ pSt2Pos->aPortion2 != pEnd2Pos->aPortion2 )
+ {
+ // This is the ugly special case, where the selection starts and
+ // ends in the same bidi portion but one start or end is inside a
+ // nested bidi portion.
+
+ if ( aRectFnSet.GetWidth(pSt2Pos->aPortion2) )
+ {
+ SwRect aTmp( aStRect );
+ tools::Long nRightAbs = aRectFnSet.GetRight(pSt2Pos->aPortion);
+ nRightAbs -= aRectFnSet.GetLeft(pSt2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - aRectFnSet.GetWidth(pSt2Pos->aPortion2);
+
+ aRectFnSet.SetRight( aTmp, nRightAbs );
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+
+ aStRect = pSt2Pos->aLine;
+ aRectFnSet.SetLeft( aStRect, bR2L ? nRightAbs : nLeftAbs );
+ aRectFnSet.SetWidth( aStRect, 1 );
+ }
+
+ SwRectFnSet fnRectX(pEndFrame);
+ if ( fnRectX.GetWidth(pEnd2Pos->aPortion2) )
+ {
+ SwRect aTmp( aEndRect );
+ tools::Long nRightAbs = fnRectX.GetRight(pEnd2Pos->aPortion);
+ nRightAbs -= fnRectX.GetLeft(pEnd2Pos->aPortion2);
+ tools::Long nLeftAbs = nRightAbs - fnRectX.GetWidth(pEnd2Pos->aPortion2);
+
+ fnRectX.SetLeft( aTmp, nLeftAbs );
+ aTmp.Intersection( aEndFrame );
+ Sub( aRegion, aTmp );
+
+ aEndRect = pEnd2Pos->aLine;
+ fnRectX.SetLeft( aEndRect, bEndR2L ? nLeftAbs : nRightAbs );
+ fnRectX.SetWidth( aEndRect, 1 );
+ }
+ }
+
+ // The charrect may be outside the paintarea (for cursortravelling)
+ // but the selection has to be restricted to the paintarea
+ if( aStRect.Left() < aStFrame.Left() )
+ aStRect.Left( aStFrame.Left() );
+ else if( aStRect.Left() > aStFrame.Right() )
+ aStRect.Left( aStFrame.Right() );
+ SwTwips nTmp = aStRect.Right();
+ if( nTmp < aStFrame.Left() )
+ aStRect.Right( aStFrame.Left() );
+ else if( nTmp > aStFrame.Right() )
+ aStRect.Right( aStFrame.Right() );
+ if( aEndRect.Left() < aEndFrame.Left() )
+ aEndRect.Left( aEndFrame.Left() );
+ else if( aEndRect.Left() > aEndFrame.Right() )
+ aEndRect.Left( aEndFrame.Right() );
+ nTmp = aEndRect.Right();
+ if( nTmp < aEndFrame.Left() )
+ aEndRect.Right( aEndFrame.Left() );
+ else if( nTmp > aEndFrame.Right() )
+ aEndRect.Right( aEndFrame.Right() );
+
+ if( pStartFrame == pEndFrame )
+ {
+ bool bSameRotatedOrBidi = pSt2Pos && pEnd2Pos &&
+ ( MultiPortionType::BIDI == pSt2Pos->nMultiType ||
+ MultiPortionType::ROT_270 == pSt2Pos->nMultiType ||
+ MultiPortionType::ROT_90 == pSt2Pos->nMultiType ) &&
+ pSt2Pos->aPortion == pEnd2Pos->aPortion;
+ //case 1: (Same frame and same row)
+ if( bSameRotatedOrBidi ||
+ aRectFnSet.GetTop(aStRect) == aRectFnSet.GetTop(aEndRect) )
+ {
+ Point aTmpSt( aStRect.Pos() );
+ Point aTmpEnd( aEndRect.Right(), aEndRect.Bottom() );
+ if (bSameRotatedOrBidi || bR2L || bB2T)
+ {
+ if( aTmpSt.Y() > aTmpEnd.Y() )
+ {
+ tools::Long nTmpY = aTmpEnd.Y();
+ aTmpEnd.setY( aTmpSt.Y() );
+ aTmpSt.setY( nTmpY );
+ }
+ if( aTmpSt.X() > aTmpEnd.X() )
+ {
+ tools::Long nTmpX = aTmpEnd.X();
+ aTmpEnd.setX( aTmpSt.X() );
+ aTmpSt.setX( nTmpX );
+ }
+ }
+
+ SwRect aTmp( aTmpSt, aTmpEnd );
+ // Bug 34888: If content is selected which doesn't take space
+ // away (i.e. PostIts, RefMarks, TOXMarks), then at
+ // least set the width of the Cursor.
+ if( 1 == aRectFnSet.GetWidth(aTmp) &&
+ pStartPos->nContent.GetIndex() !=
+ pEndPos->nContent.GetIndex() )
+ {
+ OutputDevice* pOut = pSh->GetOut();
+ tools::Long nCursorWidth = pOut->GetSettings().GetStyleSettings().
+ GetCursorSize();
+ aRectFnSet.SetWidth( aTmp, pOut->PixelToLogic(
+ Size( nCursorWidth, 0 ) ).Width() );
+ }
+ aTmp.Intersection( aStFrame );
+ Sub( aRegion, aTmp );
+ }
+ //case 2: (Same frame, but not the same line)
+ else
+ {
+ SwTwips lLeft, lRight;
+ if( pSt2Pos && pEnd2Pos && pSt2Pos->aPortion == pEnd2Pos->aPortion )
+ {
+ lLeft = aRectFnSet.GetLeft(pSt2Pos->aPortion);
+ lRight = aRectFnSet.GetRight(pSt2Pos->aPortion);
+ }
+ else
+ {
+ lLeft = aRectFnSet.GetLeft(pStartFrame->getFrameArea()) +
+ aRectFnSet.GetLeft(pStartFrame->getFramePrintArea());
+ lRight = aRectFnSet.GetRight(aEndFrame);
+ }
+ if( lLeft < aRectFnSet.GetLeft(aStFrame) )
+ lLeft = aRectFnSet.GetLeft(aStFrame);
+ if( lRight > aRectFnSet.GetRight(aStFrame) )
+ lRight = aRectFnSet.GetRight(aStFrame);
+ SwRect aSubRect( aStRect );
+ //First line
+ if( bR2L )
+ aRectFnSet.SetLeft( aSubRect, lLeft );
+ else
+ aRectFnSet.SetRight( aSubRect, lRight );
+ Sub( aRegion, aSubRect );
+
+ //If there's at least a twips between start- and endline,
+ //so the whole area between will be added.
+ SwTwips aTmpBottom = aRectFnSet.GetBottom(aStRect);
+ SwTwips aTmpTop = aRectFnSet.GetTop(aEndRect);
+ if( aTmpBottom != aTmpTop )
+ {
+ aRectFnSet.SetLeft( aSubRect, lLeft );
+ aRectFnSet.SetRight( aSubRect, lRight );
+ aRectFnSet.SetTop( aSubRect, aTmpBottom );
+ aRectFnSet.SetBottom( aSubRect, aTmpTop );
+ Sub( aRegion, aSubRect );
+ }
+ //and the last line
+ aSubRect = aEndRect;
+ if( bR2L )
+ aRectFnSet.SetRight( aSubRect, lRight );
+ else
+ aRectFnSet.SetLeft( aSubRect, lLeft );
+ Sub( aRegion, aSubRect );
+ }
+ }
+ //case 3: (Different frames, maybe with other frames between)
+ else
+ {
+ //The startframe first...
+ SwRect aSubRect( aStRect );
+ if( bR2L )
+ aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aStFrame));
+ else
+ aRectFnSet.SetRight( aSubRect, aRectFnSet.GetRight(aStFrame));
+ Sub( aRegion, aSubRect );
+ SwTwips nTmpTwips = aRectFnSet.GetBottom(aStRect);
+ if( aRectFnSet.GetBottom(aStFrame) != nTmpTwips )
+ {
+ aSubRect = aStFrame;
+ aRectFnSet.SetTop( aSubRect, nTmpTwips );
+ Sub( aRegion, aSubRect );
+ }
+
+ //Now the frames between, if there are any
+ bool const bBody = pStartFrame->IsInDocBody();
+ const SwTableBox* pCellBox = pStartFrame->GetUpper()->IsCellFrame() ?
+ static_cast<const SwCellFrame*>(pStartFrame->GetUpper())->GetTabBox() : nullptr;
+ if (pSh->IsSelectAll())
+ pCellBox = nullptr;
+
+ const SwContentFrame *pContent = pStartFrame->GetNextContentFrame();
+ SwRect aPrvRect;
+
+ OSL_ENSURE( pContent,
+ "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect" );
+ while ( pContent && pContent != pEndFrame )
+ {
+ if ( pContent->IsInFly() )
+ {
+ const SwAnchoredObject* pObj = pContent->FindFlyFrame();
+ if (!aSortObjs.Contains(*pObj))
+ { // is this even possible, assuming valid cursor pos.?
+ aSortObjs.Insert( *const_cast<SwAnchoredObject*>(pObj) );
+ }
+ }
+
+ // Consider only frames which have the same IsInDocBody value like pStartFrame
+ // If pStartFrame is inside a SwCellFrame, consider only frames which are inside the
+ // same cell frame (or its follow cell)
+ const SwTableBox* pTmpCellBox = pContent->GetUpper()->IsCellFrame() ?
+ static_cast<const SwCellFrame*>(pContent->GetUpper())->GetTabBox() : nullptr;
+ if (pSh->IsSelectAll())
+ pTmpCellBox = nullptr;
+ if ( bBody == pContent->IsInDocBody() &&
+ ( !pCellBox || pCellBox == pTmpCellBox ) )
+ {
+ SwRect aCRect( pContent->UnionFrame( true ) );
+ aCRect.Intersection( pContent->GetPaintArea() );
+ if( aCRect.Overlaps( aRegion.GetOrigin() ))
+ {
+ SwRect aTmp( aPrvRect );
+ aTmp.Union( aCRect );
+ if ( (aPrvRect.Height() * aPrvRect.Width() +
+ aCRect.Height() * aCRect.Width()) ==
+ (aTmp.Height() * aTmp.Width()) )
+ {
+ aPrvRect.Union( aCRect );
+ }
+ else
+ {
+ if ( aPrvRect.HasArea() )
+ Sub( aRegion, aPrvRect );
+ aPrvRect = aCRect;
+ }
+ }
+ }
+ pContent = pContent->GetNextContentFrame();
+ OSL_ENSURE( pContent,
+ "<SwRootFrame::CalcFrameRects(..)> - no content frame. This is a serious defect!" );
+ }
+ if ( aPrvRect.HasArea() )
+ Sub( aRegion, aPrvRect );
+
+ //At least the endframe...
+ aRectFnSet.Refresh(pEndFrame);
+ nTmpTwips = aRectFnSet.GetTop(aEndRect);
+ if( aRectFnSet.GetTop(aEndFrame) != nTmpTwips )
+ {
+ aSubRect = aEndFrame;
+ aRectFnSet.SetBottom( aSubRect, nTmpTwips );
+ Sub( aRegion, aSubRect );
+ }
+ aSubRect = aEndRect;
+ if( bEndR2L )
+ aRectFnSet.SetRight(aSubRect, aRectFnSet.GetRight(aEndFrame));
+ else
+ aRectFnSet.SetLeft( aSubRect, aRectFnSet.GetLeft(aEndFrame) );
+ Sub( aRegion, aSubRect );
+ }
+
+ aRegion.Invert();
+ pSt2Pos.reset();
+ pEnd2Pos.reset();
+
+ // Cut out Flys during loop. We don't cut out Flys when:
+ // - the Lower is StartFrame/EndFrame (FlyInCnt and all other Flys which again
+ // sit in it)
+ // - if in the Z-order we have Flys above those in which the StartFrame is
+ // placed
+ // - if they are anchored to inside the selection and thus part of it
+ const SwPageFrame *pPage = pStartFrame->FindPageFrame();
+ const SwPageFrame *pEndPage = pEndFrame->FindPageFrame();
+
+ while ( pPage )
+ {
+ if ( pPage->GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
+ if ( !pFly )
+ continue;
+ const SwVirtFlyDrawObj* pObj = pFly->GetVirtDrawObj();
+ const SwFormatSurround &rSur = pFly->GetFormat()->GetSurround();
+ SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat().GetAnchor());
+ const SwPosition* anchoredAt = rAnchor.GetContentAnchor();
+ bool inSelection = (
+ anchoredAt != nullptr
+ && ( (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR
+ && IsDestroyFrameAnchoredAtChar(*anchoredAt, *pStartPos, *pEndPos))
+ || (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ && IsSelectFrameAnchoredAtPara(*anchoredAt, *pStartPos, *pEndPos))));
+ if( inSelection )
+ Add( aRegion, pFly->getFrameArea() );
+ else if ( !pFly->IsAnLower( pStartFrame ) &&
+ (rSur.GetSurround() != css::text::WrapTextMode_THROUGH &&
+ !rSur.IsContour()) )
+ {
+ if ( aSortObjs.Contains( *pAnchoredObj ) )
+ continue;
+
+ bool bSub = true;
+ const sal_uInt32 nPos = pObj->GetOrdNum();
+ for ( size_t k = 0; bSub && k < aSortObjs.size(); ++k )
+ {
+ assert( dynamic_cast< const SwFlyFrame *>( aSortObjs[k] ) &&
+ "<SwRootFrame::CalcFrameRects(..)> - object in <aSortObjs> of unexpected type" );
+ const SwFlyFrame* pTmp = static_cast<SwFlyFrame*>(aSortObjs[k]);
+ do
+ {
+ if ( nPos < pTmp->GetVirtDrawObj()->GetOrdNumDirect() )
+ {
+ bSub = false;
+ }
+ else
+ {
+ pTmp = pTmp->GetAnchorFrame()->FindFlyFrame();
+ }
+ } while ( bSub && pTmp );
+ }
+ if ( bSub )
+ Sub( aRegion, pFly->getFrameArea() );
+ }
+ }
+ }
+ if ( pPage == pEndPage )
+ break;
+ else
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ //Because it looks better, we close the DropCaps.
+ SwRect aDropRect;
+ if ( pStartFrame->IsTextFrame() )
+ {
+ if ( static_cast<const SwTextFrame*>(pStartFrame)->GetDropRect( aDropRect ) )
+ Sub( aRegion, aDropRect );
+ }
+ if ( pEndFrame != pStartFrame && pEndFrame->IsTextFrame() )
+ {
+ if ( static_cast<const SwTextFrame*>(pEndFrame)->GetDropRect( aDropRect ) )
+ Sub( aRegion, aDropRect );
+ }
+
+ rCursor.assign( aRegion.begin(), aRegion.end() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/unusedf.cxx b/sw/source/core/layout/unusedf.cxx
new file mode 100644
index 000000000..25de7617c
--- /dev/null
+++ b/sw/source/core/layout/unusedf.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <rootfrm.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <osl/diagnose.h>
+
+void SwFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs * )
+{
+ OSL_FAIL( "Format() of the base class called." );
+}
+
+void SwFrame::PaintSwFrame(vcl::RenderContext&, SwRect const&, SwPrintData const*const) const
+{
+ OSL_FAIL( "PaintSwFrame() of the base class called." );
+}
+
+bool SwContentFrame::WouldFit(SwTwips &, bool&, bool, bool)
+{
+ OSL_FAIL( "WouldFit of ContentFrame called." );
+ return false;
+}
+
+bool SwFrame::FillSelection( SwSelectionList& , const SwRect& ) const
+{
+ OSL_FAIL( "Don't call this function at the base class!" );
+ return false;
+}
+
+bool SwFrame::GetModelPositionForViewPoint( SwPosition *, Point&, SwCursorMoveState*, bool ) const
+{
+ OSL_FAIL( "GetModelPositionForViewPoint of the base class, hi!" );
+ return false;
+}
+
+#ifdef DBG_UTIL
+
+void SwRootFrame::Cut()
+{
+ OSL_FAIL( "Cut() of RootFrame called." );
+}
+
+void SwRootFrame::Paste( SwFrame *, SwFrame * )
+{
+ OSL_FAIL( "Paste() of RootFrame called." );
+}
+
+void SwFlyFrame::Paste( SwFrame *, SwFrame * )
+{
+ OSL_FAIL( "Paste() of FlyFrame called." );
+}
+
+#endif
+
+bool SwFrame::GetCharRect( SwRect&, const SwPosition&,
+ SwCursorMoveState*, bool ) const
+{
+ OSL_FAIL( "GetCharRect() of the base called." );
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/virtoutp.cxx b/sw/source/core/layout/virtoutp.cxx
new file mode 100644
index 000000000..dd044317d
--- /dev/null
+++ b/sw/source/core/layout/virtoutp.cxx
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "virtoutp.hxx"
+#include <viewopt.hxx>
+#include <rootfrm.hxx>
+#include <osl/diagnose.h>
+
+/* The SWLayVout class manages the virtual output devices.
+ * RootFrame has a static member of this class which is created in FrameInit
+ * and destroyed in FrameFinit.
+ * */
+
+bool SwRootFrame::FlushVout()
+{
+ if (SwRootFrame::s_pVout->IsFlushable())
+ {
+ SwRootFrame::s_pVout->Flush_();
+ return true;
+ }
+ return false;
+}
+
+bool SwRootFrame::HasSameRect( const SwRect& rRect )
+{
+ if (SwRootFrame::s_pVout->IsFlushable())
+ return ( rRect == SwRootFrame::s_pVout->GetOrgRect() );
+ return false;
+}
+
+/** method to set mapping/pixel offset for virtual output device
+
+ OD 12.11.2002 #96272# - method implements two solutions for the mapping of
+ the virtual output device:
+ The old solution set the origin of the mapping mode, which will be used in
+ the virtual output device. This causes several paint errors, because of the
+ different roundings in the virtual output device and the original output device.
+ The new solution avoids the rounding differences between virtual and original
+ output device by setting a pixel offset at the virtual output device.
+ A define controls, which solution is used, in order to switch in escalation
+ back to old solution.
+
+ @param _pOrgOutDev
+ input parameter - constant instance of the original output device, for which
+ the virtual output device is created.
+
+ @param _pVirDev
+ input/output parameter - instance of the virtual output device.
+
+ @param _rNewOrigin
+ input parameter - constant instance of the origin, which will be used in
+ the virtual output device
+*/
+// define to control, if old or new solution for setting the mapping for
+// a virtual output device is used.
+static void SetMappingForVirtDev( const Point& _rNewOrigin,
+ const vcl::RenderContext* _pOrgOutDev,
+ vcl::RenderContext* _pVirDev )
+{
+ // new solution: set pixel offset at virtual output device
+ Point aPixelOffset = _pOrgOutDev->LogicToPixel( _rNewOrigin );
+ _pVirDev->SetPixelOffset( Size( -aPixelOffset.X(), -aPixelOffset.Y() ) );
+}
+
+// rSize must be pixel coordinates!
+bool SwLayVout::DoesFit( const Size &rNew )
+{
+ if( rNew.Height() > VIRTUALHEIGHT )
+ return false;
+ if( rNew.IsEmpty() )
+ return false;
+ if( rNew.Width() <= m_aSize.Width() )
+ return true;
+ if( !m_pVirDev )
+ {
+ m_pVirDev = VclPtr<VirtualDevice>::Create();
+ m_pVirDev->SetLineColor();
+ if( m_pOut )
+ {
+ if( m_pVirDev->GetFillColor() != m_pOut->GetFillColor() )
+ m_pVirDev->SetFillColor( m_pOut->GetFillColor() );
+ }
+ }
+
+ if( rNew.Width() > m_aSize.Width() )
+ {
+ m_aSize.setWidth( rNew.Width() );
+ if( !m_pVirDev->SetOutputSizePixel( m_aSize ) )
+ {
+ m_pVirDev.disposeAndClear();
+ m_aSize.setWidth( 0 );
+ return false;
+ }
+ }
+ return true;
+}
+
+/// change 2nd parameter <rRect> - no longer <const>
+/// in order to return value of class member variable <aRect>, if virtual
+/// output is used.
+/// <aRect> contains the rectangle that represents the area the virtual
+/// output device is used for and that is flushed at the end.
+void SwLayVout::Enter( SwViewShell *pShell, SwRect &rRect, bool bOn )
+{
+ Flush();
+
+#ifdef DBG_UTIL
+ if( pShell->GetViewOptions()->IsTest3() )
+ {
+ ++m_nCount;
+ return;
+ }
+#endif
+
+ bOn = bOn && !m_nCount && rRect.HasArea() && pShell->GetWin();
+ ++m_nCount;
+ if( !bOn )
+ return;
+
+ m_pShell = pShell;
+ m_pOut = nullptr;
+ OutputDevice *pO = m_pShell->GetOut();
+// We don't cheat on printers or virtual output devices...
+ if( OUTDEV_WINDOW != pO->GetOutDevType() )
+ return;
+
+ m_pOut = pO;
+ Size aPixSz( m_pOut->PixelToLogic( Size( 1,1 )) );
+ SwRect aTmp( rRect );
+ aTmp.AddWidth(aPixSz.Width()/2 + 1 );
+ aTmp.AddHeight(aPixSz.Height()/2 + 1 );
+ tools::Rectangle aTmpRect( pO->LogicToPixel( aTmp.SVRect() ) );
+
+ OSL_ENSURE( !m_pShell->GetWin()->IsReallyVisible() ||
+ aTmpRect.GetWidth() <= m_pShell->GetWin()->GetOutputSizePixel().Width() + 2,
+ "Paintwidth bigger than visarea?" );
+ // Does the rectangle fit in our buffer?
+ if( !DoesFit( aTmpRect.GetSize() ) )
+ {
+ m_pOut = nullptr;
+ return;
+ }
+
+ m_aRect = SwRect( pO->PixelToLogic( aTmpRect ) );
+
+ SetOutDev( m_pShell, m_pVirDev );
+
+ if( m_pVirDev->GetFillColor() != m_pOut->GetFillColor() )
+ m_pVirDev->SetFillColor( m_pOut->GetFillColor() );
+
+ MapMode aMapMode( m_pOut->GetMapMode() );
+ // use method to set mapping
+ //aMapMode.SetOrigin( Point(0,0) - aRect.Pos() );
+ ::SetMappingForVirtDev( m_aRect.Pos(), m_pOut, m_pVirDev );
+
+ if( aMapMode != m_pVirDev->GetMapMode() )
+ m_pVirDev->SetMapMode( aMapMode );
+
+ // set value of parameter <rRect>
+ rRect = m_aRect;
+
+}
+
+void SwLayVout::Flush_()
+{
+ OSL_ENSURE( m_pVirDev, "SwLayVout::DrawOut: nothing left Toulouse" );
+ m_pOut->DrawOutDev( m_aRect.Pos(), m_aRect.SSize(),
+ m_aRect.Pos(), m_aRect.SSize(), *m_pVirDev );
+ SetOutDev( m_pShell, m_pOut );
+ m_pOut = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/virtoutp.hxx b/sw/source/core/layout/virtoutp.hxx
new file mode 100644
index 000000000..df24ec196
--- /dev/null
+++ b/sw/source/core/layout/virtoutp.hxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_SOURCE_CORE_LAYOUT_VIRTOUTP_HXX
+#define INCLUDED_SW_SOURCE_CORE_LAYOUT_VIRTOUTP_HXX
+
+#include <vcl/virdev.hxx>
+
+#include <swrect.hxx>
+
+class SwViewShell;
+#define VIRTUALHEIGHT 64
+
+class SwLayVout
+{
+ friend void FrameFinit(); //deletes Vout
+private:
+ SwViewShell* m_pShell;
+ VclPtr<OutputDevice> m_pOut;
+ VclPtr<VirtualDevice> m_pVirDev;
+ SwRect m_aRect;
+ SwRect m_aOrgRect;
+ Size m_aSize;
+ sal_uInt16 m_nCount;
+
+ bool DoesFit( const Size &rOut );
+
+public:
+ SwLayVout() : m_pShell(nullptr), m_pOut(nullptr), m_pVirDev(nullptr), m_aSize(0, VIRTUALHEIGHT), m_nCount(0) {}
+ ~SwLayVout() { m_pVirDev.disposeAndClear(); }
+
+ /// OD 27.09.2002 #103636# - change 2nd parameter <rRect> - no longer <const>
+ void Enter( SwViewShell *pShell, SwRect &rRect, bool bOn );
+ void Leave() { --m_nCount; Flush(); }
+
+ void SetOrgRect( SwRect const &rRect ) { m_aOrgRect = rRect; }
+ const SwRect& GetOrgRect() const { return m_aOrgRect; }
+
+ bool IsFlushable() const { return bool(m_pOut); }
+ void Flush_();
+ void Flush() { if( m_pOut ) Flush_(); }
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/wsfrm.cxx b/sw/source/core/layout/wsfrm.cxx
new file mode 100644
index 000000000..1eadc9db6
--- /dev/null
+++ b/sw/source/core/layout/wsfrm.cxx
@@ -0,0 +1,4743 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <hints.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/safeint.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/brushitem.hxx>
+#include <fmtornt.hxx>
+#include <pagefrm.hxx>
+#include <section.hxx>
+#include <rootfrm.hxx>
+#include <anchoreddrawobject.hxx>
+#include <fmtanchr.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <docsh.hxx>
+#include <ftninfo.hxx>
+#include <ftnidx.hxx>
+#include <fmtclbl.hxx>
+#include <fmtfsize.hxx>
+#include <fmtpdsc.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtcntnt.hxx>
+#include <ftnfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <flyfrm.hxx>
+#include <sectfrm.hxx>
+#include <fmtclds.hxx>
+#include <txtfrm.hxx>
+#include <bodyfrm.hxx>
+#include <cellfrm.hxx>
+#include <dbg_lay.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <sortedobjs.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <layact.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+
+// RotateFlyFrame3
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+using namespace ::com::sun::star;
+
+SwFrameAreaDefinition::SwFrameAreaDefinition()
+: mbFrameAreaPositionValid(false),
+ mbFrameAreaSizeValid(false),
+ mbFramePrintAreaValid(false),
+ mnFrameId(SwFrameAreaDefinition::snLastFrameId++)
+{
+}
+
+SwFrameAreaDefinition::~SwFrameAreaDefinition()
+{
+}
+
+void SwFrameAreaDefinition::setFrameAreaPositionValid(bool bNew)
+{
+ if(mbFrameAreaPositionValid != bNew)
+ {
+ mbFrameAreaPositionValid = bNew;
+ }
+}
+
+void SwFrameAreaDefinition::setFrameAreaSizeValid(bool bNew)
+{
+ if(mbFrameAreaSizeValid != bNew)
+ {
+ mbFrameAreaSizeValid = bNew;
+ }
+}
+
+void SwFrameAreaDefinition::setFramePrintAreaValid(bool bNew)
+{
+ if(mbFramePrintAreaValid != bNew)
+ {
+ mbFramePrintAreaValid = bNew;
+ }
+}
+
+SwFrameAreaDefinition::FrameAreaWriteAccess::~FrameAreaWriteAccess()
+{
+ if(mrTarget.maFrameArea != *this)
+ {
+ mrTarget.maFrameArea = *this;
+ }
+}
+
+SwFrameAreaDefinition::FramePrintAreaWriteAccess::~FramePrintAreaWriteAccess()
+{
+ if(mrTarget.maFramePrintArea != *this)
+ {
+ mrTarget.maFramePrintArea = *this;
+ }
+}
+
+// RotateFlyFrame3 - Support for Transformations
+basegfx::B2DHomMatrix SwFrameAreaDefinition::getFrameAreaTransformation() const
+{
+ // default implementation hands out FrameArea (outer frame)
+ const SwRect& rFrameArea(getFrameArea());
+
+ return basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFrameArea.Width(), rFrameArea.Height(),
+ rFrameArea.Left(), rFrameArea.Top());
+}
+
+basegfx::B2DHomMatrix SwFrameAreaDefinition::getFramePrintAreaTransformation() const
+{
+ // default implementation hands out FramePrintArea (outer frame)
+ // Take into account that FramePrintArea is relative to FrameArea
+ const SwRect& rFrameArea(getFrameArea());
+ const SwRect& rFramePrintArea(getFramePrintArea());
+
+ return basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFramePrintArea.Width(), rFramePrintArea.Height(),
+ rFramePrintArea.Left() + rFrameArea.Left(),
+ rFramePrintArea.Top() + rFrameArea.Top());
+}
+
+void SwFrameAreaDefinition::transform_translate(const Point& rOffset)
+{
+ // RotateFlyFrame3: default is to change the FrameArea, FramePrintArea needs no
+ // change since it is relative to FrameArea
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ if (aFrm.Pos().X() != FAR_AWAY)
+ {
+ aFrm.Pos().AdjustX(rOffset.X() );
+ }
+
+ if (aFrm.Pos().Y() != FAR_AWAY)
+ {
+ aFrm.Pos().AdjustY(rOffset.Y() );
+ }
+}
+
+SwRect TransformableSwFrame::getUntransformedFrameArea() const
+{
+ const basegfx::B2DHomMatrix& rSource(getLocalFrameAreaTransformation());
+
+ if(rSource.isIdentity())
+ {
+ return mrSwFrameAreaDefinition.getFrameArea();
+ }
+ else
+ {
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSource.decompose(aScale, aTranslate, fRotate, fShearX);
+ const basegfx::B2DPoint aCenter(rSource * basegfx::B2DPoint(0.5, 0.5));
+ const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
+
+ return SwRect(
+ basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())),
+ basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())),
+ basegfx::fround(aAbsScale.getX()),
+ basegfx::fround(aAbsScale.getY()));
+ }
+}
+
+SwRect TransformableSwFrame::getUntransformedFramePrintArea() const
+{
+ const basegfx::B2DHomMatrix& rSource(getLocalFramePrintAreaTransformation());
+
+ if(rSource.isIdentity())
+ {
+ return mrSwFrameAreaDefinition.getFramePrintArea();
+ }
+ else
+ {
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSource.decompose(aScale, aTranslate, fRotate, fShearX);
+ const basegfx::B2DPoint aCenter(rSource * basegfx::B2DPoint(0.5, 0.5));
+ const basegfx::B2DVector aAbsScale(basegfx::absolute(aScale));
+ const SwRect aUntransformedFrameArea(getUntransformedFrameArea());
+
+ return SwRect(
+ basegfx::fround(aCenter.getX() - (0.5 * aAbsScale.getX())) - aUntransformedFrameArea.Left(),
+ basegfx::fround(aCenter.getY() - (0.5 * aAbsScale.getY())) - aUntransformedFrameArea.Top(),
+ basegfx::fround(aAbsScale.getX()),
+ basegfx::fround(aAbsScale.getY()));
+ }
+}
+
+void TransformableSwFrame::createFrameAreaTransformations(
+ double fRotation,
+ const basegfx::B2DPoint& rCenter)
+{
+ const basegfx::B2DHomMatrix aRotateAroundCenter(
+ basegfx::utils::createRotateAroundPoint(
+ rCenter.getX(),
+ rCenter.getY(),
+ fRotation));
+ const SwRect& rFrameArea(mrSwFrameAreaDefinition.getFrameArea());
+ const SwRect& rFramePrintArea(mrSwFrameAreaDefinition.getFramePrintArea());
+
+ maFrameAreaTransformation = aRotateAroundCenter * basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFrameArea.Width(), rFrameArea.Height(),
+ rFrameArea.Left(), rFrameArea.Top());
+ maFramePrintAreaTransformation = aRotateAroundCenter * basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rFramePrintArea.Width(), rFramePrintArea.Height(),
+ rFramePrintArea.Left() + rFrameArea.Left(), rFramePrintArea.Top() + rFrameArea.Top());
+}
+
+void TransformableSwFrame::adaptFrameAreasToTransformations()
+{
+ if(!getLocalFrameAreaTransformation().isIdentity())
+ {
+ basegfx::B2DRange aRangeFrameArea(0.0, 0.0, 1.0, 1.0);
+ aRangeFrameArea.transform(getLocalFrameAreaTransformation());
+ const SwRect aNewFrm(
+ basegfx::fround(aRangeFrameArea.getMinX()), basegfx::fround(aRangeFrameArea.getMinY()),
+ basegfx::fround(aRangeFrameArea.getWidth()), basegfx::fround(aRangeFrameArea.getHeight()));
+
+ if(aNewFrm != mrSwFrameAreaDefinition.getFrameArea())
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(mrSwFrameAreaDefinition);
+ aFrm.setSwRect(aNewFrm);
+ }
+ }
+
+ if(getLocalFramePrintAreaTransformation().isIdentity())
+ return;
+
+ basegfx::B2DRange aRangeFramePrintArea(0.0, 0.0, 1.0, 1.0);
+ aRangeFramePrintArea.transform(getLocalFramePrintAreaTransformation());
+ const SwRect aNewPrt(
+ basegfx::fround(aRangeFramePrintArea.getMinX()) - mrSwFrameAreaDefinition.getFrameArea().Left(),
+ basegfx::fround(aRangeFramePrintArea.getMinY()) - mrSwFrameAreaDefinition.getFrameArea().Top(),
+ basegfx::fround(aRangeFramePrintArea.getWidth()),
+ basegfx::fround(aRangeFramePrintArea.getHeight()));
+
+ if(aNewPrt != mrSwFrameAreaDefinition.getFramePrintArea())
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(mrSwFrameAreaDefinition);
+ aPrt.setSwRect(aNewPrt);
+ }
+}
+
+void TransformableSwFrame::restoreFrameAreas()
+{
+ // This can be done fully based on the Transformations currently
+ // set, so use this. Only needed when transformation *is* used
+ if(!getLocalFrameAreaTransformation().isIdentity())
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(mrSwFrameAreaDefinition);
+ aFrm.setSwRect(getUntransformedFrameArea());
+ }
+
+ if(!getLocalFramePrintAreaTransformation().isIdentity())
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(mrSwFrameAreaDefinition);
+ aPrt.setSwRect(getUntransformedFramePrintArea());
+ }
+}
+
+// transform by given B2DHomMatrix
+void TransformableSwFrame::transform(const basegfx::B2DHomMatrix& aTransform)
+{
+ maFrameAreaTransformation *= aTransform;
+ maFramePrintAreaTransformation *= aTransform;
+}
+
+SwFrame::SwFrame( sw::BroadcastingModify *pMod, SwFrame* pSib )
+: SwClient( pMod ),
+ mpRoot( pSib ? pSib->getRootFrame() : nullptr ),
+ mpUpper(nullptr),
+ mpNext(nullptr),
+ mpPrev(nullptr),
+ mnFrameType(SwFrameType::None),
+ mbInDtor(false),
+ mbInvalidR2L(true),
+ mbDerivedR2L(false),
+ mbRightToLeft(false),
+ mbInvalidVert(true),
+ mbDerivedVert(false),
+ mbVertical(false),
+ mbVertLR(false),
+ mbVertLRBT(false),
+ mbValidLineNum(false),
+ mbFixSize(false),
+ mbCompletePaint(true),
+ mbRetouche(false),
+ mbInfInvalid(true),
+ mbInfBody( false ),
+ mbInfTab ( false ),
+ mbInfFly ( false ),
+ mbInfFootnote ( false ),
+ mbInfSct ( false ),
+ mbColLocked(false),
+ m_isInDestroy(false),
+ mnForbidDelete(0)
+{
+ OSL_ENSURE( pMod, "No frame format given." );
+}
+
+const IDocumentDrawModelAccess& SwFrame::getIDocumentDrawModelAccess()
+{
+ return GetUpper()->GetFormat()->getIDocumentDrawModelAccess();
+}
+
+bool SwFrame::KnowsFormat( const SwFormat& rFormat ) const
+{
+ return GetRegisteredIn() == &rFormat;
+}
+
+void SwFrame::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+void SwFrame::CheckDir( SvxFrameDirection nDir, bool bVert, bool bOnlyBiDi, bool bBrowse )
+{
+ if( SvxFrameDirection::Environment == nDir || ( bVert && bOnlyBiDi ) )
+ {
+ mbDerivedVert = true;
+ if( SvxFrameDirection::Environment == nDir )
+ mbDerivedR2L = true;
+ SetDirFlags( bVert );
+ }
+ else if( bVert )
+ {
+ mbInvalidVert = false;
+ if( SvxFrameDirection::Horizontal_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir
+ || bBrowse )
+ {
+ mbVertical = false;
+ mbVertLR = false;
+ mbVertLRBT = false;
+ }
+ else
+ {
+ mbVertical = true;
+ if(SvxFrameDirection::Vertical_RL_TB == nDir)
+ {
+ mbVertLR = false;
+ mbVertLRBT = false;
+ }
+ else if(SvxFrameDirection::Vertical_LR_TB==nDir)
+ {
+ mbVertLR = true;
+ mbVertLRBT = false;
+ }
+ else if (nDir == SvxFrameDirection::Vertical_LR_BT)
+ {
+ mbVertLR = true;
+ mbVertLRBT = true;
+ }
+ }
+ }
+ else
+ {
+ mbInvalidR2L = false;
+ if( SvxFrameDirection::Horizontal_RL_TB == nDir )
+ mbRightToLeft = true;
+ else
+ mbRightToLeft = false;
+ }
+}
+
+void SwFrame::CheckDirection( bool bVert )
+{
+ if( bVert )
+ {
+ if( !IsHeaderFrame() && !IsFooterFrame() )
+ {
+ mbDerivedVert = true;
+ SetDirFlags( bVert );
+ }
+ }
+ else
+ {
+ mbDerivedR2L = true;
+ SetDirFlags( bVert );
+ }
+}
+
+void SwSectionFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ if( pFormat )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
+ bVert, true, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwFlyFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ if( pFormat )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
+ bVert, false, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwTabFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ if( pFormat )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(pFormat->GetFormatAttr(RES_FRAMEDIR).GetValue(),
+ bVert, true, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwCellFrame::CheckDirection( bool bVert )
+{
+ const SwFrameFormat* pFormat = GetFormat();
+ const SvxFrameDirectionItem* pFrameDirItem;
+ // Check if the item is set, before actually
+ // using it. Otherwise the dynamic pool default is used, which may be set
+ // to LTR in case of OOo 1.0 documents.
+ if( pFormat && (pFrameDirItem = pFormat->GetItemIfSet( RES_FRAMEDIR ) ) )
+ {
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir( pFrameDirItem->GetValue(), bVert, false, bBrowseMode );
+ }
+ else
+ SwFrame::CheckDirection( bVert );
+}
+
+void SwTextFrame::CheckDirection( bool bVert )
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ CheckDir(GetTextNodeForParaProps()->GetSwAttrSet().GetFrameDir().GetValue(),
+ bVert, true, bBrowseMode);
+}
+
+void SwFrame::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwFrameInvFlags eInvFlags = SwFrameInvFlags::NONE;
+
+ if(pLegacy->m_pOld && pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which())
+ {
+ SfxItemIter aNIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet());
+ SfxItemIter aOIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ do
+ {
+ UpdateAttrFrame(pOItem, pNItem, eInvFlags);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while (pNItem);
+ }
+ else
+ UpdateAttrFrame(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwFrameInvFlags::NONE)
+ return;
+
+ SwPageFrame* pPage = FindPageFrame();
+ InvalidatePage(pPage);
+ if(eInvFlags & SwFrameInvFlags::InvalidatePrt)
+ {
+ InvalidatePrt_();
+ if(!GetPrev() && IsTabFrame() && IsInSct())
+ FindSctFrame()->InvalidatePrt_();
+ }
+ if(eInvFlags & SwFrameInvFlags::InvalidateSize)
+ InvalidateSize_();
+ if(eInvFlags & SwFrameInvFlags::InvalidatePos)
+ InvalidatePos_();
+ if(eInvFlags & SwFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ SwFrame *pNxt;
+ if (eInvFlags & (SwFrameInvFlags::NextInvalidatePos | SwFrameInvFlags::NextSetCompletePaint)
+ && nullptr != (pNxt = GetNext()))
+ {
+ pNxt->InvalidatePage(pPage);
+ if(eInvFlags & SwFrameInvFlags::NextInvalidatePos)
+ pNxt->InvalidatePos_();
+ if(eInvFlags & SwFrameInvFlags::NextSetCompletePaint)
+ pNxt->SetCompletePaint();
+ }
+}
+
+void SwFrame::UpdateAttrFrame( const SfxPoolItem *pOld, const SfxPoolItem *pNew,
+ SwFrameInvFlags &rInvFlags )
+{
+ sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch( nWhich )
+ {
+ case RES_BOX:
+ case RES_SHADOW:
+ Prepare( PrepareHint::FixSizeChanged );
+ [[fallthrough]];
+ case RES_LR_SPACE:
+ case RES_UL_SPACE:
+ case RES_RTL_GUTTER:
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
+ | SwFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize;
+ break;
+
+ case RES_BACKGROUND:
+ case RES_BACKGROUND_FULL_SIZE:
+ rInvFlags |= SwFrameInvFlags::SetCompletePaint | SwFrameInvFlags::NextSetCompletePaint;
+ break;
+
+ case RES_KEEP:
+ rInvFlags |= SwFrameInvFlags::InvalidatePos;
+ break;
+
+ case RES_FRM_SIZE:
+ ReinitializeFrameSizeAttrFlags();
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
+ | SwFrameInvFlags::NextInvalidatePos;
+ break;
+
+ case RES_FMT_CHG:
+ rInvFlags |= SwFrameInvFlags::InvalidatePrt | SwFrameInvFlags::InvalidateSize
+ | SwFrameInvFlags::InvalidatePos | SwFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_ROW_SPLIT:
+ {
+ if ( IsRowFrame() )
+ {
+ bool bInFollowFlowRow = nullptr != IsInFollowFlowRow();
+ if ( bInFollowFlowRow || nullptr != IsInSplitTableRow() )
+ {
+ SwTabFrame* pTab = FindTabFrame();
+ if ( bInFollowFlowRow )
+ pTab = pTab->FindMaster();
+ pTab->SetRemoveFollowFlowLinePending( true );
+ }
+ }
+ break;
+ }
+ case RES_COL:
+ OSL_FAIL( "Columns for new FrameType?" );
+ break;
+
+ default:
+ // the new FillStyle has to do the same as previous RES_BACKGROUND
+ if(nWhich >= XATTR_FILL_FIRST && nWhich <= XATTR_FILL_LAST)
+ {
+ rInvFlags
+ |= SwFrameInvFlags::SetCompletePaint | SwFrameInvFlags::NextSetCompletePaint;
+ }
+ /* do Nothing */;
+ }
+}
+
+bool SwFrame::Prepare( const PrepareHint, const void *, bool )
+{
+ /* Do nothing */
+ return false;
+}
+
+/**
+ * Invalidates the page in which the Frame is currently placed.
+ * The page is invalidated depending on the type (Layout, Content, FlyFrame)
+ */
+void SwFrame::InvalidatePage( const SwPageFrame *pPage ) const
+{
+ if ( !pPage )
+ {
+ pPage = FindPageFrame();
+ // #i28701# - for at-character and as-character
+ // anchored Writer fly frames additionally invalidate also page frame
+ // its 'anchor character' is on.
+ if ( pPage && pPage->GetUpper() && IsFlyFrame() )
+ {
+ const SwFlyFrame* pFlyFrame = static_cast<const SwFlyFrame*>(this);
+ if ( pFlyFrame->IsAutoPos() || pFlyFrame->IsFlyInContentFrame() )
+ {
+ // #i33751#, #i34060# - method <GetPageFrameOfAnchor()>
+ // is replaced by method <FindPageFrameOfAnchor()>. It's return value
+ // have to be checked.
+ SwPageFrame* pPageFrameOfAnchor =
+ const_cast<SwFlyFrame*>(pFlyFrame)->FindPageFrameOfAnchor();
+ if ( pPageFrameOfAnchor && pPageFrameOfAnchor != pPage )
+ {
+ InvalidatePage( pPageFrameOfAnchor );
+ }
+ }
+ }
+ }
+
+ if ( !(pPage && pPage->GetUpper()) )
+ return;
+
+ if ( pPage->GetFormat()->GetDoc()->IsInDtor() )
+ return;
+
+ SwRootFrame *pRoot = const_cast<SwRootFrame*>(static_cast<const SwRootFrame*>(pPage->GetUpper()));
+ const SwFlyFrame *pFly = FindFlyFrame();
+ if ( IsContentFrame() )
+ {
+ if ( pRoot->IsTurboAllowed() )
+ {
+ // If a ContentFrame wants to register for a second time, make it a TurboAction.
+ if ( !pRoot->GetTurbo() || this == pRoot->GetTurbo() )
+ pRoot->SetTurbo( static_cast<const SwContentFrame*>(this) );
+ else
+ {
+ pRoot->DisallowTurbo();
+ //The page of the Turbo could be a different one then mine,
+ //therefore we have to invalidate it.
+ const SwFrame *pTmp = pRoot->GetTurbo();
+ pRoot->ResetTurbo();
+ pTmp->InvalidatePage();
+ }
+ }
+ if ( !pRoot->GetTurbo() )
+ {
+ if ( pFly )
+ { if( !pFly->IsLocked() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ { pPage->InvalidateFlyInCnt();
+ pFly->GetAnchorFrame()->InvalidatePage();
+ }
+ else
+ pPage->InvalidateFlyContent();
+ }
+ }
+ else
+ pPage->InvalidateContent();
+ }
+ }
+ else
+ {
+ pRoot->DisallowTurbo();
+ if ( pFly )
+ {
+ if ( !pFly->IsLocked() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ pPage->InvalidateFlyInCnt();
+ pFly->GetAnchorFrame()->InvalidatePage();
+ }
+ else
+ pPage->InvalidateFlyLayout();
+ }
+ }
+ else
+ pPage->InvalidateLayout();
+
+ if ( pRoot->GetTurbo() )
+ { const SwFrame *pTmp = pRoot->GetTurbo();
+ pRoot->ResetTurbo();
+ pTmp->InvalidatePage();
+ }
+ }
+ pRoot->SetIdleFlags();
+
+ if (!IsTextFrame())
+ return;
+
+ SwTextFrame const*const pText(static_cast<SwTextFrame const*>(this));
+ if (sw::MergedPara const*const pMergedPara = pText->GetMergedPara())
+ {
+ SwTextNode const* pNode(nullptr);
+ for (auto const& e : pMergedPara->extents)
+ {
+ if (e.pNode != pNode)
+ {
+ pNode = e.pNode;
+ if (pNode->IsGrammarCheckDirty())
+ {
+ pRoot->SetNeedGrammarCheck( true );
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (pText->GetTextNodeFirst()->IsGrammarCheckDirty())
+ {
+ pRoot->SetNeedGrammarCheck( true );
+ }
+ }
+}
+
+Size SwFrame::ChgSize( const Size& aNewSize )
+{
+ mbFixSize = true;
+ const Size aOldSize( getFrameArea().SSize() );
+ if ( aNewSize == aOldSize )
+ return aOldSize;
+
+ if ( GetUpper() )
+ {
+ bool bNeighb = IsNeighbourFrame();
+ SwRectFn fnRect = IsVertical() == bNeighb ? fnRectHori : ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
+ SwRect aNew( Point(0,0), aNewSize );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetWidth)( (aNew.*fnRect->fnGetWidth)() );
+ }
+
+ tools::Long nNew = (aNew.*fnRect->fnGetHeight)();
+ tools::Long nDiff = nNew - (getFrameArea().*fnRect->fnGetHeight)();
+
+ if( nDiff )
+ {
+ if ( GetUpper()->IsFootnoteBossFrame() && HasFixSize() &&
+ SwNeighbourAdjust::GrowShrink !=
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment() )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetHeight)( nNew );
+ }
+
+ SwTwips nReal = static_cast<SwLayoutFrame*>(this)->AdjustNeighbourhood(nDiff);
+
+ if ( nReal != nDiff )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetHeight)( nNew - nDiff + nReal );
+ }
+ }
+ else
+ {
+ // OD 24.10.2002 #97265# - grow/shrink not for neighbour frames
+ // NOTE: neighbour frames are cell and column frames.
+ if ( !bNeighb )
+ {
+ if ( nDiff > 0 )
+ Grow( nDiff );
+ else
+ Shrink( -nDiff );
+
+ if ( GetUpper() && (getFrameArea().*fnRect->fnGetHeight)() != nNew )
+ {
+ GetUpper()->InvalidateSize_();
+ }
+ }
+
+ // Even if grow/shrink did not yet set the desired width, for
+ // example when called by ChgColumns to set the column width, we
+ // set the right width now.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ (aFrm.*fnRect->fnSetHeight)( nNew );
+ }
+ }
+ }
+ else
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.SSize( aNewSize );
+ }
+
+ if ( getFrameArea().SSize() != aOldSize )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ GetNext()->InvalidatePage( pPage );
+ }
+ if( IsLayoutFrame() )
+ {
+ if( IsRightToLeft() )
+ InvalidatePos_();
+ if( static_cast<SwLayoutFrame*>(this)->Lower() )
+ static_cast<SwLayoutFrame*>(this)->Lower()->InvalidateSize_();
+ }
+ InvalidatePrt_();
+ InvalidateSize_();
+ InvalidatePage( pPage );
+ }
+
+ return getFrameArea().SSize();
+}
+
+/** Insert SwFrame into existing structure.
+ *
+ * Insertion is done below the parent either before pBehind or
+ * at the end of the chain if pBehind is empty.
+ */
+void SwFrame::InsertBefore( SwLayoutFrame* pParent, SwFrame* pBehind )
+{
+ OSL_ENSURE( pParent, "No parent for insert." );
+ OSL_ENSURE( (!pBehind || pParent == pBehind->GetUpper()),
+ "Frame tree is inconsistent." );
+
+ mpUpper = pParent;
+ mpNext = pBehind;
+ if( pBehind )
+ { //Insert before pBehind.
+ mpPrev = pBehind->mpPrev;
+ if( nullptr != mpPrev )
+ mpPrev->mpNext = this;
+ else
+ mpUpper->m_pLower = this;
+ pBehind->mpPrev = this;
+ }
+ else
+ { //Insert at the end, or as first node in the sub tree
+ mpPrev = mpUpper->Lower();
+ if ( mpPrev )
+ {
+ while( mpPrev->mpNext )
+ mpPrev = mpPrev->mpNext;
+ mpPrev->mpNext = this;
+ }
+ else
+ mpUpper->m_pLower = this;
+ }
+}
+
+/** Insert SwFrame into existing structure.
+ *
+ * Insertion is done below the parent either after pBehind or
+ * at the beginning of the chain if pBehind is empty.
+ */
+void SwFrame::InsertBehind( SwLayoutFrame *pParent, SwFrame *pBefore )
+{
+ OSL_ENSURE( pParent, "No Parent for Insert." );
+ OSL_ENSURE( (!pBefore || pParent == pBefore->GetUpper()),
+ "Frame tree is inconsistent." );
+
+ mpUpper = pParent;
+ mpPrev = pBefore;
+ if ( pBefore )
+ {
+ //Insert after pBefore
+ mpNext = pBefore->mpNext;
+ if ( nullptr != mpNext )
+ mpNext->mpPrev = this;
+ pBefore->mpNext = this;
+ }
+ else
+ {
+ //Insert at the beginning of the chain
+ mpNext = pParent->Lower();
+ if ( pParent->Lower() )
+ pParent->Lower()->mpPrev = this;
+ pParent->m_pLower = this;
+ }
+}
+
+/** Insert a chain of SwFrames into an existing structure
+ *
+ * Currently, this method is used to insert a SectionFrame (which may have some siblings) into an
+ * existing structure. If the third parameter is NULL, this method is (besides handling the
+ * siblings) equal to SwFrame::InsertBefore(..).
+ *
+ * If the third parameter is passed, the following happens:
+ * - this becomes mpNext of pParent
+ * - pSct becomes mpNext of the last one in the this-chain
+ * - pBehind is reconnected from pParent to pSct
+ * The purpose is: a SectionFrame (this) won't become a child of another SectionFrame (pParent), but
+ * pParent gets split into two siblings (pParent+pSect) and this is inserted between.
+ */
+bool SwFrame::InsertGroupBefore( SwFrame* pParent, SwFrame* pBehind, SwFrame* pSct )
+{
+ OSL_ENSURE( pParent, "No parent for insert." );
+ OSL_ENSURE( (!pBehind || ( (pBehind && (pParent == pBehind->GetUpper()))
+ || ((pParent->IsSctFrame() && pBehind->GetUpper()->IsColBodyFrame())) ) ),
+ "Frame tree inconsistent." );
+ if( pSct )
+ {
+ mpUpper = pParent->GetUpper();
+ SwFrame *pLast = this;
+ while( pLast->GetNext() )
+ {
+ pLast = pLast->GetNext();
+ pLast->mpUpper = GetUpper();
+ }
+ if( pBehind )
+ {
+ pLast->mpNext = pSct;
+ pSct->mpPrev = pLast;
+ pSct->mpNext = pParent->GetNext();
+ }
+ else
+ {
+ pLast->mpNext = pParent->GetNext();
+ if( pLast->GetNext() )
+ pLast->GetNext()->mpPrev = pLast;
+ }
+ pParent->mpNext = this;
+ mpPrev = pParent;
+ if( pSct->GetNext() )
+ pSct->GetNext()->mpPrev = pSct;
+ while( pLast->GetNext() )
+ {
+ pLast = pLast->GetNext();
+ pLast->mpUpper = GetUpper();
+ }
+ if( pBehind )
+ { // Insert before pBehind.
+ if( pBehind->GetPrev() )
+ pBehind->GetPrev()->mpNext = nullptr;
+ else
+ pBehind->GetUpper()->m_pLower = nullptr;
+ pBehind->mpPrev = nullptr;
+ SwLayoutFrame* pTmp = static_cast<SwLayoutFrame*>(pSct);
+ if( pTmp->Lower() )
+ {
+ OSL_ENSURE( pTmp->Lower()->IsColumnFrame(), "InsertGrp: Used SectionFrame" );
+ pTmp = static_cast<SwLayoutFrame*>(static_cast<SwLayoutFrame*>(pTmp->Lower())->Lower());
+ OSL_ENSURE( pTmp, "InsertGrp: Missing ColBody" );
+ }
+ pBehind->mpUpper = pTmp;
+ pBehind->GetUpper()->m_pLower = pBehind;
+ pLast = pBehind->GetNext();
+ while ( pLast )
+ {
+ pLast->mpUpper = pBehind->GetUpper();
+ pLast = pLast->GetNext();
+ }
+ }
+ else
+ {
+ OSL_ENSURE( pSct->IsSctFrame(), "InsertGroup: For SectionFrames only" );
+ SwFrame::DestroyFrame(pSct);
+ return false;
+ }
+ }
+ else
+ {
+ mpUpper = static_cast<SwLayoutFrame*>(pParent);
+ SwFrame *pLast = this;
+ while( pLast->GetNext() )
+ {
+ pLast = pLast->GetNext();
+ pLast->mpUpper = GetUpper();
+ }
+ pLast->mpNext = pBehind;
+ if( pBehind )
+ { // Insert before pBehind.
+ mpPrev = pBehind->mpPrev;
+ if( nullptr != mpPrev )
+ mpPrev->mpNext = this;
+ else
+ mpUpper->m_pLower = this;
+ pBehind->mpPrev = pLast;
+ }
+ else
+ {
+ //Insert at the end, or ... the first node in the subtree
+ mpPrev = mpUpper->Lower();
+ if ( mpPrev )
+ {
+ while( mpPrev->mpNext )
+ mpPrev = mpPrev->mpNext;
+ mpPrev->mpNext = this;
+ }
+ else
+ mpUpper->m_pLower = this;
+ }
+ }
+ return true;
+}
+
+void SwFrame::RemoveFromLayout()
+{
+ OSL_ENSURE( mpUpper, "Remove without upper?" );
+
+ if (mpPrev)
+ // one out of the middle is removed
+ mpPrev->mpNext = mpNext;
+ else if (mpUpper)
+ { // the first in a list is removed //TODO
+ OSL_ENSURE( mpUpper->m_pLower == this, "Layout is inconsistent." );
+ mpUpper->m_pLower = mpNext;
+ }
+ if( mpNext )
+ mpNext->mpPrev = mpPrev;
+
+ // Remove link
+ mpNext = mpPrev = nullptr;
+ mpUpper = nullptr;
+}
+
+void SwContentFrame::Paste( SwFrame* pParent, SwFrame* pSibling)
+{
+ OSL_ENSURE( pParent, "No parent for pasting." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm the parent." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I'm still registered somewhere" );
+ OSL_ENSURE( !pSibling || pSibling->IsFlowFrame(),
+ "<SwContentFrame::Paste(..)> - sibling not of expected type." );
+
+ //Insert in the tree.
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidateAll_();
+ InvalidatePage( pPage );
+
+ if( pPage )
+ {
+ pPage->InvalidateSpelling();
+ pPage->InvalidateSmartTags();
+ pPage->InvalidateAutoCompleteWords();
+ pPage->InvalidateWordCount();
+ }
+
+ if ( GetNext() )
+ {
+ SwFrame* pNxt = GetNext();
+ pNxt->InvalidatePrt_();
+ pNxt->InvalidatePos_();
+ pNxt->InvalidatePage( pPage );
+ if( pNxt->IsSctFrame() )
+ pNxt = static_cast<SwSectionFrame*>(pNxt)->ContainsContent();
+ if( pNxt && pNxt->IsTextFrame() && pNxt->IsInFootnote() )
+ pNxt->Prepare( PrepareHint::FootnoteInvalidation, nullptr, false );
+ }
+
+ if ( getFrameArea().Height() )
+ pParent->Grow( getFrameArea().Height() );
+
+ if ( getFrameArea().Width() != pParent->getFramePrintArea().Width() )
+ Prepare( PrepareHint::FixSizeChanged );
+
+ if ( GetPrev() )
+ {
+ if ( IsFollow() )
+ //I'm a direct follower of my master now
+ static_cast<SwContentFrame*>(GetPrev())->Prepare( PrepareHint::FollowFollows );
+ else
+ {
+ if ( GetPrev()->getFrameArea().Height() !=
+ GetPrev()->getFramePrintArea().Height() + GetPrev()->getFramePrintArea().Top() )
+ {
+ // Take the border into account?
+ GetPrev()->InvalidatePrt_();
+ }
+ // OD 18.02.2003 #104989# - force complete paint of previous frame,
+ // if frame is inserted at the end of a section frame, in order to
+ // get subsidiary lines repainted for the section.
+ if ( pParent->IsSctFrame() && !GetNext() )
+ {
+ // force complete paint of previous frame, if new inserted frame
+ // in the section is the last one.
+ GetPrev()->SetCompletePaint();
+ }
+ GetPrev()->InvalidatePage( pPage );
+ }
+ }
+ if ( IsInFootnote() )
+ {
+ SwFrame* pFrame = GetIndPrev();
+ if( pFrame && pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ if( !GetNext() )
+ {
+ pFrame = FindFootnoteFrame()->GetNext();
+ if( pFrame && nullptr != (pFrame=static_cast<SwLayoutFrame*>(pFrame)->ContainsAny()) )
+ pFrame->InvalidatePrt_();
+ }
+ }
+
+ InvalidateLineNum_();
+ SwFrame *pNxt = FindNextCnt();
+ if ( !pNxt )
+ return;
+
+ while ( pNxt && pNxt->IsInTab() )
+ {
+ pNxt = pNxt->FindTabFrame();
+ if( nullptr != pNxt )
+ pNxt = pNxt->FindNextCnt();
+ }
+ if ( pNxt )
+ {
+ pNxt->InvalidateLineNum_();
+ if ( pNxt != GetNext() )
+ pNxt->InvalidatePage();
+ }
+}
+
+void SwContentFrame::Cut()
+{
+ OSL_ENSURE( GetUpper(), "Cut without Upper()." );
+
+ SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ SwFrame *pFrame = GetIndPrev();
+ if( pFrame )
+ {
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if ( pFrame && pFrame->IsContentFrame() )
+ {
+ pFrame->InvalidatePrt_();
+ if( IsInFootnote() )
+ pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ }
+ // #i26250# - invalidate printing area of previous
+ // table frame.
+ else if ( pFrame && pFrame->IsTabFrame() )
+ {
+ pFrame->InvalidatePrt();
+ }
+ }
+
+ SwFrame *pNxt = FindNextCnt();
+ if ( pNxt )
+ {
+ while ( pNxt && pNxt->IsInTab() )
+ {
+ pNxt = pNxt->FindTabFrame();
+ if( nullptr != pNxt )
+ pNxt = pNxt->FindNextCnt();
+ }
+ if ( pNxt )
+ {
+ pNxt->InvalidateLineNum_();
+ if ( pNxt != GetNext() )
+ pNxt->InvalidatePage();
+ }
+ }
+
+ SwTabFrame* pMasterTab(nullptr);
+ pFrame = GetIndNext();
+ if( pFrame )
+ {
+ // The old follow may have calculated a gap to the predecessor which
+ // now becomes obsolete or different as it becomes the first one itself
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ pFrame->InvalidatePage( pPage );
+ if( pFrame->IsSctFrame() )
+ {
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ {
+ pFrame->InvalidatePrt_();
+ pFrame->InvalidatePos_();
+ pFrame->InvalidatePage( pPage );
+ }
+ }
+ if( pFrame && IsInFootnote() )
+ pFrame->Prepare( PrepareHint::ErgoSum, nullptr, false );
+ if( IsInSct() && !GetPrev() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ }
+ else
+ {
+ InvalidateNextPos();
+ //Someone needs to do the retouching: predecessor or upper
+ pFrame = GetPrev();
+ if ( nullptr != pFrame )
+ { pFrame->SetRetouche();
+ pFrame->Prepare( PrepareHint::WidowsOrphans );
+ pFrame->InvalidatePos_();
+ pFrame->InvalidatePage( pPage );
+ }
+ // If I'm (was) the only ContentFrame in my upper, it has to do the
+ // retouching. Also, perhaps a page became empty.
+ else
+ { SwRootFrame *pRoot = getRootFrame();
+ if ( pRoot )
+ {
+ pRoot->SetSuperfluous();
+ // RemoveSuperfluous can only remove empty pages at the end;
+ // find if there are pages without content following pPage
+ // and if so request a call to CheckPageDescs()
+ SwPageFrame const* pNext(pPage);
+ SwViewShell *pSh = pRoot->GetCurrShell();
+ if (pSh && pSh->Imp()->IsAction())
+ {
+ while ((pNext = static_cast<SwPageFrame const*>(pNext->GetNext())))
+ {
+ if (!sw::IsPageFrameEmpty(*pNext) && !pNext->IsFootnotePage())
+ {
+ pSh->Imp()->GetLayAction().SetCheckPageNum(pPage->GetPhyPageNum());
+ break;
+ }
+ }
+ }
+ GetUpper()->SetCompletePaint();
+ GetUpper()->InvalidatePage( pPage );
+ }
+ if( IsInSct() )
+ {
+ SwSectionFrame* pSct = FindSctFrame();
+ if( !pSct->IsFollow() )
+ {
+ pSct->InvalidatePrt_();
+ pSct->InvalidatePage( pPage );
+ }
+ }
+ // #i52253# The master table should take care
+ // of removing the follow flow line.
+ if ( IsInTab() )
+ {
+ SwTabFrame* pThisTab = FindTabFrame();
+ if (pThisTab && pThisTab->IsFollow())
+ {
+ pMasterTab = pThisTab->FindMaster();
+ }
+ }
+ }
+ }
+ //Remove first, then shrink the upper.
+ SwLayoutFrame *pUp = GetUpper();
+ RemoveFromLayout();
+ if ( !pUp )
+ {
+ assert(!pMasterTab);
+ return;
+ }
+
+ if (pMasterTab
+ && !pMasterTab->GetFollow()->GetFirstNonHeadlineRow()->ContainsContent())
+ { // only do this if there's no content in other cells of the row!
+ pMasterTab->InvalidatePos_();
+ pMasterTab->SetRemoveFollowFlowLinePending(true);
+ }
+
+ SwSectionFrame *pSct = nullptr;
+ if ( !pUp->Lower() &&
+ ( ( pUp->IsFootnoteFrame() && !pUp->IsColLocked() ) ||
+ ( pUp->IsInSct() &&
+ // #i29438#
+ // We have to consider the case that the section may be "empty"
+ // except from a temporary empty table frame.
+ // This can happen due to the new cell split feature.
+ !pUp->IsCellFrame() &&
+ // #126020# - adjust check for empty section
+ // #130797# - correct fix #126020#
+ !(pSct = pUp->FindSctFrame())->ContainsContent() &&
+ !pSct->ContainsAny( true ) ) ) )
+ {
+ if ( pUp->GetUpper() )
+ {
+
+ // prevent delete of <ColLocked> footnote frame
+ if ( pUp->IsFootnoteFrame() && !pUp->IsColLocked())
+ {
+ if( pUp->GetNext() && !pUp->GetPrev() )
+ {
+ SwFrame* pTmp = static_cast<SwLayoutFrame*>(pUp->GetNext())->ContainsAny();
+ if( pTmp )
+ pTmp->InvalidatePrt_();
+ }
+ if (!pUp->IsDeleteForbidden())
+ {
+ pUp->Cut();
+ SwFrame::DestroyFrame(pUp);
+ }
+ }
+ else
+ {
+
+ if ( pSct->IsColLocked() || !pSct->IsInFootnote() ||
+ ( pUp->IsFootnoteFrame() && pUp->IsColLocked() ) )
+ {
+ pSct->DelEmpty( false );
+ // If a locked section may not be deleted then at least
+ // its size became invalid after removing its last
+ // content.
+ pSct->InvalidateSize_();
+ }
+ else
+ {
+ pSct->DelEmpty( true );
+ SwFrame::DestroyFrame(pSct);
+ }
+ }
+ }
+ }
+ else
+ {
+ SwRectFnSet aRectFnSet(this);
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight )
+ pUp->Shrink( nFrameHeight );
+ }
+}
+
+void SwLayoutFrame::Paste( SwFrame* pParent, SwFrame* pSibling)
+{
+ OSL_ENSURE( pParent, "No parent for pasting." );
+ OSL_ENSURE( pParent->IsLayoutFrame(), "Parent is ContentFrame." );
+ OSL_ENSURE( pParent != this, "I'm the parent oneself." );
+ OSL_ENSURE( pSibling != this, "I'm my own neighbour." );
+ OSL_ENSURE( !GetPrev() && !GetNext() && !GetUpper(),
+ "I'm still registered somewhere." );
+
+ //Insert in the tree.
+ InsertBefore( static_cast<SwLayoutFrame*>(pParent), pSibling );
+
+ // OD 24.10.2002 #103517# - correct setting of variable <fnRect>
+ // <fnRect> is used for the following:
+ // (1) To invalidate the frame's size, if its size, which has to be the
+ // same as its upper/parent, differs from its upper's/parent's.
+ // (2) To adjust/grow the frame's upper/parent, if it has a dimension in its
+ // size, which is not determined by its upper/parent.
+ // Which size is which depends on the frame type and the layout direction
+ // (vertical or horizontal).
+ // There are the following cases:
+ // (A) Header and footer frames both in vertical and in horizontal layout
+ // have to size the width to the upper/parent. A dimension in the height
+ // has to cause an adjustment/grow of the upper/parent.
+ // --> <fnRect> = fnRectHori
+ // (B) Cell and column frames in vertical layout, the width has to be the
+ // same as upper/parent and a dimension in height causes adjustment/grow
+ // of the upper/parent.
+ // --> <fnRect> = fnRectHori
+ // in horizontal layout the other way around
+ // --> <fnRect> = fnRectVert
+ // (C) Other frames in vertical layout, the height has to be the
+ // same as upper/parent and a dimension in width causes adjustment/grow
+ // of the upper/parent.
+ // --> <fnRect> = fnRectVert
+ // in horizontal layout the other way around
+ // --> <fnRect> = fnRectHori
+ //SwRectFn fnRect = IsVertical() ? fnRectHori : fnRectVert;
+ SwRectFn fnRect;
+ if ( IsHeaderFrame() || IsFooterFrame() )
+ fnRect = fnRectHori;
+ else if ( IsCellFrame() || IsColumnFrame() )
+ fnRect = GetUpper()->IsVertical() ? fnRectHori : ( GetUpper()->IsVertLR() ? (GetUpper()->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert );
+ else
+ fnRect = GetUpper()->IsVertical() ? ( GetUpper()->IsVertLR() ? (GetUpper()->IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+
+ if( (getFrameArea().*fnRect->fnGetWidth)() != (pParent->getFramePrintArea().*fnRect->fnGetWidth)())
+ InvalidateSize_();
+ InvalidatePos_();
+ const SwPageFrame *pPage = FindPageFrame();
+ InvalidatePage( pPage );
+ if( !IsColumnFrame() )
+ {
+ SwFrame *pFrame = GetIndNext();
+ if( nullptr != pFrame )
+ {
+ pFrame->InvalidatePos_();
+ if( IsInFootnote() )
+ {
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->Prepare( PrepareHint::ErgoSum, nullptr, false );
+ }
+ }
+ if ( IsInFootnote() && nullptr != ( pFrame = GetIndPrev() ) )
+ {
+ if( pFrame->IsSctFrame() )
+ pFrame = static_cast<SwSectionFrame*>(pFrame)->ContainsAny();
+ if( pFrame )
+ pFrame->Prepare( PrepareHint::QuoVadis, nullptr, false );
+ }
+ }
+
+ if( !(getFrameArea().*fnRect->fnGetHeight)() )
+ return;
+
+ // AdjustNeighbourhood is now also called in columns which are not
+ // placed inside a frame
+ SwNeighbourAdjust nAdjust = GetUpper()->IsFootnoteBossFrame() ?
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
+ : SwNeighbourAdjust::GrowShrink;
+ SwTwips nGrow = (getFrameArea().*fnRect->fnGetHeight)();
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ AdjustNeighbourhood( nGrow );
+ else
+ {
+ SwTwips nReal = 0;
+ if( SwNeighbourAdjust::AdjustGrow == nAdjust )
+ nReal = AdjustNeighbourhood( nGrow );
+ if( nReal < nGrow )
+ nReal += pParent->Grow( nGrow - nReal );
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust && nReal < nGrow )
+ AdjustNeighbourhood( nGrow - nReal );
+ }
+}
+
+void SwLayoutFrame::Cut()
+{
+ if ( GetNext() )
+ GetNext()->InvalidatePos_();
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nShrink = aRectFnSet.GetHeight(getFrameArea());
+
+ // Remove first, then shrink upper.
+ SwLayoutFrame *pUp = GetUpper();
+
+ // AdjustNeighbourhood is now also called in columns which are not
+ // placed inside a frame.
+
+ // Remove must not be called before an AdjustNeighbourhood, but it has to
+ // be called before the upper-shrink-call, if the upper-shrink takes care
+ // of its content.
+ if ( pUp && nShrink )
+ {
+ if( pUp->IsFootnoteBossFrame() )
+ {
+ SwNeighbourAdjust nAdjust= static_cast<SwFootnoteBossFrame*>(pUp)->NeighbourhoodAdjustment();
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ AdjustNeighbourhood( -nShrink );
+ else
+ {
+ SwTwips nReal = 0;
+ if( SwNeighbourAdjust::AdjustGrow == nAdjust )
+ nReal = -AdjustNeighbourhood( -nShrink );
+ if( nReal < nShrink )
+ {
+ const SwTwips nOldHeight = aRectFnSet.GetHeight(getFrameArea());
+
+ // seems as if this needs to be forwarded to the SwFrame already here,
+ // changing to zero seems temporary anyways
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, 0 );
+ }
+
+ nReal += pUp->Shrink( nShrink - nReal );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nOldHeight );
+ }
+ }
+
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust && nReal < nShrink )
+ AdjustNeighbourhood( nReal - nShrink );
+ }
+ RemoveFromLayout();
+ }
+ else
+ {
+ RemoveFromLayout();
+ pUp->Shrink( nShrink );
+ }
+ }
+ else
+ RemoveFromLayout();
+
+ if( pUp && !pUp->Lower() )
+ {
+ pUp->SetCompletePaint();
+ pUp->InvalidatePage();
+ }
+}
+
+SwTwips SwFrame::Grow( SwTwips nDist, bool bTst, bool bInfo )
+{
+ OSL_ENSURE( nDist >= 0, "Negative growth?" );
+
+ PROTOCOL_ENTER( this, bTst ? PROT::GrowTest : PROT::Grow, DbgAction::NONE, &nDist )
+
+ if ( nDist )
+ {
+ SwRectFnSet aRectFnSet(this);
+
+ SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( nPrtHeight > 0 && nDist > (LONG_MAX - nPrtHeight) )
+ nDist = LONG_MAX - nPrtHeight;
+
+ if ( IsFlyFrame() )
+ return static_cast<SwFlyFrame*>(this)->Grow_( nDist, bTst );
+ else if( IsSctFrame() )
+ return static_cast<SwSectionFrame*>(this)->Grow_( nDist, bTst );
+ else
+ {
+ if (IsCellFrame())
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ const SwTabFrame* pTab = FindTabFrame();
+
+ // NEW TABLES
+ if ( pTab->IsVertical() != IsVertical() ||
+ pThisCell->GetLayoutRowSpan() < 1 )
+ return 0;
+ }
+ const SwTwips nReal = GrowFrame( nDist, bTst, bInfo );
+ if( !bTst )
+ {
+ nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight + ( IsContentFrame() ? nDist : nReal ) );
+ }
+ return nReal;
+ }
+ }
+ return 0;
+}
+
+SwTwips SwFrame::Shrink( SwTwips nDist, bool bTst, bool bInfo )
+{
+ OSL_ENSURE( nDist >= 0, "Negative reduction?" );
+
+ PROTOCOL_ENTER( this, bTst ? PROT::ShrinkTest : PROT::Shrink, DbgAction::NONE, &nDist )
+
+ if ( nDist )
+ {
+ if ( IsFlyFrame() )
+ return static_cast<SwFlyFrame*>(this)->Shrink_( nDist, bTst );
+ else if( IsSctFrame() )
+ return static_cast<SwSectionFrame*>(this)->Shrink_( nDist, bTst );
+ else
+ {
+ if (IsCellFrame())
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ const SwTabFrame* pTab = FindTabFrame();
+
+ // NEW TABLES
+ if ( (pTab && pTab->IsVertical() != IsVertical()) ||
+ pThisCell->GetLayoutRowSpan() < 1 )
+ return 0;
+ }
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nReal = aRectFnSet.GetHeight(getFrameArea());
+ ShrinkFrame( nDist, bTst, bInfo );
+ nReal -= aRectFnSet.GetHeight(getFrameArea());
+ if( !bTst )
+ {
+ const SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, nPrtHeight - ( IsContentFrame() ? nDist : nReal ) );
+ }
+ return nReal;
+ }
+ }
+ return 0;
+}
+
+/** Adjust surrounding neighbourhood after insertion
+ *
+ * A Frame needs "normalization" if it is directly placed below a footnote boss (page/column) and its
+ * size changes. There is always a frame that takes the maximum possible space (the frame that
+ * contains the Body text) and zero or more frames which only take the space needed (header/footer
+ * area, footnote container). If one of these frames changes, the body-text-frame has to grow or
+ * shrink accordingly, even though it's fixed.
+ *
+ * !! Is it possible to do this in a generic way and not restrict it to the page and a distinct
+ * frame which takes the maximum space (controlled using the FrameSize attribute)?
+ * Problems:
+ * - What if multiple frames taking the maximum space are placed next to each other?
+ * - How is the maximum space calculated?
+ * - How small can those frames become?
+ *
+ * In any case, only a certain amount of space is allowed, so we never go below a minimum value for
+ * the height of the body.
+ *
+ * @param nDiff the value around which the space has to be allocated
+ */
+SwTwips SwFrame::AdjustNeighbourhood( SwTwips nDiff, bool bTst )
+{
+ PROTOCOL_ENTER( this, PROT::AdjustN, DbgAction::NONE, &nDiff );
+
+ if ( !nDiff || !GetUpper()->IsFootnoteBossFrame() ) // only inside pages/columns
+ return 0;
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+
+ //The (Page-)Body only changes in BrowseMode, but only if it does not
+ //contain columns.
+ if ( IsPageBodyFrame() && (!bBrowse ||
+ (static_cast<SwLayoutFrame*>(this)->Lower() &&
+ static_cast<SwLayoutFrame*>(this)->Lower()->IsColumnFrame())) )
+ return 0;
+
+ //In BrowseView mode the PageFrame can handle some of the requests.
+ tools::Long nBrowseAdd = 0;
+ if ( bBrowse && GetUpper()->IsPageFrame() ) // only (Page-)BodyFrames
+ {
+ SwViewShell *pViewShell = getRootFrame()->GetCurrShell();
+ SwLayoutFrame *pUp = GetUpper();
+ tools::Long nChg;
+ const tools::Long nUpPrtBottom = pUp->getFrameArea().Height() -
+ pUp->getFramePrintArea().Height() - pUp->getFramePrintArea().Top();
+ SwRect aInva( pUp->getFrameArea() );
+ if ( pViewShell )
+ {
+ aInva.Pos().setX( pViewShell->VisArea().Left() );
+ aInva.Width( pViewShell->VisArea().Width() );
+ }
+ if ( nDiff > 0 )
+ {
+ nChg = BROWSE_HEIGHT - pUp->getFrameArea().Height();
+ nChg = std::min( nDiff, SwTwips(nChg) );
+
+ if ( !IsBodyFrame() )
+ {
+ SetCompletePaint();
+ if ( !pViewShell || pViewShell->VisArea().Height() >= pUp->getFrameArea().Height() )
+ {
+ //First minimize Body, it will grow again later.
+ SwFrame *pBody = static_cast<SwFootnoteBossFrame*>(pUp)->FindBodyCont();
+ const tools::Long nTmp = nChg - pBody->getFramePrintArea().Height();
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pBody);
+ aFrm.Height(std::max( tools::Long(0), aFrm.Height() - nChg ));
+ }
+
+ pBody->InvalidatePrt_();
+ pBody->InvalidateSize_();
+ if ( pBody->GetNext() )
+ pBody->GetNext()->InvalidatePos_();
+ if ( !IsHeaderFrame() )
+ pBody->SetCompletePaint();
+ }
+ nChg = nTmp <= 0 ? 0 : nTmp;
+ }
+ }
+
+ const tools::Long nTmp = nUpPrtBottom + 20;
+ aInva.Top( aInva.Bottom() - nTmp );
+ aInva.Height( nChg + nTmp );
+ }
+ else
+ {
+ //The page can shrink to 0. The first page keeps the same size like
+ //VisArea.
+ nChg = nDiff;
+ tools::Long nInvaAdd = 0;
+ if ( pViewShell && !pUp->GetPrev() &&
+ pUp->getFrameArea().Height() + nDiff < pViewShell->VisArea().Height() )
+ {
+ // This means that we have to invalidate adequately.
+ nChg = pViewShell->VisArea().Height() - pUp->getFrameArea().Height();
+ nInvaAdd = -(nDiff - nChg);
+ }
+
+ //Invalidate including bottom border.
+ tools::Long nBorder = nUpPrtBottom + 20;
+ nBorder -= nChg;
+ aInva.Top( aInva.Bottom() - (nBorder+nInvaAdd) );
+ if ( !IsBodyFrame() )
+ {
+ SetCompletePaint();
+ if ( !IsHeaderFrame() )
+ static_cast<SwFootnoteBossFrame*>(pUp)->FindBodyCont()->SetCompletePaint();
+ }
+ //Invalidate the page because of the frames. Thereby the page becomes
+ //the right size again if a frame didn't fit. This only works
+ //randomly for paragraph bound frames otherwise (NotifyFlys).
+ pUp->InvalidateSize();
+ }
+ if ( !bTst )
+ {
+ //Independent from nChg
+ if ( pViewShell && aInva.HasArea() && pUp->GetUpper() )
+ pViewShell->InvalidateWindows( aInva );
+ }
+ if ( !bTst && nChg )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pUp);
+ aFrm.AddHeight(nChg );
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pUp);
+ aPrt.AddHeight(nChg );
+ }
+
+ if ( pViewShell )
+ pViewShell->Imp()->SetFirstVisPageInvalid();
+
+ if ( GetNext() )
+ GetNext()->InvalidatePos_();
+
+ //Trigger a repaint if necessary.
+ std::unique_ptr<SvxBrushItem> aBack(pUp->GetFormat()->makeBackgroundBrushItem());
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( ePos != GPOS_NONE && ePos != GPOS_TILED )
+ pViewShell->InvalidateWindows( pUp->getFrameArea() );
+
+ if ( pUp->GetUpper() )
+ {
+ if ( pUp->GetNext() )
+ pUp->GetNext()->InvalidatePos();
+
+ //Sad but true: during notify on ViewImp a Calc on the page and
+ //its Lower may be called. The values should not be changed
+ //because the caller takes care of the adjustment of Frame and
+ //Prt.
+ const tools::Long nOldFrameHeight = getFrameArea().Height();
+ const tools::Long nOldPrtHeight = getFramePrintArea().Height();
+ const bool bOldComplete = IsCompletePaint();
+
+ if ( IsBodyFrame() )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( nOldFrameHeight );
+ }
+
+ if ( pUp->GetUpper() )
+ {
+ static_cast<SwRootFrame*>(pUp->GetUpper())->CheckViewLayout( nullptr, nullptr );
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( nOldFrameHeight );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( nOldPrtHeight );
+
+ mbCompletePaint = bOldComplete;
+ }
+ if ( !IsBodyFrame() )
+ pUp->InvalidateSize_();
+ InvalidatePage( static_cast<SwPageFrame*>(pUp) );
+ }
+ nDiff -= nChg;
+ if ( !nDiff )
+ return nChg;
+ else
+ nBrowseAdd = nChg;
+ }
+
+ const SwFootnoteBossFrame *pBoss = static_cast<SwFootnoteBossFrame*>(GetUpper());
+
+ SwTwips nReal = 0,
+ nAdd = 0;
+ SwFrame *pFrame = nullptr;
+ SwRectFnSet aRectFnSet(this);
+
+ if( IsBodyFrame() )
+ {
+ if( IsInSct() )
+ {
+ SwSectionFrame *pSect = FindSctFrame();
+ if( nDiff > 0 && pSect->IsEndnAtEnd() && GetNext() &&
+ GetNext()->IsFootnoteContFrame() )
+ {
+ SwFootnoteContFrame* pCont = static_cast<SwFootnoteContFrame*>(GetNext());
+ SwTwips nMinH = 0;
+ SwFootnoteFrame* pFootnote = static_cast<SwFootnoteFrame*>(pCont->Lower());
+ bool bFootnote = false;
+ while( pFootnote )
+ {
+ if( !pFootnote->GetAttr()->GetFootnote().IsEndNote() )
+ {
+ nMinH += aRectFnSet.GetHeight(pFootnote->getFrameArea());
+ bFootnote = true;
+ }
+ pFootnote = static_cast<SwFootnoteFrame*>(pFootnote->GetNext());
+ }
+ if( bFootnote )
+ nMinH += aRectFnSet.GetTop(pCont->getFramePrintArea());
+ nReal = aRectFnSet.GetHeight(pCont->getFrameArea()) - nMinH;
+ if( nReal > nDiff )
+ nReal = nDiff;
+ if( nReal > 0 )
+ pFrame = GetNext();
+ else
+ nReal = 0;
+ }
+ if( !bTst && !pSect->IsColLocked() )
+ pSect->InvalidateSize();
+ }
+ if( !pFrame )
+ return nBrowseAdd;
+ }
+ else
+ {
+ const bool bFootnotePage = pBoss->IsPageFrame() && static_cast<const SwPageFrame*>(pBoss)->IsFootnotePage();
+ if ( bFootnotePage && !IsFootnoteContFrame() )
+ pFrame = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoss->FindFootnoteCont()));
+ if ( !pFrame )
+ pFrame = const_cast<SwFrame*>(static_cast<SwFrame const *>(pBoss->FindBodyCont()));
+
+ if ( !pFrame )
+ return 0;
+
+ //If not one is found, everything else is solved.
+ nReal = aRectFnSet.GetHeight(pFrame->getFrameArea());
+ if( nReal > nDiff )
+ nReal = nDiff;
+ if( !bFootnotePage )
+ {
+ //Respect the minimal boundary!
+ if( nReal )
+ {
+ const SwTwips nMax = pBoss->GetVarSpace();
+ if ( nReal > nMax )
+ nReal = nMax;
+ }
+ if( !IsFootnoteContFrame() && nDiff > nReal &&
+ pFrame->GetNext() && pFrame->GetNext()->IsFootnoteContFrame()
+ && ( pFrame->GetNext()->IsVertical() == IsVertical() )
+ )
+ {
+ //If the Body doesn't return enough, we look for a footnote, if
+ //there is one, we steal there accordingly.
+ const SwTwips nAddMax = aRectFnSet.GetHeight(pFrame->GetNext()->getFrameArea());
+ nAdd = nDiff - nReal;
+ if ( nAdd > nAddMax )
+ nAdd = nAddMax;
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame->GetNext());
+ aRectFnSet.SetHeight(aFrm, nAddMax-nAdd);
+
+ if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ aFrm.Pos().AdjustX(nAdd );
+ }
+ }
+
+ pFrame->GetNext()->InvalidatePrt();
+
+ if ( pFrame->GetNext()->GetNext() )
+ {
+ pFrame->GetNext()->GetNext()->InvalidatePos_();
+ }
+ }
+ }
+ }
+ }
+
+ if ( !bTst && nReal )
+ {
+ SwTwips nTmp = aRectFnSet.GetHeight(pFrame->getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
+ aRectFnSet.SetHeight( aFrm, nTmp - nReal );
+
+ if( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ aFrm.Pos().AdjustX(nReal );
+ }
+ }
+
+ pFrame->InvalidatePrt();
+
+ if ( pFrame->GetNext() )
+ pFrame->GetNext()->InvalidatePos_();
+
+ if( nReal < 0 && pFrame->IsInSct() )
+ {
+ SwLayoutFrame* pUp = pFrame->GetUpper();
+ if( pUp && nullptr != ( pUp = pUp->GetUpper() ) && pUp->IsSctFrame() &&
+ !pUp->IsColLocked() )
+ pUp->InvalidateSize();
+ }
+ if( ( IsHeaderFrame() || IsFooterFrame() ) && pBoss->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pBoss->GetDrawObjs();
+ OSL_ENSURE( pBoss->IsPageFrame(), "Header/Footer out of page?" );
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ OSL_ENSURE( !pFly->IsFlyInContentFrame(), "FlyInCnt at Page?" );
+ const SwFormatVertOrient &rVert =
+ pFly->GetFormat()->GetVertOrient();
+ // When do we have to invalidate?
+ // If a frame is aligned on a PageTextArea and the header
+ // changes a TOP, MIDDLE or NONE aligned frame needs to
+ // recalculate it's position; if the footer changes a BOTTOM
+ // or MIDDLE aligned frame needs to recalculate it's
+ // position.
+ if( ( rVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ||
+ rVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA ) &&
+ ((IsHeaderFrame() && rVert.GetVertOrient()!=text::VertOrientation::BOTTOM) ||
+ (IsFooterFrame() && rVert.GetVertOrient()!=text::VertOrientation::NONE &&
+ rVert.GetVertOrient() != text::VertOrientation::TOP)) )
+ {
+ pFly->InvalidatePos_();
+ pFly->Invalidate_();
+ }
+ }
+ }
+ }
+ }
+ return (nBrowseAdd + nReal + nAdd);
+}
+
+/** method to perform additional actions on an invalidation (2004-05-19 #i28701#) */
+void SwFrame::ActionOnInvalidation( const InvalidationType )
+{
+ // default behaviour is to perform no additional action
+}
+
+/** method to determine, if an invalidation is allowed (2004-05-19 #i28701#) */
+bool SwFrame::InvalidationAllowed( const InvalidationType ) const
+{
+ // default behaviour is to allow invalidation
+ return true;
+}
+
+void SwFrame::ImplInvalidateSize()
+{
+ if ( InvalidationAllowed( INVALID_SIZE ) )
+ {
+ setFrameAreaSizeValid(false);
+
+ if ( IsFlyFrame() )
+ static_cast<SwFlyFrame*>(this)->Invalidate_();
+ else
+ InvalidatePage();
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_SIZE );
+ }
+}
+
+void SwFrame::ImplInvalidatePrt()
+{
+ if ( InvalidationAllowed( INVALID_PRTAREA ) )
+ {
+ setFramePrintAreaValid(false);
+
+ if ( IsFlyFrame() )
+ static_cast<SwFlyFrame*>(this)->Invalidate_();
+ else
+ InvalidatePage();
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_PRTAREA );
+ }
+}
+
+void SwFrame::ImplInvalidatePos()
+{
+ if ( !InvalidationAllowed( INVALID_POS ) )
+ return;
+
+ setFrameAreaPositionValid(false);
+
+ if ( IsFlyFrame() )
+ {
+ static_cast<SwFlyFrame*>(this)->Invalidate_();
+ }
+ else
+ {
+ InvalidatePage();
+ }
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_POS );
+}
+
+void SwFrame::ImplInvalidateLineNum()
+{
+ if ( InvalidationAllowed( INVALID_LINENUM ) )
+ {
+ mbValidLineNum = false;
+ OSL_ENSURE( IsTextFrame(), "line numbers are implemented for text only" );
+ InvalidatePage();
+
+ // OD 2004-05-19 #i28701#
+ ActionOnInvalidation( INVALID_LINENUM );
+ }
+}
+
+void SwFrame::ReinitializeFrameSizeAttrFlags()
+{
+ const SwFormatFrameSize &rFormatSize = GetAttrSet()->GetFrameSize();
+ if ( SwFrameSize::Variable == rFormatSize.GetHeightSizeType() ||
+ SwFrameSize::Minimum == rFormatSize.GetHeightSizeType())
+ {
+ mbFixSize = false;
+ if ( GetType() & (SwFrameType::Header | SwFrameType::Footer | SwFrameType::Row) )
+ {
+ SwFrame *pFrame = static_cast<SwLayoutFrame*>(this)->Lower();
+ while ( pFrame )
+ { pFrame->InvalidateSize_();
+ pFrame->InvalidatePrt_();
+ pFrame = pFrame->GetNext();
+ }
+ SwContentFrame *pCnt = static_cast<SwLayoutFrame*>(this)->ContainsContent();
+ // #i36991# - be save.
+ // E.g., a row can contain *no* content.
+ if ( pCnt )
+ {
+ pCnt->InvalidatePage();
+ do
+ {
+ pCnt->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pCnt->InvalidateSize_();
+ pCnt = pCnt->GetNextContentFrame();
+ } while ( static_cast<SwLayoutFrame*>(this)->IsAnLower( pCnt ) );
+ }
+ }
+ }
+ else if ( rFormatSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ {
+ if( IsVertical() )
+ ChgSize( Size( rFormatSize.GetWidth(), getFrameArea().Height()));
+ else
+ ChgSize( Size( getFrameArea().Width(), rFormatSize.GetHeight()));
+ }
+}
+
+void SwFrame::ValidateThisAndAllLowers( const sal_uInt16 nStage )
+{
+ // Stage 0: Only validate frames. Do not process any objects.
+ // Stage 1: Only validate fly frames and all of their contents.
+ // Stage 2: Validate all.
+
+ const bool bOnlyObject = 1 == nStage;
+ const bool bIncludeObjects = 1 <= nStage;
+
+ if ( !bOnlyObject || IsFlyFrame() )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ setFrameAreaPositionValid(true);
+ }
+
+ if ( bIncludeObjects )
+ {
+ const SwSortedObjs* pObjs = GetDrawObjs();
+ if ( pObjs )
+ {
+ const size_t nCnt = pObjs->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchObj = (*pObjs)[i];
+ if ( auto pFlyFrame = pAnchObj->DynCastFlyFrame() )
+ pFlyFrame->ValidateThisAndAllLowers( 2 );
+ else if ( auto pAnchoredDrawObj = dynamic_cast<SwAnchoredDrawObject *>( pAnchObj ) )
+ pAnchoredDrawObj->ValidateThis();
+ }
+ }
+ }
+
+ if ( IsLayoutFrame() )
+ {
+ SwFrame* pLower = static_cast<SwLayoutFrame*>(this)->Lower();
+ while ( pLower )
+ {
+ pLower->ValidateThisAndAllLowers( nStage );
+ pLower = pLower->GetNext();
+ }
+ }
+}
+
+SwTwips SwContentFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if( nFrameHeight > 0 &&
+ nDist > (LONG_MAX - nFrameHeight ) )
+ nDist = LONG_MAX - nFrameHeight;
+
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
+ if (bBrowse)
+ nTmpType |= SwFrameType::Body;
+ if( !(GetUpper()->GetType() & nTmpType) && GetUpper()->HasFixSize() )
+ {
+ if ( !bTst )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight + nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+ }
+
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos();
+ }
+ // #i28701# - Due to the new object positioning the
+ // frame on the next page/column can flow backward (e.g. it was moved forward
+ // due to the positioning of its objects ). Thus, invalivate this next frame,
+ // if document compatibility option 'Consider wrapping style influence on
+ // object positioning' is ON.
+ else if ( GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ }
+ return 0;
+ }
+
+ SwTwips nReal = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea());
+ SwFrame *pFrame = GetUpper()->Lower();
+ while( pFrame && nReal > 0 )
+ { nReal -= aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+
+ if ( !bTst )
+ {
+ //Contents are always resized to the wished value.
+ tools::Long nOld = aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+
+ aRectFnSet.SetHeight( aFrm, nOld + nDist );
+
+ if( IsVertical()&& !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+ }
+
+ SwTabFrame *pTab = (nOld && IsInTab()) ? FindTabFrame() : nullptr;
+ if (pTab)
+ {
+ if ( pTab->GetTable()->GetHTMLTableLayout() &&
+ !pTab->IsJoinLocked() &&
+ !pTab->GetFormat()->GetDoc()->GetDocShell()->IsReadOnly() )
+ {
+ pTab->InvalidatePos();
+ pTab->SetResizeHTMLTable();
+ }
+ }
+ }
+
+ //Only grow Upper if necessary.
+ if ( nReal < nDist )
+ {
+ if( GetUpper() )
+ {
+ if( bTst || !GetUpper()->IsFooterFrame() )
+ nReal = GetUpper()->Grow( nDist - std::max<tools::Long>(nReal, 0),
+ bTst, bInfo );
+ else
+ {
+ nReal = 0;
+ GetUpper()->InvalidateSize();
+ }
+ }
+ else
+ nReal = 0;
+ }
+ else
+ nReal = nDist;
+
+ // #i28701# - Due to the new object positioning the
+ // frame on the next page/column can flow backward (e.g. it was moved forward
+ // due to the positioning of its objects ). Thus, invalivate this next frame,
+ // if document compatibility option 'Consider wrapping style influence on
+ // object positioning' is ON.
+ if ( !bTst )
+ {
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos();
+ }
+ else if ( GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) )
+ {
+ InvalidateNextPos();
+ }
+ }
+
+ return nReal;
+}
+
+SwTwips SwContentFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ SwRectFnSet aRectFnSet(this);
+ OSL_ENSURE( nDist >= 0, "nDist < 0" );
+ OSL_ENSURE( nDist <= aRectFnSet.GetHeight(getFrameArea()),
+ "nDist > than current size." );
+
+ if ( !bTst )
+ {
+ SwTwips nRstHeight;
+ if( GetUpper() )
+ nRstHeight = aRectFnSet.BottomDist( getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()) );
+ else
+ nRstHeight = 0;
+ if( nRstHeight < 0 )
+ {
+ SwTwips nNextHeight = 0;
+ if( GetUpper()->IsSctFrame() && nDist > LONG_MAX/2 )
+ {
+ SwFrame *pNxt = GetNext();
+ while( pNxt )
+ {
+ nNextHeight += aRectFnSet.GetHeight(pNxt->getFrameArea());
+ pNxt = pNxt->GetNext();
+ }
+ }
+ nRstHeight = nDist + nRstHeight - nNextHeight;
+ }
+ else
+ {
+ nRstHeight = nDist;
+ }
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) - nDist );
+
+ if( IsVertical() && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nDist );
+ }
+ }
+
+ nDist = nRstHeight;
+ SwTabFrame *pTab = IsInTab() ? FindTabFrame() : nullptr;
+ if (pTab)
+ {
+ if ( pTab->GetTable()->GetHTMLTableLayout() &&
+ !pTab->IsJoinLocked() &&
+ !pTab->GetFormat()->GetDoc()->GetDocShell()->IsReadOnly() )
+ {
+ pTab->InvalidatePos();
+ pTab->SetResizeHTMLTable();
+ }
+ }
+ }
+
+ SwTwips nReal;
+ if( GetUpper() && nDist > 0 )
+ {
+ if( bTst || !GetUpper()->IsFooterFrame() )
+ nReal = GetUpper()->Shrink( nDist, bTst, bInfo );
+ else
+ {
+ nReal = 0;
+
+ // #108745# Sorry, dear old footer friend, I'm not gonna invalidate you,
+ // if there are any objects anchored inside your content, which
+ // overlap with the shrinking frame.
+ // This may lead to a footer frame that is too big, but this is better
+ // than looping.
+ // #109722# : The fix for #108745# was too strict.
+
+ bool bInvalidate = true;
+ const SwRect aRect( getFrameArea() );
+ const SwPageFrame* pPage = FindPageFrame();
+ const SwSortedObjs* pSorted = pPage ? pPage->GetSortedObjs() : nullptr;
+ if( pSorted )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pSorted)
+ {
+ const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() );
+
+ if( aBound.Left() > aRect.Right() )
+ continue;
+
+ if( aBound.Overlaps( aRect ) )
+ {
+ const SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ if( css::text::WrapTextMode_THROUGH != rFormat.GetSurround().GetSurround() )
+ {
+ const SwFrame* pAnchor = pAnchoredObj->GetAnchorFrame();
+ if ( pAnchor && pAnchor->FindFooterOrHeader() == GetUpper() )
+ {
+ bInvalidate = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bInvalidate )
+ GetUpper()->InvalidateSize();
+ }
+ }
+ else
+ nReal = 0;
+
+ if ( !bTst )
+ {
+ //The position of the next Frame changes for sure.
+ InvalidateNextPos();
+
+ //If I don't have a successor I have to do the retouch by myself.
+ if ( !GetNext() )
+ SetRetouche();
+ }
+ return nReal;
+}
+
+void SwContentFrame::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ SwContentFrameInvFlags eInvFlags = SwContentFrameInvFlags::NONE;
+ if(pLegacy->m_pNew && RES_ATTRSET_CHG == pLegacy->m_pNew->Which() && pLegacy->m_pOld)
+ {
+ auto& rOldSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pOld);
+ auto& rNewSetChg = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew);
+ SfxItemIter aOIter(*rOldSetChg.GetChgSet());
+ SfxItemIter aNIter(*rNewSetChg.GetChgSet());
+ const SfxPoolItem* pNItem = aNIter.GetCurItem();
+ const SfxPoolItem* pOItem = aOIter.GetCurItem();
+ SwAttrSetChg aOldSet(rOldSetChg);
+ SwAttrSetChg aNewSet(rNewSetChg);
+ do
+ {
+ UpdateAttr_(pOItem, pNItem, eInvFlags, &aOldSet, &aNewSet);
+ pNItem = aNIter.NextItem();
+ pOItem = aOIter.NextItem();
+ } while(pNItem);
+ if(aOldSet.Count() || aNewSet.Count())
+ SwFrame::SwClientNotify(rMod, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ else
+ UpdateAttr_(pLegacy->m_pOld, pLegacy->m_pNew, eInvFlags);
+
+ if(eInvFlags == SwContentFrameInvFlags::NONE)
+ return;
+
+ SwPageFrame* pPage = FindPageFrame();
+ InvalidatePage(pPage);
+ if(eInvFlags & SwContentFrameInvFlags::SetCompletePaint)
+ SetCompletePaint();
+ if(eInvFlags & SwContentFrameInvFlags::InvalidatePos)
+ InvalidatePos_();
+ if(eInvFlags & SwContentFrameInvFlags::InvalidateSize)
+ InvalidateSize_();
+ if(eInvFlags & (SwContentFrameInvFlags::InvalidateSectPrt | SwContentFrameInvFlags::SetNextCompletePaint))
+ {
+ if(IsInSct() && !GetPrev())
+ {
+ SwSectionFrame* pSect = FindSctFrame();
+ if(pSect->ContainsAny() == this)
+ {
+ pSect->InvalidatePrt_();
+ pSect->InvalidatePage(pPage);
+ }
+ }
+ InvalidatePrt_();
+ }
+ SwFrame* pNextFrame = GetIndNext();
+ if(pNextFrame && eInvFlags & SwContentFrameInvFlags::InvalidateNextPrt)
+ {
+ pNextFrame->InvalidatePrt_();
+ pNextFrame->InvalidatePage(pPage);
+ }
+ if(pNextFrame && eInvFlags & SwContentFrameInvFlags::SetNextCompletePaint)
+ {
+ pNextFrame->SetCompletePaint();
+ }
+ if(eInvFlags & SwContentFrameInvFlags::InvalidatePrevPrt)
+ {
+ SwFrame* pPrevFrame = GetPrev();
+ if(pPrevFrame)
+ {
+ pPrevFrame->InvalidatePrt_();
+ pPrevFrame->InvalidatePage(pPage);
+ }
+ }
+ if(eInvFlags & SwContentFrameInvFlags::InvalidateNextPos)
+ InvalidateNextPos();
+}
+
+void SwContentFrame::UpdateAttr_( const SfxPoolItem* pOld, const SfxPoolItem* pNew,
+ SwContentFrameInvFlags &rInvFlags,
+ SwAttrSetChg *pOldSet, SwAttrSetChg *pNewSet )
+{
+ bool bClear = true;
+ sal_uInt16 nWhich = pOld ? pOld->Which() : pNew ? pNew->Which() : 0;
+ switch ( nWhich )
+ {
+ case RES_FMT_CHG:
+ rInvFlags = SwContentFrameInvFlags::SetCompletePaint
+ | SwContentFrameInvFlags::InvalidatePos
+ | SwContentFrameInvFlags::InvalidateSize
+ | SwContentFrameInvFlags::InvalidateSectPrt
+ | SwContentFrameInvFlags::InvalidateNextPrt
+ | SwContentFrameInvFlags::InvalidatePrevPrt
+ | SwContentFrameInvFlags::InvalidateNextPos
+ | SwContentFrameInvFlags::SetNextCompletePaint;
+ [[fallthrough]];
+
+ case RES_PAGEDESC: //attribute changes (on/off)
+ if ( IsInDocBody() && !IsInTab() )
+ {
+ rInvFlags |= SwContentFrameInvFlags::InvalidatePos;
+ SwPageFrame *pPage = FindPageFrame();
+ if ( !GetPrev() )
+ CheckPageDescs( pPage );
+ if (GetPageDescItem().GetNumOffset())
+ static_cast<SwRootFrame*>(pPage->GetUpper())->SetVirtPageNum( true );
+ SwDocPosUpdate aMsgHint( pPage->getFrameArea().Top() );
+ pPage->GetFormat()->GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ }
+ break;
+
+ case RES_UL_SPACE:
+ {
+ // OD 2004-02-18 #106629# - correction
+ // Invalidation of the printing area of next frame, not only
+ // for footnote content.
+ if ( !GetIndNext() )
+ {
+ SwFrame* pNxt = FindNext();
+ if ( pNxt )
+ {
+ SwPageFrame* pPg = pNxt->FindPageFrame();
+ pNxt->InvalidatePage( pPg );
+ pNxt->InvalidatePrt_();
+ if( pNxt->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ if( pCnt )
+ {
+ pCnt->InvalidatePrt_();
+ pCnt->InvalidatePage( pPg );
+ }
+ }
+ pNxt->SetCompletePaint();
+ }
+ }
+ // OD 2004-03-17 #i11860#
+ if ( GetIndNext() &&
+ !GetUpper()->GetFormat()->getIDocumentSettingAccess().get(DocumentSettingId::USE_FORMER_OBJECT_POS) )
+ {
+ // OD 2004-07-01 #i28701# - use new method <InvalidateObjs(..)>
+ GetIndNext()->InvalidateObjs();
+ }
+ Prepare( PrepareHint::ULSpaceChanged ); //TextFrame has to correct line spacing.
+ rInvFlags |= SwContentFrameInvFlags::SetNextCompletePaint;
+ [[fallthrough]];
+ }
+ case RES_LR_SPACE:
+ case RES_BOX:
+ case RES_SHADOW:
+ {
+ Prepare( PrepareHint::FixSizeChanged );
+ SwModify aMod;
+ SwFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ rInvFlags |= SwContentFrameInvFlags::InvalidateNextPrt | SwContentFrameInvFlags::InvalidatePrevPrt;
+ break;
+ }
+ case RES_BREAK:
+ {
+ rInvFlags |= SwContentFrameInvFlags::InvalidatePos | SwContentFrameInvFlags::InvalidateNextPos;
+ const IDocumentSettingAccess& rIDSA = GetUpper()->GetFormat()->getIDocumentSettingAccess();
+ if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) ||
+ rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) )
+ {
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ SwFrame* pNxt = FindNext();
+ if( pNxt )
+ {
+ SwPageFrame* pPg = pNxt->FindPageFrame();
+ pNxt->InvalidatePage( pPg );
+ pNxt->InvalidatePrt_();
+ if( pNxt->IsSctFrame() )
+ {
+ SwFrame* pCnt = static_cast<SwSectionFrame*>(pNxt)->ContainsAny();
+ if( pCnt )
+ {
+ pCnt->InvalidatePrt_();
+ pCnt->InvalidatePage( pPg );
+ }
+ }
+ pNxt->SetCompletePaint();
+ }
+ }
+ }
+ break;
+
+ // OD 2004-02-26 #i25029#
+ case RES_PARATR_CONNECT_BORDER:
+ {
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ if ( IsTextFrame() )
+ {
+ InvalidateNextPrtArea();
+ }
+ if ( !GetIndNext() && IsInTab() && IsInSplitTableRow() )
+ {
+ FindTabFrame()->InvalidateSize();
+ }
+ }
+ break;
+
+ case RES_PARATR_TABSTOP:
+ case RES_CHRATR_SHADOWED:
+ case RES_CHRATR_AUTOKERN:
+ case RES_CHRATR_UNDERLINE:
+ case RES_CHRATR_OVERLINE:
+ case RES_CHRATR_KERNING:
+ case RES_CHRATR_FONT:
+ case RES_CHRATR_FONTSIZE:
+ case RES_CHRATR_ESCAPEMENT:
+ case RES_CHRATR_CONTOUR:
+ case RES_PARATR_NUMRULE:
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ break;
+
+ case RES_FRM_SIZE:
+ rInvFlags |= SwContentFrameInvFlags::SetCompletePaint;
+ [[fallthrough]];
+
+ default:
+ bClear = false;
+ }
+ if ( !bClear )
+ return;
+
+ if ( pOldSet || pNewSet )
+ {
+ if ( pOldSet )
+ pOldSet->ClearItem( nWhich );
+ if ( pNewSet )
+ pNewSet->ClearItem( nWhich );
+ }
+ else
+ {
+ SwModify aMod;
+ SwFrame::SwClientNotify(aMod, sw::LegacyModifyHint(pOld, pNew));
+ }
+}
+
+SwLayoutFrame::SwLayoutFrame(SwFrameFormat *const pFormat, SwFrame *const pSib)
+ : SwFrame(pFormat, pSib)
+ , m_pLower(nullptr)
+{
+ const SwFormatFrameSize &rFormatSize = pFormat->GetFrameSize();
+ if ( rFormatSize.GetHeightSizeType() == SwFrameSize::Fixed )
+ mbFixSize = true;
+}
+
+// #i28701#
+
+SwTwips SwLayoutFrame::InnerHeight() const
+{
+ const SwFrame* pCnt = Lower();
+ if (!pCnt)
+ return 0;
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nRet = 0;
+ if( pCnt->IsColumnFrame() || pCnt->IsCellFrame() )
+ {
+ do
+ {
+ SwTwips nTmp = static_cast<const SwLayoutFrame*>(pCnt)->InnerHeight();
+ if( pCnt->isFramePrintAreaValid() )
+ nTmp += aRectFnSet.GetHeight(pCnt->getFrameArea()) -
+ aRectFnSet.GetHeight(pCnt->getFramePrintArea());
+ if( nRet < nTmp )
+ nRet = nTmp;
+ pCnt = pCnt->GetNext();
+ } while ( pCnt );
+ }
+ else
+ {
+ do
+ {
+ nRet += aRectFnSet.GetHeight(pCnt->getFrameArea());
+ if( pCnt->IsContentFrame() && static_cast<const SwTextFrame*>(pCnt)->IsUndersized() )
+ nRet += static_cast<const SwTextFrame*>(pCnt)->GetParHeight() -
+ aRectFnSet.GetHeight(pCnt->getFramePrintArea());
+ if( pCnt->IsLayoutFrame() && !pCnt->IsTabFrame() )
+ nRet += static_cast<const SwLayoutFrame*>(pCnt)->InnerHeight() -
+ aRectFnSet.GetHeight(pCnt->getFramePrintArea());
+ pCnt = pCnt->GetNext();
+ } while( pCnt );
+
+ }
+ return nRet;
+}
+
+SwTwips SwLayoutFrame::GrowFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
+ if (bBrowse)
+ nTmpType |= SwFrameType::Body;
+ if( !(GetType() & nTmpType) && HasFixSize() )
+ return 0;
+
+ SwRectFnSet aRectFnSet(this);
+ const SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ const SwTwips nFramePos = getFrameArea().Pos().X();
+
+ if ( nFrameHeight > 0 && nDist > (LONG_MAX - nFrameHeight) )
+ nDist = LONG_MAX - nFrameHeight;
+
+ SwTwips nMin = 0;
+ if ( GetUpper() && !IsCellFrame() )
+ {
+ SwFrame *pFrame = GetUpper()->Lower();
+ while( pFrame )
+ { nMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+ nMin = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) - nMin;
+ if ( nMin < 0 )
+ nMin = 0;
+ }
+
+ SwRect aOldFrame( getFrameArea() );
+ bool bMoveAccFrame = false;
+
+ bool bChgPos = IsVertical();
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight + nDist );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX( -nDist );
+ }
+
+ bMoveAccFrame = true;
+ }
+
+ SwTwips nReal = nDist - nMin;
+ if ( nReal > 0 )
+ {
+ if ( GetUpper() )
+ { // AdjustNeighbourhood now only for the columns (but not in frames)
+ SwNeighbourAdjust nAdjust = GetUpper()->IsFootnoteBossFrame() ?
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
+ : SwNeighbourAdjust::GrowShrink;
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ nReal = AdjustNeighbourhood( nReal, bTst );
+ else
+ {
+ if( SwNeighbourAdjust::AdjustGrow == nAdjust )
+ nReal += AdjustNeighbourhood( nReal, bTst );
+
+ SwTwips nGrow = 0;
+ if( 0 < nReal )
+ {
+ SwFrame* pToGrow = GetUpper();
+ // NEW TABLES
+ // A cell with a row span of > 1 is allowed to grow the
+ // line containing the end of the row span if it is
+ // located in the same table frame:
+ if (IsCellFrame())
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ if ( pThisCell->GetLayoutRowSpan() > 1 )
+ {
+ SwCellFrame& rEndCell = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( false ));
+ if ( -1 == rEndCell.GetTabBox()->getRowSpan() )
+ pToGrow = rEndCell.GetUpper();
+ else
+ pToGrow = nullptr;
+ }
+ }
+ nGrow = pToGrow ? pToGrow->Grow( nReal, bTst, bInfo ) : 0;
+ }
+
+ if( SwNeighbourAdjust::GrowAdjust == nAdjust && nGrow < nReal )
+ nReal = o3tl::saturating_add(nReal, AdjustNeighbourhood( nReal - nGrow, bTst ));
+
+ if ( IsFootnoteFrame() && (nGrow != nReal) && GetNext() )
+ {
+ //Footnotes can replace their successor.
+ SwTwips nSpace = bTst ? 0 : -nDist;
+ const SwFrame *pFrame = GetUpper()->Lower();
+ do
+ { nSpace += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ } while ( pFrame != GetNext() );
+ nSpace = aRectFnSet.GetHeight(GetUpper()->getFramePrintArea()) -nSpace;
+ if ( nSpace < 0 )
+ nSpace = 0;
+ nSpace += nGrow;
+ if ( nReal > nSpace )
+ nReal = nSpace;
+ if ( nReal && !bTst )
+ static_cast<SwFootnoteFrame*>(this)->InvalidateNxtFootnoteCnts( FindPageFrame() );
+ }
+ else
+ nReal = nGrow;
+ }
+ }
+ else
+ nReal = 0;
+
+ nReal += nMin;
+ }
+ else
+ nReal = nDist;
+
+ if ( !bTst )
+ {
+ if( nReal != nDist &&
+ // NEW TABLES
+ ( !IsCellFrame() || static_cast<SwCellFrame*>(this)->GetLayoutRowSpan() > 1 ) )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight + nReal );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().setX( nFramePos - nReal );
+ }
+
+ bMoveAccFrame = true;
+ }
+
+ if ( nReal )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ if (GetNext()->IsRowFrame())
+ { // also invalidate first cell
+ static_cast<SwLayoutFrame*>(GetNext())->Lower()->InvalidatePos_();
+ }
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ }
+ if ( !IsPageBodyFrame() )
+ {
+ InvalidateAll_();
+ InvalidatePage( pPage );
+ }
+ if (!(GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
+ NotifyLowerObjs();
+
+ if( IsCellFrame() )
+ InvaPercentLowers( nReal );
+
+ std::unique_ptr<SvxBrushItem> aBack(GetFormat()->makeBackgroundBrushItem());
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( GPOS_NONE != ePos && GPOS_TILED != ePos )
+ SetCompletePaint();
+ }
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( bMoveAccFrame && IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
+ }
+ }
+#else
+ (void)bMoveAccFrame;
+ (void)aOldFrame;
+#endif
+
+ return nReal;
+}
+
+SwTwips SwLayoutFrame::ShrinkFrame( SwTwips nDist, bool bTst, bool bInfo )
+{
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowse = pSh && pSh->GetViewOptions()->getBrowseMode();
+ SwFrameType nTmpType = SwFrameType::Cell | SwFrameType::Column;
+ if (bBrowse)
+ nTmpType |= SwFrameType::Body;
+
+ if (pSh && pSh->GetViewOptions()->IsWhitespaceHidden())
+ {
+ if (IsBodyFrame())
+ {
+ // Whitespace is hidden and this body frame will not shrink, as it
+ // has a fix size.
+ // Invalidate the page frame size, so in case the reason for the
+ // shrink was that there is more whitespace on this page, the size
+ // without whitespace will be recalculated correctly.
+ SwPageFrame* pPageFrame = FindPageFrame();
+ pPageFrame->InvalidateSize();
+ }
+ }
+
+ if( !(GetType() & nTmpType) && HasFixSize() )
+ return 0;
+
+ OSL_ENSURE( nDist >= 0, "nDist < 0" );
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nDist > nFrameHeight )
+ nDist = nFrameHeight;
+
+ SwTwips nMin = 0;
+ bool bChgPos = IsVertical();
+ if ( Lower() )
+ {
+ if( !Lower()->IsNeighbourFrame() )
+ { const SwFrame *pFrame = Lower();
+ const tools::Long nTmp = aRectFnSet.GetHeight(getFramePrintArea());
+ while( pFrame && nMin < nTmp )
+ { nMin += aRectFnSet.GetHeight(pFrame->getFrameArea());
+ pFrame = pFrame->GetNext();
+ }
+ }
+ }
+ SwTwips nReal = nDist;
+ SwTwips nMinDiff = aRectFnSet.GetHeight(getFramePrintArea()) - nMin;
+ if( nReal > nMinDiff )
+ nReal = nMinDiff;
+ if( nReal <= 0 )
+ return nDist;
+
+ SwRect aOldFrame( getFrameArea() );
+ bool bMoveAccFrame = false;
+
+ SwTwips nRealDist = nReal;
+ if ( !bTst )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, nFrameHeight - nReal );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nReal );
+ }
+
+ bMoveAccFrame = true;
+ }
+
+ SwNeighbourAdjust nAdjust = GetUpper() && GetUpper()->IsFootnoteBossFrame() ?
+ static_cast<SwFootnoteBossFrame*>(GetUpper())->NeighbourhoodAdjustment()
+ : SwNeighbourAdjust::GrowShrink;
+
+ // AdjustNeighbourhood also in columns (but not in frames)
+ if( SwNeighbourAdjust::OnlyAdjust == nAdjust )
+ {
+ if ( IsPageBodyFrame() && !bBrowse )
+ nReal = nDist;
+ else
+ { nReal = AdjustNeighbourhood( -nReal, bTst );
+ nReal *= -1;
+ if ( !bTst && IsBodyFrame() && nReal < nRealDist )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nRealDist - nReal );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nRealDist - nReal );
+ }
+
+ OSL_ENSURE( !IsAccessibleFrame(), "bMoveAccFrame has to be set!" );
+ }
+ }
+ }
+ else if( IsColumnFrame() || IsColBodyFrame() )
+ {
+ SwTwips nTmp = GetUpper()->Shrink( nReal, bTst, bInfo );
+ if ( nTmp != nReal )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.SetHeight( aFrm, aRectFnSet.GetHeight(aFrm) + nReal - nTmp );
+
+ if( bChgPos && !IsVertLR() )
+ {
+ aFrm.Pos().AdjustX(nTmp - nReal );
+ }
+
+ OSL_ENSURE( !IsAccessibleFrame(), "bMoveAccFrame has to be set!" );
+ nReal = nTmp;
+ }
+ }
+ else
+ {
+ SwTwips nShrink = nReal;
+ SwFrame* pToShrink = GetUpper();
+ // NEW TABLES
+ if ( IsCellFrame() )
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(this);
+ if ( pThisCell->GetLayoutRowSpan() > 1 )
+ {
+ SwCellFrame& rEndCell = const_cast<SwCellFrame&>(pThisCell->FindStartEndOfRowSpanCell( false ));
+ pToShrink = rEndCell.GetUpper();
+ }
+ }
+
+ nReal = pToShrink ? pToShrink->Shrink( nShrink, bTst, bInfo ) : 0;
+ if( ( SwNeighbourAdjust::GrowAdjust == nAdjust || SwNeighbourAdjust::AdjustGrow == nAdjust )
+ && nReal < nShrink )
+ AdjustNeighbourhood( nReal - nShrink );
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( bMoveAccFrame && IsAccessibleFrame() )
+ {
+ SwRootFrame *pRootFrame = getRootFrame();
+ if( pRootFrame && pRootFrame->IsAnyShellAccessible() &&
+ pRootFrame->GetCurrShell() )
+ {
+ pRootFrame->GetCurrShell()->Imp()->MoveAccessibleFrame( this, aOldFrame );
+ }
+ }
+#else
+ (void)aOldFrame;
+ (void)bMoveAccFrame;
+#endif
+
+ if ( !bTst && (IsCellFrame() || IsColumnFrame() ? nReal : nRealDist) )
+ {
+ SwPageFrame *pPage = FindPageFrame();
+ if ( GetNext() )
+ {
+ GetNext()->InvalidatePos_();
+ if ( GetNext()->IsContentFrame() )
+ GetNext()->InvalidatePage( pPage );
+ if ( IsTabFrame() )
+ static_cast<SwTabFrame*>(this)->SetComplete();
+ }
+ else
+ { if ( IsRetoucheFrame() )
+ SetRetouche();
+ if ( IsTabFrame() )
+ {
+ static_cast<SwTabFrame*>(this)->SetComplete();
+ if ( Lower() ) // Can also be in the Join and be empty!
+ InvalidateNextPos();
+ }
+ }
+ if ( !IsBodyFrame() )
+ {
+ InvalidateAll_();
+ InvalidatePage( pPage );
+ bool bCompletePaint = true;
+ const SwFrameFormat* pFormat = GetFormat();
+ if (pFormat)
+ {
+ std::unique_ptr<SvxBrushItem> aBack(pFormat->makeBackgroundBrushItem());
+ const SvxGraphicPosition ePos = aBack->GetGraphicPos();
+ if ( GPOS_NONE == ePos || GPOS_TILED == ePos )
+ bCompletePaint = false;
+ }
+ if (bCompletePaint)
+ SetCompletePaint();
+ }
+
+ if (!(GetType() & (SwFrameType::Row|SwFrameType::Tab|SwFrameType::FtnCont|SwFrameType::Page|SwFrameType::Root)))
+ NotifyLowerObjs();
+
+ if( IsCellFrame() )
+ InvaPercentLowers( nReal );
+
+ SwContentFrame *pCnt;
+ if( IsFootnoteFrame() && !static_cast<SwFootnoteFrame*>(this)->GetAttr()->GetFootnote().IsEndNote() &&
+ ( GetFormat()->GetDoc()->GetFootnoteInfo().m_ePos != FTNPOS_CHAPTER ||
+ ( IsInSct() && FindSctFrame()->IsFootnoteAtEnd() ) ) &&
+ nullptr != (pCnt = static_cast<SwFootnoteFrame*>(this)->GetRefFromAttr() ) )
+ {
+ if ( pCnt->IsFollow() )
+ { // If we are in another column/page than the frame with the
+ // reference, we don't need to invalidate its master.
+ SwFrame *pTmp = pCnt->FindFootnoteBossFrame(true) == FindFootnoteBossFrame(true)
+ ? &pCnt->FindMaster()->GetFrame() : pCnt;
+ pTmp->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pTmp->InvalidateSize();
+ }
+ else
+ {
+ if (pCnt->FindPageFrame() == FindPageFrame())
+ {
+ pCnt->InvalidatePos();
+ }
+ else
+ {
+ SAL_WARN("sw.layout", "footnote frame on different page than ref frame?");
+ }
+ }
+ }
+ }
+ return nReal;
+}
+
+/**
+ * Changes the size of the directly subsidiary Frame's that have a fixed size, proportionally to the
+ * size change of the PrtArea of the Frame's.
+ *
+ * The variable Frames are also proportionally adapted; they will grow/shrink again by themselves.
+ */
+void SwLayoutFrame::ChgLowersProp( const Size& rOldSize )
+{
+ // no change of lower properties for root frame or if no lower exists.
+ if ( IsRootFrame() || !Lower() )
+ return;
+
+ // declare and init <SwFrame* pLowerFrame> with first lower
+ SwFrame *pLowerFrame = Lower();
+
+ // declare and init const booleans <bHeightChgd> and <bWidthChg>
+ const bool bHeightChgd = rOldSize.Height() != getFramePrintArea().Height();
+ const bool bWidthChgd = rOldSize.Width() != getFramePrintArea().Width();
+
+ SwRectFnSet aRectFnSet(this);
+
+ // This shortcut basically tries to handle only lower frames that
+ // are affected by the size change. Otherwise much more lower frames
+ // are invalidated.
+ if ( !( aRectFnSet.IsVert() ? bHeightChgd : bWidthChgd ) &&
+ ! Lower()->IsColumnFrame() &&
+ ( ( IsBodyFrame() && IsInDocBody() && ( !IsInSct() || !FindSctFrame()->IsColLocked() ) ) ||
+ // #i10826# Section frames without columns should not
+ // invalidate all lowers!
+ IsSctFrame() ) )
+ {
+ // Determine page frame the body frame resp. the section frame belongs to.
+ SwPageFrame *pPage = FindPageFrame();
+ // Determine last lower by traveling through them using <GetNext()>.
+ // During travel check each section frame, if it will be sized to
+ // maximum. If Yes, invalidate size of section frame and set
+ // corresponding flags at the page.
+ do
+ {
+ if( pLowerFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pLowerFrame)->ToMaximize_() )
+ {
+ pLowerFrame->InvalidateSize_();
+ pLowerFrame->InvalidatePage( pPage );
+ }
+ if( pLowerFrame->GetNext() )
+ pLowerFrame = pLowerFrame->GetNext();
+ else
+ break;
+ } while( true );
+ // If found last lower is a section frame containing no section
+ // (section frame isn't valid and will be deleted in the future),
+ // travel backwards.
+ while( pLowerFrame->IsSctFrame() && !static_cast<SwSectionFrame*>(pLowerFrame)->GetSection() &&
+ pLowerFrame->GetPrev() )
+ pLowerFrame = pLowerFrame->GetPrev();
+ // If found last lower is a section frame, set <pLowerFrame> to its last
+ // content, if the section frame is valid and is not sized to maximum.
+ // Otherwise set <pLowerFrame> to NULL - In this case body frame only
+ // contains invalid section frames.
+ if( pLowerFrame->IsSctFrame() )
+ pLowerFrame = static_cast<SwSectionFrame*>(pLowerFrame)->GetSection() &&
+ !static_cast<SwSectionFrame*>(pLowerFrame)->ToMaximize( false ) ?
+ static_cast<SwSectionFrame*>(pLowerFrame)->FindLastContent() : nullptr;
+
+ // continue with found last lower, probably the last content of a section
+ if ( pLowerFrame )
+ {
+ // If <pLowerFrame> is in a table frame, set <pLowerFrame> to this table
+ // frame and continue.
+ if ( pLowerFrame->IsInTab() )
+ {
+ // OD 28.10.2002 #97265# - safeguard for setting <pLowerFrame> to
+ // its table frame - check, if the table frame is also a lower
+ // of the body frame, in order to assure that <pLowerFrame> is not
+ // set to a frame, which is an *upper* of the body frame.
+ SwFrame* pTableFrame = pLowerFrame->FindTabFrame();
+ if ( IsAnLower( pTableFrame ) )
+ {
+ pLowerFrame = pTableFrame;
+ }
+ }
+ // Check, if variable size of body frame resp. section frame has grown
+ // OD 28.10.2002 #97265# - correct check, if variable size has grown.
+ SwTwips nOldHeight = aRectFnSet.IsVert() ? rOldSize.Width() : rOldSize.Height();
+ if( nOldHeight < aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ // If variable size of body|section frame has grown, only found
+ // last lower and the position of the its next have to be invalidated.
+ pLowerFrame->InvalidateAll_();
+ pLowerFrame->InvalidatePage( pPage );
+ if( !pLowerFrame->IsFlowFrame() ||
+ !SwFlowFrame::CastFlowFrame( pLowerFrame )->HasFollow() )
+ pLowerFrame->InvalidateNextPos( true );
+ if ( pLowerFrame->IsTextFrame() )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ else
+ {
+ // variable size of body|section frame has shrunk. Thus,
+ // invalidate all lowers not matching the new body|section size
+ // and the dedicated new last lower.
+ if( aRectFnSet.IsVert() )
+ {
+ SwTwips nBot = getFrameArea().Left() + getFramePrintArea().Left();
+ while ( pLowerFrame && pLowerFrame->GetPrev() && pLowerFrame->getFrameArea().Left() < nBot )
+ {
+ pLowerFrame->InvalidateAll_();
+ pLowerFrame->InvalidatePage( pPage );
+ pLowerFrame = pLowerFrame->GetPrev();
+ }
+ }
+ else
+ {
+ SwTwips nBot = getFrameArea().Top() + getFramePrintArea().Bottom();
+ while ( pLowerFrame && pLowerFrame->GetPrev() && pLowerFrame->getFrameArea().Top() > nBot )
+ {
+ pLowerFrame->InvalidateAll_();
+ pLowerFrame->InvalidatePage( pPage );
+ pLowerFrame = pLowerFrame->GetPrev();
+ }
+ }
+ if ( pLowerFrame )
+ {
+ pLowerFrame->InvalidateSize_();
+ pLowerFrame->InvalidatePage( pPage );
+ if ( pLowerFrame->IsTextFrame() )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ }
+ // #i41694# - improvement by removing duplicates
+ if ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsInSct() )
+ {
+ // #i41694# - follow-up of issue #i10826#
+ // No invalidation of section frame, if it's the this.
+ SwFrame* pSectFrame = pLowerFrame->FindSctFrame();
+ if( pSectFrame != this && IsAnLower( pSectFrame ) )
+ {
+ pSectFrame->InvalidateSize_();
+ pSectFrame->InvalidatePage( pPage );
+ }
+ }
+ }
+ }
+ return;
+ } // end of { special case }
+
+ // Invalidate page for content only once.
+ bool bInvaPageForContent = true;
+
+ // Declare booleans <bFixChgd> and <bVarChgd>, indicating for text frame
+ // adjustment, if fixed/variable size has changed.
+ bool bFixChgd, bVarChgd;
+ if( aRectFnSet.IsVert() == pLowerFrame->IsNeighbourFrame() )
+ {
+ bFixChgd = bWidthChgd;
+ bVarChgd = bHeightChgd;
+ }
+ else
+ {
+ bFixChgd = bHeightChgd;
+ bVarChgd = bWidthChgd;
+ }
+
+ // Declare const unsigned short <nFixWidth> and init it this frame types
+ // which has fixed width in vertical respectively horizontal layout.
+ // In vertical layout these are neighbour frames (cell and column frames),
+ // header frames and footer frames.
+ // In horizontal layout these are all frames, which aren't neighbour frames.
+ const SwFrameType nFixWidth = aRectFnSet.IsVert() ? (FRM_NEIGHBOUR | FRM_HEADFOOT)
+ : ~SwFrameType(FRM_NEIGHBOUR);
+
+ // Declare const unsigned short <nFixHeight> and init it this frame types
+ // which has fixed height in vertical respectively horizontal layout.
+ // In vertical layout these are all frames, which aren't neighbour frames,
+ // header frames, footer frames, body frames or foot note container frames.
+ // In horizontal layout these are neighbour frames.
+ const SwFrameType nFixHeight = aRectFnSet.IsVert() ? ~SwFrameType(FRM_NEIGHBOUR | FRM_HEADFOOT | FRM_BODYFTNC)
+ : FRM_NEIGHBOUR;
+
+ // Travel through all lowers using <GetNext()>
+ while ( pLowerFrame )
+ {
+ if ( pLowerFrame->IsTextFrame() )
+ {
+ // Text frames will only be invalidated - prepare invalidation
+ if ( bFixChgd )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::FixSizeChanged );
+ if ( bVarChgd )
+ static_cast<SwContentFrame*>(pLowerFrame)->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ }
+ else
+ {
+ // If lower isn't a table, row, cell or section frame, adjust its
+ // frame size.
+ const SwFrameType nLowerType = pLowerFrame->GetType();
+ if ( !(nLowerType & (SwFrameType::Tab|SwFrameType::Row|SwFrameType::Cell|SwFrameType::Section)) )
+ {
+ if ( bWidthChgd )
+ {
+ if( nLowerType & nFixWidth )
+ {
+ // Considering previous conditions:
+ // In vertical layout set width of column, header and
+ // footer frames to its upper width.
+ // In horizontal layout set width of header, footer,
+ // foot note container, foot note, body and no-text
+ // frames to its upper width.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Width( getFramePrintArea().Width() );
+ }
+ else if( rOldSize.Width() && !pLowerFrame->IsFootnoteFrame() )
+ {
+ // Adjust frame width proportional, if lower isn't a
+ // foot note frame and condition <nLowerType & nFixWidth>
+ // isn't true.
+ // Considering previous conditions:
+ // In vertical layout these are foot note container,
+ // body and no-text frames.
+ // In horizontal layout these are column and no-text frames.
+ // OD 24.10.2002 #97265# - <double> calculation
+ // Perform <double> calculation of new width, if
+ // one of the coefficients is greater than 50000
+ SwTwips nNewWidth;
+ if ( (pLowerFrame->getFrameArea().Width() > 50000) ||
+ (getFramePrintArea().Width() > 50000) )
+ {
+ double nNewWidthTmp =
+ ( double(pLowerFrame->getFrameArea().Width())
+ * double(getFramePrintArea().Width()) )
+ / double(rOldSize.Width());
+ nNewWidth = SwTwips(nNewWidthTmp);
+ }
+ else
+ {
+ nNewWidth =
+ (pLowerFrame->getFrameArea().Width() * getFramePrintArea().Width()) / rOldSize.Width();
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Width( nNewWidth );
+ }
+ }
+ if ( bHeightChgd )
+ {
+ if( nLowerType & nFixHeight )
+ {
+ // Considering previous conditions:
+ // In vertical layout set height of foot note and
+ // no-text frames to its upper height.
+ // In horizontal layout set height of column frames
+ // to its upper height.
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Height( getFramePrintArea().Height() );
+ }
+ // OD 01.10.2002 #102211#
+ // add conditions <!pLowerFrame->IsHeaderFrame()> and
+ // <!pLowerFrame->IsFooterFrame()> in order to avoid that
+ // the <Grow> of header or footer are overwritten.
+ // NOTE: Height of header/footer frame is determined by contents.
+ else if ( rOldSize.Height() &&
+ !pLowerFrame->IsFootnoteFrame() &&
+ !pLowerFrame->IsHeaderFrame() &&
+ !pLowerFrame->IsFooterFrame()
+ )
+ {
+ // Adjust frame height proportional, if lower isn't a
+ // foot note, a header or a footer frame and
+ // condition <nLowerType & nFixHeight> isn't true.
+ // Considering previous conditions:
+ // In vertical layout these are column, foot note container,
+ // body and no-text frames.
+ // In horizontal layout these are column, foot note
+ // container, body and no-text frames.
+
+ // OD 29.10.2002 #97265# - special case for page lowers
+ // The page lowers that have to be adjusted on page height
+ // change are the body frame and the foot note container
+ // frame.
+ // In vertical layout the height of both is directly
+ // adjusted to the page height change.
+ // In horizontal layout the height of the body frame is
+ // directly adjusted to the page height change and the
+ // foot note frame height isn't touched, because its
+ // determined by its content.
+ // OD 31.03.2003 #108446# - apply special case for page
+ // lowers - see description above - also for section columns.
+ if ( IsPageFrame() ||
+ ( IsColumnFrame() && IsInSct() )
+ )
+ {
+ OSL_ENSURE( pLowerFrame->IsBodyFrame() || pLowerFrame->IsFootnoteContFrame(),
+ "ChgLowersProp - only for body or foot note container" );
+ if ( pLowerFrame->IsBodyFrame() || pLowerFrame->IsFootnoteContFrame() )
+ {
+ if ( IsVertical() || pLowerFrame->IsBodyFrame() )
+ {
+ SwTwips nNewHeight =
+ pLowerFrame->getFrameArea().Height() +
+ ( getFramePrintArea().Height() - rOldSize.Height() );
+ if ( nNewHeight < 0)
+ {
+ // OD 01.04.2003 #108446# - adjust assertion condition and text
+ OSL_ENSURE( !( IsPageFrame() &&
+ (pLowerFrame->getFrameArea().Height()>0) &&
+ (pLowerFrame->isFrameAreaDefinitionValid()) ),
+ "ChgLowersProg - negative height for lower.");
+ nNewHeight = 0;
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Height( nNewHeight );
+ }
+ }
+ }
+ else
+ {
+ SwTwips nNewHeight;
+ // OD 24.10.2002 #97265# - <double> calculation
+ // Perform <double> calculation of new height, if
+ // one of the coefficients is greater than 50000
+ if ( (pLowerFrame->getFrameArea().Height() > 50000) ||
+ (getFramePrintArea().Height() > 50000) )
+ {
+ double nNewHeightTmp =
+ ( double(pLowerFrame->getFrameArea().Height())
+ * double(getFramePrintArea().Height()) )
+ / double(rOldSize.Height());
+ nNewHeight = SwTwips(nNewHeightTmp);
+ }
+ else
+ {
+ nNewHeight = ( pLowerFrame->getFrameArea().Height()
+ * getFramePrintArea().Height() ) / rOldSize.Height();
+ }
+ if( !pLowerFrame->GetNext() )
+ {
+ SwTwips nSum = getFramePrintArea().Height();
+ SwFrame* pTmp = Lower();
+ while( pTmp->GetNext() )
+ {
+ if( !pTmp->IsFootnoteContFrame() || !pTmp->IsVertical() )
+ nSum -= pTmp->getFrameArea().Height();
+ pTmp = pTmp->GetNext();
+ }
+ if( nSum - nNewHeight == 1 &&
+ nSum == pLowerFrame->getFrameArea().Height() )
+ nNewHeight = nSum;
+ }
+
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pLowerFrame);
+ aFrm.Height( nNewHeight );
+ }
+ }
+ }
+ }
+ } // end of else { NOT text frame }
+
+ pLowerFrame->InvalidateAll_();
+ if ( bInvaPageForContent && pLowerFrame->IsContentFrame() )
+ {
+ pLowerFrame->InvalidatePage();
+ bInvaPageForContent = false;
+ }
+
+ if ( !pLowerFrame->GetNext() && pLowerFrame->IsRetoucheFrame() )
+ {
+ //If a growth took place and the subordinate elements can retouch
+ //itself (currently Tabs, Sections and Content) we trigger it.
+ if ( rOldSize.Height() < getFramePrintArea().SSize().Height() ||
+ rOldSize.Width() < getFramePrintArea().SSize().Width() )
+ pLowerFrame->SetRetouche();
+ }
+ pLowerFrame = pLowerFrame->GetNext();
+ }
+
+ // Finally adjust the columns if width is set to auto
+ // Possible optimization: execute this code earlier in this function and
+ // return???
+ if ( !(( (aRectFnSet.IsVert() && bHeightChgd) || (! aRectFnSet.IsVert() && bWidthChgd) ) &&
+ Lower()->IsColumnFrame()) )
+ return;
+
+ // get column attribute
+ const SwFormatCol* pColAttr = nullptr;
+ if ( IsPageBodyFrame() )
+ {
+ OSL_ENSURE( GetUpper()->IsPageFrame(), "Upper is not page frame" );
+ pColAttr = &GetUpper()->GetFormat()->GetCol();
+ }
+ else
+ {
+ OSL_ENSURE( IsFlyFrame() || IsSctFrame(), "Columns not in fly or section" );
+ pColAttr = &GetFormat()->GetCol();
+ }
+
+ if ( pColAttr->IsOrtho() && pColAttr->GetNumCols() > 1 )
+ AdjustColumns( pColAttr, false );
+}
+
+/** "Formats" the Frame; Frame and PrtArea.
+ *
+ * The Fixsize is not set here.
+ */
+void SwLayoutFrame::Format( vcl::RenderContext* /*pRenderContext*/, const SwBorderAttrs *pAttrs )
+{
+ OSL_ENSURE( pAttrs, "LayoutFrame::Format, pAttrs is 0." );
+
+ if ( isFramePrintAreaValid() && isFrameAreaSizeValid() )
+ return;
+
+ bool bHideWhitespace = false;
+ if (IsPageFrame())
+ {
+ SwViewShell* pShell = getRootFrame()->GetCurrShell();
+ if (pShell && pShell->GetViewOptions()->IsWhitespaceHidden())
+ {
+ // This is needed so that no space is reserved for the margin on
+ // the last page of the document. Other pages would have no margin
+ // set even without this, as their frame height is the content
+ // height already.
+ bHideWhitespace = true;
+ }
+ }
+
+ const sal_uInt16 nLeft = o3tl::narrowing<sal_uInt16>(pAttrs->CalcLeft(this));
+ const sal_uInt16 nUpper = bHideWhitespace ? 0 : pAttrs->CalcTop();
+
+ const sal_uInt16 nRight = o3tl::narrowing<sal_uInt16>(pAttrs->CalcRight(this));
+ const sal_uInt16 nLower = bHideWhitespace ? 0 : pAttrs->CalcBottom();
+
+ const bool bVert = IsVertical() && !IsPageFrame();
+ SwRectFn fnRect = bVert ? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T : fnRectVertL2R) : fnRectVert ) : fnRectHori;
+ if ( !isFramePrintAreaValid() )
+ {
+ setFramePrintAreaValid(true);
+ (this->*fnRect->fnSetXMargins)( nLeft, nRight );
+ (this->*fnRect->fnSetYMargins)( nUpper, nLower );
+ }
+
+ if ( isFrameAreaSizeValid() )
+ return;
+
+ if ( !HasFixSize() )
+ {
+ const SwTwips nBorder = nUpper + nLower;
+ const SwFormatFrameSize &rSz = GetFormat()->GetFrameSize();
+ SwTwips nMinHeight = rSz.GetHeightSizeType() == SwFrameSize::Minimum ? rSz.GetHeight() : 0;
+ do
+ {
+ setFrameAreaSizeValid(true);
+
+ //The size in VarSize is calculated using the content plus the
+ // borders.
+ SwTwips nRemaining = 0;
+ SwFrame *pFrame = Lower();
+ while ( pFrame )
+ { nRemaining += (pFrame->getFrameArea().*fnRect->fnGetHeight)();
+ if( pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsUndersized() )
+ // This TextFrame would like to be a bit bigger
+ nRemaining += static_cast<SwTextFrame*>(pFrame)->GetParHeight()
+ - (pFrame->getFramePrintArea().*fnRect->fnGetHeight)();
+ else if( pFrame->IsSctFrame() && static_cast<SwSectionFrame*>(pFrame)->IsUndersized() )
+ nRemaining += static_cast<SwSectionFrame*>(pFrame)->Undersize();
+ pFrame = pFrame->GetNext();
+ }
+ nRemaining += nBorder;
+ nRemaining = std::max( nRemaining, nMinHeight );
+ const SwTwips nDiff = nRemaining-(getFrameArea().*fnRect->fnGetHeight)();
+ const tools::Long nOldLeft = (getFrameArea().*fnRect->fnGetLeft)();
+ const tools::Long nOldTop = (getFrameArea().*fnRect->fnGetTop)();
+ if ( nDiff )
+ {
+ if ( nDiff > 0 )
+ Grow( nDiff );
+ else
+ Shrink( -nDiff );
+ //Updates the positions using the fast channel.
+ MakePos();
+ }
+ //Don't exceed the bottom edge of the Upper.
+ if ( GetUpper() && (getFrameArea().*fnRect->fnGetHeight)() )
+ {
+ const SwTwips nLimit = (GetUpper()->*fnRect->fnGetPrtBottom)();
+ if( (this->*fnRect->fnSetLimit)( nLimit ) &&
+ nOldLeft == (getFrameArea().*fnRect->fnGetLeft)() &&
+ nOldTop == (getFrameArea().*fnRect->fnGetTop)() )
+ {
+ setFrameAreaSizeValid(true);
+ setFramePrintAreaValid(true);
+ }
+ }
+ } while ( !isFrameAreaSizeValid() );
+ }
+ else if (GetType() & FRM_HEADFOOT)
+ {
+ do
+ { if ( getFrameArea().Height() != pAttrs->GetSize().Height() )
+ {
+ ChgSize( Size( getFrameArea().Width(), pAttrs->GetSize().Height()));
+ }
+
+ setFrameAreaSizeValid(true);
+ MakePos();
+ } while ( !isFrameAreaSizeValid() );
+ }
+ else
+ {
+ setFrameAreaSizeValid(true);
+ }
+
+ // While updating the size, PrtArea might be invalidated.
+ if (!isFramePrintAreaValid())
+ {
+ setFramePrintAreaValid(true);
+ (this->*fnRect->fnSetXMargins)(nLeft, nRight);
+ (this->*fnRect->fnSetYMargins)(nUpper, nLower);
+ }
+}
+
+static void InvaPercentFlys( SwFrame *pFrame, SwTwips nDiff )
+{
+ OSL_ENSURE( pFrame->GetDrawObjs(), "Can't find any Objects" );
+ for (SwAnchoredObject* pAnchoredObj : *pFrame->GetDrawObjs())
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ const SwFormatFrameSize &rSz = pFly->GetFormat()->GetFrameSize();
+ if ( rSz.GetWidthPercent() || rSz.GetHeightPercent() )
+ {
+ bool bNotify = true;
+ // If we've a fly with more than 90% relative height...
+ if( rSz.GetHeightPercent() > 90 && pFly->GetAnchorFrame() &&
+ rSz.GetHeightPercent() != SwFormatFrameSize::SYNCED && nDiff )
+ {
+ const SwFrame *pRel = pFly->IsFlyLayFrame() ? pFly->GetAnchorFrame():
+ pFly->GetAnchorFrame()->GetUpper();
+ // ... and we have already more than 90% height and we
+ // not allow the text to go through...
+ // then a notification could cause an endless loop, e.g.
+ // 100% height and no text wrap inside a cell of a table.
+ if( pFly->getFrameArea().Height()*10 >
+ ( nDiff + pRel->getFramePrintArea().Height() )*9 &&
+ pFly->GetFormat()->GetSurround().GetSurround() !=
+ css::text::WrapTextMode_THROUGH )
+ bNotify = false;
+ }
+ if( bNotify )
+ pFly->InvalidateSize();
+ }
+ }
+ }
+}
+
+void SwLayoutFrame::InvaPercentLowers( SwTwips nDiff )
+{
+ if ( GetDrawObjs() )
+ ::InvaPercentFlys( this, nDiff );
+
+ SwFrame *pFrame = ContainsContent();
+ if ( !pFrame )
+ return;
+
+ do
+ {
+ if ( pFrame->IsInTab() && !IsTabFrame() )
+ {
+ SwFrame *pTmp = pFrame->FindTabFrame();
+ OSL_ENSURE( pTmp, "Where's my TabFrame?" );
+ if( IsAnLower( pTmp ) )
+ pFrame = pTmp;
+ }
+
+ if ( pFrame->IsTabFrame() )
+ {
+ const SwFormatFrameSize &rSz = static_cast<SwLayoutFrame*>(pFrame)->GetFormat()->GetFrameSize();
+ if ( rSz.GetWidthPercent() || rSz.GetHeightPercent() )
+ pFrame->InvalidatePrt();
+ }
+ else if ( pFrame->GetDrawObjs() )
+ ::InvaPercentFlys( pFrame, nDiff );
+ pFrame = pFrame->FindNextCnt();
+ } while ( pFrame && IsAnLower( pFrame ) ) ;
+}
+
+tools::Long SwLayoutFrame::CalcRel( const SwFormatFrameSize &rSz ) const
+{
+ tools::Long nRet = rSz.GetWidth(),
+ nPercent = rSz.GetWidthPercent();
+
+ if ( nPercent )
+ {
+ const SwFrame *pRel = GetUpper();
+ tools::Long nRel = LONG_MAX;
+ const SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bBrowseMode = pSh && pSh->GetViewOptions()->getBrowseMode();
+ if( pRel->IsPageBodyFrame() && pSh && bBrowseMode && pSh->VisArea().Width() )
+ {
+ nRel = pSh->GetBrowseWidth();
+ tools::Long nDiff = nRel - pRel->getFramePrintArea().Width();
+ if ( nDiff > 0 )
+ nRel -= nDiff;
+ }
+ nRel = std::min( nRel, pRel->getFramePrintArea().Width() );
+ nRet = nRel * nPercent / 100;
+ }
+ return nRet;
+}
+
+// Local helpers for SwLayoutFrame::FormatWidthCols()
+
+static tools::Long lcl_CalcMinColDiff( SwLayoutFrame *pLayFrame )
+{
+ tools::Long nDiff = 0, nFirstDiff = 0;
+ SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(pLayFrame->Lower());
+ OSL_ENSURE( pCol, "Where's the columnframe?" );
+ SwFrame *pFrame = pCol->Lower();
+ do
+ {
+ if( pFrame && pFrame->IsBodyFrame() )
+ pFrame = static_cast<SwBodyFrame*>(pFrame)->Lower();
+ if ( pFrame && pFrame->IsTextFrame() )
+ {
+ const tools::Long nTmp = static_cast<SwTextFrame*>(pFrame)->FirstLineHeight();
+ if ( nTmp != USHRT_MAX )
+ {
+ if ( pCol == pLayFrame->Lower() )
+ nFirstDiff = nTmp;
+ else
+ nDiff = nDiff ? std::min( nDiff, nTmp ) : nTmp;
+ }
+ }
+ //Skip empty columns!
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ while ( pCol && nullptr == (pFrame = pCol->Lower()) )
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+
+ } while ( pFrame && pCol );
+
+ return nDiff ? nDiff : nFirstDiff ? nFirstDiff : 240;
+}
+
+static bool lcl_IsFlyHeightClipped( SwLayoutFrame *pLay )
+{
+ SwFrame *pFrame = pLay->ContainsContent();
+ while ( pFrame )
+ {
+ if ( pFrame->IsInTab() )
+ pFrame = pFrame->FindTabFrame();
+
+ if ( pFrame->GetDrawObjs() )
+ {
+ const size_t nCnt = pFrame->GetDrawObjs()->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pFrame->GetDrawObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsHeightClipped() &&
+ ( !pFly->IsFlyFreeFrame() || pFly->GetPageFrame() ) )
+ return true;
+ }
+ }
+ }
+ pFrame = pFrame->FindNextCnt();
+ }
+ return false;
+}
+
+void SwLayoutFrame::FormatWidthCols( const SwBorderAttrs &rAttrs,
+ const SwTwips nBorder, const SwTwips nMinHeight )
+{
+ //If there are columns involved, the size is adjusted using the last column.
+ //1. Format content.
+ //2. Calculate height of the last column: if it's too big, the Fly has to
+ // grow. The amount by which the Fly grows is not the amount of the
+ // overhang because we have to act on the assumption that some text flows
+ // back which will generate some more space.
+ // The amount which we grow by equals the overhang
+ // divided by the amount of columns or the overhang itself if it's smaller
+ // than the amount of columns.
+ //3. Go back to 1. until everything is stable.
+
+ const SwFormatCol &rCol = rAttrs.GetAttrSet().GetCol();
+ const sal_uInt16 nNumCols = rCol.GetNumCols();
+
+ bool bEnd = false;
+ bool bBackLock = false;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwViewShellImp *pImp = pSh ? pSh->Imp() : nullptr;
+ vcl::RenderContext* pRenderContext = pSh ? pSh->GetOut() : nullptr;
+ {
+ // Underlying algorithm
+ // We try to find the optimal height for the column.
+ // nMinimum starts with the passed minimum height and is then remembered
+ // as the maximum height on which column content still juts out of a
+ // column.
+ // nMaximum starts with LONG_MAX and is then remembered as the minimum
+ // width on which the content fitted.
+ // In column based sections nMaximum starts at the maximum value which
+ // the surrounding defines, this can certainly be a value on which
+ // content still juts out.
+ // The columns are formatted. If content still juts out, nMinimum is
+ // adjusted accordingly, then we grow, at least by uMinDiff but not
+ // over a certain nMaximum. If no content juts out but there is still
+ // some space left in the column, shrinking is done accordingly, at
+ // least by nMindIff but not below the nMinimum.
+ // Cancel as soon as no content juts out and the difference from minimum
+ // to maximum is less than MinDiff or the maximum which was defined by
+ // the surrounding is reached even if some content still juts out.
+
+ // Criticism of this implementation
+ // 1. Theoretically situations are possible in which the content fits in
+ // a lower height but not in a higher height. To ensure that the code
+ // handles such situations the code contains a few checks concerning
+ // minimum and maximum which probably are never triggered.
+ // 2. We use the same nMinDiff for shrinking and growing, but nMinDiff
+ // is more or less the smallest first line height and doesn't seem ideal
+ // as minimum value.
+
+ tools::Long nMinimum = nMinHeight;
+ tools::Long nMaximum;
+ bool bNoBalance = false;
+ SwRectFnSet aRectFnSet(this);
+ if( IsSctFrame() )
+ {
+ nMaximum = aRectFnSet.GetHeight(getFrameArea()) - nBorder +
+ aRectFnSet.BottomDist(getFrameArea(), aRectFnSet.GetPrtBottom(*GetUpper()));
+ nMaximum += GetUpper()->Grow( LONG_MAX, true );
+ if( nMaximum < nMinimum )
+ {
+ if( nMaximum < 0 )
+ nMinimum = nMaximum = 0;
+ else
+ nMinimum = nMaximum;
+ }
+ if( nMaximum > BROWSE_HEIGHT )
+ nMaximum = BROWSE_HEIGHT;
+
+ bNoBalance = static_cast<SwSectionFrame*>(this)->GetSection()->GetFormat()->
+ GetBalancedColumns().GetValue();
+ SwFrame* pAny = ContainsAny();
+ if( bNoBalance ||
+ ( !aRectFnSet.GetHeight(getFrameArea()) && pAny ) )
+ {
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+ // #i23129# - correction
+ // to the calculated maximum height.
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nMaximum - aRectFnSet.GetHeight(getFrameArea()) );
+ }
+
+ if( nTop > nMaximum )
+ nTop = nMaximum;
+ aRectFnSet.SetYMargins( *this, nTop, 0 );
+ }
+ if( !pAny && !static_cast<SwSectionFrame*>(this)->IsFootnoteLock() )
+ {
+ SwFootnoteContFrame* pFootnoteCont = static_cast<SwSectionFrame*>(this)->ContainsFootnoteCont();
+ if( pFootnoteCont )
+ {
+ SwFrame* pFootnoteAny = pFootnoteCont->ContainsAny();
+ if( pFootnoteAny && pFootnoteAny->isFrameAreaDefinitionValid() )
+ {
+ bBackLock = true;
+ static_cast<SwSectionFrame*>(this)->SetFootnoteLock( true );
+ }
+ }
+ }
+ }
+ else
+ nMaximum = LONG_MAX;
+
+ // #i3317# - reset temporarily consideration
+ // of wrapping style influence
+ SwPageFrame* pPageFrame = FindPageFrame();
+ SwSortedObjs* pObjs = pPageFrame ? pPageFrame->GetSortedObjs() : nullptr;
+ if ( pObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pObjs)
+ {
+ if ( IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ }
+ }
+ }
+ do
+ {
+ //Could take a while therefore check for Waitcrsr here.
+ if ( pImp )
+ pImp->CheckWaitCursor();
+
+ setFrameAreaSizeValid(true);
+ //First format the column as this will relieve the stack a bit.
+ //Also set width and height of the column (if they are wrong)
+ //while we are at it.
+ SwLayoutFrame *pCol = static_cast<SwLayoutFrame*>(Lower());
+
+ // #i27399#
+ // Simply setting the column width based on the values returned by
+ // CalcColWidth does not work for automatic column width.
+ AdjustColumns( &rCol, false );
+
+ for ( sal_uInt16 i = 0; i < nNumCols; ++i )
+ {
+ pCol->Calc(pRenderContext);
+ // ColumnFrames have a BodyFrame now, which needs to be calculated
+ pCol->Lower()->Calc(pRenderContext);
+ if( pCol->Lower()->GetNext() )
+ pCol->Lower()->GetNext()->Calc(pRenderContext); // SwFootnoteCont
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+
+ ::CalcContent( this );
+
+ pCol = static_cast<SwLayoutFrame*>(Lower());
+ OSL_ENSURE( pCol && pCol->GetNext(), ":-( column making holidays?");
+ // set bMinDiff if no empty columns exist
+ bool bMinDiff = true;
+ // OD 28.03.2003 #108446# - check for all column content and all columns
+ while ( bMinDiff && pCol )
+ {
+ bMinDiff = nullptr != pCol->ContainsContent();
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+ pCol = static_cast<SwLayoutFrame*>(Lower());
+ // OD 28.03.2003 #108446# - initialize local variable
+ SwTwips nDiff = 0;
+ SwTwips nMaxFree = 0;
+ SwTwips nAllFree = LONG_MAX;
+ // set bFoundLower if there is at least one non-empty column
+ bool bFoundLower = false;
+ while( pCol )
+ {
+ SwLayoutFrame* pLay = static_cast<SwLayoutFrame*>(pCol->Lower());
+ SwTwips nInnerHeight = aRectFnSet.GetHeight(pLay->getFrameArea()) -
+ aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ if( pLay->Lower() )
+ {
+ bFoundLower = true;
+ nInnerHeight += pLay->InnerHeight();
+ }
+ else if( nInnerHeight < 0 )
+ nInnerHeight = 0;
+
+ if( pLay->GetNext() )
+ {
+ bFoundLower = true;
+ pLay = static_cast<SwLayoutFrame*>(pLay->GetNext());
+ OSL_ENSURE( pLay->IsFootnoteContFrame(),"FootnoteContainer expected" );
+ nInnerHeight += pLay->InnerHeight();
+ nInnerHeight += aRectFnSet.GetHeight(pLay->getFrameArea()) -
+ aRectFnSet.GetHeight(pLay->getFramePrintArea());
+ }
+ nInnerHeight -= aRectFnSet.GetHeight(pCol->getFramePrintArea());
+ if( nInnerHeight > nDiff )
+ {
+ nDiff = nInnerHeight;
+ nAllFree = 0;
+ }
+ else
+ {
+ if( nMaxFree < -nInnerHeight )
+ nMaxFree = -nInnerHeight;
+ if( nAllFree > -nInnerHeight )
+ nAllFree = -nInnerHeight;
+ }
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+
+ if ( bFoundLower || ( IsSctFrame() && static_cast<SwSectionFrame*>(this)->HasFollow() ) )
+ {
+ SwTwips nMinDiff = ::lcl_CalcMinColDiff( this );
+ // Here we decide if growing is needed - this is the case, if
+ // column content (nDiff) or a Fly juts over.
+ // In sections with columns we take into account to set the size
+ // when having a non-empty Follow.
+ if ( nDiff || ::lcl_IsFlyHeightClipped( this ) ||
+ ( IsSctFrame() && static_cast<SwSectionFrame*>(this)->CalcMinDiff( nMinDiff ) ) )
+ {
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ // The minimum must not be smaller than our PrtHeight as
+ // long as something juts over.
+ if( nMinimum < nPrtHeight )
+ nMinimum = nPrtHeight;
+ // The maximum must not be smaller than PrtHeight if
+ // something still juts over.
+ if( nMaximum < nPrtHeight )
+ nMaximum = nPrtHeight; // Robust, but will this ever happen?
+ if( !nDiff ) // If only Flys jut over, we grow by nMinDiff
+ nDiff = nMinDiff;
+ // If we should grow more than by nMinDiff we split it over
+ // the columns
+ if ( std::abs(nDiff - nMinDiff) > nNumCols && nDiff > static_cast<tools::Long>(nNumCols) )
+ nDiff /= nNumCols;
+
+ if ( bMinDiff )
+ { // If no empty column exists, we want to grow at least
+ // by nMinDiff. Special case: If we are smaller than the
+ // minimal FrameHeight and PrtHeight is smaller than
+ // nMindiff we grow in a way that PrtHeight is exactly
+ // nMinDiff afterwards.
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ if ( nFrameHeight > nMinHeight || nPrtHeight >= nMinDiff )
+ nDiff = std::max( nDiff, nMinDiff );
+ else if( nDiff < nMinDiff )
+ nDiff = nMinDiff - nPrtHeight + 1;
+ }
+ // nMaximum has a size which fits the content or the
+ // requested value from the surrounding therefore we don't
+ // need to exceed this value.
+ if( nDiff + nPrtHeight > nMaximum )
+ nDiff = nMaximum - nPrtHeight;
+ }
+ else if( nMaximum > nMinimum ) // We fit, do we still have some margin?
+ {
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if ( nMaximum < nPrtHeight )
+ nDiff = nMaximum - nPrtHeight; // We grew over a working
+ // height and shrink back to it, but will this ever
+ // happen?
+ else
+ { // We have a new maximum, a size which fits for the content.
+ nMaximum = nPrtHeight;
+ // If the margin in the column is bigger than nMinDiff
+ // and we therefore drop under the minimum, we deflate
+ // a bit.
+ if ( !bNoBalance &&
+ // #i23129# - <nMinDiff> can be
+ // big, because of an object at the beginning of
+ // a column. Thus, decrease optimization here.
+ //nMaxFree >= nMinDiff &&
+ nMaxFree > 0 &&
+ ( !nAllFree ||
+ nMinimum < nPrtHeight - nMinDiff ) )
+ {
+ nMaxFree /= nNumCols; // disperse over the columns
+ nDiff = nMaxFree < nMinDiff ? -nMinDiff : -nMaxFree; // min nMinDiff
+ if( nPrtHeight + nDiff <= nMinimum ) // below the minimum?
+ nDiff = ( nMinimum - nMaximum ) / 2; // Take the center
+ }
+ else if( nAllFree )
+ {
+ nDiff = -nAllFree;
+ if( nPrtHeight + nDiff <= nMinimum ) // Less than minimum?
+ nDiff = ( nMinimum - nMaximum ) / 2; // Take the center
+ }
+ }
+ }
+ if( nDiff ) // now we shrink or grow...
+ {
+ Size aOldSz( getFramePrintArea().SSize() );
+ tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+ nDiff = aRectFnSet.GetHeight(getFramePrintArea()) + nDiff + nBorder - aRectFnSet.GetHeight(getFrameArea());
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nDiff );
+ }
+
+ // #i68520#
+ SwFlyFrame *pFlyFrame = IsFlyFrame() ? static_cast<SwFlyFrame*>(this) : nullptr;
+ if (pFlyFrame)
+ {
+ pFlyFrame->InvalidateObjRectWithSpaces();
+ }
+ aRectFnSet.SetYMargins( *this, nTop, nBorder - nTop );
+ ChgLowersProp( aOldSz );
+ NotifyLowerObjs();
+
+ // #i3317# - reset temporarily consideration
+ // of wrapping style influence
+ SwPageFrame* pTmpPageFrame = FindPageFrame();
+ SwSortedObjs* pTmpObjs = pTmpPageFrame ? pTmpPageFrame->GetSortedObjs() : nullptr;
+ if ( pTmpObjs )
+ {
+ for (SwAnchoredObject* pAnchoredObj : *pTmpObjs)
+ {
+ if ( IsAnLower( pAnchoredObj->GetAnchorFrame() ) )
+ {
+ pAnchoredObj->SetTmpConsiderWrapInfluence( false );
+ }
+ }
+ }
+ //Invalidate suitable to nicely balance the Frames.
+ //- Every first one after the second column gets a
+ // InvalidatePos();
+ pCol = static_cast<SwLayoutFrame*>(Lower()->GetNext());
+ while ( pCol )
+ {
+ SwFrame *pLow = pCol->Lower();
+ if ( pLow )
+ pLow->InvalidatePos_();
+ pCol = static_cast<SwLayoutFrame*>(pCol->GetNext());
+ }
+ if( IsSctFrame() && static_cast<SwSectionFrame*>(this)->HasFollow() )
+ {
+ // If we created a Follow, we need to give its content
+ // the opportunity to flow back inside the CalcContent
+ SwContentFrame* pTmpContent =
+ static_cast<SwSectionFrame*>(this)->GetFollow()->ContainsContent();
+ if( pTmpContent )
+ pTmpContent->InvalidatePos_();
+ }
+ }
+ else
+ bEnd = true;
+ }
+ else
+ bEnd = true;
+
+ } while ( !bEnd || !isFrameAreaSizeValid() );
+ }
+ // OD 01.04.2003 #108446# - Don't collect endnotes for sections. Thus, set
+ // 2nd parameter to <true>.
+ ::CalcContent( this, true );
+ if( IsSctFrame() )
+ {
+ // OD 14.03.2003 #i11760# - adjust 2nd parameter - sal_True --> true
+ setFrameAreaSizeValid(true);
+ ::CalcContent( this, true );
+ if( bBackLock )
+ static_cast<SwSectionFrame*>(this)->SetFootnoteLock( false );
+ }
+}
+
+static SwContentFrame* lcl_InvalidateSection( SwFrame *pCnt, SwInvalidateFlags nInv )
+{
+ SwSectionFrame* pSect = pCnt->FindSctFrame();
+ // If our ContentFrame is placed inside a table or a footnote, only sections
+ // which are also placed inside are meant.
+ // Exception: If a table is directly passed.
+ if( ( ( pCnt->IsInTab() && !pSect->IsInTab() ) ||
+ ( pCnt->IsInFootnote() && !pSect->IsInFootnote() ) ) && !pCnt->IsTabFrame() )
+ return nullptr;
+ if( nInv & SwInvalidateFlags::Size )
+ pSect->InvalidateSize_();
+ if( nInv & SwInvalidateFlags::Pos )
+ pSect->InvalidatePos_();
+ if( nInv & SwInvalidateFlags::PrtArea )
+ pSect->InvalidatePrt_();
+ SwFlowFrame *pFoll = pSect->GetFollow();
+ // Temporary separation from follow
+ pSect->SetFollow( nullptr );
+ SwContentFrame* pRet = pSect->FindLastContent();
+ pSect->SetFollow( pFoll );
+ return pRet;
+}
+
+static SwContentFrame* lcl_InvalidateTable( SwTabFrame *pTable, SwInvalidateFlags nInv )
+{
+ if( ( nInv & SwInvalidateFlags::Section ) && pTable->IsInSct() )
+ lcl_InvalidateSection( pTable, nInv );
+ if( nInv & SwInvalidateFlags::Size )
+ pTable->InvalidateSize_();
+ if( nInv & SwInvalidateFlags::Pos )
+ pTable->InvalidatePos_();
+ if( nInv & SwInvalidateFlags::PrtArea )
+ pTable->InvalidatePrt_();
+ return pTable->FindLastContent();
+}
+
+static void lcl_InvalidateAllContent( SwContentFrame *pCnt, SwInvalidateFlags nInv );
+
+static void lcl_InvalidateContent( SwContentFrame *pCnt, SwInvalidateFlags nInv )
+{
+ SwContentFrame *pLastTabCnt = nullptr;
+ SwContentFrame *pLastSctCnt = nullptr;
+ while ( pCnt )
+ {
+ if( nInv & SwInvalidateFlags::Section )
+ {
+ if( pCnt->IsInSct() )
+ {
+ // See above at tables
+ if( !pLastSctCnt )
+ pLastSctCnt = lcl_InvalidateSection( pCnt, nInv );
+ if( pLastSctCnt == pCnt )
+ pLastSctCnt = nullptr;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_ENSURE( !pLastSctCnt, "Where's the last SctContent?" );
+#endif
+ }
+ if( nInv & SwInvalidateFlags::Table )
+ {
+ if( pCnt->IsInTab() )
+ {
+ // To not call FindTabFrame() for each ContentFrame of a table and
+ // then invalidate the table, we remember the last ContentFrame of
+ // the table and ignore IsInTab() until we are past it.
+ // When entering the table, LastSctCnt is set to null, so
+ // sections inside the table are correctly invalidated.
+ // If the table itself is in a section the
+ // invalidation is done three times, which is acceptable.
+ if( !pLastTabCnt )
+ {
+ pLastTabCnt = lcl_InvalidateTable( pCnt->FindTabFrame(), nInv );
+ pLastSctCnt = nullptr;
+ }
+ if( pLastTabCnt == pCnt )
+ {
+ pLastTabCnt = nullptr;
+ pLastSctCnt = nullptr;
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ OSL_ENSURE( !pLastTabCnt, "Where's the last TabContent?" );
+#endif
+ }
+
+ if( nInv & SwInvalidateFlags::Size )
+ pCnt->Prepare( PrepareHint::Clear, nullptr, false );
+ if( nInv & SwInvalidateFlags::Pos )
+ pCnt->InvalidatePos_();
+ if( nInv & SwInvalidateFlags::PrtArea )
+ pCnt->InvalidatePrt_();
+ if ( nInv & SwInvalidateFlags::LineNum )
+ pCnt->InvalidateLineNum();
+ if ( pCnt->GetDrawObjs() )
+ lcl_InvalidateAllContent( pCnt, nInv );
+ pCnt = pCnt->GetNextContentFrame();
+ }
+}
+
+static void lcl_InvalidateAllContent( SwContentFrame *pCnt, SwInvalidateFlags nInv )
+{
+ SwSortedObjs &rObjs = *pCnt->GetDrawObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if ( pFly->IsFlyInContentFrame() )
+ {
+ ::lcl_InvalidateContent( pFly->ContainsContent(), nInv );
+ if( nInv & SwInvalidateFlags::Direction )
+ pFly->CheckDirChange();
+ }
+ }
+ }
+}
+
+void SwRootFrame::InvalidateAllContent( SwInvalidateFlags nInv )
+{
+ // First process all page bound FlyFrames.
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(Lower());
+ while( pPage )
+ {
+ pPage->InvalidateFlyLayout();
+ pPage->InvalidateFlyContent();
+ pPage->InvalidateFlyInCnt();
+ pPage->InvalidateLayout();
+ pPage->InvalidateContent();
+ pPage->InvalidatePage( pPage ); // So even the Turbo disappears if applicable
+
+ if ( pPage->GetSortedObjs() )
+ {
+ const SwSortedObjs &rObjs = *pPage->GetSortedObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ ::lcl_InvalidateContent( pFly->ContainsContent(), nInv );
+ if ( nInv & SwInvalidateFlags::Direction )
+ pFly->CheckDirChange();
+ }
+ }
+ }
+ if( nInv & SwInvalidateFlags::Direction )
+ pPage->CheckDirChange();
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ }
+
+ //Invalidate the whole document content and the character bound Flys here.
+ ::lcl_InvalidateContent( ContainsContent(), nInv );
+
+ if( nInv & SwInvalidateFlags::PrtArea )
+ {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh )
+ pSh->InvalidateWindows( getFrameArea() );
+ }
+}
+
+/**
+ * Invalidate/re-calculate the position of all floating screen objects (Writer fly frames and
+ * drawing objects), that are anchored to paragraph or to character. (2004-03-16 #i11860#)
+ */
+void SwRootFrame::InvalidateAllObjPos()
+{
+ const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>(Lower());
+ while( pPageFrame )
+ {
+ pPageFrame->InvalidateFlyLayout();
+
+ if ( pPageFrame->GetSortedObjs() )
+ {
+ const SwSortedObjs& rObjs = *(pPageFrame->GetSortedObjs());
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat().GetAnchor();
+ if ((rAnch.GetAnchorId() != RndStdIds::FLY_AT_PARA) &&
+ (rAnch.GetAnchorId() != RndStdIds::FLY_AT_CHAR))
+ {
+ // only to paragraph and to character anchored objects are considered.
+ continue;
+ }
+ // #i28701# - special invalidation for anchored
+ // objects, whose wrapping style influence has to be considered.
+ if ( pAnchoredObj->ConsiderObjWrapInfluenceOnObjPos() )
+ pAnchoredObj->InvalidateObjPosForConsiderWrapInfluence();
+ else
+ pAnchoredObj->InvalidateObjPos();
+ }
+ }
+
+ pPageFrame = static_cast<const SwPageFrame*>(pPageFrame->GetNext());
+ }
+}
+
+static void AddRemoveFlysForNode(
+ SwTextFrame & rFrame, SwTextNode & rTextNode,
+ std::set<SwNodeOffset> *const pSkipped,
+ const SwFrameFormats & rTable,
+ SwPageFrame *const pPage,
+ SwTextNode const*const pNode,
+ std::vector<sw::Extent>::const_iterator const& rIterFirst,
+ std::vector<sw::Extent>::const_iterator const& rIterEnd,
+ SwTextNode const*const pFirstNode, SwTextNode const*const pLastNode)
+{
+ if (pNode == &rTextNode)
+ { // remove existing hidden at-char anchored flys
+ RemoveHiddenObjsOfNode(rTextNode, &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
+ }
+ else if (rTextNode.GetIndex() < pNode->GetIndex())
+ {
+ // pNode's frame has been deleted by CheckParaRedlineMerge()
+ AppendObjsOfNode(&rTable,
+ pNode->GetIndex(), &rFrame, pPage, &rTextNode.GetDoc(),
+ &rIterFirst, &rIterEnd, pFirstNode, pLastNode);
+ if (pSkipped)
+ {
+ // if a fly has been added by AppendObjsOfNode, it must be skipped; if not, then it doesn't matter if it's skipped or not because it has no frames and because of that it would be skipped anyway
+ for (auto const pFly : pNode->GetAnchoredFlys())
+ {
+ if (pFly->Which() != RES_DRAWFRMFMT)
+ {
+ pSkipped->insert(pFly->GetContent().GetContentIdx()->GetIndex());
+ }
+ }
+ }
+ }
+}
+
+namespace sw {
+
+/// rTextNode is the first one of the "new" merge - if rTextNode isn't the same
+/// as MergedPara::pFirstNode, then nodes before rTextNode have their flys
+/// already properly attached, so only the other nodes need handling here.
+void AddRemoveFlysAnchoredToFrameStartingAtNode(
+ SwTextFrame & rFrame, SwTextNode & rTextNode,
+ std::set<SwNodeOffset> *const pSkipped)
+{
+ auto const pMerged(rFrame.GetMergedPara());
+ if (!pMerged
+ // do this only *once*, for the *last* frame
+ // otherwise AppendObj would create multiple frames for fly-frames!
+ || rFrame.GetFollow())
+ return;
+
+ assert(pMerged->pFirstNode->GetIndex() <= rTextNode.GetIndex()
+ && rTextNode.GetIndex() <= pMerged->pLastNode->GetIndex());
+ // add visible flys in non-first node to merged frame
+ // (hidden flys remain and are deleted via DelFrames())
+ SwFrameFormats& rTable(*rTextNode.GetDoc().GetSpzFrameFormats());
+ SwPageFrame *const pPage(rFrame.FindPageFrame());
+ std::vector<sw::Extent>::const_iterator iterFirst(pMerged->extents.begin());
+ std::vector<sw::Extent>::const_iterator iter(iterFirst);
+ SwTextNode const* pNode(pMerged->pFirstNode);
+ for ( ; ; ++iter)
+ {
+ if (iter == pMerged->extents.end()
+ || iter->pNode != pNode)
+ {
+ AddRemoveFlysForNode(rFrame, rTextNode, pSkipped, rTable, pPage,
+ pNode, iterFirst, iter,
+ pMerged->pFirstNode, pMerged->pLastNode);
+ SwNodeOffset const until = iter == pMerged->extents.end()
+ ? pMerged->pLastNode->GetIndex() + 1
+ : iter->pNode->GetIndex();
+ for (SwNodeOffset i = pNode->GetIndex() + 1; i < until; ++i)
+ {
+ // let's show at-para flys on nodes that contain start/end of
+ // redline too, even if there's no text there
+ SwNode const*const pTmp(pNode->GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+ AddRemoveFlysForNode(rFrame, rTextNode, pSkipped,
+ rTable, pPage, pTmp->GetTextNode(), iter, iter,
+ pMerged->pFirstNode, pMerged->pLastNode);
+ }
+ }
+ if (iter == pMerged->extents.end())
+ {
+ break;
+ }
+ pNode = iter->pNode;
+ iterFirst = iter;
+ }
+ }
+}
+
+} // namespace sw
+
+static void UnHideRedlines(SwRootFrame & rLayout,
+ SwNodes & rNodes, SwNode const& rEndOfSectionNode,
+ std::set<SwNodeOffset> *const pSkipped)
+{
+ assert(rEndOfSectionNode.IsEndNode());
+ assert(rNodes[rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1]->IsCreateFrameWhenHidingRedlines()); // first node is never hidden
+ for (SwNodeOffset i = rEndOfSectionNode.StartOfSectionNode()->GetIndex() + 1;
+ i < rEndOfSectionNode.GetIndex(); ++i)
+ {
+ SwNode & rNode(*rNodes[i]);
+ if (rNode.IsTextNode()) // only text nodes are 1st node of a merge
+ {
+ SwTextNode & rTextNode(*rNode.GetTextNode());
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTextNode);
+ std::vector<SwTextFrame*> frames;
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame() == &rLayout)
+ {
+ if (pFrame->IsFollow())
+ {
+ frames.push_back(pFrame);
+ } // when hiding, the loop must remove the anchored flys
+ else // *before* resetting SetMergedPara anywhere - else
+ { // the fly deletion code will access multiple of the
+ // frames with inconsistent MergedPara and assert
+ frames.insert(frames.begin(), pFrame);
+ }
+ }
+ }
+ // this messes with pRegisteredIn so do it outside SwIterator
+ auto eMode(sw::FrameMode::Existing);
+ for (SwTextFrame * pFrame : frames)
+ {
+ if (rLayout.HasMergedParas())
+ {
+ assert(!pFrame->GetMergedPara() ||
+ !rNode.IsCreateFrameWhenHidingRedlines() ||
+ // FIXME: skip this assert in tables with deleted rows
+ pFrame->IsInTab());
+ if (rNode.IsCreateFrameWhenHidingRedlines())
+ {
+ {
+ auto pMerged(CheckParaRedlineMerge(*pFrame,
+ rTextNode, eMode));
+ pFrame->SetMergedPara(std::move(pMerged));
+ }
+ auto const pMerged(pFrame->GetMergedPara());
+ if (pMerged)
+ {
+ // invalidate SwInvalidateFlags::Size
+ pFrame->Prepare(PrepareHint::Clear, nullptr, false);
+ pFrame->InvalidatePage();
+ if (auto const pObjs = pFrame->GetDrawObjs())
+ { // also invalidate position of existing flys
+ // because they may need to be moved
+ for (auto const pObject : *pObjs)
+ {
+ pObject->InvalidateObjPos();
+ }
+ }
+ }
+ sw::AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rTextNode, pSkipped);
+ // only *first* frame of node gets Existing because it
+ eMode = sw::FrameMode::New; // is not idempotent!
+ }
+ }
+ else
+ {
+ if (auto const& pMergedPara = pFrame->GetMergedPara())
+ {
+ // invalidate SwInvalidateFlags::Size
+ pFrame->Prepare(PrepareHint::Clear, nullptr, false);
+ pFrame->InvalidatePage();
+ if (auto const pObjs = pFrame->GetDrawObjs())
+ { // also invalidate position of existing flys
+ for (auto const pObject : *pObjs)
+ {
+ pObject->InvalidateObjPos();
+ }
+ }
+ // SwFlyAtContentFrame::SwClientNotify() always appends to
+ // the master frame, so do the same here.
+ // (RemoveFootnotesForNode must be called at least once)
+ if (!pFrame->IsFollow())
+ {
+ // the new text frames don't exist yet, so at this point
+ // we can only delete the footnote frames so they don't
+ // point to the merged SwTextFrame any more...
+ assert(&rTextNode == pMergedPara->pFirstNode);
+ // iterate over nodes, not extents: if a node has
+ // no extents now but did have extents initially,
+ // its flys need their frames deleted too!
+ for (SwNodeOffset j = rTextNode.GetIndex() + 1;
+ j <= pMergedPara->pLastNode->GetIndex(); ++j)
+ {
+ SwNode *const pNode(rTextNode.GetNodes()[j]);
+ assert(!pNode->IsEndNode());
+ if (pNode->IsStartNode())
+ {
+ j = pNode->EndOfSectionIndex();
+ }
+ else if (pNode->IsTextNode())
+ {
+ sw::RemoveFootnotesForNode(rLayout, *pNode->GetTextNode(), nullptr);
+ // similarly, remove the anchored flys
+ for (SwFrameFormat * pFormat : pNode->GetAnchoredFlys())
+ {
+ pFormat->DelFrames(/*&rLayout*/);
+ }
+ }
+ }
+ // rely on AppendAllObjs call at the end to add
+ // all flys in first node that are hidden
+ }
+ pFrame->SetMergedPara(nullptr);
+ }
+ }
+ pFrame->Broadcast(SfxHint()); // notify SwAccessibleParagraph
+ }
+ // all nodes, not just merged ones! it may be in the same list as
+ if (rTextNode.IsNumbered(nullptr)) // a preceding merged one...
+ { // notify frames so they reformat numbering portions
+ rTextNode.NumRuleChgd();
+ }
+ }
+ else if (rNode.IsTableNode() && rLayout.IsHideRedlines())
+ {
+ SwTableNode * pTableNd = rNode.GetTableNode();
+ SwPosition const tmp(rNode);
+ SwRangeRedline const*const pRedline(
+ rLayout.GetFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedline(tmp, nullptr));
+ // pathology: redline that starts on a TableNode; cannot
+ // be created in UI but by import filters...
+ if (pRedline
+ && pRedline->GetType() == RedlineType::Delete
+ && &pRedline->Start()->nNode.GetNode() == &rNode)
+ {
+ for (SwNodeOffset j = rNode.GetIndex(); j <= rNode.EndOfSectionIndex(); ++j)
+ {
+ rNode.GetNodes()[j]->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ }
+ pTableNd->DelFrames(&rLayout);
+ }
+ else if ( pTableNd->GetTable().HasDeletedRow() )
+ {
+ pTableNd->DelFrames(&rLayout);
+ if ( !pTableNd->GetTable().IsDeleted() )
+ {
+ SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
+ pTableNd->MakeOwnFrames(&aIdx);
+ }
+ }
+ }
+ else if (rNode.IsTableNode() && !rLayout.IsHideRedlines() &&
+ rNode.GetTableNode()->GetTable().HasDeletedRow() )
+ {
+ SwTableNode * pTableNd = rNode.GetTableNode();
+ pTableNd->DelFrames(&rLayout);
+ SwNodeIndex aIdx( *pTableNd->EndOfSectionNode(), 1 );
+ pTableNd->MakeOwnFrames(&aIdx);
+ }
+
+ if (!rNode.IsCreateFrameWhenHidingRedlines())
+ {
+ if (rLayout.HasMergedParas())
+ {
+ if (rNode.IsContentNode())
+ {
+ // note: nothing to do here, already done
+#ifndef NDEBUG
+ auto const pFrame(static_cast<SwContentNode&>(rNode).getLayoutFrame(&rLayout));
+ assert(!pFrame || static_cast<SwTextFrame*>(pFrame)->GetMergedPara()->pFirstNode != &rNode);
+#endif
+ }
+ }
+ else
+ {
+ assert(!rNode.IsContentNode() || !rNode.GetContentNode()->getLayoutFrame(&rLayout) ||
+ // FIXME: skip this assert in tables with deleted rows
+ rNode.GetContentNode()->getLayoutFrame(&rLayout)->IsInTab());
+ SwNodeOffset j = i + 1;
+ for ( ; j < rEndOfSectionNode.GetIndex(); ++j)
+ {
+ if (rNodes[j]->IsCreateFrameWhenHidingRedlines())
+ {
+ break;
+ }
+ }
+ // call MakeFrames once, because sections/tables
+ // InsertCnt_ also checks for hidden sections
+ SwNodeIndex const start(rNodes, i);
+ SwNodeIndex const end(rNodes, j);
+ {
+ sw::FlyCreationSuppressor aSuppressor(false);
+ ::MakeFrames(rLayout.GetFormat()->GetDoc(), start, end);
+ }
+ i = j - 1; // will be incremented again
+ }
+ }
+ }
+}
+
+static void UnHideRedlinesExtras(SwRootFrame & rLayout,
+ SwNodes & rNodes, SwNode const& rEndOfExtraSectionNode,
+ std::set<SwNodeOffset> *const pSkipped)
+{
+ assert(rEndOfExtraSectionNode.IsEndNode());
+ for (SwNodeOffset i = rEndOfExtraSectionNode.StartOfSectionNode()->GetIndex()
+ + 1; i < rEndOfExtraSectionNode.GetIndex(); ++i)
+ {
+ SwNode const& rStartNode(*rNodes[i]);
+ assert(rStartNode.IsStartNode());
+ assert(rStartNode.GetRedlineMergeFlag() == SwNode::Merge::None);
+ SwNode const& rEndNode(*rStartNode.EndOfSectionNode());
+ bool bSkip(pSkipped && pSkipped->find(i) != pSkipped->end());
+ i = rEndNode.GetIndex();
+ for (SwNodeOffset j = rStartNode.GetIndex() + 1; j < i; ++j)
+ {
+ // note: SwStartNode has no way to access the frames, so check
+ // whether the first content-node inside the section has frames
+ SwNode const& rNode(*rNodes[j]);
+ if (rNode.IsSectionNode() &&
+ static_cast<SwSectionNode const&>(rNode).GetSection().IsHiddenFlag())
+ { // skip hidden sections - they can be inserted in fly-frames :(
+ j = rNode.EndOfSectionNode()->GetIndex();
+ continue;
+ }
+ if (rNode.IsContentNode())
+ {
+ SwContentNode const& rCNode(static_cast<SwContentNode const&>(rNode));
+ if (!rCNode.getLayoutFrame(&rLayout))
+ { // ignore footnote/fly/header/footer with no layout frame
+ bSkip = true; // they will be created from scratch later if needed
+ }
+ break;
+ }
+ }
+ if (!bSkip)
+ {
+ UnHideRedlines(rLayout, rNodes, rEndNode, pSkipped);
+ }
+ }
+}
+
+static void UnHide(SwRootFrame & rLayout)
+{
+ assert(rLayout.GetCurrShell()->ActionPend()); // tdf#125754 avoid recursive layout
+ SwDoc & rDoc(*rLayout.GetFormat()->GetDoc());
+ // don't do early return if there are no redlines:
+ // Show->Hide must init hidden number trees
+ // Hide->Show may be called after all redlines have been deleted but there
+ // may still be MergedParas because those aren't deleted yet...
+#if 0
+ if (!bHideRedlines
+ && rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())
+ {
+ return;
+ }
+#endif
+ // Hide->Show: clear MergedPara, create frames
+ // Show->Hide: call CheckParaRedlineMerge, delete frames
+ // Traverse the document via the nodes-array; traversing via the layout
+ // wouldn't find the nodes that don't have frames in the ->Show case.
+ // In-order traversal of each nodes array section should init the flags
+ // in nodes before they are iterated.
+ // Actual creation of frames should be done with existing functions
+ // if possible, particularly InsertCnt_() or its wrapper ::MakeFrames().
+ SwNodes /*const*/& rNodes(rDoc.GetNodes());
+ // Flys/footnotes: must iterate and find all the ones that already exist
+ // with frames and have redlines inside them; if any don't have frames at
+ // all, they will be created (if necessary) from scratch and completely by
+ // MakeFrames().
+ //
+ // Flys before footnotes: because footnotes may contain flys but not
+ // vice-versa; alas flys may contain flys, so we skip some of them
+ // if they have already been created from scratch via their anchor flys.
+ std::set<SwNodeOffset> skippedFlys;
+ UnHideRedlinesExtras(rLayout, rNodes, rNodes.GetEndOfAutotext(),
+ // when un-hiding, delay all fly frame creation to AppendAllObjs below
+ rLayout.HasMergedParas() ? &skippedFlys : nullptr);
+ // Footnotes are created automatically (after invalidation etc.) by
+ // ConnectFootnote(), but need to be deleted manually. Footnotes do not
+ // occur in flys or headers/footers.
+ UnHideRedlinesExtras(rLayout, rNodes, rNodes.GetEndOfInserts(), nullptr);
+ UnHideRedlines(rLayout, rNodes, rNodes.GetEndOfContent(), nullptr);
+
+ if (!rLayout.HasMergedParas())
+ { // create all previously hidden flys at once:
+ // * Flys on first node of pre-existing merged frames that are hidden
+ // (in delete redline), to be added to the existing frame
+ // * Flys on non-first (hidden/merged) nodes of pre-existing merged
+ // frames, to be added to the new frame of their node
+ // * Flys anchored in other flys that are hidden
+ AppendAllObjs(rDoc.GetSpzFrameFormats(), &rLayout);
+ }
+
+ const bool bIsShowChangesInMargin = rLayout.GetCurrShell()->GetViewOptions()->IsShowChangesInMargin();
+ for (auto const pRedline : rDoc.getIDocumentRedlineAccess().GetRedlineTable())
+ { // DELETE are handled by the code above; for other types, need to
+ // trigger repaint of text frames to add/remove the redline color font
+ // (handle deletions showed in margin also here)
+ if (bIsShowChangesInMargin || pRedline->GetType() != RedlineType::Delete)
+ {
+ pRedline->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ }
+
+ SwFootnoteIdxs & rFootnotes(rDoc.GetFootnoteIdxs());
+ if (rDoc.GetFootnoteInfo().m_eNum == FTNNUM_CHAPTER)
+ {
+ // sadly determining which node is outline node requires hidden layout
+ rFootnotes.UpdateAllFootnote();
+ }
+ // invalidate all footnotes to reformat their numbers
+ for (SwTextFootnote *const pFootnote : rFootnotes)
+ {
+ SwFormatFootnote const& rFootnote(pFootnote->GetFootnote());
+ if (rFootnote.GetNumber() != rFootnote.GetNumberRLHidden()
+ && rFootnote.GetNumStr().isEmpty())
+ {
+ pFootnote->InvalidateNumberInLayout();
+ }
+ }
+ // update various fields to re-expand them with the new layout
+ IDocumentFieldsAccess & rIDFA(rDoc.getIDocumentFieldsAccess());
+ auto const pAuthType(rIDFA.GetFieldType(
+ SwFieldIds::TableOfAuthorities, OUString(), false));
+ if (pAuthType) // created on demand...
+ { // calling DelSequenceArray() should be unnecessary here since the
+ // sequence doesn't depend on frames
+ pAuthType->UpdateFields();
+ }
+ rIDFA.GetFieldType(SwFieldIds::RefPageGet, OUString(), false)->UpdateFields();
+ rIDFA.GetSysFieldType(SwFieldIds::Chapter)->UpdateFields();
+ rIDFA.UpdateExpFields(nullptr, false);
+ rIDFA.UpdateRefFields();
+
+ // update SwPostItMgr / notes in the margin
+ // note: as long as all shells share layout, broadcast to all shells!
+ rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(nullptr, rLayout.HasMergedParas()
+ ? SwFormatFieldHintWhich::REMOVED
+ : SwFormatFieldHintWhich::INSERTED) );
+
+
+// InvalidateAllContent(SwInvalidateFlags::Size); // ??? TODO what to invalidate? this is the big hammer
+}
+
+void SwRootFrame::SetHideRedlines(bool const bHideRedlines)
+{
+ if (bHideRedlines == mbHideRedlines)
+ {
+ return;
+ }
+ // TODO: remove temporary ShowBoth
+ sw::FieldmarkMode const eMode(m_FieldmarkMode);
+ if (HasMergedParas())
+ {
+ m_FieldmarkMode = sw::FieldmarkMode::ShowBoth;
+ mbHideRedlines = false;
+ UnHide(*this);
+ }
+ if (bHideRedlines || eMode != m_FieldmarkMode)
+ {
+ m_FieldmarkMode = eMode;
+ mbHideRedlines = bHideRedlines;
+ UnHide(*this);
+ }
+}
+
+void SwRootFrame::SetFieldmarkMode(sw::FieldmarkMode const eMode)
+{
+ if (eMode == m_FieldmarkMode)
+ {
+ return;
+ }
+ // TODO: remove temporary ShowBoth
+ bool const isHideRedlines(mbHideRedlines);
+ if (HasMergedParas())
+ {
+ mbHideRedlines = false;
+ m_FieldmarkMode = sw::FieldmarkMode::ShowBoth;
+ UnHide(*this);
+ }
+ if (eMode != sw::FieldmarkMode::ShowBoth || isHideRedlines)
+ {
+ mbHideRedlines = isHideRedlines;
+ m_FieldmarkMode = eMode;
+ UnHide(*this);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/model/ModelTraverser.cxx b/sw/source/core/model/ModelTraverser.cxx
new file mode 100644
index 000000000..b7d2b2200
--- /dev/null
+++ b/sw/source/core/model/ModelTraverser.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <ModelTraverser.hxx>
+#include <node.hxx>
+#include <ndarr.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <svx/svdpage.hxx>
+#include <drawdoc.hxx>
+
+namespace sw
+{
+void ModelTraverser::traverse()
+{
+ if (m_pDoc == nullptr)
+ return;
+
+ auto const& pNodes = m_pDoc->GetNodes();
+ SwNode* pNode = nullptr;
+
+ for (SwNodeOffset n(0); n < pNodes.Count(); ++n)
+ {
+ pNode = pNodes[n];
+ if (pNode)
+ {
+ for (auto& pNodeHandler : mpNodeHandler)
+ {
+ pNodeHandler->handleNode(pNode);
+ }
+ }
+ }
+
+ IDocumentDrawModelAccess& rDrawModelAccess = m_pDoc->getIDocumentDrawModelAccess();
+ auto* pModel = rDrawModelAccess.GetDrawModel();
+ for (sal_uInt16 nPage = 0; nPage < pModel->GetPageCount(); ++nPage)
+ {
+ SdrPage* pPage = pModel->GetPage(nPage);
+ for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
+ {
+ SdrObject* pObject = pPage->GetObj(nObject);
+ if (pObject)
+ {
+ for (auto& pNodeHandler : mpNodeHandler)
+ {
+ pNodeHandler->handleSdrObject(pObject);
+ }
+ }
+ }
+ }
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/model/SearchResultLocator.cxx b/sw/source/core/model/SearchResultLocator.cxx
new file mode 100644
index 000000000..8ce21c725
--- /dev/null
+++ b/sw/source/core/model/SearchResultLocator.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <SearchResultLocator.hxx>
+#include <node.hxx>
+#include <drawdoc.hxx>
+#include <frame.hxx>
+#include <cntfrm.hxx>
+#include <viewsh.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+
+#include <tools/XmlWalker.hxx>
+#include <tools/stream.hxx>
+
+#include <boost/property_tree/json_parser.hpp>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+
+namespace sw::search
+{
+void SearchResultLocator::findOne(LocationResult& rResult, SearchIndexData const& rSearchIndexData)
+{
+ if (rSearchIndexData.meType == NodeType::WriterNode)
+ {
+ SwNodes const& rNodes = mpDocument->GetNodes();
+ if (rSearchIndexData.mnNodeIndex >= rNodes.Count())
+ return;
+ SwNode* pNode = rNodes[rSearchIndexData.mnNodeIndex];
+
+ auto* pContentNode = pNode->GetContentNode();
+ auto* pShell = mpDocument->getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ if (pContentNode && pShell)
+ {
+ const SwFrame* pFrame
+ = pContentNode->getLayoutFrame(pShell->GetLayout(), nullptr, nullptr);
+ SwRect const& rArea = pFrame->getFrameArea();
+
+ rResult.mbFound = true;
+ rResult.maRectangles.emplace_back(rArea.Left(), rArea.Top(),
+ rArea.Left() + rArea.Width(),
+ rArea.Top() + rArea.Height());
+ }
+ }
+ else if (rSearchIndexData.meType == NodeType::CommonNode)
+ {
+ IDocumentDrawModelAccess& rDrawModelAccess = mpDocument->getIDocumentDrawModelAccess();
+ auto* pModel = rDrawModelAccess.GetDrawModel();
+ for (sal_uInt16 nPage = 0; nPage < pModel->GetPageCount(); ++nPage)
+ {
+ SdrPage* pPage = pModel->GetPage(nPage);
+ for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
+ {
+ SdrObject* pObject = pPage->GetObj(nObject);
+ if (pObject)
+ {
+ if (pObject->GetName() == rSearchIndexData.maObjectName)
+ {
+ auto aRect = pObject->GetLogicRect();
+ rResult.mbFound = true;
+ rResult.maRectangles.emplace_back(aRect.Left(), aRect.Top(),
+ aRect.Left() + aRect.GetWidth(),
+ aRect.Top() + aRect.GetHeight());
+ }
+ }
+ }
+ }
+ }
+}
+
+LocationResult SearchResultLocator::find(std::vector<SearchIndexData> const& rSearchIndexDataVector)
+{
+ LocationResult aResult;
+ for (auto const& rSearchIndexData : rSearchIndexDataVector)
+ findOne(aResult, rSearchIndexData);
+
+ return aResult;
+}
+
+/** Trying to parse the payload as JSON
+ *
+ * Returns true if parsing was successful and the payload was identified as JSON, else false
+ */
+bool SearchResultLocator::tryParseJSON(const char* pPayload,
+ std::vector<sw::search::SearchIndexData>& rDataVector)
+{
+ boost::property_tree::ptree aTree;
+ std::stringstream aStream(pPayload);
+ try
+ {
+ boost::property_tree::read_json(aStream, aTree);
+ }
+ catch (const boost::property_tree::json_parser_error& /*exception*/)
+ {
+ return false;
+ }
+
+ for (const auto& rEachNode : boost::make_iterator_range(aTree.equal_range("")))
+ {
+ auto const& rEach = rEachNode.second;
+
+ std::string sType = rEach.get<std::string>("node_type", "");
+
+ auto eNodeType = sw::search::NodeType::Undefined;
+ if (sType == "writer")
+ eNodeType = sw::search::NodeType::WriterNode;
+ else if (sType == "common")
+ eNodeType = sw::search::NodeType::CommonNode;
+
+ std::string sJsonObjectName = rEach.get<std::string>("object_name", "");
+
+ SwNodeOffset nIndex(rEach.get<sal_Int32>("index", -1));
+
+ // Don't add search data elements that don't have valid data
+ if (eNodeType != sw::search::NodeType::Undefined && nIndex >= SwNodeOffset(0))
+ {
+ OUString sObjectName;
+ if (!sJsonObjectName.empty())
+ {
+ OString sObjectNameOString(sJsonObjectName.c_str());
+ sObjectName = OStringToOUString(sObjectNameOString, RTL_TEXTENCODING_UTF8);
+ }
+
+ rDataVector.emplace_back(eNodeType, nIndex, sObjectName);
+ }
+ }
+
+ return true;
+}
+
+/** Trying to parse the payload as XML
+ *
+ * Returns true if parsing was successful and the payload was identified as XML, else false
+ */
+bool SearchResultLocator::tryParseXML(const char* pPayload,
+ std::vector<sw::search::SearchIndexData>& rDataVector)
+{
+ const OString aPayloadString(pPayload);
+
+ SvMemoryStream aStream(const_cast<char*>(aPayloadString.getStr()), aPayloadString.getLength(),
+ StreamMode::READ);
+
+ tools::XmlWalker aWalker;
+
+ if (!aWalker.open(&aStream))
+ return false;
+
+ if (aWalker.name() != "indexing")
+ return true;
+
+ aWalker.children();
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "paragraph")
+ {
+ OString sType = aWalker.attribute("node_type");
+ OString sIndex = aWalker.attribute("index");
+ OString sObjectName = aWalker.attribute("object_name");
+
+ if (!sType.isEmpty() && !sIndex.isEmpty())
+ {
+ sw::search::SearchIndexData aData;
+ aData.mnNodeIndex = SwNodeOffset(sIndex.toInt32());
+ auto eNodeType = sw::search::NodeType::Undefined;
+ if (sType == "writer")
+ eNodeType = sw::search::NodeType::WriterNode;
+ else if (sType == "common")
+ eNodeType = sw::search::NodeType::CommonNode;
+
+ aData.meType = eNodeType;
+ if (!sObjectName.isEmpty())
+ aData.maObjectName = OStringToOUString(sObjectName, RTL_TEXTENCODING_UTF8);
+ rDataVector.push_back(aData);
+ }
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+ return true;
+}
+
+LocationResult SearchResultLocator::findForPayload(const char* pPayload)
+{
+ std::vector<sw::search::SearchIndexData> aDataVector;
+
+ // Try parse the payload as JSON, if not recognised as JSON, try parse
+ // it as XML
+ tryParseJSON(pPayload, aDataVector) || tryParseXML(pPayload, aDataVector);
+
+ return find(aDataVector);
+}
+
+} // end sw namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/objectpositioning/anchoredobjectposition.cxx b/sw/source/core/objectpositioning/anchoredobjectposition.cxx
new file mode 100644
index 000000000..e530e36df
--- /dev/null
+++ b/sw/source/core/objectpositioning/anchoredobjectposition.cxx
@@ -0,0 +1,1146 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <anchoredobjectposition.hxx>
+#include <environmentofanchoredobject.hxx>
+#include <flyfrm.hxx>
+#include <flyfrms.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <svx/svdobj.hxx>
+#include <dflyobj.hxx>
+#include <dcontact.hxx>
+#include <frmfmt.hxx>
+#include <fmtornt.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <ndtxt.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <textboxhelper.hxx>
+#include <fmtsrnd.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+namespace objectpositioning
+{
+
+SwAnchoredObjectPosition::SwAnchoredObjectPosition( SdrObject& _rDrawObj )
+ : mrDrawObj( _rDrawObj ),
+ mpAnchoredObj( nullptr ),
+ mpAnchorFrame( nullptr ),
+ mpContact( nullptr ),
+ mbIsObjFly( false ),
+ // #i62875#
+ mbFollowTextFlow( false ),
+ mbDoNotCaptureAnchoredObj( false )
+{
+#if OSL_DEBUG_LEVEL > 0
+ // assert, if object isn't of expected type
+ const bool bObjOfExceptedType =
+ dynamic_cast<const SwVirtFlyDrawObj*>( &mrDrawObj) != nullptr || // object representing fly frame
+ dynamic_cast<const SwDrawVirtObj*>( &mrDrawObj) != nullptr || // 'virtual' drawing object
+ ( dynamic_cast<const SdrVirtObj*>( &mrDrawObj) == nullptr && // 'master' drawing object
+ dynamic_cast<const SwFlyDrawObj*>( &mrDrawObj) == nullptr ); // - indirectly checked
+ OSL_ENSURE( bObjOfExceptedType,
+ "SwAnchoredObjectPosition(..) - object of unexpected type!" );
+#endif
+
+ GetInfoAboutObj();
+}
+
+/** determine information about object
+
+ members <mbIsObjFly>, <mpFrameOfObj>, <mpAnchorFrame>, <mpContact>,
+ <mbFollowTextFlow> and <mbDoNotCaptureAnchoredObj> are set
+*/
+void SwAnchoredObjectPosition::GetInfoAboutObj()
+{
+ // determine, if object represents a fly frame
+ {
+ mbIsObjFly = dynamic_cast<const SwVirtFlyDrawObj*>( &mrDrawObj) != nullptr;
+ }
+
+ // determine contact object
+ {
+ mpContact = GetUserCall( &mrDrawObj );
+ assert(mpContact &&
+ "SwAnchoredObjectPosition::GetInfoAboutObj() - missing SwContact-object.");
+ }
+
+ // determine anchored object, the object belongs to
+ {
+ // #i26791#
+ mpAnchoredObj = mpContact->GetAnchoredObj( &mrDrawObj );
+ assert(mpAnchoredObj &&
+ "SwAnchoredObjectPosition::GetInfoAboutObj() - missing anchored object.");
+ }
+
+ // determine frame, the object is anchored at
+ {
+ // #i26791#
+ mpAnchorFrame = mpAnchoredObj->AnchorFrame();
+ OSL_ENSURE( mpAnchorFrame,
+ "SwAnchoredObjectPosition::GetInfoAboutObj() - missing anchor frame." );
+ }
+
+ // determine format the object belongs to
+ {
+ // #i28701#
+ mpFrameFormat = &mpAnchoredObj->GetFrameFormat();
+ assert(mpFrameFormat &&
+ "<SwAnchoredObjectPosition::GetInfoAboutObj() - missing frame format.");
+ }
+
+ // #i62875# - determine attribute value of <Follow-Text-Flow>
+ {
+ mbFollowTextFlow = mpFrameFormat->GetFollowTextFlow().GetValue();
+ }
+
+ // determine, if anchored object has not to be captured on the page.
+ // the following conditions must be hold to *not* capture it:
+ // - corresponding document compatibility flag is set
+ // - it's a drawing object or it's a non-textbox wrap-though fly frame
+ // - it doesn't follow the text flow
+ {
+ bool bTextBox = SwTextBoxHelper::isTextBox(mpFrameFormat, RES_FLYFRMFMT);
+ bool bWrapThrough = mpFrameFormat->GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH;
+ mbDoNotCaptureAnchoredObj = (!mbIsObjFly || (!bTextBox && bWrapThrough)) && !mbFollowTextFlow &&
+ mpFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_CAPTURE_DRAW_OBJS_ON_PAGE);
+ }
+}
+
+SwAnchoredObjectPosition::~SwAnchoredObjectPosition()
+{}
+
+bool SwAnchoredObjectPosition::IsAnchoredToChar() const
+{
+ return false;
+}
+
+const SwFrame* SwAnchoredObjectPosition::ToCharOrientFrame() const
+{
+ return nullptr;
+}
+
+const SwRect* SwAnchoredObjectPosition::ToCharRect() const
+{
+ return nullptr;
+}
+
+// #i22341#
+SwTwips SwAnchoredObjectPosition::ToCharTopOfLine() const
+{
+ return 0;
+}
+
+/** helper method to determine top of a frame for the vertical
+ object positioning
+
+ #i11860#
+*/
+SwTwips SwAnchoredObjectPosition::GetTopForObjPos( const SwFrame& _rFrame,
+ const SwRectFn& _fnRect,
+ const bool _bVert ) const
+{
+ SwTwips nTopOfFrameForObjPos = (_rFrame.getFrameArea().*_fnRect->fnGetTop)();
+
+ if ( _rFrame.IsTextFrame() )
+ {
+ const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(_rFrame);
+ if ( _bVert )
+ {
+ nTopOfFrameForObjPos -=
+ rTextFrame.GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ }
+ else
+ {
+ nTopOfFrameForObjPos +=
+ rTextFrame.GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+
+ const SwFormatSurround& rSurround = mpFrameFormat->GetSurround();
+ bool bWrapThrough = rSurround.GetSurround() == css::text::WrapTextMode_THROUGH;
+ // If the frame format is a TextBox of a draw shape, then use the
+ // surround of the original shape.
+ SwTextBoxHelper::getShapeWrapThrough(mpFrameFormat, bWrapThrough);
+
+ // Get the offset between the top of the text frame and the top of
+ // the first line inside the frame that has more than just fly
+ // portions.
+ nTopOfFrameForObjPos += rTextFrame.GetBaseVertOffsetForFly(!bWrapThrough);
+ }
+ }
+
+ return nTopOfFrameForObjPos;
+}
+
+void SwAnchoredObjectPosition::GetVertAlignmentValues(
+ const SwFrame& _rVertOrientFrame,
+ const SwFrame& _rPageAlignLayFrame,
+ const sal_Int16 _eRelOrient,
+ SwTwips& _orAlignAreaHeight,
+ SwTwips& _orAlignAreaOffset ) const
+{
+ SwTwips nHeight = 0;
+ SwTwips nOffset = 0;
+ SwRectFnSet aRectFnSet(&_rVertOrientFrame);
+ // #i11860# - top of <_rVertOrientFrame> for object positioning
+ const SwTwips nVertOrientTop = GetTopForObjPos( _rVertOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ // #i11860# - upper space amount of <_rVertOrientFrame> considered
+ // for previous frame
+ const SwTwips nVertOrientUpperSpaceForPrevFrameAndPageGrid =
+ _rVertOrientFrame.IsTextFrame()
+ ? static_cast<const SwTextFrame&>(_rVertOrientFrame).
+ GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid()
+ : 0;
+ switch ( _eRelOrient )
+ {
+ case text::RelOrientation::FRAME:
+ {
+ // #i11860# - consider upper space of previous frame
+ nHeight = aRectFnSet.GetHeight(_rVertOrientFrame.getFrameArea()) -
+ nVertOrientUpperSpaceForPrevFrameAndPageGrid;
+ nOffset = 0;
+ }
+ break;
+ case text::RelOrientation::PRINT_AREA:
+ {
+ nHeight = aRectFnSet.GetHeight(_rVertOrientFrame.getFramePrintArea());
+ // #i11860# - consider upper space of previous frame
+ nOffset = aRectFnSet.GetTopMargin(_rVertOrientFrame) -
+ nVertOrientUpperSpaceForPrevFrameAndPageGrid;
+ // if aligned to page in horizontal layout, consider header and
+ // footer frame height appropriately.
+ if( _rVertOrientFrame.IsPageFrame() && !aRectFnSet.IsVert() )
+ {
+ const SwFrame* pPrtFrame =
+ static_cast<const SwPageFrame&>(_rVertOrientFrame).Lower();
+ while( pPrtFrame )
+ {
+ if( pPrtFrame->IsHeaderFrame() )
+ {
+ nHeight -= pPrtFrame->getFrameArea().Height();
+ nOffset += pPrtFrame->getFrameArea().Height();
+ }
+ else if( pPrtFrame->IsFooterFrame() )
+ {
+ nHeight -= pPrtFrame->getFrameArea().Height();
+ }
+ pPrtFrame = pPrtFrame->GetNext();
+ }
+ }
+ }
+ break;
+ case text::RelOrientation::PAGE_FRAME:
+ case text::RelOrientation::PAGE_PRINT_AREA_TOP:
+ {
+ nHeight = aRectFnSet.GetHeight(_rPageAlignLayFrame.getFrameArea());
+ nOffset = aRectFnSet.YDiff(
+ aRectFnSet.GetTop(_rPageAlignLayFrame.getFrameArea()),
+ nVertOrientTop );
+ }
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ nHeight = aRectFnSet.GetHeight(_rPageAlignLayFrame.getFramePrintArea());
+ nOffset = aRectFnSet.GetTopMargin(_rPageAlignLayFrame) +
+ aRectFnSet.YDiff(
+ aRectFnSet.GetTop(_rPageAlignLayFrame.getFrameArea()),
+ nVertOrientTop );
+ // if aligned to page in horizontal layout, consider header and
+ // footer frame height appropriately.
+ if( _rPageAlignLayFrame.IsPageFrame() && !aRectFnSet.IsVert() )
+ {
+ const SwFrame* pPrtFrame =
+ static_cast<const SwPageFrame&>(_rPageAlignLayFrame).Lower();
+ while( pPrtFrame )
+ {
+ if( pPrtFrame->IsHeaderFrame() )
+ {
+ nHeight -= pPrtFrame->getFrameArea().Height();
+ nOffset += pPrtFrame->getFrameArea().Height();
+ }
+ else if( pPrtFrame->IsFooterFrame() )
+ {
+ nHeight -= pPrtFrame->getFrameArea().Height();
+ }
+ pPrtFrame = pPrtFrame->GetNext();
+ }
+ }
+ }
+ break;
+ case text::RelOrientation::PAGE_PRINT_AREA_BOTTOM:
+ {
+ nHeight = aRectFnSet.GetBottomMargin(_rPageAlignLayFrame);
+ nOffset = aRectFnSet.YDiff(
+ aRectFnSet.GetPrtBottom(_rPageAlignLayFrame),
+ nVertOrientTop);
+
+ if (_rPageAlignLayFrame.IsPageFrame() && !aRectFnSet.IsVert())
+ {
+ const SwFrame* pPrtFrame =
+ static_cast<const SwPageFrame&>(_rPageAlignLayFrame).Lower();
+
+ while (pPrtFrame)
+ {
+ if (pPrtFrame->IsFooterFrame())
+ {
+ nHeight += pPrtFrame->getFrameArea().Height();
+ nOffset -= pPrtFrame->getFrameArea().Height();
+ }
+ pPrtFrame = pPrtFrame->GetNext();
+ }
+ }
+
+ }
+ break;
+ // #i22341# - vertical alignment at top of line
+ case text::RelOrientation::TEXT_LINE:
+ {
+ if ( IsAnchoredToChar() )
+ {
+ nHeight = 0;
+ nOffset = aRectFnSet.YDiff( ToCharTopOfLine(), nVertOrientTop );
+ }
+ else
+ {
+ OSL_FAIL( "<SwAnchoredObjectPosition::GetVertAlignmentValues(..)> - invalid relative alignment" );
+ }
+ }
+ break;
+ case text::RelOrientation::CHAR:
+ {
+ if ( IsAnchoredToChar() )
+ {
+ nHeight = aRectFnSet.GetHeight(*ToCharRect());
+ nOffset = aRectFnSet.YDiff( aRectFnSet.GetTop(*ToCharRect()),
+ nVertOrientTop );
+ }
+ else
+ {
+ OSL_FAIL( "<SwAnchoredObjectPosition::GetVertAlignmentValues(..)> - invalid relative alignment" );
+ }
+ }
+ break;
+ // no break here, because text::RelOrientation::CHAR is invalid, if !mbAnchorToChar
+ default:
+ {
+ OSL_FAIL( "<SwAnchoredObjectPosition::GetVertAlignmentValues(..)> - invalid relative alignment" );
+ }
+ }
+
+ _orAlignAreaHeight = nHeight;
+ _orAlignAreaOffset = nOffset;
+}
+
+// #i26791# - add output parameter <_roVertOffsetToFrameAnchorPos>
+SwTwips SwAnchoredObjectPosition::GetVertRelPos(
+ const SwFrame& _rVertOrientFrame,
+ const SwFrame& _rPageAlignLayFrame,
+ const sal_Int16 _eVertOrient,
+ const sal_Int16 _eRelOrient,
+ const SwTwips _nVertPos,
+ const SvxLRSpaceItem& _rLRSpacing,
+ const SvxULSpaceItem& _rULSpacing,
+ SwTwips& _roVertOffsetToFrameAnchorPos ) const
+{
+ SwTwips nRelPosY = 0;
+ SwRectFnSet aRectFnSet(&_rVertOrientFrame);
+
+ SwTwips nAlignAreaHeight;
+ SwTwips nAlignAreaOffset;
+ GetVertAlignmentValues( _rVertOrientFrame, _rPageAlignLayFrame,
+ _eRelOrient, nAlignAreaHeight, nAlignAreaOffset );
+
+ nRelPosY = nAlignAreaOffset;
+ const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
+ const SwTwips nObjHeight = aRectFnSet.GetHeight(aObjBoundRect);
+
+ switch ( _eVertOrient )
+ {
+ case text::VertOrientation::NONE:
+ {
+ // 'manual' vertical position
+ nRelPosY += _nVertPos;
+ }
+ break;
+ case text::VertOrientation::TOP:
+ {
+ nRelPosY += aRectFnSet.IsVert()
+ ? ( aRectFnSet.IsVertL2R()
+ ? _rLRSpacing.GetLeft()
+ : _rLRSpacing.GetRight() )
+ : _rULSpacing.GetUpper();
+ }
+ break;
+ case text::VertOrientation::CENTER:
+ {
+ nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2);
+ }
+ break;
+ case text::VertOrientation::BOTTOM:
+ {
+ nRelPosY += nAlignAreaHeight -
+ ( nObjHeight + ( aRectFnSet.IsVert()
+ ? ( aRectFnSet.IsVertL2R()
+ ? _rLRSpacing.GetRight()
+ : _rLRSpacing.GetLeft() )
+ : _rULSpacing.GetLower() ) );
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwAnchoredObjectPosition::GetVertRelPos(..) - invalid vertical positioning" );
+ }
+ }
+
+ // #i26791#
+ _roVertOffsetToFrameAnchorPos = nAlignAreaOffset;
+
+ return nRelPosY;
+}
+
+/** adjust calculated vertical in order to keep object inside
+ 'page' alignment layout frame.
+
+ #i28701# - parameter <_nTopOfAnch> and <_bVert> added
+ #i31805# - add parameter <_bCheckBottom>
+ #i26945# - add parameter <_bFollowTextFlow>
+ #i62875# - method now private and renamed.
+ OD 2009-09-01 #mongolianlayout# - add parameter <bVertL2R>
+*/
+SwTwips SwAnchoredObjectPosition::ImplAdjustVertRelPos( const SwTwips nTopOfAnch,
+ const bool bVert,
+ const bool bVertL2R,
+ const SwFrame& rPageAlignLayFrame,
+ const SwTwips nProposedRelPosY,
+ const bool bFollowTextFlow,
+ const bool bCheckBottom ) const
+{
+ SwTwips nAdjustedRelPosY = nProposedRelPosY;
+ // TODO: Replace the following condition with the correction
+ // of the implementation of option FollowTextFlow.
+ if ( SwAnchoredObject::IsDraggingOffPageAllowed(FindFrameFormat(&mrDrawObj)) &&
+ !(GetAnchorFrame().IsInTab() && DoesObjFollowsTextFlow()) )
+ {
+ return nAdjustedRelPosY;
+ }
+
+ const Size aObjSize(GetAnchoredObj().GetObjRect().SSize());
+ // determine the area of 'page' alignment frame, to which the vertical
+ // position is restricted.
+ // #i28701# - Extend restricted area for the vertical
+ // position to area of the page frame, if wrapping style influence is
+ // considered on object positioning. Needed to avoid layout loops in the
+ // object positioning algorithm considering the wrapping style influence
+ // caused by objects, which follow the text flow and thus are restricted
+ // to its environment (e.g. page header/footer).
+ SwRect aPgAlignArea;
+ {
+ // #i26945# - no extension of restricted area, if
+ // object's attribute follow text flow is set and its inside a table
+ if ( GetFrameFormat().getIDocumentSettingAccess().get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
+ ( !bFollowTextFlow ||
+ !GetAnchoredObj().GetAnchorFrame()->IsInTab() ) )
+ {
+ aPgAlignArea = rPageAlignLayFrame.FindPageFrame()->getFrameArea();
+ }
+ else
+ {
+ aPgAlignArea = rPageAlignLayFrame.getFrameArea();
+ }
+ }
+
+ if ( bVert )
+ {
+ // #i31805# - consider value of <_bCheckBottom>
+ if ( !bVertL2R )
+ {
+ if ( bCheckBottom &&
+ nTopOfAnch - nAdjustedRelPosY - aObjSize.Width() <
+ aPgAlignArea.Left() )
+ {
+ nAdjustedRelPosY = aPgAlignArea.Left() +
+ nTopOfAnch -
+ aObjSize.Width();
+ }
+ // #i32964# - correction
+ if ( nTopOfAnch - nAdjustedRelPosY > aPgAlignArea.Right() )
+ {
+ nAdjustedRelPosY = nTopOfAnch - aPgAlignArea.Right();
+ }
+ }
+ else
+ {
+ // tdf#112443 if position is completely off-page
+ // return the proposed position and do not adjust it...
+ // tdf#120839 .. unless anchored to char (anchor can jump on other page)
+ const bool bDisablePositioning = mpFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+ if ( bDisablePositioning && !IsAnchoredToChar() && nTopOfAnch + nAdjustedRelPosY > aPgAlignArea.Right() )
+ {
+ return nProposedRelPosY;
+ }
+
+ if ( bCheckBottom &&
+ nTopOfAnch + nAdjustedRelPosY + aObjSize.Width() >
+ aPgAlignArea.Right() )
+ {
+ nAdjustedRelPosY = aPgAlignArea.Right() -
+ nTopOfAnch -
+ aObjSize.Width();
+ }
+ if ( nTopOfAnch + nAdjustedRelPosY < aPgAlignArea.Left() )
+ {
+ nAdjustedRelPosY = aPgAlignArea.Left() - nTopOfAnch;
+ }
+ }
+ }
+ else
+ {
+ // tdf#112443 if position is completely off-page
+ // return the proposed position and do not adjust it...
+ const bool bDisablePositioning = mpFrameFormat->getIDocumentSettingAccess().get(DocumentSettingId::DISABLE_OFF_PAGE_POSITIONING);
+
+ // tdf#123002 disable the positioning in header and footer only
+ // we should limit this since anchors of body frames may appear on other pages
+ const bool bIsFooterOrHeader = GetAnchorFrame().GetUpper()
+ && (GetAnchorFrame().GetUpper()->IsFooterFrame() || GetAnchorFrame().GetUpper()->IsHeaderFrame() );
+
+ if ( bDisablePositioning && bIsFooterOrHeader && nTopOfAnch + nAdjustedRelPosY > aPgAlignArea.Bottom() )
+ {
+ return nProposedRelPosY;
+ }
+
+ // #i31805# - consider value of <bCheckBottom>
+ if ( bCheckBottom &&
+ nTopOfAnch + nAdjustedRelPosY + aObjSize.Height() >
+ aPgAlignArea.Top() + aPgAlignArea.Height() )
+ {
+ nAdjustedRelPosY = aPgAlignArea.Top() + aPgAlignArea.Height() -
+ nTopOfAnch -
+ aObjSize.Height();
+ }
+ if ( nTopOfAnch + nAdjustedRelPosY < aPgAlignArea.Top() )
+ {
+ nAdjustedRelPosY = aPgAlignArea.Top() - nTopOfAnch;
+ }
+ }
+ return nAdjustedRelPosY;
+}
+
+/** adjust calculated horizontal in order to keep object inside
+ 'page' alignment layout frame.
+
+ #i62875# - method now private and renamed.
+*/
+SwTwips SwAnchoredObjectPosition::ImplAdjustHoriRelPos(
+ const SwFrame& _rPageAlignLayFrame,
+ const SwTwips _nProposedRelPosX ) const
+{
+ SwTwips nAdjustedRelPosX = _nProposedRelPosX;
+
+ if (SwAnchoredObject::IsDraggingOffPageAllowed(FindFrameFormat(&mrDrawObj)))
+ return nAdjustedRelPosX;
+
+ const SwFrame& rAnchorFrame = GetAnchorFrame();
+ const bool bVert = rAnchorFrame.IsVertical();
+
+ const Size aObjSize( GetAnchoredObj().GetObjRect().SSize() );
+
+ if( bVert )
+ {
+ if ( rAnchorFrame.getFrameArea().Top() + nAdjustedRelPosX + aObjSize.Height() >
+ _rPageAlignLayFrame.getFrameArea().Bottom() )
+ {
+ nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Bottom() -
+ rAnchorFrame.getFrameArea().Top() -
+ aObjSize.Height();
+ }
+ if ( rAnchorFrame.getFrameArea().Top() + nAdjustedRelPosX <
+ _rPageAlignLayFrame.getFrameArea().Top() )
+ {
+ nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Top() -
+ rAnchorFrame.getFrameArea().Top();
+ }
+ }
+ else
+ {
+ if ( rAnchorFrame.getFrameArea().Left() + nAdjustedRelPosX + aObjSize.Width() >
+ _rPageAlignLayFrame.getFrameArea().Right() )
+ {
+ nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Right() -
+ rAnchorFrame.getFrameArea().Left() -
+ aObjSize.Width();
+ }
+ if ( rAnchorFrame.getFrameArea().Left() + nAdjustedRelPosX <
+ _rPageAlignLayFrame.getFrameArea().Left() )
+ {
+ nAdjustedRelPosX = _rPageAlignLayFrame.getFrameArea().Left() -
+ rAnchorFrame.getFrameArea().Left();
+ }
+ }
+
+ return nAdjustedRelPosX;
+}
+
+/** determine alignment value for horizontal position of object */
+void SwAnchoredObjectPosition::GetHoriAlignmentValues( const SwFrame& _rHoriOrientFrame,
+ const SwFrame& _rPageAlignLayFrame,
+ const sal_Int16 _eRelOrient,
+ const bool _bObjWrapThrough,
+ SwTwips& _orAlignAreaWidth,
+ SwTwips& _orAlignAreaOffset,
+ bool& _obAlignedRelToPage ) const
+{
+ SwTwips nWidth = 0;
+ SwTwips nOffset = 0;
+ SwRectFnSet aRectFnSet(&_rHoriOrientFrame);
+ switch ( _eRelOrient )
+ {
+ case text::RelOrientation::PRINT_AREA:
+ {
+ nWidth = aRectFnSet.GetWidth(_rHoriOrientFrame.getFramePrintArea());
+ nOffset = aRectFnSet.GetLeftMargin(_rHoriOrientFrame);
+ if ( _rHoriOrientFrame.IsTextFrame() )
+ {
+ // consider movement of text frame left
+ nOffset += static_cast<const SwTextFrame&>(_rHoriOrientFrame).GetBaseOffsetForFly( !_bObjWrapThrough );
+ }
+ else if ( _rHoriOrientFrame.IsPageFrame() && aRectFnSet.IsVert() )
+ {
+ // for to-page anchored objects, consider header/footer frame
+ // in vertical layout
+ const SwFrame* pPrtFrame =
+ static_cast<const SwPageFrame&>(_rHoriOrientFrame).Lower();
+ while( pPrtFrame )
+ {
+ if( pPrtFrame->IsHeaderFrame() )
+ {
+ nWidth -= pPrtFrame->getFrameArea().Height();
+ nOffset += pPrtFrame->getFrameArea().Height();
+ }
+ else if( pPrtFrame->IsFooterFrame() )
+ {
+ nWidth -= pPrtFrame->getFrameArea().Height();
+ }
+ pPrtFrame = pPrtFrame->GetNext();
+ }
+ }
+ break;
+ }
+ case text::RelOrientation::PAGE_LEFT:
+ {
+ // align at left border of page frame/fly frame/cell frame
+ nWidth = aRectFnSet.GetLeftMargin(_rPageAlignLayFrame);
+ nOffset = aRectFnSet.XDiff(
+ aRectFnSet.GetLeft(_rPageAlignLayFrame.getFrameArea()),
+ aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) );
+ _obAlignedRelToPage = true;
+ }
+ break;
+ case text::RelOrientation::PAGE_RIGHT:
+ {
+ // align at right border of page frame/fly frame/cell frame
+ nWidth = aRectFnSet.GetRightMargin(_rPageAlignLayFrame);
+ nOffset = aRectFnSet.XDiff(
+ aRectFnSet.GetPrtRight(_rPageAlignLayFrame),
+ aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) );
+ _obAlignedRelToPage = true;
+ }
+ break;
+ case text::RelOrientation::FRAME_LEFT:
+ {
+ // align at left border of anchor frame
+ nWidth = aRectFnSet.GetLeftMargin(_rHoriOrientFrame);
+ nOffset = 0;
+ }
+ break;
+ case text::RelOrientation::FRAME_RIGHT:
+ {
+ // align at right border of anchor frame
+ // Unify and simplify
+ nWidth = aRectFnSet.GetRightMargin(_rHoriOrientFrame);
+ nOffset = aRectFnSet.GetRight(_rHoriOrientFrame.getFramePrintArea());
+ }
+ break;
+ case text::RelOrientation::CHAR:
+ {
+ // alignment relative to character - assure, that corresponding
+ // character rectangle is set.
+ if ( IsAnchoredToChar() )
+ {
+ nWidth = 0;
+ nOffset = aRectFnSet.XDiff(
+ aRectFnSet.GetLeft(*ToCharRect()),
+ aRectFnSet.GetLeft(ToCharOrientFrame()->getFrameArea()) );
+ break;
+ }
+ [[fallthrough]];
+ }
+ case text::RelOrientation::PAGE_PRINT_AREA:
+ {
+ nWidth = aRectFnSet.GetWidth(_rPageAlignLayFrame.getFramePrintArea());
+ nOffset = aRectFnSet.XDiff(
+ aRectFnSet.GetPrtLeft(_rPageAlignLayFrame),
+ aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) );
+ if ( _rHoriOrientFrame.IsPageFrame() && aRectFnSet.IsVert() )
+ {
+ // for to-page anchored objects, consider header/footer frame
+ // in vertical layout
+ const SwFrame* pPrtFrame =
+ static_cast<const SwPageFrame&>(_rHoriOrientFrame).Lower();
+ while( pPrtFrame )
+ {
+ if( pPrtFrame->IsHeaderFrame() )
+ {
+ nWidth -= pPrtFrame->getFrameArea().Height();
+ nOffset += pPrtFrame->getFrameArea().Height();
+ }
+ else if( pPrtFrame->IsFooterFrame() )
+ {
+ nWidth -= pPrtFrame->getFrameArea().Height();
+ }
+ pPrtFrame = pPrtFrame->GetNext();
+ }
+ }
+ _obAlignedRelToPage = true;
+ break;
+ }
+ case text::RelOrientation::PAGE_FRAME:
+ {
+ nWidth = aRectFnSet.GetWidth(_rPageAlignLayFrame.getFrameArea());
+ nOffset = aRectFnSet.XDiff(
+ aRectFnSet.GetLeft(_rPageAlignLayFrame.getFrameArea()),
+ aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea()) );
+ _obAlignedRelToPage = true;
+ break;
+ }
+ default:
+ {
+ nWidth = aRectFnSet.GetWidth(_rHoriOrientFrame.getFrameArea());
+
+ bool bWrapThrough = _bObjWrapThrough;
+ // If the frame format is a TextBox of a draw shape, then use the
+ // surround of the original shape.
+ SwTextBoxHelper::getShapeWrapThrough(mpFrameFormat, bWrapThrough);
+
+ bool bIgnoreFlysAnchoredAtFrame = !bWrapThrough;
+ nOffset = _rHoriOrientFrame.IsTextFrame() ?
+ static_cast<const SwTextFrame&>(_rHoriOrientFrame).GetBaseOffsetForFly( bIgnoreFlysAnchoredAtFrame ) :
+ 0;
+ break;
+ }
+ }
+
+ _orAlignAreaWidth = nWidth;
+ _orAlignAreaOffset = nOffset;
+}
+
+/** toggle given horizontal orientation and relative alignment */
+void SwAnchoredObjectPosition::ToggleHoriOrientAndAlign(
+ const bool _bToggleLeftRight,
+ sal_Int16& _ioeHoriOrient,
+ sal_Int16& _iopeRelOrient
+ )
+{
+ if( !_bToggleLeftRight )
+ return;
+
+ // toggle orientation
+ switch ( _ioeHoriOrient )
+ {
+ case text::HoriOrientation::RIGHT :
+ {
+ _ioeHoriOrient = text::HoriOrientation::LEFT;
+ }
+ break;
+ case text::HoriOrientation::LEFT :
+ {
+ _ioeHoriOrient = text::HoriOrientation::RIGHT;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // toggle relative alignment
+ switch ( _iopeRelOrient )
+ {
+ case text::RelOrientation::PAGE_RIGHT :
+ {
+ _iopeRelOrient = text::RelOrientation::PAGE_LEFT;
+ }
+ break;
+ case text::RelOrientation::PAGE_LEFT :
+ {
+ _iopeRelOrient = text::RelOrientation::PAGE_RIGHT;
+ }
+ break;
+ case text::RelOrientation::FRAME_RIGHT :
+ {
+ _iopeRelOrient = text::RelOrientation::FRAME_LEFT;
+ }
+ break;
+ case text::RelOrientation::FRAME_LEFT :
+ {
+ _iopeRelOrient = text::RelOrientation::FRAME_RIGHT;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/** calculate relative horizontal position */
+SwTwips SwAnchoredObjectPosition::CalcRelPosX(
+ const SwFrame& _rHoriOrientFrame,
+ const SwEnvironmentOfAnchoredObject& _rEnvOfObj,
+ const SwFormatHoriOrient& _rHoriOrient,
+ const SvxLRSpaceItem& _rLRSpacing,
+ const SvxULSpaceItem& _rULSpacing,
+ const bool _bObjWrapThrough,
+ const SwTwips _nRelPosY,
+ SwTwips& _roHoriOffsetToFrameAnchorPos
+ ) const
+{
+ // determine 'page' alignment layout frame
+ const SwFrame& rPageAlignLayFrame =
+ _rEnvOfObj.GetHoriEnvironmentLayoutFrame( _rHoriOrientFrame );
+
+ const bool bEvenPage = !rPageAlignLayFrame.OnRightPage();
+ const bool bToggle = _rHoriOrient.IsPosToggle() && bEvenPage;
+
+ // determine orientation and relative alignment
+ sal_Int16 eHoriOrient = _rHoriOrient.GetHoriOrient();
+ sal_Int16 eRelOrient = _rHoriOrient.GetRelationOrient();
+ // toggle orientation and relative alignment
+ ToggleHoriOrientAndAlign( bToggle, eHoriOrient, eRelOrient );
+
+ // determine alignment parameter
+ // <nWidth>: 'width' of alignment area
+ // <nOffset>: offset of alignment area, relative to 'left' of anchor frame
+ SwTwips nWidth = 0;
+ SwTwips nOffset = 0;
+ bool bAlignedRelToPage = false;
+ GetHoriAlignmentValues( _rHoriOrientFrame, rPageAlignLayFrame,
+ eRelOrient, _bObjWrapThrough,
+ nWidth, nOffset, bAlignedRelToPage );
+
+ const SwFrame& rAnchorFrame = GetAnchorFrame();
+ SwRectFnSet aRectFnSet(&_rHoriOrientFrame);
+ SwTwips nObjWidth = aRectFnSet.GetWidth(GetAnchoredObj().GetObjRect());
+ SwTwips nRelPosX = nOffset;
+ if ( _rHoriOrient.GetHoriOrient() == text::HoriOrientation::NONE )
+ {
+ // 'manual' horizontal position
+ const bool bR2L = rAnchorFrame.IsRightToLeft();
+ if( IsAnchoredToChar() && text::RelOrientation::CHAR == eRelOrient )
+ {
+ if( bR2L )
+ nRelPosX -= _rHoriOrient.GetPos();
+ else
+ nRelPosX += _rHoriOrient.GetPos();
+ }
+ else if ( bToggle || ( !_rHoriOrient.IsPosToggle() && bR2L ) )
+ {
+ // Correction: consider <nOffset> also for
+ // toggling from left to right.
+ nRelPosX += nWidth - nObjWidth - _rHoriOrient.GetPos();
+ }
+ else
+ {
+ nRelPosX += _rHoriOrient.GetPos();
+ }
+ }
+ else if ( text::HoriOrientation::CENTER == eHoriOrient )
+ nRelPosX += (nWidth / 2) - (nObjWidth / 2);
+ else if ( text::HoriOrientation::RIGHT == eHoriOrient )
+ nRelPosX += nWidth -
+ ( nObjWidth +
+ ( aRectFnSet.IsVert() ? _rULSpacing.GetLower() : _rLRSpacing.GetRight() ) );
+ else
+ nRelPosX += aRectFnSet.IsVert() ? _rULSpacing.GetUpper() : _rLRSpacing.GetLeft();
+
+ // adjust relative position by distance between anchor frame and
+ // the frame, the object is oriented at.
+ if ( &rAnchorFrame != &_rHoriOrientFrame )
+ {
+ SwTwips nLeftOrient = aRectFnSet.GetLeft(_rHoriOrientFrame.getFrameArea());
+ SwTwips nLeftAnchor = aRectFnSet.GetLeft(rAnchorFrame.getFrameArea());
+ nRelPosX += aRectFnSet.XDiff( nLeftOrient, nLeftAnchor );
+ }
+
+ // adjust calculated relative horizontal position, in order to
+ // keep object inside 'page' alignment layout frame
+ const SwFrame& rEnvironmentLayFrame =
+ _rEnvOfObj.GetHoriEnvironmentLayoutFrame( _rHoriOrientFrame );
+ bool bFollowTextFlow = GetFrameFormat().GetFollowTextFlow().GetValue();
+ bool bWrapThrough = GetFrameFormat().GetSurround().GetSurround() != text::WrapTextMode_THROUGH;
+ // Don't try to keep wrap-though objects inside the cell, even if they are following text flow.
+ if (!rEnvironmentLayFrame.IsInTab() || !bFollowTextFlow || bWrapThrough)
+ {
+ nRelPosX = AdjustHoriRelPos( rEnvironmentLayFrame, nRelPosX );
+ }
+
+ // if object is a Writer fly frame and it's anchored to a content and
+ // it is horizontal positioned left or right, but not relative to character,
+ // it has to be drawn aside another object, which have the same horizontal
+ // position and lay below it.
+ if ( GetAnchoredObj().DynCastFlyFrame() &&
+ ( mpContact->ObjAnchoredAtPara() || mpContact->ObjAnchoredAtChar() ) &&
+ ( eHoriOrient == text::HoriOrientation::LEFT || eHoriOrient == text::HoriOrientation::RIGHT ) &&
+ eRelOrient != text::RelOrientation::CHAR )
+ {
+ nRelPosX = AdjustHoriRelPosForDrawAside( _rHoriOrientFrame,
+ nRelPosX, _nRelPosY,
+ eHoriOrient, eRelOrient,
+ _rLRSpacing, _rULSpacing,
+ bEvenPage );
+ }
+
+ // #i26791#
+ _roHoriOffsetToFrameAnchorPos = nOffset;
+
+ return nRelPosX;
+}
+
+// method incl. helper methods for adjusting proposed horizontal position,
+// if object has to draw aside another object.
+/** adjust calculated horizontal position in order to draw object
+ aside other objects with same positioning
+*/
+SwTwips SwAnchoredObjectPosition::AdjustHoriRelPosForDrawAside(
+ const SwFrame& _rHoriOrientFrame,
+ const SwTwips _nProposedRelPosX,
+ const SwTwips _nRelPosY,
+ const sal_Int16 _eHoriOrient,
+ const sal_Int16 _eRelOrient,
+ const SvxLRSpaceItem& _rLRSpacing,
+ const SvxULSpaceItem& _rULSpacing,
+ const bool _bEvenPage
+ ) const
+{
+ // #i26791#
+ if ( GetAnchorFrame().DynCastTextFrame() == nullptr ||
+ dynamic_cast<const SwFlyAtContentFrame*>( &GetAnchoredObj() ) == nullptr )
+ {
+ OSL_FAIL( "<SwAnchoredObjectPosition::AdjustHoriRelPosForDrawAside(..) - usage for wrong anchor type" );
+ return _nProposedRelPosX;
+ }
+
+ const SwTextFrame& rAnchorTextFrame = static_cast<const SwTextFrame&>(GetAnchorFrame());
+ // #i26791#
+ const SwFlyAtContentFrame& rFlyAtContentFrame =
+ static_cast<const SwFlyAtContentFrame&>(GetAnchoredObj());
+ const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
+ SwRectFnSet aRectFnSet(&_rHoriOrientFrame);
+
+ SwTwips nAdjustedRelPosX = _nProposedRelPosX;
+
+ // determine proposed object bound rectangle
+ Point aTmpPos = aRectFnSet.GetPos(rAnchorTextFrame.getFrameArea());
+ if( aRectFnSet.IsVert() )
+ {
+ aTmpPos.AdjustX( -(_nRelPosY + aObjBoundRect.Width()) );
+ aTmpPos.AdjustY(nAdjustedRelPosX );
+ }
+ else
+ {
+ aTmpPos.AdjustX(nAdjustedRelPosX );
+ aTmpPos.AdjustY(_nRelPosY );
+ }
+ SwRect aTmpObjRect( aTmpPos, aObjBoundRect.SSize() );
+
+ const sal_uInt32 nObjOrdNum = GetObject().GetOrdNum();
+ const SwPageFrame* pObjPage = rFlyAtContentFrame.FindPageFrame();
+ const SwFrame* pObjContext = ::FindContext( &rAnchorTextFrame, SwFrameType::Column );
+ SwNodeOffset nObjIndex = rAnchorTextFrame.GetTextNodeFirst()->GetIndex();
+ SwOrderIter aIter( pObjPage );
+ const SwFlyFrame* pFly = static_cast<const SwVirtFlyDrawObj*>(aIter.Bottom())->GetFlyFrame();
+ while ( pFly && nObjOrdNum > pFly->GetVirtDrawObj()->GetOrdNumDirect() )
+ {
+ if ( DrawAsideFly( pFly, aTmpObjRect, pObjContext, nObjIndex,
+ _bEvenPage, _eHoriOrient, _eRelOrient ) )
+ {
+ if( aRectFnSet.IsVert() )
+ {
+ const SvxULSpaceItem& rOtherUL = pFly->GetFormat()->GetULSpace();
+ const SwTwips nOtherTop = pFly->getFrameArea().Top() - rOtherUL.GetUpper();
+ const SwTwips nOtherBot = pFly->getFrameArea().Bottom() + rOtherUL.GetLower();
+ if ( nOtherTop <= aTmpObjRect.Bottom() + _rULSpacing.GetLower() &&
+ nOtherBot >= aTmpObjRect.Top() - _rULSpacing.GetUpper() )
+ {
+ if ( _eHoriOrient == text::HoriOrientation::LEFT )
+ {
+ SwTwips nTmp = nOtherBot + 1 + _rULSpacing.GetUpper() -
+ rAnchorTextFrame.getFrameArea().Top();
+ if ( nTmp > nAdjustedRelPosX &&
+ rAnchorTextFrame.getFrameArea().Top() + nTmp +
+ aObjBoundRect.Height() + _rULSpacing.GetLower()
+ <= pObjPage->getFrameArea().Height() + pObjPage->getFrameArea().Top() )
+ {
+ nAdjustedRelPosX = nTmp;
+ }
+ }
+ else if ( _eHoriOrient == text::HoriOrientation::RIGHT )
+ {
+ SwTwips nTmp = nOtherTop - 1 - _rULSpacing.GetLower() -
+ aObjBoundRect.Height() -
+ rAnchorTextFrame.getFrameArea().Top();
+ if ( nTmp < nAdjustedRelPosX &&
+ rAnchorTextFrame.getFrameArea().Top() + nTmp - _rULSpacing.GetUpper()
+ >= pObjPage->getFrameArea().Top() )
+ {
+ nAdjustedRelPosX = nTmp;
+ }
+ }
+ aTmpObjRect.Pos().setY( rAnchorTextFrame.getFrameArea().Top() +
+ nAdjustedRelPosX );
+ }
+ }
+ else
+ {
+ const SvxLRSpaceItem& rOtherLR = pFly->GetFormat()->GetLRSpace();
+ const SwTwips nOtherLeft = pFly->getFrameArea().Left() - rOtherLR.GetLeft();
+ const SwTwips nOtherRight = pFly->getFrameArea().Right() + rOtherLR.GetRight();
+ if( nOtherLeft <= aTmpObjRect.Right() + _rLRSpacing.GetRight() &&
+ nOtherRight >= aTmpObjRect.Left() - _rLRSpacing.GetLeft() )
+ {
+ if ( _eHoriOrient == text::HoriOrientation::LEFT )
+ {
+ SwTwips nTmp = nOtherRight + 1 + _rLRSpacing.GetLeft() -
+ rAnchorTextFrame.getFrameArea().Left();
+ if ( nTmp > nAdjustedRelPosX &&
+ rAnchorTextFrame.getFrameArea().Left() + nTmp +
+ aObjBoundRect.Width() + _rLRSpacing.GetRight()
+ <= pObjPage->getFrameArea().Width() + pObjPage->getFrameArea().Left() )
+ {
+ nAdjustedRelPosX = nTmp;
+ }
+ }
+ else if ( _eHoriOrient == text::HoriOrientation::RIGHT )
+ {
+ SwTwips nTmp = nOtherLeft - 1 - _rLRSpacing.GetRight() -
+ aObjBoundRect.Width() -
+ rAnchorTextFrame.getFrameArea().Left();
+ if ( nTmp < nAdjustedRelPosX &&
+ rAnchorTextFrame.getFrameArea().Left() + nTmp - _rLRSpacing.GetLeft()
+ >= pObjPage->getFrameArea().Left() )
+ {
+ nAdjustedRelPosX = nTmp;
+ }
+ }
+ aTmpObjRect.Pos().setX( rAnchorTextFrame.getFrameArea().Left() +
+ nAdjustedRelPosX );
+ }
+ } // end of <if (bVert)>
+ } // end of <if DrawAsideFly(..)>
+
+ pFly = static_cast<const SwVirtFlyDrawObj*>(aIter.Next())->GetFlyFrame();
+ } // end of <loop on fly frames
+
+ return nAdjustedRelPosX;
+}
+
+/** determine, if object has to draw aside given fly frame
+
+ method used by <AdjustHoriRelPosForDrawAside(..)>
+*/
+bool SwAnchoredObjectPosition::DrawAsideFly( const SwFlyFrame* _pFly,
+ const SwRect& _rObjRect,
+ const SwFrame* _pObjContext,
+ const SwNodeOffset _nObjIndex,
+ const bool _bEvenPage,
+ const sal_Int16 _eHoriOrient,
+ const sal_Int16 _eRelOrient
+ ) const
+{
+ bool bRetVal = false;
+
+ SwRectFnSet aRectFnSet(&GetAnchorFrame());
+
+ if ( _pFly->IsFlyAtContentFrame() &&
+ aRectFnSet.BottomDist( _pFly->getFrameArea(), aRectFnSet.GetTop(_rObjRect) ) < 0 &&
+ aRectFnSet.BottomDist( _rObjRect, aRectFnSet.GetTop(_pFly->getFrameArea()) ) < 0 &&
+ ::FindContext( _pFly->GetAnchorFrame(), SwFrameType::Column ) == _pObjContext )
+ {
+ SwNodeOffset nOtherIndex =
+ static_cast<const SwTextFrame*>(_pFly->GetAnchorFrame())->GetTextNodeFirst()->GetIndex();
+ if (sw::FrameContainsNode(static_cast<SwTextFrame const&>(*_pFly->GetAnchorFrame()), _nObjIndex)
+ || nOtherIndex < _nObjIndex)
+ {
+ const SwFormatHoriOrient& rHori = _pFly->GetFormat()->GetHoriOrient();
+ sal_Int16 eOtherRelOrient = rHori.GetRelationOrient();
+ if( text::RelOrientation::CHAR != eOtherRelOrient )
+ {
+ sal_Int16 eOtherHoriOrient = rHori.GetHoriOrient();
+ ToggleHoriOrientAndAlign( _bEvenPage && rHori.IsPosToggle(),
+ eOtherHoriOrient,
+ eOtherRelOrient );
+ if ( eOtherHoriOrient == _eHoriOrient &&
+ Minor_( _eRelOrient, eOtherRelOrient, text::HoriOrientation::LEFT == _eHoriOrient ) )
+ {
+ bRetVal = true;
+ }
+ }
+ }
+ }
+
+ return bRetVal;
+}
+
+/** determine, if object has to draw aside another object
+
+ the different alignments of the objects determines, if one has
+ to draw aside another one. Thus, the given alignment are checked
+ against each other, which one has to be drawn aside the other one.
+ depending on parameter _bLeft check is done for left or right
+ positioning.
+ method used by <DrawAsideFly(..)>
+*/
+bool SwAnchoredObjectPosition::Minor_( sal_Int16 _eRelOrient1,
+ sal_Int16 _eRelOrient2,
+ bool _bLeft )
+{
+ bool bRetVal;
+
+ // draw aside order for left horizontal position
+ //! one array entry for each value in text::RelOrientation
+ static sal_uInt16 const aLeft[ 10 ] =
+ { 5, 6, 0, 1, 8, 4, 7, 2, 3, 9 };
+ // draw aside order for right horizontal position
+ //! one array entry for each value in text::RelOrientation
+ static sal_uInt16 const aRight[ 10 ] =
+ { 5, 6, 0, 8, 1, 7, 4, 2, 3, 9 };
+
+ // decide depending on given order, which frame has to draw aside another frame
+ if( _bLeft )
+ bRetVal = aLeft[ _eRelOrient1 ] >= aLeft[ _eRelOrient2 ];
+ else
+ bRetVal = aRight[ _eRelOrient1 ] >= aRight[ _eRelOrient2 ];
+
+ return bRetVal;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx b/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx
new file mode 100644
index 000000000..e82659ccd
--- /dev/null
+++ b/sw/source/core/objectpositioning/ascharanchoredobjectposition.cxx
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <anchoreddrawobject.hxx>
+#include <ascharanchoredobjectposition.hxx>
+#include <frame.hxx>
+#include <txtfrm.hxx>
+#include <flyfrms.hxx>
+#include <svx/svdobj.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <osl/diagnose.h>
+#include <fmtornt.hxx>
+
+
+using namespace ::com::sun::star;
+
+namespace objectpositioning
+{
+
+/** constructor */
+SwAsCharAnchoredObjectPosition::SwAsCharAnchoredObjectPosition(
+ SdrObject& _rDrawObj,
+ const Point& _rProposedAnchorPos,
+ const AsCharFlags _nFlags,
+ const SwTwips _nLineAscent,
+ const SwTwips _nLineDescent,
+ const SwTwips _nLineAscentInclObjs,
+ const SwTwips _nLineDescentInclObjs )
+ : SwAnchoredObjectPosition( _rDrawObj ),
+ mrProposedAnchorPos( _rProposedAnchorPos ),
+ mnFlags( _nFlags ),
+ mnLineAscent( _nLineAscent ),
+ mnLineDescent( _nLineDescent ),
+ mnLineAscentInclObjs( _nLineAscentInclObjs ),
+ mnLineDescentInclObjs( _nLineDescentInclObjs ),
+ mnRelPos ( 0 ),
+ mnLineAlignment ( sw::LineAlign::NONE )
+{}
+
+/** destructor */
+SwAsCharAnchoredObjectPosition::~SwAsCharAnchoredObjectPosition()
+{}
+
+/** method to cast <SwAnchoredObjectPosition::GetAnchorFrame()> to needed type */
+const SwTextFrame& SwAsCharAnchoredObjectPosition::GetAnchorTextFrame() const
+{
+ assert( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame() ) &&
+ "SwAsCharAnchoredObjectPosition::GetAnchorTextFrame() - wrong anchor frame type" );
+
+ return static_cast<const SwTextFrame&>(GetAnchorFrame());
+}
+
+/** calculate position for object
+
+ OD 30.07.2003 #110978#
+ members <maAnchorPos>, <mnRelPos>, <maObjBoundRect> and
+ <mnLineAlignment> are calculated.
+ calculated position is set at the given object.
+*/
+void SwAsCharAnchoredObjectPosition::CalcPosition()
+{
+ const SwTextFrame& rAnchorFrame = GetAnchorTextFrame();
+ // swap anchor frame, if swapped. Note: destructor takes care of the 'undo'
+ SwFrameSwapper aFrameSwapper( &rAnchorFrame, false );
+
+ SwRectFnSet aRectFnSet(&rAnchorFrame);
+
+ Point aAnchorPos( mrProposedAnchorPos );
+
+ const SwFrameFormat& rFrameFormat = GetFrameFormat();
+
+ SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
+ SwTwips nObjWidth = aRectFnSet.GetWidth(aObjBoundRect);
+
+ // determine spacing values considering layout-/text-direction
+ const SvxLRSpaceItem& rLRSpace = rFrameFormat.GetLRSpace();
+ const SvxULSpaceItem& rULSpace = rFrameFormat.GetULSpace();
+ SwTwips nLRSpaceLeft, nLRSpaceRight, nULSpaceUpper, nULSpaceLower;
+ {
+ if ( rAnchorFrame.IsVertical() )
+ {
+ // Seems to be easier to do it all the horizontal way
+ // So, from now on think horizontal.
+ rAnchorFrame.SwitchVerticalToHorizontal( aObjBoundRect );
+ rAnchorFrame.SwitchVerticalToHorizontal( aAnchorPos );
+
+ // convert the spacing values
+ nLRSpaceLeft = rULSpace.GetUpper();
+ nLRSpaceRight = rULSpace.GetLower();
+ nULSpaceUpper = rLRSpace.GetRight();
+ nULSpaceLower = rLRSpace.GetLeft();
+ }
+ else
+ {
+ if ( rAnchorFrame.IsRightToLeft() )
+ {
+ nLRSpaceLeft = rLRSpace.GetRight();
+ nLRSpaceRight = rLRSpace.GetLeft();
+ }
+ else
+ {
+ nLRSpaceLeft = rLRSpace.GetLeft();
+ nLRSpaceRight = rLRSpace.GetRight();
+ }
+
+ nULSpaceUpper = rULSpace.GetUpper();
+ nULSpaceLower = rULSpace.GetLower();
+ }
+ }
+
+ // consider left and upper spacing by adjusting anchor position.
+ // left spacing is only considered, if requested.
+ if( mnFlags & AsCharFlags::UlSpace )
+ {
+ aAnchorPos.AdjustX(nLRSpaceLeft );
+ }
+ aAnchorPos.AdjustY(nULSpaceUpper );
+
+ // for drawing objects: consider difference between its bounding rectangle
+ // and its snapping rectangle by adjusting anchor position.
+ // left difference is only considered, if requested.
+ if( !IsObjFly() )
+ {
+ SwRect aSnapRect(GetObject().GetSnapRect());
+ if ( rAnchorFrame.IsVertical() )
+ {
+ rAnchorFrame.SwitchVerticalToHorizontal( aSnapRect );
+ }
+
+ if( mnFlags & AsCharFlags::UlSpace )
+ {
+ aAnchorPos.AdjustX(aSnapRect.Left() - aObjBoundRect.Left() );
+ }
+ aAnchorPos.AdjustY(aSnapRect.Top() - aObjBoundRect.Top() );
+ }
+
+ // enlarge bounding rectangle of object by its spacing.
+ aObjBoundRect.AddLeft( - nLRSpaceLeft );
+ aObjBoundRect.AddWidth( nLRSpaceRight );
+ aObjBoundRect.AddTop( - nULSpaceUpper );
+ aObjBoundRect.AddHeight( nULSpaceLower );
+
+ // calculate relative position to given base line.
+ const SwFormatVertOrient& rVert = rFrameFormat.GetVertOrient();
+ const SwTwips nObjBoundHeight = ( mnFlags & AsCharFlags::Rotate )
+ ? aObjBoundRect.Width()
+ : aObjBoundRect.Height();
+ const SwTwips nRelPos = GetRelPosToBase( nObjBoundHeight, rVert );
+
+ // for initial positioning:
+ // adjust the proposed anchor position by difference between
+ // calculated relative position to base line and current maximal line ascent.
+ // Note: In the following line formatting the base line will be adjusted
+ // by the same difference.
+ if( mnFlags & AsCharFlags::Init && nRelPos < 0 && mnLineAscentInclObjs < -nRelPos )
+ {
+ if( mnFlags & AsCharFlags::Rotate )
+ aAnchorPos.AdjustX( -(mnLineAscentInclObjs + nRelPos) );
+ else
+ aAnchorPos.AdjustY( -(mnLineAscentInclObjs + nRelPos) );
+ }
+
+ // consider BIDI-multiportion by adjusting proposed anchor position
+ if( mnFlags & AsCharFlags::Bidi )
+ aAnchorPos.AdjustX( -(aObjBoundRect.Width()) );
+
+ // calculate relative position considering rotation and inside rotation
+ // reverse direction.
+ Point aRelPos;
+ {
+ if( mnFlags & AsCharFlags::Rotate )
+ {
+ if( mnFlags & AsCharFlags::Reverse )
+ aRelPos.setX( -nRelPos - aObjBoundRect.Width() );
+ else
+ {
+ aRelPos.setX( nRelPos );
+ aRelPos.setY( -aObjBoundRect.Height() );
+ }
+ }
+ else
+ aRelPos.setY( nRelPos );
+ }
+
+ if( !IsObjFly() )
+ {
+ if( !( mnFlags & AsCharFlags::Quick ) )
+ {
+ // save calculated Y-position value for 'automatic' vertical positioning,
+ // in order to avoid a switch to 'manual' vertical positioning in
+ // <SwDrawContact::Changed_(..)>.
+ const sal_Int16 eVertOrient = rVert.GetVertOrient();
+ if( rVert.GetPos() != nRelPos && eVertOrient != text::VertOrientation::NONE )
+ {
+ SwFormatVertOrient aVert( rVert );
+ aVert.SetPos( nRelPos );
+ const_cast<SwFrameFormat&>(rFrameFormat).LockModify();
+ const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aVert );
+ const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify();
+ }
+
+ // determine absolute anchor position considering layout directions.
+ // Note: Use copy of <aAnchorPos>, because it's needed for
+ // setting relative position.
+ Point aAbsAnchorPos( aAnchorPos );
+ if ( rAnchorFrame.IsRightToLeft() )
+ {
+ rAnchorFrame.SwitchLTRtoRTL( aAbsAnchorPos );
+ aAbsAnchorPos.AdjustX( -nObjWidth );
+ }
+ if ( rAnchorFrame.IsVertical() )
+ rAnchorFrame.SwitchHorizontalToVertical( aAbsAnchorPos );
+
+ // set proposed anchor position at the drawing object.
+ // OD 2004-04-06 #i26791# - distinction between 'master' drawing
+ // object and 'virtual' drawing object no longer needed.
+ GetObject().SetAnchorPos( aAbsAnchorPos );
+
+ // move drawing object to set its correct relative position.
+ {
+ SwRect aSnapRect(GetObject().GetSnapRect());
+ if ( rAnchorFrame.IsVertical() )
+ rAnchorFrame.SwitchVerticalToHorizontal( aSnapRect );
+
+ Point aDiff;
+ if ( rAnchorFrame.IsRightToLeft() )
+ aDiff = aRelPos + aAbsAnchorPos - aSnapRect.TopLeft();
+ else
+ aDiff = aRelPos + aAnchorPos - aSnapRect.TopLeft();
+
+ if ( rAnchorFrame.IsVertical() )
+ aDiff = Point( -aDiff.Y(), aDiff.X() );
+
+ // OD 2004-04-06 #i26791# - distinction between 'master' drawing
+ // object and 'virtual' drawing object no longer needed.
+ GetObject().Move( Size( aDiff.X(), aDiff.Y() ) );
+ }
+ }
+
+ // switch horizontal, LTR anchor position to absolute values.
+ if ( rAnchorFrame.IsRightToLeft() )
+ {
+ rAnchorFrame.SwitchLTRtoRTL( aAnchorPos );
+ aAnchorPos.AdjustX( -nObjWidth );
+ }
+ if ( rAnchorFrame.IsVertical() )
+ rAnchorFrame.SwitchHorizontalToVertical( aAnchorPos );
+
+ // #i44347# - keep last object rectangle at anchored object
+ assert( dynamic_cast<const SwAnchoredDrawObject*>( &GetAnchoredObj() ) &&
+ "<SwAsCharAnchoredObjectPosition::CalcPosition()> - wrong type of anchored object." );
+ SwAnchoredDrawObject& rAnchoredDrawObj =
+ static_cast<SwAnchoredDrawObject&>( GetAnchoredObj() );
+ rAnchoredDrawObj.SetLastObjRect( rAnchoredDrawObj.GetObjRect().SVRect() );
+ }
+ else
+ {
+ // determine absolute anchor position and calculate corresponding
+ // relative position and its relative position attribute.
+ // Note: The relative position contains the spacing values.
+ Point aRelAttr;
+ if ( rAnchorFrame.IsRightToLeft() )
+ {
+ rAnchorFrame.SwitchLTRtoRTL( aAnchorPos );
+ aAnchorPos.AdjustX( -nObjWidth );
+ }
+ if ( rAnchorFrame.IsVertical() )
+ {
+ rAnchorFrame.SwitchHorizontalToVertical( aAnchorPos );
+ aRelAttr = Point( -nRelPos, 0 );
+ aRelPos = Point( -aRelPos.Y(), aRelPos.X() );
+ }
+ else
+ aRelAttr = Point( 0, nRelPos );
+
+ // OD 2004-03-23 #i26791#
+ assert( dynamic_cast<const SwFlyInContentFrame*>( &GetAnchoredObj()) &&
+ "<SwAsCharAnchoredObjectPosition::CalcPosition()> - wrong anchored object." );
+ const SwFlyInContentFrame& rFlyInContentFrame =
+ static_cast<const SwFlyInContentFrame&>(GetAnchoredObj());
+ if ( !(mnFlags & AsCharFlags::Quick) &&
+ ( aAnchorPos != rFlyInContentFrame.GetRefPoint() ||
+ aRelAttr != rFlyInContentFrame.GetCurrRelPos() ) )
+ {
+ // set new anchor position and relative position
+ SwFlyInContentFrame* pFlyInContentFrame = &const_cast<SwFlyInContentFrame&>(rFlyInContentFrame);
+ pFlyInContentFrame->SetRefPoint( aAnchorPos, aRelAttr, aRelPos );
+ if( nObjWidth != aRectFnSet.GetWidth(pFlyInContentFrame->getFrameArea()) )
+ {
+ // recalculate object bound rectangle, if object width has changed.
+ aObjBoundRect = GetAnchoredObj().GetObjRect();
+ aObjBoundRect.AddLeft( - rLRSpace.GetLeft() );
+ aObjBoundRect.AddWidth( rLRSpace.GetRight() );
+ aObjBoundRect.AddTop( - rULSpace.GetUpper() );
+ aObjBoundRect.AddHeight( rULSpace.GetLower() );
+ }
+ }
+ OSL_ENSURE( aRectFnSet.GetHeight(rFlyInContentFrame.getFrameArea()),
+ "SwAnchoredObjectPosition::CalcPosition(..) - fly frame has an invalid height" );
+ }
+
+ // keep calculated values
+ maAnchorPos = aAnchorPos;
+ mnRelPos = nRelPos;
+ maObjBoundRect = aObjBoundRect;
+}
+
+/** determine the relative position to base line for object position type AS_CHAR
+
+ OD 29.07.2003 #110978#
+ Note about values set at member <mnLineAlignment> -
+ value gives feedback for the line formatting.
+ 0 - no feedback; 1|2|3 - proposed formatting of characters
+ at top|at center|at bottom of line.
+*/
+SwTwips SwAsCharAnchoredObjectPosition::GetRelPosToBase(
+ const SwTwips _nObjBoundHeight,
+ const SwFormatVertOrient& _rVert )
+{
+ SwTwips nRelPosToBase = 0;
+
+ mnLineAlignment = sw::LineAlign::NONE;
+
+ const sal_Int16 eVertOrient = _rVert.GetVertOrient();
+
+ if ( eVertOrient == text::VertOrientation::NONE )
+ nRelPosToBase = _rVert.GetPos();
+ else
+ {
+ if ( eVertOrient == text::VertOrientation::CENTER )
+ nRelPosToBase -= _nObjBoundHeight / 2;
+ else if ( eVertOrient == text::VertOrientation::TOP )
+ nRelPosToBase -= _nObjBoundHeight;
+ else if ( eVertOrient == text::VertOrientation::BOTTOM )
+ nRelPosToBase = 0;
+ else if ( eVertOrient == text::VertOrientation::CHAR_CENTER )
+ nRelPosToBase -= ( _nObjBoundHeight + mnLineAscent - mnLineDescent ) / 2;
+ else if ( eVertOrient == text::VertOrientation::CHAR_TOP )
+ nRelPosToBase -= mnLineAscent;
+ else if ( eVertOrient == text::VertOrientation::CHAR_BOTTOM )
+ nRelPosToBase += mnLineDescent - _nObjBoundHeight;
+ else
+ {
+ if( _nObjBoundHeight >= mnLineAscentInclObjs + mnLineDescentInclObjs )
+ {
+ // object is at least as high as the line. Thus, no more is
+ // positioning necessary. Also, the max. ascent isn't changed.
+ nRelPosToBase -= mnLineAscentInclObjs;
+ if ( eVertOrient == text::VertOrientation::LINE_CENTER )
+ mnLineAlignment = sw::LineAlign::CENTER;
+ else if ( eVertOrient == text::VertOrientation::LINE_TOP )
+ mnLineAlignment = sw::LineAlign::TOP;
+ else if ( eVertOrient == text::VertOrientation::LINE_BOTTOM )
+ mnLineAlignment = sw::LineAlign::BOTTOM;
+ }
+ else if ( eVertOrient == text::VertOrientation::LINE_CENTER )
+ {
+ nRelPosToBase -= ( _nObjBoundHeight + mnLineAscentInclObjs - mnLineDescentInclObjs ) / 2;
+ mnLineAlignment = sw::LineAlign::CENTER;
+ }
+ else if ( eVertOrient == text::VertOrientation::LINE_TOP )
+ {
+ nRelPosToBase -= mnLineAscentInclObjs;
+ mnLineAlignment = sw::LineAlign::TOP;
+ }
+ else if ( eVertOrient == text::VertOrientation::LINE_BOTTOM )
+ {
+ nRelPosToBase += mnLineDescentInclObjs - _nObjBoundHeight;
+ mnLineAlignment = sw::LineAlign::BOTTOM;
+ }
+ }
+ }
+
+ return nRelPosToBase;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/objectpositioning/environmentofanchoredobject.cxx b/sw/source/core/objectpositioning/environmentofanchoredobject.cxx
new file mode 100644
index 000000000..e6529be38
--- /dev/null
+++ b/sw/source/core/objectpositioning/environmentofanchoredobject.cxx
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <environmentofanchoredobject.hxx>
+#include <frame.hxx>
+#include <pagefrm.hxx>
+#include <osl/diagnose.h>
+
+namespace objectpositioning
+{
+
+SwEnvironmentOfAnchoredObject::SwEnvironmentOfAnchoredObject(
+ const bool _bFollowTextFlow )
+ : mbFollowTextFlow( _bFollowTextFlow )
+{}
+
+/** determine environment layout frame for possible horizontal object positions */
+const SwLayoutFrame& SwEnvironmentOfAnchoredObject::GetHoriEnvironmentLayoutFrame(
+ const SwFrame& _rHoriOrientFrame ) const
+{
+ const SwFrame* pHoriEnvironmentLayFrame = &_rHoriOrientFrame;
+
+ if ( !mbFollowTextFlow )
+ {
+ // No exception any more for page alignment.
+ // the page frame determines the horizontal layout environment.
+ pHoriEnvironmentLayFrame = _rHoriOrientFrame.FindPageFrame();
+ }
+ else
+ {
+ while ( !pHoriEnvironmentLayFrame->IsCellFrame() &&
+ !pHoriEnvironmentLayFrame->IsFlyFrame() &&
+ !pHoriEnvironmentLayFrame->IsPageFrame() )
+ {
+ pHoriEnvironmentLayFrame = pHoriEnvironmentLayFrame->GetUpper();
+ OSL_ENSURE( pHoriEnvironmentLayFrame,
+ "SwEnvironmentOfAnchoredObject::GetHoriEnvironmentLayoutFrame(..) - no page|fly|cell frame found" );
+ }
+ }
+
+ assert( dynamic_cast< const SwLayoutFrame *>( pHoriEnvironmentLayFrame ) &&
+ "SwEnvironmentOfAnchoredObject::GetHoriEnvironmentLayoutFrame(..) - found frame isn't a layout frame" );
+
+ return static_cast<const SwLayoutFrame&>(*pHoriEnvironmentLayFrame);
+}
+
+/** determine environment layout frame for possible vertical object positions */
+const SwLayoutFrame& SwEnvironmentOfAnchoredObject::GetVertEnvironmentLayoutFrame(
+ const SwFrame& _rVertOrientFrame ) const
+{
+ const SwFrame* pVertEnvironmentLayFrame = &_rVertOrientFrame;
+
+ if ( !mbFollowTextFlow )
+ {
+ // No exception any more for page alignment.
+ // the page frame determines the vertical layout environment.
+ pVertEnvironmentLayFrame = _rVertOrientFrame.FindPageFrame();
+ }
+ else
+ {
+ while ( !pVertEnvironmentLayFrame->IsCellFrame() &&
+ !pVertEnvironmentLayFrame->IsFlyFrame() &&
+ !pVertEnvironmentLayFrame->IsHeaderFrame() &&
+ !pVertEnvironmentLayFrame->IsFooterFrame() &&
+ !pVertEnvironmentLayFrame->IsFootnoteFrame() &&
+ !pVertEnvironmentLayFrame->IsPageBodyFrame() &&
+ !pVertEnvironmentLayFrame->IsPageFrame() )
+ {
+ pVertEnvironmentLayFrame = pVertEnvironmentLayFrame->GetUpper();
+ OSL_ENSURE( pVertEnvironmentLayFrame,
+ "SwEnvironmentOfAnchoredObject::GetVertEnvironmentLayoutFrame(..) - proposed frame not found" );
+ }
+ }
+
+ assert( dynamic_cast< const SwLayoutFrame *>( pVertEnvironmentLayFrame ) &&
+ "SwEnvironmentOfAnchoredObject::GetVertEnvironmentLayoutFrame(..) - found frame isn't a layout frame" );
+
+ return static_cast<const SwLayoutFrame&>(*pVertEnvironmentLayFrame);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
new file mode 100644
index 000000000..700b737b1
--- /dev/null
+++ b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
@@ -0,0 +1,1217 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tocntntanchoredobjectposition.hxx>
+#include <anchoredobject.hxx>
+#include <frame.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <sectfrm.hxx>
+#include <tabfrm.hxx>
+#include <rootfrm.hxx>
+#include <viewopt.hxx>
+#include <viewsh.hxx>
+#include <frmfmt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfsize.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <svx/svdobj.hxx>
+#include <osl/diagnose.h>
+#include <environmentofanchoredobject.hxx>
+#include <frmatr.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <sortedobjs.hxx>
+#include <textboxhelper.hxx>
+
+using namespace ::com::sun::star;
+
+namespace objectpositioning
+{
+SwToContentAnchoredObjectPosition::SwToContentAnchoredObjectPosition( SdrObject& _rDrawObj )
+ : SwAnchoredObjectPosition ( _rDrawObj ),
+ mpVertPosOrientFrame( nullptr ),
+ mbAnchorToChar ( false ),
+ mpToCharOrientFrame( nullptr ),
+ mpToCharRect( nullptr ),
+ // #i22341#
+ mnToCharTopOfLine( 0 )
+{}
+
+SwToContentAnchoredObjectPosition::~SwToContentAnchoredObjectPosition()
+{}
+
+bool SwToContentAnchoredObjectPosition::IsAnchoredToChar() const
+{
+ return mbAnchorToChar;
+}
+
+const SwFrame* SwToContentAnchoredObjectPosition::ToCharOrientFrame() const
+{
+ return mpToCharOrientFrame;
+}
+
+const SwRect* SwToContentAnchoredObjectPosition::ToCharRect() const
+{
+ return mpToCharRect;
+}
+
+// #i22341#
+SwTwips SwToContentAnchoredObjectPosition::ToCharTopOfLine() const
+{
+ return mnToCharTopOfLine;
+}
+
+SwTextFrame& SwToContentAnchoredObjectPosition::GetAnchorTextFrame() const
+{
+ assert( dynamic_cast<const SwTextFrame*>( &GetAnchorFrame()) &&
+ "SwToContentAnchoredObjectPosition::GetAnchorTextFrame() - wrong anchor frame type" );
+
+ return static_cast<SwTextFrame&>(GetAnchorFrame());
+}
+
+// #i23512#
+static bool lcl_DoesVertPosFits( const SwTwips _nRelPosY,
+ const SwTwips _nAvail,
+ const SwLayoutFrame* _pUpperOfOrientFrame,
+ const bool _bBrowse,
+ const bool _bGrowInTable,
+ SwLayoutFrame*& _orpLayoutFrameToGrow )
+{
+ bool bVertPosFits = false;
+
+ if ( _nRelPosY <= _nAvail )
+ {
+ bVertPosFits = true;
+ }
+ else if ( _bBrowse )
+ {
+ if ( _pUpperOfOrientFrame->IsInSct() )
+ {
+ SwSectionFrame* pSctFrame =
+ const_cast<SwSectionFrame*>(_pUpperOfOrientFrame->FindSctFrame());
+ bVertPosFits = pSctFrame->GetUpper()->Grow( _nRelPosY - _nAvail, true ) > 0;
+ // Note: do not provide a layout frame for a grow.
+ }
+ else
+ {
+ bVertPosFits = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame)->
+ Grow( _nRelPosY - _nAvail, true ) > 0;
+ if ( bVertPosFits )
+ _orpLayoutFrameToGrow = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame);
+ }
+ }
+ else if ( _pUpperOfOrientFrame->IsInTab() && _bGrowInTable )
+ {
+ // #i45085# - check, if upper frame would grow the
+ // expected amount of twips.
+ const SwTwips nTwipsGrown = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame)->
+ Grow( _nRelPosY - _nAvail, true );
+ bVertPosFits = ( nTwipsGrown == ( _nRelPosY - _nAvail ) );
+ if ( bVertPosFits )
+ _orpLayoutFrameToGrow = const_cast<SwLayoutFrame*>(_pUpperOfOrientFrame);
+ }
+
+ return bVertPosFits;
+}
+
+void SwToContentAnchoredObjectPosition::CalcPosition()
+{
+ // get format of object
+ const SwFrameFormat& rFrameFormat = GetFrameFormat();
+
+ // declare and set <pFooter> to footer frame, if object is anchored
+ // at a frame belonging to the footer.
+ const SwFrame* pFooter = GetAnchorFrame().FindFooterOrHeader();
+ if ( pFooter && !pFooter->IsFooterFrame() )
+ pFooter = nullptr;
+
+ // declare and set <bBrowse> to true, if document is in browser mode and
+ // object is anchored at the body, but not at frame belonging to a table.
+ bool bBrowse = GetAnchorFrame().IsInDocBody() && !GetAnchorFrame().IsInTab();
+ if( bBrowse )
+ {
+ const SwViewShell *pSh = GetAnchorFrame().getRootFrame()->GetCurrShell();
+ if( !pSh || !pSh->GetViewOptions()->getBrowseMode() )
+ bBrowse = false;
+ }
+
+ // determine left/right and its upper/lower spacing.
+ const SvxLRSpaceItem &rLR = rFrameFormat.GetLRSpace();
+ const SvxULSpaceItem &rUL = rFrameFormat.GetULSpace();
+
+ // determine, if object has no surrounding.
+ const SwFormatSurround& rSurround = rFrameFormat.GetSurround();
+ const bool bNoSurround = rSurround.GetSurround() == css::text::WrapTextMode_NONE;
+ const bool bWrapThrough = rSurround.GetSurround() == css::text::WrapTextMode_THROUGH;
+
+ // new class <SwEnvironmentOfAnchoredObject>
+ SwEnvironmentOfAnchoredObject aEnvOfObj( DoesObjFollowsTextFlow() );
+
+ // #i18732# - grow only, if object has to follow the text flow
+ const bool bGrow = DoesObjFollowsTextFlow() &&
+ ( !GetAnchorFrame().IsInTab() ||
+ !rFrameFormat.GetFrameSize().GetHeightPercent() );
+
+ // get text frame the object is anchored at
+ const SwTextFrame& rAnchorTextFrame = GetAnchorTextFrame();
+ SwRectFnSet aRectFnSet(&rAnchorTextFrame);
+
+ const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
+
+ // local variable keeping the calculated relative position; initialized with
+ // current relative position.
+ // #i26791# - use new object instance of <SwAnchoredObject>
+ Point aRelPos( GetAnchoredObj().GetCurrRelPos() );
+
+ SwTwips nRelDiff = 0;
+
+ bool bMoveable = rAnchorTextFrame.IsMoveable();
+
+ // determine frame the object position has to be oriented at.
+ const SwTextFrame* pOrientFrame = &rAnchorTextFrame;
+ const SwTextFrame* pAnchorFrameForVertPos;
+ {
+ // if object is at-character anchored, determine character-rectangle
+ // and frame, position has to be oriented at.
+ mbAnchorToChar = (RndStdIds::FLY_AT_CHAR == rFrameFormat.GetAnchor().GetAnchorId());
+ if ( mbAnchorToChar )
+ {
+ const SwFormatAnchor& rAnch = rFrameFormat.GetAnchor();
+ // #i26791# - use new object instance of <SwAnchoredObject>
+ // Due to table break algorithm the character
+ // rectangle can have no height. Thus, check also the width
+ if ( ( !GetAnchoredObj().GetLastCharRect().Height() &&
+ !GetAnchoredObj().GetLastCharRect().Width() ) ||
+ !GetAnchoredObj().GetLastTopOfLine() )
+ {
+ GetAnchoredObj().CheckCharRectAndTopOfLine( false );
+ // Due to table break algorithm the character
+ // rectangle can have no height. Thus, check also the width
+ if ( ( !GetAnchoredObj().GetLastCharRect().Height() &&
+ !GetAnchoredObj().GetLastCharRect().Width() ) ||
+ !GetAnchoredObj().GetLastTopOfLine() )
+ {
+ // Get default for <mpVertPosOrientFrame>, if it's not set.
+ if ( !mpVertPosOrientFrame )
+ {
+ mpVertPosOrientFrame = rAnchorTextFrame.GetUpper();
+ }
+ return;
+ }
+ }
+ mpToCharRect = &(GetAnchoredObj().GetLastCharRect());
+ // #i22341# - get top of line, in which the anchor character is.
+ mnToCharTopOfLine = GetAnchoredObj().GetLastTopOfLine();
+ pOrientFrame = &(const_cast<SwTextFrame&>(rAnchorTextFrame).GetFrameAtOfst(
+ rAnchorTextFrame.MapModelToViewPos(*rAnch.GetContentAnchor())));
+ mpToCharOrientFrame = pOrientFrame;
+ }
+ }
+ aRectFnSet.Refresh(pOrientFrame);
+
+ // determine vertical position
+ {
+
+ // determine vertical positioning and alignment attributes
+ SwFormatVertOrient aVert( rFrameFormat.GetVertOrient() );
+
+ // #i18732# - determine layout frame for vertical
+ // positions aligned to 'page areas'.
+ const SwLayoutFrame& rPageAlignLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pOrientFrame );
+
+ if ( aVert.GetVertOrient() != text::VertOrientation::NONE )
+ {
+ // #i18732# - adjustments for follow text flow or not
+ // AND vertical alignment at 'page areas'.
+ SwTwips nAlignAreaHeight;
+ SwTwips nAlignAreaOffset;
+ GetVertAlignmentValues( *pOrientFrame, rPageAlignLayFrame,
+ aVert.GetRelationOrient(),
+ nAlignAreaHeight, nAlignAreaOffset );
+
+ SwRect aHeaderRect;
+ const SwPageFrame* aPageFrame = pOrientFrame->FindPageFrame();
+ const SwHeaderFrame* pHeaderFrame = aPageFrame->GetHeaderFrame();
+ if (pHeaderFrame)
+ aHeaderRect = pHeaderFrame->GetPaintArea();
+ const SwTwips nTopMarginHeight = aPageFrame->GetTopMargin() + aHeaderRect.Height();
+ const SwTwips nHeightBetweenOffsetAndMargin = nAlignAreaOffset + nTopMarginHeight;
+
+ // determine relative vertical position
+ SwTwips nRelPosY = nAlignAreaOffset;
+ const SwTwips nObjHeight = aRectFnSet.GetHeight(aObjBoundRect);
+ const SwTwips nUpperSpace = aRectFnSet.IsVert()
+ ? ( aRectFnSet.IsVertL2R()
+ ? rLR.GetLeft()
+ : rLR.GetRight() )
+ : rUL.GetUpper();
+ // --> OD 2009-08-31 #monglianlayout#
+ const SwTwips nLowerSpace = aRectFnSet.IsVert()
+ ? ( aRectFnSet.IsVertL2R()
+ ? rLR.GetLeft()
+ : rLR.GetRight() )
+ : rUL.GetLower();
+ switch ( aVert.GetVertOrient() )
+ {
+ case text::VertOrientation::CHAR_BOTTOM:
+ {
+ if ( mbAnchorToChar )
+ {
+ // bottom (to character anchored)
+ nRelPosY += nAlignAreaHeight + nUpperSpace;
+ if ( aRectFnSet.IsVert() && !aRectFnSet.IsVertL2R() )
+ {
+ nRelPosY += nObjHeight;
+ }
+ break;
+ }
+ [[fallthrough]];
+ }
+ case text::VertOrientation::TOP:
+ {
+ // #i22341# - special case for vertical
+ // alignment at top of line
+ if ( mbAnchorToChar &&
+ aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ nRelPosY -= (nObjHeight + nLowerSpace);
+ }
+ else
+ {
+ nRelPosY += nUpperSpace;
+ }
+ }
+ break;
+ // #i22341#
+ case text::VertOrientation::LINE_TOP:
+ {
+ if ( mbAnchorToChar &&
+ aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ nRelPosY -= (nObjHeight + nLowerSpace);
+ }
+ else
+ {
+ OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - unknown combination of vertical position and vertical alignment." );
+ }
+ }
+ break;
+ case text::VertOrientation::CENTER:
+ {
+ if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_TOP)
+ nRelPosY = (nAlignAreaOffset / 2) - (nObjHeight / 2) + (nHeightBetweenOffsetAndMargin / 2);
+ else
+ nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2);
+ }
+ break;
+ // #i22341#
+ case text::VertOrientation::LINE_CENTER:
+ {
+ if ( mbAnchorToChar &&
+ aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ nRelPosY += (nAlignAreaHeight / 2) - (nObjHeight / 2);
+ }
+ else
+ {
+ OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - unknown combination of vertical position and vertical alignment." );
+ }
+ }
+ break;
+ case text::VertOrientation::BOTTOM:
+ {
+ if ( ( aVert.GetRelationOrient() == text::RelOrientation::FRAME ||
+ aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ) &&
+ bNoSurround )
+ {
+ // bottom (aligned to 'paragraph areas')
+ nRelPosY += nAlignAreaHeight + nUpperSpace;
+ }
+ else
+ {
+ // #i22341# - special case for vertical
+ // alignment at top of line
+ if ( mbAnchorToChar &&
+ aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ nRelPosY += nUpperSpace;
+ }
+ else
+ {
+ if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_TOP)
+ nRelPosY = 0 - (nObjHeight + nLowerSpace) + nHeightBetweenOffsetAndMargin;
+ else
+ nRelPosY += nAlignAreaHeight - (nObjHeight + nLowerSpace);
+ }
+ }
+ }
+ break;
+ // #i22341#
+ case text::VertOrientation::LINE_BOTTOM:
+ {
+ if ( mbAnchorToChar &&
+ aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE )
+ {
+ nRelPosY += nUpperSpace;
+ }
+ else
+ {
+ OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - unknown combination of vertical position and vertical alignment." );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ // adjust relative position by distance between anchor frame and
+ // the frame, the object is oriented at.
+ // #i28701# - correction: adjust relative position,
+ // only if the floating screen object has to follow the text flow.
+ if ( DoesObjFollowsTextFlow() && pOrientFrame != &rAnchorTextFrame )
+ {
+ // #i11860# - use new method <GetTopForObjPos>
+ // to get top of frame for object positioning.
+ const SwTwips nTopOfOrient = GetTopForObjPos( *pOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ nRelPosY += aRectFnSet.YDiff( nTopOfOrient,
+ GetTopForObjPos( rAnchorTextFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() ) );
+ }
+
+ // #i42124# - capture object inside vertical
+ // layout environment.
+ {
+ const SwTwips nTopOfAnch =
+ GetTopForObjPos( *pOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ const SwLayoutFrame& rVertEnvironLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame(
+ *(pOrientFrame->GetUpper()) );
+ const bool bCheckBottom = !DoesObjFollowsTextFlow();
+ nRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(),
+ rVertEnvironLayFrame, nRelPosY,
+ DoesObjFollowsTextFlow(),
+ bCheckBottom );
+ }
+
+ // keep calculated relative vertical position - needed for filters
+ // (including the xml-filter)
+ {
+ // determine position
+ SwTwips nAttrRelPosY = nRelPosY - nAlignAreaOffset;
+ // set
+ if ( nAttrRelPosY != aVert.GetPos() )
+ {
+ aVert.SetPos( nAttrRelPosY );
+ const_cast<SwFrameFormat&>(rFrameFormat).LockModify();
+ const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aVert );
+ const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify();
+ }
+ }
+
+ // determine absolute 'vertical' position, depending on layout-direction
+ // #i26791# - determine offset to 'vertical' frame
+ // anchor position, depending on layout-direction
+ if ( aRectFnSet.IsVert() )
+ {
+ aRelPos.setX( nRelPosY );
+ maOffsetToFrameAnchorPos.setX( nAlignAreaOffset );
+ }
+ else
+ {
+ aRelPos.setY( nRelPosY );
+ maOffsetToFrameAnchorPos.setY( nAlignAreaOffset );
+ }
+ }
+
+ // Determine upper of frame vertical position is oriented at.
+ // #i28701# - determine 'virtual' anchor frame.
+ // This frame is used in the following instead of the 'real' anchor
+ // frame <rAnchorTextFrame> for the 'vertical' position in all cases.
+ const SwLayoutFrame* pUpperOfOrientFrame = nullptr;
+ {
+ // #i28701# - As long as the anchor frame is on the
+ // same page as <pOrientFrame> and the vertical position isn't aligned
+ // automatic at the anchor character or the top of the line of the
+ // anchor character, the anchor frame determines the vertical position.
+ if ( &rAnchorTextFrame == pOrientFrame ||
+ ( rAnchorTextFrame.FindPageFrame() == pOrientFrame->FindPageFrame() &&
+ aVert.GetVertOrient() == text::VertOrientation::NONE &&
+ aVert.GetRelationOrient() != text::RelOrientation::CHAR &&
+ aVert.GetRelationOrient() != text::RelOrientation::TEXT_LINE ) )
+ {
+ pUpperOfOrientFrame = rAnchorTextFrame.GetUpper();
+ pAnchorFrameForVertPos = &rAnchorTextFrame;
+ }
+ else
+ {
+ pUpperOfOrientFrame = pOrientFrame->GetUpper();
+ pAnchorFrameForVertPos = pOrientFrame;
+ }
+ }
+
+ // ignore one-column sections.
+ // #i23512# - correction: also ignore one-columned
+ // sections with footnotes/endnotes
+ if ( pUpperOfOrientFrame->IsInSct() )
+ {
+ const SwSectionFrame* pSctFrame = pUpperOfOrientFrame->FindSctFrame();
+ const bool bIgnoreSection = pUpperOfOrientFrame->IsSctFrame() ||
+ ( pSctFrame->Lower()->IsColumnFrame() &&
+ !pSctFrame->Lower()->GetNext() );
+ if ( bIgnoreSection )
+ pUpperOfOrientFrame = pSctFrame->GetUpper();
+ }
+
+ if ( aVert.GetVertOrient() == text::VertOrientation::NONE )
+ {
+ // local variable <nRelPosY> for calculation of relative vertical
+ // distance to anchor.
+ SwTwips nRelPosY = 0;
+ // #i26791# - local variable <nVertOffsetToFrameAnchorPos>
+ // for determination of the 'vertical' offset to the frame anchor
+ // position
+ SwTwips nVertOffsetToFrameAnchorPos( 0 );
+ // #i22341# - add special case for vertical alignment
+ // at top of line.
+ if ( mbAnchorToChar &&
+ ( aVert.GetRelationOrient() == text::RelOrientation::CHAR ||
+ aVert.GetRelationOrient() == text::RelOrientation::TEXT_LINE ) )
+ {
+ // #i11860# - use new method <GetTopForObjPos>
+ // to get top of frame for object positioning.
+ SwTwips nTopOfOrient = GetTopForObjPos( *pOrientFrame, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ if ( aVert.GetRelationOrient() == text::RelOrientation::CHAR )
+ {
+ nVertOffsetToFrameAnchorPos = aRectFnSet.YDiff(
+ aRectFnSet.GetBottom(*ToCharRect()),
+ nTopOfOrient );
+ }
+ else
+ {
+ nVertOffsetToFrameAnchorPos = aRectFnSet.YDiff( ToCharTopOfLine(),
+ nTopOfOrient );
+ }
+ nRelPosY = nVertOffsetToFrameAnchorPos - aVert.GetPos();
+ }
+ else
+ {
+ // #i28701# - correction: use <pAnchorFrameForVertPos>
+ // instead of <pOrientFrame> and do not adjust relative position
+ // to get correct vertical position.
+ nVertOffsetToFrameAnchorPos = 0;
+ // #i11860# - use new method <GetTopForObjPos>
+ // to get top of frame for object positioning.
+ const SwTwips nTopOfOrient =
+ GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ // Increase <nRelPosY> by margin height,
+ // if position is vertical aligned to "paragraph text area"
+ if ( aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA )
+ {
+ // #i11860# - consider upper space amount of previous frame
+ SwTwips nTopMargin = aRectFnSet.GetTopMargin(*pAnchorFrameForVertPos);
+ if ( pAnchorFrameForVertPos->IsTextFrame() )
+ {
+ nTopMargin -= pAnchorFrameForVertPos->
+ GetUpperSpaceAmountConsideredForPrevFrameAndPageGrid();
+ }
+ nVertOffsetToFrameAnchorPos += nTopMargin;
+ }
+ // #i18732# - adjust <nRelPosY> by difference
+ // between 'page area' and 'anchor' frame, if position is
+ // vertical aligned to 'page areas'
+ else if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_FRAME
+ || aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_TOP)
+ {
+ nVertOffsetToFrameAnchorPos += aRectFnSet.YDiff(
+ aRectFnSet.GetTop(rPageAlignLayFrame.getFrameArea()),
+ nTopOfOrient );
+ }
+ else if ( aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA )
+ {
+ SwRect aPgPrtRect( rPageAlignLayFrame.getFrameArea() );
+ if ( rPageAlignLayFrame.IsPageFrame() )
+ {
+ aPgPrtRect =
+ static_cast<const SwPageFrame&>(rPageAlignLayFrame).PrtWithoutHeaderAndFooter();
+ }
+ nVertOffsetToFrameAnchorPos += aRectFnSet.YDiff(
+ aRectFnSet.GetTop(aPgPrtRect),
+ nTopOfOrient );
+ }
+ else if (aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA_BOTTOM)
+ {
+ // The anchored object is relative from the bottom of the page's print area.
+ SwRect aPgPrtRect(rPageAlignLayFrame.getFrameArea());
+ if (rPageAlignLayFrame.IsPageFrame())
+ {
+ auto& rPageFrame = static_cast<const SwPageFrame&>(rPageAlignLayFrame);
+ aPgPrtRect = rPageFrame.PrtWithoutHeaderAndFooter();
+ }
+ SwTwips nPageBottom = aRectFnSet.GetBottom(aPgPrtRect);
+ nVertOffsetToFrameAnchorPos += aRectFnSet.YDiff(nPageBottom, nTopOfOrient);
+ }
+ nRelPosY = nVertOffsetToFrameAnchorPos + aVert.GetPos();
+ }
+
+ // <pUpperOfOrientFrame>: layout frame, at which the position has to
+ // is oriented at
+ // <nRelPosY>: rest of the relative distance in the current
+ // layout frame
+ // <nAvail>: space, which is available in the current
+ // layout frame
+
+ // #i26791# - determine offset to 'vertical'
+ // frame anchor position, depending on layout-direction
+ if ( aRectFnSet.IsVert() )
+ maOffsetToFrameAnchorPos.setX( nVertOffsetToFrameAnchorPos );
+ else
+ maOffsetToFrameAnchorPos.setY( nVertOffsetToFrameAnchorPos );
+ // #i11860# - use new method <GetTopForObjPos>
+ // to get top of frame for object positioning.
+ const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ if( nRelPosY <= 0 )
+ {
+ // Allow negative position, but keep it
+ // inside environment layout frame.
+ const SwLayoutFrame& rVertEnvironLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame );
+ // #i31805# - do not check, if bottom of
+ // anchored object would fit into environment layout frame, if
+ // anchored object has to follow the text flow.
+ const bool bCheckBottom = !DoesObjFollowsTextFlow();
+ nRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(),
+ rVertEnvironLayFrame, nRelPosY,
+ DoesObjFollowsTextFlow(),
+ bCheckBottom );
+ if ( aRectFnSet.IsVert() )
+ aRelPos.setX( nRelPosY );
+ else
+ aRelPos.setY( nRelPosY );
+ }
+ else
+ {
+ aRectFnSet.Refresh(pAnchorFrameForVertPos);
+ SwTwips nAvail =
+ aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame),
+ nTopOfAnch );
+ const bool bInFootnote = pAnchorFrameForVertPos->IsInFootnote();
+ while ( nRelPosY )
+ {
+ // #i23512# - correction:
+ // consider section frame for grow in online layout.
+ // use new local method <lcl_DoesVertPosFits(..)>
+ SwLayoutFrame* pLayoutFrameToGrow = nullptr;
+ const bool bDoesVertPosFits = lcl_DoesVertPosFits(
+ nRelPosY, nAvail, pUpperOfOrientFrame, bBrowse,
+ bGrow, pLayoutFrameToGrow );
+
+ if ( bDoesVertPosFits )
+ {
+ SwTwips nTmpRelPosY =
+ aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame),
+ nTopOfAnch ) -
+ nAvail + nRelPosY;
+ // #i28701# - adjust calculated
+ // relative vertical position to object's environment.
+ const SwFrame& rVertEnvironLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame );
+ // Do not check, if bottom of
+ // anchored object would fit into environment layout
+ // frame, if anchored object has to follow the text flow.
+ const bool bCheckBottom = !DoesObjFollowsTextFlow();
+ nTmpRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(),
+ rVertEnvironLayFrame,
+ nTmpRelPosY,
+ DoesObjFollowsTextFlow(),
+ bCheckBottom );
+ if ( aRectFnSet.IsVert() )
+ aRelPos.setX( nTmpRelPosY );
+ else
+ aRelPos.setY( nTmpRelPosY );
+
+ // #i23512# - use local variable
+ // <pLayoutFrameToGrow> provided by new method
+ // <lcl_DoesVertPosFits(..)>.
+ if ( pLayoutFrameToGrow )
+ {
+ // No need to grow the anchor cell in case the follow-text-flow object
+ // is wrap-though.
+ if (!GetAnchorFrame().IsInTab() || !DoesObjFollowsTextFlow() || !bWrapThrough)
+ {
+ pLayoutFrameToGrow->Grow( nRelPosY - nAvail );
+ }
+ }
+ nRelPosY = 0;
+ }
+ else
+ {
+ // #i26495# - floating screen objects,
+ // which are anchored inside a table, doesn't follow
+ // the text flow.
+ if ( DoesObjFollowsTextFlow() &&
+ ( aVert.GetRelationOrient() != text::RelOrientation::PAGE_FRAME &&
+ aVert.GetRelationOrient() != text::RelOrientation::PAGE_PRINT_AREA ) &&
+ !GetAnchorFrame().IsInTab() )
+ {
+ if ( bMoveable )
+ {
+ // follow the text flow
+ nRelPosY -= nAvail;
+ MakePageType eMakePage = bInFootnote ? MAKEPAGE_NONE
+ : MAKEPAGE_APPEND;
+ const bool bInSct = pUpperOfOrientFrame->IsInSct();
+ if( bInSct )
+ eMakePage = MAKEPAGE_NOSECTION;
+
+ const SwLayoutFrame* pTmp =
+ pUpperOfOrientFrame->GetLeaf( eMakePage, true, &rAnchorTextFrame );
+ if ( pTmp &&
+ ( !bInSct ||
+ pUpperOfOrientFrame->FindSctFrame()->IsAnFollow( pTmp->FindSctFrame() ) ) )
+ {
+ pUpperOfOrientFrame = pTmp;
+ bMoveable = rAnchorTextFrame.IsMoveable( pUpperOfOrientFrame );
+ aRectFnSet.Refresh(pUpperOfOrientFrame);
+ nAvail = aRectFnSet.GetHeight(pUpperOfOrientFrame->getFramePrintArea());
+ }
+ else
+ {
+ // if there isn't enough space in the (columned)
+ // section, leave it and set available space <nAvail>
+ // to the space below the section.
+ // if the new available space isn't also enough,
+ // new pages can be created.
+ if( bInSct )
+ {
+ const SwFrame* pSct = pUpperOfOrientFrame->FindSctFrame();
+ pUpperOfOrientFrame = pSct->GetUpper();
+ nAvail = aRectFnSet.YDiff(
+ aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame),
+ aRectFnSet.GetPrtBottom(*pSct) );
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 1
+ OSL_FAIL( "<SwToContentAnchoredObjectPosition::CalcPosition()> - !bInSct" );
+#endif
+ nRelDiff = nRelPosY;
+ nRelPosY = 0;
+ }
+ }
+ }
+ else
+ {
+ nRelPosY = 0;
+ }
+ }
+ else
+ {
+ // #i18732# - do not follow text flow respectively
+ // align at 'page areas', but stay inside given environment
+ const SwFrame& rVertEnvironLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame );
+ nRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(),
+ rVertEnvironLayFrame,
+ nRelPosY,
+ DoesObjFollowsTextFlow() );
+ if( aRectFnSet.IsVert() )
+ aRelPos.setX( nRelPosY );
+ else
+ aRelPos.setY( nRelPosY );
+ nRelPosY = 0;
+ }
+ }
+ } // end of <while ( nRelPosY )>
+ } // end of else <nRelPosY <= 0>
+ } // end of <aVert.GetVertOrient() == text::VertOrientation::NONE>
+
+ // We need to calculate the part's absolute position, in order for
+ // it to be put onto the right page and to be pulled into the
+ // LayLeaf's PrtArea
+ const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ if( aRectFnSet.IsVert() )
+ {
+ // --> OD 2009-08-31 #monglianlayout#
+ if ( !aRectFnSet.IsVertL2R() )
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch -
+ ( aRelPos.X() - nRelDiff ) -
+ aObjBoundRect.Width() );
+ }
+ else
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch +
+ ( aRelPos.X() - nRelDiff ) );
+ }
+ }
+ else
+ {
+ GetAnchoredObj().SetObjTop( nTopOfAnch +
+ ( aRelPos.Y() - nRelDiff ) );
+ }
+
+ // grow environment under certain conditions
+ // ignore one-column sections.
+ // #i23512# - correction: also ignore one-columned
+ // sections with footnotes/endnotes
+ if ( pUpperOfOrientFrame->IsInSct() )
+ {
+ const SwSectionFrame* pSctFrame = pUpperOfOrientFrame->FindSctFrame();
+ const bool bIgnoreSection = pUpperOfOrientFrame->IsSctFrame() ||
+ ( pSctFrame->Lower()->IsColumnFrame() &&
+ !pSctFrame->Lower()->GetNext() );
+ if ( bIgnoreSection )
+ pUpperOfOrientFrame = pSctFrame->GetUpper();
+ }
+ SwTwips nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) );
+ if( nDist < 0 )
+ {
+ // #i23512# - correction:
+ // consider section frame for grow in online layout and
+ // consider page alignment for grow in table.
+ SwLayoutFrame* pLayoutFrameToGrow = nullptr;
+ if ( bBrowse && rAnchorTextFrame.IsMoveable() )
+ {
+ if ( pUpperOfOrientFrame->IsInSct() )
+ {
+ pLayoutFrameToGrow = const_cast<SwLayoutFrame*>(
+ pUpperOfOrientFrame->FindSctFrame()->GetUpper());
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pLayoutFrameToGrow) );
+ if ( nDist >= 0 )
+ {
+ pLayoutFrameToGrow = nullptr;
+ }
+ }
+ else
+ {
+ pLayoutFrameToGrow =
+ const_cast<SwLayoutFrame*>(pUpperOfOrientFrame);
+ }
+ }
+ else if ( rAnchorTextFrame.IsInTab() && bGrow )
+ {
+ pLayoutFrameToGrow = const_cast<SwLayoutFrame*>(pUpperOfOrientFrame);
+ }
+ if ( pLayoutFrameToGrow )
+ {
+ // No need to grow the anchor cell in case the follow-text-flow object
+ // is wrap-though.
+ if (!GetAnchorFrame().IsInTab() || !DoesObjFollowsTextFlow() || !bWrapThrough)
+ {
+ pLayoutFrameToGrow->Grow( -nDist );
+ }
+ }
+ }
+
+ if ( DoesObjFollowsTextFlow() &&
+ ( aVert.GetRelationOrient() != text::RelOrientation::PAGE_FRAME &&
+ aVert.GetRelationOrient() != text::RelOrientation::PAGE_PRINT_AREA ) )
+ {
+
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) );
+ // #i26945# - floating screen objects, which are
+ // anchored inside a table, doesn't follow the text flow. But, they
+ // have to stay inside its layout environment.
+ if ( nDist < 0 && pOrientFrame->IsInTab() )
+ {
+ // If the anchor frame is the first content of the table cell
+ // and has no follow, the table frame is notified,
+ // that the object doesn't fit into the table cell.
+ // Adjustment of position isn't needed in this case.
+ if ( pOrientFrame == &rAnchorTextFrame &&
+ !pOrientFrame->GetFollow() &&
+ !pOrientFrame->GetIndPrev() )
+ {
+ const_cast<SwTabFrame*>(pOrientFrame->FindTabFrame())
+ ->SetDoesObjsFit( false );
+ }
+ else
+ {
+ SwTwips nTmpRelPosY( 0 );
+ if ( aRectFnSet.IsVert() )
+ nTmpRelPosY = aRelPos.X() - nDist;
+ else
+ nTmpRelPosY = aRelPos.Y() + nDist;
+ const SwLayoutFrame& rVertEnvironLayFrame =
+ aEnvOfObj.GetVertEnvironmentLayoutFrame( *pUpperOfOrientFrame );
+ nTmpRelPosY = AdjustVertRelPos( nTopOfAnch, aRectFnSet.IsVert(), aRectFnSet.IsVertL2R(),
+ rVertEnvironLayFrame,
+ nTmpRelPosY,
+ DoesObjFollowsTextFlow(),
+ false );
+ if ( aRectFnSet.IsVert() )
+ {
+ aRelPos.setX( nTmpRelPosY );
+ // --> OD 2009-08-31 #mongolianlayout#
+ if ( !aRectFnSet.IsVertL2R() )
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch -
+ aRelPos.X() -
+ aObjBoundRect.Width() );
+ }
+ else
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch + aRelPos.X() );
+ }
+ }
+ else
+ {
+ aRelPos.setY( nTmpRelPosY );
+ GetAnchoredObj().SetObjTop( nTopOfAnch + aRelPos.Y() );
+ }
+ // If the anchor frame is the first content of the table cell
+ // and the object still doesn't fit, the table frame is notified,
+ // that the object doesn't fit into the table cell.
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) );
+ if ( nDist < 0 &&
+ pOrientFrame == &rAnchorTextFrame && !pOrientFrame->GetIndPrev() )
+ {
+ const_cast<SwTabFrame*>(pOrientFrame->FindTabFrame())
+ ->SetDoesObjsFit( false );
+ }
+ }
+ }
+ else
+ {
+ // follow text flow
+ const bool bInFootnote = rAnchorTextFrame.IsInFootnote();
+ while( bMoveable && nDist < 0 )
+ {
+ bool bInSct = pUpperOfOrientFrame->IsInSct();
+ if ( bInSct )
+ {
+ const SwLayoutFrame* pTmp = pUpperOfOrientFrame->FindSctFrame()->GetUpper();
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pTmp) );
+ // #i23129# - Try to flow into next
+ // section|section column. Thus, do *not* leave section
+ // area, if anchored object doesn't fit into upper of section.
+ // But the anchored object is allowed to overlap bottom
+ // section|section column.
+ if ( nDist >= 0 )
+ {
+ break;
+ }
+ }
+ if ( !bInSct &&
+ aRectFnSet.GetTop(GetAnchoredObj().GetObjRect()) ==
+ aRectFnSet.GetPrtTop(*pUpperOfOrientFrame) )
+ // It doesn't fit, moving it would not help either anymore
+ break;
+
+ const SwLayoutFrame* pNextLay = pUpperOfOrientFrame->GetLeaf(
+ ( bInSct
+ ? MAKEPAGE_NOSECTION
+ : ( bInFootnote ? MAKEPAGE_NONE : MAKEPAGE_APPEND ) ),
+ true, &rAnchorTextFrame );
+ // correction:
+ // If anchor is in footnote and proposed next layout environment
+ // isn't a footnote frame, object can't follow the text flow
+ if ( bInFootnote && pNextLay && !pNextLay->IsFootnoteFrame() )
+ {
+ pNextLay = nullptr;
+ }
+ if ( pNextLay )
+ {
+ SwRectFnSet fnRectX(pNextLay);
+ if ( !bInSct ||
+ ( pUpperOfOrientFrame->FindSctFrame()->IsAnFollow( pNextLay->FindSctFrame() ) &&
+ fnRectX.GetHeight(pNextLay->getFramePrintArea()) ) )
+ {
+ SwTwips nTmpRelPosY =
+ aRectFnSet.YDiff( aRectFnSet.GetPrtTop(*pNextLay),
+ nTopOfAnch );
+ if ( aRectFnSet.IsVert() )
+ aRelPos.setX( nTmpRelPosY );
+ else
+ aRelPos.setY( nTmpRelPosY );
+ pUpperOfOrientFrame = pNextLay;
+ aRectFnSet.Refresh(pUpperOfOrientFrame);
+ bMoveable = rAnchorTextFrame.IsMoveable( pUpperOfOrientFrame );
+ if( fnRectX.IsVert() )
+ {
+ // --> OD 2009-08-31 #mongolianlayout#
+ if ( !aRectFnSet.IsVertL2R() )
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch -
+ aRelPos.X() -
+ aObjBoundRect.Width() );
+ }
+ else
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch +
+ aRelPos.X() );
+ }
+ }
+ else
+ GetAnchoredObj().SetObjTop( nTopOfAnch +
+ aRelPos.Y() );
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pUpperOfOrientFrame) );
+ }
+ // #i23129# - leave section area
+ else if ( bInSct )
+ {
+ const SwLayoutFrame* pTmp = pUpperOfOrientFrame->FindSctFrame()->GetUpper();
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pTmp) );
+ if( nDist < 0 )
+ pUpperOfOrientFrame = pTmp;
+ else
+ break;
+ }
+ }
+ else if ( bInSct )
+ {
+ // If we don't have enough room within the Area, we take a look at
+ // the Page
+ const SwLayoutFrame* pTmp = pUpperOfOrientFrame->FindSctFrame()->GetUpper();
+ nDist = aRectFnSet.BottomDist( GetAnchoredObj().GetObjRect(),
+ aRectFnSet.GetPrtBottom(*pTmp) );
+ if( nDist < 0 )
+ pUpperOfOrientFrame = pTmp;
+ else
+ break;
+ }
+ else
+ bMoveable = false;
+ }
+ }
+ }
+
+ // keep layout frame vertical position is oriented at.
+ mpVertPosOrientFrame = pUpperOfOrientFrame;
+
+ // If it was requested to not overlap with already formatted objects, take care of that
+ // here.
+ CalcOverlap(pAnchorFrameForVertPos, aRelPos, nTopOfAnch);
+ }
+
+ // determine 'horizontal' position
+ {
+ // determine horizontal positioning and alignment attributes
+ SwFormatHoriOrient aHori( rFrameFormat.GetHoriOrient() );
+
+ // set calculated vertical position in order to determine correct
+ // frame, the horizontal position is oriented at.
+ const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ if( aRectFnSet.IsVert() )
+ {
+ // --> OD 2009-08-31 #mongolianlayout#
+ if ( !aRectFnSet.IsVertL2R() )
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch -
+ aRelPos.X() - aObjBoundRect.Width() );
+ }
+ else
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch + aRelPos.X() );
+ }
+ }
+ else
+ GetAnchoredObj().SetObjTop( nTopOfAnch + aRelPos.Y() );
+
+ // determine frame, horizontal position is oriented at.
+ // #i28701# - If floating screen object doesn't follow
+ // the text flow, its horizontal position is oriented at <pOrientFrame>.
+ const SwFrame* pHoriOrientFrame = DoesObjFollowsTextFlow()
+ ? &GetHoriVirtualAnchor( *mpVertPosOrientFrame )
+ : pOrientFrame;
+
+ // #i26791# - get 'horizontal' offset to frame anchor position.
+ SwTwips nHoriOffsetToFrameAnchorPos( 0 );
+ SwTwips nRelPosX = CalcRelPosX( *pHoriOrientFrame, aEnvOfObj,
+ aHori, rLR, rUL, bWrapThrough,
+ ( aRectFnSet.IsVert() ? aRelPos.X() : aRelPos.Y() ),
+ nHoriOffsetToFrameAnchorPos );
+
+ // #i26791# - determine offset to 'horizontal' frame
+ // anchor position, depending on layout-direction
+ if ( aRectFnSet.IsVert() )
+ {
+ aRelPos.setY( nRelPosX );
+ maOffsetToFrameAnchorPos.setY( nHoriOffsetToFrameAnchorPos );
+ }
+ else
+ {
+ aRelPos.setX( nRelPosX );
+ maOffsetToFrameAnchorPos.setX( nHoriOffsetToFrameAnchorPos );
+ }
+
+ // save calculated horizontal position - needed for filters
+ // (including the xml-filter)
+ {
+ SwTwips nAttrRelPosX = nRelPosX - nHoriOffsetToFrameAnchorPos;
+ if ( aHori.GetHoriOrient() != text::HoriOrientation::NONE &&
+ aHori.GetPos() != nAttrRelPosX )
+ {
+ aHori.SetPos( nAttrRelPosX );
+ const_cast<SwFrameFormat&>(rFrameFormat).LockModify();
+ const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aHori );
+ const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify();
+ }
+ }
+ }
+
+ // set absolute position at object
+ const SwTwips nTopOfAnch = GetTopForObjPos( *pAnchorFrameForVertPos, aRectFnSet.FnRect(), aRectFnSet.IsVert() );
+ if( aRectFnSet.IsVert() )
+ {
+ // --> OD 2009-08-31 #mongolianlayout#
+ if ( !aRectFnSet.IsVertL2R() )
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch -
+ aRelPos.X() - aObjBoundRect.Width() );
+ }
+ else
+ {
+ GetAnchoredObj().SetObjLeft( nTopOfAnch + aRelPos.X() );
+ }
+ GetAnchoredObj().SetObjTop( rAnchorTextFrame.getFrameArea().Top() +
+ aRelPos.Y() );
+ }
+ else
+ {
+ GetAnchoredObj().SetObjLeft( rAnchorTextFrame.getFrameArea().Left() +
+ aRelPos.X() );
+ GetAnchoredObj().SetObjTop( nTopOfAnch + aRelPos.Y() );
+ }
+
+ // set relative position at object
+ GetAnchoredObj().SetCurrRelPos( aRelPos );
+}
+
+void SwToContentAnchoredObjectPosition::CalcOverlap(const SwTextFrame* pAnchorFrameForVertPos,
+ Point& rRelPos, const SwTwips nTopOfAnch)
+{
+ const SwFrameFormat& rFrameFormat = GetFrameFormat();
+ bool bAllowOverlap = rFrameFormat.GetWrapInfluenceOnObjPos().GetAllowOverlap();
+ if (bAllowOverlap)
+ {
+ return;
+ }
+
+ if (rFrameFormat.GetSurround().GetSurround() == css::text::WrapTextMode_THROUGH)
+ {
+ // This is explicit wrap through: allowed to overlap.
+ return;
+ }
+
+ if (SwTextBoxHelper::isTextBox(&rFrameFormat, RES_FLYFRMFMT))
+ {
+ // This is the frame part of a textbox, just take the offset from the textbox's shape part.
+ SwFrameFormat* pShapeOfTextBox
+ = SwTextBoxHelper::getOtherTextBoxFormat(&rFrameFormat, RES_FLYFRMFMT);
+ if (pShapeOfTextBox)
+ {
+ SwTwips nYDiff = pShapeOfTextBox->GetWrapInfluenceOnObjPos().GetOverlapVertOffset();
+ if (nYDiff > 0)
+ {
+ rRelPos.setY(rRelPos.getY() + nYDiff + 1);
+ GetAnchoredObj().SetObjTop(nTopOfAnch + rRelPos.Y());
+ }
+ }
+ return;
+ }
+
+ // Get the list of objects.
+ auto pSortedObjs = pAnchorFrameForVertPos->GetDrawObjs();
+ if (!pSortedObjs)
+ {
+ return;
+ }
+
+ for (const auto& pAnchoredObj : *pSortedObjs)
+ {
+ if (pAnchoredObj == &GetAnchoredObj())
+ {
+ // We found ourselves, stop iterating.
+ break;
+ }
+
+ if (SwTextBoxHelper::isTextBox(&pAnchoredObj->GetFrameFormat(), RES_FLYFRMFMT))
+ {
+ // Overlapping with the frame of a textbox is fine.
+ continue;
+ }
+
+ css::text::WrapTextMode eWrap = pAnchoredObj->GetFrameFormat().GetSurround().GetSurround();
+ if (eWrap == css::text::WrapTextMode_THROUGH)
+ {
+ // The other object is wrap through: allowed to overlap.
+ continue;
+ }
+
+ if (!GetAnchoredObj().GetObjRect().Overlaps(pAnchoredObj->GetObjRect()))
+ {
+ // Found an already positioned object, but it doesn't overlap, ignore.
+ continue;
+ }
+
+ // Already formatted, overlaps: resolve the conflict by shifting ourselves down.
+ SwTwips nYDiff = pAnchoredObj->GetObjRect().Bottom() - GetAnchoredObj().GetObjRect().Top();
+ rRelPos.setY(rRelPos.getY() + nYDiff + 1);
+ GetAnchoredObj().SetObjTop(nTopOfAnch + rRelPos.Y());
+
+ // Store our offset that avoids the overlap. If this is a shape of a textbox, then the frame
+ // of the textbox will use it.
+ SwFormatWrapInfluenceOnObjPos aInfluence(rFrameFormat.GetWrapInfluenceOnObjPos());
+ aInfluence.SetOverlapVertOffset(nYDiff);
+ const_cast<SwFrameFormat&>(rFrameFormat).LockModify();
+ const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr(aInfluence);
+ const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify();
+ }
+}
+
+/**
+ * Determine frame for horizontal position
+ */
+const SwFrame& SwToContentAnchoredObjectPosition::GetHoriVirtualAnchor(
+ const SwLayoutFrame& _rProposedFrame ) const
+{
+ const SwFrame* pHoriVirtAnchFrame = &_rProposedFrame;
+
+ // Search for first lower content frame, which is the anchor or a follow
+ // of the anchor (Note: <Anchor.IsAnFollow( Anchor )> is true)
+ // If none found, <_rProposedFrame> is returned.
+ const SwFrame* pFrame = _rProposedFrame.Lower();
+ while ( pFrame )
+ {
+ if ( pFrame->IsContentFrame() &&
+ GetAnchorTextFrame().IsAnFollow( static_cast<const SwContentFrame*>(pFrame) ) )
+ {
+ pHoriVirtAnchFrame = pFrame;
+ break;
+ }
+ pFrame = pFrame->GetNext();
+ }
+
+ return *pHoriVirtAnchFrame;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx b/sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx
new file mode 100644
index 000000000..3e7d98bf7
--- /dev/null
+++ b/sw/source/core/objectpositioning/tolayoutanchoredobjectposition.cxx
@@ -0,0 +1,226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tolayoutanchoredobjectposition.hxx>
+#include <anchoredobject.hxx>
+#include <frame.hxx>
+#include <pagefrm.hxx>
+#include <svx/svdobj.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <frmatr.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <rootfrm.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+
+using namespace ::com::sun::star;
+
+namespace objectpositioning
+{
+SwToLayoutAnchoredObjectPosition::SwToLayoutAnchoredObjectPosition( SdrObject& _rDrawObj )
+ : SwAnchoredObjectPosition( _rDrawObj )
+{}
+
+SwToLayoutAnchoredObjectPosition::~SwToLayoutAnchoredObjectPosition()
+{}
+
+/** calculate position for object position type TO_LAYOUT */
+void SwToLayoutAnchoredObjectPosition::CalcPosition()
+{
+ const SwRect aObjBoundRect( GetAnchoredObj().GetObjRect() );
+
+ SwRectFnSet aRectFnSet(&GetAnchorFrame());
+
+ const SwFrameFormat& rFrameFormat = GetFrameFormat();
+ const SvxLRSpaceItem &rLR = rFrameFormat.GetLRSpace();
+ const SvxULSpaceItem &rUL = rFrameFormat.GetULSpace();
+
+ const bool bFlyAtFly = RndStdIds::FLY_AT_FLY == rFrameFormat.GetAnchor().GetAnchorId();
+
+ // determine position.
+ // 'vertical' and 'horizontal' position are calculated separately
+ Point aRelPos;
+
+ // calculate 'vertical' position
+ SwFormatVertOrient aVert( rFrameFormat.GetVertOrient() );
+ {
+ // to-frame anchored objects are *only* vertical positioned centered or
+ // bottom, if its wrap mode is 'through' and its anchor frame has fixed
+ // size. Otherwise, it's positioned top.
+ sal_Int16 eVertOrient = aVert.GetVertOrient();
+ if ( bFlyAtFly &&
+ ( eVertOrient == text::VertOrientation::CENTER ||
+ eVertOrient == text::VertOrientation::BOTTOM ) &&
+ css::text::WrapTextMode_THROUGH != rFrameFormat.GetSurround().GetSurround() &&
+ !GetAnchorFrame().HasFixSize() )
+ {
+ eVertOrient = text::VertOrientation::TOP;
+ }
+ // #i26791# - get vertical offset to frame anchor position.
+ SwTwips nVertOffsetToFrameAnchorPos( 0 );
+ SwTwips nRelPosY =
+ GetVertRelPos( GetAnchorFrame(), GetAnchorFrame(), eVertOrient,
+ aVert.GetRelationOrient(), aVert.GetPos(),
+ rLR, rUL, nVertOffsetToFrameAnchorPos );
+
+ // keep the calculated relative vertical position - needed for filters
+ // (including the xml-filter)
+ {
+ SwTwips nAttrRelPosY = nRelPosY - nVertOffsetToFrameAnchorPos;
+ if ( aVert.GetVertOrient() != text::VertOrientation::NONE &&
+ aVert.GetPos() != nAttrRelPosY )
+ {
+ aVert.SetPos( nAttrRelPosY );
+ const_cast<SwFrameFormat&>(rFrameFormat).LockModify();
+ const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aVert );
+ const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify();
+ }
+ }
+
+ // determine absolute 'vertical' position, depending on layout-direction
+ // #i26791# - determine offset to 'vertical' frame
+ // anchor position, depending on layout-direction
+ if( aRectFnSet.IsVert() )
+ {
+ if ( aRectFnSet.IsVertL2R() )
+ aRelPos.setX( nRelPosY );
+ else
+ aRelPos.setX( -nRelPosY - aObjBoundRect.Width() );
+ maOffsetToFrameAnchorPos.setX( nVertOffsetToFrameAnchorPos );
+ }
+ else
+ {
+ aRelPos.setY( nRelPosY );
+ maOffsetToFrameAnchorPos.setY( nVertOffsetToFrameAnchorPos );
+ }
+
+ // if in online-layout the bottom of to-page anchored object is beyond
+ // the page bottom, the page frame has to grow by growing its body frame.
+ const SwViewShell *pSh = GetAnchorFrame().getRootFrame()->GetCurrShell();
+ if ( !bFlyAtFly && GetAnchorFrame().IsPageFrame() &&
+ pSh && pSh->GetViewOptions()->getBrowseMode() )
+ {
+ const tools::Long nAnchorBottom = GetAnchorFrame().getFrameArea().Bottom();
+ const tools::Long nBottom = GetAnchorFrame().getFrameArea().Top() +
+ aRelPos.Y() + aObjBoundRect.Height();
+ if ( nAnchorBottom < nBottom )
+ {
+ static_cast<SwPageFrame&>(GetAnchorFrame()).
+ FindBodyCont()->Grow( nBottom - nAnchorBottom );
+ }
+ }
+ } // end of determination of vertical position
+
+ // calculate 'horizontal' position
+ SwFormatHoriOrient aHori( rFrameFormat.GetHoriOrient() );
+ {
+ // consider toggle of horizontal position for even pages.
+ const bool bToggle = aHori.IsPosToggle() &&
+ !GetAnchorFrame().FindPageFrame()->OnRightPage();
+ sal_Int16 eHoriOrient = aHori.GetHoriOrient();
+ sal_Int16 eRelOrient = aHori.GetRelationOrient();
+ // toggle orientation
+ ToggleHoriOrientAndAlign( bToggle, eHoriOrient, eRelOrient );
+
+ // determine alignment values:
+ // <nWidth>: 'width' of the alignment area
+ // <nOffset>: offset of alignment area, relative to 'left' of
+ // frame anchor position
+ SwTwips nWidth, nOffset;
+ {
+ bool bDummy; // in this context irrelevant output parameter
+ GetHoriAlignmentValues( GetAnchorFrame(), GetAnchorFrame(),
+ eRelOrient, false,
+ nWidth, nOffset, bDummy );
+ }
+
+ SwTwips nObjWidth = aRectFnSet.GetWidth(aObjBoundRect);
+
+ // determine relative horizontal position
+ SwTwips nRelPosX;
+ if ( text::HoriOrientation::NONE == eHoriOrient )
+ {
+ if( bToggle ||
+ ( !aHori.IsPosToggle() && GetAnchorFrame().IsRightToLeft() ) )
+ {
+ nRelPosX = nWidth - nObjWidth - aHori.GetPos();
+ }
+ else
+ {
+ nRelPosX = aHori.GetPos();
+ }
+ }
+ else if ( text::HoriOrientation::CENTER == eHoriOrient )
+ nRelPosX = (nWidth / 2) - (nObjWidth / 2);
+ else if ( text::HoriOrientation::RIGHT == eHoriOrient )
+ nRelPosX = nWidth - ( nObjWidth +
+ ( aRectFnSet.IsVert() ? rUL.GetLower() : rLR.GetRight() ) );
+ else
+ nRelPosX = aRectFnSet.IsVert() ? rUL.GetUpper() : rLR.GetLeft();
+ nRelPosX += nOffset;
+
+ // no 'negative' relative horizontal position
+ // OD 06.11.2003 #FollowTextFlowAtFrame# - negative positions allow for
+ // to frame anchored objects.
+ if ( !bFlyAtFly && nRelPosX < 0 )
+ {
+ nRelPosX = 0;
+ }
+
+ // determine absolute 'horizontal' position, depending on layout-direction
+ // #i26791# - determine offset to 'horizontal' frame
+ // anchor position, depending on layout-direction
+ if( aRectFnSet.IsVert() || aRectFnSet.IsVertL2R() )
+ {
+
+ aRelPos.setY( nRelPosX );
+ maOffsetToFrameAnchorPos.setY( nOffset );
+ }
+ else
+ {
+ aRelPos.setX( nRelPosX );
+ maOffsetToFrameAnchorPos.setX( nOffset );
+ }
+
+ // keep the calculated relative horizontal position - needed for filters
+ // (including the xml-filter)
+ {
+ SwTwips nAttrRelPosX = nRelPosX - nOffset;
+ if ( text::HoriOrientation::NONE != aHori.GetHoriOrient() &&
+ aHori.GetPos() != nAttrRelPosX )
+ {
+ aHori.SetPos( nAttrRelPosX );
+ const_cast<SwFrameFormat&>(rFrameFormat).LockModify();
+ const_cast<SwFrameFormat&>(rFrameFormat).SetFormatAttr( aHori );
+ const_cast<SwFrameFormat&>(rFrameFormat).UnlockModify();
+ }
+ }
+ } // end of determination of horizontal position
+
+ // keep calculate relative position
+ maRelPos = aRelPos;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/ole/ndole.cxx b/sw/source/core/ole/ndole.cxx
new file mode 100644
index 000000000..8e2a1f9a4
--- /dev/null
+++ b/sw/source/core/ole/ndole.cxx
@@ -0,0 +1,1337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <sot/exchange.hxx>
+#include <tools/globname.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <vcl/outdev.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <section.hxx>
+#include <cntfrm.hxx>
+#include <ndole.hxx>
+#include <viewsh.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/graph.hxx>
+#include <sot/formats.hxx>
+#include <vcl/svapp.hxx>
+#include <strings.hrc>
+#include <svx/charthelper.hxx>
+#include <comphelper/threadpool.hxx>
+#include <atomic>
+#include <deque>
+#include <libxml/xmlwriter.h>
+#include <sfx2/xmldump.hxx>
+#include <osl/diagnose.h>
+#include <flyfrm.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star;
+
+namespace {
+
+class SwOLELRUCache
+ : private utl::ConfigItem
+{
+private:
+ std::deque<SwOLEObj *> m_OleObjects;
+ sal_Int32 m_nLRU_InitSize;
+ static uno::Sequence< OUString > GetPropertyNames();
+
+ virtual void ImplCommit() override;
+
+public:
+ SwOLELRUCache();
+
+ virtual void Notify( const uno::Sequence<
+ OUString>& aPropertyNames ) override;
+ void Load();
+
+ void InsertObj( SwOLEObj& rObj );
+ void RemoveObj( SwOLEObj& rObj );
+};
+
+}
+
+static std::shared_ptr<SwOLELRUCache> g_pOLELRU_Cache;
+
+class SwOLEListener_Impl : public ::cppu::WeakImplHelper< embed::XStateChangeListener >
+{
+ SwOLEObj* mpObj;
+public:
+ explicit SwOLEListener_Impl( SwOLEObj* pObj );
+ void dispose();
+ virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override;
+};
+
+SwOLEListener_Impl::SwOLEListener_Impl( SwOLEObj* pObj )
+: mpObj( pObj )
+{
+ if ( mpObj->IsOleRef() && mpObj->GetOleRef()->getCurrentState() == embed::EmbedStates::RUNNING )
+ {
+ g_pOLELRU_Cache->InsertObj( *mpObj );
+ }
+}
+
+void SAL_CALL SwOLEListener_Impl::changingState( const lang::EventObject&, ::sal_Int32 , ::sal_Int32 )
+{
+}
+
+void SAL_CALL SwOLEListener_Impl::stateChanged( const lang::EventObject&, ::sal_Int32 nOldState, ::sal_Int32 nNewState )
+{
+ if ( mpObj && nOldState == embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ if (!g_pOLELRU_Cache)
+ g_pOLELRU_Cache = std::make_shared<SwOLELRUCache>();
+ g_pOLELRU_Cache->InsertObj( *mpObj );
+ }
+ else if ( mpObj && nNewState == embed::EmbedStates::LOADED && nOldState == embed::EmbedStates::RUNNING )
+ {
+ if (g_pOLELRU_Cache)
+ g_pOLELRU_Cache->RemoveObj( *mpObj );
+ }
+ else if(mpObj && nNewState == embed::EmbedStates::RUNNING)
+ {
+ mpObj->resetBufferedData();
+ }
+}
+
+void SwOLEListener_Impl::dispose()
+{
+ if (mpObj && g_pOLELRU_Cache)
+ g_pOLELRU_Cache->RemoveObj( *mpObj );
+ mpObj = nullptr;
+}
+
+void SAL_CALL SwOLEListener_Impl::disposing( const lang::EventObject& )
+{
+ if (mpObj && g_pOLELRU_Cache)
+ g_pOLELRU_Cache->RemoveObj( *mpObj );
+}
+
+// TODO/LATER: actually SwEmbedObjectLink should be used here, but because different objects are used to control
+// embedded object different link objects with the same functionality had to be implemented
+
+namespace {
+
+class SwEmbedObjectLink : public sfx2::SvBaseLink
+{
+ SwOLENode* m_pOleNode;
+
+public:
+ explicit SwEmbedObjectLink(SwOLENode* pNode);
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ void Connect() { GetRealObject(); }
+};
+
+SwEmbedObjectLink::SwEmbedObjectLink(SwOLENode* pNode)
+ : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
+ , m_pOleNode(pNode)
+{
+ SetSynchron( false );
+}
+
+::sfx2::SvBaseLink::UpdateResult SwEmbedObjectLink::DataChanged(
+ const OUString&, const uno::Any& )
+{
+ if (!m_pOleNode->UpdateLinkURL_Impl())
+ {
+ // the link URL was not changed
+ uno::Reference<embed::XEmbeddedObject> xObject = m_pOleNode->GetOLEObj().GetOleRef();
+ OSL_ENSURE( xObject.is(), "The object must exist always!" );
+ if ( xObject.is() )
+ {
+ // let the object reload the link
+ // TODO/LATER: reload call could be used for this case
+
+ try
+ {
+ sal_Int32 nState = xObject->getCurrentState();
+ if ( nState != embed::EmbedStates::LOADED )
+ {
+ // in some cases the linked file probably is not locked so it could be changed
+ xObject->changeState( embed::EmbedStates::LOADED );
+ xObject->changeState( nState );
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ m_pOleNode->GetNewReplacement();
+ m_pOleNode->SetChanged();
+
+ return SUCCESS;
+}
+
+void SwEmbedObjectLink::Closed()
+{
+ m_pOleNode->BreakFileLink_Impl();
+ SvBaseLink::Closed();
+}
+
+class SwIFrameLink : public sfx2::SvBaseLink
+{
+ SwOLENode* m_pOleNode;
+
+public:
+ explicit SwIFrameLink(SwOLENode* pNode)
+ : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
+ , m_pOleNode(pNode)
+ {
+ SetSynchron( false );
+ }
+
+ ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString&, const uno::Any& )
+ {
+ uno::Reference<embed::XEmbeddedObject> xObject = m_pOleNode->GetOLEObj().GetOleRef();
+ uno::Reference<embed::XCommonEmbedPersist> xPersObj(xObject, uno::UNO_QUERY);
+ if (xPersObj.is())
+ {
+ // let the IFrameObject reload the link
+ try
+ {
+ xPersObj->reload(uno::Sequence<beans::PropertyValue>(), uno::Sequence<beans::PropertyValue>());
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ m_pOleNode->SetChanged();
+ }
+
+ return SUCCESS;
+ }
+
+};
+
+}
+
+SwOLENode::SwOLENode( const SwNodeIndex &rWhere,
+ const svt::EmbeddedObjectRef& xObj,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Ole, pGrfColl, pAutoAttr ),
+ maOLEObj( xObj ),
+ mbOLESizeInvalid( false ),
+ mpObjectLink( nullptr )
+{
+ maOLEObj.SetNode( this );
+}
+
+SwOLENode::SwOLENode( const SwNodeIndex &rWhere,
+ const OUString &rString,
+ sal_Int64 nAspect,
+ SwGrfFormatColl *pGrfColl,
+ SwAttrSet const * pAutoAttr ) :
+ SwNoTextNode( rWhere, SwNodeType::Ole, pGrfColl, pAutoAttr ),
+ maOLEObj( rString, nAspect ),
+ mbOLESizeInvalid( false ),
+ mpObjectLink( nullptr )
+{
+ maOLEObj.SetNode( this );
+}
+
+SwOLENode::~SwOLENode()
+{
+ DisconnectFileLink_Impl();
+ ResetAttr(RES_PAGEDESC);
+}
+
+const Graphic* SwOLENode::GetGraphic()
+{
+ if ( maOLEObj.GetOleRef().is() )
+ return maOLEObj.m_xOLERef.GetGraphic();
+ return nullptr;
+}
+
+/**
+ * Loading an OLE object that has been moved to the Undo Area
+ */
+bool SwOLENode::RestorePersistentData()
+{
+ OSL_ENSURE( maOLEObj.GetOleRef().is(), "No object to restore!" );
+ if ( maOLEObj.m_xOLERef.is() )
+ {
+ // If a SvPersist instance already exists, we use it
+ SfxObjectShell* p = GetDoc().GetPersist();
+ if( !p )
+ {
+ // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here?
+ // What happens to this document?
+ OSL_ENSURE( false, "Why are we creating a DocShell here?" );
+ p = new SwDocShell( GetDoc(), SfxObjectCreateMode::INTERNAL );
+ p->DoInitNew();
+ }
+
+ uno::Reference < container::XChild > xChild( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( p->GetModel() );
+
+ OSL_ENSURE( !maOLEObj.m_aName.isEmpty(), "No object name!" );
+ OUString aObjName;
+ if ( !p->GetEmbeddedObjectContainer().InsertEmbeddedObject( maOLEObj.m_xOLERef.GetObject(), aObjName ) )
+ {
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+ OSL_FAIL( "InsertObject failed" );
+ }
+ else
+ {
+ maOLEObj.m_aName = aObjName;
+ maOLEObj.m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
+ CheckFileLink_Impl();
+ }
+ }
+
+ return true;
+}
+
+void SwOLENode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwOLENode"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ GetOLEObj().dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * OLE object is transported into UNDO area
+ */
+bool SwOLENode::SavePersistentData()
+{
+ if( maOLEObj.m_xOLERef.is() )
+ {
+ comphelper::EmbeddedObjectContainer* pCnt = maOLEObj.m_xOLERef.GetContainer();
+
+#if OSL_DEBUG_LEVEL > 0
+ SfxObjectShell* p = GetDoc().GetPersist();
+ OSL_ENSURE( p, "No document!" );
+ if( p )
+ {
+ comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
+ OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!" );
+ }
+#endif
+
+ if ( pCnt && pCnt->HasEmbeddedObject( maOLEObj.m_aName ) )
+ {
+ uno::Reference < container::XChild > xChild( maOLEObj.m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+
+ /*
+ #i119941
+ When cut or move the chart, SwUndoFlyBase::DelFly will call SaveSection
+ to store the content to storage. In this step, chart filter functions
+ will be called. And chart filter will call chart core functions to create
+ the chart again. Then chart core function will call the class
+ ExplicitCategoryProvider to create data source. In this step, when SW data
+ source provider create the data source, a UnoActionRemoveContext
+ will mess with the layout and create a new SwFlyFrame.
+ But later in SwUndoFlyBase::DelFly, it will clear anchor related attributes
+ of SwFlyFrame. Then finally null pointer occur.
+ Resolution:
+ In pCnt->RemoveEmbeddedObject in SaveSection process of table chart,
+ only remove the object from the object container, without removing it's
+ storage and graphic stream. The chart already removed from formatter.
+ */
+ bool bKeepObjectToTempStorage = true;
+ uno::Reference < embed::XEmbeddedObject > xIP = GetOLEObj().GetOleRef();
+ if (IsChart() && !msChartTableName.isEmpty()
+ && svt::EmbeddedObjectRef::TryRunningState(xIP))
+ {
+ uno::Reference< chart2::XChartDocument > xChart( xIP->getComponent(), UNO_QUERY );
+ if (xChart.is() && !xChart->hasInternalDataProvider())
+ {
+ bKeepObjectToTempStorage = false;
+ }
+ }
+
+ pCnt->RemoveEmbeddedObject( maOLEObj.m_aName, bKeepObjectToTempStorage );
+
+ // TODO/LATER: aOLEObj.aName has no meaning here, since the undo container contains the object
+ // by different name, in future it might makes sense that the name is transported here.
+ maOLEObj.m_xOLERef.AssignToContainer( nullptr, maOLEObj.m_aName );
+ try
+ {
+ // "unload" object
+ maOLEObj.m_xOLERef->changeState( embed::EmbedStates::LOADED );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+
+ DisconnectFileLink_Impl();
+
+ return true;
+}
+
+SwOLENode * SwNodes::MakeOLENode( const SwNodeIndex & rWhere,
+ const svt::EmbeddedObjectRef& xObj,
+ SwGrfFormatColl* pGrfColl )
+{
+ OSL_ENSURE( pGrfColl,"SwNodes::MakeOLENode: Formatpointer is 0." );
+
+ SwOLENode *pNode =
+ new SwOLENode( rWhere, xObj, pGrfColl, nullptr );
+
+ // set parent if XChild is supported
+ //!! needed to supply Math objects with a valid reference device
+ uno::Reference< container::XChild > xChild( pNode->GetOLEObj().GetObject().GetObject(), UNO_QUERY );
+ if (xChild.is())
+ {
+ SwDocShell *pDocSh = GetDoc().GetDocShell();
+ if (pDocSh)
+ xChild->setParent( pDocSh->GetModel() );
+ }
+
+ return pNode;
+}
+
+SwOLENode * SwNodes::MakeOLENode( const SwNodeIndex & rWhere,
+ const OUString &rName, sal_Int64 nAspect, SwGrfFormatColl* pGrfColl, SwAttrSet const * pAutoAttr )
+{
+ OSL_ENSURE( pGrfColl,"SwNodes::MakeOLENode: Formatpointer is 0." );
+
+ SwOLENode *pNode =
+ new SwOLENode( rWhere, rName, nAspect, pGrfColl, pAutoAttr );
+
+ // set parent if XChild is supported
+ //!! needed to supply Math objects with a valid reference device
+ uno::Reference< container::XChild > xChild( pNode->GetOLEObj().GetObject().GetObject(), UNO_QUERY );
+ if (xChild.is())
+ {
+ SwDocShell *pDocSh = GetDoc().GetDocShell();
+ if (pDocSh)
+ xChild->setParent( pDocSh->GetModel() );
+ }
+
+ return pNode;
+}
+
+Size SwOLENode::GetTwipSize() const
+{
+ MapMode aMapMode( MapUnit::MapTwip );
+ return const_cast<SwOLENode*>(this)->maOLEObj.GetObject().GetSize( &aMapMode );
+}
+
+SwContentNode* SwOLENode::MakeCopy( SwDoc& rDoc, const SwNodeIndex& rIdx, bool) const
+{
+ // If there's already a SvPersist instance, we use it
+ SfxObjectShell* pPersistShell = rDoc.GetPersist();
+ if( !pPersistShell )
+ {
+ // TODO/LATER: is EmbeddedObjectContainer not enough?
+ // the created document will be closed by rDoc ( should use SfxObjectShellLock )
+ pPersistShell = new SwDocShell( rDoc, SfxObjectCreateMode::INTERNAL );
+ rDoc.SetTmpDocShell( pPersistShell );
+ pPersistShell->DoInitNew();
+ }
+
+ // We insert it at SvPersist level
+ // TODO/LATER: check if using the same naming scheme for all apps works here
+ OUString aNewName/*( Sw3Io::UniqueName( p->GetStorage(), "Obj" ) )*/;
+ SfxObjectShell* pSrc = GetDoc().GetPersist();
+
+ pPersistShell->GetEmbeddedObjectContainer().CopyAndGetEmbeddedObject(
+ pSrc->GetEmbeddedObjectContainer(),
+ pSrc->GetEmbeddedObjectContainer().GetEmbeddedObject( maOLEObj.m_aName ),
+ aNewName,
+ pSrc->getDocumentBaseURL(),
+ pPersistShell->getDocumentBaseURL());
+
+ SwOLENode* pOLENd = rDoc.GetNodes().MakeOLENode( rIdx, aNewName, GetAspect(),
+ rDoc.GetDfltGrfFormatColl(),
+ GetpSwAttrSet() );
+
+ pOLENd->SetChartTableName( GetChartTableName() );
+ pOLENd->SetTitle( GetTitle() );
+ pOLENd->SetDescription( GetDescription() );
+ pOLENd->SetContour( HasContour(), HasAutomaticContour() );
+ pOLENd->SetAspect( GetAspect() ); // the replacement image must be already copied
+
+ pOLENd->SetOLESizeInvalid( true );
+ rDoc.SetOLEPrtNotifyPending();
+
+ return pOLENd;
+}
+
+bool SwOLENode::IsInGlobalDocSection() const
+{
+ // Find the "Body Anchor"
+ SwNodeOffset nEndExtraIdx = GetNodes().GetEndOfExtras().GetIndex();
+ const SwNode* pAnchorNd = this;
+ do {
+ SwFrameFormat* pFlyFormat = pAnchorNd->GetFlyFormat();
+ if( !pFlyFormat )
+ return false;
+
+ const SwFormatAnchor& rAnchor = pFlyFormat->GetAnchor();
+ if( !rAnchor.GetContentAnchor() )
+ return false;
+
+ pAnchorNd = &rAnchor.GetContentAnchor()->nNode.GetNode();
+ } while( pAnchorNd->GetIndex() < nEndExtraIdx );
+
+ const SwSectionNode* pSectNd = pAnchorNd->FindSectionNode();
+ if( !pSectNd )
+ return false;
+
+ while( pSectNd )
+ {
+ pAnchorNd = pSectNd;
+ pSectNd = pAnchorNd->StartOfSectionNode()->FindSectionNode();
+ }
+
+ // pAnchorNd contains the most recently found Section Node, which
+ // now must fulfill the prerequisites for the GlobalDoc
+ pSectNd = static_cast<const SwSectionNode*>(pAnchorNd);
+ return SectionType::FileLink == pSectNd->GetSection().GetType() &&
+ pSectNd->GetIndex() > nEndExtraIdx;
+}
+
+bool SwOLENode::IsOLEObjectDeleted() const
+{
+ if( maOLEObj.m_xOLERef.is() )
+ {
+ SfxObjectShell* p = GetDoc().GetPersist();
+ if( p ) // Must be there
+ {
+ return !p->GetEmbeddedObjectContainer().HasEmbeddedObject( maOLEObj.m_aName );
+ }
+ }
+ return false;
+}
+
+void SwOLENode::GetNewReplacement()
+{
+ if ( maOLEObj.m_xOLERef.is() )
+ maOLEObj.m_xOLERef.UpdateReplacement();
+}
+
+bool SwOLENode::UpdateLinkURL_Impl()
+{
+ bool bResult = false;
+
+ if ( mpObjectLink )
+ {
+ OUString aNewLinkURL;
+ sfx2::LinkManager::GetDisplayNames( mpObjectLink, nullptr, &aNewLinkURL );
+ if ( !aNewLinkURL.equalsIgnoreAsciiCase( maLinkURL ) )
+ {
+ if ( !maOLEObj.m_xOLERef.is() )
+ maOLEObj.GetOleRef();
+
+ uno::Reference< embed::XEmbeddedObject > xObj = maOLEObj.m_xOLERef.GetObject();
+ uno::Reference< embed::XCommonEmbedPersist > xPersObj( xObj, uno::UNO_QUERY );
+ OSL_ENSURE( xPersObj.is(), "The object must exist!" );
+ if ( xPersObj.is() )
+ {
+ try
+ {
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( nCurState != embed::EmbedStates::LOADED )
+ xObj->changeState( embed::EmbedStates::LOADED );
+
+ // TODO/LATER: there should be possible to get current mediadescriptor settings from the object
+ uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ "URL", aNewLinkURL) };
+ xPersObj->reload( aArgs, uno::Sequence< beans::PropertyValue >() );
+
+ maLinkURL = aNewLinkURL;
+ bResult = true;
+
+ if ( nCurState != embed::EmbedStates::LOADED )
+ xObj->changeState( nCurState );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+
+ if ( !bResult )
+ {
+ // TODO/LATER: return the old name to the link manager, is it possible?
+ }
+ }
+ }
+
+ return bResult;
+}
+
+void SwOLENode::BreakFileLink_Impl()
+{
+ SfxObjectShell* pPers = GetDoc().GetPersist();
+
+ if ( !pPers )
+ return;
+
+ uno::Reference< embed::XStorage > xStorage = pPers->GetStorage();
+ if ( !xStorage.is() )
+ return;
+
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLinkSupport( maOLEObj.GetOleRef(), uno::UNO_QUERY_THROW );
+ xLinkSupport->breakLink( xStorage, maOLEObj.GetCurrentPersistName() );
+ DisconnectFileLink_Impl();
+ maLinkURL.clear();
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+void SwOLENode::DisconnectFileLink_Impl()
+{
+ if ( mpObjectLink )
+ {
+ GetDoc().getIDocumentLinksAdministration().GetLinkManager().Remove( mpObjectLink );
+ mpObjectLink = nullptr;
+ }
+}
+
+void SwOLENode::CheckFileLink_Impl()
+{
+ if ( !maOLEObj.m_xOLERef.GetObject().is() || mpObjectLink )
+ return;
+
+ try
+ {
+ uno::Reference<embed::XEmbeddedObject> xObject = maOLEObj.m_xOLERef.GetObject();
+ if (!xObject)
+ return;
+
+ bool bIFrame = false;
+
+ OUString aLinkURL;
+ uno::Reference<embed::XLinkageSupport> xLinkSupport(xObject, uno::UNO_QUERY);
+ if (xLinkSupport)
+ {
+ if (xLinkSupport->isLink())
+ aLinkURL = xLinkSupport->getLinkURL();
+ }
+ else
+ {
+ // get IFrame (Floating Frames) listed and updatable from the
+ // manage links dialog
+ SvGlobalName aClassId(xObject->getClassID());
+ if (aClassId == SvGlobalName(SO3_IFRAME_CLASSID))
+ {
+ uno::Reference<beans::XPropertySet> xSet(xObject->getComponent(), uno::UNO_QUERY);
+ if (xSet.is())
+ xSet->getPropertyValue("FrameURL") >>= aLinkURL;
+ bIFrame = true;
+ }
+ }
+
+ if (!aLinkURL.isEmpty()) // this is a file link so the model link manager should handle it
+ {
+ SwEmbedObjectLink* pEmbedObjectLink = nullptr;
+ if (!bIFrame)
+ {
+ pEmbedObjectLink = new SwEmbedObjectLink(this);
+ mpObjectLink = pEmbedObjectLink;
+ }
+ else
+ {
+ mpObjectLink = new SwIFrameLink(this);
+ }
+ maLinkURL = aLinkURL;
+ GetDoc().getIDocumentLinksAdministration().GetLinkManager().InsertFileLink( *mpObjectLink, sfx2::SvBaseLinkObjectType::ClientOle, aLinkURL );
+ if (pEmbedObjectLink)
+ pEmbedObjectLink->Connect();
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+}
+
+// #i99665#
+bool SwOLENode::IsChart() const
+{
+ bool bIsChart( false );
+
+ const uno::Reference< embed::XEmbeddedObject > xEmbObj =
+ const_cast<SwOLEObj&>(GetOLEObj()).GetOleRef();
+ if ( xEmbObj.is() )
+ {
+ SvGlobalName aClassID( xEmbObj->getClassID() );
+ bIsChart = SotExchange::IsChart( aClassID );
+ }
+
+ return bIsChart;
+}
+
+// react on visual change (invalidate)
+void SwOLENode::SetChanged()
+{
+ SwFrame* pFrame(getLayoutFrame(nullptr));
+
+ if(nullptr == pFrame)
+ {
+ return;
+ }
+
+ const SwRect aFrameArea(pFrame->getFrameArea());
+ SwViewShell* pVSh(GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell());
+
+ if(nullptr == pVSh)
+ {
+ return;
+ }
+
+ for(SwViewShell& rShell : pVSh->GetRingContainer())
+ {
+ CurrShell aCurr(&rShell);
+
+ if(rShell.VisArea().Overlaps(aFrameArea) && OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType())
+ {
+ // invalidate instead of painting
+ rShell.GetWin()->Invalidate(aFrameArea.SVRect());
+ }
+ }
+}
+
+namespace { class DeflateThread; }
+
+/// Holder for local data for a parallel-executed task to load a chart model
+class DeflateData
+{
+private:
+ friend DeflateThread;
+ friend class SwOLEObj;
+
+ uno::Reference< frame::XModel > maXModel;
+ drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence;
+ basegfx::B2DRange maRange;
+
+ // evtl.set from the SwOLEObj destructor when a WorkerThread is still active
+ // since it is not possible to kill it - let it terminate and delete the
+ // data working on itself
+ std::atomic< bool> mbKilled;
+
+ std::shared_ptr<comphelper::ThreadTaskTag> mpTag;
+
+public:
+ explicit DeflateData(const uno::Reference< frame::XModel >& rXModel)
+ : maXModel(rXModel),
+ mbKilled(false),
+ mpTag( comphelper::ThreadPool::createThreadTaskTag() )
+ {
+ }
+
+ const drawinglayer::primitive2d::Primitive2DContainer& getSequence() const
+ {
+ return maPrimitive2DSequence;
+ }
+
+ const basegfx::B2DRange& getRange() const
+ {
+ return maRange;
+ }
+
+ bool isFinished() const
+ {
+ return comphelper::ThreadPool::isTaskTagDone(mpTag);
+ }
+
+ void waitFinished()
+ {
+ // need to wait until the load in progress is finished.
+ // WorkerThreads need the SolarMutex to be able to continue
+ // and finish the running import.
+ SolarMutexReleaser aReleaser;
+ comphelper::ThreadPool::getSharedOptimalPool().waitUntilDone(mpTag);
+ }
+};
+
+namespace {
+
+/// Task for parallelly-executed task to load a chart model
+class DeflateThread : public comphelper::ThreadTask
+{
+ // the data to work on
+ DeflateData& mrDeflateData;
+
+public:
+ explicit DeflateThread(DeflateData& rDeflateData)
+ : comphelper::ThreadTask(rDeflateData.mpTag), mrDeflateData(rDeflateData)
+ {
+ }
+
+private:
+ virtual void doWork() override
+ {
+ try
+ {
+ // load the chart data and get the primitives
+ mrDeflateData.maPrimitive2DSequence = ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ mrDeflateData.maXModel,
+ mrDeflateData.maRange);
+
+ // model no longer needed and done
+ mrDeflateData.maXModel.clear();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ if(mrDeflateData.mbKilled)
+ {
+ // need to cleanup myself - data will not be used
+ delete &mrDeflateData;
+ }
+ }
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SwOLEObj::SwOLEObj( const svt::EmbeddedObjectRef& xObj ) :
+ m_pOLENode( nullptr ),
+ m_xOLERef( xObj )
+{
+ m_xOLERef.Lock();
+ if ( xObj.is() )
+ {
+ m_xListener = new SwOLEListener_Impl( this );
+ xObj->addStateChangeListener( m_xListener );
+ }
+}
+
+SwOLEObj::SwOLEObj( const OUString &rString, sal_Int64 nAspect ) :
+ m_pOLENode( nullptr ),
+ m_aName( rString )
+{
+ m_xOLERef.Lock();
+ m_xOLERef.SetViewAspect( nAspect );
+}
+
+SwOLEObj::~SwOLEObj() COVERITY_NOEXCEPT_FALSE
+{
+ if(m_pDeflateData)
+ {
+ // set flag so that the worker thread will delete m_pDeflateData
+ // when finished and forget about it
+ m_pDeflateData->mbKilled = true;
+ m_pDeflateData = nullptr;
+ }
+
+ if( m_xListener )
+ {
+ if ( m_xOLERef.is() )
+ m_xOLERef->removeStateChangeListener( m_xListener );
+ m_xListener->dispose();
+ m_xListener.clear();
+ }
+
+ if( m_pOLENode && !m_pOLENode->GetDoc().IsInDtor() )
+ {
+ // if the model is not currently in destruction it means that this object should be removed from the model
+ comphelper::EmbeddedObjectContainer* pCnt = m_xOLERef.GetContainer();
+
+#if OSL_DEBUG_LEVEL > 0
+ SfxObjectShell* p = m_pOLENode->GetDoc().GetPersist();
+ OSL_ENSURE( p, "No document!" );
+ if( p )
+ {
+ comphelper::EmbeddedObjectContainer& rCnt = p->GetEmbeddedObjectContainer();
+ OSL_ENSURE( !pCnt || &rCnt == pCnt, "The helper is assigned to unexpected container!" );
+ }
+#endif
+
+ if ( pCnt && pCnt->HasEmbeddedObject( m_aName ) )
+ {
+ uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+
+ // not already removed by deleting the object
+ m_xOLERef.AssignToContainer( nullptr, m_aName );
+
+ // unlock object so that object can be closed in RemoveEmbeddedObject
+ // successful closing of the object will automatically clear the reference then
+ m_xOLERef.Lock(false);
+
+ // Always remove object from container it is connected to
+ try
+ {
+ // remove object from container but don't close it
+ pCnt->RemoveEmbeddedObject( m_aName );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ }
+
+ if ( m_xOLERef.is() )
+ // in case the object wasn't closed: release it
+ // in case the object was not in the container: it's still locked, try to close
+ m_xOLERef.Clear();
+}
+
+void SwOLEObj::SetNode( SwOLENode* pNode )
+{
+ m_pOLENode = pNode;
+ if ( !m_aName.isEmpty() )
+ return;
+
+ SwDoc& rDoc = pNode->GetDoc();
+
+ // If there's already a SvPersist instance, we use it
+ SfxObjectShell* p = rDoc.GetPersist();
+ if( !p )
+ {
+ // TODO/LATER: Isn't an EmbeddedObjectContainer sufficient here?
+ // What happens to the document?
+ OSL_ENSURE( false, "Why are we creating a DocShell here??" );
+ p = new SwDocShell( rDoc, SfxObjectCreateMode::INTERNAL );
+ p->DoInitNew();
+ }
+
+ OUString aObjName;
+ uno::Reference < container::XChild > xChild( m_xOLERef.GetObject(), uno::UNO_QUERY );
+ if ( xChild.is() && xChild->getParent() != p->GetModel() )
+ // it is possible that the parent was set already
+ xChild->setParent( p->GetModel() );
+ if (!p->GetEmbeddedObjectContainer().InsertEmbeddedObject( m_xOLERef.GetObject(), aObjName ) )
+ {
+ OSL_FAIL( "InsertObject failed" );
+ if ( xChild.is() )
+ xChild->setParent( nullptr );
+ }
+ else
+ m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), aObjName );
+
+ const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required
+
+ m_aName = aObjName;
+}
+
+OUString SwOLEObj::GetStyleString()
+{
+ OUString strStyle;
+ if (m_xOLERef.is() && m_xOLERef.IsChart())
+ strStyle = m_xOLERef.GetChartType();
+ return strStyle;
+}
+
+bool SwOLEObj::IsOleRef() const
+{
+ return m_xOLERef.is();
+}
+
+IMPL_LINK_NOARG(SwOLEObj, IsProtectedHdl, LinkParamNone*, bool) { return IsProtected(); }
+
+bool SwOLEObj::IsProtected() const
+{
+ if (!m_pOLENode)
+ {
+ return false;
+ }
+
+ SwFrame* pFrame = m_pOLENode->getLayoutFrame(nullptr);
+ if (!pFrame)
+ {
+ return false;
+ }
+ SwFrame* pUpper = pFrame->GetUpper();
+ if (!pUpper || !pUpper->IsFlyFrame())
+ {
+ return false;
+ }
+
+ auto pFlyFrame = static_cast<SwFlyFrame*>(pUpper);
+ const SwFrame* pAnchor = pFlyFrame->GetAnchorFrame();
+ if (!pAnchor)
+ {
+ return false;
+ }
+
+ return pAnchor->IsProtected();
+}
+
+uno::Reference < embed::XEmbeddedObject > const & SwOLEObj::GetOleRef()
+{
+ if( !m_xOLERef.is() )
+ {
+ SfxObjectShell* p = m_pOLENode->GetDoc().GetPersist();
+ assert(p && "No SvPersist present");
+
+ OUString sDocumentBaseURL = p->getDocumentBaseURL();
+ uno::Reference < embed::XEmbeddedObject > xObj = p->GetEmbeddedObjectContainer().GetEmbeddedObject(m_aName, &sDocumentBaseURL);
+ OSL_ENSURE( !m_xOLERef.is(), "Calling GetOleRef() recursively is not permitted" );
+
+ if ( !xObj.is() )
+ {
+ // We could not load this part (probably broken)
+ tools::Rectangle aArea;
+ SwFrame *pFrame = m_pOLENode->getLayoutFrame(nullptr);
+ if ( pFrame )
+ {
+ Size aSz( pFrame->getFrameArea().SSize() );
+ aSz = o3tl::convert( aSz, o3tl::Length::twip, o3tl::Length::mm100 );
+ aArea.SetSize( aSz );
+ }
+ else
+ aArea.SetSize( Size( 5000, 5000 ) );
+ // TODO/LATER: set replacement graphic for dead object
+ // It looks as if it should work even without the object, because the replace will be generated automatically
+ OUString aTmpName;
+ xObj = p->GetEmbeddedObjectContainer().CreateEmbeddedObject( SvGlobalName( SO3_DUMMY_CLASSID ).GetByteSequence(), aTmpName );
+ }
+ if (xObj.is())
+ {
+ m_xOLERef.SetIsProtectedHdl(LINK(this, SwOLEObj, IsProtectedHdl));
+ m_xOLERef.Assign( xObj, m_xOLERef.GetViewAspect() );
+ m_xOLERef.AssignToContainer( &p->GetEmbeddedObjectContainer(), m_aName );
+ m_xListener = new SwOLEListener_Impl( this );
+ xObj->addStateChangeListener( m_xListener );
+ }
+
+ const_cast<SwOLENode*>(m_pOLENode)->CheckFileLink_Impl(); // for this notification nonconst access is required
+ }
+ else if ( m_xOLERef->getCurrentState() == embed::EmbedStates::RUNNING )
+ {
+ // move object to first position in cache
+ if (!g_pOLELRU_Cache)
+ g_pOLELRU_Cache = std::make_shared<SwOLELRUCache>();
+ g_pOLELRU_Cache->InsertObj( *this );
+ }
+
+ return m_xOLERef.GetObject();
+}
+
+svt::EmbeddedObjectRef& SwOLEObj::GetObject()
+{
+ GetOleRef();
+ return m_xOLERef;
+}
+
+bool SwOLEObj::UnloadObject()
+{
+ bool bRet = true;
+ if ( m_pOLENode )
+ {
+ const SwDoc& rDoc = m_pOLENode->GetDoc();
+ bRet = UnloadObject( m_xOLERef.GetObject(), &rDoc, m_xOLERef.GetViewAspect() );
+ }
+
+ return bRet;
+}
+
+PurgeGuard::PurgeGuard(const SwDoc& rDoc)
+ : m_rManager(const_cast<SwDoc&>(rDoc).GetDocumentSettingManager())
+ , m_bOrigPurgeOle(m_rManager.get(DocumentSettingId::PURGE_OLE))
+{
+ m_rManager.set(DocumentSettingId::PURGE_OLE, false);
+}
+
+PurgeGuard::~PurgeGuard() COVERITY_NOEXCEPT_FALSE
+{
+ m_rManager.set(DocumentSettingId::PURGE_OLE, m_bOrigPurgeOle);
+}
+
+bool SwOLEObj::UnloadObject( uno::Reference< embed::XEmbeddedObject > const & xObj, const SwDoc* pDoc, sal_Int64 nAspect )
+{
+ if ( !pDoc )
+ return false;
+
+ bool bRet = true;
+ sal_Int32 nState = xObj.is() ? xObj->getCurrentState() : embed::EmbedStates::LOADED;
+ bool bIsActive = ( nState != embed::EmbedStates::LOADED && nState != embed::EmbedStates::RUNNING );
+ sal_Int64 nMiscStatus = xObj->getStatus( nAspect );
+
+ if( nState != embed::EmbedStates::LOADED && !pDoc->IsInDtor() && !bIsActive &&
+ embed::EmbedMisc::MS_EMBED_ALWAYSRUN != ( nMiscStatus & embed::EmbedMisc::MS_EMBED_ALWAYSRUN ) &&
+ embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY != ( nMiscStatus & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) )
+ {
+ SfxObjectShell* p = pDoc->GetPersist();
+ if( p )
+ {
+ if( pDoc->GetDocumentSettingManager().get(DocumentSettingId::PURGE_OLE) )
+ {
+ try
+ {
+ uno::Reference < util::XModifiable > xMod( xObj->getComponent(), uno::UNO_QUERY );
+ if( xMod.is() && xMod->isModified() )
+ {
+ uno::Reference < embed::XEmbedPersist > xPers( xObj, uno::UNO_QUERY );
+ assert(xPers.is() && "Modified object without persistence in cache!");
+
+ PurgeGuard aGuard(*pDoc);
+ xPers->storeOwn();
+ }
+
+ // setting object to loaded state will remove it from cache
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ catch (const uno::Exception&)
+ {
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+ }
+
+ return bRet;
+}
+
+OUString SwOLEObj::GetDescription()
+{
+ uno::Reference< embed::XEmbeddedObject > xEmbObj = GetOleRef();
+ if ( !xEmbObj.is() )
+ return OUString();
+
+ SvGlobalName aClassID( xEmbObj->getClassID() );
+ if ( SotExchange::IsMath( aClassID ) )
+ return SwResId(STR_MATH_FORMULA);
+
+ if ( SotExchange::IsChart( aClassID ) )
+ return SwResId(STR_CHART);
+
+ return SwResId(STR_OLE);
+}
+
+drawinglayer::primitive2d::Primitive2DContainer const & SwOLEObj::tryToGetChartContentAsPrimitive2DSequence(
+ basegfx::B2DRange& rRange,
+ bool bSynchron)
+{
+ if(m_pDeflateData)
+ {
+ if(bSynchron)
+ {
+ // data in high quality is requested, wait until the data is available
+ // since a WorkerThread was already started to load it
+ m_pDeflateData->waitFinished();
+ }
+
+ if(m_pDeflateData->isFinished())
+ {
+ // copy the result data and cleanup
+ m_aPrimitive2DSequence = m_pDeflateData->getSequence();
+ m_aRange = m_pDeflateData->getRange();
+ m_pDeflateData.reset();
+ }
+ }
+
+ if(m_aPrimitive2DSequence.empty() && m_aRange.isEmpty() && m_xOLERef.is() && m_xOLERef.IsChart())
+ {
+ const uno::Reference< frame::XModel > aXModel(m_xOLERef->getComponent(), uno::UNO_QUERY);
+
+ if(aXModel.is())
+ {
+ // disabled for now, need to check deeper
+ static bool bAsynchronousLoadingAllowed = false; // loplugin:constvars:ignore
+
+ if(bSynchron ||
+ !bAsynchronousLoadingAllowed)
+ {
+ // load chart synchron in this Thread
+ m_aPrimitive2DSequence = ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ aXModel,
+ m_aRange);
+ }
+ else
+ {
+ // if not yet setup, initiate and start a WorkerThread to load the chart
+ // and it's primitives asynchron. If it already works, returning nothing
+ // is okay (preview will be reused)
+ if(!m_pDeflateData)
+ {
+ m_pDeflateData.reset( new DeflateData(aXModel) );
+ std::unique_ptr<DeflateThread> pNew( new DeflateThread(*m_pDeflateData) );
+ comphelper::ThreadPool::getSharedOptimalPool().pushTask(std::move(pNew));
+ }
+ }
+ }
+ }
+
+ if(!m_aPrimitive2DSequence.empty() && !m_aRange.isEmpty())
+ {
+ // when we have data, also copy the buffered Range data as output
+ rRange = m_aRange;
+ }
+
+ return m_aPrimitive2DSequence;
+}
+
+void SwOLEObj::resetBufferedData()
+{
+ m_aPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer();
+ m_aRange.reset();
+
+ if(m_pDeflateData)
+ {
+ // load is in progress, wait until finished and cleanup without using it
+ m_pDeflateData->waitFinished();
+ m_pDeflateData.reset();
+ }
+}
+
+void SwOLEObj::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwOLEObj"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ m_xOLERef.dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwOLELRUCache::SwOLELRUCache()
+ : utl::ConfigItem("Office.Common/Cache")
+ , m_nLRU_InitSize( 20 )
+{
+ EnableNotification( GetPropertyNames() );
+ Load();
+}
+
+uno::Sequence< OUString > SwOLELRUCache::GetPropertyNames()
+{
+ Sequence< OUString > aNames { "Writer/OLE_Objects" };
+ return aNames;
+}
+
+void SwOLELRUCache::Notify( const uno::Sequence< OUString>& )
+{
+ Load();
+}
+
+void SwOLELRUCache::ImplCommit()
+{
+}
+
+void SwOLELRUCache::Load()
+{
+ Sequence< OUString > aNames( GetPropertyNames() );
+ Sequence< Any > aValues = GetProperties( aNames );
+ const Any* pValues = aValues.getConstArray();
+ OSL_ENSURE( aValues.getLength() == aNames.getLength(), "GetProperties failed" );
+ if (aValues.getLength() != aNames.getLength() || !pValues->hasValue())
+ return;
+
+ sal_Int32 nVal = 0;
+ *pValues >>= nVal;
+
+ if (nVal < m_nLRU_InitSize)
+ {
+ std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent delete this
+ // size of cache has been changed
+ sal_Int32 nCount = m_OleObjects.size();
+ sal_Int32 nPos = nCount;
+
+ // try to remove the last entries until new maximum size is reached
+ while( nCount > nVal )
+ {
+ SwOLEObj *const pObj = m_OleObjects[ --nPos ];
+ if ( pObj->UnloadObject() )
+ nCount--;
+ if ( !nPos )
+ break;
+ }
+ }
+
+ m_nLRU_InitSize = nVal;
+}
+
+void SwOLELRUCache::InsertObj( SwOLEObj& rObj )
+{
+ SwOLEObj* pObj = &rObj;
+ if (auto const it = std::find(m_OleObjects.begin(), m_OleObjects.end(), pObj);
+ it != m_OleObjects.end())
+ {
+ if (it == m_OleObjects.begin())
+ return; // Everything is already in place
+ // object in cache but is currently not the first in cache
+ m_OleObjects.erase(it);
+ }
+
+ std::shared_ptr<SwOLELRUCache> xKeepAlive(g_pOLELRU_Cache); // prevent delete this
+ // try to remove objects if necessary
+ sal_Int32 nCount = m_OleObjects.size();
+ sal_Int32 nPos = nCount-1;
+ while (nPos >= 0 && nCount >= m_nLRU_InitSize)
+ {
+ pObj = m_OleObjects[ nPos-- ];
+ if ( pObj->UnloadObject() )
+ nCount--;
+ }
+ m_OleObjects.push_front(&rObj);
+}
+
+void SwOLELRUCache::RemoveObj( SwOLEObj& rObj )
+{
+ auto const it = std::find(m_OleObjects.begin(), m_OleObjects.end(), &rObj);
+ if (it != m_OleObjects.end())
+ {
+ m_OleObjects.erase(it);
+ }
+ if (m_OleObjects.empty())
+ {
+ if (g_pOLELRU_Cache.use_count() == 1) // test that we're not in InsertObj()
+ {
+ g_pOLELRU_Cache.reset();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/para/paratr.cxx b/sw/source/core/para/paratr.cxx
new file mode 100644
index 000000000..f724ac21c
--- /dev/null
+++ b/sw/source/core/para/paratr.cxx
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <unomid.h>
+#include <com/sun/star/style/DropCapFormat.hpp>
+#include <o3tl/any.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <paratr.hxx>
+#include <charfmt.hxx>
+#include <libxml/xmlwriter.h>
+#include <osl/diagnose.h>
+#include <tools/UnitConversion.hxx>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SwFormatDrop::CreateDefault() { return new SwFormatDrop; }
+SfxPoolItem* SwRegisterItem::CreateDefault() { return new SwRegisterItem; }
+SfxPoolItem* SwNumRuleItem::CreateDefault() { return new SwNumRuleItem; }
+
+SwFormatDrop::SwFormatDrop()
+ : SfxPoolItem( RES_PARATR_DROP ),
+ SwClient( nullptr ),
+ m_pDefinedIn( nullptr ),
+ m_nDistance( 0 ),
+ m_nLines( 0 ),
+ m_nChars( 0 ),
+ m_bWholeWord( false )
+{
+}
+
+SwFormatDrop::SwFormatDrop( const SwFormatDrop &rCpy )
+ : SfxPoolItem( RES_PARATR_DROP ),
+ SwClient( rCpy.GetRegisteredInNonConst() ),
+ m_pDefinedIn( nullptr ),
+ m_nDistance( rCpy.GetDistance() ),
+ m_nLines( rCpy.GetLines() ),
+ m_nChars( rCpy.GetChars() ),
+ m_bWholeWord( rCpy.GetWholeWord() )
+{
+}
+
+SwFormatDrop::~SwFormatDrop()
+{
+}
+
+void SwFormatDrop::SetCharFormat( SwCharFormat *pNew )
+{
+ assert(!pNew || !pNew->IsDefault()); // expose cases that lead to use-after-free
+ // Rewire
+ EndListeningAll();
+ if(pNew)
+ pNew->Add( this );
+}
+
+bool SwFormatDrop::GetInfo( SfxPoolItem& ) const
+{
+ return true; // Continue
+}
+
+bool SwFormatDrop::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( m_nLines == static_cast<const SwFormatDrop&>(rAttr).GetLines() &&
+ m_nChars == static_cast<const SwFormatDrop&>(rAttr).GetChars() &&
+ m_nDistance == static_cast<const SwFormatDrop&>(rAttr).GetDistance() &&
+ m_bWholeWord == static_cast<const SwFormatDrop&>(rAttr).GetWholeWord() &&
+ GetCharFormat() == static_cast<const SwFormatDrop&>(rAttr).GetCharFormat() &&
+ m_pDefinedIn == static_cast<const SwFormatDrop&>(rAttr).m_pDefinedIn );
+}
+
+SwFormatDrop* SwFormatDrop::Clone( SfxItemPool* ) const
+{
+ return new SwFormatDrop( *this );
+}
+
+bool SwFormatDrop::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ switch(nMemberId&~CONVERT_TWIPS)
+ {
+ case MID_DROPCAP_LINES : rVal <<= static_cast<sal_Int16>(m_nLines); break;
+ case MID_DROPCAP_COUNT : rVal <<= static_cast<sal_Int16>(m_nChars); break;
+ case MID_DROPCAP_DISTANCE : rVal <<= static_cast<sal_Int16>(convertTwipToMm100(m_nDistance)); break;
+ case MID_DROPCAP_FORMAT:
+ {
+ style::DropCapFormat aDrop;
+ aDrop.Lines = m_nLines ;
+ aDrop.Count = m_nChars ;
+ aDrop.Distance = convertTwipToMm100(m_nDistance);
+ rVal <<= aDrop;
+ }
+ break;
+ case MID_DROPCAP_WHOLE_WORD:
+ rVal <<= m_bWholeWord;
+ break;
+ case MID_DROPCAP_CHAR_STYLE_NAME :
+ {
+ OUString sName;
+ if(GetCharFormat())
+ sName = SwStyleNameMapper::GetProgName(
+ GetCharFormat()->GetName(), SwGetPoolIdFromName::ChrFmt );
+ rVal <<= sName;
+ }
+ break;
+ }
+ return true;
+}
+
+bool SwFormatDrop::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ switch(nMemberId&~CONVERT_TWIPS)
+ {
+ case MID_DROPCAP_LINES :
+ {
+ sal_Int8 nTemp = 0;
+ rVal >>= nTemp;
+ if(nTemp >=1 && nTemp < 0x7f)
+ m_nLines = static_cast<sal_uInt8>(nTemp);
+ }
+ break;
+ case MID_DROPCAP_COUNT :
+ {
+ sal_Int16 nTemp = 0;
+ rVal >>= nTemp;
+ if(nTemp >=1 && nTemp < 0x7f)
+ m_nChars = static_cast<sal_uInt8>(nTemp);
+ }
+ break;
+ case MID_DROPCAP_DISTANCE :
+ {
+ sal_Int16 nVal = 0;
+ if ( rVal >>= nVal )
+ m_nDistance = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ else
+ return false;
+ break;
+ }
+ case MID_DROPCAP_FORMAT:
+ {
+ if(rVal.getValueType() == ::cppu::UnoType<style::DropCapFormat>::get())
+ {
+ auto pDrop = o3tl::doAccess<style::DropCapFormat>(rVal);
+ m_nLines = pDrop->Lines;
+ m_nChars = pDrop->Count;
+ m_nDistance = o3tl::toTwips(pDrop->Distance, o3tl::Length::mm100);
+ }
+ }
+ break;
+ case MID_DROPCAP_WHOLE_WORD:
+ m_bWholeWord = *o3tl::doAccess<bool>(rVal);
+ break;
+ case MID_DROPCAP_CHAR_STYLE_NAME :
+ OSL_FAIL("char format cannot be set in PutValue()!");
+ break;
+ }
+ return true;
+}
+
+SwRegisterItem* SwRegisterItem::Clone( SfxItemPool * ) const
+{
+ return new SwRegisterItem( *this );
+}
+
+SwNumRuleItem* SwNumRuleItem::Clone( SfxItemPool * ) const
+{
+ return new SwNumRuleItem( *this );
+}
+
+bool SwNumRuleItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ return GetValue() == static_cast<const SwNumRuleItem&>(rAttr).GetValue();
+}
+
+bool SwNumRuleItem::QueryValue( uno::Any& rVal, sal_uInt8 ) const
+{
+ OUString sRet = SwStyleNameMapper::GetProgName(GetValue(), SwGetPoolIdFromName::NumRule );
+ rVal <<= sRet;
+ return true;
+}
+
+bool SwNumRuleItem::PutValue( const uno::Any& rVal, sal_uInt8 )
+{
+ OUString uName;
+ rVal >>= uName;
+ SetValue(SwStyleNameMapper::GetUIName(uName, SwGetPoolIdFromName::NumRule));
+ return true;
+}
+
+void SwNumRuleItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwNumRuleItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(GetValue().toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwParaConnectBorderItem* SwParaConnectBorderItem::Clone( SfxItemPool * ) const
+{
+ return new SwParaConnectBorderItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/sw3io/swacorr.cxx b/sw/source/core/sw3io/swacorr.cxx
new file mode 100644
index 000000000..b626c94d1
--- /dev/null
+++ b/sw/source/core/sw3io/swacorr.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swacorr.hxx>
+#include <swblocks.hxx>
+#include <SwXMLTextBlocks.hxx>
+#include <docsh.hxx>
+#include <editsh.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+/**
+ * Returns the replacement text
+ *
+ * Only for the SWG format, all others can be extracted from the word list
+ *
+ * @param rShort - the stream name (encrypted)
+ */
+bool SwAutoCorrect::GetLongText( const OUString& rShort, OUString& rLong )
+{
+ ErrCode nRet = ERRCODE_NONE;
+ assert( m_pTextBlocks );
+ nRet = m_pTextBlocks->GetText( rShort, rLong );
+ return !nRet.IsError() && !rLong.isEmpty();
+}
+
+void SwAutoCorrect::refreshBlockList( const uno::Reference< embed::XStorage >& rStg )
+{
+ if (rStg.is())
+ {
+ // mba: relative URLs don't make sense here
+ m_pTextBlocks.reset( new SwXMLTextBlocks( rStg, OUString() ) );
+ }
+ else {
+ OSL_ENSURE( rStg.is(), "Someone passed SwAutoCorrect::refreshBlockList a dud storage!");
+ }
+}
+
+/**
+ * Text with attributes
+ *
+ * Only for SWG format
+ *
+ * @param rShort - the stream name (encrypted)
+ */
+bool SwAutoCorrect::PutText( const uno::Reference < embed::XStorage >& rStg,
+ const OUString& rFileName, const OUString& rShort,
+ SfxObjectShell& rObjSh, OUString& rLong )
+{
+ SwDocShell* pDShell = dynamic_cast<SwDocShell*>(&rObjSh);
+ if (!pDShell)
+ return false;
+
+ SwEditShell* pEditSh = pDShell->GetEditShell();
+ if (!pEditSh)
+ return false;
+
+ ErrCode nRet = ERRCODE_NONE;
+
+ // mba: relative URLs don't make sense here
+ SwXMLTextBlocks aBlk( rStg, rFileName );
+ SwDoc* pDoc = aBlk.GetDoc();
+
+ nRet = aBlk.BeginPutDoc( rShort, rShort );
+ if( ! nRet.IsError() )
+ {
+ pEditSh->CopySelToDoc(*pDoc);
+ nRet = aBlk.PutDoc();
+ aBlk.AddName ( rShort, rShort );
+ if( ! nRet.IsError() )
+ nRet = aBlk.GetText( rShort, rLong );
+ }
+ return ! nRet.IsError();
+}
+
+SwAutoCorrect::SwAutoCorrect( const SvxAutoCorrect& rACorr )
+ : SvxAutoCorrect( rACorr )
+{
+ SwEditShell::SetAutoFormatFlags(&GetSwFlags());
+}
+
+SwAutoCorrect::~SwAutoCorrect()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/BlockListTokens.txt b/sw/source/core/swg/BlockListTokens.txt
new file mode 100644
index 000000000..0b5a64607
--- /dev/null
+++ b/sw/source/core/swg/BlockListTokens.txt
@@ -0,0 +1,7 @@
+abbreviated-name
+block
+block-list
+list-name
+name
+package-name
+unformatted-text
diff --git a/sw/source/core/swg/SwXMLBlockExport.cxx b/sw/source/core/swg/SwXMLBlockExport.cxx
new file mode 100644
index 000000000..fe085a4a5
--- /dev/null
+++ b/sw/source/core/swg/SwXMLBlockExport.cxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwXMLBlockExport.hxx>
+#include <SwXMLTextBlocks.hxx>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+SwXMLBlockListExport::SwXMLBlockListExport(
+ const uno::Reference< uno::XComponentContext >& rContext,
+ SwXMLTextBlocks & rBlocks,
+ const OUString &rFileName,
+ uno::Reference< xml::sax::XDocumentHandler> const &rHandler)
+: SvXMLExport( rContext, "", rFileName, util::MeasureUnit::CM, rHandler ),
+ m_rBlockList(rBlocks)
+{
+ GetNamespaceMap_().Add( GetXMLToken ( XML_NP_BLOCK_LIST ),
+ GetXMLToken ( XML_N_BLOCK_LIST ),
+ XML_NAMESPACE_BLOCKLIST );
+}
+
+ErrCode SwXMLBlockListExport::exportDoc(enum XMLTokenEnum )
+{
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ AddAttribute ( XML_NAMESPACE_NONE,
+ GetNamespaceMap_().GetAttrNameByKey ( XML_NAMESPACE_BLOCKLIST ),
+ GetNamespaceMap_().GetNameByKey ( XML_NAMESPACE_BLOCKLIST ) );
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_LIST_NAME,
+ m_rBlockList.GetName());
+ {
+ SvXMLElementExport aRoot (*this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK_LIST, true, true);
+ sal_uInt16 nBlocks= m_rBlockList.GetCount();
+ for ( sal_uInt16 i = 0; i < nBlocks; i++)
+ {
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_ABBREVIATED_NAME,
+ m_rBlockList.GetShortName(i));
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_PACKAGE_NAME,
+ m_rBlockList.GetPackageName(i));
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_NAME,
+ m_rBlockList.GetLongName(i));
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_UNFORMATTED_TEXT,
+ m_rBlockList.IsOnlyTextBlock(i) ? XML_TRUE : XML_FALSE );
+
+ SvXMLElementExport aBlock( *this, XML_NAMESPACE_BLOCKLIST, XML_BLOCK, true, true);
+ }
+ }
+ GetDocHandler()->endDocument();
+ return ERRCODE_NONE;
+}
+
+SwXMLTextBlockExport::SwXMLTextBlockExport(
+ const uno::Reference< uno::XComponentContext >& rContext,
+ SwXMLTextBlocks & rBlocks,
+ const OUString &rFileName,
+ uno::Reference< xml::sax::XDocumentHandler> const &rHandler)
+: SvXMLExport( rContext, "", rFileName, util::MeasureUnit::CM, rHandler ),
+ m_rBlockList(rBlocks)
+{
+ GetNamespaceMap_().Add( GetXMLToken ( XML_NP_BLOCK_LIST ),
+ GetXMLToken ( XML_N_BLOCK_LIST ),
+ XML_NAMESPACE_BLOCKLIST );
+ GetNamespaceMap_().Add( GetXMLToken ( XML_NP_OFFICE ),
+ GetXMLToken(XML_N_OFFICE_OOO),
+ XML_NAMESPACE_OFFICE );
+ GetNamespaceMap_().Add( GetXMLToken ( XML_NP_TEXT ),
+ GetXMLToken(XML_N_TEXT_OOO),
+ XML_NAMESPACE_TEXT );
+}
+
+void SwXMLTextBlockExport::exportDoc(std::u16string_view rText)
+{
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ AddAttribute ( XML_NAMESPACE_NONE,
+ GetNamespaceMap_().GetAttrNameByKey ( XML_NAMESPACE_BLOCKLIST ),
+ GetNamespaceMap_().GetNameByKey ( XML_NAMESPACE_BLOCKLIST ) );
+ AddAttribute ( XML_NAMESPACE_NONE,
+ GetNamespaceMap_().GetAttrNameByKey ( XML_NAMESPACE_TEXT ),
+ GetNamespaceMap_().GetNameByKey ( XML_NAMESPACE_TEXT ) );
+ AddAttribute ( XML_NAMESPACE_NONE,
+ GetNamespaceMap_().GetAttrNameByKey ( XML_NAMESPACE_OFFICE ),
+ GetNamespaceMap_().GetNameByKey ( XML_NAMESPACE_OFFICE ) );
+ AddAttribute( XML_NAMESPACE_BLOCKLIST,
+ XML_LIST_NAME,
+ m_rBlockList.GetName());
+ {
+ SvXMLElementExport aDocument (*this, XML_NAMESPACE_OFFICE, XML_DOCUMENT, true, true);
+ {
+ SvXMLElementExport aBody (*this, XML_NAMESPACE_OFFICE, XML_BODY, true, true);
+ {
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString sTemp ( o3tl::getToken(rText, 0, '\015', nPos ) );
+ SvXMLElementExport aPara (*this, XML_NAMESPACE_TEXT, XML_P, true, false);
+ GetDocHandler()->characters(sTemp);
+ } while (-1 != nPos );
+ }
+
+ }
+ }
+ GetDocHandler()->endDocument();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/SwXMLBlockImport.cxx b/sw/source/core/swg/SwXMLBlockImport.cxx
new file mode 100644
index 000000000..b0453a8a5
--- /dev/null
+++ b/sw/source/core/swg/SwXMLBlockImport.cxx
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwXMLBlockImport.hxx>
+#include <SwXMLTextBlocks.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <unotools/charclass.hxx>
+#include <swtypes.hxx>
+
+#if defined __clang__
+#if __has_warning("-Wdeprecated-register")
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-register"
+#endif
+#endif
+#include <tokens.cxx>
+#if defined __clang__
+#if __has_warning("-Wdeprecated-register")
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace css::xml::sax;
+
+class SwXMLBlockListImport;
+class SwXMLTextBlockImport;
+
+namespace {
+
+class SwXMLBlockListContext : public SvXMLImportContext
+{
+private:
+ SwXMLBlockListImport& m_rLocalRef;
+
+public:
+ SwXMLBlockListContext( SwXMLBlockListImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+};
+
+class SwXMLBlockContext : public SvXMLImportContext
+{
+public:
+ SwXMLBlockContext( SwXMLBlockListImport& rImport,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList );
+};
+
+class SwXMLTextBlockDocumentContext : public SvXMLImportContext
+{
+private:
+ SwXMLTextBlockImport& m_rLocalRef;
+
+public:
+ SwXMLTextBlockDocumentContext( SwXMLTextBlockImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+};
+
+class SwXMLTextBlockBodyContext : public SvXMLImportContext
+{
+private:
+ SwXMLTextBlockImport& m_rLocalRef;
+
+public:
+ SwXMLTextBlockBodyContext( SwXMLTextBlockImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+};
+
+class SwXMLTextBlockTextContext : public SvXMLImportContext
+{
+private:
+ SwXMLTextBlockImport& m_rLocalRef;
+
+public:
+ SwXMLTextBlockTextContext( SwXMLTextBlockImport& rImport );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
+ sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+};
+
+class SwXMLTextBlockParContext : public SvXMLImportContext
+{
+private:
+ SwXMLTextBlockImport& m_rLocalRef;
+
+public:
+ SwXMLTextBlockParContext( SwXMLTextBlockImport & rImport );
+
+ virtual void SAL_CALL characters( const OUString & aChars ) override;
+
+ virtual ~SwXMLTextBlockParContext() override;
+};
+
+}
+
+SwXMLTextBlockTokenHandler::SwXMLTextBlockTokenHandler()
+{
+}
+
+SwXMLTextBlockTokenHandler::~SwXMLTextBlockTokenHandler()
+{
+}
+
+sal_Int32 SAL_CALL SwXMLTextBlockTokenHandler::getTokenFromUTF8( const Sequence< sal_Int8 >& Identifier )
+{
+ return getTokenDirect( reinterpret_cast< const char* >( Identifier.getConstArray() ), Identifier.getLength() );
+}
+
+Sequence< sal_Int8 > SAL_CALL SwXMLTextBlockTokenHandler::getUTF8Identifier( sal_Int32 )
+{
+ return Sequence< sal_Int8 >();
+}
+
+sal_Int32 SwXMLTextBlockTokenHandler::getTokenDirect( const char *pTag, sal_Int32 nLength ) const
+{
+ if( !nLength )
+ nLength = strlen( pTag );
+ const struct xmltoken* pToken = TextBlockTokens::in_word_set( pTag, nLength );
+ return pToken ? pToken->nToken : XML_TOKEN_INVALID;
+}
+
+SwXMLBlockListTokenHandler::SwXMLBlockListTokenHandler()
+{
+}
+
+SwXMLBlockListTokenHandler::~SwXMLBlockListTokenHandler()
+{
+}
+
+sal_Int32 SAL_CALL SwXMLBlockListTokenHandler::getTokenFromUTF8( const Sequence< sal_Int8 >& Identifier )
+{
+ return getTokenDirect( reinterpret_cast< const char* >( Identifier.getConstArray() ), Identifier.getLength() );
+}
+
+Sequence< sal_Int8 > SAL_CALL SwXMLBlockListTokenHandler::getUTF8Identifier( sal_Int32 )
+{
+ return Sequence< sal_Int8 >();
+}
+
+sal_Int32 SwXMLBlockListTokenHandler::getTokenDirect( const char *pTag, sal_Int32 nLength ) const
+{
+ if( !nLength )
+ nLength = strlen( pTag );
+ const struct xmltoken* pToken = BlockListTokens::in_word_set( pTag, nLength );
+ return pToken ? pToken->nToken : XML_TOKEN_INVALID;
+}
+
+SwXMLBlockListContext::SwXMLBlockListContext(
+ SwXMLBlockListImport& rImport, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+ : SvXMLImportContext(rImport)
+ , m_rLocalRef(rImport)
+{
+ if( xAttrList.is() && xAttrList->hasAttribute( SwXMLBlockListToken::LIST_NAME ) )
+ rImport.getBlockList().SetName( xAttrList->getValue( SwXMLBlockListToken::LIST_NAME ) );
+}
+
+uno::Reference< ::xml::sax::XFastContextHandler > SAL_CALL
+SwXMLBlockListContext::createFastChildContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if ( Element == SwXMLBlockListToken::BLOCK )
+ return new SwXMLBlockContext(m_rLocalRef, xAttrList);
+ return nullptr;
+}
+
+SwXMLBlockContext::SwXMLBlockContext(
+ SwXMLBlockListImport& rImport,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList ) :
+ SvXMLImportContext( rImport )
+{
+ static const CharClass & rCC = GetAppCharClass();
+ OUString aShort, aLong, aPackageName;
+ bool bTextOnly = false;
+ if( xAttrList.is() )
+ {
+ if( xAttrList->hasAttribute( SwXMLBlockListToken::ABBREVIATED_NAME ) )
+ aShort = rCC.uppercase( xAttrList->getValue( SwXMLBlockListToken::ABBREVIATED_NAME ) );
+ if( xAttrList->hasAttribute( SwXMLBlockListToken::NAME ) )
+ aLong = xAttrList->getValue( SwXMLBlockListToken::NAME );
+ if( xAttrList->hasAttribute( SwXMLBlockListToken::PACKAGE_NAME ) )
+ aPackageName = xAttrList->getValue( SwXMLBlockListToken::PACKAGE_NAME );
+ if( xAttrList->hasAttribute( SwXMLBlockListToken::UNFORMATTED_TEXT ) )
+ {
+ OUString rAttrValue( xAttrList->getValue( SwXMLBlockListToken::UNFORMATTED_TEXT ) );
+ if( IsXMLToken( rAttrValue, XML_TRUE ) )
+ bTextOnly = true;
+ }
+ }
+ if (aShort.isEmpty() || aLong.isEmpty() || aPackageName.isEmpty())
+ return;
+ rImport.getBlockList().AddName( aShort, aLong, aPackageName, bTextOnly);
+}
+
+SwXMLTextBlockDocumentContext::SwXMLTextBlockDocumentContext(SwXMLTextBlockImport& rImport)
+ : SvXMLImportContext(rImport)
+ , m_rLocalRef(rImport)
+{
+}
+
+uno::Reference< ::xml::sax::XFastContextHandler > SAL_CALL
+SwXMLTextBlockDocumentContext::createFastChildContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ if ( Element == SwXMLTextBlockToken::OFFICE_BODY )
+ return new SwXMLTextBlockBodyContext(m_rLocalRef);
+ return nullptr;
+}
+
+SwXMLTextBlockTextContext::SwXMLTextBlockTextContext(SwXMLTextBlockImport& rImport)
+ : SvXMLImportContext(rImport)
+ , m_rLocalRef(rImport)
+{
+}
+
+uno::Reference< xml::sax::XFastContextHandler > SAL_CALL
+SwXMLTextBlockTextContext::createFastChildContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ if ( Element == SwXMLTextBlockToken::TEXT_P )
+ return new SwXMLTextBlockParContext(m_rLocalRef);
+ return nullptr;
+}
+
+SwXMLTextBlockBodyContext::SwXMLTextBlockBodyContext(SwXMLTextBlockImport& rImport)
+ : SvXMLImportContext(rImport)
+ , m_rLocalRef(rImport)
+{
+}
+
+uno::Reference < xml::sax::XFastContextHandler > SAL_CALL
+SwXMLTextBlockBodyContext::createFastChildContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ if( Element == SwXMLTextBlockToken::OFFICE_TEXT )
+ return new SwXMLTextBlockTextContext(m_rLocalRef);
+ else if( Element == SwXMLTextBlockToken::TEXT_P )
+ return new SwXMLTextBlockParContext(m_rLocalRef);
+ return nullptr;
+}
+
+SwXMLTextBlockParContext::SwXMLTextBlockParContext(SwXMLTextBlockImport& rImport)
+ : SvXMLImportContext(rImport)
+ , m_rLocalRef(rImport)
+{
+}
+
+void SAL_CALL SwXMLTextBlockParContext::characters( const OUString & aChars )
+{
+ m_rLocalRef.m_rText += aChars;
+}
+
+SwXMLTextBlockParContext::~SwXMLTextBlockParContext()
+{
+ if (m_rLocalRef.m_bTextOnly)
+ m_rLocalRef.m_rText += "\015";
+ else
+ {
+ if (!m_rLocalRef.m_rText.endsWith(" "))
+ m_rLocalRef.m_rText += " ";
+ }
+}
+
+// SwXMLBlockListImport //////////////////////////////
+SwXMLBlockListImport::SwXMLBlockListImport(
+ const uno::Reference< uno::XComponentContext >& rContext,
+ SwXMLTextBlocks &rBlocks )
+: SvXMLImport( rContext, "", SvXMLImportFlags::NONE ),
+ m_rBlockList (rBlocks)
+{
+}
+
+SwXMLBlockListImport::~SwXMLBlockListImport()
+ noexcept
+{
+}
+
+SvXMLImportContext* SwXMLBlockListImport::CreateFastContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & xAttrList )
+{
+ if( Element == SwXMLBlockListToken::BLOCK_LIST )
+ return new SwXMLBlockListContext( *this, xAttrList );
+ return nullptr;
+}
+
+SwXMLTextBlockImport::SwXMLTextBlockImport(
+ const uno::Reference< uno::XComponentContext >& rContext,
+ OUString & rNewText,
+ bool bNewTextOnly )
+: SvXMLImport(rContext, "", SvXMLImportFlags::ALL ),
+ m_bTextOnly ( bNewTextOnly ),
+ m_rText ( rNewText )
+{
+}
+
+SwXMLTextBlockImport::~SwXMLTextBlockImport()
+ noexcept
+{
+}
+
+SvXMLImportContext* SwXMLTextBlockImport::CreateFastContext( sal_Int32 Element,
+ const uno::Reference< xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ if( Element == SwXMLTextBlockToken::OFFICE_DOCUMENT ||
+ Element == SwXMLTextBlockToken::OFFICE_DOCUMENT_CONTENT )
+ return new SwXMLTextBlockDocumentContext( *this );
+ return nullptr;
+}
+
+void SAL_CALL SwXMLTextBlockImport::endDocument()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/SwXMLSectionList.cxx b/sw/source/core/swg/SwXMLSectionList.cxx
new file mode 100644
index 000000000..42ed3e736
--- /dev/null
+++ b/sw/source/core/swg/SwXMLSectionList.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwXMLSectionList.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+namespace {
+
+class SvXMLSectionListContext : public SvXMLImportContext
+{
+private:
+ SwXMLSectionList & GetImport() { return static_cast<SwXMLSectionList&>(SvXMLImportContext::GetImport()); }
+
+public:
+ SvXMLSectionListContext(SwXMLSectionList& rImport);
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override;
+};
+
+class SwXMLParentContext : public SvXMLImportContext
+{
+private:
+ SwXMLSectionList & GetImport() { return static_cast<SwXMLSectionList&>(SvXMLImportContext::GetImport()); }
+
+public:
+ SwXMLParentContext(SwXMLSectionList& rImport)
+ : SvXMLImportContext(rImport)
+ {
+ }
+
+ virtual css::uno::Reference<XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ ) override
+ {
+ if (Element == XML_ELEMENT(OFFICE, XML_BODY) ||
+ Element == XML_ELEMENT(OFFICE_OOO, XML_BODY))
+ {
+ return new SvXMLSectionListContext(GetImport());
+ }
+ if (IsTokenInNamespace(Element, XML_NAMESPACE_TEXT) ||
+ IsTokenInNamespace(Element, XML_NAMESPACE_TEXT_OOO))
+ {
+ auto nToken = Element & TOKEN_MASK;
+ if (nToken == XML_P ||
+ nToken == XML_H ||
+ nToken == XML_A ||
+ nToken == XML_SPAN ||
+ nToken == XML_SECTION ||
+ nToken == XML_INDEX_BODY ||
+ nToken == XML_INDEX_TITLE ||
+ nToken == XML_INSERTION ||
+ nToken == XML_DELETION)
+ return new SvXMLSectionListContext(GetImport());
+ }
+ return new SwXMLParentContext(GetImport());
+ }
+};
+
+}
+
+SwXMLSectionList::SwXMLSectionList(const css::uno::Reference< css::uno::XComponentContext >& rContext, std::vector<OUString> &rNewSectionList)
+: SvXMLImport(rContext, "")
+, m_rSectionList(rNewSectionList)
+{
+}
+
+SwXMLSectionList::~SwXMLSectionList()
+ noexcept
+{
+}
+
+SvXMLImportContext * SwXMLSectionList::CreateFastContext(
+ sal_Int32 /*Element*/,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ )
+{
+ return new SwXMLParentContext(*this);
+}
+
+SvXMLSectionListContext::SvXMLSectionListContext( SwXMLSectionList& rImport )
+ : SvXMLImportContext ( rImport )
+{
+}
+
+css::uno::Reference<css::xml::sax::XFastContextHandler> SvXMLSectionListContext::createFastChildContext(
+ sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList )
+{
+ SvXMLImportContext *pContext = nullptr;
+
+ if (Element == XML_ELEMENT(TEXT, XML_SECTION ) ||
+ Element == XML_ELEMENT(TEXT, XML_BOOKMARK) ||
+ Element == XML_ELEMENT(TEXT_OOO, XML_SECTION ) ||
+ Element == XML_ELEMENT(TEXT_OOO, XML_BOOKMARK) )
+ {
+ OUString sName;
+ for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ if (aIter.getToken() == XML_ELEMENT(TEXT, XML_NAME) ||
+ aIter.getToken() == XML_ELEMENT(TEXT_OOO, XML_NAME))
+ sName = aIter.toString();
+ if ( !sName.isEmpty() )
+ GetImport().m_rSectionList.push_back(sName);
+ }
+
+ pContext = new SvXMLSectionListContext(GetImport());
+ return pContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/SwXMLTextBlocks.cxx b/sw/source/core/swg/SwXMLTextBlocks.cxx
new file mode 100644
index 000000000..4055cc9ed
--- /dev/null
+++ b/sw/source/core/swg/SwXMLTextBlocks.cxx
@@ -0,0 +1,572 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <osl/file.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sot/exchange.hxx>
+#include <sot/stg.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/ucbstreamhelper.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <comphelper/storagehelper.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <swblocks.hxx>
+#include <ndtxt.hxx>
+#include <shellio.hxx>
+#include <poolfmt.hxx>
+#include <SwXMLTextBlocks.hxx>
+#include <swerror.h>
+
+using namespace ::com::sun::star;
+
+void SwXMLTextBlocks::InitBlockMode ( const uno::Reference < embed::XStorage >& rStorage )
+{
+ m_xBlkRoot = rStorage;
+ m_xRoot = nullptr;
+}
+
+void SwXMLTextBlocks::ResetBlockMode ( )
+{
+ m_xBlkRoot = nullptr;
+ m_xRoot = nullptr;
+}
+
+SwXMLTextBlocks::SwXMLTextBlocks( const OUString& rFile )
+ : SwImpBlocks(rFile)
+ , m_nFlags(SwXmlFlags::NONE)
+{
+ SwDocShell* pDocSh = new SwDocShell ( SfxObjectCreateMode::INTERNAL );
+ if( !pDocSh->DoInitNew() )
+ return;
+ m_bReadOnly = true;
+ m_xDoc = pDocSh->GetDoc();
+ m_xDocShellRef = pDocSh;
+ m_xDoc->SetOle2Link( Link<bool,void>() );
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
+ uno::Reference< embed::XStorage > refStg;
+ if( !m_aDateModified.GetDate() || !m_aTimeModified.GetTime() )
+ Touch(); // If it's created anew -> get a new timestamp
+
+ try
+ {
+ refStg = comphelper::OStorageHelper::GetStorageFromURL( rFile, embed::ElementModes::READWRITE );
+ m_bReadOnly = false;
+ }
+ catch(const uno::Exception&)
+ {
+ //FIXME: couldn't open the file - maybe it's readonly
+ }
+ if( !refStg.is())
+ {
+ try
+ {
+ refStg = comphelper::OStorageHelper::GetStorageFromURL( rFile, embed::ElementModes::READ );
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "exception while creating AutoText storage");
+ }
+ }
+ InitBlockMode ( refStg );
+ ReadInfo();
+ ResetBlockMode ();
+ m_bInfoChanged = false;
+}
+
+SwXMLTextBlocks::SwXMLTextBlocks( const uno::Reference < embed::XStorage >& rStg, const OUString& rName )
+ : SwImpBlocks( rName )
+ , m_nFlags(SwXmlFlags::NONE)
+{
+ SwDocShell* pDocSh = new SwDocShell ( SfxObjectCreateMode::INTERNAL );
+ if( !pDocSh->DoInitNew() )
+ return;
+ m_bReadOnly = false;
+ m_xDoc = pDocSh->GetDoc();
+ m_xDocShellRef = pDocSh;
+ m_xDoc->SetOle2Link( Link<bool,void>() );
+ m_xDoc->GetIDocumentUndoRedo().DoUndo(false);
+
+ InitBlockMode ( rStg );
+ ReadInfo();
+ m_bInfoChanged = false;
+}
+
+SwXMLTextBlocks::~SwXMLTextBlocks()
+{
+ if ( m_bInfoChanged )
+ WriteInfo();
+ ResetBlockMode ();
+ if(m_xDocShellRef.is())
+ m_xDocShellRef->DoClose();
+ m_xDocShellRef = nullptr;
+}
+
+void SwXMLTextBlocks::ClearDoc()
+{
+ SwDocShell * pDocShell = m_xDoc->GetDocShell();
+ pDocShell->InvalidateModel();
+ pDocShell->ReactivateModel();
+
+ m_xDoc->ClearDoc();
+ pDocShell->ClearEmbeddedObjects();
+}
+
+void SwXMLTextBlocks::AddName( const OUString& rShort, const OUString& rLong, bool bOnlyText )
+{
+ m_aPackageName = GeneratePackageName( rShort );
+ AddName(rShort, rLong, m_aPackageName, bOnlyText);
+}
+
+void SwXMLTextBlocks::AddName( const OUString& rShort, const OUString& rLong,
+ const OUString& rPackageName, bool bOnlyText )
+{
+ sal_uInt16 nIdx = GetIndex( rShort );
+ if (nIdx != USHRT_MAX)
+ {
+ m_aNames.erase( m_aNames.begin() + nIdx );
+ }
+ std::unique_ptr<SwBlockName> pNew(new SwBlockName( rShort, rLong, rPackageName ));
+ pNew->m_bIsOnlyTextFlagInit = true;
+ pNew->m_bIsOnlyText = bOnlyText;
+ m_aNames.insert( std::move(pNew) );
+ m_bInfoChanged = true;
+}
+
+ErrCode SwXMLTextBlocks::Delete( sal_uInt16 n )
+{
+ const OUString aPckName (m_aNames[n]->m_aPackageName);
+ if ( m_xBlkRoot.is() &&
+ m_xBlkRoot->hasByName( aPckName ) && m_xBlkRoot->isStreamElement( aPckName ) )
+ {
+ try
+ {
+ m_xBlkRoot->removeElement ( aPckName );
+ uno::Reference < embed::XTransactedObject > xTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ return ERRCODE_NONE;
+ }
+ catch (const uno::Exception&)
+ {
+ return ERR_SWG_WRITE_ERROR;
+ }
+ }
+ return ERRCODE_NONE;
+}
+
+ErrCode SwXMLTextBlocks::Rename( sal_uInt16 nIdx, const OUString& rNewShort )
+{
+ OSL_ENSURE( m_xBlkRoot.is(), "No storage set" );
+ if(!m_xBlkRoot.is())
+ return ERRCODE_NONE;
+ OUString aOldName (m_aNames[nIdx]->m_aPackageName);
+ m_aShort = rNewShort;
+ m_aPackageName = GeneratePackageName( m_aShort );
+
+ if(aOldName != m_aPackageName)
+ {
+ if (IsOnlyTextBlock ( nIdx ) )
+ {
+ OUString sExt(".xml");
+ OUString aOldStreamName = aOldName + sExt;
+ OUString aNewStreamName = m_aPackageName + sExt;
+
+ m_xRoot = m_xBlkRoot->openStorageElement( aOldName, embed::ElementModes::READWRITE );
+ try
+ {
+ m_xRoot->renameElement ( aOldStreamName, aNewStreamName );
+ }
+ catch(const container::ElementExistException&)
+ {
+ SAL_WARN("sw", "Couldn't rename " << aOldStreamName << " to " << aNewStreamName);
+ }
+ uno::Reference < embed::XTransactedObject > xTrans( m_xRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ m_xRoot = nullptr;
+ }
+
+ try
+ {
+ m_xBlkRoot->renameElement ( aOldName, m_aPackageName );
+ }
+ catch(const container::ElementExistException&)
+ {
+ SAL_WARN("sw", "Couldn't rename " << aOldName << " to " << m_aPackageName);
+ }
+ }
+ uno::Reference < embed::XTransactedObject > xTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ // No need to commit xBlkRoot here as SwTextBlocks::Rename calls
+ // WriteInfo which does the commit
+ return ERRCODE_NONE;
+}
+
+ErrCode SwXMLTextBlocks::CopyBlock( SwImpBlocks& rDestImp, OUString& rShort,
+ const OUString& rLong)
+{
+ ErrCode nError = ERRCODE_NONE;
+ OpenFile();
+ rDestImp.OpenFile(false);
+ const OUString aGroup( rShort );
+ bool bTextOnly = IsOnlyTextBlock ( rShort ) ;//pImp->pBlkRoot->IsStream( aGroup );
+ sal_uInt16 nIndex = GetIndex ( rShort );
+ OUString sPackageName( GetPackageName (nIndex) );
+ OUString sDestShortName( sPackageName );
+ sal_uInt16 nIdx = 0;
+
+ OSL_ENSURE( m_xBlkRoot.is(), "No storage set" );
+ if(!m_xBlkRoot.is())
+ return ERR_SWG_WRITE_ERROR;
+
+ uno::Reference < container::XNameAccess > xAccess(static_cast<SwXMLTextBlocks&>(rDestImp).m_xBlkRoot);
+ while ( xAccess->hasByName( sDestShortName ) )
+ {
+ ++nIdx;
+ // If someone is that crazy ...
+ if(USHRT_MAX == nIdx)
+ {
+ CloseFile();
+ rDestImp.CloseFile();
+ return ERR_SWG_WRITE_ERROR;
+ }
+ sDestShortName = sPackageName + OUString::number( nIdx );
+ }
+
+ try
+ {
+ uno::Reference < embed::XStorage > rSourceRoot = m_xBlkRoot->openStorageElement( aGroup, embed::ElementModes::READ );
+ uno::Reference < embed::XStorage > rDestRoot = static_cast<SwXMLTextBlocks&>(rDestImp).m_xBlkRoot->openStorageElement( sDestShortName, embed::ElementModes::READWRITE );
+ rSourceRoot->copyToStorage( rDestRoot );
+ }
+ catch (const uno::Exception&)
+ {
+ nError = ERR_SWG_WRITE_ERROR;
+ }
+
+ if(!nError)
+ {
+ rShort = sDestShortName;
+ static_cast<SwXMLTextBlocks&>(rDestImp).AddName( rShort, rLong, bTextOnly );
+ static_cast<SwXMLTextBlocks&>(rDestImp).MakeBlockList();
+ }
+ CloseFile();
+ rDestImp.CloseFile();
+ return nError;
+}
+
+ErrCode SwXMLTextBlocks::StartPutBlock( const OUString& rShort, const OUString& rPackageName )
+{
+ OSL_ENSURE( m_xBlkRoot.is(), "No storage set" );
+ if(!m_xBlkRoot.is())
+ return ERRCODE_NONE;
+ GetIndex ( rShort );
+ try
+ {
+ m_xRoot = m_xBlkRoot->openStorageElement( rPackageName, embed::ElementModes::READWRITE );
+
+ uno::Reference< beans::XPropertySet > xRootProps( m_xRoot, uno::UNO_QUERY_THROW );
+ OUString aMime( SotExchange::GetFormatMimeType( SotClipboardFormatId::STARWRITER_8 ) );
+ xRootProps->setPropertyValue( "MediaType", uno::Any( aMime ) );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return ERRCODE_NONE;
+}
+
+ErrCode SwXMLTextBlocks::BeginPutDoc( const OUString& rShort, const OUString& rLong )
+{
+ // Store in base class
+ m_aShort = rShort;
+ m_aLong = rLong;
+ m_aPackageName = GeneratePackageName( rShort );
+ SetIsTextOnly( rShort, false);
+ return StartPutBlock (rShort, m_aPackageName);
+}
+
+ErrCode SwXMLTextBlocks::PutBlock()
+{
+ ErrCode nRes = ERRCODE_NONE; // dead variable, this always returns 0
+ SwXmlFlags nCommitFlags = m_nFlags;
+
+ WriterRef xWrt;
+ ::GetXMLWriter ( std::u16string_view(), GetBaseURL(), xWrt);
+ SwWriter aWriter (m_xRoot, *m_xDoc );
+
+ xWrt->m_bBlock = true;
+ nRes = aWriter.Write ( xWrt );
+ xWrt->m_bBlock = false;
+ // Save OLE objects if there are some
+ SwDocShell *pDocSh = m_xDoc->GetDocShell();
+
+ bool bHasChildren = pDocSh && pDocSh->GetEmbeddedObjectContainer().HasEmbeddedObjects();
+ if( !nRes && bHasChildren )
+ {
+ // we have to write to the temporary storage first, since the used below functions are optimized
+ // TODO/LATER: it is only a temporary solution, that should be changed soon, the used methods should be
+ // called without optimization
+ bool bOK = false;
+
+ if ( m_xRoot.is() )
+ {
+ std::unique_ptr<SfxMedium> pTmpMedium;
+ try
+ {
+ uno::Reference< embed::XStorage > xTempStorage =
+ ::comphelper::OStorageHelper::GetTemporaryStorage();
+
+ m_xRoot->copyToStorage( xTempStorage );
+
+ // TODO/LATER: no progress bar?!
+ // TODO/MBA: strange construct
+ pTmpMedium.reset(new SfxMedium(xTempStorage, GetBaseURL()));
+ bool bTmpOK = pDocSh->SaveAsChildren( *pTmpMedium );
+ if( bTmpOK )
+ bTmpOK = pDocSh->SaveCompletedChildren();
+
+ xTempStorage->copyToStorage( m_xRoot );
+ bOK = bTmpOK;
+ }
+ catch(const uno::Exception&)
+ {
+ }
+ }
+
+ if( !bOK )
+ nRes = ERR_SWG_WRITE_ERROR;
+ }
+
+ try
+ {
+ uno::Reference < embed::XTransactedObject > xTrans( m_xRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ m_xRoot = nullptr;
+ if ( nCommitFlags == SwXmlFlags::NONE )
+ {
+ uno::Reference < embed::XTransactedObject > xTmpTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTmpTrans.is() )
+ xTmpTrans->commit();
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ //TODO/LATER: error handling
+ return ERRCODE_NONE;
+}
+
+ErrCode SwXMLTextBlocks::PutDoc()
+{
+ std::unique_ptr<SwPaM> pPaM = MakePaM();
+ ErrCode nErr = PutBlock();
+ return nErr;
+}
+
+ErrCode SwXMLTextBlocks::GetText( std::u16string_view rShort, OUString& rText )
+{
+ return GetBlockText( rShort, rText );
+}
+
+ErrCode SwXMLTextBlocks::MakeBlockList()
+{
+ WriteInfo();
+ return ERRCODE_NONE;
+}
+
+bool SwXMLTextBlocks::PutMuchEntries( bool bOn )
+{
+ bool bRet = false;
+ if( bOn )
+ {
+ if( m_bInPutMuchBlocks )
+ {
+ OSL_ENSURE( false, "Nested calls are not allowed");
+ }
+ else if( !IsFileChanged() )
+ {
+ bRet = ERRCODE_NONE == OpenFile( false );
+ if( bRet )
+ {
+ m_nFlags |= SwXmlFlags::NoRootCommit;
+ m_bInPutMuchBlocks = true;
+ }
+ }
+ }
+ else if( m_bInPutMuchBlocks )
+ {
+ m_nFlags &= ~SwXmlFlags::NoRootCommit;
+ if( m_xBlkRoot.is() )
+ {
+ try
+ {
+ uno::Reference < embed::XTransactedObject > xTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ MakeBlockList();
+ CloseFile();
+ Touch();
+ m_bInPutMuchBlocks = false;
+ bRet = true;
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ }
+ }
+ return bRet;
+}
+
+ErrCode SwXMLTextBlocks::OpenFile( bool bRdOnly )
+{
+ ErrCode nRet = ERRCODE_NONE;
+ try
+ {
+ uno::Reference < embed::XStorage > refStg = comphelper::OStorageHelper::GetStorageFromURL( m_aFile,
+ bRdOnly ? embed::ElementModes::READ : embed::ElementModes::READWRITE );
+ InitBlockMode ( refStg );
+ }
+ catch (const uno::Exception&)
+ {
+ //TODO/LATER: error handling
+ nRet = ErrCode(1);
+ }
+
+ return nRet;
+}
+
+void SwXMLTextBlocks::CloseFile()
+{
+ if (m_bInfoChanged)
+ WriteInfo();
+ ResetBlockMode();
+}
+
+void SwXMLTextBlocks::SetIsTextOnly( const OUString& rShort, bool bNewValue )
+{
+ sal_uInt16 nIdx = GetIndex ( rShort );
+ if (nIdx != USHRT_MAX)
+ m_aNames[nIdx]->m_bIsOnlyText = bNewValue;
+}
+
+bool SwXMLTextBlocks::IsOnlyTextBlock( const OUString& rShort ) const
+{
+ sal_uInt16 nIdx = GetIndex ( rShort );
+ bool bRet = false;
+ if (nIdx != USHRT_MAX)
+ {
+ bRet = m_aNames[nIdx]->m_bIsOnlyText;
+ }
+ return bRet;
+}
+bool SwXMLTextBlocks::IsOnlyTextBlock( sal_uInt16 nIdx ) const
+{
+ return m_aNames[nIdx]->m_bIsOnlyText;
+}
+
+bool SwXMLTextBlocks::IsFileUCBStorage( const OUString & rFileName)
+{
+ OUString aName( rFileName );
+ INetURLObject aObj( aName );
+ if ( aObj.GetProtocol() == INetProtocol::NotValid )
+ {
+ OUString aURL;
+ osl::FileBase::getFileURLFromSystemPath( aName, aURL );
+ aObj.SetURL( aURL );
+ aName = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+
+ std::unique_ptr<SvStream> pStm = ::utl::UcbStreamHelper::CreateStream( aName, StreamMode::STD_READ );
+ bool bRet = UCBStorage::IsStorageFile( pStm.get() );
+ return bRet;
+}
+
+OUString SwXMLTextBlocks::GeneratePackageName ( std::u16string_view rShort )
+{
+ OString sByte(OUStringToOString(rShort, RTL_TEXTENCODING_UTF7));
+ OUStringBuffer aBuf(OStringToOUString(sByte, RTL_TEXTENCODING_ASCII_US));
+ const sal_Int32 nLen = aBuf.getLength();
+ for (sal_Int32 nPos=0; nPos<nLen; ++nPos)
+ {
+ switch (aBuf[nPos])
+ {
+ case '!':
+ case '/':
+ case ':':
+ case '.':
+ case '\\':
+ aBuf[nPos] = '_';
+ break;
+ default:
+ break;
+ }
+ }
+ return aBuf.makeStringAndClear();
+}
+
+ErrCode SwXMLTextBlocks::PutText( const OUString& rShort, const OUString& rName,
+ const OUString& rText )
+{
+ ErrCode nRes = ERRCODE_NONE;
+ m_aShort = rShort;
+ m_aLong = rName;
+ m_aCurrentText = rText;
+ SetIsTextOnly( m_aShort, true );
+ m_aPackageName = GeneratePackageName( rShort );
+ ClearDoc();
+ nRes = PutBlockText( rShort, rText, m_aPackageName );
+ return nRes;
+}
+
+void SwXMLTextBlocks::MakeBlockText( std::u16string_view rText )
+{
+ SwTextNode* pTextNode = m_xDoc->GetNodes()[ m_xDoc->GetNodes().GetEndOfContent().
+ GetIndex() - 1 ]->GetTextNode();
+ if( pTextNode->GetTextColl() == m_xDoc->GetDfltTextFormatColl() )
+ pTextNode->ChgFormatColl( m_xDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ));
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ if ( nPos )
+ {
+ pTextNode = static_cast<SwTextNode*>(pTextNode->AppendNode( SwPosition( *pTextNode ) ));
+ }
+ SwIndex aIdx( pTextNode );
+ pTextNode->InsertText( OUString(o3tl::getToken(rText, 0, '\015', nPos )), aIdx );
+ } while ( -1 != nPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/SwXMLTextBlocks1.cxx b/sw/source/core/swg/SwXMLTextBlocks1.cxx
new file mode 100644
index 000000000..ba610776e
--- /dev/null
+++ b/sw/source/core/swg/SwXMLTextBlocks1.cxx
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <osl/diagnose.h>
+#include <svl/macitem.hxx>
+#include <svtools/unoevent.hxx>
+#include <sfx2/docfile.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/FastToken.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <shellio.hxx>
+#include <SwXMLTextBlocks.hxx>
+#include <SwXMLBlockImport.hxx>
+#include <SwXMLBlockExport.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <sfx2/event.hxx>
+#include <swerror.h>
+
+constexpr OUStringLiteral XMLN_BLOCKLIST = u"BlockList.xml";
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace css::xml::sax;
+using namespace xmloff::token;
+
+using ::xmloff::token::XML_BLOCK_LIST;
+using ::xmloff::token::XML_UNFORMATTED_TEXT;
+using ::xmloff::token::GetXMLToken;
+
+ErrCode SwXMLTextBlocks::GetDoc( sal_uInt16 nIdx )
+{
+ OUString aFolderName ( GetPackageName ( nIdx ) );
+
+ if (!IsOnlyTextBlock ( nIdx ) )
+ {
+ try
+ {
+ m_xRoot = m_xBlkRoot->openStorageElement( aFolderName, embed::ElementModes::READ );
+ m_xMedium = new SfxMedium( m_xRoot, GetBaseURL(), "writer8" );
+ SwReader aReader( *m_xMedium, aFolderName, m_xDoc.get() );
+ ReadXML->SetBlockMode( true );
+ aReader.Read( *ReadXML );
+ ReadXML->SetBlockMode( false );
+ // Ole objects fail to display when inserted into the document, as
+ // the ObjectReplacement folder and contents are missing
+ OUString sObjReplacements( "ObjectReplacements" );
+ if ( m_xRoot->hasByName( sObjReplacements ) )
+ {
+ uno::Reference< document::XStorageBasedDocument > xDocStor( m_xDoc->GetDocShell()->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< embed::XStorage > xStr( xDocStor->getDocumentStorage() );
+ if ( xStr.is() )
+ {
+ m_xRoot->copyElementTo( sObjReplacements, xStr, sObjReplacements );
+ uno::Reference< embed::XTransactedObject > xTrans( xStr, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ m_xRoot = nullptr;
+ }
+ else
+ {
+ OUString aStreamName = aFolderName + ".xml";
+ try
+ {
+ m_xRoot = m_xBlkRoot->openStorageElement( aFolderName, embed::ElementModes::READ );
+ uno::Reference < io::XStream > xStream = m_xRoot->openStreamElement( aStreamName, embed::ElementModes::READ );
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = m_aNames[nIdx]->m_aPackageName;
+
+ aParserInput.aInputStream = xStream->getInputStream();
+
+ // get filter
+ uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SwXMLTextBlockImport( xContext, m_aCurrentText, true );
+ uno::Reference< xml::sax::XFastTokenHandler > xTokenHandler = new SwXMLTextBlockTokenHandler();
+
+ // connect parser and filter
+ uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create(xContext);
+ xParser->setFastDocumentHandler( xFilter );
+ xParser->setTokenHandler( xTokenHandler );
+
+ xParser->registerNamespace( "http://openoffice.org/2000/text", FastToken::NAMESPACE | XML_NAMESPACE_TEXT );
+ xParser->registerNamespace( "http://openoffice.org/2000/office", FastToken::NAMESPACE | XML_NAMESPACE_OFFICE );
+
+ // parse
+ try
+ {
+ xParser->parseStream( aParserInput );
+ }
+ catch( xml::sax::SAXParseException& )
+ {
+ // re throw ?
+ }
+ catch( xml::sax::SAXException& )
+ {
+ // re throw ?
+ }
+ catch( io::IOException& )
+ {
+ // re throw ?
+ }
+
+ m_bInfoChanged = false;
+ MakeBlockText(m_aCurrentText);
+ }
+ catch( uno::Exception& )
+ {
+ }
+
+ m_xRoot = nullptr;
+ }
+ return ERRCODE_NONE;
+}
+
+// event description for autotext events; this constant should really be
+// taken from unocore/unoevents.cxx or ui/unotxt.cxx
+const struct SvEventDescription aAutotextEvents[] =
+{
+ { SvMacroItemId::SwStartInsGlossary, "OnInsertStart" },
+ { SvMacroItemId::SwEndInsGlossary, "OnInsertDone" },
+ { SvMacroItemId::NONE, nullptr }
+};
+
+ErrCode SwXMLTextBlocks::GetMacroTable( sal_uInt16 nIdx,
+ SvxMacroTableDtor& rMacroTable )
+{
+ // set current auto text
+ m_aShort = m_aNames[nIdx]->m_aShort;
+ m_aLong = m_aNames[nIdx]->m_aLong;
+ m_aPackageName = m_aNames[nIdx]->m_aPackageName;
+
+ // open stream in proper sub-storage
+ CloseFile();
+ if ( OpenFile() != ERRCODE_NONE )
+ return ERR_SWG_READ_ERROR;
+
+ try
+ {
+ m_xRoot = m_xBlkRoot->openStorageElement( m_aPackageName, embed::ElementModes::READ );
+ bool bOasis = SotStorage::GetVersion( m_xRoot ) > SOFFICE_FILEFORMAT_60;
+
+ uno::Reference < io::XStream > xDocStream = m_xRoot->openStreamElement(
+ "atevent.xml", embed::ElementModes::READ );
+ OSL_ENSURE(xDocStream.is(), "Can't create stream");
+ if ( !xDocStream.is() )
+ return ERR_SWG_READ_ERROR;
+
+ uno::Reference<io::XInputStream> xInputStream = xDocStream->getInputStream();
+
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = m_aName;
+ aParserInput.aInputStream = xInputStream;
+
+ // get service factory
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ // get parser
+ uno::Reference< xml::sax::XParser > xParser = xml::sax::Parser::create( xContext );
+
+ // create descriptor and reference to it. Either
+ // both or neither must be kept because of the
+ // reference counting!
+ rtl::Reference<SvMacroTableEventDescriptor> pDescriptor =
+ new SvMacroTableEventDescriptor(aAutotextEvents);
+ Sequence<Any> aFilterArguments{ Any(uno::Reference<XNameReplace>(pDescriptor)) };
+
+ // get filter
+ OUString sFilterComponent = bOasis
+ ? OUString("com.sun.star.comp.Writer.XMLOasisAutotextEventsImporter")
+ : OUString("com.sun.star.comp.Writer.XMLAutotextEventsImporter");
+ uno::Reference< xml::sax::XDocumentHandler > xFilter(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ sFilterComponent, aFilterArguments, xContext),
+ UNO_QUERY );
+ OSL_ENSURE( xFilter.is(), "can't instantiate autotext-events filter");
+ if ( !xFilter.is() )
+ return ERR_SWG_READ_ERROR;
+
+ // connect parser and filter
+ xParser->setDocumentHandler( xFilter );
+
+ // parse the stream
+ try
+ {
+ xParser->parseStream( aParserInput );
+ }
+ catch( xml::sax::SAXParseException& )
+ {
+ // workaround for #83452#: SetSize doesn't work
+ // nRet = ERR_SWG_READ_ERROR;
+ }
+ catch( xml::sax::SAXException& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ return ERR_SWG_READ_ERROR;
+ }
+ catch( io::IOException& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ return ERR_SWG_READ_ERROR;
+ }
+
+ // and finally, copy macro into table
+ pDescriptor->copyMacrosIntoTable(rMacroTable);
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "");
+ return ERR_SWG_READ_ERROR;
+ }
+
+ // success!
+ return ERRCODE_NONE;
+}
+
+ErrCode SwXMLTextBlocks::GetBlockText( std::u16string_view rShort, OUString& rText )
+{
+ OUString aFolderName = GeneratePackageName ( rShort );
+ OUString aStreamName = aFolderName + ".xml";
+ rText.clear();
+
+ try
+ {
+ bool bTextOnly = true;
+
+ m_xRoot = m_xBlkRoot->openStorageElement( aFolderName, embed::ElementModes::READ );
+ if ( !m_xRoot->hasByName( aStreamName ) || !m_xRoot->isStreamElement( aStreamName ) )
+ {
+ bTextOnly = false;
+ aStreamName = "content.xml";
+ }
+
+ uno::Reference < io::XStream > xContents = m_xRoot->openStreamElement( aStreamName, embed::ElementModes::READ );
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = m_aName;
+ aParserInput.aInputStream = xContents->getInputStream();
+
+ // get filter
+ uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SwXMLTextBlockImport( xContext, rText, bTextOnly );
+ uno::Reference< xml::sax::XFastTokenHandler > xTokenHandler = new SwXMLTextBlockTokenHandler();
+
+ // connect parser and filter
+ uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create(xContext);
+ xParser->setFastDocumentHandler( xFilter );
+ xParser->setTokenHandler( xTokenHandler );
+
+ xParser->registerNamespace( "urn:oasis:names:tc:opendocument:xmlns:office:1.0", FastToken::NAMESPACE | XML_NAMESPACE_OFFICE );
+ xParser->registerNamespace( "urn:oasis:names:tc:opendocument:xmlns:text:1.0", FastToken::NAMESPACE | XML_NAMESPACE_TEXT );
+
+ // parse
+ try
+ {
+ xParser->parseStream( aParserInput );
+ }
+ catch( xml::sax::SAXParseException& )
+ {
+ // re throw ?
+ }
+ catch( xml::sax::SAXException& )
+ {
+ // re throw ?
+ }
+ catch( io::IOException& )
+ {
+ // re throw ?
+ }
+
+ m_xRoot = nullptr;
+ }
+ catch ( uno::Exception& )
+ {
+ OSL_FAIL( "Tried to open non-existent folder or stream!");
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode SwXMLTextBlocks::PutBlockText( const OUString& rShort,
+ std::u16string_view rText, const OUString& rPackageName )
+{
+ GetIndex ( rShort );
+ /*
+ if (xBlkRoot->IsContained ( rPackageName ) )
+ {
+ xBlkRoot->Remove ( rPackageName );
+ xBlkRoot->Commit ( );
+ }
+ */
+ OUString aStreamName = rPackageName + ".xml";
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
+ ErrCode nRes = ERRCODE_NONE;
+
+ try
+ {
+ m_xRoot = m_xBlkRoot->openStorageElement( rPackageName, embed::ElementModes::WRITE );
+ uno::Reference < io::XStream > xDocStream = m_xRoot->openStreamElement( aStreamName,
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE );
+
+ uno::Reference < beans::XPropertySet > xSet( xDocStream, uno::UNO_QUERY );
+ xSet->setPropertyValue("MediaType", Any(OUString( "text/xml" )) );
+ uno::Reference < io::XOutputStream > xOut = xDocStream->getOutputStream();
+ xWriter->setOutputStream(xOut);
+
+ rtl::Reference<SwXMLTextBlockExport> xExp( new SwXMLTextBlockExport( xContext, *this, GetXMLToken ( XML_UNFORMATTED_TEXT ), xWriter) );
+
+ xExp->exportDoc( rText );
+
+ uno::Reference < embed::XTransactedObject > xTrans( m_xRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+
+ if (! (m_nFlags & SwXmlFlags::NoRootCommit) )
+ {
+ uno::Reference < embed::XTransactedObject > xTmpTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTmpTrans.is() )
+ xTmpTrans->commit();
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ nRes = ERR_SWG_WRITE_ERROR;
+ }
+
+ m_xRoot = nullptr;
+
+ //TODO/LATER: error handling
+ /*
+ sal_uLong nErr = xBlkRoot->GetError();
+ sal_uLong nRes = 0;
+ if( nErr == SVSTREAM_DISK_FULL )
+ nRes = ERR_W4W_WRITE_FULL;
+ else if( nErr != ERRCODE_NONE )
+ nRes = ERR_SWG_WRITE_ERROR;
+ */
+ if( !nRes ) // So that we can access the Doc via GetText & nCur
+ MakeBlockText( rText );
+
+ return nRes;
+}
+
+void SwXMLTextBlocks::ReadInfo()
+{
+ const OUString sDocName( XMLN_BLOCKLIST );
+ try
+ {
+ if ( !m_xBlkRoot.is() || !m_xBlkRoot->hasByName( sDocName ) || !m_xBlkRoot->isStreamElement( sDocName ) )
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ xml::sax::InputSource aParserInput;
+ aParserInput.sSystemId = sDocName;
+
+ uno::Reference < io::XStream > xDocStream = m_xBlkRoot->openStreamElement( sDocName, embed::ElementModes::READ );
+ aParserInput.aInputStream = xDocStream->getInputStream();
+
+ // get filter
+ uno::Reference< xml::sax::XFastDocumentHandler > xFilter = new SwXMLBlockListImport( xContext, *this );
+ uno::Reference< xml::sax::XFastTokenHandler > xTokenHandler = new SwXMLBlockListTokenHandler();
+
+ // connect parser and filter
+ uno::Reference< xml::sax::XFastParser > xParser = xml::sax::FastParser::create(xContext);
+ xParser->setFastDocumentHandler( xFilter );
+ xParser->registerNamespace( "http://openoffice.org/2001/block-list", FastToken::NAMESPACE | XML_NAMESPACE_BLOCKLIST );
+ xParser->setTokenHandler( xTokenHandler );
+
+ // parse
+ xParser->parseStream( aParserInput );
+ }
+ catch ( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sw", "when loading " << sDocName);
+ // re throw ?
+ }
+}
+void SwXMLTextBlocks::WriteInfo()
+{
+ if ( !(m_xBlkRoot.is() || ERRCODE_NONE == OpenFile ( false )) )
+ return;
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ uno::Reference < xml::sax::XWriter > xWriter = xml::sax::Writer::create(xContext);
+
+ /*
+ if ( xBlkRoot->IsContained( sDocName) )
+ {
+ xBlkRoot->Remove ( sDocName );
+ xBlkRoot->Commit();
+ }
+ */
+
+ try
+ {
+ uno::Reference < io::XStream > xDocStream = m_xBlkRoot->openStreamElement( XMLN_BLOCKLIST,
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE );
+
+ uno::Reference < beans::XPropertySet > xSet( xDocStream, uno::UNO_QUERY );
+ xSet->setPropertyValue("MediaType", Any(OUString( "text/xml" )) );
+ uno::Reference < io::XOutputStream > xOut = xDocStream->getOutputStream();
+ xWriter->setOutputStream(xOut);
+
+ rtl::Reference<SwXMLBlockListExport> xExp(new SwXMLBlockListExport( xContext, *this, XMLN_BLOCKLIST, xWriter) );
+
+ xExp->exportDoc( XML_BLOCK_LIST );
+
+ uno::Reference < embed::XTransactedObject > xTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ m_bInfoChanged = false;
+ return;
+}
+
+ErrCode SwXMLTextBlocks::SetMacroTable(
+ sal_uInt16 nIdx,
+ const SvxMacroTableDtor& rMacroTable )
+{
+ // set current autotext
+ m_aShort = m_aNames[nIdx]->m_aShort;
+ m_aLong = m_aNames[nIdx]->m_aLong;
+ m_aPackageName = m_aNames[nIdx]->m_aPackageName;
+
+ // start XML autotext event export
+ ErrCode nRes = ERRCODE_NONE;
+
+ uno::Reference< uno::XComponentContext > xContext =
+ comphelper::getProcessComponentContext();
+
+ // Get model
+ uno::Reference< lang::XComponent > xModelComp =
+ m_xDoc->GetDocShell()->GetModel();
+ OSL_ENSURE( xModelComp.is(), "XMLWriter::Write: got no model" );
+ if( !xModelComp.is() )
+ return ERR_SWG_WRITE_ERROR;
+
+ // open stream in proper sub-storage
+ CloseFile(); // close (it may be open in read-only-mode)
+ nRes = OpenFile ( false );
+
+ if ( ERRCODE_NONE == nRes )
+ {
+ try
+ {
+ m_xRoot = m_xBlkRoot->openStorageElement( m_aPackageName, embed::ElementModes::WRITE );
+ bool bOasis = SotStorage::GetVersion( m_xRoot ) > SOFFICE_FILEFORMAT_60;
+
+ uno::Reference < io::XStream > xDocStream = m_xRoot->openStreamElement( "atevent.xml",
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE );
+
+ uno::Reference < beans::XPropertySet > xSet( xDocStream, uno::UNO_QUERY );
+ xSet->setPropertyValue("MediaType", Any(OUString( "text/xml" )) );
+ uno::Reference < io::XOutputStream > xOutputStream = xDocStream->getOutputStream();
+
+ // get XML writer
+ uno::Reference< xml::sax::XWriter > xSaxWriter =
+ xml::sax::Writer::create( xContext );
+
+ // connect XML writer to output stream
+ xSaxWriter->setOutputStream( xOutputStream );
+
+ // construct events object
+ uno::Reference<XNameAccess> xEvents =
+ new SvMacroTableEventDescriptor(rMacroTable,aAutotextEvents);
+
+ // prepare arguments (prepend doc handler to given arguments)
+ Sequence<Any> aParams{ Any(xSaxWriter), Any(xEvents) };
+
+ // get filter component
+ OUString sFilterComponent = bOasis
+ ? OUString("com.sun.star.comp.Writer.XMLOasisAutotextEventsExporter")
+ : OUString("com.sun.star.comp.Writer.XMLAutotextEventsExporter");
+ uno::Reference< document::XExporter > xExporter(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ sFilterComponent, aParams, xContext), UNO_QUERY);
+ OSL_ENSURE( xExporter.is(),
+ "can't instantiate export filter component" );
+ if( xExporter.is() )
+ {
+ // connect model and filter
+ xExporter->setSourceDocument( xModelComp );
+
+ // filter!
+ Sequence<beans::PropertyValue> aFilterProps( 0 );
+ uno::Reference < document::XFilter > xFilter( xExporter,
+ UNO_QUERY );
+ xFilter->filter( aFilterProps );
+ }
+ else
+ nRes = ERR_SWG_WRITE_ERROR;
+
+ // finally, commit stream, sub-storage and storage
+ uno::Reference < embed::XTransactedObject > xTmpTrans( m_xRoot, uno::UNO_QUERY );
+ if ( xTmpTrans.is() )
+ xTmpTrans->commit();
+
+ uno::Reference < embed::XTransactedObject > xTrans( m_xBlkRoot, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+
+ m_xRoot = nullptr;
+ }
+ catch ( uno::Exception& )
+ {
+ nRes = ERR_SWG_WRITE_ERROR;
+ }
+
+ CloseFile();
+ }
+ else
+ nRes = ERR_SWG_WRITE_ERROR;
+
+ return nRes;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/swg/TextBlockTokens.txt b/sw/source/core/swg/TextBlockTokens.txt
new file mode 100644
index 000000000..8698704fc
--- /dev/null
+++ b/sw/source/core/swg/TextBlockTokens.txt
@@ -0,0 +1,5 @@
+body
+text
+document
+document-content
+p
diff --git a/sw/source/core/swg/swblocks.cxx b/sw/source/core/swg/swblocks.cxx
new file mode 100644
index 000000000..ca4983d99
--- /dev/null
+++ b/sw/source/core/swg/swblocks.cxx
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <algorithm>
+
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <svl/fstathelper.hxx>
+#include <svl/macitem.hxx>
+#include <unotools/charclass.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <shellio.hxx>
+#include <swblocks.hxx>
+#include <SwXMLTextBlocks.hxx>
+
+#include <swerror.h>
+
+/**
+ * Calculate hash code (is not guaranteed to be unique)
+ */
+sal_uInt16 SwImpBlocks::Hash( const OUString& r )
+{
+ sal_uInt16 n = 0;
+ // std::min requires an explicit cast to sal_Int32 on 32bit platforms
+ const sal_Int32 nLen = std::min(r.getLength(), static_cast<sal_Int32>(8));
+ for (sal_Int32 i=0; i<nLen; ++i)
+ {
+ n = ( n << 1 ) + r[i];
+ }
+ return n;
+}
+
+SwBlockName::SwBlockName( const OUString& rShort, const OUString& rLong )
+ : m_aShort( rShort ), m_aLong( rLong ), m_aPackageName (rShort),
+ m_bIsOnlyTextFlagInit( false ), m_bIsOnlyText( false )
+{
+ m_nHashS = SwImpBlocks::Hash( rShort );
+ m_nHashL = SwImpBlocks::Hash( rLong );
+}
+
+SwBlockName::SwBlockName( const OUString& rShort, const OUString& rLong, const OUString& rPackageName)
+ : m_aShort( rShort ), m_aLong( rLong ), m_aPackageName (rPackageName),
+ m_bIsOnlyTextFlagInit( false ), m_bIsOnlyText( false )
+{
+ m_nHashS = SwImpBlocks::Hash( rShort );
+ m_nHashL = SwImpBlocks::Hash( rLong );
+}
+
+/**
+ * Is the provided file a storage or doesn't it exist?
+ */
+SwImpBlocks::FileType SwImpBlocks::GetFileType( const OUString& rFile )
+{
+ if( !FStatHelper::IsDocument( rFile ) )
+ return FileType::NoFile;
+ if( SwXMLTextBlocks::IsFileUCBStorage( rFile ) )
+ return FileType::XML;
+ //otherwise return NONE
+ return FileType::None;
+}
+
+SwImpBlocks::SwImpBlocks( const OUString& rFile )
+ : m_aFile( rFile ),
+ m_aDateModified( Date::EMPTY ),
+ m_aTimeModified( tools::Time::EMPTY ),
+ m_nCurrentIndex( USHRT_MAX ),
+ m_bReadOnly( true ), m_bInPutMuchBlocks( false ),
+ m_bInfoChanged(false)
+{
+ FStatHelper::GetModifiedDateTimeOfFile( rFile,
+ &m_aDateModified, &m_aTimeModified );
+ INetURLObject aObj(rFile);
+ aObj.setExtension( u"" );
+ m_aName = aObj.GetBase();
+}
+
+SwImpBlocks::~SwImpBlocks()
+{
+}
+
+/**
+ * Delete the document's content
+ */
+void SwImpBlocks::ClearDoc()
+{
+ m_xDoc->ClearDoc();
+}
+
+/**
+ * Creating a PaM, that spans the whole document
+ */
+std::unique_ptr<SwPaM> SwImpBlocks::MakePaM()
+{
+ std::unique_ptr<SwPaM> pPam(new SwPaM( m_xDoc->GetNodes().GetEndOfContent() ));
+ pPam->Move( fnMoveBackward, GoInDoc );
+ pPam->SetMark();
+ pPam->Move( fnMoveForward, GoInDoc );
+ pPam->Exchange();
+ return pPam;
+}
+
+sal_uInt16 SwImpBlocks::GetCount() const
+{
+ return m_aNames.size();
+}
+
+/**
+ * Case Insensitive
+ */
+sal_uInt16 SwImpBlocks::GetIndex( const OUString& rShort ) const
+{
+ const OUString s( GetAppCharClass().uppercase( rShort ) );
+ const sal_uInt16 nHash = Hash( s );
+ for( size_t i = 0; i < m_aNames.size(); i++ )
+ {
+ const SwBlockName* pName = m_aNames[ i ].get();
+ if( pName->m_nHashS == nHash
+ && pName->m_aShort == s )
+ return i;
+ }
+ return USHRT_MAX;
+}
+
+sal_uInt16 SwImpBlocks::GetLongIndex( const OUString& rLong ) const
+{
+ sal_uInt16 nHash = Hash( rLong );
+ for( size_t i = 0; i < m_aNames.size(); i++ )
+ {
+ const SwBlockName* pName = m_aNames[ i ].get();
+ if( pName->m_nHashL == nHash
+ && pName->m_aLong == rLong )
+ return i;
+ }
+ return USHRT_MAX;
+}
+
+OUString SwImpBlocks::GetShortName( sal_uInt16 n ) const
+{
+ if( n < m_aNames.size() )
+ return m_aNames[n]->m_aShort;
+ return OUString();
+}
+
+OUString SwImpBlocks::GetLongName( sal_uInt16 n ) const
+{
+ if( n < m_aNames.size() )
+ return m_aNames[n]->m_aLong;
+ return OUString();
+}
+
+OUString SwImpBlocks::GetPackageName( sal_uInt16 n ) const
+{
+ if( n < m_aNames.size() )
+ return m_aNames[n]->m_aPackageName;
+ return OUString();
+}
+
+void SwImpBlocks::AddName( const OUString& rShort, const OUString& rLong,
+ bool bOnlyText )
+{
+ sal_uInt16 nIdx = GetIndex( rShort );
+ if( nIdx != USHRT_MAX )
+ {
+ m_aNames.erase( m_aNames.begin() + nIdx );
+ }
+ std::unique_ptr<SwBlockName> pNew(new SwBlockName( rShort, rLong ));
+ pNew->m_bIsOnlyTextFlagInit = true;
+ pNew->m_bIsOnlyText = bOnlyText;
+ m_aNames.insert( std::move(pNew) );
+}
+
+bool SwImpBlocks::IsFileChanged() const
+{
+ Date aTempDateModified( m_aDateModified );
+ tools::Time aTempTimeModified( m_aTimeModified );
+ return FStatHelper::GetModifiedDateTimeOfFile( m_aFile, &aTempDateModified, &aTempTimeModified ) &&
+ ( m_aDateModified != aTempDateModified ||
+ m_aTimeModified != aTempTimeModified );
+}
+
+void SwImpBlocks::Touch()
+{
+ FStatHelper::GetModifiedDateTimeOfFile( m_aFile, &m_aDateModified, &m_aTimeModified );
+}
+
+bool SwImpBlocks::IsOnlyTextBlock( const OUString& ) const
+{
+ return false;
+}
+
+ErrCode SwImpBlocks::GetMacroTable( sal_uInt16, SvxMacroTableDtor& )
+{
+ return ERRCODE_NONE;
+}
+
+ErrCode SwImpBlocks::SetMacroTable( sal_uInt16 , const SvxMacroTableDtor& )
+{
+ return ERRCODE_NONE;
+}
+
+bool SwImpBlocks::PutMuchEntries( bool )
+{
+ return false;
+}
+
+SwTextBlocks::SwTextBlocks( const OUString& rFile )
+ : m_nErr( 0 )
+{
+ INetURLObject aObj(rFile);
+ const OUString sFileName = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ switch( SwImpBlocks::GetFileType( rFile ) )
+ {
+ case SwImpBlocks::FileType::XML: m_pImp.reset( new SwXMLTextBlocks( sFileName ) ); break;
+ case SwImpBlocks::FileType::NoFile: m_pImp.reset( new SwXMLTextBlocks( sFileName ) ); break;
+ default: break;
+ }
+ if( m_pImp )
+ return;
+
+ m_nErr = ERR_SWG_FILE_FORMAT_ERROR;
+}
+
+SwTextBlocks::~SwTextBlocks()
+{
+}
+
+OUString SwTextBlocks::GetName() const
+{
+ return m_pImp ? m_pImp->m_aName : OUString();
+}
+
+void SwTextBlocks::SetName( const OUString& r )
+{
+ if( m_pImp )
+ m_pImp->SetName( r );
+}
+
+sal_uInt16 SwTextBlocks::GetCount() const
+{
+ return m_pImp ? m_pImp->GetCount() : 0;
+}
+
+sal_uInt16 SwTextBlocks::GetIndex( const OUString& r ) const
+{
+ return m_pImp ? m_pImp->GetIndex( r ) : USHRT_MAX;
+}
+
+sal_uInt16 SwTextBlocks::GetLongIndex( const OUString& r ) const
+{
+ return m_pImp ? m_pImp->GetLongIndex( r ) : USHRT_MAX;
+}
+
+OUString SwTextBlocks::GetShortName( sal_uInt16 n ) const
+{
+ if( m_pImp )
+ return m_pImp->GetShortName( n );
+ return OUString();
+}
+
+OUString SwTextBlocks::GetLongName( sal_uInt16 n ) const
+{
+ if( m_pImp )
+ return m_pImp->GetLongName( n );
+ return OUString();
+}
+
+bool SwTextBlocks::Delete( sal_uInt16 n )
+{
+ if( m_pImp && !m_pImp->m_bInPutMuchBlocks )
+ {
+ if( m_pImp->IsFileChanged() )
+ m_nErr = ERR_TXTBLOCK_NEWFILE_ERROR;
+ else if( ERRCODE_NONE == (m_nErr = m_pImp->OpenFile( false ) ))
+ {
+ m_nErr = m_pImp->Delete( n );
+ if( !m_nErr )
+ {
+ m_pImp->m_aNames.erase( m_pImp->m_aNames.begin() + n );
+ }
+ if( n == m_pImp->m_nCurrentIndex )
+ m_pImp->m_nCurrentIndex = USHRT_MAX;
+ if( !m_nErr )
+ m_nErr = m_pImp->MakeBlockList();
+ }
+ m_pImp->CloseFile();
+ m_pImp->Touch();
+
+ return ( m_nErr == ERRCODE_NONE );
+ }
+ return false;
+}
+
+void SwTextBlocks::Rename( sal_uInt16 n, const OUString* s, const OUString* l )
+{
+ if( !m_pImp || m_pImp->m_bInPutMuchBlocks )
+ return;
+
+ m_pImp->m_nCurrentIndex = USHRT_MAX;
+ OUString aNew;
+ OUString aLong;
+ if( s )
+ aNew = aLong = *s;
+ if( l )
+ aLong = *l;
+ if( aNew.isEmpty() )
+ {
+ OSL_ENSURE( false, "No short name provided in the rename" );
+ m_nErr = ERR_SWG_INTERNAL_ERROR;
+ return;
+ }
+
+ if( m_pImp->IsFileChanged() )
+ m_nErr = ERR_TXTBLOCK_NEWFILE_ERROR;
+ else if( ERRCODE_NONE == ( m_nErr = m_pImp->OpenFile( false )))
+ {
+ // Set the new entry in the list before we do that!
+ aNew = GetAppCharClass().uppercase( aNew );
+ m_nErr = m_pImp->Rename( n, aNew );
+ if( !m_nErr )
+ {
+ bool bOnlyText = m_pImp->m_aNames[ n ]->m_bIsOnlyText;
+ m_pImp->m_aNames.erase( m_pImp->m_aNames.begin() + n );
+ m_pImp->AddName( aNew, aLong, bOnlyText );
+ m_nErr = m_pImp->MakeBlockList();
+ }
+ }
+ m_pImp->CloseFile();
+ m_pImp->Touch();
+}
+
+ErrCode const & SwTextBlocks::CopyBlock( SwTextBlocks const & rSource, OUString& rSrcShort,
+ const OUString& rLong )
+{
+ if (m_pImp->m_bInPutMuchBlocks)
+ m_nErr = ERR_SWG_INTERNAL_ERROR;
+ else
+ m_nErr = m_pImp->CopyBlock(*rSource.m_pImp, rSrcShort, rLong);
+ return m_nErr;
+}
+
+bool SwTextBlocks::BeginGetDoc( sal_uInt16 n )
+{
+ if( m_pImp && !m_pImp->m_bInPutMuchBlocks )
+ {
+ if( m_pImp->IsFileChanged() )
+ m_nErr = ERR_TXTBLOCK_NEWFILE_ERROR;
+ else if( ERRCODE_NONE == ( m_nErr = m_pImp->OpenFile()))
+ {
+ m_pImp->ClearDoc();
+ m_nErr = m_pImp->GetDoc( n );
+ if( m_nErr )
+ m_pImp->m_nCurrentIndex = USHRT_MAX;
+ else
+ m_pImp->m_nCurrentIndex = n;
+ }
+ return ( m_nErr == ERRCODE_NONE );
+ }
+ return false;
+}
+
+void SwTextBlocks::EndGetDoc()
+{
+ if( m_pImp && !m_pImp->m_bInPutMuchBlocks )
+ m_pImp->CloseFile();
+}
+
+bool SwTextBlocks::BeginPutDoc( const OUString& s, const OUString& l )
+{
+ if( m_pImp )
+ {
+ bool bOk = m_pImp->m_bInPutMuchBlocks;
+ if( !bOk )
+ {
+ if( m_pImp->IsFileChanged() )
+ m_nErr = ERR_TXTBLOCK_NEWFILE_ERROR;
+ else
+ m_nErr = m_pImp->OpenFile( false );
+ bOk = ERRCODE_NONE == m_nErr;
+ }
+ if( bOk )
+ {
+ const OUString aNew = GetAppCharClass().uppercase(s);
+ m_nErr = m_pImp->BeginPutDoc( aNew, l );
+ }
+ if( m_nErr )
+ m_pImp->CloseFile();
+ }
+ return ERRCODE_NONE == m_nErr;
+}
+
+sal_uInt16 SwTextBlocks::PutDoc()
+{
+ sal_uInt16 nIdx = USHRT_MAX;
+ if( m_pImp )
+ {
+ m_nErr = m_pImp->PutDoc();
+ if( !m_nErr )
+ {
+ m_pImp->m_nCurrentIndex = GetIndex( m_pImp->m_aShort );
+ if( m_pImp->m_nCurrentIndex != USHRT_MAX )
+ m_pImp->m_aNames[ m_pImp->m_nCurrentIndex ]->m_aLong = m_pImp->m_aLong;
+ else
+ {
+ m_pImp->AddName( m_pImp->m_aShort, m_pImp->m_aLong );
+ m_pImp->m_nCurrentIndex = m_pImp->GetIndex( m_pImp->m_aShort );
+ }
+ if( !m_pImp->m_bInPutMuchBlocks )
+ m_nErr = m_pImp->MakeBlockList();
+ }
+ if( !m_pImp->m_bInPutMuchBlocks )
+ {
+ m_pImp->CloseFile();
+ m_pImp->Touch();
+ }
+ nIdx = m_pImp->m_nCurrentIndex;
+ }
+ return nIdx;
+}
+
+sal_uInt16 SwTextBlocks::PutText( const OUString& rShort, const OUString& rName,
+ const OUString& rText )
+{
+ sal_uInt16 nIdx = USHRT_MAX;
+ if( m_pImp )
+ {
+ bool bOk = m_pImp->m_bInPutMuchBlocks;
+ if( !bOk )
+ {
+ if( m_pImp->IsFileChanged() )
+ m_nErr = ERR_TXTBLOCK_NEWFILE_ERROR;
+ else
+ m_nErr = m_pImp->OpenFile( false );
+ bOk = ERRCODE_NONE == m_nErr;
+ }
+ if( bOk )
+ {
+ OUString aNew = GetAppCharClass().uppercase( rShort );
+ m_nErr = m_pImp->PutText( aNew, rName, rText );
+ m_pImp->m_nCurrentIndex = USHRT_MAX;
+ if( !m_nErr )
+ {
+ nIdx = GetIndex( m_pImp->m_aShort );
+ if( nIdx != USHRT_MAX )
+ m_pImp->m_aNames[ nIdx ]->m_aLong = rName;
+ else
+ {
+ m_pImp->AddName( m_pImp->m_aShort, rName, true );
+ nIdx = m_pImp->GetIndex( m_pImp->m_aShort );
+ }
+ if( !m_pImp->m_bInPutMuchBlocks )
+ m_nErr = m_pImp->MakeBlockList();
+ }
+ }
+ if( !m_pImp->m_bInPutMuchBlocks )
+ {
+ m_pImp->CloseFile();
+ m_pImp->Touch();
+ }
+ }
+ return nIdx;
+}
+
+SwDoc* SwTextBlocks::GetDoc()
+{
+ if( m_pImp )
+ return m_pImp->m_xDoc.get();
+ return nullptr;
+}
+
+void SwTextBlocks::ClearDoc()
+{
+ if( m_pImp )
+ {
+ m_pImp->ClearDoc();
+ m_pImp->m_nCurrentIndex = USHRT_MAX;
+ }
+}
+
+OUString const & SwTextBlocks::GetFileName() const
+{
+ return m_pImp->GetFileName();
+}
+
+bool SwTextBlocks::IsReadOnly() const
+{
+ return m_pImp->m_bReadOnly;
+}
+
+bool SwTextBlocks::IsOnlyTextBlock( sal_uInt16 nIdx ) const
+{
+ bool bRet = false;
+ if( m_pImp && !m_pImp->m_bInPutMuchBlocks )
+ {
+ SwBlockName* pBlkNm = m_pImp->m_aNames[ nIdx ].get();
+ if( !pBlkNm->m_bIsOnlyTextFlagInit &&
+ !m_pImp->IsFileChanged() && !m_pImp->OpenFile() )
+ {
+ pBlkNm->m_bIsOnlyText = m_pImp->IsOnlyTextBlock( pBlkNm->m_aShort );
+ pBlkNm->m_bIsOnlyTextFlagInit = true;
+ m_pImp->CloseFile();
+ }
+ bRet = pBlkNm->m_bIsOnlyText;
+ }
+ return bRet;
+}
+
+bool SwTextBlocks::IsOnlyTextBlock( const OUString& rShort ) const
+{
+ sal_uInt16 nIdx = m_pImp->GetIndex( rShort );
+ if( USHRT_MAX != nIdx )
+ {
+ if( m_pImp->m_aNames[ nIdx ]->m_bIsOnlyTextFlagInit )
+ return m_pImp->m_aNames[ nIdx ]->m_bIsOnlyText;
+ return IsOnlyTextBlock( nIdx );
+ }
+
+ OSL_ENSURE( false, "Invalid name" );
+ return false;
+}
+
+bool SwTextBlocks::GetMacroTable( sal_uInt16 nIdx, SvxMacroTableDtor& rMacroTable )
+{
+ bool bRet = true;
+ if ( m_pImp && !m_pImp->m_bInPutMuchBlocks )
+ bRet = ( ERRCODE_NONE == m_pImp->GetMacroTable( nIdx, rMacroTable ) );
+ return bRet;
+}
+
+bool SwTextBlocks::SetMacroTable( sal_uInt16 nIdx, const SvxMacroTableDtor& rMacroTable )
+{
+ bool bRet = true;
+ if ( m_pImp && !m_pImp->m_bInPutMuchBlocks )
+ bRet = ( ERRCODE_NONE == m_pImp->SetMacroTable( nIdx, rMacroTable ) );
+ return bRet;
+}
+
+bool SwTextBlocks::StartPutMuchBlockEntries()
+{
+ bool bRet = false;
+ if( m_pImp )
+ bRet = m_pImp->PutMuchEntries( true );
+ return bRet;
+}
+
+void SwTextBlocks::EndPutMuchBlockEntries()
+{
+ if( m_pImp )
+ m_pImp->PutMuchEntries( false );
+}
+
+OUString SwTextBlocks::GetBaseURL() const
+{
+ if(m_pImp)
+ return m_pImp->GetBaseURL();
+ return OUString();
+}
+
+void SwTextBlocks::SetBaseURL( const OUString& rURL )
+{
+ if(m_pImp)
+ m_pImp->SetBaseURL(rURL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/table/swnewtable.cxx b/sw/source/core/table/swnewtable.cxx
new file mode 100644
index 000000000..36607971f
--- /dev/null
+++ b/sw/source/core/table/swnewtable.cxx
@@ -0,0 +1,2512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swtable.hxx>
+#include <tblsel.hxx>
+#include <tblrwcl.hxx>
+#include <ndtxt.hxx>
+#include <ndole.hxx>
+#include <node.hxx>
+#include <UndoTable.hxx>
+#include <pam.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <cellfrm.hxx>
+#include <fmtfsize.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <cstdlib>
+#include <vector>
+#include <set>
+#include <list>
+#include <memory>
+#include <editeng/boxitem.hxx>
+#include <editeng/protitem.hxx>
+#include <swtblfmt.hxx>
+#include <calbck.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+/** SwBoxSelection is a small helperclass (structure) to handle selections
+ of cells (boxes) between table functions
+
+ It contains an "array" of table boxes, a rectangulare selection of table boxes.
+ To be more specific, it contains a vector of box selections,
+ every box selection (SwSelBoxes) contains the selected boxes inside one row.
+ The member mnMergeWidth contains the width of the selected boxes
+*/
+
+class SwBoxSelection
+{
+public:
+ std::vector<SwSelBoxes> maBoxes;
+ tools::Long mnMergeWidth;
+ SwBoxSelection() : mnMergeWidth(0) {}
+ bool isEmpty() const { return maBoxes.empty(); }
+ void push_back(const SwSelBoxes& rNew) { maBoxes.push_back(rNew); }
+};
+
+/** NewMerge(..) removes the superfluous cells after cell merge
+
+SwTable::NewMerge(..) does some cleaning up,
+it simply deletes the superfluous cells ("cell span")
+and notifies the Undo about it.
+The main work has been done by SwTable::PrepareMerge(..) already.
+
+@param rBoxes
+the boxes to remove
+
+@param pUndo
+the undo object to notify, maybe empty
+
+@return true for compatibility reasons with OldMerge(..)
+*/
+
+bool SwTable::NewMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes,
+ const SwSelBoxes& rMerged, SwUndoTableMerge* pUndo )
+{
+ if( pUndo )
+ pUndo->SetSelBoxes( rBoxes );
+ DeleteSel( pDoc, rBoxes, &rMerged, nullptr, true, true );
+
+ CHECK_TABLE( *this )
+ return true;
+}
+
+/** lcl_CheckMinMax helps evaluating (horizontal) min/max of boxes
+
+lcl_CheckMinMax(..) compares the left border and the right border
+of a given cell with the given range and sets it accordingly.
+
+@param rMin
+will be decremented if necessary to the left border of the cell
+
+@param rMax
+will be incremented if necessary to the right border of the cell
+
+@param rLine
+the row (table line) of the interesting box
+
+@param nCheck
+the index of the box in the table box array of the given row
+
+@param bSet
+if bSet is false, rMin and rMax will be manipulated if necessary
+if bSet is true, rMin and rMax will be set to the left and right border of the box
+
+*/
+
+static void lcl_CheckMinMax( tools::Long& rMin, tools::Long& rMax, const SwTableLine& rLine, size_t nCheck, bool bSet )
+{
+ ++nCheck;
+ if( rLine.GetTabBoxes().size() < nCheck )
+ { // robust
+ OSL_FAIL( "Box out of table line" );
+ nCheck = rLine.GetTabBoxes().size();
+ }
+
+ tools::Long nNew = 0; // will be the right border of the current box
+ tools::Long nWidth = 0; // the width of the current box
+ for( size_t nCurrBox = 0; nCurrBox < nCheck; ++nCurrBox )
+ {
+ SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
+ OSL_ENSURE( pBox, "Missing table box" );
+ nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ nNew += nWidth;
+ }
+ // nNew is the right border of the wished box
+ if( bSet || nNew > rMax )
+ rMax = nNew;
+ nNew -= nWidth; // nNew becomes the left border of the wished box
+ if( bSet || nNew < rMin )
+ rMin = nNew;
+}
+
+/** lcl_Box2LeftBorder(..) delivers the left (logical) border of a table box
+
+The left logical border of a table box is the sum of the cell width before this
+box.
+
+@param rBox
+is the requested table box
+
+@return is the left logical border (long, even it cannot be negative)
+
+*/
+
+static tools::Long lcl_Box2LeftBorder( const SwTableBox& rBox )
+{
+ if( !rBox.GetUpper() )
+ return 0;
+ tools::Long nLeft = 0;
+ const SwTableLine &rLine = *rBox.GetUpper();
+ const size_t nCount = rLine.GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
+ {
+ SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
+ OSL_ENSURE( pBox, "Missing table box" );
+ if( pBox == &rBox )
+ return nLeft;
+ nLeft += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ OSL_FAIL( "Box not found in own upper?" );
+ return nLeft;
+}
+
+/** lcl_LeftBorder2Box delivers the box to a given left border
+
+It's used to find the master/follow table boxes in previous/next rows.
+Don't call this function to check if there is such a box,
+call it if you know there has to be such box.
+
+@param nLeft
+the left border (logical x-value) of the demanded box
+
+@param rLine
+the row (table line) to be scanned
+
+@return a pointer to the table box inside the given row with the wished left border
+
+*/
+
+static SwTableBox* lcl_LeftBorder2Box( tools::Long nLeft, const SwTableLine* pLine )
+{
+ if( !pLine )
+ return nullptr;
+ tools::Long nCurrLeft = 0;
+ const size_t nCount = pLine->GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
+ OSL_ENSURE( pBox, "Missing table box" );
+ if( pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
+ {
+ if( nCurrLeft == nLeft )
+ return pBox;
+ // HACK: It appears that rounding errors may result in positions not matching
+ // exactly, so allow a little tolerance. This happens at least with merged cells
+ // in the doc from fdo#38414 .
+ if( std::abs( nCurrLeft - nLeft ) <= ( nLeft / 1000 ))
+ return pBox;
+ if( nCurrLeft >= nLeft )
+ {
+ SAL_WARN( "sw.core", "Possibly wrong box found" );
+ return pBox;
+ }
+ }
+ nCurrLeft += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ }
+ OSL_FAIL( "Didn't find wished box" );
+ return nullptr;
+}
+
+/** lcl_ChangeRowSpan corrects row span after insertion/deletion of rows
+
+lcl_ChangeRowSpan(..) has to be called after an insertion or deletion of rows
+to adjust the row spans of previous rows accordingly.
+If rows are deleted, the previous rows with row spans into the deleted area
+have to be decremented by the number of _overlapped_ inserted rows.
+If rows are inserted, the previous rows with row span into the inserted area
+have to be incremented by the number of inserted rows.
+For those row spans which ends exactly above the inserted area it has to be
+decided by the parameter bSingle if they have to be expanded or not.
+
+@param rTable
+the table to manipulate (has to be a new model table)
+
+@param nDiff
+the number of rows which has been inserted (nDiff > 0) or deleted (nDiff < 0)
+
+@param nRowIdx
+the index of the first row which has to be checked
+
+@param bSingle
+true if the new inserted row should not extend row spans which ends in the row above
+this is for rows inserted by UI "insert row"
+false if all cells of an inserted row has to be overlapped by the previous row
+this is for rows inserted by "split row"
+false is also needed for deleted rows
+
+*/
+
+static void lcl_ChangeRowSpan( const SwTable& rTable, const tools::Long nDiff,
+ sal_uInt16 nRowIdx, const bool bSingle )
+{
+ if( !nDiff || nRowIdx >= rTable.GetTabLines().size() )
+ return;
+ OSL_ENSURE( !bSingle || nDiff > 0, "Don't set bSingle when deleting lines!" );
+ bool bGoOn;
+ // nDistance is the distance between the current row and the critical row,
+ // e.g. the deleted rows or the inserted rows.
+ // If the row span is lower than the distance there is nothing to do
+ // because the row span ends before the critical area.
+ // When the inserted rows should not be overlapped by row spans which ends
+ // exactly in the row above, the trick is to start with a distance of 1.
+ tools::Long nDistance = bSingle ? 1 : 0;
+ do
+ {
+ bGoOn = false; // will be set to true if we found a non-master cell
+ // which has to be manipulated => we have to check the previous row, too.
+ const SwTableLine* pLine = rTable.GetTabLines()[ nRowIdx ];
+ const size_t nBoxCount = pLine->GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
+ {
+ sal_Int32 nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
+ sal_Int32 nAbsSpan = nRowSpan > 0 ? nRowSpan : -nRowSpan;
+ // Check if the last overlapped cell is above or below
+ // the critical area
+ if( nAbsSpan > nDistance )
+ {
+ if( nDiff > 0 )
+ {
+ if( nRowSpan > 0 )
+ nRowSpan += nDiff; // increment row span of master cell
+ else
+ {
+ nRowSpan -= nDiff; // increment row span of non-master cell
+ bGoOn = true;
+ }
+ }
+ else
+ {
+ if( nRowSpan > 0 )
+ { // A master cell
+ // end of row span behind the deleted area ..
+ if( nRowSpan - nDistance > -nDiff )
+ nRowSpan += nDiff;
+ else // .. or inside the deleted area
+ nRowSpan = nDistance + 1;
+ }
+ else
+ { // Same for a non-master cell
+ if( nRowSpan + nDistance < nDiff )
+ nRowSpan -= nDiff;
+ else
+ nRowSpan = -nDistance - 1;
+ bGoOn = true; // We have to continue
+ }
+ }
+ pLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan );
+ }
+ }
+ ++nDistance;
+ if( nRowIdx )
+ --nRowIdx;
+ else
+ bGoOn = false; //robust
+ } while( bGoOn );
+}
+
+/** CollectBoxSelection(..) create a rectangulare selection based on the given SwPaM
+ and prepares the selected cells for merging
+*/
+
+std::unique_ptr<SwBoxSelection> SwTable::CollectBoxSelection( const SwPaM& rPam ) const
+{
+ OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
+ if( m_aLines.empty() )
+ return nullptr;
+ const SwNode* pStartNd = rPam.Start()->nNode.GetNode().FindTableBoxStartNode();
+ const SwNode* pEndNd = rPam.End()->nNode.GetNode().FindTableBoxStartNode();
+ if( !pStartNd || !pEndNd || pStartNd == pEndNd )
+ return nullptr;
+
+ const size_t nLines = m_aLines.size();
+ size_t nTop = 0;
+ size_t nBottom = 0;
+ tools::Long nMin = 0, nMax = 0;
+ int nFound = 0;
+ for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ OSL_ENSURE( pLine, "Missing table line" );
+ const size_t nCols = pLine->GetTabBoxes().size();
+ for( size_t nCol = 0; nCol < nCols; ++nCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
+ OSL_ENSURE( pBox, "Missing table box" );
+ if( nFound )
+ {
+ if( pBox->GetSttNd() == pEndNd )
+ {
+ nBottom = nRow;
+ lcl_CheckMinMax( nMin, nMax, *pLine, nCol, false );
+ ++nFound;
+ break;
+ }
+ }
+ else if( pBox->GetSttNd() == pStartNd )
+ {
+ nTop = nRow;
+ lcl_CheckMinMax( nMin, nMax, *pLine, nCol, true );
+ ++nFound;
+ }
+ }
+ }
+ if( nFound < 2 )
+ return nullptr;
+
+ bool bOkay = true;
+ tools::Long nMid = ( nMin + nMax ) / 2;
+
+ auto pRet(std::make_unique<SwBoxSelection>());
+ std::vector< std::pair< SwTableBox*, tools::Long > > aNewWidthVector;
+ size_t nCheckBottom = nBottom;
+ tools::Long nLeftSpan = 0;
+ tools::Long nRightSpan = 0;
+ tools::Long nLeftSpanCnt = 0;
+ tools::Long nRightSpanCnt = 0;
+ for( size_t nRow = nTop; nRow <= nBottom && bOkay && nRow < nLines; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ OSL_ENSURE( pLine, "Missing table line" );
+ SwSelBoxes aBoxes;
+ tools::Long nRight = 0;
+ const size_t nCount = pLine->GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
+ OSL_ENSURE( pBox, "Missing table box" );
+ tools::Long nLeft = nRight;
+ nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRight <= nMin )
+ {
+ if( nRight == nMin && nLeftSpanCnt )
+ bOkay = false;
+ continue;
+ }
+ SwTableBox* pInnerBox = nullptr;
+ SwTableBox* pLeftBox = nullptr;
+ SwTableBox* pRightBox = nullptr;
+ tools::Long nDiff = 0;
+ tools::Long nDiff2 = 0;
+ if( nLeft < nMin )
+ {
+ if( nRight >= nMid || nRight + nLeft >= nMin + nMin )
+ {
+ if( nCurrBox )
+ {
+ aBoxes.insert(pBox);
+ pInnerBox = pBox;
+ pLeftBox = pLine->GetTabBoxes()[nCurrBox-1];
+ nDiff = nMin - nLeft;
+ if( nRight > nMax )
+ {
+ if( nCurrBox+1 < nCount )
+ {
+ pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
+ nDiff2 = nRight - nMax;
+ }
+ else
+ bOkay = false;
+ }
+ else if( nRightSpanCnt && nRight == nMax )
+ bOkay = false;
+ }
+ else
+ bOkay = false;
+ }
+ else if( nCurrBox+1 < nCount )
+ {
+ pLeftBox = pBox;
+ pInnerBox = pLine->GetTabBoxes()[nCurrBox+1];
+ nDiff = nMin - nRight;
+ }
+ else
+ bOkay = false;
+ }
+ else if( nRight <= nMax )
+ {
+ aBoxes.insert(pBox);
+ if( nRow == nTop && nRowSpan < 0 )
+ {
+ bOkay = false;
+ break;
+ }
+ if( nRowSpan > 1 && nRow + nRowSpan - 1 > nBottom )
+ nBottom = nRow + nRowSpan - 1;
+ if( nRowSpan < -1 && nRow - nRowSpan - 1 > nBottom )
+ nBottom = nRow - nRowSpan - 1;
+ if( nRightSpanCnt && nRight == nMax )
+ bOkay = false;
+ }
+ else if( nLeft < nMax )
+ {
+ if( nLeft <= nMid || nRight + nLeft <= nMax )
+ {
+ if( nCurrBox+1 < nCount )
+ {
+ aBoxes.insert(pBox);
+ pInnerBox = pBox;
+ pRightBox = pLine->GetTabBoxes()[nCurrBox+1];
+ nDiff = nRight - nMax;
+ }
+ else
+ bOkay = false;
+ }
+ else if( nCurrBox )
+ {
+ pRightBox = pBox;
+ pInnerBox = pLine->GetTabBoxes()[nCurrBox-1];
+ nDiff = nLeft - nMax;
+ }
+ else
+ bOkay = false;
+ }
+ else
+ break;
+ if( pInnerBox )
+ {
+ if( nRow == nBottom )
+ {
+ tools::Long nTmpSpan = pInnerBox->getRowSpan();
+ if( nTmpSpan > 1 )
+ nBottom += nTmpSpan - 1;
+ else if( nTmpSpan < -1 )
+ nBottom -= nTmpSpan + 1;
+ }
+ SwTableBox* pOuterBox = pLeftBox;
+ do
+ {
+ if( pOuterBox )
+ {
+ tools::Long nOutSpan = pOuterBox->getRowSpan();
+ if( nOutSpan != 1 )
+ {
+ size_t nCheck = nRow;
+ if( nOutSpan < 0 )
+ {
+ const SwTableBox& rBox =
+ pOuterBox->FindStartOfRowSpan( *this );
+ nOutSpan = rBox.getRowSpan();
+ const SwTableLine* pTmpL = rBox.GetUpper();
+ nCheck = GetTabLines().GetPos( pTmpL );
+ if( nCheck < nTop )
+ bOkay = false;
+ if( pOuterBox == pLeftBox )
+ {
+ if( !nLeftSpanCnt || nMin - nDiff != nLeftSpan )
+ bOkay = false;
+ }
+ else
+ {
+ if( !nRightSpanCnt || nMax + nDiff != nRightSpan )
+ bOkay = false;
+ }
+ }
+ else
+ {
+ if( pOuterBox == pLeftBox )
+ {
+ if( nLeftSpanCnt )
+ bOkay = false;
+ nLeftSpan = nMin - nDiff;
+ nLeftSpanCnt = nOutSpan;
+ }
+ else
+ {
+ if( nRightSpanCnt )
+ bOkay = false;
+ nRightSpan = nMax + nDiff;
+ nRightSpanCnt = nOutSpan;
+ }
+ }
+ nCheck += nOutSpan - 1;
+ if( nCheck > nCheckBottom )
+ nCheckBottom = nCheck;
+ }
+ else if( ( nLeftSpanCnt && pLeftBox == pOuterBox ) ||
+ ( nRightSpanCnt && pRightBox == pOuterBox ) )
+ bOkay = false;
+ std::pair< SwTableBox*, long > aTmp;
+ aTmp.first = pInnerBox;
+ aTmp.second = -nDiff;
+ aNewWidthVector.push_back(aTmp);
+ aTmp.first = pOuterBox;
+ aTmp.second = nDiff;
+ aNewWidthVector.push_back(aTmp);
+ }
+ pOuterBox = pOuterBox == pRightBox ? nullptr : pRightBox;
+ if( nDiff2 )
+ nDiff = nDiff2;
+ } while( pOuterBox );
+ }
+ }
+ if( nLeftSpanCnt )
+ --nLeftSpanCnt;
+ if( nRightSpanCnt )
+ --nRightSpanCnt;
+ pRet->push_back(aBoxes);
+ }
+ if( nCheckBottom > nBottom )
+ bOkay = false;
+ if( bOkay )
+ {
+ pRet->mnMergeWidth = nMax - nMin;
+ for (auto const& newWidth : aNewWidthVector)
+ {
+ SwFrameFormat* pFormat = newWidth.first->ClaimFrameFormat();
+ tools::Long nNewWidth = pFormat->GetFrameSize().GetWidth() + newWidth.second;
+ pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, nNewWidth, 0 ) );
+ }
+ }
+ else
+ pRet.reset();
+
+ return pRet;
+}
+
+/** lcl_InvalidateCellFrame(..) invalidates all layout representations of a given cell
+ to initiate a reformatting
+*/
+
+static void lcl_InvalidateCellFrame( const SwTableBox& rBox )
+{
+ SwIterator<SwCellFrame,SwFormat> aIter( *rBox.GetFrameFormat() );
+ for( SwCellFrame* pCell = aIter.First(); pCell; pCell = aIter.Next() )
+ {
+ if( pCell->GetTabBox() == &rBox )
+ {
+ pCell->InvalidateSize();
+ SwFrame* pLower = pCell->GetLower();
+ if( pLower )
+ pLower->InvalidateSize_();
+ }
+ }
+}
+
+/** lcl_InsertPosition(..) evaluates the insert positions in every table line,
+ when a selection of cells is given and returns the average cell widths
+*/
+
+static tools::Long lcl_InsertPosition( SwTable &rTable, std::vector<sal_uInt16>& rInsPos,
+ const SwSelBoxes& rBoxes, bool bBehind )
+{
+ sal_Int32 nAddWidth = 0;
+ tools::Long nCount = 0;
+ for (size_t j = 0; j < rBoxes.size(); ++j)
+ {
+ SwTableBox *pBox = rBoxes[j];
+ SwTableLine* pLine = pBox->GetUpper();
+ tools::Long nWidth = rBoxes[j]->GetFrameFormat()->GetFrameSize().GetWidth();
+ nAddWidth += nWidth;
+ sal_uInt16 nCurrBox = pLine->GetBoxPos( pBox );
+ sal_uInt16 nCurrLine = rTable.GetTabLines().GetPos( pLine );
+ OSL_ENSURE( nCurrLine != USHRT_MAX, "Time to say Good-Bye.." );
+ if( rInsPos[ nCurrLine ] == USHRT_MAX )
+ {
+ rInsPos[ nCurrLine ] = nCurrBox;
+ ++nCount;
+ }
+ else if( ( rInsPos[ nCurrLine ] > nCurrBox ) == !bBehind )
+ rInsPos[ nCurrLine ] = nCurrBox;
+ }
+ if( nCount )
+ nAddWidth /= nCount;
+ return nAddWidth;
+}
+
+/** SwTable::NewInsertCol(..) insert new column(s) into a table
+
+@param pDoc
+the document
+
+@param rBoxes
+the selected boxes
+
+@param nCnt
+the number of columns to insert
+
+@param bBehind
+insertion behind (true) or before (false) the selected boxes
+
+@return true, if any insertion has been done successfully
+
+*/
+
+bool SwTable::NewInsertCol( SwDoc& rDoc, const SwSelBoxes& rBoxes,
+ sal_uInt16 nCnt, bool bBehind )
+{
+ if( m_aLines.empty() || !nCnt )
+ return false;
+
+ CHECK_TABLE( *this )
+ tools::Long nNewBoxWidth = 0;
+ std::vector< sal_uInt16 > aInsPos( m_aLines.size(), USHRT_MAX );
+ { // Calculation of the insert positions and the width of the new boxes
+ sal_uInt64 nTableWidth = 0;
+ for( size_t i = 0; i < m_aLines[0]->GetTabBoxes().size(); ++i )
+ nTableWidth += m_aLines[0]->GetTabBoxes()[i]->GetFrameFormat()->GetFrameSize().GetWidth();
+
+ // Fill the vector of insert positions and the (average) width to insert
+ sal_uInt64 nAddWidth = lcl_InsertPosition( *this, aInsPos, rBoxes, bBehind );
+
+ // Given is the (average) width of the selected boxes, if we would
+ // insert nCnt of columns the table would grow
+ // So we will shrink the table first, then insert the new boxes and
+ // get a table with the same width than before.
+ // But we will not shrink the table by the full already calculated value,
+ // we will reduce this value proportional to the old table width
+ nAddWidth *= nCnt; // we have to insert nCnt boxes per line
+ sal_uInt64 nResultingWidth = nAddWidth + nTableWidth;
+ if( !nResultingWidth )
+ return false;
+ nAddWidth = (nAddWidth * nTableWidth) / nResultingWidth;
+ nNewBoxWidth = tools::Long( nAddWidth / nCnt ); // Rounding
+ nAddWidth = nNewBoxWidth * nCnt; // Rounding
+ if( !nAddWidth || nAddWidth >= nTableWidth )
+ return false;
+ AdjustWidths( static_cast< tools::Long >(nTableWidth), static_cast< tools::Long >(nTableWidth - nAddWidth) );
+ }
+
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rBoxes, *this );
+ aFndBox.DelFrames( *this );
+
+ SwTableNode* pTableNd = GetTableNode();
+ std::vector<SwTableBoxFormat*> aInsFormat( nCnt, nullptr );
+ size_t nLastLine = SAL_MAX_SIZE;
+ sal_Int32 nLastRowSpan = 1;
+
+ for( size_t i = 0; i < m_aLines.size(); ++i )
+ {
+ SwTableLine* pLine = m_aLines[ i ];
+ sal_uInt16 nInsPos = aInsPos[i];
+ assert(nInsPos != USHRT_MAX); // didn't find insert position
+ SwTableBox* pBox = pLine->GetTabBoxes()[ nInsPos ];
+ if( bBehind )
+ ++nInsPos;
+ SwTableBoxFormat* pBoxFrameFormat = static_cast<SwTableBoxFormat*>(pBox->GetFrameFormat());
+ ::InsTableBox( rDoc, pTableNd, pLine, pBoxFrameFormat, pBox, nInsPos, nCnt );
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ tools::Long nDiff = i - nLastLine;
+ bool bNewSpan = false;
+ if( nLastLine != SAL_MAX_SIZE && nDiff <= nLastRowSpan &&
+ nRowSpan != nDiff - nLastRowSpan )
+ {
+ bNewSpan = true;
+ while( nLastLine < i )
+ {
+ SwTableLine* pTmpLine = m_aLines[ nLastLine ];
+ sal_uInt16 nTmpPos = aInsPos[nLastLine];
+ if( bBehind )
+ ++nTmpPos;
+ for( sal_uInt16 j = 0; j < nCnt; ++j )
+ pTmpLine->GetTabBoxes()[nTmpPos+j]->setRowSpan( nDiff );
+ if( nDiff > 0 )
+ nDiff = -nDiff;
+ ++nDiff;
+ ++nLastLine;
+ }
+ }
+ if( nRowSpan > 0 )
+ bNewSpan = true;
+ if( bNewSpan )
+ {
+ nLastLine = i;
+ if( nRowSpan < 0 )
+ nLastRowSpan = -nRowSpan;
+ else
+ nLastRowSpan = nRowSpan;
+ }
+ const SvxBoxItem& aSelBoxItem = pBoxFrameFormat->GetBox();
+ std::unique_ptr<SvxBoxItem> pNoRightBorder;
+ if( aSelBoxItem.GetRight() )
+ {
+ pNoRightBorder.reset( new SvxBoxItem( aSelBoxItem ));
+ pNoRightBorder->SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ for( sal_uInt16 j = 0; j < nCnt; ++j )
+ {
+ SwTableBox *pCurrBox = pLine->GetTabBoxes()[nInsPos+j];
+ if( bNewSpan )
+ {
+ pCurrBox->setRowSpan( nLastRowSpan );
+ SwFrameFormat* pFrameFormat = pCurrBox->ClaimFrameFormat();
+ SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
+ aFrameSz.SetWidth( nNewBoxWidth );
+ pFrameFormat->SetFormatAttr( aFrameSz );
+ if( pNoRightBorder && ( !bBehind || j+1 < nCnt ) )
+ pFrameFormat->SetFormatAttr( *pNoRightBorder );
+ aInsFormat[j] = static_cast<SwTableBoxFormat*>(pFrameFormat);
+ }
+ else
+ pCurrBox->ChgFrameFormat( aInsFormat[j] );
+ }
+ if( bBehind && pNoRightBorder )
+ {
+ SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
+ pFrameFormat->SetFormatAttr( *pNoRightBorder );
+ }
+ }
+
+ aFndBox.MakeFrames( *this );
+#if OSL_DEBUG_LEVEL > 0
+ {
+ const SwTableBoxes &rTabBoxes = m_aLines[0]->GetTabBoxes();
+ tools::Long nNewWidth = 0;
+ for( size_t i = 0; i < rTabBoxes.size(); ++i )
+ nNewWidth += rTabBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
+ OSL_ENSURE( nNewWidth > 0, "Very small" );
+ }
+#endif
+ CHECK_TABLE( *this )
+
+ return true;
+}
+
+/** SwTable::PrepareMerge(..) some preparation for the coming Merge(..)
+
+For the old table model, ::GetMergeSel(..) is called only,
+for the new table model, PrepareMerge does the main work.
+It modifies all cells to merge (width, border, rowspan etc.) and collects
+the cells which have to be deleted by Merge(..) afterwards.
+If there are superfluous rows, these cells are put into the deletion list as well.
+
+@param rPam
+the selection to merge
+
+@param rBoxes
+should be empty at the beginning, at the end it is filled with boxes to delete.
+
+@param ppMergeBox
+will be set to the master cell box
+
+@param pUndo
+the undo object to record all changes
+can be Null, e.g. when called by Redo(..)
+
+@return
+
+*/
+
+bool SwTable::PrepareMerge( const SwPaM& rPam, SwSelBoxes& rBoxes,
+ SwSelBoxes& rMerged, SwTableBox** ppMergeBox, SwUndoTableMerge* pUndo )
+{
+ if( !m_bNewModel )
+ {
+ ::GetMergeSel( rPam, rBoxes, ppMergeBox, pUndo );
+ return rBoxes.size() > 1;
+ }
+ CHECK_TABLE( *this )
+ // We have to assert a "rectangular" box selection before we start to merge
+ std::unique_ptr< SwBoxSelection > pSel( CollectBoxSelection( rPam ) );
+ if (!pSel || pSel->isEmpty())
+ return false;
+ // Now we should have a rectangle of boxes,
+ // i.e. contiguous cells in contiguous rows
+ bool bMerge = false; // will be set if any content is transferred from
+ // a "not already overlapped" cell into the new master cell.
+ const SwSelBoxes& rFirstBoxes = pSel->maBoxes[0];
+ if (rFirstBoxes.empty())
+ return false;
+ SwTableBox *pMergeBox = rFirstBoxes[0]; // the master cell box
+ if( !pMergeBox )
+ return false;
+ (*ppMergeBox) = pMergeBox;
+ // The new master box will get the left and the top border of the top-left
+ // box of the selection and because the new master cell _is_ the top-left
+ // box, the left and right border does not need to be changed.
+ // The right and bottom border instead has to be derived from the right-
+ // bottom box of the selection. If this is an overlapped cell,
+ // the appropriate master box.
+ SwTableBox* pLastBox = nullptr; // the right-bottom (master) cell
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ SwPosition aInsPos( *pMergeBox->GetSttNd()->EndOfSectionNode() );
+ SwPaM aChkPam( aInsPos );
+ // The number of lines in the selection rectangle: nLineCount
+ const size_t nLineCount = pSel->maBoxes.size();
+ // BTW: nLineCount is the rowspan of the new master cell
+ sal_Int32 nRowSpan = static_cast<tools::Long>(nLineCount);
+ // We will need the first and last line of the selection
+ // to check if there any superfluous row after merging
+ SwTableLine* pFirstLn = nullptr;
+ SwTableLine* pLastLn = nullptr;
+ // Iteration over the lines of the selection...
+ for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
+ {
+ // The selected boxes in the current line
+ const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
+ size_t nColCount = rLineBoxes.size();
+ // Iteration over the selected cell in the current row
+ for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
+ {
+ SwTableBox* pBox = rLineBoxes[nCurrCol];
+ rMerged.insert( pBox );
+ // Only the first selected cell in every row will be alive,
+ // the other will be deleted => put into rBoxes
+ if( nCurrCol )
+ rBoxes.insert( pBox );
+ else
+ {
+ if( nCurrLine == 1 )
+ pFirstLn = pBox->GetUpper(); // we need this line later on
+ if( nCurrLine + 1 == nLineCount )
+ pLastLn = pBox->GetUpper(); // and this one, too.
+ }
+ // A box has to be merged if it's not the master box itself,
+ // but an already overlapped cell must not be merged as well.
+ bool bDoMerge = pBox != pMergeBox && pBox->getRowSpan() > 0;
+ // The last box has to be in the last "column" of the selection
+ // and it has to be a master cell
+ if( nCurrCol+1 == nColCount && pBox->getRowSpan() > 0 )
+ pLastBox = pBox;
+ if( bDoMerge )
+ {
+ bMerge = true;
+ // If the cell to merge contains only one empty paragraph,
+ // we do not transfer this paragraph.
+ if( !IsEmptyBox( *pBox, aChkPam ) )
+ {
+ SwNodeIndex& rInsPosNd = aInsPos.nNode;
+ SwPaM aPam( aInsPos );
+ aPam.GetPoint()->nNode.Assign( *pBox->GetSttNd()->EndOfSectionNode(), -1 );
+ SwContentNode* pCNd = aPam.GetContentNode();
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 );
+ SwNodeIndex aSttNdIdx( *pBox->GetSttNd(), 1 );
+ bool const bUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
+ if( pUndo )
+ {
+ pDoc->GetIDocumentUndoRedo().DoUndo(false);
+ }
+ pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() );
+ if( pUndo )
+ {
+ pDoc->GetIDocumentUndoRedo().DoUndo(bUndo);
+ }
+ SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode );
+ if( pUndo )
+ pUndo->MoveBoxContent( *pDoc, aRg, rInsPosNd );
+ else
+ {
+ pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, rInsPosNd,
+ SwMoveFlags::NO_DELFRMS );
+ }
+ }
+ }
+ // Only the cell of the first selected column will stay alive
+ // and got a new row span
+ if( !nCurrCol )
+ pBox->setRowSpan( nRowSpan );
+ }
+ if( nRowSpan > 0 ) // the master cell is done, from now on we set
+ nRowSpan = -nRowSpan; // negative row spans
+ ++nRowSpan; // ... -3, -2, -1
+ }
+ if( bMerge )
+ {
+ // A row containing overlapped cells is superfluous,
+ // these cells can be put into rBoxes for deletion
+ FindSuperfluousRows_( rBoxes, pFirstLn, pLastLn );
+ // pNewFormat will be set to the new master box and the overlapped cells
+ SwFrameFormat* pNewFormat = pMergeBox->ClaimFrameFormat();
+ pNewFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, pSel->mnMergeWidth, 0 ) );
+ for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
+ {
+ const SwSelBoxes& rLineBoxes = pSel->maBoxes[nCurrLine];
+ size_t nColCount = rLineBoxes.size();
+ for (size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol)
+ {
+ SwTableBox* pBox = rLineBoxes[nCurrCol];
+ if( nCurrCol )
+ {
+ // Even this box will be deleted soon,
+ // we have to correct the width to avoid side effects
+ SwFrameFormat* pFormat = pBox->ClaimFrameFormat();
+ pFormat->SetFormatAttr( SwFormatFrameSize( SwFrameSize::Variable, 0, 0 ) );
+ }
+ else
+ {
+ pBox->ChgFrameFormat( static_cast<SwTableBoxFormat*>(pNewFormat) );
+ // remove numbering from cells that will be disabled in the merge
+ if( nCurrLine )
+ {
+ SwPaM aPam( *pBox->GetSttNd(), 0 );
+ aPam.GetPoint()->nNode++;
+ SwTextNode* pNd = aPam.GetNode().GetTextNode();
+ while( pNd )
+ {
+ pNd->SetCountedInList( false );
+
+ aPam.GetPoint()->nNode++;
+ pNd = aPam.GetNode().GetTextNode();
+ }
+ }
+ }
+ }
+ }
+ if( pLastBox ) // Robust
+ {
+ // The new borders of the master cell...
+ SvxBoxItem aBox( pMergeBox->GetFrameFormat()->GetBox() );
+ bool bOld = aBox.GetRight() || aBox.GetBottom();
+ const SvxBoxItem& rBox = pLastBox->GetFrameFormat()->GetBox();
+ aBox.SetLine( rBox.GetRight(), SvxBoxItemLine::RIGHT );
+ aBox.SetLine( rBox.GetBottom(), SvxBoxItemLine::BOTTOM );
+ if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() )
+ (*ppMergeBox)->GetFrameFormat()->SetFormatAttr( aBox );
+ }
+
+ if( pUndo )
+ pUndo->AddNewBox( pMergeBox->GetSttIdx() );
+ }
+ return bMerge;
+}
+
+/** SwTable::FindSuperfluousRows_(..) is looking for superfluous rows, i.e. rows
+ containing overlapped cells only.
+*/
+
+void SwTable::FindSuperfluousRows_( SwSelBoxes& rBoxes,
+ SwTableLine* pFirstLn, SwTableLine* pLastLn )
+{
+ if( !pFirstLn || !pLastLn )
+ {
+ if( rBoxes.empty() )
+ return;
+ pFirstLn = rBoxes[0]->GetUpper();
+ pLastLn = rBoxes.back()->GetUpper();
+ }
+ sal_uInt16 nFirstLn = GetTabLines().GetPos( pFirstLn );
+ sal_uInt16 nLastLn = GetTabLines().GetPos( pLastLn );
+ for( sal_uInt16 nRow = nFirstLn; nRow <= nLastLn; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ OSL_ENSURE( pLine, "Missing table line" );
+ const size_t nCols = pLine->GetTabBoxes().size();
+ bool bSuperfl = true;
+ for( size_t nCol = 0; nCol < nCols; ++nCol )
+ {
+ SwTableBox *pBox = pLine->GetTabBoxes()[nCol];
+ if( pBox->getRowSpan() > 0 &&
+ rBoxes.end() == rBoxes.find( pBox ) )
+ {
+ bSuperfl = false;
+ break;
+ }
+ }
+ if( bSuperfl )
+ {
+ for( size_t nCol = 0; nCol < nCols; ++nCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
+ rBoxes.insert( pBox );
+ }
+ }
+ }
+}
+
+/** SwTableBox::FindStartOfRowSpan(..) returns the "master" cell, the cell which
+ overlaps the given cell, it maybe the cell itself.
+*/
+
+SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
+{
+ if( getRowSpan() > 0 || !nMaxStep )
+ return *this;
+
+ tools::Long nLeftBorder = lcl_Box2LeftBorder( *this );
+ SwTableBox* pBox = this;
+ const SwTableLine* pMyUpper = GetUpper();
+ sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
+ if( nLine && nLine < rTable.GetTabLines().size() )
+ {
+ SwTableBox* pNext;
+ do
+ {
+ pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] );
+ if( pNext )
+ pBox = pNext;
+ } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 );
+ }
+
+ return *pBox;
+}
+
+/** SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is
+ any. Otherwise the cell itself will returned.
+*/
+
+SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep )
+{
+ tools::Long nAbsSpan = getRowSpan();
+ if( nAbsSpan < 0 )
+ nAbsSpan = -nAbsSpan;
+ if( nAbsSpan == 1 || !nMaxStep )
+ return *this;
+
+ if( nMaxStep > --nAbsSpan )
+ nMaxStep = o3tl::narrowing<sal_uInt16>(nAbsSpan);
+ const SwTableLine* pMyUpper = GetUpper();
+ sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
+ nMaxStep = nLine + nMaxStep;
+ if( nMaxStep >= rTable.GetTabLines().size() )
+ nMaxStep = rTable.GetTabLines().size() - 1;
+ tools::Long nLeftBorder = lcl_Box2LeftBorder( *this );
+ SwTableBox* pBox =
+ lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] );
+ if ( !pBox )
+ pBox = this;
+
+ return *pBox;
+}
+
+/** lcl_getAllMergedBoxes(..) collects all overlapped boxes to a given (master) box
+*/
+
+static void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox )
+{
+ SwTableBox* pBox = &rBox;
+ OSL_ENSURE( pBox == &rBox.FindStartOfRowSpan( rTable ), "Not a master box" );
+ rBoxes.insert( pBox );
+ if( pBox->getRowSpan() == 1 )
+ return;
+ const SwTableLine* pMyUpper = pBox->GetUpper();
+ sal_uInt16 nLine = rTable.GetTabLines().GetPos( pMyUpper );
+ tools::Long nLeftBorder = lcl_Box2LeftBorder( *pBox );
+ sal_uInt16 nCount = rTable.GetTabLines().size();
+ while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 )
+ {
+ pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] );
+ if( pBox )
+ rBoxes.insert( pBox );
+ }
+}
+
+/** lcl_UnMerge(..) manipulates the row span attribute of a given master cell
+ and its overlapped cells to split them into several pieces.
+*/
+
+static void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, size_t nCnt,
+ bool bSameHeight )
+{
+ SwSelBoxes aBoxes;
+ lcl_getAllMergedBoxes( rTable, aBoxes, rBox );
+ size_t const nCount = aBoxes.size();
+ if( nCount < 2 )
+ return;
+ if( nCnt > nCount )
+ nCnt = nCount;
+ std::unique_ptr<size_t[]> const pSplitIdx(new size_t[nCnt]);
+ if( bSameHeight )
+ {
+ std::unique_ptr<SwTwips[]> const pHeights(new SwTwips[nCount]);
+ SwTwips nHeight = 0;
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SwTableLine* pLine = aBoxes[ i ]->GetUpper();
+ SwFrameFormat *pRowFormat = pLine->GetFrameFormat();
+ pHeights[ i ] = pRowFormat->GetFrameSize().GetHeight();
+ nHeight += pHeights[ i ];
+ }
+ SwTwips nSumH = 0;
+ size_t nIdx = 0;
+ for (size_t i = 1; i <= nCnt; ++i)
+ {
+ SwTwips nSplit = ( i * nHeight ) / nCnt;
+ while( nSumH < nSplit && nIdx < nCount )
+ nSumH += pHeights[ nIdx++ ];
+ pSplitIdx[ i - 1 ] = nIdx;
+ }
+ }
+ else
+ {
+ for (size_t i = 1; i <= nCnt; ++i)
+ {
+ pSplitIdx[ i - 1 ] = ( i * nCount ) / nCnt;
+ }
+ }
+ size_t nIdx = 0;
+ for (size_t i = 0; i < nCnt; ++i)
+ {
+ size_t nNextIdx = pSplitIdx[ i ];
+ aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx );
+ lcl_InvalidateCellFrame( *aBoxes[ nIdx ] );
+ while( ++nIdx < nNextIdx )
+ aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx );
+ }
+}
+
+/** lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure
+*/
+
+static void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine )
+{
+ const size_t nBoxCount = rLine.GetTabBoxes().size();
+ for( size_t i = 0; i < nBoxCount; ++i )
+ rBoxes.insert( rLine.GetTabBoxes()[i] );
+}
+
+/** SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e. rows containing
+ overlapped cells only. This is a preparation for an upcoming split.
+*/
+
+void SwTable::InsertSpannedRow( SwDoc& rDoc, sal_uInt16 nRowIdx, sal_uInt16 nCnt )
+{
+ CHECK_TABLE( *this )
+ OSL_ENSURE( nCnt && nRowIdx < GetTabLines().size(), "Wrong call of InsertSpannedRow" );
+ SwSelBoxes aBoxes;
+ SwTableLine& rLine = *GetTabLines()[ nRowIdx ];
+ lcl_FillSelBoxes( aBoxes, rLine );
+ SwFormatFrameSize aFSz( rLine.GetFrameFormat()->GetFrameSize() );
+ if( SwFrameSize::Variable != aFSz.GetHeightSizeType() )
+ {
+ SwFrameFormat* pFrameFormat = rLine.ClaimFrameFormat();
+ tools::Long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 );
+ if( !nNewHeight )
+ ++nNewHeight;
+ aFSz.SetHeight( nNewHeight );
+ pFrameFormat->SetFormatAttr( aFSz );
+ }
+ InsertRow_( &rDoc, aBoxes, nCnt, true );
+ const size_t nBoxCount = rLine.GetTabBoxes().size();
+ for( sal_uInt16 n = 0; n < nCnt; ++n )
+ {
+ SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ];
+ for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
+ {
+ sal_Int32 nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan();
+ if( nRowSpan > 0 )
+ nRowSpan = - nRowSpan;
+ pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
+ }
+ }
+ lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false );
+ CHECK_TABLE( *this )
+}
+
+typedef std::pair< sal_uInt16, sal_uInt16 > SwLineOffset;
+typedef std::vector< SwLineOffset > SwLineOffsetArray;
+
+/*
+* When a couple of table boxes has to be split,
+* lcl_SophisticatedFillLineIndices delivers the information where and how many
+* rows have to be inserted.
+* Input
+* rTable: the table to manipulate
+* rBoxes: an array of boxes to split
+* nCnt: how many parts are wanted
+* Output
+* rArr: a list of pairs ( line index, number of lines to insert )
+*/
+static void lcl_SophisticatedFillLineIndices( SwLineOffsetArray &rArr,
+ const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
+{
+ std::list< SwLineOffset > aBoxes;
+ SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ { // Collect all end line indices and the row spans
+ const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
+ OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
+ if( nCnt > rBox.getRowSpan() )
+ {
+ const SwTableLine *pLine = rBox.GetUpper();
+ const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() +
+ rTable.GetTabLines().GetPos( pLine ) );
+ // The next if statement is a small optimization
+ if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() )
+ {
+ aLnOfs.first = nEnd; // ok, this is the line behind the box
+ aLnOfs.second = sal_uInt16( rBox.getRowSpan() ); // the row span
+ aBoxes.insert( aBoxes.end(), aLnOfs );
+ }
+ }
+ }
+ // As I said, I noted the line index _behind_ the last line of the boxes
+ // in the resulting array the index has to be _on_ the line
+ // nSum is to evaluate the wished value
+ sal_uInt16 nSum = 1;
+ while( !aBoxes.empty() )
+ {
+ // I. step:
+ // Looking for the "smallest" line end with the smallest row span
+ std::list< SwLineOffset >::iterator pCurr = aBoxes.begin();
+ aLnOfs = *pCurr; // the line end and row span of the first box
+ while( ++pCurr != aBoxes.end() )
+ {
+ if( aLnOfs.first > pCurr->first )
+ { // Found a smaller line end
+ aLnOfs.first = pCurr->first;
+ aLnOfs.second = pCurr->second; // row span
+ }
+ else if( aLnOfs.first == pCurr->first &&
+ aLnOfs.second < pCurr->second )
+ aLnOfs.second = pCurr->second; // Found a smaller row span
+ }
+ OSL_ENSURE( aLnOfs.second < nCnt, "Clean-up failed" );
+ aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert
+ rArr.emplace_back( aLnOfs.first - nSum, aLnOfs.second );
+ // the correction has to be incremented because in the following
+ // loops the line ends were manipulated
+ nSum = nSum + aLnOfs.second;
+
+ pCurr = aBoxes.begin();
+ while( pCurr != aBoxes.end() )
+ {
+ if( pCurr->first == aLnOfs.first )
+ { // These boxes can be removed because the last insertion
+ // of rows will expand their row span above the needed value
+ pCurr = aBoxes.erase(pCurr);
+ }
+ else
+ {
+ bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first );
+ // Manipulation of the end line indices as if the rows are
+ // already inserted
+ pCurr->first = pCurr->first + aLnOfs.second;
+ if( bBefore )
+ { // If the insertion is inside the box,
+ // its row span has to be incremented
+ pCurr->second = pCurr->second + aLnOfs.second;
+ if( pCurr->second >= nCnt )
+ { // if the row span is bigger than the split factor
+ // this box is done
+ pCurr = aBoxes.erase(pCurr);
+ }
+ else
+ ++pCurr;
+ }
+ else
+ ++pCurr;
+ }
+ }
+ }
+}
+
+typedef std::set< SwTwips > SwSplitLines;
+
+/** lcl_CalculateSplitLineHeights(..) delivers all y-positions where table rows have
+ to be split to fulfill the requested "split same height"
+*/
+
+static sal_uInt16 lcl_CalculateSplitLineHeights( SwSplitLines &rCurr, SwSplitLines &rNew,
+ const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt )
+{
+ if( nCnt < 2 )
+ return 0;
+ std::vector< SwLineOffset > aBoxes;
+ SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX );
+ sal_uInt16 nFirst = USHRT_MAX; // becomes the index of the first line
+ sal_uInt16 nLast = 0; // becomes the index of the last line of the splitting
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ { // Collect all pairs (start+end) of line indices to split
+ const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable );
+ OSL_ENSURE( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" );
+ const SwTableLine *pLine = rBox.GetUpper();
+ const sal_uInt16 nStart = rTable.GetTabLines().GetPos( pLine );
+ const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + nStart - 1 );
+ // The next if statement is a small optimization
+ if( aLnOfs.first != nStart || aLnOfs.second != nEnd )
+ {
+ aLnOfs.first = nStart;
+ aLnOfs.second = nEnd;
+ aBoxes.push_back( aLnOfs );
+ if( nStart < nFirst )
+ nFirst = nStart;
+ if( nEnd > nLast )
+ nLast = nEnd;
+ }
+ }
+
+ if (nFirst == USHRT_MAX)
+ {
+ assert(aBoxes.empty());
+ return 0;
+ }
+
+ SwTwips nHeight = 0;
+ std::unique_ptr<SwTwips[]> pLines(new SwTwips[ nLast + 1 - nFirst ]);
+ for( sal_uInt16 i = nFirst; i <= nLast; ++i )
+ {
+ bool bLayoutAvailable = false;
+ nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable );
+ rCurr.insert( rCurr.end(), nHeight );
+ pLines[ i - nFirst ] = nHeight;
+ }
+ for( const auto& rSplit : aBoxes )
+ {
+ SwTwips nBase = rSplit.first <= nFirst ? 0 :
+ pLines[ rSplit.first - nFirst - 1 ];
+ SwTwips nDiff = pLines[ rSplit.second - nFirst ] - nBase;
+ for( sal_uInt16 i = 1; i < nCnt; ++i )
+ {
+ SwTwips nSplit = nBase + ( i * nDiff ) / nCnt;
+ rNew.insert( nSplit );
+ }
+ }
+ return nFirst;
+}
+
+/** lcl_LineIndex(..) delivers the line index of the line behind or above
+ the box selection.
+*/
+
+static sal_uInt16 lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes,
+ bool bBehind )
+{
+ sal_uInt16 nDirect = USHRT_MAX;
+ sal_uInt16 nSpan = USHRT_MAX;
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ SwTableBox *pBox = rBoxes[i];
+ const SwTableLine* pLine = rBoxes[i]->GetUpper();
+ sal_uInt16 nPos = rTable.GetTabLines().GetPos( pLine );
+ if( USHRT_MAX != nPos )
+ {
+ if( bBehind )
+ {
+ if( nPos > nDirect || nDirect == USHRT_MAX )
+ nDirect = nPos;
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan < 2 )
+ nSpan = 0;
+ else if( nSpan )
+ {
+ sal_uInt16 nEndOfRowSpan = o3tl::narrowing<sal_uInt16>(nPos + nRowSpan - 1);
+ if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX )
+ nSpan = nEndOfRowSpan;
+ }
+ }
+ else if( nPos < nDirect )
+ nDirect = nPos;
+ }
+ }
+ if( nSpan && nSpan < USHRT_MAX )
+ return nSpan;
+ return nDirect;
+}
+
+/** SwTable::NewSplitRow(..) splits all selected boxes horizontally.
+*/
+
+bool SwTable::NewSplitRow( SwDoc& rDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt,
+ bool bSameHeight )
+{
+ CHECK_TABLE( *this )
+ ++nCnt;
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rBoxes, *this );
+
+ if( bSameHeight && rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ SwSplitLines aRowLines;
+ SwSplitLines aSplitLines;
+ sal_uInt16 nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines,
+ *this, rBoxes, nCnt );
+ aFndBox.DelFrames( *this );
+ SwTwips nLast = 0;
+ SwSplitLines::iterator pSplit = aSplitLines.begin();
+ for( const auto& rCurr : aRowLines )
+ {
+ while( pSplit != aSplitLines.end() && *pSplit < rCurr )
+ {
+ InsertSpannedRow( rDoc, nFirst, 1 );
+ SwTableLine* pRow = GetTabLines()[ nFirst ];
+ SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
+ SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
+ aFSz.SetHeightSizeType( SwFrameSize::Minimum );
+ aFSz.SetHeight( *pSplit - nLast );
+ pRowFormat->SetFormatAttr( aFSz );
+ nLast = *pSplit;
+ ++pSplit;
+ ++nFirst;
+ }
+ if( pSplit != aSplitLines.end() && rCurr == *pSplit )
+ ++pSplit;
+ SwTableLine* pRow = GetTabLines()[ nFirst ];
+ SwFrameFormat* pRowFormat = pRow->ClaimFrameFormat();
+ SwFormatFrameSize aFSz( pRowFormat->GetFrameSize() );
+ aFSz.SetHeightSizeType( SwFrameSize::Minimum );
+ aFSz.SetHeight( rCurr - nLast );
+ pRowFormat->SetFormatAttr( aFSz );
+ nLast = rCurr;
+ ++nFirst;
+ }
+ }
+ else
+ {
+ aFndBox.DelFrames( *this );
+ bSameHeight = false;
+ }
+ if( !bSameHeight )
+ {
+ SwLineOffsetArray aLineOffs;
+ lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt );
+ SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() );
+ while( pCurr != aLineOffs.rend() )
+ {
+ InsertSpannedRow( rDoc, pCurr->first, pCurr->second );
+ ++pCurr;
+ }
+ }
+
+ std::set<size_t> aIndices;
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ OSL_ENSURE( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" );
+ if( rBoxes[i]->getRowSpan() > 1 )
+ aIndices.insert( i );
+ }
+
+ for( const auto& rCurrBox : aIndices )
+ lcl_UnMerge( *this, *rBoxes[rCurrBox], nCnt, bSameHeight );
+
+ CHECK_TABLE( *this )
+ // update the layout
+ aFndBox.MakeFrames( *this );
+
+ return true;
+}
+
+/** SwTable::InsertRow(..) inserts one or more rows before or behind the selected
+ boxes.
+*/
+
+bool SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes,
+ sal_uInt16 nCnt, bool bBehind )
+{
+ bool bRet = false;
+ if( IsNewModel() )
+ {
+ CHECK_TABLE( *this )
+ sal_uInt16 nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind );
+ if( nRowIdx < USHRT_MAX )
+ {
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( rBoxes, *this );
+ aFndBox.DelFrames( *this );
+
+ bRet = true;
+ SwTableLine *pLine = GetTabLines()[ nRowIdx ];
+ SwSelBoxes aLineBoxes;
+ lcl_FillSelBoxes( aLineBoxes, *pLine );
+ InsertRow_( pDoc, aLineBoxes, nCnt, bBehind );
+ const size_t nBoxCount = pLine->GetTabBoxes().size();
+ sal_uInt16 nOfs = bBehind ? 0 : 1;
+ for( sal_uInt16 n = 0; n < nCnt; ++n )
+ {
+ SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs];
+ for( size_t nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox )
+ {
+ sal_Int32 nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan();
+ if( bBehind )
+ {
+ if( nRowSpan == 1 || nRowSpan == -1 )
+ nRowSpan = n + 1;
+ else if( nRowSpan > 1 )
+ {
+ nRowSpan = - nRowSpan;
+
+ // tdf#123102 disable numbering of the new hidden
+ // paragraph in merged cells to avoid of bad
+ // renumbering of next list elements
+ SwTableBox* pBox = pNewLine->GetTabBoxes()[nCurrBox];
+ SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( pCNd && pCNd->IsTextNode() && pCNd->GetTextNode()->GetNumRule() )
+ {
+ SwPosition aPos( *pCNd->GetTextNode() );
+ SwPaM aPam( aPos, aPos );
+ pDoc->DelNumRules( aPam );
+ }
+ }
+ }
+ else
+ {
+ if( nRowSpan > 0 )
+ nRowSpan = n + 1;
+ else
+ --nRowSpan;
+ }
+ pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n );
+ }
+ }
+ if( bBehind )
+ ++nRowIdx;
+ if( nRowIdx )
+ lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true );
+ // update the layout
+ aFndBox.MakeFrames( *this );
+ }
+ CHECK_TABLE( *this )
+ }
+ else
+ bRet = InsertRow_( pDoc, rBoxes, nCnt, bBehind );
+ return bRet;
+}
+
+/** SwTable::PrepareDelBoxes(..) adjusts the row span attributes for an upcoming
+ deletion of table cells and invalidates the layout of these cells.
+*/
+
+void SwTable::PrepareDelBoxes( const SwSelBoxes& rBoxes )
+{
+ if( !IsNewModel() )
+ return;
+
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ SwTableBox* pBox = rBoxes[i];
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan != 1 && pBox->GetFrameFormat()->GetFrameSize().GetWidth() )
+ {
+ tools::Long nLeft = lcl_Box2LeftBorder( *pBox );
+ SwTableLine *pLine = pBox->GetUpper();
+ sal_uInt16 nLinePos = GetTabLines().GetPos( pLine);
+ OSL_ENSURE( nLinePos < USHRT_MAX, "Box/table mismatch" );
+ if( nRowSpan > 1 )
+ {
+ if( ++nLinePos < GetTabLines().size() )
+ {
+ pLine = GetTabLines()[ nLinePos ];
+ pBox = lcl_LeftBorder2Box( nLeft, pLine );
+ OSL_ENSURE( pBox, "RowSpan irritation I" );
+ if( pBox )
+ pBox->setRowSpan( --nRowSpan );
+ }
+ }
+ else if( nLinePos > 0 )
+ {
+ do
+ {
+ pLine = GetTabLines()[ --nLinePos ];
+ pBox = lcl_LeftBorder2Box( nLeft, pLine );
+ OSL_ENSURE( pBox, "RowSpan irritation II" );
+ if( pBox )
+ {
+ nRowSpan = pBox->getRowSpan();
+ if( nRowSpan > 1 )
+ {
+ lcl_InvalidateCellFrame( *pBox );
+ --nRowSpan;
+ }
+ else
+ ++nRowSpan;
+ pBox->setRowSpan( nRowSpan );
+ }
+ else
+ nRowSpan = 1;
+ }
+ while( nRowSpan < 0 && nLinePos > 0 );
+ }
+ }
+ }
+}
+
+/** lcl_SearchSelBox(..) adds cells of a given table row to the selection structure
+ if it overlaps with the given x-position range
+*/
+
+static void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, tools::Long nMin, tools::Long nMax,
+ SwTableLine& rLine, bool bChkProtected, bool bColumn )
+{
+ tools::Long nLeft = 0;
+ tools::Long nRight = 0;
+ tools::Long nMid = ( nMax + nMin )/ 2;
+ const size_t nCount = rLine.GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
+ {
+ SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox];
+ OSL_ENSURE( pBox, "Missing table box" );
+ tools::Long nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ nRight += nWidth;
+ if( nRight > nMin )
+ {
+ bool bAdd = false;
+ if( nRight <= nMax )
+ bAdd = nLeft >= nMin || nRight >= nMid ||
+ nRight - nMin > nMin - nLeft;
+ else
+ bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft;
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( bAdd &&
+ ( !bChkProtected ||
+ !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) )
+ {
+ size_t const nOldCnt = rBoxes.size();
+ rBoxes.insert( pBox );
+ if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.size() )
+ {
+ SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox
+ : &pBox->FindStartOfRowSpan( rTable );
+ lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox );
+ }
+ }
+ }
+ if( nRight >= nMax )
+ break;
+ nLeft = nRight;
+ }
+}
+
+/** void SwTable::CreateSelection(..) fills the selection structure with table cells
+ for a given SwPaM, ie. start and end position inside a table
+*/
+
+void SwTable::CreateSelection( const SwPaM& rPam, SwSelBoxes& rBoxes,
+ const SearchType eSearch, bool bChkProtected ) const
+{
+ OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
+ if( m_aLines.empty() )
+ return;
+ const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode();
+ if( !pStartNd || !pEndNd )
+ return;
+ CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected );
+}
+
+/** void SwTable::CreateSelection(..) fills the selection structure with table cells
+ for given start and end nodes inside a table
+*/
+void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd,
+ SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const
+{
+ rBoxes.clear();
+ // Looking for start and end of the selection given by SwNode-pointer
+ const size_t nLines = m_aLines.size();
+ // nTop becomes the line number of the upper box
+ // nBottom becomes the line number of the lower box
+ size_t nTop = 0;
+ size_t nBottom = 0;
+ // nUpperMin becomes the left border value of the upper box
+ // nUpperMax becomes the right border of the upper box
+ // nLowerMin and nLowerMax the borders of the lower box
+ tools::Long nUpperMin = 0, nUpperMax = 0;
+ tools::Long nLowerMin = 0, nLowerMax = 0;
+ // nFound will incremented if a box is found
+ // 0 => no box found; 1 => the upper box has been found; 2 => both found
+ int nFound = 0;
+ for( size_t nRow = 0; nFound < 2 && nRow < nLines; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ OSL_ENSURE( pLine, "Missing table line" );
+ const size_t nCols = pLine->GetTabBoxes().size();
+ for( size_t nCol = 0; nCol < nCols; ++nCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
+ OSL_ENSURE( pBox, "Missing table box" );
+ if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd )
+ {
+ if( !bChkProtected ||
+ !pBox->GetFrameFormat()->GetProtect().IsContentProtected() )
+ rBoxes.insert( pBox );
+ if( nFound )
+ {
+ nBottom = nRow;
+ lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true );
+ ++nFound;
+ break;
+ }
+ else
+ {
+ nTop = nRow;
+ lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true );
+ ++nFound;
+ // If start and end node are identical, we're nearly done...
+ if( pEndNd == pStartNd )
+ {
+ nBottom = nTop;
+ nLowerMin = nUpperMin;
+ nLowerMax = nUpperMax;
+ ++nFound;
+ }
+ }
+ }
+ }
+ }
+ if( nFound < 2 )
+ return; // At least one node was not a part of the given table
+ if( eSearch == SEARCH_ROW )
+ {
+ // Selection of a row is quiet easy:
+ // every (unprotected) box between start and end line
+ // with a positive row span will be collected
+ for( size_t nRow = nTop; nRow <= nBottom; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ OSL_ENSURE( pLine, "Missing table line" );
+ const size_t nCount = pLine->GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
+ OSL_ENSURE( pBox, "Missing table box" );
+ if( pBox->getRowSpan() > 0 && ( !bChkProtected ||
+ !pBox->GetFrameFormat()->GetProtect().IsContentProtected() ) )
+ rBoxes.insert( pBox );
+ }
+ }
+ return;
+ }
+ bool bCombine = nTop == nBottom;
+ if( !bCombine )
+ {
+ tools::Long nMinWidth = nUpperMax - nUpperMin;
+ tools::Long nTmp = nLowerMax - nLowerMin;
+ if( nMinWidth > nTmp )
+ nMinWidth = nTmp;
+ nTmp = std::min(nLowerMax, nUpperMax);
+ nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin;
+ // If the overlapping between upper and lower box is less than half
+ // of the width (of the smaller cell), bCombine is set,
+ // e.g. if upper and lower cell are in different columns
+ bCombine = ( nTmp + nTmp < nMinWidth );
+ }
+ if( bCombine )
+ {
+ if( nUpperMin < nLowerMin )
+ nLowerMin = nUpperMin;
+ else
+ nUpperMin = nLowerMin;
+ if( nUpperMax > nLowerMax )
+ nLowerMax = nUpperMax;
+ else
+ nUpperMax = nLowerMax;
+ }
+ const bool bColumn = eSearch == SEARCH_COL;
+ if( bColumn )
+ {
+ for( size_t i = 0; i < nTop; ++i )
+ lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax,
+ *m_aLines[i], bChkProtected, bColumn );
+ }
+
+ {
+ tools::Long nMin = std::min(nUpperMin, nLowerMin);
+ tools::Long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax;
+ for( size_t i = nTop; i <= nBottom; ++i )
+ lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *m_aLines[i],
+ bChkProtected, bColumn );
+ }
+ if( bColumn )
+ {
+ for( size_t i = nBottom + 1; i < nLines; ++i )
+ lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *m_aLines[i],
+ bChkProtected, true );
+ }
+}
+
+/** void SwTable::ExpandColumnSelection(..) adds cell to the give selection to
+ assure that at least one cell of every row is part of the selection.
+*/
+
+void SwTable::ExpandColumnSelection( SwSelBoxes& rBoxes, tools::Long &rMin, tools::Long &rMax ) const
+{
+ OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
+ rMin = 0;
+ rMax = 0;
+ if( m_aLines.empty() || rBoxes.empty() )
+ return;
+
+ const size_t nLineCnt = m_aLines.size();
+ const size_t nBoxCnt = rBoxes.size();
+ size_t nBox = 0;
+ for( size_t nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ OSL_ENSURE( pLine, "Missing table line" );
+ const size_t nCols = pLine->GetTabBoxes().size();
+ for( size_t nCol = 0; nCol < nCols; ++nCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCol];
+ OSL_ENSURE( pBox, "Missing table box" );
+ if( pBox == rBoxes[nBox] )
+ {
+ lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 );
+ if( ++nBox >= nBoxCnt )
+ break;
+ }
+ }
+ }
+ for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ const size_t nCols = pLine->GetTabBoxes().size();
+ tools::Long nRight = 0;
+ for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
+ {
+ tools::Long nLeft = nRight;
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
+ nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ if( nLeft >= rMin && nRight <= rMax )
+ rBoxes.insert( pBox );
+ }
+ }
+}
+
+/** SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of
+ a cell selection for an upcoming (column) deletion
+*/
+void SwTable::PrepareDeleteCol( tools::Long nMin, tools::Long nMax )
+{
+ OSL_ENSURE( m_bNewModel, "Don't call me for old tables" );
+ if( m_aLines.empty() || nMax < nMin )
+ return;
+ tools::Long nMid = nMin ? ( nMin + nMax ) / 2 : 0;
+ const SwTwips nTabSize = GetFrameFormat()->GetFrameSize().GetWidth();
+ if( nTabSize == nMax )
+ nMid = nMax;
+ const size_t nLineCnt = m_aLines.size();
+ for( size_t nRow = 0; nRow < nLineCnt; ++nRow )
+ {
+ SwTableLine* pLine = m_aLines[nRow];
+ const size_t nCols = pLine->GetTabBoxes().size();
+ tools::Long nRight = 0;
+ for( size_t nCurrBox = 0; nCurrBox < nCols; ++nCurrBox )
+ {
+ tools::Long nLeft = nRight;
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
+ nRight += pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ if( nRight < nMin )
+ continue;
+ if( nLeft > nMax )
+ break;
+ tools::Long nNewWidth = -1;
+ if( nLeft < nMin )
+ {
+ if( nRight <= nMax )
+ nNewWidth = nMid - nLeft;
+ }
+ else if( nRight > nMax )
+ nNewWidth = nRight - nMid;
+ else
+ nNewWidth = 0;
+ if( nNewWidth >= 0 )
+ {
+ SwFrameFormat* pFrameFormat = pBox->ClaimFrameFormat();
+ SwFormatFrameSize aFrameSz( pFrameFormat->GetFrameSize() );
+ aFrameSz.SetWidth( nNewWidth );
+ pFrameFormat->SetFormatAttr( aFrameSz );
+ }
+ }
+ }
+}
+
+/** SwTable::ExpandSelection(..) adds all boxes to the box selections which are
+ overlapped by it.
+*/
+
+void SwTable::ExpandSelection( SwSelBoxes& rBoxes ) const
+{
+ for (size_t i = 0; i < rBoxes.size(); ++i)
+ {
+ SwTableBox *pBox = rBoxes[i];
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan != 1 )
+ {
+ SwTableBox *pMasterBox = nRowSpan > 0 ? pBox
+ : &pBox->FindStartOfRowSpan( *this );
+ lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox );
+ }
+ }
+}
+
+/** SwTable::CheckRowSpan(..) looks for the next line without an overlapping to
+ the previous line.
+*/
+
+void SwTable::CheckRowSpan( SwTableLine* &rpLine, bool bUp ) const
+{
+ OSL_ENSURE( IsNewModel(), "Don't call me for old tables" );
+ sal_uInt16 nLineIdx = GetTabLines().GetPos( rpLine );
+ OSL_ENSURE( nLineIdx < GetTabLines().size(), "Start line out of range" );
+ bool bChange = true;
+ if( bUp )
+ {
+ while( bChange )
+ {
+ bChange = false;
+ rpLine = GetTabLines()[ nLineIdx ];
+ const size_t nCols = rpLine->GetTabBoxes().size();
+ for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
+ {
+ SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
+ if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 )
+ bChange = true;
+ }
+ if( bChange )
+ {
+ if( nLineIdx )
+ --nLineIdx;
+ else
+ {
+ bChange = false;
+ rpLine = nullptr;
+ }
+ }
+ }
+ }
+ else
+ {
+ const size_t nMaxLine = GetTabLines().size();
+ while( bChange )
+ {
+ bChange = false;
+ rpLine = GetTabLines()[ nLineIdx ];
+ const size_t nCols = rpLine->GetTabBoxes().size();
+ for( size_t nCol = 0; !bChange && nCol < nCols; ++nCol )
+ {
+ SwTableBox* pBox = rpLine->GetTabBoxes()[nCol];
+ if( pBox->getRowSpan() < 0 )
+ bChange = true;
+ }
+ if( bChange )
+ {
+ ++nLineIdx;
+ if( nLineIdx >= nMaxLine )
+ {
+ bChange = false;
+ rpLine = nullptr;
+ }
+ }
+ }
+ }
+}
+
+// This structure corrects the row span attributes for a top line of a table
+// In a top line no negative row span is allowed, so these have to be corrected.
+// If there has been at least one correction, all values are stored
+// and can be used by undo of table split
+SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn )
+ : mnSplitLine( nSplitLn )
+{
+ bool bDontSave = true; // nothing changed, nothing to save
+ const size_t nColCount = rBoxes.size();
+ OSL_ENSURE( nColCount, "Empty Table Line" );
+ mnRowSpans.resize( nColCount );
+ for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
+ {
+ SwTableBox* pBox = rBoxes[nCurrCol];
+ OSL_ENSURE( pBox, "Missing Table Box" );
+ sal_Int32 nRowSp = pBox->getRowSpan();
+ mnRowSpans[ nCurrCol ] = nRowSp;
+ if( nRowSp < 0 )
+ {
+ bDontSave = false;
+ nRowSp = -nRowSp;
+ pBox->setRowSpan( nRowSp ); // correction needed
+ }
+ }
+ if( bDontSave )
+ mnRowSpans.clear();
+}
+
+// This function is called by undo of table split to restore the old row span
+// values at the split line
+void SwTable::RestoreRowSpan( const SwSaveRowSpan& rSave )
+{
+ if( !IsNewModel() ) // for new model only
+ return;
+ sal_uInt16 nLineCount = GetTabLines().size();
+ OSL_ENSURE( rSave.mnSplitLine < nLineCount, "Restore behind last line?" );
+ if( rSave.mnSplitLine >= nLineCount )
+ return;
+
+ SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine];
+ const size_t nColCount = pLine->GetTabBoxes().size();
+ OSL_ENSURE( nColCount, "Empty Table Line" );
+ OSL_ENSURE( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" );
+ if( nColCount != rSave.mnRowSpans.size() )
+ return;
+
+ for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
+ OSL_ENSURE( pBox, "Missing Table Box" );
+ sal_Int32 nRowSp = pBox->getRowSpan();
+ if( nRowSp != rSave.mnRowSpans[ nCurrCol ] )
+ {
+ OSL_ENSURE( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" );
+ OSL_ENSURE( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" );
+ pBox->setRowSpan( -nRowSp );
+
+ sal_uInt16 nLine = rSave.mnSplitLine;
+ if( nLine )
+ {
+ tools::Long nLeftBorder = lcl_Box2LeftBorder( *pBox );
+ SwTableBox* pNext;
+ do
+ {
+ pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] );
+ if( pNext )
+ {
+ pBox = pNext;
+ tools::Long nNewSpan = pBox->getRowSpan();
+ if( pBox->getRowSpan() < 1 )
+ nNewSpan -= nRowSp;
+ else
+ {
+ nNewSpan += nRowSp;
+ pNext = nullptr;
+ }
+ pBox->setRowSpan( nNewSpan );
+ }
+ } while( nLine && pNext );
+ }
+ }
+ }
+}
+
+std::unique_ptr<SwSaveRowSpan> SwTable::CleanUpTopRowSpan( sal_uInt16 nSplitLine )
+{
+ if( !IsNewModel() )
+ return nullptr;
+ std::unique_ptr<SwSaveRowSpan> pRet(new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine ));
+ if( pRet->mnRowSpans.empty() )
+ return nullptr;
+ return pRet;
+}
+
+void SwTable::CleanUpBottomRowSpan( sal_uInt16 nDelLines )
+{
+ if( !IsNewModel() )
+ return;
+ const size_t nLastLine = GetTabLines().size()-1;
+ SwTableLine* pLine = GetTabLines()[nLastLine];
+ const size_t nColCount = pLine->GetTabBoxes().size();
+ OSL_ENSURE( nColCount, "Empty Table Line" );
+ for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
+ OSL_ENSURE( pBox, "Missing Table Box" );
+ sal_Int32 nRowSp = pBox->getRowSpan();
+ if( nRowSp < 0 )
+ nRowSp = -nRowSp;
+ if( nRowSp > 1 )
+ {
+ lcl_ChangeRowSpan( *this, -static_cast<tools::Long>(nDelLines),
+ o3tl::narrowing<sal_uInt16>(nLastLine), false );
+ break;
+ }
+ }
+}
+
+/**
+ This is kind of similar to InsertSpannedRow()/InsertRow() but that one would
+ recursively copy subtables, which would kind of defeat the purpose;
+ this function directly moves the subtable rows's cells into the newly
+ created rows. For the non-subtable boxes, covered-cells are created.
+
+ Outer row heights are adjusted to match the inner row heights, and the
+ last row's height is tweaked to ensure the sum of the heights is at least
+ the original outer row's minimal height.
+
+ Inner row backgrounds are copied to its cells, if they lack a background.
+
+ This currently can't handle more than 1 subtable in a row;
+ the inner rows of all subtables would need to be sorted by their height
+ to create the correct outer row structure, which is tricky and probably
+ requires a layout for the typical variable-height case.
+
+ */
+void SwTable::ConvertSubtableBox(sal_uInt16 const nRow, sal_uInt16 const nBox)
+{
+ SwDoc *const pDoc(GetFrameFormat()->GetDoc());
+ SwTableLine *const pSourceLine(GetTabLines()[nRow]);
+ SwTableBox *const pSubTableBox(pSourceLine->GetTabBoxes()[nBox]);
+ assert(!pSubTableBox->GetTabLines().empty());
+ // are relative (%) heights possible? apparently not
+ SwFormatFrameSize const outerSize(pSourceLine->GetFrameFormat()->GetFrameSize());
+ if (outerSize.GetHeightSizeType() != SwFrameSize::Variable)
+ { // tdf#145871 clear fixed size in first row
+ pSourceLine->ClaimFrameFormat();
+ pSourceLine->GetFrameFormat()->ResetFormatAttr(RES_FRM_SIZE);
+ }
+ tools::Long minHeights(0);
+ {
+ SwFrameFormat const& rSubLineFormat(*pSubTableBox->GetTabLines()[0]->GetFrameFormat());
+ SwFormatFrameSize const* pSize = rSubLineFormat.GetItemIfSet(RES_FRM_SIZE);
+ if (pSize)
+ { // for first row, apply height from inner row to outer row.
+ // in case the existing outer row height was larger than the entire
+ // subtable, the last inserted row needs to be tweaked (below)
+ pSourceLine->GetFrameFormat()->SetFormatAttr(*pSize);
+ if (pSize->GetHeightSizeType() != SwFrameSize::Variable)
+ {
+ minHeights += pSize->GetHeight();
+ }
+ }
+ }
+ for (size_t i = 1; i < pSubTableBox->GetTabLines().size(); ++i)
+ {
+ SwTableLine *const pSubLine(pSubTableBox->GetTabLines()[i]);
+ SwTableLine *const pNewLine = new SwTableLine(
+ static_cast<SwTableLineFormat*>(pSourceLine->GetFrameFormat()),
+ pSourceLine->GetTabBoxes().size() - 1 + pSubLine->GetTabBoxes().size(),
+ nullptr);
+ SwFrameFormat const& rSubLineFormat(*pSubLine->GetFrameFormat());
+ SwFormatFrameSize const* pSize = rSubLineFormat.GetItemIfSet(RES_FRM_SIZE);
+ if (pSize)
+ { // for rows 2..N, copy inner row height to outer row
+ pNewLine->ClaimFrameFormat();
+ pNewLine->GetFrameFormat()->SetFormatAttr(*pSize);
+ if (pSize->GetHeightSizeType() != SwFrameSize::Variable)
+ {
+ minHeights += pSize->GetHeight();
+ }
+ }
+ // ensure the sum of the lines is at least as high as the outer line was
+ if (i == pSubTableBox->GetTabLines().size() - 1
+ && outerSize.GetHeightSizeType() != SwFrameSize::Variable
+ && minHeights < outerSize.GetHeight())
+ {
+ SwFormatFrameSize lastSize(pNewLine->GetFrameFormat()->GetFrameSize());
+ lastSize.SetHeight(lastSize.GetHeight() + outerSize.GetHeight() - minHeights);
+ if (lastSize.GetHeightSizeType() == SwFrameSize::Variable)
+ {
+ lastSize.SetHeightSizeType(SwFrameSize::Minimum);
+ }
+ pNewLine->ClaimFrameFormat();
+ pNewLine->GetFrameFormat()->SetFormatAttr(lastSize);
+ }
+ SfxPoolItem const* pRowBrush(nullptr);
+ (void)rSubLineFormat.GetItemState(RES_BACKGROUND, true, &pRowBrush);
+ GetTabLines().insert(GetTabLines().begin() + nRow + i, pNewLine);
+ for (size_t j = 0; j < pSourceLine->GetTabBoxes().size(); ++j)
+ {
+ if (j == nBox)
+ {
+ for (size_t k = 0; k < pSubLine->GetTabBoxes().size(); ++k)
+ {
+ // move box k to new outer row
+ SwTableBox *const pSourceBox(pSubLine->GetTabBoxes()[k]);
+ assert(pSourceBox->getRowSpan() == 1);
+ // import filter (xmltbli.cxx) converts all box widths to absolute
+ assert(pSourceBox->GetFrameFormat()->GetFrameSize().GetWidthPercent() == 0);
+ ::InsTableBox(*pDoc, GetTableNode(), pNewLine,
+ static_cast<SwTableBoxFormat*>(pSourceBox->GetFrameFormat()),
+ pSourceBox, j+k, 1);
+ // insert dummy text node...
+ pDoc->GetNodes().MakeTextNode(
+ SwNodeIndex(*pSourceBox->GetSttNd(), +1),
+ pDoc->GetDfltTextFormatColl());
+ SwNodeRange content(*pSourceBox->GetSttNd(), SwNodeOffset(+2),
+ *pSourceBox->GetSttNd()->EndOfSectionNode());
+ SwTableBox *const pNewBox(pNewLine->GetTabBoxes()[j+k]);
+ SwNodeIndex insPos(*pNewBox->GetSttNd(), 1);
+ // MoveNodes would delete the box SwStartNode/SwEndNode
+ // without the dummy node
+#if 0
+ pDoc->GetNodes().MoveNodes(content, pDoc->GetNodes(), insPos, false);
+#else
+ pDoc->getIDocumentContentOperations().MoveNodeRange(content, insPos, SwMoveFlags::NO_DELFRMS|SwMoveFlags::REDLINES);
+#endif
+ // delete the empty node that was bundled in the new box
+ pDoc->GetNodes().Delete(insPos);
+ if (pRowBrush)
+ {
+ if (pNewBox->GetFrameFormat()->GetItemState(RES_BACKGROUND, true) != SfxItemState::SET)
+ { // set inner row background on inner cell
+ pNewBox->ClaimFrameFormat();
+ pNewBox->GetFrameFormat()->SetFormatAttr(*pRowBrush);
+ }
+ }
+ // assume that the borders can be left as they are, because
+ // lines don't have borders, only boxes do
+ }
+ }
+ else
+ {
+ // insert covered cell for box j
+ SwTableBox *const pSourceBox(pSourceLine->GetTabBoxes()[j]);
+ assert(pSourceBox->GetTabLines().empty()); // checked for that
+ sal_uInt16 const nInsPos(j < nBox ? j : j + pSubLine->GetTabBoxes().size() - 1);
+ ::InsTableBox(*pDoc, GetTableNode(), pNewLine,
+ static_cast<SwTableBoxFormat*>(pSourceBox->GetFrameFormat()),
+ pSourceBox, nInsPos, 1);
+ // adjust row span:
+ // N rows in subtable, N-1 rows inserted:
+ // -1 -> -N ; -(N-1) ... -1
+ // -2 -> -(N+1) ; -N .. -2
+ // 1 -> N ; -(N-1) .. -1
+ // 2 -> N+1 ; -N .. -2
+ sal_Int32 newSourceRowSpan(pSourceBox->getRowSpan());
+ sal_Int32 newBoxRowSpan;
+ if (newSourceRowSpan < 0)
+ {
+ newSourceRowSpan -= pSubTableBox->GetTabLines().size() - 1;
+ newBoxRowSpan = newSourceRowSpan + i;
+ }
+ else
+ {
+ newSourceRowSpan += pSubTableBox->GetTabLines().size() - 1;
+ newBoxRowSpan = -(newSourceRowSpan - sal::static_int_cast<tools::Long>(i));
+ }
+ pNewLine->GetTabBoxes()[nInsPos]->setRowSpan(newBoxRowSpan);
+ if (i == pSubTableBox->GetTabLines().size() - 1)
+ { // only last iteration
+ pSourceBox->setRowSpan(newSourceRowSpan);
+ }
+ }
+ }
+ }
+ // delete inner rows 2..N
+ while (1 < pSubTableBox->GetTabLines().size())
+ {
+ // careful: the last box deletes pSubLine!
+ SwTableLine *const pSubLine(pSubTableBox->GetTabLines()[1]);
+ for (size_t j = pSubLine->GetTabBoxes().size(); 0 < j; --j)
+ {
+ SwTableBox *const pBox(pSubLine->GetTabBoxes()[0]);
+ DeleteBox_(*this, pBox, nullptr, false, false, nullptr);
+ }
+ }
+ // fix row spans in lines preceding nRow
+ lcl_ChangeRowSpan(*this, pSubTableBox->GetTabLines().size() - 1, nRow - 1, false);
+ // note: the first line of the inner table remains; caller will call
+ // GCLines() to remove it
+}
+
+bool SwTable::CanConvertSubtables() const
+{
+ for (SwTableLine const*const pLine : GetTabLines())
+ {
+ bool haveSubtable(false);
+ for (SwTableBox const*const pBox : pLine->GetTabBoxes())
+ {
+ if (pBox->IsFormulaOrValueBox() == RES_BOXATR_FORMULA)
+ {
+ return false; // no table box formulas yet
+ }
+ if (!pBox->GetTabLines().empty())
+ {
+ if (haveSubtable)
+ { // can't handle 2 subtable in a row yet
+ return false;
+ }
+ haveSubtable = true;
+ bool haveNonFixedInnerLine(false);
+ for (SwTableLine const*const pInnerLine : pBox->GetTabLines())
+ {
+ // bitmap row background will look different
+ SwFrameFormat const& rRowFormat(*pInnerLine->GetFrameFormat());
+ std::unique_ptr<SvxBrushItem> pBrush(rRowFormat.makeBackgroundBrushItem());
+ assert(pBrush);
+ if (pBrush->GetGraphicObject() != nullptr)
+ {
+ /* TODO: all cells could override this?
+ for (SwTableBox & rInnerBox : rInnerLine.GetTabBoxes())
+ */
+ if (1 < pInnerLine->GetTabBoxes().size()) // except if only 1 cell?
+ {
+ return false;
+ }
+ }
+ if (SwFormatFrameSize const* pSize = rRowFormat.GetItemIfSet(RES_FRM_SIZE))
+ {
+ if (pSize->GetHeightSizeType() != SwFrameSize::Fixed)
+ {
+ haveNonFixedInnerLine = true;
+ }
+ }
+ else
+ {
+ haveNonFixedInnerLine = true; // default
+ }
+ for (SwTableBox const*const pInnerBox : pInnerLine->GetTabBoxes())
+ {
+ if (!pInnerBox->GetTabLines().empty())
+ {
+ return false; // nested subtable :(
+ }
+ }
+ }
+ if (haveNonFixedInnerLine)
+ {
+ if (SwFormatFrameSize const* pSize = pLine->GetFrameFormat()->GetItemIfSet(RES_FRM_SIZE))
+ {
+ if (pSize->GetHeightSizeType() != SwFrameSize::Variable)
+ {
+ // not possible to distribute fixed outer row height on rows without layout
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ // note: fields that refer to table cells may be *outside* the table,
+ // so the entire document needs to be imported before checking here
+ // (same for table box formulas and charts)
+ SwDoc *const pDoc(GetFrameFormat()->GetDoc());
+ SwFieldType const*const pTableFields(
+ pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Table, "", false));
+ std::vector<SwFormatField*> vFields;
+ pTableFields->GatherFields(vFields);
+ if (!vFields.empty())
+ {
+ return false; // no formulas in fields yet
+ }
+ if (pDoc->GetAttrPool().GetItemCount2(RES_BOXATR_FORMULA) != 0)
+ {
+ return false; // no table box formulas yet
+ }
+ OUString const tableName(GetFrameFormat()->GetName());
+ SwNodeIndex temp(*pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), +1);
+ while (SwStartNode const*const pStartNode = temp.GetNode().GetStartNode())
+ {
+ ++temp;
+ SwOLENode const*const pOLENode(temp.GetNode().GetOLENode());
+ if (pOLENode && tableName == pOLENode->GetChartTableName())
+ { // there are charts that refer to this table
+ // presumably such charts would need to be adapted somehow?
+ return false;
+ }
+ temp.Assign(*pStartNode->EndOfSectionNode(), +1);
+ }
+ return true;
+}
+
+void SwTable::ConvertSubtables()
+{
+ FndBox_ all(nullptr, nullptr);
+ all.DelFrames(*this); // tdf#151375 avoid UAF by frames on deleted cells
+ for (size_t i = 0; i < GetTabLines().size(); ++i)
+ {
+ SwTableLine *const pLine(GetTabLines()[i]);
+ for (size_t j = 0; j < pLine->GetTabBoxes().size(); ++j)
+ {
+ SwTableBox *const pBox(pLine->GetTabBoxes()[j]);
+ SwTableLines & rInnerLines(pBox->GetTabLines());
+ if (!rInnerLines.empty())
+ {
+ ConvertSubtableBox(i, j);
+ }
+ }
+ }
+ GCLines();
+ m_bNewModel = true;
+ all.MakeFrames(*this);
+#if 0
+ // note: outline nodes (and ordinary lists) are sorted by MoveNodes() itself
+ // (this could change order inside table of contents, but that's a
+ // really esoteric use-case)
+ // nodes were moved - sort marks, redlines, footnotes
+ SwDoc *const pDoc(GetFrameFormat()->GetDoc());
+ pDoc->getIDocumentMarkAccess()->assureSortedMarkContainers();
+ pDoc->getIDocumentRedlineAccess().GetRedlineTable().Resort();
+ pDoc->GetFootnoteIdxs().UpdateAllFootnote();
+#endif
+ // assume that there aren't any node indexes to the deleted box start/end nodes
+ CHECK_TABLE( *this )
+}
+
+#ifdef DBG_UTIL
+
+namespace {
+
+struct RowSpanCheck
+{
+ sal_Int32 nRowSpan;
+ SwTwips nLeft;
+ SwTwips nRight;
+};
+
+}
+
+void SwTable::CheckConsistency() const
+{
+ if( !IsNewModel() )
+ return;
+ const size_t nLineCount = GetTabLines().size();
+ const SwTwips nTabSize = GetFrameFormat()->GetFrameSize().GetWidth();
+ SwTwips nLineWidth = 0;
+ std::list< RowSpanCheck > aRowSpanCells;
+ std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end();
+ SwNodeIndex index(*GetTableNode());
+ ++index;
+ for( size_t nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine )
+ {
+ SwTwips nWidth = 0;
+ SwTableLine* pLine = GetTabLines()[nCurrLine];
+ SAL_WARN_IF( !pLine, "sw.core", "Missing Table Line" );
+ const size_t nColCount = pLine->GetTabBoxes().size();
+ SAL_WARN_IF( !nColCount, "sw.core", "Empty Table Line" );
+ for( size_t nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol];
+ assert(pBox);
+ SAL_WARN_IF(GetTableNode()->EndOfSectionIndex() <= index.GetIndex(), "sw.core", "Box not in table nodes");
+ SAL_WARN_IF(!index.GetNode().IsStartNode(), "sw.core", "No box start node");
+ index = *index.GetNode().EndOfSectionNode();
+ ++index;
+ SwTwips nNewWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth() + nWidth;
+ sal_Int32 nRowSp = pBox->getRowSpan();
+ if( nRowSp < 0 )
+ {
+ SAL_WARN_IF( aIter == aRowSpanCells.end(),
+ "sw.core", "Missing master box");
+ if (aIter != aRowSpanCells.end())
+ {
+ SAL_WARN_IF( aIter->nLeft != nWidth || aIter->nRight != nNewWidth,
+ "sw.core", "Wrong position/size of overlapped table box");
+ --(aIter->nRowSpan);
+ SAL_WARN_IF( aIter->nRowSpan != -nRowSp, "sw.core",
+ "Wrong row span value" );
+ if( nRowSp == -1 )
+ {
+ aIter = aRowSpanCells.erase(aIter);
+ }
+ else
+ ++aIter;
+ }
+ }
+ else if( nRowSp != 1 )
+ {
+ SAL_WARN_IF( !nRowSp, "sw.core", "Zero row span?!" );
+ RowSpanCheck aEntry;
+ aEntry.nLeft = nWidth;
+ aEntry.nRight = nNewWidth;
+ aEntry.nRowSpan = nRowSp;
+ aRowSpanCells.insert( aIter, aEntry );
+ }
+ nWidth = nNewWidth;
+ }
+ if( !nCurrLine )
+ nLineWidth = nWidth;
+ SAL_WARN_IF( nWidth != nLineWidth, "sw.core",
+ "Different Line Widths: first: " << nLineWidth
+ << " current [" << nCurrLine << "]: " << nWidth);
+ SAL_WARN_IF( std::abs(nWidth - nTabSize) > 1 /* how tolerant? */, "sw.core",
+ "Line width differs from table width: " << nTabSize
+ << " current [" << nCurrLine << "]: " << nWidth);
+ SAL_WARN_IF( nWidth < 0 || nWidth > USHRT_MAX, "sw.core",
+ "Width out of range [" << nCurrLine << "]: " << nWidth);
+ SAL_WARN_IF( aIter != aRowSpanCells.end(), "sw.core",
+ "Missing overlapped box" );
+ aIter = aRowSpanCells.begin();
+ }
+ bool bEmpty = aRowSpanCells.empty();
+ SAL_WARN_IF( !bEmpty, "sw.core", "Open row span detected" );
+ SAL_WARN_IF(GetTableNode()->EndOfSectionNode() != &index.GetNode(), "sw.core", "table end node not found");
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/table/swtable.cxx b/sw/source/core/table/swtable.cxx
new file mode 100644
index 000000000..c2ff14822
--- /dev/null
+++ b/sw/source/core/table/swtable.cxx
@@ -0,0 +1,2960 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/colritem.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/linkmgr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtpdsc.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <frmatr.hxx>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docary.hxx>
+#include <frame.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <tabcol.hxx>
+#include <tabfrm.hxx>
+#include <cellfrm.hxx>
+#include <rowfrm.hxx>
+#include <swserv.hxx>
+#include <expfld.hxx>
+#include <mdiexp.hxx>
+#include <cellatr.hxx>
+#include <txatbase.hxx>
+#include <htmltbl.hxx>
+#include <swtblfmt.hxx>
+#include <ndindex.hxx>
+#include <tblrwcl.hxx>
+#include <shellres.hxx>
+#include <viewsh.hxx>
+#include <redline.hxx>
+#include <vector>
+#include <calbck.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/numformat.hxx>
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+using namespace com::sun::star;
+
+
+#define COLFUZZY 20
+
+static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
+ bool bChgAlign, SwNodeOffset nNdPos );
+
+sal_Int32 SwTableBox::getRowSpan() const
+{
+ return mnRowSpan;
+}
+
+void SwTableBox::setRowSpan( sal_Int32 nNewRowSpan )
+{
+ mnRowSpan = nNewRowSpan;
+}
+
+bool SwTableBox::getDummyFlag() const
+{
+ return mbDummyFlag;
+}
+
+void SwTableBox::setDummyFlag( bool bDummy )
+{
+ mbDummyFlag = bDummy;
+}
+
+//JP 15.09.98: Bug 55741 - Keep tabs (front and rear)
+static OUString& lcl_TabToBlankAtSttEnd( OUString& rText )
+{
+ sal_Unicode c;
+ sal_Int32 n;
+
+ for( n = 0; n < rText.getLength() && ' ' >= ( c = rText[n] ); ++n )
+ if( '\x9' == c )
+ rText = rText.replaceAt( n, 1, u" " );
+ for( n = rText.getLength(); n && ' ' >= ( c = rText[--n] ); )
+ if( '\x9' == c )
+ rText = rText.replaceAt( n, 1, u" " );
+ return rText;
+}
+
+static OUString& lcl_DelTabsAtSttEnd( OUString& rText )
+{
+ sal_Unicode c;
+ sal_Int32 n;
+ OUStringBuffer sBuff(rText);
+
+ for( n = 0; n < sBuff.getLength() && ' ' >= ( c = sBuff[ n ]); ++n )
+ {
+ if( '\x9' == c )
+ sBuff.remove( n--, 1 );
+ }
+ for( n = sBuff.getLength(); n && ' ' >= ( c = sBuff[ --n ]); )
+ {
+ if( '\x9' == c )
+ sBuff.remove( n, 1 );
+ }
+ rText = sBuff.makeStringAndClear();
+ return rText;
+}
+
+void InsTableBox( SwDoc& rDoc, SwTableNode* pTableNd,
+ SwTableLine* pLine, SwTableBoxFormat* pBoxFrameFormat,
+ SwTableBox* pBox,
+ sal_uInt16 nInsPos, sal_uInt16 nCnt )
+{
+ OSL_ENSURE( pBox->GetSttNd(), "Box with no start node" );
+ SwNodeIndex aIdx( *pBox->GetSttNd(), +1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = rDoc.GetNodes().GoNext( &aIdx );
+ OSL_ENSURE( pCNd, "Box with no content node" );
+
+ if( pCNd->IsTextNode() )
+ {
+ if( pCNd->GetpSwAttrSet() )
+ {
+ SwAttrSet aAttrSet( *pCNd->GetpSwAttrSet() );
+ if(pCNd->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT))
+ {
+ SwFormatAutoFormat format = aAttrSet.Get(RES_PARATR_LIST_AUTOFMT);
+ const std::shared_ptr<SfxItemSet>& handle = format.GetStyleHandle();
+ aAttrSet.Put(*handle);
+ }
+ if( pBox->GetSaveNumFormatColor() )
+ {
+ if( pBox->GetSaveUserColor() )
+ aAttrSet.Put( SvxColorItem( *pBox->GetSaveUserColor(), RES_CHRATR_COLOR ));
+ else
+ aAttrSet.ClearItem( RES_CHRATR_COLOR );
+ }
+ rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
+ static_cast<SwTextNode*>(pCNd)->GetTextColl(),
+ &aAttrSet, nInsPos, nCnt );
+ }
+ else
+ rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
+ static_cast<SwTextNode*>(pCNd)->GetTextColl(),
+ pCNd->GetpSwAttrSet(), nInsPos, nCnt );
+ }
+ else
+ rDoc.GetNodes().InsBoxen( pTableNd, pLine, pBoxFrameFormat,
+ rDoc.GetDfltTextFormatColl(), nullptr,
+ nInsPos, nCnt );
+
+ sal_Int32 nRowSpan = pBox->getRowSpan();
+ if( nRowSpan != 1 )
+ {
+ SwTableBoxes& rTableBoxes = pLine->GetTabBoxes();
+ for( sal_uInt16 i = 0; i < nCnt; ++i )
+ {
+ pBox = rTableBoxes[ i + nInsPos ];
+ pBox->setRowSpan( nRowSpan );
+ }
+ }
+}
+
+SwTable::SwTable()
+ : SwClient( nullptr ),
+ m_pTableNode( nullptr ),
+ m_nGraphicsThatResize( 0 ),
+ m_nRowsToRepeat( 1 ),
+ m_bModifyLocked( false ),
+ m_bNewModel( true )
+{
+ // default value set in the options
+ m_eTableChgMode = GetTableChgDefaultMode();
+}
+
+SwTable::SwTable( const SwTable& rTable )
+ : SwClient( rTable.GetFrameFormat() ),
+ m_pTableNode( nullptr ),
+ m_eTableChgMode( rTable.m_eTableChgMode ),
+ m_nGraphicsThatResize( 0 ),
+ m_nRowsToRepeat( rTable.GetRowsToRepeat() ),
+ maTableStyleName(rTable.maTableStyleName),
+ m_bModifyLocked( false ),
+ m_bNewModel( rTable.m_bNewModel )
+{
+}
+
+void DelBoxNode( SwTableSortBoxes const & rSortCntBoxes )
+{
+ for (size_t n = 0; n < rSortCntBoxes.size(); ++n)
+ {
+ rSortCntBoxes[ n ]->m_pStartNode = nullptr;
+ }
+}
+
+SwTable::~SwTable()
+{
+ if( m_xRefObj.is() )
+ {
+ SwDoc* pDoc = GetFrameFormat()->GetDoc();
+ if( !pDoc->IsInDtor() ) // then remove from the list
+ pDoc->getIDocumentLinksAdministration().GetLinkManager().RemoveServer( m_xRefObj.get() );
+
+ m_xRefObj->Closed();
+ }
+
+ // the table can be deleted if it's the last client of the FrameFormat
+ SwTableFormat* pFormat = GetFrameFormat();
+ pFormat->Remove( this ); // remove
+
+ if( !pFormat->HasWriterListeners() )
+ pFormat->GetDoc()->DelTableFrameFormat( pFormat ); // and delete
+
+ // Delete the pointers from the SortArray of the boxes. The objects
+ // are preserved and are deleted by the lines/boxes arrays dtor.
+ // Note: unfortunately not enough, pointers to the StartNode of the
+ // section need deletion.
+ DelBoxNode(m_TabSortContentBoxes);
+ m_TabSortContentBoxes.clear();
+}
+
+namespace
+{
+
+template<class T>
+T lcl_MulDiv64(sal_uInt64 nA, sal_uInt64 nM, sal_uInt64 nD)
+{
+ assert(nD != 0);
+ return nD == 0 ? static_cast<T>(nA*nM) : static_cast<T>((nA*nM)/nD);
+}
+
+}
+
+static void FormatInArr( std::vector<SwFormat*>& rFormatArr, SwFormat* pBoxFormat )
+{
+ std::vector<SwFormat*>::const_iterator it = std::find( rFormatArr.begin(), rFormatArr.end(), pBoxFormat );
+ if ( it == rFormatArr.end() )
+ rFormatArr.push_back( pBoxFormat );
+}
+
+static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
+ const tools::Long nNew, std::vector<SwFormat*>& rFormatArr );
+
+static void lcl_ModifyLines( SwTableLines &rLines, const tools::Long nOld,
+ const tools::Long nNew, std::vector<SwFormat*>& rFormatArr, const bool bCheckSum )
+{
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ ::lcl_ModifyBoxes( rLines[i]->GetTabBoxes(), nOld, nNew, rFormatArr );
+ if( bCheckSum )
+ {
+ for(SwFormat* pFormat : rFormatArr)
+ {
+ const SwTwips nBox = lcl_MulDiv64<SwTwips>(pFormat->GetFrameSize().GetWidth(), nNew, nOld);
+ SwFormatFrameSize aNewBox( SwFrameSize::Variable, nBox, 0 );
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aNewBox );
+ pFormat->UnlockModify();
+ }
+ }
+}
+
+static void lcl_ModifyBoxes( SwTableBoxes &rBoxes, const tools::Long nOld,
+ const tools::Long nNew, std::vector<SwFormat*>& rFormatArr )
+{
+ sal_uInt64 nSum = 0; // To avoid rounding errors we summarize all box widths
+ sal_uInt64 nOriginalSum = 0; // Sum of original widths
+ for ( size_t i = 0; i < rBoxes.size(); ++i )
+ {
+ SwTableBox &rBox = *rBoxes[i];
+ if ( !rBox.GetTabLines().empty() )
+ {
+ // For SubTables the rounding problem will not be solved :-(
+ ::lcl_ModifyLines( rBox.GetTabLines(), nOld, nNew, rFormatArr, false );
+ }
+ // Adjust the box
+ SwFrameFormat *pFormat = rBox.GetFrameFormat();
+ sal_uInt64 nBox = pFormat->GetFrameSize().GetWidth();
+ nOriginalSum += nBox;
+ nBox = lcl_MulDiv64<sal_uInt64>(nBox, nNew, nOld);
+ const sal_uInt64 nWishedSum = lcl_MulDiv64<sal_uInt64>(nOriginalSum, nNew, nOld) - nSum;
+ if( nWishedSum > 0 )
+ {
+ if( nBox == nWishedSum )
+ FormatInArr( rFormatArr, pFormat );
+ else
+ {
+ nBox = nWishedSum;
+ pFormat = rBox.ClaimFrameFormat();
+ SwFormatFrameSize aNewBox( SwFrameSize::Variable, static_cast< SwTwips >(nBox), 0 );
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aNewBox );
+ pFormat->UnlockModify();
+ }
+ }
+ else {
+ OSL_FAIL( "Rounding error" );
+ }
+ nSum += nBox;
+ }
+}
+
+void SwTable::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ // catch SSize changes, to adjust the lines/boxes
+ const sal_uInt16 nWhich = pLegacy->GetWhich();
+ const SwFormatFrameSize* pNewSize = nullptr, *pOldSize = nullptr;
+ switch(nWhich)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ if (pLegacy->m_pOld && pLegacy->m_pNew
+ && (pNewSize = static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet()->GetItemIfSet(
+ RES_FRM_SIZE,
+ false)))
+ {
+ pOldSize = &static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->GetFrameSize();
+ }
+ }
+ break;
+ case RES_FRM_SIZE:
+ {
+ pOldSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pOld);
+ pNewSize = static_cast<const SwFormatFrameSize*>(pLegacy->m_pNew);
+ }
+ break;
+ default:
+ CheckRegistration(pLegacy->m_pOld);
+ }
+ if (pOldSize && pNewSize && !m_bModifyLocked)
+ AdjustWidths(pOldSize->GetWidth(), pNewSize->GetWidth());
+}
+
+void SwTable::AdjustWidths( const tools::Long nOld, const tools::Long nNew )
+{
+ std::vector<SwFormat*> aFormatArr;
+ aFormatArr.reserve( m_aLines[0]->GetTabBoxes().size() );
+ ::lcl_ModifyLines( m_aLines, nOld, nNew, aFormatArr, true );
+}
+
+static void lcl_RefreshHidden( SwTabCols &rToFill, size_t nPos )
+{
+ for ( size_t i = 0; i < rToFill.Count(); ++i )
+ {
+ if ( std::abs(static_cast<tools::Long>(nPos) - rToFill[i]) <= COLFUZZY )
+ {
+ rToFill.SetHidden( i, false );
+ break;
+ }
+ }
+}
+
+static void lcl_SortedTabColInsert( SwTabCols &rToFill, const SwTableBox *pBox,
+ const SwFrameFormat *pTabFormat, const bool bHidden,
+ const bool bRefreshHidden )
+{
+ const tools::Long nWish = pTabFormat->GetFrameSize().GetWidth();
+ OSL_ENSURE(nWish, "weird <= 0 width frmfrm");
+
+ // The value for the left edge of the box is calculated from the
+ // widths of the previous boxes.
+ tools::Long nPos = 0;
+ tools::Long nLeftMin = 0;
+ tools::Long nRightMax = 0;
+ if (nWish != 0) //fdo#33012 0 width frmfmt
+ {
+ SwTwips nSum = 0;
+ const SwTableBox *pCur = pBox;
+ const SwTableLine *pLine = pBox->GetUpper();
+ const tools::Long nAct = rToFill.GetRight() - rToFill.GetLeft(); // +1 why?
+
+ while ( pLine )
+ {
+ const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
+ for ( size_t i = 0; i < rBoxes.size(); ++i )
+ {
+ const SwTwips nWidth = rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth();
+ nSum += nWidth;
+ const tools::Long nTmp = lcl_MulDiv64<tools::Long>(nSum, nAct, nWish);
+
+ if (rBoxes[i] != pCur)
+ {
+ if ( pLine == pBox->GetUpper() || 0 == nLeftMin )
+ nLeftMin = nTmp - nPos;
+ nPos = nTmp;
+ }
+ else
+ {
+ nSum -= nWidth;
+ if ( 0 == nRightMax )
+ nRightMax = nTmp - nPos;
+ break;
+ }
+ }
+ pCur = pLine->GetUpper();
+ pLine = pCur ? pCur->GetUpper() : nullptr;
+ }
+ }
+
+ bool bInsert = !bRefreshHidden;
+ for ( size_t j = 0; bInsert && (j < rToFill.Count()); ++j )
+ {
+ tools::Long nCmp = rToFill[j];
+ if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
+ (nPos <= (nCmp + COLFUZZY)) )
+ {
+ bInsert = false; // Already has it.
+ }
+ else if ( nPos < nCmp )
+ {
+ bInsert = false;
+ rToFill.Insert( nPos, bHidden, j );
+ }
+ }
+ if ( bInsert )
+ rToFill.Insert( nPos, bHidden, rToFill.Count() );
+ else if ( bRefreshHidden )
+ ::lcl_RefreshHidden( rToFill, nPos );
+
+ if ( !bHidden || bRefreshHidden )
+ return;
+
+ // calculate minimum/maximum values for the existing entries:
+ nLeftMin = nPos - nLeftMin;
+ nRightMax = nPos + nRightMax;
+
+ // check if nPos is entry:
+ bool bFoundPos = false;
+ bool bFoundMax = false;
+ for ( size_t j = 0; !(bFoundPos && bFoundMax ) && j < rToFill.Count(); ++j )
+ {
+ SwTabColsEntry& rEntry = rToFill.GetEntry( j );
+ tools::Long nCmp = rToFill[j];
+
+ if ( (nPos >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
+ (nPos <= (nCmp + COLFUZZY)) )
+ {
+ // check if nLeftMin is > old minimum for entry nPos:
+ const tools::Long nOldMin = rEntry.nMin;
+ if ( nLeftMin > nOldMin )
+ rEntry.nMin = nLeftMin;
+ // check if nRightMin is < old maximum for entry nPos:
+ const tools::Long nOldMax = rEntry.nMax;
+ if ( nRightMax < nOldMax )
+ rEntry.nMax = nRightMax;
+
+ bFoundPos = true;
+ }
+ else if ( (nRightMax >= ((nCmp >= COLFUZZY) ? nCmp - COLFUZZY : nCmp)) &&
+ (nRightMax <= (nCmp + COLFUZZY)) )
+ {
+ // check if nPos is > old minimum for entry nRightMax:
+ const tools::Long nOldMin = rEntry.nMin;
+ if ( nPos > nOldMin )
+ rEntry.nMin = nPos;
+
+ bFoundMax = true;
+ }
+ }
+}
+
+static void lcl_ProcessBoxGet( const SwTableBox *pBox, SwTabCols &rToFill,
+ const SwFrameFormat *pTabFormat, bool bRefreshHidden )
+{
+ if ( !pBox->GetTabLines().empty() )
+ {
+ const SwTableLines &rLines = pBox->GetTabLines();
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
+ for ( size_t j = 0; j < rBoxes.size(); ++j )
+ ::lcl_ProcessBoxGet( rBoxes[j], rToFill, pTabFormat, bRefreshHidden);
+ }
+ }
+ else
+ ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, false, bRefreshHidden );
+}
+
+static void lcl_ProcessLineGet( const SwTableLine *pLine, SwTabCols &rToFill,
+ const SwFrameFormat *pTabFormat )
+{
+ for ( size_t i = 0; i < pLine->GetTabBoxes().size(); ++i )
+ {
+ const SwTableBox *pBox = pLine->GetTabBoxes()[i];
+ if ( pBox->GetSttNd() )
+ ::lcl_SortedTabColInsert( rToFill, pBox, pTabFormat, true, false );
+ else
+ for ( size_t j = 0; j < pBox->GetTabLines().size(); ++j )
+ ::lcl_ProcessLineGet( pBox->GetTabLines()[j], rToFill, pTabFormat );
+ }
+}
+
+void SwTable::GetTabCols( SwTabCols &rToFill, const SwTableBox *pStart,
+ bool bRefreshHidden, bool bCurRowOnly ) const
+{
+ // Optimization: if bHidden is set, we only update the Hidden Array.
+ if ( bRefreshHidden )
+ {
+ // remove corrections
+ for ( size_t i = 0; i < rToFill.Count(); ++i )
+ {
+ SwTabColsEntry& rEntry = rToFill.GetEntry( i );
+ rEntry.nPos -= rToFill.GetLeft();
+ rEntry.nMin -= rToFill.GetLeft();
+ rEntry.nMax -= rToFill.GetLeft();
+ }
+
+ // All are hidden, so add the visible ones.
+ for ( size_t i = 0; i < rToFill.Count(); ++i )
+ rToFill.SetHidden( i, true );
+ }
+ else
+ {
+ rToFill.Remove( 0, rToFill.Count() );
+ }
+
+ // Insertion cases:
+ // 1. All boxes which are inferior to Line which is superior to the Start,
+ // as well as their inferior boxes if present.
+ // 2. Starting from the Line, the superior box plus its neighbours; but no inferiors.
+ // 3. Apply 2. to the Line superior to the chain of boxes,
+ // until the Line's superior is not a box but the table.
+ // Only those boxes are inserted that don't contain further rows. The insertion
+ // function takes care to avoid duplicates. In order to achieve this, we work
+ // with some degree of fuzzyness (to avoid rounding errors).
+ // Only the left edge of the boxes are inserted.
+ // Finally, the first entry is removed again, because it's already
+ // covered by the border.
+ // 4. Scan the table again and insert _all_ boxes, this time as hidden.
+
+ const SwFrameFormat *pTabFormat = GetFrameFormat();
+
+ // 1.
+ const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
+
+ for ( size_t i = 0; i < rBoxes.size(); ++i )
+ ::lcl_ProcessBoxGet( rBoxes[i], rToFill, pTabFormat, bRefreshHidden );
+
+ // 2. and 3.
+ const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
+ pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
+ while ( pLine )
+ {
+ const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
+ for ( size_t k = 0; k < rBoxes2.size(); ++k )
+ ::lcl_SortedTabColInsert( rToFill, rBoxes2[k],
+ pTabFormat, false, bRefreshHidden );
+ pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
+ }
+
+ if ( !bRefreshHidden )
+ {
+ // 4.
+ if ( !bCurRowOnly )
+ {
+ for ( size_t i = 0; i < m_aLines.size(); ++i )
+ ::lcl_ProcessLineGet( m_aLines[i], rToFill, pTabFormat );
+ }
+
+ rToFill.Remove( 0 );
+ }
+
+ // Now the coordinates are relative to the left table border - i.e.
+ // relative to SwTabCols.nLeft. However, they are expected
+ // relative to the left document border, i.e. SwTabCols.nLeftMin.
+ // So all values need to be extended by nLeft.
+ for ( size_t i = 0; i < rToFill.Count(); ++i )
+ {
+ SwTabColsEntry& rEntry = rToFill.GetEntry( i );
+ rEntry.nPos += rToFill.GetLeft();
+ rEntry.nMin += rToFill.GetLeft();
+ rEntry.nMax += rToFill.GetLeft();
+ }
+}
+
+// Structure for parameter passing
+struct Parm
+{
+ const SwTabCols &rNew;
+ const SwTabCols &rOld;
+ tools::Long nNewWish,
+ nOldWish;
+ std::deque<SwTableBox*> aBoxArr;
+ SwShareBoxFormats aShareFormats;
+
+ Parm( const SwTabCols &rN, const SwTabCols &rO )
+ : rNew( rN ), rOld( rO ), nNewWish(0), nOldWish(0)
+ {}
+};
+
+static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm );
+
+static void lcl_ProcessLine( SwTableLine *pLine, Parm &rParm )
+{
+ SwTableBoxes &rBoxes = pLine->GetTabBoxes();
+ for ( size_t i = rBoxes.size(); i > 0; )
+ {
+ --i;
+ ::lcl_ProcessBoxSet( rBoxes[i], rParm );
+ }
+}
+
+static void lcl_ProcessBoxSet( SwTableBox *pBox, Parm &rParm )
+{
+ if ( !pBox->GetTabLines().empty() )
+ {
+ SwTableLines &rLines = pBox->GetTabLines();
+ for ( size_t i = rLines.size(); i > 0; )
+ {
+ --i;
+ lcl_ProcessLine( rLines[i], rParm );
+ }
+ }
+ else
+ {
+ // Search the old TabCols for the current position (calculate from
+ // left and right edge). Adjust the box if the values differ from
+ // the new TabCols. If the adjusted edge has no neighbour we also
+ // adjust all superior boxes.
+
+ const tools::Long nOldAct = rParm.rOld.GetRight() -
+ rParm.rOld.GetLeft(); // +1 why?
+
+ // The value for the left edge of the box is calculated from the
+ // widths of the previous boxes plus the left edge.
+ tools::Long nLeft = rParm.rOld.GetLeft();
+ const SwTableBox *pCur = pBox;
+ const SwTableLine *pLine = pBox->GetUpper();
+
+ while ( pLine )
+ {
+ const SwTableBoxes &rBoxes = pLine->GetTabBoxes();
+ for ( size_t i = 0; (i < rBoxes.size()) && (rBoxes[i] != pCur); ++i)
+ {
+ nLeft += lcl_MulDiv64<tools::Long>(
+ rBoxes[i]->GetFrameFormat()->GetFrameSize().GetWidth(),
+ nOldAct, rParm.nOldWish);
+ }
+ pCur = pLine->GetUpper();
+ pLine = pCur ? pCur->GetUpper() : nullptr;
+ }
+ tools::Long nLeftDiff = 0;
+ tools::Long nRightDiff = 0;
+ if ( nLeft != rParm.rOld.GetLeft() ) // There are still boxes before this.
+ {
+ // Right edge is left edge plus width.
+ const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
+ pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
+ nOldAct, rParm.nOldWish);
+ const tools::Long nRight = nLeft + nWidth;
+ size_t nLeftPos = 0;
+ size_t nRightPos = 0;
+ bool bFoundLeftPos = false;
+ bool bFoundRightPos = false;
+ for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
+ {
+ if ( nLeft >= (rParm.rOld[i] - COLFUZZY) &&
+ nLeft <= (rParm.rOld[i] + COLFUZZY) )
+ {
+ nLeftPos = i;
+ bFoundLeftPos = true;
+ }
+ else if ( nRight >= (rParm.rOld[i] - COLFUZZY) &&
+ nRight <= (rParm.rOld[i] + COLFUZZY) )
+ {
+ nRightPos = i;
+ bFoundRightPos = true;
+ }
+ }
+ nLeftDiff = bFoundLeftPos ?
+ rParm.rOld[nLeftPos] - rParm.rNew[nLeftPos] : 0;
+ nRightDiff= bFoundRightPos ?
+ rParm.rNew[nRightPos] - rParm.rOld[nRightPos] : 0;
+ }
+ else // The first box.
+ {
+ nLeftDiff = rParm.rOld.GetLeft() - rParm.rNew.GetLeft();
+ if ( rParm.rOld.Count() )
+ {
+ // Calculate the difference to the edge touching the first box.
+ const tools::Long nWidth = lcl_MulDiv64<tools::Long>(
+ pBox->GetFrameFormat()->GetFrameSize().GetWidth(),
+ nOldAct, rParm.nOldWish);
+ const tools::Long nTmp = nWidth + rParm.rOld.GetLeft();
+ for ( size_t i = 0; i < rParm.rOld.Count(); ++i )
+ {
+ if ( nTmp >= (rParm.rOld[i] - COLFUZZY) &&
+ nTmp <= (rParm.rOld[i] + COLFUZZY) )
+ {
+ nRightDiff = rParm.rNew[i] - rParm.rOld[i];
+ break;
+ }
+ }
+ }
+ }
+
+ if( pBox->getRowSpan() == 1 )
+ {
+ const sal_uInt16 nPos = pBox->GetUpper()->GetBoxPos( pBox );
+ SwTableBoxes& rTableBoxes = pBox->GetUpper()->GetTabBoxes();
+ if( nPos && rTableBoxes[ nPos - 1 ]->getRowSpan() != 1 )
+ nLeftDiff = 0;
+ if( nPos + 1 < o3tl::narrowing<sal_uInt16>(rTableBoxes.size()) &&
+ rTableBoxes[ nPos + 1 ]->getRowSpan() != 1 )
+ nRightDiff = 0;
+ }
+ else
+ nLeftDiff = nRightDiff = 0;
+
+ if ( nLeftDiff || nRightDiff )
+ {
+ // The difference is the actual difference amount. For stretched
+ // tables, it does not make sense to adjust the attributes of the
+ // boxes by this amount. The difference amount needs to be converted
+ // accordingly.
+ tools::Long nTmp = rParm.rNew.GetRight() - rParm.rNew.GetLeft(); // +1 why?
+ nLeftDiff *= rParm.nNewWish;
+ nLeftDiff /= nTmp;
+ nRightDiff *= rParm.nNewWish;
+ nRightDiff /= nTmp;
+ tools::Long nDiff = nLeftDiff + nRightDiff;
+
+ // Adjust the box and all superiors by the difference amount.
+ while ( pBox )
+ {
+ SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
+ aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
+ if ( aFormatFrameSize.GetWidth() < 0 )
+ aFormatFrameSize.SetWidth( -aFormatFrameSize.GetWidth() );
+ rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
+
+ // The outer cells of the last row are responsible to adjust a surrounding cell.
+ // Last line check:
+ if ( pBox->GetUpper()->GetUpper() &&
+ pBox->GetUpper() != pBox->GetUpper()->GetUpper()->GetTabLines().back())
+ {
+ pBox = nullptr;
+ }
+ else
+ {
+ // Middle cell check:
+ if ( pBox != pBox->GetUpper()->GetTabBoxes().front() )
+ nDiff = nRightDiff;
+
+ if ( pBox != pBox->GetUpper()->GetTabBoxes().back() )
+ nDiff -= nRightDiff;
+
+ pBox = nDiff ? pBox->GetUpper()->GetUpper() : nullptr;
+ }
+ }
+ }
+ }
+}
+
+static void lcl_ProcessBoxPtr( SwTableBox *pBox, std::deque<SwTableBox*> &rBoxArr,
+ bool bBefore )
+{
+ if ( !pBox->GetTabLines().empty() )
+ {
+ const SwTableLines &rLines = pBox->GetTabLines();
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ const SwTableBoxes &rBoxes = rLines[i]->GetTabBoxes();
+ for ( size_t j = 0; j < rBoxes.size(); ++j )
+ ::lcl_ProcessBoxPtr( rBoxes[j], rBoxArr, bBefore );
+ }
+ }
+ else if ( bBefore )
+ rBoxArr.push_front( pBox );
+ else
+ rBoxArr.push_back( pBox );
+}
+
+static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm );
+
+static void lcl_AdjustLines( SwTableLines &rLines, const tools::Long nDiff, Parm &rParm )
+{
+ for ( size_t i = 0; i < rLines.size(); ++i )
+ {
+ SwTableBox *pBox = rLines[i]->GetTabBoxes()
+ [rLines[i]->GetTabBoxes().size()-1];
+ lcl_AdjustBox( pBox, nDiff, rParm );
+ }
+}
+
+static void lcl_AdjustBox( SwTableBox *pBox, const tools::Long nDiff, Parm &rParm )
+{
+ if ( !pBox->GetTabLines().empty() )
+ ::lcl_AdjustLines( pBox->GetTabLines(), nDiff, rParm );
+
+ // Adjust the size of the box.
+ SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
+ aFormatFrameSize.SetWidth( aFormatFrameSize.GetWidth() + nDiff );
+
+ rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
+}
+
+void SwTable::SetTabCols( const SwTabCols &rNew, const SwTabCols &rOld,
+ const SwTableBox *pStart, bool bCurRowOnly )
+{
+ CHECK_TABLE( *this )
+
+ SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>()); // delete HTML-Layout
+
+ // FME: Made rOld const. The caller is responsible for passing correct
+ // values of rOld. Therefore we do not have to call GetTabCols anymore:
+ //GetTabCols( rOld, pStart );
+
+ Parm aParm( rNew, rOld );
+
+ OSL_ENSURE( rOld.Count() == rNew.Count(), "Number of columns changed.");
+
+ // Convert the edges. We need to adjust the size of the table and some boxes.
+ // For the size adjustment, we must not make use of the Modify, since that'd
+ // adjust all boxes, which we really don't want.
+ SwFrameFormat *pFormat = GetFrameFormat();
+ aParm.nOldWish = aParm.nNewWish = pFormat->GetFrameSize().GetWidth();
+ if ( (rOld.GetLeft() != rNew.GetLeft()) ||
+ (rOld.GetRight()!= rNew.GetRight()) )
+ {
+ LockModify();
+ {
+ SvxLRSpaceItem aLR( pFormat->GetLRSpace() );
+ SvxShadowItem aSh( pFormat->GetShadow() );
+
+ SwTwips nShRight = aSh.CalcShadowSpace( SvxShadowItemSide::RIGHT );
+ SwTwips nShLeft = aSh.CalcShadowSpace( SvxShadowItemSide::LEFT );
+
+ aLR.SetLeft ( rNew.GetLeft() - nShLeft );
+ aLR.SetRight( rNew.GetRightMax() - rNew.GetRight() - nShRight );
+ pFormat->SetFormatAttr( aLR );
+
+ // The alignment of the table needs to be adjusted accordingly.
+ // This is done by preserving the exact positions that have been
+ // set by the user.
+ SwFormatHoriOrient aOri( pFormat->GetHoriOrient() );
+ if( text::HoriOrientation::NONE != aOri.GetHoriOrient() &&
+ text::HoriOrientation::CENTER != aOri.GetHoriOrient() )
+ {
+ const bool bLeftDist = rNew.GetLeft() != nShLeft;
+ const bool bRightDist = rNew.GetRight() + nShRight != rNew.GetRightMax();
+ if(!bLeftDist && !bRightDist)
+ aOri.SetHoriOrient( text::HoriOrientation::FULL );
+ else if(!bRightDist && rNew.GetLeft() > nShLeft )
+ aOri.SetHoriOrient( text::HoriOrientation::RIGHT );
+ else if(!bLeftDist && rNew.GetRight() + nShRight < rNew.GetRightMax())
+ aOri.SetHoriOrient( text::HoriOrientation::LEFT );
+ else
+ {
+ // if an automatic table hasn't (really) changed size, then leave it as auto.
+ const tools::Long nOldWidth = rOld.GetRight() - rOld.GetLeft();
+ const tools::Long nNewWidth = rNew.GetRight() - rNew.GetLeft();
+ if (aOri.GetHoriOrient() != text::HoriOrientation::FULL
+ || std::abs(nOldWidth - nNewWidth) > COLFUZZY)
+ {
+ aOri.SetHoriOrient(text::HoriOrientation::LEFT_AND_WIDTH);
+ }
+ }
+ }
+ pFormat->SetFormatAttr( aOri );
+ }
+ const tools::Long nAct = rOld.GetRight() - rOld.GetLeft(); // +1 why?
+ tools::Long nTabDiff = 0;
+
+ if ( rOld.GetLeft() != rNew.GetLeft() )
+ {
+ nTabDiff = rOld.GetLeft() - rNew.GetLeft();
+ nTabDiff *= aParm.nOldWish;
+ nTabDiff /= nAct;
+ }
+ if ( rOld.GetRight() != rNew.GetRight() )
+ {
+ tools::Long nDiff = rNew.GetRight() - rOld.GetRight();
+ nDiff *= aParm.nOldWish;
+ nDiff /= nAct;
+ nTabDiff += nDiff;
+ if( !IsNewModel() )
+ ::lcl_AdjustLines( GetTabLines(), nDiff, aParm );
+ }
+
+ // Adjust the size of the table, watch out for stretched tables.
+ if ( nTabDiff )
+ {
+ aParm.nNewWish += nTabDiff;
+ if ( aParm.nNewWish < 0 )
+ aParm.nNewWish = USHRT_MAX; // Oops! Have to roll back.
+ SwFormatFrameSize aSz( pFormat->GetFrameSize() );
+ if ( aSz.GetWidth() != aParm.nNewWish )
+ {
+ aSz.SetWidth( aParm.nNewWish );
+ aSz.SetWidthPercent( 0 );
+ pFormat->SetFormatAttr( aSz );
+ }
+ }
+ UnlockModify();
+ }
+
+ if( IsNewModel() )
+ NewSetTabCols( aParm, rNew, rOld, pStart, bCurRowOnly );
+ else
+ {
+ if ( bCurRowOnly )
+ {
+ // To adjust the current row, we need to process all its boxes,
+ // similar to the filling of the TabCols (see GetTabCols()).
+ // Unfortunately we again have to take care to adjust the boxes
+ // from back to front, respectively from outer to inner.
+ // The best way to achieve this is probably to track the boxes
+ // in a PtrArray.
+ const SwTableBoxes &rBoxes = pStart->GetUpper()->GetTabBoxes();
+ for ( size_t i = 0; i < rBoxes.size(); ++i )
+ ::lcl_ProcessBoxPtr( rBoxes[i], aParm.aBoxArr, false );
+
+ const SwTableLine *pLine = pStart->GetUpper()->GetUpper() ?
+ pStart->GetUpper()->GetUpper()->GetUpper() : nullptr;
+ const SwTableBox *pExcl = pStart->GetUpper()->GetUpper();
+ while ( pLine )
+ {
+ const SwTableBoxes &rBoxes2 = pLine->GetTabBoxes();
+ bool bBefore = true;
+ for ( size_t i = 0; i < rBoxes2.size(); ++i )
+ {
+ if ( rBoxes2[i] != pExcl )
+ ::lcl_ProcessBoxPtr( rBoxes2[i], aParm.aBoxArr, bBefore );
+ else
+ bBefore = false;
+ }
+ pExcl = pLine->GetUpper();
+ pLine = pLine->GetUpper() ? pLine->GetUpper()->GetUpper() : nullptr;
+ }
+ // After we've inserted a bunch of boxes (hopefully all and in
+ // correct order), we just need to process them in reverse order.
+ for ( int j = aParm.aBoxArr.size()-1; j >= 0; --j )
+ {
+ SwTableBox *pBox = aParm.aBoxArr[j];
+ ::lcl_ProcessBoxSet( pBox, aParm );
+ }
+ }
+ else
+ {
+ // Adjusting the entire table is 'easy'. All boxes without lines are
+ // adjusted, as are their superiors. Of course we need to process
+ // in reverse order to prevent fooling ourselves!
+ SwTableLines &rLines = GetTabLines();
+ for ( size_t i = rLines.size(); i > 0; )
+ {
+ --i;
+ ::lcl_ProcessLine( rLines[i], aParm );
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ {
+ // do some checking for correct table widths
+ SwTwips nSize = GetFrameFormat()->GetFrameSize().GetWidth();
+ for (size_t n = 0; n < m_aLines.size(); ++n)
+ {
+ CheckBoxWidth( *m_aLines[ n ], nSize );
+ }
+ }
+#endif
+}
+
+typedef std::pair<sal_uInt16, sal_uInt16> ColChange;
+typedef std::list< ColChange > ChangeList;
+
+static void lcl_AdjustWidthsInLine( SwTableLine* pLine, ChangeList& rOldNew,
+ Parm& rParm, sal_uInt16 nColFuzzy )
+{
+ ChangeList::iterator pCurr = rOldNew.begin();
+ if( pCurr == rOldNew.end() )
+ return;
+ const size_t nCount = pLine->GetTabBoxes().size();
+ SwTwips nBorder = 0;
+ SwTwips nRest = 0;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[i];
+ SwTwips nWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ SwTwips nNewWidth = nWidth - nRest;
+ nRest = 0;
+ nBorder += nWidth;
+ if( pCurr != rOldNew.end() && nBorder + nColFuzzy >= pCurr->first )
+ {
+ nBorder -= nColFuzzy;
+ while( pCurr != rOldNew.end() && nBorder > pCurr->first )
+ ++pCurr;
+ if( pCurr != rOldNew.end() )
+ {
+ nBorder += nColFuzzy;
+ if( nBorder + nColFuzzy >= pCurr->first )
+ {
+ if( pCurr->second == pCurr->first )
+ nRest = 0;
+ else
+ nRest = pCurr->second - nBorder;
+ nNewWidth += nRest;
+ ++pCurr;
+ }
+ }
+ }
+ if( nNewWidth != nWidth )
+ {
+ if( nNewWidth < 0 )
+ {
+ nRest += 1 - nNewWidth;
+ nNewWidth = 1;
+ }
+ SwFormatFrameSize aFormatFrameSize( pBox->GetFrameFormat()->GetFrameSize() );
+ aFormatFrameSize.SetWidth( nNewWidth );
+ rParm.aShareFormats.SetSize( *pBox, aFormatFrameSize );
+ }
+ }
+}
+
+static void lcl_CalcNewWidths( std::vector<sal_uInt16> &rSpanPos, ChangeList& rChanges,
+ SwTableLine* pLine, tools::Long nWish, tools::Long nWidth, bool bTop )
+{
+ if( rChanges.empty() )
+ {
+ rSpanPos.clear();
+ return;
+ }
+ if( rSpanPos.empty() )
+ {
+ rChanges.clear();
+ return;
+ }
+ std::vector<sal_uInt16> aNewSpanPos;
+ ChangeList::iterator pCurr = rChanges.begin();
+ ChangeList aNewChanges { *pCurr }; // Nullposition
+ std::vector<sal_uInt16>::iterator pSpan = rSpanPos.begin();
+ sal_uInt16 nCurr = 0;
+ SwTwips nOrgSum = 0;
+ bool bRowSpan = false;
+ sal_uInt16 nRowSpanCount = 0;
+ const size_t nCount = pLine->GetTabBoxes().size();
+ for( size_t nCurrBox = 0; nCurrBox < nCount; ++nCurrBox )
+ {
+ SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox];
+ SwTwips nCurrWidth = pBox->GetFrameFormat()->GetFrameSize().GetWidth();
+ const sal_Int32 nRowSpan = pBox->getRowSpan();
+ const bool bCurrRowSpan = bTop ? nRowSpan < 0 :
+ ( nRowSpan > 1 || nRowSpan < -1 );
+ if( bRowSpan || bCurrRowSpan )
+ aNewSpanPos.push_back( nRowSpanCount );
+ bRowSpan = bCurrRowSpan;
+ nOrgSum += nCurrWidth;
+ const sal_uInt16 nPos = lcl_MulDiv64<sal_uInt16>(
+ lcl_MulDiv64<sal_uInt64>(nOrgSum, nWidth, nWish),
+ nWish, nWidth);
+ while( pCurr != rChanges.end() && pCurr->first < nPos )
+ {
+ ++nCurr;
+ ++pCurr;
+ }
+ bool bNew = true;
+ if( pCurr != rChanges.end() && pCurr->first <= nPos &&
+ pCurr->first != pCurr->second )
+ {
+ pSpan = std::find_if(pSpan, rSpanPos.end(),
+ [nCurr](const sal_uInt16 nSpan) { return nSpan >= nCurr; });
+ if( pSpan != rSpanPos.end() && *pSpan == nCurr )
+ {
+ aNewChanges.push_back( *pCurr );
+ ++nRowSpanCount;
+ bNew = false;
+ }
+ }
+ if( bNew )
+ {
+ ColChange aTmp( nPos, nPos );
+ aNewChanges.push_back( aTmp );
+ ++nRowSpanCount;
+ }
+ }
+
+ pCurr = aNewChanges.begin();
+ ChangeList::iterator pLast = pCurr;
+ ChangeList::iterator pLeftMove = pCurr;
+ while( pCurr != aNewChanges.end() )
+ {
+ if( pLeftMove == pCurr )
+ {
+ while( ++pLeftMove != aNewChanges.end() && pLeftMove->first <= pLeftMove->second )
+ ;
+ }
+ if( pCurr->second == pCurr->first )
+ {
+ if( pLeftMove != aNewChanges.end() && pCurr->second > pLeftMove->second )
+ {
+ if( pLeftMove->first == pLast->first )
+ pCurr->second = pLeftMove->second;
+ else
+ {
+ pCurr->second = lcl_MulDiv64<sal_uInt16>(
+ pCurr->first - pLast->first,
+ pLeftMove->second - pLast->second,
+ pLeftMove->first - pLast->first) + pLast->second;
+ }
+ }
+ pLast = pCurr;
+ ++pCurr;
+ }
+ else if( pCurr->second > pCurr->first )
+ {
+ pLast = pCurr;
+ ++pCurr;
+ ChangeList::iterator pNext = pCurr;
+ while( pNext != pLeftMove && pNext->second == pNext->first &&
+ pNext->second < pLast->second )
+ ++pNext;
+ while( pCurr != pNext )
+ {
+ if( pNext == aNewChanges.end() || pNext->first == pLast->first )
+ pCurr->second = pLast->second;
+ else
+ {
+ pCurr->second = lcl_MulDiv64<sal_uInt16>(
+ pCurr->first - pLast->first,
+ pNext->second - pLast->second,
+ pNext->first - pLast->first) + pLast->second;
+ }
+ ++pCurr;
+ }
+ pLast = pCurr;
+ }
+ else
+ {
+ pLast = pCurr;
+ ++pCurr;
+ }
+ }
+
+ rChanges.swap(aNewChanges);
+ rSpanPos.swap(aNewSpanPos);
+}
+
+void SwTable::NewSetTabCols( Parm &rParm, const SwTabCols &rNew,
+ const SwTabCols &rOld, const SwTableBox *pStart, bool bCurRowOnly )
+{
+#if OSL_DEBUG_LEVEL > 1
+ static int nCallCount = 0;
+ ++nCallCount;
+#endif
+ // First step: evaluate which lines have been moved/which widths changed
+ ChangeList aOldNew;
+ const tools::Long nNewWidth = rParm.rNew.GetRight() - rParm.rNew.GetLeft();
+ const tools::Long nOldWidth = rParm.rOld.GetRight() - rParm.rOld.GetLeft();
+ if( nNewWidth < 1 || nOldWidth < 1 )
+ return;
+ for( size_t i = 0; i <= rOld.Count(); ++i )
+ {
+ tools::Long nNewPos;
+ tools::Long nOldPos;
+ if( i == rOld.Count() )
+ {
+ nOldPos = rParm.rOld.GetRight() - rParm.rOld.GetLeft();
+ nNewPos = rParm.rNew.GetRight() - rParm.rNew.GetLeft();
+ }
+ else
+ {
+ nOldPos = rOld[i] - rParm.rOld.GetLeft();
+ nNewPos = rNew[i] - rParm.rNew.GetLeft();
+ }
+ nNewPos = lcl_MulDiv64<tools::Long>(nNewPos, rParm.nNewWish, nNewWidth);
+ nOldPos = lcl_MulDiv64<tools::Long>(nOldPos, rParm.nOldWish, nOldWidth);
+ if( nOldPos != nNewPos && nNewPos > 0 && nOldPos > 0 )
+ {
+ ColChange aChg( o3tl::narrowing<sal_uInt16>(nOldPos), o3tl::narrowing<sal_uInt16>(nNewPos) );
+ aOldNew.push_back( aChg );
+ }
+ }
+ // Finished first step
+ int nCount = aOldNew.size();
+ if( !nCount )
+ return; // no change, nothing to do
+ SwTableLines &rLines = GetTabLines();
+ if( bCurRowOnly )
+ {
+ const SwTableLine* pCurrLine = pStart->GetUpper();
+ sal_uInt16 nCurr = rLines.GetPos( pCurrLine );
+ if( nCurr >= USHRT_MAX )
+ return;
+
+ ColChange aChg( 0, 0 );
+ aOldNew.push_front( aChg );
+ std::vector<sal_uInt16> aRowSpanPos;
+ if( nCurr )
+ {
+ ChangeList aCopy;
+ sal_uInt16 nPos = 0;
+ for( const auto& rCop : aOldNew )
+ {
+ aCopy.push_back( rCop );
+ aRowSpanPos.push_back( nPos++ );
+ }
+ lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
+ rParm.nOldWish, nOldWidth, true );
+ bool bGoOn = !aRowSpanPos.empty();
+ sal_uInt16 j = nCurr;
+ while( bGoOn )
+ {
+ lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[--j],
+ rParm.nOldWish, nOldWidth, true );
+ lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
+ bGoOn = !aRowSpanPos.empty() && j > 0;
+ }
+ aRowSpanPos.clear();
+ }
+ if( nCurr+1 < o3tl::narrowing<sal_uInt16>(rLines.size()) )
+ {
+ ChangeList aCopy;
+ sal_uInt16 nPos = 0;
+ for( const auto& rCop : aOldNew )
+ {
+ aCopy.push_back( rCop );
+ aRowSpanPos.push_back( nPos++ );
+ }
+ lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[nCurr],
+ rParm.nOldWish, nOldWidth, false );
+ bool bGoOn = !aRowSpanPos.empty();
+ sal_uInt16 j = nCurr;
+ while( bGoOn )
+ {
+ lcl_CalcNewWidths( aRowSpanPos, aCopy, rLines[++j],
+ rParm.nOldWish, nOldWidth, false );
+ lcl_AdjustWidthsInLine( rLines[j], aCopy, rParm, 0 );
+ bGoOn = !aRowSpanPos.empty() && j+1 < o3tl::narrowing<sal_uInt16>(rLines.size());
+ }
+ }
+ ::lcl_AdjustWidthsInLine( rLines[nCurr], aOldNew, rParm, COLFUZZY );
+ }
+ else
+ {
+ for( size_t i = 0; i < rLines.size(); ++i )
+ ::lcl_AdjustWidthsInLine( rLines[i], aOldNew, rParm, COLFUZZY );
+ }
+ CHECK_TABLE( *this )
+}
+
+// return the pointer of the box specified.
+static bool lcl_IsValidRowName( std::u16string_view rStr )
+{
+ bool bIsValid = true;
+ size_t nLen = rStr.size();
+ for( size_t i = 0; i < nLen && bIsValid; ++i )
+ {
+ const sal_Unicode cChar = rStr[i];
+ if (cChar < '0' || cChar > '9')
+ bIsValid = false;
+ }
+ return bIsValid;
+}
+
+// #i80314#
+// add 3rd parameter and its handling
+sal_uInt16 SwTable::GetBoxNum( OUString& rStr, bool bFirstPart,
+ const bool bPerformValidCheck )
+{
+ sal_uInt16 nRet = 0;
+ if( bFirstPart ) // true == column; false == row
+ {
+ sal_Int32 nPos = 0;
+ // the first one uses letters for addressing!
+ bool bFirst = true;
+ sal_uInt32 num = 0;
+ bool overflow = false;
+ while (nPos<rStr.getLength())
+ {
+ sal_Unicode cChar = rStr[nPos];
+ if ((cChar<'A' || cChar>'Z') && (cChar<'a' || cChar>'z'))
+ break;
+ cChar -= 'A';
+ if( cChar >= 26 )
+ cChar -= 'a' - '[';
+ if( bFirst )
+ bFirst = false;
+ else
+ ++num;
+ num = num * 52 + cChar;
+ if (num > SAL_MAX_UINT16) {
+ overflow = true;
+ }
+ ++nPos;
+ }
+ nRet = overflow ? SAL_MAX_UINT16 : num;
+ rStr = rStr.copy( nPos ); // Remove char from String
+ }
+ else
+ {
+ const sal_Int32 nPos = rStr.indexOf( "." );
+ if ( nPos<0 )
+ {
+ nRet = 0;
+ if ( !bPerformValidCheck || lcl_IsValidRowName( rStr ) )
+ {
+ nRet = o3tl::narrowing<sal_uInt16>(rStr.toInt32());
+ }
+ rStr.clear();
+ }
+ else
+ {
+ nRet = 0;
+ const std::u16string_view aText( rStr.subView( 0, nPos ) );
+ if ( !bPerformValidCheck || lcl_IsValidRowName( aText ) )
+ {
+ nRet = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(aText));
+ }
+ rStr = rStr.copy( nPos+1 );
+ }
+ }
+ return nRet;
+}
+
+// #i80314#
+// add 2nd parameter and its handling
+const SwTableBox* SwTable::GetTableBox( const OUString& rName,
+ const bool bPerformValidCheck ) const
+{
+ const SwTableBox* pBox = nullptr;
+ const SwTableLine* pLine;
+ const SwTableLines* pLines;
+
+ sal_uInt16 nLine, nBox;
+ OUString aNm( rName );
+ while( !aNm.isEmpty() )
+ {
+ nBox = SwTable::GetBoxNum( aNm, nullptr == pBox, bPerformValidCheck );
+ // first box ?
+ if( !pBox )
+ pLines = &GetTabLines();
+ else
+ {
+ pLines = &pBox->GetTabLines();
+ if( nBox )
+ --nBox;
+ }
+
+ nLine = SwTable::GetBoxNum( aNm, false, bPerformValidCheck );
+
+ // determine line
+ if( !nLine || nLine > pLines->size() )
+ return nullptr;
+ pLine = (*pLines)[ nLine-1 ];
+
+ // determine box
+ const SwTableBoxes* pBoxes = &pLine->GetTabBoxes();
+ if( nBox >= pBoxes->size() )
+ return nullptr;
+ pBox = (*pBoxes)[ nBox ];
+ }
+
+ // check if the box found has any contents
+ if( pBox && !pBox->GetSttNd() )
+ {
+ OSL_FAIL( "Box without content, looking for the next one!" );
+ // "drop this" until the first box
+ while( !pBox->GetTabLines().empty() )
+ pBox = pBox->GetTabLines().front()->GetTabBoxes().front();
+ }
+ return pBox;
+}
+
+SwTableBox* SwTable::GetTableBox( SwNodeOffset nSttIdx )
+{
+ // For optimizations, don't always process the entire SortArray.
+ // Converting text to table, tries certain conditions
+ // to ask for a table box of a table that is not yet having a format
+ if(!GetFrameFormat())
+ return nullptr;
+ SwTableBox* pRet = nullptr;
+ SwNodes& rNds = GetFrameFormat()->GetDoc()->GetNodes();
+ SwNodeOffset nIndex = nSttIdx + 1;
+ SwContentNode* pCNd = nullptr;
+ SwTableNode* pTableNd = nullptr;
+
+ while ( nIndex < rNds.Count() )
+ {
+ pTableNd = rNds[ nIndex ]->GetTableNode();
+ if ( pTableNd )
+ break;
+
+ pCNd = rNds[ nIndex ]->GetContentNode();
+ if ( pCNd )
+ break;
+
+ ++nIndex;
+ }
+
+ if ( pCNd || pTableNd )
+ {
+ sw::BroadcastingModify* pModify = pCNd;
+ // #144862# Better handling of table in table
+ if ( pTableNd && pTableNd->GetTable().GetFrameFormat() )
+ pModify = pTableNd->GetTable().GetFrameFormat();
+
+ SwFrame* pFrame = pModify ? SwIterator<SwFrame,sw::BroadcastingModify>(*pModify).First() : nullptr;
+ while ( pFrame && !pFrame->IsCellFrame() )
+ pFrame = pFrame->GetUpper();
+ if ( pFrame )
+ pRet = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
+ }
+
+ // In case the layout doesn't exist yet or anything else goes wrong.
+ if ( !pRet )
+ {
+ for (size_t n = m_TabSortContentBoxes.size(); n; )
+ {
+ if (m_TabSortContentBoxes[ --n ]->GetSttIdx() == nSttIdx)
+ {
+ return m_TabSortContentBoxes[ n ];
+ }
+ }
+ }
+ return pRet;
+}
+
+bool SwTable::IsTableComplex() const
+{
+ // Returns true for complex tables, i.e. tables that contain nestings,
+ // like containing boxes not part of the first line, e.g. results of
+ // splits/merges which lead to more complex structures.
+ for (size_t n = 0; n < m_TabSortContentBoxes.size(); ++n)
+ {
+ if (m_TabSortContentBoxes[ n ]->GetUpper()->GetUpper())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+SwTableLine::SwTableLine( SwTableLineFormat *pFormat, sal_uInt16 nBoxes,
+ SwTableBox *pUp )
+ : SwClient( pFormat )
+ , m_pUpper( pUp )
+ , m_eRedlineType( RedlineType::None )
+{
+ m_aBoxes.reserve( nBoxes );
+}
+
+SwTableLine::~SwTableLine()
+{
+ for (size_t i = 0; i < m_aBoxes.size(); ++i)
+ {
+ delete m_aBoxes[i];
+ }
+ // the TabelleLine can be deleted if it's the last client of the FrameFormat
+ sw::BroadcastingModify* pMod = GetFrameFormat();
+ pMod->Remove( this ); // remove,
+ if( !pMod->HasWriterListeners() )
+ delete pMod; // and delete
+}
+
+SwFrameFormat* SwTableLine::ClaimFrameFormat()
+{
+ // This method makes sure that this object is an exclusive SwTableLine client
+ // of an SwTableLineFormat object
+ // If other SwTableLine objects currently listen to the same SwTableLineFormat as
+ // this one, something needs to be done
+ SwTableLineFormat *pRet = static_cast<SwTableLineFormat*>(GetFrameFormat());
+ SwIterator<SwTableLine,SwFormat> aIter( *pRet );
+ for( SwTableLine* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if ( pLast != this )
+ {
+ // found another SwTableLine that is a client of the current Format
+ // create a new Format as a copy and use it for this object
+ SwTableLineFormat *pNewFormat = pRet->GetDoc()->MakeTableLineFormat();
+ *pNewFormat = *pRet;
+
+ // register SwRowFrames that know me as clients at the new Format
+ SwIterator<SwRowFrame,SwFormat> aFrameIter( *pRet );
+ for( SwRowFrame* pFrame = aFrameIter.First(); pFrame; pFrame = aFrameIter.Next() )
+ if( pFrame->GetTabLine() == this )
+ pFrame->RegisterToFormat( *pNewFormat );
+
+ // register myself
+ pNewFormat->Add( this );
+ pRet = pNewFormat;
+ break;
+ }
+ }
+
+ return pRet;
+}
+
+void SwTableLine::ChgFrameFormat(SwTableLineFormat* pNewFormat)
+{
+ auto pOld = GetFrameFormat();
+ pOld->CallSwClientNotify(sw::TableLineFormatChanged(*pNewFormat, *this));
+ // Now, re-register self.
+ pNewFormat->Add(this);
+ if(!pOld->HasWriterListeners())
+ delete pOld;
+}
+
+SwTwips SwTableLine::GetTableLineHeight( bool& bLayoutAvailable ) const
+{
+ SwTwips nRet = 0;
+ bLayoutAvailable = false;
+ SwIterator<SwRowFrame,SwFormat> aIter( *GetFrameFormat() );
+ // A row could appear several times in headers/footers so only one chain of master/follow tables
+ // will be accepted...
+ const SwTabFrame* pChain = nullptr; // My chain
+ for( SwRowFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->GetTabLine() == this )
+ {
+ const SwTabFrame* pTab = pLast->FindTabFrame();
+ bLayoutAvailable = ( pTab && pTab->IsVertical() ) ?
+ ( 0 < pTab->getFrameArea().Height() ) :
+ ( 0 < pTab->getFrameArea().Width() );
+
+ // The first one defines the chain, if a chain is defined, only members of the chain
+ // will be added.
+ if (pTab && (!pChain || pChain->IsAnFollow( pTab ) || pTab->IsAnFollow(pChain)))
+ {
+ pChain = pTab; // defines my chain (even it is already)
+ if( pTab->IsVertical() )
+ nRet += pLast->getFrameArea().Width();
+ else
+ nRet += pLast->getFrameArea().Height();
+ // Optimization, if there are no master/follows in my chain, nothing more to add
+ if( !pTab->HasFollow() && !pTab->IsFollow() )
+ break;
+ // This is not an optimization, this is necessary to avoid double additions of
+ // repeating rows
+ if( pTab->IsInHeadline(*pLast) )
+ break;
+ }
+ }
+ }
+ return nRet;
+}
+
+bool SwTableLine::IsEmpty() const
+{
+ for (size_t i = 0; i < m_aBoxes.size(); ++i)
+ {
+ if ( !m_aBoxes[i]->IsEmpty() )
+ return false;
+ }
+ return true;
+}
+
+bool SwTable::HasDeletedRow() const
+{
+ const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ if ( aRedlineTable.empty() )
+ return false;
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for (size_t i = 0; i < m_aLines.size(); ++i)
+ {
+ if ( m_aLines[i]->IsDeleted(nRedlinePos) )
+ return true;
+ }
+ return false;
+}
+
+bool SwTable::IsDeleted() const
+{
+ const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ if ( aRedlineTable.empty() )
+ return false;
+
+ SwRedlineTable::size_type nRedlinePos = 0;
+ for (size_t i = 0; i < m_aLines.size(); ++i)
+ {
+ if ( !m_aLines[i]->IsDeleted(nRedlinePos) )
+ return false;
+ }
+ return true;
+}
+
+// TODO Set HasTextChangesOnly=true, if needed based on the redlines in the cells.
+// At tracked row deletion, return with the newest deletion of the row or
+// at tracked row insertion, return with the oldest insertion in the row, which
+// contain the change data of the row change.
+// If the return value is SwRedlineTable::npos, there is no tracked row change.
+SwRedlineTable::size_type SwTableLine::UpdateTextChangesOnly(
+ SwRedlineTable::size_type& rRedlinePos, bool bUpdateProperty ) const
+{
+ SwRedlineTable::size_type nRet = SwRedlineTable::npos;
+ const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+
+ // check table row property "HasTextChangesOnly", if it's defined and its
+ // value is false, and all text content is in delete redlines, the row is deleted
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
+ {
+ const SwTableBoxes & rBoxes = GetTabBoxes();
+ size_t nBoxes = rBoxes.size();
+ bool bInsertion = false;
+ bool bPlainTextInLine = false;
+ SwRedlineTable::size_type nOldestRedline = SwRedlineTable::npos;
+ SwRedlineTable::size_type nNewestRedline = SwRedlineTable::npos;
+ for (size_t nBoxIndex = 0; nBoxIndex < nBoxes && rRedlinePos < aRedlineTable.size(); ++nBoxIndex)
+ {
+ auto pBox = rBoxes[nBoxIndex];
+ if ( pBox->IsEmpty() )
+ {
+ // no text content, check the next cells
+ continue;
+ }
+
+ bool bHasRedlineInBox = false;
+ SwPosition aCellStart( SwNodeIndex( *pBox->GetSttNd(), 0 ) );
+ SwPosition aCellEnd( SwNodeIndex( *pBox->GetSttNd()->EndOfSectionNode(), -1 ) );
+ SwNodeIndex pEndNodeIndex(aCellEnd.nNode.GetNode());
+ SwRangeRedline* pPreviousDeleteRedline = nullptr;
+ for( ; rRedlinePos < aRedlineTable.size(); ++rRedlinePos )
+ {
+ const SwRangeRedline* pRedline = aRedlineTable[ rRedlinePos ];
+
+ if ( pRedline->Start()->nNode > pEndNodeIndex )
+ {
+ // no more redlines in the actual cell,
+ // check the next ones
+ break;
+ }
+
+ // redline in the cell
+ if ( aCellStart <= *pRedline->Start() )
+ {
+ if ( !bHasRedlineInBox )
+ {
+ bHasRedlineInBox = true;
+ // plain text before the first redline in the text
+ if ( pRedline->Start()->nContent.GetIndex() > 0 )
+ bPlainTextInLine = true;
+ }
+
+ RedlineType nType = pRedline->GetType();
+
+ // first insert redline
+ if ( !bInsertion )
+ {
+ if ( RedlineType::Insert == nType )
+ {
+ bInsertion = true;
+ }
+ else
+ {
+ // plain text between the delete redlines
+ if ( pPreviousDeleteRedline &&
+ *pPreviousDeleteRedline->End() < *pRedline->Start() )
+ {
+ bPlainTextInLine = true;
+ }
+ pPreviousDeleteRedline = const_cast<SwRangeRedline*>(pRedline);
+ }
+ }
+
+ // search newest and oldest redlines
+ if ( nNewestRedline == SwRedlineTable::npos ||
+ aRedlineTable[nNewestRedline]->GetRedlineData().GetTimeStamp() <
+ pRedline->GetRedlineData().GetTimeStamp() )
+ {
+ nNewestRedline = rRedlinePos;
+ }
+ if ( nOldestRedline == SwRedlineTable::npos ||
+ aRedlineTable[nOldestRedline]->GetRedlineData().GetTimeStamp() >
+ pRedline->GetRedlineData().GetTimeStamp() )
+ {
+ nOldestRedline = rRedlinePos;
+ }
+ }
+ }
+
+ // there is text content outside of redlines: not a deletion
+ if ( !bInsertion && ( !bHasRedlineInBox || ( pPreviousDeleteRedline &&
+ ( pPreviousDeleteRedline->End()->nNode < aCellEnd.nNode ||
+ pPreviousDeleteRedline->End()->nContent.GetIndex() <
+ aCellEnd.nNode.GetNode().GetContentNode()->Len() ) ) ) )
+ {
+ bPlainTextInLine = true;
+ // not deleted cell content: the row is not empty
+ // maybe insertion of a row, try to search it
+ bInsertion = true;
+ }
+ }
+
+ // choose return redline, if it exists or remove changed row attribute
+ if ( bInsertion && SwRedlineTable::npos != nOldestRedline &&
+ RedlineType::Insert == aRedlineTable[ nOldestRedline ]->GetType() )
+ {
+ // there is an insert redline, which is the oldest redline in the row
+ nRet = nOldestRedline;
+ }
+ else if ( !bInsertion && !bPlainTextInLine && SwRedlineTable::npos != nNewestRedline &&
+ RedlineType::Delete == aRedlineTable[ nNewestRedline ]->GetType() )
+ {
+ // there is a delete redline, which is the newest redline in the row,
+ // and no text outside of redlines, and no insert redline in the row,
+ // i.e. whole text content is deleted
+ nRet = nNewestRedline;
+ }
+ else
+ {
+ // no longer tracked row insertion or deletion
+ nRet = SwRedlineTable::npos;
+ // set TextChangesOnly = true to remove the tracked deletion
+ // FIXME Undo is not supported here (this is only a fallback,
+ // because using SetRowNotTracked() is not recommended here)
+ if ( bUpdateProperty )
+ {
+ SvxPrintItem aUnsetTracking(RES_PRINT, true);
+ SwFrameFormat *pFormat = const_cast<SwTableLine*>(this)->ClaimFrameFormat();
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aUnsetTracking );
+ pFormat->UnlockModify();
+ }
+ }
+ }
+
+ // cache the result
+ const_cast<SwTableLine*>(this)->SetRedlineType( SwRedlineTable::npos == nRet
+ ? RedlineType::None
+ : aRedlineTable[ nRet ]->GetType());
+
+ return nRet;
+}
+
+bool SwTableLine::IsDeleted(SwRedlineTable::size_type& rRedlinePos) const
+{
+ SwRedlineTable::size_type nPos = UpdateTextChangesOnly(rRedlinePos);
+ if ( nPos != SwRedlineTable::npos )
+ {
+ const SwRedlineTable& aRedlineTable =
+ GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ if ( RedlineType::Delete == aRedlineTable[nPos]->GetType() )
+ return true;
+ }
+ return false;
+}
+
+RedlineType SwTableLine::GetRedlineType() const
+{
+ const SwRedlineTable& aRedlineTable = GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ if ( aRedlineTable.empty() )
+ return RedlineType::None;
+
+ // check table row property "HasTextChangesOnly", if it's defined and its value is
+ // false, return with the cached redline type, if it exists, otherwise calculate it
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( pHasTextChangesOnlyProp && !pHasTextChangesOnlyProp->GetValue() )
+ {
+ if ( RedlineType::None != m_eRedlineType )
+ return m_eRedlineType;
+
+ SwRedlineTable::size_type nPos = 0;
+ nPos = UpdateTextChangesOnly(nPos);
+ if ( nPos != SwRedlineTable::npos )
+ return aRedlineTable[nPos]->GetType();
+ }
+ else if ( RedlineType::None != m_eRedlineType )
+ // empty the cache
+ const_cast<SwTableLine*>(this)->SetRedlineType( RedlineType::None );
+
+ return RedlineType::None;
+}
+
+SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, sal_uInt16 nLines, SwTableLine *pUp )
+ : SwClient(nullptr)
+ , m_aLines()
+ , m_pStartNode(nullptr)
+ , m_pUpper(pUp)
+ , mnRowSpan(1)
+ , mbDummyFlag(false)
+ , mbDirectFormatting(false)
+{
+ m_aLines.reserve( nLines );
+ CheckBoxFormat( pFormat )->Add( this );
+}
+
+SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwNodeIndex &rIdx,
+ SwTableLine *pUp )
+ : SwClient(nullptr)
+ , m_aLines()
+ , m_pUpper(pUp)
+ , mnRowSpan(1)
+ , mbDummyFlag(false)
+ , mbDirectFormatting(false)
+{
+ CheckBoxFormat( pFormat )->Add( this );
+
+ m_pStartNode = rIdx.GetNode().GetStartNode();
+
+ // insert into the table
+ const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
+ assert(pTableNd && "In which table is that box?");
+ SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
+ GetTabSortBoxes());
+ SwTableBox* p = this; // error: &this
+ rSrtArr.insert( p ); // insert
+}
+
+SwTableBox::SwTableBox( SwTableBoxFormat* pFormat, const SwStartNode& rSttNd, SwTableLine *pUp )
+ : SwClient(nullptr)
+ , m_aLines()
+ , m_pStartNode(&rSttNd)
+ , m_pUpper(pUp)
+ , mnRowSpan(1)
+ , mbDummyFlag(false)
+ , mbDirectFormatting(false)
+{
+ CheckBoxFormat( pFormat )->Add( this );
+
+ // insert into the table
+ const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
+ OSL_ENSURE( pTableNd, "In which table is the box?" );
+ SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
+ GetTabSortBoxes());
+ SwTableBox* p = this; // error: &this
+ rSrtArr.insert( p ); // insert
+}
+
+void SwTableBox::RemoveFromTable()
+{
+ if (m_pStartNode) // box containing contents?
+ {
+ // remove from table
+ const SwTableNode* pTableNd = m_pStartNode->FindTableNode();
+ assert(pTableNd && "In which table is that box?");
+ SwTableSortBoxes& rSrtArr = const_cast<SwTableSortBoxes&>(pTableNd->GetTable().
+ GetTabSortBoxes());
+ SwTableBox *p = this; // error: &this
+ rSrtArr.erase( p ); // remove
+ m_pStartNode = nullptr; // clear it so this is only run once
+ }
+}
+
+SwTableBox::~SwTableBox()
+{
+ if (!GetFrameFormat()->GetDoc()->IsInDtor())
+ {
+ RemoveFromTable();
+ }
+
+ // the TabelleBox can be deleted if it's the last client of the FrameFormat
+ sw::BroadcastingModify* pMod = GetFrameFormat();
+ pMod->Remove( this ); // remove,
+ if( !pMod->HasWriterListeners() )
+ delete pMod; // and delete
+}
+
+SwTableBoxFormat* SwTableBox::CheckBoxFormat( SwTableBoxFormat* pFormat )
+{
+ // We might need to create a new format here, because the box must be
+ // added to the format solely if pFormat has a value or form.
+ if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ) )
+ {
+ SwTableBox* pOther = SwIterator<SwTableBox,SwFormat>( *pFormat ).First();
+ if( pOther )
+ {
+ SwTableBoxFormat* pNewFormat = pFormat->GetDoc()->MakeTableBoxFormat();
+ pNewFormat->LockModify();
+ *pNewFormat = *pFormat;
+
+ // Remove values and formulas
+ pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
+ pNewFormat->UnlockModify();
+
+ pFormat = pNewFormat;
+ }
+ }
+ return pFormat;
+}
+
+SwFrameFormat* SwTableBox::ClaimFrameFormat()
+{
+ // This method makes sure that this object is an exclusive SwTableBox client
+ // of an SwTableBoxFormat object
+ // If other SwTableBox objects currently listen to the same SwTableBoxFormat as
+ // this one, something needs to be done
+ SwTableBoxFormat *pRet = static_cast<SwTableBoxFormat*>(GetFrameFormat());
+ SwIterator<SwTableBox,SwFormat> aIter( *pRet );
+ for( SwTableBox* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if ( pLast != this )
+ {
+ // Found another SwTableBox object
+ // create a new Format as a copy and assign me to it
+ // don't copy values and formulas
+ SwTableBoxFormat* pNewFormat = pRet->GetDoc()->MakeTableBoxFormat();
+ pNewFormat->LockModify();
+ *pNewFormat = *pRet;
+ pNewFormat->ResetFormatAttr( RES_BOXATR_FORMULA, RES_BOXATR_VALUE );
+ pNewFormat->UnlockModify();
+
+ // re-register SwCellFrame objects that know me
+ SwIterator<SwCellFrame,SwFormat> aFrameIter( *pRet );
+ for( SwCellFrame* pCell = aFrameIter.First(); pCell; pCell = aFrameIter.Next() )
+ if( pCell->GetTabBox() == this )
+ pCell->RegisterToFormat( *pNewFormat );
+
+ // re-register myself
+ pNewFormat->Add( this );
+ pRet = pNewFormat;
+ break;
+ }
+ }
+ return pRet;
+}
+
+void SwTableBox::ChgFrameFormat(SwTableBoxFormat* pNewFormat, bool bNeedToReregister)
+{
+ SwFrameFormat* pOld = GetFrameFormat();
+ // tdf#84635 We set bNeedToReregister=false to avoid a quadratic slowdown on loading large tables,
+ // and since we are creating the table for the first time, no re-registration is necessary.
+ // First, re-register the Frames.
+ if(bNeedToReregister)
+ pOld->CallSwClientNotify(sw::TableBoxFormatChanged(*pNewFormat, *this));
+ // Now, re-register self.
+ pNewFormat->Add(this);
+ if(!pOld->HasWriterListeners())
+ delete pOld;
+}
+
+// Return the name of this box. This is determined dynamically
+// resulting from the position in the lines/boxes/tables.
+void sw_GetTableBoxColStr( sal_uInt16 nCol, OUString& rNm )
+{
+ const sal_uInt16 coDiff = 52; // 'A'-'Z' 'a' - 'z'
+
+ do {
+ const sal_uInt16 nCalc = nCol % coDiff;
+ if( nCalc >= 26 )
+ rNm = OUStringChar( sal_Unicode('a' - 26 + nCalc) ) + rNm;
+ else
+ rNm = OUStringChar( sal_Unicode('A' + nCalc) ) + rNm;
+
+ nCol = nCol - nCalc;
+ if( 0 == nCol )
+ break;
+ nCol /= coDiff;
+ --nCol;
+ } while( true );
+}
+
+Point SwTableBox::GetCoordinates() const
+{
+ if( !m_pStartNode ) // box without content?
+ {
+ // search for the next first box?
+ return Point( 0, 0 );
+ }
+
+ const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable();
+ sal_uInt16 nX, nY;
+ const SwTableBox* pBox = this;
+ do {
+ const SwTableLine* pLine = pBox->GetUpper();
+ // at the first level?
+ const SwTableLines* pLines = pLine->GetUpper()
+ ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines();
+
+ nY = pLines->GetPos( pLine ) + 1 ;
+ nX = pBox->GetUpper()->GetBoxPos( pBox ) + 1;
+ pBox = pLine->GetUpper();
+ } while( pBox );
+ return Point( nX, nY );
+}
+
+OUString SwTableBox::GetName() const
+{
+ if( !m_pStartNode ) // box without content?
+ {
+ // search for the next first box?
+ return OUString();
+ }
+
+ const SwTable& rTable = m_pStartNode->FindTableNode()->GetTable();
+ sal_uInt16 nPos;
+ OUString sNm, sTmp;
+ const SwTableBox* pBox = this;
+ do {
+ const SwTableLine* pLine = pBox->GetUpper();
+ // at the first level?
+ const SwTableLines* pLines = pLine->GetUpper()
+ ? &pLine->GetUpper()->GetTabLines() : &rTable.GetTabLines();
+
+ nPos = pLines->GetPos( pLine ) + 1;
+ sTmp = OUString::number( nPos );
+ if( !sNm.isEmpty() )
+ sNm = sTmp + "." + sNm;
+ else
+ sNm = sTmp;
+
+ nPos = pBox->GetUpper()->GetBoxPos( pBox );
+ sTmp = OUString::number(nPos + 1);
+ pBox = pLine->GetUpper();
+ if( nullptr != pBox )
+ sNm = sTmp + "." + sNm;
+ else
+ sw_GetTableBoxColStr( nPos, sNm );
+
+ } while( pBox );
+ return sNm;
+}
+
+bool SwTableBox::IsInHeadline( const SwTable* pTable ) const
+{
+ if( !GetUpper() ) // should only happen upon merge.
+ return false;
+
+ if( !pTable )
+ pTable = &m_pStartNode->FindTableNode()->GetTable();
+
+ const SwTableLine* pLine = GetUpper();
+ while( pLine->GetUpper() )
+ pLine = pLine->GetUpper()->GetUpper();
+
+ // Headerline?
+ return pTable->GetTabLines()[ 0 ] == pLine;
+}
+
+SwNodeOffset SwTableBox::GetSttIdx() const
+{
+ return m_pStartNode ? m_pStartNode->GetIndex() : SwNodeOffset(0);
+}
+
+bool SwTableBox::IsEmpty() const
+{
+ const SwStartNode *pSttNd = GetSttNd();
+ if( pSttNd &&
+ pSttNd->GetIndex() + 2 == pSttNd->EndOfSectionIndex() )
+ {
+ const SwContentNode *pCNd =
+ pSttNd->GetNodes()[pSttNd->GetIndex()+1]->GetContentNode();
+ if( pCNd && !pCNd->Len() )
+ return true;
+ }
+
+ return false;
+}
+
+ // retrieve information from the client
+bool SwTable::GetInfo( SfxPoolItem& rInfo ) const
+{
+ switch( rInfo.Which() )
+ {
+ case RES_AUTOFMT_DOCNODE:
+ {
+ const SwTableNode* pNode = GetTableNode();
+ if (pNode && &pNode->GetNodes() == static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes)
+ {
+ if (!m_TabSortContentBoxes.empty())
+ {
+ SwNodeIndex aIdx( *m_TabSortContentBoxes[0]->GetSttNd() );
+ GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx );
+ }
+ return false;
+ }
+ break;
+ }
+ case RES_FINDNEARESTNODE:
+ if( GetFrameFormat() &&
+ GetFrameFormat()->GetFormatAttr( RES_PAGEDESC ).GetPageDesc() &&
+ !m_TabSortContentBoxes.empty() &&
+ m_TabSortContentBoxes[0]->GetSttNd()->GetNodes().IsDocNodes() )
+ static_cast<SwFindNearestNode&>(rInfo).CheckNode( *
+ m_TabSortContentBoxes[0]->GetSttNd()->FindTableNode() );
+ break;
+
+ case RES_CONTENT_VISIBLE:
+ static_cast<SwPtrMsgPoolItem&>(rInfo).pObject = SwIterator<SwFrame,SwFormat>( *GetFrameFormat() ).First();
+ return false;
+ }
+ return true;
+}
+
+SwTable * SwTable::FindTable( SwFrameFormat const*const pFormat )
+{
+ return pFormat
+ ? SwIterator<SwTable,SwFormat>(*pFormat).First()
+ : nullptr;
+}
+
+SwTableNode* SwTable::GetTableNode() const
+{
+ return !GetTabSortBoxes().empty() ?
+ const_cast<SwTableNode*>(GetTabSortBoxes()[ 0 ]->GetSttNd()->FindTableNode()) :
+ m_pTableNode;
+}
+
+void SwTable::SetRefObject( SwServerObject* pObj )
+{
+ if( m_xRefObj.is() )
+ m_xRefObj->Closed();
+
+ m_xRefObj = pObj;
+}
+
+void SwTable::SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout> const& r)
+{
+ m_xHTMLLayout = r;
+}
+
+static void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
+ bool bChgAlign )
+{
+ SwNodeOffset nNdPos = rBox.IsValidNumTextNd();
+ ChgTextToNum( rBox,rText,pCol,bChgAlign,nNdPos);
+}
+void ChgTextToNum( SwTableBox& rBox, const OUString& rText, const Color* pCol,
+ bool bChgAlign, SwNodeOffset nNdPos )
+{
+
+ if( NODE_OFFSET_MAX == nNdPos )
+ return;
+
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+ SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode();
+
+ // assign adjustment
+ if( bChgAlign )
+ {
+ const SfxPoolItem* pItem;
+ pItem = &pTNd->SwContentNode::GetAttr( RES_PARATR_ADJUST );
+ SvxAdjust eAdjust = static_cast<const SvxAdjustItem*>(pItem)->GetAdjust();
+ if( SvxAdjust::Left == eAdjust || SvxAdjust::Block == eAdjust )
+ {
+ SvxAdjustItem aAdjust( *static_cast<const SvxAdjustItem*>(pItem) );
+ aAdjust.SetAdjust( SvxAdjust::Right );
+ pTNd->SetAttr( aAdjust );
+ }
+ }
+
+ // assign color or save "user color"
+ const SvxColorItem* pColorItem = nullptr;
+ if( pTNd->GetpSwAttrSet() )
+ pColorItem = pTNd->GetpSwAttrSet()->GetItemIfSet( RES_CHRATR_COLOR, false );
+
+ const std::optional<Color>& pOldNumFormatColor = rBox.GetSaveNumFormatColor();
+ std::optional<Color> pNewUserColor;
+ if (pColorItem)
+ pNewUserColor = pColorItem->GetValue();
+
+ if( ( pNewUserColor && pOldNumFormatColor &&
+ *pNewUserColor == *pOldNumFormatColor ) ||
+ ( !pNewUserColor && !pOldNumFormatColor ))
+ {
+ // Keep the user color, set updated values, delete old NumFormatColor if needed
+ if( pCol )
+ // if needed, set the color
+ pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
+ else if( pColorItem )
+ {
+ pNewUserColor = rBox.GetSaveUserColor();
+ if( pNewUserColor )
+ pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR ));
+ else
+ pTNd->ResetAttr( RES_CHRATR_COLOR );
+ }
+ }
+ else
+ {
+ // Save user color, set NumFormat color if needed, but never reset the color
+ rBox.SetSaveUserColor( pNewUserColor ? *pNewUserColor : std::optional<Color>() );
+
+ if( pCol )
+ // if needed, set the color
+ pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
+
+ }
+ rBox.SetSaveNumFormatColor( pCol ? *pCol : std::optional<Color>() );
+
+ if( pTNd->GetText() != rText )
+ {
+ // Exchange text. Bugfix to keep Tabs (front and back!) and annotations (inword comment anchors)
+ const OUString& rOrig = pTNd->GetText();
+ sal_Int32 n;
+
+ for( n = 0; n < rOrig.getLength() && ('\x9' == rOrig[n] || CH_TXTATR_INWORD == rOrig[n]); ++n )
+ ;
+ for( ; n < rOrig.getLength() && '\x01' == rOrig[n]; ++n )
+ ;
+ SwIndex aIdx( pTNd, n );
+ for( n = rOrig.getLength(); n && ('\x9' == rOrig[--n] || CH_TXTATR_INWORD == rOrig[n]); )
+ ;
+ sal_Int32 nEndPos = n;
+ n -= aIdx.GetIndex() - 1;
+
+ // Reset DontExpand-Flags before exchange, to retrigger expansion
+ {
+ SwIndex aResetIdx( aIdx, n );
+ pTNd->DontExpandFormat( aResetIdx, false, false );
+ }
+
+ if( !pDoc->getIDocumentRedlineAccess().IsIgnoreRedline() && !pDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aTemp(*pTNd, 0, *pTNd, rOrig.getLength());
+ pDoc->getIDocumentRedlineAccess().DeleteRedline(aTemp, true, RedlineType::Any);
+ }
+
+ // preserve comments inside of the number by deleting number portions starting from the back
+ sal_Int32 nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos );
+ while( nCommentPos > aIdx.GetIndex() )
+ {
+ pTNd->EraseText( SwIndex(pTNd, nCommentPos+1), nEndPos - nCommentPos, SwInsertFlags::EMPTYEXPAND );
+ // find the next non-sequential comment anchor
+ do
+ {
+ nEndPos = nCommentPos;
+ n = nEndPos - aIdx.GetIndex();
+ nCommentPos = pTNd->GetText().lastIndexOf( CH_TXTATR_INWORD, nEndPos );
+ --nEndPos;
+ }
+ while( nCommentPos > aIdx.GetIndex() && nCommentPos == nEndPos );
+ }
+
+ pTNd->EraseText( aIdx, n, SwInsertFlags::EMPTYEXPAND );
+ pTNd->InsertText( rText, aIdx, SwInsertFlags::EMPTYEXPAND );
+
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SwPaM aTemp(*pTNd, 0, *pTNd, rText.getLength());
+ pDoc->getIDocumentRedlineAccess().AppendRedline(new SwRangeRedline(RedlineType::Insert, aTemp), true);
+ }
+ }
+
+ // assign vertical orientation
+ const SwFormatVertOrient* pVertOrientItem;
+ if( bChgAlign &&
+ ( !(pVertOrientItem = rBox.GetFrameFormat()->GetItemIfSet( RES_VERT_ORIENT )) ||
+ text::VertOrientation::TOP == pVertOrientItem->GetVertOrient() ))
+ {
+ rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::BOTTOM ));
+ }
+
+}
+
+static void ChgNumToText( SwTableBox& rBox, sal_uLong nFormat )
+{
+ SwNodeOffset nNdPos = rBox.IsValidNumTextNd( false );
+ if( NODE_OFFSET_MAX == nNdPos )
+ return;
+
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+ SwTextNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTextNode();
+ bool bChgAlign = pDoc->IsInsTableAlignNum();
+
+ const Color * pCol = nullptr;
+ if( getSwDefaultTextFormat() != nFormat )
+ {
+ // special text format:
+ OUString sTmp;
+ const OUString sText( pTNd->GetText() );
+ pDoc->GetNumberFormatter()->GetOutputString( sText, nFormat, sTmp, &pCol );
+ if( sText != sTmp )
+ {
+ // exchange text
+ SwIndex aIdx( pTNd, sText.getLength() );
+ // Reset DontExpand-Flags before exchange, to retrigger expansion
+ pTNd->DontExpandFormat( aIdx, false, false );
+ aIdx = 0;
+ pTNd->EraseText( aIdx, SAL_MAX_INT32, SwInsertFlags::EMPTYEXPAND );
+ pTNd->InsertText( sTmp, aIdx, SwInsertFlags::EMPTYEXPAND );
+ }
+ }
+
+ const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
+
+ // assign adjustment
+ const SvxAdjustItem* pAdjustItem;
+ if( bChgAlign && pAttrSet &&
+ (pAdjustItem = pAttrSet->GetItemIfSet( RES_PARATR_ADJUST, false )) &&
+ SvxAdjust::Right == pAdjustItem->GetAdjust() )
+ {
+ pTNd->SetAttr( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
+ }
+
+ // assign color or save "user color"
+ const SvxColorItem* pColorItem = nullptr;
+ if( pAttrSet )
+ pColorItem = pAttrSet->GetItemIfSet( RES_CHRATR_COLOR, false );
+
+ const std::optional<Color>& pOldNumFormatColor = rBox.GetSaveNumFormatColor();
+ std::optional<Color> pNewUserColor;
+ if (pColorItem)
+ pNewUserColor = pColorItem->GetValue();
+
+ if( ( pNewUserColor && pOldNumFormatColor &&
+ *pNewUserColor == *pOldNumFormatColor ) ||
+ ( !pNewUserColor && !pOldNumFormatColor ))
+ {
+ // Keep the user color, set updated values, delete old NumFormatColor if needed
+ if( pCol )
+ // if needed, set the color
+ pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
+ else if( pColorItem )
+ {
+ pNewUserColor = rBox.GetSaveUserColor();
+ if( pNewUserColor )
+ pTNd->SetAttr( SvxColorItem( *pNewUserColor, RES_CHRATR_COLOR ));
+ else
+ pTNd->ResetAttr( RES_CHRATR_COLOR );
+ }
+ }
+ else
+ {
+ // Save user color, set NumFormat color if needed, but never reset the color
+ rBox.SetSaveUserColor( pNewUserColor );
+
+ if( pCol )
+ // if needed, set the color
+ pTNd->SetAttr( SvxColorItem( *pCol, RES_CHRATR_COLOR ));
+
+ }
+ rBox.SetSaveNumFormatColor( pCol ? *pCol : std::optional<Color>() );
+
+ // assign vertical orientation
+ const SwFormatVertOrient* pVertOrientItem;
+ if( bChgAlign &&
+ (pVertOrientItem = rBox.GetFrameFormat()->GetItemIfSet( RES_VERT_ORIENT, false )) &&
+ text::VertOrientation::BOTTOM == pVertOrientItem->GetVertOrient() )
+ {
+ rBox.GetFrameFormat()->SetFormatAttr( SwFormatVertOrient( 0, text::VertOrientation::TOP ));
+ }
+
+}
+void SwTableBoxFormat::BoxAttributeChanged(SwTableBox& rBox, const SwTableBoxNumFormat* pNewFormat, const SwTableBoxFormula* pNewFormula, const SwTableBoxValue* pNewValue, sal_uLong nOldFormat)
+{
+ sal_uLong nNewFormat;
+ if(pNewFormat)
+ {
+ nNewFormat = pNewFormat->GetValue();
+ // new formatting
+ // is it newer or has the current been removed?
+ if( SfxItemState::SET != GetItemState(RES_BOXATR_VALUE, false))
+ pNewFormat = nullptr;
+ }
+ else
+ {
+ // fetch the current Item
+ pNewFormat = GetItemIfSet(RES_BOXATR_FORMAT, false);
+ nOldFormat = GetTableBoxNumFormat().GetValue();
+ nNewFormat = pNewFormat ? pNewFormat->GetValue() : nOldFormat;
+ }
+
+ // is it newer or has the current been removed?
+ if(pNewValue)
+ {
+ if(GetDoc()->GetNumberFormatter()->IsTextFormat(nNewFormat))
+ nOldFormat = 0;
+ else
+ {
+ if(SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false))
+ nOldFormat = getSwDefaultTextFormat();
+ else
+ nNewFormat = getSwDefaultTextFormat();
+ }
+ }
+
+ // Logic:
+ // Value change: -> "simulate" a format change!
+ // Format change:
+ // Text -> !Text or format change:
+ // - align right for horizontal alignment, if LEFT or JUSTIFIED
+ // - align bottom for vertical alignment, if TOP is set, or default
+ // - replace text (color? negative numbers RED?)
+ // !Text -> Text:
+ // - align left for horizontal alignment, if RIGHT
+ // - align top for vertical alignment, if BOTTOM is set
+ SvNumberFormatter* pNumFormatr = GetDoc()->GetNumberFormatter();
+ bool bNewIsTextFormat = pNumFormatr->IsTextFormat(nNewFormat);
+
+ if((!bNewIsTextFormat && nOldFormat != nNewFormat) || pNewFormula)
+ {
+ bool bIsNumFormat = false;
+ OUString aOrigText;
+ bool bChgText = true;
+ double fVal = 0;
+ if(!pNewValue)
+ pNewValue = GetItemIfSet(RES_BOXATR_VALUE, false);
+ if(!pNewValue)
+ {
+ // so far, no value has been set, so try to evaluate the content
+ SwNodeOffset nNdPos = rBox.IsValidNumTextNd();
+ if(NODE_OFFSET_MAX != nNdPos)
+ {
+ sal_uInt32 nTmpFormatIdx = nNewFormat;
+ OUString aText(GetDoc()->GetNodes()[nNdPos] ->GetTextNode()->GetRedlineText());
+ aOrigText = aText;
+ if(aText.isEmpty())
+ bChgText = false;
+ else
+ {
+ // Keep Tabs
+ lcl_TabToBlankAtSttEnd(aText);
+
+ // JP 22.04.98: Bug 49659 -
+ // Special casing for percent
+ if(SvNumFormatType::PERCENT == pNumFormatr->GetType(nNewFormat))
+ {
+ sal_uInt32 nTmpFormat = 0;
+ if(GetDoc()->IsNumberFormat(aText, nTmpFormat, fVal))
+ {
+ if(SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat))
+ aText += "%";
+
+ bIsNumFormat = GetDoc()->IsNumberFormat(aText, nTmpFormatIdx, fVal);
+ }
+ }
+ else
+ bIsNumFormat = GetDoc()->IsNumberFormat(aText, nTmpFormatIdx, fVal);
+
+ if(bIsNumFormat)
+ {
+ // directly assign value - without Modify
+ bool bIsLockMod = IsModifyLocked();
+ LockModify();
+ SetFormatAttr(SwTableBoxValue(fVal));
+ if(!bIsLockMod)
+ UnlockModify();
+ }
+ }
+ }
+ }
+ else
+ {
+ fVal = pNewValue->GetValue();
+ bIsNumFormat = true;
+ }
+
+ // format contents with the new value assigned and write to paragraph
+ const Color* pCol = nullptr;
+ OUString sNewText;
+ bool bChangeFormat = true;
+ if(DBL_MAX == fVal)
+ {
+ sNewText = SwViewShell::GetShellRes()->aCalc_Error;
+ }
+ else
+ {
+ if(bIsNumFormat)
+ pNumFormatr->GetOutputString(fVal, nNewFormat, sNewText, &pCol);
+ else
+ {
+ // Original text could not be parsed as
+ // number/date/time/..., so keep the text.
+#if 0
+ // Actually the text should be formatted
+ // according to the format, which may include
+ // additional text from the format, for example
+ // in {0;-0;"BAD: "@}. But other places when
+ // entering a new value or changing text or
+ // changing to a different format of type Text
+ // don't do this (yet?).
+ pNumFormatr->GetOutputString(aOrigText, nNewFormat, sNewText, &pCol);
+#else
+ sNewText = aOrigText;
+#endif
+ // Remove the newly assigned numbering format as well if text actually exists.
+ // Exception: assume user-defined formats are always intentional.
+ if (bChgText && pNumFormatr->IsTextFormat(nOldFormat)
+ && !pNumFormatr->IsUserDefined(nNewFormat))
+ {
+ rBox.GetFrameFormat()->ResetFormatAttr(RES_BOXATR_FORMAT);
+ bChangeFormat = false;
+ }
+ }
+
+ if(!bChgText)
+ sNewText.clear();
+ }
+
+ // across all boxes
+ if (bChangeFormat)
+ ChgTextToNum(rBox, sNewText, pCol, GetDoc()->IsInsTableAlignNum());
+
+ }
+ else if(bNewIsTextFormat && nOldFormat != nNewFormat)
+ ChgNumToText(rBox, nNewFormat);
+}
+
+SwTableBox* SwTableBoxFormat::SwTableBoxFormat::GetTableBox()
+{
+ SwIterator<SwTableBox,SwFormat> aIter(*this);
+ auto pBox = aIter.First();
+ SAL_INFO_IF(!pBox, "sw.core", "no box found at format");
+ SAL_WARN_IF(pBox && aIter.Next(), "sw.core", "more than one box found at format");
+ return pBox;
+}
+
+// for detection of modifications (mainly TableBoxAttribute)
+void SwTableBoxFormat::SwClientNotify(const SwModify& rMod, const SfxHint& rHint)
+{
+ if(rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(IsModifyLocked() || !GetDoc() || GetDoc()->IsInDtor())
+ {
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+ return;
+ }
+ const SwTableBoxNumFormat* pNewFormat = nullptr;
+ const SwTableBoxFormula* pNewFormula = nullptr;
+ const SwTableBoxValue* pNewVal = nullptr;
+ sal_uLong nOldFormat = getSwDefaultTextFormat();
+
+ switch(pLegacy->m_pNew ? pLegacy->m_pNew->Which() : 0)
+ {
+ case RES_ATTRSET_CHG:
+ {
+ const SfxItemSet& rSet = *static_cast<const SwAttrSetChg*>(pLegacy->m_pNew)->GetChgSet();
+ pNewFormat = rSet.GetItemIfSet( RES_BOXATR_FORMAT, false);
+ if(pNewFormat)
+ nOldFormat = static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet()->Get(RES_BOXATR_FORMAT).GetValue();
+ pNewFormula = rSet.GetItemIfSet(RES_BOXATR_FORMULA, false);
+ pNewVal = rSet.GetItemIfSet(RES_BOXATR_VALUE, false);
+ break;
+ }
+ case RES_BOXATR_FORMAT:
+ pNewFormat = static_cast<const SwTableBoxNumFormat*>(pLegacy->m_pNew);
+ nOldFormat = static_cast<const SwTableBoxNumFormat*>(pLegacy->m_pOld)->GetValue();
+ break;
+ case RES_BOXATR_FORMULA:
+ pNewFormula = static_cast<const SwTableBoxFormula*>(pLegacy->m_pNew);
+ break;
+ case RES_BOXATR_VALUE:
+ pNewVal = static_cast<const SwTableBoxValue*>(pLegacy->m_pNew);
+ break;
+ }
+
+ // something changed and some BoxAttribut remained in the set!
+ if( pNewFormat || pNewFormula || pNewVal )
+ {
+ GetDoc()->getIDocumentFieldsAccess().SetFieldsDirty(true, nullptr, SwNodeOffset(0));
+
+ if(SfxItemState::SET == GetItemState(RES_BOXATR_FORMAT, false) ||
+ SfxItemState::SET == GetItemState(RES_BOXATR_VALUE, false) ||
+ SfxItemState::SET == GetItemState(RES_BOXATR_FORMULA, false) )
+ {
+ if(auto pBox = GetTableBox())
+ BoxAttributeChanged(*pBox, pNewFormat, pNewFormula, pNewVal, nOldFormat);
+ }
+ }
+ // call base class
+ SwFrameFormat::SwClientNotify(rMod, rHint);
+}
+
+bool SwTableBoxFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return false;
+}
+
+bool SwTableFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return false;
+}
+
+bool SwTableLineFormat::supportsFullDrawingLayerFillAttributeSet() const
+{
+ return false;
+}
+
+bool SwTableBox::HasNumContent( double& rNum, sal_uInt32& rFormatIndex,
+ bool& rIsEmptyTextNd ) const
+{
+ bool bRet = false;
+ SwNodeOffset nNdPos = IsValidNumTextNd();
+ if( NODE_OFFSET_MAX != nNdPos )
+ {
+ OUString aText( m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetRedlineText() );
+ // Keep Tabs
+ lcl_TabToBlankAtSttEnd( aText );
+ rIsEmptyTextNd = aText.isEmpty();
+ SvNumberFormatter* pNumFormatr = GetFrameFormat()->GetDoc()->GetNumberFormatter();
+
+ if( const SwTableBoxNumFormat* pItem = GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT, false) )
+ {
+ rFormatIndex = pItem->GetValue();
+ // Special casing for percent
+ if( !rIsEmptyTextNd && SvNumFormatType::PERCENT == pNumFormatr->GetType( rFormatIndex ))
+ {
+ sal_uInt32 nTmpFormat = 0;
+ if( GetFrameFormat()->GetDoc()->IsNumberFormat( aText, nTmpFormat, rNum ) &&
+ SvNumFormatType::NUMBER == pNumFormatr->GetType( nTmpFormat ))
+ aText += "%";
+ }
+ }
+ else
+ rFormatIndex = 0;
+
+ bRet = GetFrameFormat()->GetDoc()->IsNumberFormat( aText, rFormatIndex, rNum );
+ }
+ else
+ rIsEmptyTextNd = false;
+ return bRet;
+}
+
+bool SwTableBox::IsNumberChanged() const
+{
+ bool bRet = true;
+
+ if( SfxItemState::SET == GetFrameFormat()->GetItemState( RES_BOXATR_FORMULA, false ))
+ {
+ const SwTableBoxNumFormat *pNumFormat = GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMAT, false );
+ const SwTableBoxValue *pValue = GetFrameFormat()->GetItemIfSet( RES_BOXATR_VALUE, false );
+
+ SwNodeOffset nNdPos;
+ if( pNumFormat && pValue && NODE_OFFSET_MAX != ( nNdPos = IsValidNumTextNd() ) )
+ {
+ OUString sNewText, sOldText( m_pStartNode->GetNodes()[ nNdPos ]->
+ GetTextNode()->GetRedlineText() );
+ lcl_DelTabsAtSttEnd( sOldText );
+
+ const Color* pCol = nullptr;
+ GetFrameFormat()->GetDoc()->GetNumberFormatter()->GetOutputString(
+ pValue->GetValue(), pNumFormat->GetValue(), sNewText, &pCol );
+
+ bRet = sNewText != sOldText ||
+ !( ( !pCol && !GetSaveNumFormatColor() ) ||
+ ( pCol && GetSaveNumFormatColor() &&
+ *pCol == *GetSaveNumFormatColor() ));
+ }
+ }
+ return bRet;
+}
+
+SwNodeOffset SwTableBox::IsValidNumTextNd( bool bCheckAttr ) const
+{
+ SwNodeOffset nPos = NODE_OFFSET_MAX;
+ if( m_pStartNode )
+ {
+ SwNodeIndex aIdx( *m_pStartNode );
+ SwNodeOffset nIndex = aIdx.GetIndex();
+ const SwNodeOffset nIndexEnd = m_pStartNode->GetNodes()[ nIndex ]->EndOfSectionIndex();
+ const SwTextNode *pTextNode = nullptr;
+ while( ++nIndex < nIndexEnd )
+ {
+ const SwNode* pNode = m_pStartNode->GetNodes()[nIndex];
+ if( pNode->IsTableNode() )
+ {
+ pTextNode = nullptr;
+ break;
+ }
+ if( pNode->IsTextNode() )
+ {
+ if( pTextNode )
+ {
+ pTextNode = nullptr;
+ break;
+ }
+ else
+ {
+ pTextNode = pNode->GetTextNode();
+ nPos = nIndex;
+ }
+ }
+ }
+ if( pTextNode )
+ {
+ if( bCheckAttr )
+ {
+ const SwpHints* pHts = pTextNode->GetpSwpHints();
+ // do some tests if there's only text in the node!
+ // Flys/fields/...
+ if( pHts )
+ {
+ sal_Int32 nNextSetField = 0;
+ for( size_t n = 0; n < pHts->Count(); ++n )
+ {
+ const SwTextAttr* pAttr = pHts->Get(n);
+ if( RES_TXTATR_NOEND_BEGIN <= pAttr->Which() )
+ {
+ if ( (pAttr->GetStart() == nNextSetField)
+ && (pAttr->Which() == RES_TXTATR_FIELD))
+ {
+ // #i104949# hideous hack for report builder:
+ // it inserts hidden variable-set fields at
+ // the beginning of para in cell, but they
+ // should not turn cell into text cell
+ const SwField* pField = pAttr->GetFormatField().GetField();
+ if (pField &&
+ (pField->GetTypeId() == SwFieldTypesEnum::Set) &&
+ (0 != (static_cast<SwSetExpField const*>
+ (pField)->GetSubType() &
+ nsSwExtendedSubType::SUB_INVISIBLE)))
+ {
+ nNextSetField = pAttr->GetStart() + 1;
+ continue;
+ }
+ }
+ else if( RES_TXTATR_ANNOTATION == pAttr->Which() ||
+ RES_TXTATR_FTN == pAttr->Which() )
+ {
+ continue;
+ }
+ nPos = NODE_OFFSET_MAX;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ nPos = NODE_OFFSET_MAX;
+ }
+ return nPos;
+}
+
+// is this a Formula box or one with numeric content (AutoSum)
+sal_uInt16 SwTableBox::IsFormulaOrValueBox() const
+{
+ sal_uInt16 nWhich = 0;
+ const SwTextNode* pTNd;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA, false ))
+ nWhich = RES_BOXATR_FORMULA;
+ else if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE, false ) &&
+ !pFormat->GetDoc()->GetNumberFormatter()->IsTextFormat(
+ pFormat->GetTableBoxNumFormat().GetValue() ))
+ nWhich = RES_BOXATR_VALUE;
+ else if( m_pStartNode && m_pStartNode->GetIndex() + 2 == m_pStartNode->EndOfSectionIndex()
+ && nullptr != ( pTNd = m_pStartNode->GetNodes()[ m_pStartNode->GetIndex() + 1 ]
+ ->GetTextNode() ) && pTNd->GetText().isEmpty())
+ nWhich = USHRT_MAX;
+
+ return nWhich;
+}
+
+void SwTableBox::ActualiseValueBox()
+{
+ SwFrameFormat* pFormat = GetFrameFormat();
+ const SwTableBoxNumFormat *pFormatItem = pFormat->GetItemIfSet( RES_BOXATR_FORMAT, true );
+ if (!pFormatItem)
+ return;
+ const SwTableBoxValue *pValItem = pFormat->GetItemIfSet( RES_BOXATR_VALUE );
+ if (!pValItem)
+ return;
+
+ const sal_uLong nFormatId = pFormatItem->GetValue();
+ SwNodeOffset nNdPos = NODE_OFFSET_MAX;
+ SvNumberFormatter* pNumFormatr = pFormat->GetDoc()->GetNumberFormatter();
+
+ if( !pNumFormatr->IsTextFormat( nFormatId ) &&
+ NODE_OFFSET_MAX != (nNdPos = IsValidNumTextNd()) )
+ {
+ double fVal = pValItem->GetValue();
+ const Color* pCol = nullptr;
+ OUString sNewText;
+ pNumFormatr->GetOutputString( fVal, nFormatId, sNewText, &pCol );
+
+ const OUString& rText = m_pStartNode->GetNodes()[ nNdPos ]->GetTextNode()->GetText();
+ if( rText != sNewText )
+ ChgTextToNum( *this, sNewText, pCol, false ,nNdPos);
+ }
+}
+
+struct SwTableCellInfo::Impl
+{
+ const SwTable * m_pTable;
+ const SwCellFrame * m_pCellFrame;
+ const SwTabFrame * m_pTabFrame;
+ typedef o3tl::sorted_vector<const SwTableBox *> TableBoxes_t;
+ TableBoxes_t m_HandledTableBoxes;
+
+public:
+ Impl()
+ : m_pTable(nullptr), m_pCellFrame(nullptr), m_pTabFrame(nullptr)
+ {
+ }
+
+ void setTable(const SwTable * pTable)
+ {
+ m_pTable = pTable;
+ SwFrameFormat * pFrameFormat = m_pTable->GetFrameFormat();
+ m_pTabFrame = SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
+ if (m_pTabFrame && m_pTabFrame->IsFollow())
+ m_pTabFrame = m_pTabFrame->FindMaster(true);
+ }
+
+ const SwCellFrame * getCellFrame() const { return m_pCellFrame; }
+
+ const SwFrame * getNextFrameInTable(const SwFrame * pFrame);
+ const SwCellFrame * getNextCellFrame(const SwFrame * pFrame);
+ const SwCellFrame * getNextTableBoxsCellFrame(const SwFrame * pFrame);
+ bool getNext();
+};
+
+const SwFrame * SwTableCellInfo::Impl::getNextFrameInTable(const SwFrame * pFrame)
+{
+ const SwFrame * pResult = nullptr;
+
+ if (((! pFrame->IsTabFrame()) || pFrame == m_pTabFrame) && pFrame->GetLower())
+ pResult = pFrame->GetLower();
+ else if (pFrame->GetNext())
+ pResult = pFrame->GetNext();
+ else
+ {
+ while (pFrame->GetUpper() != nullptr)
+ {
+ pFrame = pFrame->GetUpper();
+
+ if (pFrame->IsTabFrame())
+ {
+ m_pTabFrame = static_cast<const SwTabFrame *>(pFrame)->GetFollow();
+ pResult = m_pTabFrame;
+ break;
+ }
+ else if (pFrame->GetNext())
+ {
+ pResult = pFrame->GetNext();
+ break;
+ }
+ }
+ }
+
+ return pResult;
+}
+
+const SwCellFrame * SwTableCellInfo::Impl::getNextCellFrame(const SwFrame * pFrame)
+{
+ const SwCellFrame * pResult = nullptr;
+
+ while ((pFrame = getNextFrameInTable(pFrame)) != nullptr)
+ {
+ if (pFrame->IsCellFrame())
+ {
+ pResult = static_cast<const SwCellFrame *>(pFrame);
+ break;
+ }
+ }
+
+ return pResult;
+}
+
+const SwCellFrame * SwTableCellInfo::Impl::getNextTableBoxsCellFrame(const SwFrame * pFrame)
+{
+ const SwCellFrame * pResult = nullptr;
+
+ while ((pFrame = getNextCellFrame(pFrame)) != nullptr)
+ {
+ const SwCellFrame * pCellFrame = static_cast<const SwCellFrame *>(pFrame);
+ const SwTableBox * pTabBox = pCellFrame->GetTabBox();
+ auto aIt = m_HandledTableBoxes.insert(pTabBox);
+ if (aIt.second)
+ {
+ pResult = pCellFrame;
+ break;
+ }
+ }
+
+ return pResult;
+}
+
+const SwCellFrame * SwTableCellInfo::getCellFrame() const
+{
+ return m_pImpl->getCellFrame();
+}
+
+bool SwTableCellInfo::Impl::getNext()
+{
+ if (m_pCellFrame == nullptr)
+ {
+ if (m_pTabFrame != nullptr)
+ m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pTabFrame);
+ }
+ else
+ m_pCellFrame = Impl::getNextTableBoxsCellFrame(m_pCellFrame);
+
+ return m_pCellFrame != nullptr;
+}
+
+SwTableCellInfo::SwTableCellInfo(const SwTable * pTable)
+ : m_pImpl(std::make_unique<Impl>())
+{
+ m_pImpl->setTable(pTable);
+}
+
+SwTableCellInfo::~SwTableCellInfo()
+{
+}
+
+bool SwTableCellInfo::getNext()
+{
+ return m_pImpl->getNext();
+}
+
+SwRect SwTableCellInfo::getRect() const
+{
+ SwRect aRet;
+
+ if (getCellFrame() != nullptr)
+ aRet = getCellFrame()->getFrameArea();
+
+ return aRet;
+}
+
+const SwTableBox * SwTableCellInfo::getTableBox() const
+{
+ const SwTableBox * pRet = nullptr;
+
+ if (getCellFrame() != nullptr)
+ pRet = getCellFrame()->GetTabBox();
+
+ return pRet;
+}
+
+void SwTable::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+bool SwTable::HasLayout() const
+{
+ const SwFrameFormat* pFrameFormat = GetFrameFormat();
+ //a table in a clipboard document doesn't have any layout information
+ return pFrameFormat && SwIterator<SwTabFrame,SwFormat>(*pFrameFormat).First();
+}
+
+void SwTableBox::RegisterToFormat( SwFormat& rFormat )
+{
+ rFormat.Add( this );
+}
+
+// free's any remaining child objects
+SwTableLines::~SwTableLines()
+{
+ for ( const_iterator it = begin(); it != end(); ++it )
+ delete *it;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx
new file mode 100644
index 000000000..007d4a846
--- /dev/null
+++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx
@@ -0,0 +1,2417 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <EnhancedPDFExportHelper.hxx>
+#include <hintids.hxx>
+
+#include <sot/exchange.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <tools/multisel.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/langitem.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/languageoptions.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <swatrset.hxx>
+#include <frmatr.hxx>
+#include <paratr.hxx>
+#include <ndtxt.hxx>
+#include <ndole.hxx>
+#include <section.hxx>
+#include <tox.hxx>
+#include <fmtfld.hxx>
+#include <txtinet.hxx>
+#include <fmtinfmt.hxx>
+#include <fchrfmt.hxx>
+#include <charfmt.hxx>
+#include <fmtanchr.hxx>
+#include <fmturl.hxx>
+#include <editsh.hxx>
+#include <viscrs.hxx>
+#include <txtfld.hxx>
+#include <reffld.hxx>
+#include <doc.hxx>
+#include <IDocumentOutlineNodes.hxx>
+#include <mdiexp.hxx>
+#include <docufld.hxx>
+#include <ftnidx.hxx>
+#include <txtftn.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <tabfrm.hxx>
+#include <rowfrm.hxx>
+#include <cellfrm.hxx>
+#include <sectfrm.hxx>
+#include <flyfrm.hxx>
+#include <notxtfrm.hxx>
+#include "porfld.hxx"
+#include <SwStyleNameMapper.hxx>
+#include "itrpaint.hxx"
+#include <i18nlangtag/languagetag.hxx>
+#include <IMark.hxx>
+#include <printdata.hxx>
+#include <SwNodeNum.hxx>
+#include <calbck.hxx>
+#include <stack>
+#include <frmtool.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <authfld.hxx>
+#include <dcontact.hxx>
+
+#include <tools/globname.hxx>
+#include <svx/svdobj.hxx>
+
+using namespace ::com::sun::star;
+
+// Some static data structures
+
+TableColumnsMap SwEnhancedPDFExportHelper::s_aTableColumnsMap;
+LinkIdMap SwEnhancedPDFExportHelper::s_aLinkIdMap;
+NumListIdMap SwEnhancedPDFExportHelper::s_aNumListIdMap;
+NumListBodyIdMap SwEnhancedPDFExportHelper::s_aNumListBodyIdMap;
+FrameTagIdMap SwEnhancedPDFExportHelper::s_aFrameTagIdMap;
+
+LanguageType SwEnhancedPDFExportHelper::s_eLanguageDefault = LANGUAGE_SYSTEM;
+
+#if OSL_DEBUG_LEVEL > 1
+
+static std::vector< sal_uInt16 > aStructStack;
+
+void lcl_DBGCheckStack()
+{
+ /* NonStructElement = 0 Document = 1 Part = 2
+ * Article = 3 Section = 4 Division = 5
+ * BlockQuote = 6 Caption = 7 TOC = 8
+ * TOCI = 9 Index = 10 Paragraph = 11
+ * Heading = 12 H1-6 = 13 - 18 List = 19
+ * ListItem = 20 LILabel = 21 LIBody = 22
+ * Table = 23 TableRow = 24 TableHeader = 25
+ * TableData = 26 Span = 27 Quote = 28
+ * Note = 29 Reference = 30 BibEntry = 31
+ * Code = 32 Link = 33 Figure = 34
+ * Formula = 35 Form = 36 Continued frame = 99
+ */
+
+ sal_uInt16 nElement;
+ for ( const auto& rItem : aStructStack )
+ {
+ nElement = rItem;
+ }
+ (void)nElement;
+};
+
+#endif
+
+namespace
+{
+// ODF Style Names:
+const char aTableHeadingName[] = "Table Heading";
+const char aQuotations[] = "Quotations";
+const char aCaption[] = "Caption";
+const char aHeading[] = "Heading";
+const char aQuotation[] = "Quotation";
+const char aSourceText[] = "Source Text";
+
+// PDF Tag Names:
+constexpr OUStringLiteral aDocumentString = u"Document";
+constexpr OUStringLiteral aDivString = u"Div";
+constexpr OUStringLiteral aSectString = u"Sect";
+constexpr OUStringLiteral aHString = u"H";
+constexpr OUStringLiteral aH1String = u"H1";
+constexpr OUStringLiteral aH2String = u"H2";
+constexpr OUStringLiteral aH3String = u"H3";
+constexpr OUStringLiteral aH4String = u"H4";
+constexpr OUStringLiteral aH5String = u"H5";
+constexpr OUStringLiteral aH6String = u"H6";
+constexpr OUStringLiteral aListString = u"L";
+constexpr OUStringLiteral aListItemString = u"LI";
+constexpr OUStringLiteral aListBodyString = u"LBody";
+constexpr OUStringLiteral aBlockQuoteString = u"BlockQuote";
+constexpr OUStringLiteral aCaptionString = u"Caption";
+constexpr OUStringLiteral aIndexString = u"Index";
+constexpr OUStringLiteral aTOCString = u"TOC";
+constexpr OUStringLiteral aTOCIString = u"TOCI";
+constexpr OUStringLiteral aTableString = u"Table";
+constexpr OUStringLiteral aTRString = u"TR";
+constexpr OUStringLiteral aTDString = u"TD";
+constexpr OUStringLiteral aTHString = u"TH";
+constexpr OUStringLiteral aBibEntryString = u"BibEntry";
+constexpr OUStringLiteral aQuoteString = u"Quote";
+constexpr OUStringLiteral aSpanString = u"Span";
+constexpr OUStringLiteral aCodeString = u"Code";
+constexpr OUStringLiteral aFigureString = u"Figure";
+constexpr OUStringLiteral aFormulaString = u"Formula";
+constexpr OUStringLiteral aLinkString = u"Link";
+constexpr OUStringLiteral aNoteString = u"Note";
+
+// returns true if first paragraph in cell frame has 'table heading' style
+bool lcl_IsHeadlineCell( const SwCellFrame& rCellFrame )
+{
+ bool bRet = false;
+
+ const SwContentFrame *pCnt = rCellFrame.ContainsContent();
+ if ( pCnt && pCnt->IsTextFrame() )
+ {
+ SwTextNode const*const pTextNode = static_cast<const SwTextFrame*>(pCnt)->GetTextNodeForParaProps();
+ const SwFormat* pTextFormat = pTextNode->GetFormatColl();
+
+ OUString sStyleName;
+ SwStyleNameMapper::FillProgName( pTextFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
+ bRet = sStyleName == aTableHeadingName;
+ }
+
+ return bRet;
+}
+
+// List all frames for which the NonStructElement tag is set:
+bool lcl_IsInNonStructEnv( const SwFrame& rFrame )
+{
+ bool bRet = false;
+
+ if ( nullptr != rFrame.FindFooterOrHeader() &&
+ !rFrame.IsHeaderFrame() && !rFrame.IsFooterFrame() )
+ {
+ bRet = true;
+ }
+ else if ( rFrame.IsInTab() && !rFrame.IsTabFrame() )
+ {
+ const SwTabFrame* pTabFrame = rFrame.FindTabFrame();
+ if ( rFrame.GetUpper() != pTabFrame &&
+ pTabFrame->IsFollow() && pTabFrame->IsInHeadline( rFrame ) )
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+// Generate key from frame for reopening tags:
+void* lcl_GetKeyFromFrame( const SwFrame& rFrame )
+{
+ void* pKey = nullptr;
+
+ if ( rFrame.IsPageFrame() )
+ pKey = const_cast<void*>(static_cast<void const *>(&(static_cast<const SwPageFrame&>(rFrame).GetFormat()->getIDocumentSettingAccess())));
+ else if ( rFrame.IsTextFrame() )
+ pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwTextFrame&>(rFrame).GetTextNodeFirst()));
+ else if ( rFrame.IsSctFrame() )
+ pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwSectionFrame&>(rFrame).GetSection()));
+ else if ( rFrame.IsTabFrame() )
+ pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwTabFrame&>(rFrame).GetTable()));
+ else if ( rFrame.IsRowFrame() )
+ pKey = const_cast<void*>(static_cast<void const *>(static_cast<const SwRowFrame&>(rFrame).GetTabLine()));
+ else if ( rFrame.IsCellFrame() )
+ {
+ const SwTabFrame* pTabFrame = rFrame.FindTabFrame();
+ const SwTable* pTable = pTabFrame->GetTable();
+ pKey = const_cast<void*>(static_cast<void const *>(& static_cast<const SwCellFrame&>(rFrame).GetTabBox()->FindStartOfRowSpan( *pTable )));
+ }
+
+ return pKey;
+}
+
+bool lcl_HasPreviousParaSameNumRule(SwTextFrame const& rTextFrame, const SwTextNode& rNode)
+{
+ bool bRet = false;
+ SwNodeIndex aIdx( rNode );
+ const SwDoc& rDoc = rNode.GetDoc();
+ const SwNodes& rNodes = rDoc.GetNodes();
+ const SwNode* pNode = &rNode;
+ const SwNumRule* pNumRule = rNode.GetNumRule();
+
+ while (pNode != rNodes.DocumentSectionStartNode(const_cast<SwNode*>(static_cast<SwNode const *>(&rNode))) )
+ {
+ sw::GotoPrevLayoutTextFrame(aIdx, rTextFrame.getRootFrame());
+
+ if (aIdx.GetNode().IsTextNode())
+ {
+ const SwTextNode *const pPrevTextNd = sw::GetParaPropsNode(
+ *rTextFrame.getRootFrame(), *aIdx.GetNode().GetTextNode());
+ const SwNumRule * pPrevNumRule = pPrevTextNd->GetNumRule();
+
+ // We find the previous text node. Now check, if the previous text node
+ // has the same numrule like rNode:
+ if ( (pPrevNumRule == pNumRule) &&
+ (!pPrevTextNd->IsOutline() == !rNode.IsOutline()))
+ bRet = true;
+
+ break;
+ }
+
+ pNode = &aIdx.GetNode();
+ }
+ return bRet;
+}
+
+bool lcl_TryMoveToNonHiddenField(SwEditShell& rShell, const SwTextNode& rNd, const SwFormatField& rField)
+{
+ // 1. Check if the whole paragraph is hidden
+ // 2. Move to the field
+ // 3. Check for hidden text attribute
+ if(rNd.IsHidden())
+ return false;
+ if(!rShell.GotoFormatField(rField) || rShell.SelectHiddenRange())
+ {
+ rShell.SwCursorShell::ClearMark();
+ return false;
+ }
+ return true;
+};
+
+} // end namespace
+
+SwTaggedPDFHelper::SwTaggedPDFHelper( const Num_Info* pNumInfo,
+ const Frame_Info* pFrameInfo,
+ const Por_Info* pPorInfo,
+ OutputDevice const & rOut )
+ : m_nEndStructureElement( 0 ),
+ m_nRestoreCurrentTag( -1 ),
+ mpNumInfo( pNumInfo ),
+ mpFrameInfo( pFrameInfo ),
+ mpPorInfo( pPorInfo )
+{
+ mpPDFExtOutDevData =
+ dynamic_cast< vcl::PDFExtOutDevData*>( rOut.GetExtOutDevData() );
+
+ if ( !(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportTaggedPDF()) )
+ return;
+
+#if OSL_DEBUG_LEVEL > 1
+ sal_Int32 nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement();
+ lcl_DBGCheckStack();
+#endif
+ if ( mpNumInfo )
+ BeginNumberedListStructureElements();
+ else if ( mpFrameInfo )
+ BeginBlockStructureElements();
+ else if ( mpPorInfo )
+ BeginInlineStructureElements();
+ else
+ BeginTag( vcl::PDFWriter::NonStructElement, OUString() );
+
+#if OSL_DEBUG_LEVEL > 1
+ nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement();
+ lcl_DBGCheckStack();
+ (void)nCurrentStruct;
+#endif
+}
+
+SwTaggedPDFHelper::~SwTaggedPDFHelper()
+{
+ if ( !(mpPDFExtOutDevData && mpPDFExtOutDevData->GetIsExportTaggedPDF()) )
+ return;
+
+#if OSL_DEBUG_LEVEL > 1
+ sal_Int32 nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement();
+ lcl_DBGCheckStack();
+#endif
+ EndStructureElements();
+
+#if OSL_DEBUG_LEVEL > 1
+ nCurrentStruct = mpPDFExtOutDevData->GetCurrentStructureElement();
+ lcl_DBGCheckStack();
+ (void)nCurrentStruct;
+#endif
+}
+
+static auto GetReopenTagFromFrame(SwFrame const& rFrame) -> sal_Int32
+{
+ void const*const pKey = lcl_GetKeyFromFrame(rFrame);
+
+ if (pKey)
+ {
+ FrameTagIdMap const& rFrameTagIdMap(SwEnhancedPDFExportHelper::GetFrameTagIdMap());
+ auto const it(rFrameTagIdMap.find(pKey));
+ if (it != rFrameTagIdMap.end())
+ {
+ return (*it).second;
+ }
+ }
+ return -1;
+}
+
+sal_Int32 SwDrawContact::GetPDFAnchorStructureElementId(SdrObject const& rObj, OutputDevice const&)
+{
+ SwFrame const*const pAnchorFrame(GetAnchoredObj(&rObj)->GetAnchorFrame());
+ return pAnchorFrame ? GetReopenTagFromFrame(*pAnchorFrame) : -1;
+}
+
+bool SwTaggedPDFHelper::CheckReopenTag()
+{
+ bool bRet = false;
+ sal_Int32 nReopenTag = -1;
+ bool bContinue = false; // in some cases we just have to reopen a tag without early returning
+
+ if ( mpFrameInfo )
+ {
+ const SwFrame& rFrame = mpFrameInfo->mrFrame;
+ const SwFrame* pKeyFrame = nullptr;
+
+ // Reopen an existing structure element if
+ // - rFrame is not the first page frame (reopen Document tag)
+ // - rFrame is a follow frame (reopen Master tag)
+ // - rFrame is a fly frame anchored at content (reopen Anchor paragraph tag)
+ // - rFrame is a fly frame anchored at page (reopen Document tag)
+ // - rFrame is a follow flow row (reopen TableRow tag)
+ // - rFrame is a cell frame in a follow flow row (reopen TableData tag)
+ if ( ( rFrame.IsPageFrame() && static_cast<const SwPageFrame&>(rFrame).GetPrev() ) ||
+ ( rFrame.IsFlowFrame() && SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() ) ||
+ ( rFrame.IsRowFrame() && rFrame.IsInFollowFlowRow() ) ||
+ ( rFrame.IsCellFrame() && const_cast<SwFrame&>(rFrame).GetPrevCellLeaf() ) )
+ {
+ pKeyFrame = &rFrame;
+ }
+ else if ( rFrame.IsFlyFrame() )
+ {
+ const SwFormatAnchor& rAnchor =
+ static_cast<const SwFlyFrame*>(&rFrame)->GetFormat()->GetAnchor();
+ if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PAGE == rAnchor.GetAnchorId()))
+ {
+ pKeyFrame = static_cast<const SwFlyFrame&>(rFrame).GetAnchorFrame();
+ bContinue = true;
+ }
+ }
+
+ if ( pKeyFrame )
+ {
+ nReopenTag = GetReopenTagFromFrame(*pKeyFrame);
+ }
+ }
+
+ if ( -1 != nReopenTag )
+ {
+ m_nRestoreCurrentTag = mpPDFExtOutDevData->GetCurrentStructureElement();
+ const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( nReopenTag );
+ OSL_ENSURE( bSuccess, "Failed to reopen tag" );
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.push_back( 99 );
+#endif
+
+ bRet = bSuccess;
+ }
+
+ return bRet && !bContinue;
+}
+
+void SwTaggedPDFHelper::CheckRestoreTag() const
+{
+ if ( m_nRestoreCurrentTag != -1 )
+ {
+ const bool bSuccess = mpPDFExtOutDevData->SetCurrentStructureElement( m_nRestoreCurrentTag );
+ OSL_ENSURE( bSuccess, "Failed to restore reopened tag" );
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.pop_back();
+#endif
+ }
+}
+
+void SwTaggedPDFHelper::BeginTag( vcl::PDFWriter::StructElement eType, const OUString& rString )
+{
+ // write new tag
+ const sal_Int32 nId = mpPDFExtOutDevData->BeginStructureElement( eType, rString );
+ ++m_nEndStructureElement;
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.push_back( o3tl::narrowing<sal_uInt16>(eType) );
+#endif
+
+ // Store the id of the current structure element if
+ // - it is a list structure element
+ // - it is a list body element with children
+ // - rFrame is the first page frame
+ // - rFrame is a master frame
+ // - rFrame has objects anchored to it
+ // - rFrame is a row frame or cell frame in a split table row
+
+ if ( mpNumInfo )
+ {
+ const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(mpNumInfo->mrFrame);
+ SwTextNode const*const pTextNd = rTextFrame.GetTextNodeForParaProps();
+ const SwNodeNum* pNodeNum = pTextNd->GetNum(rTextFrame.getRootFrame());
+
+ if ( vcl::PDFWriter::List == eType )
+ {
+ NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+ rNumListIdMap[ pNodeNum ] = nId;
+ }
+ else if ( vcl::PDFWriter::LIBody == eType )
+ {
+ NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
+ rNumListBodyIdMap[ pNodeNum ] = nId;
+ }
+ }
+ else if ( mpFrameInfo )
+ {
+ const SwFrame& rFrame = mpFrameInfo->mrFrame;
+
+ if ( ( rFrame.IsPageFrame() && !static_cast<const SwPageFrame&>(rFrame).GetPrev() ) ||
+ ( rFrame.IsFlowFrame() && !SwFlowFrame::CastFlowFrame(&rFrame)->IsFollow() && SwFlowFrame::CastFlowFrame(&rFrame)->HasFollow() ) ||
+ ( rFrame.IsTextFrame() && rFrame.GetDrawObjs() ) ||
+ ( rFrame.IsRowFrame() && rFrame.IsInSplitTableRow() ) ||
+ ( rFrame.IsCellFrame() && const_cast<SwFrame&>(rFrame).GetNextCellLeaf() ) )
+ {
+ const void* pKey = lcl_GetKeyFromFrame( rFrame );
+
+ if ( pKey )
+ {
+ FrameTagIdMap& rFrameTagIdMap = SwEnhancedPDFExportHelper::GetFrameTagIdMap();
+ rFrameTagIdMap[ pKey ] = nId;
+ }
+ }
+ }
+
+ SetAttributes( eType );
+}
+
+void SwTaggedPDFHelper::EndTag()
+{
+ mpPDFExtOutDevData->EndStructureElement();
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.pop_back();
+#endif
+}
+
+// Sets the attributes according to the structure type.
+void SwTaggedPDFHelper::SetAttributes( vcl::PDFWriter::StructElement eType )
+{
+ sal_Int32 nVal;
+
+ /*
+ * ATTRIBUTES FOR BLSE
+ */
+ if ( mpFrameInfo )
+ {
+ vcl::PDFWriter::StructAttributeValue eVal;
+ const SwFrame* pFrame = &mpFrameInfo->mrFrame;
+ SwRectFnSet aRectFnSet(pFrame);
+
+ bool bPlacement = false;
+ bool bWritingMode = false;
+ bool bSpaceBefore = false;
+ bool bSpaceAfter = false;
+ bool bStartIndent = false;
+ bool bEndIndent = false;
+ bool bTextIndent = false;
+ bool bTextAlign = false;
+ bool bWidth = false;
+ bool bHeight = false;
+ bool bBox = false;
+ bool bRowSpan = false;
+ bool bAltText = false;
+
+ // Check which attributes to set:
+
+ switch ( eType )
+ {
+ case vcl::PDFWriter::Document :
+ bWritingMode = true;
+ break;
+
+ case vcl::PDFWriter::Table :
+ bPlacement =
+ bWritingMode =
+ bSpaceBefore =
+ bSpaceAfter =
+ bStartIndent =
+ bEndIndent =
+ bWidth =
+ bHeight =
+ bBox = true;
+ break;
+
+ case vcl::PDFWriter::TableRow :
+ bPlacement =
+ bWritingMode = true;
+ break;
+
+ case vcl::PDFWriter::TableHeader :
+ mpPDFExtOutDevData->SetStructureAttribute(vcl::PDFWriter::Scope, vcl::PDFWriter::Column);
+ [[fallthrough]];
+ case vcl::PDFWriter::TableData :
+ bPlacement =
+ bWritingMode =
+ bWidth =
+ bHeight =
+ bRowSpan = true;
+ break;
+
+ case vcl::PDFWriter::H1 :
+ case vcl::PDFWriter::H2 :
+ case vcl::PDFWriter::H3 :
+ case vcl::PDFWriter::H4 :
+ case vcl::PDFWriter::H5 :
+ case vcl::PDFWriter::H6 :
+ case vcl::PDFWriter::Paragraph :
+ case vcl::PDFWriter::Heading :
+ case vcl::PDFWriter::Caption :
+ case vcl::PDFWriter::BlockQuote :
+
+ bPlacement =
+ bWritingMode =
+ bSpaceBefore =
+ bSpaceAfter =
+ bStartIndent =
+ bEndIndent =
+ bTextIndent =
+ bTextAlign = true;
+ break;
+
+ case vcl::PDFWriter::Formula :
+ case vcl::PDFWriter::Figure :
+ bAltText =
+ bPlacement =
+ bWidth =
+ bHeight =
+ bBox = true;
+ break;
+
+ case vcl::PDFWriter::Division:
+ if (pFrame->IsFlyFrame()) // this can be something else too
+ {
+ bAltText = true;
+ bBox = true;
+ }
+ break;
+
+ default :
+ break;
+ }
+
+ // Set the attributes:
+
+ if ( bPlacement )
+ {
+ eVal = vcl::PDFWriter::TableHeader == eType ||
+ vcl::PDFWriter::TableData == eType ?
+ vcl::PDFWriter::Inline :
+ vcl::PDFWriter::Block;
+
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::Placement, eVal );
+ }
+
+ if ( bWritingMode )
+ {
+ eVal = pFrame->IsVertical() ?
+ vcl::PDFWriter::TbRl :
+ pFrame->IsRightToLeft() ?
+ vcl::PDFWriter::RlTb :
+ vcl::PDFWriter::LrTb;
+
+ if ( vcl::PDFWriter::LrTb != eVal )
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::WritingMode, eVal );
+ }
+
+ if ( bSpaceBefore )
+ {
+ nVal = aRectFnSet.GetTopMargin(*pFrame);
+ if ( 0 != nVal )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceBefore, nVal );
+ }
+
+ if ( bSpaceAfter )
+ {
+ nVal = aRectFnSet.GetBottomMargin(*pFrame);
+ if ( 0 != nVal )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::SpaceAfter, nVal );
+ }
+
+ if ( bStartIndent )
+ {
+ nVal = aRectFnSet.GetLeftMargin(*pFrame);
+ if ( 0 != nVal )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::StartIndent, nVal );
+ }
+
+ if ( bEndIndent )
+ {
+ nVal = aRectFnSet.GetRightMargin(*pFrame);
+ if ( 0 != nVal )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::EndIndent, nVal );
+ }
+
+ if ( bTextIndent )
+ {
+ OSL_ENSURE( pFrame->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
+ const SvxLRSpaceItem &rSpace =
+ static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace();
+ nVal = rSpace.GetTextFirstLineOffset();
+ if ( 0 != nVal )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::TextIndent, nVal );
+ }
+
+ if ( bTextAlign )
+ {
+ OSL_ENSURE( pFrame->IsTextFrame(), "Frame type <-> tag attribute mismatch" );
+ const SwAttrSet& aSet = static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps()->GetSwAttrSet();
+ const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust();
+ if ( SvxAdjust::Block == nAdjust || SvxAdjust::Center == nAdjust ||
+ ( (pFrame->IsRightToLeft() && SvxAdjust::Left == nAdjust) ||
+ (!pFrame->IsRightToLeft() && SvxAdjust::Right == nAdjust) ) )
+ {
+ eVal = SvxAdjust::Block == nAdjust ?
+ vcl::PDFWriter::Justify :
+ SvxAdjust::Center == nAdjust ?
+ vcl::PDFWriter::Center :
+ vcl::PDFWriter::End;
+
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextAlign, eVal );
+ }
+ }
+
+ // ISO 14289-1:2014, Clause: 7.3
+ // ISO 14289-1:2014, Clause: 7.7
+ // For images (but not embedded objects), an ObjectInfoPrimitive2D is
+ // created, but it's not evaluated by VclMetafileProcessor2D any more;
+ // that would require producing StructureTagPrimitive2D here but that
+ // looks impossible so instead duplicate the code that sets the Alt
+ // text here again.
+ if (bAltText)
+ {
+ SwFlyFrameFormat const& rFly(*static_cast<SwFlyFrame const*>(pFrame)->GetFormat());
+ OUString const sep(
+ (rFly.GetObjTitle().isEmpty() || rFly.GetObjDescription().isEmpty())
+ ? OUString() : OUString(" - "));
+ OUString const altText(rFly.GetObjTitle() + sep + rFly.GetObjDescription());
+ if (!altText.isEmpty())
+ {
+ mpPDFExtOutDevData->SetAlternateText(altText);
+ }
+ }
+
+ if ( bWidth )
+ {
+ nVal = aRectFnSet.GetWidth(pFrame->getFrameArea());
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Width, nVal );
+ }
+
+ if ( bHeight )
+ {
+ nVal = aRectFnSet.GetHeight(pFrame->getFrameArea());
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Height, nVal );
+ }
+
+ if ( bBox )
+ {
+ // BBox only for non-split tables:
+ if ( vcl::PDFWriter::Table != eType ||
+ ( pFrame->IsTabFrame() &&
+ !static_cast<const SwTabFrame*>(pFrame)->IsFollow() &&
+ !static_cast<const SwTabFrame*>(pFrame)->HasFollow() ) )
+ {
+ mpPDFExtOutDevData->SetStructureBoundingBox(pFrame->getFrameArea().SVRect());
+ }
+ }
+
+ if ( bRowSpan )
+ {
+ if ( pFrame->IsCellFrame() )
+ {
+ const SwCellFrame* pThisCell = static_cast<const SwCellFrame*>(pFrame);
+ nVal = pThisCell->GetTabBox()->getRowSpan();
+ if ( nVal > 1 )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::RowSpan, nVal );
+
+ // calculate colspan:
+ const SwTabFrame* pTabFrame = pThisCell->FindTabFrame();
+ const SwTable* pTable = pTabFrame->GetTable();
+
+ SwRectFnSet fnRectX(pTabFrame);
+
+ const TableColumnsMapEntry& rCols = SwEnhancedPDFExportHelper::GetTableColumnsMap()[ pTable ];
+
+ const tools::Long nLeft = fnRectX.GetLeft(pThisCell->getFrameArea());
+ const tools::Long nRight = fnRectX.GetRight(pThisCell->getFrameArea());
+ const TableColumnsMapEntry::const_iterator aLeftIter = rCols.find( nLeft );
+ const TableColumnsMapEntry::const_iterator aRightIter = rCols.find( nRight );
+
+ OSL_ENSURE( aLeftIter != rCols.end() && aRightIter != rCols.end(), "Colspan trouble" );
+ if ( aLeftIter != rCols.end() && aRightIter != rCols.end() )
+ {
+ nVal = std::distance( aLeftIter, aRightIter );
+ if ( nVal > 1 )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::ColSpan, nVal );
+ }
+ }
+ }
+ }
+
+ /*
+ * ATTRIBUTES FOR ILSE
+ */
+ else if ( mpPorInfo )
+ {
+ const SwLinePortion* pPor = &mpPorInfo->mrPor;
+ const SwTextPaintInfo& rInf = mpPorInfo->mrTextPainter.GetInfo();
+
+ bool bActualText = false;
+ bool bBaselineShift = false;
+ bool bTextDecorationType = false;
+ bool bLinkAttribute = false;
+ bool bLanguage = false;
+
+ // Check which attributes to set:
+
+ switch ( eType )
+ {
+ case vcl::PDFWriter::Span :
+ case vcl::PDFWriter::Quote :
+ case vcl::PDFWriter::Code :
+ if( PortionType::HyphenStr == pPor->GetWhichPor() || PortionType::SoftHyphenStr == pPor->GetWhichPor() ||
+ PortionType::Hyphen == pPor->GetWhichPor() || PortionType::SoftHyphen == pPor->GetWhichPor() )
+ bActualText = true;
+ else
+ {
+ bBaselineShift =
+ bTextDecorationType =
+ bLanguage = true;
+ }
+ break;
+
+ case vcl::PDFWriter::Link :
+ bTextDecorationType =
+ bBaselineShift =
+ bLinkAttribute =
+ bLanguage = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if ( bActualText )
+ {
+ OUString aActualText;
+ if (pPor->GetWhichPor() == PortionType::SoftHyphen || pPor->GetWhichPor() == PortionType::Hyphen)
+ aActualText = OUString(u'\x00ad'); // soft hyphen
+ else
+ aActualText = rInf.GetText().copy(sal_Int32(rInf.GetIdx()), sal_Int32(pPor->GetLen()));
+ mpPDFExtOutDevData->SetActualText( aActualText );
+ }
+
+ if ( bBaselineShift )
+ {
+ // TODO: Calculate correct values!
+ nVal = rInf.GetFont()->GetEscapement();
+ if ( nVal > 0 ) nVal = 33;
+ else if ( nVal < 0 ) nVal = -33;
+
+ if ( 0 != nVal )
+ {
+ nVal = nVal * pPor->Height() / 100;
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::BaselineShift, nVal );
+ }
+ }
+
+ if ( bTextDecorationType )
+ {
+ if ( LINESTYLE_NONE != rInf.GetFont()->GetUnderline() )
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::Underline );
+ if ( LINESTYLE_NONE != rInf.GetFont()->GetOverline() )
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::Overline );
+ if ( STRIKEOUT_NONE != rInf.GetFont()->GetStrikeout() )
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::LineThrough );
+ if ( FontEmphasisMark::NONE != rInf.GetFont()->GetEmphasisMark() )
+ mpPDFExtOutDevData->SetStructureAttribute( vcl::PDFWriter::TextDecorationType, vcl::PDFWriter::Overline );
+ }
+
+ if ( bLanguage )
+ {
+
+ const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage();
+ const LanguageType nDefaultLang = SwEnhancedPDFExportHelper::GetDefaultLanguage();
+
+ if ( nDefaultLang != nCurrentLanguage )
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::Language, static_cast<sal_uInt16>(nCurrentLanguage) );
+ }
+
+ if ( bLinkAttribute )
+ {
+ const LinkIdMap& rLinkIdMap = SwEnhancedPDFExportHelper::GetLinkIdMap();
+ SwRect aPorRect;
+ rInf.CalcRect( *pPor, &aPorRect );
+ const Point aPorCenter = aPorRect.Center();
+ auto aIter = std::find_if(rLinkIdMap.begin(), rLinkIdMap.end(),
+ [&aPorCenter](const IdMapEntry& rEntry) { return rEntry.first.Contains(aPorCenter); });
+ if (aIter != rLinkIdMap.end())
+ {
+ sal_Int32 nLinkId = (*aIter).second;
+ mpPDFExtOutDevData->SetStructureAttributeNumerical( vcl::PDFWriter::LinkAnnotation, nLinkId );
+ }
+ }
+ }
+}
+
+void SwTaggedPDFHelper::BeginNumberedListStructureElements()
+{
+ OSL_ENSURE( mpNumInfo, "List without mpNumInfo?" );
+ if ( !mpNumInfo )
+ return;
+
+ const SwFrame& rFrame = mpNumInfo->mrFrame;
+ OSL_ENSURE( rFrame.IsTextFrame(), "numbered only for text frames" );
+ const SwTextFrame& rTextFrame = static_cast<const SwTextFrame&>(rFrame);
+
+ // Lowers of NonStructureElements should not be considered:
+
+ if ( lcl_IsInNonStructEnv( rTextFrame ) || rTextFrame.IsFollow() )
+ return;
+
+ const SwTextNode *const pTextNd = rTextFrame.GetTextNodeForParaProps();
+ const SwNumRule* pNumRule = pTextNd->GetNumRule();
+ const SwNodeNum* pNodeNum = pTextNd->GetNum(rTextFrame.getRootFrame());
+
+ const bool bNumbered = !pTextNd->IsOutline() && pNodeNum && pNodeNum->GetParent() && pNumRule;
+
+ // Check, if we have to reopen a list or a list body:
+ // First condition:
+ // Paragraph is numbered/bulleted
+ if ( !bNumbered )
+ return;
+
+ const SwNumberTreeNode* pParent = pNodeNum->GetParent();
+ const bool bSameNumbering = lcl_HasPreviousParaSameNumRule(rTextFrame, *pTextNd);
+
+ // Second condition: current numbering is not 'interrupted'
+ if ( bSameNumbering )
+ {
+ sal_Int32 nReopenTag = -1;
+
+ // Two cases:
+ // 1. We have to reopen an existing list body tag:
+ // - If the current node is either the first child of its parent
+ // and its level > 1 or
+ // - Numbering should restart at the current node and its level > 1
+ // - The current item has no label
+ const bool bNewSubListStart = pParent->GetParent() && (pParent->IsFirst( pNodeNum ) || pTextNd->IsListRestart() );
+ const bool bNoLabel = !pTextNd->IsCountedInList() && !pTextNd->IsListRestart();
+ if ( bNewSubListStart || bNoLabel )
+ {
+ // Fine, we try to reopen the appropriate list body
+ NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
+
+ if ( bNewSubListStart )
+ {
+ // The list body tag associated with the parent has to be reopened
+ // to start a new list inside the list body
+ NumListBodyIdMap::const_iterator aIter;
+
+ do
+ aIter = rNumListBodyIdMap.find( pParent );
+ while ( aIter == rNumListBodyIdMap.end() && nullptr != ( pParent = pParent->GetParent() ) );
+
+ if ( aIter != rNumListBodyIdMap.end() )
+ nReopenTag = (*aIter).second;
+ }
+ else // if(bNoLabel)
+ {
+ // The list body tag of a 'counted' predecessor has to be reopened
+ const SwNumberTreeNode* pPrevious = pNodeNum->GetPred(true);
+ while ( pPrevious )
+ {
+ if ( pPrevious->IsCounted())
+ {
+ // get id of list body tag
+ const NumListBodyIdMap::const_iterator aIter = rNumListBodyIdMap.find( pPrevious );
+ if ( aIter != rNumListBodyIdMap.end() )
+ {
+ nReopenTag = (*aIter).second;
+ break;
+ }
+ }
+ pPrevious = pPrevious->GetPred(true);
+ }
+ }
+ }
+ // 2. We have to reopen an existing list tag:
+ else if ( !pParent->IsFirst( pNodeNum ) && !pTextNd->IsListRestart() )
+ {
+ // any other than the first node in a list level has to reopen the current
+ // list. The current list is associated in a map with the first child of the list:
+ NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+
+ // Search backwards and check if any of the previous nodes has a list associated with it:
+ const SwNumberTreeNode* pPrevious = pNodeNum->GetPred(true);
+ while ( pPrevious )
+ {
+ // get id of list tag
+ const NumListIdMap::const_iterator aIter = rNumListIdMap.find( pPrevious );
+ if ( aIter != rNumListIdMap.end() )
+ {
+ nReopenTag = (*aIter).second;
+ break;
+ }
+
+ pPrevious = pPrevious->GetPred(true);
+ }
+ }
+
+ if ( -1 != nReopenTag )
+ {
+ m_nRestoreCurrentTag = mpPDFExtOutDevData->GetCurrentStructureElement();
+ mpPDFExtOutDevData->SetCurrentStructureElement( nReopenTag );
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.push_back( 99 );
+#endif
+ }
+ }
+ else
+ {
+ // clear list maps in case a list has been interrupted
+ NumListIdMap& rNumListIdMap = SwEnhancedPDFExportHelper::GetNumListIdMap();
+ rNumListIdMap.clear();
+ NumListBodyIdMap& rNumListBodyIdMap = SwEnhancedPDFExportHelper::GetNumListBodyIdMap();
+ rNumListBodyIdMap.clear();
+ }
+
+ // New tags:
+ const bool bNewListTag = (pNodeNum->GetParent()->IsFirst( pNodeNum ) || pTextNd->IsListRestart() || !bSameNumbering);
+ const bool bNewItemTag = bNewListTag || pTextNd->IsCountedInList(); // If the text node is not counted, we do not start a new list item:
+
+ if ( bNewListTag )
+ BeginTag( vcl::PDFWriter::List, aListString );
+
+ if ( bNewItemTag )
+ {
+ BeginTag( vcl::PDFWriter::ListItem, aListItemString );
+ BeginTag( vcl::PDFWriter::LIBody, aListBodyString );
+ }
+}
+
+void SwTaggedPDFHelper::BeginBlockStructureElements()
+{
+ const SwFrame* pFrame = &mpFrameInfo->mrFrame;
+
+ // Lowers of NonStructureElements should not be considered:
+
+ if (lcl_IsInNonStructEnv(*pFrame) && !pFrame->IsFlyFrame())
+ return;
+
+ // Check if we have to reopen an existing structure element.
+ // This has to be done e.g., if pFrame is a follow frame.
+ if ( CheckReopenTag() )
+ return;
+
+ sal_uInt16 nPDFType = USHRT_MAX;
+ OUString aPDFType;
+
+ switch ( pFrame->GetType() )
+ {
+ /*
+ * GROUPING ELEMENTS
+ */
+
+ case SwFrameType::Page :
+
+ // Document: Document
+
+ nPDFType = vcl::PDFWriter::Document;
+ aPDFType = aDocumentString;
+ break;
+
+ case SwFrameType::Header :
+ case SwFrameType::Footer :
+
+ // Header, Footer: NonStructElement
+
+ nPDFType = vcl::PDFWriter::NonStructElement;
+ break;
+
+ case SwFrameType::FtnCont :
+
+ // Footnote container: Division
+
+ nPDFType = vcl::PDFWriter::Division;
+ aPDFType = aDivString;
+ break;
+
+ case SwFrameType::Ftn :
+
+ // Footnote frame: Note
+
+ // Note: vcl::PDFWriter::Note is actually a ILSE. Nevertheless
+ // we treat it like a grouping element!
+ nPDFType = vcl::PDFWriter::Note;
+ aPDFType = aNoteString;
+ break;
+
+ case SwFrameType::Section :
+
+ // Section: TOX, Index, or Sect
+
+ {
+ const SwSection* pSection =
+ static_cast<const SwSectionFrame*>(pFrame)->GetSection();
+ if ( SectionType::ToxContent == pSection->GetType() )
+ {
+ const SwTOXBase* pTOXBase = pSection->GetTOXBase();
+ if ( pTOXBase )
+ {
+ if ( TOX_INDEX == pTOXBase->GetType() )
+ {
+ nPDFType = vcl::PDFWriter::Index;
+ aPDFType = aIndexString;
+ }
+ else
+ {
+ nPDFType = vcl::PDFWriter::TOC;
+ aPDFType = aTOCString;
+ }
+ }
+ }
+ else if ( SectionType::Content == pSection->GetType() )
+ {
+ nPDFType = vcl::PDFWriter::Section;
+ aPDFType = aSectString;
+ }
+ }
+ break;
+
+ /*
+ * BLOCK-LEVEL STRUCTURE ELEMENTS
+ */
+
+ case SwFrameType::Txt :
+ {
+ const SwTextNode* pTextNd =
+ static_cast<const SwTextFrame*>(pFrame)->GetTextNodeForParaProps();
+
+ const SwFormat* pTextFormat = pTextNd->GetFormatColl();
+ const SwFormat* pParentTextFormat = pTextFormat ? pTextFormat->DerivedFrom() : nullptr;
+
+ OUString sStyleName;
+ OUString sParentStyleName;
+
+ if ( pTextFormat)
+ SwStyleNameMapper::FillProgName( pTextFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
+ if ( pParentTextFormat)
+ SwStyleNameMapper::FillProgName( pParentTextFormat->GetName(), sParentStyleName, SwGetPoolIdFromName::TxtColl );
+
+ // This is the default. If the paragraph could not be mapped to
+ // any of the standard pdf tags, we write a user defined tag
+ // <stylename> with role = P
+ nPDFType = vcl::PDFWriter::Paragraph;
+ aPDFType = sStyleName;
+
+ // Quotations: BlockQuote
+
+ if (sStyleName == aQuotations)
+ {
+ nPDFType = vcl::PDFWriter::BlockQuote;
+ aPDFType = aBlockQuoteString;
+ }
+
+ // Caption: Caption
+
+ else if (sStyleName == aCaption)
+ {
+ nPDFType = vcl::PDFWriter::Caption;
+ aPDFType = aCaptionString;
+ }
+
+ // Caption: Caption
+
+ else if (sParentStyleName == aCaption)
+ {
+ nPDFType = vcl::PDFWriter::Caption;
+ aPDFType = sStyleName + aCaptionString;
+ }
+
+ // Heading: H
+
+ else if (sStyleName == aHeading)
+ {
+ nPDFType = vcl::PDFWriter::Heading;
+ aPDFType = aHString;
+ }
+
+ // Heading: H1 - H6
+
+ if (pTextNd->IsOutline()
+ && sw::IsParaPropsNode(*pFrame->getRootFrame(), *pTextNd))
+ {
+ int nRealLevel = pTextNd->GetAttrOutlineLevel()-1;
+ nRealLevel = std::min(nRealLevel, 5);
+
+ nPDFType = o3tl::narrowing<sal_uInt16>(vcl::PDFWriter::H1 + nRealLevel);
+ switch(nRealLevel)
+ {
+ case 0 :
+ aPDFType = aH1String;
+ break;
+ case 1 :
+ aPDFType = aH2String;
+ break;
+ case 2 :
+ aPDFType = aH3String;
+ break;
+ case 3 :
+ aPDFType = aH4String;
+ break;
+ case 4 :
+ aPDFType = aH5String;
+ break;
+ default:
+ aPDFType = aH6String;
+ break;
+ }
+ }
+
+ // Section: TOCI
+
+ else if ( pFrame->IsInSct() )
+ {
+ const SwSectionFrame* pSctFrame = pFrame->FindSctFrame();
+ const SwSection* pSection = pSctFrame->GetSection();
+
+ if ( SectionType::ToxContent == pSection->GetType() )
+ {
+ const SwTOXBase* pTOXBase = pSection->GetTOXBase();
+ if ( pTOXBase && TOX_INDEX != pTOXBase->GetType() )
+ {
+ // Special case: Open additional TOCI tag:
+ BeginTag( vcl::PDFWriter::TOCI, aTOCIString );
+ }
+ }
+ }
+ }
+ break;
+
+ case SwFrameType::Tab :
+
+ // TabFrame: Table
+
+ nPDFType = vcl::PDFWriter::Table;
+ aPDFType = aTableString;
+
+ {
+ // set up table column data:
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pFrame);
+ const SwTable* pTable = pTabFrame->GetTable();
+
+ TableColumnsMap& rTableColumnsMap = SwEnhancedPDFExportHelper::GetTableColumnsMap();
+ const TableColumnsMap::const_iterator aIter = rTableColumnsMap.find( pTable );
+
+ if ( aIter == rTableColumnsMap.end() )
+ {
+ SwRectFnSet aRectFnSet(pTabFrame);
+ TableColumnsMapEntry& rCols = rTableColumnsMap[ pTable ];
+
+ const SwTabFrame* pMasterFrame = pTabFrame->IsFollow() ? pTabFrame->FindMaster( true ) : pTabFrame;
+
+ while ( pMasterFrame )
+ {
+ const SwRowFrame* pRowFrame = static_cast<const SwRowFrame*>(pMasterFrame->GetLower());
+
+ while ( pRowFrame )
+ {
+ const SwFrame* pCellFrame = pRowFrame->GetLower();
+
+ const tools::Long nLeft = aRectFnSet.GetLeft(pCellFrame->getFrameArea());
+ rCols.insert( nLeft );
+
+ while ( pCellFrame )
+ {
+ const tools::Long nRight = aRectFnSet.GetRight(pCellFrame->getFrameArea());
+ rCols.insert( nRight );
+ pCellFrame = pCellFrame->GetNext();
+ }
+ pRowFrame = static_cast<const SwRowFrame*>(pRowFrame->GetNext());
+ }
+ pMasterFrame = pMasterFrame->GetFollow();
+ }
+ }
+ }
+
+ break;
+
+ /*
+ * TABLE ELEMENTS
+ */
+
+ case SwFrameType::Row :
+
+ // RowFrame: TR
+
+ if ( !static_cast<const SwRowFrame*>(pFrame)->IsRepeatedHeadline() )
+ {
+ nPDFType = vcl::PDFWriter::TableRow;
+ aPDFType = aTRString;
+ }
+ else
+ {
+ nPDFType = vcl::PDFWriter::NonStructElement;
+ }
+ break;
+
+ case SwFrameType::Cell :
+
+ // CellFrame: TH, TD
+
+ {
+ const SwTabFrame* pTable = static_cast<const SwCellFrame*>(pFrame)->FindTabFrame();
+ if ( pTable->IsInHeadline( *pFrame ) || lcl_IsHeadlineCell( *static_cast<const SwCellFrame*>(pFrame) ) )
+ {
+ nPDFType = vcl::PDFWriter::TableHeader;
+ aPDFType = aTHString;
+ }
+ else
+ {
+ nPDFType = vcl::PDFWriter::TableData;
+ aPDFType = aTDString;
+ }
+ }
+ break;
+
+ /*
+ * ILLUSTRATION
+ */
+
+ case SwFrameType::Fly :
+
+ // FlyFrame: Figure, Formula, Control
+ // fly in content or fly at page
+ {
+ const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>(pFrame);
+ if (pFly->GetAnchorFrame()->FindFooterOrHeader() != nullptr)
+ {
+ nPDFType = vcl::PDFWriter::NonStructElement;
+ }
+ else if (pFly->Lower() && pFly->Lower()->IsNoTextFrame())
+ {
+ bool bFormula = false;
+
+ const SwNoTextFrame* pNoTextFrame = static_cast<const SwNoTextFrame*>(pFly->Lower());
+ SwOLENode* pOLENd = const_cast<SwOLENode*>(pNoTextFrame->GetNode()->GetOLENode());
+ if ( pOLENd )
+ {
+ SwOLEObj& aOLEObj = pOLENd->GetOLEObj();
+ uno::Reference< embed::XEmbeddedObject > aRef = aOLEObj.GetOleRef();
+ if ( aRef.is() )
+ {
+ bFormula = 0 != SotExchange::IsMath( SvGlobalName( aRef->getClassID() ) );
+ }
+ }
+ if ( bFormula )
+ {
+ nPDFType = vcl::PDFWriter::Formula;
+ aPDFType = aFormulaString;
+ }
+ else
+ {
+ nPDFType = vcl::PDFWriter::Figure;
+ aPDFType = aFigureString;
+ }
+ }
+ else
+ {
+ nPDFType = vcl::PDFWriter::Division;
+ aPDFType = aDivString;
+ }
+ }
+ break;
+
+ default: break;
+ }
+
+ if ( USHRT_MAX != nPDFType )
+ {
+ BeginTag( static_cast<vcl::PDFWriter::StructElement>(nPDFType), aPDFType );
+ }
+}
+
+void SwTaggedPDFHelper::EndStructureElements()
+{
+ while ( m_nEndStructureElement > 0 )
+ {
+ EndTag();
+ --m_nEndStructureElement;
+ }
+
+ CheckRestoreTag();
+}
+
+void SwTaggedPDFHelper::BeginInlineStructureElements()
+{
+ const SwLinePortion* pPor = &mpPorInfo->mrPor;
+ const SwTextPaintInfo& rInf = mpPorInfo->mrTextPainter.GetInfo();
+ const SwTextFrame* pFrame = rInf.GetTextFrame();
+
+ // Lowers of NonStructureElements should not be considered:
+
+ if ( lcl_IsInNonStructEnv( *pFrame ) )
+ return;
+
+ sal_uInt16 nPDFType = USHRT_MAX;
+ OUString aPDFType;
+
+ switch ( pPor->GetWhichPor() )
+ {
+ case PortionType::Hyphen :
+ case PortionType::SoftHyphen :
+ // Check for alternative spelling:
+ case PortionType::HyphenStr :
+ case PortionType::SoftHyphenStr :
+ nPDFType = vcl::PDFWriter::Span;
+ aPDFType = aSpanString;
+ break;
+
+ case PortionType::Lay :
+ case PortionType::Text :
+ case PortionType::Para :
+ {
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ pFrame->MapViewToModel(rInf.GetIdx()));
+ SwTextAttr const*const pInetFormatAttr =
+ pos.first->GetTextAttrAt(pos.second, RES_TXTATR_INETFMT);
+
+ OUString sStyleName;
+ if ( !pInetFormatAttr )
+ {
+ std::vector<SwTextAttr *> const charAttrs(
+ pos.first->GetTextAttrsAt(pos.second, RES_TXTATR_CHARFMT));
+ // TODO: handle more than 1 char style?
+ const SwCharFormat* pCharFormat = (charAttrs.size())
+ ? (*charAttrs.begin())->GetCharFormat().GetCharFormat() : nullptr;
+ if ( pCharFormat )
+ SwStyleNameMapper::FillProgName( pCharFormat->GetName(), sStyleName, SwGetPoolIdFromName::TxtColl );
+ }
+
+ // Check for Link:
+ if( pInetFormatAttr )
+ {
+ nPDFType = vcl::PDFWriter::Link;
+ aPDFType = aLinkString;
+ }
+ // Check for Quote/Code character style:
+ else if (sStyleName == aQuotation)
+ {
+ nPDFType = vcl::PDFWriter::Quote;
+ aPDFType = aQuoteString;
+ }
+ else if (sStyleName == aSourceText)
+ {
+ nPDFType = vcl::PDFWriter::Code;
+ aPDFType = aCodeString;
+ }
+ else
+ {
+ const LanguageType nCurrentLanguage = rInf.GetFont()->GetLanguage();
+ const SwFontScript nFont = rInf.GetFont()->GetActual();
+ const LanguageType nDefaultLang = SwEnhancedPDFExportHelper::GetDefaultLanguage();
+
+ if ( LINESTYLE_NONE != rInf.GetFont()->GetUnderline() ||
+ LINESTYLE_NONE != rInf.GetFont()->GetOverline() ||
+ STRIKEOUT_NONE != rInf.GetFont()->GetStrikeout() ||
+ FontEmphasisMark::NONE != rInf.GetFont()->GetEmphasisMark() ||
+ 0 != rInf.GetFont()->GetEscapement() ||
+ SwFontScript::Latin != nFont ||
+ nCurrentLanguage != nDefaultLang ||
+ !sStyleName.isEmpty())
+ {
+ nPDFType = vcl::PDFWriter::Span;
+ if (!sStyleName.isEmpty())
+ aPDFType = sStyleName;
+ else
+ aPDFType = aSpanString;
+ }
+ }
+ }
+ break;
+
+ case PortionType::Footnote :
+ nPDFType = vcl::PDFWriter::Link;
+ aPDFType = aLinkString;
+ break;
+
+ case PortionType::Field :
+ {
+ // check field type:
+ TextFrameIndex const nIdx = static_cast<const SwFieldPortion*>(pPor)->IsFollow()
+ ? rInf.GetIdx() - TextFrameIndex(1)
+ : rInf.GetIdx();
+ const SwTextAttr* pHint = mpPorInfo->mrTextPainter.GetAttr( nIdx );
+ if ( pHint && RES_TXTATR_FIELD == pHint->Which() )
+ {
+ const SwField* pField = pHint->GetFormatField().GetField();
+ if ( SwFieldIds::GetRef == pField->Which() )
+ {
+ nPDFType = vcl::PDFWriter::Link;
+ aPDFType = aLinkString;
+ }
+ else if ( SwFieldIds::TableOfAuthorities == pField->Which() )
+ {
+ nPDFType = vcl::PDFWriter::BibEntry;
+ aPDFType = aBibEntryString;
+ }
+ }
+ }
+ break;
+
+ case PortionType::Tab :
+ case PortionType::TabRight :
+ case PortionType::TabCenter :
+ case PortionType::TabDecimal :
+ nPDFType = vcl::PDFWriter::NonStructElement;
+ break;
+ default: break;
+ }
+
+ if ( USHRT_MAX != nPDFType )
+ {
+ BeginTag( static_cast<vcl::PDFWriter::StructElement>(nPDFType), aPDFType );
+ }
+}
+
+bool SwTaggedPDFHelper::IsExportTaggedPDF( const OutputDevice& rOut )
+{
+ vcl::PDFExtOutDevData* pPDFExtOutDevData = dynamic_cast< vcl::PDFExtOutDevData*>( rOut.GetExtOutDevData() );
+ return pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportTaggedPDF();
+}
+
+SwEnhancedPDFExportHelper::SwEnhancedPDFExportHelper( SwEditShell& rSh,
+ OutputDevice& rOut,
+ const OUString& rPageRange,
+ bool bSkipEmptyPages,
+ bool bEditEngineOnly,
+ const SwPrintData& rPrintData )
+ : mrSh( rSh ),
+ mrOut( rOut ),
+ mbSkipEmptyPages( bSkipEmptyPages ),
+ mbEditEngineOnly( bEditEngineOnly ),
+ mrPrintData( rPrintData )
+{
+ if ( !rPageRange.isEmpty() )
+ mpRangeEnum.reset( new StringRangeEnumerator( rPageRange, 0, mrSh.GetPageCount()-1 ) );
+
+ if ( mbSkipEmptyPages )
+ {
+ maPageNumberMap.resize( mrSh.GetPageCount() );
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+ sal_Int32 nPageNumber = 0;
+ for ( size_t i = 0, n = maPageNumberMap.size(); i < n && pCurrPage; ++i )
+ {
+ if ( pCurrPage->IsEmptyPage() )
+ maPageNumberMap[i] = -1;
+ else
+ maPageNumberMap[i] = nPageNumber++;
+
+ pCurrPage = static_cast<const SwPageFrame*>( pCurrPage->GetNext() );
+ }
+ }
+
+ s_aTableColumnsMap.clear();
+ s_aLinkIdMap.clear();
+ s_aNumListIdMap.clear();
+ s_aNumListBodyIdMap.clear();
+ s_aFrameTagIdMap.clear();
+
+#if OSL_DEBUG_LEVEL > 1
+ aStructStack.clear();
+#endif
+
+ const sal_Int16 nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+ sal_uInt16 nLangRes = RES_CHRATR_LANGUAGE;
+
+ if ( i18n::ScriptType::ASIAN == nScript )
+ nLangRes = RES_CHRATR_CJK_LANGUAGE;
+ else if ( i18n::ScriptType::COMPLEX == nScript )
+ nLangRes = RES_CHRATR_CTL_LANGUAGE;
+
+ s_eLanguageDefault = static_cast<const SvxLanguageItem*>(&mrSh.GetDoc()->GetDefault( nLangRes ))->GetLanguage();
+
+ EnhancedPDFExport();
+}
+
+SwEnhancedPDFExportHelper::~SwEnhancedPDFExportHelper()
+{
+}
+
+tools::Rectangle SwEnhancedPDFExportHelper::SwRectToPDFRect(const SwPageFrame* pCurrPage,
+ const tools::Rectangle& rRectangle) const
+{
+ SwPostItMode nPostItMode = mrPrintData.GetPrintPostIts();
+ if (nPostItMode != SwPostItMode::InMargins)
+ return rRectangle;
+ //the page has been scaled by 75% and vertically centered, so adjust these
+ //rectangles equivalently
+ tools::Rectangle aRect(rRectangle);
+ Size aRectSize(aRect.GetSize());
+ double fScale = 0.75;
+ aRectSize.setWidth( aRectSize.Width() * fScale );
+ aRectSize.setHeight( aRectSize.Height() * fScale );
+ tools::Long nOrigHeight = pCurrPage->getFrameArea().Height();
+ tools::Long nNewHeight = nOrigHeight*fScale;
+ tools::Long nShiftY = (nOrigHeight-nNewHeight)/2;
+ aRect.SetLeft( aRect.Left() * fScale );
+ aRect.SetTop( aRect.Top() * fScale );
+ aRect.Move(0, nShiftY);
+ aRect.SetSize(aRectSize);
+ return aRect;
+}
+
+void SwEnhancedPDFExportHelper::EnhancedPDFExport()
+{
+ vcl::PDFExtOutDevData* pPDFExtOutDevData =
+ dynamic_cast< vcl::PDFExtOutDevData*>( mrOut.GetExtOutDevData() );
+
+ if ( !pPDFExtOutDevData )
+ return;
+
+ // set the document locale
+
+ css::lang::Locale aDocLocale( LanguageTag( SwEnhancedPDFExportHelper::GetDefaultLanguage() ).getLocale() );
+ pPDFExtOutDevData->SetDocumentLocale( aDocLocale );
+
+ // Prepare the output device:
+
+ mrOut.Push( vcl::PushFlags::MAPMODE );
+ MapMode aMapMode( mrOut.GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::MapTwip );
+ mrOut.SetMapMode( aMapMode );
+
+ // Create new cursor and lock the view:
+
+ SwDoc* pDoc = mrSh.GetDoc();
+ mrSh.SwCursorShell::Push();
+ mrSh.SwCursorShell::ClearMark();
+ const bool bOldLockView = mrSh.IsViewLocked();
+ mrSh.LockView( true );
+
+ if ( !mbEditEngineOnly )
+ {
+
+ // POSTITS
+
+ if ( pPDFExtOutDevData->GetIsExportNotes() )
+ {
+ std::vector<SwFormatField*> vpFields;
+ mrSh.GetFieldType(SwFieldIds::Postit, OUString())->GatherFields(vpFields);
+ for(auto pFormatField : vpFields)
+ {
+ const SwTextNode* pTNd = pFormatField->GetTextField()->GetpTextNode();
+ OSL_ENSURE(nullptr != pTNd, "Enhanced pdf export - text node is missing");
+ if(!lcl_TryMoveToNonHiddenField(mrSh, *pTNd, *pFormatField))
+ continue;
+ // Link Rectangle
+ const SwRect& rNoteRect = mrSh.GetCharRect();
+ const SwPageFrame* pCurrPage = static_cast<const SwPageFrame*>(mrSh.GetLayout()->Lower());
+
+ // Link PageNums
+ std::vector<sal_Int32> aNotePageNums = CalcOutputPageNums(rNoteRect);
+ for (sal_Int32 aNotePageNum : aNotePageNums)
+ {
+
+ // Use the NumberFormatter to get the date string:
+ const SwPostItField* pField = static_cast<SwPostItField*>(pFormatField->GetField());
+ SvNumberFormatter* pNumFormatter = pDoc->GetNumberFormatter();
+ const Date aDateDiff(pField->GetDate() - pNumFormatter->GetNullDate());
+ const sal_uLong nFormat = pNumFormatter->GetStandardFormat(SvNumFormatType::DATE, pField->GetLanguage());
+ OUString sDate;
+ const Color* pColor;
+ pNumFormatter->GetOutputString(aDateDiff.GetDate(), nFormat, sDate, &pColor);
+
+ vcl::PDFNote aNote;
+ // The title should consist of the author and the date:
+ aNote.Title = pField->GetPar1() + ", " + sDate + ", " + (pField->GetResolved() ? SwResId(STR_RESOLVED) : "");
+ // Guess what the contents contains...
+ aNote.Contents = pField->GetText();
+
+ // Link Export
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rNoteRect.SVRect()));
+ pPDFExtOutDevData->CreateNote(aRect, aNote, aNotePageNum);
+ }
+ mrSh.SwCursorShell::ClearMark();
+ }
+ }
+
+ // HYPERLINKS
+
+ SwGetINetAttrs aArr;
+ mrSh.GetINetAttrs( aArr );
+ for( auto &rAttr : aArr )
+ {
+ SwGetINetAttr* p = &rAttr;
+ OSL_ENSURE( nullptr != p, "Enhanced pdf export - SwGetINetAttr is missing" );
+
+ const SwTextNode* pTNd = p->rINetAttr.GetpTextNode();
+ OSL_ENSURE( nullptr != pTNd, "Enhanced pdf export - text node is missing" );
+
+ // 1. Check if the whole paragraph is hidden
+ // 2. Move to the hyperlink
+ // 3. Check for hidden text attribute
+ if ( !pTNd->IsHidden() &&
+ mrSh.GotoINetAttr( p->rINetAttr ) &&
+ !mrSh.SelectHiddenRange() )
+ {
+ // Select the hyperlink:
+ mrSh.SwCursorShell::Right( 1, CRSR_SKIP_CHARS );
+ if ( mrSh.SwCursorShell::SelectTextAttr( RES_TXTATR_INETFMT, true ) )
+ {
+ // First, we create the destination, because there may be more
+ // than one link to this destination:
+ OUString aURL( INetURLObject::decode(
+ p->rINetAttr.GetINetFormat().GetValue(),
+ INetURLObject::DecodeMechanism::Unambiguous ) );
+
+ // We have to distinguish between intern and real URLs
+ const bool bIntern = '#' == aURL[0];
+
+ // GetCursor_() is a SwShellCursor, which is derived from
+ // SwSelPaintRects, therefore the rectangles of the current
+ // selection can be easily obtained:
+ // Note: We make a copy of the rectangles, because they may
+ // be deleted again in JumpToSwMark.
+ SwRects aTmp;
+ aTmp.insert( aTmp.begin(), mrSh.SwCursorShell::GetCursor_()->begin(), mrSh.SwCursorShell::GetCursor_()->end() );
+ OSL_ENSURE( !aTmp.empty(), "Enhanced pdf export - rectangles are missing" );
+ OUString const altText(mrSh.GetSelText());
+
+ const SwPageFrame* pSelectionPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Create the destination for internal links:
+ sal_Int32 nDestId = -1;
+ if ( bIntern )
+ {
+ aURL = aURL.copy( 1 );
+ mrSh.SwCursorShell::ClearMark();
+ if (! JumpToSwMark( &mrSh, aURL ))
+ {
+ continue; // target deleted
+ }
+
+ // Destination Rectangle
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ // Destination Export
+ if ( -1 != nDestPageNum )
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+ }
+ }
+
+ if ( !bIntern || -1 != nDestId )
+ {
+ // #i44368# Links in Header/Footer
+ const SwPosition aPos( *pTNd );
+ const bool bHeaderFooter = pDoc->IsInHeaderFooter( aPos.nNode );
+
+ // Create links for all selected rectangles:
+ const size_t nNumOfRects = aTmp.size();
+ for ( size_t i = 0; i < nNumOfRects; ++i )
+ {
+ // Link Rectangle
+ const SwRect& rLinkRect( aTmp[ i ] );
+
+ // Link PageNums
+ std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( rLinkRect );
+
+ for (sal_Int32 aLinkPageNum : aLinkPageNums)
+ {
+ // Link Export
+ tools::Rectangle aRect(SwRectToPDFRect(pSelectionPage, rLinkRect.SVRect()));
+ const sal_Int32 nLinkId =
+ pPDFExtOutDevData->CreateLink(aRect, altText, aLinkPageNum);
+
+ // Store link info for tagged pdf output:
+ const IdMapEntry aLinkEntry( rLinkRect, nLinkId );
+ s_aLinkIdMap.push_back( aLinkEntry );
+
+ // Connect Link and Destination:
+ if ( bIntern )
+ pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
+ else
+ pPDFExtOutDevData->SetLinkURL( nLinkId, aURL );
+
+ // #i44368# Links in Header/Footer
+ if ( bHeaderFooter )
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, aURL, bIntern, altText);
+ }
+ }
+ }
+ }
+ }
+ mrSh.SwCursorShell::ClearMark();
+ }
+
+ // HYPERLINKS (Graphics, Frames, OLEs )
+
+ SwFrameFormats* pTable = pDoc->GetSpzFrameFormats();
+ const size_t nSpzFrameFormatsCount = pTable->size();
+ for( size_t n = 0; n < nSpzFrameFormatsCount; ++n )
+ {
+ SwFrameFormat* pFrameFormat = (*pTable)[n];
+ const SwFormatURL* pItem;
+ if ( RES_DRAWFRMFMT != pFrameFormat->Which() &&
+ GetFrameOfModify(mrSh.GetLayout(), *pFrameFormat, SwFrameType::Fly) &&
+ (pItem = pFrameFormat->GetAttrSet().GetItemIfSet( RES_URL )) )
+ {
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ OUString aURL( pItem->GetURL() );
+ if (aURL.isEmpty())
+ continue;
+ const bool bIntern = '#' == aURL[0];
+
+ // Create the destination for internal links:
+ sal_Int32 nDestId = -1;
+ if ( bIntern )
+ {
+ aURL = aURL.copy( 1 );
+ mrSh.SwCursorShell::ClearMark();
+ if (! JumpToSwMark( &mrSh, aURL ))
+ {
+ continue; // target deleted
+ }
+
+ // Destination Rectangle
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ // Destination Export
+ if ( -1 != nDestPageNum )
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+ }
+ }
+
+ if ( !bIntern || -1 != nDestId )
+ {
+ Point aNullPt;
+ const SwRect aLinkRect = pFrameFormat->FindLayoutRect( false, &aNullPt );
+ OUString const formatName(pFrameFormat->GetName());
+ // Link PageNums
+ std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( aLinkRect );
+
+ // Link Export
+ for (sal_Int32 aLinkPageNum : aLinkPageNums)
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, aLinkRect.SVRect()));
+ const sal_Int32 nLinkId =
+ pPDFExtOutDevData->CreateLink(aRect, formatName, aLinkPageNum);
+
+ // Connect Link and Destination:
+ if ( bIntern )
+ pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
+ else
+ pPDFExtOutDevData->SetLinkURL( nLinkId, aURL );
+
+ // #i44368# Links in Header/Footer
+ const SwFormatAnchor &rAnch = pFrameFormat->GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE != rAnch.GetAnchorId())
+ {
+ const SwPosition* pPosition = rAnch.GetContentAnchor();
+ if ( pPosition && pDoc->IsInHeaderFooter( pPosition->nNode ) )
+ {
+ const SwTextNode* pTNd = pPosition->nNode.GetNode().GetTextNode();
+ if ( pTNd )
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, aLinkRect, nDestId, aURL, bIntern, formatName);
+ }
+ }
+ }
+ }
+ }
+ else if (pFrameFormat->Which() == RES_DRAWFRMFMT)
+ {
+ // Turn media shapes into Screen annotations.
+ if (SdrObject* pObject = pFrameFormat->FindRealSdrObject())
+ {
+ SwRect aSnapRect(pObject->GetSnapRect());
+ std::vector<sal_Int32> aScreenPageNums = CalcOutputPageNums(aSnapRect);
+ if (aScreenPageNums.empty())
+ continue;
+
+ uno::Reference<drawing::XShape> xShape(pObject->getUnoShape(), uno::UNO_QUERY);
+ if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape")
+ {
+ uno::Reference<beans::XPropertySet> xShapePropSet(xShape, uno::UNO_QUERY);
+ OUString aMediaURL;
+ xShapePropSet->getPropertyValue("MediaURL") >>= aMediaURL;
+ if (!aMediaURL.isEmpty())
+ {
+ const SwPageFrame* pCurrPage = mrSh.GetLayout()->GetPageAtPos(aSnapRect.Center());
+ tools::Rectangle aPDFRect(SwRectToPDFRect(pCurrPage, aSnapRect.SVRect()));
+ for (sal_Int32 nScreenPageNum : aScreenPageNums)
+ {
+ sal_Int32 nScreenId = pPDFExtOutDevData->CreateScreen(aPDFRect, nScreenPageNum);
+ if (aMediaURL.startsWith("vnd.sun.star.Package:"))
+ {
+ // Embedded media.
+ OUString aTempFileURL;
+ xShapePropSet->getPropertyValue("PrivateTempFileURL") >>= aTempFileURL;
+ pPDFExtOutDevData->SetScreenStream(nScreenId, aTempFileURL);
+ }
+ else
+ // Linked media.
+ pPDFExtOutDevData->SetScreenURL(nScreenId, aMediaURL);
+ }
+ }
+ }
+ }
+ }
+ mrSh.SwCursorShell::ClearMark();
+ }
+
+ // REFERENCES
+
+ std::vector<SwFormatField*> vpFields;
+ mrSh.GetFieldType( SwFieldIds::GetRef, OUString() )->GatherFields(vpFields);
+ for(auto pFormatField : vpFields )
+ {
+ if( pFormatField->GetTextField() && pFormatField->IsFieldInDoc() )
+ {
+ const SwTextNode* pTNd = pFormatField->GetTextField()->GetpTextNode();
+ OSL_ENSURE( nullptr != pTNd, "Enhanced pdf export - text node is missing" );
+ if(!lcl_TryMoveToNonHiddenField(mrSh, *pTNd, *pFormatField))
+ continue;
+ // Select the field:
+ mrSh.SwCursorShell::SetMark();
+ mrSh.SwCursorShell::Right( 1, CRSR_SKIP_CHARS );
+
+ // Link Rectangles
+ SwRects aTmp;
+ aTmp.insert( aTmp.begin(), mrSh.SwCursorShell::GetCursor_()->begin(), mrSh.SwCursorShell::GetCursor_()->end() );
+ OSL_ENSURE( !aTmp.empty(), "Enhanced pdf export - rectangles are missing" );
+
+ mrSh.SwCursorShell::ClearMark();
+
+ // Destination Rectangle
+ const SwGetRefField* pField = static_cast<SwGetRefField*>(pFormatField->GetField());
+ const OUString& rRefName = pField->GetSetRefName();
+ mrSh.GotoRefMark( rRefName, pField->GetSubType(), pField->GetSeqNo() );
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ const SwPageFrame* pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ if ( -1 != nDestPageNum )
+ {
+ // Destination Export
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+
+ // #i44368# Links in Header/Footer
+ const SwPosition aPos( *pTNd );
+ const bool bHeaderFooter = pDoc->IsInHeaderFooter( aPos.nNode );
+ OUString const content(pField->ExpandField(true, mrSh.GetLayout()));
+
+ // Create links for all selected rectangles:
+ const size_t nNumOfRects = aTmp.size();
+ for ( size_t i = 0; i < nNumOfRects; ++i )
+ {
+ // Link rectangle
+ const SwRect& rLinkRect( aTmp[ i ] );
+
+ // Link PageNums
+ std::vector<sal_Int32> aLinkPageNums = CalcOutputPageNums( rLinkRect );
+
+ for (sal_Int32 aLinkPageNum : aLinkPageNums)
+ {
+ // Link Export
+ aRect = SwRectToPDFRect(pCurrPage, rLinkRect.SVRect());
+ const sal_Int32 nLinkId =
+ pPDFExtOutDevData->CreateLink(aRect, content, aLinkPageNum);
+
+ // Store link info for tagged pdf output:
+ const IdMapEntry aLinkEntry( rLinkRect, nLinkId );
+ s_aLinkIdMap.push_back( aLinkEntry );
+
+ // Connect Link and Destination:
+ pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
+
+ // #i44368# Links in Header/Footer
+ if ( bHeaderFooter )
+ {
+ MakeHeaderFooterLinks(*pPDFExtOutDevData, *pTNd, rLinkRect, nDestId, "", true, content);
+ }
+ }
+ }
+ }
+ }
+ mrSh.SwCursorShell::ClearMark();
+ }
+
+ ExportAuthorityEntryLinks();
+
+ // FOOTNOTES
+
+ const size_t nFootnoteCount = pDoc->GetFootnoteIdxs().size();
+ for ( size_t nIdx = 0; nIdx < nFootnoteCount; ++nIdx )
+ {
+ // Set cursor to text node that contains the footnote:
+ const SwTextFootnote* pTextFootnote = pDoc->GetFootnoteIdxs()[ nIdx ];
+ SwTextNode& rTNd = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
+
+ mrSh.GetCursor_()->GetPoint()->nNode = rTNd;
+ mrSh.GetCursor_()->GetPoint()->nContent.Assign( &rTNd, pTextFootnote->GetStart() );
+
+ // 1. Check if the whole paragraph is hidden
+ // 2. Check for hidden text attribute
+ if (rTNd.GetTextNode()->IsHidden() || mrSh.SelectHiddenRange()
+ || (mrSh.GetLayout()->IsHideRedlines()
+ && sw::IsFootnoteDeleted(pDoc->getIDocumentRedlineAccess(), *pTextFootnote)))
+ {
+ continue;
+ }
+
+ SwCursorSaveState aSaveState( *mrSh.GetCursor_() );
+
+ // Select the footnote:
+ mrSh.SwCursorShell::SetMark();
+ mrSh.SwCursorShell::Right( 1, CRSR_SKIP_CHARS );
+
+ // Link Rectangle
+ SwRects aTmp;
+ aTmp.insert( aTmp.begin(), mrSh.SwCursorShell::GetCursor_()->begin(), mrSh.SwCursorShell::GetCursor_()->end() );
+ OSL_ENSURE( !aTmp.empty(), "Enhanced pdf export - rectangles are missing" );
+
+ mrSh.GetCursor_()->RestoreSavePos();
+ mrSh.SwCursorShell::ClearMark();
+
+ if (aTmp.empty())
+ continue;
+
+ const SwRect aLinkRect( aTmp[ 0 ] );
+
+ // Goto footnote text:
+ if ( mrSh.GotoFootnoteText() )
+ {
+ // Destination Rectangle
+ const SwRect& rDestRect = mrSh.GetCharRect();
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+ if ( -1 != nDestPageNum )
+ {
+ const SwPageFrame* pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+ // Destination PageNum
+ tools::Rectangle aRect = SwRectToPDFRect(pCurrPage, rDestRect.SVRect());
+ // Back link rectangle calculation
+ const SwPageFrame* fnBodyPage = pCurrPage->getRootFrame()->GetPageByPageNum(nDestPageNum+1);
+ SwRect fnSymbolRect;
+ if (fnBodyPage->IsVertical()){
+ tools::Long fnSymbolTop = fnBodyPage->GetTopMargin() + fnBodyPage->getFrameArea().Top();
+ tools::Long symbolHeight = rDestRect.Top() - fnSymbolTop;
+ fnSymbolRect = SwRect(rDestRect.Pos().X(),fnSymbolTop,rDestRect.Width(),symbolHeight);
+ } else {
+ if (fnBodyPage->IsRightToLeft()){
+ tools::Long fnSymbolRight = fnBodyPage->getFrameArea().Right() - fnBodyPage->GetRightMargin();
+ tools::Long symbolWidth = fnSymbolRight - rDestRect.Right();
+ fnSymbolRect = SwRect(rDestRect.Pos().X(),rDestRect.Pos().Y(),symbolWidth,rDestRect.Height());
+ } else {
+ tools::Long fnSymbolLeft = fnBodyPage->GetLeftMargin() + fnBodyPage->getFrameArea().Left();
+ tools::Long symbolWidth = rDestRect.Left() - fnSymbolLeft;
+ fnSymbolRect = SwRect(fnSymbolLeft,rDestRect.Pos().Y(),symbolWidth,rDestRect.Height());
+ }
+ }
+ tools::Rectangle aFootnoteSymbolRect = SwRectToPDFRect(pCurrPage, fnSymbolRect.SVRect());
+
+ OUString const numStrSymbol(pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, mrSh.GetLayout(), true));
+ OUString const numStrRef(pTextFootnote->GetFootnote().GetViewNumStr(*pDoc, mrSh.GetLayout(), false));
+
+ // Export back link
+ const sal_Int32 nBackLinkId = pPDFExtOutDevData->CreateLink(aFootnoteSymbolRect, numStrSymbol, nDestPageNum);
+ // Destination Export
+ const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+ mrSh.GotoFootnoteAnchor();
+ // Link PageNums
+ sal_Int32 aLinkPageNum = CalcOutputPageNum( aLinkRect );
+ pCurrPage = static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+ // Link Export
+ aRect = SwRectToPDFRect(pCurrPage, aLinkRect.SVRect());
+ const sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, numStrRef, aLinkPageNum);
+ // Back link destination Export
+ const sal_Int32 nBackDestId = pPDFExtOutDevData->CreateDest(aRect, aLinkPageNum);
+ // Store link info for tagged pdf output:
+ const IdMapEntry aLinkEntry( aLinkRect, nLinkId );
+ s_aLinkIdMap.push_back( aLinkEntry );
+
+ // Store backlink info for tagged pdf output:
+ const IdMapEntry aBackLinkEntry( aFootnoteSymbolRect, nBackLinkId );
+ s_aLinkIdMap.push_back( aBackLinkEntry );
+ // Connect Links and Destinations:
+ pPDFExtOutDevData->SetLinkDest( nLinkId, nDestId );
+ pPDFExtOutDevData->SetLinkDest( nBackLinkId, nBackDestId );
+ }
+ }
+ }
+
+ // OUTLINE
+
+ if( pPDFExtOutDevData->GetIsExportBookmarks() )
+ {
+ typedef std::pair< sal_Int8, sal_Int32 > StackEntry;
+ std::stack< StackEntry > aOutlineStack;
+ aOutlineStack.push( StackEntry( -1, -1 ) ); // push default value
+
+ const SwOutlineNodes::size_type nOutlineCount =
+ mrSh.getIDocumentOutlineNodesAccess()->getOutlineNodesCount();
+ for ( SwOutlineNodes::size_type i = 0; i < nOutlineCount; ++i )
+ {
+ // Check if outline is hidden
+ const SwTextNode* pTNd = mrSh.GetNodes().GetOutLineNds()[ i ]->GetTextNode();
+ OSL_ENSURE( nullptr != pTNd, "Enhanced pdf export - text node is missing" );
+
+ if ( pTNd->IsHidden() ||
+ !sw::IsParaPropsNode(*mrSh.GetLayout(), *pTNd) ||
+ // #i40292# Skip empty outlines:
+ pTNd->GetText().isEmpty())
+ continue;
+
+ // Get parent id from stack:
+ const sal_Int8 nLevel = static_cast<sal_Int8>(mrSh.getIDocumentOutlineNodesAccess()->getOutlineLevel( i ));
+ sal_Int8 nLevelOnTopOfStack = aOutlineStack.top().first;
+ while ( nLevelOnTopOfStack >= nLevel &&
+ nLevelOnTopOfStack != -1 )
+ {
+ aOutlineStack.pop();
+ nLevelOnTopOfStack = aOutlineStack.top().first;
+ }
+ const sal_Int32 nParent = aOutlineStack.top().second;
+
+ // Destination rectangle
+ mrSh.GotoOutline(i);
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ if ( -1 != nDestPageNum )
+ {
+ // Destination Export
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ const sal_Int32 nDestId =
+ pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+
+ // Outline entry text
+ const OUString& rEntry = mrSh.getIDocumentOutlineNodesAccess()->getOutlineText(
+ i, mrSh.GetLayout(), true, false, false );
+
+ // Create a new outline item:
+ const sal_Int32 nOutlineId =
+ pPDFExtOutDevData->CreateOutlineItem( nParent, rEntry, nDestId );
+
+ // Push current level and nOutlineId on stack:
+ aOutlineStack.push( StackEntry( nLevel, nOutlineId ) );
+ }
+ }
+ }
+
+ if( pPDFExtOutDevData->GetIsExportNamedDestinations() )
+ {
+ // #i56629# the iteration to convert the OOo bookmark (#bookmark)
+ // into PDF named destination, see section 8.2.1 in PDF 1.4 spec
+ // We need:
+ // 1. a name for the destination, formed from the standard OOo bookmark name
+ // 2. the destination, obtained from where the bookmark destination lies
+ IDocumentMarkAccess* const pMarkAccess = mrSh.GetDoc()->getIDocumentMarkAccess();
+ for(IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getBookmarksBegin();
+ ppMark != pMarkAccess->getBookmarksEnd();
+ ++ppMark)
+ {
+ //get the name
+ const ::sw::mark::IMark* pBkmk = *ppMark;
+ mrSh.SwCursorShell::ClearMark();
+ const OUString& sBkName = pBkmk->GetName();
+
+ //jump to it
+ if (! JumpToSwMark( &mrSh, sBkName ))
+ {
+ continue;
+ }
+
+ // Destination Rectangle
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ // Destination Export
+ if ( -1 != nDestPageNum )
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ pPDFExtOutDevData->CreateNamedDest(sBkName, aRect, nDestPageNum);
+ }
+ }
+ mrSh.SwCursorShell::ClearMark();
+ //<--- i56629
+ }
+ }
+ else
+ {
+
+ // LINKS FROM EDITENGINE
+
+ std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
+ for ( const auto& rBookmark : rBookmarks )
+ {
+ OUString aBookmarkName( rBookmark.aBookmark );
+ const bool bIntern = '#' == aBookmarkName[0];
+ if ( bIntern )
+ {
+ aBookmarkName = aBookmarkName.copy( 1 );
+ JumpToSwMark( &mrSh, aBookmarkName );
+
+ // Destination Rectangle
+ const SwRect& rDestRect = mrSh.GetCharRect();
+
+ const SwPageFrame* pCurrPage =
+ static_cast<const SwPageFrame*>( mrSh.GetLayout()->Lower() );
+
+ // Destination PageNum
+ const sal_Int32 nDestPageNum = CalcOutputPageNum( rDestRect );
+
+ if ( -1 != nDestPageNum )
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pCurrPage, rDestRect.SVRect()));
+ if ( rBookmark.nLinkId != -1 )
+ {
+ // Destination Export
+ const sal_Int32 nDestId = pPDFExtOutDevData->CreateDest(aRect, nDestPageNum);
+
+ // Connect Link and Destination:
+ pPDFExtOutDevData->SetLinkDest( rBookmark.nLinkId, nDestId );
+ }
+ else
+ {
+ pPDFExtOutDevData->DescribeRegisteredDest(rBookmark.nDestId, aRect, nDestPageNum);
+ }
+ }
+ }
+ else
+ pPDFExtOutDevData->SetLinkURL( rBookmark.nLinkId, aBookmarkName );
+ }
+ rBookmarks.clear();
+ }
+
+ // Restore view, cursor, and outdev:
+ mrSh.LockView( bOldLockView );
+ mrSh.SwCursorShell::Pop(SwCursorShell::PopMode::DeleteCurrent);
+ mrOut.Pop();
+}
+
+void SwEnhancedPDFExportHelper::ExportAuthorityEntryLinks()
+{
+ auto pPDFExtOutDevData = dynamic_cast<vcl::PDFExtOutDevData*>(mrOut.GetExtOutDevData());
+ if (!pPDFExtOutDevData)
+ {
+ return;
+ }
+
+ std::vector<SwFormatField*> aFields;
+ SwFieldType* pType = mrSh.GetFieldType(SwFieldIds::TableOfAuthorities, OUString());
+ if (!pType)
+ {
+ return;
+ }
+
+ pType->GatherFields(aFields);
+ const auto pPageFrame = static_cast<const SwPageFrame*>(mrSh.GetLayout()->Lower());
+ for (const auto pFormatField : aFields)
+ {
+ if (!pFormatField->GetTextField() || !pFormatField->IsFieldInDoc())
+ {
+ continue;
+ }
+
+ const auto& rAuthorityField
+ = *static_cast<const SwAuthorityField*>(pFormatField->GetField());
+ if (!rAuthorityField.HasURL())
+ {
+ continue;
+ }
+
+ const OUString& rURL = rAuthorityField.GetAuthEntry()->GetAuthorField(AUTH_FIELD_URL);
+ const SwTextNode& rTextNode = pFormatField->GetTextField()->GetTextNode();
+ if (!lcl_TryMoveToNonHiddenField(mrSh, rTextNode, *pFormatField))
+ {
+ continue;
+ }
+
+ OUString const content(rAuthorityField.ExpandField(true, mrSh.GetLayout()));
+
+ // Select the field.
+ mrSh.SwCursorShell::SetMark();
+ mrSh.SwCursorShell::Right(1, CRSR_SKIP_CHARS);
+
+ // Create the links.
+ for (const auto& rLinkRect : *mrSh.SwCursorShell::GetCursor_())
+ {
+ for (const auto& rLinkPageNum : CalcOutputPageNums(rLinkRect))
+ {
+ tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, rLinkRect.SVRect()));
+ sal_Int32 nLinkId = pPDFExtOutDevData->CreateLink(aRect, content, rLinkPageNum);
+ IdMapEntry aLinkEntry(rLinkRect, nLinkId);
+ s_aLinkIdMap.push_back(aLinkEntry);
+ pPDFExtOutDevData->SetLinkURL(nLinkId, rURL);
+ }
+ }
+
+ mrSh.SwCursorShell::ClearMark();
+ }
+}
+
+// Returns the page number in the output pdf on which the given rect is located.
+// If this page is duplicated, method will return first occurrence of it.
+sal_Int32 SwEnhancedPDFExportHelper::CalcOutputPageNum( const SwRect& rRect ) const
+{
+ std::vector< sal_Int32 > aPageNums = CalcOutputPageNums( rRect );
+ if ( !aPageNums.empty() )
+ return aPageNums[0];
+ return -1;
+}
+
+// Returns a vector of the page numbers in the output pdf on which the given
+// rect is located. There can be many such pages since StringRangeEnumerator
+// allows duplication of its entries.
+std::vector< sal_Int32 > SwEnhancedPDFExportHelper::CalcOutputPageNums(
+ const SwRect& rRect ) const
+{
+ std::vector< sal_Int32 > aPageNums;
+
+ // Document page number.
+ sal_Int32 nPageNumOfRect = mrSh.GetPageNumAndSetOffsetForPDF( mrOut, rRect );
+ if ( nPageNumOfRect < 0 )
+ return aPageNums;
+
+ // What will be the page numbers of page nPageNumOfRect in the output pdf?
+ if ( mpRangeEnum )
+ {
+ if ( mbSkipEmptyPages )
+ // Map the page number to the range without empty pages.
+ nPageNumOfRect = maPageNumberMap[ nPageNumOfRect ];
+
+ if ( mpRangeEnum->hasValue( nPageNumOfRect ) )
+ {
+ sal_Int32 nOutputPageNum = 0;
+ StringRangeEnumerator::Iterator aIter = mpRangeEnum->begin();
+ StringRangeEnumerator::Iterator aEnd = mpRangeEnum->end();
+ for ( ; aIter != aEnd; ++aIter )
+ {
+ if ( *aIter == nPageNumOfRect )
+ aPageNums.push_back( nOutputPageNum );
+ ++nOutputPageNum;
+ }
+ }
+ }
+ else
+ {
+ if ( mbSkipEmptyPages )
+ {
+ sal_Int32 nOutputPageNum = 0;
+ for ( size_t i = 0; i < maPageNumberMap.size(); ++i )
+ {
+ if ( maPageNumberMap[i] >= 0 ) // is not empty?
+ {
+ if ( i == static_cast<size_t>( nPageNumOfRect ) )
+ {
+ aPageNums.push_back( nOutputPageNum );
+ break;
+ }
+ ++nOutputPageNum;
+ }
+ }
+ }
+ else
+ aPageNums.push_back( nPageNumOfRect );
+ }
+
+ return aPageNums;
+}
+
+void SwEnhancedPDFExportHelper::MakeHeaderFooterLinks( vcl::PDFExtOutDevData& rPDFExtOutDevData,
+ const SwTextNode& rTNd,
+ const SwRect& rLinkRect,
+ sal_Int32 nDestId,
+ const OUString& rURL,
+ bool bIntern,
+ OUString const& rContent) const
+{
+ // We assume, that the primary link has just been exported. Therefore
+ // the offset of the link rectangle calculates as follows:
+ const Point aOffset = rLinkRect.Pos() + mrOut.GetMapMode().GetOrigin();
+
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTNd);
+ for ( SwTextFrame* pTmpFrame = aIter.First(); pTmpFrame; pTmpFrame = aIter.Next() )
+ {
+ // Add offset to current page:
+ const SwPageFrame* pPageFrame = pTmpFrame->FindPageFrame();
+ SwRect aHFLinkRect( rLinkRect );
+ aHFLinkRect.Pos() = pPageFrame->getFrameArea().Pos() + aOffset;
+
+ // #i97135# the gcc_x64 optimizer gets aHFLinkRect != rLinkRect wrong
+ // fool it by comparing the position only (the width and height are the
+ // same anyway)
+ if ( aHFLinkRect.Pos() != rLinkRect.Pos() )
+ {
+ // Link PageNums
+ std::vector<sal_Int32> aHFLinkPageNums = CalcOutputPageNums( aHFLinkRect );
+
+ for (sal_Int32 aHFLinkPageNum : aHFLinkPageNums)
+ {
+ // Link Export
+ tools::Rectangle aRect(SwRectToPDFRect(pPageFrame, aHFLinkRect.SVRect()));
+ const sal_Int32 nHFLinkId =
+ rPDFExtOutDevData.CreateLink(aRect, rContent, aHFLinkPageNum);
+
+ // Connect Link and Destination:
+ if ( bIntern )
+ rPDFExtOutDevData.SetLinkDest( nHFLinkId, nDestId );
+ else
+ rPDFExtOutDevData.SetLinkURL( nHFLinkId, rURL );
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/SwGrammarMarkUp.cxx b/sw/source/core/text/SwGrammarMarkUp.cxx
new file mode 100644
index 000000000..53fd4b13c
--- /dev/null
+++ b/sw/source/core/text/SwGrammarMarkUp.cxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwGrammarMarkUp.hxx>
+
+SwGrammarMarkUp::~SwGrammarMarkUp()
+{
+}
+
+SwWrongList* SwGrammarMarkUp::Clone()
+{
+ SwWrongList* pClone = new SwGrammarMarkUp();
+ pClone->CopyFrom( *this );
+ return pClone;
+}
+
+void SwGrammarMarkUp::CopyFrom( const SwWrongList& rCopy )
+{
+ maSentence = static_cast<const SwGrammarMarkUp&>(rCopy).maSentence;
+ SwWrongList::CopyFrom( rCopy );
+}
+
+void SwGrammarMarkUp::MoveGrammar( sal_Int32 nPos, sal_Int32 nDiff )
+{
+ Move( nPos, nDiff );
+ if( maSentence.empty() )
+ return;
+ auto pIter = std::find_if(maSentence.begin(), maSentence.end(),
+ [nPos](const sal_Int32& rPos) { return rPos >= nPos; });
+ const sal_Int32 nEnd = nDiff < 0 ? nPos-nDiff : nPos;
+ while( pIter != maSentence.end() )
+ {
+ if( *pIter >= nEnd )
+ *pIter += nDiff;
+ else
+ *pIter = nPos;
+ ++pIter;
+ }
+}
+
+std::unique_ptr<SwGrammarMarkUp> SwGrammarMarkUp::SplitGrammarList( sal_Int32 nSplitPos )
+{
+ std::unique_ptr<SwGrammarMarkUp> pNew( static_cast<SwGrammarMarkUp*>(SplitList( nSplitPos ).release()) );
+ if( maSentence.empty() )
+ return pNew;
+ auto pIter = std::find_if(maSentence.begin(), maSentence.end(),
+ [nSplitPos](const sal_Int32& rPos) { return rPos >= nSplitPos; });
+ if( pIter != maSentence.begin() )
+ {
+ if( !pNew ) {
+ pNew.reset(new SwGrammarMarkUp());
+ pNew->SetInvalid( 0, COMPLETE_STRING );
+ }
+ pNew->maSentence.insert( pNew->maSentence.begin(), maSentence.begin(), pIter );
+ maSentence.erase( maSentence.begin(), pIter );
+ }
+ return pNew;
+}
+
+void SwGrammarMarkUp::JoinGrammarList( SwGrammarMarkUp* pNext, sal_Int32 nInsertPos )
+{
+ JoinList( pNext, nInsertPos );
+ if (pNext)
+ {
+ if( pNext->maSentence.empty() )
+ return;
+ for( auto& rPos : pNext->maSentence )
+ {
+ rPos += nInsertPos;
+ }
+ maSentence.insert( maSentence.end(), pNext->maSentence.begin(), pNext->maSentence.end() );
+ }
+}
+
+void SwGrammarMarkUp::ClearGrammarList( sal_Int32 nSentenceEnd )
+{
+ if( COMPLETE_STRING == nSentenceEnd ) {
+ ClearList();
+ maSentence.clear();
+ Validate();
+ } else if( GetBeginInv() <= nSentenceEnd ) {
+ std::vector< sal_Int32 >::iterator pIter = maSentence.begin();
+ sal_Int32 nStart = 0;
+ while( pIter != maSentence.end() && *pIter < GetBeginInv() )
+ {
+ nStart = *pIter;
+ ++pIter;
+ }
+ auto pLast = std::find_if(pIter, maSentence.end(),
+ [nSentenceEnd](const sal_Int32& rPos) { return rPos > nSentenceEnd; });
+ maSentence.erase( pIter, pLast );
+ RemoveEntry( nStart, nSentenceEnd );
+ SetInvalid( nSentenceEnd + 1, COMPLETE_STRING );
+ }
+}
+
+void SwGrammarMarkUp::setSentence( sal_Int32 nStart )
+{
+ auto pIter = std::find_if(maSentence.begin(), maSentence.end(),
+ [nStart](const sal_Int32& rPos) { return rPos >= nStart; });
+ if( pIter == maSentence.end() || *pIter > nStart )
+ maSentence.insert( pIter, nStart );
+}
+
+sal_Int32 SwGrammarMarkUp::getSentenceStart( sal_Int32 nPos )
+{
+ if( maSentence.empty() )
+ return 0;
+ auto pIter = std::find_if(maSentence.begin(), maSentence.end(),
+ [nPos](const sal_Int32& rPos) { return rPos >= nPos; });
+ if( pIter != maSentence.begin() )
+ --pIter;
+ if( pIter != maSentence.end() && *pIter < nPos )
+ return *pIter;
+ return 0;
+}
+
+sal_Int32 SwGrammarMarkUp::getSentenceEnd( sal_Int32 nPos )
+{
+ if( maSentence.empty() )
+ return COMPLETE_STRING;
+ auto pIter = std::find_if(maSentence.begin(), maSentence.end(),
+ [nPos](const sal_Int32& rPos) { return rPos > nPos; });
+ if( pIter != maSentence.end() )
+ return *pIter;
+ return COMPLETE_STRING;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx
new file mode 100644
index 000000000..851615325
--- /dev/null
+++ b/sw/source/core/text/atrhndl.hxx
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+#define NUM_ATTRIBUTE_STACKS 45
+
+#include <vector>
+#include <swfntcch.hxx>
+
+class SwTextAttr;
+class SwAttrSet;
+class IDocumentSettingAccess;
+class SwViewShell;
+class SfxPoolItem;
+extern const sal_uInt8 StackPos[];
+
+/**
+ * Used by Attribute Iterators to organize attributes on stacks to
+ * find the valid attribute in each category
+ */
+class SwAttrHandler
+{
+private:
+ std::vector<const SwTextAttr*> m_aAttrStack[NUM_ATTRIBUTE_STACKS]; // stack collection
+ const SfxPoolItem* m_pDefaultArray[ NUM_DEFAULT_VALUES ];
+ const IDocumentSettingAccess* m_pIDocumentSettingAccess;
+ const SwViewShell* m_pShell;
+
+ // This is the base font for the paragraph. It is stored in order to have
+ // a template, if we have to restart the attribute evaluation
+ std::optional<SwFont> m_oFnt;
+
+ bool m_bVertLayout;
+ bool m_bVertLayoutLRBT;
+
+ const SwTextAttr* GetTop(sal_uInt16 nStack);
+ void RemoveFromStack(sal_uInt16 nWhich, const SwTextAttr& rAttr);
+
+ // change font according to pool item
+ void FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush );
+
+ // push attribute to specified stack, returns true, if attribute has
+ // been pushed on top of stack (important for stacks containing different
+ // attributes with different priority and redlining)
+ bool Push( const SwTextAttr& rAttr, const SfxPoolItem& rItem );
+
+ // apply top attribute on stack to font
+ void ActivateTop( SwFont& rFnt, sal_uInt16 nStackPos );
+
+public:
+ // Ctor
+ SwAttrHandler();
+ ~SwAttrHandler();
+
+ // set default attributes to values in rAttrSet or from cache
+ void Init( const SwAttrSet& rAttrSet,
+ const IDocumentSettingAccess& rIDocumentSettingAccess );
+ void Init( const SfxPoolItem** pPoolItem, const SwAttrSet* pAttrSet,
+ const IDocumentSettingAccess& rIDocumentSettingAccess,
+ const SwViewShell* pShell, SwFont& rFnt,
+ bool bVertLayout, bool bVertLayoutLRBT );
+
+ bool IsVertLayout() const { return m_bVertLayout; }
+
+ // remove everything from internal stacks, keep default data
+ void Reset( );
+
+ // insert specified attribute and change font
+ void PushAndChg( const SwTextAttr& rAttr, SwFont& rFnt );
+
+ // remove specified attribute and reset font
+ void PopAndChg( const SwTextAttr& rAttr, SwFont& rFnt );
+ void Pop( const SwTextAttr& rAttr );
+
+ // apply script dependent attributes
+ // void ChangeScript( SwFont& rFnt, const sal_uInt8 nScr );
+
+ // do not call these if you only used the small init function
+ inline void ResetFont( SwFont& rFnt ) const;
+ inline const SwFont* GetFont() const;
+
+ void GetDefaultAscentAndHeight(SwViewShell const * pShell,
+ OutputDevice const & rOut,
+ sal_uInt16& nAscent,
+ sal_uInt16& nHeight) const;
+};
+
+inline void SwAttrHandler::ResetFont( SwFont& rFnt ) const
+{
+ OSL_ENSURE(m_oFnt, "ResetFont without a font");
+ if (m_oFnt)
+ rFnt = *m_oFnt;
+};
+
+inline const SwFont* SwAttrHandler::GetFont() const
+{
+ return m_oFnt ? &*m_oFnt : nullptr;
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx
new file mode 100644
index 000000000..7fab6da10
--- /dev/null
+++ b/sw/source/core/text/atrstck.cxx
@@ -0,0 +1,851 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "atrhndl.hxx"
+#include <svl/itemiter.hxx>
+#include <vcl/outdev.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <viewopt.hxx>
+#include <charfmt.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <editeng/brushitem.hxx>
+#include <fmtinfmt.hxx>
+#include <txtinet.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <viewsh.hxx>
+
+/**
+ * Attribute to Stack Mapping
+ *
+ * Attributes applied to a text are pushed on different stacks. For each
+ * stack, the top most attribute on the stack is valid. Because some
+ * kinds of attributes have to be pushed to the same stacks we map their
+ * ids to stack ids
+ * Attention: The first NUM_DEFAULT_VALUES ( defined in swfntcch.hxx )
+ * are stored in the defaultitem-cache, if you add one, you have to increase
+ * NUM_DEFAULT_VALUES.
+ * Also adjust NUM_ATTRIBUTE_STACKS in atrhndl.hxx.
+ */
+const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN + 1 ] =
+{
+ 0, // // 0
+ 1, // RES_CHRATR_CASEMAP = RES_CHRATR_BEGIN // 1
+ 0, // RES_CHRATR_CHARSETCOLOR, // 2
+ 2, // RES_CHRATR_COLOR, // 3
+ 3, // RES_CHRATR_CONTOUR, // 4
+ 4, // RES_CHRATR_CROSSEDOUT, // 5
+ 5, // RES_CHRATR_ESCAPEMENT, // 6
+ 6, // RES_CHRATR_FONT, // 7
+ 7, // RES_CHRATR_FONTSIZE, // 8
+ 8, // RES_CHRATR_KERNING, // 9
+ 9, // RES_CHRATR_LANGUAGE, // 10
+ 10, // RES_CHRATR_POSTURE, // 11
+ 0, // RES_CHRATR_UNUSED1, // 12
+ 11, // RES_CHRATR_SHADOWED, // 13
+ 12, // RES_CHRATR_UNDERLINE, // 14
+ 13, // RES_CHRATR_WEIGHT, // 15
+ 14, // RES_CHRATR_WORDLINEMODE, // 16
+ 15, // RES_CHRATR_AUTOKERN, // 17
+ 16, // RES_CHRATR_BLINK, // 18
+ 17, // RES_CHRATR_NOHYPHEN, // 19
+ 0, // RES_CHRATR_UNUSED2, // 20
+ 18, // RES_CHRATR_BACKGROUND, // 21
+ 19, // RES_CHRATR_CJK_FONT, // 22
+ 20, // RES_CHRATR_CJK_FONTSIZE, // 23
+ 21, // RES_CHRATR_CJK_LANGUAGE, // 24
+ 22, // RES_CHRATR_CJK_POSTURE, // 25
+ 23, // RES_CHRATR_CJK_WEIGHT, // 26
+ 24, // RES_CHRATR_CTL_FONT, // 27
+ 25, // RES_CHRATR_CTL_FONTSIZE, // 28
+ 26, // RES_CHRATR_CTL_LANGUAGE, // 29
+ 27, // RES_CHRATR_CTL_POSTURE, // 30
+ 28, // RES_CHRATR_CTL_WEIGHT, // 31
+ 29, // RES_CHRATR_ROTATE, // 32
+ 30, // RES_CHRATR_EMPHASIS_MARK, // 33
+ 31, // RES_CHRATR_TWO_LINES, // 34
+ 32, // RES_CHRATR_SCALEW, // 35
+ 33, // RES_CHRATR_RELIEF, // 36
+ 34, // RES_CHRATR_HIDDEN, // 37
+ 35, // RES_CHRATR_OVERLINE, // 38
+ 0, // RES_CHRATR_RSID, // 39
+ 36, // RES_CHRATR_BOX, // 40
+ 37, // RES_CHRATR_SHADOW, // 41
+ 38, // RES_CHRATR_HIGHLIGHT, // 42
+ 0, // RES_CHRATR_GRABBAG, // 43
+ 0, // RES_CHRATR_BIDIRTL, // 44
+ 0, // RES_CHRATR_IDCTHINT, // 45
+ 39, // RES_TXTATR_REFMARK, // 46
+ 40, // RES_TXTATR_TOXMARK, // 47
+ 41, // RES_TXTATR_META, // 48
+ 41, // RES_TXTATR_METAFIELD, // 49
+ 0, // RES_TXTATR_AUTOFMT, // 50
+ 0, // RES_TXTATR_INETFMT // 51
+ 0, // RES_TXTATR_CHARFMT, // 52
+ 42, // RES_TXTATR_CJK_RUBY, // 53
+ 0, // RES_TXTATR_UNKNOWN_CONTAINER, // 54
+ 43, // RES_TXTATR_INPUTFIELD // 55
+ 44, // RES_TXTATR_CONTENTCONTROL // 56
+};
+
+namespace CharFormat
+{
+
+/// Returns the item set associated with a character/inet/auto style
+const SfxItemSet* GetItemSet( const SfxPoolItem& rAttr )
+{
+ const SfxItemSet* pSet = nullptr;
+
+ if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ pSet = rAttr.StaticWhichCast(RES_TXTATR_AUTOFMT).GetStyleHandle().get();
+ }
+ else
+ {
+ // Get the attributes from the template
+ const SwCharFormat* pFormat = RES_TXTATR_INETFMT == rAttr.Which() ?
+ rAttr.StaticWhichCast(RES_TXTATR_INETFMT).GetTextINetFormat()->GetCharFormat() :
+ static_cast<const SwFormatCharFormat&>(rAttr).GetCharFormat();
+ if( pFormat )
+ {
+ pSet = &pFormat->GetAttrSet();
+ }
+ }
+
+ return pSet;
+}
+
+/// Extracts pool item of type nWhich from rAttr
+const SfxPoolItem* GetItem( const SwTextAttr& rAttr, sal_uInt16 nWhich )
+{
+ if ( RES_TXTATR_INETFMT == rAttr.Which() ||
+ RES_TXTATR_CHARFMT == rAttr.Which() ||
+ RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
+ if ( !pSet ) return nullptr;
+
+ bool bInParent = RES_TXTATR_AUTOFMT != rAttr.Which();
+ const SfxPoolItem* pItem;
+ bool bRet = SfxItemState::SET == pSet->GetItemState( nWhich, bInParent, &pItem );
+
+ return bRet ? pItem : nullptr;
+ }
+
+ return ( nWhich == rAttr.Which() ) ? &rAttr.GetAttr() : nullptr;
+}
+
+/// Checks if item is included in character/inet/auto style
+bool IsItemIncluded( const sal_uInt16 nWhich, const SwTextAttr *pAttr )
+{
+ bool bRet = false;
+
+ const SfxItemSet* pItemSet = CharFormat::GetItemSet( pAttr->GetAttr() );
+ if ( pItemSet )
+ bRet = SfxItemState::SET == pItemSet->GetItemState( nWhich );
+
+ return bRet;
+}
+}
+
+/**
+ * The color of hyperlinks is taken from the associated character attribute,
+ * depending on its 'visited' state. There are actually two cases, which
+ * should override the colors from the character attribute:
+ * 1. We never take the 'visited' color during printing/pdf export/preview
+ * 2. The user has chosen to override these colors in the view options
+ */
+static bool lcl_ChgHyperLinkColor( const SwTextAttr& rAttr,
+ const SfxPoolItem& rItem,
+ const SwViewShell* pShell,
+ Color* pColor )
+{
+ if ( !pShell ||
+ RES_TXTATR_INETFMT != rAttr.Which() ||
+ RES_CHRATR_COLOR != rItem.Which() )
+ return false;
+
+ // #i15455#
+ // 1. case:
+ // We do not want to show visited links:
+ // (printing, pdf export, page preview)
+
+ SwTextINetFormat & rINetAttr(const_cast<SwTextINetFormat&>(
+ static_txtattr_cast<SwTextINetFormat const&>(rAttr)));
+ if ( pShell->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
+ pShell->GetViewOptions()->IsPDFExport() ||
+ pShell->GetViewOptions()->IsPagePreview() )
+ {
+ if (rINetAttr.IsVisited())
+ {
+ if ( pColor )
+ {
+ // take color from character format 'unvisited link'
+ rINetAttr.SetVisited(false);
+ const SwCharFormat* pTmpFormat = rINetAttr.GetCharFormat();
+ if (const SvxColorItem* pItem = pTmpFormat->GetItemIfSet(RES_CHRATR_COLOR))
+ *pColor = pItem->GetValue();
+ rINetAttr.SetVisited(true);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ // 2. case:
+ // We do not want to apply the color set in the hyperlink
+ // attribute, instead we take the colors from the view options:
+
+ if ( pShell->GetWin() &&
+ (
+ (rINetAttr.IsVisited() && SwViewOption::IsVisitedLinks()) ||
+ (!rINetAttr.IsVisited() && SwViewOption::IsLinks())
+ )
+ )
+ {
+ if ( pColor )
+ {
+ if (rINetAttr.IsVisited())
+ {
+ // take color from view option 'visited link color'
+ *pColor = SwViewOption::GetVisitedLinksColor();
+ }
+ else
+ {
+ // take color from view option 'unvisited link color'
+ *pColor = SwViewOption::GetLinksColor();
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+SwAttrHandler::SwAttrHandler()
+ : m_pIDocumentSettingAccess(nullptr)
+ , m_pShell(nullptr)
+ , m_bVertLayout(false)
+ , m_bVertLayoutLRBT(false)
+{
+ memset( m_pDefaultArray, 0, NUM_DEFAULT_VALUES * sizeof(SfxPoolItem*) );
+}
+
+SwAttrHandler::~SwAttrHandler()
+{
+}
+
+void SwAttrHandler::Init( const SwAttrSet& rAttrSet,
+ const IDocumentSettingAccess& rIDocumentSettingAcces )
+{
+ m_pIDocumentSettingAccess = &rIDocumentSettingAcces;
+ m_pShell = nullptr;
+
+ for ( sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++ )
+ m_pDefaultArray[ StackPos[ i ] ] = &rAttrSet.Get( i );
+}
+
+void SwAttrHandler::Init( const SfxPoolItem** pPoolItem, const SwAttrSet* pAS,
+ const IDocumentSettingAccess& rIDocumentSettingAcces,
+ const SwViewShell* pSh,
+ SwFont& rFnt, bool bVL, bool bVertLayoutLRBT )
+{
+ // initialize default array
+ memcpy( m_pDefaultArray, pPoolItem,
+ NUM_DEFAULT_VALUES * sizeof(SfxPoolItem*) );
+
+ m_pIDocumentSettingAccess = &rIDocumentSettingAcces;
+ m_pShell = pSh;
+
+ // do we have to apply additional paragraph attributes?
+ m_bVertLayout = bVL;
+ m_bVertLayoutLRBT = bVertLayoutLRBT;
+
+ if ( pAS && pAS->Count() )
+ {
+ SfxItemIter aIter( *pAS );
+ sal_uInt16 nWhich;
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ nWhich = pItem->Which();
+ if (isCHRATR(nWhich))
+ {
+ m_pDefaultArray[ StackPos[ nWhich ] ] = pItem;
+ FontChg( *pItem, rFnt, true );
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+ }
+
+ // It is possible, that Init is called more than once, e.g., in a
+ // SwTextFrame::FormatOnceMore situation or (since sw_redlinehide)
+ // from SwAttrIter::Seek(); in the latter case SwTextSizeInfo::m_pFnt
+ // is an alias of m_pFnt so it must not be deleted!
+ if (m_oFnt)
+ *m_oFnt = rFnt;
+ else
+ m_oFnt.emplace(rFnt);
+}
+
+void SwAttrHandler::Reset( )
+{
+ for (auto& i : m_aAttrStack)
+ i.clear();
+}
+
+void SwAttrHandler::PushAndChg( const SwTextAttr& rAttr, SwFont& rFnt )
+{
+ // these special attributes in fact represent a collection of attributes
+ // they have to be pushed to each stack they belong to
+ if ( RES_TXTATR_INETFMT == rAttr.Which() ||
+ RES_TXTATR_CHARFMT == rAttr.Which() ||
+ RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
+ if ( !pSet ) return;
+
+ for ( sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++)
+ {
+ const SfxPoolItem* pItem;
+ bool bRet = SfxItemState::SET == pSet->GetItemState( i, rAttr.Which() != RES_TXTATR_AUTOFMT, &pItem );
+
+ if ( bRet )
+ {
+ // we push rAttr onto the appropriate stack
+ if ( Push( rAttr, *pItem ) )
+ {
+ // we let pItem change rFnt
+ Color aColor;
+ if (lcl_ChgHyperLinkColor(rAttr, *pItem, m_pShell, &aColor))
+ {
+ SvxColorItem aItemNext( aColor, RES_CHRATR_COLOR );
+ FontChg( aItemNext, rFnt, true );
+ }
+ else
+ FontChg( *pItem, rFnt, true );
+ }
+ }
+ }
+ }
+ // this is the usual case, we have a basic attribute, push it onto the
+ // stack and change the font
+ else
+ {
+ if ( Push( rAttr, rAttr.GetAttr() ) )
+ // we let pItem change rFnt
+ FontChg( rAttr.GetAttr(), rFnt, true );
+ }
+}
+
+const SwTextAttr* SwAttrHandler::GetTop(sal_uInt16 nStack)
+{
+ return m_aAttrStack[nStack].empty() ? nullptr : m_aAttrStack[nStack].back();
+}
+
+bool SwAttrHandler::Push( const SwTextAttr& rAttr, const SfxPoolItem& rItem )
+{
+ OSL_ENSURE( rItem.Which() < RES_TXTATR_WITHEND_END,
+ "I do not want this attribute, nWhich >= RES_TXTATR_WITHEND_END" );
+
+ // robust
+ if ( RES_TXTATR_WITHEND_END <= rItem.Which() )
+ return false;
+
+ const sal_uInt16 nStack = StackPos[ rItem.Which() ];
+
+ // attributes originating from redlining have highest priority
+ // second priority are hyperlink attributes, which have a color replacement
+ const SwTextAttr* pTopAttr = GetTop(nStack);
+ if ( !pTopAttr
+ || rAttr.IsPriorityAttr()
+ || ( !pTopAttr->IsPriorityAttr()
+ && !lcl_ChgHyperLinkColor(*pTopAttr, rItem, m_pShell, nullptr)))
+ {
+ m_aAttrStack[nStack].push_back(&rAttr);
+ return true;
+ }
+
+ const auto it = m_aAttrStack[nStack].end() - 1;
+ m_aAttrStack[nStack].insert(it, &rAttr);
+ return false;
+}
+
+void SwAttrHandler::RemoveFromStack(sal_uInt16 nWhich, const SwTextAttr& rAttr)
+{
+ auto& rStack = m_aAttrStack[StackPos[nWhich]];
+ const auto it = std::find(rStack.begin(), rStack.end(), &rAttr);
+ if (it != rStack.end())
+ rStack.erase(it);
+}
+
+void SwAttrHandler::PopAndChg( const SwTextAttr& rAttr, SwFont& rFnt )
+{
+ if ( RES_TXTATR_WITHEND_END <= rAttr.Which() )
+ return; // robust
+
+ // these special attributes in fact represent a collection of attributes
+ // they have to be removed from each stack they belong to
+ if ( RES_TXTATR_INETFMT == rAttr.Which() ||
+ RES_TXTATR_CHARFMT == rAttr.Which() ||
+ RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pSet = CharFormat::GetItemSet( rAttr.GetAttr() );
+ if ( !pSet ) return;
+
+ for ( sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++)
+ {
+ const SfxPoolItem* pItem;
+ bool bRet = SfxItemState::SET == pSet->GetItemState( i, RES_TXTATR_AUTOFMT != rAttr.Which(), &pItem );
+ if ( bRet )
+ {
+ // we remove rAttr from the appropriate stack
+ RemoveFromStack(i, rAttr);
+ // reset font according to attribute on top of stack
+ // or default value
+ ActivateTop( rFnt, i );
+ }
+ }
+ }
+ // this is the usual case, we have a basic attribute, remove it from the
+ // stack and reset the font
+ else
+ {
+ RemoveFromStack(rAttr.Which(), rAttr);
+ // reset font according to attribute on top of stack
+ // or default value
+ ActivateTop( rFnt, rAttr.Which() );
+ }
+}
+
+/// Only used during redlining
+void SwAttrHandler::Pop( const SwTextAttr& rAttr )
+{
+ OSL_ENSURE( rAttr.Which() < RES_TXTATR_WITHEND_END,
+ "I do not have this attribute, nWhich >= RES_TXTATR_WITHEND_END" );
+
+ if ( rAttr.Which() < RES_TXTATR_WITHEND_END )
+ {
+ RemoveFromStack(rAttr.Which(), rAttr);
+ }
+}
+
+void SwAttrHandler::ActivateTop( SwFont& rFnt, const sal_uInt16 nAttr )
+{
+ OSL_ENSURE( nAttr < RES_TXTATR_WITHEND_END,
+ "I cannot activate this attribute, nWhich >= RES_TXTATR_WITHEND_END" );
+
+ const sal_uInt16 nStackPos = StackPos[ nAttr ];
+ const SwTextAttr* pTopAt = GetTop(nStackPos);
+ if ( pTopAt )
+ {
+ const SfxPoolItem* pItemNext(nullptr);
+
+ // check if top attribute is collection of attributes
+ if ( RES_TXTATR_INETFMT == pTopAt->Which() ||
+ RES_TXTATR_CHARFMT == pTopAt->Which() ||
+ RES_TXTATR_AUTOFMT == pTopAt->Which() )
+ {
+ const SfxItemSet* pSet = CharFormat::GetItemSet( pTopAt->GetAttr() );
+ if (pSet)
+ pSet->GetItemState( nAttr, RES_TXTATR_AUTOFMT != pTopAt->Which(), &pItemNext );
+ }
+
+ if (pItemNext)
+ {
+ Color aColor;
+ if (lcl_ChgHyperLinkColor(*pTopAt, *pItemNext, m_pShell, &aColor))
+ {
+ SvxColorItem aItemNext( aColor, RES_CHRATR_COLOR );
+ FontChg( aItemNext, rFnt, false );
+ }
+ else
+ FontChg( *pItemNext, rFnt, false );
+ }
+ else
+ FontChg( pTopAt->GetAttr(), rFnt, false );
+ }
+
+ // default value has to be set, we only have default values for char attribs
+ else if ( nStackPos < NUM_DEFAULT_VALUES )
+ FontChg( *m_pDefaultArray[ nStackPos ], rFnt, false );
+ else if ( RES_TXTATR_REFMARK == nAttr )
+ rFnt.GetRef()--;
+ else if ( RES_TXTATR_TOXMARK == nAttr )
+ rFnt.GetTox()--;
+ else if ( (RES_TXTATR_META == nAttr) || (RES_TXTATR_METAFIELD == nAttr) )
+ {
+ rFnt.GetMeta()--;
+ }
+ else if (nAttr == RES_TXTATR_CONTENTCONTROL)
+ {
+ rFnt.GetContentControl()--;
+ }
+ else if ( RES_TXTATR_CJK_RUBY == nAttr )
+ {
+ // ruby stack has no more attributes
+ // check, if a rotation attribute has to be applied
+ const sal_uInt16 nTwoLineStack = StackPos[ RES_CHRATR_TWO_LINES ];
+ bool bTwoLineAct = false;
+ const SwTextAttr* pTwoLineAttr = GetTop(nTwoLineStack);
+
+ if ( pTwoLineAttr )
+ {
+ const auto& rTwoLineItem = *CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES );
+ bTwoLineAct = rTwoLineItem.GetValue();
+ }
+ else
+ bTwoLineAct = m_pDefaultArray[ nTwoLineStack ]->StaticWhichCast(RES_CHRATR_TWO_LINES).GetValue();
+
+ if ( bTwoLineAct )
+ return;
+
+ // eventually, a rotate attribute has to be activated
+ const sal_uInt16 nRotateStack = StackPos[ RES_CHRATR_ROTATE ];
+ const SwTextAttr* pRotateAttr = GetTop(nRotateStack);
+
+ if ( pRotateAttr )
+ {
+ const auto& rRotateItem = *CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE );
+ rFnt.SetVertical( rRotateItem.GetValue(), m_bVertLayout );
+ }
+ else
+ rFnt.SetVertical( m_pDefaultArray[ nRotateStack ]->StaticWhichCast(RES_CHRATR_ROTATE).GetValue(), m_bVertLayout );
+ }
+ else if ( RES_TXTATR_INPUTFIELD == nAttr )
+ rFnt.GetInputField()--;
+}
+
+/**
+ * When popping an attribute from the stack, the top more remaining
+ * attribute in the stack becomes valid. The following function change
+ * a font depending on the stack id.
+ */
+void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush )
+{
+ switch ( rItem.Which() )
+ {
+ case RES_CHRATR_CASEMAP :
+ rFnt.SetCaseMap( rItem.StaticWhichCast(RES_CHRATR_CASEMAP).GetCaseMap() );
+ break;
+ case RES_CHRATR_COLOR :
+ rFnt.SetColor( rItem.StaticWhichCast(RES_CHRATR_COLOR).GetValue() );
+ break;
+ case RES_CHRATR_CONTOUR :
+ rFnt.SetOutline( rItem.StaticWhichCast(RES_CHRATR_CONTOUR).GetValue() );
+ break;
+ case RES_CHRATR_CROSSEDOUT :
+ rFnt.SetStrikeout( rItem.StaticWhichCast(RES_CHRATR_CROSSEDOUT).GetStrikeout() );
+ break;
+ case RES_CHRATR_ESCAPEMENT :
+ rFnt.SetEscapement( rItem.StaticWhichCast(RES_CHRATR_ESCAPEMENT).GetEsc() );
+ rFnt.SetProportion( rItem.StaticWhichCast(RES_CHRATR_ESCAPEMENT).GetProportionalHeight() );
+ break;
+ case RES_CHRATR_FONT :
+ {
+ auto& rFontItem = rItem.StaticWhichCast(RES_CHRATR_FONT);
+ rFnt.SetName( rFontItem.GetFamilyName(), SwFontScript::Latin );
+ rFnt.SetStyleName( rFontItem.GetStyleName(), SwFontScript::Latin );
+ rFnt.SetFamily( rFontItem.GetFamily(), SwFontScript::Latin );
+ rFnt.SetPitch( rFontItem.GetPitch(), SwFontScript::Latin );
+ rFnt.SetCharSet( rFontItem.GetCharSet(), SwFontScript::Latin );
+ break;
+ }
+ case RES_CHRATR_FONTSIZE :
+ rFnt.SetSize(Size(0, rItem.StaticWhichCast(RES_CHRATR_FONTSIZE).GetHeight() ), SwFontScript::Latin );
+ break;
+ case RES_CHRATR_KERNING :
+ rFnt.SetFixKerning( rItem.StaticWhichCast(RES_CHRATR_KERNING).GetValue() );
+ break;
+ case RES_CHRATR_LANGUAGE :
+ rFnt.SetLanguage( rItem.StaticWhichCast(RES_CHRATR_LANGUAGE).GetLanguage(), SwFontScript::Latin );
+ break;
+ case RES_CHRATR_POSTURE :
+ rFnt.SetItalic( rItem.StaticWhichCast(RES_CHRATR_POSTURE).GetPosture(), SwFontScript::Latin );
+ break;
+ case RES_CHRATR_SHADOWED :
+ rFnt.SetShadow( rItem.StaticWhichCast(RES_CHRATR_SHADOWED).GetValue() );
+ break;
+ case RES_CHRATR_UNDERLINE :
+ {
+ const sal_uInt16 nStackPos = StackPos[ RES_CHRATR_HIDDEN ];
+ const SwTextAttr* pTopAt = GetTop(nStackPos);
+
+ const SfxPoolItem* pTmpItem = pTopAt ?
+ CharFormat::GetItem( *pTopAt, RES_CHRATR_HIDDEN ) :
+ m_pDefaultArray[ nStackPos ];
+
+ if ((m_pShell && !m_pShell->GetWin()) ||
+ (pTmpItem && !pTmpItem->StaticWhichCast(RES_CHRATR_HIDDEN).GetValue()) )
+ {
+ rFnt.SetUnderline( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetLineStyle() );
+ rFnt.SetUnderColor( rItem.StaticWhichCast(RES_CHRATR_UNDERLINE).GetColor() );
+ }
+ break;
+ }
+ case RES_CHRATR_BOX:
+ {
+ const SvxBoxItem& aBoxItem = rItem.StaticWhichCast(RES_CHRATR_BOX);
+ rFnt.SetTopBorder( aBoxItem.GetTop() );
+ rFnt.SetBottomBorder( aBoxItem.GetBottom() );
+ rFnt.SetRightBorder( aBoxItem.GetRight() );
+ rFnt.SetLeftBorder( aBoxItem.GetLeft() );
+ rFnt.SetTopBorderDist( aBoxItem.GetDistance(SvxBoxItemLine::TOP) );
+ rFnt.SetBottomBorderDist( aBoxItem.GetDistance(SvxBoxItemLine::BOTTOM) );
+ rFnt.SetRightBorderDist( aBoxItem.GetDistance(SvxBoxItemLine::RIGHT) );
+ rFnt.SetLeftBorderDist( aBoxItem.GetDistance(SvxBoxItemLine::LEFT) );
+ break;
+ }
+ case RES_CHRATR_SHADOW:
+ {
+ const SvxShadowItem& aShadowItem = rItem.StaticWhichCast(RES_CHRATR_SHADOW);
+ rFnt.SetShadowColor( aShadowItem.GetColor() );
+ rFnt.SetShadowWidth( aShadowItem.GetWidth() );
+ rFnt.SetShadowLocation( aShadowItem.GetLocation() );
+ break;
+ }
+ case RES_CHRATR_OVERLINE :
+ rFnt.SetOverline( rItem.StaticWhichCast(RES_CHRATR_OVERLINE).GetLineStyle() );
+ rFnt.SetOverColor( rItem.StaticWhichCast(RES_CHRATR_OVERLINE).GetColor() );
+ break;
+ case RES_CHRATR_WEIGHT :
+ rFnt.SetWeight( rItem.StaticWhichCast(RES_CHRATR_WEIGHT).GetWeight(), SwFontScript::Latin );
+ break;
+ case RES_CHRATR_WORDLINEMODE :
+ rFnt.SetWordLineMode( rItem.StaticWhichCast(RES_CHRATR_WORDLINEMODE).GetValue() );
+ break;
+ case RES_CHRATR_AUTOKERN :
+ if( rItem.StaticWhichCast(RES_CHRATR_AUTOKERN).GetValue() )
+ {
+ rFnt.SetAutoKern( (!m_pIDocumentSettingAccess ||
+ !m_pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION)) ?
+ FontKerning::FontSpecific :
+ FontKerning::Asian );
+ }
+ else
+ rFnt.SetAutoKern( FontKerning::NONE );
+ break;
+ case RES_CHRATR_BACKGROUND :
+ rFnt.SetBackColor(rItem.StaticWhichCast(RES_CHRATR_BACKGROUND).GetColor());
+ break;
+ case RES_CHRATR_HIGHLIGHT :
+ rFnt.SetHighlightColor( rItem.StaticWhichCast(RES_CHRATR_HIGHLIGHT).GetColor() );
+ break;
+ case RES_CHRATR_CJK_FONT :
+ {
+ auto& rFontItem = rItem.StaticWhichCast(RES_CHRATR_CJK_FONT);
+ rFnt.SetName( rFontItem.GetFamilyName(), SwFontScript::CJK );
+ rFnt.SetStyleName( rFontItem.GetStyleName(), SwFontScript::CJK );
+ rFnt.SetFamily( rFontItem.GetFamily(), SwFontScript::CJK );
+ rFnt.SetPitch( rFontItem.GetPitch(), SwFontScript::CJK );
+ rFnt.SetCharSet( rFontItem.GetCharSet(), SwFontScript::CJK );
+ break;
+ }
+ case RES_CHRATR_CJK_FONTSIZE :
+ rFnt.SetSize(Size( 0, rItem.StaticWhichCast(RES_CHRATR_CJK_FONTSIZE).GetHeight()), SwFontScript::CJK);
+ break;
+ case RES_CHRATR_CJK_LANGUAGE :
+ rFnt.SetLanguage( rItem.StaticWhichCast(RES_CHRATR_CJK_LANGUAGE).GetLanguage(), SwFontScript::CJK );
+ break;
+ case RES_CHRATR_CJK_POSTURE :
+ rFnt.SetItalic( rItem.StaticWhichCast(RES_CHRATR_CJK_POSTURE).GetPosture(), SwFontScript::CJK );
+ break;
+ case RES_CHRATR_CJK_WEIGHT :
+ rFnt.SetWeight( rItem.StaticWhichCast(RES_CHRATR_CJK_WEIGHT).GetWeight(), SwFontScript::CJK );
+ break;
+ case RES_CHRATR_CTL_FONT :
+ {
+ auto& rFontItem = rItem.StaticWhichCast(RES_CHRATR_CTL_FONT);
+ rFnt.SetName( rFontItem.GetFamilyName(), SwFontScript::CTL );
+ rFnt.SetStyleName( rFontItem.GetStyleName(), SwFontScript::CTL );
+ rFnt.SetFamily( rFontItem.GetFamily(), SwFontScript::CTL );
+ rFnt.SetPitch( rFontItem.GetPitch(), SwFontScript::CTL );
+ rFnt.SetCharSet( rFontItem.GetCharSet(), SwFontScript::CTL );
+ break;
+ }
+ case RES_CHRATR_CTL_FONTSIZE :
+ rFnt.SetSize(Size(0, rItem.StaticWhichCast(RES_CHRATR_CTL_FONTSIZE).GetHeight() ), SwFontScript::CTL);
+ break;
+ case RES_CHRATR_CTL_LANGUAGE :
+ rFnt.SetLanguage( rItem.StaticWhichCast(RES_CHRATR_CTL_LANGUAGE).GetLanguage(), SwFontScript::CTL );
+ break;
+ case RES_CHRATR_CTL_POSTURE :
+ rFnt.SetItalic( rItem.StaticWhichCast(RES_CHRATR_CTL_POSTURE).GetPosture(), SwFontScript::CTL );
+ break;
+ case RES_CHRATR_CTL_WEIGHT :
+ rFnt.SetWeight( rItem.StaticWhichCast(RES_CHRATR_CTL_WEIGHT).GetWeight(), SwFontScript::CTL );
+ break;
+ case RES_CHRATR_EMPHASIS_MARK :
+ rFnt.SetEmphasisMark( rItem.StaticWhichCast(RES_CHRATR_EMPHASIS_MARK).GetEmphasisMark() );
+ break;
+ case RES_CHRATR_SCALEW :
+ rFnt.SetPropWidth( rItem.StaticWhichCast(RES_CHRATR_SCALEW).GetValue() );
+ break;
+ case RES_CHRATR_RELIEF :
+ rFnt.SetRelief( rItem.StaticWhichCast(RES_CHRATR_RELIEF).GetValue() );
+ break;
+ case RES_CHRATR_HIDDEN :
+ if (m_pShell && m_pShell->GetWin())
+ {
+ if ( rItem.StaticWhichCast(RES_CHRATR_HIDDEN).GetValue() )
+ rFnt.SetUnderline( LINESTYLE_DOTTED );
+ else
+ ActivateTop( rFnt, RES_CHRATR_UNDERLINE );
+ }
+ break;
+ case RES_CHRATR_ROTATE :
+ {
+ // rotate attribute is applied, when:
+ // 1. ruby stack is empty and
+ // 2. top of two line stack ( or default attribute )is an
+ // deactivated two line attribute
+ const bool bRuby =
+ 0 != m_aAttrStack[ StackPos[ RES_TXTATR_CJK_RUBY ] ].size();
+
+ if ( bRuby )
+ break;
+
+ const sal_uInt16 nTwoLineStack = StackPos[ RES_CHRATR_TWO_LINES ];
+ bool bTwoLineAct = false;
+ const SwTextAttr* pTwoLineAttr = GetTop(nTwoLineStack);
+
+ if ( pTwoLineAttr )
+ {
+ const auto& rTwoLineItem = *CharFormat::GetItem( *pTwoLineAttr, RES_CHRATR_TWO_LINES );
+ bTwoLineAct = rTwoLineItem.GetValue();
+ }
+ else
+ bTwoLineAct = m_pDefaultArray[ nTwoLineStack ]->StaticWhichCast(RES_CHRATR_TWO_LINES).GetValue();
+
+ if ( !bTwoLineAct )
+ rFnt.SetVertical( rItem.StaticWhichCast(RES_CHRATR_ROTATE).GetValue(), m_bVertLayout, m_bVertLayoutLRBT );
+
+ break;
+ }
+ case RES_CHRATR_TWO_LINES :
+ {
+ bool bRuby = 0 !=
+ m_aAttrStack[ StackPos[ RES_TXTATR_CJK_RUBY ] ].size();
+
+ // two line is activated, if
+ // 1. no ruby attribute is set and
+ // 2. attribute is active
+ if ( !bRuby && rItem.StaticWhichCast(RES_CHRATR_TWO_LINES).GetValue() )
+ {
+ rFnt.SetVertical( 0_deg10, m_bVertLayout );
+ break;
+ }
+
+ // a deactivating two line attribute is on top of stack,
+ // check if rotate attribute has to be enabled
+ if ( bRuby )
+ break;
+
+ const sal_uInt16 nRotateStack = StackPos[ RES_CHRATR_ROTATE ];
+ const SwTextAttr* pRotateAttr = GetTop(nRotateStack);
+
+ if ( pRotateAttr )
+ {
+ const auto& rRotateItem = *CharFormat::GetItem( *pRotateAttr, RES_CHRATR_ROTATE );
+ rFnt.SetVertical( rRotateItem.GetValue(), m_bVertLayout );
+ }
+ else
+ rFnt.SetVertical(m_pDefaultArray[ nRotateStack ]->StaticWhichCast(RES_CHRATR_ROTATE).GetValue(), m_bVertLayout);
+ break;
+ }
+ case RES_TXTATR_CJK_RUBY :
+ rFnt.SetVertical( 0_deg10, m_bVertLayout );
+ break;
+ case RES_TXTATR_REFMARK :
+ if ( bPush )
+ rFnt.GetRef()++;
+ else
+ rFnt.GetRef()--;
+ break;
+ case RES_TXTATR_TOXMARK :
+ if ( bPush )
+ rFnt.GetTox()++;
+ else
+ rFnt.GetTox()--;
+ break;
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ if ( bPush )
+ rFnt.GetMeta()++;
+ else
+ rFnt.GetMeta()--;
+ break;
+ case RES_TXTATR_CONTENTCONTROL:
+ if (bPush)
+ {
+ rFnt.GetContentControl()++;
+ }
+ else
+ {
+ rFnt.GetContentControl()--;
+ }
+ break;
+ case RES_TXTATR_INPUTFIELD :
+ if ( bPush )
+ rFnt.GetInputField()++;
+ else
+ rFnt.GetInputField()--;
+ break;
+ }
+}
+
+/// Takes the default font and calculated the ascent and height
+void SwAttrHandler::GetDefaultAscentAndHeight( SwViewShell const * pShell, OutputDevice const & rOut,
+ sal_uInt16& nAscent, sal_uInt16& nHeight ) const
+{
+ OSL_ENSURE(m_oFnt, "No font available for GetDefaultAscentAndHeight");
+
+ if (m_oFnt)
+ {
+ SwFont aFont( *m_oFnt );
+ nHeight = aFont.GetHeight( pShell, rOut );
+ nAscent = aFont.GetAscent( pShell, rOut );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/frmcrsr.cxx b/sw/source/core/text/frmcrsr.cxx
new file mode 100644
index 000000000..58792ecf8
--- /dev/null
+++ b/sw/source/core/text/frmcrsr.cxx
@@ -0,0 +1,1689 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <paratr.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <colfrm.hxx>
+#include <swtypes.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include "pormulti.hxx"
+#include <doc.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <sortedobjs.hxx>
+
+#include <unicode/ubidi.h>
+
+#include <txtfrm.hxx>
+#include "inftxt.hxx"
+#include "itrtxt.hxx"
+#include <crstate.hxx>
+#include <viewsh.hxx>
+#include <swfntcch.hxx>
+#include <flyfrm.hxx>
+
+#define MIN_OFFSET_STEP 10
+
+using namespace ::com::sun::star;
+
+/*
+ * - SurvivalKit: For how long do we get past the last char of the line.
+ * - RightMargin abstains from adjusting position with -1
+ * - GetCharRect returns a GetEndCharRect for CursorMoveState::RightMargin
+ * - GetEndCharRect sets bRightMargin to true
+ * - SwTextCursor::bRightMargin is set to false by CharCursorToLine
+ */
+
+namespace
+{
+
+SwTextFrame *GetAdjFrameAtPos( SwTextFrame *pFrame, const SwPosition &rPos,
+ const bool bRightMargin, const bool bNoScroll = true )
+{
+ // RightMargin in the last master line
+ TextFrameIndex const nOffset = pFrame->MapModelToViewPos(rPos);
+ SwTextFrame *pFrameAtPos = pFrame;
+ if( !bNoScroll || pFrame->GetFollow() )
+ {
+ pFrameAtPos = pFrame->GetFrameAtPos( rPos );
+ if (nOffset < pFrameAtPos->GetOffset() &&
+ !pFrameAtPos->IsFollow() )
+ {
+ assert(pFrameAtPos->MapModelToViewPos(rPos) == nOffset);
+ TextFrameIndex nNew(nOffset);
+ if (nNew < TextFrameIndex(MIN_OFFSET_STEP))
+ nNew = TextFrameIndex(0);
+ else
+ nNew -= TextFrameIndex(MIN_OFFSET_STEP);
+ sw_ChangeOffset( pFrameAtPos, nNew );
+ }
+ }
+ while( pFrame != pFrameAtPos )
+ {
+ pFrame = pFrameAtPos;
+ pFrame->GetFormatted();
+ pFrameAtPos = pFrame->GetFrameAtPos( rPos );
+ }
+
+ if( nOffset && bRightMargin )
+ {
+ while (pFrameAtPos &&
+ pFrameAtPos->MapViewToModelPos(pFrameAtPos->GetOffset()) == rPos &&
+ pFrameAtPos->IsFollow() )
+ {
+ pFrameAtPos->GetFormatted();
+ pFrameAtPos = pFrameAtPos->FindMaster();
+ }
+ OSL_ENSURE( pFrameAtPos, "+GetCharRect: no frame with my rightmargin" );
+ }
+ return pFrameAtPos ? pFrameAtPos : pFrame;
+}
+
+}
+
+bool sw_ChangeOffset(SwTextFrame* pFrame, TextFrameIndex nNew)
+{
+ // Do not scroll in areas and outside of flies
+ OSL_ENSURE( !pFrame->IsFollow(), "Illegal Scrolling by Follow!" );
+ if( pFrame->GetOffset() != nNew && !pFrame->IsInSct() )
+ {
+ SwFlyFrame *pFly = pFrame->FindFlyFrame();
+ // Attention: if e.g. in a column frame the size is still invalid
+ // we must not scroll around just like that
+ if ( ( pFly && pFly->isFrameAreaDefinitionValid() &&
+ !pFly->GetNextLink() && !pFly->GetPrevLink() ) ||
+ ( !pFly && pFrame->IsInTab() ) )
+ {
+ SwViewShell* pVsh = pFrame->getRootFrame()->GetCurrShell();
+ if( pVsh )
+ {
+ if( pVsh->GetRingContainer().size() > 1 ||
+ ( pFrame->GetDrawObjs() && pFrame->GetDrawObjs()->size() ) )
+ {
+ if( !pFrame->GetOffset() )
+ return false;
+ nNew = TextFrameIndex(0);
+ }
+ pFrame->SetOffset( nNew );
+ pFrame->SetPara( nullptr );
+ pFrame->GetFormatted();
+ if( pFrame->getFrameArea().HasArea() )
+ pFrame->getRootFrame()->GetCurrShell()->InvalidateWindows( pFrame->getFrameArea() );
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+SwTextFrame& SwTextFrame::GetFrameAtOfst(TextFrameIndex const nWhere)
+{
+ SwTextFrame* pRet = this;
+ while( pRet->HasFollow() && nWhere >= pRet->GetFollow()->GetOffset() )
+ pRet = pRet->GetFollow();
+ return *pRet;
+}
+
+SwTextFrame *SwTextFrame::GetFrameAtPos( const SwPosition &rPos )
+{
+ TextFrameIndex const nPos(MapModelToViewPos(rPos));
+ SwTextFrame *pFoll = this;
+ while( pFoll->GetFollow() )
+ {
+ if (nPos > pFoll->GetFollow()->GetOffset())
+ pFoll = pFoll->GetFollow();
+ else
+ {
+ if (nPos == pFoll->GetFollow()->GetOffset()
+ && !SwTextCursor::IsRightMargin() )
+ pFoll = pFoll->GetFollow();
+ else
+ break;
+ }
+ }
+ return pFoll;
+}
+
+/*
+ * GetCharRect() returns the char's char line described by aPos.
+ * GetModelPositionForViewPoint() does the reverse: It goes from a document coordinate to
+ * a Pam.
+ * Both are virtual in the frame base class and thus are redefined here.
+ */
+
+bool SwTextFrame::GetCharRect( SwRect& rOrig, const SwPosition &rPos,
+ SwCursorMoveState *pCMS, bool bAllowFarAway ) const
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::GetCharRect with swapped frame" );
+
+ if( IsLocked() || IsHiddenNow() )
+ return false;
+
+ // Find the right frame first. We need to keep in mind that:
+ // - the cached information could be invalid (GetPara() == 0)
+ // - we could have a Follow
+ // - the Follow chain grows dynamically; the one we end up in
+ // needs to be formatted
+
+ // Optimisation: reading ahead saves us a GetAdjFrameAtPos
+ const bool bRightMargin = pCMS && ( CursorMoveState::RightMargin == pCMS->m_eState );
+ const bool bNoScroll = pCMS && pCMS->m_bNoScroll;
+ SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), rPos, bRightMargin,
+ bNoScroll );
+ pFrame->GetFormatted();
+
+ const SwFrame* pTmpFrame = pFrame->GetUpper();
+ if (pTmpFrame->getFrameArea().Top() == FAR_AWAY && !bAllowFarAway)
+ return false;
+
+ SwRectFnSet aRectFnSet(pFrame);
+ const SwTwips nUpperMaxY = aRectFnSet.GetPrtBottom(*pTmpFrame);
+ const SwTwips nFrameMaxY = aRectFnSet.GetPrtBottom(*pFrame);
+
+ // nMaxY is an absolute value
+ SwTwips nMaxY = aRectFnSet.IsVert() ?
+ ( aRectFnSet.IsVertL2R() ? std::min( nFrameMaxY, nUpperMaxY ) : std::max( nFrameMaxY, nUpperMaxY ) ) :
+ std::min( nFrameMaxY, nUpperMaxY );
+
+ bool bRet = false;
+
+ if ( pFrame->IsEmpty() || ! aRectFnSet.GetHeight(pFrame->getFramePrintArea()) )
+ {
+ Point aPnt1 = pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
+ SwTextNode const*const pTextNd(GetTextNodeForParaProps());
+ short nFirstOffset;
+ pTextNd->GetFirstLineOfsWithNum( nFirstOffset );
+
+ Point aPnt2;
+ if ( aRectFnSet.IsVert() )
+ {
+ if( nFirstOffset > 0 )
+ aPnt1.AdjustY(nFirstOffset );
+ if ( aPnt1.X() < nMaxY && !aRectFnSet.IsVertL2R() )
+ aPnt1.setX( nMaxY );
+ aPnt2.setX( aPnt1.X() + pFrame->getFramePrintArea().Width() );
+ aPnt2.setY( aPnt1.Y() );
+ if( aPnt2.X() < nMaxY )
+ aPnt2.setX( nMaxY );
+ }
+ else
+ {
+ if( nFirstOffset > 0 )
+ aPnt1.AdjustX(nFirstOffset );
+
+ if( aPnt1.Y() > nMaxY )
+ aPnt1.setY( nMaxY );
+ aPnt2.setX( aPnt1.X() );
+ aPnt2.setY( aPnt1.Y() + pFrame->getFramePrintArea().Height() );
+ if( aPnt2.Y() > nMaxY )
+ aPnt2.setY( nMaxY );
+ }
+
+ rOrig = SwRect( aPnt1, aPnt2 );
+
+ if ( pCMS )
+ {
+ pCMS->m_aRealHeight.setX( 0 );
+ pCMS->m_aRealHeight.setY( aRectFnSet.IsVert() ? -rOrig.Width() : rOrig.Height() );
+ }
+
+ if ( pFrame->IsRightToLeft() )
+ pFrame->SwitchLTRtoRTL( rOrig );
+
+ bRet = true;
+ }
+ else
+ {
+ if( !pFrame->HasPara() )
+ return false;
+
+ SwFrameSwapper aSwapper( pFrame, true );
+ if ( aRectFnSet.IsVert() )
+ nMaxY = pFrame->SwitchVerticalToHorizontal( nMaxY );
+
+ bool bGoOn = true;
+ TextFrameIndex const nOffset = MapModelToViewPos(rPos);
+ assert(nOffset != TextFrameIndex(COMPLETE_STRING)); // not going to end well
+ TextFrameIndex nNextOfst;
+
+ do
+ {
+ {
+ SwTextSizeInfo aInf( pFrame );
+ SwTextCursor aLine( pFrame, &aInf );
+ nNextOfst = aLine.GetEnd();
+ // See comment in AdjustFrame
+ // Include the line's last char?
+ if (bRightMargin)
+ aLine.GetEndCharRect( &rOrig, nOffset, pCMS, nMaxY );
+ else
+ aLine.GetCharRect( &rOrig, nOffset, pCMS, nMaxY );
+ bRet = true;
+ }
+
+ if ( pFrame->IsRightToLeft() )
+ pFrame->SwitchLTRtoRTL( rOrig );
+
+ if ( aRectFnSet.IsVert() )
+ pFrame->SwitchHorizontalToVertical( rOrig );
+
+ if( pFrame->IsUndersized() && pCMS && !pFrame->GetNext() &&
+ aRectFnSet.GetBottom(rOrig) == nUpperMaxY &&
+ pFrame->GetOffset() < nOffset &&
+ !pFrame->IsFollow() && !bNoScroll &&
+ TextFrameIndex(pFrame->GetText().getLength()) != nNextOfst)
+ {
+ bGoOn = sw_ChangeOffset( pFrame, nNextOfst );
+ }
+ else
+ bGoOn = false;
+ } while ( bGoOn );
+
+ if ( pCMS )
+ {
+ if ( pFrame->IsRightToLeft() )
+ {
+ if( pCMS->m_b2Lines && pCMS->m_p2Lines)
+ {
+ pFrame->SwitchLTRtoRTL( pCMS->m_p2Lines->aLine );
+ pFrame->SwitchLTRtoRTL( pCMS->m_p2Lines->aPortion );
+ }
+ }
+
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( pCMS->m_bRealHeight )
+ {
+ pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
+ if ( pCMS->m_aRealHeight.Y() < 0 )
+ {
+ // writing direction is from top to bottom
+ pCMS->m_aRealHeight.setX( rOrig.Width() -
+ pCMS->m_aRealHeight.X() +
+ pCMS->m_aRealHeight.Y() );
+ }
+ }
+ if( pCMS->m_b2Lines && pCMS->m_p2Lines)
+ {
+ pFrame->SwitchHorizontalToVertical( pCMS->m_p2Lines->aLine );
+ pFrame->SwitchHorizontalToVertical( pCMS->m_p2Lines->aPortion );
+ }
+ }
+
+ }
+ }
+ if( bRet )
+ {
+ SwPageFrame *pPage = pFrame->FindPageFrame();
+ OSL_ENSURE( pPage, "Text escaped from page?" );
+ const SwTwips nOrigTop = aRectFnSet.GetTop(rOrig);
+ const SwTwips nPageTop = aRectFnSet.GetTop(pPage->getFrameArea());
+ const SwTwips nPageBott = aRectFnSet.GetBottom(pPage->getFrameArea());
+
+ // We have the following situation: if the frame is in an invalid
+ // sectionframe, it's possible that the frame is outside the page.
+ // If we restrict the cursor position to the page area, we enforce
+ // the formatting of the page, of the section frame and the frame itself.
+ if( aRectFnSet.YDiff( nPageTop, nOrigTop ) > 0 )
+ aRectFnSet.SetTop( rOrig, nPageTop );
+
+ if ( aRectFnSet.YDiff( nOrigTop, nPageBott ) > 0 )
+ aRectFnSet.SetTop( rOrig, nPageBott );
+ }
+
+ return bRet;
+}
+
+/*
+ * GetAutoPos() looks up the char's char line which is described by rPos
+ * and is used by the auto-positioned frame.
+ */
+
+bool SwTextFrame::GetAutoPos( SwRect& rOrig, const SwPosition &rPos ) const
+{
+ if( IsHiddenNow() )
+ return false;
+
+ TextFrameIndex const nOffset = MapModelToViewPos(rPos);
+ SwTextFrame* pFrame = &(const_cast<SwTextFrame*>(this)->GetFrameAtOfst( nOffset ));
+
+ pFrame->GetFormatted();
+ const SwFrame* pTmpFrame = pFrame->GetUpper();
+
+ SwRectFnSet aRectFnSet(pTmpFrame);
+ SwTwips nUpperMaxY = aRectFnSet.GetPrtBottom(*pTmpFrame);
+
+ // nMaxY is in absolute value
+ SwTwips nMaxY;
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( aRectFnSet.IsVertL2R() )
+ nMaxY = std::min( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
+ else
+ nMaxY = std::max( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
+ }
+ else
+ nMaxY = std::min( SwTwips(aRectFnSet.GetPrtBottom(*pFrame)), nUpperMaxY );
+ if ( pFrame->IsEmpty() || ! aRectFnSet.GetHeight(pFrame->getFramePrintArea()) )
+ {
+ Point aPnt1 = pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos();
+ Point aPnt2;
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( aPnt1.X() < nMaxY && !aRectFnSet.IsVertL2R() )
+ aPnt1.setX( nMaxY );
+
+ aPnt2.setX( aPnt1.X() + pFrame->getFramePrintArea().Width() );
+ aPnt2.setY( aPnt1.Y() );
+ if( aPnt2.X() < nMaxY )
+ aPnt2.setX( nMaxY );
+ }
+ else
+ {
+ if( aPnt1.Y() > nMaxY )
+ aPnt1.setY( nMaxY );
+ aPnt2.setX( aPnt1.X() );
+ aPnt2.setY( aPnt1.Y() + pFrame->getFramePrintArea().Height() );
+ if( aPnt2.Y() > nMaxY )
+ aPnt2.setY( nMaxY );
+ }
+ rOrig = SwRect( aPnt1, aPnt2 );
+ return true;
+ }
+ else
+ {
+ if( !pFrame->HasPara() )
+ return false;
+
+ SwFrameSwapper aSwapper( pFrame, true );
+ if ( aRectFnSet.IsVert() )
+ nMaxY = pFrame->SwitchVerticalToHorizontal( nMaxY );
+
+ SwTextSizeInfo aInf( pFrame );
+ SwTextCursor aLine( pFrame, &aInf );
+ SwCursorMoveState aTmpState( CursorMoveState::SetOnlyText );
+ aTmpState.m_bRealHeight = true;
+ aLine.GetCharRect( &rOrig, nOffset, &aTmpState, nMaxY );
+ if( aTmpState.m_aRealHeight.X() >= 0 )
+ {
+ rOrig.Pos().AdjustY(aTmpState.m_aRealHeight.X() );
+ rOrig.Height( aTmpState.m_aRealHeight.Y() );
+ }
+
+ if ( pFrame->IsRightToLeft() )
+ pFrame->SwitchLTRtoRTL( rOrig );
+
+ if ( aRectFnSet.IsVert() )
+ pFrame->SwitchHorizontalToVertical( rOrig );
+
+ return true;
+ }
+}
+
+/** determine top of line for given position in the text frame
+
+ - Top of first paragraph line is the top of the printing area of the text frame
+ - If a proportional line spacing is applied use top of anchor character as
+ top of the line.
+*/
+bool SwTextFrame::GetTopOfLine( SwTwips& _onTopOfLine,
+ const SwPosition& _rPos ) const
+{
+ bool bRet = true;
+
+ // get position offset
+ TextFrameIndex const nOffset = MapModelToViewPos(_rPos);
+
+ if (TextFrameIndex(GetText().getLength()) < nOffset)
+ {
+ bRet = false;
+ }
+ else
+ {
+ SwRectFnSet aRectFnSet(this);
+ if ( IsEmpty() || !aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ // consider upper space amount considered
+ // for previous frame and the page grid.
+ _onTopOfLine = aRectFnSet.GetPrtTop(*this);
+ }
+ else
+ {
+ // determine formatted text frame that contains the requested position
+ SwTextFrame* pFrame = &(const_cast<SwTextFrame*>(this)->GetFrameAtOfst( nOffset ));
+ pFrame->GetFormatted();
+ aRectFnSet.Refresh(pFrame);
+ // If proportional line spacing is applied
+ // to the text frame, the top of the anchor character is also the
+ // top of the line.
+ // Otherwise the line layout determines the top of the line
+ const SvxLineSpacingItem& rSpace = GetAttrSet()->GetLineSpacing();
+ if ( rSpace.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop )
+ {
+ SwRect aCharRect;
+ if ( GetAutoPos( aCharRect, _rPos ) )
+ {
+ _onTopOfLine = aRectFnSet.GetTop(aCharRect);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else
+ {
+ // assure that text frame is in a horizontal layout
+ SwFrameSwapper aSwapper( pFrame, true );
+ // determine text line that contains the requested position
+ SwTextSizeInfo aInf( pFrame );
+ SwTextCursor aLine( pFrame, &aInf );
+ aLine.CharCursorToLine( nOffset );
+ // determine top of line
+ _onTopOfLine = aLine.Y();
+ if ( aRectFnSet.IsVert() )
+ {
+ _onTopOfLine = pFrame->SwitchHorizontalToVertical( _onTopOfLine );
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+// Minimum distance of non-empty lines is a little less than 2 cm
+#define FILL_MIN_DIST 1100
+
+struct SwFillData
+{
+ SwRect aFrame;
+ const SwCursorMoveState *pCMS;
+ SwPosition* pPos;
+ const Point& rPoint;
+ SwTwips nLineWidth;
+ bool bFirstLine : 1;
+ bool bInner : 1;
+ bool bColumn : 1;
+ bool bEmpty : 1;
+ SwFillData( const SwCursorMoveState *pC, SwPosition* pP, const SwRect& rR,
+ const Point& rPt ) : aFrame( rR ), pCMS( pC ), pPos( pP ), rPoint( rPt ),
+ nLineWidth( 0 ), bFirstLine( true ), bInner( false ), bColumn( false ),
+ bEmpty( true ){}
+ SwFillMode Mode() const { return pCMS->m_pFill->eMode; }
+ tools::Long X() const { return rPoint.X(); }
+ tools::Long Y() const { return rPoint.Y(); }
+ tools::Long Left() const { return aFrame.Left(); }
+ tools::Long Right() const { return aFrame.Right(); }
+ tools::Long Bottom() const { return aFrame.Bottom(); }
+ SwFillCursorPos &Fill() const { return *pCMS->m_pFill; }
+ void SetTab( sal_uInt16 nNew ) { pCMS->m_pFill->nTabCnt = nNew; }
+ void SetSpace( sal_uInt16 nNew ) { pCMS->m_pFill->nSpaceCnt = nNew; }
+ void SetSpaceOnly( sal_uInt16 nNew ) { pCMS->m_pFill->nSpaceOnlyCnt = nNew; }
+ void SetOrient( const sal_Int16 eNew ){ pCMS->m_pFill->eOrient = eNew; }
+};
+
+bool SwTextFrame::GetModelPositionForViewPoint_(SwPosition* pPos, const Point& rPoint,
+ const bool bChgFrame, SwCursorMoveState* pCMS ) const
+{
+ // GetModelPositionForViewPoint_ is called by GetModelPositionForViewPoint and GetKeyCursorOfst.
+ // Never just a return false.
+
+ if( IsLocked() || IsHiddenNow() )
+ return false;
+
+ const_cast<SwTextFrame*>(this)->GetFormatted();
+
+ Point aOldPoint( rPoint );
+
+ if ( IsVertical() )
+ {
+ SwitchVerticalToHorizontal( const_cast<Point&>(rPoint) );
+ const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
+ }
+
+ if ( IsRightToLeft() )
+ SwitchRTLtoLTR( const_cast<Point&>(rPoint) );
+
+ std::unique_ptr<SwFillData> pFillData;
+ if ( pCMS && pCMS->m_pFill )
+ pFillData.reset(new SwFillData( pCMS, pPos, getFrameArea(), rPoint ));
+
+ if ( IsEmpty() )
+ {
+ *pPos = MapViewToModelPos(TextFrameIndex(0));
+ if( pCMS && pCMS->m_bFieldInfo )
+ {
+ SwTwips nDiff = rPoint.X() - getFrameArea().Left() - getFramePrintArea().Left();
+ if( nDiff > 50 || nDiff < 0 )
+ pCMS->m_bPosCorr = true;
+ }
+ }
+ else
+ {
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(this), &aInf );
+
+ // See comment in AdjustFrame()
+ SwTwips nMaxY = getFrameArea().Top() + getFramePrintArea().Top() + getFramePrintArea().Height();
+ aLine.TwipsToLine( rPoint.Y() );
+ while( aLine.Y() + aLine.GetLineHeight() > nMaxY )
+ {
+ if( !aLine.Prev() )
+ break;
+ }
+
+ if( aLine.GetDropLines() >= aLine.GetLineNr() && 1 != aLine.GetLineNr()
+ && rPoint.X() < aLine.FirstLeft() + aLine.GetDropLeft() )
+ while( aLine.GetLineNr() > 1 )
+ aLine.Prev();
+
+ TextFrameIndex nOffset = aLine.GetModelPositionForViewPoint(pPos, rPoint, bChgFrame, pCMS);
+
+ if( pCMS && pCMS->m_eState == CursorMoveState::NONE && aLine.GetEnd() == nOffset )
+ pCMS->m_eState = CursorMoveState::RightMargin;
+
+ // pPos is a pure IN parameter and must not be evaluated.
+ // pIter->GetModelPositionForViewPoint returns from a nesting with COMPLETE_STRING.
+ // If SwTextIter::GetModelPositionForViewPoint calls GetModelPositionForViewPoint further by itself
+ // nNode changes the position.
+ // In such cases, pPos must not be calculated.
+ if (TextFrameIndex(COMPLETE_STRING) != nOffset)
+ {
+ *pPos = MapViewToModelPos(nOffset);
+ if( pFillData )
+ {
+ if (TextFrameIndex(GetText().getLength()) > nOffset ||
+ rPoint.Y() < getFrameArea().Top() )
+ pFillData->bInner = true;
+ pFillData->bFirstLine = aLine.GetLineNr() < 2;
+ if (GetText().getLength())
+ {
+ pFillData->bEmpty = false;
+ pFillData->nLineWidth = aLine.GetCurr()->Width();
+ }
+ }
+ }
+ }
+ bool bChgFillData = false;
+ if( pFillData && FindPageFrame()->getFrameArea().Contains( aOldPoint ) )
+ {
+ FillCursorPos( *pFillData );
+ bChgFillData = true;
+ }
+
+ if ( IsVertical() )
+ {
+ if ( bChgFillData )
+ SwitchHorizontalToVertical( pFillData->Fill().aCursor.Pos() );
+ const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
+ }
+
+ if ( IsRightToLeft() && bChgFillData )
+ {
+ SwitchLTRtoRTL( pFillData->Fill().aCursor.Pos() );
+ const sal_Int16 eOrient = pFillData->pCMS->m_pFill->eOrient;
+
+ if ( text::HoriOrientation::LEFT == eOrient )
+ pFillData->SetOrient( text::HoriOrientation::RIGHT );
+ else if ( text::HoriOrientation::RIGHT == eOrient )
+ pFillData->SetOrient( text::HoriOrientation::LEFT );
+ }
+
+ const_cast<Point&>(rPoint) = aOldPoint;
+
+ return true;
+}
+
+bool SwTextFrame::GetModelPositionForViewPoint(SwPosition* pPos, Point& rPoint,
+ SwCursorMoveState* pCMS, bool ) const
+{
+ const bool bChgFrame = !(pCMS && CursorMoveState::UpDown == pCMS->m_eState);
+ return GetModelPositionForViewPoint_( pPos, rPoint, bChgFrame, pCMS );
+}
+
+/*
+ * Layout-oriented cursor movement to the line start.
+ */
+
+bool SwTextFrame::LeftMargin(SwPaM *pPam) const
+{
+ assert(GetMergedPara() || &pPam->GetNode() == static_cast<SwContentNode const*>(GetDep()));
+
+ SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
+ SwTextCursor::IsRightMargin() );
+ pFrame->GetFormatted();
+ TextFrameIndex nIndx;
+ if ( pFrame->IsEmpty() )
+ nIndx = TextFrameIndex(0);
+ else
+ {
+ SwTextSizeInfo aInf( pFrame );
+ SwTextCursor aLine( pFrame, &aInf );
+
+ aLine.CharCursorToLine(pFrame->MapModelToViewPos(*pPam->GetPoint()));
+ nIndx = aLine.GetStart();
+ if( pFrame->GetOffset() && !pFrame->IsFollow() && !aLine.GetPrev() )
+ {
+ sw_ChangeOffset(pFrame, TextFrameIndex(0));
+ nIndx = TextFrameIndex(0);
+ }
+ }
+ *pPam->GetPoint() = pFrame->MapViewToModelPos(nIndx);
+ SwTextCursor::SetRightMargin( false );
+ return true;
+}
+
+/*
+ * To the line end: That's the position before the last char of the line.
+ * Exception: In the last line, it should be able to place the cursor after
+ * the last char in order to append text.
+ */
+
+bool SwTextFrame::RightMargin(SwPaM *pPam, bool bAPI) const
+{
+ assert(GetMergedPara() || &pPam->GetNode() == static_cast<SwContentNode const*>(GetDep()));
+
+ SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *pPam->GetPoint(),
+ SwTextCursor::IsRightMargin() );
+ pFrame->GetFormatted();
+ TextFrameIndex nRightMargin(0);
+ if (!IsEmpty())
+ {
+ SwTextSizeInfo aInf( pFrame );
+ SwTextCursor aLine( pFrame, &aInf );
+
+ aLine.CharCursorToLine(MapModelToViewPos(*pPam->GetPoint()));
+ nRightMargin = aLine.GetStart() + aLine.GetCurr()->GetLen();
+
+ // We skip hard line breaks
+ if( aLine.GetCurr()->GetLen() &&
+ CH_BREAK == aInf.GetText()[sal_Int32(nRightMargin) - 1])
+ --nRightMargin;
+ else if( !bAPI && (aLine.GetNext() || pFrame->GetFollow()) )
+ {
+ while( nRightMargin > aLine.GetStart() &&
+ ' ' == aInf.GetText()[sal_Int32(nRightMargin) - 1])
+ --nRightMargin;
+ }
+ }
+ *pPam->GetPoint() = pFrame->MapViewToModelPos(nRightMargin);
+ SwTextCursor::SetRightMargin( !bAPI );
+ return true;
+}
+
+// The following two methods try to put the Cursor into the next/successive
+// line. If we do not have a preceding/successive line we forward the call
+// to the base class.
+// The Cursor's horizontal justification is done afterwards by the CursorShell.
+
+namespace {
+
+class SwSetToRightMargin
+{
+ bool m_bRight;
+
+public:
+ SwSetToRightMargin()
+ : m_bRight(false)
+ {
+ }
+ ~SwSetToRightMargin() { SwTextCursor::SetRightMargin(m_bRight); }
+ void SetRight(const bool bNew) { m_bRight = bNew; }
+};
+
+}
+
+bool SwTextFrame::UnitUp_( SwPaM *pPam, const SwTwips nOffset,
+ bool bSetInReadOnly ) const
+{
+ // Set the RightMargin if needed
+ SwSetToRightMargin aSet;
+
+ if( IsInTab() &&
+ pPam->GetNode().StartOfSectionNode() !=
+ pPam->GetNode( false ).StartOfSectionNode() )
+ {
+ // If the PaM is located within different boxes, we have a table selection,
+ // which is handled by the base class.
+ return SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
+ }
+
+ const_cast<SwTextFrame*>(this)->GetFormatted();
+ const TextFrameIndex nPos = MapModelToViewPos(*pPam->GetPoint());
+ SwRect aCharBox;
+
+ if( !IsEmpty() && !IsHiddenNow() )
+ {
+ TextFrameIndex nFormat(COMPLETE_STRING);
+ do
+ {
+ if (nFormat != TextFrameIndex(COMPLETE_STRING) && !IsFollow())
+ sw_ChangeOffset( const_cast<SwTextFrame*>(this), nFormat );
+
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(this), &aInf );
+
+ // Optimize away flys with no flow and IsDummy()
+ if( nPos )
+ aLine.CharCursorToLine( nPos );
+ else
+ aLine.Top();
+
+ const SwLineLayout *pPrevLine = aLine.GetPrevLine();
+ const TextFrameIndex nStart = aLine.GetStart();
+ aLine.GetCharRect( &aCharBox, nPos );
+
+ bool bSecondOfDouble = ( aInf.IsMulti() && ! aInf.IsFirstMulti() );
+ bool bPrevLine = ( pPrevLine && pPrevLine != aLine.GetCurr() );
+
+ if( !pPrevLine && !bSecondOfDouble && GetOffset() && !IsFollow() )
+ {
+ nFormat = GetOffset();
+ TextFrameIndex nDiff = aLine.GetLength();
+ if( !nDiff )
+ nDiff = TextFrameIndex(MIN_OFFSET_STEP);
+ if( nFormat > nDiff )
+ nFormat = nFormat - nDiff;
+ else
+ nFormat = TextFrameIndex(0);
+ continue;
+ }
+
+ // We select the target line for the cursor, in case we are in a
+ // double line portion, prev line = curr line
+ if( bPrevLine && !bSecondOfDouble )
+ {
+ aLine.PrevLine();
+ while ( aLine.GetStart() == nStart &&
+ nullptr != ( pPrevLine = aLine.GetPrevLine() ) &&
+ pPrevLine != aLine.GetCurr() )
+ aLine.PrevLine();
+ }
+
+ if ( bPrevLine || bSecondOfDouble )
+ {
+ aCharBox.Width( aCharBox.SSize().Width() / 2 );
+ aCharBox.Pos().setX( aCharBox.Pos().X() - 150 );
+
+ // See comment in SwTextFrame::GetModelPositionForViewPoint()
+#if OSL_DEBUG_LEVEL > 0
+ const SwNodeOffset nOldNode = pPam->GetPoint()->nNode.GetIndex();
+#endif
+ // The node should not be changed
+ TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint(pPam->GetPoint(),
+ aCharBox.Pos(), false );
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
+ "SwTextFrame::UnitUp: illegal node change" );
+#endif
+
+ // We make sure that we move up.
+ if( nTmpOfst >= nStart && nStart && !bSecondOfDouble )
+ {
+ nTmpOfst = nStart;
+ aSet.SetRight( true );
+ }
+ *pPam->GetPoint() = MapViewToModelPos(nTmpOfst);
+ return true;
+ }
+
+ if ( IsFollow() )
+ {
+ aLine.GetCharRect( &aCharBox, nPos );
+ aCharBox.Width( aCharBox.SSize().Width() / 2 );
+ }
+ break;
+ } while ( true );
+ }
+ /* If 'this' is a follow and a prev failed, we need to go to the
+ * last line of the master, which is us.
+ * Or: If we are a follow with follow, we need to get the master.
+ */
+ if ( IsFollow() )
+ {
+ const SwTextFrame *pTmpPrev = FindMaster();
+ TextFrameIndex nOffs = GetOffset();
+ if( pTmpPrev )
+ {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const bool bProtectedAllowed = pSh && pSh->GetViewOptions()->IsCursorInProtectedArea();
+ const SwTextFrame *pPrevPrev = pTmpPrev;
+ // We skip protected frames and frames without content here
+ while( pPrevPrev && ( pPrevPrev->GetOffset() == nOffs ||
+ ( !bProtectedAllowed && pPrevPrev->IsProtected() ) ) )
+ {
+ pTmpPrev = pPrevPrev;
+ nOffs = pTmpPrev->GetOffset();
+ if ( pPrevPrev->IsFollow() )
+ pPrevPrev = pTmpPrev->FindMaster();
+ else
+ pPrevPrev = nullptr;
+ }
+ if ( !pPrevPrev )
+ return pTmpPrev->SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
+ aCharBox.Pos().setY( pPrevPrev->getFrameArea().Bottom() - 1 );
+ return pPrevPrev->GetKeyCursorOfst( pPam->GetPoint(), aCharBox.Pos() );
+ }
+ }
+ return SwContentFrame::UnitUp( pPam, nOffset, bSetInReadOnly );
+}
+
+// Used for Bidi. nPos is the logical position in the string, bLeft indicates
+// if left arrow or right arrow was pressed. The return values are:
+// nPos: the new visual position
+// bLeft: whether the break iterator has to add or subtract from the
+// current position
+static void lcl_VisualMoveRecursion(const SwLineLayout& rCurrLine, TextFrameIndex nIdx,
+ TextFrameIndex & nPos, bool& bRight,
+ sal_uInt8& nCursorLevel, sal_uInt8 nDefaultDir )
+{
+ const SwLinePortion* pPor = rCurrLine.GetFirstPortion();
+ const SwLinePortion* pLast = nullptr;
+
+ // What's the current portion?
+ while ( pPor && nIdx + pPor->GetLen() <= nPos )
+ {
+ nIdx = nIdx + pPor->GetLen();
+ pLast = pPor;
+ pPor = pPor->GetNextPortion();
+ }
+
+ if ( bRight )
+ {
+ bool bRecurse = pPor && pPor->IsMultiPortion() &&
+ static_cast<const SwMultiPortion*>(pPor)->IsBidi();
+
+ // 1. special case: at beginning of bidi portion
+ if ( bRecurse && nIdx == nPos )
+ {
+ nPos = nPos + pPor->GetLen();
+
+ // leave bidi portion
+ if ( nCursorLevel != nDefaultDir )
+ {
+ bRecurse = false;
+ }
+ else
+ // special case:
+ // buffer: abcXYZ123 in LTR paragraph
+ // view: abc123ZYX
+ // cursor is between c and X in the buffer and cursor level = 0
+ nCursorLevel++;
+ }
+
+ // 2. special case: at beginning of portion after bidi portion
+ else if ( pLast && pLast->IsMultiPortion() &&
+ static_cast<const SwMultiPortion*>(pLast)->IsBidi() && nIdx == nPos )
+ {
+ // enter bidi portion
+ if ( nCursorLevel != nDefaultDir )
+ {
+ bRecurse = true;
+ nIdx = nIdx - pLast->GetLen();
+ pPor = pLast;
+ }
+ }
+
+ // Recursion
+ if ( bRecurse )
+ {
+ const SwLineLayout& rLine = static_cast<const SwMultiPortion*>(pPor)->GetRoot();
+ TextFrameIndex nTmpPos = nPos - nIdx;
+ bool bTmpForward = ! bRight;
+ sal_uInt8 nTmpCursorLevel = nCursorLevel;
+ lcl_VisualMoveRecursion(rLine, TextFrameIndex(0), nTmpPos, bTmpForward,
+ nTmpCursorLevel, nDefaultDir + 1 );
+
+ nPos = nTmpPos + nIdx;
+ bRight = bTmpForward;
+ nCursorLevel = nTmpCursorLevel;
+ }
+
+ // go forward
+ else
+ {
+ bRight = true;
+ nCursorLevel = nDefaultDir;
+ }
+
+ }
+ else
+ {
+ bool bRecurse = pPor && pPor->IsMultiPortion() && static_cast<const SwMultiPortion*>(pPor)->IsBidi();
+
+ // 1. special case: at beginning of bidi portion
+ if ( bRecurse && nIdx == nPos )
+ {
+ // leave bidi portion
+ if ( nCursorLevel == nDefaultDir )
+ {
+ bRecurse = false;
+ }
+ }
+
+ // 2. special case: at beginning of portion after bidi portion
+ else if ( pLast && pLast->IsMultiPortion() &&
+ static_cast<const SwMultiPortion*>(pLast)->IsBidi() && nIdx == nPos )
+ {
+ nPos = nPos - pLast->GetLen();
+
+ // enter bidi portion
+ if ( nCursorLevel % 2 == nDefaultDir % 2 )
+ {
+ bRecurse = true;
+ nIdx = nIdx - pLast->GetLen();
+ pPor = pLast;
+
+ // special case:
+ // buffer: abcXYZ123 in LTR paragraph
+ // view: abc123ZYX
+ // cursor is behind 3 in the buffer and cursor level = 2
+ if ( nDefaultDir + 2 == nCursorLevel )
+ nPos = nPos + pLast->GetLen();
+ }
+ }
+
+ // go forward
+ if ( bRecurse )
+ {
+ const SwLineLayout& rLine = static_cast<const SwMultiPortion*>(pPor)->GetRoot();
+ TextFrameIndex nTmpPos = nPos - nIdx;
+ bool bTmpForward = ! bRight;
+ sal_uInt8 nTmpCursorLevel = nCursorLevel;
+ lcl_VisualMoveRecursion(rLine, TextFrameIndex(0), nTmpPos, bTmpForward,
+ nTmpCursorLevel, nDefaultDir + 1 );
+
+ // special case:
+ // buffer: abcXYZ123 in LTR paragraph
+ // view: abc123ZYX
+ // cursor is between Z and 1 in the buffer and cursor level = 2
+ if ( nTmpPos == pPor->GetLen() && nTmpCursorLevel == nDefaultDir + 1 )
+ {
+ nTmpPos = nTmpPos - pPor->GetLen();
+ nTmpCursorLevel = nDefaultDir;
+ bTmpForward = ! bTmpForward;
+ }
+
+ nPos = nTmpPos + nIdx;
+ bRight = bTmpForward;
+ nCursorLevel = nTmpCursorLevel;
+ }
+
+ // go backward
+ else
+ {
+ bRight = false;
+ nCursorLevel = nDefaultDir;
+ }
+ }
+}
+
+void SwTextFrame::PrepareVisualMove(TextFrameIndex & nPos, sal_uInt8& nCursorLevel,
+ bool& bForward, bool bInsertCursor )
+{
+ if( IsEmpty() || IsHiddenNow() )
+ return;
+
+ GetFormatted();
+
+ SwTextSizeInfo aInf(this);
+ SwTextCursor aLine(this, &aInf);
+
+ if( nPos )
+ aLine.CharCursorToLine( nPos );
+ else
+ aLine.Top();
+
+ const SwLineLayout* pLine = aLine.GetCurr();
+ const TextFrameIndex nStt = aLine.GetStart();
+ const TextFrameIndex nLen = pLine->GetLen();
+
+ // We have to distinguish between an insert and overwrite cursor:
+ // The insert cursor position depends on the cursor level:
+ // buffer: abcXYZdef in LTR paragraph
+ // display: abcZYXdef
+ // If cursor is between c and X in the buffer and cursor level is 0,
+ // the cursor blinks between c and Z and -> sets the cursor between Z and Y.
+ // If the cursor level is 1, the cursor blinks between X and d and
+ // -> sets the cursor between d and e.
+ // The overwrite cursor simply travels to the next visual character.
+ if ( bInsertCursor )
+ {
+ lcl_VisualMoveRecursion( *pLine, nStt, nPos, bForward,
+ nCursorLevel, IsRightToLeft() ? 1 : 0 );
+ return;
+ }
+
+ const sal_uInt8 nDefaultDir = static_cast<sal_uInt8>(IsRightToLeft() ? UBIDI_RTL : UBIDI_LTR);
+ const bool bVisualRight = ( nDefaultDir == UBIDI_LTR && bForward ) ||
+ ( nDefaultDir == UBIDI_RTL && ! bForward );
+
+ // Bidi functions from icu 2.0
+
+ const sal_Unicode* pLineString = GetText().getStr();
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( sal_Int32(nLen), 0, &nError );
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(pLineString),
+ sal_Int32(nLen), nDefaultDir, nullptr, &nError );
+
+ TextFrameIndex nTmpPos(0);
+ bool bOutOfBounds = false;
+
+ if ( nPos < nStt + nLen )
+ {
+ nTmpPos = TextFrameIndex(ubidi_getVisualIndex( pBidi, sal_Int32(nPos), &nError ));
+
+ // visual indices are always LTR aligned
+ if ( bVisualRight )
+ {
+ if (nTmpPos + TextFrameIndex(1) < nStt + nLen)
+ ++nTmpPos;
+ else
+ {
+ nPos = nDefaultDir == UBIDI_RTL ? TextFrameIndex(0) : nStt + nLen;
+ bOutOfBounds = true;
+ }
+ }
+ else
+ {
+ if ( nTmpPos )
+ --nTmpPos;
+ else
+ {
+ nPos = nDefaultDir == UBIDI_RTL ? nStt + nLen : TextFrameIndex(0);
+ bOutOfBounds = true;
+ }
+ }
+ }
+ else
+ {
+ nTmpPos = nDefaultDir == UBIDI_LTR ? nPos - TextFrameIndex(1) : TextFrameIndex(0);
+ }
+
+ if ( ! bOutOfBounds )
+ {
+ nPos = TextFrameIndex(ubidi_getLogicalIndex( pBidi, sal_Int32(nTmpPos), &nError ));
+
+ if ( bForward )
+ {
+ if ( nPos )
+ --nPos;
+ else
+ {
+ ++nPos;
+ bForward = ! bForward;
+ }
+ }
+ else
+ ++nPos;
+ }
+
+ ubidi_close( pBidi );
+}
+
+bool SwTextFrame::UnitDown_(SwPaM *pPam, const SwTwips nOffset,
+ bool bSetInReadOnly ) const
+{
+
+ if ( IsInTab() &&
+ pPam->GetNode().StartOfSectionNode() !=
+ pPam->GetNode( false ).StartOfSectionNode() )
+ {
+ // If the PaM is located within different boxes, we have a table selection,
+ // which is handled by the base class.
+ return SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
+ }
+ const_cast<SwTextFrame*>(this)->GetFormatted();
+ const TextFrameIndex nPos = MapModelToViewPos(*pPam->GetPoint());
+ SwRect aCharBox;
+ const SwContentFrame *pTmpFollow = nullptr;
+
+ if ( IsVertical() )
+ const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
+
+ if ( !IsEmpty() && !IsHiddenNow() )
+ {
+ TextFrameIndex nFormat(COMPLETE_STRING);
+ do
+ {
+ if (nFormat != TextFrameIndex(COMPLETE_STRING) && !IsFollow() &&
+ !sw_ChangeOffset( const_cast<SwTextFrame*>(this), nFormat ) )
+ break;
+
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(this), &aInf );
+ nFormat = aLine.GetEnd();
+
+ aLine.CharCursorToLine( nPos );
+
+ const SwLineLayout* pNextLine = aLine.GetNextLine();
+ const TextFrameIndex nStart = aLine.GetStart();
+ aLine.GetCharRect( &aCharBox, nPos );
+
+ bool bFirstOfDouble = ( aInf.IsMulti() && aInf.IsFirstMulti() );
+
+ if( pNextLine || bFirstOfDouble )
+ {
+ aCharBox.Width( aCharBox.SSize().Width() / 2 );
+#if OSL_DEBUG_LEVEL > 0
+ // See comment in SwTextFrame::GetModelPositionForViewPoint()
+ const SwNodeOffset nOldNode = pPam->GetPoint()->nNode.GetIndex();
+#endif
+ if ( pNextLine && ! bFirstOfDouble )
+ aLine.NextLine();
+
+ TextFrameIndex nTmpOfst = aLine.GetModelPositionForViewPoint( pPam->GetPoint(),
+ aCharBox.Pos(), false );
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( nOldNode == pPam->GetPoint()->nNode.GetIndex(),
+ "SwTextFrame::UnitDown: illegal node change" );
+#endif
+
+ // We make sure that we move down.
+ if( nTmpOfst <= nStart && ! bFirstOfDouble )
+ nTmpOfst = nStart + TextFrameIndex(1);
+ *pPam->GetPoint() = MapViewToModelPos(nTmpOfst);
+
+ if ( IsVertical() )
+ const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
+
+ return true;
+ }
+ pTmpFollow = GetFollow();
+ if( nullptr != pTmpFollow )
+ { // Skip protected follows
+ const SwContentFrame* pTmp = pTmpFollow;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( !pSh || !pSh->GetViewOptions()->IsCursorInProtectedArea() )
+ {
+ while( pTmpFollow && pTmpFollow->IsProtected() )
+ {
+ pTmp = pTmpFollow;
+ pTmpFollow = pTmpFollow->GetFollow();
+ }
+ }
+ if( !pTmpFollow ) // Only protected ones left
+ {
+ if ( IsVertical() )
+ const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
+ return pTmp->SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
+ }
+
+ aLine.GetCharRect( &aCharBox, nPos );
+ aCharBox.Width( aCharBox.SSize().Width() / 2 );
+ }
+ else if( !IsFollow() )
+ {
+ TextFrameIndex nTmpLen(aInf.GetText().getLength());
+ if( aLine.GetEnd() < nTmpLen )
+ {
+ if( nFormat <= GetOffset() )
+ {
+ nFormat = std::min(GetOffset() + TextFrameIndex(MIN_OFFSET_STEP),
+ nTmpLen );
+ if( nFormat <= GetOffset() )
+ break;
+ }
+ continue;
+ }
+ }
+ break;
+ } while( true );
+ }
+ else
+ pTmpFollow = GetFollow();
+
+ if ( IsVertical() )
+ const_cast<SwTextFrame*>(this)->SwapWidthAndHeight();
+
+ // We take a shortcut for follows
+ if( pTmpFollow )
+ {
+ aCharBox.Pos().setY( pTmpFollow->getFrameArea().Top() + 1 );
+ return static_cast<const SwTextFrame*>(pTmpFollow)->GetKeyCursorOfst( pPam->GetPoint(),
+ aCharBox.Pos() );
+ }
+ return SwContentFrame::UnitDown( pPam, nOffset, bSetInReadOnly );
+}
+
+bool SwTextFrame::UnitUp(SwPaM *pPam, const SwTwips nOffset,
+ bool bSetInReadOnly ) const
+{
+ /* We call ContentNode::GertFrame() in CursorSh::Up().
+ * This _always returns the master.
+ * In order to not mess with cursor travelling, we correct here
+ * in SwTextFrame.
+ * We calculate UnitUp for pFrame. pFrame is either a master (= this) or a
+ * follow (!= this).
+ */
+ const SwTextFrame *pFrame = GetAdjFrameAtPos( const_cast<SwTextFrame*>(this), *(pPam->GetPoint()),
+ SwTextCursor::IsRightMargin() );
+ const bool bRet = pFrame->UnitUp_( pPam, nOffset, bSetInReadOnly );
+
+ // No SwTextCursor::SetRightMargin( false );
+ // Instead we have a SwSetToRightMargin in UnitUp_
+ return bRet;
+}
+
+bool SwTextFrame::UnitDown(SwPaM *pPam, const SwTwips nOffset,
+ bool bSetInReadOnly ) const
+{
+ const SwTextFrame *pFrame = GetAdjFrameAtPos(const_cast<SwTextFrame*>(this), *(pPam->GetPoint()),
+ SwTextCursor::IsRightMargin() );
+ const bool bRet = pFrame->UnitDown_( pPam, nOffset, bSetInReadOnly );
+ SwTextCursor::SetRightMargin( false );
+ return bRet;
+}
+
+void SwTextFrame::FillCursorPos( SwFillData& rFill ) const
+{
+ if( !rFill.bColumn && GetUpper()->IsColBodyFrame() ) // ColumnFrames now with BodyFrame
+ {
+ const SwColumnFrame* pTmp =
+ static_cast<const SwColumnFrame*>(GetUpper()->GetUpper()->GetUpper()->Lower()); // The 1st column
+ // The first SwFrame in BodyFrame of the first column
+ const SwFrame* pFrame = static_cast<const SwLayoutFrame*>(pTmp->Lower())->Lower();
+ sal_uInt16 nNextCol = 0;
+ // In which column do we end up in?
+ while( rFill.X() > pTmp->getFrameArea().Right() && pTmp->GetNext() )
+ {
+ pTmp = static_cast<const SwColumnFrame*>(pTmp->GetNext());
+ if( static_cast<const SwLayoutFrame*>(pTmp->Lower())->Lower() ) // ColumnFrames now with BodyFrame
+ {
+ pFrame = static_cast<const SwLayoutFrame*>(pTmp->Lower())->Lower();
+ nNextCol = 0;
+ }
+ else
+ ++nNextCol; // Empty columns require column breaks
+ }
+ if( pTmp != GetUpper()->GetUpper() ) // Did we end up in another column?
+ {
+ if( !pFrame )
+ return;
+ if( nNextCol )
+ {
+ while( pFrame->GetNext() )
+ pFrame = pFrame->GetNext();
+ }
+ else
+ {
+ while( pFrame->GetNext() && pFrame->getFrameArea().Bottom() < rFill.Y() )
+ pFrame = pFrame->GetNext();
+ }
+ // No filling, if the last frame in the targeted column does
+ // not contain a paragraph, but e.g. a table
+ if( pFrame->IsTextFrame() )
+ {
+ rFill.Fill().nColumnCnt = nNextCol;
+ rFill.bColumn = true;
+ if( rFill.pPos )
+ {
+ SwTextFrame const*const pTextFrame(static_cast<const SwTextFrame*>(pFrame));
+ *rFill.pPos = pTextFrame->MapViewToModelPos(
+ TextFrameIndex(pTextFrame->GetText().getLength()));
+ }
+ if( nNextCol )
+ {
+ rFill.aFrame = pTmp->getFramePrintArea();
+ rFill.aFrame += pTmp->getFrameArea().Pos();
+ }
+ else
+ rFill.aFrame = pFrame->getFrameArea();
+ static_cast<const SwTextFrame*>(pFrame)->FillCursorPos( rFill );
+ }
+ return;
+ }
+ }
+ std::unique_ptr<SwFont> pFnt;
+ SwTextFormatColl* pColl = GetTextNodeForParaProps()->GetTextColl();
+ SwTwips nFirst = GetTextNodeForParaProps()->GetSwAttrSet().GetULSpace().GetLower();
+ SwTwips nDiff = rFill.Y() - getFrameArea().Bottom();
+ if( nDiff < nFirst )
+ nDiff = -1;
+ else
+ pColl = &pColl->GetNextTextFormatColl();
+ SwAttrSet aSet(const_cast<SwDoc&>(GetDoc()).GetAttrPool(), aTextFormatCollSetRange );
+ const SwAttrSet* pSet = &pColl->GetAttrSet();
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if (GetTextNodeForParaProps()->HasSwAttrSet())
+ {
+ // sw_redlinehide: pSet is mostly used for para props, but there are
+ // accesses to char props via pFnt - why does it use only the node's
+ // props for this, and not hints?
+ aSet.Put( *GetTextNodeForParaProps()->GetpSwAttrSet() );
+ aSet.SetParent( pSet );
+ pSet = &aSet;
+ pFnt.reset(new SwFont( pSet, &GetDoc().getIDocumentSettingAccess() ));
+ }
+ else
+ {
+ SwFontAccess aFontAccess( pColl, pSh );
+ pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
+ pFnt->CheckFontCacheId( pSh, pFnt->GetActual() );
+ }
+ OutputDevice* pOut = pSh->GetOut();
+ if( !pSh->GetViewOptions()->getBrowseMode() || pSh->GetViewOptions()->IsPrtFormat() )
+ pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
+
+ pFnt->SetFntChg( true );
+ pFnt->ChgPhysFnt( pSh, *pOut );
+
+ SwTwips nLineHeight = pFnt->GetHeight( pSh, *pOut );
+
+ bool bFill = false;
+ if( nLineHeight )
+ {
+ bFill = true;
+ const SvxULSpaceItem &rUL = pSet->GetULSpace();
+ SwTwips nDist = std::max( rUL.GetLower(), rUL.GetUpper() );
+ if( rFill.Fill().nColumnCnt )
+ {
+ rFill.aFrame.Height( nLineHeight );
+ nDiff = rFill.Y() - rFill.Bottom();
+ nFirst = 0;
+ }
+ else if( nDist < nFirst )
+ nFirst = nFirst - nDist;
+ else
+ nFirst = 0;
+ nDist = std::max( nDist, SwTwips(GetLineSpace()) );
+ nDist += nLineHeight;
+ nDiff -= nFirst;
+
+ if( nDiff > 0 )
+ {
+ nDiff /= nDist;
+ rFill.Fill().nParaCnt = o3tl::narrowing<sal_uInt16>(nDiff + 1);
+ rFill.nLineWidth = 0;
+ rFill.bInner = false;
+ rFill.bEmpty = true;
+ rFill.SetOrient( text::HoriOrientation::LEFT );
+ }
+ else
+ nDiff = -1;
+ if( rFill.bInner )
+ bFill = false;
+ else
+ {
+ const SvxTabStopItem &rRuler = pSet->GetTabStops();
+ const SvxLRSpaceItem &rLRSpace = pSet->GetLRSpace();
+
+ SwRect &rRect = rFill.Fill().aCursor;
+ rRect.Top( rFill.Bottom() + (nDiff+1) * nDist - nLineHeight );
+ if( nFirst && nDiff > -1 )
+ rRect.Top( rRect.Top() + nFirst );
+ rRect.Height( nLineHeight );
+ SwTwips nLeft = rFill.Left() + rLRSpace.GetLeft() +
+ GetTextNodeForParaProps()->GetLeftMarginWithNum();
+ SwTwips nRight = rFill.Right() - rLRSpace.GetRight();
+ SwTwips nCenter = ( nLeft + nRight ) / 2;
+ rRect.Left( nLeft );
+ if( SwFillMode::Margin == rFill.Mode() )
+ {
+ if( rFill.bEmpty )
+ {
+ rFill.SetOrient( text::HoriOrientation::LEFT );
+ if( rFill.X() < nCenter )
+ {
+ if( rFill.X() > ( nLeft + 2 * nCenter ) / 3 )
+ {
+ rFill.SetOrient( text::HoriOrientation::CENTER );
+ rRect.Left( nCenter );
+ }
+ }
+ else if( rFill.X() > ( nRight + 2 * nCenter ) / 3 )
+ {
+ rFill.SetOrient( text::HoriOrientation::RIGHT );
+ rRect.Left( nRight );
+ }
+ else
+ {
+ rFill.SetOrient( text::HoriOrientation::CENTER );
+ rRect.Left( nCenter );
+ }
+ }
+ else
+ bFill = false;
+ }
+ else
+ {
+ SwTwips nSpace = 0;
+ if( SwFillMode::Tab != rFill.Mode() )
+ {
+ SwDrawTextInfo aDrawInf( pSh, *pOut, " ", 0, 2 );
+ nSpace = pFnt->GetTextSize_( aDrawInf ).Width()/2;
+ }
+ if( rFill.X() >= nRight )
+ {
+ if( SwFillMode::Indent != rFill.Mode() && ( rFill.bEmpty ||
+ rFill.X() > rFill.nLineWidth + FILL_MIN_DIST ) )
+ {
+ rFill.SetOrient( text::HoriOrientation::RIGHT );
+ rRect.Left( nRight );
+ }
+ else
+ bFill = false;
+ }
+ else if( SwFillMode::Indent == rFill.Mode() )
+ {
+ SwTwips nIndent = rFill.X();
+ if( !rFill.bEmpty || nIndent > nRight )
+ bFill = false;
+ else
+ {
+ nIndent -= rFill.Left();
+ if( nIndent >= 0 && nSpace )
+ {
+ nIndent /= nSpace;
+ nIndent *= nSpace;
+ rFill.SetTab( sal_uInt16( nIndent ) );
+ rRect.Left( nIndent + rFill.Left() );
+ }
+ else
+ bFill = false;
+ }
+ }
+ else if( rFill.X() > nLeft )
+ {
+ SwTwips nTextLeft = rFill.Left() + rLRSpace.GetTextLeft() +
+ GetTextNodeForParaProps()->GetLeftMarginWithNum(true);
+ rFill.nLineWidth += rFill.bFirstLine ? nLeft : nTextLeft;
+ SwTwips nLeftTab;
+ SwTwips nRightTab = nLeft;
+ sal_uInt16 nSpaceCnt = 0;
+ sal_uInt16 nSpaceOnlyCnt = 0;
+ sal_uInt16 nTabCnt = 0;
+ sal_uInt16 nIdx = 0;
+ do
+ {
+ nLeftTab = nRightTab;
+ if( nIdx < rRuler.Count() )
+ {
+ const SvxTabStop &rTabStop = rRuler.operator[](nIdx);
+ nRightTab = nTextLeft + rTabStop.GetTabPos();
+ if( nLeftTab < nTextLeft && nRightTab > nTextLeft )
+ nRightTab = nTextLeft;
+ else
+ ++nIdx;
+ if( nRightTab > rFill.nLineWidth )
+ ++nTabCnt;
+ }
+ else
+ {
+ const SvxTabStopItem& rTab =
+ pSet->GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
+ const SwTwips nDefTabDist = rTab[0].GetTabPos();
+ nRightTab = nLeftTab - nTextLeft;
+ nRightTab /= nDefTabDist;
+ nRightTab = nRightTab * nDefTabDist + nTextLeft;
+ while ( nRightTab <= nLeftTab )
+ nRightTab += nDefTabDist;
+ if( nRightTab > rFill.nLineWidth )
+ ++nTabCnt;
+ while ( nRightTab < rFill.X() )
+ {
+ nRightTab += nDefTabDist;
+ if( nRightTab > rFill.nLineWidth )
+ ++nTabCnt;
+ }
+ if( nLeftTab < nRightTab - nDefTabDist )
+ nLeftTab = nRightTab - nDefTabDist;
+ }
+ if( nRightTab > nRight )
+ nRightTab = nRight;
+ }
+ while( rFill.X() > nRightTab );
+ --nTabCnt;
+ if( SwFillMode::TabSpace == rFill.Mode() )
+ {
+ if( nSpace > 0 )
+ {
+ if( !nTabCnt )
+ nLeftTab = rFill.nLineWidth;
+ while( nLeftTab < rFill.X() )
+ {
+ nLeftTab += nSpace;
+ ++nSpaceCnt;
+ }
+ if( nSpaceCnt )
+ {
+ nLeftTab -= nSpace;
+ --nSpaceCnt;
+ }
+ if( rFill.X() - nLeftTab > nRightTab - rFill.X() )
+ {
+ nSpaceCnt = 0;
+ ++nTabCnt;
+ rRect.Left( nRightTab );
+ }
+ else
+ {
+ if( rFill.X() - nLeftTab > nSpace/2 )
+ {
+ ++nSpaceCnt;
+ rRect.Left( nLeftTab + nSpace );
+ }
+ else
+ rRect.Left( nLeftTab );
+ }
+ }
+ else if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
+ rRect.Left( nLeftTab );
+ else
+ {
+ if( nRightTab >= nRight )
+ {
+ rFill.SetOrient( text::HoriOrientation::RIGHT );
+ rRect.Left( nRight );
+ nTabCnt = 0;
+ nSpaceCnt = 0;
+ }
+ else
+ {
+ rRect.Left( nRightTab );
+ ++nTabCnt;
+ }
+ }
+ }
+ else if( SwFillMode::Space == rFill.Mode() )
+ {
+ SwTwips nLeftSpace = nLeft;
+ while( nLeftSpace < rFill.X() )
+ {
+ nLeftSpace += nSpace;
+ ++nSpaceOnlyCnt;
+ }
+ rRect.Left( nLeftSpace );
+ }
+ else
+ {
+ if( rFill.X() - nLeftTab < nRightTab - rFill.X() )
+ rRect.Left( nLeftTab );
+ else
+ {
+ if( nRightTab >= nRight )
+ {
+ rFill.SetOrient( text::HoriOrientation::RIGHT );
+ rRect.Left( nRight );
+ nTabCnt = 0;
+ nSpaceCnt = 0;
+ }
+ else
+ {
+ rRect.Left( nRightTab );
+ ++nTabCnt;
+ }
+ }
+ }
+ rFill.SetTab( nTabCnt );
+ rFill.SetSpace( nSpaceCnt );
+ rFill.SetSpaceOnly( nSpaceOnlyCnt );
+ if( bFill )
+ {
+ if( std::abs( rFill.X() - nCenter ) <=
+ std::abs( rFill.X() - rRect.Left() ) )
+ {
+ rFill.SetOrient( text::HoriOrientation::CENTER );
+ rFill.SetTab( 0 );
+ rFill.SetSpace( 0 );
+ rFill.SetSpaceOnly( 0 );
+ rRect.Left( nCenter );
+ }
+ if( !rFill.bEmpty )
+ rFill.nLineWidth += FILL_MIN_DIST;
+ if( rRect.Left() < rFill.nLineWidth )
+ bFill = false;
+ }
+ }
+ }
+ // Do we extend over the page's/column's/etc. lower edge?
+ const SwFrame* pUp = GetUpper();
+ if( pUp->IsInSct() )
+ {
+ if( pUp->IsSctFrame() )
+ pUp = pUp->GetUpper();
+ else if( pUp->IsColBodyFrame() &&
+ pUp->GetUpper()->GetUpper()->IsSctFrame() )
+ pUp = pUp->GetUpper()->GetUpper()->GetUpper();
+ }
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nLimit = aRectFnSet.GetPrtBottom(*pUp);
+ SwTwips nRectBottom = rRect.Bottom();
+ if ( aRectFnSet.IsVert() )
+ nRectBottom = SwitchHorizontalToVertical( nRectBottom );
+
+ if( aRectFnSet.YDiff( nLimit, nRectBottom ) < 0 )
+ bFill = false;
+ else
+ rRect.Width( 1 );
+ }
+ }
+ const_cast<SwCursorMoveState*>(rFill.pCMS)->m_bFillRet = bFill;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
new file mode 100644
index 000000000..e2bff47bd
--- /dev/null
+++ b/sw/source/core/text/frmform.cxx
@@ -0,0 +1,2104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <IDocumentRedlineAccess.hxx>
+#include <anchoredobject.hxx>
+#include <bodyfrm.hxx>
+#include <hintids.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <pagefrm.hxx>
+#include <ndtxt.hxx>
+#include <ftnfrm.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <paratr.hxx>
+#include <viewopt.hxx>
+#include <viewsh.hxx>
+#include <frmatr.hxx>
+#include <pam.hxx>
+#include <fmtanchr.hxx>
+#include "itrform2.hxx"
+#include "widorp.hxx"
+#include "txtcache.hxx"
+#include <sectfrm.hxx>
+#include <rootfrm.hxx>
+#include <frmfmt.hxx>
+#include <sortedobjs.hxx>
+#include <editeng/tstpitem.hxx>
+#include <redline.hxx>
+#include <comphelper/lok.hxx>
+
+// Tolerance in formatting and text output
+#define SLOPPY_TWIPS 5
+
+namespace {
+
+class FormatLevel
+{
+ static sal_uInt16 s_nLevel;
+public:
+ FormatLevel() { ++s_nLevel; }
+ ~FormatLevel() { --s_nLevel; }
+ static sal_uInt16 GetLevel() { return s_nLevel; }
+ static bool LastLevel() { return 10 < s_nLevel; }
+};
+
+}
+
+sal_uInt16 FormatLevel::s_nLevel = 0;
+
+void ValidateText( SwFrame *pFrame ) // Friend of frame
+{
+ if ( ( ! pFrame->IsVertical() &&
+ pFrame->getFrameArea().Width() == pFrame->GetUpper()->getFramePrintArea().Width() ) ||
+ ( pFrame->IsVertical() &&
+ pFrame->getFrameArea().Height() == pFrame->GetUpper()->getFramePrintArea().Height() ) )
+ {
+ pFrame->setFrameAreaSizeValid(true);
+ }
+}
+
+void SwTextFrame::ValidateFrame()
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ // Validate surroundings to avoid oscillation
+ SwSwapIfSwapped swap( this );
+
+ if ( !IsInFly() && !IsInTab() )
+ { // Only validate 'this' when inside a fly, the rest should actually only be
+ // needed for footnotes, which do not exist in flys.
+ SwSectionFrame* pSct = FindSctFrame();
+ if( pSct )
+ {
+ if( !pSct->IsColLocked() )
+ pSct->ColLock();
+ else
+ pSct = nullptr;
+ }
+
+ SwFrame *pUp = GetUpper();
+ pUp->Calc(pRenderContext);
+ if( pSct )
+ pSct->ColUnlock();
+ }
+ ValidateText( this );
+
+ // We at least have to save the MustFit flag!
+ assert(HasPara() && "ResetPreps(), missing ParaPortion, SwCache bug?");
+ SwParaPortion *pPara = GetPara();
+ const bool bMustFit = pPara->IsPrepMustFit();
+ ResetPreps();
+ pPara->SetPrepMustFit( bMustFit );
+}
+
+// After a RemoveFootnote the BodyFrame and all Frames contained within it, need to be
+// recalculated, so that the DeadLine is right.
+// First we search outwards, on the way back we calculate everything.
+static void ValidateBodyFrame_( SwFrame *pFrame )
+{
+ vcl::RenderContext* pRenderContext = pFrame ? pFrame->getRootFrame()->GetCurrShell()->GetOut() : nullptr;
+ if( !pFrame || pFrame->IsCellFrame() )
+ return;
+
+ if( !pFrame->IsBodyFrame() && pFrame->GetUpper() )
+ ValidateBodyFrame_( pFrame->GetUpper() );
+ if( !pFrame->IsSctFrame() )
+ pFrame->Calc(pRenderContext);
+ else
+ {
+ const bool bOld = static_cast<SwSectionFrame*>(pFrame)->IsContentLocked();
+ static_cast<SwSectionFrame*>(pFrame)->SetContentLock( true );
+ pFrame->Calc(pRenderContext);
+ if( !bOld )
+ static_cast<SwSectionFrame*>(pFrame)->SetContentLock( false );
+ }
+}
+
+void SwTextFrame::ValidateBodyFrame()
+{
+ SwSwapIfSwapped swap( this );
+
+ // See comment in ValidateFrame()
+ if ( !IsInFly() && !IsInTab() &&
+ !( IsInSct() && FindSctFrame()->Lower()->IsColumnFrame() ) )
+ ValidateBodyFrame_( GetUpper() );
+}
+
+bool SwTextFrame::GetDropRect_( SwRect &rRect ) const
+{
+ SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
+
+ OSL_ENSURE( HasPara(), "SwTextFrame::GetDropRect_: try again next year." );
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(this) );
+ SwTextMargin aLine( const_cast<SwTextFrame*>(this), &aInf );
+ if( aLine.GetDropLines() )
+ {
+ rRect.Top( aLine.Y() );
+ rRect.Left( aLine.GetLineStart() );
+ rRect.Height( aLine.GetDropHeight() );
+ rRect.Width( aLine.GetDropLeft() );
+
+ if ( IsRightToLeft() )
+ SwitchLTRtoRTL( rRect );
+
+ if ( IsVertical() )
+ SwitchHorizontalToVertical( rRect );
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextFrame::CalcFollow(TextFrameIndex const nTextOfst)
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ SwSwapIfSwapped swap( this );
+
+ OSL_ENSURE( HasFollow(), "CalcFollow: missing Follow." );
+
+ SwTextFrame* pMyFollow = GetFollow();
+
+ SwParaPortion *pPara = GetPara();
+ const bool bFollowField = pPara && pPara->IsFollowField();
+
+ if( !pMyFollow->GetOffset() || pMyFollow->GetOffset() != nTextOfst ||
+ bFollowField || pMyFollow->IsFieldFollow() ||
+ ( pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Width() ) ||
+ ( ! pMyFollow->IsVertical() && !pMyFollow->getFramePrintArea().Height() ) )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const SwFrame *pOldUp = GetUpper();
+#endif
+
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nOldBottom = aRectFnSet.GetBottom(GetUpper()->getFrameArea());
+ SwTwips nMyPos = aRectFnSet.GetTop(getFrameArea());
+
+ const SwPageFrame *pPage = nullptr;
+ bool bOldInvaContent = true;
+ if ( !IsInFly() && GetNext() )
+ {
+ pPage = FindPageFrame();
+ // Minimize (reset if possible) invalidations: see below
+ bOldInvaContent = pPage->IsInvalidContent();
+ }
+
+ pMyFollow->SetOffset_( nTextOfst );
+ pMyFollow->SetFieldFollow( bFollowField );
+ if( HasFootnote() || pMyFollow->HasFootnote() )
+ {
+ ValidateFrame();
+ ValidateBodyFrame();
+ if( pPara )
+ {
+ pPara->GetReformat() = SwCharRange();
+ pPara->SetDelta(0);
+ }
+ }
+
+ // The footnote area must not get larger
+ SwSaveFootnoteHeight aSave( FindFootnoteBossFrame( true ), LONG_MAX );
+
+ pMyFollow->CalcFootnoteFlag();
+ if ( !pMyFollow->GetNext() && !pMyFollow->HasFootnote() )
+ nOldBottom = aRectFnSet.IsVert() ? 0 : LONG_MAX;
+
+ // tdf#122892 check flag:
+ // 1. WidowsAndOrphans::FindWidows() determines follow is a widow
+ // 2. SwTextFrame::PrepWidows() calls SetPrepWidows() on master;
+ // if it can spare lines, master truncates one line
+ // 3. SwTextFrame::CalcPreps() on master (below);
+ // unless IsPrepMustFit(), if master hasn't shrunk via 2., it will SetWidow()
+ // 4. loop must exit then, because the follow didn't grow so nothing will ever change
+ while (!IsWidow())
+ {
+ if( !FormatLevel::LastLevel() )
+ {
+ // If the follow is contained within a column section or column
+ // frame, we need to calculate that first. This is because the
+ // FormatWidthCols() does not work if it is called from MakeAll
+ // of the _locked_ follow.
+ SwSectionFrame* pSct = pMyFollow->FindSctFrame();
+ if( pSct && !pSct->IsAnLower( this ) )
+ {
+ if( pSct->GetFollow() )
+ pSct->SimpleFormat();
+ else if( ( pSct->IsVertical() && !pSct->getFrameArea().Width() ) ||
+ ( ! pSct->IsVertical() && !pSct->getFrameArea().Height() ) )
+ break;
+ }
+ // i#11760 - Intrinsic format of follow is controlled.
+ if ( FollowFormatAllowed() )
+ {
+ // i#11760 - No nested format of follows, if
+ // text frame is contained in a column frame.
+ // Thus, forbid intrinsic format of follow.
+ {
+ bool bIsFollowInColumn = false;
+ SwFrame* pFollowUpper = pMyFollow->GetUpper();
+ while ( pFollowUpper )
+ {
+ if ( pFollowUpper->IsColumnFrame() )
+ {
+ bIsFollowInColumn = true;
+ break;
+ }
+ if ( pFollowUpper->IsPageFrame() ||
+ pFollowUpper->IsFlyFrame() )
+ {
+ break;
+ }
+ pFollowUpper = pFollowUpper->GetUpper();
+ }
+ if ( bIsFollowInColumn )
+ {
+ pMyFollow->ForbidFollowFormat();
+ }
+ }
+
+ pMyFollow->Calc(pRenderContext);
+ // The Follow can tell from its getFrameArea().Height() that something went wrong
+ OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: cheesy follow" );
+ if( pMyFollow->GetPrev() )
+ {
+ pMyFollow->Prepare();
+ pMyFollow->Calc(pRenderContext);
+ OSL_ENSURE( !pMyFollow->GetPrev(), "SwTextFrame::CalcFollow: very cheesy follow" );
+ }
+
+ // i#11760 - Reset control flag for follow format.
+ pMyFollow->AllowFollowFormat();
+ }
+
+ // Make sure that the Follow gets painted
+ pMyFollow->SetCompletePaint();
+ }
+
+ pPara = GetPara();
+ // As long as the Follow requests lines due to Orphans, it is
+ // passed these and is formatted again if possible
+ if( pPara && pPara->IsPrepWidows() )
+ CalcPreps();
+ else
+ break;
+ }
+
+ if( HasFootnote() || pMyFollow->HasFootnote() )
+ {
+ ValidateBodyFrame();
+ ValidateFrame();
+ if( pPara )
+ {
+ pPara->GetReformat() = SwCharRange();
+ pPara->SetDelta(0);
+ }
+ }
+
+ if ( pPage && !bOldInvaContent )
+ pPage->ValidateContent();
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( pOldUp == GetUpper(), "SwTextFrame::CalcFollow: heavy follow" );
+#endif
+
+ const tools::Long nRemaining =
+ - aRectFnSet.BottomDist( GetUpper()->getFrameArea(), nOldBottom );
+ if ( nRemaining > 0 &&
+ nRemaining != ( aRectFnSet.IsVert() ?
+ nMyPos - getFrameArea().Right() :
+ getFrameArea().Top() - nMyPos ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SwTextFrame::MakePos()
+{
+ SwFrame::MakePos();
+ // Inform LOK clients about change in position of redlines (if any)
+ if(!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ SwTextNode const* pTextNode = GetTextNodeFirst();
+ const SwRedlineTable& rTable = pTextNode->getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
+ {
+ SwRangeRedline* pRedln = rTable[nRedlnPos];
+ if (pTextNode->GetIndex() == pRedln->GetPoint()->nNode.GetNode().GetIndex())
+ {
+ pRedln->MaybeNotifyRedlinePositionModification(getFrameArea().Top());
+ if (GetMergedPara()
+ && pRedln->GetType() == RedlineType::Delete
+ && pRedln->GetPoint()->nNode != pRedln->GetMark()->nNode)
+ {
+ pTextNode = pRedln->End()->nNode.GetNode().GetTextNode();
+ }
+ }
+ }
+}
+
+void SwTextFrame::AdjustFrame( const SwTwips nChgHght, bool bHasToFit )
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ if( IsUndersized() )
+ {
+ if( GetOffset() && !IsFollow() ) // A scrolled paragraph (undersized)
+ return;
+ SetUndersized( nChgHght == 0 || bHasToFit );
+ }
+
+ // AdjustFrame is called with a swapped frame during
+ // formatting but the frame is not swapped during FormatEmpty
+ SwSwapIfSwapped swap( this );
+ SwRectFnSet aRectFnSet(this);
+
+ // The Frame's size variable is incremented by Grow or decremented by Shrink.
+ // If the size cannot change, nothing should happen!
+ if( nChgHght >= 0)
+ {
+ SwTwips nChgHeight = nChgHght;
+ if( nChgHght && !bHasToFit )
+ {
+ if( IsInFootnote() && !IsInSct() )
+ {
+ SwTwips nReal = Grow( nChgHght, true );
+ if( nReal < nChgHght )
+ {
+ SwTwips nBot = aRectFnSet.YInc( aRectFnSet.GetBottom(getFrameArea()),
+ nChgHght - nReal );
+ SwFrame* pCont = FindFootnoteFrame()->GetUpper();
+
+ if( aRectFnSet.BottomDist( pCont->getFrameArea(), nBot ) > 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, nChgHght );
+
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+
+ if( aRectFnSet.IsVert() )
+ {
+ aPrt.AddWidth(nChgHght );
+ }
+ else
+ {
+ aPrt.AddHeight(nChgHght );
+ }
+
+ return;
+ }
+ }
+ }
+
+ Grow( nChgHght );
+
+ if ( IsInFly() )
+ {
+ // If one of the Upper is a Fly, it's very likely that this fly changes its
+ // position by the Grow. Therefore, my position has to be corrected also or
+ // the check further down is not meaningful.
+ // The predecessors need to be calculated, so that the position can be
+ // calculated correctly.
+ if ( GetPrev() )
+ {
+ SwFrame *pPre = GetUpper()->Lower();
+ do
+ { pPre->Calc(pRenderContext);
+ pPre = pPre->GetNext();
+ } while ( pPre && pPre != this );
+ }
+ const Point aOldPos( getFrameArea().Pos() );
+ MakePos();
+ if ( aOldPos != getFrameArea().Pos() )
+ {
+ InvalidateObjs(false);
+ }
+ }
+ nChgHeight = 0;
+ }
+ // A Grow() is always accepted by the Layout, even if the
+ // FixSize of the surrounding layout frame should not allow it.
+ // We text for this case and correct the values.
+ // The Frame must NOT be shrunk further than its size permits
+ // even in the case of an emergency.
+ SwTwips nRstHeight;
+ if ( IsVertical() )
+ {
+ OSL_ENSURE( ! IsSwapped(),"Swapped frame while calculating nRstHeight" );
+
+ if ( IsVertLR() )
+ nRstHeight = GetUpper()->getFrameArea().Left()
+ + GetUpper()->getFramePrintArea().Left()
+ + GetUpper()->getFramePrintArea().Width()
+ - getFrameArea().Left();
+ else
+ nRstHeight = getFrameArea().Left() + getFrameArea().Width() -
+ ( GetUpper()->getFrameArea().Left() + GetUpper()->getFramePrintArea().Left() );
+ }
+ else
+ nRstHeight = GetUpper()->getFrameArea().Top()
+ + GetUpper()->getFramePrintArea().Top()
+ + GetUpper()->getFramePrintArea().Height()
+ - getFrameArea().Top();
+
+ // We can get a bit of space in table cells, because there could be some
+ // left through a vertical alignment to the top.
+ // Assure that first lower in upper is the current one or is valid.
+ if ( IsInTab() &&
+ ( GetUpper()->Lower() == this ||
+ GetUpper()->Lower()->isFrameAreaDefinitionValid() ) )
+ {
+ tools::Long nAdd = aRectFnSet.YDiff( aRectFnSet.GetTop(GetUpper()->Lower()->getFrameArea()),
+ aRectFnSet.GetPrtTop(*GetUpper()) );
+ OSL_ENSURE( nAdd >= 0, "Ey" );
+ nRstHeight += nAdd;
+ }
+
+ // nRstHeight < 0 means that the TextFrame is located completely outside of its Upper.
+ // This can happen, if it's located within a FlyAtContentFrame, which changed sides by a
+ // Grow(). In such a case, it's wrong to execute the following Grow().
+ // In the case of a bug, we end up with an infinite loop.
+ SwTwips nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ SwTwips nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+
+ if( nRstHeight < nFrameHeight )
+ {
+ // It can be that I have the right size, but the Upper is too small and can get me some room
+ if( ( nRstHeight >= 0 || ( IsInFootnote() && IsInSct() ) ) && !bHasToFit )
+ nRstHeight += GetUpper()->Grow( nFrameHeight - nRstHeight );
+ // In column sections we do not want to get too big or else more areas are created by
+ // GetNextSctLeaf. Instead, we shrink and remember bUndersized, so that FormatWidthCols
+ // can calculate the right column size.
+ if ( nRstHeight < nFrameHeight )
+ {
+ if( bHasToFit || !IsMoveable() ||
+ ( IsInSct() && !FindSctFrame()->MoveAllowed(this) ) )
+ {
+ SetUndersized( true );
+ Shrink( std::min( ( nFrameHeight - nRstHeight), nPrtHeight ) );
+ }
+ else
+ SetUndersized( false );
+ }
+ }
+ else if( nChgHeight )
+ {
+ if( nRstHeight - nFrameHeight < nChgHeight )
+ nChgHeight = nRstHeight - nFrameHeight;
+ if( nChgHeight )
+ Grow( nChgHeight );
+ }
+ }
+ else
+ Shrink( -nChgHght );
+}
+
+css::uno::Sequence< css::style::TabStop > SwTextFrame::GetTabStopInfo( SwTwips CurrentPos )
+{
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
+ SwTextFormatter aLine( this, &aInf );
+ SwTextCursor TextCursor( this, &aInf );
+ const Point aCharPos( TextCursor.GetTopLeft() );
+
+ SwTwips nRight = aLine.Right();
+ CurrentPos -= aCharPos.X();
+
+ // get current tab stop information stored in the Frame
+ const SvxTabStop *pTS = aLine.GetLineInfo().GetTabStop( CurrentPos, nRight );
+
+ if( !pTS )
+ {
+ return {};
+ }
+
+ // copy tab stop information into a Sequence, which only contains one element.
+ css::style::TabStop ts;
+ ts.Position = pTS->GetTabPos();
+ ts.DecimalChar = pTS->GetDecimal();
+ ts.FillChar = pTS->GetFill();
+ switch( pTS->GetAdjustment() )
+ {
+ case SvxTabAdjust::Left : ts.Alignment = css::style::TabAlign_LEFT; break;
+ case SvxTabAdjust::Center : ts.Alignment = css::style::TabAlign_CENTER; break;
+ case SvxTabAdjust::Right : ts.Alignment = css::style::TabAlign_RIGHT; break;
+ case SvxTabAdjust::Decimal: ts.Alignment = css::style::TabAlign_DECIMAL; break;
+ case SvxTabAdjust::Default: ts.Alignment = css::style::TabAlign_DEFAULT; break;
+ default: break; // prevent warning
+ }
+
+ return { ts };
+}
+
+// AdjustFollow expects the following situation:
+// The SwTextIter points to the lower end of the Master, the Offset is set in the Follow.
+// nOffset holds the Offset in the text string, from which the Master closes
+// and the Follow starts.
+// If it's 0, the FollowFrame is deleted.
+void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
+ const TextFrameIndex nOffset, const TextFrameIndex nEnd,
+ const sal_uInt8 nMode )
+{
+ SwFrameSwapper aSwapper( this, false );
+
+ // We got the rest of the text mass: Delete all Follows
+ // DummyPortions() are a special case.
+ // Special cases are controlled by parameter <nMode>.
+ if( HasFollow() && !(nMode & 1) && nOffset == nEnd )
+ {
+ while( GetFollow() )
+ {
+ if( GetFollow()->IsLocked() )
+ {
+ // this can happen when follow calls pMaster->GetFormatted()
+ SAL_INFO("sw.core", "+SwTextFrame::JoinFrame: Follow is locked." );
+ return;
+ }
+ if (GetFollow()->IsDeleteForbidden())
+ return;
+ JoinFrame();
+ }
+
+ return;
+ }
+
+ // Dancing on the volcano: We'll just format the last line quickly
+ // for the QuoVadis stuff.
+ // The Offset can move of course:
+ const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || HasFollow()))
+ ? rLine.FormatQuoVadis(nOffset) : nOffset;
+
+ if( !(nMode & 1) )
+ {
+ // We steal text mass from our Follows
+ // It can happen that we have to join some of them
+ while( GetFollow() && GetFollow()->GetFollow() &&
+ nNewOfst >= GetFollow()->GetFollow()->GetOffset() )
+ {
+ JoinFrame();
+ }
+ }
+
+ // The Offset moved
+ if( GetFollow() )
+ {
+ if ( nMode )
+ GetFollow()->ManipOfst(TextFrameIndex(0));
+
+ if ( CalcFollow( nNewOfst ) ) // CalcFollow only at the end, we do a SetOffset there
+ rLine.SetOnceMore( true );
+ }
+}
+
+SwContentFrame *SwTextFrame::JoinFrame()
+{
+ OSL_ENSURE( GetFollow(), "+SwTextFrame::JoinFrame: no follow" );
+ SwTextFrame *pFoll = GetFollow();
+
+ SwTextFrame *pNxt = pFoll->GetFollow();
+
+ // All footnotes of the to-be-destroyed Follow are relocated to us
+ TextFrameIndex nStart = pFoll->GetOffset();
+ if ( pFoll->HasFootnote() )
+ {
+ SwFootnoteBossFrame *pFootnoteBoss = nullptr;
+ SwFootnoteBossFrame *pEndBoss = nullptr;
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIter iter(*pFoll);
+ for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
+ {
+ if (RES_TXTATR_FTN == pHt->Which()
+ && nStart <= pFoll->MapModelToView(pNode, pHt->GetStart()))
+ {
+ if (pHt->GetFootnote().IsEndNote())
+ {
+ if (!pEndBoss)
+ pEndBoss = pFoll->FindFootnoteBossFrame();
+ SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
+ }
+ else
+ {
+ if (!pFootnoteBoss)
+ pFootnoteBoss = pFoll->FindFootnoteBossFrame( true );
+ SwFootnoteBossFrame::ChangeFootnoteRef( pFoll, static_cast<const SwTextFootnote*>(pHt), this );
+ }
+ SetFootnote( true );
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ else if ( pFoll->isFramePrintAreaValid() ||
+ pFoll->isFrameAreaSizeValid() )
+ {
+ pFoll->CalcFootnoteFlag();
+ OSL_ENSURE( !pFoll->HasFootnote(), "Missing FootnoteFlag." );
+ }
+#endif
+
+ pFoll->MoveFlyInCnt( this, nStart, TextFrameIndex(COMPLETE_STRING) );
+ pFoll->SetFootnote( false );
+ // i#27138
+ // Notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph, which
+ // is <this>, will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pFoll->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pFoll->FindNextCnt( true );
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ this );
+ }
+ }
+#endif
+
+ pFoll->Cut();
+ SetFollow(pNxt);
+ SwFrame::DestroyFrame(pFoll);
+ return pNxt;
+}
+
+void SwTextFrame::SplitFrame(TextFrameIndex const nTextPos)
+{
+ SwSwapIfSwapped swap( this );
+
+ // The Paste sends a Modify() to me
+ // I lock myself, so that my data does not disappear
+ TextFrameLockGuard aLock( this );
+ SwTextFrame *const pNew = static_cast<SwTextFrame *>(GetTextNodeFirst()->MakeFrame(this));
+
+ pNew->SetFollow( GetFollow() );
+ SetFollow( pNew );
+
+ pNew->Paste( GetUpper(), GetNext() );
+ // i#27138
+ // notify accessibility paragraphs objects about changed CONTENT_FLOWS_FROM/_TO relation.
+ // Relation CONTENT_FLOWS_FROM for current next paragraph will change
+ // and relation CONTENT_FLOWS_TO for current previous paragraph, which
+ // is <this>, will change.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ {
+ SwViewShell* pViewShell( pNew->getRootFrame()->GetCurrShell() );
+ if ( pViewShell && pViewShell->GetLayout() &&
+ pViewShell->GetLayout()->IsAnyShellAccessible() )
+ {
+ auto pNext = pNew->FindNextCnt( true );
+ pViewShell->InvalidateAccessibleParaFlowRelation(
+ pNext ? pNext->DynCastTextFrame() : nullptr,
+ this );
+ }
+ }
+#endif
+
+ // If footnotes end up in pNew bz our actions, we need
+ // to re-register them
+ if ( HasFootnote() )
+ {
+ SwFootnoteBossFrame *pFootnoteBoss = nullptr;
+ SwFootnoteBossFrame *pEndBoss = nullptr;
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIter iter(*this);
+ for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
+ {
+ if (RES_TXTATR_FTN == pHt->Which()
+ && nTextPos <= MapModelToView(pNode, pHt->GetStart()))
+ {
+ if (pHt->GetFootnote().IsEndNote())
+ {
+ if (!pEndBoss)
+ pEndBoss = FindFootnoteBossFrame();
+ SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
+ }
+ else
+ {
+ if (!pFootnoteBoss)
+ pFootnoteBoss = FindFootnoteBossFrame( true );
+ SwFootnoteBossFrame::ChangeFootnoteRef( this, static_cast<const SwTextFootnote*>(pHt), pNew );
+ }
+ pNew->SetFootnote( true );
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ else
+ {
+ CalcFootnoteFlag( nTextPos - TextFrameIndex(1) );
+ OSL_ENSURE( !HasFootnote(), "Missing FootnoteFlag." );
+ }
+#endif
+
+ MoveFlyInCnt( pNew, nTextPos, TextFrameIndex(COMPLETE_STRING) );
+
+ // No SetOffset or CalcFollow, because an AdjustFollow follows immediately anyways
+
+ pNew->ManipOfst( nTextPos );
+}
+
+void SwTextFrame::SetOffset_(TextFrameIndex const nNewOfst)
+{
+ // We do not need to invalidate our Follow.
+ // We are a Follow, get formatted right away and call
+ // SetOffset() from there
+ mnOffset = nNewOfst;
+ SwParaPortion *pPara = GetPara();
+ if( pPara )
+ {
+ SwCharRange &rReformat = pPara->GetReformat();
+ rReformat.Start() = TextFrameIndex(0);
+ rReformat.Len() = TextFrameIndex(GetText().getLength());
+ pPara->SetDelta(sal_Int32(rReformat.Len()));
+ }
+ InvalidateSize();
+}
+
+bool SwTextFrame::CalcPreps()
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(), "SwTextFrame::CalcPreps with swapped frame" );
+ SwRectFnSet aRectFnSet(this);
+
+ SwParaPortion *pPara = GetPara();
+ if ( !pPara )
+ return false;
+ const bool bPrep = pPara->IsPrep();
+ const bool bPrepWidows = pPara->IsPrepWidows();
+ const bool bPrepAdjust = pPara->IsPrepAdjust();
+ const bool bPrepMustFit = pPara->IsPrepMustFit();
+ ResetPreps();
+
+ bool bRet = false;
+ if( bPrep && !pPara->GetReformat().Len() )
+ {
+ // PrepareHint::Widows means that the orphans rule got activated in the Follow.
+ // In unfortunate cases we could also have a PrepAdjust!
+ if( bPrepWidows )
+ {
+ if( !GetFollow() )
+ {
+ OSL_ENSURE( GetFollow(), "+SwTextFrame::CalcPreps: no credits" );
+ return false;
+ }
+
+ // We need to prepare for two cases:
+ // We were able to hand over a few lines to the Follow
+ // -> we need to shrink
+ // or we need to go on the next page
+ // -> we let our Frame become too big
+
+ SwTwips nChgHeight = GetParHeight();
+ if( nChgHeight >= aRectFnSet.GetHeight(getFramePrintArea()) )
+ {
+ if( bPrepMustFit )
+ {
+ GetFollow()->SetJustWidow( true );
+ GetFollow()->Prepare();
+ }
+ else if ( aRectFnSet.IsVert() )
+ {
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( aFrm.Width() + aFrm.Left() );
+ aFrm.Left( 0 );
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( aPrt.Width() + getFrameArea().Left() );
+ }
+
+ SetWidow( true );
+ }
+ else
+ {
+ // nTmp should be very large, but not so large as to cause overflow later (e.g.,
+ // GetFrameOfModify in sw/source/core/layout/frmtool.cxx calculates nCurrentDist
+ // from, among others, the square of aDiff.getY(), which can be close to nTmp);
+ // the previously used value TWIPS_MAX/2 (i.e., (LONG_MAX - 1)/2) depended on
+ // the range of 'long', while the value (SAL_MAX_INT32 - 1)/2 (which matches the
+ // old value on platforms where 'long' is 'sal_Int32') is empirically shown to
+ // be large enough in practice even on platforms where 'long' is 'sal_Int64':
+ SwTwips const nTmp = sw::WIDOW_MAGIC - (getFrameArea().Top()+10000);
+ SwTwips nDiff = nTmp - getFrameArea().Height();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height( nTmp );
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( aPrt.Height() + nDiff );
+ }
+
+ SetWidow( true );
+ }
+ }
+ else
+ {
+ OSL_ENSURE( nChgHeight < aRectFnSet.GetHeight(getFramePrintArea()),
+ "+SwTextFrame::CalcPrep: want to shrink" );
+
+ nChgHeight = aRectFnSet.GetHeight(getFramePrintArea()) - nChgHeight;
+
+ GetFollow()->SetJustWidow( true );
+ GetFollow()->Prepare();
+ Shrink( nChgHeight );
+ SwRect &rRepaint = pPara->GetRepaint();
+
+ if ( aRectFnSet.IsVert() )
+ {
+ SwRect aRepaint( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
+ SwitchVerticalToHorizontal( aRepaint );
+ rRepaint.Chg( aRepaint.Pos(), aRepaint.SSize() );
+ }
+ else
+ rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
+
+ if( 0 >= rRepaint.Width() )
+ rRepaint.Width(1);
+ }
+ bRet = true;
+ }
+ else if ( bPrepAdjust )
+ {
+ if ( HasFootnote() )
+ {
+ if( !CalcPrepFootnoteAdjust() )
+ {
+ if( bPrepMustFit )
+ {
+ SwTextLineAccess aAccess( this );
+ aAccess.GetPara()->SetPrepMustFit(true);
+ }
+ return false;
+ }
+ }
+
+ {
+ SwSwapIfNotSwapped swap( this );
+
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
+ SwTextFormatter aLine( this, &aInf );
+
+ WidowsAndOrphans aFrameBreak( this );
+ // Whatever the attributes say: we split the paragraph in
+ // MustFit case if necessary
+ if( bPrepMustFit )
+ {
+ aFrameBreak.SetKeep( false );
+ aFrameBreak.ClrOrphLines();
+ }
+ // Before calling FormatAdjust, we need to make sure
+ // that the lines protruding at the bottom get indeed
+ // truncated
+ bool bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
+ bRet = true;
+ while( !bBreak && aLine.Next() )
+ {
+ bBreak = aFrameBreak.IsBreakNowWidAndOrp( aLine );
+ }
+ if( bBreak )
+ {
+ // We run into troubles: when TruncLines is called, the
+ // conditions in IsInside change immediately such that
+ // IsBreakNow can return different results.
+ // For this reason, we tell rFrameBreak that the
+ // end is reached at the location of rLine.
+ // Let's see if it works ...
+ aLine.TruncLines();
+ aFrameBreak.SetRstHeight( aLine );
+ FormatAdjust( aLine, aFrameBreak, TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
+ }
+ else
+ {
+ if( !GetFollow() )
+ {
+ FormatAdjust( aLine, aFrameBreak,
+ TextFrameIndex(aInf.GetText().getLength()), aInf.IsStop() );
+ }
+ else if ( !aFrameBreak.IsKeepAlways() )
+ {
+ // We delete a line before the Master, because the Follow
+ // could hand over a line
+ const SwCharRange aFollowRg(GetFollow()->GetOffset(), TextFrameIndex(1));
+ pPara->GetReformat() += aFollowRg;
+ // We should continue!
+ bRet = false;
+ }
+ }
+ }
+
+ // A final check, if FormatAdjust() didn't help we need to
+ // truncate
+ if( bPrepMustFit )
+ {
+ const SwTwips nMust = aRectFnSet.GetPrtBottom(*GetUpper());
+ const SwTwips nIs = aRectFnSet.GetBottom(getFrameArea());
+
+ if( aRectFnSet.IsVert() && nIs < nMust )
+ {
+ Shrink( nMust - nIs );
+
+ if( getFramePrintArea().Width() < 0 )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( 0 );
+ }
+
+ SetUndersized( true );
+ }
+ else if ( ! aRectFnSet.IsVert() && nIs > nMust )
+ {
+ Shrink( nIs - nMust );
+
+ if( getFramePrintArea().Height() < 0 )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height( 0 );
+ }
+
+ SetUndersized( true );
+ }
+ }
+ }
+ }
+ pPara->SetPrepMustFit( bPrepMustFit );
+ return bRet;
+}
+
+// Move the as-character objects - footnotes must be moved by RemoveFootnote!
+void SwTextFrame::ChangeOffset( SwTextFrame* pFrame, TextFrameIndex nNew )
+{
+ if( pFrame->GetOffset() < nNew )
+ pFrame->MoveFlyInCnt( this, TextFrameIndex(0), nNew );
+ else if( pFrame->GetOffset() > nNew )
+ MoveFlyInCnt( pFrame, nNew, TextFrameIndex(COMPLETE_STRING) );
+}
+
+void SwTextFrame::FormatAdjust( SwTextFormatter &rLine,
+ WidowsAndOrphans &rFrameBreak,
+ TextFrameIndex const nStrLen,
+ const bool bDummy )
+{
+ SwSwapIfNotSwapped swap( this );
+
+ SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
+
+ TextFrameIndex nEnd = rLine.GetStart();
+
+ const bool bHasToFit = pPara->IsPrepMustFit();
+
+ // The StopFlag is set by footnotes which want to go onto the next page
+ // Call base class method <SwTextFrameBreak::IsBreakNow(..)>
+ // instead of method <WidowsAndOrphans::IsBreakNow(..)> to get a break,
+ // even if due to widow rule no enough lines exists.
+ sal_uInt8 nNew = ( !GetFollow() &&
+ nEnd < nStrLen &&
+ ( rLine.IsStop() ||
+ ( bHasToFit
+ ? ( rLine.GetLineNr() > 1 &&
+ !rFrameBreak.IsInside( rLine ) )
+ : rFrameBreak.IsBreakNow( rLine ) ) ) )
+ ? 1 : 0;
+ // i#84870
+ // no split of text frame, which only contains an as-character anchored object
+ bool bOnlyContainsAsCharAnchoredObj =
+ !IsFollow() && nStrLen == TextFrameIndex(1) &&
+ GetDrawObjs() && GetDrawObjs()->size() == 1 &&
+ (*GetDrawObjs())[0]->GetFrameFormat().GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR;
+
+ // Still try split text frame if we have columns.
+ if (FindColFrame())
+ bOnlyContainsAsCharAnchoredObj = false;
+
+ if ( nNew && bOnlyContainsAsCharAnchoredObj )
+ {
+ nNew = 0;
+ }
+
+ if ( nNew )
+ {
+ SplitFrame( nEnd );
+ }
+
+ const SwFrame *pBodyFrame = FindBodyFrame();
+
+ const tools::Long nBodyHeight = pBodyFrame ? ( IsVertical() ?
+ pBodyFrame->getFrameArea().Width() :
+ pBodyFrame->getFrameArea().Height() ) : 0;
+
+ // If the current values have been calculated, show that they
+ // are valid now
+ pPara->GetReformat() = SwCharRange();
+ bool bDelta = pPara->GetDelta() != 0;
+ pPara->SetDelta(0);
+
+ if( rLine.IsStop() )
+ {
+ rLine.TruncLines( true );
+ nNew = 1;
+ }
+
+ // FindBreak truncates the last line
+ if( !rFrameBreak.FindBreak( this, rLine, bHasToFit ) )
+ {
+ // If we're done formatting, we set nEnd to the end.
+ // AdjustFollow might execute JoinFrame() because of this.
+ // Else, nEnd is the end of the last line in the Master.
+ TextFrameIndex nOld = nEnd;
+ nEnd = rLine.GetEnd();
+ if( GetFollow() )
+ {
+ if( nNew && nOld < nEnd )
+ RemoveFootnote( nOld, nEnd - nOld );
+ ChangeOffset( GetFollow(), nEnd );
+ if( !bDelta )
+ GetFollow()->ManipOfst( nEnd );
+ }
+ }
+ else
+ { // If we pass over lines, we must not call Join in Follows, instead we even
+ // need to create a Follow.
+ // We also need to do this if the whole mass of text remains in the Master,
+ // because a hard line break could necessitate another line (without text mass)!
+ TextFrameIndex const nOld(nEnd);
+ nEnd = rLine.GetEnd();
+ if( GetFollow() )
+ {
+ // Another case for not joining the follow:
+ // Text frame has no content, but a numbering. Then, do *not* join.
+ // Example of this case: When an empty, but numbered paragraph
+ // at the end of page is completely displaced by a fly frame.
+ // Thus, the text frame introduced a follow by a
+ // <SwTextFrame::SplitFrame(..)> - see below. The follow then shows
+ // the numbering and must stay.
+ if ( GetFollow()->GetOffset() != nEnd ||
+ GetFollow()->IsFieldFollow() ||
+ (nStrLen == TextFrameIndex(0) && GetTextNodeForParaProps()->GetNumRule()))
+ {
+ nNew |= 3;
+ }
+ else if (FindTabFrame() && nEnd > TextFrameIndex(0) &&
+ rLine.GetInfo().GetChar(nEnd - TextFrameIndex(1)) == CH_BREAK)
+ {
+ // We are in a table, the paragraph has a follow and the text
+ // ends with a hard line break. Don't join the follow just
+ // because the follow would have no content, we may still need it
+ // for the paragraph mark.
+ nNew |= 1;
+ }
+ // move footnotes if the follow is kept - if RemoveFootnote() is
+ // called in next format iteration, it will be with the *new*
+ // offset so no effect!
+ if (nNew && nOld < nEnd)
+ {
+ RemoveFootnote(nOld, nEnd - nOld);
+ }
+ ChangeOffset( GetFollow(), nEnd );
+ GetFollow()->ManipOfst( nEnd );
+ }
+ else
+ {
+ const SwTextNode* pTextNode = GetTextNodeForParaProps();
+ bool bHasVisibleNumRule = nStrLen == TextFrameIndex(0) && pTextNode->GetNumRule();
+
+ if (!pTextNode->HasVisibleNumberingOrBullet())
+ {
+ bHasVisibleNumRule = false;
+ }
+
+ // Only split frame, if the frame contains
+ // content or contains no content, but has a numbering.
+ // i#84870 - No split, if text frame only contains one
+ // as-character anchored object.
+ if ( !bOnlyContainsAsCharAnchoredObj &&
+ (nStrLen > TextFrameIndex(0) ||
+ bHasVisibleNumRule )
+ )
+ {
+ SplitFrame( nEnd );
+ nNew |= 3;
+ }
+ }
+ // If the remaining height changed e.g by RemoveFootnote() we need to
+ // fill up in order to avoid oscillation.
+ if( bDummy && pBodyFrame &&
+ nBodyHeight < ( IsVertical() ?
+ pBodyFrame->getFrameArea().Width() :
+ pBodyFrame->getFrameArea().Height() ) )
+ rLine.MakeDummyLine();
+ }
+
+ // In AdjustFrame() we set ourselves via Grow/Shrink
+ // In AdjustFollow() we set our FollowFrame
+
+ const SwTwips nDocPrtTop = getFrameArea().Top() + getFramePrintArea().Top();
+ const SwTwips nOldHeight = getFramePrintArea().SSize().Height();
+ SwTwips nChg = rLine.CalcBottomLine() - nDocPrtTop - nOldHeight;
+
+ //#i84870# - no shrink of text frame, if it only contains one as-character anchored object.
+ if ( nChg < 0 && !bDelta && bOnlyContainsAsCharAnchoredObj )
+ {
+ nChg = 0;
+ }
+
+ // Vertical Formatting:
+ // The (rotated) repaint rectangle's x coordinate refers to the frame.
+ // If the frame grows (or shirks) the repaint rectangle cannot simply
+ // be rotated back after formatting, because we use the upper left point
+ // of the frame for rotation. This point changes when growing/shrinking.
+
+ if ( IsVertical() && !IsVertLR() && nChg )
+ {
+ SwRect &rRepaint = pPara->GetRepaint();
+ rRepaint.Left( rRepaint.Left() - nChg );
+ rRepaint.Width( rRepaint.Width() - nChg );
+ }
+
+ AdjustFrame( nChg, bHasToFit );
+
+ if( HasFollow() || IsInFootnote() )
+ AdjustFollow_( rLine, nEnd, nStrLen, nNew );
+
+ pPara->SetPrepMustFit( false );
+}
+
+// bPrev is set whether Reformat.Start() was called because of Prev().
+// Else, wo don't know whether we can limit the repaint or not.
+bool SwTextFrame::FormatLine( SwTextFormatter &rLine, const bool bPrev )
+{
+ OSL_ENSURE( ! IsVertical() || IsSwapped(),
+ "SwTextFrame::FormatLine( rLine, bPrev) with unswapped frame" );
+ SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
+ const SwLineLayout *pOldCur = rLine.GetCurr();
+ const TextFrameIndex nOldLen = pOldCur->GetLen();
+ const SwTwips nOldAscent = pOldCur->GetAscent();
+ const SwTwips nOldHeight = pOldCur->Height();
+ const SwTwips nOldWidth = pOldCur->Width() + pOldCur->GetHangingMargin();
+ const bool bOldHyph = pOldCur->IsEndHyph();
+ SwTwips nOldTop = 0;
+ SwTwips nOldBottom = 0;
+ if( rLine.GetCurr()->IsClipping() )
+ rLine.CalcUnclipped( nOldTop, nOldBottom );
+
+ TextFrameIndex const nNewStart = rLine.FormatLine( rLine.GetStart() );
+
+ OSL_ENSURE( getFrameArea().Pos().Y() + getFramePrintArea().Pos().Y() == rLine.GetFirstPos(),
+ "SwTextFrame::FormatLine: frame leaves orbit." );
+ OSL_ENSURE( rLine.GetCurr()->Height(),
+ "SwTextFrame::FormatLine: line height is zero" );
+
+ // The current line break object
+ const SwLineLayout *pNew = rLine.GetCurr();
+
+ bool bUnChg = nOldLen == pNew->GetLen() &&
+ bOldHyph == pNew->IsEndHyph();
+ if ( bUnChg && !bPrev )
+ {
+ const tools::Long nWidthDiff = nOldWidth > pNew->Width()
+ ? nOldWidth - pNew->Width()
+ : pNew->Width() - nOldWidth;
+
+ // we only declare a line as unchanged, if its main values have not
+ // changed and it is not the last line (!paragraph end symbol!)
+ bUnChg = nOldHeight == pNew->Height() &&
+ nOldAscent == pNew->GetAscent() &&
+ nWidthDiff <= SLOPPY_TWIPS &&
+ pOldCur->GetNext();
+ }
+
+ // Calculate rRepaint
+ const SwTwips nBottom = rLine.Y() + rLine.GetLineHeight();
+ SwRepaint &rRepaint = pPara->GetRepaint();
+ if( bUnChg && rRepaint.Top() == rLine.Y()
+ && (bPrev || nNewStart <= pPara->GetReformat().Start())
+ && (nNewStart < TextFrameIndex(GetText().getLength())))
+ {
+ rRepaint.Top( nBottom );
+ rRepaint.Height( 0 );
+ }
+ else
+ {
+ if( nOldTop )
+ {
+ if( nOldTop < rRepaint.Top() )
+ rRepaint.Top( nOldTop );
+ if( !rLine.IsUnclipped() || nOldBottom > rRepaint.Bottom() )
+ {
+ rRepaint.Bottom( nOldBottom - 1 );
+ rLine.SetUnclipped( true );
+ }
+ }
+ if( rLine.GetCurr()->IsClipping() && rLine.IsFlyInCntBase() )
+ {
+ SwTwips nTmpTop, nTmpBottom;
+ rLine.CalcUnclipped( nTmpTop, nTmpBottom );
+ if( nTmpTop < rRepaint.Top() )
+ rRepaint.Top( nTmpTop );
+ if( !rLine.IsUnclipped() || nTmpBottom > rRepaint.Bottom() )
+ {
+ rRepaint.Bottom( nTmpBottom - 1 );
+ rLine.SetUnclipped( true );
+ }
+ }
+ else
+ {
+ if( !rLine.IsUnclipped() || nBottom > rRepaint.Bottom() )
+ {
+ rRepaint.Bottom( nBottom - 1 );
+ rLine.SetUnclipped( false );
+ }
+ }
+ SwTwips nRght = std::max( nOldWidth, pNew->Width() +
+ pNew->GetHangingMargin() );
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ const SwViewOption *pOpt = pSh ? pSh->GetViewOptions() : nullptr;
+ if( pOpt && (pOpt->IsParagraph() || pOpt->IsLineBreak()) )
+ nRght += ( std::max( nOldAscent, pNew->GetAscent() ) );
+ else
+ nRght += ( std::max( nOldAscent, pNew->GetAscent() ) / 4);
+ nRght += rLine.GetLeftMargin();
+ if( rRepaint.GetOffset() || rRepaint.GetRightOfst() < nRght )
+ rRepaint.SetRightOfst( nRght );
+
+ // Finally we enlarge the repaint rectangle if we found an underscore
+ // within our line. 40 Twips should be enough
+ const bool bHasUnderscore =
+ ( rLine.GetInfo().GetUnderScorePos() < nNewStart );
+ if ( bHasUnderscore || rLine.GetCurr()->HasUnderscore() )
+ rRepaint.Bottom( rRepaint.Bottom() + 40 );
+
+ const_cast<SwLineLayout*>(rLine.GetCurr())->SetUnderscore( bHasUnderscore );
+ }
+
+ // Calculating the good ol' nDelta
+ const sal_Int32 nDiff = sal_Int32(pNew->GetLen()) - sal_Int32(nOldLen);
+ pPara->SetDelta(pPara->GetDelta() - nDiff);
+
+ // Stop!
+ if( rLine.IsStop() )
+ return false;
+
+ // Absolutely another line
+ if( rLine.IsNewLine() )
+ return true;
+
+ // Until the String's end?
+ if (nNewStart >= TextFrameIndex(GetText().getLength()))
+ return false;
+
+ if( rLine.GetInfo().IsShift() )
+ return true;
+
+ // Reached the Reformat's end?
+ const TextFrameIndex nEnd = pPara->GetReformat().Start() +
+ pPara->GetReformat().Len();
+
+ if( nNewStart <= nEnd )
+ return true;
+
+ return 0 != pPara->GetDelta();
+}
+
+void SwTextFrame::Format_( SwTextFormatter &rLine, SwTextFormatInfo &rInf,
+ const bool bAdjust )
+{
+ OSL_ENSURE( ! IsVertical() || IsSwapped(),"SwTextFrame::Format_ with unswapped frame" );
+
+ SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
+ rLine.SetUnclipped( false );
+
+ const OUString & rString = GetText();
+ const TextFrameIndex nStrLen(rString.getLength());
+
+ SwCharRange &rReformat = pPara->GetReformat();
+ SwRepaint &rRepaint = pPara->GetRepaint();
+ std::unique_ptr<SwRepaint> pFreeze;
+
+ // Due to performance reasons we set rReformat to COMPLETE_STRING in Init()
+ // In this case we adjust rReformat
+ if( rReformat.Len() > nStrLen )
+ rReformat.Len() = nStrLen;
+
+ if( rReformat.Start() + rReformat.Len() > nStrLen )
+ rReformat.Len() = nStrLen - rReformat.Start();
+
+ SwTwips nOldBottom;
+ if( GetOffset() && !IsFollow() )
+ {
+ rLine.Bottom();
+ nOldBottom = rLine.Y();
+ rLine.Top();
+ }
+ else
+ nOldBottom = 0;
+ rLine.CharToLine( rReformat.Start() );
+
+ // When inserting or removing a Space, words can be moved out of the edited
+ // line and into the preceding line, hence the preceding line must be
+ // formatted as well.
+ // Optimization: If rReformat starts after the first word of the line,
+ // this line cannot possibly influence the previous one.
+ // ...Turns out that unfortunately it can: Text size changes + FlyFrames;
+ // the feedback can affect multiple lines (Frames!)!
+
+ // i#46560
+ // FME: Yes, consider this case: "(word )" has to go to the next line
+ // because ")" is a forbidden character at the beginning of a line although
+ // "(word" would still fit on the previous line. Adding text right in front
+ // of ")" would not trigger a reformatting of the previous line. Adding 1
+ // to the result of FindBrk() does not solve the problem in all cases,
+ // nevertheless it should be sufficient.
+ bool bPrev = rLine.GetPrev() &&
+ (FindBrk(rString, rLine.GetStart(), rReformat.Start() + TextFrameIndex(1))
+ // i#46560
+ + TextFrameIndex(1)
+ >= rReformat.Start() ||
+ rLine.GetCurr()->IsRest() );
+ if( bPrev )
+ {
+ while( rLine.Prev() )
+ if( rLine.GetCurr()->GetLen() && !rLine.GetCurr()->IsRest() )
+ {
+ if( !rLine.GetStart() )
+ rLine.Top(); // So that NumDone doesn't get confused
+ break;
+ }
+ TextFrameIndex nNew = rLine.GetStart() + rLine.GetLength();
+ if( nNew )
+ {
+ --nNew;
+ if (CH_BREAK == rString[sal_Int32(nNew)])
+ {
+ ++nNew;
+ rLine.Next();
+ bPrev = false;
+ }
+ }
+ rReformat.Len() += rReformat.Start() - nNew;
+ rReformat.Start() = nNew;
+ }
+
+ rRepaint.SetOffset( 0 );
+ rRepaint.SetRightOfst( 0 );
+ rRepaint.Chg( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
+ if( pPara->IsMargin() )
+ rRepaint.Width( rRepaint.Width() + pPara->GetHangingMargin() );
+ rRepaint.Top( rLine.Y() );
+ if( 0 >= rRepaint.Width() )
+ rRepaint.Width(1);
+ WidowsAndOrphans aFrameBreak( this, rInf.IsTest() ? 1 : 0 );
+
+ // rLine is now set to the first line which needs formatting.
+ // The bFirst flag makes sure that Next() is not called.
+ // The whole thing looks weird, but we need to make sure that
+ // rLine stops at the last non-fitting line when calling IsBreakNow.
+ bool bFirst = true;
+ bool bFormat = true;
+
+ // The CharToLine() can also get us into the danger zone.
+ // In that case we need to walk back until rLine is set
+ // to the non-fitting line. Or else the mass of text is lost,
+ // because the Ofst was set wrongly in the Follow.
+
+ bool bBreak = ( !pPara->IsPrepMustFit() || rLine.GetLineNr() > 1 )
+ && aFrameBreak.IsBreakNowWidAndOrp( rLine );
+ if( bBreak )
+ {
+ bool bPrevDone = nullptr != rLine.Prev();
+ while( bPrevDone && aFrameBreak.IsBreakNowWidAndOrp(rLine) )
+ bPrevDone = nullptr != rLine.Prev();
+ if( bPrevDone )
+ {
+ aFrameBreak.SetKeep( false );
+ rLine.Next();
+ }
+ rLine.TruncLines();
+
+ // Play it safe
+ aFrameBreak.IsBreakNowWidAndOrp(rLine);
+ }
+
+ /* Meaning if the following flags are set:
+
+ Watch(End/Mid)Hyph: we need to format if we have a break at
+ the line end/Fly, as long as MaxHyph is reached
+
+ Jump(End/Mid)Flag: the next line which has no break (line end/Fly),
+ needs to be formatted, because we could wrap now. This might have been
+ forbidden earlier by MaxHyph
+
+ Watch(End/Mid)Hyph: if the last formatted line got a cutoff point, but
+ didn't have one before
+
+ Jump(End/Mid)Hyph: if a cutoff point disappears
+ */
+ bool bJumpEndHyph = false;
+ bool bWatchEndHyph = false;
+ bool bJumpMidHyph = false;
+ bool bWatchMidHyph = false;
+
+ const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
+ rInf.MaxHyph() = rAttrSet.GetHyphenZone().GetMaxHyphens();
+ bool bMaxHyph = 0 != rInf.MaxHyph();
+ if ( bMaxHyph )
+ rLine.InitCntHyph();
+
+ if( IsFollow() && IsFieldFollow() && rLine.GetStart() == GetOffset() )
+ {
+ SwTextFrame *pMaster = FindMaster();
+ OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
+ const SwLineLayout* pLine=nullptr;
+ if (pMaster)
+ {
+ if (!pMaster->HasPara())
+ { // master could be locked because it's being formatted upstack
+ SAL_WARN("sw", "SwTextFrame::Format_: master not formatted!");
+ }
+ else
+ {
+ SwTextSizeInfo aInf( pMaster );
+ SwTextIter aMasterLine( pMaster, &aInf );
+ aMasterLine.Bottom();
+ pLine = aMasterLine.GetCurr();
+ assert(aMasterLine.GetEnd() == GetOffset());
+ }
+ }
+ SwLinePortion* pRest = pLine ?
+ rLine.MakeRestPortion(pLine, GetOffset()) : nullptr;
+ if( pRest )
+ rInf.SetRest( pRest );
+ else
+ SetFieldFollow( false );
+ }
+
+ /* Ad cancel criterion:
+ * In order to recognize, whether a line does not fit onto the page
+ * anymore, we need to format it. This overflow is removed again in
+ * e.g. AdjustFollow.
+ * Another complication: if we are the Master, we need to traverse
+ * the lines, because it could happen that one line can overflow
+ * from the Follow to the Master.
+ */
+ do
+ {
+ if( bFirst )
+ bFirst = false;
+ else
+ {
+ if ( bMaxHyph )
+ {
+ if ( rLine.GetCurr()->IsEndHyph() )
+ rLine.CntEndHyph()++;
+ else
+ rLine.CntEndHyph() = 0;
+ if ( rLine.GetCurr()->IsMidHyph() )
+ rLine.CntMidHyph()++;
+ else
+ rLine.CntMidHyph() = 0;
+ }
+ if( !rLine.Next() )
+ {
+ if( !bFormat )
+ {
+ SwLinePortion* pRest =
+ rLine.MakeRestPortion( rLine.GetCurr(), rLine.GetEnd() );
+ if( pRest )
+ rInf.SetRest( pRest );
+ }
+ rLine.Insert( new SwLineLayout() );
+ rLine.Next();
+ bFormat = true;
+ }
+ }
+ if ( !bFormat && bMaxHyph &&
+ (bWatchEndHyph || bJumpEndHyph || bWatchMidHyph || bJumpMidHyph) )
+ {
+ if ( rLine.GetCurr()->IsEndHyph() )
+ {
+ if ( bWatchEndHyph )
+ bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
+ }
+ else
+ {
+ bFormat = bJumpEndHyph;
+ bWatchEndHyph = false;
+ bJumpEndHyph = false;
+ }
+ if ( rLine.GetCurr()->IsMidHyph() )
+ {
+ if ( bWatchMidHyph && !bFormat )
+ bFormat = ( rLine.CntEndHyph() == rInf.MaxHyph() );
+ }
+ else
+ {
+ bFormat |= bJumpMidHyph;
+ bWatchMidHyph = false;
+ bJumpMidHyph = false;
+ }
+ }
+ if( bFormat )
+ {
+ const bool bOldEndHyph = rLine.GetCurr()->IsEndHyph();
+ const bool bOldMidHyph = rLine.GetCurr()->IsMidHyph();
+ bFormat = FormatLine( rLine, bPrev );
+ // There can only be one bPrev ... (???)
+ bPrev = false;
+ if ( bMaxHyph )
+ {
+ if ( rLine.GetCurr()->IsEndHyph() != bOldEndHyph )
+ {
+ bWatchEndHyph = !bOldEndHyph;
+ bJumpEndHyph = bOldEndHyph;
+ }
+ if ( rLine.GetCurr()->IsMidHyph() != bOldMidHyph )
+ {
+ bWatchMidHyph = !bOldMidHyph;
+ bJumpMidHyph = bOldMidHyph;
+ }
+ }
+ }
+
+ if( !rInf.IsNewLine() )
+ {
+ if( !bFormat )
+ bFormat = nullptr != rInf.GetRest();
+ if( rInf.IsStop() || rInf.GetIdx() >= nStrLen )
+ break;
+ if( !bFormat && ( !bMaxHyph || ( !bWatchEndHyph &&
+ !bJumpEndHyph && !bWatchMidHyph && !bJumpMidHyph ) ) )
+ {
+ if( GetFollow() )
+ {
+ while( rLine.Next() )
+ ; //Nothing
+ pFreeze.reset(new SwRepaint( rRepaint )); // to minimize painting
+ }
+ else
+ break;
+ }
+ }
+ bBreak = aFrameBreak.IsBreakNowWidAndOrp(rLine);
+ }while( !bBreak );
+
+ if( pFreeze )
+ {
+ rRepaint = *pFreeze;
+ pFreeze.reset();
+ }
+
+ if( !rLine.IsStop() )
+ {
+ // If we're finished formatting the text and we still
+ // have other line objects left, these are superfluous
+ // now because the text has gotten shorter.
+ bool bTruncLines = false;
+ if( rLine.GetStart() + rLine.GetLength() >= nStrLen &&
+ rLine.GetCurr()->GetNext() )
+ {
+ bTruncLines = true;
+ }
+ else if (GetMergedPara() && rLine.GetCurr()->GetNext())
+ {
+ // We can also have superfluous lines with redlining in case the current line is shorter
+ // than the text length, but the total length of lines is still more than expected.
+ // Truncate in this case as well.
+ TextFrameIndex nLen(0);
+ for (const SwLineLayout* pLine = pPara; pLine; pLine = pLine->GetNext())
+ {
+ nLen += pLine->GetLen();
+ }
+ bTruncLines = nLen > nStrLen;
+ }
+
+ if (bTruncLines)
+ {
+ rLine.TruncLines();
+ rLine.SetTruncLines( true );
+ }
+ }
+
+ if( rInf.IsTest() )
+ return;
+
+ // FormatAdjust does not pay off at OnceMore
+ if( bAdjust || !rLine.GetDropFormat() || !rLine.CalcOnceMore() )
+ {
+ FormatAdjust( rLine, aFrameBreak, nStrLen, rInf.IsStop() );
+ }
+ if( rRepaint.HasArea() )
+ SetRepaint();
+ rLine.SetTruncLines( false );
+ if( nOldBottom ) // We check whether paragraphs that need scrolling can
+ // be shrunk, so that they don't need scrolling anymore
+ {
+ rLine.Bottom();
+ SwTwips nNewBottom = rLine.Y();
+ if( nNewBottom < nOldBottom )
+ SetOffset_(TextFrameIndex(0));
+ }
+}
+
+void SwTextFrame::FormatOnceMore( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
+{
+ OSL_ENSURE( ! IsVertical() || IsSwapped(),
+ "A frame is not swapped in SwTextFrame::FormatOnceMore" );
+
+ SwParaPortion *pPara = rLine.GetInfo().GetParaPortion();
+ if( !pPara )
+ return;
+
+ // If necessary the pPara
+ sal_uInt16 nOld = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
+ bool bShrink = false;
+ bool bGrow = false;
+ bool bGoOn = rLine.IsOnceMore();
+ sal_uInt8 nGo = 0;
+ while( bGoOn )
+ {
+ ++nGo;
+ rInf.Init();
+ rLine.Top();
+ if( !rLine.GetDropFormat() )
+ rLine.SetOnceMore( false );
+ SwCharRange aRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
+ pPara->GetReformat() = aRange;
+ Format_( rLine, rInf );
+
+ bGoOn = rLine.IsOnceMore();
+ if( bGoOn )
+ {
+ const sal_uInt16 nNew = static_cast<const SwTextMargin&>(rLine).GetDropHeight();
+ if( nOld == nNew )
+ bGoOn = false;
+ else
+ {
+ if( nOld > nNew )
+ bShrink = true;
+ else
+ bGrow = true;
+
+ if( bShrink == bGrow || 5 < nGo )
+ bGoOn = false;
+
+ nOld = nNew;
+ }
+
+ // If something went wrong, we need to reformat again
+ if( !bGoOn )
+ {
+ rInf.CtorInitTextFormatInfo( getRootFrame()->GetCurrShell()->GetOut(), this );
+ rLine.CtorInitTextFormatter( this, &rInf );
+ rLine.SetDropLines( 1 );
+ rLine.CalcDropHeight( 1 );
+ SwCharRange aTmpRange(TextFrameIndex(0), TextFrameIndex(rInf.GetText().getLength()));
+ pPara->GetReformat() = aTmpRange;
+ Format_( rLine, rInf, true );
+ // We paint everything ...
+ SetCompletePaint();
+ }
+ }
+ }
+}
+
+void SwTextFrame::Format_( vcl::RenderContext* pRenderContext, SwParaPortion *pPara )
+{
+ const bool bIsEmpty = GetText().isEmpty();
+
+ if ( bIsEmpty )
+ {
+ // Empty lines do not get tortured for very long:
+ // pPara is cleared, which is the same as:
+ // *pPara = SwParaPortion;
+ const bool bMustFit = pPara->IsPrepMustFit();
+ pPara->Truncate();
+ pPara->FormatReset();
+
+ // delete pSpaceAdd and pKanaComp
+ pPara->FinishSpaceAdd();
+ pPara->FinishKanaComp();
+ pPara->ResetFlags();
+ pPara->SetPrepMustFit( bMustFit );
+ }
+
+ OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Format_" );
+
+ if ( IsVertical() )
+ SwapWidthAndHeight();
+
+ SwTextFormatInfo aInf( pRenderContext, this );
+ SwTextFormatter aLine( this, &aInf );
+
+ HideAndShowObjects();
+
+ Format_( aLine, aInf );
+
+ if( aLine.IsOnceMore() )
+ FormatOnceMore( aLine, aInf );
+
+ if ( IsVertical() )
+ SwapWidthAndHeight();
+
+ OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Format_" );
+
+ if( 1 >= aLine.GetDropLines() )
+ return;
+
+ if( SvxAdjust::Left != aLine.GetAdjust() &&
+ SvxAdjust::Block != aLine.GetAdjust() )
+ {
+ aLine.CalcDropAdjust();
+ aLine.SetPaintDrop( true );
+ }
+
+ if( aLine.IsPaintDrop() )
+ {
+ aLine.CalcDropRepaint();
+ aLine.SetPaintDrop( false );
+ }
+}
+
+// We calculate the text frame's size and send a notification.
+// Shrink() or Grow() to adjust the frame's size to the changed required space.
+void SwTextFrame::Format( vcl::RenderContext* pRenderContext, const SwBorderAttrs * )
+{
+ SwRectFnSet aRectFnSet(this);
+
+ CalcAdditionalFirstLineOffset();
+
+ // The range autopilot or the BASIC interface pass us TextFrames with
+ // a width <= 0 from time to time
+ if( aRectFnSet.GetWidth(getFramePrintArea()) <= 0 )
+ {
+ // If MustFit is set, we shrink to the Upper's bottom edge if needed.
+ // Else we just take a standard size of 12 Pt. (240 twip).
+ SwTextLineAccess aAccess( this );
+ tools::Long nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+
+ if( aAccess.GetPara()->IsPrepMustFit() )
+ {
+ const SwTwips nLimit = aRectFnSet.GetPrtBottom(*GetUpper());
+ const SwTwips nDiff = - aRectFnSet.BottomDist( getFrameArea(), nLimit );
+ if( nDiff > 0 )
+ Shrink( nDiff );
+ }
+ else if( 240 < nFrameHeight )
+ {
+ Shrink( nFrameHeight - 240 );
+ }
+ else if( 240 > nFrameHeight )
+ {
+ Grow( 240 - nFrameHeight );
+ }
+
+ nFrameHeight = aRectFnSet.GetHeight(getFrameArea());
+ const tools::Long nTop = aRectFnSet.GetTopMargin(*this);
+
+ if( nTop > nFrameHeight )
+ {
+ aRectFnSet.SetYMargins( *this, nFrameHeight, 0 );
+ }
+ else if( aRectFnSet.GetHeight(getFramePrintArea()) < 0 )
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aRectFnSet.SetHeight( aPrt, 0 );
+ }
+
+ return;
+ }
+
+ const TextFrameIndex nStrLen(GetText().getLength());
+ if ( nStrLen || !FormatEmpty() )
+ {
+
+ SetEmpty( false );
+ // In order to not get confused by nested Formats
+ FormatLevel aLevel;
+ if( 12 == FormatLevel::GetLevel() )
+ return;
+
+ // We could be possibly not allowed to alter the format information
+ if( IsLocked() )
+ return;
+
+ // Attention: Format() could be triggered by GetFormatted()
+ if( IsHiddenNow() )
+ {
+ tools::Long nPrtHeight = aRectFnSet.GetHeight(getFramePrintArea());
+ if( nPrtHeight )
+ {
+ HideHidden();
+ Shrink( nPrtHeight );
+ }
+ else
+ {
+ // Assure that objects anchored
+ // at paragraph resp. at/as character inside paragraph
+ // are hidden.
+ HideAndShowObjects();
+ }
+ ChgThisLines();
+ return;
+ }
+
+ // We do not want to be interrupted during formatting
+ TextFrameLockGuard aLock(this);
+
+ // this is to ensure that the similar code in SwTextFrame::Format_
+ // finds the master formatted in case it's needed
+ if (IsFollow() && IsFieldFollow())
+ {
+ SwTextFrame *pMaster = FindMaster();
+ assert(pMaster);
+ if (!pMaster->HasPara())
+ {
+ pMaster->GetFormatted();
+ }
+ if (!pMaster->HasPara())
+ { // master could be locked because it's being formatted upstack
+ SAL_WARN("sw", "SwTextFrame::Format: failed to format master!");
+ }
+ else
+ {
+ SwTextSizeInfo aInf( pMaster );
+ SwTextIter aMasterLine( pMaster, &aInf );
+ aMasterLine.Bottom();
+ SetOffset(aMasterLine.GetEnd());
+ }
+ }
+
+ SwTextLineAccess aAccess( this );
+ const bool bNew = !aAccess.IsAvailable();
+ const bool bSetOffset =
+ (GetOffset() && GetOffset() > TextFrameIndex(GetText().getLength()));
+
+ if( CalcPreps() )
+ ; // nothing
+ // We return if already formatted, but if the TextFrame was just created
+ // and does not have any format information
+ else if( !bNew && !aAccess.GetPara()->GetReformat().Len() )
+ {
+ if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
+ {
+ aAccess.GetPara()->SetPrepAdjust();
+ aAccess.GetPara()->SetPrep();
+ CalcPreps();
+ }
+ SetWidow( false );
+ }
+ else if( bSetOffset && IsFollow() )
+ {
+ SwTextFrame *pMaster = FindMaster();
+ OSL_ENSURE( pMaster, "SwTextFrame::Format: homeless follow" );
+ if( pMaster )
+ pMaster->Prepare( PrepareHint::FollowFollows );
+ SwTwips nMaxY = aRectFnSet.GetPrtBottom(*GetUpper());
+
+ if( aRectFnSet.OverStep( getFrameArea(), nMaxY ) )
+ {
+ aRectFnSet.SetLimit( *this, nMaxY );
+ }
+ else if( aRectFnSet.BottomDist( getFrameArea(), nMaxY ) < 0 )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aRectFnSet.AddBottom( aFrm, -aRectFnSet.GetHeight(aFrm) );
+ }
+ }
+ else
+ {
+ // bSetOffset here means that we have the "red arrow situation"
+ if ( bSetOffset )
+ SetOffset_(TextFrameIndex(0));
+
+ const bool bOrphan = IsWidow();
+ const SwFootnoteBossFrame* pFootnoteBoss = HasFootnote() ? FindFootnoteBossFrame() : nullptr;
+ SwTwips nFootnoteHeight = 0;
+ if( pFootnoteBoss )
+ {
+ const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
+ nFootnoteHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
+ }
+ do
+ {
+ Format_( pRenderContext, aAccess.GetPara() );
+ if( pFootnoteBoss && nFootnoteHeight )
+ {
+ const SwFootnoteContFrame* pCont = pFootnoteBoss->FindFootnoteCont();
+ SwTwips nNewHeight = pCont ? aRectFnSet.GetHeight(pCont->getFrameArea()) : 0;
+ // If we lost some footnotes, we may have more space
+ // for our main text, so we have to format again ...
+ if( nNewHeight < nFootnoteHeight )
+ nFootnoteHeight = nNewHeight;
+ else
+ break;
+ }
+ else
+ break;
+ } while ( pFootnoteBoss );
+ if( bOrphan )
+ {
+ ValidateFrame();
+ SetWidow( false );
+ }
+ }
+ if( IsEmptyMaster() )
+ {
+ SwFrame* pPre = GetPrev();
+ if( pPre &&
+ // i#10826 It's the first, it cannot keep!
+ pPre->GetIndPrev() &&
+ pPre->GetAttrSet()->GetKeep().GetValue() )
+ {
+ pPre->InvalidatePos();
+ }
+ }
+ }
+
+ ChgThisLines();
+
+ // the PrepMustFit should not survive a Format operation
+ SwParaPortion *pPara = GetPara();
+ if ( pPara )
+ pPara->SetPrepMustFit( false );
+
+ CalcBaseOfstForFly();
+ CalcHeightOfLastLine(); // i#11860 - Adjust spacing implementation for
+ // object positioning - Compatibility to MS Word
+ // tdf#117982 -- Fix cell spacing hides content
+ // Check if the cell's content has greater size than the row height
+ if (IsInTab() && GetUpper() && ((GetUpper()->getFramePrintArea().Height() < getFramePrintArea().Height())
+ || (getFramePrintArea().Height() <= 0)))
+ {
+ SAL_INFO("sw.core", "Warn: Cell content has greater size than cell height!");
+ //get font size...
+ SwTwips aTmpHeight = getFrameArea().Height();
+ //...and push it into the text frame
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ //if only bottom margin what we have:
+ if (GetTopMargin() == 0)
+ //set the frame to its original location
+ aPrt.SetTopAndHeight(0, aTmpHeight);
+ }
+}
+
+// bForceQuickFormat is set if GetFormatted() has been called during the
+// painting process. Actually I cannot imagine a situation which requires
+// a full formatting of the paragraph during painting, on the other hand
+// a full formatting can cause the invalidation of other layout frames,
+// e.g., if there are footnotes in this paragraph, and invalid layout
+// frames will not calculated during the painting. So I actually want to
+// avoid a formatting during painting, but since I'm a coward, I'll only
+// force the quick formatting in the situation of issue i29062.
+bool SwTextFrame::FormatQuick( bool bForceQuickFormat )
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
+ "SwTextFrame::FormatQuick with swapped frame" );
+
+ if( IsEmpty() && FormatEmpty() )
+ return true;
+
+ // We're very picky:
+ if( HasPara() || IsWidow() || IsLocked()
+ || !isFrameAreaSizeValid() ||
+ ( ( IsVertical() ? getFramePrintArea().Width() : getFramePrintArea().Height() ) && IsHiddenNow() ) )
+ return false;
+
+ SwTextLineAccess aAccess( this );
+ SwParaPortion *pPara = aAccess.GetPara();
+ if( !pPara )
+ return false;
+
+ SwFrameSwapper aSwapper( this, true );
+
+ TextFrameLockGuard aLock(this);
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true );
+ if( 0 != aInf.MaxHyph() ) // Respect MaxHyphen!
+ return false;
+
+ SwTextFormatter aLine( this, &aInf );
+
+ // DropCaps are too complicated ...
+ if( aLine.GetDropFormat() )
+ return false;
+
+ TextFrameIndex nStart = GetOffset();
+ const TextFrameIndex nEnd = GetFollow()
+ ? GetFollow()->GetOffset()
+ : TextFrameIndex(aInf.GetText().getLength());
+
+ int nLoopProtection = 0;
+ do
+ {
+ TextFrameIndex nNewStart = aLine.FormatLine(nStart);
+ if (nNewStart == nStart)
+ ++nLoopProtection;
+ else
+ nLoopProtection = 0;
+ nStart = nNewStart;
+ const bool bWillEndlessInsert = nLoopProtection > 250;
+ SAL_WARN_IF(bWillEndlessInsert, "sw", "loop detection triggered");
+ if ((!bWillEndlessInsert) // Check for special case: line is invisible,
+ // like in too thin table cell: tdf#66141
+ && (aInf.IsNewLine() || (!aInf.IsStop() && nStart < nEnd)))
+ aLine.Insert( new SwLineLayout() );
+ } while( aLine.Next() );
+
+ // Last exit: the heights need to match
+ Point aTopLeft( getFrameArea().Pos() );
+ aTopLeft += getFramePrintArea().Pos();
+ const SwTwips nNewHeight = aLine.Y() + aLine.GetLineHeight();
+ const SwTwips nOldHeight = aTopLeft.Y() + getFramePrintArea().Height();
+
+ if( !bForceQuickFormat && nNewHeight != nOldHeight && !IsUndersized() )
+ {
+ // Attention: This situation can occur due to FormatLevel==12. Don't panic!
+ TextFrameIndex const nStrt = GetOffset();
+ InvalidateRange_( SwCharRange( nStrt, nEnd - nStrt) );
+ return false;
+ }
+
+ if (m_pFollow && nStart != static_cast<SwTextFrame*>(m_pFollow)->GetOffset())
+ return false; // can be caused by e.g. Orphans
+
+ // We made it!
+
+ // Set repaint
+ pPara->GetRepaint().Pos( aTopLeft );
+ pPara->GetRepaint().SSize( getFramePrintArea().SSize() );
+
+ // Delete reformat
+ pPara->GetReformat() = SwCharRange();
+ pPara->SetDelta(0);
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/frminf.cxx b/sw/source/core/text/frminf.cxx
new file mode 100644
index 000000000..a123691db
--- /dev/null
+++ b/sw/source/core/text/frminf.cxx
@@ -0,0 +1,291 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <frminf.hxx>
+#include "itrtxt.hxx"
+
+TextFrameIndex SwTextMargin::GetTextStart() const
+{
+ const OUString &rText = GetInfo().GetText();
+ const TextFrameIndex nEnd = m_nStart + m_pCurr->GetLen();
+
+ for (TextFrameIndex i = m_nStart; i < nEnd; ++i)
+ {
+ const sal_Unicode aChar = rText[sal_Int32(i)];
+ if( CH_TAB != aChar && ' ' != aChar )
+ return i;
+ }
+ return nEnd;
+}
+
+TextFrameIndex SwTextMargin::GetTextEnd() const
+{
+ const OUString &rText = GetInfo().GetText();
+ const TextFrameIndex nEnd = m_nStart + m_pCurr->GetLen();
+ for (TextFrameIndex i = nEnd - TextFrameIndex(1); i >= m_nStart; --i)
+ {
+ const sal_Unicode aChar = rText[sal_Int32(i)];
+ if( CH_TAB != aChar && CH_BREAK != aChar && ' ' != aChar )
+ return i + TextFrameIndex(1);
+ }
+ return m_nStart;
+}
+
+// Does the paragraph fit into one line?
+bool SwTextFrameInfo::IsOneLine() const
+{
+ const SwLineLayout *pLay = m_pFrame->GetPara();
+ if( !pLay )
+ return false;
+
+ // For follows false of course
+ if( m_pFrame->GetFollow() )
+ return false;
+
+ pLay = pLay->GetNext();
+ while( pLay )
+ {
+ if( pLay->GetLen() )
+ return false;
+ pLay = pLay->GetNext();
+ }
+ return true;
+}
+
+// Is the line filled for X percent?
+bool SwTextFrameInfo::IsFilled( const sal_uInt8 nPercent ) const
+{
+ const SwLineLayout *pLay = m_pFrame->GetPara();
+ if( !pLay )
+ return false;
+
+ tools::Long nWidth = m_pFrame->getFramePrintArea().Width();
+ nWidth *= nPercent;
+ nWidth /= 100;
+ return nWidth <= pLay->Width();
+}
+
+// Where does the text start (without whitespace)? (document global)
+SwTwips SwTextFrameInfo::GetLineStart( const SwTextCursor &rLine )
+{
+ const TextFrameIndex nTextStart = rLine.GetTextStart();
+ if( rLine.GetStart() == nTextStart )
+ return rLine.GetLineStart();
+
+ SwRect aRect;
+ const_cast<SwTextCursor&>(rLine).GetCharRect( &aRect, nTextStart );
+ return aRect.Left();
+}
+
+// Where does the text start (without whitespace)? (relative in the Frame)
+SwTwips SwTextFrameInfo::GetLineStart() const
+{
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(m_pFrame) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(m_pFrame), &aInf );
+ return GetLineStart( aLine ) - m_pFrame->getFrameArea().Left() - m_pFrame->getFramePrintArea().Left();
+}
+
+// Calculates the character's position and returns the middle position
+SwTwips SwTextFrameInfo::GetCharPos(TextFrameIndex const nChar, bool bCenter) const
+{
+ SwRectFnSet aRectFnSet(m_pFrame);
+ SwFrameSwapper aSwapper( m_pFrame, true );
+
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(m_pFrame) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(m_pFrame), &aInf );
+
+ SwTwips nStt, nNext;
+ SwRect aRect;
+ aLine.GetCharRect( &aRect, nChar );
+ if ( aRectFnSet.IsVert() )
+ m_pFrame->SwitchHorizontalToVertical( aRect );
+
+ nStt = aRectFnSet.GetLeft(aRect);
+
+ if( !bCenter )
+ return nStt - aRectFnSet.GetLeft(m_pFrame->getFrameArea());
+
+ aLine.GetCharRect( &aRect, nChar + TextFrameIndex(1) );
+ if ( aRectFnSet.IsVert() )
+ m_pFrame->SwitchHorizontalToVertical( aRect );
+
+ nNext = aRectFnSet.GetLeft(aRect);
+
+ return (( nNext + nStt ) / 2 ) - aRectFnSet.GetLeft(m_pFrame->getFrameArea());
+}
+
+static void
+AddRange(std::vector<std::pair<TextFrameIndex, TextFrameIndex>> & rRanges,
+ TextFrameIndex const nPos, TextFrameIndex const nLen)
+{
+ assert(rRanges.empty() || rRanges.back().second <= nPos);
+ if( nLen )
+ {
+ if (!rRanges.empty() && nPos == rRanges.back().second)
+ {
+ rRanges.back().second += nLen;
+ }
+ else
+ {
+ rRanges.emplace_back(nPos, nPos + nLen);
+ }
+ }
+}
+
+// Accumulates the whitespace at line start and end in the vector
+void SwTextFrameInfo::GetSpaces(
+ std::vector<std::pair<TextFrameIndex, TextFrameIndex>> & rRanges,
+ bool const bWithLineBreak) const
+{
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(m_pFrame) );
+ SwTextMargin aLine( const_cast<SwTextFrame*>(m_pFrame), &aInf );
+ bool bFirstLine = true;
+ do {
+
+ if( aLine.GetCurr()->GetLen() )
+ {
+ TextFrameIndex nPos = aLine.GetTextStart();
+ // Do NOT include the blanks/tabs from the first line
+ // in the selection
+ if( !bFirstLine && nPos > aLine.GetStart() )
+ AddRange( rRanges, aLine.GetStart(), nPos - aLine.GetStart() );
+
+ // Do NOT include the blanks/tabs from the last line
+ // in the selection
+ if( aLine.GetNext() )
+ {
+ nPos = aLine.GetTextEnd();
+
+ if( nPos < aLine.GetEnd() )
+ {
+ TextFrameIndex const nOff( !bWithLineBreak && CH_BREAK ==
+ aLine.GetInfo().GetChar(aLine.GetEnd() - TextFrameIndex(1))
+ ? 1 : 0 );
+ AddRange( rRanges, nPos, aLine.GetEnd() - nPos - nOff );
+ }
+ }
+ }
+ bFirstLine = false;
+ }
+ while( aLine.Next() );
+}
+
+// Is there a bullet/symbol etc. at the text position?
+// Fonts: CharSet, SYMBOL and DONTKNOW
+bool SwTextFrameInfo::IsBullet(TextFrameIndex const nTextStart) const
+{
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(m_pFrame) );
+ SwTextMargin aLine( const_cast<SwTextFrame*>(m_pFrame), &aInf );
+ aInf.SetIdx( nTextStart );
+ return aLine.IsSymbol( nTextStart );
+}
+
+// Get first line indent
+// The precondition for a positive or negative first line indent:
+// All lines (except for the first one) have the same left margin.
+// We do not want to be so picky and work with a tolerance of TOLERANCE twips.
+SwTwips SwTextFrameInfo::GetFirstIndent() const
+{
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(m_pFrame) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(m_pFrame), &aInf );
+ const SwTwips nFirst = GetLineStart( aLine );
+ const SwTwips TOLERANCE = 20;
+
+ if( !aLine.Next() )
+ return 0;
+
+ SwTwips nLeft = GetLineStart( aLine );
+ while( aLine.Next() )
+ {
+ if( aLine.GetCurr()->GetLen() )
+ {
+ const SwTwips nCurrLeft = GetLineStart( aLine );
+ if( nLeft + TOLERANCE < nCurrLeft ||
+ nLeft - TOLERANCE > nCurrLeft )
+ return 0;
+ }
+ }
+
+ // At first we only return +1, -1 and 0
+ if( nLeft == nFirst )
+ return 0;
+
+ if( nLeft > nFirst )
+ return -1;
+
+ return 1;
+}
+
+sal_Int32 SwTextFrameInfo::GetBigIndent(TextFrameIndex& rFndPos,
+ const SwTextFrame *pNextFrame ) const
+{
+ SwTextSizeInfo aInf( const_cast<SwTextFrame*>(m_pFrame) );
+ SwTextCursor aLine( const_cast<SwTextFrame*>(m_pFrame), &aInf );
+ SwTwips nNextIndent = 0;
+
+ if( pNextFrame )
+ {
+ // I'm a single line
+ SwTextSizeInfo aNxtInf( const_cast<SwTextFrame*>(pNextFrame) );
+ SwTextCursor aNxtLine( const_cast<SwTextFrame*>(pNextFrame), &aNxtInf );
+ nNextIndent = GetLineStart( aNxtLine );
+ }
+ else
+ {
+ // I'm multi-line
+ if( aLine.Next() )
+ {
+ nNextIndent = GetLineStart( aLine );
+ aLine.Prev();
+ }
+ }
+
+ if( nNextIndent <= GetLineStart( aLine ) )
+ return 0;
+
+ const Point aPoint( nNextIndent, aLine.Y() );
+ rFndPos = aLine.GetModelPositionForViewPoint( nullptr, aPoint, false );
+ if (TextFrameIndex(1) >= rFndPos)
+ return 0;
+
+ // Is on front of a non-space
+ const OUString& rText = aInf.GetText();
+ sal_Unicode aChar = rText[sal_Int32(rFndPos)];
+ if( CH_TAB == aChar || CH_BREAK == aChar || ' ' == aChar ||
+ (( CH_TXTATR_BREAKWORD == aChar || CH_TXTATR_INWORD == aChar ) &&
+ aInf.HasHint( rFndPos ) ) )
+ return 0;
+
+ // and after a space
+ aChar = rText[sal_Int32(rFndPos) - 1];
+ if( CH_TAB != aChar && CH_BREAK != aChar &&
+ ( ( CH_TXTATR_BREAKWORD != aChar && CH_TXTATR_INWORD != aChar ) ||
+ !aInf.HasHint(rFndPos - TextFrameIndex(1))) &&
+ // More than two Blanks!
+ (' ' != aChar || ' ' != rText[sal_Int32(rFndPos) - 2]))
+ return 0;
+
+ SwRect aRect;
+ aLine.GetCharRect( &aRect, rFndPos );
+ return static_cast<sal_Int32>(aRect.Left() - m_pFrame->getFrameArea().Left() - m_pFrame->getFramePrintArea().Left());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/frmpaint.cxx b/sw/source/core/text/frmpaint.cxx
new file mode 100644
index 000000000..36885753e
--- /dev/null
+++ b/sw/source/core/text/frmpaint.cxx
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <editeng/pgrditem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <tgrditem.hxx>
+#include <paratr.hxx>
+
+#include <fmtline.hxx>
+#include <lineinfo.hxx>
+#include <charfmt.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <frmatr.hxx>
+#include <txtfrm.hxx>
+#include "itrpaint.hxx"
+#include "txtpaint.hxx"
+#include "txtcache.hxx"
+#include <flyfrm.hxx>
+#include "redlnitr.hxx"
+#include <redline.hxx>
+#include <swmodule.hxx>
+#include <tabfrm.hxx>
+#include <numrule.hxx>
+#include <wrong.hxx>
+
+#include <EnhancedPDFExportHelper.hxx>
+
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+
+#define REDLINE_DISTANCE 567/4
+#define REDLINE_MINDIST 567/10
+
+using namespace ::com::sun::star;
+
+static bool bInitFont = true;
+
+namespace {
+
+class SwExtraPainter
+{
+ SwSaveClip m_aClip;
+ SwRect m_aRect;
+ const SwTextFrame* m_pTextFrame;
+ SwViewShell *m_pSh;
+ std::unique_ptr<SwFont> m_pFnt;
+ const SwLineNumberInfo &m_rLineInf;
+ SwTwips m_nX;
+ SwTwips m_nRedX;
+ sal_uLong m_nLineNr;
+ sal_uInt16 m_nDivider;
+ bool m_bGoLeft;
+ bool IsClipChg() const { return m_aClip.IsChg(); }
+
+ SwExtraPainter(const SwExtraPainter&) = delete;
+ SwExtraPainter& operator=(const SwExtraPainter&) = delete;
+
+public:
+ SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh,
+ const SwLineNumberInfo &rLnInf, const SwRect &rRct,
+ sal_Int16 eHor, bool bLnNm );
+ SwFont* GetFont() const { return m_pFnt.get(); }
+ void IncLineNr() { ++m_nLineNr; }
+ bool HasNumber() const {
+ assert( m_rLineInf.GetCountBy() != 0 );
+ if( m_rLineInf.GetCountBy() == 0 )
+ return false;
+ return !( m_nLineNr % m_rLineInf.GetCountBy() );
+ }
+ bool HasDivider() const {
+ assert( m_rLineInf.GetDividerCountBy() != 0 );
+ if( !m_nDivider || m_rLineInf.GetDividerCountBy() == 0 )
+ return false;
+ return !(m_nLineNr % m_rLineInf.GetDividerCountBy());
+ }
+
+ void PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText = nullptr );
+ void PaintRedline( SwTwips nY, tools::Long nMax );
+};
+
+}
+
+SwExtraPainter::SwExtraPainter( const SwTextFrame *pFrame, SwViewShell *pVwSh,
+ const SwLineNumberInfo &rLnInf, const SwRect &rRct,
+ sal_Int16 eHor, bool bLineNum )
+ : m_aClip( pVwSh->GetWin() || pFrame->IsUndersized() ? pVwSh->GetOut() : nullptr )
+ , m_aRect( rRct )
+ , m_pTextFrame( pFrame )
+ , m_pSh( pVwSh )
+ , m_rLineInf( rLnInf )
+ , m_nX(0)
+ , m_nRedX(0)
+ , m_nLineNr( 1 )
+ , m_nDivider(0)
+ , m_bGoLeft(false)
+{
+ if( pFrame->IsUndersized() )
+ {
+ SwTwips nBottom = pFrame->getFrameArea().Bottom();
+ if( m_aRect.Bottom() > nBottom )
+ m_aRect.Bottom( nBottom );
+ }
+ std::optional<bool> oIsRightPage;
+ {
+ /* Initializes the Members necessary for line numbering:
+
+ nDivider, how often do we want a substring; 0 == never
+ nX, line number's x position
+ pFnt, line number's font
+ nLineNr, the first line number
+ bLineNum is set back to false if the numbering is completely
+ outside of the paint rect
+ */
+ m_nDivider = !m_rLineInf.GetDivider().isEmpty() ? m_rLineInf.GetDividerCountBy() : 0;
+ m_nX = pFrame->getFrameArea().Left();
+ SwCharFormat* pFormat = m_rLineInf.GetCharFormat( const_cast<IDocumentStylePoolAccess&>(pFrame->GetDoc().getIDocumentStylePoolAccess()) );
+ OSL_ENSURE( pFormat, "PaintExtraData without CharFormat" );
+ m_pFnt.reset( new SwFont(&pFormat->GetAttrSet(), &pFrame->GetDoc().getIDocumentSettingAccess()) );
+ m_pFnt->Invalidate();
+ m_pFnt->ChgPhysFnt( m_pSh, *m_pSh->GetOut() );
+ m_pFnt->SetVertical( 0_deg10, pFrame->IsVertical() );
+ }
+
+ if( bLineNum )
+ {
+ m_nLineNr += pFrame->GetAllLines() - pFrame->GetThisLines();
+ LineNumberPosition ePos = m_rLineInf.GetPos();
+ if( ePos != LINENUMBER_POS_LEFT && ePos != LINENUMBER_POS_RIGHT )
+ {
+ if( pFrame->FindPageFrame()->OnRightPage() )
+ {
+ oIsRightPage = true;
+ ePos = ePos == LINENUMBER_POS_INSIDE ?
+ LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT;
+ }
+ else
+ {
+ oIsRightPage = false;
+ ePos = ePos == LINENUMBER_POS_OUTSIDE ?
+ LINENUMBER_POS_LEFT : LINENUMBER_POS_RIGHT;
+ }
+ }
+ if( LINENUMBER_POS_LEFT == ePos )
+ {
+ m_bGoLeft = true;
+ m_nX -= m_rLineInf.GetPosFromLeft();
+ }
+ else
+ {
+ m_bGoLeft = false;
+ m_nX += pFrame->getFrameArea().Width() + m_rLineInf.GetPosFromLeft();
+ }
+ }
+ if( eHor == text::HoriOrientation::NONE )
+ return;
+
+ if( text::HoriOrientation::INSIDE == eHor || text::HoriOrientation::OUTSIDE == eHor )
+ {
+ if (!oIsRightPage)
+ oIsRightPage = pFrame->FindPageFrame()->OnRightPage();
+ if (*oIsRightPage)
+ eHor = eHor == text::HoriOrientation::INSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT;
+ else
+ eHor = eHor == text::HoriOrientation::OUTSIDE ? text::HoriOrientation::LEFT : text::HoriOrientation::RIGHT;
+ }
+ const SwFrame* pTmpFrame = pFrame->FindTabFrame();
+ if( !pTmpFrame )
+ pTmpFrame = pFrame;
+ m_nRedX = text::HoriOrientation::LEFT == eHor ? pTmpFrame->getFrameArea().Left() - REDLINE_DISTANCE :
+ pTmpFrame->getFrameArea().Right() + REDLINE_DISTANCE;
+}
+
+void SwExtraPainter::PaintExtra( SwTwips nY, tools::Long nAsc, tools::Long nMax, bool bRed, const OUString* pRedlineText )
+{
+ const OUString aTmp( pRedlineText
+ // Tracked change is stronger than the line number
+ ? *pRedlineText
+ : ( HasNumber()
+ // Line number is stronger than the divider
+ ? m_rLineInf.GetNumType().GetNumStr( m_nLineNr )
+ : m_rLineInf.GetDivider() ) );
+
+ // Get script type of line numbering:
+ m_pFnt->SetActual( SwScriptInfo::WhichFont(0, aTmp) );
+
+ if ( pRedlineText )
+ {
+ m_pFnt->SetColor(NON_PRINTING_CHARACTER_COLOR);
+ // don't strike out text in Insertions In Margin mode
+ if ( !m_pSh->GetViewOptions()->IsShowChangesInMargin2() )
+ m_pFnt->SetStrikeout( STRIKEOUT_SINGLE );
+ m_pFnt->SetSize( Size( 0, 200), m_pFnt->GetActual() );
+ }
+
+ SwDrawTextInfo aDrawInf( m_pSh, *m_pSh->GetOut(), aTmp, 0, aTmp.getLength() );
+ aDrawInf.SetSpace( 0 );
+ aDrawInf.SetWrong( nullptr );
+ aDrawInf.SetGrammarCheck( nullptr );
+ aDrawInf.SetSmartTags( nullptr );
+ aDrawInf.SetFrame( m_pTextFrame );
+ aDrawInf.SetFont( m_pFnt.get() );
+ aDrawInf.SetSnapToGrid( false );
+ aDrawInf.SetIgnoreFrameRTL( true );
+
+ bool bTooBig = m_pFnt->GetSize( m_pFnt->GetActual() ).Height() > nMax &&
+ m_pFnt->GetHeight( m_pSh, *m_pSh->GetOut() ) > nMax;
+ SwFont* pTmpFnt;
+ if( bTooBig )
+ {
+ pTmpFnt = new SwFont( *GetFont() );
+ if( nMax >= 20 )
+ {
+ nMax *= 17;
+ nMax /= 20;
+ }
+ pTmpFnt->SetSize( Size( 0, nMax ), pTmpFnt->GetActual() );
+ }
+ else
+ pTmpFnt = GetFont();
+ Point aTmpPos( m_nX, nY );
+ aTmpPos.AdjustY(nAsc );
+ if ( pRedlineText )
+ {
+ Size aSize = pTmpFnt->GetTextSize_( aDrawInf );
+ aTmpPos.AdjustX( -(aSize.Width()) - 200 );
+ }
+ bool bPaint = true;
+ if( !IsClipChg() )
+ {
+ Size aSize = pTmpFnt->GetTextSize_( aDrawInf );
+ if( m_bGoLeft )
+ aTmpPos.AdjustX( -(aSize.Width()) );
+ // calculate rectangle containing the line number
+ SwRect aRct( Point( aTmpPos.X(),
+ aTmpPos.Y() - pTmpFnt->GetAscent( m_pSh, *m_pSh->GetOut() )
+ ), aSize );
+ if( !m_aRect.Contains( aRct ) )
+ {
+ if( aRct.Intersection( m_aRect ).IsEmpty() )
+ bPaint = false;
+ else
+ m_aClip.ChgClip( m_aRect, m_pTextFrame );
+ }
+ }
+ else if( m_bGoLeft )
+ aTmpPos.AdjustX( -(pTmpFnt->GetTextSize_( aDrawInf ).Width()) );
+ aDrawInf.SetPos( aTmpPos );
+ if( bPaint )
+ pTmpFnt->DrawText_( aDrawInf );
+
+ if( bTooBig )
+ delete pTmpFnt;
+ if( bRed )
+ {
+ tools::Long nDiff = m_bGoLeft ? m_nRedX - m_nX : m_nX - m_nRedX;
+ if( nDiff > REDLINE_MINDIST )
+ PaintRedline( nY, nMax );
+ }
+}
+
+void SwExtraPainter::PaintRedline( SwTwips nY, tools::Long nMax )
+{
+ Point aStart( m_nRedX, nY );
+ Point aEnd( m_nRedX, nY + nMax );
+
+ if( !IsClipChg() )
+ {
+ SwRect aRct( aStart, aEnd );
+ if( !m_aRect.Contains( aRct ) )
+ {
+ if( aRct.Intersection( m_aRect ).IsEmpty() )
+ return;
+ m_aClip.ChgClip( m_aRect, m_pTextFrame );
+ }
+ }
+ const Color aOldCol( m_pSh->GetOut()->GetLineColor() );
+ m_pSh->GetOut()->SetLineColor( SW_MOD()->GetRedlineMarkColor() );
+
+ if ( m_pTextFrame->IsVertical() )
+ {
+ m_pTextFrame->SwitchHorizontalToVertical( aStart );
+ m_pTextFrame->SwitchHorizontalToVertical( aEnd );
+ }
+
+ m_pSh->GetOut()->DrawLine( aStart, aEnd );
+ m_pSh->GetOut()->SetLineColor( aOldCol );
+}
+
+void SwTextFrame::PaintExtraData( const SwRect &rRect ) const
+{
+ if( getFrameArea().Top() > rRect.Bottom() || getFrameArea().Bottom() < rRect.Top() )
+ return;
+
+ PaintOutlineContentVisibilityButton();
+
+ SwDoc const& rDoc(GetDoc());
+ const IDocumentRedlineAccess& rIDRA = rDoc.getIDocumentRedlineAccess();
+ const SwLineNumberInfo &rLineInf = rDoc.GetLineNumberInfo();
+ const SwFormatLineNumber &rLineNum = GetAttrSet()->GetLineNumber();
+ bool bLineNum = !IsInTab() && rLineInf.IsPaintLineNumbers() &&
+ ( !IsInFly() || rLineInf.IsCountInFlys() ) && rLineNum.IsCount();
+ sal_Int16 eHor = static_cast<sal_Int16>(SW_MOD()->GetRedlineMarkPos());
+ if (eHor != text::HoriOrientation::NONE
+ && (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
+ || getRootFrame()->IsHideRedlines()))
+ {
+ eHor = text::HoriOrientation::NONE;
+ }
+ bool bRedLine = eHor != text::HoriOrientation::NONE;
+ if ( !bLineNum && !bRedLine )
+ return;
+
+ if( IsLocked() || IsHiddenNow() || !getFramePrintArea().Height() )
+ return;
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
+ SwRect rOldRect( rRect );
+
+ if ( IsVertical() )
+ SwitchVerticalToHorizontal( const_cast<SwRect&>(rRect) );
+
+ SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() );
+ aLayoutModeModifier.Modify( false );
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pSh->GetOut() );
+
+ SwExtraPainter aExtra( this, pSh, rLineInf, rRect, eHor, bLineNum );
+
+ if( HasPara() )
+ {
+ TextFrameLockGuard aLock(const_cast<SwTextFrame*>(this));
+
+ SwTextLineAccess aAccess( this );
+ aAccess.GetPara();
+
+ SwTextPaintInfo aInf( const_cast<SwTextFrame*>(this), rRect );
+
+ aLayoutModeModifier.Modify( false );
+
+ SwTextPainter aLine( const_cast<SwTextFrame*>(this), &aInf );
+ bool bNoDummy = !aLine.GetNext(); // Only one empty line!
+
+ while( aLine.Y() + aLine.GetLineHeight() <= rRect.Top() )
+ {
+ if( !aLine.GetCurr()->IsDummy() &&
+ ( rLineInf.IsCountBlankLines() ||
+ aLine.GetCurr()->HasContent() ) )
+ aExtra.IncLineNr();
+ if( !aLine.Next() )
+ {
+ const_cast<SwRect&>(rRect) = rOldRect;
+ return;
+ }
+ }
+
+ tools::Long nBottom = rRect.Bottom();
+
+ bool bNoPrtLine = 0 == GetMinPrtLine();
+ if( !bNoPrtLine )
+ {
+ while ( aLine.Y() < GetMinPrtLine() )
+ {
+ if( ( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )
+ && !aLine.GetCurr()->IsDummy() )
+ aExtra.IncLineNr();
+ if( !aLine.Next() )
+ break;
+ }
+ bNoPrtLine = aLine.Y() >= GetMinPrtLine();
+ }
+ const bool bIsShowChangesInMargin = pSh->GetViewOptions()->IsShowChangesInMargin();
+ if( bNoPrtLine )
+ {
+ do
+ {
+ if( bNoDummy || !aLine.GetCurr()->IsDummy() )
+ {
+ bool bRed = bRedLine && aLine.GetCurr()->HasRedline();
+ if( rLineInf.IsCountBlankLines() || aLine.GetCurr()->HasContent() )
+ {
+ bool bRedInMargin = bIsShowChangesInMargin && bRed;
+ bool bNum = bLineNum && ( aExtra.HasNumber() || aExtra.HasDivider() );
+ if( bRedInMargin || bNum )
+ {
+ SwTwips nTmpHeight, nTmpAscent;
+ aLine.CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+ if ( bRedInMargin )
+ {
+ const OUString* pRedlineText = aLine.GetCurr()->GetRedlineText();
+ if( !pRedlineText->isEmpty() )
+ {
+ aExtra.PaintExtra( aLine.Y(), nTmpAscent,
+ nTmpHeight, bRed, pRedlineText );
+ bRed = false;
+ bNum = false;
+ }
+ }
+ if ( bNum )
+ {
+ aExtra.PaintExtra( aLine.Y(), nTmpAscent, nTmpHeight, bRed );
+ bRed = false;
+ }
+ }
+ aExtra.IncLineNr();
+ }
+ if( bRed )
+ aExtra.PaintRedline( aLine.Y(), aLine.GetLineHeight() );
+ }
+ } while( aLine.Next() && aLine.Y() <= nBottom );
+ }
+ }
+ else
+ {
+ if (SwRedlineTable::npos == rIDRA.GetRedlinePos(*GetTextNodeFirst(), RedlineType::Any))
+ {
+ bRedLine = false;
+ }
+
+ if( bLineNum && rLineInf.IsCountBlankLines() &&
+ ( aExtra.HasNumber() || aExtra.HasDivider() ) )
+ {
+ aExtra.PaintExtra( getFrameArea().Top()+getFramePrintArea().Top(), aExtra.GetFont()
+ ->GetAscent( pSh, *pSh->GetOut() ), getFramePrintArea().Height(), bRedLine );
+ }
+ else if( bRedLine )
+ aExtra.PaintRedline( getFrameArea().Top()+getFramePrintArea().Top(), getFramePrintArea().Height() );
+ }
+
+ const_cast<SwRect&>(rRect) = rOldRect;
+
+}
+
+SwRect SwTextFrame::GetPaintSwRect()
+{
+ // finger layout
+ OSL_ENSURE( isFrameAreaPositionValid(), "+SwTextFrame::GetPaintSwRect: no Calc()" );
+
+ SwRect aRet( getFramePrintArea() );
+ if ( IsEmpty() || !HasPara() )
+ aRet += getFrameArea().Pos();
+ else
+ {
+ // We return the right paint rect. Use the calculated PaintOfst as the
+ // left margin
+ SwRepaint& rRepaint = GetPara()->GetRepaint();
+ tools::Long l;
+
+ if ( IsVertLR() && !IsVertLRBT()) // mba: the following line was added, but we don't need it for the existing directions; kept for IsVertLR(), but should be checked
+ rRepaint.Chg( GetUpper()->getFrameArea().Pos() + GetUpper()->getFramePrintArea().Pos(), GetUpper()->getFramePrintArea().SSize() );
+
+ if( rRepaint.GetOffset() )
+ rRepaint.Left( rRepaint.GetOffset() );
+
+ l = rRepaint.GetRightOfst();
+ if( l && l > rRepaint.Right() )
+ rRepaint.Right( l );
+ rRepaint.SetOffset( 0 );
+ aRet = rRepaint;
+
+ // In case our left edge is the same as the body frame's left edge,
+ // then extend the rectangle to include the page margin as well,
+ // otherwise some font will be clipped.
+ SwLayoutFrame* pBodyFrame = GetUpper();
+ if (pBodyFrame->IsBodyFrame() && aRet.Left() == (pBodyFrame->getFrameArea().Left() + pBodyFrame->getFramePrintArea().Left()))
+ if (SwLayoutFrame* pPageFrame = pBodyFrame->GetUpper())
+ aRet.Left(pPageFrame->getFrameArea().Left());
+
+ if ( IsRightToLeft() )
+ SwitchLTRtoRTL( aRet );
+
+ if ( IsVertical() )
+ SwitchHorizontalToVertical( aRet );
+ }
+ ResetRepaint();
+
+ return aRet;
+}
+
+bool SwTextFrame::PaintEmpty( const SwRect &rRect, bool bCheck ) const
+{
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if( pSh && ( pSh->GetViewOptions()->IsParagraph() || bInitFont ) )
+ {
+ bInitFont = false;
+ SwTextFly aTextFly( this );
+ aTextFly.SetTopRule();
+ SwRect aRect;
+ if( bCheck && aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
+ return false;
+ else if( pSh->GetWin() )
+ {
+ std::unique_ptr<SwFont> pFnt;
+ RedlineType eRedline = RedlineType::None;
+ const SwTextNode& rTextNode = *GetTextNodeForParaProps();
+ if ( rTextNode.HasSwAttrSet() )
+ {
+ const SwAttrSet *pAttrSet = &( rTextNode.GetSwAttrSet() );
+ pFnt.reset(new SwFont( pAttrSet, rTextNode.getIDocumentSettingAccess() ));
+ }
+ else
+ {
+ SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), pSh );
+ pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
+ }
+
+ const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess();
+ if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
+ && !getRootFrame()->IsHideRedlines())
+ {
+ const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ SwAttrHandler aAttrHandler;
+ aAttrHandler.Init( rTextNode.GetSwAttrSet(),
+ *rTextNode.getIDocumentSettingAccess() );
+ SwRedlineItr aRedln(rTextNode, *pFnt, aAttrHandler, nRedlPos, SwRedlineItr::Mode::Show);
+ const SwRangeRedline* pRedline = rIDRA.GetRedlineTable()[nRedlPos];
+ // show redlining only on the inserted/deleted empty paragraph, but not on the next one
+ if ( rTextNode.GetIndex() != pRedline->End()->nNode.GetIndex() )
+ eRedline = pRedline->GetType();
+ // except if the next empty paragraph starts a new redline (e.g. deletion after insertion)
+ else if ( nRedlPos + 1 < rIDRA.GetRedlineTable().size() )
+ {
+ const SwRangeRedline* pNextRedline = rIDRA.GetRedlineTable()[nRedlPos + 1];
+ if ( rTextNode.GetIndex() == pNextRedline->Start()->nNode.GetIndex() )
+ eRedline = pNextRedline->GetType();
+ }
+ }
+ }
+
+ if( pSh->GetViewOptions()->IsParagraph() && getFramePrintArea().Height() )
+ {
+ if( RTL_TEXTENCODING_SYMBOL == pFnt->GetCharSet( SwFontScript::Latin ) &&
+ pFnt->GetName( SwFontScript::Latin ) != numfunc::GetDefBulletFontname() )
+ {
+ pFnt->SetFamily( FAMILY_DONTKNOW, SwFontScript::Latin );
+ pFnt->SetName( numfunc::GetDefBulletFontname(), SwFontScript::Latin );
+ pFnt->SetStyleName(OUString(), SwFontScript::Latin);
+ pFnt->SetCharSet( RTL_TEXTENCODING_SYMBOL, SwFontScript::Latin );
+ }
+ pFnt->SetVertical( 0_deg10, IsVertical() );
+ SwFrameSwapper aSwapper( this, true );
+ SwLayoutModeModifier aLayoutModeModifier( *pSh->GetOut() );
+ aLayoutModeModifier.Modify( IsRightToLeft() );
+
+ pFnt->Invalidate();
+ pFnt->ChgPhysFnt( pSh, *pSh->GetOut() );
+ Point aPos = getFrameArea().Pos() + getFramePrintArea().Pos();
+
+ const SvxLRSpaceItem &rSpace =
+ GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace();
+
+ if ( rSpace.GetTextFirstLineOffset() > 0 )
+ aPos.AdjustX(rSpace.GetTextFirstLineOffset() );
+
+ std::unique_ptr<SwSaveClip, o3tl::default_delete<SwSaveClip>> xClip;
+ if( IsUndersized() )
+ {
+ xClip.reset(new SwSaveClip( pSh->GetOut() ));
+ xClip->ChgClip( rRect );
+ }
+
+ aPos.AdjustY(pFnt->GetAscent( pSh, *pSh->GetOut() ) );
+
+ if (GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() &&
+ IsInDocBody() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
+ if ( pGrid )
+ {
+ // center character in grid line
+ aPos.AdjustY(( pGrid->GetBaseHeight() -
+ pFnt->GetHeight( pSh, *pSh->GetOut() ) ) / 2 );
+
+ if ( ! pGrid->GetRubyTextBelow() )
+ aPos.AdjustY(pGrid->GetRubyHeight() );
+ }
+ }
+
+ // Don't show the paragraph mark for collapsed paragraphs, when they are hidden
+ if ( EmptyHeight( ) > 1 )
+ {
+ SwDrawTextInfo aDrawInf( pSh, *pSh->GetOut(), CH_PAR, 0, 1 );
+ aDrawInf.SetPos( aPos );
+ aDrawInf.SetSpace( 0 );
+ aDrawInf.SetKanaComp( 0 );
+ aDrawInf.SetWrong( nullptr );
+ aDrawInf.SetGrammarCheck( nullptr );
+ aDrawInf.SetSmartTags( nullptr );
+ aDrawInf.SetFrame( this );
+ aDrawInf.SetFont( pFnt.get() );
+ aDrawInf.SetSnapToGrid( false );
+
+ // show redline color and settings drawing a background pilcrow,
+ // but keep also other formattings (with neutral pilcrow color)
+ if ( eRedline != RedlineType::None )
+ {
+ pFnt->DrawText_( aDrawInf );
+ if ( eRedline == RedlineType::Delete )
+ pFnt->SetStrikeout( STRIKEOUT_NONE );
+ else
+ pFnt->SetUnderline( LINESTYLE_NONE );
+ }
+
+ pFnt->SetColor(NON_PRINTING_CHARACTER_COLOR);
+ pFnt->DrawText_( aDrawInf );
+ }
+ }
+ return true;
+ }
+ }
+ else
+ return true;
+ return false;
+}
+
+void SwTextFrame::PaintSwFrame(vcl::RenderContext& rRenderContext, SwRect const& rRect, SwPrintData const*const) const
+{
+ ResetRepaint();
+
+ // #i16816# tagged pdf support
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+
+ Num_Info aNumInfo( *this );
+ SwTaggedPDFHelper aTaggedPDFHelperNumbering( &aNumInfo, nullptr, nullptr, rRenderContext );
+
+ Frame_Info aFrameInfo( *this );
+ SwTaggedPDFHelper aTaggedPDFHelperParagraph( nullptr, &aFrameInfo, nullptr, rRenderContext );
+
+ if( IsEmpty() && PaintEmpty( rRect, true ) )
+ return;
+
+ if( IsLocked() || IsHiddenNow() || ! getFramePrintArea().HasArea() )
+ return;
+
+ // It can happen that the IdleCollector withdrew my cached information
+ if( !HasPara() )
+ {
+ OSL_ENSURE( isFrameAreaPositionValid(), "+SwTextFrame::PaintSwFrame: no Calc()" );
+
+ // #i29062# pass info that we are currently
+ // painting.
+ const_cast<SwTextFrame*>(this)->GetFormatted( true );
+ if( IsEmpty() )
+ {
+ PaintEmpty( rRect, false );
+ return;
+ }
+ if( !HasPara() )
+ {
+ OSL_ENSURE( false, "+SwTextFrame::PaintSwFrame: missing format information" );
+ return;
+ }
+ }
+
+ // We don't want to be interrupted while painting.
+ // Do that after thr Format()!
+ TextFrameLockGuard aLock(const_cast<SwTextFrame*>(this));
+
+ // We only paint the part of the TextFrame which changed, is within the
+ // range and was requested to paint.
+ // One could think that the area rRect _needs_ to be painted, although
+ // rRepaint is set. Indeed, we cannot avoid this problem from a formal
+ // perspective. Luckily we can assume rRepaint to be empty when we need
+ // paint the while Frame.
+ SwTextLineAccess aAccess( this );
+ SwParaPortion *pPara = aAccess.GetPara();
+
+ SwRepaint &rRepaint = pPara->GetRepaint();
+
+ // Switch off recycling when in the FlyContentFrame.
+ // A DrawRect is called for repainting the line anyways.
+ if( rRepaint.GetOffset() )
+ {
+ const SwFlyFrame *pFly = FindFlyFrame();
+ if( pFly && pFly->IsFlyInContentFrame() )
+ rRepaint.SetOffset( 0 );
+ }
+
+ // Ge the String for painting. The length is of special interest.
+
+ // Rectangle
+ OSL_ENSURE( ! IsSwapped(), "A frame is swapped before Paint" );
+ SwRect aOldRect( rRect );
+
+ {
+ SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
+
+ if ( IsVertical() )
+ SwitchVerticalToHorizontal( const_cast<SwRect&>(rRect) );
+
+ if ( IsRightToLeft() )
+ SwitchRTLtoLTR( const_cast<SwRect&>(rRect) );
+
+ SwTextPaintInfo aInf( const_cast<SwTextFrame*>(this), rRect );
+ sw::WrongListIterator iterWrong(*this, &SwTextNode::GetWrong);
+ sw::WrongListIterator iterGrammar(*this, &SwTextNode::GetGrammarCheck);
+ sw::WrongListIterator iterSmartTags(*this, &SwTextNode::GetSmartTags);
+ if (iterWrong.LooksUseful())
+ {
+ aInf.SetWrongList( &iterWrong );
+ }
+ if (iterGrammar.LooksUseful())
+ {
+ aInf.SetGrammarCheckList( &iterGrammar );
+ }
+ if (iterSmartTags.LooksUseful())
+ {
+ aInf.SetSmartTags( &iterSmartTags );
+ }
+ aInf.GetTextFly().SetTopRule();
+
+ SwTextPainter aLine( const_cast<SwTextFrame*>(this), &aInf );
+ // Optimization: if no free flying Frame overlaps into our line, the
+ // SwTextFly just switches off
+ aInf.GetTextFly().Relax();
+
+ OutputDevice* pOut = aInf.GetOut();
+ const bool bOnWin = pSh->GetWin() != nullptr;
+
+ SwSaveClip aClip( bOnWin || IsUndersized() ? pOut : nullptr );
+
+ // Output loop: For each Line ... (which is still visible) ...
+ // adapt rRect (Top + 1, Bottom - 1)
+ // Because the Iterator attaches the Lines without a gap to each other
+ aLine.TwipsToLine( rRect.Top() + 1 );
+ tools::Long nBottom = rRect.Bottom();
+
+ bool bNoPrtLine = 0 == GetMinPrtLine();
+ if( !bNoPrtLine )
+ {
+ while ( aLine.Y() < GetMinPrtLine() && aLine.Next() )
+ ;
+ bNoPrtLine = aLine.Y() >= GetMinPrtLine();
+ }
+ if( bNoPrtLine )
+ {
+ do
+ {
+ aLine.DrawTextLine( rRect, aClip, IsUndersized() );
+
+ } while( aLine.Next() && aLine.Y() <= nBottom );
+ }
+
+ // Once is enough:
+ if( aLine.IsPaintDrop() )
+ aLine.PaintDropPortion();
+
+ if( rRepaint.HasArea() )
+ rRepaint.Clear();
+ }
+
+ const_cast<SwRect&>(rRect) = aOldRect;
+
+ OSL_ENSURE( ! IsSwapped(), "A frame is swapped after Paint" );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/guess.cxx b/sw/source/core/text/guess.cxx
new file mode 100644
index 000000000..d209105cd
--- /dev/null
+++ b/sw/source/core/text/guess.cxx
@@ -0,0 +1,659 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/unolingu.hxx>
+#include <breakit.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include "guess.hxx"
+#include "inftxt.hxx"
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+#include <com/sun/star/i18n/BreakType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <unotools/charclass.hxx>
+#include "porfld.hxx"
+#include <paratr.hxx>
+#include <doc.hxx>
+#include <unotools/linguprops.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+
+namespace{
+
+bool IsBlank(sal_Unicode ch) { return ch == CH_BLANK || ch == CH_FULL_BLANK || ch == CH_NB_SPACE || ch == CH_SIX_PER_EM; }
+
+}
+
+// provides information for line break calculation
+// returns true if no line break has to be performed
+// otherwise possible break or hyphenation position is determined
+bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
+ const sal_uInt16 nPorHeight )
+{
+ m_nCutPos = rInf.GetIdx();
+
+ // Empty strings are always 0
+ if( !rInf.GetLen() || rInf.GetText().isEmpty() )
+ return false;
+
+ OSL_ENSURE( rInf.GetIdx() < TextFrameIndex(rInf.GetText().getLength()),
+ "+SwTextGuess::Guess: invalid SwTextFormatInfo" );
+
+ OSL_ENSURE( nPorHeight, "+SwTextGuess::Guess: no height" );
+
+ sal_uInt16 nMaxSizeDiff;
+
+ const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
+
+ sal_uInt16 nMaxComp = ( SwFontScript::CJK == rInf.GetFont()->GetActual() ) &&
+ rSI.CountCompChg() &&
+ ! rInf.IsMulti() &&
+ ! rPor.InFieldGrp() &&
+ ! rPor.IsDropPortion() ?
+ 10000 :
+ 0 ;
+
+ SwTwips nLineWidth = rInf.GetLineWidth();
+ TextFrameIndex nMaxLen = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
+
+ const SvxAdjust& rAdjust = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust();
+
+ // tdf#104668 space chars at the end should be cut if the compatibility option is enabled
+ // for LTR mode only
+ if ( !rInf.GetTextFrame()->IsRightToLeft() )
+ {
+ if (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
+ {
+ if ( rAdjust == SvxAdjust::Right || rAdjust == SvxAdjust::Center )
+ {
+ TextFrameIndex nSpaceCnt(0);
+ for (sal_Int32 i = rInf.GetText().getLength() - 1;
+ sal_Int32(rInf.GetIdx()) <= i; --i)
+ {
+ sal_Unicode cChar = rInf.GetText()[i];
+ if ( cChar != CH_BLANK && cChar != CH_FULL_BLANK && cChar != CH_SIX_PER_EM )
+ break;
+ ++nSpaceCnt;
+ }
+ TextFrameIndex nCharsCnt = nMaxLen - nSpaceCnt;
+ if ( nSpaceCnt && nCharsCnt < rPor.GetLen() )
+ {
+ nMaxLen = nCharsCnt;
+ if ( !nMaxLen )
+ return true;
+ }
+ }
+ }
+ }
+
+ if ( rInf.GetLen() < nMaxLen )
+ nMaxLen = rInf.GetLen();
+
+ if( !nMaxLen )
+ return false;
+
+ sal_uInt16 nItalic = 0;
+ if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
+ {
+ bool bAddItalic = true;
+
+ // do not add extra italic value if we have an active character grid
+ if ( rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid(
+ GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
+ bAddItalic = !pGrid || GRID_LINES_CHARS != pGrid->GetGridType();
+ }
+
+ // do not add extra italic value for an isolated blank:
+ if (TextFrameIndex(1) == rInf.GetLen() &&
+ CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx())])
+ {
+ bAddItalic = false;
+ }
+
+ if (rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_MARGIN))
+ {
+ // Content is allowed over the margin: in this case over-margin content caused by italic
+ // formatting is OK.
+ bAddItalic = false;
+ }
+
+ nItalic = bAddItalic ? nPorHeight / 12 : 0;
+
+ nLineWidth -= nItalic;
+
+ // #i46524# LineBreak bug with italics
+ if ( nLineWidth < 0 ) nLineWidth = 0;
+ }
+
+ const sal_Int32 nLeftRightBorderSpace =
+ (!rPor.GetJoinBorderWithNext() ? rInf.GetFont()->GetRightBorderSpace() : 0) +
+ (!rPor.GetJoinBorderWithPrev() ? rInf.GetFont()->GetLeftBorderSpace() : 0);
+
+ nLineWidth -= nLeftRightBorderSpace;
+
+ const bool bUnbreakableNumberings = rInf.GetTextFrame()->GetDoc()
+ .getIDocumentSettingAccess().get(DocumentSettingId::UNBREAKABLE_NUMBERINGS);
+
+ // first check if everything fits to line
+ if ( ( nLineWidth * 2 > SwTwips(sal_Int32(nMaxLen)) * nPorHeight ) ||
+ ( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
+ {
+ // call GetTextSize with maximum compression (for kanas)
+ rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen,
+ nMaxComp, m_nBreakWidth, nMaxSizeDiff );
+
+ if ( ( m_nBreakWidth <= nLineWidth ) || ( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
+ {
+ // portion fits to line
+ m_nCutPos = rInf.GetIdx() + nMaxLen;
+ if( nItalic &&
+ (m_nCutPos >= TextFrameIndex(rInf.GetText().getLength()) ||
+ // #i48035# Needed for CalcFitToContent
+ // if first line ends with a manual line break
+ rInf.GetText()[sal_Int32(m_nCutPos)] == CH_BREAK))
+ m_nBreakWidth = m_nBreakWidth + nItalic;
+
+ // save maximum width for later use
+ if ( nMaxSizeDiff )
+ rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
+
+ m_nBreakWidth += nLeftRightBorderSpace;
+
+ return true;
+ }
+ }
+
+ bool bHyph = rInf.IsHyphenate() && !rInf.IsHyphForbud();
+ TextFrameIndex nHyphPos(0);
+
+ // nCutPos is the first character not fitting to the current line
+ // nHyphPos is the first character not fitting to the current line,
+ // considering an additional "-" for hyphenation
+ if( bHyph )
+ {
+ // nHyphZone is the first character not fitting in the hyphenation zone,
+ // or 0, if the whole line in the hyphenation zone,
+ // or -1, if no hyphenation zone defined (i.e. it is 0)
+ sal_Int32 nHyphZone = -1;
+ const css::beans::PropertyValues & rHyphValues = rInf.GetHyphValues();
+ assert( rHyphValues.getLength() > 5 && rHyphValues[5].Name == UPN_HYPH_ZONE );
+ // hyphenation zone (distance from the line end in twips)
+ sal_uInt16 nTextHyphenZone;
+ if ( rHyphValues[5].Value >>= nTextHyphenZone )
+ nHyphZone = nTextHyphenZone >= nLineWidth
+ ? 0
+ : sal_Int32(rInf.GetTextBreak( nLineWidth - nTextHyphenZone,
+ nMaxLen, nMaxComp, rInf.GetCachedVclData().get() ));
+
+ m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, nHyphPos, rInf.GetCachedVclData().get() );
+
+ // don't try to hyphenate in the hyphenation zone
+ if ( nHyphZone != -1 && TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
+ {
+ sal_Int32 nZonePos = sal_Int32(m_nCutPos);
+ // disable hyphenation, if there is a space within the hyphenation zone
+ // Note: for better interoperability, not fitting space character at
+ // rInf.GetIdx()[nHyphZone] always disables the hyphenation, don't need to calculate
+ // with its fitting part. Moreover, do not check double or more spaces there, they
+ // are accepted outside of the hyphenation zone, too.
+ for (; sal_Int32(rInf.GetIdx()) <= nZonePos && nHyphZone <= nZonePos; --nZonePos )
+ {
+ sal_Unicode cChar = rInf.GetText()[nZonePos];
+ if ( cChar == CH_BLANK || cChar == CH_FULL_BLANK || cChar == CH_SIX_PER_EM )
+ {
+ bHyph = false;
+ }
+ }
+ }
+
+ // search start of the last word, if needed
+ if ( bHyph )
+ {
+ // nLastWord is the space character before the last word
+ sal_Int32 nLastWord = rInf.GetText().getLength() - 1;
+ bool bHyphenationNoLastWord = false;
+ assert( rHyphValues.getLength() > 3 && rHyphValues[3].Name == UPN_HYPH_NO_LAST_WORD );
+ if ( rHyphValues[3].Value >>= bHyphenationNoLastWord )
+ {
+ // skip spaces after the last word
+ bool bCutBlank = false;
+ for (; sal_Int32(rInf.GetIdx()) <= nLastWord; --nLastWord )
+ {
+ sal_Unicode cChar = rInf.GetText()[nLastWord];
+ if ( cChar != CH_BLANK && cChar != CH_FULL_BLANK && cChar != CH_SIX_PER_EM )
+ bCutBlank = true;
+ else if ( bCutBlank )
+ break;
+ }
+ }
+
+ // don't hyphenate the last word of the paragraph
+ if ( bHyphenationNoLastWord && sal_Int32(m_nCutPos) > nLastWord &&
+ TextFrameIndex(COMPLETE_STRING) != m_nCutPos &&
+ // if the last word is multiple line long, e.g. an URL,
+ // apply this only if the space before the word is there
+ // in the actual line, i.e. start the long word in a new
+ // line, but still allows to break its last parts
+ sal_Int32(rInf.GetIdx()) < nLastWord )
+ {
+ m_nCutPos = TextFrameIndex(nLastWord);
+ }
+ }
+
+ if ( !nHyphPos && rInf.GetIdx() )
+ nHyphPos = rInf.GetIdx() - TextFrameIndex(1);
+ }
+ else
+ {
+ m_nCutPos = rInf.GetTextBreak( nLineWidth, nMaxLen, nMaxComp, rInf.GetCachedVclData().get() );
+
+#if OSL_DEBUG_LEVEL > 1
+ if ( TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
+ {
+ sal_uInt16 nMinSize;
+ rInf.GetTextSize( &rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(),
+ nMaxComp, nMinSize, nMaxSizeDiff );
+ OSL_ENSURE( nMinSize <= nLineWidth, "What a Guess!!!" );
+ }
+#endif
+ }
+
+ if( m_nCutPos > rInf.GetIdx() + nMaxLen )
+ {
+ // second check if everything fits to line
+ m_nCutPos = m_nBreakPos = rInf.GetIdx() + nMaxLen - TextFrameIndex(1);
+ rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen, nMaxComp,
+ m_nBreakWidth, nMaxSizeDiff );
+
+ // The following comparison should always give true, otherwise
+ // there likely has been a pixel rounding error in GetTextBreak
+ if ( m_nBreakWidth <= nLineWidth )
+ {
+ if (nItalic && (m_nBreakPos + TextFrameIndex(1)) >= TextFrameIndex(rInf.GetText().getLength()))
+ m_nBreakWidth = m_nBreakWidth + nItalic;
+
+ // save maximum width for later use
+ if ( nMaxSizeDiff )
+ rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
+
+ m_nBreakWidth += nLeftRightBorderSpace;
+
+ return true;
+ }
+ }
+
+ // we have to trigger an underflow for a footnote portion
+ // which does not fit to the current line
+ if ( rPor.IsFootnotePortion() )
+ {
+ m_nBreakPos = rInf.GetIdx();
+ m_nCutPos = TextFrameIndex(-1);
+ return false;
+ }
+
+ TextFrameIndex nPorLen(0);
+ // do not call the break iterator nCutPos is a blank
+ sal_Unicode cCutChar = m_nCutPos < TextFrameIndex(rInf.GetText().getLength())
+ ? rInf.GetText()[sal_Int32(m_nCutPos)]
+ : 0;
+ if (IsBlank(cCutChar))
+ {
+ m_nBreakPos = m_nCutPos;
+ TextFrameIndex nX = m_nBreakPos;
+
+ if ( rAdjust == SvxAdjust::Left )
+ {
+ // we step back until a non blank character has been found
+ // or there is only one more character left
+ while (nX && TextFrameIndex(rInf.GetText().getLength()) < m_nBreakPos &&
+ IsBlank(rInf.GetChar(--nX)))
+ --m_nBreakPos;
+ }
+ else // #i20878#
+ {
+ while (nX && m_nBreakPos > rInf.GetLineStart() + TextFrameIndex(1) &&
+ IsBlank(rInf.GetChar(--nX)))
+ --m_nBreakPos;
+ }
+
+ if( m_nBreakPos > rInf.GetIdx() )
+ nPorLen = m_nBreakPos - rInf.GetIdx();
+ while (++m_nCutPos < TextFrameIndex(rInf.GetText().getLength()) &&
+ IsBlank(rInf.GetChar(m_nCutPos)))
+ ; // nothing
+
+ m_nBreakStart = m_nCutPos;
+ }
+ else
+ {
+ // New: We should have a look into the last portion, if it was a
+ // field portion. For this, we expand the text of the field portion
+ // into our string. If the line break position is inside of before
+ // the field portion, we trigger an underflow.
+
+ TextFrameIndex nOldIdx = rInf.GetIdx();
+ sal_Unicode cFieldChr = 0;
+
+#if OSL_DEBUG_LEVEL > 0
+ OUString aDebugString;
+#endif
+
+ // be careful: a field portion can be both: 0x01 (common field)
+ // or 0x02 (the follow of a footnode)
+ if ( rInf.GetLast() && rInf.GetLast()->InFieldGrp() &&
+ ! rInf.GetLast()->IsFootnotePortion() &&
+ rInf.GetIdx() > rInf.GetLineStart() &&
+ CH_TXTATR_BREAKWORD ==
+ (cFieldChr = rInf.GetText()[sal_Int32(rInf.GetIdx()) - 1]))
+ {
+ SwFieldPortion* pField = static_cast<SwFieldPortion*>(rInf.GetLast());
+ OUString aText;
+ pField->GetExpText( rInf, aText );
+
+ if ( !aText.isEmpty() )
+ {
+ m_nFieldDiff = TextFrameIndex(aText.getLength() - 1);
+ m_nCutPos = m_nCutPos + m_nFieldDiff;
+ nHyphPos = nHyphPos + m_nFieldDiff;
+
+#if OSL_DEBUG_LEVEL > 0
+ aDebugString = rInf.GetText();
+#endif
+
+ // this is pretty nutso... reverted at the end...
+ OUString& rOldText = const_cast<OUString&> (rInf.GetText());
+ rOldText = rOldText.replaceAt(sal_Int32(rInf.GetIdx()) - 1, 1, aText);
+ rInf.SetIdx( rInf.GetIdx() + m_nFieldDiff );
+ }
+ else
+ cFieldChr = 0;
+ }
+
+ LineBreakHyphenationOptions aHyphOpt;
+ Reference< XHyphenator > xHyph;
+ if( bHyph )
+ {
+ xHyph = ::GetHyphenator();
+ aHyphOpt = LineBreakHyphenationOptions( xHyph,
+ rInf.GetHyphValues(), sal_Int32(nHyphPos));
+ }
+
+ // Get Language for break iterator.
+ // We have to switch the current language if we have a script
+ // change at nCutPos. Otherwise LATIN punctuation would never
+ // be allowed to be hanging punctuation.
+ // NEVER call GetLang if the string has been modified!!!
+ LanguageType aLang = rInf.GetFont()->GetLanguage();
+
+ // If we are inside a field portion, we use a temporary string which
+ // differs from the string at the textnode. Therefore we are not allowed
+ // to call the GetLang function.
+ if ( m_nCutPos && ! rPor.InFieldGrp() )
+ {
+ const CharClass& rCC = GetAppCharClass();
+
+ // step back until a non-punctuation character is reached
+ TextFrameIndex nLangIndex = m_nCutPos;
+
+ // If a field has been expanded right in front of us we do not
+ // step further than the beginning of the expanded field
+ // (which is the position of the field placeholder in our
+ // original string).
+ const TextFrameIndex nDoNotStepOver = CH_TXTATR_BREAKWORD == cFieldChr
+ ? rInf.GetIdx() - m_nFieldDiff - TextFrameIndex(1)
+ : TextFrameIndex(0);
+
+ if ( nLangIndex > nDoNotStepOver &&
+ TextFrameIndex(rInf.GetText().getLength()) == nLangIndex)
+ --nLangIndex;
+
+ while ( nLangIndex > nDoNotStepOver &&
+ !rCC.isLetterNumeric(rInf.GetText(), sal_Int32(nLangIndex)))
+ --nLangIndex;
+
+ // last "real" character is not inside our current portion
+ // we have to check the script type of the last "real" character
+ if ( nLangIndex < rInf.GetIdx() )
+ {
+ sal_uInt16 nScript = g_pBreakIt->GetRealScriptOfText( rInf.GetText(),
+ sal_Int32(nLangIndex));
+ OSL_ENSURE( nScript, "Script is not between 1 and 4" );
+
+ // compare current script with script from last "real" character
+ if ( SwFontScript(nScript - 1) != rInf.GetFont()->GetActual() )
+ {
+ aLang = rInf.GetTextFrame()->GetLangOfChar(
+ CH_TXTATR_BREAKWORD == cFieldChr
+ ? nDoNotStepOver
+ : nLangIndex,
+ nScript, true);
+ }
+ }
+ }
+
+ const ForbiddenCharacters aForbidden(
+ *rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().getForbiddenCharacters(aLang, true));
+
+ const bool bAllowHanging = rInf.IsHanging() && ! rInf.IsMulti() &&
+ ! rInf.GetTextFrame()->IsInTab() &&
+ ! rPor.InFieldGrp();
+
+ LineBreakUserOptions aUserOpt(
+ aForbidden.beginLine, aForbidden.endLine,
+ rInf.HasForbiddenChars(), bAllowHanging, false );
+
+ // !!! We must have a local copy of the locale, because inside
+ // getLineBreak the LinguEventListener can trigger a new formatting,
+ // which can corrupt the locale pointer inside pBreakIt.
+ const lang::Locale aLocale = g_pBreakIt->GetLocale( aLang );
+
+ // determines first possible line break from nCutPos to
+ // start index of current line
+ LineBreakResults aResult = g_pBreakIt->GetBreakIter()->getLineBreak(
+ rInf.GetText(), sal_Int32(m_nCutPos), aLocale,
+ sal_Int32(rInf.GetLineStart()), aHyphOpt, aUserOpt );
+
+ m_nBreakPos = TextFrameIndex(aResult.breakIndex);
+
+ // if we are formatting multi portions we want to allow line breaks
+ // at the border between single line and multi line portion
+ // we have to be careful with footnote portions, they always come in
+ // with an index 0
+ if ( m_nBreakPos < rInf.GetLineStart() && rInf.IsFirstMulti() &&
+ ! rInf.IsFootnoteInside() )
+ m_nBreakPos = rInf.GetLineStart();
+
+ m_nBreakStart = m_nBreakPos;
+
+ bHyph = BreakType::HYPHENATION == aResult.breakType;
+
+ if (bHyph && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
+ {
+ // found hyphenation position within line
+ // nBreakPos is set to the hyphenation position
+ m_xHyphWord = aResult.rHyphenatedWord;
+ m_nBreakPos += TextFrameIndex(m_xHyphWord->getHyphenationPos() + 1);
+
+ // if not in interactive mode, we have to break behind a soft hyphen
+ if ( ! rInf.IsInterHyph() && rInf.GetIdx() )
+ {
+ sal_Int32 const nSoftHyphPos =
+ m_xHyphWord->getWord().indexOf( CHAR_SOFTHYPHEN );
+
+ if ( nSoftHyphPos >= 0 &&
+ m_nBreakStart + TextFrameIndex(nSoftHyphPos) <= m_nBreakPos &&
+ m_nBreakPos > rInf.GetLineStart() )
+ m_nBreakPos = rInf.GetIdx() - TextFrameIndex(1);
+ }
+
+ if( m_nBreakPos >= rInf.GetIdx() )
+ {
+ nPorLen = m_nBreakPos - rInf.GetIdx();
+ if ('-' == rInf.GetText()[ sal_Int32(m_nBreakPos) - 1 ])
+ m_xHyphWord = nullptr;
+ }
+ }
+ else if ( !bHyph && m_nBreakPos >= rInf.GetLineStart() )
+ {
+ OSL_ENSURE(sal_Int32(m_nBreakPos) != COMPLETE_STRING, "we should have found a break pos");
+
+ // found break position within line
+ m_xHyphWord = nullptr;
+
+ // check, if break position is soft hyphen and an underflow
+ // has to be triggered
+ if( m_nBreakPos > rInf.GetLineStart() && rInf.GetIdx() &&
+ CHAR_SOFTHYPHEN == rInf.GetText()[ sal_Int32(m_nBreakPos) - 1 ])
+ {
+ m_nBreakPos = rInf.GetIdx() - TextFrameIndex(1);
+ }
+
+ if( rAdjust != SvxAdjust::Left )
+ {
+ // Delete any blanks at the end of a line, but be careful:
+ // If a field has been expanded, we do not want to delete any
+ // blanks inside the field portion. This would cause an unwanted
+ // underflow
+ TextFrameIndex nX = m_nBreakPos;
+ while( nX > rInf.GetLineStart() &&
+ ( CH_TXTATR_BREAKWORD != cFieldChr || nX > rInf.GetIdx() ) &&
+ ( CH_BLANK == rInf.GetChar( --nX ) ||
+ CH_SIX_PER_EM == rInf.GetChar( nX ) ||
+ CH_FULL_BLANK == rInf.GetChar( nX ) ) )
+ m_nBreakPos = nX;
+ }
+ if( m_nBreakPos > rInf.GetIdx() )
+ nPorLen = m_nBreakPos - rInf.GetIdx();
+ }
+ else
+ {
+ // no line break found, setting nBreakPos to COMPLETE_STRING
+ // causes a break cut
+ m_nBreakPos = TextFrameIndex(COMPLETE_STRING);
+ OSL_ENSURE( m_nCutPos >= rInf.GetIdx(), "Deep cut" );
+ nPorLen = m_nCutPos - rInf.GetIdx();
+ }
+
+ if (m_nBreakPos > m_nCutPos && m_nBreakPos != TextFrameIndex(COMPLETE_STRING))
+ {
+ const TextFrameIndex nHangingLen = m_nBreakPos - m_nCutPos;
+ SwPosSize aTmpSize = rInf.GetTextSize( &rSI, m_nCutPos, nHangingLen );
+ aTmpSize.Width(aTmpSize.Width() + nLeftRightBorderSpace);
+ OSL_ENSURE( !m_pHanging, "A hanging portion is hanging around" );
+ m_pHanging.reset( new SwHangingPortion( std::move(aTmpSize) ) );
+ m_pHanging->SetLen( nHangingLen );
+ nPorLen = m_nCutPos - rInf.GetIdx();
+ }
+
+ // If we expanded a field, we must repair the original string.
+ // In case we do not trigger an underflow, we correct the nBreakPos
+ // value, but we cannot correct the nBreakStart value:
+ // If we have found a hyphenation position, nBreakStart can lie before
+ // the field.
+ if ( CH_TXTATR_BREAKWORD == cFieldChr )
+ {
+ if ( m_nBreakPos < rInf.GetIdx() )
+ m_nBreakPos = nOldIdx - TextFrameIndex(1);
+ else if (TextFrameIndex(COMPLETE_STRING) != m_nBreakPos)
+ {
+ OSL_ENSURE( m_nBreakPos >= m_nFieldDiff, "I've got field trouble!" );
+ m_nBreakPos = m_nBreakPos - m_nFieldDiff;
+ }
+
+ OSL_ENSURE( m_nCutPos >= rInf.GetIdx() && m_nCutPos >= m_nFieldDiff,
+ "I've got field trouble, part2!" );
+ m_nCutPos = m_nCutPos - m_nFieldDiff;
+
+ OUString& rOldText = const_cast<OUString&> (rInf.GetText());
+ OUString aReplacement( cFieldChr );
+ rOldText = rOldText.replaceAt(sal_Int32(nOldIdx) - 1, sal_Int32(m_nFieldDiff) + 1, aReplacement);
+ rInf.SetIdx( nOldIdx );
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( aDebugString == rInf.GetText(),
+ "Somebody, somebody, somebody put something in my string" );
+#endif
+ }
+ }
+
+ if( nPorLen )
+ {
+ rInf.GetTextSize( &rSI, rInf.GetIdx(), nPorLen,
+ nMaxComp, m_nBreakWidth, nMaxSizeDiff,
+ rInf.GetCachedVclData().get() );
+
+ // save maximum width for later use
+ if ( nMaxSizeDiff )
+ rInf.SetMaxWidthDiff( &rPor, nMaxSizeDiff );
+
+ m_nBreakWidth += nItalic + nLeftRightBorderSpace;
+ }
+ else
+ m_nBreakWidth = 0;
+
+ if( m_pHanging )
+ {
+ m_nBreakPos = m_nCutPos;
+ // Keep following SwBreakPortion in the same line.
+ if ( CH_BREAK == rInf.GetChar( m_nBreakPos + m_pHanging->GetLen() ) )
+ return true;
+ }
+
+ return false;
+}
+
+// returns true if word at position nPos has a different spelling
+// if hyphenated at this position (old german spelling)
+bool SwTextGuess::AlternativeSpelling( const SwTextFormatInfo &rInf,
+ const TextFrameIndex nPos)
+{
+ // get word boundaries
+ Boundary aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rInf.GetText(), sal_Int32(nPos),
+ g_pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ),
+ WordType::DICTIONARY_WORD, true );
+ m_nBreakStart = TextFrameIndex(aBound.startPos);
+ sal_Int32 nWordLen = aBound.endPos - sal_Int32(m_nBreakStart);
+
+ // if everything else fails, we want to cut at nPos
+ m_nCutPos = nPos;
+
+ OUString const aText( rInf.GetText().copy(sal_Int32(m_nBreakStart), nWordLen) );
+
+ // check, if word has alternative spelling
+ Reference< XHyphenator > xHyph( ::GetHyphenator() );
+ OSL_ENSURE( xHyph.is(), "Hyphenator is missing");
+ //! subtract 1 since the UNO-interface is 0 based
+ m_xHyphWord = xHyph->queryAlternativeSpelling( aText,
+ g_pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ),
+ sal::static_int_cast<sal_Int16>(sal_Int32(nPos - m_nBreakStart)),
+ rInf.GetHyphValues() );
+ return m_xHyphWord.is() && m_xHyphWord->isAlternativeSpelling();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/guess.hxx b/sw/source/core/text/guess.hxx
new file mode 100644
index 000000000..696a09fc8
--- /dev/null
+++ b/sw/source/core/text/guess.hxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+#include <memory>
+
+#include "porrst.hxx"
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star::linguistic2 { class XHyphenatedWord; }
+
+class SwTextFormatInfo;
+
+class SwTextGuess
+{
+ css::uno::Reference< css::linguistic2::XHyphenatedWord > m_xHyphWord;
+ std::unique_ptr<SwHangingPortion> m_pHanging; // for hanging punctuation
+ TextFrameIndex m_nCutPos; // this character doesn't fit
+ TextFrameIndex m_nBreakStart; // start index of word containing line break
+ TextFrameIndex m_nBreakPos; // start index of break position
+ TextFrameIndex m_nFieldDiff; // absolute positions can be wrong if we
+ // a field in the text has been expanded
+ sal_uInt16 m_nBreakWidth; // width of the broken portion
+public:
+ SwTextGuess(): m_nCutPos(0), m_nBreakStart(0),
+ m_nBreakPos(0), m_nFieldDiff(0), m_nBreakWidth(0)
+ { }
+
+ // true, if current portion still fits to current line
+ bool Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
+ const sal_uInt16 nHeight );
+ bool AlternativeSpelling( const SwTextFormatInfo &rInf, const TextFrameIndex nPos );
+
+ SwHangingPortion* GetHangingPortion() const { return m_pHanging.get(); }
+ SwHangingPortion* ReleaseHangingPortion() { return m_pHanging.release(); }
+ sal_uInt16 BreakWidth() const { return m_nBreakWidth; }
+ TextFrameIndex CutPos() const { return m_nCutPos; }
+ TextFrameIndex BreakStart() const { return m_nBreakStart; }
+ TextFrameIndex BreakPos() const {return m_nBreakPos; }
+ TextFrameIndex FieldDiff() const {return m_nFieldDiff; }
+ const css::uno::Reference< css::linguistic2::XHyphenatedWord >& HyphWord() const
+ { return m_xHyphWord; }
+};
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx
new file mode 100644
index 000000000..38f665d68
--- /dev/null
+++ b/sw/source/core/text/inftxt.cxx
@@ -0,0 +1,2013 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
+
+#include <unotools/linguprops.hxx>
+#include <unotools/lingucfg.hxx>
+#include <hintids.hxx>
+#include <svl/ctloptions.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/printer.hxx>
+#include <sal/log.hxx>
+#include <editeng/hyphenzoneitem.hxx>
+#include <editeng/hngpnctitem.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <editeng/splwrap.hxx>
+#include <editeng/pgrditem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/shaditem.hxx>
+
+#include <SwSmartTagMgr.hxx>
+#include <breakit.hxx>
+#include <editeng/forbiddenruleitem.hxx>
+#include <swmodule.hxx>
+#include <vcl/svapp.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <frmtool.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <paratr.hxx>
+#include <rootfrm.hxx>
+#include "inftxt.hxx"
+#include <noteurl.hxx>
+#include "porftn.hxx"
+#include "porrst.hxx"
+#include "itratr.hxx"
+#include "portab.hxx"
+#include <wrong.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <numrule.hxx>
+#include <EnhancedPDFExportHelper.hxx>
+#include <docsh.hxx>
+#include <strings.hrc>
+#include <o3tl/deleter.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/gradient.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <formatlinebreak.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+#define CHAR_UNDERSCORE u'_'
+#define CHAR_LEFT_ARROW u'\x25C0'
+#define CHAR_RIGHT_ARROW u'\x25B6'
+#define CHAR_TAB u'\x2192'
+#define CHAR_TAB_RTL u'\x2190'
+#define CHAR_LINEBREAK u'\x21B5'
+#define CHAR_LINEBREAK_RTL u'\x21B3'
+
+#define DRAW_SPECIAL_OPTIONS_CENTER 1
+#define DRAW_SPECIAL_OPTIONS_ROTATE 2
+
+SwLineInfo::SwLineInfo()
+ : m_pSpace( nullptr ),
+ m_nVertAlign( SvxParaVertAlignItem::Align::Automatic ),
+ m_nDefTabStop( 0 ),
+ m_bListTabStopIncluded( false ),
+ m_nListTabStopPosition( 0 )
+{
+}
+
+SwLineInfo::~SwLineInfo()
+{
+}
+
+void SwLineInfo::CtorInitLineInfo( const SwAttrSet& rAttrSet,
+ const SwTextNode& rTextNode )
+{
+ m_oRuler.emplace( rAttrSet.GetTabStops() );
+ if ( rTextNode.GetListTabStopPosition( m_nListTabStopPosition ) )
+ {
+ m_bListTabStopIncluded = true;
+
+ // insert the list tab stop into SvxTabItem instance <pRuler>
+ const SvxTabStop aListTabStop( m_nListTabStopPosition,
+ SvxTabAdjust::Left );
+ m_oRuler->Insert( aListTabStop );
+
+ // remove default tab stops, which are before the inserted list tab stop
+ for ( sal_uInt16 i = 0; i < m_oRuler->Count(); i++ )
+ {
+ if ( (*m_oRuler)[i].GetTabPos() < m_nListTabStopPosition &&
+ (*m_oRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
+ {
+ m_oRuler->Remove(i);
+ continue;
+ }
+ }
+ }
+
+ if ( !rTextNode.getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
+ {
+ // remove default tab stop at position 0
+ for ( sal_uInt16 i = 0; i < m_oRuler->Count(); i++ )
+ {
+ if ( (*m_oRuler)[i].GetTabPos() == 0 &&
+ (*m_oRuler)[i].GetAdjustment() == SvxTabAdjust::Default )
+ {
+ m_oRuler->Remove(i);
+ break;
+ }
+ }
+ }
+
+ m_pSpace = &rAttrSet.GetLineSpacing();
+ m_nVertAlign = rAttrSet.GetParaVertAlign().GetValue();
+ m_nDefTabStop = USHRT_MAX;
+}
+
+void SwTextInfo::CtorInitTextInfo( SwTextFrame *pFrame )
+{
+ m_pPara = pFrame->GetPara();
+ m_nTextStart = pFrame->GetOffset();
+ if (!m_pPara)
+ {
+ SAL_WARN("sw.core", "+SwTextInfo::CTOR: missing paragraph information");
+ pFrame->Format(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+ m_pPara = pFrame->GetPara();
+ }
+}
+
+SwTextInfo::SwTextInfo( const SwTextInfo &rInf )
+ : m_pPara( const_cast<SwTextInfo&>(rInf).GetParaPortion() )
+ , m_nTextStart( rInf.GetTextStart() )
+{ }
+
+#if OSL_DEBUG_LEVEL > 0
+
+static void ChkOutDev( const SwTextSizeInfo &rInf )
+{
+ if ( !rInf.GetVsh() )
+ return;
+
+ const OutputDevice* pOut = rInf.GetOut();
+ const OutputDevice* pRef = rInf.GetRefDev();
+ OSL_ENSURE( pOut && pRef, "ChkOutDev: invalid output devices" );
+}
+#endif
+
+static TextFrameIndex GetMinLen( const SwTextSizeInfo &rInf )
+{
+ const TextFrameIndex nTextLen(rInf.GetText().getLength());
+ if (rInf.GetLen() == TextFrameIndex(COMPLETE_STRING))
+ return nTextLen;
+ const TextFrameIndex nInfLen = rInf.GetIdx() + rInf.GetLen();
+ return std::min(nTextLen, nInfLen);
+}
+
+SwTextSizeInfo::SwTextSizeInfo()
+: m_pKanaComp(nullptr)
+, m_pVsh(nullptr)
+, m_pOut(nullptr)
+, m_pRef(nullptr)
+, m_pFnt(nullptr)
+, m_pUnderFnt(nullptr)
+, m_pFrame(nullptr)
+, m_pOpt(nullptr)
+, m_pText(nullptr)
+, m_nIdx(0)
+, m_nLen(0)
+, m_nKanaIdx(0)
+, m_bOnWin (false)
+, m_bNotEOL (false)
+, m_bURLNotify(false)
+, m_bStopUnderflow(false)
+, m_bFootnoteInside(false)
+, m_bOtherThanFootnoteInside(false)
+, m_bMulti(false)
+, m_bFirstMulti(false)
+, m_bRuby(false)
+, m_bHanging(false)
+, m_bScriptSpace(false)
+, m_bForbiddenChars(false)
+, m_bSnapToGrid(false)
+, m_nDirection(0)
+{}
+
+SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew )
+ : SwTextInfo( rNew ),
+ m_pKanaComp(rNew.GetpKanaComp()),
+ m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
+ m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
+ m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
+ m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
+ m_pUnderFnt(rNew.GetUnderFnt()),
+ m_pFrame(rNew.m_pFrame),
+ m_pOpt(&rNew.GetOpt()),
+ m_pText(&rNew.GetText()),
+ m_nIdx(rNew.GetIdx()),
+ m_nLen(rNew.GetLen()),
+ m_nKanaIdx( rNew.GetKanaIdx() ),
+ m_bOnWin( rNew.OnWin() ),
+ m_bNotEOL( rNew.NotEOL() ),
+ m_bURLNotify( rNew.URLNotify() ),
+ m_bStopUnderflow( rNew.StopUnderflow() ),
+ m_bFootnoteInside( rNew.IsFootnoteInside() ),
+ m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
+ m_bMulti( rNew.IsMulti() ),
+ m_bFirstMulti( rNew.IsFirstMulti() ),
+ m_bRuby( rNew.IsRuby() ),
+ m_bHanging( rNew.IsHanging() ),
+ m_bScriptSpace( rNew.HasScriptSpace() ),
+ m_bForbiddenChars( rNew.HasForbiddenChars() ),
+ m_bSnapToGrid( rNew.SnapToGrid() ),
+ m_nDirection( rNew.GetDirection() )
+{
+#if OSL_DEBUG_LEVEL > 0
+ ChkOutDev( *this );
+#endif
+}
+
+void SwTextSizeInfo::CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
+ TextFrameIndex const nNewIdx)
+{
+ m_pKanaComp = nullptr;
+ m_nKanaIdx = 0;
+ m_pFrame = pFrame;
+ CtorInitTextInfo( m_pFrame );
+ SwDoc const& rDoc(m_pFrame->GetDoc());
+ m_pVsh = m_pFrame->getRootFrame()->GetCurrShell();
+
+ // Get the output and reference device
+ if ( m_pVsh )
+ {
+ m_pOut = pRenderContext;
+ m_pRef = &m_pVsh->GetRefDev();
+ m_bOnWin = m_pVsh->GetWin() || OUTDEV_WINDOW == m_pOut->GetOutDevType() || m_pVsh->isOutputToWindow();
+ }
+ else
+ {
+ // Access via StarONE. We do not need a Shell or an active one.
+ if (rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE))
+ {
+ // We can only pick the AppWin here? (there's nothing better to pick?)
+ m_pOut = Application::GetDefaultDevice();
+ }
+ else
+ m_pOut = rDoc.getIDocumentDeviceAccess().getPrinter(false);
+
+ m_pRef = m_pOut;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ ChkOutDev( *this );
+#endif
+
+ // Set default layout mode ( LTR or RTL ).
+ if ( m_pFrame->IsRightToLeft() )
+ {
+ m_pOut->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
+ m_pRef->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
+ m_nDirection = DIR_RIGHT2LEFT;
+ }
+ else
+ {
+ m_pOut->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong );
+ m_pRef->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiStrong );
+ m_nDirection = DIR_LEFT2RIGHT;
+ }
+
+ // The Options
+
+ m_pOpt = m_pVsh ?
+ m_pVsh->GetViewOptions() :
+ SW_MOD()->GetViewOption(rDoc.getIDocumentSettingAccess().get(DocumentSettingId::HTML_MODE)); // Options from Module, due to StarONE
+
+ // bURLNotify is set if MakeGraphic prepares it
+ // TODO: Unwind
+ m_bURLNotify = pNoteURL && !m_bOnWin;
+
+ SetSnapToGrid( m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue() &&
+ m_pFrame->IsInDocBody() );
+
+ m_pFnt = nullptr;
+ m_pUnderFnt = nullptr;
+ m_pText = &m_pFrame->GetText();
+
+ m_nIdx = nNewIdx;
+ m_nLen = TextFrameIndex(COMPLETE_STRING);
+ m_bNotEOL = false;
+ m_bStopUnderflow = m_bFootnoteInside = m_bOtherThanFootnoteInside = false;
+ m_bMulti = m_bFirstMulti = m_bRuby = m_bHanging = m_bScriptSpace =
+ m_bForbiddenChars = false;
+
+ SetLen( GetMinLen( *this ) );
+}
+
+SwTextSizeInfo::SwTextSizeInfo( const SwTextSizeInfo &rNew, const OUString* pText,
+ TextFrameIndex const nIndex)
+ : SwTextInfo( rNew ),
+ m_pKanaComp(rNew.GetpKanaComp()),
+ m_pVsh(const_cast<SwTextSizeInfo&>(rNew).GetVsh()),
+ m_pOut(const_cast<SwTextSizeInfo&>(rNew).GetOut()),
+ m_pRef(const_cast<SwTextSizeInfo&>(rNew).GetRefDev()),
+ m_pFnt(const_cast<SwTextSizeInfo&>(rNew).GetFont()),
+ m_pUnderFnt(rNew.GetUnderFnt()),
+ m_pFrame( rNew.m_pFrame ),
+ m_pOpt(&rNew.GetOpt()),
+ m_pText(pText),
+ m_nIdx(nIndex),
+ m_nLen(COMPLETE_STRING),
+ m_nKanaIdx( rNew.GetKanaIdx() ),
+ m_bOnWin( rNew.OnWin() ),
+ m_bNotEOL( rNew.NotEOL() ),
+ m_bURLNotify( rNew.URLNotify() ),
+ m_bStopUnderflow( rNew.StopUnderflow() ),
+ m_bFootnoteInside( rNew.IsFootnoteInside() ),
+ m_bOtherThanFootnoteInside( rNew.IsOtherThanFootnoteInside() ),
+ m_bMulti( rNew.IsMulti() ),
+ m_bFirstMulti( rNew.IsFirstMulti() ),
+ m_bRuby( rNew.IsRuby() ),
+ m_bHanging( rNew.IsHanging() ),
+ m_bScriptSpace( rNew.HasScriptSpace() ),
+ m_bForbiddenChars( rNew.HasForbiddenChars() ),
+ m_bSnapToGrid( rNew.SnapToGrid() ),
+ m_nDirection( rNew.GetDirection() )
+{
+#if OSL_DEBUG_LEVEL > 0
+ ChkOutDev( *this );
+#endif
+ SetLen( GetMinLen( *this ) );
+}
+
+SwTextSizeInfo::SwTextSizeInfo(SwTextFrame *const pTextFrame,
+ TextFrameIndex const nIndex)
+ : m_bOnWin(false)
+{
+ CtorInitTextSizeInfo( pTextFrame->getRootFrame()->GetCurrShell()->GetOut(), pTextFrame, nIndex );
+}
+
+void SwTextSizeInfo::SelectFont()
+{
+ // The path needs to go via ChgPhysFnt or the FontMetricCache gets confused.
+ // In this case pLastMet has it's old value.
+ // Wrong: GetOut()->SetFont( GetFont()->GetFnt() );
+ GetFont()->Invalidate();
+ GetFont()->ChgPhysFnt( m_pVsh, *GetOut() );
+}
+
+void SwTextSizeInfo::NoteAnimation() const
+{
+ if( OnWin() )
+ SwRootFrame::FlushVout();
+
+ OSL_ENSURE( m_pOut == m_pVsh->GetOut(),
+ "SwTextSizeInfo::NoteAnimation() changed m_pOut" );
+}
+
+SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
+ const SwScriptInfo* pSI,
+ const OUString& rText,
+ const TextFrameIndex nIndex,
+ const TextFrameIndex nLength) const
+{
+ SwDrawTextInfo aDrawInf( m_pVsh, *pOutDev, pSI, rText, nIndex, nLength );
+ aDrawInf.SetFrame( m_pFrame );
+ aDrawInf.SetFont( m_pFnt );
+ aDrawInf.SetSnapToGrid( SnapToGrid() );
+ aDrawInf.SetKanaComp( 0 );
+ return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
+}
+
+SwPosSize SwTextSizeInfo::GetTextSize() const
+{
+ const SwScriptInfo& rSI =
+ const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
+
+ // in some cases, compression is not allowed or suppressed for
+ // performance reasons
+ sal_uInt16 nComp =( SwFontScript::CJK == GetFont()->GetActual() &&
+ rSI.CountCompChg() &&
+ ! IsMulti() ) ?
+ GetKanaComp() :
+ 0 ;
+
+ SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
+ aDrawInf.SetFrame( m_pFrame );
+ aDrawInf.SetFont( m_pFnt );
+ aDrawInf.SetSnapToGrid( SnapToGrid() );
+ aDrawInf.SetKanaComp( nComp );
+ return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
+}
+
+void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex nIndex,
+ const TextFrameIndex nLength, const sal_uInt16 nComp,
+ sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
+ vcl::text::TextLayoutCache const*const pCache) const
+{
+ SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
+ 0, false, pCache);
+ aDrawInf.SetFrame( m_pFrame );
+ aDrawInf.SetFont( m_pFnt );
+ aDrawInf.SetSnapToGrid( SnapToGrid() );
+ aDrawInf.SetKanaComp( nComp );
+ SwPosSize aSize( m_pFnt->GetTextSize_( aDrawInf ) );
+ nMaxSizeDiff = o3tl::narrowing<sal_uInt16>(aDrawInf.GetKanaDiff());
+ nMinSize = aSize.Width();
+}
+
+TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
+ const TextFrameIndex nMaxLen,
+ const sal_uInt16 nComp,
+ vcl::text::TextLayoutCache const*const pCache) const
+{
+ const SwScriptInfo& rScriptInfo =
+ const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
+
+ OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
+ SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
+ *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
+ aDrawInf.SetFrame( m_pFrame );
+ aDrawInf.SetFont( m_pFnt );
+ aDrawInf.SetSnapToGrid( SnapToGrid() );
+ aDrawInf.SetKanaComp( nComp );
+ aDrawInf.SetHyphPos( nullptr );
+
+ return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
+}
+
+TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
+ const TextFrameIndex nMaxLen,
+ const sal_uInt16 nComp,
+ TextFrameIndex& rExtraCharPos,
+ vcl::text::TextLayoutCache const*const pCache) const
+{
+ const SwScriptInfo& rScriptInfo =
+ const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
+
+ OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
+ SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
+ *m_pText, GetIdx(), nMaxLen, 0, false, pCache );
+ aDrawInf.SetFrame( m_pFrame );
+ aDrawInf.SetFont( m_pFnt );
+ aDrawInf.SetSnapToGrid( SnapToGrid() );
+ aDrawInf.SetKanaComp( nComp );
+ aDrawInf.SetHyphPos( &rExtraCharPos );
+
+ return m_pFnt->GetTextBreak( aDrawInf, nLineWidth );
+}
+
+bool SwTextSizeInfo::HasHint(TextFrameIndex const nPos) const
+{
+ std::pair<SwTextNode const*, sal_Int32> const pos(m_pFrame->MapViewToModel(nPos));
+ return pos.first->GetTextAttrForCharAt(pos.second);
+}
+
+void SwTextPaintInfo::CtorInitTextPaintInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame, const SwRect &rPaint )
+{
+ CtorInitTextSizeInfo( pRenderContext, pFrame, TextFrameIndex(0) );
+ m_aTextFly.CtorInitTextFly( pFrame );
+ m_aPaintRect = rPaint;
+ m_nSpaceIdx = 0;
+ m_pSpaceAdd = nullptr;
+ m_pWrongList = nullptr;
+ m_pGrammarCheckList = nullptr;
+ m_pSmartTags = nullptr;
+ m_pBrushItem = nullptr;
+}
+
+SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* pText )
+ : SwTextSizeInfo( rInf, pText )
+ , m_pWrongList( rInf.GetpWrongList() )
+ , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
+ , m_pSmartTags( rInf.GetSmartTags() )
+ , m_pSpaceAdd( rInf.GetpSpaceAdd() ),
+ m_pBrushItem( rInf.GetBrushItem() ),
+ m_aTextFly( rInf.GetTextFly() ),
+ m_aPos( rInf.GetPos() ),
+ m_aPaintRect( rInf.GetPaintRect() ),
+ m_nSpaceIdx( rInf.GetSpaceIdx() )
+{ }
+
+SwTextPaintInfo::SwTextPaintInfo( const SwTextPaintInfo &rInf )
+ : SwTextSizeInfo( rInf )
+ , m_pWrongList( rInf.GetpWrongList() )
+ , m_pGrammarCheckList( rInf.GetGrammarCheckList() )
+ , m_pSmartTags( rInf.GetSmartTags() )
+ , m_pSpaceAdd( rInf.GetpSpaceAdd() ),
+ m_pBrushItem( rInf.GetBrushItem() ),
+ m_aTextFly( rInf.GetTextFly() ),
+ m_aPos( rInf.GetPos() ),
+ m_aPaintRect( rInf.GetPaintRect() ),
+ m_nSpaceIdx( rInf.GetSpaceIdx() )
+{ }
+
+SwTextPaintInfo::SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint )
+{
+ CtorInitTextPaintInfo( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, rPaint );
+}
+
+namespace
+{
+/**
+ * Context class that captures the draw operations on rDrawInf's output device for transparency
+ * purposes.
+ */
+class SwTransparentTextGuard
+{
+ ScopedVclPtrInstance<VirtualDevice> m_aContentVDev;
+ GDIMetaFile m_aContentMetafile;
+ MapMode m_aNewMapMode;
+ SwRect m_aPorRect;
+ SwTextPaintInfo& m_rPaintInf;
+ SwDrawTextInfo& m_rDrawInf;
+
+public:
+ SwTransparentTextGuard(const SwLinePortion& rPor, SwTextPaintInfo& rPaintInf,
+ SwDrawTextInfo& rDrawInf);
+ ~SwTransparentTextGuard();
+};
+
+SwTransparentTextGuard::SwTransparentTextGuard(const SwLinePortion& rPor,
+ SwTextPaintInfo& rPaintInf, SwDrawTextInfo& rDrawInf)
+ : m_aNewMapMode(rPaintInf.GetOut()->GetMapMode())
+ , m_rPaintInf(rPaintInf)
+ , m_rDrawInf(rDrawInf)
+{
+ rPaintInf.CalcRect(rPor, &m_aPorRect);
+ rDrawInf.SetOut(*m_aContentVDev);
+ m_aContentVDev->SetMapMode(rPaintInf.GetOut()->GetMapMode());
+ m_aContentMetafile.Record(m_aContentVDev.get());
+ m_aContentVDev->SetLineColor(rPaintInf.GetOut()->GetLineColor());
+ m_aContentVDev->SetFillColor(rPaintInf.GetOut()->GetFillColor());
+ m_aContentVDev->SetFont(rPaintInf.GetOut()->GetFont());
+ m_aContentVDev->SetDrawMode(rPaintInf.GetOut()->GetDrawMode());
+ m_aContentVDev->SetSettings(rPaintInf.GetOut()->GetSettings());
+ m_aContentVDev->SetRefPoint(rPaintInf.GetOut()->GetRefPoint());
+}
+
+SwTransparentTextGuard::~SwTransparentTextGuard()
+{
+ m_aContentMetafile.Stop();
+ m_aContentMetafile.WindStart();
+ m_aNewMapMode.SetOrigin(m_aPorRect.TopLeft());
+ m_aContentMetafile.SetPrefMapMode(m_aNewMapMode);
+ m_aContentMetafile.SetPrefSize(m_aPorRect.SSize());
+ m_rDrawInf.SetOut(*m_rPaintInf.GetOut());
+ Gradient aVCLGradient;
+ sal_uInt8 nTransPercentVcl = 255 - m_rPaintInf.GetFont()->GetColor().GetAlpha();
+ const Color aTransColor(nTransPercentVcl, nTransPercentVcl, nTransPercentVcl);
+ aVCLGradient.SetStyle(GradientStyle::Linear);
+ aVCLGradient.SetStartColor(aTransColor);
+ aVCLGradient.SetEndColor(aTransColor);
+ aVCLGradient.SetAngle(0_deg10);
+ aVCLGradient.SetBorder(0);
+ aVCLGradient.SetOfsX(0);
+ aVCLGradient.SetOfsY(0);
+ aVCLGradient.SetStartIntensity(100);
+ aVCLGradient.SetEndIntensity(100);
+ aVCLGradient.SetSteps(2);
+ m_rPaintInf.GetOut()->DrawTransparent(m_aContentMetafile, m_aPorRect.TopLeft(),
+ m_aPorRect.SSize(), aVCLGradient);
+}
+}
+
+void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPor,
+ TextFrameIndex const nStart, TextFrameIndex const nLength,
+ const bool bKern, const bool bWrong,
+ const bool bSmartTag,
+ const bool bGrammarCheck )
+{
+ if( !nLength )
+ return;
+
+ // The SwScriptInfo is useless if we are inside a field portion
+ SwScriptInfo* pSI = nullptr;
+ if ( ! rPor.InFieldGrp() )
+ pSI = &GetParaPortion()->GetScriptInfo();
+
+ // in some cases, kana compression is not allowed or suppressed for
+ // performance reasons
+ sal_uInt16 nComp = 0;
+ if ( ! IsMulti() )
+ nComp = GetKanaComp();
+
+ bool bCfgIsAutoGrammar = false;
+ SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_AUTO ) >>= bCfgIsAutoGrammar;
+ const bool bBullet = OnWin() && GetOpt().IsBlank() && IsNoSymbol();
+ const bool bTmpWrong = bWrong && OnWin() && GetOpt().IsOnlineSpell();
+ const bool bTmpGrammarCheck = bGrammarCheck && OnWin() && bCfgIsAutoGrammar && GetOpt().IsOnlineSpell();
+ const bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled();
+
+ OSL_ENSURE( GetParaPortion(), "No paragraph!");
+ SwDrawTextInfo aDrawInf( m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart, nLength,
+ rPor.Width(), bBullet );
+
+ aDrawInf.SetUnderFnt( m_pUnderFnt );
+
+ const tools::Long nSpaceAdd = ( rPor.IsBlankPortion() || rPor.IsDropPortion() ||
+ rPor.InNumberGrp() ) ? 0 : GetSpaceAdd();
+ if ( nSpaceAdd )
+ {
+ TextFrameIndex nCharCnt(0);
+ // #i41860# Thai justified alignment needs some
+ // additional information:
+ aDrawInf.SetNumberOfBlanks( rPor.InTextGrp() ?
+ static_cast<const SwTextPortion&>(rPor).GetSpaceCnt( *this, nCharCnt ) :
+ TextFrameIndex(0) );
+ }
+
+ aDrawInf.SetSpace( nSpaceAdd );
+ aDrawInf.SetKanaComp( nComp );
+
+ // the font is used to identify the current script via nActual
+ aDrawInf.SetFont( m_pFnt );
+ // the frame is used to identify the orientation
+ aDrawInf.SetFrame( GetTextFrame() );
+ // we have to know if the paragraph should snap to grid
+ aDrawInf.SetSnapToGrid( SnapToGrid() );
+ // for underlining we must know when not to add extra space behind
+ // a character in justified mode
+ aDrawInf.SetSpaceStop( ! rPor.GetNextPortion() ||
+ rPor.GetNextPortion()->InFixMargGrp() ||
+ rPor.GetNextPortion()->IsHolePortion() );
+
+ // Draw text next to the left border
+ Point aFontPos(m_aPos);
+ if( m_pFnt->GetLeftBorder() && rPor.InTextGrp() && !static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev() )
+ {
+ const sal_uInt16 nLeftBorderSpace = m_pFnt->GetLeftBorderSpace();
+ if ( GetTextFrame()->IsRightToLeft() )
+ {
+ aFontPos.AdjustX( -nLeftBorderSpace );
+ }
+ else
+ {
+ switch( m_pFnt->GetOrientation(GetTextFrame()->IsVertical()).get() )
+ {
+ case 0 :
+ aFontPos.AdjustX(nLeftBorderSpace );
+ break;
+ case 900 :
+ aFontPos.AdjustY( -nLeftBorderSpace );
+ break;
+ case 1800 :
+ aFontPos.AdjustX( -nLeftBorderSpace );
+ break;
+ case 2700 :
+ aFontPos.AdjustY(nLeftBorderSpace );
+ break;
+ }
+ }
+ if( aFontPos.X() < 0 )
+ aFontPos.setX( 0 );
+ if( aFontPos.Y() < 0 )
+ aFontPos.setY( 0 );
+ }
+
+ // Handle semi-transparent text if necessary.
+ std::unique_ptr<SwTransparentTextGuard, o3tl::default_delete<SwTransparentTextGuard>> pTransparentText;
+ if (m_pFnt->GetColor() != COL_AUTO && m_pFnt->GetColor().IsTransparent())
+ {
+ pTransparentText.reset(new SwTransparentTextGuard(rPor, *this, aDrawInf));
+ }
+
+ if( GetTextFly().IsOn() )
+ {
+ // aPos needs to be the TopLeft, because we cannot calculate the
+ // ClipRects otherwise
+ const Point aPoint( aFontPos.X(), aFontPos.Y() - rPor.GetAscent() );
+ const Size aSize( rPor.Width(), rPor.Height() );
+ aDrawInf.SetPos( aPoint );
+ aDrawInf.SetSize( aSize );
+ aDrawInf.SetAscent( rPor.GetAscent() );
+ aDrawInf.SetKern( bKern ? rPor.Width() : 0 );
+ aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
+ aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
+ aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
+ GetTextFly().DrawTextOpaque( aDrawInf );
+ }
+ else
+ {
+ aDrawInf.SetPos( aFontPos );
+ if( bKern )
+ m_pFnt->DrawStretchText_( aDrawInf );
+ else
+ {
+ aDrawInf.SetWrong( bTmpWrong ? m_pWrongList : nullptr );
+ aDrawInf.SetGrammarCheck( bTmpGrammarCheck ? m_pGrammarCheckList : nullptr );
+ aDrawInf.SetSmartTags( bTmpSmart ? m_pSmartTags : nullptr );
+ m_pFnt->DrawText_( aDrawInf );
+ }
+ }
+}
+
+void SwTextPaintInfo::CalcRect( const SwLinePortion& rPor,
+ SwRect* pRect, SwRect* pIntersect,
+ const bool bInsideBox ) const
+{
+ Size aSize( rPor.Width(), rPor.Height() );
+ if( rPor.IsHangingPortion() )
+ aSize.setWidth( static_cast<const SwHangingPortion&>(rPor).GetInnerWidth() );
+ if( rPor.InSpaceGrp() && GetSpaceAdd() )
+ {
+ SwTwips nAdd = rPor.CalcSpacing( GetSpaceAdd(), *this );
+ if( rPor.InFieldGrp() && GetSpaceAdd() < 0 && nAdd )
+ nAdd += GetSpaceAdd() / SPACING_PRECISION_FACTOR;
+ aSize.AdjustWidth(nAdd );
+ }
+
+ Point aPoint;
+
+ if( IsRotated() )
+ {
+ tools::Long nTmp = aSize.Width();
+ aSize.setWidth( aSize.Height() );
+ aSize.setHeight( nTmp );
+ if ( 1 == GetDirection() )
+ {
+ aPoint.setX( X() - rPor.GetAscent() );
+ aPoint.setY( Y() - aSize.Height() );
+ }
+ else
+ {
+ aPoint.setX( X() - rPor.Height() + rPor.GetAscent() );
+ aPoint.setY( Y() );
+ }
+ }
+ else
+ {
+ aPoint.setX( X() );
+ if (GetTextFrame()->IsVertLR() && !GetTextFrame()->IsVertLRBT())
+ aPoint.setY( Y() - rPor.Height() + rPor.GetAscent() );
+ else
+ aPoint.setY( Y() - rPor.GetAscent() );
+ }
+
+ // Adjust x coordinate if we are inside a bidi portion
+ const bool bFrameDir = GetTextFrame()->IsRightToLeft();
+ const bool bCounterDir = ( !bFrameDir && DIR_RIGHT2LEFT == GetDirection() ) ||
+ ( bFrameDir && DIR_LEFT2RIGHT == GetDirection() );
+
+ if ( bCounterDir )
+ aPoint.AdjustX( -(aSize.Width()) );
+
+ SwRect aRect( aPoint, aSize );
+
+ if ( GetTextFrame()->IsRightToLeft() )
+ GetTextFrame()->SwitchLTRtoRTL( aRect );
+
+ if ( GetTextFrame()->IsVertical() )
+ GetTextFrame()->SwitchHorizontalToVertical( aRect );
+
+ if( bInsideBox && rPor.InTextGrp() )
+ {
+ const bool bJoinWithPrev =
+ static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithPrev();
+ const bool bJoinWithNext =
+ static_cast<const SwTextPortion&>(rPor).GetJoinBorderWithNext();
+ const bool bIsVert = GetTextFrame()->IsVertical();
+ const bool bIsVertLRBT = GetTextFrame()->IsVertLRBT();
+ aRect.AddTop( GetFont()->CalcShadowSpace(SvxShadowItemSide::TOP, bIsVert, bIsVertLRBT,
+ bJoinWithPrev, bJoinWithNext));
+ aRect.AddBottom( - GetFont()->CalcShadowSpace(SvxShadowItemSide::BOTTOM, bIsVert, bIsVertLRBT,
+ bJoinWithPrev, bJoinWithNext));
+ aRect.AddLeft( GetFont()->CalcShadowSpace(SvxShadowItemSide::LEFT, bIsVert, bIsVertLRBT,
+ bJoinWithPrev, bJoinWithNext));
+ aRect.AddRight( - GetFont()->CalcShadowSpace(SvxShadowItemSide::RIGHT, bIsVert, bIsVertLRBT,
+ bJoinWithPrev, bJoinWithNext));
+ }
+
+ if ( pRect )
+ *pRect = aRect;
+
+ if( aRect.HasArea() && pIntersect )
+ {
+ ::SwAlignRect( aRect, GetVsh(), GetOut() );
+
+ if ( GetOut()->IsClipRegion() )
+ {
+ SwRect aClip( GetOut()->GetClipRegion().GetBoundRect() );
+ aRect.Intersection( aClip );
+ }
+
+ *pIntersect = aRect;
+ }
+}
+
+/**
+ * Draws a special portion
+ * E.g.: line break portion, tab portion
+ *
+ * @param rPor The portion
+ * @param rRect The rectangle surrounding the character
+ * @param rCol Specify a color for the character
+ * @param bCenter Draw the character centered, otherwise left aligned
+ * @param bRotate Rotate the character if character rotation is set
+ */
+static void lcl_DrawSpecial( const SwTextPaintInfo& rTextPaintInfo, const SwLinePortion& rPor,
+ SwRect& rRect, const Color& rCol, sal_Unicode cChar,
+ sal_uInt8 nOptions )
+{
+ bool bCenter = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_CENTER );
+ bool bRotate = 0 != ( nOptions & DRAW_SPECIAL_OPTIONS_ROTATE );
+
+ // rRect is given in absolute coordinates
+ if ( rTextPaintInfo.GetTextFrame()->IsRightToLeft() )
+ rTextPaintInfo.GetTextFrame()->SwitchRTLtoLTR( rRect );
+ if ( rTextPaintInfo.GetTextFrame()->IsVertical() )
+ rTextPaintInfo.GetTextFrame()->SwitchVerticalToHorizontal( rRect );
+
+ const SwFont* pOldFnt = rTextPaintInfo.GetFont();
+
+ // Font is generated only once:
+ static SwFont s_aFnt = [&]()
+ {
+ SwFont tmp( *pOldFnt );
+ tmp.SetFamily( FAMILY_DONTKNOW, tmp.GetActual() );
+ tmp.SetName( numfunc::GetDefBulletFontname(), tmp.GetActual() );
+ tmp.SetStyleName(OUString(), tmp.GetActual());
+ tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL, tmp.GetActual() );
+ return tmp;
+ }();
+
+ // Some of the current values are set at the font:
+ if ( ! bRotate )
+ s_aFnt.SetVertical( 0_deg10, rTextPaintInfo.GetTextFrame()->IsVertical() );
+ else
+ s_aFnt.SetVertical( pOldFnt->GetOrientation() );
+
+ s_aFnt.SetColor(rCol);
+
+ Size aFontSize( 0, SPECIAL_FONT_HEIGHT );
+ s_aFnt.SetSize( aFontSize, s_aFnt.GetActual() );
+
+ SwTextPaintInfo& rNonConstTextPaintInfo = const_cast<SwTextPaintInfo&>(rTextPaintInfo);
+
+ rNonConstTextPaintInfo.SetFont( &s_aFnt );
+
+ // The maximum width depends on the current orientation
+ const Degree10 nDir = s_aFnt.GetOrientation( rTextPaintInfo.GetTextFrame()->IsVertical() );
+ SwTwips nMaxWidth;
+ if (nDir == 900_deg10 || nDir == 2700_deg10)
+ nMaxWidth = rRect.Height();
+ else
+ {
+ assert(nDir == 0_deg10); //Unknown direction set at font
+ nMaxWidth = rRect.Width();
+ }
+
+ // check if char fits into rectangle
+ const OUString aTmp( cChar );
+ aFontSize = rTextPaintInfo.GetTextSize( aTmp ).SvLSize();
+ while ( aFontSize.Width() > nMaxWidth )
+ {
+ SwTwips nFactor = ( 100 * aFontSize.Width() ) / nMaxWidth;
+ const SwTwips nOldWidth = aFontSize.Width();
+
+ // new height for font
+ const SwFontScript nAct = s_aFnt.GetActual();
+ aFontSize.setHeight( ( 100 * s_aFnt.GetSize( nAct ).Height() ) / nFactor );
+ aFontSize.setWidth( ( 100 * s_aFnt.GetSize( nAct).Width() ) / nFactor );
+
+ if ( !aFontSize.Width() && !aFontSize.Height() )
+ break;
+
+ s_aFnt.SetSize( aFontSize, nAct );
+
+ aFontSize = rTextPaintInfo.GetTextSize( aTmp ).SvLSize();
+
+ if ( aFontSize.Width() >= nOldWidth )
+ break;
+ }
+
+ const Point aOldPos( rTextPaintInfo.GetPos() );
+
+ // adjust values so that tab is vertically and horizontally centered
+ SwTwips nX = rRect.Left();
+ SwTwips nY = rRect.Top();
+ switch ( nDir.get() )
+ {
+ case 0 :
+ if ( bCenter )
+ nX += ( rRect.Width() - aFontSize.Width() ) / 2;
+ nY += ( rRect.Height() - aFontSize.Height() ) / 2 + rTextPaintInfo.GetAscent();
+ break;
+ case 900 :
+ if ( bCenter )
+ nX += ( rRect.Width() - aFontSize.Height() ) / 2 + rTextPaintInfo.GetAscent();
+ nY += ( rRect.Height() + aFontSize.Width() ) / 2;
+ break;
+ case 2700 :
+ if ( bCenter )
+ nX += ( rRect.Width() + aFontSize.Height() ) / 2 - rTextPaintInfo.GetAscent();
+ nY += ( rRect.Height() - aFontSize.Width() ) / 2;
+ break;
+ }
+
+ Point aTmpPos( nX, nY );
+ rNonConstTextPaintInfo.SetPos( aTmpPos );
+ sal_uInt16 nOldWidth = rPor.Width();
+ const_cast<SwLinePortion&>(rPor).Width( o3tl::narrowing<sal_uInt16>(aFontSize.Width()) );
+ rTextPaintInfo.DrawText( aTmp, rPor );
+ const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
+ rNonConstTextPaintInfo.SetFont( const_cast<SwFont*>(pOldFnt) );
+ rNonConstTextPaintInfo.SetPos( aOldPos );
+}
+
+void SwTextPaintInfo::DrawRect( const SwRect &rRect, bool bRetouche ) const
+{
+ if ( OnWin() || !bRetouche )
+ {
+ if( m_aTextFly.IsOn() )
+ const_cast<SwTextPaintInfo*>(this)->GetTextFly().
+ DrawFlyRect( m_pOut, rRect );
+ else
+ m_pOut->DrawRect( rRect.SVRect() );
+ }
+}
+
+void SwTextPaintInfo::DrawTab( const SwLinePortion &rPor ) const
+{
+ if( !OnWin() )
+ return;
+
+ SwRect aRect;
+ CalcRect( rPor, &aRect );
+
+ if ( ! aRect.HasArea() )
+ return;
+
+ const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ? CHAR_TAB_RTL : CHAR_TAB;
+ const sal_uInt8 nOptions = DRAW_SPECIAL_OPTIONS_CENTER | DRAW_SPECIAL_OPTIONS_ROTATE;
+
+ lcl_DrawSpecial( *this, rPor, aRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
+}
+
+void SwTextPaintInfo::DrawLineBreak( const SwLinePortion &rPor ) const
+{
+ if( !OnWin() )
+ return;
+
+ SwLineBreakClear eClear = SwLineBreakClear::NONE;
+ if (rPor.IsBreakPortion())
+ {
+ const auto& rBreakPortion = static_cast<const SwBreakPortion&>(rPor);
+ eClear = rBreakPortion.GetClear();
+ }
+
+ sal_uInt16 nOldWidth = rPor.Width();
+ const_cast<SwLinePortion&>(rPor).Width( LINE_BREAK_WIDTH );
+
+ SwRect aRect;
+ CalcRect( rPor, &aRect );
+
+ if( aRect.HasArea() )
+ {
+ const sal_Unicode cChar = GetTextFrame()->IsRightToLeft() ?
+ CHAR_LINEBREAK_RTL : CHAR_LINEBREAK;
+ const sal_uInt8 nOptions = 0;
+
+ SwRect aTextRect(aRect);
+ if (eClear == SwLineBreakClear::LEFT || eClear == SwLineBreakClear::ALL)
+ aTextRect.AddLeft(30);
+ if (eClear == SwLineBreakClear::RIGHT || eClear == SwLineBreakClear::ALL)
+ aTextRect.AddRight(-30);
+ lcl_DrawSpecial( *this, rPor, aTextRect, NON_PRINTING_CHARACTER_COLOR, cChar, nOptions );
+
+ if (eClear != SwLineBreakClear::NONE)
+ {
+ // Paint indicator if this clear is left/right/all.
+ m_pOut->Push(vcl::PushFlags::LINECOLOR);
+ m_pOut->SetLineColor(NON_PRINTING_CHARACTER_COLOR);
+ if (eClear != SwLineBreakClear::RIGHT)
+ m_pOut->DrawLine(aRect.BottomLeft(), aRect.TopLeft());
+ if (eClear != SwLineBreakClear::LEFT)
+ m_pOut->DrawLine(aRect.BottomRight(), aRect.TopRight());
+ m_pOut->Pop();
+ }
+ }
+
+ const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
+}
+
+void SwTextPaintInfo::DrawRedArrow( const SwLinePortion &rPor ) const
+{
+ Size aSize( SPECIAL_FONT_HEIGHT, SPECIAL_FONT_HEIGHT );
+ SwRect aRect( static_cast<const SwArrowPortion&>(rPor).GetPos(), aSize );
+ sal_Unicode cChar;
+ if( static_cast<const SwArrowPortion&>(rPor).IsLeft() )
+ {
+ aRect.Pos().AdjustY(20 - GetAscent() );
+ aRect.Pos().AdjustX(20 );
+ if( aSize.Height() > rPor.Height() )
+ aRect.Height( rPor.Height() );
+ cChar = CHAR_LEFT_ARROW;
+ }
+ else
+ {
+ if( aSize.Height() > rPor.Height() )
+ aRect.Height( rPor.Height() );
+ aRect.Pos().AdjustY( -(aRect.Height() + 20) );
+ aRect.Pos().AdjustX( -(aRect.Width() + 20) );
+ cChar = CHAR_RIGHT_ARROW;
+ }
+
+ if ( GetTextFrame()->IsVertical() )
+ GetTextFrame()->SwitchHorizontalToVertical( aRect );
+
+ if( aRect.HasArea() )
+ {
+ const sal_uInt8 nOptions = 0;
+ lcl_DrawSpecial( *this, rPor, aRect, COL_LIGHTRED, cChar, nOptions );
+ }
+}
+
+void SwTextPaintInfo::DrawPostIts( bool bScript ) const
+{
+ if( !OnWin() || !m_pOpt->IsPostIts() )
+ return;
+
+ Size aSize;
+ Point aTmp;
+
+ const sal_uInt16 nPostItsWidth = SwViewOption::GetPostItsWidth( GetOut() );
+ const sal_uInt16 nFontHeight = m_pFnt->GetHeight( m_pVsh, *GetOut() );
+ const sal_uInt16 nFontAscent = m_pFnt->GetAscent( m_pVsh, *GetOut() );
+
+ switch ( m_pFnt->GetOrientation( GetTextFrame()->IsVertical() ).get() )
+ {
+ case 0 :
+ aSize.setWidth( nPostItsWidth );
+ aSize.setHeight( nFontHeight );
+ aTmp.setX( m_aPos.X() );
+ aTmp.setY( m_aPos.Y() - nFontAscent );
+ break;
+ case 900 :
+ aSize.setHeight( nPostItsWidth );
+ aSize.setWidth( nFontHeight );
+ aTmp.setX( m_aPos.X() - nFontAscent );
+ aTmp.setY( m_aPos.Y() );
+ break;
+ case 2700 :
+ aSize.setHeight( nPostItsWidth );
+ aSize.setWidth( nFontHeight );
+ aTmp.setX( m_aPos.X() - nFontHeight +
+ nFontAscent );
+ aTmp.setY( m_aPos.Y() );
+ break;
+ }
+
+ SwRect aTmpRect( aTmp, aSize );
+
+ if ( GetTextFrame()->IsRightToLeft() )
+ GetTextFrame()->SwitchLTRtoRTL( aTmpRect );
+
+ if ( GetTextFrame()->IsVertical() )
+ GetTextFrame()->SwitchHorizontalToVertical( aTmpRect );
+
+ SwViewOption::PaintPostIts( const_cast<OutputDevice*>(GetOut()), aTmpRect, bScript );
+
+}
+
+void SwTextPaintInfo::DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const
+{
+ SwRect aIntersect;
+ CalcRect( rPor, &aIntersect );
+ if ( !aIntersect.HasArea() )
+ return;
+
+ if (OnWin() && SwViewOption::IsFieldShadings() &&
+ !GetOpt().IsPagePreview())
+ {
+ OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
+ pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
+ pOut->SetLineColor();
+ pOut->DrawRect( aIntersect.SVRect() );
+ pOut->Pop();
+ }
+ const int delta=10;
+ tools::Rectangle r(aIntersect.Left()+delta, aIntersect.Top()+delta, aIntersect.Right()-delta, aIntersect.Bottom()-delta);
+ m_pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ m_pOut->SetLineColor( Color(0, 0, 0));
+ m_pOut->SetFillColor();
+ m_pOut->DrawRect( r );
+ if (bChecked)
+ {
+ m_pOut->DrawLine(r.TopLeft(), r.BottomRight());
+ m_pOut->DrawLine(r.TopRight(), r.BottomLeft());
+ }
+ m_pOut->Pop();
+}
+
+void SwTextPaintInfo::DrawBackground( const SwLinePortion &rPor, const Color *pColor ) const
+{
+ OSL_ENSURE( OnWin(), "SwTextPaintInfo::DrawBackground: printer pollution ?" );
+
+ SwRect aIntersect;
+ CalcRect( rPor, nullptr, &aIntersect, true );
+
+ if ( !aIntersect.HasArea() )
+ return;
+
+ OutputDevice* pOut = const_cast<OutputDevice*>(GetOut());
+ pOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+
+ if ( pColor )
+ pOut->SetFillColor( *pColor );
+ else
+ pOut->SetFillColor( SwViewOption::GetFieldShadingsColor() );
+
+ pOut->SetLineColor();
+
+ DrawRect( aIntersect, true );
+ pOut->Pop();
+}
+
+void SwTextPaintInfo::DrawBackBrush( const SwLinePortion &rPor ) const
+{
+ {
+ SwRect aIntersect;
+ CalcRect( rPor, &aIntersect, nullptr, true );
+ if(aIntersect.HasArea())
+ {
+ SwPosition const aPosition(m_pFrame->MapViewToModelPos(GetIdx()));
+ const ::sw::mark::IMark* pFieldmark =
+ m_pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
+ bool bIsStartMark = (TextFrameIndex(1) == GetLen()
+ && CH_TXT_ATR_FIELDSTART == GetText()[sal_Int32(GetIdx())]);
+ if(pFieldmark) {
+ SAL_INFO("sw.core", "Found Fieldmark " << pFieldmark->ToString());
+ }
+ if(bIsStartMark)
+ SAL_INFO("sw.core", "Found StartMark");
+ if (OnWin() && (pFieldmark!=nullptr || bIsStartMark) &&
+ SwViewOption::IsFieldShadings() &&
+ !GetOpt().IsPagePreview())
+ {
+ OutputDevice* pOutDev = const_cast<OutputDevice*>(GetOut());
+ pOutDev->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ pOutDev->SetFillColor( SwViewOption::GetFieldShadingsColor() );
+ pOutDev->SetLineColor( );
+ pOutDev->DrawRect( aIntersect.SVRect() );
+ pOutDev->Pop();
+ }
+ }
+ }
+
+ SwRect aIntersect;
+ CalcRect( rPor, nullptr, &aIntersect, true );
+
+ if ( !aIntersect.HasArea() )
+ return;
+
+ OutputDevice* pTmpOut = const_cast<OutputDevice*>(GetOut());
+
+ // #i16816# tagged pdf support
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, nullptr, *pTmpOut );
+
+ Color aFillColor;
+
+ if( m_pFnt->GetHighlightColor() != COL_TRANSPARENT )
+ {
+ aFillColor = m_pFnt->GetHighlightColor();
+ }
+ else
+ {
+ if( !m_pFnt->GetBackColor() )
+ return;
+ aFillColor = *m_pFnt->GetBackColor();
+ }
+
+ // tdf#104349 do not highlight portions of space chars before end of line if the compatibility option is enabled
+ // for LTR mode only
+ if ( !GetTextFrame()->IsRightToLeft() )
+ {
+ if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS))
+ {
+ bool draw = false;
+ bool full = false;
+ SwLinePortion *pPos = const_cast<SwLinePortion *>(&rPor);
+ TextFrameIndex nIdx = GetIdx();
+ TextFrameIndex nLen;
+
+ do
+ {
+ nLen = pPos->GetLen();
+ for (TextFrameIndex i = nIdx; i < (nIdx + nLen); ++i)
+ {
+ if (i < TextFrameIndex(GetText().getLength())
+ && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
+ {
+ if ( i >= (GetIdx() + rPor.GetLen()) )
+ {
+ goto drawcontinue;
+ }
+ }
+ if (i >= TextFrameIndex(GetText().getLength())
+ || GetText()[sal_Int32(i)] != CH_BLANK)
+ {
+ draw = true;
+ if ( i >= (GetIdx() + rPor.GetLen()) )
+ {
+ full = true;
+ goto drawcontinue;
+ }
+ }
+ }
+ nIdx += nLen;
+ pPos = pPos->GetNextPortion();
+ } while ( pPos );
+
+ drawcontinue:
+
+ if ( !draw )
+ return;
+
+ if ( !full )
+ {
+ pPos = const_cast<SwLinePortion *>(&rPor);
+ nIdx = GetIdx();
+
+ nLen = pPos->GetLen();
+ for (TextFrameIndex i = nIdx + nLen - TextFrameIndex(1);
+ i >= nIdx; --i)
+ {
+ if (i < TextFrameIndex(GetText().getLength())
+ && GetText()[sal_Int32(i)] == CH_TXTATR_NEWLINE)
+ {
+ continue;
+ }
+ if ((i + TextFrameIndex(1) ).get() > GetText().getLength())
+ // prevent crash by not passing bad data down to GetTextSize->SwDrawTextInfo
+ SAL_WARN("sw", "something dodgy, clamping text index to prevent crash");
+ else if (i >= TextFrameIndex(GetText().getLength())
+ || GetText()[sal_Int32(i)] != CH_BLANK)
+ {
+ sal_uInt16 nOldWidth = rPor.Width();
+ sal_uInt16 nNewWidth = GetTextSize(m_pOut, nullptr,
+ GetText(), nIdx, (i + TextFrameIndex(1) - nIdx)).Width();
+
+ const_cast<SwLinePortion&>(rPor).Width( nNewWidth );
+ CalcRect( rPor, nullptr, &aIntersect, true );
+ const_cast<SwLinePortion&>(rPor).Width( nOldWidth );
+
+ if ( !aIntersect.HasArea() )
+ {
+ return;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ pTmpOut->Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+
+ pTmpOut->SetFillColor(aFillColor);
+ pTmpOut->SetLineColor();
+
+ DrawRect( aIntersect, false );
+
+ pTmpOut->Pop();
+}
+
+void SwTextPaintInfo::DrawBorder( const SwLinePortion &rPor ) const
+{
+ SwRect aDrawArea;
+ CalcRect( rPor, &aDrawArea );
+ if ( aDrawArea.HasArea() )
+ {
+ PaintCharacterBorder(*m_pFnt, aDrawArea, GetTextFrame()->IsVertical(),
+ GetTextFrame()->IsVertLRBT(), rPor.GetJoinBorderWithPrev(),
+ rPor.GetJoinBorderWithNext());
+ }
+}
+
+void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor,
+ PortionType nWhich, const Color *pColor ) const
+{
+ if( !OnWin() || IsMulti() )
+ return;
+
+ bool bDraw = false;
+ switch( nWhich )
+ {
+ case PortionType::Footnote:
+ case PortionType::QuoVadis:
+ case PortionType::Number:
+ case PortionType::Field:
+ case PortionType::Hidden:
+ case PortionType::Tox:
+ case PortionType::Ref:
+ case PortionType::Meta:
+ case PortionType::ContentControl:
+ case PortionType::ControlChar:
+ if ( !GetOpt().IsPagePreview()
+ && !GetOpt().IsReadonly()
+ && SwViewOption::IsFieldShadings()
+ && ( PortionType::Number != nWhich
+ || m_pFrame->GetTextNodeForParaProps()->HasMarkedLabel())) // #i27615#
+ {
+ bDraw = PortionType::Footnote != nWhich || m_pFrame->IsFootnoteAllowed();
+ }
+ break;
+ case PortionType::Bookmark:
+ // no shading
+ break;
+ case PortionType::InputField:
+ // input field shading also in read-only mode
+ if ( !GetOpt().IsPagePreview()
+ && SwViewOption::IsFieldShadings() )
+ {
+ bDraw = true;
+ }
+ break;
+ case PortionType::Tab:
+ if ( GetOpt().IsTab() ) bDraw = true;
+ break;
+ case PortionType::SoftHyphen:
+ if ( GetOpt().IsSoftHyph() )bDraw = true;
+ break;
+ case PortionType::Blank:
+ if ( GetOpt().IsHardBlank())bDraw = true;
+ break;
+ default:
+ {
+ OSL_ENSURE( false, "SwTextPaintInfo::DrawViewOpt: don't know how to draw this" );
+ break;
+ }
+ }
+ if ( bDraw )
+ DrawBackground( rPor, pColor );
+}
+
+static void lcl_InitHyphValues( PropertyValues &rVals,
+ sal_Int16 nMinLeading, sal_Int16 nMinTrailing,
+ bool bNoCapsHyphenation, bool bNoLastWordHyphenation,
+ sal_Int16 nMinWordLength, sal_Int16 nTextHyphZone )
+{
+ sal_Int32 nLen = rVals.getLength();
+
+ if (0 == nLen) // yet to be initialized?
+ {
+ rVals.realloc( 6 );
+ PropertyValue *pVal = rVals.getArray();
+
+ pVal[0].Name = UPN_HYPH_MIN_LEADING;
+ pVal[0].Handle = UPH_HYPH_MIN_LEADING;
+ pVal[0].Value <<= nMinLeading;
+
+ pVal[1].Name = UPN_HYPH_MIN_TRAILING;
+ pVal[1].Handle = UPH_HYPH_MIN_TRAILING;
+ pVal[1].Value <<= nMinTrailing;
+
+ pVal[2].Name = UPN_HYPH_NO_CAPS;
+ pVal[2].Handle = UPH_HYPH_NO_CAPS;
+ pVal[2].Value <<= bNoCapsHyphenation;
+
+ pVal[3].Name = UPN_HYPH_NO_LAST_WORD;
+ pVal[3].Handle = UPH_HYPH_NO_LAST_WORD;
+ pVal[3].Value <<= bNoLastWordHyphenation;
+
+ pVal[4].Name = UPN_HYPH_MIN_WORD_LENGTH;
+ pVal[4].Handle = UPH_HYPH_MIN_WORD_LENGTH;
+ pVal[4].Value <<= nMinWordLength;
+
+ pVal[5].Name = UPN_HYPH_ZONE;
+ pVal[5].Handle = UPH_HYPH_ZONE;
+ pVal[5].Value <<= nTextHyphZone;
+ }
+ else if (6 == nLen) // already initialized once?
+ {
+ PropertyValue *pVal = rVals.getArray();
+ pVal[0].Value <<= nMinLeading;
+ pVal[1].Value <<= nMinTrailing;
+ pVal[2].Value <<= bNoCapsHyphenation;
+ pVal[3].Value <<= bNoLastWordHyphenation;
+ pVal[4].Value <<= nMinWordLength;
+ pVal[5].Value <<= nTextHyphZone;
+ }
+ else {
+ OSL_FAIL( "unexpected size of sequence" );
+ }
+}
+
+const PropertyValues & SwTextFormatInfo::GetHyphValues() const
+{
+ OSL_ENSURE( 6 == m_aHyphVals.getLength(),
+ "hyphenation values not yet initialized" );
+ return m_aHyphVals;
+}
+
+bool SwTextFormatInfo::InitHyph( const bool bAutoHyphen )
+{
+ const SwAttrSet& rAttrSet = GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
+ SetHanging( rAttrSet.GetHangingPunctuation().GetValue() );
+ SetScriptSpace( rAttrSet.GetScriptSpace().GetValue() );
+ SetForbiddenChars( rAttrSet.GetForbiddenRule().GetValue() );
+ const SvxHyphenZoneItem &rAttr = rAttrSet.GetHyphenZone();
+ MaxHyph() = rAttr.GetMaxHyphens();
+ const bool bAuto = bAutoHyphen || rAttr.IsHyphen();
+ if( bAuto || m_bInterHyph )
+ {
+ const sal_Int16 nMinimalLeading = std::max(rAttr.GetMinLead(), sal_uInt8(2));
+ const sal_Int16 nMinimalTrailing = rAttr.GetMinTrail();
+ const sal_Int16 nMinimalWordLength = rAttr.GetMinWordLength();
+ const bool bNoCapsHyphenation = rAttr.IsNoCapsHyphenation();
+ const bool bNoLastWordHyphenation = rAttr.IsNoLastWordHyphenation();
+ const sal_Int16 nTextHyphZone = rAttr.GetTextHyphenZone();
+ lcl_InitHyphValues( m_aHyphVals, nMinimalLeading, nMinimalTrailing,
+ bNoCapsHyphenation, bNoLastWordHyphenation,
+ nMinimalWordLength, nTextHyphZone );
+ }
+ return bAuto;
+}
+
+void SwTextFormatInfo::CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwTextFrame *pNewFrame, const bool bNewInterHyph,
+ const bool bNewQuick, const bool bTst )
+{
+ CtorInitTextPaintInfo( pRenderContext, pNewFrame, SwRect() );
+
+ m_bQuick = bNewQuick;
+ m_bInterHyph = bNewInterHyph;
+
+ //! needs to be done in this order
+ m_bAutoHyph = InitHyph();
+
+ m_bIgnoreFly = false;
+ m_bFakeLineStart = false;
+ m_bShift = false;
+ m_bDropInit = false;
+ m_bTestFormat = bTst;
+ m_nLeft = 0;
+ m_nRight = 0;
+ m_nFirst = 0;
+ m_nRealWidth = 0;
+ m_nForcedLeftMargin = 0;
+ m_pRest = nullptr;
+ m_nLineHeight = 0;
+ m_nLineNetHeight = 0;
+ SetLineStart(TextFrameIndex(0));
+
+ SvtCTLOptions::TextNumerals const nTextNumerals(
+ SW_MOD()->GetCTLOptions().GetCTLTextNumerals());
+ // cannot cache for NUMERALS_CONTEXT because we need to know the string
+ // for the whole paragraph now
+ if (nTextNumerals != SvtCTLOptions::NUMERALS_CONTEXT)
+ {
+ // set digit mode to what will be used later to get same results
+ SwDigitModeModifier const m(*m_pRef, LANGUAGE_NONE /*dummy*/);
+ assert(m_pRef->GetDigitLanguage() != LANGUAGE_NONE);
+ SetCachedVclData(OutputDevice::CreateTextLayoutCache(*m_pText));
+ }
+
+ Init();
+}
+
+/**
+ * If the Hyphenator returns ERROR or the language is set to NOLANGUAGE
+ * we do not hyphenate.
+ * Else, we always hyphenate if we do interactive hyphenation.
+ * If we do not do interactive hyphenation, we only hyphenate if ParaFormat is
+ * set to automatic hyphenation.
+ */
+bool SwTextFormatInfo::IsHyphenate() const
+{
+ if( !m_bInterHyph && !m_bAutoHyph )
+ return false;
+
+ LanguageType eTmp = GetFont()->GetLanguage();
+ // TODO: check for more ideographic langs w/o hyphenation as a concept
+ if ( LANGUAGE_DONTKNOW == eTmp || LANGUAGE_NONE == eTmp
+ || !MsLangId::usesHyphenation(eTmp) )
+ return false;
+
+ uno::Reference< XHyphenator > xHyph = ::GetHyphenator();
+ if (!xHyph.is())
+ return false;
+
+ if (m_bInterHyph)
+ SvxSpellWrapper::CheckHyphLang( xHyph, eTmp );
+
+ if (!xHyph->hasLocale(g_pBreakIt->GetLocale(eTmp)))
+ {
+ SfxObjectShell* pShell = m_pFrame->GetDoc().GetDocShell();
+ if (pShell)
+ {
+ pShell->AppendInfoBarWhenReady(
+ "hyphenationmissing", SwResId(STR_HYPH_MISSING),
+ SwResId(STR_HYPH_MISSING_DETAIL)
+ .replaceFirst("%1", LanguageTag::convertToBcp47( g_pBreakIt->GetLocale(eTmp))),
+ InfobarType::WARNING);
+ }
+ }
+
+ return xHyph->hasLocale( g_pBreakIt->GetLocale(eTmp) );
+}
+
+const SwFormatDrop *SwTextFormatInfo::GetDropFormat() const
+{
+ const SwFormatDrop *pDrop = &GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetDrop();
+ if( 1 >= pDrop->GetLines() ||
+ ( !pDrop->GetChars() && !pDrop->GetWholeWord() ) )
+ pDrop = nullptr;
+ return pDrop;
+}
+
+void SwTextFormatInfo::Init()
+{
+ // Not initialized: pRest, nLeft, nRight, nFirst, nRealWidth
+ X(0);
+ m_bArrowDone = m_bFull = m_bFootnoteDone = m_bErgoDone = m_bNumDone = m_bNoEndHyph =
+ m_bNoMidHyph = m_bStop = m_bNewLine = m_bUnderflow = m_bTabOverflow = false;
+
+ // generally we do not allow number portions in follows, except...
+ if ( GetTextFrame()->IsFollow() )
+ {
+ const SwTextFrame* pMaster = GetTextFrame()->FindMaster();
+ OSL_ENSURE(pMaster, "pTextFrame without Master");
+ const SwLinePortion* pTmpPara = pMaster ? pMaster->GetPara() : nullptr;
+
+ // there is a master for this follow and the master does not have
+ // any contents (especially it does not have a number portion)
+ m_bNumDone = ! pTmpPara ||
+ ! static_cast<const SwParaPortion*>(pTmpPara)->GetFirstPortion()->IsFlyPortion();
+ }
+
+ m_pRoot = nullptr;
+ m_pLast = nullptr;
+ m_pFly = nullptr;
+ m_pLastTab = nullptr;
+ m_pUnderflow = nullptr;
+ m_cTabDecimal = 0;
+ m_nWidth = m_nRealWidth;
+ m_nForcedLeftMargin = 0;
+ m_nSoftHyphPos = TextFrameIndex(0);
+ m_nUnderScorePos = TextFrameIndex(COMPLETE_STRING);
+ m_nLastBookmarkPos = TextFrameIndex(-1);
+ m_cHookChar = 0;
+ SetIdx(TextFrameIndex(0));
+ SetLen(TextFrameIndex(GetText().getLength()));
+ SetPaintOfst(0);
+}
+
+SwTextFormatInfo::SwTextFormatInfo(OutputDevice* pRenderContext, SwTextFrame *pFrame, const bool bInterHyphL,
+ const bool bQuickL, const bool bTst)
+{
+ CtorInitTextFormatInfo(pRenderContext, pFrame, bInterHyphL, bQuickL, bTst);
+}
+
+/**
+ * There are a few differences between a copy constructor
+ * and the following constructor for multi-line formatting.
+ * The root is the first line inside the multi-portion,
+ * the line start is the actual position in the text,
+ * the line width is the rest width from the surrounding line
+ * and the bMulti and bFirstMulti-flag has to be set correctly.
+ */
+SwTextFormatInfo::SwTextFormatInfo( const SwTextFormatInfo& rInf,
+ SwLineLayout& rLay, SwTwips nActWidth ) :
+ SwTextPaintInfo( rInf ),
+ m_pRoot(&rLay),
+ m_pLast(&rLay),
+ m_pFly(nullptr),
+ m_pUnderflow(nullptr),
+ m_pRest(nullptr),
+ m_pLastTab(nullptr),
+ m_nSoftHyphPos(TextFrameIndex(0)),
+ m_nLineStart(rInf.GetIdx()),
+ m_nUnderScorePos(TextFrameIndex(COMPLETE_STRING)),
+ m_nLeft(rInf.m_nLeft),
+ m_nRight(rInf.m_nRight),
+ m_nFirst(rInf.m_nLeft),
+ m_nRealWidth(sal_uInt16(nActWidth)),
+ m_nWidth(m_nRealWidth),
+ m_nLineHeight(0),
+ m_nLineNetHeight(0),
+ m_nForcedLeftMargin(0),
+ m_bFull(false),
+ m_bFootnoteDone(true),
+ m_bErgoDone(true),
+ m_bNumDone(true),
+ m_bArrowDone(true),
+ m_bStop(false),
+ m_bNewLine(true),
+ m_bShift(false),
+ m_bUnderflow(false),
+ m_bInterHyph(false),
+ m_bAutoHyph(false),
+ m_bDropInit(false),
+ m_bQuick(rInf.m_bQuick),
+ m_bNoEndHyph(false),
+ m_bNoMidHyph(false),
+ m_bIgnoreFly(false),
+ m_bFakeLineStart(false),
+ m_bTabOverflow( false ),
+ m_bTestFormat(rInf.m_bTestFormat),
+ m_cTabDecimal(0),
+ m_cHookChar(0),
+ m_nMaxHyph(0)
+{
+ SetMulti( true );
+ SetFirstMulti( rInf.IsFirstMulti() );
+}
+
+bool SwTextFormatInfo::CheckFootnotePortion_( SwLineLayout const * pCurr )
+{
+ const sal_uInt16 nHeight = pCurr->GetRealHeight();
+ for( SwLinePortion *pPor = pCurr->GetNextPortion(); pPor; pPor = pPor->GetNextPortion() )
+ {
+ if( pPor->IsFootnotePortion() && nHeight > static_cast<SwFootnotePortion*>(pPor)->Orig() )
+ {
+ SetLineHeight( nHeight );
+ SetLineNetHeight( pCurr->Height() );
+ return true;
+ }
+ }
+ return false;
+}
+
+TextFrameIndex SwTextFormatInfo::ScanPortionEnd(TextFrameIndex const nStart,
+ TextFrameIndex const nEnd)
+{
+ m_cHookChar = 0;
+ TextFrameIndex i = nStart;
+
+ // Used for decimal tab handling:
+ const sal_Unicode cTabDec = GetLastTab() ? GetTabDecimal() : 0;
+ const sal_Unicode cThousandSep = ',' == cTabDec ? '.' : ',';
+
+ // #i45951# German (Switzerland) uses ' as thousand separator
+ const sal_Unicode cThousandSep2 = ',' == cTabDec ? '.' : '\'';
+
+ bool bNumFound = false;
+ const bool bTabCompat = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
+
+ for( ; i < nEnd; ++i )
+ {
+ const sal_Unicode cPos = GetChar( i );
+ switch( cPos )
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ if( !HasHint( i ))
+ break;
+ [[fallthrough]];
+
+ case CHAR_SOFTHYPHEN:
+ case CHAR_HARDHYPHEN:
+ case CHAR_HARDBLANK:
+ case CH_TAB:
+ case CH_BREAK:
+ case CHAR_ZWSP :
+ case CHAR_WJ :
+ m_cHookChar = cPos;
+ return i;
+
+ case CHAR_UNDERSCORE:
+ if (TextFrameIndex(COMPLETE_STRING) == m_nUnderScorePos)
+ m_nUnderScorePos = i;
+ break;
+
+ default:
+ if ( cTabDec )
+ {
+ if( cTabDec == cPos )
+ {
+ OSL_ENSURE( cPos, "Unexpected end of string" );
+ if( cPos ) // robust
+ {
+ m_cHookChar = cPos;
+ return i;
+ }
+ }
+
+ // Compatibility: First non-digit character behind a
+ // a digit character becomes the hook character
+ if ( bTabCompat )
+ {
+ if ( ( 0x2F < cPos && cPos < 0x3A ) ||
+ ( bNumFound && ( cPos == cThousandSep || cPos == cThousandSep2 ) ) )
+ {
+ bNumFound = true;
+ }
+ else
+ {
+ if ( bNumFound )
+ {
+ m_cHookChar = cPos;
+ SetTabDecimal( cPos );
+ return i;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Check if character *behind* the portion has
+ // to become the hook:
+ if (i == nEnd && i < TextFrameIndex(GetText().getLength()) && bNumFound)
+ {
+ const sal_Unicode cPos = GetChar( i );
+ if ( cPos != cTabDec && cPos != cThousandSep && cPos !=cThousandSep2 && ( 0x2F >= cPos || cPos >= 0x3A ) )
+ {
+ m_cHookChar = GetChar( i );
+ SetTabDecimal( m_cHookChar );
+ }
+ }
+
+ return i;
+}
+
+bool SwTextFormatInfo::LastKernPortion()
+{
+ if( GetLast() )
+ {
+ if( GetLast()->IsKernPortion() )
+ return true;
+ if( GetLast()->Width() || ( GetLast()->GetLen() &&
+ !GetLast()->IsHolePortion() ) )
+ return false;
+ }
+ SwLinePortion* pPor = GetRoot();
+ SwLinePortion *pKern = nullptr;
+ while( pPor )
+ {
+ if( pPor->IsKernPortion() )
+ pKern = pPor;
+ else if( pPor->Width() || ( pPor->GetLen() && !pPor->IsHolePortion() ) )
+ pKern = nullptr;
+ pPor = pPor->GetNextPortion();
+ }
+ if( pKern )
+ {
+ SetLast( pKern );
+ return true;
+ }
+ return false;
+}
+
+SwTwips SwTextFormatInfo::GetLineWidth()
+{
+ SwTwips nLineWidth = Width() - X();
+
+ const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_MARGIN);
+ const bool bTabOverSpacing = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_SPACING);
+ if (!bTabOverMargin && !bTabOverSpacing)
+ return nLineWidth;
+
+ SwTabPortion* pLastTab = GetLastTab();
+ if (!pLastTab)
+ return nLineWidth;
+
+ // Consider tab portions over the printing bounds of the text frame.
+ if (pLastTab->GetTabPos() <= Width())
+ return nLineWidth;
+
+ // Calculate the width that starts at the left (or in case of first line:
+ // first) margin, but ends after the right paragraph margin:
+ //
+ // +--------------------+
+ // |LL| |RR|
+ // +--------------------+
+ // ^ m_nLeftMargin (absolute)
+ // ^ nLeftMarginWidth (relative to m_nLeftMargin), X() is relative to this
+ // ^ right margin
+ // ^ paragraph right
+ // <--------------------> is GetTextFrame()->getFrameArea().Width()
+ // <--------------> is Width()
+ // <-----------------> is what we need to be able to compare to X() (nTextFrameWidth)
+ SwTwips nLeftMarginWidth = m_nLeftMargin - GetTextFrame()->getFrameArea().Left();
+ SwTwips nTextFrameWidth = GetTextFrame()->getFrameArea().Width() - nLeftMarginWidth;
+
+ // If there is one such tab portion, then text is allowed to use the full
+ // text frame area to the right (RR above, but not LL).
+ nLineWidth = nTextFrameWidth - X();
+
+ if (!bTabOverMargin) // thus bTabOverSpacing only
+ {
+ // right, center, decimal can back-fill all the available space - same as TabOverMargin
+ if (pLastTab->GetWhichPor() == PortionType::TabLeft)
+ nLineWidth = nTextFrameWidth - pLastTab->GetTabPos();
+ }
+ return nLineWidth;
+}
+
+SwTextSlot::SwTextSlot(
+ const SwTextSizeInfo *pNew,
+ const SwLinePortion *pPor,
+ bool bTextLen,
+ bool bExgLists,
+ OUString const & rCh )
+ : pOldText(nullptr)
+ , m_pOldSmartTagList(nullptr)
+ , m_pOldGrammarCheckList(nullptr)
+ , nIdx(0)
+ , nLen(0)
+ , pInf(nullptr)
+{
+ if( rCh.isEmpty() )
+ {
+ bOn = pPor->GetExpText( *pNew, aText );
+ }
+ else
+ {
+ aText = rCh;
+ bOn = true;
+ }
+
+ // The text is replaced ...
+ if( !bOn )
+ return;
+
+ pInf = const_cast<SwTextSizeInfo*>(pNew);
+ nIdx = pInf->GetIdx();
+ nLen = pInf->GetLen();
+ pOldText = &(pInf->GetText());
+ m_pOldCachedVclData = pInf->GetCachedVclData();
+ pInf->SetText( aText );
+ pInf->SetIdx(TextFrameIndex(0));
+ pInf->SetLen(bTextLen ? TextFrameIndex(pInf->GetText().getLength()) : pPor->GetLen());
+ pInf->SetCachedVclData(nullptr);
+
+ // ST2
+ if ( !bExgLists )
+ return;
+
+ m_pOldSmartTagList = static_cast<SwTextPaintInfo*>(pInf)->GetSmartTags();
+ if (m_pOldSmartTagList)
+ {
+ std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
+ SwWrongList const*const pSmartTags(pos.first->GetSmartTags());
+ if (pSmartTags)
+ {
+ const sal_uInt16 nPos = pSmartTags->GetWrongPos(pos.second);
+ const sal_Int32 nListPos = pSmartTags->Pos(nPos);
+ if (nListPos == pos.second && pSmartTags->SubList(nPos) != nullptr)
+ {
+ m_pTempIter.reset(new sw::WrongListIterator(*pSmartTags->SubList(nPos)));
+ static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
+ }
+ else if (!m_pTempList && nPos < pSmartTags->Count()
+ && nListPos < pos.second && !aText.isEmpty())
+ {
+ m_pTempList.reset(new SwWrongList( WRONGLIST_SMARTTAG ));
+ m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
+ m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
+ static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pTempIter.get());
+ }
+ else
+ static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
+ }
+ else
+ static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(nullptr);
+ }
+ m_pOldGrammarCheckList = static_cast<SwTextPaintInfo*>(pInf)->GetGrammarCheckList();
+ if (!m_pOldGrammarCheckList)
+ return;
+
+ std::pair<SwTextNode const*, sal_Int32> pos(pNew->GetTextFrame()->MapViewToModel(nIdx));
+ SwWrongList const*const pGrammar(pos.first->GetGrammarCheck());
+ if (pGrammar)
+ {
+ const sal_uInt16 nPos = pGrammar->GetWrongPos(pos.second);
+ const sal_Int32 nListPos = pGrammar->Pos(nPos);
+ if (nListPos == pos.second && pGrammar->SubList(nPos) != nullptr)
+ {
+ m_pTempIter.reset(new sw::WrongListIterator(*pGrammar->SubList(nPos)));
+ static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
+ }
+ else if (!m_pTempList && nPos < pGrammar->Count()
+ && nListPos < pos.second && !aText.isEmpty())
+ {
+ m_pTempList.reset(new SwWrongList( WRONGLIST_GRAMMAR ));
+ m_pTempList->Insert( OUString(), nullptr, 0, aText.getLength(), 0 );
+ m_pTempIter.reset(new sw::WrongListIterator(*m_pTempList));
+ static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pTempIter.get());
+ }
+ else
+ static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
+ }
+ else
+ static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(nullptr);
+}
+
+SwTextSlot::~SwTextSlot()
+{
+ if( !bOn )
+ return;
+
+ pInf->SetCachedVclData(m_pOldCachedVclData);
+ pInf->SetText( *pOldText );
+ pInf->SetIdx( nIdx );
+ pInf->SetLen( nLen );
+
+ // ST2
+ // Restore old smart tag list
+ if (m_pOldSmartTagList)
+ static_cast<SwTextPaintInfo*>(pInf)->SetSmartTags(m_pOldSmartTagList);
+ if (m_pOldGrammarCheckList)
+ static_cast<SwTextPaintInfo*>(pInf)->SetGrammarCheckList(m_pOldGrammarCheckList);
+}
+
+SwFontSave::SwFontSave(const SwTextSizeInfo &rInf, SwFont *pNew,
+ SwAttrIter* pItr)
+ : pInf(nullptr)
+ , pFnt(pNew ? const_cast<SwTextSizeInfo&>(rInf).GetFont() : nullptr)
+ , pIter(nullptr)
+{
+ if( !pFnt )
+ return;
+
+ pInf = &const_cast<SwTextSizeInfo&>(rInf);
+ // In these cases we temporarily switch to the new font:
+ // 1. the fonts have a different magic number
+ // 2. they have different script types
+ // 3. their background colors differ (this is not covered by 1.)
+ if( pFnt->DifferentFontCacheId( pNew, pFnt->GetActual() ) ||
+ pNew->GetActual() != pFnt->GetActual() ||
+ ( ! pNew->GetBackColor() && pFnt->GetBackColor() ) ||
+ ( pNew->GetBackColor() && ! pFnt->GetBackColor() ) ||
+ ( pNew->GetBackColor() && pFnt->GetBackColor() &&
+ ( *pNew->GetBackColor() != *pFnt->GetBackColor() ) ) )
+ {
+ pNew->SetTransparent( true );
+ pNew->SetAlign( ALIGN_BASELINE );
+ pInf->SetFont( pNew );
+ }
+ else
+ pFnt = nullptr;
+ pNew->Invalidate();
+ pNew->ChgPhysFnt( pInf->GetVsh(), *pInf->GetOut() );
+ if( pItr && pItr->GetFnt() == pFnt )
+ {
+ pIter = pItr;
+ pIter->SetFnt( pNew );
+ }
+}
+
+SwFontSave::~SwFontSave()
+{
+ if( pFnt )
+ {
+ // Reset SwFont
+ pFnt->Invalidate();
+ pInf->SetFont( pFnt );
+ if( pIter )
+ {
+ pIter->SetFnt( pFnt );
+ pIter->m_nPosition = COMPLETE_STRING;
+ }
+ }
+}
+
+bool SwTextFormatInfo::ChgHyph( const bool bNew )
+{
+ const bool bOld = m_bAutoHyph;
+ if( m_bAutoHyph != bNew )
+ {
+ m_bAutoHyph = bNew;
+ InitHyph( bNew );
+ // Set language in the Hyphenator
+ if( m_pFnt )
+ m_pFnt->ChgPhysFnt( m_pVsh, *m_pOut );
+ }
+ return bOld;
+}
+
+
+bool SwTextFormatInfo::CheckCurrentPosBookmark()
+{
+ if (m_nLastBookmarkPos != GetIdx())
+ {
+ m_nLastBookmarkPos = GetIdx();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/inftxt.hxx b/sw/source/core/text/inftxt.hxx
new file mode 100644
index 000000000..1621b4f35
--- /dev/null
+++ b/sw/source/core/text/inftxt.hxx
@@ -0,0 +1,786 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <memory>
+#include <optional>
+#include <com/sun/star/beans/PropertyValues.hpp>
+
+#include <map>
+
+#include <swtypes.hxx>
+#include <swrect.hxx>
+#include <txtfly.hxx>
+#include <swfont.hxx>
+#include "porlay.hxx"
+#include <txtfrm.hxx>
+#include <ndtxt.hxx>
+#include <editeng/paravertalignitem.hxx>
+
+namespace com::sun::star::linguistic2 { class XHyphenatedWord; }
+
+class SvxBrushItem;
+class SvxLineSpacingItem;
+class SvxTabStop;
+class SvxTabStopItem;
+class SwFlyPortion;
+class SwFormatDrop;
+class SwLinePortion;
+class SwTabPortion;
+class SwViewOption;
+class SwViewShell;
+class SwAttrIter;
+struct SwMultiCreator;
+class SwMultiPortion;
+namespace sw { class WrongListIterator; }
+
+#define ARROW_WIDTH 200
+#define DIR_LEFT2RIGHT 0
+#define DIR_BOTTOM2TOP 1
+#define DIR_RIGHT2LEFT 2
+#define DIR_TOP2BOTTOM 3
+
+// Respects the attribute LineSpace when calculating the Height/Ascent
+class SwLineInfo
+{
+ friend class SwTextIter;
+
+ std::optional<SvxTabStopItem> m_oRuler;
+ const SvxLineSpacingItem *m_pSpace;
+ SvxParaVertAlignItem::Align m_nVertAlign;
+ sal_uInt16 m_nDefTabStop;
+ bool m_bListTabStopIncluded;
+ tools::Long m_nListTabStopPosition;
+
+ void CtorInitLineInfo( const SwAttrSet& rAttrSet,
+ const SwTextNode& rTextNode );
+
+ SwLineInfo();
+ ~SwLineInfo();
+public:
+ // #i24363# tab stops relative to indent - returns the tab stop following nSearchPos or NULL
+ const SvxTabStop* GetTabStop(const SwTwips nSearchPos, SwTwips& nRight) const;
+ const SvxLineSpacingItem *GetLineSpacing() const { return m_pSpace; }
+ sal_uInt16 GetDefTabStop() const { return m_nDefTabStop; }
+ void SetDefTabStop( sal_uInt16 nNew ) const
+ { const_cast<SwLineInfo*>(this)->m_nDefTabStop = nNew; }
+
+ // vertical alignment
+ SvxParaVertAlignItem::Align GetVertAlign() const { return m_nVertAlign; }
+ bool HasSpecialAlign( bool bVert ) const
+ { return bVert ?
+ ( SvxParaVertAlignItem::Align::Baseline != m_nVertAlign ) :
+ ( SvxParaVertAlignItem::Align::Baseline != m_nVertAlign &&
+ SvxParaVertAlignItem::Align::Automatic != m_nVertAlign ); }
+
+ sal_uInt16 NumberOfTabStops() const;
+
+ bool IsListTabStopIncluded() const
+ {
+ return m_bListTabStopIncluded;
+ }
+ tools::Long GetListTabStopPosition() const
+ {
+ return m_nListTabStopPosition;
+ }
+};
+
+class SwTextInfo
+{
+ // Implementation in txthyph.cxx
+ friend void SetParaPortion( SwTextInfo *pInf, SwParaPortion *pRoot );
+ SwParaPortion *m_pPara;
+ TextFrameIndex m_nTextStart; // TextOfst for Follows
+
+protected:
+ SwTextInfo()
+ : m_pPara(nullptr)
+ , m_nTextStart(0)
+ {}
+
+public:
+ void CtorInitTextInfo( SwTextFrame *pFrame );
+ SwTextInfo( const SwTextInfo &rInf );
+ explicit SwTextInfo( SwTextFrame *pFrame ) { CtorInitTextInfo( pFrame ); }
+ SwParaPortion *GetParaPortion() { return m_pPara; }
+ const SwParaPortion *GetParaPortion() const { return m_pPara; }
+ TextFrameIndex GetTextStart() const { return m_nTextStart; }
+};
+
+class SwTextSizeInfo : public SwTextInfo
+{
+private:
+ typedef std::map< SwLinePortion const *, sal_uInt16 > SwTextPortionMap;
+
+protected:
+ // during formatting, a small database is built, mapping portion pointers
+ // to their maximum size (used for kana compression)
+ SwTextPortionMap m_aMaxWidth;
+ // for each line, an array of compression values is calculated
+ // this array is passed over to the info structure
+ std::deque<sal_uInt16>* m_pKanaComp;
+
+ SwViewShell *m_pVsh;
+
+ // m_pOut is the output device, m_pRef is the device used for formatting
+ VclPtr<OutputDevice> m_pOut;
+ VclPtr<OutputDevice> m_pRef;
+
+ // performance hack - this is only used by SwTextFormatInfo but
+ // because it's not even possible to dynamic_cast these things
+ // currently it has to be stored here
+ std::shared_ptr<const vcl::text::TextLayoutCache> m_pCachedVclData;
+
+ SwFont *m_pFnt;
+ SwUnderlineFont *m_pUnderFnt; // Font for underlining
+ SwTextFrame *m_pFrame;
+ const SwViewOption *m_pOpt;
+ const OUString *m_pText;
+ TextFrameIndex m_nIdx;
+ TextFrameIndex m_nLen;
+ sal_uInt16 m_nKanaIdx;
+ bool m_bOnWin : 1;
+ bool m_bNotEOL : 1;
+ bool m_bURLNotify : 1;
+ bool m_bStopUnderflow : 1; // Underflow was stopped e.g. by a FlyPortion
+ bool m_bFootnoteInside : 1; // the current line contains a footnote
+ bool m_bOtherThanFootnoteInside : 1; // the current line contains another portion than a footnote portion.
+ // needed for checking keep together of footnote portion with previous portion
+ bool m_bMulti : 1; // inside a multiportion
+ bool m_bFirstMulti : 1; // this flag is used for two purposes:
+ // - the multiportion is the first lineportion
+ // - indicates, if we are currently in second
+ // line of multi portion
+ bool m_bRuby : 1; // during the formatting of a phonetic line
+ bool m_bHanging : 1; // formatting of hanging punctuation allowed
+ bool m_bScriptSpace : 1; // space between different scripts (Asian/Latin)
+ bool m_bForbiddenChars : 1; // Forbidden start/endline characters
+ bool m_bSnapToGrid : 1; // paragraph snaps to grid
+ sal_uInt8 m_nDirection : 2; // writing direction: 0/90/180/270 degree
+
+protected:
+ void CtorInitTextSizeInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame,
+ TextFrameIndex nIdx);
+ SwTextSizeInfo();
+public:
+ SwTextSizeInfo( const SwTextSizeInfo &rInf );
+ SwTextSizeInfo( const SwTextSizeInfo &rInf, const OUString* pText,
+ TextFrameIndex nIdx = TextFrameIndex(0) );
+ SwTextSizeInfo(SwTextFrame *pTextFrame, TextFrameIndex nIndex = TextFrameIndex(0));
+
+ // GetMultiAttr returns the text attribute of the multiportion,
+ // if rPos is inside any multi-line part.
+ // rPos will set to the end of the multi-line part.
+ std::optional<SwMultiCreator> GetMultiCreator(TextFrameIndex &rPos, SwMultiPortion const* pM) const;
+
+ bool OnWin() const { return m_bOnWin; }
+ void SetOnWin( const bool bNew ) { m_bOnWin = bNew; }
+ bool NotEOL() const { return m_bNotEOL; }
+ void SetNotEOL( const bool bNew ) { m_bNotEOL = bNew; }
+ bool URLNotify() const { return m_bURLNotify; }
+ bool StopUnderflow() const { return m_bStopUnderflow; }
+ void SetStopUnderflow( const bool bNew ) { m_bStopUnderflow = bNew; }
+ bool IsFootnoteInside() const { return m_bFootnoteInside; }
+ void SetFootnoteInside( const bool bNew ) { m_bFootnoteInside = bNew; }
+ bool IsOtherThanFootnoteInside() const { return m_bOtherThanFootnoteInside; }
+ void SetOtherThanFootnoteInside( const bool bNew ) { m_bOtherThanFootnoteInside = bNew; }
+ bool IsMulti() const { return m_bMulti; }
+ void SetMulti( const bool bNew ) { m_bMulti = bNew; }
+ bool IsFirstMulti() const { return m_bFirstMulti; }
+ void SetFirstMulti( const bool bNew ) { m_bFirstMulti = bNew; }
+ bool IsRuby() const { return m_bRuby; }
+ void SetRuby( const bool bNew ) { m_bRuby = bNew; }
+ bool IsHanging() const { return m_bHanging; }
+ void SetHanging( const bool bNew ) { m_bHanging = bNew; }
+ bool HasScriptSpace() const { return m_bScriptSpace; }
+ void SetScriptSpace( const bool bNew ) { m_bScriptSpace = bNew; }
+ bool HasForbiddenChars() const { return m_bForbiddenChars; }
+ void SetForbiddenChars( const bool bN ) { m_bForbiddenChars = bN; }
+ bool SnapToGrid() const { return m_bSnapToGrid; }
+ void SetSnapToGrid( const bool bN ) { m_bSnapToGrid = bN; }
+ sal_uInt8 GetDirection() const { return m_nDirection; }
+ void SetDirection( const sal_uInt8 nNew ) { m_nDirection = nNew; }
+ bool IsRotated() const { return ( 1 & m_nDirection ); }
+
+ SwViewShell *GetVsh() { return m_pVsh; }
+ const SwViewShell *GetVsh() const { return m_pVsh; }
+
+ vcl::RenderContext *GetOut() { return m_pOut; }
+ const vcl::RenderContext *GetOut() const { return m_pOut; }
+ void SetOut( OutputDevice* pNewOut ) { m_pOut = pNewOut; }
+
+ vcl::RenderContext *GetRefDev() { return m_pRef; }
+ const vcl::RenderContext *GetRefDev() const { return m_pRef; }
+
+ SwFont *GetFont() { return m_pFnt; }
+ const SwFont *GetFont() const { return m_pFnt; }
+ void SetFont( SwFont *pNew ) { m_pFnt = pNew; }
+ void SelectFont();
+ void SetUnderFnt( SwUnderlineFont* pNew ) { m_pUnderFnt = pNew; }
+ SwUnderlineFont* GetUnderFnt() const { return m_pUnderFnt; }
+
+ const SwViewOption &GetOpt() const { return *m_pOpt; }
+ const OUString &GetText() const { return *m_pText; }
+ sal_Unicode GetChar(TextFrameIndex const nPos) const {
+ if (m_pText && nPos < TextFrameIndex(m_pText->getLength())) return (*m_pText)[sal_Int32(nPos)];
+ return 0;
+ }
+
+ sal_uInt16 GetTextHeight() const;
+
+ SwPosSize GetTextSize( OutputDevice* pOut, const SwScriptInfo* pSI,
+ const OUString& rText, TextFrameIndex nIdx,
+ TextFrameIndex nLen ) const;
+ SwPosSize GetTextSize() const;
+ void GetTextSize( const SwScriptInfo* pSI, TextFrameIndex nIdx,
+ TextFrameIndex nLen, const sal_uInt16 nComp,
+ sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
+ vcl::text::TextLayoutCache const* = nullptr) const;
+ inline SwPosSize GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx,
+ TextFrameIndex nLen) const;
+ inline SwPosSize GetTextSize( const OUString &rText ) const;
+
+ TextFrameIndex GetTextBreak( const tools::Long nLineWidth,
+ const TextFrameIndex nMaxLen,
+ const sal_uInt16 nComp,
+ vcl::text::TextLayoutCache const*) const;
+ TextFrameIndex GetTextBreak( const tools::Long nLineWidth,
+ const TextFrameIndex nMaxLen,
+ const sal_uInt16 nComp,
+ TextFrameIndex& rExtraCharPos,
+ vcl::text::TextLayoutCache const*) const;
+
+ sal_uInt16 GetAscent() const;
+ sal_uInt16 GetHangingBaseline() const;
+
+ TextFrameIndex GetIdx() const { return m_nIdx; }
+ void SetIdx(const TextFrameIndex nNew) { m_nIdx = nNew; }
+ TextFrameIndex GetLen() const { return m_nLen; }
+ void SetLen(const TextFrameIndex nNew) { m_nLen = nNew; }
+ void SetText( const OUString &rNew ){ m_pText = &rNew; }
+
+ // No Bullets for the symbol font!
+ bool IsNoSymbol() const
+ { return RTL_TEXTENCODING_SYMBOL != m_pFnt->GetCharSet( m_pFnt->GetActual() ); }
+
+ void NoteAnimation() const;
+
+ // Home is where Your heart is...
+ SwTextFrame *GetTextFrame() { return m_pFrame; }
+ const SwTextFrame *GetTextFrame() const { return m_pFrame; }
+
+ bool HasHint(TextFrameIndex nPos) const;
+
+ // If Kana Compression is enabled, a minimum and maximum portion width
+ // is calculated. We format lines with minimal size and share remaining
+ // space among compressed kanas.
+ // During formatting, the maximum values of compressible portions are
+ // stored in m_aMaxWidth and discarded after a line has been formatted.
+ void SetMaxWidthDiff( const SwLinePortion *nKey, sal_uInt16 nVal )
+ {
+ m_aMaxWidth.insert( std::make_pair( nKey, nVal ) );
+ };
+ sal_uInt16 GetMaxWidthDiff( const SwLinePortion *nKey )
+ {
+ SwTextPortionMap::iterator it = m_aMaxWidth.find( nKey );
+
+ if( it != m_aMaxWidth.end() )
+ return it->second;
+ else
+ return 0;
+ };
+ void ResetMaxWidthDiff()
+ {
+ m_aMaxWidth.clear();
+ };
+ bool CompressLine()
+ {
+ return !m_aMaxWidth.empty();
+ };
+
+ // Feature: Kana Compression
+
+ sal_uInt16 GetKanaIdx() const { return m_nKanaIdx; }
+ void ResetKanaIdx(){ m_nKanaIdx = 0; }
+ void SetKanaIdx( sal_uInt16 nNew ) { m_nKanaIdx = nNew; }
+ void IncKanaIdx() { ++m_nKanaIdx; }
+ void SetKanaComp( std::deque<sal_uInt16> *pNew ){ m_pKanaComp = pNew; }
+ std::deque<sal_uInt16>* GetpKanaComp() const { return m_pKanaComp; }
+ sal_uInt16 GetKanaComp() const
+ { return ( m_pKanaComp && m_nKanaIdx < m_pKanaComp->size() )
+ ? (*m_pKanaComp)[m_nKanaIdx] : 0; }
+
+ const std::shared_ptr<const vcl::text::TextLayoutCache>& GetCachedVclData() const
+ {
+ return m_pCachedVclData;
+ }
+ void SetCachedVclData(std::shared_ptr<const vcl::text::TextLayoutCache> const& pCachedVclData)
+ {
+ m_pCachedVclData = pCachedVclData;
+ }
+};
+
+class SwTextPaintInfo : public SwTextSizeInfo
+{
+ sw::WrongListIterator *m_pWrongList;
+ sw::WrongListIterator *m_pGrammarCheckList;
+ sw::WrongListIterator *m_pSmartTags;
+ std::vector<tools::Long>* m_pSpaceAdd;
+ const SvxBrushItem *m_pBrushItem; // For the background
+ SwTextFly m_aTextFly; // Calculate the FlyFrame
+ Point m_aPos; // Paint position
+ SwRect m_aPaintRect; // Original paint rect (from Layout paint)
+
+ sal_uInt16 m_nSpaceIdx;
+ void DrawText_(const OUString &rText, const SwLinePortion &rPor,
+ const TextFrameIndex nIdx, const TextFrameIndex nLen,
+ const bool bKern, const bool bWrong = false,
+ const bool bSmartTag = false,
+ const bool bGrammarCheck = false );
+
+ SwTextPaintInfo &operator=(const SwTextPaintInfo&) = delete;
+
+protected:
+ SwTextPaintInfo()
+ : m_pWrongList(nullptr)
+ , m_pGrammarCheckList(nullptr)
+ , m_pSmartTags(nullptr)
+ , m_pSpaceAdd(nullptr)
+ , m_pBrushItem(nullptr)
+ , m_nSpaceIdx(0)
+ {}
+
+public:
+ SwTextPaintInfo( const SwTextPaintInfo &rInf );
+ SwTextPaintInfo( const SwTextPaintInfo &rInf, const OUString* pText );
+
+ void CtorInitTextPaintInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame, const SwRect &rPaint );
+
+ const SvxBrushItem *GetBrushItem() const { return m_pBrushItem; }
+
+ SwTextPaintInfo( SwTextFrame *pFrame, const SwRect &rPaint );
+
+ SwTwips X() const { return m_aPos.X(); }
+ void X( const tools::Long nNew ) { m_aPos.setX(nNew); }
+ SwTwips Y() const { return m_aPos.Y(); }
+ void Y( const SwTwips nNew ) { m_aPos.setY(nNew); }
+
+ SwTextFly& GetTextFly() { return m_aTextFly; }
+ const SwTextFly& GetTextFly() const { return m_aTextFly; }
+ inline void DrawText( const OUString &rText, const SwLinePortion &rPor,
+ TextFrameIndex nIdx = TextFrameIndex(0),
+ TextFrameIndex nLen = TextFrameIndex(COMPLETE_STRING),
+ const bool bKern = false) const;
+ inline void DrawText( const SwLinePortion &rPor, TextFrameIndex nLen,
+ const bool bKern = false ) const;
+ inline void DrawMarkedText( const SwLinePortion &rPor, TextFrameIndex nLen,
+ const bool bWrong,
+ const bool bSmartTags,
+ const bool bGrammarCheck ) const;
+
+ void DrawRect( const SwRect &rRect, bool bRetouche ) const;
+
+ void DrawTab( const SwLinePortion &rPor ) const;
+ void DrawLineBreak( const SwLinePortion &rPor ) const;
+ void DrawRedArrow( const SwLinePortion &rPor ) const;
+ void DrawPostIts( bool bScript ) const;
+ void DrawBackground( const SwLinePortion &rPor, const Color *pColor=nullptr ) const;
+ void DrawViewOpt( const SwLinePortion &rPor, PortionType nWhich, const Color *pColor=nullptr ) const;
+ void DrawBackBrush( const SwLinePortion &rPor ) const;
+
+ /**
+ * Draw character border around a line portion.
+ *
+ * @param[in] rPor line portion around which border have to be drawn.
+ **/
+ void DrawBorder( const SwLinePortion &rPor ) const;
+
+ void DrawCheckBox(const SwFieldFormCheckboxPortion &rPor, bool bChecked) const;
+
+ /**
+ * Calculate the rectangular area where the portion takes place.
+ * @param[in] rPor portion for which the method specify the painting area
+ * @param[out] pRect whole area of the portion
+ * @param[out] pIntersect part of the portion area clipped by OutputDevice's clip region
+ * @param[in] bInsideBox area of portion's content, padding and border, but shadow
+ * is excluded (e.g. for background)
+ **/
+ void CalcRect( const SwLinePortion& rPor, SwRect* pRect,
+ SwRect* pIntersect = nullptr, const bool bInsideBox = false ) const;
+
+ inline SwTwips GetPaintOfst() const;
+ inline void SetPaintOfst( const SwTwips nNew );
+ const Point &GetPos() const { return m_aPos; }
+ void SetPos( const Point &rNew ) { m_aPos = rNew; }
+
+ const SwRect &GetPaintRect() const { return m_aPaintRect; }
+
+ // STUFF FOR JUSTIFIED ALIGNMENT
+
+ sal_uInt16 GetSpaceIdx() const { return m_nSpaceIdx; }
+ void ResetSpaceIdx(){m_nSpaceIdx = 0; }
+ void SetSpaceIdx( sal_uInt16 nNew ) { m_nSpaceIdx = nNew; }
+ void IncSpaceIdx() { ++m_nSpaceIdx; }
+ void RemoveFirstSpaceAdd() { m_pSpaceAdd->erase( m_pSpaceAdd->begin() ); }
+ tools::Long GetSpaceAdd() const
+ { return ( m_pSpaceAdd && m_nSpaceIdx < m_pSpaceAdd->size() )
+ ? (*m_pSpaceAdd)[m_nSpaceIdx] : 0; }
+
+ void SetpSpaceAdd( std::vector<tools::Long>* pNew ){ m_pSpaceAdd = pNew; }
+ std::vector<tools::Long>* GetpSpaceAdd() const { return m_pSpaceAdd; }
+
+ void SetWrongList(sw::WrongListIterator *const pNew) { m_pWrongList = pNew; }
+ sw::WrongListIterator* GetpWrongList() const { return m_pWrongList; }
+
+ void SetGrammarCheckList(sw::WrongListIterator *const pNew) { m_pGrammarCheckList = pNew; }
+ sw::WrongListIterator* GetGrammarCheckList() const { return m_pGrammarCheckList; }
+
+ void SetSmartTags(sw::WrongListIterator *const pNew) { m_pSmartTags = pNew; }
+ sw::WrongListIterator* GetSmartTags() const { return m_pSmartTags; }
+};
+
+class SwTextFormatInfo : public SwTextPaintInfo
+{
+ // temporary arguments for hyphenation
+ css::beans::PropertyValues m_aHyphVals;
+
+ SwLineLayout *m_pRoot; // The Root of the current line (pCurr)
+ SwLinePortion *m_pLast; // The last Portion
+ SwFlyPortion *m_pFly; // The following FlyPortion
+ SwLinePortion *m_pUnderflow; // Underflow: Last Portion
+ SwLinePortion *m_pRest; // The Rest is the start of the next Line
+
+ SwTabPortion *m_pLastTab; // The _last_ TabPortion
+
+ TextFrameIndex m_nSoftHyphPos; ///< SoftHyphPos for Hyphenation
+ TextFrameIndex m_nLineStart; ///< Current line start in rText
+ TextFrameIndex m_nUnderScorePos; ///< enlarge repaint if underscore has been found
+ TextFrameIndex m_nLastBookmarkPos; ///< need to check for bookmarks at every portion
+ // #i34348# Changed type from sal_uInt16 to SwTwips
+ SwTwips m_nLeft; // Left margin
+ SwTwips m_nRight; // Right margin
+ SwTwips m_nFirst; // EZE
+ /// First or left margin, depending on context.
+ SwTwips m_nLeftMargin = 0;
+ sal_uInt16 m_nRealWidth; // "real" line width
+ sal_uInt16 m_nWidth; // "virtual" line width
+ sal_uInt16 m_nLineHeight; // Final height after CalcLine
+ sal_uInt16 m_nLineNetHeight; // line height without spacing
+ sal_uInt16 m_nForcedLeftMargin; // Shift of left margin due to frame
+
+ bool m_bFull : 1; // Line is full
+ bool m_bFootnoteDone : 1; // Footnote already formatted
+ bool m_bErgoDone : 1; // ErgoDone already formatted
+ bool m_bNumDone : 1; // bNumDone already formatted
+ bool m_bArrowDone : 1; // Arrow to the left for scrolling paragraphs
+ bool m_bStop : 1; // Cancel immediately, discarding the line
+ bool m_bNewLine : 1; // Format another line
+ bool m_bShift : 1; // Position change: Repaint until further notice
+ bool m_bUnderflow : 1; // Context: Underflow() ?
+ bool m_bInterHyph : 1; // Interactive hyphenation?
+ bool m_bAutoHyph : 1; // Automatic hyphenation?
+ bool m_bDropInit : 1; // Set DropWidth
+ bool m_bQuick : 1; // FormatQuick()
+ bool m_bNoEndHyph : 1; // Switch off hyphenation at the line end (due to MaxHyphens)
+ bool m_bNoMidHyph : 1; // Switch off hyphenation before flys (due to MaxHyphens)
+ bool m_bIgnoreFly : 1; // FitToContent ignores flys
+ bool m_bFakeLineStart : 1; // String has been replaced by field portion
+ // info structure only pretends that we are at
+ // the beginning of a line
+ bool m_bTabOverflow : 1; // Tabs are expanding after the end margin
+ bool m_bTestFormat : 1; // Test formatting from WouldFit, no notification etc.
+
+ sal_Unicode m_cTabDecimal; // the current decimal delimiter
+ sal_Unicode m_cHookChar; // For tabs in fields etc.
+ sal_uInt8 m_nMaxHyph; // Max. line count of followup hyphenations
+
+ // Hyphenating ...
+ bool InitHyph( const bool bAuto = false );
+ bool CheckFootnotePortion_( SwLineLayout const * pCurr );
+
+public:
+ void CtorInitTextFormatInfo( OutputDevice* pRenderContext, SwTextFrame *pFrame, const bool bInterHyph = false,
+ const bool bQuick = false, const bool bTst = false );
+ SwTextFormatInfo(OutputDevice* pRenderContext, SwTextFrame *pFrame, const bool bInterHyphL = false,
+ const bool bQuickL = false, const bool bTst = false);
+
+ // For the formatting inside a double line in a line (multi-line portion)
+ // we need a modified text-format-info:
+ SwTextFormatInfo( const SwTextFormatInfo& rInf, SwLineLayout& rLay,
+ SwTwips nActWidth );
+
+ sal_uInt16 Width() const { return m_nWidth; }
+ void Width( const sal_uInt16 nNew ) { m_nWidth = nNew; }
+ void Init();
+
+ /**
+ * Returns the distance between the current horizontal position and the end
+ * of the line.
+ */
+ SwTwips GetLineWidth();
+
+ // Returns the first changed position of the paragraph
+ inline TextFrameIndex GetReformatStart() const;
+
+ // Margins
+ SwTwips Left() const { return m_nLeft; }
+ void Left( const SwTwips nNew ) { m_nLeft = nNew; }
+ SwTwips Right() const { return m_nRight; }
+ void Right( const SwTwips nNew ) { m_nRight = nNew; }
+ SwTwips First() const { return m_nFirst; }
+ void First( const SwTwips nNew ) { m_nFirst = nNew; }
+ void LeftMargin( const SwTwips nNew) { m_nLeftMargin = nNew; }
+ sal_uInt16 RealWidth() const { return m_nRealWidth; }
+ void RealWidth( const sal_uInt16 nNew ) { m_nRealWidth = nNew; }
+ sal_uInt16 ForcedLeftMargin() const { return m_nForcedLeftMargin; }
+ void ForcedLeftMargin( const sal_uInt16 nN ) { m_nForcedLeftMargin = nN; }
+
+ sal_uInt8 &MaxHyph() { return m_nMaxHyph; }
+ const sal_uInt8 &MaxHyph() const { return m_nMaxHyph; }
+
+ SwLineLayout *GetRoot() { return m_pRoot; }
+ const SwLineLayout *GetRoot() const { return m_pRoot; }
+
+ void SetRoot( SwLineLayout *pNew ) { m_pRoot = pNew; }
+ SwLinePortion *GetLast() { return m_pLast; }
+ void SetLast( SwLinePortion *pNewLast ) { m_pLast = pNewLast; }
+ bool IsFull() const { return m_bFull; }
+ void SetFull( const bool bNew ) { m_bFull = bNew; }
+ bool IsHyphForbud() const
+ { return m_pFly ? m_bNoMidHyph : m_bNoEndHyph; }
+ void ChkNoHyph( const sal_uInt8 bEnd, const sal_uInt8 bMid )
+ { m_bNoEndHyph = (m_nMaxHyph && bEnd >= m_nMaxHyph);
+ m_bNoMidHyph = (m_nMaxHyph && bMid >= m_nMaxHyph); }
+ bool IsIgnoreFly() const { return m_bIgnoreFly; }
+ void SetIgnoreFly( const bool bNew ) { m_bIgnoreFly = bNew; }
+ bool IsFakeLineStart() const { return m_bFakeLineStart; }
+ void SetFakeLineStart( const bool bNew ) { m_bFakeLineStart = bNew; }
+ bool IsStop() const { return m_bStop; }
+ void SetStop( const bool bNew ) { m_bStop = bNew; }
+ SwLinePortion *GetRest() { return m_pRest; }
+ void SetRest( SwLinePortion *pNewRest ) { m_pRest = pNewRest; }
+ bool IsNewLine() const { return m_bNewLine; }
+ void SetNewLine( const bool bNew ) { m_bNewLine = bNew; }
+ bool IsShift() const { return m_bShift; }
+ void SetShift( const bool bNew ) { m_bShift = bNew; }
+ bool IsInterHyph() const { return m_bInterHyph; }
+ bool IsUnderflow() const { return m_bUnderflow; }
+ void ClrUnderflow() { m_bUnderflow = false; }
+ bool IsDropInit() const { return m_bDropInit; }
+ void SetDropInit( const bool bNew ) { m_bDropInit = bNew; }
+ bool IsQuick() const { return m_bQuick; }
+ bool IsTest() const { return m_bTestFormat; }
+
+ TextFrameIndex GetLineStart() const { return m_nLineStart; }
+ void SetLineStart(TextFrameIndex const nNew) { m_nLineStart = nNew; }
+
+ // these are used during fly calculation
+ sal_uInt16 GetLineHeight() const { return m_nLineHeight; }
+ void SetLineHeight( const sal_uInt16 nNew ) { m_nLineHeight = nNew; }
+ sal_uInt16 GetLineNetHeight() const { return m_nLineNetHeight; }
+ void SetLineNetHeight( const sal_uInt16 nNew ) { m_nLineNetHeight = nNew; }
+
+ const SwLinePortion *GetUnderflow() const { return m_pUnderflow; }
+ SwLinePortion *GetUnderflow() { return m_pUnderflow; }
+ void SetUnderflow( SwLinePortion *pNew )
+ { m_pUnderflow = pNew; m_bUnderflow = true; }
+ TextFrameIndex GetSoftHyphPos() const { return m_nSoftHyphPos; }
+ void SetSoftHyphPos(TextFrameIndex const nNew) { m_nSoftHyphPos = nNew; }
+
+ inline void SetParaFootnote();
+
+ // FlyFrames
+ SwFlyPortion *GetFly() { return m_pFly; }
+ void SetFly( SwFlyPortion *pNew ) { m_pFly = pNew; }
+
+ inline const SwAttrSet& GetCharAttr() const;
+
+ // Tabs
+ SwTabPortion *GetLastTab() { return m_pLastTab; }
+ void SetLastTab( SwTabPortion *pNew ) { m_pLastTab = pNew; }
+ sal_Unicode GetTabDecimal() const { return m_cTabDecimal; }
+ void SetTabDecimal( const sal_Unicode cNew ) { m_cTabDecimal = cNew;}
+
+ void ClearHookChar() { m_cHookChar = 0; }
+ void SetHookChar( const sal_Unicode cNew ) { m_cHookChar = cNew; }
+ sal_Unicode GetHookChar() const { return m_cHookChar; }
+
+ // Done-Flags
+ bool IsFootnoteDone() const { return m_bFootnoteDone; }
+ void SetFootnoteDone( const bool bNew ) { m_bFootnoteDone = bNew; }
+ bool IsErgoDone() const { return m_bErgoDone; }
+ void SetErgoDone( const bool bNew ) { m_bErgoDone = bNew; }
+ bool IsNumDone() const { return m_bNumDone; }
+ void SetNumDone( const bool bNew ) { m_bNumDone = bNew; }
+ bool IsArrowDone() const { return m_bArrowDone; }
+ void SetArrowDone( const bool bNew ) { m_bArrowDone = bNew; }
+
+ bool CheckCurrentPosBookmark();
+
+ // For SwTextPortion::Hyphenate
+ bool ChgHyph( const bool bNew );
+
+ // Should the hyphenate helper be discarded?
+ bool IsHyphenate() const;
+ TextFrameIndex GetUnderScorePos() const { return m_nUnderScorePos; }
+ void SetUnderScorePos(TextFrameIndex const nNew) { m_nUnderScorePos = nNew; }
+
+ // Calls HyphenateWord() of Hyphenator
+ css::uno::Reference< css::linguistic2::XHyphenatedWord >
+ HyphWord( const OUString &rText, const sal_Int32 nMinTrail );
+ const css::beans::PropertyValues & GetHyphValues() const;
+
+ bool CheckFootnotePortion( SwLineLayout const * pCurr )
+ { return IsFootnoteInside() && CheckFootnotePortion_( pCurr ); }
+
+ // Dropcaps called by SwTextFormatter::CTOR
+ const SwFormatDrop *GetDropFormat() const;
+
+ // Sets the last SwKernPortion as pLast, if it is followed by empty portions
+ bool LastKernPortion();
+
+ // Looks for tabs, TabDec, TXTATR and BRK from nIdx until nEnd.
+ // Return: Position; sets cHookChar if necessary
+ TextFrameIndex ScanPortionEnd(TextFrameIndex nStart, TextFrameIndex nEnd);
+
+ void SetTabOverflow( bool bOverflow ) { m_bTabOverflow = bOverflow; }
+ bool IsTabOverflow() const { return m_bTabOverflow; }
+
+};
+
+/**
+ * For the text replacement and restoration of SwTextSizeInfo.
+ * The way this is done is a bit of a hack: Although rInf is const we change it
+ * anyway.
+ * Because rInf is restored again in the DTOR, we can do this.
+ * You could call it a "logical const", if you wish.
+ */
+class SwTextSlot final
+{
+ OUString aText;
+ std::shared_ptr<const vcl::text::TextLayoutCache> m_pOldCachedVclData;
+ const OUString *pOldText;
+ sw::WrongListIterator * m_pOldSmartTagList;
+ sw::WrongListIterator * m_pOldGrammarCheckList;
+ std::unique_ptr<SwWrongList> m_pTempList;
+ std::unique_ptr<sw::WrongListIterator> m_pTempIter;
+ TextFrameIndex nIdx;
+ TextFrameIndex nLen;
+ bool bOn;
+ SwTextSizeInfo *pInf;
+
+public:
+ // The replacement string originates either from the portion via GetExpText()
+ // or from the rCh, if it is not empty.
+ SwTextSlot( const SwTextSizeInfo *pNew, const SwLinePortion *pPor, bool bTextLen,
+ bool bExgLists, OUString const & rCh = OUString() );
+ ~SwTextSlot();
+};
+
+class SwFontSave
+{
+ SwTextSizeInfo *pInf;
+ SwFont *pFnt;
+ SwAttrIter *pIter;
+public:
+ SwFontSave( const SwTextSizeInfo &rInf, SwFont *pFnt,
+ SwAttrIter* pItr = nullptr );
+ ~SwFontSave();
+};
+
+inline sal_uInt16 SwTextSizeInfo::GetAscent() const
+{
+ assert(GetOut());
+ return const_cast<SwFont*>(GetFont())->GetAscent( m_pVsh, *GetOut() );
+}
+
+inline sal_uInt16 SwTextSizeInfo::GetTextHeight() const
+{
+ assert(GetOut());
+ return const_cast<SwFont*>(GetFont())->GetHeight( m_pVsh, *GetOut() );
+}
+
+inline sal_uInt16 SwTextSizeInfo::GetHangingBaseline() const
+{
+ assert(GetOut());
+ return const_cast<SwFont*>(GetFont())->GetHangingBaseline( m_pVsh, *GetOut() );
+}
+
+inline SwPosSize SwTextSizeInfo::GetTextSize( const OUString &rText ) const
+{
+ return GetTextSize(m_pOut, nullptr, rText, TextFrameIndex(0), TextFrameIndex(rText.getLength()));
+}
+
+inline SwPosSize SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI,
+ TextFrameIndex const nNewIdx,
+ TextFrameIndex const nNewLen) const
+{
+ return GetTextSize( m_pOut, pSI, *m_pText, nNewIdx, nNewLen );
+}
+
+inline SwTwips SwTextPaintInfo::GetPaintOfst() const
+{
+ return GetParaPortion()->GetRepaint().GetOffset();
+}
+
+inline void SwTextPaintInfo::SetPaintOfst( const SwTwips nNew )
+{
+ GetParaPortion()->GetRepaint().SetOffset( nNew );
+}
+
+inline void SwTextPaintInfo::DrawText( const OUString &rText,
+ const SwLinePortion &rPor,
+ const TextFrameIndex nStart, const TextFrameIndex nLength,
+ const bool bKern ) const
+{
+ const_cast<SwTextPaintInfo*>(this)->DrawText_( rText, rPor, nStart, nLength, bKern );
+}
+
+inline void SwTextPaintInfo::DrawText( const SwLinePortion &rPor,
+ const TextFrameIndex nLength, const bool bKern ) const
+{
+ const_cast<SwTextPaintInfo*>(this)->DrawText_( *m_pText, rPor, m_nIdx, nLength, bKern );
+}
+
+inline void SwTextPaintInfo::DrawMarkedText( const SwLinePortion &rPor,
+ const TextFrameIndex nLength,
+ const bool bWrong,
+ const bool bSmartTags,
+ const bool bGrammarCheck ) const
+{
+ const_cast<SwTextPaintInfo*>(this)->DrawText_( *m_pText, rPor, m_nIdx, nLength, false/*bKern*/, bWrong, bSmartTags, bGrammarCheck );
+}
+
+inline TextFrameIndex SwTextFormatInfo::GetReformatStart() const
+{
+ return GetParaPortion()->GetReformat().Start();
+}
+
+inline const SwAttrSet& SwTextFormatInfo::GetCharAttr() const
+{
+ // sw_redlinehide: this is used for numbering/footnote number portions, so:
+ return GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet();
+}
+
+inline void SwTextFormatInfo::SetParaFootnote()
+{
+ GetTextFrame()->SetFootnote( true );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx
new file mode 100644
index 000000000..a952ce764
--- /dev/null
+++ b/sw/source/core/text/itradj.cxx
@@ -0,0 +1,841 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+
+#include <IDocumentSettingAccess.hxx>
+#include <doc.hxx>
+
+#include "itrtxt.hxx"
+#include "porglue.hxx"
+#include "porlay.hxx"
+#include "porfly.hxx"
+#include "pormulti.hxx"
+#include "portab.hxx"
+#include <memory>
+
+#define MIN_TAB_WIDTH 60
+
+using namespace ::com::sun::star;
+
+void SwTextAdjuster::FormatBlock( )
+{
+ // Block format does not apply to the last line.
+ // And for tabs it doesn't exist out of tradition
+ // If we have Flys we continue.
+
+ const SwLinePortion *pFly = nullptr;
+
+ bool bSkip = !IsLastBlock() &&
+ m_nStart + m_pCurr->GetLen() >= TextFrameIndex(GetInfo().GetText().getLength());
+
+ // Multi-line fields are tricky, because we need to check whether there are
+ // any other text portions in the paragraph.
+ if( bSkip )
+ {
+ const SwLineLayout *pLay = m_pCurr->GetNext();
+ while( pLay && !pLay->GetLen() )
+ {
+ const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ while( pPor && bSkip )
+ {
+ if( pPor->InTextGrp() )
+ bSkip = false;
+ pPor = pPor->GetNextPortion();
+ }
+ pLay = bSkip ? pLay->GetNext() : nullptr;
+ }
+ }
+
+ if( bSkip )
+ {
+ if( !GetInfo().GetParaPortion()->HasFly() )
+ {
+ if( IsLastCenter() )
+ CalcFlyAdjust( m_pCurr );
+ m_pCurr->FinishSpaceAdd();
+ return;
+ }
+ else
+ {
+ const SwLinePortion *pTmpFly = nullptr;
+
+ // End at the last Fly
+ const SwLinePortion *pPos = m_pCurr->GetFirstPortion();
+ while( pPos )
+ {
+ // Look for the last Fly which has text coming after it:
+ if( pPos->IsFlyPortion() )
+ pTmpFly = pPos; // Found a Fly
+ else if ( pTmpFly && pPos->InTextGrp() )
+ {
+ pFly = pTmpFly; // A Fly with follow-up text!
+ pTmpFly = nullptr;
+ }
+ pPos = pPos->GetNextPortion();
+ }
+ // End if we didn't find one
+ if( !pFly )
+ {
+ if( IsLastCenter() )
+ CalcFlyAdjust( m_pCurr );
+ m_pCurr->FinishSpaceAdd();
+ return;
+ }
+ }
+ }
+
+ const TextFrameIndex nOldIdx = GetInfo().GetIdx();
+ GetInfo().SetIdx( m_nStart );
+ CalcNewBlock( m_pCurr, pFly );
+ GetInfo().SetIdx( nOldIdx );
+ GetInfo().GetParaPortion()->GetRepaint().SetOffset(0);
+}
+
+static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr,
+ sal_Int32& rKashidas, TextFrameIndex& nGluePortion)
+{
+ // i60594 validate Kashida justification
+ TextFrameIndex nIdx = rItr.GetStart();
+ TextFrameIndex nEnd = rItr.GetEnd();
+
+ // Note on calling KashidaJustify():
+ // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
+ // total number of kashida positions, or the number of kashida positions after some positions
+ // have been dropped.
+ // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
+ rKashidas = rSI.KashidaJustify(nullptr, rItr.GetStart(), rItr.GetLength());
+
+ if (rKashidas <= 0) // nothing to do
+ return true;
+
+ // kashida positions found in SwScriptInfo are not necessarily valid in every font
+ // if two characters are replaced by a ligature glyph, there will be no place for a kashida
+ std::vector<TextFrameIndex> aKashidaPos;
+ rSI.GetKashidaPositions(nIdx, rItr.GetLength(), aKashidaPos);
+ assert(aKashidaPos.size() >= o3tl::make_unsigned(rKashidas));
+ std::vector<TextFrameIndex> aKashidaPosDropped(aKashidaPos.size());
+ sal_Int32 nKashidaIdx = 0;
+ while ( rKashidas && nIdx < nEnd )
+ {
+ rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
+ TextFrameIndex nNext = rItr.GetNextAttr();
+
+ // is there also a script change before?
+ // if there is, nNext should point to the script change
+ TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
+ if( nNextScript < nNext )
+ nNext = nNextScript;
+
+ if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
+ nNext = nEnd;
+ sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nIdx, nNext - nIdx);
+ if (nKashidasInAttr > 0)
+ {
+ // Kashida glyph looks suspicious, skip Kashida justification
+ if ( rInf.GetOut()->GetMinKashida() <= 0 )
+ {
+ return false;
+ }
+
+ sal_Int32 nKashidasDropped = 0;
+ if ( !SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
+ {
+ nKashidasDropped = nKashidasInAttr;
+ rKashidas -= nKashidasDropped;
+ }
+ else
+ {
+ vcl::text::ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode();
+ rInf.GetOut()->SetLayoutMode ( nOldLayout | vcl::text::ComplexTextLayoutFlags::BiDiRtl );
+ nKashidasDropped = rInf.GetOut()->ValidateKashidas(
+ rInf.GetText(), sal_Int32(nIdx), sal_Int32(nNext - nIdx),
+ nKashidasInAttr,
+ reinterpret_cast<sal_Int32*>(aKashidaPos.data() + nKashidaIdx),
+ reinterpret_cast<sal_Int32*>(aKashidaPosDropped.data()));
+ rInf.GetOut()->SetLayoutMode ( nOldLayout );
+ if ( nKashidasDropped )
+ {
+ rSI.MarkKashidasInvalid(nKashidasDropped, aKashidaPosDropped.data());
+ rKashidas -= nKashidasDropped;
+ nGluePortion -= TextFrameIndex(nKashidasDropped);
+ }
+ }
+ nKashidaIdx += nKashidasInAttr;
+ }
+ nIdx = nNext;
+ }
+
+ // return false if all kashidas have been eliminated
+ return (rKashidas > 0);
+}
+
+static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr, sal_Int32& rKashidas,
+ TextFrameIndex& nGluePortion, const tools::Long nGluePortionWidth, tools::Long& nSpaceAdd )
+{
+ // check kashida width
+ // if width is smaller than minimal kashida width allowed by fonts in the current line
+ // drop one kashida after the other until kashida width is OK
+ while (rKashidas)
+ {
+ bool bAddSpaceChanged = false;
+ TextFrameIndex nIdx = rItr.GetStart();
+ TextFrameIndex nEnd = rItr.GetEnd();
+ while ( nIdx < nEnd )
+ {
+ rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
+ TextFrameIndex nNext = rItr.GetNextAttr();
+
+ // is there also a script change before?
+ // if there is, nNext should point to the script change
+ TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
+ if( nNextScript < nNext )
+ nNext = nNextScript;
+
+ if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
+ nNext = nEnd;
+ sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nIdx, nNext - nIdx);
+
+ tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida();
+ if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
+ {
+ sal_Int32 nKashidasDropped = 0;
+ while ( rKashidas && nGluePortion && nKashidasInAttr > 0 &&
+ nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
+ {
+ --nGluePortion;
+ --rKashidas;
+ --nKashidasInAttr;
+ ++nKashidasDropped;
+ if( !rKashidas || !nGluePortion ) // nothing left, return false to
+ return false; // do regular blank justification
+
+ nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
+ bAddSpaceChanged = true;
+ }
+ if( nKashidasDropped )
+ rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
+ }
+ if ( bAddSpaceChanged )
+ break; // start all over again
+ nIdx = nNext;
+ }
+ if ( !bAddSpaceChanged )
+ break; // everything was OK
+ }
+ return true;
+}
+
+// CalcNewBlock() must only be called _after_ CalcLine()!
+// We always span between two RandPortions or FixPortions (Tabs and Flys).
+// We count the Glues and call ExpandBlock.
+void SwTextAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
+ const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
+{
+ OSL_ENSURE( GetInfo().IsMulti() || SvxAdjust::Block == GetAdjust(),
+ "CalcNewBlock: Why?" );
+ OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
+
+ pCurrent->InitSpaceAdd();
+ TextFrameIndex nGluePortion(0);
+ TextFrameIndex nCharCnt(0);
+ sal_uInt16 nSpaceIdx = 0;
+
+ // i60591: hennerdrews
+ SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
+ SwTextSizeInfo aInf ( GetTextFrame() );
+ SwTextIter aItr ( GetTextFrame(), &aInf );
+
+ if ( rSI.CountKashida() )
+ {
+ while (aItr.GetCurr() != pCurrent && aItr.GetNext())
+ aItr.Next();
+
+ if( bSkipKashida )
+ {
+ rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
+ }
+ else
+ {
+ rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
+ rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
+ }
+ }
+
+ // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
+ if (!bSkipKashida)
+ CalcRightMargin( pCurrent, nReal );
+
+ // #i49277#
+ const bool bDoNotJustifyLinesWithManualBreak =
+ GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK);
+ bool bDoNotJustifyTab = false;
+
+ SwLinePortion *pPos = pCurrent->GetNextPortion();
+
+ while( pPos )
+ {
+ if ( ( bDoNotJustifyLinesWithManualBreak || bDoNotJustifyTab ) &&
+ pPos->IsBreakPortion() && !IsLastBlock() )
+ {
+ pCurrent->FinishSpaceAdd();
+ break;
+ }
+
+ switch ( pPos->GetWhichPor() )
+ {
+ case PortionType::TabCenter :
+ case PortionType::TabRight :
+ case PortionType::TabDecimal :
+ bDoNotJustifyTab = true;
+ break;
+ case PortionType::TabLeft :
+ case PortionType::Break:
+ bDoNotJustifyTab = false;
+ break;
+ default: break;
+ }
+
+ if ( pPos->InTextGrp() )
+ nGluePortion = nGluePortion + static_cast<SwTextPortion*>(pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
+ else if( pPos->IsMultiPortion() )
+ {
+ SwMultiPortion* pMulti = static_cast<SwMultiPortion*>(pPos);
+ // a multiportion with a tabulator inside breaks the text adjustment
+ // a ruby portion will not be stretched by text adjustment
+ // a double line portion takes additional space for each blank
+ // in the wider line
+ if( pMulti->HasTabulator() )
+ {
+ if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
+ pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
+
+ nSpaceIdx++;
+ nGluePortion = TextFrameIndex(0);
+ nCharCnt = TextFrameIndex(0);
+ }
+ else if( pMulti->IsDouble() )
+ nGluePortion = nGluePortion + static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
+ else if ( pMulti->IsBidi() )
+ nGluePortion = nGluePortion + static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt( GetInfo() ); // i60594
+ }
+
+ if( pPos->InGlueGrp() )
+ {
+ if( pPos->InFixMargGrp() )
+ {
+ if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
+ pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
+
+ const tools::Long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
+ SPACING_PRECISION_FACTOR;
+
+ sal_Int32 nKashidas = 0;
+ if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
+ {
+ // kashida positions found in SwScriptInfo are not necessarily valid in every font
+ // if two characters are replaced by a ligature glyph, there will be no place for a kashida
+ if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
+ {
+ // all kashida positions are invalid
+ // do regular blank justification
+ pCurrent->FinishSpaceAdd();
+ GetInfo().SetIdx( m_nStart );
+ CalcNewBlock( pCurrent, pStopAt, nReal, true );
+ return;
+ }
+ }
+
+ if( nGluePortion )
+ {
+ tools::Long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
+
+ // i60594
+ if( rSI.CountKashida() && !bSkipKashida )
+ {
+ if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
+ {
+ // no kashidas left
+ // do regular blank justification
+ pCurrent->FinishSpaceAdd();
+ GetInfo().SetIdx( m_nStart );
+ CalcNewBlock( pCurrent, pStopAt, nReal, true );
+ return;
+ }
+ }
+
+ pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
+ pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
+ }
+ else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
+ {
+ const tools::Long nSpaceAdd = - nGluePortionWidth / (sal_Int32(nCharCnt) - 1);
+ pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
+ pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
+ }
+
+ nSpaceIdx++;
+ nGluePortion = TextFrameIndex(0);
+ nCharCnt = TextFrameIndex(0);
+ }
+ else
+ ++nGluePortion;
+ }
+ GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
+ if ( pPos == pStopAt )
+ {
+ pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
+ break;
+ }
+ pPos = pPos->GetNextPortion();
+ }
+}
+
+SwTwips SwTextAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
+{
+ OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
+ OSL_ENSURE( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
+
+ pCurrent->SetKanaComp( std::make_unique<std::deque<sal_uInt16>>() );
+
+ const sal_uInt16 nNull = 0;
+ size_t nKanaIdx = 0;
+ tools::Long nKanaDiffSum = 0;
+ SwTwips nRepaintOfst = 0;
+ SwTwips nX = 0;
+ bool bNoCompression = false;
+
+ // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
+ CalcRightMargin( pCurrent );
+
+ SwLinePortion* pPos = pCurrent->GetNextPortion();
+
+ while( pPos )
+ {
+ if ( pPos->InTextGrp() )
+ {
+ // get maximum portion width from info structure, calculated
+ // during text formatting
+ sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
+
+ // check, if information is stored under other key
+ if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
+ nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
+
+ // calculate difference between portion width and max. width
+ nKanaDiffSum += nMaxWidthDiff;
+
+ // we store the beginning of the first compressible portion
+ // for repaint
+ if ( nMaxWidthDiff && !nRepaintOfst )
+ nRepaintOfst = nX + GetLeftMargin();
+ }
+ else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
+ {
+ if ( nKanaIdx == pCurrent->GetKanaComp().size() )
+ pCurrent->GetKanaComp().push_back( nNull );
+
+ tools::Long nRest;
+
+ if ( pPos->InTabGrp() )
+ {
+ nRest = ! bNoCompression &&
+ ( pPos->Width() > MIN_TAB_WIDTH ) ?
+ pPos->Width() - MIN_TAB_WIDTH :
+ 0;
+
+ // for simplifying the handling of left, right ... tabs,
+ // we do expand portions, which are lying behind
+ // those special tabs
+ bNoCompression = !pPos->IsTabLeftPortion();
+ }
+ else
+ {
+ nRest = ! bNoCompression ?
+ static_cast<SwGluePortion*>(pPos)->GetPrtGlue() :
+ 0;
+
+ bNoCompression = false;
+ }
+
+ if( nKanaDiffSum )
+ {
+ sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
+
+ if ( nCompress >= 10000 )
+ // kanas can be expanded to 100%, and there is still
+ // some space remaining
+ nCompress = 0;
+
+ else
+ nCompress = 10000 - nCompress;
+
+ ( pCurrent->GetKanaComp() )[ nKanaIdx ] = o3tl::narrowing<sal_uInt16>(nCompress);
+ nKanaDiffSum = 0;
+ }
+
+ nKanaIdx++;
+ }
+
+ nX += pPos->Width();
+ pPos = pPos->GetNextPortion();
+ }
+
+ // set portion width
+ nKanaIdx = 0;
+ sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
+ pPos = pCurrent->GetNextPortion();
+ tools::Long nDecompress = 0;
+
+ while( pPos )
+ {
+ if ( pPos->InTextGrp() )
+ {
+ const SwTwips nMinWidth = pPos->Width();
+
+ // get maximum portion width from info structure, calculated
+ // during text formatting
+ SwTwips nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
+
+ // check, if information is stored under other key
+ if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
+ nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
+ pPos->Width( nMinWidth +
+ ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
+ nDecompress += pPos->Width() - nMinWidth;
+ }
+ else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
+ {
+ pPos->Width(pPos->Width() - nDecompress);
+
+ if ( pPos->InTabGrp() )
+ // set fix width to width
+ static_cast<SwTabPortion*>(pPos)->SetFixWidth( pPos->Width() );
+
+ if ( ++nKanaIdx < pCurrent->GetKanaComp().size() )
+ nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
+
+ nDecompress = 0;
+ }
+ pPos = pPos->GetNextPortion();
+ }
+
+ return nRepaintOfst;
+}
+
+SwMarginPortion *SwTextAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
+ SwTwips nReal )
+{
+ tools::Long nRealWidth;
+ const sal_uInt16 nRealHeight = GetLineHeight();
+ const sal_uInt16 nLineHeight = pCurrent->Height();
+
+ sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
+ SwLinePortion *pLast = pCurrent->FindLastPortion();
+
+ if( GetInfo().IsMulti() )
+ nRealWidth = nReal;
+ else
+ {
+ nRealWidth = GetLineWidth();
+ // For each FlyFrame extending into the right margin, we create a FlyPortion.
+ const tools::Long nLeftMar = GetLeftMargin();
+ SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
+ nRealWidth - nPrtWidth, nLineHeight );
+
+ SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
+ while( pFly && tools::Long( nPrtWidth )< nRealWidth )
+ {
+ pLast->Append( pFly );
+ pLast = pFly;
+ if( pFly->GetFix() > nPrtWidth )
+ pFly->Width( ( pFly->GetFix() - nPrtWidth) + pFly->Width() + 1);
+ nPrtWidth += pFly->Width() + 1;
+ aCurrRect.Left( nLeftMar + nPrtWidth );
+ pFly = CalcFlyPortion( nRealWidth, aCurrRect );
+ }
+ delete pFly;
+ }
+
+ SwMarginPortion *pRight = new SwMarginPortion;
+ pLast->Append( pRight );
+
+ if( tools::Long( nPrtWidth )< nRealWidth )
+ pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
+
+ // pCurrent->Width() is set to the real size, because we attach the
+ // MarginPortions.
+ // This trick gives miraculous results:
+ // If pCurrent->Width() == nRealWidth, then the adjustment gets overruled
+ // implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
+ // line filled with chars.
+
+ pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
+ return pRight;
+}
+
+void SwTextAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent )
+{
+ // 1) We insert a left margin:
+ SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
+ SwGluePortion *pGlue = pLeft; // the last GluePortion
+
+ // 2) We attach a right margin:
+ // CalcRightMargin also calculates a possible overlap with FlyFrames.
+ CalcRightMargin( pCurrent );
+
+ SwLinePortion *pPos = pLeft->GetNextPortion();
+ TextFrameIndex nLen(0);
+
+ // If we only have one line, the text portion is consecutive and we center, then ...
+ bool bComplete = TextFrameIndex(0) == m_nStart;
+ const bool bTabCompat = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
+ bool bMultiTab = false;
+
+ while( pPos )
+ {
+ if ( pPos->IsMultiPortion() && static_cast<SwMultiPortion*>(pPos)->HasTabulator() )
+ bMultiTab = true;
+ else if( pPos->InFixMargGrp() &&
+ ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
+ {
+ // in tab compat mode we do not want to change tab portions
+ // in non tab compat mode we do not want to change margins if we
+ // found a multi portion with tabs
+ if( SvxAdjust::Right == GetAdjust() )
+ static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
+ else
+ {
+ // We set the first text portion to right-aligned and the last one
+ // to left-aligned.
+ // The first text portion gets the whole Glue, but only if we have
+ // more than one line.
+ if (bComplete && TextFrameIndex(GetInfo().GetText().getLength()) == nLen)
+ static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
+ else
+ {
+ if ( ! bTabCompat )
+ {
+ if( pLeft == pGlue )
+ {
+ // If we only have a left and right margin, the
+ // margins share the Glue.
+ if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
+ static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
+ else
+ static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
+ }
+ else
+ {
+ // The last text portion retains its Glue.
+ if( !pPos->IsMarginPortion() )
+ static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
+ }
+ }
+ else
+ static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
+ }
+ }
+
+ pGlue = static_cast<SwGluePortion*>(pPos);
+ bComplete = false;
+ }
+ nLen = nLen + pPos->GetLen();
+ pPos = pPos->GetNextPortion();
+ }
+
+ if( ! bTabCompat && ! bMultiTab && SvxAdjust::Right == GetAdjust() )
+ // portions are moved to the right if possible
+ pLeft->AdjustRight( pCurrent );
+}
+
+void SwTextAdjuster::CalcAdjLine( SwLineLayout *pCurrent )
+{
+ OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
+
+ pCurrent->SetFormatAdj(false);
+
+ SwParaPortion* pPara = GetInfo().GetParaPortion();
+
+ switch( GetAdjust() )
+ {
+ case SvxAdjust::Right:
+ case SvxAdjust::Center:
+ {
+ CalcFlyAdjust( pCurrent );
+ pPara->GetRepaint().SetOffset( 0 );
+ break;
+ }
+ case SvxAdjust::Block:
+ {
+ FormatBlock();
+ break;
+ }
+ default : return;
+ }
+}
+
+// This is a quite complicated calculation: nCurrWidth is the width _before_
+// adding the word, that still fits onto the line! For this reason the FlyPortion's
+// width is still correct if we get a deadlock-situation of:
+// bFirstWord && !WORDFITS
+SwFlyPortion *SwTextAdjuster::CalcFlyPortion( const tools::Long nRealWidth,
+ const SwRect &rCurrRect )
+{
+ SwTextFly aTextFly( GetTextFrame() );
+
+ const sal_uInt16 nCurrWidth = m_pCurr->PrtWidth();
+ SwFlyPortion *pFlyPortion = nullptr;
+
+ SwRect aLineVert( rCurrRect );
+ if ( GetTextFrame()->IsRightToLeft() )
+ GetTextFrame()->SwitchLTRtoRTL( aLineVert );
+ if ( GetTextFrame()->IsVertical() )
+ GetTextFrame()->SwitchHorizontalToVertical( aLineVert );
+
+ // aFlyRect is document-global!
+ SwRect aFlyRect( aTextFly.GetFrame( aLineVert ) );
+
+ if ( GetTextFrame()->IsRightToLeft() )
+ GetTextFrame()->SwitchRTLtoLTR( aFlyRect );
+ if ( GetTextFrame()->IsVertical() )
+ GetTextFrame()->SwitchVerticalToHorizontal( aFlyRect );
+
+ // If a Frame overlapps we open a Portion
+ if( aFlyRect.HasArea() )
+ {
+ // aLocal is frame-local
+ SwRect aLocal( aFlyRect );
+ aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
+ if( nCurrWidth > aLocal.Left() )
+ aLocal.Left( nCurrWidth );
+
+ // If the rect is wider than the line, we adjust it to the right size
+ const tools::Long nLocalWidth = aLocal.Left() + aLocal.Width();
+ if( nRealWidth < nLocalWidth )
+ aLocal.Width( nRealWidth - aLocal.Left() );
+ GetInfo().GetParaPortion()->SetFly();
+ pFlyPortion = new SwFlyPortion( aLocal );
+ pFlyPortion->Height( sal_uInt16( rCurrRect.Height() ) );
+ // The Width could be smaller than the FixWidth, thus:
+ pFlyPortion->AdjFixWidth();
+ }
+ return pFlyPortion;
+}
+
+// CalcDropAdjust is called at the end by Format() if needed
+void SwTextAdjuster::CalcDropAdjust()
+{
+ OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left!=GetAdjust() && SvxAdjust::Block!=GetAdjust(),
+ "CalcDropAdjust: No reason for DropAdjustment." );
+
+ const sal_uInt16 nLineNumber = GetLineNr();
+
+ // 1) Skip dummies
+ Top();
+
+ if( !m_pCurr->IsDummy() || NextLine() )
+ {
+ // Adjust first
+ GetAdjusted();
+
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+
+ // 2) Make sure we include the ropPortion
+ // 3) pLeft is the GluePor preceding the DropPor
+ if( pPor->InGlueGrp() && pPor->GetNextPortion()
+ && pPor->GetNextPortion()->IsDropPortion() )
+ {
+ const SwLinePortion *pDropPor = pPor->GetNextPortion();
+ SwGluePortion *pLeft = static_cast<SwGluePortion*>( pPor );
+
+ // 4) pRight: Find the GluePor coming after the DropPor
+ pPor = pPor->GetNextPortion();
+ while( pPor && !pPor->InFixMargGrp() )
+ pPor = pPor->GetNextPortion();
+
+ SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
+ static_cast<SwGluePortion*>(pPor) : nullptr;
+ if( pRight && pRight != pLeft )
+ {
+ // 5) Calculate nMinLeft. Who is the most to left?
+ const auto nDropLineStart =
+ GetLineStart() + pLeft->Width() + pDropPor->Width();
+ auto nMinLeft = nDropLineStart;
+ for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
+ {
+ if( NextLine() )
+ {
+ // Adjust first
+ GetAdjusted();
+
+ pPor = m_pCurr->GetFirstPortion();
+ const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
+ static_cast<SwMarginPortion*>(pPor) : nullptr;
+ if( !pMar )
+ nMinLeft = 0;
+ else
+ {
+ const auto nLineStart =
+ GetLineStart() + pMar->Width();
+ if( nMinLeft > nLineStart )
+ nMinLeft = nLineStart;
+ }
+ }
+ }
+
+ // 6) Distribute the Glue anew between pLeft and pRight
+ if( nMinLeft < nDropLineStart )
+ {
+ // The Glue is always passed from pLeft to pRight, so that
+ // the text moves to the left.
+ const auto nGlue = nDropLineStart - nMinLeft;
+ if( !nMinLeft )
+ pLeft->MoveAllGlue( pRight );
+ else
+ pLeft->MoveGlue( pRight, nGlue );
+ }
+ }
+ }
+ }
+
+ if( nLineNumber != GetLineNr() )
+ {
+ Top();
+ while( nLineNumber != GetLineNr() && Next() )
+ ;
+ }
+}
+
+void SwTextAdjuster::CalcDropRepaint()
+{
+ Top();
+ SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
+ if( rRepaint.Top() > Y() )
+ rRepaint.Top( Y() );
+ for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
+ NextLine();
+ const SwTwips nBottom = Y() + GetLineHeight() - 1;
+ if( rRepaint.Bottom() < nBottom )
+ rRepaint.Bottom( nBottom );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
new file mode 100644
index 000000000..4f4a840a5
--- /dev/null
+++ b/sw/source/core/text/itratr.cxx
@@ -0,0 +1,1494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <hintids.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <svl/itemiter.hxx>
+#include <svx/svdobj.hxx>
+#include <vcl/svapp.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <fmtornt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtftn.hxx>
+#include <frmatr.hxx>
+#include <frmfmt.hxx>
+#include <fmtfld.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <txatbase.hxx>
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <fldbas.hxx>
+#include <pam.hxx>
+#include "itratr.hxx"
+#include <htmltbl.hxx>
+#include <swtable.hxx>
+#include "redlnitr.hxx"
+#include <redline.hxx>
+#include <fmtsrnd.hxx>
+#include "itrtxt.hxx"
+#include <breakit.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <editeng/lrspitem.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star;
+
+static sal_Int32 GetNextAttrImpl(SwTextNode const* pTextNode,
+ size_t nStartIndex, size_t nEndIndex, sal_Int32 nPosition);
+
+SwAttrIter::SwAttrIter(SwTextNode const * pTextNode)
+ : m_pViewShell(nullptr)
+ , m_pFont(nullptr)
+ , m_pScriptInfo(nullptr)
+ , m_pLastOut(nullptr)
+ , m_nChgCnt(0)
+ , m_nStartIndex(0)
+ , m_nEndIndex(0)
+ , m_nPosition(0)
+ , m_nPropFont(0)
+ , m_pTextNode(pTextNode)
+ , m_pMergedPara(nullptr)
+{
+ m_aFontCacheIds[SwFontScript::Latin] = m_aFontCacheIds[SwFontScript::CJK] = m_aFontCacheIds[SwFontScript::CTL] = nullptr;
+}
+
+SwAttrIter::SwAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const*const pFrame)
+ : m_pViewShell(nullptr)
+ , m_pFont(nullptr)
+ , m_pScriptInfo(nullptr)
+ , m_pLastOut(nullptr)
+ , m_nChgCnt(0)
+ , m_nPropFont(0)
+ , m_pTextNode(&rTextNode)
+ , m_pMergedPara(nullptr)
+{
+ CtorInitAttrIter(rTextNode, rScrInf, pFrame);
+}
+
+void SwAttrIter::Chg( SwTextAttr const *pHt )
+{
+ assert(pHt && m_pFont && "No attribute of font available for change");
+ if( m_pRedline && m_pRedline->IsOn() )
+ m_pRedline->ChangeTextAttr( m_pFont, *pHt, true );
+ else
+ m_aAttrHandler.PushAndChg( *pHt, *m_pFont );
+ m_nChgCnt++;
+}
+
+void SwAttrIter::Rst( SwTextAttr const *pHt )
+{
+ assert(pHt && m_pFont && "No attribute of font available for reset");
+ // get top from stack after removing pHt
+ if( m_pRedline && m_pRedline->IsOn() )
+ m_pRedline->ChangeTextAttr( m_pFont, *pHt, false );
+ else
+ m_aAttrHandler.PopAndChg( *pHt, *m_pFont );
+ m_nChgCnt--;
+}
+
+SwAttrIter::~SwAttrIter()
+{
+ m_pRedline.reset();
+ delete m_pFont;
+}
+
+bool SwAttrIter::MaybeHasHints() const
+{
+ return nullptr != m_pTextNode->GetpSwpHints() || nullptr != m_pMergedPara;
+}
+
+/**
+ * Returns the attribute for a position
+ *
+ * Only if the attribute is exactly at the position @param nPos and
+ * does not have an EndIndex
+ *
+ * We need this function for attributes which should alter formatting without
+ * changing the content of the string.
+ * Such "degenerated" attributes are e.g.: fields which retain expanded text and
+ * line-bound Frames.
+ * In order to avoid ambiguities between different such attributes, we insert a
+ * special character at the start of the string, when creating such an attribute.
+ * The Formatter later on encounters such a special character and retrieves the
+ * degenerate attribute via GetAttr().
+ */
+SwTextAttr *SwAttrIter::GetAttr(TextFrameIndex const nPosition) const
+{
+ std::pair<SwTextNode const*, sal_Int32> const pos( m_pMergedPara
+ ? sw::MapViewToModel(*m_pMergedPara, nPosition)
+ : std::make_pair(m_pTextNode, sal_Int32(nPosition)));
+ return pos.first->GetTextAttrForCharAt(pos.second);
+}
+
+bool SwAttrIter::SeekAndChgAttrIter(TextFrameIndex const nNewPos, OutputDevice* pOut)
+{
+ std::pair<SwTextNode const*, sal_Int32> const pos( m_pMergedPara
+ ? sw::MapViewToModel(*m_pMergedPara, nNewPos)
+ : std::make_pair(m_pTextNode, sal_Int32(nNewPos)));
+ bool bChg = m_nStartIndex && pos.first == m_pTextNode && pos.second == m_nPosition
+ ? m_pFont->IsFntChg()
+ : Seek( nNewPos );
+ if ( m_pLastOut.get() != pOut )
+ {
+ m_pLastOut = pOut;
+ m_pFont->SetFntChg( true );
+ bChg = true;
+ }
+ if( bChg )
+ {
+ // if the change counter is zero, we know the cache id of the wanted font
+ if ( !m_nChgCnt && !m_nPropFont )
+ m_pFont->SetFontCacheId( m_aFontCacheIds[ m_pFont->GetActual() ],
+ m_aFontIdx[ m_pFont->GetActual() ], m_pFont->GetActual() );
+ m_pFont->ChgPhysFnt( m_pViewShell, *pOut );
+ }
+
+ return bChg;
+}
+
+bool SwAttrIter::IsSymbol(TextFrameIndex const nNewPos)
+{
+ Seek( nNewPos );
+ if ( !m_nChgCnt && !m_nPropFont )
+ m_pFont->SetFontCacheId( m_aFontCacheIds[ m_pFont->GetActual() ],
+ m_aFontIdx[ m_pFont->GetActual() ], m_pFont->GetActual() );
+ return m_pFont->IsSymbol( m_pViewShell );
+}
+
+bool SwTextFrame::IsSymbolAt(TextFrameIndex const nPos) const
+{
+ SwTextInfo info(const_cast<SwTextFrame*>(this));
+ SwTextIter iter(const_cast<SwTextFrame*>(this), &info);
+ return iter.IsSymbol(nPos);
+}
+
+bool SwAttrIter::SeekStartAndChgAttrIter( OutputDevice* pOut, const bool bParaFont )
+{
+ SwTextNode const*const pFirstTextNode(m_pMergedPara ? m_pMergedPara->pFirstNode : m_pTextNode);
+ if ( m_pRedline && m_pRedline->ExtOn() )
+ m_pRedline->LeaveExtend(*m_pFont, pFirstTextNode->GetIndex(), 0);
+
+ if (m_pTextNode != pFirstTextNode)
+ {
+ assert(m_pMergedPara);
+ m_pTextNode = m_pMergedPara->pFirstNode;
+ InitFontAndAttrHandler(*m_pMergedPara->pParaPropsNode, *m_pTextNode,
+ m_pMergedPara->mergedText, nullptr, nullptr);
+ }
+
+ // reset font to its original state
+ m_aAttrHandler.Reset();
+ m_aAttrHandler.ResetFont( *m_pFont );
+
+ m_nStartIndex = 0;
+ m_nEndIndex = 0;
+ m_nPosition = 0;
+ m_nChgCnt = 0;
+ if( m_nPropFont )
+ m_pFont->SetProportion( m_nPropFont );
+ if( m_pRedline )
+ {
+ m_pRedline->Clear( m_pFont );
+ if( !bParaFont )
+ m_nChgCnt = m_nChgCnt + m_pRedline->Seek(*m_pFont, pFirstTextNode->GetIndex(), 0, COMPLETE_STRING);
+ else
+ m_pRedline->Reset();
+ }
+
+ SwpHints const*const pHints(m_pTextNode->GetpSwpHints());
+ if (pHints && !bParaFont)
+ {
+ SwTextAttr *pTextAttr;
+ // While we've not reached the end of the StartArray && the TextAttribute starts at position 0...
+ while ((m_nStartIndex < pHints->Count()) &&
+ !((pTextAttr = pHints->Get(m_nStartIndex))->GetStart()))
+ {
+ // open the TextAttributes
+ Chg( pTextAttr );
+ m_nStartIndex++;
+ }
+ }
+
+ bool bChg = m_pFont->IsFntChg();
+ if ( m_pLastOut.get() != pOut )
+ {
+ m_pLastOut = pOut;
+ m_pFont->SetFntChg( true );
+ bChg = true;
+ }
+ if( bChg )
+ {
+ // if the application counter is zero, we know the cache id of the wanted font
+ if ( !m_nChgCnt && !m_nPropFont )
+ m_pFont->SetFontCacheId( m_aFontCacheIds[ m_pFont->GetActual() ],
+ m_aFontIdx[ m_pFont->GetActual() ], m_pFont->GetActual() );
+ m_pFont->ChgPhysFnt( m_pViewShell, *pOut );
+ }
+ return bChg;
+}
+
+// AMA: New AttrIter Nov 94
+void SwAttrIter::SeekFwd(const sal_Int32 nOldPos, const sal_Int32 nNewPos)
+{
+ SwpHints const*const pHints(m_pTextNode->GetpSwpHints());
+ SwTextAttr *pTextAttr;
+ const auto nHintsCount = pHints->Count();
+
+ if ( m_nStartIndex ) // If attributes have been opened at all ...
+ {
+ // Close attributes that are currently open, but stop at nNewPos+1
+
+ // As long as we've not yet reached the end of EndArray and the
+ // TextAttribute ends before or at the new position ...
+ while ((m_nEndIndex < nHintsCount) &&
+ ((pTextAttr = pHints->GetSortedByEnd(m_nEndIndex))->GetAnyEnd() <= nNewPos))
+ {
+ // Close the TextAttributes, whose StartPos were before or at
+ // the old nPos and are currently open
+ if (pTextAttr->GetStart() <= nOldPos) Rst( pTextAttr );
+ m_nEndIndex++;
+ }
+ }
+ else // skip the not opened ends
+ {
+ while ((m_nEndIndex < nHintsCount) &&
+ (pHints->GetSortedByEnd(m_nEndIndex)->GetAnyEnd() <= nNewPos))
+ {
+ m_nEndIndex++;
+ }
+ }
+
+ // As long as we've not yet reached the end of EndArray and the
+ // TextAttribute ends before or at the new position...
+ while ((m_nStartIndex < nHintsCount) &&
+ ((pTextAttr = pHints->Get(m_nStartIndex))->GetStart() <= nNewPos))
+ {
+
+ // open the TextAttributes, whose ends lie behind the new position
+ if ( pTextAttr->GetAnyEnd() > nNewPos ) Chg( pTextAttr );
+ m_nStartIndex++;
+ }
+
+}
+
+bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
+{
+ // note: nNewPos isn't necessarily an index returned from GetNextAttr
+ std::pair<SwTextNode const*, sal_Int32> const newPos( m_pMergedPara
+ ? sw::MapViewToModel(*m_pMergedPara, nNewPos)
+ : std::make_pair(m_pTextNode, sal_Int32(nNewPos)));
+
+ if ( m_pRedline && m_pRedline->ExtOn() )
+ m_pRedline->LeaveExtend(*m_pFont, newPos.first->GetIndex(), newPos.second);
+ if (m_pTextNode->GetIndex() < newPos.first->GetIndex())
+ {
+ // Skipping to a different node - first seek until the end of this node
+ // to get rid of all hint items
+ if (m_pTextNode->GetpSwpHints())
+ {
+ sal_Int32 nPos(m_nPosition);
+ do
+ {
+ sal_Int32 const nOldPos(nPos);
+ nPos = GetNextAttrImpl(m_pTextNode, m_nStartIndex, m_nEndIndex, nPos);
+ if (nPos <= m_pTextNode->Len())
+ {
+ SeekFwd(nOldPos, nPos);
+ }
+ else
+ {
+ SeekFwd(nOldPos, m_pTextNode->Len());
+ }
+ }
+ while (nPos < m_pTextNode->Len());
+ }
+ assert(m_nChgCnt == 0); // should have reset it all? there cannot be ExtOn() inside of a Delete redline, surely?
+ // Unapply current para items:
+ // the SwAttrHandler doesn't appear to be capable of *unapplying*
+ // items at all; it can only apply a previously effective item.
+ // So do this by recreating the font from scratch.
+ // Apply new para items:
+ assert(m_pMergedPara);
+ InitFontAndAttrHandler(*m_pMergedPara->pParaPropsNode, *newPos.first,
+ m_pMergedPara->mergedText, nullptr, nullptr);
+ // reset to next
+ m_pTextNode = newPos.first;
+ m_nStartIndex = 0;
+ m_nEndIndex = 0;
+ m_nPosition = 0;
+ assert(m_pRedline);
+ }
+
+ // sw_redlinehide: Seek(0) must move before the first character, which
+ // has a special case where the first node starts with delete redline.
+ if ((!nNewPos && !m_pMergedPara)
+ || newPos.first != m_pTextNode
+ || newPos.second < m_nPosition)
+ {
+ if (m_pMergedPara)
+ {
+ if (m_pTextNode != newPos.first)
+ {
+ m_pTextNode = newPos.first;
+ // sw_redlinehide: hope it's okay to use the current text node
+ // here; the AttrHandler shouldn't care about non-char items
+ InitFontAndAttrHandler(*m_pMergedPara->pParaPropsNode, *m_pTextNode,
+ m_pMergedPara->mergedText, nullptr, nullptr);
+ }
+ }
+ if (m_pMergedPara || m_pTextNode->GetpSwpHints())
+ {
+ if( m_pRedline )
+ m_pRedline->Clear( nullptr );
+
+ // reset font to its original state
+ m_aAttrHandler.Reset();
+ m_aAttrHandler.ResetFont( *m_pFont );
+
+ if( m_nPropFont )
+ m_pFont->SetProportion( m_nPropFont );
+ m_nStartIndex = 0;
+ m_nEndIndex = 0;
+ m_nPosition = 0;
+ m_nChgCnt = 0;
+
+ // Attention!
+ // resetting the font here makes it necessary to apply any
+ // changes for extended input directly to the font
+ if ( m_pRedline && m_pRedline->ExtOn() )
+ {
+ m_pRedline->UpdateExtFont( *m_pFont );
+ ++m_nChgCnt;
+ }
+ }
+ }
+
+ if (m_pTextNode->GetpSwpHints())
+ {
+ if (m_pMergedPara)
+ {
+ // iterate hint by hint: SeekFwd does not mix ends and starts,
+ // it always applies all the starts last, so it must be called once
+ // per position where hints start/end!
+ sal_Int32 nPos(m_nPosition);
+ do
+ {
+ sal_Int32 const nOldPos(nPos);
+ nPos = GetNextAttrImpl(m_pTextNode, m_nStartIndex, m_nEndIndex, nPos);
+ if (nPos <= newPos.second)
+ {
+ SeekFwd(nOldPos, nPos);
+ }
+ else
+ {
+ SeekFwd(nOldPos, newPos.second);
+ }
+ }
+ while (nPos < newPos.second);
+ }
+ else
+ {
+ SeekFwd(m_nPosition, newPos.second);
+ }
+ }
+
+ m_pFont->SetActual( m_pScriptInfo->WhichFont(nNewPos) );
+
+ if( m_pRedline )
+ m_nChgCnt = m_nChgCnt + m_pRedline->Seek(*m_pFont, m_pTextNode->GetIndex(), newPos.second, m_nPosition);
+ m_nPosition = newPos.second;
+
+ if( m_nPropFont )
+ m_pFont->SetProportion( m_nPropFont );
+
+ return m_pFont->IsFntChg();
+}
+
+static void InsertCharAttrs(SfxPoolItem const** pAttrs, SfxItemSet const& rItems)
+{
+ SfxItemIter iter(rItems);
+ for (SfxPoolItem const* pItem = iter.GetCurItem(); pItem; pItem = iter.NextItem())
+ {
+ auto const nWhich(pItem->Which());
+ if (isCHRATR(nWhich) && RES_CHRATR_RSID != nWhich)
+ {
+ pAttrs[nWhich - RES_CHRATR_BEGIN] = pItem;
+ }
+ else if (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
+ {
+ pAttrs[RES_CHRATR_END - RES_CHRATR_BEGIN] = pItem;
+ }
+ }
+}
+
+// if return false: portion ends at start of redline, indexes unchanged
+// if return true: portion end not known (past end of redline), indexes point to first hint past end of redline
+static bool CanSkipOverRedline(
+ SwTextNode const& rStartNode, sal_Int32 const nStartRedline,
+ SwRangeRedline const& rRedline,
+ size_t & rStartIndex, size_t & rEndIndex,
+ bool const isTheAnswerYes)
+{
+ size_t nStartIndex(rStartIndex);
+ size_t nEndIndex(rEndIndex);
+ SwPosition const*const pRLEnd(rRedline.End());
+ if (!pRLEnd->nNode.GetNode().IsTextNode() // if fully deleted...
+ || pRLEnd->nContent == pRLEnd->nNode.GetNode().GetTextNode()->Len())
+ {
+ // shortcut: nothing follows redline
+ // current state is end state
+ return false;
+ }
+ std::vector<SwTextAttr*> activeCharFmts;
+ // can't compare the SwFont that's stored somewhere, it doesn't have compare
+ // operator, so try to recreate the situation with some temp arrays here
+ SfxPoolItem const* activeCharAttrsStart[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, };
+ if (&rStartNode != &pRLEnd->nNode.GetNode())
+ { // nodes' attributes are only needed if there are different nodes
+ InsertCharAttrs(activeCharAttrsStart, rStartNode.GetSwAttrSet());
+ }
+ if (SwpHints const*const pStartHints = rStartNode.GetpSwpHints())
+ {
+ // check hint ends of hints that start before and end within
+ sal_Int32 const nRedlineEnd(&rStartNode == &pRLEnd->nNode.GetNode()
+ ? pRLEnd->nContent.GetIndex()
+ : rStartNode.Len());
+ for ( ; nEndIndex < pStartHints->Count(); ++nEndIndex)
+ {
+ SwTextAttr *const pAttr(pStartHints->GetSortedByEnd(nEndIndex));
+ if (!pAttr->End())
+ {
+ continue;
+ }
+ if (nRedlineEnd < *pAttr->End())
+ {
+ break;
+ }
+ if (nStartRedline <= pAttr->GetStart())
+ {
+ continue;
+ }
+ if (pAttr->IsFormatIgnoreEnd())
+ {
+ continue;
+ }
+ switch (pAttr->Which())
+ {
+ // if any of these ends inside RL then we need a new portion
+ case RES_TXTATR_REFMARK:
+ case RES_TXTATR_TOXMARK:
+ case RES_TXTATR_META: // actually these 2 aren't allowed to overlap ???
+ case RES_TXTATR_METAFIELD:
+ case RES_TXTATR_INETFMT:
+ case RES_TXTATR_CJK_RUBY:
+ case RES_TXTATR_INPUTFIELD:
+ case RES_TXTATR_CONTENTCONTROL:
+ {
+ if (!isTheAnswerYes) return false; // always break
+ }
+ break;
+ // these are guaranteed not to overlap
+ // and come in order of application
+ case RES_TXTATR_AUTOFMT:
+ case RES_TXTATR_CHARFMT:
+ {
+ if (pAttr->Which() == RES_TXTATR_CHARFMT)
+ {
+ activeCharFmts.push_back(pAttr);
+ }
+ // pure formatting hints may end inside the redline &
+ // start again inside the redline, which must not cause
+ // a new text portion if they have the same items - so
+ // store the effective items & compare all at the end
+ SfxItemSet const& rSet((pAttr->Which() == RES_TXTATR_CHARFMT)
+ ? static_cast<SfxItemSet const&>(pAttr->GetCharFormat().GetCharFormat()->GetAttrSet())
+ : *pAttr->GetAutoFormat().GetStyleHandle());
+ InsertCharAttrs(activeCharAttrsStart, rSet);
+ }
+ break;
+ // SwTextNode::SetAttr puts it into AUTOFMT which is quite
+ // sensible so it doesn't actually exist as a hint
+ case RES_TXTATR_UNKNOWN_CONTAINER:
+ default: assert(false);
+ }
+ }
+ assert(nEndIndex == pStartHints->Count() ||
+ pRLEnd->nContent.GetIndex() < pStartHints->GetSortedByEnd(nEndIndex)->GetAnyEnd());
+ }
+
+ if (&rStartNode != &pRLEnd->nNode.GetNode())
+ {
+ nStartIndex = 0;
+ nEndIndex = 0;
+ }
+
+ // treat para properties as text properties
+ // ... with the FormatToTextAttr we get autofmts that correspond to the *effective* attr set difference
+ // effective attr set: para props + charfmts + autofmt *in that order*
+ // ... and the charfmt must be *nominally* the same
+
+ SfxPoolItem const* activeCharAttrsEnd[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, };
+ if (&rStartNode != &pRLEnd->nNode.GetNode())
+ { // nodes' attributes are only needed if there are different nodes
+ InsertCharAttrs(activeCharAttrsEnd,
+ pRLEnd->nNode.GetNode().GetTextNode()->GetSwAttrSet());
+ }
+
+ if (SwpHints *const pEndHints = pRLEnd->nNode.GetNode().GetTextNode()->GetpSwpHints())
+ {
+ // check hint starts of hints that start within and end after
+#ifndef NDEBUG
+ sal_Int32 const nRedlineStart(&rStartNode == &pRLEnd->nNode.GetNode()
+ ? nStartRedline
+ : 0);
+#endif
+ for ( ; nStartIndex < pEndHints->Count(); ++nStartIndex)
+ {
+ SwTextAttr *const pAttr(pEndHints->Get(nStartIndex));
+ // compare with < here, not <=, to get the effective formatting
+ // of the 1st char after the redline; should not cause problems
+ // with consecutive delete redlines because those are handed by
+ // GetNextRedln() and here we have the last end pos.
+ if (pRLEnd->nContent.GetIndex() < pAttr->GetStart())
+ {
+ break;
+ }
+ if (!pAttr->End())
+ continue;
+ if (pAttr->IsFormatIgnoreStart())
+ {
+ continue;
+ }
+ assert(nRedlineStart <= pAttr->GetStart()); // we wouldn't be here otherwise?
+ if (*pAttr->End() <= pRLEnd->nContent.GetIndex())
+ {
+ continue;
+ }
+ switch (pAttr->Which())
+ {
+ case RES_TXTATR_REFMARK:
+ case RES_TXTATR_TOXMARK:
+ case RES_TXTATR_META: // actually these 2 aren't allowed to overlap ???
+ case RES_TXTATR_METAFIELD:
+ case RES_TXTATR_INETFMT:
+ case RES_TXTATR_CJK_RUBY:
+ case RES_TXTATR_INPUTFIELD:
+ case RES_TXTATR_CONTENTCONTROL:
+ {
+ if (!isTheAnswerYes) return false;
+ }
+ break;
+ case RES_TXTATR_AUTOFMT:
+ case RES_TXTATR_CHARFMT:
+ {
+ // char formats must be *nominally* the same
+ if (pAttr->Which() == RES_TXTATR_CHARFMT)
+ {
+ auto iter = std::find_if(activeCharFmts.begin(), activeCharFmts.end(),
+ [&pAttr](const SwTextAttr* pCharFmt) { return *pCharFmt == *pAttr; });
+ if (iter != activeCharFmts.end())
+ activeCharFmts.erase(iter);
+ else if (!isTheAnswerYes)
+ return false;
+ }
+ SfxItemSet const& rSet((pAttr->Which() == RES_TXTATR_CHARFMT)
+ ? static_cast<SfxItemSet const&>(pAttr->GetCharFormat().GetCharFormat()->GetAttrSet())
+ : *pAttr->GetAutoFormat().GetStyleHandle());
+ InsertCharAttrs(activeCharAttrsEnd, rSet);
+
+ }
+ break;
+ // SwTextNode::SetAttr puts it into AUTOFMT which is quite
+ // sensible so it doesn't actually exist as a hint
+ case RES_TXTATR_UNKNOWN_CONTAINER:
+ default: assert(false);
+ }
+ }
+ if (&rStartNode != &pRLEnd->nNode.GetNode())
+ {
+ // need to iterate the nEndIndex forward too so the loop in the
+ // caller can look for the right ends in the next iteration
+ for (nEndIndex = 0; nEndIndex < pEndHints->Count(); ++nEndIndex)
+ {
+ SwTextAttr *const pAttr(pEndHints->GetSortedByEnd(nEndIndex));
+ if (!pAttr->End())
+ continue;
+ if (pRLEnd->nContent.GetIndex() < *pAttr->End())
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ // if we didn't find a matching start for any end, then it really ends inside
+ if (!activeCharFmts.empty())
+ {
+ if (!isTheAnswerYes) return false;
+ }
+ for (size_t i = 0; i < SAL_N_ELEMENTS(activeCharAttrsStart); ++i)
+ {
+ // all of these are poolable
+// assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->IsItemPoolable(*activeCharAttrsStart[i]));
+ if (activeCharAttrsStart[i] != activeCharAttrsEnd[i])
+ {
+ if (!isTheAnswerYes) return false;
+ }
+ }
+ rStartIndex = nStartIndex;
+ rEndIndex = nEndIndex;
+ return true;
+}
+
+static sal_Int32 GetNextAttrImpl(SwTextNode const*const pTextNode,
+ size_t const nStartIndex, size_t const nEndIndex,
+ sal_Int32 const nPosition)
+{
+ // note: this used to be COMPLETE_STRING, but was set to Len() + 1 below,
+ // which is rather silly, so set it to Len() instead
+ sal_Int32 nNext = pTextNode->Len();
+ if (SwpHints const*const pHints = pTextNode->GetpSwpHints())
+ {
+ // are there attribute starts left?
+ for (size_t i = nStartIndex; i < pHints->Count(); ++i)
+ {
+ SwTextAttr *const pAttr(pHints->Get(i));
+ if (!pAttr->IsFormatIgnoreStart())
+ {
+ nNext = pAttr->GetStart();
+ break;
+ }
+ }
+ // are there attribute ends left?
+ for (size_t i = nEndIndex; i < pHints->Count(); ++i)
+ {
+ SwTextAttr *const pAttr(pHints->GetSortedByEnd(i));
+ if (!pAttr->IsFormatIgnoreEnd())
+ {
+ sal_Int32 const nNextEnd = pAttr->GetAnyEnd();
+ nNext = std::min(nNext, nNextEnd); // pick nearest one
+ break;
+ }
+ }
+ }
+ // TODO: maybe use hints like FieldHints for this instead of looking at the text...
+ const sal_Int32 l = std::min(nNext, pTextNode->Len());
+ sal_Int32 p = nPosition;
+ const sal_Unicode* pStr = pTextNode->GetText().getStr();
+ while (p < l)
+ {
+ sal_Unicode aChar = pStr[p];
+ switch (aChar)
+ {
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ goto break_; // sigh...
+ default:
+ ++p;
+ }
+ }
+break_:
+ assert(p <= nNext);
+ if (p < l)
+ {
+ // found a CH_TXT_ATR_FIELD*: if it's same as current position,
+ // skip behind it so that both before- and after-positions are returned
+ nNext = (nPosition < p) ? p : p + 1;
+ }
+ return nNext;
+}
+
+TextFrameIndex SwAttrIter::GetNextAttr() const
+{
+ size_t nStartIndex(m_nStartIndex);
+ size_t nEndIndex(m_nEndIndex);
+ size_t nPosition(m_nPosition);
+ SwTextNode const* pTextNode(m_pTextNode);
+ SwRedlineTable::size_type nActRedline(m_pRedline ? m_pRedline->GetAct() : SwRedlineTable::npos);
+
+ while (true)
+ {
+ sal_Int32 nNext = GetNextAttrImpl(pTextNode, nStartIndex, nEndIndex, nPosition);
+ if( m_pRedline )
+ {
+ std::pair<sal_Int32, std::pair<SwRangeRedline const*, size_t>> const redline(
+ m_pRedline->GetNextRedln(nNext, pTextNode, nActRedline));
+ if (redline.second.first)
+ {
+ assert(m_pMergedPara);
+ assert(redline.second.first->End()->nNode.GetIndex() <= m_pMergedPara->pLastNode->GetIndex()
+ || !redline.second.first->End()->nNode.GetNode().IsTextNode());
+ if (CanSkipOverRedline(*pTextNode, redline.first, *redline.second.first,
+ nStartIndex, nEndIndex, m_nPosition == redline.first))
+ { // if current position is start of the redline, must skip!
+ nActRedline += redline.second.second;
+ if (&redline.second.first->End()->nNode.GetNode() != pTextNode)
+ {
+ pTextNode = redline.second.first->End()->nNode.GetNode().GetTextNode();
+ nPosition = redline.second.first->End()->nContent.GetIndex();
+ }
+ else
+ {
+ nPosition = redline.second.first->End()->nContent.GetIndex();
+ }
+ }
+ else
+ {
+ return sw::MapModelToView(*m_pMergedPara, pTextNode, redline.first);
+ }
+ }
+ else
+ {
+ return m_pMergedPara
+ ? sw::MapModelToView(*m_pMergedPara, pTextNode, redline.first)
+ : TextFrameIndex(redline.first);
+ }
+ }
+ else
+ {
+ return TextFrameIndex(nNext);
+ }
+ }
+}
+
+namespace {
+
+class SwMinMaxArgs
+{
+public:
+ VclPtr<OutputDevice> m_pOut;
+ SwViewShell const* m_pSh;
+ sal_uLong& m_rMin;
+ sal_uLong& m_rAbsMin;
+ tools::Long m_nRowWidth;
+ tools::Long m_nWordWidth;
+ tools::Long m_nWordAdd;
+ sal_Int32 m_nNoLineBreak;
+ SwMinMaxArgs(OutputDevice* pOutI, SwViewShell const* pShI, sal_uLong& rMinI, sal_uLong& rAbsI)
+ : m_pOut(pOutI)
+ , m_pSh(pShI)
+ , m_rMin(rMinI)
+ , m_rAbsMin(rAbsI)
+ , m_nRowWidth(0)
+ , m_nWordWidth(0)
+ , m_nWordAdd(0)
+ , m_nNoLineBreak(COMPLETE_STRING)
+ { }
+ void Minimum( tools::Long nNew ) const {
+ if (static_cast<tools::Long>(m_rMin) < nNew)
+ m_rMin = nNew;
+ }
+ void NewWord() { m_nWordAdd = m_nWordWidth = 0; }
+};
+
+}
+
+static bool lcl_MinMaxString( SwMinMaxArgs& rArg, SwFont* pFnt, const OUString &rText,
+ sal_Int32 nIdx, sal_Int32 nEnd )
+{
+ bool bRet = false;
+ while( nIdx < nEnd )
+ {
+ sal_Int32 nStop = nIdx;
+ LanguageType eLang = pFnt->GetLanguage();
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ bool bClear = CH_BLANK == rText[ nStop ];
+ Boundary aBndry( g_pBreakIt->GetBreakIter()->getWordBoundary( rText, nIdx,
+ g_pBreakIt->GetLocale( eLang ),
+ WordType::DICTIONARY_WORD, true ) );
+ nStop = aBndry.endPos;
+ if (nIdx <= aBndry.startPos && nIdx && nIdx - 1 != rArg.m_nNoLineBreak)
+ rArg.NewWord();
+ if( nStop == nIdx )
+ ++nStop;
+ if( nStop > nEnd )
+ nStop = nEnd;
+
+ SwDrawTextInfo aDrawInf(rArg.m_pSh, *rArg.m_pOut, rText, nIdx, nStop - nIdx);
+ tools::Long nCurrentWidth = pFnt->GetTextSize_( aDrawInf ).Width();
+ rArg.m_nRowWidth += nCurrentWidth;
+ if( bClear )
+ rArg.NewWord();
+ else
+ {
+ rArg.m_nWordWidth += nCurrentWidth;
+ if (static_cast<tools::Long>(rArg.m_rAbsMin) < rArg.m_nWordWidth)
+ rArg.m_rAbsMin = rArg.m_nWordWidth;
+ rArg.Minimum(rArg.m_nWordWidth + rArg.m_nWordAdd);
+ bRet = true;
+ }
+ nIdx = nStop;
+ }
+ return bRet;
+}
+
+bool SwTextNode::IsSymbolAt(const sal_Int32 nBegin) const
+{
+ SwScriptInfo aScriptInfo;
+ SwAttrIter aIter( *const_cast<SwTextNode*>(this), aScriptInfo );
+ aIter.Seek( TextFrameIndex(nBegin) );
+ return aIter.GetFnt()->IsSymbol( getIDocumentLayoutAccess().GetCurrentViewShell() );
+}
+
+namespace {
+
+class SwMinMaxNodeArgs
+{
+public:
+ sal_uLong m_nMaxWidth; // sum of all frame widths
+ tools::Long m_nMinWidth; // biggest frame
+ tools::Long m_nLeftRest; // space not already covered by frames in the left margin
+ tools::Long m_nRightRest; // space not already covered by frames in the right margin
+ tools::Long m_nLeftDiff; // Min/Max-difference of the frame in the left margin
+ tools::Long m_nRightDiff; // Min/Max-difference of the frame in the right margin
+ SwNodeOffset m_nIndex; // index of the node
+ void Minimum( tools::Long nNew ) {
+ if (nNew > m_nMinWidth)
+ m_nMinWidth = nNew;
+ }
+};
+
+}
+
+static void lcl_MinMaxNode(SwFrameFormat* pNd, SwMinMaxNodeArgs& rIn)
+{
+ const SwFormatAnchor& rFormatA = pNd->GetAnchor();
+
+ if ((RndStdIds::FLY_AT_PARA != rFormatA.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_CHAR != rFormatA.GetAnchorId()))
+ {
+ return;
+ }
+
+ const SwPosition *pPos = rFormatA.GetContentAnchor();
+ OSL_ENSURE(pPos, "Unexpected NULL arguments");
+ if (!pPos || rIn.m_nIndex != pPos->nNode.GetIndex())
+ return;
+
+ tools::Long nMin, nMax;
+ SwHTMLTableLayout *pLayout = nullptr;
+ const bool bIsDrawFrameFormat = pNd->Which()==RES_DRAWFRMFMT;
+ if( !bIsDrawFrameFormat )
+ {
+ // Does the frame contain a table at the start or the end?
+ const SwNodes& rNodes = pNd->GetDoc()->GetNodes();
+ const SwFormatContent& rFlyContent = pNd->GetContent();
+ SwNodeOffset nStt = rFlyContent.GetContentIdx()->GetIndex();
+ SwTableNode* pTableNd = rNodes[nStt+1]->GetTableNode();
+ if( !pTableNd )
+ {
+ SwNode *pNd2 = rNodes[nStt];
+ pNd2 = rNodes[pNd2->EndOfSectionIndex()-1];
+ if( pNd2->IsEndNode() )
+ pTableNd = pNd2->StartOfSectionNode()->GetTableNode();
+ }
+
+ if( pTableNd )
+ pLayout = pTableNd->GetTable().GetHTMLTableLayout();
+ }
+
+ const SwFormatHoriOrient& rOrient = pNd->GetHoriOrient();
+ sal_Int16 eHoriOri = rOrient.GetHoriOrient();
+
+ tools::Long nDiff;
+ if( pLayout )
+ {
+ nMin = pLayout->GetMin();
+ nMax = pLayout->GetMax();
+ nDiff = nMax - nMin;
+ }
+ else
+ {
+ if( bIsDrawFrameFormat )
+ {
+ const SdrObject* pSObj = pNd->FindSdrObject();
+ if( pSObj )
+ nMin = pSObj->GetCurrentBoundRect().GetWidth();
+ else
+ nMin = 0;
+
+ }
+ else
+ {
+ const SwFormatFrameSize &rSz = pNd->GetFrameSize();
+ nMin = rSz.GetWidth();
+ }
+ nMax = nMin;
+ nDiff = 0;
+ }
+
+ const SvxLRSpaceItem &rLR = pNd->GetLRSpace();
+ nMin += rLR.GetLeft();
+ nMin += rLR.GetRight();
+ nMax += rLR.GetLeft();
+ nMax += rLR.GetRight();
+
+ if( css::text::WrapTextMode_THROUGH == pNd->GetSurround().GetSurround() )
+ {
+ rIn.Minimum( nMin );
+ return;
+ }
+
+ // Frames, which are left- or right-aligned are only party considered
+ // when calculating the maximum, since the border is already being considered.
+ // Only if the frame extends into the text body, this part is being added
+ switch( eHoriOri )
+ {
+ case text::HoriOrientation::RIGHT:
+ {
+ if( nDiff )
+ {
+ rIn.m_nRightRest -= rIn.m_nRightDiff;
+ rIn.m_nRightDiff = nDiff;
+ }
+ if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() )
+ {
+ if (rIn.m_nRightRest > 0)
+ rIn.m_nRightRest = 0;
+ }
+ rIn.m_nRightRest -= nMin;
+ break;
+ }
+ case text::HoriOrientation::LEFT:
+ {
+ if( nDiff )
+ {
+ rIn.m_nLeftRest -= rIn.m_nLeftDiff;
+ rIn.m_nLeftDiff = nDiff;
+ }
+ if (text::RelOrientation::FRAME != rOrient.GetRelationOrient() && rIn.m_nLeftRest < 0)
+ rIn.m_nLeftRest = 0;
+ rIn.m_nLeftRest -= nMin;
+ break;
+ }
+ default:
+ {
+ rIn.m_nMaxWidth += nMax;
+ rIn.Minimum(nMin);
+ }
+ }
+}
+
+#define FLYINCNT_MIN_WIDTH 284
+
+/**
+ * Changing this method very likely requires changing of GetScalingOfSelectedText
+ * This one is called exclusively from import filters, so there is no layout.
+ */
+void SwTextNode::GetMinMaxSize( SwNodeOffset nIndex, sal_uLong& rMin, sal_uLong &rMax,
+ sal_uLong& rAbsMin ) const
+{
+ SwViewShell const * pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
+ OutputDevice* pOut = nullptr;
+ if( pSh )
+ pOut = pSh->GetWin()->GetOutDev();
+ if( !pOut )
+ pOut = Application::GetDefaultDevice();
+
+ MapMode aOldMap( pOut->GetMapMode() );
+ pOut->SetMapMode( MapMode( MapUnit::MapTwip ) );
+
+ rMin = 0;
+ rMax = 0;
+ rAbsMin = 0;
+
+ const SvxLRSpaceItem &rSpace = GetSwAttrSet().GetLRSpace();
+ tools::Long nLROffset = rSpace.GetTextLeft() + GetLeftMarginWithNum( true );
+ short nFLOffs;
+ // For enumerations a negative first line indentation is probably filled already
+ if( !GetFirstLineOfsWithNum( nFLOffs ) || nFLOffs > nLROffset )
+ nLROffset = nFLOffs;
+
+ SwMinMaxNodeArgs aNodeArgs;
+ aNodeArgs.m_nMinWidth = 0;
+ aNodeArgs.m_nMaxWidth = 0;
+ aNodeArgs.m_nLeftRest = nLROffset;
+ aNodeArgs.m_nRightRest = rSpace.GetRight();
+ aNodeArgs.m_nLeftDiff = 0;
+ aNodeArgs.m_nRightDiff = 0;
+ if( nIndex )
+ {
+ SwFrameFormats* pTmp = const_cast<SwFrameFormats*>(GetDoc().GetSpzFrameFormats());
+ if( pTmp )
+ {
+ aNodeArgs.m_nIndex = nIndex;
+ for( SwFrameFormat *pFormat : *pTmp )
+ lcl_MinMaxNode(pFormat, aNodeArgs);
+ }
+ }
+ if (aNodeArgs.m_nLeftRest < 0)
+ aNodeArgs.Minimum(nLROffset - aNodeArgs.m_nLeftRest);
+ aNodeArgs.m_nLeftRest -= aNodeArgs.m_nLeftDiff;
+ if (aNodeArgs.m_nLeftRest < 0)
+ aNodeArgs.m_nMaxWidth -= aNodeArgs.m_nLeftRest;
+
+ if (aNodeArgs.m_nRightRest < 0)
+ aNodeArgs.Minimum(rSpace.GetRight() - aNodeArgs.m_nRightRest);
+ aNodeArgs.m_nRightRest -= aNodeArgs.m_nRightDiff;
+ if (aNodeArgs.m_nRightRest < 0)
+ aNodeArgs.m_nMaxWidth -= aNodeArgs.m_nRightRest;
+
+ SwScriptInfo aScriptInfo;
+ SwAttrIter aIter( *const_cast<SwTextNode*>(this), aScriptInfo );
+ TextFrameIndex nIdx(0);
+ aIter.SeekAndChgAttrIter( nIdx, pOut );
+ TextFrameIndex nLen(m_Text.getLength());
+ tools::Long nCurrentWidth = 0;
+ tools::Long nAdd = 0;
+ SwMinMaxArgs aArg( pOut, pSh, rMin, rAbsMin );
+ while( nIdx < nLen )
+ {
+ TextFrameIndex nNextChg = aIter.GetNextAttr();
+ TextFrameIndex nStop = aScriptInfo.NextScriptChg( nIdx );
+ if( nNextChg > nStop )
+ nNextChg = nStop;
+ SwTextAttr *pHint = nullptr;
+ sal_Unicode cChar = CH_BLANK;
+ nStop = nIdx;
+ while( nStop < nLen && nStop < nNextChg &&
+ CH_TAB != (cChar = m_Text[sal_Int32(nStop)]) &&
+ CH_BREAK != cChar && CHAR_HARDBLANK != cChar &&
+ CHAR_HARDHYPHEN != cChar && CHAR_SOFTHYPHEN != cChar &&
+ CH_TXT_ATR_INPUTFIELDSTART != cChar &&
+ CH_TXT_ATR_INPUTFIELDEND != cChar &&
+ CH_TXT_ATR_FORMELEMENT != cChar &&
+ CH_TXT_ATR_FIELDSTART != cChar &&
+ CH_TXT_ATR_FIELDSEP != cChar &&
+ CH_TXT_ATR_FIELDEND != cChar &&
+ !pHint )
+ {
+ // this looks like some defensive programming to handle dummy char
+ // with missing hint? but it's rather silly because it may pass the
+ // dummy char to lcl_MinMaxString in that case...
+ if( ( CH_TXTATR_BREAKWORD != cChar && CH_TXTATR_INWORD != cChar )
+ || ( nullptr == ( pHint = aIter.GetAttr( nStop ) ) ) )
+ ++nStop;
+ }
+ if (lcl_MinMaxString(aArg, aIter.GetFnt(), m_Text, sal_Int32(nIdx), sal_Int32(nStop)))
+ {
+ nAdd = 20;
+ }
+ nIdx = nStop;
+ aIter.SeekAndChgAttrIter( nIdx, pOut );
+ switch( cChar )
+ {
+ case CH_BREAK :
+ {
+ if (static_cast<tools::Long>(rMax) < aArg.m_nRowWidth)
+ rMax = aArg.m_nRowWidth;
+ aArg.m_nRowWidth = 0;
+ aArg.NewWord();
+ aIter.SeekAndChgAttrIter( ++nIdx, pOut );
+ }
+ break;
+ case CH_TAB :
+ {
+ aArg.NewWord();
+ aIter.SeekAndChgAttrIter( ++nIdx, pOut );
+ }
+ break;
+ case CHAR_SOFTHYPHEN:
+ ++nIdx;
+ break;
+ case CHAR_HARDBLANK:
+ case CHAR_HARDHYPHEN:
+ {
+ OUString sTmp( cChar );
+ SwDrawTextInfo aDrawInf( pSh,
+ *pOut, sTmp, 0, 1, 0, false );
+ nCurrentWidth = aIter.GetFnt()->GetTextSize_( aDrawInf ).Width();
+ aArg.m_nWordWidth += nCurrentWidth;
+ aArg.m_nRowWidth += nCurrentWidth;
+ if (static_cast<tools::Long>(rAbsMin) < aArg.m_nWordWidth)
+ rAbsMin = aArg.m_nWordWidth;
+ aArg.Minimum(aArg.m_nWordWidth + aArg.m_nWordAdd);
+ aArg.m_nNoLineBreak = sal_Int32(nIdx++);
+ }
+ break;
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ {
+ if( !pHint )
+ break;
+ tools::Long nOldWidth = aArg.m_nWordWidth;
+ tools::Long nOldAdd = aArg.m_nWordAdd;
+ aArg.NewWord();
+
+ switch( pHint->Which() )
+ {
+ case RES_TXTATR_FLYCNT :
+ {
+ SwFrameFormat *pFrameFormat = pHint->GetFlyCnt().GetFrameFormat();
+ const SvxLRSpaceItem &rLR = pFrameFormat->GetLRSpace();
+ if( RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ const SdrObject* pSObj = pFrameFormat->FindSdrObject();
+ if( pSObj )
+ nCurrentWidth = pSObj->GetCurrentBoundRect().GetWidth();
+ else
+ nCurrentWidth = 0;
+ }
+ else
+ {
+ const SwFormatFrameSize& rTmpSize = pFrameFormat->GetFrameSize();
+ if( RES_FLYFRMFMT == pFrameFormat->Which()
+ && rTmpSize.GetWidthPercent() )
+ {
+ // This is a hack for the following situation: In the paragraph there's a
+ // text frame with relative size. Then let's take 0.5 cm as minimum width
+ // and USHRT_MAX as maximum width
+ // It were cleaner and maybe necessary later on to iterate over the content
+ // of the text frame and call GetMinMaxSize recursively
+ nCurrentWidth = FLYINCNT_MIN_WIDTH; // 0.5 cm
+ rMax = std::max(rMax, sal_uLong(USHRT_MAX));
+ }
+ else
+ nCurrentWidth = pFrameFormat->GetFrameSize().GetWidth();
+ }
+ nCurrentWidth += rLR.GetLeft();
+ nCurrentWidth += rLR.GetRight();
+ aArg.m_nWordAdd = nOldWidth + nOldAdd;
+ aArg.m_nWordWidth = nCurrentWidth;
+ aArg.m_nRowWidth += nCurrentWidth;
+ if (static_cast<tools::Long>(rAbsMin) < aArg.m_nWordWidth)
+ rAbsMin = aArg.m_nWordWidth;
+ aArg.Minimum(aArg.m_nWordWidth + aArg.m_nWordAdd);
+ break;
+ }
+ case RES_TXTATR_FTN :
+ {
+ const OUString aText = pHint->GetFootnote().GetNumStr();
+ if( lcl_MinMaxString( aArg, aIter.GetFnt(), aText, 0,
+ aText.getLength() ) )
+ nAdd = 20;
+ break;
+ }
+
+ case RES_TXTATR_FIELD :
+ case RES_TXTATR_ANNOTATION :
+ {
+ SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField());
+ const OUString aText = pField->ExpandField(true, nullptr);
+ if( lcl_MinMaxString( aArg, aIter.GetFnt(), aText, 0,
+ aText.getLength() ) )
+ nAdd = 20;
+ break;
+ }
+ default:
+ aArg.m_nWordWidth = nOldWidth;
+ aArg.m_nWordAdd = nOldAdd;
+ }
+ aIter.SeekAndChgAttrIter( ++nIdx, pOut );
+ }
+ break;
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ { // just skip it and continue with the content...
+ aIter.SeekAndChgAttrIter( ++nIdx, pOut );
+ }
+ break;
+ }
+ }
+ if (static_cast<tools::Long>(rMax) < aArg.m_nRowWidth)
+ rMax = aArg.m_nRowWidth;
+
+ nLROffset += rSpace.GetRight();
+
+ rAbsMin += nLROffset;
+ rAbsMin += nAdd;
+ rMin += nLROffset;
+ rMin += nAdd;
+ if (static_cast<tools::Long>(rMin) < aNodeArgs.m_nMinWidth)
+ rMin = aNodeArgs.m_nMinWidth;
+ if (static_cast<tools::Long>(rAbsMin) < aNodeArgs.m_nMinWidth)
+ rAbsMin = aNodeArgs.m_nMinWidth;
+ rMax += aNodeArgs.m_nMaxWidth;
+ rMax += nLROffset;
+ rMax += nAdd;
+ if( rMax < rMin ) // e.g. Frames with flow through only contribute to the minimum
+ rMax = rMin;
+ pOut->SetMapMode( aOldMap );
+}
+
+/**
+ * Calculates the width of the text part specified by nStart and nEnd,
+ * the height of the line containing nStart is divided by this width,
+ * indicating the scaling factor, if the text part is rotated.
+ * Having CH_BREAKs in the text part, this method returns the scaling
+ * factor for the longest of the text parts separated by the CH_BREAK
+ *
+ * Changing this method very likely requires changing of "GetMinMaxSize"
+ */
+sal_uInt16 SwTextFrame::GetScalingOfSelectedText(
+ TextFrameIndex nStart, TextFrameIndex nEnd)
+{
+ assert(GetOffset() <= nStart && (!GetFollow() || nStart < GetFollow()->GetOffset()));
+ SwViewShell const*const pSh = getRootFrame()->GetCurrShell();
+ assert(pSh);
+ OutputDevice *const pOut = &pSh->GetRefDev();
+ assert(pOut);
+
+ MapMode aOldMap( pOut->GetMapMode() );
+ pOut->SetMapMode( MapMode( MapUnit::MapTwip ) );
+
+ if (nStart == nEnd)
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ SwScriptInfo aScriptInfo;
+ SwAttrIter aIter(*GetTextNodeFirst(), aScriptInfo, this);
+ aIter.SeekAndChgAttrIter( nStart, pOut );
+
+ Boundary aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ GetText(), sal_Int32(nStart),
+ g_pBreakIt->GetLocale( aIter.GetFnt()->GetLanguage() ),
+ WordType::DICTIONARY_WORD, true );
+
+ if (sal_Int32(nStart) == aBound.startPos)
+ {
+ // cursor is at left or right border of word
+ pOut->SetMapMode( aOldMap );
+ return 100;
+ }
+
+ nStart = TextFrameIndex(aBound.startPos);
+ nEnd = TextFrameIndex(aBound.endPos);
+
+ if (nStart == nEnd)
+ {
+ pOut->SetMapMode( aOldMap );
+ return 100;
+ }
+ }
+
+ SwScriptInfo aScriptInfo;
+ SwAttrIter aIter(*GetTextNodeFirst(), aScriptInfo, this);
+
+ // We do not want scaling attributes to be considered during this
+ // calculation. For this, we push a temporary scaling attribute with
+ // scaling value 100 and priority flag on top of the scaling stack
+ SwAttrHandler& rAH = aIter.GetAttrHandler();
+ SvxCharScaleWidthItem aItem(100, RES_CHRATR_SCALEW);
+ SwTextAttrEnd aAttr( aItem, 0, COMPLETE_STRING );
+ aAttr.SetPriorityAttr( true );
+ rAH.PushAndChg( aAttr, *(aIter.GetFnt()) );
+
+ TextFrameIndex nIdx = nStart;
+
+ sal_uLong nWidth = 0;
+ sal_uLong nProWidth = 0;
+
+ while( nIdx < nEnd )
+ {
+ aIter.SeekAndChgAttrIter( nIdx, pOut );
+
+ // scan for end of portion
+ TextFrameIndex const nNextChg = std::min(aIter.GetNextAttr(), aScriptInfo.NextScriptChg(nIdx));
+
+ TextFrameIndex nStop = nIdx;
+ sal_Unicode cChar = CH_BLANK;
+ SwTextAttr* pHint = nullptr;
+
+ // stop at special characters in [ nIdx, nNextChg ]
+ while( nStop < nEnd && nStop < nNextChg )
+ {
+ cChar = GetText()[sal_Int32(nStop)];
+ if (
+ CH_TAB == cChar ||
+ CH_BREAK == cChar ||
+ CHAR_HARDBLANK == cChar ||
+ CHAR_HARDHYPHEN == cChar ||
+ CHAR_SOFTHYPHEN == cChar ||
+ CH_TXT_ATR_INPUTFIELDSTART == cChar ||
+ CH_TXT_ATR_INPUTFIELDEND == cChar ||
+ CH_TXT_ATR_FORMELEMENT == cChar ||
+ CH_TXT_ATR_FIELDSTART == cChar ||
+ CH_TXT_ATR_FIELDSEP == cChar ||
+ CH_TXT_ATR_FIELDEND == cChar ||
+ (
+ (CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar) &&
+ (nullptr == (pHint = aIter.GetAttr(nStop)))
+ )
+ )
+ {
+ break;
+ }
+ else
+ ++nStop;
+ }
+
+ // calculate text widths up to cChar
+ if ( nStop > nIdx )
+ {
+ SwDrawTextInfo aDrawInf(pSh, *pOut, GetText(), sal_Int32(nIdx), sal_Int32(nStop - nIdx));
+ nProWidth += aIter.GetFnt()->GetTextSize_( aDrawInf ).Width();
+ }
+
+ nIdx = nStop;
+ aIter.SeekAndChgAttrIter( nIdx, pOut );
+
+ if ( cChar == CH_BREAK )
+ {
+ nWidth = std::max( nWidth, nProWidth );
+ nProWidth = 0;
+ nIdx++;
+ }
+ else if ( cChar == CH_TAB )
+ {
+ // tab receives width of one space
+ SwDrawTextInfo aDrawInf(pSh, *pOut, OUStringChar(CH_BLANK), 0, 1);
+ nProWidth += aIter.GetFnt()->GetTextSize_( aDrawInf ).Width();
+ nIdx++;
+ }
+ else if ( cChar == CHAR_SOFTHYPHEN )
+ ++nIdx;
+ else if ( cChar == CHAR_HARDBLANK || cChar == CHAR_HARDHYPHEN )
+ {
+ OUString sTmp( cChar );
+ SwDrawTextInfo aDrawInf(pSh, *pOut, sTmp, 0, 1);
+ nProWidth += aIter.GetFnt()->GetTextSize_( aDrawInf ).Width();
+ nIdx++;
+ }
+ else if ( pHint && ( cChar == CH_TXTATR_BREAKWORD || cChar == CH_TXTATR_INWORD ) )
+ {
+ switch( pHint->Which() )
+ {
+ case RES_TXTATR_FTN :
+ {
+ const OUString aText = pHint->GetFootnote().GetNumStr();
+ SwDrawTextInfo aDrawInf(pSh, *pOut, aText, 0, aText.getLength());
+
+ nProWidth += aIter.GetFnt()->GetTextSize_( aDrawInf ).Width();
+ break;
+ }
+
+ case RES_TXTATR_FIELD :
+ case RES_TXTATR_ANNOTATION :
+ {
+ SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField());
+ OUString const aText = pField->ExpandField(true, getRootFrame());
+ SwDrawTextInfo aDrawInf(pSh, *pOut, aText, 0, aText.getLength());
+
+ nProWidth += aIter.GetFnt()->GetTextSize_( aDrawInf ).Width();
+ break;
+ }
+
+ default:
+ {
+ // any suggestions for a default action?
+ }
+ } // end of switch
+ nIdx++;
+ }
+ else if (CH_TXT_ATR_INPUTFIELDSTART == cChar ||
+ CH_TXT_ATR_INPUTFIELDEND == cChar ||
+ CH_TXT_ATR_FORMELEMENT == cChar ||
+ CH_TXT_ATR_FIELDSTART == cChar ||
+ CH_TXT_ATR_FIELDSEP == cChar ||
+ CH_TXT_ATR_FIELDEND == cChar)
+ { // just skip it and continue with the content...
+ ++nIdx;
+ }
+ } // end of while
+
+ nWidth = std::max( nWidth, nProWidth );
+
+ // search for the line containing nStart
+ if (HasPara())
+ {
+ SwTextInfo aInf(this);
+ SwTextIter aLine(this, &aInf);
+ aLine.CharToLine( nStart );
+ pOut->SetMapMode( aOldMap );
+ return o3tl::narrowing<sal_uInt16>( nWidth ?
+ ( ( 100 * aLine.GetCurr()->Height() ) / nWidth ) : 0 );
+ }
+ // no frame or no paragraph, we take the height of the character
+ // at nStart as line height
+
+ aIter.SeekAndChgAttrIter( nStart, pOut );
+ pOut->SetMapMode( aOldMap );
+
+ SwDrawTextInfo aDrawInf(pSh, *pOut, GetText(), sal_Int32(nStart), 1);
+ return o3tl::narrowing<sal_uInt16>( nWidth ? ((100 * aIter.GetFnt()->GetTextSize_( aDrawInf ).Height()) / nWidth ) : 0 );
+}
+
+SwTwips SwTextNode::GetWidthOfLeadingTabs() const
+{
+ SwTwips nRet = 0;
+
+ sal_Int32 nIdx = 0;
+
+ while ( nIdx < GetText().getLength() )
+ {
+ const sal_Unicode cCh = GetText()[nIdx];
+ if ( cCh!='\t' && cCh!=' ' )
+ {
+ break;
+ }
+ ++nIdx;
+ }
+
+ if ( nIdx > 0 )
+ {
+ SwPosition aPos( *this );
+ aPos.nContent += nIdx;
+
+ // Find the non-follow text frame:
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for( SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ // Only consider master frames:
+ if (!pFrame->IsFollow() &&
+ pFrame->GetTextNodeForFirstText() == this)
+ {
+ SwRectFnSet aRectFnSet(pFrame);
+ SwRect aRect;
+ pFrame->GetCharRect( aRect, aPos );
+ nRet = pFrame->IsRightToLeft() ?
+ aRectFnSet.GetPrtRight(*pFrame) - aRectFnSet.GetRight(aRect) :
+ aRectFnSet.GetLeft(aRect) - aRectFnSet.GetPrtLeft(*pFrame);
+ break;
+ }
+ }
+ }
+
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itratr.hxx b/sw/source/core/text/itratr.hxx
new file mode 100644
index 000000000..e4dedc22c
--- /dev/null
+++ b/sw/source/core/text/itratr.hxx
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <o3tl/deleter.hxx>
+#include "atrhndl.hxx"
+#include <swfont.hxx>
+
+namespace sw { struct MergedPara; }
+class SwTextAttr;
+class SwTextNode;
+class SwRedlineItr;
+class SwViewShell;
+class SwTextFrame;
+
+class SwAttrIter
+{
+ friend class SwFontSave;
+protected:
+
+ SwAttrHandler m_aAttrHandler;
+ SwViewShell *m_pViewShell;
+ SwFont* m_pFont;
+ SwScriptInfo* m_pScriptInfo;
+
+private:
+ VclPtr<OutputDevice> m_pLastOut;
+ /// count currently open hints, redlines, ext-input
+ short m_nChgCnt;
+ std::unique_ptr<SwRedlineItr, o3tl::default_delete<SwRedlineItr>> m_pRedline;
+ /// current iteration index in HintStarts
+ size_t m_nStartIndex;
+ /// current iteration index in HintEnds
+ size_t m_nEndIndex;
+ /// current iteration index in text node
+ sal_Int32 m_nPosition;
+ sal_uInt8 m_nPropFont;
+ o3tl::enumarray<SwFontScript, const void*> m_aFontCacheIds;
+ o3tl::enumarray<SwFontScript, sal_uInt16> m_aFontIdx;
+ /// input: the current text node
+ const SwTextNode* m_pTextNode;
+ sw::MergedPara const* m_pMergedPara;
+
+ void SeekFwd(sal_Int32 nOldPos, sal_Int32 nNewPos);
+ void SetFnt( SwFont* pNew ) { m_pFont = pNew; }
+ void InitFontAndAttrHandler(
+ SwTextNode const& rPropsNode, SwTextNode const& rTextNode,
+ OUString const& rText, bool const* pbVertLayout,
+ bool const* pbVertLayoutLRBT);
+
+protected:
+ void Chg( SwTextAttr const *pHt );
+ void Rst( SwTextAttr const *pHt );
+ void CtorInitAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const* pFrame = nullptr);
+ explicit SwAttrIter(SwTextNode const * pTextNode);
+
+public:
+ /// All subclasses of this always have a SwTextFrame passed to the
+ /// constructor, but SwAttrIter itself may be created without a
+ /// SwTextFrame in certain special cases via this ctor here
+ SwAttrIter(SwTextNode& rTextNode, SwScriptInfo& rScrInf, SwTextFrame const*const pFrame = nullptr);
+
+ virtual ~SwAttrIter();
+
+ SwRedlineItr *GetRedln() { return m_pRedline.get(); }
+ // The parameter returns the position of the next change before or at the
+ // char position.
+ TextFrameIndex GetNextAttr() const;
+ /// Enables the attributes used at char pos nPos in the logical font
+ bool Seek(TextFrameIndex nPos);
+ // Creates the font at the specified position via Seek() and checks
+ // if it's a symbol font.
+ bool IsSymbol(TextFrameIndex nPos);
+
+ /** Executes ChgPhysFnt if Seek() returns true
+ * and change font to merge character border with neighbours.
+ **/
+ bool SeekAndChgAttrIter(TextFrameIndex nPos, OutputDevice* pOut);
+ bool SeekStartAndChgAttrIter( OutputDevice* pOut, const bool bParaFont );
+
+ // Do we possibly have nasty things like footnotes?
+ bool MaybeHasHints() const;
+
+ // Returns the attribute for a position
+ SwTextAttr *GetAttr(TextFrameIndex nPos) const;
+
+ SwFont *GetFnt() { return m_pFont; }
+ const SwFont *GetFnt() const { return m_pFont; }
+
+ sal_uInt8 GetPropFont() const { return m_nPropFont; }
+ void SetPropFont( const sal_uInt8 nNew ) { m_nPropFont = nNew; }
+
+ SwAttrHandler& GetAttrHandler() { return m_aAttrHandler; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrcrsr.cxx b/sw/source/core/text/itrcrsr.cxx
new file mode 100644
index 000000000..546d91b17
--- /dev/null
+++ b/sw/source/core/text/itrcrsr.cxx
@@ -0,0 +1,1950 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <paratr.hxx>
+#include <flyfrm.hxx>
+#include <pam.hxx>
+#include <swselectionlist.hxx>
+#include <sortedobjs.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <frmatr.hxx>
+#include <tgrditem.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <pagefrm.hxx>
+
+#include "itrtxt.hxx"
+#include <txtfrm.hxx>
+#include <flyfrms.hxx>
+#include "porfld.hxx"
+#include "porfly.hxx"
+#include "pordrop.hxx"
+#include <crstate.hxx>
+#include "pormulti.hxx"
+#include <numrule.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+// Not reentrant !!!
+// is set in GetCharRect and is interpreted in UnitUp/Down.
+bool SwTextCursor::s_bRightMargin = false;
+
+// After calculating the position of a character during GetCharRect
+// this function allows to find the coordinates of a position (defined
+// in pCMS->pSpecialPos) inside a special portion (e.g., a field)
+static void lcl_GetCharRectInsideField( SwTextSizeInfo& rInf, SwRect& rOrig,
+ const SwCursorMoveState& rCMS,
+ const SwLinePortion& rPor )
+{
+ OSL_ENSURE( rCMS.m_pSpecialPos, "Information about special pos missing" );
+
+ if ( rPor.InFieldGrp() && !static_cast<const SwFieldPortion&>(rPor).GetExp().isEmpty() )
+ {
+ const sal_Int32 nCharOfst = rCMS.m_pSpecialPos->nCharOfst;
+ sal_Int32 nFieldIdx = 0;
+ sal_Int32 nFieldLen = 0;
+
+ OUString sString;
+ const OUString* pString = nullptr;
+ const SwLinePortion* pPor = &rPor;
+ do
+ {
+ if ( pPor->InFieldGrp() )
+ {
+ sString = static_cast<const SwFieldPortion*>(pPor)->GetExp();
+ pString = &sString;
+ nFieldLen = pString->getLength();
+ }
+ else
+ {
+ pString = nullptr;
+ nFieldLen = 0;
+ }
+
+ if ( ! pPor->GetNextPortion() || nFieldIdx + nFieldLen > nCharOfst )
+ break;
+
+ nFieldIdx = nFieldIdx + nFieldLen;
+ rOrig.Pos().AdjustX(pPor->Width() );
+ pPor = pPor->GetNextPortion();
+
+ } while ( true );
+
+ OSL_ENSURE( nCharOfst >= nFieldIdx, "Request of position inside field failed" );
+ sal_Int32 nLen = nCharOfst - nFieldIdx + 1;
+
+ if ( pString )
+ {
+ // get script for field portion
+ rInf.GetFont()->SetActual( SwScriptInfo::WhichFont(0, *pString) );
+
+ TextFrameIndex const nOldLen = pPor->GetLen();
+ const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen - 1));
+ const SwTwips nX1 = pPor->GetLen() ?
+ pPor->GetTextSize( rInf ).Width() :
+ 0;
+
+ SwTwips nX2 = 0;
+ if ( rCMS.m_bRealWidth )
+ {
+ const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen));
+ nX2 = pPor->GetTextSize( rInf ).Width();
+ }
+
+ const_cast<SwLinePortion*>(pPor)->SetLen( nOldLen );
+
+ rOrig.Pos().AdjustX(nX1 );
+ rOrig.Width( ( nX2 > nX1 ) ?
+ ( nX2 - nX1 ) :
+ 1 );
+ }
+ }
+ else
+ {
+ // special cases: no common fields, e.g., graphic number portion,
+ // FlyInCntPortions, Notes
+ rOrig.Width( rCMS.m_bRealWidth && rPor.Width() ? rPor.Width() : 1 );
+ }
+}
+
+// #i111284#
+namespace {
+ bool IsLabelAlignmentActive( const SwTextNode& rTextNode )
+ {
+ bool bRet( false );
+
+ if ( rTextNode.GetNumRule() )
+ {
+ int nListLevel = rTextNode.GetActualListLevel();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ const SwNumFormat& rNumFormat =
+ rTextNode.GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
+ if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ bRet = true;
+ }
+ }
+
+ return bRet;
+ }
+} // end of anonymous namespace
+
+void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
+{
+ CtorInitTextIter( pNewFrame, pNewInf );
+
+ m_pInf = pNewInf;
+ GetInfo().SetFont( GetFnt() );
+ const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
+
+ const SvxLRSpaceItem &rSpace = pNode->GetSwAttrSet().GetLRSpace();
+ // #i95907#
+ // #i111284#
+ const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
+ const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
+ const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable();
+ const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;
+
+ // Carefully adjust the text formatting ranges.
+
+ // This whole area desperately needs some rework. There are
+ // quite a couple of values that need to be considered:
+ // 1. paragraph indent
+ // 2. paragraph first line indent
+ // 3. numbering indent
+ // 4. numbering spacing to text
+ // 5. paragraph border
+ // Note: These values have already been used during calculation
+ // of the printing area of the paragraph.
+ const int nLMWithNum = pNode->GetLeftMarginWithNum( true );
+ if ( m_pFrame->IsRightToLeft() )
+ {
+ // this calculation is identical this the calculation for L2R layout - see below
+ mnLeft = m_pFrame->getFrameArea().Left() +
+ m_pFrame->getFramePrintArea().Left() +
+ nLMWithNum -
+ pNode->GetLeftMarginWithNum() -
+ // #i95907#
+ // #i111284#
+ // rSpace.GetLeft() + rSpace.GetTextLeft();
+ ( bListLevelIndentsApplicableAndLabelAlignmentActive
+ ? 0
+ : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
+ }
+ else
+ {
+ // #i95907#
+ // #i111284#
+ if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
+ !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
+ {
+ // this calculation is identical this the calculation for R2L layout - see above
+ mnLeft = m_pFrame->getFrameArea().Left() +
+ m_pFrame->getFramePrintArea().Left() +
+ nLMWithNum -
+ pNode->GetLeftMarginWithNum() -
+ // #i95907#
+ // #i111284#
+ ( bListLevelIndentsApplicableAndLabelAlignmentActive
+ ? 0
+ : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
+ }
+ else
+ {
+ mnLeft = m_pFrame->getFrameArea().Left() +
+ std::max( tools::Long( rSpace.GetTextLeft() + nLMWithNum ),
+ m_pFrame->getFramePrintArea().Left() );
+ }
+ }
+
+ mnRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();
+
+ if( mnLeft >= mnRight &&
+ // #i53066# Omit adjustment of nLeft for numbered
+ // paras inside cells inside new documents:
+ ( pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
+ !m_pFrame->IsInTab() ||
+ ( !nLMWithNum && (!bLabelAlignmentActive || bListLevelIndentsApplicable) ) ) )
+ {
+ mnLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
+ if( mnLeft >= mnRight ) // e.g. with large paragraph indentations in slim table columns
+ mnRight = mnLeft + 1; // einen goennen wir uns immer
+ }
+
+ if( m_pFrame->IsFollow() && m_pFrame->GetOffset() )
+ mnFirst = mnLeft;
+ else
+ {
+ short nFLOfst = 0;
+ tools::Long nFirstLineOfs = 0;
+ if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
+ rSpace.IsAutoFirst() )
+ {
+ nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
+ LanguageType const aLang = m_pFrame->GetLangOfChar(
+ TextFrameIndex(0), css::i18n::ScriptType::ASIAN);
+ if (aLang != LANGUAGE_KOREAN && aLang != LANGUAGE_JAPANESE)
+ nFirstLineOfs<<=1;
+
+ // tdf#129448: Auto first-line indent should not be effected by line space.
+ // Below is for compatibility with old documents.
+ if (!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE))
+ {
+ const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
+ if( pSpace )
+ {
+ switch( pSpace->GetLineSpaceRule() )
+ {
+ case SvxLineSpaceRule::Auto:
+ break;
+ case SvxLineSpaceRule::Min:
+ {
+ if( nFirstLineOfs < pSpace->GetLineHeight() )
+ nFirstLineOfs = pSpace->GetLineHeight();
+ break;
+ }
+ case SvxLineSpaceRule::Fix:
+ nFirstLineOfs = pSpace->GetLineHeight();
+ break;
+ default: OSL_FAIL( ": unknown LineSpaceRule" );
+ }
+ switch( pSpace->GetInterLineSpaceRule() )
+ {
+ case SvxInterLineSpaceRule::Off:
+ break;
+ case SvxInterLineSpaceRule::Prop:
+ {
+ tools::Long nTmp = pSpace->GetPropLineSpace();
+ // 50% is the minimum, at 0% we switch to
+ // the default value 100%...
+ if( nTmp < 50 )
+ nTmp = nTmp ? 50 : 100;
+
+ nTmp *= nFirstLineOfs;
+ nTmp /= 100;
+ if( !nTmp )
+ ++nTmp;
+ nFirstLineOfs = nTmp;
+ break;
+ }
+ case SvxInterLineSpaceRule::Fix:
+ {
+ nFirstLineOfs += pSpace->GetInterLineSpace();
+ break;
+ }
+ default: OSL_FAIL( ": unknown InterLineSpaceRule" );
+ }
+ }
+ }
+ }
+ else
+ nFirstLineOfs = nFLOfst;
+
+ // #i95907#
+ // #i111284#
+ if ( m_pFrame->IsRightToLeft() ||
+ bListLevelIndentsApplicableAndLabelAlignmentActive ||
+ !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
+ {
+ if ( nFirstLineOfs < 0 && m_pFrame->IsInTab() &&
+ mnLeft == m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left() &&
+ !m_pFrame->IsRightToLeft() &&
+ !bListLevelIndentsApplicableAndLabelAlignmentActive )
+ {
+ // tdf#130218 always show hanging indent in narrow table cells
+ // to avoid hiding the text content of the first line
+ mnLeft -= nFirstLineOfs;
+ }
+
+ mnFirst = mnLeft + nFirstLineOfs;
+ }
+ else
+ {
+ mnFirst = m_pFrame->getFrameArea().Left() +
+ std::max( rSpace.GetTextLeft() + nLMWithNum+ nFirstLineOfs,
+ m_pFrame->getFramePrintArea().Left() );
+ }
+
+ // Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
+ // value for the new list label position and space mode LABEL_ALIGNMENT
+ // and label alignment CENTER and RIGHT in L2R layout respectively
+ // label alignment LEFT and CENTER in R2L layout
+ mnFirst += m_pFrame->GetAdditionalFirstLineOffset();
+
+ if( mnFirst >= mnRight )
+ mnFirst = mnRight - 1;
+ }
+ const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
+ mnAdjust = rAdjust.GetAdjust();
+
+ // left is left and right is right
+ if ( m_pFrame->IsRightToLeft() )
+ {
+ if ( SvxAdjust::Left == mnAdjust )
+ mnAdjust = SvxAdjust::Right;
+ else if ( SvxAdjust::Right == mnAdjust )
+ mnAdjust = SvxAdjust::Left;
+ }
+
+ m_bOneBlock = rAdjust.GetOneWord() == SvxAdjust::Block;
+ m_bLastBlock = rAdjust.GetLastBlock() == SvxAdjust::Block;
+ m_bLastCenter = rAdjust.GetLastBlock() == SvxAdjust::Center;
+
+ // #i91133#
+ mnTabLeft = pNode->GetLeftMarginForTabCalculation();
+
+ DropInit();
+}
+
+void SwTextMargin::DropInit()
+{
+ mnDropLeft = mnDropLines = mnDropHeight = mnDropDescent = 0;
+ const SwParaPortion *pPara = GetInfo().GetParaPortion();
+ if( pPara )
+ {
+ const SwDropPortion *pPorDrop = pPara->FindDropPortion();
+ if ( pPorDrop )
+ {
+ mnDropLeft = pPorDrop->GetDropLeft();
+ mnDropLines = pPorDrop->GetLines();
+ mnDropHeight = pPorDrop->GetDropHeight();
+ mnDropDescent = pPorDrop->GetDropDescent();
+ }
+ }
+}
+
+// The function is interpreting / observing / evaluating / keeping / respecting the first line indention and the specified width.
+SwTwips SwTextMargin::GetLineStart() const
+{
+ SwTwips nRet = GetLeftMargin();
+ if( GetAdjust() != SvxAdjust::Left &&
+ !m_pCurr->GetFirstPortion()->IsMarginPortion() )
+ {
+ // If the first portion is a Margin, then the
+ // adjustment is expressed by the portions.
+ if( GetAdjust() == SvxAdjust::Right )
+ nRet = Right() - CurrWidth();
+ else if( GetAdjust() == SvxAdjust::Center )
+ nRet += (GetLineWidth() - CurrWidth()) / 2;
+ }
+ return nRet;
+}
+
+void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
+{
+ CtorInitTextMargin( pNewFrame, pNewInf );
+ // 6096: Attention, the iterators are derived!
+ // GetInfo().SetOut( GetInfo().GetWin() );
+}
+
+// 1170: Ancient bug: Shift-End forgets the last character ...
+void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
+ SwCursorMoveState* pCMS, const tools::Long nMax )
+{
+ // 1170: Ambiguity of document positions
+ s_bRightMargin = true;
+ CharCursorToLine(nOfst);
+
+ // Somehow twisted: nOfst names the position behind the last
+ // character of the last line == This is the position in front of the first character
+ // of the line, in which we are situated:
+ if( nOfst != GetStart() || !m_pCurr->GetLen() )
+ {
+ // 8810: Master line RightMargin, after that LeftMargin
+ GetCharRect( pOrig, nOfst, pCMS, nMax );
+ s_bRightMargin = nOfst >= GetEnd() && nOfst < TextFrameIndex(GetInfo().GetText().getLength());
+ return;
+ }
+
+ if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
+ {
+ GetCharRect( pOrig, nOfst, pCMS, nMax );
+ return;
+ }
+
+ // If necessary, as catch up, do the adjustment
+ GetAdjusted();
+
+ tools::Long nX = 0;
+ tools::Long nLast = 0;
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+
+ SwTwips nTmpHeight, nTmpAscent;
+ CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+ sal_uInt16 nPorHeight = nTmpHeight;
+ sal_uInt16 nPorAscent = nTmpAscent;
+
+ // Search for the last Text/EndPortion of the line
+ while( pPor )
+ {
+ nX = nX + pPor->Width();
+ if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
+ && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
+ {
+ nLast = nX;
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+ pPor = pPor->GetNextPortion();
+ }
+
+ const Size aCharSize( 1, nTmpHeight );
+ pOrig->Pos( GetTopLeft() );
+ pOrig->SSize( aCharSize );
+ pOrig->Pos().AdjustX(nLast );
+ const SwTwips nTmpRight = Right() - 1;
+ if( pOrig->Left() > nTmpRight )
+ pOrig->Pos().setX( nTmpRight );
+
+ if ( pCMS && pCMS->m_bRealHeight )
+ {
+ if ( nTmpAscent > nPorAscent )
+ pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
+ else
+ pCMS->m_aRealHeight.setX( 0 );
+ OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
+ pCMS->m_aRealHeight.setY( nPorHeight );
+ }
+}
+
+// internal function, called by SwTextCursor::GetCharRect() to calculate
+// the relative character position in the current line.
+// pOrig refers to x and y coordinates, width and height of the cursor
+// pCMS is used for restricting the cursor, if there are different font
+// heights in one line ( first value = offset to y of pOrig, second
+// value = real height of (shortened) cursor
+void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
+ SwCursorMoveState* pCMS )
+{
+ const OUString aText = GetInfo().GetText();
+ SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
+ if( GetPropFont() )
+ aInf.GetFont()->SetProportion( GetPropFont() );
+ SwTwips nTmpAscent, nTmpHeight; // Line height
+ CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+ const Size aCharSize( 1, nTmpHeight );
+ const Point aCharPos;
+ pOrig->Pos( aCharPos );
+ pOrig->SSize( aCharSize );
+
+ // If we are looking for a position inside a field which covers
+ // more than one line we may not skip any "empty portions" at the
+ // beginning of a line
+ const bool bInsideFirstField = pCMS && pCMS->m_pSpecialPos &&
+ ( pCMS->m_pSpecialPos->nLineOfst ||
+ SwSPExtendRange::BEFORE ==
+ pCMS->m_pSpecialPos->nExtendRange );
+
+ bool bWidth = pCMS && pCMS->m_bRealWidth;
+ if( !m_pCurr->GetLen() && !m_pCurr->Width() )
+ {
+ if ( pCMS && pCMS->m_bRealHeight )
+ {
+ pCMS->m_aRealHeight.setX( 0 );
+ pCMS->m_aRealHeight.setY( nTmpHeight );
+ }
+ }
+ else
+ {
+ SwTwips nPorHeight = nTmpHeight;
+ SwTwips nPorAscent = nTmpAscent;
+ SwTwips nX = 0;
+ SwTwips nTmpFirst = 0;
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ SwBidiPortion* pLastBidiPor = nullptr;
+ TextFrameIndex nLastBidiIdx(-1);
+ SwTwips nLastBidiPorWidth = 0;
+ std::deque<sal_uInt16>* pKanaComp = m_pCurr->GetpKanaComp();
+ sal_uInt16 nSpaceIdx = 0;
+ size_t nKanaIdx = 0;
+ tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
+
+ bool bNoText = true;
+
+ // First all portions without Len at beginning of line are skipped.
+ // Exceptions are the mean special portions from WhichFirstPortion:
+ // Num, ErgoSum, FootnoteNum, FieldRests
+ // 8477: but also the only Textportion of an empty line with
+ // Right/Center-Adjustment! So not just pPor->GetExpandPortion() ...
+ while( pPor && !pPor->GetLen() && ! bInsideFirstField )
+ {
+ nX += pPor->Width();
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nX += pPor->CalcSpacing( nSpaceAdd, aInf );
+ if( bNoText )
+ nTmpFirst = nX;
+ // 8670: EndPortions count once as TextPortions.
+ // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
+ if( pPor->InTextGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
+ {
+ bNoText = false;
+ nTmpFirst = nX;
+ }
+ if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
+ {
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
+ ++nKanaIdx;
+ }
+ if( pPor->InFixMargGrp() )
+ {
+ if( pPor->IsMarginPortion() )
+ bNoText = false;
+ else
+ {
+ // fix margin portion => next SpaceAdd, KanaComp value
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
+ ++nKanaIdx;
+ }
+ }
+ pPor = pPor->GetNextPortion();
+ }
+
+ if( !pPor )
+ {
+ // There's just Spezialportions.
+ nX = nTmpFirst;
+ }
+ else
+ {
+ if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
+ (!pPor->InFieldGrp() || pPor->GetAscent() ) )
+ {
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+ while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
+ ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
+ {
+ if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
+ (!pPor->InFieldGrp() || pPor->GetAscent() ) )
+ {
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+
+ // If we are behind the portion, we add the portion width to
+ // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
+ // For common portions (including BidiPortions) we want to add
+ // the portion width to nX. For MultiPortions, nExtra = 0,
+ // therefore we go to the 'else' branch and start a recursion.
+ const TextFrameIndex nExtra( (pPor->IsMultiPortion()
+ && !static_cast<SwMultiPortion*>(pPor)->IsBidi()
+ && !bWidth)
+ ? 0 : 1 );
+ if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
+ {
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nX += pPor->PrtWidth() +
+ pPor->CalcSpacing( nSpaceAdd, aInf );
+ else
+ {
+ if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
+ {
+ // update to current SpaceAdd, KanaComp values
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if ( pKanaComp &&
+ ( nKanaIdx + 1 ) < pKanaComp->size()
+ )
+ ++nKanaIdx;
+ }
+ if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
+ !pPor->GetNextPortion()->IsMarginPortion() ) )
+ nX += pPor->PrtWidth();
+ }
+ if( pPor->IsMultiPortion() )
+ {
+ if ( static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
+ {
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
+ ++nKanaIdx;
+ }
+
+ // if we are right behind a BidiPortion, we have to
+ // hold a pointer to the BidiPortion in order to
+ // find the correct cursor position, depending on the
+ // cursor level
+ if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() &&
+ aInf.GetIdx() + pPor->GetLen() == nOfst )
+ {
+ pLastBidiPor = static_cast<SwBidiPortion*>(pPor);
+ nLastBidiIdx = aInf.GetIdx();
+ nLastBidiPorWidth = pLastBidiPor->Width() +
+ pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );
+ }
+ }
+
+ aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
+ pPor = pPor->GetNextPortion();
+ }
+ else
+ {
+ if( pPor->IsMultiPortion() )
+ {
+ nTmpAscent = AdjustBaseLine( *m_pCurr, pPor );
+ GetInfo().SetMulti( true );
+ pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );
+
+ if( pCMS && pCMS->m_b2Lines )
+ {
+ const bool bRecursion (pCMS->m_p2Lines);
+ if ( !bRecursion )
+ {
+ pCMS->m_p2Lines.reset(new Sw2LinesPos);
+ pCMS->m_p2Lines->aLine = SwRect(aCharPos, aCharSize);
+ }
+
+ if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
+ {
+ if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
+ pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_270;
+ else
+ pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_90;
+ }
+ else if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
+ pCMS->m_p2Lines->nMultiType = MultiPortionType::TWOLINE;
+ else if( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ pCMS->m_p2Lines->nMultiType = MultiPortionType::BIDI;
+ else
+ pCMS->m_p2Lines->nMultiType = MultiPortionType::RUBY;
+
+ SwTwips nTmpWidth = pPor->Width();
+ if( nSpaceAdd )
+ nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
+
+ SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
+ Size( nTmpWidth, pPor->Height() ) );
+
+ if ( ! bRecursion )
+ pCMS->m_p2Lines->aPortion = aRect;
+ else
+ pCMS->m_p2Lines->aPortion2 = aRect;
+ }
+
+ // In a multi-portion we use GetCharRect()-function
+ // recursively and must add the x-position
+ // of the multi-portion.
+ TextFrameIndex const nOldStart = m_nStart;
+ SwTwips nOldY = m_nY;
+ sal_uInt8 nOldProp = GetPropFont();
+ m_nStart = aInf.GetIdx();
+ SwLineLayout* pOldCurr = m_pCurr;
+ m_pCurr = &static_cast<SwMultiPortion*>(pPor)->GetRoot();
+ if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
+ SetPropFont( 50 );
+
+ SwTextGridItem const*const pGrid(
+ GetGridItem(GetTextFrame()->FindPageFrame()));
+ const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
+ const sal_uInt16 nRubyHeight = bHasGrid ?
+ pGrid->GetRubyHeight() : 0;
+
+ if( m_nStart + m_pCurr->GetLen() <= nOfst && GetNext() &&
+ ( ! static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
+ static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
+ {
+ sal_uInt16 nOffset;
+ // in grid mode we may only add the height of the
+ // ruby line if ruby line is on top
+ if ( bHasGrid &&
+ static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
+ static_cast<SwMultiPortion*>(pPor)->OnTop() )
+ nOffset = nRubyHeight;
+ else
+ nOffset = GetLineHeight();
+
+ pOrig->Pos().AdjustY(nOffset );
+ Next();
+ }
+
+ const bool bSpaceChg = static_cast<SwMultiPortion*>(pPor)->
+ ChgSpaceAdd( m_pCurr, nSpaceAdd );
+ Point aOldPos = pOrig->Pos();
+
+ // Ok, for ruby portions in grid mode we have to
+ // temporarily set the inner line height to the
+ // outer line height because that value is needed
+ // for the adjustment inside the recursion
+ const sal_uInt16 nOldRubyHeight = m_pCurr->Height();
+ const sal_uInt16 nOldRubyRealHeight = m_pCurr->GetRealHeight();
+ const bool bChgHeight =
+ static_cast<SwMultiPortion*>(pPor)->IsRuby() && bHasGrid;
+
+ if ( bChgHeight )
+ {
+ m_pCurr->Height( pOldCurr->Height() - nRubyHeight );
+ m_pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
+ nRubyHeight );
+ }
+
+ SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
+ if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ {
+ aLayoutModeModifier.Modify(
+ static_cast<SwBidiPortion*>(pPor)->GetLevel() % 2 );
+ }
+
+ GetCharRect_( pOrig, nOfst, pCMS );
+
+ if ( bChgHeight )
+ {
+ m_pCurr->Height( nOldRubyHeight );
+ m_pCurr->SetRealHeight( nOldRubyRealHeight );
+ }
+
+ // if we are still in the first row of
+ // our 2 line multiportion, we use the FirstMulti flag
+ // to indicate this
+ if ( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
+ {
+ // the recursion may have damaged our font size
+ SetPropFont( nOldProp );
+ GetInfo().GetFont()->SetProportion( 100 );
+
+ if ( m_pCurr == &static_cast<SwMultiPortion*>(pPor)->GetRoot() )
+ {
+ GetInfo().SetFirstMulti( true );
+
+ // we want to treat a double line portion like a
+ // single line portion, if there is no text in
+ // the second line
+ if ( !m_pCurr->GetNext() ||
+ !m_pCurr->GetNext()->GetLen() )
+ GetInfo().SetMulti( false );
+ }
+ }
+ // ruby portions are treated like single line portions
+ else if( static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
+ static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ GetInfo().SetMulti( false );
+
+ // calculate cursor values
+ if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
+ {
+ GetInfo().SetMulti( false );
+ tools::Long nTmp = pOrig->Width();
+ pOrig->Width( pOrig->Height() );
+ pOrig->Height( nTmp );
+ nTmp = pOrig->Left() - aOldPos.X();
+
+ // if we travel into our rotated portion from
+ // a line below, we have to take care, that the
+ // y coord in pOrig is less than line height:
+ if ( nTmp )
+ nTmp--;
+
+ pOrig->Pos().setX( nX + aOldPos.X() );
+ if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
+ pOrig->Pos().setY( aOldPos.Y() + nTmp );
+ else
+ pOrig->Pos().setY( aOldPos.Y()
+ + pPor->Height() - nTmp - pOrig->Height() );
+ if ( pCMS && pCMS->m_bRealHeight )
+ {
+ pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
+ // result for rotated multi portion is not
+ // correct for reverse (270 degree) portions
+ if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
+ {
+ if ( SvxParaVertAlignItem::Align::Automatic ==
+ GetLineInfo().GetVertAlign() )
+ // if vertical alignment is set to auto,
+ // we switch from base line alignment
+ // to centered alignment
+ pCMS->m_aRealHeight.setX(
+ ( pOrig->Width() +
+ pCMS->m_aRealHeight.Y() ) / 2 );
+ else
+ pCMS->m_aRealHeight.setX(
+ pOrig->Width() -
+ pCMS->m_aRealHeight.X() +
+ pCMS->m_aRealHeight.Y() );
+ }
+ }
+ }
+ else
+ {
+ pOrig->Pos().AdjustY(aOldPos.Y() );
+ if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ {
+ const SwTwips nPorWidth = pPor->Width() +
+ pPor->CalcSpacing( nSpaceAdd, aInf );
+ const SwTwips nInsideOfst = pOrig->Pos().X();
+ pOrig->Pos().setX( nX + nPorWidth -
+ nInsideOfst - pOrig->Width() );
+ }
+ else
+ pOrig->Pos().AdjustX(nX );
+
+ if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
+ pOrig->Pos().AdjustX(
+ static_cast<SwDoubleLinePortion*>(pPor)->PreWidth() );
+ }
+
+ if( bSpaceChg )
+ SwDoubleLinePortion::ResetSpaceAdd( m_pCurr );
+
+ m_pCurr = pOldCurr;
+ m_nStart = nOldStart;
+ m_nY = nOldY;
+ m_bPrev = false;
+
+ return;
+ }
+ if ( pPor->PrtWidth() )
+ {
+ TextFrameIndex const nOldLen = pPor->GetLen();
+ pPor->SetLen( nOfst - aInf.GetIdx() );
+ aInf.SetLen( pPor->GetLen() );
+ if( nX || !pPor->InNumberGrp() )
+ {
+ SeekAndChg( aInf );
+ const bool bOldOnWin = aInf.OnWin();
+ aInf.SetOnWin( false ); // no BULLETs!
+ SwTwips nTmp = nX;
+ aInf.SetKanaComp( pKanaComp );
+ aInf.SetKanaIdx( nKanaIdx );
+ nX += pPor->GetTextSize( aInf ).Width();
+ aInf.SetOnWin( bOldOnWin );
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nX += pPor->CalcSpacing( nSpaceAdd, aInf );
+ if( bWidth )
+ {
+ pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
+ aInf.SetLen( pPor->GetLen() );
+ aInf.SetOnWin( false ); // no BULLETs!
+ nTmp += pPor->GetTextSize( aInf ).Width();
+ aInf.SetOnWin( bOldOnWin );
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
+ pOrig->Width( nTmp - nX );
+ }
+ }
+ pPor->SetLen( nOldLen );
+
+ // Shift the cursor with the right border width
+ // Note: nX remains positive because GetTextSize() also include the width of the right border
+ if( aInf.GetIdx() < nOfst && nOfst < aInf.GetIdx() + pPor->GetLen() )
+ {
+ // Find the current drop portion part and use its right border
+ if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
+ {
+ SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
+ const SwDropPortionPart* pCurrPart = pDrop->GetPart();
+ TextFrameIndex nSumLength(0);
+ while( pCurrPart && (nSumLength += pCurrPart->GetLen()) < nOfst - aInf.GetIdx() )
+ {
+ pCurrPart = pCurrPart->GetFollow();
+ }
+ if( pCurrPart && nSumLength != nOfst - aInf.GetIdx() &&
+ pCurrPart->GetFont().GetRightBorder() && !pCurrPart->GetJoinBorderWithNext() )
+ {
+ nX -= pCurrPart->GetFont().GetRightBorderSpace();
+ }
+ }
+ else if( GetInfo().GetFont()->GetRightBorder() && !pPor->GetJoinBorderWithNext())
+ {
+ nX -= GetInfo().GetFont()->GetRightBorderSpace();
+ }
+ }
+ }
+ bWidth = false;
+ break;
+ }
+ }
+ }
+
+ if( pPor )
+ {
+ OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
+ bool bEmptyField = false;
+ if( pPor->InFieldGrp() && pPor->GetLen() )
+ {
+ SwFieldPortion *pTmp = static_cast<SwFieldPortion*>(pPor);
+ while( pTmp->HasFollow() && pTmp->GetExp().isEmpty() )
+ {
+ sal_uInt16 nAddX = pTmp->Width();
+ SwLinePortion *pNext = pTmp->GetNextPortion();
+ while( pNext && !pNext->InFieldGrp() )
+ {
+ OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
+ nAddX = nAddX + pNext->Width();
+ pNext = pNext->GetNextPortion();
+ }
+ if( !pNext )
+ break;
+ pTmp = static_cast<SwFieldPortion*>(pNext);
+ nPorHeight = pTmp->Height();
+ nPorAscent = pTmp->GetAscent();
+ nX += nAddX;
+ bEmptyField = true;
+ }
+ }
+ // 8513: Fields in justified text, skipped
+ while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
+ ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
+ pPor->IsBlankPortion() || pPor->InTabGrp() ||
+ ( !bEmptyField && pPor->InFieldGrp() ) ) )
+ {
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nX += pPor->PrtWidth() +
+ pPor->CalcSpacing( nSpaceAdd, aInf );
+ else
+ {
+ if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
+ {
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
+ ++nKanaIdx;
+ }
+ if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
+ !pPor->GetNextPortion()->IsMarginPortion() ) )
+ nX += pPor->PrtWidth();
+ }
+ if( pPor->IsMultiPortion() &&
+ static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
+ {
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
+ ++nKanaIdx;
+ }
+ if( !pPor->IsFlyPortion() )
+ {
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+ pPor = pPor->GetNextPortion();
+ }
+
+ if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
+ pPor->GetNextPortion() && pPor->GetNextPortion()->InFixGrp() )
+ {
+ // All special portions have to be skipped
+ // Taking the German word "zusammen" as example: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
+ // Without the adjustment we end up in front of '-', with the
+ // adjustment in front of the 's'.
+ while( pPor && !pPor->GetLen() )
+ {
+ nX += pPor->Width();
+ if( !pPor->IsMarginPortion() )
+ {
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+ pPor = pPor->GetNextPortion();
+ }
+ }
+ if( pPor && pCMS )
+ {
+ if( pCMS->m_bFieldInfo && pPor->InFieldGrp() && pPor->Width() )
+ pOrig->Width( pPor->Width() );
+ if( pPor->IsDropPortion() )
+ {
+ nPorAscent = static_cast<SwDropPortion*>(pPor)->GetDropHeight();
+ // The drop height is only calculated, if we have more than
+ // one line. Otherwise it is 0.
+ if ( ! nPorAscent)
+ nPorAscent = pPor->Height();
+ nPorHeight = nPorAscent;
+ pOrig->Height( nPorHeight +
+ static_cast<SwDropPortion*>(pPor)->GetDropDescent() );
+ if( nTmpHeight < pOrig->Height() )
+ {
+ nTmpAscent = nPorAscent;
+ nTmpHeight = sal_uInt16( pOrig->Height() );
+ }
+ }
+ if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
+ aInf.GetIdx() == nOfst )
+ {
+ if( !pPor->IsFlyPortion() && pPor->Height() &&
+ pPor->GetAscent() )
+ {
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+ SwTwips nTmp;
+ if (TextFrameIndex(2) > pPor->GetLen())
+ {
+ nTmp = pPor->Width();
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
+ }
+ else
+ {
+ const bool bOldOnWin = aInf.OnWin();
+ TextFrameIndex const nOldLen = pPor->GetLen();
+ pPor->SetLen( TextFrameIndex(1) );
+ aInf.SetLen( pPor->GetLen() );
+ SeekAndChg( aInf );
+ aInf.SetOnWin( false ); // no BULLETs!
+ aInf.SetKanaComp( pKanaComp );
+ aInf.SetKanaIdx( nKanaIdx );
+ nTmp = pPor->GetTextSize( aInf ).Width();
+ aInf.SetOnWin( bOldOnWin );
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
+ pPor->SetLen( nOldLen );
+ }
+ pOrig->Width( nTmp );
+ }
+
+ // travel inside field portion?
+ if ( pCMS->m_pSpecialPos )
+ {
+ // apply attributes to font
+ Seek( nOfst );
+ lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
+ }
+ }
+ }
+
+ // special case: We are at the beginning of a BidiPortion or
+ // directly behind a BidiPortion
+ if ( pCMS &&
+ ( pLastBidiPor ||
+ ( pPor &&
+ pPor->IsMultiPortion() &&
+ static_cast<SwMultiPortion*>(pPor)->IsBidi() ) ) )
+ {
+ // we determine if the cursor has to blink before or behind
+ // the bidi portion
+ if ( pLastBidiPor )
+ {
+ const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
+
+ if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
+ {
+ // we came from inside the bidi portion, we want to blink
+ // behind the portion
+ pOrig->Pos().AdjustX( -nLastBidiPorWidth );
+
+ // Again, there is a special case: logically behind
+ // the portion can actually mean that the cursor is inside
+ // the portion. This can happen is the last portion
+ // inside the bidi portion is a nested bidi portion
+ SwLineLayout& rLineLayout =
+ static_cast<SwMultiPortion*>(pLastBidiPor)->GetRoot();
+
+ const SwLinePortion *pLast = rLineLayout.FindLastPortion();
+ if ( pLast->IsMultiPortion() )
+ {
+ OSL_ENSURE( static_cast<const SwMultiPortion*>(pLast)->IsBidi(),
+ "Non-BidiPortion inside BidiPortion" );
+ TextFrameIndex const nIdx = aInf.GetIdx();
+ // correct the index before using CalcSpacing.
+ aInf.SetIdx(nLastBidiIdx);
+ pOrig->Pos().AdjustX(pLast->Width() +
+ pLast->CalcSpacing( nSpaceAdd, aInf ) );
+ aInf.SetIdx(nIdx);
+ }
+ }
+ }
+ else
+ {
+ const sal_uInt8 nPortionLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
+
+ if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
+ {
+ // we came from inside the bidi portion, we want to blink
+ // behind the portion
+ pOrig->Pos().AdjustX(pPor->Width() +
+ pPor->CalcSpacing( nSpaceAdd, aInf ) );
+ }
+ }
+ }
+
+ pOrig->Pos().AdjustX(nX );
+
+ if ( pCMS && pCMS->m_bRealHeight )
+ {
+ nTmpAscent = AdjustBaseLine( *m_pCurr, nullptr, nPorHeight, nPorAscent );
+ if ( nTmpAscent > nPorAscent )
+ pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
+ else
+ pCMS->m_aRealHeight.setX( 0 );
+ OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
+ if ( nTmpHeight > nPorHeight )
+ pCMS->m_aRealHeight.setY( nPorHeight );
+ else
+ pCMS->m_aRealHeight.setY( nTmpHeight );
+ }
+ }
+}
+
+void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
+ SwCursorMoveState* pCMS, const tools::Long nMax )
+{
+ CharCursorToLine(nOfst);
+
+ // Indicates that a position inside a special portion (field, number portion)
+ // is requested.
+ const bool bSpecialPos = pCMS && pCMS->m_pSpecialPos;
+ TextFrameIndex nFindOfst = nOfst;
+
+ if ( bSpecialPos )
+ {
+ const SwSPExtendRange nExtendRange = pCMS->m_pSpecialPos->nExtendRange;
+
+ OSL_ENSURE( ! pCMS->m_pSpecialPos->nLineOfst || SwSPExtendRange::BEFORE != nExtendRange,
+ "LineOffset AND Number Portion?" );
+
+ // portions which are behind the string
+ if ( SwSPExtendRange::BEHIND == nExtendRange )
+ ++nFindOfst;
+
+ // skip lines for fields which cover more than one line
+ for ( sal_uInt16 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
+ Next();
+ }
+
+ // If necessary, as catch up, do the adjustment
+ GetAdjusted();
+
+ const Point aCharPos( GetTopLeft() );
+
+ GetCharRect_( pOrig, nFindOfst, pCMS );
+
+ // This actually would have to be "-1 LogicToPixel", but that seems too
+ // expensive, so it's a value (-12), that should hopefully be OK.
+ const SwTwips nTmpRight = Right() - 12;
+
+ pOrig->Pos().AdjustX(aCharPos.X() );
+ pOrig->Pos().AdjustY(aCharPos.Y() );
+
+ if( pCMS && pCMS->m_b2Lines && pCMS->m_p2Lines )
+ {
+ pCMS->m_p2Lines->aLine.Pos().AdjustX(aCharPos.X() );
+ pCMS->m_p2Lines->aLine.Pos().AdjustY(aCharPos.Y() );
+ pCMS->m_p2Lines->aPortion.Pos().AdjustX(aCharPos.X() );
+ pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
+ }
+
+ const IDocumentSettingAccess& rIDSA = GetTextFrame()->GetDoc().getIDocumentSettingAccess();
+ const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN)
+ || rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
+ // Make sure the cursor respects the right margin, unless in compat mode, where the tab size has priority over the margin size.
+ if( pOrig->Left() > nTmpRight && !bTabOverMargin)
+ pOrig->Pos().setX( nTmpRight );
+
+ if( nMax )
+ {
+ if( pOrig->Top() + pOrig->Height() > nMax )
+ {
+ if( pOrig->Top() > nMax )
+ pOrig->Top( nMax );
+ pOrig->Height( nMax - pOrig->Top() );
+ }
+ if ( pCMS && pCMS->m_bRealHeight && pCMS->m_aRealHeight.Y() >= 0 )
+ {
+ tools::Long nTmp = pCMS->m_aRealHeight.X() + pOrig->Top();
+ if( nTmp >= nMax )
+ {
+ pCMS->m_aRealHeight.setX( nMax - pOrig->Top() );
+ pCMS->m_aRealHeight.setY( 0 );
+ }
+ else if( nTmp + pCMS->m_aRealHeight.Y() > nMax )
+ pCMS->m_aRealHeight.setY( nMax - nTmp );
+ }
+ }
+ tools::Long nOut = pOrig->Right() - GetTextFrame()->getFrameArea().Right();
+ if( nOut > 0 )
+ {
+ if( GetTextFrame()->getFrameArea().Width() < GetTextFrame()->getFramePrintArea().Left()
+ + GetTextFrame()->getFramePrintArea().Width() )
+ nOut += GetTextFrame()->getFrameArea().Width() - GetTextFrame()->getFramePrintArea().Left()
+ - GetTextFrame()->getFramePrintArea().Width();
+ if( nOut > 0 )
+ pOrig->Pos().AdjustX( -(nOut + 10) );
+ }
+}
+
+/**
+ * Determines if SwTextCursor::GetModelPositionForViewPoint() should consider the next portion when calculating the
+ * doc model position from a Point.
+ */
+static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, SwTwips nWidth30, sal_uInt16 nX)
+{
+ if (!pPor->GetNextPortion() || pPor->IsBreakPortion())
+ {
+ return false;
+ }
+
+ // tdf#138592: consider all following zero-width text portions of current text portion,
+ // like combining characters.
+ if (nWidth30 == nX && pPor->IsTextPortion() && pPor->GetNextPortion()->IsTextPortion()
+ && pPor->GetNextPortion()->Width() == 0)
+ return true;
+
+ // If we're past the target position, stop the iteration in general.
+ // Exception: don't stop the iteration between as-char fly portions and their comments.
+ if (nWidth30 >= nX && (!pPor->IsFlyCntPortion() || !pPor->GetNextPortion()->IsPostItsPortion()))
+ {
+ // Normally returns false.
+
+ // Another exception: If the cursor is at the very end of the portion, and the next portion is a comment,
+ // then place the cursor after the zero-width comment. This is primarily to benefit the very end of a line.
+ return nWidth30 == nX && pPor->GetNextPortion()->IsPostItsPortion();
+ }
+
+ return true;
+}
+
+// Return: Offset in String
+TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
+ bool bChgNode, SwCursorMoveState* pCMS ) const
+{
+ // If necessary, as catch up, do the adjustment
+ GetAdjusted();
+
+ const OUString &rText = GetInfo().GetText();
+ TextFrameIndex nOffset(0);
+
+ // x is the horizontal offset within the line.
+ SwTwips x = rPoint.X();
+ const SwTwips nLeftMargin = GetLineStart();
+ SwTwips nRightMargin = GetLineEnd() +
+ ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 );
+ if( nRightMargin == nLeftMargin )
+ nRightMargin += 30;
+
+ const bool bLeftOver = x < nLeftMargin;
+ if( bLeftOver )
+ x = nLeftMargin;
+ const bool bRightOver = x > nRightMargin;
+ if( bRightOver )
+ x = nRightMargin;
+
+ const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );
+
+ // Until here everything in document coordinates.
+ x -= nLeftMargin;
+
+ SwTwips nX = x;
+
+ // If there are attribute changes in the line, search for the paragraph,
+ // in which nX is situated.
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ TextFrameIndex nCurrStart = m_nStart;
+ bool bHolePortion = false;
+ bool bLastHyph = false;
+
+ std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
+ TextFrameIndex const nOldIdx = GetInfo().GetIdx();
+ sal_uInt16 nSpaceIdx = 0;
+ size_t nKanaIdx = 0;
+ tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
+ short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
+
+ // nWidth is the width of the line, or the width of
+ // the paragraph with the font change, in which nX is situated.
+
+ SwTwips nWidth = pPor->Width();
+ if ( m_pCurr->IsSpaceAdd() || pKanaComp )
+ {
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ {
+ const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
+ nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
+ }
+ if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
+ ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
+ )
+ {
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if( pKanaComp )
+ {
+ if ( nKanaIdx + 1 < pKanaComp->size() )
+ nKanaComp = (*pKanaComp)[++nKanaIdx];
+ else
+ nKanaComp = 0;
+ }
+ }
+ }
+
+ SwTwips nWidth30;
+ if ( pPor->IsPostItsPortion() )
+ nWidth30 = 0;
+ else
+ nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
+ 30 :
+ nWidth;
+
+ while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
+ {
+ nX = nX - nWidth;
+ nCurrStart = nCurrStart + pPor->GetLen();
+ bHolePortion = pPor->IsHolePortion();
+ pPor = pPor->GetNextPortion();
+ nWidth = pPor->Width();
+ if ( m_pCurr->IsSpaceAdd() || pKanaComp )
+ {
+ if ( pPor->InSpaceGrp() && nSpaceAdd )
+ {
+ const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
+ nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
+ }
+
+ if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
+ ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
+ )
+ {
+ if ( m_pCurr->IsSpaceAdd() )
+ {
+ if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
+ nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
+ else
+ nSpaceAdd = 0;
+ }
+
+ if ( pKanaComp )
+ {
+ if( nKanaIdx + 1 < pKanaComp->size() )
+ nKanaComp = (*pKanaComp)[++nKanaIdx];
+ else
+ nKanaComp = 0;
+ }
+ }
+ }
+
+ if ( pPor->IsPostItsPortion() )
+ nWidth30 = 0;
+ else
+ nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
+ 30 :
+ nWidth;
+ if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
+ bLastHyph = pPor->InHyphGrp();
+ }
+
+ const bool bLastPortion = (nullptr == pPor->GetNextPortion());
+
+ if( nX==nWidth )
+ {
+ SwLinePortion *pNextPor = pPor->GetNextPortion();
+ while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
+ {
+ nCurrStart = nCurrStart + pPor->GetLen();
+ pPor = pNextPor;
+ if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
+ bLastHyph = pPor->InHyphGrp();
+ pNextPor = pPor->GetNextPortion();
+ }
+ }
+
+ const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nOldIdx );
+
+ TextFrameIndex nLength = pPor->GetLen();
+
+ const bool bFieldInfo = pCMS && pCMS->m_bFieldInfo;
+
+ if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
+ ( pPor->InNumberGrp() && !pPor->IsFootnoteNumPortion() ) ||
+ ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
+ pCMS->m_bPosCorr = true;
+
+ // #i27615#
+ if (pCMS && pCMS->m_bInFrontOfLabel)
+ {
+ if (2 * nX >= nWidth || !pPor->InNumberGrp() || pPor->IsFootnoteNumPortion())
+ pCMS->m_bInFrontOfLabel = false;
+ }
+
+ // 7684: We are exactly ended up at their HyphPortion. It is our task to
+ // provide, that we end up in the String.
+ // 7993: If length = 0, then we must exit...
+ if( !nLength )
+ {
+ if( pCMS )
+ {
+ if( pPor->IsFlyPortion() && bFieldInfo )
+ pCMS->m_bPosCorr = true;
+
+ if (!bRightOver && nX)
+ {
+ if( pPor->IsFootnoteNumPortion())
+ pCMS->m_bFootnoteNoInfo = true;
+ else if (pPor->InNumberGrp() ) // #i23726#
+ {
+ pCMS->m_nInNumPortionOffset = nX;
+ pCMS->m_bInNumPortion = true;
+ }
+ }
+ }
+ if( !nCurrStart )
+ return TextFrameIndex(0);
+
+ // 7849, 7816: pPor->GetHyphPortion is mandatory!
+ if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
+ ( pPor->IsMarginPortion() && !pPor->GetNextPortion() &&
+ // 46598: Consider the situation: We might end up behind the last character,
+ // in the last line of a centered paragraph
+ nCurrStart < TextFrameIndex(rText.getLength())))
+ --nCurrStart;
+ else if( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->IsFollow()
+ && nWidth > nX )
+ {
+ if( bFieldInfo )
+ --nCurrStart;
+ else
+ {
+ sal_uInt16 nHeight = pPor->Height();
+ if ( !nHeight || nHeight > nWidth )
+ nHeight = nWidth;
+ if( bChgNode && nWidth - nHeight/2 > nX )
+ --nCurrStart;
+ }
+ }
+ return nCurrStart;
+ }
+ if (TextFrameIndex(1) == nLength || pPor->InFieldGrp())
+ {
+ if ( nWidth )
+ {
+ // no quick return for as-character frames, we want to peek inside
+ if (!(bChgNode && pPos && pPor->IsFlyCntPortion())
+ // if we want to get the position inside the field, we should not return
+ && (!pCMS || !pCMS->m_pSpecialPos))
+ {
+ if ( pPor->InFieldGrp() ||
+ ( pPor->IsMultiPortion() &&
+ static_cast<SwMultiPortion*>(pPor)->IsBidi() ) )
+ {
+ sal_uInt16 nHeight = 0;
+ if( !bFieldInfo )
+ {
+ nHeight = pPor->Height();
+ if ( !nHeight || nHeight > nWidth )
+ nHeight = nWidth;
+ }
+
+ if( nWidth - nHeight/2 <= nX &&
+ ( ! pPor->InFieldGrp() ||
+ !static_cast<SwFieldPortion*>(pPor)->HasFollow() ) )
+ {
+ if (pPor->InFieldGrp())
+ {
+ nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
+ }
+ else
+ {
+ ++nCurrStart;
+ }
+ }
+ }
+ else if ( ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
+ !pPor->GetNextPortion()->IsMarginPortion() &&
+ !pPor->GetNextPortion()->IsHolePortion() ) )
+ && ( nWidth/2 < nX ) &&
+ ( !bFieldInfo ||
+ ( pPor->GetNextPortion() &&
+ pPor->GetNextPortion()->IsPostItsPortion() ) )
+ && ( bRightAllowed || !bLastHyph ))
+ ++nCurrStart;
+
+ return nCurrStart;
+ }
+ }
+ else
+ {
+ if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
+ pPor->InToxRefGrp() )
+ {
+ SwPostItsPortion* pPostItsPortion = pPor->IsPostItsPortion() ? dynamic_cast<SwPostItsPortion*>(pPor) : nullptr;
+ if (pPostItsPortion)
+ {
+ if (!pPostItsPortion->IsScript()) // tdf#141079
+ {
+ // Offset would be nCurrStart + nLength below, do the same for post-it portions.
+ nCurrStart += pPor->GetLen();
+ }
+ }
+ return nCurrStart;
+ }
+ if ( pPor->InFieldGrp() )
+ {
+ if( bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow() )
+ {
+ nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
+ }
+ return nCurrStart;
+ }
+ }
+ }
+
+ // Skip space at the end of the line
+ if( bLastPortion && (m_pCurr->GetNext() || m_pFrame->GetFollow() )
+ && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ' )
+ --nLength;
+
+ if( nWidth > nX ||
+ ( nWidth == nX && pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsDouble() ) )
+ {
+ if( pPor->IsMultiPortion() )
+ {
+ // In a multi-portion we use GetModelPositionForViewPoint()-function recursively
+ SwTwips nTmpY = rPoint.Y() - m_pCurr->GetAscent() + pPor->GetAscent();
+ // if we are in the first line of a double line portion, we have
+ // to add a value to nTmpY for not staying in this line
+ // we also want to skip the first line, if we are inside ruby
+ if ( ( static_cast<SwTextSizeInfo*>(m_pInf)->IsMulti() &&
+ static_cast<SwTextSizeInfo*>(m_pInf)->IsFirstMulti() ) ||
+ ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
+ static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
+ nTmpY += static_cast<SwMultiPortion*>(pPor)->Height();
+
+ // Important for cursor traveling in ruby portions:
+ // We have to set nTmpY to 0 in order to stay in the first row
+ // if the phonetic line is the second row
+ if ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
+ ! static_cast<SwMultiPortion*>(pPor)->OnTop() )
+ nTmpY = 0;
+
+ SwTextCursorSave aSave( const_cast<SwTextCursor*>(this), static_cast<SwMultiPortion*>(pPor),
+ nTmpY, nX, nCurrStart, nSpaceAdd );
+
+ SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
+ if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ {
+ const sal_uInt8 nBidiLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
+ aLayoutModeModifier.Modify( nBidiLevel % 2 );
+ }
+
+ if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
+ {
+ nTmpY -= m_nY;
+ if( !static_cast<SwMultiPortion*>(pPor)->IsRevers() )
+ nTmpY = pPor->Height() - nTmpY;
+ if( nTmpY < 0 )
+ nTmpY = 0;
+ nX = o3tl::narrowing<sal_uInt16>(nTmpY);
+ }
+
+ if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
+ {
+ const sal_uInt16 nPreWidth = static_cast<SwDoubleLinePortion*>(pPor)->PreWidth();
+ if ( nX > nPreWidth )
+ nX = nX - nPreWidth;
+ else
+ nX = 0;
+ }
+
+ return GetModelPositionForViewPoint( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
+ bChgNode, pCMS );
+ }
+ if( pPor->InTextGrp() )
+ {
+ sal_uInt8 nOldProp;
+ if( GetPropFont() )
+ {
+ const_cast<SwFont*>(GetFnt())->SetProportion( GetPropFont() );
+ nOldProp = GetFnt()->GetPropr();
+ }
+ else
+ nOldProp = 0;
+ {
+ SwTextSizeInfo aSizeInf( GetInfo(), &rText, nCurrStart );
+ const_cast<SwTextCursor*>(this)->SeekAndChg( aSizeInf );
+ SwTextSlot aDiffText( &aSizeInf, static_cast<SwTextPortion*>(pPor), false, false );
+ SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
+ static_cast<SwDropPortion*>(pPor)->GetFnt() : nullptr );
+
+ SwParaPortion* pPara = const_cast<SwParaPortion*>(GetInfo().GetParaPortion());
+ OSL_ENSURE( pPara, "No paragraph!" );
+
+ // protect against bugs elsewhere
+ SAL_WARN_IF( aSizeInf.GetIdx().get() + pPor->GetLen().get() > aSizeInf.GetText().getLength(), "sw", "portion and text are out of sync" );
+ TextFrameIndex nSafeLen( std::min(pPor->GetLen().get(), aSizeInf.GetText().getLength() - aSizeInf.GetIdx().get()) );
+
+ SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
+ *aSizeInf.GetOut(),
+ &pPara->GetScriptInfo(),
+ aSizeInf.GetText(),
+ aSizeInf.GetIdx(),
+ nSafeLen );
+
+ // Drop portion works like a multi portion, just its parts are not portions
+ if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
+ {
+ SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
+ const SwDropPortionPart* pCurrPart = pDrop->GetPart();
+ sal_uInt16 nSumWidth = 0;
+ sal_uInt16 nSumBorderWidth = 0;
+ // Shift offset with the right and left border of previous parts and left border of actual one
+ while (pCurrPart && nSumWidth <= nX - sal_Int32(nCurrStart))
+ {
+ nSumWidth += pCurrPart->GetWidth();
+ if( pCurrPart->GetFont().GetLeftBorder() && !pCurrPart->GetJoinBorderWithPrev() )
+ {
+ nSumBorderWidth += pCurrPart->GetFont().GetLeftBorderSpace();
+ }
+ if (nSumWidth <= nX - sal_Int32(nCurrStart) && pCurrPart->GetFont().GetRightBorder() &&
+ !pCurrPart->GetJoinBorderWithNext() )
+ {
+ nSumBorderWidth += pCurrPart->GetFont().GetRightBorderSpace();
+ }
+ pCurrPart = pCurrPart->GetFollow();
+ }
+ nX = std::max(static_cast<SwTwips>(0), nX - nSumBorderWidth);
+ }
+ // Shift the offset with the left border width
+ else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() )
+ {
+ nX = std::max(static_cast<SwTwips>(0), nX - GetInfo().GetFont()->GetLeftBorderSpace());
+ }
+
+ aDrawInf.SetOffset( nX );
+
+ if ( nSpaceAdd )
+ {
+ TextFrameIndex nCharCnt(0);
+ // #i41860# Thai justified alignment needs some
+ // additional information:
+ aDrawInf.SetNumberOfBlanks( pPor->InTextGrp() ?
+ static_cast<const SwTextPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
+ TextFrameIndex(0) );
+ }
+
+ if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
+ aDrawInf.SetLen( TextFrameIndex(COMPLETE_STRING) );
+
+ aDrawInf.SetSpace( nSpaceAdd );
+ aDrawInf.SetFont( aSizeInf.GetFont() );
+ aDrawInf.SetFrame( m_pFrame );
+ aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
+ aDrawInf.SetPosMatchesBounds( pCMS && pCMS->m_bPosMatchesBounds );
+
+ if ( SwFontScript::CJK == aSizeInf.GetFont()->GetActual() &&
+ pPara->GetScriptInfo().CountCompChg() &&
+ ! pPor->InFieldGrp() )
+ aDrawInf.SetKanaComp( nKanaComp );
+
+ nLength = aSizeInf.GetFont()->GetModelPositionForViewPoint_( aDrawInf );
+
+ // get position inside field portion?
+ if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
+ {
+ pCMS->m_pSpecialPos->nCharOfst = sal_Int32(nLength);
+ nLength = TextFrameIndex(0);
+ }
+
+ // set cursor bidi level
+ if ( pCMS )
+ pCMS->m_nCursorBidiLevel =
+ aDrawInf.GetCursorBidiLevel();
+
+ if( bFieldInfo && nLength == pPor->GetLen() &&
+ ( ! pPor->GetNextPortion() ||
+ ! pPor->GetNextPortion()->IsPostItsPortion() ) )
+ --nLength;
+ }
+ if( nOldProp )
+ const_cast<SwFont*>(GetFnt())->SetProportion( nOldProp );
+ }
+ else
+ {
+ sw::FlyContentPortion* pFlyPor(nullptr);
+ if(bChgNode && pPos && (pFlyPor = dynamic_cast<sw::FlyContentPortion*>(pPor)))
+ {
+ // JP 24.11.94: if the Position is not in Fly, then
+ // we many not return with COMPLETE_STRING as value!
+ // (BugId: 9692 + Change in feshview)
+ SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
+ SwFrame* pLower = pTmp->GetLower();
+ // Allow non-text-frames to get SwGrfNode for as-char anchored images into pPos
+ // instead of the closest SwTextNode, to be consistent with at-char behavior.
+ bool bChgNodeInner = pLower
+ && (pLower->IsTextFrame() || pLower->IsLayoutFrame() || pLower->IsNoTextFrame());
+ Point aTmpPoint( rPoint );
+
+ if ( m_pFrame->IsRightToLeft() )
+ m_pFrame->SwitchLTRtoRTL( aTmpPoint );
+
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchHorizontalToVertical( aTmpPoint );
+
+ if( bChgNodeInner && pTmp->getFrameArea().Contains( aTmpPoint ) &&
+ !( pTmp->IsProtected() ) )
+ {
+ pFlyPor->GetFlyCursorOfst(aTmpPoint, *pPos, pCMS);
+ // After a change of the frame, our font must be still
+ // available for/in the OutputDevice.
+ // For comparison: Paint and new SwFlyCntPortion !
+ static_cast<SwTextSizeInfo*>(m_pInf)->SelectFont();
+
+ // 6776: The pIter->GetModelPositionForViewPoint is returning here
+ // from a nesting with COMPLETE_STRING.
+ return TextFrameIndex(COMPLETE_STRING);
+ }
+ }
+ else
+ nLength = pPor->GetModelPositionForViewPoint( nX );
+ }
+ }
+ nOffset = nCurrStart + nLength;
+
+ // 7684: We end up in front of the HyphPortion. We must assure
+ // that we end up in the string.
+ // If we are at end of line in front of FlyFrames, we must proceed the same way.
+ if( nOffset && pPor->GetLen() == nLength && pPor->GetNextPortion() &&
+ !pPor->GetNextPortion()->GetLen() && pPor->GetNextPortion()->InHyphGrp() )
+ --nOffset;
+
+ return nOffset;
+}
+
+/** Looks for text portions which are inside the given rectangle
+
+ For a rectangular text selection every text portions which is inside the given
+ rectangle has to be put into the SwSelectionList as SwPaM
+ From these SwPaM the SwCursors will be created.
+
+ @param rSelList
+ The container for the overlapped text portions
+
+ @param rRect
+ A rectangle in document coordinates, text inside this rectangle has to be
+ selected.
+
+ @return [ true, false ]
+ true if any overlapping text portion has been found and put into list
+ false if no portion overlaps, the list has been unchanged
+*/
+bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
+{
+ bool bRet = false;
+ // GetPaintArea() instead getFrameArea() for negative indents
+ SwRect aTmpFrame( GetPaintArea() );
+ if( !rRect.Overlaps( aTmpFrame ) )
+ return false;
+ if( rSelList.checkContext( this ) )
+ {
+ SwRect aRect( aTmpFrame );
+ aRect.Intersection( rRect );
+ SwPosition aPosL( MapViewToModelPos(TextFrameIndex(0)) );
+ if( IsEmpty() )
+ {
+ SwPaM *pPam = new SwPaM( aPosL, aPosL );
+ rSelList.insertPaM( pPam );
+ }
+ else if( aRect.HasArea() )
+ {
+ SwPosition aOld(aPosL.nNode.GetNodes().GetEndOfContent());
+ SwPosition aPosR( aPosL );
+ Point aPoint;
+ SwTextInfo aInf( const_cast<SwTextFrame*>(this) );
+ SwTextIter aLine( const_cast<SwTextFrame*>(this), &aInf );
+ // We have to care for top-to-bottom layout, where right becomes top etc.
+ SwRectFnSet aRectFnSet(this);
+ SwTwips nTop = aRectFnSet.GetTop(aRect);
+ SwTwips nBottom = aRectFnSet.GetBottom(aRect);
+ SwTwips nLeft = aRectFnSet.GetLeft(aRect);
+ SwTwips nRight = aRectFnSet.GetRight(aRect);
+ SwTwips nY = aLine.Y(); // Top position of the first line
+ SwTwips nLastY = nY;
+ while( nY < nTop && aLine.Next() ) // line above rectangle
+ {
+ nLastY = nY;
+ nY = aLine.Y();
+ }
+ bool bLastLine = false;
+ if( nY < nTop && !aLine.GetNext() )
+ {
+ bLastLine = true;
+ nY += aLine.GetLineHeight();
+ }
+ do // check the lines for overlapping
+ {
+ if( nLastY < nTop ) // if the last line was above rectangle
+ nLastY = nTop;
+ if( nY > nBottom ) // if the current line leaves the rectangle
+ nY = nBottom;
+ if( nY >= nLastY ) // gotcha: overlapping
+ {
+ nLastY += nY;
+ nLastY /= 2;
+ if( aRectFnSet.IsVert() )
+ {
+ aPoint.setX( nLastY );
+ aPoint.setY( nLeft );
+ }
+ else
+ {
+ aPoint.setX( nLeft );
+ aPoint.setY( nLastY );
+ }
+ // Looking for the position of the left border of the rectangle
+ // in this text line
+ SwCursorMoveState aState( CursorMoveState::UpDown );
+ if( GetModelPositionForViewPoint( &aPosL, aPoint, &aState ) )
+ {
+ if( aRectFnSet.IsVert() )
+ {
+ aPoint.setX( nLastY );
+ aPoint.setY( nRight );
+ }
+ else
+ {
+ aPoint.setX( nRight );
+ aPoint.setY( nLastY );
+ }
+ // If we get a right position and if the left position
+ // is not the same like the left position of the line before
+ // which could happen e.g. for field portions or fly frames
+ // a SwPaM will be inserted with these positions
+ if( GetModelPositionForViewPoint( &aPosR, aPoint, &aState ) &&
+ aOld != aPosL)
+ {
+ SwPaM *pPam = new SwPaM( aPosL, aPosR );
+ rSelList.insertPaM( pPam );
+ aOld = aPosL;
+ }
+ }
+ }
+ if( aLine.Next() )
+ {
+ nLastY = nY;
+ nY = aLine.Y();
+ }
+ else if( !bLastLine )
+ {
+ bLastLine = true;
+ nLastY = nY;
+ nY += aLine.GetLineHeight();
+ }
+ else
+ break;
+ }while( nLastY < nBottom );
+ }
+ }
+ if( GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *GetDrawObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
+ if( !pFly )
+ continue;
+ if( pFly->IsFlyInContentFrame() && pFly->FillSelection( rSelList, rRect ) )
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx
new file mode 100644
index 000000000..2401b40c3
--- /dev/null
+++ b/sw/source/core/text/itrform2.cxx
@@ -0,0 +1,3060 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <memory>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <editeng/lspcitem.hxx>
+#include <txtflcnt.hxx>
+#include <txtftn.hxx>
+#include <flyfrms.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtftn.hxx>
+#include <ftninfo.hxx>
+#include <charfmt.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <layfrm.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <paratr.hxx>
+#include "itrform2.hxx"
+#include "porrst.hxx"
+#include "portab.hxx"
+#include "porfly.hxx"
+#include "portox.hxx"
+#include "porref.hxx"
+#include "porfld.hxx"
+#include "porftn.hxx"
+#include "porhyph.hxx"
+#include "pordrop.hxx"
+#include "redlnitr.hxx"
+#include <sortedobjs.hxx>
+#include <fmtanchr.hxx>
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+#include <doc.hxx>
+#include "pormulti.hxx"
+#include <unotools/charclass.hxx>
+#include <xmloff/odffields.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IMark.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <comphelper/processfactory.hxx>
+#include <docsh.hxx>
+#include <unocrsrhelper.hxx>
+#include <com/sun/star/rdf/Statement.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/URIs.hpp>
+#include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
+#include <com/sun/star/rdf/XLiteral.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+
+using namespace ::com::sun::star;
+
+namespace {
+ //! Calculates and sets optimal repaint offset for the current line
+ tools::Long lcl_CalcOptRepaint( SwTextFormatter &rThis,
+ SwLineLayout const &rCurr,
+ TextFrameIndex nOldLineEnd,
+ const std::vector<tools::Long> &rFlyStarts );
+ //! Determine if we need to build hidden portions
+ bool lcl_BuildHiddenPortion(const SwTextSizeInfo& rInf, TextFrameIndex &rPos);
+
+ // Check whether the two font has the same border
+ bool lcl_HasSameBorder(const SwFont& rFirst, const SwFont& rSecond);
+}
+
+static void ClearFly( SwTextFormatInfo &rInf )
+{
+ delete rInf.GetFly();
+ rInf.SetFly(nullptr);
+}
+
+void SwTextFormatter::CtorInitTextFormatter( SwTextFrame *pNewFrame, SwTextFormatInfo *pNewInf )
+{
+ CtorInitTextPainter( pNewFrame, pNewInf );
+ m_pInf = pNewInf;
+ m_pDropFormat = GetInfo().GetDropFormat();
+ m_pMulti = nullptr;
+
+ m_bOnceMore = false;
+ m_bFlyInContentBase = false;
+ m_bTruncLines = false;
+ m_nContentEndHyph = 0;
+ m_nContentMidHyph = 0;
+ m_nLeftScanIdx = TextFrameIndex(COMPLETE_STRING);
+ m_nRightScanIdx = TextFrameIndex(0);
+ m_pByEndIter.reset();
+ m_pFirstOfBorderMerge = nullptr;
+
+ if (m_nStart > TextFrameIndex(GetInfo().GetText().getLength()))
+ {
+ OSL_ENSURE( false, "+SwTextFormatter::CTOR: bad offset" );
+ m_nStart = TextFrameIndex(GetInfo().GetText().getLength());
+ }
+
+}
+
+SwTextFormatter::~SwTextFormatter()
+{
+ // Extremely unlikely, but still possible
+ // e.g.: field splits up, widows start to matter
+ if( GetInfo().GetRest() )
+ {
+ delete GetInfo().GetRest();
+ GetInfo().SetRest(nullptr);
+ }
+}
+
+void SwTextFormatter::Insert( SwLineLayout *pLay )
+{
+ // Insert BEHIND the current element
+ if ( m_pCurr )
+ {
+ pLay->SetNext( m_pCurr->GetNext() );
+ m_pCurr->SetNext( pLay );
+ }
+ else
+ m_pCurr = pLay;
+}
+
+sal_uInt16 SwTextFormatter::GetFrameRstHeight() const
+{
+ // We want the rest height relative to the page.
+ // If we're in a table, then pFrame->GetUpper() is not the page.
+
+ // GetFrameRstHeight() is being called with Footnote.
+ // Wrong: const SwFrame *pUpper = pFrame->GetUpper();
+ const SwFrame *pPage = m_pFrame->FindPageFrame();
+ const SwTwips nHeight = pPage->getFrameArea().Top()
+ + pPage->getFramePrintArea().Top()
+ + pPage->getFramePrintArea().Height() - Y();
+ if( 0 > nHeight )
+ return m_pCurr->Height();
+ else
+ return sal_uInt16( nHeight );
+}
+
+bool SwTextFormatter::ClearIfIsFirstOfBorderMerge(const SwLinePortion* pPortion)
+{
+ if (pPortion == m_pFirstOfBorderMerge)
+ {
+ m_pFirstOfBorderMerge = nullptr;
+ return true;
+ }
+ return false;
+}
+
+SwLinePortion *SwTextFormatter::Underflow( SwTextFormatInfo &rInf )
+{
+ // Save values and initialize rInf
+ SwLinePortion *pUnderflow = rInf.GetUnderflow();
+ if( !pUnderflow )
+ return nullptr;
+
+ // We format backwards, i.e. attribute changes can happen the next
+ // line again.
+ // Can be seen in 8081.sdw, if you enter text in the first line
+
+ TextFrameIndex const nSoftHyphPos = rInf.GetSoftHyphPos();
+ TextFrameIndex const nUnderScorePos = rInf.GetUnderScorePos();
+
+ // Save flys and set to 0, or else segmentation fault
+ // Not ClearFly(rInf) !
+ SwFlyPortion *pFly = rInf.GetFly();
+ rInf.SetFly( nullptr );
+
+ FeedInf( rInf );
+ rInf.SetLast( m_pCurr );
+ // pUnderflow does not need to be deleted, because it will drown in the following
+ // Truncate()
+ rInf.SetUnderflow(nullptr);
+ rInf.SetSoftHyphPos( nSoftHyphPos );
+ rInf.SetUnderScorePos( nUnderScorePos );
+ rInf.SetPaintOfst( GetLeftMargin() );
+
+ // We look for the portion with the under-flow position
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ if( pPor != pUnderflow )
+ {
+ // pPrev will be the last portion before pUnderflow,
+ // which still has a real width.
+ // Exception: SoftHyphPortion must not be forgotten, of course!
+ // Although they don't have a width.
+ SwLinePortion *pTmpPrev = pPor;
+ while( pPor && pPor != pUnderflow )
+ {
+ if( !pPor->IsKernPortion() &&
+ ( pPor->Width() || pPor->IsSoftHyphPortion() ) )
+ {
+ while( pTmpPrev != pPor )
+ {
+ pTmpPrev->Move( rInf );
+ rInf.SetLast( pTmpPrev );
+ pTmpPrev = pTmpPrev->GetNextPortion();
+ OSL_ENSURE( pTmpPrev, "Underflow: losing control!" );
+ };
+ }
+ pPor = pPor->GetNextPortion();
+ }
+ pPor = pTmpPrev;
+ if( pPor && // Skip flys and initials when underflow.
+ ( pPor->IsFlyPortion() || pPor->IsDropPortion() ||
+ pPor->IsFlyCntPortion() ) )
+ {
+ pPor->Move( rInf );
+ rInf.SetLast( pPor );
+ rInf.SetStopUnderflow( true );
+ pPor = pUnderflow;
+ }
+ }
+
+ // What? The under-flow portion is not in the portion chain?
+ OSL_ENSURE( pPor, "SwTextFormatter::Underflow: overflow but underflow" );
+
+ // Snapshot
+ if ( pPor==rInf.GetLast() )
+ {
+ // We end up here, if the portion triggering the under-flow
+ // spans over the whole line. E.g. if a word spans across
+ // multiple lines and flows into a fly in the second line.
+ rInf.SetFly( pFly );
+ pPor->Truncate();
+ return pPor; // Is that enough?
+ }
+ // End the snapshot
+
+ // X + Width == 0 with SoftHyph > Line?!
+ if( !pPor || !(rInf.X() + pPor->Width()) )
+ {
+ delete pFly;
+ return nullptr;
+ }
+
+ // Preparing for Format()
+ // We need to chip off the chain behind pLast, because we Insert after the Format()
+ SeekAndChg( rInf );
+
+ // line width is adjusted, so that pPor does not fit to current
+ // line anymore
+ rInf.Width( rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0) );
+ rInf.SetLen( pPor->GetLen() );
+ rInf.SetFull( false );
+ if( pFly )
+ {
+ // We need to recalculate the FlyPortion due to the following reason:
+ // If the base line is lowered by a big font in the middle of the line,
+ // causing overlapping with a fly, the FlyPortion has a wrong size/fixed
+ // size.
+ rInf.SetFly( pFly );
+ CalcFlyWidth( rInf );
+ }
+ rInf.GetLast()->SetNextPortion(nullptr);
+
+ // The SwLineLayout is an exception to this, which splits at the first
+ // portion change.
+ // Here only the other way around:
+ if( rInf.GetLast() == m_pCurr )
+ {
+ if( pPor->InTextGrp() && !pPor->InExpGrp() )
+ {
+ const PortionType nOldWhich = m_pCurr->GetWhichPor();
+ *static_cast<SwLinePortion*>(m_pCurr) = *pPor;
+ m_pCurr->SetNextPortion( pPor->GetNextPortion() );
+ m_pCurr->SetWhichPor( nOldWhich );
+ pPor->SetNextPortion( nullptr );
+ delete pPor;
+ pPor = m_pCurr;
+ }
+ }
+
+ // Make sure that m_pFirstOfBorderMerge does not point to a portion which
+ // will be deleted by Truncate() below.
+ SwLinePortion* pNext = pPor->GetNextPortion();
+ while (pNext)
+ {
+ if (ClearIfIsFirstOfBorderMerge(pNext))
+ break;
+ pNext = pNext->GetNextPortion();
+ }
+ pPor->Truncate();
+ SwLinePortion *const pRest( rInf.GetRest() );
+ if (pRest && pRest->InFieldGrp() &&
+ static_cast<SwFieldPortion*>(pRest)->IsNoLength())
+ {
+ // HACK: decrement again, so we pick up the suffix in next line!
+ m_pByEndIter->PrevAttr();
+ }
+ delete pRest;
+ rInf.SetRest(nullptr);
+ return pPor;
+}
+
+void SwTextFormatter::InsertPortion( SwTextFormatInfo &rInf,
+ SwLinePortion *pPor )
+{
+ SwLinePortion *pLast = nullptr;
+ // The new portion is inserted, but everything's different for
+ // LineLayout...
+ if( pPor == m_pCurr )
+ {
+ if ( m_pCurr->GetNextPortion() )
+ {
+ pLast = pPor;
+ pPor = m_pCurr->GetNextPortion();
+ }
+
+ // i#112181 - Prevent footnote anchor being wrapped to next line
+ // without preceding word
+ rInf.SetOtherThanFootnoteInside( rInf.IsOtherThanFootnoteInside() || !pPor->IsFootnotePortion() );
+ }
+ else
+ {
+ pLast = rInf.GetLast();
+ if( pLast->GetNextPortion() )
+ {
+ while( pLast->GetNextPortion() )
+ pLast = pLast->GetNextPortion();
+ rInf.SetLast( pLast );
+ }
+ pLast->Insert( pPor );
+
+ rInf.SetOtherThanFootnoteInside( rInf.IsOtherThanFootnoteInside() || !pPor->IsFootnotePortion() );
+
+ // Adjust maxima
+ if( m_pCurr->Height() < pPor->Height() )
+ m_pCurr->Height( pPor->Height(), pPor->IsTextPortion() );
+ if( m_pCurr->GetAscent() < pPor->GetAscent() )
+ m_pCurr->SetAscent( pPor->GetAscent() );
+ if( m_pCurr->GetHangingBaseline() < pPor->GetHangingBaseline() )
+ m_pCurr->SetHangingBaseline( pPor->GetHangingBaseline() );
+
+ if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::MS_WORD_COMP_MIN_LINE_HEIGHT_BY_FLY))
+ {
+ // For DOCX with compat=14 the only shape in line defines height of the line in spite of used font
+ if (pLast->IsFlyCntPortion() && pPor->IsTextPortion() && pPor->GetLen() == TextFrameIndex(0))
+ {
+ m_pCurr->SetAscent(pLast->GetAscent());
+ m_pCurr->Height(pLast->Height());
+ }
+ }
+ }
+
+ // Sometimes chains are constructed (e.g. by hyphenate)
+ rInf.SetLast( pPor );
+ while( pPor )
+ {
+ if (!pPor->IsDropPortion())
+ MergeCharacterBorder(*pPor, pLast, rInf);
+
+ pPor->Move( rInf );
+ rInf.SetLast( pPor );
+ pLast = pPor;
+ pPor = pPor->GetNextPortion();
+ }
+}
+
+void SwTextFormatter::BuildPortions( SwTextFormatInfo &rInf )
+{
+ OSL_ENSURE( rInf.GetText().getLength() < COMPLETE_STRING,
+ "SwTextFormatter::BuildPortions: bad text length in info" );
+
+ rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
+
+ // First NewTextPortion() decides whether pCurr ends up in pPor.
+ // We need to make sure that the font is being set in any case.
+ // This is done automatically in CalcAscent.
+ rInf.SetLast( m_pCurr );
+ rInf.ForcedLeftMargin( 0 );
+
+ OSL_ENSURE( m_pCurr->FindLastPortion() == m_pCurr, "pLast supposed to equal pCurr" );
+
+ if( !m_pCurr->GetAscent() && !m_pCurr->Height() )
+ CalcAscent( rInf, m_pCurr );
+
+ SeekAndChg( rInf );
+
+ // Width() is shortened in CalcFlyWidth if we have a FlyPortion
+ OSL_ENSURE( !rInf.X() || m_pMulti, "SwTextFormatter::BuildPortion X=0?" );
+ CalcFlyWidth( rInf );
+ SwFlyPortion *pFly = rInf.GetFly();
+ if( pFly )
+ {
+ if ( 0 < pFly->GetFix() )
+ ClearFly( rInf );
+ else
+ rInf.SetFull(true);
+ }
+
+ ::std::optional<TextFrameIndex> oMovedFlyIndex;
+ if (SwTextFrame const*const pFollow = GetTextFrame()->GetFollow())
+ {
+ // flys are always on master!
+ if (GetTextFrame()->GetDrawObjs() && pFollow->GetUpper() != GetTextFrame()->GetUpper())
+ {
+ for (SwAnchoredObject const*const pAnchoredObj : *GetTextFrame()->GetDrawObjs())
+ {
+ // tdf#146500 try to stop where a fly is anchored in the follow
+ // that has recently been moved (presumably by splitting this
+ // frame); similar to check in SwFlowFrame::MoveBwd()
+ if (pAnchoredObj->RestartLayoutProcess()
+ && !pAnchoredObj->IsTmpConsiderWrapInfluence())
+ {
+ SwFormatAnchor const& rAnchor(pAnchoredObj->GetFrameFormat().GetAnchor());
+ assert(rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA);
+ TextFrameIndex const nAnchor(GetTextFrame()->MapModelToViewPos(*rAnchor.GetContentAnchor()));
+ if (pFollow->GetOffset() <= nAnchor
+ && (pFollow->GetFollow() == nullptr
+ || nAnchor < pFollow->GetFollow()->GetOffset()))
+ {
+ if (!oMovedFlyIndex || nAnchor < *oMovedFlyIndex)
+ {
+ oMovedFlyIndex.emplace(nAnchor);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ SwLinePortion *pPor = NewPortion(rInf, oMovedFlyIndex);
+
+ // Asian grid stuff
+ SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
+ const bool bHasGrid = pGrid && rInf.SnapToGrid() &&
+ GRID_LINES_CHARS == pGrid->GetGridType();
+
+
+ const SwDoc & rDoc = rInf.GetTextFrame()->GetDoc();
+ const sal_uInt16 nGridWidth = bHasGrid ? GetGridWidth(*pGrid, rDoc) : 0;
+
+ // used for grid mode only:
+ // the pointer is stored, because after formatting of non-asian text,
+ // the width of the kerning portion has to be adjusted
+ // Inserting a SwKernPortion before a SwTabPortion isn't necessary
+ // and will break the SwTabPortion.
+ SwKernPortion* pGridKernPortion = nullptr;
+
+ bool bFull = false;
+ SwTwips nUnderLineStart = 0;
+ rInf.Y( Y() );
+
+ while( pPor && !rInf.IsStop() )
+ {
+ OSL_ENSURE(rInf.GetLen() < TextFrameIndex(COMPLETE_STRING) &&
+ rInf.GetIdx() <= TextFrameIndex(rInf.GetText().getLength()),
+ "SwTextFormatter::BuildPortions: bad length in info" );
+
+ // We have to check the script for fields in order to set the
+ // correct nActual value for the font.
+ if( pPor->InFieldGrp() )
+ static_cast<SwFieldPortion*>(pPor)->CheckScript( rInf );
+
+ if( ! bHasGrid && rInf.HasScriptSpace() &&
+ rInf.GetLast() && rInf.GetLast()->InTextGrp() &&
+ rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() )
+ {
+ SwFontScript nNxtActual = rInf.GetFont()->GetActual();
+ SwFontScript nLstActual = nNxtActual;
+ sal_uInt16 nLstHeight = o3tl::narrowing<sal_uInt16>(rInf.GetFont()->GetHeight());
+ bool bAllowBehind = false;
+ const CharClass& rCC = GetAppCharClass();
+
+ // are there any punctuation characters on both sides
+ // of the kerning portion?
+ if ( pPor->InFieldGrp() )
+ {
+ OUString aAltText;
+ if ( static_cast<SwFieldPortion*>(pPor)->GetExpText( rInf, aAltText ) &&
+ !aAltText.isEmpty() )
+ {
+ bAllowBehind = rCC.isLetterNumeric( aAltText, 0 );
+
+ const SwFont* pTmpFnt = static_cast<SwFieldPortion*>(pPor)->GetFont();
+ if ( pTmpFnt )
+ nNxtActual = pTmpFnt->GetActual();
+ }
+ }
+ else
+ {
+ const OUString& rText = rInf.GetText();
+ sal_Int32 nIdx = sal_Int32(rInf.GetIdx());
+ bAllowBehind = nIdx < rText.getLength() && rCC.isLetterNumeric(rText, nIdx);
+ }
+
+ const SwLinePortion* pLast = rInf.GetLast();
+ if ( bAllowBehind && pLast )
+ {
+ bool bAllowBefore = false;
+
+ if ( pLast->InFieldGrp() )
+ {
+ OUString aAltText;
+ if ( static_cast<const SwFieldPortion*>(pLast)->GetExpText( rInf, aAltText ) &&
+ !aAltText.isEmpty() )
+ {
+ bAllowBefore = rCC.isLetterNumeric( aAltText, aAltText.getLength() - 1 );
+
+ const SwFont* pTmpFnt = static_cast<const SwFieldPortion*>(pLast)->GetFont();
+ if ( pTmpFnt )
+ {
+ nLstActual = pTmpFnt->GetActual();
+ nLstHeight = o3tl::narrowing<sal_uInt16>(pTmpFnt->GetHeight());
+ }
+ }
+ }
+ else if ( rInf.GetIdx() )
+ {
+ bAllowBefore = rCC.isLetterNumeric(rInf.GetText(), sal_Int32(rInf.GetIdx()) - 1);
+ // Note: ScriptType returns values in [1,4]
+ if ( bAllowBefore )
+ nLstActual = SwFontScript(m_pScriptInfo->ScriptType(rInf.GetIdx() - TextFrameIndex(1)) - 1);
+ }
+
+ nLstHeight /= 5;
+ // does the kerning portion still fit into the line?
+ if( bAllowBefore && ( nLstActual != nNxtActual ) &&
+ nLstHeight && rInf.X() + nLstHeight <= rInf.Width() &&
+ ! pPor->InTabGrp() )
+ {
+ SwKernPortion* pKrn =
+ new SwKernPortion( *rInf.GetLast(), nLstHeight,
+ pLast->InFieldGrp() && pPor->InFieldGrp() );
+ rInf.GetLast()->SetNextPortion( nullptr );
+ InsertPortion( rInf, pKrn );
+ }
+ }
+ }
+ else if ( bHasGrid && ! pGridKernPortion && ! m_pMulti && ! pPor->InTabGrp() )
+ {
+ // insert a grid kerning portion
+ pGridKernPortion = pPor->IsKernPortion() ?
+ static_cast<SwKernPortion*>(pPor) :
+ new SwKernPortion( *m_pCurr );
+
+ // if we have a new GridKernPortion, we initially calculate
+ // its size so that its ends on the grid
+ const SwPageFrame* pPageFrame = m_pFrame->FindPageFrame();
+ const SwLayoutFrame* pBody = pPageFrame->FindBodyCont();
+ SwRectFnSet aRectFnSet(pPageFrame);
+
+ const tools::Long nGridOrigin = pBody ?
+ aRectFnSet.GetPrtLeft(*pBody) :
+ aRectFnSet.GetPrtLeft(*pPageFrame);
+
+ SwTwips nStartX = rInf.X() + GetLeftMargin();
+ if ( aRectFnSet.IsVert() )
+ {
+ Point aPoint( nStartX, 0 );
+ m_pFrame->SwitchHorizontalToVertical( aPoint );
+ nStartX = aPoint.Y();
+ }
+
+ const SwTwips nOfst = nStartX - nGridOrigin;
+ if ( nOfst )
+ {
+ const sal_uLong i = ( nOfst > 0 ) ?
+ ( ( nOfst - 1 ) / nGridWidth + 1 ) :
+ 0;
+ const SwTwips nKernWidth = i * nGridWidth - nOfst;
+ const SwTwips nRestWidth = rInf.Width() - rInf.X();
+
+ if ( nKernWidth <= nRestWidth )
+ pGridKernPortion->Width( nKernWidth );
+ }
+
+ if ( pGridKernPortion != pPor )
+ InsertPortion( rInf, pGridKernPortion );
+ }
+
+ if( pPor->IsDropPortion() )
+ MergeCharacterBorder(*static_cast<SwDropPortion*>(pPor));
+
+ // the multi-portion has its own format function
+ if( pPor->IsMultiPortion() && ( !m_pMulti || m_pMulti->IsBidi() ) )
+ bFull = BuildMultiPortion( rInf, *static_cast<SwMultiPortion*>(pPor) );
+ else
+ bFull = pPor->Format( rInf );
+
+ if( rInf.IsRuby() && !rInf.GetRest() )
+ bFull = true;
+
+ // if we are underlined, we store the beginning of this underlined
+ // segment for repaint optimization
+ if ( LINESTYLE_NONE != m_pFont->GetUnderline() && ! nUnderLineStart )
+ nUnderLineStart = GetLeftMargin() + rInf.X();
+
+ if ( pPor->IsFlyPortion() )
+ m_pCurr->SetFly( true );
+ // some special cases, where we have to take care for the repaint
+ // offset:
+ // 1. Underlined portions due to special underline feature
+ // 2. Right Tab
+ // 3. BidiPortions
+ // 4. other Multiportions
+ // 5. DropCaps
+ // 6. Grid Mode
+ else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) &&
+ // 1. Underlined portions
+ nUnderLineStart &&
+ // reformat is at end of an underlined portion and next portion
+ // is not underlined
+ ( ( rInf.GetReformatStart() == rInf.GetIdx() &&
+ LINESTYLE_NONE == m_pFont->GetUnderline()
+ ) ||
+ // reformat is inside portion and portion is underlined
+ ( rInf.GetReformatStart() >= rInf.GetIdx() &&
+ rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() &&
+ LINESTYLE_NONE != m_pFont->GetUnderline() ) ) )
+ rInf.SetPaintOfst( nUnderLineStart );
+ else if ( ! rInf.GetPaintOfst() &&
+ // 2. Right Tab
+ ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) ||
+ // 3. BidiPortions
+ ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsBidi() ) ||
+ // 4. Multi Portion and 5. Drop Caps
+ ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) &&
+ rInf.GetReformatStart() >= rInf.GetIdx() &&
+ rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() )
+ // 6. Grid Mode
+ || ( bHasGrid && SwFontScript::CJK != m_pFont->GetActual() )
+ )
+ )
+ // we store the beginning of the critical portion as our
+ // paint offset
+ rInf.SetPaintOfst( GetLeftMargin() + rInf.X() );
+
+ // under one of these conditions we are allowed to delete the
+ // start of the underline portion
+ if ( IsUnderlineBreak( *pPor, *m_pFont ) )
+ nUnderLineStart = 0;
+
+ if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() &&
+ static_cast<SwMultiPortion*>(pPor)->HasFlyInContent() ) )
+ SetFlyInCntBase();
+ // bUnderflow needs to be reset or we wrap again at the next softhyphen
+ if ( !bFull )
+ {
+ rInf.ClrUnderflow();
+ if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTextGrp() &&
+ pPor->GetLen() && !pPor->InFieldGrp() )
+ {
+ // The distance between two different scripts is set
+ // to 20% of the fontheight.
+ TextFrameIndex const nTmp = rInf.GetIdx() + pPor->GetLen();
+ if (nTmp == m_pScriptInfo->NextScriptChg(nTmp - TextFrameIndex(1)) &&
+ nTmp != TextFrameIndex(rInf.GetText().getLength()) &&
+ (m_pScriptInfo->ScriptType(nTmp - TextFrameIndex(1)) == css::i18n::ScriptType::ASIAN ||
+ m_pScriptInfo->ScriptType(nTmp) == css::i18n::ScriptType::ASIAN) )
+ {
+ const SwTwips nDist = rInf.GetFont()->GetHeight()/5;
+
+ if( nDist )
+ {
+ // we do not want a kerning portion if any end
+ // would be a punctuation character
+ const CharClass& rCC = GetAppCharClass();
+ if (rCC.isLetterNumeric(rInf.GetText(), sal_Int32(nTmp) - 1)
+ && rCC.isLetterNumeric(rInf.GetText(), sal_Int32(nTmp)))
+ {
+ // does the kerning portion still fit into the line?
+ if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() )
+ new SwKernPortion( *pPor, nDist );
+ else
+ bFull = true;
+ }
+ }
+ }
+ }
+ }
+
+ if ( bHasGrid && pPor != pGridKernPortion && ! m_pMulti && ! pPor->InTabGrp() )
+ {
+ TextFrameIndex const nTmp = rInf.GetIdx() + pPor->GetLen();
+ const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width();
+
+ const SwFontScript nCurrScript = m_pFont->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() );
+ const SwFontScript nNextScript =
+ nTmp >= TextFrameIndex(rInf.GetText().getLength())
+ ? SwFontScript::CJK
+ : m_pScriptInfo->WhichFont(nTmp);
+
+ // snap non-asian text to grid if next portion is ASIAN or
+ // there are no more portions in this line
+ // be careful when handling an underflow event: the gridkernportion
+ // could have been deleted
+ if ( nRestWidth > 0 && SwFontScript::CJK != nCurrScript &&
+ ! rInf.IsUnderflow() && ( bFull || SwFontScript::CJK == nNextScript ) )
+ {
+ OSL_ENSURE( pGridKernPortion, "No GridKernPortion available" );
+
+ // calculate size
+ SwLinePortion* pTmpPor = pGridKernPortion->GetNextPortion();
+ sal_uInt16 nSumWidth = pPor->Width();
+ while ( pTmpPor )
+ {
+ nSumWidth = nSumWidth + pTmpPor->Width();
+ pTmpPor = pTmpPor->GetNextPortion();
+ }
+
+ const SwTwips i = nSumWidth ?
+ ( nSumWidth - 1 ) / nGridWidth + 1 :
+ 0;
+ const SwTwips nTmpWidth = i * nGridWidth;
+ const SwTwips nKernWidth = std::min(nTmpWidth - nSumWidth, nRestWidth);
+ const SwTwips nKernWidth_1 = pGrid->IsSnapToChars() ?
+ nKernWidth / 2 : 0;
+
+ OSL_ENSURE( nKernWidth <= nRestWidth,
+ "Not enough space left for adjusting non-asian text in grid mode" );
+ if (nKernWidth_1)
+ {
+ pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 );
+ rInf.X( rInf.X() + nKernWidth_1 );
+ }
+
+ if ( ! bFull && nKernWidth - nKernWidth_1 > 0 )
+ new SwKernPortion( *pPor, static_cast<short>(nKernWidth - nKernWidth_1),
+ false, true );
+
+ pGridKernPortion = nullptr;
+ }
+ else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() ||
+ pPor->IsFlyCntPortion() || pPor->InNumberGrp() ||
+ pPor->InFieldGrp() || nCurrScript != nNextScript )
+ // next portion should snap to grid
+ pGridKernPortion = nullptr;
+ }
+
+ rInf.SetFull( bFull );
+
+ // Restportions from fields with multiple lines don't yet have the right ascent
+ if ( !pPor->GetLen() && !pPor->IsFlyPortion()
+ && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp()
+ && !pPor->IsMultiPortion() )
+ CalcAscent( rInf, pPor );
+
+ InsertPortion( rInf, pPor );
+ if (pPor->IsMultiPortion() && (!m_pMulti || m_pMulti->IsBidi()))
+ {
+ (void) rInf.CheckCurrentPosBookmark(); // bookmark was already created inside MultiPortion!
+ }
+ pPor = NewPortion(rInf, oMovedFlyIndex);
+ }
+
+ if( !rInf.IsStop() )
+ {
+ // The last right centered, decimal tab
+ SwTabPortion *pLastTab = rInf.GetLastTab();
+ if( pLastTab )
+ pLastTab->FormatEOL( rInf );
+ else if( rInf.GetLast() && rInf.LastKernPortion() )
+ rInf.GetLast()->FormatEOL( rInf );
+ }
+ if( m_pCurr->GetNextPortion() && m_pCurr->GetNextPortion()->InNumberGrp()
+ && static_cast<SwNumberPortion*>(m_pCurr->GetNextPortion())->IsHide() )
+ rInf.SetNumDone( false );
+
+ // Delete fly in any case
+ ClearFly( rInf );
+
+ // Reinit the tab overflow flag after the line
+ rInf.SetTabOverflow( false );
+}
+
+void SwTextFormatter::CalcAdjustLine( SwLineLayout *pCurrent )
+{
+ if( SvxAdjust::Left != GetAdjust() && !m_pMulti)
+ {
+ pCurrent->SetFormatAdj(true);
+ if( IsFlyInCntBase() )
+ {
+ CalcAdjLine( pCurrent );
+ // For e.g. centered fly we need to switch the RefPoint
+ // That's why bAlways = true
+ UpdatePos( pCurrent, GetTopLeft(), GetStart(), true );
+ }
+ }
+}
+
+void SwTextFormatter::CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor )
+{
+ bool bCalc = false;
+ if ( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->GetFont() )
+ {
+ // Numbering + InterNetFields can keep an own font, then their size is
+ // independent from hard attribute values
+ SwFont* pFieldFnt = static_cast<SwFieldPortion*>(pPor)->m_pFont.get();
+ SwFontSave aSave( rInf, pFieldFnt );
+ pPor->Height( rInf.GetTextHeight() );
+ pPor->SetAscent( rInf.GetAscent() );
+ bCalc = true;
+ }
+ // i#89179
+ // tab portion representing the list tab of a list label gets the
+ // same height and ascent as the corresponding number portion
+ else if ( pPor->InTabGrp() && pPor->GetLen() == TextFrameIndex(0) &&
+ rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
+ static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() )
+ {
+ const SwLinePortion* pLast = rInf.GetLast();
+ pPor->Height( pLast->Height() );
+ pPor->SetAscent( pLast->GetAscent() );
+ }
+ else if (pPor->GetWhichPor() == PortionType::Bookmark
+ && rInf.GetIdx() == TextFrameIndex(rInf.GetText().getLength()))
+ {
+ // bookmark at end of paragraph: *don't* advance iterator, use the
+ // current font instead; it's possible that there's a font size on the
+ // paragraph and it's overridden on the last line of the paragraph and
+ // we don't want to apply it via SwBookmarkPortion and grow the line
+ // height (example: n758883.docx)
+ SwLinePortion const*const pLast = rInf.GetLast();
+ assert(pLast);
+ pPor->Height( pLast->Height(), false );
+ pPor->SetAscent( pLast->GetAscent() );
+ }
+ else
+ {
+ const SwLinePortion *pLast = rInf.GetLast();
+ bool bChg = false;
+
+ // In empty lines the attributes are switched on via SeekStart
+ const bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
+ if ( pPor->IsQuoVadisPortion() )
+ bChg = SeekStartAndChg( rInf, true );
+ else
+ {
+ if( bFirstPor )
+ {
+ if( !rInf.GetText().isEmpty() )
+ {
+ if ( pPor->GetLen() || !rInf.GetIdx()
+ || ( m_pCurr != pLast && !pLast->IsFlyPortion() )
+ || !m_pCurr->IsRest() ) // instead of !rInf.GetRest()
+ bChg = SeekAndChg( rInf );
+ else
+ bChg = SeekAndChgBefore( rInf );
+ }
+ else if ( m_pMulti )
+ // do not open attributes starting at 0 in empty multi
+ // portions (rotated numbering followed by a footnote
+ // can cause trouble, because the footnote attribute
+ // starts at 0, but if we open it, the attribute handler
+ // cannot handle it.
+ bChg = false;
+ else
+ bChg = SeekStartAndChg( rInf );
+ }
+ else
+ bChg = SeekAndChg( rInf );
+ }
+ if( bChg || bFirstPor || !pPor->GetAscent()
+ || !rInf.GetLast()->InTextGrp() )
+ {
+ pPor->SetHangingBaseline( rInf.GetHangingBaseline() );
+ pPor->SetAscent( rInf.GetAscent() );
+ pPor->Height( rInf.GetTextHeight() );
+ bCalc = true;
+ }
+ else
+ {
+ pPor->Height( pLast->Height() );
+ pPor->SetAscent( pLast->GetAscent() );
+ }
+ }
+
+ if( pPor->InTextGrp() && bCalc )
+ {
+ pPor->SetAscent(pPor->GetAscent() +
+ rInf.GetFont()->GetTopBorderSpace());
+ pPor->Height(pPor->Height() +
+ rInf.GetFont()->GetTopBorderSpace() +
+ rInf.GetFont()->GetBottomBorderSpace() );
+ }
+}
+
+namespace {
+
+class SwMetaPortion : public SwTextPortion
+{
+ Color m_aShadowColor;
+public:
+ SwMetaPortion() { SetWhichPor( PortionType::Meta ); }
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ void SetShadowColor(const Color& rCol ) { m_aShadowColor = rCol; }
+};
+
+/// A content control portion is a text portion that is inside RES_TXTATR_CONTENTCONTROL.
+class SwContentControlPortion : public SwTextPortion
+{
+public:
+ SwContentControlPortion() { SetWhichPor(PortionType::ContentControl); }
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+};
+}
+
+void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if ( Width() )
+ {
+ rInf.DrawViewOpt( *this, PortionType::Meta,
+ // custom shading (RDF metadata)
+ COL_BLACK == m_aShadowColor
+ ? nullptr
+ : &m_aShadowColor );
+
+ SwTextPortion::Paint( rInf );
+ }
+}
+
+void SwContentControlPortion::Paint(const SwTextPaintInfo& rInf) const
+{
+ if (Width())
+ {
+ rInf.DrawViewOpt(*this, PortionType::ContentControl);
+ SwTextPortion::Paint(rInf);
+ }
+}
+
+namespace sw::mark {
+ OUString ExpandFieldmark(IFieldmark* pBM)
+ {
+ if (pBM->GetFieldname() == ODF_FORMCHECKBOX)
+ {
+ ::sw::mark::ICheckboxFieldmark const*const pCheckboxFm(
+ dynamic_cast<ICheckboxFieldmark const*>(pBM));
+ assert(pCheckboxFm);
+ return pCheckboxFm->IsChecked()
+ ? OUString(u"\u2612")
+ : OUString(u"\u2610");
+ }
+ assert(pBM->GetFieldname() == ODF_FORMDROPDOWN);
+ const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters();
+ sal_Int32 nCurrentIdx = 0;
+ const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(OUString(ODF_FORMDROPDOWN_RESULT));
+ if(pResult != pParameters->end())
+ pResult->second >>= nCurrentIdx;
+
+ const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(OUString(ODF_FORMDROPDOWN_LISTENTRY));
+ if (pListEntries != pParameters->end())
+ {
+ uno::Sequence< OUString > vListEntries;
+ pListEntries->second >>= vListEntries;
+ if (nCurrentIdx < vListEntries.getLength())
+ return vListEntries[nCurrentIdx];
+ }
+
+ static constexpr OUStringLiteral vEnSpaces = u"\u2002\u2002\u2002\u2002\u2002";
+ return vEnSpaces;
+ }
+}
+
+SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const
+{
+ SwTextPortion *pPor = nullptr;
+ if( GetFnt()->IsTox() )
+ {
+ pPor = new SwToxPortion;
+ }
+ else if ( GetFnt()->IsInputField() )
+ {
+ if (rInf.GetOpt().IsFieldName())
+ {
+ OUString aFieldName = SwFieldType::GetTypeStr(SwFieldTypesEnum::Input);
+ // assume this is only the *first* portion and follows will be created elsewhere => input field must start at Idx
+ assert(rInf.GetText()[sal_Int32(rInf.GetIdx())] == CH_TXT_ATR_INPUTFIELDSTART);
+ TextFrameIndex nFieldLen(-1);
+ for (TextFrameIndex i = rInf.GetIdx() + TextFrameIndex(1); ; ++i)
+ {
+ assert(rInf.GetText()[sal_Int32(i)] != CH_TXT_ATR_INPUTFIELDSTART); // can't nest
+ if (rInf.GetText()[sal_Int32(i)] == CH_TXT_ATR_INPUTFIELDEND)
+ {
+ nFieldLen = i + TextFrameIndex(1) - rInf.GetIdx();
+ break;
+ }
+ }
+ assert(2 <= sal_Int32(nFieldLen));
+ pPor = new SwFieldPortion(aFieldName, nullptr, false, nFieldLen);
+ }
+ else
+ {
+ pPor = new SwTextInputFieldPortion();
+ }
+ }
+ else
+ {
+ if( GetFnt()->IsRef() )
+ pPor = new SwRefPortion;
+ else if (GetFnt()->IsMeta())
+ {
+ auto pMetaPor = new SwMetaPortion;
+
+ // set custom LO_EXT_SHADING color, if it exists
+ SwTextFrame const*const pFrame(rInf.GetTextFrame());
+ SwPosition aPosition(pFrame->MapViewToModelPos(rInf.GetIdx()));
+ SwPaM aPam(aPosition);
+ uno::Reference<text::XTextContent> const xRet(
+ SwUnoCursorHelper::GetNestedTextContent(
+ *aPam.GetNode().GetTextNode(), aPosition.nContent.GetIndex(), false) );
+ if (xRet.is())
+ {
+ const SwDoc & rDoc = rInf.GetTextFrame()->GetDoc();
+ static uno::Reference< uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext());
+
+ static uno::Reference< rdf::XURI > xODF_SHADING(
+ rdf::URI::createKnown(xContext, rdf::URIs::LO_EXT_SHADING), uno::UNO_SET_THROW);
+
+ uno::Reference<rdf::XDocumentMetadataAccess> xDocumentMetadataAccess(
+ rDoc.GetDocShell()->GetBaseModel(), uno::UNO_QUERY);
+
+ const css::uno::Reference<css::rdf::XResource> xSubject(xRet, uno::UNO_QUERY);
+ const uno::Reference<rdf::XRepository>& xRepository =
+ xDocumentMetadataAccess->getRDFRepository();
+ const uno::Reference<container::XEnumeration> xEnum(
+ xRepository->getStatements(xSubject, xODF_SHADING, nullptr), uno::UNO_SET_THROW);
+
+ while (xEnum->hasMoreElements())
+ {
+ rdf::Statement stmt;
+ if (!(xEnum->nextElement() >>= stmt)) {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<rdf::XLiteral> xObject(stmt.Object, uno::UNO_QUERY);
+ if (!xObject.is()) continue;
+ if (xEnum->hasMoreElements()) {
+ SAL_INFO("sw.uno", "ignoring other odf:shading statements");
+ }
+ Color rColor = Color::STRtoRGB(xObject->getValue());
+ pMetaPor->SetShadowColor(rColor);
+ break;
+ }
+ }
+ pPor = pMetaPor;
+ }
+ else if (GetFnt()->IsContentControl())
+ {
+ pPor = new SwContentControlPortion;
+ }
+ else
+ {
+ // Only at the End!
+ // If pCurr does not have a width, it can however already have content.
+ // E.g. for non-displayable characters
+
+ auto const ch(rInf.GetText()[sal_Int32(rInf.GetIdx())]);
+ SwTextFrame const*const pFrame(rInf.GetTextFrame());
+ SwPosition aPosition(pFrame->MapViewToModelPos(rInf.GetIdx()));
+ sw::mark::IFieldmark *pBM = pFrame->GetDoc().getIDocumentMarkAccess()->getFieldmarkFor(aPosition);
+ if(pBM != nullptr && pBM->GetFieldname( ) == ODF_FORMDATE)
+ {
+ if (ch == CH_TXT_ATR_FIELDSTART)
+ pPor = new SwFieldFormDatePortion(pBM, true);
+ else if (ch == CH_TXT_ATR_FIELDSEP)
+ pPor = new SwFieldMarkPortion(); // it's added in DateFieldmark?
+ else if (ch == CH_TXT_ATR_FIELDEND)
+ pPor = new SwFieldFormDatePortion(pBM, false);
+ }
+ else if (ch == CH_TXT_ATR_FIELDSTART)
+ pPor = new SwFieldMarkPortion();
+ else if (ch == CH_TXT_ATR_FIELDSEP)
+ pPor = new SwFieldMarkPortion();
+ else if (ch == CH_TXT_ATR_FIELDEND)
+ pPor = new SwFieldMarkPortion();
+ else if (ch == CH_TXT_ATR_FORMELEMENT)
+ {
+ OSL_ENSURE(pBM != nullptr, "Where is my form field bookmark???");
+ if (pBM != nullptr)
+ {
+ if (pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
+ {
+ pPor = new SwFieldFormCheckboxPortion();
+ }
+ else if (pBM->GetFieldname( ) == ODF_FORMDROPDOWN)
+ {
+ pPor = new SwFieldFormDropDownPortion(pBM, sw::mark::ExpandFieldmark(pBM));
+ }
+ /* we need to check for ODF_FORMTEXT for scenario having FormFields inside FORMTEXT.
+ * Otherwise file will crash on open.
+ */
+ else if (pBM->GetFieldname( ) == ODF_FORMTEXT)
+ {
+ pPor = new SwFieldMarkPortion();
+ }
+ }
+ }
+ if( !pPor )
+ {
+ if( !rInf.X() && !m_pCurr->GetNextPortion() && !m_pCurr->GetLen() )
+ pPor = m_pCurr;
+ else
+ pPor = new SwTextPortion;
+ }
+ }
+ }
+ return pPor;
+}
+
+// We calculate the length, the following portion limits are defined:
+// 1) Tabs
+// 2) Linebreaks
+// 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD
+// 4) next attribute change
+
+SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
+{
+ // If we're at the line's beginning, we take pCurr
+ // If pCurr is not derived from SwTextPortion, we need to duplicate
+ Seek( rInf.GetIdx() );
+ SwTextPortion *pPor = WhichTextPor( rInf );
+
+ // until next attribute change:
+ const TextFrameIndex nNextAttr = GetNextAttr();
+ TextFrameIndex nNextChg = std::min(nNextAttr, TextFrameIndex(rInf.GetText().getLength()));
+
+ // end of script type:
+ const TextFrameIndex nNextScript = m_pScriptInfo->NextScriptChg(rInf.GetIdx());
+ nNextChg = std::min( nNextChg, nNextScript );
+
+ // end of direction:
+ const TextFrameIndex nNextDir = m_pScriptInfo->NextDirChg(rInf.GetIdx());
+ nNextChg = std::min( nNextChg, nNextDir );
+
+ // hidden change (potentially via bookmark):
+ const TextFrameIndex nNextHidden = m_pScriptInfo->NextHiddenChg(rInf.GetIdx());
+ nNextChg = std::min( nNextChg, nNextHidden );
+
+ // bookmarks
+ const TextFrameIndex nNextBookmark = m_pScriptInfo->NextBookmark(rInf.GetIdx());
+ nNextChg = std::min(nNextChg, nNextBookmark);
+
+ // Turbo boost:
+ // We assume that font characters are not larger than twice
+ // as wide as height.
+ // Very crazy: we need to take the ascent into account.
+
+ // Mind the trap! GetSize() contains the wished-for height, the real height
+ // is only known in CalcAscent!
+
+ // The ratio is even crazier: a blank in Times New Roman has an ascent of
+ // 182, a height of 200 and a width of 53!
+ // It follows that a line with a lot of blanks is processed incorrectly.
+ // Therefore we increase from factor 2 to 8 (due to negative kerning).
+
+ pPor->SetLen(TextFrameIndex(1));
+ CalcAscent( rInf, pPor );
+
+ const SwFont* pTmpFnt = rInf.GetFont();
+ sal_Int32 nExpect = std::min( sal_Int32( pTmpFnt->GetHeight() ),
+ sal_Int32( pPor->GetAscent() ) ) / 8;
+ if ( !nExpect )
+ nExpect = 1;
+ nExpect = sal_Int32(rInf.GetIdx()) + (rInf.GetLineWidth() / nExpect);
+ if (TextFrameIndex(nExpect) > rInf.GetIdx() && nNextChg > TextFrameIndex(nExpect))
+ nNextChg = TextFrameIndex(std::min(nExpect, rInf.GetText().getLength()));
+
+ // we keep an invariant during method calls:
+ // there are no portion ending characters like hard spaces
+ // or tabs in [ nLeftScanIdx, nRightScanIdx ]
+ if ( m_nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= m_nRightScanIdx )
+ {
+ if ( nNextChg > m_nRightScanIdx )
+ nNextChg = m_nRightScanIdx =
+ rInf.ScanPortionEnd( m_nRightScanIdx, nNextChg );
+ }
+ else
+ {
+ m_nLeftScanIdx = rInf.GetIdx();
+ nNextChg = m_nRightScanIdx =
+ rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg );
+ }
+
+ pPor->SetLen( nNextChg - rInf.GetIdx() );
+ rInf.SetLen( pPor->GetLen() );
+ return pPor;
+}
+
+// first portions have no length
+SwLinePortion *SwTextFormatter::WhichFirstPortion(SwTextFormatInfo &rInf)
+{
+ SwLinePortion *pPor = nullptr;
+
+ if( rInf.GetRest() )
+ {
+ // Tabs and fields
+ if( '\0' != rInf.GetHookChar() )
+ return nullptr;
+
+ pPor = rInf.GetRest();
+ if( pPor->IsErgoSumPortion() )
+ rInf.SetErgoDone(true);
+ else
+ if( pPor->IsFootnoteNumPortion() )
+ rInf.SetFootnoteDone(true);
+ else
+ if( pPor->InNumberGrp() )
+ rInf.SetNumDone(true);
+
+ rInf.SetRest(nullptr);
+ m_pCurr->SetRest( true );
+ return pPor;
+ }
+
+ // We can stand in the follow, it's crucial that
+ // pFrame->GetOffset() == 0!
+ if( rInf.GetIdx() )
+ {
+ // We now too can elongate FootnotePortions and ErgoSumPortions
+
+ // 1. The ErgoSumTexts
+ if( !rInf.IsErgoDone() )
+ {
+ if( m_pFrame->IsInFootnote() && !m_pFrame->GetIndPrev() )
+ pPor = NewErgoSumPortion( rInf );
+ rInf.SetErgoDone( true );
+ }
+
+ // 2. Arrow portions
+ if( !pPor && !rInf.IsArrowDone() )
+ {
+ if( m_pFrame->GetOffset() && !m_pFrame->IsFollow() &&
+ rInf.GetIdx() == m_pFrame->GetOffset() )
+ pPor = new SwArrowPortion( *m_pCurr );
+ rInf.SetArrowDone( true );
+ }
+
+ // 3. Kerning portions at beginning of line in grid mode
+ if ( ! pPor && ! m_pCurr->GetNextPortion() )
+ {
+ SwTextGridItem const*const pGrid(
+ GetGridItem(GetTextFrame()->FindPageFrame()));
+ if ( pGrid )
+ pPor = new SwKernPortion( *m_pCurr );
+ }
+
+ // 4. The line rests (multiline fields)
+ if( !pPor )
+ {
+ pPor = rInf.GetRest();
+ // Only for pPor of course
+ if( pPor )
+ {
+ m_pCurr->SetRest( true );
+ rInf.SetRest(nullptr);
+ }
+ }
+ }
+ else
+ {
+ // 5. The foot note count
+ if( !rInf.IsFootnoteDone() )
+ {
+ OSL_ENSURE( ( ! rInf.IsMulti() && ! m_pMulti ) || m_pMulti->HasRotation(),
+ "Rotated number portion trouble" );
+
+ const bool bFootnoteNum = m_pFrame->IsFootnoteNumFrame();
+ rInf.GetParaPortion()->SetFootnoteNum( bFootnoteNum );
+ if( bFootnoteNum )
+ pPor = NewFootnoteNumPortion( rInf );
+ rInf.SetFootnoteDone( true );
+ }
+
+ // 6. The ErgoSumTexts of course also exist in the TextMaster,
+ // it's crucial whether the SwFootnoteFrame is aFollow
+ if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() )
+ {
+ if( m_pFrame->IsInFootnote() && !m_pFrame->GetIndPrev() )
+ pPor = NewErgoSumPortion( rInf );
+ rInf.SetErgoDone( true );
+ }
+
+ // 7. The numbering
+ if( !rInf.IsNumDone() && !pPor )
+ {
+ OSL_ENSURE( ( ! rInf.IsMulti() && ! m_pMulti ) || m_pMulti->HasRotation(),
+ "Rotated number portion trouble" );
+
+ // If we're in the follow, then of course not
+ if (GetTextFrame()->GetTextNodeForParaProps()->GetNumRule())
+ pPor = NewNumberPortion( rInf );
+ rInf.SetNumDone( true );
+ }
+ // 8. The DropCaps
+ if( !pPor && GetDropFormat() && ! rInf.IsMulti() )
+ pPor = NewDropPortion( rInf );
+
+ // 9. Kerning portions at beginning of line in grid mode
+ if ( !pPor && !m_pCurr->GetNextPortion() )
+ {
+ SwTextGridItem const*const pGrid(
+ GetGridItem(GetTextFrame()->FindPageFrame()));
+ if ( pGrid )
+ pPor = new SwKernPortion( *m_pCurr );
+ }
+ }
+
+ // 10. Decimal tab portion at the beginning of each line in table cells
+ if ( !pPor && !m_pCurr->GetNextPortion() &&
+ GetTextFrame()->IsInTab() &&
+ GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT))
+ {
+ pPor = NewTabPortion( rInf, true );
+ }
+
+ // 11. suffix of meta-field
+ if (!pPor)
+ {
+ pPor = TryNewNoLengthPortion(rInf);
+ }
+
+ // 12. bookmarks
+ // check this *last* so that BuildMultiPortion() can find it!
+ if (!pPor && rInf.CheckCurrentPosBookmark())
+ {
+ auto const bookmark(m_pScriptInfo->GetBookmark(rInf.GetIdx()));
+ if (static_cast<bool>(bookmark))
+ {
+ sal_Unicode mark;
+ if ((bookmark & (SwScriptInfo::MarkKind::Start|SwScriptInfo::MarkKind::End))
+ == (SwScriptInfo::MarkKind::Start|SwScriptInfo::MarkKind::End))
+ {
+ //mark = u'\u2336'; // not in OpenSymbol :(
+ mark = '|';
+ // hmm ... paint U+2345 over U+2346 should be same width?
+ // and U+237F // or U+2E20/U+2E21
+ }
+ else if (bookmark & SwScriptInfo::MarkKind::Start)
+ {
+ mark = '[';
+ }
+ else if (bookmark & SwScriptInfo::MarkKind::End)
+ {
+ mark = ']';
+ }
+ else
+ {
+ assert(bookmark & SwScriptInfo::MarkKind::Point);
+ mark = '|';
+ }
+ pPor = new SwBookmarkPortion(mark);
+ }
+ }
+
+ return pPor;
+}
+
+static bool lcl_OldFieldRest( const SwLineLayout* pCurr )
+{
+ if( !pCurr->GetNext() )
+ return false;
+ const SwLinePortion *pPor = pCurr->GetNext()->GetNextPortion();
+ bool bRet = false;
+ while( pPor && !bRet )
+ {
+ bRet = (pPor->InFieldGrp() && static_cast<const SwFieldPortion*>(pPor)->IsFollow()) ||
+ (pPor->IsMultiPortion() && static_cast<const SwMultiPortion*>(pPor)->IsFollowField());
+ if( !pPor->GetLen() )
+ break;
+ pPor = pPor->GetNextPortion();
+ }
+ return bRet;
+}
+
+/* NewPortion sets rInf.nLen
+ * A SwTextPortion is limited by a tab, break, txtatr or attr change
+ * We can have three cases:
+ * 1) The line is full and the wrap was not emulated
+ * -> return 0;
+ * 2) The line is full and a wrap was emulated
+ * -> Reset width and return new FlyPortion
+ * 3) We need to construct a new portion
+ * -> CalcFlyWidth emulates the width and return portion, if needed
+ */
+
+SwLinePortion *SwTextFormatter::NewPortion(SwTextFormatInfo &rInf,
+ ::std::optional<TextFrameIndex> const oMovedFlyIndex)
+{
+ if (oMovedFlyIndex && *oMovedFlyIndex <= rInf.GetIdx())
+ {
+ SAL_WARN_IF(*oMovedFlyIndex != rInf.GetIdx(), "sw.core", "stopping too late, no portion break at fly anchor?");
+ rInf.SetStop(true);
+ return nullptr;
+ }
+
+ // Underflow takes precedence
+ rInf.SetStopUnderflow( false );
+ if( rInf.GetUnderflow() )
+ {
+ OSL_ENSURE( rInf.IsFull(), "SwTextFormatter::NewPortion: underflow but not full" );
+ return Underflow( rInf );
+ }
+
+ // If the line is full, flys and Underflow portions could be waiting ...
+ if( rInf.IsFull() )
+ {
+ // LineBreaks and Flys (bug05.sdw)
+ // IsDummy()
+ if( rInf.IsNewLine() && (!rInf.GetFly() || !m_pCurr->IsDummy()) )
+ return nullptr;
+
+ // When the text bumps into the Fly, or when the Fly comes first because
+ // it juts out over the left edge, GetFly() is returned.
+ // When IsFull() and no GetFly() is available, naturally zero is returned.
+ if( rInf.GetFly() )
+ {
+ if( rInf.GetLast()->IsBreakPortion() )
+ {
+ delete rInf.GetFly();
+ rInf.SetFly( nullptr );
+ }
+
+ return rInf.GetFly();
+ }
+
+ // A nasty special case: A frame without wrap overlaps the Footnote area.
+ // We must declare the Footnote portion as rest of line, so that
+ // SwTextFrame::Format doesn't abort (the text mass already was formatted).
+ if( rInf.GetRest() )
+ rInf.SetNewLine( true );
+ else
+ {
+ // When the next line begins with a rest of a field, but now no
+ // rest remains, the line must definitely be formatted anew!
+ if( lcl_OldFieldRest( GetCurr() ) )
+ rInf.SetNewLine( true );
+ else
+ {
+ SwLinePortion *pFirst = WhichFirstPortion( rInf );
+ if( pFirst )
+ {
+ rInf.SetNewLine( true );
+ if( pFirst->InNumberGrp() )
+ rInf.SetNumDone( false) ;
+ delete pFirst;
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
+ SwLinePortion *pPor = WhichFirstPortion( rInf );
+
+ // Check for Hidden Portion:
+ if ( !pPor )
+ {
+ TextFrameIndex nEnd = rInf.GetIdx();
+ if ( ::lcl_BuildHiddenPortion( rInf, nEnd ) )
+ pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() );
+ }
+
+ if( !pPor )
+ {
+ if( ( !m_pMulti || m_pMulti->IsBidi() ) &&
+ // i#42734
+ // No multi portion if there is a hook character waiting:
+ ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) )
+ {
+ // We open a multiportion part, if we enter a multi-line part
+ // of the paragraph.
+ TextFrameIndex nEnd = rInf.GetIdx();
+ std::optional<SwMultiCreator> pCreate = rInf.GetMultiCreator( nEnd, m_pMulti );
+ if( pCreate )
+ {
+ SwMultiPortion* pTmp = nullptr;
+
+ if ( SwMultiCreatorId::Bidi == pCreate->nId )
+ pTmp = new SwBidiPortion( nEnd, pCreate->nLevel );
+ else if ( SwMultiCreatorId::Ruby == pCreate->nId )
+ {
+ pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(),
+ GetTextFrame()->GetDoc().getIDocumentSettingAccess(),
+ nEnd, TextFrameIndex(0), rInf );
+ }
+ else if( SwMultiCreatorId::Rotate == pCreate->nId )
+ {
+ pTmp = new SwRotatedPortion( *pCreate, nEnd,
+ GetTextFrame()->IsRightToLeft() );
+ GetTextFrame()->SetHasRotatedPortions(true);
+ }
+ else
+ pTmp = new SwDoubleLinePortion( *pCreate, nEnd );
+
+ pCreate.reset();
+ CalcFlyWidth( rInf );
+
+ return pTmp;
+ }
+ }
+ // Tabs and Fields
+ sal_Unicode cChar = rInf.GetHookChar();
+
+ if( cChar )
+ {
+ /* We fetch cChar again to be sure that the tab is pending now and
+ * didn't move to the next line (as happens behind frames).
+ * However, when a FieldPortion is in the rest, we must naturally fetch
+ * the cChar from the field content, e.g. DecimalTabs and fields (22615)
+ */
+ if( !rInf.GetRest() || !rInf.GetRest()->InFieldGrp() )
+ cChar = rInf.GetChar( rInf.GetIdx() );
+ rInf.ClearHookChar();
+ }
+ else
+ {
+ if (rInf.GetIdx() >= TextFrameIndex(rInf.GetText().getLength()))
+ {
+ rInf.SetFull(true);
+ CalcFlyWidth( rInf );
+ return pPor;
+ }
+ cChar = rInf.GetChar( rInf.GetIdx() );
+ }
+
+ switch( cChar )
+ {
+ case CH_TAB:
+ pPor = NewTabPortion( rInf, false ); break;
+
+ case CH_BREAK:
+ {
+ SwTextAttr* pHint = GetAttr(rInf.GetIdx());
+ pPor = new SwBreakPortion(*rInf.GetLast(), pHint);
+ break;
+ }
+
+ case CHAR_SOFTHYPHEN: // soft hyphen
+ pPor = new SwSoftHyphPortion; break;
+
+ case CHAR_HARDBLANK: // no-break space
+ // Please check tdf#115067 if you want to edit the char
+ pPor = new SwBlankPortion( cChar ); break;
+
+ case CHAR_HARDHYPHEN: // non-breaking hyphen
+ pPor = new SwBlankPortion( '-' ); break;
+
+ case CHAR_ZWSP: // zero width space
+ case CHAR_WJ : // word joiner
+ pPor = new SwControlCharPortion( cChar ); break;
+
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ if( rInf.HasHint( rInf.GetIdx() ) )
+ {
+ pPor = NewExtraPortion( rInf );
+ break;
+ }
+ [[fallthrough]];
+ default :
+ {
+ SwTabPortion* pLastTabPortion = rInf.GetLastTab();
+ if ( pLastTabPortion && cChar == rInf.GetTabDecimal() )
+ {
+ // Abandon dec. tab position if line is full
+ // We have a decimal tab portion in the line and the next character has to be
+ // aligned at the tab stop position. We store the width from the beginning of
+ // the tab stop portion up to the portion containing the decimal separator:
+ if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ &&
+ PortionType::TabDecimal == pLastTabPortion->GetWhichPor() )
+ {
+ OSL_ENSURE( rInf.X() >= pLastTabPortion->GetFix(), "Decimal tab stop position cannot be calculated" );
+ const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = o3tl::narrowing<sal_uInt16>(rInf.X() - pLastTabPortion->GetFix() );
+ static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition );
+ rInf.SetTabDecimal( 0 );
+ }
+ else
+ rInf.SetFull( rInf.GetLastTab()->Format( rInf ) );
+ }
+
+ if( rInf.GetRest() )
+ {
+ if( rInf.IsFull() )
+ {
+ rInf.SetNewLine(true);
+ return nullptr;
+ }
+ pPor = rInf.GetRest();
+ rInf.SetRest(nullptr);
+ }
+ else
+ {
+ if( rInf.IsFull() )
+ return nullptr;
+ pPor = NewTextPortion( rInf );
+ }
+ break;
+ }
+ }
+
+ // if a portion is created despite there being a pending RestPortion,
+ // then it is a field which has been split (e.g. because it contains a Tab)
+ if( pPor && rInf.GetRest() )
+ pPor->SetLen(TextFrameIndex(0));
+
+ // robust:
+ if( !pPor || rInf.IsStop() )
+ {
+ delete pPor;
+ return nullptr;
+ }
+ }
+
+ assert(pPor && "can only reach here with pPor existing");
+
+ // Special portions containing numbers (footnote anchor, footnote number,
+ // numbering) can be contained in a rotated portion, if the user
+ // choose a rotated character attribute.
+ if (!m_pMulti)
+ {
+ if ( pPor->IsFootnotePortion() )
+ {
+ const SwTextFootnote* pTextFootnote = static_cast<SwFootnotePortion*>(pPor)->GetTextFootnote();
+
+ if ( pTextFootnote )
+ {
+ SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pTextFootnote->GetFootnote());
+ const SwDoc *const pDoc = &rInf.GetTextFrame()->GetDoc();
+ const SwEndNoteInfo* pInfo;
+ if( rFootnote.IsEndNote() )
+ pInfo = &pDoc->GetEndNoteInfo();
+ else
+ pInfo = &pDoc->GetFootnoteInfo();
+ const SwAttrSet& rSet = pInfo->GetAnchorCharFormat(const_cast<SwDoc&>(*pDoc))->GetAttrSet();
+
+ Degree10 nDir(0);
+ if( const SvxCharRotateItem* pItem = rSet.GetItemIfSet( RES_CHRATR_ROTATE ) )
+ nDir = pItem->GetValue();
+
+ if ( nDir )
+ {
+ delete pPor;
+ pPor = new SwRotatedPortion(rInf.GetIdx() + TextFrameIndex(1),
+ 900_deg10 == nDir
+ ? DIR_BOTTOM2TOP
+ : DIR_TOP2BOTTOM );
+ }
+ }
+ }
+ else if ( pPor->InNumberGrp() )
+ {
+ const SwFont* pNumFnt = static_cast<SwFieldPortion*>(pPor)->GetFont();
+
+ if ( pNumFnt )
+ {
+ Degree10 nDir = pNumFnt->GetOrientation( rInf.GetTextFrame()->IsVertical() );
+ if ( nDir )
+ {
+ delete pPor;
+ pPor = new SwRotatedPortion(TextFrameIndex(0), 900_deg10 == nDir
+ ? DIR_BOTTOM2TOP
+ : DIR_TOP2BOTTOM );
+
+ rInf.SetNumDone( false );
+ rInf.SetFootnoteDone( false );
+ }
+ }
+ }
+ }
+
+ // The font is set in output device,
+ // the ascent and the height will be calculated.
+ if( !pPor->GetAscent() && !pPor->Height() )
+ CalcAscent( rInf, pPor );
+ rInf.SetLen( pPor->GetLen() );
+
+ // In CalcFlyWidth Width() will be shortened if a FlyPortion is present.
+ CalcFlyWidth( rInf );
+
+ // One must not forget that pCurr as GetLast() must provide reasonable values:
+ if( !m_pCurr->Height() )
+ {
+ OSL_ENSURE( m_pCurr->Height(), "SwTextFormatter::NewPortion: limbo dance" );
+ m_pCurr->Height( pPor->Height(), false );
+ m_pCurr->SetAscent( pPor->GetAscent() );
+ }
+
+ OSL_ENSURE(pPor->Height(), "SwTextFormatter::NewPortion: something went wrong");
+ if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() )
+ {
+ delete pPor;
+ pPor = rInf.GetFly();
+ }
+ return pPor;
+}
+
+TextFrameIndex SwTextFormatter::FormatLine(TextFrameIndex const nStartPos)
+{
+ OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
+ "SwTextFormatter::FormatLine( nStartPos ) with unswapped frame" );
+
+ // For the formatting routines, we set pOut to the reference device.
+ SwHookOut aHook( GetInfo() );
+ if (GetInfo().GetLen() < TextFrameIndex(GetInfo().GetText().getLength()))
+ GetInfo().SetLen(TextFrameIndex(GetInfo().GetText().getLength()));
+
+ bool bBuild = true;
+ SetFlyInCntBase( false );
+ GetInfo().SetLineHeight( 0 );
+ GetInfo().SetLineNetHeight( 0 );
+
+ // Recycling must be suppressed by changed line height and also
+ // by changed ascent (lowering of baseline).
+ const SwTwips nOldHeight = m_pCurr->Height();
+ const SwTwips nOldAscent = m_pCurr->GetAscent();
+
+ m_pCurr->SetEndHyph( false );
+ m_pCurr->SetMidHyph( false );
+
+ // fly positioning can make it necessary format a line several times
+ // for this, we have to keep a copy of our rest portion
+ SwLinePortion* pField = GetInfo().GetRest();
+ std::unique_ptr<SwFieldPortion> xSaveField;
+
+ if ( pField && pField->InFieldGrp() && !pField->IsFootnotePortion() )
+ xSaveField.reset(new SwFieldPortion( *static_cast<SwFieldPortion*>(pField) ));
+
+ // for an optimal repaint rectangle, we want to compare fly portions
+ // before and after the BuildPortions call
+ const bool bOptimizeRepaint = AllowRepaintOpt();
+ TextFrameIndex const nOldLineEnd = nStartPos + m_pCurr->GetLen();
+ std::vector<tools::Long> flyStarts;
+
+ // these are the conditions for a fly position comparison
+ if ( bOptimizeRepaint && m_pCurr->IsFly() )
+ {
+ SwLinePortion* pPor = m_pCurr->GetFirstPortion();
+ tools::Long nPOfst = 0;
+ while ( pPor )
+ {
+ if ( pPor->IsFlyPortion() )
+ // insert start value of fly portion
+ flyStarts.push_back( nPOfst );
+
+ nPOfst += pPor->Width();
+ pPor = pPor->GetNextPortion();
+ }
+ }
+
+ // Here soon the underflow check follows.
+ while( bBuild )
+ {
+ GetInfo().SetFootnoteInside( false );
+ GetInfo().SetOtherThanFootnoteInside( false );
+
+ // These values must not be reset by FormatReset();
+ const bool bOldNumDone = GetInfo().IsNumDone();
+ const bool bOldArrowDone = GetInfo().IsArrowDone();
+ const bool bOldErgoDone = GetInfo().IsErgoDone();
+
+ // besides other things, this sets the repaint offset to 0
+ FormatReset( GetInfo() );
+
+ GetInfo().SetNumDone( bOldNumDone );
+ GetInfo().SetArrowDone( bOldArrowDone );
+ GetInfo().SetErgoDone( bOldErgoDone );
+
+ // build new portions for this line
+ BuildPortions( GetInfo() );
+
+ if( GetInfo().IsStop() )
+ {
+ m_pCurr->SetLen(TextFrameIndex(0));
+ m_pCurr->Height( GetFrameRstHeight() + 1, false );
+ m_pCurr->SetRealHeight( GetFrameRstHeight() + 1 );
+ m_pCurr->Width(0);
+ m_pCurr->Truncate();
+ return nStartPos;
+ }
+ else if( GetInfo().IsDropInit() )
+ {
+ DropInit();
+ GetInfo().SetDropInit( false );
+ }
+
+ m_pCurr->CalcLine( *this, GetInfo() );
+ CalcRealHeight( GetInfo().IsNewLine() );
+
+ //i#120864 For Special case that at the first calculation couldn't get
+ //correct height. And need to recalculate for the right height.
+ SwLinePortion* pPorTmp = m_pCurr->GetNextPortion();
+ if ( IsFlyInCntBase() && (!IsQuick() || (pPorTmp && pPorTmp->IsFlyCntPortion() && !pPorTmp->GetNextPortion() &&
+ m_pCurr->Height() > pPorTmp->Height())))
+ {
+ SwTwips nTmpAscent, nTmpHeight;
+ CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+ AlignFlyInCntBase( Y() + tools::Long( nTmpAscent ) );
+ m_pCurr->CalcLine( *this, GetInfo() );
+ CalcRealHeight();
+ }
+
+ // bBuild decides if another lap of honor is done
+ if ( m_pCurr->GetRealHeight() <= GetInfo().GetLineHeight() )
+ {
+ m_pCurr->SetRealHeight( GetInfo().GetLineHeight() );
+ bBuild = false;
+ }
+ else
+ {
+ bBuild = ( GetInfo().GetTextFly().IsOn() && ChkFlyUnderflow(GetInfo()) )
+ || GetInfo().CheckFootnotePortion(m_pCurr);
+ if( bBuild )
+ {
+ GetInfo().SetNumDone( bOldNumDone );
+ GetInfo().ResetMaxWidthDiff();
+
+ // delete old rest
+ if ( GetInfo().GetRest() )
+ {
+ delete GetInfo().GetRest();
+ GetInfo().SetRest( nullptr );
+ }
+
+ // set original rest portion
+ if ( xSaveField )
+ GetInfo().SetRest( new SwFieldPortion( *xSaveField ) );
+
+ m_pCurr->SetLen(TextFrameIndex(0));
+ m_pCurr->Width(0);
+ m_pCurr->Truncate();
+ }
+ }
+ }
+
+ // In case of compat mode, it's possible that a tab portion is wider after
+ // formatting than before. If this is the case, we also have to make sure
+ // the SwLineLayout is wider as well.
+ if (GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN))
+ {
+ sal_uInt16 nSum = 0;
+ SwLinePortion* pPor = m_pCurr->GetFirstPortion();
+
+ while (pPor)
+ {
+ nSum += pPor->Width();
+ pPor = pPor->GetNextPortion();
+ }
+
+ if (nSum > m_pCurr->Width())
+ m_pCurr->Width(nSum);
+ }
+
+ // calculate optimal repaint rectangle
+ if ( bOptimizeRepaint )
+ {
+ GetInfo().SetPaintOfst( ::lcl_CalcOptRepaint( *this, *m_pCurr, nOldLineEnd, flyStarts ) );
+ flyStarts.clear();
+ }
+ else
+ // Special case: we do not allow an optimization of the repaint
+ // area, but during formatting the repaint offset is set to indicate
+ // a maximum value for the offset. This value has to be reset:
+ GetInfo().SetPaintOfst( 0 );
+
+ // This corrects the start of the reformat range if something has
+ // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt
+ // will give us a wrong result if we have to reformat another line
+ GetInfo().GetParaPortion()->GetReformat().LeftMove( GetInfo().GetIdx() );
+
+ // delete master copy of rest portion
+ xSaveField.reset();
+
+ TextFrameIndex const nNewStart = nStartPos + m_pCurr->GetLen();
+
+ // adjust text if kana compression is enabled
+ if ( GetInfo().CompressLine() )
+ {
+ SwTwips nRepaintOfst = CalcKanaAdj( m_pCurr );
+
+ // adjust repaint offset
+ if ( nRepaintOfst < GetInfo().GetPaintOfst() )
+ GetInfo().SetPaintOfst( nRepaintOfst );
+ }
+
+ CalcAdjustLine( m_pCurr );
+
+ if( nOldHeight != m_pCurr->Height() || nOldAscent != m_pCurr->GetAscent() )
+ {
+ SetFlyInCntBase();
+ GetInfo().SetPaintOfst( 0 ); // changed line height => no recycling
+ // all following line must be painted and when Flys are around,
+ // also formatted
+ GetInfo().SetShift( true );
+ }
+
+ if ( IsFlyInCntBase() && !IsQuick() )
+ UpdatePos( m_pCurr, GetTopLeft(), GetStart() );
+
+ return nNewStart;
+}
+
+void SwTextFormatter::RecalcRealHeight()
+{
+ do
+ {
+ CalcRealHeight();
+ } while (Next());
+}
+
+void SwTextFormatter::CalcRealHeight( bool bNewLine )
+{
+ SwTwips nLineHeight = m_pCurr->Height();
+ m_pCurr->SetClipping( false );
+
+ SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
+ if ( pGrid && GetInfo().SnapToGrid() )
+ {
+ const sal_uInt16 nGridWidth = pGrid->GetBaseHeight();
+ const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
+ const bool bRubyTop = ! pGrid->GetRubyTextBelow();
+
+ nLineHeight = nGridWidth + nRubyHeight;
+ const sal_uInt16 nAmpRatio = (m_pCurr->Height() + nLineHeight - 1)/nLineHeight;
+ nLineHeight *= nAmpRatio;
+
+ const sal_uInt16 nAsc = m_pCurr->GetAscent() +
+ ( bRubyTop ?
+ ( nLineHeight - m_pCurr->Height() + nRubyHeight ) / 2 :
+ ( nLineHeight - m_pCurr->Height() - nRubyHeight ) / 2 );
+
+ m_pCurr->Height( nLineHeight, false );
+ m_pCurr->SetAscent( nAsc );
+ m_pInf->GetParaPortion()->SetFixLineHeight();
+
+ // we ignore any line spacing options except from ...
+ const SvxLineSpacingItem* pSpace = m_aLineInf.GetLineSpacing();
+ if ( ! IsParaLine() && pSpace &&
+ SvxInterLineSpaceRule::Prop == pSpace->GetInterLineSpaceRule() )
+ {
+ sal_uLong nTmp = pSpace->GetPropLineSpace();
+
+ if( nTmp < 100 )
+ nTmp = 100;
+
+ nTmp *= nLineHeight;
+ nLineHeight = nTmp / 100;
+ }
+
+ m_pCurr->SetRealHeight( nLineHeight );
+ return;
+ }
+
+ // The dummy flag is set on lines that only contain flyportions, these shouldn't
+ // consider register-true and so on. Unfortunately an empty line can be at
+ // the end of a paragraph (empty paragraphs or behind a Shift-Return),
+ // which should consider the register.
+ if (!m_pCurr->IsDummy() || (!m_pCurr->GetNext()
+ && GetStart() >= TextFrameIndex(GetTextFrame()->GetText().getLength())
+ && !bNewLine))
+ {
+ const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
+ if( pSpace )
+ {
+ switch( pSpace->GetLineSpaceRule() )
+ {
+ case SvxLineSpaceRule::Auto:
+ // shrink first line of paragraph too on spacing < 100%
+ if (IsParaLine() &&
+ pSpace->GetInterLineSpaceRule() == SvxInterLineSpaceRule::Prop
+ && GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::PROP_LINE_SPACING_SHRINKS_FIRST_LINE))
+ {
+ tools::Long nTmp = pSpace->GetPropLineSpace();
+ // Word will render < 50% too but it's just not readable
+ if( nTmp < 50 )
+ nTmp = nTmp ? 50 : 100;
+ if (nTmp<100) { // code adapted from fixed line height
+ nTmp *= nLineHeight;
+ nTmp /= 100;
+ if( !nTmp )
+ ++nTmp;
+ nLineHeight = nTmp;
+ sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
+#if 0
+ // could do clipping here (like Word does)
+ // but at 0.5 its unreadable either way...
+ if( nAsc < pCurr->GetAscent() ||
+ nLineHeight - nAsc < pCurr->Height() -
+ pCurr->GetAscent() )
+ pCurr->SetClipping( true );
+#endif
+ m_pCurr->SetAscent( nAsc );
+ m_pCurr->Height( nLineHeight, false );
+ m_pInf->GetParaPortion()->SetFixLineHeight();
+ }
+ }
+ break;
+ case SvxLineSpaceRule::Min:
+ {
+ if( nLineHeight < pSpace->GetLineHeight() )
+ nLineHeight = pSpace->GetLineHeight();
+ break;
+ }
+ case SvxLineSpaceRule::Fix:
+ {
+ nLineHeight = pSpace->GetLineHeight();
+ const sal_uInt16 nAsc = ( 4 * nLineHeight ) / 5; // 80%
+ if( nAsc < m_pCurr->GetAscent() ||
+ nLineHeight - nAsc < m_pCurr->Height() - m_pCurr->GetAscent() )
+ m_pCurr->SetClipping( true );
+ m_pCurr->Height( nLineHeight, false );
+ m_pCurr->SetAscent( nAsc );
+ m_pInf->GetParaPortion()->SetFixLineHeight();
+ }
+ break;
+ default: OSL_FAIL( ": unknown LineSpaceRule" );
+ }
+ // Note: for the _first_ line the line spacing of the previous
+ // paragraph is applied in SwFlowFrame::CalcUpperSpace()
+ if( !IsParaLine() )
+ switch( pSpace->GetInterLineSpaceRule() )
+ {
+ case SvxInterLineSpaceRule::Off:
+ break;
+ case SvxInterLineSpaceRule::Prop:
+ {
+ tools::Long nTmp = pSpace->GetPropLineSpace();
+ // 50% is the minimum, if 0% we switch to the
+ // default value 100% ...
+ if( nTmp < 50 )
+ nTmp = nTmp ? 50 : 100;
+
+ // extend line height by (nPropLineSpace - 100) percent of the font height
+ nTmp -= 100;
+ nTmp *= m_pCurr->GetTextHeight();
+ nTmp /= 100;
+ nTmp += nLineHeight;
+ if (nTmp < 1)
+ nTmp = 1;
+ nLineHeight = nTmp;
+ break;
+ }
+ case SvxInterLineSpaceRule::Fix:
+ {
+ nLineHeight = nLineHeight + pSpace->GetInterLineSpace();
+ break;
+ }
+ default: OSL_FAIL( ": unknown InterLineSpaceRule" );
+ }
+ }
+
+ if( IsRegisterOn() )
+ {
+ SwTwips nTmpY = Y() + m_pCurr->GetAscent() + nLineHeight - m_pCurr->Height();
+ SwRectFnSet aRectFnSet(m_pFrame);
+ if ( aRectFnSet.IsVert() )
+ nTmpY = m_pFrame->SwitchHorizontalToVertical( nTmpY );
+ nTmpY = aRectFnSet.YDiff( nTmpY, RegStart() );
+ const sal_uInt16 nDiff = sal_uInt16( nTmpY % RegDiff() );
+ if( nDiff )
+ nLineHeight += RegDiff() - nDiff;
+ }
+ }
+ m_pCurr->SetRealHeight( nLineHeight );
+}
+
+void SwTextFormatter::FeedInf( SwTextFormatInfo &rInf ) const
+{
+ // delete Fly in any case!
+ ClearFly( rInf );
+ rInf.Init();
+
+ rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() );
+ rInf.SetRoot( m_pCurr );
+ rInf.SetLineStart( m_nStart );
+ rInf.SetIdx( m_nStart );
+ rInf.Left( Left() );
+ rInf.Right( Right() );
+ rInf.First( FirstLeft() );
+ rInf.LeftMargin(GetLeftMargin());
+
+ rInf.RealWidth( sal_uInt16(rInf.Right() - GetLeftMargin()) );
+ rInf.Width( rInf.RealWidth() );
+ if( const_cast<SwTextFormatter*>(this)->GetRedln() )
+ {
+ const_cast<SwTextFormatter*>(this)->GetRedln()->Clear( const_cast<SwTextFormatter*>(this)->GetFnt() );
+ const_cast<SwTextFormatter*>(this)->GetRedln()->Reset();
+ }
+}
+
+void SwTextFormatter::FormatReset( SwTextFormatInfo &rInf )
+{
+ m_pFirstOfBorderMerge = nullptr;
+ m_pCurr->Truncate();
+ m_pCurr->Init();
+
+ // delete pSpaceAdd and pKanaComp
+ m_pCurr->FinishSpaceAdd();
+ m_pCurr->FinishKanaComp();
+ m_pCurr->ResetFlags();
+ FeedInf( rInf );
+}
+
+bool SwTextFormatter::CalcOnceMore()
+{
+ if( m_pDropFormat )
+ {
+ const sal_uInt16 nOldDrop = GetDropHeight();
+ CalcDropHeight( m_pDropFormat->GetLines() );
+ m_bOnceMore = nOldDrop != GetDropHeight();
+ }
+ else
+ m_bOnceMore = false;
+ return m_bOnceMore;
+}
+
+SwTwips SwTextFormatter::CalcBottomLine() const
+{
+ SwTwips nRet = Y() + GetLineHeight();
+ SwTwips nMin = GetInfo().GetTextFly().GetMinBottom();
+ if( nMin && ++nMin > nRet )
+ {
+ SwTwips nDist = m_pFrame->getFrameArea().Height() - m_pFrame->getFramePrintArea().Height()
+ - m_pFrame->getFramePrintArea().Top();
+ if( nRet + nDist < nMin )
+ {
+ const bool bRepaint = HasTruncLines() &&
+ GetInfo().GetParaPortion()->GetRepaint().Bottom() == nRet-1;
+ nRet = nMin - nDist;
+ if( bRepaint )
+ {
+ const_cast<SwRepaint&>(GetInfo().GetParaPortion()
+ ->GetRepaint()).Bottom( nRet-1 );
+ const_cast<SwTextFormatInfo&>(GetInfo()).SetPaintOfst( 0 );
+ }
+ }
+ }
+ return nRet;
+}
+
+// FME/OD: This routine does a limited text formatting.
+SwTwips SwTextFormatter::CalcFitToContent_()
+{
+ FormatReset( GetInfo() );
+ BuildPortions( GetInfo() );
+ m_pCurr->CalcLine( *this, GetInfo() );
+ return m_pCurr->Width();
+}
+
+// determines if the calculation of a repaint offset is allowed
+// otherwise each line is painted from 0 (this is a copy of the beginning
+// of the former SwTextFormatter::Recycle() function
+bool SwTextFormatter::AllowRepaintOpt() const
+{
+ // reformat position in front of current line? Only in this case
+ // we want to set the repaint offset
+ bool bOptimizeRepaint = m_nStart < GetInfo().GetReformatStart() &&
+ m_pCurr->GetLen();
+
+ // a special case is the last line of a block adjusted paragraph:
+ if ( bOptimizeRepaint )
+ {
+ switch( GetAdjust() )
+ {
+ case SvxAdjust::Block:
+ {
+ if( IsLastBlock() || IsLastCenter() )
+ bOptimizeRepaint = false;
+ else
+ {
+ // ????: blank in the last master line (blocksat.sdw)
+ bOptimizeRepaint = nullptr == m_pCurr->GetNext() && !m_pFrame->GetFollow();
+ if ( bOptimizeRepaint )
+ {
+ SwLinePortion *pPos = m_pCurr->GetFirstPortion();
+ while ( pPos && !pPos->IsFlyPortion() )
+ pPos = pPos->GetNextPortion();
+ bOptimizeRepaint = !pPos;
+ }
+ }
+ break;
+ }
+ case SvxAdjust::Center:
+ case SvxAdjust::Right:
+ bOptimizeRepaint = false;
+ break;
+ default: ;
+ }
+ }
+
+ // Again another special case: invisible SoftHyphs
+ const TextFrameIndex nReformat = GetInfo().GetReformatStart();
+ if (bOptimizeRepaint && TextFrameIndex(COMPLETE_STRING) != nReformat)
+ {
+ const sal_Unicode cCh = nReformat >= TextFrameIndex(GetInfo().GetText().getLength())
+ ? 0
+ : GetInfo().GetText()[ sal_Int32(nReformat) ];
+ bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
+ || ! GetInfo().HasHint( nReformat );
+ }
+
+ return bOptimizeRepaint;
+}
+
+void SwTextFormatter::CalcUnclipped( SwTwips& rTop, SwTwips& rBottom )
+{
+ OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
+ "SwTextFormatter::CalcUnclipped with unswapped frame" );
+
+ SwTwips nFlyAsc, nFlyDesc;
+ m_pCurr->MaxAscentDescent( rTop, rBottom, nFlyAsc, nFlyDesc );
+ rTop = Y() + GetCurr()->GetAscent();
+ rBottom = rTop + nFlyDesc;
+ rTop -= nFlyAsc;
+}
+
+void SwTextFormatter::UpdatePos( SwLineLayout *pCurrent, Point aStart,
+ TextFrameIndex const nStartIdx, bool bAlways) const
+{
+ OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
+ "SwTextFormatter::UpdatePos with unswapped frame" );
+
+ if( GetInfo().IsTest() )
+ return;
+ SwLinePortion *pFirst = pCurrent->GetFirstPortion();
+ SwLinePortion *pPos = pFirst;
+ SwTextPaintInfo aTmpInf( GetInfo() );
+ aTmpInf.SetpSpaceAdd( pCurrent->GetpLLSpaceAdd() );
+ aTmpInf.ResetSpaceIdx();
+ aTmpInf.SetKanaComp( pCurrent->GetpKanaComp() );
+ aTmpInf.ResetKanaIdx();
+
+ // The frame's size
+ aTmpInf.SetIdx( nStartIdx );
+ aTmpInf.SetPos( aStart );
+
+ SwTwips nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
+ pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
+
+ const SwTwips nTmpHeight = pCurrent->GetRealHeight();
+ SwTwips nAscent = pCurrent->GetAscent() + nTmpHeight - pCurrent->Height();
+ AsCharFlags nFlags = AsCharFlags::UlSpace;
+ if( GetMulti() )
+ {
+ aTmpInf.SetDirection( GetMulti()->GetDirection() );
+ if( GetMulti()->HasRotation() )
+ {
+ nFlags |= AsCharFlags::Rotate;
+ if( GetMulti()->IsRevers() )
+ {
+ nFlags |= AsCharFlags::Reverse;
+ aTmpInf.X( aTmpInf.X() - nAscent );
+ }
+ else
+ aTmpInf.X( aTmpInf.X() + nAscent );
+ }
+ else
+ {
+ if ( GetMulti()->IsBidi() )
+ nFlags |= AsCharFlags::Bidi;
+ aTmpInf.Y( aTmpInf.Y() + nAscent );
+ }
+ }
+ else
+ aTmpInf.Y( aTmpInf.Y() + nAscent );
+
+ while( pPos )
+ {
+ // We only know one case where changing the position (caused by the
+ // adjustment) could be relevant for a portion: We need to SetRefPoint
+ // for FlyCntPortions.
+ if( ( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() )
+ && ( bAlways || !IsQuick() ) )
+ {
+ pCurrent->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos );
+
+ if( pPos->IsGrfNumPortion() )
+ {
+ if( !nFlyAsc && !nFlyDesc )
+ {
+ nTmpAscent = nAscent;
+ nFlyAsc = nAscent;
+ nTmpDescent = nTmpHeight - nAscent;
+ nFlyDesc = nTmpDescent;
+ }
+ static_cast<SwGrfNumPortion*>(pPos)->SetBase( nTmpAscent, nTmpDescent,
+ nFlyAsc, nFlyDesc );
+ }
+ else
+ {
+ Point aBase( aTmpInf.GetPos() );
+ if ( GetInfo().GetTextFrame()->IsVertical() )
+ GetInfo().GetTextFrame()->SwitchHorizontalToVertical( aBase );
+
+ static_cast<SwFlyCntPortion*>(pPos)->SetBase( *aTmpInf.GetTextFrame(),
+ aBase, nTmpAscent, nTmpDescent, nFlyAsc,
+ nFlyDesc, nFlags );
+ }
+ }
+ if( pPos->IsMultiPortion() && static_cast<SwMultiPortion*>(pPos)->HasFlyInContent() )
+ {
+ OSL_ENSURE( !GetMulti(), "Too much multi" );
+ const_cast<SwTextFormatter*>(this)->m_pMulti = static_cast<SwMultiPortion*>(pPos);
+ SwLineLayout *pLay = &GetMulti()->GetRoot();
+ Point aSt( aTmpInf.X(), aStart.Y() );
+
+ if ( GetMulti()->HasBrackets() )
+ {
+ OSL_ENSURE( GetMulti()->IsDouble(), "Brackets only for doubles");
+ aSt.AdjustX(static_cast<SwDoubleLinePortion*>(GetMulti())->PreWidth() );
+ }
+ else if( GetMulti()->HasRotation() )
+ {
+ aSt.AdjustY(pCurrent->GetAscent() - GetMulti()->GetAscent() );
+ if( GetMulti()->IsRevers() )
+ aSt.AdjustX(GetMulti()->Width() );
+ else
+ aSt.AdjustY(GetMulti()->Height() );
+ }
+ else if ( GetMulti()->IsBidi() )
+ // jump to end of the bidi portion
+ aSt.AdjustX(pLay->Width() );
+
+ TextFrameIndex nStIdx = aTmpInf.GetIdx();
+ do
+ {
+ UpdatePos( pLay, aSt, nStIdx, bAlways );
+ nStIdx = nStIdx + pLay->GetLen();
+ aSt.AdjustY(pLay->Height() );
+ pLay = pLay->GetNext();
+ } while ( pLay );
+ const_cast<SwTextFormatter*>(this)->m_pMulti = nullptr;
+ }
+ pPos->Move( aTmpInf );
+ pPos = pPos->GetNextPortion();
+ }
+}
+
+void SwTextFormatter::AlignFlyInCntBase( tools::Long nBaseLine ) const
+{
+ OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
+ "SwTextFormatter::AlignFlyInCntBase with unswapped frame" );
+
+ if( GetInfo().IsTest() )
+ return;
+ SwLinePortion *pFirst = m_pCurr->GetFirstPortion();
+ SwLinePortion *pPos = pFirst;
+ AsCharFlags nFlags = AsCharFlags::None;
+ if( GetMulti() && GetMulti()->HasRotation() )
+ {
+ nFlags |= AsCharFlags::Rotate;
+ if( GetMulti()->IsRevers() )
+ nFlags |= AsCharFlags::Reverse;
+ }
+
+ SwTwips nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
+
+ while( pPos )
+ {
+ if( pPos->IsFlyCntPortion() || pPos->IsGrfNumPortion() )
+ {
+ m_pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, pPos );
+
+ if( pPos->IsGrfNumPortion() )
+ static_cast<SwGrfNumPortion*>(pPos)->SetBase( nTmpAscent, nTmpDescent,
+ nFlyAsc, nFlyDesc );
+ else
+ {
+ Point aBase;
+ if ( GetInfo().GetTextFrame()->IsVertical() )
+ {
+ nBaseLine = GetInfo().GetTextFrame()->SwitchHorizontalToVertical( nBaseLine );
+ aBase = Point( nBaseLine, static_cast<SwFlyCntPortion*>(pPos)->GetRefPoint().Y() );
+ }
+ else
+ aBase = Point( static_cast<SwFlyCntPortion*>(pPos)->GetRefPoint().X(), nBaseLine );
+
+ static_cast<SwFlyCntPortion*>(pPos)->SetBase( *GetInfo().GetTextFrame(), aBase, nTmpAscent, nTmpDescent,
+ nFlyAsc, nFlyDesc, nFlags );
+ }
+ }
+ pPos = pPos->GetNextPortion();
+ }
+}
+
+bool SwTextFormatter::ChkFlyUnderflow( SwTextFormatInfo &rInf ) const
+{
+ OSL_ENSURE( rInf.GetTextFly().IsOn(), "SwTextFormatter::ChkFlyUnderflow: why?" );
+ if( GetCurr() )
+ {
+ // First we check, whether a fly overlaps with the line.
+ // = GetLineHeight()
+ const sal_uInt16 nHeight = GetCurr()->GetRealHeight();
+ SwRect aLine( GetLeftMargin(), Y(), rInf.RealWidth(), nHeight );
+
+ SwRect aLineVert( aLine );
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchHorizontalToVertical( aLineVert );
+ SwRect aInter( rInf.GetTextFly().GetFrame( aLineVert ) );
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchVerticalToHorizontal( aInter );
+
+ if( !aInter.HasArea() )
+ return false;
+
+ // We now check every portion that could have lowered for overlapping
+ // with the fly.
+ const SwLinePortion *pPos = GetCurr()->GetFirstPortion();
+ aLine.Pos().setY( Y() + GetCurr()->GetRealHeight() - GetCurr()->Height() );
+ aLine.Height( GetCurr()->Height() );
+
+ while( pPos )
+ {
+ aLine.Width( pPos->Width() );
+
+ aLineVert = aLine;
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchHorizontalToVertical( aLineVert );
+ aInter = rInf.GetTextFly().GetFrame( aLineVert );
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchVerticalToHorizontal( aInter );
+
+ // New flys from below?
+ if( !pPos->IsFlyPortion() )
+ {
+ if( aInter.Overlaps( aLine ) )
+ {
+ aInter.Intersection_( aLine );
+ if( aInter.HasArea() )
+ {
+ // To be evaluated during reformat of this line:
+ // RealHeight including spacing
+ rInf.SetLineHeight( nHeight );
+ // Height without extra spacing
+ rInf.SetLineNetHeight( m_pCurr->Height() );
+ return true;
+ }
+ }
+ }
+ else
+ {
+ // The fly portion is not intersected by a fly anymore
+ if ( ! aInter.Overlaps( aLine ) )
+ {
+ rInf.SetLineHeight( nHeight );
+ rInf.SetLineNetHeight( m_pCurr->Height() );
+ return true;
+ }
+ else
+ {
+ aInter.Intersection_( aLine );
+
+ // No area means a fly has become invalid because of
+ // lowering the line => reformat the line
+ // we also have to reformat the line, if the fly size
+ // differs from the intersection interval's size.
+ if( ! aInter.HasArea() ||
+ static_cast<const SwFlyPortion*>(pPos)->GetFixWidth() != aInter.Width() )
+ {
+ rInf.SetLineHeight( nHeight );
+ rInf.SetLineNetHeight( m_pCurr->Height() );
+ return true;
+ }
+ }
+ }
+
+ aLine.Left( aLine.Left() + pPos->Width() );
+ pPos = pPos->GetNextPortion();
+ }
+ }
+ return false;
+}
+
+void SwTextFormatter::CalcFlyWidth( SwTextFormatInfo &rInf )
+{
+ if( GetMulti() || rInf.GetFly() )
+ return;
+
+ SwTextFly& rTextFly = rInf.GetTextFly();
+ if( !rTextFly.IsOn() || rInf.IsIgnoreFly() )
+ return;
+
+ const SwLinePortion *pLast = rInf.GetLast();
+
+ tools::Long nAscent;
+ tools::Long nTop = Y();
+ tools::Long nHeight;
+
+ if( rInf.GetLineHeight() )
+ {
+ // Real line height has already been calculated, we only have to
+ // search for intersections in the lower part of the strip
+ nAscent = m_pCurr->GetAscent();
+ nHeight = rInf.GetLineNetHeight();
+ nTop += rInf.GetLineHeight() - nHeight;
+ }
+ else
+ {
+ // We make a first guess for the lines real height
+ if ( ! m_pCurr->GetRealHeight() )
+ CalcRealHeight();
+
+ nAscent = pLast->GetAscent();
+ nHeight = pLast->Height();
+
+ if ( m_pCurr->GetRealHeight() > nHeight )
+ nTop += m_pCurr->GetRealHeight() - nHeight;
+ else
+ // Important for fixed space between lines
+ nHeight = m_pCurr->GetRealHeight();
+ }
+
+ const tools::Long nLeftMar = GetLeftMargin();
+ const tools::Long nLeftMin = (rInf.X() || GetDropLeft()) ? nLeftMar : GetLeftMin();
+
+ SwRect aLine( rInf.X() + nLeftMin, nTop, rInf.RealWidth() - rInf.X()
+ + nLeftMar - nLeftMin , nHeight );
+
+ // tdf#116486: consider also the upper margin from getFramePrintArea because intersections
+ // with this additional space should lead to repositioning of paragraphs
+ // For compatibility we grab a related compat flag:
+ if (GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS)
+ && IsFirstTextLine())
+ {
+ const tools::Long nUpper = m_pFrame->getFramePrintArea().Top();
+ // Increase the rectangle
+ if( nUpper > 0 && nTop >= nUpper )
+ aLine.SubTop( nUpper );
+ }
+ SwRect aLineVert( aLine );
+ if ( m_pFrame->IsRightToLeft() )
+ m_pFrame->SwitchLTRtoRTL( aLineVert );
+
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchHorizontalToVertical( aLineVert );
+
+ // GetFrame(...) determines and returns the intersection rectangle
+ SwRect aInter( rTextFly.GetFrame( aLineVert ) );
+
+ if ( m_pFrame->IsRightToLeft() )
+ m_pFrame->SwitchRTLtoLTR( aInter );
+
+ if ( m_pFrame->IsVertical() )
+ m_pFrame->SwitchVerticalToHorizontal( aInter );
+
+ if (!aInter.IsEmpty() && aInter.Bottom() < nTop)
+ {
+ // Intersects with the frame area (with upper margin), but not with the print area (without
+ // upper margin). Don't reserve space for the fly portion in this case, text is allowed to
+ // flow there.
+ aInter.Height(0);
+ }
+
+ if( !aInter.Overlaps( aLine ) )
+ return;
+
+ aLine.Left( rInf.X() + nLeftMar );
+ bool bForced = false;
+ if( aInter.Left() <= nLeftMin )
+ {
+ SwTwips nFrameLeft = GetTextFrame()->getFrameArea().Left();
+ if( GetTextFrame()->getFramePrintArea().Left() < 0 )
+ nFrameLeft += GetTextFrame()->getFramePrintArea().Left();
+ if( aInter.Left() < nFrameLeft )
+ aInter.Left( nFrameLeft );
+
+ tools::Long nAddMar = 0;
+ if ( m_pFrame->IsRightToLeft() )
+ {
+ nAddMar = m_pFrame->getFrameArea().Right() - Right();
+ if ( nAddMar < 0 )
+ nAddMar = 0;
+ }
+ else
+ nAddMar = nLeftMar - nFrameLeft;
+
+ aInter.Width( aInter.Width() + nAddMar );
+ // For a negative first line indent, we set this flag to show
+ // that the indentation/margin has been moved.
+ // This needs to be respected by the DefaultTab at the zero position.
+ if( IsFirstTextLine() && HasNegFirst() )
+ bForced = true;
+ }
+ aInter.Intersection( aLine );
+ if( !aInter.HasArea() )
+ return;
+
+ const bool bFullLine = aLine.Left() == aInter.Left() &&
+ aLine.Right() == aInter.Right();
+
+ // Although no text is left, we need to format another line,
+ // because also empty lines need to avoid a Fly with no wrapping.
+ if (bFullLine && rInf.GetIdx() == TextFrameIndex(rInf.GetText().getLength()))
+ {
+ rInf.SetNewLine( true );
+ // We know that for dummies, it holds ascent == height
+ m_pCurr->SetDummy(true);
+ }
+
+ // aInter becomes frame-local
+ aInter.Pos().AdjustX( -nLeftMar );
+ SwFlyPortion *pFly = new SwFlyPortion( aInter );
+ if( bForced )
+ {
+ m_pCurr->SetForcedLeftMargin();
+ rInf.ForcedLeftMargin( o3tl::narrowing<sal_uInt16>(aInter.Width()) );
+ }
+
+ if( bFullLine )
+ {
+ // In order to properly flow around Flys with different
+ // wrapping attributes, we need to increase by units of line height.
+ // The last avoiding line should be adjusted in height, so that
+ // we don't get a frame spacing effect.
+ // It is important that ascent == height, because the FlyPortion
+ // values are transferred to pCurr in CalcLine and IsDummy() relies
+ // on this behaviour.
+ // To my knowledge we only have two places where DummyLines can be
+ // created: here and in MakeFlyDummies.
+ // IsDummy() is evaluated in IsFirstTextLine(), when moving lines
+ // and in relation with DropCaps.
+ pFly->Height( aInter.Height() );
+
+ // nNextTop now contains the margin's bottom edge, which we avoid
+ // or the next margin's top edge, which we need to respect.
+ // That means we can comfortably grow up to this value; that's how
+ // we save a few empty lines.
+ tools::Long nNextTop = rTextFly.GetNextTop();
+ if ( m_pFrame->IsVertical() )
+ nNextTop = m_pFrame->SwitchVerticalToHorizontal( nNextTop );
+ if( nNextTop > aInter.Bottom() )
+ {
+ SwTwips nH = nNextTop - aInter.Top();
+ if( nH < SAL_MAX_UINT16 )
+ pFly->Height( nH );
+ }
+ if( nAscent < pFly->Height() )
+ pFly->SetAscent( nAscent );
+ else
+ pFly->SetAscent( pFly->Height() );
+ }
+ else
+ {
+ if (rInf.GetIdx() == TextFrameIndex(rInf.GetText().getLength()))
+ {
+ // Don't use nHeight, or we have a huge descent
+ pFly->Height( pLast->Height() );
+ pFly->SetAscent( pLast->GetAscent() );
+ }
+ else
+ {
+ pFly->Height( aInter.Height() );
+ if( nAscent < pFly->Height() )
+ pFly->SetAscent( nAscent );
+ else
+ pFly->SetAscent( pFly->Height() );
+ }
+ }
+
+ rInf.SetFly( pFly );
+
+ if( pFly->GetFix() < rInf.Width() )
+ rInf.Width( pFly->GetFix() );
+
+ SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
+ if ( !pGrid )
+ return;
+
+ const SwPageFrame* pPageFrame = m_pFrame->FindPageFrame();
+ const SwLayoutFrame* pBody = pPageFrame->FindBodyCont();
+
+ SwRectFnSet aRectFnSet(pPageFrame);
+
+ const tools::Long nGridOrigin = pBody ?
+ aRectFnSet.GetPrtLeft(*pBody) :
+ aRectFnSet.GetPrtLeft(*pPageFrame);
+
+ const SwDoc & rDoc = rInf.GetTextFrame()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, rDoc);
+
+ SwTwips nStartX = GetLeftMargin();
+ if ( aRectFnSet.IsVert() )
+ {
+ Point aPoint( nStartX, 0 );
+ m_pFrame->SwitchHorizontalToVertical( aPoint );
+ nStartX = aPoint.Y();
+ }
+
+ const SwTwips nOfst = nStartX - nGridOrigin;
+ const SwTwips nTmpWidth = rInf.Width() + nOfst;
+
+ const SwTwips i = nTmpWidth / nGridWidth + 1;
+
+ const SwTwips nNewWidth = ( i - 1 ) * nGridWidth - nOfst;
+ if ( nNewWidth > 0 )
+ rInf.Width( nNewWidth );
+ else
+ rInf.Width( 0 );
+
+
+}
+
+SwFlyCntPortion *SwTextFormatter::NewFlyCntPortion( SwTextFormatInfo &rInf,
+ SwTextAttr *pHint ) const
+{
+ const SwFrame *pFrame = m_pFrame;
+
+ SwFlyInContentFrame *pFly;
+ SwFrameFormat* pFrameFormat = static_cast<SwTextFlyCnt*>(pHint)->GetFlyCnt().GetFrameFormat();
+ if( RES_FLYFRMFMT == pFrameFormat->Which() )
+ {
+ // set Lock pFrame to avoid m_pCurr getting deleted
+ TextFrameLockGuard aGuard(m_pFrame);
+ pFly = static_cast<SwTextFlyCnt*>(pHint)->GetFlyFrame(pFrame);
+ }
+ else
+ pFly = nullptr;
+ // aBase is the document-global position, from which the new extra portion is placed
+ // aBase.X() = Offset in the line after the current position
+ // aBase.Y() = LineIter.Y() + Ascent of the current position
+
+ SwTwips nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc;
+ // i#11859 - use new method <SwLineLayout::MaxAscentDescent(..)>
+ // to change line spacing behaviour at paragraph - Compatibility to MS Word
+ //SwLinePortion *pPos = pCurr->GetFirstPortion();
+ //lcl_MaxAscDescent( pPos, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
+ m_pCurr->MaxAscentDescent( nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc );
+
+ // If the ascent of the frame is larger than the ascent of the current position,
+ // we use this one when calculating the base, or the frame would be positioned
+ // too much to the top, sliding down after all causing a repaint in an area
+ // he actually never was in.
+ SwTwips nAscent = 0;
+
+ const bool bTextFrameVertical = GetInfo().GetTextFrame()->IsVertical();
+
+ const bool bUseFlyAscent = pFly && pFly->isFrameAreaPositionValid() &&
+ 0 != ( bTextFrameVertical ?
+ pFly->GetRefPoint().X() :
+ pFly->GetRefPoint().Y() );
+
+ if ( bUseFlyAscent )
+ nAscent = std::abs( int( bTextFrameVertical ?
+ pFly->GetRelPos().X() :
+ pFly->GetRelPos().Y() ) );
+
+ // Check if be prefer to use the ascent of the last portion:
+ if ( IsQuick() ||
+ !bUseFlyAscent ||
+ nAscent < rInf.GetLast()->GetAscent() )
+ {
+ nAscent = rInf.GetLast()->GetAscent();
+ }
+ else if( nAscent > nFlyAsc )
+ nFlyAsc = nAscent;
+
+ Point aBase( GetLeftMargin() + rInf.X(), Y() + nAscent );
+ AsCharFlags nMode = IsQuick() ? AsCharFlags::Quick : AsCharFlags::None;
+ if( GetMulti() && GetMulti()->HasRotation() )
+ {
+ nMode |= AsCharFlags::Rotate;
+ if( GetMulti()->IsRevers() )
+ nMode |= AsCharFlags::Reverse;
+ }
+
+ Point aTmpBase( aBase );
+ if ( GetInfo().GetTextFrame()->IsVertical() )
+ GetInfo().GetTextFrame()->SwitchHorizontalToVertical( aTmpBase );
+
+ SwFlyCntPortion* pRet(nullptr);
+ if( pFly )
+ {
+ pRet = sw::FlyContentPortion::Create(*GetInfo().GetTextFrame(), pFly, aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode);
+ // We need to make sure that our font is set again in the OutputDevice
+ // It could be that the FlyInCnt was added anew and GetFlyFrame() would
+ // in turn cause, that it'd be created anew again.
+ // This one's frames get formatted right away, which change the font.
+ rInf.SelectFont();
+ if( pRet->GetAscent() > nAscent )
+ {
+ aBase.setY( Y() + pRet->GetAscent() );
+ nMode |= AsCharFlags::UlSpace;
+ if( !rInf.IsTest() )
+ {
+ aTmpBase = aBase;
+ if ( GetInfo().GetTextFrame()->IsVertical() )
+ GetInfo().GetTextFrame()->SwitchHorizontalToVertical( aTmpBase );
+
+ pRet->SetBase( *rInf.GetTextFrame(), aTmpBase, nTmpAscent,
+ nTmpDescent, nFlyAsc, nFlyDesc, nMode );
+ }
+ }
+ }
+ else
+ {
+ pRet = sw::DrawFlyCntPortion::Create(*rInf.GetTextFrame(), *pFrameFormat, aTmpBase, nTmpAscent, nTmpDescent, nFlyAsc, nFlyDesc, nMode);
+ }
+ return pRet;
+}
+
+/* Drop portion is a special case, because it has parts which aren't portions
+ but we have handle them just like portions */
+void SwTextFormatter::MergeCharacterBorder( SwDropPortion const & rPortion )
+{
+ if( rPortion.GetLines() <= 1 )
+ return;
+
+ SwDropPortionPart* pCurrPart = rPortion.GetPart();
+ while( pCurrPart )
+ {
+ if( pCurrPart->GetFollow() &&
+ ::lcl_HasSameBorder(pCurrPart->GetFont(), pCurrPart->GetFollow()->GetFont()) )
+ {
+ pCurrPart->SetJoinBorderWithNext(true);
+ pCurrPart->GetFollow()->SetJoinBorderWithPrev(true);
+ }
+ pCurrPart = pCurrPart->GetFollow();
+ }
+}
+
+void SwTextFormatter::MergeCharacterBorder( SwLinePortion& rPortion, SwLinePortion const *pPrev, SwTextFormatInfo& rInf )
+{
+ const SwFont aCurFont = *rInf.GetFont();
+ if( !aCurFont.HasBorder() )
+ return;
+
+ if (pPrev && pPrev->GetJoinBorderWithNext() )
+ {
+ // In some case border merge is called twice to the portion
+ if( !rPortion.GetJoinBorderWithPrev() )
+ {
+ rPortion.SetJoinBorderWithPrev(true);
+ if( rPortion.InTextGrp() && rPortion.Width() > aCurFont.GetLeftBorderSpace() )
+ rPortion.Width(rPortion.Width() - aCurFont.GetLeftBorderSpace());
+ }
+ }
+ else
+ {
+ rPortion.SetJoinBorderWithPrev(false);
+ m_pFirstOfBorderMerge = &rPortion;
+ }
+
+ // Get next portion's font
+ bool bSeek = false;
+ if (!rInf.IsFull() && // Not the last portion of the line (in case of line break)
+ rInf.GetIdx() + rPortion.GetLen() != TextFrameIndex(rInf.GetText().getLength())) // Not the last portion of the paragraph
+ {
+ bSeek = Seek(rInf.GetIdx() + rPortion.GetLen());
+ }
+ // Don't join the next portion if SwKernPortion sits between two different boxes.
+ bool bDisconnect = rPortion.IsKernPortion() && !rPortion.GetJoinBorderWithPrev();
+ // If next portion has the same border then merge
+ if( bSeek && GetFnt()->HasBorder() && ::lcl_HasSameBorder(aCurFont, *GetFnt()) && !bDisconnect )
+ {
+ // In some case border merge is called twice to the portion
+ if( !rPortion.GetJoinBorderWithNext() )
+ {
+ rPortion.SetJoinBorderWithNext(true);
+ if( rPortion.InTextGrp() && rPortion.Width() > aCurFont.GetRightBorderSpace() )
+ rPortion.Width(rPortion.Width() - aCurFont.GetRightBorderSpace());
+ }
+ }
+ // If this is the last portion of the merge group then make the real height merge
+ else
+ {
+ rPortion.SetJoinBorderWithNext(false);
+ if( m_pFirstOfBorderMerge != &rPortion )
+ {
+ // Calculate maximum height and ascent
+ SwLinePortion* pActPor = m_pFirstOfBorderMerge;
+ sal_uInt16 nMaxAscent = 0;
+ sal_uInt16 nMaxHeight = 0;
+ bool bReachCurrent = false;
+ while( pActPor )
+ {
+ if( nMaxHeight < pActPor->Height() )
+ nMaxHeight = pActPor->Height();
+ if( nMaxAscent < pActPor->GetAscent() )
+ nMaxAscent = pActPor->GetAscent();
+
+ pActPor = pActPor->GetNextPortion();
+ if( !pActPor && !bReachCurrent )
+ {
+ pActPor = &rPortion;
+ bReachCurrent = true;
+ }
+ }
+
+ // Change all portion's height and ascent
+ pActPor = m_pFirstOfBorderMerge;
+ bReachCurrent = false;
+ while( pActPor )
+ {
+ if( nMaxHeight > pActPor->Height() )
+ pActPor->Height(nMaxHeight);
+ if( nMaxAscent > pActPor->GetAscent() )
+ pActPor->SetAscent(nMaxAscent);
+
+ pActPor = pActPor->GetNextPortion();
+ if( !pActPor && !bReachCurrent )
+ {
+ pActPor = &rPortion;
+ bReachCurrent = true;
+ }
+ }
+ m_pFirstOfBorderMerge = nullptr;
+ }
+ }
+ Seek(rInf.GetIdx());
+}
+
+namespace {
+ // calculates and sets optimal repaint offset for the current line
+ tools::Long lcl_CalcOptRepaint( SwTextFormatter &rThis,
+ SwLineLayout const &rCurr,
+ TextFrameIndex const nOldLineEnd,
+ const std::vector<tools::Long> &rFlyStarts )
+ {
+ SwTextFormatInfo& txtFormatInfo = rThis.GetInfo();
+ if ( txtFormatInfo.GetIdx() < txtFormatInfo.GetReformatStart() )
+ // the reformat position is behind our new line, that means
+ // something of our text has moved to the next line
+ return 0;
+
+ TextFrameIndex nReformat = std::min(txtFormatInfo.GetReformatStart(), nOldLineEnd);
+
+ // in case we do not have any fly in our line, our repaint position
+ // is the changed position - 1
+ if ( rFlyStarts.empty() && ! rCurr.IsFly() )
+ {
+ // this is the maximum repaint offset determined during formatting
+ // for example: the beginning of the first right tab stop
+ // if this value is 0, this means that we do not have an upper
+ // limit for the repaint offset
+ const tools::Long nFormatRepaint = txtFormatInfo.GetPaintOfst();
+
+ if (nReformat < txtFormatInfo.GetLineStart() + TextFrameIndex(3))
+ return 0;
+
+ // step back two positions for smoother repaint
+ nReformat -= TextFrameIndex(2);
+
+ // i#28795, i#34607, i#38388
+ // step back more characters, this is required by complex scripts
+ // e.g., for Khmer (thank you, Javier!)
+ static const TextFrameIndex nMaxContext(10);
+ if (nReformat > txtFormatInfo.GetLineStart() + nMaxContext)
+ nReformat = nReformat - nMaxContext;
+ else
+ {
+ nReformat = txtFormatInfo.GetLineStart();
+ //reset the margin flag - prevent loops
+ SwTextCursor::SetRightMargin(false);
+ }
+
+ // Weird situation: Our line used to end with a hole portion
+ // and we delete some characters at the end of our line. We have
+ // to take care for repainting the blanks which are not anymore
+ // covered by the hole portion
+ while ( nReformat > txtFormatInfo.GetLineStart() &&
+ CH_BLANK == txtFormatInfo.GetChar( nReformat ) )
+ --nReformat;
+
+ OSL_ENSURE( nReformat < txtFormatInfo.GetIdx(), "Reformat too small for me!" );
+ SwRect aRect;
+
+ // Note: GetChareRect is not const. It definitely changes the
+ // bMulti flag. We have to save and restore the old value.
+ bool bOldMulti = txtFormatInfo.IsMulti();
+ rThis.GetCharRect( &aRect, nReformat );
+ txtFormatInfo.SetMulti( bOldMulti );
+
+ return nFormatRepaint ? std::min( aRect.Left(), nFormatRepaint ) :
+ aRect.Left();
+ }
+ else
+ {
+ // nReformat may be wrong, if something around flys has changed:
+ // we compare the former and the new fly positions in this line
+ // if anything has changed, we carefully have to adjust the right
+ // repaint position
+ tools::Long nPOfst = 0;
+ size_t nCnt = 0;
+ tools::Long nX = 0;
+ TextFrameIndex nIdx = rThis.GetInfo().GetLineStart();
+ SwLinePortion* pPor = rCurr.GetFirstPortion();
+
+ while ( pPor )
+ {
+ if ( pPor->IsFlyPortion() )
+ {
+ // compare start of fly with former start of fly
+ if (nCnt < rFlyStarts.size() &&
+ nX == rFlyStarts[ nCnt ] &&
+ nIdx < nReformat
+ )
+ // found fix position, nothing has changed left from nX
+ nPOfst = nX + pPor->Width();
+ else
+ break;
+
+ nCnt++;
+ }
+ nX = nX + pPor->Width();
+ nIdx = nIdx + pPor->GetLen();
+ pPor = pPor->GetNextPortion();
+ }
+
+ return nPOfst + rThis.GetLeftMargin();
+ }
+ }
+
+ // Determine if we need to build hidden portions
+ bool lcl_BuildHiddenPortion(const SwTextSizeInfo& rInf, TextFrameIndex & rPos)
+ {
+ // Only if hidden text should not be shown:
+ // if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() )
+ const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar();
+ const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( true ) && rInf.GetOpt().IsPrinting();
+ if (bShowInDocView || bShowForPrinting)
+ return false;
+
+ const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
+ TextFrameIndex nHiddenStart;
+ TextFrameIndex nHiddenEnd;
+ rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd );
+ if ( nHiddenEnd )
+ {
+ rPos = nHiddenEnd;
+ return true;
+ }
+
+ return false;
+ }
+
+ bool lcl_HasSameBorder(const SwFont& rFirst, const SwFont& rSecond)
+ {
+ return
+ rFirst.GetTopBorder() == rSecond.GetTopBorder() &&
+ rFirst.GetBottomBorder() == rSecond.GetBottomBorder() &&
+ rFirst.GetLeftBorder() == rSecond.GetLeftBorder() &&
+ rFirst.GetRightBorder() == rSecond.GetRightBorder() &&
+ rFirst.GetTopBorderDist() == rSecond.GetTopBorderDist() &&
+ rFirst.GetBottomBorderDist() == rSecond.GetBottomBorderDist() &&
+ rFirst.GetLeftBorderDist() == rSecond.GetLeftBorderDist() &&
+ rFirst.GetRightBorderDist() == rSecond.GetRightBorderDist() &&
+ rFirst.GetOrientation() == rSecond.GetOrientation() &&
+ rFirst.GetShadowColor() == rSecond.GetShadowColor() &&
+ rFirst.GetShadowWidth() == rSecond.GetShadowWidth() &&
+ rFirst.GetShadowLocation() == rSecond.GetShadowLocation();
+ }
+
+} //end unnamed namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrform2.hxx b/sw/source/core/text/itrform2.hxx
new file mode 100644
index 000000000..2d71fad34
--- /dev/null
+++ b/sw/source/core/text/itrform2.hxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include "itrpaint.hxx"
+
+class SwFlyCntPortion;
+class SwDropPortion;
+class SwFormatDrop;
+class SwTextAttr;
+class SwNumberPortion;
+class SwErgoSumPortion;
+class SwExpandPortion;
+class SwMultiPortion;
+class SwFootnotePortion;
+
+class SwTextFormatter : public SwTextPainter
+{
+ const SwFormatDrop *m_pDropFormat;
+ SwMultiPortion* m_pMulti; // during formatting a multi-portion
+ sal_uInt8 m_nContentEndHyph; // Counts consecutive hyphens at the line end
+ sal_uInt8 m_nContentMidHyph; // Counts consecutive hyphens before flies
+ TextFrameIndex m_nLeftScanIdx; // for increasing performance during
+ TextFrameIndex m_nRightScanIdx; // scanning for portion ends
+ bool m_bOnceMore : 1; // Another round?
+ bool m_bFlyInContentBase : 1; // Base reference that sets a character-bound frame
+ bool m_bTruncLines : 1; // Flag for extending the repaint rect, if needed
+ bool m_bUnclipped : 1; // Flag whether repaint is larger than the fixed line height
+ std::unique_ptr<sw::MergedAttrIterByEnd> m_pByEndIter; // HACK for TryNewNoLengthPortion
+ SwLinePortion* m_pFirstOfBorderMerge; // The first text portion of a joined border (during portion building)
+
+ SwLinePortion *NewPortion(SwTextFormatInfo &rInf, ::std::optional<TextFrameIndex>);
+ SwTextPortion *NewTextPortion( SwTextFormatInfo &rInf );
+ SwLinePortion *NewExtraPortion( SwTextFormatInfo &rInf );
+ SwTabPortion *NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const;
+ SwNumberPortion *NewNumberPortion( SwTextFormatInfo &rInf ) const;
+ SwDropPortion *NewDropPortion( SwTextFormatInfo &rInf );
+ SwNumberPortion *NewFootnoteNumPortion( SwTextFormatInfo const &rInf ) const;
+ SwErgoSumPortion *NewErgoSumPortion( SwTextFormatInfo const &rInf ) const;
+ SwExpandPortion *NewFieldPortion( SwTextFormatInfo &rInf,
+ const SwTextAttr *pHt ) const;
+ SwFootnotePortion *NewFootnotePortion( SwTextFormatInfo &rInf, SwTextAttr *pHt );
+
+ /**
+ Sets a new portion for an object anchored as character
+ */
+ SwFlyCntPortion *NewFlyCntPortion( SwTextFormatInfo &rInf,
+ SwTextAttr *pHt ) const;
+ SwLinePortion *WhichFirstPortion( SwTextFormatInfo &rInf );
+ SwTextPortion *WhichTextPor( SwTextFormatInfo &rInf ) const;
+ SwExpandPortion * TryNewNoLengthPortion( SwTextFormatInfo const & rInfo );
+
+ // The center piece of formatting
+ void BuildPortions( SwTextFormatInfo &rInf );
+
+ bool BuildMultiPortion( SwTextFormatInfo &rInf, SwMultiPortion& rMulti );
+
+ /**
+ Calculation of the emulated right side.
+
+ Determines the next object, that reaches into the rest of the line and
+ constructs the appropriate FlyPortion.
+ SwTextFly::GetFrame(const SwRect&, bool) will be needed for this.
+
+ The right edge can be shortened by flys
+ */
+ void CalcFlyWidth( SwTextFormatInfo &rInf );
+
+ // Is overloaded by SwTextFormatter because of UpdatePos
+ void CalcAdjustLine( SwLineLayout *pCurr );
+
+ // considers line spacing attributes
+ void CalcRealHeight( bool bNewLine = false );
+
+ // Transfers the data to rInf
+ void FeedInf( SwTextFormatInfo &rInf ) const;
+
+ // Treats underflow situations
+ SwLinePortion *Underflow( SwTextFormatInfo &rInf );
+
+ // Calculates the ascent and the height from the fontmetric
+ void CalcAscent( SwTextFormatInfo &rInf, SwLinePortion *pPor );
+
+ // determines, if an optimized repaint rectangle is allowed
+ bool AllowRepaintOpt() const;
+
+ // Is called by FormatLine
+ void FormatReset( SwTextFormatInfo &rInf );
+
+ /**
+ The position of the portions changes with the adjustment.
+
+ This method updates the reference point of the anchored as character objects,
+ for example after adjustment change (right alignment, justified, etc.)
+ Mainly to correct the X position.
+ */
+ void UpdatePos(SwLineLayout *pCurr, Point aStart, TextFrameIndex nStartIdx,
+ bool bAlways = false ) const;
+
+ /**
+ Set all anchored as character objects to the passed BaseLine
+ (in Y direction).
+ */
+ void AlignFlyInCntBase( tools::Long nBaseLine ) const;
+
+ /**
+ This is called after the real height of the line has been calculated
+ Therefore it is possible, that more flys from below intersect with the
+ line, or that flys from above do not intersect with the line anymore.
+ We check this and return true, meaning that the line has to be
+ formatted again.
+ */
+ bool ChkFlyUnderflow( SwTextFormatInfo &rInf ) const;
+
+ // Insert portion
+ void InsertPortion( SwTextFormatInfo &rInf, SwLinePortion *pPor );
+
+ // Guess height for the DropPortion
+ void GuessDropHeight( const sal_uInt16 nLines );
+
+public:
+ // Calculate the height for the DropPortion
+ void CalcDropHeight( const sal_uInt16 nLines );
+
+ // Calculates the paragraphs bottom, takes anchored objects within it into
+ // account which have a wrap setting of "wrap at 1st paragraph"
+ SwTwips CalcBottomLine() const;
+
+ // Takes character-bound objects into account when calculating the
+ // repaint rect in lines with fixed line height
+ void CalcUnclipped( SwTwips& rTop, SwTwips& rBottom );
+
+ // Amongst others for DropCaps
+ bool CalcOnceMore();
+
+ void CtorInitTextFormatter( SwTextFrame *pFrame, SwTextFormatInfo *pInf );
+ SwTextFormatter(SwTextFrame *pTextFrame, SwTextFormatInfo *pTextFormatInf)
+ : SwTextPainter(pTextFrame->GetTextNodeFirst())
+ , m_bUnclipped(false)
+ {
+ CtorInitTextFormatter( pTextFrame, pTextFormatInf );
+ }
+ virtual ~SwTextFormatter() override;
+
+ TextFrameIndex FormatLine(TextFrameIndex nStart);
+
+ void RecalcRealHeight();
+
+ // We format a line for interactive hyphenation
+ bool Hyphenate(SwInterHyphInfoTextFrame & rInf);
+
+ // A special method for QuoVadis texts:
+ // nErgo is the page number of the ErgoSum Footnote
+ // At 0 it's still unclear
+ TextFrameIndex FormatQuoVadis(TextFrameIndex nStart);
+
+ // The emergency break: Cancel formatting, discard line
+ bool IsStop() const { return GetInfo().IsStop(); }
+
+ // The counterpart: Continue formatting at all costs
+ bool IsNewLine() const { return GetInfo().IsNewLine(); }
+
+ // FormatQuick(); Refresh formatting information
+ bool IsQuick() const { return GetInfo().IsQuick(); }
+
+ // Create a SwLineLayout if needed, which avoids Footnote/Fly to oscillate
+ void MakeDummyLine();
+
+ // SwTextIter functionality
+ void Insert( SwLineLayout *pLine );
+
+ // The remaining height to the page border
+ sal_uInt16 GetFrameRstHeight() const;
+
+ // How wide would you be without any bounds (Flys etc.)?
+ SwTwips CalcFitToContent_( );
+
+ SwLinePortion* MakeRestPortion(const SwLineLayout* pLine, TextFrameIndex nPos);
+
+ const SwFormatDrop *GetDropFormat() const { return m_pDropFormat; }
+ void ClearDropFormat() { m_pDropFormat = nullptr; }
+
+ SwMultiPortion *GetMulti() const { return m_pMulti; }
+
+ bool IsOnceMore() const { return m_bOnceMore; }
+ void SetOnceMore( bool bNew ) { m_bOnceMore = bNew; }
+
+ bool HasTruncLines() const { return m_bTruncLines; }
+ void SetTruncLines( bool bNew ) { m_bTruncLines = bNew; }
+
+ bool IsUnclipped() const { return m_bUnclipped; }
+ void SetUnclipped( bool bNew ) { m_bUnclipped = bNew; }
+
+ bool IsFlyInCntBase() const { return m_bFlyInContentBase; }
+ void SetFlyInCntBase( bool bNew = true ) { m_bFlyInContentBase = bNew; }
+
+ SwTextFormatInfo &GetInfo()
+ { return static_cast<SwTextFormatInfo&>(SwTextIter::GetInfo()); }
+ const SwTextFormatInfo &GetInfo() const
+ { return static_cast<const SwTextFormatInfo&>(SwTextIter::GetInfo()); }
+
+ void InitCntHyph() { CntHyphens( m_nContentEndHyph, m_nContentMidHyph ); }
+ const sal_uInt8 &CntEndHyph() const { return m_nContentEndHyph; }
+ const sal_uInt8 &CntMidHyph() const { return m_nContentMidHyph; }
+ sal_uInt8 &CntEndHyph() { return m_nContentEndHyph; }
+ sal_uInt8 &CntMidHyph() { return m_nContentMidHyph; }
+
+ /**
+ * Merge border of the drop portion with modifying the font of
+ * the portions' part. Removing left or right border.
+ * @param rPortion drop portion for merge
+ **/
+ static void MergeCharacterBorder( SwDropPortion const & rPortion );
+
+ /**
+ * Merge border of the line portion with setting the portion's
+ * m_bJoinBorderWidthNext and m_bJoinBorderWidthPrev members and
+ * changing the size (width, height and ascent) of the portion
+ * to get a merged border.
+ * @param rPortion portion for merge
+ * @param pPrev portion immediately before rPortion
+ * @param rInf contain information
+ **/
+ void MergeCharacterBorder( SwLinePortion& rPortion, SwLinePortion const *pPrev, SwTextFormatInfo& rInf );
+
+ bool ClearIfIsFirstOfBorderMerge(SwLinePortion const *pPortion);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrpaint.cxx b/sw/source/core/text/itrpaint.cxx
new file mode 100644
index 000000000..393563019
--- /dev/null
+++ b/sw/source/core/text/itrpaint.cxx
@@ -0,0 +1,713 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <viewopt.hxx>
+#include <tools/multisel.hxx>
+#include <editeng/udlnitem.hxx>
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+
+#include <EnhancedPDFExportHelper.hxx>
+#include <IDocumentSettingAccess.hxx>
+
+#include <viewsh.hxx>
+#include "itrpaint.hxx"
+#include <txtfrm.hxx>
+#include <swfont.hxx>
+#include "txtpaint.hxx"
+#include "portab.hxx"
+#include <txatbase.hxx>
+#include <charfmt.hxx>
+#include "redlnitr.hxx"
+#include "porrst.hxx"
+#include "pormulti.hxx"
+#include <doc.hxx>
+
+// Returns, if we have an underline breaking situation
+// Adding some more conditions here means you also have to change them
+// in SwTextPainter::CheckSpecialUnderline
+bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt )
+{
+ return LINESTYLE_NONE == rFnt.GetUnderline() ||
+ rPor.IsFlyPortion() || rPor.IsFlyCntPortion() ||
+ rPor.IsBreakPortion() || rPor.IsMarginPortion() ||
+ rPor.IsHolePortion() ||
+ ( rPor.IsMultiPortion() && ! static_cast<const SwMultiPortion&>(rPor).IsBidi() ) ||
+ rFnt.GetEscapement() < 0 || rFnt.IsWordLineMode() ||
+ SvxCaseMap::SmallCaps == rFnt.GetCaseMap();
+}
+
+static Color GetUnderColor( const SwFont *pFont )
+{
+ return pFont->GetUnderColor() == COL_AUTO ?
+ pFont->GetColor() : pFont->GetUnderColor();
+}
+
+void SwTextPainter::CtorInitTextPainter( SwTextFrame *pNewFrame, SwTextPaintInfo *pNewInf )
+{
+ CtorInitTextCursor( pNewFrame, pNewInf );
+ m_pInf = pNewInf;
+ SwFont *pMyFnt = GetFnt();
+ GetInfo().SetFont( pMyFnt );
+ m_bPaintDrop = false;
+}
+
+SwLinePortion *SwTextPainter::CalcPaintOfst( const SwRect &rPaint )
+{
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ GetInfo().SetPaintOfst( 0 );
+ SwTwips nPaintOfst = rPaint.Left();
+
+ // nPaintOfst was exactly set to the end, therefore <=
+ // nPaintOfst is document global, therefore add up nLeftMar
+ // const sal_uInt16 nLeftMar = sal_uInt16(GetLeftMargin());
+ // 8310: paint of LineBreaks in empty lines.
+ if( nPaintOfst && m_pCurr->Width() )
+ {
+ SwLinePortion *pLast = nullptr;
+ // 7529 and 4757: not <= nPaintOfst
+ while( pPor && GetInfo().X() + pPor->Width() + (pPor->Height()/2)
+ < nPaintOfst )
+ {
+ if( pPor->InSpaceGrp() && GetInfo().GetSpaceAdd() )
+ {
+ tools::Long nTmp = GetInfo().X() +pPor->Width() +
+ pPor->CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
+ if( nTmp + (pPor->Height()/2) >= nPaintOfst )
+ break;
+ GetInfo().X( nTmp );
+ GetInfo().SetIdx( GetInfo().GetIdx() + pPor->GetLen() );
+ }
+ else
+ pPor->Move( GetInfo() );
+ pLast = pPor;
+ pPor = pPor->GetNextPortion();
+ }
+
+ // 7529: if PostIts return also pLast.
+ if( pLast && !pLast->Width() && pLast->IsPostItsPortion() )
+ {
+ pPor = pLast;
+ GetInfo().SetIdx( GetInfo().GetIdx() - pPor->GetLen() );
+ }
+ }
+ return pPor;
+}
+
+// There are two possibilities to output transparent font:
+// 1) DrawRect on the whole line and DrawText afterwards
+// (objectively fast, subjectively slow)
+// 2) For every portion a DrawRect with subsequent DrawText is done
+// (objectively slow, subjectively fast)
+// Since the user usually judges subjectively the second method is set as default.
+void SwTextPainter::DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
+ const bool bUnderSized )
+{
+#if OSL_DEBUG_LEVEL > 1
+// sal_uInt16 nFntHeight = GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), GetInfo().GetOut() );
+// sal_uInt16 nFntAscent = GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), GetInfo().GetOut() );
+#endif
+
+ // maybe catch-up adjustment
+ GetAdjusted();
+ GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
+ GetInfo().ResetSpaceIdx();
+ GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
+ GetInfo().ResetKanaIdx();
+ // The size of the frame
+ GetInfo().SetIdx( GetStart() );
+ GetInfo().SetPos( GetTopLeft() );
+
+ const bool bDrawInWindow = GetInfo().OnWin();
+
+ // 6882: blank lines can't be optimized by removing them if Formatting Marks are shown
+ const bool bEndPor = GetInfo().GetOpt().IsParagraph() && GetInfo().GetText().isEmpty();
+
+ SwLinePortion *pPor = bEndPor ? m_pCurr->GetFirstPortion() : CalcPaintOfst( rPaint );
+
+ // Optimization!
+ SwTwips nMaxRight = std::min<SwTwips>( rPaint.Right(), Right() );
+ const SwTwips nTmpLeft = GetInfo().X();
+ //compatibility setting: allow tabstop text to exceed right margin
+ if (GetInfo().GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN))
+ {
+ SwLinePortion* pPorIter = pPor;
+ while( pPorIter )
+ {
+ if( pPorIter->InTabGrp() )
+ {
+ const SwTabPortion* pTabPor = static_cast<SwTabPortion*>(pPorIter);
+ const SwTwips nTabPos = nTmpLeft + pTabPor->GetTabPos();
+ if( nMaxRight < nTabPos )
+ {
+ nMaxRight = rPaint.Right();
+ break;
+ }
+ }
+ pPorIter = pPorIter->GetNextPortion();
+ }
+ }
+ if( !bEndPor && nTmpLeft >= nMaxRight )
+ return;
+
+ // DropCaps!
+ // 7538: of course for the printer, too
+ if( !m_bPaintDrop )
+ {
+ // 8084: Optimization, less painting
+ // AMA: By 8084 7538 has been revived
+ // bDrawInWindow removed, so that DropCaps also can be printed
+ m_bPaintDrop = pPor == m_pCurr->GetFirstPortion()
+ && GetDropLines() >= GetLineNr();
+ }
+
+ SwTwips nTmpHeight, nTmpAscent;
+ CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+
+ // bClip decides if there's a need to clip
+ // The whole thing must be done before retouching
+
+ bool bClip = ( bDrawInWindow || bUnderSized ) && !rClip.IsChg();
+ if( bClip && pPor )
+ {
+ // If TopLeft or BottomLeft of the line are outside, the we must clip.
+ // The check for Right() is done in the output loop ...
+
+ if( GetInfo().GetPos().X() < rPaint.Left() ||
+ GetInfo().GetPos().Y() < rPaint.Top() ||
+ GetInfo().GetPos().Y() + nTmpHeight > rPaint.Top() + rPaint.Height() )
+ {
+ bClip = false;
+ rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
+ }
+#if OSL_DEBUG_LEVEL > 1
+ static bool bClipAlways = false;
+ if( bClip && bClipAlways )
+ { bClip = false;
+ rClip.ChgClip( rPaint );
+ }
+#endif
+ }
+
+ // Alignment
+ OutputDevice* pOut = GetInfo().GetOut();
+ Point aPnt1( nTmpLeft, GetInfo().GetPos().Y() );
+ if ( aPnt1.X() < rPaint.Left() )
+ aPnt1.setX( rPaint.Left() );
+ if ( aPnt1.Y() < rPaint.Top() )
+ aPnt1.setY( rPaint.Top() );
+ Point aPnt2( GetInfo().GetPos().X() + nMaxRight - GetInfo().X(),
+ GetInfo().GetPos().Y() + nTmpHeight );
+ if ( aPnt2.X() > rPaint.Right() )
+ aPnt2.setX( rPaint.Right() );
+ if ( aPnt2.Y() > rPaint.Bottom() )
+ aPnt2.setY( rPaint.Bottom() );
+
+ const SwRect aLineRect( aPnt1, aPnt2 );
+
+ if( m_pCurr->IsClipping() )
+ {
+ const SwTextFrame& rFrame = *GetInfo().GetTextFrame();
+ // tdf#117448 at small fixed line height, enlarge clipping area in table cells
+ // to show previously clipped text content on the area of paragraph margins
+ if ( rFrame.IsInTab() )
+ rClip.ChgClip( aLineRect, m_pFrame, false, rFrame.GetTopMargin(), rFrame.GetBottomMargin() );
+ else
+ rClip.ChgClip( aLineRect, m_pFrame );
+ bClip = false;
+ }
+
+ if( !pPor && !bEndPor )
+ return;
+
+ // Baseline output also if non-TextPortion (compare TabPor with Fill)
+ // if no special vertical alignment is used,
+ // we calculate Y value for the whole line
+ SwTextGridItem const*const pGrid(GetGridItem(GetTextFrame()->FindPageFrame()));
+ const bool bAdjustBaseLine =
+ GetLineInfo().HasSpecialAlign( GetTextFrame()->IsVertical() ) ||
+ ( nullptr != pGrid ) || m_pCurr->GetHangingBaseline();
+ const SwTwips nLineBaseLine = GetInfo().GetPos().Y() + nTmpAscent;
+ if ( ! bAdjustBaseLine )
+ GetInfo().Y( nLineBaseLine );
+
+ // 7529: Pre-paint post-its
+ if( GetInfo().OnWin() && pPor && !pPor->Width() )
+ {
+ SeekAndChg( GetInfo() );
+
+ if( bAdjustBaseLine )
+ {
+ const SwTwips nOldY = GetInfo().Y();
+
+ GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, nullptr,
+ GetInfo().GetFont()->GetHeight( GetInfo().GetVsh(), *pOut ),
+ GetInfo().GetFont()->GetAscent( GetInfo().GetVsh(), *pOut )
+ ) );
+
+ pPor->PrePaint( GetInfo(), pPor );
+ GetInfo().Y( nOldY );
+ }
+ else
+ pPor->PrePaint( GetInfo(), pPor );
+ }
+
+ // 7923: EndPortions output chars, too, that's why we change the font
+ if( bEndPor )
+ SeekStartAndChg( GetInfo() );
+
+ const bool bRest = m_pCurr->IsRest();
+ bool bFirst = true;
+
+ SwArrowPortion *pArrow = nullptr;
+ // Reference portion for the paragraph end portion
+ SwLinePortion* pEndTempl = m_pCurr->GetFirstPortion();
+
+ while( pPor )
+ {
+ bool bSeeked = true;
+ GetInfo().SetLen( pPor->GetLen() );
+
+ const SwTwips nOldY = GetInfo().Y();
+
+ if ( bAdjustBaseLine )
+ {
+ GetInfo().Y( GetInfo().GetPos().Y() + AdjustBaseLine( *m_pCurr, pPor ) );
+
+ // we store the last portion, because a possible paragraph
+ // end character has the same font as this portion
+ // (only in special vertical alignment case, otherwise the first
+ // portion of the line is used)
+ if ( pPor->Width() && pPor->InTextGrp() )
+ pEndTempl = pPor;
+ }
+
+ // set redlining for line break symbol
+ if ( pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() && GetRedln() )
+ {
+ SeekAndChg( GetInfo() );
+ if ( m_pCurr->GetRedlineEndType() != RedlineType::None )
+ static_cast<SwBreakPortion&>(*pPor).SetRedline( m_pCurr->GetRedlineEndType() );
+ }
+
+ // A special case are GluePortions which output blanks.
+
+ // 6168: Avoid that the rest of a FieldPortion gets the attributes of the
+ // next portion with SeekAndChgBefore():
+ if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
+ SeekAndChgBefore( GetInfo() );
+ else if ( pPor->IsQuoVadisPortion() )
+ {
+ // A remark on QuoVadis/ErgoSum:
+ // We use the Font set for the Paragraph for these portions.
+ // Thus, we initialize:
+ TextFrameIndex nOffset = GetInfo().GetIdx();
+ SeekStartAndChg( GetInfo(), true );
+ if( GetRedln() && m_pCurr->HasRedline() )
+ {
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ GetTextFrame()->MapViewToModel(nOffset));
+ GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
+ }
+ }
+ else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
+ SeekAndChg( GetInfo() );
+ else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
+ {
+ // Paragraph symbols should have the same font as the paragraph in front of them,
+ // except for the case that there's redlining in the paragraph
+ if( GetRedln() )
+ SeekAndChg( GetInfo() );
+ else
+ SeekAndChgBefore( GetInfo() );
+ }
+ else
+ bSeeked = false;
+
+ // bRest = false;
+
+ // If the end of the portion juts out, it is clipped.
+ // A safety distance of half the height is added, so that
+ // TTF-"f" isn't overlapping into the page margin.
+ if( bClip &&
+ GetInfo().X() + pPor->Width() + ( pPor->Height() / 2 ) > nMaxRight )
+ {
+ bClip = false;
+ rClip.ChgClip( rPaint, m_pFrame, m_pCurr->HasUnderscore() );
+ }
+
+ // Portions, which lay "below" the text like post-its
+ SwLinePortion *pNext = pPor->GetNextPortion();
+ if( GetInfo().OnWin() && pNext && !pNext->Width() )
+ {
+ // Fix 11289: Fields were omitted here because of Last!=Owner during
+ // loading Brief.sdw. Now the fields are allowed again,
+ // by bSeeked Last!=Owner is being avoided.
+ if ( !bSeeked )
+ SeekAndChg( GetInfo() );
+ pNext->PrePaint( GetInfo(), pPor );
+ }
+
+ // We calculate a separate font for underlining.
+ CheckSpecialUnderline( pPor, bAdjustBaseLine ? nOldY : 0 );
+ SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
+ if ( pUnderLineFnt )
+ {
+ const Point aTmpPoint( GetInfo().X(),
+ bAdjustBaseLine ?
+ pUnderLineFnt->GetPos().Y() :
+ nLineBaseLine );
+ pUnderLineFnt->SetPos( aTmpPoint );
+ }
+
+ // in extended input mode we do not want a common underline font.
+ SwUnderlineFont* pOldUnderLineFnt = nullptr;
+ if ( GetRedln() && GetRedln()->ExtOn() )
+ {
+ pOldUnderLineFnt = GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( nullptr );
+ }
+
+ {
+ // #i16816# tagged pdf support
+ Por_Info aPorInfo( *pPor, *this );
+ SwTaggedPDFHelper aTaggedPDFHelper( nullptr, nullptr, &aPorInfo, *pOut );
+
+ if( pPor->IsMultiPortion() )
+ PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor) );
+ else
+ pPor->Paint( GetInfo() );
+ }
+
+ // reset underline font
+ if ( pOldUnderLineFnt )
+ GetInfo().SetUnderFnt( pOldUnderLineFnt );
+
+ // reset (for special vertical alignment)
+ GetInfo().Y( nOldY );
+
+ bFirst &= !pPor->GetLen();
+ if( pNext || !pPor->IsMarginPortion() )
+ pPor->Move( GetInfo() );
+ if( pPor->IsArrowPortion() && GetInfo().OnWin() && !pArrow )
+ pArrow = static_cast<SwArrowPortion*>(pPor);
+
+ pPor = bDrawInWindow || GetInfo().X() <= nMaxRight ||
+ // #i16816# tagged pdf support
+ ( GetInfo().GetVsh() &&
+ GetInfo().GetVsh()->GetViewOptions()->IsPDFExport() &&
+ pNext && pNext->IsHolePortion() ) ?
+ pNext :
+ nullptr;
+ }
+
+ // delete underline font
+ delete GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( nullptr );
+
+ // paint remaining stuff
+ if( bDrawInWindow )
+ {
+ // If special vertical alignment is enabled, GetInfo().Y() is the
+ // top of the current line. Therefore is has to be adjusted for
+ // the painting of the remaining stuff. We first store the old value.
+ const SwTwips nOldY = GetInfo().Y();
+
+ if( !GetNextLine() &&
+ GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() &&
+ GetInfo().GetOpt().IsParagraph() && !GetTextFrame()->GetFollow() &&
+ GetInfo().GetIdx() >= TextFrameIndex(GetInfo().GetText().getLength()))
+ {
+ bool bHasRedlineEnd( GetRedln() && m_pCurr->HasRedlineEnd() );
+ RedlineType eRedlineEnd = bHasRedlineEnd ? m_pCurr->GetRedlineEndType() : RedlineType::None;
+ if( bHasRedlineEnd )
+ {
+ TextFrameIndex nOffset = GetInfo().GetIdx();
+ SeekStartAndChg( GetInfo(), true );
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ GetTextFrame()->MapViewToModel(nOffset));
+ GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
+ }
+ const SwTmpEndPortion aEnd( *pEndTempl,
+ bHasRedlineEnd && eRedlineEnd != RedlineType::Delete ? m_pFont->GetUnderline() : LINESTYLE_NONE,
+ bHasRedlineEnd && eRedlineEnd == RedlineType::Delete ? m_pFont->GetStrikeout() : STRIKEOUT_NONE,
+ bHasRedlineEnd ? m_pFont->GetColor() : COL_AUTO );
+ GetFnt()->ChgPhysFnt( GetInfo().GetVsh(), *pOut );
+
+ if ( bAdjustBaseLine )
+ GetInfo().Y( GetInfo().GetPos().Y()
+ + AdjustBaseLine( *m_pCurr, &aEnd ) );
+ GetInfo().X( GetInfo().X() +
+ ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 ) );
+ aEnd.Paint( GetInfo() );
+ GetInfo().Y( nOldY );
+ }
+ if( GetInfo().GetVsh() && !GetInfo().GetVsh()->IsPreview() )
+ {
+ const bool bNextUndersized =
+ ( GetTextFrame()->GetNext() &&
+ 0 == GetTextFrame()->GetNext()->getFramePrintArea().Height() &&
+ GetTextFrame()->GetNext()->IsTextFrame() &&
+ static_cast<SwTextFrame*>(GetTextFrame()->GetNext())->IsUndersized() ) ;
+
+ if( bUnderSized || bNextUndersized )
+ {
+ if ( bAdjustBaseLine )
+ GetInfo().Y( GetInfo().GetPos().Y() + m_pCurr->GetAscent() );
+
+ // Left arrow (text overflowing)
+ if( pArrow )
+ GetInfo().DrawRedArrow( *pArrow );
+
+ // GetInfo().Y() must be current baseline
+ SwTwips nDiff = GetInfo().Y() + nTmpHeight - nTmpAscent - GetTextFrame()->getFrameArea().Bottom();
+ if( ( nDiff > 0 &&
+ (GetEnd() < TextFrameIndex(GetInfo().GetText().getLength()) ||
+ ( nDiff > nTmpHeight/2 && GetPrevLine() ) ) ) ||
+ (nDiff >= 0 && bNextUndersized) )
+
+ {
+ // Right arrow (text overflowing)
+ SwArrowPortion aArrow( GetInfo() );
+ GetInfo().DrawRedArrow( aArrow );
+ }
+
+ GetInfo().Y( nOldY );
+ }
+ }
+ }
+
+ if( m_pCurr->IsClipping() )
+ rClip.ChgClip( rPaint, m_pFrame );
+}
+
+void SwTextPainter::CheckSpecialUnderline( const SwLinePortion* pPor,
+ tools::Long nAdjustBaseLine )
+{
+ // Check if common underline should not be continued
+ if ( IsUnderlineBreak( *pPor, *m_pFont ) )
+ {
+ // delete underline font
+ delete GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( nullptr );
+ return;
+ }
+ // Reuse calculated underline font as much as possible.
+ if (GetInfo().GetUnderFnt() &&
+ GetInfo().GetIdx() + pPor->GetLen() <= GetInfo().GetUnderFnt()->GetEnd() + TextFrameIndex(1))
+ {
+ SwFont &rFont = GetInfo().GetUnderFnt()->GetFont();
+ const Color aColor = GetUnderColor( GetInfo().GetFont() );
+ if ( GetUnderColor( &rFont ) != aColor )
+ rFont.SetColor( aColor );
+ return;
+ }
+
+ // If current underline matches the common underline font, we continue
+ // to use the common underline font.
+ // Bug 120769:Color of underline display wrongly
+ if ( GetInfo().GetUnderFnt() &&
+ GetInfo().GetUnderFnt()->GetFont().GetUnderline() == GetFnt()->GetUnderline() &&
+ GetInfo().GetFont() && GetInfo().GetFont()->GetUnderColor() != COL_AUTO )
+ return;
+ //Bug 120769(End)
+
+ OSL_ENSURE( GetFnt() && LINESTYLE_NONE != GetFnt()->GetUnderline(),
+ "CheckSpecialUnderline without underlined font" );
+ MultiSelection aUnderMulti( Range( 0, GetInfo().GetText().getLength() ) );
+ const SwFont* pParaFnt = GetAttrHandler().GetFont();
+ if( pParaFnt && pParaFnt->GetUnderline() == GetFnt()->GetUnderline() )
+ aUnderMulti.SelectAll();
+
+ if (sw::MergedPara const*const pMerged = GetTextFrame()->GetMergedPara())
+ {
+ // first, add the paragraph properties to MultiSelection - if there are
+ // Hints too, they will override the positions if they're added later
+ sal_Int32 nTmp(0);
+ for (auto const& e : pMerged->extents)
+ {
+ if (const SvxUnderlineItem* pItem = e.pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_UNDERLINE))
+ {
+ const bool bUnderSelect(m_pFont->GetUnderline() ==
+ pItem->GetLineStyle());
+ aUnderMulti.Select(Range(nTmp, nTmp + e.nEnd - e.nStart - 1),
+ bUnderSelect);
+ }
+ nTmp += e.nEnd - e.nStart;
+ }
+ }
+
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIter iter(*GetTextFrame());
+ for (SwTextAttr const* pTextAttr = iter.NextAttr(&pNode); pTextAttr;
+ pTextAttr = iter.NextAttr(&pNode))
+ {
+ SvxUnderlineItem const*const pItem =
+ CharFormat::GetItem(*pTextAttr, RES_CHRATR_UNDERLINE);
+
+ if (pItem)
+ {
+ TextFrameIndex const nStart(
+ GetTextFrame()->MapModelToView(pNode, pTextAttr->GetStart()));
+ TextFrameIndex const nEnd(
+ GetTextFrame()->MapModelToView(pNode, *pTextAttr->End()));
+ if (nEnd > nStart)
+ {
+ const bool bUnderSelect = m_pFont->GetUnderline() == pItem->GetLineStyle();
+ aUnderMulti.Select(Range(sal_Int32(nStart), sal_Int32(nEnd) - 1),
+ bUnderSelect);
+ }
+ }
+ }
+
+ const TextFrameIndex nIndx = GetInfo().GetIdx();
+ TextFrameIndex nUnderEnd(0);
+ const size_t nCnt = aUnderMulti.GetRangeCount();
+
+ // find the underline range the current portion is contained in
+ for( size_t i = 0; i < nCnt; ++i )
+ {
+ const Range& rRange = aUnderMulti.GetRange( i );
+ if (nUnderEnd == TextFrameIndex(rRange.Min()))
+ nUnderEnd = TextFrameIndex(rRange.Max());
+ else if (nIndx >= TextFrameIndex(rRange.Min()))
+ {
+ nUnderEnd = TextFrameIndex(rRange.Max());
+ }
+ else
+ break;
+ }
+
+ if ( GetEnd() && GetEnd() <= nUnderEnd )
+ nUnderEnd = GetEnd() - TextFrameIndex(1);
+
+ // calculate the new common underline font
+ SwFont* pUnderlineFnt = nullptr;
+ Point aCommonBaseLine;
+
+ // check, if underlining is not isolated
+ if (nIndx + GetInfo().GetLen() < nUnderEnd + TextFrameIndex(1))
+ {
+ // here starts the algorithm for calculating the underline font
+ SwScriptInfo& rScriptInfo = GetInfo().GetParaPortion()->GetScriptInfo();
+ SwAttrIter aIter(*GetInfo().GetTextFrame()->GetTextNodeFirst(),
+ rScriptInfo, GetTextFrame());
+
+ TextFrameIndex nTmpIdx = nIndx;
+ sal_uLong nSumWidth = 0;
+ sal_uLong nSumHeight = 0;
+ sal_uLong nBold = 0;
+ sal_uInt16 nMaxBaseLineOfst = 0;
+ int nNumberOfPortions = 0;
+
+ while (nTmpIdx <= nUnderEnd && pPor)
+ {
+ if ( pPor->IsFlyPortion() || pPor->IsFlyCntPortion() ||
+ pPor->IsBreakPortion() || pPor->IsMarginPortion() ||
+ pPor->IsHolePortion() ||
+ ( pPor->IsMultiPortion() && ! static_cast<const SwMultiPortion*>(pPor)->IsBidi() ) )
+ break;
+
+ aIter.Seek( nTmpIdx );
+ if ( aIter.GetFnt()->GetEscapement() < 0 || m_pFont->IsWordLineMode() ||
+ SvxCaseMap::SmallCaps == m_pFont->GetCaseMap() )
+ break;
+
+ if ( !aIter.GetFnt()->GetEscapement() )
+ {
+ nSumWidth += pPor->Width();
+ const sal_uLong nFontHeight = aIter.GetFnt()->GetHeight();
+
+ // If we do not have a common baseline we take the baseline
+ // and the font of the lowest portion.
+ if ( nAdjustBaseLine )
+ {
+ const sal_uInt16 nTmpBaseLineOfst = AdjustBaseLine( *m_pCurr, pPor );
+ if ( nMaxBaseLineOfst < nTmpBaseLineOfst )
+ {
+ nMaxBaseLineOfst = nTmpBaseLineOfst;
+ nSumHeight = nFontHeight;
+ }
+ }
+ // in horizontal layout we build a weighted sum of the heights
+ else
+ nSumHeight += pPor->Width() * nFontHeight;
+
+ if ( WEIGHT_NORMAL != aIter.GetFnt()->GetWeight() )
+ nBold += pPor->Width();
+ }
+
+ ++nNumberOfPortions;
+
+ nTmpIdx += pPor->GetLen();
+ pPor = pPor->GetNextPortion();
+ }
+
+ // resulting height
+ if ( nNumberOfPortions > 1 && nSumWidth )
+ {
+ const sal_uLong nNewFontHeight = nAdjustBaseLine ?
+ nSumHeight :
+ nSumHeight / nSumWidth;
+
+ pUnderlineFnt = new SwFont( *GetInfo().GetFont() );
+
+ // font height
+ const SwFontScript nActual = pUnderlineFnt->GetActual();
+ pUnderlineFnt->SetSize( Size( pUnderlineFnt->GetSize( nActual ).Width(),
+ nNewFontHeight ), nActual );
+
+ // font weight
+ if ( 2 * nBold > nSumWidth )
+ pUnderlineFnt->SetWeight( WEIGHT_BOLD, nActual );
+ else
+ pUnderlineFnt->SetWeight( WEIGHT_NORMAL, nActual );
+
+ // common base line
+ aCommonBaseLine.setY( nAdjustBaseLine + nMaxBaseLineOfst );
+ }
+ }
+
+ // an escaped redlined portion should also have a special underlining
+ if( ! pUnderlineFnt && m_pFont->GetEscapement() > 0 && GetRedln() &&
+ GetRedln()->ChkSpecialUnderline() )
+ pUnderlineFnt = new SwFont( *m_pFont );
+
+ delete GetInfo().GetUnderFnt();
+
+ if ( pUnderlineFnt )
+ {
+ pUnderlineFnt->SetProportion( 100 );
+ pUnderlineFnt->SetEscapement( 0 );
+ pUnderlineFnt->SetStrikeout( STRIKEOUT_NONE );
+ pUnderlineFnt->SetOverline( LINESTYLE_NONE );
+ const Color aFillColor( COL_TRANSPARENT );
+ pUnderlineFnt->SetFillColor( aFillColor );
+
+ GetInfo().SetUnderFnt( new SwUnderlineFont( *pUnderlineFnt, nUnderEnd,
+ aCommonBaseLine ) );
+ }
+ else
+ // I'm sorry, we do not have a special underlining font for you.
+ GetInfo().SetUnderFnt( nullptr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrpaint.hxx b/sw/source/core/text/itrpaint.hxx
new file mode 100644
index 000000000..c016cff4e
--- /dev/null
+++ b/sw/source/core/text/itrpaint.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "itrtxt.hxx"
+
+class SwSaveClip; // SwTextPainter
+class SwMultiPortion;
+
+class SwTextPainter : public SwTextCursor
+{
+ bool m_bPaintDrop;
+
+ SwLinePortion *CalcPaintOfst( const SwRect &rPaint );
+ void CheckSpecialUnderline( const SwLinePortion* pPor,
+ tools::Long nAdjustBaseLine = 0 );
+protected:
+ void CtorInitTextPainter( SwTextFrame *pFrame, SwTextPaintInfo *pInf );
+ explicit SwTextPainter(SwTextNode const * pTextNode)
+ : SwTextCursor(pTextNode)
+ , m_bPaintDrop(false)
+ {
+ }
+
+public:
+ SwTextPainter(SwTextFrame *pTextFrame, SwTextPaintInfo *pTextPaintInf)
+ : SwTextCursor(pTextFrame->GetTextNodeFirst())
+ {
+ CtorInitTextPainter( pTextFrame, pTextPaintInf );
+ }
+ void DrawTextLine( const SwRect &rPaint, SwSaveClip &rClip,
+ const bool bUnderSz );
+ void PaintDropPortion();
+ // if PaintMultiPortion is called recursively, we have to pass the
+ // surrounding SwBidiPortion
+ void PaintMultiPortion( const SwRect &rPaint, SwMultiPortion& rMulti,
+ const SwMultiPortion* pEnvPor = nullptr );
+ void SetPaintDrop( const bool bNew ) { m_bPaintDrop = bNew; }
+ bool IsPaintDrop() const { return m_bPaintDrop; }
+ SwTextPaintInfo &GetInfo()
+ { return static_cast<SwTextPaintInfo&>(SwTextIter::GetInfo()); }
+ const SwTextPaintInfo &GetInfo() const
+ { return static_cast<const SwTextPaintInfo&>(SwTextIter::GetInfo()); }
+};
+
+bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrtxt.cxx b/sw/source/core/text/itrtxt.cxx
new file mode 100644
index 000000000..1d1eed3e0
--- /dev/null
+++ b/sw/source/core/text/itrtxt.cxx
@@ -0,0 +1,465 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <ndtxt.hxx>
+#include <txatbase.hxx>
+#include <paratr.hxx>
+#include <vcl/outdev.hxx>
+#include <editeng/paravertalignitem.hxx>
+
+#include "pormulti.hxx"
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+#include "porfld.hxx"
+#include "porrst.hxx"
+
+#include "itrtxt.hxx"
+#include <txtfrm.hxx>
+
+void SwTextIter::CtorInitTextIter( SwTextFrame *pNewFrame, SwTextInfo *pNewInf )
+{
+ assert(pNewFrame->GetPara());
+
+ CtorInitAttrIter( *pNewFrame->GetTextNodeFirst(), pNewFrame->GetPara()->GetScriptInfo(), pNewFrame );
+
+ SwTextNode const*const pNode = pNewFrame->GetTextNodeForParaProps();
+
+ m_pFrame = pNewFrame;
+ m_pInf = pNewInf;
+ m_aLineInf.CtorInitLineInfo( pNode->GetSwAttrSet(), *pNode );
+ m_nFrameStart = m_pFrame->getFrameArea().Pos().Y() + m_pFrame->getFramePrintArea().Pos().Y();
+ SwTextIter::Init();
+
+ // Order is important: only execute FillRegister if GetValue!=0
+ m_bRegisterOn = pNode->GetSwAttrSet().GetRegister().GetValue()
+ && m_pFrame->FillRegister( m_nRegStart, m_nRegDiff );
+}
+
+void SwTextIter::Init()
+{
+ m_pCurr = m_pInf->GetParaPortion();
+ m_nStart = m_pInf->GetTextStart();
+ m_nY = m_nFrameStart;
+ m_bPrev = true;
+ m_pPrev = nullptr;
+ m_nLineNr = 1;
+}
+
+void SwTextIter::CalcAscentAndHeight( SwTwips &rAscent, SwTwips &rHeight ) const
+{
+ rHeight = GetLineHeight();
+ rAscent = m_pCurr->GetAscent() + rHeight - m_pCurr->Height();
+}
+
+SwLineLayout *SwTextIter::GetPrev_()
+{
+ m_pPrev = nullptr;
+ m_bPrev = true;
+ SwLineLayout *pLay = m_pInf->GetParaPortion();
+ if( m_pCurr == pLay )
+ return nullptr;
+ while( pLay->GetNext() != m_pCurr )
+ pLay = pLay->GetNext();
+ m_pPrev = pLay;
+ return m_pPrev;
+}
+
+const SwLineLayout *SwTextIter::GetPrev()
+{
+ if(! m_bPrev)
+ GetPrev_();
+ return m_pPrev;
+}
+
+const SwLineLayout *SwTextIter::Prev()
+{
+ if( !m_bPrev )
+ GetPrev_();
+ if( m_pPrev )
+ {
+ m_bPrev = false;
+ m_pCurr = m_pPrev;
+ m_nStart = m_nStart - m_pCurr->GetLen();
+ m_nY = m_nY - GetLineHeight();
+ if( !m_pCurr->IsDummy() && !(--m_nLineNr) )
+ ++m_nLineNr;
+ return m_pCurr;
+ }
+ else
+ return nullptr;
+}
+
+const SwLineLayout *SwTextIter::Next()
+{
+ if(m_pCurr->GetNext())
+ {
+ m_pPrev = m_pCurr;
+ m_bPrev = true;
+ m_nStart = m_nStart + m_pCurr->GetLen();
+ m_nY += GetLineHeight();
+ if( m_pCurr->GetLen() || ( m_nLineNr>1 && !m_pCurr->IsDummy() ) )
+ ++m_nLineNr;
+ m_pCurr = m_pCurr->GetNext();
+ return m_pCurr;
+ }
+ else
+ return nullptr;
+}
+
+const SwLineLayout *SwTextIter::NextLine()
+{
+ const SwLineLayout *pNext = Next();
+ while( pNext && pNext->IsDummy() && pNext->GetNext() )
+ {
+ pNext = Next();
+ }
+ return pNext;
+}
+
+const SwLineLayout *SwTextIter::GetNextLine() const
+{
+ const SwLineLayout *pNext = m_pCurr->GetNext();
+ while( pNext && pNext->IsDummy() && pNext->GetNext() )
+ {
+ pNext = pNext->GetNext();
+ }
+ return pNext;
+}
+
+const SwLineLayout *SwTextIter::GetPrevLine()
+{
+ const SwLineLayout *pRoot = m_pInf->GetParaPortion();
+ if( pRoot == m_pCurr )
+ return nullptr;
+ const SwLineLayout *pLay = pRoot;
+
+ while( pLay->GetNext() != m_pCurr )
+ pLay = pLay->GetNext();
+
+ if( pLay->IsDummy() )
+ {
+ const SwLineLayout *pTmp = pRoot;
+ pLay = pRoot->IsDummy() ? nullptr : pRoot;
+ while( pTmp->GetNext() != m_pCurr )
+ {
+ if( !pTmp->IsDummy() )
+ pLay = pTmp;
+ pTmp = pTmp->GetNext();
+ }
+ }
+
+ // If nothing has changed, then there are only dummy's
+ return pLay;
+}
+
+const SwLineLayout *SwTextIter::PrevLine()
+{
+ const SwLineLayout *pMyPrev = Prev();
+ if( !pMyPrev )
+ return nullptr;
+
+ const SwLineLayout *pLast = pMyPrev;
+ while( pMyPrev && pMyPrev->IsDummy() )
+ {
+ pLast = pMyPrev;
+ pMyPrev = Prev();
+ }
+ return pMyPrev ? pMyPrev : pLast;
+}
+
+void SwTextIter::Bottom()
+{
+ while( Next() )
+ {
+ // nothing
+ }
+}
+
+void SwTextIter::CharToLine(TextFrameIndex const nChar)
+{
+ while( m_nStart + m_pCurr->GetLen() <= nChar && Next() )
+ ;
+ while( m_nStart > nChar && Prev() )
+ ;
+}
+
+// 1170: takes into account ambiguities:
+const SwLineLayout *SwTextCursor::CharCursorToLine(TextFrameIndex const nPosition)
+{
+ CharToLine( nPosition );
+ if( nPosition != m_nStart )
+ s_bRightMargin = false;
+ bool bPrevious = s_bRightMargin && m_pCurr->GetLen() && GetPrev() &&
+ GetPrev()->GetLen();
+ if (bPrevious && nPosition && CH_BREAK == GetInfo().GetChar(nPosition - TextFrameIndex(1)))
+ bPrevious = false;
+ return bPrevious ? PrevLine() : m_pCurr;
+}
+
+SwTwips SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine,
+ const SwLinePortion* pPor,
+ SwTwips nPorHeight, SwTwips nPorAscent,
+ const bool bAutoToCentered ) const
+{
+ if ( pPor )
+ {
+ nPorHeight = pPor->Height();
+ nPorAscent = pPor->GetAscent();
+ }
+
+ SwTwips nOfst = rLine.GetRealHeight() - rLine.Height();
+
+ SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
+
+ if ( pGrid && GetInfo().SnapToGrid() && pGrid->IsSquaredMode() )
+ {
+ const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
+ const bool bRubyTop = ! pGrid->GetRubyTextBelow();
+
+ if ( GetInfo().IsMulti() )
+ // we are inside the GetCharRect recursion for multi portions
+ // we center the portion in its surrounding line
+ nOfst = ( m_pCurr->Height() - nPorHeight ) / 2 + nPorAscent;
+ else
+ {
+ // We have to take care for ruby portions.
+ // The ruby portion is NOT centered
+ nOfst = nOfst + nPorAscent;
+
+ if ( ! pPor || ! pPor->IsMultiPortion() ||
+ ! static_cast<const SwMultiPortion*>(pPor)->IsRuby() )
+ {
+ // Portions which are bigger than on grid distance are
+ // centered inside the whole line.
+
+ //for text refactor
+ const sal_uInt16 nLineNet = rLine.Height() - nRubyHeight;
+ //const sal_uInt16 nLineNet = ( nPorHeight > nGridWidth ) ?
+ // rLine.Height() - nRubyHeight :
+ // nGridWidth;
+ nOfst += ( nLineNet - nPorHeight ) / 2;
+ if ( bRubyTop )
+ nOfst += nRubyHeight;
+ }
+ }
+ }
+ else
+ {
+ switch ( GetLineInfo().GetVertAlign() ) {
+ case SvxParaVertAlignItem::Align::Top :
+ nOfst = nOfst + nPorAscent;
+ break;
+ case SvxParaVertAlignItem::Align::Center :
+ OSL_ENSURE( rLine.Height() >= nPorHeight, "Portion height > Line height");
+ nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent;
+ break;
+ case SvxParaVertAlignItem::Align::Bottom :
+ nOfst += rLine.Height() - nPorHeight + nPorAscent;
+ break;
+ case SvxParaVertAlignItem::Align::Automatic :
+ if ( bAutoToCentered || GetInfo().GetTextFrame()->IsVertical() )
+ {
+ // Vertical text has these cases to calculate the baseline:
+ // - Implicitly TB and RL: the origo is the top right corner, offset is the
+ // ascent.
+ // - (Implicitly TB and) LR: the origo is the top left corner, offset is the
+ // descent.
+ // - BT and LR: the origo is the bottom left corner, offset is the ascent.
+ if (GetInfo().GetTextFrame()->IsVertLR() && !GetInfo().GetTextFrame()->IsVertLRBT())
+ nOfst += rLine.Height() - ( rLine.Height() - nPorHeight ) / 2 - nPorAscent;
+ else
+ {
+ SwTwips nLineHeight = 0;
+ bool bHadClearingBreak = false;
+ if (GetInfo().GetTextFrame()->IsVertical())
+ {
+ // Ignore the height of clearing break portions in the automatic
+ // alignment case.
+ const SwLinePortion* pLinePor = rLine.GetFirstPortion();
+ while (pLinePor)
+ {
+ bool bClearingBreak = false;
+ if (pLinePor->IsBreakPortion())
+ {
+ auto pBreakPortion = static_cast<const SwBreakPortion*>(pLinePor);
+ bClearingBreak = pBreakPortion->GetClear() != SwLineBreakClear::NONE;
+ if (bClearingBreak)
+ {
+ bHadClearingBreak = true;
+ }
+ }
+ if (!bClearingBreak && pLinePor->Height() > nLineHeight)
+ {
+ nLineHeight = pLinePor->Height();
+ }
+ pLinePor = pLinePor->GetNextPortion();
+ }
+ }
+
+ if (!bHadClearingBreak)
+ {
+ nLineHeight = rLine.Height();
+ }
+
+ nOfst += ( nLineHeight - nPorHeight ) / 2 + nPorAscent;
+ }
+ break;
+ }
+ [[fallthrough]];
+ case SvxParaVertAlignItem::Align::Baseline :
+ // base line
+ if (pPor && pPor->GetHangingBaseline())
+ {
+ nOfst += rLine.GetAscent() // Romn baseline of the line.
+ - rLine.GetHangingBaseline() // Hanging baseline of the line.
+ + pPor->GetHangingBaseline(); // Romn baseline of the portion.
+ }
+ else
+ nOfst = nOfst + rLine.GetAscent();
+ break;
+ }
+ }
+
+ return nOfst;
+}
+
+void SwTextIter::TwipsToLine( const SwTwips y)
+{
+ while( m_nY + GetLineHeight() <= y && Next() )
+ ;
+ while( m_nY > y && Prev() )
+ ;
+}
+
+// Local helper function to check, if pCurr needs a field rest portion:
+static bool lcl_NeedsFieldRest( const SwLineLayout* pCurr )
+{
+ const SwLinePortion *pPor = pCurr->GetNextPortion();
+ bool bRet = false;
+ while( pPor && !bRet )
+ {
+ bRet = pPor->InFieldGrp() && static_cast<const SwFieldPortion*>(pPor)->HasFollow();
+ if( !pPor->GetNextPortion() || !pPor->GetNextPortion()->InFieldGrp() )
+ break;
+ pPor = pPor->GetNextPortion();
+ }
+ return bRet;
+}
+
+void SwTextIter::TruncLines( bool bNoteFollow )
+{
+ SwLineLayout *pDel = m_pCurr->GetNext();
+ TextFrameIndex const nEnd = m_nStart + m_pCurr->GetLen();
+
+ if( pDel )
+ {
+ m_pCurr->SetNext( nullptr );
+ if (MaybeHasHints() && bNoteFollow)
+ {
+ GetInfo().GetParaPortion()->SetFollowField( pDel->IsRest() ||
+ lcl_NeedsFieldRest( m_pCurr ) );
+
+ // bug 88534: wrong positioning of flys
+ SwTextFrame* pFollow = GetTextFrame()->GetFollow();
+ if ( pFollow && ! pFollow->IsLocked() &&
+ nEnd == pFollow->GetOffset() )
+ {
+ TextFrameIndex nRangeEnd = nEnd;
+ SwLineLayout* pLine = pDel;
+
+ // determine range to be searched for flys anchored as characters
+ while ( pLine )
+ {
+ nRangeEnd = nRangeEnd + pLine->GetLen();
+ pLine = pLine->GetNext();
+ }
+
+ // examine hints in range nEnd - (nEnd + nRangeChar)
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIter iter(*GetTextFrame());
+ for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
+ {
+ if( RES_TXTATR_FLYCNT == pHt->Which() )
+ {
+ // check if hint is in our range
+ TextFrameIndex const nTmpPos(
+ GetTextFrame()->MapModelToView(pNode, pHt->GetStart()));
+ if ( nEnd <= nTmpPos && nTmpPos < nRangeEnd )
+ pFollow->InvalidateRange_(
+ SwCharRange( nTmpPos, nTmpPos ) );
+ }
+ }
+ }
+ }
+ delete pDel;
+ }
+ if( m_pCurr->IsDummy() &&
+ !m_pCurr->GetLen() &&
+ m_nStart < TextFrameIndex(GetTextFrame()->GetText().getLength()))
+ {
+ m_pCurr->SetRealHeight( 1 );
+ }
+ if (MaybeHasHints())
+ m_pFrame->RemoveFootnote( nEnd );
+}
+
+void SwTextIter::CntHyphens( sal_uInt8 &nEndCnt, sal_uInt8 &nMidCnt) const
+{
+ nEndCnt = 0;
+ nMidCnt = 0;
+ if ( m_bPrev && m_pPrev && !m_pPrev->IsEndHyph() && !m_pPrev->IsMidHyph() )
+ return;
+ SwLineLayout *pLay = m_pInf->GetParaPortion();
+ if( m_pCurr == pLay )
+ return;
+ while( pLay != m_pCurr )
+ {
+ if ( pLay->IsEndHyph() )
+ nEndCnt++;
+ else
+ nEndCnt = 0;
+ if ( pLay->IsMidHyph() )
+ nMidCnt++;
+ else
+ nMidCnt = 0;
+ pLay = pLay->GetNext();
+ }
+}
+
+// Change current output device to formatting device, this has to be done before
+// formatting.
+SwHookOut::SwHookOut( SwTextSizeInfo& rInfo ) :
+ pInf( &rInfo ),
+ pOut( rInfo.GetOut() ),
+ bOnWin( rInfo.OnWin() )
+{
+ OSL_ENSURE( rInfo.GetRefDev(), "No reference device for text formatting" );
+
+ // set new values
+ rInfo.SetOut( rInfo.GetRefDev() );
+ rInfo.SetOnWin( false );
+}
+
+SwHookOut::~SwHookOut()
+{
+ pInf->SetOut( pOut );
+ pInf->SetOnWin( bOnWin );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/itrtxt.hxx b/sw/source/core/text/itrtxt.hxx
new file mode 100644
index 000000000..f36932dbd
--- /dev/null
+++ b/sw/source/core/text/itrtxt.hxx
@@ -0,0 +1,340 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <swtypes.hxx>
+#include "itratr.hxx"
+#include "inftxt.hxx"
+
+struct SwPosition;
+struct SwCursorMoveState;
+class SwMarginPortion;
+class SwFlyPortion;
+
+class SwTextIter : public SwAttrIter
+{
+protected:
+ SwLineInfo m_aLineInf;
+ SwTextFrame *m_pFrame;
+ SwTextInfo *m_pInf;
+ SwLineLayout *m_pCurr;
+ SwLineLayout *m_pPrev;
+ SwTwips m_nFrameStart;
+ SwTwips m_nY;
+ SwTwips m_nRegStart; // The register's start position (Y)
+ TextFrameIndex m_nStart; // Start in the text string, end = pCurr->GetLen()
+ sal_uInt16 m_nRegDiff; // Register's line distance
+ sal_uInt16 m_nLineNr; // Line number
+ bool m_bPrev : 1;
+ bool m_bRegisterOn : 1; // Keep in register
+ bool m_bOneBlock : 1; // Justified text: Dispose single words
+ bool m_bLastBlock : 1; // Justified text: Also the last line
+ bool m_bLastCenter : 1; // Justified text: Center last line
+
+ SwLineLayout *GetPrev_();
+
+ // Reset in the first line
+ void Init();
+ void CtorInitTextIter( SwTextFrame *pFrame, SwTextInfo *pInf );
+ explicit SwTextIter(SwTextNode const * pTextNode)
+ : SwAttrIter(pTextNode)
+ , m_pFrame(nullptr)
+ , m_pInf(nullptr)
+ , m_pCurr(nullptr)
+ , m_pPrev(nullptr)
+ , m_nFrameStart(0)
+ , m_nY(0)
+ , m_nRegStart(0)
+ , m_nStart(0)
+ , m_nRegDiff(0)
+ , m_nLineNr(0)
+ , m_bPrev(false)
+ , m_bRegisterOn(false)
+ , m_bOneBlock(false)
+ , m_bLastBlock(false)
+ , m_bLastCenter(false)
+ {
+ }
+public:
+ SwTextIter(SwTextFrame *pTextFrame, SwTextInfo *pTextInf)
+ : SwAttrIter(pTextFrame->GetTextNodeFirst())
+ , m_bOneBlock(false)
+ , m_bLastBlock(false)
+ , m_bLastCenter(false)
+ {
+ CtorInitTextIter(pTextFrame, pTextInf);
+ }
+ const SwLineLayout *GetCurr() const { return m_pCurr; } // NEVER 0!
+ const SwLineLayout *GetNext() const { return m_pCurr->GetNext(); }
+ const SwLineLayout *GetPrev();
+ TextFrameIndex GetLength() const { return m_pCurr->GetLen(); }
+ sal_uInt16 GetLineNr() const { return m_nLineNr; }
+ TextFrameIndex GetStart() const { return m_nStart; }
+ TextFrameIndex GetEnd() const { return GetStart() + GetLength(); }
+ SwTwips Y() const { return m_nY; }
+
+ SwTwips RegStart() const { return m_nRegStart; }
+ sal_uInt16 RegDiff() const { return m_nRegDiff; }
+ bool IsRegisterOn() const { return m_bRegisterOn; }
+
+ SwTextInfo &GetInfo() { return *m_pInf; }
+ const SwTextInfo &GetInfo() const { return *m_pInf; }
+
+ void Top() { Init(); }
+ void Bottom();
+ const SwLineLayout *Next();
+ const SwLineLayout *Prev();
+
+ // Skips the FlyFrames dummy line
+ const SwLineLayout *NextLine();
+ const SwLineLayout *PrevLine();
+ const SwLineLayout *GetNextLine() const;
+ const SwLineLayout *GetPrevLine();
+
+ void CharToLine(TextFrameIndex);
+ void TwipsToLine(const SwTwips);
+
+ // Truncates all after pCurr
+ void TruncLines( bool bNoteFollow = false );
+
+ SwTwips GetLineHeight() const { return m_pCurr->GetRealHeight(); }
+ void CalcAscentAndHeight( SwTwips &rAscent, SwTwips &rHeight ) const;
+
+ // Lots of trouble for querying pCurr == pPara
+ bool IsFirstTextLine() const
+ { return m_nStart == GetInfo().GetTextStart() &&
+ !( m_pCurr->IsDummy() && GetNextLine() ); }
+
+ // Replacement for the old IsFirstLine()
+ bool IsParaLine() const
+ { return m_pCurr == m_pInf->GetParaPortion(); }
+
+ const SwLineInfo &GetLineInfo() const { return m_aLineInf; }
+ SwTwips GetFirstPos() const { return m_nFrameStart; }
+ inline bool SeekAndChg( SwTextSizeInfo &rInf );
+ inline bool SeekAndChgBefore( SwTextSizeInfo &rInf );
+ inline bool SeekStartAndChg( SwTextSizeInfo &rInf, const bool bPara=false );
+
+ SwTextFrame *GetTextFrame() { return m_pFrame; }
+ const SwTextFrame *GetTextFrame() const { return m_pFrame; }
+
+ // Counts consecutive hyphens in order to be within the boundary given by MaxHyphens
+ void CntHyphens( sal_uInt8 &nEndCnt, sal_uInt8 &nMidCnt) const;
+};
+
+class SwTextMargin : public SwTextIter
+{
+private:
+ SwTwips mnLeft;
+ SwTwips mnRight;
+ SwTwips mnFirst;
+ sal_uInt16 mnDropLeft;
+ sal_uInt16 mnDropHeight;
+ sal_uInt16 mnDropDescent;
+ sal_uInt16 mnDropLines;
+ SvxAdjust mnAdjust;
+ // #i91133#
+ SwTwips mnTabLeft;
+
+protected:
+ // For FormatQuoVadis
+ void Right( const SwTwips nNew ) { mnRight = nNew; }
+
+ void CtorInitTextMargin( SwTextFrame *pFrame, SwTextSizeInfo *pInf );
+ explicit SwTextMargin(SwTextNode const * pTextNode)
+ : SwTextIter(pTextNode)
+ , mnLeft(0)
+ , mnRight(0)
+ , mnFirst(0)
+ , mnDropLeft(0)
+ , mnDropHeight(0)
+ , mnDropDescent(0)
+ , mnDropLines(0)
+ , mnAdjust(SvxAdjust::Left)
+ , mnTabLeft(0)
+ {
+ }
+public:
+ SwTextMargin(SwTextFrame *pTextFrame, SwTextSizeInfo *pTextSizeInf)
+ : SwTextIter(pTextFrame->GetTextNodeFirst())
+ {
+ CtorInitTextMargin( pTextFrame, pTextSizeInf );
+ }
+ inline SwTwips GetLeftMargin() const;
+ inline SwTwips Left() const;
+ SwTwips Right() const { return mnRight; }
+ SwTwips FirstLeft() const { return mnFirst; }
+ SwTwips CurrWidth() const { return m_pCurr->PrtWidth(); }
+ SwTwips GetLineStart() const;
+ SwTwips GetLineEnd() const { return GetLineStart() + CurrWidth(); }
+ Point GetTopLeft() const { return Point( GetLineStart(), Y() ); }
+ bool IsOneBlock() const { return m_bOneBlock; }
+ bool IsLastBlock() const { return m_bLastBlock; }
+ bool IsLastCenter() const { return m_bLastCenter; }
+ SvxAdjust GetAdjust() const { return mnAdjust; }
+ sal_uInt16 GetLineWidth() const
+ { return sal_uInt16( Right() - GetLeftMargin() + 1 ); }
+ SwTwips GetLeftMin() const { return std::min(mnFirst, mnLeft); }
+ bool HasNegFirst() const { return mnFirst < mnLeft; }
+
+ // #i91133#
+ SwTwips GetTabLeft() const
+ {
+ return mnTabLeft;
+ }
+ // DropCaps
+ sal_uInt16 GetDropLines() const { return mnDropLines; }
+ void SetDropLines( const sal_uInt16 nNew ) { mnDropLines = nNew; }
+ sal_uInt16 GetDropLeft() const { return mnDropLeft; }
+ sal_uInt16 GetDropHeight() const { return mnDropHeight; }
+ void SetDropHeight( const sal_uInt16 nNew ) { mnDropHeight = nNew; }
+ sal_uInt16 GetDropDescent() const { return mnDropDescent; }
+ void SetDropDescent( const sal_uInt16 nNew ) { mnDropDescent = nNew; }
+ void DropInit();
+
+ // Returns the TextPos for start and end of the current line without whitespace
+ // Implemented in frminf.cxx
+ TextFrameIndex GetTextStart() const;
+ TextFrameIndex GetTextEnd() const;
+
+ SwTextSizeInfo &GetInfo()
+ { return static_cast<SwTextSizeInfo&>(SwTextIter::GetInfo()); }
+ const SwTextSizeInfo &GetInfo() const
+ { return static_cast<const SwTextSizeInfo&>(SwTextIter::GetInfo()); }
+
+};
+
+class SwTextAdjuster : public SwTextMargin
+{
+ // Adjusts the portion, if we have adjustment and FlyFrames
+ void CalcFlyAdjust( SwLineLayout *pCurr );
+
+ // Calls SplitGlues and CalcBlockAdjust
+ void FormatBlock( );
+
+ // Creates the glue chain for short lines
+ SwMarginPortion* CalcRightMargin( SwLineLayout *pCurr, SwTwips nReal = 0 );
+
+ // Calculate the adjustment (FlyPortions)
+ SwFlyPortion *CalcFlyPortion( const tools::Long nRealWidth,
+ const SwRect &rCurrRect );
+
+protected:
+ explicit SwTextAdjuster(SwTextNode const * pTextNode) : SwTextMargin(pTextNode) { }
+ // Creates the Glues for adjusted paragraphs
+ void CalcNewBlock( SwLineLayout *pCurr, const SwLinePortion *pStopAt,
+ SwTwips nReal = 0, bool bSkipKashida = false );
+ SwTwips CalcKanaAdj( SwLineLayout *pCurr );
+
+public:
+ // Is overloaded by SwTextFormatter due to UpdatePos
+ void CalcAdjLine( SwLineLayout *pCurr );
+
+ // For adjusting afterwards
+ void GetAdjusted() const
+ {
+ if( m_pCurr->IsFormatAdj() )
+ const_cast<SwTextAdjuster*>(this)->CalcAdjLine( m_pCurr );
+ }
+
+ // Special treatment for DropCaps
+ void CalcDropAdjust();
+ void CalcDropRepaint();
+};
+
+class SwTextCursor : public SwTextAdjuster
+{
+ // A small helper-class to save SwTextCursor member, manipulate them
+ // and to restore them
+ friend class SwTextCursorSave;
+
+ // Ambiguities
+ static bool s_bRightMargin;
+ void GetCharRect_(SwRect *, TextFrameIndex, SwCursorMoveState *);
+protected:
+ void CtorInitTextCursor( SwTextFrame *pFrame, SwTextSizeInfo *pInf );
+ explicit SwTextCursor(SwTextNode const * pTextNode) : SwTextAdjuster(pTextNode) { }
+public:
+ SwTextCursor( SwTextFrame *pTextFrame, SwTextSizeInfo *pTextSizeInf )
+ : SwTextAdjuster(pTextFrame->GetTextNodeFirst())
+ {
+ CtorInitTextCursor(pTextFrame, pTextSizeInf);
+ }
+ void GetCharRect(SwRect *, TextFrameIndex, SwCursorMoveState* = nullptr,
+ const tools::Long nMax = 0 );
+ void GetEndCharRect(SwRect *, TextFrameIndex, SwCursorMoveState* = nullptr,
+ const tools::Long nMax = 0 );
+ TextFrameIndex GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
+ bool bChgNode, SwCursorMoveState* = nullptr ) const;
+ // Respects ambiguities: For the implementation see below
+ const SwLineLayout *CharCursorToLine(TextFrameIndex const nPos);
+
+ // calculates baseline for portion rPor
+ // bAutoToCentered indicates, if AUTOMATIC mode means CENTERED or BASELINE
+ SwTwips AdjustBaseLine( const SwLineLayout& rLine, const SwLinePortion* pPor,
+ SwTwips nPorHeight = 0, SwTwips nAscent = 0,
+ const bool bAutoToCentered = false ) const;
+
+ static void SetRightMargin( const bool bNew ){ s_bRightMargin = bNew; }
+ static bool IsRightMargin() { return s_bRightMargin; }
+};
+
+// Change current output device to printer, this has to be done before
+// formatting.
+class SwHookOut
+{
+ SwTextSizeInfo* pInf;
+ VclPtr<OutputDevice> pOut;
+ bool bOnWin;
+public:
+ explicit SwHookOut( SwTextSizeInfo& rInfo );
+ ~SwHookOut();
+};
+
+inline bool SwTextIter::SeekAndChg( SwTextSizeInfo &rInf )
+{
+ return SeekAndChgAttrIter( rInf.GetIdx(), rInf.GetOut() );
+}
+
+inline bool SwTextIter::SeekAndChgBefore( SwTextSizeInfo &rInf )
+{
+ if ( rInf.GetIdx() )
+ return SeekAndChgAttrIter(rInf.GetIdx() - TextFrameIndex(1), rInf.GetOut());
+ else
+ return SeekAndChgAttrIter( rInf.GetIdx(), rInf.GetOut() );
+}
+
+inline bool SwTextIter::SeekStartAndChg( SwTextSizeInfo &rInf, const bool bPara )
+{
+ return SeekStartAndChgAttrIter( rInf.GetOut(), bPara );
+}
+
+inline SwTwips SwTextMargin::GetLeftMargin() const
+{
+ return IsFirstTextLine() ? mnFirst : Left();
+}
+
+inline SwTwips SwTextMargin::Left() const
+{
+ return (mnDropLines >= m_nLineNr && 1 != m_nLineNr) ? mnFirst + mnDropLeft : mnLeft;
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/noteurl.cxx b/sw/source/core/text/noteurl.cxx
new file mode 100644
index 000000000..fa91ea252
--- /dev/null
+++ b/sw/source/core/text/noteurl.cxx
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <noteurl.hxx>
+
+// Global variable
+SwNoteURL* pNoteURL = nullptr;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/pordrop.hxx b/sw/source/core/text/pordrop.hxx
new file mode 100644
index 000000000..50dd00e56
--- /dev/null
+++ b/sw/source/core/text/pordrop.hxx
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include "portxt.hxx"
+#include <swfont.hxx>
+
+#include <memory>
+
+class SwFont;
+
+// DropCap cache, global variable initialized/destroyed in txtinit.cxx
+// and used in txtdrop.cxx for initial calculation
+
+class SwDropCapCache;
+extern SwDropCapCache *pDropCapCache;
+
+// A drop portion can consist of one or more parts in order to allow
+// attribute changes inside them.
+class SwDropPortionPart
+{
+ std::unique_ptr<SwDropPortionPart> m_pFollow;
+ std::unique_ptr<SwFont> m_pFnt;
+ TextFrameIndex m_nLen;
+ sal_uInt16 m_nWidth;
+ bool m_bJoinBorderWithNext;
+ bool m_bJoinBorderWithPrev;
+
+public:
+ SwDropPortionPart( SwFont& rFont, const TextFrameIndex nL )
+ : m_pFnt( &rFont ), m_nLen( nL ), m_nWidth( 0 ), m_bJoinBorderWithNext(false), m_bJoinBorderWithPrev(false) {};
+ ~SwDropPortionPart();
+
+ SwDropPortionPart* GetFollow() const { return m_pFollow.get(); };
+ void SetFollow( std::unique_ptr<SwDropPortionPart> pNew ) { m_pFollow = std::move(pNew); };
+ SwFont& GetFont() const { return *m_pFnt; }
+ TextFrameIndex GetLen() const { return m_nLen; }
+ sal_uInt16 GetWidth() const { return m_nWidth; }
+ void SetWidth( sal_uInt16 nNew ) { m_nWidth = nNew; }
+
+ bool GetJoinBorderWithPrev() const { return m_bJoinBorderWithPrev; }
+ bool GetJoinBorderWithNext() const { return m_bJoinBorderWithNext; }
+ void SetJoinBorderWithPrev( const bool bJoinPrev ) { m_bJoinBorderWithPrev = bJoinPrev; }
+ void SetJoinBorderWithNext( const bool bJoinNext ) { m_bJoinBorderWithNext = bJoinNext; }
+};
+
+/// Text portion for the Format -> Paragraph -> Drop Caps functionality.
+class SwDropPortion : public SwTextPortion
+{
+ friend class SwDropCapCache;
+ std::unique_ptr<SwDropPortionPart> m_pPart; // due to script/attribute changes
+ sal_uInt16 m_nLines; // Line count
+ sal_uInt16 m_nDropHeight; // Height
+ sal_uInt16 m_nDropDescent; // Distance to the next line
+ sal_uInt16 m_nDistance; // Distance to the text
+ sal_uInt16 m_nFix; // Fixed position
+ short m_nY; // Y Offset
+
+ bool FormatText( SwTextFormatInfo &rInf );
+ void PaintText( const SwTextPaintInfo &rInf ) const;
+
+public:
+ SwDropPortion( const sal_uInt16 nLineCnt,
+ const sal_uInt16 nDropHeight,
+ const sal_uInt16 nDropDescent,
+ const sal_uInt16 nDistance );
+ virtual ~SwDropPortion() override;
+
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ void PaintDrop( const SwTextPaintInfo &rInf ) const;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+
+ sal_uInt16 GetLines() const { return m_nLines; }
+ sal_uInt16 GetDistance() const { return m_nDistance; }
+ sal_uInt16 GetDropHeight() const { return m_nDropHeight; }
+ sal_uInt16 GetDropDescent() const { return m_nDropDescent; }
+ sal_uInt16 GetDropLeft() const { return Width() + m_nFix; }
+
+ SwDropPortionPart* GetPart() const { return m_pPart.get(); }
+ void SetPart( std::unique_ptr<SwDropPortionPart> pNew ) { m_pPart = std::move(pNew); }
+
+ void SetY( short nNew ) { m_nY = nNew; }
+
+ SwFont* GetFnt() const { return m_pPart ? &m_pPart->GetFont() : nullptr; }
+
+ static void DeleteDropCapCache();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porexp.cxx b/sw/source/core/text/porexp.cxx
new file mode 100644
index 000000000..f38e7b1f7
--- /dev/null
+++ b/sw/source/core/text/porexp.cxx
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <viewopt.hxx>
+#include <SwPortionHandler.hxx>
+#include "inftxt.hxx"
+#include "porexp.hxx"
+
+TextFrameIndex SwExpandPortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+{ return SwLinePortion::GetModelPositionForViewPoint( nOfst ); }
+
+bool SwExpandPortion::GetExpText( const SwTextSizeInfo&, OUString &rText ) const
+{
+ rText.clear();
+ // Do not do: return 0 != rText.Len();
+ // Reason being: empty fields replace CH_TXTATR with an empty string
+ return true;
+}
+
+void SwExpandPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), OUString(), GetWhichPor() );
+}
+
+void SwExpandPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwExpandPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwPosSize SwExpandPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+{
+ SwTextSlot aDiffText( &rInf, this, false, false );
+ return rInf.GetTextSize();
+}
+
+bool SwExpandPortion::Format( SwTextFormatInfo &rInf )
+{
+ SwTextSlot aDiffText( &rInf, this, true, false );
+ TextFrameIndex const nFullLen = rInf.GetLen();
+
+ // As odd as it may seem: the query for GetLen() must return
+ // false due to the ExpandPortions _after_ the aDiffText (see SoftHyphs)
+ // caused by the SetFull ...
+ if( !nFullLen )
+ {
+ // Do not Init(), because we need height and ascent
+ Width(0);
+ return false;
+ }
+ return SwTextPortion::Format( rInf );
+}
+
+void SwExpandPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ SwTextSlot aDiffText( &rInf, this, true, true );
+ const SwFont aOldFont = *rInf.GetFont();
+ if( GetJoinBorderWithPrev() )
+ const_cast<SwTextPaintInfo&>(rInf).GetFont()->SetLeftBorder(nullptr);
+ if( GetJoinBorderWithNext() )
+ const_cast<SwTextPaintInfo&>(rInf).GetFont()->SetRightBorder(nullptr);
+
+ rInf.DrawBackBrush( *this );
+ rInf.DrawBorder( *this );
+
+ // Do we have to repaint a post it portion?
+ if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
+ mpNextPortion->PrePaint( rInf, this );
+
+ // The contents of field portions is not considered during the
+ // calculation of the directions. Therefore we let vcl handle
+ // the calculation by removing the BIDI_STRONG_FLAG temporarily.
+ SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
+ aLayoutModeModifier.SetAuto();
+
+ // ST2
+ if ( rInf.GetSmartTags() || rInf.GetGrammarCheckList() )
+ rInf.DrawMarkedText( *this, rInf.GetLen(), false,
+ nullptr != rInf.GetSmartTags(), nullptr != rInf.GetGrammarCheckList() );
+ else
+ rInf.DrawText( *this, rInf.GetLen() );
+
+ if( GetJoinBorderWithPrev() || GetJoinBorderWithNext() )
+ *const_cast<SwTextPaintInfo&>(rInf).GetFont() = aOldFont;
+}
+
+SwLinePortion *SwBlankPortion::Compress() { return this; }
+
+/**
+ * If a Line is full of HardBlanks and overflows, we must not generate
+ * underflows!
+ * Causes problems with Fly
+ */
+sal_uInt16 SwBlankPortion::MayUnderflow( const SwTextFormatInfo &rInf,
+ TextFrameIndex const nIdx, bool bUnderflow)
+{
+ if( rInf.StopUnderflow() )
+ return 0;
+ const SwLinePortion *pPos = rInf.GetRoot();
+ if( pPos->GetNextPortion() )
+ pPos = pPos->GetNextPortion();
+ while( pPos && pPos->IsBlankPortion() )
+ pPos = pPos->GetNextPortion();
+ if( !pPos || !rInf.GetIdx() || ( !pPos->GetLen() && pPos == rInf.GetRoot() ) )
+ return 0; // There are just BlankPortions left
+
+ // If a Blank is preceding us, we do not need to trigger underflow
+ // If a Blank is succeeding us, we do not need to pass on the underflow
+ if (bUnderflow
+ && nIdx + TextFrameIndex(1) < TextFrameIndex(rInf.GetText().getLength())
+ && CH_BLANK == rInf.GetText()[sal_Int32(nIdx) + 1])
+ {
+ return 0;
+ }
+ if( nIdx && !const_cast<SwTextFormatInfo&>(rInf).GetFly() )
+ {
+ while( pPos && !pPos->IsFlyPortion() )
+ pPos = pPos->GetNextPortion();
+ if( !pPos )
+ {
+ // We check to see if there are useful line breaks, blanks or fields etc. left
+ // In case there still are some, no underflow
+ // If there are Flys, we still allow the underflow
+ TextFrameIndex nBlank = nIdx;
+ while( --nBlank > rInf.GetLineStart() )
+ {
+ const sal_Unicode cCh = rInf.GetChar( nBlank );
+ if( CH_BLANK == cCh ||
+ (( CH_TXTATR_BREAKWORD == cCh || CH_TXTATR_INWORD == cCh )
+ && rInf.HasHint( nBlank ) ) )
+ break;
+ }
+ if( nBlank <= rInf.GetLineStart() )
+ return 0;
+ }
+ }
+ if (nIdx < TextFrameIndex(2))
+ return 1;
+ sal_Unicode const cCh(rInf.GetChar(nIdx - TextFrameIndex(1)));
+ if (CH_BLANK == cCh)
+ return 1;
+ if( CH_BREAK == cCh )
+ return 0;
+ return 2;
+}
+
+/**
+ * Format End of Line
+ */
+void SwBlankPortion::FormatEOL( SwTextFormatInfo &rInf )
+{
+ sal_uInt16 nMay = MayUnderflow( rInf, rInf.GetIdx() - mnLineLength, true );
+ if( !nMay )
+ return;
+
+ if( nMay > 1 )
+ {
+ if( rInf.GetLast() == this )
+ rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
+ rInf.X( rInf.X() - PrtWidth() );
+ rInf.SetIdx( rInf.GetIdx() - GetLen() );
+ }
+ Truncate();
+ rInf.SetUnderflow( this );
+ if( rInf.GetLast()->IsKernPortion() )
+ rInf.SetUnderflow( rInf.GetLast() );
+}
+
+/**
+ * Pass on the underflows and trigger them ourselves!
+ */
+bool SwBlankPortion::Format( SwTextFormatInfo &rInf )
+{
+ const bool bFull = rInf.IsUnderflow() || SwExpandPortion::Format( rInf );
+ if( bFull && MayUnderflow( rInf, rInf.GetIdx(), rInf.IsUnderflow() ) )
+ {
+ Truncate();
+ rInf.SetUnderflow( this );
+ if( rInf.GetLast()->IsKernPortion() )
+ rInf.SetUnderflow( rInf.GetLast() );
+ }
+ return bFull;
+}
+
+void SwBlankPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( !m_bMulti ) // No gray background for multiportion brackets
+ rInf.DrawViewOpt( *this, PortionType::Blank );
+ SwExpandPortion::Paint( rInf );
+}
+
+bool SwBlankPortion::GetExpText( const SwTextSizeInfo&, OUString &rText ) const
+{
+ rText = OUString(m_cChar);
+ return true;
+}
+
+void SwBlankPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), OUString( m_cChar ), GetWhichPor() );
+}
+
+void SwBlankPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBlankPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("char"),
+ BAD_CAST(OUString(m_cChar).toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("multi"),
+ BAD_CAST(OString::boolean(m_bMulti).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwPostItsPortion::SwPostItsPortion( bool bScrpt )
+ : m_bScript( bScrpt )
+{
+ mnLineLength = TextFrameIndex(1);
+ SetWhichPor( PortionType::PostIts );
+}
+
+void SwPostItsPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( rInf.OnWin() && Width() )
+ rInf.DrawPostIts( IsScript() );
+}
+
+sal_uInt16 SwPostItsPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+{
+ // Unbelievable: PostIts are always visible
+ return rInf.OnWin() ? SwViewOption::GetPostItsWidth( rInf.GetOut() ) : 0;
+}
+
+bool SwPostItsPortion::Format( SwTextFormatInfo &rInf )
+{
+ const bool bRet = SwLinePortion::Format( rInf );
+ // PostIts should not have an effect on line height etc.
+ SetAscent( 1 );
+ Height( 1 );
+ return bRet;
+}
+
+bool SwPostItsPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
+{
+ if( rInf.OnWin() && rInf.GetOpt().IsPostIts() )
+ rText = " ";
+ else
+ rText.clear();
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porexp.hxx b/sw/source/core/text/porexp.hxx
new file mode 100644
index 000000000..82de5b1be
--- /dev/null
+++ b/sw/source/core/text/porexp.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "portxt.hxx"
+
+class SwExpandPortion : public SwTextPortion
+{
+public:
+ SwExpandPortion() { SetWhichPor( PortionType::Expand ); }
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+/// Non-breaking space or non-breaking hyphen.
+class SwBlankPortion : public SwExpandPortion
+{
+ sal_Unicode m_cChar;
+ bool m_bMulti; // For multiportion brackets
+public:
+ SwBlankPortion( sal_Unicode cCh, bool bMult = false )
+ : m_cChar( cCh ), m_bMulti( bMult )
+ { SetLen(TextFrameIndex(1)); SetWhichPor( PortionType::Blank ); }
+
+ virtual SwLinePortion *Compress() override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ static sal_uInt16 MayUnderflow(const SwTextFormatInfo &rInf, TextFrameIndex nIdx,
+ bool bUnderflow );
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+class SwPostItsPortion : public SwExpandPortion
+{
+ bool m_bScript;
+public:
+ explicit SwPostItsPortion( bool bScrpt );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ bool IsScript() const { return m_bScript; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx
new file mode 100644
index 000000000..444313cc2
--- /dev/null
+++ b/sw/source/core/text/porfld.cxx
@@ -0,0 +1,1380 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <vcl/graph.hxx>
+#include <editeng/brushitem.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/outdev.hxx>
+#include <viewopt.hxx>
+#include <SwPortionHandler.hxx>
+#include "porlay.hxx"
+#include "porfld.hxx"
+#include "inftxt.hxx"
+#include <fmtornt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <rootfrm.hxx>
+#include <breakit.hxx>
+#include "porftn.hxx"
+#include <accessibilityoptions.hxx>
+#include <editeng/lrspitem.hxx>
+#include <unicode/ubidi.h>
+#include <bookmark.hxx>
+
+using namespace ::com::sun::star;
+
+SwLinePortion *SwFieldPortion::Compress()
+{ return (GetLen() || !m_aExpand.isEmpty() || SwLinePortion::Compress()) ? this : nullptr; }
+
+SwFieldPortion *SwFieldPortion::Clone( const OUString &rExpand ) const
+{
+ std::unique_ptr<SwFont> pNewFnt;
+ if( m_pFont )
+ {
+ pNewFnt.reset(new SwFont( *m_pFont ));
+ }
+ // #i107143#
+ // pass placeholder property to created <SwFieldPortion> instance.
+ SwFieldPortion* pClone = new SwFieldPortion( rExpand, std::move(pNewFnt), m_bPlaceHolder );
+ pClone->SetNextOffset( m_nNextOffset );
+ pClone->m_bNoLength = m_bNoLength;
+ return pClone;
+}
+
+void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField )
+{
+ OSL_ENSURE( pField, "TakeNextOffset: Missing Source" );
+ m_nNextOffset = pField->GetNextOffset();
+ m_aExpand = m_aExpand.replaceAt(0, sal_Int32(m_nNextOffset), u"");
+ m_bFollow = true;
+}
+
+SwFieldPortion::SwFieldPortion(const OUString &rExpand, std::unique_ptr<SwFont> pFont, bool bPlaceHold, TextFrameIndex const nFieldLen)
+ : m_aExpand(rExpand), m_pFont(std::move(pFont)), m_nNextOffset(0)
+ , m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0)
+ , m_bFollow( false ), m_bLeft( false), m_bHide( false)
+ , m_bCenter (false), m_bHasFollow( false )
+ , m_bAnimated( false), m_bNoPaint( false)
+ , m_bReplace( false), m_bPlaceHolder( bPlaceHold )
+ , m_bNoLength( false )
+ , m_nAttrFieldType(0)
+{
+ SetWhichPor( PortionType::Field );
+}
+
+SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField )
+ : SwExpandPortion( rField )
+ , m_aExpand( rField.GetExp() )
+ , m_nNextOffset( rField.GetNextOffset() )
+ , m_nNextScriptChg( rField.m_nNextScriptChg )
+ , m_nFieldLen(rField.m_nFieldLen)
+ , m_nViewWidth( rField.m_nViewWidth )
+ , m_bFollow( rField.IsFollow() )
+ , m_bLeft( rField.IsLeft() )
+ , m_bHide( rField.IsHide() )
+ , m_bCenter( rField.IsCenter() )
+ , m_bHasFollow( rField.HasFollow() )
+ , m_bAnimated ( rField.m_bAnimated )
+ , m_bNoPaint( rField.m_bNoPaint)
+ , m_bReplace( rField.m_bReplace )
+ , m_bPlaceHolder( rField.m_bPlaceHolder )
+ , m_bNoLength( rField.m_bNoLength )
+ , m_nAttrFieldType( rField.m_nAttrFieldType)
+{
+ if ( rField.HasFont() )
+ m_pFont.reset( new SwFont( *rField.GetFont() ) );
+
+ SetWhichPor( PortionType::Field );
+}
+
+SwFieldPortion::~SwFieldPortion()
+{
+ m_pFont.reset();
+}
+
+sal_uInt16 SwFieldPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+{
+ // even though this is const, nViewWidth should be computed at the very end:
+ SwFieldPortion* pThis = const_cast<SwFieldPortion*>(this);
+ if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
+ !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
+ {
+ if( !m_nViewWidth )
+ pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
+ }
+ else
+ pThis->m_nViewWidth = 0;
+ return m_nViewWidth;
+}
+
+namespace {
+
+/**
+ * Never just use SetLen(0)
+ */
+class SwFieldSlot
+{
+ std::shared_ptr<const vcl::text::TextLayoutCache> m_pOldCachedVclData;
+ const OUString *pOldText;
+ OUString aText;
+ TextFrameIndex nIdx;
+ TextFrameIndex nLen;
+ SwTextFormatInfo *pInf;
+ bool bOn;
+public:
+ SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor );
+ ~SwFieldSlot();
+};
+
+}
+
+SwFieldSlot::SwFieldSlot( const SwTextFormatInfo* pNew, const SwFieldPortion *pPor )
+ : pOldText(nullptr)
+ , nIdx(0)
+ , nLen(0)
+ , pInf(nullptr)
+{
+ bOn = pPor->GetExpText( *pNew, aText );
+
+ // The text will be replaced ...
+ if( !bOn )
+ return;
+
+ pInf = const_cast<SwTextFormatInfo*>(pNew);
+ nIdx = pInf->GetIdx();
+ nLen = pInf->GetLen();
+ pOldText = &(pInf->GetText());
+ m_pOldCachedVclData = pInf->GetCachedVclData();
+ pInf->SetLen(TextFrameIndex(aText.getLength()));
+ pInf->SetCachedVclData(nullptr);
+ if( pPor->IsFollow() )
+ {
+ pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
+ pInf->SetIdx(TextFrameIndex(0));
+ }
+ else
+ {
+ TextFrameIndex nEnd(pOldText->getLength());
+ if (nIdx < nEnd)
+ {
+ sal_Int32 const nFieldLen(pPor->GetFieldLen());
+ aText = (*pOldText).replaceAt(sal_Int32(nIdx), nFieldLen, aText);
+ }
+ else if (nIdx == nEnd)
+ aText = *pOldText + aText;
+ else
+ SAL_WARN("sw.core", "SwFieldSlot bad SwFieldPortion index.");
+ }
+ pInf->SetText( aText );
+}
+
+SwFieldSlot::~SwFieldSlot()
+{
+ if( bOn )
+ {
+ pInf->SetCachedVclData(m_pOldCachedVclData);
+ pInf->SetText( *pOldText );
+ pInf->SetIdx( nIdx );
+ pInf->SetLen( nLen );
+ pInf->SetFakeLineStart( false );
+ }
+}
+
+void SwFieldPortion::CheckScript( const SwTextSizeInfo &rInf )
+{
+ OUString aText;
+ if (!GetExpText(rInf, aText) || aText.isEmpty())
+ return;
+
+ SwFontScript nActual = m_pFont ? m_pFont->GetActual() : rInf.GetFont()->GetActual();
+ sal_uInt16 nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, 0 );
+ sal_Int32 nChg = 0;
+ if( i18n::ScriptType::WEAK == nScript )
+ {
+ nChg = g_pBreakIt->GetBreakIter()->endOfScript(aText,0,nScript);
+ if (nChg < aText.getLength() && nChg >= 0)
+ nScript = g_pBreakIt->GetBreakIter()->getScriptType( aText, nChg );
+ }
+
+ // nNextScriptChg will be evaluated during SwFieldPortion::Format()
+
+ if (nChg < aText.getLength() && nChg >= 0)
+ m_nNextScriptChg = TextFrameIndex(
+ g_pBreakIt->GetBreakIter()->endOfScript(aText, nChg, nScript));
+ else
+ m_nNextScriptChg = TextFrameIndex(aText.getLength());
+
+ SwFontScript nTmp;
+ switch ( nScript ) {
+ case i18n::ScriptType::LATIN : nTmp = SwFontScript::Latin; break;
+ case i18n::ScriptType::ASIAN : nTmp = SwFontScript::CJK; break;
+ case i18n::ScriptType::COMPLEX : nTmp = SwFontScript::CTL; break;
+ default: nTmp = nActual;
+ }
+
+ // #i16354# Change script type for RTL text to CTL.
+ const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
+ // #i98418#
+ const sal_uInt8 nFieldDir = (IsNumberPortion() || IsFootnoteNumPortion())
+ ? rSI.GetDefaultDir()
+ : rSI.DirType(IsFollow() ? rInf.GetIdx() - m_nFieldLen : rInf.GetIdx());
+
+ {
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( aText.getLength(), 0, &nError );
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.getStr()), aText.getLength(), nFieldDir, nullptr, &nError );
+ int32_t nEnd;
+ UBiDiLevel nCurrDir;
+ ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
+ ubidi_close( pBidi );
+ const TextFrameIndex nNextDirChg(nEnd);
+ m_nNextScriptChg = std::min( m_nNextScriptChg, nNextDirChg );
+
+ // #i89825# change the script type also to CTL
+ // if there is no strong LTR char in the LTR run (numbers)
+ if (nCurrDir != UBIDI_RTL &&
+ (UBIDI_LTR != nFieldDir || i18n::ScriptType::COMPLEX == nScript))
+ {
+ nCurrDir = UBIDI_RTL;
+ for( sal_Int32 nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
+ {
+ UCharDirection nCharDir = u_charDirection ( aText[ nCharIdx ]);
+ if ( nCharDir == U_LEFT_TO_RIGHT ||
+ nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
+ nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
+ {
+ nCurrDir = UBIDI_LTR;
+ break;
+ }
+ }
+ }
+
+ if (nCurrDir == UBIDI_RTL)
+ {
+ nTmp = SwFontScript::CTL;
+ // If we decided that this range was RTL after all and the
+ // previous range was complex but clipped to the start of this
+ // range, then extend it to be complex over the additional RTL range
+ if (nScript == i18n::ScriptType::COMPLEX)
+ m_nNextScriptChg = nNextDirChg;
+ }
+ }
+
+ // #i98418#
+ // keep determined script type for footnote portions as preferred script type.
+ // For footnote portions a font can not be created directly - see footnote
+ // portion format method.
+ if ( IsFootnotePortion() )
+ {
+ static_cast<SwFootnotePortion*>(this)->SetPreferredScriptType( nTmp );
+ }
+ else if ( nTmp != nActual )
+ {
+ if( !m_pFont )
+ m_pFont.reset( new SwFont( *rInf.GetFont() ) );
+ m_pFont->SetActual( nTmp );
+ }
+
+}
+
+bool SwFieldPortion::Format( SwTextFormatInfo &rInf )
+{
+ // Scope wegen aDiffText::DTOR!
+ bool bFull = false;
+ bool bEOL = false;
+ TextFrameIndex const nTextRest = TextFrameIndex(rInf.GetText().getLength()) - rInf.GetIdx();
+ {
+ TextFrameIndex nRest;
+ SwFieldSlot aDiffText( &rInf, this );
+ SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
+ aLayoutModeModifier.SetAuto();
+
+ // Field portion has to be split in several parts if
+ // 1. There are script/direction changes inside the field
+ // 2. There are portion breaks (tab, break) inside the field:
+ const TextFrameIndex nOldFullLen = rInf.GetLen();
+ TextFrameIndex nFullLen = rInf.ScanPortionEnd(rInf.GetIdx(), rInf.GetIdx() + nOldFullLen) - rInf.GetIdx();
+ if ( m_nNextScriptChg < nFullLen )
+ {
+ nFullLen = m_nNextScriptChg;
+ rInf.SetHookChar( 0 );
+ }
+ rInf.SetLen( nFullLen );
+
+ if (TextFrameIndex(COMPLETE_STRING) != rInf.GetUnderScorePos() &&
+ rInf.GetUnderScorePos() > rInf.GetIdx() )
+ rInf.SetUnderScorePos( rInf.GetIdx() );
+
+ if( m_pFont )
+ m_pFont->AllocFontCacheId( rInf.GetVsh(), m_pFont->GetActual() );
+
+ SwFontSave aSave( rInf, m_pFont.get() );
+
+ // Length must be 0: the length is set for bFull after format
+ // and passed along in nRest. Or else the old length would be
+ // retained and be used for nRest!
+ SetLen(TextFrameIndex(0));
+ TextFrameIndex const nFollow(IsFollow() ? TextFrameIndex(0) : m_nFieldLen);
+
+ // As odd is may seem: the query for GetLen() must return false due
+ // to the ExpandPortions _after_ aDiffText (see SoftHyphs), caused
+ // by SetFull.
+ if( !nFullLen )
+ {
+ // Don't Init(), as we need height and ascent
+ Width(0);
+ bFull = rInf.Width() <= rInf.GetPos().X();
+ }
+ else
+ {
+ TextFrameIndex const nOldLineStart = rInf.GetLineStart();
+ if( IsFollow() )
+ rInf.SetLineStart(TextFrameIndex(0));
+ rInf.SetNotEOL( nFullLen == nOldFullLen && nTextRest > nFollow );
+
+ // the height depending on the fields font is set,
+ // this is required for SwTextGuess::Guess
+ Height( rInf.GetTextHeight() + rInf.GetFont()->GetTopBorderSpace() +
+ rInf.GetFont()->GetBottomBorderSpace() );
+ // If a kerning portion is inserted after our field portion,
+ // the ascent and height must be known
+ SetAscent( rInf.GetAscent() + rInf.GetFont()->GetTopBorderSpace() );
+ bFull = SwTextPortion::Format( rInf );
+ rInf.SetNotEOL( false );
+ rInf.SetLineStart( nOldLineStart );
+ }
+ TextFrameIndex const nTmpLen = GetLen();
+ bEOL = !nTmpLen && nFollow && bFull;
+ nRest = nOldFullLen - nTmpLen;
+
+ // The char is held in the first position
+ // Unconditionally after format!
+ SetLen( m_bNoLength ? TextFrameIndex(0) : nFollow );
+
+ if( nRest )
+ {
+ // aExpand has not yet been shortened; the new Ofst is a
+ // result of nRest
+ TextFrameIndex nNextOfst = TextFrameIndex(m_aExpand.getLength()) - nRest;
+
+ if ( IsQuoVadisPortion() )
+ nNextOfst = nNextOfst + TextFrameIndex(static_cast<SwQuoVadisPortion*>(this)->GetContText().getLength());
+
+ OUString aNew( m_aExpand.copy(sal_Int32(nNextOfst)) );
+ m_aExpand = m_aExpand.copy(0, sal_Int32(nNextOfst));
+
+ // These characters should not be contained in the follow
+ // field portion. They are handled via the HookChar mechanism.
+ const sal_Unicode nNew = !aNew.isEmpty() ? aNew[0] : 0;
+ switch (nNew)
+ {
+ case CH_BREAK : bFull = true;
+ [[fallthrough]];
+ case ' ' :
+ case CH_TAB :
+ case CHAR_HARDHYPHEN: // non-breaking hyphen
+ case CHAR_SOFTHYPHEN:
+ case CHAR_HARDBLANK:
+ case CHAR_ZWSP :
+ case CHAR_WJ :
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ {
+ aNew = aNew.copy( 1 );
+ ++nNextOfst;
+ break;
+ }
+ default: ;
+ }
+
+ // Even if there is no more text left for a follow field,
+ // we have to build a follow field portion (without font),
+ // otherwise the HookChar mechanism would not work.
+ SwFieldPortion *pField = Clone( aNew );
+ if( !aNew.isEmpty() && !pField->GetFont() )
+ {
+ pField->SetFont( std::make_unique<SwFont>( *rInf.GetFont() ) );
+ }
+ pField->SetFollow( true );
+ SetHasFollow( true );
+
+ // For a newly created field, nNextOffset contains the Offset
+ // of its start of the original string
+ // If a FollowField is created when formatting, this FollowField's
+ // Offset is being held in nNextOffset
+ m_nNextOffset = m_nNextOffset + nNextOfst;
+ pField->SetNextOffset( m_nNextOffset );
+ rInf.SetRest( pField );
+ }
+ }
+
+ if( bEOL && rInf.GetLast() && !rInf.GetUnderflow() )
+ rInf.GetLast()->FormatEOL( rInf );
+ return bFull;
+}
+
+void SwFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ SwFontSave aSave( rInf, m_pFont.get() );
+
+// OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
+ if( Width() && ( !m_bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) && !m_bContentControl )
+ {
+ // A very liberal use of the background
+ rInf.DrawViewOpt( *this, PortionType::Field );
+ SwExpandPortion::Paint( rInf );
+ }
+}
+
+bool SwFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
+{
+ rText = m_aExpand;
+ if( rText.isEmpty() && rInf.OnWin() &&
+ !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
+ SwViewOption::IsFieldShadings() &&
+ !HasFollow() )
+ rText = " ";
+ return true;
+}
+
+void SwFieldPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ sal_Int32 nH = 0;
+ sal_Int32 nW = 0;
+ if (m_pFont)
+ {
+ nH = m_pFont->GetSize(m_pFont->GetActual()).Height();
+ nW = m_pFont->GetSize(m_pFont->GetActual()).Width();
+ }
+ rPH.Special( GetLen(), m_aExpand, GetWhichPor(), nH, nW, m_pFont.get() );
+}
+
+void SwFieldPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFieldPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("expand"), BAD_CAST(m_aExpand.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwPosSize SwFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+{
+ SwFontSave aSave( rInf, m_pFont.get() );
+ SwPosSize aSize( SwExpandPortion::GetTextSize( rInf ) );
+ return aSize;
+}
+
+SwFieldPortion *SwHiddenPortion::Clone(const OUString &rExpand ) const
+{
+ std::unique_ptr<SwFont> pNewFnt;
+ if( m_pFont )
+ pNewFnt.reset(new SwFont( *m_pFont ));
+ return new SwHiddenPortion( rExpand, std::move(pNewFnt) );
+}
+
+void SwHiddenPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( Width() )
+ {
+ SwFontSave aSave( rInf, m_pFont.get() );
+ rInf.DrawViewOpt( *this, PortionType::Hidden );
+ SwExpandPortion::Paint( rInf );
+ }
+}
+
+bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
+{
+ // Do not query for IsHidden()!
+ return SwFieldPortion::GetExpText( rInf, rText );
+}
+
+SwNumberPortion::SwNumberPortion( const OUString &rExpand,
+ std::unique_ptr<SwFont> pFont,
+ const bool bLft,
+ const bool bCntr,
+ const sal_uInt16 nMinDst,
+ const bool bLabelAlignmentPosAndSpaceModeActive )
+ : SwFieldPortion( rExpand, std::move(pFont) ),
+ m_nFixWidth(0),
+ m_nMinDist( nMinDst ),
+ mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
+{
+ SetWhichPor( PortionType::Number );
+ SetLeft( bLft );
+ SetHide( false );
+ SetCenter( bCntr );
+}
+
+TextFrameIndex SwNumberPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+{
+ return TextFrameIndex(0);
+}
+
+SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const
+{
+ std::unique_ptr<SwFont> pNewFnt;
+ if( m_pFont )
+ pNewFnt.reset(new SwFont( *m_pFont ));
+
+ return new SwNumberPortion( rExpand, std::move(pNewFnt), IsLeft(), IsCenter(),
+ m_nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
+}
+
+/**
+ * We can create multiple NumFields
+ * Tricky, if one enters enough previous-text in the dialog box
+ * to cause the line to overflow
+ * We need to keep the Fly's evasion tactics in mind
+ */
+bool SwNumberPortion::Format( SwTextFormatInfo &rInf )
+{
+ SetHide( false );
+ const bool bFull = SwFieldPortion::Format( rInf );
+ SetLen(TextFrameIndex(0));
+ // a numbering portion can be contained in a rotated portion!!!
+ m_nFixWidth = rInf.IsMulti() ? Height() : Width();
+ rInf.SetNumDone( !rInf.GetRest() );
+ if( rInf.IsNumDone() )
+ {
+// SetAscent( rInf.GetAscent() );
+ OSL_ENSURE( Height() && mnAscent, "NumberPortions without Height | Ascent" );
+
+ tools::Long nDiff( 0 );
+
+ if ( !mbLabelAlignmentPosAndSpaceModeActive )
+ {
+ if (!rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
+ // #i32902#
+ !IsFootnoteNumPortion() )
+ {
+ nDiff = rInf.Left()
+ + rInf.GetTextFrame()->GetTextNodeForParaProps()->
+ GetSwAttrSet().GetLRSpace().GetTextFirstLineOffset()
+ - rInf.First()
+ + rInf.ForcedLeftMargin();
+ }
+ else
+ {
+ nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
+ }
+ }
+ // The text part of the numbering should always at least
+ // start at the left margin
+ if( nDiff < 0 )
+ nDiff = 0;
+ else if ( nDiff > rInf.X() )
+ nDiff -= rInf.X();
+ else
+ nDiff = 0;
+
+ if( nDiff < m_nFixWidth + m_nMinDist )
+ nDiff = m_nFixWidth + m_nMinDist;
+
+ // Numbering evades the Fly, no nDiff in the second round
+ // Tricky special case: FlyFrame is in an Area we're just about to
+ // acquire
+ // The NumberPortion is marked as hidden
+ const bool bFly = rInf.GetFly() ||
+ ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
+ if( nDiff > rInf.Width() )
+ {
+ nDiff = rInf.Width();
+ if ( bFly )
+ SetHide( true );
+ }
+
+ // A numbering portion can be inside a SwRotatedPortion. Then the
+ // Height has to be changed
+ if ( rInf.IsMulti() )
+ {
+ if ( Height() < nDiff )
+ Height( nDiff );
+ }
+ else if( Width() < nDiff )
+ Width( nDiff );
+ }
+ return bFull;
+}
+
+
+/**
+ * A FormatEOL indicates that the subsequent text did not fit onto
+ * the line anymore. In order for the Numbering to follow through,
+ * we hide this NumberPortion
+ */
+void SwNumberPortion::FormatEOL( SwTextFormatInfo& )
+{
+
+ // This caused trouble with flys anchored as characters.
+ // If one of these is numbered but does not fit to the line,
+ // it calls this function, causing a loop because both the number
+ // portion and the fly portion go to the next line
+// SetHide( true );
+}
+
+
+/**
+ * A hidden NumberPortion is not displayed, unless there are TextPortions in
+ * this line or there's just one line at all
+ */
+void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
+ {
+ SwLinePortion *pTmp = GetNextPortion();
+ while ( pTmp && !pTmp->InTextGrp() )
+ pTmp = pTmp->GetNextPortion();
+ if ( !pTmp )
+ return;
+ }
+
+ // calculate the width of the number portion, including follows
+ const sal_uInt16 nOldWidth = Width();
+ sal_uInt16 nSumWidth = 0;
+ sal_uInt16 nOffset = 0;
+
+ const SwLinePortion* pTmp = this;
+ while ( pTmp && pTmp->InNumberGrp() )
+ {
+ nSumWidth = nSumWidth + pTmp->Width();
+ if ( static_cast<const SwNumberPortion*>(pTmp)->HasFollow() )
+ pTmp = pTmp->GetNextPortion();
+ else
+ {
+ nOffset = pTmp->Width() - static_cast<const SwNumberPortion*>(pTmp)->m_nFixWidth;
+ break;
+ }
+ }
+
+ // The master portion takes care for painting the background of the
+ // follow field portions
+ if ( ! IsFollow() )
+ {
+ SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
+ pThis->Width( nSumWidth );
+ rInf.DrawViewOpt( *this, PortionType::Number );
+ pThis->Width( nOldWidth );
+ }
+
+ if( m_aExpand.isEmpty() )
+ return;
+
+ const SwFont *pTmpFnt = rInf.GetFont();
+ bool bPaintSpace = ( LINESTYLE_NONE != pTmpFnt->GetUnderline() ||
+ LINESTYLE_NONE != pTmpFnt->GetOverline() ||
+ STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
+ !pTmpFnt->IsWordLineMode();
+ if( bPaintSpace && m_pFont )
+ bPaintSpace = ( LINESTYLE_NONE != m_pFont->GetUnderline() ||
+ LINESTYLE_NONE != m_pFont->GetOverline() ||
+ STRIKEOUT_NONE != m_pFont->GetStrikeout() ) &&
+ !m_pFont->IsWordLineMode();
+
+ SwFontSave aSave( rInf, m_pFont.get() );
+
+ if( m_nFixWidth == Width() && ! HasFollow() )
+ SwExpandPortion::Paint( rInf );
+ else
+ {
+ // logical const: reset width
+ SwNumberPortion *pThis = const_cast<SwNumberPortion*>(this);
+ bPaintSpace = bPaintSpace && m_nFixWidth < nOldWidth;
+ sal_uInt16 nSpaceOffs = m_nFixWidth;
+ pThis->Width( m_nFixWidth );
+
+ if( ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
+ ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() ) )
+ SwExpandPortion::Paint( rInf );
+ else
+ {
+ SwTextPaintInfo aInf( rInf );
+ if( nOffset < m_nMinDist )
+ nOffset = 0;
+ else
+ {
+ if( IsCenter() )
+ {
+ /* #110778# a / 2 * 2 == a is not a tautology */
+ sal_uInt16 nTmpOffset = nOffset;
+ nOffset /= 2;
+ if( nOffset < m_nMinDist )
+ nOffset = nTmpOffset - m_nMinDist;
+ }
+ else
+ nOffset = nOffset - m_nMinDist;
+ }
+ aInf.X( aInf.X() + nOffset );
+ SwExpandPortion::Paint( aInf );
+ if( bPaintSpace )
+ nSpaceOffs = nSpaceOffs + nOffset;
+ }
+ if( bPaintSpace && nOldWidth > nSpaceOffs )
+ {
+ SwTextPaintInfo aInf( rInf );
+ aInf.X( aInf.X() + nSpaceOffs );
+
+ // #i53199# Adjust position of underline:
+ if ( rInf.GetUnderFnt() )
+ {
+ const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
+ rInf.GetUnderFnt()->SetPos( aNewPos );
+ }
+
+ pThis->Width( nOldWidth - nSpaceOffs + 12 );
+ {
+ SwTextSlot aDiffText( &aInf, this, true, false, " " );
+ aInf.DrawText( *this, aInf.GetLen(), true );
+ }
+ }
+ pThis->Width( nOldWidth );
+ }
+}
+
+SwBulletPortion::SwBulletPortion( const sal_UCS4 cBullet,
+ std::u16string_view rBulletFollowedBy,
+ std::unique_ptr<SwFont> pFont,
+ const bool bLft,
+ const bool bCntr,
+ const sal_uInt16 nMinDst,
+ const bool bLabelAlignmentPosAndSpaceModeActive )
+ : SwNumberPortion( OUString(&cBullet, 1) + rBulletFollowedBy,
+ std::move(pFont), bLft, bCntr, nMinDst,
+ bLabelAlignmentPosAndSpaceModeActive )
+{
+ SetWhichPor( PortionType::Bullet );
+}
+
+#define GRFNUM_SECURE 10
+
+SwGrfNumPortion::SwGrfNumPortion(
+ const OUString& rGraphicFollowedBy,
+ const SvxBrushItem* pGrfBrush, OUString const & referer,
+ const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize,
+ const bool bLft, const bool bCntr, const sal_uInt16 nMinDst,
+ const bool bLabelAlignmentPosAndSpaceModeActive ) :
+ SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst,
+ bLabelAlignmentPosAndSpaceModeActive ),
+ m_pBrush( new SvxBrushItem(RES_BACKGROUND) ), m_nId( 0 )
+{
+ SetWhichPor( PortionType::GrfNum );
+ SetAnimated( false );
+ m_bReplace = false;
+ if( pGrfBrush )
+ {
+ m_pBrush.reset(pGrfBrush->Clone());
+ const Graphic* pGraph = pGrfBrush->GetGraphic(referer);
+ if( pGraph )
+ SetAnimated( pGraph->IsAnimated() );
+ else
+ m_bReplace = true;
+ }
+ if( pGrfOrient )
+ {
+ m_nYPos = pGrfOrient->GetPos();
+ m_eOrient = pGrfOrient->GetVertOrient();
+ }
+ else
+ {
+ m_nYPos = 0;
+ m_eOrient = text::VertOrientation::TOP;
+ }
+ Width( rGrfSize.Width() + 2 * GRFNUM_SECURE );
+ m_nFixWidth = Width();
+ m_nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
+ Height( sal_uInt16(m_nGrfHeight) );
+ m_bNoPaint = false;
+}
+
+SwGrfNumPortion::~SwGrfNumPortion()
+{
+ if ( IsAnimated() )
+ {
+ Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
+ if (pGraph)
+ pGraph->StopAnimation( nullptr, m_nId );
+ }
+ m_pBrush.reset();
+}
+
+void SwGrfNumPortion::StopAnimation( const OutputDevice* pOut )
+{
+ if ( IsAnimated() )
+ {
+ Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
+ if (pGraph)
+ pGraph->StopAnimation( pOut, m_nId );
+ }
+}
+
+bool SwGrfNumPortion::Format( SwTextFormatInfo &rInf )
+{
+ SetHide( false );
+// Width( nFixWidth );
+ sal_uInt16 nFollowedByWidth( 0 );
+ if ( mbLabelAlignmentPosAndSpaceModeActive )
+ {
+ SwFieldPortion::Format( rInf );
+ nFollowedByWidth = Width();
+ SetLen(TextFrameIndex(0));
+ }
+ Width( m_nFixWidth + nFollowedByWidth );
+ const bool bFull = rInf.Width() < rInf.X() + Width();
+ const bool bFly = rInf.GetFly() ||
+ ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
+ SetAscent( GetRelPos() > 0 ? GetRelPos() : 0 );
+ if( GetAscent() > Height() )
+ Height( GetAscent() );
+
+ if( bFull )
+ {
+ Width( rInf.Width() - rInf.X() );
+ if( bFly )
+ {
+ SetLen(TextFrameIndex(0));
+ m_bNoPaint = true;
+ rInf.SetNumDone( false );
+ return true;
+ }
+ }
+ rInf.SetNumDone( true );
+// long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
+ tools::Long nDiff = mbLabelAlignmentPosAndSpaceModeActive
+ ? 0
+ : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
+ // The TextPortion should at least always start on the
+ // left margin
+ if( nDiff < 0 )
+ nDiff = 0;
+ else if ( nDiff > rInf.X() )
+ nDiff -= rInf.X();
+ if( nDiff < m_nFixWidth + m_nMinDist )
+ nDiff = m_nFixWidth + m_nMinDist;
+
+ // Numbering evades Fly, no nDiff in the second round
+ // Tricky special case: FlyFrame is in the Area we were just
+ // about to get a hold of.
+ // The NumberPortion is marked as hidden
+ if( nDiff > rInf.Width() )
+ {
+ nDiff = rInf.Width();
+ if( bFly )
+ SetHide( true );
+ }
+
+ if( Width() < nDiff )
+ Width( nDiff );
+ return bFull;
+}
+
+
+/**
+ * A hidden NumberPortion is not displayed, unless there are TextPortions in
+ * this line or there's only one line at all
+ */
+void SwGrfNumPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( m_bNoPaint )
+ return;
+ if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
+ {
+ SwLinePortion *pTmp = GetNextPortion();
+ while ( pTmp && !pTmp->InTextGrp() )
+ pTmp = pTmp->GetNextPortion();
+ if ( !pTmp )
+ return;
+ }
+ Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
+ tools::Long nTmpWidth = std::max( tools::Long(0), static_cast<tools::Long>(m_nFixWidth - 2 * GRFNUM_SECURE) );
+ Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
+
+ const bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
+ ( IsLeft() && ! rInf.GetTextFrame()->IsRightToLeft() ) ||
+ ( ! IsLeft() && ! IsCenter() && rInf.GetTextFrame()->IsRightToLeft() );
+
+ if( m_nFixWidth < Width() && !bTmpLeft )
+ {
+ sal_uInt16 nOffset = Width() - m_nFixWidth;
+ if( nOffset < m_nMinDist )
+ nOffset = 0;
+ else
+ {
+ if( IsCenter() )
+ {
+ nOffset /= 2;
+ if( nOffset < m_nMinDist )
+ nOffset = Width() - m_nFixWidth - m_nMinDist;
+ }
+ else
+ nOffset = nOffset - m_nMinDist;
+ }
+ aPos.AdjustX(nOffset );
+ }
+
+ if( m_bReplace )
+ {
+ const tools::Long nTmpH = GetNextPortion() ? GetNextPortion()->GetAscent() : 120;
+ aSize = Size( nTmpH, nTmpH );
+ aPos.setY( rInf.Y() - nTmpH );
+ }
+ SwRect aTmp( aPos, aSize );
+
+ bool bDraw = true;
+
+ if ( IsAnimated() )
+ {
+ bDraw = !rInf.GetOpt().IsGraphic();
+ if( !m_nId )
+ {
+ SetId( reinterpret_cast<sal_IntPtr>( rInf.GetTextFrame() ) );
+ rInf.GetTextFrame()->SetAnimation();
+ }
+ if( aTmp.Overlaps( rInf.GetPaintRect() ) && !bDraw )
+ {
+ rInf.NoteAnimation();
+ const SwViewShell* pViewShell = rInf.GetVsh();
+
+ // virtual device, not pdf export
+ if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
+ pViewShell && pViewShell->GetWin() )
+ {
+ Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
+ if (pGraph)
+ pGraph->StopAnimation(nullptr,m_nId);
+ rInf.GetTextFrame()->getRootFrame()->GetCurrShell()->InvalidateWindows( aTmp );
+ }
+
+ else if ( pViewShell &&
+ !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
+ !pViewShell->IsPreview() &&
+ // #i9684# Stop animation during printing/pdf export.
+ pViewShell->GetWin() )
+ {
+ Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
+ if (pGraph)
+ {
+ const OutputDevice* pOut = rInf.GetOut();
+ assert(pOut);
+ pGraph->StartAnimation(
+ *const_cast<OutputDevice*>(pOut), aPos, aSize, m_nId);
+ }
+ }
+
+ // pdf export, printing, preview, stop animations...
+ else
+ bDraw = true;
+ }
+ if( bDraw )
+ {
+
+ Graphic* pGraph = const_cast<Graphic*>(m_pBrush->GetGraphic());
+ if (pGraph)
+ pGraph->StopAnimation( nullptr, m_nId );
+ }
+ }
+
+ SwRect aRepaint( rInf.GetPaintRect() );
+ const SwTextFrame& rFrame = *rInf.GetTextFrame();
+ if( rFrame.IsVertical() )
+ {
+ rFrame.SwitchHorizontalToVertical( aTmp );
+ rFrame.SwitchHorizontalToVertical( aRepaint );
+ }
+
+ if( rFrame.IsRightToLeft() )
+ {
+ rFrame.SwitchLTRtoRTL( aTmp );
+ rFrame.SwitchLTRtoRTL( aRepaint );
+ }
+
+ if( bDraw && aTmp.HasArea() )
+ {
+ const OutputDevice* pOut = rInf.GetOut();
+ assert(pOut);
+ DrawGraphic( m_pBrush.get(), *const_cast<OutputDevice*>(pOut),
+ aTmp, aRepaint, m_bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
+ }
+}
+
+void SwGrfNumPortion::SetBase( tools::Long nLnAscent, tools::Long nLnDescent,
+ tools::Long nFlyAsc, tools::Long nFlyDesc )
+{
+ if ( GetOrient() == text::VertOrientation::NONE )
+ return;
+
+ SetRelPos( 0 );
+ if ( GetOrient() == text::VertOrientation::CENTER )
+ SetRelPos( GetGrfHeight() / 2 );
+ else if ( GetOrient() == text::VertOrientation::TOP )
+ SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
+ else if ( GetOrient() == text::VertOrientation::BOTTOM )
+ ;
+ else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
+ SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
+ else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
+ SetRelPos( nLnAscent );
+ else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
+ SetRelPos( GetGrfHeight() - nLnDescent );
+ else
+ {
+ if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
+ {
+ // If I'm as large as the line, I do not need to adjust
+ // at the line; I'll leave the max. ascent unchanged
+ SetRelPos( nFlyAsc );
+ }
+ else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
+ SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
+ else if ( GetOrient() == text::VertOrientation::LINE_TOP )
+ SetRelPos( nFlyAsc );
+ else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
+ SetRelPos( GetGrfHeight() - nFlyDesc );
+ }
+}
+
+void SwTextFrame::StopAnimation( const OutputDevice* pOut )
+{
+ OSL_ENSURE( HasAnimation(), "SwTextFrame::StopAnimation: Which Animation?" );
+ if( !HasPara() )
+ return;
+
+ SwLineLayout *pLine = GetPara();
+ while( pLine )
+ {
+ SwLinePortion *pPor = pLine->GetNextPortion();
+ while( pPor )
+ {
+ if( pPor->IsGrfNumPortion() )
+ static_cast<SwGrfNumPortion*>(pPor)->StopAnimation( pOut );
+ // The NumberPortion is always at the first char,
+ // which means we can cancel as soon as we've reached a portion
+ // with a length > 0
+ pPor = pPor->GetLen() ? nullptr : pPor->GetNextPortion();
+ }
+ pLine = pLine->GetLen() ? nullptr : pLine->GetNext();
+ }
+}
+
+/**
+ * Initializes the script array and clears the width array
+ */
+SwCombinedPortion::SwCombinedPortion( const OUString &rText )
+ : SwFieldPortion( rText )
+ , m_aWidth{ static_cast<sal_uInt16>(0),
+ static_cast<sal_uInt16>(0),
+ static_cast<sal_uInt16>(0) }
+ , m_nUpPos(0)
+ , m_nLowPos(0)
+ , m_nProportion(55)
+{
+ SetLen(TextFrameIndex(1));
+ SetWhichPor( PortionType::Combined );
+ if( m_aExpand.getLength() > 6 )
+ m_aExpand = m_aExpand.copy( 0, 6 );
+
+ // Initialization of the scripttype array,
+ // the arrays of width and position are filled by the format function
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ SwFontScript nScr = SW_SCRIPTS;
+ for( sal_Int32 i = 0; i < rText.getLength(); ++i )
+ {
+ switch ( g_pBreakIt->GetBreakIter()->getScriptType( rText, i ) ) {
+ case i18n::ScriptType::LATIN : nScr = SwFontScript::Latin; break;
+ case i18n::ScriptType::ASIAN : nScr = SwFontScript::CJK; break;
+ case i18n::ScriptType::COMPLEX : nScr = SwFontScript::CTL; break;
+ }
+ m_aScrType[i] = nScr;
+ }
+}
+
+void SwCombinedPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ OSL_ENSURE(GetLen() <= TextFrameIndex(1), "SwFieldPortion::Paint: rest-portion pollution?");
+ if( !Width() )
+ return;
+
+ rInf.DrawBackBrush( *this );
+ rInf.DrawViewOpt( *this, PortionType::Field );
+
+ // do we have to repaint a post it portion?
+ if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
+ mpNextPortion->PrePaint( rInf, this );
+
+ const sal_Int32 nCount = m_aExpand.getLength();
+ if( !nCount )
+ return;
+ OSL_ENSURE( nCount < 7, "Too much combined characters" );
+
+ // the first character of the second row
+ const sal_Int32 nTop = ( nCount + 1 ) / 2;
+
+ SwFont aTmpFont( *rInf.GetFont() );
+ aTmpFont.SetProportion( m_nProportion ); // a smaller font
+ SwFontSave aFontSave( rInf, &aTmpFont );
+
+ Point aOldPos = rInf.GetPos();
+ Point aOutPos( aOldPos.X(), aOldPos.Y() - m_nUpPos );// Y of the first row
+ for( sal_Int32 i = 0 ; i < nCount; ++i )
+ {
+ if( i == nTop ) // change the row
+ aOutPos.setY( aOldPos.Y() + m_nLowPos ); // Y of the second row
+ aOutPos.setX( aOldPos.X() + m_aPos[i] ); // X position
+ const SwFontScript nAct = m_aScrType[i]; // script type
+ aTmpFont.SetActual( nAct );
+
+ // if there're more than 4 characters to display, we choose fonts
+ // with 2/3 of the original font width.
+ if( m_aWidth[ nAct ] )
+ {
+ Size aTmpSz = aTmpFont.GetSize( nAct );
+ if( aTmpSz.Width() != m_aWidth[ nAct ] )
+ {
+ aTmpSz.setWidth( m_aWidth[ nAct ] );
+ aTmpFont.SetSize( aTmpSz, nAct );
+ }
+ }
+ const_cast<SwTextPaintInfo&>(rInf).SetPos( aOutPos );
+ rInf.DrawText(m_aExpand, *this, TextFrameIndex(i), TextFrameIndex(1));
+ }
+ // rInf is const, so we have to take back our manipulations
+ const_cast<SwTextPaintInfo&>(rInf).SetPos( aOldPos );
+
+}
+
+bool SwCombinedPortion::Format( SwTextFormatInfo &rInf )
+{
+ const sal_Int32 nCount = m_aExpand.getLength();
+ if( !nCount )
+ {
+ Width( 0 );
+ return false;
+ }
+
+ OSL_ENSURE( nCount < 7, "Too much combined characters" );
+
+ // If there are leading "weak"-scripttyped characters in this portion,
+ // they get the actual scripttype.
+ for( sal_Int32 i = 0; i < nCount && SW_SCRIPTS == m_aScrType[i]; ++i )
+ m_aScrType[i] = rInf.GetFont()->GetActual();
+ if( nCount > 4 )
+ {
+ // more than four? Ok, then we need the 2/3 font width
+ for( sal_Int32 i = 0; i < m_aExpand.getLength(); ++i )
+ {
+ OSL_ENSURE( m_aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
+ if( !m_aWidth[ m_aScrType[i] ] )
+ {
+ rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( m_aScrType[i] ) );
+ m_aWidth[ m_aScrType[i] ] =
+ o3tl::narrowing<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetFontSize().Width() / 3);
+ }
+ }
+ }
+
+ const sal_Int32 nTop = ( nCount + 1 ) / 2; // the first character of the second line
+ SwViewShell *pSh = rInf.GetTextFrame()->getRootFrame()->GetCurrShell();
+ SwFont aTmpFont( *rInf.GetFont() );
+ SwFontSave aFontSave( rInf, &aTmpFont );
+ m_nProportion = 55;
+ // In nMainAscent/Descent we store the ascent and descent
+ // of the original surrounding font
+ sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
+ sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
+ const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
+ nMainDescent = nMainDescent - nMainAscent;
+ // we start with a 50% font, but if we notice that the combined portion
+ // becomes bigger than the surrounding font, we check 45% and maybe 40%.
+ do
+ {
+ m_nProportion -= 5;
+ aTmpFont.SetProportion( m_nProportion );
+ memset( &m_aPos, 0, sizeof(m_aPos) );
+ nMaxDescent = 0;
+ nMaxAscent = 0;
+ nMaxWidth = 0;
+ m_nUpPos = m_nLowPos = 0;
+
+ // Now we get the width of all characters.
+ // The ascent and the width of the first line are stored in the
+ // ascent member of the portion, the descent in nLowPos.
+ // The ascent, descent and width of the second line are stored in the
+ // local nMaxAscent, nMaxDescent and nMaxWidth variables.
+ for( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ SwFontScript nScrp = m_aScrType[i];
+ aTmpFont.SetActual( nScrp );
+ if( m_aWidth[ nScrp ] )
+ {
+ Size aFontSize( aTmpFont.GetSize( nScrp ) );
+ aFontSize.setWidth( m_aWidth[ nScrp ] );
+ aTmpFont.SetSize( aFontSize, nScrp );
+ }
+
+ SwDrawTextInfo aDrawInf(pSh, *rInf.GetOut(), m_aExpand, i, 1);
+ Size aSize = aTmpFont.GetTextSize_( aDrawInf );
+ const sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
+ m_aPos[ i ] = o3tl::narrowing<sal_uInt16>(aSize.Width());
+ if( i == nTop ) // enter the second line
+ {
+ m_nLowPos = nMaxDescent;
+ Height( nMaxDescent + nMaxAscent );
+ Width( nMaxWidth );
+ SetAscent( nMaxAscent );
+ nMaxAscent = 0;
+ nMaxDescent = 0;
+ nMaxWidth = 0;
+ }
+ nMaxWidth = nMaxWidth + m_aPos[ i ];
+ if( nAsc > nMaxAscent )
+ nMaxAscent = nAsc;
+ if( aSize.Height() - nAsc > nMaxDescent )
+ nMaxDescent = aSize.Height() - nAsc;
+ }
+ // for one or two characters we double the width of the portion
+ if( nCount < 3 )
+ {
+ nMaxWidth *= 2;
+ Width( 2*Width() );
+ if( nCount < 2 )
+ {
+ Height( nMaxAscent + nMaxDescent );
+ m_nLowPos = nMaxDescent;
+ }
+ }
+ Height( Height() + nMaxDescent + nMaxAscent );
+ m_nUpPos = nMaxAscent;
+ SetAscent( Height() - nMaxDescent - m_nLowPos );
+ } while( m_nProportion > 40 && ( GetAscent() > nMainAscent ||
+ Height() - GetAscent() > nMainDescent ) );
+ // if the combined portion is smaller than the surrounding text,
+ // the portion grows. This looks better, if there's a character background.
+ if( GetAscent() < nMainAscent )
+ {
+ Height( Height() + nMainAscent - GetAscent() );
+ SetAscent( nMainAscent );
+ }
+ if( Height() < nMainAscent + nMainDescent )
+ Height( nMainAscent + nMainDescent );
+
+ // We calculate the x positions of the characters in both lines...
+ sal_uInt16 nTopDiff = 0;
+ sal_uInt16 nBotDiff = 0;
+ if( nMaxWidth > Width() )
+ {
+ nTopDiff = ( nMaxWidth - Width() ) / 2;
+ Width( nMaxWidth );
+ }
+ else
+ nBotDiff = ( Width() - nMaxWidth ) / 2;
+ switch( nTop)
+ {
+ case 3: m_aPos[1] = m_aPos[0] + nTopDiff;
+ [[fallthrough]];
+ case 2: m_aPos[nTop-1] = Width() - m_aPos[nTop-1];
+ }
+ m_aPos[0] = 0;
+ switch( nCount )
+ {
+ case 5: m_aPos[4] = m_aPos[3] + nBotDiff;
+ [[fallthrough]];
+ case 3: m_aPos[nTop] = nBotDiff; break;
+ case 6: m_aPos[4] = m_aPos[3] + nBotDiff;
+ [[fallthrough]];
+ case 4: m_aPos[nTop] = 0;
+ [[fallthrough]];
+ case 2: m_aPos[nCount-1] = Width() - m_aPos[nCount-1];
+ }
+
+ // Does the combined portion fit the line?
+ const bool bFull = rInf.Width() < rInf.X() + Width();
+ if( bFull )
+ {
+ if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFieldGrp()
+ || !static_cast<SwFieldPortion*>(rInf.GetLast())->IsFollow() ) )
+ Width( rInf.Width() - rInf.X() );
+ else
+ {
+ Truncate();
+ Width( 0 );
+ SetLen(TextFrameIndex(0));
+ if( rInf.GetLast() )
+ rInf.GetLast()->FormatEOL( rInf );
+ }
+ }
+ return bFull;
+}
+
+sal_uInt16 SwCombinedPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+{
+ if( !GetLen() ) // for the dummy part at the end of the line, where
+ return 0; // the combined portion doesn't fit.
+ return SwFieldPortion::GetViewWidth( rInf );
+}
+
+SwFieldPortion *SwFieldFormDropDownPortion::Clone(const OUString &rExpand) const
+{
+ return new SwFieldFormDropDownPortion(m_pFieldMark, rExpand);
+}
+
+void SwFieldFormDropDownPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ SwFieldPortion::Paint( rInf );
+
+ ::sw::mark::DropDownFieldmark* pDropDownField = dynamic_cast< ::sw::mark::DropDownFieldmark* >(m_pFieldMark);
+ if(pDropDownField)
+ {
+ SwRect aPaintArea;
+ rInf.CalcRect( *this, &aPaintArea );
+ pDropDownField->SetPortionPaintArea(aPaintArea);
+ }
+}
+
+SwFieldPortion *SwFieldFormDatePortion::Clone(const OUString &/*rExpand*/) const
+{
+ return new SwFieldFormDatePortion(m_pFieldMark, m_bStart);
+}
+
+void SwFieldFormDatePortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ SwFieldPortion::Paint( rInf );
+
+ ::sw::mark::DateFieldmark* pDateField = dynamic_cast< ::sw::mark::DateFieldmark* >(m_pFieldMark);
+ if(pDateField)
+ {
+ SwRect aPaintArea;
+ rInf.CalcRect( *this, &aPaintArea );
+ if(m_bStart)
+ pDateField->SetPortionPaintAreaStart(aPaintArea);
+ else
+ pDateField->SetPortionPaintAreaEnd(aPaintArea);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porfld.hxx b/sw/source/core/text/porfld.hxx
new file mode 100644
index 000000000..84141a340
--- /dev/null
+++ b/sw/source/core/text/porfld.hxx
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <swtypes.hxx>
+#include <swfont.hxx>
+#include "porexp.hxx"
+#include <o3tl/enumarray.hxx>
+
+class SvxBrushItem;
+class SwFormatVertOrient;
+
+class SwFieldPortion : public SwExpandPortion
+{
+ friend class SwTextFormatter;
+protected:
+ OUString m_aExpand; // The expanded field
+ std::unique_ptr<SwFont> m_pFont; // For multi-line fields
+ TextFrameIndex m_nNextOffset; // Offset of the follow in the original string
+ TextFrameIndex m_nNextScriptChg;
+ TextFrameIndex m_nFieldLen; //< Length of field text, 1 for normal fields, any number for input fields
+ // TODO ^ do we need this as member or is base class len enough?
+ sal_uInt16 m_nViewWidth; // Screen width for empty fields
+ bool m_bFollow : 1; // 2nd or later part of a field
+ bool m_bLeft : 1; // Used by SwNumberPortion
+ bool m_bHide : 1; // Used by SwNumberPortion
+ bool m_bCenter : 1; // Used by SwNumberPortion
+ bool m_bHasFollow : 1; // Continues on the next line
+ bool m_bAnimated : 1; // Used by SwGrfNumPortion
+ bool m_bNoPaint : 1; // Used by SwGrfNumPortion
+ bool m_bReplace : 1; // Used by SwGrfNumPortion
+ const bool m_bPlaceHolder : 1;
+ bool m_bNoLength : 1; // HACK for meta suffix (no CH_TXTATR)
+ bool m_bContentControl = false;
+
+ void SetFont( std::unique_ptr<SwFont> pNew ) { m_pFont = std::move(pNew); }
+ bool IsNoLength() const { return m_bNoLength; }
+ void SetNoLength() { m_bNoLength = true; }
+
+public:
+ SwFieldPortion( const SwFieldPortion& rField );
+ SwFieldPortion(const OUString &rExpand, std::unique_ptr<SwFont> pFnt = nullptr, bool bPlaceHolder = false, TextFrameIndex nLen = TextFrameIndex(1));
+ virtual ~SwFieldPortion() override;
+
+ sal_uInt16 m_nAttrFieldType;
+ void TakeNextOffset( const SwFieldPortion* pField );
+ void CheckScript( const SwTextSizeInfo &rInf );
+ bool HasFont() const { return nullptr != m_pFont; }
+ // #i89179# - made public
+ const SwFont *GetFont() const { return m_pFont.get(); }
+
+ const OUString& GetExp() const { return m_aExpand; }
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+
+ // Empty fields are also allowed
+ virtual SwLinePortion *Compress() override;
+
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+
+ bool IsFollow() const { return m_bFollow; }
+ void SetFollow( bool bNew ) { m_bFollow = bNew; }
+
+ bool IsLeft() const { return m_bLeft; }
+ void SetLeft( bool bNew ) { m_bLeft = bNew; }
+
+ bool IsHide() const { return m_bHide; }
+ void SetHide( bool bNew ) { m_bHide = bNew; }
+
+ bool IsCenter() const { return m_bCenter; }
+ void SetCenter( bool bNew ) { m_bCenter = bNew; }
+
+ bool HasFollow() const { return m_bHasFollow; }
+ void SetHasFollow( bool bNew ) { m_bHasFollow = bNew; }
+
+ TextFrameIndex GetNextOffset() const { return m_nNextOffset; }
+ void SetNextOffset(TextFrameIndex nNew) { m_nNextOffset = nNew; }
+
+ TextFrameIndex GetFieldLen() const { return m_nFieldLen; }
+
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand ) const;
+
+ // Extra GetTextSize because of pFnt
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void SetContentControl(bool bContentControl) { m_bContentControl = bContentControl; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+/**
+ * Distinguish only for painting/hide
+ */
+class SwHiddenPortion : public SwFieldPortion
+{
+public:
+ SwHiddenPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFntL = nullptr )
+ : SwFieldPortion( rExpand, std::move(pFntL) )
+ { SetLen(TextFrameIndex(1)); SetWhichPor( PortionType::Hidden ); }
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand ) const override;
+};
+
+class SwNumberPortion : public SwFieldPortion
+{
+protected:
+ sal_uInt16 m_nFixWidth; // See Glues
+ sal_uInt16 m_nMinDist; // Minimal distance to the text
+ bool mbLabelAlignmentPosAndSpaceModeActive;
+
+public:
+ SwNumberPortion( const OUString &rExpand,
+ std::unique_ptr<SwFont> pFnt,
+ const bool bLeft,
+ const bool bCenter,
+ const sal_uInt16 nMinDst,
+ const bool bLabelAlignmentPosAndSpaceModeActive );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand ) const override;
+ virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
+};
+
+class SwBulletPortion : public SwNumberPortion
+{
+public:
+ SwBulletPortion( const sal_UCS4 cCh,
+ std::u16string_view rBulletFollowedBy,
+ std::unique_ptr<SwFont> pFnt,
+ const bool bLeft,
+ const bool bCenter,
+ const sal_uInt16 nMinDst,
+ const bool bLabelAlignmentPosAndSpaceModeActive );
+};
+
+class SwGrfNumPortion : public SwNumberPortion
+{
+ std::unique_ptr<SvxBrushItem> m_pBrush;
+ tools::Long m_nId; // For StopAnimation
+ SwTwips m_nYPos; // _Always_ contains the current RelPos
+ SwTwips m_nGrfHeight;
+ sal_Int16 m_eOrient;
+public:
+ SwGrfNumPortion( const OUString& rGraphicFollowedBy,
+ const SvxBrushItem* pGrfBrush,
+ OUString const & referer,
+ const SwFormatVertOrient* pGrfOrient,
+ const Size& rGrfSize,
+ const bool bLeft,
+ const bool bCenter,
+ const sal_uInt16 nMinDst,
+ const bool bLabelAlignmentPosAndSpaceModeActive );
+ virtual ~SwGrfNumPortion() override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+
+ void SetBase( tools::Long nLnAscent, tools::Long nLnDescent,
+ tools::Long nFlyAscent, tools::Long nFlyDescent );
+
+ void StopAnimation( const OutputDevice* pOut );
+
+ bool IsAnimated() const { return m_bAnimated; }
+ void SetAnimated( bool bNew ) { m_bAnimated = bNew; }
+ void SetRelPos( SwTwips nNew ) { m_nYPos = nNew; }
+ void SetId( tools::Long nNew ) const
+ { const_cast<SwGrfNumPortion*>(this)->m_nId = nNew; }
+ SwTwips GetRelPos() const { return m_nYPos; }
+ SwTwips GetGrfHeight() const { return m_nGrfHeight; }
+ sal_Int16 GetOrient() const { return m_eOrient; }
+};
+
+/**
+ * Used in for asian layout specialities to display up to six characters
+ * in 2 rows and 2-3 columns.
+ * E.g.: <pre>
+ * A.. A.. A.B A.B A.B.C A.B.C
+ * ... ..B .C. C.D .D.E. D.E.F
+ * </pre>
+ */
+class SwCombinedPortion : public SwFieldPortion
+{
+ sal_uInt16 m_aPos[6]; // up to six X positions
+ o3tl::enumarray<SwFontScript,sal_uInt16> m_aWidth; // one width for every scripttype
+ SwFontScript m_aScrType[6]; // scripttype of every character
+ sal_uInt16 m_nUpPos; // the Y position of the upper baseline
+ sal_uInt16 m_nLowPos; // the Y position of the lower baseline
+ sal_uInt8 m_nProportion; // relative font height
+public:
+ explicit SwCombinedPortion( const OUString &rExpand );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+};
+
+namespace sw::mark { class IFieldmark; }
+
+class SwFieldFormDropDownPortion : public SwFieldPortion
+{
+public:
+ explicit SwFieldFormDropDownPortion(sw::mark::IFieldmark *pFieldMark, const OUString &rExpand)
+ : SwFieldPortion(rExpand)
+ , m_pFieldMark(pFieldMark)
+ {
+ }
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand ) const override;
+
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+
+private:
+ sw::mark::IFieldmark* m_pFieldMark;
+};
+
+class SwFieldFormDatePortion : public SwFieldPortion
+{
+public:
+ explicit SwFieldFormDatePortion(sw::mark::IFieldmark *pFieldMark, bool bStart)
+ : SwFieldPortion("")
+ , m_pFieldMark(pFieldMark)
+ , m_bStart(bStart)
+ {
+ }
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand) const override;
+
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+
+private:
+ sw::mark::IFieldmark* m_pFieldMark;
+ bool m_bStart;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porfly.cxx b/sw/source/core/text/porfly.cxx
new file mode 100644
index 000000000..9abef0c83
--- /dev/null
+++ b/sw/source/core/text/porfly.cxx
@@ -0,0 +1,413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <dcontact.hxx>
+#include <dflyobj.hxx>
+#include <pam.hxx>
+#include "portab.hxx"
+#include <flyfrm.hxx>
+#include <rootfrm.hxx>
+#include <frmfmt.hxx>
+#include <viewsh.hxx>
+#include <textboxhelper.hxx>
+#include <IDocumentState.hxx>
+
+#include <sal/log.hxx>
+#include <fmtanchr.hxx>
+#include <fmtflcnt.hxx>
+#include <flyfrms.hxx>
+#include <txatbase.hxx>
+#include "porfly.hxx"
+#include "porlay.hxx"
+#include "inftxt.hxx"
+
+#include <sortedobjs.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <PostItMgr.hxx>
+
+/**
+ * class SwFlyPortion => we expect a frame-locale SwRect!
+ */
+
+void SwFlyPortion::Paint( const SwTextPaintInfo& ) const
+{
+}
+
+bool SwFlyPortion::Format( SwTextFormatInfo &rInf )
+{
+ OSL_ENSURE( GetFix() >= rInf.X(), "SwFlyPortion::Format" );
+
+ // tabs must be expanded
+ if( rInf.GetLastTab() )
+ rInf.GetLastTab()->FormatEOL( rInf );
+
+ rInf.GetLast()->FormatEOL( rInf );
+ PrtWidth( o3tl::narrowing<sal_uInt16>(GetFix() - rInf.X() + PrtWidth()) );
+ if( !Width() )
+ {
+ OSL_ENSURE( Width(), "+SwFlyPortion::Format: a fly is a fly is a fly" );
+ Width(1);
+ }
+
+ // resetting
+ rInf.SetFly( nullptr );
+ rInf.Width( rInf.RealWidth() );
+ rInf.GetParaPortion()->SetFly();
+
+ // trailing blank:
+ if( rInf.GetIdx() < TextFrameIndex(rInf.GetText().getLength())
+ && TextFrameIndex(1) < rInf.GetIdx()
+ && !rInf.GetRest()
+ && ' ' == rInf.GetChar( rInf.GetIdx() )
+ && ' ' != rInf.GetChar(rInf.GetIdx() - TextFrameIndex(1))
+ && ( !rInf.GetLast() || !rInf.GetLast()->IsBreakPortion() ) )
+ {
+ SetBlankWidth( rInf.GetTextSize(OUString(' ')).Width() );
+ SetLen(TextFrameIndex(1));
+ }
+
+ const sal_uInt16 nNewWidth = o3tl::narrowing<sal_uInt16>(rInf.X() + PrtWidth());
+ if( rInf.Width() <= nNewWidth )
+ {
+ Truncate();
+ if( nNewWidth > rInf.Width() )
+ {
+ PrtWidth( nNewWidth - rInf.Width() );
+ SetFixWidth( PrtWidth() );
+ }
+ return true;
+ }
+ return false;
+}
+
+bool SwFlyCntPortion::Format( SwTextFormatInfo &rInf )
+{
+ bool bFull = rInf.Width() < rInf.X() + PrtWidth();
+
+ if( bFull )
+ {
+ // If the line is full, and the character-bound frame is at
+ // the beginning of a line
+ // If it is not possible to side step into a Fly
+ // "Begin of line" criteria ( ! rInf.X() ) has to be extended.
+ // KerningPortions at beginning of line, e.g., for grid layout
+ // must be considered.
+ const SwLinePortion* pLastPor = rInf.GetLast();
+ const sal_uInt16 nLeft = ( pLastPor &&
+ ( pLastPor->IsKernPortion() ||
+ pLastPor->IsErgoSumPortion() ) ) ?
+ pLastPor->Width() :
+ 0;
+
+ if( nLeft == rInf.X() && ! rInf.GetFly() )
+ {
+ Width( rInf.Width() );
+ bFull = false; // so that notes can still be placed in this line
+ }
+ else
+ {
+ if( !rInf.GetFly() )
+ rInf.SetNewLine( true );
+ Width(0);
+ SetAscent(0);
+ SetLen(TextFrameIndex(0));
+ if( rInf.GetLast() )
+ rInf.GetLast()->FormatEOL( rInf );
+
+ return bFull;
+ }
+ }
+
+ rInf.GetParaPortion()->SetFly();
+ return bFull;
+}
+
+//TODO: improve documentation
+/** move character-bound objects inside the given area
+ *
+ * This allows moving those objects from Master to Follow, or vice versa.
+ *
+ * @param pNew
+ * @param nStart
+ * @param nEnd
+ */
+void SwTextFrame::MoveFlyInCnt(SwTextFrame *pNew,
+ TextFrameIndex const nStart, TextFrameIndex const nEnd)
+{
+ SwSortedObjs *pObjs = GetDrawObjs();
+ if ( nullptr == pObjs )
+ return;
+
+ for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
+ {
+ // Consider changed type of <SwSortedList> entries
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat().GetAnchor();
+ if (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ const SwPosition* pPos = rAnch.GetContentAnchor();
+ TextFrameIndex const nIndex(MapModelToViewPos(*pPos));
+ if (nStart <= nIndex && nIndex < nEnd)
+ {
+ if ( auto pFlyFrame = pAnchoredObj->DynCastFlyFrame() )
+ {
+ RemoveFly( pFlyFrame );
+ pNew->AppendFly( pFlyFrame );
+ }
+ else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr )
+ {
+ RemoveDrawObj( *pAnchoredObj );
+ pNew->AppendDrawObj( *pAnchoredObj );
+ }
+ --i;
+ }
+ }
+ }
+}
+
+TextFrameIndex SwTextFrame::CalcFlyPos( SwFrameFormat const * pSearch )
+{
+ sw::MergedAttrIter iter(*this);
+ for (SwTextAttr const* pHt = iter.NextAttr(); pHt; pHt = iter.NextAttr())
+ {
+ if( RES_TXTATR_FLYCNT == pHt->Which() )
+ {
+ SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat();
+ if( pFrameFormat == pSearch )
+ {
+ return TextFrameIndex(pHt->GetStart());
+ }
+ }
+ }
+ OSL_ENSURE(false, "CalcFlyPos: Not Found!");
+ return TextFrameIndex(COMPLETE_STRING);
+}
+
+void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const
+{
+ // Baseline output
+ // Re-paint everything at a CompletePaint call
+ SwRect aRepaintRect(rInf.GetPaintRect());
+
+ if(rInf.GetTextFrame()->IsRightToLeft())
+ rInf.GetTextFrame()->SwitchLTRtoRTL(aRepaintRect);
+
+ if(rInf.GetTextFrame()->IsVertical())
+ rInf.GetTextFrame()->SwitchHorizontalToVertical(aRepaintRect);
+
+ if(!((m_pFly->IsCompletePaint() ||
+ m_pFly->getFrameArea().Overlaps(aRepaintRect)) &&
+ SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), m_pFly->getRootFrame()->GetCurrShell())))
+ return;
+
+ SwRect aRect(m_pFly->getFrameArea());
+ if(!m_pFly->IsCompletePaint())
+ aRect.Intersection_(aRepaintRect);
+
+ // GetFlyFrame() may change the layout mode at the output device.
+ {
+ SwLayoutModeModifier aLayoutModeModifier(*rInf.GetOut());
+ m_pFly->PaintSwFrame(const_cast<vcl::RenderContext&>(*rInf.GetOut()), aRect);
+
+ // track changes: cross out the image, if it is deleted
+ const SwFrame *pFrame = m_pFly->Lower();
+ if ( GetAuthor() != std::string::npos && IsDeleted() && pFrame )
+ {
+ SwRect aPaintRect( pFrame->GetPaintArea() );
+
+ const AntialiasingFlags nFormerAntialiasing( rInf.GetOut()->GetAntialiasing() );
+ const bool bIsAntiAliasing = officecfg::Office::Common::Drawinglayer::AntiAliasing::get();
+ if ( bIsAntiAliasing )
+ const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetAntialiasing(AntialiasingFlags::Enable);
+ tools::Long startX = aPaintRect.Left( ), endX = aPaintRect.Right();
+ tools::Long startY = aPaintRect.Top( ), endY = aPaintRect.Bottom();
+ const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetLineColor(
+ SwPostItMgr::GetColorAnchor(GetAuthor()) );
+ const_cast<vcl::RenderContext&>(*rInf.GetOut()).DrawLine(Point(startX, startY), Point(endX, endY));
+ const_cast<vcl::RenderContext&>(*rInf.GetOut()).DrawLine(Point(startX, endY), Point(endX, startY));
+ if ( bIsAntiAliasing )
+ const_cast<vcl::RenderContext&>(*rInf.GetOut()).SetAntialiasing(nFormerAntialiasing);
+ }
+ }
+ const_cast<SwTextPaintInfo&>(rInf).GetRefDev()->SetLayoutMode(rInf.GetOut()->GetLayoutMode());
+
+ // As the OutputDevice might be anything, the font must be re-selected.
+ // Being in const method should not be a problem.
+ const_cast<SwTextPaintInfo&>(rInf).SelectFont();
+
+ assert(rInf.GetVsh());
+ SAL_WARN_IF(rInf.GetVsh()->GetOut() != rInf.GetOut(), "sw.core", "SwFlyCntPortion::Paint: Outdev has changed");
+ if(rInf.GetVsh())
+ const_cast<SwTextPaintInfo&>(rInf).SetOut(rInf.GetVsh()->GetOut());
+}
+
+void sw::DrawFlyCntPortion::Paint(const SwTextPaintInfo&) const
+{
+ if(!m_pContact->GetAnchorFrame())
+ {
+ // No direct positioning of the drawing object is needed
+ m_pContact->ConnectToLayout();
+ }
+}
+
+/**
+ * Use the dimensions of pFly->OutRect()
+ */
+SwFlyCntPortion::SwFlyCntPortion()
+ : m_bMax(false)
+ , m_bDeleted(false)
+ , m_nAuthor(std::string::npos)
+ , m_eAlign(sw::LineAlign::NONE)
+{
+ mnLineLength = TextFrameIndex(1);
+ SetWhichPor(PortionType::FlyCnt);
+}
+
+sw::FlyContentPortion::FlyContentPortion(SwFlyInContentFrame* pFly)
+ : m_pFly(pFly)
+{
+ SAL_WARN_IF(!pFly, "sw.core", "SwFlyCntPortion::SwFlyCntPortion: no SwFlyInContentFrame!");
+}
+
+sw::DrawFlyCntPortion::DrawFlyCntPortion(SwFrameFormat const & rFormat)
+ : m_pContact(nullptr)
+{
+ rFormat.CallSwClientNotify(sw::CreatePortionHint(&m_pContact));
+ assert(m_pContact);
+}
+
+sw::FlyContentPortion* sw::FlyContentPortion::Create(const SwTextFrame& rFrame, SwFlyInContentFrame* pFly, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags)
+{
+ auto pNew(new sw::FlyContentPortion(pFly));
+ pNew->SetBase(rFrame, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags | AsCharFlags::UlSpace | AsCharFlags::Init);
+ return pNew;
+}
+
+sw::DrawFlyCntPortion* sw::DrawFlyCntPortion::Create(const SwTextFrame& rFrame, SwFrameFormat const & rFormat, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags)
+{
+ auto pNew(new DrawFlyCntPortion(rFormat));
+ pNew->SetBase(rFrame, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags | AsCharFlags::UlSpace | AsCharFlags::Init);
+ return pNew;
+}
+
+sw::DrawFlyCntPortion::~DrawFlyCntPortion() {};
+sw::FlyContentPortion::~FlyContentPortion() {};
+
+SdrObject* sw::FlyContentPortion::GetSdrObj(const SwTextFrame&)
+{
+ return m_pFly->GetVirtDrawObj();
+}
+
+SdrObject* sw::DrawFlyCntPortion::GetSdrObj(const SwTextFrame& rFrame)
+{
+ SdrObject* pSdrObj;
+ // Determine drawing object ('master' or 'virtual') by frame
+ pSdrObj = m_pContact->GetDrawObjectByAnchorFrame(rFrame);
+ if(!pSdrObj)
+ {
+ SAL_WARN("sw.core", "SwFlyCntPortion::SetBase(..) - No drawing object found by <GetDrawContact()->GetDrawObjectByAnchorFrame( rFrame )>");
+ pSdrObj = m_pContact->GetMaster();
+ }
+
+ // Call <SwAnchoredDrawObject::MakeObjPos()> to assure that flag at
+ // the <DrawFrameFormat> and at the <SwAnchoredDrawObject> instance are
+ // correctly set
+ if(pSdrObj)
+ m_pContact->GetAnchoredObj(pSdrObj)->MakeObjPos();
+ return pSdrObj;
+}
+
+/**
+ * After setting the RefPoints, the ascent needs to be recalculated
+ * because it is dependent on RelPos
+ *
+ * @param rBase CAUTION: needs to be an absolute value!
+ */
+void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase,
+ tools::Long nLnAscent, tools::Long nLnDescent,
+ tools::Long nFlyAsc, tools::Long nFlyDesc,
+ AsCharFlags nFlags )
+{
+ // Use new class to position object
+ // Determine drawing object
+ SdrObject* pSdrObj = GetSdrObj(rFrame);
+ if (!pSdrObj)
+ return;
+
+ // position object
+ objectpositioning::SwAsCharAnchoredObjectPosition aObjPositioning(
+ *pSdrObj,
+ rBase, nFlags,
+ nLnAscent, nLnDescent, nFlyAsc, nFlyDesc );
+
+ // Scope of local variable <aObjPosInProgress>
+ {
+ SwObjPositioningInProgress aObjPosInProgress( *pSdrObj );
+ aObjPositioning.CalcPosition();
+ }
+
+ if (auto pFormat = FindFrameFormat(pSdrObj))
+ {
+ if (pFormat->GetOtherTextBoxFormats()
+ && pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ // TODO: Improve security with moving this sync call to other place,
+ // where it works for typing but not during layout calc.
+ const bool bModified = pFormat->GetDoc()->getIDocumentState().IsEnableSetModified();
+ pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(false);
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::changeAnchor, pFormat,
+ pFormat->FindRealSdrObject());
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(SwTextBoxHelper::syncTextBoxSize,
+ pFormat, pFormat->FindRealSdrObject());
+ pFormat->GetDoc()->getIDocumentState().SetEnableSetModified(bModified);
+ }
+ }
+
+ SetAlign( aObjPositioning.GetLineAlignment() );
+
+ m_aRef = aObjPositioning.GetAnchorPos();
+ if( nFlags & AsCharFlags::Rotate )
+ SvXSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() );
+ else
+ SvLSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() );
+ if( Height() )
+ {
+ // GetRelPosY returns the relative position to baseline (if 0, the
+ // upper border of the FlyCnt if on the baseline of a line)
+ SwTwips nRelPos = aObjPositioning.GetRelPosY();
+ if ( nRelPos < 0 )
+ {
+ mnAscent = -nRelPos;
+ if( mnAscent > Height() )
+ Height( mnAscent );
+ }
+ else
+ {
+ mnAscent = 0;
+ Height( Height() + o3tl::narrowing<sal_uInt16>(nRelPos) );
+ }
+ }
+ else
+ {
+ Height( 1 );
+ mnAscent = 0;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porfly.hxx b/sw/source/core/text/porfly.hxx
new file mode 100644
index 000000000..a519c1109
--- /dev/null
+++ b/sw/source/core/text/porfly.hxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <ascharanchoredobjectposition.hxx>
+
+#include "porglue.hxx"
+#include <flyfrms.hxx>
+
+class SwDrawContact;
+class SwTextFrame;
+struct SwCursorMoveState;
+
+class SwFlyPortion : public SwFixPortion
+{
+ sal_uInt16 m_nBlankWidth;
+public:
+ explicit SwFlyPortion( const SwRect &rFlyRect )
+ : SwFixPortion(rFlyRect), m_nBlankWidth( 0 ) { SetWhichPor( PortionType::Fly ); }
+ sal_uInt16 GetBlankWidth( ) const { return m_nBlankWidth; }
+ void SetBlankWidth( const sal_uInt16 nNew ) { m_nBlankWidth = nNew; }
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+};
+
+/// This portion represents an as-character anchored fly (shape, frame, etc.)
+class SwFlyCntPortion : public SwLinePortion
+{
+ Point m_aRef; // Relatively to this point we calculate the AbsPos
+ bool m_bMax; // Line adjustment and height == line height
+ bool m_bDeleted; // Part of tracked deletion: it needs strikethrough
+ size_t m_nAuthor; // Redline author for color of the strikethrough
+ sw::LineAlign m_eAlign;
+
+ virtual SdrObject* GetSdrObj(const SwTextFrame&) =0;
+
+public:
+ SwFlyCntPortion();
+ const Point& GetRefPoint() const { return m_aRef; }
+ bool IsMax() const { return m_bMax; }
+ bool IsDeleted() const { return m_bDeleted; }
+ void SetAuthor(size_t nAuthor) { m_nAuthor = nAuthor; }
+ size_t GetAuthor() const { return m_nAuthor; }
+ sw::LineAlign GetAlign() const { return m_eAlign; }
+ void SetAlign(sw::LineAlign eAlign) { m_eAlign = eAlign; }
+ void SetMax(bool bMax) { m_bMax = bMax; }
+ void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
+ void SetBase(const SwTextFrame& rFrame, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAscent, tools::Long nFlyDescent, AsCharFlags nFlags);
+ virtual bool Format(SwTextFormatInfo& rInf) override;
+};
+
+namespace sw
+{
+ class FlyContentPortion final : public SwFlyCntPortion
+ {
+ SwFlyInContentFrame* m_pFly;
+ virtual SdrObject* GetSdrObj(const SwTextFrame&) override;
+ public:
+ FlyContentPortion(SwFlyInContentFrame* pFly);
+ static FlyContentPortion* Create(const SwTextFrame& rFrame, SwFlyInContentFrame* pFly, const Point& rBase, tools::Long nAscent, tools::Long nDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags);
+ SwFlyInContentFrame* GetFlyFrame() { return m_pFly; }
+ void GetFlyCursorOfst(Point& rPoint, SwPosition& rPos, SwCursorMoveState* pCMS) const { m_pFly->GetModelPositionForViewPoint(&rPos, rPoint, pCMS); };
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+ virtual ~FlyContentPortion() override;
+ };
+ class DrawFlyCntPortion final : public SwFlyCntPortion
+ {
+ SwDrawContact* m_pContact;
+ virtual SdrObject* GetSdrObj(const SwTextFrame&) override;
+ public:
+ DrawFlyCntPortion(SwFrameFormat const & rFormat);
+ static DrawFlyCntPortion* Create(const SwTextFrame& rFrame, SwFrameFormat const & rFormat, const Point& rBase, tools::Long nAsc, tools::Long nDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags);
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+ virtual ~DrawFlyCntPortion() override;
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porftn.hxx b/sw/source/core/text/porftn.hxx
new file mode 100644
index 000000000..a5ecda64d
--- /dev/null
+++ b/sw/source/core/text/porftn.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "porfld.hxx"
+
+class SwTextFootnote;
+
+class SwFootnotePortion : public SwFieldPortion
+{
+ SwTextFootnote *m_pFootnote;
+ sal_uInt16 m_nOrigHeight;
+ // #i98418#
+ bool mbPreferredScriptTypeSet;
+ SwFontScript mnPreferredScriptType;
+public:
+ SwFootnotePortion( const OUString &rExpand, SwTextFootnote *pFootnote,
+ sal_uInt16 nOrig = USHRT_MAX );
+ sal_uInt16& Orig() { return m_nOrigHeight; }
+
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+
+ // #i98418#
+ void SetPreferredScriptType( SwFontScript nPreferredScriptType );
+
+ const SwTextFootnote* GetTextFootnote() const { return m_pFootnote; };
+};
+
+class SwFootnoteNumPortion : public SwNumberPortion
+{
+public:
+ SwFootnoteNumPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFntL )
+ : SwNumberPortion( rExpand, std::move(pFntL), true, false, 0, false )
+ { SetWhichPor( PortionType::FootnoteNum ); }
+};
+
+/**
+ * Used in footnotes if they break across pages, master has this portion at the end.
+ *
+ * Created only in case Tools -> Footnotes and Endnotes sets the End of footnote to a non-empty
+ * value.
+ */
+class SwQuoVadisPortion : public SwFieldPortion
+{
+ OUString m_aErgo;
+public:
+ SwQuoVadisPortion( const OUString &rExp, const OUString& rStr );
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+
+ void SetNumber( const OUString& rStr ) { m_aErgo = rStr; }
+ const OUString& GetQuoText() const { return m_aExpand; }
+ const OUString &GetContText() const { return m_aErgo; }
+
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand ) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+};
+
+/**
+ * Used in footnotes if they break across pages, follow starts with this portion.
+ *
+ * Created only in case Tools -> Footnotes and Endnotes sets the Start of next page to a non-empty
+ * value.
+ */
+class SwErgoSumPortion : public SwFieldPortion
+{
+public:
+ SwErgoSumPortion( const OUString &rExp, std::u16string_view rStr );
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+
+ // Field cloner for SplitGlue
+ virtual SwFieldPortion *Clone( const OUString &rExpand ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porglue.cxx b/sw/source/core/text/porglue.cxx
new file mode 100644
index 000000000..5f1fd2a7d
--- /dev/null
+++ b/sw/source/core/text/porglue.cxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swrect.hxx>
+#include <viewopt.hxx>
+#include "porglue.hxx"
+#include "inftxt.hxx"
+#include "porlay.hxx"
+#include "porfly.hxx"
+#include <comphelper/string.hxx>
+
+SwGluePortion::SwGluePortion( const sal_uInt16 nInitFixWidth )
+ : m_nFixWidth( nInitFixWidth )
+{
+ PrtWidth( m_nFixWidth );
+ SetWhichPor( PortionType::Glue );
+}
+
+TextFrameIndex SwGluePortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+{
+ // FIXME why nOfst > GetLen() ? is that supposed to be > Width() ?
+ if( !GetLen() || nOfst > sal_Int32(GetLen()) || !Width() )
+ return SwLinePortion::GetModelPositionForViewPoint( nOfst );
+ else
+ return TextFrameIndex(nOfst / (Width() / sal_Int32(GetLen())));
+}
+
+SwPosSize SwGluePortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+{
+ if (TextFrameIndex(1) >= GetLen() || rInf.GetLen() > GetLen() || !Width() || !GetLen())
+ return SwPosSize(*this);
+ else
+ return SwPosSize((Width() / sal_Int32(GetLen())) * sal_Int32(rInf.GetLen()), Height());
+}
+
+bool SwGluePortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
+{
+ if( GetLen() && rInf.OnWin() &&
+ rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() )
+ {
+ OUStringBuffer aBuf(GetLen().get());
+ comphelper::string::padToLength(aBuf, sal_Int32(GetLen()), CH_BULLET);
+ rText = aBuf.makeStringAndClear();
+ return true;
+ }
+ return false;
+}
+
+void SwGluePortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( !GetLen() )
+ return;
+
+ if( rInf.GetFont()->IsPaintBlank() )
+ {
+ const sal_Int32 nCount = GetFixWidth() / sal_Int32(GetLen());
+ OUStringBuffer aBuf(nCount);
+ comphelper::string::padToLength(aBuf, nCount, ' ');
+ OUString aText(aBuf.makeStringAndClear());
+ SwTextPaintInfo aInf( rInf, &aText );
+ aInf.DrawText(*this, TextFrameIndex(aText.getLength()), true);
+ }
+
+ if( !(rInf.OnWin() && rInf.GetOpt().IsBlank() && rInf.IsNoSymbol()) )
+ return;
+
+#if OSL_DEBUG_LEVEL > 0
+ const sal_Unicode cChar = rInf.GetChar( rInf.GetIdx() );
+ OSL_ENSURE( CH_BLANK == cChar || CH_BULLET == cChar,
+ "SwGluePortion::Paint: blank expected" );
+#endif
+ if (TextFrameIndex(1) == GetLen())
+ {
+ OUString aBullet( CH_BULLET );
+ SwPosSize aBulletSize( rInf.GetTextSize( aBullet ) );
+ Point aPos( rInf.GetPos() );
+ aPos.AdjustX((Width()/2) - (aBulletSize.Width()/2) );
+ SwTextPaintInfo aInf( rInf, &aBullet );
+ aInf.SetPos( aPos );
+ SwTextPortion aBulletPor;
+ aBulletPor.Width( aBulletSize.Width() );
+ aBulletPor.Height( aBulletSize.Height() );
+ aBulletPor.SetAscent( GetAscent() );
+ aInf.DrawText(aBulletPor, TextFrameIndex(aBullet.getLength()), true);
+ }
+ else
+ {
+ SwTextSlot aSlot( &rInf, this, true, false );
+ rInf.DrawText( *this, rInf.GetLen(), true );
+ }
+}
+
+void SwGluePortion::MoveGlue( SwGluePortion *pTarget, const tools::Long nPrtGlue )
+{
+ auto nPrt = std::min( nPrtGlue, GetPrtGlue() );
+ if( 0 < nPrt )
+ {
+ pTarget->AddPrtWidth( nPrt ); //TODO: overflow
+ SubPrtWidth( nPrt ); //TODO: overflow
+ }
+}
+
+void SwGluePortion::Join( SwGluePortion *pVictim )
+{
+ // The GluePortion is extracted and flushed away ...
+ AddPrtWidth( pVictim->PrtWidth() );
+ SetLen( pVictim->GetLen() + GetLen() );
+ if( Height() < pVictim->Height() )
+ Height( pVictim->Height() );
+
+ AdjFixWidth();
+ Cut( pVictim );
+ delete pVictim;
+}
+
+void SwGluePortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwGluePortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fix-width"), BAD_CAST(OString::number(m_nFixWidth).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/**
+ * We're expecting a frame-local SwRect!
+ */
+SwFixPortion::SwFixPortion( const SwRect &rRect )
+ :SwGluePortion( sal_uInt16(rRect.Width()) ), m_nFix( sal_uInt16(rRect.Left()) )
+{
+ Height( sal_uInt16(rRect.Height()) );
+ SetWhichPor( PortionType::Fix );
+}
+
+SwFixPortion::SwFixPortion()
+ : SwGluePortion(0), m_nFix(0)
+{
+ SetWhichPor( PortionType::Fix );
+}
+
+void SwFixPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFixPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("fix"),
+ BAD_CAST(OString::number(m_nFix).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwMarginPortion::SwMarginPortion()
+ :SwGluePortion( 0 )
+{
+ SetWhichPor( PortionType::Margin );
+}
+
+/**
+ * In the outer loop all portions are inspected - the GluePortions
+ * at the end are processed first.
+ * The end is shifted forwardly till no more GluePortions remain.
+ * Always GluePortion-pairs (pLeft and pRight) are treated, where
+ * textportions between pLeft and pRight are moved at the back of
+ * pRight if pRight has enough Glue. With every move part of the
+ * Glue is transferred from pRight to pLeft.
+ * The next loop starts with the processed pLeft as pRight.
+ */
+void SwMarginPortion::AdjustRight( const SwLineLayout *pCurr )
+{
+ SwGluePortion *pRight = nullptr;
+ bool bNoMove = nullptr != pCurr->GetpKanaComp();
+ while( pRight != this )
+ {
+
+ // 1) We search for the left Glue
+ SwLinePortion *pPos = this;
+ SwGluePortion *pLeft = nullptr;
+ while( pPos )
+ {
+ if( pPos->InFixMargGrp() )
+ pLeft = static_cast<SwGluePortion*>(pPos);
+ pPos = pPos->GetNextPortion();
+ if( pPos == pRight)
+ pPos = nullptr;
+ }
+
+ // Two adjoining FlyPortions are merged
+ if( pRight && pLeft && pLeft->GetNextPortion() == pRight )
+ {
+ pRight->MoveAllGlue( pLeft );
+ pRight = nullptr;
+ }
+ auto nRightGlue = pRight && 0 < pRight->GetPrtGlue()
+ ? pRight->GetPrtGlue() : 0;
+ // 2) balance left and right Glue
+ // But not for tabs ...
+ if( pLeft && nRightGlue && !pRight->InTabGrp() )
+ {
+ // pPrev is the portion immediately before pRight
+ SwLinePortion *pPrev = pRight->FindPrevPortion( pLeft );
+
+ if ( pRight->IsFlyPortion() && pRight->GetLen() )
+ {
+ SwFlyPortion *pFly = static_cast<SwFlyPortion *>(pRight);
+ if ( pFly->GetBlankWidth() < nRightGlue )
+ {
+ // Creating new TextPortion, that takes over the
+ // Blank previously swallowed by the Fly.
+ nRightGlue = nRightGlue - pFly->GetBlankWidth();
+ pFly->SubPrtWidth( pFly->GetBlankWidth() );
+ pFly->SetLen(TextFrameIndex(0));
+ SwTextPortion *pNewPor = new SwTextPortion;
+ pNewPor->SetLen(TextFrameIndex(1));
+ pNewPor->Height( pFly->Height() );
+ pNewPor->Width( pFly->GetBlankWidth() );
+ pFly->Insert( pNewPor );
+ }
+ else
+ pPrev = pLeft;
+ }
+ while( pPrev != pLeft )
+ {
+ if( bNoMove || pPrev->PrtWidth() >= nRightGlue ||
+ pPrev->InHyphGrp() || pPrev->IsKernPortion() )
+ {
+ // The portion before the pRight cannot be moved
+ // because no Glue is remaining.
+ // We set the break condition:
+ pPrev = pLeft;
+ }
+ else
+ {
+ nRightGlue = nRightGlue - pPrev->PrtWidth();
+ // pPrev is moved behind pRight. For this the
+ // Glue value between pRight and pLeft gets balanced.
+ pRight->MoveGlue( pLeft, pPrev->PrtWidth() );
+ // Now fix the linking of our portions.
+ SwLinePortion *pPrevPrev = pPrev->FindPrevPortion( pLeft );
+ pPrevPrev->SetNextPortion( pRight );
+ pPrev->SetNextPortion( pRight->GetNextPortion() );
+ pRight->SetNextPortion( pPrev );
+ if ( pPrev->GetNextPortion() && pPrev->InTextGrp()
+ && pPrev->GetNextPortion()->IsHolePortion() )
+ {
+ SwHolePortion *pHolePor =
+ static_cast<SwHolePortion*>(pPrev->GetNextPortion());
+ if ( !pHolePor->GetNextPortion() ||
+ !pHolePor->GetNextPortion()->InFixMargGrp() )
+ {
+ pPrev->AddPrtWidth( pHolePor->GetBlankWidth() );
+ pPrev->SetLen(pPrev->GetLen() + TextFrameIndex(1));
+ pPrev->SetNextPortion( pHolePor->GetNextPortion() );
+ delete pHolePor;
+ }
+ }
+ pPrev = pPrevPrev;
+ }
+ }
+ }
+ // If no left Glue remains, we set the break condition.
+ pRight = pLeft ? pLeft : this;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porglue.hxx b/sw/source/core/text/porglue.hxx
new file mode 100644
index 000000000..d46962d45
--- /dev/null
+++ b/sw/source/core/text/porglue.hxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "porlin.hxx"
+
+class SwRect;
+class SwLineLayout;
+
+class SwGluePortion : public SwLinePortion
+{
+private:
+ sal_uInt16 m_nFixWidth;
+public:
+ explicit SwGluePortion( const sal_uInt16 nInitFixWidth );
+
+ void Join( SwGluePortion *pVictim );
+
+ inline tools::Long GetPrtGlue() const;
+ sal_uInt16 GetFixWidth() const { return m_nFixWidth; }
+ void SetFixWidth( const sal_uInt16 nNew ) { m_nFixWidth = nNew; }
+ void MoveGlue( SwGluePortion *pTarget, const tools::Long nPrtGlue );
+ inline void MoveAllGlue( SwGluePortion *pTarget );
+ inline void MoveHalfGlue( SwGluePortion *pTarget );
+ inline void AdjFixWidth();
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const override;
+};
+
+class SwFixPortion : public SwGluePortion
+{
+ sal_uInt16 m_nFix; // The width offset in the line
+public:
+ explicit SwFixPortion( const SwRect &rFlyRect );
+ SwFixPortion();
+ void SetFix( const sal_uInt16 nNewFix ) { m_nFix = nNewFix; }
+ sal_uInt16 GetFix() const { return m_nFix; }
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const override;
+};
+
+class SwMarginPortion : public SwGluePortion
+{
+public:
+ explicit SwMarginPortion();
+ void AdjustRight( const SwLineLayout* pCurr );
+};
+
+inline tools::Long SwGluePortion::GetPrtGlue() const
+{ return Width() - m_nFixWidth; }
+
+// The FixWidth MUST NEVER be larger than the accumulated width!
+inline void SwGluePortion::AdjFixWidth()
+{
+ if( m_nFixWidth > PrtWidth() )
+ m_nFixWidth = PrtWidth();
+}
+
+inline void SwGluePortion::MoveAllGlue( SwGluePortion *pTarget )
+{
+ MoveGlue( pTarget, GetPrtGlue() );
+}
+
+inline void SwGluePortion::MoveHalfGlue( SwGluePortion *pTarget )
+{
+ MoveGlue( pTarget, GetPrtGlue() / 2 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porhyph.hxx b/sw/source/core/text/porhyph.hxx
new file mode 100644
index 000000000..dc3c6651d
--- /dev/null
+++ b/sw/source/core/text/porhyph.hxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "porexp.hxx"
+
+class SwHyphPortion : public SwExpandPortion
+{
+public:
+ SwHyphPortion()
+ {
+ SetWhichPor( PortionType::Hyphen );
+ }
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+class SwHyphStrPortion : public SwHyphPortion
+{
+ OUString m_aExpand;
+public:
+ explicit SwHyphStrPortion(std::u16string_view rStr)
+ : m_aExpand(OUString::Concat(rStr) + "-")
+ {
+ SetWhichPor( PortionType::HyphenStr );
+ }
+
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+};
+
+class SwSoftHyphPortion : public SwHyphPortion
+{
+ bool m_bExpand;
+ sal_uInt16 m_nViewWidth;
+
+public:
+ SwSoftHyphPortion();
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual SwLinePortion *Compress() override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
+ void SetExpand( const bool bNew ) { m_bExpand = bNew; }
+ bool IsExpand() const { return m_bExpand; }
+
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+};
+
+class SwSoftHyphStrPortion : public SwHyphStrPortion
+{
+public:
+ explicit SwSoftHyphStrPortion( std::u16string_view rStr );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx
new file mode 100644
index 000000000..d91edd87c
--- /dev/null
+++ b/sw/source/core/text/porlay.cxx
@@ -0,0 +1,2842 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "porlay.hxx"
+#include "itrform2.hxx"
+#include "porglue.hxx"
+#include "redlnitr.hxx"
+#include "porfly.hxx"
+#include "porrst.hxx"
+#include "pormulti.hxx"
+#include "pordrop.hxx"
+#include <breakit.hxx>
+#include <unicode/uchar.h>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/CTLScriptType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <paratr.hxx>
+#include <sal/log.hxx>
+#include <optional>
+#include <editeng/adjustitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <svl/asiancfg.hxx>
+#include <svl/languageoptions.hxx>
+#include <tools/multisel.hxx>
+#include <unotools/charclass.hxx>
+#include <charfmt.hxx>
+#include <docary.hxx>
+#include <fmtanchr.hxx>
+#include <redline.hxx>
+#include <calbck.hxx>
+#include <doc.hxx>
+#include <swscanner.hxx>
+#include <txatbase.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IMark.hxx>
+#include <sortedobjs.hxx>
+
+using namespace ::com::sun::star;
+using namespace i18n::ScriptType;
+
+#include <unicode/ubidi.h>
+#include <i18nutil/scripttypedetector.hxx>
+#include <i18nutil/unicode.hxx>
+
+/*
+ https://www.khtt.net/en/page/1821/the-big-kashida-secret
+
+ the rules of priorities that govern the addition of kashidas in Arabic text
+ made ... for ... Explorer 5.5 browser.
+
+ The kashida justification is based on a connection priority scheme that
+ decides where kashidas are put automatically.
+
+ This is how the software decides on kashida-inserting priorities:
+ 1. First it looks for characters with the highest priority in each word,
+ which means kashida-extensions will only been used in one position in each
+ word. Not more.
+ 2. The kashida will be connected to the character with the highest priority.
+ 3. If kashida connection opportunities are found with an equal level of
+ priority in one word, the kashida will be placed towards the end of the
+ word.
+
+ The priority list of characters and the positioning is as follows:
+ 1. after a kashida that is manually placed in the text by the user,
+ 2. after a Seen or Sad (initial and medial form),
+ 3. before the final form of Taa Marbutah, Haa, Dal,
+ 4. before the final form of Alef, Tah Lam, Kaf and Gaf,
+ 5. before the preceding medial Baa of Ra, Ya and Alef Maqsurah,
+ 6. before the final form of Waw, Ain, Qaf and Fa,
+ 7. before the final form of other characters that can be connected.
+*/
+
+#define IS_JOINING_GROUP(c, g) ( u_getIntPropertyValue( (c), UCHAR_JOINING_GROUP ) == U_JG_##g )
+#define isAinChar(c) IS_JOINING_GROUP((c), AIN)
+#define isAlefChar(c) IS_JOINING_GROUP((c), ALEF)
+#define isDalChar(c) IS_JOINING_GROUP((c), DAL)
+#if U_ICU_VERSION_MAJOR_NUM >= 58
+#define isFehChar(c) (IS_JOINING_GROUP((c), FEH) || IS_JOINING_GROUP((c), AFRICAN_FEH))
+#else
+#define isFehChar(c) IS_JOINING_GROUP((c), FEH)
+#endif
+#define isGafChar(c) IS_JOINING_GROUP((c), GAF)
+#define isHehChar(c) IS_JOINING_GROUP((c), HEH)
+#define isKafChar(c) IS_JOINING_GROUP((c), KAF)
+#define isLamChar(c) IS_JOINING_GROUP((c), LAM)
+#if U_ICU_VERSION_MAJOR_NUM >= 58
+#define isQafChar(c) (IS_JOINING_GROUP((c), QAF) || IS_JOINING_GROUP((c), AFRICAN_QAF))
+#else
+#define isQafChar(c) IS_JOINING_GROUP((c), QAF)
+#endif
+#define isRehChar(c) IS_JOINING_GROUP((c), REH)
+#define isTahChar(c) IS_JOINING_GROUP((c), TAH)
+#define isTehMarbutaChar(c) IS_JOINING_GROUP((c), TEH_MARBUTA)
+#define isWawChar(c) IS_JOINING_GROUP((c), WAW)
+#define isSeenOrSadChar(c) (IS_JOINING_GROUP((c), SAD) || IS_JOINING_GROUP((c), SEEN))
+
+// Beh and characters that behave like Beh in medial form.
+static bool isBehChar(sal_Unicode cCh)
+{
+ bool bRet = false;
+ switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
+ {
+ case U_JG_BEH:
+ case U_JG_NOON:
+#if U_ICU_VERSION_MAJOR_NUM >= 58
+ case U_JG_AFRICAN_NOON:
+#endif
+ case U_JG_NYA:
+ case U_JG_YEH:
+ case U_JG_FARSI_YEH:
+ case U_JG_BURUSHASKI_YEH_BARREE:
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+
+ return bRet;
+}
+
+// Yeh and characters that behave like Yeh in final form.
+static bool isYehChar(sal_Unicode cCh)
+{
+ bool bRet = false;
+ switch (u_getIntPropertyValue(cCh, UCHAR_JOINING_GROUP))
+ {
+ case U_JG_YEH:
+ case U_JG_FARSI_YEH:
+ case U_JG_YEH_BARREE:
+ case U_JG_BURUSHASKI_YEH_BARREE:
+ case U_JG_YEH_WITH_TAIL:
+ bRet = true;
+ break;
+ default:
+ bRet = false;
+ break;
+ }
+
+ return bRet;
+}
+
+static bool isTransparentChar ( sal_Unicode cCh )
+{
+ return u_getIntPropertyValue( cCh, UCHAR_JOINING_TYPE ) == U_JT_TRANSPARENT;
+}
+
+// Checks if cCh + cNectCh builds a ligature (used for Kashidas)
+static bool lcl_IsLigature( sal_Unicode cCh, sal_Unicode cNextCh )
+{
+ // Lam + Alef
+ return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
+}
+
+// Checks if cCh is connectable to cPrevCh (used for Kashidas)
+static bool lcl_ConnectToPrev( sal_Unicode cCh, sal_Unicode cPrevCh )
+{
+ const int32_t nJoiningType = u_getIntPropertyValue( cPrevCh, UCHAR_JOINING_TYPE );
+ bool bRet = nJoiningType != U_JT_RIGHT_JOINING && nJoiningType != U_JT_NON_JOINING;
+
+ // check for ligatures cPrevChar + cChar
+ if( bRet )
+ bRet = !lcl_IsLigature( cPrevCh, cCh );
+
+ return bRet;
+}
+
+static bool lcl_HasStrongLTR ( std::u16string_view rText, sal_Int32 nStart, sal_Int32 nEnd )
+ {
+ for( sal_Int32 nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
+ {
+ const UCharDirection nCharDir = u_charDirection ( rText[ nCharIdx ] );
+ if ( nCharDir == U_LEFT_TO_RIGHT ||
+ nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
+ nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
+ return true;
+ }
+ return false;
+ }
+
+// This is (meant to be) functionally equivalent to 'delete m_pNext' where
+// deleting a SwLineLayout recursively deletes the owned m_pNext SwLineLayout.
+//
+// Here, instead of using a potentially deep stack, iterate over all the
+// SwLineLayouts that would be deleted recursively and delete them linearly
+void SwLineLayout::DeleteNext()
+{
+ if (!m_pNext)
+ return;
+ SwLineLayout* pNext = m_pNext;
+ do
+ {
+ SwLineLayout* pLastNext = pNext;
+ pNext = pNext->GetNext();
+ pLastNext->SetNext(nullptr);
+ delete pLastNext;
+ }
+ while (pNext);
+}
+
+void SwLineLayout::Height(const SwTwips nNew, const bool bText)
+{
+ SwPosSize::Height(nNew);
+ if (bText)
+ m_nTextHeight = nNew;
+}
+
+// class SwLineLayout: This is the layout of a single line, which is made
+// up of its dimension, the character count and the word spacing in the line.
+// Line objects are managed in an own pool, in order to store them continuously
+// in memory so that they are paged out together and don't fragment memory.
+SwLineLayout::~SwLineLayout()
+{
+ Truncate();
+ DeleteNext();
+ m_pLLSpaceAdd.reset();
+ m_pKanaComp.reset();
+}
+
+SwLinePortion *SwLineLayout::Insert( SwLinePortion *pIns )
+{
+ // First attribute change: copy mass and length from *pIns into the first
+ // text portion
+ if( !mpNextPortion )
+ {
+ if( GetLen() )
+ {
+ mpNextPortion = SwTextPortion::CopyLinePortion(*this);
+ if( IsBlinking() )
+ {
+ SetBlinking( false );
+ }
+ }
+ else
+ {
+ SetNextPortion( pIns );
+ return pIns;
+ }
+ }
+ // Call with scope or we'll end up with recursion!
+ return mpNextPortion->SwLinePortion::Insert( pIns );
+}
+
+SwLinePortion *SwLineLayout::Append( SwLinePortion *pIns )
+{
+ // First attribute change: copy mass and length from *pIns into the first
+ // text portion
+ if( !mpNextPortion )
+ mpNextPortion = SwTextPortion::CopyLinePortion(*this);
+ // Call with scope or we'll end up with recursion!
+ return mpNextPortion->SwLinePortion::Append( pIns );
+}
+
+// For special treatment of empty lines
+
+bool SwLineLayout::Format( SwTextFormatInfo &rInf )
+{
+ if( GetLen() )
+ return SwTextPortion::Format( rInf );
+
+ Height( rInf.GetTextHeight() );
+ return true;
+}
+
+// We collect all FlyPortions at the beginning of the line and make that a
+// MarginPortion.
+SwMarginPortion *SwLineLayout::CalcLeftMargin()
+{
+ SwMarginPortion *pLeft = (GetNextPortion() && GetNextPortion()->IsMarginPortion()) ?
+ static_cast<SwMarginPortion *>(GetNextPortion()) : nullptr;
+ if( !GetNextPortion() )
+ SetNextPortion(SwTextPortion::CopyLinePortion(*this));
+ if( !pLeft )
+ {
+ pLeft = new SwMarginPortion;
+ pLeft->SetNextPortion( GetNextPortion() );
+ SetNextPortion( pLeft );
+ }
+ else
+ {
+ pLeft->Height( 0 );
+ pLeft->Width( 0 );
+ pLeft->SetLen(TextFrameIndex(0));
+ pLeft->SetAscent( 0 );
+ pLeft->SetNextPortion( nullptr );
+ pLeft->SetFixWidth(0);
+ }
+
+ SwLinePortion *pPos = pLeft->GetNextPortion();
+ while( pPos )
+ {
+ if( pPos->IsFlyPortion() )
+ {
+ // The FlyPortion gets sucked out...
+ pLeft->Join( static_cast<SwGluePortion*>(pPos) );
+ pPos = pLeft->GetNextPortion();
+ if( GetpKanaComp() && !GetKanaComp().empty() )
+ GetKanaComp().pop_front();
+ }
+ else
+ pPos = nullptr;
+ }
+ return pLeft;
+}
+
+void SwLineLayout::InitSpaceAdd()
+{
+ if ( !m_pLLSpaceAdd )
+ CreateSpaceAdd();
+ else
+ SetLLSpaceAdd( 0, 0 );
+}
+
+void SwLineLayout::CreateSpaceAdd( const tools::Long nInit )
+{
+ m_pLLSpaceAdd.reset( new std::vector<tools::Long> );
+ SetLLSpaceAdd( nInit, 0 );
+}
+
+// Returns true if there are only blanks in [nStt, nEnd[
+static bool lcl_HasOnlyBlanks(std::u16string_view rText, TextFrameIndex nStt, TextFrameIndex nEnd)
+{
+ bool bBlankOnly = true;
+ while ( nStt < nEnd )
+ {
+ const sal_Unicode cChar = rText[ sal_Int32(nStt++) ];
+ if ( ' ' != cChar && CH_FULL_BLANK != cChar && CH_SIX_PER_EM != cChar )
+ {
+ bBlankOnly = false;
+ break;
+ }
+ }
+ return bBlankOnly;
+}
+
+// Swapped out from FormatLine()
+void SwLineLayout::CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf )
+{
+ const sal_uInt16 nLineWidth = rInf.RealWidth();
+
+ sal_uInt16 nFlyAscent = 0;
+ sal_uInt16 nFlyHeight = 0;
+ sal_uInt16 nFlyDescent = 0;
+
+ // If this line has a clearing break, then this is the portion's height.
+ sal_uInt16 nBreakHeight = 0;
+
+ bool bOnlyPostIts = true;
+ SetHanging( false );
+
+ bool bTmpDummy = !GetLen();
+ SwFlyCntPortion* pFlyCnt = nullptr;
+ if( bTmpDummy )
+ {
+ nFlyAscent = 0;
+ nFlyHeight = 0;
+ nFlyDescent = 0;
+ }
+
+ // #i3952#
+ const bool bIgnoreBlanksAndTabsForLineHeightCalculation =
+ rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION);
+
+ bool bHasBlankPortion = false;
+ bool bHasOnlyBlankPortions = true;
+ bool bHasFlyPortion = false;
+
+ if( mpNextPortion )
+ {
+ SetContent( false );
+ if( mpNextPortion->IsBreakPortion() )
+ {
+ SetLen( mpNextPortion->GetLen() );
+ if( GetLen() )
+ bTmpDummy = false;
+ }
+ else
+ {
+ const SwTwips nLineHeight = Height();
+ Init( GetNextPortion() );
+ SwLinePortion *pPos = mpNextPortion;
+ SwLinePortion *pLast = this;
+ sal_uInt16 nMaxDescent = 0;
+
+ // A group is a segment in the portion chain of pCurr or a fixed
+ // portion spanning to the end or the next fixed portion
+ while( pPos )
+ {
+ SAL_WARN_IF( PortionType::NONE == pPos->GetWhichPor(),
+ "sw.core", "SwLineLayout::CalcLine: don't use SwLinePortions !" );
+
+ // Null portions are eliminated. They can form if two FlyFrames
+ // overlap.
+ if( !pPos->Compress() )
+ {
+ // Only take over Height and Ascent if the rest of the line
+ // is empty.
+ if( !pPos->GetNextPortion() )
+ {
+ if( !Height() )
+ Height( pPos->Height(), false );
+ if( !GetAscent() )
+ SetAscent( pPos->GetAscent() );
+ }
+ SwLinePortion* pPortion = pLast->Cut( pPos );
+ rLine.ClearIfIsFirstOfBorderMerge(pPortion);
+ delete pPortion;
+ pPos = pLast->GetNextPortion();
+ continue;
+ }
+
+ TextFrameIndex const nPorSttIdx = rInf.GetLineStart() + mnLineLength;
+ mnLineLength += pPos->GetLen();
+ AddPrtWidth( pPos->Width() );
+
+ // #i3952#
+ if (bIgnoreBlanksAndTabsForLineHeightCalculation && !rInf.GetLineStart())
+ {
+ if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
+ ( pPos->IsTextPortion() &&
+ lcl_HasOnlyBlanks( rInf.GetText(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
+ {
+ pLast = pPos;
+ pPos = pPos->GetNextPortion();
+ bHasBlankPortion = true;
+ continue;
+ }
+ }
+
+ // Ignore drop portion height
+ // tdf#130804 ... and bookmark portions
+ if ((pPos->IsDropPortion() && static_cast<SwDropPortion*>(pPos)->GetLines() > 1)
+ || pPos->GetWhichPor() == PortionType::Bookmark)
+ {
+ pLast = pPos;
+ pPos = pPos->GetNextPortion();
+ continue;
+ }
+
+ bHasOnlyBlankPortions = false;
+
+ // We had an attribute change: Sum up/build maxima of length and mass
+
+ SwTwips nPosHeight = pPos->Height();
+ SwTwips nPosAscent = pPos->GetAscent();
+
+ SAL_WARN_IF( nPosHeight < nPosAscent,
+ "sw.core", "SwLineLayout::CalcLine: bad ascent or height" );
+
+ if( pPos->IsHangingPortion() )
+ {
+ SetHanging(true);
+ rInf.GetParaPortion()->SetMargin();
+ }
+ else if( !bHasFlyPortion && ( pPos->IsFlyCntPortion() || pPos->IsFlyPortion() ) )
+ bHasFlyPortion = true;
+
+ // A line break portion only influences the height of the line in case it's the only
+ // portion in the line, except when it's a clearing break.
+ bool bClearingBreak = false;
+ if (pPos->IsBreakPortion())
+ {
+ auto pBreakPortion = static_cast<SwBreakPortion*>(pPos);
+ bClearingBreak = pBreakPortion->GetClear() != SwLineBreakClear::NONE;
+ nBreakHeight = nPosHeight;
+ }
+ if (!(pPos->IsBreakPortion() && !bClearingBreak) || !Height())
+ {
+ if (!pPos->IsPostItsPortion()) bOnlyPostIts = false;
+
+ if( bTmpDummy && !mnLineLength )
+ {
+ if( pPos->IsFlyPortion() )
+ {
+ if( nFlyHeight < nPosHeight )
+ nFlyHeight = nPosHeight;
+ if( nFlyAscent < nPosAscent )
+ nFlyAscent = nPosAscent;
+ if( nFlyDescent < nPosHeight - nPosAscent )
+ nFlyDescent = nPosHeight - nPosAscent;
+ }
+ else
+ {
+ if( pPos->InNumberGrp() )
+ {
+ sal_uInt16 nTmp = rInf.GetFont()->GetAscent(
+ rInf.GetVsh(), *rInf.GetOut() );
+ if( nTmp > nPosAscent )
+ {
+ nPosHeight += nTmp - nPosAscent;
+ nPosAscent = nTmp;
+ }
+ nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
+ *rInf.GetOut() );
+ if( nTmp > nPosHeight )
+ nPosHeight = nTmp;
+ }
+ Height( nPosHeight, false );
+ mnAscent = nPosAscent;
+ nMaxDescent = nPosHeight - nPosAscent;
+ }
+ }
+ else if( !pPos->IsFlyPortion() )
+ {
+ if( Height() < nPosHeight )
+ {
+ // Height is set to 0 when Init() is called.
+ if (bIgnoreBlanksAndTabsForLineHeightCalculation && pPos->IsFlyCntPortion())
+ // Compat flag set: take the line height, if it's larger.
+ Height(std::max(nPosHeight, nLineHeight), false);
+ else
+ // Just care about the portion height.
+ Height(nPosHeight, pPos->IsTextPortion());
+ }
+ SwFlyCntPortion* pAsFly(nullptr);
+ if(pPos->IsFlyCntPortion())
+ pAsFly = static_cast<SwFlyCntPortion*>(pPos);
+ if( pAsFly || ( pPos->IsMultiPortion()
+ && static_cast<SwMultiPortion*>(pPos)->HasFlyInContent() ) )
+ rLine.SetFlyInCntBase();
+ if(pAsFly && pAsFly->GetAlign() != sw::LineAlign::NONE)
+ {
+ pAsFly->SetMax(false);
+ if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() )
+ pFlyCnt = pAsFly;
+ }
+ else
+ {
+ if( mnAscent < nPosAscent )
+ mnAscent = nPosAscent;
+ if( nMaxDescent < nPosHeight - nPosAscent )
+ nMaxDescent = nPosHeight - nPosAscent;
+ }
+ }
+ }
+ else if( pPos->GetLen() )
+ bTmpDummy = false;
+
+ if( !HasContent() && !pPos->InNumberGrp() )
+ {
+ if ( pPos->InExpGrp() )
+ {
+ OUString aText;
+ if( pPos->GetExpText( rInf, aText ) && !aText.isEmpty() )
+ SetContent(true);
+ }
+ else if( ( pPos->InTextGrp() || pPos->IsMultiPortion() ) &&
+ pPos->GetLen() )
+ SetContent(true);
+ }
+
+ bTmpDummy &= !HasContent() && ( !pPos->Width() || pPos->IsFlyPortion() );
+
+ pLast = pPos;
+ pPos = pPos->GetNextPortion();
+ }
+
+ if( pFlyCnt )
+ {
+ if( pFlyCnt->Height() == Height() )
+ {
+ pFlyCnt->SetMax( true );
+ if( Height() > nMaxDescent + mnAscent )
+ {
+ if( sw::LineAlign::BOTTOM == pFlyCnt->GetAlign() )
+ mnAscent = Height() - nMaxDescent;
+ else if( sw::LineAlign::CENTER == pFlyCnt->GetAlign() )
+ mnAscent = ( Height() + mnAscent - nMaxDescent ) / 2;
+ }
+ pFlyCnt->SetAscent( mnAscent );
+ }
+ }
+
+ if( bTmpDummy && nFlyHeight )
+ {
+ mnAscent = nFlyAscent;
+ if( nFlyDescent > nFlyHeight - nFlyAscent )
+ Height( nFlyHeight + nFlyDescent, false );
+ else
+ {
+ if (nBreakHeight > nFlyHeight)
+ {
+ // The line has no content, but it has a clearing break: then the line
+ // height is not only the intersection of the fly and line's rectangle, but
+ // also includes the clearing break's height.
+ Height(nBreakHeight, false);
+ }
+ else
+ {
+ Height(nFlyHeight, false);
+ }
+ }
+ }
+ else if( nMaxDescent > Height() - mnAscent )
+ Height( nMaxDescent + mnAscent, false );
+
+ if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) )
+ {
+ Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
+ mnAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() );
+ }
+ }
+ }
+ else
+ {
+ SetContent( !bTmpDummy );
+
+ // #i3952#
+ if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
+ lcl_HasOnlyBlanks( rInf.GetText(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
+ {
+ bHasBlankPortion = true;
+ }
+ }
+
+ // #i3952#
+ if ( bHasBlankPortion && bHasOnlyBlankPortions )
+ {
+ sal_uInt16 nTmpAscent = GetAscent();
+ sal_uInt16 nTmpHeight = Height();
+ rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
+ SetAscent( nTmpAscent );
+ Height( nTmpHeight, false );
+ }
+
+ // Robust:
+ if( nLineWidth < Width() )
+ Width( nLineWidth );
+ SAL_WARN_IF( nLineWidth < Width(), "sw.core", "SwLineLayout::CalcLine: line is bursting" );
+ SetDummy( bTmpDummy );
+ std::pair<SwTextNode const*, sal_Int32> const start(
+ rInf.GetTextFrame()->MapViewToModel(rLine.GetStart()));
+ std::pair<SwTextNode const*, sal_Int32> const end(
+ rInf.GetTextFrame()->MapViewToModel(rLine.GetEnd()));
+ bool bHasRedline = rLine.GetRedln();
+ if( bHasRedline )
+ {
+ OUString sRedlineText;
+ bool bHasRedlineEnd;
+ enum RedlineType eRedlineEnd;
+ bHasRedline = rLine.GetRedln()->CheckLine(start.first->GetIndex(), start.second,
+ end.first->GetIndex(), end.second, sRedlineText, bHasRedlineEnd, eRedlineEnd);
+ if( bHasRedline )
+ {
+ SetRedlineText( sRedlineText );
+ if( bHasRedlineEnd )
+ SetRedlineEnd( bHasRedlineEnd );
+ if( eRedlineEnd != RedlineType::None )
+ SetRedlineEndType( eRedlineEnd );
+ }
+ }
+ SetRedline( bHasRedline );
+
+ // redlining: set crossing out for deleted anchored objects
+ if ( !bHasFlyPortion )
+ return;
+
+ SwLinePortion *pPos = mpNextPortion;
+ TextFrameIndex nLineLength;
+ while ( pPos )
+ {
+ TextFrameIndex const nPorSttIdx = rInf.GetLineStart() + nLineLength;
+ nLineLength += pPos->GetLen();
+ // anchored as characters
+ if( pPos->IsFlyCntPortion() )
+ {
+ bool bDeleted = false;
+ size_t nAuthor = std::string::npos;
+ if ( bHasRedline )
+ {
+ OUString sRedlineText;
+ bool bHasRedlineEnd;
+ enum RedlineType eRedlineEnd;
+ std::pair<SwTextNode const*, sal_Int32> const flyStart(
+ rInf.GetTextFrame()->MapViewToModel(nPorSttIdx));
+ bool bHasFlyRedline = rLine.GetRedln()->CheckLine(flyStart.first->GetIndex(),
+ flyStart.second, flyStart.first->GetIndex(), flyStart.second, sRedlineText,
+ bHasRedlineEnd, eRedlineEnd, /*pAuthorAtPos=*/&nAuthor);
+ bDeleted = bHasFlyRedline && eRedlineEnd == RedlineType::Delete;
+ }
+ static_cast<SwFlyCntPortion*>(pPos)->SetDeleted(bDeleted);
+ static_cast<SwFlyCntPortion*>(pPos)->SetAuthor(nAuthor);
+ }
+ // anchored to characters
+ else if ( pPos->IsFlyPortion() )
+ {
+ const IDocumentRedlineAccess& rIDRA =
+ rInf.GetTextFrame()->GetDoc().getIDocumentRedlineAccess();
+ SwSortedObjs *pObjs = rInf.GetTextFrame()->GetDrawObjs();
+ if ( pObjs && IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) )
+ {
+ for ( size_t i = 0; rInf.GetTextFrame()->GetDrawObjs() && i < pObjs->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*rInf.GetTextFrame()->GetDrawObjs())[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ bool bDeleted = false;
+ size_t nAuthor = std::string::npos;
+ const SwFormatAnchor& rAnchor = pAnchoredObj->GetFrameFormat().GetAnchor();
+ if ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR )
+ {
+ SwPosition aAnchor = *rAnchor.GetContentAnchor();
+ SwRedlineTable::size_type n = 0;
+ const SwRangeRedline* pFnd =
+ rIDRA.GetRedlineTable().FindAtPosition( aAnchor, n );
+ if ( pFnd && RedlineType::Delete == pFnd->GetType() )
+ {
+ bDeleted = true;
+ nAuthor = pFnd->GetAuthor();
+ }
+ }
+ pFly->SetDeleted(bDeleted);
+ pFly->SetAuthor(nAuthor);
+ }
+ }
+ }
+ }
+ pPos = pPos->GetNextPortion();
+ }
+}
+
+// #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
+// to control, if the fly content portions and line portion are considered.
+void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent,
+ SwTwips& _orDescent,
+ SwTwips& _orObjAscent,
+ SwTwips& _orObjDescent,
+ const SwLinePortion* _pDontConsiderPortion,
+ const bool _bNoFlyCntPorAndLinePor ) const
+{
+ _orAscent = 0;
+ _orDescent = 0;
+ _orObjAscent = 0;
+ _orObjDescent = 0;
+
+ const SwLinePortion* pTmpPortion = this;
+ if ( !pTmpPortion->GetLen() && pTmpPortion->GetNextPortion() )
+ {
+ pTmpPortion = pTmpPortion->GetNextPortion();
+ }
+
+ while ( pTmpPortion )
+ {
+ if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() &&
+ // tdf#130804 ignore bookmark portions
+ pTmpPortion->GetWhichPor() != PortionType::Bookmark &&
+ ( !_bNoFlyCntPorAndLinePor ||
+ ( !pTmpPortion->IsFlyCntPortion() &&
+ !(pTmpPortion == this && pTmpPortion->GetNextPortion() ) ) ) )
+ {
+ SwTwips nPortionAsc = pTmpPortion->GetAscent();
+ SwTwips nPortionDesc = pTmpPortion->Height() - nPortionAsc;
+
+ const bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
+ static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
+ ( pTmpPortion != _pDontConsiderPortion );
+
+ if ( bFlyCmp )
+ {
+ _orObjAscent = std::max( _orObjAscent, nPortionAsc );
+ _orObjDescent = std::max( _orObjDescent, nPortionDesc );
+ }
+
+ if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() )
+ {
+ _orAscent = std::max( _orAscent, nPortionAsc );
+ _orDescent = std::max( _orDescent, nPortionDesc );
+ }
+ }
+ pTmpPortion = pTmpPortion->GetNextPortion();
+ }
+}
+
+void SwLineLayout::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwLineLayout"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwLineLayout::ResetFlags()
+{
+ m_bFormatAdj = m_bDummy = m_bEndHyph = m_bMidHyph = m_bFly
+ = m_bRest = m_bBlinking = m_bClipping = m_bContent = m_bRedline
+ = m_bRedlineEnd = m_bForcedLeftMargin = m_bHanging = false;
+ m_eRedlineEnd = RedlineType::None;
+}
+
+SwLineLayout::SwLineLayout()
+ : m_pNext( nullptr ),
+ m_nRealHeight( 0 ),
+ m_nTextHeight( 0 ),
+ m_bUnderscore( false )
+{
+ ResetFlags();
+ SetWhichPor( PortionType::Lay );
+}
+
+SwLinePortion *SwLineLayout::GetFirstPortion() const
+{
+ const SwLinePortion *pRet = mpNextPortion ? mpNextPortion : this;
+ return const_cast<SwLinePortion*>(pRet);
+}
+
+SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange)
+{
+ if (TextFrameIndex(0) != rRange.m_nLen)
+ {
+ if (TextFrameIndex(0) == m_nLen) {
+ m_nStart = rRange.m_nStart;
+ m_nLen = rRange.m_nLen ;
+ }
+ else {
+ if(rRange.m_nStart + rRange.m_nLen > m_nStart + m_nLen) {
+ m_nLen = rRange.m_nStart + rRange.m_nLen - m_nStart;
+ }
+ if(rRange.m_nStart < m_nStart) {
+ m_nLen += m_nStart - rRange.m_nStart;
+ m_nStart = rRange.m_nStart;
+ }
+ }
+ }
+ return *this;
+}
+
+SwScriptInfo::SwScriptInfo()
+ : m_nInvalidityPos(0)
+ , m_nDefaultDir(0)
+{
+};
+
+SwScriptInfo::~SwScriptInfo()
+{
+}
+
+// Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to
+// Sw Script Types (SwFontScript::Latin, SwFontScript::CJK, SwFontScript::CTL), used to identify the font
+static SwFontScript lcl_ScriptToFont(sal_uInt16 const nScript)
+{
+ switch ( nScript ) {
+ case i18n::ScriptType::LATIN : return SwFontScript::Latin;
+ case i18n::ScriptType::ASIAN : return SwFontScript::CJK;
+ case i18n::ScriptType::COMPLEX : return SwFontScript::CTL;
+ }
+
+ OSL_FAIL( "Somebody tells lies about the script type!" );
+ return SwFontScript::Latin;
+}
+
+SwFontScript SwScriptInfo::WhichFont(TextFrameIndex const nIdx) const
+{
+ const sal_uInt16 nScript(ScriptType(nIdx));
+ return lcl_ScriptToFont(nScript);
+}
+
+SwFontScript SwScriptInfo::WhichFont(sal_Int32 nIdx, OUString const& rText)
+{
+ const sal_uInt16 nScript(g_pBreakIt->GetRealScriptOfText(rText, nIdx));
+ return lcl_ScriptToFont(nScript);
+}
+
+static void InitBookmarks(
+ std::optional<std::vector<sw::Extent>::const_iterator> oPrevIter,
+ std::vector<sw::Extent>::const_iterator iter,
+ std::vector<sw::Extent>::const_iterator const end,
+ TextFrameIndex nOffset,
+ std::vector<std::pair<sw::mark::IBookmark const*, SwScriptInfo::MarkKind>> & rBookmarks,
+ std::vector<std::pair<TextFrameIndex, SwScriptInfo::MarkKind>> & o_rBookmarks)
+{
+ SwTextNode const*const pNode(iter->pNode);
+ for (auto const& it : rBookmarks)
+ {
+ assert(iter->pNode == pNode || pNode->GetIndex() < iter->pNode->GetIndex());
+ assert(!oPrevIter || (*oPrevIter)->pNode->GetIndex() <= pNode->GetIndex());
+ switch (it.second)
+ {
+ case SwScriptInfo::MarkKind::Start:
+ {
+ // SwUndoSaveContent::DelContentIndex() is rather messy but
+ // apparently bookmarks "on the edge" are deleted if
+ // * point: equals start-of-selection (not end-of-selection)
+ // * expanded: one position equals edge of selection
+ // and other does not (is inside)
+ // interesting case: if end[/start] of the mark is on the
+ // start of first[/end of last] extent, and the other one
+ // is outside this merged paragraph, is it deleted or not?
+ // assume "no" because the line break it contains isn't deleted.
+ SwPosition const& rStart(it.first->GetMarkStart());
+ SwPosition const& rEnd(it.first->GetMarkEnd());
+ assert(&rStart.nNode.GetNode() == pNode);
+ while (iter != end)
+ {
+ if (&rStart.nNode.GetNode() != iter->pNode // iter moved to next node
+ || rStart.nContent.GetIndex() < iter->nStart)
+ {
+ if (rEnd.nNode.GetIndex() < iter->pNode->GetIndex()
+ || (&rEnd.nNode.GetNode() == iter->pNode && rEnd.nContent.GetIndex() <= iter->nStart))
+ {
+ break; // deleted - skip it
+ }
+ else
+ {
+ o_rBookmarks.emplace_back(nOffset, it.second);
+ break;
+ }
+ }
+ else if (rStart.nContent.GetIndex() <= iter->nEnd)
+ {
+ auto const iterNext(iter + 1);
+ if (rStart.nContent.GetIndex() == iter->nEnd
+ && (iterNext == end
+ ? &rEnd.nNode.GetNode() == iter->pNode
+ : (rEnd.nNode.GetIndex() < iterNext->pNode->GetIndex()
+ || (&rEnd.nNode.GetNode() == iterNext->pNode && rEnd.nContent.GetIndex() < iterNext->nStart))))
+ {
+ break; // deleted - skip it
+ }
+ else
+ {
+ o_rBookmarks.emplace_back(
+ nOffset + TextFrameIndex(rStart.nContent.GetIndex() - iter->nStart),
+ it.second);
+ break;
+ }
+ }
+ else
+ {
+ nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ oPrevIter = iter;
+ ++iter; // bookmarks are sorted...
+ }
+ }
+ if (iter == end)
+ {
+ if (pNode->GetIndex() < rEnd.nNode.GetIndex()) // pNode is last node of merged
+ {
+ break; // deleted - skip it
+ }
+ else
+ {
+ o_rBookmarks.emplace_back(nOffset, it.second);
+ }
+ }
+ break;
+ }
+ case SwScriptInfo::MarkKind::End:
+ {
+ SwPosition const& rEnd(it.first->GetMarkEnd());
+ assert(&rEnd.nNode.GetNode() == pNode);
+ while (true)
+ {
+ if (iter == end
+ || &rEnd.nNode.GetNode() != iter->pNode // iter moved to next node
+ || rEnd.nContent.GetIndex() <= iter->nStart)
+ {
+ SwPosition const& rStart(it.first->GetMarkStart());
+ // oPrevIter may point to pNode or a preceding node
+ if (oPrevIter
+ ? ((*oPrevIter)->pNode->GetIndex() < rStart.nNode.GetIndex()
+ || ((*oPrevIter)->pNode == &rStart.nNode.GetNode()
+ && ((iter != end && &rEnd.nNode.GetNode() == iter->pNode && rEnd.nContent.GetIndex() == iter->nStart)
+ ? (*oPrevIter)->nEnd < rStart.nContent.GetIndex()
+ : (*oPrevIter)->nEnd <= rStart.nContent.GetIndex())))
+ : rStart.nNode == rEnd.nNode)
+ {
+ break; // deleted - skip it
+ }
+ else
+ {
+ o_rBookmarks.emplace_back(nOffset, it.second);
+ break;
+ }
+ }
+ else if (rEnd.nContent.GetIndex() <= iter->nEnd)
+ {
+ o_rBookmarks.emplace_back(
+ nOffset + TextFrameIndex(rEnd.nContent.GetIndex() - iter->nStart),
+ it.second);
+ break;
+ }
+ else
+ {
+ nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ oPrevIter = iter;
+ ++iter;
+ }
+ }
+ break;
+ }
+ case SwScriptInfo::MarkKind::Point:
+ {
+ SwPosition const& rPos(it.first->GetMarkPos());
+ assert(&rPos.nNode.GetNode() == pNode);
+ while (iter != end)
+ {
+ if (&rPos.nNode.GetNode() != iter->pNode // iter moved to next node
+ || rPos.nContent.GetIndex() < iter->nStart)
+ {
+ break; // deleted - skip it
+ }
+ else if (rPos.nContent.GetIndex() <= iter->nEnd)
+ {
+ if (rPos.nContent.GetIndex() == iter->nEnd
+ && rPos.nContent.GetIndex() != iter->pNode->Len())
+ {
+ break; // deleted - skip it
+ }
+ else
+ {
+ o_rBookmarks.emplace_back(
+ nOffset + TextFrameIndex(rPos.nContent.GetIndex() - iter->nStart),
+ it.second);
+ }
+ break;
+ }
+ else
+ {
+ nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ oPrevIter = iter;
+ ++iter;
+ }
+ }
+ break;
+ }
+ }
+ if (iter == end)
+ {
+ break; // remaining marks are hidden
+ }
+ }
+}
+
+// searches for script changes in rText and stores them
+void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
+ sw::MergedPara const*const pMerged)
+{
+ InitScriptInfo( rNode, pMerged, m_nDefaultDir == UBIDI_RTL );
+}
+
+void SwScriptInfo::InitScriptInfo(const SwTextNode& rNode,
+ sw::MergedPara const*const pMerged, bool bRTL)
+{
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ const OUString& rText(pMerged ? pMerged->mergedText : rNode.GetText());
+
+ // HIDDEN TEXT INFORMATION
+
+ m_Bookmarks.clear();
+ m_HiddenChg.clear();
+ if (pMerged)
+ {
+ SwTextNode const* pNode(nullptr);
+ TextFrameIndex nOffset(0);
+ std::optional<std::vector<sw::Extent>::const_iterator> oPrevIter;
+ for (auto iter = pMerged->extents.begin(); iter != pMerged->extents.end();
+ oPrevIter = iter)
+ {
+ if (iter->pNode == pNode)
+ {
+ nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ ++iter;
+ continue; // skip extents at end of previous node
+ }
+ pNode = iter->pNode;
+ Range aRange( 0, pNode->Len() > 0 ? pNode->Len() - 1 : 0 );
+ MultiSelection aHiddenMulti( aRange );
+ std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> bookmarks;
+ CalcHiddenRanges(*pNode, aHiddenMulti, &bookmarks);
+
+ InitBookmarks(oPrevIter, iter, pMerged->extents.end(), nOffset, bookmarks, m_Bookmarks);
+
+ for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ const sal_Int32 nStart = rRange.Min();
+ const sal_Int32 nEnd = rRange.Max() + 1;
+ bool isStartHandled(false);
+ ::std::optional<sal_Int32> oExtend;
+
+ if (nEnd <= iter->nStart)
+ { // entirely in gap, skip this hidden range
+ continue;
+ }
+
+ do
+ {
+ if (!isStartHandled && nStart <= iter->nEnd)
+ {
+ isStartHandled = true;
+ if (nStart <= iter->nStart && !m_HiddenChg.empty()
+ && m_HiddenChg.back() == nOffset)
+ {
+ // previous one went until end of extent, extend it
+ oExtend.emplace(::std::min(iter->nEnd, nEnd) - ::std::max(iter->nStart, nStart));
+ }
+ else
+ {
+ m_HiddenChg.push_back(nOffset + TextFrameIndex(::std::max(nStart - iter->nStart, sal_Int32(0))));
+ }
+ }
+ else if (oExtend)
+ {
+ *oExtend += ::std::min(iter->nEnd, nEnd) - iter->nStart;
+ }
+ if (nEnd <= iter->nEnd)
+ {
+ if (oExtend)
+ {
+ m_HiddenChg.back() += TextFrameIndex(*oExtend);
+ }
+ else
+ {
+ m_HiddenChg.push_back(nOffset + TextFrameIndex(::std::max(nEnd - iter->nStart, sal_Int32(0))));
+ }
+ break; // iterate to next hidden range
+ }
+ nOffset += TextFrameIndex(iter->nEnd - iter->nStart);
+ ++iter;
+ }
+ while (iter != pMerged->extents.end() && iter->pNode == pNode);
+ if (iter == pMerged->extents.end() || iter->pNode != pNode)
+ {
+ if (isStartHandled)
+ { // dangling end
+ if (oExtend)
+ {
+ m_HiddenChg.back() += TextFrameIndex(*oExtend);
+ }
+ else
+ {
+ m_HiddenChg.push_back(nOffset);
+ }
+ } // else: beyond last extent in node, ignore
+ break; // skip hidden ranges beyond last extent in node
+ }
+ }
+ }
+ }
+ else
+ {
+ Range aRange( 0, !rText.isEmpty() ? rText.getLength() - 1 : 0 );
+ MultiSelection aHiddenMulti( aRange );
+ std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> bookmarks;
+ CalcHiddenRanges(rNode, aHiddenMulti, &bookmarks);
+
+ for (auto const& it : bookmarks)
+ {
+ switch (it.second)
+ {
+ case MarkKind::Start:
+ m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkStart().nContent.GetIndex()), it.second);
+ break;
+ case MarkKind::End:
+ m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkEnd().nContent.GetIndex()), it.second);
+ break;
+ case MarkKind::Point:
+ m_Bookmarks.emplace_back(TextFrameIndex(it.first->GetMarkPos().nContent.GetIndex()), it.second);
+ break;
+ }
+ }
+
+ m_HiddenChg.reserve( aHiddenMulti.GetRangeCount() * 2 );
+ for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ const sal_Int32 nStart = rRange.Min();
+ const sal_Int32 nEnd = rRange.Max() + 1;
+
+ m_HiddenChg.push_back( TextFrameIndex(nStart) );
+ m_HiddenChg.push_back( TextFrameIndex(nEnd) );
+ }
+ }
+
+ // SCRIPT AND SCRIPT RELATED INFORMATION
+
+ TextFrameIndex nChg = m_nInvalidityPos;
+
+ // COMPLETE_STRING means the data structure is up to date
+ m_nInvalidityPos = TextFrameIndex(COMPLETE_STRING);
+
+ // this is the default direction
+ m_nDefaultDir = static_cast<sal_uInt8>(bRTL ? UBIDI_RTL : UBIDI_LTR);
+
+ // counter for script info arrays
+ size_t nCnt = 0;
+ // counter for compression information arrays
+ size_t nCntComp = 0;
+ // counter for kashida array
+ size_t nCntKash = 0;
+
+ sal_Int16 nScript = i18n::ScriptType::LATIN;
+
+ // compression type
+ const CharCompressType aCompEnum = rNode.getIDocumentSettingAccess()->getCharacterCompressionType();
+
+ auto const& rParaItems((pMerged ? *pMerged->pParaPropsNode : rNode).GetSwAttrSet());
+ // justification type
+ const bool bAdjustBlock = SvxAdjust::Block == rParaItems.GetAdjust().GetAdjust();
+
+ // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
+
+ if( nChg )
+ {
+ // if change position = 0 we do not use any data from the arrays
+ // because by deleting all characters of the first group at the beginning
+ // of a paragraph nScript is set to a wrong value
+ SAL_WARN_IF( !CountScriptChg(), "sw.core", "Where're my changes of script?" );
+ while( nCnt < CountScriptChg() )
+ {
+ if ( nChg > GetScriptChg( nCnt ) )
+ nCnt++;
+ else
+ {
+ nScript = GetScriptType( nCnt );
+ break;
+ }
+ }
+ if( CharCompressType::NONE != aCompEnum )
+ {
+ while( nCntComp < CountCompChg() )
+ {
+ if ( nChg <= GetCompStart( nCntComp ) )
+ break;
+ nCntComp++;
+ }
+ }
+ if ( bAdjustBlock )
+ {
+ while( nCntKash < CountKashida() )
+ {
+ if ( nChg <= GetKashida( nCntKash ) )
+ break;
+ nCntKash++;
+ }
+ }
+ }
+
+ // ADJUST nChg VALUE:
+
+ // by stepping back one position we know that we are inside a group
+ // declared as an nScript group
+ if ( nChg )
+ --nChg;
+
+ const TextFrameIndex nGrpStart = nCnt ? GetScriptChg(nCnt - 1) : TextFrameIndex(0);
+
+ // we go back in our group until we reach the first character of
+ // type nScript
+ while ( nChg > nGrpStart &&
+ nScript != g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)))
+ --nChg;
+
+ // If we are at the start of a group, we do not trust nScript,
+ // we better get nScript from the breakiterator:
+ if ( nChg == nGrpStart )
+ nScript = static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)));
+
+ // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
+
+ // remove invalid entries from script information arrays
+ m_ScriptChanges.erase(m_ScriptChanges.begin() + nCnt, m_ScriptChanges.end());
+
+ // get the start of the last compression group
+ TextFrameIndex nLastCompression = nChg;
+ if( nCntComp )
+ {
+ --nCntComp;
+ nLastCompression = GetCompStart( nCntComp );
+ if( nChg >= nLastCompression + GetCompLen( nCntComp ) )
+ {
+ nLastCompression = nChg;
+ ++nCntComp;
+ }
+ }
+
+ // remove invalid entries from compression information arrays
+ m_CompressionChanges.erase(m_CompressionChanges.begin() + nCntComp,
+ m_CompressionChanges.end());
+
+ // get the start of the last kashida group
+ TextFrameIndex nLastKashida = nChg;
+ if( nCntKash && i18n::ScriptType::COMPLEX == nScript )
+ {
+ --nCntKash;
+ nLastKashida = GetKashida( nCntKash );
+ }
+
+ // remove invalid entries from kashida array
+ m_Kashida.erase(m_Kashida.begin() + nCntKash, m_Kashida.end());
+
+ // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
+ // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
+
+ if (WEAK == g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)))
+ {
+ // If the beginning of the current group is weak, this means that
+ // all of the characters in this group are weak. We have to assign
+ // the scripts to these characters depending on the fonts which are
+ // set for these characters to display them.
+ TextFrameIndex nEnd(
+ g_pBreakIt->GetBreakIter()->endOfScript(rText, sal_Int32(nChg), WEAK));
+
+ if (nEnd > TextFrameIndex(rText.getLength()) || nEnd < TextFrameIndex(0))
+ nEnd = TextFrameIndex(rText.getLength());
+
+ nScript = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+
+ SAL_WARN_IF( i18n::ScriptType::LATIN != nScript &&
+ i18n::ScriptType::ASIAN != nScript &&
+ i18n::ScriptType::COMPLEX != nScript, "sw.core", "Wrong default language" );
+
+ nChg = nEnd;
+
+ // Get next script type or set to weak in order to exit
+ sal_uInt8 nNextScript = (nEnd < TextFrameIndex(rText.getLength()))
+ ? static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nEnd)))
+ : sal_uInt8(WEAK);
+
+ if ( nScript != nNextScript )
+ {
+ m_ScriptChanges.emplace_back(nEnd, nScript);
+ nCnt++;
+ nScript = nNextScript;
+ }
+ }
+
+ // UPDATE THE SCRIPT INFO ARRAYS:
+
+ while (nChg < TextFrameIndex(rText.getLength())
+ || (m_ScriptChanges.empty() && rText.isEmpty()))
+ {
+ SAL_WARN_IF( i18n::ScriptType::WEAK == nScript,
+ "sw.core", "Inserting WEAK into SwScriptInfo structure" );
+
+ TextFrameIndex nSearchStt = nChg;
+ nChg = TextFrameIndex(g_pBreakIt->GetBreakIter()->endOfScript(
+ rText, sal_Int32(nSearchStt), nScript));
+
+ if (nChg > TextFrameIndex(rText.getLength()) || nChg < TextFrameIndex(0))
+ nChg = TextFrameIndex(rText.getLength());
+
+ // #i28203#
+ // for 'complex' portions, we make sure that a portion does not contain more
+ // than one script:
+ if( i18n::ScriptType::COMPLEX == nScript )
+ {
+ const short nScriptType = ScriptTypeDetector::getCTLScriptType(
+ rText, sal_Int32(nSearchStt) );
+ TextFrameIndex nNextCTLScriptStart = nSearchStt;
+ short nCurrentScriptType = nScriptType;
+ while( css::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
+ {
+ nNextCTLScriptStart = TextFrameIndex(
+ ScriptTypeDetector::endOfCTLScriptType(
+ rText, sal_Int32(nNextCTLScriptStart)));
+ if (nNextCTLScriptStart >= TextFrameIndex(rText.getLength())
+ || nNextCTLScriptStart >= nChg)
+ break;
+ nCurrentScriptType = ScriptTypeDetector::getCTLScriptType(
+ rText, sal_Int32(nNextCTLScriptStart));
+ }
+ nChg = std::min( nChg, nNextCTLScriptStart );
+ }
+
+ // special case for dotted circle since it can be used with complex
+ // before a mark, so we want it associated with the mark's script
+ if (nChg < TextFrameIndex(rText.getLength()) && nChg > TextFrameIndex(0)
+ && (i18n::ScriptType::WEAK ==
+ g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg) - 1)))
+ {
+ int8_t nType = u_charType(rText[sal_Int32(nChg)]);
+ if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
+ nType == U_COMBINING_SPACING_MARK )
+ {
+ m_ScriptChanges.emplace_back(nChg-TextFrameIndex(1), nScript);
+ }
+ else
+ {
+ m_ScriptChanges.emplace_back(nChg, nScript);
+ }
+ }
+ else
+ {
+ m_ScriptChanges.emplace_back(nChg, nScript);
+ }
+ ++nCnt;
+
+ // if current script is asian, we search for compressible characters
+ // in this range
+ if ( CharCompressType::NONE != aCompEnum &&
+ i18n::ScriptType::ASIAN == nScript )
+ {
+ CompType ePrevState = NONE;
+ CompType eState = NONE;
+ TextFrameIndex nPrevChg = nLastCompression;
+
+ while ( nLastCompression < nChg )
+ {
+ sal_Unicode cChar = rText[ sal_Int32(nLastCompression) ];
+
+ // examine current character
+ switch ( cChar )
+ {
+ // Left punctuation found
+ case 0x3008: case 0x300A: case 0x300C: case 0x300E:
+ case 0x3010: case 0x3014: case 0x3016: case 0x3018:
+ case 0x301A: case 0x301D:
+ eState = SPECIAL_LEFT;
+ break;
+ // Right punctuation found
+ case 0x3009: case 0x300B:
+ case 0x300D: case 0x300F: case 0x3011: case 0x3015:
+ case 0x3017: case 0x3019: case 0x301B: case 0x301E:
+ case 0x301F:
+ eState = SPECIAL_RIGHT;
+ break;
+ case 0x3001: case 0x3002: // Fullstop or comma
+ eState = SPECIAL_MIDDLE ;
+ break;
+ default:
+ eState = ( 0x3040 <= cChar && 0x3100 > cChar ) ? KANA : NONE;
+ }
+
+ // insert range of compressible characters
+ if( ePrevState != eState )
+ {
+ if ( ePrevState != NONE )
+ {
+ // insert start and type
+ if ( CharCompressType::PunctuationAndKana == aCompEnum ||
+ ePrevState != KANA )
+ {
+ m_CompressionChanges.emplace_back(nPrevChg,
+ nLastCompression - nPrevChg, ePrevState);
+ }
+ }
+
+ ePrevState = eState;
+ nPrevChg = nLastCompression;
+ }
+
+ nLastCompression++;
+ }
+
+ // we still have to examine last entry
+ if ( ePrevState != NONE )
+ {
+ // insert start and type
+ if ( CharCompressType::PunctuationAndKana == aCompEnum ||
+ ePrevState != KANA )
+ {
+ m_CompressionChanges.emplace_back(nPrevChg,
+ nLastCompression - nPrevChg, ePrevState);
+ }
+ }
+ }
+
+ // we search for connecting opportunities (kashida)
+ else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
+ {
+ // sw_redlinehide: this is the only place that uses SwScanner with
+ // frame text, so we convert to sal_Int32 here
+ std::function<LanguageType (sal_Int32, sal_Int32, bool)> const pGetLangOfCharM(
+ [&pMerged](sal_Int32 const nBegin, sal_uInt16 const script, bool const bNoChar)
+ {
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ sw::MapViewToModel(*pMerged, TextFrameIndex(nBegin)));
+ return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, script);
+ });
+ std::function<LanguageType (sal_Int32, sal_Int32, bool)> const pGetLangOfChar1(
+ [&rNode](sal_Int32 const nBegin, sal_uInt16 const script, bool const bNoChar)
+ { return rNode.GetLang(nBegin, bNoChar ? 0 : 1, script); });
+ auto pGetLangOfChar(pMerged ? pGetLangOfCharM : pGetLangOfChar1);
+ SwScanner aScanner( pGetLangOfChar, rText, nullptr, ModelToViewHelper(),
+ i18n::WordType::DICTIONARY_WORD,
+ sal_Int32(nLastKashida), sal_Int32(nChg));
+
+ // the search has to be performed on a per word base
+ while ( aScanner.NextWord() )
+ {
+ const OUString& rWord = aScanner.GetWord();
+
+ sal_Int32 nIdx = 0;
+ sal_Int32 nKashidaPos = -1;
+ sal_Unicode cCh;
+ sal_Unicode cPrevCh = 0;
+
+ int nPriorityLevel = 7; // 0..6 = level found
+ // 7 not found
+
+ sal_Int32 nWordLen = rWord.getLength();
+
+ // ignore trailing vowel chars
+ while( nWordLen && isTransparentChar( rWord[ nWordLen - 1 ] ))
+ --nWordLen;
+
+ while (nIdx < nWordLen)
+ {
+ cCh = rWord[ nIdx ];
+
+ // 1. Priority:
+ // after user inserted kashida
+ if ( 0x640 == cCh )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx;
+ nPriorityLevel = 0;
+ }
+
+ // 2. Priority:
+ // after a Seen or Sad
+ if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
+ {
+ if( isSeenOrSadChar( cCh )
+ && (rWord[ nIdx+1 ] != 0x200C) ) // #i98410#: prevent ZWNJ expansion
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx;
+ nPriorityLevel = 1;
+ }
+ }
+
+ // 3. Priority:
+ // before final form of Teh Marbuta, Heh, Dal
+ if ( nPriorityLevel >= 2 && nIdx > 0 )
+ {
+ if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
+ isDalChar ( cCh ) || // Dal (right joining) final form may appear in the middle of word
+ ( isHehChar ( cCh ) && nIdx == nWordLen - 1)) // Heh (dual joining) only at end of word
+ {
+
+ SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 2;
+ }
+ }
+ }
+
+ // 4. Priority:
+ // before final form of Alef, Tah, Lam, Kaf or Gaf
+ if ( nPriorityLevel >= 3 && nIdx > 0 )
+ {
+ if ( isAlefChar ( cCh ) || // Alef (right joining) final form may appear in the middle of word
+ (( isLamChar ( cCh ) || // Lam,
+ isTahChar ( cCh ) || // Tah,
+ isKafChar ( cCh ) || // Kaf (all dual joining)
+ isGafChar ( cCh ) )
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 3;
+ }
+ }
+ }
+
+ // 5. Priority:
+ // before medial Beh-like
+ if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
+ {
+ if ( isBehChar ( cCh ) )
+ {
+ // check if next character is Reh or Yeh-like
+ sal_Unicode cNextCh = rWord[ nIdx + 1 ];
+ if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 4;
+ }
+ }
+ }
+ }
+
+ // 6. Priority:
+ // before the final form of Waw, Ain, Qaf and Feh
+ if ( nPriorityLevel >= 5 && nIdx > 0 )
+ {
+ if ( isWawChar ( cCh ) || // Wav (right joining)
+ // final form may appear in the middle of word
+ (( isAinChar ( cCh ) || // Ain (dual joining)
+ isQafChar ( cCh ) || // Qaf (dual joining)
+ isFehChar ( cCh ) ) // Feh (dual joining)
+ && nIdx == nWordLen - 1)) // only at end of word
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 5;
+ }
+ }
+ }
+
+ // other connecting possibilities
+ if ( nPriorityLevel >= 6 && nIdx > 0 )
+ {
+ // Reh, Zain
+ if ( isRehChar ( cCh ) )
+ {
+ SAL_WARN_IF( 0 == cPrevCh, "sw.core", "No previous character" );
+ // check if character is connectable to previous character,
+ if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
+ {
+ nKashidaPos = aScanner.GetBegin() + nIdx - 1;
+ nPriorityLevel = 6;
+ }
+ }
+ }
+
+ // Do not consider vowel marks when checking if a character
+ // can be connected to previous character.
+ if ( !isTransparentChar ( cCh) )
+ cPrevCh = cCh;
+
+ ++nIdx;
+ } // end of current word
+
+ if ( -1 != nKashidaPos )
+ {
+ m_Kashida.insert(m_Kashida.begin() + nCntKash, TextFrameIndex(nKashidaPos));
+ nCntKash++;
+ }
+ } // end of kashida search
+ }
+
+ if (nChg < TextFrameIndex(rText.getLength()))
+ nScript = static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType(rText, sal_Int32(nChg)));
+
+ nLastCompression = nChg;
+ nLastKashida = nChg;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ // check kashida data
+ TextFrameIndex nTmpKashidaPos(-1);
+ bool bWrongKash = false;
+ for (size_t i = 0; i < m_Kashida.size(); ++i)
+ {
+ TextFrameIndex nCurrKashidaPos = GetKashida( i );
+ if ( nCurrKashidaPos <= nTmpKashidaPos )
+ {
+ bWrongKash = true;
+ break;
+ }
+ nTmpKashidaPos = nCurrKashidaPos;
+ }
+ SAL_WARN_IF( bWrongKash, "sw.core", "Kashida array contains wrong data" );
+#endif
+
+ // remove invalid entries from direction information arrays
+ m_DirectionChanges.clear();
+
+ // Perform Unicode Bidi Algorithm for text direction information
+ {
+ UpdateBidiInfo( rText );
+
+ // #i16354# Change script type for RTL text to CTL:
+ // 1. All text in RTL runs will use the CTL font
+ // #i89825# change the script type also to CTL (hennerdrewes)
+ // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
+ for (size_t nDirIdx = 0; nDirIdx < m_DirectionChanges.size(); ++nDirIdx)
+ {
+ const sal_uInt8 nCurrDirType = GetDirType( nDirIdx );
+ // nStart is start of RTL run:
+ const TextFrameIndex nStart = nDirIdx > 0 ? GetDirChg(nDirIdx - 1) : TextFrameIndex(0);
+ // nEnd is end of RTL run:
+ const TextFrameIndex nEnd = GetDirChg( nDirIdx );
+
+ if ( nCurrDirType % 2 == UBIDI_RTL || // text in RTL run
+ (nCurrDirType > UBIDI_LTR && // non-strong text in embedded LTR run
+ !lcl_HasStrongLTR(rText, sal_Int32(nStart), sal_Int32(nEnd))))
+ {
+ // nScriptIdx points into the ScriptArrays:
+ size_t nScriptIdx = 0;
+
+ // Skip entries in ScriptArray which are not inside the RTL run:
+ // Make nScriptIdx become the index of the script group with
+ // 1. nStartPosOfGroup <= nStart and
+ // 2. nEndPosOfGroup > nStart
+ while ( GetScriptChg( nScriptIdx ) <= nStart )
+ ++nScriptIdx;
+
+ const TextFrameIndex nStartPosOfGroup = nScriptIdx
+ ? GetScriptChg(nScriptIdx - 1)
+ : TextFrameIndex(0);
+ const sal_uInt8 nScriptTypeOfGroup = GetScriptType( nScriptIdx );
+
+ SAL_WARN_IF( nStartPosOfGroup > nStart || GetScriptChg( nScriptIdx ) <= nStart,
+ "sw.core", "Script override with CTL font trouble" );
+
+ // Check if we have to insert a new script change at
+ // position nStart. If nStartPosOfGroup < nStart,
+ // we have to insert a new script change:
+ if (nStart > TextFrameIndex(0) && nStartPosOfGroup < nStart)
+ {
+ m_ScriptChanges.insert(m_ScriptChanges.begin() + nScriptIdx,
+ ScriptChangeInfo(nStart, nScriptTypeOfGroup) );
+ ++nScriptIdx;
+ }
+
+ // Remove entries in ScriptArray which end inside the RTL run:
+ while (nScriptIdx < m_ScriptChanges.size()
+ && GetScriptChg(nScriptIdx) <= nEnd)
+ {
+ m_ScriptChanges.erase(m_ScriptChanges.begin() + nScriptIdx);
+ }
+
+ // Insert a new entry in ScriptArray for the end of the RTL run:
+ m_ScriptChanges.insert(m_ScriptChanges.begin() + nScriptIdx,
+ ScriptChangeInfo(nEnd, i18n::ScriptType::COMPLEX) );
+
+#if OSL_DEBUG_LEVEL > 1
+ // Check that ScriptChangeInfos are in increasing order of
+ // position and that we don't have "empty" changes.
+ sal_uInt8 nLastTyp = i18n::ScriptType::WEAK;
+ TextFrameIndex nLastPos = TextFrameIndex(0);
+ for (const auto& rScriptChange : m_ScriptChanges)
+ {
+ SAL_WARN_IF( nLastTyp == rScriptChange.type ||
+ nLastPos >= rScriptChange.position,
+ "sw.core", "Heavy InitScriptType() confusion" );
+ nLastPos = rScriptChange.position;
+ nLastTyp = rScriptChange.type;
+ }
+#endif
+ }
+ }
+ }
+}
+
+void SwScriptInfo::UpdateBidiInfo( const OUString& rText )
+{
+ // remove invalid entries from direction information arrays
+ m_DirectionChanges.clear();
+
+ // Bidi functions from icu 2.0
+
+ UErrorCode nError = U_ZERO_ERROR;
+ UBiDi* pBidi = ubidi_openSized( rText.getLength(), 0, &nError );
+ nError = U_ZERO_ERROR;
+
+ ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rText.getStr()), rText.getLength(),
+ m_nDefaultDir, nullptr, &nError );
+ nError = U_ZERO_ERROR;
+ int nCount = ubidi_countRuns( pBidi, &nError );
+ int32_t nStart = 0;
+ int32_t nEnd;
+ UBiDiLevel nCurrDir;
+ for ( int nIdx = 0; nIdx < nCount; ++nIdx )
+ {
+ ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
+ m_DirectionChanges.emplace_back(TextFrameIndex(nEnd), nCurrDir);
+ nStart = nEnd;
+ }
+
+ ubidi_close( pBidi );
+}
+
+// returns the position of the next character which belongs to another script
+// than the character of the actual (input) position.
+// If there's no script change until the end of the paragraph, it will return
+// COMPLETE_STRING.
+// Scripts are Asian (Chinese, Japanese, Korean),
+// Latin ( English etc.)
+// and Complex ( Hebrew, Arabian )
+TextFrameIndex SwScriptInfo::NextScriptChg(const TextFrameIndex nPos) const
+{
+ const size_t nEnd = CountScriptChg();
+ for( size_t nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetScriptChg( nX ) )
+ return GetScriptChg( nX );
+ }
+
+ return TextFrameIndex(COMPLETE_STRING);
+}
+
+// returns the script of the character at the input position
+sal_Int16 SwScriptInfo::ScriptType(const TextFrameIndex nPos) const
+{
+ const size_t nEnd = CountScriptChg();
+ for( size_t nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetScriptChg( nX ) )
+ return GetScriptType( nX );
+ }
+
+ // the default is the application language script
+ return SvtLanguageOptions::GetI18NScriptTypeOfLanguage( GetAppLanguage() );
+}
+
+TextFrameIndex SwScriptInfo::NextDirChg(const TextFrameIndex nPos,
+ const sal_uInt8* pLevel ) const
+{
+ const sal_uInt8 nCurrDir = pLevel ? *pLevel : 62;
+ const size_t nEnd = CountDirChg();
+ for( size_t nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetDirChg( nX ) &&
+ ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) )
+ return GetDirChg( nX );
+ }
+
+ return TextFrameIndex(COMPLETE_STRING);
+}
+
+sal_uInt8 SwScriptInfo::DirType(const TextFrameIndex nPos) const
+{
+ const size_t nEnd = CountDirChg();
+ for( size_t nX = 0; nX < nEnd; ++nX )
+ {
+ if( nPos < GetDirChg( nX ) )
+ return GetDirType( nX );
+ }
+
+ return 0;
+}
+
+TextFrameIndex SwScriptInfo::NextHiddenChg(TextFrameIndex const nPos) const
+{
+ for (auto const& it : m_HiddenChg)
+ {
+ if (nPos < it)
+ {
+ return it;
+ }
+ }
+ return TextFrameIndex(COMPLETE_STRING);
+}
+
+TextFrameIndex SwScriptInfo::NextBookmark(TextFrameIndex const nPos) const
+{
+ for (auto const& it : m_Bookmarks)
+ {
+ if (nPos < it.first)
+ {
+ return it.first;
+ }
+ }
+ return TextFrameIndex(COMPLETE_STRING);
+}
+
+auto SwScriptInfo::GetBookmark(TextFrameIndex const nPos) const -> MarkKind
+{
+ MarkKind ret{0};
+ for (auto const& it : m_Bookmarks)
+ {
+ if (nPos == it.first)
+ {
+ ret |= it.second;
+ }
+ else if (nPos < it.first)
+ {
+ break;
+ }
+ }
+ return ret;
+}
+
+// Takes a string and replaced the hidden ranges with cChar.
+sal_Int32 SwScriptInfo::MaskHiddenRanges( const SwTextNode& rNode, OUStringBuffer & rText,
+ const sal_Int32 nStt, const sal_Int32 nEnd,
+ const sal_Unicode cChar )
+{
+ assert(rNode.GetText().getLength() == rText.getLength());
+
+ std::vector<sal_Int32> aList;
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ sal_Int32 nNumOfHiddenChars = 0;
+ GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
+ auto rFirst( aList.crbegin() );
+ auto rLast( aList.crend() );
+ while ( rFirst != rLast )
+ {
+ nHiddenEnd = *(rFirst++);
+ nHiddenStart = *(rFirst++);
+
+ if ( nHiddenEnd < nStt || nHiddenStart > nEnd )
+ continue;
+
+ while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
+ {
+ if (nHiddenStart >= nStt)
+ {
+ rText[nHiddenStart] = cChar;
+ ++nNumOfHiddenChars;
+ }
+ ++nHiddenStart;
+ }
+ }
+
+ return nNumOfHiddenChars;
+}
+
+// Takes a SwTextNode and deletes the hidden ranges from the node.
+void SwScriptInfo::DeleteHiddenRanges( SwTextNode& rNode )
+{
+ std::vector<sal_Int32> aList;
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
+ auto rFirst( aList.crbegin() );
+ auto rLast( aList.crend() );
+ while ( rFirst != rLast )
+ {
+ nHiddenEnd = *(rFirst++);
+ nHiddenStart = *(rFirst++);
+
+ SwPaM aPam( rNode, nHiddenStart, rNode, nHiddenEnd );
+ rNode.getIDocumentContentOperations().DeleteRange( aPam );
+ }
+}
+
+bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTextNode& rNode, sal_Int32 nPos,
+ sal_Int32& rnStartPos, sal_Int32& rnEndPos,
+ std::vector<sal_Int32>* pList )
+{
+ rnStartPos = COMPLETE_STRING;
+ rnEndPos = 0;
+
+ bool bNewContainsHiddenChars = false;
+
+ // Optimization: First examine the flags at the text node:
+
+ if ( !rNode.IsCalcHiddenCharFlags() )
+ {
+ bool bWholePara = rNode.HasHiddenCharAttribute( true );
+ bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false );
+ if ( !bContainsHiddenChars )
+ return false;
+
+ if ( bWholePara )
+ {
+ if ( pList )
+ {
+ pList->push_back( 0 );
+ pList->push_back(rNode.GetText().getLength());
+ }
+
+ rnStartPos = 0;
+ rnEndPos = rNode.GetText().getLength();
+ return true;
+ }
+ }
+
+ // sw_redlinehide: this won't work if it's merged
+#if 0
+ const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
+ if ( pSI )
+ {
+
+ // Check first, if we have a valid SwScriptInfo object for this text node:
+
+ bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList );
+ const bool bNewHiddenCharsHidePara =
+ rnStartPos == 0 && rnEndPos >= rNode.GetText().getLength();
+ rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
+ }
+ else
+#endif
+ {
+
+ // No valid SwScriptInfo Object, we have to do it the hard way:
+
+ Range aRange(0, (!rNode.GetText().isEmpty())
+ ? rNode.GetText().getLength() - 1
+ : 0);
+ MultiSelection aHiddenMulti( aRange );
+ SwScriptInfo::CalcHiddenRanges(rNode, aHiddenMulti, nullptr);
+ for( sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ const sal_Int32 nHiddenStart = rRange.Min();
+ const sal_Int32 nHiddenEnd = rRange.Max() + 1;
+
+ if ( nHiddenStart > nPos )
+ break;
+ if (nPos < nHiddenEnd)
+ {
+ rnStartPos = nHiddenStart;
+ rnEndPos = std::min<sal_Int32>(nHiddenEnd,
+ rNode.GetText().getLength());
+ break;
+ }
+ }
+
+ if ( pList )
+ {
+ for( sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
+ {
+ const Range& rRange = aHiddenMulti.GetRange( i );
+ pList->push_back( rRange.Min() );
+ pList->push_back( rRange.Max() + 1 );
+ }
+ }
+
+ bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0;
+ }
+
+ return bNewContainsHiddenChars;
+}
+
+bool SwScriptInfo::GetBoundsOfHiddenRange(TextFrameIndex nPos,
+ TextFrameIndex & rnStartPos, TextFrameIndex & rnEndPos) const
+{
+ rnStartPos = TextFrameIndex(COMPLETE_STRING);
+ rnEndPos = TextFrameIndex(0);
+
+ const size_t nEnd = CountHiddenChg();
+ for( size_t nX = 0; nX < nEnd; ++nX )
+ {
+ const TextFrameIndex nHiddenStart = GetHiddenChg( nX++ );
+ const TextFrameIndex nHiddenEnd = GetHiddenChg( nX );
+
+ if ( nHiddenStart > nPos )
+ break;
+ if (nPos < nHiddenEnd)
+ {
+ rnStartPos = nHiddenStart;
+ rnEndPos = nHiddenEnd;
+ break;
+ }
+ }
+
+ return CountHiddenChg() > 0;
+}
+
+bool SwScriptInfo::IsInHiddenRange( const SwTextNode& rNode, sal_Int32 nPos )
+{
+ sal_Int32 nStartPos;
+ sal_Int32 nEndPos;
+ SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos );
+ return nStartPos != COMPLETE_STRING;
+}
+
+#ifdef DBG_UTIL
+// returns the type of the compressed character
+SwScriptInfo::CompType SwScriptInfo::DbgCompType(const TextFrameIndex nPos) const
+{
+ const size_t nEnd = CountCompChg();
+ for( size_t nX = 0; nX < nEnd; ++nX )
+ {
+ const TextFrameIndex nChg = GetCompStart(nX);
+
+ if ( nPos < nChg )
+ return NONE;
+
+ if( nPos < nChg + GetCompLen( nX ) )
+ return GetCompType( nX );
+ }
+ return NONE;
+}
+#endif
+
+// returns, if there are compressible kanas or specials
+// between nStart and nEnd
+size_t SwScriptInfo::HasKana(TextFrameIndex const nStart, TextFrameIndex const nLen) const
+{
+ const size_t nCnt = CountCompChg();
+ TextFrameIndex nEnd = nStart + nLen;
+
+ for( size_t nX = 0; nX < nCnt; ++nX )
+ {
+ TextFrameIndex nKanaStart = GetCompStart(nX);
+ TextFrameIndex nKanaEnd = nKanaStart + GetCompLen(nX);
+
+ if ( nKanaStart >= nEnd )
+ return SAL_MAX_SIZE;
+
+ if ( nStart < nKanaEnd )
+ return nX;
+ }
+
+ return SAL_MAX_SIZE;
+}
+
+tools::Long SwScriptInfo::Compress(sal_Int32* pKernArray, TextFrameIndex nIdx, TextFrameIndex nLen,
+ const sal_uInt16 nCompress, const sal_uInt16 nFontHeight,
+ bool bCenter,
+ Point* pPoint ) const
+{
+ SAL_WARN_IF( !nCompress, "sw.core", "Compression without compression?!" );
+ SAL_WARN_IF( !nLen, "sw.core", "Compression without text?!" );
+ const size_t nCompCount = CountCompChg();
+
+ // In asian typography, there are full width and half width characters.
+ // Full width punctuation characters can be compressed by 50%
+ // to determine this, we compare the font width with 75% of its height
+ const tools::Long nMinWidth = ( 3 * nFontHeight ) / 4;
+
+ size_t nCompIdx = HasKana( nIdx, nLen );
+
+ if ( SAL_MAX_SIZE == nCompIdx )
+ return 0;
+
+ TextFrameIndex nChg = GetCompStart( nCompIdx );
+ TextFrameIndex nCompLen = GetCompLen( nCompIdx );
+ sal_Int32 nI = 0;
+ nLen += nIdx;
+
+ if( nChg > nIdx )
+ {
+ nI = sal_Int32(nChg - nIdx);
+ nIdx = nChg;
+ }
+ else if( nIdx < nChg + nCompLen )
+ nCompLen -= nIdx - nChg;
+
+ if( nIdx > nLen || nCompIdx >= nCompCount )
+ return 0;
+
+ tools::Long nSub = 0;
+ tools::Long nLast = nI ? pKernArray[ nI - 1 ] : 0;
+ do
+ {
+ const CompType nType = GetCompType( nCompIdx );
+#ifdef DBG_UTIL
+ SAL_WARN_IF( nType != DbgCompType( nIdx ), "sw.core", "Gimme the right type!" );
+#endif
+ nCompLen += nIdx;
+ if( nCompLen > nLen )
+ nCompLen = nLen;
+
+ // are we allowed to compress the character?
+ if ( pKernArray[ nI ] - nLast < nMinWidth )
+ {
+ nIdx++; nI++;
+ }
+ else
+ {
+ while( nIdx < nCompLen )
+ {
+ SAL_WARN_IF( SwScriptInfo::NONE == nType, "sw.core", "None compression?!" );
+
+ // nLast is width of current character
+ nLast -= pKernArray[ nI ];
+
+ nLast *= nCompress;
+ tools::Long nMove = 0;
+ if( SwScriptInfo::KANA != nType )
+ {
+ nLast /= 24000;
+ if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType )
+ {
+ if( nI )
+ nMove = nLast;
+ else
+ {
+ pPoint->AdjustX(nLast );
+ nLast = 0;
+ }
+ }
+ else if( bCenter && SwScriptInfo::SPECIAL_MIDDLE == nType )
+ nMove = nLast / 2;
+ }
+ else
+ nLast /= 100000;
+ nSub -= nLast;
+ nLast = pKernArray[ nI ];
+ if( nI && nMove )
+ pKernArray[ nI - 1 ] += nMove;
+ pKernArray[ nI++ ] -= nSub;
+ ++nIdx;
+ }
+ }
+
+ if( nIdx >= nLen )
+ break;
+
+ TextFrameIndex nTmpChg = nLen;
+ if( ++nCompIdx < nCompCount )
+ {
+ nTmpChg = GetCompStart( nCompIdx );
+ if( nTmpChg > nLen )
+ nTmpChg = nLen;
+ nCompLen = GetCompLen( nCompIdx );
+ }
+
+ while( nIdx < nTmpChg )
+ {
+ nLast = pKernArray[ nI ];
+ pKernArray[ nI++ ] -= nSub;
+ ++nIdx;
+ }
+ } while( nIdx < nLen );
+ return nSub;
+}
+
+// Note on calling KashidaJustify():
+// Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
+// total number of kashida positions, or the number of kashida positions after some positions
+// have been dropped, depending on the state of the m_KashidaInvalid set.
+
+sal_Int32 SwScriptInfo::KashidaJustify( sal_Int32* pKernArray,
+ TextFrameIndex const nStt,
+ TextFrameIndex const nLen,
+ tools::Long nSpaceAdd ) const
+{
+ SAL_WARN_IF( !nLen, "sw.core", "Kashida justification without text?!" );
+
+ if( !IsKashidaLine(nStt))
+ return -1;
+
+ // evaluate kashida information in collected in SwScriptInfo
+
+ size_t nCntKash = 0;
+ while( nCntKash < CountKashida() )
+ {
+ if ( nStt <= GetKashida( nCntKash ) )
+ break;
+ ++nCntKash;
+ }
+
+ const TextFrameIndex nEnd = nStt + nLen;
+
+ size_t nCntKashEnd = nCntKash;
+ while ( nCntKashEnd < CountKashida() )
+ {
+ if ( nEnd <= GetKashida( nCntKashEnd ) )
+ break;
+ ++nCntKashEnd;
+ }
+
+ size_t nActualKashCount = nCntKashEnd - nCntKash;
+ for (size_t i = nCntKash; i < nCntKashEnd; ++i)
+ {
+ if ( nActualKashCount && !IsKashidaValid ( i ) )
+ --nActualKashCount;
+ }
+
+ if ( !pKernArray )
+ return nActualKashCount;
+
+ // do nothing if there is no more kashida
+ if ( nCntKash < CountKashida() )
+ {
+ // skip any invalid kashidas
+ while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
+ ++nCntKash;
+
+ TextFrameIndex nIdx = nCntKash < nCntKashEnd && IsKashidaValid(nCntKash)
+ ? GetKashida(nCntKash)
+ : nEnd;
+ tools::Long nKashAdd = nSpaceAdd;
+
+ while ( nIdx < nEnd )
+ {
+ TextFrameIndex nArrayPos = nIdx - nStt;
+
+ // next kashida position
+ ++nCntKash;
+ while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
+ ++nCntKash;
+
+ nIdx = nCntKash < nCntKashEnd && IsKashidaValid(nCntKash) ? GetKashida(nCntKash) : nEnd;
+ if ( nIdx > nEnd )
+ nIdx = nEnd;
+
+ const TextFrameIndex nArrayEnd = nIdx - nStt;
+
+ while ( nArrayPos < nArrayEnd )
+ {
+ pKernArray[ sal_Int32(nArrayPos) ] += nKashAdd;
+ ++nArrayPos;
+ }
+ nKashAdd += nSpaceAdd;
+ }
+ }
+
+ return 0;
+}
+
+// Checks if the current text is 'Arabic' text. Note that only the first
+// character has to be checked because a ctl portion only contains one
+// script, see NewTextPortion
+bool SwScriptInfo::IsArabicText(const OUString& rText,
+ TextFrameIndex const nStt, TextFrameIndex const nLen)
+{
+ using namespace ::com::sun::star::i18n;
+ static const ScriptTypeList typeList[] = {
+ { UnicodeScript_kArabic, UnicodeScript_kArabic, sal_Int16(UnicodeScript_kArabic) }, // 11,
+ { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, sal_Int16(UnicodeScript_kScriptCount) } // 88
+ };
+
+ // go forward if current position does not hold a regular character:
+ const CharClass& rCC = GetAppCharClass();
+ sal_Int32 nIdx = sal_Int32(nStt);
+ const sal_Int32 nEnd = sal_Int32(nStt + nLen);
+ while ( nIdx < nEnd && !rCC.isLetterNumeric( rText, nIdx ) )
+ {
+ ++nIdx;
+ }
+
+ if( nIdx == nEnd )
+ {
+ // no regular character found in this portion. Go backward:
+ --nIdx;
+ while ( nIdx >= 0 && !rCC.isLetterNumeric( rText, nIdx ) )
+ {
+ --nIdx;
+ }
+ }
+
+ if( nIdx >= 0 )
+ {
+ const sal_Unicode cCh = rText[nIdx];
+ const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, sal_Int16(UnicodeScript_kScriptCount) );
+ return type == sal_Int16(UnicodeScript_kArabic);
+ }
+ return false;
+}
+
+bool SwScriptInfo::IsKashidaValid(size_t const nKashPos) const
+{
+ return m_KashidaInvalid.find(nKashPos) == m_KashidaInvalid.end();
+}
+
+void SwScriptInfo::ClearKashidaInvalid(size_t const nKashPos)
+{
+ m_KashidaInvalid.erase(nKashPos);
+}
+
+// bMark == true:
+// marks the first valid kashida in the given text range as invalid
+// bMark == false:
+// clears all kashida invalid flags in the given text range
+bool SwScriptInfo::MarkOrClearKashidaInvalid(
+ TextFrameIndex const nStt, TextFrameIndex const nLen,
+ bool bMark, sal_Int32 nMarkCount)
+{
+ size_t nCntKash = 0;
+ while( nCntKash < CountKashida() )
+ {
+ if ( nStt <= GetKashida( nCntKash ) )
+ break;
+ nCntKash++;
+ }
+
+ const TextFrameIndex nEnd = nStt + nLen;
+
+ while ( nCntKash < CountKashida() )
+ {
+ if ( nEnd <= GetKashida( nCntKash ) )
+ break;
+ if(bMark)
+ {
+ if ( MarkKashidaInvalid ( nCntKash ) )
+ {
+ --nMarkCount;
+ if (!nMarkCount)
+ return true;
+ }
+ }
+ else
+ {
+ ClearKashidaInvalid ( nCntKash );
+ }
+ nCntKash++;
+ }
+ return false;
+}
+
+bool SwScriptInfo::MarkKashidaInvalid(size_t const nKashPos)
+{
+ return m_KashidaInvalid.insert(nKashPos).second;
+}
+
+// retrieve the kashida positions in the given text range
+void SwScriptInfo::GetKashidaPositions(
+ TextFrameIndex const nStt, TextFrameIndex const nLen,
+ std::vector<TextFrameIndex>& rKashidaPosition)
+{
+ size_t nCntKash = 0;
+ while( nCntKash < CountKashida() )
+ {
+ if ( nStt <= GetKashida( nCntKash ) )
+ break;
+ nCntKash++;
+ }
+
+ const TextFrameIndex nEnd = nStt + nLen;
+
+ size_t nCntKashEnd = nCntKash;
+ while ( nCntKashEnd < CountKashida() )
+ {
+ if ( nEnd <= GetKashida( nCntKashEnd ) )
+ break;
+ rKashidaPosition.push_back(GetKashida(nCntKashEnd));
+ nCntKashEnd++;
+ }
+}
+
+void SwScriptInfo::SetNoKashidaLine(TextFrameIndex const nStt, TextFrameIndex const nLen)
+{
+ m_NoKashidaLine.push_back( nStt );
+ m_NoKashidaLineEnd.push_back( nStt + nLen );
+}
+
+// determines if the line uses kashida justification
+bool SwScriptInfo::IsKashidaLine(TextFrameIndex const nCharIdx) const
+{
+ for (size_t i = 0; i < m_NoKashidaLine.size(); ++i)
+ {
+ if (nCharIdx >= m_NoKashidaLine[i] && nCharIdx < m_NoKashidaLineEnd[i])
+ return false;
+ }
+ return true;
+}
+
+void SwScriptInfo::ClearNoKashidaLine(TextFrameIndex const nStt, TextFrameIndex const nLen)
+{
+ size_t i = 0;
+ while (i < m_NoKashidaLine.size())
+ {
+ if (nStt + nLen >= m_NoKashidaLine[i] && nStt < m_NoKashidaLineEnd[i])
+ {
+ m_NoKashidaLine.erase(m_NoKashidaLine.begin() + i);
+ m_NoKashidaLineEnd.erase(m_NoKashidaLineEnd.begin() + i);
+ }
+ else
+ ++i;
+ }
+}
+
+// mark the given character indices as invalid kashida positions
+void SwScriptInfo::MarkKashidasInvalid(sal_Int32 const nCnt,
+ const TextFrameIndex* pKashidaPositions)
+{
+ SAL_WARN_IF( !pKashidaPositions || nCnt == 0, "sw.core", "Where are kashidas?" );
+
+ size_t nCntKash = 0;
+ sal_Int32 nKashidaPosIdx = 0;
+
+ while (nCntKash < CountKashida() && nKashidaPosIdx < nCnt)
+ {
+ if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) )
+ {
+ ++nCntKash;
+ continue;
+ }
+
+ if ( pKashidaPositions [nKashidaPosIdx] != GetKashida( nCntKash ) || !IsKashidaValid ( nCntKash ) )
+ return; // something is wrong
+
+ MarkKashidaInvalid ( nCntKash );
+ nKashidaPosIdx++;
+ }
+}
+
+TextFrameIndex SwScriptInfo::ThaiJustify( const OUString& rText, sal_Int32* pKernArray,
+ TextFrameIndex const nStt,
+ TextFrameIndex const nLen,
+ TextFrameIndex nNumberOfBlanks,
+ tools::Long nSpaceAdd )
+{
+ SAL_WARN_IF( nStt + nLen > TextFrameIndex(rText.getLength()), "sw.core", "String in ThaiJustify too small" );
+
+ SwTwips nNumOfTwipsToDistribute = nSpaceAdd * sal_Int32(nNumberOfBlanks) /
+ SPACING_PRECISION_FACTOR;
+
+ tools::Long nSpaceSum = 0;
+ TextFrameIndex nCnt(0);
+
+ for (sal_Int32 nI = 0; nI < sal_Int32(nLen); ++nI)
+ {
+ const sal_Unicode cCh = rText[sal_Int32(nStt) + nI];
+
+ // check if character is not above or below base
+ if ( ( 0xE34 > cCh || cCh > 0xE3A ) &&
+ ( 0xE47 > cCh || cCh > 0xE4E ) && cCh != 0xE31 )
+ {
+ if (nNumberOfBlanks > TextFrameIndex(0))
+ {
+ nSpaceAdd = nNumOfTwipsToDistribute / sal_Int32(nNumberOfBlanks);
+ --nNumberOfBlanks;
+ nNumOfTwipsToDistribute -= nSpaceAdd;
+ }
+ nSpaceSum += nSpaceAdd;
+ ++nCnt;
+ }
+
+ if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
+ }
+
+ return nCnt;
+}
+
+SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTextNode& rTNd,
+ SwTextFrame const**const o_ppFrame,
+ bool const bAllowInvalid)
+{
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rTNd);
+ SwScriptInfo* pScriptInfo = nullptr;
+
+ for( SwTextFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ pScriptInfo = const_cast<SwScriptInfo*>(pLast->GetScriptInfo());
+ if ( pScriptInfo )
+ {
+ if (bAllowInvalid ||
+ TextFrameIndex(COMPLETE_STRING) == pScriptInfo->GetInvalidityA())
+ {
+ if (o_ppFrame)
+ {
+ *o_ppFrame = pLast;
+ }
+ break;
+ }
+ pScriptInfo = nullptr;
+ }
+ }
+
+ return pScriptInfo;
+}
+
+SwParaPortion::SwParaPortion()
+{
+ FormatReset();
+ m_bFlys = m_bFootnoteNum = m_bMargin = false;
+ SetWhichPor( PortionType::Para );
+}
+
+SwParaPortion::~SwParaPortion()
+{
+}
+
+TextFrameIndex SwParaPortion::GetParLen() const
+{
+ TextFrameIndex nLen(0);
+ const SwLineLayout *pLay = this;
+ while( pLay )
+ {
+ nLen += pLay->GetLen();
+ pLay = pLay->GetNext();
+ }
+ return nLen;
+}
+
+const SwDropPortion *SwParaPortion::FindDropPortion() const
+{
+ const SwLineLayout *pLay = this;
+ while( pLay && pLay->IsDummy() )
+ pLay = pLay->GetNext();
+ while( pLay )
+ {
+ const SwLinePortion *pPos = pLay->GetNextPortion();
+ while ( pPos && !pPos->GetLen() )
+ pPos = pPos->GetNextPortion();
+ if( pPos && pPos->IsDropPortion() )
+ return static_cast<const SwDropPortion *>(pPos);
+ pLay = pLay->GetLen() ? nullptr : pLay->GetNext();
+ }
+ return nullptr;
+}
+
+void SwParaPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwParaPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwLineLayout::Init( SwLinePortion* pNextPortion )
+{
+ Height( 0, false );
+ Width( 0 );
+ SetLen(TextFrameIndex(0));
+ SetAscent( 0 );
+ SetRealHeight( 0 );
+ SetNextPortion( pNextPortion );
+}
+
+// looks for hanging punctuation portions in the paragraph
+// and return the maximum right offset of them.
+// If no such portion is found, the Margin/Hanging-flags will be updated.
+SwTwips SwLineLayout::GetHangingMargin_() const
+{
+ SwLinePortion* pPor = GetNextPortion();
+ bool bFound = false;
+ SwTwips nDiff = 0;
+ while( pPor)
+ {
+ if( pPor->IsHangingPortion() )
+ {
+ nDiff = static_cast<SwHangingPortion*>(pPor)->GetInnerWidth() - pPor->Width();
+ if( nDiff )
+ bFound = true;
+ }
+ // the last post its portion
+ else if ( pPor->IsPostItsPortion() && ! pPor->GetNextPortion() )
+ nDiff = mnAscent;
+
+ pPor = pPor->GetNextPortion();
+ }
+ if( !bFound ) // update the hanging-flag
+ const_cast<SwLineLayout*>(this)->SetHanging( false );
+ return nDiff;
+}
+
+SwTwips SwTextFrame::HangingMargin() const
+{
+ SAL_WARN_IF( !HasPara(), "sw.core", "Don't call me without a paraportion" );
+ if( !GetPara()->IsMargin() )
+ return 0;
+ const SwLineLayout* pLine = GetPara();
+ SwTwips nRet = 0;
+ do
+ {
+ SwTwips nDiff = pLine->GetHangingMargin();
+ if( nDiff > nRet )
+ nRet = nDiff;
+ pLine = pLine->GetNext();
+ } while ( pLine );
+ if( !nRet ) // update the margin-flag
+ const_cast<SwParaPortion*>(GetPara())->SetMargin( false );
+ return nRet;
+}
+
+void SwScriptInfo::selectHiddenTextProperty(const SwTextNode& rNode,
+ MultiSelection & rHiddenMulti,
+ std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> *const pBookmarks)
+{
+ assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
+ || (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
+
+ const SvxCharHiddenItem* pItem = rNode.GetSwAttrSet().GetItemIfSet( RES_CHRATR_HIDDEN );
+ if( pItem && pItem->GetValue() )
+ {
+ rHiddenMulti.SelectAll();
+ }
+
+ const SwpHints* pHints = rNode.GetpSwpHints();
+
+ if( pHints )
+ {
+ for( size_t nTmp = 0; nTmp < pHints->Count(); ++nTmp )
+ {
+ const SwTextAttr* pTextAttr = pHints->Get( nTmp );
+ const SvxCharHiddenItem* pHiddenItem = CharFormat::GetItem( *pTextAttr, RES_CHRATR_HIDDEN );
+ if( pHiddenItem )
+ {
+ const sal_Int32 nSt = pTextAttr->GetStart();
+ const sal_Int32 nEnd = *pTextAttr->End();
+ if( nEnd > nSt )
+ {
+ Range aTmp( nSt, nEnd - 1 );
+ rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() );
+ }
+ }
+ }
+ }
+
+ for (const SwIndex* pIndex = rNode.GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
+ {
+ const sw::mark::IMark* pMark = pIndex->GetMark();
+ const sw::mark::IBookmark* pBookmark = dynamic_cast<const sw::mark::IBookmark*>(pMark);
+ if (pBookmarks && pBookmark)
+ {
+ if (!pBookmark->IsExpanded())
+ {
+ pBookmarks->emplace_back(pBookmark, MarkKind::Point);
+ }
+ else if (pIndex == &pBookmark->GetMarkStart().nContent)
+ {
+ pBookmarks->emplace_back(pBookmark, MarkKind::Start);
+ }
+ else
+ {
+ assert(pIndex == &pBookmark->GetMarkEnd().nContent);
+ pBookmarks->emplace_back(pBookmark, MarkKind::End);
+ }
+ }
+
+ // condition is evaluated in DocumentFieldsManager::UpdateExpFields()
+ if (pBookmark && pBookmark->IsHidden())
+ {
+ // intersect bookmark range with textnode range and add the intersection to rHiddenMulti
+
+ const sal_Int32 nSt = pBookmark->GetMarkStart().nContent.GetIndex();
+ const sal_Int32 nEnd = pBookmark->GetMarkEnd().nContent.GetIndex();
+
+ if( nEnd > nSt )
+ {
+ Range aTmp( nSt, nEnd - 1 );
+ rHiddenMulti.Select(aTmp, true);
+ }
+ }
+ }
+}
+
+void SwScriptInfo::selectRedLineDeleted(const SwTextNode& rNode, MultiSelection &rHiddenMulti, bool bSelect)
+{
+ assert((rNode.GetText().isEmpty() && rHiddenMulti.GetTotalRange().Len() == 1)
+ || (rNode.GetText().getLength() == rHiddenMulti.GetTotalRange().Len()));
+
+ const IDocumentRedlineAccess& rIDRA = rNode.getIDocumentRedlineAccess();
+ if ( !IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) )
+ return;
+
+ SwRedlineTable::size_type nAct = rIDRA.GetRedlinePos( rNode, RedlineType::Any );
+
+ for ( ; nAct < rIDRA.GetRedlineTable().size(); nAct++ )
+ {
+ const SwRangeRedline* pRed = rIDRA.GetRedlineTable()[ nAct ];
+
+ if (pRed->Start()->nNode > rNode.GetIndex())
+ break;
+
+ if (pRed->GetType() != RedlineType::Delete)
+ continue;
+
+ sal_Int32 nRedlStart;
+ sal_Int32 nRedlnEnd;
+ pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd );
+ //clip it if the redline extends past the end of the nodes text
+ nRedlnEnd = std::min<sal_Int32>(nRedlnEnd, rNode.GetText().getLength());
+ if ( nRedlnEnd > nRedlStart )
+ {
+ Range aTmp( nRedlStart, nRedlnEnd - 1 );
+ rHiddenMulti.Select( aTmp, bSelect );
+ }
+ }
+}
+
+// Returns a MultiSection indicating the hidden ranges.
+void SwScriptInfo::CalcHiddenRanges( const SwTextNode& rNode,
+ MultiSelection & rHiddenMulti,
+ std::vector<std::pair<sw::mark::IBookmark const*, MarkKind>> *const pBookmarks)
+{
+ selectHiddenTextProperty(rNode, rHiddenMulti, pBookmarks);
+
+ // If there are any hidden ranges in the current text node, we have
+ // to unhide the redlining ranges:
+ selectRedLineDeleted(rNode, rHiddenMulti, false);
+
+ // We calculated a lot of stuff. Finally we can update the flags at the text node.
+
+ const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0;
+ bool bNewHiddenCharsHidePara = false;
+ if ( bNewContainsHiddenChars )
+ {
+ const Range& rRange = rHiddenMulti.GetRange( 0 );
+ const sal_Int32 nHiddenStart = rRange.Min();
+ const sal_Int32 nHiddenEnd = rRange.Max() + 1;
+ bNewHiddenCharsHidePara =
+ (nHiddenStart == 0 && nHiddenEnd >= rNode.GetText().getLength());
+ }
+ rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
+}
+
+TextFrameIndex SwScriptInfo::CountCJKCharacters(const OUString &rText,
+ TextFrameIndex nPos, TextFrameIndex const nEnd, LanguageType aLang)
+{
+ TextFrameIndex nCount(0);
+ if (nEnd > nPos)
+ {
+ sal_Int32 nDone = 0;
+ const lang::Locale &rLocale = g_pBreakIt->GetLocale( aLang );
+ while ( nPos < nEnd )
+ {
+ nPos = TextFrameIndex(g_pBreakIt->GetBreakIter()->nextCharacters(
+ rText, sal_Int32(nPos),
+ rLocale,
+ i18n::CharacterIteratorMode::SKIPCELL, 1, nDone));
+ nCount++;
+ }
+ }
+ else
+ nCount = nEnd - nPos ;
+
+ return nCount;
+}
+
+void SwScriptInfo::CJKJustify( const OUString& rText, sal_Int32* pKernArray,
+ TextFrameIndex const nStt,
+ TextFrameIndex const nLen, LanguageType aLang,
+ tools::Long nSpaceAdd, bool bIsSpaceStop )
+{
+ assert( pKernArray != nullptr && sal_Int32(nStt) >= 0 );
+ if (sal_Int32(nLen) <= 0)
+ return;
+
+ tools::Long nSpaceSum = 0;
+ const lang::Locale &rLocale = g_pBreakIt->GetLocale( aLang );
+ sal_Int32 nDone = 0;
+ sal_Int32 nNext(nStt);
+ for ( sal_Int32 nI = 0; nI < sal_Int32(nLen); ++nI )
+ {
+ if (nI + sal_Int32(nStt) == nNext)
+ {
+ nNext = g_pBreakIt->GetBreakIter()->nextCharacters( rText, nNext,
+ rLocale,
+ i18n::CharacterIteratorMode::SKIPCELL, 1, nDone );
+ if (nNext < sal_Int32(nStt + nLen) || !bIsSpaceStop)
+ nSpaceSum += nSpaceAdd;
+ }
+ pKernArray[ nI ] += nSpaceSum;
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porlay.hxx b/sw/source/core/text/porlay.hxx
new file mode 100644
index 000000000..3960671fc
--- /dev/null
+++ b/sw/source/core/text/porlay.hxx
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <scriptinfo.hxx>
+
+#include <swrect.hxx>
+#include <swtypes.hxx>
+#include "portxt.hxx"
+#include <svx/ctredlin.hxx>
+
+#include <vector>
+#include <deque>
+
+class SwMarginPortion;
+class SwDropPortion;
+class SwTextFormatter;
+
+class SwCharRange
+{
+private:
+ TextFrameIndex m_nStart;
+ TextFrameIndex m_nLen;
+
+public:
+ SwCharRange(TextFrameIndex const nInitStart = TextFrameIndex(0),
+ TextFrameIndex const nInitLen = TextFrameIndex(0))
+ : m_nStart( nInitStart ), m_nLen(nInitLen) {}
+ TextFrameIndex & Start() { return m_nStart; }
+ TextFrameIndex const& Start() const { return m_nStart; }
+ void LeftMove(TextFrameIndex const nNew)
+ { if ( nNew < m_nStart ) { m_nLen += m_nStart-nNew; m_nStart = nNew; } }
+ TextFrameIndex & Len() { return m_nLen; }
+ TextFrameIndex const& Len() const { return m_nLen; }
+ bool operator<(const SwCharRange &rRange) const
+ { return m_nStart < rRange.m_nStart; }
+ bool operator>(const SwCharRange &rRange) const
+ { return m_nStart + m_nLen > rRange.m_nStart + rRange.m_nLen; }
+ bool operator!=(const SwCharRange &rRange) const
+ { return *this < rRange || *this > rRange; }
+ SwCharRange &operator+=(const SwCharRange &rRange);
+};
+
+// SwRepaint is a document-global SwRect
+// nOfst states from where in the first line should be painted
+// nRightOfst gives the right margin
+class SwRepaint : public SwRect
+{
+ SwTwips m_nOffset;
+ SwTwips m_nRightOffset;
+public:
+ SwRepaint() : SwRect(), m_nOffset( 0 ), m_nRightOffset( 0 ) {}
+
+ SwTwips GetOffset() const { return m_nOffset; }
+ void SetOffset( const SwTwips nNew ) { m_nOffset = nNew; }
+ SwTwips GetRightOfst() const { return m_nRightOffset; }
+ void SetRightOfst( const SwTwips nNew ) { m_nRightOffset = nNew; }
+};
+
+/// Collection of SwLinePortion instances, representing one line of text.
+/// Typically owned by an SwParaPortion.
+class SW_DLLPUBLIC SwLineLayout : public SwTextPortion
+{
+private:
+ SwLineLayout *m_pNext; // The next Line
+ std::unique_ptr<std::vector<tools::Long>> m_pLLSpaceAdd; // Used for justified alignment
+ std::unique_ptr<std::deque<sal_uInt16>> m_pKanaComp; // Used for Kana compression
+ SwTwips m_nRealHeight; // The height resulting from line spacing and register
+ SwTwips m_nTextHeight; // The max height of all non-FlyCnt portions in this Line
+ bool m_bFormatAdj : 1;
+ bool m_bDummy : 1;
+ bool m_bEndHyph : 1;
+ bool m_bMidHyph : 1;
+ bool m_bFly : 1;
+ bool m_bRest : 1;
+ bool m_bBlinking : 1;
+ bool m_bClipping : 1; // Clipping needed for exact line height
+ bool m_bContent : 1; // Text for line numbering
+ bool m_bRedline : 1; // The Redlining
+ bool m_bRedlineEnd: 1; // Redlining for paragraph mark: tracked change at the end
+ bool m_bForcedLeftMargin : 1; // Left adjustment moved by the Fly
+ bool m_bHanging : 1; // Contains a hanging portion in the margin
+ bool m_bUnderscore : 1;
+
+ enum RedlineType m_eRedlineEnd; // redline type of pilcrow and line break symbols
+
+ OUString m_sRedlineText; // shortened text of (first) tracked deletion shown in margin
+
+ SwTwips GetHangingMargin_() const;
+
+ void DeleteNext();
+public:
+ // From SwPosSize
+ using SwPosSize::Height;
+ virtual void Height(const SwTwips nNew, const bool bText = true) override;
+
+ // From SwLinePortion
+ virtual SwLinePortion *Insert( SwLinePortion *pPortion ) override;
+ virtual SwLinePortion *Append( SwLinePortion *pPortion ) override;
+ SwLinePortion *GetFirstPortion() const;
+
+ // Flags
+ void ResetFlags();
+ void SetFormatAdj( const bool bNew ) { m_bFormatAdj = bNew; }
+ bool IsFormatAdj() const { return m_bFormatAdj; }
+ void SetEndHyph( const bool bNew ) { m_bEndHyph = bNew; }
+ bool IsEndHyph() const { return m_bEndHyph; }
+ void SetMidHyph( const bool bNew ) { m_bMidHyph = bNew; }
+ bool IsMidHyph() const { return m_bMidHyph; }
+ void SetFly( const bool bNew ) { m_bFly = bNew; }
+ bool IsFly() const { return m_bFly; }
+ void SetRest( const bool bNew ) { m_bRest = bNew; }
+ bool IsRest() const { return m_bRest; }
+ void SetBlinking( const bool bNew ) { m_bBlinking = bNew; }
+ bool IsBlinking() const { return m_bBlinking; }
+ void SetContent( const bool bNew ) { m_bContent = bNew; }
+ bool HasContent() const { return m_bContent; }
+ void SetRedline( const bool bNew ) { m_bRedline = bNew; }
+ bool HasRedline() const { return m_bRedline; }
+ void SetRedlineEnd( const bool bNew ) { m_bRedlineEnd = bNew; }
+ bool HasRedlineEnd() const { return m_bRedlineEnd; }
+ void SetRedlineEndType( const enum RedlineType eNew ) { m_eRedlineEnd = eNew; }
+ RedlineType GetRedlineEndType() const { return m_eRedlineEnd; }
+ void SetRedlineText ( const OUString& sText ) { m_sRedlineText = sText; }
+ const OUString* GetRedlineText() const { return &m_sRedlineText; }
+ void SetForcedLeftMargin() { m_bForcedLeftMargin = true; }
+ bool HasForcedLeftMargin() const { return m_bForcedLeftMargin; }
+ void SetHanging( const bool bNew ) { m_bHanging = bNew; }
+ bool IsHanging() const { return m_bHanging; }
+ void SetUnderscore( const bool bNew ) { m_bUnderscore = bNew; }
+ bool HasUnderscore() const { return m_bUnderscore; }
+
+ // Respecting empty dummy lines
+ void SetDummy( const bool bNew ) { m_bDummy = bNew; }
+ bool IsDummy() const { return m_bDummy; }
+
+ void SetClipping( const bool bNew ) { m_bClipping = bNew; }
+ bool IsClipping() const { return m_bClipping; }
+
+ SwLineLayout();
+ virtual ~SwLineLayout() override;
+
+ SwLineLayout *GetNext() { return m_pNext; }
+ const SwLineLayout *GetNext() const { return m_pNext; }
+ void SetNext( SwLineLayout *pNew ) { m_pNext = pNew; }
+
+ void Init( SwLinePortion *pNextPortion = nullptr);
+
+ // Collects the data for the line
+ void CalcLine( SwTextFormatter &rLine, SwTextFormatInfo &rInf );
+
+ void SetRealHeight( SwTwips nNew ) { m_nRealHeight = nNew; }
+ SwTwips GetRealHeight() const { return m_nRealHeight; }
+
+ SwTwips GetTextHeight() const { return m_nTextHeight; }
+
+ // Creates the glue chain for short lines
+ SwMarginPortion *CalcLeftMargin();
+
+ SwTwips GetHangingMargin() const
+ { return GetHangingMargin_(); }
+
+ // For special treatment for empty lines
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+
+ // Stuff for justified alignment
+ bool IsSpaceAdd() const { return m_pLLSpaceAdd != nullptr; }
+ void InitSpaceAdd(); // Creates pLLSpaceAdd if necessary
+ void CreateSpaceAdd( const tools::Long nInit = 0 );
+ void FinishSpaceAdd() { m_pLLSpaceAdd.reset(); }
+ sal_uInt16 GetLLSpaceAddCount() const { return sal::static_int_cast< sal_uInt16 >(m_pLLSpaceAdd->size()); }
+ void SetLLSpaceAdd( tools::Long nNew, sal_uInt16 nIdx )
+ {
+ if ( nIdx == GetLLSpaceAddCount() )
+ m_pLLSpaceAdd->push_back( nNew );
+ else
+ (*m_pLLSpaceAdd)[ nIdx ] = nNew;
+ }
+ tools::Long GetLLSpaceAdd( sal_uInt16 nIdx ) { return (*m_pLLSpaceAdd)[ nIdx ]; }
+ void RemoveFirstLLSpaceAdd() { m_pLLSpaceAdd->erase( m_pLLSpaceAdd->begin() ); }
+ std::vector<tools::Long>* GetpLLSpaceAdd() const { return m_pLLSpaceAdd.get(); }
+
+ // Stuff for Kana compression
+ void SetKanaComp( std::unique_ptr<std::deque<sal_uInt16>> pNew ){ m_pKanaComp = std::move(pNew); }
+ void FinishKanaComp() { m_pKanaComp.reset(); }
+ std::deque<sal_uInt16>* GetpKanaComp() const { return m_pKanaComp.get(); }
+ std::deque<sal_uInt16>& GetKanaComp() { return *m_pKanaComp; }
+
+ /** determine ascent and descent for positioning of as-character anchored
+ object
+
+ OD 07.01.2004 #i11859# - previously local method <lcl_MaxAscDescent>
+ Method calculates maximum ascents and descents of the line layout.
+ One value considering as-character anchored objects, one without these
+ objects.
+ Portions for other anchored objects aren't considered.
+ OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
+ to control, if the fly content portions and line portion are considered.
+
+ @param _orAscent
+ output parameter - maximum ascent without as-character anchored objects
+
+ @param _orDescent
+ output parameter - maximum descent without as-character anchored objects
+
+ @param _orObjAscent
+ output parameter - maximum ascent with as-character anchored objects
+
+ @param _orObjDescent
+ output parameter - maximum descent with as-character anchored objects
+
+ @param _pDontConsiderPortion
+ input parameter - portion, which isn't considered for calculating
+ <_orObjAscent> and <_orObjDescent>, if it isn't a portion for a
+ as-character anchored object or it isn't as high as the line.
+
+ @param _bNoFlyCntPorAndLinePor
+ optional input parameter - boolean, indicating that fly content portions
+ and the line portion are considered or not.
+ */
+ void MaxAscentDescent( SwTwips& _orAscent,
+ SwTwips& _orDescent,
+ SwTwips& _orObjAscent,
+ SwTwips& _orObjDescent,
+ const SwLinePortion* _pDontConsiderPortion = nullptr,
+ const bool _bNoFlyCntPorAndLinePor = false ) const;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+/// Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
+/// Typically owned by an SwTextFrame.
+class SwParaPortion : public SwLineLayout
+{
+ // Area that needs repainting
+ SwRepaint m_aRepaint;
+ // Area that needs reformatting
+ SwCharRange m_aReformat;
+ SwScriptInfo m_aScriptInfo;
+
+ // Fraction aZoom;
+ tools::Long m_nDelta;
+
+ // If a SwTextFrame is locked, no changes occur to the formatting data (under
+ // pLine) (compare with Orphans)
+ bool m_bFlys : 1; // Overlapping Flys?
+ bool m_bPrep : 1; // PREP_*
+ bool m_bPrepWidows : 1; // PrepareHint::Widows
+ bool m_bPrepAdjust : 1; // PrepareHint::AdjustSizeWithoutFormatting
+ bool m_bPrepMustFit : 1; // PrepareHint::MustFit
+ bool m_bFollowField : 1; // We have a bit of field left for the Follow
+
+ bool m_bFixLineHeight : 1; // Fixed line height
+ bool m_bFootnoteNum : 1; // contains a footnotenumberportion
+ bool m_bMargin : 1; // contains a hanging punctuation in the margin
+
+public:
+ SwParaPortion();
+ virtual ~SwParaPortion() override;
+
+ // Resets all formatting information (except for bFlys)
+ inline void FormatReset();
+
+ // Resets the Flags
+ inline void ResetPreps();
+
+ // Get/Set methods
+ SwRepaint& GetRepaint() { return m_aRepaint; }
+ const SwRepaint& GetRepaint() const { return m_aRepaint; }
+ SwCharRange& GetReformat() { return m_aReformat; }
+ const SwCharRange& GetReformat() const { return m_aReformat; }
+ void SetDelta(tools::Long nDelta) { m_nDelta = nDelta; }
+ tools::Long GetDelta() const { return m_nDelta; }
+ SwScriptInfo& GetScriptInfo() { return m_aScriptInfo; }
+ const SwScriptInfo& GetScriptInfo() const { return m_aScriptInfo; }
+
+ // For SwTextFrame::Format: returns the paragraph's current length
+ TextFrameIndex GetParLen() const;
+
+ // For Prepare()
+ bool UpdateQuoVadis( std::u16string_view rQuo );
+
+ // Flags
+ void SetFly() { m_bFlys = true; }
+ bool HasFly() const { return m_bFlys; }
+
+ // Preps
+ void SetPrep() { m_bPrep = true; }
+ bool IsPrep() const { return m_bPrep; }
+ void SetPrepWidows() { m_bPrepWidows = true; }
+ bool IsPrepWidows() const { return m_bPrepWidows; }
+ void SetPrepMustFit( const bool bNew ) { m_bPrepMustFit = bNew; }
+ bool IsPrepMustFit() const { return m_bPrepMustFit; }
+ void SetPrepAdjust() { m_bPrepAdjust = true; }
+ bool IsPrepAdjust() const { return m_bPrepAdjust; }
+ void SetFollowField( const bool bNew ) { m_bFollowField = bNew; }
+ bool IsFollowField() const { return m_bFollowField; }
+ void SetFixLineHeight() { m_bFixLineHeight = true; }
+ bool IsFixLineHeight() const { return m_bFixLineHeight; }
+
+ void SetFootnoteNum( const bool bNew ) { m_bFootnoteNum = bNew; }
+ bool IsFootnoteNum() const { return m_bFootnoteNum; }
+ void SetMargin( const bool bNew = true ) { m_bMargin = bNew; }
+ bool IsMargin() const { return m_bMargin; }
+
+ // Set nErgo in the QuoVadisPortion
+ void SetErgoSumNum( const OUString &rErgo );
+
+ const SwDropPortion *FindDropPortion() const;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+inline void SwParaPortion::ResetPreps()
+{
+ m_bPrep = m_bPrepWidows = m_bPrepAdjust = m_bPrepMustFit = false;
+}
+
+inline void SwParaPortion::FormatReset()
+{
+ m_nDelta = 0;
+ m_aReformat = SwCharRange(TextFrameIndex(0), TextFrameIndex(COMPLETE_STRING));
+ // bFlys needs to be retained in SwTextFrame::Format_() so that empty
+ // paragraphs that needed to avoid Frames with no flow, reformat
+ // when the Frame disappears from the Area
+ // bFlys = false;
+ ResetPreps();
+ m_bFollowField = m_bFixLineHeight = m_bMargin = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porlin.cxx b/sw/source/core/text/porlin.cxx
new file mode 100644
index 000000000..d2e95e882
--- /dev/null
+++ b/sw/source/core/text/porlin.cxx
@@ -0,0 +1,353 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/outdev.hxx>
+#include <SwPortionHandler.hxx>
+
+#include "porlin.hxx"
+#include "inftxt.hxx"
+#include "pormulti.hxx"
+#if OSL_DEBUG_LEVEL > 0
+
+static bool ChkChain( SwLinePortion *pStart )
+{
+ SwLinePortion *pPor = pStart->GetNextPortion();
+ sal_uInt16 nCount = 0;
+ while( pPor )
+ {
+ ++nCount;
+ OSL_ENSURE( nCount < 200 && pPor != pStart,
+ "ChkChain(): lost in chains" );
+ if( nCount >= 200 || pPor == pStart )
+ {
+ // the lifesaver
+ pPor = pStart->GetNextPortion();
+ pStart->SetNextPortion(nullptr);
+ pPor->Truncate();
+ pStart->SetNextPortion( pPor );
+ return false;
+ }
+ pPor = pPor->GetNextPortion();
+ }
+ return true;
+}
+#endif
+
+SwLinePortion::~SwLinePortion()
+{
+}
+
+SwLinePortion *SwLinePortion::Compress()
+{
+ return GetLen() || Width() ? this : nullptr;
+}
+
+sal_uInt16 SwLinePortion::GetViewWidth( const SwTextSizeInfo & ) const
+{
+ return 0;
+}
+
+SwLinePortion::SwLinePortion( ) :
+ mpNextPortion( nullptr ),
+ mnLineLength( 0 ),
+ mnAscent( 0 ),
+ mnHangingBaseline( 0 ),
+ mnWhichPor( PortionType::NONE ),
+ m_bJoinBorderWithPrev(false),
+ m_bJoinBorderWithNext(false)
+{
+}
+
+void SwLinePortion::PrePaint( const SwTextPaintInfo& rInf,
+ const SwLinePortion* pLast ) const
+{
+ OSL_ENSURE( rInf.OnWin(), "SwLinePortion::PrePaint: don't prepaint on a printer");
+ OSL_ENSURE( !Width(), "SwLinePortion::PrePaint: For Width()==0 only!");
+
+ const sal_uInt16 nViewWidth = GetViewWidth( rInf );
+
+ if( ! nViewWidth )
+ return;
+
+ const sal_uInt16 nHalfView = nViewWidth / 2;
+ sal_uInt16 nLastWidth = pLast->Width();
+
+ if ( pLast->InSpaceGrp() && rInf.GetSpaceAdd() )
+ nLastWidth = nLastWidth + o3tl::narrowing<sal_uInt16>(pLast->CalcSpacing( rInf.GetSpaceAdd(), rInf ));
+
+ sal_uInt16 nPos;
+ SwTextPaintInfo aInf( rInf );
+
+ const bool bBidiPor = rInf.GetTextFrame()->IsRightToLeft() !=
+ bool( vcl::text::ComplexTextLayoutFlags::BiDiRtl & rInf.GetOut()->GetLayoutMode() );
+
+ Degree10 nDir = bBidiPor ?
+ 1800_deg10 :
+ rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() );
+
+ // pLast == this *only* for the 1st portion in the line so nLastWidth is 0;
+ // allow this too, will paint outside the frame but might look better...
+ if (nLastWidth > nHalfView || pLast == this)
+ {
+ switch (nDir.get())
+ {
+ case 0:
+ nPos = sal_uInt16( rInf.X() );
+ nPos += nLastWidth - nHalfView;
+ aInf.X( nPos );
+ break;
+ case 900:
+ nPos = sal_uInt16( rInf.Y() );
+ nPos -= nLastWidth - nHalfView;
+ aInf.Y( nPos );
+ break;
+ case 1800:
+ nPos = sal_uInt16( rInf.X() );
+ nPos -= nLastWidth - nHalfView;
+ aInf.X( nPos );
+ break;
+ case 2700:
+ nPos = sal_uInt16( rInf.Y() );
+ nPos += nLastWidth - nHalfView;
+ aInf.Y( nPos );
+ break;
+ }
+ }
+
+ SwLinePortion *pThis = const_cast<SwLinePortion*>(this);
+ pThis->Width( nViewWidth );
+ Paint( aInf );
+ pThis->Width(0);
+}
+
+void SwLinePortion::CalcTextSize( const SwTextSizeInfo &rInf )
+{
+ if( GetLen() == rInf.GetLen() )
+ *static_cast<SwPosSize*>(this) = GetTextSize( rInf );
+ else
+ {
+ SwTextSizeInfo aInf( rInf );
+ aInf.SetLen( GetLen() );
+ *static_cast<SwPosSize*>(this) = GetTextSize( aInf );
+ }
+}
+
+// all following portions will be deleted
+void SwLinePortion::Truncate_()
+{
+ SwLinePortion *pPos = mpNextPortion;
+ do
+ {
+ OSL_ENSURE( pPos != this, "SwLinePortion::Truncate: loop" );
+ SwLinePortion *pLast = pPos;
+ pPos = pPos->GetNextPortion();
+ pLast->SetNextPortion( nullptr );
+ delete pLast;
+
+ } while( pPos );
+
+ mpNextPortion = nullptr;
+}
+
+// It always will be inserted after us.
+SwLinePortion *SwLinePortion::Insert( SwLinePortion *pIns )
+{
+ pIns->FindLastPortion()->SetNextPortion( mpNextPortion );
+ SetNextPortion( pIns );
+#if OSL_DEBUG_LEVEL > 0
+ ChkChain( this );
+#endif
+ return pIns;
+}
+
+SwLinePortion *SwLinePortion::FindLastPortion()
+{
+ SwLinePortion *pPos = this;
+ // Find the end and link pLinPortion to the last one...
+ while( pPos->GetNextPortion() )
+ {
+ pPos = pPos->GetNextPortion();
+ }
+ return pPos;
+}
+
+SwLinePortion *SwLinePortion::Append( SwLinePortion *pIns )
+{
+ SwLinePortion *pPos = FindLastPortion();
+ pPos->SetNextPortion( pIns );
+ pIns->SetNextPortion( nullptr );
+#if OSL_DEBUG_LEVEL > 0
+ ChkChain( this );
+#endif
+ return pIns;
+}
+
+SwLinePortion *SwLinePortion::Cut( SwLinePortion *pVictim )
+{
+ SwLinePortion *pPrev = pVictim->FindPrevPortion( this );
+ OSL_ENSURE( pPrev, "SwLinePortion::Cut(): can't cut" );
+ pPrev->SetNextPortion( pVictim->GetNextPortion() );
+ pVictim->SetNextPortion(nullptr);
+ return pVictim;
+}
+
+SwLinePortion *SwLinePortion::FindPrevPortion( const SwLinePortion *pRoot )
+{
+ OSL_ENSURE( pRoot != this, "SwLinePortion::FindPrevPortion(): invalid root" );
+ SwLinePortion *pPos = const_cast<SwLinePortion*>(pRoot);
+ while( pPos->GetNextPortion() && pPos->GetNextPortion() != this )
+ {
+ pPos = pPos->GetNextPortion();
+ }
+ OSL_ENSURE( pPos->GetNextPortion(),
+ "SwLinePortion::FindPrevPortion: blowing in the wind");
+ return pPos;
+}
+
+TextFrameIndex SwLinePortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+{
+ if( nOfst > ( PrtWidth() / 2 ) )
+ return GetLen();
+ else
+ return TextFrameIndex(0);
+}
+
+SwPosSize SwLinePortion::GetTextSize( const SwTextSizeInfo & ) const
+{
+ OSL_ENSURE( false, "SwLinePortion::GetTextSize: don't ask me about sizes, "
+ "I'm only a stupid SwLinePortion" );
+ return SwPosSize();
+}
+
+bool SwLinePortion::Format( SwTextFormatInfo &rInf )
+{
+ if( rInf.X() > rInf.Width() )
+ {
+ Truncate();
+ rInf.SetUnderflow( this );
+ return true;
+ }
+
+ const SwLinePortion *pLast = rInf.GetLast();
+ Height( pLast->Height() );
+ SetAscent( pLast->GetAscent() );
+ const sal_uInt16 nNewWidth = o3tl::narrowing<sal_uInt16>(rInf.X() + PrtWidth());
+ // Only portions with true width can return true
+ // Notes for example never set bFull==true
+ if( rInf.Width() <= nNewWidth && PrtWidth() && ! IsKernPortion() )
+ {
+ Truncate();
+ if( nNewWidth > rInf.Width() )
+ PrtWidth( nNewWidth - rInf.Width() );
+ rInf.GetLast()->FormatEOL( rInf );
+ return true;
+ }
+ return false;
+}
+
+// Format end of line
+
+void SwLinePortion::FormatEOL( SwTextFormatInfo & )
+{ }
+
+void SwLinePortion::Move( SwTextPaintInfo &rInf )
+{
+ bool bB2T = rInf.GetDirection() == DIR_BOTTOM2TOP;
+ const bool bFrameDir = rInf.GetTextFrame()->IsRightToLeft();
+ bool bCounterDir = ( ! bFrameDir && DIR_RIGHT2LEFT == rInf.GetDirection() ) ||
+ ( bFrameDir && DIR_LEFT2RIGHT == rInf.GetDirection() );
+
+ if ( InSpaceGrp() && rInf.GetSpaceAdd() )
+ {
+ SwTwips nTmp = PrtWidth() + CalcSpacing( rInf.GetSpaceAdd(), rInf );
+ if( rInf.IsRotated() )
+ rInf.Y( rInf.Y() + ( bB2T ? -nTmp : nTmp ) );
+ else if ( bCounterDir )
+ rInf.X( rInf.X() - nTmp );
+ else
+ rInf.X( rInf.X() + nTmp );
+ }
+ else
+ {
+ if( InFixMargGrp() && !IsMarginPortion() )
+ {
+ rInf.IncSpaceIdx();
+ rInf.IncKanaIdx();
+ }
+ if( rInf.IsRotated() )
+ rInf.Y( rInf.Y() + ( bB2T ? -PrtWidth() : PrtWidth() ) );
+ else if ( bCounterDir )
+ rInf.X( rInf.X() - PrtWidth() );
+ else
+ rInf.X( rInf.X() + PrtWidth() );
+ }
+ if( IsMultiPortion() && static_cast<SwMultiPortion*>(this)->HasTabulator() )
+ rInf.IncSpaceIdx();
+
+ rInf.SetIdx( rInf.GetIdx() + GetLen() );
+}
+
+tools::Long SwLinePortion::CalcSpacing( tools::Long , const SwTextSizeInfo & ) const
+{
+ return 0;
+}
+
+bool SwLinePortion::GetExpText( const SwTextSizeInfo &, OUString & ) const
+{
+ return false;
+}
+
+void SwLinePortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), OUString(), GetWhichPor(), Height(), Width() );
+}
+
+void SwLinePortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwLinePortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwLinePortion::dumpAsXmlAttributes(xmlTextWriterPtr pWriter, std::u16string_view rText, TextFrameIndex nOffset) const
+{
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("width"),
+ BAD_CAST(OString::number(Width()).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("height"),
+ BAD_CAST(OString::number(Height()).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("length"),
+ BAD_CAST(OString::number(static_cast<sal_Int32>(mnLineLength)).getStr()));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("type"),
+ BAD_CAST(sw::PortionTypeToString(GetWhichPor())));
+ OUString aText( rText.substr(sal_Int32(nOffset), sal_Int32(GetLen())) );
+ for (int i = 0; i < 32; ++i)
+ aText = aText.replace(i, '*');
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("portion"),
+ BAD_CAST(aText.toUtf8().getStr()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porlin.hxx b/sw/source/core/text/porlin.hxx
new file mode 100644
index 000000000..3cd1d9fff
--- /dev/null
+++ b/sw/source/core/text/porlin.hxx
@@ -0,0 +1,215 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <libxml/xmlwriter.h>
+
+#include "possiz.hxx"
+#include <txttypes.hxx>
+#include <TextFrameIndex.hxx>
+#include <rtl/ustring.hxx>
+
+class SwTextSizeInfo;
+class SwTextPaintInfo;
+class SwTextFormatInfo;
+class SwPortionHandler;
+
+/// Portion groups
+/// @see enum PortionType in txttypes.hxx
+#define PORGRP_TXT 0x8000
+#define PORGRP_EXP 0x4000
+#define PORGRP_FLD 0x2000
+#define PORGRP_HYPH 0x1000
+#define PORGRP_NUMBER 0x0800
+#define PORGRP_GLUE 0x0400
+#define PORGRP_FIX 0x0200
+#define PORGRP_TAB 0x0100
+// Small special groups
+#define PORGRP_FIXMARG 0x0040
+//#define PORGRP_? 0x0020
+#define PORGRP_TABNOTLFT 0x0010
+#define PORGRP_TOXREF 0x0008
+
+/// Base class for anything that can be part of a line in the Writer layout.
+/// Typically owned by SwLineLayout.
+class SAL_DLLPUBLIC_RTTI SwLinePortion: public SwPosSize
+{
+protected:
+ // Here we have areas with different attributes
+ SwLinePortion *mpNextPortion;
+ // Count of chars and spaces on the line
+ TextFrameIndex mnLineLength;
+ SwTwips mnAscent; // Maximum ascender
+ SwTwips mnHangingBaseline;
+
+ SwLinePortion();
+private:
+ PortionType mnWhichPor; // Who's who?
+ bool m_bJoinBorderWithPrev;
+ bool m_bJoinBorderWithNext;
+
+ void Truncate_();
+
+public:
+ explicit inline SwLinePortion(const SwLinePortion &rPortion);
+ virtual ~SwLinePortion();
+
+ // Access methods
+ SwLinePortion *GetNextPortion() const { return mpNextPortion; }
+ inline SwLinePortion &operator=(const SwLinePortion &rPortion);
+ TextFrameIndex GetLen() const { return mnLineLength; }
+ void SetLen(TextFrameIndex const nLen) { mnLineLength = nLen; }
+ void SetNextPortion( SwLinePortion *pNew ){ mpNextPortion = pNew; }
+ SwTwips &GetAscent() { return mnAscent; }
+ SwTwips GetAscent() const { return mnAscent; }
+ void SetAscent( const SwTwips nNewAsc ) { mnAscent = nNewAsc; }
+ void PrtWidth( SwTwips nNewWidth ) { Width( nNewWidth ); }
+ SwTwips PrtWidth() const { return Width(); }
+ void AddPrtWidth( const SwTwips nNew ) { Width( Width() + nNew ); }
+ void SubPrtWidth( const SwTwips nNew ) { Width( Width() - nNew ); }
+ SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
+ void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline = nNewBaseline; }
+
+ // Insert methods
+ virtual SwLinePortion *Insert( SwLinePortion *pPortion );
+ virtual SwLinePortion *Append( SwLinePortion *pPortion );
+ SwLinePortion *Cut( SwLinePortion *pVictim );
+ inline void Truncate();
+
+ // Returns 0, if there's no payload
+ virtual SwLinePortion *Compress();
+
+ void SetWhichPor( const PortionType nNew ) { mnWhichPor = nNew; }
+ PortionType GetWhichPor( ) const { return mnWhichPor; }
+
+// Group queries
+ bool InTextGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_TXT) != 0; }
+ bool InGlueGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_GLUE) != 0; }
+ bool InTabGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_TAB) != 0; }
+ bool InHyphGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_HYPH) != 0; }
+ bool InNumberGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_NUMBER) != 0; }
+ bool InFixGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_FIX) != 0; }
+ bool InFieldGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_FLD) != 0; }
+ bool InToxRefGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_TOXREF) != 0; }
+ bool InToxRefOrFieldGrp() const { return (sal_uInt16(mnWhichPor) & ( PORGRP_FLD | PORGRP_TOXREF )) != 0; }
+ bool InExpGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_EXP) != 0; }
+ bool InFixMargGrp() const { return (sal_uInt16(mnWhichPor) & PORGRP_FIXMARG) != 0; }
+ bool InSpaceGrp() const { return InTextGrp() || IsMultiPortion(); }
+// Individual queries
+ bool IsGrfNumPortion() const { return mnWhichPor == PortionType::GrfNum; }
+ bool IsFlyCntPortion() const { return mnWhichPor == PortionType::FlyCnt; }
+ bool IsBlankPortion() const { return mnWhichPor == PortionType::Blank; }
+ bool IsBreakPortion() const { return mnWhichPor == PortionType::Break; }
+ bool IsErgoSumPortion() const { return mnWhichPor == PortionType::ErgoSum; }
+ bool IsQuoVadisPortion() const { return mnWhichPor == PortionType::QuoVadis; }
+ bool IsTabLeftPortion() const { return mnWhichPor == PortionType::TabLeft; }
+ bool IsTabRightPortion() const { return mnWhichPor == PortionType::TabRight; }
+ bool IsTabCenterPortion() const { return mnWhichPor == PortionType::TabCenter; }
+ bool IsTabDecimalPortion() const { return mnWhichPor == PortionType::TabDecimal; }
+ bool IsFootnoteNumPortion() const { return mnWhichPor == PortionType::FootnoteNum; }
+ bool IsFootnotePortion() const { return mnWhichPor == PortionType::Footnote; }
+ bool IsDropPortion() const { return mnWhichPor == PortionType::Drop; }
+ bool IsLayPortion() const { return mnWhichPor == PortionType::Lay; }
+ bool IsParaPortion() const { return mnWhichPor == PortionType::Para; }
+ bool IsMarginPortion() const { return mnWhichPor == PortionType::Margin; }
+ bool IsFlyPortion() const { return mnWhichPor == PortionType::Fly; }
+ bool IsHolePortion() const { return mnWhichPor == PortionType::Hole; }
+ bool IsSoftHyphPortion() const { return mnWhichPor == PortionType::SoftHyphen; }
+ bool IsPostItsPortion() const { return mnWhichPor == PortionType::PostIts; }
+ bool IsCombinedPortion() const { return mnWhichPor == PortionType::Combined; }
+ bool IsTextPortion() const { return mnWhichPor == PortionType::Text; }
+ bool IsHangingPortion() const { return mnWhichPor == PortionType::Hanging; }
+ bool IsKernPortion() const { return mnWhichPor == PortionType::Kern; }
+ bool IsArrowPortion() const { return mnWhichPor == PortionType::Arrow; }
+ bool IsMultiPortion() const { return mnWhichPor == PortionType::Multi; }
+ bool IsNumberPortion() const { return mnWhichPor == PortionType::Number; } // #i23726#
+ bool IsControlCharPortion() const { return mnWhichPor == PortionType::ControlChar || mnWhichPor == PortionType::Bookmark; }
+
+ // Positioning
+ SwLinePortion *FindPrevPortion( const SwLinePortion *pRoot );
+ SwLinePortion *FindLastPortion();
+
+ /// the parameter is actually SwTwips apparently?
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const;
+ void CalcTextSize( const SwTextSizeInfo &rInfo );
+
+ // Output
+ virtual void Paint( const SwTextPaintInfo &rInf ) const = 0;
+ void PrePaint( const SwTextPaintInfo &rInf, const SwLinePortion *pLast ) const;
+
+ virtual bool Format( SwTextFormatInfo &rInf );
+ // Is called for the line's last portion
+ virtual void FormatEOL( SwTextFormatInfo &rInf );
+ void Move( SwTextPaintInfo &rInf );
+
+ // For SwTextSlot
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const;
+
+ // For SwFieldPortion, SwSoftHyphPortion
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const;
+
+ // for text- and multi-portions
+ virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const;
+
+ bool GetJoinBorderWithPrev() const { return m_bJoinBorderWithPrev; }
+ bool GetJoinBorderWithNext() const { return m_bJoinBorderWithNext; }
+ void SetJoinBorderWithPrev( const bool bJoinPrev ) { m_bJoinBorderWithPrev = bJoinPrev; }
+ void SetJoinBorderWithNext( const bool bJoinNext ) { m_bJoinBorderWithNext = bJoinNext; }
+
+ virtual void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& rOffset) const;
+ void dumpAsXmlAttributes(xmlTextWriterPtr writer, std::u16string_view rText,
+ TextFrameIndex nOffset) const;
+};
+
+inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion)
+{
+ *static_cast<SwPosSize*>(this) = rPortion;
+ mnLineLength = rPortion.mnLineLength;
+ mnAscent = rPortion.mnAscent;
+ mnHangingBaseline = rPortion.mnHangingBaseline;
+ mnWhichPor = rPortion.mnWhichPor;
+ m_bJoinBorderWithPrev = rPortion.m_bJoinBorderWithPrev;
+ m_bJoinBorderWithNext = rPortion.m_bJoinBorderWithNext;
+ return *this;
+}
+
+inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) :
+ SwPosSize( rPortion ),
+ mpNextPortion( nullptr ),
+ mnLineLength( rPortion.mnLineLength ),
+ mnAscent( rPortion.mnAscent ),
+ mnHangingBaseline( rPortion.mnHangingBaseline ),
+ mnWhichPor( rPortion.mnWhichPor ),
+ m_bJoinBorderWithPrev( rPortion.m_bJoinBorderWithPrev ),
+ m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext )
+{
+}
+
+inline void SwLinePortion::Truncate()
+{
+ if ( mpNextPortion )
+ Truncate_();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/pormulti.cxx b/sw/source/core/text/pormulti.cxx
new file mode 100644
index 000000000..518294e12
--- /dev/null
+++ b/sw/source/core/text/pormulti.cxx
@@ -0,0 +1,2593 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <deque>
+#include <memory>
+
+#include <hintids.hxx>
+
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <vcl/outdev.hxx>
+#include <txatbase.hxx>
+#include <fmtruby.hxx>
+#include <txtatr.hxx>
+#include <charfmt.hxx>
+#include <layfrm.hxx>
+#include <SwPortionHandler.hxx>
+#include "pormulti.hxx"
+#include "inftxt.hxx"
+#include "itrpaint.hxx"
+#include <viewopt.hxx>
+#include "itrform2.hxx"
+#include "porfld.hxx"
+#include "porglue.hxx"
+#include "porrst.hxx"
+#include <pagefrm.hxx>
+#include <rowfrm.hxx>
+#include <tgrditem.hxx>
+#include <swtable.hxx>
+#include <fmtfsize.hxx>
+#include <doc.hxx>
+
+using namespace ::com::sun::star;
+
+// A SwMultiPortion is not a simple portion,
+// it's a container, which contains almost a SwLineLayoutPortion.
+// This SwLineLayout could be followed by other textportions via pPortion
+// and by another SwLineLayout via pNext to realize a doubleline portion.
+SwMultiPortion::~SwMultiPortion()
+{
+}
+
+void SwMultiPortion::Paint( const SwTextPaintInfo & ) const
+{
+ OSL_FAIL( "Don't try SwMultiPortion::Paint, try SwTextPainter::PaintMultiPortion" );
+}
+
+// Summarize the internal lines to calculate the (external) size.
+// The internal line has to calculate first.
+void SwMultiPortion::CalcSize( SwTextFormatter& rLine, SwTextFormatInfo &rInf )
+{
+ Width( 0 );
+ Height( 0 );
+ SetAscent( 0 );
+ SetFlyInContent( false );
+ SwLineLayout *pLay = &GetRoot();
+ do
+ {
+ pLay->CalcLine( rLine, rInf );
+ if( rLine.IsFlyInCntBase() )
+ SetFlyInContent( true );
+ if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) )
+ {
+ // An empty phonetic line don't need an ascent or a height.
+ if( !pLay->Width() )
+ {
+ pLay->SetAscent( 0 );
+ pLay->Height( 0 );
+ }
+ if( OnTop() )
+ SetAscent( GetAscent() + pLay->Height() );
+ }
+ else
+ SetAscent( GetAscent() + pLay->GetAscent() );
+
+ // Increase the line height, except for ruby text on the right.
+ if ( !IsRuby() || !OnRight() || pLay == &GetRoot() )
+ Height( Height() + pLay->Height() );
+ else
+ {
+ // We already added the width after building the portion,
+ // so no need to add it twice.
+ break;
+ }
+
+ if( Width() < pLay->Width() )
+ Width( pLay->Width() );
+ pLay = pLay->GetNext();
+ } while ( pLay );
+ if( !HasBrackets() )
+ return;
+
+ sal_uInt16 nTmp = static_cast<SwDoubleLinePortion*>(this)->GetBrackets()->nHeight;
+ if( nTmp > Height() )
+ {
+ const sal_uInt16 nAdd = ( nTmp - Height() ) / 2;
+ GetRoot().SetAscent( GetRoot().GetAscent() + nAdd );
+ GetRoot().Height( GetRoot().Height() + nAdd );
+ Height( nTmp );
+ }
+ nTmp = static_cast<SwDoubleLinePortion*>(this)->GetBrackets()->nAscent;
+ if( nTmp > GetAscent() )
+ SetAscent( nTmp );
+}
+
+tools::Long SwMultiPortion::CalcSpacing( tools::Long , const SwTextSizeInfo & ) const
+{
+ return 0;
+}
+
+bool SwMultiPortion::ChgSpaceAdd( SwLineLayout*, tools::Long ) const
+{
+ return false;
+}
+
+void SwMultiPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Text( GetLen(), GetWhichPor() );
+}
+
+void SwMultiPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwMultiPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ // Intentionally not incrementing nOffset here, one of the child portions will do that.
+
+ const SwLineLayout* pLine = &GetRoot();
+ while (pLine)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwLineLayout"));
+ pLine->dumpAsXmlAttributes(pWriter, rText, nOffset);
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ while (pPor)
+ {
+ pPor->dumpAsXml(pWriter, rText, nOffset);
+ pPor = pPor->GetNextPortion();
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ pLine = pLine->GetNext();
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// sets the tabulator-flag, if there's any tabulator-portion inside.
+void SwMultiPortion::ActualizeTabulator()
+{
+ SwLinePortion* pPor = GetRoot().GetFirstPortion();
+ // First line
+ for( m_bTab1 = m_bTab2 = false; pPor; pPor = pPor->GetNextPortion() )
+ if( pPor->InTabGrp() )
+ SetTab1( true );
+ if( GetRoot().GetNext() )
+ {
+ // Second line
+ pPor = GetRoot().GetNext()->GetFirstPortion();
+ do
+ {
+ if( pPor->InTabGrp() )
+ SetTab2( true );
+ pPor = pPor->GetNextPortion();
+ } while ( pPor );
+ }
+}
+
+SwRotatedPortion::SwRotatedPortion( const SwMultiCreator& rCreate,
+ TextFrameIndex const nEnd, bool bRTL )
+ : SwMultiPortion( nEnd )
+{
+ const SvxCharRotateItem* pRot = static_cast<const SvxCharRotateItem*>(rCreate.pItem);
+ if( !pRot )
+ {
+ const SwTextAttr& rAttr = *rCreate.pAttr;
+ const SfxPoolItem *const pItem =
+ CharFormat::GetItem(rAttr, RES_CHRATR_ROTATE);
+ if ( pItem )
+ {
+ pRot = static_cast<const SvxCharRotateItem*>(pItem);
+ }
+ }
+ if( pRot )
+ {
+ sal_uInt8 nDir;
+ if ( bRTL )
+ nDir = pRot->IsBottomToTop() ? 3 : 1;
+ else
+ nDir = pRot->IsBottomToTop() ? 1 : 3;
+
+ SetDirection( nDir );
+ }
+}
+
+SwBidiPortion::SwBidiPortion(TextFrameIndex const nEnd, sal_uInt8 nLv)
+ : SwMultiPortion( nEnd ), m_nLevel( nLv )
+{
+ SetBidi();
+
+ if ( m_nLevel % 2 )
+ SetDirection( DIR_RIGHT2LEFT );
+ else
+ SetDirection( DIR_LEFT2RIGHT );
+}
+
+tools::Long SwBidiPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo& rInf ) const
+{
+ return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt(rInf)) * nSpaceAdd / SPACING_PRECISION_FACTOR;
+}
+
+bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const
+{
+ if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() )
+ {
+ pCurr->CreateSpaceAdd();
+ pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
+ return true;
+ }
+
+ return false;
+}
+
+TextFrameIndex SwBidiPortion::GetSpaceCnt(const SwTextSizeInfo &rInf) const
+{
+ // Calculate number of blanks for justified alignment
+ TextFrameIndex nTmpStart = rInf.GetIdx();
+ TextFrameIndex nNull(0);
+ TextFrameIndex nBlanks(0);
+
+ for (SwLinePortion* pPor = GetRoot().GetFirstPortion(); pPor; pPor = pPor->GetNextPortion())
+ {
+ if( pPor->InTextGrp() )
+ nBlanks = nBlanks + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
+ else if ( pPor->IsMultiPortion() &&
+ static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ nBlanks = nBlanks + static_cast<SwBidiPortion*>(pPor)->GetSpaceCnt( rInf );
+
+ const_cast<SwTextSizeInfo &>(rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ }
+ const_cast<SwTextSizeInfo &>(rInf).SetIdx( nTmpStart );
+ return nBlanks;
+}
+
+// This constructor is for the continuation of a doubleline portion
+// in the next line.
+// It takes the same brackets and if the original has no content except
+// brackets, these will be deleted.
+SwDoubleLinePortion::SwDoubleLinePortion(
+ SwDoubleLinePortion& rDouble, TextFrameIndex const nEnd)
+ : SwMultiPortion(nEnd)
+ , m_nLineDiff(0)
+ , m_nBlank1(0)
+ , m_nBlank2(0)
+{
+ SetDirection( rDouble.GetDirection() );
+ SetDouble();
+ if( rDouble.GetBrackets() )
+ {
+ SetBrackets( rDouble );
+ // An empty multiportion needs no brackets.
+ // Notice: GetLen() might be zero, if the multiportion contains
+ // the second part of a field and the width might be zero, if
+ // it contains a note only. In this cases the brackets are okay.
+ // But if the length and the width are both zero, the portion
+ // is really empty.
+ if( rDouble.Width() == rDouble.BracketWidth() )
+ rDouble.ClearBrackets();
+ }
+}
+
+// This constructor uses the textattribute to get the right brackets.
+// The textattribute could be a 2-line-attribute or a character- or
+// internet style, which contains the 2-line-attribute.
+SwDoubleLinePortion::SwDoubleLinePortion(
+ const SwMultiCreator& rCreate, TextFrameIndex const nEnd)
+ : SwMultiPortion(nEnd)
+ , m_pBracket(new SwBracket)
+ , m_nLineDiff(0)
+ , m_nBlank1(0)
+ , m_nBlank2(0)
+{
+ m_pBracket->nAscent = 0;
+ m_pBracket->nHeight = 0;
+ m_pBracket->nPreWidth = 0;
+ m_pBracket->nPostWidth = 0;
+
+ SetDouble();
+ const SvxTwoLinesItem* pTwo = static_cast<const SvxTwoLinesItem*>(rCreate.pItem);
+ if( pTwo )
+ m_pBracket->nStart = TextFrameIndex(0);
+ else
+ {
+ const SwTextAttr& rAttr = *rCreate.pAttr;
+ m_pBracket->nStart = rCreate.nStartOfAttr;
+
+ const SfxPoolItem * const pItem =
+ CharFormat::GetItem( rAttr, RES_CHRATR_TWO_LINES );
+ if ( pItem )
+ {
+ pTwo = static_cast<const SvxTwoLinesItem*>(pItem);
+ }
+ }
+ if( pTwo )
+ {
+ m_pBracket->cPre = pTwo->GetStartBracket();
+ m_pBracket->cPost = pTwo->GetEndBracket();
+ }
+ else
+ {
+ m_pBracket->cPre = 0;
+ m_pBracket->cPost = 0;
+ }
+ SwFontScript nTmp = SW_SCRIPTS;
+ if( m_pBracket->cPre > 255 )
+ {
+ OUString aText(m_pBracket->cPre);
+ nTmp = SwScriptInfo::WhichFont(0, aText);
+ }
+ m_pBracket->nPreScript = nTmp;
+ nTmp = SW_SCRIPTS;
+ if( m_pBracket->cPost > 255 )
+ {
+ OUString aText(m_pBracket->cPost);
+ nTmp = SwScriptInfo::WhichFont(0, aText);
+ }
+ m_pBracket->nPostScript = nTmp;
+
+ if( !m_pBracket->cPre && !m_pBracket->cPost )
+ {
+ m_pBracket.reset();
+ }
+
+ // double line portions have the same direction as the frame directions
+ if ( rCreate.nLevel % 2 )
+ SetDirection( DIR_RIGHT2LEFT );
+ else
+ SetDirection( DIR_LEFT2RIGHT );
+}
+
+// paints the wished bracket,
+// if the multiportion has surrounding brackets.
+// The X-position of the SwTextPaintInfo will be modified:
+// the open bracket sets position behind itself,
+// the close bracket in front of itself.
+void SwDoubleLinePortion::PaintBracket( SwTextPaintInfo &rInf,
+ tools::Long nSpaceAdd,
+ bool bOpen ) const
+{
+ sal_Unicode cCh = bOpen ? m_pBracket->cPre : m_pBracket->cPost;
+ if( !cCh )
+ return;
+ const sal_uInt16 nChWidth = bOpen ? PreWidth() : PostWidth();
+ if( !nChWidth )
+ return;
+ if( !bOpen )
+ rInf.X( rInf.X() + Width() - PostWidth() +
+ ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) );
+
+ SwBlankPortion aBlank( cCh, true );
+ aBlank.SetAscent( m_pBracket->nAscent );
+ aBlank.Width( nChWidth );
+ aBlank.Height( m_pBracket->nHeight );
+ {
+ SwFont aTmpFnt( *rInf.GetFont() );
+ SwFontScript nAct = bOpen ? m_pBracket->nPreScript : m_pBracket->nPostScript;
+ if( SW_SCRIPTS > nAct )
+ aTmpFnt.SetActual( nAct );
+ aTmpFnt.SetProportion( 100 );
+ SwFontSave aSave( rInf, &aTmpFnt );
+ aBlank.Paint( rInf );
+ }
+ if( bOpen )
+ rInf.X( rInf.X() + PreWidth() );
+}
+
+// creates the bracket-structure
+// and fills it, if not both characters are 0x00.
+void SwDoubleLinePortion::SetBrackets( const SwDoubleLinePortion& rDouble )
+{
+ if( rDouble.m_pBracket )
+ {
+ m_pBracket.reset( new SwBracket );
+ m_pBracket->cPre = rDouble.m_pBracket->cPre;
+ m_pBracket->cPost = rDouble.m_pBracket->cPost;
+ m_pBracket->nPreScript = rDouble.m_pBracket->nPreScript;
+ m_pBracket->nPostScript = rDouble.m_pBracket->nPostScript;
+ m_pBracket->nStart = rDouble.m_pBracket->nStart;
+ }
+}
+
+// calculates the size of the brackets => pBracket,
+// reduces the nMaxWidth-parameter ( minus bracket-width )
+// and moves the rInf-x-position behind the opening bracket.
+void SwDoubleLinePortion::FormatBrackets( SwTextFormatInfo &rInf, SwTwips& nMaxWidth )
+{
+ nMaxWidth -= rInf.X();
+ SwFont aTmpFnt( *rInf.GetFont() );
+ aTmpFnt.SetProportion( 100 );
+ m_pBracket->nAscent = 0;
+ m_pBracket->nHeight = 0;
+ if( m_pBracket->cPre )
+ {
+ OUString aStr( m_pBracket->cPre );
+ SwFontScript nActualScr = aTmpFnt.GetActual();
+ if( SW_SCRIPTS > m_pBracket->nPreScript )
+ aTmpFnt.SetActual( m_pBracket->nPreScript );
+ SwFontSave aSave( rInf, &aTmpFnt );
+ SwPosSize aSize = rInf.GetTextSize( aStr );
+ m_pBracket->nAscent = rInf.GetAscent();
+ m_pBracket->nHeight = aSize.Height();
+ aTmpFnt.SetActual( nActualScr );
+ if( nMaxWidth > aSize.Width() )
+ {
+ m_pBracket->nPreWidth = aSize.Width();
+ nMaxWidth -= aSize.Width();
+ rInf.X( rInf.X() + aSize.Width() );
+ }
+ else
+ {
+ m_pBracket->nPreWidth = 0;
+ nMaxWidth = 0;
+ }
+ }
+ else
+ m_pBracket->nPreWidth = 0;
+ if( m_pBracket->cPost )
+ {
+ OUString aStr( m_pBracket->cPost );
+ if( SW_SCRIPTS > m_pBracket->nPostScript )
+ aTmpFnt.SetActual( m_pBracket->nPostScript );
+ SwFontSave aSave( rInf, &aTmpFnt );
+ SwPosSize aSize = rInf.GetTextSize( aStr );
+ const sal_uInt16 nTmpAsc = rInf.GetAscent();
+ if( nTmpAsc > m_pBracket->nAscent )
+ {
+ m_pBracket->nHeight += nTmpAsc - m_pBracket->nAscent;
+ m_pBracket->nAscent = nTmpAsc;
+ }
+ if( aSize.Height() > m_pBracket->nHeight )
+ m_pBracket->nHeight = aSize.Height();
+ if( nMaxWidth > aSize.Width() )
+ {
+ m_pBracket->nPostWidth = aSize.Width();
+ nMaxWidth -= aSize.Width();
+ }
+ else
+ {
+ m_pBracket->nPostWidth = 0;
+ nMaxWidth = 0;
+ }
+ }
+ else
+ m_pBracket->nPostWidth = 0;
+ nMaxWidth += rInf.X();
+}
+
+// calculates the number of blanks in each line and
+// the difference of the width of the two lines.
+// These results are used from the text adjustment.
+void SwDoubleLinePortion::CalcBlanks( SwTextFormatInfo &rInf )
+{
+ SwLinePortion* pPor = GetRoot().GetFirstPortion();
+ TextFrameIndex nNull(0);
+ TextFrameIndex nStart = rInf.GetIdx();
+ SetTab1( false );
+ SetTab2( false );
+ for (m_nBlank1 = TextFrameIndex(0); pPor; pPor = pPor->GetNextPortion())
+ {
+ if( pPor->InTextGrp() )
+ m_nBlank1 = m_nBlank1 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
+ rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ if( pPor->InTabGrp() )
+ SetTab1( true );
+ }
+ m_nLineDiff = GetRoot().Width();
+ if( GetRoot().GetNext() )
+ {
+ pPor = GetRoot().GetNext()->GetFirstPortion();
+ m_nLineDiff -= GetRoot().GetNext()->Width();
+ }
+ for (m_nBlank2 = TextFrameIndex(0); pPor; pPor = pPor->GetNextPortion())
+ {
+ if( pPor->InTextGrp() )
+ m_nBlank2 = m_nBlank2 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
+ rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ if( pPor->InTabGrp() )
+ SetTab2( true );
+ }
+ rInf.SetIdx( nStart );
+}
+
+tools::Long SwDoubleLinePortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo & ) const
+{
+ return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt()) * nSpaceAdd / SPACING_PRECISION_FACTOR;
+}
+
+// Merges the spaces for text adjustment from the inner and outer part.
+// Inside the doubleline portion the wider line has no spaceadd-array, the
+// smaller line has such an array to reach width of the wider line.
+// If the surrounding line has text adjustment and the doubleline portion
+// contains no tabulator, it is necessary to create/manipulate the inner
+// space arrays.
+bool SwDoubleLinePortion::ChgSpaceAdd( SwLineLayout* pCurr,
+ tools::Long nSpaceAdd ) const
+{
+ bool bRet = false;
+ if( !HasTabulator() && nSpaceAdd > 0 )
+ {
+ if( !pCurr->IsSpaceAdd() )
+ {
+ // The wider line gets the spaceadd from the surrounding line direct
+ pCurr->CreateSpaceAdd();
+ pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
+ bRet = true;
+ }
+ else
+ {
+ sal_Int32 const nMyBlank = sal_Int32(GetSmallerSpaceCnt());
+ sal_Int32 const nOther = sal_Int32(GetSpaceCnt());
+ SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd;
+
+ if( nMyBlank )
+ nMultiSpace /= sal_Int32(nMyBlank);
+
+// pCurr->SetLLSpaceAdd( nMultiSpace, 0 );
+ // #i65711# SetLLSpaceAdd replaces the first value,
+ // instead we want to insert a new first value:
+ std::vector<tools::Long>* pVec = pCurr->GetpLLSpaceAdd();
+ pVec->insert( pVec->begin(), nMultiSpace );
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+// cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..)
+void SwDoubleLinePortion::ResetSpaceAdd( SwLineLayout* pCurr )
+{
+ pCurr->RemoveFirstLLSpaceAdd();
+ if( !pCurr->GetLLSpaceAddCount() )
+ pCurr->FinishSpaceAdd();
+}
+
+SwDoubleLinePortion::~SwDoubleLinePortion()
+{
+}
+
+// constructs a ruby portion, i.e. an additional text is displayed
+// beside the main text, e.g. phonetic characters.
+SwRubyPortion::SwRubyPortion(const SwRubyPortion& rRuby, TextFrameIndex const nEnd)
+ : SwMultiPortion( nEnd )
+ , m_nRubyOffset( rRuby.GetRubyOffset() )
+ , m_nAdjustment( rRuby.GetAdjustment() )
+{
+ SetDirection( rRuby.GetDirection() );
+ SetRubyPosition( rRuby.GetRubyPosition() );
+ SetRuby();
+}
+
+// constructs a ruby portion, i.e. an additional text is displayed
+// beside the main text, e.g. phonetic characters.
+SwRubyPortion::SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
+ const IDocumentSettingAccess& rIDocumentSettingAccess,
+ TextFrameIndex const nEnd, TextFrameIndex const nOffs,
+ const SwTextSizeInfo &rInf )
+ : SwMultiPortion( nEnd )
+{
+ SetRuby();
+ OSL_ENSURE( SwMultiCreatorId::Ruby == rCreate.nId, "Ruby expected" );
+ OSL_ENSURE( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" );
+ const SwFormatRuby& rRuby = rCreate.pAttr->GetRuby();
+ m_nAdjustment = rRuby.GetAdjustment();
+ m_nRubyOffset = nOffs;
+
+ const SwTextFrame *pFrame = rInf.GetTextFrame();
+ RubyPosition ePos = static_cast<RubyPosition>( rRuby.GetPosition() );
+
+ // RIGHT is designed for horizontal writing mode only.
+ if ( ePos == RubyPosition::RIGHT && pFrame->IsVertical() )
+ ePos = RubyPosition::ABOVE;
+
+ // In grid mode we force the ruby text to the upper or lower line
+ if ( rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid( GetGridItem(pFrame->FindPageFrame()) );
+ if ( pGrid )
+ ePos = pGrid->GetRubyTextBelow() ? RubyPosition::BELOW : RubyPosition::ABOVE;
+ }
+
+ SetRubyPosition( ePos );
+
+ const SwCharFormat *const pFormat =
+ static_txtattr_cast<SwTextRuby const*>(rCreate.pAttr)->GetCharFormat();
+ std::unique_ptr<SwFont> pRubyFont;
+ if( pFormat )
+ {
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ pRubyFont.reset(new SwFont( rFnt ));
+ pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess );
+
+ // we do not allow a vertical font for the ruby text
+ pRubyFont->SetVertical( rFnt.GetOrientation() , OnRight() );
+ }
+
+ OUString aStr = rRuby.GetText().copy( sal_Int32(nOffs) );
+ SwFieldPortion *pField = new SwFieldPortion( aStr, std::move(pRubyFont) );
+ pField->SetNextOffset( nOffs );
+ pField->SetFollow( true );
+
+ if( OnTop() )
+ GetRoot().SetNextPortion( pField );
+ else
+ {
+ GetRoot().SetNext( new SwLineLayout() );
+ GetRoot().GetNext()->SetNextPortion( pField );
+ }
+
+ // ruby portions have the same direction as the frame directions
+ if ( rCreate.nLevel % 2 )
+ {
+ // switch right and left ruby adjustment in rtl environment
+ if ( css::text::RubyAdjust_LEFT == m_nAdjustment )
+ m_nAdjustment = css::text::RubyAdjust_RIGHT;
+ else if ( css::text::RubyAdjust_RIGHT == m_nAdjustment )
+ m_nAdjustment = css::text::RubyAdjust_LEFT;
+
+ SetDirection( DIR_RIGHT2LEFT );
+ }
+ else
+ SetDirection( DIR_LEFT2RIGHT );
+}
+
+// In ruby portion there are different alignments for
+// the ruby text and the main text.
+// Left, right, centered and two possibilities of block adjustment
+// The block adjustment is realized by spacing between the characters,
+// either with a half space or no space in front of the first letter and
+// a half space at the end of the last letter.
+// Notice: the smaller line will be manipulated, normally it's the ruby line,
+// but it could be the main text, too.
+// If there is a tabulator in smaller line, no adjustment is possible.
+void SwRubyPortion::Adjust_( SwTextFormatInfo &rInf )
+{
+ SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width();
+ TextFrameIndex const nOldIdx = rInf.GetIdx();
+ if( !nLineDiff )
+ return;
+ SwLineLayout *pCurr;
+ if( nLineDiff < 0 )
+ { // The first line has to be adjusted.
+ if( GetTab1() )
+ return;
+ pCurr = &GetRoot();
+ nLineDiff = -nLineDiff;
+ }
+ else
+ { // The second line has to be adjusted.
+ if( GetTab2() )
+ return;
+ pCurr = GetRoot().GetNext();
+ rInf.SetIdx( nOldIdx + GetRoot().GetLen() );
+ }
+ sal_uInt16 nLeft = 0; // the space in front of the first letter
+ sal_uInt16 nRight = 0; // the space at the end of the last letter
+ TextFrameIndex nSub(0);
+ switch ( m_nAdjustment )
+ {
+ case css::text::RubyAdjust_CENTER: nRight = o3tl::narrowing<sal_uInt16>(nLineDiff / 2);
+ [[fallthrough]];
+ case css::text::RubyAdjust_RIGHT: nLeft = o3tl::narrowing<sal_uInt16>(nLineDiff - nRight); break;
+ case css::text::RubyAdjust_BLOCK: nSub = TextFrameIndex(1);
+ [[fallthrough]];
+ case css::text::RubyAdjust_INDENT_BLOCK:
+ {
+ TextFrameIndex nCharCnt(0);
+ SwLinePortion *pPor;
+ for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetNextPortion() )
+ {
+ if( pPor->InTextGrp() )
+ static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nCharCnt );
+ rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
+ }
+ if( nCharCnt > nSub )
+ {
+ SwTwips nCalc = nLineDiff / sal_Int32(nCharCnt - nSub);
+ short nTmp;
+ if( nCalc < SHRT_MAX )
+ nTmp = -short(nCalc);
+ else
+ nTmp = SHRT_MIN;
+
+ pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp );
+ nLineDiff -= nCalc * (sal_Int32(nCharCnt) - 1);
+ }
+ if( nLineDiff > 1 )
+ {
+ nRight = o3tl::narrowing<sal_uInt16>(nLineDiff / 2);
+ nLeft = o3tl::narrowing<sal_uInt16>(nLineDiff - nRight);
+ }
+ break;
+ }
+ default: OSL_FAIL( "New ruby adjustment" );
+ }
+ if( nLeft || nRight )
+ {
+ if( !pCurr->GetNextPortion() )
+ pCurr->SetNextPortion(SwTextPortion::CopyLinePortion(*pCurr));
+ if( nLeft )
+ {
+ SwMarginPortion *pMarg = new SwMarginPortion;
+ pMarg->AddPrtWidth( nLeft );
+ pMarg->SetNextPortion( pCurr->GetNextPortion() );
+ pCurr->SetNextPortion( pMarg );
+ }
+ if( nRight )
+ {
+ SwMarginPortion *pMarg = new SwMarginPortion;
+ pMarg->AddPrtWidth( nRight );
+ pCurr->FindLastPortion()->Append( pMarg );
+ }
+ }
+
+ pCurr->Width( Width() );
+ rInf.SetIdx( nOldIdx );
+}
+
+// has to change the nRubyOffset, if there's a fieldportion
+// in the phonetic line.
+// The nRubyOffset is the position in the rubystring, where the
+// next SwRubyPortion has start the displaying of the phonetics.
+void SwRubyPortion::CalcRubyOffset()
+{
+ const SwLineLayout *pCurr = &GetRoot();
+ if( !OnTop() )
+ {
+ pCurr = pCurr->GetNext();
+ if( !pCurr )
+ return;
+ }
+ const SwLinePortion *pPor = pCurr->GetFirstPortion();
+ const SwFieldPortion *pField = nullptr;
+ while( pPor )
+ {
+ if( pPor->InFieldGrp() )
+ pField = static_cast<const SwFieldPortion*>(pPor);
+ pPor = pPor->GetNextPortion();
+ }
+ if( pField )
+ {
+ if( pField->HasFollow() )
+ m_nRubyOffset = pField->GetNextOffset();
+ else
+ m_nRubyOffset = TextFrameIndex(COMPLETE_STRING);
+ }
+}
+
+// A little helper function for GetMultiCreator(..)
+// It extracts the 2-line-format from a 2-line-attribute or a character style.
+// The rValue is set to true, if the 2-line-attribute's value is set and
+// no 2-line-format reference is passed. If there is a 2-line-format reference,
+// then the rValue is set only, if the 2-line-attribute's value is set _and_
+// the 2-line-formats has the same brackets.
+static bool lcl_Check2Lines(const SfxPoolItem *const pItem,
+ const SvxTwoLinesItem* &rpRef, bool &rValue)
+{
+ if( pItem )
+ {
+ rValue = static_cast<const SvxTwoLinesItem*>(pItem)->GetValue();
+ if( !rpRef )
+ rpRef = static_cast<const SvxTwoLinesItem*>(pItem);
+ else if( static_cast<const SvxTwoLinesItem*>(pItem)->GetEndBracket() !=
+ rpRef->GetEndBracket() ||
+ static_cast<const SvxTwoLinesItem*>(pItem)->GetStartBracket() !=
+ rpRef->GetStartBracket() )
+ rValue = false;
+ return true;
+ }
+ return false;
+}
+
+static bool lcl_Has2Lines(const SwTextAttr& rAttr,
+ const SvxTwoLinesItem* &rpRef, bool &rValue)
+{
+ const SfxPoolItem* pItem = CharFormat::GetItem(rAttr, RES_CHRATR_TWO_LINES);
+ return lcl_Check2Lines(pItem, rpRef, rValue);
+}
+
+// is a little help function for GetMultiCreator(..)
+// It extracts the charrotation from a charrotate-attribute or a character style.
+// The rValue is set to true, if the charrotate-attribute's value is set and
+// no charrotate-format reference is passed.
+// If there is a charrotate-format reference, then the rValue is set only,
+// if the charrotate-attribute's value is set _and_ identical
+// to the charrotate-format's value.
+static bool lcl_CheckRotation(const SfxPoolItem *const pItem,
+ const SvxCharRotateItem* &rpRef, bool &rValue)
+{
+ if ( pItem )
+ {
+ rValue = static_cast<const SvxCharRotateItem*>(pItem)->GetValue() != 0_deg10;
+ if( !rpRef )
+ rpRef = static_cast<const SvxCharRotateItem*>(pItem);
+ else if( static_cast<const SvxCharRotateItem*>(pItem)->GetValue() !=
+ rpRef->GetValue() )
+ rValue = false;
+ return true;
+ }
+
+ return false;
+}
+
+static bool lcl_HasRotation(const SwTextAttr& rAttr,
+ const SvxCharRotateItem* &rpRef, bool &rValue)
+{
+ const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_ROTATE );
+ return lcl_CheckRotation(pItem, rpRef, rValue);
+}
+
+namespace sw {
+ namespace {
+
+ // need to use a very special attribute iterator here that returns
+ // both the hints and the nodes, so that GetMultiCreator() can handle
+ // items in the nodes' set properly
+ class MergedAttrIterMulti
+ : public MergedAttrIterBase
+ {
+ private:
+ bool m_First = true;
+ public:
+ MergedAttrIterMulti(SwTextFrame const& rFrame) : MergedAttrIterBase(rFrame) {}
+ SwTextAttr const* NextAttr(SwTextNode const*& rpNode);
+ // can't have operator= because m_pMerged/m_pNode const
+ void Assign(MergedAttrIterMulti const& rOther)
+ {
+ assert(m_pMerged == rOther.m_pMerged);
+ assert(m_pNode == rOther.m_pNode);
+ m_CurrentExtent = rOther.m_CurrentExtent;
+ m_CurrentHint = rOther.m_CurrentHint;
+ m_First = rOther.m_First;
+ }
+ };
+
+ }
+
+ SwTextAttr const* MergedAttrIterMulti::NextAttr(SwTextNode const*& rpNode)
+ {
+ if (m_First)
+ {
+ m_First = false;
+ rpNode = m_pMerged
+ ? !m_pMerged->extents.empty()
+ ? m_pMerged->extents[0].pNode
+ : m_pMerged->pFirstNode
+ : m_pNode;
+ return nullptr;
+ }
+ if (m_pMerged)
+ {
+ const auto nExtentsSize = m_pMerged->extents.size();
+ while (m_CurrentExtent < nExtentsSize)
+ {
+ sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent]);
+ if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
+ {
+ auto nHintsCount = pHints->Count();
+ while (m_CurrentHint < nHintsCount)
+ {
+ SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
+ if (rExtent.nEnd < pHint->GetStart())
+ {
+ break;
+ }
+ ++m_CurrentHint;
+ if (rExtent.nStart <= pHint->GetStart())
+ {
+ rpNode = rExtent.pNode;
+ return pHint;
+ }
+ }
+ }
+ ++m_CurrentExtent;
+ if (m_CurrentExtent < nExtentsSize &&
+ rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode)
+ {
+ m_CurrentHint = 0; // reset
+ rpNode = m_pMerged->extents[m_CurrentExtent].pNode;
+ return nullptr;
+ }
+ }
+ return nullptr;
+ }
+ else
+ {
+ SwpHints const*const pHints(m_pNode->GetpSwpHints());
+ if (pHints)
+ {
+ if (m_CurrentHint < pHints->Count())
+ {
+ SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
+ ++m_CurrentHint;
+ rpNode = m_pNode;
+ return pHint;
+ }
+ }
+ return nullptr;
+ }
+ }
+}
+
+// If we (e.g. the position rPos) are inside a two-line-attribute or
+// a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
+// otherwise the function returns zero.
+// The rPos parameter is set to the end of the multiportion,
+// normally this is the end of the attribute,
+// but sometimes it is the start of another attribute, which finished or
+// interrupts the first attribute.
+// E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
+// with different brackets interrupts another 2-line-attribute.
+std::optional<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
+ SwMultiPortion const * pMulti ) const
+{
+ SwScriptInfo& rSI = const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
+
+ // get the last embedding level
+ sal_uInt8 nCurrLevel;
+ if ( pMulti )
+ {
+ OSL_ENSURE( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" );
+ // level associated with bidi-portion;
+ nCurrLevel = static_cast<SwBidiPortion const *>(pMulti)->GetLevel();
+ }
+ else
+ // no nested bidi portion required
+ nCurrLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
+
+ // check if there is a field at rPos:
+ sal_uInt8 nNextLevel = nCurrLevel;
+ bool bFieldBidi = false;
+
+ if (rPos < TextFrameIndex(GetText().getLength()) && CH_TXTATR_BREAKWORD == GetChar(rPos))
+ {
+ bFieldBidi = true;
+ }
+ else
+ nNextLevel = rSI.DirType( rPos );
+
+ if (TextFrameIndex(GetText().getLength()) != rPos && nNextLevel > nCurrLevel)
+ {
+ rPos = bFieldBidi ? rPos + TextFrameIndex(1) : rSI.NextDirChg(rPos, &nCurrLevel);
+ if (TextFrameIndex(COMPLETE_STRING) == rPos)
+ return {};
+ SwMultiCreator aRet;
+ aRet.pItem = nullptr;
+ aRet.pAttr = nullptr;
+ aRet.nStartOfAttr = TextFrameIndex(-1);
+ aRet.nId = SwMultiCreatorId::Bidi;
+ aRet.nLevel = nCurrLevel + 1;
+ return aRet;
+ }
+
+ // a bidi portion can only contain other bidi portions
+ if ( pMulti )
+ return {};
+
+ // need the node that contains input rPos
+ std::pair<SwTextNode const*, sal_Int32> startPos(m_pFrame->MapViewToModel(rPos));
+ const SvxCharRotateItem* pActiveRotateItem(nullptr);
+ const SvxCharRotateItem* pNodeRotateItem(nullptr);
+ const SvxTwoLinesItem* pActiveTwoLinesItem(nullptr);
+ const SvxTwoLinesItem* pNodeTwoLinesItem(nullptr);
+ SwTextAttr const* pActiveTwoLinesHint(nullptr);
+ SwTextAttr const* pActiveRotateHint(nullptr);
+ const SwTextAttr *pRuby = nullptr;
+ sw::MergedAttrIterMulti iterAtStartOfNode(*m_pFrame);
+ bool bTwo = false;
+ bool bRot = false;
+
+ for (sw::MergedAttrIterMulti iter = *m_pFrame; ; )
+ {
+ SwTextNode const* pNode(nullptr);
+ SwTextAttr const*const pAttr = iter.NextAttr(pNode);
+ if (!pNode)
+ {
+ break;
+ }
+ if (pAttr)
+ {
+ assert(pNode->GetIndex() <= startPos.first->GetIndex()); // should break earlier
+ if (startPos.first->GetIndex() <= pNode->GetIndex())
+ {
+ if (startPos.first->GetIndex() != pNode->GetIndex()
+ || startPos.second < pAttr->GetStart())
+ {
+ break;
+ }
+ if (startPos.second < pAttr->GetAnyEnd())
+ {
+ // sw_redlinehide: ruby *always* splits
+ if (RES_TXTATR_CJK_RUBY == pAttr->Which())
+ pRuby = pAttr;
+ else
+ {
+ const SvxCharRotateItem* pRoTmp = nullptr;
+ if (lcl_HasRotation( *pAttr, pRoTmp, bRot ))
+ {
+ pActiveRotateHint = bRot ? pAttr : nullptr;
+ pActiveRotateItem = pRoTmp;
+ }
+ const SvxTwoLinesItem* p2Tmp = nullptr;
+ if (lcl_Has2Lines( *pAttr, p2Tmp, bTwo ))
+ {
+ pActiveTwoLinesHint = bTwo ? pAttr : nullptr;
+ pActiveTwoLinesItem = p2Tmp;
+ }
+ }
+ }
+ }
+ }
+ else if (pNode) // !pAttr && pNode means the node changed
+ {
+ if (startPos.first->GetIndex() < pNode->GetIndex())
+ {
+ break; // only one node initially
+ }
+ if (startPos.first->GetIndex() == pNode->GetIndex())
+ {
+ iterAtStartOfNode.Assign(iter);
+ if (SfxItemState::SET == pNode->GetSwAttrSet().GetItemState(
+ RES_CHRATR_ROTATE, true, &pNodeRotateItem) &&
+ pNodeRotateItem->GetValue())
+ {
+ pActiveRotateItem = pNodeRotateItem;
+ }
+ else
+ {
+ pNodeRotateItem = nullptr;
+ }
+ if (SfxItemState::SET == startPos.first->GetSwAttrSet().GetItemState(
+ RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem) &&
+ pNodeTwoLinesItem->GetValue())
+ {
+ pActiveTwoLinesItem = pNodeTwoLinesItem;
+ }
+ else
+ {
+ pNodeTwoLinesItem = nullptr;
+ }
+ }
+ }
+ }
+ if (!pRuby && !pActiveTwoLinesItem && !pActiveRotateItem)
+ return {};
+
+ if( pRuby )
+ { // The winner is ... a ruby attribute and so
+ // the end of the multiportion is the end of the ruby attribute.
+ rPos = m_pFrame->MapModelToView(startPos.first, *pRuby->End());
+ SwMultiCreator aRet;
+ aRet.pItem = nullptr;
+ aRet.pAttr = pRuby;
+ aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
+ aRet.nId = SwMultiCreatorId::Ruby;
+ aRet.nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
+ return aRet;
+ }
+ if (pActiveTwoLinesHint ||
+ (pNodeTwoLinesItem && pNodeTwoLinesItem == pActiveTwoLinesItem &&
+ rPos < TextFrameIndex(GetText().getLength())))
+ { // The winner is a 2-line-attribute,
+ // the end of the multiportion depends on the following attributes...
+ SwMultiCreator aRet;
+
+ // We note the endpositions of the 2-line attributes in aEnd as stack
+ std::deque<TextFrameIndex> aEnd;
+
+ // The bOn flag signs the state of the last 2-line attribute in the
+ // aEnd-stack, it is compatible with the winner-attribute or
+ // it interrupts the other attribute.
+ bool bOn = true;
+
+ if (pActiveTwoLinesHint)
+ {
+ aRet.pItem = nullptr;
+ aRet.pAttr = pActiveTwoLinesHint;
+ aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
+ if (pNodeTwoLinesItem)
+ {
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
+ bOn = pNodeTwoLinesItem->GetEndBracket() ==
+ pActiveTwoLinesItem->GetEndBracket() &&
+ pNodeTwoLinesItem->GetStartBracket() ==
+ pActiveTwoLinesItem->GetStartBracket();
+ }
+ else
+ {
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *aRet.pAttr->End()));
+ }
+ }
+ else
+ {
+ aRet.pItem = pNodeTwoLinesItem;
+ aRet.pAttr = nullptr;
+ aRet.nStartOfAttr = TextFrameIndex(-1);
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
+ }
+ aRet.nId = SwMultiCreatorId::Double;
+ aRet.nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
+
+ // pActiveTwoLinesHint is the last 2-line-attribute, which contains
+ // the actual position.
+
+ // At this moment we know that at position rPos the "winner"-attribute
+ // causes a 2-line-portion. The end of the attribute is the end of the
+ // portion, if there's no interrupting attribute.
+ // There are two kinds of interrupters:
+ // - ruby attributes stops the 2-line-attribute, the end of the
+ // multiline is the start of the ruby attribute
+ // - 2-line-attributes with value "Off" or with different brackets,
+ // these attributes may interrupt the winner, but they could be
+ // neutralized by another 2-line-attribute starting at the same
+ // position with the same brackets as the winner-attribute.
+
+ // In the following loop rPos is the critical position and it will be
+ // evaluated, if at rPos starts an interrupting or a maintaining
+ // continuity attribute.
+
+ // iterAtStartOfNode is positioned to the first hint of the node
+ // (if any); the node item itself has already been handled above
+ for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
+ {
+ SwTextNode const* pNode(nullptr);
+ SwTextAttr const*const pTmp = iter.NextAttr(pNode);
+ if (!pNode)
+ {
+ break;
+ }
+ assert(startPos.first->GetIndex() <= pNode->GetIndex());
+ TextFrameIndex nTmpStart;
+ TextFrameIndex nTmpEnd;
+ if (pTmp)
+ {
+ nTmpEnd = m_pFrame->MapModelToView(pNode, pTmp->GetAnyEnd());
+ if (nTmpEnd <= rPos)
+ continue;
+ nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
+ }
+ else
+ {
+ pNodeTwoLinesItem = pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_TWO_LINES);
+ nTmpStart = m_pFrame->MapModelToView(pNode, 0);
+ nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
+ assert(rPos <= nTmpEnd); // next node must not have smaller index
+ }
+
+ if (rPos < nTmpStart)
+ {
+ // If bOn is false and the next attribute starts later than rPos
+ // the winner attribute is interrupted at rPos.
+ // If the start of the next attribute is behind the end of
+ // the last attribute on the aEnd-stack, this is the endposition
+ // on the stack is the end of the 2-line portion.
+ if (!bOn || aEnd.back() < nTmpStart)
+ break;
+ // At this moment, bOn is true and the next attribute starts
+ // behind rPos, so we could move rPos to the next startpoint
+ rPos = nTmpStart;
+ // We clean up the aEnd-stack, endpositions equal to rPos are
+ // superfluous.
+ while( !aEnd.empty() && aEnd.back() <= rPos )
+ {
+ bOn = !bOn;
+ aEnd.pop_back();
+ }
+ // If the endstack is empty, we simulate an attribute with
+ // state true and endposition rPos
+ if( aEnd.empty() )
+ {
+ aEnd.push_front( rPos );
+ bOn = true;
+ }
+ }
+ // A ruby attribute stops the 2-line immediately
+ if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
+ return aRet;
+ if (pTmp ? lcl_Has2Lines(*pTmp, pActiveTwoLinesItem, bTwo)
+ : lcl_Check2Lines(pNodeTwoLinesItem, pActiveTwoLinesItem, bTwo))
+ { // We have an interesting attribute...
+ if( bTwo == bOn )
+ { // .. with the same state, so the last attribute could
+ // be continued.
+ if (aEnd.back() < nTmpEnd)
+ aEnd.back() = nTmpEnd;
+ }
+ else
+ { // .. with a different state.
+ bOn = bTwo;
+ // If this is smaller than the last on the stack, we put
+ // it on the stack. If it has the same endposition, the last
+ // could be removed.
+ if (nTmpEnd < aEnd.back())
+ aEnd.push_back( nTmpEnd );
+ else if( aEnd.size() > 1 )
+ aEnd.pop_back();
+ else
+ aEnd.back() = nTmpEnd;
+ }
+ }
+ }
+ if( bOn && !aEnd.empty() )
+ rPos = aEnd.back();
+ return aRet;
+ }
+ if (pActiveRotateHint ||
+ (pNodeRotateItem && pNodeRotateItem == pActiveRotateItem &&
+ rPos < TextFrameIndex(GetText().getLength())))
+ { // The winner is a rotate-attribute,
+ // the end of the multiportion depends on the following attributes...
+ SwMultiCreator aRet;
+ aRet.nId = SwMultiCreatorId::Rotate;
+
+ // We note the endpositions of the 2-line attributes in aEnd as stack
+ std::deque<TextFrameIndex> aEnd;
+
+ // The bOn flag signs the state of the last 2-line attribute in the
+ // aEnd-stack, which could interrupts the winning rotation attribute.
+ bool bOn = pNodeTwoLinesItem != nullptr;
+ aEnd.push_front(TextFrameIndex(GetText().getLength()));
+
+ // first, search for the start position of the next TWOLINE portion
+ // because the ROTATE portion must end there at the latest
+ TextFrameIndex n2Start = rPos;
+ for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
+ {
+ SwTextNode const* pNode(nullptr);
+ SwTextAttr const*const pTmp = iter.NextAttr(pNode);
+ if (!pNode)
+ {
+ break;
+ }
+ assert(startPos.first->GetIndex() <= pNode->GetIndex());
+ TextFrameIndex nTmpStart;
+ TextFrameIndex nTmpEnd;
+ if (pTmp)
+ {
+ nTmpEnd = m_pFrame->MapModelToView(pNode, pTmp->GetAnyEnd());
+ if (nTmpEnd <= n2Start)
+ continue;
+ nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
+ }
+ else
+ {
+ pNodeTwoLinesItem = pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_TWO_LINES);
+ nTmpStart = m_pFrame->MapModelToView(pNode, 0);
+ nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
+ assert(n2Start <= nTmpEnd); // next node must not have smaller index
+ }
+
+ if (n2Start < nTmpStart)
+ {
+ if (bOn || aEnd.back() < nTmpStart)
+ break;
+ n2Start = nTmpStart;
+ while( !aEnd.empty() && aEnd.back() <= n2Start )
+ {
+ bOn = !bOn;
+ aEnd.pop_back();
+ }
+ if( aEnd.empty() )
+ {
+ aEnd.push_front( n2Start );
+ bOn = false;
+ }
+ }
+ // A ruby attribute stops immediately
+ if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
+ {
+ bOn = true;
+ break;
+ }
+ const SvxTwoLinesItem* p2Lines = nullptr;
+ if (pTmp ? lcl_Has2Lines(*pTmp, p2Lines, bTwo)
+ : lcl_Check2Lines(pNodeTwoLinesItem, p2Lines, bTwo))
+ {
+ if( bTwo == bOn )
+ {
+ if (aEnd.back() < nTmpEnd)
+ aEnd.back() = nTmpEnd;
+ }
+ else
+ {
+ bOn = bTwo;
+ if (nTmpEnd < aEnd.back())
+ aEnd.push_back( nTmpEnd );
+ else if( aEnd.size() > 1 )
+ aEnd.pop_back();
+ else
+ aEnd.back() = nTmpEnd;
+ }
+ }
+ }
+ if( !bOn && !aEnd.empty() )
+ n2Start = aEnd.back();
+
+ aEnd.clear();
+
+ // now, search for the end of the ROTATE portion, similar to above
+ bOn = true;
+ if (pActiveRotateHint)
+ {
+ aRet.pItem = nullptr;
+ aRet.pAttr = pActiveRotateHint;
+ aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
+ if (pNodeRotateItem)
+ {
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
+ bOn = pNodeRotateItem->GetValue() ==
+ pActiveRotateItem->GetValue();
+ }
+ else
+ {
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *aRet.pAttr->End()));
+ }
+ }
+ else
+ {
+ aRet.pItem = pNodeRotateItem;
+ aRet.pAttr = nullptr;
+ aRet.nStartOfAttr = TextFrameIndex(-1);
+ aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
+ }
+ for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
+ {
+ SwTextNode const* pNode(nullptr);
+ SwTextAttr const*const pTmp = iter.NextAttr(pNode);
+ if (!pNode)
+ {
+ break;
+ }
+ assert(startPos.first->GetIndex() <= pNode->GetIndex());
+ TextFrameIndex nTmpStart;
+ TextFrameIndex nTmpEnd;
+ if (pTmp)
+ {
+ nTmpEnd = m_pFrame->MapModelToView(pNode, pTmp->GetAnyEnd());
+ if (nTmpEnd <= rPos)
+ continue;
+ nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
+ }
+ else
+ {
+ pNodeRotateItem = pNode->GetSwAttrSet().GetItemIfSet(
+ RES_CHRATR_ROTATE);
+ nTmpStart = m_pFrame->MapModelToView(pNode, 0);
+ nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
+ assert(rPos <= nTmpEnd); // next node must not have smaller index
+ }
+
+ if (rPos < nTmpStart)
+ {
+ if (!bOn || aEnd.back() < nTmpStart)
+ break;
+ rPos = nTmpStart;
+ while( !aEnd.empty() && aEnd.back() <= rPos )
+ {
+ bOn = !bOn;
+ aEnd.pop_back();
+ }
+ if( aEnd.empty() )
+ {
+ aEnd.push_front( rPos );
+ bOn = true;
+ }
+ }
+ if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
+ {
+ bOn = false;
+ break;
+ }
+ // TODO why does this use bTwo, not bRot ???
+ if (pTmp ? lcl_HasRotation(*pTmp, pActiveRotateItem, bTwo)
+ : lcl_CheckRotation(pNodeRotateItem, pActiveRotateItem, bTwo))
+ {
+ if( bTwo == bOn )
+ {
+ if (aEnd.back() < nTmpEnd)
+ aEnd.back() = nTmpEnd;
+ }
+ else
+ {
+ bOn = bTwo;
+ if (nTmpEnd < aEnd.back())
+ aEnd.push_back( nTmpEnd );
+ else if( aEnd.size() > 1 )
+ aEnd.pop_back();
+ else
+ aEnd.back() = nTmpEnd;
+ }
+ }
+ }
+ if( bOn && !aEnd.empty() )
+ rPos = aEnd.back();
+ if( rPos > n2Start )
+ rPos = n2Start;
+ return aRet;
+ }
+ return {};
+}
+
+namespace {
+
+// A little helper class to manage the spaceadd-arrays of the text adjustment
+// during a PaintMultiPortion.
+// The constructor prepares the array for the first line of multiportion,
+// the SecondLine-function restores the values for the first line and prepares
+// the second line.
+// The destructor restores the values of the last manipulation.
+class SwSpaceManipulator
+{
+ SwTextPaintInfo& m_rInfo;
+ SwMultiPortion& m_rMulti;
+ std::vector<tools::Long>* m_pOldSpaceAdd;
+ sal_uInt16 m_nOldSpaceIndex;
+ tools::Long m_nSpaceAdd;
+ bool m_bSpaceChg;
+ sal_uInt8 m_nOldDir;
+
+public:
+ SwSpaceManipulator( SwTextPaintInfo& rInf, SwMultiPortion& rMult );
+ ~SwSpaceManipulator();
+ void SecondLine();
+ tools::Long GetSpaceAdd() const { return m_nSpaceAdd; }
+};
+
+}
+
+SwSpaceManipulator::SwSpaceManipulator(SwTextPaintInfo& rInf, SwMultiPortion& rMult)
+ : m_rInfo(rInf)
+ , m_rMulti(rMult)
+ , m_nSpaceAdd(0)
+{
+ m_pOldSpaceAdd = m_rInfo.GetpSpaceAdd();
+ m_nOldSpaceIndex = m_rInfo.GetSpaceIdx();
+ m_nOldDir = m_rInfo.GetDirection();
+ m_rInfo.SetDirection(m_rMulti.GetDirection());
+ m_bSpaceChg = false;
+
+ if (m_rMulti.IsDouble())
+ {
+ m_nSpaceAdd = (m_pOldSpaceAdd && !m_rMulti.HasTabulator()) ? m_rInfo.GetSpaceAdd() : 0;
+ if (m_rMulti.GetRoot().IsSpaceAdd())
+ {
+ m_rInfo.SetpSpaceAdd(m_rMulti.GetRoot().GetpLLSpaceAdd());
+ m_rInfo.ResetSpaceIdx();
+ m_bSpaceChg = m_rMulti.ChgSpaceAdd(&m_rMulti.GetRoot(), m_nSpaceAdd);
+ }
+ else if (m_rMulti.HasTabulator())
+ m_rInfo.SetpSpaceAdd(nullptr);
+ }
+ else if (!m_rMulti.IsBidi())
+ {
+ m_rInfo.SetpSpaceAdd(m_rMulti.GetRoot().GetpLLSpaceAdd());
+ m_rInfo.ResetSpaceIdx();
+ }
+}
+
+void SwSpaceManipulator::SecondLine()
+{
+ if (m_bSpaceChg)
+ {
+ m_rInfo.RemoveFirstSpaceAdd();
+ m_bSpaceChg = false;
+ }
+ SwLineLayout* pLay = m_rMulti.GetRoot().GetNext();
+ if( pLay->IsSpaceAdd() )
+ {
+ m_rInfo.SetpSpaceAdd(pLay->GetpLLSpaceAdd());
+ m_rInfo.ResetSpaceIdx();
+ m_bSpaceChg = m_rMulti.ChgSpaceAdd(pLay, m_nSpaceAdd);
+ }
+ else
+ {
+ m_rInfo.SetpSpaceAdd((!m_rMulti.IsDouble() || m_rMulti.HasTabulator()) ? nullptr
+ : m_pOldSpaceAdd);
+ m_rInfo.SetSpaceIdx(m_nOldSpaceIndex);
+ }
+}
+
+SwSpaceManipulator::~SwSpaceManipulator()
+{
+ if (m_bSpaceChg)
+ {
+ m_rInfo.RemoveFirstSpaceAdd();
+ m_bSpaceChg = false;
+ }
+ m_rInfo.SetpSpaceAdd(m_pOldSpaceAdd);
+ m_rInfo.SetSpaceIdx(m_nOldSpaceIndex);
+ m_rInfo.SetDirection(m_nOldDir);
+}
+
+// Manages the paint for a SwMultiPortion.
+// External, for the calling function, it seems to be a normal Paint-function,
+// internal it is like a SwTextFrame::PaintSwFrame with multiple DrawTextLines
+void SwTextPainter::PaintMultiPortion( const SwRect &rPaint,
+ SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor )
+{
+ SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
+ const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
+ sal_uInt16 nRubyHeight = 0;
+ bool bRubyTop = true;
+
+ if ( bHasGrid && pGrid->IsSquaredMode() )
+ {
+ nRubyHeight = pGrid->GetRubyHeight();
+ bRubyTop = ! pGrid->GetRubyTextBelow();
+ }
+
+ // do not allow grid mode for first line in ruby portion
+ const bool bRubyInGrid = bHasGrid && rMulti.IsRuby();
+
+ const sal_uInt16 nOldHeight = rMulti.Height();
+ const bool bOldGridModeAllowed = GetInfo().SnapToGrid();
+
+ if ( bRubyInGrid )
+ {
+ GetInfo().SetSnapToGrid( ! bRubyTop );
+ if (pGrid->IsSquaredMode())
+ rMulti.Height( m_pCurr->Height() );
+ }
+
+ SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
+ bool bEnvDir = false;
+ bool bThisDir = false;
+ bool bFrameDir = false;
+ if ( rMulti.IsBidi() )
+ {
+ // these values are needed for the calculation of the x coordinate
+ // and the layout mode
+ OSL_ENSURE( ! pEnvPor || pEnvPor->IsBidi(),
+ "Oh no, I expected a BidiPortion" );
+ bFrameDir = GetInfo().GetTextFrame()->IsRightToLeft();
+ bEnvDir = pEnvPor ? ((static_cast<const SwBidiPortion*>(pEnvPor)->GetLevel() % 2) != 0) : bFrameDir;
+ bThisDir = (static_cast<SwBidiPortion&>(rMulti).GetLevel() % 2) != 0;
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ // only paint first level bidi portions
+ if( rMulti.Width() > 1 && ! pEnvPor )
+ GetInfo().DrawViewOpt( rMulti, PortionType::Field );
+#endif
+
+ if ( bRubyInGrid && pGrid->IsSquaredMode() )
+ rMulti.Height( nOldHeight );
+
+ // do we have to repaint a post it portion?
+ if( GetInfo().OnWin() && rMulti.GetNextPortion() &&
+ ! rMulti.GetNextPortion()->Width() )
+ rMulti.GetNextPortion()->PrePaint( GetInfo(), &rMulti );
+
+ // old values must be saved and restored at the end
+ TextFrameIndex const nOldLen = GetInfo().GetLen();
+ const SwTwips nOldX = GetInfo().X();
+ const SwTwips nOldY = GetInfo().Y();
+ TextFrameIndex const nOldIdx = GetInfo().GetIdx();
+
+ SwSpaceManipulator aManip( GetInfo(), rMulti );
+
+ std::unique_ptr<SwFontSave> pFontSave;
+ std::unique_ptr<SwFont> pTmpFnt;
+
+ if( rMulti.IsDouble() )
+ {
+ pTmpFnt.reset(new SwFont( *GetInfo().GetFont() ));
+ if( rMulti.IsDouble() )
+ {
+ SetPropFont( 50 );
+ pTmpFnt->SetProportion( GetPropFont() );
+ }
+ pFontSave.reset(new SwFontSave( GetInfo(), pTmpFnt.get(), this ));
+ }
+ else
+ {
+ pFontSave = nullptr;
+ pTmpFnt = nullptr;
+ }
+
+ if( rMulti.HasBrackets() )
+ {
+ TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx();
+ GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
+ SeekAndChg( GetInfo() );
+ static_cast<SwDoubleLinePortion&>(rMulti).PaintBracket( GetInfo(), 0, true );
+ GetInfo().SetIdx( nTmpOldIdx );
+ }
+
+ const SwTwips nTmpX = GetInfo().X();
+
+ SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion
+ SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line
+ SwTwips nOfst = 0;
+
+ // GetInfo().Y() is the baseline from the surrounding line. We must switch
+ // this temporary to the baseline of the inner lines of the multiportion.
+ if( rMulti.HasRotation() )
+ {
+ if( rMulti.IsRevers() )
+ {
+ GetInfo().Y( nOldY - rMulti.GetAscent() );
+ nOfst = nTmpX + rMulti.Width();
+ }
+ else
+ {
+ GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
+ nOfst = nTmpX;
+ }
+ }
+ else if ( rMulti.IsBidi() )
+ {
+ // does the current bidi portion has the same direction
+ // as its environment?
+ if ( bEnvDir != bThisDir )
+ {
+ // different directions, we have to adjust the x coordinate
+ SwTwips nMultiWidth = rMulti.Width() +
+ rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
+
+ if ( bFrameDir == bThisDir )
+ GetInfo().X( GetInfo().X() - nMultiWidth );
+ else
+ GetInfo().X( GetInfo().X() + nMultiWidth );
+ }
+
+ nOfst = nOldY - rMulti.GetAscent();
+
+ // set layout mode
+ aLayoutModeModifier.Modify( bThisDir );
+ }
+ else
+ nOfst = nOldY - rMulti.GetAscent();
+
+ bool bRest = pLay->IsRest();
+ bool bFirst = true;
+
+ OSL_ENSURE( nullptr == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
+ " Only BiDi portions are allowed to use the common underlining font" );
+
+ if ( rMulti.IsRuby() )
+ GetInfo().SetRuby( rMulti.OnTop() );
+
+ do
+ {
+ if ( bHasGrid && pGrid->IsSquaredMode() )
+ {
+ if( rMulti.HasRotation() )
+ {
+ const sal_uInt16 nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 +
+ pPor->GetAscent();
+ if( rMulti.IsRevers() )
+ GetInfo().X( nOfst - nAdjustment );
+ else
+ GetInfo().X( nOfst + nAdjustment );
+ }
+ else
+ {
+ // special treatment for ruby portions in grid mode
+ SwTwips nAdjustment = 0;
+ if ( rMulti.IsRuby() )
+ {
+ if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) )
+ // adjust base text
+ nAdjustment = ( m_pCurr->Height() - nRubyHeight - pPor->Height() ) / 2;
+ else if ( bRubyTop )
+ // adjust upper ruby text
+ nAdjustment = nRubyHeight - pPor->Height();
+ // else adjust lower ruby text
+ }
+
+ GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() );
+ }
+ }
+ else if( rMulti.HasRotation() )
+ {
+ if( rMulti.IsRevers() )
+ GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, true ) );
+ else
+ GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) );
+ }
+ else if ( rMulti.IsRuby() && rMulti.OnRight() && GetInfo().IsRuby() )
+ {
+ SwTwips nLineDiff = std::max(( rMulti.GetRoot().Height() - pPor->Width() ) / 2, static_cast<SwTwips>(0) );
+ GetInfo().Y( nOfst + nLineDiff );
+ // Draw the ruby text on top of the preserved space.
+ GetInfo().X( GetInfo().X() - pPor->Height() );
+ }
+ else
+ GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
+
+ bool bSeeked = true;
+ GetInfo().SetLen( pPor->GetLen() );
+
+ if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
+ {
+ if( static_cast<SwFieldPortion*>(pPor)->HasFont() )
+ bSeeked = false;
+ else
+ SeekAndChgBefore( GetInfo() );
+ }
+ else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
+ SeekAndChg( GetInfo() );
+ else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
+ {
+ if( GetRedln() )
+ SeekAndChg( GetInfo() );
+ else
+ SeekAndChgBefore( GetInfo() );
+ }
+ else
+ bSeeked = false;
+
+ SwLinePortion *pNext = pPor->GetNextPortion();
+ if(GetInfo().OnWin() && pNext && !pNext->Width() )
+ {
+ if ( !bSeeked )
+ SeekAndChg( GetInfo() );
+ pNext->PrePaint( GetInfo(), pPor );
+ }
+
+ CheckSpecialUnderline( pPor );
+ SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
+ if ( pUnderLineFnt )
+ {
+ if ( rMulti.IsDouble() )
+ pUnderLineFnt->GetFont().SetProportion( 50 );
+ pUnderLineFnt->SetPos( GetInfo().GetPos() );
+ }
+
+ if ( rMulti.IsBidi() )
+ {
+ // we do not allow any rotation inside a bidi portion
+ SwFont* pTmpFont = GetInfo().GetFont();
+ pTmpFont->SetVertical( 0_deg10, GetInfo().GetTextFrame()->IsVertical() );
+ }
+
+ if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsBidi() )
+ {
+ // but we do allow nested bidi portions
+ OSL_ENSURE( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" );
+ PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor), &rMulti );
+ }
+ else
+ pPor->Paint( GetInfo() );
+
+ bFirst &= !pPor->GetLen();
+ if( pNext || !pPor->IsMarginPortion() )
+ pPor->Move( GetInfo() );
+
+ pPor = pNext;
+
+ // If there's no portion left, we go to the next line
+ if( !pPor && pLay->GetNext() )
+ {
+ pLay = pLay->GetNext();
+ pPor = pLay->GetFirstPortion();
+ bRest = pLay->IsRest();
+ aManip.SecondLine();
+
+ // delete underline font
+ delete GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( nullptr );
+
+ if( rMulti.HasRotation() )
+ {
+ if( rMulti.IsRevers() )
+ {
+ nOfst += pLay->Height();
+ GetInfo().Y( nOldY - rMulti.GetAscent() );
+ }
+ else
+ {
+ nOfst -= pLay->Height();
+ GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
+ }
+ }
+ else if ( bHasGrid && rMulti.IsRuby() )
+ {
+ GetInfo().SetSnapToGrid( bRubyTop );
+ GetInfo().X( nTmpX );
+ if (pGrid->IsSquaredMode() )
+ {
+ if ( bRubyTop )
+ nOfst += nRubyHeight;
+ else
+ nOfst += m_pCurr->Height() - nRubyHeight;
+ }
+ else
+ {
+ nOfst += rMulti.GetRoot().Height();
+ }
+ }
+ else if ( rMulti.IsRuby() && rMulti.OnRight() )
+ {
+ GetInfo().SetDirection( DIR_TOP2BOTTOM );
+ GetInfo().SetRuby( true );
+ } else
+ {
+ GetInfo().X( nTmpX );
+ // We switch to the baseline of the next inner line
+ nOfst += rMulti.GetRoot().Height();
+ }
+ }
+ } while( pPor );
+
+ if ( bRubyInGrid )
+ GetInfo().SetSnapToGrid( bOldGridModeAllowed );
+
+ // delete underline font
+ if ( ! rMulti.IsBidi() )
+ {
+ delete GetInfo().GetUnderFnt();
+ GetInfo().SetUnderFnt( nullptr );
+ }
+
+ GetInfo().SetIdx( nOldIdx );
+ GetInfo().Y( nOldY );
+
+ if( rMulti.HasBrackets() )
+ {
+ TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx();
+ GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
+ SeekAndChg( GetInfo() );
+ GetInfo().X( nOldX );
+ static_cast<SwDoubleLinePortion&>(rMulti).PaintBracket( GetInfo(),
+ aManip.GetSpaceAdd(), false );
+ GetInfo().SetIdx( nTmpOldIdx );
+ }
+ // Restore the saved values
+ GetInfo().X( nOldX );
+ GetInfo().SetLen( nOldLen );
+ pFontSave.reset();
+ pTmpFnt.reset();
+ SetPropFont( 0 );
+}
+
+static bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpField )
+{
+ SwLinePortion* pLast = pLine;
+ rpField = pLine->GetNextPortion();
+ while( rpField && !rpField->InFieldGrp() )
+ {
+ pLast = rpField;
+ rpField = rpField->GetNextPortion();
+ }
+ bool bRet = rpField != nullptr;
+ if( bRet )
+ {
+ if( static_cast<SwFieldPortion*>(rpField)->IsFollow() )
+ {
+ rpField->Truncate();
+ pLast->SetNextPortion( nullptr );
+ }
+ else
+ rpField = nullptr;
+ }
+ pLine->Truncate();
+ return bRet;
+}
+
+// If a multi portion completely has to go to the
+// next line, this function is called to truncate
+// the rest of the remaining multi portion
+static void lcl_TruncateMultiPortion( SwMultiPortion& rMulti, SwTextFormatInfo& rInf,
+ TextFrameIndex const nStartIdx)
+{
+ rMulti.GetRoot().Truncate();
+ rMulti.GetRoot().SetLen(TextFrameIndex(0));
+ rMulti.GetRoot().Width(0);
+// rMulti.CalcSize( *this, aInf );
+ if ( rMulti.GetRoot().GetNext() )
+ {
+ rMulti.GetRoot().GetNext()->Truncate();
+ rMulti.GetRoot().GetNext()->SetLen(TextFrameIndex(0));
+ rMulti.GetRoot().GetNext()->Width( 0 );
+ }
+ rMulti.Width( 0 );
+ rMulti.SetLen(TextFrameIndex(0));
+ rInf.SetIdx( nStartIdx );
+}
+
+// Manages the formatting of a SwMultiPortion. External, for the calling
+// function, it seems to be a normal Format-function, internal it is like a
+// SwTextFrame::Format_ with multiple BuildPortions
+bool SwTextFormatter::BuildMultiPortion( SwTextFormatInfo &rInf,
+ SwMultiPortion& rMulti )
+{
+ SwTwips nMaxWidth = rInf.Width();
+ SwTwips nOldX = 0;
+
+ if( rMulti.HasBrackets() )
+ {
+ TextFrameIndex const nOldIdx = rInf.GetIdx();
+ rInf.SetIdx( static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart );
+ SeekAndChg( rInf );
+ nOldX = GetInfo().X();
+ static_cast<SwDoubleLinePortion&>(rMulti).FormatBrackets( rInf, nMaxWidth );
+ rInf.SetIdx( nOldIdx );
+ }
+
+ SeekAndChg( rInf );
+ std::unique_ptr<SwFontSave> xFontSave;
+ std::unique_ptr<SwFont> xTmpFont;
+ if( rMulti.IsDouble() )
+ {
+ xTmpFont.reset(new SwFont( *rInf.GetFont() ));
+ if( rMulti.IsDouble() )
+ {
+ SetPropFont( 50 );
+ xTmpFont->SetProportion( GetPropFont() );
+ }
+ xFontSave.reset(new SwFontSave(rInf, xTmpFont.get(), this));
+ }
+
+ SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
+ if ( rMulti.IsBidi() )
+ {
+ // set layout mode
+ aLayoutModeModifier.Modify( ! rInf.GetTextFrame()->IsRightToLeft() );
+ }
+
+ SwTwips nTmpX = 0;
+
+ if( rMulti.HasRotation() )
+ {
+ // For nMaxWidth we take the height of the body frame.
+ // #i25067#: If the current frame is inside a table, we restrict
+ // nMaxWidth to the current frame height, unless the frame size
+ // attribute is set to variable size:
+
+ // We set nTmpX (which is used for portion calculating) to the
+ // current Y value
+ const SwPageFrame* pPage = m_pFrame->FindPageFrame();
+ OSL_ENSURE( pPage, "No page in frame!");
+ const SwLayoutFrame* pUpperFrame = pPage;
+
+ if ( m_pFrame->IsInTab() )
+ {
+ pUpperFrame = m_pFrame->GetUpper();
+ while ( pUpperFrame && !pUpperFrame->IsCellFrame() )
+ pUpperFrame = pUpperFrame->GetUpper();
+ assert(pUpperFrame); //pFrame is in table but does not have an upper cell frame
+ if (!pUpperFrame)
+ return false;
+ const SwTableLine* pLine = static_cast<const SwRowFrame*>(pUpperFrame->GetUpper())->GetTabLine();
+ const SwFormatFrameSize& rFrameFormatSize = pLine->GetFrameFormat()->GetFrameSize();
+ if ( SwFrameSize::Variable == rFrameFormatSize.GetHeightSizeType() )
+ pUpperFrame = pPage;
+ }
+ if ( pUpperFrame == pPage && !m_pFrame->IsInFootnote() )
+ pUpperFrame = pPage->FindBodyCont();
+
+ nMaxWidth = pUpperFrame ?
+ ( rInf.GetTextFrame()->IsVertical() ?
+ pUpperFrame->getFramePrintArea().Width() :
+ pUpperFrame->getFramePrintArea().Height() ) :
+ USHRT_MAX;
+ }
+ else
+ nTmpX = rInf.X();
+
+ SwMultiPortion* pOldMulti = m_pMulti;
+
+ m_pMulti = &rMulti;
+ SwLineLayout *pOldCurr = m_pCurr;
+ TextFrameIndex const nOldStart = GetStart();
+ SwTwips nMinWidth = nTmpX + 1;
+ SwTwips nActWidth = nMaxWidth;
+ const TextFrameIndex nStartIdx = rInf.GetIdx();
+ TextFrameIndex nMultiLen = rMulti.GetLen();
+
+ SwLinePortion *pFirstRest;
+ SwLinePortion *pSecondRest;
+ if( rMulti.IsFormatted() )
+ {
+ if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest )
+ && rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
+ lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest );
+ if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
+ lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest );
+ else
+ pSecondRest = nullptr;
+ }
+ else
+ {
+ pFirstRest = rMulti.GetRoot().GetNextPortion();
+ pSecondRest = rMulti.GetRoot().GetNext() ?
+ rMulti.GetRoot().GetNext()->GetNextPortion() : nullptr;
+ if( pFirstRest )
+ rMulti.GetRoot().SetNextPortion( nullptr );
+ if( pSecondRest )
+ rMulti.GetRoot().GetNext()->SetNextPortion( nullptr );
+ rMulti.SetFormatted();
+ nMultiLen = nMultiLen - rInf.GetIdx();
+ }
+
+ // save some values
+ const OUString* pOldText = &(rInf.GetText());
+ const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
+ std::shared_ptr<const vcl::text::TextLayoutCache> const pOldCachedVclData(rInf.GetCachedVclData());
+ rInf.SetCachedVclData(nullptr);
+
+ OUString const aMultiStr( rInf.GetText().copy(0, sal_Int32(nMultiLen + rInf.GetIdx())) );
+ rInf.SetText( aMultiStr );
+ SwTextFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth );
+ // Do we allow break cuts? The FirstMulti-Flag is evaluated during
+ // line break determination.
+ bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart();
+
+ SwLinePortion *pNextFirst = nullptr;
+ SwLinePortion *pNextSecond = nullptr;
+ bool bRet = false;
+
+ SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
+ const bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
+
+ bool bRubyTop = false;
+
+ if ( bHasGrid )
+ bRubyTop = ! pGrid->GetRubyTextBelow();
+
+ do
+ {
+ m_pCurr = &rMulti.GetRoot();
+ m_nStart = nStartIdx;
+ bRet = false;
+ FormatReset( aInf );
+ aInf.X( nTmpX );
+ aInf.Width( sal_uInt16(nActWidth) );
+ aInf.RealWidth( sal_uInt16(nActWidth) );
+ aInf.SetFirstMulti( bFirstMulti );
+ aInf.SetNumDone( rInf.IsNumDone() );
+ aInf.SetFootnoteDone( rInf.IsFootnoteDone() );
+
+ // if there's a bookmark at the start of the MultiPortion, it will be
+ // painted with the rotation etc. of the MultiPortion; move it *inside*
+ // so it gets positioned correctly; currently there's no other portion
+ // inserted between the end of WhichFirstPortion() and
+ // BuildMultiPortion()
+ if (rInf.GetLast()->GetWhichPor() == PortionType::Bookmark)
+ {
+ auto const pBookmark(static_cast<SwBookmarkPortion*>(rInf.GetLast()));
+ auto *const pPrevious = pBookmark->FindPrevPortion(rInf.GetRoot());
+ assert(!pPrevious || pPrevious->GetNextPortion() == pBookmark);
+ if (pPrevious)
+ {
+ pPrevious->SetNextPortion(nullptr);
+ }
+ rInf.SetLast(pPrevious);
+ assert(m_pCurr->GetNextPortion() == nullptr);
+ m_pCurr->SetNextPortion(pBookmark);
+ }
+
+ if( pFirstRest )
+ {
+ OSL_ENSURE( pFirstRest->InFieldGrp(), "BuildMulti: Fieldrest expected");
+ SwFieldPortion *pField =
+ static_cast<SwFieldPortion*>(pFirstRest)->Clone(
+ static_cast<SwFieldPortion*>(pFirstRest)->GetExp() );
+ pField->SetFollow( true );
+ aInf.SetRest( pField );
+ }
+ aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() );
+
+ // in grid mode we temporarily have to disable the grid for the ruby line
+ const bool bOldGridModeAllowed = GetInfo().SnapToGrid();
+ if ( bHasGrid && aInf.IsRuby() && bRubyTop )
+ aInf.SetSnapToGrid( false );
+
+ // If there's no more rubytext, then buildportion is forbidden
+ if( pFirstRest || !aInf.IsRuby() )
+ BuildPortions( aInf );
+
+ aInf.SetSnapToGrid( bOldGridModeAllowed );
+
+ rMulti.CalcSize( *this, aInf );
+ m_pCurr->SetRealHeight( m_pCurr->Height() );
+
+ if( rMulti.IsBidi() )
+ {
+ pNextFirst = aInf.GetRest();
+ break;
+ }
+
+ if( rMulti.HasRotation() && !rMulti.IsDouble() )
+ break;
+ // second line has to be formatted
+ else if( m_pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest())
+ {
+ TextFrameIndex const nFirstLen = m_pCurr->GetLen();
+ delete m_pCurr->GetNext();
+ m_pCurr->SetNext( new SwLineLayout() );
+ m_pCurr = m_pCurr->GetNext();
+ m_nStart = aInf.GetIdx();
+ aInf.X( nTmpX );
+ SwTextFormatInfo aTmp( aInf, *m_pCurr, nActWidth );
+ if( rMulti.IsRuby() )
+ {
+ aTmp.SetRuby( !rMulti.OnTop() );
+ pNextFirst = aInf.GetRest();
+ if( pSecondRest )
+ {
+ OSL_ENSURE( pSecondRest->InFieldGrp(), "Fieldrest expected");
+ SwFieldPortion *pField = static_cast<SwFieldPortion*>(pSecondRest)->Clone(
+ static_cast<SwFieldPortion*>(pSecondRest)->GetExp() );
+ pField->SetFollow( true );
+ aTmp.SetRest( pField );
+ }
+ if( !rMulti.OnTop() && nFirstLen < nMultiLen )
+ bRet = true;
+ }
+ else
+ aTmp.SetRest( aInf.GetRest() );
+ aInf.SetRest( nullptr );
+
+ // in grid mode we temporarily have to disable the grid for the ruby line
+ if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop )
+ aTmp.SetSnapToGrid( false );
+
+ BuildPortions( aTmp );
+
+ const SwLinePortion *pRightPortion = rMulti.OnRight() ?
+ rMulti.GetRoot().GetNext()->GetNextPortion() : nullptr;
+ if (pRightPortion)
+ {
+ // The ruby text on the right is vertical.
+ // The width and the height are swapped.
+ SwTwips nHeight = pRightPortion->Height();
+ // Keep room for the ruby text.
+ rMulti.GetRoot().FindLastPortion()->AddPrtWidth( nHeight );
+ }
+
+ aTmp.SetSnapToGrid( bOldGridModeAllowed );
+
+ rMulti.CalcSize( *this, aInf );
+ rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() );
+ m_pCurr->SetRealHeight( m_pCurr->Height() );
+ if( rMulti.IsRuby() )
+ {
+ pNextSecond = aTmp.GetRest();
+ if( pNextFirst )
+ bRet = true;
+ }
+ else
+ pNextFirst = aTmp.GetRest();
+ if( ( !aTmp.IsRuby() && nFirstLen + m_pCurr->GetLen() < nMultiLen )
+ || aTmp.GetRest() )
+ // our guess for width of multiportion was too small,
+ // text did not fit into multiportion
+ bRet = true;
+ }
+ if( rMulti.IsRuby() )
+ break;
+ if( bRet )
+ {
+ // our guess for multiportion width was too small,
+ // we set min to act
+ nMinWidth = nActWidth;
+ nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
+ if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() )
+ // we have too less space, we must allow break cuts
+ // ( the first multi flag is considered during TextPortion::Format_() )
+ bFirstMulti = false;
+ if( nActWidth <= nMinWidth )
+ break;
+ }
+ else
+ {
+ // For Solaris, this optimization can causes trouble:
+ // Setting this to the portion width ( = rMulti.Width() )
+ // can make GetTextBreak inside SwTextGuess::Guess return too small
+ // values. Therefore we add some extra twips.
+ if( nActWidth > nTmpX + rMulti.Width() + 6 )
+ nActWidth = nTmpX + rMulti.Width() + 6;
+ nMaxWidth = nActWidth;
+ nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
+ if( nActWidth >= nMaxWidth )
+ break;
+ // we do not allow break cuts during formatting
+ bFirstMulti = true;
+ }
+ delete pNextFirst;
+ pNextFirst = nullptr;
+ } while ( true );
+
+ m_pMulti = pOldMulti;
+
+ m_pCurr = pOldCurr;
+ m_nStart = nOldStart;
+ SetPropFont( 0 );
+
+ rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ?
+ rMulti.GetRoot().GetNext()->GetLen() : TextFrameIndex(0) ) );
+
+ if( rMulti.IsDouble() )
+ {
+ static_cast<SwDoubleLinePortion&>(rMulti).CalcBlanks( rInf );
+ if( static_cast<SwDoubleLinePortion&>(rMulti).GetLineDiff() )
+ {
+ SwLineLayout* pLine = &rMulti.GetRoot();
+ if( static_cast<SwDoubleLinePortion&>(rMulti).GetLineDiff() > 0 )
+ {
+ rInf.SetIdx( nStartIdx + pLine->GetLen() );
+ pLine = pLine->GetNext();
+ }
+ if( pLine )
+ {
+ GetInfo().SetMulti( true );
+
+ // If the fourth element bSkipKashida of function CalcNewBlock is true, multiportion will be showed in justification.
+ // Kashida (Persian) is a type of justification used in some cursive scripts, particularly Arabic.
+ // In contrast to white-space justification, which increases the length of a line of text by expanding spaces between words or individual letters,
+ // kashida justification is accomplished by elongating characters at certain chosen points.
+ // Kashida justification can be combined with white-space justification to various extents.
+ // The default value of bSkipKashida (the 4th parameter passed to 'CalcNewBlock') is false.
+ // Only when Adjust is SvxAdjust::Block ( alignment is justify ), multiportion will be showed in justification in new code.
+ CalcNewBlock( pLine, nullptr, rMulti.Width(), GetAdjust() != SvxAdjust::Block );
+
+ GetInfo().SetMulti( false );
+ }
+ rInf.SetIdx( nStartIdx );
+ }
+ if( static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets() )
+ {
+ rMulti.Width( rMulti.Width() +
+ static_cast<SwDoubleLinePortion&>(rMulti).BracketWidth() );
+ GetInfo().X( nOldX );
+ }
+ }
+ else
+ {
+ rMulti.ActualizeTabulator();
+ if( rMulti.IsRuby() )
+ {
+ static_cast<SwRubyPortion&>(rMulti).Adjust( rInf );
+ static_cast<SwRubyPortion&>(rMulti).CalcRubyOffset();
+ }
+ }
+ if( rMulti.HasRotation() )
+ {
+ SwTwips nH = rMulti.Width();
+ SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2;
+ if( nAsc > nH )
+ nAsc = nH;
+ else if( nAsc < 0 )
+ nAsc = 0;
+ rMulti.Width( rMulti.Height() );
+ rMulti.Height( sal_uInt16(nH) );
+ rMulti.SetAscent( sal_uInt16(nAsc) );
+ bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) &&
+ nStartIdx != rInf.GetLineStart();
+ }
+ else if ( rMulti.IsBidi() )
+ {
+ bRet = rMulti.GetLen() < nMultiLen || pNextFirst;
+ }
+
+ // line break has to be performed!
+ if( bRet )
+ {
+ OSL_ENSURE( !pNextFirst || pNextFirst->InFieldGrp(),
+ "BuildMultiPortion: Surprising restportion, field expected" );
+ SwMultiPortion *pTmp;
+ if( rMulti.IsDouble() )
+ pTmp = new SwDoubleLinePortion( static_cast<SwDoubleLinePortion&>(rMulti),
+ nMultiLen + rInf.GetIdx() );
+ else if( rMulti.IsRuby() )
+ {
+ OSL_ENSURE( !pNextSecond || pNextSecond->InFieldGrp(),
+ "BuildMultiPortion: Surprising restportion, field expected" );
+
+ if ( rInf.GetIdx() == rInf.GetLineStart() )
+ {
+ // the ruby portion has to be split in two portions
+ pTmp = new SwRubyPortion( static_cast<SwRubyPortion&>(rMulti),
+ nMultiLen + rInf.GetIdx() );
+
+ if( pNextSecond )
+ {
+ pTmp->GetRoot().SetNext( new SwLineLayout() );
+ pTmp->GetRoot().GetNext()->SetNextPortion( pNextSecond );
+ }
+ pTmp->SetFollowField();
+ }
+ else
+ {
+ // we try to keep our ruby portion together
+ lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ pTmp = nullptr;
+ }
+ }
+ else if( rMulti.HasRotation() )
+ {
+ // we try to keep our rotated portion together
+ lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+ pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
+ rMulti.GetDirection() );
+ }
+ // during a recursion of BuildMultiPortions we may not build
+ // a new SwBidiPortion, this would cause a memory leak
+ else if( rMulti.IsBidi() && ! m_pMulti )
+ {
+ if ( ! rMulti.GetLen() )
+ lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
+
+ // If there is a HolePortion at the end of the bidi portion,
+ // it has to be moved behind the bidi portion. Otherwise
+ // the visual cursor travelling gets into trouble.
+ SwLineLayout& aRoot = rMulti.GetRoot();
+ SwLinePortion* pPor = aRoot.GetFirstPortion();
+ while ( pPor )
+ {
+ if ( pPor->GetNextPortion() && pPor->GetNextPortion()->IsHolePortion() )
+ {
+ SwLinePortion* pHolePor = pPor->GetNextPortion();
+ pPor->SetNextPortion( nullptr );
+ aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() );
+ rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() );
+ rMulti.SetNextPortion( pHolePor );
+ break;
+ }
+ pPor = pPor->GetNextPortion();
+ }
+
+ pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(),
+ static_cast<SwBidiPortion&>(rMulti).GetLevel() );
+ }
+ else
+ pTmp = nullptr;
+
+ if ( ! rMulti.GetLen() && rInf.GetLast() )
+ {
+ SeekAndChgBefore( rInf );
+ rInf.GetLast()->FormatEOL( rInf );
+ }
+
+ if( pNextFirst && pTmp )
+ {
+ pTmp->SetFollowField();
+ pTmp->GetRoot().SetNextPortion( pNextFirst );
+ }
+ else
+ // A follow field portion is still waiting. If nobody wants it,
+ // we delete it.
+ delete pNextFirst;
+
+ rInf.SetRest( pTmp );
+ }
+
+ rInf.SetCachedVclData(pOldCachedVclData);
+ rInf.SetText( *pOldText );
+ rInf.SetPaintOfst( nOldPaintOfst );
+ rInf.SetStop( aInf.IsStop() );
+ rInf.SetNumDone( true );
+ rInf.SetFootnoteDone( true );
+ SeekAndChg( rInf );
+ delete pFirstRest;
+ delete pSecondRest;
+ xFontSave.reset();
+ return bRet;
+}
+
+// When a fieldportion at the end of line breaks and needs a following
+// fieldportion in the next line, then the "restportion" of the formatinfo
+// has to be set. Normally this happens during the formatting of the first
+// part of the fieldportion.
+// But sometimes the formatting starts at the line with the following part,
+// especially when the following part is on the next page.
+// In this case the MakeRestPortion-function has to create the following part.
+// The first parameter is the line that contains possibly a first part
+// of a field. When the function finds such field part, it creates the right
+// restportion. This may be a multiportion, e.g. if the field is surrounded by
+// a doubleline- or ruby-portion.
+// The second parameter is the start index of the line.
+SwLinePortion* SwTextFormatter::MakeRestPortion( const SwLineLayout* pLine,
+ TextFrameIndex nPosition)
+{
+ if( !nPosition )
+ return nullptr;
+ TextFrameIndex nMultiPos = nPosition - pLine->GetLen();
+ const SwMultiPortion *pTmpMulti = nullptr;
+ const SwMultiPortion *pHelpMulti = nullptr;
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ SwFieldPortion *pField = nullptr;
+ while( pPor )
+ {
+ if( pPor->GetLen() && !pHelpMulti )
+ {
+ nMultiPos = nMultiPos + pPor->GetLen();
+ pTmpMulti = nullptr;
+ }
+ if( pPor->InFieldGrp() )
+ {
+ if( !pHelpMulti )
+ pTmpMulti = nullptr;
+ pField = const_cast<SwFieldPortion*>(static_cast<const SwFieldPortion*>(pPor));
+ }
+ else if( pPor->IsMultiPortion() )
+ {
+ OSL_ENSURE( !pHelpMulti || pHelpMulti->IsBidi(),
+ "Nested multiportions are forbidden." );
+
+ pField = nullptr;
+ pTmpMulti = static_cast<const SwMultiPortion*>(pPor);
+ }
+ pPor = pPor->GetNextPortion();
+ // If the last portion is a multi-portion, we enter it
+ // and look for a field portion inside.
+ // If we are already in a multiportion, we could change to the
+ // next line
+ if( !pPor && pTmpMulti )
+ {
+ if( pHelpMulti )
+ { // We're already inside the multiportion, let's take the second
+ // line, if we are in a double line portion
+ if( !pHelpMulti->IsRuby() )
+ pPor = pHelpMulti->GetRoot().GetNext();
+ pTmpMulti = nullptr;
+ }
+ else
+ { // Now we enter a multiportion, in a ruby portion we take the
+ // main line, not the phonetic line, in a doublelineportion we
+ // starts with the first line.
+ pHelpMulti = pTmpMulti;
+ nMultiPos = nMultiPos - pHelpMulti->GetLen();
+ if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() )
+ pPor = pHelpMulti->GetRoot().GetNext();
+ else
+ pPor = pHelpMulti->GetRoot().GetFirstPortion();
+ }
+ }
+ }
+ if( pField && !pField->HasFollow() )
+ pField = nullptr;
+
+ SwLinePortion *pRest = nullptr;
+ if( pField )
+ {
+ const SwTextAttr *pHint = GetAttr(nPosition - TextFrameIndex(1));
+ if ( pHint
+ && ( pHint->Which() == RES_TXTATR_FIELD
+ || pHint->Which() == RES_TXTATR_ANNOTATION ) )
+ {
+ pRest = NewFieldPortion( GetInfo(), pHint );
+ if( pRest->InFieldGrp() )
+ static_cast<SwFieldPortion*>(pRest)->TakeNextOffset( pField );
+ else
+ {
+ delete pRest;
+ pRest = nullptr;
+ }
+ }
+ }
+ if( !pHelpMulti )
+ return pRest;
+
+ nPosition = nMultiPos + pHelpMulti->GetLen();
+ std::optional<SwMultiCreator> pCreate = GetInfo().GetMultiCreator( nMultiPos, nullptr );
+
+ if ( !pCreate )
+ {
+ OSL_ENSURE( !pHelpMulti->GetLen(), "Multiportion without attribute?" );
+ if ( nMultiPos )
+ --nMultiPos;
+ pCreate = GetInfo().GetMultiCreator( --nMultiPos, nullptr );
+ }
+
+ if (!pCreate)
+ return pRest;
+
+ if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
+ static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset() < TextFrameIndex(COMPLETE_STRING)))
+ {
+ SwMultiPortion* pTmp;
+ if( pHelpMulti->IsDouble() )
+ pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
+ else if( pHelpMulti->IsBidi() )
+ pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
+ else if( pHelpMulti->IsRuby() )
+ {
+ pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
+ m_pFrame->GetDoc().getIDocumentSettingAccess(),
+ nMultiPos, static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset(),
+ GetInfo() );
+ }
+ else if( pHelpMulti->HasRotation() )
+ pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() );
+ else
+ {
+ return pRest;
+ }
+ pCreate.reset();
+ pTmp->SetFollowField();
+ if( pRest )
+ {
+ SwLineLayout *pLay = &pTmp->GetRoot();
+ if( pTmp->IsRuby() && pTmp->OnTop() )
+ {
+ pLay->SetNext( new SwLineLayout() );
+ pLay = pLay->GetNext();
+ }
+ pLay->SetNextPortion( pRest );
+ }
+ return pTmp;
+ }
+ return pRest;
+}
+
+// SwTextCursorSave notes the start and current line of a SwTextCursor,
+// sets them to the values for GetModelPositionForViewPoint inside a multiportion
+// and restores them in the destructor.
+SwTextCursorSave::SwTextCursorSave( SwTextCursor* pCursor,
+ SwMultiPortion* pMulti,
+ SwTwips nY,
+ SwTwips& nX,
+ TextFrameIndex const nCurrStart,
+ tools::Long nSpaceAdd )
+ : pTextCursor(pCursor),
+ pCurr(pCursor->m_pCurr),
+ nStart(pCursor->m_nStart)
+{
+ pCursor->m_nStart = nCurrStart;
+ pCursor->m_pCurr = &pMulti->GetRoot();
+ while( pCursor->Y() + pCursor->GetLineHeight() < nY &&
+ pCursor->Next() )
+ ; // nothing
+ nWidth = pCursor->m_pCurr->Width();
+ nOldProp = pCursor->GetPropFont();
+
+ if ( pMulti->IsDouble() || pMulti->IsBidi() )
+ {
+ bSpaceChg = pMulti->ChgSpaceAdd( pCursor->m_pCurr, nSpaceAdd );
+
+ TextFrameIndex nSpaceCnt;
+ if ( pMulti->IsDouble() )
+ {
+ pCursor->SetPropFont( 50 );
+ nSpaceCnt = static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
+ }
+ else
+ {
+ TextFrameIndex const nOldIdx = pCursor->GetInfo().GetIdx();
+ pCursor->GetInfo().SetIdx ( nCurrStart );
+ nSpaceCnt = static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt(pCursor->GetInfo());
+ pCursor->GetInfo().SetIdx ( nOldIdx );
+ }
+
+ if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
+ pCursor->m_pCurr->Width( o3tl::narrowing<sal_uInt16>(nWidth + nSpaceAdd * sal_Int32(nSpaceCnt) / SPACING_PRECISION_FACTOR) );
+
+ // For a BidiPortion we have to calculate the offset from the
+ // end of the portion
+ if ( nX && pMulti->IsBidi() )
+ nX = pCursor->m_pCurr->Width() - nX;
+ }
+ else
+ bSpaceChg = false;
+}
+
+SwTextCursorSave::~SwTextCursorSave()
+{
+ if( bSpaceChg )
+ SwDoubleLinePortion::ResetSpaceAdd( pTextCursor->m_pCurr );
+ pTextCursor->m_pCurr->Width( nWidth );
+ pTextCursor->m_pCurr = pCurr;
+ pTextCursor->m_nStart = nStart;
+ pTextCursor->SetPropFont( nOldProp );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/pormulti.hxx b/sw/source/core/text/pormulti.hxx
new file mode 100644
index 000000000..00389e796
--- /dev/null
+++ b/sw/source/core/text/pormulti.hxx
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <memory>
+#include "porlay.hxx"
+#include <com/sun/star/text/RubyAdjust.hpp>
+
+class IDocumentSettingAccess;
+class SwTextFormatInfo;
+class SwTextCursor;
+class SwTextPaintInfo;
+class SwTextAttr;
+class SfxPoolItem;
+class SwFont;
+
+// SwMultiCreator is a small structure to create a multiportion.
+// It contains the kind of multiportion and a textattribute
+// or a poolitem.
+// The GetMultiCreator-function fills this structure and
+// the Ctor of the SwMultiPortion uses it.
+enum class SwMultiCreatorId
+{
+ Double, Ruby, Rotate, Bidi
+};
+
+enum class RubyPosition : sal_uInt16
+{
+ ABOVE = 0,
+ BELOW = 1,
+ RIGHT = 2
+};
+
+struct SwMultiCreator
+{
+ TextFrameIndex nStartOfAttr;
+ const SwTextAttr* pAttr;
+ const SfxPoolItem* pItem;
+ SwMultiCreatorId nId;
+ sal_uInt8 nLevel;
+};
+
+// A two-line-portion (SwMultiPortion) could have surrounding brackets,
+// in this case the structure SwBracket will be used.
+struct SwBracket
+{
+ TextFrameIndex nStart; // Start of text attribute determines the font
+ sal_uInt16 nAscent; // Ascent of the brackets
+ sal_uInt16 nHeight; // Height of them
+ sal_uInt16 nPreWidth; // Width of the opening bracket
+ sal_uInt16 nPostWidth; // Width of the closing bracket
+ sal_Unicode cPre; // Initial character, e.g. '('
+ sal_Unicode cPost; // Final character, e.g. ')'
+ SwFontScript nPreScript; // Script of the initial character
+ SwFontScript nPostScript; // Script of the final character
+};
+
+// The SwMultiPortion is line portion inside a line portion,
+// it's a group of portions,
+// e.g. a double line portion in a line
+// or phonetics (ruby)
+// or combined characters
+// or a rotated portion.
+class SAL_DLLPUBLIC_RTTI SwMultiPortion : public SwLinePortion
+{
+ SwLineLayout m_aRoot; // One or more lines
+ bool m_bTab1 :1; // First line tabulator
+ bool m_bTab2 :1; // Second line includes tabulator
+ bool m_bDouble :1; // Double line
+ bool m_bRuby :1; // Phonetics
+ bool m_bBidi :1;
+ bool m_bFormatted :1; // Already formatted
+ bool m_bFollowField :1; // Field follow inside
+ bool m_bFlyInContent:1; // Fly as character inside
+ RubyPosition m_eRubyPosition; // Phonetic position
+ sal_uInt8 m_nDirection:2; // Direction (0/90/180/270 degrees)
+protected:
+ explicit SwMultiPortion(TextFrameIndex const nEnd)
+ : m_bTab1(false)
+ , m_bTab2(false)
+ , m_bDouble(false)
+ , m_bRuby(false)
+ , m_bBidi(false)
+ , m_bFormatted(false)
+ , m_bFollowField(false)
+ , m_bFlyInContent(false)
+ , m_eRubyPosition( RubyPosition::ABOVE )
+ , m_nDirection(0)
+ {
+ SetWhichPor(PortionType::Multi);
+ SetLen(nEnd);
+ }
+ void SetDouble() { m_bDouble = true; }
+ void SetRuby() { m_bRuby = true; }
+ void SetBidi() { m_bBidi = true; }
+ void SetRubyPosition( RubyPosition eNew ) { m_eRubyPosition = eNew; }
+ void SetTab1( bool bNew ) { m_bTab1 = bNew; }
+ void SetTab2( bool bNew ) { m_bTab2 = bNew; }
+ void SetDirection( sal_uInt8 nNew ) { m_nDirection = nNew; }
+ bool GetTab1() const { return m_bTab1; }
+ bool GetTab2() const { return m_bTab2; }
+public:
+ virtual ~SwMultiPortion() override;
+ const SwLineLayout& GetRoot() const { return m_aRoot; }
+ SwLineLayout& GetRoot() { return m_aRoot; }
+
+ bool HasTabulator() const { return m_bTab1 || m_bTab2; }
+ bool IsFormatted() const { return m_bFormatted; }
+ void SetFormatted() { m_bFormatted = true; }
+ bool IsFollowField() const { return m_bFollowField; }
+ void SetFollowField() { m_bFollowField = true; }
+ bool HasFlyInContent() const { return m_bFlyInContent; }
+ void SetFlyInContent( bool bNew ) { m_bFlyInContent = bNew; }
+ bool IsDouble() const { return m_bDouble; }
+ bool IsRuby() const { return m_bRuby; }
+ bool IsBidi() const { return m_bBidi; }
+ bool OnTop() const { return m_eRubyPosition == RubyPosition::ABOVE; }
+ bool OnRight() const { return m_eRubyPosition == RubyPosition::RIGHT; }
+ RubyPosition GetRubyPosition() const { return m_eRubyPosition; }
+ void ActualizeTabulator();
+
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ virtual bool ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const;
+
+ // Summarize the internal lines to calculate the (external) size
+ void CalcSize( SwTextFormatter& rLine, SwTextFormatInfo &rInf );
+
+ inline bool HasBrackets() const;
+ bool HasRotation() const { return 0 != (1 & m_nDirection); }
+ bool IsRevers() const { return 0 != (2 & m_nDirection); }
+ sal_uInt8 GetDirection() const { return m_nDirection; }
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+class SwDoubleLinePortion : public SwMultiPortion
+{
+ std::unique_ptr<SwBracket> m_pBracket; // Surrounding brackets
+ SwTwips m_nLineDiff; // Difference of the width of the both lines
+ TextFrameIndex m_nBlank1; ///< Number of blanks in the first line
+ TextFrameIndex m_nBlank2; ///< Number of blanks in the second line
+public:
+ SwDoubleLinePortion(SwDoubleLinePortion& rDouble, TextFrameIndex nEnd);
+ SwDoubleLinePortion(const SwMultiCreator& rCreate, TextFrameIndex nEnd);
+ virtual ~SwDoubleLinePortion() override;
+
+ SwBracket* GetBrackets() const { return m_pBracket.get(); }
+ void SetBrackets( const SwDoubleLinePortion& rDouble );
+ void PaintBracket( SwTextPaintInfo& rInf, tools::Long nSpaceAdd, bool bOpen ) const;
+ void FormatBrackets( SwTextFormatInfo &rInf, SwTwips& nMaxWidth );
+ sal_uInt16 PreWidth() const { return m_pBracket->nPreWidth; };
+ sal_uInt16 PostWidth() const { return m_pBracket->nPostWidth; }
+ void ClearBrackets()
+ { m_pBracket->nPreWidth = m_pBracket->nPostWidth=0; Width( 0 ); }
+ sal_uInt16 BracketWidth(){ return PreWidth() + PostWidth(); }
+
+ void CalcBlanks( SwTextFormatInfo &rInf );
+ static void ResetSpaceAdd( SwLineLayout* pCurr );
+ SwTwips GetLineDiff() const { return m_nLineDiff; }
+ TextFrameIndex GetSpaceCnt() const
+ { return ( m_nLineDiff < 0 ) ? m_nBlank2 : m_nBlank1; }
+ TextFrameIndex GetSmallerSpaceCnt() const
+ { return ( m_nLineDiff < 0 ) ? m_nBlank1 : m_nBlank2; }
+
+ virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ virtual bool ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const override;
+};
+
+class SwRubyPortion : public SwMultiPortion
+{
+ TextFrameIndex m_nRubyOffset;
+ css::text::RubyAdjust m_nAdjustment;
+ void Adjust_( SwTextFormatInfo &rInf);
+public:
+ SwRubyPortion(const SwRubyPortion& rRuby, TextFrameIndex nEnd);
+
+ SwRubyPortion( const SwMultiCreator& rCreate, const SwFont& rFnt,
+ const IDocumentSettingAccess& rIDocumentSettingAccess,
+ TextFrameIndex nEnd, TextFrameIndex nOffs,
+ const SwTextSizeInfo &rInf );
+
+ void CalcRubyOffset();
+ void Adjust( SwTextFormatInfo &rInf )
+ { if(m_nAdjustment != css::text::RubyAdjust_LEFT && GetRoot().GetNext()) Adjust_(rInf); }
+ css::text::RubyAdjust GetAdjustment() const { return m_nAdjustment; }
+ TextFrameIndex GetRubyOffset() const { return m_nRubyOffset; }
+};
+
+class SwRotatedPortion : public SwMultiPortion
+{
+public:
+ SwRotatedPortion(TextFrameIndex const nEnd, sal_uInt8 nDir)
+ : SwMultiPortion( nEnd ) { SetDirection( nDir ); }
+ SwRotatedPortion( const SwMultiCreator& rCreate, TextFrameIndex nEnd,
+ bool bRTL );
+};
+
+class SwBidiPortion : public SwMultiPortion
+{
+ sal_uInt8 m_nLevel;
+
+public:
+ SwBidiPortion(TextFrameIndex nEnd, sal_uInt8 nLv);
+
+ sal_uInt8 GetLevel() const { return m_nLevel; }
+ // Get number of blanks for justified alignment
+ TextFrameIndex GetSpaceCnt(const SwTextSizeInfo &rInf) const;
+ // Calculates extra spacing based on number of blanks
+ virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+ // Manipulate the spacing array at pCurr
+ virtual bool ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const override;
+};
+
+// For cursor travelling in multiportions
+
+class SwTextCursorSave
+{
+ SwTextCursor* pTextCursor;
+ SwLineLayout* pCurr;
+ TextFrameIndex nStart;
+ sal_uInt16 nWidth;
+ sal_uInt8 nOldProp;
+ bool bSpaceChg;
+public:
+ SwTextCursorSave( SwTextCursor* pTextCursor, SwMultiPortion* pMulti,
+ SwTwips nY, SwTwips& nX, TextFrameIndex nCurrStart, tools::Long nSpaceAdd);
+ ~SwTextCursorSave();
+};
+
+inline bool SwMultiPortion::HasBrackets() const
+{
+ return IsDouble() && nullptr != static_cast<const SwDoubleLinePortion*>(this)->GetBrackets();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porref.cxx b/sw/source/core/text/porref.cxx
new file mode 100644
index 000000000..502b681c0
--- /dev/null
+++ b/sw/source/core/text/porref.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwPortionHandler.hxx>
+#include <viewopt.hxx>
+
+#include "porref.hxx"
+#include "inftxt.hxx"
+
+void SwRefPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( Width() )
+ {
+ rInf.DrawViewOpt( *this, PortionType::Ref );
+ SwTextPortion::Paint( rInf );
+ }
+}
+
+SwLinePortion *SwIsoRefPortion::Compress() { return this; }
+
+SwIsoRefPortion::SwIsoRefPortion() : m_nViewWidth(0)
+{
+ SetLen(TextFrameIndex(1));
+ SetWhichPor( PortionType::IsoRef );
+}
+
+sal_uInt16 SwIsoRefPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+{
+ // Although we are const, nViewWidth should be calculated in the last
+ // moment possible
+ SwIsoRefPortion* pThis = const_cast<SwIsoRefPortion*>(this);
+ if( !Width() && rInf.OnWin() && SwViewOption::IsFieldShadings() &&
+ !rInf.GetOpt().IsReadonly() && !rInf.GetOpt().IsPagePreview() )
+ {
+ if( !m_nViewWidth )
+ pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
+ }
+ else
+ pThis->m_nViewWidth = 0;
+ return m_nViewWidth;
+}
+
+bool SwIsoRefPortion::Format( SwTextFormatInfo &rInf )
+{
+ return SwLinePortion::Format( rInf );
+}
+
+void SwIsoRefPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( Width() )
+ rInf.DrawViewOpt( *this, PortionType::Ref );
+}
+
+void SwIsoRefPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), OUString(), GetWhichPor() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porref.hxx b/sw/source/core/text/porref.hxx
new file mode 100644
index 000000000..d12803080
--- /dev/null
+++ b/sw/source/core/text/porref.hxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include "portxt.hxx"
+
+class SwRefPortion : public SwTextPortion
+{
+public:
+ SwRefPortion() { SetWhichPor(PortionType::Ref); }
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+};
+
+class SwIsoRefPortion : public SwRefPortion
+{
+ sal_uInt16 m_nViewWidth;
+
+public:
+ SwIsoRefPortion();
+ virtual bool Format(SwTextFormatInfo& rInf) override;
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+ virtual SwLinePortion* Compress() override;
+ virtual sal_uInt16 GetViewWidth(const SwTextSizeInfo& rInf) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion(SwPortionHandler& rPH) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx
new file mode 100644
index 000000000..271c986a8
--- /dev/null
+++ b/sw/source/core/text/porrst.cxx
@@ -0,0 +1,760 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/lspcitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <ndtxt.hxx>
+#include <pagefrm.hxx>
+#include <paratr.hxx>
+#include <SwPortionHandler.hxx>
+#include "porrst.hxx"
+#include "inftxt.hxx"
+#include "txtpaint.hxx"
+#include <swfntcch.hxx>
+#include <tgrditem.hxx>
+#include <pagedesc.hxx>
+#include <frmatr.hxx>
+#include "redlnitr.hxx"
+#include "atrhndl.hxx"
+#include <rootfrm.hxx>
+#include <formatlinebreak.hxx>
+#include <txatbase.hxx>
+
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+
+#include <crsrsh.hxx>
+
+SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion,
+ const FontLineStyle eUL,
+ const FontStrikeout eStrkout,
+ const Color& rCol ) :
+ m_eUnderline( eUL ), m_eStrikeout( eStrkout ), m_aColor( rCol )
+{
+ Height( rPortion.Height() );
+ SetAscent( rPortion.GetAscent() );
+ SetWhichPor( PortionType::TempEnd );
+}
+
+void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if (!(rInf.OnWin() && rInf.GetOpt().IsParagraph()))
+ return;
+
+ const SwFont* pOldFnt = rInf.GetFont();
+
+ SwFont aFont(*pOldFnt);
+
+ // Paint strikeout/underline based on redline color and settings
+ // (with an extra pilcrow in the background, because there is
+ // no SetStrikeoutColor(), also SetUnderColor() doesn't work()).
+ if ( m_eUnderline != LINESTYLE_NONE || m_eStrikeout != STRIKEOUT_NONE )
+ {
+ aFont.SetColor( m_aColor );
+ aFont.SetUnderline( m_eUnderline );
+ aFont.SetStrikeout( m_eStrikeout );
+
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
+
+ // draw the pilcrow with strikeout/underline in redline color
+ rInf.DrawText(CH_PAR, *this);
+
+ }
+
+ aFont.SetColor( NON_PRINTING_CHARACTER_COLOR );
+ aFont.SetStrikeout( STRIKEOUT_NONE );
+ aFont.SetUnderline( LINESTYLE_NONE );
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
+
+ // draw the pilcrow
+ rInf.DrawText(CH_PAR, *this);
+
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
+}
+
+SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion, const SwTextAttr* pAttr )
+ : SwLinePortion( rPortion )
+{
+ mnLineLength = TextFrameIndex(1);
+ m_eRedline = RedlineType::None;
+ SetWhichPor( PortionType::Break );
+
+ m_eClear = SwLineBreakClear::NONE;
+ if (pAttr && pAttr->Which() == RES_TXTATR_LINEBREAK)
+ {
+ m_eClear = pAttr->GetLineBreak().GetValue();
+ }
+ m_nTextHeight = 0;
+}
+
+TextFrameIndex SwBreakPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+{
+ return TextFrameIndex(0);
+}
+
+sal_uInt16 SwBreakPortion::GetViewWidth( const SwTextSizeInfo & ) const
+{ return 0; }
+
+SwLinePortion *SwBreakPortion::Compress()
+{ return (GetNextPortion() && GetNextPortion()->InTextGrp() ? nullptr : this); }
+
+void SwBreakPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( !(rInf.OnWin() && rInf.GetOpt().IsLineBreak()) )
+ return;
+
+ // Reduce height to text height for the duration of the print, so the vertical height will look
+ // correct for the line break character, even for clearing breaks.
+ SwTwips nHeight = Height();
+ SwTwips nVertPosOffset = (nHeight - m_nTextHeight) / 2;
+ auto pPortion = const_cast<SwBreakPortion*>(this);
+ pPortion->Height(m_nTextHeight, false);
+ if (rInf.GetTextFrame()->IsVertical())
+ {
+ // Compensate for the offset done in SwTextCursor::AdjustBaseLine() for the vertical case.
+ const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() + nVertPosOffset);
+ }
+ comphelper::ScopeGuard g(
+ [pPortion, nHeight, &rInf, nVertPosOffset]
+ {
+ if (rInf.GetTextFrame()->IsVertical())
+ {
+ const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() - nVertPosOffset);
+ }
+ pPortion->Height(nHeight, false);
+ });
+
+ rInf.DrawLineBreak( *this );
+
+ // paint redlining
+ if (m_eRedline == RedlineType::None)
+ return;
+
+ sal_Int16 nNoBreakWidth = rInf.GetTextSize(S_NOBREAK_FOR_REDLINE).Width();
+ if ( nNoBreakWidth > 0 )
+ {
+ // approximate portion size with multiple no-break spaces
+ // and draw these spaces (at least a single one) by DrawText
+ // painting the requested redline underline/strikeout
+ sal_Int16 nSpaces = (LINE_BREAK_WIDTH + nNoBreakWidth/2) / nNoBreakWidth;
+ OUStringBuffer aBuf(S_NOBREAK_FOR_REDLINE);
+ for (sal_Int16 i = 1; i < nSpaces; ++i)
+ aBuf.append(S_NOBREAK_FOR_REDLINE);
+
+ const SwFont* pOldFnt = rInf.GetFont();
+
+ SwFont aFont(*pOldFnt);
+
+ if (m_eRedline == RedlineType::Delete)
+ aFont.SetUnderline( LINESTYLE_NONE );
+ else
+ aFont.SetStrikeout( STRIKEOUT_NONE );
+
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
+
+ rInf.DrawText(aBuf.makeStringAndClear(), *this);
+
+ const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
+ }
+}
+
+bool SwBreakPortion::Format( SwTextFormatInfo &rInf )
+{
+ const SwLinePortion *pRoot = rInf.GetRoot();
+ Width( 0 );
+ Height( pRoot->Height() );
+ m_nTextHeight = Height();
+
+ // See if this is a clearing break. If so, calculate how much we need to "jump down" so the next
+ // line can again use the full text width.
+ SwLineBreakClear eClear = m_eClear;
+ if (rInf.GetTextFrame()->IsRightToLeft() && eClear != SwLineBreakClear::ALL)
+ {
+ // RTL ignores left/right breaks.
+ eClear = SwLineBreakClear::NONE;
+ }
+ if (eClear != SwLineBreakClear::NONE)
+ {
+ SwTextFly& rTextFly = rInf.GetTextFly();
+ if (rTextFly.IsOn())
+ {
+ SwTwips nHeight = rTextFly.GetMaxBottom(*this, rInf) - rInf.Y();
+ if (nHeight > Height())
+ {
+ Height(nHeight, /*bText=*/false);
+ }
+ }
+ }
+
+ SetAscent( pRoot->GetAscent() );
+ if (rInf.GetIdx() + TextFrameIndex(1) == TextFrameIndex(rInf.GetText().getLength()))
+ rInf.SetNewLine( true );
+ return true;
+}
+
+void SwBreakPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Text( GetLen(), GetWhichPor() );
+}
+
+void SwBreakPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex&
+ nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBreakPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("text-height"),
+ BAD_CAST(OString::number(m_nTextHeight).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwLineBreakClear SwBreakPortion::GetClear() const { return m_eClear; }
+
+SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn,
+ bool bBG, bool bGK ) :
+ m_nKern( nKrn ), m_bBackground( bBG ), m_bGridKern( bGK )
+{
+ Height( rPortion.Height() );
+ SetAscent( rPortion.GetAscent() );
+ mnLineLength = TextFrameIndex(0);
+ SetWhichPor( PortionType::Kern );
+ if( m_nKern > 0 )
+ Width( m_nKern );
+ rPortion.Insert( this );
+}
+
+SwKernPortion::SwKernPortion( const SwLinePortion& rPortion ) :
+ m_nKern( 0 ), m_bBackground( false ), m_bGridKern( true )
+{
+ Height( rPortion.Height() );
+ SetAscent( rPortion.GetAscent() );
+
+ mnLineLength = TextFrameIndex(0);
+ SetWhichPor( PortionType::Kern );
+}
+
+void SwKernPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( !Width() )
+ return;
+
+ // bBackground is set for Kerning Portions between two fields
+ if ( m_bBackground )
+ rInf.DrawViewOpt( *this, PortionType::Field );
+
+ rInf.DrawBackBrush( *this );
+ if (GetJoinBorderWithNext() ||GetJoinBorderWithPrev())
+ rInf.DrawBorder( *this );
+
+ // do we have to repaint a post it portion?
+ if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
+ mpNextPortion->PrePaint( rInf, this );
+
+ if( rInf.GetFont()->IsPaintBlank() )
+ {
+ SwRect aClipRect;
+ rInf.CalcRect( *this, &aClipRect );
+ SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
+ aClip.ChgClip( aClipRect );
+ rInf.DrawText(" ", *this, TextFrameIndex(0), TextFrameIndex(2), true );
+ }
+}
+
+void SwKernPortion::FormatEOL( SwTextFormatInfo &rInf )
+{
+ if ( m_bGridKern )
+ return;
+
+ if( rInf.GetLast() == this )
+ rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
+ if( m_nKern < 0 )
+ Width( -m_nKern );
+ else
+ Width( 0 );
+ rInf.GetLast()->FormatEOL( rInf );
+}
+
+SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) :
+ m_bLeft( true )
+{
+ Height( rPortion.Height() );
+ SetAscent( rPortion.GetAscent() );
+ mnLineLength = TextFrameIndex(0);
+ SetWhichPor( PortionType::Arrow );
+}
+
+SwArrowPortion::SwArrowPortion( const SwTextPaintInfo &rInf )
+ : m_bLeft( false )
+{
+ Height( o3tl::narrowing<sal_uInt16>(rInf.GetTextFrame()->getFramePrintArea().Height()) );
+ m_aPos.setX( rInf.GetTextFrame()->getFrameArea().Left() +
+ rInf.GetTextFrame()->getFramePrintArea().Right() );
+ m_aPos.setY( rInf.GetTextFrame()->getFrameArea().Top() +
+ rInf.GetTextFrame()->getFramePrintArea().Bottom() );
+ SetWhichPor( PortionType::Arrow );
+}
+
+void SwArrowPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ const_cast<SwArrowPortion*>(this)->m_aPos = rInf.GetPos();
+}
+
+SwLinePortion *SwArrowPortion::Compress() { return this; }
+
+SwTwips SwTextFrame::EmptyHeight() const
+{
+ if (IsCollapse()) {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( auto pCrSh = dynamic_cast<SwCursorShell*>( pSh ) ) {
+ // this is called during formatting so avoid recursive layout
+ SwContentFrame const*const pCurrFrame = pCrSh->GetCurrFrame(false);
+ if (pCurrFrame==static_cast<SwContentFrame const *>(this)) {
+ // do nothing
+ } else {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ }
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::EmptyHeight with swapped frame" );
+
+ std::unique_ptr<SwFont> pFnt;
+ const SwTextNode& rTextNode = *GetTextNodeForParaProps();
+ const IDocumentSettingAccess* pIDSA = rTextNode.getIDocumentSettingAccess();
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( rTextNode.HasSwAttrSet() )
+ {
+ const SwAttrSet *pAttrSet = &( rTextNode.GetSwAttrSet() );
+ pFnt.reset(new SwFont( pAttrSet, pIDSA ));
+ }
+ else
+ {
+ SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), pSh);
+ pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
+ pFnt->CheckFontCacheId( pSh, pFnt->GetActual() );
+ }
+
+ if ( IsVertical() )
+ pFnt->SetVertical( 2700_deg10 );
+
+ OutputDevice* pOut = pSh ? pSh->GetOut() : nullptr;
+ if ( !pOut || !pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsPrtFormat() )
+ {
+ pOut = rTextNode.getIDocumentDeviceAccess().getReferenceDevice(true);
+ }
+
+ const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess();
+ if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
+ && !getRootFrame()->IsHideRedlines())
+ {
+ const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ SwAttrHandler aAttrHandler;
+ aAttrHandler.Init(rTextNode.GetSwAttrSet(),
+ *rTextNode.getIDocumentSettingAccess());
+ SwRedlineItr aRedln( rTextNode, *pFnt, aAttrHandler,
+ nRedlPos, SwRedlineItr::Mode::Show);
+ }
+ }
+
+ SwTwips nRet;
+ if( !pOut )
+ nRet = IsVertical() ?
+ getFramePrintArea().SSize().Width() + 1 :
+ getFramePrintArea().SSize().Height() + 1;
+ else
+ {
+ pFnt->SetFntChg( true );
+ pFnt->ChgPhysFnt( pSh, *pOut );
+ nRet = pFnt->GetHeight( pSh, *pOut );
+ }
+ return nRet;
+}
+
+bool SwTextFrame::FormatEmpty()
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::FormatEmpty with swapped frame" );
+
+ bool bCollapse = EmptyHeight( ) == 1 && IsCollapse( );
+
+ // sw_redlinehide: just disable FormatEmpty optimisation for now
+ if (HasFollow() || GetMergedPara() || GetTextNodeFirst()->GetpSwpHints() ||
+ nullptr != GetTextNodeForParaProps()->GetNumRule() ||
+ GetTextNodeFirst()->HasHiddenCharAttribute(true) ||
+ IsInFootnote() || ( HasPara() && GetPara()->IsPrepMustFit() ) )
+ return false;
+ const SwAttrSet& aSet = GetTextNodeForParaProps()->GetSwAttrSet();
+ const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust();
+ if( !bCollapse && ( ( ( ! IsRightToLeft() && ( SvxAdjust::Left != nAdjust ) ) ||
+ ( IsRightToLeft() && ( SvxAdjust::Right != nAdjust ) ) ) ||
+ aSet.GetRegister().GetValue() ) )
+ return false;
+ const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing();
+ if( !bCollapse && ( SvxLineSpaceRule::Min == rSpacing.GetLineSpaceRule() ||
+ SvxLineSpaceRule::Fix == rSpacing.GetLineSpaceRule() ||
+ aSet.GetLRSpace().IsAutoFirst() ) )
+ return false;
+
+ SwTextFly aTextFly( this );
+ SwRect aRect;
+ bool bFirstFlyCheck = 0 != getFramePrintArea().Height();
+ if ( !bCollapse && bFirstFlyCheck &&
+ aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
+ return false;
+
+ // only need to check one node because of early return on GetMerged()
+ for (SwIndex const* pIndex = GetTextNodeFirst()->GetFirstIndex();
+ pIndex; pIndex = pIndex->GetNext())
+ {
+ sw::mark::IMark const*const pMark = pIndex->GetMark();
+ if (dynamic_cast<const sw::mark::IBookmark*>(pMark) != nullptr)
+ { // need bookmark portions!
+ return false;
+ }
+ }
+
+ SwTwips nHeight = EmptyHeight();
+
+ if (aSet.GetParaGrid().GetValue() &&
+ IsInDocBody() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
+ if ( pGrid )
+ nHeight = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
+ }
+
+ SwRectFnSet aRectFnSet(this);
+ const SwTwips nChg = nHeight - aRectFnSet.GetHeight(getFramePrintArea());
+
+ if( !nChg )
+ SetUndersized( false );
+ AdjustFrame( nChg );
+
+ if (GetHasRotatedPortions())
+ {
+ ClearPara();
+ SetHasRotatedPortions(false);
+ }
+
+ RemoveFromCache();
+ if( !IsEmpty() )
+ {
+ SetEmpty( true );
+ SetCompletePaint();
+ }
+ if( !bCollapse && !bFirstFlyCheck &&
+ aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
+ return false;
+
+ // #i35635# - call method <HideAndShowObjects()>
+ // to assure that objects anchored at the empty paragraph are
+ // correctly visible resp. invisible.
+ HideAndShowObjects();
+ return true;
+}
+
+bool SwTextFrame::FillRegister( SwTwips& rRegStart, sal_uInt16& rRegDiff )
+{
+ const SwFrame *pFrame = this;
+ rRegDiff = 0;
+ while( !( ( SwFrameType::Body | SwFrameType::Fly )
+ & pFrame->GetType() ) && pFrame->GetUpper() )
+ pFrame = pFrame->GetUpper();
+ if( ( SwFrameType::Body| SwFrameType::Fly ) & pFrame->GetType() )
+ {
+ SwRectFnSet aRectFnSet(pFrame);
+ rRegStart = aRectFnSet.GetPrtTop(*pFrame);
+ pFrame = pFrame->FindPageFrame();
+ if( pFrame->IsPageFrame() )
+ {
+ SwPageDesc* pDesc = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pFrame))->FindPageDesc();
+ if( pDesc )
+ {
+ rRegDiff = pDesc->GetRegHeight();
+ if( !rRegDiff )
+ {
+ const SwTextFormatColl *pFormat = pDesc->GetRegisterFormatColl();
+ if( pFormat )
+ {
+ const SvxLineSpacingItem &rSpace = pFormat->GetLineSpacing();
+ if( SvxLineSpaceRule::Fix == rSpace.GetLineSpaceRule() )
+ {
+ rRegDiff = rSpace.GetLineHeight();
+ pDesc->SetRegHeight( rRegDiff );
+ pDesc->SetRegAscent( ( 4 * rRegDiff ) / 5 );
+ }
+ else
+ {
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ SwFontAccess aFontAccess( pFormat, pSh );
+ SwFont aFnt( aFontAccess.Get()->GetFont() );
+
+ OutputDevice *pOut = nullptr;
+ if( !pSh || !pSh->GetViewOptions()->getBrowseMode() ||
+ pSh->GetViewOptions()->IsPrtFormat() )
+ pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
+
+ if( pSh && !pOut )
+ pOut = pSh->GetWin()->GetOutDev();
+
+ if( !pOut )
+ pOut = Application::GetDefaultDevice();
+
+ MapMode aOldMap( pOut->GetMapMode() );
+ pOut->SetMapMode( MapMode( MapUnit::MapTwip ) );
+
+ aFnt.ChgFnt( pSh, *pOut );
+ rRegDiff = aFnt.GetHeight( pSh, *pOut );
+ sal_uInt16 nNetHeight = rRegDiff;
+
+ switch( rSpace.GetLineSpaceRule() )
+ {
+ case SvxLineSpaceRule::Auto:
+ break;
+ case SvxLineSpaceRule::Min:
+ {
+ if( rRegDiff < rSpace.GetLineHeight() )
+ rRegDiff = rSpace.GetLineHeight();
+ break;
+ }
+ default:
+ OSL_FAIL( ": unknown LineSpaceRule" );
+ }
+ switch( rSpace.GetInterLineSpaceRule() )
+ {
+ case SvxInterLineSpaceRule::Off:
+ break;
+ case SvxInterLineSpaceRule::Prop:
+ {
+ tools::Long nTmp = rSpace.GetPropLineSpace();
+ if( nTmp < 50 )
+ nTmp = nTmp ? 50 : 100;
+ nTmp *= rRegDiff;
+ nTmp /= 100;
+ if( !nTmp )
+ ++nTmp;
+ rRegDiff = o3tl::narrowing<sal_uInt16>(nTmp);
+ nNetHeight = rRegDiff;
+ break;
+ }
+ case SvxInterLineSpaceRule::Fix:
+ {
+ rRegDiff = rRegDiff + rSpace.GetInterLineSpace();
+ nNetHeight = rRegDiff;
+ break;
+ }
+ default: OSL_FAIL( ": unknown InterLineSpaceRule" );
+ }
+ pDesc->SetRegHeight( rRegDiff );
+ pDesc->SetRegAscent( rRegDiff - nNetHeight +
+ aFnt.GetAscent( pSh, *pOut ) );
+ pOut->SetMapMode( aOldMap );
+ }
+ }
+ }
+ const tools::Long nTmpDiff = pDesc->GetRegAscent() - rRegDiff;
+ if ( aRectFnSet.IsVert() )
+ rRegStart -= nTmpDiff;
+ else
+ rRegStart += nTmpDiff;
+ }
+ }
+ }
+ return ( 0 != rRegDiff );
+}
+
+void SwHiddenTextPortion::Paint( const SwTextPaintInfo & rInf) const
+{
+#ifdef DBG_UTIL
+ OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
+ Color aCol( SwViewOption::GetFieldShadingsColor() );
+ Color aOldColor( pOut->GetFillColor() );
+ pOut->SetFillColor( aCol );
+ Point aPos( rInf.GetPos() );
+ aPos.AdjustY( -150 );
+ aPos.AdjustX( -25 );
+ SwRect aRect( aPos, Size( 100, 200 ) );
+ pOut->DrawRect( aRect.SVRect() );
+ pOut->SetFillColor( aOldColor );
+#else
+ (void)rInf;
+#endif
+}
+
+bool SwHiddenTextPortion::Format( SwTextFormatInfo &rInf )
+{
+ Width( 0 );
+ rInf.GetTextFrame()->HideFootnotes( rInf.GetIdx(), rInf.GetIdx() + GetLen() );
+
+ return false;
+};
+
+bool SwControlCharPortion::DoPaint(SwTextPaintInfo const&,
+ OUString & rOutString, SwFont & rTmpFont, int &) const
+{
+ if (mcChar == CHAR_WJ || !SwViewOption::IsFieldShadings())
+ {
+ return false;
+ }
+
+ switch (mcChar)
+ {
+ case CHAR_ZWSP:
+ rOutString = "/"; break;
+// case CHAR_LRM :
+// rText = sal_Unicode(0x2514); break;
+// case CHAR_RLM :
+// rText = sal_Unicode(0x2518); break;
+ default:
+ assert(false);
+ break;
+ }
+
+ rTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 );
+ const sal_uInt16 nProp = 40;
+ rTmpFont.SetProportion( nProp ); // a smaller font
+
+ return true;
+}
+
+bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
+ OUString & rOutString, SwFont & rFont, int & rDeltaY) const
+{
+ if (!rTextPaintInfo.GetOpt().IsShowBookmarks())
+ {
+ return false;
+ }
+
+ rOutString = OUStringChar(mcChar);
+
+ // init font: we want OpenSymbol to ensure it doesn't look too crazy;
+ // thin and a bit higher than the surrounding text
+ auto const nOrigAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
+ rFont.SetName("OpenSymbol", rFont.GetActual());
+ Size aSize(rFont.GetSize(rFont.GetActual()));
+ // use also the external leading (line gap) of the portion, but don't use
+ // 100% of it because i can't figure out how to baseline align that
+ assert(aSize.Height() != 0);
+ auto const nFactor = aSize.Height() > 0 ? (Height() * 95) / aSize.Height() : Height();
+ rFont.SetProportion(nFactor);
+ rFont.SetWeight(WEIGHT_THIN, rFont.GetActual());
+ rFont.SetColor(SwViewOption::GetFieldShadingsColor());
+ // reset these to default...
+ rFont.SetAlign(ALIGN_BASELINE);
+ rFont.SetUnderline(LINESTYLE_NONE);
+ rFont.SetOverline(LINESTYLE_NONE);
+ rFont.SetStrikeout(STRIKEOUT_NONE);
+ rFont.SetOutline(false);
+ rFont.SetShadow(false);
+ rFont.SetTransparent(false);
+ rFont.SetEmphasisMark(FontEmphasisMark::NONE);
+ rFont.SetEscapement(0);
+ rFont.SetPitch(PITCH_DONTKNOW, rFont.GetActual());
+ rFont.SetRelief(FontRelief::NONE);
+
+ // adjust Y position to account for different baselines of the fonts
+ auto const nOSAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
+ rDeltaY = nOSAscent - nOrigAscent;
+
+ return true;
+}
+
+void SwControlCharPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if ( !Width() ) // is only set during prepaint mode
+ return;
+
+ rInf.DrawViewOpt(*this, GetWhichPor());
+
+ int deltaY(0);
+ SwFont aTmpFont( *rInf.GetFont() );
+ OUString aOutString;
+
+ if (!(rInf.OnWin()
+ && !rInf.GetOpt().IsPagePreview()
+ && !rInf.GetOpt().IsReadonly()
+ && DoPaint(rInf, aOutString, aTmpFont, deltaY)))
+ return;
+
+ SwFontSave aFontSave( rInf, &aTmpFont );
+
+ if ( !mnHalfCharWidth )
+ mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2;
+
+ Point aOldPos = rInf.GetPos();
+ Point aNewPos( aOldPos );
+ auto const deltaX((Width() / 2) - mnHalfCharWidth);
+ switch (rInf.GetFont()->GetOrientation(rInf.GetTextFrame()->IsVertical()).get())
+ {
+ case 0:
+ aNewPos.AdjustX(deltaX);
+ aNewPos.AdjustY(deltaY);
+ break;
+ case 900:
+ aNewPos.AdjustY(-deltaX);
+ aNewPos.AdjustX(deltaY);
+ break;
+ case 2700:
+ aNewPos.AdjustY(deltaX);
+ aNewPos.AdjustX(-deltaY);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+
+ rInf.DrawText( aOutString, *this );
+
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
+}
+
+bool SwControlCharPortion::Format( SwTextFormatInfo &rInf )
+{
+ const SwLinePortion* pRoot = rInf.GetRoot();
+ Width( 0 );
+ Height( pRoot->Height() );
+ SetAscent( pRoot->GetAscent() );
+
+ return false;
+}
+
+sal_uInt16 SwControlCharPortion::GetViewWidth( const SwTextSizeInfo& rInf ) const
+{
+ if( !mnViewWidth )
+ mnViewWidth = rInf.GetTextSize(OUString(' ')).Width();
+
+ return mnViewWidth;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/porrst.hxx b/sw/source/core/text/porrst.hxx
new file mode 100644
index 000000000..66552eea3
--- /dev/null
+++ b/sw/source/core/text/porrst.hxx
@@ -0,0 +1,199 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <tools/gen.hxx>
+
+#include <TextFrameIndex.hxx>
+#include <txttypes.hxx>
+#include <txtfrm.hxx>
+#include <svx/ctredlin.hxx>
+
+#include "porlin.hxx"
+#include "portxt.hxx"
+#include "possiz.hxx"
+
+class SwPortionHandler;
+class SwTextPaintInfo;
+class SwTextSizeInfo;
+class SwFont;
+
+#define LINE_BREAK_WIDTH 150
+#define SPECIAL_FONT_HEIGHT 200
+
+class SwTextFormatInfo;
+
+class SwTmpEndPortion : public SwLinePortion
+{
+ const FontLineStyle m_eUnderline;
+ const FontStrikeout m_eStrikeout;
+ Color m_aColor;
+
+public:
+ explicit SwTmpEndPortion( const SwLinePortion &rPortion,
+ const FontLineStyle eUnderline,
+ const FontStrikeout eStrikeout,
+ const Color& rColor );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+};
+
+enum class SwLineBreakClear;
+
+class SwBreakPortion : public SwLinePortion
+{
+ RedlineType m_eRedline;
+
+ /// Tracks the type of the breaking clear from SwTextLineBreak, if there is one.
+ SwLineBreakClear m_eClear;
+
+ /// Height of the line-break character itself, without spacing added for clearing.
+ SwTwips m_nTextHeight;
+
+public:
+ explicit SwBreakPortion(const SwLinePortion& rPortion, const SwTextAttr* pAttr);
+ // Returns 0 if we have no usable data
+ virtual SwLinePortion *Compress() override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo &rInf ) const override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+
+ static constexpr OUStringLiteral S_NOBREAK_FOR_REDLINE = u"\u00A0";
+ void SetRedline( const RedlineType eRedline ) { m_eRedline = eRedline; }
+
+ SwLineBreakClear GetClear() const;
+};
+
+class SwKernPortion : public SwLinePortion
+{
+ short m_nKern;
+ bool m_bBackground;
+ bool m_bGridKern;
+
+public:
+
+ // This constructor automatically appends the portion to rPortion
+ // bBG indicates, that the background of the kerning portion has to
+ // be painted, e.g., if the portion if positioned between to fields.
+ // bGridKern indicates, that the kerning portion is used to provide
+ // additional space in grid mode.
+ SwKernPortion( SwLinePortion &rPortion, short nKrn,
+ bool bBG = false, bool bGridKern = false );
+
+ // This constructor only sets the height and ascent to the values
+ // of rPortion. It is only used for kerning portions for grid mode
+ explicit SwKernPortion( const SwLinePortion &rPortion );
+
+ virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+};
+
+/// Indicator that the content does not fit into a fixed height frame (red triangle on the UI).
+class SwArrowPortion : public SwLinePortion
+{
+ Point m_aPos;
+ bool m_bLeft;
+public:
+ explicit SwArrowPortion( const SwLinePortion &rPortion );
+ explicit SwArrowPortion( const SwTextPaintInfo &rInf );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual SwLinePortion *Compress() override;
+ bool IsLeft() const { return m_bLeft; }
+ const Point& GetPos() const { return m_aPos; }
+};
+
+// The characters which are forbidden at the start of a line like the dot and
+// other punctuation marks are allowed to display in the margin of the page
+// by a user option.
+// The SwHangingPortion is the corresponding textportion to do that.
+class SwHangingPortion : public SwTextPortion
+{
+ sal_uInt16 m_nInnerWidth;
+public:
+ explicit SwHangingPortion( SwPosSize aSize ) : m_nInnerWidth( aSize.Width() )
+ {
+ SetWhichPor( PortionType::Hanging );
+ SetLen(TextFrameIndex(1));
+ Height( aSize.Height() );
+ }
+
+ sal_uInt16 GetInnerWidth() const { return m_nInnerWidth; }
+};
+
+// Used to hide text
+class SwHiddenTextPortion : public SwLinePortion
+{
+public:
+ explicit SwHiddenTextPortion(TextFrameIndex const nLen)
+ {
+ SetWhichPor( PortionType::HiddenText ); SetLen( nLen );
+ }
+
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+};
+
+class SwControlCharPortion : public SwLinePortion
+{
+
+private:
+ mutable sal_uInt16 mnViewWidth; // used to cache a calculated value
+ mutable sal_uInt16 mnHalfCharWidth; // used to cache a calculated value
+protected:
+ sal_Unicode mcChar;
+
+public:
+
+ explicit SwControlCharPortion( sal_Unicode cChar )
+ : mnViewWidth( 0 ), mnHalfCharWidth( 0 ), mcChar( cChar )
+ {
+ SetWhichPor( PortionType::ControlChar ); SetLen( TextFrameIndex(1) );
+ }
+
+ virtual bool DoPaint(SwTextPaintInfo const& rInf,
+ OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual sal_uInt16 GetViewWidth( const SwTextSizeInfo& rInf ) const override;
+};
+
+/// for showing bookmark starts and ends; note that in contrast to
+/// SwControlCharPortion these do not have a character in the text.
+class SwBookmarkPortion : public SwControlCharPortion
+{
+public:
+ explicit SwBookmarkPortion(sal_Unicode const cChar)
+ : SwControlCharPortion(cChar)
+ {
+ SetWhichPor(PortionType::Bookmark);
+ SetLen(TextFrameIndex(0));
+ }
+
+ virtual bool DoPaint(SwTextPaintInfo const& rInf,
+ OUString & rOutString, SwFont & rTmpFont, int & rDeltaY) const override;
+ virtual SwLinePortion * Compress() override { return this; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/portab.hxx b/sw/source/core/text/portab.hxx
new file mode 100644
index 000000000..9ffe7be50
--- /dev/null
+++ b/sw/source/core/text/portab.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include "porglue.hxx"
+
+class SwTabPortion : public SwFixPortion
+{
+ const sal_uInt16 m_nTabPos;
+ const sal_Unicode m_cFill;
+ const bool m_bAutoTabStop;
+
+ // Format() branches either into PreFormat() or PostFormat()
+ bool PreFormat( SwTextFormatInfo &rInf );
+public:
+ SwTabPortion( const sal_uInt16 nTabPos, const sal_Unicode cFill, const bool bAutoTab = true );
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
+ bool PostFormat( SwTextFormatInfo &rInf );
+ bool IsFilled() const { return 0 != m_cFill; }
+ sal_uInt16 GetTabPos() const { return m_nTabPos; }
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+};
+
+class SwTabLeftPortion : public SwTabPortion
+{
+public:
+ SwTabLeftPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cFillChar, bool bAutoTab )
+ : SwTabPortion( nTabPosVal, cFillChar, bAutoTab )
+ { SetWhichPor( PortionType::TabLeft ); }
+};
+
+class SwTabRightPortion : public SwTabPortion
+{
+public:
+ SwTabRightPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cFillChar )
+ : SwTabPortion( nTabPosVal, cFillChar )
+ { SetWhichPor( PortionType::TabRight ); }
+};
+
+class SwTabCenterPortion : public SwTabPortion
+{
+public:
+ SwTabCenterPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cFillChar )
+ : SwTabPortion( nTabPosVal, cFillChar )
+ { SetWhichPor( PortionType::TabCenter ); }
+};
+
+class SwTabDecimalPortion : public SwTabPortion
+{
+ const sal_Unicode mcTab;
+
+ /*
+ * During text formatting, we already store the width of the portions
+ * following the tab stop up to the decimal position. This value is
+ * evaluated during pLastTab->FormatEOL. FME 2006-01-06 #127428#.
+ */
+ sal_uInt16 mnWidthOfPortionsUpTpDecimalPosition;
+
+public:
+ SwTabDecimalPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cTab,
+ const sal_Unicode cFillChar )
+ : SwTabPortion( nTabPosVal, cFillChar ),
+ mcTab(cTab),
+ mnWidthOfPortionsUpTpDecimalPosition( USHRT_MAX )
+ { SetWhichPor( PortionType::TabDecimal ); }
+
+ sal_Unicode GetTabDecimal() const { return mcTab; }
+
+ void SetWidthOfPortionsUpToDecimalPosition( sal_uInt16 nNew )
+ {
+ mnWidthOfPortionsUpTpDecimalPosition = nNew;
+ }
+ sal_uInt16 GetWidthOfPortionsUpToDecimalPosition() const
+ {
+ return mnWidthOfPortionsUpTpDecimalPosition;
+ }
+};
+
+class SwAutoTabDecimalPortion : public SwTabDecimalPortion
+{
+public:
+ SwAutoTabDecimalPortion( const sal_uInt16 nTabPosVal, const sal_Unicode cTab,
+ const sal_Unicode cFillChar )
+ : SwTabDecimalPortion( nTabPosVal, cTab, cFillChar )
+ {
+ SetLen(TextFrameIndex(0));
+ }
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/portox.cxx b/sw/source/core/text/portox.cxx
new file mode 100644
index 000000000..982ec4f5f
--- /dev/null
+++ b/sw/source/core/text/portox.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwPortionHandler.hxx>
+#include <viewopt.hxx>
+
+#include "portox.hxx"
+#include "inftxt.hxx"
+
+void SwToxPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( Width() )
+ {
+ rInf.DrawViewOpt( *this, PortionType::Tox );
+ SwTextPortion::Paint( rInf );
+ }
+}
+
+SwLinePortion *SwIsoToxPortion::Compress() { return this; }
+
+SwIsoToxPortion::SwIsoToxPortion() : m_nViewWidth(0)
+{
+ SetLen(TextFrameIndex(1));
+ SetWhichPor( PortionType::IsoTox );
+}
+
+sal_uInt16 SwIsoToxPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+{
+ // Although we are const, nViewWidth should be calculated in the last
+ // moment possible
+ SwIsoToxPortion* pThis = const_cast<SwIsoToxPortion*>(this);
+ // nViewWidth need to be calculated
+ if( !Width() && rInf.OnWin() &&
+ !rInf.GetOpt().IsPagePreview() &&
+ !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
+ {
+ if( !m_nViewWidth )
+ pThis->m_nViewWidth = rInf.GetTextSize(OUString(' ')).Width();
+ }
+ else
+ pThis->m_nViewWidth = 0;
+ return m_nViewWidth;
+}
+
+bool SwIsoToxPortion::Format( SwTextFormatInfo &rInf )
+{
+ return SwLinePortion::Format( rInf );
+}
+
+void SwIsoToxPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( Width() )
+ rInf.DrawViewOpt( *this, PortionType::Tox );
+}
+
+void SwIsoToxPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), OUString(), GetWhichPor() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/portox.hxx b/sw/source/core/text/portox.hxx
new file mode 100644
index 000000000..07d7c44fb
--- /dev/null
+++ b/sw/source/core/text/portox.hxx
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include "portxt.hxx"
+
+class SwToxPortion : public SwTextPortion
+{
+public:
+ SwToxPortion() { SetWhichPor(PortionType::Tox); }
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+};
+
+class SwIsoToxPortion : public SwToxPortion
+{
+ sal_uInt16 m_nViewWidth;
+
+public:
+ SwIsoToxPortion();
+ virtual bool Format(SwTextFormatInfo& rInf) override;
+ virtual void Paint(const SwTextPaintInfo& rInf) const override;
+ virtual SwLinePortion* Compress() override;
+ virtual sal_uInt16 GetViewWidth(const SwTextSizeInfo& rInf) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion(SwPortionHandler& rPH) const override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
new file mode 100644
index 000000000..a5ae0ea7e
--- /dev/null
+++ b/sw/source/core/text/portxt.cxx
@@ -0,0 +1,868 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <i18nlangtag/mslangid.hxx>
+#include <breakit.hxx>
+#include <hintids.hxx>
+#include <EnhancedPDFExportHelper.hxx>
+#include <SwPortionHandler.hxx>
+#include "porlay.hxx"
+#include "inftxt.hxx"
+#include "guess.hxx"
+#include "porfld.hxx"
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+
+#include <IMark.hxx>
+#include <pam.hxx>
+#include <doc.hxx>
+#include <xmloff/odffields.hxx>
+#include <viewopt.hxx>
+
+using namespace ::sw::mark;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n::ScriptType;
+
+// Returns for how many characters an extra space has to be added
+// (for justified alignment).
+static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo &rInf,
+ const OUString* pStr, const SwLinePortion& rPor)
+{
+ TextFrameIndex nPos, nEnd;
+ const SwScriptInfo* pSI = nullptr;
+
+ if ( pStr )
+ {
+ // passing a string means we are inside a field
+ nPos = TextFrameIndex(0);
+ nEnd = TextFrameIndex(pStr->getLength());
+ }
+ else
+ {
+ nPos = rInf.GetIdx();
+ nEnd = rInf.GetIdx() + rPor.GetLen();
+ pStr = &rInf.GetText();
+ pSI = &const_cast<SwParaPortion*>(rInf.GetParaPortion())->GetScriptInfo();
+ }
+
+ TextFrameIndex nCnt(0);
+ sal_uInt8 nScript = 0;
+
+ // If portion consists of Asian characters and language is not
+ // Korean, we add extra space to each character.
+ // first we get the script type
+ if ( pSI )
+ nScript = pSI->ScriptType( nPos );
+ else
+ nScript = static_cast<sal_uInt8>(
+ g_pBreakIt->GetBreakIter()->getScriptType(*pStr, sal_Int32(nPos)));
+
+ // Note: rInf.GetIdx() can differ from nPos,
+ // e.g., when rPor is a field portion. nPos refers to the string passed
+ // to the function, rInf.GetIdx() refers to the original string.
+
+ // We try to find out which justification mode is required. This is done by
+ // evaluating the script type and the language attribute set for this portion
+
+ // Asian Justification: Each character get some extra space
+ if ( nEnd > nPos && ASIAN == nScript )
+ {
+ LanguageType aLang =
+ rInf.GetTextFrame()->GetLangOfChar(rInf.GetIdx(), nScript);
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ const SwLinePortion* pPor = rPor.GetNextPortion();
+ if ( pPor && ( pPor->IsKernPortion() ||
+ pPor->IsControlCharPortion() ||
+ pPor->IsPostItsPortion() ) )
+ pPor = pPor->GetNextPortion();
+
+ nCnt += SwScriptInfo::CountCJKCharacters( *pStr, nPos, nEnd, aLang );
+
+ if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ||
+ pPor->IsBreakPortion() )
+ --nCnt;
+
+ return nCnt;
+ }
+ }
+
+ // Kashida Justification: Insert Kashidas
+ if ( nEnd > nPos && pSI && COMPLEX == nScript )
+ {
+ if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() )
+ {
+ const sal_Int32 nKashRes = pSI->KashidaJustify(nullptr, nPos, nEnd - nPos);
+ // i60591: need to check result of KashidaJustify
+ // determine if kashida justification is applicable
+ if (nKashRes != -1)
+ return TextFrameIndex(nKashRes);
+ }
+ }
+
+ // Thai Justification: Each character cell gets some extra space
+ if ( nEnd > nPos && COMPLEX == nScript )
+ {
+ LanguageType aLang =
+ rInf.GetTextFrame()->GetLangOfChar(rInf.GetIdx(), nScript);
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ nCnt = SwScriptInfo::ThaiJustify(*pStr, nullptr, nPos, nEnd - nPos);
+
+ const SwLinePortion* pPor = rPor.GetNextPortion();
+ if ( pPor && ( pPor->IsKernPortion() ||
+ pPor->IsControlCharPortion() ||
+ pPor->IsPostItsPortion() ) )
+ pPor = pPor->GetNextPortion();
+
+ if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) )
+ --nCnt;
+
+ return nCnt;
+ }
+ }
+
+ // Here starts the good old "Look for blanks and add space to them" part.
+ // Note: We do not want to add space to an isolated latin blank in front
+ // of some complex characters in RTL environment
+ const bool bDoNotAddSpace =
+ LATIN == nScript && (nEnd == nPos + TextFrameIndex(1)) && pSI &&
+ ( i18n::ScriptType::COMPLEX ==
+ pSI->ScriptType(nPos + TextFrameIndex(1))) &&
+ rInf.GetTextFrame() && rInf.GetTextFrame()->IsRightToLeft();
+
+ if ( bDoNotAddSpace )
+ return nCnt;
+
+ TextFrameIndex nTextEnd = std::min(nEnd, TextFrameIndex(pStr->getLength()));
+ for ( ; nPos < nTextEnd; ++nPos )
+ {
+ if (CH_BLANK == (*pStr)[ sal_Int32(nPos) ])
+ ++nCnt;
+ }
+
+ // We still have to examine the next character:
+ // If the next character is ASIAN and not KOREAN we have
+ // to add an extra space
+ // nPos refers to the original string, even if a field string has
+ // been passed to this function
+ nPos = rInf.GetIdx() + rPor.GetLen();
+ if (nPos < TextFrameIndex(rInf.GetText().getLength()))
+ {
+ sal_uInt8 nNextScript = 0;
+ const SwLinePortion* pPor = rPor.GetNextPortion();
+ if ( pPor && pPor->IsKernPortion() )
+ pPor = pPor->GetNextPortion();
+
+ if (!pPor || pPor->InFixMargGrp())
+ return nCnt;
+
+ // next character is inside a field?
+ if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() )
+ {
+ bool bOldOnWin = rInf.OnWin();
+ const_cast<SwTextSizeInfo &>(rInf).SetOnWin( false );
+
+ OUString aStr;
+ pPor->GetExpText( rInf, aStr );
+ const_cast<SwTextSizeInfo &>(rInf).SetOnWin( bOldOnWin );
+
+ nNextScript = static_cast<sal_uInt8>(g_pBreakIt->GetBreakIter()->getScriptType( aStr, 0 ));
+ }
+ else
+ nNextScript = static_cast<sal_uInt8>(
+ g_pBreakIt->GetBreakIter()->getScriptType(rInf.GetText(), sal_Int32(nPos)));
+
+ if( ASIAN == nNextScript )
+ {
+ LanguageType aLang =
+ rInf.GetTextFrame()->GetLangOfChar(nPos, nNextScript);
+
+ if (!MsLangId::isKorean(aLang))
+ ++nCnt;
+ }
+ }
+
+ return nCnt;
+}
+
+SwTextPortion * SwTextPortion::CopyLinePortion(const SwLinePortion &rPortion)
+{
+ SwTextPortion *const pNew(new SwTextPortion);
+ static_cast<SwLinePortion&>(*pNew) = rPortion;
+ pNew->SetWhichPor( PortionType::Text ); // overwrite that!
+ return pNew;
+}
+
+void SwTextPortion::BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess )
+{
+ // The word/char is larger than the line
+ // Special case 1: The word is larger than the line
+ // We truncate ...
+ const sal_uInt16 nLineWidth = o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X());
+ TextFrameIndex nLen = rGuess.CutPos() - rInf.GetIdx();
+ if (nLen > TextFrameIndex(0))
+ {
+ // special case: guess does not always provide the correct
+ // width, only in common cases.
+ if ( !rGuess.BreakWidth() )
+ {
+ rInf.SetLen( nLen );
+ SetLen( nLen );
+ CalcTextSize( rInf );
+
+ // changing these values requires also changing them in
+ // guess.cxx
+ sal_uInt16 nItalic = 0;
+ if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() )
+ {
+ nItalic = Height() / 12;
+ }
+ Width( Width() + nItalic );
+ }
+ else
+ {
+ Width( rGuess.BreakWidth() );
+ SetLen( nLen );
+ }
+ }
+ // special case: first character does not fit to line
+ else if ( rGuess.CutPos() == rInf.GetLineStart() )
+ {
+ SetLen( TextFrameIndex(1) );
+ Width( nLineWidth );
+ }
+ else
+ {
+ SetLen( TextFrameIndex(0) );
+ Width( 0 );
+ }
+}
+
+void SwTextPortion::BreakUnderflow( SwTextFormatInfo &rInf )
+{
+ Truncate();
+ Height( 0 );
+ Width( 0 );
+ SetLen( TextFrameIndex(0) );
+ SetAscent( 0 );
+ rInf.SetUnderflow( this );
+}
+
+static bool lcl_HasContent( const SwFieldPortion& rField, SwTextFormatInfo const &rInf )
+{
+ OUString aText;
+ return rField.GetExpText( rInf, aText ) && !aText.isEmpty();
+}
+
+bool SwTextPortion::Format_( SwTextFormatInfo &rInf )
+{
+ // 5744: If only the hyphen does not fit anymore, we still need to wrap
+ // the word, or else return true!
+ if( rInf.IsUnderflow() && rInf.GetSoftHyphPos() )
+ {
+ // soft hyphen portion has triggered an underflow event because
+ // of an alternative spelling position
+ bool bFull = false;
+ const bool bHyph = rInf.ChgHyph( true );
+ if( rInf.IsHyphenate() )
+ {
+ SwTextGuess aGuess;
+ // check for alternative spelling left from the soft hyphen
+ // this should usually be true but
+ aGuess.AlternativeSpelling(rInf, rInf.GetSoftHyphPos() - TextFrameIndex(1));
+ bFull = CreateHyphen( rInf, aGuess );
+ OSL_ENSURE( bFull, "Problem with hyphenation!!!" );
+ }
+ rInf.ChgHyph( bHyph );
+ rInf.SetSoftHyphPos( TextFrameIndex(0) );
+ return bFull;
+ }
+
+ SwTextGuess aGuess;
+ const bool bFull = !aGuess.Guess( *this, rInf, Height() );
+
+ // these are the possible cases:
+ // A Portion fits to current line
+ // B Portion does not fit to current line but a possible line break
+ // within the portion has been found by the break iterator, 2 subcases
+ // B1 break is hyphen
+ // B2 break is word end
+ // C Portion does not fit to current line and no possible line break
+ // has been found by break iterator, 2 subcases:
+ // C1 break iterator found a possible line break in portion before us
+ // ==> this break is used (underflow)
+ // C2 break iterator does not found a possible line break at all:
+ // ==> line break
+
+ // case A: line not yet full
+ if ( !bFull )
+ {
+ Width( aGuess.BreakWidth() );
+ // Caution!
+ if( !InExpGrp() || InFieldGrp() )
+ SetLen( rInf.GetLen() );
+
+ short nKern = rInf.GetFont()->CheckKerning();
+ if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern )
+ {
+ nKern = static_cast<short>(rInf.Width() - rInf.X() - Width() - 1);
+ if( nKern < 0 )
+ nKern = 0;
+ }
+ if( nKern )
+ new SwKernPortion( *this, nKern );
+ }
+ // special case: hanging portion
+ else if( bFull && aGuess.GetHangingPortion() )
+ {
+ Width( aGuess.BreakWidth() );
+ SetLen( aGuess.BreakPos() - rInf.GetIdx() );
+ aGuess.GetHangingPortion()->SetAscent( GetAscent() );
+ Insert( aGuess.ReleaseHangingPortion() );
+ }
+ // breakPos >= index
+ else if (aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != TextFrameIndex(COMPLETE_STRING))
+ {
+ // case B1
+ if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart()
+ && ( aGuess.BreakPos() > rInf.GetIdx() ||
+ ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) )
+ {
+ CreateHyphen( rInf, aGuess );
+ if ( rInf.GetFly() )
+ rInf.GetRoot()->SetMidHyph( true );
+ else
+ rInf.GetRoot()->SetEndHyph( true );
+ }
+ // case C1
+ // - Footnote portions with fake line start (i.e., not at beginning of line)
+ // should keep together with the text portion. (Note: no keep together
+ // with only footnote portions.
+ // - TabPortions not at beginning of line should keep together with the
+ // text portion, if they are not followed by a blank
+ // (work around different definition of tab stop character - breaking or
+ // non breaking character - in compatibility mode)
+ else if ( ( IsFootnotePortion() && rInf.IsFakeLineStart() &&
+
+ rInf.IsOtherThanFootnoteInside() ) ||
+ ( rInf.GetLast() &&
+ rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT) &&
+ rInf.GetLast()->InTabGrp() &&
+ rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() &&
+ aGuess.BreakPos() == rInf.GetIdx() &&
+ CH_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
+ CH_FULL_BLANK != rInf.GetChar( rInf.GetIdx() ) &&
+ CH_SIX_PER_EM != rInf.GetChar( rInf.GetIdx() ) ) )
+ BreakUnderflow( rInf );
+ // case B2
+ else if( rInf.GetIdx() > rInf.GetLineStart() ||
+ aGuess.BreakPos() > rInf.GetIdx() ||
+ // this is weird: during formatting the follow of a field
+ // the values rInf.GetIdx and rInf.GetLineStart are replaced
+ // IsFakeLineStart indicates GetIdx > GetLineStart
+ rInf.IsFakeLineStart() ||
+ rInf.GetFly() ||
+ rInf.IsFirstMulti() ||
+ ( rInf.GetLast() &&
+ ( rInf.GetLast()->IsFlyPortion() ||
+ ( rInf.GetLast()->InFieldGrp() &&
+ ! rInf.GetLast()->InNumberGrp() &&
+ ! rInf.GetLast()->IsErgoSumPortion() &&
+ lcl_HasContent(*static_cast<SwFieldPortion*>(rInf.GetLast()),rInf ) ) ) ) )
+ {
+ // GetLineWidth() takes care of DocumentSettingId::TAB_OVER_MARGIN.
+ if (aGuess.BreakWidth() <= rInf.GetLineWidth())
+ Width( aGuess.BreakWidth() );
+ else
+ // this actually should not happen
+ Width( sal_uInt16(rInf.Width() - rInf.X()) );
+
+ SetLen( aGuess.BreakPos() - rInf.GetIdx() );
+
+ OSL_ENSURE( aGuess.BreakStart() >= aGuess.FieldDiff(),
+ "Trouble with expanded field portions during line break" );
+ TextFrameIndex const nRealStart = aGuess.BreakStart() - aGuess.FieldDiff();
+ if( aGuess.BreakPos() < nRealStart && !InExpGrp() )
+ {
+ SwHolePortion *pNew = new SwHolePortion( *this );
+ pNew->SetLen( nRealStart - aGuess.BreakPos() );
+ Insert( pNew );
+ }
+ }
+ else // case C2, last exit
+ BreakCut( rInf, aGuess );
+ }
+ // breakPos < index or no breakpos at all
+ else
+ {
+ bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx();
+ const bool bBreakLineIfHasFly
+ = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::WRAP_AS_CHAR_FLYS_LIKE_IN_OOXML);
+ if (aGuess.BreakPos() != TextFrameIndex(COMPLETE_STRING) &&
+ (aGuess.BreakPos() != rInf.GetLineStart() || bBreakLineIfHasFly) &&
+ ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() ||
+ rInf.IsFirstMulti() ) &&
+ ( !rInf.GetLast()->IsBlankPortion() ||
+ SwBlankPortion::MayUnderflow(rInf, rInf.GetIdx() - TextFrameIndex(1), true)))
+ { // case C1 (former BreakUnderflow())
+ BreakUnderflow( rInf );
+ }
+ else
+ // case C2, last exit
+ BreakCut(rInf, aGuess);
+ }
+
+ return bFull;
+}
+
+bool SwTextPortion::Format( SwTextFormatInfo &rInf )
+{
+ // GetLineWidth() takes care of DocumentSettingId::TAB_OVER_MARGIN.
+ if( rInf.GetLineWidth() < 0 || (!GetLen() && !InExpGrp()) )
+ {
+ Height( 0 );
+ Width( 0 );
+ SetLen( TextFrameIndex(0) );
+ SetAscent( 0 );
+ SetNextPortion( nullptr ); // ????
+ return true;
+ }
+
+ OSL_ENSURE( rInf.RealWidth() || (rInf.X() == rInf.Width()),
+ "SwTextPortion::Format: missing real width" );
+ OSL_ENSURE( Height(), "SwTextPortion::Format: missing height" );
+
+ return Format_( rInf );
+}
+
+// Format end of line
+// 5083: We can have awkward cases e.g.:
+// "from {Santa}"
+// Santa wraps, "from " turns into "from" and " " in a justified
+// paragraph, in which the glue gets expanded instead of merged
+// with the MarginPortion.
+
+// rInf.nIdx points to the next word, nIdx-1 is the portion's last char
+void SwTextPortion::FormatEOL( SwTextFormatInfo &rInf )
+{
+ if( ( GetNextPortion() &&
+ ( !GetNextPortion()->IsKernPortion() || GetNextPortion()->GetNextPortion() ) ) ||
+ !GetLen() ||
+ rInf.GetIdx() >= TextFrameIndex(rInf.GetText().getLength()) ||
+ TextFrameIndex(1) >= rInf.GetIdx() ||
+ ' ' != rInf.GetChar(rInf.GetIdx() - TextFrameIndex(1)) ||
+ rInf.GetLast()->IsHolePortion() )
+ return;
+
+ // calculate number of blanks
+ TextFrameIndex nX(rInf.GetIdx() - TextFrameIndex(1));
+ TextFrameIndex nHoleLen(1);
+ while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) )
+ nHoleLen++;
+
+ // First set ourselves and the insert, because there could be
+ // a SwLineLayout
+ sal_uInt16 nBlankSize;
+ if( nHoleLen == GetLen() )
+ nBlankSize = Width();
+ else
+ nBlankSize = sal_Int32(nHoleLen) * rInf.GetTextSize(OUString(' ')).Width();
+ Width( Width() - nBlankSize );
+ rInf.X( rInf.X() - nBlankSize );
+ SetLen( GetLen() - nHoleLen );
+ SwLinePortion *pHole = new SwHolePortion( *this );
+ static_cast<SwHolePortion *>( pHole )->SetBlankWidth( nBlankSize );
+ static_cast<SwHolePortion *>( pHole )->SetLen( nHoleLen );
+ Insert( pHole );
+
+}
+
+TextFrameIndex SwTextPortion::GetModelPositionForViewPoint(const sal_uInt16 nOfst) const
+{
+ OSL_ENSURE( false, "SwTextPortion::GetModelPositionForViewPoint: don't use this method!" );
+ return SwLinePortion::GetModelPositionForViewPoint( nOfst );
+}
+
+// The GetTextSize() assumes that the own length is correct
+SwPosSize SwTextPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+{
+ SwPosSize aSize = rInf.GetTextSize();
+ if( !GetJoinBorderWithPrev() )
+ aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace() );
+ if( !GetJoinBorderWithNext() )
+ aSize.Width(aSize.Width() + rInf.GetFont()->GetRightBorderSpace() );
+
+ aSize.Height(aSize.Height() +
+ rInf.GetFont()->GetTopBorderSpace() +
+ rInf.GetFont()->GetBottomBorderSpace() );
+
+ return aSize;
+}
+
+void SwTextPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if (rInf.OnWin() && TextFrameIndex(1) == rInf.GetLen()
+ && CH_TXT_ATR_FIELDEND == rInf.GetText()[sal_Int32(rInf.GetIdx())])
+ {
+ assert(false); // this is some debugging only code
+ rInf.DrawBackBrush( *this );
+ const OUString aText(CH_TXT_ATR_SUBST_FIELDEND);
+ rInf.DrawText(aText, *this, TextFrameIndex(0), TextFrameIndex(aText.getLength()));
+ }
+ else if (rInf.OnWin() && TextFrameIndex(1) == rInf.GetLen()
+ && CH_TXT_ATR_FIELDSTART == rInf.GetText()[sal_Int32(rInf.GetIdx())])
+ {
+ assert(false); // this is some debugging only code
+ rInf.DrawBackBrush( *this );
+ const OUString aText(CH_TXT_ATR_SUBST_FIELDSTART);
+ rInf.DrawText(aText, *this, TextFrameIndex(0), TextFrameIndex(aText.getLength()));
+ }
+ else if( GetLen() )
+ {
+ rInf.DrawBackBrush( *this );
+ rInf.DrawBorder( *this );
+
+ // do we have to repaint a post it portion?
+ if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
+ mpNextPortion->PrePaint( rInf, this );
+
+ auto const* pWrongList = rInf.GetpWrongList();
+ auto const* pGrammarCheckList = rInf.GetGrammarCheckList();
+ auto const* pSmarttags = rInf.GetSmartTags();
+
+ const bool bWrong = nullptr != pWrongList;
+ const bool bGrammarCheck = nullptr != pGrammarCheckList;
+ const bool bSmartTags = nullptr != pSmarttags;
+
+ if ( bWrong || bSmartTags || bGrammarCheck )
+ rInf.DrawMarkedText( *this, rInf.GetLen(), bWrong, bSmartTags, bGrammarCheck );
+ else
+ rInf.DrawText( *this, rInf.GetLen() );
+ }
+}
+
+bool SwTextPortion::GetExpText( const SwTextSizeInfo &, OUString & ) const
+{
+ return false;
+}
+
+// Responsible for the justified paragraph. They calculate the blank
+// count and the resulting added space.
+TextFrameIndex SwTextPortion::GetSpaceCnt(const SwTextSizeInfo &rInf,
+ TextFrameIndex& rCharCnt) const
+{
+ TextFrameIndex nCnt(0);
+ TextFrameIndex nPos(0);
+
+ if ( rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
+ if (pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
+ return TextFrameIndex(0);
+ }
+
+ if ( InExpGrp() || PortionType::InputField == GetWhichPor() )
+ {
+ if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
+ {
+ // OnWin() likes to return a blank instead of an empty string from
+ // time to time. We cannot use that here at all, however.
+ bool bOldOnWin = rInf.OnWin();
+ const_cast<SwTextSizeInfo &>(rInf).SetOnWin( false );
+
+ OUString aStr;
+ GetExpText( rInf, aStr );
+ const_cast<SwTextSizeInfo &>(rInf).SetOnWin( bOldOnWin );
+
+ nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
+ nPos = TextFrameIndex(aStr.getLength());
+ }
+ }
+ else if( !IsDropPortion() )
+ {
+ nCnt = nCnt + lcl_AddSpace( rInf, nullptr, *this );
+ nPos = GetLen();
+ }
+ rCharCnt = rCharCnt + nPos;
+ return nCnt;
+}
+
+tools::Long SwTextPortion::CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const
+{
+ TextFrameIndex nCnt(0);
+
+ if ( rInf.SnapToGrid() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetTextFrame()->FindPageFrame()));
+ if (pGrid && GRID_LINES_CHARS == pGrid->GetGridType() && pGrid->IsSnapToChars())
+ return 0;
+ }
+
+ if ( InExpGrp() || PortionType::InputField == GetWhichPor() )
+ {
+ if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() )
+ {
+ // OnWin() likes to return a blank instead of an empty string from
+ // time to time. We cannot use that here at all, however.
+ bool bOldOnWin = rInf.OnWin();
+ const_cast<SwTextSizeInfo &>(rInf).SetOnWin( false );
+
+ OUString aStr;
+ GetExpText( rInf, aStr );
+ const_cast<SwTextSizeInfo &>(rInf).SetOnWin( bOldOnWin );
+ if( nSpaceAdd > 0 )
+ nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this );
+ else
+ {
+ nSpaceAdd = -nSpaceAdd;
+ nCnt = TextFrameIndex(aStr.getLength());
+ }
+ }
+ }
+ else if( !IsDropPortion() )
+ {
+ if( nSpaceAdd > 0 )
+ nCnt = nCnt + lcl_AddSpace( rInf, nullptr, *this );
+ else
+ {
+ nSpaceAdd = -nSpaceAdd;
+ nCnt = GetLen();
+ SwLinePortion* pPor = GetNextPortion();
+
+ // we do not want an extra space in front of margin portions
+ if ( nCnt )
+ {
+ while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() )
+ pPor = pPor->GetNextPortion();
+
+ if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() )
+ --nCnt;
+ }
+ }
+ }
+
+ return sal_Int32(nCnt) * nSpaceAdd / SPACING_PRECISION_FACTOR;
+}
+
+void SwTextPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
+}
+
+SwTextInputFieldPortion::SwTextInputFieldPortion()
+{
+ SetWhichPor( PortionType::InputField );
+}
+
+bool SwTextInputFieldPortion::Format(SwTextFormatInfo &rTextFormatInfo)
+{
+ return SwTextPortion::Format(rTextFormatInfo);
+}
+
+void SwTextInputFieldPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if ( Width() )
+ {
+ rInf.DrawViewOpt( *this, PortionType::InputField );
+ SwTextSlot aPaintText( &rInf, this, true, true, OUString() );
+ SwTextPortion::Paint( rInf );
+ }
+ else
+ {
+ // highlight empty input field, elsewhere they are completely invisible for the user
+ SwRect aIntersect;
+ rInf.CalcRect(*this, &aIntersect);
+ const sal_uInt16 aAreaWidth = rInf.GetTextSize(OUString(' ')).Width();
+ aIntersect.Left(aIntersect.Left() - aAreaWidth/2);
+ aIntersect.Width(aAreaWidth);
+
+ if (aIntersect.HasArea()
+ && rInf.OnWin()
+ && SwViewOption::IsFieldShadings()
+ && !rInf.GetOpt().IsPagePreview())
+ {
+ OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
+ pOut->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+ pOut->SetFillColor(SwViewOption::GetFieldShadingsColor());
+ pOut->SetLineColor();
+ pOut->DrawRect(aIntersect.SVRect());
+ pOut->Pop();
+ }
+ }
+}
+
+bool SwTextInputFieldPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
+{
+ sal_Int32 nIdx(rInf.GetIdx());
+ sal_Int32 nLen(GetLen());
+ if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART )
+ {
+ ++nIdx;
+ --nLen;
+ }
+ if (rInf.GetChar(rInf.GetIdx() + GetLen() - TextFrameIndex(1)) == CH_TXT_ATR_INPUTFIELDEND)
+ {
+ --nLen;
+ }
+ rText = rInf.GetText().copy( nIdx, std::min( nLen, rInf.GetText().getLength() - nIdx ) );
+
+ return true;
+}
+
+SwPosSize SwTextInputFieldPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+{
+ SwTextSlot aFormatText( &rInf, this, true, false );
+ if (rInf.GetLen() == TextFrameIndex(0))
+ {
+ return SwPosSize( 0, 0 );
+ }
+
+ return rInf.GetTextSize();
+}
+
+SwHolePortion::SwHolePortion( const SwTextPortion &rPor )
+ : m_nBlankWidth( 0 )
+{
+ SetLen( TextFrameIndex(1) );
+ Height( rPor.Height() );
+ SetAscent( rPor.GetAscent() );
+ SetWhichPor( PortionType::Hole );
+}
+
+SwLinePortion *SwHolePortion::Compress() { return this; }
+
+void SwHolePortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( !rInf.GetOut() )
+ return;
+
+ bool bPDFExport = rInf.GetVsh()->GetViewOptions()->IsPDFExport();
+
+ // #i16816# export stuff only needed for tagged pdf support
+ if (bPDFExport && !SwTaggedPDFHelper::IsExportTaggedPDF( *rInf.GetOut()) )
+ return;
+
+ // #i68503# the hole must have no decoration for a consistent visual appearance
+ const SwFont* pOrigFont = rInf.GetFont();
+ std::unique_ptr<SwFont> pHoleFont;
+ std::unique_ptr<SwFontSave> pFontSave;
+ if( pOrigFont->GetUnderline() != LINESTYLE_NONE
+ || pOrigFont->GetOverline() != LINESTYLE_NONE
+ || pOrigFont->GetStrikeout() != STRIKEOUT_NONE )
+ {
+ pHoleFont.reset(new SwFont( *pOrigFont ));
+ pHoleFont->SetUnderline( LINESTYLE_NONE );
+ pHoleFont->SetOverline( LINESTYLE_NONE );
+ pHoleFont->SetStrikeout( STRIKEOUT_NONE );
+ pFontSave.reset(new SwFontSave( rInf, pHoleFont.get() ));
+ }
+
+ if (bPDFExport)
+ {
+ rInf.DrawText(" ", *this, TextFrameIndex(0), TextFrameIndex(1));
+ }
+ else
+ {
+ // tdf#43244: Paint spaces even at end of line,
+ // but only if this paint is not called for pdf export, to keep that pdf export intact
+ rInf.DrawText(*this, rInf.GetLen());
+ }
+
+ pFontSave.reset();
+ pHoleFont.reset();
+}
+
+bool SwHolePortion::Format( SwTextFormatInfo &rInf )
+{
+ return rInf.IsFull() || rInf.X() >= rInf.Width();
+}
+
+void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Text( GetLen(), GetWhichPor() );
+}
+
+void SwHolePortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHolePortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("blank-width"),
+ BAD_CAST(OString::number(m_nBlankWidth).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwFieldMarkPortion::Paint( const SwTextPaintInfo & /*rInf*/) const
+{
+ // These shouldn't be painted!
+ //SwTextPortion::Paint(rInf);
+}
+
+bool SwFieldMarkPortion::Format( SwTextFormatInfo & )
+{
+ Width(0);
+ return false;
+}
+
+void SwFieldFormCheckboxPortion::Paint( const SwTextPaintInfo& rInf ) const
+{
+ SwPosition const aPosition(rInf.GetTextFrame()->MapViewToModelPos(rInf.GetIdx()));
+
+ IFieldmark const*const pBM = rInf.GetTextFrame()->GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(aPosition);
+
+ OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX,
+ "Where is my form field bookmark???");
+
+ if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
+ {
+ const ICheckboxFieldmark* pCheckboxFm = dynamic_cast<ICheckboxFieldmark const*>(pBM);
+ bool bChecked = pCheckboxFm && pCheckboxFm->IsChecked();
+ rInf.DrawCheckBox(*this, bChecked);
+ }
+}
+
+bool SwFieldFormCheckboxPortion::Format( SwTextFormatInfo & rInf )
+{
+ SwPosition const aPosition(rInf.GetTextFrame()->MapViewToModelPos(rInf.GetIdx()));
+ IFieldmark const*const pBM = rInf.GetTextFrame()->GetDoc().getIDocumentMarkAccess()->getFieldmarkAt(aPosition);
+ OSL_ENSURE(pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX, "Where is my form field bookmark???");
+ if (pBM && pBM->GetFieldname( ) == ODF_FORMCHECKBOX)
+ {
+ // the width of the checkbox portion is the same as its height since it's a square
+ // and that size depends on the font size.
+ // See:
+ // http://document-foundation-mail-archive.969070.n3.nabble.com/Wrong-copy-paste-in-SwFieldFormCheckboxPortion-Format-td4269112.html
+ Width( rInf.GetTextHeight( ) );
+ Height( rInf.GetTextHeight( ) );
+ SetAscent( rInf.GetAscent( ) );
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/portxt.hxx b/sw/source/core/text/portxt.hxx
new file mode 100644
index 000000000..77ec0a9f1
--- /dev/null
+++ b/sw/source/core/text/portxt.hxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include "porlin.hxx"
+
+class SwTextGuess;
+
+/// This portion represents a part of the paragraph string.
+class SwTextPortion : public SwLinePortion
+{
+ void BreakCut( SwTextFormatInfo &rInf, const SwTextGuess &rGuess );
+ void BreakUnderflow( SwTextFormatInfo &rInf );
+ bool Format_( SwTextFormatInfo &rInf );
+
+public:
+ SwTextPortion(){ SetWhichPor( PortionType::Text ); }
+ static SwTextPortion * CopyLinePortion(const SwLinePortion &rPortion);
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void FormatEOL( SwTextFormatInfo &rInf ) override;
+ virtual TextFrameIndex GetModelPositionForViewPoint(sal_uInt16 nOfst) const override;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual tools::Long CalcSpacing( tools::Long nSpaceAdd, const SwTextSizeInfo &rInf ) const override;
+
+ // Counts the spaces for justified paragraph
+ TextFrameIndex GetSpaceCnt(const SwTextSizeInfo &rInf, TextFrameIndex& rCnt) const;
+
+ bool CreateHyphen( SwTextFormatInfo &rInf, SwTextGuess const &rGuess );
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+};
+
+class SwTextInputFieldPortion : public SwTextPortion
+{
+public:
+ SwTextInputFieldPortion();
+
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const override;
+ virtual SwPosSize GetTextSize( const SwTextSizeInfo &rInfo ) const override;
+};
+
+class SwHolePortion : public SwLinePortion
+{
+ sal_uInt16 m_nBlankWidth;
+public:
+ explicit SwHolePortion( const SwTextPortion &rPor );
+ sal_uInt16 GetBlankWidth( ) const { return m_nBlankWidth; }
+ void SetBlankWidth( const sal_uInt16 nNew ) { m_nBlankWidth = nNew; }
+ virtual SwLinePortion *Compress() override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+
+ // Accessibility: pass information about this portion to the PortionHandler
+ virtual void HandlePortion( SwPortionHandler& rPH ) const override;
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const override;
+};
+
+class SwFieldMarkPortion : public SwTextPortion
+{
+ public:
+ SwFieldMarkPortion() : SwTextPortion()
+ {
+ SetWhichPor(PortionType::FieldMark);
+ }
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+};
+
+class SwFieldFormCheckboxPortion : public SwTextPortion
+{
+public:
+ SwFieldFormCheckboxPortion() : SwTextPortion()
+ {
+ SetWhichPor(PortionType::FieldFormCheckbox);
+ }
+ virtual void Paint( const SwTextPaintInfo &rInf ) const override;
+ virtual bool Format( SwTextFormatInfo &rInf ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/possiz.hxx b/sw/source/core/text/possiz.hxx
new file mode 100644
index 000000000..f66bd1988
--- /dev/null
+++ b/sw/source/core/text/possiz.hxx
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <tools/gen.hxx>
+#include <swtypes.hxx>
+
+// Compared to the SV sizes SwPosSize is always positive
+class SwPosSize
+{
+ SwTwips m_nWidth;
+ SwTwips m_nHeight;
+public:
+ SwPosSize( const SwTwips nW = 0, const SwTwips nH = 0 )
+ : m_nWidth(nW)
+ , m_nHeight(nH)
+ {
+ }
+ explicit SwPosSize( const Size &rSize )
+ : m_nWidth(SwTwips(rSize.Width()))
+ , m_nHeight(SwTwips(rSize.Height()))
+ {
+ }
+#if defined(__COVERITY__)
+ virtual ~SwPosSize() COVERITY_NOEXCEPT_FALSE {}
+#else
+ virtual ~SwPosSize() {}
+#endif
+ SwPosSize(SwPosSize const &) = default;
+ SwPosSize(SwPosSize &&) = default;
+ SwPosSize & operator =(SwPosSize const &) = default;
+ SwPosSize & operator =(SwPosSize &&) = default;
+ SwTwips Height() const { return m_nHeight; }
+ virtual void Height(const SwTwips nNew, const bool = true) { m_nHeight = nNew; }
+ SwTwips Width() const { return m_nWidth; }
+ void Width( const SwTwips nNew ) { m_nWidth = nNew; }
+ Size SvLSize() const { return Size( m_nWidth, m_nHeight ); }
+ void SvLSize( const Size &rSize )
+ {
+ m_nWidth = SwTwips(rSize.Width());
+ m_nHeight = SwTwips(rSize.Height());
+ }
+ void SvXSize( const Size &rSize )
+ {
+ m_nHeight = SwTwips(rSize.Width());
+ m_nWidth = SwTwips(rSize.Height());
+ }
+ SwPosSize& operator=( const Size &rSize )
+ {
+ m_nWidth = SwTwips(rSize.Width());
+ m_nHeight = SwTwips(rSize.Height());
+ return *this;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
new file mode 100644
index 000000000..12e4fea9f
--- /dev/null
+++ b/sw/source/core/text/redlnitr.cxx
@@ -0,0 +1,1137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <hintids.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/whiter.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <scriptinfo.hxx>
+#include <swmodule.hxx>
+#include <redline.hxx>
+#include <txatbase.hxx>
+#include <docary.hxx>
+#include "itratr.hxx"
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IMark.hxx>
+#include <bookmark.hxx>
+#include <rootfrm.hxx>
+#include <breakit.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/settings.hxx>
+#include <txtfrm.hxx>
+#include <ftnfrm.hxx>
+#include <vcl/svapp.hxx>
+#include "redlnitr.hxx"
+#include <extinput.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/udlnitem.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class HideIterator
+{
+private:
+ IDocumentRedlineAccess const& m_rIDRA;
+ IDocumentMarkAccess const& m_rIDMA;
+ bool const m_isHideRedlines;
+ sw::FieldmarkMode const m_eFieldmarkMode;
+ SwPosition const m_Start;
+ /// next redline
+ SwRedlineTable::size_type m_RedlineIndex;
+ /// next fieldmark
+ std::pair<sw::mark::IFieldmark const*, std::unique_ptr<SwPosition>> m_Fieldmark;
+ std::optional<SwPosition> m_oNextFieldmarkHide;
+ /// current start/end pair
+ SwPosition const* m_pStartPos;
+ SwPosition const* m_pEndPos;
+
+public:
+ SwPosition const* GetStartPos() const { return m_pStartPos; }
+ SwPosition const* GetEndPos() const { return m_pEndPos; }
+
+ HideIterator(SwTextNode & rTextNode,
+ bool const isHideRedlines, sw::FieldmarkMode const eMode)
+ : m_rIDRA(rTextNode.getIDocumentRedlineAccess())
+ , m_rIDMA(*rTextNode.getIDocumentMarkAccess())
+ , m_isHideRedlines(isHideRedlines)
+ , m_eFieldmarkMode(eMode)
+ , m_Start(rTextNode, 0)
+ , m_RedlineIndex(m_rIDRA.GetRedlinePos(rTextNode, RedlineType::Any))
+ , m_pStartPos(nullptr)
+ , m_pEndPos(&m_Start)
+ {
+ }
+
+ // delete redlines and fieldmarks can't overlap, due to sw::CalcBreaks()
+ // and no combining of adjacent redlines
+ // -> dummy chars are delete-redlined *iff* entire fieldmark is
+ // Note: caller is responsible for checking for immediately adjacent hides
+ bool Next()
+ {
+ SwPosition const* pNextRedlineHide(nullptr);
+ assert(m_pEndPos);
+ if (m_isHideRedlines)
+ {
+ // position on current or next redline
+ for (; m_RedlineIndex < m_rIDRA.GetRedlineTable().size(); ++m_RedlineIndex)
+ {
+ SwRangeRedline const*const pRed = m_rIDRA.GetRedlineTable()[m_RedlineIndex];
+
+ if (m_pEndPos->nNode.GetIndex() < pRed->Start()->nNode.GetIndex())
+ break;
+
+ if (pRed->GetType() != RedlineType::Delete)
+ continue;
+
+ SwPosition const*const pStart(pRed->Start());
+ SwPosition const*const pEnd(pRed->End());
+ if (*pStart == *pEnd)
+ { // only allowed while moving (either way?)
+// assert(IDocumentRedlineAccess::IsHideChanges(rIDRA.GetRedlineFlags()));
+ continue;
+ }
+ if (pStart->nNode.GetNode().IsTableNode())
+ {
+ assert(pEnd->nNode == m_Start.nNode && pEnd->nContent.GetIndex() == 0);
+ continue; // known pathology, ignore it
+ }
+ if (*m_pEndPos <= *pStart)
+ {
+ pNextRedlineHide = pStart;
+ break; // the next one
+ }
+ }
+ }
+
+ // position on current or next fieldmark
+ m_oNextFieldmarkHide.reset();
+ if (m_eFieldmarkMode != sw::FieldmarkMode::ShowBoth)
+ {
+ sal_Unicode const magic(m_eFieldmarkMode == sw::FieldmarkMode::ShowResult
+ ? CH_TXT_ATR_FIELDSTART
+ : CH_TXT_ATR_FIELDSEP);
+ SwTextNode* pTextNode = m_pEndPos->nNode.GetNode().GetTextNode();
+ sal_Int32 const nPos = pTextNode ? pTextNode->GetText().indexOf(
+ magic, m_pEndPos->nContent.GetIndex()) : -1;
+ if (nPos != -1)
+ {
+ m_oNextFieldmarkHide.emplace(*pTextNode, nPos);
+ sw::mark::IFieldmark const*const pFieldmark(
+ m_eFieldmarkMode == sw::FieldmarkMode::ShowResult
+ ? m_rIDMA.getFieldmarkAt(*m_oNextFieldmarkHide)
+ : m_rIDMA.getFieldmarkFor(*m_oNextFieldmarkHide));
+ assert(pFieldmark);
+ m_Fieldmark.first = pFieldmark;
+ // for cursor travelling, there should be 2 visible chars;
+ // whichever char is hidden, the cursor travelling needs to
+ // be adapted in any case to skip in some situation or other;
+ // always hide the CH_TXT_ATR_FIELDSEP for now
+ if (m_eFieldmarkMode == sw::FieldmarkMode::ShowResult)
+ {
+ m_Fieldmark.second.reset(
+ new SwPosition(sw::mark::FindFieldSep(*m_Fieldmark.first)));
+ ++m_Fieldmark.second->nContent;
+ ++m_oNextFieldmarkHide->nContent; // skip start
+ }
+ else
+ {
+ m_Fieldmark.second.reset(
+ new SwPosition(pFieldmark->GetMarkEnd()));
+ --m_Fieldmark.second->nContent;
+ }
+ }
+ }
+
+ // == can happen only if redline starts inside field command, and in
+ // that case redline will end before field separator
+ assert(!pNextRedlineHide || !m_oNextFieldmarkHide
+ || *pNextRedlineHide != *m_oNextFieldmarkHide
+ || *m_rIDRA.GetRedlineTable()[m_RedlineIndex]->End() < *m_Fieldmark.second);
+ if (pNextRedlineHide
+ && (!m_oNextFieldmarkHide || *pNextRedlineHide < *m_oNextFieldmarkHide))
+ {
+ SwRangeRedline const*const pRed(m_rIDRA.GetRedlineTable()[m_RedlineIndex]);
+ m_pStartPos = pRed->Start();
+ m_pEndPos = pRed->End();
+ ++m_RedlineIndex;
+ return true;
+ }
+ else if (m_oNextFieldmarkHide)
+ {
+ assert(!pNextRedlineHide || *m_oNextFieldmarkHide <= *pNextRedlineHide);
+ m_pStartPos = &*m_oNextFieldmarkHide;
+ m_pEndPos = m_Fieldmark.second.get();
+ return true;
+ }
+ else // nothing
+ {
+ assert(!pNextRedlineHide && !m_oNextFieldmarkHide);
+ m_pStartPos = nullptr;
+ m_pEndPos = nullptr;
+ return false;
+ }
+ }
+};
+
+}
+
+namespace sw {
+
+std::unique_ptr<sw::MergedPara>
+CheckParaRedlineMerge(SwTextFrame & rFrame, SwTextNode & rTextNode,
+ FrameMode const eMode)
+{
+ if (!rFrame.getRootFrame()->HasMergedParas())
+ {
+ return nullptr;
+ }
+ bool bHaveRedlines(false);
+ std::vector<SwTextNode *> nodes{ &rTextNode };
+ std::vector<SwTableNode *> tables;
+ std::vector<SwSectionNode *> sections;
+ std::vector<sw::Extent> extents;
+ OUStringBuffer mergedText;
+ SwTextNode * pParaPropsNode(nullptr);
+ SwTextNode * pNode(&rTextNode);
+ sal_Int32 nLastEnd(0);
+ for (auto iter = HideIterator(rTextNode,
+ rFrame.getRootFrame()->IsHideRedlines(),
+ rFrame.getRootFrame()->GetFieldmarkMode()); iter.Next(); )
+ {
+ SwPosition const*const pStart(iter.GetStartPos());
+ SwPosition const*const pEnd(iter.GetEndPos());
+ bHaveRedlines = true;
+ assert(pNode != &rTextNode || &pStart->nNode.GetNode() == &rTextNode); // detect calls with wrong start node
+ if (pStart->nContent != nLastEnd) // not 0 so we eliminate adjacent deletes
+ {
+ extents.emplace_back(pNode, nLastEnd, pStart->nContent.GetIndex());
+ mergedText.append(pNode->GetText().subView(nLastEnd, pStart->nContent.GetIndex() - nLastEnd));
+ }
+ if (&pEnd->nNode.GetNode() != pNode)
+ {
+ if (pNode == &rTextNode)
+ {
+ pNode->SetRedlineMergeFlag(SwNode::Merge::First);
+ } // else: was already set before
+ int nLevel(0);
+ for (SwNodeOffset j = pNode->GetIndex() + 1; j < pEnd->nNode.GetIndex(); ++j)
+ {
+ SwNode *const pTmp(pNode->GetNodes()[j]);
+ if (nLevel == 0)
+ {
+ if (pTmp->IsTextNode())
+ {
+ nodes.push_back(pTmp->GetTextNode());
+ }
+ else if (pTmp->IsTableNode())
+ {
+ tables.push_back(pTmp->GetTableNode());
+ }
+ else if (pTmp->IsSectionNode())
+ {
+ sections.push_back(pTmp->GetSectionNode());
+ }
+ }
+ if (pTmp->IsStartNode())
+ {
+ ++nLevel;
+ }
+ else if (pTmp->IsEndNode())
+ {
+ --nLevel;
+ }
+ pTmp->SetRedlineMergeFlag(SwNode::Merge::Hidden);
+ }
+ // note: in DelLastPara() case, the end node is not actually merged
+ // and is likely a SwTableNode!
+ if (!pEnd->nNode.GetNode().IsTextNode())
+ {
+ assert(pEnd->nNode != pStart->nNode);
+ // must set pNode too because it will mark the last node
+ pNode = nodes.back();
+ assert(pNode == pNode->GetNodes()[pEnd->nNode.GetIndex() - 1]);
+ if (pNode != &rTextNode)
+ { // something might depend on last merged one being NonFirst?
+ pNode->SetRedlineMergeFlag(SwNode::Merge::NonFirst);
+ }
+ nLastEnd = pNode->Len();
+ }
+ else
+ {
+ pNode = pEnd->nNode.GetNode().GetTextNode();
+ nodes.push_back(pNode);
+ pNode->SetRedlineMergeFlag(SwNode::Merge::NonFirst);
+ nLastEnd = pEnd->nContent.GetIndex();
+ }
+ }
+ else
+ {
+ nLastEnd = pEnd->nContent.GetIndex();
+ }
+ }
+ if (pNode == &rTextNode)
+ {
+ if (rTextNode.GetRedlineMergeFlag() != SwNode::Merge::None)
+ {
+ rTextNode.SetRedlineMergeFlag(SwNode::Merge::None);
+ }
+ }
+ // Reset flag of the following text node since we know it's not merged;
+ // also any table/sections in between.
+ // * the following SwTextNode is in same nodes section as pNode (nLevel=0)
+ // * the start nodes that don't have a SwTextNode before them
+ // on their level, and their corresponding end nodes
+ // * the first SwTextNode inside each start node of the previous point
+ // Other (non-first) SwTextNodes in nested sections shouldn't be reset!
+ int nLevel(0);
+ for (SwNodeOffset j = pNode->GetIndex() + 1; j < pNode->GetNodes().Count(); ++j)
+ {
+ SwNode *const pTmp(pNode->GetNodes()[j]);
+ if (!pTmp->IsCreateFrameWhenHidingRedlines())
+ { // clear stale flag caused by editing with redlines shown
+ pTmp->SetRedlineMergeFlag(SwNode::Merge::None);
+ }
+ if (pTmp->IsStartNode())
+ {
+ ++nLevel;
+ }
+ else if (pTmp->IsEndNode())
+ {
+ if (nLevel == 0)
+ {
+ break; // there is no following text node; avoid leaving section
+ }
+ --nLevel;
+ }
+ else if (pTmp->IsTextNode())
+ {
+ if (nLevel == 0)
+ {
+ break; // done
+ }
+ else
+ { // skip everything other than 1st text node in section!
+ j = pTmp->EndOfSectionIndex() - 1; // will be incremented again
+ }
+ }
+ }
+ if (!bHaveRedlines)
+ {
+ if (rTextNode.IsInList() && !rTextNode.GetNum(rFrame.getRootFrame()))
+ {
+ rTextNode.AddToListRLHidden(); // try to add it...
+ }
+ return nullptr;
+ }
+ if (nLastEnd != pNode->Len())
+ {
+ extents.emplace_back(pNode, nLastEnd, pNode->Len());
+ mergedText.append(pNode->GetText().subView(nLastEnd, pNode->Len() - nLastEnd));
+ }
+ if (extents.empty()) // there was no text anywhere
+ {
+ assert(mergedText.isEmpty());
+ pParaPropsNode = pNode; // if every node is empty, the last one wins
+ }
+ else
+ {
+ assert(!mergedText.isEmpty());
+ pParaPropsNode = extents.begin()->pNode; // para props from first node that isn't empty
+ }
+// pParaPropsNode = &rTextNode; // well, actually...
+ // keep lists up to date with visible nodes
+ if (pParaPropsNode->IsInList() && !pParaPropsNode->GetNum(rFrame.getRootFrame()))
+ {
+ pParaPropsNode->AddToListRLHidden(); // try to add it...
+ }
+ for (auto const pTextNode : nodes)
+ {
+ if (pTextNode != pParaPropsNode)
+ {
+ pTextNode->RemoveFromListRLHidden();
+ }
+ }
+ if (eMode == FrameMode::Existing)
+ {
+ // remove existing footnote frames for first node;
+ // for non-first nodes with own frames, DelFrames will remove all
+ // (could possibly call lcl_ChangeFootnoteRef, not sure if worth it)
+ // note: must be done *before* changing listeners!
+ // for non-first nodes that are already merged with this frame,
+ // need to remove here too, otherwise footnotes can be removed only
+ // by lucky accident, e.g. TruncLines().
+ auto itExtent(extents.begin());
+ for (auto const pTextNode : nodes)
+ {
+ sal_Int32 nLast(0);
+ std::vector<std::pair<sal_Int32, sal_Int32>> hidden;
+ for ( ; itExtent != extents.end(); ++itExtent)
+ {
+ if (itExtent->pNode != pTextNode)
+ {
+ break;
+ }
+ if (itExtent->nStart != 0)
+ {
+ assert(itExtent->nStart != nLast);
+ hidden.emplace_back(nLast, itExtent->nStart);
+ }
+ nLast = itExtent->nEnd;
+ }
+ if (nLast != pTextNode->Len())
+ {
+ hidden.emplace_back(nLast, pTextNode->Len());
+ }
+ sw::RemoveFootnotesForNode(*rFrame.getRootFrame(), *pTextNode, &hidden);
+ }
+ // unfortunately DelFrames() must be done before StartListening too,
+ // otherwise footnotes cannot be deleted by SwTextFootnote::DelFrames!
+ auto const end(--nodes.rend());
+ for (auto iter = nodes.rbegin(); iter != end; ++iter)
+ {
+ (**iter).DelFrames(rFrame.getRootFrame());
+ }
+ // also delete tables & sections here; not necessary, but convenient
+ for (auto const pTableNode : tables)
+ {
+ pTableNode->DelFrames(rFrame.getRootFrame());
+ }
+ for (auto const pSectionNode : sections)
+ {
+ pSectionNode->GetSection().GetFormat()->DelFrames(/*rFrame.getRootFrame()*/);
+ }
+ }
+ auto pRet(std::make_unique<sw::MergedPara>(rFrame, std::move(extents),
+ mergedText.makeStringAndClear(), pParaPropsNode, &rTextNode,
+ nodes.back()));
+ for (SwTextNode * pTmp : nodes)
+ {
+ pRet->listener.StartListening(pTmp);
+ }
+ rFrame.EndListeningAll();
+ return pRet;
+}
+
+} // namespace sw
+
+void SwAttrIter::InitFontAndAttrHandler(
+ SwTextNode const& rPropsNode,
+ SwTextNode const& rTextNode,
+ OUString const& rText,
+ bool const*const pbVertLayout,
+ bool const*const pbVertLayoutLRBT)
+{
+ // Build a font matching the default paragraph style:
+ SwFontAccess aFontAccess( &rPropsNode.GetAnyFormatColl(), m_pViewShell );
+ // It is possible that Init is called more than once, e.g., in a
+ // SwTextFrame::FormatOnceMore situation or (since sw_redlinehide)
+ // from SwAttrIter::Seek(); in the latter case SwTextSizeInfo::m_pFnt
+ // is an alias of m_pFont so it must not be deleted!
+ if (m_pFont)
+ {
+ *m_pFont = aFontAccess.Get()->GetFont();
+ }
+ else
+ {
+ m_pFont = new SwFont( aFontAccess.Get()->GetFont() );
+ }
+
+ // set font to vertical if frame layout is vertical
+ // if it's a re-init, the vert flag never changes
+ bool bVertLayoutLRBT = false;
+ if (pbVertLayoutLRBT)
+ bVertLayoutLRBT = *pbVertLayoutLRBT;
+ if (pbVertLayout ? *pbVertLayout : m_aAttrHandler.IsVertLayout())
+ {
+ m_pFont->SetVertical(m_pFont->GetOrientation(), true, bVertLayoutLRBT);
+ }
+
+ // Initialize the default attribute of the attribute handler
+ // based on the attribute array cached together with the font.
+ // If any further attributes for the paragraph are given in pAttrSet
+ // consider them during construction of the default array, and apply
+ // them to the font
+ m_aAttrHandler.Init(aFontAccess.Get()->GetDefault(), rTextNode.GetpSwAttrSet(),
+ *rTextNode.getIDocumentSettingAccess(), m_pViewShell, *m_pFont,
+ pbVertLayout ? *pbVertLayout : m_aAttrHandler.IsVertLayout(),
+ bVertLayoutLRBT );
+
+ m_aFontCacheIds[SwFontScript::Latin] = m_aFontCacheIds[SwFontScript::CJK] = m_aFontCacheIds[SwFontScript::CTL] = nullptr;
+
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ m_pFont->SetActual( m_pScriptInfo->WhichFont(TextFrameIndex(0)) );
+
+ TextFrameIndex nChg(0);
+ size_t nCnt = 0;
+
+ do
+ {
+ if ( nCnt >= m_pScriptInfo->CountScriptChg() )
+ break;
+ nChg = m_pScriptInfo->GetScriptChg( nCnt );
+ SwFontScript nTmp = SW_SCRIPTS;
+ switch ( m_pScriptInfo->GetScriptType( nCnt++ ) ) {
+ case i18n::ScriptType::ASIAN :
+ if( !m_aFontCacheIds[SwFontScript::CJK] ) nTmp = SwFontScript::CJK;
+ break;
+ case i18n::ScriptType::COMPLEX :
+ if( !m_aFontCacheIds[SwFontScript::CTL] ) nTmp = SwFontScript::CTL;
+ break;
+ default:
+ if( !m_aFontCacheIds[SwFontScript::Latin ] ) nTmp = SwFontScript::Latin;
+ }
+ if( nTmp < SW_SCRIPTS )
+ {
+ m_pFont->CheckFontCacheId( m_pViewShell, nTmp );
+ m_pFont->GetFontCacheId( m_aFontCacheIds[ nTmp ], m_aFontIdx[ nTmp ], nTmp );
+ }
+ }
+ while (nChg < TextFrameIndex(rText.getLength()));
+}
+
+void SwAttrIter::CtorInitAttrIter(SwTextNode & rTextNode,
+ SwScriptInfo & rScriptInfo, SwTextFrame const*const pFrame)
+{
+ // during HTML-Import it can happen, that no layout exists
+ SwRootFrame* pRootFrame = rTextNode.getIDocumentLayoutAccess().GetCurrentLayout();
+ m_pViewShell = pRootFrame ? pRootFrame->GetCurrShell() : nullptr;
+
+ m_pScriptInfo = &rScriptInfo;
+
+ // set font to vertical if frame layout is vertical
+ bool bVertLayout = false;
+ bool bVertLayoutLRBT = false;
+ bool bRTL = false;
+ if ( pFrame )
+ {
+ if ( pFrame->IsVertical() )
+ {
+ bVertLayout = true;
+ }
+ if (pFrame->IsVertLRBT())
+ {
+ bVertLayoutLRBT = true;
+ }
+ bRTL = pFrame->IsRightToLeft();
+ m_pMergedPara = pFrame->GetMergedPara();
+ }
+
+ // determine script changes if not already done for current paragraph
+ assert(m_pScriptInfo);
+ if (m_pScriptInfo->GetInvalidityA() != TextFrameIndex(COMPLETE_STRING))
+ m_pScriptInfo->InitScriptInfo(rTextNode, m_pMergedPara, bRTL);
+
+ InitFontAndAttrHandler(
+ m_pMergedPara ? *m_pMergedPara->pParaPropsNode : rTextNode,
+ rTextNode,
+ m_pMergedPara ? m_pMergedPara->mergedText : rTextNode.GetText(),
+ & bVertLayout,
+ & bVertLayoutLRBT);
+
+ m_nStartIndex = m_nEndIndex = m_nPosition = m_nChgCnt = 0;
+ m_nPropFont = 0;
+ SwDoc& rDoc = rTextNode.GetDoc();
+ const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess();
+
+ // sw_redlinehide: this is a Ring - pExtInp is the first PaM that's inside
+ // the node. It's not clear whether there can be more than 1 PaM in the
+ // Ring, and this code doesn't handle that case; neither did the old code.
+ const SwExtTextInput* pExtInp = rDoc.GetExtTextInput( rTextNode );
+ if (!pExtInp && m_pMergedPara)
+ {
+ SwTextNode const* pNode(&rTextNode);
+ for (auto const& rExtent : m_pMergedPara->extents)
+ {
+ if (rExtent.pNode != pNode)
+ {
+ pNode = rExtent.pNode;
+ pExtInp = rDoc.GetExtTextInput(*pNode);
+ if (pExtInp)
+ break;
+ }
+ }
+ }
+ const bool bShow = IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
+ && pRootFrame && !pRootFrame->IsHideRedlines();
+ if (!(pExtInp || m_pMergedPara || bShow))
+ return;
+
+ SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any );
+ if (SwRedlineTable::npos == nRedlPos && m_pMergedPara)
+ {
+ SwTextNode const* pNode(&rTextNode);
+ for (auto const& rExtent : m_pMergedPara->extents)
+ { // note: have to search because extents based only on Delete
+ if (rExtent.pNode != pNode)
+ {
+ pNode = rExtent.pNode;
+ nRedlPos = rIDRA.GetRedlinePos(*pNode, RedlineType::Any);
+ if (SwRedlineTable::npos != nRedlPos)
+ break;
+ }
+ }
+ // TODO this is true initially but after delete ops it may be false... need to delete m_pMerged somewhere?
+ // assert(SwRedlineTable::npos != nRedlPos);
+ // false now with fieldmarks
+ assert(!pRootFrame
+ || pRootFrame->GetFieldmarkMode() != sw::FieldmarkMode::ShowBoth
+ || SwRedlineTable::npos != nRedlPos || m_pMergedPara->extents.size() <= 1);
+ }
+ if (!(pExtInp || m_pMergedPara || SwRedlineTable::npos != nRedlPos))
+ return;
+
+ const std::vector<ExtTextInputAttr> *pArr = nullptr;
+ if( pExtInp )
+ {
+ pArr = &pExtInp->GetAttrs();
+ Seek( TextFrameIndex(0) );
+ }
+
+ m_pRedline.reset(new SwRedlineItr( rTextNode, *m_pFont, m_aAttrHandler, nRedlPos,
+ (pRootFrame && pRootFrame->IsHideRedlines())
+ ? SwRedlineItr::Mode::Hide
+ : bShow
+ ? SwRedlineItr::Mode::Show
+ : SwRedlineItr::Mode::Ignore,
+ pArr, pExtInp ? pExtInp->Start() : nullptr));
+
+ if( m_pRedline->IsOn() )
+ ++m_nChgCnt;
+}
+
+// The Redline-Iterator
+// The following information/states exist in RedlineIterator:
+//
+// m_nFirst is the first index of RedlineTable, which overlaps with the paragraph.
+//
+// m_nAct is the currently active (if m_bOn is set) or the next possible index.
+// m_nStart and m_nEnd give you the borders of the object within the paragraph.
+//
+// If m_bOn is set, the font has been manipulated according to it.
+//
+// If m_nAct is set to SwRedlineTable::npos (via Reset()), then currently no
+// Redline is active, m_nStart and m_nEnd are invalid.
+SwRedlineItr::SwRedlineItr( const SwTextNode& rTextNd, SwFont& rFnt,
+ SwAttrHandler& rAH, sal_Int32 nRed,
+ Mode const mode,
+ const std::vector<ExtTextInputAttr> *pArr,
+ SwPosition const*const pExtInputStart)
+ : m_rDoc( rTextNd.GetDoc() )
+ , m_rAttrHandler( rAH )
+ , m_nNdIdx( rTextNd.GetIndex() )
+ , m_nFirst( nRed )
+ , m_nAct( SwRedlineTable::npos )
+ , m_bOn( false )
+ , m_eMode( mode )
+{
+ if( pArr )
+ {
+ assert(pExtInputStart);
+ m_pExt.reset( new SwExtend(*pArr, pExtInputStart->nNode.GetIndex(),
+ pExtInputStart->nContent.GetIndex()) );
+ }
+ else
+ m_pExt = nullptr;
+ assert(m_pExt || m_eMode != Mode::Ignore); // only create if necessary
+ Seek(rFnt, m_nNdIdx, 0, COMPLETE_STRING);
+}
+
+SwRedlineItr::~SwRedlineItr() COVERITY_NOEXCEPT_FALSE
+{
+ Clear( nullptr );
+ m_pExt.reset();
+}
+
+// The return value of SwRedlineItr::Seek tells you if the current font
+// has been manipulated by leaving (-1) or accessing (+1) of a section
+short SwRedlineItr::Seek(SwFont& rFnt,
+ SwNodeOffset const nNode, sal_Int32 const nNew, sal_Int32 const nOld)
+{
+ short nRet = 0;
+ if( ExtOn() )
+ return 0; // Abbreviation: if we're within an ExtendTextInputs
+ // there can't be other changes of attributes (not even by redlining)
+ if (m_eMode == Mode::Show)
+ {
+ if (m_bOn)
+ {
+ if (nNew >= m_nEnd)
+ {
+ --nRet;
+ Clear_( &rFnt ); // We go behind the current section
+ ++m_nAct; // and check the next one
+ }
+ else if (nNew < m_nStart)
+ {
+ --nRet;
+ Clear_( &rFnt ); // We go in front of the current section
+ if (m_nAct > m_nFirst)
+ m_nAct = m_nFirst; // the test has to run from the beginning
+ else
+ return nRet + EnterExtend(rFnt, nNode, nNew); // There's none prior to us
+ }
+ else
+ return nRet + EnterExtend(rFnt, nNode, nNew); // We stayed in the same section
+ }
+ if (SwRedlineTable::npos == m_nAct || nOld > nNew)
+ m_nAct = m_nFirst;
+
+ m_nStart = COMPLETE_STRING;
+ m_nEnd = COMPLETE_STRING;
+ const SwRedlineTable& rTable = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+
+ for ( ; m_nAct < rTable.size() ; ++m_nAct)
+ {
+ rTable[ m_nAct ]->CalcStartEnd(nNode, m_nStart, m_nEnd);
+
+ if (nNew < m_nEnd)
+ {
+ if (nNew >= m_nStart) // only possible candidate
+ {
+ m_bOn = true;
+ const SwRangeRedline *pRed = rTable[ m_nAct ];
+
+ if (m_pSet)
+ m_pSet->ClearItem();
+ else
+ {
+ SwAttrPool& rPool =
+ const_cast<SwDoc&>(m_rDoc).GetAttrPool();
+ m_pSet = std::make_unique<SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1>>(rPool);
+ }
+
+ if( 1 < pRed->GetStackCount() )
+ FillHints( pRed->GetAuthor( 1 ), pRed->GetType( 1 ) );
+ FillHints( pRed->GetAuthor(), pRed->GetType() );
+
+ SfxWhichIter aIter( *m_pSet );
+
+ // moved text: dark green with double underline or strikethrough
+ if ( pRed->IsMoved() )
+ {
+ m_pSet->Put(SvxColorItem( COL_GREEN, RES_CHRATR_COLOR ));
+ if (SfxItemState::SET == m_pSet->GetItemState(RES_CHRATR_CROSSEDOUT, true))
+ m_pSet->Put(SvxCrossedOutItem( STRIKEOUT_DOUBLE, RES_CHRATR_CROSSEDOUT ));
+ else
+ m_pSet->Put(SvxUnderlineItem( LINESTYLE_DOUBLE, RES_CHRATR_UNDERLINE ));
+ }
+
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ const SfxPoolItem* pItem;
+ if( ( nWhich < RES_CHRATR_END ) &&
+ ( SfxItemState::SET == aIter.GetItemState( true, &pItem ) ) )
+ {
+ SwTextAttr* pAttr = MakeRedlineTextAttr(
+ const_cast<SwDoc&>(m_rDoc),
+ *const_cast<SfxPoolItem*>(pItem) );
+ pAttr->SetPriorityAttr( true );
+ m_Hints.push_back(pAttr);
+ m_rAttrHandler.PushAndChg( *pAttr, rFnt );
+ }
+ nWhich = aIter.NextWhich();
+ }
+
+ ++nRet;
+ }
+ break;
+ }
+ m_nStart = COMPLETE_STRING;
+ m_nEnd = COMPLETE_STRING;
+ }
+ }
+ else if (m_eMode == Mode::Hide)
+ { // ... just iterate to update m_nAct for GetNextRedln();
+ // there is no need to care about formatting in this mode
+ if (m_nAct == SwRedlineTable::npos || nOld == COMPLETE_STRING)
+ { // reset, or move backward
+ m_nAct = m_nFirst;
+ }
+ for ( ; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++m_nAct)
+ { // only Start matters in this mode
+ // Seeks until it finds a RL that starts at or behind the seek pos.
+ // - then update m_nStart/m_nEnd to the intersection of it with the
+ // current node (if any).
+ // The only way to skip to a different node is if there is a Delete
+ // RL, so if there is no intersection we'll never skip again.
+ // Note: here, assume that delete can't nest inside delete!
+ SwRangeRedline const*const pRedline(
+ m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[m_nAct]);
+ SwPosition const*const pStart(pRedline->Start());
+ if (pRedline->GetType() == RedlineType::Delete
+ && (nNode < pStart->nNode.GetIndex()
+ || (nNode == pStart->nNode.GetIndex()
+ && nNew <= pStart->nContent.GetIndex())))
+ {
+ pRedline->CalcStartEnd(nNode, m_nStart, m_nEnd);
+ break;
+ }
+ m_nStart = COMPLETE_STRING;
+ m_nEnd = COMPLETE_STRING;
+ }
+ }
+ return nRet + EnterExtend(rFnt, nNode, nNew);
+}
+
+void SwRedlineItr::FillHints( std::size_t nAuthor, RedlineType eType )
+{
+ switch ( eType )
+ {
+ case RedlineType::Insert:
+ SW_MOD()->GetInsertAuthorAttr(nAuthor, *m_pSet);
+ break;
+ case RedlineType::Delete:
+ SW_MOD()->GetDeletedAuthorAttr(nAuthor, *m_pSet);
+ break;
+ case RedlineType::Format:
+ case RedlineType::FmtColl:
+ SW_MOD()->GetFormatAuthorAttr(nAuthor, *m_pSet);
+ break;
+ default:
+ break;
+ }
+}
+
+void SwRedlineItr::ChangeTextAttr( SwFont* pFnt, SwTextAttr const &rHt, bool bChg )
+{
+ OSL_ENSURE( IsOn(), "SwRedlineItr::ChangeTextAttr: Off?" );
+
+ if (m_eMode != Mode::Show && !m_pExt)
+ return;
+
+ if( bChg )
+ {
+ if (m_pExt && m_pExt->IsOn())
+ m_rAttrHandler.PushAndChg( rHt, *m_pExt->GetFont() );
+ else
+ m_rAttrHandler.PushAndChg( rHt, *pFnt );
+ }
+ else
+ {
+ OSL_ENSURE( ! m_pExt || ! m_pExt->IsOn(), "Pop of attribute during opened extension" );
+ m_rAttrHandler.PopAndChg( rHt, *pFnt );
+ }
+}
+
+void SwRedlineItr::Clear_( SwFont* pFnt )
+{
+ OSL_ENSURE( m_bOn, "SwRedlineItr::Clear: Off?" );
+ m_bOn = false;
+ for (auto const& hint : m_Hints)
+ {
+ if( pFnt )
+ m_rAttrHandler.PopAndChg( *hint, *pFnt );
+ else
+ m_rAttrHandler.Pop( *hint );
+ SwTextAttr::Destroy(hint, const_cast<SwDoc&>(m_rDoc).GetAttrPool() );
+ }
+ m_Hints.clear();
+}
+
+/// Ignore mode: does nothing.
+/// Show mode: returns end of redline if currently in one, or start of next
+/// Hide mode: returns start of next redline in current node, plus (if it's a
+/// Delete) its end position and number of consecutive RLs
+std::pair<sal_Int32, std::pair<SwRangeRedline const*, size_t>>
+SwRedlineItr::GetNextRedln(sal_Int32 nNext, SwTextNode const*const pNode,
+ SwRedlineTable::size_type & rAct)
+{
+ sal_Int32 nStart(m_nStart);
+ sal_Int32 nEnd(m_nEnd);
+ nNext = NextExtend(pNode->GetIndex(), nNext);
+ if (m_eMode == Mode::Ignore || SwRedlineTable::npos == m_nFirst)
+ return std::make_pair(nNext, std::make_pair(nullptr, 0));
+ if (SwRedlineTable::npos == rAct)
+ {
+ rAct = m_nFirst;
+ }
+ if (rAct != m_nAct)
+ {
+ while (rAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())
+ {
+ SwRangeRedline const*const pRedline(
+ m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]);
+ pRedline->CalcStartEnd(pNode->GetIndex(), nStart, nEnd);
+ if (m_eMode != Mode::Hide
+ || pRedline->GetType() == RedlineType::Delete)
+ {
+ break;
+ }
+ ++rAct; // Hide mode: search a Delete RL
+ }
+ }
+ if (rAct == m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())
+ {
+ return std::make_pair(nNext, std::make_pair(nullptr, 0)); // no Delete here
+ }
+ if (m_bOn || (m_eMode == Mode::Show && nStart == 0))
+ { // in Ignore mode, the end of redlines isn't relevant, except as returned in the second in the pair!
+ if (nEnd < nNext)
+ nNext = nEnd;
+ }
+ else if (nStart <= nNext)
+ {
+ if (m_eMode == Mode::Show)
+ {
+ nNext = nStart;
+ }
+ else
+ {
+ assert(m_eMode == Mode::Hide);
+ SwRangeRedline const* pRedline(
+ m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct]);
+ assert(pRedline->GetType() == RedlineType::Delete); //?
+ if (pRedline->GetType() == RedlineType::Delete)
+ {
+ nNext = nStart;
+ size_t nSkipped(1); // (consecutive) candidates to be skipped
+ while (rAct + nSkipped <
+ m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size())
+ {
+ SwRangeRedline const*const pNext =
+ m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[rAct + nSkipped];
+ if (*pRedline->End() < *pNext->Start())
+ {
+ break; // done for now
+ }
+ else if (*pNext->Start() == *pRedline->End() &&
+ pNext->GetType() == RedlineType::Delete)
+ {
+ // consecutive delete - continue
+ pRedline = pNext;
+ }
+ ++nSkipped;
+ }
+ return std::make_pair(nNext, std::make_pair(pRedline, nSkipped));
+ }
+ }
+ }
+ return std::make_pair(nNext, std::make_pair(nullptr, 0));
+}
+
+bool SwRedlineItr::ChkSpecialUnderline_() const
+{
+ // If the underlining or the escapement is caused by redlining,
+ // we always apply the SpecialUnderlining, i.e. the underlining
+ // below the base line
+ for (SwTextAttr* pHint : m_Hints)
+ {
+ const sal_uInt16 nWhich = pHint->Which();
+ if( RES_CHRATR_UNDERLINE == nWhich ||
+ RES_CHRATR_ESCAPEMENT == nWhich )
+ return true;
+ }
+ return false;
+}
+
+bool SwRedlineItr::CheckLine(
+ SwNodeOffset const nStartNode, sal_Int32 const nChkStart,
+ SwNodeOffset const nEndNode, sal_Int32 nChkEnd, OUString& rRedlineText,
+ bool& bRedlineEnd, RedlineType& eRedlineEnd, size_t* pAuthorAtPos)
+{
+ // note: previously this would return true in the (!m_bShow && m_pExt)
+ // case, but surely that was a bug?
+ if (m_nFirst == SwRedlineTable::npos || m_eMode != Mode::Show)
+ return false;
+ if( nChkEnd == nChkStart && pAuthorAtPos == nullptr ) // empty lines look one char further
+ ++nChkEnd;
+ sal_Int32 nOldStart = m_nStart;
+ sal_Int32 nOldEnd = m_nEnd;
+ SwRedlineTable::size_type const nOldAct = m_nAct;
+ bool bRet = bRedlineEnd = false;
+ eRedlineEnd = RedlineType::None;
+
+ SwPosition const start(*m_rDoc.GetNodes()[nStartNode]->GetContentNode(), nChkStart);
+ SwPosition const end(*m_rDoc.GetNodes()[nEndNode]->GetContentNode(), nChkEnd);
+ SwRangeRedline const* pPrevRedline = nullptr;
+ bool isBreak(false);
+ for (m_nAct = m_nFirst; m_nAct < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++m_nAct)
+ {
+ SwRangeRedline const*const pRedline(
+ m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ m_nAct ] );
+ // collect text of the hidden redlines at the end of the line
+ bool isExtendText(false);
+ switch (ComparePosition(*pRedline->Start(), *pRedline->End(), start, end))
+ {
+ case SwComparePosition::Behind:
+ isBreak = true;
+ break;
+ case SwComparePosition::OverlapBehind:
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ // store redlining at line end (for line break formatting)
+ eRedlineEnd = pRedline->GetType();
+ bRedlineEnd = true;
+ isBreak = true;
+ if (pAuthorAtPos)
+ *pAuthorAtPos = pRedline->GetAuthor();
+ [[fallthrough]];
+ case SwComparePosition::OverlapBefore:
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::Inside:
+ {
+ bRet = true;
+ // start to collect text of invisible redlines for ChangesInMargin layout
+ if (rRedlineText.isEmpty() && !pRedline->IsVisible())
+ {
+ rRedlineText = const_cast<SwRangeRedline*>(pRedline)->GetDescr(/*bSimplified=*/true);
+ pPrevRedline = pRedline;
+ isExtendText = true;
+ }
+ // join the text of the next invisible redlines in the same position
+ // i.e. characters deleted by pressing backspace or delete
+ else if (pPrevRedline && !pRedline->IsVisible() &&
+ *pRedline->Start() == *pPrevRedline->Start() && *pRedline->End() == *pPrevRedline->End() )
+ {
+ OUString sExtendText(const_cast<SwRangeRedline*>(pRedline)->GetDescr(/*bSimplified=*/true));
+ if (!sExtendText.isEmpty())
+ {
+ if (rRedlineText.getLength() < 12)
+ {
+ // TODO: remove extra space from GetDescr(true),
+ // but show deletion of paragraph or line break
+ rRedlineText = rRedlineText +
+ const_cast<SwRangeRedline*>(pRedline)->GetDescr(/*bSimplified=*/true).subView(1);
+ }
+ else
+ rRedlineText = OUString::Concat(rRedlineText.subView(0, rRedlineText.getLength() - 3)) + "...";
+ }
+ isExtendText = true;
+ }
+ break;
+ }
+ case SwComparePosition::Before:
+ break; // -Werror=switch
+ }
+ if (isBreak && !isExtendText)
+ {
+ break;
+ }
+ }
+
+ m_nStart = nOldStart;
+ m_nEnd = nOldEnd;
+ m_nAct = nOldAct;
+ return bRet;
+}
+
+void SwExtend::ActualizeFont( SwFont &rFnt, ExtTextInputAttr nAttr )
+{
+ if ( nAttr & ExtTextInputAttr::Underline )
+ rFnt.SetUnderline( LINESTYLE_SINGLE );
+ else if ( nAttr & ExtTextInputAttr::DoubleUnderline )
+ rFnt.SetUnderline( LINESTYLE_DOUBLE );
+ else if ( nAttr & ExtTextInputAttr::BoldUnderline )
+ rFnt.SetUnderline( LINESTYLE_BOLD );
+ else if ( nAttr & ExtTextInputAttr::DottedUnderline )
+ rFnt.SetUnderline( LINESTYLE_DOTTED );
+ else if ( nAttr & ExtTextInputAttr::DashDotUnderline )
+ rFnt.SetUnderline( LINESTYLE_DOTTED );
+
+ if ( nAttr & ExtTextInputAttr::RedText )
+ rFnt.SetColor( COL_RED );
+
+ if ( nAttr & ExtTextInputAttr::Highlight )
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ rFnt.SetColor( rStyleSettings.GetHighlightTextColor() );
+ rFnt.SetBackColor( rStyleSettings.GetHighlightColor() );
+ }
+ if ( nAttr & ExtTextInputAttr::GrayWaveline )
+ rFnt.SetGreyWave( true );
+}
+
+short SwExtend::Enter(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
+{
+ OSL_ENSURE( !m_pFont, "SwExtend: Enter with Font" );
+ if (nNode != m_nNode)
+ return 0;
+ OSL_ENSURE( !Inside(), "SwExtend: Enter without Leave" );
+ m_nPos = nNew;
+ if( Inside() )
+ {
+ m_pFont.reset( new SwFont(rFnt) );
+ ActualizeFont( rFnt, m_rArr[m_nPos - m_nStart] );
+ return 1;
+ }
+ return 0;
+}
+
+bool SwExtend::Leave_(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
+{
+ OSL_ENSURE(nNode == m_nNode && Inside(), "SwExtend: Leave without Enter");
+ if (nNode != m_nNode)
+ return true;
+ const ExtTextInputAttr nOldAttr = m_rArr[m_nPos - m_nStart];
+ m_nPos = nNew;
+ if( Inside() )
+ { // We stayed within the ExtendText-section
+ const ExtTextInputAttr nAttr = m_rArr[m_nPos - m_nStart];
+ if( nOldAttr != nAttr ) // Is there an (inner) change of attributes?
+ {
+ rFnt = *m_pFont;
+ ActualizeFont( rFnt, nAttr );
+ }
+ }
+ else
+ {
+ rFnt = *m_pFont;
+ m_pFont.reset();
+ return true;
+ }
+ return false;
+}
+
+sal_Int32 SwExtend::Next(SwNodeOffset const nNode, sal_Int32 nNext)
+{
+ if (nNode != m_nNode)
+ return nNext;
+ if (m_nPos < m_nStart)
+ {
+ if (nNext > m_nStart)
+ nNext = m_nStart;
+ }
+ else if (m_nPos < m_nEnd)
+ {
+ sal_Int32 nIdx = m_nPos - m_nStart;
+ const ExtTextInputAttr nAttr = m_rArr[ nIdx ];
+ while (o3tl::make_unsigned(++nIdx) < m_rArr.size() && nAttr == m_rArr[nIdx])
+ ; //nothing
+ nIdx = nIdx + m_nStart;
+ if( nNext > nIdx )
+ nNext = nIdx;
+ }
+ return nNext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/redlnitr.hxx b/sw/source/core/text/redlnitr.hxx
new file mode 100644
index 000000000..5c3013126
--- /dev/null
+++ b/sw/source/core/text/redlnitr.hxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <docary.hxx>
+#include <swfont.hxx>
+
+#include <vcl/commandevent.hxx>
+
+#include <cstddef>
+#include <deque>
+#include <memory>
+#include <vector>
+
+class SwTextNode;
+class SwDoc;
+class SfxItemSet;
+class SwAttrHandler;
+
+class SwExtend
+{
+ std::unique_ptr<SwFont> m_pFont;
+ const std::vector<ExtTextInputAttr> &m_rArr;
+ /// position of start of SwExtTextInput
+ SwNodeOffset const m_nNode;
+ sal_Int32 const m_nStart;
+ /// current position (inside)
+ sal_Int32 m_nPos;
+ /// position of end of SwExtTextInput (in same node as start)
+ sal_Int32 const m_nEnd;
+ bool Leave_(SwFont& rFnt, SwNodeOffset nNode, sal_Int32 nNew);
+ bool Inside() const { return (m_nPos >= m_nStart && m_nPos < m_nEnd); }
+ static void ActualizeFont( SwFont &rFnt, ExtTextInputAttr nAttr );
+public:
+ SwExtend(const std::vector<ExtTextInputAttr> &rArr,
+ SwNodeOffset const nNode, sal_Int32 const nStart)
+ : m_rArr(rArr)
+ , m_nNode(nNode)
+ , m_nStart(nStart)
+ , m_nPos(COMPLETE_STRING)
+ , m_nEnd(m_nStart + rArr.size())
+ {}
+ bool IsOn() const { return m_pFont != nullptr; }
+ void Reset() { m_pFont.reset(); m_nPos = COMPLETE_STRING; }
+ bool Leave(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
+ { return m_pFont && Leave_(rFnt, nNode, nNew); }
+ short Enter(SwFont& rFnt, SwNodeOffset nNode, sal_Int32 nNew);
+ sal_Int32 Next(SwNodeOffset nNode, sal_Int32 nNext);
+ SwFont* GetFont() { return m_pFont.get(); }
+ void UpdateFont(SwFont &rFont) { ActualizeFont(rFont, m_rArr[m_nPos - m_nStart]); }
+};
+
+class SwRedlineItr
+{
+ std::deque<SwTextAttr *> m_Hints;
+ const SwDoc& m_rDoc;
+ SwAttrHandler& m_rAttrHandler;
+ std::unique_ptr<SfxItemSet> m_pSet;
+ std::unique_ptr<SwExtend> m_pExt;
+ // note: this isn't actually used in the merged-para (Hide) case
+ SwNodeOffset const m_nNdIdx;
+ SwRedlineTable::size_type const m_nFirst;
+ SwRedlineTable::size_type m_nAct;
+ sal_Int32 m_nStart;
+ sal_Int32 m_nEnd;
+ bool m_bOn;
+public:
+ enum class Mode { Show, Ignore, Hide };
+private:
+ Mode const m_eMode;
+
+ void Clear_( SwFont* pFnt );
+ bool ChkSpecialUnderline_() const;
+ void FillHints( std::size_t nAuthor, RedlineType eType );
+ short EnterExtend(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
+ {
+ if (m_pExt) return m_pExt->Enter(rFnt, nNode, nNew);
+ return 0;
+ }
+ sal_Int32 NextExtend(SwNodeOffset const nNode, sal_Int32 const nNext) {
+ if (m_pExt) return m_pExt->Next(nNode, nNext);
+ return nNext;
+ }
+public:
+ SwRedlineItr( const SwTextNode& rTextNd, SwFont& rFnt, SwAttrHandler& rAH,
+ sal_Int32 nRedlPos, Mode mode,
+ const std::vector<ExtTextInputAttr> *pArr = nullptr,
+ SwPosition const* pExtInputStart = nullptr);
+ ~SwRedlineItr() COVERITY_NOEXCEPT_FALSE;
+ SwRedlineTable::size_type GetAct() const { return m_nAct; }
+ bool IsOn() const { return m_bOn || (m_pExt && m_pExt->IsOn()); }
+ void Clear( SwFont* pFnt ) { if (m_bOn) Clear_( pFnt ); }
+ void ChangeTextAttr( SwFont* pFnt, SwTextAttr const &rHt, bool bChg );
+ short Seek(SwFont& rFnt, SwNodeOffset nNode, sal_Int32 nNew, sal_Int32 nOld);
+ void Reset() {
+ if (m_nAct != m_nFirst) m_nAct = SwRedlineTable::npos;
+ if (m_pExt) m_pExt->Reset();
+ }
+ std::pair<sal_Int32, std::pair<SwRangeRedline const*, size_t>> GetNextRedln(
+ sal_Int32 nNext, SwTextNode const* pNode, SwRedlineTable::size_type & rAct);
+ bool ChkSpecialUnderline() const
+ { return IsOn() && ChkSpecialUnderline_(); }
+ bool CheckLine(SwNodeOffset nStartNode, sal_Int32 nChkStart, SwNodeOffset nEndNode,
+ sal_Int32 nChkEnd, OUString& rRedlineText, bool& bRedlineEnd,
+ RedlineType& eRedlineEnd, size_t* pAuthorAtPos = nullptr);
+ bool LeaveExtend(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const nNew)
+ { return m_pExt->Leave(rFnt, nNode, nNew); }
+ bool ExtOn() {
+ if (m_pExt) return m_pExt->IsOn();
+ return false;
+ }
+ void UpdateExtFont( SwFont &rFnt ) {
+ OSL_ENSURE( ExtOn(), "UpdateExtFont without ExtOn" );
+ m_pExt->UpdateFont( rFnt );
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtcache.cxx b/sw/source/core/text/txtcache.cxx
new file mode 100644
index 000000000..188fa7bec
--- /dev/null
+++ b/sw/source/core/text/txtcache.cxx
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "txtcache.hxx"
+#include <txtfrm.hxx>
+#include "porlay.hxx"
+
+#include <sfx2/viewsh.hxx>
+#include <osl/diagnose.h>
+#include <view.hxx>
+
+SwTextLine::SwTextLine( SwTextFrame const *pFrame, std::unique_ptr<SwParaPortion> pNew ) :
+ SwCacheObj( static_cast<void const *>(pFrame) ),
+ m_pLine( std::move(pNew) )
+{
+}
+
+SwTextLine::~SwTextLine()
+{
+}
+
+void SwTextLine::UpdateCachePos()
+{
+ // note: SwTextFrame lives longer than its SwTextLine, see ~SwTextFrame
+ assert(m_pOwner);
+ const_cast<SwTextFrame *>(static_cast<SwTextFrame const *>(m_pOwner))->SetCacheIdx(GetCachePos());
+}
+
+SwCacheObj *SwTextLineAccess::NewObj()
+{
+ return new SwTextLine( static_cast<SwTextFrame const *>(m_pOwner) );
+}
+
+SwParaPortion *SwTextLineAccess::GetPara()
+{
+ SwTextLine *pRet;
+ if ( m_pObj )
+ pRet = static_cast<SwTextLine*>(m_pObj);
+ else
+ {
+ pRet = static_cast<SwTextLine*>(Get(false));
+ const_cast<SwTextFrame *>(static_cast<SwTextFrame const *>(m_pOwner))->SetCacheIdx( pRet->GetCachePos() );
+ }
+ if ( !pRet->GetPara() )
+ pRet->SetPara( new SwParaPortion, true/*bDelete*/ );
+ return pRet->GetPara();
+}
+
+SwTextLineAccess::SwTextLineAccess( const SwTextFrame *pOwn ) :
+ SwCacheAccess( *SwTextFrame::GetTextCache(), pOwn, pOwn->GetCacheIdx() )
+{
+}
+
+bool SwTextLineAccess::IsAvailable() const
+{
+ return m_pObj && static_cast<SwTextLine*>(m_pObj)->GetPara();
+}
+
+bool SwTextFrame::HasPara_() const
+{
+ SwTextLine *pTextLine = static_cast<SwTextLine*>(SwTextFrame::GetTextCache()->
+ Get( this, GetCacheIdx(), false ));
+ if ( pTextLine )
+ {
+ if ( pTextLine->GetPara() )
+ return true;
+ }
+ else
+ const_cast<SwTextFrame*>(this)->mnCacheIndex = USHRT_MAX;
+
+ return false;
+}
+
+SwParaPortion *SwTextFrame::GetPara()
+{
+ if ( GetCacheIdx() != USHRT_MAX )
+ {
+ SwTextLine *pLine = static_cast<SwTextLine*>(SwTextFrame::GetTextCache()->
+ Get( this, GetCacheIdx(), false ));
+ if ( pLine )
+ return pLine->GetPara();
+ else
+ mnCacheIndex = USHRT_MAX;
+ }
+ return nullptr;
+}
+
+void SwTextFrame::ClearPara()
+{
+ OSL_ENSURE( !IsLocked(), "+SwTextFrame::ClearPara: this is locked." );
+ if ( !IsLocked() && GetCacheIdx() != USHRT_MAX )
+ {
+ SwTextLine *pTextLine = static_cast<SwTextLine*>(SwTextFrame::GetTextCache()->
+ Get( this, GetCacheIdx(), false ));
+ if ( pTextLine )
+ {
+ pTextLine->SetPara( nullptr, true/*bDelete*/ );
+ }
+ else
+ mnCacheIndex = USHRT_MAX;
+ }
+}
+
+void SwTextFrame::RemoveFromCache()
+{
+ if (GetCacheIdx() != USHRT_MAX)
+ {
+ s_pTextCache->Delete(this, GetCacheIdx());
+ SetCacheIdx(USHRT_MAX);
+ }
+}
+
+void SwTextFrame::SetPara( SwParaPortion *pNew, bool bDelete )
+{
+ if ( GetCacheIdx() != USHRT_MAX )
+ {
+ // Only change the information, the CacheObj stays there
+ SwTextLine *pTextLine = static_cast<SwTextLine*>(SwTextFrame::GetTextCache()->
+ Get( this, GetCacheIdx(), false ));
+ if ( pTextLine )
+ {
+ pTextLine->SetPara( pNew, bDelete );
+ }
+ else
+ {
+ OSL_ENSURE( !pNew, "+SetPara: Losing SwParaPortion" );
+ mnCacheIndex = USHRT_MAX;
+ }
+ }
+ else if ( pNew )
+ { // Insert a new one
+ SwTextLine *pTextLine = new SwTextLine( this, std::unique_ptr<SwParaPortion>(pNew) );
+ if (SwTextFrame::GetTextCache()->Insert(pTextLine, false))
+ mnCacheIndex = pTextLine->GetCachePos();
+ else
+ {
+ OSL_FAIL( "+SetPara: InsertCache failed." );
+ }
+ }
+}
+
+/** Prevent the SwParaPortions of the *visible* paragraphs from being deleted;
+ they would just be recreated on the next paint.
+
+ Heuristic: 100 per view are visible
+
+ If the cache is too small, enlarge it to ensure there are sufficient free
+ entries for the layout so it doesn't have to throw away a node's
+ SwParaPortion when it starts formatting the next node.
+*/
+SwSaveSetLRUOfst::SwSaveSetLRUOfst()
+{
+ sal_uInt16 nVisibleShells(0);
+ for (auto pView = SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>);
+ pView != nullptr;
+ pView = SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>))
+ {
+ // Apparently we are not interested here what document pView is for, but only in the
+ // total number of shells in the process?
+ ++nVisibleShells;
+ }
+
+ sal_uInt16 const nPreserved(100 * nVisibleShells);
+ SwCache & rCache(*SwTextFrame::GetTextCache());
+ if (rCache.GetCurMax() < nPreserved + 250)
+ {
+ rCache.IncreaseMax(nPreserved + 250 - rCache.GetCurMax());
+ }
+ rCache.SetLRUOfst(nPreserved);
+}
+
+SwSaveSetLRUOfst::~SwSaveSetLRUOfst()
+{
+ SwTextFrame::GetTextCache()->ResetLRUOfst();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtcache.hxx b/sw/source/core/text/txtcache.hxx
new file mode 100644
index 000000000..e71be076f
--- /dev/null
+++ b/sw/source/core/text/txtcache.hxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <swcache.hxx>
+#include "porlay.hxx"
+#include <memory>
+
+class SwTextFrame;
+
+class SwTextLine : public SwCacheObj
+{
+ std::unique_ptr<SwParaPortion> m_pLine;
+
+ virtual void UpdateCachePos() override;
+
+public:
+ SwTextLine(SwTextFrame const* pFrame, std::unique_ptr<SwParaPortion> pNew = nullptr);
+ virtual ~SwTextLine() override;
+
+ SwParaPortion* GetPara() { return m_pLine.get(); }
+ const SwParaPortion* GetPara() const { return m_pLine.get(); }
+
+ void SetPara(SwParaPortion* pNew, bool bDelete)
+ {
+ if (!bDelete)
+ (void)m_pLine.release();
+ m_pLine.reset(pNew);
+ }
+};
+
+class SwTextLineAccess : public SwCacheAccess
+{
+protected:
+ virtual SwCacheObj* NewObj() override;
+
+public:
+ explicit SwTextLineAccess(const SwTextFrame* pOwner);
+
+ SwParaPortion* GetPara();
+
+ bool IsAvailable() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtdrop.cxx b/sw/source/core/text/txtdrop.cxx
new file mode 100644
index 000000000..9f6d94a8c
--- /dev/null
+++ b/sw/source/core/text/txtdrop.cxx
@@ -0,0 +1,1079 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <vcl/svapp.hxx>
+#include <paratr.hxx>
+#include <txtfrm.hxx>
+#include <charfmt.hxx>
+#include <viewopt.hxx>
+#include <viewsh.hxx>
+#include "pordrop.hxx"
+#include "itrform2.hxx"
+#include "txtpaint.hxx"
+#include <breakit.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <editeng/langitem.hxx>
+#include <charatr.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <calbck.hxx>
+#include <doc.hxx>
+
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star;
+
+/**
+ * Calculates if a drop caps portion intersects with a fly
+ * The width and height of the drop caps portion are passed as arguments,
+ * the position is calculated from the values in rInf
+ */
+static bool lcl_IsDropFlyInter( const SwTextFormatInfo &rInf,
+ sal_uInt16 nWidth, sal_uInt16 nHeight )
+{
+ const SwTextFly& rTextFly = rInf.GetTextFly();
+ if( rTextFly.IsOn() )
+ {
+ SwRect aRect( rInf.GetTextFrame()->getFrameArea().Pos(), Size( nWidth, nHeight) );
+ aRect.Pos() += rInf.GetTextFrame()->getFramePrintArea().Pos();
+ aRect.Pos().AdjustX(rInf.X() );
+ aRect.Pos().setY( rInf.Y() );
+ aRect = rTextFly.GetFrame( aRect );
+ return aRect.HasArea();
+ }
+
+ return false;
+}
+
+namespace {
+
+class SwDropSave
+{
+ SwTextPaintInfo* pInf;
+ sal_Int32 nIdx;
+ sal_Int32 nLen;
+ tools::Long nX;
+ tools::Long nY;
+
+public:
+ explicit SwDropSave( const SwTextPaintInfo &rInf );
+ ~SwDropSave();
+};
+
+}
+
+SwDropSave::SwDropSave( const SwTextPaintInfo &rInf ) :
+ pInf( const_cast<SwTextPaintInfo*>(&rInf) ), nIdx( rInf.GetIdx() ),
+ nLen( rInf.GetLen() ), nX( rInf.X() ), nY( rInf.Y() )
+{
+}
+
+SwDropSave::~SwDropSave()
+{
+ pInf->SetIdx(TextFrameIndex(nIdx));
+ pInf->SetLen(TextFrameIndex(nLen));
+ pInf->X( nX );
+ pInf->Y( nY );
+}
+
+/// SwDropPortionPart DTor
+SwDropPortionPart::~SwDropPortionPart()
+{
+ m_pFollow.reset();
+ m_pFnt.reset();
+}
+
+/// SwDropPortion CTor, DTor
+SwDropPortion::SwDropPortion( const sal_uInt16 nLineCnt,
+ const sal_uInt16 nDrpHeight,
+ const sal_uInt16 nDrpDescent,
+ const sal_uInt16 nDist )
+ : m_nLines( nLineCnt ),
+ m_nDropHeight(nDrpHeight),
+ m_nDropDescent(nDrpDescent),
+ m_nDistance(nDist),
+ m_nFix(0),
+ m_nY(0)
+{
+ SetWhichPor( PortionType::Drop );
+}
+
+SwDropPortion::~SwDropPortion()
+{
+ m_pPart.reset();
+}
+
+/// nWishLen = 0 indicates that we want a whole word
+sal_Int32 SwTextNode::GetDropLen( sal_Int32 nWishLen ) const
+{
+ sal_Int32 nEnd = GetText().getLength();
+ if( nWishLen && nWishLen < nEnd )
+ nEnd = nWishLen;
+
+ if (! nWishLen)
+ {
+ // find first word
+ const SwAttrSet& rAttrSet = GetSwAttrSet();
+ const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText( GetText(), 0 );
+
+ LanguageType eLanguage;
+
+ switch ( nTextScript )
+ {
+ case i18n::ScriptType::ASIAN :
+ eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
+ break;
+ case i18n::ScriptType::COMPLEX :
+ eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
+ break;
+ default :
+ eLanguage = rAttrSet.GetLanguage().GetLanguage();
+ break;
+ }
+
+ Boundary aBound =
+ g_pBreakIt->GetBreakIter()->getWordBoundary( GetText(), 0,
+ g_pBreakIt->GetLocale( eLanguage ), WordType::DICTIONARY_WORD, true );
+
+ nEnd = aBound.endPos;
+ }
+
+ sal_Int32 i = 0;
+ for( ; i < nEnd; ++i )
+ {
+ sal_Unicode const cChar = GetText()[i];
+ if( CH_TAB == cChar || CH_BREAK == cChar ||
+ (( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar )
+ && GetTextAttrForCharAt(i)) )
+ break;
+ }
+ return i;
+}
+
+/// nWishLen = 0 indicates that we want a whole word
+TextFrameIndex SwTextFrame::GetDropLen(TextFrameIndex const nWishLen) const
+{
+ TextFrameIndex nEnd(GetText().getLength());
+ if (nWishLen && nWishLen < nEnd)
+ nEnd = nWishLen;
+
+ if (! nWishLen)
+ {
+ // find first word
+ const SwAttrSet& rAttrSet = GetTextNodeForParaProps()->GetSwAttrSet();
+ const sal_uInt16 nTextScript = g_pBreakIt->GetRealScriptOfText(GetText(), 0);
+
+ LanguageType eLanguage;
+
+ switch ( nTextScript )
+ {
+ case i18n::ScriptType::ASIAN :
+ eLanguage = rAttrSet.GetCJKLanguage().GetLanguage();
+ break;
+ case i18n::ScriptType::COMPLEX :
+ eLanguage = rAttrSet.GetCTLLanguage().GetLanguage();
+ break;
+ default :
+ eLanguage = rAttrSet.GetLanguage().GetLanguage();
+ break;
+ }
+
+ Boundary aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ GetText(), 0, g_pBreakIt->GetLocale(eLanguage),
+ WordType::DICTIONARY_WORD, true );
+
+ nEnd = TextFrameIndex(aBound.endPos);
+ }
+
+ TextFrameIndex i(0);
+ for ( ; i < nEnd; ++i)
+ {
+ sal_Unicode const cChar = GetText()[sal_Int32(i)];
+ if (CH_TAB == cChar || CH_BREAK == cChar ||
+ CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
+ {
+#ifndef NDEBUG
+ if (CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
+ {
+ std::pair<SwTextNode const*, sal_Int32> const pos(MapViewToModel(i));
+ assert(pos.first->GetTextAttrForCharAt(pos.second) != nullptr);
+ }
+#endif
+ break;
+ }
+ }
+ return i;
+}
+
+/**
+ * If a dropcap is found the return value is true otherwise false. The
+ * drop cap sizes passed back by reference are font height, drop height
+ * and drop descent.
+ */
+bool SwTextNode::GetDropSize(int& rFontHeight, int& rDropHeight, int& rDropDescent) const
+{
+ rFontHeight = 0;
+ rDropHeight = 0;
+ rDropDescent =0;
+
+ const SwAttrSet& rSet = GetSwAttrSet();
+ const SwFormatDrop& rDrop = rSet.GetDrop();
+
+ // Return (0,0) if there is no drop cap at this paragraph
+ if( 1 >= rDrop.GetLines() ||
+ ( !rDrop.GetChars() && !rDrop.GetWholeWord() ) )
+ {
+ return false;
+ }
+
+ // get text frame
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for( SwTextFrame* pLastFrame = aIter.First(); pLastFrame; pLastFrame = aIter.Next() )
+ {
+ // Only (master-) text frames can have a drop cap.
+ if (!pLastFrame->IsFollow() &&
+ pLastFrame->GetTextNodeForFirstText() == this)
+ {
+
+ if( !pLastFrame->HasPara() )
+ pLastFrame->GetFormatted();
+
+ if ( !pLastFrame->IsEmpty() )
+ {
+ const SwParaPortion* pPara = pLastFrame->GetPara();
+ OSL_ENSURE( pPara, "GetDropSize could not find the ParaPortion, I'll guess the drop cap size" );
+
+ if ( pPara )
+ {
+ const SwLinePortion* pFirstPor = pPara->GetFirstPortion();
+ if (pFirstPor && pFirstPor->IsDropPortion())
+ {
+ const SwDropPortion* pDrop = static_cast<const SwDropPortion*>(pFirstPor);
+ rDropHeight = pDrop->GetDropHeight();
+ rDropDescent = pDrop->GetDropDescent();
+ if (const SwFont *pFont = pDrop->GetFnt())
+ rFontHeight = pFont->GetSize(pFont->GetActual()).Height();
+ else
+ {
+ const SvxFontHeightItem& rItem = rSet.Get(RES_CHRATR_FONTSIZE);
+ rFontHeight = rItem.GetHeight();
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (rFontHeight==0 && rDropHeight==0 && rDropDescent==0)
+ {
+ const sal_uInt16 nLines = rDrop.GetLines();
+
+ const SvxFontHeightItem& rItem = rSet.Get( RES_CHRATR_FONTSIZE );
+ rFontHeight = rItem.GetHeight();
+ rDropHeight = nLines * rFontHeight;
+ rDropDescent = rFontHeight / 5;
+ return false;
+ }
+
+ return true;
+}
+
+/// Manipulate the width, otherwise the chars are being stretched
+void SwDropPortion::PaintText( const SwTextPaintInfo &rInf ) const
+{
+ OSL_ENSURE( m_nDropHeight && m_pPart && m_nLines != 1, "Drop Portion painted twice" );
+
+ const SwDropPortionPart* pCurrPart = GetPart();
+ const TextFrameIndex nOldLen = GetLen();
+ const sal_uInt16 nOldWidth = Width();
+ const sal_uInt16 nOldAscent = GetAscent();
+
+ const SwTwips nBasePosY = rInf.Y();
+ const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY + m_nY );
+ const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent + m_nY );
+ SwDropSave aSave( rInf );
+ // for text inside drop portions we let vcl handle the text directions
+ SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
+ aLayoutModeModifier.SetAuto();
+
+ while ( pCurrPart )
+ {
+ const_cast<SwDropPortion*>(this)->SetLen( pCurrPart->GetLen() );
+ const_cast<SwDropPortion*>(this)->Width( pCurrPart->GetWidth() );
+ const_cast<SwTextPaintInfo&>(rInf).SetLen( pCurrPart->GetLen() );
+ SwFontSave aFontSave( rInf, &pCurrPart->GetFont() );
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
+
+ if ( rInf.OnWin() &&
+ !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() &&
+ (!pCurrPart->GetFont().GetBackColor() || *pCurrPart->GetFont().GetBackColor() == COL_TRANSPARENT) )
+ {
+ rInf.DrawBackground( *this );
+ }
+
+ SwTextPortion::Paint( rInf );
+
+ const_cast<SwTextPaintInfo&>(rInf).SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
+ const_cast<SwTextPaintInfo&>(rInf).X( rInf.X() + pCurrPart->GetWidth() );
+ pCurrPart = pCurrPart->GetFollow();
+ }
+
+ const_cast<SwTextPaintInfo&>(rInf).Y( nBasePosY );
+ const_cast<SwDropPortion*>(this)->Width( nOldWidth );
+ const_cast<SwDropPortion*>(this)->SetLen( nOldLen );
+ const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
+}
+
+void SwDropPortion::PaintDrop( const SwTextPaintInfo &rInf ) const
+{
+ // normal output is being done during the normal painting
+ if( ! m_nDropHeight || ! m_pPart || m_nLines == 1 )
+ return;
+
+ // set the lying values
+ const sal_uInt16 nOldHeight = Height();
+ const sal_uInt16 nOldWidth = Width();
+ const sal_uInt16 nOldAscent = GetAscent();
+ const SwTwips nOldPosY = rInf.Y();
+ const SwTwips nOldPosX = rInf.X();
+ const SwParaPortion *pPara = rInf.GetParaPortion();
+ const Point aOutPos( nOldPosX, nOldPosY - pPara->GetAscent()
+ - pPara->GetRealHeight() + pPara->Height() );
+ // make good for retouching
+
+ // Set baseline
+ const_cast<SwTextPaintInfo&>(rInf).Y( aOutPos.Y() + m_nDropHeight );
+
+ // for background
+ const_cast<SwDropPortion*>(this)->Height( m_nDropHeight + m_nDropDescent );
+ const_cast<SwDropPortion*>(this)->SetAscent( m_nDropHeight );
+
+ // Always adapt Clipregion to us, never set it off using the existing ClipRect
+ // as that could be set for the line
+ SwRect aClipRect;
+ if ( rInf.OnWin() )
+ {
+ aClipRect = SwRect( aOutPos, SvLSize() );
+ aClipRect.Intersection( rInf.GetPaintRect() );
+ }
+ SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
+ aClip.ChgClip( aClipRect, rInf.GetTextFrame() );
+
+ // Just do, what we always do ...
+ PaintText( rInf );
+
+ // save old values
+ const_cast<SwDropPortion*>(this)->Height( nOldHeight );
+ const_cast<SwDropPortion*>(this)->Width( nOldWidth );
+ const_cast<SwDropPortion*>(this)->SetAscent( nOldAscent );
+ const_cast<SwTextPaintInfo&>(rInf).Y( nOldPosY );
+}
+
+void SwDropPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ // normal output is being done here
+ if( !(! m_nDropHeight || ! m_pPart || 1 == m_nLines) )
+ return;
+
+ if ( rInf.OnWin() &&
+ !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
+ rInf.DrawBackground( *this );
+
+ // make sure that font is not rotated
+ std::unique_ptr<SwFont> pTmpFont;
+ if ( rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() ) )
+ {
+ pTmpFont.reset(new SwFont( *rInf.GetFont() ));
+ pTmpFont->SetVertical( 0_deg10, rInf.GetTextFrame()->IsVertical() );
+ }
+
+ SwFontSave aFontSave( rInf, pTmpFont.get() );
+ // for text inside drop portions we let vcl handle the text directions
+ SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
+ aLayoutModeModifier.SetAuto();
+
+ SwTextPortion::Paint( rInf );
+}
+
+bool SwDropPortion::FormatText( SwTextFormatInfo &rInf )
+{
+ const TextFrameIndex nOldLen = GetLen();
+ const TextFrameIndex nOldInfLen = rInf.GetLen();
+ if (!SwTextPortion::Format( rInf ))
+ return false;
+
+ // looks like shit, but what can we do?
+ rInf.SetUnderflow( nullptr );
+ Truncate();
+ SetLen( nOldLen );
+ rInf.SetLen( nOldInfLen );
+
+ return true;
+}
+
+SwPosSize SwDropPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
+{
+ sal_uInt16 nMyX = 0;
+ TextFrameIndex nIdx(0);
+
+ const SwDropPortionPart* pCurrPart = GetPart();
+
+ // skip parts
+ while ( pCurrPart && nIdx + pCurrPart->GetLen() < rInf.GetLen() )
+ {
+ nMyX = nMyX + pCurrPart->GetWidth();
+ nIdx = nIdx + pCurrPart->GetLen();
+ pCurrPart = pCurrPart->GetFollow();
+ }
+
+ TextFrameIndex const nOldIdx = rInf.GetIdx();
+ TextFrameIndex const nOldLen = rInf.GetLen();
+
+ const_cast<SwTextSizeInfo&>(rInf).SetIdx( nIdx );
+ const_cast<SwTextSizeInfo&>(rInf).SetLen( rInf.GetLen() - nIdx );
+
+ if( pCurrPart )
+ {
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
+ }
+
+ // robust
+ SwFontSave aFontSave( rInf, pCurrPart ? &pCurrPart->GetFont() : nullptr );
+ SwPosSize aPosSize( SwTextPortion::GetTextSize( rInf ) );
+ aPosSize.Width( aPosSize.Width() + nMyX );
+
+ const_cast<SwTextSizeInfo&>(rInf).SetIdx( nOldIdx );
+ const_cast<SwTextSizeInfo&>(rInf).SetLen( nOldLen );
+ if( pCurrPart )
+ {
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithNext(false);
+ const_cast<SwDropPortion*>(this)->SetJoinBorderWithPrev(false);
+ }
+
+ return aPosSize;
+}
+
+TextFrameIndex SwDropPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+{
+ return TextFrameIndex(0);
+}
+
+void SwTextFormatter::CalcDropHeight( const sal_uInt16 nLines )
+{
+ const SwLinePortion *const pOldCurr = GetCurr();
+ sal_uInt16 nDropHght = 0;
+ SwTwips nAscent = 0;
+ SwTwips nHeight = 0;
+ sal_uInt16 nDropLns = 0;
+ const bool bRegisterOld = IsRegisterOn();
+ m_bRegisterOn = false;
+
+ Top();
+
+ while( GetCurr()->IsDummy() )
+ {
+ if ( !Next() )
+ break;
+ }
+
+ // If we have only one line we return 0
+ if( GetNext() || GetDropLines() == 1 )
+ {
+ for( ; nDropLns < nLines; nDropLns++ )
+ {
+ if ( GetCurr()->IsDummy() )
+ break;
+ else
+ {
+ CalcAscentAndHeight( nAscent, nHeight );
+ nDropHght = nDropHght + nHeight;
+ m_bRegisterOn = bRegisterOld;
+ }
+ if ( !Next() )
+ {
+ nDropLns++;
+ break;
+ }
+ }
+
+ // We hit the line ascent when reaching the last line!
+ nDropHght = nDropHght - nHeight;
+ nDropHght = nDropHght + nAscent;
+ Top();
+ }
+ m_bRegisterOn = bRegisterOld;
+ SetDropDescent( nHeight - nAscent );
+ SetDropHeight( nDropHght );
+ SetDropLines( nDropLns );
+ // Find old position!
+ while( pOldCurr != GetCurr() )
+ {
+ if( !Next() )
+ {
+ OSL_ENSURE( false, "SwTextFormatter::_CalcDropHeight: left Toulouse" );
+ break;
+ }
+ }
+}
+
+/**
+ * We assume that the font height doesn't change and that at first there
+ * are at least as many lines, as the DropCap-setting claims
+ */
+void SwTextFormatter::GuessDropHeight( const sal_uInt16 nLines )
+{
+ OSL_ENSURE( nLines, "GuessDropHeight: Give me more Lines!" );
+ SwTwips nAscent = 0;
+ SwTwips nHeight = 0;
+ SetDropLines( nLines );
+ if ( GetDropLines() > 1 )
+ {
+ CalcRealHeight();
+ CalcAscentAndHeight( nAscent, nHeight );
+ }
+ SetDropDescent( nHeight - nAscent );
+ SetDropHeight( nHeight * nLines - GetDropDescent() );
+}
+
+SwDropPortion *SwTextFormatter::NewDropPortion( SwTextFormatInfo &rInf )
+{
+ if( !m_pDropFormat )
+ return nullptr;
+
+ TextFrameIndex nPorLen(m_pDropFormat->GetWholeWord() ? 0 : m_pDropFormat->GetChars());
+ nPorLen = m_pFrame->GetDropLen( nPorLen );
+ if( !nPorLen )
+ {
+ ClearDropFormat();
+ return nullptr;
+ }
+
+ SwDropPortion *pDropPor = nullptr;
+
+ // first or second round?
+ if ( !( GetDropHeight() || IsOnceMore() ) )
+ {
+ if ( GetNext() )
+ CalcDropHeight( m_pDropFormat->GetLines() );
+ else
+ GuessDropHeight( m_pDropFormat->GetLines() );
+ }
+
+ // the DropPortion
+ if( GetDropHeight() )
+ pDropPor = new SwDropPortion( GetDropLines(), GetDropHeight(),
+ GetDropDescent(), m_pDropFormat->GetDistance() );
+ else
+ pDropPor = new SwDropPortion( 0,0,0,m_pDropFormat->GetDistance() );
+
+ pDropPor->SetLen( nPorLen );
+
+ // If it was not possible to create a proper drop cap portion
+ // due to avoiding endless loops. We return a drop cap portion
+ // with an empty SwDropCapPart. For these portions the current
+ // font is used.
+ if ( GetDropLines() < 2 )
+ {
+ SetPaintDrop( true );
+ return pDropPor;
+ }
+
+ // build DropPortionParts:
+ OSL_ENSURE( ! rInf.GetIdx(), "Drop Portion not at 0 position!" );
+ TextFrameIndex nNextChg(0);
+ const SwCharFormat* pFormat = m_pDropFormat->GetCharFormat();
+ SwDropPortionPart* pCurrPart = nullptr;
+
+ while ( nNextChg < nPorLen )
+ {
+ // check for attribute changes and if the portion has to split:
+ Seek( nNextChg );
+
+ // the font is deleted in the destructor of the drop portion part
+ SwFont* pTmpFnt = new SwFont( *rInf.GetFont() );
+ if ( pFormat )
+ {
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ pTmpFnt->SetDiffFnt(&rSet, &m_pFrame->GetDoc().getIDocumentSettingAccess());
+ }
+
+ // we do not allow a vertical font for the drop portion
+ pTmpFnt->SetVertical( 0_deg10, rInf.GetTextFrame()->IsVertical() );
+
+ // find next attribute change / script change
+ const TextFrameIndex nTmpIdx = nNextChg;
+ TextFrameIndex nNextAttr = GetNextAttr();
+ nNextChg = m_pScriptInfo->NextScriptChg( nTmpIdx );
+ if( nNextChg > nNextAttr )
+ nNextChg = nNextAttr;
+ if ( nNextChg > nPorLen )
+ nNextChg = nPorLen;
+
+ std::unique_ptr<SwDropPortionPart> pPart(
+ new SwDropPortionPart( *pTmpFnt, nNextChg - nTmpIdx ) );
+ auto pPartTemp = pPart.get();
+
+ if ( ! pCurrPart )
+ pDropPor->SetPart( std::move(pPart) );
+ else
+ pCurrPart->SetFollow( std::move(pPart) );
+
+ pCurrPart = pPartTemp;
+ }
+
+ SetPaintDrop( true );
+ return pDropPor;
+}
+
+void SwTextPainter::PaintDropPortion()
+{
+ const SwDropPortion *pDrop = GetInfo().GetParaPortion()->FindDropPortion();
+ OSL_ENSURE( pDrop, "DrapCop-Portion not available." );
+ if( !pDrop )
+ return;
+
+ const SwTwips nOldY = GetInfo().Y();
+
+ Top();
+
+ GetInfo().SetpSpaceAdd( m_pCurr->GetpLLSpaceAdd() );
+ GetInfo().ResetSpaceIdx();
+ GetInfo().SetKanaComp( m_pCurr->GetpKanaComp() );
+ GetInfo().ResetKanaIdx();
+
+ // 8047: Drops and Dummies
+ while( !m_pCurr->GetLen() && Next() )
+ ;
+
+ // MarginPortion and Adjustment!
+ const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ tools::Long nX = 0;
+ while( pPor && !pPor->IsDropPortion() )
+ {
+ nX = nX + pPor->Width();
+ pPor = pPor->GetNextPortion();
+ }
+ Point aLineOrigin( GetTopLeft() );
+
+ aLineOrigin.AdjustX(nX );
+ SwTwips nTmpAscent, nTmpHeight;
+ CalcAscentAndHeight( nTmpAscent, nTmpHeight );
+ aLineOrigin.AdjustY(nTmpAscent );
+ GetInfo().SetIdx( GetStart() );
+ GetInfo().SetPos( aLineOrigin );
+ GetInfo().SetLen( pDrop->GetLen() );
+
+ pDrop->PaintDrop( GetInfo() );
+
+ GetInfo().Y( nOldY );
+}
+
+// Since the calculation of the font size is expensive, this is being
+// channeled through a DropCapCache
+#define DROP_CACHE_SIZE 10
+
+class SwDropCapCache
+{
+ const void* m_aFontCacheId[ DROP_CACHE_SIZE ] = {};
+ OUString m_aText[ DROP_CACHE_SIZE ];
+ sal_uInt16 m_aFactor[ DROP_CACHE_SIZE ];
+ sal_uInt16 m_aWishedHeight[ DROP_CACHE_SIZE ] = {};
+ short m_aDescent[ DROP_CACHE_SIZE ];
+ sal_uInt16 m_nIndex = 0;
+public:
+ SwDropCapCache() = default;
+ void CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf );
+};
+
+void SwDropPortion::DeleteDropCapCache()
+{
+ delete pDropCapCache;
+}
+
+void SwDropCapCache::CalcFontSize( SwDropPortion* pDrop, SwTextFormatInfo &rInf )
+{
+ const void* nFntCacheId = nullptr;
+ sal_uInt16 nTmpIdx = 0;
+
+ OSL_ENSURE( pDrop->GetPart(),"DropPortion without part during font calculation");
+
+ SwDropPortionPart* pCurrPart = pDrop->GetPart();
+ const bool bUseCache = ! pCurrPart->GetFollow() && !pCurrPart->GetFont().HasBorder();
+ TextFrameIndex nIdx = rInf.GetIdx();
+ OUString aStr(rInf.GetText().copy(sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen())));
+
+ tools::Long nDescent = 0;
+ tools::Long nFactor = -1;
+
+ if ( bUseCache )
+ {
+ SwFont& rFnt = pCurrPart->GetFont();
+ rFnt.CheckFontCacheId( rInf.GetVsh(), rFnt.GetActual() );
+ rFnt.GetFontCacheId( nFntCacheId, nTmpIdx, rFnt.GetActual() );
+
+ nTmpIdx = 0;
+
+ while( nTmpIdx < DROP_CACHE_SIZE &&
+ ( m_aText[ nTmpIdx ] != aStr || m_aFontCacheId[ nTmpIdx ] != nFntCacheId ||
+ m_aWishedHeight[ nTmpIdx ] != pDrop->GetDropHeight() ) )
+ ++nTmpIdx;
+ }
+
+ // we have to calculate a new font scaling factor if
+ // 1. we did not find a scaling factor in the cache or
+ // 2. we are not allowed to use the cache because the drop portion
+ // consists of more than one part
+ if( nTmpIdx >= DROP_CACHE_SIZE || ! bUseCache )
+ {
+ ++m_nIndex;
+ m_nIndex %= DROP_CACHE_SIZE;
+ nTmpIdx = m_nIndex;
+
+ tools::Long nWishedHeight = pDrop->GetDropHeight();
+ tools::Long nAscent = 0;
+
+ // find out biggest font size for initial scaling factor
+ tools::Long nMaxFontHeight = 1;
+ while ( pCurrPart )
+ {
+ const SwFont& rFnt = pCurrPart->GetFont();
+ const tools::Long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
+ if ( nCurrHeight > nMaxFontHeight )
+ nMaxFontHeight = nCurrHeight;
+
+ pCurrPart = pCurrPart->GetFollow();
+ }
+
+ nFactor = ( 1000 * nWishedHeight ) / nMaxFontHeight;
+
+ if ( bUseCache )
+ {
+ // save keys for cache
+ m_aFontCacheId[ nTmpIdx ] = nFntCacheId;
+ m_aText[ nTmpIdx ] = aStr;
+ m_aWishedHeight[ nTmpIdx ] = sal_uInt16(nWishedHeight);
+ // save initial scaling factor
+ m_aFactor[ nTmpIdx ] = o3tl::narrowing<sal_uInt16>(nFactor);
+ }
+
+ bool bGrow = (pDrop->GetLen() != TextFrameIndex(0));
+
+ // for growing control
+ tools::Long nMax = USHRT_MAX;
+ tools::Long nMin = 0;
+#if OSL_DEBUG_LEVEL > 1
+ tools::Long nGrow = 0;
+#endif
+
+ bool bWinUsed = false;
+ vcl::Font aOldFnt;
+ MapMode aOldMap( MapUnit::MapTwip );
+ OutputDevice* pOut = rInf.GetOut();
+ OutputDevice* pWin;
+ if( rInf.GetVsh() && rInf.GetVsh()->GetWin() )
+ pWin = rInf.GetVsh()->GetWin()->GetOutDev();
+ else
+ pWin = Application::GetDefaultDevice();
+
+ while( bGrow )
+ {
+ // reset pCurrPart to first part
+ pCurrPart = pDrop->GetPart();
+ bool bFirstGlyphRect = true;
+ tools::Rectangle aCommonRect, aRect;
+
+ while ( pCurrPart )
+ {
+ // current font
+ SwFont& rFnt = pCurrPart->GetFont();
+
+ // Get height including proportion
+ const tools::Long nCurrHeight = rFnt.GetHeight( rFnt.GetActual() );
+
+ // Get without proportion
+ const sal_uInt8 nOldProp = rFnt.GetPropr();
+ rFnt.SetProportion( 100 );
+ Size aOldSize( 0, rFnt.GetHeight( rFnt.GetActual() ) );
+
+ Size aNewSize( 0, ( nFactor * nCurrHeight ) / 1000 );
+ rFnt.SetSize( aNewSize, rFnt.GetActual() );
+ rFnt.ChgPhysFnt( rInf.GetVsh(), *pOut );
+
+ nAscent = rFnt.GetAscent( rInf.GetVsh(), *pOut );
+
+ // we get the rectangle that covers all chars
+ bool bHaveGlyphRect = pOut->GetTextBoundRect( aRect, rInf.GetText(), 0,
+ sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen()))
+ && ! aRect.IsEmpty();
+
+ if ( ! bHaveGlyphRect )
+ {
+ // getting glyph boundaries failed for some reason,
+ // we take the window for calculating sizes
+ if ( pWin )
+ {
+ if ( ! bWinUsed )
+ {
+ bWinUsed = true;
+ aOldMap = pWin->GetMapMode( );
+ pWin->SetMapMode( MapMode( MapUnit::MapTwip ) );
+ aOldFnt = pWin->GetFont();
+ }
+ pWin->SetFont( rFnt.GetActualFont() );
+
+ bHaveGlyphRect = pWin->GetTextBoundRect( aRect, rInf.GetText(), 0,
+ sal_Int32(nIdx), sal_Int32(pCurrPart->GetLen()))
+ && ! aRect.IsEmpty();
+ }
+ if (!bHaveGlyphRect)
+ {
+ // We do not have a window or our window could not
+ // give us glyph boundaries.
+ aRect = tools::Rectangle( Point( 0, 0 ), Size( 0, nAscent ) );
+ }
+ }
+
+ // Now we (hopefully) have a bounding rectangle for the
+ // glyphs of the current portion and the ascent of the current
+ // font
+
+ // reset font size and proportion
+ rFnt.SetSize( aOldSize, rFnt.GetActual() );
+ rFnt.SetProportion( nOldProp );
+
+ // Modify the bounding rectangle with the borders
+ // Robust: If the padding is so big as drop cap letter has no enough space than
+ // remove all padding.
+ if( rFnt.GetTopBorderSpace() + rFnt.GetBottomBorderSpace() >= nWishedHeight )
+ {
+ rFnt.SetTopBorderDist(0);
+ rFnt.SetBottomBorderDist(0);
+ rFnt.SetRightBorderDist(0);
+ rFnt.SetLeftBorderDist(0);
+ }
+
+ if( rFnt.GetTopBorder() )
+ {
+ aRect.setHeight(aRect.GetHeight() + rFnt.GetTopBorderSpace());
+ aRect.SetPosY(aRect.Top() - rFnt.GetTopBorderSpace());
+ }
+
+ if( rFnt.GetBottomBorder() )
+ {
+ aRect.setHeight(aRect.GetHeight() + rFnt.GetBottomBorderSpace());
+ }
+
+ if ( bFirstGlyphRect )
+ {
+ aCommonRect = aRect;
+ bFirstGlyphRect = false;
+ }
+ else
+ aCommonRect.Union( aRect );
+
+ nIdx = nIdx + pCurrPart->GetLen();
+ pCurrPart = pCurrPart->GetFollow();
+ }
+
+ // now we have a union ( aCommonRect ) of all glyphs with
+ // respect to a common baseline : 0
+
+ // get descent and ascent from union
+ if ( rInf.GetTextFrame()->IsVertical() )
+ {
+ nDescent = aCommonRect.Left();
+ nAscent = aCommonRect.Right();
+
+ if ( nDescent < 0 )
+ nDescent = -nDescent;
+ }
+ else
+ {
+ nDescent = aCommonRect.Bottom();
+ nAscent = aCommonRect.Top();
+ }
+ if ( nAscent < 0 )
+ nAscent = -nAscent;
+
+ const tools::Long nHght = nAscent + nDescent;
+ if ( nHght )
+ {
+ if ( nHght > nWishedHeight )
+ nMax = nFactor;
+ else
+ {
+ if ( bUseCache )
+ m_aFactor[ nTmpIdx ] = o3tl::narrowing<sal_uInt16>(nFactor);
+ nMin = nFactor;
+ }
+
+ nFactor = ( nFactor * nWishedHeight ) / nHght;
+ bGrow = ( nFactor > nMin ) && ( nFactor < nMax );
+#if OSL_DEBUG_LEVEL > 1
+ if ( bGrow )
+ nGrow++;
+#endif
+ nIdx = rInf.GetIdx();
+ }
+ else
+ bGrow = false;
+ }
+
+ if ( bWinUsed )
+ {
+ // reset window if it has been used
+ pWin->SetMapMode( aOldMap );
+ pWin->SetFont( aOldFnt );
+ }
+
+ if ( bUseCache )
+ m_aDescent[ nTmpIdx ] = -short( nDescent );
+ }
+
+ pCurrPart = pDrop->GetPart();
+
+ // did made any new calculations or did we use the cache?
+ if ( -1 == nFactor )
+ {
+ nFactor = m_aFactor[ nTmpIdx ];
+ nDescent = m_aDescent[ nTmpIdx ];
+ }
+ else
+ nDescent = -nDescent;
+
+ while ( pCurrPart )
+ {
+ // scale current font
+ SwFont& rFnt = pCurrPart->GetFont();
+ Size aNewSize( 0, ( nFactor * rFnt.GetHeight( rFnt.GetActual() ) ) / 1000 );
+
+ const sal_uInt8 nOldProp = rFnt.GetPropr();
+ rFnt.SetProportion( 100 );
+ rFnt.SetSize( aNewSize, rFnt.GetActual() );
+ rFnt.SetProportion( nOldProp );
+
+ pCurrPart = pCurrPart->GetFollow();
+ }
+ pDrop->SetY( static_cast<short>(nDescent) );
+}
+
+bool SwDropPortion::Format( SwTextFormatInfo &rInf )
+{
+ bool bFull = false;
+ m_nFix = o3tl::narrowing<sal_uInt16>(rInf.X());
+
+ SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
+ aLayoutModeModifier.SetAuto();
+
+ if( m_nDropHeight && m_pPart && m_nLines!=1 )
+ {
+ if( !pDropCapCache )
+ pDropCapCache = new SwDropCapCache;
+
+ // adjust font sizes to fit into the rectangle
+ pDropCapCache->CalcFontSize( this, rInf );
+
+ const tools::Long nOldX = rInf.X();
+ {
+ SwDropSave aSave( rInf );
+ SwDropPortionPart* pCurrPart = m_pPart.get();
+
+ while ( pCurrPart )
+ {
+ rInf.SetLen( pCurrPart->GetLen() );
+ SwFont& rFnt = pCurrPart->GetFont();
+ {
+ SwFontSave aFontSave( rInf, &rFnt );
+ SetJoinBorderWithNext(pCurrPart->GetJoinBorderWithNext());
+ SetJoinBorderWithPrev(pCurrPart->GetJoinBorderWithPrev());
+ bFull = FormatText( rInf );
+
+ if ( bFull )
+ break;
+ }
+
+ const SwTwips nTmpWidth =
+ ( InSpaceGrp() && rInf.GetSpaceAdd() ) ?
+ Width() + CalcSpacing( rInf.GetSpaceAdd(), rInf ) :
+ Width();
+
+ // set values
+ pCurrPart->SetWidth( o3tl::narrowing<sal_uInt16>(nTmpWidth) );
+
+ // Move
+ rInf.SetIdx( rInf.GetIdx() + pCurrPart->GetLen() );
+ rInf.X( rInf.X() + nTmpWidth );
+ pCurrPart = pCurrPart->GetFollow();
+ }
+ SetJoinBorderWithNext(false);
+ SetJoinBorderWithPrev(false);
+ Width( o3tl::narrowing<sal_uInt16>(rInf.X() - nOldX) );
+ }
+
+ // reset my length
+ SetLen( rInf.GetLen() );
+
+ // Quit when Flys are overlapping
+ if( ! bFull )
+ bFull = lcl_IsDropFlyInter( rInf, Width(), m_nDropHeight );
+
+ if( bFull )
+ {
+ // FormatText could have caused nHeight to be 0
+ if ( !Height() )
+ Height( rInf.GetTextHeight() );
+
+ // And now for another round
+ m_nDropHeight = m_nLines = 0;
+ m_pPart.reset();
+
+ // Meanwhile use normal formatting
+ bFull = SwTextPortion::Format( rInf );
+ }
+ else
+ rInf.SetDropInit( true );
+
+ Height( rInf.GetTextHeight() );
+ SetAscent( rInf.GetAscent() );
+ }
+ else
+ bFull = SwTextPortion::Format( rInf );
+
+ if( bFull )
+ m_nDistance = 0;
+ else
+ {
+ const sal_uInt16 nWant = Width() + GetDistance();
+ const sal_uInt16 nRest = o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X());
+ if( ( nWant > nRest ) ||
+ lcl_IsDropFlyInter( rInf, Width() + GetDistance(), m_nDropHeight ) )
+ m_nDistance = 0;
+
+ Width( Width() + m_nDistance );
+ }
+ return bFull;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx
new file mode 100644
index 000000000..ede8e30f2
--- /dev/null
+++ b/sw/source/core/text/txtfld.cxx
@@ -0,0 +1,836 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <charfmt.hxx>
+#include <fmtautofmt.hxx>
+
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <ndtxt.hxx>
+#include <fldbas.hxx>
+#include <viewopt.hxx>
+#include <flyfrm.hxx>
+#include <viewimp.hxx>
+#include <swfont.hxx>
+#include <swmodule.hxx>
+#include "porfld.hxx"
+#include "porftn.hxx"
+#include "porref.hxx"
+#include "portox.hxx"
+#include "porfly.hxx"
+#include "itrform2.hxx"
+#include <chpfld.hxx>
+#include <dbfld.hxx>
+#include <expfld.hxx>
+#include <docufld.hxx>
+#include <pagedesc.hxx>
+#include <fmtmeta.hxx>
+#include <reffld.hxx>
+#include <flddat.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
+#include <sfx2/docfile.hxx>
+#include <svl/grabbagitem.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+
+static bool lcl_IsInBody( SwFrame const *pFrame )
+{
+ if ( pFrame->IsInDocBody() )
+ return true;
+ else
+ {
+ const SwFrame *pTmp = pFrame;
+ const SwFlyFrame *pFly;
+ while ( nullptr != (pFly = pTmp->FindFlyFrame()) )
+ pTmp = pFly->GetAnchorFrame();
+ return pTmp->IsInDocBody();
+ }
+}
+
+SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf,
+ const SwTextAttr *pHint ) const
+{
+ SwExpandPortion *pRet = nullptr;
+ SwFrame *pFrame = m_pFrame;
+ SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField());
+ const bool bName = rInf.GetOpt().IsFieldName();
+
+ SwCharFormat* pChFormat = nullptr;
+ bool bNewFlyPor = false;
+ sal_uInt16 subType = 0;
+
+ // set language
+ const_cast<SwTextFormatter*>(this)->SeekAndChg( rInf );
+ if (pField->GetLanguage() != GetFnt()->GetLanguage())
+ {
+ pField->SetLanguage( GetFnt()->GetLanguage() );
+ // let the visual note know about its new language
+ if (pField->GetTyp()->Which()==SwFieldIds::Postit)
+ const_cast<SwFormatField*> (&pHint->GetFormatField())->Broadcast( SwFormatFieldHint( &pHint->GetFormatField(), SwFormatFieldHintWhich::LANGUAGE ) );
+ }
+
+ SwViewShell *pSh = rInf.GetVsh();
+ SwDoc *const pDoc( pSh ? pSh->GetDoc() : nullptr );
+ bool const bInClipboard( pDoc == nullptr || pDoc->IsClipBoard() );
+ bool bPlaceHolder = false;
+
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::Script:
+ case SwFieldIds::Postit:
+ pRet = new SwPostItsPortion( SwFieldIds::Script == pField->GetTyp()->Which() );
+ break;
+
+ case SwFieldIds::CombinedChars:
+ {
+ if( bName )
+ pRet = new SwFieldPortion( pField->GetFieldName() );
+ else
+ pRet = new SwCombinedPortion( pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ }
+ break;
+
+ case SwFieldIds::HiddenText:
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwHiddenPortion(aStr);
+ }
+ break;
+
+ case SwFieldIds::Chapter:
+ if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
+ {
+ static_cast<SwChapterField*>(pField)->ChangeExpansion(*pFrame,
+ &static_txtattr_cast<SwTextField const*>(pHint)->GetTextNode());
+ }
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion( aStr );
+ }
+ break;
+
+ case SwFieldIds::DocStat:
+ if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
+ {
+ static_cast<SwDocStatField*>(pField)->ChangeExpansion( pFrame );
+ }
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion( aStr );
+ }
+ static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGECOUNTFLD;
+ break;
+
+ case SwFieldIds::PageNumber:
+ {
+ if( !bName && pSh && pSh->GetLayout() && !pSh->Imp()->IsUpdateExpFields() )
+ {
+ SwPageNumberFieldType *pPageNr = static_cast<SwPageNumberFieldType *>(pField->GetTyp());
+
+ const SwRootFrame* pTmpRootFrame = pSh->GetLayout();
+ const bool bVirt = pTmpRootFrame->IsVirtPageNum();
+
+ sal_uInt16 nVirtNum = pFrame->GetVirtPageNum();
+ sal_uInt16 nNumPages = pTmpRootFrame->GetPageNum();
+ SvxNumType nNumFormat = SvxNumType(-1);
+ if(SVX_NUM_PAGEDESC == pField->GetFormat())
+ nNumFormat = pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType();
+ static_cast<SwPageNumberField*>(pField)
+ ->ChangeExpansion(nVirtNum, nNumPages);
+ pPageNr->ChangeExpansion(pDoc,
+ bVirt, nNumFormat != SvxNumType(-1) ? &nNumFormat : nullptr);
+ }
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion( aStr );
+ }
+ static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGENUMBERFLD;
+ break;
+ }
+ case SwFieldIds::GetExp:
+ {
+ if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
+ {
+ SwGetExpField* pExpField = static_cast<SwGetExpField*>(pField);
+ if( !::lcl_IsInBody( pFrame ) )
+ {
+ pExpField->ChgBodyTextFlag( false );
+ pExpField->ChangeExpansion(*pFrame,
+ *static_txtattr_cast<SwTextField const*>(pHint));
+ }
+ else if( !pExpField->IsInBodyText() )
+ {
+ // Was something else previously, thus: expand first, then convert it!
+ pExpField->ChangeExpansion(*pFrame,
+ *static_txtattr_cast<SwTextField const*>(pHint));
+ pExpField->ChgBodyTextFlag( true );
+ }
+ }
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion( aStr );
+ }
+ break;
+ }
+ case SwFieldIds::Database:
+ {
+ if( !bName )
+ {
+ SwDBField* pDBField = static_cast<SwDBField*>(pField);
+ pDBField->ChgBodyTextFlag( ::lcl_IsInBody( pFrame ) );
+ }
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion(aStr);
+ }
+ break;
+ }
+ case SwFieldIds::RefPageGet:
+ if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() )
+ {
+ static_cast<SwRefPageGetField*>(pField)->ChangeExpansion(*pFrame,
+ static_txtattr_cast<SwTextField const*>(pHint));
+ }
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion(aStr);
+ }
+ break;
+
+ case SwFieldIds::JumpEdit:
+ if( !bName )
+ pChFormat = static_cast<SwJumpEditField*>(pField)->GetCharFormat();
+ bNewFlyPor = true;
+ bPlaceHolder = true;
+ break;
+ case SwFieldIds::GetRef:
+ subType = static_cast<SwGetRefField*>(pField)->GetSubType();
+ {
+ OUString const str( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion(str);
+ }
+ if( subType == REF_BOOKMARK )
+ static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_BOOKMARKFLD;
+ else if( subType == REF_SETREFATTR )
+ static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_SETREFATTRFLD;
+ break;
+ case SwFieldIds::DateTime:
+ subType = static_cast<SwDateTimeField*>(pField)->GetSubType();
+ {
+ OUString const str( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion(str);
+ }
+ if( subType & DATEFLD )
+ static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_DATEFLD;
+ else if( subType & TIMEFLD )
+ static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_TIMEFLD;
+ break;
+ default:
+ {
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion(aStr);
+ }
+ }
+
+ if( bNewFlyPor )
+ {
+ std::unique_ptr<SwFont> pTmpFnt;
+ if( !bName )
+ {
+ pTmpFnt.reset(new SwFont( *m_pFont ));
+ pTmpFnt->SetDiffFnt(&pChFormat->GetAttrSet(), &m_pFrame->GetDoc().getIDocumentSettingAccess());
+ }
+ OUString const aStr( bName
+ ? pField->GetFieldName()
+ : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) );
+ pRet = new SwFieldPortion(aStr, std::move(pTmpFnt), bPlaceHolder);
+ }
+
+ return pRet;
+}
+
+static SwFieldPortion * lcl_NewMetaPortion(SwTextAttr & rHint, const bool bPrefix)
+{
+ ::sw::Meta *const pMeta(
+ static_cast<SwFormatMeta &>(rHint.GetAttr()).GetMeta() );
+ OUString fix;
+ ::sw::MetaField *const pField( dynamic_cast< ::sw::MetaField * >(pMeta) );
+ OSL_ENSURE(pField, "lcl_NewMetaPortion: no meta field?");
+ if (pField)
+ {
+ OUString color;
+ pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix, &color);
+ }
+ return new SwFieldPortion( fix );
+}
+
+/**
+ * Try to create a new portion with zero length, for an end of a hint
+ * (where there is no CH_TXTATR). Because there may be multiple hint ends at a
+ * given index, m_pByEndIter is used to keep track of the already created
+ * portions. But the portions created here may actually be deleted again,
+ * due to Underflow. In that case, m_pByEndIter must be decremented,
+ * so the portion will be created again on the next line.
+ */
+SwExpandPortion * SwTextFormatter::TryNewNoLengthPortion(SwTextFormatInfo const & rInfo)
+{
+ const TextFrameIndex nIdx(rInfo.GetIdx());
+
+ // sw_redlinehide: because there is a dummy character at the start of these
+ // hints, it's impossible to have ends of hints from different nodes at the
+ // same view position, so it's sufficient to check the hints of the current
+ // node. However, m_pByEndIter exists for the whole text frame, so
+ // it's necessary to iterate all hints for that purpose...
+ if (!m_pByEndIter)
+ {
+ m_pByEndIter.reset(new sw::MergedAttrIterByEnd(*rInfo.GetTextFrame()));
+ }
+ SwTextNode const* pNode(nullptr);
+ for (SwTextAttr const* pHint = m_pByEndIter->NextAttr(pNode); pHint;
+ pHint = m_pByEndIter->NextAttr(pNode))
+ {
+ SwTextAttr & rHint(const_cast<SwTextAttr&>(*pHint));
+ TextFrameIndex const nEnd(
+ rInfo.GetTextFrame()->MapModelToView(pNode, rHint.GetAnyEnd()));
+ if (nEnd > nIdx)
+ {
+ m_pByEndIter->PrevAttr();
+ break;
+ }
+ if (nEnd == nIdx)
+ {
+ if (RES_TXTATR_METAFIELD == rHint.Which())
+ {
+ SwFieldPortion *const pPortion(
+ lcl_NewMetaPortion(rHint, false));
+ pPortion->SetNoLength(); // no CH_TXTATR at hint end!
+ return pPortion;
+ }
+ }
+ }
+ return nullptr;
+}
+
+SwLinePortion *SwTextFormatter::NewExtraPortion( SwTextFormatInfo &rInf )
+{
+ SwTextAttr *pHint = GetAttr( rInf.GetIdx() );
+ SwLinePortion *pRet = nullptr;
+ if( !pHint )
+ {
+ pRet = new SwTextPortion;
+ pRet->SetLen(TextFrameIndex(1));
+ rInf.SetLen(TextFrameIndex(1));
+ return pRet;
+ }
+
+ switch( pHint->Which() )
+ {
+ case RES_TXTATR_FLYCNT :
+ {
+ pRet = NewFlyCntPortion( rInf, pHint );
+ break;
+ }
+ case RES_TXTATR_FTN :
+ {
+ pRet = NewFootnotePortion( rInf, pHint );
+ break;
+ }
+ case RES_TXTATR_FIELD :
+ case RES_TXTATR_ANNOTATION :
+ {
+ pRet = NewFieldPortion( rInf, pHint );
+ break;
+ }
+ case RES_TXTATR_REFMARK :
+ {
+ pRet = new SwIsoRefPortion;
+ break;
+ }
+ case RES_TXTATR_TOXMARK :
+ {
+ pRet = new SwIsoToxPortion;
+ break;
+ }
+ case RES_TXTATR_METAFIELD:
+ {
+ pRet = lcl_NewMetaPortion( *pHint, true );
+ break;
+ }
+ default: ;
+ }
+ if( !pRet )
+ {
+ auto pFieldPortion = new SwFieldPortion( "" );
+ if (pHint->Which() == RES_TXTATR_CONTENTCONTROL)
+ {
+ pFieldPortion->SetContentControl(true);
+ }
+ pRet = pFieldPortion;
+ rInf.SetLen(TextFrameIndex(1));
+ }
+ return pRet;
+}
+
+/**
+ * OOXML spec says that w:rPr inside w:pPr specifies formatting for the paragraph mark symbol (i.e. the control
+ * character than can be configured to be shown). However, in practice MSO also uses it as direct formatting
+ * for numbering in that paragraph. I don't know if the problem is in the spec or in MSWord.
+ */
+static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextFormatInfo& rInf,
+ const IDocumentSettingAccess* pIDSA,
+ const SwAttrSet* pFormat)
+{
+ if( !pIDSA->get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ))
+ return;
+
+ SwFormatAutoFormat const& rListAutoFormat(rInf.GetTextFrame()->GetTextNodeForParaProps()->GetAttr(RES_PARATR_LIST_AUTOFMT));
+ std::shared_ptr<SfxItemSet> pSet(rListAutoFormat.GetStyleHandle());
+
+ // TODO remove this fallback (for WW8/RTF)
+ bool isDOCX = pIDSA->get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS);
+ if (!isDOCX && !pSet)
+ {
+ TextFrameIndex const nTextLen(rInf.GetTextFrame()->GetText().getLength());
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIterReverse iter(*rInf.GetTextFrame());
+ for (SwTextAttr const* pHint = iter.PrevAttr(&pNode); pHint;
+ pHint = iter.PrevAttr(&pNode))
+ {
+ TextFrameIndex const nHintEnd(
+ rInf.GetTextFrame()->MapModelToView(pNode, pHint->GetAnyEnd()));
+ if (nHintEnd < nTextLen)
+ {
+ break; // only those at para end are interesting
+ }
+ // Formatting for the paragraph mark is usually set to apply only to the
+ // (non-existent) extra character at end of the text node, but there can be
+ // other hints too (ending at nTextLen), so look for all matching hints.
+ // Still the (non-existent) extra character at the end is preferred if it exists.
+ if (pHint->Which() == RES_TXTATR_AUTOFMT)
+ {
+ pSet = pHint->GetAutoFormat().GetStyleHandle();
+ // When we find an empty hint (start == end) we got what we are looking for.
+ if (pHint->GetStart() == *pHint->End())
+ break;
+ }
+ }
+ }
+
+ // Check each item and in case it should be ignored, then clear it.
+ if (!pSet)
+ return;
+
+ std::unique_ptr<SfxItemSet> const pCleanedSet = pSet->Clone();
+
+ if (pCleanedSet->HasItem(RES_TXTATR_CHARFMT))
+ {
+ // Insert attributes of referenced char format into current set
+ const SwFormatCharFormat& rCharFormat = pCleanedSet->Get(RES_TXTATR_CHARFMT);
+ const SwAttrSet& rStyleAttrs = static_cast<const SwCharFormat *>(rCharFormat.GetRegisteredIn())->GetAttrSet();
+ SfxWhichIter aIter(rStyleAttrs);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ if (!SwTextNode::IsIgnoredCharFormatForNumbering(nWhich, /*bIsCharStyle=*/true)
+ && !pCleanedSet->HasItem(nWhich)
+ && !(pFormat && pFormat->HasItem(nWhich)) )
+ {
+ // Copy from parent sets only allowed items which will not overwrite
+ // values explicitly defined in current set (pCleanedSet) or in pFormat
+ if (const SfxPoolItem* pItem = rStyleAttrs.GetItem(nWhich, true))
+ pCleanedSet->Put(*pItem);
+ }
+ nWhich = aIter.NextWhich();
+ }
+
+ // It is not required here anymore, all referenced items are inserted
+ pCleanedSet->ClearItem(RES_TXTATR_CHARFMT);
+ };
+
+ SfxItemIter aIter(*pSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ while (pItem)
+ {
+ if (SwTextNode::IsIgnoredCharFormatForNumbering(pItem->Which()))
+ pCleanedSet->ClearItem(pItem->Which());
+ else if (pFormat && pFormat->HasItem(pItem->Which()))
+ pCleanedSet->ClearItem(pItem->Which());
+ else if (pItem->Which() == RES_CHRATR_BACKGROUND)
+ {
+ bool bShadingWasImported = false;
+ // If Shading was imported, it should not be converted to a Highlight,
+ // but remain as Shading which is ignored for numbering.
+ if (pCleanedSet->HasItem(RES_CHRATR_GRABBAG))
+ {
+ SfxGrabBagItem aGrabBag = pCleanedSet->Get(RES_CHRATR_GRABBAG, /*bSrchInParent=*/false);
+ std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag();
+ auto aIterator = rMap.find("CharShadingMarker");
+ if (aIterator != rMap.end())
+ aIterator->second >>= bShadingWasImported;
+ }
+
+ // If used, BACKGROUND is converted to HIGHLIGHT. So also ignore if a highlight already exists.
+ if (bShadingWasImported
+ || pCleanedSet->HasItem(RES_CHRATR_HIGHLIGHT)
+ || (pFormat && pFormat->HasItem(RES_CHRATR_HIGHLIGHT)))
+ {
+ pCleanedSet->ClearItem(pItem->Which());
+ }
+ }
+ pItem = aIter.NextItem();
+ };
+
+ // SetDiffFnt resets the background color (why?), so capture it and re-apply if it had a value,
+ // because an existing value should override anything inherited from the paragraph marker.
+ const std::optional<Color> oFontBackColor = pNumFnt->GetBackColor();
+ // The same is true for the highlight color.
+ const Color aHighlight = pNumFnt->GetHighlightColor();
+
+ pNumFnt->SetDiffFnt(pCleanedSet.get(), pIDSA);
+
+ if (oFontBackColor)
+ pNumFnt->SetBackColor(oFontBackColor);
+ if (aHighlight != COL_TRANSPARENT)
+ pNumFnt->SetHighlightColor(aHighlight);
+}
+
+static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode,
+ bool& bIsMoved )
+{
+ const SwDoc& rDoc = rTextNode.GetDoc();
+ SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rTextNode, RedlineType::Any );
+
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ const SwNodeOffset nNdIdx = rTextNode.GetIndex();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ for( ; nRedlPos < rTable.size() ; ++nRedlPos )
+ {
+ const SwRangeRedline* pTmp = rTable[ nRedlPos ];
+ if( RedlineType::Delete == pTmp->GetType() ||
+ RedlineType::Insert == pTmp->GetType() )
+ {
+ const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
+ if( pRStt->nNode <= nNdIdx && pREnd->nNode > nNdIdx )
+ {
+ bIsMoved = pTmp->IsMoved();
+ return pTmp;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+static bool lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextNode, const std::unique_ptr<SwFont>& pNumFnt )
+{
+ if ( rInf.GetVsh()->GetLayout()->IsHideRedlines() )
+ return false;
+
+ bool bIsMoved;
+ const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode, bIsMoved );
+ if (!pRedlineNum)
+ return false;
+
+ // moved text: dark green with double underline or strikethrough
+ if ( bIsMoved )
+ {
+ pNumFnt->SetColor(COL_GREEN);
+ if ( RedlineType::Delete == pRedlineNum->GetType() )
+ pNumFnt->SetStrikeout(STRIKEOUT_DOUBLE);
+ else
+ pNumFnt->SetUnderline(LINESTYLE_DOUBLE);
+ return true;
+ }
+
+ SwAttrPool& rPool = rInf.GetVsh()->GetDoc()->GetAttrPool();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);
+
+ std::size_t aAuthor = (1 < pRedlineNum->GetStackCount())
+ ? pRedlineNum->GetAuthor( 1 )
+ : pRedlineNum->GetAuthor();
+
+ if ( RedlineType::Delete == pRedlineNum->GetType() )
+ SW_MOD()->GetDeletedAuthorAttr(aAuthor, aSet);
+ else
+ SW_MOD()->GetInsertAuthorAttr(aAuthor, aSet);
+
+ if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR))
+ pNumFnt->SetColor(pItem->GetValue());
+ if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE))
+ pNumFnt->SetUnderline(pItem->GetLineStyle());
+ if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT))
+ pNumFnt->SetStrikeout( pItem->GetStrikeout() );
+
+ return true;
+}
+
+SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) const
+{
+ if( rInf.IsNumDone() || rInf.GetTextStart() != m_nStart
+ || rInf.GetTextStart() != rInf.GetIdx() )
+ return nullptr;
+
+ SwNumberPortion *pRet = nullptr;
+ // sw_redlinehide: at this point it's certain that pTextNd is the node with
+ // the numbering of the frame; only the actual number-vector (GetNumString)
+ // depends on the hide-mode in the layout so other calls don't need to care
+ const SwTextNode *const pTextNd = GetTextFrame()->GetTextNodeForParaProps();
+ const SwNumRule* pNumRule = pTextNd->GetNumRule();
+
+ // Has a "valid" number?
+ // sw_redlinehide: check that pParaPropsNode is the correct one
+ assert(pTextNd->IsNumbered(m_pFrame->getRootFrame()) == pTextNd->IsNumbered(nullptr));
+ if (pTextNd->IsNumbered(m_pFrame->getRootFrame()) && pTextNd->IsCountedInList())
+ {
+ int nLevel = pTextNd->GetActualListLevel();
+
+ if (nLevel < 0)
+ nLevel = 0;
+
+ if (nLevel >= MAXLEVEL)
+ nLevel = MAXLEVEL - 1;
+
+ const SwNumFormat &rNumFormat = pNumRule->Get( nLevel );
+ const bool bLeft = SvxAdjust::Left == rNumFormat.GetNumAdjust();
+ const bool bCenter = SvxAdjust::Center == rNumFormat.GetNumAdjust();
+ const bool bLabelAlignmentPosAndSpaceModeActive(
+ rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT );
+ const sal_uInt16 nMinDist = bLabelAlignmentPosAndSpaceModeActive
+ ? 0 : rNumFormat.GetCharTextDistance();
+
+ if( SVX_NUM_BITMAP == rNumFormat.GetNumberingType() )
+ {
+ OUString referer;
+ if (auto const sh1 = rInf.GetVsh()) {
+ if (auto const doc = sh1->GetDoc()) {
+ auto const sh2 = doc->GetPersist();
+ if (sh2 != nullptr && sh2->HasName()) {
+ referer = sh2->GetMedium()->GetName();
+ }
+ }
+ }
+ pRet = new SwGrfNumPortion( pTextNd->GetLabelFollowedBy(),
+ rNumFormat.GetBrush(), referer,
+ rNumFormat.GetGraphicOrientation(),
+ rNumFormat.GetGraphicSize(),
+ bLeft, bCenter, nMinDist,
+ bLabelAlignmentPosAndSpaceModeActive );
+ tools::Long nTmpA = rInf.GetLast()->GetAscent();
+ tools::Long nTmpD = rInf.GetLast()->Height() - nTmpA;
+ if( !rInf.IsTest() )
+ static_cast<SwGrfNumPortion*>(pRet)->SetBase( nTmpA, nTmpD, nTmpA, nTmpD );
+ }
+ else
+ {
+ // The SwFont is created dynamically and passed in the ctor,
+ // as the CharFormat only returns an SV-Font.
+ // In the dtor of SwNumberPortion, the SwFont is deleted.
+ const SwAttrSet* pFormat = rNumFormat.GetCharFormat() ?
+ &rNumFormat.GetCharFormat()->GetAttrSet() :
+ nullptr;
+ const IDocumentSettingAccess* pIDSA = pTextNd->getIDocumentSettingAccess();
+
+ if( SVX_NUM_CHAR_SPECIAL == rNumFormat.GetNumberingType() )
+ {
+ const std::optional<vcl::Font> pFormatFnt = rNumFormat.GetBulletFont();
+
+ // Build a new bullet font basing on the current paragraph font:
+ std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA ));
+
+ // #i53199#
+ if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) )
+ {
+ // i18463:
+ // Underline style of paragraph font should not be considered
+ // Overline style of paragraph font should not be considered
+ // Weight style of paragraph font should not be considered
+ // Posture style of paragraph font should not be considered
+ pNumFnt->SetUnderline( LINESTYLE_NONE );
+ pNumFnt->SetOverline( LINESTYLE_NONE );
+ pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::Latin );
+ pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CJK );
+ pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CTL );
+ pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::Latin );
+ pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK );
+ pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL );
+ }
+
+ // Apply the explicit attributes from the character style
+ // associated with the numbering to the new bullet font.
+ if( pFormat )
+ pNumFnt->SetDiffFnt( pFormat, pIDSA );
+
+ checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat);
+
+ if ( pFormatFnt )
+ {
+ const SwFontScript nAct = pNumFnt->GetActual();
+ pNumFnt->SetFamily( pFormatFnt->GetFamilyType(), nAct );
+ pNumFnt->SetName( pFormatFnt->GetFamilyName(), nAct );
+ pNumFnt->SetStyleName( pFormatFnt->GetStyleName(), nAct );
+ pNumFnt->SetCharSet( pFormatFnt->GetCharSet(), nAct );
+ pNumFnt->SetPitch( pFormatFnt->GetPitch(), nAct );
+ }
+
+ // we do not allow a vertical font
+ pNumFnt->SetVertical( pNumFnt->GetOrientation(),
+ m_pFrame->IsVertical() );
+
+ lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt );
+
+ // --> OD 2008-01-23 #newlistelevelattrs#
+ if (rNumFormat.GetBulletChar())
+ {
+ pRet = new SwBulletPortion(rNumFormat.GetBulletChar(),
+ pTextNd->GetLabelFollowedBy(),
+ std::move(pNumFnt),
+ bLeft, bCenter, nMinDist,
+ bLabelAlignmentPosAndSpaceModeActive);
+ }
+ }
+ else
+ {
+ // Show Changes mode shows the actual numbering (SwListRedlineType::HIDDEN) and
+ // the original one (SwListRedlineType::ORIGTEXT) instead of the fake numbering
+ // (SwListRedlineType::SHOW, which counts removed and inserted numbered paragraphs
+ // in a single list)
+ bool bHasHiddenNum = false;
+ OUString aText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::HIDDEN) );
+ const SwDoc& rDoc = pTextNd->GetDoc();
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ if ( rTable.size() && !rInf.GetVsh()->GetLayout()->IsHideRedlines() )
+ {
+ OUString aHiddenText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::ORIGTEXT) );
+
+ if ( !aText.isEmpty() || !aHiddenText.isEmpty() )
+ {
+ if (aText != aHiddenText && !aHiddenText.isEmpty())
+ {
+ bHasHiddenNum = true;
+ // show also original number after the actual one enclosed in [ and ],
+ // and replace tabulator with space to avoid messy indentation
+ // resulted by the longer numbering, e.g. "1.[2.]" instead of "1.".
+ aText = aText + "[" + aHiddenText + "]"
+ + pTextNd->GetLabelFollowedBy().replaceAll("\t", " ");
+ }
+ else if (!aText.isEmpty())
+ aText += pTextNd->GetLabelFollowedBy();
+ }
+ }
+ else if (pTextNd->getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY)
+ || !aText.isEmpty())
+ aText += pTextNd->GetLabelFollowedBy();
+
+ // Not just an optimization ...
+ // A number portion without text will be assigned a width of 0.
+ // The succeeding text portion will flow into the BreakCut in the BreakLine,
+ // although we have rInf.GetLast()->GetFlyPortion()!
+ if( !aText.isEmpty() )
+ {
+
+ // Build a new numbering font basing on the current paragraph font:
+ std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA ));
+
+ const SwTextNode& rTextNode = *rInf.GetTextFrame()->GetTextNodeForParaProps();
+ if (const SwpHints* pHints = rTextNode.GetpSwpHints())
+ {
+ // Also look for an empty character hint that sits at the paragraph end:
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ const SwTextAttr* pHint = pHints->GetSortedByEnd(i);
+ if (pHint->Which() == RES_TXTATR_AUTOFMT && pHint->GetEnd()
+ && pHint->GetStart() == *pHint->GetEnd()
+ && pHint->GetStart() == rTextNode.GetText().getLength())
+ {
+ std::shared_ptr<SfxItemSet> pSet
+ = pHint->GetAutoFormat().GetStyleHandle();
+ if (pSet)
+ {
+ pNumFnt->SetDiffFnt(pSet.get(), pIDSA);
+ break;
+ }
+ }
+ }
+ }
+
+ // #i53199#
+ if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) )
+ {
+ // i18463:
+ // Underline style of paragraph font should not be considered
+ pNumFnt->SetUnderline( LINESTYLE_NONE );
+ // Overline style of paragraph font should not be considered
+ pNumFnt->SetOverline( LINESTYLE_NONE );
+ }
+
+ // Apply the explicit attributes from the character style
+ // associated with the numbering to the new bullet font.
+ if( pFormat )
+ pNumFnt->SetDiffFnt( pFormat, pIDSA );
+
+ checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat);
+
+ if ( !lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ) && bHasHiddenNum )
+ pNumFnt->SetColor(NON_PRINTING_CHARACTER_COLOR);
+
+ // we do not allow a vertical font
+ pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
+
+ pRet = new SwNumberPortion( aText, std::move(pNumFnt),
+ bLeft, bCenter, nMinDist,
+ bLabelAlignmentPosAndSpaceModeActive );
+ }
+ }
+ }
+ }
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtfly.cxx b/sw/source/core/text/txtfly.cxx
new file mode 100644
index 000000000..ea4b5c472
--- /dev/null
+++ b/sw/source/core/text/txtfly.cxx
@@ -0,0 +1,1473 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/outdev.hxx>
+
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <pam.hxx>
+#include <swfont.hxx>
+#include <swregion.hxx>
+#include <dflyobj.hxx>
+#include <drawfont.hxx>
+#include <flyfrm.hxx>
+#include <flyfrms.hxx>
+#include <fmtornt.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <ndtxt.hxx>
+#include <txtfly.hxx>
+#include "inftxt.hxx"
+#include "porrst.hxx"
+#include "txtpaint.hxx"
+#include <notxtfrm.hxx>
+#include <fmtcnct.hxx>
+#include <svx/obj3d.hxx>
+#include <editeng/txtrange.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <pagedesc.hxx>
+#include <sortedobjs.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <formatlinebreak.hxx>
+#include <svx/svdoedge.hxx>
+
+#ifdef DBG_UTIL
+#include <viewsh.hxx>
+#include <doc.hxx>
+#endif
+
+using namespace ::com::sun::star;
+
+namespace
+{
+ // #i68520#
+ struct AnchoredObjOrder
+ {
+ bool mbR2L;
+ SwRectFn mfnRect;
+
+ AnchoredObjOrder( const bool bR2L,
+ SwRectFn fnRect )
+ : mbR2L( bR2L ),
+ mfnRect( fnRect )
+ {}
+
+ bool operator()( const SwAnchoredObject* pListedAnchoredObj,
+ const SwAnchoredObject* pNewAnchoredObj )
+ {
+ const SwRect& aBoundRectOfListedObj( pListedAnchoredObj->GetObjRectWithSpaces() );
+ const SwRect& aBoundRectOfNewObj( pNewAnchoredObj->GetObjRectWithSpaces() );
+ if ( ( mbR2L &&
+ ( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() ==
+ (aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) ||
+ ( !mbR2L &&
+ ( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() ==
+ (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) )
+ {
+ SwTwips nTopDiff =
+ (*mfnRect->fnYDiff)( (aBoundRectOfNewObj.*mfnRect->fnGetTop)(),
+ (aBoundRectOfListedObj.*mfnRect->fnGetTop)() );
+ if ( nTopDiff == 0 &&
+ ( ( mbR2L &&
+ ( (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() >
+ (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() ) ) ||
+ ( !mbR2L &&
+ ( (aBoundRectOfNewObj.*mfnRect->fnGetRight)() <
+ (aBoundRectOfListedObj.*mfnRect->fnGetRight)() ) ) ) )
+ {
+ return true;
+ }
+ else if ( nTopDiff > 0 )
+ {
+ return true;
+ }
+ }
+ else if ( ( mbR2L &&
+ ( (aBoundRectOfListedObj.*mfnRect->fnGetRight)() >
+ (aBoundRectOfNewObj.*mfnRect->fnGetRight)() ) ) ||
+ ( !mbR2L &&
+ ( (aBoundRectOfListedObj.*mfnRect->fnGetLeft)() <
+ (aBoundRectOfNewObj.*mfnRect->fnGetLeft)() ) ) )
+ {
+ return true;
+ }
+
+ return false;
+ }
+ };
+}
+
+SwContourCache::SwContourCache() :
+ mnPointCount( 0 )
+{
+}
+
+SwContourCache::~SwContourCache()
+{
+}
+
+void SwContourCache::ClrObject( sal_uInt16 nPos )
+{
+ mnPointCount -= mvItems[ nPos ].mxTextRanger->GetPointCount();
+ mvItems.erase(mvItems.begin() + nPos);
+}
+
+void ClrContourCache( const SdrObject *pObj )
+{
+ if( pContourCache && pObj )
+ for( sal_uInt16 i = 0; i < pContourCache->GetCount(); ++i )
+ if( pObj == pContourCache->GetObject( i ) )
+ {
+ pContourCache->ClrObject( i );
+ break;
+ }
+}
+
+void ClrContourCache()
+{
+ if( pContourCache )
+ {
+ pContourCache->mvItems.clear();
+ pContourCache->mnPointCount = 0;
+ }
+}
+
+// #i68520#
+SwRect SwContourCache::CalcBoundRect( const SwAnchoredObject* pAnchoredObj,
+ const SwRect &rLine,
+ const SwTextFrame* pFrame,
+ const tools::Long nXPos,
+ const bool bRight )
+{
+ SwRect aRet;
+ const SwFrameFormat* pFormat = &(pAnchoredObj->GetFrameFormat());
+ bool bHandleContour(pFormat->GetSurround().IsContour());
+
+ if(!bHandleContour)
+ {
+ // RotateFlyFrame3: Object has no set contour, but for rotated
+ // FlyFrames we can create a 'default' contour to make text
+ // flow around the free, non-covered
+ const SwFlyFreeFrame* pSwFlyFreeFrame(dynamic_cast< const SwFlyFreeFrame* >(pAnchoredObj));
+
+ if(nullptr != pSwFlyFreeFrame && pSwFlyFreeFrame->supportsAutoContour())
+ {
+ bHandleContour = true;
+ }
+ }
+
+ if( bHandleContour &&
+ ( pAnchoredObj->DynCastFlyFrame() == nullptr ||
+ ( static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower() &&
+ static_cast<const SwFlyFrame*>(pAnchoredObj)->Lower()->IsNoTextFrame() ) ) )
+ {
+ aRet = pAnchoredObj->GetObjRectWithSpaces();
+ if( aRet.Overlaps( rLine ) )
+ {
+ if( !pContourCache )
+ pContourCache = new SwContourCache;
+
+ aRet = pContourCache->ContourRect(
+ pFormat, pAnchoredObj->GetDrawObj(), pFrame, rLine, nXPos, bRight );
+ }
+ else
+ aRet.Width( 0 );
+ }
+ else
+ {
+ aRet = pAnchoredObj->GetObjRectWithSpaces();
+ }
+
+ return aRet;
+}
+
+SwRect SwContourCache::ContourRect( const SwFormat* pFormat,
+ const SdrObject* pObj, const SwTextFrame* pFrame, const SwRect &rLine,
+ const tools::Long nXPos, const bool bRight )
+{
+ SwRect aRet;
+ sal_uInt16 nPos = 0; // Search in the Cache
+ while( nPos < GetCount() && pObj != mvItems[ nPos ].mpSdrObj )
+ ++nPos;
+ if( GetCount() == nPos ) // Not found
+ {
+ if( GetCount() == POLY_CNT )
+ {
+ mnPointCount -= mvItems.back().mxTextRanger->GetPointCount();
+ mvItems.pop_back();
+ }
+ ::basegfx::B2DPolyPolygon aPolyPolygon;
+ std::optional<::basegfx::B2DPolyPolygon> pPolyPolygon;
+
+ if ( auto pVirtFlyDrawObj = dynamic_cast< const SwVirtFlyDrawObj *>( pObj ) )
+ {
+ // GetContour() causes the graphic to be loaded, which may cause
+ // the graphic to change its size, call ClrObject()
+ tools::PolyPolygon aPoly;
+ if( !pVirtFlyDrawObj->GetFlyFrame()->GetContour( aPoly ) )
+ aPoly = tools::PolyPolygon( pVirtFlyDrawObj->
+ GetFlyFrame()->getFrameArea().SVRect() );
+ aPolyPolygon.clear();
+ aPolyPolygon.append(aPoly.getB2DPolyPolygon());
+ }
+ else
+ {
+ if( dynamic_cast< const E3dObject *>( pObj ) == nullptr )
+ {
+ aPolyPolygon = pObj->TakeXorPoly();
+ }
+
+ pPolyPolygon = pObj->TakeContour();
+ }
+ const SvxLRSpaceItem &rLRSpace = pFormat->GetLRSpace();
+ const SvxULSpaceItem &rULSpace = pFormat->GetULSpace();
+ CacheItem item {
+ pObj, // due to #37347 the Object must be entered only after GetContour()
+ std::make_unique<TextRanger>( aPolyPolygon, pPolyPolygon ? &*pPolyPolygon : nullptr, 20,
+ o3tl::narrowing<sal_uInt16>(rLRSpace.GetLeft()), o3tl::narrowing<sal_uInt16>(rLRSpace.GetRight()),
+ pFormat->GetSurround().IsOutside(), false, pFrame->IsVertical() )
+ };
+ mvItems.insert(mvItems.begin(), std::move(item));
+ mvItems[0].mxTextRanger->SetUpper( rULSpace.GetUpper() );
+ mvItems[0].mxTextRanger->SetLower( rULSpace.GetLower() );
+
+ pPolyPolygon.reset();
+
+ mnPointCount += mvItems[0].mxTextRanger->GetPointCount();
+ while( mnPointCount > POLY_MAX && mvItems.size() > POLY_MIN )
+ {
+ mnPointCount -= mvItems.back().mxTextRanger->GetPointCount();
+ mvItems.pop_back();
+ }
+ }
+ else if( nPos )
+ {
+ CacheItem item = std::move(mvItems[nPos]);
+ mvItems.erase(mvItems.begin() + nPos);
+ mvItems.insert(mvItems.begin(), std::move(item));
+ }
+ SwRectFnSet aRectFnSet(pFrame);
+ tools::Long nTmpTop = aRectFnSet.GetTop(rLine);
+ // fnGetBottom is top + height
+ tools::Long nTmpBottom = aRectFnSet.GetBottom(rLine);
+
+ Range aRange( std::min( nTmpTop, nTmpBottom ), std::max( nTmpTop, nTmpBottom ) );
+
+ std::deque<tools::Long>* pTmp = mvItems[0].mxTextRanger->GetTextRanges( aRange );
+
+ const size_t nCount = pTmp->size();
+ if( 0 != nCount )
+ {
+ size_t nIdx = 0;
+ while( nIdx < nCount && (*pTmp)[ nIdx ] < nXPos )
+ ++nIdx;
+ bool bOdd = nIdx % 2;
+ bool bSet = true;
+ if( bOdd )
+ --nIdx; // within interval
+ else if( ! bRight && ( nIdx >= nCount || (*pTmp)[ nIdx ] != nXPos ) )
+ {
+ if( nIdx )
+ nIdx -= 2; // an interval to the left
+ else
+ bSet = false; // before the first interval
+ }
+
+ if( bSet && nIdx < nCount )
+ {
+ aRectFnSet.SetTopAndHeight( aRet, aRectFnSet.GetTop(rLine),
+ aRectFnSet.GetHeight(rLine) );
+ aRectFnSet.SetLeft( aRet, (*pTmp)[ nIdx ] );
+ aRectFnSet.SetRight( aRet, (*pTmp)[ nIdx + 1 ] + 1 );
+ }
+ }
+ return aRet;
+}
+
+SwTextFly::SwTextFly()
+ : m_pPage(nullptr)
+ , mpCurrAnchoredObj(nullptr)
+ , m_pCurrFrame(nullptr)
+ , m_pMaster(nullptr)
+ , m_nMinBottom(0)
+ , m_nNextTop(0)
+ , m_nCurrFrameNodeIndex(0)
+ , m_bOn(false)
+ , m_bTopRule(false)
+ , mbIgnoreCurrentFrame(false)
+ , mbIgnoreContour(false)
+ , mbIgnoreObjsInHeaderFooter(false)
+
+{
+}
+
+SwTextFly::SwTextFly( const SwTextFrame *pFrame )
+{
+ CtorInitTextFly( pFrame );
+}
+
+SwTextFly::SwTextFly( const SwTextFly& rTextFly )
+{
+ m_pPage = rTextFly.m_pPage;
+ mpCurrAnchoredObj = rTextFly.mpCurrAnchoredObj;
+ m_pCurrFrame = rTextFly.m_pCurrFrame;
+ m_pMaster = rTextFly.m_pMaster;
+ if( rTextFly.mpAnchoredObjList )
+ {
+ mpAnchoredObjList.reset( new SwAnchoredObjList( *(rTextFly.mpAnchoredObjList) ) );
+ }
+
+ m_bOn = rTextFly.m_bOn;
+ m_bTopRule = rTextFly.m_bTopRule;
+ m_nMinBottom = rTextFly.m_nMinBottom;
+ m_nNextTop = rTextFly.m_nNextTop;
+ m_nCurrFrameNodeIndex = rTextFly.m_nCurrFrameNodeIndex;
+ mbIgnoreCurrentFrame = rTextFly.mbIgnoreCurrentFrame;
+ mbIgnoreContour = rTextFly.mbIgnoreContour;
+ mbIgnoreObjsInHeaderFooter = rTextFly.mbIgnoreObjsInHeaderFooter;
+}
+
+SwTextFly::~SwTextFly()
+{
+}
+
+void SwTextFly::CtorInitTextFly( const SwTextFrame *pFrame )
+{
+ mbIgnoreCurrentFrame = false;
+ mbIgnoreContour = false;
+ mbIgnoreObjsInHeaderFooter = false;
+ m_pPage = pFrame->FindPageFrame();
+ const SwFlyFrame* pTmp = pFrame->FindFlyFrame();
+ // #i68520#
+ mpCurrAnchoredObj = pTmp;
+ m_pCurrFrame = pFrame;
+ m_pMaster = m_pCurrFrame->IsFollow() ? nullptr : m_pCurrFrame;
+ // If we're not overlapped by a frame or if a FlyCollection does not exist
+ // at all, we switch off forever.
+ // It could be, however, that a line is added while formatting, that
+ // extends into a frame.
+ // That's why we do not optimize for: bOn = pSortedFlys && IsAnyFrame();
+ m_bOn = m_pPage->GetSortedObjs() != nullptr;
+ m_bTopRule = true;
+ m_nMinBottom = 0;
+ m_nNextTop = 0;
+ m_nCurrFrameNodeIndex = NODE_OFFSET_MAX;
+}
+
+SwRect SwTextFly::GetFrame_( const SwRect &rRect ) const
+{
+ SwRect aRet;
+ if( ForEach( rRect, &aRet, true ) )
+ {
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+ aRectFnSet.SetTop( aRet, aRectFnSet.GetTop(rRect) );
+
+ // Do not always adapt the bottom
+ const SwTwips nRetBottom = aRectFnSet.GetBottom(aRet);
+ const SwTwips nRectBottom = aRectFnSet.GetBottom(rRect);
+ if ( aRectFnSet.YDiff( nRetBottom, nRectBottom ) > 0 ||
+ aRectFnSet.GetHeight(aRet) < 0 )
+ aRectFnSet.SetBottom( aRet, nRectBottom );
+ }
+ return aRet;
+}
+
+bool SwTextFly::IsAnyFrame() const
+{
+ SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
+
+ OSL_ENSURE( m_bOn, "IsAnyFrame: Why?" );
+ SwRect aRect(m_pCurrFrame->getFrameArea().Pos() + m_pCurrFrame->getFramePrintArea().Pos(),
+ m_pCurrFrame->getFramePrintArea().SSize());
+
+ return ForEach( aRect, nullptr, false );
+}
+
+bool SwTextFly::IsAnyObj( const SwRect &rRect ) const
+{
+ OSL_ENSURE( m_bOn, "SwTextFly::IsAnyObj: Who's knocking?" );
+
+ SwRect aRect( rRect );
+ if ( aRect.IsEmpty() )
+ {
+ aRect = SwRect(m_pCurrFrame->getFrameArea().Pos() + m_pCurrFrame->getFramePrintArea().Pos(),
+ m_pCurrFrame->getFramePrintArea().SSize());
+ }
+
+ const SwSortedObjs *pSorted = m_pPage->GetSortedObjs();
+ if( pSorted ) // bOn actually makes sure that we have objects on the side,
+ // but who knows who deleted something in the meantime?
+ {
+ for ( size_t i = 0; i < pSorted->size(); ++i )
+ {
+ const SwAnchoredObject* pObj = (*pSorted)[i];
+
+ const SwRect aBound( pObj->GetObjRectWithSpaces() );
+
+ // Optimization
+ if( pObj->GetObjRect().Left() > aRect.Right() )
+ continue;
+
+ // #i68520#
+ if( mpCurrAnchoredObj != pObj && aBound.Overlaps( aRect ) )
+ return true;
+ }
+ }
+ return false;
+}
+
+const SwTextFrame* SwTextFly::GetMaster_()
+{
+ m_pMaster = m_pCurrFrame;
+ while (m_pMaster && m_pMaster->IsFollow())
+ m_pMaster = m_pMaster->FindMaster();
+ return m_pMaster;
+}
+
+void SwTextFly::DrawTextOpaque( SwDrawTextInfo &rInf )
+{
+ SwSaveClip aClipSave( rInf.GetpOut() );
+ SwRect aRect( rInf.GetPos(), rInf.GetSize() );
+ if( rInf.GetSpace() )
+ {
+ TextFrameIndex const nTmpLen = TextFrameIndex(COMPLETE_STRING) == rInf.GetLen()
+ ? TextFrameIndex(rInf.GetText().getLength())
+ : rInf.GetLen();
+ if( rInf.GetSpace() > 0 )
+ {
+ sal_Int32 nSpaceCnt = 0;
+ const TextFrameIndex nEndPos = rInf.GetIdx() + nTmpLen;
+ for (TextFrameIndex nPos = rInf.GetIdx(); nPos < nEndPos; ++nPos)
+ {
+ if (CH_BLANK == rInf.GetText()[sal_Int32(nPos)])
+ ++nSpaceCnt;
+ }
+ if( nSpaceCnt )
+ aRect.Width( aRect.Width() + nSpaceCnt * rInf.GetSpace() );
+ }
+ else
+ aRect.Width( aRect.Width() - sal_Int32(nTmpLen) * rInf.GetSpace() );
+ }
+
+ if( aClipSave.IsOn() && rInf.GetOut().IsClipRegion() )
+ {
+ SwRect aClipRect( rInf.GetOut().GetClipRegion().GetBoundRect() );
+ aRect.Intersection( aClipRect );
+ }
+
+ SwRegionRects aRegion( aRect );
+
+ bool bOpaque = false;
+ // #i68520#
+ const sal_uInt32 nCurrOrd = mpCurrAnchoredObj
+ ? mpCurrAnchoredObj->GetDrawObj()->GetOrdNum()
+ : SAL_MAX_UINT32;
+ OSL_ENSURE( !m_bTopRule, "DrawTextOpaque: Wrong TopRule" );
+
+ // #i68520#
+ const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList()->size() : 0 );
+ if (nCount > 0)
+ {
+ const SdrLayerID nHellId = m_pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
+ for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
+ {
+ // #i68520#
+ const SwAnchoredObject* pTmpAnchoredObj = (*mpAnchoredObjList)[i];
+ const SwFlyFrame* pFly = pTmpAnchoredObj->DynCastFlyFrame();
+ if( pFly && mpCurrAnchoredObj != pTmpAnchoredObj )
+ {
+ // #i68520#
+ if( aRegion.GetOrigin().Overlaps( pFly->getFrameArea() ) )
+ {
+ const SwFrameFormat *pFormat = pFly->GetFormat();
+ const SwFormatSurround &rSur = pFormat->GetSurround();
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ // Only the ones who are opaque and more to the top
+ if( ! pFly->IsBackgroundTransparent() &&
+ css::text::WrapTextMode_THROUGH == rSur.GetSurround() &&
+ ( !rSur.IsAnchorOnly() ||
+ // #i68520#
+ GetMaster() == pFly->GetAnchorFrame() ||
+ ((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId())
+ )
+ ) &&
+ // #i68520#
+ pTmpAnchoredObj->GetDrawObj()->GetLayer() != nHellId &&
+ nCurrOrd < pTmpAnchoredObj->GetDrawObj()->GetOrdNum()
+ )
+ {
+ // Except for the content is transparent
+ const SwNoTextFrame *pNoText =
+ pFly->Lower() && pFly->Lower()->IsNoTextFrame()
+ ? static_cast<const SwNoTextFrame*>(pFly->Lower())
+ : nullptr;
+ if ( !pNoText ||
+ (!pNoText->IsTransparent() && !rSur.IsContour()) )
+ {
+ bOpaque = true;
+ aRegion -= pFly->getFrameArea();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Point aPos( rInf.GetPos().X(), rInf.GetPos().Y() + rInf.GetAscent() );
+ const Point aOldPos(rInf.GetPos());
+ rInf.SetPos( aPos );
+
+ if( !bOpaque )
+ {
+ if( rInf.GetKern() )
+ rInf.GetFont()->DrawStretchText_( rInf );
+ else
+ rInf.GetFont()->DrawText_( rInf );
+ rInf.SetPos(aOldPos);
+ return;
+ }
+ else if( !aRegion.empty() )
+ {
+ // What a huge effort ...
+ SwSaveClip aClipVout( rInf.GetpOut() );
+ for( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ SwRect &rRect = aRegion[i];
+ if( rRect != aRegion.GetOrigin() )
+ aClipVout.ChgClip( rRect );
+ if( rInf.GetKern() )
+ rInf.GetFont()->DrawStretchText_( rInf );
+ else
+ rInf.GetFont()->DrawText_( rInf );
+ }
+ }
+ rInf.SetPos(aOldPos);
+}
+
+void SwTextFly::DrawFlyRect( OutputDevice* pOut, const SwRect &rRect )
+{
+ SwRegionRects aRegion( rRect );
+ OSL_ENSURE( !m_bTopRule, "DrawFlyRect: Wrong TopRule" );
+ // #i68520#
+ const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList()->size() : 0 );
+ if (nCount > 0)
+ {
+ const SdrLayerID nHellId = m_pPage->getRootFrame()->GetCurrShell()->getIDocumentDrawModelAccess().GetHellId();
+ for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
+ {
+ // #i68520#
+ const SwAnchoredObject* pAnchoredObjTmp = (*mpAnchoredObjList)[i];
+ if (mpCurrAnchoredObj == pAnchoredObjTmp)
+ continue;
+
+ // #i68520#
+ const SwFlyFrame* pFly = pAnchoredObjTmp->DynCastFlyFrame();
+ if (pFly)
+ {
+ // #i68520#
+ const SwFormatSurround& rSur = pAnchoredObjTmp->GetFrameFormat().GetSurround();
+
+ // OD 24.01.2003 #106593# - correct clipping of fly frame area.
+ // Consider that fly frame background/shadow can be transparent
+ // and <SwAlignRect(..)> fly frame area
+ // #i47804# - consider transparent graphics
+ // and OLE objects.
+ bool bClipFlyArea =
+ ( ( css::text::WrapTextMode_THROUGH == rSur.GetSurround() )
+ // #i68520#
+ ? (pAnchoredObjTmp->GetDrawObj()->GetLayer() != nHellId)
+ : !rSur.IsContour() ) &&
+ !pFly->IsBackgroundTransparent() &&
+ ( !pFly->Lower() ||
+ !pFly->Lower()->IsNoTextFrame() ||
+ !static_cast<const SwNoTextFrame*>(pFly->Lower())->IsTransparent() );
+ if ( bClipFlyArea )
+ {
+ // #i68520#
+ SwRect aFly( pAnchoredObjTmp->GetObjRect() );
+ // OD 24.01.2003 #106593#
+ ::SwAlignRect( aFly, m_pPage->getRootFrame()->GetCurrShell(), pOut );
+ if( !aFly.IsEmpty() )
+ aRegion -= aFly;
+ }
+ }
+ }
+ }
+
+ for( size_t i = 0; i < aRegion.size(); ++i )
+ {
+ pOut->DrawRect( aRegion[i].SVRect() );
+ }
+}
+
+/**
+ * #i26945# - change first parameter
+ * Now it's the <SwAnchoredObject> instance of the floating screen object
+ */
+bool SwTextFly::GetTop( const SwAnchoredObject* _pAnchoredObj,
+ const bool bInFootnote,
+ const bool bInFooterOrHeader )
+{
+ // #i68520#
+ // <mpCurrAnchoredObj> is set, if <m_pCurrFrame> is inside a fly frame
+ if( _pAnchoredObj != mpCurrAnchoredObj )
+ {
+ // #i26945#
+ const SdrObject* pNew = _pAnchoredObj->GetDrawObj();
+ // #102344# Ignore connectors which have one or more connections
+ if (const SdrEdgeObj* pEdgeObj = dynamic_cast<const SdrEdgeObj*>(pNew))
+ {
+ if (pEdgeObj->GetConnectedNode(true) || pEdgeObj->GetConnectedNode(false))
+ {
+ return false;
+ }
+ }
+
+ if( ( bInFootnote || bInFooterOrHeader ) && m_bTopRule )
+ {
+ // #i26945#
+ const SwFrameFormat& rFrameFormat = _pAnchoredObj->GetFrameFormat();
+ const SwFormatAnchor& rNewA = rFrameFormat.GetAnchor();
+ if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
+ {
+ if ( bInFootnote )
+ return false;
+
+ if ( bInFooterOrHeader )
+ {
+ const SwFormatVertOrient& aVert( rFrameFormat.GetVertOrient() );
+ bool bVertPrt = aVert.GetRelationOrient() == text::RelOrientation::PRINT_AREA ||
+ aVert.GetRelationOrient() == text::RelOrientation::PAGE_PRINT_AREA;
+ if( bVertPrt )
+ return false;
+ }
+ }
+ }
+
+ // #i68520#
+ // bEvade: consider pNew, if we are not inside a fly
+ // consider pNew, if pNew is lower of <mpCurrAnchoredObj>
+ bool bEvade = !mpCurrAnchoredObj ||
+ Is_Lower_Of( mpCurrAnchoredObj->DynCastFlyFrame(), pNew);
+
+ if ( !bEvade )
+ {
+ // We are currently inside a fly frame and pNew is not
+ // inside this fly frame. We can do some more checks if
+ // we have to consider pNew.
+
+ // If bTopRule is not set, we ignore the frame types.
+ // We directly check the z-order
+ if ( !m_bTopRule )
+ bEvade = true;
+ else
+ {
+ // Within chained Flys we only avoid Lower
+ // #i68520#
+ const SwFormatChain &rChain = mpCurrAnchoredObj->GetFrameFormat().GetChain();
+ if ( !rChain.GetPrev() && !rChain.GetNext() )
+ {
+ // #i26945#
+ const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat().GetAnchor();
+ // #i68520#
+ const SwFormatAnchor& rCurrA = mpCurrAnchoredObj->GetFrameFormat().GetAnchor();
+
+ // If <mpCurrAnchoredObj> is anchored as character, its content
+ // does not wrap around pNew
+ if (RndStdIds::FLY_AS_CHAR == rCurrA.GetAnchorId())
+ return false;
+
+ // If pNew is anchored to page and <mpCurrAnchoredObj is not anchored
+ // to page, the content of <mpCurrAnchoredObj> does not wrap around pNew
+ // If both pNew and <mpCurrAnchoredObj> are anchored to page, we can do
+ // some more checks
+ if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
+ {
+ if (RndStdIds::FLY_AT_PAGE == rCurrA.GetAnchorId())
+ {
+ bEvade = true;
+ }
+ else
+ return false;
+ }
+ else if (RndStdIds::FLY_AT_PAGE == rCurrA.GetAnchorId())
+ return false; // Page anchored ones only avoid page anchored ones
+ else if (RndStdIds::FLY_AT_FLY == rNewA.GetAnchorId())
+ bEvade = true; // Non-page anchored ones avoid frame anchored ones
+ else if( RndStdIds::FLY_AT_FLY == rCurrA.GetAnchorId() )
+ return false; // Frame anchored ones do not avoid paragraph anchored ones
+ // #i57062#
+ // In order to avoid loop situation, it's decided to adjust
+ // the wrapping behaviour of content of at-paragraph/at-character
+ // anchored objects to one in the page header/footer and
+ // the document body --> content of at-paragraph/at-character
+ // anchored objects doesn't wrap around each other.
+ else
+ return false;
+ }
+ }
+
+ // But: we never avoid a subordinate one and additionally we only avoid when overlapping.
+ // #i68520#
+ bEvade &= ( mpCurrAnchoredObj->GetDrawObj()->GetOrdNum() < pNew->GetOrdNum() );
+ if( bEvade )
+ {
+ // #i68520#
+ const SwRect& aTmp( _pAnchoredObj->GetObjRectWithSpaces() );
+ if ( !aTmp.Overlaps( mpCurrAnchoredObj->GetObjRectWithSpaces() ) )
+ bEvade = false;
+ }
+ }
+
+ if ( bEvade )
+ {
+ // #i26945#
+ const SwFormatAnchor& rNewA = _pAnchoredObj->GetFrameFormat().GetAnchor();
+ OSL_ENSURE( RndStdIds::FLY_AS_CHAR != rNewA.GetAnchorId(),
+ "Don't call GetTop with a FlyInContentFrame" );
+ if (RndStdIds::FLY_AT_PAGE == rNewA.GetAnchorId())
+ return true; // We always avoid page anchored ones
+
+ // If Flys anchored at paragraph are caught in a FlyCnt, then
+ // their influence ends at the borders of the FlyCnt!
+ // If we are currently formatting the text of the FlyCnt, then
+ // it has to get out of the way of the Frame anchored at paragraph!
+ // m_pCurrFrame is the anchor of pNew?
+ // #i26945#
+ const SwFrame* pTmp = _pAnchoredObj->GetAnchorFrame();
+ if (pTmp == m_pCurrFrame)
+ return true;
+ if( pTmp->IsTextFrame() && ( pTmp->IsInFly() || pTmp->IsInFootnote() ) )
+ {
+ // #i26945#
+ Point aPos = _pAnchoredObj->GetObjRect().Pos();
+ pTmp = GetVirtualUpper( pTmp, aPos );
+ }
+ // #i26945#
+ // If <pTmp> is a text frame inside a table, take the upper
+ // of the anchor frame, which contains the anchor position.
+ else if ( pTmp->IsTextFrame() && pTmp->IsInTab() )
+ {
+ pTmp = const_cast<SwAnchoredObject*>(_pAnchoredObj)
+ ->GetAnchorFrameContainingAnchPos()->GetUpper();
+ }
+ // #i28701# - consider all objects in same context,
+ // if wrapping style is considered on object positioning.
+ // Thus, text will wrap around negative positioned objects.
+ // #i3317# - remove condition on checking,
+ // if wrappings style is considered on object positioning.
+ // Thus, text is wrapping around negative positioned objects.
+ // #i35640# - no consideration of negative
+ // positioned objects, if wrapping style isn't considered on
+ // object position and former text wrapping is applied.
+ // This condition is typically for documents imported from the
+ // OpenOffice.org file format.
+ const IDocumentSettingAccess* pIDSA = &m_pCurrFrame->GetDoc().getIDocumentSettingAccess();
+ if ( ( pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) ||
+ !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ) &&
+ ::FindContext( pTmp, SwFrameType::None ) == ::FindContext(m_pCurrFrame, SwFrameType::None))
+ {
+ return true;
+ }
+
+ const SwFrame* pHeader = nullptr;
+ if (m_pCurrFrame->GetNext() != pTmp &&
+ (IsFrameInSameContext( pTmp, m_pCurrFrame ) ||
+ // #i13832#, #i24135# wrap around objects in page header
+ ( !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) &&
+ nullptr != ( pHeader = pTmp->FindFooterOrHeader() ) &&
+ m_pCurrFrame->IsInDocBody())))
+ {
+ if( pHeader || RndStdIds::FLY_AT_FLY == rNewA.GetAnchorId() )
+ return true;
+
+ // Compare indices:
+ // The Index of the other is retrieved from the anchor attr.
+ SwNodeOffset nTmpIndex = rNewA.GetContentAnchor()->nNode.GetIndex();
+ // Now check whether the current paragraph is before the anchor
+ // of the displaced object in the text, then we don't have to
+ // get out of its way.
+ // If possible determine Index via SwFormatAnchor because
+ // otherwise it's quite expensive.
+ if (NODE_OFFSET_MAX == m_nCurrFrameNodeIndex)
+ m_nCurrFrameNodeIndex = m_pCurrFrame->GetTextNodeFirst()->GetIndex();
+
+ if (FrameContainsNode(*m_pCurrFrame, nTmpIndex) || nTmpIndex < m_nCurrFrameNodeIndex)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// #i68520#
+SwAnchoredObjList* SwTextFly::InitAnchoredObjList()
+{
+ OSL_ENSURE( m_pCurrFrame, "InitFlyList: No Frame, no FlyList" );
+ // #i68520#
+ OSL_ENSURE( !mpAnchoredObjList, "InitFlyList: FlyList already initialized" );
+
+ SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
+
+ const SwSortedObjs *pSorted = m_pPage->GetSortedObjs();
+ const size_t nCount = pSorted ? pSorted->size() : 0;
+ // --> #108724# Page header/footer content doesn't have to wrap around
+ // floating screen objects
+ // which was added simply to be compatible with MS Office.
+ // MSO still allows text to wrap around in-table-flies in headers/footers/footnotes
+ const bool bFooterHeader = nullptr != m_pCurrFrame->FindFooterOrHeader();
+ const IDocumentSettingAccess* pIDSA = &m_pCurrFrame->GetDoc().getIDocumentSettingAccess();
+ // #i40155# - check, if frame is marked not to wrap
+ const bool bAllowCompatWrap = m_pCurrFrame->IsInTab() && (bFooterHeader || m_pCurrFrame->IsInFootnote());
+ const bool bWrapAllowed = ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) ||
+ bAllowCompatWrap ||
+ (!m_pCurrFrame->IsInFootnote() && !bFooterHeader));
+
+ m_bOn = false;
+
+ if( nCount && bWrapAllowed )
+ {
+ // #i68520#
+ mpAnchoredObjList.reset(new SwAnchoredObjList );
+
+ // #i28701# - consider complete frame area for new
+ // text wrapping
+ SwRect aRect;
+ if ( pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) )
+ {
+ aRect = m_pCurrFrame->getFramePrintArea();
+ aRect += m_pCurrFrame->getFrameArea().Pos();
+ }
+ else
+ {
+ aRect = m_pCurrFrame->getFrameArea();
+ }
+ // Make ourselves a little smaller than we are,
+ // so that 1-Twip-overlappings are ignored (#49532)
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+ const tools::Long nRight = aRectFnSet.GetRight(aRect) - 1;
+ const tools::Long nLeft = aRectFnSet.GetLeft(aRect) + 1;
+ const bool bR2L = m_pCurrFrame->IsRightToLeft();
+
+ const IDocumentDrawModelAccess& rIDDMA = m_pCurrFrame->GetDoc().getIDocumentDrawModelAccess();
+
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ // #i68520#
+ // do not consider hidden objects
+ // check, if object has to be considered for text wrap
+ // #118809# - If requested, do not consider
+ // objects in page header|footer for text frames not in page
+ // header|footer. This is requested for the calculation of
+ // the base offset for objects <SwTextFrame::CalcBaseOfstForFly()>
+ // #i20505# Do not consider oversized objects
+ SwAnchoredObject* pAnchoredObj = (*pSorted)[ i ];
+ assert(pAnchoredObj);
+ if ( !pAnchoredObj ||
+ !rIDDMA.IsVisibleLayerId( pAnchoredObj->GetDrawObj()->GetLayer() ) ||
+ !pAnchoredObj->ConsiderForTextWrap() ||
+ ( mbIgnoreObjsInHeaderFooter && !bFooterHeader &&
+ pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader() ) ||
+ ( bAllowCompatWrap && !pAnchoredObj->GetFrameFormat().GetFollowTextFlow().GetValue() )
+ )
+ {
+ continue;
+ }
+
+ const SwRect aBound( pAnchoredObj->GetObjRectWithSpaces() );
+ if ( nRight < aRectFnSet.GetLeft(aBound) ||
+ aRectFnSet.YDiff( aRectFnSet.GetTop(aRect),
+ aRectFnSet.GetBottom(aBound) ) > 0 ||
+ nLeft > aRectFnSet.GetRight(aBound) ||
+ aRectFnSet.GetHeight(aBound) >
+ 2 * aRectFnSet.GetHeight(m_pPage->getFrameArea()) )
+ {
+ continue;
+ }
+
+ // #i26945# - pass <pAnchoredObj> to method
+ // <GetTop(..)> instead of only the <SdrObject> instance of the
+ // anchored object
+ if (GetTop(pAnchoredObj, m_pCurrFrame->IsInFootnote(), bFooterHeader))
+ {
+ // OD 11.03.2003 #107862# - adjust insert position:
+ // overlapping objects should be sorted from left to right and
+ // inside left to right sorting from top to bottom.
+ // If objects on the same position are found, they are sorted
+ // on its width.
+ // #i68520#
+ {
+ SwAnchoredObjList::iterator aInsPosIter =
+ std::lower_bound( mpAnchoredObjList->begin(),
+ mpAnchoredObjList->end(),
+ pAnchoredObj,
+ AnchoredObjOrder( bR2L, aRectFnSet.FnRect() ) );
+
+ mpAnchoredObjList->insert( aInsPosIter, pAnchoredObj );
+ }
+
+ const SwFormatSurround &rFlyFormat = pAnchoredObj->GetFrameFormat().GetSurround();
+ // #i68520#
+ if ( rFlyFormat.IsAnchorOnly() &&
+ pAnchoredObj->GetAnchorFrame() == GetMaster() )
+ {
+ const SwFormatVertOrient &rTmpFormat =
+ pAnchoredObj->GetFrameFormat().GetVertOrient();
+ if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
+ m_nMinBottom = ( aRectFnSet.IsVert() && m_nMinBottom ) ?
+ std::min( m_nMinBottom, aBound.Left() ) :
+ std::max( m_nMinBottom, aRectFnSet.GetBottom(aBound) );
+ }
+
+ m_bOn = true;
+ }
+ }
+ if( m_nMinBottom )
+ {
+ SwTwips nMax = aRectFnSet.GetPrtBottom(*m_pCurrFrame->GetUpper());
+ if( aRectFnSet.YDiff( m_nMinBottom, nMax ) > 0 )
+ m_nMinBottom = nMax;
+ }
+ }
+ else
+ {
+ // #i68520#
+ mpAnchoredObjList.reset( new SwAnchoredObjList );
+ }
+
+ // #i68520#
+ return mpAnchoredObjList.get();
+}
+
+SwTwips SwTextFly::CalcMinBottom() const
+{
+ SwTwips nRet = 0;
+ const SwContentFrame *pLclMaster = GetMaster();
+ OSL_ENSURE(pLclMaster, "SwTextFly without master");
+ const SwSortedObjs *pDrawObj = pLclMaster ? pLclMaster->GetDrawObjs() : nullptr;
+ const size_t nCount = pDrawObj ? pDrawObj->size() : 0;
+ if( nCount )
+ {
+ SwTwips nEndOfFrame = m_pCurrFrame->getFrameArea().Bottom();
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pDrawObj)[ i ];
+ const SwFormatSurround &rFlyFormat = pAnchoredObj->GetFrameFormat().GetSurround();
+ if( rFlyFormat.IsAnchorOnly() )
+ {
+ const SwFormatVertOrient &rTmpFormat =
+ pAnchoredObj->GetFrameFormat().GetVertOrient();
+ if( text::VertOrientation::BOTTOM != rTmpFormat.GetVertOrient() )
+ {
+ const SwRect& aBound( pAnchoredObj->GetObjRectWithSpaces() );
+ if( aBound.Top() < nEndOfFrame )
+ nRet = std::max( nRet, SwTwips(aBound.Bottom()) );
+ }
+ }
+ }
+ SwTwips nMax = m_pCurrFrame->GetUpper()->getFrameArea().Top() +
+ m_pCurrFrame->GetUpper()->getFramePrintArea().Bottom();
+ if( nRet > nMax )
+ nRet = nMax;
+ }
+ return nRet;
+}
+
+SwTwips SwTextFly::GetMaxBottom(const SwBreakPortion& rPortion, const SwTextFormatInfo& rInfo) const
+{
+ // Note that m_pCurrFrame is already swapped at this stage, so it's correct to bypass
+ // SwRectFnSet here.
+ SwTwips nRet = 0;
+ size_t nCount(m_bOn ? GetAnchoredObjList()->size() : 0);
+
+ // Get the horizontal position of the break portion in absolute twips. The frame area is in
+ // absolute twips, the frame's print area is relative to the frame area. Finally the portion's
+ // position is relative to the frame's print area.
+ SwTwips nX = rInfo.X();
+ nX += m_pCurrFrame->getFrameArea().Left();
+ nX += m_pCurrFrame->getFramePrintArea().Left();
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i];
+
+ if (pAnchoredObj->GetAnchorFrame()->FindFooterOrHeader())
+ {
+ // Anchored in the header or footer, ignore it for clearing break purposes.
+ continue;
+ }
+
+ SwRect aRect(pAnchoredObj->GetObjRectWithSpaces());
+
+ if (m_pCurrFrame->IsVertical())
+ {
+ m_pCurrFrame->SwitchVerticalToHorizontal(aRect);
+ }
+
+ if (rPortion.GetClear() == SwLineBreakClear::LEFT)
+ {
+ if (nX < aRect.Left())
+ {
+ // Want to jump down to the first line that's unblocked on the left. This object is
+ // on the right of the break, ignore it.
+ continue;
+ }
+ }
+ if (rPortion.GetClear() == SwLineBreakClear::RIGHT)
+ {
+ if (nX > aRect.Right())
+ {
+ // Want to jump down to the first line that's unblocked on the right. This object is
+ // on the left of the break, ignore it.
+ continue;
+ }
+ }
+ SwTwips nBottom = aRect.Top() + aRect.Height();
+ if (nBottom > nRet)
+ {
+ nRet = nBottom;
+ }
+ }
+ return nRet;
+}
+
+bool SwTextFly::ForEach( const SwRect &rRect, SwRect* pRect, bool bAvoid ) const
+{
+ SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
+
+ // Optimization
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+
+ // tdf#127235 stop if the area is larger than the page
+ if( aRectFnSet.GetHeight(m_pPage->getFrameArea()) < aRectFnSet.GetHeight(rRect))
+ {
+ // get the doc model description
+ const SwPageDesc* pPageDesc = m_pPage->GetPageDesc();
+
+ // if there is no next page style or it is the same as the current
+ // => stop trying to place the frame (it would end in an infinite loop)
+ if( pPageDesc &&
+ ( !pPageDesc->GetFollow() || pPageDesc->GetFollow() == pPageDesc) )
+ {
+ return false;
+ }
+ }
+
+ bool bRet = false;
+ // #i68520#
+ const SwAnchoredObjList::size_type nCount( m_bOn ? GetAnchoredObjList()->size() : 0 );
+ if (nCount > 0)
+ {
+ for( SwAnchoredObjList::size_type i = 0; i < nCount; ++i )
+ {
+ // #i68520#
+ const SwAnchoredObject* pAnchoredObj = (*mpAnchoredObjList)[i];
+
+ SwRect aRect( pAnchoredObj->GetObjRectWithSpaces() );
+
+ if( aRectFnSet.GetLeft(aRect) > aRectFnSet.GetRight(rRect) )
+ break;
+
+ // #i68520#
+ if ( mpCurrAnchoredObj != pAnchoredObj && aRect.Overlaps( rRect ) )
+ {
+ // #i68520#
+ const SwFormat* pFormat( &(pAnchoredObj->GetFrameFormat()) );
+ const SwFormatSurround &rSur = pFormat->GetSurround();
+ if( bAvoid )
+ {
+ // If the text flows below, it has no influence on
+ // formatting. In LineIter::DrawText() it is "just"
+ // necessary to cleverly set the ClippingRegions
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if( ( css::text::WrapTextMode_THROUGH == rSur.GetSurround() &&
+ ( !rSur.IsAnchorOnly() ||
+ // #i68520#
+ GetMaster() == pAnchoredObj->GetAnchorFrame() ||
+ ((RndStdIds::FLY_AT_PARA != rAnchor.GetAnchorId()) &&
+ (RndStdIds::FLY_AT_CHAR != rAnchor.GetAnchorId())) ) )
+ || aRect.Top() == FAR_AWAY )
+ continue;
+ }
+
+ // #i58642#
+ // Compare <GetMaster()> instead of <m_pCurrFrame> with the
+ // anchor frame of the anchored object, because a follow frame
+ // has to ignore the anchored objects of its master frame.
+ // Note: Anchored objects are always registered at the master
+ // frame, exception are as-character anchored objects,
+ // but these aren't handled here.
+ // #i68520#
+ if ( mbIgnoreCurrentFrame &&
+ GetMaster() == pAnchoredObj->GetAnchorFrame() )
+ continue;
+
+ if( pRect )
+ {
+ // #i68520#
+ SwRect aFly = AnchoredObjToRect( pAnchoredObj, rRect );
+ if( aFly.IsEmpty() || !aFly.Overlaps( rRect ) )
+ continue;
+ if( !bRet || (
+ (!m_pCurrFrame->IsRightToLeft() &&
+ ( aRectFnSet.GetLeft(aFly) <
+ aRectFnSet.GetLeft(*pRect) ) ) ||
+ (m_pCurrFrame->IsRightToLeft() &&
+ ( aRectFnSet.GetRight(aFly) >
+ aRectFnSet.GetRight(*pRect) ) ) ) )
+ *pRect = aFly;
+ if( rSur.IsContour() )
+ {
+ bRet = true;
+ continue;
+ }
+ }
+ bRet = true;
+ break;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+// #i68520#
+SwAnchoredObjList::size_type SwTextFly::GetPos( const SwAnchoredObject* pAnchoredObj ) const
+{
+ SwAnchoredObjList::size_type nCount = GetAnchoredObjList()->size();
+ SwAnchoredObjList::size_type nRet = 0;
+ while ( nRet < nCount && pAnchoredObj != (*mpAnchoredObjList)[ nRet ] )
+ ++nRet;
+ return nRet;
+}
+
+// #i68520#
+void SwTextFly::CalcRightMargin( SwRect &rFly,
+ SwAnchoredObjList::size_type nFlyPos,
+ const SwRect &rLine ) const
+{
+ // Usually the right margin is the right margin of the Printarea
+ OSL_ENSURE( !m_pCurrFrame->IsVertical() || !m_pCurrFrame->IsSwapped(),
+ "SwTextFly::CalcRightMargin with swapped frame" );
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+ // #118796# - correct determination of right of printing area
+ SwTwips nRight = aRectFnSet.GetPrtRight(*m_pCurrFrame);
+ SwTwips nFlyRight = aRectFnSet.GetRight(rFly);
+ SwRect aLine( rLine );
+ aRectFnSet.SetRight( aLine, nRight );
+ aRectFnSet.SetLeft( aLine, aRectFnSet.GetLeft(rFly) );
+
+ // It is possible that there is another object that is _above_ us
+ // and protrudes into the same line.
+ // Flys with run-through are invisible for those below, i.e., they
+ // are ignored for computing the margins of other Flys.
+ // 3301: pNext->getFrameArea().Overlaps( rLine ) is necessary
+ // #i68520#
+ css::text::WrapTextMode eSurroundForTextWrap;
+
+ bool bStop = false;
+ // #i68520#
+ SwAnchoredObjList::size_type nPos = 0;
+
+ // #i68520#
+ while( nPos < mpAnchoredObjList->size() && !bStop )
+ {
+ if( nPos == nFlyPos )
+ {
+ ++nPos;
+ continue;
+ }
+ // #i68520#
+ const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nPos++ ];
+ if ( pNext == mpCurrAnchoredObj )
+ continue;
+ eSurroundForTextWrap = GetSurroundForTextWrap( pNext );
+ if( css::text::WrapTextMode_THROUGH == eSurroundForTextWrap )
+ continue;
+
+ const SwRect aTmp( SwContourCache::CalcBoundRect
+ ( pNext, aLine, m_pCurrFrame, nFlyRight, true ) );
+ SwTwips nTmpRight = aRectFnSet.GetRight(aTmp);
+
+ // optimization:
+ // Record in nNextTop at which Y-position frame related changes are
+ // likely. This is so that, despite only looking at frames in the
+ // current line height, for frames without wrap the line height is
+ // incremented so that with a single line the lower border of the frame
+ // (or possibly the upper border of another frame) is reached.
+ // Especially in HTML documents there are often (dummy) paragraphs in
+ // 2 pt font, and they used to only evade big frames after huge numbers
+ // of empty lines.
+ const tools::Long nTmpTop = aRectFnSet.GetTop(aTmp);
+ if( aRectFnSet.YDiff( nTmpTop, aRectFnSet.GetTop(aLine) ) > 0 )
+ {
+ if( aRectFnSet.YDiff( m_nNextTop, nTmpTop ) > 0 )
+ SetNextTop( nTmpTop ); // upper border of next frame
+ }
+ else if (!aRectFnSet.GetWidth(aTmp)) // typical for Objects with contour wrap
+ { // For Objects with contour wrap that start before the current
+ // line, and end below it, but do not actually overlap it, the
+ // optimization has to be disabled, because the circumstances
+ // can change in the next line.
+ if( ! aRectFnSet.GetHeight(aTmp) ||
+ aRectFnSet.YDiff( aRectFnSet.GetBottom(aTmp),
+ aRectFnSet.GetTop(aLine) ) > 0 )
+ SetNextTop( 0 );
+ }
+ if( aTmp.Overlaps( aLine ) && nTmpRight > nFlyRight )
+ {
+ nFlyRight = nTmpRight;
+ if( css::text::WrapTextMode_RIGHT == eSurroundForTextWrap ||
+ css::text::WrapTextMode_PARALLEL == eSurroundForTextWrap )
+ {
+ // overrule the FlyFrame
+ if( nRight > nFlyRight )
+ nRight = nFlyRight;
+ bStop = true;
+ }
+ }
+ }
+ aRectFnSet.SetRight( rFly, nRight );
+}
+
+// #i68520#
+void SwTextFly::CalcLeftMargin( SwRect &rFly,
+ SwAnchoredObjList::size_type nFlyPos,
+ const SwRect &rLine ) const
+{
+ OSL_ENSURE( !m_pCurrFrame->IsVertical() || !m_pCurrFrame->IsSwapped(),
+ "SwTextFly::CalcLeftMargin with swapped frame" );
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+ // #118796# - correct determination of left of printing area
+ SwTwips nLeft = aRectFnSet.GetPrtLeft(*m_pCurrFrame);
+ const SwTwips nFlyLeft = aRectFnSet.GetLeft(rFly);
+
+ if( nLeft > nFlyLeft )
+ nLeft = rFly.Left();
+
+ SwRect aLine( rLine );
+ aRectFnSet.SetLeft( aLine, nLeft );
+
+ // It is possible that there is another object that is _above_ us
+ // and protrudes into the same line.
+ // Flys with run-through are invisible for those below, i.e., they
+ // are ignored for computing the margins of other Flys.
+ // 3301: pNext->getFrameArea().Overlaps( rLine ) is necessary
+
+ // #i68520#
+ SwAnchoredObjList::size_type nMyPos = nFlyPos;
+ while( ++nFlyPos < mpAnchoredObjList->size() )
+ {
+ // #i68520#
+ const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ];
+ const SwRect& aTmp( pNext->GetObjRectWithSpaces() );
+ if( aRectFnSet.GetLeft(aTmp) >= nFlyLeft )
+ break;
+ }
+
+ while( nFlyPos )
+ {
+ if( --nFlyPos == nMyPos )
+ continue;
+ // #i68520#
+ const SwAnchoredObject* pNext = (*mpAnchoredObjList)[ nFlyPos ];
+ if( pNext == mpCurrAnchoredObj )
+ continue;
+ css::text::WrapTextMode eSurroundForTextWrap = GetSurroundForTextWrap( pNext );
+ if( css::text::WrapTextMode_THROUGH == eSurroundForTextWrap )
+ continue;
+
+ const SwRect aTmp( SwContourCache::CalcBoundRect
+ (pNext, aLine, m_pCurrFrame, nFlyLeft, false) );
+
+ if( aRectFnSet.GetLeft(aTmp) < nFlyLeft && aTmp.Overlaps( aLine ) )
+ {
+ // #118796# - no '+1', because <..fnGetRight>
+ // returns the correct value.
+ SwTwips nTmpRight = aRectFnSet.GetRight(aTmp);
+ if ( nLeft <= nTmpRight )
+ nLeft = nTmpRight;
+
+ break;
+ }
+ }
+ aRectFnSet.SetLeft( rFly, nLeft );
+}
+
+// #i68520#
+SwRect SwTextFly::AnchoredObjToRect( const SwAnchoredObject* pAnchoredObj,
+ const SwRect &rLine ) const
+{
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+
+ const tools::Long nXPos = m_pCurrFrame->IsRightToLeft() ?
+ rLine.Right() :
+ aRectFnSet.GetLeft(rLine);
+
+ SwRect aFly = mbIgnoreContour ?
+ pAnchoredObj->GetObjRectWithSpaces() :
+ SwContourCache::CalcBoundRect(pAnchoredObj, rLine, m_pCurrFrame,
+ nXPos, !m_pCurrFrame->IsRightToLeft());
+
+ if( !aFly.Width() )
+ return aFly;
+
+ // so the line may grow up to the lower edge of the frame
+ SetNextTop( aRectFnSet.GetBottom(aFly) );
+ SwAnchoredObjList::size_type nFlyPos = GetPos( pAnchoredObj );
+
+ // LEFT and RIGHT, we grow the rectangle.
+ // We have some problems, when several frames are to be seen.
+ // At the moment, only the easier case is assumed:
+ // + LEFT means that the text must flow on the left of the frame,
+ // that is the frame expands to the right edge of the print area
+ // or to the next frame.
+ // + RIGHT is the opposite.
+ // Otherwise the set distance between text and frame is always
+ // added up.
+ switch( GetSurroundForTextWrap( pAnchoredObj ) )
+ {
+ case css::text::WrapTextMode_LEFT :
+ {
+ CalcRightMargin( aFly, nFlyPos, rLine );
+ break;
+ }
+ case css::text::WrapTextMode_RIGHT :
+ {
+ CalcLeftMargin( aFly, nFlyPos, rLine );
+ break;
+ }
+ case css::text::WrapTextMode_NONE :
+ {
+ CalcRightMargin( aFly, nFlyPos, rLine );
+ CalcLeftMargin( aFly, nFlyPos, rLine );
+ break;
+ }
+ default:
+ break;
+ }
+ return aFly;
+}
+
+// #i68520#
+
+// Wrap only on sides with at least 2cm space for the text
+#define TEXT_MIN 1134
+
+// MS Word wraps on sides with even less space (value guessed).
+#define TEXT_MIN_SMALL 300
+
+// Wrap on both sides up to a frame width of 1.5cm
+#define FRAME_MAX 850
+
+css::text::WrapTextMode SwTextFly::GetSurroundForTextWrap( const SwAnchoredObject* pAnchoredObj ) const
+{
+ const SwFrameFormat* pFormat = &(pAnchoredObj->GetFrameFormat());
+ const SwFormatSurround &rFlyFormat = pFormat->GetSurround();
+ css::text::WrapTextMode eSurroundForTextWrap = rFlyFormat.GetSurround();
+
+ if( rFlyFormat.IsAnchorOnly() && pAnchoredObj->GetAnchorFrame() != GetMaster() )
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if ((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()))
+ {
+ return css::text::WrapTextMode_NONE;
+ }
+ }
+
+ // in cause of run-through and nowrap ignore smartly
+ if( css::text::WrapTextMode_THROUGH == eSurroundForTextWrap ||
+ css::text::WrapTextMode_NONE == eSurroundForTextWrap )
+ return eSurroundForTextWrap;
+
+ // left is left and right is right
+ if (m_pCurrFrame->IsRightToLeft())
+ {
+ if ( css::text::WrapTextMode_LEFT == eSurroundForTextWrap )
+ eSurroundForTextWrap = css::text::WrapTextMode_RIGHT;
+ else if ( css::text::WrapTextMode_RIGHT == eSurroundForTextWrap )
+ eSurroundForTextWrap = css::text::WrapTextMode_LEFT;
+ }
+
+ // "ideal page wrap":
+ if ( css::text::WrapTextMode_DYNAMIC == eSurroundForTextWrap )
+ {
+ SwRectFnSet aRectFnSet(m_pCurrFrame);
+ const tools::Long nCurrLeft = aRectFnSet.GetPrtLeft(*m_pCurrFrame);
+ const tools::Long nCurrRight = aRectFnSet.GetPrtRight(*m_pCurrFrame);
+ const SwRect& aRect( pAnchoredObj->GetObjRectWithSpaces() );
+ tools::Long nFlyLeft = aRectFnSet.GetLeft(aRect);
+ tools::Long nFlyRight = aRectFnSet.GetRight(aRect);
+
+ if ( nFlyRight < nCurrLeft || nFlyLeft > nCurrRight )
+ eSurroundForTextWrap = css::text::WrapTextMode_PARALLEL;
+ else
+ {
+ tools::Long nLeft = nFlyLeft - nCurrLeft;
+ tools::Long nRight = nCurrRight - nFlyRight;
+ if( nFlyRight - nFlyLeft > FRAME_MAX )
+ {
+ if( nLeft < nRight )
+ nLeft = 0;
+ else
+ nRight = 0;
+ }
+ const int textMin = GetMaster()->GetDoc()
+ .getIDocumentSettingAccess().get(DocumentSettingId::SURROUND_TEXT_WRAP_SMALL )
+ ? TEXT_MIN_SMALL : TEXT_MIN;
+
+ // In case there is no space on either side, then css::text::WrapTextMode_PARALLEL
+ // gives the same result when doing the initial layout or a layout
+ // update after editing, so prefer that over css::text::WrapTextMode_NONE.
+ if (nLeft == 0 && nRight == 0)
+ return css::text::WrapTextMode_PARALLEL;
+
+ if( nLeft < textMin )
+ nLeft = 0;
+ if( nRight < textMin )
+ nRight = 0;
+ if( nLeft )
+ eSurroundForTextWrap = nRight ? css::text::WrapTextMode_PARALLEL : css::text::WrapTextMode_LEFT;
+ else
+ eSurroundForTextWrap = nRight ? css::text::WrapTextMode_RIGHT: css::text::WrapTextMode_NONE;
+ }
+ }
+
+ return eSurroundForTextWrap;
+}
+
+bool SwTextFly::IsAnyFrame( const SwRect &rLine ) const
+{
+
+ SwSwapIfSwapped swap(const_cast<SwTextFrame *>(m_pCurrFrame));
+
+ OSL_ENSURE( m_bOn, "IsAnyFrame: Why?" );
+
+ return ForEach( rLine, nullptr, false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtfrm.cxx b/sw/source/core/text/txtfrm.cxx
new file mode 100644
index 000000000..9c4ac5b0e
--- /dev/null
+++ b/sw/source/core/text/txtfrm.cxx
@@ -0,0 +1,4110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <svl/ctloptions.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/pgrditem.hxx>
+#include <unotools/configmgr.hxx>
+#include <swmodule.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <doc.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewsh.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <viewopt.hxx>
+#include <flyfrm.hxx>
+#include <tabfrm.hxx>
+#include <frmatr.hxx>
+#include <frmtool.hxx>
+#include <tgrditem.hxx>
+#include <dbg_lay.hxx>
+#include <fmtfld.hxx>
+#include <fmtftn.hxx>
+#include <txtfld.hxx>
+#include <txtftn.hxx>
+#include <ftninfo.hxx>
+#include <fmtline.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <sectfrm.hxx>
+#include "itrform2.hxx"
+#include "widorp.hxx"
+#include "txtcache.hxx"
+#include <fntcache.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <lineinfo.hxx>
+#include <SwPortionHandler.hxx>
+#include <dcontact.hxx>
+#include <sortedobjs.hxx>
+#include <txtflcnt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtcntnt.hxx>
+#include <numrule.hxx>
+#include <IGrammarContact.hxx>
+#include <calbck.hxx>
+#include <ftnidx.hxx>
+#include <ftnfrm.hxx>
+
+#include <wrtsh.hxx>
+#include <view.hxx>
+#include <edtwin.hxx>
+#include <FrameControlsManager.hxx>
+
+namespace sw {
+
+ MergedAttrIterBase::MergedAttrIterBase(SwTextFrame const& rFrame)
+ : m_pMerged(rFrame.GetMergedPara())
+ , m_pNode(m_pMerged ? nullptr : rFrame.GetTextNodeFirst())
+ , m_CurrentExtent(0)
+ , m_CurrentHint(0)
+ {
+ }
+
+ SwTextAttr const* MergedAttrIter::NextAttr(SwTextNode const** ppNode)
+ {
+ if (m_pMerged)
+ {
+ while (m_CurrentExtent < m_pMerged->extents.size())
+ {
+ sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent]);
+ if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
+ {
+ while (m_CurrentHint < pHints->Count())
+ {
+ SwTextAttr *const pHint(pHints->Get(m_CurrentHint));
+ if (rExtent.nEnd < pHint->GetStart()
+ // <= if it has no end or isn't empty
+ || (rExtent.nEnd == pHint->GetStart()
+ && (!pHint->GetEnd()
+ || *pHint->GetEnd() != pHint->GetStart())))
+ {
+ break;
+ }
+ ++m_CurrentHint;
+ if (rExtent.nStart <= pHint->GetStart())
+ {
+ if (ppNode)
+ {
+ *ppNode = rExtent.pNode;
+ }
+ return pHint;
+ }
+ }
+ }
+ ++m_CurrentExtent;
+ if (m_CurrentExtent < m_pMerged->extents.size() &&
+ rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode)
+ {
+ m_CurrentHint = 0; // reset
+ }
+ }
+ return nullptr;
+ }
+ else
+ {
+ SwpHints const*const pHints(m_pNode->GetpSwpHints());
+ if (pHints)
+ {
+ if (m_CurrentHint < pHints->Count())
+ {
+ SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
+ ++m_CurrentHint;
+ if (ppNode)
+ {
+ *ppNode = m_pNode;
+ }
+ return pHint;
+ }
+ }
+ return nullptr;
+ }
+ }
+
+ MergedAttrIterByEnd::MergedAttrIterByEnd(SwTextFrame const& rFrame)
+ : m_pNode(rFrame.GetMergedPara() ? nullptr : rFrame.GetTextNodeFirst())
+ , m_CurrentHint(0)
+ {
+ if (!m_pNode)
+ {
+ MergedAttrIterReverse iter(rFrame);
+ SwTextNode const* pNode(nullptr);
+ while (SwTextAttr const* pHint = iter.PrevAttr(&pNode))
+ {
+ m_Hints.emplace_back(pNode, pHint);
+ }
+ }
+ }
+
+ SwTextAttr const* MergedAttrIterByEnd::NextAttr(SwTextNode const*& rpNode)
+ {
+ if (m_pNode)
+ {
+ SwpHints const*const pHints(m_pNode->GetpSwpHints());
+ if (pHints)
+ {
+ if (m_CurrentHint < pHints->Count())
+ {
+ SwTextAttr const*const pHint(
+ pHints->GetSortedByEnd(m_CurrentHint));
+ ++m_CurrentHint;
+ rpNode = m_pNode;
+ return pHint;
+ }
+ }
+ return nullptr;
+ }
+ else
+ {
+ if (m_CurrentHint < m_Hints.size())
+ {
+ auto const ret = m_Hints[m_Hints.size() - m_CurrentHint - 1];
+ ++m_CurrentHint;
+ rpNode = ret.first;
+ return ret.second;
+ }
+ return nullptr;
+ }
+ }
+
+ void MergedAttrIterByEnd::PrevAttr()
+ {
+ assert(0 < m_CurrentHint); // should only rewind as far as 0
+ --m_CurrentHint;
+ }
+
+ MergedAttrIterReverse::MergedAttrIterReverse(SwTextFrame const& rFrame)
+ : MergedAttrIterBase(rFrame)
+ {
+ if (m_pMerged)
+ {
+ m_CurrentExtent = m_pMerged->extents.size();
+ SwpHints const*const pHints(0 < m_CurrentExtent
+ ? m_pMerged->extents[m_CurrentExtent-1].pNode->GetpSwpHints()
+ : nullptr);
+ if (pHints)
+ {
+ pHints->SortIfNeedBe();
+ m_CurrentHint = pHints->Count();
+ }
+ }
+ else
+ {
+ if (SwpHints const*const pHints = m_pNode->GetpSwpHints())
+ {
+ pHints->SortIfNeedBe();
+ m_CurrentHint = pHints->Count();
+ }
+ }
+ }
+
+ SwTextAttr const* MergedAttrIterReverse::PrevAttr(SwTextNode const** ppNode)
+ {
+ if (m_pMerged)
+ {
+ while (0 < m_CurrentExtent)
+ {
+ sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent-1]);
+ if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
+ {
+ while (0 < m_CurrentHint)
+ {
+ SwTextAttr *const pHint(
+ pHints->GetSortedByEnd(m_CurrentHint - 1));
+ if (pHint->GetAnyEnd() < rExtent.nStart
+ // <= if it has end and isn't empty
+ || (pHint->GetEnd()
+ && *pHint->GetEnd() != pHint->GetStart()
+ && *pHint->GetEnd() == rExtent.nStart))
+ {
+ break;
+ }
+ --m_CurrentHint;
+ if (pHint->GetAnyEnd() <= rExtent.nEnd)
+ {
+ if (ppNode)
+ {
+ *ppNode = rExtent.pNode;
+ }
+ return pHint;
+ }
+ }
+ }
+ --m_CurrentExtent;
+ if (0 < m_CurrentExtent &&
+ rExtent.pNode != m_pMerged->extents[m_CurrentExtent-1].pNode)
+ {
+ SwpHints const*const pHints(
+ m_pMerged->extents[m_CurrentExtent-1].pNode->GetpSwpHints());
+ m_CurrentHint = pHints ? pHints->Count() : 0; // reset
+ if (pHints)
+ pHints->SortIfNeedBe();
+ }
+ }
+ return nullptr;
+ }
+ else
+ {
+ SwpHints const*const pHints(m_pNode->GetpSwpHints());
+ if (pHints && 0 < m_CurrentHint)
+ {
+ SwTextAttr const*const pHint(pHints->GetSortedByEnd(m_CurrentHint - 1));
+ --m_CurrentHint;
+ if (ppNode)
+ {
+ *ppNode = m_pNode;
+ }
+ return pHint;
+ }
+ return nullptr;
+ }
+ }
+
+ bool FrameContainsNode(SwContentFrame const& rFrame, SwNodeOffset const nNodeIndex)
+ {
+ if (rFrame.IsTextFrame())
+ {
+ SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(rFrame));
+ if (sw::MergedPara const*const pMerged = rTextFrame.GetMergedPara())
+ {
+ SwNodeOffset const nFirst(pMerged->pFirstNode->GetIndex());
+ SwNodeOffset const nLast(pMerged->pLastNode->GetIndex());
+ return (nFirst <= nNodeIndex && nNodeIndex <= nLast);
+ }
+ else
+ {
+ return rTextFrame.GetTextNodeFirst()->GetIndex() == nNodeIndex;
+ }
+ }
+ else
+ {
+ assert(rFrame.IsNoTextFrame());
+ return static_cast<SwNoTextFrame const&>(rFrame).GetNode()->GetIndex() == nNodeIndex;
+ }
+ }
+
+ bool IsParaPropsNode(SwRootFrame const& rLayout, SwTextNode const& rNode)
+ {
+ if (rLayout.HasMergedParas())
+ {
+ if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(rNode.getLayoutFrame(&rLayout)))
+ {
+ sw::MergedPara const*const pMerged(pFrame->GetMergedPara());
+ if (pMerged && pMerged->pParaPropsNode != &rNode)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ SwTextNode *
+ GetParaPropsNode(SwRootFrame const& rLayout, SwNodeIndex const& rPos)
+ {
+ SwTextNode *const pTextNode(rPos.GetNode().GetTextNode());
+ if (pTextNode && !sw::IsParaPropsNode(rLayout, *pTextNode))
+ {
+ return static_cast<SwTextFrame*>(pTextNode->getLayoutFrame(&rLayout))->GetMergedPara()->pParaPropsNode;
+ }
+ else
+ {
+ return pTextNode;
+ }
+ }
+
+ SwPosition
+ GetParaPropsPos(SwRootFrame const& rLayout, SwPosition const& rPos)
+ {
+ SwPosition pos(rPos);
+ SwTextNode const*const pNode(pos.nNode.GetNode().GetTextNode());
+ if (pNode)
+ {
+ pos.nNode = *sw::GetParaPropsNode(rLayout, *pNode);
+ pos.nContent.Assign(pos.nNode.GetNode().GetContentNode(), 0);
+ }
+ return pos;
+ }
+
+ std::pair<SwTextNode *, SwTextNode *>
+ GetFirstAndLastNode(SwRootFrame const& rLayout, SwNodeIndex const& rPos)
+ {
+ SwTextNode *const pTextNode(rPos.GetNode().GetTextNode());
+ if (pTextNode && rLayout.HasMergedParas())
+ {
+ if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pTextNode->getLayoutFrame(&rLayout)))
+ {
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ return std::make_pair(pMerged->pFirstNode, const_cast<SwTextNode*>(pMerged->pLastNode));
+ }
+ }
+ }
+ return std::make_pair(pTextNode, pTextNode);
+ }
+
+ SwTextNode const& GetAttrMerged(SfxItemSet & rFormatSet,
+ SwTextNode const& rNode, SwRootFrame const*const pLayout)
+ {
+ rNode.SwContentNode::GetAttr(rFormatSet);
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ auto pFrame = static_cast<SwTextFrame*>(rNode.getLayoutFrame(pLayout));
+ if (sw::MergedPara const*const pMerged = pFrame ? pFrame->GetMergedPara() : nullptr)
+ {
+ if (pMerged->pFirstNode != &rNode)
+ {
+ rFormatSet.ClearItem(RES_PAGEDESC);
+ rFormatSet.ClearItem(RES_BREAK);
+ static_assert(RES_PAGEDESC + 1 == sal_uInt16(RES_BREAK),
+ "first-node items must be adjacent");
+ SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(*rFormatSet.GetPool());
+ pMerged->pFirstNode->SwContentNode::GetAttr(firstSet);
+ rFormatSet.Put(firstSet);
+
+ }
+ if (pMerged->pParaPropsNode != &rNode)
+ {
+ for (sal_uInt16 i = RES_PARATR_BEGIN; i != RES_FRMATR_END; ++i)
+ {
+ if (i != RES_PAGEDESC && i != RES_BREAK)
+ {
+ rFormatSet.ClearItem(i);
+ }
+ }
+ for (sal_uInt16 i = XATTR_FILL_FIRST; i <= XATTR_FILL_LAST; ++i)
+ {
+ rFormatSet.ClearItem(i);
+ }
+ SfxItemSetFixed<RES_PARATR_BEGIN, RES_PAGEDESC,
+ RES_BREAK+1, RES_FRMATR_END,
+ XATTR_FILL_FIRST, XATTR_FILL_LAST+1>
+ propsSet(*rFormatSet.GetPool());
+ pMerged->pParaPropsNode->SwContentNode::GetAttr(propsSet);
+ rFormatSet.Put(propsSet);
+ return *pMerged->pParaPropsNode;
+ }
+ // keep all the CHRATR/UNKNOWNATR anyway...
+ }
+ }
+ return rNode;
+ }
+
+} // namespace sw
+
+/// Switches width and height of the text frame
+void SwTextFrame::SwapWidthAndHeight()
+{
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+
+ if ( ! mbIsSwapped )
+ {
+ const tools::Long nPrtOfstX = aPrt.Pos().X();
+ aPrt.Pos().setX( aPrt.Pos().Y() );
+
+ if( IsVertLR() )
+ {
+ aPrt.Pos().setY( nPrtOfstX );
+ }
+ else
+ {
+ aPrt.Pos().setY( getFrameArea().Width() - ( nPrtOfstX + aPrt.Width() ) );
+ }
+ }
+ else
+ {
+ const tools::Long nPrtOfstY = aPrt.Pos().Y();
+ aPrt.Pos().setY( aPrt.Pos().X() );
+
+ if( IsVertLR() )
+ {
+ aPrt.Pos().setX( nPrtOfstY );
+ }
+ else
+ {
+ aPrt.Pos().setX( getFrameArea().Height() - ( nPrtOfstY + aPrt.Height() ) );
+ }
+ }
+
+ const tools::Long nPrtWidth = aPrt.Width();
+ aPrt.Width( aPrt.Height() );
+ aPrt.Height( nPrtWidth );
+ }
+
+ {
+ const tools::Long nFrameWidth = getFrameArea().Width();
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( aFrm.Height() );
+ aFrm.Height( nFrameWidth );
+ }
+
+ mbIsSwapped = ! mbIsSwapped;
+}
+
+/**
+ * Calculates the coordinates of a rectangle when switching from
+ * horizontal to vertical layout.
+ */
+void SwTextFrame::SwitchHorizontalToVertical( SwRect& rRect ) const
+{
+ // calc offset inside frame
+ tools::Long nOfstX, nOfstY;
+ if ( IsVertLR() )
+ {
+ if (IsVertLRBT())
+ {
+ // X and Y offsets here mean the position of the point that will be the top left corner
+ // after the switch.
+ nOfstX = rRect.Left() + rRect.Width() - getFrameArea().Left();
+ nOfstY = rRect.Top() - getFrameArea().Top();
+ }
+ else
+ {
+ nOfstX = rRect.Left() - getFrameArea().Left();
+ nOfstY = rRect.Top() - getFrameArea().Top();
+ }
+ }
+ else
+ {
+ nOfstX = rRect.Left() - getFrameArea().Left();
+ nOfstY = rRect.Top() + rRect.Height() - getFrameArea().Top();
+ }
+
+ const tools::Long nWidth = rRect.Width();
+ const tools::Long nHeight = rRect.Height();
+
+ if ( IsVertLR() )
+ {
+ rRect.Left(getFrameArea().Left() + nOfstY);
+ }
+ else
+ {
+ if ( mbIsSwapped )
+ rRect.Left( getFrameArea().Left() + getFrameArea().Height() - nOfstY );
+ else
+ // frame is rotated
+ rRect.Left( getFrameArea().Left() + getFrameArea().Width() - nOfstY );
+ }
+
+ if (IsVertLRBT())
+ {
+ if (mbIsSwapped)
+ rRect.Top(getFrameArea().Top() + getFrameArea().Width() - nOfstX);
+ else
+ rRect.Top(getFrameArea().Top() + getFrameArea().Height() - nOfstX);
+ }
+ else
+ rRect.Top(getFrameArea().Top() + nOfstX);
+ rRect.Width( nHeight );
+ rRect.Height( nWidth );
+}
+
+/**
+ * Calculates the coordinates of a point when switching from
+ * horizontal to vertical layout.
+ */
+void SwTextFrame::SwitchHorizontalToVertical( Point& rPoint ) const
+{
+ if (IsVertLRBT())
+ {
+ // The horizontal origo is the top left corner, the LRBT origo is the
+ // bottom left corner. Finally x and y has to be swapped.
+ SAL_WARN_IF(!mbIsSwapped, "sw.core",
+ "SwTextFrame::SwitchHorizontalToVertical, IsVertLRBT, not swapped");
+ Point aPoint(rPoint);
+ rPoint.setX(getFrameArea().Left() + (aPoint.Y() - getFrameArea().Top()));
+ // This would be bottom - x delta, but bottom is top + height, finally
+ // width (and not height), as it's swapped.
+ rPoint.setY(getFrameArea().Top() + getFrameArea().Width()
+ - (aPoint.X() - getFrameArea().Left()));
+ return;
+ }
+
+ // calc offset inside frame
+ const tools::Long nOfstX = rPoint.X() - getFrameArea().Left();
+ const tools::Long nOfstY = rPoint.Y() - getFrameArea().Top();
+ if ( IsVertLR() )
+ rPoint.setX( getFrameArea().Left() + nOfstY );
+ else
+ {
+ if ( mbIsSwapped )
+ rPoint.setX( getFrameArea().Left() + getFrameArea().Height() - nOfstY );
+ else
+ // calc rotated coords
+ rPoint.setX( getFrameArea().Left() + getFrameArea().Width() - nOfstY );
+ }
+
+ rPoint.setY( getFrameArea().Top() + nOfstX );
+}
+
+/**
+ * Calculates the a limit value when switching from
+ * horizontal to vertical layout.
+ */
+tools::Long SwTextFrame::SwitchHorizontalToVertical( tools::Long nLimit ) const
+{
+ Point aTmp( 0, nLimit );
+ SwitchHorizontalToVertical( aTmp );
+ return aTmp.X();
+}
+
+/**
+ * Calculates the coordinates of a rectangle when switching from
+ * vertical to horizontal layout.
+ */
+void SwTextFrame::SwitchVerticalToHorizontal( SwRect& rRect ) const
+{
+ tools::Long nOfstX;
+
+ // calc offset inside frame
+ if ( IsVertLR() )
+ nOfstX = rRect.Left() - getFrameArea().Left();
+ else
+ {
+ if ( mbIsSwapped )
+ nOfstX = getFrameArea().Left() + getFrameArea().Height() - ( rRect.Left() + rRect.Width() );
+ else
+ nOfstX = getFrameArea().Left() + getFrameArea().Width() - ( rRect.Left() + rRect.Width() );
+ }
+
+ tools::Long nOfstY;
+ if (IsVertLRBT())
+ {
+ // Note that mbIsSwapped only affects the frame area, not rRect, so rRect.Height() is used
+ // here unconditionally.
+ if (mbIsSwapped)
+ nOfstY = getFrameArea().Top() + getFrameArea().Width() - (rRect.Top() + rRect.Height());
+ else
+ nOfstY = getFrameArea().Top() + getFrameArea().Height() - (rRect.Top() + rRect.Height());
+ }
+ else
+ nOfstY = rRect.Top() - getFrameArea().Top();
+ const tools::Long nWidth = rRect.Height();
+ const tools::Long nHeight = rRect.Width();
+
+ // calc rotated coords
+ rRect.Left( getFrameArea().Left() + nOfstY );
+ rRect.Top( getFrameArea().Top() + nOfstX );
+ rRect.Width( nWidth );
+ rRect.Height( nHeight );
+}
+
+/**
+ * Calculates the coordinates of a point when switching from
+ * vertical to horizontal layout.
+ */
+void SwTextFrame::SwitchVerticalToHorizontal( Point& rPoint ) const
+{
+ tools::Long nOfstX;
+
+ // calc offset inside frame
+ if ( IsVertLR() )
+ // X offset is Y - left.
+ nOfstX = rPoint.X() - getFrameArea().Left();
+ else
+ {
+ // X offset is right - X.
+ if ( mbIsSwapped )
+ nOfstX = getFrameArea().Left() + getFrameArea().Height() - rPoint.X();
+ else
+ nOfstX = getFrameArea().Left() + getFrameArea().Width() - rPoint.X();
+ }
+
+ tools::Long nOfstY;
+ if (IsVertLRBT())
+ {
+ // Y offset is bottom - Y.
+ if (mbIsSwapped)
+ nOfstY = getFrameArea().Top() + getFrameArea().Width() - rPoint.Y();
+ else
+ nOfstY = getFrameArea().Top() + getFrameArea().Height() - rPoint.Y();
+ }
+ else
+ // Y offset is Y - top.
+ nOfstY = rPoint.Y() - getFrameArea().Top();
+
+ // calc rotated coords
+ rPoint.setX( getFrameArea().Left() + nOfstY );
+ rPoint.setY( getFrameArea().Top() + nOfstX );
+}
+
+/**
+ * Calculates the a limit value when switching from
+ * vertical to horizontal layout.
+ */
+tools::Long SwTextFrame::SwitchVerticalToHorizontal( tools::Long nLimit ) const
+{
+ Point aTmp( nLimit, 0 );
+ SwitchVerticalToHorizontal( aTmp );
+ return aTmp.Y();
+}
+
+SwFrameSwapper::SwFrameSwapper( const SwTextFrame* pTextFrame, bool bSwapIfNotSwapped )
+ : pFrame( pTextFrame ), bUndo( false )
+{
+ if (pFrame->IsVertical() && bSwapIfNotSwapped != pFrame->IsSwapped())
+ {
+ bUndo = true;
+ const_cast<SwTextFrame*>(pFrame)->SwapWidthAndHeight();
+ }
+}
+
+SwFrameSwapper::~SwFrameSwapper()
+{
+ if ( bUndo )
+ const_cast<SwTextFrame*>(pFrame)->SwapWidthAndHeight();
+}
+
+void SwTextFrame::SwitchLTRtoRTL( SwRect& rRect ) const
+{
+ SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
+
+ tools::Long nWidth = rRect.Width();
+ rRect.Left( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) +
+ getFramePrintArea().Width() - rRect.Right() - 1 );
+
+ rRect.Width( nWidth );
+}
+
+void SwTextFrame::SwitchLTRtoRTL( Point& rPoint ) const
+{
+ SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
+
+ rPoint.setX( 2 * ( getFrameArea().Left() + getFramePrintArea().Left() ) + getFramePrintArea().Width() - rPoint.X() - 1 );
+}
+
+SwLayoutModeModifier::SwLayoutModeModifier( const OutputDevice& rOutp ) :
+ m_rOut( rOutp ), m_nOldLayoutMode( rOutp.GetLayoutMode() )
+{
+}
+
+SwLayoutModeModifier::~SwLayoutModeModifier()
+{
+ const_cast<OutputDevice&>(m_rOut).SetLayoutMode( m_nOldLayoutMode );
+}
+
+void SwLayoutModeModifier::Modify( bool bChgToRTL )
+{
+ const_cast<OutputDevice&>(m_rOut).SetLayoutMode( bChgToRTL ?
+ vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl :
+ vcl::text::ComplexTextLayoutFlags::BiDiStrong );
+}
+
+void SwLayoutModeModifier::SetAuto()
+{
+ const vcl::text::ComplexTextLayoutFlags nNewLayoutMode = m_nOldLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ const_cast<OutputDevice&>(m_rOut).SetLayoutMode( nNewLayoutMode );
+}
+
+SwDigitModeModifier::SwDigitModeModifier( const OutputDevice& rOutp, LanguageType eCurLang ) :
+ rOut( rOutp ), nOldLanguageType( rOutp.GetDigitLanguage() )
+{
+ LanguageType eLang = eCurLang;
+ if (utl::ConfigManager::IsFuzzing())
+ eLang = LANGUAGE_ENGLISH_US;
+ else
+ {
+ const SvtCTLOptions::TextNumerals nTextNumerals = SW_MOD()->GetCTLOptions().GetCTLTextNumerals();
+
+ if ( SvtCTLOptions::NUMERALS_HINDI == nTextNumerals )
+ eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
+ else if ( SvtCTLOptions::NUMERALS_ARABIC == nTextNumerals )
+ eLang = LANGUAGE_ENGLISH;
+ else if ( SvtCTLOptions::NUMERALS_SYSTEM == nTextNumerals )
+ eLang = ::GetAppLanguage();
+ }
+
+ const_cast<OutputDevice&>(rOut).SetDigitLanguage( eLang );
+}
+
+SwDigitModeModifier::~SwDigitModeModifier()
+{
+ const_cast<OutputDevice&>(rOut).SetDigitLanguage( nOldLanguageType );
+}
+
+void SwTextFrame::Init()
+{
+ OSL_ENSURE( !IsLocked(), "+SwTextFrame::Init: this is locked." );
+ if( !IsLocked() )
+ {
+ ClearPara();
+ SetHasRotatedPortions(false);
+ // set flags directly to save a ResetPreps call,
+ // and thereby an unnecessary GetPara call
+ // don't set bOrphan, bLocked or bWait to false!
+ // bOrphan = bFlag7 = bFlag8 = false;
+ }
+}
+
+SwTextFrame::SwTextFrame(SwTextNode * const pNode, SwFrame* pSib,
+ sw::FrameMode const eMode)
+ : SwContentFrame( pNode, pSib )
+ , mnAllLines( 0 )
+ , mnThisLines( 0 )
+ , mnFlyAnchorOfst( 0 )
+ , mnFlyAnchorOfstNoWrap( 0 )
+ , mnFlyAnchorVertOfstNoWrap( 0 )
+ , mnFootnoteLine( 0 )
+ , mnHeightOfLastLine( 0 )
+ , mnAdditionalFirstLineOffset( 0 )
+ , mnOffset( 0 )
+ , mnCacheIndex( USHRT_MAX )
+ , mbLocked( false )
+ , mbWidow( false )
+ , mbJustWidow( false )
+ , mbEmpty( false )
+ , mbInFootnoteConnect( false )
+ , mbFootnote( false )
+ , mbRepaint( false )
+ , mbHasRotatedPortions( false )
+ , mbFieldFollow( false )
+ , mbHasAnimation( false )
+ , mbIsSwapped( false )
+ , mbFollowFormatAllowed( true )
+{
+ mnFrameType = SwFrameType::Txt;
+ // note: this may call SwClientNotify if it's in a list so do it last
+ // note: this may change this->pRegisteredIn to m_pMergedPara->listeners
+ m_pMergedPara = CheckParaRedlineMerge(*this, *pNode, eMode);
+}
+
+namespace sw {
+
+SwTextFrame * MakeTextFrame(SwTextNode & rNode, SwFrame *const pSibling,
+ sw::FrameMode const eMode)
+{
+ return new SwTextFrame(&rNode, pSibling, eMode);
+}
+
+void RemoveFootnotesForNode(
+ SwRootFrame const& rLayout, SwTextNode const& rTextNode,
+ std::vector<std::pair<sal_Int32, sal_Int32>> const*const pExtents)
+{
+ if (pExtents && pExtents->empty())
+ {
+ return; // nothing to do
+ }
+ const SwFootnoteIdxs &rFootnoteIdxs = rTextNode.GetDoc().GetFootnoteIdxs();
+ size_t nPos = 0;
+ SwNodeOffset const nIndex = rTextNode.GetIndex();
+ rFootnoteIdxs.SeekEntry( rTextNode, &nPos );
+ if (nPos < rFootnoteIdxs.size())
+ {
+ while (nPos && &rTextNode == &(rFootnoteIdxs[ nPos ]->GetTextNode()))
+ --nPos;
+ if (nPos || &rTextNode != &(rFootnoteIdxs[ nPos ]->GetTextNode()))
+ ++nPos;
+ }
+ size_t iter(0);
+ for ( ; nPos < rFootnoteIdxs.size(); ++nPos)
+ {
+ SwTextFootnote* pTextFootnote = rFootnoteIdxs[ nPos ];
+ if (pTextFootnote->GetTextNode().GetIndex() > nIndex)
+ break;
+ if (pExtents)
+ {
+ while ((*pExtents)[iter].second <= pTextFootnote->GetStart())
+ {
+ ++iter;
+ if (iter == pExtents->size())
+ {
+ return;
+ }
+ }
+ if (pTextFootnote->GetStart() < (*pExtents)[iter].first)
+ {
+ continue;
+ }
+ }
+ pTextFootnote->DelFrames(&rLayout);
+ }
+}
+
+} // namespace sw
+
+void SwTextFrame::DestroyImpl()
+{
+ // Remove associated SwParaPortion from s_pTextCache
+ ClearPara();
+
+ assert(!GetDoc().IsInDtor()); // this shouldn't be happening with ViewShell owning layout
+ if (!GetDoc().IsInDtor() && HasFootnote())
+ {
+ if (m_pMergedPara)
+ {
+ SwTextNode const* pNode(nullptr);
+ for (auto const& e : m_pMergedPara->extents)
+ {
+ if (e.pNode != pNode)
+ {
+ pNode = e.pNode;
+ // sw_redlinehide: not sure if it's necessary to check
+ // if the nodes are still alive here, which would require
+ // accessing WriterMultiListener::m_vDepends
+ sw::RemoveFootnotesForNode(*getRootFrame(), *pNode, nullptr);
+ }
+ }
+ }
+ else
+ {
+ SwTextNode *const pNode(static_cast<SwTextNode*>(GetDep()));
+ if (pNode)
+ {
+ sw::RemoveFootnotesForNode(*getRootFrame(), *pNode, nullptr);
+ }
+ }
+ }
+
+ if (!GetDoc().IsInDtor())
+ {
+ if (SwView* pView = GetActiveView())
+ pView->GetEditWin().GetFrameControlsManager().RemoveControls(this);
+ }
+
+ SwContentFrame::DestroyImpl();
+}
+
+SwTextFrame::~SwTextFrame()
+{
+ RemoveFromCache();
+}
+
+namespace sw {
+
+// 1. if real insert => correct nStart/nEnd for full nLen
+// 2. if rl un-delete => do not correct nStart/nEnd but just include un-deleted
+static TextFrameIndex UpdateMergedParaForInsert(MergedPara & rMerged,
+ bool const isRealInsert,
+ SwTextNode const& rNode, sal_Int32 const nIndex, sal_Int32 const nLen)
+{
+ assert(!isRealInsert || nLen); // can 0 happen? yes, for redline in empty node
+ assert(nIndex <= rNode.Len());
+ assert(nIndex + nLen <= rNode.Len());
+ assert(rMerged.pFirstNode->GetIndex() <= rNode.GetIndex() && rNode.GetIndex() <= rMerged.pLastNode->GetIndex());
+ if (!nLen)
+ {
+ return TextFrameIndex(0);
+ }
+ OUStringBuffer text(rMerged.mergedText);
+ sal_Int32 nTFIndex(0); // index used for insertion at the end
+ sal_Int32 nInserted(0);
+ bool bInserted(false);
+ bool bFoundNode(false);
+ auto itInsert(rMerged.extents.end());
+ for (auto it = rMerged.extents.begin(); it != rMerged.extents.end(); ++it)
+ {
+ if (it->pNode == &rNode)
+ {
+ if (isRealInsert)
+ {
+ bFoundNode = true;
+ if (it->nStart <= nIndex && nIndex <= it->nEnd)
+ { // note: this can happen only once
+ text.insert(nTFIndex + (nIndex - it->nStart),
+ rNode.GetText().subView(nIndex, nLen));
+ it->nEnd += nLen;
+ nInserted = nLen;
+ assert(!bInserted);
+ bInserted = true;
+ }
+ else if (nIndex < it->nStart)
+ {
+ if (itInsert == rMerged.extents.end())
+ {
+ itInsert = it;
+ }
+ it->nStart += nLen;
+ it->nEnd += nLen;
+ }
+ }
+ else
+ {
+ assert(it == rMerged.extents.begin() || (it-1)->pNode != &rNode || (it-1)->nEnd < nIndex);
+ if (nIndex + nLen < it->nStart)
+ {
+ itInsert = it;
+ break;
+ }
+ if (nIndex < it->nStart)
+ {
+ text.insert(nTFIndex,
+ rNode.GetText().subView(nIndex, it->nStart - nIndex));
+ nInserted += it->nStart - nIndex;
+ it->nStart = nIndex;
+ bInserted = true;
+ }
+ assert(it->nStart <= nIndex);
+ if (nIndex <= it->nEnd)
+ {
+ nTFIndex += it->nEnd - it->nStart;
+ while (it->nEnd < nIndex + nLen)
+ {
+ auto *const pNext(
+ (it+1) != rMerged.extents.end() && (it+1)->pNode == it->pNode
+ ? &*(it+1)
+ : nullptr);
+ if (pNext && pNext->nStart <= nIndex + nLen)
+ {
+ text.insert(nTFIndex,
+ rNode.GetText().subView(it->nEnd, pNext->nStart - it->nEnd));
+ nTFIndex += pNext->nStart - it->nEnd;
+ nInserted += pNext->nStart - it->nEnd;
+ pNext->nStart = it->nStart;
+ it = rMerged.extents.erase(it);
+ }
+ else
+ {
+ text.insert(nTFIndex,
+ rNode.GetText().subView(it->nEnd, nIndex + nLen - it->nEnd));
+ nTFIndex += nIndex + nLen - it->nEnd;
+ nInserted += nIndex + nLen - it->nEnd;
+ it->nEnd = nIndex + nLen;
+ }
+ }
+ bInserted = true;
+ break;
+ }
+ }
+ }
+ else if (rNode.GetIndex() < it->pNode->GetIndex() || bFoundNode)
+ {
+ if (itInsert == rMerged.extents.end())
+ {
+ itInsert = it;
+ }
+ break;
+ }
+ if (itInsert == rMerged.extents.end())
+ {
+ nTFIndex += it->nEnd - it->nStart;
+ }
+ }
+// assert((bFoundNode || rMerged.extents.empty()) && "text node not found - why is it sending hints to us");
+ if (!bInserted)
+ { // must be in a gap
+ rMerged.extents.emplace(itInsert, const_cast<SwTextNode*>(&rNode), nIndex, nIndex + nLen);
+ text.insert(nTFIndex, rNode.GetText().subView(nIndex, nLen));
+ nInserted = nLen;
+ if (rMerged.extents.size() == 1 // also if it was empty!
+ || rMerged.pParaPropsNode->GetIndex() < rNode.GetIndex())
+ { // text inserted after current para-props node
+ rMerged.pParaPropsNode->RemoveFromListRLHidden();
+ rMerged.pParaPropsNode = &const_cast<SwTextNode&>(rNode);
+ rMerged.pParaPropsNode->AddToListRLHidden();
+ }
+ // called from SwRangeRedline::InvalidateRange()
+ if (rNode.GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ const_cast<SwTextNode&>(rNode).SetRedlineMergeFlag(SwNode::Merge::NonFirst);
+ }
+ }
+ rMerged.mergedText = text.makeStringAndClear();
+ return TextFrameIndex(nInserted);
+}
+
+// 1. if real delete => correct nStart/nEnd for full nLen
+// 2. if rl delete => do not correct nStart/nEnd but just exclude deleted
+TextFrameIndex UpdateMergedParaForDelete(MergedPara & rMerged,
+ bool const isRealDelete,
+ SwTextNode const& rNode, sal_Int32 nIndex, sal_Int32 const nLen)
+{
+ assert(nIndex <= rNode.Len());
+ assert(rMerged.pFirstNode->GetIndex() <= rNode.GetIndex() && rNode.GetIndex() <= rMerged.pLastNode->GetIndex());
+ OUStringBuffer text(rMerged.mergedText);
+ sal_Int32 nTFIndex(0);
+ sal_Int32 nToDelete(nLen);
+ sal_Int32 nDeleted(0);
+ size_t nFoundNode(0);
+ size_t nErased(0);
+ auto it = rMerged.extents.begin();
+ for (; it != rMerged.extents.end(); )
+ {
+ bool bErase(false);
+ if (it->pNode == &rNode)
+ {
+ ++nFoundNode;
+ if (nIndex + nToDelete < it->nStart)
+ {
+ nToDelete = 0;
+ if (!isRealDelete)
+ {
+ break;
+ }
+ it->nStart -= nLen;
+ it->nEnd -= nLen;
+ }
+ else
+ {
+ if (nIndex < it->nStart)
+ {
+ // do not adjust nIndex into the text frame index space!
+ nToDelete -= it->nStart - nIndex;
+ nIndex = it->nStart;
+ // note: continue with the if check below, no else!
+ }
+ if (it->nStart <= nIndex && nIndex < it->nEnd)
+ {
+ sal_Int32 const nDeleteHere(nIndex + nToDelete <= it->nEnd
+ ? nToDelete
+ : it->nEnd - nIndex);
+ text.remove(nTFIndex + (nIndex - it->nStart), nDeleteHere);
+ bErase = nDeleteHere == it->nEnd - it->nStart;
+ if (bErase)
+ {
+ ++nErased;
+ assert(it->nStart == nIndex);
+ it = rMerged.extents.erase(it);
+ }
+ else if (isRealDelete)
+ { // adjust for deleted text
+ it->nStart -= (nLen - nToDelete);
+ it->nEnd -= (nLen - nToDelete + nDeleteHere);
+ if (it != rMerged.extents.begin()
+ && (it-1)->pNode == &rNode
+ && (it-1)->nEnd == it->nStart)
+ { // merge adjacent extents
+ nTFIndex += it->nEnd - it->nStart;
+ (it-1)->nEnd = it->nEnd;
+ it = rMerged.extents.erase(it);
+ bErase = true; // skip increment
+ }
+ }
+ else
+ { // exclude text marked as deleted
+ if (nIndex + nDeleteHere == it->nEnd)
+ {
+ it->nEnd -= nDeleteHere;
+ }
+ else
+ {
+ if (nIndex == it->nStart)
+ {
+ it->nStart += nDeleteHere;
+ }
+ else
+ {
+ sal_Int32 const nOldEnd(it->nEnd);
+ it->nEnd = nIndex;
+ it = rMerged.extents.emplace(it+1,
+ it->pNode, nIndex + nDeleteHere, nOldEnd);
+ }
+ assert(nDeleteHere == nToDelete);
+ }
+ }
+ nDeleted += nDeleteHere;
+ nToDelete -= nDeleteHere;
+ nIndex += nDeleteHere;
+ if (!isRealDelete && nToDelete == 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+ else if (nFoundNode != 0)
+ {
+ break;
+ }
+ if (!bErase)
+ {
+ nTFIndex += it->nEnd - it->nStart;
+ ++it;
+ }
+ }
+// assert(nFoundNode != 0 && "text node not found - why is it sending hints to us");
+ assert(nIndex <= rNode.Len() + nLen);
+ // if there's a remaining deletion, it must be in gap at the end of the node
+// can't do: might be last one in node was erased assert(nLen == 0 || rMerged.empty() || (it-1)->nEnd <= nIndex);
+ // note: if first node gets deleted then that must call DelFrames as
+ // pFirstNode is never updated
+ if (nErased && nErased == nFoundNode)
+ { // all visible text from node was erased
+#if 1
+ if (rMerged.pParaPropsNode == &rNode)
+ {
+ rMerged.pParaPropsNode->RemoveFromListRLHidden();
+ rMerged.pParaPropsNode = rMerged.extents.empty()
+ ? const_cast<SwTextNode*>(rMerged.pLastNode)
+ : rMerged.extents.front().pNode;
+ rMerged.pParaPropsNode->AddToListRLHidden();
+ }
+#endif
+// NOPE must listen on all non-hidden nodes; particularly on pLastNode rMerged.listener.EndListening(&const_cast<SwTextNode&>(rNode));
+ }
+ rMerged.mergedText = text.makeStringAndClear();
+ return TextFrameIndex(nDeleted);
+}
+
+std::pair<SwTextNode*, sal_Int32>
+MapViewToModel(MergedPara const& rMerged, TextFrameIndex const i_nIndex)
+{
+ sal_Int32 nIndex(i_nIndex);
+ sw::Extent const* pExtent(nullptr);
+ for (const auto& rExt : rMerged.extents)
+ {
+ pExtent = &rExt;
+ if (nIndex < (pExtent->nEnd - pExtent->nStart))
+ {
+ return std::make_pair(pExtent->pNode, pExtent->nStart + nIndex);
+ }
+ nIndex = nIndex - (pExtent->nEnd - pExtent->nStart);
+ }
+ assert(nIndex == 0 && "view index out of bounds");
+ return pExtent
+ ? std::make_pair(pExtent->pNode, pExtent->nEnd) //1-past-the-end index
+ : std::make_pair(const_cast<SwTextNode*>(rMerged.pLastNode), rMerged.pLastNode->Len());
+}
+
+TextFrameIndex MapModelToView(MergedPara const& rMerged, SwTextNode const*const pNode, sal_Int32 const nIndex)
+{
+ assert(rMerged.pFirstNode->GetIndex() <= pNode->GetIndex()
+ && pNode->GetIndex() <= rMerged.pLastNode->GetIndex());
+ sal_Int32 nRet(0);
+ bool bFoundNode(false);
+ for (auto const& e : rMerged.extents)
+ {
+ if (pNode->GetIndex() < e.pNode->GetIndex())
+ {
+ return TextFrameIndex(nRet);
+ }
+ if (e.pNode == pNode)
+ {
+ if (e.nStart <= nIndex && nIndex <= e.nEnd)
+ {
+ return TextFrameIndex(nRet + (nIndex - e.nStart));
+ }
+ else if (nIndex < e.nStart)
+ {
+ // in gap before this extent => map to 0 here TODO???
+ return TextFrameIndex(nRet);
+ }
+ bFoundNode = true;
+ }
+ else if (bFoundNode)
+ {
+ break;
+ }
+ nRet += e.nEnd - e.nStart;
+ }
+ if (bFoundNode)
+ {
+ // must be in a gap at the end of the node
+ assert(nIndex <= pNode->Len());
+ return TextFrameIndex(nRet);
+ }
+ else if (rMerged.extents.empty())
+ {
+ assert(nIndex <= pNode->Len());
+ return TextFrameIndex(0);
+ }
+ return TextFrameIndex(rMerged.mergedText.getLength());
+}
+
+} // namespace sw
+
+std::pair<SwTextNode*, sal_Int32>
+SwTextFrame::MapViewToModel(TextFrameIndex const nIndex) const
+{
+//nope assert(GetPara());
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ {
+ return sw::MapViewToModel(*pMerged, nIndex);
+ }
+ else
+ {
+ return std::make_pair(static_cast<SwTextNode*>(const_cast<sw::BroadcastingModify*>(
+ SwFrame::GetDep())), sal_Int32(nIndex));
+ }
+}
+
+SwPosition SwTextFrame::MapViewToModelPos(TextFrameIndex const nIndex) const
+{
+ std::pair<SwTextNode*, sal_Int32> const ret(MapViewToModel(nIndex));
+ return SwPosition(*ret.first, ret.second);
+}
+
+TextFrameIndex SwTextFrame::MapModelToView(SwTextNode const*const pNode, sal_Int32 const nIndex) const
+{
+//nope assert(GetPara());
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ {
+ return sw::MapModelToView(*pMerged, pNode, nIndex);
+ }
+ else
+ {
+ assert(static_cast<SwTextNode*>(const_cast<sw::BroadcastingModify*>(SwFrame::GetDep())) == pNode);
+ return TextFrameIndex(nIndex);
+ }
+}
+
+TextFrameIndex SwTextFrame::MapModelToViewPos(SwPosition const& rPos) const
+{
+ SwTextNode const*const pNode(rPos.nNode.GetNode().GetTextNode());
+ sal_Int32 const nIndex(rPos.nContent.GetIndex());
+ return MapModelToView(pNode, nIndex);
+}
+
+void SwTextFrame::SetMergedPara(std::unique_ptr<sw::MergedPara> p)
+{
+ SwTextNode *const pFirst(m_pMergedPara ? m_pMergedPara->pFirstNode : nullptr);
+ m_pMergedPara = std::move(p);
+ if (pFirst)
+ {
+ if (m_pMergedPara)
+ {
+ assert(pFirst == m_pMergedPara->pFirstNode);
+ }
+ else
+ {
+ pFirst->Add(this); // must register at node again
+ }
+ }
+ // postcondition: frame must be listening somewhere
+ assert(m_pMergedPara || GetDep());
+}
+
+const OUString& SwTextFrame::GetText() const
+{
+//nope assert(GetPara());
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ return pMerged->mergedText;
+ else
+ return static_cast<SwTextNode const*>(SwFrame::GetDep())->GetText();
+}
+
+SwTextNode const* SwTextFrame::GetTextNodeForParaProps() const
+{
+ // FIXME can GetPara be 0 ? yes... this is needed in SwContentNotify::SwContentNotify() which is called before any formatting is started
+//nope assert(GetPara());
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ {
+// assert(pMerged->pFirstNode == pMerged->pParaPropsNode); // surprising news!
+ return pMerged->pParaPropsNode;
+ }
+ else
+ return static_cast<SwTextNode const*>(SwFrame::GetDep());
+}
+
+SwTextNode const* SwTextFrame::GetTextNodeForFirstText() const
+{
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ return pMerged->extents.empty()
+ ? pMerged->pFirstNode
+ : pMerged->extents.front().pNode;
+ else
+ return static_cast<SwTextNode const*>(SwFrame::GetDep());
+}
+
+SwTextNode const* SwTextFrame::GetTextNodeFirst() const
+{
+//nope assert(GetPara());
+ sw::MergedPara const*const pMerged(GetMergedPara());
+ if (pMerged)
+ return pMerged->pFirstNode;
+ else
+ return static_cast<SwTextNode const*>(SwFrame::GetDep());
+}
+
+SwDoc const& SwTextFrame::GetDoc() const
+{
+ return GetTextNodeFirst()->GetDoc();
+}
+
+LanguageType SwTextFrame::GetLangOfChar(TextFrameIndex const nIndex,
+ sal_uInt16 const nScript, bool const bNoChar) const
+{
+ // a single character can be mapped uniquely!
+ std::pair<SwTextNode const*, sal_Int32> const pos(MapViewToModel(nIndex));
+ return pos.first->GetLang(pos.second, bNoChar ? 0 : 1, nScript);
+}
+
+void SwTextFrame::ResetPreps()
+{
+ if ( GetCacheIdx() != USHRT_MAX )
+ {
+ if (SwParaPortion *pPara = GetPara())
+ pPara->ResetPreps();
+ }
+}
+
+bool SwTextFrame::IsHiddenNow() const
+{
+ SwFrameSwapper aSwapper( this, true );
+
+ if( !getFrameArea().Width() && isFrameAreaDefinitionValid() && GetUpper()->isFrameAreaDefinitionValid() ) // invalid when stack overflows (StackHack)!
+ {
+// OSL_FAIL( "SwTextFrame::IsHiddenNow: thin frame" );
+ return true;
+ }
+
+ bool bHiddenCharsHidePara(false);
+ bool bHiddenParaField(false);
+ if (m_pMergedPara)
+ {
+ TextFrameIndex nHiddenStart(COMPLETE_STRING);
+ TextFrameIndex nHiddenEnd(0);
+ if (auto const pScriptInfo = GetScriptInfo())
+ {
+ pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0),
+ nHiddenStart, nHiddenEnd);
+ }
+ else // ParaPortion is created in Format, but this is called earlier
+ {
+ SwScriptInfo aInfo;
+ aInfo.InitScriptInfo(*m_pMergedPara->pFirstNode, m_pMergedPara.get(), IsRightToLeft());
+ aInfo.GetBoundsOfHiddenRange(TextFrameIndex(0),
+ nHiddenStart, nHiddenEnd);
+ }
+ if (TextFrameIndex(0) == nHiddenStart &&
+ TextFrameIndex(GetText().getLength()) <= nHiddenEnd)
+ {
+ bHiddenCharsHidePara = true;
+ }
+ sw::MergedAttrIter iter(*this);
+ SwTextNode const* pNode(nullptr);
+ int nNewResultWeight = 0;
+ for (SwTextAttr const* pHint = iter.NextAttr(&pNode); pHint; pHint = iter.NextAttr(&pNode))
+ {
+ if (pHint->Which() == RES_TXTATR_FIELD)
+ {
+ // see also SwpHints::CalcHiddenParaField()
+ const SwFormatField& rField = pHint->GetFormatField();
+ int nCurWeight = pNode->GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which());
+ if (nCurWeight > nNewResultWeight)
+ {
+ nNewResultWeight = nCurWeight;
+ bHiddenParaField = pNode->GetDoc().FieldHidesPara(*rField.GetField());
+ }
+ else if (nCurWeight == nNewResultWeight && bHiddenParaField)
+ {
+ // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
+ // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
+ // care about fields of higher weight.
+ bHiddenParaField = pNode->GetDoc().FieldHidesPara(*rField.GetField());
+ }
+ }
+ }
+ }
+ else
+ {
+ bHiddenCharsHidePara = static_cast<SwTextNode const*>(SwFrame::GetDep())->HasHiddenCharAttribute( true );
+ bHiddenParaField = static_cast<SwTextNode const*>(SwFrame::GetDep())->IsHiddenByParaField();
+ }
+ const SwViewShell* pVsh = getRootFrame()->GetCurrShell();
+
+ if ( pVsh && ( bHiddenCharsHidePara || bHiddenParaField ) )
+ {
+
+ if (
+ ( bHiddenParaField &&
+ ( !pVsh->GetViewOptions()->IsShowHiddenPara() &&
+ !pVsh->GetViewOptions()->IsFieldName() ) ) ||
+ ( bHiddenCharsHidePara &&
+ !pVsh->GetViewOptions()->IsShowHiddenChar() ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/// Removes Textfrm's attachments, when it's hidden
+void SwTextFrame::HideHidden()
+{
+ OSL_ENSURE( !GetFollow() && IsHiddenNow(),
+ "HideHidden on visible frame of hidden frame has follow" );
+
+ HideFootnotes(GetOffset(), TextFrameIndex(COMPLETE_STRING));
+ HideAndShowObjects();
+
+ // format information is obsolete
+ ClearPara();
+}
+
+void SwTextFrame::HideFootnotes(TextFrameIndex const nStart, TextFrameIndex const nEnd)
+{
+ SwPageFrame *pPage = nullptr;
+ sw::MergedAttrIter iter(*this);
+ SwTextNode const* pNode(nullptr);
+ for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
+ {
+ if (pHt->Which() == RES_TXTATR_FTN)
+ {
+ TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
+ if (nEnd < nIdx)
+ break;
+ if (nStart <= nIdx)
+ {
+ if (!pPage)
+ pPage = FindPageFrame();
+ pPage->RemoveFootnote( this, static_cast<const SwTextFootnote*>(pHt) );
+ }
+ }
+ }
+}
+
+/**
+ * as-character anchored graphics, which are used for a graphic bullet list.
+ * As long as these graphic bullet list aren't imported, do not hide a
+ * at-character anchored object, if
+ * (a) the document is an imported WW8 document -
+ * checked by checking certain compatibility options -
+ * (b) the paragraph is the last content in the document and
+ * (c) the anchor character is an as-character anchored graphic.
+ */
+bool sw_HideObj( const SwTextFrame& _rFrame,
+ const RndStdIds _eAnchorType,
+ SwPosition const& rAnchorPos,
+ SwAnchoredObject* _pAnchoredObj )
+{
+ bool bRet( true );
+
+ if (_eAnchorType == RndStdIds::FLY_AT_CHAR)
+ {
+ const IDocumentSettingAccess *const pIDSA = &_rFrame.GetDoc().getIDocumentSettingAccess();
+ if ( !pIDSA->get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) &&
+ !pIDSA->get(DocumentSettingId::OLD_LINE_SPACING) &&
+ !pIDSA->get(DocumentSettingId::USE_FORMER_OBJECT_POS) &&
+ pIDSA->get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) &&
+ _rFrame.IsInDocBody() && !_rFrame.FindNextCnt() )
+ {
+ SwTextNode const& rNode(*rAnchorPos.nNode.GetNode().GetTextNode());
+ assert(FrameContainsNode(_rFrame, rNode.GetIndex()));
+ sal_Int32 const nObjAnchorPos(rAnchorPos.nContent.GetIndex());
+ const sal_Unicode cAnchorChar = nObjAnchorPos < rNode.Len()
+ ? rNode.GetText()[nObjAnchorPos]
+ : 0;
+ if (cAnchorChar == CH_TXTATR_BREAKWORD)
+ {
+ const SwTextAttr* const pHint(
+ rNode.GetTextAttrForCharAt(nObjAnchorPos, RES_TXTATR_FLYCNT));
+ if ( pHint )
+ {
+ const SwFrameFormat* pFrameFormat =
+ static_cast<const SwTextFlyCnt*>(pHint)->GetFlyCnt().GetFrameFormat();
+ if ( pFrameFormat->Which() == RES_FLYFRMFMT )
+ {
+ SwNodeIndex nContentIndex = *(pFrameFormat->GetContent().GetContentIdx());
+ ++nContentIndex;
+ if ( nContentIndex.GetNode().IsNoTextNode() )
+ {
+ bRet = false;
+ // set needed data structure values for object positioning
+ SwRectFnSet aRectFnSet(&_rFrame);
+ SwRect aLastCharRect( _rFrame.getFrameArea() );
+ aRectFnSet.SetWidth( aLastCharRect, 1 );
+ _pAnchoredObj->maLastCharRect = aLastCharRect;
+ _pAnchoredObj->mnLastTopOfLine = aRectFnSet.GetTop(aLastCharRect);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+/**
+ * Hide/show objects
+ *
+ * Method hides respectively shows objects, which are anchored at paragraph,
+ * at/as a character of the paragraph, corresponding to the paragraph and
+ * paragraph portion visibility.
+ *
+ * - is called from HideHidden() - should hide objects in hidden paragraphs and
+ * - from Format_() - should hide/show objects in partly visible paragraphs
+ */
+void SwTextFrame::HideAndShowObjects()
+{
+ if ( GetDrawObjs() )
+ {
+ if ( IsHiddenNow() )
+ {
+ // complete paragraph is hidden. Thus, hide all objects
+ for (SwAnchoredObject* i : *GetDrawObjs())
+ {
+ SdrObject* pObj = i->DrawObj();
+ SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
+ // under certain conditions
+ const RndStdIds eAnchorType( pContact->GetAnchorId() );
+ if ((eAnchorType != RndStdIds::FLY_AT_CHAR) ||
+ sw_HideObj(*this, eAnchorType, pContact->GetContentAnchor(),
+ i ))
+ {
+ pContact->MoveObjToInvisibleLayer( pObj );
+ }
+ }
+ }
+ else
+ {
+ // paragraph is visible, but can contain hidden text portion.
+ // first we check if objects are allowed to be hidden:
+ const SwViewShell* pVsh = getRootFrame()->GetCurrShell();
+ const bool bShouldBeHidden = !pVsh || !pVsh->GetWin() ||
+ !pVsh->GetViewOptions()->IsShowHiddenChar();
+
+ // Thus, show all objects, which are anchored at paragraph and
+ // hide/show objects, which are anchored at/as character, according
+ // to the visibility of the anchor character.
+ for (SwAnchoredObject* i : *GetDrawObjs())
+ {
+ SdrObject* pObj = i->DrawObj();
+ SwContact* pContact = static_cast<SwContact*>(pObj->GetUserCall());
+ // Determine anchor type only once
+ const RndStdIds eAnchorType( pContact->GetAnchorId() );
+
+ if (eAnchorType == RndStdIds::FLY_AT_PARA)
+ {
+ pContact->MoveObjToVisibleLayer( pObj );
+ }
+ else if ((eAnchorType == RndStdIds::FLY_AT_CHAR) ||
+ (eAnchorType == RndStdIds::FLY_AS_CHAR))
+ {
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ const SwPosition& rAnchor = pContact->GetContentAnchor();
+ SwScriptInfo::GetBoundsOfHiddenRange(
+ *rAnchor.nNode.GetNode().GetTextNode(),
+ rAnchor.nContent.GetIndex(), nHiddenStart, nHiddenEnd);
+ // Under certain conditions
+ if ( nHiddenStart != COMPLETE_STRING && bShouldBeHidden &&
+ sw_HideObj(*this, eAnchorType, rAnchor, i))
+ {
+ pContact->MoveObjToInvisibleLayer( pObj );
+ }
+ else
+ pContact->MoveObjToVisibleLayer( pObj );
+ }
+ else
+ {
+ OSL_FAIL( "<SwTextFrame::HideAndShowObjects()> - object not anchored at/inside paragraph!?" );
+ }
+ }
+ }
+ }
+
+ if (IsFollow())
+ {
+ SwTextFrame *pMaster = FindMaster();
+ OSL_ENSURE(pMaster, "SwTextFrame without master");
+ if (pMaster)
+ pMaster->HideAndShowObjects();
+ }
+}
+
+/**
+ * Returns the first possible break point in the current line.
+ * This method is used in SwTextFrame::Format() to decide whether the previous
+ * line has to be formatted as well.
+ * nFound is <= nEndLine.
+ */
+TextFrameIndex SwTextFrame::FindBrk(const OUString &rText,
+ const TextFrameIndex nStart,
+ const TextFrameIndex nEnd)
+{
+ sal_Int32 nFound = sal_Int32(nStart);
+ const sal_Int32 nEndLine = std::min(sal_Int32(nEnd), rText.getLength() - 1);
+
+ // Skip all leading blanks.
+ while( nFound <= nEndLine && ' ' == rText[nFound] )
+ {
+ nFound++;
+ }
+
+ // A tricky situation with the TextAttr-Dummy-character (in this case "$"):
+ // "Dr.$Meyer" at the beginning of the second line. Typing a blank after that
+ // doesn't result in the word moving into first line, even though that would work.
+ // For this reason we don't skip the dummy char.
+ while( nFound <= nEndLine && ' ' != rText[nFound] )
+ {
+ nFound++;
+ }
+
+ return TextFrameIndex(nFound);
+}
+
+bool SwTextFrame::IsIdxInside(TextFrameIndex const nPos, TextFrameIndex const nLen) const
+{
+// Silence over-eager warning emitted at least by GCC trunk towards 6:
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+#endif
+ if (nLen != TextFrameIndex(COMPLETE_STRING) && GetOffset() > nPos + nLen) // the range preceded us
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic pop
+#endif
+ return false;
+
+ if( !GetFollow() ) // the range doesn't precede us,
+ return true; // nobody follows us.
+
+ TextFrameIndex const nMax = GetFollow()->GetOffset();
+
+ // either the range overlap or our text has been deleted
+ // sw_redlinehide: GetText() should be okay here because it has already
+ // been updated in the INS/DEL hint case
+ if (nMax > nPos || nMax > TextFrameIndex(GetText().getLength()))
+ return true;
+
+ // changes made in the first line of a follow can modify the master
+ const SwParaPortion* pPara = GetFollow()->GetPara();
+ return pPara && ( nPos <= nMax + pPara->GetLen() );
+}
+
+inline void SwTextFrame::InvalidateRange(const SwCharRange &aRange, const tools::Long nD)
+{
+ if ( IsIdxInside( aRange.Start(), aRange.Len() ) )
+ InvalidateRange_( aRange, nD );
+}
+
+void SwTextFrame::InvalidateRange_( const SwCharRange &aRange, const tools::Long nD)
+{
+ if ( !HasPara() )
+ { InvalidateSize();
+ return;
+ }
+
+ SetWidow( false );
+ SwParaPortion *pPara = GetPara();
+
+ bool bInv = false;
+ if( 0 != nD )
+ {
+ // In nDelta the differences between old and new
+ // linelengths are being added, that's why it's negative
+ // if chars have been added and positive, if chars have
+ // deleted
+ pPara->SetDelta(pPara->GetDelta() + nD);
+ bInv = true;
+ }
+ SwCharRange &rReformat = pPara->GetReformat();
+ if(aRange != rReformat) {
+ if (TextFrameIndex(COMPLETE_STRING) == rReformat.Len())
+ rReformat = aRange;
+ else
+ rReformat += aRange;
+ bInv = true;
+ }
+ if(bInv)
+ {
+ InvalidateSize();
+ }
+}
+
+void SwTextFrame::CalcLineSpace()
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
+ "SwTextFrame::CalcLineSpace with swapped frame!" );
+
+ if( IsLocked() || !HasPara() )
+ return;
+
+ if( GetDrawObjs() ||
+ GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace().IsAutoFirst())
+ {
+ Init();
+ return;
+ }
+
+ SwParaPortion *const pPara(GetPara());
+ assert(pPara);
+ if (pPara->IsFixLineHeight())
+ {
+ Init();
+ return;
+ }
+
+ Size aNewSize( getFramePrintArea().SSize() );
+
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
+ SwTextFormatter aLine( this, &aInf );
+ if( aLine.GetDropLines() )
+ {
+ Init();
+ return;
+ }
+
+ aLine.Top();
+ aLine.RecalcRealHeight();
+
+ aNewSize.setHeight( (aLine.Y() - getFrameArea().Top()) + aLine.GetLineHeight() );
+
+ SwTwips nDelta = aNewSize.Height() - getFramePrintArea().Height();
+ // Underflow with free-flying frames
+ if( aInf.GetTextFly().IsOn() )
+ {
+ SwRect aTmpFrame( getFrameArea() );
+ if( nDelta < 0 )
+ aTmpFrame.Height( getFramePrintArea().Height() );
+ else
+ aTmpFrame.Height( aNewSize.Height() );
+ if( aInf.GetTextFly().Relax( aTmpFrame ) )
+ {
+ Init();
+ return;
+ }
+ }
+
+ if( !nDelta )
+ return;
+
+ SwTextFrameBreak aBreak( this );
+ if( GetFollow() || aBreak.IsBreakNow( aLine ) )
+ {
+ // if there is a Follow() or if we need to break here, reformat
+ Init();
+ }
+ else
+ {
+ // everything is business as usual...
+ pPara->SetPrepAdjust();
+ pPara->SetPrep();
+ }
+}
+
+static void lcl_SetWrong( SwTextFrame& rFrame, SwTextNode const& rNode,
+ sal_Int32 const nPos, sal_Int32 const nCnt, bool const bMove)
+{
+ if ( !rFrame.IsFollow() )
+ {
+ SwTextNode* pTextNode = const_cast<SwTextNode*>(&rNode);
+ IGrammarContact* pGrammarContact = getGrammarContact( *pTextNode );
+ SwGrammarMarkUp* pWrongGrammar = pGrammarContact ?
+ pGrammarContact->getGrammarCheck( *pTextNode, false ) :
+ pTextNode->GetGrammarCheck();
+ bool bGrammarProxy = pWrongGrammar != pTextNode->GetGrammarCheck();
+ if( bMove )
+ {
+ if( pTextNode->GetWrong() )
+ pTextNode->GetWrong()->Move( nPos, nCnt );
+ if( pWrongGrammar )
+ pWrongGrammar->MoveGrammar( nPos, nCnt );
+ if( bGrammarProxy && pTextNode->GetGrammarCheck() )
+ pTextNode->GetGrammarCheck()->MoveGrammar( nPos, nCnt );
+ if( pTextNode->GetSmartTags() )
+ pTextNode->GetSmartTags()->Move( nPos, nCnt );
+ }
+ else
+ {
+ if( pTextNode->GetWrong() )
+ pTextNode->GetWrong()->Invalidate( nPos, nCnt );
+ if( pWrongGrammar )
+ pWrongGrammar->Invalidate( nPos, nCnt );
+ if( pTextNode->GetSmartTags() )
+ pTextNode->GetSmartTags()->Invalidate( nPos, nCnt );
+ }
+ const sal_Int32 nEnd = nPos + (nCnt > 0 ? nCnt : 1 );
+ if ( !pTextNode->GetWrong() && !pTextNode->IsWrongDirty() )
+ {
+ pTextNode->SetWrong( std::make_unique<SwWrongList>( WRONGLIST_SPELL ) );
+ pTextNode->GetWrong()->SetInvalid( nPos, nEnd );
+ }
+ if ( !pTextNode->GetSmartTags() && !pTextNode->IsSmartTagDirty() )
+ {
+ pTextNode->SetSmartTags( std::make_unique<SwWrongList>( WRONGLIST_SMARTTAG ) );
+ pTextNode->GetSmartTags()->SetInvalid( nPos, nEnd );
+ }
+ pTextNode->SetWrongDirty(SwTextNode::WrongState::TODO);
+ pTextNode->SetGrammarCheckDirty( true );
+ pTextNode->SetWordCountDirty( true );
+ pTextNode->SetAutoCompleteWordDirty( true );
+ pTextNode->SetSmartTagDirty( true );
+ }
+
+ SwRootFrame *pRootFrame = rFrame.getRootFrame();
+ if (pRootFrame)
+ {
+ pRootFrame->SetNeedGrammarCheck( true );
+ }
+
+ SwPageFrame *pPage = rFrame.FindPageFrame();
+ if( pPage )
+ {
+ pPage->InvalidateSpelling();
+ pPage->InvalidateAutoCompleteWords();
+ pPage->InvalidateWordCount();
+ pPage->InvalidateSmartTags();
+ }
+}
+
+static void lcl_SetScriptInval(SwTextFrame& rFrame, TextFrameIndex const nPos)
+{
+ if( rFrame.GetPara() )
+ rFrame.GetPara()->GetScriptInfo().SetInvalidityA( nPos );
+}
+
+// note: SwClientNotify will be called once for every frame => just fix own Ofst
+static void lcl_ModifyOfst(SwTextFrame & rFrame,
+ TextFrameIndex const nPos, TextFrameIndex const nLen,
+ TextFrameIndex (* op)(TextFrameIndex const&, TextFrameIndex const&))
+{
+ assert(nLen != TextFrameIndex(COMPLETE_STRING));
+ if (rFrame.IsFollow() && nPos < rFrame.GetOffset())
+ {
+ rFrame.ManipOfst( std::max(nPos, op(rFrame.GetOffset(), nLen)) );
+ assert(sal_Int32(rFrame.GetOffset()) <= rFrame.GetText().getLength());
+ }
+}
+
+namespace {
+
+void UpdateMergedParaForMove(sw::MergedPara & rMerged,
+ SwTextFrame & rTextFrame,
+ bool & o_rbRecalcFootnoteFlag,
+ SwTextNode const& rDestNode,
+ SwTextNode const& rNode,
+ sal_Int32 const nDestStart,
+ sal_Int32 const nSourceStart,
+ sal_Int32 const nLen)
+{
+ std::vector<std::pair<sal_Int32, sal_Int32>> deleted;
+ sal_Int32 const nSourceEnd(nSourceStart + nLen);
+ sal_Int32 nLastEnd(0);
+ for (const auto& rExt : rMerged.extents)
+ {
+ if (rExt.pNode == &rNode)
+ {
+ sal_Int32 const nStart(std::max(nLastEnd, nSourceStart));
+ sal_Int32 const nEnd(std::min(rExt.nStart, nSourceEnd));
+ if (nStart < nEnd)
+ {
+ deleted.emplace_back(nStart, nEnd);
+ }
+ nLastEnd = rExt.nEnd;
+ if (nSourceEnd <= rExt.nEnd)
+ {
+ break;
+ }
+ }
+ else if (rNode.GetIndex() < rExt.pNode->GetIndex())
+ {
+ break;
+ }
+ }
+ if (nLastEnd != rNode.Len()) // without nLen, string yet to be removed
+ {
+ if (nLastEnd < nSourceEnd)
+ {
+ deleted.emplace_back(std::max(nLastEnd, nSourceStart), nSourceEnd);
+ }
+ }
+ if (deleted.empty())
+ return;
+
+ o_rbRecalcFootnoteFlag = true;
+ for (auto const& it : deleted)
+ {
+ sal_Int32 const nStart(it.first - nSourceStart + nDestStart);
+ TextFrameIndex const nDeleted = UpdateMergedParaForDelete(rMerged, false,
+ rDestNode, nStart, it.second - it.first);
+//FIXME asserts valid for join - but if called from split, the new node isn't there yet and it will be added later... assert(nDeleted);
+// assert(nDeleted == it.second - it.first);
+ if(nDeleted)
+ {
+ // InvalidateRange/lcl_SetScriptInval was called sufficiently for SwInsText
+ lcl_SetWrong(rTextFrame, rDestNode, nStart, it.first - it.second, false);
+ TextFrameIndex const nIndex(sw::MapModelToView(rMerged, &rDestNode, nStart));
+ lcl_ModifyOfst(rTextFrame, nIndex, nDeleted, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+}
+
+} // namespace
+
+/**
+ * Related: fdo#56031 filter out attribute changes that don't matter for
+ * humans/a11y to stop flooding the destination mortal with useless noise
+ */
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+static bool isA11yRelevantAttribute(sal_uInt16 nWhich)
+{
+ return nWhich != RES_CHRATR_RSID;
+}
+
+static bool hasA11yRelevantAttribute( const std::vector<sal_uInt16>& rWhichFmtAttr )
+{
+ for( sal_uInt16 nWhich : rWhichFmtAttr )
+ if ( isA11yRelevantAttribute( nWhich ) )
+ return true;
+
+ return false;
+}
+#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
+
+// Note: for now this overrides SwClient::SwClientNotify; the intermediary
+// classes still override SwClient::Modify, which should continue to work
+// as their implementation of SwClientNotify is SwClient's which calls Modify.
+// Therefore we also don't need to call SwClient::SwClientNotify(rModify, rHint)
+// because that's all it does, and this implementation calls
+// SwContentFrame::SwClientNotify() when appropriate.
+void SwTextFrame::SwClientNotify(SwModify const& rModify, SfxHint const& rHint)
+{
+ SfxPoolItem const* pOld(nullptr);
+ SfxPoolItem const* pNew(nullptr);
+ sw::MoveText const* pMoveText(nullptr);
+ sw::RedlineDelText const* pRedlineDelText(nullptr);
+ sw::RedlineUnDelText const* pRedlineUnDelText(nullptr);
+
+ sal_uInt16 nWhich = 0;
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ pOld = pHint->m_pOld;
+ pNew = pHint->m_pNew;
+ nWhich = pHint->GetWhich();
+ }
+ else if (auto const pHt = dynamic_cast<sw::MoveText const*>(&rHint))
+ {
+ pMoveText = pHt;
+ }
+ else if (auto const pHynt = dynamic_cast<sw::RedlineDelText const*>(&rHint))
+ {
+ pRedlineDelText = pHynt;
+ }
+ else if (auto const pHnt = dynamic_cast<sw::RedlineUnDelText const*>(&rHint))
+ {
+ pRedlineUnDelText = pHnt;
+ }
+ else
+ {
+ assert(!"unexpected hint");
+ }
+
+ if (m_pMergedPara)
+ {
+ assert(m_pMergedPara->listener.IsListeningTo(&rModify));
+ }
+
+ SwTextNode const& rNode(static_cast<SwTextNode const&>(rModify));
+
+ // modifications concerning frame attributes are processed by the base class
+ if( IsInRange( aFrameFormatSetRange, nWhich ) || RES_FMT_CHG == nWhich )
+ {
+ if (m_pMergedPara)
+ { // ignore item set changes that don't apply
+ SwTextNode const*const pAttrNode(
+ (nWhich == RES_PAGEDESC || nWhich == RES_BREAK)
+ ? m_pMergedPara->pFirstNode
+ : m_pMergedPara->pParaPropsNode);
+ if (pAttrNode != &rModify)
+ {
+ return;
+ }
+ }
+ SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(pOld, pNew));
+ if( nWhich == RES_FMT_CHG && getRootFrame()->GetCurrShell() )
+ {
+ // collection has changed
+ Prepare();
+ InvalidatePrt_();
+ lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
+ SetDerivedR2L( false );
+ CheckDirChange();
+ // Force complete paint due to existing indents.
+ SetCompletePaint();
+ InvalidateLineNum();
+ }
+ return;
+ }
+
+ if (m_pMergedPara && m_pMergedPara->pParaPropsNode != &rModify)
+ {
+ if (isPARATR(nWhich) || isPARATR_LIST(nWhich)) // FRMATR handled above
+ {
+ return; // ignore it
+ }
+ }
+
+ Broadcast(SfxHint()); // notify SwAccessibleParagraph
+
+ // while locked ignore all modifications
+ if( IsLocked() )
+ return;
+
+ // save stack
+ // warning: one has to ensure that all variables are set
+ TextFrameIndex nPos;
+ TextFrameIndex nLen;
+ bool bSetFieldsDirty = false;
+ bool bRecalcFootnoteFlag = false;
+
+ if (pRedlineDelText)
+ {
+ if (m_pMergedPara)
+ {
+ sal_Int32 const nNPos = pRedlineDelText->nStart;
+ sal_Int32 const nNLen = pRedlineDelText->nLen;
+ nPos = MapModelToView(&rNode, nNPos);
+ // update merged before doing anything else
+ nLen = UpdateMergedParaForDelete(*m_pMergedPara, false, rNode, nNPos, nNLen);
+ const sal_Int32 m = -nNLen;
+ if (nLen && IsIdxInside(nPos, nLen))
+ {
+ InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
+ }
+ lcl_SetWrong( *this, rNode, nNPos, m, false );
+ if (nLen)
+ {
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = bRecalcFootnoteFlag = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+ }
+ else if (pRedlineUnDelText)
+ {
+ if (m_pMergedPara)
+ {
+ sal_Int32 const nNPos = pRedlineUnDelText->nStart;
+ sal_Int32 const nNLen = pRedlineUnDelText->nLen;
+ nPos = MapModelToView(&rNode, nNPos);
+ nLen = UpdateMergedParaForInsert(*m_pMergedPara, false, rNode, nNPos, nNLen);
+ if (IsIdxInside(nPos, nLen))
+ {
+ if (!nLen)
+ {
+ // Refresh NumPortions even when line is empty!
+ if (nPos)
+ InvalidateSize();
+ else
+ Prepare();
+ }
+ else
+ InvalidateRange_( SwCharRange( nPos, nLen ), nNLen );
+ }
+ lcl_SetWrong( *this, rNode, nNPos, nNLen, false );
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator+<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+ else if (pMoveText)
+ {
+ if (m_pMergedPara
+ && m_pMergedPara->pFirstNode->GetIndex() <= pMoveText->pDestNode->GetIndex()
+ && pMoveText->pDestNode->GetIndex() <= m_pMergedPara->pLastNode->GetIndex())
+ { // if it's not 2 nodes in merged frame, assume the target node doesn't have frames at all
+ assert(abs(rNode.GetIndex() - pMoveText->pDestNode->GetIndex()) == SwNodeOffset(1));
+ UpdateMergedParaForMove(*m_pMergedPara,
+ *this,
+ bRecalcFootnoteFlag,
+ *pMoveText->pDestNode, rNode,
+ pMoveText->nDestStart,
+ pMoveText->nSourceStart,
+ pMoveText->nLen);
+ }
+ else
+ {
+ // there is a situation where this is okay: from JoinNext, which will then call CheckResetRedlineMergeFlag, which will then create merged from scratch for this frame
+ // assert(!m_pMergedPara || !getRootFrame()->IsHideRedlines() || !pMoveText->pDestNode->getLayoutFrame(getRootFrame()));
+ }
+ }
+ else switch (nWhich)
+ {
+ case RES_LINENUMBER:
+ {
+ assert(false); // should have been forwarded to SwContentFrame
+ InvalidateLineNum();
+ }
+ break;
+ case RES_INS_TXT:
+ {
+ sal_Int32 const nNPos = static_cast<const SwInsText*>(pNew)->nPos;
+ sal_Int32 const nNLen = static_cast<const SwInsText*>(pNew)->nLen;
+ nPos = MapModelToView(&rNode, nNPos);
+ // unlike redlines, inserting into fieldmark must be explicitly handled
+ bool isHidden(false);
+ switch (getRootFrame()->GetFieldmarkMode())
+ {
+ case sw::FieldmarkMode::ShowCommand:
+ isHidden = static_cast<const SwInsText*>(pNew)->isInsideFieldmarkResult;
+ break;
+ case sw::FieldmarkMode::ShowResult:
+ isHidden = static_cast<const SwInsText*>(pNew)->isInsideFieldmarkCommand;
+ break;
+ case sw::FieldmarkMode::ShowBoth: // just to avoid the warning
+ break;
+ }
+ if (!isHidden)
+ {
+ nLen = TextFrameIndex(nNLen);
+ if (m_pMergedPara)
+ {
+ UpdateMergedParaForInsert(*m_pMergedPara, true, rNode, nNPos, nNLen);
+ }
+ if( IsIdxInside( nPos, nLen ) )
+ {
+ if( !nLen )
+ {
+ // Refresh NumPortions even when line is empty!
+ if( nPos )
+ InvalidateSize();
+ else
+ Prepare();
+ }
+ else
+ InvalidateRange_( SwCharRange( nPos, nLen ), nNLen );
+ }
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator+<sal_Int32, Tag_TextFrameIndex>);
+ }
+ lcl_SetWrong( *this, rNode, nNPos, nNLen, true );
+ }
+ break;
+ case RES_DEL_CHR:
+ {
+ sal_Int32 const nNPos = static_cast<const SwDelChr*>(pNew)->nPos;
+ nPos = MapModelToView(&rNode, nNPos);
+ if (m_pMergedPara)
+ {
+ nLen = UpdateMergedParaForDelete(*m_pMergedPara, true, rNode, nNPos, 1);
+ }
+ else
+ {
+ nLen = TextFrameIndex(1);
+ }
+ lcl_SetWrong( *this, rNode, nNPos, -1, true );
+ if (nLen)
+ {
+ InvalidateRange( SwCharRange(nPos, nLen), -1 );
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = bRecalcFootnoteFlag = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+ break;
+ case RES_DEL_TXT:
+ {
+ sal_Int32 const nNPos = static_cast<const SwDelText*>(pNew)->nStart;
+ sal_Int32 const nNLen = static_cast<const SwDelText*>(pNew)->nLen;
+ nPos = MapModelToView(&rNode, nNPos);
+ if (m_pMergedPara)
+ { // update merged before doing anything else
+ nLen = UpdateMergedParaForDelete(*m_pMergedPara, true, rNode, nNPos, nNLen);
+ }
+ else
+ {
+ nLen = TextFrameIndex(nNLen);
+ }
+ const sal_Int32 m = -nNLen;
+ if ((!m_pMergedPara || nLen) && IsIdxInside(nPos, nLen))
+ {
+ if( !nLen )
+ InvalidateSize();
+ else
+ InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), m );
+ }
+ lcl_SetWrong( *this, rNode, nNPos, m, true );
+ if (nLen)
+ {
+ lcl_SetScriptInval( *this, nPos );
+ bSetFieldsDirty = bRecalcFootnoteFlag = true;
+ lcl_ModifyOfst(*this, nPos, nLen, &o3tl::operator-<sal_Int32, Tag_TextFrameIndex>);
+ }
+ }
+ break;
+ case RES_UPDATE_ATTR:
+ {
+ const SwUpdateAttr* pNewUpdate = static_cast<const SwUpdateAttr*>(pNew);
+
+ sal_Int32 const nNPos = pNewUpdate->getStart();
+ sal_Int32 const nNLen = pNewUpdate->getEnd() - nNPos;
+ nPos = MapModelToView(&rNode, nNPos);
+ nLen = MapModelToView(&rNode, nNPos + nNLen) - nPos;
+ if( IsIdxInside( nPos, nLen ) )
+ {
+ // We need to reformat anyways, even if the invalidated
+ // range is empty.
+ // E.g.: empty line, set 14 pt!
+
+ // FootnoteNumbers need to be formatted
+ if( !nLen )
+ nLen = TextFrameIndex(1);
+
+ InvalidateRange_( SwCharRange( nPos, nLen) );
+ const sal_uInt16 nTmp = pNewUpdate->getWhichAttr();
+
+ if( ! nTmp || RES_TXTATR_CHARFMT == nTmp || RES_TXTATR_INETFMT == nTmp || RES_TXTATR_AUTOFMT == nTmp ||
+ RES_FMT_CHG == nTmp || RES_ATTRSET_CHG == nTmp )
+ {
+ lcl_SetWrong( *this, rNode, nNPos, nNPos + nNLen, false );
+ lcl_SetScriptInval( *this, nPos );
+ }
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( isA11yRelevantAttribute( pNewUpdate->getWhichAttr() ) &&
+ hasA11yRelevantAttribute( pNewUpdate->getFmtAttrs() ) )
+ {
+ SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if ( pViewSh )
+ {
+ pViewSh->InvalidateAccessibleParaAttrs( *this );
+ }
+ }
+#endif
+ }
+ break;
+ case RES_OBJECTDYING:
+ break;
+
+ case RES_PARATR_LINESPACING:
+ {
+ CalcLineSpace();
+ InvalidateSize();
+ InvalidatePrt_();
+ if( IsInSct() && !GetPrev() )
+ {
+ SwSectionFrame *pSect = FindSctFrame();
+ if( pSect->ContainsAny() == this )
+ pSect->InvalidatePrt();
+ }
+
+ // i#11859
+ // (1) Also invalidate next frame on next page/column.
+ // (2) Skip empty sections and hidden paragraphs
+ // Thus, use method <InvalidateNextPrtArea()>
+ InvalidateNextPrtArea();
+
+ SetCompletePaint();
+ }
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ {
+ sal_Int32 const nNPos = static_cast<const SwFormatField*>(pNew)->GetTextField()->GetStart();
+ nPos = MapModelToView(&rNode, nNPos);
+ if (IsIdxInside(nPos, TextFrameIndex(1)))
+ {
+ if( pNew == pOld )
+ {
+ // only repaint
+ // opt: invalidate window?
+ InvalidatePage();
+ SetCompletePaint();
+ }
+ else
+ InvalidateRange_(SwCharRange(nPos, TextFrameIndex(1)));
+ }
+ bSetFieldsDirty = true;
+ // ST2
+ if ( SwSmartTagMgr::Get().IsSmartTagsEnabled() )
+ lcl_SetWrong( *this, rNode, nNPos, nNPos + 1, false );
+ }
+ break;
+
+ case RES_TXTATR_FTN :
+ {
+ if (!IsInFootnote())
+ { // the hint may be sent from the anchor node, or from a
+ // node in the footnote; the anchor index is only valid in the
+ // anchor node!
+ assert(&rNode == &static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetTextNode());
+ nPos = MapModelToView(&rNode,
+ static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote()->GetStart());
+ }
+#ifdef _MSC_VER
+ else nPos = TextFrameIndex(42); // shut up MSVC 2017 spurious warning C4701
+#endif
+ if (IsInFootnote() || IsIdxInside(nPos, TextFrameIndex(1)))
+ Prepare( PrepareHint::FootnoteInvalidation, static_cast<const SwFormatFootnote*>(pNew)->GetTextFootnote() );
+ break;
+ }
+
+ case RES_ATTRSET_CHG:
+ {
+ InvalidateLineNum();
+
+ const SwAttrSet& rNewSet = *static_cast<const SwAttrSetChg*>(pNew)->GetChgSet();
+ int nClear = 0;
+ sal_uInt16 nCount = rNewSet.Count();
+
+ if( const SwFormatFootnote* pItem = rNewSet.GetItemIfSet( RES_TXTATR_FTN, false ) )
+ {
+ nPos = MapModelToView(&rNode, pItem->GetTextFootnote()->GetStart());
+ if (IsIdxInside(nPos, TextFrameIndex(1)))
+ Prepare( PrepareHint::FootnoteInvalidation, pNew );
+ nClear = 0x01;
+ --nCount;
+ }
+
+ if( const SwFormatField* pItem = rNewSet.GetItemIfSet( RES_TXTATR_FIELD, false ) )
+ {
+ nPos = MapModelToView(&rNode, pItem->GetTextField()->GetStart());
+ if (IsIdxInside(nPos, TextFrameIndex(1)))
+ {
+ const SfxPoolItem* pOldItem = pOld ?
+ &(static_cast<const SwAttrSetChg*>(pOld)->GetChgSet()->Get(RES_TXTATR_FIELD)) : nullptr;
+ if( pItem == pOldItem )
+ {
+ InvalidatePage();
+ SetCompletePaint();
+ }
+ else
+ InvalidateRange_(SwCharRange(nPos, TextFrameIndex(1)));
+ }
+ nClear |= 0x02;
+ --nCount;
+ }
+ bool bLineSpace = SfxItemState::SET == rNewSet.GetItemState(
+ RES_PARATR_LINESPACING, false ),
+ bRegister = SfxItemState::SET == rNewSet.GetItemState(
+ RES_PARATR_REGISTER, false );
+ if ( bLineSpace || bRegister )
+ {
+ if (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ {
+ Prepare( bRegister ? PrepareHint::Register : PrepareHint::AdjustSizeWithoutFormatting );
+ CalcLineSpace();
+ InvalidateSize();
+ InvalidatePrt_();
+
+ // i#11859
+ // (1) Also invalidate next frame on next page/column.
+ // (2) Skip empty sections and hidden paragraphs
+ // Thus, use method <InvalidateNextPrtArea()>
+ InvalidateNextPrtArea();
+
+ SetCompletePaint();
+ }
+ nClear |= 0x04;
+ if ( bLineSpace )
+ {
+ --nCount;
+ if ((!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ && IsInSct() && !GetPrev())
+ {
+ SwSectionFrame *pSect = FindSctFrame();
+ if( pSect->ContainsAny() == this )
+ pSect->InvalidatePrt();
+ }
+ }
+ if ( bRegister )
+ --nCount;
+ }
+ if ( SfxItemState::SET == rNewSet.GetItemState( RES_PARATR_SPLIT,
+ false ))
+ {
+ if (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ {
+ if (GetPrev())
+ CheckKeep();
+ Prepare();
+ InvalidateSize();
+ }
+ nClear |= 0x08;
+ --nCount;
+ }
+
+ if( SfxItemState::SET == rNewSet.GetItemState( RES_BACKGROUND, false)
+ && (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify)
+ && !IsFollow() && GetDrawObjs() )
+ {
+ SwSortedObjs *pObjs = GetDrawObjs();
+ for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*pObjs)[i];
+ if ( auto pFly = pAnchoredObj->DynCastFlyFrame() )
+ {
+ if( !pFly->IsFlyInContentFrame() )
+ {
+ const SvxBrushItem &rBack =
+ pFly->GetAttrSet()->GetBackground();
+ // #GetTransChg#
+ // following condition determines, if the fly frame
+ // "inherites" the background color of text frame.
+ // This is the case, if fly frame background
+ // color is "no fill"/"auto fill" and if the fly frame
+ // has no background graphic.
+ // Thus, check complete fly frame background
+ // color and *not* only its transparency value
+ if ( (rBack.GetColor() == COL_TRANSPARENT) &&
+ rBack.GetGraphicPos() == GPOS_NONE )
+ {
+ pFly->SetCompletePaint();
+ pFly->InvalidatePage();
+ }
+ }
+ }
+ }
+ }
+
+ if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_TXTATR_CHARFMT, false ) )
+ {
+ lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
+ lcl_SetScriptInval( *this, TextFrameIndex(0) );
+ }
+ else if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_LANGUAGE, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CJK_LANGUAGE, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CTL_LANGUAGE, false ) )
+ lcl_SetWrong( *this, rNode, 0, COMPLETE_STRING, false );
+ else if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_FONT, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CJK_FONT, false ) ||
+ SfxItemState::SET ==
+ rNewSet.GetItemState( RES_CHRATR_CTL_FONT, false ) )
+ lcl_SetScriptInval( *this, TextFrameIndex(0) );
+ else if ( SfxItemState::SET ==
+ rNewSet.GetItemState( RES_FRAMEDIR, false )
+ && (!m_pMergedPara || m_pMergedPara->pParaPropsNode == &rModify))
+ {
+ SetDerivedR2L( false );
+ CheckDirChange();
+ // Force complete paint due to existing indents.
+ SetCompletePaint();
+ }
+
+ if( nCount )
+ {
+ if( getRootFrame()->GetCurrShell() )
+ {
+ Prepare();
+ InvalidatePrt_();
+ }
+
+ if (nClear || (m_pMergedPara &&
+ (m_pMergedPara->pParaPropsNode != &rModify ||
+ m_pMergedPara->pFirstNode != &rModify)))
+ {
+ assert(pOld);
+ SwAttrSetChg aOldSet( *static_cast<const SwAttrSetChg*>(pOld) );
+ SwAttrSetChg aNewSet( *static_cast<const SwAttrSetChg*>(pNew) );
+
+ if (m_pMergedPara && m_pMergedPara->pParaPropsNode != &rModify)
+ {
+ for (sal_uInt16 i = RES_PARATR_BEGIN; i != RES_FRMATR_END; ++i)
+ {
+ if (i != RES_BREAK && i != RES_PAGEDESC)
+ {
+ aOldSet.ClearItem(i);
+ aNewSet.ClearItem(i);
+ }
+ }
+ for (sal_uInt16 i = XATTR_FILL_FIRST; i <= XATTR_FILL_LAST; ++i)
+ {
+ aOldSet.ClearItem(i);
+ aNewSet.ClearItem(i);
+ }
+ }
+ if (m_pMergedPara && m_pMergedPara->pFirstNode != &rModify)
+ {
+ aOldSet.ClearItem(RES_BREAK);
+ aNewSet.ClearItem(RES_BREAK);
+ aOldSet.ClearItem(RES_PAGEDESC);
+ aNewSet.ClearItem(RES_PAGEDESC);
+ }
+
+ if( 0x01 & nClear )
+ {
+ aOldSet.ClearItem( RES_TXTATR_FTN );
+ aNewSet.ClearItem( RES_TXTATR_FTN );
+ }
+ if( 0x02 & nClear )
+ {
+ aOldSet.ClearItem( RES_TXTATR_FIELD );
+ aNewSet.ClearItem( RES_TXTATR_FIELD );
+ }
+ if ( 0x04 & nClear )
+ {
+ if ( bLineSpace )
+ {
+ aOldSet.ClearItem( RES_PARATR_LINESPACING );
+ aNewSet.ClearItem( RES_PARATR_LINESPACING );
+ }
+ if ( bRegister )
+ {
+ aOldSet.ClearItem( RES_PARATR_REGISTER );
+ aNewSet.ClearItem( RES_PARATR_REGISTER );
+ }
+ }
+ if ( 0x08 & nClear )
+ {
+ aOldSet.ClearItem( RES_PARATR_SPLIT );
+ aNewSet.ClearItem( RES_PARATR_SPLIT );
+ }
+ if (aOldSet.Count() || aNewSet.Count())
+ {
+ SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(&aOldSet, &aNewSet));
+ }
+ }
+ else
+ SwContentFrame::SwClientNotify(rModify, sw::LegacyModifyHint(pOld, pNew));
+ }
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (isA11yRelevantAttribute(nWhich))
+ {
+ SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if ( pViewSh )
+ {
+ pViewSh->InvalidateAccessibleParaAttrs( *this );
+ }
+ }
+#endif
+ }
+ break;
+
+ // Process SwDocPosUpdate
+ case RES_DOCPOS_UPDATE:
+ {
+ if( pOld && pNew )
+ {
+ const SwDocPosUpdate *pDocPos = static_cast<const SwDocPosUpdate*>(pOld);
+ if( pDocPos->nDocPos <= getFrameArea().Top() )
+ {
+ const SwFormatField *pField = static_cast<const SwFormatField *>(pNew);
+ TextFrameIndex const nIndex(MapModelToView(&rNode,
+ pField->GetTextField()->GetStart()));
+ InvalidateRange(SwCharRange(nIndex, TextFrameIndex(1)));
+ }
+ }
+ break;
+ }
+ case RES_PARATR_SPLIT:
+ if ( GetPrev() )
+ CheckKeep();
+ Prepare();
+ bSetFieldsDirty = true;
+ break;
+ case RES_FRAMEDIR :
+ assert(false); // should have been forwarded to SwContentFrame
+ SetDerivedR2L( false );
+ CheckDirChange();
+ break;
+ default:
+ {
+ Prepare();
+ InvalidatePrt_();
+ if ( !nWhich )
+ {
+ // is called by e. g. HiddenPara with 0
+ SwFrame *pNxt = FindNext();
+ if ( nullptr != pNxt )
+ pNxt->InvalidatePrt();
+ }
+ }
+ } // switch
+
+ if( bSetFieldsDirty )
+ GetDoc().getIDocumentFieldsAccess().SetFieldsDirty( true, &rNode, SwNodeOffset(1) );
+
+ if ( bRecalcFootnoteFlag )
+ CalcFootnoteFlag();
+}
+
+bool SwTextFrame::GetInfo( SfxPoolItem &rHint ) const
+{
+ if ( RES_VIRTPAGENUM_INFO == rHint.Which() && IsInDocBody() && ! IsFollow() )
+ {
+ SwVirtPageNumInfo &rInfo = static_cast<SwVirtPageNumInfo&>(rHint);
+ const SwPageFrame *pPage = FindPageFrame();
+ if ( pPage )
+ {
+ if ( pPage == rInfo.GetOrigPage() && !GetPrev() )
+ {
+ // this should be the one
+ // (could only differ temporarily; is that disturbing?)
+ rInfo.SetInfo( pPage, this );
+ return false;
+ }
+ if ( pPage->GetPhyPageNum() < rInfo.GetOrigPage()->GetPhyPageNum() &&
+ (!rInfo.GetPage() || pPage->GetPhyPageNum() > rInfo.GetPage()->GetPhyPageNum()))
+ {
+ // this could be the one
+ rInfo.SetInfo( pPage, this );
+ }
+ }
+ }
+ return true;
+}
+
+void SwTextFrame::PrepWidows( const sal_uInt16 nNeed, bool bNotify )
+{
+ OSL_ENSURE(GetFollow() && nNeed, "+SwTextFrame::Prepare: lost all friends");
+
+ SwParaPortion *pPara = GetPara();
+ if ( !pPara )
+ return;
+ pPara->SetPrepWidows();
+
+ sal_uInt16 nHave = nNeed;
+
+ // We yield a few lines and shrink in CalcPreps()
+ SwSwapIfNotSwapped swap( this );
+
+ SwTextSizeInfo aInf( this );
+ SwTextMargin aLine( this, &aInf );
+ aLine.Bottom();
+ TextFrameIndex nTmpLen = aLine.GetCurr()->GetLen();
+ while( nHave && aLine.PrevLine() )
+ {
+ if( nTmpLen )
+ --nHave;
+ nTmpLen = aLine.GetCurr()->GetLen();
+ }
+
+ // If it's certain that we can yield lines, the Master needs
+ // to check the widow rule
+ if( !nHave )
+ {
+ bool bSplit = true;
+ if( !IsFollow() ) // only a master decides about orphans
+ {
+ const WidowsAndOrphans aWidOrp( this );
+ bSplit = ( aLine.GetLineNr() >= aWidOrp.GetOrphansLines() &&
+ aLine.GetLineNr() >= aLine.GetDropLines() );
+ }
+
+ if( bSplit )
+ {
+ GetFollow()->SetOffset( aLine.GetEnd() );
+ aLine.TruncLines( true );
+ if( pPara->IsFollowField() )
+ GetFollow()->SetFieldFollow( true );
+ }
+ }
+ if ( bNotify )
+ {
+ InvalidateSize_();
+ InvalidatePage();
+ }
+}
+
+static bool lcl_ErgoVadis(SwTextFrame* pFrame, TextFrameIndex & rPos, const PrepareHint ePrep)
+{
+ const SwFootnoteInfo &rFootnoteInfo = pFrame->GetDoc().GetFootnoteInfo();
+ if( ePrep == PrepareHint::ErgoSum )
+ {
+ if( rFootnoteInfo.m_aErgoSum.isEmpty() )
+ return false;
+ rPos = pFrame->GetOffset();
+ }
+ else
+ {
+ if( rFootnoteInfo.m_aQuoVadis.isEmpty() )
+ return false;
+ if( pFrame->HasFollow() )
+ rPos = pFrame->GetFollow()->GetOffset();
+ else
+ rPos = TextFrameIndex(pFrame->GetText().getLength());
+ if( rPos )
+ --rPos; // our last character
+ }
+ return true;
+}
+
+// Silence over-eager warning emitted at least by GCC 5.3.1
+#if defined __GNUC__ && !defined __clang__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wstrict-overflow"
+#endif
+bool SwTextFrame::Prepare( const PrepareHint ePrep, const void* pVoid,
+ bool bNotify )
+{
+ bool bParaPossiblyInvalid = false;
+
+ SwFrameSwapper aSwapper( this, false );
+
+ if ( IsEmpty() )
+ {
+ switch ( ePrep )
+ {
+ case PrepareHint::BossChanged:
+ SetInvalidVert( true ); // Test
+ [[fallthrough]];
+ case PrepareHint::WidowsOrphans:
+ case PrepareHint::Widows:
+ case PrepareHint::FootnoteInvalidationGone : return bParaPossiblyInvalid;
+
+ case PrepareHint::FramePositionChanged :
+ {
+ // We also need an InvalidateSize for Areas (with and without columns),
+ // so that we format and bUndersized is set (if needed)
+ if( IsInFly() || IsInSct() )
+ {
+ SwTwips nTmpBottom = GetUpper()->getFrameArea().Top() +
+ GetUpper()->getFramePrintArea().Bottom();
+ if( nTmpBottom < getFrameArea().Bottom() )
+ break;
+ }
+ // Are there any free-flying frames on this page?
+ SwTextFly aTextFly( this );
+ if( aTextFly.IsOn() )
+ {
+ // Does any free-flying frame overlap?
+ if ( aTextFly.Relax() || IsUndersized() )
+ break;
+ }
+ if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
+ break;
+
+ SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
+ if (pGrid && GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
+ break;
+
+ // i#28701 - consider anchored objects
+ if ( GetDrawObjs() )
+ break;
+
+ return bParaPossiblyInvalid;
+ }
+ default:
+ break;
+ }
+ }
+
+ if( !HasPara() && PrepareHint::MustFit != ePrep )
+ {
+ SetInvalidVert( true ); // Test
+ OSL_ENSURE( !IsLocked(), "SwTextFrame::Prepare: three of a perfect pair" );
+ if ( bNotify )
+ InvalidateSize();
+ else
+ InvalidateSize_();
+ return bParaPossiblyInvalid;
+ }
+
+ // Get object from cache while locking
+ SwTextLineAccess aAccess( this );
+ SwParaPortion *pPara = aAccess.GetPara();
+
+ switch( ePrep )
+ {
+ case PrepareHint::FootnoteMove :
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Height(0);
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Height(0);
+ }
+
+ InvalidatePrt_();
+ InvalidateSize_();
+ [[fallthrough]];
+ case PrepareHint::AdjustSizeWithoutFormatting :
+ pPara->SetPrepAdjust();
+ if( IsFootnoteNumFrame() != pPara->IsFootnoteNum() ||
+ IsUndersized() )
+ {
+ InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
+ if( GetOffset() && !IsFollow() )
+ SetOffset_(TextFrameIndex(0));
+ }
+ break;
+ case PrepareHint::MustFit :
+ pPara->SetPrepMustFit(true);
+ [[fallthrough]];
+ case PrepareHint::WidowsOrphans :
+ pPara->SetPrepAdjust();
+ break;
+ case PrepareHint::Widows :
+ // MustFit is stronger than anything else
+ if( pPara->IsPrepMustFit() )
+ return bParaPossiblyInvalid;
+ // see comment in WidowsAndOrphans::FindOrphans and CalcPreps()
+ PrepWidows( *static_cast<const sal_uInt16 *>(pVoid), bNotify );
+ break;
+
+ case PrepareHint::FootnoteInvalidation :
+ {
+ SwTextFootnote const *pFootnote = static_cast<SwTextFootnote const *>(pVoid);
+ if( IsInFootnote() )
+ {
+ // Am I the first TextFrame of a footnote?
+ if( !GetPrev() )
+ // So we're a TextFrame of the footnote, which has
+ // to display the footnote number or the ErgoSum text
+ InvalidateRange(SwCharRange(TextFrameIndex(0), TextFrameIndex(1)), 1);
+
+ if( !GetNext() )
+ {
+ // We're the last Footnote; we need to update the
+ // QuoVadis texts now
+ const SwFootnoteInfo &rFootnoteInfo = GetDoc().GetFootnoteInfo();
+ if( !pPara->UpdateQuoVadis( rFootnoteInfo.m_aQuoVadis ) )
+ {
+ TextFrameIndex nPos = pPara->GetParLen();
+ if( nPos )
+ --nPos;
+ InvalidateRange( SwCharRange(nPos, TextFrameIndex(1)), 1);
+ }
+ }
+ }
+ else
+ {
+ // We are the TextFrame _with_ the footnote
+ TextFrameIndex const nPos = MapModelToView(
+ &pFootnote->GetTextNode(), pFootnote->GetStart());
+ InvalidateRange(SwCharRange(nPos, TextFrameIndex(1)), 1);
+ }
+ break;
+ }
+ case PrepareHint::BossChanged :
+ {
+ // Test
+ {
+ SetInvalidVert( false );
+ bool bOld = IsVertical();
+ SetInvalidVert( true );
+ if( bOld != IsVertical() )
+ InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(COMPLETE_STRING)));
+ }
+
+ if( HasFollow() )
+ {
+ TextFrameIndex nNxtOfst = GetFollow()->GetOffset();
+ if( nNxtOfst )
+ --nNxtOfst;
+ InvalidateRange(SwCharRange( nNxtOfst, TextFrameIndex(1)), 1);
+ }
+ if( IsInFootnote() )
+ {
+ TextFrameIndex nPos;
+ if( lcl_ErgoVadis( this, nPos, PrepareHint::QuoVadis ) )
+ InvalidateRange( SwCharRange( nPos, TextFrameIndex(1)) );
+ if( lcl_ErgoVadis( this, nPos, PrepareHint::ErgoSum ) )
+ InvalidateRange( SwCharRange( nPos, TextFrameIndex(1)) );
+ }
+ // If we have a page number field, we must invalidate those spots
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIter iter(*this);
+ TextFrameIndex const nEnd = GetFollow()
+ ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING);
+ for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
+ {
+ TextFrameIndex const nStart(MapModelToView(pNode, pHt->GetStart()));
+ if (nStart >= GetOffset())
+ {
+ if (nStart >= nEnd)
+ break;
+
+ // If we're flowing back and own a Footnote, the Footnote also flows
+ // with us. So that it doesn't obstruct us, we send ourselves
+ // an ADJUST_FRM.
+ // pVoid != 0 means MoveBwd()
+ const sal_uInt16 nWhich = pHt->Which();
+ if (RES_TXTATR_FIELD == nWhich ||
+ (HasFootnote() && pVoid && RES_TXTATR_FTN == nWhich))
+ InvalidateRange(SwCharRange(nStart, TextFrameIndex(1)), 1);
+ }
+ }
+ // A new boss, a new chance for growing
+ if( IsUndersized() )
+ {
+ InvalidateSize_();
+ InvalidateRange(SwCharRange(GetOffset(), TextFrameIndex(1)), 1);
+ }
+ break;
+ }
+
+ case PrepareHint::FramePositionChanged :
+ {
+ if ( isFramePrintAreaValid() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
+ if (pGrid && GetTextNodeForParaProps()->GetSwAttrSet().GetParaGrid().GetValue())
+ InvalidatePrt();
+ }
+
+ // If we don't overlap with anybody:
+ // did any free-flying frame overlapped _before_ the position change?
+ bool bFormat = pPara->HasFly();
+ if( !bFormat )
+ {
+ if( IsInFly() )
+ {
+ SwTwips nTmpBottom = GetUpper()->getFrameArea().Top() +
+ GetUpper()->getFramePrintArea().Bottom();
+ if( nTmpBottom < getFrameArea().Bottom() )
+ bFormat = true;
+ }
+ if( !bFormat )
+ {
+ if ( GetDrawObjs() )
+ {
+ const size_t nCnt = GetDrawObjs()->size();
+ for ( size_t i = 0; i < nCnt; ++i )
+ {
+ SwAnchoredObject* pAnchoredObj = (*GetDrawObjs())[i];
+ // i#28701 - consider all
+ // to-character anchored objects
+ if ( pAnchoredObj->GetFrameFormat().GetAnchor().GetAnchorId()
+ == RndStdIds::FLY_AT_CHAR )
+ {
+ bFormat = true;
+ break;
+ }
+ }
+ }
+ if( !bFormat )
+ {
+ // Are there any free-flying frames on this page?
+ SwTextFly aTextFly( this );
+ if( aTextFly.IsOn() )
+ {
+ // Does any free-flying frame overlap?
+ const bool bRelaxed = aTextFly.Relax();
+ bFormat = bRelaxed || IsUndersized();
+ if (bRelaxed)
+ {
+ // It's possible that pPara was deleted above; retrieve it again
+ pPara = aAccess.GetPara();
+ }
+ }
+ }
+ }
+ }
+
+ if( bFormat )
+ {
+ if( !IsLocked() )
+ {
+ if( pPara->GetRepaint().HasArea() )
+ SetCompletePaint();
+ Init();
+ pPara = nullptr;
+ InvalidateSize_();
+ }
+ }
+ else
+ {
+ if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
+ bParaPossiblyInvalid = Prepare( PrepareHint::Register, nullptr, bNotify );
+ // The Frames need to be readjusted, which caused by changes
+ // in position
+ else if( HasFootnote() )
+ {
+ bParaPossiblyInvalid = Prepare( PrepareHint::AdjustSizeWithoutFormatting, nullptr, bNotify );
+ InvalidateSize_();
+ }
+ else
+ return bParaPossiblyInvalid; // So that there's no SetPrep()
+
+ if (bParaPossiblyInvalid)
+ {
+ // It's possible that pPara was deleted above; retrieve it again
+ pPara = aAccess.GetPara();
+ }
+
+ }
+ break;
+ }
+ case PrepareHint::Register:
+ if (GetTextNodeForParaProps()->GetSwAttrSet().GetRegister().GetValue())
+ {
+ pPara->SetPrepAdjust();
+ CalcLineSpace();
+
+ // It's possible that pPara was deleted above; retrieve it again
+ bParaPossiblyInvalid = true;
+ pPara = aAccess.GetPara();
+
+ InvalidateSize();
+ InvalidatePrt_();
+ SwFrame* pNxt = GetIndNext();
+ if ( nullptr != pNxt )
+ {
+ pNxt->InvalidatePrt_();
+ if ( pNxt->IsLayoutFrame() )
+ pNxt->InvalidatePage();
+ }
+ SetCompletePaint();
+ }
+ break;
+ case PrepareHint::FootnoteInvalidationGone :
+ {
+ // If a Follow is calling us, because a footnote is being deleted, our last
+ // line has to be formatted, so that the first line of the Follow can flow up.
+ // Which had flowed to the next page to be together with the footnote (this is
+ // especially true for areas with columns)
+ OSL_ENSURE( GetFollow(), "PrepareHint::FootnoteInvalidationGone may only be called by Follow" );
+ TextFrameIndex nPos = GetFollow()->GetOffset();
+ if( IsFollow() && GetOffset() == nPos ) // If we don't have a mass of text, we call our
+ FindMaster()->Prepare( PrepareHint::FootnoteInvalidationGone ); // Master's Prepare
+ if( nPos )
+ --nPos; // The char preceding our Follow
+ InvalidateRange(SwCharRange(nPos, TextFrameIndex(1)));
+ return bParaPossiblyInvalid;
+ }
+ case PrepareHint::ErgoSum:
+ case PrepareHint::QuoVadis:
+ {
+ TextFrameIndex nPos;
+ if( lcl_ErgoVadis( this, nPos, ePrep ) )
+ InvalidateRange(SwCharRange(nPos, TextFrameIndex(1)));
+ }
+ break;
+ case PrepareHint::FlyFrameAttributesChanged:
+ {
+ if( pVoid )
+ {
+ TextFrameIndex const nWhere = CalcFlyPos( static_cast<SwFrameFormat const *>(pVoid) );
+ OSL_ENSURE( TextFrameIndex(COMPLETE_STRING) != nWhere, "Prepare: Why me?" );
+ InvalidateRange(SwCharRange(nWhere, TextFrameIndex(1)));
+ return bParaPossiblyInvalid;
+ }
+ [[fallthrough]]; // else: continue with default case block
+ }
+ case PrepareHint::Clear:
+ default:
+ {
+ if( IsLocked() )
+ {
+ if( PrepareHint::FlyFrameArrive == ePrep || PrepareHint::FlyFrameLeave == ePrep )
+ {
+ TextFrameIndex const nLen = (GetFollow()
+ ? GetFollow()->GetOffset()
+ : TextFrameIndex(COMPLETE_STRING))
+ - GetOffset();
+ InvalidateRange( SwCharRange( GetOffset(), nLen ) );
+ }
+ }
+ else
+ {
+ if( pPara->GetRepaint().HasArea() )
+ SetCompletePaint();
+ Init();
+ pPara = nullptr;
+ if( GetOffset() && !IsFollow() )
+ SetOffset_( TextFrameIndex(0) );
+ if ( bNotify )
+ InvalidateSize();
+ else
+ InvalidateSize_();
+ }
+ return bParaPossiblyInvalid; // no SetPrep() happened
+ }
+ }
+ if( pPara )
+ {
+ pPara->SetPrep();
+ }
+
+ return bParaPossiblyInvalid;
+}
+#if defined __GNUC__ && !defined __clang__
+# pragma GCC diagnostic pop
+#endif
+
+/**
+ * Small Helper class:
+ * Prepares a test format.
+ * The frame is changed in size and position, its SwParaPortion is moved aside
+ * and a new one is created.
+ * To achieve this, run formatting with bTestFormat flag set.
+ * In the destructor the TextFrame is reset to its original state.
+ */
+class SwTestFormat
+{
+ SwTextFrame *pFrame;
+ SwParaPortion *pOldPara;
+ SwRect aOldFrame, aOldPrt;
+public:
+ SwTestFormat( SwTextFrame* pTextFrame, const SwFrame* pPrv, SwTwips nMaxHeight );
+ ~SwTestFormat();
+};
+
+SwTestFormat::SwTestFormat( SwTextFrame* pTextFrame, const SwFrame* pPre, SwTwips nMaxHeight )
+ : pFrame( pTextFrame )
+{
+ aOldFrame = pFrame->getFrameArea();
+ aOldPrt = pFrame->getFramePrintArea();
+
+ SwRectFnSet aRectFnSet(pFrame);
+ SwTwips nLower = aRectFnSet.GetBottomMargin(*pFrame);
+
+ {
+ // indeed, here the GetUpper()->getFramePrintArea() gets copied and manipulated
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
+ aFrm.setSwRect(pFrame->GetUpper()->getFramePrintArea());
+ aFrm += pFrame->GetUpper()->getFrameArea().Pos();
+ aRectFnSet.SetHeight( aFrm, nMaxHeight );
+
+ if( pFrame->GetPrev() )
+ {
+ aRectFnSet.SetPosY(
+ aFrm,
+ aRectFnSet.GetBottom(pFrame->GetPrev()->getFrameArea()) - ( aRectFnSet.IsVert() ? nMaxHeight + 1 : 0 ) );
+ }
+ }
+
+ SwBorderAttrAccess aAccess( SwFrame::GetCache(), pFrame );
+ const SwBorderAttrs &rAttrs = *aAccess.Get();
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFrame);
+ aRectFnSet.SetPosX(aPrt, rAttrs.CalcLeft( pFrame ) );
+ }
+
+ if( pPre )
+ {
+ SwTwips nUpper = pFrame->CalcUpperSpace( &rAttrs, pPre );
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFrame);
+ aRectFnSet.SetPosY(aPrt, nUpper );
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFrame);
+ aRectFnSet.SetHeight( aPrt, std::max( tools::Long(0) , aRectFnSet.GetHeight(pFrame->getFrameArea()) - aRectFnSet.GetTop(aPrt) - nLower ) );
+ aRectFnSet.SetWidth( aPrt, aRectFnSet.GetWidth(pFrame->getFrameArea()) - ( rAttrs.CalcLeft( pFrame ) + rAttrs.CalcRight( pFrame ) ) );
+ }
+
+ pOldPara = pFrame->HasPara() ? pFrame->GetPara() : nullptr;
+ pFrame->SetPara( new SwParaPortion(), false );
+ OSL_ENSURE( ! pFrame->IsSwapped(), "A frame is swapped before Format_" );
+
+ if ( pFrame->IsVertical() )
+ pFrame->SwapWidthAndHeight();
+
+ SwTextFormatInfo aInf( pFrame->getRootFrame()->GetCurrShell()->GetOut(), pFrame, false, true, true );
+ SwTextFormatter aLine( pFrame, &aInf );
+
+ pFrame->Format_( aLine, aInf );
+
+ if ( pFrame->IsVertical() )
+ pFrame->SwapWidthAndHeight();
+
+ OSL_ENSURE( ! pFrame->IsSwapped(), "A frame is swapped after Format_" );
+}
+
+SwTestFormat::~SwTestFormat()
+{
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*pFrame);
+ aFrm.setSwRect(aOldFrame);
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*pFrame);
+ aPrt.setSwRect(aOldPrt);
+ }
+
+ pFrame->SetPara( pOldPara );
+}
+
+bool SwTextFrame::TestFormat( const SwFrame* pPrv, SwTwips &rMaxHeight, bool &bSplit )
+{
+ PROTOCOL_ENTER( this, PROT::TestFormat, DbgAction::NONE, nullptr )
+
+ if( IsLocked() && GetUpper()->getFramePrintArea().Width() <= 0 )
+ return false;
+
+ SwTestFormat aSave( this, pPrv, rMaxHeight );
+
+ return SwTextFrame::WouldFit(rMaxHeight, bSplit, true, false);
+}
+
+/**
+ * We should not and don't need to reformat.
+ * We assume that we already formatted and that the formatting
+ * data is still current.
+ *
+ * We also assume that the frame width of the Master and Follow
+ * are the same. That's why we're not calling FindBreak() for
+ * FindOrphans().
+ * The required height is coming from nMaxHeight.
+ *
+ * @returns true if I can split
+ */
+bool SwTextFrame::WouldFit(SwTwips &rMaxHeight, bool &bSplit, bool bTst, bool bMoveBwd)
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
+ "SwTextFrame::WouldFit with swapped frame" );
+ SwRectFnSet aRectFnSet(this);
+
+ if( IsLocked() )
+ return false;
+
+ // it can happen that the IdleCollector removed the cached information
+ if( !IsEmpty() )
+ GetFormatted();
+
+ // i#27801 - correction: 'short cut' for empty paragraph
+ // can *not* be applied, if test format is in progress. The test format doesn't
+ // adjust the frame and the printing area - see method <SwTextFrame::Format_(..)>,
+ // which is called in <SwTextFrame::TestFormat(..)>
+ if ( IsEmpty() && !bTst )
+ {
+ bSplit = false;
+ SwTwips nHeight = aRectFnSet.IsVert() ? getFramePrintArea().SSize().Width() : getFramePrintArea().SSize().Height();
+ if( rMaxHeight < nHeight )
+ return false;
+ else
+ {
+ rMaxHeight -= nHeight;
+ return true;
+ }
+ }
+
+ // GetPara can still be 0 in edge cases
+ // We return true in order to be reformatted on the new Page
+ OSL_ENSURE( HasPara() || IsHiddenNow(), "WouldFit: GetFormatted() and then !HasPara()" );
+ if( !HasPara() || ( !aRectFnSet.GetHeight(getFrameArea()) && IsHiddenNow() ) )
+ return true;
+
+ // Because the Orphan flag only exists for a short moment, we also check
+ // whether the Framesize is set to very huge by CalcPreps, in order to
+ // force a MoveFwd
+ if (IsWidow() || (aRectFnSet.IsVert()
+ ? (0 == getFrameArea().Left())
+ : (sw::WIDOW_MAGIC - 20000 < getFrameArea().Bottom())))
+ {
+ SetWidow(false);
+ if ( GetFollow() )
+ {
+ // If we've ended up here due to a Widow request by our Follow, we check
+ // whether there's a Follow with a real height at all.
+ // Else (e.g. for newly created SctFrames) we ignore the IsWidow() and
+ // still check if we can find enough room
+ if (((!aRectFnSet.IsVert() && getFrameArea().Bottom() <= sw::WIDOW_MAGIC - 20000) ||
+ ( aRectFnSet.IsVert() && 0 < getFrameArea().Left() ) ) &&
+ ( GetFollow()->IsVertical() ?
+ !GetFollow()->getFrameArea().Width() :
+ !GetFollow()->getFrameArea().Height() ) )
+ {
+ SwTextFrame* pFoll = GetFollow()->GetFollow();
+ while( pFoll &&
+ ( pFoll->IsVertical() ?
+ !pFoll->getFrameArea().Width() :
+ !pFoll->getFrameArea().Height() ) )
+ pFoll = pFoll->GetFollow();
+ if( pFoll )
+ return false;
+ }
+ else
+ return false;
+ }
+ }
+
+ SwSwapIfNotSwapped swap( this );
+
+ SwTextSizeInfo aInf( this );
+ SwTextMargin aLine( this, &aInf );
+
+ WidowsAndOrphans aFrameBreak( this, rMaxHeight, bSplit );
+
+ bool bRet = true;
+
+ aLine.Bottom();
+ // is breaking necessary?
+ bSplit = !aFrameBreak.IsInside( aLine );
+ if ( bSplit )
+ bRet = !aFrameBreak.IsKeepAlways() && aFrameBreak.WouldFit(aLine, rMaxHeight, bTst, bMoveBwd);
+ else
+ {
+ // we need the total height including the current line
+ aLine.Top();
+ do
+ {
+ rMaxHeight -= aLine.GetLineHeight();
+ } while ( aLine.Next() );
+ }
+
+ return bRet;
+}
+
+SwTwips SwTextFrame::GetParHeight() const
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
+ "SwTextFrame::GetParHeight with swapped frame" );
+
+ if( !HasPara() )
+ { // For non-empty paragraphs this is a special case
+ // For UnderSized we can simply just ask 1 Twip more
+ sal_uInt16 nRet = o3tl::narrowing<sal_uInt16>(getFramePrintArea().SSize().Height());
+ if( IsUndersized() )
+ {
+ if( IsEmpty() || GetText().isEmpty() )
+ nRet = o3tl::narrowing<sal_uInt16>(EmptyHeight());
+ else
+ ++nRet;
+ }
+ return nRet;
+ }
+
+ // TODO: Refactor and improve code
+ const SwLineLayout* pLineLayout = GetPara();
+ SwTwips nHeight = pLineLayout ? pLineLayout->GetRealHeight() : 0;
+
+ // Is this paragraph scrolled? Our height until now is at least
+ // one line height too low then
+ if( GetOffset() && !IsFollow() )
+ nHeight *= 2;
+
+ while ( pLineLayout && pLineLayout->GetNext() )
+ {
+ pLineLayout = pLineLayout->GetNext();
+ nHeight = nHeight + pLineLayout->GetRealHeight();
+ }
+
+ return nHeight;
+}
+
+/**
+ * @returns this _always_ in the formatted state!
+ */
+SwTextFrame* SwTextFrame::GetFormatted( bool bForceQuickFormat )
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ SwSwapIfSwapped swap( this );
+
+ // In case the SwLineLayout was cleared out of the s_pTextCache, recreate it
+ // Not for empty paragraphs
+ if( !HasPara() && !(isFrameAreaDefinitionValid() && IsEmpty()) )
+ {
+ // Calc() must be called, because frame position can be wrong
+ const bool bFormat = isFrameAreaSizeValid();
+ Calc(pRenderContext); // calls Format() if invalid
+
+ // If the flags were valid (hence bFormat=true), Calc did nothing,
+ // so Format() must be called manually in order to recreate
+ // the SwLineLayout that has been deleted from the
+ // SwTextFrame::s_pTextCache (hence !HasPara() above).
+ // Optimization with FormatQuick()
+ if( bFormat && !FormatQuick( bForceQuickFormat ) )
+ Format(getRootFrame()->GetCurrShell()->GetOut());
+ }
+
+ return this;
+}
+
+SwTwips SwTextFrame::CalcFitToContent()
+{
+ // i#31490
+ // If we are currently locked, we better return with a
+ // fairly reasonable value:
+ if ( IsLocked() )
+ return getFramePrintArea().Width();
+
+ SwParaPortion* pOldPara = GetPara();
+ SwParaPortion *pDummy = new SwParaPortion();
+ SetPara( pDummy, false );
+ const SwPageFrame* pPage = FindPageFrame();
+
+ const Point aOldFramePos = getFrameArea().Pos();
+ const SwTwips nOldFrameWidth = getFrameArea().Width();
+ const SwTwips nOldPrtWidth = getFramePrintArea().Width();
+ const SwTwips nPageWidth = GetUpper()->IsVertical() ?
+ pPage->getFramePrintArea().Height() :
+ pPage->getFramePrintArea().Width();
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( nPageWidth );
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( nPageWidth );
+ }
+
+ // i#25422 objects anchored as character in RTL
+ if ( IsRightToLeft() )
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Pos().AdjustX(nOldFrameWidth - nPageWidth );
+ }
+
+ TextFrameLockGuard aLock( this );
+
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true, true );
+ aInf.SetIgnoreFly( true );
+ SwTextFormatter aLine( this, &aInf );
+ SwHookOut aHook( aInf );
+
+ // i#54031 - assure minimum of MINLAY twips.
+ const SwTwips nMax = std::max( SwTwips(MINLAY), aLine.CalcFitToContent_() + 1 );
+
+ {
+ SwFrameAreaDefinition::FrameAreaWriteAccess aFrm(*this);
+ aFrm.Width( nOldFrameWidth );
+
+ // i#25422 objects anchored as character in RTL
+ if ( IsRightToLeft() )
+ {
+ aFrm.Pos() = aOldFramePos;
+ }
+ }
+
+ {
+ SwFrameAreaDefinition::FramePrintAreaWriteAccess aPrt(*this);
+ aPrt.Width( nOldPrtWidth );
+ }
+
+ SetPara( pOldPara );
+
+ return nMax;
+}
+
+/**
+ * Simulate format for a list item paragraph, whose list level attributes
+ * are in LABEL_ALIGNMENT mode, in order to determine additional first
+ * line offset for the real text formatting due to the value of label
+ * adjustment attribute of the list level.
+ */
+void SwTextFrame::CalcAdditionalFirstLineOffset()
+{
+ if ( IsLocked() )
+ return;
+
+ // reset additional first line offset
+ mnAdditionalFirstLineOffset = 0;
+
+ const SwTextNode* pTextNode( GetTextNodeForParaProps() );
+ // sw_redlinehide: check that pParaPropsNode is the correct one
+ assert(pTextNode->IsNumbered(getRootFrame()) == pTextNode->IsNumbered(nullptr));
+ if (!(pTextNode->IsNumbered(getRootFrame()) &&
+ pTextNode->IsCountedInList() && pTextNode->GetNumRule()))
+ return;
+
+ int nListLevel = pTextNode->GetActualListLevel();
+
+ if (nListLevel < 0)
+ nListLevel = 0;
+
+ if (nListLevel >= MAXLEVEL)
+ nListLevel = MAXLEVEL - 1;
+
+ const SwNumFormat& rNumFormat =
+ pTextNode->GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
+ if ( rNumFormat.GetPositionAndSpaceMode() != SvxNumberFormat::LABEL_ALIGNMENT )
+ return;
+
+ // keep current paragraph portion and apply dummy paragraph portion
+ SwParaPortion* pOldPara = GetPara();
+ SwParaPortion *pDummy = new SwParaPortion();
+ SetPara( pDummy, false );
+
+ // lock paragraph
+ TextFrameLockGuard aLock( this );
+
+ // simulate text formatting
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, false, true, true );
+ aInf.SetIgnoreFly( true );
+ SwTextFormatter aLine( this, &aInf );
+ SwHookOut aHook( aInf );
+ aLine.CalcFitToContent_();
+
+ // determine additional first line offset
+ const SwLinePortion* pFirstPortion = aLine.GetCurr()->GetFirstPortion();
+ if ( pFirstPortion->InNumberGrp() && !pFirstPortion->IsFootnoteNumPortion() )
+ {
+ SwTwips nNumberPortionWidth( pFirstPortion->Width() );
+
+ const SwLinePortion* pPortion = pFirstPortion->GetNextPortion();
+ while ( pPortion &&
+ pPortion->InNumberGrp() && !pPortion->IsFootnoteNumPortion())
+ {
+ nNumberPortionWidth += pPortion->Width();
+ pPortion = pPortion->GetNextPortion();
+ }
+
+ if ( ( IsRightToLeft() &&
+ rNumFormat.GetNumAdjust() == SvxAdjust::Left ) ||
+ ( !IsRightToLeft() &&
+ rNumFormat.GetNumAdjust() == SvxAdjust::Right ) )
+ {
+ mnAdditionalFirstLineOffset = -nNumberPortionWidth;
+ }
+ else if ( rNumFormat.GetNumAdjust() == SvxAdjust::Center )
+ {
+ mnAdditionalFirstLineOffset = -(nNumberPortionWidth/2);
+ }
+ }
+
+ // restore paragraph portion
+ SetPara( pOldPara );
+}
+
+/**
+ * Determine the height of the last line for the calculation of
+ * the proportional line spacing
+ *
+ * Height of last line will be stored in new member
+ * mnHeightOfLastLine and can be accessed via method
+ * GetHeightOfLastLine()
+ *
+ * @param _bUseFont force the usage of the former algorithm to
+ * determine the height of the last line, which
+ * uses the font
+ */
+void SwTextFrame::CalcHeightOfLastLine( const bool _bUseFont )
+{
+ // i#71281
+ // Invalidate printing area, if height of last line changes
+ const SwTwips nOldHeightOfLastLine( mnHeightOfLastLine );
+
+ // determine output device
+ SwViewShell* pVsh = getRootFrame()->GetCurrShell();
+ OSL_ENSURE( pVsh, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no SwViewShell" );
+
+ // i#78921
+ // There could be no <SwViewShell> instance in the case of loading a binary
+ // StarOffice file format containing an embedded Writer document.
+ if ( !pVsh )
+ {
+ return;
+ }
+ OutputDevice* pOut = pVsh->GetOut();
+ const IDocumentSettingAccess *const pIDSA = &GetDoc().getIDocumentSettingAccess();
+ if ( !pVsh->GetViewOptions()->getBrowseMode() ||
+ pVsh->GetViewOptions()->IsPrtFormat() )
+ {
+ pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
+ }
+ OSL_ENSURE( pOut, "<SwTextFrame::_GetHeightOfLastLineForPropLineSpacing()> - no OutputDevice" );
+
+ if ( !pOut )
+ {
+ return;
+ }
+
+ // determine height of last line
+ if ( _bUseFont || pIDSA->get(DocumentSettingId::OLD_LINE_SPACING ) )
+ {
+ // former determination of last line height for proportional line
+ // spacing - take height of font set at the paragraph
+ // FIXME actually... must the font match across all nodes?
+ SwFont aFont( &GetTextNodeForParaProps()->GetSwAttrSet(), pIDSA );
+
+ // we must ensure that the font is restored correctly on the OutputDevice
+ // otherwise Last!=Owner could occur
+ if ( pLastFont )
+ {
+ SwFntObj *pOldFont = pLastFont;
+ pLastFont = nullptr;
+ aFont.SetFntChg( true );
+ aFont.ChgPhysFnt( pVsh, *pOut );
+ mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut );
+ assert(pLastFont && "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
+ pLastFont->Unlock();
+ pLastFont = pOldFont;
+ pLastFont->SetDevFont( pVsh, *pOut );
+ }
+ else
+ {
+ vcl::Font aOldFont = pOut->GetFont();
+ aFont.SetFntChg( true );
+ aFont.ChgPhysFnt( pVsh, *pOut );
+ mnHeightOfLastLine = aFont.GetHeight( pVsh, *pOut );
+ assert(pLastFont && "coverity[var_deref_model] - pLastFont should be set in SwSubFont::ChgFnt");
+ pLastFont->Unlock();
+ pLastFont = nullptr;
+ pOut->SetFont( aOldFont );
+ }
+ }
+ else
+ {
+ // new determination of last line height - take actually height of last line
+ // i#89000
+ // assure same results, if paragraph is undersized
+ if ( IsUndersized() )
+ {
+ mnHeightOfLastLine = 0;
+ }
+ else
+ {
+ bool bCalcHeightOfLastLine = true;
+ if ( ( !HasPara() && IsEmpty( ) ) || GetText().isEmpty() )
+ {
+ mnHeightOfLastLine = EmptyHeight();
+ bCalcHeightOfLastLine = false;
+ }
+
+ if ( bCalcHeightOfLastLine )
+ {
+ OSL_ENSURE( HasPara(),
+ "<SwTextFrame::CalcHeightOfLastLine()> - missing paragraph portions." );
+ const SwLineLayout* pLineLayout = GetPara();
+ while ( pLineLayout && pLineLayout->GetNext() )
+ {
+ // iteration to last line
+ pLineLayout = pLineLayout->GetNext();
+ }
+ if ( pLineLayout )
+ {
+ SwTwips nAscent, nDescent, nDummy1, nDummy2;
+ // i#47162 - suppress consideration of
+ // fly content portions and the line portion.
+ pLineLayout->MaxAscentDescent( nAscent, nDescent,
+ nDummy1, nDummy2,
+ nullptr, true );
+ // i#71281
+ // Suppress wrong invalidation of printing area, if method is
+ // called recursive.
+ // Thus, member <mnHeightOfLastLine> is only set directly, if
+ // no recursive call is needed.
+ const SwTwips nNewHeightOfLastLine = nAscent + nDescent;
+ // i#47162 - if last line only contains
+ // fly content portions, <mnHeightOfLastLine> is zero.
+ // In this case determine height of last line by the font
+ if ( nNewHeightOfLastLine == 0 )
+ {
+ CalcHeightOfLastLine( true );
+ }
+ else
+ {
+ mnHeightOfLastLine = nNewHeightOfLastLine;
+ }
+ }
+ }
+ }
+ }
+ // i#71281
+ // invalidate printing area, if height of last line changes
+ if ( mnHeightOfLastLine != nOldHeightOfLastLine )
+ {
+ InvalidatePrt();
+ }
+}
+
+/**
+ * Method returns the value of the inter line spacing for a text frame.
+ * Such a value exists for proportional line spacings ("1,5 Lines",
+ * "Double", "Proportional" and for leading line spacing ("Leading").
+ *
+ * @param _bNoPropLineSpacing (default = false) control whether the
+ * value of a proportional line spacing is
+ * returned or not
+ */
+tools::Long SwTextFrame::GetLineSpace( const bool _bNoPropLineSpace ) const
+{
+ tools::Long nRet = 0;
+
+ const SvxLineSpacingItem &rSpace = GetTextNodeForParaProps()->GetSwAttrSet().GetLineSpacing();
+
+ switch( rSpace.GetInterLineSpaceRule() )
+ {
+ case SvxInterLineSpaceRule::Prop:
+ {
+ if ( _bNoPropLineSpace )
+ {
+ break;
+ }
+
+ // i#11860 - adjust spacing implementation for object positioning
+ // - compatibility to MS Word
+ nRet = GetHeightOfLastLine();
+
+ tools::Long nTmp = nRet;
+ nTmp *= rSpace.GetPropLineSpace();
+ nTmp /= 100;
+ nTmp -= nRet;
+ if ( nTmp > 0 )
+ nRet = nTmp;
+ else
+ nRet = 0;
+ }
+ break;
+ case SvxInterLineSpaceRule::Fix:
+ {
+ if ( rSpace.GetInterLineSpace() > 0 )
+ nRet = rSpace.GetInterLineSpace();
+ }
+ break;
+ default:
+ break;
+ }
+ return nRet;
+}
+
+sal_uInt16 SwTextFrame::FirstLineHeight() const
+{
+ if ( !HasPara() )
+ {
+ if( IsEmpty() && isFrameAreaDefinitionValid() )
+ return IsVertical() ? o3tl::narrowing<sal_uInt16>(getFramePrintArea().Width()) : o3tl::narrowing<sal_uInt16>(getFramePrintArea().Height());
+ return USHRT_MAX;
+ }
+ const SwParaPortion *pPara = GetPara();
+ if ( !pPara )
+ return USHRT_MAX;
+
+ // tdf#146500 Lines with only fly overlap cannot be "moved", so the idea
+ // here is to continue until there's some text.
+ // FIXME ideally we want to count a fly to the line in which it is anchored
+ // - it may even be anchored in some other paragraph! SwFlyPortion doesn't
+ // have a pointer sadly so no way to find out.
+ sal_uInt16 nHeight(0);
+ for (SwLineLayout const* pLine = pPara; pLine; pLine = pLine->GetNext())
+ {
+ nHeight += pLine->Height();
+ bool hasNonFly(false);
+ for (SwLinePortion const* pPortion = pLine->GetFirstPortion();
+ pPortion; pPortion = pPortion->GetNextPortion())
+ {
+ switch (pPortion->GetWhichPor())
+ {
+ case PortionType::Fly:
+ case PortionType::Glue:
+ case PortionType::Margin:
+ break;
+ default:
+ {
+ hasNonFly = true;
+ break;
+ }
+ }
+ if (hasNonFly)
+ {
+ break;
+ }
+ }
+ if (hasNonFly)
+ {
+ break;
+ }
+ }
+ return nHeight;
+}
+
+sal_uInt16 SwTextFrame::GetLineCount(TextFrameIndex const nPos)
+{
+ sal_uInt16 nRet = 0;
+ SwTextFrame *pFrame = this;
+ do
+ {
+ pFrame->GetFormatted();
+ if( !pFrame->HasPara() )
+ break;
+ SwTextSizeInfo aInf( pFrame );
+ SwTextMargin aLine( pFrame, &aInf );
+ if (TextFrameIndex(COMPLETE_STRING) == nPos)
+ aLine.Bottom();
+ else
+ aLine.CharToLine( nPos );
+ nRet = nRet + aLine.GetLineNr();
+ pFrame = pFrame->GetFollow();
+ } while ( pFrame && pFrame->GetOffset() <= nPos );
+ return nRet;
+}
+
+void SwTextFrame::ChgThisLines()
+{
+ // not necessary to format here (GetFormatted etc.), because we have to come from there!
+ sal_uInt32 nNew = 0;
+ const SwLineNumberInfo &rInf = GetDoc().GetLineNumberInfo();
+ if ( !GetText().isEmpty() && HasPara() )
+ {
+ SwTextSizeInfo aInf( this );
+ SwTextMargin aLine( this, &aInf );
+ if ( rInf.IsCountBlankLines() )
+ {
+ aLine.Bottom();
+ nNew = aLine.GetLineNr();
+ }
+ else
+ {
+ do
+ {
+ if( aLine.GetCurr()->HasContent() )
+ ++nNew;
+ } while ( aLine.NextLine() );
+ }
+ }
+ else if ( rInf.IsCountBlankLines() )
+ nNew = 1;
+
+ if ( nNew == mnThisLines )
+ return;
+
+ if (!IsInTab() && GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber().IsCount())
+ {
+ mnAllLines -= mnThisLines;
+ mnThisLines = nNew;
+ mnAllLines += mnThisLines;
+ SwFrame *pNxt = GetNextContentFrame();
+ while( pNxt && pNxt->IsInTab() )
+ {
+ pNxt = pNxt->FindTabFrame();
+ if( nullptr != pNxt )
+ pNxt = pNxt->FindNextCnt();
+ }
+ if( pNxt )
+ pNxt->InvalidateLineNum();
+
+ // Extend repaint to the bottom.
+ if ( HasPara() )
+ {
+ SwRepaint& rRepaint = GetPara()->GetRepaint();
+ rRepaint.Bottom( std::max( rRepaint.Bottom(),
+ getFrameArea().Top()+getFramePrintArea().Bottom()));
+ }
+ }
+ else // Paragraphs which are not counted should not manipulate the AllLines.
+ mnThisLines = nNew;
+}
+
+void SwTextFrame::RecalcAllLines()
+{
+ ValidateLineNum();
+
+ if ( IsInTab() )
+ return;
+
+ const sal_uLong nOld = GetAllLines();
+ const SwFormatLineNumber &rLineNum = GetTextNodeForParaProps()->GetSwAttrSet().GetLineNumber();
+ sal_uLong nNewNum;
+ const bool bRestart = GetDoc().GetLineNumberInfo().IsRestartEachPage();
+
+ if ( !IsFollow() && rLineNum.GetStartValue() && rLineNum.IsCount() )
+ nNewNum = rLineNum.GetStartValue() - 1;
+ // If it is a follow or not has not be considered if it is a restart at each page; the
+ // restart should also take effect at follows.
+ else if ( bRestart && FindPageFrame()->FindFirstBodyContent() == this )
+ {
+ nNewNum = 0;
+ }
+ else
+ {
+ SwContentFrame *pPrv = GetPrevContentFrame();
+ while ( pPrv &&
+ (pPrv->IsInTab() || pPrv->IsInDocBody() != IsInDocBody()) )
+ pPrv = pPrv->GetPrevContentFrame();
+
+ // i#78254 Restart line numbering at page change
+ // First body content may be in table!
+ if ( bRestart && pPrv && pPrv->FindPageFrame() != FindPageFrame() )
+ pPrv = nullptr;
+
+ nNewNum = pPrv ? static_cast<SwTextFrame*>(pPrv)->GetAllLines() : 0;
+ }
+ if ( rLineNum.IsCount() )
+ nNewNum += GetThisLines();
+
+ if ( nOld == nNewNum )
+ return;
+
+ mnAllLines = nNewNum;
+ SwContentFrame *pNxt = GetNextContentFrame();
+ while ( pNxt &&
+ (pNxt->IsInTab() || pNxt->IsInDocBody() != IsInDocBody()) )
+ pNxt = pNxt->GetNextContentFrame();
+ if ( pNxt )
+ {
+ if ( pNxt->GetUpper() != GetUpper() )
+ pNxt->InvalidateLineNum();
+ else
+ pNxt->InvalidateLineNum_();
+ }
+}
+
+void SwTextFrame::VisitPortions( SwPortionHandler& rPH ) const
+{
+ const SwParaPortion* pPara = isFrameAreaDefinitionValid() ? GetPara() : nullptr;
+
+ if (pPara)
+ {
+ if ( IsFollow() )
+ rPH.Skip( GetOffset() );
+
+ const SwLineLayout* pLine = pPara;
+ while ( pLine )
+ {
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ while ( pPor )
+ {
+ pPor->HandlePortion( rPH );
+ pPor = pPor->GetNextPortion();
+ }
+
+ rPH.LineBreak(pLine->Width());
+ pLine = pLine->GetNext();
+ }
+ }
+
+ rPH.Finish();
+}
+
+const SwScriptInfo* SwTextFrame::GetScriptInfo() const
+{
+ const SwParaPortion* pPara = GetPara();
+ return pPara ? &pPara->GetScriptInfo() : nullptr;
+}
+
+/**
+ * Helper function for SwTextFrame::CalcBasePosForFly()
+ */
+static SwTwips lcl_CalcFlyBasePos( const SwTextFrame& rFrame, SwRect aFlyRect,
+ SwTextFly const & rTextFly )
+{
+ SwRectFnSet aRectFnSet(&rFrame);
+ SwTwips nRet = rFrame.IsRightToLeft() ?
+ aRectFnSet.GetRight(rFrame.getFrameArea()) :
+ aRectFnSet.GetLeft(rFrame.getFrameArea());
+
+ do
+ {
+ SwRect aRect = rTextFly.GetFrame( aFlyRect );
+ if ( 0 != aRectFnSet.GetWidth(aRect) )
+ {
+ if ( rFrame.IsRightToLeft() )
+ {
+ if ( aRectFnSet.GetRight(aRect) -
+ aRectFnSet.GetRight(aFlyRect) >= 0 )
+ {
+ aRectFnSet.SetRight(
+aFlyRect, aRectFnSet.GetLeft(aRect) );
+ nRet = aRectFnSet.GetLeft(aRect);
+ }
+ else
+ break;
+ }
+ else
+ {
+ if ( aRectFnSet.GetLeft(aFlyRect) -
+ aRectFnSet.GetLeft(aRect) >= 0 )
+ {
+ aRectFnSet.SetLeft(
+aFlyRect, aRectFnSet.GetRight(aRect) + 1 );
+ nRet = aRectFnSet.GetRight(aRect);
+ }
+ else
+ break;
+ }
+ }
+ else
+ break;
+ }
+ while ( aRectFnSet.GetWidth(aFlyRect) > 0 );
+
+ return nRet;
+}
+
+void SwTextFrame::CalcBaseOfstForFly()
+{
+ OSL_ENSURE( !IsVertical() || !IsSwapped(),
+ "SwTextFrame::CalcBasePosForFly with swapped frame!" );
+
+ if (!GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_FLY_OFFSETS))
+ return;
+
+ SwRectFnSet aRectFnSet(this);
+
+ SwRect aFlyRect( getFrameArea().Pos() + getFramePrintArea().Pos(), getFramePrintArea().SSize() );
+
+ // Get first 'real' line and adjust position and height of line rectangle.
+ // Correct behaviour if no 'real' line exists
+ // (empty paragraph with and without a dummy portion)
+ SwTwips nFlyAnchorVertOfstNoWrap = 0;
+ {
+ SwTwips nTop = aRectFnSet.GetTop(aFlyRect);
+ const SwLineLayout* pLay = GetPara();
+ SwTwips nLineHeight = 200;
+ while( pLay && pLay->IsDummy() && pLay->GetNext() )
+ {
+ nTop += pLay->Height();
+ nFlyAnchorVertOfstNoWrap += pLay->Height();
+ pLay = pLay->GetNext();
+ }
+ if ( pLay )
+ {
+ nLineHeight = pLay->Height();
+ }
+ aRectFnSet.SetTopAndHeight( aFlyRect, nTop, nLineHeight );
+ }
+
+ SwTextFly aTextFly( this );
+ aTextFly.SetIgnoreCurrentFrame( true );
+ aTextFly.SetIgnoreContour( true );
+ // ignore objects in page header|footer for
+ // text frames not in page header|footer
+ aTextFly.SetIgnoreObjsInHeaderFooter( true );
+ SwTwips nRet1 = lcl_CalcFlyBasePos( *this, aFlyRect, aTextFly );
+ aTextFly.SetIgnoreCurrentFrame( false );
+ SwTwips nRet2 = lcl_CalcFlyBasePos( *this, aFlyRect, aTextFly );
+
+ // make values relative to frame start position
+ SwTwips nLeft = IsRightToLeft() ?
+ aRectFnSet.GetRight(getFrameArea()) :
+ aRectFnSet.GetLeft(getFrameArea());
+
+ mnFlyAnchorOfst = nRet1 - nLeft;
+ mnFlyAnchorOfstNoWrap = nRet2 - nLeft;
+
+ if (!GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS))
+ return;
+
+ if (mnFlyAnchorOfstNoWrap > 0)
+ mnFlyAnchorVertOfstNoWrap = nFlyAnchorVertOfstNoWrap;
+}
+
+SwTwips SwTextFrame::GetBaseVertOffsetForFly(bool bIgnoreFlysAnchoredAtThisFrame) const
+{
+ return bIgnoreFlysAnchoredAtThisFrame ? 0 : mnFlyAnchorVertOfstNoWrap;
+}
+
+/**
+ * Repaint all text frames of the given text node
+ */
+void SwTextFrame::repaintTextFrames( const SwTextNode& rNode )
+{
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
+ for( const SwTextFrame *pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ SwRect aRec( pFrame->GetPaintArea() );
+ const SwRootFrame *pRootFrame = pFrame->getRootFrame();
+ SwViewShell *pCurShell = pRootFrame ? pRootFrame->GetCurrShell() : nullptr;
+ if( pCurShell )
+ pCurShell->InvalidateWindows( aRec );
+ }
+}
+
+void SwTextFrame::UpdateOutlineContentVisibilityButton(SwWrtShell* pWrtSh) const
+{
+ if (pWrtSh && pWrtSh->GetViewOptions()->IsShowOutlineContentVisibilityButton() &&
+ GetTextNodeFirst()->IsOutline())
+ {
+ SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin();
+ SwFrameControlsManager& rMngr = rEditWin.GetFrameControlsManager();
+ rMngr.SetOutlineContentVisibilityButton(this);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtftn.cxx b/sw/source/core/text/txtftn.cxx
new file mode 100644
index 000000000..64d0f0418
--- /dev/null
+++ b/sw/source/core/text/txtftn.cxx
@@ -0,0 +1,1590 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <ndtxt.hxx>
+#include <SwPortionHandler.hxx>
+#include <txtftn.hxx>
+#include <flyfrm.hxx>
+#include <fmtftn.hxx>
+#include <ftninfo.hxx>
+#include <charfmt.hxx>
+#include <rowfrm.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <tabfrm.hxx>
+#include <sortedobjs.hxx>
+
+#include <swfont.hxx>
+#include "porftn.hxx"
+#include "porfly.hxx"
+#include "porlay.hxx"
+#include <txtfrm.hxx>
+#include "itrform2.hxx"
+#include <ftnfrm.hxx>
+#include <pagedesc.hxx>
+#include "redlnitr.hxx"
+#include <sectfrm.hxx>
+#include <layouter.hxx>
+#include <frmtool.hxx>
+#include <ndindex.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <swmodule.hxx>
+#include <unotextrange.hxx>
+#include <redline.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/CharSet.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+using namespace ::com::sun::star;
+
+bool SwTextFrame::IsFootnoteNumFrame_() const
+{
+ if (IsInTab())
+ return false; // tdf#102073 first frame in cell doesn't have mpPrev set
+ const SwFootnoteFrame* pFootnote = FindFootnoteFrame()->GetMaster();
+ while( pFootnote && !pFootnote->ContainsContent() )
+ pFootnote = pFootnote->GetMaster();
+ return !pFootnote;
+}
+
+/**
+ * Looks for the TextFrame matching the SwTextFootnote within a master-follow chain
+ */
+SwTextFrame *SwTextFrame::FindFootnoteRef( const SwTextFootnote *pFootnote )
+{
+ SwTextFrame *pFrame = this;
+ const bool bFwd = MapModelToView(&pFootnote->GetTextNode(), pFootnote->GetStart()) >= GetOffset();
+ while( pFrame )
+ {
+ if( SwFootnoteBossFrame::FindFootnote( pFrame, pFootnote ) )
+ return pFrame;
+ pFrame = bFwd ? pFrame->GetFollow() :
+ pFrame->IsFollow() ? pFrame->FindMaster() : nullptr;
+ }
+ return pFrame;
+}
+
+void SwTextFrame::SetHasRotatedPortions(bool bHasRotatedPortions)
+{
+ mbHasRotatedPortions = bHasRotatedPortions;
+}
+
+#ifdef DBG_UTIL
+void SwTextFrame::CalcFootnoteFlag(TextFrameIndex nStop) // For testing the SplitFrame
+#else
+void SwTextFrame::CalcFootnoteFlag()
+#endif
+{
+ mbFootnote = false;
+
+#ifdef DBG_UTIL
+ const TextFrameIndex nEnd = nStop != TextFrameIndex(COMPLETE_STRING)
+ ? nStop
+ : GetFollow() ? GetFollow()->GetOffset() : TextFrameIndex(COMPLETE_STRING);
+#else
+ const TextFrameIndex nEnd = GetFollow()
+ ? GetFollow()->GetOffset()
+ : TextFrameIndex(COMPLETE_STRING);
+#endif
+
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIter iter(*this);
+ for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode))
+ {
+ if ( pHt->Which() == RES_TXTATR_FTN )
+ {
+ TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
+ if ( nEnd < nIdx )
+ break;
+ if( GetOffset() <= nIdx )
+ {
+ mbFootnote = true;
+ break;
+ }
+ }
+ }
+}
+
+bool SwTextFrame::CalcPrepFootnoteAdjust()
+{
+ OSL_ENSURE( HasFootnote(), "Who´s calling me?" );
+ SwFootnoteBossFrame *pBoss = FindFootnoteBossFrame( true );
+ const SwFootnoteFrame *pFootnote = pBoss->FindFirstFootnote( this );
+ if (pFootnote && FTNPOS_CHAPTER != GetDoc().GetFootnoteInfo().m_ePos &&
+ ( !pBoss->GetUpper()->IsSctFrame() ||
+ !static_cast<SwSectionFrame*>(pBoss->GetUpper())->IsFootnoteAtEnd() ) )
+ {
+ const SwFootnoteContFrame *pCont = pBoss->FindFootnoteCont();
+ bool bReArrange = true;
+
+ SwRectFnSet aRectFnSet(this);
+ if ( pCont && aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()),
+ aRectFnSet.GetBottom(getFrameArea()) ) > 0 )
+ {
+ pBoss->RearrangeFootnotes( aRectFnSet.GetBottom(getFrameArea()), false,
+ pFootnote->GetAttr() );
+ ValidateBodyFrame();
+ ValidateFrame();
+ pFootnote = pBoss->FindFirstFootnote( this );
+ }
+ else
+ bReArrange = false;
+ if( !pCont || !pFootnote || bReArrange != (pFootnote->FindFootnoteBossFrame() == pBoss) )
+ {
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this );
+ SwTextFormatter aLine( this, &aInf );
+ aLine.TruncLines();
+ SetPara( nullptr ); // May be deleted!
+ ResetPreps();
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Local helper function. Checks if nLower should be taken as the boundary
+ * for the footnote.
+ */
+static SwTwips lcl_GetFootnoteLower( const SwTextFrame* pFrame, SwTwips nLower )
+{
+ // nLower is an absolute value. It denotes the bottom of the line
+ // containing the footnote.
+ SwRectFnSet aRectFnSet(pFrame);
+
+ OSL_ENSURE( !pFrame->IsVertical() || !pFrame->IsSwapped(),
+ "lcl_GetFootnoteLower with swapped frame" );
+
+ SwTwips nAdd;
+ SwTwips nRet = nLower;
+
+ // Check if text is inside a table.
+ if ( pFrame->IsInTab() )
+ {
+ // If pFrame is inside a table, we have to check if
+ // a) The table is not allowed to split or
+ // b) The table row is not allowed to split
+
+ // Inside a table, there are no footnotes,
+ // see SwFrame::FindFootnoteBossFrame. So we don't have to check
+ // the case that pFrame is inside a (footnote collecting) section
+ // within the table.
+ const SwFrame* pRow = pFrame;
+ while( !pRow->IsRowFrame() || !pRow->GetUpper()->IsTabFrame() )
+ pRow = pRow->GetUpper();
+ const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
+
+ OSL_ENSURE( pTabFrame && pRow &&
+ pRow->GetUpper()->IsTabFrame(), "Upper of row should be tab" );
+
+ const bool bDontSplit = !pTabFrame->IsFollow() &&
+ !pTabFrame->IsLayoutSplitAllowed();
+
+ SwTwips nMin = 0;
+ if ( bDontSplit )
+ nMin = aRectFnSet.GetBottom(pTabFrame->getFrameArea());
+ else if ( !static_cast<const SwRowFrame*>(pRow)->IsRowSplitAllowed() )
+ nMin = aRectFnSet.GetBottom(pRow->getFrameArea());
+
+ if ( nMin && aRectFnSet.YDiff( nMin, nLower ) > 0 )
+ nRet = nMin;
+
+ nAdd = aRectFnSet.GetBottomMargin(*pRow->GetUpper());
+ }
+ else
+ nAdd = aRectFnSet.GetBottomMargin(*pFrame);
+
+ if( nAdd > 0 )
+ {
+ if ( aRectFnSet.IsVert() )
+ nRet -= nAdd;
+ else
+ nRet += nAdd;
+ }
+
+ // #i10770#: If there are fly frames anchored at previous paragraphs,
+ // the deadline should consider their lower borders.
+ const SwFrame* pStartFrame = pFrame->GetUpper()->GetLower();
+ OSL_ENSURE( pStartFrame, "Upper has no lower" );
+ SwTwips nFlyLower = aRectFnSet.IsVert() ? LONG_MAX : 0;
+ while ( pStartFrame != pFrame )
+ {
+ OSL_ENSURE( pStartFrame, "Frame chain is broken" );
+ if ( pStartFrame->GetDrawObjs() )
+ {
+ const SwSortedObjs &rObjs = *pStartFrame->GetDrawObjs();
+ for (SwAnchoredObject* pAnchoredObj : rObjs)
+ {
+ SwRect aRect( pAnchoredObj->GetObjRect() );
+
+ auto pFlyFrame = pAnchoredObj->DynCastFlyFrame();
+ if ( !pFlyFrame ||
+ pFlyFrame->isFrameAreaDefinitionValid() )
+ {
+ const SwTwips nBottom = aRectFnSet.GetBottom(aRect);
+ if ( aRectFnSet.YDiff( nBottom, nFlyLower ) > 0 )
+ nFlyLower = nBottom;
+ }
+ }
+ }
+
+ pStartFrame = pStartFrame->GetNext();
+ }
+
+ if ( aRectFnSet.IsVert() )
+ nRet = std::min( nRet, nFlyLower );
+ else
+ nRet = std::max( nRet, nFlyLower );
+
+ return nRet;
+}
+
+SwTwips SwTextFrame::GetFootnoteLine( const SwTextFootnote *pFootnote ) const
+{
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),
+ "SwTextFrame::GetFootnoteLine with swapped frame" );
+
+ SwTextFrame *pThis = const_cast<SwTextFrame*>(this);
+
+ if( !HasPara() )
+ {
+ // #109071# GetFormatted() does not work here, because most probably
+ // the frame is currently locked. We return the previous value.
+ return pThis->mnFootnoteLine > 0 ?
+ pThis->mnFootnoteLine :
+ IsVertical() ? getFrameArea().Left() : getFrameArea().Bottom();
+ }
+
+ SwTwips nRet;
+ {
+ SwSwapIfNotSwapped swap(const_cast<SwTextFrame *>(this));
+
+ SwTextInfo aInf( pThis );
+ SwTextIter aLine( pThis, &aInf );
+ TextFrameIndex const nPos(MapModelToView(
+ &pFootnote->GetTextNode(), pFootnote->GetStart()));
+ aLine.CharToLine( nPos );
+
+ nRet = aLine.Y() + aLine.GetLineHeight();
+ if( IsVertical() )
+ nRet = SwitchHorizontalToVertical( nRet );
+ }
+
+ nRet = lcl_GetFootnoteLower( pThis, nRet );
+
+ pThis->mnFootnoteLine = nRet;
+ return nRet;
+}
+
+/**
+ * Calculates the maximum reachable height for the TextFrame in the Footnote Area.
+ * The cell's bottom margin with the Footnote Reference limit's this height.
+ */
+SwTwips SwTextFrame::GetFootnoteFrameHeight_() const
+{
+ OSL_ENSURE( !IsFollow() && IsInFootnote(), "SwTextFrame::SetFootnoteLine: moon walk" );
+
+ const SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame();
+ const SwTextFrame *pRef = static_cast<const SwTextFrame *>(pFootnoteFrame->GetRef());
+ const SwFootnoteBossFrame *pBoss = FindFootnoteBossFrame();
+ if( pBoss != pRef->FindFootnoteBossFrame( !pFootnoteFrame->GetAttr()->
+ GetFootnote().IsEndNote() ) )
+ return 0;
+
+ SwSwapIfSwapped swap(const_cast<SwTextFrame *>(this));
+
+ SwTwips nHeight = pRef->IsInFootnoteConnect() ?
+ 1 : pRef->GetFootnoteLine( pFootnoteFrame->GetAttr() );
+ if( nHeight )
+ {
+ // As odd as it may seem: the first Footnote on the page may not touch the
+ // Footnote Reference, when entering text in the Footnote Area.
+ const SwFrame *pCont = pFootnoteFrame->GetUpper();
+
+ // Height within the Container which we're allowed to consume anyways
+ SwRectFnSet aRectFnSet(pCont);
+ SwTwips nTmp = aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*pCont),
+ aRectFnSet.GetTop(getFrameArea()) );
+
+#if OSL_DEBUG_LEVEL > 0
+ if( nTmp < 0 )
+ {
+ bool bInvalidPos = false;
+ const SwLayoutFrame* pTmp = GetUpper();
+ while( !bInvalidPos && pTmp )
+ {
+ bInvalidPos = !pTmp->isFrameAreaPositionValid() ||
+ !pTmp->Lower()->isFrameAreaPositionValid();
+ if( pTmp == pCont )
+ break;
+ pTmp = pTmp->GetUpper();
+ }
+ OSL_ENSURE( bInvalidPos, "Hanging below FootnoteCont" );
+ }
+#endif
+
+ if ( aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight) > 0 )
+ {
+ // Growth potential of the container
+ if ( !pRef->IsInFootnoteConnect() )
+ {
+ SwSaveFootnoteHeight aSave( const_cast<SwFootnoteBossFrame*>(pBoss), nHeight );
+ nHeight = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pCont))->Grow( LONG_MAX, true );
+ }
+ else
+ nHeight = const_cast<SwFootnoteContFrame*>(static_cast<const SwFootnoteContFrame*>(pCont))->Grow( LONG_MAX, true );
+
+ nHeight += nTmp;
+ if( nHeight < 0 )
+ nHeight = 0;
+ }
+ else
+ { // The container has to shrink
+ nTmp += aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()), nHeight);
+ if( nTmp > 0 )
+ nHeight = nTmp;
+ else
+ nHeight = 0;
+ }
+ }
+
+ return nHeight;
+}
+
+SwTextFrame *SwTextFrame::FindQuoVadisFrame()
+{
+ // Check whether we're in a FootnoteFrame
+ if( GetIndPrev() || !IsInFootnote() )
+ return nullptr;
+
+ // To the preceding FootnoteFrame
+ SwFootnoteFrame *pFootnoteFrame = FindFootnoteFrame()->GetMaster();
+ if( !pFootnoteFrame )
+ return nullptr;
+
+ // Now the last Content
+ SwContentFrame *pCnt = pFootnoteFrame->ContainsContent();
+ if( !pCnt )
+ return nullptr;
+ SwContentFrame *pLast;
+ do
+ { pLast = pCnt;
+ pCnt = pCnt->GetNextContentFrame();
+ } while( pCnt && pFootnoteFrame->IsAnLower( pCnt ) );
+ return static_cast<SwTextFrame*>(pLast);
+}
+
+void SwTextFrame::RemoveFootnote(TextFrameIndex const nStart, TextFrameIndex const nLen)
+{
+ if ( !IsFootnoteAllowed() )
+ return;
+
+ bool bRollBack = nLen != TextFrameIndex(COMPLETE_STRING);
+ TextFrameIndex nEnd;
+ SwTextFrame* pSource;
+ if( bRollBack )
+ {
+ nEnd = nStart + nLen;
+ pSource = GetFollow();
+ if( !pSource )
+ return;
+ }
+ else
+ {
+ nEnd = TextFrameIndex(COMPLETE_STRING);
+ pSource = this;
+ }
+
+ SwPageFrame* pUpdate = nullptr;
+ bool bRemove = false;
+ SwFootnoteBossFrame *pFootnoteBoss = nullptr;
+ SwFootnoteBossFrame *pEndBoss = nullptr;
+ bool bFootnoteEndDoc = FTNPOS_CHAPTER == GetDoc().GetFootnoteInfo().m_ePos;
+ SwTextNode const* pNode(nullptr);
+ sw::MergedAttrIterReverse iter(*this);
+ for (SwTextAttr const* pHt = iter.PrevAttr(&pNode); pHt; pHt = iter.PrevAttr(&pNode))
+ {
+ if (RES_TXTATR_FTN != pHt->Which())
+ continue;
+
+ TextFrameIndex const nIdx(MapModelToView(pNode, pHt->GetStart()));
+ if (nStart > nIdx)
+ break;
+
+ if (nEnd >= nIdx)
+ {
+ SwTextFootnote const*const pFootnote(static_cast<SwTextFootnote const*>(pHt));
+ const bool bEndn = pFootnote->GetFootnote().IsEndNote();
+
+ if (bEndn)
+ {
+ if (!pEndBoss)
+ pEndBoss = pSource->FindFootnoteBossFrame();
+ }
+ else
+ {
+ if (!pFootnoteBoss)
+ {
+ pFootnoteBoss = pSource->FindFootnoteBossFrame( true );
+ if( pFootnoteBoss->GetUpper()->IsSctFrame() )
+ {
+ SwSectionFrame* pSect = static_cast<SwSectionFrame*>(
+ pFootnoteBoss->GetUpper());
+ if (pSect->IsFootnoteAtEnd())
+ bFootnoteEndDoc = false;
+ }
+ }
+ }
+
+ // We don't delete, but move instead.
+ // Three cases are to be considered:
+ // 1) There's neither Follow nor PrevFollow:
+ // -> RemoveFootnote() (maybe even a OSL_ENSURE(value))
+ //
+ // 2) nStart > GetOffset, I have a Follow
+ // -> Footnote moves into Follow
+ //
+ // 3) nStart < GetOffset, I am a Follow
+ // -> Footnote moves into the PrevFollow
+ //
+ // Both need to be on one Page/in one Column
+ SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote(pSource, pFootnote);
+
+ if (pFootnoteFrame)
+ {
+ const bool bEndDoc = bEndn || bFootnoteEndDoc;
+ if( bRollBack )
+ {
+ while (pFootnoteFrame)
+ {
+ pFootnoteFrame->SetRef( this );
+ pFootnoteFrame = pFootnoteFrame->GetFollow();
+ SetFootnote( true );
+ }
+ }
+ else if (GetFollow())
+ {
+ SwContentFrame *pDest = GetFollow();
+ while (pDest->GetFollow() && static_cast<SwTextFrame*>(pDest->
+ GetFollow())->GetOffset() <= nIdx)
+ pDest = pDest->GetFollow();
+ OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote(
+ pDest,pFootnote),"SwTextFrame::RemoveFootnote: footnote exists");
+
+ // Never deregister; always move
+ if (bEndDoc ||
+ !pFootnoteFrame->FindFootnoteBossFrame()->IsBefore(pDest->FindFootnoteBossFrame(!bEndn))
+ )
+ {
+ SwPageFrame* pTmp = pFootnoteFrame->FindPageFrame();
+ if( pUpdate && pUpdate != pTmp )
+ pUpdate->UpdateFootnoteNum();
+ pUpdate = pTmp;
+ while ( pFootnoteFrame )
+ {
+ pFootnoteFrame->SetRef( pDest );
+ pFootnoteFrame = pFootnoteFrame->GetFollow();
+ }
+ }
+ else
+ {
+ pFootnoteBoss->MoveFootnotes( this, pDest, pFootnote );
+ bRemove = true;
+ }
+ static_cast<SwTextFrame*>(pDest)->SetFootnote( true );
+
+ OSL_ENSURE( SwFootnoteBossFrame::FindFootnote( pDest,
+ pFootnote),"SwTextFrame::RemoveFootnote: footnote ChgRef failed");
+ }
+ else
+ {
+ if (!bEndDoc || ( bEndn && pEndBoss->IsInSct() &&
+ !SwLayouter::Collecting( &GetDoc(),
+ pEndBoss->FindSctFrame(), nullptr ) ))
+ {
+ if( bEndn )
+ pEndBoss->RemoveFootnote( this, pFootnote );
+ else
+ pFootnoteBoss->RemoveFootnote( this, pFootnote );
+ bRemove = bRemove || !bEndDoc;
+ OSL_ENSURE( !SwFootnoteBossFrame::FindFootnote( this, pFootnote ),
+ "SwTextFrame::RemoveFootnote: can't get off that footnote" );
+ }
+ }
+ }
+ }
+ }
+ if (pUpdate)
+ pUpdate->UpdateFootnoteNum();
+
+ // We break the oscillation
+ if (bRemove && !bFootnoteEndDoc && HasPara())
+ {
+ ValidateBodyFrame();
+ ValidateFrame();
+ }
+
+ // We call the RemoveFootnote from within the FindBreak, because the last line is
+ // to be passed to the Follow. The Offset of the Follow is, however, outdated;
+ // it'll be set soon. CalcFntFlag depends on a correctly set Follow Offset.
+ // Therefore we temporarily calculate the Follow Offset here
+ TextFrameIndex nOldOfst(COMPLETE_STRING);
+ if( HasFollow() && nStart > GetOffset() )
+ {
+ nOldOfst = GetFollow()->GetOffset();
+ GetFollow()->ManipOfst(nStart + (bRollBack ? nLen : TextFrameIndex(0)));
+ }
+ pSource->CalcFootnoteFlag();
+ if (nOldOfst < TextFrameIndex(COMPLETE_STRING))
+ GetFollow()->ManipOfst( nOldOfst );
+}
+
+
+/**
+ * We basically only have two possibilities:
+ *
+ * a) The Footnote is already present
+ * => we move it, if another pSrcFrame has been found
+ *
+ * b) The Footnote is not present
+ * => we have it created for us
+ *
+ * Whether the Footnote ends up on our Page/Column, doesn't matter in this
+ * context.
+ *
+ * Optimization for Endnotes.
+ *
+ * Another problem: if the Deadline falls within the Footnote Area, we need
+ * to move the Footnote.
+ *
+ * @returns false on any type of error
+ */
+void SwTextFrame::ConnectFootnote( SwTextFootnote *pFootnote, const SwTwips nDeadLine )
+{
+ OSL_ENSURE( !IsVertical() || !IsSwapped(),
+ "SwTextFrame::ConnectFootnote with swapped frame" );
+
+ mbFootnote = true;
+ mbInFootnoteConnect = true; // Just reset!
+ // See if pFootnote is an endnote on a separate endnote page.
+ const IDocumentSettingAccess& rSettings = GetDoc().getIDocumentSettingAccess();
+ const bool bContinuousEndnotes = rSettings.get(DocumentSettingId::CONTINUOUS_ENDNOTES);
+ const bool bEnd = pFootnote->GetFootnote().IsEndNote();
+
+ // We want to store this value, because it is needed as a fallback
+ // in GetFootnoteLine(), if there is no paragraph information available
+ mnFootnoteLine = nDeadLine;
+
+ // We always need a parent (Page/Column)
+ SwSectionFrame *pSect;
+ SwContentFrame *pContent = this;
+ if( bEnd && IsInSct() )
+ {
+ pSect = FindSctFrame();
+ if( pSect->IsEndnAtEnd() )
+ pContent = pSect->FindLastContent( SwFindMode::EndNote );
+ if( !pContent )
+ pContent = this;
+ }
+
+ SwFootnoteBossFrame *pBoss = pContent->FindFootnoteBossFrame( !bEnd );
+
+ pSect = pBoss->FindSctFrame();
+ bool bDocEnd = bEnd ? !( pSect && pSect->IsEndnAtEnd() ) :
+ ( !( pSect && pSect->IsFootnoteAtEnd() ) &&
+ FTNPOS_CHAPTER == GetDoc().GetFootnoteInfo().m_ePos);
+
+ // Footnote can be registered with the Follow
+ SwContentFrame *pSrcFrame = FindFootnoteRef( pFootnote );
+
+ if( bDocEnd )
+ {
+ if ((pSect || bContinuousEndnotes) && pSrcFrame)
+ {
+ SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
+ if (pFootnoteFrame && (pFootnoteFrame->IsInSct() || bContinuousEndnotes))
+ {
+ // We either have a foot/endnote that goes to the end of the section or are in Word
+ // compatibility mode where endnotes go to the end of the document. Handle both
+ // cases by removing the footnote here, then later appending them to the correct
+ // last page of the document or section.
+ pBoss->RemoveFootnote( pSrcFrame, pFootnote );
+ pSrcFrame = nullptr;
+ }
+ }
+ }
+ else if( bEnd && pSect )
+ {
+ SwFootnoteFrame *pFootnoteFrame = pSrcFrame ? SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote ) : nullptr;
+ if( pFootnoteFrame && !pFootnoteFrame->GetUpper() )
+ pFootnoteFrame = nullptr;
+ SwDoc *const pDoc = &GetDoc();
+ if( SwLayouter::Collecting( pDoc, pSect, pFootnoteFrame ) )
+ {
+ if( !pSrcFrame )
+ {
+ SwFootnoteFrame *pNew = new SwFootnoteFrame(pDoc->GetDfltFrameFormat(),this,this,pFootnote);
+ SwNodeIndex aIdx( *pFootnote->GetStartNode(), 1 );
+ ::InsertCnt_( pNew, pDoc, aIdx.GetIndex() );
+ pDoc->getIDocumentLayoutAccess().GetLayouter()->CollectEndnote( pNew );
+ }
+ else if( pSrcFrame != this )
+ SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
+ mbInFootnoteConnect = false;
+ return;
+ }
+ else if( pSrcFrame )
+ {
+ SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
+ if( !pFootnoteBoss->IsInSct() ||
+ pFootnoteBoss->ImplFindSctFrame()->GetSection()!=pSect->GetSection() )
+ {
+ pBoss->RemoveFootnote( pSrcFrame, pFootnote );
+ pSrcFrame = nullptr;
+ }
+ }
+ }
+
+ if( bDocEnd || bEnd )
+ {
+ if( !pSrcFrame )
+ pBoss->AppendFootnote( this, pFootnote );
+ else if( pSrcFrame != this )
+ SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
+ mbInFootnoteConnect = false;
+ return;
+ }
+
+ SwSaveFootnoteHeight aHeight( pBoss, nDeadLine );
+
+ if( !pSrcFrame ) // No Footnote was found at all
+ pBoss->AppendFootnote( this, pFootnote );
+ else
+ {
+ SwFootnoteFrame *pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pSrcFrame, pFootnote );
+ SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
+
+ bool bBrutal = false;
+
+ if( pFootnoteBoss == pBoss ) // Ref and Footnote are on the same Page/Column
+ {
+ SwFrame *pCont = pFootnoteFrame->GetUpper();
+
+ SwRectFnSet aRectFnSet(pCont);
+ tools::Long nDiff = aRectFnSet.YDiff( aRectFnSet.GetTop(pCont->getFrameArea()),
+ nDeadLine );
+
+ if( nDiff >= 0 )
+ {
+ // If the Footnote has been registered to a Follow, we need to
+ // rewire it now too
+ if ( pSrcFrame != this )
+ SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
+
+ // We have some room left, so the Footnote can grow
+ if ( pFootnoteFrame->GetFollow() && nDiff > 0 )
+ {
+ SwFrameDeleteGuard aDeleteGuard(pCont);
+ SwTwips nHeight = aRectFnSet.GetHeight(pCont->getFrameArea());
+ pBoss->RearrangeFootnotes( nDeadLine, false, pFootnote );
+ ValidateBodyFrame();
+ ValidateFrame();
+ SwViewShell *pSh = getRootFrame()->GetCurrShell();
+ if ( pSh && nHeight == aRectFnSet.GetHeight(pCont->getFrameArea()) )
+ // So that we don't miss anything
+ pSh->InvalidateWindows( pCont->getFrameArea() );
+ }
+ mbInFootnoteConnect = false;
+ return;
+ }
+ else
+ bBrutal = true;
+ }
+ else
+ {
+ // Ref and Footnote are not on one Page; attempt to move is necessary
+ SwFrame* pTmp = this;
+ while( pTmp->GetNext() && pSrcFrame != pTmp )
+ pTmp = pTmp->GetNext();
+ if( pSrcFrame == pTmp )
+ bBrutal = true;
+ else
+ { // If our Parent is in a column Area, but the Page already has a
+ // FootnoteContainer, we can only brute force it
+ if( pSect && pSect->FindFootnoteBossFrame( !bEnd )->FindFootnoteCont() )
+ bBrutal = true;
+
+ else if ( !pFootnoteFrame->GetPrev() ||
+ pFootnoteBoss->IsBefore( pBoss )
+ )
+ {
+ SwFootnoteBossFrame *pSrcBoss = pSrcFrame->FindFootnoteBossFrame( !bEnd );
+ pSrcBoss->MoveFootnotes( pSrcFrame, this, pFootnote );
+ }
+ else
+ SwFootnoteBossFrame::ChangeFootnoteRef( pSrcFrame, pFootnote, this );
+ }
+ }
+
+ // The brute force method: Remove Footnote and append.
+ // We need to call SetFootnoteDeadLine(), as we can more easily adapt the
+ // nMaxFootnoteHeight after RemoveFootnote
+ if( bBrutal )
+ {
+ pBoss->RemoveFootnote( pSrcFrame, pFootnote, false );
+ std::unique_ptr<SwSaveFootnoteHeight> pHeight(bEnd ? nullptr : new SwSaveFootnoteHeight( pBoss, nDeadLine ));
+ pBoss->AppendFootnote( this, pFootnote );
+ }
+ }
+
+ // In column Areas, that not yet reach the Page's border a RearrangeFootnotes is not
+ // useful yet, as the Footnote container has not yet been calculated
+ if( !pSect || !pSect->Growable() )
+ {
+ // Validate environment, to avoid oscillation
+ SwSaveFootnoteHeight aNochmal( pBoss, nDeadLine );
+ ValidateBodyFrame();
+ pBoss->RearrangeFootnotes( nDeadLine, true );
+ ValidateFrame();
+ }
+ else if( pSect->IsFootnoteAtEnd() )
+ {
+ ValidateBodyFrame();
+ ValidateFrame();
+ }
+
+ mbInFootnoteConnect = false;
+}
+
+/**
+ * The portion for the Footnote Reference in the Text
+ */
+SwFootnotePortion *SwTextFormatter::NewFootnotePortion( SwTextFormatInfo &rInf,
+ SwTextAttr *pHint )
+{
+ OSL_ENSURE( ! m_pFrame->IsVertical() || m_pFrame->IsSwapped(),
+ "NewFootnotePortion with unswapped frame" );
+
+ SwTextFootnote *pFootnote = static_cast<SwTextFootnote*>(pHint);
+
+ if( !m_pFrame->IsFootnoteAllowed() )
+ return new SwFootnotePortion("", pFootnote);
+
+ const SwFormatFootnote& rFootnote = pFootnote->GetFootnote();
+ SwDoc *const pDoc = &m_pFrame->GetDoc();
+
+ if( rInf.IsTest() )
+ return new SwFootnotePortion(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()), pFootnote);
+
+ SwSwapIfSwapped swap(m_pFrame);
+
+ sal_uInt16 nReal;
+ {
+ sal_uInt16 nOldReal = m_pCurr->GetRealHeight();
+ sal_uInt16 nOldAscent = m_pCurr->GetAscent();
+ sal_uInt16 nOldHeight = m_pCurr->Height();
+ CalcRealHeight();
+ nReal = m_pCurr->GetRealHeight();
+ if( nReal < nOldReal )
+ nReal = nOldReal;
+ m_pCurr->SetRealHeight( nOldReal );
+ m_pCurr->Height( nOldHeight );
+ m_pCurr->SetAscent( nOldAscent );
+ }
+
+ SwTwips nLower = Y() + nReal;
+
+ const bool bVertical = m_pFrame->IsVertical();
+ if( bVertical )
+ nLower = m_pFrame->SwitchHorizontalToVertical( nLower );
+
+ nLower = lcl_GetFootnoteLower( m_pFrame, nLower );
+
+ // We just refresh.
+ // The Connect does not do anything useful in this case, but will
+ // mostly throw away the Footnote and create it anew.
+ if( !rInf.IsQuick() )
+ m_pFrame->ConnectFootnote( pFootnote, nLower );
+
+ SwTextFrame *pScrFrame = m_pFrame->FindFootnoteRef( pFootnote );
+ SwFootnoteBossFrame *pBoss = m_pFrame->FindFootnoteBossFrame( !rFootnote.IsEndNote() );
+ SwFootnoteFrame *pFootnoteFrame = nullptr;
+ if( pScrFrame )
+ pFootnoteFrame = SwFootnoteBossFrame::FindFootnote( pScrFrame, pFootnote );
+
+ // We see whether our Append has caused some Footnote to
+ // still be on the Page/Column. If not, our line disappears too,
+ // which will lead to the following undesired behaviour:
+ // Footnote1 still fits onto the Page/Column, but Footnote2 doesn't.
+ // The Footnote2 Reference remains on the Page/Column. The Footnote itself
+ // is on the next Page/Column.
+ //
+ // Exception: If the Page/Column cannot accommodate another line,
+ // the Footnote Reference should be moved to the next one.
+ if( !rFootnote.IsEndNote() )
+ {
+ SwSectionFrame *pSct = pBoss->FindSctFrame();
+ bool bAtSctEnd = pSct && pSct->IsFootnoteAtEnd();
+ if( FTNPOS_CHAPTER != pDoc->GetFootnoteInfo().m_ePos || bAtSctEnd )
+ {
+ SwFrame* pFootnoteCont = pBoss->FindFootnoteCont();
+ // If the Parent is within an Area, it can only be a Column of this
+ // Area. If this one is not the first Column, we can avoid it.
+ if( !m_pFrame->IsInTab() && ( GetLineNr() > 1 || m_pFrame->GetPrev() ||
+ ( !bAtSctEnd && m_pFrame->GetIndPrev() ) ||
+ ( pSct && pBoss->GetPrev() ) ) )
+ {
+ if( !pFootnoteCont )
+ {
+ rInf.SetStop( true );
+ return nullptr;
+ }
+ else
+ {
+ // There must not be any Footnote Containers in column Areas and at the same time on the
+ // Page/Page column
+ if( pSct && !bAtSctEnd ) // Is the Container in a (column) Area?
+ {
+ SwFootnoteBossFrame* pTmp = pBoss->FindSctFrame()->FindFootnoteBossFrame( true );
+ SwFootnoteContFrame* pFootnoteC = pTmp->FindFootnoteCont();
+ if( pFootnoteC )
+ {
+ SwFootnoteFrame* pTmpFrame = static_cast<SwFootnoteFrame*>(pFootnoteC->Lower());
+ if( pTmpFrame && *pTmpFrame < pFootnote )
+ {
+ rInf.SetStop( true );
+ return nullptr;
+ }
+ }
+ }
+ // Is this the last Line that fits?
+ SwTwips nTmpBot = Y() + nReal * 2;
+
+ if( bVertical )
+ nTmpBot = m_pFrame->SwitchHorizontalToVertical( nTmpBot );
+
+ SwRectFnSet aRectFnSet(pFootnoteCont);
+
+ const tools::Long nDiff = aRectFnSet.YDiff(
+ aRectFnSet.GetTop(pFootnoteCont->getFrameArea()),
+ nTmpBot );
+
+ if( pScrFrame && nDiff < 0 )
+ {
+ if( pFootnoteFrame )
+ {
+ SwFootnoteBossFrame *pFootnoteBoss = pFootnoteFrame->FindFootnoteBossFrame();
+ if( pFootnoteBoss != pBoss )
+ {
+ // We're in the last Line and the Footnote has moved
+ // to another Page. We also want to be on that Page!
+ rInf.SetStop( true );
+ return nullptr;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // Finally: Create FootnotePortion and exit ...
+ SwFootnotePortion *pRet = new SwFootnotePortion(
+ rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame()),
+ pFootnote, nReal );
+ rInf.SetFootnoteInside( true );
+
+ return pRet;
+}
+
+/**
+ * The portion for the Footnote Numbering in the Footnote Area
+ */
+SwNumberPortion *SwTextFormatter::NewFootnoteNumPortion( SwTextFormatInfo const &rInf ) const
+{
+ OSL_ENSURE( m_pFrame->IsInFootnote() && !m_pFrame->GetIndPrev() && !rInf.IsFootnoteDone(),
+ "This is the wrong place for a ftnnumber" );
+ if( rInf.GetTextStart() != m_nStart ||
+ rInf.GetTextStart() != rInf.GetIdx() )
+ return nullptr;
+
+ const SwFootnoteFrame* pFootnoteFrame = m_pFrame->FindFootnoteFrame();
+ const SwTextFootnote* pFootnote = pFootnoteFrame->GetAttr();
+
+ // Aha! So we're in the Footnote Area!
+ SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
+
+ SwDoc *const pDoc = &m_pFrame->GetDoc();
+ OUString aFootnoteText(rFootnote.GetViewNumStr(*pDoc, m_pFrame->getRootFrame(), true));
+
+ const SwEndNoteInfo* pInfo;
+ if( rFootnote.IsEndNote() )
+ pInfo = &pDoc->GetEndNoteInfo();
+ else
+ pInfo = &pDoc->GetFootnoteInfo();
+
+ const SwAttrSet* pParSet = &rInf.GetCharAttr();
+ const IDocumentSettingAccess* pIDSA = &pDoc->getIDocumentSettingAccess();
+ std::unique_ptr<SwFont> pNumFnt(new SwFont( pParSet, pIDSA ));
+
+ // #i37142#
+ // Underline style of paragraph font should not be considered
+ // Overline style of paragraph font should not be considered
+ // Weight style of paragraph font should not be considered
+ // Posture style of paragraph font should not be considered
+ // See also #i18463# and SwTextFormatter::NewNumberPortion()
+ pNumFnt->SetUnderline( LINESTYLE_NONE );
+ pNumFnt->SetOverline( LINESTYLE_NONE );
+ pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::Latin );
+ pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CJK );
+ pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CTL );
+ pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::Latin );
+ pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK );
+ pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL );
+
+ const auto xAnchor = rFootnote.getAnchor(*pDoc);
+ uno::Reference<beans::XPropertySet> xAnchorProps(xAnchor, uno::UNO_QUERY);
+ if (xAnchorProps.is())
+ {
+ auto aAny = xAnchorProps->getPropertyValue("CharFontCharSet");
+ sal_Int16 eCharSet;
+ if ((aAny >>= eCharSet) && eCharSet == awt::CharSet::SYMBOL)
+ {
+ OUString aFontName;
+ aAny = xAnchorProps->getPropertyValue("CharFontName");
+ if (aAny >>= aFontName)
+ {
+ pNumFnt->SetName(aFontName, SwFontScript::Latin);
+ pNumFnt->SetName(aFontName, SwFontScript::CJK);
+ pNumFnt->SetName(aFontName, SwFontScript::CTL);
+ pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::Latin);
+ pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::CJK);
+ pNumFnt->SetCharSet(RTL_TEXTENCODING_SYMBOL, SwFontScript::CTL);
+ }
+ }
+ }
+
+ const SwAttrSet& rSet = pInfo->GetCharFormat(*pDoc)->GetAttrSet();
+ pNumFnt->SetDiffFnt(&rSet, pIDSA );
+ pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() );
+
+ // tdf#85610 apply redline coloring to the footnote numbering in the footnote area
+ SwUnoInternalPaM aPam(*pDoc);
+ if ( ::sw::XTextRangeToSwPaM(aPam, xAnchor) )
+ {
+ SwRedlineTable::size_type nRedlinePos = 0;
+ const SwRedlineTable& rTable = pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ const SwRangeRedline* pRedline = rTable.FindAtPosition( *aPam.Start(), nRedlinePos );
+ if (pRedline)
+ {
+ SwAttrPool& rPool = pDoc->GetAttrPool();
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool);
+
+ std::size_t aAuthor = (1 < pRedline->GetStackCount())
+ ? pRedline->GetAuthor( 1 )
+ : pRedline->GetAuthor();
+
+ if ( RedlineType::Delete == pRedline->GetType() )
+ SW_MOD()->GetDeletedAuthorAttr(aAuthor, aSet);
+ else
+ SW_MOD()->GetInsertAuthorAttr(aAuthor, aSet);
+
+ if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR))
+ pNumFnt->SetColor(pItem->GetValue());
+ if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE))
+ pNumFnt->SetUnderline(pItem->GetLineStyle());
+ if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT))
+ pNumFnt->SetStrikeout( pItem->GetStrikeout() );
+ }
+ }
+
+ SwFootnoteNumPortion* pNewPor = new SwFootnoteNumPortion( aFootnoteText, std::move(pNumFnt) );
+ pNewPor->SetLeft( !m_pFrame->IsRightToLeft() );
+ return pNewPor;
+}
+
+static OUString lcl_GetPageNumber( const SwPageFrame* pPage )
+{
+ OSL_ENSURE( pPage, "GetPageNumber: Homeless TextFrame" );
+ const sal_uInt16 nVirtNum = pPage->GetVirtPageNum();
+ const SvxNumberType& rNum = pPage->GetPageDesc()->GetNumType();
+ return rNum.GetNumStr( nVirtNum );
+}
+
+SwErgoSumPortion *SwTextFormatter::NewErgoSumPortion( SwTextFormatInfo const &rInf ) const
+{
+ // We cannot assume we're a Follow
+ if( !m_pFrame->IsInFootnote() || m_pFrame->GetPrev() ||
+ rInf.IsErgoDone() || rInf.GetIdx() != m_pFrame->GetOffset() ||
+ m_pFrame->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
+ return nullptr;
+
+ // we are in the footnote container
+ const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
+ SwTextFrame *pQuoFrame = m_pFrame->FindQuoVadisFrame();
+ if( !pQuoFrame )
+ return nullptr;
+ const SwPageFrame* pPage = m_pFrame->FindPageFrame();
+ const SwPageFrame* pQuoPage = pQuoFrame->FindPageFrame();
+ if( pPage == pQuoFrame->FindPageFrame() )
+ return nullptr; // If the QuoVadis is on the same Column/Page
+ const OUString aPage = lcl_GetPageNumber( pPage );
+ SwParaPortion *pPara = pQuoFrame->GetPara();
+ if( pPara )
+ pPara->SetErgoSumNum( aPage );
+ if( rFootnoteInfo.m_aErgoSum.isEmpty() )
+ return nullptr;
+ SwErgoSumPortion *pErgo = new SwErgoSumPortion( rFootnoteInfo.m_aErgoSum,
+ lcl_GetPageNumber( pQuoPage ) );
+ return pErgo;
+}
+
+TextFrameIndex SwTextFormatter::FormatQuoVadis(TextFrameIndex const nOffset)
+{
+ OSL_ENSURE( ! m_pFrame->IsVertical() || ! m_pFrame->IsSwapped(),
+ "SwTextFormatter::FormatQuoVadis with swapped frame" );
+
+ if( !m_pFrame->IsInFootnote() || m_pFrame->ImplFindFootnoteFrame()->GetAttr()->GetFootnote().IsEndNote() )
+ return nOffset;
+
+ const SwFrame* pErgoFrame = m_pFrame->FindFootnoteFrame()->GetFollow();
+ if( !pErgoFrame && m_pFrame->HasFollow() )
+ pErgoFrame = m_pFrame->GetFollow();
+ if( !pErgoFrame )
+ return nOffset;
+
+ if( pErgoFrame == m_pFrame->GetNext() )
+ {
+ SwFrame *pCol = m_pFrame->FindColFrame();
+ while( pCol && !pCol->GetNext() )
+ pCol = pCol->GetUpper()->FindColFrame();
+ if( pCol )
+ return nOffset;
+ }
+ else
+ {
+ const SwPageFrame* pPage = m_pFrame->FindPageFrame();
+ const SwPageFrame* pErgoPage = pErgoFrame->FindPageFrame();
+ if( pPage == pErgoPage )
+ return nOffset; // If the ErgoSum is on the same Page
+ }
+
+ SwTextFormatInfo &rInf = GetInfo();
+ const SwFootnoteInfo &rFootnoteInfo = m_pFrame->GetDoc().GetFootnoteInfo();
+ if( rFootnoteInfo.m_aQuoVadis.isEmpty() )
+ return nOffset;
+
+ // A remark on QuoVadis/ErgoSum:
+ // We use the Font set for the Paragraph for these texts.
+ // Thus, we initialize:
+ // TODO: ResetFont();
+ FeedInf( rInf );
+ SeekStartAndChg( rInf, true );
+ if( GetRedln() && m_pCurr->HasRedline() )
+ {
+ std::pair<SwTextNode const*, sal_Int32> const pos(
+ GetTextFrame()->MapViewToModel(nOffset));
+ GetRedln()->Seek(*m_pFont, pos.first->GetIndex(), pos.second, 0);
+ }
+
+ // A tricky special case: Flyfrms extend into the Line and are at the
+ // position we want to insert the Quovadis text
+ // Let's see if it is that bad indeed:
+ SwLinePortion *pPor = m_pCurr->GetFirstPortion();
+ sal_uInt16 nLastLeft = 0;
+ while( pPor )
+ {
+ if ( pPor->IsFlyPortion() )
+ nLastLeft = static_cast<SwFlyPortion*>(pPor)->GetFix() +
+ static_cast<SwFlyPortion*>(pPor)->Width();
+ pPor = pPor->GetNextPortion();
+ }
+
+ // The old game all over again: we want the Line to wrap around
+ // at a certain point, so we adjust the width.
+ // nLastLeft is now basically the right margin
+ const sal_uInt16 nOldRealWidth = rInf.RealWidth();
+ rInf.RealWidth( nOldRealWidth - nLastLeft );
+
+ OUString aErgo = lcl_GetPageNumber( pErgoFrame->FindPageFrame() );
+ SwQuoVadisPortion *pQuo = new SwQuoVadisPortion(rFootnoteInfo.m_aQuoVadis, aErgo );
+ pQuo->SetAscent( rInf.GetAscent() );
+ pQuo->Height( rInf.GetTextHeight() );
+ pQuo->Format( rInf );
+ sal_uInt16 nQuoWidth = pQuo->Width();
+ SwLinePortion* pCurrPor = pQuo;
+
+ while ( rInf.GetRest() )
+ {
+ SwLinePortion* pFollow = rInf.GetRest();
+ rInf.SetRest( nullptr );
+ pCurrPor->Move( rInf );
+
+ OSL_ENSURE( pFollow->IsQuoVadisPortion(),
+ "Quo Vadis, rest of QuoVadisPortion" );
+
+ // format the rest and append it to the other QuoVadis parts
+ pFollow->Format( rInf );
+ nQuoWidth = nQuoWidth + pFollow->Width();
+
+ pCurrPor->Append( pFollow );
+ pCurrPor = pFollow;
+ }
+
+ Right( Right() - nQuoWidth );
+
+ TextFrameIndex nRet;
+ {
+ SwSwapIfNotSwapped swap(m_pFrame);
+
+ nRet = FormatLine( m_nStart );
+ }
+
+ Right( rInf.Left() + nOldRealWidth - 1 );
+
+ nLastLeft = nOldRealWidth - m_pCurr->Width();
+ FeedInf( rInf );
+
+ // It's possible that there's a Margin Portion at the end, which would
+ // just cause a lot of trouble, when respanning
+ pPor = m_pCurr->FindLastPortion();
+ SwGluePortion *pGlue = pPor->IsMarginPortion() ? static_cast<SwMarginPortion*>(pPor) : nullptr;
+ if( pGlue )
+ {
+ pGlue->Height( 0 );
+ pGlue->Width( 0 );
+ pGlue->SetLen(TextFrameIndex(0));
+ pGlue->SetAscent( 0 );
+ pGlue->SetNextPortion( nullptr );
+ pGlue->SetFixWidth(0);
+ }
+
+ // Luxury: We make sure the QuoVadis text appears on the right, by
+ // using Glues.
+ nLastLeft = nLastLeft - nQuoWidth;
+ if( nLastLeft )
+ {
+ if( nLastLeft > pQuo->GetAscent() ) // Minimum distance
+ {
+ switch( GetAdjust() )
+ {
+ case SvxAdjust::Block:
+ {
+ if( !m_pCurr->GetLen() ||
+ CH_BREAK != GetInfo().GetChar(m_nStart + m_pCurr->GetLen() - TextFrameIndex(1)))
+ nLastLeft = pQuo->GetAscent();
+ nQuoWidth = nQuoWidth + nLastLeft;
+ break;
+ }
+ case SvxAdjust::Right:
+ {
+ nLastLeft = pQuo->GetAscent();
+ nQuoWidth = nQuoWidth + nLastLeft;
+ break;
+ }
+ case SvxAdjust::Center:
+ {
+ nQuoWidth = nQuoWidth + pQuo->GetAscent();
+ tools::Long nDiff = nLastLeft - nQuoWidth;
+ if( nDiff < 0 )
+ {
+ nLastLeft = pQuo->GetAscent();
+ nQuoWidth = o3tl::narrowing<sal_uInt16>(-nDiff + nLastLeft);
+ }
+ else
+ {
+ nQuoWidth = 0;
+ nLastLeft = sal_uInt16(( pQuo->GetAscent() + nDiff ) / 2);
+ }
+ break;
+ }
+ default:
+ nQuoWidth = nQuoWidth + nLastLeft;
+ }
+ }
+ else
+ nQuoWidth = nQuoWidth + nLastLeft;
+ if( nLastLeft )
+ {
+ pGlue = new SwGluePortion(0);
+ pGlue->Width( nLastLeft );
+ pPor->Append( pGlue );
+ pPor = pPor->GetNextPortion();
+ }
+ }
+
+ // Finally: we insert the QuoVadis Portion
+ pCurrPor = pQuo;
+ while ( pCurrPor )
+ {
+ // pPor->Append deletes the pPortion pointer of pPor.
+ // Therefore we have to keep a pointer to the next portion
+ pQuo = static_cast<SwQuoVadisPortion*>(pCurrPor->GetNextPortion());
+ pPor->Append( pCurrPor );
+ pPor = pPor->GetNextPortion();
+ pCurrPor = pQuo;
+ }
+
+ m_pCurr->Width( m_pCurr->Width() + nQuoWidth );
+
+ // And adjust again, due to the adjustment and due to the following special
+ // case:
+ // The DummyUser has set a smaller Font in the Line than the one used
+ // by the QuoVadis text ...
+ CalcAdjustLine( m_pCurr );
+
+ return nRet;
+}
+
+/**
+ * This function creates a Line that reaches to the other Page Margin.
+ * DummyLines or DummyPortions make sure, that oscillations stop, because
+ * there's no way to flow back.
+ * They are used for Footnotes in paragraph-bound Frames and for Footnote
+ * oscillations
+ */
+void SwTextFormatter::MakeDummyLine()
+{
+ sal_uInt16 nRstHeight = GetFrameRstHeight();
+ if( m_pCurr && nRstHeight > m_pCurr->Height() )
+ {
+ SwLineLayout *pLay = new SwLineLayout;
+ nRstHeight = nRstHeight - m_pCurr->Height();
+ pLay->Height( nRstHeight );
+ pLay->SetAscent( nRstHeight );
+ Insert( pLay );
+ Next();
+ }
+}
+
+namespace {
+
+class SwFootnoteSave
+{
+ SwTextSizeInfo* m_pInf;
+ SwFont* m_pFnt;
+ std::unique_ptr<SwFont> m_pOld;
+
+ SwFootnoteSave(const SwFootnoteSave&) = delete;
+ SwFootnoteSave& operator=(const SwFootnoteSave&) = delete;
+
+public:
+ SwFootnoteSave( const SwTextSizeInfo &rInf,
+ const SwTextFootnote *pTextFootnote,
+ const bool bApplyGivenScriptType,
+ const SwFontScript nGivenScriptType );
+ ~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE;
+};
+
+}
+
+SwFootnoteSave::SwFootnoteSave(const SwTextSizeInfo& rInf, const SwTextFootnote* pTextFootnote,
+ const bool bApplyGivenScriptType,
+ const SwFontScript nGivenScriptType)
+ : m_pInf(&const_cast<SwTextSizeInfo&>(rInf))
+ , m_pFnt(nullptr)
+{
+ if( pTextFootnote && rInf.GetTextFrame() )
+ {
+ m_pFnt = const_cast<SwTextSizeInfo&>(rInf).GetFont();
+ m_pOld.reset(new SwFont(*m_pFnt));
+ m_pOld->GetTox() = m_pFnt->GetTox();
+ m_pFnt->GetTox() = 0;
+ SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(pTextFootnote->GetFootnote());
+ const SwDoc *const pDoc = &rInf.GetTextFrame()->GetDoc();
+
+ // #i98418#
+ if ( bApplyGivenScriptType )
+ {
+ m_pFnt->SetActual(nGivenScriptType);
+ }
+ else
+ {
+ // examine text and set script
+ OUString aTmpStr(rFootnote.GetViewNumStr(*pDoc, rInf.GetTextFrame()->getRootFrame()));
+ m_pFnt->SetActual(SwScriptInfo::WhichFont(0, aTmpStr));
+ }
+
+ const SwEndNoteInfo* pInfo;
+ if( rFootnote.IsEndNote() )
+ pInfo = &pDoc->GetEndNoteInfo();
+ else
+ pInfo = &pDoc->GetFootnoteInfo();
+ const SwAttrSet& rSet = pInfo->GetAnchorCharFormat(const_cast<SwDoc&>(*pDoc))->GetAttrSet();
+ m_pFnt->SetDiffFnt(&rSet, &pDoc->getIDocumentSettingAccess());
+
+ // we reduce footnote size, if we are inside a double line portion
+ if (!m_pOld->GetEscapement() && 50 == m_pOld->GetPropr())
+ {
+ Size aSize = m_pFnt->GetSize(m_pFnt->GetActual());
+ m_pFnt->SetSize(Size(aSize.Width() / 2, aSize.Height() / 2), m_pFnt->GetActual());
+ }
+
+ // set the correct rotation at the footnote font
+ if( const SvxCharRotateItem* pItem = rSet.GetItemIfSet( RES_CHRATR_ROTATE ) )
+ m_pFnt->SetVertical(pItem->GetValue(),
+ rInf.GetTextFrame()->IsVertical());
+
+ m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
+
+ if( const SvxBrushItem* pItem = rSet.GetItemIfSet( RES_CHRATR_BACKGROUND ) )
+ m_pFnt->SetBackColor(pItem->GetColor());
+ }
+ else
+ m_pFnt = nullptr;
+}
+
+SwFootnoteSave::~SwFootnoteSave() COVERITY_NOEXCEPT_FALSE
+{
+ if (m_pFnt)
+ {
+ // Put back SwFont
+ *m_pFnt = *m_pOld;
+ m_pFnt->GetTox() = m_pOld->GetTox();
+ m_pFnt->ChgPhysFnt(m_pInf->GetVsh(), *m_pInf->GetOut());
+ m_pOld.reset();
+ }
+}
+
+SwFootnotePortion::SwFootnotePortion( const OUString &rExpand,
+ SwTextFootnote *pFootn, sal_uInt16 nReal )
+ : SwFieldPortion( rExpand, nullptr )
+ , m_pFootnote(pFootn)
+ , m_nOrigHeight( nReal )
+ // #i98418#
+ , mbPreferredScriptTypeSet( false )
+ , mnPreferredScriptType( SwFontScript::Latin )
+{
+ SetLen(TextFrameIndex(1));
+ SetWhichPor( PortionType::Footnote );
+}
+
+bool SwFootnotePortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
+{
+ rText = m_aExpand;
+ return true;
+}
+
+bool SwFootnotePortion::Format( SwTextFormatInfo &rInf )
+{
+ // #i98418#
+// SwFootnoteSave aFootnoteSave( rInf, pFootnote );
+ SwFootnoteSave aFootnoteSave( rInf, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
+ // the idx is manipulated in SwExpandPortion::Format
+ // this flag indicates, that a footnote is allowed to trigger
+ // an underflow during SwTextGuess::Guess
+ rInf.SetFakeLineStart( rInf.GetIdx() > rInf.GetLineStart() );
+ const bool bFull = SwFieldPortion::Format( rInf );
+ rInf.SetFakeLineStart( false );
+ SetAscent( rInf.GetAscent() );
+ Height( rInf.GetTextHeight() );
+ rInf.SetFootnoteDone( !bFull );
+ if( !bFull )
+ rInf.SetParaFootnote();
+ return bFull;
+}
+
+void SwFootnotePortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ // #i98418#
+// SwFootnoteSave aFootnoteSave( rInf, pFootnote );
+ SwFootnoteSave aFootnoteSave( rInf, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
+ rInf.DrawViewOpt( *this, PortionType::Footnote );
+ SwExpandPortion::Paint( rInf );
+}
+
+SwPosSize SwFootnotePortion::GetTextSize( const SwTextSizeInfo &rInfo ) const
+{
+ // #i98418#
+// SwFootnoteSave aFootnoteSave( rInfo, pFootnote );
+ SwFootnoteSave aFootnoteSave( rInfo, m_pFootnote, mbPreferredScriptTypeSet, mnPreferredScriptType );
+ return SwExpandPortion::GetTextSize( rInfo );
+}
+
+// #i98418#
+void SwFootnotePortion::SetPreferredScriptType( SwFontScript nPreferredScriptType )
+{
+ mbPreferredScriptTypeSet = true;
+ mnPreferredScriptType = nPreferredScriptType;
+}
+
+SwFieldPortion *SwQuoVadisPortion::Clone( const OUString &rExpand ) const
+{
+ return new SwQuoVadisPortion( rExpand, m_aErgo );
+}
+
+SwQuoVadisPortion::SwQuoVadisPortion( const OUString &rExp, const OUString& rStr )
+ : SwFieldPortion( rExp ), m_aErgo(rStr)
+{
+ SetLen(TextFrameIndex(0));
+ SetWhichPor( PortionType::QuoVadis );
+}
+
+bool SwQuoVadisPortion::Format( SwTextFormatInfo &rInf )
+{
+ // First try; maybe the Text fits
+ CheckScript( rInf );
+ bool bFull = SwFieldPortion::Format( rInf );
+ SetLen(TextFrameIndex(0));
+
+ if( bFull )
+ {
+ // Second try; we make the String shorter
+ m_aExpand = "...";
+ bFull = SwFieldPortion::Format( rInf );
+ SetLen(TextFrameIndex(0));
+ if( bFull )
+ // Third try; we're done: we crush
+ Width( sal_uInt16(rInf.Width() - rInf.X()) );
+
+ // No multiline Fields for QuoVadis and ErgoSum
+ if( rInf.GetRest() )
+ {
+ delete rInf.GetRest();
+ rInf.SetRest( nullptr );
+ }
+ }
+ return bFull;
+}
+
+bool SwQuoVadisPortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
+{
+ rText = m_aExpand;
+ // if this QuoVadisPortion has a follow, the follow is responsible for
+ // the ergo text.
+ if ( ! HasFollow() )
+ rText += m_aErgo;
+ return true;
+}
+
+void SwQuoVadisPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), m_aExpand + m_aErgo, GetWhichPor() );
+}
+
+void SwQuoVadisPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ // We _always_ want to output per DrawStretchText, because nErgo
+ // can quickly switch
+ if( PrtWidth() )
+ {
+ rInf.DrawViewOpt( *this, PortionType::QuoVadis );
+ SwTextSlot aDiffText( &rInf, this, true, false );
+ SwFontSave aSave( rInf, m_pFont.get() );
+ rInf.DrawText( *this, rInf.GetLen(), true );
+ }
+}
+
+SwFieldPortion *SwErgoSumPortion::Clone( const OUString &rExpand ) const
+{
+ return new SwErgoSumPortion( rExpand, std::u16string_view() );
+}
+
+SwErgoSumPortion::SwErgoSumPortion(const OUString &rExp, std::u16string_view rStr)
+ : SwFieldPortion( rExp )
+{
+ SetLen(TextFrameIndex(0));
+ m_aExpand += rStr;
+
+ // One blank distance to the text
+ m_aExpand += " ";
+ SetWhichPor( PortionType::ErgoSum );
+}
+
+TextFrameIndex SwErgoSumPortion::GetModelPositionForViewPoint(const sal_uInt16) const
+{
+ return TextFrameIndex(0);
+}
+
+bool SwErgoSumPortion::Format( SwTextFormatInfo &rInf )
+{
+ const bool bFull = SwFieldPortion::Format( rInf );
+ SetLen(TextFrameIndex(0));
+ rInf.SetErgoDone( true );
+
+ // No multiline Fields for QuoVadis and ErgoSum
+ if( bFull && rInf.GetRest() )
+ {
+ delete rInf.GetRest();
+ rInf.SetRest( nullptr );
+ }
+
+ // We return false in order to get some text into the current line,
+ // even if it's full (better than looping)
+ return false;
+}
+
+void SwParaPortion::SetErgoSumNum( const OUString& rErgo )
+{
+ SwLineLayout *pLay = this;
+ while( pLay->GetNext() )
+ {
+ pLay = pLay->GetNext();
+ }
+ SwLinePortion *pPor = pLay;
+ SwQuoVadisPortion *pQuo = nullptr;
+ while( pPor && !pQuo )
+ {
+ if ( pPor->IsQuoVadisPortion() )
+ pQuo = static_cast<SwQuoVadisPortion*>(pPor);
+ pPor = pPor->GetNextPortion();
+ }
+ if( pQuo )
+ pQuo->SetNumber( rErgo );
+}
+
+/**
+ * Is called in SwTextFrame::Prepare()
+ */
+bool SwParaPortion::UpdateQuoVadis( std::u16string_view rQuo )
+{
+ SwLineLayout *pLay = this;
+ while( pLay->GetNext() )
+ {
+ pLay = pLay->GetNext();
+ }
+ SwLinePortion *pPor = pLay;
+ SwQuoVadisPortion *pQuo = nullptr;
+ while( pPor && !pQuo )
+ {
+ if ( pPor->IsQuoVadisPortion() )
+ pQuo = static_cast<SwQuoVadisPortion*>(pPor);
+ pPor = pPor->GetNextPortion();
+ }
+
+ if( !pQuo )
+ return false;
+
+ return pQuo->GetQuoText() == rQuo;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txthyph.cxx b/sw/source/core/text/txthyph.cxx
new file mode 100644
index 000000000..e9f6a9d0e
--- /dev/null
+++ b/sw/source/core/text/txthyph.cxx
@@ -0,0 +1,582 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <breakit.hxx>
+#include <editeng/unolingu.hxx>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <viewopt.hxx>
+#include <viewsh.hxx>
+#include <SwPortionHandler.hxx>
+#include "porhyph.hxx"
+#include "inftxt.hxx"
+#include "itrform2.hxx"
+#include "guess.hxx"
+#include <rootfrm.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::i18n;
+
+Reference< XHyphenatedWord > SwTextFormatInfo::HyphWord(
+ const OUString &rText, const sal_Int32 nMinTrail )
+{
+ if( rText.getLength() < 4 || m_pFnt->IsSymbol(m_pVsh) )
+ return nullptr;
+ Reference< XHyphenator > xHyph = ::GetHyphenator();
+ Reference< XHyphenatedWord > xHyphWord;
+
+ if( xHyph.is() )
+ xHyphWord = xHyph->hyphenate( rText,
+ g_pBreakIt->GetLocale( m_pFnt->GetLanguage() ),
+ rText.getLength() - nMinTrail, GetHyphValues() );
+ return xHyphWord;
+
+}
+
+/**
+ * We format a row for interactive hyphenation
+ */
+bool SwTextFrame::Hyphenate(SwInterHyphInfoTextFrame & rHyphInf)
+{
+ vcl::RenderContext* pRenderContext = getRootFrame()->GetCurrShell()->GetOut();
+ OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"swapped frame at SwTextFrame::Hyphenate" );
+
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ // We lock it, to start with
+ OSL_ENSURE( !IsLocked(), "SwTextFrame::Hyphenate: this is locked" );
+
+ // The frame::Frame must have a valid SSize!
+ Calc(pRenderContext);
+ GetFormatted();
+
+ bool bRet = false;
+ if( !IsEmpty() )
+ {
+ // We always need to enable hyphenation
+ // Don't be afraid: the SwTextIter saves the old row in the hyphenate
+ TextFrameLockGuard aLock( this );
+
+ if ( IsVertical() )
+ SwapWidthAndHeight();
+
+ SwTextFormatInfo aInf( getRootFrame()->GetCurrShell()->GetOut(), this, true ); // true for interactive hyph!
+ SwTextFormatter aLine( this, &aInf );
+ aLine.CharToLine( rHyphInf.m_nStart );
+
+ // If we're within the first word of a row, it could've been hyphenated
+ // in the row earlier.
+ // That's why we go one row back.
+ if( aLine.Prev() )
+ {
+ SwLinePortion *pPor = aLine.GetCurr()->GetFirstPortion();
+ while( pPor->GetNextPortion() )
+ pPor = pPor->GetNextPortion();
+ if( pPor->GetWhichPor() == PortionType::SoftHyphen ||
+ pPor->GetWhichPor() == PortionType::SoftHyphenStr )
+ aLine.Next();
+ }
+
+ const TextFrameIndex nEnd = rHyphInf.m_nEnd;
+ while( !bRet && aLine.GetStart() < nEnd )
+ {
+ bRet = aLine.Hyphenate( rHyphInf );
+ if( !aLine.Next() )
+ break;
+ }
+
+ if ( IsVertical() )
+ SwapWidthAndHeight();
+ }
+ return bRet;
+}
+
+/**
+ * We format a row for interactive hyphenation
+ * We can assume that we've already formatted.
+ * We just reformat the row, the hyphenator will be prepared like
+ * the UI expects it to be.
+ * TODO: We can of course optimize this a lot.
+ */
+void SetParaPortion( SwTextInfo *pInf, SwParaPortion *pRoot )
+{
+ OSL_ENSURE( pRoot, "SetParaPortion: no root anymore" );
+ pInf->m_pPara = pRoot;
+}
+
+bool SwTextFormatter::Hyphenate(SwInterHyphInfoTextFrame & rHyphInf)
+{
+ SwTextFormatInfo &rInf = GetInfo();
+
+ // We never need to hyphenate anything in the last row
+ // Except for, if it contains a FlyPortion or if it's the
+ // last row of the Master
+ if( !GetNext() && !rInf.GetTextFly().IsOn() && !m_pFrame->GetFollow() )
+ return false;
+
+ TextFrameIndex nWrdStart = m_nStart;
+
+ // We need to retain the old row
+ // E.g.: The attribute for hyphenation was not set, but
+ // it's always set in SwTextFrame::Hyphenate, because we want
+ // to set breakpoints.
+ SwLineLayout *pOldCurr = m_pCurr;
+
+ InitCntHyph();
+
+ // 5298: IsParaLine() (ex.IsFirstLine) calls GetParaPortion().
+ // We have to create the same conditions: in the first line
+ // we format SwParaPortions...
+ if( pOldCurr->IsParaPortion() )
+ {
+ SwParaPortion *pPara = new SwParaPortion();
+ SetParaPortion( &rInf, pPara );
+ m_pCurr = pPara;
+ OSL_ENSURE( IsParaLine(), "SwTextFormatter::Hyphenate: not the first" );
+ }
+ else
+ m_pCurr = new SwLineLayout();
+
+ nWrdStart = FormatLine( nWrdStart );
+
+ // You always should keep in mind that for example there are fields
+ // which can be hyphenated
+ if( m_pCurr->PrtWidth() && m_pCurr->GetLen() )
+ {
+ // We must be prepared that there are FlyFrames in the line,
+ // at which line breaking is possible. So we search for the first
+ // HyphPortion in the specified range.
+
+ SwLinePortion *pPos = m_pCurr->GetNextPortion();
+ const TextFrameIndex nPamStart = rHyphInf.m_nStart;
+ nWrdStart = m_nStart;
+ const TextFrameIndex nEnd = rHyphInf.m_nEnd;
+ while( pPos )
+ {
+ // Either we are above or we are running into a HyphPortion
+ // at the end of line or before a Fly.
+ if( nWrdStart >= nEnd )
+ {
+ nWrdStart = TextFrameIndex(0);
+ break;
+ }
+
+ if( nWrdStart >= nPamStart && pPos->InHyphGrp()
+ && ( !pPos->IsSoftHyphPortion()
+ || static_cast<SwSoftHyphPortion*>(pPos)->IsExpand() ) )
+ {
+ nWrdStart = nWrdStart + pPos->GetLen();
+ break;
+ }
+
+ nWrdStart = nWrdStart + pPos->GetLen();
+ pPos = pPos->GetNextPortion();
+ }
+ // When pPos is null, no hyphen position was found.
+ if( !pPos )
+ nWrdStart = TextFrameIndex(0);
+ }
+ else
+ // In case the whole line is zero-length, that's the same situation as
+ // above when the portion iteration ends without explicitly breaking
+ // from the loop.
+ nWrdStart = TextFrameIndex(0);
+
+ // the old LineLayout is set again ...
+ delete m_pCurr;
+ m_pCurr = pOldCurr;
+
+ if( pOldCurr->IsParaPortion() )
+ {
+ SetParaPortion( &rInf, static_cast<SwParaPortion*>(pOldCurr) );
+ OSL_ENSURE( IsParaLine(), "SwTextFormatter::Hyphenate: even not the first" );
+ }
+
+ if (nWrdStart == TextFrameIndex(0))
+ return false;
+
+ // nWrdStart contains the position in string that should be hyphenated
+ rHyphInf.m_nWordStart = nWrdStart;
+
+ TextFrameIndex nLen(0);
+ const TextFrameIndex nEnd = nWrdStart;
+
+ // we search forwards
+ Reference< XHyphenatedWord > xHyphWord;
+
+ Boundary const aBound = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ rInf.GetText(), sal_Int32(nWrdStart),
+ g_pBreakIt->GetLocale( rInf.GetFont()->GetLanguage() ), WordType::DICTIONARY_WORD, true );
+ nWrdStart = TextFrameIndex(aBound.startPos);
+ nLen = TextFrameIndex(aBound.endPos) - nWrdStart;
+ if (nLen == TextFrameIndex(0))
+ return false;
+
+ OUString const aSelText(rInf.GetText().copy(sal_Int32(nWrdStart), sal_Int32(nLen)));
+ const sal_Int32 nMinTrail = (nWrdStart + nLen > nEnd)
+ ? sal_Int32(nWrdStart + nLen - nEnd) - 1
+ : 0;
+
+ //!! rHyphInf.SetHyphWord( ... ) must done here
+ xHyphWord = rInf.HyphWord( aSelText, nMinTrail );
+ if ( xHyphWord.is() )
+ {
+ rHyphInf.SetHyphWord( xHyphWord );
+ rHyphInf.m_nWordStart = nWrdStart;
+ rHyphInf.m_nWordLen = nLen;
+ return true;
+ }
+
+ return false;
+}
+
+bool SwTextPortion::CreateHyphen( SwTextFormatInfo &rInf, SwTextGuess const &rGuess )
+{
+ const Reference< XHyphenatedWord >& xHyphWord = rGuess.HyphWord();
+
+ OSL_ENSURE( !mpNextPortion, "SwTextPortion::CreateHyphen(): another portion, another planet..." );
+ OSL_ENSURE( xHyphWord.is(), "SwTextPortion::CreateHyphen(): You are lucky! The code is robust here." );
+
+ if( rInf.IsHyphForbud() ||
+ mpNextPortion || // robust
+ !xHyphWord.is() || // more robust
+ // multi-line fields can't be hyphenated interactively
+ ( rInf.IsInterHyph() && InFieldGrp() ) )
+ return false;
+
+ std::unique_ptr<SwHyphPortion> pHyphPor;
+ TextFrameIndex nPorEnd;
+ SwTextSizeInfo aInf( rInf );
+
+ // first case: hyphenated word has alternative spelling
+ if ( xHyphWord->isAlternativeSpelling() )
+ {
+ SvxAlternativeSpelling aAltSpell = SvxGetAltSpelling( xHyphWord );
+ OSL_ENSURE( aAltSpell.bIsAltSpelling, "no alternative spelling" );
+
+ OUString aAltText = aAltSpell.aReplacement;
+ nPorEnd = TextFrameIndex(aAltSpell.nChangedPos) + rGuess.BreakStart() - rGuess.FieldDiff();
+ sal_Int32 nTmpLen = 0;
+
+ // soft hyphen at alternative spelling position?
+ if( rInf.GetText()[sal_Int32(rInf.GetSoftHyphPos())] == CHAR_SOFTHYPHEN )
+ {
+ pHyphPor.reset(new SwSoftHyphStrPortion( aAltText ));
+ nTmpLen = 1;
+ }
+ else {
+ pHyphPor.reset(new SwHyphStrPortion( aAltText ));
+ }
+
+ // length of pHyphPor is adjusted
+ pHyphPor->SetLen( TextFrameIndex(aAltText.getLength() + 1) );
+ static_cast<SwPosSize&>(*pHyphPor) = pHyphPor->GetTextSize( rInf );
+ pHyphPor->SetLen( TextFrameIndex(aAltSpell.nChangedLength + nTmpLen) );
+ }
+ else
+ {
+ // second case: no alternative spelling
+ pHyphPor.reset(new SwHyphPortion);
+ pHyphPor->SetLen(TextFrameIndex(1));
+
+ static const void* nLastFontCacheId = nullptr;
+ static sal_uInt16 aMiniCacheH = 0, aMiniCacheW = 0;
+ const void* nTmpFontCacheId;
+ sal_uInt16 nFntIdx;
+ rInf.GetFont()->GetFontCacheId( nTmpFontCacheId, nFntIdx, rInf.GetFont()->GetActual() );
+ if( !nLastFontCacheId || nLastFontCacheId != nTmpFontCacheId ) {
+ nLastFontCacheId = nTmpFontCacheId;
+ static_cast<SwPosSize&>(*pHyphPor) = pHyphPor->GetTextSize( rInf );
+ aMiniCacheH = pHyphPor->Height();
+ aMiniCacheW = pHyphPor->Width();
+ } else {
+ pHyphPor->Height( aMiniCacheH );
+ pHyphPor->Width( aMiniCacheW );
+ }
+ pHyphPor->SetLen(TextFrameIndex(0));
+
+ // values required for this
+ nPorEnd = TextFrameIndex(xHyphWord->getHyphenPos() + 1)
+ + rGuess.BreakStart() - rGuess.FieldDiff();
+ }
+
+ // portion end must be in front of us
+ // we do not put hyphens at start of line
+ if ( nPorEnd > rInf.GetIdx() ||
+ ( nPorEnd == rInf.GetIdx() && rInf.GetLineStart() != rInf.GetIdx() ) )
+ {
+ aInf.SetLen( nPorEnd - rInf.GetIdx() );
+ pHyphPor->SetAscent( GetAscent() );
+ SetLen( aInf.GetLen() );
+ CalcTextSize( aInf );
+
+ Insert( pHyphPor.release() );
+
+ short nKern = rInf.GetFont()->CheckKerning();
+ if( nKern )
+ new SwKernPortion( *this, nKern );
+
+ return true;
+ }
+
+ // last exit for the lost
+ pHyphPor.reset();
+ BreakCut( rInf, rGuess );
+ return false;
+}
+
+bool SwHyphPortion::GetExpText( const SwTextSizeInfo &/*rInf*/, OUString &rText ) const
+{
+ rText = "-";
+ return true;
+}
+
+void SwHyphPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), OUString('-'), GetWhichPor() );
+}
+
+void SwHyphPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText,
+ TextFrameIndex& nOffset) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHyphPortion"));
+ dumpAsXmlAttributes(pWriter, rText, nOffset);
+ nOffset += GetLen();
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SwHyphPortion::Format( SwTextFormatInfo &rInf )
+{
+ const SwLinePortion *pLast = rInf.GetLast();
+ Height( pLast->Height() );
+ SetAscent( pLast->GetAscent() );
+ OUString aText;
+
+ if( !GetExpText( rInf, aText ) )
+ return false;
+
+ PrtWidth( rInf.GetTextSize( aText ).Width() );
+ const bool bFull = rInf.Width() <= rInf.X() + PrtWidth();
+ if( bFull && !rInf.IsUnderflow() ) {
+ Truncate();
+ rInf.SetUnderflow( this );
+ }
+
+ return bFull;
+}
+
+bool SwHyphStrPortion::GetExpText( const SwTextSizeInfo &, OUString &rText ) const
+{
+ rText = m_aExpand;
+ return true;
+}
+
+void SwHyphStrPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Special( GetLen(), m_aExpand, GetWhichPor() );
+}
+
+SwLinePortion *SwSoftHyphPortion::Compress() { return this; }
+
+SwSoftHyphPortion::SwSoftHyphPortion() :
+ m_bExpand(false), m_nViewWidth(0)
+{
+ SetLen(TextFrameIndex(1));
+ SetWhichPor( PortionType::SoftHyphen );
+}
+
+sal_uInt16 SwSoftHyphPortion::GetViewWidth( const SwTextSizeInfo &rInf ) const
+{
+ // Although we're in the const, nViewWidth should be calculated at
+ // the last possible moment
+ if( !Width() && rInf.OnWin() && rInf.GetOpt().IsSoftHyph() && !IsExpand() )
+ {
+ if( !m_nViewWidth )
+ const_cast<SwSoftHyphPortion*>(this)->m_nViewWidth
+ = rInf.GetTextSize(OUString('-')).Width();
+ }
+ else
+ const_cast<SwSoftHyphPortion*>(this)->m_nViewWidth = 0;
+ return m_nViewWidth;
+}
+
+/**
+ * Cases:
+ *
+ * 1) SoftHyph is in the line, ViewOpt off
+ * -> invisible, neighbors unchanged
+ * 2) SoftHyph is in the line, ViewOpt on
+ * -> visible, neighbors unchanged
+ * 3) SoftHyph is at the end of the line, ViewOpt on or off
+ * -> always visible, neighbors unchanged
+ */
+void SwSoftHyphPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ if( Width() )
+ {
+ rInf.DrawViewOpt( *this, PortionType::SoftHyphen );
+ SwExpandPortion::Paint( rInf );
+ }
+}
+
+/**
+ * We get the final width from the FormatEOL()
+ *
+ * During the underflow-phase we determine, whether or not
+ * there's an alternative spelling at all ...
+ *
+ * Case 1: "Au-to"
+ * 1) {Au}{-}{to}, {to} does not fit anymore => underflow
+ * 2) {-} calls hyphenate => no alternative
+ * 3) FormatEOL() and bFull = true
+ *
+ * Case 2: "Zuc-ker"
+ * 1) {Zuc}{-}{ker}, {ker} does not fit anymore => underflow
+ * 2) {-} calls hyphenate => alternative!
+ * 3) Underflow() and bFull = true
+ * 4) {Zuc} calls hyphenate => {Zuk}{-}{ker}
+ */
+bool SwSoftHyphPortion::Format( SwTextFormatInfo &rInf )
+{
+ bool bFull = true;
+
+ // special case for old German spelling
+ if( rInf.IsUnderflow() )
+ {
+ if( rInf.GetSoftHyphPos() )
+ return true;
+
+ const bool bHyph = rInf.ChgHyph( true );
+ if( rInf.IsHyphenate() )
+ {
+ rInf.SetSoftHyphPos( rInf.GetIdx() );
+ Width(0);
+ // if the soft hyphened word has an alternative spelling
+ // when hyphenated (old German spelling), the soft hyphen
+ // portion has to trigger an underflow
+ SwTextGuess aGuess;
+ bFull = rInf.IsInterHyph() ||
+ !aGuess.AlternativeSpelling(rInf, rInf.GetIdx() - TextFrameIndex(1));
+ }
+ rInf.ChgHyph( bHyph );
+
+ if( bFull && !rInf.IsHyphForbud() )
+ {
+ rInf.SetSoftHyphPos(TextFrameIndex(0));
+ FormatEOL( rInf );
+ if ( rInf.GetFly() )
+ rInf.GetRoot()->SetMidHyph( true );
+ else
+ rInf.GetRoot()->SetEndHyph( true );
+ }
+ else
+ {
+ rInf.SetSoftHyphPos( rInf.GetIdx() );
+ Truncate();
+ rInf.SetUnderflow( this );
+ }
+ return true;
+ }
+
+ rInf.SetSoftHyphPos(TextFrameIndex(0));
+ SetExpand( true );
+ bFull = SwHyphPortion::Format( rInf );
+ SetExpand( false );
+ if( !bFull )
+ {
+ // By default, we do not have a width, but we do have a height
+ Width(0);
+ }
+ return bFull;
+}
+
+/**
+ * Format End of Line
+ */
+void SwSoftHyphPortion::FormatEOL( SwTextFormatInfo &rInf )
+{
+ if( IsExpand() )
+ return;
+
+ SetExpand( true );
+ if( rInf.GetLast() == this )
+ rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
+
+ // We need to reset the old values
+ const SwTwips nOldX = rInf.X();
+ TextFrameIndex const nOldIdx = rInf.GetIdx();
+ rInf.X( rInf.X() - PrtWidth() );
+ rInf.SetIdx( rInf.GetIdx() - GetLen() );
+ const bool bFull = SwHyphPortion::Format( rInf );
+
+ // Shady business: We're allowed to get wider, but a Fly is also
+ // being processed, which needs a correct X position
+ if( bFull || !rInf.GetFly() )
+ rInf.X( nOldX );
+ else
+ rInf.X( nOldX + Width() );
+ rInf.SetIdx( nOldIdx );
+}
+
+/**
+ * We're expanding:
+ * - if the special characters should be visible
+ * - if we're at the end of the line
+ * - if we're before a (real/emulated) line break
+ */
+bool SwSoftHyphPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) const
+{
+ if( IsExpand() || ( rInf.OnWin() && rInf.GetOpt().IsSoftHyph() ) ||
+ ( GetNextPortion() && ( GetNextPortion()->InFixGrp() ||
+ GetNextPortion()->IsDropPortion() || GetNextPortion()->IsLayPortion() ||
+ GetNextPortion()->IsParaPortion() || GetNextPortion()->IsBreakPortion() ) ) )
+ {
+ return SwHyphPortion::GetExpText( rInf, rText );
+ }
+ return false;
+}
+
+void SwSoftHyphPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ const PortionType nWhich = ! Width() ?
+ PortionType::SoftHyphenComp :
+ GetWhichPor();
+ rPH.Special( GetLen(), OUString('-'), nWhich );
+}
+
+void SwSoftHyphStrPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ // Bug or feature?:
+ // {Zu}{k-}{ker}, {k-} will be gray instead of {-}
+ rInf.DrawViewOpt( *this, PortionType::SoftHyphen );
+ SwHyphStrPortion::Paint( rInf );
+}
+
+SwSoftHyphStrPortion::SwSoftHyphStrPortion( std::u16string_view rStr )
+ : SwHyphStrPortion( rStr )
+{
+ SetLen(TextFrameIndex(1));
+ SetWhichPor( PortionType::SoftHyphenStr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtinit.cxx b/sw/source/core/text/txtinit.cxx
new file mode 100644
index 000000000..b8a6540f6
--- /dev/null
+++ b/sw/source/core/text/txtinit.cxx
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swcache.hxx>
+#include <fntcache.hxx>
+#include <swfntcch.hxx>
+#include <txtfrm.hxx>
+#include "pordrop.hxx"
+#include <init.hxx>
+#include <txtfly.hxx>
+#include <dbg_lay.hxx>
+
+SwCache *SwTextFrame::s_pTextCache = nullptr;
+SwContourCache *pContourCache = nullptr;
+SwDropCapCache *pDropCapCache = nullptr;
+
+// Are ONLY used in init.cxx.
+// There we have extern void TextFinit()
+// and extern void TextInit_(...)
+
+void TextInit_()
+{
+ pFntCache = new SwFntCache; // Cache for SwSubFont -> SwFntObj = { Font aFont, Font* pScrFont, Font* pPrtFont, OutputDevice* pPrinter, ... }
+ pSwFontCache = new SwFontCache; // Cache for SwTextFormatColl -> SwFontObj = { SwFont aSwFont, SfxPoolItem* pDefaultArray }
+ SwCache *pTextCache = new SwCache( 250 // Cache for SwTextFrame -> SwTextLine = { SwParaPortion* pLine }
+#ifdef DBG_UTIL
+ , "static SwTextFrame::s_pTextCache"
+#endif
+ );
+ SwTextFrame::SetTextCache( pTextCache );
+ PROTOCOL_INIT
+}
+
+void TextFinit()
+{
+ PROTOCOL_STOP
+ delete SwTextFrame::GetTextCache();
+ delete pSwFontCache;
+ delete pFntCache;
+ delete pContourCache;
+ SwDropPortion::DeleteDropCapCache();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtpaint.cxx b/sw/source/core/text/txtpaint.cxx
new file mode 100644
index 000000000..ccd9647bd
--- /dev/null
+++ b/sw/source/core/text/txtpaint.cxx
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "txtpaint.hxx"
+#include <txtfrm.hxx>
+#include <swrect.hxx>
+#include <rootfrm.hxx>
+
+SwSaveClip::~SwSaveClip()
+{
+ // We recover the old state
+ if( !(m_pOut && m_bChg) )
+ return;
+
+ if ( m_pOut->GetConnectMetaFile() )
+ m_pOut->Pop();
+ else
+ {
+ if( m_bOn )
+ m_pOut->SetClipRegion( m_aClip );
+ else
+ m_pOut->SetClipRegion();
+ }
+ m_bChg = false;
+}
+
+void SwSaveClip::ChgClip_( const SwRect &rRect, const SwTextFrame* pFrame,
+ bool bEnlargeRect,
+ sal_Int32 nEnlargeTop,
+ sal_Int32 nEnlargeBottom )
+{
+ SwRect aOldRect( rRect );
+ const bool bVertical = pFrame && pFrame->IsVertical();
+
+ if ( pFrame && pFrame->IsRightToLeft() )
+ pFrame->SwitchLTRtoRTL( const_cast<SwRect&>(rRect) );
+
+ if ( bVertical )
+ pFrame->SwitchHorizontalToVertical( const_cast<SwRect&>(rRect) );
+
+ if ( !m_pOut || (!rRect.HasArea() && !m_pOut->IsClipRegion()) )
+ {
+ const_cast<SwRect&>(rRect) = aOldRect;
+ return;
+ }
+
+ if ( !m_bChg )
+ {
+ if ( m_pOut->GetConnectMetaFile() )
+ m_pOut->Push();
+ else if ( m_bOn )
+ m_aClip = m_pOut->GetClipRegion();
+ }
+
+ if ( !rRect.HasArea() )
+ m_pOut->SetClipRegion();
+ else
+ {
+ tools::Rectangle aRect( rRect.SVRect() );
+
+ // Having underscores in our line, we enlarged the repaint area
+ // (see frmform.cxx) because for some fonts it could be too small.
+ // Consequently, we have to enlarge the clipping rectangle as well.
+ if ( bEnlargeRect && ! bVertical )
+ aRect.AdjustBottom(40 );
+
+ // enlarge clip for paragraph margins at small fixed line height
+ if ( nEnlargeTop > 0 )
+ aRect.AdjustTop( -nEnlargeTop );
+
+ if ( nEnlargeBottom > 0 )
+ aRect.AdjustBottom( nEnlargeBottom );
+
+ // If the ClipRect is identical, nothing will happen
+ if( m_pOut->IsClipRegion() ) // no && because of Mac
+ {
+ if ( aRect == m_pOut->GetClipRegion().GetBoundRect() )
+ {
+ const_cast<SwRect&>(rRect) = aOldRect;
+ return;
+ }
+ }
+
+ if( SwRootFrame::HasSameRect( rRect ) )
+ m_pOut->SetClipRegion();
+ else
+ {
+ const vcl::Region aClipRegion( aRect );
+ m_pOut->SetClipRegion( aClipRegion );
+ }
+ }
+ m_bChg = true;
+
+ const_cast<SwRect&>(rRect) = aOldRect;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txtpaint.hxx b/sw/source/core/text/txtpaint.hxx
new file mode 100644
index 000000000..1f9700af4
--- /dev/null
+++ b/sw/source/core/text/txtpaint.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#pragma once
+
+#include <vcl/outdev.hxx>
+
+class SwRect; // SwSaveClip
+class SwTextFrame;
+
+class SwSaveClip final
+{
+ vcl::Region m_aClip;
+ const bool m_bOn;
+ bool m_bChg;
+
+ VclPtr<OutputDevice> m_pOut;
+ void ChgClip_( const SwRect &rRect, const SwTextFrame* pFrame,
+ bool bEnlargeRect,
+ sal_Int32 nEnlargeTop,
+ sal_Int32 nEnlargeBottom );
+public:
+ explicit SwSaveClip(OutputDevice* pOutDev)
+ : m_bOn(pOutDev && pOutDev->IsClipRegion())
+ , m_bChg(false)
+ , m_pOut(pOutDev)
+ {
+ }
+
+ ~SwSaveClip();
+ void ChgClip( const SwRect &rRect, const SwTextFrame* pFrame = nullptr,
+ bool bEnlargeRect = false,
+ sal_Int32 nEnlargeTop = 0,
+ sal_Int32 nEnlargeBottom = 0)
+ { if( m_pOut ) ChgClip_( rRect, pFrame,
+ bEnlargeRect, nEnlargeTop, nEnlargeBottom ); }
+ bool IsOn() const { return m_bOn; }
+ bool IsChg() const { return m_bChg; }
+};
+
+
+#ifdef DBG_UTIL
+
+class SwDbgOut
+{
+protected:
+ VclPtr<OutputDevice> pOut;
+public:
+ inline SwDbgOut( OutputDevice* pOutDev, const bool bOn );
+};
+
+class DbgBackColor : public SwDbgOut
+{
+ Color aOldFillColor;
+public:
+ DbgBackColor( OutputDevice* pOut, const bool bOn );
+ ~DbgBackColor();
+};
+
+class DbgRect : public SwDbgOut
+{
+public:
+ DbgRect( OutputDevice* pOut, const tools::Rectangle &rRect,
+ const bool bOn,
+ Color eColor );
+};
+
+inline SwDbgOut::SwDbgOut( OutputDevice* pOutDev, const bool bOn )
+ :pOut( bOn ? pOutDev : nullptr )
+{ }
+
+inline DbgBackColor::DbgBackColor( OutputDevice* pOutDev, const bool bOn )
+ :SwDbgOut( pOutDev, bOn )
+{
+ if( pOut )
+ {
+ aOldFillColor = pOut->GetFillColor();
+ pOut->SetFillColor( COL_RED );
+ }
+}
+
+inline DbgBackColor::~DbgBackColor()
+{
+ if( pOut )
+ {
+ pOut->SetFillColor( aOldFillColor );
+ }
+}
+
+inline DbgRect::DbgRect( OutputDevice* pOutDev, const tools::Rectangle &rRect,
+ const bool bOn,
+ Color eColor )
+ : SwDbgOut( pOutDev, bOn )
+{
+ if( pOut )
+ {
+ const Color aColor( eColor );
+ Color aLineColor = pOut->GetLineColor();
+ pOut->SetLineColor( aColor );
+ Color aFillColor = pOut->GetFillColor();
+ pOut->SetFillColor( COL_TRANSPARENT );
+ pOut->DrawRect( rRect );
+ pOut->SetLineColor( aLineColor );
+ pOut->SetFillColor( aFillColor );
+ }
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/txttab.cxx b/sw/source/core/text/txttab.cxx
new file mode 100644
index 000000000..afcd31217
--- /dev/null
+++ b/sw/source/core/text/txttab.cxx
@@ -0,0 +1,661 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/tstpitem.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <doc.hxx>
+#include <SwPortionHandler.hxx>
+
+#include <viewopt.hxx>
+#include "portab.hxx"
+#include "inftxt.hxx"
+#include "itrform2.hxx"
+#include <txtfrm.hxx>
+#include "porfld.hxx"
+#include <memory>
+
+/**
+ * #i24363# tab stops relative to indent
+ *
+ * Return the first tab stop that is > nSearchPos.
+ * If the tab stop is outside the print area, we
+ * return 0 if it is not the first tab stop.
+ */
+const SvxTabStop* SwLineInfo::GetTabStop(const SwTwips nSearchPos, SwTwips& nRight) const
+{
+ for( sal_uInt16 i = 0; i < m_oRuler->Count(); ++i )
+ {
+ const SvxTabStop &rTabStop = m_oRuler->operator[](i);
+ if (nRight && rTabStop.GetTabPos() > nRight)
+ {
+ // Consider the first tabstop to always be in-bounds.
+ if (!i)
+ nRight = rTabStop.GetTabPos();
+ return i ? nullptr : &rTabStop;
+ }
+ if( rTabStop.GetTabPos() > nSearchPos )
+ {
+ if (!i && !nRight)
+ nRight = rTabStop.GetTabPos();
+ return &rTabStop;
+ }
+ }
+ return nullptr;
+}
+
+sal_uInt16 SwLineInfo::NumberOfTabStops() const
+{
+ return m_oRuler->Count();
+}
+
+SwTabPortion *SwTextFormatter::NewTabPortion( SwTextFormatInfo &rInf, bool bAuto ) const
+{
+ IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
+ const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
+ const bool bTabsRelativeToIndent = rIDSA.get(DocumentSettingId::TABS_RELATIVE_TO_INDENT);
+
+ // Update search location - since Center/Decimal tabstops' width is dependent on the following text.
+ SwTabPortion* pTmpLastTab = rInf.GetLastTab();
+ if (pTmpLastTab && (pTmpLastTab->IsTabCenterPortion() || pTmpLastTab->IsTabDecimalPortion()))
+ pTmpLastTab->PostFormat(rInf);
+
+ sal_Unicode cFill = 0;
+ sal_Unicode cDec = 0;
+ SvxTabAdjust eAdj;
+
+ sal_uInt16 nNewTabPos;
+ bool bAutoTabStop = true;
+ {
+ const bool bRTL = m_pFrame->IsRightToLeft();
+ // #i24363# tab stops relative to indent
+ // nTabLeft: The absolute value, the tab stops are relative to: Tabs origin.
+
+ // #i91133#
+ const SwTwips nTabLeft = bRTL
+ ? m_pFrame->getFrameArea().Right() -
+ ( bTabsRelativeToIndent ? GetTabLeft() : 0 )
+ : m_pFrame->getFrameArea().Left() +
+ ( bTabsRelativeToIndent ? GetTabLeft() : 0 );
+
+ // The absolute position, where we started the line formatting
+ SwTwips nLinePos = GetLeftMargin();
+ if ( bRTL )
+ {
+ Point aPoint( nLinePos, 0 );
+ m_pFrame->SwitchLTRtoRTL( aPoint );
+ nLinePos = aPoint.X();
+ }
+
+ // The current position, relative to the line start
+ SwTwips nTabPos = rInf.GetLastTab() ? rInf.GetLastTab()->GetTabPos() : 0;
+ if( nTabPos < rInf.X() )
+ {
+ nTabPos = rInf.X();
+ }
+
+ // The current position in absolute coordinates
+ const SwTwips nCurrentAbsPos = bRTL ?
+ nLinePos - nTabPos :
+ nLinePos + nTabPos;
+
+ SwTwips nMyRight;
+ if ( m_pFrame->IsVertLR() )
+ nMyRight = Left();
+ else
+ nMyRight = Right();
+
+ if ( m_pFrame->IsVertical() )
+ {
+ Point aRightTop( nMyRight, m_pFrame->getFrameArea().Top() );
+ m_pFrame->SwitchHorizontalToVertical( aRightTop );
+ nMyRight = aRightTop.Y();
+ }
+
+ SwTwips nNextPos = 0;
+ bool bAbsoluteNextPos = false;
+
+ // #i24363# tab stops relative to indent
+ // nSearchPos: The current position relative to the tabs origin
+ const SwTwips nSearchPos = bRTL ?
+ nTabLeft - nCurrentAbsPos :
+ nCurrentAbsPos - nTabLeft;
+
+ // First, we examine the tab stops set at the paragraph style or
+ // any hard set tab stops:
+ // Note: If there are no user defined tab stops, there is always a
+ // default tab stop.
+ const SwTwips nOldRight = nMyRight;
+ // Accept left-tabstops beyond the paragraph margin for bTabOverSpacing
+ if (bTabOverSpacing)
+ nMyRight = 0;
+ const SvxTabStop* pTabStop = m_aLineInf.GetTabStop( nSearchPos, nMyRight );
+ if (!nMyRight)
+ nMyRight = nOldRight;
+ if (pTabStop)
+ {
+ cFill = ' ' != pTabStop->GetFill() ? pTabStop->GetFill() : 0;
+ cDec = pTabStop->GetDecimal();
+ eAdj = pTabStop->GetAdjustment();
+ nNextPos = pTabStop->GetTabPos();
+ if(!bTabsRelativeToIndent && eAdj == SvxTabAdjust::Default && nSearchPos < 0)
+ {
+ //calculate default tab position of default tabs in negative indent
+ nNextPos = ( nSearchPos / nNextPos ) * nNextPos;
+ }
+ else if (pTabStop->GetTabPos() > nMyRight
+ && pTabStop->GetAdjustment() != SvxTabAdjust::Left)
+ {
+ // A rather special situation. The tabstop found is:
+ // 1.) in a document compatible with MS formats
+ // 2.) not a left tabstop.
+ // 3.) not the first tabstop (in that case nMyRight was adjusted to match tabPos).
+ // 4.) beyond the end of the text area
+ // Therefore, they act like right-tabstops at the edge of the para area.
+ // This benefits DOCX 2013+, and doesn't hurt the earlier formats,
+ // since up till now these were just treated as automatic tabstops.
+ eAdj = SvxTabAdjust::Right;
+ bAbsoluteNextPos = true;
+ nNextPos = rInf.Width();
+ }
+ bAutoTabStop = false;
+ }
+ else
+ {
+ sal_uInt16 nDefTabDist = m_aLineInf.GetDefTabStop();
+ if( USHRT_MAX == nDefTabDist )
+ {
+ const SvxTabStopItem& rTab =
+ m_pFrame->GetAttrSet()->GetPool()->GetDefaultItem( RES_PARATR_TABSTOP );
+ if( rTab.Count() )
+ nDefTabDist = o3tl::narrowing<sal_uInt16>(rTab[0].GetTabPos());
+ else
+ nDefTabDist = SVX_TAB_DEFDIST;
+ m_aLineInf.SetDefTabStop( nDefTabDist );
+ }
+ SwTwips nCount = nSearchPos;
+
+ // Minimum tab stop width is 1
+ if (nDefTabDist <= 0)
+ nDefTabDist = 1;
+
+ nCount /= nDefTabDist;
+ nNextPos = ( nCount < 0 || ( !nCount && nSearchPos <= 0 ) )
+ ? ( nCount * nDefTabDist )
+ : ( ( nCount + 1 ) * nDefTabDist );
+
+ // --> FME 2004-09-21 #117919 Minimum tab stop width is 1 or 51 twips:
+ const SwTwips nMinimumTabWidth = m_pFrame->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT) ? 0 : 50;
+ if( ( bRTL && nTabLeft - nNextPos >= nCurrentAbsPos - nMinimumTabWidth ) ||
+ ( !bRTL && nNextPos + nTabLeft <= nCurrentAbsPos + nMinimumTabWidth ) )
+ {
+ nNextPos += nDefTabDist;
+ }
+ cFill = 0;
+ eAdj = SvxTabAdjust::Left;
+ }
+
+ // #i115705# - correction and refactoring:
+ // overrule determined next tab stop position in order to apply
+ // a tab stop at the left margin under the following conditions:
+ // - the new tab portion is inside the hanging indent
+ // - a tab stop at the left margin is allowed
+ // - the determined next tab stop is a default tab stop position OR
+ // the determined next tab stop is beyond the left margin
+ {
+ tools::Long nLeftMarginTabPos = 0;
+ {
+ if ( !bTabsRelativeToIndent )
+ {
+ if ( bRTL )
+ {
+ Point aPoint( Left(), 0 );
+ m_pFrame->SwitchLTRtoRTL( aPoint );
+ nLeftMarginTabPos = m_pFrame->getFrameArea().Right() - aPoint.X();
+ }
+ else
+ {
+ nLeftMarginTabPos = Left() - m_pFrame->getFrameArea().Left();
+ }
+ }
+ if( m_pCurr->HasForcedLeftMargin() )
+ {
+ SwLinePortion* pPor = m_pCurr->GetNextPortion();
+ while( pPor && !pPor->IsFlyPortion() )
+ {
+ pPor = pPor->GetNextPortion();
+ }
+ if ( pPor )
+ {
+ nLeftMarginTabPos += pPor->Width();
+ }
+ }
+ }
+ const bool bNewTabPortionInsideHangingIndent =
+ bRTL ? nCurrentAbsPos > nTabLeft - nLeftMarginTabPos
+ : nCurrentAbsPos < nTabLeft + nLeftMarginTabPos;
+ if ( bNewTabPortionInsideHangingIndent )
+ {
+ // If the paragraph is not inside a list having a list tab stop following
+ // the list label or no further tab stop found in such a paragraph or
+ // the next tab stop position does not equal the list tab stop,
+ // a tab stop at the left margin can be applied. If this condition is
+ // not hold, it is overruled by compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST.
+ const bool bTabAtLeftMarginAllowed =
+ ( !m_aLineInf.IsListTabStopIncluded() ||
+ !pTabStop ||
+ nNextPos != m_aLineInf.GetListTabStopPosition() ) ||
+ // compatibility option TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST:
+ m_pFrame->GetDoc().getIDocumentSettingAccess().
+ get(DocumentSettingId::TAB_AT_LEFT_INDENT_FOR_PARA_IN_LIST);
+ if ( bTabAtLeftMarginAllowed )
+ {
+ if ( !pTabStop || eAdj == SvxTabAdjust::Default ||
+ ( nNextPos > nLeftMarginTabPos ) )
+ {
+ eAdj = SvxTabAdjust::Default;
+ cFill = 0;
+ nNextPos = nLeftMarginTabPos;
+ }
+ }
+ }
+ }
+
+ if (!bAbsoluteNextPos)
+ nNextPos += bRTL ? nLinePos - nTabLeft : nTabLeft - nLinePos;
+ OSL_ENSURE( nNextPos >= 0, "GetTabStop: Don't go back!" );
+ nNewTabPos = sal_uInt16(nNextPos);
+ }
+
+ SwTabPortion *pTabPor = nullptr;
+ if ( bAuto )
+ {
+ if ( SvxTabAdjust::Decimal == eAdj &&
+ 1 == m_aLineInf.NumberOfTabStops() )
+ pTabPor = new SwAutoTabDecimalPortion( nNewTabPos, cDec, cFill );
+ }
+ else
+ {
+ switch( eAdj )
+ {
+ case SvxTabAdjust::Right :
+ {
+ pTabPor = new SwTabRightPortion( nNewTabPos, cFill );
+ break;
+ }
+ case SvxTabAdjust::Center :
+ {
+ pTabPor = new SwTabCenterPortion( nNewTabPos, cFill );
+ break;
+ }
+ case SvxTabAdjust::Decimal :
+ {
+ pTabPor = new SwTabDecimalPortion( nNewTabPos, cDec, cFill );
+ break;
+ }
+ default:
+ {
+ OSL_ENSURE( SvxTabAdjust::Left == eAdj || SvxTabAdjust::Default == eAdj,
+ "+SwTextFormatter::NewTabPortion: unknown adjustment" );
+ pTabPor = new SwTabLeftPortion( nNewTabPos, cFill, bAutoTabStop );
+ break;
+ }
+ }
+ }
+
+ return pTabPor;
+}
+
+/**
+ * The base class is initialized without setting anything
+ */
+SwTabPortion::SwTabPortion( const sal_uInt16 nTabPosition, const sal_Unicode cFillChar, const bool bAutoTab )
+ : m_nTabPos(nTabPosition), m_cFill(cFillChar), m_bAutoTabStop( bAutoTab )
+{
+ mnLineLength = TextFrameIndex(1);
+ OSL_ENSURE(!IsFilled() || ' ' != m_cFill, "SwTabPortion::CTOR: blanks ?!");
+ SetWhichPor( PortionType::Tab );
+}
+
+bool SwTabPortion::Format( SwTextFormatInfo &rInf )
+{
+ SwTabPortion *pLastTab = rInf.GetLastTab();
+ if( pLastTab == this )
+ return PostFormat( rInf );
+ if( pLastTab )
+ pLastTab->PostFormat( rInf );
+ return PreFormat( rInf );
+}
+
+void SwTabPortion::FormatEOL( SwTextFormatInfo &rInf )
+{
+ if( rInf.GetLastTab() == this )
+ PostFormat( rInf );
+}
+
+bool SwTabPortion::PreFormat( SwTextFormatInfo &rInf )
+{
+ OSL_ENSURE( rInf.X() <= GetTabPos(), "SwTabPortion::PreFormat: rush hour" );
+
+ // Here we settle down ...
+ SetFix( o3tl::narrowing<sal_uInt16>(rInf.X()) );
+
+ IDocumentSettingAccess const& rIDSA(rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess());
+ const bool bTabCompat = rIDSA.get(DocumentSettingId::TAB_COMPAT);
+ const bool bTabOverflow = rIDSA.get(DocumentSettingId::TAB_OVERFLOW);
+ const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
+ const bool bTabOverSpacing = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
+ const sal_Int32 nTextFrameWidth = rInf.GetTextFrame()->getFrameArea().Width();
+
+ // The minimal width of a tab is one blank at least.
+ // #i37686# In compatibility mode, the minimum width
+ // should be 1, even for non-left tab stops.
+ sal_uInt16 nMinimumTabWidth = 1;
+ if ( !bTabCompat )
+ {
+ // #i89179#
+ // tab portion representing the list tab of a list label gets the
+ // same font as the corresponding number portion
+ std::unique_ptr< SwFontSave > pSave;
+ if ( GetLen() == TextFrameIndex(0) &&
+ rInf.GetLast() && rInf.GetLast()->InNumberGrp() &&
+ static_cast<SwNumberPortion*>(rInf.GetLast())->HasFont() )
+ {
+ const SwFont* pNumberPortionFont =
+ static_cast<SwNumberPortion*>(rInf.GetLast())->GetFont();
+ pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) );
+ }
+ OUString aTmp( ' ' );
+ SwTextSizeInfo aInf( rInf, &aTmp );
+ nMinimumTabWidth = aInf.GetTextSize().Width();
+ }
+ PrtWidth( nMinimumTabWidth );
+
+ // Break tab stop to next line if:
+ // 1. Minimal width does not fit to line anymore.
+ // 2. An underflow event was called for the tab portion.
+ bool bFull = ( bTabCompat && rInf.IsUnderflow() ) ||
+ ( rInf.Width() <= rInf.X() + PrtWidth() && rInf.X() <= rInf.Width() ) ;
+
+ // #95477# Rotated tab stops get the width of one blank
+ const Degree10 nDir = rInf.GetFont()->GetOrientation( rInf.GetTextFrame()->IsVertical() );
+
+ if( ! bFull && 0_deg10 == nDir )
+ {
+ const PortionType nWhich = GetWhichPor();
+ switch( nWhich )
+ {
+ case PortionType::TabRight:
+ case PortionType::TabDecimal:
+ case PortionType::TabCenter:
+ {
+ if( PortionType::TabDecimal == nWhich )
+ rInf.SetTabDecimal(
+ static_cast<SwTabDecimalPortion*>(this)->GetTabDecimal());
+ rInf.SetLastTab( this );
+ break;
+ }
+ case PortionType::TabLeft:
+ {
+ // handle this case in PostFormat
+ if ((bTabOverMargin || bTabOverSpacing) && GetTabPos() > rInf.Width()
+ && (!m_bAutoTabStop || (!bTabOverMargin && rInf.X() > rInf.Width())))
+ {
+ if (bTabOverMargin || GetTabPos() < nTextFrameWidth)
+ {
+ rInf.SetLastTab(this);
+ break;
+ }
+ else
+ {
+ assert(!bTabOverMargin && bTabOverSpacing && GetTabPos() >= nTextFrameWidth);
+ bFull = true;
+ break;
+ }
+ }
+
+ PrtWidth( o3tl::narrowing<sal_uInt16>(GetTabPos() - rInf.X()) );
+ bFull = rInf.Width() <= rInf.X() + PrtWidth();
+
+ // In tabulator compatibility mode, we reset the bFull flag
+ // if the tabulator is at the end of the paragraph and the
+ // tab stop position is outside the frame:
+ bool bAtParaEnd = rInf.GetIdx() + GetLen() == TextFrameIndex(rInf.GetText().getLength());
+ if ( bFull && bTabCompat &&
+ ( ( bTabOverflow && ( rInf.IsTabOverflow() || !m_bAutoTabStop ) ) || bAtParaEnd ) &&
+ GetTabPos() >= nTextFrameWidth)
+ {
+ bFull = false;
+ if ( bTabOverflow && !m_bAutoTabStop )
+ rInf.SetTabOverflow( true );
+ }
+
+ break;
+ }
+ default: OSL_ENSURE( false, "SwTabPortion::PreFormat: unknown adjustment" );
+ }
+ }
+
+ if( bFull )
+ {
+ // We have to look for endless loops, if the width is smaller than one blank
+ if( rInf.GetIdx() == rInf.GetLineStart() &&
+ // #119175# TabStop should be forced to current
+ // line if there is a fly reducing the line width:
+ !rInf.GetFly() )
+ {
+ PrtWidth( o3tl::narrowing<sal_uInt16>(rInf.Width() - rInf.X()) );
+ SetFixWidth( PrtWidth() );
+ }
+ else
+ {
+ Height( 0 );
+ Width( 0 );
+ SetLen( TextFrameIndex(0) );
+ SetAscent( 0 );
+ SetNextPortion( nullptr ); //?????
+ }
+ return true;
+ }
+ else
+ {
+ // A trick with impact: The new Tabportions now behave like
+ // FlyFrames, located in the line - including adjustment !
+ SetFixWidth( PrtWidth() );
+ return false;
+ }
+}
+
+bool SwTabPortion::PostFormat( SwTextFormatInfo &rInf )
+{
+ bool bTabOverMargin = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_MARGIN);
+ bool bTabOverSpacing = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(
+ DocumentSettingId::TAB_OVER_SPACING);
+ if (rInf.GetTextFrame()->IsInSct())
+ bTabOverMargin = false;
+
+ // If the tab position is larger than the right margin, it gets scaled down by default.
+ // However, if compat mode enabled, we allow tabs to go over the margin: the rest of the paragraph is not broken into lines.
+ const sal_uInt16 nRight
+ = bTabOverMargin
+ ? GetTabPos()
+ : bTabOverSpacing
+ ? std::min<long>(GetTabPos(), rInf.GetTextFrame()->getFrameArea().Right())
+ : std::min(GetTabPos(), rInf.Width());
+ const SwLinePortion *pPor = GetNextPortion();
+
+ sal_uInt16 nPorWidth = 0;
+ while( pPor )
+ {
+ nPorWidth = nPorWidth + pPor->Width();
+ pPor = pPor->GetNextPortion();
+ }
+
+ const PortionType nWhich = GetWhichPor();
+ const bool bTabCompat = rInf.GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT);
+
+ if ((bTabOverMargin || bTabOverSpacing) && PortionType::TabLeft == nWhich)
+ {
+ nPorWidth = 0;
+ }
+
+ // #127428# Abandon dec. tab position if line is full
+ if ( bTabCompat && PortionType::TabDecimal == nWhich )
+ {
+ sal_uInt16 nPrePorWidth = static_cast<const SwTabDecimalPortion*>(this)->GetWidthOfPortionsUpToDecimalPosition();
+
+ // no value was set => no decimal character was found
+ if ( USHRT_MAX != nPrePorWidth )
+ {
+ if ( !bTabOverMargin && nPrePorWidth && nPorWidth - nPrePorWidth > rInf.Width() - nRight )
+ {
+ nPrePorWidth += nPorWidth - nPrePorWidth - ( rInf.Width() - nRight );
+ }
+
+ nPorWidth = nPrePorWidth - 1;
+ }
+ }
+
+ if( PortionType::TabCenter == nWhich )
+ {
+ // centered tabs are problematic:
+ // We have to detect how much fits into the line.
+ sal_uInt16 nNewWidth = nPorWidth /2;
+ if (!bTabOverMargin && !bTabOverSpacing && nNewWidth > rInf.Width() - nRight)
+ nNewWidth = nPorWidth - (rInf.Width() - nRight);
+ nPorWidth = nNewWidth;
+ }
+
+ const sal_uInt16 nDiffWidth = nRight - GetFix();
+
+ if( nDiffWidth > nPorWidth )
+ {
+ const sal_uInt16 nOldWidth = GetFixWidth();
+ const sal_uInt16 nAdjDiff = nDiffWidth - nPorWidth;
+ if( nAdjDiff > GetFixWidth() )
+ PrtWidth( nAdjDiff );
+ // Don't be afraid: we have to move rInf further.
+ // The right-tab till now only had the width of one blank.
+ // Now that we stretched, the difference had to be added to rInf.X() !
+ rInf.X( rInf.X() + PrtWidth() - nOldWidth );
+ }
+ SetFixWidth( PrtWidth() );
+ // reset last values
+ rInf.SetLastTab(nullptr);
+ if( PortionType::TabDecimal == nWhich )
+ rInf.SetTabDecimal(0);
+
+ return rInf.Width() <= rInf.X();
+}
+
+/**
+ * Ex: LineIter::DrawTab()
+ */
+void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const
+{
+ // #i89179#
+ // tab portion representing the list tab of a list label gets the
+ // same font as the corresponding number portion
+ std::unique_ptr< SwFontSave > pSave;
+ bool bAfterNumbering = false;
+ if (GetLen() == TextFrameIndex(0))
+ {
+ const SwLinePortion* pPrevPortion =
+ const_cast<SwTabPortion*>(this)->FindPrevPortion( rInf.GetParaPortion() );
+ if ( pPrevPortion &&
+ pPrevPortion->InNumberGrp() &&
+ static_cast<const SwNumberPortion*>(pPrevPortion)->HasFont() )
+ {
+ const SwFont* pNumberPortionFont =
+ static_cast<const SwNumberPortion*>(pPrevPortion)->GetFont();
+ pSave.reset( new SwFontSave( rInf, const_cast<SwFont*>(pNumberPortionFont) ) );
+ bAfterNumbering = true;
+ }
+ }
+ rInf.DrawBackBrush( *this );
+ if( !bAfterNumbering )
+ rInf.DrawBorder( *this );
+
+ // do we have to repaint a post it portion?
+ if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
+ mpNextPortion->PrePaint( rInf, this );
+
+ // display special characters
+ if( rInf.OnWin() && rInf.GetOpt().IsTab() )
+ {
+ // filled tabs are shaded in gray
+ if( IsFilled() )
+ rInf.DrawViewOpt( *this, PortionType::Tab );
+ else
+ rInf.DrawTab( *this );
+ }
+
+ // Tabs should be underlined at once
+ if( rInf.GetFont()->IsPaintBlank() )
+ {
+ // Tabs with filling/filled tabs
+ const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(' ')).Width();
+
+ // Robust:
+ if( nCharWidth )
+ {
+ // Always with kerning, also on printer!
+ sal_uInt16 nChar = Width() / nCharWidth;
+ OUStringBuffer aBuf(nChar);
+ comphelper::string::padToLength(aBuf, nChar, ' ');
+ rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
+ TextFrameIndex(nChar), true);
+ }
+ }
+
+ // Display fill characters
+ if( !IsFilled() )
+ return;
+
+ // Tabs with filling/filled tabs
+ const sal_uInt16 nCharWidth = rInf.GetTextSize(OUString(m_cFill)).Width();
+ OSL_ENSURE( nCharWidth, "!SwTabPortion::Paint: sophisticated tabchar" );
+
+ // Robust:
+ if( nCharWidth )
+ {
+ // Always with kerning, also on printer!
+ sal_uInt16 nChar = Width() / nCharWidth;
+ if ( m_cFill == '_' )
+ ++nChar; // to avoid gaps
+ OUStringBuffer aBuf(nChar);
+ comphelper::string::padToLength(aBuf, nChar, m_cFill);
+ rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0),
+ TextFrameIndex(nChar), true);
+ }
+}
+
+void SwAutoTabDecimalPortion::Paint( const SwTextPaintInfo & ) const
+{
+}
+
+void SwTabPortion::HandlePortion( SwPortionHandler& rPH ) const
+{
+ rPH.Text( GetLen(), GetWhichPor(), Height(), Width() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx
new file mode 100644
index 000000000..ceae9ee8d
--- /dev/null
+++ b/sw/source/core/text/widorp.cxx
@@ -0,0 +1,609 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <layfrm.hxx>
+#include <ftnboss.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <editeng/orphitem.hxx>
+#include <editeng/widwitem.hxx>
+#include <editeng/keepitem.hxx>
+#include <editeng/spltitem.hxx>
+#include <frmatr.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <rowfrm.hxx>
+
+#include "widorp.hxx"
+#include <txtfrm.hxx>
+#include "itrtxt.hxx"
+#include <sectfrm.hxx>
+#include <ftnfrm.hxx>
+#include <pagefrm.hxx>
+
+#undef WIDOWTWIPS
+
+namespace
+{
+
+// A Follow on the same page as its master is nasty.
+bool IsNastyFollow( const SwTextFrame *pFrame )
+{
+ OSL_ENSURE( !pFrame->IsFollow() || !pFrame->GetPrev() ||
+ static_cast<const SwTextFrame*>(pFrame->GetPrev())->GetFollow() == pFrame,
+ "IsNastyFollow: What is going on here?" );
+ return pFrame->IsFollow() && pFrame->GetPrev();
+}
+
+}
+
+SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, const SwTwips nRst )
+ : m_nRstHeight(nRst), m_pFrame(pNewFrame)
+{
+ SwSwapIfSwapped swap(m_pFrame);
+ SwRectFnSet aRectFnSet(m_pFrame);
+ m_nOrigin = aRectFnSet.GetPrtTop(*m_pFrame);
+ m_bKeep = !m_pFrame->IsMoveable() || IsNastyFollow( m_pFrame );
+ if( !m_bKeep && m_pFrame->IsInSct() )
+ {
+ const SwSectionFrame* const pSct = m_pFrame->FindSctFrame();
+ m_bKeep = pSct->Lower()->IsColumnFrame() && !pSct->MoveAllowed( m_pFrame );
+ }
+ m_bKeep = m_bKeep || !m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetSplit().GetValue() ||
+ m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep().GetValue();
+ m_bBreak = false;
+
+ if( !m_nRstHeight && !m_pFrame->IsFollow() && m_pFrame->IsInFootnote() && m_pFrame->HasPara() )
+ {
+ m_nRstHeight = m_pFrame->GetFootnoteFrameHeight();
+ m_nRstHeight += aRectFnSet.GetHeight(m_pFrame->getFramePrintArea()) -
+ aRectFnSet.GetHeight(m_pFrame->getFrameArea());
+ if( m_nRstHeight < 0 )
+ m_nRstHeight = 0;
+ }
+}
+
+/**
+ * BP 18.6.93: Widows.
+ * In contrast to the first implementation the Widows are not calculated
+ * in advance but detected when formatting the split Follow.
+ * In Master the Widows-calculation is dropped completely
+ * (nWidows is manipulated). If the Follow detects that the
+ * Widows rule applies it sends a Prepare to its predecessor.
+ * A special problem is when the Widow rule applies but in Master
+ * there are some lines available.
+ *
+ * BP(22.07.92): Calculation of Widows and Orphans.
+ * The method returns true if one of the rules matches.
+ *
+ * One difficulty with Widows and different formats between
+ * Master- and Follow-Frame:
+ * Example: If the first column is 3cm and the second is 4cm and
+ * Widows is set to 3, the decision if the Widows rule matches can not
+ * be done until the Follow is formatted. Unfortunately this is crucial
+ * to decide if the whole paragraph goes to the next page or not.
+ */
+bool SwTextFrameBreak::IsInside( SwTextMargin const &rLine ) const
+{
+ bool bFit = false;
+
+ SwSwapIfSwapped swap(m_pFrame);
+ SwRectFnSet aRectFnSet(m_pFrame);
+ // nOrigin is an absolute value, rLine refers to the swapped situation.
+
+ SwTwips nTmpY;
+ if ( m_pFrame->IsVertical() )
+ nTmpY = m_pFrame->SwitchHorizontalToVertical( rLine.Y() + rLine.GetLineHeight() );
+ else
+ nTmpY = rLine.Y() + rLine.GetLineHeight();
+
+ SwTwips nLineHeight = aRectFnSet.YDiff( nTmpY , m_nOrigin );
+
+ // Calculate extra space for bottom border.
+ nLineHeight += aRectFnSet.GetBottomMargin(*m_pFrame);
+
+ if( m_nRstHeight )
+ bFit = m_nRstHeight >= nLineHeight;
+ else
+ {
+ // The Frame has a height to fit on the page.
+ SwTwips nHeight =
+ aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*m_pFrame->GetUpper()), m_nOrigin );
+ SwTwips nDiff = nHeight - nLineHeight;
+
+ // Hide whitespace may require not to insert a new page.
+ SwPageFrame* pPageFrame = m_pFrame->FindPageFrame();
+ if (!pPageFrame->CheckPageHeightValidForHideWhitespace(nDiff))
+ nDiff = 0;
+
+ // If everything is inside the existing frame the result is true;
+ bFit = nDiff >= 0;
+
+ // If it didn't fit, try to add the space of footnotes that are anchored
+ // in frames below (in next-chain of) this one as they will need to move
+ // forward anyway if this frame is split.
+ // - except if in tables (need to check if row is splittable?
+ // also, multiple columns looks difficult)
+ if (!bFit && !m_pFrame->IsInTab())
+ {
+ if (SwFootnoteBossFrame const*const pBoss = m_pFrame->FindFootnoteBossFrame())
+ {
+ if (SwFootnoteContFrame const*const pCont = pBoss->FindFootnoteCont())
+ {
+ SwContentFrame const* pContent(m_pFrame);
+ while (pContent->HasFollow())
+ {
+ pContent = pContent->GetFollow();
+ }
+ // start with first text frame that isn't a follow
+ // (ignoring Keep attribute for now, MakeAll should handle it?)
+ pContent = pContent->GetNextContentFrame();
+ ::std::set<SwContentFrame const*> nextFrames;
+ while (pBoss->IsAnLower(pContent))
+ {
+ nextFrames.insert(pContent);
+ pContent = pContent->GetNextContentFrame();
+ }
+ SwTwips nNextFootnotes(0);
+ for (SwFootnoteFrame const* pFootnote = static_cast<SwFootnoteFrame const*>(pCont->Lower());
+ pFootnote != nullptr;
+ pFootnote = static_cast<SwFootnoteFrame const*>(pFootnote->GetNext()))
+ {
+ SwContentFrame const*const pAnchor = pFootnote->GetRef();
+ if (nextFrames.find(pAnchor) != nextFrames.end())
+ {
+ nNextFootnotes += aRectFnSet.GetHeight(pFootnote->getFrameArea());
+ }
+ }
+ bFit = 0 <= nDiff + nNextFootnotes;
+ SAL_INFO_IF(bFit, "sw.core", "no text frame break because ignoring "
+ << nNextFootnotes << " footnote height");
+ }
+ }
+ }
+ if (!bFit && rLine.MaybeHasHints() && m_pFrame->GetFollow()
+ // if using same footnote container as the follow, pointless to try?
+ && m_pFrame->FindFootnoteBossFrame() != m_pFrame->GetFollow()->FindFootnoteBossFrame())
+ {
+ // possibly a footnote that is anchored beyond the end of this
+ // (the last) line is in the way, try to remove it and check again
+ m_pFrame->RemoveFootnote(rLine.GetEnd());
+ nHeight = aRectFnSet.YDiff( aRectFnSet.GetPrtBottom(*m_pFrame->GetUpper()), m_nOrigin );
+ bFit = nHeight >= nLineHeight;
+ }
+ if ( !bFit )
+ {
+ if ( rLine.GetNext() &&
+ m_pFrame->IsInTab() && !m_pFrame->GetFollow() && !m_pFrame->GetIndNext() )
+ {
+ // add additional space taken as lower space as last content in a table
+ // for all text lines except the last one.
+ nHeight += m_pFrame->CalcAddLowerSpaceAsLastInTableCell();
+ bFit = nHeight >= nLineHeight;
+ }
+ }
+ if( !bFit )
+ {
+ // The LineHeight exceeds the current Frame height.
+ // Call a test Grow to detect if the Frame could
+ // grow the requested area.
+ nHeight += m_pFrame->GrowTst( LONG_MAX );
+
+ // The Grow() returns the height by which the Upper of the TextFrame
+ // would let the TextFrame grow.
+ // The TextFrame itself can grow as much as it wants.
+ bFit = nHeight >= nLineHeight;
+ }
+ }
+
+ return bFit;
+}
+
+bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine )
+{
+ SwSwapIfSwapped swap(m_pFrame);
+
+ // bKeep is stronger than IsBreakNow()
+ // Is there enough space ?
+ if( m_bKeep || IsInside( rLine ) )
+ m_bBreak = false;
+ else
+ {
+ /* This class assumes that the SwTextMargin is processed from Top to
+ * Bottom. Because of performance reasons we stop splitting in the
+ * following cases:
+ * If only one line does not fit.
+ * Special case: with DummyPortions there is LineNr == 1, though we
+ * want to split.
+ */
+ // Include DropLines
+
+ bool bFirstLine = 1 == rLine.GetLineNr() && !rLine.GetPrev();
+ m_bBreak = true;
+ if( ( bFirstLine && m_pFrame->GetIndPrev() )
+ || ( rLine.GetLineNr() <= rLine.GetDropLines() ) )
+ {
+ m_bKeep = true;
+ m_bBreak = false;
+ }
+ else if(bFirstLine && m_pFrame->IsInFootnote() && !m_pFrame->FindFootnoteFrame()->GetPrev())
+ {
+ SwLayoutFrame* pTmp = m_pFrame->FindFootnoteBossFrame()->FindBodyCont();
+ if( !pTmp || !pTmp->Lower() )
+ m_bBreak = false;
+ }
+ }
+
+ return m_bBreak;
+}
+
+void SwTextFrameBreak::SetRstHeight( const SwTextMargin &rLine )
+{
+ // Consider bottom margin
+ SwRectFnSet aRectFnSet(m_pFrame);
+
+ m_nRstHeight = aRectFnSet.GetBottomMargin(*m_pFrame);
+
+ if ( aRectFnSet.IsVert() )
+ {
+ if ( m_pFrame->IsVertLR() )
+ m_nRstHeight = aRectFnSet.YDiff( m_pFrame->SwitchHorizontalToVertical( rLine.Y() ) , m_nOrigin );
+ else
+ m_nRstHeight += m_nOrigin - m_pFrame->SwitchHorizontalToVertical( rLine.Y() );
+ }
+ else
+ m_nRstHeight += rLine.Y() - m_nOrigin;
+}
+
+WidowsAndOrphans::WidowsAndOrphans( SwTextFrame *pNewFrame, const SwTwips nRst,
+ bool bChkKeep )
+ : SwTextFrameBreak( pNewFrame, nRst ), m_nWidLines( 0 ), m_nOrphLines( 0 )
+{
+ SwSwapIfSwapped swap(m_pFrame);
+
+ if( m_bKeep )
+ {
+ // If paragraph should not be split but is larger than
+ // the page, then bKeep is overruled.
+ if( bChkKeep && !m_pFrame->GetPrev() && !m_pFrame->IsInFootnote() &&
+ m_pFrame->IsMoveable() &&
+ ( !m_pFrame->IsInSct() || m_pFrame->FindSctFrame()->MoveAllowed(m_pFrame) ) )
+ m_bKeep = false;
+ // Even if Keep is set, Orphans has to be respected.
+ // e.g. if there are chained frames where a Follow in the last frame
+ // receives a Keep, because it is not (forward) movable -
+ // nevertheless the paragraph can request lines from the Master
+ // because of the Orphan rule.
+ if( m_pFrame->IsFollow() )
+ m_nWidLines = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetWidows().GetValue();
+ }
+ else
+ {
+ const SwAttrSet& rSet = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet();
+ const SvxOrphansItem &rOrph = rSet.GetOrphans();
+ if ( rOrph.GetValue() > 1 )
+ m_nOrphLines = rOrph.GetValue();
+ if ( m_pFrame->IsFollow() )
+ m_nWidLines = rSet.GetWidows().GetValue();
+
+ }
+
+ if ( !(m_bKeep || m_nWidLines || m_nOrphLines) )
+ return;
+
+ bool bResetFlags = false;
+
+ if ( m_pFrame->IsInTab() )
+ {
+ // For compatibility reasons, we disable Keep/Widows/Orphans
+ // inside splittable row frames:
+ if ( m_pFrame->GetNextCellLeaf() || m_pFrame->IsInFollowFlowRow() )
+ {
+ const SwFrame* pTmpFrame = m_pFrame->GetUpper();
+ while ( !pTmpFrame->IsRowFrame() )
+ pTmpFrame = pTmpFrame->GetUpper();
+ if ( static_cast<const SwRowFrame*>(pTmpFrame)->IsRowSplitAllowed() )
+ bResetFlags = true;
+ }
+ }
+
+ if( m_pFrame->IsInFootnote() && !m_pFrame->GetIndPrev() )
+ {
+ // Inside of footnotes there are good reasons to turn off the Keep attribute
+ // as well as Widows/Orphans.
+ SwFootnoteFrame *pFootnote = m_pFrame->FindFootnoteFrame();
+ const bool bFt = !pFootnote->GetAttr()->GetFootnote().IsEndNote();
+ if( !pFootnote->GetPrev() &&
+ pFootnote->FindFootnoteBossFrame( bFt ) != pFootnote->GetRef()->FindFootnoteBossFrame( bFt )
+ && ( !m_pFrame->IsInSct() || m_pFrame->FindSctFrame()->MoveAllowed(m_pFrame) ) )
+ {
+ bResetFlags = true;
+ }
+ }
+
+ if ( bResetFlags )
+ {
+ m_bKeep = false;
+ m_nOrphLines = 0;
+ m_nWidLines = 0;
+ }
+}
+
+/**
+ * The Find*-Methods do not only search, but adjust the SwTextMargin to the
+ * line where the paragraph should have a break and truncate the paragraph there.
+ * FindBreak()
+ */
+bool WidowsAndOrphans::FindBreak( SwTextFrame *pFrame, SwTextMargin &rLine,
+ bool bHasToFit )
+{
+ // i#16128 - Why member <pFrame> _*and*_ parameter <pFrame>??
+ // Thus, assertion on situation, that these are different to figure out why.
+ OSL_ENSURE( m_pFrame == pFrame, "<WidowsAndOrphans::FindBreak> - pFrame != pFrame" );
+
+ SwSwapIfSwapped swap(m_pFrame);
+
+ bool bRet = true;
+ sal_uInt16 nOldOrphans = m_nOrphLines;
+ if( bHasToFit )
+ m_nOrphLines = 0;
+ rLine.Bottom();
+
+ if( !IsBreakNowWidAndOrp( rLine ) )
+ bRet = false;
+ if( !FindWidows( pFrame, rLine ) )
+ {
+ bool bBack = false;
+
+ while( IsBreakNowWidAndOrp( rLine ) )
+ {
+ if( rLine.PrevLine() )
+ bBack = true;
+ else
+ break;
+ }
+ // Usually Orphans are not taken into account for HasToFit.
+ // But if Dummy-Lines are concerned and the Orphans rule is violated
+ // we make an exception: We leave behind one Dummyline and take
+ // the whole text to the next page/column.
+ if( rLine.GetLineNr() <= nOldOrphans &&
+ rLine.GetInfo().GetParaPortion()->IsDummy() &&
+ ( ( bHasToFit && bRet ) || IsBreakNow( rLine ) ) )
+ rLine.Top();
+
+ rLine.TruncLines( true );
+ bRet = bBack;
+ }
+ m_nOrphLines = nOldOrphans;
+
+ return bRet;
+}
+
+/**
+ * FindWidows positions the SwTextMargin of the Master to the line where to
+ * break by examining and formatting the Follow.
+ * Returns true if the Widows-rule matches, that means that the
+ * paragraph should not be split (keep) !
+ */
+bool WidowsAndOrphans::FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine )
+{
+ OSL_ENSURE( ! pFrame->IsVertical() || ! pFrame->IsSwapped(),
+ "WidowsAndOrphans::FindWidows with swapped frame" );
+
+ if( !m_nWidLines || !pFrame->IsFollow() )
+ return false;
+
+ rLine.Bottom();
+
+ // We can still cut something off
+ SwTextFrame *pMaster = pFrame->FindMaster();
+ OSL_ENSURE(pMaster, "+WidowsAndOrphans::FindWidows: Widows in a master?");
+ if( !pMaster )
+ return false;
+
+ // If the first line of the Follow does not fit, the master
+ // probably is full of Dummies. In this case a PrepareHint::Widows would be fatal.
+ if( pMaster->GetOffset() == pFrame->GetOffset() )
+ return false;
+
+ // Remaining height of the master
+ SwRectFnSet aRectFnSet(pFrame);
+
+ const SwTwips nDocPrtTop = aRectFnSet.GetPrtTop(*pFrame);
+ SwTwips nOldHeight;
+ SwTwips nTmpY = rLine.Y() + rLine.GetLineHeight();
+
+ if ( aRectFnSet.IsVert() )
+ {
+ nTmpY = pFrame->SwitchHorizontalToVertical( nTmpY );
+ nOldHeight = -aRectFnSet.GetHeight(pFrame->getFramePrintArea());
+ }
+ else
+ nOldHeight = aRectFnSet.GetHeight(pFrame->getFramePrintArea());
+
+ const SwTwips nChg = aRectFnSet.YDiff( nTmpY, nDocPrtTop + nOldHeight );
+
+ // below the Widows-threshold...
+ if( rLine.GetLineNr() >= m_nWidLines )
+ {
+ // Follow to Master I
+ // If the Follow *grows*, there is the chance for the Master to
+ // receive lines, that it was forced to hand over to the Follow lately:
+ // Prepare(Need); check that below nChg!
+ // (0W, 2O, 2M, 2F) + 1F = 3M, 2F
+ if( rLine.GetLineNr() > m_nWidLines && pFrame->IsJustWidow() )
+ {
+ // If the Master is locked, it has probably just donated a line
+ // to us, we don't return that just because we turned it into
+ // multiple lines (e.g. via frames).
+ if( !pMaster->IsLocked() && pMaster->GetUpper() )
+ {
+ const SwTwips nTmpRstHeight = aRectFnSet.BottomDist( pMaster->getFrameArea(),
+ aRectFnSet.GetPrtBottom(*pMaster->GetUpper()) );
+ if ( nTmpRstHeight >=
+ rLine.GetInfo().GetParaPortion()->Height() )
+ {
+ pMaster->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pMaster->InvalidateSize_();
+ pMaster->InvalidatePage();
+ }
+ }
+
+ pFrame->SetJustWidow( false );
+ }
+ return false;
+ }
+
+ // Follow to Master II
+ // If the Follow *shrinks*, maybe the Master can absorb the whole Orphan.
+ // (0W, 2O, 2M, 1F) - 1F = 3M, 0F -> PrepareHint::AdjustSizeWithoutFormatting
+ // (0W, 2O, 3M, 2F) - 1F = 2M, 2F -> PrepareHint::Widows
+
+ if( 0 > nChg && !pMaster->IsLocked() && pMaster->GetUpper() )
+ {
+ SwTwips nTmpRstHeight = aRectFnSet.BottomDist( pMaster->getFrameArea(),
+ aRectFnSet.GetPrtBottom(*pMaster->GetUpper()) );
+ if( nTmpRstHeight >= rLine.GetInfo().GetParaPortion()->Height() )
+ {
+ pMaster->Prepare( PrepareHint::AdjustSizeWithoutFormatting );
+ pMaster->InvalidateSize_();
+ pMaster->InvalidatePage();
+ pFrame->SetJustWidow( false );
+ return false;
+ }
+ }
+
+ // Master to Follow
+ // If the Follow contains fewer lines than Widows after formatting,
+ // we still can move over some lines from the Master. If this triggers
+ // the Orphans rule of the Master, the Master frame must be Grow()n
+ // in its CalcPreps(), such that it won't fit onto its page anymore.
+ // But if the Master Frame can still lose a few lines, we need to
+ // do a Shrink() in the CalcPreps(); the Follow with the Widows then
+ // moves onto the page of the Master, but remains unsplit, so that
+ // it (finally) moves onto the next page. So much for the theory!
+ //
+ // We only request one line at a time for now, because a Master's line
+ // could result in multiple lines for us.
+ // Therefore, the CalcFollow() remains in control until the Follow got all
+ // necessary lines.
+ sal_uInt16 nNeed = 1; // was: nWidLines - rLine.GetLineNr();
+
+ // Special case: Master cannot give lines to follow
+ // i#91421
+ if ( !pMaster->GetIndPrev() )
+ {
+ pMaster->ChgThisLines();
+ sal_uLong nLines = pMaster->GetThisLines();
+ if(nLines == 0 && pMaster->HasPara())
+ {
+ const SwParaPortion *pMasterPara = pMaster->GetPara();
+ if(pMasterPara && pMasterPara->GetNext())
+ nLines = 2;
+ }
+ if( nLines <= nNeed )
+ return false;
+ }
+
+ pMaster->Prepare( PrepareHint::Widows, static_cast<void*>(&nNeed) );
+ return true;
+}
+
+bool WidowsAndOrphans::WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTst, bool bMoveBwd )
+{
+ // Here it does not matter, if pFrame is swapped or not.
+ // IsInside() takes care of itself
+
+ // We expect that rLine is set to the last line
+ OSL_ENSURE( !rLine.GetNext(), "WouldFit: aLine::Bottom missed!" );
+ sal_uInt16 nLineCnt = rLine.GetLineNr();
+
+ // First satisfy the Orphans-rule and the wish for initials ...
+ const sal_uInt16 nMinLines = std::max( GetOrphansLines(), rLine.GetDropLines() );
+ if ( nLineCnt < nMinLines )
+ return false;
+
+ rLine.Top();
+ SwTwips nLineSum = rLine.GetLineHeight();
+
+ // tdf#146500 for MoveBwd(), want at least 1 line with non-fly
+ bool hasNonFly(!bMoveBwd);
+ while (nMinLines > rLine.GetLineNr() || !hasNonFly)
+ {
+ if( !rLine.NextLine() )
+ {
+ if (nMinLines > rLine.GetLineNr())
+ return false;
+ else
+ break;
+ }
+ nLineSum += rLine.GetLineHeight();
+ for (SwLinePortion const* pPortion = rLine.GetCurr()->GetFirstPortion();
+ !hasNonFly && pPortion; pPortion = pPortion->GetNextPortion())
+ {
+ switch (pPortion->GetWhichPor())
+ {
+ case PortionType::Fly:
+ case PortionType::Glue:
+ case PortionType::Margin:
+ break;
+ default:
+ {
+ hasNonFly = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // We do not fit
+ if( !IsInside( rLine ) )
+ return false;
+
+ // Check the Widows-rule
+ if( !m_nWidLines && !m_pFrame->IsFollow() )
+ {
+ // Usually we only have to check for Widows if we are a Follow.
+ // On WouldFit the rule has to be checked for the Master too,
+ // because we are just in the middle of calculating the break.
+ // In Ctor of WidowsAndOrphans the nWidLines are only calced for
+ // Follows from the AttrSet - so we catch up now:
+ const SwAttrSet& rSet = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet();
+ m_nWidLines = rSet.GetWidows().GetValue();
+ }
+
+ // After Orphans/Initials, do enough lines remain for Widows?
+ // If we are currently doing a test formatting, we may not
+ // consider the widows rule for two reasons:
+ // 1. The columns may have different widths.
+ // Widow lines would have wrong width.
+ // 2. Test formatting is only done up to the given space.
+ // we do not have any lines for widows at all.
+ if( bTst || nLineCnt - nMinLines >= m_nWidLines )
+ {
+ if( rMaxHeight >= nLineSum )
+ {
+ rMaxHeight -= nLineSum;
+ return true;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/widorp.hxx b/sw/source/core/text/widorp.hxx
new file mode 100644
index 000000000..193d27f63
--- /dev/null
+++ b/sw/source/core/text/widorp.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <swtypes.hxx>
+#include "itrtxt.hxx"
+
+class SwTextFrame;
+
+class SwTextFrameBreak
+{
+private:
+ SwTwips m_nRstHeight;
+ SwTwips m_nOrigin;
+protected:
+ SwTextFrame *m_pFrame;
+ bool m_bBreak;
+ bool m_bKeep;
+public:
+ SwTextFrameBreak( SwTextFrame *pFrame, const SwTwips nRst = 0 );
+ bool IsBreakNow( SwTextMargin &rLine );
+ bool IsKeepAlways() const { return m_bKeep; }
+
+ void SetKeep( const bool bNew ) { m_bKeep = bNew; }
+
+ bool IsInside( SwTextMargin const &rLine ) const;
+
+ // In order to be able to handle special cases with Footnote.
+ // SetRstHeight sets the rest height for SwTextFrameBreak. This is needed
+ // to call TruncLines() without IsBreakNow() returning another value.
+ // We assume that rLine is pointing to the last non-fitting line.
+
+ void SetRstHeight( const SwTextMargin &rLine );
+};
+
+class WidowsAndOrphans : public SwTextFrameBreak
+{
+private:
+ sal_uInt16 m_nWidLines, m_nOrphLines;
+
+public:
+ WidowsAndOrphans( SwTextFrame *pFrame, const SwTwips nRst = 0,
+ bool bCheckKeep = true );
+ bool FindWidows( SwTextFrame *pFrame, SwTextMargin &rLine );
+ sal_uInt16 GetOrphansLines() const
+ { return m_nOrphLines; }
+ void ClrOrphLines(){ m_nOrphLines = 0; }
+
+ bool FindBreak( SwTextFrame *pFrame, SwTextMargin &rLine, bool bHasToFit );
+ bool WouldFit( SwTextMargin &rLine, SwTwips &rMaxHeight, bool bTest, bool bMoveBwd );
+ // i#16128 - This method is named this way to avoid confusion with
+ // base class method <SwTextFrameBreak::IsBreakNow>, which isn't virtual.
+ bool IsBreakNowWidAndOrp( SwTextMargin &rLine )
+ {
+ bool isOnFirstLine = (rLine.GetLineNr() == 1 && !rLine.GetPrev());
+ if ( isOnFirstLine && rLine.GetCurr()->IsDummy()) {
+ return IsBreakNow( rLine );
+ }
+ if ( rLine.GetLineNr() > m_nOrphLines ) {
+ return IsBreakNow( rLine );
+ }
+ return false;
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/wrong.cxx b/sw/source/core/text/wrong.cxx
new file mode 100644
index 000000000..6240716c2
--- /dev/null
+++ b/sw/source/core/text/wrong.cxx
@@ -0,0 +1,937 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <swtypes.hxx>
+
+#include <SwGrammarMarkUp.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+
+#include <osl/diagnose.h>
+
+SwWrongArea::SwWrongArea( const OUString& rType, WrongListType listType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nPos,
+ sal_Int32 nLen)
+: maType(rType), mxPropertyBag(xPropertyBag), mnPos(nPos), mnLen(nLen), mpSubList(nullptr)
+{
+ mColor = getWrongAreaColor(listType, xPropertyBag);
+ mLineType = getWrongAreaLineType(listType, xPropertyBag);
+}
+
+SwWrongArea::SwWrongArea( const OUString& rType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nPos,
+ sal_Int32 nLen,
+ SwWrongList* pSubList)
+: maType(rType), mxPropertyBag(xPropertyBag), mnPos(nPos), mnLen(nLen), mpSubList(pSubList), mLineType(WRONGAREA_NONE)
+{
+ if (pSubList != nullptr)
+ {
+ mColor = getWrongAreaColor(pSubList->GetWrongListType(), xPropertyBag);
+ mLineType = getWrongAreaLineType(pSubList->GetWrongListType(), xPropertyBag);
+ }
+}
+
+SwWrongList::SwWrongList( WrongListType eType ) :
+ meType (eType),
+ mnBeginInvalid(COMPLETE_STRING), // everything correct... (the invalid area starts beyond the string)
+ mnEndInvalid (COMPLETE_STRING)
+{
+ maList.reserve( 5 );
+}
+
+SwWrongList::~SwWrongList()
+{
+ ClearList();
+}
+
+SwWrongList* SwWrongList::Clone()
+{
+ SwWrongList* pClone = new SwWrongList( meType );
+ pClone->CopyFrom( *this );
+ return pClone;
+}
+
+void SwWrongList::CopyFrom( const SwWrongList& rCopy )
+{
+ maList = rCopy.maList;
+ meType = rCopy.meType;
+ mnBeginInvalid = rCopy.mnBeginInvalid;
+ mnEndInvalid = rCopy.mnEndInvalid;
+ for(SwWrongArea & i : maList)
+ {
+ if( i.mpSubList )
+ i.mpSubList = i.mpSubList->Clone();
+ }
+}
+
+void SwWrongList::ClearList()
+{
+ for (SwWrongArea & i : maList)
+ {
+ delete i.mpSubList;
+ i.mpSubList = nullptr;
+ }
+ maList.clear();
+}
+
+/** If a word is incorrectly selected, this method returns begin and length of it.
+
+ @param[in,out] rChk starting position of the word to check
+ @param[out] rLn length of the word
+
+ @return <true> if incorrectly selected, <false> otherwise
+ */
+bool SwWrongList::InWrongWord( sal_Int32 &rChk, sal_Int32 &rLn ) const
+{
+ const sal_uInt16 nPos = GetWrongPos( rChk );
+ if ( nPos >= Count() )
+ return false;
+ const sal_Int32 nWrPos = Pos( nPos );
+ if ( nWrPos <= rChk )
+ {
+ rLn = Len( nPos );
+ if( nWrPos + rLn <= rChk )
+ return false;
+ rChk = nWrPos;
+ return true;
+ }
+ return false;
+}
+
+/** Calculate first incorrectly selected area.
+
+ @param[in,out] rChk starting position of the word to check
+ @param[in,out] rLn length of the word
+
+ @return <true> if incorrectly selected area was found, <false> otherwise
+ */
+bool SwWrongList::Check( sal_Int32 &rChk, sal_Int32 &rLn ) const
+{
+ sal_uInt16 nPos = GetWrongPos( rChk );
+ rLn += rChk;
+
+ if( nPos == Count() )
+ return false;
+
+ sal_Int32 nWrPos = Pos( nPos );
+ sal_Int32 nEnd = nWrPos + Len( nPos );
+ if( nEnd == rChk )
+ {
+ ++nPos;
+ if( nPos == Count() )
+ return false;
+
+ nWrPos = Pos( nPos );
+ nEnd = nWrPos + Len( nPos );
+ }
+ if( nEnd > rChk && nWrPos < rLn )
+ {
+ if( nWrPos > rChk )
+ rChk = nWrPos;
+ if( nEnd < rLn )
+ rLn = nEnd;
+ rLn -= rChk;
+ return 0 != rLn;
+ }
+ return false;
+}
+
+/** Find next incorrectly selected position.
+
+ @param[in] rChk starting position of the word to check
+
+ @return starting position of incorrectly selected area, <COMPLETE_STRING> otherwise
+ */
+sal_Int32 SwWrongList::NextWrong( sal_Int32 nChk ) const
+{
+ sal_Int32 nRet = COMPLETE_STRING;
+ sal_uInt16 nPos = GetWrongPos( nChk );
+ if( nPos < Count() )
+ {
+ nRet = Pos( nPos );
+ if( nRet < nChk && nRet + Len( nPos ) <= nChk )
+ {
+ if( ++nPos < Count() )
+ nRet = Pos( nPos );
+ else
+ nRet = COMPLETE_STRING;
+ }
+ }
+ if( nRet > GetBeginInv() && nChk < GetEndInv() )
+ nRet = std::max(nChk, GetBeginInv());
+ return nRet;
+}
+
+/** Find the first position that is greater or equal to the given value.
+
+ @note Resulting position might be behind the last element of the array.
+ @param[in] nValue value for comparison
+
+ @return first position that is greater or equal to the given value
+ */
+sal_uInt16 SwWrongList::GetWrongPos( sal_Int32 nValue ) const
+{
+ sal_uInt16 nMax = Count();
+ sal_uInt16 nMin = 0;
+
+ if( nMax > 0 )
+ {
+ // For smart tag lists, we may not use a binary search. We return the
+ // position of the first smart tag which covers nValue
+ if ( !maList[0].maType.isEmpty() || maList[0].mpSubList )
+ {
+ auto aIter = std::find_if(maList.begin(), maList.end(),
+ [nValue](const SwWrongArea& rST) {
+ return (rST.mnPos <= nValue && nValue < rST.mnPos + rST.mnLen)
+ || (rST.mnPos > nValue);
+ });
+ return o3tl::narrowing<sal_uInt16>(std::distance(maList.begin(), aIter));
+ }
+
+ --nMax;
+ sal_uInt16 nMid = 0;
+ while( nMin <= nMax )
+ {
+ nMid = nMin + ( nMax - nMin ) / 2;
+ const sal_Int32 nTmp = Pos( nMid );
+ if( nTmp == nValue )
+ {
+ nMin = nMid;
+ break;
+ }
+ else if( nTmp < nValue )
+ {
+ if( nTmp + Len( nMid ) >= nValue )
+ {
+ nMin = nMid;
+ break;
+ }
+ nMin = nMid + 1;
+ }
+ else if( nMid == 0 )
+ {
+ break;
+ }
+ else
+ nMax = nMid - 1;
+ }
+ }
+
+ // nMin now points to an index i into the wrong list which
+ // 1. nValue is inside [ Area[i].pos, Area[i].pos + Area[i].len ] (inclusive!!!)
+ // 2. nValue < Area[i].pos
+
+ return nMin;
+}
+
+void SwWrongList::Invalidate_( sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ if ( nBegin < GetBeginInv() )
+ mnBeginInvalid = nBegin;
+ if ( nEnd > GetEndInv() || GetEndInv() == COMPLETE_STRING )
+ mnEndInvalid = nEnd;
+}
+
+void SwWrongList::SetInvalid( sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ mnBeginInvalid = nBegin;
+ mnEndInvalid = nEnd;
+}
+
+/** Change all values after the given position.
+
+ Needed after insert/deletion of characters.
+
+ @param nPos position after that everything should be changed
+ @param nDiff amount how much the positions should be moved
+ */
+void SwWrongList::Move( sal_Int32 nPos, sal_Int32 nDiff )
+{
+ sal_uInt16 i = GetWrongPos( nPos );
+ if( nDiff < 0 )
+ {
+ const sal_Int32 nEnd = nPos - nDiff;
+ sal_uInt16 nLst = i;
+ bool bJump = false;
+ while( nLst < Count() && Pos( nLst ) < nEnd )
+ ++nLst;
+ if( nLst > i )
+ {
+ const sal_Int32 nWrPos = Pos( nLst - 1 );
+ if ( nWrPos <= nPos )
+ {
+ sal_Int32 nWrLen = Len( nLst - 1 );
+ // calculate new length of word
+ nWrLen = ( nEnd > nWrPos + nWrLen ) ?
+ nPos - nWrPos :
+ nWrLen + nDiff;
+ if( nWrLen )
+ {
+ maList[--nLst].mnLen = nWrLen;
+ bJump = true;
+ }
+ }
+ }
+ Remove( i, nLst - i );
+
+ if ( bJump )
+ ++i;
+ if( COMPLETE_STRING == GetBeginInv() )
+ SetInvalid( nPos ? nPos - 1 : nPos, nPos + 1 );
+ else
+ {
+ ShiftLeft( mnBeginInvalid, nPos, nEnd );
+ if( mnEndInvalid != COMPLETE_STRING )
+ ShiftLeft( mnEndInvalid, nPos, nEnd );
+ Invalidate_( nPos ? nPos - 1 : nPos, nPos + 1 );
+ }
+ }
+ else
+ {
+ const sal_Int32 nEnd = nPos + nDiff;
+ if( COMPLETE_STRING != GetBeginInv() )
+ {
+ if( mnBeginInvalid > nPos )
+ mnBeginInvalid += nDiff;
+ if( mnEndInvalid >= nPos && mnEndInvalid != COMPLETE_STRING )
+ mnEndInvalid += nDiff;
+ }
+ // If the pointer is in the middle of a wrong word,
+ // invalidation must happen from the beginning of that word.
+ if( i < Count() )
+ {
+ const sal_Int32 nWrPos = Pos( i );
+ if (nPos >= nWrPos)
+ {
+ Invalidate( nWrPos, nEnd );
+ const sal_Int32 nWrLen = Len( i ) + nDiff;
+ maList[i++].mnLen = nWrLen;
+ Invalidate( nWrPos, nWrPos + nWrLen );
+ }
+ }
+ else
+ Invalidate( nPos, nEnd );
+ }
+ while( i < Count() )
+ {
+ maList[i++].mnPos += nDiff;
+ }
+}
+
+// TODO: Complete documentation
+/** Remove given range of entries
+
+ For a given range [nPos, nPos + nLen[ and an index nIndex, this function
+ basically counts the number of SwWrongArea entries starting with nIndex
+ up to nPos + nLen. All these entries are removed.
+
+ @param rStart ???
+ @param rEnd ???
+ @param nPos starting position of the range
+ @param nLen length of the range
+ @param nIndex index to start lookup at
+ @param nCursorPos ???
+
+ @return <true> if ???
+ */
+auto SwWrongList::Fresh( sal_Int32 &rStart, sal_Int32 &rEnd, sal_Int32 nPos,
+ sal_Int32 nLen, sal_uInt16 nIndex, sal_Int32 nCursorPos ) -> FreshState
+{
+ // length of word must be greater than 0
+ // only report a spelling error if the cursor position is outside the word,
+ // so that the user is not annoyed while typing
+ FreshState eRet = nLen
+ ? (nCursorPos > nPos + nLen || nCursorPos < nPos)
+ ? FreshState::FRESH
+ : FreshState::CURSOR
+ : FreshState::NOTHING;
+
+ sal_Int32 nWrPos = 0;
+ sal_Int32 nWrEnd = rEnd;
+ sal_uInt16 nCnt = nIndex;
+ if( nCnt < Count() )
+ {
+ nWrPos = Pos( nCnt );
+ if( nWrPos < nPos && rStart > nWrPos )
+ rStart = nWrPos;
+ }
+
+ while( nCnt < Count() )
+ {
+ nWrPos = Pos( nCnt );
+ if ( nWrPos >= nPos )
+ break;
+ nWrEnd = nWrPos + Len( nCnt++ );
+ }
+
+ if( nCnt < Count() && nWrPos == nPos && Len( nCnt ) == nLen )
+ {
+ ++nCnt;
+ eRet = FreshState::FRESH;
+ }
+ else
+ {
+ if (FreshState::FRESH == eRet)
+ {
+ if( rStart > nPos )
+ rStart = nPos;
+ nWrEnd = nPos + nLen;
+ }
+ }
+
+ nPos += nLen;
+
+ if( nCnt < Count() )
+ {
+ nWrPos = Pos( nCnt );
+ if( nWrPos < nPos && rStart > nWrPos )
+ rStart = nWrPos;
+ }
+
+ while( nCnt < Count() )
+ {
+ nWrPos = Pos( nCnt );
+ if ( nWrPos >= nPos )
+ break;
+ nWrEnd = nWrPos + Len( nCnt++ );
+ }
+
+ if( rEnd < nWrEnd )
+ rEnd = nWrEnd;
+
+ Remove( nIndex, nCnt - nIndex );
+
+ return eRet;
+}
+
+void SwWrongList::Invalidate( sal_Int32 nBegin, sal_Int32 nEnd )
+{
+ if (COMPLETE_STRING == GetBeginInv())
+ SetInvalid( nBegin, nEnd );
+ else
+ Invalidate_( nBegin, nEnd );
+}
+
+bool SwWrongList::InvalidateWrong( )
+{
+ if( Count() )
+ {
+ const sal_Int32 nFirst = Pos( 0 );
+ const sal_Int32 nLast = Pos( Count() - 1 ) + Len( Count() - 1 );
+ Invalidate( nFirst, nLast );
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<SwWrongList> SwWrongList::SplitList( sal_Int32 nSplitPos )
+{
+ std::unique_ptr<SwWrongList> pRet;
+ sal_uInt16 nLst = 0;
+ while( nLst < Count() && Pos( nLst ) < nSplitPos )
+ ++nLst;
+ if( nLst )
+ {
+ sal_Int32 nWrPos = Pos( nLst - 1 );
+ sal_Int32 nWrLen = Len( nLst - 1 );
+ if ( nWrPos+nWrLen > nSplitPos )
+ {
+ nWrLen += nWrPos - nSplitPos;
+ maList[--nLst].mnPos = nSplitPos;
+ maList[nLst].mnLen = nWrLen;
+ }
+ }
+ if( nLst )
+ {
+ if( WRONGLIST_GRAMMAR == GetWrongListType() )
+ pRet.reset(new SwGrammarMarkUp());
+ else
+ pRet.reset(new SwWrongList( GetWrongListType() ));
+ pRet->Insert(0, maList.begin(), ( nLst >= maList.size() ? maList.end() : maList.begin() + nLst ) );
+ pRet->SetInvalid( GetBeginInv(), GetEndInv() );
+ pRet->Invalidate_( nSplitPos ? nSplitPos - 1 : nSplitPos, nSplitPos );
+ Remove( 0, nLst );
+ }
+ if( COMPLETE_STRING == GetBeginInv() )
+ SetInvalid( 0, 1 );
+ else
+ {
+ ShiftLeft( mnBeginInvalid, 0, nSplitPos );
+ if( mnEndInvalid != COMPLETE_STRING )
+ ShiftLeft( mnEndInvalid, 0, nSplitPos );
+ Invalidate_( 0, 1 );
+ }
+ for (nLst = 0; nLst < Count(); ++nLst )
+ {
+ maList[nLst].mnPos -= nSplitPos;
+ }
+ return pRet;
+}
+
+void SwWrongList::JoinList( SwWrongList* pNext, sal_Int32 nInsertPos )
+{
+ if (pNext)
+ {
+ OSL_ENSURE( GetWrongListType() == pNext->GetWrongListType(), "type mismatch with next list" );
+
+ sal_uInt16 nCnt = Count();
+ pNext->Move( 0, nInsertPos );
+ Insert(nCnt, pNext->maList.begin(), pNext->maList.end());
+
+ Invalidate( pNext->GetBeginInv(), pNext->GetEndInv() );
+ if( nCnt && Count() > nCnt )
+ {
+ sal_Int32 nWrPos = Pos( nCnt );
+ sal_Int32 nWrLen = Len( nCnt );
+ if( !nWrPos )
+ {
+ nWrPos += nInsertPos;
+ nWrLen -= nInsertPos;
+ maList[nCnt].mnPos = nWrPos;
+ maList[nCnt].mnLen = nWrLen;
+ }
+ if( nWrPos == Pos( nCnt - 1 ) + Len( nCnt - 1 ) )
+ {
+ nWrLen += Len( nCnt - 1 );
+ maList[nCnt - 1].mnLen = nWrLen;
+ Remove( nCnt, 1 );
+ }
+ }
+ }
+ Invalidate( nInsertPos ? nInsertPos - 1 : nInsertPos, nInsertPos + 1 );
+}
+
+void SwWrongList::InsertSubList( sal_Int32 nNewPos, sal_Int32 nNewLen, sal_uInt16 nWhere, SwWrongList* pSubList )
+{
+ if (pSubList)
+ {
+ OSL_ENSURE( GetWrongListType() == pSubList->GetWrongListType(), "type mismatch with sub list" );
+ }
+ std::vector<SwWrongArea>::iterator i = maList.begin();
+ if ( nWhere >= maList.size() )
+ i = maList.end(); // robust
+ else
+ i += nWhere;
+ maList.insert(i, SwWrongArea( OUString(), nullptr, nNewPos, nNewLen, pSubList ) );
+}
+
+// New functions: Necessary because SwWrongList has been changed to use std::vector
+void SwWrongList::Insert(sal_uInt16 nWhere, std::vector<SwWrongArea>::iterator startPos, std::vector<SwWrongArea>::iterator const & endPos)
+{
+ std::vector<SwWrongArea>::iterator i = maList.begin();
+ if ( nWhere >= maList.size() )
+ i = maList.end(); // robust
+ else
+ i += nWhere;
+ maList.insert(i, startPos, endPos); // insert [startPos, endPos[ before i
+
+ // ownership of the sublist is passed to maList, therefore we have to set the
+ // pSubList-Pointers to 0
+ while ( startPos != endPos )
+ {
+ (*startPos).mpSubList = nullptr;
+ ++startPos;
+ }
+}
+
+void SwWrongList::Remove(sal_uInt16 nIdx, sal_uInt16 nLen )
+{
+ if ( nIdx >= maList.size() ) return;
+ std::vector<SwWrongArea>::iterator i1 = maList.begin();
+ i1 += nIdx;
+
+ std::vector<SwWrongArea>::iterator i2 = i1;
+ if ( nIdx + nLen >= o3tl::narrowing<sal_uInt16>(maList.size()) )
+ i2 = maList.end(); // robust
+ else
+ i2 += nLen;
+
+ std::vector<SwWrongArea>::iterator iLoop = i1;
+ while ( iLoop != i2 )
+ {
+ delete (*iLoop).mpSubList;
+ ++iLoop;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ const int nOldSize = Count();
+#endif
+
+ maList.erase(i1, i2);
+
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( Count() + nLen == nOldSize, "SwWrongList::Remove() trouble" );
+#endif
+}
+
+void SwWrongList::RemoveEntry( sal_Int32 nBegin, sal_Int32 nEnd ) {
+ std::vector<SwWrongArea>::const_iterator aEnd(maList.end());
+ auto aDelIter = std::find_if(maList.cbegin(), aEnd,
+ [nBegin](const SwWrongArea& rST) { return rST.mnPos >= nBegin; });
+ auto aIter = aDelIter;
+ if( WRONGLIST_GRAMMAR == GetWrongListType() )
+ {
+ if( nBegin < nEnd )
+ {
+ aIter = std::find_if(aDelIter, aEnd,
+ [nEnd](const SwWrongArea& rST) { return rST.mnPos >= nEnd; });
+ }
+ }
+ else
+ {
+ aIter = std::find_if(aDelIter, aEnd,
+ [nBegin, nEnd](const SwWrongArea& rST) {
+ return (rST.mnPos != nBegin) || ((rST.mnPos + rST.mnLen) != nEnd);
+ });
+ }
+ auto nDel = o3tl::narrowing<sal_uInt16>(std::distance(aDelIter, aIter));
+ if( nDel )
+ {
+ auto nDelPos = o3tl::narrowing<sal_uInt16>(std::distance(maList.cbegin(), aDelIter));
+ Remove( nDelPos, nDel );
+ }
+}
+
+bool SwWrongList::LookForEntry( sal_Int32 nBegin, sal_Int32 nEnd ) {
+ auto aIter = std::find_if(maList.begin(), maList.end(),
+ [nBegin](const SwWrongArea& rST) { return rST.mnPos >= nBegin; });
+ return aIter != maList.end()
+ && nBegin == (*aIter).mnPos
+ && nEnd == (*aIter).mnPos + (*aIter).mnLen;
+}
+
+void SwWrongList::Insert( const OUString& rType,
+ css::uno::Reference< css::container::XStringKeyMap > const & xPropertyBag,
+ sal_Int32 nNewPos, sal_Int32 nNewLen )
+{
+ auto aIter = std::find_if(maList.begin(), maList.end(),
+ [nNewPos](const SwWrongArea& rST) { return nNewPos <= rST.mnPos; });
+ if ( aIter != maList.end() && nNewPos == (*aIter).mnPos )
+ {
+ const sal_Int32 nSTPos = (*aIter).mnPos;
+
+ aIter = std::find_if(aIter, maList.end(),
+ [nSTPos, nNewLen](const SwWrongArea& rST) { return rST.mnPos != nSTPos || nNewLen < rST.mnLen; });
+ }
+
+ maList.insert(aIter, SwWrongArea( rType, meType, xPropertyBag, nNewPos, nNewLen) );
+}
+
+namespace sw {
+
+WrongListIteratorBase::WrongListIteratorBase(SwTextFrame const& rFrame,
+ SwWrongList const* (SwTextNode::*pGetWrongList)() const)
+ : m_pGetWrongList(pGetWrongList)
+ , m_pMergedPara(rFrame.GetMergedPara())
+ , m_CurrentExtent(0)
+ , m_CurrentIndex(0)
+ , m_pWrongList(m_pMergedPara
+ ? nullptr
+ : (rFrame.GetTextNodeFirst()->*pGetWrongList)())
+{
+}
+
+WrongListIteratorBase::WrongListIteratorBase(SwWrongList const& rWrongList)
+ : m_pGetWrongList(nullptr)
+ , m_pMergedPara(nullptr)
+ , m_CurrentExtent(0)
+ , m_CurrentIndex(0)
+ , m_pWrongList(&rWrongList)
+{
+}
+
+WrongListIterator::WrongListIterator(SwTextFrame const& rFrame,
+ SwWrongList const* (SwTextNode::*pGetWrongList)() const)
+ : WrongListIteratorBase(rFrame, pGetWrongList)
+{
+}
+
+WrongListIterator::WrongListIterator(SwWrongList const& rWrongList)
+ : WrongListIteratorBase(rWrongList)
+{
+}
+
+bool WrongListIterator::Check(TextFrameIndex & rStart, TextFrameIndex & rLen)
+{
+ if (m_pMergedPara)
+ {
+ if (rStart < m_CurrentIndex)
+ { // rewind
+ m_CurrentExtent = 0;
+ m_CurrentIndex = TextFrameIndex(0);
+ }
+ while (m_CurrentExtent < m_pMergedPara->extents.size())
+ {
+ sw::Extent const& rExtent(m_pMergedPara->extents[m_CurrentExtent]);
+ if (rStart + rLen <= m_CurrentIndex)
+ {
+ return false;
+ }
+ else if (rStart < m_CurrentIndex)
+ {
+ rLen -= m_CurrentIndex - rStart;
+ assert(0 < sal_Int32(rLen));
+ rStart = m_CurrentIndex;
+ }
+ if (m_CurrentIndex <= rStart &&
+ rStart < m_CurrentIndex + TextFrameIndex(rExtent.nEnd - rExtent.nStart))
+ {
+ SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
+ // found the extent containing start - first, call Check
+ sal_Int32 nStart(rExtent.nStart + sal_Int32(rStart - m_CurrentIndex)); // (m_CurrentIndex - m_CurrentNodeIndex));
+ sal_Int32 nLen;
+ if (sal_Int32(rLen) < rExtent.nEnd - nStart)
+ {
+ nLen = sal_Int32(rLen);
+ }
+ else
+ {
+ sal_Int32 nInLen(rLen);
+ nLen = rExtent.nEnd - nStart;
+ nInLen -= nLen;
+ for (size_t i = m_CurrentExtent + 1;
+ i < m_pMergedPara->extents.size(); ++i)
+ {
+ sw::Extent const& rExtentEnd(m_pMergedPara->extents[i]);
+ if (rExtentEnd.pNode != rExtent.pNode)
+ {
+ break;
+ }
+ // add gap too
+ nLen += rExtentEnd.nStart - m_pMergedPara->extents[i-1].nEnd;
+ if (nInLen <= rExtentEnd.nEnd - rExtentEnd.nStart)
+ {
+ nLen += nInLen;
+ break;
+ }
+ nLen += rExtentEnd.nEnd - rExtentEnd.nStart;
+ nInLen -= rExtentEnd.nEnd - rExtentEnd.nStart;
+ }
+ }
+ if (pWrongList && pWrongList->Check(nStart, nLen))
+ {
+ // check if there's overlap with this extent
+ if (rExtent.nStart <= nStart && nStart < rExtent.nEnd)
+ {
+ // yes - now compute end position / length
+ sal_Int32 const nEnd(nStart + nLen);
+ rStart = m_CurrentIndex + TextFrameIndex(nStart - rExtent.nStart);
+ TextFrameIndex const nOrigLen(rLen);
+ if (nEnd <= rExtent.nEnd)
+ {
+ rLen = TextFrameIndex(nEnd - nStart);
+ }
+ else // have to search other extents for the end...
+ {
+ rLen = TextFrameIndex(rExtent.nEnd - nStart);
+ for (size_t i = m_CurrentExtent + 1;
+ i < m_pMergedPara->extents.size(); ++i)
+ {
+ sw::Extent const& rExtentEnd(m_pMergedPara->extents[i]);
+ if (rExtentEnd.pNode != rExtent.pNode
+ || nEnd <= rExtentEnd.nStart)
+ {
+ break;
+ }
+ if (nEnd <= rExtentEnd.nEnd)
+ {
+ rLen += TextFrameIndex(nEnd - rExtentEnd.nStart);
+ break;
+ }
+ rLen += TextFrameIndex(rExtentEnd.nEnd - rExtentEnd.nStart);
+ }
+ }
+ assert(rLen <= nOrigLen); (void) nOrigLen;
+ return true;
+ }
+ }
+ }
+ m_CurrentIndex += TextFrameIndex(rExtent.nEnd - rExtent.nStart);
+ ++m_CurrentExtent;
+ }
+ return false;
+ }
+ else if (m_pWrongList)
+ {
+ sal_Int32 nStart(rStart);
+ sal_Int32 nLen(rLen);
+ bool const bRet(m_pWrongList->Check(nStart, nLen));
+ rStart = TextFrameIndex(nStart);
+ rLen = TextFrameIndex(nLen);
+ return bRet;
+ }
+ return false;
+}
+
+const SwWrongArea*
+WrongListIterator::GetWrongElement(TextFrameIndex const nStart)
+{
+ if (m_pMergedPara)
+ {
+ if (nStart < m_CurrentIndex)
+ { // rewind
+ m_CurrentExtent = 0;
+ m_CurrentIndex = TextFrameIndex(0);
+ }
+ while (m_CurrentExtent < m_pMergedPara->extents.size())
+ {
+ sw::Extent const& rExtent(m_pMergedPara->extents[m_CurrentExtent]);
+ if (m_CurrentIndex <= nStart &&
+ nStart <= m_CurrentIndex + TextFrameIndex(rExtent.nEnd - rExtent.nStart))
+ {
+ // note: the returned object isn't wrapped because fntcache.cxx
+ // does not look at its positions, only its formatting props
+ SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
+ if (pWrongList)
+ {
+ sal_Int32 const nNStart(rExtent.nStart + sal_Int32(nStart - m_CurrentIndex)); // (m_CurrentIndex - m_CurrentNodeIndex));
+ sal_Int16 const nPos(pWrongList->GetWrongPos(nNStart));
+ return pWrongList->GetElement(nPos);
+ }
+ }
+ m_CurrentIndex += TextFrameIndex(rExtent.nEnd - rExtent.nStart);
+ ++m_CurrentExtent;
+ }
+ return nullptr;
+ }
+ else if (m_pWrongList)
+ {
+ sal_Int16 const nPos(m_pWrongList->GetWrongPos(sal_Int32(nStart)));
+ return m_pWrongList->GetElement(nPos);
+ }
+ return nullptr;
+}
+
+WrongListIteratorCounter::WrongListIteratorCounter(SwTextFrame const& rFrame,
+ SwWrongList const* (SwTextNode::*pGetWrongList)() const)
+ : WrongListIteratorBase(rFrame, pGetWrongList)
+{
+}
+
+WrongListIteratorCounter::WrongListIteratorCounter(SwWrongList const& rWrongList)
+ : WrongListIteratorBase(rWrongList)
+{
+}
+
+sal_uInt16 WrongListIteratorCounter::GetElementCount()
+{
+ if (m_pMergedPara)
+ {
+ sal_uInt16 nRet(0);
+ m_CurrentExtent = 0;
+ m_CurrentIndex = TextFrameIndex(0);
+ SwNode const* pNode(nullptr);
+ sal_uInt16 InCurrentNode(0);
+ while (m_CurrentExtent < m_pMergedPara->extents.size())
+ {
+ sw::Extent const& rExtent(m_pMergedPara->extents[m_CurrentExtent]);
+ if (rExtent.pNode != pNode)
+ {
+ InCurrentNode = 0;
+ pNode = rExtent.pNode;
+ }
+ SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
+ for (; pWrongList && InCurrentNode < pWrongList->Count(); ++InCurrentNode)
+ {
+ SwWrongArea const*const pWrong(pWrongList->GetElement(InCurrentNode));
+ TextFrameIndex const nExtentEnd(
+ m_CurrentIndex + TextFrameIndex(rExtent.nEnd - rExtent.nStart));
+ if (nExtentEnd <= TextFrameIndex(pWrong->mnPos))
+ {
+ break; // continue outer loop
+ }
+ if (m_CurrentIndex < TextFrameIndex(pWrong->mnPos + pWrong->mnLen))
+ {
+ ++nRet;
+ }
+ }
+ m_CurrentIndex += TextFrameIndex(rExtent.nEnd - rExtent.nStart);
+ ++m_CurrentExtent;
+ }
+ return nRet;
+ }
+ else if (m_pWrongList)
+ {
+ return m_pWrongList->Count();
+ }
+ return 0;
+}
+
+std::optional<std::pair<TextFrameIndex, TextFrameIndex>>
+WrongListIteratorCounter::GetElementAt(sal_uInt16 nIndex)
+{
+ if (m_pMergedPara)
+ {
+ m_CurrentExtent = 0;
+ m_CurrentIndex = TextFrameIndex(0);
+ SwNode const* pNode(nullptr);
+ sal_uInt16 InCurrentNode(0);
+ while (m_CurrentExtent < m_pMergedPara->extents.size())
+ {
+ sw::Extent const& rExtent(m_pMergedPara->extents[m_CurrentExtent]);
+ if (rExtent.pNode != pNode)
+ {
+ InCurrentNode = 0;
+ pNode = rExtent.pNode;
+ }
+ SwWrongList const*const pWrongList((rExtent.pNode->*m_pGetWrongList)());
+ for (; pWrongList && InCurrentNode < pWrongList->Count(); ++InCurrentNode)
+ {
+ SwWrongArea const*const pWrong(pWrongList->GetElement(InCurrentNode));
+ TextFrameIndex const nExtentEnd(
+ m_CurrentIndex + TextFrameIndex(rExtent.nEnd - rExtent.nStart));
+ if (nExtentEnd <= TextFrameIndex(pWrong->mnPos))
+ {
+ break; // continue outer loop
+ }
+ if (m_CurrentIndex < TextFrameIndex(pWrong->mnPos + pWrong->mnLen))
+ {
+ if (nIndex == 0)
+ {
+ return std::optional<std::pair<TextFrameIndex, TextFrameIndex>>(
+ std::pair<TextFrameIndex, TextFrameIndex>(
+ m_CurrentIndex - TextFrameIndex(rExtent.nStart -
+ std::max(rExtent.nStart, pWrong->mnPos)),
+ m_CurrentIndex - TextFrameIndex(rExtent.nStart -
+ std::min(pWrong->mnPos + pWrong->mnLen, rExtent.nEnd))));
+ }
+ --nIndex;
+ }
+ }
+ m_CurrentIndex += TextFrameIndex(rExtent.nEnd - rExtent.nStart);
+ ++m_CurrentExtent;
+ }
+ return std::optional<std::pair<TextFrameIndex, TextFrameIndex>>();
+ }
+ else if (m_pWrongList)
+ {
+ SwWrongArea const*const pWrong(m_pWrongList->GetElement(nIndex));
+ return std::optional<std::pair<TextFrameIndex, TextFrameIndex>>(
+ std::pair<TextFrameIndex, TextFrameIndex>(
+ TextFrameIndex(pWrong->mnPos),
+ TextFrameIndex(pWrong->mnPos + pWrong->mnLen)));
+ }
+ return std::optional<std::pair<TextFrameIndex, TextFrameIndex>>();
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx
new file mode 100644
index 000000000..9c8a5ba91
--- /dev/null
+++ b/sw/source/core/text/xmldump.cxx
@@ -0,0 +1,678 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <frame.hxx>
+#include <frmfmt.hxx>
+#include <ftnfrm.hxx>
+#include <sectfrm.hxx>
+#include <tabfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtfrm.hxx>
+#include <cellfrm.hxx>
+#include <flyfrm.hxx>
+#include <hffrm.hxx>
+#include <rootfrm.hxx>
+#include <ndtxt.hxx>
+#include <sortedobjs.hxx>
+#include <swfont.hxx>
+#include <txttypes.hxx>
+#include <anchoredobject.hxx>
+#include <libxml/xmlwriter.h>
+#include <SwPortionHandler.hxx>
+#include <view.hxx>
+#include <svx/svdobj.hxx>
+
+#include "porlay.hxx"
+
+const char* sw::PortionTypeToString(PortionType nType)
+{
+ switch (nType)
+ {
+ case PortionType::NONE:
+ return "PortionType::NONE";
+ case PortionType::FlyCnt:
+ return "PortionType::FlyCnt";
+
+ case PortionType::Hole:
+ return "PortionType::Hole";
+ case PortionType::TempEnd:
+ return "PortionType::TempEnd";
+ case PortionType::Break:
+ return "PortionType::Break";
+ case PortionType::Kern:
+ return "PortionType::Kern";
+ case PortionType::Arrow:
+ return "PortionType::Arrow";
+ case PortionType::Multi:
+ return "PortionType::Multi";
+ case PortionType::HiddenText:
+ return "PortionType::HiddenText";
+ case PortionType::ControlChar:
+ return "PortionType::ControlChar";
+ case PortionType::Bookmark:
+ return "PortionType::Bookmark";
+
+ case PortionType::Text:
+ return "PortionType::Text";
+ case PortionType::Lay:
+ return "PortionType::Lay";
+ case PortionType::Para:
+ return "PortionType::Para";
+ case PortionType::Hanging:
+ return "PortionType::Hanging";
+
+ case PortionType::Drop:
+ return "PortionType::Drop";
+ case PortionType::Tox:
+ return "PortionType::Tox";
+ case PortionType::IsoTox:
+ return "PortionType::IsoTox";
+ case PortionType::Ref:
+ return "PortionType::Ref";
+ case PortionType::IsoRef:
+ return "PortionType::IsoRef";
+ case PortionType::Meta:
+ return "PortionType::Meta";
+ case PortionType::ContentControl:
+ return "PortionType::ContentControl";
+ case PortionType::FieldMark:
+ return "PortionType::FieldMark";
+ case PortionType::FieldFormCheckbox:
+ return "PortionType::FieldFormCheckbox";
+ case PortionType::InputField:
+ return "PortionType::InputField";
+
+ case PortionType::Expand:
+ return "PortionType::Expand";
+ case PortionType::Blank:
+ return "PortionType::Blank";
+ case PortionType::PostIts:
+ return "PortionType::PostIts";
+
+ case PortionType::Hyphen:
+ return "PortionType::Hyphen";
+ case PortionType::HyphenStr:
+ return "PortionType::HyphenStr";
+ case PortionType::SoftHyphen:
+ return "PortionType::SoftHyphen";
+ case PortionType::SoftHyphenStr:
+ return "PortionType::SoftHyphenStr";
+ case PortionType::SoftHyphenComp:
+ return "PortionType::SoftHyphenComp";
+
+ case PortionType::Field:
+ return "PortionType::Field";
+ case PortionType::Hidden:
+ return "PortionType::Hidden";
+ case PortionType::QuoVadis:
+ return "PortionType::QuoVadis";
+ case PortionType::ErgoSum:
+ return "PortionType::ErgoSum";
+ case PortionType::Combined:
+ return "PortionType::Combined";
+ case PortionType::Footnote:
+ return "PortionType::Footnote";
+
+ case PortionType::FootnoteNum:
+ return "PortionType::FootnoteNum";
+ case PortionType::Number:
+ return "PortionType::Number";
+ case PortionType::Bullet:
+ return "PortionType::Bullet";
+ case PortionType::GrfNum:
+ return "PortionType::GrfNum";
+
+ case PortionType::Glue:
+ return "PortionType::Glue";
+
+ case PortionType::Margin:
+ return "PortionType::Margin";
+
+ case PortionType::Fix:
+ return "PortionType::Fix";
+ case PortionType::Fly:
+ return "PortionType::Fly";
+
+ case PortionType::Tab:
+ return "PortionType::Tab";
+
+ case PortionType::TabRight:
+ return "PortionType::TabRight";
+ case PortionType::TabCenter:
+ return "PortionType::TabCenter";
+ case PortionType::TabDecimal:
+ return "PortionType::TabDecimal";
+
+ case PortionType::TabLeft:
+ return "PortionType::TabLeft";
+ default:
+ return "Unknown";
+ }
+}
+
+namespace {
+
+class XmlPortionDumper:public SwPortionHandler
+{
+ private:
+ xmlTextWriterPtr m_Writer;
+ TextFrameIndex m_Ofs;
+ const OUString& m_rText;
+ OUString m_aLine;
+
+ public:
+ explicit XmlPortionDumper(xmlTextWriterPtr some_writer, const OUString& rText)
+ : m_Writer(some_writer)
+ , m_Ofs(0)
+ , m_rText(rText)
+ {
+ }
+
+ /**
+ @param nLength
+ length of this portion in the model string
+ @param rText
+ text which is painted on-screen
+ */
+ virtual void Text( TextFrameIndex nLength,
+ PortionType nType,
+ sal_Int32 nHeight,
+ sal_Int32 nWidth) override
+ {
+ (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Text"));
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nLength"), "%i",
+ static_cast<int>(static_cast<sal_Int32>(nLength)));
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nType"), "%s",
+ sw::PortionTypeToString(nType));
+ if (nHeight > 0)
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nHeight"), "%i",
+ static_cast<int>(nHeight));
+ if (nWidth > 0)
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nWidth"), "%i",
+ static_cast<int>(nWidth));
+ if (nLength > TextFrameIndex(0))
+ (void)xmlTextWriterWriteAttribute(
+ m_Writer, BAD_CAST("Portion"),
+ BAD_CAST(m_rText.copy(sal_Int32(m_Ofs), sal_Int32(nLength)).toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(m_Writer);
+ m_aLine += m_rText.subView(sal_Int32(m_Ofs), sal_Int32(nLength));
+ m_Ofs += nLength;
+ }
+
+ /**
+ @param nLength
+ length of this portion in the model string
+ @param rText
+ text which is painted on-screen
+ @param nType
+ type of this portion
+ @param nHeight
+ font size of the painted text
+ */
+ virtual void Special( TextFrameIndex nLength,
+ const OUString & rText,
+ PortionType nType,
+ sal_Int32 nHeight,
+ sal_Int32 nWidth,
+ const SwFont* pFont ) override
+ {
+ (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Special"));
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nLength"), "%i",
+ static_cast<int>(static_cast<sal_Int32>(nLength)));
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nType"), "%s",
+ sw::PortionTypeToString(nType));
+ OString sText8 = OUStringToOString( rText, RTL_TEXTENCODING_UTF8 );
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("rText"), "%s", sText8.getStr());
+
+ if (nHeight > 0)
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nHeight"), "%i",
+ static_cast<int>(nHeight));
+
+ if (nWidth > 0)
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nWidth"), "%i",
+ static_cast<int>(nWidth));
+
+ if (pFont)
+ pFont->dumpAsXml(m_Writer);
+
+ (void)xmlTextWriterEndElement(m_Writer);
+ m_aLine += rText;
+ m_Ofs += nLength;
+ }
+
+ virtual void LineBreak( sal_Int32 nWidth ) override
+ {
+ (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("LineBreak"));
+ if (nWidth > 0)
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nWidth"), "%i",
+ static_cast<int>(nWidth));
+ if (!m_aLine.isEmpty())
+ {
+ (void)xmlTextWriterWriteAttribute(m_Writer, BAD_CAST("Line"),
+ BAD_CAST(m_aLine.toUtf8().getStr()));
+ m_aLine.clear();
+ }
+ (void)xmlTextWriterEndElement(m_Writer);
+ }
+
+ /**
+ * @param nLength
+ * number of 'model string' characters to be skipped
+ */
+ virtual void Skip( TextFrameIndex nLength ) override
+ {
+ (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Skip"));
+ (void)xmlTextWriterWriteFormatAttribute(m_Writer, BAD_CAST("nLength"), "%i",
+ static_cast<int>(static_cast<sal_Int32>(nLength)));
+ (void)xmlTextWriterEndElement(m_Writer);
+ m_Ofs += nLength;
+ }
+
+ virtual void Finish( ) override
+ {
+ (void)xmlTextWriterStartElement(m_Writer, BAD_CAST("Finish"));
+ (void)xmlTextWriterEndElement(m_Writer);
+ }
+
+};
+
+ xmlTextWriterPtr lcl_createDefaultWriter()
+ {
+ xmlTextWriterPtr writer = xmlNewTextWriterFilename( "layout.xml", 0 );
+ xmlTextWriterSetIndent(writer,1);
+ (void)xmlTextWriterSetIndentString(writer, BAD_CAST(" "));
+ (void)xmlTextWriterStartDocument( writer, nullptr, nullptr, nullptr );
+ return writer;
+ }
+
+ void lcl_freeWriter( xmlTextWriterPtr writer )
+ {
+ (void)xmlTextWriterEndDocument( writer );
+ xmlFreeTextWriter( writer );
+ }
+}
+
+void SwFrame::dumpTopMostAsXml(xmlTextWriterPtr writer) const
+{
+ const SwFrame* pFrame = this;
+ while (pFrame->GetUpper())
+ {
+ pFrame = pFrame->GetUpper();
+ }
+
+ pFrame->dumpAsXml(writer);
+}
+
+void SwFrame::dumpAsXml( xmlTextWriterPtr writer ) const
+{
+ bool bCreateWriter = ( nullptr == writer );
+ if ( bCreateWriter )
+ writer = lcl_createDefaultWriter();
+
+ const char *name = nullptr;
+
+ switch ( GetType( ) )
+ {
+ case SwFrameType::Root:
+ name = "root";
+ break;
+ case SwFrameType::Page:
+ name = "page";
+ break;
+ case SwFrameType::Column:
+ name = "column";
+ break;
+ case SwFrameType::Header:
+ name = "header";
+ break;
+ case SwFrameType::Footer:
+ name = "footer";
+ break;
+ case SwFrameType::FtnCont:
+ name = "ftncont";
+ break;
+ case SwFrameType::Ftn:
+ name = "ftn";
+ break;
+ case SwFrameType::Body:
+ name = "body";
+ break;
+ case SwFrameType::Fly:
+ name = "fly";
+ break;
+ case SwFrameType::Section:
+ name = "section";
+ break;
+ case SwFrameType::Tab:
+ name = "tab";
+ break;
+ case SwFrameType::Row:
+ name = "row";
+ break;
+ case SwFrameType::Cell:
+ name = "cell";
+ break;
+ case SwFrameType::Txt:
+ name = "txt";
+ break;
+ case SwFrameType::NoTxt:
+ name = "notxt";
+ break;
+ default: break;
+ }
+
+ if ( name != nullptr )
+ {
+ (void)xmlTextWriterStartElement( writer, reinterpret_cast<const xmlChar *>(name) );
+
+ dumpAsXmlAttributes( writer );
+
+ if (IsRootFrame())
+ {
+ const SwRootFrame* pRootFrame = static_cast<const SwRootFrame*>(this);
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("sfxViewShells"));
+ SwView* pView = static_cast<SwView*>(SfxViewShell::GetFirst(true, checkSfxViewShell<SwView>));
+ while (pView)
+ {
+ if (pRootFrame->GetCurrShell()->GetSfxViewShell() && pView->GetObjectShell() == pRootFrame->GetCurrShell()->GetSfxViewShell()->GetObjectShell())
+ pView->dumpAsXml(writer);
+ pView = static_cast<SwView*>(SfxViewShell::GetNext(*pView, true, checkSfxViewShell<SwView>));
+ }
+ (void)xmlTextWriterEndElement(writer);
+ }
+
+ if (IsPageFrame())
+ {
+ const SwPageFrame* pPageFrame = static_cast<const SwPageFrame*>(this);
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("page_status"));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyLayout()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyContent()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidFlyInCnt"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidFlyInCnt()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidLayout"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidLayout()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("ValidContent"), BAD_CAST(OString::boolean(!pPageFrame->IsInvalidContent()).getStr()));
+ (void)xmlTextWriterEndElement(writer);
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("page_info"));
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("phyNum"), "%d", pPageFrame->GetPhyPageNum());
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("virtNum"), "%d", pPageFrame->GetVirtPageNum());
+ OUString aFormatName = pPageFrame->GetPageDesc()->GetName();
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("pageDesc"), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr()));
+ (void)xmlTextWriterEndElement(writer);
+ if (auto const* pObjs = pPageFrame->GetSortedObjs())
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("sorted_objs"));
+ for (SwAnchoredObject const*const pObj : *pObjs)
+ { // just print pointer, full details will be printed on its anchor frame
+ // this nonsense is needed because of multiple inheritance
+ if (SwFlyFrame const* pFly = pObj->DynCastFlyFrame())
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("fly"));
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pFly);
+ }
+ else
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST(pObj->getElementName()));
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", pObj);
+ }
+ (void)xmlTextWriterEndElement(writer);
+ }
+ (void)xmlTextWriterEndElement(writer);
+ }
+ }
+
+ if (IsTextFrame())
+ {
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this);
+ sw::MergedPara const*const pMerged(pTextFrame->GetMergedPara());
+ if (pMerged)
+ {
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "merged" ) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "paraPropsNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(pMerged->pParaPropsNode->GetIndex()) );
+ for (auto const& e : pMerged->extents)
+ {
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "extent" ) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(e.pNode->GetIndex()) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "start" ), "%" SAL_PRIdINT32, e.nStart );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "end" ), "%" SAL_PRIdINT32, e.nEnd );
+ (void)xmlTextWriterEndElement( writer );
+ }
+ (void)xmlTextWriterEndElement( writer );
+ }
+ }
+
+ if (IsCellFrame())
+ {
+ SwCellFrame const* pCellFrame(static_cast<SwCellFrame const*>(this));
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "rowspan" ), "%ld", pCellFrame->GetLayoutRowSpan() );
+ }
+
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "infos" ) );
+ dumpInfosAsXml( writer );
+ (void)xmlTextWriterEndElement( writer );
+
+ // Dump Anchored objects if any
+ const SwSortedObjs* pAnchored = GetDrawObjs();
+ if ( pAnchored && pAnchored->size() > 0 )
+ {
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "anchored" ) );
+
+ for (SwAnchoredObject* pObject : *pAnchored)
+ {
+ pObject->dumpAsXml( writer );
+ }
+
+ (void)xmlTextWriterEndElement( writer );
+ }
+
+ // Dump the children
+ if ( IsTextFrame( ) )
+ {
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this);
+ OUString aText = pTextFrame->GetText( );
+ for ( int i = 0; i < 32; i++ )
+ {
+ aText = aText.replace( i, '*' );
+ }
+ OString aText8 =OUStringToOString( aText,
+ RTL_TEXTENCODING_UTF8 );
+ (void)xmlTextWriterWriteString( writer,
+ reinterpret_cast<const xmlChar *>(aText8.getStr( )) );
+ XmlPortionDumper pdumper( writer, aText );
+ pTextFrame->VisitPortions( pdumper );
+ if (const SwParaPortion* pPara = pTextFrame->GetPara())
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("SwParaPortion"));
+ TextFrameIndex nOffset(0);
+ const OUString& rText = pTextFrame->GetText();
+ pPara->dumpAsXmlAttributes(writer, rText, nOffset);
+ const SwLineLayout* pLine = pPara;
+ if (pTextFrame->IsFollow())
+ {
+ nOffset += pTextFrame->GetOffset();
+ }
+ while (pLine)
+ {
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("SwLineLayout"));
+ pLine->dumpAsXmlAttributes(writer, rText, nOffset);
+ const SwLinePortion* pPor = pLine->GetFirstPortion();
+ while (pPor)
+ {
+ pPor->dumpAsXml(writer, rText, nOffset);
+ pPor = pPor->GetNextPortion();
+ }
+ (void)xmlTextWriterEndElement(writer);
+ pLine = pLine->GetNext();
+ }
+ (void)xmlTextWriterEndElement(writer);
+ }
+
+ }
+ else
+ {
+ dumpChildrenAsXml( writer );
+ }
+ (void)xmlTextWriterEndElement( writer );
+ }
+
+ if ( bCreateWriter )
+ lcl_freeWriter( writer );
+}
+
+void SwFrame::dumpInfosAsXml( xmlTextWriterPtr writer ) const
+{
+ // output the Frame
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) );
+ getFrameArea().dumpAsXmlAttributes(writer);
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFixSize"), BAD_CAST(OString::boolean(HasFixSize()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFrameAreaPositionValid"), BAD_CAST(OString::boolean(isFrameAreaPositionValid()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFrameAreaSizeValid"), BAD_CAST(OString::boolean(isFrameAreaSizeValid()).getStr()));
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("mbFramePrintAreaValid"), BAD_CAST(OString::boolean(isFramePrintAreaValid()).getStr()));
+ (void)xmlTextWriterEndElement( writer );
+
+ // output the print area
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "prtBounds" ) );
+ getFramePrintArea().dumpAsXmlAttributes(writer);
+ (void)xmlTextWriterEndElement( writer );
+}
+
+void SwFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
+{
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "id" ), "%" SAL_PRIuUINT32, GetFrameId() );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "symbol" ), "%s", BAD_CAST( typeid( *this ).name( ) ) );
+ if ( GetNext( ) )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "next" ), "%" SAL_PRIuUINT32, GetNext()->GetFrameId() );
+ if ( GetPrev( ) )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "prev" ), "%" SAL_PRIuUINT32, GetPrev()->GetFrameId() );
+ if ( GetUpper( ) )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "upper" ), "%" SAL_PRIuUINT32, GetUpper()->GetFrameId() );
+ if ( GetLower( ) )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "lower" ), "%" SAL_PRIuUINT32, GetLower()->GetFrameId() );
+ if (IsFootnoteFrame())
+ {
+ SwFootnoteFrame const*const pFF(static_cast<SwFootnoteFrame const*>(this));
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("ref"), "%" SAL_PRIuUINT32, pFF->GetRef()->GetFrameId() );
+ if (pFF->GetMaster())
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("master"), "%" SAL_PRIuUINT32, pFF->GetMaster()->GetFrameId() );
+ if (pFF->GetFollow())
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST("follow"), "%" SAL_PRIuUINT32, pFF->GetFollow()->GetFrameId() );
+ }
+ if (IsSctFrame())
+ {
+ SwSectionFrame const*const pFrame(static_cast<SwSectionFrame const*>(this));
+ SwSectionNode const*const pNode(pFrame->GetSection() ? pFrame->GetSection()->GetFormat()->GetSectionNode() : nullptr);
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("sectionNodeIndex"), "%" SAL_PRIdINT32, pNode ? sal_Int32(pNode->GetIndex()) : -1);
+ }
+ if ( IsTextFrame( ) )
+ {
+ const SwTextFrame *pTextFrame = static_cast<const SwTextFrame *>(this);
+ const SwTextNode *pTextNode = pTextFrame->GetTextNodeFirst();
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "txtNodeIndex" ), "%" SAL_PRIdINT32, sal_Int32(pTextNode->GetIndex()) );
+
+ OString aMode = "Horizontal";
+ if (IsVertLRBT())
+ {
+ aMode = "VertBTLR";
+ }
+ else if (IsVertLR())
+ {
+ aMode = "VertLR";
+ }
+ else if (IsVertical())
+ {
+ aMode = "Vertical";
+ }
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("WritingMode"), BAD_CAST(aMode.getStr()));
+ }
+ if (IsHeaderFrame() || IsFooterFrame())
+ {
+ const SwHeadFootFrame *pHeadFootFrame = static_cast<const SwHeadFootFrame*>(this);
+ OUString aFormatName = pHeadFootFrame->GetFormat()->GetName();
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtName" ), "%s", BAD_CAST(OUStringToOString(aFormatName, RTL_TEXTENCODING_UTF8).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "fmtPtr" ), "%p", pHeadFootFrame->GetFormat());
+ }
+}
+
+void SwFrame::dumpChildrenAsXml( xmlTextWriterPtr writer ) const
+{
+ const SwFrame *pFrame = GetLower( );
+ for ( ; pFrame != nullptr; pFrame = pFrame->GetNext( ) )
+ {
+ pFrame->dumpAsXml( writer );
+ }
+}
+
+void SwAnchoredObject::dumpAsXml( xmlTextWriterPtr writer ) const
+{
+ bool bCreateWriter = ( nullptr == writer );
+ if ( bCreateWriter )
+ writer = lcl_createDefaultWriter();
+
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( getElementName() ) );
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "ptr" ), "%p", this );
+
+ (void)xmlTextWriterStartElement( writer, BAD_CAST( "bounds" ) );
+ // don't call GetObjBoundRect(), it modifies the layout
+ SwRect(GetDrawObj()->GetLastBoundRect()).dumpAsXmlAttributes(writer);
+ (void)xmlTextWriterEndElement( writer );
+
+ if (const SdrObject* pObject = GetDrawObj())
+ pObject->dumpAsXml(writer);
+
+ (void)xmlTextWriterEndElement( writer );
+
+ if ( bCreateWriter )
+ lcl_freeWriter( writer );
+}
+
+void SwFont::dumpAsXml(xmlTextWriterPtr writer) const
+{
+ (void)xmlTextWriterStartElement(writer, BAD_CAST("SwFont"));
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("ptr"), "%p", this);
+ // do not use Color::AsRGBHexString() as that omits the transparency
+ (void)xmlTextWriterWriteFormatAttribute(writer, BAD_CAST("color"), "%08" SAL_PRIxUINT32, sal_uInt32(GetColor()));
+ {
+ std::stringstream ss;
+ ss << GetWeight();
+ (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("weight"), BAD_CAST(ss.str().c_str()));
+ }
+ (void)xmlTextWriterEndElement(writer);
+}
+
+void SwTextFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
+{
+ SwFrame::dumpAsXmlAttributes( writer );
+ if ( HasFollow() )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
+
+ if (m_pPrecede != nullptr)
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTextFrame*>(m_pPrecede)->GetFrameId() );
+}
+
+void SwSectionFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
+{
+ SwFrame::dumpAsXmlAttributes( writer );
+ if ( HasFollow() )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
+
+ if (m_pPrecede != nullptr)
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwSectionFrame*>( m_pPrecede )->GetFrameId() );
+}
+
+void SwTabFrame::dumpAsXmlAttributes( xmlTextWriterPtr writer ) const
+{
+ SwFrame::dumpAsXmlAttributes( writer );
+ if ( HasFollow() )
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "follow" ), "%" SAL_PRIuUINT32, GetFollow()->GetFrameId() );
+
+ if (m_pPrecede != nullptr)
+ (void)xmlTextWriterWriteFormatAttribute( writer, BAD_CAST( "precede" ), "%" SAL_PRIuUINT32, static_cast<SwTabFrame*>( m_pPrecede )->GetFrameId() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxLinkProcessor.cxx b/sw/source/core/tox/ToxLinkProcessor.cxx
new file mode 100644
index 000000000..731a7c8a8
--- /dev/null
+++ b/sw/source/core/tox/ToxLinkProcessor.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <memory>
+#include <ToxLinkProcessor.hxx>
+
+#include <SwStyleNameMapper.hxx>
+#include <ndtxt.hxx>
+#include <sal/log.hxx>
+#include <rtl/uri.hxx>
+
+namespace sw {
+
+void
+ToxLinkProcessor::StartNewLink(sal_Int32 startPosition, const OUString& characterStyle)
+{
+ SAL_INFO_IF(m_pStartedLink, "sw.core", "ToxLinkProcessor: LS without LE");
+ m_pStartedLink = std::make_unique<StartedLink>(
+ startPosition, characterStyle);
+}
+
+void ToxLinkProcessor::CloseLink(sal_Int32 endPosition, const OUString& url, bool bRelative)
+{
+ if (m_pStartedLink == nullptr)
+ {
+ SAL_INFO("sw.core", "ToxLinkProcessor: LE without LS");
+ return;
+ }
+
+ if (url.isEmpty()) {
+ return;
+ }
+
+ OUString uri;
+ if (bRelative)
+ {
+ // url contains '|' which must be encoded; also in some cases contains
+ // arbitrary strings that need to be encoded
+ assert(url[0] == '#'); // all links are internal
+ uri = "#"
+ + rtl::Uri::encode(url.copy(1), rtl_UriCharClassUricNoSlash,
+ rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ uri = url;
+ }
+
+ std::unique_ptr<ClosedLink> pClosedLink(
+ new ClosedLink(uri, m_pStartedLink->mStartPosition, endPosition));
+
+ const OUString& characterStyle = m_pStartedLink->mCharacterStyle;
+ sal_uInt16 poolId = ObtainPoolId(characterStyle);
+ pClosedLink->mINetFormat.SetVisitedFormatAndId(characterStyle, poolId);
+ pClosedLink->mINetFormat.SetINetFormatAndId(characterStyle, poolId);
+
+ m_ClosedLinks.push_back(std::move(pClosedLink));
+ m_pStartedLink.reset();
+}
+
+sal_uInt16
+ToxLinkProcessor::ObtainPoolId(const OUString& characterStyle) const
+{
+ if (characterStyle.isEmpty()) {
+ return USHRT_MAX;
+ }
+ else {
+ return SwStyleNameMapper::GetPoolIdFromUIName(characterStyle, SwGetPoolIdFromName::ChrFmt);
+ }
+}
+
+
+void
+ToxLinkProcessor::InsertLinkAttributes(SwTextNode& node)
+{
+ for (auto const& clink : m_ClosedLinks)
+ {
+ node.InsertItem(clink->mINetFormat, clink->mStartTextPos, clink->mEndTextPos);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxTabStopTokenHandler.cxx b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
new file mode 100644
index 000000000..a89e9ad27
--- /dev/null
+++ b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <ToxTabStopTokenHandler.hxx>
+
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+
+#include <cntfrm.hxx>
+#include <fmtfsize.hxx>
+#include <fmtpdsc.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <ndtxt.hxx>
+#include <pagedesc.hxx>
+#include <pagefrm.hxx>
+#include <swrect.hxx>
+#include <tox.hxx>
+
+namespace sw {
+
+DefaultToxTabStopTokenHandler::DefaultToxTabStopTokenHandler(SwNodeOffset indexOfSectionNode,
+ const SwPageDesc& defaultPageDescription,
+ bool tabPositionIsRelativeToParagraphIndent,
+ TabStopReferencePolicy referencePolicy)
+: mIndexOfSectionNode(indexOfSectionNode),
+ mDefaultPageDescription(defaultPageDescription),
+ mTabPositionIsRelativeToParagraphIndent(tabPositionIsRelativeToParagraphIndent),
+ mTabStopReferencePolicy(referencePolicy)
+{
+}
+
+
+ToxTabStopTokenHandler::HandledTabStopToken
+DefaultToxTabStopTokenHandler::HandleTabStopToken(
+ const SwFormToken& aToken, const SwTextNode& targetNode, const SwRootFrame *currentLayout) const
+{
+ HandledTabStopToken result;
+
+ if (aToken.bWithTab) { // #i21237#
+ result.text = "\t";
+ }
+
+ // check whether a tab adjustment has been specified.
+ if (SvxTabAdjust::End > aToken.eTabAlign) {
+ const SvxLRSpaceItem& rLR = targetNode.SwContentNode::GetAttr(RES_LR_SPACE);
+
+ tools::Long nTabPosition = aToken.nTabStopPosition;
+ if (!mTabPositionIsRelativeToParagraphIndent && rLR.GetTextLeft()) {
+ nTabPosition -= rLR.GetTextLeft();
+ }
+ result.tabStop = SvxTabStop(nTabPosition, aToken.eTabAlign, cDfltDecimalChar, aToken.cTabFillChar);
+ return result;
+ }
+
+ SwRect aNdRect;
+ if (CanUseLayoutRectangle(targetNode, currentLayout)) {
+ aNdRect = targetNode.FindLayoutRect(true);
+ }
+ tools::Long nRightMargin;
+ if (aNdRect.IsEmpty()) {
+ nRightMargin = CalculatePageMarginFromPageDescription(targetNode);
+ } else {
+ nRightMargin = aNdRect.Width();
+ }
+ //#i24363# tab stops relative to indent
+ if (mTabStopReferencePolicy == TABSTOPS_RELATIVE_TO_INDENT) {
+ // left margin of paragraph style
+ const SvxLRSpaceItem& rLRSpace = targetNode.GetTextColl()->GetLRSpace();
+ nRightMargin -= rLRSpace.GetLeft();
+ nRightMargin -= rLRSpace.GetTextFirstLineOffset();
+ }
+
+ result.tabStop = SvxTabStop(nRightMargin, SvxTabAdjust::Right, cDfltDecimalChar, aToken.cTabFillChar);
+ return result;
+}
+
+tools::Long
+DefaultToxTabStopTokenHandler::CalculatePageMarginFromPageDescription(const SwTextNode& targetNode) const
+{
+ SwNodeOffset nPgDescNdIdx = targetNode.GetIndex() + 1;
+ const SwPageDesc *pPageDesc = targetNode.FindPageDesc(&nPgDescNdIdx);
+ if (!pPageDesc || nPgDescNdIdx < mIndexOfSectionNode) {
+ // Use default page description, if none is found or the found one is given by a Node before the
+ // table-of-content section.
+ pPageDesc = &mDefaultPageDescription;
+ }
+ const SwFrameFormat& rPgDscFormat = pPageDesc->GetMaster();
+ tools::Long result = rPgDscFormat.GetFrameSize().GetWidth() - rPgDscFormat.GetLRSpace().GetLeft()
+ - rPgDscFormat.GetLRSpace().GetRight();
+ // Also consider borders
+ const SvxBoxItem& rBox = rPgDscFormat.GetBox();
+ result -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
+ return result;
+}
+
+
+/*static*/ bool
+DefaultToxTabStopTokenHandler::CanUseLayoutRectangle(const SwTextNode& targetNode, const SwRootFrame *currentLayout)
+{
+ const SwPageDesc* pageDescription =
+ targetNode.SwContentNode::GetAttr(RES_PAGEDESC).GetPageDesc();
+
+ if (!pageDescription) {
+ return false;
+ }
+ const SwFrame* pFrame = targetNode.getLayoutFrame(currentLayout);
+ if (!pFrame) {
+ return false;
+ }
+ pFrame = pFrame->FindPageFrame();
+ if (!pFrame) {
+ return false;
+ }
+ const SwPageFrame* pageFrame = static_cast<const SwPageFrame*>(pFrame);
+ return pageDescription == pageFrame->GetPageDesc();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxTextGenerator.cxx b/sw/source/core/tox/ToxTextGenerator.cxx
new file mode 100644
index 000000000..82adcbca6
--- /dev/null
+++ b/sw/source/core/tox/ToxTextGenerator.cxx
@@ -0,0 +1,468 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <ToxTextGenerator.hxx>
+
+#include <chpfld.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <ndindex.hxx>
+#include <fchrfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <ndtxt.hxx>
+#include <tox.hxx>
+#include <txmsrt.hxx>
+#include <fmtautofmt.hxx>
+#include <swatrset.hxx>
+#include <ToxWhitespaceStripper.hxx>
+#include <ToxLinkProcessor.hxx>
+#include <ToxTabStopTokenHandler.hxx>
+#include <txatbase.hxx>
+#include <modeltoviewhelper.hxx>
+#include <strings.hrc>
+
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <svl/itemiter.hxx>
+
+#include <cassert>
+#include <memory>
+
+namespace {
+
+bool sortTabHasNoToxSourcesOrFirstToxSourceHasNoNode(const SwTOXSortTabBase& sortTab)
+{
+ if (sortTab.aTOXSources.empty()) {
+ return true;
+ }
+ if (sortTab.aTOXSources.at(0).pNd == nullptr) {
+ return true;
+ }
+ return false;
+}
+
+} // end anonymous namespace
+
+namespace sw {
+
+OUString
+ToxTextGenerator::GetNumStringOfFirstNode(const SwTOXSortTabBase& rBase,
+ bool bUsePrefix, sal_uInt8 nLevel,
+ SwRootFrame const*const pLayout)
+{
+ if (sortTabHasNoToxSourcesOrFirstToxSourceHasNoNode(rBase)) {
+ return OUString();
+ }
+
+ OUString sRet;
+ if (rBase.pTextMark) { // only if it's not a Mark
+ return sRet;
+ }
+
+ const SwTextNode* pNd = rBase.aTOXSources[0].pNd->GetTextNode();
+ if (!pNd) {
+ return sRet;
+ }
+ if (pLayout && pLayout->HasMergedParas())
+ { // note: pNd could be any node, since it could be Sequence etc.
+ pNd = sw::GetParaPropsNode(*pLayout, SwNodeIndex(*pNd));
+ }
+
+ const SwNumRule* pRule = pNd->GetNumRule();
+ if (!pRule) {
+ return sRet;
+ }
+
+ if (pNd->GetActualListLevel() < MAXLEVEL) {
+ sRet = pNd->GetNumString(bUsePrefix, nLevel, pLayout);
+ }
+
+ if (!sRet.isEmpty()) {
+ sRet += " ";// Makes sure spacing is done only when there is outline numbering
+ }
+
+ return sRet;
+}
+
+
+ToxTextGenerator::ToxTextGenerator(const SwForm& toxForm,
+ std::shared_ptr<ToxTabStopTokenHandler> const & tabStopHandler)
+: mToxForm(toxForm),
+ mLinkProcessor(std::make_shared<ToxLinkProcessor>()),
+ mTabStopTokenHandler(tabStopHandler)
+{}
+
+ToxTextGenerator::~ToxTextGenerator()
+{}
+
+OUString
+ToxTextGenerator::HandleChapterToken(const SwTOXSortTabBase& rBase,
+ const SwFormToken& aToken, SwRootFrame const*const pLayout) const
+{
+ if (sortTabHasNoToxSourcesOrFirstToxSourceHasNoNode(rBase)) {
+ return OUString();
+ }
+
+ // A bit tricky: Find a random Frame
+ const SwContentNode* contentNode = rBase.aTOXSources.at(0).pNd->GetContentNode();
+ if (!contentNode) {
+ return OUString();
+ }
+
+ // #i53420#
+ const SwContentFrame* contentFrame = contentNode->getLayoutFrame(pLayout);
+ if (!contentFrame) {
+ return OUString();
+ }
+
+ return GenerateTextForChapterToken(aToken, contentFrame, contentNode, pLayout);
+}
+
+OUString
+ToxTextGenerator::GenerateTextForChapterToken(const SwFormToken& chapterToken, const SwContentFrame* contentFrame,
+ const SwContentNode *contentNode,
+ SwRootFrame const*const pLayout) const
+{
+ OUString retval;
+
+ SwChapterFieldType chapterFieldType;
+ SwChapterField aField = ObtainChapterField(&chapterFieldType, &chapterToken, contentFrame, contentNode);
+
+ //---> #i89791#
+ // continue to support CF_NUMBER and CF_NUM_TITLE in order to handle ODF 1.0/1.1 written by OOo 3.x
+ // in the same way as OOo 2.x would handle them.
+ if (CF_NUM_NOPREPST_TITLE == chapterToken.nChapterFormat || CF_NUMBER == chapterToken.nChapterFormat) {
+ retval += aField.GetNumber(pLayout); // get the string number without pre/postfix
+ }
+ else if (CF_NUMBER_NOPREPST == chapterToken.nChapterFormat || CF_NUM_TITLE == chapterToken.nChapterFormat) {
+ retval += aField.GetNumber(pLayout) + " " + aField.GetTitle(pLayout);
+ } else if (CF_TITLE == chapterToken.nChapterFormat) {
+ retval += aField.GetTitle(pLayout);
+ }
+ return retval;
+}
+
+// Add parameter <_TOXSectNdIdx> and <_pDefaultPageDesc> in order to control,
+// which page description is used, no appropriate one is found.
+void
+ToxTextGenerator::GenerateText(SwDoc* pDoc,
+ std::unordered_map<OUString, int> & rMarkURLs,
+ const std::vector<std::unique_ptr<SwTOXSortTabBase>> &entries,
+ sal_uInt16 indexOfEntryToProcess, sal_uInt16 numberOfEntriesToProcess,
+ SwRootFrame const*const pLayout)
+{
+ // pTOXNd is only set at the first mark
+ SwTextNode* pTOXNd = const_cast<SwTextNode*>(entries.at(indexOfEntryToProcess)->pTOXNd);
+ // FIXME this operates directly on the node text
+ OUString & rText = const_cast<OUString&>(pTOXNd->GetText());
+ rText.clear();
+ for(sal_uInt16 nIndex = indexOfEntryToProcess; nIndex < indexOfEntryToProcess + numberOfEntriesToProcess; nIndex++)
+ {
+ if(nIndex > indexOfEntryToProcess)
+ rText += ", "; // comma separation
+ // Initialize String with the Pattern from the form
+ const SwTOXSortTabBase& rBase = *entries.at(nIndex);
+ sal_uInt16 nLvl = rBase.GetLevel();
+ OSL_ENSURE( nLvl < mToxForm.GetFormMax(), "invalid FORM_LEVEL");
+
+ SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ // create an enumerator
+ // #i21237#
+ SwFormTokens aPattern = mToxForm.GetPattern(nLvl);
+ // remove text from node
+ for(const auto& aToken : aPattern) // #i21237#
+ {
+ sal_Int32 nStartCharStyle = rText.getLength();
+ OUString aCharStyleName = aToken.sCharStyleName;
+ switch( aToken.eTokenType )
+ {
+ case TOKEN_ENTRY_NO:
+ // for TOC numbering
+ rText += GetNumStringOfFirstNode(rBase,
+ aToken.nChapterFormat == CF_NUMBER,
+ static_cast<sal_uInt8>(aToken.nOutlineLevel - 1), pLayout);
+ break;
+
+ case TOKEN_ENTRY_TEXT: {
+ HandledTextToken htt = HandleTextToken(rBase, pDoc->GetAttrPool(), pLayout);
+ ApplyHandledTextToken(htt, *pTOXNd);
+ }
+ break;
+
+ case TOKEN_ENTRY:
+ {
+ // for TOC numbering
+ rText += GetNumStringOfFirstNode(rBase, true, MAXLEVEL, pLayout);
+ HandledTextToken htt = HandleTextToken(rBase, pDoc->GetAttrPool(), pLayout);
+ ApplyHandledTextToken(htt, *pTOXNd);
+ }
+ break;
+
+ case TOKEN_TAB_STOP: {
+ ToxTabStopTokenHandler::HandledTabStopToken htst =
+ mTabStopTokenHandler->HandleTabStopToken(aToken, *pTOXNd, pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ rText += htst.text;
+ aTStops.Insert(htst.tabStop);
+ break;
+ }
+
+ case TOKEN_TEXT:
+ rText += aToken.sText;
+ break;
+
+ case TOKEN_PAGE_NUMS:
+ rText += ConstructPageNumberPlaceholder(rBase.aTOXSources.size());
+ break;
+
+ case TOKEN_CHAPTER_INFO:
+ rText += HandleChapterToken(rBase, aToken, pLayout);
+ break;
+
+ case TOKEN_LINK_START:
+ mLinkProcessor->StartNewLink(rText.getLength(), aToken.sCharStyleName);
+ break;
+
+ case TOKEN_LINK_END:
+ {
+ auto [url, isMark] = rBase.GetURL(pLayout);
+ if (isMark)
+ {
+ auto [iter, _] = rMarkURLs.emplace(url, 0);
+ (void) _; // sigh... ignore it more explicitly
+ ++iter->second;
+ url = "#" + OUString::number(iter->second) + url;
+ }
+ mLinkProcessor->CloseLink(rText.getLength(), url, /*bRelative=*/true);
+ }
+ break;
+
+ case TOKEN_AUTHORITY:
+ {
+ ToxAuthorityField eField = static_cast<ToxAuthorityField>(aToken.nAuthorityField);
+ SwIndex aIdx( pTOXNd, rText.getLength() );
+ if (eField == ToxAuthorityField::AUTH_FIELD_URL)
+ {
+ aCharStyleName = SwResId(STR_POOLCHR_INET_NORMAL);
+ mLinkProcessor->StartNewLink(rText.getLength(), aCharStyleName);
+ }
+ rBase.FillText( *pTOXNd, aIdx, o3tl::narrowing<sal_uInt16>(eField), pLayout );
+ if (eField == ToxAuthorityField::AUTH_FIELD_URL)
+ {
+ // Get the absolute URL, the text may be a relative one.
+ const auto& rAuthority = static_cast<const SwTOXAuthority&>(rBase);
+ OUString aURL = SwTOXAuthority::GetSourceURL(
+ rAuthority.GetText(AUTH_FIELD_URL, pLayout));
+
+ mLinkProcessor->CloseLink(rText.getLength(), aURL, /*bRelative=*/false);
+ }
+ }
+ break;
+ case TOKEN_END: break;
+ }
+
+ if (!aCharStyleName.isEmpty())
+ {
+ SwCharFormat* pCharFormat;
+ if( USHRT_MAX != aToken.nPoolId )
+ pCharFormat = pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( aToken.nPoolId );
+ else
+ pCharFormat = pDoc->FindCharFormatByName(aCharStyleName);
+
+ if (pCharFormat)
+ {
+ SwFormatCharFormat aFormat( pCharFormat );
+ pTOXNd->InsertItem( aFormat, nStartCharStyle,
+ rText.getLength(), SetAttrMode::DONTEXPAND );
+ }
+ }
+ }
+
+ pTOXNd->SetAttr( aTStops );
+ }
+ mLinkProcessor->InsertLinkAttributes(*pTOXNd);
+}
+
+/*static*/ std::shared_ptr<SfxItemSet>
+ToxTextGenerator::CollectAttributesForTox(const SwTextAttr& hint, SwAttrPool& pool)
+{
+ auto retval = std::make_shared<SfxItemSet>(pool);
+ if (hint.Which() != RES_TXTATR_AUTOFMT) {
+ return retval;
+ }
+ const SwFormatAutoFormat& afmt = hint.GetAutoFormat();
+ SfxItemIter aIter( *afmt.GetStyleHandle());
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if (pItem->Which() == RES_CHRATR_ESCAPEMENT ||
+ pItem->Which() == RES_CHRATR_POSTURE ||
+ pItem->Which() == RES_CHRATR_CJK_POSTURE ||
+ pItem->Which() == RES_CHRATR_CTL_POSTURE)
+ {
+ retval->Put(std::unique_ptr<SfxPoolItem>(pItem->Clone()));
+ }
+ pItem = aIter.NextItem();
+ } while (pItem);
+ return retval;
+}
+
+void ToxTextGenerator::GetAttributesForNode(
+ ToxTextGenerator::HandledTextToken & rResult,
+ sal_Int32 & rOffset,
+ SwTextNode const& rNode,
+ ToxWhitespaceStripper const& rStripper,
+ SwAttrPool & rPool,
+ SwRootFrame const*const pLayout)
+{
+ // note: this *must* use the same flags as SwTextNode::GetExpandText()
+ // or indexes will be off!
+ ExpandMode eMode = ExpandMode::ExpandFields | ExpandMode::HideFieldmarkCommands;
+ if (pLayout && pLayout->IsHideRedlines())
+ {
+ eMode |= ExpandMode::HideDeletions;
+ }
+ ModelToViewHelper aConversionMap(rNode, pLayout, eMode);
+ if (SwpHints const*const pHints = rNode.GetpSwpHints())
+ {
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ const SwTextAttr* pHint = pHints->Get(i);
+ std::shared_ptr<SfxItemSet> attributesToClone =
+ CollectAttributesForTox(*pHint, rPool);
+ if (attributesToClone->Count() <= 0) {
+ continue;
+ }
+
+ // sw_redlinehide: due to the ... interesting ... multi-level index
+ // mapping going on here, can't use the usual merged attr iterators :(
+
+ sal_Int32 const nStart(aConversionMap.ConvertToViewPosition(pHint->GetStart()));
+ sal_Int32 const nEnd(aConversionMap.ConvertToViewPosition(pHint->GetAnyEnd()));
+ if (nStart != nEnd) // might be in delete redline, and useless anyway
+ {
+ std::unique_ptr<SwFormatAutoFormat> pClone(pHint->GetAutoFormat().Clone());
+ pClone->SetStyleHandle(attributesToClone);
+ rResult.autoFormats.push_back(std::move(pClone));
+ // note the rStripper is on the whole merged text, so need rOffset
+ rResult.startPositions.push_back(
+ rStripper.GetPositionInStrippedString(rOffset + nStart));
+ rResult.endPositions.push_back(
+ rStripper.GetPositionInStrippedString(rOffset + nEnd));
+ }
+ }
+ }
+ rOffset += aConversionMap.getViewText().getLength();
+}
+
+ToxTextGenerator::HandledTextToken
+ToxTextGenerator::HandleTextToken(const SwTOXSortTabBase& source,
+ SwAttrPool& pool, SwRootFrame const*const pLayout)
+{
+ HandledTextToken result;
+ ToxWhitespaceStripper stripper(source.GetText().sText);
+ result.text = stripper.GetStrippedString();
+
+ // FIXME: there is a pre-existing problem that the index mapping of the
+ // attributes only works if the paragraph is fully selected
+ if (!source.IsFullPara() || source.aTOXSources.empty())
+ return result;
+
+ const SwTextNode* pSrc = source.aTOXSources.front().pNd->GetTextNode();
+ if (!pSrc)
+ {
+ return result;
+ }
+
+ sal_Int32 nOffset(0);
+ GetAttributesForNode(result, nOffset, *pSrc, stripper, pool, pLayout);
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pSrc->getLayoutFrame(pLayout)))
+ {
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ // pSrc already copied above
+ assert(pSrc == pMerged->pParaPropsNode);
+ for (SwNodeOffset i = pSrc->GetIndex() + 1;
+ i <= pMerged->pLastNode->GetIndex(); ++i)
+ {
+ SwNode *const pTmp(pSrc->GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+ GetAttributesForNode(result, nOffset,
+ *pTmp->GetTextNode(), stripper, pool, pLayout);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*static*/ void
+ToxTextGenerator::ApplyHandledTextToken(const HandledTextToken& htt, SwTextNode& targetNode)
+{
+ sal_Int32 offset = targetNode.GetText().getLength();
+ SwIndex aIdx(&targetNode, offset);
+ targetNode.InsertText(htt.text, aIdx);
+ for (size_t i=0; i < htt.autoFormats.size(); ++i) {
+ targetNode.InsertItem(*htt.autoFormats.at(i),
+ htt.startPositions.at(i) + offset,
+ htt.endPositions.at(i) + offset);
+ }
+}
+
+/*static*/ OUString
+ToxTextGenerator::ConstructPageNumberPlaceholder(size_t numberOfToxSources)
+{
+ if (numberOfToxSources == 0) {
+ return OUString();
+ }
+ OUStringBuffer retval;
+ // Place holder for the PageNumber; we only respect the first one
+ retval.append(C_NUM_REPL);
+ for (size_t i = 1; i < numberOfToxSources; ++i) {
+ retval.append(SwTOXMark::S_PAGE_DELI);
+ retval.append(C_NUM_REPL);
+ }
+ retval.append(C_END_PAGE_NUM);
+ return retval.makeStringAndClear();
+}
+
+/*virtual*/ SwChapterField
+ToxTextGenerator::ObtainChapterField(SwChapterFieldType* chapterFieldType,
+ const SwFormToken* chapterToken, const SwContentFrame* contentFrame,
+ const SwContentNode* contentNode) const
+{
+ assert(chapterToken);
+ assert(chapterToken->nOutlineLevel >= 1);
+
+ SwChapterField retval(chapterFieldType, chapterToken->nChapterFormat);
+ retval.SetLevel(static_cast<sal_uInt8>(chapterToken->nOutlineLevel - 1));
+ // #i53420#
+ retval.ChangeExpansion(*contentFrame, contentNode, true);
+ return retval;
+}
+} // end namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxWhitespaceStripper.cxx b/sw/source/core/tox/ToxWhitespaceStripper.cxx
new file mode 100644
index 000000000..28c9056ce
--- /dev/null
+++ b/sw/source/core/tox/ToxWhitespaceStripper.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <ToxWhitespaceStripper.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+
+namespace sw {
+
+ToxWhitespaceStripper::ToxWhitespaceStripper(const OUString& inputString)
+{
+ OUStringBuffer buffer;
+
+ bool lastCharacterWasWhitespace = false;
+ for (sal_Int32 pos = 0; pos < inputString.getLength(); ++pos) {
+ sal_Unicode cur = inputString[pos];
+
+ if (cur == ' ' || cur == '\n' || cur == '\t') {
+ // merge consecutive whitespaces (and translate them to spaces)
+ if (!lastCharacterWasWhitespace) {
+ buffer.append(' ');
+ }
+ lastCharacterWasWhitespace = true;
+ }
+ else {
+ buffer.append(cur);
+ lastCharacterWasWhitespace = false;
+ }
+ mNewPositions.push_back(buffer.getLength()-1);
+ }
+ // strip the last whitespace (if there was one)
+ if (lastCharacterWasWhitespace) {
+ buffer.truncate(buffer.getLength() - 1);
+ }
+ mNewPositions.push_back(buffer.getLength());
+ mStripped = buffer.makeStringAndClear();
+}
+
+
+sal_Int32
+ToxWhitespaceStripper::GetPositionInStrippedString(sal_Int32 pos) const
+{
+ assert(0 <= pos);
+ if (o3tl::make_unsigned(pos) >= mNewPositions.size()) {
+ // TODO probably this should assert, not just warn?
+ SAL_WARN("sw.core", "Requested position of TOX entry text which does not exist. "
+ "Maybe the formatting hint is corrupt?");
+ return mNewPositions.back();
+ }
+ return mNewPositions.at(pos);
+}
+
+
+}
diff --git a/sw/source/core/tox/tox.cxx b/sw/source/core/tox/tox.cxx
new file mode 100644
index 000000000..53fc36d38
--- /dev/null
+++ b/sw/source/core/tox/tox.cxx
@@ -0,0 +1,949 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <calbck.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <editeng/tstpitem.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <rootfrm.hxx>
+#include <scriptinfo.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <tox.hxx>
+#include <txtfrm.hxx>
+#include <txttxmrk.hxx>
+
+#include <optional>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <string_view>
+
+
+const sal_Unicode C_NUM_REPL = '@';
+const sal_Unicode C_END_PAGE_NUM = '~';
+
+namespace
+{
+void lcl_FillAuthPattern(SwFormTokens &rAuthTokens, sal_uInt16 nTypeId)
+{
+ rAuthTokens.reserve(9); // Worst case: Start+Sep1+Auth+3*(Sep2+Auth)
+
+ SwFormToken aStartToken( TOKEN_AUTHORITY );
+ aStartToken.nAuthorityField = AUTH_FIELD_IDENTIFIER;
+ rAuthTokens.push_back( aStartToken );
+ SwFormToken aSeparatorToken( TOKEN_TEXT );
+ aSeparatorToken.sText = ": ";
+ rAuthTokens.push_back( aSeparatorToken );
+
+ --nTypeId; // compensate +1 offset introduced by caller
+
+ SwFormToken aTextToken( TOKEN_TEXT );
+ aTextToken.sText = ", ";
+
+ const ToxAuthorityField nVals[4] = {
+ AUTH_FIELD_AUTHOR,
+ AUTH_FIELD_TITLE,
+ AUTH_FIELD_YEAR,
+ nTypeId == AUTH_TYPE_WWW ? AUTH_FIELD_URL : AUTH_FIELD_END
+ };
+
+ for(size_t i = 0; i < SAL_N_ELEMENTS(nVals); ++i)
+ {
+ if(nVals[i] == AUTH_FIELD_END)
+ break;
+ if( i > 0 )
+ rAuthTokens.push_back( aTextToken );
+
+ // -> #i21237#
+ SwFormToken aToken(TOKEN_AUTHORITY);
+
+ aToken.nAuthorityField = nVals[i];
+ rAuthTokens.push_back(aToken);
+ // <- #i21237#
+ }
+}
+}
+
+/// pool default constructor
+SwTOXMark::SwTOXMark()
+ : SfxPoolItem(RES_TXTATR_TOXMARK)
+ , m_pType(nullptr)
+ , m_pTextAttr(nullptr)
+ , m_nLevel(0)
+ , m_bAutoGenerated(false)
+ , m_bMainEntry(false)
+{
+}
+
+SwTOXMark::SwTOXMark(const SwTOXType* pType)
+ : SfxPoolItem(RES_TXTATR_TOXMARK)
+ , m_pType(pType)
+ , m_pTextAttr(nullptr)
+ , m_nLevel(0)
+ , m_bAutoGenerated(false)
+ , m_bMainEntry(false)
+{
+ StartListening(const_cast<SwTOXType*>(m_pType)->GetNotifier());
+}
+
+SwTOXMark::SwTOXMark(const SwTOXMark& rCopy)
+ : SfxPoolItem(RES_TXTATR_TOXMARK)
+ , SvtListener()
+ , m_pType(rCopy.m_pType)
+ , m_aPrimaryKey(rCopy.m_aPrimaryKey)
+ , m_aSecondaryKey(rCopy.m_aSecondaryKey)
+ , m_aTextReading(rCopy.m_aTextReading)
+ , m_aPrimaryKeyReading(rCopy.m_aPrimaryKeyReading)
+ , m_aSecondaryKeyReading(rCopy.m_aSecondaryKeyReading)
+ , m_pTextAttr(nullptr)
+ , m_nLevel(rCopy.m_nLevel)
+ , m_bAutoGenerated(rCopy.m_bAutoGenerated)
+ , m_bMainEntry(rCopy.m_bMainEntry)
+{
+ if(m_pType)
+ StartListening(const_cast<SwTOXType*>(m_pType)->GetNotifier());
+ // Copy AlternativString
+ m_aAltText = rCopy.m_aAltText;
+}
+
+SwTOXMark::~SwTOXMark()
+{
+}
+
+void SwTOXMark::RegisterToTOXType(SwTOXType& rType)
+{
+ SvtListener::EndListeningAll();
+ m_pType = &rType;
+ StartListening(rType.GetNotifier());
+}
+
+bool SwTOXMark::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return m_pType == static_cast<const SwTOXMark&>(rAttr).m_pType;
+}
+
+SwTOXMark* SwTOXMark::Clone( SfxItemPool* ) const
+{
+ return new SwTOXMark( *this );
+}
+
+void SwTOXMark::Notify(const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ if (pLegacyHint->m_pOld && (RES_REMOVE_UNO_OBJECT == pLegacyHint->m_pOld->Which()))
+ SetXTOXMark(css::uno::Reference<css::text::XDocumentIndexMark>(nullptr));
+ } else if (auto pCollectHint = dynamic_cast<const sw::CollectTextMarksHint*>(&rHint))
+ {
+ if(GetTextTOXMark())
+ pCollectHint->m_rMarks.push_back(this);
+ } else if (auto pCollectLayoutHint = dynamic_cast<const sw::CollectTextTOXMarksForLayoutHint*>(&rHint))
+ {
+ if(!GetTextTOXMark())
+ return;
+ auto& rTextMark = *GetTextTOXMark();
+ auto& rNode = rTextMark.GetTextNode();
+ auto pLayout = pCollectLayoutHint->m_pLayout;
+ // Check basic sanity and that it is part of our layout and not in undo
+ if(!rNode.GetNodes().IsDocNodes() || !rNode.GetText().getLength() || !rNode.HasWriterListeners() || !rNode.getLayoutFrame(pLayout))
+ return;
+ // Check for being hidden
+ if(rNode.IsHiddenByParaField() || SwScriptInfo::IsInHiddenRange(rNode, rTextMark.GetStart()))
+ return;
+ // Check for being hidden by hidden redlines
+ if (pLayout && pLayout->HasMergedParas() && sw::IsMarkHintHidden(*pLayout, rNode, rTextMark))
+ return;
+ pCollectLayoutHint->m_rMarks.push_back(rTextMark);
+ }
+}
+
+void SwTOXMark::InvalidateTOXMark()
+{
+ const SwPtrMsgPoolItem aMsgHint(RES_REMOVE_UNO_OBJECT, &static_cast<sw::BroadcastingModify&>(*this));
+ CallSwClientNotify(sw::LegacyModifyHint(&aMsgHint, &aMsgHint));
+}
+
+OUString SwTOXMark::GetText(SwRootFrame const*const pLayout) const
+{
+ if( !m_aAltText.isEmpty() )
+ return m_aAltText;
+
+ if( m_pTextAttr && m_pTextAttr->GetpTextNd() )
+ {
+ const sal_Int32* pEndIdx = m_pTextAttr->GetEnd();
+ OSL_ENSURE( pEndIdx, "TOXMark without mark!");
+ if( pEndIdx )
+ {
+ const sal_Int32 nStt = m_pTextAttr->GetStart();
+ return m_pTextAttr->GetpTextNd()->GetExpandText(pLayout, nStt, *pEndIdx-nStt);
+ }
+ }
+
+ return OUString();
+}
+
+// Manage types of TOX
+SwTOXType::SwTOXType(SwDoc& rDoc, TOXTypes eTyp, const OUString& rName)
+ : m_rDoc(rDoc)
+ , m_aName(rName)
+ , m_eType(eTyp)
+{
+}
+
+SwTOXType::SwTOXType(const SwTOXType& rCopy)
+ : m_rDoc(rCopy.m_rDoc)
+ , m_aName(rCopy.m_aName)
+ , m_eType(rCopy.m_eType)
+{
+ if (auto pRegisteredIn = const_cast<SwTOXType&>(rCopy).GetRegisteredIn())
+ pRegisteredIn->Add(this);
+}
+
+const TranslateId STR_POOLCOLL_TOX_ARY[] =
+{
+ // Subcategory Index-Directories
+ STR_POOLCOLL_TOX_IDXH,
+ STR_POOLCOLL_TOX_IDX1,
+ STR_POOLCOLL_TOX_IDX2,
+ STR_POOLCOLL_TOX_IDX3,
+ STR_POOLCOLL_TOX_IDXBREAK
+};
+
+const TranslateId STR_POOLCOLL_TOX_CNTNT_ARY[] =
+{
+ // Subcategory Tables of Contents
+ STR_POOLCOLL_TOX_CNTNTH,
+ STR_POOLCOLL_TOX_CNTNT1,
+ STR_POOLCOLL_TOX_CNTNT2,
+ STR_POOLCOLL_TOX_CNTNT3,
+ STR_POOLCOLL_TOX_CNTNT4,
+ STR_POOLCOLL_TOX_CNTNT5
+};
+
+const TranslateId STR_POOLCOLL_TOX_CNTNT_EXTRA_ARY[] =
+{
+ // Subcategory Table of Contents more Levels 5 - 10
+ STR_POOLCOLL_TOX_CNTNT6,
+ STR_POOLCOLL_TOX_CNTNT7,
+ STR_POOLCOLL_TOX_CNTNT8,
+ STR_POOLCOLL_TOX_CNTNT9,
+ STR_POOLCOLL_TOX_CNTNT10
+};
+
+const TranslateId STR_POOLCOLL_TOX_USER_ARY[] =
+{
+ // Subcategory User-Directories:
+ STR_POOLCOLL_TOX_USERH,
+ STR_POOLCOLL_TOX_USER1,
+ STR_POOLCOLL_TOX_USER2,
+ STR_POOLCOLL_TOX_USER3,
+ STR_POOLCOLL_TOX_USER4,
+ STR_POOLCOLL_TOX_USER5
+};
+
+const TranslateId STR_POOLCOLL_TOX_USER_EXTRA_ARY[] =
+{
+ // Subcategory User-Directories more Levels 5 - 10
+ STR_POOLCOLL_TOX_USER6,
+ STR_POOLCOLL_TOX_USER7,
+ STR_POOLCOLL_TOX_USER8,
+ STR_POOLCOLL_TOX_USER9,
+ STR_POOLCOLL_TOX_USER10
+};
+
+const TranslateId STR_POOLCOLL_TOX_ILLUS_ARY[] =
+{
+ // Illustrations Index
+ STR_POOLCOLL_TOX_ILLUSH,
+ STR_POOLCOLL_TOX_ILLUS1
+};
+
+const TranslateId STR_POOLCOLL_TOX_OBJECT_ARY[] =
+{
+ // Object Index
+ STR_POOLCOLL_TOX_OBJECTH,
+ STR_POOLCOLL_TOX_OBJECT1
+};
+
+const TranslateId STR_POOLCOLL_TOX_TABLES_ARY[] =
+{
+ // Tables Index
+ STR_POOLCOLL_TOX_TABLESH,
+ STR_POOLCOLL_TOX_TABLES1
+};
+
+const TranslateId STR_POOLCOLL_TOX_AUTHORITIES_ARY[] =
+{
+ // Index of Authorities
+ STR_POOLCOLL_TOX_AUTHORITIESH,
+ STR_POOLCOLL_TOX_AUTHORITIES1
+};
+
+const TranslateId STR_POOLCOLL_TOX_CITATION_ARY[] =
+{
+ STR_POOLCOLL_TOX_CITATION
+};
+
+// Edit forms
+SwForm::SwForm( TOXTypes eTyp ) // #i21237#
+ : m_eType( eTyp ), m_nFormMaxLevel( SwForm::GetFormMaxLevel( eTyp )),
+// nFirstTabPos( lNumberIndent ),
+ m_bCommaSeparated(false)
+{
+ //bHasFirstTabPos =
+ m_bIsRelTabPos = true;
+
+ // The table of contents has a certain number of headlines + headings
+ // The user has 10 levels + headings
+ // Keyword has 3 levels + headings+ separator
+ // Indexes of tables, object illustrations and authorities consist of a heading and one level
+
+ const TranslateId* pPoolId;
+ switch( m_eType )
+ {
+ case TOX_INDEX: pPoolId = STR_POOLCOLL_TOX_ARY; break;
+ case TOX_USER: pPoolId = STR_POOLCOLL_TOX_USER_ARY; break;
+ case TOX_CONTENT: pPoolId = STR_POOLCOLL_TOX_CNTNT_ARY; break;
+ case TOX_ILLUSTRATIONS: pPoolId = STR_POOLCOLL_TOX_ILLUS_ARY; break;
+ case TOX_OBJECTS : pPoolId = STR_POOLCOLL_TOX_OBJECT_ARY; break;
+ case TOX_TABLES : pPoolId = STR_POOLCOLL_TOX_TABLES_ARY; break;
+ case TOX_AUTHORITIES : pPoolId = STR_POOLCOLL_TOX_AUTHORITIES_ARY; break;
+ case TOX_CITATION : pPoolId = STR_POOLCOLL_TOX_CITATION_ARY; break;
+ default:
+ OSL_ENSURE( false, "invalid TOXTyp");
+ return ;
+ }
+
+ SwFormTokens aTokens;
+ if (TOX_CONTENT == m_eType || TOX_ILLUSTRATIONS == m_eType )
+ {
+ SwFormToken aLinkStt (TOKEN_LINK_START);
+ aLinkStt.sCharStyleName = SwResId(STR_POOLCHR_TOXJUMP);
+ aTokens.push_back(aLinkStt);
+ }
+
+ if (TOX_CONTENT == m_eType)
+ {
+ aTokens.emplace_back(TOKEN_ENTRY_NO);
+ aTokens.emplace_back(TOKEN_ENTRY_TEXT);
+ }
+ else
+ aTokens.emplace_back(TOKEN_ENTRY);
+
+ if (TOX_AUTHORITIES != m_eType)
+ {
+ SwFormToken aToken(TOKEN_TAB_STOP);
+ aToken.nTabStopPosition = 0;
+
+ // #i36870# right aligned tab for all
+ aToken.cTabFillChar = '.';
+ aToken.eTabAlign = SvxTabAdjust::End;
+
+ aTokens.push_back(aToken);
+ aTokens.emplace_back(TOKEN_PAGE_NUMS);
+ }
+
+ if (TOX_CONTENT == m_eType || TOX_ILLUSTRATIONS == m_eType)
+ aTokens.emplace_back(TOKEN_LINK_END);
+
+ SetTemplate(0, SwResId(*pPoolId++));
+
+ if(TOX_INDEX == m_eType)
+ {
+ for( sal_uInt16 i = 1; i < 5; ++i )
+ {
+ if(1 == i)
+ {
+ SwFormTokens aTmpTokens;
+ SwFormToken aTmpToken(TOKEN_ENTRY);
+ aTmpTokens.push_back(aTmpToken);
+
+ SetPattern( i, std::move(aTmpTokens) );
+ SetTemplate(i, SwResId(STR_POOLCOLL_TOX_IDXBREAK));
+ }
+ else
+ {
+ SetPattern( i, std::vector(aTokens) );
+ SetTemplate(i, SwResId(STR_POOLCOLL_TOX_ARY[i - 1]));
+ }
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = 1; i < GetFormMax(); ++i, ++pPoolId) // Number 0 is the title
+ {
+ if (TOX_AUTHORITIES == m_eType)
+ {
+ SwFormTokens aAuthTokens;
+ lcl_FillAuthPattern(aAuthTokens, i);
+ SetPattern(i, std::move(aAuthTokens));
+ }
+ else
+ SetPattern( i, std::vector(aTokens) );
+
+ if( TOX_CONTENT == m_eType && 6 == i )
+ pPoolId = STR_POOLCOLL_TOX_CNTNT_EXTRA_ARY;
+ else if( TOX_USER == m_eType && 6 == i )
+ pPoolId = STR_POOLCOLL_TOX_USER_EXTRA_ARY;
+ else if( TOX_AUTHORITIES == m_eType ) //reuse the same STR_POOLCOLL_TOX_AUTHORITIES1 id each time
+ pPoolId = STR_POOLCOLL_TOX_AUTHORITIES_ARY + 1;
+ SetTemplate(i, SwResId(*pPoolId));
+ }
+ }
+}
+
+SwForm::SwForm(const SwForm& rForm)
+ : m_eType( rForm.m_eType )
+{
+ *this = rForm;
+}
+
+SwForm& SwForm::operator=(const SwForm& rForm)
+{
+ m_eType = rForm.m_eType;
+ m_nFormMaxLevel = rForm.m_nFormMaxLevel;
+// nFirstTabPos = rForm.nFirstTabPos;
+// bHasFirstTabPos = rForm.bHasFirstTabPos;
+ m_bIsRelTabPos = rForm.m_bIsRelTabPos;
+ m_bCommaSeparated = rForm.m_bCommaSeparated;
+ for(sal_uInt16 i=0; i < m_nFormMaxLevel; ++i)
+ {
+ m_aPattern[i] = rForm.m_aPattern[i];
+ m_aTemplate[i] = rForm.m_aTemplate[i];
+ }
+ return *this;
+}
+
+sal_uInt16 SwForm::GetFormMaxLevel( TOXTypes eTOXType )
+{
+ switch( eTOXType )
+ {
+ case TOX_INDEX:
+ return 5;
+ case TOX_USER:
+ case TOX_CONTENT:
+ return MAXLEVEL + 1;
+ case TOX_ILLUSTRATIONS:
+ case TOX_OBJECTS:
+ case TOX_TABLES:
+ return 2;
+ case TOX_BIBLIOGRAPHY:
+ case TOX_CITATION:
+ case TOX_AUTHORITIES:
+ return AUTH_TYPE_END + 1;
+ }
+ return 0;
+}
+
+void SwForm::AdjustTabStops( SwDoc const & rDoc ) // #i21237#
+{
+ const sal_uInt16 nFormMax = GetFormMax();
+ for ( sal_uInt16 nLevel = 1; nLevel < nFormMax; ++nLevel )
+ {
+ SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName( GetTemplate(nLevel) );
+ if( pColl == nullptr )
+ {
+ // Paragraph Style for this level has not been created.
+ // --> No need to propagate default values
+ continue;
+ }
+
+ const SvxTabStopItem& rTabStops = pColl->GetTabStops(false);
+ const sal_uInt16 nTabCount = rTabStops.Count();
+ if (nTabCount != 0)
+ {
+ SwFormTokens aCurrentPattern = GetPattern(nLevel);
+ SwFormTokens::iterator aIt = aCurrentPattern.begin();
+
+ bool bChanged = false;
+ for(sal_uInt16 nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ const SvxTabStop& rTab = rTabStops[nTab];
+
+ if ( rTab.GetAdjustment() == SvxTabAdjust::Default )
+ continue; // ignore the default tab stop
+
+ aIt = find_if( aIt, aCurrentPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_TAB_STOP) );
+ if ( aIt != aCurrentPattern.end() )
+ {
+ bChanged = true;
+ aIt->nTabStopPosition = rTab.GetTabPos();
+ aIt->eTabAlign =
+ ( nTab == nTabCount - 1
+ && rTab.GetAdjustment() == SvxTabAdjust::Right )
+ ? SvxTabAdjust::End
+ : rTab.GetAdjustment();
+ aIt->cTabFillChar = rTab.GetFill();
+ ++aIt;
+ }
+ else
+ break; // no more tokens to replace
+ }
+
+ if ( bChanged )
+ SetPattern( nLevel, std::move(aCurrentPattern) );
+ }
+ }
+}
+
+OUString SwForm::GetFormEntry() {return "<E>";}
+OUString SwForm::GetFormTab() {return "<T>";}
+OUString SwForm::GetFormPageNums() {return "<#>";}
+OUString SwForm::GetFormLinkStt() {return "<LS>";}
+OUString SwForm::GetFormLinkEnd() {return "<LE>";}
+OUString SwForm::GetFormEntryNum() {return "<E#>";}
+OUString SwForm::GetFormEntryText() {return "<ET>";}
+OUString SwForm::GetFormChapterMark() {return "<C>";}
+OUString SwForm::GetFormText() {return "<X>";}
+OUString SwForm::GetFormAuth() {return "<A>";}
+
+SwTOXBase::SwTOXBase(const SwTOXType* pTyp, const SwForm& rForm,
+ SwTOXElement nCreaType, const OUString& rTitle )
+ : SwClient(const_cast<sw::BroadcastingModify*>(static_cast<sw::BroadcastingModify const *>(pTyp)))
+ , m_aForm(rForm)
+ , m_aTitle(rTitle)
+ , m_eLanguage(::GetAppLanguage())
+ , m_nCreateType(nCreaType)
+ , m_nOLEOptions(SwTOOElements::NONE)
+ , m_eCaptionDisplay(CAPTION_COMPLETE)
+ , m_bProtected( true )
+ , m_bFromChapter(false)
+ , m_bFromObjectNames(false)
+ , m_bLevelFromChapter(false)
+ , maMSTOCExpression()
+ , mbKeepExpression(true)
+{
+ m_aData.nOptions = SwTOIOptions::NONE;
+}
+
+SwTOXBase::SwTOXBase( const SwTOXBase& rSource, SwDoc* pDoc )
+ : SwClient( rSource.GetRegisteredInNonConst() )
+ , mbKeepExpression(true)
+{
+ CopyTOXBase( pDoc, rSource );
+}
+
+void SwTOXBase::RegisterToTOXType( SwTOXType& rType )
+{
+ rType.Add( this );
+}
+
+void SwTOXBase::CopyTOXBase( SwDoc* pDoc, const SwTOXBase& rSource )
+{
+ maMSTOCExpression = rSource.maMSTOCExpression;
+ SwTOXType* pType = const_cast<SwTOXType*>(rSource.GetTOXType());
+ if( pDoc &&
+ std::find_if(pDoc->GetTOXTypes().begin(), pDoc->GetTOXTypes().end(),
+ [=](const std::unique_ptr<SwTOXType> & p) { return p.get() == pType; })
+ == pDoc->GetTOXTypes().end())
+ {
+ // type not in pDoc, so create it now
+ const SwTOXTypes& rTypes = pDoc->GetTOXTypes();
+ bool bFound = false;
+ for( size_t n = rTypes.size(); n; )
+ {
+ const SwTOXType* pCmp = rTypes[ --n ].get();
+ if( pCmp->GetType() == pType->GetType() &&
+ pCmp->GetTypeName() == pType->GetTypeName() )
+ {
+ pType = const_cast<SwTOXType*>(pCmp);
+ bFound = true;
+ break;
+ }
+ }
+
+ if( !bFound )
+ pType = const_cast<SwTOXType*>(pDoc->InsertTOXType( *pType ));
+ }
+ pType->Add( this );
+
+ m_nCreateType = rSource.m_nCreateType;
+ m_aTitle = rSource.m_aTitle;
+ m_aForm = rSource.m_aForm;
+ m_aBookmarkName = rSource.m_aBookmarkName;
+ m_bProtected = rSource.m_bProtected;
+ m_bFromChapter = rSource.m_bFromChapter;
+ m_bFromObjectNames = rSource.m_bFromObjectNames;
+ m_sMainEntryCharStyle = rSource.m_sMainEntryCharStyle;
+ m_sSequenceName = rSource.m_sSequenceName;
+ m_eCaptionDisplay = rSource.m_eCaptionDisplay;
+ m_nOLEOptions = rSource.m_nOLEOptions;
+ m_eLanguage = rSource.m_eLanguage;
+ m_sSortAlgorithm = rSource.m_sSortAlgorithm;
+ m_bLevelFromChapter = rSource.m_bLevelFromChapter;
+
+ for( sal_uInt16 i = 0; i < MAXLEVEL; ++i )
+ m_aStyleNames[i] = rSource.m_aStyleNames[i];
+
+ // it's the same data type!
+ m_aData.nOptions = rSource.m_aData.nOptions;
+
+ if( !pDoc || pDoc->IsCopyIsMove() )
+ m_aName = rSource.GetTOXName();
+ else
+ m_aName = pDoc->GetUniqueTOXBaseName( *pType, rSource.GetTOXName() );
+}
+
+// TOX specific functions
+SwTOXBase::~SwTOXBase()
+{
+// if( GetTOXType()->GetType() == TOX_USER )
+// delete aData.pTemplateName;
+}
+
+void SwTOXBase::SetTitle(const OUString& rTitle)
+ { m_aTitle = rTitle; }
+
+void SwTOXBase::SetBookmarkName(const OUString& bName)
+{
+ m_aBookmarkName = bName;
+}
+
+SwTOXBase & SwTOXBase::operator = (const SwTOXBase & rSource)
+{
+ m_aForm = rSource.m_aForm;
+ m_aName = rSource.m_aName;
+ m_aTitle = rSource.m_aTitle;
+ m_aBookmarkName = rSource.m_aBookmarkName;
+ m_sMainEntryCharStyle = rSource.m_sMainEntryCharStyle;
+ for(sal_uInt16 nLevel = 0; nLevel < MAXLEVEL; nLevel++)
+ m_aStyleNames[nLevel] = rSource.m_aStyleNames[nLevel];
+ m_sSequenceName = rSource.m_sSequenceName;
+ m_eLanguage = rSource.m_eLanguage;
+ m_sSortAlgorithm = rSource.m_sSortAlgorithm;
+ m_aData = rSource.m_aData;
+ m_nCreateType = rSource.m_nCreateType;
+ m_nOLEOptions = rSource.m_nOLEOptions;
+ m_eCaptionDisplay = rSource.m_eCaptionDisplay;
+ m_bProtected = rSource.m_bProtected;
+ m_bFromChapter = rSource.m_bFromChapter;
+ m_bFromObjectNames = rSource.m_bFromObjectNames;
+ m_bLevelFromChapter = rSource.m_bLevelFromChapter;
+
+ if (rSource.GetAttrSet())
+ SetAttrSet(*rSource.GetAttrSet());
+
+ return *this;
+}
+
+OUString SwFormToken::GetString() const
+{
+ OUString sToken;
+
+ switch( eTokenType )
+ {
+ case TOKEN_ENTRY_NO:
+ sToken = SwForm::GetFormEntryNum();
+ break;
+ case TOKEN_ENTRY_TEXT:
+ sToken = SwForm::GetFormEntryText();
+ break;
+ case TOKEN_ENTRY:
+ sToken = SwForm::GetFormEntry();
+ break;
+ case TOKEN_TAB_STOP:
+ sToken = SwForm::GetFormTab();
+ break;
+ case TOKEN_TEXT:
+ // Return a Token only if Text is not empty!
+ if( sText.isEmpty() )
+ {
+ return OUString();
+ }
+ sToken = SwForm::GetFormText();
+ break;
+ case TOKEN_PAGE_NUMS:
+ sToken = SwForm::GetFormPageNums();
+ break;
+ case TOKEN_CHAPTER_INFO:
+ sToken = SwForm::GetFormChapterMark();
+ break;
+ case TOKEN_LINK_START:
+ sToken = SwForm::GetFormLinkStt();
+ break;
+ case TOKEN_LINK_END:
+ sToken = SwForm::GetFormLinkEnd();
+ break;
+ case TOKEN_AUTHORITY:
+ {
+ sToken = SwForm::GetFormAuth();
+ }
+ break;
+ case TOKEN_END:
+ break;
+ }
+
+ OUString sData = " " + sCharStyleName + "," + OUString::number( nPoolId ) + ",";
+
+ // TabStopPosition and TabAlign or ChapterInfoFormat
+ switch (eTokenType)
+ {
+ case TOKEN_TAB_STOP:
+ sData += OUString::number( nTabStopPosition ) + ","
+ + OUString::number( static_cast< sal_Int32 >(eTabAlign) ) + ","
+ + OUStringChar(cTabFillChar) + ","
+ + OUString::number( bWithTab ? 1 : 0 );
+ break;
+ case TOKEN_CHAPTER_INFO:
+ case TOKEN_ENTRY_NO:
+ // add also maximum permitted level
+ sData += OUString::number( nChapterFormat ) + ","
+ + OUString::number( nOutlineLevel );
+ break;
+ case TOKEN_TEXT:
+ sData += OUStringChar(TOX_STYLE_DELIMITER)
+ + sText.replaceAll(OUStringChar(TOX_STYLE_DELIMITER), "")
+ + OUStringChar(TOX_STYLE_DELIMITER);
+ break;
+ case TOKEN_AUTHORITY:
+ if (nAuthorityField<10)
+ {
+ sData = "0" + OUString::number( nAuthorityField ) + sData;
+ }
+ else
+ {
+ sData = OUString::number( nAuthorityField ) + sData;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return sToken.subView(0, sToken.getLength()-1) + sData + sToken.subView(sToken.getLength()-1);
+}
+
+// -> #i21237#
+
+/**
+ Returns the type of a token.
+
+ @param sToken the string representation of the token
+ @param rTokenLen return parameter the length of the head of the token
+
+ @return the type of the token
+*/
+static FormTokenType lcl_GetTokenType(std::u16string_view sToken,
+ sal_Int32 & rTokenLen)
+{
+ static struct
+ {
+ OUString sTokenStart;
+ sal_Int16 nTokenLength;
+ FormTokenType eTokenType;
+ } const aTokenArr[] = {
+ { SwForm::GetFormTab().copy(0, 2), 3, TOKEN_TAB_STOP },
+ { SwForm::GetFormPageNums().copy(0, 2), 3, TOKEN_PAGE_NUMS },
+ { SwForm::GetFormLinkStt().copy(0, 3), 4, TOKEN_LINK_START },
+ { SwForm::GetFormLinkEnd().copy(0, 3), 4, TOKEN_LINK_END },
+ { SwForm::GetFormEntryNum().copy(0, 3), 4, TOKEN_ENTRY_NO },
+ { SwForm::GetFormEntryText().copy(0, 3), 4, TOKEN_ENTRY_TEXT },
+ { SwForm::GetFormChapterMark().copy(0, 2), 3, TOKEN_CHAPTER_INFO },
+ { SwForm::GetFormText().copy(0, 2), 3, TOKEN_TEXT },
+ { SwForm::GetFormEntry().copy(0, 2), 3, TOKEN_ENTRY },
+ { SwForm::GetFormAuth().copy(0, 2), 5, TOKEN_AUTHORITY }
+ };
+
+ for(const auto & i : aTokenArr)
+ {
+ if( o3tl::starts_with( sToken, i.sTokenStart ) )
+ {
+ rTokenLen = i.nTokenLength;
+ return i.eTokenType;
+ }
+ }
+
+ SAL_WARN("sw.core", "SwFormTokensHelper: invalid token");
+ return TOKEN_END;
+}
+
+/**
+ Returns the string of a token.
+
+ @param sPattern the whole pattern
+ @param nStt starting position of the token
+
+ @return the string representation of the token
+*/
+static OUString
+lcl_SearchNextToken(const OUString & sPattern, sal_Int32 const nStt)
+{
+ sal_Int32 nEnd = sPattern.indexOf( '>', nStt );
+ if (nEnd >= 0)
+ {
+ // apparently the TOX_STYLE_DELIMITER act as a bracketing for
+ // TOKEN_TEXT tokens so that the user can have '>' inside the text...
+ const sal_Int32 nTextSeparatorFirst = sPattern.indexOf( TOX_STYLE_DELIMITER, nStt );
+ if ( nTextSeparatorFirst >= 0
+ && nTextSeparatorFirst + 1 < sPattern.getLength()
+ && nTextSeparatorFirst < nEnd)
+ {
+ const sal_Int32 nTextSeparatorSecond = sPattern.indexOf( TOX_STYLE_DELIMITER,
+ nTextSeparatorFirst + 1 );
+ // Since nEnd>=0 we don't need to check if nTextSeparatorSecond<0!
+ if( nEnd < nTextSeparatorSecond )
+ nEnd = sPattern.indexOf( '>', nTextSeparatorSecond );
+ // FIXME: No check to verify that nEnd is still >=0?
+ assert(nEnd >= 0);
+ }
+
+ ++nEnd;
+
+ return sPattern.copy( nStt, nEnd - nStt );
+ }
+
+ return OUString();
+}
+
+/**
+ Builds a token from its string representation.
+
+ @sPattern the whole pattern
+ @nCurPatternPos starting position of the token
+
+ @return the token
+ */
+static std::optional<SwFormToken>
+lcl_BuildToken(const OUString & sPattern, sal_Int32 & nCurPatternPos)
+{
+ OUString sToken( lcl_SearchNextToken(sPattern, nCurPatternPos) );
+ nCurPatternPos += sToken.getLength();
+ sal_Int32 nTokenLen = 0;
+ FormTokenType const eTokenType = lcl_GetTokenType(sToken, nTokenLen);
+ if (TOKEN_END == eTokenType) // invalid input? skip it
+ {
+ nCurPatternPos = sPattern.getLength();
+ return std::optional<SwFormToken>();
+ }
+
+ // at this point sPattern contains the
+ // character style name, the PoolId, tab stop position, tab stop alignment, chapter info format
+ // the form is: CharStyleName, PoolId[, TabStopPosition|ChapterInfoFormat[, TabStopAlignment[, TabFillChar]]]
+ // in text tokens the form differs from the others: CharStyleName, PoolId[,\0xffinserted text\0xff]
+ SwFormToken eRet( eTokenType );
+ const OUString sAuthFieldEnum = sToken.copy( 2, 2 );
+ sToken = sToken.copy( nTokenLen, sToken.getLength() - nTokenLen - 1);
+
+ sal_Int32 nIdx{ 0 };
+ eRet.sCharStyleName = sToken.getToken( 0, ',', nIdx );
+ std::u16string_view sTmp( o3tl::getToken(sToken, 0, ',', nIdx ));
+ if( !sTmp.empty() )
+ eRet.nPoolId = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sTmp));
+
+ switch( eTokenType )
+ {
+//i53420
+ case TOKEN_CHAPTER_INFO:
+//i53420
+ case TOKEN_ENTRY_NO:
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 2
+ if( !sTmp.empty() )
+ eRet.nChapterFormat = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sTmp));
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 3
+ if( !sTmp.empty() )
+ eRet.nOutlineLevel = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sTmp)); //the maximum outline level to examine
+ break;
+
+ case TOKEN_TEXT:
+ {
+ const sal_Int32 nStartText = sToken.indexOf( TOX_STYLE_DELIMITER );
+ if( nStartText>=0 && nStartText+1<sToken.getLength())
+ {
+ const sal_Int32 nEndText = sToken.indexOf( TOX_STYLE_DELIMITER,
+ nStartText + 1);
+ if( nEndText>=0 )
+ {
+ eRet.sText = sToken.copy( nStartText + 1,
+ nEndText - nStartText - 1);
+ }
+ }
+ }
+ break;
+
+ case TOKEN_TAB_STOP:
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 2
+ if( !sTmp.empty() )
+ eRet.nTabStopPosition = o3tl::toInt32(sTmp);
+
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 3
+ if( !sTmp.empty() )
+ eRet.eTabAlign = static_cast<SvxTabAdjust>(o3tl::toInt32(sTmp));
+
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 4
+ if( !sTmp.empty() )
+ eRet.cTabFillChar = sTmp[0];
+
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 5
+ if( !sTmp.empty() )
+ eRet.bWithTab = 0 != o3tl::toInt32(sTmp);
+ break;
+
+ case TOKEN_AUTHORITY:
+ eRet.nAuthorityField = o3tl::narrowing<sal_uInt16>(sAuthFieldEnum.toInt32());
+ break;
+ default: break;
+ }
+ return eRet;
+}
+
+SwFormTokensHelper::SwFormTokensHelper(const OUString & rPattern)
+{
+ sal_Int32 nCurPatternPos = 0;
+
+ while (nCurPatternPos < rPattern.getLength())
+ {
+ std::optional<SwFormToken> const oToken(
+ lcl_BuildToken(rPattern, nCurPatternPos));
+ if (oToken)
+ m_Tokens.push_back(*oToken);
+ }
+}
+
+// <- #i21237#
+
+void SwForm::SetPattern(sal_uInt16 nLevel, SwFormTokens&& rTokens)
+{
+ OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX");
+ m_aPattern[nLevel] = std::move(rTokens);
+}
+
+void SwForm::SetPattern(sal_uInt16 nLevel, const OUString & rStr)
+{
+ OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX");
+
+ SwFormTokensHelper aHelper(rStr);
+ m_aPattern[nLevel] = aHelper.GetTokens();
+}
+
+const SwFormTokens& SwForm::GetPattern(sal_uInt16 nLevel) const
+{
+ OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX");
+ return m_aPattern[nLevel];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/toxhlp.cxx b/sw/source/core/tox/toxhlp.cxx
new file mode 100644
index 000000000..6acff7399
--- /dev/null
+++ b/sw/source/core/tox/toxhlp.cxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/IndexEntrySupplier.hpp>
+#include <toxwrap.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+IndexEntrySupplierWrapper::IndexEntrySupplierWrapper()
+{
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ try {
+ m_xIES = i18n::IndexEntrySupplier::create(xContext);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "IndexEntrySupplierWrapper" );
+ }
+}
+
+IndexEntrySupplierWrapper::~IndexEntrySupplierWrapper()
+{
+}
+
+OUString IndexEntrySupplierWrapper::GetIndexKey( const OUString& rText,
+ const OUString& rTextReading,
+ const css::lang::Locale& rLocale ) const
+{
+ OUString sRet;
+ try {
+ sRet = m_xIES->getIndexKey( rText, rTextReading, rLocale );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "getIndexKey" );
+ }
+ return sRet;
+}
+
+OUString IndexEntrySupplierWrapper::GetFollowingText( bool bMorePages ) const
+{
+ OUString sRet;
+ try {
+ sRet = m_xIES->getIndexFollowPageWord( bMorePages, m_aLcl );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "getIndexFollowPageWord" );
+ }
+ return sRet;
+}
+
+css::uno::Sequence< OUString > IndexEntrySupplierWrapper::GetAlgorithmList( const css::lang::Locale& rLcl ) const
+{
+ uno::Sequence< OUString > sRet;
+
+ try {
+ sRet = m_xIES->getAlgorithmList( rLcl );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "getAlgorithmList" );
+ }
+ return sRet;
+}
+
+bool IndexEntrySupplierWrapper::LoadAlgorithm(
+ const css::lang::Locale& rLcl,
+ const OUString& sSortAlgorithm, tools::Long nOptions ) const
+{
+ bool bRet = false;
+ try {
+ bRet = m_xIES->loadAlgorithm( rLcl, sSortAlgorithm, nOptions );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "loadAlgorithm" );
+ }
+ return bRet;
+}
+
+sal_Int16 IndexEntrySupplierWrapper::CompareIndexEntry(
+ const OUString& rText1, const OUString& rTextReading1,
+ const css::lang::Locale& rLocale1,
+ const OUString& rText2, const OUString& rTextReading2,
+ const css::lang::Locale& rLocale2 ) const
+{
+ sal_Int16 nRet = 0;
+ try {
+ nRet = m_xIES->compareIndexEntry( rText1, rTextReading1, rLocale1,
+ rText2, rTextReading2, rLocale2 );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "compareIndexEntry" );
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/txmsrt.cxx b/sw/source/core/tox/txmsrt.cxx
new file mode 100644
index 000000000..8e2272b1f
--- /dev/null
+++ b/sw/source/core/tox/txmsrt.cxx
@@ -0,0 +1,989 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <txtfld.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <modeltoviewhelper.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <txttxmrk.hxx>
+#include <frmfmt.hxx>
+#include <fmtfld.hxx>
+#include <txmsrt.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <expfld.hxx>
+#include <authfld.hxx>
+#include <toxwrap.hxx>
+
+#include <strings.hrc>
+#include <reffld.hxx>
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+// Initialize strings
+SwTOIOptions SwTOXSortTabBase::nOpt = SwTOIOptions::NONE;
+
+SwTOXInternational::SwTOXInternational( LanguageType nLang, SwTOIOptions nOpt,
+ const OUString& rSortAlgorithm ) :
+ m_eLang( nLang ),
+ m_sSortAlgorithm(rSortAlgorithm),
+ m_nOptions( nOpt )
+{
+ Init();
+}
+
+SwTOXInternational::SwTOXInternational( const SwTOXInternational& rIntl ) :
+ m_eLang( rIntl.m_eLang ),
+ m_sSortAlgorithm(rIntl.m_sSortAlgorithm),
+ m_nOptions( rIntl.m_nOptions )
+{
+ Init();
+}
+
+void SwTOXInternational::Init()
+{
+ m_pIndexWrapper.reset( new IndexEntrySupplierWrapper() );
+
+ const lang::Locale aLcl( LanguageTag::convertToLocale( m_eLang ) );
+ m_pIndexWrapper->SetLocale( aLcl );
+
+ if(m_sSortAlgorithm.isEmpty())
+ {
+ Sequence < OUString > aSeq( m_pIndexWrapper->GetAlgorithmList( aLcl ));
+ if(aSeq.hasElements())
+ m_sSortAlgorithm = aSeq.getConstArray()[0];
+ }
+
+ if ( m_nOptions & SwTOIOptions::CaseSensitive )
+ m_pIndexWrapper->LoadAlgorithm( aLcl, m_sSortAlgorithm, 0 );
+ else
+ m_pIndexWrapper->LoadAlgorithm( aLcl, m_sSortAlgorithm, SW_COLLATOR_IGNORES );
+
+ m_pCharClass.reset( new CharClass( LanguageTag( aLcl )) );
+
+}
+
+SwTOXInternational::~SwTOXInternational()
+{
+ m_pCharClass.reset();
+ m_pIndexWrapper.reset();
+}
+
+OUString SwTOXInternational::ToUpper( const OUString& rStr, sal_Int32 nPos ) const
+{
+ return m_pCharClass->uppercase( rStr, nPos, 1 );
+}
+
+inline bool SwTOXInternational::IsNumeric( const OUString& rStr ) const
+{
+ return m_pCharClass->isNumeric( rStr );
+}
+
+sal_Int32 SwTOXInternational::Compare( const TextAndReading& rTaR1,
+ const lang::Locale& rLocale1,
+ const TextAndReading& rTaR2,
+ const lang::Locale& rLocale2 ) const
+{
+ return m_pIndexWrapper->CompareIndexEntry( rTaR1.sText, rTaR1.sReading, rLocale1,
+ rTaR2.sText, rTaR2.sReading, rLocale2 );
+}
+
+OUString SwTOXInternational::GetIndexKey( const TextAndReading& rTaR,
+ const lang::Locale& rLocale ) const
+{
+ return m_pIndexWrapper->GetIndexKey( rTaR.sText, rTaR.sReading, rLocale );
+}
+
+OUString SwTOXInternational::GetFollowingText( bool bMorePages ) const
+{
+ return m_pIndexWrapper->GetFollowingText( bMorePages );
+}
+
+// SortElement for TOX entries
+SwTOXSortTabBase::SwTOXSortTabBase( TOXSortType nTyp, const SwContentNode* pNd,
+ const SwTextTOXMark* pMark,
+ const SwTOXInternational* pInter,
+ const lang::Locale* pLocale )
+ : pTOXNd( nullptr ), pTextMark( pMark ), pTOXIntl( pInter ),
+ nPos( 0 ), nCntPos( 0 ), nType( o3tl::narrowing<sal_uInt16>(nTyp) )
+ , m_bValidText( false )
+{
+ if ( pLocale )
+ aLocale = *pLocale;
+
+ if( !pNd )
+ return;
+
+ sal_Int32 n = 0;
+ if( pTextMark )
+ n = pTextMark->GetStart();
+ SwTOXSource aTmp( pNd, n, pTextMark && pTextMark->GetTOXMark().IsMainEntry() );
+ aTOXSources.push_back(aTmp);
+
+ nPos = pNd->GetIndex();
+
+ switch( nTyp )
+ {
+ case TOX_SORT_CONTENT:
+ case TOX_SORT_PARA:
+ case TOX_SORT_TABLE:
+ // If they are in a special areas, we should get the position at the
+ // body
+ if( nPos < pNd->GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // Then get the 'anchor' (body) position
+ Point aPt;
+ std::pair<Point, bool> tmp(aPt, false);
+ const SwContentFrame *const pFrame = pNd->getLayoutFrame(
+ pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ if( pFrame )
+ {
+ SwPosition aPos( *pNd );
+ const SwDoc& rDoc = pNd->GetDoc();
+ bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the text node");
+ nPos = aPos.nNode.GetIndex();
+ nCntPos = aPos.nContent.GetIndex();
+ }
+ }
+ else
+ nCntPos = n;
+ break;
+ default: break;
+ }
+}
+
+std::pair<OUString, bool> SwTOXSortTabBase::GetURL(SwRootFrame const*const pLayout) const
+{
+ OUString typeName;
+ SwTOXType const& rType(*pTextMark->GetTOXMark().GetTOXType());
+ switch (rType.GetType())
+ {
+ case TOX_INDEX:
+ typeName = "A";
+ break;
+ case TOX_CONTENT:
+ typeName = "C";
+ break;
+ case TOX_USER:
+ typeName = "U" + rType.GetTypeName();
+ break;
+ default:
+ assert(false); // other tox can't have toxmarks as source
+ break;
+ }
+ OUString const decodedUrl( // counter will be added by caller!
+ OUStringChar(toxMarkSeparator) + pTextMark->GetTOXMark().GetText(pLayout)
+ + OUStringChar(toxMarkSeparator) + typeName
+ + OUStringChar(cMarkSeparator) + "toxmark" );
+
+ return std::make_pair(decodedUrl, true);
+}
+
+bool SwTOXSortTabBase::IsFullPara() const
+{
+ return false;
+}
+
+void SwTOXSortTabBase::FillText( SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16, SwRootFrame const*const) const
+{
+ rNd.InsertText( GetText().sText, rInsPos );
+}
+
+bool SwTOXSortTabBase::equivalent(const SwTOXSortTabBase& rCmp)
+{
+ bool bRet = nPos == rCmp.nPos && nCntPos == rCmp.nCntPos &&
+ (!aTOXSources[0].pNd || !rCmp.aTOXSources[0].pNd ||
+ aTOXSources[0].pNd == rCmp.aTOXSources[0].pNd );
+
+ if( TOX_SORT_CONTENT == nType )
+ {
+ bRet = bRet && pTextMark && rCmp.pTextMark &&
+ pTextMark->GetStart() == rCmp.pTextMark->GetStart();
+
+ if( bRet )
+ {
+ // Both pointers exist -> compare text
+ // else -> compare AlternativeText
+ const sal_Int32 *pEnd = pTextMark->End();
+ const sal_Int32 *pEndCmp = rCmp.pTextMark->End();
+
+ bRet = ( ( pEnd && pEndCmp ) || ( !pEnd && !pEndCmp ) ) &&
+ pTOXIntl->IsEqual( GetText(), GetLocale(),
+ rCmp.GetText(), rCmp.GetLocale() );
+ }
+ }
+ return bRet;
+}
+
+bool SwTOXSortTabBase::sort_lt(const SwTOXSortTabBase& rCmp)
+{
+ if( nPos < rCmp.nPos )
+ return true;
+
+ if( nPos == rCmp.nPos )
+ {
+ if( nCntPos < rCmp.nCntPos )
+ return true;
+
+ if( nCntPos == rCmp.nCntPos )
+ {
+ const SwNode* pFirst = aTOXSources[0].pNd;
+ const SwNode* pNext = rCmp.aTOXSources[0].pNd;
+
+ if( pFirst && pFirst == pNext )
+ {
+ if( TOX_SORT_CONTENT == nType && pTextMark && rCmp.pTextMark )
+ {
+ if( pTextMark->GetStart() < rCmp.pTextMark->GetStart() )
+ return true;
+
+ if( pTextMark->GetStart() == rCmp.pTextMark->GetStart() )
+ {
+ const sal_Int32 *pEnd = pTextMark->End();
+ const sal_Int32 *pEndCmp = rCmp.pTextMark->End();
+
+ // Both pointers exist -> compare text
+ // else -> compare AlternativeText
+ if( ( pEnd && pEndCmp ) || ( !pEnd && !pEndCmp ) )
+ {
+ return pTOXIntl->IsLess( GetText(), GetLocale(),
+ rCmp.GetText(), rCmp.GetLocale() );
+ }
+ if( pEnd && !pEndCmp )
+ return true;
+ }
+ }
+ }
+ else if( pFirst && pFirst->IsTextNode() &&
+ pNext && pNext->IsTextNode() )
+ return ::IsFrameBehind( *static_cast<const SwTextNode*>(pNext), nCntPos,
+ *static_cast<const SwTextNode*>(pFirst), nCntPos );
+ }
+ }
+ return false;
+}
+
+// Sorted keyword entry
+SwTOXIndex::SwTOXIndex( const SwTextNode& rNd,
+ const SwTextTOXMark* pMark, SwTOIOptions nOptions,
+ sal_uInt8 nKyLevel,
+ const SwTOXInternational& rIntl,
+ const lang::Locale& rLocale )
+ : SwTOXSortTabBase( TOX_SORT_INDEX, &rNd, pMark, &rIntl, &rLocale ),
+ nKeyLevel(nKyLevel)
+{
+ nPos = rNd.GetIndex();
+ nOpt = nOptions;
+}
+
+// Compare keywords. Only relates to the text.
+
+bool SwTOXIndex::equivalent(const SwTOXSortTabBase& rCmpBase)
+{
+ const SwTOXIndex& rCmp = static_cast<const SwTOXIndex&>(rCmpBase);
+
+ // Respect case taking dependencies into account
+ if(GetLevel() != rCmp.GetLevel() || nKeyLevel != rCmp.nKeyLevel)
+ return false;
+
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+
+ bool bRet = pTOXIntl->IsEqual( GetText(), GetLocale(),
+ rCmp.GetText(), rCmp.GetLocale() );
+
+ // If we don't summarize we need to evaluate the Pos
+ if(bRet && !(GetOptions() & SwTOIOptions::SameEntry))
+ bRet = nPos == rCmp.nPos;
+
+ return bRet;
+}
+
+// operator, only depends on the text
+
+bool SwTOXIndex::sort_lt(const SwTOXSortTabBase& rCmpBase)
+{
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+
+ const TextAndReading aMyTaR(GetText());
+ const TextAndReading aOtherTaR(rCmpBase.GetText());
+
+ bool bRet = GetLevel() == rCmpBase.GetLevel() &&
+ pTOXIntl->IsLess( aMyTaR, GetLocale(),
+ aOtherTaR, rCmpBase.GetLocale() );
+
+ // If we don't summarize we need to evaluate the Pos
+ if( !bRet && !(GetOptions() & SwTOIOptions::SameEntry) )
+ {
+ bRet = pTOXIntl->IsEqual( aMyTaR, GetLocale(),
+ aOtherTaR, rCmpBase.GetLocale() ) &&
+ nPos < rCmpBase.nPos;
+ }
+
+ return bRet;
+}
+
+// The keyword itself
+
+TextAndReading SwTOXIndex::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+ const SwTOXMark& rTOXMark = pTextMark->GetTOXMark();
+
+ TextAndReading aRet;
+ switch(nKeyLevel)
+ {
+ case FORM_PRIMARY_KEY :
+ {
+ aRet.sText = rTOXMark.GetPrimaryKey();
+ aRet.sReading = rTOXMark.GetPrimaryKeyReading();
+ }
+ break;
+ case FORM_SECONDARY_KEY :
+ {
+ aRet.sText = rTOXMark.GetSecondaryKey();
+ aRet.sReading = rTOXMark.GetSecondaryKeyReading();
+ }
+ break;
+ case FORM_ENTRY :
+ {
+ aRet.sText = rTOXMark.GetText(pLayout);
+ aRet.sReading = rTOXMark.GetTextReading();
+ }
+ break;
+ }
+ // if SwTOIOptions::InitialCaps is set, first character is to be capitalized
+ if( SwTOIOptions::InitialCaps & nOpt && pTOXIntl && !aRet.sText.isEmpty())
+ {
+ aRet.sText = pTOXIntl->ToUpper( aRet.sText, 0 ) + aRet.sText.subView(1);
+ }
+
+ return aRet;
+}
+
+void SwTOXIndex::FillText( SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!"sw_redlinehide: this is dead code, Bibliography only has SwTOXAuthority");
+ const sal_Int32* pEnd = pTextMark->End();
+
+ TextAndReading aRet;
+ if( pEnd && !pTextMark->GetTOXMark().IsAlternativeText() &&
+ !(GetOptions() & SwTOIOptions::KeyAsEntry))
+ {
+ aRet.sText = static_cast<const SwTextNode*>(aTOXSources[0].pNd)->GetExpandText(
+ pLayout,
+ pTextMark->GetStart(),
+ *pEnd - pTextMark->GetStart(),
+ false, false, false,
+ ExpandMode::ExpandFootnote
+ | (pLayout && pLayout->IsHideRedlines()
+ ? ExpandMode::HideDeletions
+ : ExpandMode(0)));
+ if(SwTOIOptions::InitialCaps & nOpt && pTOXIntl && !aRet.sText.isEmpty())
+ {
+ aRet.sText = pTOXIntl->ToUpper( aRet.sText, 0 ) + aRet.sText.subView(1);
+ }
+ }
+ else
+ aRet = GetText();
+
+ rNd.InsertText( aRet.sText, rInsPos );
+}
+
+sal_uInt16 SwTOXIndex::GetLevel() const
+{
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+
+ sal_uInt16 nForm = FORM_PRIMARY_KEY;
+
+ if( !(GetOptions() & SwTOIOptions::KeyAsEntry)&&
+ !pTextMark->GetTOXMark().GetPrimaryKey().isEmpty() )
+ {
+ nForm = FORM_SECONDARY_KEY;
+ if( !pTextMark->GetTOXMark().GetSecondaryKey().isEmpty() )
+ nForm = FORM_ENTRY;
+ }
+ return nForm;
+}
+
+// Key and separator
+SwTOXCustom::SwTOXCustom(const TextAndReading& rKey,
+ sal_uInt16 nLevel,
+ const SwTOXInternational& rIntl,
+ const lang::Locale& rLocale )
+ : SwTOXSortTabBase( TOX_SORT_CUSTOM, nullptr, nullptr, &rIntl, &rLocale ),
+ m_aKey(rKey), nLev(nLevel)
+{
+}
+
+bool SwTOXCustom::equivalent(const SwTOXSortTabBase& rCmpBase)
+{
+ return GetLevel() == rCmpBase.GetLevel() &&
+ pTOXIntl->IsEqual( GetText(), GetLocale(),
+ rCmpBase.GetText(), rCmpBase.GetLocale() );
+}
+
+bool SwTOXCustom::sort_lt(const SwTOXSortTabBase& rCmpBase)
+{
+ return GetLevel() <= rCmpBase.GetLevel() &&
+ pTOXIntl->IsLess( GetText(), GetLocale(),
+ rCmpBase.GetText(), rCmpBase.GetLocale() );
+}
+
+sal_uInt16 SwTOXCustom::GetLevel() const
+{
+ return nLev;
+}
+
+TextAndReading SwTOXCustom::GetText_Impl(SwRootFrame const*const) const
+{
+ return m_aKey;
+}
+
+// Sorts the TOX entries
+SwTOXContent::SwTOXContent( const SwTextNode& rNd, const SwTextTOXMark* pMark,
+ const SwTOXInternational& rIntl)
+ : SwTOXSortTabBase( TOX_SORT_CONTENT, &rNd, pMark, &rIntl )
+{
+}
+
+// The content's text
+
+TextAndReading SwTOXContent::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ const sal_Int32* pEnd = pTextMark->End();
+ if( pEnd && !pTextMark->GetTOXMark().IsAlternativeText() )
+ {
+ return TextAndReading(
+ static_cast<const SwTextNode*>(aTOXSources[0].pNd)->GetExpandText(
+ pLayout,
+ pTextMark->GetStart(),
+ *pEnd - pTextMark->GetStart(),
+ false, false, false,
+ ExpandMode::ExpandFootnote
+ | (pLayout && pLayout->IsHideRedlines()
+ ? ExpandMode::HideDeletions
+ : ExpandMode(0))),
+ pTextMark->GetTOXMark().GetTextReading());
+ }
+
+ return TextAndReading(pTextMark->GetTOXMark().GetAlternativeText(), OUString());
+}
+
+void SwTOXContent::FillText(SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!"sw_redlinehide: this is dead code, Bibliography only has SwTOXAuthority");
+ const sal_Int32* pEnd = pTextMark->End();
+ if( pEnd && !pTextMark->GetTOXMark().IsAlternativeText() )
+ // sw_redlinehide: this probably won't HideDeletions
+ static_cast<const SwTextNode*>(aTOXSources[0].pNd)->CopyExpandText(
+ rNd, &rInsPos, pTextMark->GetStart(),
+ *pEnd - pTextMark->GetStart(), pLayout);
+ else
+ {
+ rNd.InsertText( GetText().sText, rInsPos );
+ }
+}
+
+// The level for displaying it
+
+sal_uInt16 SwTOXContent::GetLevel() const
+{
+ return pTextMark->GetTOXMark().GetLevel();
+}
+
+// TOX assembled from paragraphs
+// Watch out for OLE/graphics when sorting!
+// The position must not come from the document, but from the "anchor"!
+SwTOXPara::SwTOXPara(SwContentNode& rNd, SwTOXElement eT, sal_uInt16 nLevel, const OUString& sSeqName)
+ : SwTOXSortTabBase( TOX_SORT_PARA, &rNd, nullptr, nullptr ),
+ eType( eT ),
+ m_nLevel(nLevel),
+ nStartIndex(0),
+ nEndIndex(-1),
+ m_sSequenceName( sSeqName )
+{
+ // tdf#123313 create any missing bookmarks *before* generating ToX nodes!
+ switch (eType)
+ {
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ assert(rNd.IsTextNode());
+ rNd.GetDoc().getIDocumentMarkAccess()->getMarkForTextNode(
+ *rNd.GetTextNode(), IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
+ break;
+ default:
+ break;
+ }
+}
+
+TextAndReading SwTOXPara::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ const SwContentNode* pNd = aTOXSources[0].pNd;
+ switch( eType )
+ {
+ case SwTOXElement::Sequence:
+ if (nStartIndex != 0 || nEndIndex != -1)
+ {
+ // sw_redlinehide: "captions" are a rather fuzzily defined concept anyway
+ return TextAndReading(static_cast<const SwTextNode*>(pNd)->GetExpandText(
+ pLayout,
+ nStartIndex,
+ nEndIndex == -1 ? -1 : nEndIndex - nStartIndex,
+ false, false, false,
+ pLayout && pLayout->IsHideRedlines()
+ ? ExpandMode::HideDeletions
+ : ExpandMode(0)),
+ OUString());
+ }
+ [[fallthrough]];
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ {
+ assert(nStartIndex == 0);
+ assert(nEndIndex == -1);
+ return TextAndReading(sw::GetExpandTextMerged(
+ pLayout, *static_cast<const SwTextNode*>(pNd),
+ false, false, ExpandMode::HideInvisible | ExpandMode::HideDeletions),
+ OUString());
+ }
+ break;
+
+ case SwTOXElement::Ole:
+ case SwTOXElement::Graphic:
+ case SwTOXElement::Frame:
+ {
+ // Find the FlyFormat; the object/graphic name is there
+ SwFrameFormat* pFly = pNd->GetFlyFormat();
+ if( pFly )
+ return TextAndReading(pFly->GetName(), OUString());
+
+ OSL_ENSURE( false, "Graphic/object without name" );
+ TranslateId pId = SwTOXElement::Ole == eType
+ ? STR_OBJECT_DEFNAME
+ : SwTOXElement::Graphic == eType
+ ? STR_GRAPHIC_DEFNAME
+ : STR_FRAME_DEFNAME;
+ return TextAndReading(SwResId(pId), OUString());
+ }
+ break;
+ default: break;
+ }
+ return TextAndReading();
+}
+
+void SwTOXPara::FillText( SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!"sw_redlinehide: this is dead code, Bibliography only has SwTOXAuthority");
+ if( SwTOXElement::Template == eType || SwTOXElement::Sequence == eType || SwTOXElement::OutlineLevel == eType)
+ {
+ const SwTextNode* pSrc = static_cast<const SwTextNode*>(aTOXSources[0].pNd);
+ if (SwTOXElement::Sequence == eType
+ && (nStartIndex != 0 || nEndIndex != -1))
+ {
+ pSrc->CopyExpandText( rNd, &rInsPos, nStartIndex,
+ nEndIndex == -1 ? -1 : nEndIndex - nStartIndex,
+ pLayout, false, false, true );
+ }
+ else
+ {
+ assert(nStartIndex == 0);
+ assert(nEndIndex == -1);
+ // sw_redlinehide: this probably won't HideDeletions
+ pSrc->CopyExpandText( rNd, &rInsPos, 0, -1,
+ pLayout, false, false, true );
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pSrc->getLayoutFrame(pLayout)))
+ {
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ // pSrc already copied above
+ assert(pSrc == pMerged->pParaPropsNode);
+ for (SwNodeOffset i = pSrc->GetIndex() + 1;
+ i <= pMerged->pLastNode->GetIndex(); ++i)
+ {
+ SwNode *const pTmp(pSrc->GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+
+ pTmp->GetTextNode()->CopyExpandText(
+ rNd, &rInsPos, 0, -1,
+ pLayout, false, false, false );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ rNd.InsertText( GetText().sText.replace('\t', ' '), rInsPos );
+ }
+}
+
+sal_uInt16 SwTOXPara::GetLevel() const
+{
+ sal_uInt16 nRet = m_nLevel;
+ const SwContentNode* pNd = aTOXSources[0].pNd;
+
+ if( SwTOXElement::OutlineLevel == eType && pNd->GetTextNode() )
+ {
+ const int nTmp = static_cast<const SwTextNode*>(pNd)->GetAttrOutlineLevel();
+ if(nTmp != 0 )
+ nRet = o3tl::narrowing<sal_uInt16>(nTmp);
+ }
+ return nRet;
+}
+
+std::pair<OUString, bool> SwTOXPara::GetURL(SwRootFrame const*const) const
+{
+ OUString aText;
+ const SwContentNode* pNd = aTOXSources[0].pNd;
+ switch( eType )
+ {
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ {
+ const SwTextNode * pTextNd = pNd->GetTextNode();
+
+ SwDoc& rDoc = const_cast<SwDoc&>( pTextNd->GetDoc() );
+ // tdf#123313: this *must not* create a bookmark, its Undo would
+ // be screwed! create it as preparatory step, in ctor!
+ ::sw::mark::IMark const * const pMark = rDoc.getIDocumentMarkAccess()->getMarkForTextNode(
+ *pTextNd,
+ IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
+ aText = "#" + pMark->GetName();
+ }
+ break;
+
+ case SwTOXElement::Ole:
+ case SwTOXElement::Graphic:
+ case SwTOXElement::Frame:
+ {
+ // Find the FlyFormat; the object/graphic name is there
+ SwFrameFormat* pFly = pNd->GetFlyFormat();
+ if( pFly )
+ {
+ aText = "#" + pFly->GetName() + OUStringChar(cMarkSeparator);
+ const char* pStr;
+ switch( eType )
+ {
+ case SwTOXElement::Ole: pStr = "ole"; break;
+ case SwTOXElement::Graphic: pStr = "graphic"; break;
+ case SwTOXElement::Frame: pStr = "frame"; break;
+ default: pStr = nullptr;
+ }
+ if( pStr )
+ aText += OUString::createFromAscii( pStr );
+ }
+ }
+ break;
+ case SwTOXElement::Sequence:
+ {
+ aText = "#" + m_sSequenceName + OUStringChar(cMarkSeparator)
+ + "sequence";
+ }
+ break;
+ default: break;
+ }
+ return std::make_pair(aText, false);
+}
+
+bool SwTOXPara::IsFullPara() const
+{
+ switch (eType)
+ {
+ case SwTOXElement::Sequence:
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ return nStartIndex == 0 && nEndIndex == -1;
+ default:
+ return false;
+ }
+}
+
+// Table
+SwTOXTable::SwTOXTable( const SwContentNode& rNd )
+ : SwTOXSortTabBase( TOX_SORT_TABLE, &rNd, nullptr, nullptr ),
+ nLevel(FORM_ALPHA_DELIMITER)
+{
+}
+
+TextAndReading SwTOXTable::GetText_Impl(SwRootFrame const*const) const
+{
+ const SwNode* pNd = aTOXSources[0].pNd;
+ if( pNd )
+ {
+ const SwTableNode* pTableNd =
+ pNd->FindTableNode();
+ if (pTableNd)
+ {
+ return TextAndReading(pTableNd->GetTable().GetFrameFormat()->GetName(), OUString());
+ }
+ }
+
+ OSL_ENSURE( false, "Where's my table?" );
+ return TextAndReading(SwResId( STR_TABLE_DEFNAME ), OUString());
+}
+
+sal_uInt16 SwTOXTable::GetLevel() const
+{
+ return nLevel;
+}
+
+std::pair<OUString, bool> SwTOXTable::GetURL(SwRootFrame const*const) const
+{
+ const SwNode* pNd = aTOXSources[0].pNd;
+ if (!pNd)
+ return std::make_pair(OUString(), false);
+
+ pNd = pNd->FindTableNode();
+ if (!pNd)
+ return std::make_pair(OUString(), false);
+
+ const OUString sName = static_cast<const SwTableNode*>(pNd)->GetTable().GetFrameFormat()->GetName();
+ if ( sName.isEmpty() )
+ return std::make_pair(OUString(), false);
+
+ return std::make_pair("#" + sName + OUStringChar(cMarkSeparator) + "table", false);
+}
+
+SwTOXAuthority::SwTOXAuthority( const SwContentNode& rNd,
+ SwFormatField& rField, const SwTOXInternational& rIntl ) :
+ SwTOXSortTabBase( TOX_SORT_AUTHORITY, &rNd, nullptr, &rIntl ),
+ m_rField(rField)
+{
+ if(rField.GetTextField())
+ nCntPos = rField.GetTextField()->GetStart();
+}
+
+sal_uInt16 SwTOXAuthority::GetLevel() const
+{
+ OUString sText(static_cast<SwAuthorityField*>(m_rField.GetField())->GetFieldText(AUTH_FIELD_AUTHORITY_TYPE));
+ //#i18655# the level '0' is the heading level therefore the values are incremented here
+ sal_uInt16 nRet = 1;
+ if( pTOXIntl->IsNumeric( sText ) )
+ {
+ nRet = sText.toUInt32();
+ nRet++;
+ }
+ //illegal values are also set to 'ARTICLE' as non-numeric values are
+ if(nRet > AUTH_TYPE_END)
+ nRet = 1;
+ return nRet;
+}
+
+static OUString lcl_GetText(SwFormatField const& rField, SwRootFrame const*const pLayout)
+{
+ return rField.GetField()->ExpandField(true, pLayout);
+}
+
+TextAndReading SwTOXAuthority::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ return TextAndReading(lcl_GetText(m_rField, pLayout), OUString());
+}
+
+OUString SwTOXAuthority::GetText(sal_uInt16 nAuthField, const SwRootFrame* pLayout) const
+{
+ SwAuthorityField* pField = static_cast<SwAuthorityField*>(m_rField.GetField());
+ OUString sText;
+ if(AUTH_FIELD_IDENTIFIER == nAuthField)
+ {
+ sText = lcl_GetText(m_rField, pLayout);
+ const SwAuthorityFieldType* pType = static_cast<const SwAuthorityFieldType*>(pField->GetTyp());
+ sal_Unicode cChar = pType->GetPrefix();
+ if(cChar && cChar != ' ')
+ sText = sText.copy(1);
+ cChar = pType->GetSuffix();
+ if(cChar && cChar != ' ')
+ sText = sText.copy(0, sText.getLength() - 1);
+ }
+ else if(AUTH_FIELD_AUTHORITY_TYPE == nAuthField)
+ {
+ sal_uInt16 nLevel = GetLevel();
+ if(nLevel)
+ sText = SwAuthorityFieldType::GetAuthTypeName(static_cast<ToxAuthorityType>(--nLevel));
+ }
+ else
+ sText = pField->GetFieldText(static_cast<ToxAuthorityField>(nAuthField));
+ return sText;
+}
+
+OUString SwTOXAuthority::GetSourceURL(const OUString& rText)
+{
+ OUString aText = rText;
+
+ uno::Reference<uri::XUriReferenceFactory> xUriReferenceFactory
+ = uri::UriReferenceFactory::create(comphelper::getProcessComponentContext());
+ uno::Reference<uri::XUriReference> xUriRef;
+ try
+ {
+ xUriRef = xUriReferenceFactory->parse(aText);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("sw.core",
+ "SwTOXAuthority::GetSourceURL: failed to parse url: " << rException.Message);
+ }
+ if (xUriRef.is() && xUriRef->getFragment().startsWith("page="))
+ {
+ xUriRef->clearFragment();
+ aText = xUriRef->getUriReference();
+ }
+
+ return aText;
+}
+
+void SwTOXAuthority::FillText(SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16 nAuthField,
+ SwRootFrame const* const pLayout) const
+{
+ OUString aText = GetText(nAuthField, pLayout);
+ if (nAuthField == AUTH_FIELD_URL)
+ {
+ aText = GetSourceURL(aText);
+
+ // Convert URL to a relative one if requested.
+ SwDoc* pDoc = static_cast<SwAuthorityFieldType*>(m_rField.GetField()->GetTyp())->GetDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ const OUString aBaseURL = pDocShell->getDocumentBaseURL();
+ std::u16string_view aBaseURIScheme;
+ sal_Int32 nSep = aBaseURL.indexOf(':');
+ if (nSep != -1)
+ {
+ aBaseURIScheme = aBaseURL.subView(0, nSep);
+ }
+
+ uno::Reference<uri::XUriReferenceFactory> xUriReferenceFactory
+ = uri::UriReferenceFactory::create(comphelper::getProcessComponentContext());
+ uno::Reference<uri::XUriReference> xUriRef;
+ try
+ {
+ xUriRef = xUriReferenceFactory->parse(aText);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("sw.core",
+ "SwTOXAuthority::FillText: failed to parse url: " << rException.Message);
+ }
+
+ bool bSaveRelFSys = officecfg::Office::Common::Save::URL::FileSystem::get();
+ if (xUriRef.is() && bSaveRelFSys && xUriRef->getScheme() == aBaseURIScheme)
+ {
+ aText = INetURLObject::GetRelURL(aBaseURL, aText);
+ }
+ }
+
+ rNd.InsertText(aText, rInsPos);
+}
+
+bool SwTOXAuthority::equivalent(const SwTOXSortTabBase& rCmp)
+{
+ if (nType != rCmp.nType)
+ {
+ return false;
+ }
+
+ // Compare our SwAuthEntry and rCmp's SwAuthEntry, but the URL is considered equivalent, as long
+ // as it only differs in a page number, as that's still the same source.
+ const SwAuthEntry* pThis = static_cast<SwAuthorityField*>(m_rField.GetField())->GetAuthEntry();
+ const SwAuthEntry* pOther = static_cast<SwAuthorityField*>(
+ static_cast<const SwTOXAuthority&>(rCmp).m_rField.GetField())
+ ->GetAuthEntry();
+ if (pThis == pOther)
+ {
+ return true;
+ }
+
+ for (int i = 0; i < AUTH_FIELD_END; ++i)
+ {
+ auto eField = static_cast<ToxAuthorityField>(i);
+ if (eField == AUTH_FIELD_URL)
+ {
+ if (GetSourceURL(pThis->GetAuthorField(AUTH_FIELD_URL))
+ != GetSourceURL(pOther->GetAuthorField(AUTH_FIELD_URL)))
+ {
+ return false;
+ }
+ continue;
+ }
+
+ if (pThis->GetAuthorField(eField) != pOther->GetAuthorField(eField))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SwTOXAuthority::sort_lt(const SwTOXSortTabBase& rBase)
+{
+ bool bRet = false;
+ SwAuthorityField* pField = static_cast<SwAuthorityField*>(m_rField.GetField());
+ SwAuthorityFieldType* pType = static_cast<SwAuthorityFieldType*>(
+ pField->GetTyp());
+ if(pType->IsSortByDocument())
+ bRet = SwTOXSortTabBase::sort_lt(rBase);
+ else
+ {
+ SwAuthorityField* pCmpField =
+ static_cast<SwAuthorityField*>(static_cast<const SwTOXAuthority&>(rBase).m_rField.GetField());
+
+ for(sal_uInt16 i = 0; i < pType->GetSortKeyCount(); i++)
+ {
+ const SwTOXSortKey* pKey = pType->GetSortKey(i);
+ const TextAndReading aMy(pField->GetFieldText(pKey->eField), OUString());
+ const TextAndReading aOther(pCmpField->GetFieldText(pKey->eField), OUString());
+
+ sal_Int32 nComp = pTOXIntl->Compare( aMy, GetLocale(),
+ aOther, rBase.GetLocale() );
+
+ if( nComp )
+ {
+ bRet = (-1 == nComp) == pKey->bSortAscending;
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/SwGrammarContact.cxx b/sw/source/core/txtnode/SwGrammarContact.cxx
new file mode 100644
index 000000000..0b3b49da4
--- /dev/null
+++ b/sw/source/core/txtnode/SwGrammarContact.cxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <vcl/timer.hxx>
+#include <IGrammarContact.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <txtfrm.hxx>
+#include <svl/listener.hxx>
+
+namespace {
+
+/*
+ * This class is responsible for the delayed display of grammar checks when a paragraph is edited
+ * It's a client of the paragraph the cursor points to.
+ * If the cursor position changes, updateCursorPosition has to be called
+ * If the grammar checker wants to set a grammar marker at a paragraph, he has to request
+ * the grammar list from this class. If the requested paragraph is not edited, it returns
+ * the normal grammar list. But if the paragraph is the active one, a proxy list will be returned and
+ * all changes are set in this proxy list. If the cursor leaves the paragraph the proxy list
+ * will replace the old list. If the grammar checker has completed the paragraph ('setChecked')
+ * then a timer is setup which replaces the old list as well.
+ */
+class SwGrammarContact final : public IGrammarContact, public SvtListener
+{
+ Timer m_aTimer;
+ std::unique_ptr<SwGrammarMarkUp> m_pProxyList;
+ bool m_isFinished;
+ SwTextNode* m_pTextNode;
+ DECL_LINK( TimerRepaint, Timer *, void );
+
+public:
+ SwGrammarContact();
+ virtual ~SwGrammarContact() override { m_aTimer.Stop(); }
+
+ // (pure) virtual functions of IGrammarContact
+ virtual void updateCursorPosition( const SwPosition& rNewPos ) override;
+ virtual SwGrammarMarkUp* getGrammarCheck( SwTextNode& rTextNode, bool bCreate ) override;
+ virtual void finishGrammarCheck( SwTextNode& rTextNode ) override;
+ void CheckBroadcaster()
+ {
+ if(HasBroadcaster())
+ return;
+ m_pTextNode = nullptr;
+ m_pProxyList.reset();
+ }
+};
+
+}
+
+SwGrammarContact::SwGrammarContact()
+ : m_aTimer( "sw::SwGrammarContact TimerRepaint" ),
+ m_isFinished( false ),
+ m_pTextNode(nullptr)
+{
+ m_aTimer.SetTimeout( 2000 ); // Repaint of grammar check after 'setChecked'
+ m_aTimer.SetInvokeHandler( LINK(this, SwGrammarContact, TimerRepaint) );
+}
+
+IMPL_LINK( SwGrammarContact, TimerRepaint, Timer *, pTimer, void )
+{
+ CheckBroadcaster();
+ if( pTimer )
+ {
+ pTimer->Stop();
+ if( m_pTextNode )
+ { //Replace the old wrong list by the proxy list and repaint all frames
+ m_pTextNode->SetGrammarCheck( std::move(m_pProxyList) );
+ SwTextFrame::repaintTextFrames( *m_pTextNode );
+ }
+ }
+}
+
+/* I'm always a client of the current paragraph */
+void SwGrammarContact::updateCursorPosition( const SwPosition& rNewPos )
+{
+ CheckBroadcaster();
+ SwTextNode* pTextNode = rNewPos.nNode.GetNode().GetTextNode();
+ if( pTextNode == m_pTextNode ) // paragraph has been changed
+ return;
+
+ m_aTimer.Stop();
+ if( m_pTextNode ) // My last paragraph has been left
+ {
+ if( m_pProxyList )
+ { // replace old list by the proxy list and repaint
+ m_pTextNode->SetGrammarCheck( std::move(m_pProxyList) );
+ SwTextFrame::repaintTextFrames( *m_pTextNode );
+ }
+ EndListeningAll();
+ }
+ if( pTextNode )
+ {
+ m_pTextNode = pTextNode;
+ EndListeningAll();
+ StartListening(pTextNode->GetNotifier()); // welcome new paragraph
+ }
+}
+
+/* deliver a grammar check list for the given text node */
+SwGrammarMarkUp* SwGrammarContact::getGrammarCheck( SwTextNode& rTextNode, bool bCreate )
+{
+ SwGrammarMarkUp *pRet = nullptr;
+ CheckBroadcaster();
+ if( m_pTextNode == &rTextNode ) // hey, that's my current paragraph!
+ { // so you will get a proxy list...
+ if( bCreate )
+ {
+ if( m_isFinished )
+ {
+ m_pProxyList.reset();
+ }
+ if( !m_pProxyList )
+ {
+ if( rTextNode.GetGrammarCheck() )
+ m_pProxyList.reset( static_cast<SwGrammarMarkUp*>(rTextNode.GetGrammarCheck()->Clone()) );
+ else
+ {
+ m_pProxyList.reset( new SwGrammarMarkUp() );
+ m_pProxyList->SetInvalid( 0, COMPLETE_STRING );
+ }
+ }
+ m_isFinished = false;
+ }
+ pRet = m_pProxyList.get();
+ }
+ else
+ {
+ pRet = rTextNode.GetGrammarCheck(); // do you have already a list?
+ if( bCreate && !pRet ) // do you want to create a list?
+ {
+ pRet = new SwGrammarMarkUp();
+ pRet->SetInvalid( 0, COMPLETE_STRING );
+ rTextNode.SetGrammarCheck( std::unique_ptr<SwGrammarMarkUp>(pRet) );
+ rTextNode.SetGrammarCheckDirty( true );
+ }
+ }
+ return pRet;
+}
+
+void SwGrammarContact::finishGrammarCheck( SwTextNode& rTextNode )
+{
+ CheckBroadcaster();
+ if( &rTextNode != m_pTextNode ) // not my paragraph
+ SwTextFrame::repaintTextFrames( rTextNode ); // can be repainted directly
+ else
+ {
+ if( m_pProxyList )
+ {
+ m_isFinished = true;
+ m_aTimer.Start(); // will replace old list and repaint with delay
+ }
+ else if( m_pTextNode->GetGrammarCheck() )
+ { // all grammar problems seems to be gone, no delay needed
+ m_pTextNode->ClearGrammarCheck();
+ SwTextFrame::repaintTextFrames( *m_pTextNode );
+ }
+ }
+}
+
+IGrammarContact* createGrammarContact()
+{
+ return new SwGrammarContact();
+}
+
+void finishGrammarCheck( SwTextNode& rTextNode )
+{
+ IGrammarContact* pGrammarContact = getGrammarContact( rTextNode );
+ if( pGrammarContact )
+ pGrammarContact->finishGrammarCheck( rTextNode );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/atrfld.cxx b/sw/source/core/txtnode/atrfld.cxx
new file mode 100644
index 000000000..d6fab571b
--- /dev/null
+++ b/sw/source/core/txtnode/atrfld.cxx
@@ -0,0 +1,756 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fmtfld.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <fldbas.hxx>
+#include <txtfld.hxx>
+#include <txtannotationfld.hxx>
+#include <docfld.hxx>
+#include <docufld.hxx>
+#include <doc.hxx>
+
+#include <pam.hxx>
+#include <reffld.hxx>
+#include <ddefld.hxx>
+#include <usrfld.hxx>
+#include <expfld.hxx>
+#include <ndtxt.hxx>
+#include <calc.hxx>
+#include <hints.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fieldhint.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+
+// constructor for default item in attribute-pool
+SwFormatField::SwFormatField( sal_uInt16 nWhich )
+ : SfxPoolItem( nWhich )
+ , SfxBroadcaster()
+ , mpTextField( nullptr )
+{
+}
+
+SwFormatField::SwFormatField( const SwField &rField )
+ : SfxPoolItem( RES_TXTATR_FIELD )
+ , SfxBroadcaster()
+ , mpField( rField.CopyField() )
+ , mpTextField( nullptr )
+{
+ rField.GetTyp()->Add(this);
+ if ( mpField->GetTyp()->Which() == SwFieldIds::Input )
+ {
+ // input field in-place editing
+ SetWhich( RES_TXTATR_INPUTFIELD );
+ static_cast<SwInputField*>(mpField.get())->SetFormatField( *this );
+ }
+ else if (mpField->GetTyp()->Which() == SwFieldIds::SetExp)
+ {
+ // see SwWrtShell::StartInputFieldDlg
+ 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 )
+ {
+ // text annotation field
+ SetWhich( RES_TXTATR_ANNOTATION );
+ }
+}
+
+// #i24434#
+// Since Items are used in ItemPool and in default constructed ItemSets with
+// full pool range, all items need to be clonable. Thus, this one needed to be
+// corrected
+SwFormatField::SwFormatField( const SwFormatField& rAttr )
+ : SfxPoolItem( rAttr )
+ , SfxBroadcaster()
+ , mpTextField( nullptr )
+{
+ if ( !rAttr.mpField )
+ return;
+
+ rAttr.mpField->GetTyp()->Add(this);
+ mpField = rAttr.mpField->CopyField();
+ if ( mpField->GetTyp()->Which() == SwFieldIds::Input )
+ {
+ // input field in-place editing
+ SetWhich( RES_TXTATR_INPUTFIELD );
+ SwInputField *pField = dynamic_cast<SwInputField*>(mpField.get());
+ assert(pField);
+ if (pField)
+ pField->SetFormatField( *this );
+ }
+ 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
+ pSetField->SetFormatField(*this);
+ }
+ else if ( mpField->GetTyp()->Which() == SwFieldIds::Postit )
+ {
+ // text annotation field
+ SetWhich( RES_TXTATR_ANNOTATION );
+ }
+}
+
+SwFormatField::~SwFormatField()
+{
+ SwFieldType* pType = mpField ? mpField->GetTyp() : nullptr;
+
+ if (pType && pType->Which() == SwFieldIds::Database)
+ pType = nullptr; // DB field types destroy themselves
+
+ Broadcast( SwFormatFieldHint( this, SwFormatFieldHintWhich::REMOVED ) );
+ mpField.reset();
+
+ // some fields need to delete their field type
+ if( !(pType && pType->HasOnlyOneListener()) )
+ return;
+
+ bool bDel = false;
+ switch( pType->Which() )
+ {
+ case SwFieldIds::User:
+ bDel = static_cast<SwUserFieldType*>(pType)->IsDeleted();
+ break;
+
+ case SwFieldIds::SetExp:
+ bDel = static_cast<SwSetExpFieldType*>(pType)->IsDeleted();
+ break;
+
+ case SwFieldIds::Dde:
+ bDel = static_cast<SwDDEFieldType*>(pType)->IsDeleted();
+ break;
+ default: break;
+ }
+
+ if( bDel )
+ {
+ // unregister before deleting
+ pType->Remove( this );
+ delete pType;
+ }
+}
+
+void SwFormatField::RegisterToFieldType( SwFieldType& rType )
+{
+ rType.Add(this);
+}
+
+void SwFormatField::SetField(std::unique_ptr<SwField> _pField)
+{
+ mpField = std::move(_pField);
+ if ( mpField->GetTyp()->Which() == SwFieldIds::Input )
+ {
+ static_cast<SwInputField* >(mpField.get())->SetFormatField( *this );
+ }
+ else if (mpField->GetTyp()->Which() == SwFieldIds::SetExp)
+ {
+ // see SwWrtShell::StartInputFieldDlg
+ static_cast<SwSetExpField *>(mpField.get())->SetFormatField(*this);
+ }
+ Broadcast( SwFormatFieldHint( this, SwFormatFieldHintWhich::CHANGED ) );
+}
+
+void SwFormatField::SetTextField( SwTextField& rTextField )
+{
+ mpTextField = &rTextField;
+}
+
+void SwFormatField::ClearTextField()
+{
+ mpTextField = nullptr;
+}
+
+bool SwFormatField::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return ( mpField
+ && static_cast<const SwFormatField&>(rAttr).mpField
+ && mpField->GetTyp() == static_cast<const SwFormatField&>(rAttr).mpField->GetTyp()
+ && mpField->GetFormat() == static_cast<const SwFormatField&>(rAttr).mpField->GetFormat() )
+ ||
+ ( !mpField && !static_cast<const SwFormatField&>(rAttr).mpField );
+}
+
+SwFormatField* SwFormatField::Clone( SfxItemPool* ) const
+{
+ return new SwFormatField( *this );
+}
+
+void SwFormatField::InvalidateField()
+{
+ const SwPtrMsgPoolItem aItem(RES_REMOVE_UNO_OBJECT, &static_cast<sw::BroadcastingModify&>(*this));
+ CallSwClientNotify(sw::LegacyModifyHint{ &aItem, &aItem });
+}
+
+void SwFormatField::SwClientNotify( const SwModify& rModify, const SfxHint& rHint )
+{
+ SwClient::SwClientNotify(rModify, rHint);
+ if (const auto pFieldHint = dynamic_cast<const SwFieldHint*>( &rHint ))
+ {
+ // replace field content by text
+ SwPaM* pPaM = pFieldHint->m_pPaM;
+ pPaM->DeleteMark(); // TODO: this is really hackish
+
+ if( !mpTextField )
+ return;
+
+ SwDoc& rDoc = pPaM->GetDoc();
+ const SwTextNode& rTextNode = mpTextField->GetTextNode();
+ pPaM->GetPoint()->nNode = rTextNode;
+ pPaM->GetPoint()->nContent.Assign( const_cast<SwTextNode*>(&rTextNode), mpTextField->GetStart() );
+
+ OUString const aEntry(mpField->ExpandField(rDoc.IsClipBoard(), pFieldHint->m_pLayout));
+ pPaM->SetMark();
+ pPaM->Move( fnMoveForward );
+ rDoc.getIDocumentContentOperations().DeleteRange( *pPaM );
+ rDoc.getIDocumentContentOperations().InsertString( *pPaM, aEntry );
+ } else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if( !mpTextField )
+ return;
+ UpdateTextNode(pLegacyHint->m_pOld, pLegacyHint->m_pNew);
+ } else if (const auto pFindForFieldHint = dynamic_cast<const sw::FindFormatForFieldHint*>( &rHint ))
+ {
+ if(pFindForFieldHint->m_rpFormat == nullptr && pFindForFieldHint->m_pField == GetField())
+ pFindForFieldHint->m_rpFormat = this;
+ } else if (const auto pFindForPostItIdHint = dynamic_cast<const sw::FindFormatForPostItIdHint*>( &rHint ))
+ {
+ auto pPostItField = dynamic_cast<SwPostItField*>(mpField.get());
+ if(pPostItField && pFindForPostItIdHint->m_rpFormat == nullptr && pFindForPostItIdHint->m_nPostItId == pPostItField->GetPostItId())
+ pFindForPostItIdHint->m_rpFormat = this;
+ } else if (const auto pCollectPostItsHint = dynamic_cast<const sw::CollectPostItsHint*>( &rHint ))
+ {
+ if(GetTextField() && IsFieldInDoc() && (!pCollectPostItsHint->m_bHideRedlines || !sw::IsFieldDeletedInModel(pCollectPostItsHint->m_rIDRA, *GetTextField())))
+ pCollectPostItsHint->m_rvFormatFields.push_back(this);
+ } else if (const auto pHasHiddenInfoHint = dynamic_cast<const sw::HasHiddenInformationNotesHint*>( &rHint ))
+ {
+ if(!pHasHiddenInfoHint->m_rbHasHiddenInformationNotes && GetTextField() && IsFieldInDoc())
+ pHasHiddenInfoHint->m_rbHasHiddenInformationNotes = true;
+ } else if (const auto pGatherNodeIndexHint = dynamic_cast<const sw::GatherNodeIndexHint*>( &rHint ))
+ {
+ if(auto pTextField = GetTextField())
+ pGatherNodeIndexHint->m_rvNodeIndex.push_back(pTextField->GetTextNode().GetIndex());
+ } else if (const auto pGatherRefFieldsHint = dynamic_cast<const sw::GatherRefFieldsHint*>( &rHint ))
+ {
+ if(!GetTextField() || pGatherRefFieldsHint->m_nType != GetField()->GetSubType())
+ return;
+ SwTextNode* pNd = GetTextField()->GetpTextNode();
+ if(pNd && pNd->GetNodes().IsDocNodes())
+ pGatherRefFieldsHint->m_rvRFields.push_back(static_cast<SwGetRefField*>(GetField()));
+ } else if (const auto pGatherFieldsHint = dynamic_cast<const sw::GatherFieldsHint*>( &rHint ))
+ {
+ if(pGatherFieldsHint->m_bCollectOnlyInDocNodes)
+ {
+ if(!GetTextField())
+ return;
+ SwTextNode* pNd = GetTextField()->GetpTextNode();
+ if(!pNd || !pNd->GetNodes().IsDocNodes())
+ return;
+ }
+ pGatherFieldsHint->m_rvFields.push_back(this);
+ }
+}
+
+void SwFormatField::UpdateTextNode(const SfxPoolItem* pOld, const SfxPoolItem* pNew)
+{
+ if (pOld && (RES_REMOVE_UNO_OBJECT == pOld->Which()))
+ { // invalidate cached UNO object
+ m_wXTextField.clear();
+ // ??? why does this Modify method not already do this?
+ CallSwClientNotify(sw::LegacyModifyHint(pOld, pNew));
+ return;
+ }
+
+ if (!IsFieldInDoc())
+ return;
+
+ // don't do anything, especially not expand!
+ if( pNew && pNew->Which() == RES_OBJECTDYING )
+ return;
+
+ SwTextNode* pTextNd = &mpTextField->GetTextNode();
+ OSL_ENSURE( pTextNd, "Where is my Node?" );
+
+ bool bTriggerNode = false;
+ bool bExpand = false;
+ const SfxPoolItem* pNodeOld = nullptr;
+ const SfxPoolItem* pNodeNew = nullptr;
+ if(pNew)
+ {
+ switch(pNew->Which())
+ {
+ case RES_REFMARKFLD_UPDATE:
+ // update GetRef fields
+ if( SwFieldIds::GetRef == mpField->GetTyp()->Which() )
+ {
+ // #i81002#
+ static_cast<SwGetRefField*>(mpField.get())->UpdateField( mpTextField );
+ }
+ break;
+ case RES_DOCPOS_UPDATE:
+ // handled in SwTextFrame::Modify()
+ bTriggerNode = true;
+ pNodeOld = pNew;
+ pNodeNew = this;
+ break;
+ case RES_ATTRSET_CHG:
+ case RES_FMT_CHG:
+ bTriggerNode = true;
+ pNodeOld = pOld;
+ pNodeNew = pNew;
+ break;
+ default:
+ break;
+ }
+ }
+ if(!bTriggerNode)
+ {
+ switch (mpField->GetTyp()->Which())
+ {
+ case SwFieldIds::HiddenPara:
+ if( !pOld || pOld->Which() != RES_HIDDENPARA_PRINT ) {
+ bExpand =true;
+ break;
+ }
+ [[fallthrough]];
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ case SwFieldIds::DatabaseName:
+ bTriggerNode = true;
+ pNodeNew = pNew;
+ break;
+ case SwFieldIds::User:
+ {
+ SwUserFieldType* pType = static_cast<SwUserFieldType*>(mpField->GetTyp());
+ if(!pType->IsValid())
+ {
+ SwCalc aCalc( pTextNd->GetDoc() );
+ pType->GetValue( aCalc );
+ }
+ bExpand = true;
+ }
+ break;
+ default:
+ bExpand = true;
+ break;
+ }
+ }
+ if(bTriggerNode)
+ pTextNd->TriggerNodeUpdate(sw::LegacyModifyHint(pNodeOld, pNodeNew));
+ if(!bExpand)
+ return;
+
+ bool bForceNotify = pOld == nullptr && pNew == nullptr;
+ if (bForceNotify)
+ {
+ // Force notify was added for conditional text fields, at least the below fields need
+ // no forced notify.
+ const SwField* pField = mpTextField->GetFormatField().GetField();
+ const SwFieldIds nWhich = pField->GetTyp()->Which();
+ if (nWhich == SwFieldIds::DocInfo)
+ {
+ auto pDocInfoField = static_cast<const SwDocInfoField*>(pField);
+ sal_uInt16 nSubType = pDocInfoField->GetSubType();
+ // Do not consider extended SubTypes.
+ nSubType &= 0xff;
+ switch (nSubType)
+ {
+ case nsSwDocInfoSubType::DI_TITLE:
+ case nsSwDocInfoSubType::DI_SUBJECT:
+ case nsSwDocInfoSubType::DI_CHANGE:
+ case nsSwDocInfoSubType::DI_CUSTOM:
+ bForceNotify = false;
+ break;
+ }
+ }
+ }
+
+ mpTextField->ExpandTextField(bForceNotify);
+}
+
+bool SwFormatField::GetInfo( SfxPoolItem& rInfo ) const
+{
+ if( RES_AUTOFMT_DOCNODE != rInfo.Which() || !mpTextField )
+ return true;
+ const SwTextNode* pTextNd = mpTextField->GetpTextNode();
+ return nullptr == pTextNd ||
+ &pTextNd->GetNodes() != static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes;
+}
+
+bool SwFormatField::IsFieldInDoc() const
+{
+ return mpTextField != nullptr
+ && mpTextField->IsFieldInDoc();
+}
+
+bool SwFormatField::IsProtect() const
+{
+ return mpTextField != nullptr
+ && mpTextField->GetpTextNode() != nullptr
+ && mpTextField->GetpTextNode()->IsProtect();
+}
+
+void SwFormatField::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatField"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("mpTextField"), "%p", mpTextField);
+
+ SfxPoolItem::dumpAsXml(pWriter);
+ if (mpField) // pool default doesn't have one
+ {
+ mpField->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// class SwTextField ////////////////////////////////////////////////////
+
+SwTextField::SwTextField(
+ SwFormatField & rAttr,
+ sal_Int32 const nStartPos,
+ bool const bInClipboard)
+ : SwTextAttr( rAttr, nStartPos )
+// fdo#39694 the ExpandField here may not give the correct result in all cases,
+// but is better than nothing
+ , m_aExpand( rAttr.GetField()->ExpandField(bInClipboard, nullptr) )
+ , m_pTextNode( nullptr )
+{
+ rAttr.SetTextField( *this );
+ SetHasDummyChar(true);
+}
+
+SwTextField::~SwTextField( )
+{
+ SwFormatField & rFormatField( static_cast<SwFormatField &>(GetAttr()) );
+ if ( this == rFormatField.GetTextField() )
+ {
+ rFormatField.ClearTextField();
+ }
+}
+
+bool SwTextField::IsFieldInDoc() const
+{
+ return GetpTextNode() != nullptr
+ && GetpTextNode()->GetNodes().IsDocNodes();
+}
+
+void SwTextField::ExpandTextField(const bool bForceNotify) const
+{
+ OSL_ENSURE( m_pTextNode, "SwTextField: where is my TextNode?" );
+
+ const SwField* pField = GetFormatField().GetField();
+ const OUString aNewExpand( pField->ExpandField(m_pTextNode->GetDoc().IsClipBoard(),
+ // can't do any better than this here...
+ m_pTextNode->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout()) );
+
+ const SwFieldIds nWhich = pField->GetTyp()->Which();
+ const bool bSameExpandSimpleNotification
+ = SwFieldIds::Chapter != nWhich && SwFieldIds::PageNumber != nWhich
+ && SwFieldIds::RefPageGet != nWhich
+ // Page count fields to not use aExpand during formatting,
+ // therefore an invalidation of the text frame has to be triggered even if aNewExpand == aExpand:
+ && (SwFieldIds::DocStat != nWhich
+ || DS_PAGE != static_cast<const SwDocStatField*>(pField)->GetSubType())
+ && (SwFieldIds::GetExp != nWhich
+ || static_cast<const SwGetExpField*>(pField)->IsInBodyText());
+
+ bool bHiddenParaChanged = false;
+ if (aNewExpand != m_aExpand || bSameExpandSimpleNotification)
+ bHiddenParaChanged = m_pTextNode->CalcHiddenParaField();
+
+ if (aNewExpand == m_aExpand)
+ {
+ if ( bSameExpandSimpleNotification )
+ {
+ if( bHiddenParaChanged )
+ m_pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, nullptr));
+ if ( !bForceNotify )
+ {
+ // done, if no further notification forced.
+ return;
+ }
+ }
+ }
+ else
+ m_aExpand = aNewExpand;
+
+ const_cast<SwTextField*>(this)->NotifyContentChange( const_cast<SwFormatField&>(GetFormatField()) );
+}
+
+void SwTextField::CopyTextField( SwTextField *pDest ) const
+{
+ OSL_ENSURE( m_pTextNode, "SwTextField: where is my TextNode?" );
+ OSL_ENSURE( pDest->m_pTextNode, "SwTextField: where is pDest's TextNode?" );
+
+ IDocumentFieldsAccess* pIDFA = &m_pTextNode->getIDocumentFieldsAccess();
+ IDocumentFieldsAccess* pDestIDFA = &pDest->m_pTextNode->getIDocumentFieldsAccess();
+
+ SwFormatField& rDestFormatField = const_cast<SwFormatField&>(pDest->GetFormatField());
+ const SwFieldIds nFieldWhich = rDestFormatField.GetField()->GetTyp()->Which();
+
+ if( pIDFA != pDestIDFA )
+ {
+ // different documents, e.g. clipboard:
+ // register field type in target document
+ SwFieldType* pFieldType;
+ if( nFieldWhich != SwFieldIds::Database
+ && nFieldWhich != SwFieldIds::User
+ && nFieldWhich != SwFieldIds::SetExp
+ && nFieldWhich != SwFieldIds::Dde
+ && SwFieldIds::TableOfAuthorities != nFieldWhich )
+ {
+ pFieldType = pDestIDFA->GetSysFieldType( nFieldWhich );
+ }
+ else
+ {
+ pFieldType = pDestIDFA->InsertFieldType( *rDestFormatField.GetField()->GetTyp() );
+ }
+
+ // DDE fields need special treatment
+ if( SwFieldIds::Dde == nFieldWhich )
+ {
+ if( rDestFormatField.GetTextField() )
+ {
+ static_cast<SwDDEFieldType*>(rDestFormatField.GetField()->GetTyp())->DecRefCnt();
+ }
+ static_cast<SwDDEFieldType*>(pFieldType)->IncRefCnt();
+ }
+
+ OSL_ENSURE( pFieldType, "unknown FieldType" );
+ pFieldType->Add( &rDestFormatField ); // register at the field type
+ rDestFormatField.GetField()->ChgTyp( pFieldType );
+ }
+
+ // update expression fields
+ if( nFieldWhich == SwFieldIds::SetExp
+ || nFieldWhich == SwFieldIds::GetExp
+ || nFieldWhich == SwFieldIds::HiddenText )
+ {
+ SwTextField* pField = const_cast<SwTextField*>(this);
+ pDestIDFA->UpdateExpFields( pField, true );
+ }
+ // table fields: external display
+ else if( SwFieldIds::Table == nFieldWhich
+ && static_cast<SwTableField*>(rDestFormatField.GetField())->IsIntrnlName() )
+ {
+ // convert internal (core) to external (UI) formula
+ const SwTableNode* pTableNd = m_pTextNode->FindTableNode();
+ if( pTableNd ) // in a table?
+ static_cast<SwTableField*>(rDestFormatField.GetField())->PtrToBoxNm( &pTableNd->GetTable() );
+ }
+}
+
+void SwTextField::NotifyContentChange(SwFormatField& rFormatField)
+{
+ //if not in undo section notify the change
+ if (m_pTextNode && m_pTextNode->GetNodes().IsDocNodes())
+ m_pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, &rFormatField));
+}
+
+/*static*/
+void SwTextField::GetPamForTextField(
+ const SwTextField& rTextField,
+ std::shared_ptr< SwPaM >& rPamForTextField )
+{
+ if (rTextField.GetpTextNode() == nullptr)
+ {
+ SAL_WARN("sw.core", "<SwTextField::GetPamForField> - missing <SwTextNode>");
+ return;
+ }
+
+ const SwTextNode& rTextNode = rTextField.GetTextNode();
+
+ rPamForTextField = std::make_shared<SwPaM>( rTextNode,
+ (rTextField.End() != nullptr) ? *(rTextField.End()) : ( rTextField.GetStart() + 1 ),
+ rTextNode,
+ rTextField.GetStart() );
+
+}
+
+/*static*/
+void SwTextField::DeleteTextField( const SwTextField& rTextField )
+{
+ if (rTextField.GetpTextNode() != nullptr)
+ {
+ std::shared_ptr< SwPaM > pPamForTextField;
+ GetPamForTextField(rTextField, pPamForTextField);
+ if (pPamForTextField != nullptr)
+ {
+ rTextField.GetTextNode().GetDoc().getIDocumentContentOperations().DeleteAndJoin(*pPamForTextField);
+ }
+ }
+}
+
+// class SwTextInputField ///////////////////////////////////////////////
+
+// input field in-place editing
+SwTextInputField::SwTextInputField(
+ SwFormatField & rAttr,
+ sal_Int32 const nStart,
+ sal_Int32 const nEnd,
+ bool const bInClipboard )
+
+ : SwTextAttr( rAttr, nStart )
+ , SwTextAttrNesting( rAttr, nStart, nEnd )
+ , SwTextField( rAttr, nStart, bInClipboard )
+ , m_bLockNotifyContentChange( false )
+{
+ SetHasDummyChar( false );
+ SetHasContent( true );
+}
+
+SwTextInputField::~SwTextInputField()
+{
+}
+
+bool SwTextInputField::LockNotifyContentChange()
+{
+ if (m_bLockNotifyContentChange)
+ {
+ return false;
+ }
+ m_bLockNotifyContentChange = true;
+ return true;
+}
+
+void SwTextInputField::UnlockNotifyContentChange()
+{
+ m_bLockNotifyContentChange = false;
+}
+
+void SwTextInputField::NotifyContentChange( SwFormatField& rFormatField )
+{
+ if ( !m_bLockNotifyContentChange )
+ {
+ LockNotifyContentChange();
+
+ SwTextField::NotifyContentChange( rFormatField );
+ UpdateTextNodeContent( GetFieldContent() );
+
+ UnlockNotifyContentChange();
+ }
+}
+
+OUString SwTextInputField::GetFieldContent() const
+{
+ return GetFormatField().GetField()->ExpandField(false, nullptr/*ignored anyway*/);
+}
+
+void SwTextInputField::UpdateFieldContent()
+{
+ if ( !(IsFieldInDoc()
+ && GetStart() != (*End())) )
+ return;
+
+ assert( (*End()) - GetStart() >= 2 &&
+ "<SwTextInputField::UpdateFieldContent()> - Are CH_TXT_ATR_INPUTFIELDSTART and/or CH_TXT_ATR_INPUTFIELDEND missing?" );
+ // skip CH_TXT_ATR_INPUTFIELDSTART character
+ const sal_Int32 nIdx = GetStart() + 1;
+ // skip CH_TXT_ATR_INPUTFIELDEND character
+ const sal_Int32 nLen = static_cast<sal_Int32>(std::max<sal_Int32>( 0, ( (*End()) - 1 - nIdx ) ));
+ const OUString aNewFieldContent = GetTextNode().GetExpandText(nullptr, nIdx, nLen);
+
+ 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);
+}
+
+void SwTextInputField::UpdateTextNodeContent( const OUString& rNewContent )
+{
+ assert(IsFieldInDoc() &&
+ "<SwTextInputField::UpdateTextNodeContent(..)> - misusage as Input Field is not in document content.");
+
+ assert( (*End()) - GetStart() >= 2 &&
+ "<SwTextInputField::UpdateTextNodeContent(..)> - Are CH_TXT_ATR_INPUTFIELDSTART and/or CH_TXT_ATR_INPUTFIELDEND missing?" );
+ // skip CH_TXT_ATR_INPUTFIELDSTART character
+ const sal_Int32 nIdx = GetStart() + 1;
+ // skip CH_TXT_ATR_INPUTFIELDEND character
+ const sal_Int32 nDelLen = std::max<sal_Int32>( 0, ( (*End()) - 1 - nIdx ) );
+ SwIndex aIdx( &GetTextNode(), nIdx );
+ GetTextNode().ReplaceText( aIdx, nDelLen, rNewContent );
+}
+
+// class SwTextAnnotationField //////////////////////////////////////////
+
+// text annotation field
+SwTextAnnotationField::SwTextAnnotationField(
+ SwFormatField & rAttr,
+ sal_Int32 const nStart,
+ bool const bInClipboard )
+ : SwTextAttr( rAttr, nStart )
+ , SwTextField( rAttr, nStart, bInClipboard )
+{
+}
+
+SwTextAnnotationField::~SwTextAnnotationField()
+{
+}
+
+::sw::mark::IMark* SwTextAnnotationField::GetAnnotationMark() const
+{
+ auto pPostItField = dynamic_cast<const SwPostItField*>(GetFormatField().GetField());
+ assert(pPostItField);
+
+ SwDoc& rDoc = static_cast<const SwPostItFieldType*>(pPostItField->GetTyp())->GetDoc();
+
+ IDocumentMarkAccess* pMarksAccess = rDoc.getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t pMark = pMarksAccess->findAnnotationMark( pPostItField->GetName() );
+ return pMark != pMarksAccess->getAnnotationMarksEnd()
+ ? *pMark
+ : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/atrflyin.cxx b/sw/source/core/txtnode/atrflyin.cxx
new file mode 100644
index 000000000..bb42a0853
--- /dev/null
+++ b/sw/source/core/txtnode/atrflyin.cxx
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <pam.hxx>
+#include <flyfrm.hxx>
+#include <ndtxt.hxx>
+#include <frmfmt.hxx>
+#include <fmtflcnt.hxx>
+#include <txtflcnt.hxx>
+#include <fmtanchr.hxx>
+#include <txtfrm.hxx>
+#include <flyfrms.hxx>
+#include <objectformatter.hxx>
+#include <calbck.hxx>
+#include <dcontact.hxx>
+#include <textboxhelper.hxx>
+#include <osl/diagnose.h>
+
+SwFormatFlyCnt::SwFormatFlyCnt( SwFrameFormat *pFrameFormat )
+ : SfxPoolItem( RES_TXTATR_FLYCNT ),
+ m_pTextAttr( nullptr ),
+ m_pFormat( pFrameFormat )
+{
+}
+
+bool SwFormatFlyCnt::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return( m_pTextAttr && static_cast<const SwFormatFlyCnt&>(rAttr).m_pTextAttr &&
+ m_pTextAttr->GetStart() == static_cast<const SwFormatFlyCnt&>(rAttr).m_pTextAttr->GetStart() &&
+ m_pFormat == static_cast<const SwFormatFlyCnt&>(rAttr).GetFrameFormat() );
+}
+
+SwFormatFlyCnt* SwFormatFlyCnt::Clone( SfxItemPool* ) const
+{
+ return new SwFormatFlyCnt( m_pFormat );
+}
+
+SwTextFlyCnt::SwTextFlyCnt( SwFormatFlyCnt& rAttr, sal_Int32 nStartPos )
+ : SwTextAttr( rAttr, nStartPos )
+{
+ rAttr.m_pTextAttr = this;
+ SetHasDummyChar(true);
+}
+
+/** An overview of how a new SwTextFlyCnt is created:
+ * MakeTextAttr() is called e.g. by SwTextNode::CopyText().
+ * The following steps are required to clone:
+ * 1) copying the pFormat with content, attributes etc.
+ * 2) setting the anchor
+ * 3) notification
+ * Because not all required information is available at all times,
+ * the steps are distributed variously:
+ * ad 1) MakeTextAttr() calls DocumentLayoutManager::CopyLayoutFormat()
+ * which creates the new SwFlyFrameFormat and copies the content of the
+ * fly frame.
+ * ad 2) SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor
+ * position in the SwFlyFrameFormat to the SwPosition of the dummy
+ * CH_TXTATR_BREAKWORD. This cannot be done in MakeTextAttr() because it
+ * doesn't know the target text node.
+ * ad 3) GetFlyFrame_() is called during text formatting by SwTextFormatter
+ * and searches for the SwFlyFrame for the dummy char of the current
+ * SwTextFrame. If none is found, a new SwFlyInContentFrame is created.
+ * Important: pTextFrame->AppendFly() immediately triggers a reformat
+ * of pTextFrame. However, the recursion is blocked by the lock mechanism
+ * in SwTextFrame::Format().
+ * The advantage of all this is that it's not necessary to explicitly iterate
+ * over all SwTextFrames that depend on the SwTextNode to create the
+ * SwFlyInContentFrame - this is done automatically already.
+ */
+
+void SwTextFlyCnt::CopyFlyFormat( SwDoc& rDoc )
+{
+ SwFrameFormat* pFormat = GetFlyCnt().GetFrameFormat();
+ assert(pFormat);
+ // The FlyFrameFormat must be copied - CopyLayoutFormat
+ // (DocumentLayoutManager.cxx) creates the FlyFrameFormat and copies the
+ // content.
+
+ // disable undo while copying attribute
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ if ((RndStdIds::FLY_AT_PAGE != aAnchor.GetAnchorId()) &&
+ (&rDoc != pFormat->GetDoc())) // different documents?
+ {
+ // JP 03.06.96: ensure that the copied anchor points to valid content!
+ // setting it to the correct position is done later.
+ SwNodeIndex aIdx( rDoc.GetNodes().GetEndOfExtras(), +2 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = rDoc.GetNodes().GoNext( &aIdx );
+
+ SwPosition pos = *aAnchor.GetContentAnchor();
+ pos.nNode = aIdx;
+ if (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId())
+ {
+ pos.nContent.Assign( pCNd, 0 );
+ }
+ else
+ {
+ pos.nContent.Assign( nullptr, 0 );
+ assert(false);
+ }
+ aAnchor.SetAnchor( &pos );
+ }
+
+ SwFrameFormat* pNew = rDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, false, false );
+ const_cast<SwFormatFlyCnt&>(GetFlyCnt()).SetFlyFormat( pNew );
+}
+
+/** SetAnchor() is called by SwTextNode::InsertHint() and sets the anchor
+ * position in the SwFlyFrameFormat to the SwPosition of the dummy
+ * CH_TXTATR_BREAKWORD. This cannot be done in MakeTextAttr() because it
+ * doesn't know the target text node.
+ */
+void SwTextFlyCnt::SetAnchor( const SwTextNode *pNode )
+{
+ // for Undo, the new anchor must be known already!
+
+ SwDoc& rDoc = const_cast<SwDoc&>(pNode->GetDoc());
+
+ SwIndex aIdx( const_cast<SwTextNode*>(pNode), GetStart() );
+ SwPosition aPos( *pNode->StartOfSectionNode(), aIdx );
+ SwFrameFormat* pFormat = GetFlyCnt().GetFrameFormat();
+ SwFormatAnchor aAnchor( pFormat->GetAnchor() );
+ SwNode *const pOldNode(aAnchor.GetContentAnchor()
+ ? &aAnchor.GetContentAnchor()->nNode.GetNode()
+ : nullptr);
+
+ if (!pOldNode || !pOldNode->GetNodes().IsDocNodes() ||
+ pOldNode != static_cast<SwNode const *>(pNode))
+ {
+ aPos.nNode = *pNode;
+ }
+ else
+ {
+ aPos.nNode = *pOldNode;
+ }
+
+ aAnchor.SetType( RndStdIds::FLY_AS_CHAR ); // default!
+ aAnchor.SetAnchor( &aPos );
+
+ // in case of anchor change, delete all FlyFrames
+ // JP 25.04.95: if the Frames can be moved within SplitNode, they don't
+ // need to be deleted
+ if( ( !pNode->GetpSwpHints() || !pNode->GetpSwpHints()->IsInSplitNode() )
+ && RES_DRAWFRMFMT != pFormat->Which() )
+ pFormat->DelFrames();
+
+ // copy into a different document?
+ if( &rDoc != pFormat->GetDoc() )
+ {
+ // disable undo while copying attribute
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ SwFrameFormat* pNew = rDoc.getIDocumentLayoutAccess().CopyLayoutFormat( *pFormat, aAnchor, false, false );
+
+ ::sw::UndoGuard const undoGuardFormat(
+ pFormat->GetDoc()->GetIDocumentUndoRedo());
+ pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+ const_cast<SwFormatFlyCnt&>(GetFlyCnt()).SetFlyFormat( pNew );
+ }
+ else if( pNode->GetpSwpHints() &&
+ pNode->GetpSwpHints()->IsInSplitNode() &&
+ RES_DRAWFRMFMT != pFormat->Which() )
+ {
+ pFormat->LockModify();
+ pFormat->SetFormatAttr( aAnchor ); // only set the anchor
+ // tdf#91228 must notify the anchor nodes despite LockModify
+ assert(pOldNode);
+ pOldNode->RemoveAnchoredFly(pFormat);
+ aPos.nNode.GetNode().AddAnchoredFly(pFormat);
+ pFormat->UnlockModify();
+ }
+ else
+ {
+ assert(!pFormat->IsModifyLocked()); // need to notify anchor node
+ if (RES_DRAWFRMFMT == pFormat->Which())
+ {
+ if (SdrObject const*const pObj = pFormat->FindSdrObject())
+ { // tdf#123259 disconnect with *old* anchor position
+ static_cast<SwDrawContact*>(::GetUserCall(pObj))->DisconnectFromLayout(false);
+ }
+ }
+ pFormat->SetFormatAttr( aAnchor ); // only set the anchor
+
+ // If the draw format has a TextBox, then set its anchor as well.
+ if (SwFrameFormat* pTextBox
+ = SwTextBoxHelper::getOtherTextBoxFormat(pFormat, RES_DRAWFRMFMT))
+ {
+ SwFormatAnchor aTextBoxAnchor(pTextBox->GetAnchor());
+ aTextBoxAnchor.SetAnchor(aAnchor.GetContentAnchor());
+
+ // SwFlyAtContentFrame::SwClientNotify() assumes the anchor has a matching layout frame, which
+ // may not be the case when we're in the process of a node split, so block
+ // notifications.
+ bool bIsInSplitNode = pNode->GetpSwpHints() && pNode->GetpSwpHints()->IsInSplitNode();
+ if (bIsInSplitNode)
+ {
+ pTextBox->LockModify();
+ }
+ else
+ {
+ // Otherwise delete fly frames on anchor change.
+ pTextBox->DelFrames();
+ }
+
+ pTextBox->SetFormatAttr(aTextBoxAnchor);
+
+ if (bIsInSplitNode)
+ {
+ pOldNode->RemoveAnchoredFly(pTextBox);
+ aPos.nNode.GetNode().AddAnchoredFly(pTextBox);
+ pTextBox->UnlockModify();
+ }
+ else
+ {
+ pTextBox->MakeFrames();
+ }
+ }
+ }
+
+ // The node may have several SwTextFrames - for every SwTextFrame a
+ // SwFlyInContentFrame is created.
+}
+
+
+/** GetFlyFrame_() is called during text formatting by SwTextFormatter
+ * and searches for the SwFlyFrame for the dummy char of the current
+ * SwTextFrame. If none is found, a new SwFlyInContentFrame is created.
+ */
+SwFlyInContentFrame *SwTextFlyCnt::GetFlyFrame_( const SwFrame *pCurrFrame )
+{
+ SwFrameFormat* pFrameFormat = GetFlyCnt().GetFrameFormat();
+ if( RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ OSL_ENSURE( false, "SwTextFlyCnt::GetFlyFrame_: DrawInCnt-under construction!" );
+ return nullptr;
+ }
+
+ SwIterator<SwFlyFrame,SwFormat> aIter( *GetFlyCnt().m_pFormat );
+ assert(pCurrFrame->IsTextFrame());
+ SwFrame* pFrame = aIter.First();
+ if ( pFrame )
+ {
+ SwTextFrame *pFirst = const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCurrFrame));
+ while ( pFirst->IsFollow() )
+ pFirst = pFirst->FindMaster();
+ do
+ {
+ SwTextFrame *pTmp = pFirst;
+ do
+ { if( static_cast<SwFlyFrame*>(pFrame)->GetAnchorFrame() == static_cast<SwFrame*>(pTmp) )
+ {
+ if ( pTmp != pCurrFrame )
+ {
+ pTmp->RemoveFly( static_cast<SwFlyFrame*>(pFrame) );
+ const_cast<SwTextFrame*>(static_cast<const SwTextFrame*>(pCurrFrame))->AppendFly( static_cast<SwFlyFrame*>(pFrame) );
+ }
+ return static_cast<SwFlyInContentFrame*>(pFrame);
+ }
+ pTmp = pTmp->GetFollow();
+ } while ( pTmp );
+
+ pFrame = aIter.Next();
+
+ } while( pFrame );
+ }
+
+ // We did not find a matching FlyFrame, so create a new one.
+ // AppendFly() triggers a reformat of pCurrentFrame. However, the
+ // recursion is blocked by the lock mechanism in SwTextFrame::Format().
+ SwFrame* pCurrentFrame = const_cast<SwFrame*>(pCurrFrame);
+ SwFlyInContentFrame *pFly = new SwFlyInContentFrame(static_cast<SwFlyFrameFormat*>(pFrameFormat), pCurrentFrame, pCurrentFrame);
+ pCurrentFrame->AppendFly(pFly);
+ pFly->RegistFlys();
+
+ // We must ensure that the content of the FlyInCnt is fully formatted
+ // right after construction.
+ // #i26945# - Use new object formatter to format Writer
+ // fly frame and its content.
+ SwObjectFormatter::FormatObj( *pFly, const_cast<SwFrame*>(pCurrFrame),
+ pCurrFrame->FindPageFrame() );
+
+ return pFly;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/atrftn.cxx b/sw/source/core/txtnode/atrftn.cxx
new file mode 100644
index 000000000..82485f4c5
--- /dev/null
+++ b/sw/source/core/txtnode/atrftn.cxx
@@ -0,0 +1,608 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <fmtftn.hxx>
+
+#include <doc.hxx>
+#include <DocumentContentOperationsManager.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <cntfrm.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <txtftn.hxx>
+#include <ftnidx.hxx>
+#include <ftninfo.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <ftnfrm.hxx>
+#include <ndindex.hxx>
+#include <fmtftntx.hxx>
+#include <section.hxx>
+#include <calbck.hxx>
+#include <hints.hxx>
+#include <pam.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <vcl/svapp.hxx>
+#include <unotextrange.hxx>
+#include <osl/diagnose.h>
+
+namespace {
+ /// Get a sorted list of the used footnote reference numbers.
+ /// @param[in] rDoc The active document.
+ /// @param[in] pExclude A footnote whose reference number should be excluded from the set.
+ /// @param[out] rUsedRef The set of used reference numbers.
+ /// @param[out] rInvalid A returned list of all items that had an invalid reference number.
+ void lcl_FillUsedFootnoteRefNumbers(SwDoc &rDoc,
+ SwTextFootnote const *pExclude,
+ std::set<sal_uInt16> &rUsedRef,
+ std::vector<SwTextFootnote*> &rInvalid)
+ {
+ SwFootnoteIdxs& ftnIdxs = rDoc.GetFootnoteIdxs();
+
+ rInvalid.clear();
+
+ for( size_t n = 0; n < ftnIdxs.size(); ++n )
+ {
+ SwTextFootnote* pTextFootnote = ftnIdxs[ n ];
+ if ( pTextFootnote != pExclude )
+ {
+ if ( USHRT_MAX == pTextFootnote->GetSeqRefNo() )
+ {
+ rInvalid.push_back(pTextFootnote);
+ }
+ else
+ {
+ rUsedRef.insert( pTextFootnote->GetSeqRefNo() );
+ }
+ }
+ }
+ }
+
+ /// Check whether a requested reference number is available.
+ /// @param[in] rUsedNums Set of used reference numbers.
+ /// @param[in] requested The requested reference number.
+ /// @returns true if the number is available, false if not.
+ bool lcl_IsRefNumAvailable(std::set<sal_uInt16> const &rUsedNums,
+ sal_uInt16 requested)
+ {
+ if ( USHRT_MAX == requested )
+ return false; // Invalid sequence number.
+ if ( rUsedNums.count(requested) )
+ return false; // Number already used.
+ return true;
+ }
+
+ /// Get the first few unused sequential reference numbers.
+ /// @param[out] rLowestUnusedNums The lowest unused sequential reference numbers.
+ /// @param[in] rUsedNums The set of used sequential reference numbers.
+ /// @param[in] numRequired The number of reference number required.
+ void lcl_FillUnusedSeqRefNums(std::vector<sal_uInt16> &rLowestUnusedNums,
+ const std::set<sal_uInt16> &rUsedNums,
+ size_t numRequired)
+ {
+ if (!numRequired)
+ return;
+
+ rLowestUnusedNums.reserve(numRequired);
+ sal_uInt16 newNum = 0;
+ //Start by using numbers from gaps in rUsedNums
+ for( const auto& rNum : rUsedNums )
+ {
+ while ( newNum < rNum )
+ {
+ rLowestUnusedNums.push_back( newNum++ );
+ if ( --numRequired == 0)
+ return;
+ }
+ newNum++;
+ }
+ //Filled in all gaps. Fill the rest of the list with new numbers.
+ do
+ {
+ rLowestUnusedNums.push_back( newNum++ );
+ }
+ while ( --numRequired > 0 );
+ }
+
+}
+
+SwFormatFootnote::SwFormatFootnote( bool bEndNote )
+ : SfxPoolItem( RES_TXTATR_FTN )
+ , sw::BroadcastingModify()
+ , m_pTextAttr(nullptr)
+ , m_nNumber(0)
+ , m_nNumberRLHidden(0)
+ , m_bEndNote(bEndNote)
+{
+}
+
+bool SwFormatFootnote::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return m_nNumber == static_cast<const SwFormatFootnote&>(rAttr).m_nNumber &&
+ //FIXME?
+ m_aNumber == static_cast<const SwFormatFootnote&>(rAttr).m_aNumber &&
+ m_bEndNote == static_cast<const SwFormatFootnote&>(rAttr).m_bEndNote;
+}
+
+SwFormatFootnote* SwFormatFootnote::Clone( SfxItemPool* ) const
+{
+ SwFormatFootnote* pNew = new SwFormatFootnote;
+ pNew->m_aNumber = m_aNumber;
+ pNew->m_nNumber = m_nNumber;
+ pNew->m_nNumberRLHidden = m_nNumberRLHidden;
+ pNew->m_bEndNote = m_bEndNote;
+ return pNew;
+}
+
+void SwFormatFootnote::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
+ SetXFootnote(css::uno::Reference<css::text::XFootnote>(nullptr));
+}
+
+void SwFormatFootnote::InvalidateFootnote()
+{
+ SwPtrMsgPoolItem const item(RES_REMOVE_UNO_OBJECT,
+ &static_cast<sw::BroadcastingModify&>(*this)); // cast to base class (void*)
+ CallSwClientNotify(sw::LegacyModifyHint(&item, &item));
+}
+
+void SwFormatFootnote::SetEndNote( bool b )
+{
+ if ( b != m_bEndNote )
+ {
+ if ( GetTextFootnote() )
+ {
+ GetTextFootnote()->DelFrames(nullptr);
+ }
+ m_bEndNote = b;
+ }
+}
+
+SwFormatFootnote::~SwFormatFootnote()
+{
+}
+
+OUString SwFormatFootnote::GetFootnoteText(SwRootFrame const& rLayout) const
+{
+ OUStringBuffer buf;
+ if( m_pTextAttr->GetStartNode() )
+ {
+ SwNodeIndex aIdx( *m_pTextAttr->GetStartNode(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetTextNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNext( &aIdx );
+
+ if( pCNd->IsTextNode() ) {
+ buf.append(static_cast<SwTextNode*>(pCNd)->GetExpandText(&rLayout));
+
+ ++aIdx;
+ while ( !aIdx.GetNode().IsEndNode() ) {
+ if ( aIdx.GetNode().IsTextNode() )
+ {
+ buf.append(" ");
+ buf.append(aIdx.GetNode().GetTextNode()->GetExpandText(&rLayout));
+ }
+ ++aIdx;
+ }
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+/// return the view string of the foot/endnote
+OUString SwFormatFootnote::GetViewNumStr(const SwDoc& rDoc,
+ SwRootFrame const*const pLayout, bool bInclStrings) const
+{
+ OUString sRet( GetNumStr() );
+ if( sRet.isEmpty() )
+ {
+ // in this case the number is needed, get it via SwDoc's FootnoteInfo
+ bool bMakeNum = true;
+ const SwSectionNode* pSectNd = m_pTextAttr
+ ? SwUpdFootnoteEndNtAtEnd::FindSectNdWithEndAttr( *m_pTextAttr )
+ : nullptr;
+ sal_uInt16 const nNumber(pLayout && pLayout->IsHideRedlines()
+ ? GetNumberRLHidden()
+ : GetNumber());
+
+ if( pSectNd )
+ {
+ const SwFormatFootnoteEndAtTextEnd& rFootnoteEnd = static_cast<const SwFormatFootnoteEndAtTextEnd&>(
+ pSectNd->GetSection().GetFormat()->GetFormatAttr(
+ IsEndNote() ?
+ o3tl::narrowing<sal_uInt16>(RES_END_AT_TXTEND) :
+ o3tl::narrowing<sal_uInt16>(RES_FTN_AT_TXTEND) ) );
+
+ if( FTNEND_ATTXTEND_OWNNUMANDFMT == rFootnoteEnd.GetValue() )
+ {
+ bMakeNum = false;
+ sRet = rFootnoteEnd.GetSwNumType().GetNumStr( nNumber );
+ if( bInclStrings )
+ {
+ sRet = rFootnoteEnd.GetPrefix() + sRet + rFootnoteEnd.GetSuffix();
+ }
+ }
+ }
+
+ if( bMakeNum )
+ {
+ const SwEndNoteInfo* pInfo;
+ if( IsEndNote() )
+ pInfo = &rDoc.GetEndNoteInfo();
+ else
+ pInfo = &rDoc.GetFootnoteInfo();
+ sRet = pInfo->m_aFormat.GetNumStr( nNumber );
+ if( bInclStrings )
+ {
+ sRet = pInfo->GetPrefix() + sRet + pInfo->GetSuffix();
+ }
+ }
+ }
+ return sRet;
+}
+
+uno::Reference<text::XTextRange> SwFormatFootnote::getAnchor(SwDoc& rDoc) const
+{
+ SolarMutexGuard aGuard;
+ if (!m_pTextAttr)
+ return uno::Reference<text::XTextRange>();
+ SwPaM aPam(m_pTextAttr->GetTextNode(), m_pTextAttr->GetStart());
+ aPam.SetMark();
+ ++aPam.GetMark()->nContent;
+ const uno::Reference<text::XTextRange> xRet =
+ SwXTextRange::CreateXTextRange(rDoc, *aPam.Start(), aPam.End());
+ return xRet;
+}
+
+SwTextFootnote::SwTextFootnote( SwFormatFootnote& rAttr, sal_Int32 nStartPos )
+ : SwTextAttr( rAttr, nStartPos )
+ , m_pTextNode( nullptr )
+ , m_nSeqNo( USHRT_MAX )
+{
+ rAttr.m_pTextAttr = this;
+ SetHasDummyChar(true);
+}
+
+SwTextFootnote::~SwTextFootnote()
+{
+ SetStartNode( nullptr );
+}
+
+void SwTextFootnote::SetStartNode( const SwNodeIndex *pNewNode, bool bDelNode )
+{
+ if( pNewNode )
+ {
+ if ( !m_pStartNode )
+ {
+ m_pStartNode.reset(new SwNodeIndex(*pNewNode));
+ }
+ else
+ {
+ *m_pStartNode = *pNewNode;
+ }
+ }
+ else if ( m_pStartNode )
+ {
+ // need to do 2 things:
+ // 1) unregister footnotes at their pages
+ // 2) delete the footnote section in the Inserts of the nodes-array
+ SwDoc* pDoc;
+ if ( m_pTextNode )
+ {
+ pDoc = &m_pTextNode->GetDoc();
+ }
+ else
+ {
+ //JP 27.01.97: the sw3-Reader creates a StartNode but the
+ // attribute isn't anchored in the TextNode yet.
+ // If it is deleted (e.g. Insert File with footnote
+ // inside fly frame), the content must also be deleted.
+ pDoc = &m_pStartNode->GetNodes().GetDoc();
+ }
+
+ // If called from ~SwDoc(), must not delete the footnote nodes,
+ // and not necessary to delete the footnote frames.
+ if( !pDoc->IsInDtor() )
+ {
+ if( bDelNode )
+ {
+ // 2) delete the section for the footnote nodes
+ // it's possible that the Inserts have already been deleted (how???)
+ pDoc->getIDocumentContentOperations().DeleteSection( &m_pStartNode->GetNode() );
+ }
+ else
+ // If the nodes are not deleted, their frames must be removed
+ // from the page (deleted), there is nothing else that deletes
+ // them (particularly not Undo)
+ DelFrames( nullptr );
+ }
+ m_pStartNode.reset();
+
+ // remove the footnote from the SwDoc's array
+ for( size_t n = 0; n < pDoc->GetFootnoteIdxs().size(); ++n )
+ if( this == pDoc->GetFootnoteIdxs()[n] )
+ {
+ pDoc->GetFootnoteIdxs().erase( pDoc->GetFootnoteIdxs().begin() + n );
+ // if necessary, update following footnotes
+ if( !pDoc->IsInDtor() && n < pDoc->GetFootnoteIdxs().size() )
+ {
+ SwNodeIndex aTmp( pDoc->GetFootnoteIdxs()[n]->GetTextNode() );
+ pDoc->GetFootnoteIdxs().UpdateFootnote( aTmp );
+ }
+ break;
+ }
+ }
+}
+
+void SwTextFootnote::SetNumber(const sal_uInt16 nNewNum,
+ sal_uInt16 const nNumberRLHidden, const OUString &sNumStr)
+{
+ SwFormatFootnote& rFootnote = const_cast<SwFormatFootnote&>(GetFootnote());
+
+ rFootnote.m_aNumber = sNumStr;
+ if ( sNumStr.isEmpty() )
+ {
+ rFootnote.m_nNumber = nNewNum;
+ rFootnote.m_nNumberRLHidden = nNumberRLHidden;
+ }
+ InvalidateNumberInLayout();
+}
+
+void SwTextFootnote::InvalidateNumberInLayout()
+{
+ assert(m_pTextNode);
+ SwNodes &rNodes = m_pTextNode->GetDoc().GetNodes();
+ const sw::LegacyModifyHint aHint(nullptr, &GetFootnote());
+ m_pTextNode->TriggerNodeUpdate(aHint);
+ if ( m_pStartNode )
+ {
+ // must iterate over all TextNodes because of footnotes on other pages
+ SwNodeOffset nSttIdx = m_pStartNode->GetIndex() + 1;
+ SwNodeOffset nEndIdx = m_pStartNode->GetNode().EndOfSectionIndex();
+ for( ; nSttIdx < nEndIdx; ++nSttIdx )
+ {
+ SwNode* pNd;
+ if( ( pNd = rNodes[ nSttIdx ] )->IsTextNode() )
+ static_cast<SwTextNode*>(pNd)->TriggerNodeUpdate(aHint);
+ }
+ }
+}
+
+void SwTextFootnote::CopyFootnote(
+ SwTextFootnote & rDest,
+ SwTextNode & rDestNode ) const
+{
+ if (m_pStartNode && !rDest.GetStartNode())
+ {
+ // dest missing node section? create it here!
+ // (happens in SwTextNode::CopyText if pDest == this)
+ rDest.MakeNewTextSection( rDestNode.GetNodes() );
+ }
+ if (m_pStartNode && rDest.GetStartNode())
+ {
+ // footnotes not necessarily in same document!
+ SwDoc& rDstDoc = rDestNode.GetDoc();
+ SwNodes &rDstNodes = rDstDoc.GetNodes();
+
+ // copy only the content of the section
+ SwNodeRange aRg( *m_pStartNode, SwNodeOffset(1),
+ *m_pStartNode->GetNode().EndOfSectionNode() );
+
+ // insert at the end of rDest, i.e., the nodes are appended.
+ // nDestLen contains number of ContentNodes in rDest _before_ copy.
+ SwNodeIndex aStart( *(rDest.GetStartNode()) );
+ SwNodeIndex aEnd( *aStart.GetNode().EndOfSectionNode() );
+ SwNodeOffset nDestLen = aEnd.GetIndex() - aStart.GetIndex() - 1;
+
+ m_pTextNode->GetDoc().GetDocumentContentOperationsManager().CopyWithFlyInFly(aRg, aEnd);
+
+ // in case the destination section was not empty, delete the old nodes
+ // before: Src: SxxxE, Dst: SnE
+ // now: Src: SxxxE, Dst: SnxxxE
+ // after: Src: SxxxE, Dst: SxxxE
+ ++aStart;
+ rDstNodes.Delete( aStart, nDestLen );
+ }
+
+ // also copy user defined number string
+ if( !GetFootnote().m_aNumber.isEmpty() )
+ {
+ const_cast<SwFormatFootnote &>(rDest.GetFootnote()).m_aNumber = GetFootnote().m_aNumber;
+ }
+}
+
+/// create a new nodes-array section for the footnote
+void SwTextFootnote::MakeNewTextSection( SwNodes& rNodes )
+{
+ if ( m_pStartNode )
+ return;
+
+ // set the footnote style on the SwTextNode
+ SwTextFormatColl *pFormatColl;
+ const SwEndNoteInfo* pInfo;
+ sal_uInt16 nPoolId;
+
+ if( GetFootnote().IsEndNote() )
+ {
+ pInfo = &rNodes.GetDoc().GetEndNoteInfo();
+ nPoolId = RES_POOLCOLL_ENDNOTE;
+ }
+ else
+ {
+ pInfo = &rNodes.GetDoc().GetFootnoteInfo();
+ nPoolId = RES_POOLCOLL_FOOTNOTE;
+ }
+
+ pFormatColl = pInfo->GetFootnoteTextColl();
+ if( nullptr == pFormatColl )
+ pFormatColl = rNodes.GetDoc().getIDocumentStylePoolAccess().GetTextCollFromPool( nPoolId );
+
+ SwStartNode* pSttNd = rNodes.MakeTextSection( SwNodeIndex( rNodes.GetEndOfInserts() ),
+ SwFootnoteStartNode, pFormatColl );
+ m_pStartNode.reset(new SwNodeIndex(*pSttNd));
+}
+
+void SwTextFootnote::DelFrames(SwRootFrame const*const pRoot)
+{
+ // delete the FootnoteFrames from the pages
+ OSL_ENSURE( m_pTextNode, "SwTextFootnote: where is my TextNode?" );
+ if ( !m_pTextNode )
+ return;
+
+ bool bFrameFnd = false;
+ {
+ SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*m_pTextNode);
+ for( SwContentFrame* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
+ {
+ if( pRoot != pFnd->getRootFrame() && pRoot )
+ continue;
+ SwPageFrame* pPage = pFnd->FindPageFrame();
+ if( pPage )
+ {
+ // note: we have found the correct frame only if the footnote
+ // was actually removed; in case this is called from
+ // SwTextFrame::DestroyImpl(), then that frame isn't connected
+ // to SwPageFrame any more, and RemoveFootnote on any follow
+ // must not prevent the fall-back to the !bFrameFnd code.
+ bFrameFnd = pPage->RemoveFootnote(pFnd, this);
+ }
+ }
+ }
+ //JP 13.05.97: if the layout is deleted before the footnotes are deleted,
+ // try to delete the footnote's frames by another way
+ if ( bFrameFnd || !m_pStartNode )
+ return;
+
+ SwNodeIndex aIdx( *m_pStartNode );
+ SwContentNode* pCNd = m_pTextNode->GetNodes().GoNext( &aIdx );
+ if( !pCNd )
+ return;
+
+ SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pCNd);
+ for( SwContentFrame* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
+ {
+ if( pRoot != pFnd->getRootFrame() && pRoot )
+ continue;
+ SwPageFrame* pPage = pFnd->FindPageFrame();
+
+ SwFrame *pFrame = pFnd->GetUpper();
+ while ( pFrame && !pFrame->IsFootnoteFrame() )
+ pFrame = pFrame->GetUpper();
+
+ SwFootnoteFrame *pFootnote = static_cast<SwFootnoteFrame*>(pFrame);
+ while ( pFootnote && pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ OSL_ENSURE( pFootnote->GetAttr() == this, "Footnote mismatch error." );
+
+ while ( pFootnote )
+ {
+ SwFootnoteFrame *pFoll = pFootnote->GetFollow();
+ pFootnote->Cut();
+ SwFrame::DestroyFrame(pFootnote);
+ pFootnote = pFoll;
+ }
+
+ // #i20556# During hiding of a section, the connection
+ // to the layout is already lost. pPage may be 0:
+ if ( pPage )
+ pPage->UpdateFootnoteNum();
+ }
+}
+
+/// Set the sequence number for the current footnote.
+/// @returns The new sequence number or USHRT_MAX if invalid.
+void SwTextFootnote::SetSeqRefNo()
+{
+ if( !m_pTextNode )
+ return;
+
+ SwDoc& rDoc = m_pTextNode->GetDoc();
+ if( rDoc.IsInReading() )
+ return;
+
+ std::set<sal_uInt16> aUsedNums;
+ std::vector<SwTextFootnote*> badRefNums;
+ ::lcl_FillUsedFootnoteRefNumbers(rDoc, this, aUsedNums, badRefNums);
+ if ( ::lcl_IsRefNumAvailable(aUsedNums, m_nSeqNo) )
+ return;
+ std::vector<sal_uInt16> unused;
+ ::lcl_FillUnusedSeqRefNums(unused, aUsedNums, 1);
+ m_nSeqNo = unused[0];
+}
+
+/// Set a unique sequential reference number for every footnote in the document.
+/// @param[in] rDoc The document to be processed.
+void SwTextFootnote::SetUniqueSeqRefNo( SwDoc& rDoc )
+{
+ std::set<sal_uInt16> aUsedNums;
+ std::vector<SwTextFootnote*> badRefNums;
+ ::lcl_FillUsedFootnoteRefNumbers(rDoc, nullptr, aUsedNums, badRefNums);
+ std::vector<sal_uInt16> aUnused;
+ ::lcl_FillUnusedSeqRefNums(aUnused, aUsedNums, badRefNums.size());
+
+ for (size_t i = 0; i < badRefNums.size(); ++i)
+ {
+ badRefNums[i]->m_nSeqNo = aUnused[i];
+ }
+}
+
+void SwTextFootnote::CheckCondColl()
+{
+//FEATURE::CONDCOLL
+ if( GetStartNode() )
+ static_cast<SwStartNode&>(GetStartNode()->GetNode()).CheckSectionCondColl();
+//FEATURE::CONDCOLL
+}
+
+void SwTextFootnote::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFootnote"));
+ SwTextAttr::dumpAsXml(pWriter);
+
+ if (m_pStartNode)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pStartNode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(sal_Int32(m_pStartNode->GetIndex())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ if (m_pTextNode)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pTextNode"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(sal_Int32(m_pTextNode->GetIndex())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_nSeqNo"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(m_nSeqNo).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/atrref.cxx b/sw/source/core/txtnode/atrref.cxx
new file mode 100644
index 000000000..e28180305
--- /dev/null
+++ b/sw/source/core/txtnode/atrref.cxx
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <fmtrfmrk.hxx>
+
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <txtrfmrk.hxx>
+
+SwFormatRefMark::~SwFormatRefMark( )
+{
+}
+
+SwFormatRefMark::SwFormatRefMark( const OUString& rName )
+ : SfxPoolItem(RES_TXTATR_REFMARK)
+ , sw::BroadcastingModify()
+ , m_pTextAttr(nullptr)
+ , m_aRefName(rName)
+{
+}
+
+SwFormatRefMark::SwFormatRefMark( const SwFormatRefMark& rAttr )
+ : SfxPoolItem(RES_TXTATR_REFMARK)
+ , sw::BroadcastingModify()
+ , m_pTextAttr(nullptr)
+ , m_aRefName(rAttr.m_aRefName)
+{
+}
+
+bool SwFormatRefMark::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return m_aRefName == static_cast<const SwFormatRefMark&>(rAttr).m_aRefName;
+}
+
+SwFormatRefMark* SwFormatRefMark::Clone( SfxItemPool* ) const
+{
+ return new SwFormatRefMark( *this );
+}
+
+void SwFormatRefMark::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
+ SetXRefMark(css::uno::Reference<css::text::XTextContent>(nullptr));
+}
+
+void SwFormatRefMark::InvalidateRefMark()
+{
+ SwPtrMsgPoolItem const item(RES_REMOVE_UNO_OBJECT,
+ &static_cast<sw::BroadcastingModify&>(*this)); // cast to base class (void*)
+ CallSwClientNotify(sw::LegacyModifyHint(&item, &item));
+}
+
+// attribute for content references in the text
+
+SwTextRefMark::SwTextRefMark( SwFormatRefMark& rAttr,
+ sal_Int32 const nStartPos, sal_Int32 const*const pEnd)
+ : SwTextAttr(rAttr, nStartPos)
+ , SwTextAttrEnd( rAttr, nStartPos, nStartPos )
+ , m_pTextNode( nullptr )
+ , m_pEnd( nullptr )
+{
+ rAttr.m_pTextAttr = this;
+ if ( pEnd )
+ {
+ m_nEnd = *pEnd;
+ m_pEnd = & m_nEnd;
+ }
+ else
+ {
+ SetHasDummyChar(true);
+ }
+ SetDontMoveAttr( true );
+ SetOverlapAllowedAttr( true );
+}
+
+const sal_Int32* SwTextRefMark::GetEnd() const
+{
+ return m_pEnd;
+}
+
+void SwTextRefMark::SetEnd(sal_Int32 n)
+{
+ *m_pEnd = n;
+ if (m_pHints)
+ m_pHints->EndPosChanged();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/atrtox.cxx b/sw/source/core/txtnode/atrtox.cxx
new file mode 100644
index 000000000..664ce5596
--- /dev/null
+++ b/sw/source/core/txtnode/atrtox.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <txttxmrk.hxx>
+#include <tox.hxx>
+
+SwTextTOXMark::SwTextTOXMark( SwTOXMark& rAttr,
+ sal_Int32 const nStartPos, sal_Int32 const*const pEnd)
+ : SwTextAttr( rAttr, nStartPos )
+ , SwTextAttrEnd( rAttr, nStartPos, nStartPos )
+ , m_pTextNode( nullptr )
+ , m_pEnd( nullptr )
+{
+ rAttr.m_pTextAttr = this;
+ if ( rAttr.GetAlternativeText().isEmpty() )
+ {
+ m_nEnd = *pEnd;
+ m_pEnd = & m_nEnd;
+ }
+ else
+ {
+ SetHasDummyChar(true);
+ }
+ SetDontMoveAttr( true );
+ SetOverlapAllowedAttr( true );
+}
+
+SwTextTOXMark::~SwTextTOXMark()
+{
+}
+
+const sal_Int32* SwTextTOXMark::GetEnd() const
+{
+ return m_pEnd;
+}
+
+void SwTextTOXMark::SetEnd(sal_Int32 n)
+{
+ *m_pEnd = n;
+ if (m_pHints)
+ m_pHints->EndPosChanged();
+}
+
+void SwTextTOXMark::CopyTOXMark( SwDoc& rDoc )
+{
+ SwTOXMark& rTOX = const_cast<SwTOXMark&>(GetTOXMark());
+ TOXTypes eType = rTOX.GetTOXType()->GetType();
+ const sal_uInt16 nCount = rDoc.GetTOXTypeCount( eType );
+ const SwTOXType* pType = nullptr;
+ const OUString rNm = rTOX.GetTOXType()->GetTypeName();
+
+ for(sal_uInt16 i=0; i < nCount; ++i)
+ {
+ const SwTOXType* pSrcType = rDoc.GetTOXType(eType, i);
+ if(pSrcType->GetTypeName() == rNm )
+ {
+ pType = pSrcType;
+ break;
+ }
+ }
+
+ // if the requested tox type does not exist, create it
+ if(!pType)
+ {
+ rDoc.InsertTOXType( SwTOXType( rDoc, eType, rNm ) );
+ pType = rDoc.GetTOXType(eType, 0);
+ }
+
+ // register at target tox type
+ const_cast<SwTOXType*>(pType)->Add( &rTOX );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx
new file mode 100644
index 000000000..fb2176024
--- /dev/null
+++ b/sw/source/core/txtnode/attrcontentcontrol.cxx
@@ -0,0 +1,506 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <formatcontentcontrol.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <sal/log.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <svl/numformat.hxx>
+#include <vcl/keycod.hxx>
+
+#include <ndtxt.hxx>
+#include <textcontentcontrol.hxx>
+#include <doc.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+inline constexpr OUStringLiteral CURRENT_DATE_FORMAT = u"YYYY-MM-DD";
+}
+
+SwFormatContentControl* SwFormatContentControl::CreatePoolDefault(sal_uInt16 nWhich)
+{
+ return new SwFormatContentControl(nWhich);
+}
+
+SwFormatContentControl::SwFormatContentControl(sal_uInt16 nWhich)
+ : SfxPoolItem(nWhich)
+ , m_pTextAttr(nullptr)
+{
+}
+
+SwFormatContentControl::SwFormatContentControl(
+ const std::shared_ptr<SwContentControl>& pContentControl, sal_uInt16 nWhich)
+ : SfxPoolItem(nWhich)
+ , m_pContentControl(pContentControl)
+ , m_pTextAttr(nullptr)
+{
+ if (!pContentControl)
+ {
+ SAL_WARN("sw.core", "SwFormatContentControl ctor: no pContentControl?");
+ }
+ // Not calling m_pContentControl->SetFormatContentControl(this) here; only from SetTextAttr.
+}
+
+SwFormatContentControl::~SwFormatContentControl()
+{
+ if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this))
+ {
+ NotifyChangeTextNode(nullptr);
+ m_pContentControl->SetFormatContentControl(nullptr);
+ }
+}
+
+bool SwFormatContentControl::operator==(const SfxPoolItem& rOther) const
+{
+ return SfxPoolItem::operator==(rOther)
+ && m_pContentControl
+ == static_cast<const SwFormatContentControl&>(rOther).m_pContentControl;
+}
+
+SwFormatContentControl* SwFormatContentControl::Clone(SfxItemPool* /*pPool*/) const
+{
+ // If this is indeed a copy, then DoCopy will be called later.
+ if (m_pContentControl)
+ {
+ return new SwFormatContentControl(m_pContentControl, Which());
+ }
+ else
+ {
+ return new SwFormatContentControl(Which());
+ }
+}
+
+void SwFormatContentControl::SetTextAttr(SwTextContentControl* pTextAttr)
+{
+ if (m_pTextAttr && pTextAttr)
+ {
+ SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: already has a text attribute");
+ }
+ if (!m_pTextAttr && !pTextAttr)
+ {
+ SAL_WARN("sw.core", "SwFormatContentControl::SetTextAttr: no attribute to remove");
+ }
+ m_pTextAttr = pTextAttr;
+ if (!m_pContentControl)
+ {
+ SAL_WARN("sw.core", "inserted SwFormatContentControl has no SwContentControl");
+ }
+ // The SwContentControl should be able to find the current text attribute.
+ if (m_pContentControl)
+ {
+ if (pTextAttr)
+ {
+ m_pContentControl->SetFormatContentControl(this);
+ }
+ else if (m_pContentControl->GetFormatContentControl() == this)
+ {
+ // The text attribute is gone, so de-register from text node.
+ NotifyChangeTextNode(nullptr);
+ m_pContentControl->SetFormatContentControl(nullptr);
+ }
+ }
+}
+
+void SwFormatContentControl::NotifyChangeTextNode(SwTextNode* pTextNode)
+{
+ // Not deleting m_pTextAttr here, SwNodes::ChgNode() doesn't do that, either.
+ if (!m_pContentControl)
+ {
+ SAL_WARN("sw.core", "SwFormatContentControl::NotifyChangeTextNode: no content control?");
+ }
+ if (m_pContentControl && (m_pContentControl->GetFormatContentControl() == this))
+ {
+ // Not calling Modify, that would call SwXContentControl::SwClientNotify.
+ m_pContentControl->NotifyChangeTextNode(pTextNode);
+ }
+}
+
+// This SwFormatContentControl has been cloned and points at the same SwContentControl as the
+// source: this function copies the SwContentControl.
+void SwFormatContentControl::DoCopy(SwTextNode& rTargetTextNode)
+{
+ if (!m_pContentControl)
+ {
+ SAL_WARN("sw.core", "SwFormatContentControl::DoCopy: called for SwFormatContentControl "
+ "with no SwContentControl.");
+ return;
+ }
+
+ m_pContentControl = std::make_shared<SwContentControl>(this);
+ m_pContentControl->NotifyChangeTextNode(&rTargetTextNode);
+}
+
+void SwFormatContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatContentControl"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pTextAttr"), "%p", m_pTextAttr);
+ SfxPoolItem::dumpAsXml(pWriter);
+
+ if (m_pContentControl)
+ {
+ m_pContentControl->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwContentControl::SwContentControl(SwFormatContentControl* pFormat)
+ : sw::BroadcastingModify()
+ , m_pFormat(pFormat)
+ , m_pTextNode(nullptr)
+{
+}
+
+SwContentControl::~SwContentControl() {}
+
+SwTextContentControl* SwContentControl::GetTextAttr() const
+{
+ return m_pFormat ? m_pFormat->GetTextAttr() : nullptr;
+}
+
+void SwContentControl::NotifyChangeTextNode(SwTextNode* pTextNode)
+{
+ m_pTextNode = pTextNode;
+ if (m_pTextNode && (GetRegisteredIn() != m_pTextNode))
+ {
+ m_pTextNode->Add(this);
+ }
+ else if (!m_pTextNode)
+ {
+ EndListeningAll();
+ }
+ if (!pTextNode)
+ {
+ // If the text node is gone, then invalidate clients (e.g. UNO object).
+ GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
+ }
+}
+
+void SwContentControl::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged));
+
+ if (pLegacy->GetWhich() == RES_REMOVE_UNO_OBJECT)
+ {
+ // Invalidate cached uno object.
+ SetXContentControl(uno::Reference<text::XTextContent>());
+ GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
+ }
+}
+
+OUString SwContentControl::GetDateString() const
+{
+ SwDoc& rDoc = m_pTextNode->GetDoc();
+ SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
+ sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(
+ m_aDateFormat, LanguageTag(m_aDateLanguage).getLanguageType());
+
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ // If not found, then create it.
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString aFormat = m_aDateFormat;
+ pNumberFormatter->PutEntry(aFormat, nCheckPos, nType, nFormat,
+ LanguageTag(m_aDateLanguage).getLanguageType());
+ }
+
+ const Color* pColor = nullptr;
+ OUString aFormatted;
+ if (!m_oSelectedDate)
+ {
+ return OUString();
+ }
+
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ return OUString();
+ }
+
+ pNumberFormatter->GetOutputString(*m_oSelectedDate, nFormat, aFormatted, &pColor, false);
+ return aFormatted;
+}
+
+void SwContentControl::SetCurrentDateValue(double fCurrentDate)
+{
+ SwDoc& rDoc = m_pTextNode->GetDoc();
+ SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
+ OUString aFormatted;
+ sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US);
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ // If not found, then create it.
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString sFormat = CURRENT_DATE_FORMAT;
+ pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
+ }
+
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ return;
+ }
+
+ const Color* pColor = nullptr;
+ pNumberFormatter->GetOutputString(fCurrentDate, nFormat, aFormatted, &pColor, false);
+ m_aCurrentDate = aFormatted + "T00:00:00Z";
+}
+
+double SwContentControl::GetCurrentDateValue() const
+{
+ if (m_aCurrentDate.isEmpty())
+ {
+ return 0;
+ }
+
+ SwDoc& rDoc = m_pTextNode->GetDoc();
+ SvNumberFormatter* pNumberFormatter = rDoc.GetNumberFormatter();
+ sal_uInt32 nFormat = pNumberFormatter->GetEntryKey(CURRENT_DATE_FORMAT, LANGUAGE_ENGLISH_US);
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_Int32 nCheckPos = 0;
+ SvNumFormatType nType;
+ OUString sFormat = CURRENT_DATE_FORMAT;
+ pNumberFormatter->PutEntry(sFormat, nCheckPos, nType, nFormat, LANGUAGE_ENGLISH_US);
+ }
+
+ if (nFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ return 0;
+ }
+
+ double dCurrentDate = 0;
+ OUString aCurrentDate = m_aCurrentDate.replaceAll("T00:00:00Z", "");
+ (void)pNumberFormatter->IsNumberFormat(aCurrentDate, nFormat, dCurrentDate);
+ return dCurrentDate;
+}
+
+bool SwContentControl::IsInteractingCharacter(sal_Unicode cCh)
+{
+ if (GetCheckbox())
+ {
+ return cCh == ' ';
+ }
+
+ if (GetPicture())
+ {
+ return cCh == '\r';
+ }
+
+ return false;
+}
+
+bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& rKeyCode)
+{
+ if (HasListItems() || GetDate())
+ {
+ // Alt-down opens the popup.
+ return rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_DOWN;
+ }
+
+ return false;
+}
+
+void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(
+ pWriter, BAD_CAST("showing-place-holder"), "%s",
+ BAD_CAST(OString::boolean(m_bShowingPlaceHolder).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checkbox"), "%s",
+ BAD_CAST(OString::boolean(m_bCheckbox).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checked"), "%s",
+ BAD_CAST(OString::boolean(m_bChecked).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("checked-state"), "%s",
+ BAD_CAST(m_aCheckedState.toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("unchecked-state"), "%s",
+ BAD_CAST(m_aUncheckedState.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("picture"),
+ BAD_CAST(OString::boolean(m_bPicture).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date"),
+ BAD_CAST(OString::boolean(m_bDate).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-format"),
+ BAD_CAST(m_aDateFormat.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("date-language"),
+ BAD_CAST(m_aDateLanguage.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("current-date"),
+ BAD_CAST(m_aCurrentDate.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("placeholder-doc-part"),
+ BAD_CAST(m_aPlaceholderDocPart.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-prefix-mappings"),
+ BAD_CAST(m_aDataBindingPrefixMappings.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-xpath"),
+ BAD_CAST(m_aDataBindingXpath.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("data-binding-store-item-id"),
+ BAD_CAST(m_aDataBindingStoreItemID.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("color"),
+ BAD_CAST(m_aColor.toUtf8().getStr()));
+
+ if (!m_aListItems.empty())
+ {
+ for (const auto& rListItem : m_aListItems)
+ {
+ rListItem.dumpAsXml(pWriter);
+ }
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwContentControlListItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControlListItem"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("display-text"),
+ BAD_CAST(m_aDisplayText.toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(m_aValue.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+OUString SwContentControlListItem::ToString() const
+{
+ if (!m_aDisplayText.isEmpty())
+ {
+ return m_aDisplayText;
+ }
+
+ return m_aValue;
+}
+
+bool SwContentControlListItem::operator==(const SwContentControlListItem& rOther) const
+{
+ return m_aDisplayText == rOther.m_aDisplayText && m_aValue == rOther.m_aValue;
+}
+
+void SwContentControlListItem::ItemsToAny(const std::vector<SwContentControlListItem>& rItems,
+ uno::Any& rVal)
+{
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aRet(rItems.size());
+
+ uno::Sequence<beans::PropertyValue>* pRet = aRet.getArray();
+ for (size_t i = 0; i < rItems.size(); ++i)
+ {
+ const SwContentControlListItem& rItem = rItems[i];
+ uno::Sequence<beans::PropertyValue> aItem = {
+ comphelper::makePropertyValue("DisplayText", rItem.m_aDisplayText),
+ comphelper::makePropertyValue("Value", rItem.m_aValue),
+ };
+ pRet[i] = aItem;
+ }
+
+ rVal <<= aRet;
+}
+
+std::vector<SwContentControlListItem>
+SwContentControlListItem::ItemsFromAny(const css::uno::Any& rVal)
+{
+ std::vector<SwContentControlListItem> aRet;
+
+ uno::Sequence<uno::Sequence<beans::PropertyValue>> aSequence;
+ rVal >>= aSequence;
+ for (const auto& rItem : aSequence)
+ {
+ comphelper::SequenceAsHashMap aMap(rItem);
+ SwContentControlListItem aItem;
+ auto it = aMap.find("DisplayText");
+ if (it != aMap.end())
+ {
+ it->second >>= aItem.m_aDisplayText;
+ }
+ it = aMap.find("Value");
+ if (it != aMap.end())
+ {
+ it->second >>= aItem.m_aValue;
+ }
+ aRet.push_back(aItem);
+ }
+
+ return aRet;
+}
+
+SwTextContentControl* SwTextContentControl::CreateTextContentControl(SwTextNode* pTargetTextNode,
+ SwFormatContentControl& rAttr,
+ sal_Int32 nStart,
+ sal_Int32 nEnd, bool bIsCopy)
+{
+ if (bIsCopy)
+ {
+ // rAttr is already cloned, now call DoCopy to copy the SwContentControl
+ if (!pTargetTextNode)
+ {
+ SAL_WARN("sw.core",
+ "SwTextContentControl ctor: cannot copy content control without target node");
+ }
+ rAttr.DoCopy(*pTargetTextNode);
+ }
+ auto pTextContentControl(new SwTextContentControl(rAttr, nStart, nEnd));
+ return pTextContentControl;
+}
+
+SwTextContentControl::SwTextContentControl(SwFormatContentControl& rAttr, sal_Int32 nStart,
+ sal_Int32 nEnd)
+ : SwTextAttr(rAttr, nStart)
+ , SwTextAttrNesting(rAttr, nStart, nEnd)
+{
+ rAttr.SetTextAttr(this);
+ SetHasDummyChar(true);
+}
+
+SwTextContentControl::~SwTextContentControl()
+{
+ auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
+ if (rFormatContentControl.GetTextAttr() == this)
+ {
+ rFormatContentControl.SetTextAttr(nullptr);
+ }
+}
+
+void SwTextContentControl::ChgTextNode(SwTextNode* pNode)
+{
+ auto& rFormatContentControl = static_cast<SwFormatContentControl&>(GetAttr());
+ if (rFormatContentControl.GetTextAttr() == this)
+ {
+ rFormatContentControl.NotifyChangeTextNode(pNode);
+ }
+}
+
+void SwTextContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextContentControl"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ SwTextAttr::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/attrlinebreak.cxx b/sw/source/core/txtnode/attrlinebreak.cxx
new file mode 100644
index 000000000..6c919a790
--- /dev/null
+++ b/sw/source/core/txtnode/attrlinebreak.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <formatlinebreak.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <vcl/svapp.hxx>
+
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <pam.hxx>
+#include <textlinebreak.hxx>
+#include <ndtxt.hxx>
+#include <unotextrange.hxx>
+
+using namespace com::sun::star;
+
+SwFormatLineBreak::SwFormatLineBreak(SwLineBreakClear eClear)
+ : SfxEnumItem(RES_TXTATR_LINEBREAK, eClear)
+ , sw::BroadcastingModify()
+ , m_pTextAttr(nullptr)
+{
+}
+
+SwFormatLineBreak::~SwFormatLineBreak() {}
+
+bool SwFormatLineBreak::operator==(const SfxPoolItem& rAttr) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return GetValue() == static_cast<const SwFormatLineBreak&>(rAttr).GetValue();
+}
+
+SwFormatLineBreak* SwFormatLineBreak::Clone(SfxItemPool*) const
+{
+ return new SwFormatLineBreak(GetValue());
+}
+
+void SwFormatLineBreak::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ if (RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
+ {
+ SetXLineBreak(css::uno::Reference<css::text::XTextContent>(nullptr));
+ }
+}
+
+sal_uInt16 SwFormatLineBreak::GetValueCount() const
+{
+ return static_cast<sal_uInt16>(SwLineBreakClear::LAST) + 1;
+}
+
+uno::Reference<text::XTextRange> SwFormatLineBreak::GetAnchor() const
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pTextAttr)
+ {
+ return uno::Reference<text::XTextRange>();
+ }
+
+ SwPaM aPam(m_pTextAttr->GetTextNode(), m_pTextAttr->GetStart());
+ aPam.SetMark();
+ ++aPam.GetMark()->nContent;
+ uno::Reference<text::XTextRange> xRet
+ = SwXTextRange::CreateXTextRange(aPam.GetDoc(), *aPam.Start(), aPam.End());
+ return xRet;
+}
+
+void SwFormatLineBreak::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatLineBreak"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(GetEnumValue()).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pTextAttr"), "%p", m_pTextAttr);
+
+ SfxPoolItem::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwTextLineBreak::SwTextLineBreak(SwFormatLineBreak& rAttr, sal_Int32 nStartPos)
+ : SwTextAttr(rAttr, nStartPos)
+ , m_pTextNode(nullptr)
+{
+ rAttr.SetTextLineBreak(this);
+ SetHasDummyChar(true);
+}
+
+SwTextLineBreak::~SwTextLineBreak() {}
+
+void SwTextLineBreak::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextLineBreak"));
+ if (m_pTextNode)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pTextNode"));
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(sal_Int32(m_pTextNode->GetIndex())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ SwTextAttr::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwTextLineBreak::SetTextNode(SwTextNode* pNew) { m_pTextNode = pNew; }
+
+const SwTextNode& SwTextLineBreak::GetTextNode() const
+{
+ assert(m_pTextNode);
+ return *m_pTextNode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/chrfmt.cxx b/sw/source/core/txtnode/chrfmt.cxx
new file mode 100644
index 000000000..11a89d6eb
--- /dev/null
+++ b/sw/source/core/txtnode/chrfmt.cxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <charfmt.hxx>
+#include <charformats.hxx>
+#include <doc.hxx>
+
+void SwCharFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwCharFormat"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"),
+ BAD_CAST(GetName().toUtf8().getStr()));
+
+ if (mpLinkedParaFormat)
+ {
+ (void)xmlTextWriterWriteAttribute(
+ pWriter, BAD_CAST("linked"), BAD_CAST(mpLinkedParaFormat->GetName().toUtf8().getStr()));
+ }
+
+ GetAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwCharFormat::SetLinkedParaFormat(SwTextFormatColl* pLink) { mpLinkedParaFormat = pLink; }
+
+const SwTextFormatColl* SwCharFormat::GetLinkedParaFormat() const { return mpLinkedParaFormat; }
+
+SwCharFormat::~SwCharFormat()
+{
+ if (GetDoc()->IsInDtor())
+ {
+ return;
+ }
+
+ for (const auto& pTextFormat : *GetDoc()->GetTextFormatColls())
+ {
+ if (pTextFormat->GetLinkedCharFormat() == this)
+ {
+ pTextFormat->SetLinkedCharFormat(nullptr);
+ }
+ }
+}
+
+void SwCharFormats::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwCharFormats"));
+ for (size_t i = 0; i < size(); ++i)
+ GetFormat(i)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwCharFormats::SwCharFormats()
+ : m_PosIndex(m_Array.get<0>())
+ , m_NameIndex(m_Array.get<1>())
+{
+}
+
+SwCharFormats::~SwCharFormats()
+{
+ // default char format is owned by SwDoc
+ DeleteAndDestroyAll(true);
+}
+
+SwCharFormats::const_iterator SwCharFormats::find(const SwCharFormat* x) const
+{
+ ByName::iterator it
+ = m_NameIndex.find(std::make_tuple(x->GetName(), const_cast<SwCharFormat*>(x)));
+ return m_Array.project<0>(it);
+}
+
+SwCharFormats::ByName::const_iterator SwCharFormats::findByName(const OUString& name) const
+{
+ return m_NameIndex.find(std::make_tuple(name));
+}
+
+SwCharFormat* SwCharFormats::FindFormatByName(const OUString& rName) const
+{
+ auto it = findByName(rName);
+ if (it != m_NameIndex.end())
+ return *it;
+ return nullptr;
+}
+
+void SwCharFormats::DeleteAndDestroyAll(bool keepDefault)
+{
+ if (empty())
+ return;
+ const int _offset = keepDefault ? 1 : 0;
+ for (const_iterator it = begin() + _offset; it != end(); ++it)
+ {
+ assert(!(*it)->HasName(u"Character style"));
+ delete *it;
+ }
+ if (_offset)
+ m_PosIndex.erase(begin() + _offset, end());
+ else
+ m_Array.clear();
+}
+
+void SwCharFormats::insert(SwCharFormat* x)
+{
+ assert(!ContainsFormat(x));
+ m_PosIndex.push_back(x);
+}
+
+void SwCharFormats::erase(const_iterator const& position) { m_PosIndex.erase(position); }
+
+bool SwCharFormats::ContainsFormat(const SwCharFormat* x) const { return find(x) != end(); }
+
+/** Need to call this when the format name changes */
+void SwCharFormats::SetFormatNameAndReindex(SwCharFormat* v, const OUString& sNewName)
+{
+ auto it = find(v);
+ erase(it);
+ v->SetName(sNewName);
+ insert(v);
+}
+
+size_t SwCharFormats::GetPos(const SwCharFormat* p) const
+{
+ auto it = find(p);
+ return it == end() ? SIZE_MAX : it - begin();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/fmtatr2.cxx b/sw/source/core/txtnode/fmtatr2.cxx
new file mode 100644
index 000000000..67a55e28c
--- /dev/null
+++ b/sw/source/core/txtnode/fmtatr2.cxx
@@ -0,0 +1,849 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+#include <hintids.hxx>
+#include <poolfmt.hxx>
+#include <unomid.h>
+
+#include <o3tl/any.hxx>
+#include <svl/macitem.hxx>
+#include <svl/stylepool.hxx>
+#include <fmtautofmt.hxx>
+#include <fchrfmt.hxx>
+#include <fmtinfmt.hxx>
+#include <txtatr.hxx>
+#include <fmtruby.hxx>
+#include <charfmt.hxx>
+#include <unoevent.hxx>
+#include <com/sun/star/text/RubyAdjust.hpp>
+#include <com/sun/star/text/RubyPosition.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+
+#include <com/sun/star/uno/Any.h>
+#include <SwStyleNameMapper.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <fmtmeta.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <unometa.hxx>
+#include <docsh.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SwFormatINetFormat::CreateDefault() { return new SwFormatINetFormat; }
+
+SwFormatCharFormat::SwFormatCharFormat( SwCharFormat *pFormat )
+ : SfxPoolItem( RES_TXTATR_CHARFMT ),
+ SwClient(pFormat),
+ m_pTextAttribute( nullptr )
+{
+}
+
+SwFormatCharFormat::SwFormatCharFormat( const SwFormatCharFormat& rAttr )
+ : SfxPoolItem( RES_TXTATR_CHARFMT ),
+ SwClient( rAttr.GetCharFormat() ),
+ m_pTextAttribute( nullptr )
+{
+}
+
+SwFormatCharFormat::~SwFormatCharFormat() {}
+
+bool SwFormatCharFormat::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return GetCharFormat() == static_cast<const SwFormatCharFormat&>(rAttr).GetCharFormat();
+}
+
+SwFormatCharFormat* SwFormatCharFormat::Clone( SfxItemPool* ) const
+{
+ return new SwFormatCharFormat( *this );
+}
+
+// forward to the TextAttribute
+void SwFormatCharFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(m_pTextAttribute)
+ m_pTextAttribute->TriggerNodeUpdate(*pLegacy);
+}
+
+// forward to the TextAttribute
+bool SwFormatCharFormat::GetInfo( SfxPoolItem& rInfo ) const
+{
+ return m_pTextAttribute && m_pTextAttribute->GetInfo( rInfo );
+}
+bool SwFormatCharFormat::QueryValue( uno::Any& rVal, sal_uInt8 ) const
+{
+ OUString sCharFormatName;
+ if(GetCharFormat())
+ SwStyleNameMapper::FillProgName(GetCharFormat()->GetName(), sCharFormatName, SwGetPoolIdFromName::ChrFmt );
+ rVal <<= sCharFormatName;
+ return true;
+}
+bool SwFormatCharFormat::PutValue( const uno::Any& , sal_uInt8 )
+{
+ OSL_FAIL("format cannot be set with PutValue!");
+ return false;
+}
+
+SwFormatAutoFormat::SwFormatAutoFormat( sal_uInt16 nInitWhich )
+ : SfxPoolItem( nInitWhich )
+{
+}
+
+bool SwFormatAutoFormat::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return mpHandle == static_cast<const SwFormatAutoFormat&>(rAttr).mpHandle;
+}
+
+SwFormatAutoFormat* SwFormatAutoFormat::Clone( SfxItemPool* ) const
+{
+ return new SwFormatAutoFormat( *this );
+}
+
+bool SwFormatAutoFormat::QueryValue( uno::Any& rVal, sal_uInt8 ) const
+{
+ rVal <<= StylePool::nameOf( mpHandle );
+ return true;
+}
+
+bool SwFormatAutoFormat::PutValue( const uno::Any& , sal_uInt8 )
+{
+ //the format is not renameable via API
+ return false;
+}
+
+void SwFormatAutoFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwFormatAutoFormat"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ if (mpHandle) // pool default doesn't have one
+ {
+ mpHandle->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwFormatINetFormat::SwFormatINetFormat()
+ : SfxPoolItem( RES_TXTATR_INETFMT )
+ , msURL()
+ , msTargetFrame()
+ , msINetFormatName()
+ , msVisitedFormatName()
+ , msHyperlinkName()
+ , mpTextAttr( nullptr )
+ , mnINetFormatId( 0 )
+ , mnVisitedFormatId( 0 )
+{}
+
+SwFormatINetFormat::SwFormatINetFormat( const OUString& rURL, const OUString& rTarget )
+ : SfxPoolItem( RES_TXTATR_INETFMT )
+ , msURL( rURL )
+ , msTargetFrame( rTarget )
+ , msINetFormatName()
+ , msVisitedFormatName()
+ , msHyperlinkName()
+ , mpTextAttr( nullptr )
+ , mnINetFormatId( RES_POOLCHR_INET_NORMAL )
+ , mnVisitedFormatId( RES_POOLCHR_INET_VISIT )
+{
+ SwStyleNameMapper::FillUIName( mnINetFormatId, msINetFormatName );
+ SwStyleNameMapper::FillUIName( mnVisitedFormatId, msVisitedFormatName );
+}
+
+SwFormatINetFormat::SwFormatINetFormat( const SwFormatINetFormat& rAttr )
+ : SfxPoolItem( RES_TXTATR_INETFMT )
+ , sw::BroadcasterMixin()
+ , msURL( rAttr.GetValue() )
+ , msTargetFrame( rAttr.msTargetFrame )
+ , msINetFormatName( rAttr.msINetFormatName )
+ , msVisitedFormatName( rAttr.msVisitedFormatName )
+ , msHyperlinkName( rAttr.msHyperlinkName )
+ , mpTextAttr( nullptr )
+ , mnINetFormatId( rAttr.mnINetFormatId )
+ , mnVisitedFormatId( rAttr.mnVisitedFormatId )
+{
+ if ( rAttr.GetMacroTable() )
+ mpMacroTable.reset( new SvxMacroTableDtor( *rAttr.GetMacroTable() ) );
+}
+
+SwFormatINetFormat::~SwFormatINetFormat()
+{
+}
+
+bool SwFormatINetFormat::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ bool bRet = SfxPoolItem::operator==( rAttr )
+ && msURL == static_cast<const SwFormatINetFormat&>(rAttr).msURL
+ && msHyperlinkName == static_cast<const SwFormatINetFormat&>(rAttr).msHyperlinkName
+ && msTargetFrame == static_cast<const SwFormatINetFormat&>(rAttr).msTargetFrame
+ && msINetFormatName == static_cast<const SwFormatINetFormat&>(rAttr).msINetFormatName
+ && msVisitedFormatName == static_cast<const SwFormatINetFormat&>(rAttr).msVisitedFormatName
+ && mnINetFormatId == static_cast<const SwFormatINetFormat&>(rAttr).mnINetFormatId
+ && mnVisitedFormatId == static_cast<const SwFormatINetFormat&>(rAttr).mnVisitedFormatId;
+
+ if( !bRet )
+ return false;
+
+ const SvxMacroTableDtor* pOther = static_cast<const SwFormatINetFormat&>(rAttr).mpMacroTable.get();
+ if( !mpMacroTable )
+ return ( !pOther || pOther->empty() );
+ if( !pOther )
+ return mpMacroTable->empty();
+
+ const SvxMacroTableDtor& rOwn = *mpMacroTable;
+ const SvxMacroTableDtor& rOther = *pOther;
+
+ return rOwn == rOther;
+}
+
+SwFormatINetFormat* SwFormatINetFormat::Clone( SfxItemPool* ) const
+{
+ return new SwFormatINetFormat( *this );
+}
+
+void SwFormatINetFormat::SetMacroTable( const SvxMacroTableDtor* pNewTable )
+{
+ if( pNewTable )
+ {
+ if( mpMacroTable )
+ *mpMacroTable = *pNewTable;
+ else
+ mpMacroTable.reset( new SvxMacroTableDtor( *pNewTable ) );
+ }
+ else
+ {
+ mpMacroTable.reset();
+ }
+}
+
+void SwFormatINetFormat::SetMacro( SvMacroItemId nEvent, const SvxMacro& rMacro )
+{
+ if( !mpMacroTable )
+ mpMacroTable.reset( new SvxMacroTableDtor );
+
+ mpMacroTable->Insert( nEvent, rMacro );
+}
+
+const SvxMacro* SwFormatINetFormat::GetMacro( SvMacroItemId nEvent ) const
+{
+ const SvxMacro* pRet = nullptr;
+ if( mpMacroTable && mpMacroTable->IsKeyValid( nEvent ) )
+ pRet = mpMacroTable->Get( nEvent );
+ return pRet;
+}
+
+bool SwFormatINetFormat::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_URL_URL:
+ rVal <<= msURL;
+ break;
+ case MID_URL_TARGET:
+ rVal <<= msTargetFrame;
+ break;
+ case MID_URL_HYPERLINKNAME:
+ rVal <<= msHyperlinkName;
+ break;
+ case MID_URL_VISITED_FMT:
+ {
+ OUString sVal = msVisitedFormatName;
+ if (sVal.isEmpty() && mnVisitedFormatId != 0)
+ SwStyleNameMapper::FillUIName(mnVisitedFormatId, sVal);
+ if (!sVal.isEmpty())
+ SwStyleNameMapper::FillProgName(sVal, sVal,
+ SwGetPoolIdFromName::ChrFmt);
+ rVal <<= sVal;
+ }
+ break;
+ case MID_URL_UNVISITED_FMT:
+ {
+ OUString sVal = msINetFormatName;
+ if (sVal.isEmpty() && mnINetFormatId != 0)
+ SwStyleNameMapper::FillUIName(mnINetFormatId, sVal);
+ if (!sVal.isEmpty())
+ SwStyleNameMapper::FillProgName(sVal, sVal,
+ SwGetPoolIdFromName::ChrFmt);
+ rVal <<= sVal;
+ }
+ break;
+ case MID_URL_HYPERLINKEVENTS:
+ {
+ // create (and return) event descriptor
+ rtl::Reference<SwHyperlinkEventDescriptor> pEvents =
+ new SwHyperlinkEventDescriptor();
+ pEvents->copyMacrosFromINetFormat(*this);
+
+ // all others return a string; so we just set rVal here and exit
+ rVal <<= uno::Reference<container::XNameReplace>(pEvents);
+ }
+ break;
+ default:
+ rVal <<= OUString();
+ break;
+ }
+ return true;
+}
+bool SwFormatINetFormat::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ nMemberId &= ~CONVERT_TWIPS;
+
+ // all properties except HyperlinkEvents are of type string, hence
+ // we treat HyperlinkEvents specially
+ if (MID_URL_HYPERLINKEVENTS == nMemberId)
+ {
+ uno::Reference<container::XNameReplace> xReplace;
+ rVal >>= xReplace;
+ if (xReplace.is())
+ {
+ // Create hyperlink event descriptor. Then copy events
+ // from argument into descriptor. Then copy events from
+ // the descriptor into the format.
+ rtl::Reference<SwHyperlinkEventDescriptor> pEvents = new SwHyperlinkEventDescriptor();
+ pEvents->copyMacrosFromNameReplace(xReplace);
+ pEvents->copyMacrosIntoINetFormat(*this);
+ }
+ else
+ {
+ // wrong type!
+ bRet = false;
+ }
+ }
+ else
+ {
+ // all string properties:
+ if(rVal.getValueType() != ::cppu::UnoType<OUString>::get())
+ return false;
+
+ switch(nMemberId)
+ {
+ case MID_URL_URL:
+ rVal >>= msURL;
+ break;
+ case MID_URL_TARGET:
+ rVal >>= msTargetFrame;
+ break;
+ case MID_URL_HYPERLINKNAME:
+ rVal >>= msHyperlinkName;
+ break;
+ case MID_URL_VISITED_FMT:
+ {
+ OUString sVal;
+ rVal >>= sVal;
+ OUString aString;
+ SwStyleNameMapper::FillUIName( sVal, aString, SwGetPoolIdFromName::ChrFmt );
+ msVisitedFormatName = aString;
+ mnVisitedFormatId = SwStyleNameMapper::GetPoolIdFromUIName( msVisitedFormatName,
+ SwGetPoolIdFromName::ChrFmt );
+ }
+ break;
+ case MID_URL_UNVISITED_FMT:
+ {
+ OUString sVal;
+ rVal >>= sVal;
+ OUString aString;
+ SwStyleNameMapper::FillUIName( sVal, aString, SwGetPoolIdFromName::ChrFmt );
+ msINetFormatName = aString;
+ mnINetFormatId = SwStyleNameMapper::GetPoolIdFromUIName( msINetFormatName, SwGetPoolIdFromName::ChrFmt );
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+SwFormatRuby::SwFormatRuby( const OUString& rRubyText )
+ : SfxPoolItem( RES_TXTATR_CJK_RUBY ),
+ m_sRubyText( rRubyText ),
+ m_pTextAttr( nullptr ),
+ m_nCharFormatId( 0 ),
+ m_nPosition( 0 ),
+ m_eAdjustment( css::text::RubyAdjust_LEFT )
+{
+}
+
+SwFormatRuby::SwFormatRuby( const SwFormatRuby& rAttr )
+ : SfxPoolItem( RES_TXTATR_CJK_RUBY ),
+ m_sRubyText( rAttr.m_sRubyText ),
+ m_sCharFormatName( rAttr.m_sCharFormatName ),
+ m_pTextAttr( nullptr ),
+ m_nCharFormatId( rAttr.m_nCharFormatId),
+ m_nPosition( rAttr.m_nPosition ),
+ m_eAdjustment( rAttr.m_eAdjustment )
+{
+}
+
+SwFormatRuby::~SwFormatRuby()
+{
+}
+
+SwFormatRuby& SwFormatRuby::operator=( const SwFormatRuby& rAttr )
+{
+ if(this == &rAttr)
+ return *this;
+
+ m_sRubyText = rAttr.m_sRubyText;
+ m_sCharFormatName = rAttr.m_sCharFormatName;
+ m_nCharFormatId = rAttr.m_nCharFormatId;
+ m_nPosition = rAttr.m_nPosition;
+ m_eAdjustment = rAttr.m_eAdjustment;
+ m_pTextAttr = nullptr;
+ return *this;
+}
+
+bool SwFormatRuby::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return m_sRubyText == static_cast<const SwFormatRuby&>(rAttr).m_sRubyText &&
+ m_sCharFormatName == static_cast<const SwFormatRuby&>(rAttr).m_sCharFormatName &&
+ m_nCharFormatId == static_cast<const SwFormatRuby&>(rAttr).m_nCharFormatId &&
+ m_nPosition == static_cast<const SwFormatRuby&>(rAttr).m_nPosition &&
+ m_eAdjustment == static_cast<const SwFormatRuby&>(rAttr).m_eAdjustment;
+}
+
+SwFormatRuby* SwFormatRuby::Clone( SfxItemPool* ) const
+{
+ return new SwFormatRuby( *this );
+}
+
+bool SwFormatRuby::QueryValue( uno::Any& rVal,
+ sal_uInt8 nMemberId ) const
+{
+ bool bRet = true;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_RUBY_TEXT: rVal <<= m_sRubyText; break;
+ case MID_RUBY_ADJUST: rVal <<= static_cast<sal_Int16>(m_eAdjustment); break;
+ case MID_RUBY_CHARSTYLE:
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName(m_sCharFormatName, aString, SwGetPoolIdFromName::ChrFmt );
+ rVal <<= aString;
+ }
+ break;
+ case MID_RUBY_ABOVE:
+ {
+ rVal <<= static_cast<bool>(!m_nPosition);
+ }
+ break;
+ case MID_RUBY_POSITION:
+ {
+ rVal <<= m_nPosition;
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+bool SwFormatRuby::PutValue( const uno::Any& rVal,
+ sal_uInt8 nMemberId )
+{
+ bool bRet = true;
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_RUBY_TEXT:
+ bRet = rVal >>= m_sRubyText;
+ break;
+ case MID_RUBY_ADJUST:
+ {
+ sal_Int16 nSet = 0;
+ rVal >>= nSet;
+ if(nSet >= sal_Int16(text::RubyAdjust_LEFT) && nSet <= sal_Int16(text::RubyAdjust_INDENT_BLOCK))
+ m_eAdjustment = static_cast<text::RubyAdjust>(nSet);
+ else
+ bRet = false;
+ }
+ break;
+ case MID_RUBY_ABOVE:
+ {
+ const uno::Type& rType = cppu::UnoType<bool>::get();
+ if(rVal.hasValue() && rVal.getValueType() == rType)
+ {
+ bool bAbove = *o3tl::doAccess<bool>(rVal);
+ m_nPosition = bAbove ? 0 : 1;
+ }
+ }
+ break;
+ case MID_RUBY_POSITION:
+ {
+ sal_Int16 nSet = 0;
+ rVal >>= nSet;
+ if(nSet >= sal_Int16(text::RubyPosition::ABOVE) && nSet <= sal_Int16(text::RubyPosition::INTER_CHARACTER))
+ m_nPosition = nSet;
+ else
+ bRet = false;
+ }
+ break;
+ case MID_RUBY_CHARSTYLE:
+ {
+ OUString sTmp;
+ bRet = rVal >>= sTmp;
+ if(bRet)
+ m_sCharFormatName = SwStyleNameMapper::GetUIName(sTmp, SwGetPoolIdFromName::ChrFmt );
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwFormatMeta * SwFormatMeta::CreatePoolDefault(const sal_uInt16 i_nWhich)
+{
+ return new SwFormatMeta(i_nWhich);
+}
+
+SwFormatMeta::SwFormatMeta(const sal_uInt16 i_nWhich)
+ : SfxPoolItem( i_nWhich )
+ , m_pMeta()
+ , m_pTextAttr( nullptr )
+{
+ OSL_ENSURE((RES_TXTATR_META == i_nWhich) || (RES_TXTATR_METAFIELD == i_nWhich),
+ "ERROR: SwFormatMeta: invalid which id!");
+}
+
+SwFormatMeta::SwFormatMeta( std::shared_ptr< ::sw::Meta > const & i_pMeta,
+ const sal_uInt16 i_nWhich )
+ : SfxPoolItem( i_nWhich )
+ , m_pMeta( i_pMeta )
+ , m_pTextAttr( nullptr )
+{
+ OSL_ENSURE((RES_TXTATR_META == i_nWhich) || (RES_TXTATR_METAFIELD == i_nWhich),
+ "ERROR: SwFormatMeta: invalid which id!");
+ OSL_ENSURE(m_pMeta, "SwFormatMeta: no Meta ?");
+ // DO NOT call m_pMeta->SetFormatMeta(this) here; only from SetTextAttr!
+}
+
+SwFormatMeta::~SwFormatMeta()
+{
+ if (m_pMeta && (m_pMeta->GetFormatMeta() == this))
+ {
+ NotifyChangeTextNode(nullptr);
+ m_pMeta->SetFormatMeta(nullptr);
+ }
+}
+
+bool SwFormatMeta::operator==( const SfxPoolItem & i_rOther ) const
+{
+ return SfxPoolItem::operator==( i_rOther )
+ && m_pMeta == static_cast<SwFormatMeta const &>( i_rOther ).m_pMeta;
+}
+
+SwFormatMeta* SwFormatMeta::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ // if this is indeed a copy, then DoCopy must be called later!
+ return m_pMeta // #i105148# pool default may be cloned also!
+ ? new SwFormatMeta( m_pMeta, Which() ) : new SwFormatMeta( Which() );
+}
+
+void SwFormatMeta::SetTextAttr(SwTextMeta * const i_pTextAttr)
+{
+ OSL_ENSURE(!(m_pTextAttr && i_pTextAttr),
+ "SwFormatMeta::SetTextAttr: already has text attribute?");
+ OSL_ENSURE( m_pTextAttr || i_pTextAttr ,
+ "SwFormatMeta::SetTextAttr: no attribute to remove?");
+ m_pTextAttr = i_pTextAttr;
+ OSL_ENSURE(m_pMeta, "inserted SwFormatMeta has no sw::Meta?");
+ // the sw::Meta must be able to find the current text attribute!
+ if (m_pMeta)
+ {
+ if (i_pTextAttr)
+ {
+ m_pMeta->SetFormatMeta(this);
+ }
+ else if (m_pMeta->GetFormatMeta() == this)
+ { // text attribute gone => de-register from text node!
+ NotifyChangeTextNode(nullptr);
+ m_pMeta->SetFormatMeta(nullptr);
+ }
+ }
+}
+
+void SwFormatMeta::NotifyChangeTextNode(SwTextNode *const pTextNode)
+{
+ // N.B.: do not reset m_pTextAttr here: see call in nodes.cxx,
+ // where the hint is not deleted!
+ OSL_ENSURE(m_pMeta, "SwFormatMeta::NotifyChangeTextNode: no Meta?");
+ if (m_pMeta && (m_pMeta->GetFormatMeta() == this))
+ { // do not call Modify, that would call SwXMeta::SwClientNotify
+ m_pMeta->NotifyChangeTextNode(pTextNode);
+ }
+}
+
+// this SwFormatMeta has been cloned and points at the same sw::Meta as the source
+// this method copies the sw::Meta
+void SwFormatMeta::DoCopy(::sw::MetaFieldManager & i_rTargetDocManager,
+ SwTextNode & i_rTargetTextNode)
+{
+ OSL_ENSURE(m_pMeta, "DoCopy called for SwFormatMeta with no sw::Meta?");
+ if (!m_pMeta)
+ return;
+
+ const std::shared_ptr< ::sw::Meta> pOriginal( m_pMeta );
+ if (RES_TXTATR_META == Which())
+ {
+ m_pMeta = std::make_shared<::sw::Meta>(this);
+ }
+ else
+ {
+ ::sw::MetaField *const pMetaField(
+ static_cast< ::sw::MetaField* >(pOriginal.get()));
+ m_pMeta = i_rTargetDocManager.makeMetaField( this,
+ pMetaField->m_nNumberFormat, pMetaField->IsFixedLanguage() );
+ }
+ // Meta must have a text node before calling RegisterAsCopyOf
+ m_pMeta->NotifyChangeTextNode(& i_rTargetTextNode);
+ // this cannot be done in Clone: a Clone is not necessarily a copy!
+ m_pMeta->RegisterAsCopyOf(*pOriginal);
+}
+
+namespace sw {
+
+Meta::Meta(SwFormatMeta * const i_pFormat)
+ : ::sfx2::Metadatable()
+ , sw::BroadcastingModify()
+ , m_pFormat(i_pFormat)
+ , m_pTextNode(nullptr)
+{
+}
+
+Meta::~Meta()
+{
+}
+
+SwTextMeta * Meta::GetTextAttr() const
+{
+ return m_pFormat ? m_pFormat->GetTextAttr() : nullptr;
+}
+
+
+void Meta::NotifyChangeTextNode(SwTextNode *const pTextNode)
+{
+ m_pTextNode = pTextNode;
+ if (m_pTextNode && (GetRegisteredIn() != m_pTextNode))
+ {
+ m_pTextNode->Add(this);
+ }
+ else if (!m_pTextNode)
+ {
+ EndListeningAll();
+ }
+ if (!pTextNode) // text node gone? invalidate UNO object!
+ {
+ GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
+ }
+}
+
+void Meta::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ GetNotifier().Broadcast(SfxHint(SfxHintId::DataChanged));
+ if(RES_REMOVE_UNO_OBJECT == pLegacy->GetWhich())
+ { // invalidate cached uno object
+ SetXMeta(uno::Reference<rdf::XMetadatable>(nullptr));
+ GetNotifier().Broadcast(SfxHint(SfxHintId::Deinitializing));
+ }
+}
+
+// sfx2::Metadatable
+::sfx2::IXmlIdRegistry& Meta::GetRegistry()
+{
+ SwTextNode * const pTextNode( GetTextNode() );
+ // GetRegistry may only be called on a meta that is actually in the
+ // document, which means it has a pointer to its text node
+ OSL_ENSURE(pTextNode, "ERROR: GetRegistry: no text node?");
+ if (!pTextNode)
+ throw uno::RuntimeException();
+ return pTextNode->GetRegistry();
+}
+
+bool Meta::IsInClipboard() const
+{
+ const SwTextNode * const pTextNode( GetTextNode() );
+// no text node: in UNDO OSL_ENSURE(pTextNode, "IsInClipboard: no text node?");
+ return pTextNode && pTextNode->IsInClipboard();
+}
+
+bool Meta::IsInUndo() const
+{
+ const SwTextNode * const pTextNode( GetTextNode() );
+// no text node: in UNDO OSL_ENSURE(pTextNode, "IsInUndo: no text node?");
+ return pTextNode == nullptr || pTextNode->IsInUndo();
+}
+
+bool Meta::IsInContent() const
+{
+ const SwTextNode * const pTextNode( GetTextNode() );
+ OSL_ENSURE(pTextNode, "IsInContent: no text node?");
+ return pTextNode == nullptr || pTextNode->IsInContent();
+}
+
+css::uno::Reference< css::rdf::XMetadatable > Meta::MakeUnoObject()
+{
+ return SwXMeta::CreateXMeta(*this);
+}
+
+MetaField::MetaField(SwFormatMeta * const i_pFormat,
+ const sal_uInt32 nNumberFormat, const bool bIsFixedLanguage)
+ : Meta(i_pFormat)
+ , m_nNumberFormat( nNumberFormat )
+ , m_bIsFixedLanguage( bIsFixedLanguage )
+{
+}
+
+void MetaField::GetPrefixAndSuffix(
+ OUString *const o_pPrefix, OUString *const o_pSuffix, OUString *const o_pShadowsColor)
+{
+ try
+ {
+ const uno::Reference<rdf::XMetadatable> xMetaField( MakeUnoObject() );
+ assert(dynamic_cast<SwXMetaField*>(xMetaField.get()) && "GetPrefixAndSuffix: no SwXMetaField?");
+ if (xMetaField.is())
+ {
+ SwTextNode * const pTextNode( GetTextNode() );
+ SwDocShell const * const pShell(pTextNode->GetDoc().GetDocShell());
+ const uno::Reference<frame::XModel> xModel(
+ pShell ? pShell->GetModel() : nullptr, uno::UNO_SET_THROW);
+ getPrefixAndSuffix(xModel, xMetaField, o_pPrefix, o_pSuffix, o_pShadowsColor);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "");
+ }
+}
+
+sal_uInt32 MetaField::GetNumberFormat(OUString const & rContent) const
+{
+ //TODO: this probably lacks treatment for some special cases
+ sal_uInt32 nNumberFormat( m_nNumberFormat );
+ SwTextNode * const pTextNode( GetTextNode() );
+ if (pTextNode)
+ {
+ double number;
+ (void) pTextNode->GetDoc().IsNumberFormat( rContent, nNumberFormat, number );
+ }
+ return nNumberFormat;
+}
+
+void MetaField::SetNumberFormat(sal_uInt32 nNumberFormat)
+{
+ // effectively, the member is only a default:
+ // GetNumberFormat checks if the text actually conforms
+ m_nNumberFormat = nNumberFormat;
+}
+
+MetaFieldManager::MetaFieldManager()
+{
+}
+
+std::shared_ptr<MetaField>
+MetaFieldManager::makeMetaField(SwFormatMeta * const i_pFormat,
+ const sal_uInt32 nNumberFormat, const bool bIsFixedLanguage)
+{
+ const std::shared_ptr<MetaField> pMetaField(
+ new MetaField(i_pFormat, nNumberFormat, bIsFixedLanguage) );
+ m_MetaFields.push_back(pMetaField);
+ return pMetaField;
+}
+
+namespace {
+
+struct IsInUndo
+{
+ bool operator()(std::weak_ptr<MetaField> const & pMetaField) {
+ return pMetaField.lock()->IsInUndo();
+ }
+};
+
+struct MakeUnoObject
+{
+ uno::Reference<text::XTextField>
+ operator()(std::weak_ptr<MetaField> const & pMetaField) {
+ return uno::Reference<text::XTextField>(
+ pMetaField.lock()->MakeUnoObject(), uno::UNO_QUERY);
+ }
+};
+
+}
+
+std::vector< uno::Reference<text::XTextField> >
+MetaFieldManager::getMetaFields()
+{
+ // erase deleted fields
+ const MetaFieldList_t::iterator iter(
+ std::remove_if(m_MetaFields.begin(), m_MetaFields.end(),
+ [] (std::weak_ptr<MetaField> const& rField) { return rField.expired(); }));
+ m_MetaFields.erase(iter, m_MetaFields.end());
+ // filter out fields in UNDO
+ MetaFieldList_t filtered(m_MetaFields.size());
+ const MetaFieldList_t::iterator iter2(
+ std::remove_copy_if(m_MetaFields.begin(), m_MetaFields.end(),
+ filtered.begin(), IsInUndo()));
+ filtered.erase(iter2, filtered.end());
+ // create uno objects
+ std::vector< uno::Reference<text::XTextField> > ret(filtered.size());
+ std::transform(filtered.begin(), filtered.end(), ret.begin(),
+ MakeUnoObject());
+ return ret;
+}
+
+void MetaFieldManager::copyDocumentProperties(const SwDoc& rSource)
+{
+ const SwDocShell* pDocShell = rSource.GetDocShell();
+ if (!pDocShell)
+ return;
+
+ uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(pDocShell->GetModel(), uno::UNO_QUERY);
+ uno::Reference<util::XCloneable> xCloneable(xDocumentPropertiesSupplier->getDocumentProperties(), uno::UNO_QUERY);
+ m_xDocumentProperties.set(xCloneable->createClone(), uno::UNO_QUERY);
+}
+
+const uno::Reference<document::XDocumentProperties>& MetaFieldManager::getDocumentProperties() const
+{
+ return m_xDocumentProperties;
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/fntcache.cxx b/sw/source/core/txtnode/fntcache.cxx
new file mode 100644
index 000000000..f787c55ec
--- /dev/null
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -0,0 +1,2226 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <i18nlangtag/mslangid.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/glyphitemcache.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <breakit.hxx>
+#include <paintfrm.hxx>
+#include <viewsh.hxx>
+#include <viewopt.hxx>
+#include <fntcache.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <swfont.hxx>
+#include <wrong.hxx>
+#include <txtfrm.hxx>
+#include <pagefrm.hxx>
+#include <tgrditem.hxx>
+#include <scriptinfo.hxx>
+#include <editeng/brushitem.hxx>
+#include <accessibilityoptions.hxx>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <doc.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <vcl/glyphitem.hxx>
+#include <vcl/vcllayout.hxx>
+#include <docsh.hxx>
+#include <strings.hrc>
+#include <fntcap.hxx>
+#include <vcl/outdev/ScopedStates.hxx>
+#include <o3tl/hash_combine.hxx>
+#include <cstdint>
+#include <memory>
+#include "justify.hxx"
+
+using namespace ::com::sun::star;
+
+// global variables declared in fntcache.hxx
+// FontCache is created in txtinit.cxx TextInit_ and deleted in TextFinit
+SwFntCache *pFntCache = nullptr;
+// last Font set by ChgFntCache
+SwFntObj *pLastFont = nullptr;
+
+constexpr Color gWaveCol(COL_GRAY);
+
+tools::Long SwFntObj::s_nPixWidth;
+MapMode* SwFntObj::s_pPixMap = nullptr;
+static vcl::DeleteOnDeinit< VclPtr<OutputDevice> > s_pFntObjPixOut {};
+
+void SwFntCache::Flush( )
+{
+ if ( pLastFont )
+ {
+ pLastFont->Unlock();
+ pLastFont = nullptr;
+ }
+ SwCache::Flush( );
+}
+
+SwFntObj::SwFntObj(const SwSubFont &rFont, std::uintptr_t nFontCacheId, SwViewShell const *pSh)
+ : SwCacheObj(reinterpret_cast<void *>(nFontCacheId))
+ , m_aFont(rFont)
+ , m_pScrFont(nullptr)
+ , m_pPrtFont(&m_aFont)
+ , m_pPrinter(nullptr)
+ , m_nGuessedLeading(USHRT_MAX)
+ , m_nExtLeading(USHRT_MAX)
+ , m_nScrAscent(0)
+ , m_nPrtAscent(USHRT_MAX)
+ , m_nScrHeight(0)
+ , m_nPrtHeight(USHRT_MAX)
+ , m_nPropWidth(rFont.GetPropWidth())
+ , m_nScrHangingBaseline(0)
+ , m_nPrtHangingBaseline(0)
+{
+ m_nZoom = pSh ? pSh->GetViewOptions()->GetZoom() : USHRT_MAX;
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == m_aFont.GetCharSet();
+ m_bPaintBlank = ( LINESTYLE_NONE != m_aFont.GetUnderline()
+ || LINESTYLE_NONE != m_aFont.GetOverline()
+ || STRIKEOUT_NONE != m_aFont.GetStrikeout() )
+ && !m_aFont.IsWordLineMode();
+ m_aFont.SetLanguage(rFont.GetLanguage());
+}
+
+SwFntObj::~SwFntObj()
+{
+ if ( m_pScrFont != m_pPrtFont )
+ delete m_pScrFont;
+ if ( m_pPrtFont != &m_aFont )
+ delete m_pPrtFont;
+}
+
+void SwFntObj::CreatePrtFont( const OutputDevice& rPrt )
+{
+ if ( m_nPropWidth == 100 || m_pPrinter == &rPrt )
+ return;
+
+ if( m_pScrFont != m_pPrtFont )
+ delete m_pScrFont;
+ if( m_pPrtFont != &m_aFont )
+ delete m_pPrtFont;
+
+ const vcl::Font aOldFnt( rPrt.GetFont() );
+ const_cast<OutputDevice&>(rPrt).SetFont( m_aFont );
+ const FontMetric aWinMet( rPrt.GetFontMetric() );
+ const_cast<OutputDevice&>(rPrt).SetFont( aOldFnt );
+ auto nWidth = ( aWinMet.GetFontSize().Width() * m_nPropWidth ) / 100;
+
+ if( !nWidth )
+ ++nWidth;
+ m_pPrtFont = new vcl::Font( m_aFont );
+ m_pPrtFont->SetFontSize( Size( nWidth, m_aFont.GetFontSize().Height() ) );
+ m_pScrFont = nullptr;
+
+}
+
+/*
+ * returns whether we have to adjust the output font to resemble
+ * the formatting font
+ *
+ * _Not_ necessary if
+ *
+ * 1. RefDef == OutDev (text formatting, online layout...)
+ * 2. PDF export from online layout
+ * 3. Prospect/PagePreview printing
+ */
+static bool lcl_IsFontAdjustNecessary( const vcl::RenderContext& rOutDev,
+ const vcl::RenderContext& rRefDev )
+{
+ return &rRefDev != &rOutDev &&
+ OUTDEV_WINDOW != rRefDev.GetOutDevType() &&
+ ( OUTDEV_PRINTER != rRefDev.GetOutDevType() ||
+ OUTDEV_PRINTER != rOutDev.GetOutDevType() );
+}
+
+namespace {
+
+struct CalcLinePosData
+{
+ SwDrawTextInfo& rInf;
+ vcl::Font& rFont;
+ TextFrameIndex nCnt;
+ const bool bSwitchH2V;
+ const bool bSwitchH2VLRBT;
+ const bool bSwitchL2R;
+ tools::Long nHalfSpace;
+ sal_Int32* pKernArray;
+ const bool bBidiPor;
+
+ CalcLinePosData( SwDrawTextInfo& _rInf, vcl::Font& _rFont,
+ TextFrameIndex const _nCnt, const bool _bSwitchH2V, const bool _bSwitchH2VLRBT, const bool _bSwitchL2R,
+ tools::Long _nHalfSpace, sal_Int32* _pKernArray, const bool _bBidiPor) :
+ rInf( _rInf ),
+ rFont( _rFont ),
+ nCnt( _nCnt ),
+ bSwitchH2V( _bSwitchH2V ),
+ bSwitchH2VLRBT( _bSwitchH2VLRBT ),
+ bSwitchL2R( _bSwitchL2R ),
+ nHalfSpace( _nHalfSpace ),
+ pKernArray( _pKernArray ),
+ bBidiPor( _bBidiPor )
+ {
+ }
+};
+
+}
+
+// Computes the start and end position of an underline. This function is called
+// from the DrawText-method (for underlining misspelled words or smarttag terms).
+static void lcl_calcLinePos( const CalcLinePosData &rData,
+ Point &rStart, Point &rEnd, TextFrameIndex const nStart, TextFrameIndex const nWrLen)
+{
+ tools::Long nBlank = 0;
+ const TextFrameIndex nEnd = nStart + nWrLen;
+ const tools::Long nTmpSpaceAdd = rData.rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+
+ if ( nEnd < rData.nCnt
+ && CH_BLANK == rData.rInf.GetText()[sal_Int32(rData.rInf.GetIdx() + nEnd)] )
+ {
+ if (nEnd + TextFrameIndex(1) == rData.nCnt)
+ nBlank -= nTmpSpaceAdd;
+ else
+ nBlank -= rData.nHalfSpace;
+ }
+
+ // determine start, end and length of wave line
+ sal_Int32 nKernStart = nStart ? rData.pKernArray[sal_Int32(nStart) - 1] : 0;
+ sal_Int32 nKernEnd = rData.pKernArray[sal_Int32(nEnd) - 1];
+
+ const Degree10 nDir = rData.bBidiPor ? 1800_deg10
+ : UnMapDirection(rData.rFont.GetOrientation(),
+ rData.bSwitchH2V, rData.bSwitchH2VLRBT);
+
+ switch ( nDir.get() )
+ {
+ case 0 :
+ rStart.AdjustX(nKernStart );
+ rEnd.setX( nBlank + rData.rInf.GetPos().X() + nKernEnd );
+ rEnd.setY( rData.rInf.GetPos().Y() );
+ break;
+ case 900 :
+ rStart.AdjustY( -nKernStart );
+ rEnd.setX( rData.rInf.GetPos().X() );
+ rEnd.setY( nBlank + rData.rInf.GetPos().Y() - nKernEnd );
+ break;
+ case 1800 :
+ rStart.AdjustX( -nKernStart );
+ rEnd.setX( rData.rInf.GetPos().X() - nKernEnd - nBlank );
+ rEnd.setY( rData.rInf.GetPos().Y() );
+ break;
+ case 2700 :
+ rStart.AdjustY(nKernStart );
+ rEnd.setX( rData.rInf.GetPos().X() );
+ rEnd.setY( nBlank + rData.rInf.GetPos().Y() + nKernEnd );
+ break;
+ }
+
+ if ( rData.bSwitchL2R )
+ {
+ rData.rInf.GetFrame()->SwitchLTRtoRTL( rStart );
+ rData.rInf.GetFrame()->SwitchLTRtoRTL( rEnd );
+ }
+
+ if ( rData.bSwitchH2V )
+ {
+ rData.rInf.GetFrame()->SwitchHorizontalToVertical( rStart );
+ rData.rInf.GetFrame()->SwitchHorizontalToVertical( rEnd );
+ }
+}
+
+// Returns the Ascent of the Font on the given output device;
+// it may be necessary to create the screen font first.
+sal_uInt16 SwFntObj::GetFontAscent( const SwViewShell *pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ {
+ CreateScrFont( *pSh, rOut );
+ OSL_ENSURE( USHRT_MAX != m_nScrAscent, "nScrAscent is going berzerk" );
+ nRet = m_nScrAscent;
+ }
+ else
+ {
+ if (m_nPrtAscent == USHRT_MAX) // printer ascent unknown?
+ {
+ CreatePrtFont( rOut );
+ const vcl::Font aOldFnt( rRefDev.GetFont() );
+ const_cast<OutputDevice&>(rRefDev).SetFont( *m_pPrtFont );
+ const FontMetric aOutMet( rRefDev.GetFontMetric() );
+ m_nPrtAscent = o3tl::narrowing<sal_uInt16>(aOutMet.GetAscent());
+ m_nPrtHangingBaseline = o3tl::narrowing<sal_uInt16>(aOutMet.GetHangingBaseline());
+ const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
+ }
+
+ nRet = m_nPrtAscent;
+ }
+
+#if !defined(MACOSX) // #i89844# extleading is below the line for Mac
+ // TODO: move extleading below the line for all platforms too
+ nRet += GetFontLeading( pSh, rRefDev );
+#endif
+
+ OSL_ENSURE( USHRT_MAX != nRet, "GetFontAscent returned USHRT_MAX" );
+ return nRet;
+}
+
+// Returns the height of the Font on the given output device;
+// it may be necessary to create the screen font first.
+sal_uInt16 SwFntObj::GetFontHeight( const SwViewShell* pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ {
+ CreateScrFont( *pSh, rOut );
+ OSL_ENSURE( USHRT_MAX != m_nScrHeight, "nScrHeight is going berzerk" );
+ nRet = m_nScrHeight + GetFontLeading( pSh, rRefDev );
+ }
+ else
+ {
+ if (m_nPrtHeight == USHRT_MAX) // printer height unknown?
+ {
+ CreatePrtFont( rOut );
+ const vcl::Font aOldFnt( rRefDev.GetFont() );
+ const_cast<OutputDevice&>(rRefDev).SetFont( *m_pPrtFont );
+ m_nPrtHeight = o3tl::narrowing<sal_uInt16>(rRefDev.GetTextHeight());
+
+#if OSL_DEBUG_LEVEL > 0
+ // Check if vcl did not change the meaning of GetTextHeight
+ const FontMetric aOutMet( rRefDev.GetFontMetric() );
+ tools::Long nTmpPrtHeight = o3tl::narrowing<sal_uInt16>(aOutMet.GetAscent()) + aOutMet.GetDescent();
+ // #i106098#: do not compare with == here due to rounding error
+ OSL_ENSURE( std::abs(nTmpPrtHeight - m_nPrtHeight) < 3,
+ "GetTextHeight != Ascent + Descent" );
+#endif
+
+ const_cast<OutputDevice&>(rRefDev).SetFont( aOldFnt );
+ }
+
+ nRet = m_nPrtHeight + GetFontLeading( pSh, rRefDev );
+ }
+
+ OSL_ENSURE( USHRT_MAX != nRet, "GetFontHeight returned USHRT_MAX" );
+ return nRet;
+}
+
+sal_uInt16 SwFntObj::GetFontLeading( const SwViewShell *pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+
+ if ( pSh )
+ {
+ if ( USHRT_MAX == m_nGuessedLeading || USHRT_MAX == m_nExtLeading )
+ {
+ SolarMutexGuard aGuard;
+
+ const vcl::Font aOldFnt( rOut.GetFont() );
+ const_cast<OutputDevice&>(rOut).SetFont( *m_pPrtFont );
+ const FontMetric aMet( rOut.GetFontMetric() );
+ const_cast<OutputDevice&>(rOut).SetFont( aOldFnt );
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
+ GuessLeading( *pSh, aMet );
+ m_nExtLeading = o3tl::narrowing<sal_uInt16>(aMet.GetExternalLeading());
+ /* HACK: FIXME There is something wrong with Writer's bullet rendering, causing lines
+ with bullets to be higher than they should be. I think this is because
+ Writer uses font's external leading incorrect, as the vertical distance
+ added to every line instead of only a distance between multiple lines,
+ which means a single bullet has external leading added even though it
+ shouldn't, but frankly this is just an educated guess rather than understanding
+ Writer's layout (heh).
+ Symbol font in some documents is 'StarSymbol; Arial Unicode MS', and Windows
+ machines often do not have StarSymbol, falling back to Arial Unicode MS, which
+ has unusually high external leading. So just reset external leading for fonts
+ which are used to bullets, as those should not be used on multiple lines anyway,
+ so in correct rendering external leading should be irrelevant anyway.
+ Interestingly enough, bSymbol is false for 'StarSymbol; Arial Unicode MS', so
+ also check explicitly.
+ */
+ if( m_bSymbol || IsStarSymbol( m_pPrtFont->GetFamilyName()))
+ m_nExtLeading = 0;
+ }
+
+ const IDocumentSettingAccess& rIDSA = pSh->getIDocumentSettingAccess();
+ const bool bBrowse = ( pSh->GetWin() &&
+ pSh->GetViewOptions()->getBrowseMode() &&
+ !pSh->GetViewOptions()->IsPrtFormat() );
+
+ if ( !bBrowse && rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) )
+ nRet = m_nExtLeading;
+ else
+ nRet = m_nGuessedLeading;
+ }
+
+ OSL_ENSURE( USHRT_MAX != nRet, "GetFontLeading returned USHRT_MAX" );
+ return nRet;
+}
+
+sal_uInt16 SwFntObj::GetFontHangingBaseline( const SwViewShell* pSh, const OutputDevice& rOut )
+{
+ sal_uInt16 nRet = 0;
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ GetFontAscent(pSh, rOut);
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ nRet = m_nScrHangingBaseline;
+ else
+ nRet = m_nPrtHangingBaseline;
+
+ return nRet;
+}
+
+// pOut is the output device, not the reference device
+void SwFntObj::CreateScrFont( const SwViewShell& rSh, const OutputDevice& rOut )
+{
+ if ( m_pScrFont )
+ return;
+
+ // any changes to the output device are reset at the end of the function
+ OutputDevice* pOut = const_cast<OutputDevice*>(&rOut);
+
+ // Save old font
+ vcl::Font aOldOutFont( pOut->GetFont() );
+
+ m_nScrHeight = USHRT_MAX;
+
+ // Condition for output font / refdev font adjustment
+ OutputDevice* pPrt = &rSh.GetRefDev();
+
+ if( !rSh.GetWin() ||
+ !rSh.GetViewOptions()->getBrowseMode() ||
+ rSh.GetViewOptions()->IsPrtFormat() )
+ {
+ // After CreatePrtFont m_pPrtFont is the font which is actually used
+ // by the reference device
+ CreatePrtFont( *pPrt );
+ m_pPrinter = pPrt;
+
+ // save old reference device font
+ vcl::Font aOldPrtFnt( pPrt->GetFont() );
+
+ // set the font used at the reference device at the reference device
+ // and the output device
+ pPrt->SetFont( *m_pPrtFont );
+ pOut->SetFont( *m_pPrtFont );
+
+ // This should be the default for pScrFont.
+ m_pScrFont = m_pPrtFont;
+
+ FontMetric aMet = pPrt->GetFontMetric( );
+ // Don't lose "faked" properties of the logical font that don't truly
+ // exist in the physical font metrics which vcl which fake up for us
+ aMet.SetWeight(m_pScrFont->GetWeight());
+ aMet.SetItalic(m_pScrFont->GetItalic());
+
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == aMet.GetCharSet();
+
+ if ( USHRT_MAX == m_nGuessedLeading )
+ GuessLeading( rSh, aMet );
+
+ if ( USHRT_MAX == m_nExtLeading )
+ m_nExtLeading = o3tl::narrowing<sal_uInt16>(aMet.GetExternalLeading());
+
+ // reset the original reference device font
+ pPrt->SetFont( aOldPrtFnt );
+ }
+ else
+ {
+ m_bSymbol = RTL_TEXTENCODING_SYMBOL == m_aFont.GetCharSet();
+ if ( m_nGuessedLeading == USHRT_MAX )
+ m_nGuessedLeading = 0;
+
+ // no external leading in browse mode
+ if ( m_nExtLeading == USHRT_MAX )
+ m_nExtLeading = 0;
+
+ m_pScrFont = m_pPrtFont;
+ }
+
+ m_nScrAscent = o3tl::narrowing<sal_uInt16>(pOut->GetFontMetric().GetAscent());
+ m_nScrHangingBaseline = o3tl::narrowing<sal_uInt16>(pOut->GetFontMetric().GetHangingBaseline());
+ if ( USHRT_MAX == m_nScrHeight )
+ m_nScrHeight = o3tl::narrowing<sal_uInt16>(pOut->GetTextHeight());
+
+ // reset original output device font
+ pOut->SetFont( aOldOutFont );
+}
+
+void SwFntObj::GuessLeading( const SwViewShell&
+#if defined(_WIN32)
+ rSh
+#endif
+ , const FontMetric& rMet )
+{
+ // If leading >= 5, this seems to be enough leading.
+ // Nothing has to be done.
+ if ( rMet.GetInternalLeading() >= 5 )
+ {
+ m_nGuessedLeading = 0;
+ return;
+ }
+
+#if defined(_WIN32)
+ OutputDevice *pWin = rSh.GetWin() ?
+ rSh.GetWin()->GetOutDev() :
+ Application::GetDefaultDevice();
+ if ( pWin )
+ {
+ MapMode aTmpMap( MapUnit::MapTwip );
+ MapMode aOldMap = pWin->GetMapMode( );
+ pWin->SetMapMode( aTmpMap );
+ const vcl::Font aOldFnt( pWin->GetFont() );
+ pWin->SetFont( *m_pPrtFont );
+ const FontMetric aWinMet( pWin->GetFontMetric() );
+ const sal_uInt16 nWinHeight = sal_uInt16( aWinMet.GetFontSize().Height() );
+ if( m_pPrtFont->GetFamilyName().indexOf( aWinMet.GetFamilyName() ) != -1 )
+ {
+ // If the Leading on the Window is also 0, then it has to stay
+ // that way (see also StarMath).
+ tools::Long nTmpLeading = aWinMet.GetInternalLeading();
+ if( nTmpLeading <= 0 )
+ {
+ pWin->SetFont( rMet );
+ nTmpLeading = pWin->GetFontMetric().GetInternalLeading();
+ if( nTmpLeading < 0 )
+ m_nGuessedLeading = 0;
+ else
+ m_nGuessedLeading = sal_uInt16(nTmpLeading);
+ }
+ else
+ {
+ m_nGuessedLeading = sal_uInt16(nTmpLeading);
+ // Manta-Hack #50153#:
+ // Wer beim Leading luegt, luegt moeglicherweise auch beim
+ // Ascent/Descent, deshalb wird hier ggf. der Font ein wenig
+ // tiefergelegt, ohne dabei seine Hoehe zu aendern.
+ // (above original comment preserved for cultural reasons)
+ // Those who lie about their Leading, may lie about their
+ // Ascent/Descent as well, hence the Font will be lowered a
+ // little without changing its height.
+ tools::Long nDiff = std::min( rMet.GetDescent() - aWinMet.GetDescent(),
+ aWinMet.GetAscent() - rMet.GetAscent() - nTmpLeading );
+ if( nDiff > 0 )
+ {
+ OSL_ENSURE( m_nPrtAscent < USHRT_MAX, "GuessLeading: PrtAscent-Fault" );
+ if ( m_nPrtAscent < USHRT_MAX )
+ m_nPrtAscent = m_nPrtAscent + o3tl::narrowing<sal_uInt16>(( 2 * nDiff ) / 5);
+ }
+ }
+ }
+ else
+ {
+ // If all else fails, take 15% of the height, as empirically
+ // determined by CL
+ m_nGuessedLeading = (nWinHeight * 15) / 100;
+ }
+ pWin->SetFont( aOldFnt );
+ pWin->SetMapMode( aOldMap );
+ }
+ else
+ m_nGuessedLeading = 0;
+#else
+ m_nGuessedLeading = 0;
+#endif
+}
+
+// Set the font at the given output device; for screens it may be
+// necessary to do some adjustment first.
+void SwFntObj::SetDevFont( const SwViewShell *pSh, OutputDevice& rOut )
+{
+ const OutputDevice& rRefDev = pSh ? pSh->GetRefDev() : rOut;
+
+ if ( pSh && lcl_IsFontAdjustNecessary( rOut, rRefDev ) )
+ {
+ CreateScrFont( *pSh, rOut );
+ if( !GetScrFont()->IsSameInstance( rOut.GetFont() ) )
+ rOut.SetFont( *m_pScrFont );
+ if( m_pPrinter && ( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) ) )
+ m_pPrinter->SetFont( *m_pPrtFont );
+ }
+ else
+ {
+ CreatePrtFont( rOut );
+ if( !m_pPrtFont->IsSameInstance( rOut.GetFont() ) )
+ rOut.SetFont( *m_pPrtFont );
+ }
+
+ // Here, we actually do not need the leading values, but by calling
+ // GetFontLeading() we assure that the values are calculated for later use.
+ GetFontLeading( pSh, rRefDev );
+}
+
+#define WRONG_SHOW_MIN 5
+
+/*
+ * Output text:
+ * on screen => DrawTextArray
+ * on printer, !Kerning => DrawText
+ * on printer + Kerning => DrawStretchText
+ */
+static bool lcl_IsMonoSpaceFont( const vcl::RenderContext& rOut )
+{
+ const tools::Long nWidth1 = rOut.GetTextWidth( OUString( u'\x3008' ) );
+ const tools::Long nWidth2 = rOut.GetTextWidth( OUString( u'\x307C' ) );
+ return nWidth1 == nWidth2;
+}
+
+static bool lcl_IsFullstopCentered( const vcl::RenderContext& rOut )
+{
+ const FontMetric aMetric( rOut.GetFontMetric() );
+ return aMetric.IsFullstopCentered() ;
+}
+
+/* This helper structure (SwForbidden) contains the already marked parts of the string
+ to avoid double lines (e.g grammar + spell check error) */
+
+typedef std::vector<std::pair<TextFrameIndex, TextFrameIndex>> SwForbidden;
+
+static void lcl_DrawLineForWrongListData(
+ SwForbidden &rForbidden,
+ const SwDrawTextInfo &rInf,
+ sw::WrongListIterator *pWList,
+ const CalcLinePosData &rCalcLinePosData,
+ const Size &rPrtFontSize )
+{
+ if (!pWList) return;
+
+ TextFrameIndex nStart = rInf.GetIdx();
+ TextFrameIndex nWrLen = rInf.GetLen();
+
+ // check if respective data is available in the current text range
+ if (!pWList->Check( nStart, nWrLen ))
+ {
+ return;
+ }
+
+ tools::Long nHght = rInf.GetOut().LogicToPixel( rPrtFontSize ).Height();
+
+ // Draw wavy lines for spell and grammar errors only if font is large enough.
+ // Lines for smart tags will always be drawn.
+ if (pWList != rInf.GetSmartTags() && WRONG_SHOW_MIN >= nHght)
+ {
+ return;
+ }
+
+ SwForbidden::iterator pIter = rForbidden.begin();
+ if (rInf.GetOut().GetConnectMetaFile())
+ rInf.GetOut().Push();
+
+ const Color aCol( rInf.GetOut().GetLineColor() );
+
+ // iterate over all ranges stored in the respective SwWrongList
+ do
+ {
+ nStart -= rInf.GetIdx();
+
+ const TextFrameIndex nEnd = nStart + nWrLen;
+ TextFrameIndex nNext = nStart;
+ while( nNext < nEnd )
+ {
+ while( pIter != rForbidden.end() && pIter->second <= nNext )
+ ++pIter;
+
+ const TextFrameIndex nNextStart = nNext;
+ TextFrameIndex nNextEnd = nEnd;
+
+ if( pIter == rForbidden.end() || nNextEnd <= pIter->first )
+ {
+ // No overlapping mark up found
+ rForbidden.insert(pIter, std::make_pair(nNextStart, nNextEnd));
+ pIter = rForbidden.begin();
+ nNext = nEnd;
+ }
+ else
+ {
+ nNext = pIter->second;
+ if( nNextStart < pIter->first )
+ {
+ nNextEnd = pIter->first;
+ pIter->first = nNextStart;
+ }
+ else
+ continue;
+ }
+ // determine line pos
+ Point aStart( rInf.GetPos() );
+ Point aEnd;
+ lcl_calcLinePos( rCalcLinePosData, aStart, aEnd, nNextStart, nNextEnd - nNextStart );
+
+ SwWrongArea const*const wrongArea = pWList->GetWrongElement(nNextStart + rInf.GetIdx());
+ if (wrongArea != nullptr)
+ {
+ const SwViewShell* pShell = rInf.GetShell();
+ sal_uInt16 nZoom = pShell ? round(pShell->GetViewOptions()->GetZoom()/100) : 1;
+ if (WRONGAREA_WAVE == wrongArea->mLineType)
+ {
+ vcl::ScopedAntialiasing a(rInf.GetOut(), true);
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+ rInf.GetOut().DrawWaveLine( aStart, aEnd, 1 + nZoom, 3 + nZoom );
+ }
+ else if (WRONGAREA_BOLDWAVE == wrongArea->mLineType)
+ {
+ vcl::ScopedAntialiasing a(rInf.GetOut(), true);
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+ rInf.GetOut().DrawWaveLine( aStart, aEnd, 2 + nZoom, 4 + nZoom );
+ }
+ else if (WRONGAREA_BOLD == wrongArea->mLineType)
+ {
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+
+ aStart.AdjustY(30 );
+ aEnd.AdjustY(30 );
+
+ LineInfo aLineInfo( LineStyle::Solid, 26 );
+
+ rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
+ }
+ else if (WRONGAREA_DASHED == wrongArea->mLineType)
+ {
+ rInf.GetOut().SetLineColor( wrongArea->mColor );
+
+ aStart.AdjustY(30 );
+ aEnd.AdjustY(30 );
+
+ LineInfo aLineInfo( LineStyle::Dash );
+ aLineInfo.SetDistance( 40 );
+ aLineInfo.SetDashLen( 1 );
+ aLineInfo.SetDashCount(1);
+
+ rInf.GetOut().DrawLine( aStart, aEnd, aLineInfo );
+ }
+ }
+ }
+
+ nStart = nEnd + rInf.GetIdx();
+ nWrLen = rInf.GetIdx() + rInf.GetLen() - nStart;
+ }
+ while (nWrLen && pWList->Check( nStart, nWrLen ));
+
+ rInf.GetOut().SetLineColor( aCol );
+
+ if (rInf.GetOut().GetConnectMetaFile())
+ rInf.GetOut().Pop();
+}
+
+static void GetTextArray(const OutputDevice& rDevice, const OUString& rStr, std::vector<sal_Int32>& rDXAry,
+ sal_Int32 nIndex, sal_Int32 nLen, const vcl::text::TextLayoutCache* layoutCache = nullptr)
+{
+ const SalLayoutGlyphs* pLayoutCache = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rDevice, rStr, nIndex, nLen,
+ 0, layoutCache);
+ rDevice.GetTextArray(rStr, &rDXAry, nIndex, nLen, layoutCache, pLayoutCache);
+}
+
+static void GetTextArray(const OutputDevice& rOutputDevice, const SwDrawTextInfo& rInf, std::vector<sal_Int32>& rDXAry)
+{
+ return GetTextArray(rOutputDevice, rInf.GetText(), rDXAry, rInf.GetIdx().get(), rInf.GetLen().get(), rInf.GetVclCache());
+}
+
+static void GetTextArray(const OutputDevice& rOutputDevice, const SwDrawTextInfo& rInf, std::vector<sal_Int32>& rDXAry, sal_Int32 nLen)
+{
+ // Substring is fine.
+ assert( nLen <= rInf.GetLen().get());
+ return GetTextArray(rOutputDevice, rInf.GetText(), rDXAry, rInf.GetIdx().get(), nLen, rInf.GetVclCache());
+}
+
+void SwFntObj::DrawText( SwDrawTextInfo &rInf )
+{
+ OSL_ENSURE( rInf.GetShell(), "SwFntObj::DrawText without shell" );
+
+ OutputDevice& rRefDev = rInf.GetShell()->GetRefDev();
+ vcl::Window* pWin = rInf.GetShell()->GetWin();
+
+ // true if pOut is the printer and the printer has been used for formatting
+ const bool bPrt = OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() &&
+ OUTDEV_PRINTER == rRefDev.GetOutDevType();
+ const bool bBrowse = ( pWin &&
+ rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
+ !rInf.GetShell()->GetViewOptions()->IsPrtFormat() &&
+ !rInf.GetBullet() &&
+ ( rInf.GetSpace() || !rInf.GetKern() ) &&
+ !rInf.GetWrong() &&
+ !rInf.GetGrammarCheck() &&
+ !rInf.GetSmartTags() &&
+ !rInf.GetGreyWave() );
+
+ // bDirectPrint indicates that we can enter the branch which calls
+ // the DrawText functions instead of calling the DrawTextArray functions
+ const bool bDirectPrint = bPrt || bBrowse;
+
+ // Condition for output font / refdev font adjustment
+ const bool bUseScrFont =
+ lcl_IsFontAdjustNecessary( rInf.GetOut(), rRefDev );
+
+ vcl::Font* pTmpFont = bUseScrFont ? m_pScrFont : m_pPrtFont;
+
+ // bDirectPrint and bUseScrFont should have these values:
+
+ // Outdev / RefDef | Printer | VirtPrinter | Window
+
+ // Printer | 1 - 0 | 0 - 1 | -
+
+ // VirtPrinter/PDF | 0 - 1 | 0 - 1 | -
+
+ // Window/VirtWindow| 0 - 1 | 0 - 1 | 1 - 0
+
+ // Exception: During painting of a Writer OLE object, we do not have
+ // a window. Therefore bUseSrcFont is always 0 in this case.
+
+#if OSL_DEBUG_LEVEL > 0
+
+ const bool bNoAdjust = bPrt ||
+ ( pWin &&
+ rInf.GetShell()->GetViewOptions()->getBrowseMode() &&
+ !rInf.GetShell()->GetViewOptions()->IsPrtFormat() );
+
+ if ( OUTDEV_PRINTER == rInf.GetOut().GetOutDevType() )
+ {
+ // Printer output
+ if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( bNoAdjust && !bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( rRefDev.IsVirtual() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+ }
+ else if ( rInf.GetOut().IsVirtual() && ! pWin )
+ {
+ // PDF export
+ if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( rRefDev.IsVirtual() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+ }
+ else if ( OUTDEV_WINDOW == rInf.GetOut().GetOutDevType() ||
+ ( rInf.GetOut().IsVirtual() && pWin ) )
+ {
+ // Window or virtual window
+ if ( OUTDEV_PRINTER == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( rRefDev.IsVirtual() )
+ {
+ OSL_ENSURE( !bNoAdjust && bUseScrFont, "Outdev Check failed" );
+ }
+ else if ( OUTDEV_WINDOW == rRefDev.GetOutDevType() )
+ {
+ OSL_ENSURE( bNoAdjust && !bUseScrFont, "Outdev Check failed" );
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "Outdev Check failed" );
+ }
+
+#endif
+
+ // robust: better use the printer font instead of using no font at all
+ OSL_ENSURE( pTmpFont, "No screen or printer font?" );
+ if ( ! pTmpFont )
+ pTmpFont = m_pPrtFont;
+
+ // HACK: LINESTYLE_WAVE must not be abused any more, hence the grey wave
+ // line of the ExtendedAttributeSets will appear in the font color first
+
+ const bool bSwitchH2V = rInf.GetFrame() && rInf.GetFrame()->IsVertical();
+ const bool bSwitchH2VLRBT = rInf.GetFrame() && rInf.GetFrame()->IsVertLRBT();
+ const bool bSwitchL2R = rInf.GetFrame() && rInf.GetFrame()->IsRightToLeft() &&
+ ! rInf.IsIgnoreFrameRTL();
+ const vcl::text::ComplexTextLayoutFlags nMode = rInf.GetOut().GetLayoutMode();
+ const bool bBidiPor = ( bSwitchL2R !=
+ ( vcl::text::ComplexTextLayoutFlags::Default != ( vcl::text::ComplexTextLayoutFlags::BiDiRtl & nMode ) ) );
+
+ // be sure to have the correct layout mode at the printer
+ if ( m_pPrinter )
+ {
+ m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
+ m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
+ }
+
+ Point aTextOriginPos( rInf.GetPos() );
+ if( !bPrt )
+ {
+ if( rInf.GetpOut() != *s_pFntObjPixOut.get() || rInf.GetOut().GetMapMode() != *s_pPixMap )
+ {
+ *s_pPixMap = rInf.GetOut().GetMapMode();
+ (*s_pFntObjPixOut.get()) = rInf.GetpOut();
+ Size aTmp( 1, 1 );
+ s_nPixWidth = rInf.GetOut().PixelToLogic( aTmp ).Width();
+ }
+
+ aTextOriginPos.AdjustX(rInf.GetFrame()->IsRightToLeft() ? 0 : s_nPixWidth );
+ }
+
+ Color aOldColor( pTmpFont->GetColor() );
+ bool bChgColor = rInf.ApplyAutoColor( pTmpFont );
+ if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *pTmpFont );
+ if ( bChgColor )
+ pTmpFont->SetColor( aOldColor );
+
+ if (TextFrameIndex(COMPLETE_STRING) == rInf.GetLen())
+ rInf.SetLen( TextFrameIndex(rInf.GetText().getLength()) );
+
+ // ASIAN LINE AND CHARACTER GRID MODE START
+
+ if ( rInf.GetFrame() && rInf.SnapToGrid() && rInf.GetFont() &&
+ SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+
+ // ASIAN LINE AND CHARACTER GRID MODE
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() )
+ {
+ //for textgrid refactor
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const tools::Long nGridWidth = GetGridWidth(*pGrid, *pDoc);
+ tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+
+ // kerning array - gives the absolute position of end of each character
+ std::vector<sal_Int32> aKernArray;
+
+ if ( m_pPrinter )
+ GetTextArray(*m_pPrinter, rInf, aKernArray);
+ else
+ GetTextArray(rInf.GetOut(), rInf, aKernArray);
+
+ tools::Long nDelta = 0;
+
+ if (pGrid->IsSnapToChars())
+ {
+ nDelta = Justify::SnapToGrid(aKernArray, rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()), nGridWidth, false);
+ }
+ else
+ {
+ Justify::SnapToGridEdge(aKernArray, sal_Int32(rInf.GetLen()), nGridWidth,
+ nSpaceAdd, rInf.GetKern());
+ }
+
+ if (nDelta)
+ aTextOriginPos.AdjustX(nDelta);
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ aKernArray, sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ return;
+ }
+ }
+
+ // DIRECT PAINTING WITHOUT SCREEN ADJUSTMENT
+
+ if ( bDirectPrint )
+ {
+ const Fraction aTmp( 1, 1 );
+ bool bStretch = rInf.GetWidth() && (rInf.GetLen() > TextFrameIndex(1)) && bPrt
+ && ( aTmp != rInf.GetOut().GetMapMode().GetScaleX() );
+
+ if ( bSwitchL2R )
+ rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ // In the good old days we used to have a simple DrawText if the
+ // output device is the printer. Now we need a DrawTextArray if
+ // 1. KanaCompression is enabled
+ // 2. Justified alignment
+ // Simple kerning is handled by DrawStretchText
+ if( rInf.GetSpace() || rInf.GetKanaComp() )
+ {
+ std::vector<sal_Int32> aKernArray;
+ GetTextArray(rInf.GetOut(), rInf, aKernArray);
+
+ if( bStretch )
+ {
+ sal_Int32 nZwi = sal_Int32(rInf.GetLen()) - 1;
+ tools::Long nDiff = rInf.GetWidth() - aKernArray[ nZwi ]
+ - sal_Int32(rInf.GetLen()) * rInf.GetKern();
+ tools::Long nRest = nDiff % nZwi;
+ tools::Long nAdd;
+ if( nRest < 0 )
+ {
+ nAdd = -1;
+ nRest += nZwi;
+ }
+ else
+ {
+ nAdd = +1;
+ nRest = nZwi - nRest;
+ }
+ nDiff /= nZwi;
+ tools::Long nSum = nDiff;
+ for( sal_Int32 i = 0; i < nZwi; )
+ {
+ aKernArray[ i ] += nSum;
+ if( ++i == nRest )
+ nDiff += nAdd;
+ nSum += nDiff;
+ }
+ }
+
+ // Modify Array for special justifications
+
+ tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ bool bSpecialJust = false;
+
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+
+ // Kana Compression
+ if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() ) )
+ {
+ pSI->Compress( aKernArray.data(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos );
+ bSpecialJust = true;
+ }
+
+ // Asian Justification
+ if ( SwFontScript::CJK == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(),
+ rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
+
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+
+ // Kashida Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
+ {
+ if ( pSI && pSI->CountKashida() &&
+ pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(),
+ rInf.GetLen(), nSpaceAdd ) != -1 )
+ {
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ // Thai Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CTL );
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ // Use rInf.GetSpace() because it has more precision than
+ // nSpaceAdd:
+ SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(),
+ rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetNumberOfBlanks(),
+ rInf.GetSpace() );
+
+ // adding space to blanks is already done
+ bSpecialJust = true;
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ tools::Long nKernSum = rInf.GetKern();
+
+ if ( bStretch || m_bPaintBlank || rInf.GetKern() || bSpecialJust )
+ {
+ for (sal_Int32 i = 0; i < sal_Int32(rInf.GetLen()); i++,
+ nKernSum += rInf.GetKern() )
+ {
+ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
+ nKernSum += nSpaceAdd;
+ aKernArray[i] += nKernSum;
+ }
+
+ // In case of underlined/strike-through justified text
+ // a blank at the end requires special handling:
+ if( m_bPaintBlank && rInf.GetLen() && ( CH_BLANK ==
+ rInf.GetText()[sal_Int32(rInf.GetIdx() + rInf.GetLen())-1]))
+ {
+ // If it is a single underlined space, output 2 spaces:
+ if (TextFrameIndex(1) == rInf.GetLen())
+ {
+ aKernArray[0] = rInf.GetWidth() + nSpaceAdd;
+
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ aKernArray, sal_Int32(rInf.GetIdx()), 1 );
+ }
+ else
+ {
+ aKernArray[ sal_Int32(rInf.GetLen()) - 2 ] += nSpaceAdd;
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ aKernArray, sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ }
+ else
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ aKernArray, sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ else
+ {
+ Point aTmpPos( aTextOriginPos );
+ sal_Int32 j = 0;
+ sal_Int32 i;
+ for( i = 0; i < sal_Int32(rInf.GetLen()); i++ )
+ {
+ if (CH_BLANK == rInf.GetText()[sal_Int32(rInf.GetIdx()) + i])
+ {
+ nKernSum += nSpaceAdd;
+ if( j < i )
+ rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
+ sal_Int32(rInf.GetIdx()) + j, i - j);
+ j = i + 1;
+ SwTwips nAdd = aKernArray[ i ] + nKernSum;
+ if ( ( vcl::text::ComplexTextLayoutFlags::BiDiStrong | vcl::text::ComplexTextLayoutFlags::BiDiRtl ) == nMode )
+ nAdd *= -1;
+ aTmpPos.setX( aTextOriginPos.X() + nAdd );
+ }
+ }
+ if( j < i )
+ rInf.GetOut().DrawText( aTmpPos, rInf.GetText(),
+ sal_Int32(rInf.GetIdx()) + j, i - j);
+ }
+ }
+ else if( bStretch )
+ {
+ tools::Long nTmpWidth = rInf.GetWidth();
+ if( rInf.GetKern() && rInf.GetLen() && nTmpWidth > rInf.GetKern() )
+ nTmpWidth -= rInf.GetKern();
+ rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
+ rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ else if( rInf.GetKern() )
+ {
+ const tools::Long nTmpWidth = GetTextSize( rInf ).Width();
+
+ const Color aSaveColor( pTmpFont->GetColor() );
+ const bool bColorChanged = rInf.ApplyAutoColor( pTmpFont );
+
+ if( bColorChanged )
+ {
+ if( !pTmpFont->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *pTmpFont );
+ pTmpFont->SetColor( aSaveColor );
+ }
+
+ rInf.GetOut().DrawStretchText( aTextOriginPos, nTmpWidth,
+ rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+ else
+ rInf.GetOut().DrawText( aTextOriginPos, rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+
+ // PAINTING WITH FORMATTING DEVICE/SCREEN ADJUSTMENT
+
+ else
+ {
+ const bool bOrigTextRenderModeForResolutionIndependentLayout(rInf.GetOut().GetTextRenderModeForResolutionIndependentLayout());
+ // set text render mode to suit use of resolution independent text layout
+ rInf.GetOut().SetTextRenderModeForResolutionIndependentLayout(true);
+
+ OUString aBulletOverlay;
+ bool bBullet = rInf.GetBullet();
+ if( m_bSymbol )
+ bBullet = false;
+ std::vector<sal_Int32> aKernArray;
+ CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
+
+ // OLE: no printer available
+ // OSL_ENSURE( pPrinter, "DrawText needs pPrinter" )
+ if ( m_pPrinter )
+ {
+ // pTmpFont has already been set as current font for rInf.GetOut()
+ if ( m_pPrinter.get() != rInf.GetpOut() || pTmpFont != m_pPrtFont )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont( *m_pPrtFont );
+ }
+ GetTextArray(*m_pPrinter, rInf, aKernArray);
+ }
+ else
+ {
+ GetTextArray(rInf.GetOut(), rInf, aKernArray);
+ }
+
+ // Modify Printer and ScreenArrays for special justifications
+
+ tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ bool bNoHalfSpace = false;
+
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+
+ // Kana Compression
+ if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() ) )
+ {
+ pSI->Compress( aKernArray.data(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered( rInf.GetOut() ), &aTextOriginPos );
+ }
+
+ // Asian Justification
+ if ( SwFontScript::CJK == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(),
+ rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
+
+ nSpaceAdd = 0;
+ }
+ }
+
+ // Kashida Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
+ {
+ if ( pSI && pSI->CountKashida() &&
+ pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(),
+ rInf.GetLen(), nSpaceAdd ) != -1 )
+ nSpaceAdd = 0;
+ else
+ bNoHalfSpace = true;
+ }
+ }
+
+ // Thai Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CTL );
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(),
+ rInf.GetIdx(),
+ rInf.GetLen(),
+ rInf.GetNumberOfBlanks(),
+ rInf.GetSpace() );
+
+ // adding space to blanks is already done
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ if( bBullet )
+ {
+ // Copy the substring that will be painted, and replace spaces with
+ // bullets, and everything else with space.
+ sal_Int32 nCopyStart = sal_Int32(rInf.GetIdx());
+ sal_Int32 nCopyLen = sal_Int32(rInf.GetLen());
+
+ aBulletOverlay = rInf.GetText().copy( nCopyStart, nCopyLen );
+
+ for( sal_Int32 i = 0; i < aBulletOverlay.getLength(); ++i )
+ if( CH_BLANK == aBulletOverlay[ i ] )
+ {
+ /* fdo#72488 Hack: try to see if the space is zero width
+ * and don't bother with inserting a bullet in this case.
+ */
+ if ((i + nCopyStart + 1 >= sal_Int32(rInf.GetLen())) ||
+ aKernArray[i + nCopyStart] != aKernArray[ i + nCopyStart + 1])
+ {
+ aBulletOverlay = aBulletOverlay.replaceAt(i, 1, rtl::OUStringChar(CH_BULLET));
+ }
+ else
+ {
+ aBulletOverlay = aBulletOverlay.replaceAt(i, 1, rtl::OUStringChar(CH_BLANK));
+ }
+ }
+ else
+ {
+ aBulletOverlay = aBulletOverlay.replaceAt(i, 1, rtl::OUStringChar(CH_BLANK));
+ }
+ }
+
+ TextFrameIndex nCnt(rInf.GetText().getLength());
+ if ( nCnt < rInf.GetIdx() )
+ assert(false); // layout bug, not handled below
+ else
+ nCnt = nCnt - rInf.GetIdx();
+ nCnt = std::min(nCnt, rInf.GetLen());
+ sal_Unicode cChPrev = rInf.GetText()[sal_Int32(rInf.GetIdx())];
+
+ // In case of a single underlined space in justified text,
+ // have to output 2 spaces:
+ if ((nCnt == TextFrameIndex(1)) && rInf.GetSpace() && (cChPrev == CH_BLANK))
+ {
+ aKernArray[0] = rInf.GetWidth() +
+ rInf.GetKern() +
+ ( rInf.GetSpace() / SPACING_PRECISION_FACTOR );
+
+ if ( bSwitchL2R )
+ rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
+ aKernArray, sal_Int32(rInf.GetIdx()), 1 );
+ if( bBullet )
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), aKernArray,
+ sal_Int32(rInf.GetIdx()), 1 );
+ }
+ else
+ {
+ if (m_pPrtFont->IsWordLineMode())
+ bNoHalfSpace = true;
+
+ Justify::SpaceDistribution(aKernArray, rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(nCnt), nSpaceAdd, rInf.GetKern(), bNoHalfSpace);
+
+ if( rInf.GetGreyWave() )
+ {
+ if( rInf.GetLen() )
+ {
+ tools::Long nHght = rInf.GetOut().LogicToPixel(
+ m_pPrtFont->GetFontSize() ).Height();
+ if( WRONG_SHOW_MIN < nHght )
+ {
+ if ( rInf.GetOut().GetConnectMetaFile() )
+ rInf.GetOut().Push();
+
+ Color aCol( rInf.GetOut().GetLineColor() );
+ bool bColSave = aCol != gWaveCol;
+ if ( bColSave )
+ rInf.GetOut().SetLineColor( gWaveCol );
+
+ Point aEnd;
+ tools::Long nKernVal = aKernArray[sal_Int32(rInf.GetLen()) - 1];
+
+ const Degree10 nDir = bBidiPor
+ ? 1800_deg10
+ : UnMapDirection(GetFont().GetOrientation(),
+ bSwitchH2V, bSwitchH2VLRBT);
+
+ switch ( nDir.get() )
+ {
+ case 0 :
+ aEnd.setX( rInf.GetPos().X() + nKernVal );
+ aEnd.setY( rInf.GetPos().Y() );
+ break;
+ case 900 :
+ aEnd.setX( rInf.GetPos().X() );
+ aEnd.setY( rInf.GetPos().Y() - nKernVal );
+ break;
+ case 1800 :
+ aEnd.setX( rInf.GetPos().X() - nKernVal );
+ aEnd.setY( rInf.GetPos().Y() );
+ break;
+ case 2700 :
+ aEnd.setX( rInf.GetPos().X() );
+ aEnd.setY( rInf.GetPos().Y() + nKernVal );
+ break;
+ }
+
+ Point aCurrPos( rInf.GetPos() );
+
+ if ( bSwitchL2R )
+ {
+ rInf.GetFrame()->SwitchLTRtoRTL( aCurrPos );
+ rInf.GetFrame()->SwitchLTRtoRTL( aEnd );
+ }
+
+ if ( bSwitchH2V )
+ {
+ rInf.GetFrame()->SwitchHorizontalToVertical( aCurrPos );
+ rInf.GetFrame()->SwitchHorizontalToVertical( aEnd );
+ }
+ {
+ vcl::ScopedAntialiasing a(rInf.GetOut(), true);
+ rInf.GetOut().DrawWaveLine( aCurrPos, aEnd );
+ }
+ if ( bColSave )
+ rInf.GetOut().SetLineColor( aCol );
+
+ if ( rInf.GetOut().GetConnectMetaFile() )
+ rInf.GetOut().Pop();
+ }
+ }
+ }
+ else if( !m_bSymbol && rInf.GetLen() )
+ {
+ // anything to do?
+ if (rInf.GetWrong() || rInf.GetGrammarCheck() || rInf.GetSmartTags())
+ {
+ const tools::Long nHalfSpace = bNoHalfSpace ? 0 : nSpaceAdd / 2;
+ CalcLinePosData aCalcLinePosData(rInf, GetFont(), nCnt, bSwitchH2V,
+ bSwitchH2VLRBT, bSwitchL2R, nHalfSpace,
+ aKernArray.data(), bBidiPor);
+
+ SwForbidden aForbidden;
+ // draw line for smart tag data
+ lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetSmartTags(), aCalcLinePosData, Size() );
+ // draw wave line for spell check errors
+ // draw them BEFORE the grammar check lines to 'override' the latter in case of conflict.
+ // reason: some grammar errors can only be found if spelling errors are fixed,
+ // therefore we don't want the user to miss a spelling error.
+ lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetWrong(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
+ // draw wave line for grammar check errors
+ lcl_DrawLineForWrongListData( aForbidden, rInf, rInf.GetGrammarCheck(), aCalcLinePosData, m_pPrtFont->GetFontSize() );
+ }
+ }
+
+ sal_Int32 nLen = sal_Int32(rInf.GetLen());
+
+ if( nLen > 0 )
+ {
+
+ if ( bSwitchL2R )
+ rInf.GetFrame()->SwitchLTRtoRTL( aTextOriginPos );
+
+ if ( bSwitchH2V )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
+
+ sal_Int32 nIdx = sal_Int32(rInf.GetIdx());
+ const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rInf.GetOut(),
+ rInf.GetText(), nIdx, nLen);
+ rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), aKernArray,
+ nIdx, nLen, SalLayoutFlags::NONE, pGlyphs );
+ if (bBullet)
+ {
+ rInf.GetOut().Push();
+ Color aPreviousColor = pTmpFont->GetColor();
+
+ FontLineStyle aPreviousUnderline = pTmpFont->GetUnderline();
+ FontLineStyle aPreviousOverline = pTmpFont->GetOverline();
+ FontStrikeout aPreviousStrikeout = pTmpFont->GetStrikeout();
+
+ pTmpFont->SetColor( NON_PRINTING_CHARACTER_COLOR );
+ pTmpFont->SetUnderline(LINESTYLE_NONE);
+ pTmpFont->SetOverline(LINESTYLE_NONE);
+ pTmpFont->SetStrikeout(STRIKEOUT_NONE);
+ rInf.GetOut().SetFont( *pTmpFont );
+ tools::Long nShift = rInf.GetOut( ).GetFontMetric( ).GetBulletOffset( );
+ if ( nShift )
+ {
+ tools::Long nAdd = 0;
+
+ if ( aBulletOverlay[ 0 ] == CH_BULLET )
+ {
+ if (bSwitchH2V)
+ aTextOriginPos.AdjustY(nShift ) ;
+ else
+ aTextOriginPos.AdjustX(nShift ) ;
+ nAdd = nShift ;
+ }
+ for( sal_Int32 i = 1 ; i < nLen ; ++i )
+ {
+ if ( aBulletOverlay[ i ] == CH_BULLET )
+ aKernArray [ i - 1 ] += nShift ;
+ if ( nAdd )
+ aKernArray [ i - 1 ] -= nAdd;
+ }
+ }
+ rInf.GetOut().DrawTextArray( aTextOriginPos, aBulletOverlay, aKernArray,
+ 0, nLen );
+ pTmpFont->SetColor( aPreviousColor );
+
+ pTmpFont->SetUnderline(aPreviousUnderline);
+ pTmpFont->SetOverline(aPreviousOverline);
+ pTmpFont->SetStrikeout(aPreviousStrikeout);
+ rInf.GetOut().Pop();
+ }
+ }
+ }
+
+ rInf.GetOut().SetTextRenderModeForResolutionIndependentLayout(bOrigTextRenderModeForResolutionIndependentLayout);
+ }
+}
+
+Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf )
+{
+ Size aTextSize;
+ const TextFrameIndex nLn = (TextFrameIndex(COMPLETE_STRING) != rInf.GetLen())
+ ? rInf.GetLen()
+ : TextFrameIndex(rInf.GetText().getLength());
+
+ // be sure to have the correct layout mode at the printer
+ if ( m_pPrinter )
+ {
+ m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
+ m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
+ }
+
+ if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() && rInf.GetFont() &&
+ SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() )
+ {
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ OutputDevice* pOutDev;
+
+ if ( m_pPrinter )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont(*m_pPrtFont);
+ pOutDev = m_pPrinter;
+ }
+ else
+ pOutDev = rInf.GetpOut();
+
+ aTextSize.setWidth( pOutDev->GetTextWidth(rInf.GetText(),
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn)) );
+
+ OSL_ENSURE( !rInf.GetShell() ||
+ ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExternalLeading() ),
+ "Leading values should be already calculated" );
+ aTextSize.setHeight( pOutDev->GetTextHeight() +
+ GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
+
+ std::vector<sal_Int32> aKernArray;
+ GetTextArray(*pOutDev, rInf, aKernArray, sal_Int32(rInf.GetLen()));
+ if (pGrid->IsSnapToChars())
+ {
+ Justify::SnapToGrid(aKernArray, rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()), nGridWidth, true);
+ }
+ else
+ {
+ // use 0 to calculate raw width without rInf.GetSpace().
+ Justify::SnapToGridEdge(aKernArray, sal_Int32(rInf.GetLen()), nGridWidth, 0,
+ rInf.GetKern());
+ }
+
+ aTextSize.setWidth(aKernArray[sal_Int32(rInf.GetLen()) - 1]);
+ rInf.SetKanaDiff( 0 );
+ return aTextSize;
+ }
+ }
+
+ const bool bCompress = rInf.GetKanaComp() && nLn &&
+ rInf.GetFont() &&
+ SwFontScript::CJK == rInf.GetFont()->GetActual() &&
+ rInf.GetScriptInfo() &&
+ rInf.GetScriptInfo()->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() );
+
+ OSL_ENSURE( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
+ CountCompChg()), "Compression without info" );
+
+ // This is the part used e.g., for cursor travelling
+ // See condition for DrawText or DrawTextArray (bDirectPrint)
+ std::vector<sal_Int32> aKernArray;
+ if ( m_pPrinter && m_pPrinter.get() != rInf.GetpOut() )
+ {
+ if( !m_pPrtFont->IsSameInstance( m_pPrinter->GetFont() ) )
+ m_pPrinter->SetFont(*m_pPrtFont);
+ aTextSize.setHeight( m_pPrinter->GetTextHeight() );
+
+ CreateScrFont( *rInf.GetShell(), rInf.GetOut() );
+ if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *m_pScrFont );
+
+ GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray,
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
+ }
+ else
+ {
+ if( !m_pPrtFont->IsSameInstance( rInf.GetOut().GetFont() ) )
+ rInf.GetOut().SetFont( *m_pPrtFont );
+ aTextSize.setHeight( rInf.GetOut().GetTextHeight() );
+
+ GetTextArray(rInf.GetOut(), rInf, aKernArray, nLn.get());
+ }
+
+ if (bCompress)
+ {
+ rInf.SetKanaDiff(rInf.GetScriptInfo()->Compress(aKernArray.data(), rInf.GetIdx(), nLn, rInf.GetKanaComp(),
+ o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()), lcl_IsFullstopCentered(rInf.GetOut())));
+ }
+ else
+ rInf.SetKanaDiff( 0 );
+
+ if (nLn)
+ {
+ aTextSize.setWidth(aKernArray[sal_Int32(nLn) - 1]);
+
+ // Note that we can't simply use sal_Int(nLn) - 1 as nSpaceCount
+ // because a glyph may be made up of more than one characters.
+ sal_Int32 nSpaceCount = 0;
+ tools::Long nOldValue = aKernArray[0];
+
+ for(sal_Int32 i = 1; i < sal_Int32(nLn); ++i)
+ {
+ if (nOldValue != aKernArray[i])
+ {
+ ++nSpaceCount;
+ nOldValue = aKernArray[i];
+ }
+ }
+
+ if (rInf.GetKern())
+ aTextSize.AdjustWidth(nSpaceCount * rInf.GetKern());
+ }
+
+ OSL_ENSURE( !rInf.GetShell() ||
+ ( USHRT_MAX != GetGuessedLeading() && USHRT_MAX != GetExternalLeading() ),
+ "Leading values should be already calculated" );
+ aTextSize.AdjustHeight(GetFontLeading( rInf.GetShell(), rInf.GetOut() ) );
+ return aTextSize;
+}
+
+TextFrameIndex SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf)
+{
+ tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ const tools::Long nCharacterSpacing = -rInf.GetCharacterSpacing() / SPACING_PRECISION_FACTOR;
+ tools::Long nKern = rInf.GetKern();
+
+ if( 0 != nCharacterSpacing )
+ nKern -= nCharacterSpacing;
+
+ std::vector<sal_Int32> aKernArray;
+
+ // be sure to have the correct layout mode at the printer
+ if ( m_pPrinter )
+ {
+ m_pPrinter->SetLayoutMode( rInf.GetOut().GetLayoutMode() );
+ m_pPrinter->SetDigitLanguage( rInf.GetOut().GetDigitLanguage() );
+ GetTextArray(*m_pPrinter, rInf, aKernArray);
+ }
+ else
+ {
+ GetTextArray(rInf.GetOut(), rInf, aKernArray);
+ }
+
+ if ( rInf.GetFrame() && rInf.GetLen() && rInf.SnapToGrid() &&
+ rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() )
+ {
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ if (pGrid->IsSnapToChars())
+ {
+ Justify::SnapToGrid(aKernArray, rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()), nGridWidth, true);
+ }
+ else
+ {
+ Justify::SnapToGridEdge(aKernArray, sal_Int32(rInf.GetLen()), nGridWidth,
+ nSpaceAdd, rInf.GetKern());
+ }
+
+ return TextFrameIndex(Justify::GetModelPosition(aKernArray, sal_Int32(rInf.GetLen()),
+ rInf.GetOffset()));
+ }
+ }
+
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+ if ( rInf.GetFont() && rInf.GetLen() )
+ {
+ const SwFontScript nActual = rInf.GetFont()->GetActual();
+
+ // Kana Compression
+ if ( SwFontScript::CJK == nActual && rInf.GetKanaComp() &&
+ pSI && pSI->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() ) )
+ {
+ pSI->Compress( aKernArray.data(), rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetKanaComp(),
+ o3tl::narrowing<sal_uInt16>(m_aFont.GetFontSize().Height()),
+ lcl_IsFullstopCentered( rInf.GetOut() ) );
+ }
+
+ // Asian Justification
+ if ( SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CJK );
+
+ if (!MsLangId::isKorean(aLang))
+ {
+ SwScriptInfo::CJKJustify( rInf.GetText(), aKernArray.data(),
+ rInf.GetIdx(), rInf.GetLen(), aLang, nSpaceAdd, rInf.IsSpaceStop() );
+
+ nSpaceAdd = 0;
+ }
+
+ }
+
+ // Kashida Justification
+ if ( SwFontScript::CTL == nActual && rInf.GetSpace() )
+ {
+ if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), rInf.GetLen() ) )
+ {
+ if ( pSI && pSI->CountKashida() &&
+ pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(), rInf.GetLen(),
+ nSpaceAdd ) != -1 )
+ nSpaceAdd = 0;
+ }
+ }
+
+ // Thai Justification
+ if ( SwFontScript::CTL == nActual && nSpaceAdd )
+ {
+ LanguageType aLang = rInf.GetFont()->GetLanguage( SwFontScript::CTL );
+
+ if ( LANGUAGE_THAI == aLang )
+ {
+ SwScriptInfo::ThaiJustify( rInf.GetText(), aKernArray.data(),
+ rInf.GetIdx(), rInf.GetLen(),
+ rInf.GetNumberOfBlanks(),
+ rInf.GetSpace() );
+
+ // adding space to blanks is already done
+ nSpaceAdd = 0;
+ }
+ }
+ }
+
+ tools::Long nLeft = 0;
+ tools::Long nRight = 0;
+ TextFrameIndex nCnt(0);
+ tools::Long nSpaceSum = 0;
+ tools::Long nKernSum = 0;
+
+ sal_Int32 nDone = 0;
+ TextFrameIndex nIdx = rInf.GetIdx();
+ TextFrameIndex nLastIdx = nIdx;
+ const TextFrameIndex nEnd = rInf.GetIdx() + rInf.GetLen();
+
+ // #i105901#
+ // skip character cells for all script types
+ LanguageType aLang = rInf.GetFont()->GetLanguage();
+
+ while ( ( nRight < tools::Long( rInf.GetOffset() ) ) && ( nIdx < nEnd ) )
+ {
+ if (nSpaceAdd && CH_BLANK == rInf.GetText()[ sal_Int32(nIdx)])
+ nSpaceSum += nSpaceAdd;
+
+ // go to next character (cell).
+ nLastIdx = nIdx;
+
+ nIdx = TextFrameIndex(g_pBreakIt->GetBreakIter()->nextCharacters(
+ rInf.GetText(), sal_Int32(nIdx),
+ g_pBreakIt->GetLocale( aLang ),
+ i18n::CharacterIteratorMode::SKIPCELL, 1, nDone));
+ if ( nIdx <= nLastIdx )
+ break;
+
+ // the next character might be outside the layout range (e.g tdf124116-1.odt)
+ if (nIdx > nEnd)
+ nIdx = nEnd;
+
+ nLeft = nRight;
+ nRight = aKernArray[sal_Int32(nIdx - rInf.GetIdx()) - 1] + nKernSum + nSpaceSum;
+
+ nKernSum += nKern;
+ }
+
+ // step back if position is before the middle of the character
+ // or if we do not want to go to the next character
+ if ( nIdx > rInf.GetIdx() &&
+ ( rInf.IsPosMatchesBounds() ||
+ ( ( nRight > tools::Long( rInf.GetOffset() ) ) &&
+ ( nRight - rInf.GetOffset() > rInf.GetOffset() - nLeft ) ) ) )
+ nCnt = nLastIdx - rInf.GetIdx(); // first half
+ else
+ nCnt = nIdx - rInf.GetIdx(); // second half
+
+ if ( pSI )
+ rInf.SetCursorBidiLevel( pSI->DirType( nLastIdx ) );
+
+ return nCnt;
+}
+
+SwFntAccess::SwFntAccess( const void* & rnFontCacheId,
+ sal_uInt16 &rIndex, const void *pOwn, SwViewShell const *pSh,
+ bool bCheck ) :
+ SwCacheAccess( *pFntCache, rnFontCacheId, rIndex ),
+ m_pShell( pSh )
+{
+ // the used ctor of SwCacheAccess searches for rnFontCacheId+rIndex in the cache
+ if ( m_pObj )
+ {
+ // fast case: known Font (rnFontCacheId), no need to check printer and zoom
+ if ( !bCheck )
+ return;
+
+ // Font is known, but has to be checked
+ }
+ else
+ { // Font not known, must be searched
+ bCheck = false;
+ }
+
+ {
+ OutputDevice* pOut = nullptr;
+ sal_uInt16 nZoom = USHRT_MAX;
+
+ // Get the reference device
+ if ( pSh )
+ {
+ pOut = &pSh->GetRefDev();
+ nZoom = pSh->GetViewOptions()->GetZoom();
+ }
+
+ SwFntObj *pFntObj;
+ if ( bCheck )
+ {
+ pFntObj = Get();
+ if ( ( pFntObj->GetZoom( ) == nZoom ) &&
+ ( pFntObj->m_pPrinter == pOut ) &&
+ pFntObj->GetPropWidth() ==
+ static_cast<SwSubFont const *>(pOwn)->GetPropWidth() )
+ {
+ return; // result of Check: Drucker+Zoom okay.
+ }
+ pFntObj->Unlock(); // forget this object, printer/zoom differs
+ m_pObj = nullptr;
+ }
+
+ // Search by font comparison, quite expensive!
+ // Look for same font and same printer
+ pFntObj = pFntCache->First();
+ while ( pFntObj && !( pFntObj->m_aFont == *static_cast<vcl::Font const *>(pOwn) &&
+ pFntObj->GetZoom() == nZoom &&
+ pFntObj->GetPropWidth() ==
+ static_cast<SwSubFont const *>(pOwn)->GetPropWidth() &&
+ ( !pFntObj->m_pPrinter || pFntObj->m_pPrinter == pOut ) ) )
+ pFntObj = SwFntCache::Next( pFntObj );
+
+ if( pFntObj && pFntObj->m_pPrinter.get() != pOut )
+ {
+ // found one without printer, let's see if there is one with
+ // the same printer as well
+ SwFntObj *pTmpObj = pFntObj;
+ while( pTmpObj && !( pTmpObj->m_aFont == *static_cast<vcl::Font const *>(pOwn) &&
+ pTmpObj->GetZoom()==nZoom && pTmpObj->m_pPrinter==pOut &&
+ pTmpObj->GetPropWidth() ==
+ static_cast<SwSubFont const *>(pOwn)->GetPropWidth() ) )
+ pTmpObj = SwFntCache::Next( pTmpObj );
+ if( pTmpObj )
+ pFntObj = pTmpObj;
+ }
+
+ if ( !pFntObj ) // Font has not been found, create one
+ {
+ // Have to create new Object, hence Owner must be a SwFont, later
+ // the Owner will be the "MagicNumber"
+ SwCacheAccess::m_pOwner = pOwn;
+ pFntObj = Get(); // will create via NewObj() and lock
+ OSL_ENSURE(pFntObj, "No Font, no Fun.");
+ }
+ else // Font has been found, so we lock it.
+ {
+ pFntObj->Lock();
+ if (pFntObj->m_pPrinter.get() != pOut) // if no printer is known by now
+ {
+ OSL_ENSURE( !pFntObj->m_pPrinter, "SwFntAccess: Printer Changed" );
+ pFntObj->CreatePrtFont( *pOut );
+ pFntObj->m_pPrinter = pOut;
+ pFntObj->m_pScrFont = nullptr;
+ pFntObj->m_nGuessedLeading = USHRT_MAX;
+ pFntObj->m_nExtLeading = USHRT_MAX;
+ pFntObj->m_nPrtAscent = USHRT_MAX;
+ pFntObj->m_nPrtHeight = USHRT_MAX;
+ }
+ m_pObj = pFntObj;
+ }
+
+ // no matter if new or found, now the Owner of the Object is a
+ // MagicNumber, and will be given to the SwFont, as well as the Index
+ // for later direct access
+ rnFontCacheId = reinterpret_cast<void*>(reinterpret_cast<sal_IntPtr>(pFntObj->GetOwner()));
+ SwCacheAccess::m_pOwner = pFntObj->GetOwner();
+ rIndex = pFntObj->GetCachePos();
+ }
+}
+
+SwCacheObj *SwFntAccess::NewObj( )
+{
+ // "MagicNumber" used to identify Fonts
+ static std::uintptr_t fontCacheIdCounter = 0;
+ // a new Font, a new "MagicNumber".
+ return new SwFntObj( *static_cast<SwSubFont const *>(m_pOwner), ++fontCacheIdCounter, m_pShell );
+}
+
+TextFrameIndex SwFont::GetTextBreak(SwDrawTextInfo const & rInf, tools::Long nTextWidth)
+{
+ ChgFnt( rInf.GetShell(), rInf.GetOut() );
+
+ const bool bCompress = rInf.GetKanaComp() && rInf.GetLen() &&
+ SwFontScript::CJK == GetActual() &&
+ rInf.GetScriptInfo() &&
+ rInf.GetScriptInfo()->CountCompChg() &&
+ lcl_IsMonoSpaceFont( rInf.GetOut() );
+
+ OSL_ENSURE( !bCompress || ( rInf.GetScriptInfo() && rInf.GetScriptInfo()->
+ CountCompChg()), "Compression without info" );
+
+ TextFrameIndex nTextBreak(0);
+ tools::Long nKern = 0;
+
+ TextFrameIndex nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
+ ? TextFrameIndex(rInf.GetText().getLength()) : rInf.GetLen();
+
+ if ( rInf.GetFrame() && nLn && rInf.SnapToGrid() &&
+ rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() )
+ {
+ SwTextGridItem const*const pGrid(GetGridItem(rInf.GetFrame()->FindPageFrame()));
+ if ( pGrid && GRID_LINES_CHARS == pGrid->GetGridType() )
+ {
+ const SwDoc* pDoc = rInf.GetShell()->GetDoc();
+ const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
+
+ std::vector<sal_Int32> aKernArray;
+ GetTextArray( rInf.GetOut(), rInf.GetText(), aKernArray,
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+
+ if (pGrid->IsSnapToChars())
+ {
+ Justify::SnapToGrid(aKernArray, rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ sal_Int32(rInf.GetLen()), nGridWidth, true);
+ }
+ else
+ {
+ // use 0 to calculate raw width without rInf.GetSpace().
+ Justify::SnapToGridEdge(aKernArray, sal_Int32(rInf.GetLen()), nGridWidth,
+ 0, rInf.GetKern());
+ }
+
+ while(nTextBreak < rInf.GetLen() && aKernArray[sal_Int32(nTextBreak)] <= nTextWidth)
+ ++nTextBreak;
+
+ return nTextBreak + rInf.GetIdx();
+ }
+ }
+
+ if( m_aSub[m_nActual].IsCapital() && nLn )
+ {
+ nTextBreak = GetCapitalBreak( rInf.GetShell(), rInf.GetpOut(),
+ rInf.GetScriptInfo(), rInf.GetText(), nTextWidth, rInf.GetIdx(),
+ nLn );
+ }
+ else
+ {
+ nKern = CheckKerning();
+
+ const OUString* pTmpText;
+ OUString aTmpText;
+ TextFrameIndex nTmpIdx;
+ TextFrameIndex nTmpLen;
+ bool bTextReplaced = false;
+
+ if ( !m_aSub[m_nActual].IsCaseMap() )
+ {
+ pTmpText = &rInf.GetText();
+ nTmpIdx = rInf.GetIdx();
+ nTmpLen = nLn;
+ }
+ else
+ {
+ const OUString aSnippet(rInf.GetText().copy(sal_Int32(rInf.GetIdx()), sal_Int32(nLn)));
+ aTmpText = m_aSub[m_nActual].CalcCaseMap( aSnippet );
+ const bool bTitle = SvxCaseMap::Capitalize == m_aSub[m_nActual].GetCaseMap();
+
+ // Uaaaaahhhh!!! In title case mode, we would get wrong results
+ if ( bTitle && nLn )
+ {
+ // check if rInf.GetIdx() is begin of word
+ if ( !g_pBreakIt->GetBreakIter()->isBeginWord(
+ rInf.GetText(), sal_Int32(rInf.GetIdx()),
+ g_pBreakIt->GetLocale( m_aSub[m_nActual].GetLanguage() ),
+ i18n::WordType::ANYWORD_IGNOREWHITESPACES ) )
+ {
+ // In this case, the beginning of aTmpText is wrong.
+ OUString aSnippetTmp(aSnippet.copy(0, 1));
+ aSnippetTmp = m_aSub[m_nActual].CalcCaseMap( aSnippetTmp );
+ aTmpText = aTmpText.replaceAt( 0, aSnippetTmp.getLength(), rtl::OUStringChar(aSnippet[0]) );
+ }
+ }
+
+ pTmpText = &aTmpText;
+ nTmpIdx = TextFrameIndex(0);
+ nTmpLen = TextFrameIndex(aTmpText.getLength());
+ bTextReplaced = true;
+ }
+
+ if( rInf.GetHyphPos() ) {
+ sal_Int32 nHyphPos = sal_Int32(*rInf.GetHyphPos());
+ const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
+ &rInf.GetOut(), *pTmpText, nTmpIdx.get(), nTmpLen.get(), 0, rInf.GetVclCache());
+ nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak(
+ *pTmpText, nTextWidth,
+ u'-', nHyphPos,
+ sal_Int32(nTmpIdx), sal_Int32(nTmpLen),
+ nKern, rInf.GetVclCache(), pGlyphs));
+ *rInf.GetHyphPos() = TextFrameIndex((nHyphPos == -1) ? COMPLETE_STRING : nHyphPos);
+ }
+ else
+ {
+ SwFntAccess aFntAccess(m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex,
+ &m_aSub[m_nActual], rInf.GetShell());
+ const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rInf.GetOut(),
+ *pTmpText, nTmpIdx.get(), nTmpLen.get(), 0, rInf.GetVclCache());
+ nTextBreak = TextFrameIndex(rInf.GetOut().GetTextBreak(
+ *pTmpText, nTextWidth,
+ sal_Int32(nTmpIdx), sal_Int32(nTmpLen),
+ nKern, rInf.GetVclCache(), pGlyphs));
+ }
+
+ if (bTextReplaced && sal_Int32(nTextBreak) != -1)
+ {
+ if ( nTmpLen != nLn )
+ nTextBreak = sw_CalcCaseMap( *this, rInf.GetText(),
+ rInf.GetIdx(), nLn, nTextBreak );
+ else
+ nTextBreak = nTextBreak + rInf.GetIdx();
+ }
+ }
+
+ TextFrameIndex nTextBreak2 = sal_Int32(nTextBreak) == -1
+ ? TextFrameIndex(COMPLETE_STRING)
+ : nTextBreak;
+
+ // tdf112290 tdf136588 Break the line correctly only if there is an image inline,
+ // and the image wider than the line...
+ if (GetCaseMap() == SvxCaseMap::SmallCaps && TextFrameIndex(COMPLETE_STRING) == nTextBreak2 &&
+ ! bCompress && nTextWidth == 0)
+ // If nTextWidth == 0 means the line is full, we have to break it
+ nTextBreak2 = TextFrameIndex(1);
+
+ if ( ! bCompress )
+ return nTextBreak2;
+
+ nTextBreak2 = nTextBreak2 - rInf.GetIdx();
+
+ if( nTextBreak2 < nLn )
+ {
+ if( !nTextBreak2 && nLn )
+ nLn = TextFrameIndex(1);
+ else if (nLn > nTextBreak2 + nTextBreak2)
+ nLn = nTextBreak2 + nTextBreak2;
+ std::vector<sal_Int32> aKernArray;
+ GetTextArray( rInf.GetOut(), rInf.GetText(), aKernArray,
+ sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
+ if( rInf.GetScriptInfo()->Compress( aKernArray.data(), rInf.GetIdx(), nLn,
+ rInf.GetKanaComp(), o3tl::narrowing<sal_uInt16>(GetHeight( m_nActual )),
+ lcl_IsFullstopCentered( rInf.GetOut() ) ) )
+ {
+ tools::Long nKernAdd = nKern;
+ TextFrameIndex const nTmpBreak = nTextBreak2;
+ if( nKern && nTextBreak2 )
+ nKern *= sal_Int32(nTextBreak2) - 1;
+ while (nTextBreak2 < nLn && nTextWidth >= aKernArray[sal_Int32(nTextBreak2)] + nKern)
+ {
+ nKern += nKernAdd;
+ ++nTextBreak2;
+ }
+ if( rInf.GetHyphPos() )
+ *rInf.GetHyphPos() += nTextBreak2 - nTmpBreak; // It's not perfect
+ }
+ }
+ nTextBreak2 = nTextBreak2 + rInf.GetIdx();
+
+ return nTextBreak2;
+}
+
+bool SwDrawTextInfo::ApplyAutoColor( vcl::Font* pFont )
+{
+ const vcl::Font& rFnt = pFont ? *pFont : GetOut().GetFont();
+ Color nNewColor = COL_BLACK;
+ bool bChgFntColor = false;
+ bool bChgLineColor = false;
+
+ if (GetShell() && !GetShell()->GetWin() && GetShell()->GetViewOptions()->IsBlackFont())
+ {
+ if ( COL_BLACK != rFnt.GetColor() )
+ bChgFntColor = true;
+
+ if ( (COL_BLACK != GetOut().GetLineColor()) ||
+ (COL_BLACK != GetOut().GetOverlineColor()) )
+ bChgLineColor = true;
+ }
+ else
+ {
+ // FontColor has to be changed if:
+ // 1. FontColor = AUTO or 2. IsAlwaysAutoColor is set
+ // LineColor has to be changed if:
+ // 1. IsAlwaysAutoColor is set
+
+ bChgLineColor = GetShell() && GetShell()->GetWin() &&
+ GetShell()->GetAccessibilityOptions()->IsAlwaysAutoColor();
+
+ bChgFntColor = COL_AUTO == rFnt.GetColor() || bChgLineColor;
+
+ if ( bChgFntColor )
+ {
+ // check if current background has a user defined setting
+ std::optional<Color> pCol;
+ if (GetFont())
+ pCol = GetFont()->GetBackColor();
+ if( ! pCol || COL_TRANSPARENT == *pCol )
+ {
+ const SvxBrushItem* pItem;
+ SwRect aOrigBackRect;
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ /// OD 21.08.2002
+ /// consider, that [GetBackgroundBrush(...)] can set <pCol>
+ /// - see implementation in /core/layout/paintfrm.cxx
+ /// OD 21.08.2002 #99657#
+ /// There is a user defined setting for the background, if there
+ /// is a background brush and its color is *not* "no fill"/"auto fill".
+ if( GetFrame()->GetBackgroundBrush( aFillAttributes, pItem, pCol, aOrigBackRect, false, /*bConsiderTextBox=*/true ) )
+ {
+ if (aFillAttributes && aFillAttributes->isUsed())
+ {
+ // First see if fill attributes provide a color.
+ pCol = Color(aFillAttributes->getAverageColor(aGlobalRetoucheColor.getBColor()));
+ }
+
+ // If not, then fall back to the old brush item.
+ if ( !pCol )
+ {
+ pCol = pItem->GetColor();
+ }
+
+ /// OD 30.08.2002 #99657#
+ /// determined color <pCol> can be <COL_TRANSPARENT>. Thus, check it.
+ if ( *pCol == COL_TRANSPARENT)
+ pCol.reset();
+ }
+ else
+ pCol.reset();
+ }
+
+ // no user defined color at paragraph or font background
+ if ( ! pCol )
+ pCol = aGlobalRetoucheColor;
+
+ if( GetShell() && GetShell()->GetWin() )
+ {
+ // here we determine the preferred window text color for painting
+ const SwViewOption* pViewOption = GetShell()->GetViewOptions();
+ if(pViewOption->IsPagePreview() &&
+ !officecfg::Office::Common::Accessibility::IsForPagePreviews::get())
+ nNewColor = COL_BLACK;
+ else
+ // we take the font color from the appearance page
+ nNewColor = SwViewOption::GetFontColor();
+ }
+
+ // change painting color depending of dark/bright background
+ Color aTmpColor( nNewColor );
+ if ( pCol->IsDark() && aTmpColor.IsDark() )
+ nNewColor = COL_WHITE;
+ else if ( pCol->IsBright() && aTmpColor.IsBright() )
+ nNewColor = COL_BLACK;
+ }
+ }
+
+ if ( bChgFntColor || bChgLineColor )
+ {
+ Color aNewColor( nNewColor );
+
+ if ( bChgFntColor )
+ {
+ if ( pFont && aNewColor != pFont->GetColor() )
+ {
+ // only set the new color at the font passed as argument
+ pFont->SetColor( aNewColor );
+ }
+ else if ( aNewColor != GetOut().GetFont().GetColor() )
+ {
+ // set new font with new color at output device
+ vcl::Font aFont( rFnt );
+ aFont.SetColor( aNewColor );
+ GetOut().SetFont( aFont );
+ }
+ }
+
+ // the underline and overline colors have to be set separately
+ if ( bChgLineColor )
+ {
+ // get current font color or color set at output device
+ aNewColor = pFont ? pFont->GetColor() : GetOut().GetFont().GetColor();
+ if ( aNewColor != GetOut().GetLineColor() )
+ GetOut().SetLineColor( aNewColor );
+ if ( aNewColor != GetOut().GetOverlineColor() )
+ GetOut().SetOverlineColor( aNewColor );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/fntcap.cxx b/sw/source/core/txtnode/fntcap.cxx
new file mode 100644
index 000000000..a08988c45
--- /dev/null
+++ b/sw/source/core/txtnode/fntcap.cxx
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/svxfont.hxx>
+
+#include <vcl/outdev.hxx>
+#include <com/sun/star/i18n/CharType.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+
+#include <fntcache.hxx>
+#include <swfont.hxx>
+#include <breakit.hxx>
+#include <txtfrm.hxx>
+#include <scriptinfo.hxx>
+#include <fntcap.hxx>
+
+using namespace ::com::sun::star::i18n;
+
+namespace {
+
+// The information encapsulated in SwCapitalInfo is required
+// by the ::Do functions. They contain the information about
+// the original string, whereas rDo.GetInf() contains information
+// about the display string.
+class SwCapitalInfo
+{
+public:
+ explicit SwCapitalInfo( const OUString& rOrigText ) :
+ rString( rOrigText ), nIdx( 0 ), nLen( 0 ) {};
+ const OUString& rString;
+ TextFrameIndex nIdx;
+ TextFrameIndex nLen;
+};
+
+}
+
+// rFnt: required for CalcCaseMap
+// rOrigString: The original string
+// nOfst: Position of the substring in rOrigString
+// nLen: Length if the substring in rOrigString
+// nIdx: Refers to a position in the display string and should be mapped
+// to a position in rOrigString
+TextFrameIndex sw_CalcCaseMap(const SwFont& rFnt,
+ const OUString& rOrigString,
+ TextFrameIndex const nOfst,
+ TextFrameIndex const nLen,
+ TextFrameIndex const nIdx)
+{
+ int j = 0;
+ const TextFrameIndex nEnd = nOfst + nLen;
+ OSL_ENSURE( sal_Int32(nEnd) <= rOrigString.getLength(), "sw_CalcCaseMap: Wrong parameters" );
+
+ // special case for title case:
+ const bool bTitle = SvxCaseMap::Capitalize == rFnt.GetCaseMap();
+ for (TextFrameIndex i = nOfst; i < nEnd; ++i)
+ {
+ OUString aTmp(rOrigString.copy(sal_Int32(i), 1));
+
+ if ( !bTitle ||
+ g_pBreakIt->GetBreakIter()->isBeginWord(
+ rOrigString, sal_Int32(i),
+ g_pBreakIt->GetLocale( rFnt.GetLanguage() ),
+ WordType::ANYWORD_IGNOREWHITESPACES ) )
+ aTmp = rFnt.GetActualFont().CalcCaseMap( aTmp );
+
+ j += aTmp.getLength();
+
+ if (TextFrameIndex(j) > nIdx)
+ return i;
+ }
+
+ return nOfst + nLen;
+}
+
+class SwDoCapitals
+{
+protected:
+ SwDrawTextInfo &m_rInf;
+ SwCapitalInfo* m_pCapInf; // refers to additional information
+ // required by the ::Do function
+ explicit SwDoCapitals ( SwDrawTextInfo &rInfo ) : m_rInf( rInfo ), m_pCapInf( nullptr ) { }
+ ~SwDoCapitals() {}
+public:
+ virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) = 0;
+ virtual void Do() = 0;
+ OutputDevice& GetOut() { return m_rInf.GetOut(); }
+ SwDrawTextInfo& GetInf() { return m_rInf; }
+ SwCapitalInfo* GetCapInf() const { return m_pCapInf; }
+ void SetCapInf( SwCapitalInfo& rNew ) { m_pCapInf = &rNew; }
+};
+
+namespace {
+
+class SwDoGetCapitalSize : public SwDoCapitals
+{
+protected:
+ Size m_aTextSize;
+
+public:
+ explicit SwDoGetCapitalSize( SwDrawTextInfo &rInfo ) : SwDoCapitals ( rInfo ) { }
+ virtual ~SwDoGetCapitalSize() {}
+ virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) override;
+ virtual void Do() override;
+ const Size& GetSize() const { return m_aTextSize; }
+};
+
+}
+
+void SwDoGetCapitalSize::Init( SwFntObj *, SwFntObj * )
+{
+ m_aTextSize.setHeight(0);
+ m_aTextSize.setWidth(0);
+}
+
+void SwDoGetCapitalSize::Do()
+{
+ m_aTextSize.AdjustWidth(m_rInf.GetSize().Width());
+ if( m_rInf.GetUpper() )
+ m_aTextSize.setHeight(m_rInf.GetSize().Height());
+}
+
+Size SwSubFont::GetCapitalSize( SwDrawTextInfo& rInf )
+{
+ // Start:
+ const tools::Long nOldKern = rInf.GetKern();
+ rInf.SetKern( CheckKerning() );
+ rInf.SetPos( Point() );
+ rInf.SetSpace( 0 );
+ rInf.SetDrawSpace( false );
+ SwDoGetCapitalSize aDo( rInf );
+ DoOnCapitals( aDo );
+ Size aTextSize( aDo.GetSize() );
+
+ // End:
+ if( !aTextSize.Height() )
+ {
+ SV_STAT( nGetTextSize );
+ aTextSize.setHeight( short ( rInf.GetpOut()->GetTextHeight() ) );
+ }
+ rInf.SetKern( nOldKern );
+ return aTextSize;
+}
+
+namespace {
+
+class SwDoGetCapitalBreak : public SwDoCapitals
+{
+protected:
+ tools::Long m_nTextWidth;
+ TextFrameIndex m_nBreak;
+
+public:
+ SwDoGetCapitalBreak(SwDrawTextInfo& rInfo, tools::Long const nWidth)
+ : SwDoCapitals(rInfo)
+ , m_nTextWidth(nWidth)
+ , m_nBreak(-1)
+ { }
+ virtual ~SwDoGetCapitalBreak() {}
+ virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) override;
+ virtual void Do() override;
+ TextFrameIndex getBreak() const { return m_nBreak; }
+};
+
+}
+
+void SwDoGetCapitalBreak::Init( SwFntObj *, SwFntObj * )
+{
+}
+
+void SwDoGetCapitalBreak::Do()
+{
+ if (!m_nTextWidth)
+ return;
+
+ if (m_rInf.GetSize().Width() < m_nTextWidth)
+ m_nTextWidth -= m_rInf.GetSize().Width();
+ else
+ {
+ TextFrameIndex nEnd = m_rInf.GetEnd();
+ m_nBreak = TextFrameIndex(GetOut().GetTextBreak(m_rInf.GetText(), m_nTextWidth,
+ sal_Int32(m_rInf.GetIdx()),
+ sal_Int32(m_rInf.GetLen()), m_rInf.GetKern()));
+
+ if (m_nBreak > nEnd || m_nBreak < TextFrameIndex(0))
+ m_nBreak = nEnd;
+
+ // m_nBreak may be relative to the display string. It has to be
+ // calculated relative to the original string:
+ if ( GetCapInf() )
+ {
+ if ( GetCapInf()->nLen != m_rInf.GetLen() )
+ m_nBreak = sw_CalcCaseMap( *m_rInf.GetFont(),
+ GetCapInf()->rString,
+ GetCapInf()->nIdx,
+ GetCapInf()->nLen, m_nBreak );
+ else
+ m_nBreak = m_nBreak + GetCapInf()->nIdx;
+ }
+
+ m_nTextWidth = 0;
+ }
+}
+
+TextFrameIndex SwFont::GetCapitalBreak( SwViewShell const * pSh, const OutputDevice* pOut,
+ const SwScriptInfo* pScript, const OUString& rText, tools::Long const nTextWidth,
+ TextFrameIndex const nIdx, TextFrameIndex const nLen)
+{
+ // Start:
+ Point aPos( 0, 0 );
+ SwDrawTextInfo aInfo(pSh, *const_cast<OutputDevice*>(pOut), pScript, rText, nIdx, nLen,
+ 0, false);
+ aInfo.SetPos( aPos );
+ aInfo.SetSpace( 0 );
+ aInfo.SetWrong( nullptr );
+ aInfo.SetGrammarCheck( nullptr );
+ aInfo.SetSmartTags( nullptr );
+ aInfo.SetDrawSpace( false );
+ aInfo.SetKern( CheckKerning() );
+ aInfo.SetKanaComp( pScript ? 0 : 100 );
+ aInfo.SetFont( this );
+
+ SwDoGetCapitalBreak aDo(aInfo, nTextWidth);
+ DoOnCapitals( aDo );
+ return aDo.getBreak();
+}
+
+namespace {
+
+class SwDoDrawCapital : public SwDoCapitals
+{
+protected:
+ SwFntObj* m_pUpperFnt;
+ SwFntObj* m_pLowerFnt;
+
+public:
+ explicit SwDoDrawCapital(SwDrawTextInfo& rInfo)
+ : SwDoCapitals(rInfo)
+ , m_pUpperFnt(nullptr)
+ , m_pLowerFnt(nullptr)
+ { }
+ virtual ~SwDoDrawCapital() {}
+ virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) override;
+ virtual void Do() override;
+ void DrawSpace( Point &rPos );
+};
+
+}
+
+void SwDoDrawCapital::Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont )
+{
+ m_pUpperFnt = pUpperFont;
+ m_pLowerFnt = pLowerFont;
+}
+
+void SwDoDrawCapital::Do()
+{
+ SV_STAT( nDrawText );
+ const sal_uInt16 nOrgWidth = m_rInf.GetWidth();
+ m_rInf.SetWidth( sal_uInt16(m_rInf.GetSize().Width()) );
+ if ( m_rInf.GetUpper() )
+ m_pUpperFnt->DrawText(m_rInf);
+ else
+ {
+ bool bOldBullet = m_rInf.GetBullet();
+ m_rInf.SetBullet( false );
+ m_pLowerFnt->DrawText(m_rInf);
+ m_rInf.SetBullet( bOldBullet );
+ }
+
+ OSL_ENSURE(m_pUpperFnt, "No upper font, dying soon!");
+ m_rInf.Shift(m_pUpperFnt->GetFont().GetOrientation());
+ m_rInf.SetWidth( nOrgWidth );
+}
+
+void SwDoDrawCapital::DrawSpace( Point &rPos )
+{
+ tools::Long nDiff = m_rInf.GetPos().X() - rPos.X();
+
+ Point aPos( rPos );
+ const bool bSwitchL2R = m_rInf.GetFrame()->IsRightToLeft() &&
+ ! m_rInf.IsIgnoreFrameRTL();
+
+ if ( bSwitchL2R )
+ m_rInf.GetFrame()->SwitchLTRtoRTL( aPos );
+
+ const vcl::text::ComplexTextLayoutFlags nMode = m_rInf.GetpOut()->GetLayoutMode();
+ const bool bBidiPor = ( bSwitchL2R !=
+ ( vcl::text::ComplexTextLayoutFlags::Default != ( vcl::text::ComplexTextLayoutFlags::BiDiRtl & nMode ) ) );
+
+ if ( bBidiPor )
+ nDiff = -nDiff;
+
+ if ( m_rInf.GetFrame()->IsVertical() )
+ m_rInf.GetFrame()->SwitchHorizontalToVertical( aPos );
+
+ if ( nDiff )
+ {
+ m_rInf.ApplyAutoColor();
+ GetOut().DrawStretchText( aPos, nDiff,
+ " ", 0, 2 );
+ }
+ rPos.setX( m_rInf.GetPos().X() + m_rInf.GetWidth() );
+}
+
+void SwSubFont::DrawCapital( SwDrawTextInfo &rInf )
+{
+ // Precondition: rInf.GetPos() has already been calculated
+
+ rInf.SetDrawSpace( GetUnderline() != LINESTYLE_NONE ||
+ GetOverline() != LINESTYLE_NONE ||
+ GetStrikeout() != STRIKEOUT_NONE );
+ SwDoDrawCapital aDo( rInf );
+ DoOnCapitals( aDo );
+}
+
+namespace {
+
+class SwDoCapitalCursorOfst : public SwDoCapitals
+{
+protected:
+ SwFntObj* m_pUpperFnt;
+ SwFntObj* m_pLowerFnt;
+ TextFrameIndex m_nCursor;
+ sal_uInt16 m_nOfst;
+
+public:
+ SwDoCapitalCursorOfst(SwDrawTextInfo& rInfo, const sal_uInt16 nOfs)
+ : SwDoCapitals(rInfo)
+ , m_pUpperFnt(nullptr)
+ , m_pLowerFnt(nullptr)
+ , m_nCursor(0)
+ , m_nOfst(nOfs)
+ { }
+ virtual ~SwDoCapitalCursorOfst() {}
+ virtual void Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont ) override;
+ virtual void Do() override;
+
+ TextFrameIndex GetCursor() const { return m_nCursor; }
+};
+
+}
+
+void SwDoCapitalCursorOfst::Init( SwFntObj *pUpperFont, SwFntObj *pLowerFont )
+{
+ m_pUpperFnt = pUpperFont;
+ m_pLowerFnt = pLowerFont;
+}
+
+void SwDoCapitalCursorOfst::Do()
+{
+ if (!m_nOfst)
+ return;
+
+ if (static_cast<tools::Long>(m_nOfst) > m_rInf.GetSize().Width())
+ {
+ m_nOfst -= m_rInf.GetSize().Width();
+ m_nCursor = m_nCursor + m_rInf.GetLen();
+ }
+ else
+ {
+ SwDrawTextInfo aDrawInf( m_rInf.GetShell(), *m_rInf.GetpOut(),
+ m_rInf.GetScriptInfo(),
+ m_rInf.GetText(),
+ m_rInf.GetIdx(),
+ m_rInf.GetLen(), 0, false );
+ aDrawInf.SetOffset(m_nOfst);
+ aDrawInf.SetKern( m_rInf.GetKern() );
+ aDrawInf.SetKanaComp( m_rInf.GetKanaComp() );
+ aDrawInf.SetFrame( m_rInf.GetFrame() );
+ aDrawInf.SetFont( m_rInf.GetFont() );
+
+ if ( m_rInf.GetUpper() )
+ {
+ aDrawInf.SetSpace( 0 );
+ m_nCursor = m_nCursor + m_pUpperFnt->GetModelPositionForViewPoint(aDrawInf);
+ }
+ else
+ {
+ aDrawInf.SetSpace( m_rInf.GetSpace() );
+ m_nCursor = m_nCursor + m_pLowerFnt->GetModelPositionForViewPoint(aDrawInf);
+ }
+ m_nOfst = 0;
+ }
+}
+
+TextFrameIndex SwSubFont::GetCapitalCursorOfst( SwDrawTextInfo& rInf )
+{
+ const tools::Long nOldKern = rInf.GetKern();
+ rInf.SetKern( CheckKerning() );
+ SwDoCapitalCursorOfst aDo( rInf, rInf.GetOffset() );
+ rInf.SetPos( Point() );
+ rInf.SetDrawSpace( false );
+ DoOnCapitals( aDo );
+ rInf.SetKern( nOldKern );
+ return aDo.GetCursor();
+}
+
+namespace {
+
+class SwDoDrawStretchCapital : public SwDoDrawCapital
+{
+ const TextFrameIndex m_nStrLen;
+ const sal_uInt16 m_nCapWidth;
+ const sal_uInt16 m_nOrgWidth;
+
+public:
+ virtual void Do() override;
+
+ SwDoDrawStretchCapital(SwDrawTextInfo& rInfo, const sal_uInt16 nCapitalWidth)
+ : SwDoDrawCapital(rInfo)
+ , m_nStrLen(rInfo.GetLen())
+ , m_nCapWidth(nCapitalWidth)
+ , m_nOrgWidth(rInfo.GetWidth())
+ { }
+};
+
+}
+
+void SwDoDrawStretchCapital::Do()
+{
+ SV_STAT( nDrawStretchText );
+ tools::Long nPartWidth = m_rInf.GetSize().Width();
+
+ if( m_rInf.GetLen() )
+ {
+ // small caps and kerning
+ tools::Long nDiff = tools::Long(m_nOrgWidth) - tools::Long(m_nCapWidth);
+ if( nDiff )
+ {
+ nDiff *= sal_Int32(m_rInf.GetLen());
+ nDiff /= sal_Int32(m_nStrLen);
+ nDiff += nPartWidth;
+ if( 0 < nDiff )
+ nPartWidth = nDiff;
+ }
+
+ m_rInf.ApplyAutoColor();
+
+ Point aPos( m_rInf.GetPos() );
+ const bool bSwitchL2R = m_rInf.GetFrame()->IsRightToLeft() &&
+ ! m_rInf.IsIgnoreFrameRTL();
+
+ if ( bSwitchL2R )
+ m_rInf.GetFrame()->SwitchLTRtoRTL( aPos );
+
+ if ( m_rInf.GetFrame()->IsVertical() )
+ m_rInf.GetFrame()->SwitchHorizontalToVertical( aPos );
+
+ // Optimise:
+ if (TextFrameIndex(1) >= m_rInf.GetLen())
+ GetOut().DrawText(aPos, m_rInf.GetText(), sal_Int32(m_rInf.GetIdx()),
+ sal_Int32(m_rInf.GetLen()));
+ else
+ GetOut().DrawStretchText(aPos, nPartWidth, m_rInf.GetText(),
+ sal_Int32(m_rInf.GetIdx()), sal_Int32(m_rInf.GetLen()));
+ }
+ const_cast<Point&>(m_rInf.GetPos()).AdjustX(nPartWidth );
+}
+
+void SwSubFont::DrawStretchCapital( SwDrawTextInfo &rInf )
+{
+ // Precondition: rInf.GetPos() has already been calculated
+
+ if (rInf.GetLen() == TextFrameIndex(COMPLETE_STRING))
+ rInf.SetLen(TextFrameIndex(rInf.GetText().getLength()));
+
+ const Point aOldPos = rInf.GetPos();
+ const sal_uInt16 nCapWidth = o3tl::narrowing<sal_uInt16>( GetCapitalSize( rInf ).Width() );
+ rInf.SetPos(aOldPos);
+
+ rInf.SetDrawSpace( GetUnderline() != LINESTYLE_NONE ||
+ GetOverline() != LINESTYLE_NONE ||
+ GetStrikeout() != STRIKEOUT_NONE );
+ SwDoDrawStretchCapital aDo( rInf, nCapWidth );
+ DoOnCapitals( aDo );
+}
+
+void SwSubFont::DoOnCapitals( SwDoCapitals &rDo )
+{
+ OSL_ENSURE( pLastFont, "SwFont::DoOnCapitals: No LastFont?!" );
+
+ tools::Long nKana = 0;
+ const OUString aText( CalcCaseMap( rDo.GetInf().GetText() ) );
+ TextFrameIndex nMaxPos = std::min(
+ TextFrameIndex(rDo.GetInf().GetText().getLength()) - rDo.GetInf().GetIdx(),
+ rDo.GetInf().GetLen() );
+ rDo.GetInf().SetLen( nMaxPos );
+
+ const OUString oldText = rDo.GetInf().GetText();
+ rDo.GetInf().SetText( aText );
+ TextFrameIndex nPos = rDo.GetInf().GetIdx();
+ TextFrameIndex nOldPos = nPos;
+ nMaxPos = nMaxPos + nPos;
+
+ // Look if the length of the original text and the ToUpper-converted
+ // text is different. If yes, do special handling.
+ SwCapitalInfo aCapInf(oldText);
+ bool bCaseMapLengthDiffers(aText.getLength() != oldText.getLength());
+ if ( bCaseMapLengthDiffers )
+ rDo.SetCapInf( aCapInf );
+
+ SwFntObj *pOldLast = pLastFont;
+ std::unique_ptr<SwFntAccess> pBigFontAccess;
+ SwFntObj *pBigFont;
+ std::unique_ptr<SwFntAccess> pSpaceFontAccess;
+ SwFntObj *pSpaceFont = nullptr;
+
+ const void* nFontCacheId2 = nullptr;
+ sal_uInt16 nIndex2 = 0;
+ SwSubFont aFont( *this );
+ Point aStartPos( rDo.GetInf().GetPos() );
+
+ const bool bTextLines = aFont.GetUnderline() != LINESTYLE_NONE
+ || aFont.GetOverline() != LINESTYLE_NONE
+ || aFont.GetStrikeout() != STRIKEOUT_NONE;
+ const bool bWordWise = bTextLines && aFont.IsWordLineMode() &&
+ rDo.GetInf().GetDrawSpace();
+ const tools::Long nTmpKern = rDo.GetInf().GetKern();
+
+ if ( bTextLines )
+ {
+ if ( bWordWise )
+ {
+ aFont.SetWordLineMode( false );
+ pSpaceFontAccess.reset(new SwFntAccess( nFontCacheId2, nIndex2, &aFont,
+ rDo.GetInf().GetShell() ));
+ pSpaceFont = pSpaceFontAccess->Get();
+ }
+ else
+ pSpaceFont = pLastFont;
+
+ // Construct a font for the capitals:
+ aFont.SetUnderline( LINESTYLE_NONE );
+ aFont.SetOverline( LINESTYLE_NONE );
+ aFont.SetStrikeout( STRIKEOUT_NONE );
+ nFontCacheId2 = nullptr;
+ nIndex2 = 0;
+ pBigFontAccess.reset(new SwFntAccess( nFontCacheId2, nIndex2, &aFont,
+ rDo.GetInf().GetShell() ));
+ pBigFont = pBigFontAccess->Get();
+ }
+ else
+ pBigFont = pLastFont;
+
+ // Older LO versions had 66 as the small caps percentage size, later changed to 80,
+ // therefore a backwards compatibility option is kept (otherwise layout is changed).
+ // NOTE: There are more uses of SMALL_CAPS_PERCENTAGE in editeng, but it seems they
+ // do not matter for Writer (and if they did it'd be pretty ugly to propagate
+ // the option there).
+ int smallCapsPercentage = m_bSmallCapsPercentage66 ? 66 : SMALL_CAPS_PERCENTAGE;
+ aFont.SetProportion( (aFont.GetPropr() * smallCapsPercentage ) / 100 );
+ nFontCacheId2 = nullptr;
+ nIndex2 = 0;
+ std::unique_ptr<SwFntAccess> pSmallFontAccess( new SwFntAccess( nFontCacheId2, nIndex2, &aFont,
+ rDo.GetInf().GetShell() ));
+ SwFntObj *pSmallFont = pSmallFontAccess->Get();
+
+ rDo.Init( pBigFont, pSmallFont );
+ OutputDevice* pOutSize = pSmallFont->GetPrt();
+ if( !pOutSize )
+ pOutSize = &rDo.GetOut();
+ OutputDevice* pOldOut = &rDo.GetOut();
+
+ const LanguageType eLng = LANGUAGE_DONTKNOW == GetLanguage()
+ ? LANGUAGE_SYSTEM : GetLanguage();
+
+ if( nPos < nMaxPos )
+ {
+ nPos = TextFrameIndex(g_pBreakIt->GetBreakIter()->endOfCharBlock(
+ oldText, sal_Int32(nPos),
+ g_pBreakIt->GetLocale( eLng ), CharType::LOWERCASE_LETTER));
+ if (nPos < TextFrameIndex(0))
+ nPos = nOldPos;
+ else if( nPos > nMaxPos )
+ nPos = nMaxPos;
+ }
+
+ while( nOldPos < nMaxPos )
+ {
+
+ // The lower ones...
+ if( nOldPos != nPos )
+ {
+ SV_STAT( nGetTextSize );
+ pLastFont = pSmallFont;
+ pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() );
+
+ // #107816#, #i14820#
+ if( bCaseMapLengthDiffers )
+ {
+ // Build an own 'changed' string for the given part of the
+ // source string and use it. That new string may differ in length
+ // from the source string.
+ const OUString aNewText = CalcCaseMap(
+ oldText.copy(sal_Int32(nOldPos), sal_Int32(nPos-nOldPos)));
+ aCapInf.nIdx = nOldPos;
+ aCapInf.nLen = nPos - nOldPos;
+ rDo.GetInf().SetTextIdxLen(aNewText, TextFrameIndex(0), TextFrameIndex(aNewText.getLength()));
+ }
+ else
+ {
+ rDo.GetInf().SetIdxLen(nOldPos, nPos - nOldPos);
+ }
+
+ rDo.GetInf().SetUpper( false );
+ rDo.GetInf().SetOut( *pOutSize );
+ Size aPartSize = pSmallFont->GetTextSize( rDo.GetInf() );
+ nKana += rDo.GetInf().GetKanaDiff();
+ rDo.GetInf().SetOut( *pOldOut );
+ if( nTmpKern && nPos < nMaxPos )
+ aPartSize.AdjustWidth(nTmpKern );
+ rDo.GetInf().SetSize( aPartSize );
+ rDo.Do();
+ nOldPos = nPos;
+ }
+ nPos = TextFrameIndex(g_pBreakIt->GetBreakIter()->nextCharBlock(
+ oldText, sal_Int32(nPos),
+ g_pBreakIt->GetLocale( eLng ), CharType::LOWERCASE_LETTER));
+ if (nPos < TextFrameIndex(0) || nPos > nMaxPos)
+ nPos = nMaxPos;
+ OSL_ENSURE( nPos, "nextCharBlock not implemented?" );
+#if OSL_DEBUG_LEVEL > 1
+ if( !nPos )
+ nPos = nMaxPos;
+#endif
+ // The upper ones...
+ if( nOldPos != nPos )
+ {
+ const tools::Long nSpaceAdd = rDo.GetInf().GetSpace() / SPACING_PRECISION_FACTOR;
+
+ do
+ {
+ rDo.GetInf().SetUpper( true );
+ pLastFont = pBigFont;
+ pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() );
+ TextFrameIndex nTmp;
+ if( bWordWise )
+ {
+ nTmp = nOldPos;
+ while (nTmp < nPos && CH_BLANK == oldText[sal_Int32(nTmp)])
+ ++nTmp;
+ if( nOldPos < nTmp )
+ {
+ pLastFont = pSpaceFont;
+ pLastFont->SetDevFont( rDo.GetInf().GetShell(),
+ rDo.GetOut() );
+ static_cast<SwDoDrawCapital&>(rDo).DrawSpace( aStartPos );
+ pLastFont = pBigFont;
+ pLastFont->SetDevFont( rDo.GetInf().GetShell(),
+ rDo.GetOut() );
+
+ // #107816#, #i14820#
+ if( bCaseMapLengthDiffers )
+ {
+ // Build an own 'changed' string for the given part of the
+ // source string and use it. That new string may differ in length
+ // from the source string.
+ const OUString aNewText = CalcCaseMap(
+ oldText.copy(sal_Int32(nOldPos), sal_Int32(nTmp-nOldPos)));
+ aCapInf.nIdx = nOldPos;
+ aCapInf.nLen = nTmp - nOldPos;
+ rDo.GetInf().SetIdx(TextFrameIndex(0));
+ rDo.GetInf().SetLen(TextFrameIndex(aNewText.getLength()));
+ rDo.GetInf().SetText( aNewText );
+ }
+ else
+ {
+ rDo.GetInf().SetIdx( nOldPos );
+ rDo.GetInf().SetLen( nTmp - nOldPos );
+ }
+
+ rDo.GetInf().SetOut( *pOutSize );
+ Size aPartSize = pBigFont->GetTextSize( rDo.GetInf() );
+ nKana += rDo.GetInf().GetKanaDiff();
+ rDo.GetInf().SetOut( *pOldOut );
+ if( nSpaceAdd )
+ aPartSize.AdjustWidth(nSpaceAdd * sal_Int32(nTmp - nOldPos));
+ if( nTmpKern && nPos < nMaxPos )
+ aPartSize.AdjustWidth(nTmpKern );
+ rDo.GetInf().SetSize( aPartSize );
+ rDo.Do();
+ aStartPos = rDo.GetInf().GetPos();
+ nOldPos = nTmp;
+ }
+
+ while (nTmp < nPos && CH_BLANK != oldText[sal_Int32(nTmp)])
+ ++nTmp;
+ }
+ else
+ nTmp = nPos;
+ if( nTmp > nOldPos )
+ {
+ // #107816#, #i14820#
+ if( bCaseMapLengthDiffers )
+ {
+ // Build an own 'changed' string for the given part of the
+ // source string and use it. That new string may differ in length
+ // from the source string.
+ const OUString aNewText = CalcCaseMap(
+ oldText.copy(sal_Int32(nOldPos), sal_Int32(nTmp-nOldPos)));
+ aCapInf.nIdx = nOldPos;
+ aCapInf.nLen = nTmp - nOldPos;
+ rDo.GetInf().SetTextIdxLen( aNewText, TextFrameIndex(0), TextFrameIndex(aNewText.getLength()));
+ }
+ else
+ {
+ rDo.GetInf().SetIdxLen( nOldPos, nTmp - nOldPos );
+ }
+
+ rDo.GetInf().SetOut( *pOutSize );
+ Size aPartSize = pBigFont->GetTextSize( rDo.GetInf() );
+ nKana += rDo.GetInf().GetKanaDiff();
+ rDo.GetInf().SetOut( *pOldOut );
+ if( !bWordWise && rDo.GetInf().GetSpace() )
+ {
+ for (TextFrameIndex nI = nOldPos; nI < nPos; ++nI)
+ {
+ if (CH_BLANK == oldText[sal_Int32(nI)])
+ aPartSize.AdjustWidth(nSpaceAdd );
+ }
+ }
+ if( nTmpKern && nPos < nMaxPos )
+ aPartSize.AdjustWidth(nTmpKern );
+ rDo.GetInf().SetSize( aPartSize );
+ rDo.Do();
+ nOldPos = nTmp;
+ }
+ } while( nOldPos != nPos );
+ }
+ nPos = TextFrameIndex(g_pBreakIt->GetBreakIter()->endOfCharBlock(
+ oldText, sal_Int32(nPos),
+ g_pBreakIt->GetLocale( eLng ), CharType::LOWERCASE_LETTER));
+ if (nPos < TextFrameIndex(0) || nPos > nMaxPos)
+ nPos = nMaxPos;
+ OSL_ENSURE( nPos, "endOfCharBlock not implemented?" );
+#if OSL_DEBUG_LEVEL > 1
+ if( !nPos )
+ nPos = nMaxPos;
+#endif
+ }
+
+ // clean up:
+ if( pBigFont != pOldLast )
+ pBigFontAccess.reset();
+
+ if( bTextLines )
+ {
+ if( rDo.GetInf().GetDrawSpace() )
+ {
+ pLastFont = pSpaceFont;
+ pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() );
+ static_cast<SwDoDrawCapital&>( rDo ).DrawSpace( aStartPos );
+ }
+ if ( bWordWise )
+ pSpaceFontAccess.reset();
+ }
+ pLastFont = pOldLast;
+ pLastFont->SetDevFont( rDo.GetInf().GetShell(), rDo.GetOut() );
+
+ pSmallFontAccess.reset();
+ rDo.GetInf().SetText(oldText);
+ rDo.GetInf().SetKanaDiff( nKana );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/justify.cxx b/sw/source/core/txtnode/justify.cxx
new file mode 100644
index 000000000..1cbc16c36
--- /dev/null
+++ b/sw/source/core/txtnode/justify.cxx
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <vector>
+#include <sal/types.h>
+#include <swfont.hxx>
+#include "justify.hxx"
+
+namespace
+{
+enum class IdeographicPunctuationClass
+{
+ NONE,
+ OPEN_BRACKET,
+ CLOSE_BRACKET,
+ COMMA_OR_FULLSTOP
+};
+
+IdeographicPunctuationClass lcl_WhichPunctuationClass(sal_Unicode cChar)
+{
+ if ((cChar < 0x3001 || cChar > 0x3002) && (cChar < 0x3008 || cChar > 0x3011)
+ && (cChar < 0x3014 || cChar > 0x301F) && 0xFF62 != cChar && 0xFF63 != cChar)
+ return IdeographicPunctuationClass::NONE;
+ else if (0x3001 == cChar || 0x3002 == cChar)
+ return IdeographicPunctuationClass::COMMA_OR_FULLSTOP;
+ else if (0x3009 == cChar || 0x300B == cChar || 0x300D == cChar || 0x300F == cChar
+ || 0x3011 == cChar || 0x3015 == cChar || 0x3017 == cChar || 0x3019 == cChar
+ || 0x301B == cChar || 0x301E == cChar || 0x301F == cChar || 0xFF63 == cChar)
+ // right punctuation
+ return IdeographicPunctuationClass::CLOSE_BRACKET;
+
+ return IdeographicPunctuationClass::OPEN_BRACKET;
+}
+
+tools::Long lcl_MinGridWidth(tools::Long nGridWidth, tools::Long nCharWidth)
+{
+ tools::Long nCount = nCharWidth > nGridWidth ? (nCharWidth - 1) / nGridWidth + 1 : 1;
+ return nCount * nGridWidth;
+}
+
+tools::Long lcl_OffsetFromGridEdge(tools::Long nMinWidth, tools::Long nCharWidth, sal_Unicode cChar,
+ bool bForceLeft)
+{
+ if (bForceLeft)
+ return 0;
+
+ tools::Long nOffset = 0;
+
+ switch (lcl_WhichPunctuationClass(cChar))
+ {
+ case IdeographicPunctuationClass::NONE:
+ // Centered
+ nOffset = (nMinWidth - nCharWidth) / 2;
+ break;
+ case IdeographicPunctuationClass::OPEN_BRACKET:
+ // Align to next edge, closer to next ideograph
+ nOffset = nMinWidth - nCharWidth;
+ break;
+ default:
+ // CLOSE_BRACKET or COMMA_OR_FULLSTOP:
+ // Align to previous edge, closer to previous ideograph.
+ break;
+ }
+ return nOffset;
+}
+}
+
+namespace Justify
+{
+sal_Int32 GetModelPosition(const std::vector<sal_Int32>& rKernArray, sal_Int32 nLen, tools::Long nX)
+{
+ tools::Long nLeft = 0, nRight = 0;
+ sal_Int32 nLast = 0, nIdx = 0;
+
+ do
+ {
+ nRight = rKernArray[nLast];
+ ++nIdx;
+ while (nIdx < nLen && rKernArray[nIdx] == rKernArray[nLast])
+ ++nIdx;
+
+ if (nIdx < nLen)
+ {
+ if (nX < nRight)
+ return (nX - nLeft < nRight - nX) ? nLast : nIdx;
+
+ nLeft = nRight;
+ nLast = nIdx;
+ }
+ } while (nIdx < nLen);
+ return nIdx;
+}
+
+void SpaceDistribution(std::vector<sal_Int32>& rKernArray, const OUString& rText, sal_Int32 nStt,
+ sal_Int32 nLen, tools::Long nSpaceAdd, tools::Long nKern, bool bNoHalfSpace)
+{
+ assert(nStt + nLen <= rText.getLength());
+ assert(nLen <= sal_Int32(rKernArray.size()));
+ // nSpaceSum contains the sum of the intermediate space distributed
+ // among Spaces by the Justification.
+ // The Spaces themselves will be positioned in the middle of the
+ // intermediate space, hence the nSpace/2.
+ // In case of word-by-word underlining they have to be positioned
+ // at the beginning of the intermediate space, so that the space
+ // is not underlined.
+ // A Space at the beginning or end of the text must be positioned
+ // before (resp. after) the whole intermediate space, otherwise
+ // the underline/strike-through would have gaps.
+ tools::Long nSpaceSum = 0;
+ // in word line mode and for Arabic, we disable the half space trick:
+ const tools::Long nHalfSpace = bNoHalfSpace ? 0 : nSpaceAdd / 2;
+ const tools::Long nOtherHalf = nSpaceAdd - nHalfSpace;
+ tools::Long nKernSum = nKern;
+ sal_Unicode cChPrev = rText[nStt];
+
+ if (nSpaceAdd && (cChPrev == CH_BLANK))
+ nSpaceSum = nHalfSpace;
+
+ sal_Int32 nPrevIdx = 0;
+
+ for (sal_Int32 i = 1; i < nLen; ++i, nKernSum += nKern)
+ {
+ // Find the beginning of the next cluster that has a different kern value.
+ while (i < nLen && rKernArray[i] == rKernArray[nPrevIdx])
+ ++i;
+
+ if (i == nLen)
+ break;
+
+ sal_Unicode nCh = rText[nStt + i];
+
+ // Apply SpaceSum
+ if (cChPrev == CH_BLANK)
+ {
+ // no Pixel is lost:
+ nSpaceSum += nOtherHalf;
+ }
+
+ if (nCh == CH_BLANK)
+ {
+ if (i + 1 == nLen)
+ nSpaceSum += nSpaceAdd;
+ else
+ nSpaceSum += nHalfSpace;
+ }
+
+ cChPrev = nCh;
+ rKernArray[nPrevIdx] += nKernSum + nSpaceSum;
+ // In word line mode and for Arabic, we disabled the half space trick. If a portion
+ // ends with a blank, the full nSpaceAdd value has been added to the character in
+ // front of the blank. This leads to painting artifacts, therefore we remove the
+ // nSpaceAdd value again:
+ if (bNoHalfSpace && i + 1 == nLen && nCh == CH_BLANK)
+ rKernArray[nPrevIdx] = rKernArray[nPrevIdx] - nSpaceAdd;
+
+ // Advance nPrevIdx and assign kern values to previous cluster.
+ for (tools::Long nValue = rKernArray[nPrevIdx++]; nPrevIdx < i; ++nPrevIdx)
+ rKernArray[nPrevIdx] = nValue;
+ }
+
+ // the layout engine requires the total width of the output
+ while (nPrevIdx < nLen)
+ rKernArray[nPrevIdx++] += nKernSum + nSpaceSum;
+}
+
+tools::Long SnapToGrid(std::vector<sal_Int32>& rKernArray, const OUString& rText, sal_Int32 nStt,
+ sal_Int32 nLen, tools::Long nGridWidth, bool bForceLeft)
+{
+ assert(nStt + nLen <= rText.getLength());
+ assert(nLen <= sal_Int32(rKernArray.size()));
+
+ tools::Long nCharWidth = rKernArray[0];
+ tools::Long nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth);
+ tools::Long nDelta = lcl_OffsetFromGridEdge(nMinWidth, nCharWidth, rText[nStt], bForceLeft);
+ tools::Long nEdge = nMinWidth - nDelta;
+
+ sal_Int32 nLast = 0;
+
+ for (sal_Int32 i = 1; i < nLen; ++i)
+ {
+ if (rKernArray[i] == rKernArray[nLast])
+ continue;
+
+ nCharWidth = rKernArray[i] - rKernArray[nLast];
+ nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth);
+ tools::Long nX
+ = nEdge + lcl_OffsetFromGridEdge(nMinWidth, nCharWidth, rText[nStt + i], bForceLeft);
+ nEdge += nMinWidth;
+
+ while (nLast < i)
+ rKernArray[nLast++] = nX;
+ }
+
+ while (nLast < nLen)
+ rKernArray[nLast++] = nEdge;
+
+ return nDelta;
+}
+
+void SnapToGridEdge(std::vector<sal_Int32>& rKernArray, sal_Int32 nLen, tools::Long nGridWidth,
+ tools::Long nSpace, tools::Long nKern)
+{
+ assert(nLen <= sal_Int32(rKernArray.size()));
+
+ tools::Long nCharWidth = rKernArray[0];
+ tools::Long nEdge = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern) + nSpace;
+
+ sal_Int32 nLast = 0;
+
+ for (sal_Int32 i = 1; i < nLen; ++i)
+ {
+ if (rKernArray[i] == rKernArray[nLast])
+ continue;
+
+ nCharWidth = rKernArray[i] - rKernArray[nLast];
+ tools::Long nMinWidth = lcl_MinGridWidth(nGridWidth, nCharWidth + nKern);
+ while (nLast < i)
+ rKernArray[nLast++] = nEdge;
+
+ nEdge += nMinWidth + nSpace;
+ }
+
+ while (nLast < nLen)
+ rKernArray[nLast++] = nEdge;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/justify.hxx b/sw/source/core/txtnode/justify.hxx
new file mode 100644
index 000000000..6ea07adec
--- /dev/null
+++ b/sw/source/core/txtnode/justify.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+#include <sal/types.h>
+
+namespace Justify
+{
+/// Get model position base on given kern array.
+/// @param rKernArray text positions from OutDev::GetTextArray().
+/// @param nLen number of elements to process in rKernArray.
+/// @param nX the visual position
+SW_DLLPUBLIC sal_Int32 GetModelPosition(const std::vector<sal_Int32>& rKernArray, sal_Int32 nLen,
+ tools::Long nX);
+/// Distribute space between words and letters.
+/// @param[in,out] rKernArray text positions from OutDev::GetTextArray().
+/// @param rText string used to determine where space and kern are inserted.
+/// @param nStt starting index of rText.
+/// @param nLen number of elements to process in rKernArray and rText.
+/// @param nSpaceAdd amount of space to insert for each CH_BLANK.
+/// @param nKern amount of space to insert between letters.
+/// @param bNoHalfSpace whether to split the space into two halves.
+/// Split spaces are inserted before and after CH_BLANK.
+/// Set to true in word line mode and for Arabic text to avoid splitting.
+SW_DLLPUBLIC void SpaceDistribution(std::vector<sal_Int32>& rKernArray, const OUString& rText,
+ sal_Int32 nStt, sal_Int32 nLen, tools::Long nSpaceAdd,
+ tools::Long nKern, bool bNoHalfSpace);
+
+/// Snap ideographs to text grids:
+/// a) Ideographic open brackets are aligned to the rightmost edge of spanned grids so that
+// they can be closer to the next ideograph.
+/// b) Ideographic close brackets, ideographic comma, and ideographic fullstop are aligned
+/// to the leftmost edge of spanned grids so that they can be closer to the previous
+/// ideograph.
+/// c) Other ideographs are aligned to the center of the spanned grids.
+/// @param[in,out] rKernArray text positions from OutDev::GetTextArray().
+/// @param rText string used to determine where space and kern are inserted.
+/// @param nStt starting index of rText.
+/// @param nLen number of elements to process in rKernArray and rText.
+/// @param nGridWidth width of a text grid
+/// @param bForceLeft for align to the left edge of the grid disregard of the punctuation type.
+/// This is useful for calculate text width, line break, and conversion model position.
+/// @return the delta offset of first glyph so text origin can be updated accordingly.
+SW_DLLPUBLIC tools::Long SnapToGrid(std::vector<sal_Int32>& rKernArray, const OUString& rText,
+ sal_Int32 nStt, sal_Int32 nLen, tools::Long nGridWidth,
+ bool bForceLeft);
+
+/// Snap ideographs to text grids edge ( used when snap to char is off ):
+/// space will be distributed ( in case that alignment is set to justify. ).
+/// @param[in,out] rKernArray text positions from OutDev::GetTextArray().
+/// @param nLen number of elements to process in rKernArray and rText.
+/// @param nGridWidth width of a text grid
+/// @param nSpace amount of space distributed under justify text alignment mode.
+/// @param nKern letter spacing.
+SW_DLLPUBLIC void SnapToGridEdge(std::vector<sal_Int32>& rKernArray, sal_Int32 nLen,
+ tools::Long nGridWidth, tools::Long nSpace, tools::Long nKern);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/modeltoviewhelper.cxx b/sw/source/core/txtnode/modeltoviewhelper.cxx
new file mode 100644
index 000000000..306f58c2b
--- /dev/null
+++ b/sw/source/core/txtnode/modeltoviewhelper.cxx
@@ -0,0 +1,468 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tools/multisel.hxx>
+#include <doc.hxx>
+#include <IMark.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <fmtftn.hxx>
+#include <modeltoviewhelper.hxx>
+#include <ndtxt.hxx>
+#include <pam.hxx>
+#include <txatbase.hxx>
+#include <txtfld.hxx>
+#include <txtftn.hxx>
+#include <scriptinfo.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <bookmark.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <deque>
+#include <vector>
+
+namespace {
+
+struct FieldResult
+{
+ sal_Int32 m_nFieldPos;
+ OUString m_sExpand;
+ enum { NONE, FIELD, FOOTNOTE } m_eType;
+ explicit FieldResult(sal_Int32 const nPos)
+ : m_nFieldPos(nPos), m_eType(NONE)
+ { }
+};
+
+class sortfieldresults
+{
+public:
+ bool operator()(const FieldResult &rOne, const FieldResult &rTwo) const
+ {
+ return rOne.m_nFieldPos < rTwo.m_nFieldPos;
+ }
+};
+
+}
+
+namespace {
+
+struct block
+{
+ sal_Int32 m_nStart;
+ sal_Int32 m_nLen;
+ bool m_bVisible;
+ o3tl::sorted_vector<FieldResult, sortfieldresults> m_aAttrs;
+ block(sal_Int32 nStart, sal_Int32 nLen, bool bVisible)
+ : m_nStart(nStart), m_nLen(nLen), m_bVisible(bVisible)
+ {
+ }
+};
+
+struct containsPos
+{
+ const sal_Int32 m_nPos;
+ explicit containsPos(const sal_Int32 nPos)
+ : m_nPos(nPos)
+ {
+ }
+ bool operator() (const block& rIn) const
+ {
+ return m_nPos >= rIn.m_nStart && m_nPos < rIn.m_nStart + rIn.m_nLen;
+ }
+};
+
+}
+
+ModelToViewHelper::ModelToViewHelper(const SwTextNode &rNode,
+ SwRootFrame const*const pLayout, ExpandMode eMode)
+{
+ const OUString& rNodeText = rNode.GetText();
+ m_aRetText = rNodeText;
+
+ if (eMode == ExpandMode::PassThrough)
+ return;
+
+ Range aRange( 0, rNodeText.isEmpty() ? 0 : rNodeText.getLength() - 1);
+ MultiSelection aHiddenMulti(aRange);
+
+ if (eMode & ExpandMode::HideInvisible)
+ SwScriptInfo::selectHiddenTextProperty(rNode, aHiddenMulti, nullptr);
+
+ if (eMode & ExpandMode::HideDeletions)
+ SwScriptInfo::selectRedLineDeleted(rNode, aHiddenMulti);
+
+ if (eMode & ExpandMode::HideFieldmarkCommands)
+ {
+ // hide fieldmark commands
+ IDocumentMarkAccess const& rIDMA(*rNode.GetDoc().getIDocumentMarkAccess());
+ ::std::deque<::std::pair<sw::mark::IFieldmark const*, bool>> startedFields;
+ SwPaM cursor(rNode, 0);
+ while (true)
+ {
+ sw::mark::IFieldmark const* pFieldMark(nullptr);
+ while (true) // loop to skip NonTextFieldmarks, those are handled later
+ {
+ pFieldMark = rIDMA.getFieldmarkFor(*cursor.GetPoint());
+ if (pFieldMark == nullptr
+ || pFieldMark->GetMarkStart().nNode.GetNode().GetTextNode()->GetText()[
+ pFieldMark->GetMarkStart().nContent.GetIndex()]
+ != CH_TXT_ATR_FORMELEMENT)
+ {
+ break;
+ }
+ pFieldMark = nullptr;
+ if (!cursor.Move(fnMoveBackward, GoInContent))
+ {
+ break;
+ }
+ }
+ if (!pFieldMark)
+ {
+ break;
+ }
+ assert(pFieldMark->GetMarkStart().nNode.GetNode().GetTextNode()->GetText()[pFieldMark->GetMarkStart().nContent.GetIndex()] != CH_TXT_ATR_FORMELEMENT);
+ // getFieldmarkFor may also return one that starts at rNode,0 -
+ // skip it, must be handled in loop below
+ if (pFieldMark->GetMarkStart().nNode < rNode)
+ {
+ // this can be a nested field's end - skip over those!
+ if (pFieldMark->GetMarkEnd().nNode < rNode)
+ {
+ assert(cursor.GetPoint()->nNode.GetNode().GetTextNode()->GetText()[cursor.GetPoint()->nContent.GetIndex()] == CH_TXT_ATR_FIELDEND);
+ }
+ else
+ {
+ SwPosition const sepPos(::sw::mark::FindFieldSep(*pFieldMark));
+ startedFields.emplace_front(pFieldMark, sepPos.nNode < rNode);
+ }
+ *cursor.GetPoint() = pFieldMark->GetMarkStart();
+ }
+ if (!cursor.Move(fnMoveBackward, GoInContent))
+ {
+ break;
+ }
+ }
+ ::std::optional<sal_Int32> oStartHidden;
+ if (!::std::all_of(startedFields.begin(), startedFields.end(),
+ [](auto const& it) { return it.second; }))
+ {
+ oStartHidden.emplace(0); // node starts out hidden as field command
+ }
+ for (sal_Int32 i = 0; i < rNode.GetText().getLength(); ++i)
+ {
+ switch (rNode.GetText()[i])
+ {
+ case CH_TXT_ATR_FIELDSTART:
+ {
+ auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(const_cast<SwTextNode&>(rNode), i)));
+ assert(pFieldMark);
+ startedFields.emplace_back(pFieldMark, false);
+ if (!oStartHidden)
+ {
+ oStartHidden.emplace(i);
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDSEP:
+ {
+ assert(startedFields.back().first->IsCoveringPosition(SwPosition(const_cast<SwTextNode&>(rNode), i)));
+ startedFields.back().second = true;
+ assert(oStartHidden);
+ if (::std::all_of(startedFields.begin(), startedFields.end(),
+ [](auto const& it) { return it.second; }))
+ {
+// prevent -Werror=maybe-uninitialized under gcc 11.2.0
+#if defined __GNUC__ && !defined __clang_ && __GNUC__ >= 11 && __GNUC__ <= 12
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+ // i is still hidden but the Range end is oddly "-1"
+ aHiddenMulti.Select({*oStartHidden, i}, true);
+ oStartHidden.reset();
+#if defined __GNUC__ && !defined __clang_ && __GNUC__ >= 11 && __GNUC__ <= 12
+#pragma GCC diagnostic pop
+#endif
+ }
+ break;
+ }
+ case CH_TXT_ATR_FIELDEND:
+ {
+ assert(startedFields.back().first == rIDMA.getFieldmarkAt(SwPosition(const_cast<SwTextNode&>(rNode), i)));
+ startedFields.pop_back();
+ aHiddenMulti.Select({i, i}, true);
+ break;
+ }
+ }
+ }
+ if (oStartHidden && rNode.Len() != 0)
+ {
+ aHiddenMulti.Select({*oStartHidden, rNode.Len() - 1}, true);
+ }
+ }
+ else if (eMode & ExpandMode::ExpandFields) // subset: only hide dummy chars
+ {
+ for (sal_Int32 i = 0; i < rNode.GetText().getLength(); ++i)
+ {
+ switch (rNode.GetText()[i])
+ {
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ {
+ aHiddenMulti.Select({i, i}, true);
+ break;
+ }
+ }
+ }
+ }
+
+ std::vector<block> aBlocks;
+
+ sal_Int32 nShownStart = 0;
+ for (sal_Int32 i = 0; i < aHiddenMulti.GetRangeCount(); ++i)
+ {
+ const Range& rRange = aHiddenMulti.GetRange(i);
+ const sal_Int32 nHiddenStart = rRange.Min();
+ const sal_Int32 nHiddenEnd = rRange.Max() + 1;
+ const sal_Int32 nHiddenLen = nHiddenEnd - nHiddenStart;
+
+ const sal_Int32 nShownEnd = nHiddenStart;
+ const sal_Int32 nShownLen = nShownEnd - nShownStart;
+
+ if (nShownLen)
+ aBlocks.emplace_back(nShownStart, nShownLen, true);
+
+ if (nHiddenLen)
+ aBlocks.emplace_back(nHiddenStart, nHiddenLen, false);
+
+ nShownStart = nHiddenEnd;
+ }
+
+ sal_Int32 nTrailingShownLen = rNodeText.getLength() - nShownStart;
+ if (nTrailingShownLen)
+ aBlocks.emplace_back(nShownStart, nTrailingShownLen, true);
+
+ if (eMode & ExpandMode::ExpandFields || eMode & ExpandMode::ExpandFootnote)
+ {
+ //first the normal fields, get their position in the node and what the text they expand
+ //to is
+ const SwpHints* pSwpHints2 = rNode.GetpSwpHints();
+ for ( size_t i = 0; pSwpHints2 && i < pSwpHints2->Count(); ++i )
+ {
+ const SwTextAttr* pAttr = pSwpHints2->Get(i);
+ if (pAttr->HasDummyChar())
+ {
+ const sal_Int32 nDummyCharPos = pAttr->GetStart();
+ if (aHiddenMulti.IsSelected(nDummyCharPos))
+ continue;
+ std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(),
+ aBlocks.end(), containsPos(nDummyCharPos));
+ if (aFind != aBlocks.end())
+ {
+ FieldResult aFieldResult(nDummyCharPos);
+ switch (pAttr->Which())
+ {
+ case RES_TXTATR_ANNOTATION:
+ if (eMode & ExpandMode::ExpandFields)
+ {
+ // this uses CH_TXTATR_INWORD so replace with nothing
+ aFieldResult.m_eType = FieldResult::FIELD;
+ }
+ break;
+ case RES_TXTATR_FIELD:
+ if (eMode & ExpandMode::ExpandFields)
+ {
+ // add a ZWSP before the expanded field in replace mode
+ aFieldResult.m_sExpand = ((eMode & ExpandMode::ReplaceMode)
+ ? OUString(CHAR_ZWSP) : OUString("")) +
+ static_txtattr_cast<SwTextField const*>(pAttr)->
+ GetFormatField().GetField()->ExpandField(true, pLayout);
+ aFieldResult.m_eType = FieldResult::FIELD;
+ }
+ break;
+ case RES_TXTATR_FTN:
+ if (eMode & ExpandMode::ExpandFootnote)
+ {
+ const SwFormatFootnote& rFootnote = static_cast<SwTextFootnote const*>(pAttr)->GetFootnote();
+ const SwDoc& rDoc = rNode.GetDoc();
+ aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
+ ? OUString(CHAR_ZWSP)
+ : rFootnote.GetViewNumStr(rDoc, pLayout);
+ aFieldResult.m_eType = FieldResult::FOOTNOTE;
+ }
+ break;
+ default:
+ break;
+ }
+ aFind->m_aAttrs.insert(aFieldResult);
+ }
+ }
+ }
+
+ if (eMode & ExpandMode::ExpandFields)
+ {
+ //now get the dropdown formfields, get their position in the node and what the text they expand
+ //to is
+ SwPaM aPaM(rNode, 0, rNode, rNode.Len());
+ std::vector<sw::mark::IFieldmark*> aNoTextFieldmarks =
+ rNode.GetDoc().getIDocumentMarkAccess()->getNoTextFieldmarksIn(aPaM);
+
+ for (sw::mark::IFieldmark *const pMark : aNoTextFieldmarks)
+ {
+ const sal_Int32 nDummyCharPos = pMark->GetMarkStart().nContent.GetIndex();
+ if (aHiddenMulti.IsSelected(nDummyCharPos))
+ continue;
+ std::vector<block>::iterator aFind = std::find_if(aBlocks.begin(), aBlocks.end(),
+ containsPos(nDummyCharPos));
+ if (aFind != aBlocks.end())
+ {
+ FieldResult aFieldResult(nDummyCharPos);
+ aFieldResult.m_sExpand = (eMode & ExpandMode::ReplaceMode)
+ ? OUString(CHAR_ZWSP)
+ : sw::mark::ExpandFieldmark(pMark);
+ aFieldResult.m_eType = FieldResult::FIELD;
+ aFind->m_aAttrs.insert(aFieldResult);
+ }
+ }
+ }
+ }
+
+ //store the end of each range in the model and where that end of range
+ //maps to in the view
+ sal_Int32 nOffset = 0;
+ for (const auto& rBlock : aBlocks)
+ {
+ const sal_Int32 nBlockLen = rBlock.m_nLen;
+ if (!nBlockLen)
+ continue;
+ const sal_Int32 nBlockStart = rBlock.m_nStart;
+ const sal_Int32 nBlockEnd = nBlockStart + nBlockLen;
+
+ if (!rBlock.m_bVisible)
+ {
+ sal_Int32 const modelBlockPos(nBlockEnd);
+ sal_Int32 const viewBlockPos(nBlockStart + nOffset);
+ m_aMap.emplace_back(modelBlockPos, viewBlockPos, false);
+
+ m_aRetText = m_aRetText.replaceAt(nOffset + nBlockStart, nBlockLen, u"");
+ nOffset -= nBlockLen;
+ }
+ else
+ {
+ for (const auto& rAttr : rBlock.m_aAttrs)
+ {
+ sal_Int32 const modelFieldPos(rAttr.m_nFieldPos);
+ sal_Int32 const viewFieldPos(rAttr.m_nFieldPos + nOffset);
+ m_aMap.emplace_back(modelFieldPos, viewFieldPos, true );
+
+ m_aRetText = m_aRetText.replaceAt(viewFieldPos, 1, rAttr.m_sExpand);
+ nOffset += rAttr.m_sExpand.getLength() - 1;
+
+ switch (rAttr.m_eType)
+ {
+ case FieldResult::FIELD:
+ m_FieldPositions.push_back(viewFieldPos);
+ break;
+ case FieldResult::FOOTNOTE:
+ m_FootnotePositions.push_back(viewFieldPos);
+ break;
+ case FieldResult::NONE: /*ignore*/
+ break;
+ }
+ }
+
+ sal_Int32 const modelEndBlock(nBlockEnd);
+ sal_Int32 const viewFieldPos(nBlockEnd + nOffset);
+ m_aMap.emplace_back(modelEndBlock, viewFieldPos, true);
+ }
+ }
+}
+
+/** Converts a model position into a view position
+*/
+sal_Int32 ModelToViewHelper::ConvertToViewPosition( sal_Int32 nModelPos ) const
+{
+ // Search for entry after nPos:
+ auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [nModelPos](const ConversionMapEntry& rEntry) { return rEntry.m_nModelPos >= nModelPos; });
+ if (aIter != m_aMap.end())
+ {
+ //if it's an invisible portion, map all contained positions
+ //to the anchor viewpos
+ if (!aIter->m_bVisible)
+ return aIter->m_nViewPos;
+
+ //if it's a visible portion, then the view position is the anchor
+ //viewpos - the offset of the input modelpos from the anchor
+ //modelpos
+ const sal_Int32 nOffsetFromEnd = aIter->m_nModelPos - nModelPos;
+ return aIter->m_nViewPos - nOffsetFromEnd;
+ }
+
+ return nModelPos;
+}
+
+/** Converts a view position into a model position
+*/
+ModelToViewHelper::ModelPosition ModelToViewHelper::ConvertToModelPosition( sal_Int32 nViewPos ) const
+{
+ ModelPosition aRet;
+ aRet.mnPos = nViewPos;
+
+ // Search for entry after nPos:
+ auto aIter = std::find_if(m_aMap.begin(), m_aMap.end(),
+ [nViewPos](const ConversionMapEntry& rEntry) { return rEntry.m_nViewPos > nViewPos; });
+
+ // If nViewPos is in front of first field, we are finished.
+ if (aIter != m_aMap.end() && aIter != m_aMap.begin())
+ {
+ const sal_Int32 nPosModel = aIter->m_nModelPos;
+ const sal_Int32 nPosExpand = aIter->m_nViewPos;
+
+ --aIter;
+
+ // nPrevPosModel is the field position
+ const sal_Int32 nPrevPosModel = aIter->m_nModelPos;
+ const sal_Int32 nPrevPosExpand = aIter->m_nViewPos;
+
+ const sal_Int32 nLengthModel = nPosModel - nPrevPosModel;
+ const sal_Int32 nLengthExpand = nPosExpand - nPrevPosExpand;
+
+ const sal_Int32 nFieldLengthExpand = nLengthExpand - nLengthModel + 1;
+ const sal_Int32 nFieldEndExpand = nPrevPosExpand + nFieldLengthExpand;
+
+ // Check if nPos is outside of field:
+ if ( nFieldEndExpand <= nViewPos )
+ {
+ // nPos is outside of field:
+ const sal_Int32 nDistToField = nViewPos - nFieldEndExpand + 1;
+ aRet.mnPos = nPrevPosModel + nDistToField;
+ }
+ else
+ {
+ // nViewPos is inside a field:
+ aRet.mnPos = nPrevPosModel;
+ aRet.mnSubPos = nViewPos - nPrevPosExpand;
+ aRet.mbIsField = true;
+ }
+ }
+
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/ndhints.cxx b/sw/source/core/txtnode/ndhints.cxx
new file mode 100644
index 000000000..167d5aee4
--- /dev/null
+++ b/sw/source/core/txtnode/ndhints.cxx
@@ -0,0 +1,492 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/rsiditem.hxx>
+#include <sal/log.hxx>
+#include <txatbase.hxx>
+#include <ndhints.hxx>
+#include <txtatr.hxx>
+
+#ifdef DBG_UTIL
+#include <pam.hxx>
+#include <fmtautofmt.hxx>
+#endif
+
+/// sort order: Start, End (reverse), Which (reverse),
+/// (char style: sort number), at last the pointer
+static bool CompareSwpHtStart( const SwTextAttr* lhs, const SwTextAttr* rhs )
+{
+ const SwTextAttr &rHt1 = *lhs;
+ const SwTextAttr &rHt2 = *rhs;
+ if ( rHt1.GetStart() == rHt2.GetStart() )
+ {
+ const sal_Int32 nHt1 = rHt1.GetAnyEnd();
+ const sal_Int32 nHt2 = rHt2.GetAnyEnd();
+ if ( nHt1 == nHt2 )
+ {
+ const sal_uInt16 nWhich1 = rHt1.Which();
+ const sal_uInt16 nWhich2 = rHt2.Which();
+ if ( nWhich1 == nWhich2 )
+ {
+ if ( RES_TXTATR_CHARFMT == nWhich1 )
+ {
+ const sal_uInt16 nS1 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
+ const sal_uInt16 nS2 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
+ if ( nS1 != nS2 ) // robust
+ return nS1 < nS2;
+ }
+
+ return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
+ }
+ // order is important! for requirements see hintids.hxx
+ return ( nWhich1 > nWhich2 );
+ }
+ return ( nHt1 > nHt2 );
+ }
+ return ( rHt1.GetStart() < rHt2.GetStart() );
+}
+
+/// sort order: Which, Start, End(reverse) at last the pointer
+bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const sal_uInt16 nWhich ) const
+{
+ return lhs->Which() < nWhich;
+}
+bool CompareSwpHtWhichStart::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
+{
+ const SwTextAttr &rHt1 = *lhs;
+ const SwTextAttr &rHt2 = *rhs;
+ const sal_uInt16 nWhich1 = rHt1.Which();
+ const sal_uInt16 nWhich2 = rHt2.Which();
+ if ( nWhich1 < nWhich2 )
+ return true;
+ if ( nWhich1 > nWhich2 )
+ return false;
+ if (rHt1.GetStart() < rHt2.GetStart())
+ return true;
+ if (rHt1.GetStart() > rHt2.GetStart())
+ return false;
+ if ( RES_TXTATR_CHARFMT == nWhich1 )
+ {
+ const sal_uInt16 nS1 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
+ const sal_uInt16 nS2 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
+ if ( nS1 != nS2 ) // robust
+ return nS1 < nS2;
+ }
+ const sal_Int32 nEnd1 = rHt1.GetAnyEnd();
+ const sal_Int32 nEnd2 = rHt2.GetAnyEnd();
+ if ( nEnd1 > nEnd2 )
+ return true;
+ if ( nEnd1 < nEnd2 )
+ return false;
+ return reinterpret_cast<sal_IntPtr>(&rHt1) < reinterpret_cast<sal_IntPtr>(&rHt2);
+}
+
+/// sort order: End, Start(reverse), Which
+/// (char style: sort number), at last the pointer(reverse)
+bool CompareSwpHtEnd::operator()( sal_Int32 nEndPos, const SwTextAttr* rhs ) const
+{
+ return nEndPos < rhs->GetAnyEnd();
+}
+bool CompareSwpHtEnd::operator()( const SwTextAttr* lhs, const SwTextAttr* rhs ) const
+{
+ const SwTextAttr &rHt1 = *lhs;
+ const SwTextAttr &rHt2 = *rhs;
+ const sal_Int32 nHt1 = rHt1.GetAnyEnd();
+ const sal_Int32 nHt2 = rHt2.GetAnyEnd();
+ if ( nHt1 == nHt2 )
+ {
+ if ( rHt1.GetStart() == rHt2.GetStart() )
+ {
+ const sal_uInt16 nWhich1 = rHt1.Which();
+ const sal_uInt16 nWhich2 = rHt2.Which();
+ if ( nWhich1 == nWhich2 )
+ {
+ if ( RES_TXTATR_CHARFMT == nWhich1 )
+ {
+ const sal_uInt16 nS1 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt1).GetSortNumber();
+ const sal_uInt16 nS2 =
+ static_txtattr_cast<const SwTextCharFormat&>(rHt2).GetSortNumber();
+ if ( nS1 != nS2 ) // robust
+ return nS1 > nS2;
+ }
+
+ return reinterpret_cast<sal_IntPtr>(&rHt1) > reinterpret_cast<sal_IntPtr>(&rHt2);
+ }
+ // order is important! for requirements see hintids.hxx
+ return ( nWhich1 < nWhich2 );
+ }
+ else
+ return ( rHt1.GetStart() > rHt2.GetStart() );
+ }
+ return ( nHt1 < nHt2 );
+}
+
+void SwpHints::Insert(SwTextAttr* pHt)
+{
+ assert(std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
+ == m_HintsByStart.end()); // "Insert: hint already in HtStart"
+ assert( pHt->m_pHints == nullptr );
+ pHt->m_pHints = this;
+
+ if (m_bStartMapNeedsSorting)
+ ResortStartMap();
+ if (m_bEndMapNeedsSorting)
+ ResortEndMap();
+ if (m_bWhichMapNeedsSorting)
+ ResortWhichMap();
+
+ auto it1 = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), pHt, CompareSwpHtStart);
+ m_HintsByStart.insert(it1, pHt);
+
+ auto it2 = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
+ m_HintsByEnd.insert(it2, pHt);
+
+ auto it3 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
+ m_HintsByWhichAndStart.insert(it3, pHt);
+}
+
+bool SwpHints::Contains( const SwTextAttr *pHt ) const
+{
+ // DO NOT use find() or CHECK here!
+ // if called from SwTextNode::InsertItem, pHt has already been deleted,
+ // so it cannot be dereferenced
+ return std::find(m_HintsByStart.begin(), m_HintsByStart.end(), pHt)
+ != m_HintsByStart.end();
+}
+
+#ifdef DBG_UTIL
+
+#define CHECK_ERR(cond, text) \
+ if(!(cond)) \
+ { \
+ SAL_WARN("sw.core", text); \
+ Resort(); \
+ return false; \
+ }
+
+bool SwpHints::Check(bool bPortionsMerged) const
+{
+ // 1) both arrays have same size
+ CHECK_ERR( m_HintsByStart.size() == m_HintsByEnd.size(),
+ "HintsCheck: wrong sizes" );
+ sal_Int32 nLastStart = 0;
+ sal_Int32 nLastEnd = 0;
+
+ const SwTextAttr *pLastStart = nullptr;
+ const SwTextAttr *pLastEnd = nullptr;
+ o3tl::sorted_vector<SwTextAttr const*> RsidOnlyAutoFormats;
+ if (bPortionsMerged)
+ {
+ for (size_t i = 0; i < Count(); ++i)
+ {
+ SwTextAttr const*const pHint(m_HintsByStart[i]);
+ if (RES_TXTATR_AUTOFMT == pHint->Which())
+ {
+ std::shared_ptr<SfxItemSet> const & pSet(
+ pHint->GetAutoFormat().GetStyleHandle());
+ if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
+ {
+ RsidOnlyAutoFormats.insert(pHint);
+ }
+ }
+ }
+ }
+
+ for( size_t i = 0; i < Count(); ++i )
+ {
+ // --- check Starts ---
+
+ // 2a) valid pointer? depends on overwriting freed mem with 0xFF
+ const SwTextAttr *pHt = m_HintsByStart[i];
+ CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHt), "HintsCheck: start ptr was deleted" );
+
+ // 3a) start sort order?
+ sal_Int32 nIdx = pHt->GetStart();
+ CHECK_ERR( nIdx >= nLastStart, "HintsCheck: starts are unsorted" );
+
+ // 4a) IsLessStart consistency
+ if( pLastStart )
+ CHECK_ERR( CompareSwpHtStart( pLastStart, pHt ), "HintsCheck: IsLastStart" );
+
+ nLastStart = nIdx;
+ pLastStart = pHt;
+
+ // --- check Ends ---
+
+ // 2b) valid pointer? see DELETEFF
+ const SwTextAttr *pHtEnd = m_HintsByEnd[i];
+ CHECK_ERR( 0xFF != *reinterpret_cast<unsigned char const *>(pHtEnd), "HintsCheck: end ptr was deleted" );
+
+ // 3b) end sort order?
+ nIdx = pHtEnd->GetAnyEnd();
+ CHECK_ERR( nIdx >= nLastEnd, "HintsCheck: ends are unsorted" );
+
+ // 4b) IsLessEnd consistency
+ if( pLastEnd )
+ CHECK_ERR( CompareSwpHtEnd()( pLastEnd, pHtEnd ), "HintsCheck: IsLastEnd" );
+
+ nLastEnd = nIdx;
+ pLastEnd = pHtEnd;
+
+ // --- cross checks ---
+
+ // 5) same pointers in both arrays
+ if (std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart) == m_HintsByStart.end())
+ nIdx = COMPLETE_STRING;
+
+ CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetStartOf" );
+
+ // 6) same pointers in both arrays
+ if (std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtEnd()) == m_HintsByEnd.end())
+ nIdx = COMPLETE_STRING;
+
+ CHECK_ERR( COMPLETE_STRING != nIdx, "HintsCheck: no GetEndOf" );
+
+ // 7a) character attributes in array?
+ sal_uInt16 nWhich = pHt->Which();
+ CHECK_ERR( !isCHRATR(nWhich),
+ "HintsCheck: Character attribute in start array" );
+
+ // 7b) character attributes in array?
+ nWhich = pHtEnd->Which();
+ CHECK_ERR( !isCHRATR(nWhich),
+ "HintsCheck: Character attribute in end array" );
+
+ // 8) style portion check
+ const SwTextAttr* pHtThis = m_HintsByStart[i];
+ const SwTextAttr* pHtLast = i > 0 ? m_HintsByStart[i-1] : nullptr;
+ CHECK_ERR( (0 == i)
+ || ( (RES_TXTATR_CHARFMT != pHtLast->Which())
+ && (RES_TXTATR_AUTOFMT != pHtLast->Which()))
+ || ( (RES_TXTATR_CHARFMT != pHtThis->Which())
+ && (RES_TXTATR_AUTOFMT != pHtThis->Which()))
+ || (pHtThis->GetStart() >= *pHtLast->End()) // no overlap
+ || ( ( (pHtThis->GetStart() == pHtLast->GetStart())
+ && (*pHtThis->End() == *pHtLast->End())
+ ) // same range
+ && ( (pHtThis->Which() != RES_TXTATR_AUTOFMT)
+ || (pHtLast->Which() != RES_TXTATR_AUTOFMT)
+ ) // never two AUTOFMT on same range
+ && ( (pHtThis->Which() != RES_TXTATR_CHARFMT)
+ || (pHtLast->Which() != RES_TXTATR_CHARFMT)
+ || (static_txtattr_cast<const SwTextCharFormat *>(pHtThis)
+ ->GetSortNumber() !=
+ static_txtattr_cast<const SwTextCharFormat *>(pHtLast)
+ ->GetSortNumber())
+ ) // multiple CHARFMT on same range need distinct sorter
+ )
+ || (pHtThis->GetStart() == *pHtThis->End()), // this empty
+ "HintsCheck: Portion inconsistency. "
+ "This can be temporarily ok during undo operations" );
+
+ // 8 1/2) format ignore start/end flag check
+ // (problems because MergePortions buggy or not called)
+ if (bPortionsMerged)
+ {
+ if (RES_TXTATR_AUTOFMT == pHt->Which() ||
+ RES_TXTATR_CHARFMT == pHt->Which())
+ {
+ // mostly ignore the annoying no-length hints
+ // BuildPortions inserts these in the middle of an existing one
+ bool const bNoLength(pHt->GetStart() == *pHt->End());
+ bool bNeedContinuation(!bNoLength && pHt->IsFormatIgnoreEnd());
+ bool bForbidContinuation(!bNoLength && !bNeedContinuation);
+ if (RES_TXTATR_AUTOFMT == pHt->Which())
+ {
+ if (RsidOnlyAutoFormats.find(pHt) != RsidOnlyAutoFormats.end())
+ {
+ assert(pHt->IsFormatIgnoreStart());
+ bNeedContinuation = false;
+ // don't forbid continuation - may be other hint here!
+ }
+ }
+ if (bNeedContinuation || bForbidContinuation)
+ {
+ bool bFound(false);
+ for (size_t j = i + 1; j < Count(); ++j)
+ {
+ SwTextAttr *const pOther(m_HintsByStart[j]);
+ if (pOther->GetStart() > *pHt->End())
+ {
+ break; // done
+ }
+ else if (pOther->GetStart() == pOther->GetAnyEnd())
+ {
+ continue; // empty hint: ignore
+ }
+ else if (pOther->GetStart() == *pHt->End())
+ {
+ if (RES_TXTATR_AUTOFMT == pOther->Which() ||
+ RES_TXTATR_CHARFMT == pOther->Which())
+ { // multiple charfmt on same range must all match
+ if (bNeedContinuation)
+ {
+ assert(pOther->IsFormatIgnoreStart());
+ bFound = true;
+ }
+ else if (bForbidContinuation &&
+ (RsidOnlyAutoFormats.find(pOther) ==
+ RsidOnlyAutoFormats.end()))
+ {
+ assert(!pOther->IsFormatIgnoreStart());
+ }
+ }
+ }
+ }
+ if (bNeedContinuation)
+ {
+ assert(bFound); // ? can this happen temp. during undo?
+ }
+ }
+ }
+ else
+ {
+ assert(!pHt->IsFormatIgnoreStart());
+ assert(!pHt->IsFormatIgnoreEnd());
+ }
+ }
+
+ // 9) nesting portion check
+ if (pHtThis->IsNesting())
+ {
+ for (size_t j = 0; j < i; ++j)
+ {
+ SwTextAttr const * const pOther( m_HintsByStart[j] );
+ if (pOther->IsNesting())
+ {
+ SwComparePosition cmp = ComparePosition(
+ pHtThis->GetStart(), *pHtThis->End(),
+ pOther->GetStart(), *pOther->End());
+ CHECK_ERR( (SwComparePosition::OverlapBefore != cmp) &&
+ (SwComparePosition::OverlapBehind != cmp),
+ "HintsCheck: overlapping nesting hints!!!" );
+ }
+ }
+ }
+
+ // 10) dummy char check (unfortunately cannot check SwTextNode::m_Text)
+ if (pHtThis->HasDummyChar())
+ {
+ for ( size_t j = 0; j < i; ++j )
+ {
+ SwTextAttr const * const pOther( m_HintsByStart[j] );
+ if (pOther->HasDummyChar())
+ {
+ CHECK_ERR( (pOther->GetStart() != pHtThis->GetStart()),
+ "HintsCheck: multiple hints claim same CH_TXTATR!");
+ }
+ }
+ }
+ }
+ return true;
+}
+
+#endif /* DBG_UTIL */
+
+// Resort() is called before every Insert and Delete.
+// Various SwTextNode methods modify hints in a way that violates the
+// sort order of the m_HintsByStart, m_HintsByEnd arrays, so this method is needed
+// to restore the order.
+
+void SwpHints::Resort() const
+{
+ if (m_bStartMapNeedsSorting)
+ {
+ auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
+ std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
+ m_bStartMapNeedsSorting = false;
+ }
+ if (m_bEndMapNeedsSorting)
+ {
+ auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
+ std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
+ m_bEndMapNeedsSorting = false;
+ }
+ if (m_bWhichMapNeedsSorting)
+ {
+ auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
+ std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
+ m_bWhichMapNeedsSorting = false;
+ }
+}
+
+void SwpHints::ResortStartMap() const
+{
+ if (m_bStartMapNeedsSorting)
+ {
+ auto & rStartMap = const_cast<SwpHints*>(this)->m_HintsByStart;
+ std::sort(rStartMap.begin(), rStartMap.end(), CompareSwpHtStart);
+ m_bStartMapNeedsSorting = false;
+ }
+}
+
+void SwpHints::ResortEndMap() const
+{
+ if (m_bEndMapNeedsSorting)
+ {
+ auto & rEndMap = const_cast<SwpHints*>(this)->m_HintsByEnd;
+ std::sort(rEndMap.begin(), rEndMap.end(), CompareSwpHtEnd());
+ m_bEndMapNeedsSorting = false;
+ }
+}
+
+void SwpHints::ResortWhichMap() const
+{
+ if (m_bWhichMapNeedsSorting)
+ {
+ auto & rWhichStartMap = const_cast<SwpHints*>(this)->m_HintsByWhichAndStart;
+ std::sort(rWhichStartMap.begin(), rWhichStartMap.end(), CompareSwpHtWhichStart());
+ m_bWhichMapNeedsSorting = false;
+ }
+}
+
+size_t SwpHints::GetFirstPosSortedByWhichAndStart( sal_uInt16 nWhich ) const
+{
+ if (m_bWhichMapNeedsSorting)
+ ResortWhichMap();
+ auto it = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), nWhich, CompareSwpHtWhichStart());
+ if ( it == m_HintsByWhichAndStart.end() )
+ return SAL_MAX_SIZE;
+ return it - m_HintsByWhichAndStart.begin();
+}
+
+int SwpHints::GetLastPosSortedByEnd( sal_Int32 nEndPos ) const
+{
+ if (m_bEndMapNeedsSorting)
+ ResortEndMap();
+ auto it = std::upper_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), nEndPos, CompareSwpHtEnd());
+ return it - m_HintsByEnd.begin() - 1;
+}
+
+size_t SwpHints::GetIndexOf( const SwTextAttr *pHt ) const
+{
+ if (m_bStartMapNeedsSorting)
+ ResortStartMap();
+ auto it = std::lower_bound(m_HintsByStart.begin(), m_HintsByStart.end(), const_cast<SwTextAttr*>(pHt), CompareSwpHtStart);
+ if ( it == m_HintsByStart.end() || *it != pHt )
+ return SAL_MAX_SIZE;
+ return it - m_HintsByStart.begin();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx
new file mode 100644
index 000000000..121f4e6e9
--- /dev/null
+++ b/sw/source/core/txtnode/ndtxt.cxx
@@ -0,0 +1,5436 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <hints.hxx>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/rsiditem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <anchoredobject.hxx>
+#include <txtfld.hxx>
+#include <txtinet.hxx>
+#include <fmtanchr.hxx>
+#include <fmtinfmt.hxx>
+#include <fmtrfmrk.hxx>
+#include <txttxmrk.hxx>
+#include <fchrfmt.hxx>
+#include <txtftn.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtfld.hxx>
+#include <frmatr.hxx>
+#include <ftnidx.hxx>
+#include <ftninfo.hxx>
+#include <fmtftn.hxx>
+#include <charfmt.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <IDocumentListsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <pam.hxx>
+#include <fldbas.hxx>
+#include <paratr.hxx>
+#include <txtfrm.hxx>
+#include <ftnfrm.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <expfld.hxx>
+#include <section.hxx>
+#include <mvsave.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <redline.hxx>
+#include <IMark.hxx>
+#include <scriptinfo.hxx>
+#include <istyleaccess.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <numrule.hxx>
+#include <docsh.hxx>
+#include <SwNodeNum.hxx>
+#include <svl/grabbagitem.hxx>
+#include <svl/intitem.hxx>
+#include <sortedobjs.hxx>
+#include <calbck.hxx>
+#include <attrhint.hxx>
+#include <memory>
+#include <unoparagraph.hxx>
+#include <wrtsh.hxx>
+#include <fmtpdsc.hxx>
+#include <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <svl/itemiter.hxx>
+
+using namespace ::com::sun::star;
+
+typedef std::vector<SwTextAttr*> SwpHts;
+
+
+// unfortunately everyone can change Hints without ensuring order or the linking between them
+#ifdef DBG_UTIL
+#define CHECK_SWPHINTS(pNd) { if( pNd->GetpSwpHints() && \
+ !pNd->GetDoc().IsInReading() ) \
+ pNd->GetpSwpHints()->Check(true); }
+#define CHECK_SWPHINTS_IF_FRM(pNd) { if( pNd->GetpSwpHints() && \
+ !pNd->GetDoc().IsInReading() ) \
+ pNd->GetpSwpHints()->Check(getLayoutFrame(nullptr, nullptr, nullptr) != nullptr); }
+#else
+#define CHECK_SWPHINTS(pNd)
+#define CHECK_SWPHINTS_IF_FRM(pNd)
+#endif
+
+SwTextNode *SwNodes::MakeTextNode( const SwNodeIndex & rWhere,
+ SwTextFormatColl *pColl, bool const bNewFrames)
+{
+ OSL_ENSURE( pColl, "Collection pointer is 0." );
+
+ SwTextNode *pNode = new SwTextNode( rWhere, pColl, nullptr );
+
+ SwNodeIndex aIdx( *pNode );
+
+ // if there is no layout or it is in a hidden section, MakeFrames is not needed
+ const SwSectionNode* pSectNd;
+ if (!bNewFrames ||
+ !GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell() ||
+ ( nullptr != (pSectNd = pNode->FindSectionNode()) &&
+ pSectNd->GetSection().IsHiddenFlag() ))
+ return pNode;
+
+ SwNodeIndex aTmp( rWhere );
+ do {
+ // max. 2 loops:
+ // 1. take the successor
+ // 2. take the predecessor
+
+ SwNode * pNd = & aTmp.GetNode();
+ switch (pNd->GetNodeType())
+ {
+ case SwNodeType::Table:
+ static_cast<SwTableNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
+ return pNode;
+
+ case SwNodeType::Section:
+ if( static_cast<SwSectionNode*>(pNd)->GetSection().IsHidden() ||
+ static_cast<SwSectionNode*>(pNd)->IsContentHidden() )
+ {
+ SwNodeIndex aTmpIdx( *pNode );
+ pNd = FindPrvNxtFrameNode( aTmpIdx, pNode );
+ if( !pNd )
+ return pNode;
+ aTmp = *pNd;
+ break;
+ }
+ static_cast<SwSectionNode*>(pNd)->MakeFramesForAdjacentContentNode(aIdx);
+ return pNode;
+
+ case SwNodeType::Text:
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ static_cast<SwContentNode*>(pNd)->MakeFramesForAdjacentContentNode(*pNode);
+ return pNode;
+
+ case SwNodeType::End:
+ if( pNd->StartOfSectionNode()->IsSectionNode() &&
+ aTmp.GetIndex() < rWhere.GetIndex() )
+ {
+ if( pNd->StartOfSectionNode()->GetSectionNode()->GetSection().IsHiddenFlag())
+ {
+ if( !GoPrevSection( &aTmp, true, false ) ||
+ aTmp.GetNode().FindTableNode() !=
+ pNode->FindTableNode() )
+ return pNode;
+ }
+ else
+ aTmp = *pNd->StartOfSectionNode();
+ break;
+ }
+ else if( pNd->StartOfSectionNode()->IsTableNode() &&
+ aTmp.GetIndex() < rWhere.GetIndex() )
+ {
+ // after a table node
+ aTmp = *pNd->StartOfSectionNode();
+ break;
+ }
+ [[fallthrough]];
+ default:
+ if( rWhere == aTmp )
+ aTmp -= SwNodeOffset(2);
+ else
+ return pNode;
+ break;
+ }
+ } while( true );
+}
+
+SwTextNode::SwTextNode( const SwNodeIndex &rWhere, SwTextFormatColl *pTextColl, const SfxItemSet* pAutoAttr )
+: SwContentNode( rWhere, SwNodeType::Text, pTextColl ),
+ m_pParaIdleData_Impl(nullptr),
+ m_bContainsHiddenChars(false),
+ m_bHiddenCharsHidePara(false),
+ m_bRecalcHiddenCharFlags(false),
+ m_bLastOutlineState( false ),
+ m_bNotifiable( false ),
+ mbEmptyListStyleSetDueToSetOutlineLevelAttr( false ),
+ mbInSetOrResetAttr( false ),
+ m_bInUndo(false)
+{
+ InitSwParaStatistics( true );
+
+ if( pAutoAttr )
+ SetAttr( *pAutoAttr );
+
+ if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
+ {
+ // #i101516#
+ // apply paragraph style's assigned outline style list level as
+ // list level of the paragraph, if it has none set already.
+ if ( !HasAttrListLevel() &&
+ pTextColl && pTextColl->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ SetAttrListLevel( pTextColl->GetAssignedOutlineStyleLevel() );
+ }
+ AddToList();
+ }
+
+ // call method <UpdateOutlineNode(..)> only for the document nodes array
+ if (GetNodes().IsDocNodes())
+ GetNodes().UpdateOutlineNode(*this);
+
+ m_bNotifiable = true;
+
+ m_bContainsHiddenChars = m_bHiddenCharsHidePara = false;
+ m_bRecalcHiddenCharFlags = true;
+}
+
+SwTextNode::~SwTextNode()
+{
+ // delete only removes the pointer not the array elements!
+ if ( m_pSwpHints )
+ {
+ // do not delete attributes twice when those delete their content
+ std::unique_ptr<SwpHints> pTmpHints(std::move(m_pSwpHints));
+
+ for( size_t j = pTmpHints->Count(); j; )
+ {
+ // first remove the attribute from the array otherwise
+ // if would delete itself
+ DestroyAttr( pTmpHints->Get( --j ) );
+ }
+ }
+
+ // must be removed from outline nodes by now
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ SwOutlineNodes::size_type foo;
+ assert(!GetNodes().GetOutLineNds().Seek_Entry(this, &foo));
+#endif
+
+ RemoveFromList();
+
+ InitSwParaStatistics( false );
+ DelFrames(nullptr); // must be called here while it's still a SwTextNode
+ DelFrames_TextNodePart();
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+ if (!GetDoc().IsInDtor())
+ ResetAttr(RES_PAGEDESC);
+#else
+ ResetAttr(RES_PAGEDESC);
+#endif
+ InvalidateInSwCache(RES_OBJECTDYING);
+}
+
+void SwTextNode::FileLoadedInitHints()
+{
+ if (m_pSwpHints)
+ {
+ m_pSwpHints->MergePortions(*this);
+ }
+}
+
+SwContentFrame *SwTextNode::MakeFrame( SwFrame* pSib )
+{
+ SwContentFrame *pFrame = sw::MakeTextFrame(*this, pSib, sw::FrameMode::New);
+ return pFrame;
+}
+
+sal_Int32 SwTextNode::Len() const
+{
+ return m_Text.getLength();
+}
+
+// After a split node, it's necessary to actualize the ref-pointer of the ftnfrms.
+static void lcl_ChangeFootnoteRef( SwTextNode &rNode )
+{
+ SwpHints *pSwpHints = rNode.GetpSwpHints();
+ if( !(pSwpHints && rNode.GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell()) )
+ return;
+
+ SwContentFrame* pFrame = nullptr;
+ // OD 07.11.2002 #104840# - local variable to remember first footnote
+ // of node <rNode> in order to invalidate position of its first content.
+ // Thus, in its <MakeAll()> it will checked its position relative to its reference.
+ SwFootnoteFrame* pFirstFootnoteOfNode = nullptr;
+ for( size_t j = pSwpHints->Count(); j; )
+ {
+ SwTextAttr* pHt = pSwpHints->Get(--j);
+ if (RES_TXTATR_FTN == pHt->Which())
+ {
+ if( !pFrame )
+ {
+ pFrame = SwIterator<SwContentFrame, SwTextNode, sw::IteratorMode::UnwrapMulti>(rNode).First();
+ if (!pFrame)
+ return;
+ }
+ SwTextFootnote *pAttr = static_cast<SwTextFootnote*>(pHt);
+ OSL_ENSURE( pAttr->GetStartNode(), "FootnoteAtr without StartNode." );
+ SwNodeIndex aIdx( *pAttr->GetStartNode(), 1 );
+ SwContentNode *pNd = aIdx.GetNode().GetContentNode();
+ if ( !pNd )
+ pNd = pFrame->GetAttrSet()->GetDoc()->
+ GetNodes().GoNextSection( &aIdx, true, false );
+ if ( !pNd )
+ continue;
+
+ SwIterator<SwContentFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
+ SwContentFrame* pContent = aIter.First();
+ if( pContent )
+ {
+ OSL_ENSURE( pContent->getRootFrame() == pFrame->getRootFrame(),
+ "lcl_ChangeFootnoteRef: Layout double?" );
+ SwFootnoteFrame *pFootnote = pContent->FindFootnoteFrame();
+ if( pFootnote && pFootnote->GetAttr() == pAttr )
+ {
+ while( pFootnote->GetMaster() )
+ pFootnote = pFootnote->GetMaster();
+ // #104840# - remember footnote frame
+ pFirstFootnoteOfNode = pFootnote;
+ while ( pFootnote )
+ {
+ pFootnote->SetRef( pFrame );
+ pFootnote = pFootnote->GetFollow();
+ static_cast<SwTextFrame*>(pFrame)->SetFootnote( true );
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ while( nullptr != (pContent = aIter.Next()) )
+ {
+ SwFootnoteFrame *pDbgFootnote = pContent->FindFootnoteFrame();
+ OSL_ENSURE( !pDbgFootnote || pDbgFootnote->GetRef() == pFrame,
+ "lcl_ChangeFootnoteRef: Who's that guy?" );
+ }
+#endif
+ }
+ }
+ } // end of for-loop on <SwpHints>
+ // #104840# - invalidate
+ if ( pFirstFootnoteOfNode )
+ {
+ SwContentFrame* pContent = pFirstFootnoteOfNode->ContainsContent();
+ if ( pContent )
+ {
+ pContent->InvalidatePos_();
+ }
+ }
+}
+
+namespace sw {
+
+// check if there are flys on the existing frames (now on "pNode")
+// that need to be moved to the new frames of "this"
+void MoveMergedFlysAndFootnotes(std::vector<SwTextFrame*> const& rFrames,
+ SwTextNode const& rFirstNode, SwTextNode & rSecondNode,
+ bool isSplitNode)
+{
+ if (!isSplitNode)
+ {
+ lcl_ChangeFootnoteRef(rSecondNode);
+ }
+ for (SwNodeOffset nIndex = rSecondNode.GetIndex() + 1; ; ++nIndex)
+ {
+ SwNode *const pTmp(rSecondNode.GetNodes()[nIndex]);
+ if (pTmp->IsCreateFrameWhenHidingRedlines() || pTmp->IsEndNode())
+ {
+ break;
+ }
+ else if (pTmp->IsStartNode())
+ {
+ nIndex = pTmp->EndOfSectionIndex();
+ }
+ else if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst
+ && pTmp->IsTextNode())
+ {
+ lcl_ChangeFootnoteRef(*pTmp->GetTextNode());
+ }
+ }
+ for (SwTextFrame *const pFrame : rFrames)
+ {
+ if (SwSortedObjs *const pObjs = pFrame->GetDrawObjs())
+ {
+ std::vector<SwAnchoredObject*> objs;
+ objs.reserve(pObjs->size());
+ for (SwAnchoredObject *const pObj : *pObjs)
+ {
+ objs.push_back(pObj);
+ }
+ for (SwAnchoredObject *const pObj : objs)
+ {
+ SwFrameFormat & rFormat(pObj->GetFrameFormat());
+ SwFormatAnchor const& rAnchor(rFormat.GetAnchor());
+ if (rFirstNode.GetIndex() < rAnchor.GetContentAnchor()->nNode.GetIndex())
+ {
+ // move it to the new frame of "this"
+ rFormat.CallSwClientNotify(sw::LegacyModifyHint(&rAnchor, &rAnchor));
+ // note pObjs will be deleted if it becomes empty
+ assert(!pFrame->GetDrawObjs() || !pObjs->Contains(*pObj));
+ }
+ }
+ }
+ }
+}
+
+} // namespace
+
+SwTextNode *SwTextNode::SplitContentNode(const SwPosition & rPos,
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool AtStart)> const*const pContentIndexRestore)
+{
+ bool isHide(false);
+ SwNode::Merge const eOldMergeFlag(GetRedlineMergeFlag());
+ bool parentIsOutline = IsOutline();
+
+ // create a node "in front" of me
+ const sal_Int32 nSplitPos = rPos.nContent.GetIndex();
+ const sal_Int32 nTextLen = m_Text.getLength();
+ SwTextNode* const pNode =
+ MakeNewTextNode( rPos.nNode, false, nSplitPos==nTextLen );
+
+ // the first paragraph gets the XmlId,
+ // _except_ if it is empty and the second is not empty
+ if (nSplitPos != 0) {
+ pNode->RegisterAsCopyOf(*this, true);
+ if (nSplitPos == nTextLen)
+ {
+ RemoveMetadataReference();
+ // NB: SwUndoSplitNode will call pNode->JoinNext,
+ // which is sufficient even in this case!
+ }
+ }
+
+ ResetAttr( RES_PARATR_LIST_ISRESTART );
+ ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+ if ( GetNumRule() == nullptr || (parentIsOutline && !IsOutline()) )
+ {
+ ResetAttr( RES_PARATR_LIST_ID );
+ ResetAttr( RES_PARATR_LIST_LEVEL );
+ }
+
+ if ( HasWriterListeners() && !m_Text.isEmpty() && (nTextLen / 2) < nSplitPos )
+ {
+ // optimization for SplitNode: If a split is at the end of a node then
+ // move the frames from the current to the new one and create new ones
+ // for the current one.
+
+ // If fly frames are moved, they don't need to destroy their layout
+ // frames. Set a flag that is checked in SwTextFlyCnt::SetAnchor.
+ if ( HasHints() )
+ {
+ pNode->GetOrCreateSwpHints().SetInSplitNode(true);
+ }
+
+ // Move the first part of the content to the new node and delete
+ // it in the old node.
+ SwIndex aIdx( this );
+ CutText( pNode, aIdx, nSplitPos );
+
+ if( GetWrong() )
+ {
+ pNode->SetWrong( GetWrong()->SplitList( nSplitPos ) );
+ }
+ SetWrongDirty(WrongState::TODO);
+
+ if( GetGrammarCheck() )
+ {
+ pNode->SetGrammarCheck( GetGrammarCheck()->SplitGrammarList( nSplitPos ) );
+ }
+ SetGrammarCheckDirty( true );
+
+ SetWordCountDirty( true );
+
+ if( GetSmartTags() )
+ {
+ pNode->SetSmartTags( GetSmartTags()->SplitList( nSplitPos ) );
+ }
+ SetSmartTagDirty( true );
+
+ if ( pNode->HasHints() )
+ {
+ if ( pNode->m_pSwpHints->CanBeDeleted() )
+ {
+ pNode->m_pSwpHints.reset();
+ }
+ else
+ {
+ pNode->m_pSwpHints->SetInSplitNode(false);
+ }
+
+ // All fly frames anchored as char that are moved to the new
+ // node must have their layout frames deleted.
+ // This comment is sort of silly because we actually delete the
+ // layout frames of those which were not moved?
+ // JP 01.10.96: delete all empty and not-to-be-expanded attributes
+ if ( HasHints() )
+ {
+ for ( size_t j = m_pSwpHints->Count(); j; )
+ {
+ SwTextAttr* const pHt = m_pSwpHints->Get( --j );
+ if ( RES_TXTATR_FLYCNT == pHt ->Which() )
+ {
+ pHt->GetFlyCnt().GetFrameFormat()->DelFrames();
+ }
+ else if ( pHt->DontExpand() )
+ {
+ const sal_Int32* const pEnd = pHt->GetEnd();
+ if (pEnd && pHt->GetStart() == *pEnd )
+ {
+ // delete it!
+ m_pSwpHints->DeleteAtPos( j );
+ DestroyAttr( pHt );
+ }
+ }
+ }
+ }
+
+ }
+
+ if (pContentIndexRestore)
+ { // call before making frames and before RegisterToNode
+ (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
+ }
+ if (eOldMergeFlag != SwNode::Merge::None)
+ { // clear before making frames and before RegisterToNode
+ SetRedlineMergeFlag(SwNode::Merge::None);
+ } // now RegisterToNode will set merge flags in both nodes properly!
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ isHide = true;
+ }
+ frames.push_back(pFrame);
+ }
+ for (SwTextFrame * pFrame : frames)
+ {
+ pFrame->RegisterToNode( *pNode );
+ if (!pFrame->IsFollow() && pFrame->GetOffset())
+ {
+ pFrame->SetOffset( TextFrameIndex(0) );
+ }
+ }
+
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ if ( HasHints() )
+ {
+ MoveTextAttr_To_AttrSet();
+ }
+ // in case there are frames, the RegisterToNode has set the merge flag
+ pNode->MakeFramesForAdjacentContentNode(*this);
+ lcl_ChangeFootnoteRef( *this );
+ if (pContentIndexRestore)
+ { // call after making frames; listeners will take care of adding to the right frame
+ (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, false);
+ }
+ if (eOldMergeFlag != SwNode::Merge::None)
+ {
+ MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
+ }
+ }
+ else
+ {
+ std::unique_ptr<SwWrongList> pList = ReleaseWrong();
+ SetWrongDirty(WrongState::TODO);
+
+ std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
+ SetGrammarCheckDirty( true );
+
+ SetWordCountDirty( true );
+
+ std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
+ SetSmartTagDirty( true );
+
+ SwIndex aIdx( this );
+ CutText( pNode, aIdx, nSplitPos );
+
+ // JP 01.10.96: delete all empty and not-to-be-expanded attributes
+ if ( HasHints() )
+ {
+ for ( size_t j = m_pSwpHints->Count(); j; )
+ {
+ SwTextAttr* const pHt = m_pSwpHints->Get( --j );
+ const sal_Int32* const pEnd = pHt->GetEnd();
+ if ( pHt->DontExpand() && pEnd && (pHt->GetStart() == *pEnd) )
+ {
+ // delete it!
+ m_pSwpHints->DeleteAtPos( j );
+ DestroyAttr( pHt );
+ }
+ }
+ MoveTextAttr_To_AttrSet();
+ }
+
+ if( pList )
+ {
+ pNode->SetWrong( pList->SplitList( nSplitPos ) );
+ SetWrong( std::move(pList) );
+ }
+
+ if( pList3 )
+ {
+ pNode->SetGrammarCheck( pList3->SplitGrammarList( nSplitPos ) );
+ SetGrammarCheck( std::move(pList3) );
+ }
+
+ if( pList2 )
+ {
+ pNode->SetSmartTags( pList2->SplitList( nSplitPos ) );
+ SetSmartTags( std::move(pList2) );
+ }
+
+ if (pContentIndexRestore)
+ { // call before making frames and before RegisterToNode
+ (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::NonFlys, false);
+ }
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*this);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ frames.push_back(pFrame);
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ isHide = true;
+ }
+ }
+ bool bNonMerged(false);
+ bool bRecreateThis(false);
+ for (SwTextFrame * pFrame : frames)
+ {
+ // sw_redlinehide: for this to work properly with hidden nodes,
+ // the frame needs to listen on them too.
+ // also: have to check the frame; this->GetRedlineMergeFlag()
+ // is None in case there's a delete redline inside the paragraph,
+ // but that could still result in a merged frame after split...
+ if (pFrame->GetMergedPara())
+ {
+ // Can't special case this == First here - that could (if
+ // both nodes are still merged by redline) lead to
+ // duplicate frames on "this".
+ // Update the extents with new node; also inits merge flag,
+ // so the MakeFramesForAdjacentContentNode below respects it
+ pFrame->RegisterToNode(*pNode);
+ if (nSplitPos == 0)
+ {
+ // in this case, it was not
+ // invalidated because Cut didn't sent it any hints,
+ // so we have to invalidate it here!
+ pFrame->Prepare(PrepareHint::Clear, nullptr, false);
+ }
+ if (!pFrame->GetMergedPara() ||
+ !pFrame->GetMergedPara()->listener.IsListeningTo(this))
+ {
+ // it's no longer listening - need to recreate frame
+ // (note this is idempotent, can be done once per frame)
+ SetRedlineMergeFlag(SwNode::Merge::None);
+ bRecreateThis = true;
+ }
+ }
+ else
+ {
+ bNonMerged = true;
+ }
+ }
+ assert(!(bNonMerged && bRecreateThis)); // 2 layouts not handled yet - maybe best to simply use the other branch then?
+ if (!frames.empty() && bNonMerged)
+ {
+ // the existing frame on "this" should have been updated by Cut
+ MakeFramesForAdjacentContentNode(*pNode);
+ lcl_ChangeFootnoteRef(*pNode);
+ }
+ else if (bRecreateThis)
+ {
+ assert(pNode->HasWriterListeners()); // was just moved there
+ pNode->MakeFramesForAdjacentContentNode(*this);
+ lcl_ChangeFootnoteRef(*this);
+ }
+
+ if (pContentIndexRestore)
+ { // call after making frames; listeners will take care of adding to the right frame
+ (*pContentIndexRestore)(pNode, sw::mark::RestoreMode::Flys, nSplitPos == 0);
+ }
+
+ if (bRecreateThis)
+ {
+ MoveMergedFlysAndFootnotes(frames, *pNode, *this, true);
+ }
+ }
+
+ // pNode is the previous node, 'this' is the next node from the split.
+ if (nSplitPos == nTextLen && m_pSwpHints)
+ {
+ // We just created an empty next node: avoid unwanted superscript in the new node if it's
+ // there.
+ for (size_t i = 0; i < m_pSwpHints->Count(); ++i)
+ {
+ SwTextAttr* pHt = m_pSwpHints->Get(i);
+ if (pHt->Which() != RES_TXTATR_AUTOFMT)
+ {
+ continue;
+ }
+
+ const sal_Int32* pEnd = pHt->GetEnd();
+ if (!pEnd || pHt->GetStart() != *pEnd)
+ {
+ continue;
+ }
+
+ const std::shared_ptr<SfxItemSet>& pSet = pHt->GetAutoFormat().GetStyleHandle();
+ if (!pSet || pSet->Count() != 1 || !pSet->GetItem(RES_CHRATR_ESCAPEMENT))
+ {
+ continue;
+ }
+
+ m_pSwpHints->DeleteAtPos(i);
+ SwTextAttr::Destroy(pHt, GetDoc().GetAttrPool());
+ --i;
+ }
+ }
+
+#ifndef NDEBUG
+ if (isHide) // otherwise flags won't be set anyway
+ {
+ // First
+ // -> First,NonFirst
+ // -> First,Hidden
+ // -> None,First
+ // Hidden
+ // -> Hidden,Hidden (if still inside merge rl)
+ // -> NonFirst,First (if redline was split)
+ // NonFirst
+ // -> NonFirst,First (if split after end of "incoming" redline &
+ // before start of "outgoing" redline)
+ // -> NonFirst,None (if split after end of "incoming" redline)
+ // -> NonFirst,Hidden (if split after start of "outgoing" redline)
+ // -> Hidden, NonFirst (if split before end of "incoming" redline)
+ // None
+ // -> None,None
+ // -> First,NonFirst (if splitting inside a delete redline)
+ SwNode::Merge const eFirst(pNode->GetRedlineMergeFlag());
+ SwNode::Merge const eSecond(GetRedlineMergeFlag());
+ switch (eOldMergeFlag)
+ {
+ case Merge::First:
+ assert((eFirst == Merge::First && eSecond == Merge::NonFirst)
+ || (eFirst == Merge::First && eSecond == Merge::Hidden)
+ || (eFirst == Merge::None && eSecond == Merge::First));
+ break;
+ case Merge::Hidden:
+ assert((eFirst == Merge::Hidden && eSecond == Merge::Hidden)
+ || (eFirst == Merge::NonFirst && eSecond == Merge::First)
+ // next ones can happen temp. in UndoDelete :(
+ || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst)
+ || (eFirst == Merge::NonFirst && eSecond == Merge::None));
+ break;
+ case Merge::NonFirst:
+ assert((eFirst == Merge::NonFirst && eSecond == Merge::First)
+ || (eFirst == Merge::NonFirst && eSecond == Merge::None)
+ || (eFirst == Merge::NonFirst && eSecond == Merge::Hidden)
+ || (eFirst == Merge::Hidden && eSecond == Merge::NonFirst));
+ break;
+ case Merge::None:
+ assert((eFirst == Merge::None && eSecond == Merge::None)
+ || (eFirst == Merge::First && eSecond == Merge::NonFirst));
+ break;
+ }
+ }
+#else
+ (void) isHide;
+#endif
+
+ {
+ // Send Hint for PageDesc. This should be done in the Layout when
+ // pasting the frames, but that causes other problems that look
+ // expensive to solve.
+ const SwFormatPageDesc *pItem;
+ if(HasWriterListeners() && (pItem = pNode->GetSwAttrSet().GetItemIfSet(RES_PAGEDESC)))
+ pNode->TriggerNodeUpdate(sw::LegacyModifyHint(pItem, pItem));
+ }
+ return pNode;
+}
+
+void SwTextNode::MoveTextAttr_To_AttrSet()
+{
+ OSL_ENSURE( m_pSwpHints, "MoveTextAttr_To_AttrSet without SwpHints?" );
+ for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
+ {
+ SwTextAttr *pHt = m_pSwpHints->Get(i);
+
+ if( pHt->GetStart() )
+ break;
+
+ const sal_Int32* pHtEndIdx = pHt->GetEnd();
+
+ if( !pHtEndIdx )
+ continue;
+
+ if (*pHtEndIdx < m_Text.getLength() || pHt->IsCharFormatAttr())
+ break;
+
+ if( !pHt->IsDontMoveAttr() &&
+ SetAttr( pHt->GetAttr() ) )
+ {
+ m_pSwpHints->DeleteAtPos(i);
+ DestroyAttr( pHt );
+ --i;
+ }
+ }
+
+}
+
+namespace sw {
+
+/// if first node is deleted & second survives, then the first node's frame
+/// will be deleted too; prevent this by moving the frame to the second node
+/// if necessary.
+void MoveDeletedPrevFrames(const SwTextNode & rDeletedPrev, SwTextNode & rNode)
+{
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rDeletedPrev);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ }
+ {
+ auto frames2(frames);
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIt(rNode);
+ for (SwTextFrame* pFrame = aIt.First(); pFrame; pFrame = aIt.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ auto const it(std::find(frames2.begin(), frames2.end(), pFrame));
+ assert(it != frames2.end());
+ frames2.erase(it);
+ }
+ }
+ assert(frames2.empty());
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ pFrame->RegisterToNode(rNode, true);
+ }
+}
+
+// typical Join:
+// None,Node->None
+// None,First->First
+// First,NonFirst->First
+// NonFirst,First->NonFirst
+// NonFirst,None->NonFirst
+
+/// if first node is First, its frames may need to be moved, never deleted.
+/// if first node is NonFirst, second node's own frames (First/None) must be deleted
+void CheckResetRedlineMergeFlag(SwTextNode & rNode, Recreate const eRecreateMerged)
+{
+ if (eRecreateMerged != sw::Recreate::No)
+ {
+ SwTextNode * pMergeNode(&rNode);
+ if (eRecreateMerged == sw::Recreate::Predecessor
+ // tdf#135018 check that there is a predecessor node, i.e. rNode
+ // isn't the first node after the body start node
+ && rNode.GetNodes()[rNode.GetIndex() - 1]->StartOfSectionIndex() != SwNodeOffset(0))
+ {
+ for (SwNodeOffset i = rNode.GetIndex() - 1; ; --i)
+ {
+ SwNode *const pNode(rNode.GetNodes()[i]);
+ assert(!pNode->IsStartNode());
+ if (pNode->IsEndNode())
+ {
+ i = pNode->StartOfSectionIndex();
+ }
+ else if (pNode->IsTextNode())
+ {
+ pMergeNode = pNode->GetTextNode(); // use predecessor to merge
+ break;
+ }
+ }
+ }
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pMergeNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ }
+ auto eMode(sw::FrameMode::Existing);
+ for (SwTextFrame * pFrame : frames)
+ {
+ SwTextNode & rFirstNode(pFrame->GetMergedPara()
+ ? *pFrame->GetMergedPara()->pFirstNode
+ : *pMergeNode);
+ assert(rFirstNode.GetIndex() <= rNode.GetIndex());
+ pFrame->SetMergedPara(sw::CheckParaRedlineMerge(
+ *pFrame, rFirstNode, eMode));
+ // there is no merged para in case the deleted node had one but
+ // nothing was actually hidden
+ if (pFrame->GetMergedPara())
+ {
+ assert(pFrame->GetMergedPara()->listener.IsListeningTo(&rNode));
+ assert(rNode.GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
+ // tdf#135978 Join: recreate fly frames anchored to subsequent nodes
+ if (eRecreateMerged == sw::Recreate::ThisNode)
+ {
+ AddRemoveFlysAnchoredToFrameStartingAtNode(*pFrame, rNode, nullptr);
+ }
+ }
+ eMode = sw::FrameMode::New; // Existing is not idempotent!
+ }
+ }
+ else if (rNode.GetRedlineMergeFlag() != SwNode::Merge::None)
+ {
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(rNode);
+ for (SwTextFrame * pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (auto const pMergedPara = pFrame->GetMergedPara())
+ {
+ if (pMergedPara->pFirstNode == pMergedPara->pLastNode)
+ {
+ assert(pMergedPara->pFirstNode == &rNode);
+ rNode.SetRedlineMergeFlag(SwNode::Merge::None);
+ }
+ break; // checking once is enough
+ }
+ else if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ rNode.SetRedlineMergeFlag(SwNode::Merge::None);
+ break; // checking once is enough
+ }
+ }
+ }
+}
+
+bool HasNumberingWhichNeedsLayoutUpdate(const SwTextNode& rTextNode)
+{
+ const SwNodeNum* pNodeNum = rTextNode.GetNum();
+ if (!pNodeNum)
+ {
+ return false;
+ }
+
+ const SwNumRule* pNumRule = pNodeNum->GetNumRule();
+ if (!pNumRule)
+ {
+ return false;
+ }
+
+ const SwNumFormat* pFormat
+ = pNumRule->GetNumFormat(o3tl::narrowing<sal_uInt16>(rTextNode.GetAttrListLevel()));
+ if (!pFormat)
+ {
+ return false;
+ }
+
+ switch (pFormat->GetNumberingType())
+ {
+ case SVX_NUM_NUMBER_NONE:
+ case SVX_NUM_CHAR_SPECIAL:
+ case SVX_NUM_BITMAP:
+ return false;
+ default:
+ return true;
+ }
+}
+} // namespace
+
+SwContentNode *SwTextNode::JoinNext()
+{
+ SwNodes& rNds = GetNodes();
+ SwNodeIndex aIdx( *this );
+ if( SwContentNode::CanJoinNext( &aIdx ) )
+ {
+ SwDoc& rDoc = rNds.GetDoc();
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save(rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
+ SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
+ sal_Int32 nOldLen = m_Text.getLength();
+
+ // METADATA: merge
+ JoinMetadatable(*pTextNode, !Len(), !pTextNode->Len());
+
+ std::unique_ptr<SwWrongList> pList = ReleaseWrong();
+ if( pList )
+ {
+ pList->JoinList( pTextNode->GetWrong(), nOldLen );
+ SetWrongDirty(WrongState::TODO);
+ }
+ else
+ {
+ pList = pTextNode->ReleaseWrong();
+ if( pList )
+ {
+ pList->Move( 0, nOldLen );
+ SetWrongDirty(WrongState::TODO);
+ }
+ }
+
+ std::unique_ptr<SwGrammarMarkUp> pList3 = ReleaseGrammarCheck();
+ if( pList3 )
+ {
+ pList3->JoinGrammarList( pTextNode->GetGrammarCheck(), nOldLen );
+ SetGrammarCheckDirty( true );
+ }
+ else
+ {
+ pList3 = pTextNode->ReleaseGrammarCheck();
+ if( pList3 )
+ {
+ pList3->MoveGrammar( 0, nOldLen );
+ SetGrammarCheckDirty( true );
+ }
+ }
+
+ std::unique_ptr<SwWrongList> pList2 = ReleaseSmartTags();
+ if( pList2 )
+ {
+ pList2->JoinList( pTextNode->GetSmartTags(), nOldLen );
+ SetSmartTagDirty( true );
+ }
+ else
+ {
+ pList2 = pTextNode->ReleaseSmartTags();
+ if( pList2 )
+ {
+ pList2->Move( 0, nOldLen );
+ SetSmartTagDirty( true );
+ }
+ }
+
+ { // scope for SwIndex
+ pTextNode->CutText( this, SwIndex(pTextNode), pTextNode->Len() );
+ }
+ // move all Bookmarks/TOXMarks
+ if( !pContentStore->Empty())
+ pContentStore->Restore( rDoc, GetIndex(), nOldLen );
+
+ if( pTextNode->HasAnyIndex() )
+ {
+ // move all ShellCursor/StackCursor/UnoCursor out of delete range
+ rDoc.CorrAbs( aIdx, SwPosition( *this ), nOldLen, true );
+ }
+ SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
+ auto eRecreateMerged(eOldMergeFlag == SwNode::Merge::First
+ ? sw::Recreate::ThisNode
+ : sw::Recreate::No);
+ if (eRecreateMerged == sw::Recreate::No)
+ {
+ // tdf#137318 if a delete is inside one node, flag is still None!
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->GetMergedPara())
+ {
+ eRecreateMerged = sw::Recreate::ThisNode;
+ break;
+ }
+ }
+ }
+ bool bOldHasNumberingWhichNeedsLayoutUpdate = HasNumberingWhichNeedsLayoutUpdate(*pTextNode);
+
+ rNds.Delete(aIdx);
+ SetWrong( std::move(pList) );
+ SetGrammarCheck( std::move(pList3) );
+ SetSmartTags( std::move(pList2) );
+
+ if (bOldHasNumberingWhichNeedsLayoutUpdate || HasNumberingWhichNeedsLayoutUpdate(*this))
+ {
+ // Repaint all text frames that belong to this numbering to avoid outdated generated
+ // numbers.
+ InvalidateNumRule();
+ }
+
+ CheckResetRedlineMergeFlag(*this, eRecreateMerged);
+ }
+ else {
+ OSL_FAIL( "No TextNode." );
+ }
+
+ return this;
+}
+
+void SwTextNode::JoinPrev()
+{
+ SwNodes& rNds = GetNodes();
+ SwNodeIndex aIdx( *this );
+ if( SwContentNode::CanJoinPrev( &aIdx ) )
+ {
+ SwDoc& rDoc = rNds.GetDoc();
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ pContentStore->Save( rDoc, aIdx.GetIndex(), SAL_MAX_INT32);
+ SwTextNode *pTextNode = aIdx.GetNode().GetTextNode();
+ const sal_Int32 nLen = pTextNode->Len();
+
+ std::unique_ptr<SwWrongList> pList = pTextNode->ReleaseWrong();
+ if( pList )
+ {
+ pList->JoinList( GetWrong(), Len() );
+ SetWrongDirty(WrongState::TODO);
+ ClearWrong();
+ }
+ else
+ {
+ pList = ReleaseWrong();
+ if( pList )
+ {
+ pList->Move( 0, nLen );
+ SetWrongDirty(WrongState::TODO);
+ }
+ }
+
+ std::unique_ptr<SwGrammarMarkUp> pList3 = pTextNode->ReleaseGrammarCheck();
+ if( pList3 )
+ {
+ pList3->JoinGrammarList( GetGrammarCheck(), Len() );
+ SetGrammarCheckDirty( true );
+ ClearGrammarCheck();
+ }
+ else
+ {
+ pList3 = ReleaseGrammarCheck();
+ if( pList3 )
+ {
+ pList3->MoveGrammar( 0, nLen );
+ SetGrammarCheckDirty( true );
+ }
+ }
+
+ std::unique_ptr<SwWrongList> pList2 = pTextNode->ReleaseSmartTags();
+ if( pList2 )
+ {
+ pList2->JoinList( GetSmartTags(), Len() );
+ SetSmartTagDirty( true );
+ ClearSmartTags();
+ }
+ else
+ {
+ pList2 = ReleaseSmartTags();
+ if( pList2 )
+ {
+ pList2->Move( 0, nLen );
+ SetSmartTagDirty( true );
+ }
+ }
+
+ { // scope for SwIndex
+ pTextNode->CutText( this, SwIndex(this), SwIndex(pTextNode), nLen );
+ }
+ // move all Bookmarks/TOXMarks
+ if( !pContentStore->Empty() )
+ pContentStore->Restore( rDoc, GetIndex() );
+
+ if( pTextNode->HasAnyIndex() )
+ {
+ // move all ShellCursor/StackCursor/UnoCursor out of delete range
+ rDoc.CorrAbs( aIdx, SwPosition( *this ), nLen, true );
+ }
+ SwNode::Merge const eOldMergeFlag(pTextNode->GetRedlineMergeFlag());
+ if (eOldMergeFlag == SwNode::Merge::First
+ && !IsCreateFrameWhenHidingRedlines())
+ {
+ sw::MoveDeletedPrevFrames(*pTextNode, *this);
+ }
+ rNds.Delete(aIdx);
+ SetWrong( std::move(pList) );
+ SetGrammarCheck( std::move(pList3) );
+ SetSmartTags( std::move(pList2) );
+ InvalidateNumRule();
+ sw::CheckResetRedlineMergeFlag(*this,
+ eOldMergeFlag == SwNode::Merge::NonFirst
+ ? sw::Recreate::Predecessor
+ : sw::Recreate::No);
+ }
+ else {
+ OSL_FAIL( "No TextNode." );
+ }
+}
+
+// create an AttrSet with ranges for Frame-/Para/Char-attributes
+void SwTextNode::NewAttrSet( SwAttrPool& rPool )
+{
+ OSL_ENSURE( !mpAttrSet, "AttrSet is set after all" );
+ SwAttrSet aNewAttrSet( rPool, aTextNodeSetRange );
+
+ // put names of parent style and conditional style:
+ const SwFormatColl* pAnyFormatColl = &GetAnyFormatColl();
+ const SwFormatColl* pFormatColl = GetFormatColl();
+ OUString sVal;
+ SwStyleNameMapper::FillProgName( pAnyFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ SfxStringItem aAnyFormatColl( RES_FRMATR_STYLE_NAME, sVal );
+ if ( pFormatColl != pAnyFormatColl )
+ SwStyleNameMapper::FillProgName( pFormatColl->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ SfxStringItem aFormatColl( RES_FRMATR_CONDITIONAL_STYLE_NAME, sVal );
+ aNewAttrSet.Put( aAnyFormatColl );
+ aNewAttrSet.Put( aFormatColl );
+
+ aNewAttrSet.SetParent( &pAnyFormatColl->GetAttrSet() );
+ mpAttrSet = GetDoc().GetIStyleAccess().getAutomaticStyle( aNewAttrSet, IStyleAccess::AUTO_STYLE_PARA, &sVal );
+}
+
+// override SwIndexReg::Update => text hints do not need SwIndex for start/end!
+void SwTextNode::Update(
+ SwIndex const & rPos,
+ const sal_Int32 nChangeLen,
+ const bool bNegative,
+ const bool bDelete )
+{
+ SetAutoCompleteWordDirty( true );
+
+ std::unique_ptr<SwpHts> pCollector;
+ const sal_Int32 nChangePos = rPos.GetIndex();
+
+ if ( HasHints() )
+ {
+ if ( bNegative )
+ {
+ std::vector<SwTextInputField*> aTextInputFields;
+
+ const sal_Int32 nChangeEnd = nChangePos + nChangeLen;
+ for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
+ {
+ bool bTextAttrChanged = false;
+ bool bStartOfTextAttrChanged = false;
+ SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
+ if ( pHint->GetStart() > nChangePos )
+ {
+ if ( pHint->GetStart() > nChangeEnd )
+ {
+ pHint->SetStart( pHint->GetStart() - nChangeLen );
+ }
+ else
+ {
+ pHint->SetStart( nChangePos );
+ }
+ bStartOfTextAttrChanged = true;
+ }
+
+ const sal_Int32 * const pEnd = pHint->GetEnd();
+ if (pEnd && *pEnd > nChangePos )
+ {
+ if( *pEnd > nChangeEnd )
+ {
+ pHint->SetEnd(*pEnd - nChangeLen);
+ }
+ else
+ {
+ pHint->SetEnd(nChangePos);
+ }
+ bTextAttrChanged = !bStartOfTextAttrChanged;
+ }
+
+ if ( bTextAttrChanged
+ && pHint->Which() == RES_TXTATR_INPUTFIELD )
+ {
+ SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
+ if ( pTextInputField )
+ aTextInputFields.push_back(pTextInputField);
+ }
+ }
+
+ //wait until all the attribute positions are correct
+ //before updating the field contents
+ for (SwTextInputField* pTextInputField : aTextInputFields)
+ {
+ pTextInputField->UpdateFieldContent();
+ }
+
+ m_pSwpHints->MergePortions( *this );
+ }
+ else
+ {
+ bool bNoExp = false;
+ bool bResort = false;
+ bool bMergePortionsNeeded = false;
+ const int coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
+ std::vector<SwTextInputField*> aTextInputFields;
+
+ bool aDontExp[ coArrSz ] = {};
+
+ for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
+ {
+ bool bTextAttrChanged = false;
+ SwTextAttr * const pHint = m_pSwpHints->GetWithoutResorting(n);
+ const sal_Int32 * const pEnd = pHint->GetEnd();
+ if ( pHint->GetStart() >= nChangePos )
+ {
+ pHint->SetStart( pHint->GetStart() + nChangeLen );
+ if ( pEnd )
+ {
+ pHint->SetEnd(*pEnd + nChangeLen);
+ }
+ }
+ else if ( pEnd && (*pEnd >= nChangePos) )
+ {
+ if ( (*pEnd > nChangePos) || IsIgnoreDontExpand() )
+ {
+ pHint->SetEnd(*pEnd + nChangeLen);
+ bTextAttrChanged = true;
+ }
+ else // *pEnd == nChangePos
+ {
+ const sal_uInt16 nWhich = pHint->Which();
+
+ OSL_ENSURE(!isCHRATR(nWhich), "Update: char attr hint?");
+ if (!(isCHRATR(nWhich) || isTXTATR_WITHEND(nWhich)))
+ continue;
+
+ const sal_uInt16 nWhPos = nWhich - RES_CHRATR_BEGIN;
+
+ if( aDontExp[ nWhPos ] )
+ continue;
+
+ if ( pHint->DontExpand() )
+ {
+ pHint->SetDontExpand( false );
+ bResort = true;
+ // could have a continuation with IgnoreStart()...
+ if (pHint->IsFormatIgnoreEnd())
+ {
+ bMergePortionsNeeded = true;
+ }
+ if ( pHint->IsCharFormatAttr() )
+ {
+ bNoExp = true;
+ aDontExp[ RES_TXTATR_CHARFMT - RES_CHRATR_BEGIN ] = true;
+ aDontExp[ RES_TXTATR_INETFMT - RES_CHRATR_BEGIN ] = true;
+ }
+ else
+ aDontExp[ nWhPos ] = true;
+ }
+ else if( bNoExp )
+ {
+ if (!pCollector)
+ {
+ pCollector.reset( new SwpHts );
+ }
+ auto it = std::find_if(pCollector->begin(), pCollector->end(),
+ [nWhich](const SwTextAttr *pTmp) { return nWhich == pTmp->Which(); });
+ if (it != pCollector->end())
+ {
+ SwTextAttr *pTmp = *it;
+ pCollector->erase( it );
+ SwTextAttr::Destroy( pTmp, GetDoc().GetAttrPool() );
+ }
+ SwTextAttr * const pTmp =
+ MakeTextAttr( GetDoc(),
+ pHint->GetAttr(), nChangePos, nChangePos + nChangeLen);
+ pCollector->push_back( pTmp );
+ }
+ else
+ {
+ pHint->SetEnd(*pEnd + nChangeLen);
+ bTextAttrChanged = true;
+ }
+ }
+ }
+
+ if ( bTextAttrChanged
+ && pHint->Which() == RES_TXTATR_INPUTFIELD )
+ {
+ SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pHint);
+ if ( pTextInputField )
+ aTextInputFields.push_back(pTextInputField);
+ }
+ }
+
+ //wait until all the attribute positions are correct
+ //before updating the field contents
+ for (SwTextInputField* pTextInputField : aTextInputFields)
+ {
+ pTextInputField->UpdateFieldContent();
+ }
+
+ if (bMergePortionsNeeded)
+ {
+ m_pSwpHints->MergePortions(*this); // does Resort too
+ }
+ else if (bResort)
+ {
+ m_pSwpHints->Resort();
+ }
+ }
+ }
+
+ bool bSortMarks = false;
+ SwIndexReg aTmpIdxReg;
+ if ( !bNegative && !bDelete )
+ {
+ const SwRedlineTable& rTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type n = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Any);
+ for( ; n < rTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rTable[ n ];
+ if ( pRedl->HasMark() )
+ {
+ SwPosition* const pEnd = pRedl->End();
+ if ( this == &pEnd->nNode.GetNode() &&
+ *pRedl->GetPoint() != *pRedl->GetMark() )
+ {
+ SwIndex & rIdx = pEnd->nContent;
+ if (nChangePos == rIdx.GetIndex())
+ {
+ rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
+ }
+ }
+ }
+ else if ( this == &pRedl->GetPoint()->nNode.GetNode() )
+ {
+ SwIndex & rIdx = pRedl->GetPoint()->nContent;
+ if (nChangePos == rIdx.GetIndex())
+ {
+ rIdx.Assign( &aTmpIdxReg, rIdx.GetIndex() );
+ }
+ // the unused position must not be on a SwTextNode
+ bool const isOneUsed(&pRedl->GetBound() == pRedl->GetPoint());
+ assert(!pRedl->GetBound(!isOneUsed).nNode.GetNode().IsTextNode());
+ assert(!pRedl->GetBound(!isOneUsed).nContent.GetIdxReg()); (void)isOneUsed;
+ }
+ }
+
+ // Bookmarks must never grow to either side, when editing (directly)
+ // to the left or right (i#29942)! Exception: if the bookmark has
+ // 2 positions and start == end, then expand it (tdf#96479)
+ {
+ bool bAtLeastOneBookmarkMoved = false;
+ bool bAtLeastOneExpandedBookmarkAtInsertionPosition = false;
+ // A text node already knows its marks via its SwIndexes.
+ o3tl::sorted_vector<const sw::mark::IMark*> aSeenMarks;
+ const SwIndex* next;
+ for (const SwIndex* pIndex = GetFirstIndex(); pIndex; pIndex = next )
+ {
+ next = pIndex->GetNext();
+ const sw::mark::IMark* pMark = pIndex->GetMark();
+ if (!pMark)
+ continue;
+ // Only handle bookmarks once, if they start and end at this node as well.
+ if (!aSeenMarks.insert(pMark).second)
+ continue;
+ const SwPosition* pEnd = &pMark->GetMarkEnd();
+ SwIndex & rEndIdx = const_cast<SwIndex&>(pEnd->nContent);
+ if( this == &pEnd->nNode.GetNode() &&
+ rPos.GetIndex() == rEndIdx.GetIndex() )
+ {
+ if (&rEndIdx == next) // nasty corner case:
+ { // don't switch to iterating aTmpIdxReg!
+ next = rEndIdx.GetNext();
+ }
+ // tdf#96479: if start == end, ignore the other position
+ // so it is moved!
+ rEndIdx.Assign( &aTmpIdxReg, rEndIdx.GetIndex() );
+ bAtLeastOneBookmarkMoved = true;
+ }
+ else if ( !bAtLeastOneExpandedBookmarkAtInsertionPosition )
+ {
+ if ( pMark->IsExpanded() )
+ {
+ const SwPosition* pStart = &pMark->GetMarkStart();
+ if ( this == &pStart->nNode.GetNode()
+ && rPos.GetIndex() == pStart->nContent.GetIndex() )
+ {
+ bAtLeastOneExpandedBookmarkAtInsertionPosition = true;
+ }
+ }
+ }
+ }
+
+ bSortMarks = bAtLeastOneBookmarkMoved && bAtLeastOneExpandedBookmarkAtInsertionPosition;
+ }
+
+ // at-char anchored flys shouldn't be moved, either.
+ if (!m_bInUndo)
+ {
+ std::vector<SwFrameFormat*> const& rFlys(GetAnchoredFlys());
+ for (size_t i = 0; i != rFlys.size(); ++i)
+ {
+ SwFrameFormat const*const pFormat = rFlys[i];
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ const SwPosition* pContentAnchor = rAnchor.GetContentAnchor();
+ if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR && pContentAnchor)
+ {
+ // The fly is at-char anchored and has an anchor position.
+ SwIndex& rEndIdx = const_cast<SwIndex&>(pContentAnchor->nContent);
+ if (&pContentAnchor->nNode.GetNode() == this && rEndIdx.GetIndex() == rPos.GetIndex())
+ {
+ // The anchor position is exactly our insert position.
+ rEndIdx.Assign(&aTmpIdxReg, rEndIdx.GetIndex());
+ }
+ }
+ }
+ }
+
+ // The cursors of other shells shouldn't be moved, either.
+ if (SwDocShell* pDocShell = GetDoc().GetDocShell())
+ {
+ if (pDocShell->GetWrtShell())
+ {
+ for (SwViewShell& rShell : pDocShell->GetWrtShell()->GetRingContainer())
+ {
+ auto pWrtShell = dynamic_cast<SwWrtShell*>(&rShell);
+ if (!pWrtShell || pWrtShell == pDocShell->GetWrtShell())
+ continue;
+
+ SwShellCursor* pCursor = pWrtShell->GetCursor_();
+ if (!pCursor)
+ continue;
+
+ SwIndex& rIndex = pCursor->Start()->nContent;
+ if (&pCursor->Start()->nNode.GetNode() == this && rIndex.GetIndex() == rPos.GetIndex())
+ {
+ // The cursor position of this other shell is exactly our insert position.
+ rIndex.Assign(&aTmpIdxReg, rIndex.GetIndex());
+ }
+ }
+ }
+ }
+ }
+
+ // base class
+ SwIndexReg::Update( rPos, nChangeLen, bNegative, bDelete );
+
+ if (pCollector)
+ {
+ const size_t nCount = pCollector->size();
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ m_pSwpHints->TryInsertHint( (*pCollector)[ i ], *this );
+ }
+ }
+
+ aTmpIdxReg.MoveTo( *this );
+ if ( bSortMarks )
+ {
+ getIDocumentMarkAccess()->assureSortedMarkContainers();
+ }
+
+ //Any drawing objects anchored into this text node may be sorted by their
+ //anchor position which may have changed here, so resort them
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this);
+ for (SwTextFrame* pFrame = iter.First(); pFrame; pFrame = iter.Next())
+ {
+ SwSortedObjs * pSortedObjs(pFrame->GetDrawObjs());
+ if (pSortedObjs)
+ {
+ pSortedObjs->UpdateAll();
+ }
+ // also sort the objs on the page frame
+ pSortedObjs = pFrame->FindPageFrame()->GetSortedObjs();
+ if (pSortedObjs) // doesn't exist yet if called for inserting as-char fly
+ {
+ pSortedObjs->UpdateAll();
+ }
+ }
+
+ // Update the paragraph signatures.
+ if (SwEditShell* pEditShell = GetDoc().GetEditShell())
+ {
+ pEditShell->ValidateParagraphSignatures(this, true);
+ }
+
+ // Inform LOK clients about change in position of redlines (if any)
+ // Don't emit notifications during save: redline flags are temporarily changed during save, but
+ // it's not useful to let clients know about such changes.
+ if (!comphelper::LibreOfficeKit::isActive() || GetDoc().IsInWriting())
+ return;
+
+ const SwRedlineTable& rTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRedlineTable::size_type nRedlnPos = 0; nRedlnPos < rTable.size(); ++nRedlnPos)
+ {
+ SwRangeRedline* pRedln = rTable[nRedlnPos];
+ if (pRedln->HasMark())
+ {
+ if (this == &pRedln->End()->nNode.GetNode() && *pRedln->GetPoint() != *pRedln->GetMark())
+ {
+ // Redline is changed only when some change occurs before it
+ if (nChangePos <= pRedln->Start()->nContent.GetIndex())
+ {
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
+ }
+ }
+ }
+ else if (this == &pRedln->GetPoint()->nNode.GetNode())
+ SwRedlineTable::LOKRedlineNotification(RedlineNotification::Modify, pRedln);
+ }
+}
+
+void SwTextNode::ChgTextCollUpdateNum( const SwTextFormatColl *pOldColl,
+ const SwTextFormatColl *pNewColl)
+{
+ SwDoc& rDoc = GetDoc();
+ // query the OutlineLevel and if it changed, notify the Nodes-Array!
+ const int nOldLevel = pOldColl && pOldColl->IsAssignedToListLevelOfOutlineStyle() ?
+ pOldColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
+ const int nNewLevel = pNewColl && pNewColl->IsAssignedToListLevelOfOutlineStyle() ?
+ pNewColl->GetAssignedOutlineStyleLevel() : MAXLEVEL;
+
+ if ( MAXLEVEL != nNewLevel && -1 != nNewLevel )
+ {
+ SetAttrListLevel(nNewLevel);
+ }
+ rDoc.GetNodes().UpdateOutlineNode(*this);
+
+ SwNodes& rNds = GetNodes();
+ // If Level 0 (Chapter), update the footnotes!
+ if( ( !nNewLevel || !nOldLevel) && !rDoc.GetFootnoteIdxs().empty() &&
+ FTNNUM_CHAPTER == rDoc.GetFootnoteInfo().m_eNum &&
+ rNds.IsDocNodes() )
+ {
+ SwNodeIndex aTmpIndex( rNds, GetIndex());
+
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aTmpIndex);
+ }
+
+ if( pNewColl && RES_CONDTXTFMTCOLL == pNewColl->Which() )
+ {
+ // check the condition of the text node again
+ ChkCondColl();
+ }
+}
+
+// If positioned exactly at the end of a CharStyle or Hyperlink,
+// set its DontExpand flag.
+bool SwTextNode::DontExpandFormat( const SwIndex& rIdx, bool bFlag,
+ bool bFormatToTextAttributes )
+{
+ const sal_Int32 nIdx = rIdx.GetIndex();
+ if (bFormatToTextAttributes && nIdx == m_Text.getLength())
+ {
+ FormatToTextAttr( this );
+ }
+
+ bool bRet = false;
+ if ( HasHints() )
+ {
+ m_pSwpHints->SortIfNeedBe();
+ int nPos = m_pSwpHints->GetLastPosSortedByEnd(nIdx);
+ for ( ; nPos >= 0; --nPos)
+ {
+ SwTextAttr *pTmp = m_pSwpHints->GetSortedByEnd( nPos );
+ const sal_Int32 *pEnd = pTmp->GetEnd();
+ if( !pEnd )
+ continue;
+ assert( *pEnd <= nIdx );
+ if( nIdx != *pEnd )
+ break;
+ if( bFlag != pTmp->DontExpand() && !pTmp->IsLockExpandFlag()
+ && *pEnd > pTmp->GetStart())
+ {
+ bRet = true;
+ m_pSwpHints->NoteInHistory( pTmp );
+ pTmp->SetDontExpand( bFlag );
+ }
+ }
+ }
+ return bRet;
+}
+
+static bool lcl_GetTextAttrDefault(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
+{
+ return ((nHintStart <= nIndex) && (nIndex < nHintEnd));
+}
+static bool lcl_GetTextAttrExpand(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
+{
+ return ((nHintStart < nIndex) && (nIndex <= nHintEnd));
+}
+static bool lcl_GetTextAttrParent(sal_Int32 nIndex, sal_Int32 nHintStart, sal_Int32 nHintEnd)
+{
+ return ((nHintStart < nIndex) && (nIndex < nHintEnd));
+}
+
+static void
+lcl_GetTextAttrs(
+ std::vector<SwTextAttr *> *const pVector,
+ SwTextAttr **const ppTextAttr,
+ SwpHints const *const pSwpHints,
+ sal_Int32 const nIndex, sal_uInt16 const nWhich,
+ enum SwTextNode::GetTextAttrMode const eMode)
+{
+ assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
+ if (!pSwpHints)
+ return;
+ size_t const nSize = pSwpHints->Count();
+ sal_Int32 nPreviousIndex(0); // index of last hint with nWhich
+ bool (*pMatchFunc)(sal_Int32, sal_Int32, sal_Int32)=nullptr;
+ switch (eMode)
+ {
+ case SwTextNode::DEFAULT: pMatchFunc = &lcl_GetTextAttrDefault; break;
+ case SwTextNode::EXPAND: pMatchFunc = &lcl_GetTextAttrExpand; break;
+ case SwTextNode::PARENT: pMatchFunc = &lcl_GetTextAttrParent; break;
+ default: assert(false);
+ }
+
+ for( size_t i = pSwpHints->GetFirstPosSortedByWhichAndStart(nWhich); i < nSize; ++i )
+ {
+ SwTextAttr *const pHint = pSwpHints->GetSortedByWhichAndStart(i);
+ if (pHint->Which() != nWhich)
+ break; // hints are sorted by which&start, so we are done...
+
+ sal_Int32 const nHintStart = pHint->GetStart();
+ if (nIndex < nHintStart)
+ break; // hints are sorted by which&start, so we are done...
+
+ sal_Int32 const*const pEndIdx = pHint->GetEnd();
+ // cannot have hint with no end and no dummy char
+ assert(pEndIdx || pHint->HasDummyChar());
+ // If EXPAND is set, simulate the text input behavior, i.e.
+ // move the start, and expand the end.
+ bool const bContained( pEndIdx
+ ? (*pMatchFunc)(nIndex, nHintStart, *pEndIdx)
+ : (nHintStart == nIndex) );
+ if (bContained)
+ {
+ if (pVector)
+ {
+ if (nPreviousIndex < nHintStart)
+ {
+ pVector->clear(); // clear hints that are outside pHint
+ nPreviousIndex = nHintStart;
+ }
+ pVector->push_back(pHint);
+ }
+ else
+ {
+ *ppTextAttr = pHint; // and possibly overwrite outer hint
+ }
+ if (!pEndIdx)
+ {
+ break;
+ }
+ }
+ }
+}
+
+std::vector<SwTextAttr *>
+SwTextNode::GetTextAttrsAt(sal_Int32 const nIndex, sal_uInt16 const nWhich) const
+{
+ assert(nWhich >= RES_TXTATR_BEGIN && nWhich < RES_TXTATR_END);
+ std::vector<SwTextAttr *> ret;
+ lcl_GetTextAttrs(&ret, nullptr, m_pSwpHints.get(), nIndex, nWhich, DEFAULT);
+ return ret;
+}
+
+SwTextAttr *
+SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich,
+ enum GetTextAttrMode const eMode) const
+{
+ assert( (nWhich == RES_TXTATR_META)
+ || (nWhich == RES_TXTATR_METAFIELD)
+ || (nWhich == RES_TXTATR_AUTOFMT)
+ || (nWhich == RES_TXTATR_INETFMT)
+ || (nWhich == RES_TXTATR_CJK_RUBY)
+ || (nWhich == RES_TXTATR_UNKNOWN_CONTAINER)
+ || (nWhich == RES_TXTATR_CONTENTCONTROL)
+ || (nWhich == RES_TXTATR_INPUTFIELD ) );
+ // "GetTextAttrAt() will give wrong result for this hint!")
+
+ SwTextAttr * pRet(nullptr);
+ lcl_GetTextAttrs(nullptr, & pRet, m_pSwpHints.get(), nIndex, nWhich, eMode);
+ return pRet;
+}
+
+const SwTextInputField* SwTextNode::GetOverlappingInputField( const SwTextAttr& rTextAttr ) const
+{
+ const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( rTextAttr.GetStart(), RES_TXTATR_INPUTFIELD, PARENT ));
+
+ if ( pTextInputField == nullptr && rTextAttr.End() != nullptr )
+ {
+ pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( *(rTextAttr.End()), RES_TXTATR_INPUTFIELD, PARENT ));
+ }
+
+ return pTextInputField;
+}
+
+void SwTextNode::DelFrames_TextNodePart()
+{
+ SetWrong( nullptr );
+ SetWrongDirty(WrongState::TODO);
+
+ SetGrammarCheck( nullptr );
+ SetGrammarCheckDirty( true );
+
+ SetSmartTags( nullptr );
+ SetSmartTagDirty( true );
+
+ SetWordCountDirty( true );
+ SetAutoCompleteWordDirty( true );
+}
+
+SwTextField* SwTextNode::GetFieldTextAttrAt(
+ const sal_Int32 nIndex,
+ const bool bIncludeInputFieldAtStart ) const
+{
+ SwTextField* pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_FIELD ));
+ if ( pTextField == nullptr )
+ {
+ pTextField = dynamic_cast<SwTextField*>(GetTextAttrForCharAt( nIndex, RES_TXTATR_ANNOTATION ));
+ }
+ if ( pTextField == nullptr )
+ {
+ pTextField =
+ dynamic_cast<SwTextField*>( GetTextAttrAt(
+ nIndex,
+ RES_TXTATR_INPUTFIELD,
+ bIncludeInputFieldAtStart ? DEFAULT : EXPAND ));
+ }
+
+ return pTextField;
+}
+
+static SwCharFormat* lcl_FindCharFormat( const SwCharFormats* pCharFormats, std::u16string_view rName )
+{
+ if( !rName.empty() )
+ {
+ const size_t nArrLen = pCharFormats->size();
+ for( size_t i = 1; i < nArrLen; i++ )
+ {
+ SwCharFormat* pFormat = (*pCharFormats)[ i ];
+ if( pFormat->GetName()==rName )
+ return pFormat;
+ }
+ }
+ return nullptr;
+}
+
+static void lcl_CopyHint(
+ const sal_uInt16 nWhich,
+ const SwTextAttr * const pHt,
+ SwTextAttr *const pNewHt,
+ SwDoc *const pOtherDoc,
+ SwTextNode *const pDest )
+{
+ assert(nWhich == pHt->Which()); // wrong hint-id
+ switch( nWhich )
+ {
+ // copy nodesarray section with footnote content
+ case RES_TXTATR_FTN :
+ assert(pDest); // "lcl_CopyHint: no destination text node?"
+ static_cast<const SwTextFootnote*>(pHt)->CopyFootnote( *static_cast<SwTextFootnote*>(pNewHt), *pDest);
+ break;
+
+ // Fields that are copied into different SwDocs must be registered
+ // at their new FieldTypes.
+
+ case RES_TXTATR_FIELD :
+ {
+ if( pOtherDoc != nullptr )
+ {
+ static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
+ static_txtattr_cast<SwTextField*>(pNewHt));
+ }
+
+ // Table Formula must be copied relative.
+ const SwFormatField& rField = pHt->GetFormatField();
+ if( SwFieldIds::Table == rField.GetField()->GetTyp()->Which()
+ && static_cast<const SwTableField*>(rField.GetField())->IsIntrnlName())
+ {
+ // convert internal formula to external
+ const SwTableNode* const pDstTableNd =
+ static_txtattr_cast<const SwTextField*>(pHt)->GetTextNode().FindTableNode();
+ if( pDstTableNd )
+ {
+ SwTableField* const pTableField =
+ const_cast<SwTableField*>(static_cast<const SwTableField*>(
+ pNewHt->GetFormatField().GetField()));
+ pTableField->PtrToBoxNm( &pDstTableNd->GetTable() );
+ }
+ }
+ }
+ break;
+
+ case RES_TXTATR_INPUTFIELD :
+ case RES_TXTATR_ANNOTATION :
+ if( pOtherDoc != nullptr )
+ {
+ static_txtattr_cast<const SwTextField*>(pHt)->CopyTextField(
+ static_txtattr_cast<SwTextField*>(pNewHt));
+ }
+ break;
+
+ case RES_TXTATR_TOXMARK :
+ if( pOtherDoc && pDest && pDest->GetpSwpHints()
+ && pDest->GetpSwpHints()->Contains( pNewHt ) )
+ {
+ // ToXMarks that are copied to different SwDocs must register
+ // at their new ToX (sw::BroadcastingModify).
+ static_txtattr_cast<SwTextTOXMark*>(pNewHt)->CopyTOXMark(*pOtherDoc);
+ }
+ break;
+
+ case RES_TXTATR_CHARFMT :
+ // For CharacterStyles, the format must be copied too.
+ if( pDest && pDest->GetpSwpHints()
+ && pDest->GetpSwpHints()->Contains( pNewHt ) )
+ {
+ SwCharFormat* pFormat = pHt->GetCharFormat().GetCharFormat();
+
+ if (pOtherDoc)
+ {
+ pFormat = pOtherDoc->CopyCharFormat( *pFormat );
+ }
+ const_cast<SwFormatCharFormat&>(
+ pNewHt->GetCharFormat() ).SetCharFormat( pFormat );
+ }
+ break;
+ case RES_TXTATR_INETFMT :
+ {
+ // For Hyperlinks, the format must be copied too.
+ if( pOtherDoc && pDest && pDest->GetpSwpHints()
+ && pDest->GetpSwpHints()->Contains( pNewHt ) )
+ {
+ const SwDoc& rDoc = static_txtattr_cast<
+ const SwTextINetFormat*>(pHt)->GetTextNode().GetDoc();
+ const SwCharFormats* pCharFormats = rDoc.GetCharFormats();
+ const SwFormatINetFormat& rFormat = pHt->GetINetFormat();
+ SwCharFormat* pFormat;
+ pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetINetFormat() );
+ if( pFormat )
+ pOtherDoc->CopyCharFormat( *pFormat );
+ pFormat = lcl_FindCharFormat( pCharFormats, rFormat.GetVisitedFormat() );
+ if( pFormat )
+ pOtherDoc->CopyCharFormat( *pFormat );
+ }
+ //JP 24.04.98: The attribute must point to a text node, so that
+ // the styles can be created.
+ SwTextINetFormat *const pINetHt = static_txtattr_cast<SwTextINetFormat*>(pNewHt);
+ if ( !pINetHt->GetpTextNode() )
+ {
+ pINetHt->ChgTextNode( pDest );
+ }
+
+ //JP 22.10.97: set up link to char style
+ pINetHt->GetCharFormat();
+ break;
+ }
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ OSL_ENSURE( pNewHt, "copying Meta should not fail!" );
+ OSL_ENSURE( pDest
+ && (CH_TXTATR_INWORD == pDest->GetText()[pNewHt->GetStart()]),
+ "missing CH_TXTATR?");
+ break;
+ }
+}
+
+/// copy attributes at position nTextStartIdx to node pDest
+// BP 7.6.93: Intentionally copy only attributes _with_ EndIdx!
+// CopyAttr is usually called when attributes are set on a
+// node with no text.
+void SwTextNode::CopyAttr( SwTextNode *pDest, const sal_Int32 nTextStartIdx,
+ const sal_Int32 nOldPos )
+{
+ if ( HasHints() )
+ {
+ SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ?
+ &pDest->GetDoc() : nullptr;
+
+ for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
+ {
+ SwTextAttr *const pHt = m_pSwpHints->Get(i);
+ sal_Int32 const nAttrStartIdx = pHt->GetStart();
+ if ( nTextStartIdx < nAttrStartIdx )
+ break; // beyond end of text, because nLen == 0
+
+ const sal_Int32 *const pEndIdx = pHt->GetEnd();
+ if ( pEndIdx && !pHt->HasDummyChar() )
+ {
+ sal_uInt16 const nWhich = pHt->Which();
+ if (RES_TXTATR_INPUTFIELD != nWhich // fdo#74981 skip fields
+ && ( *pEndIdx > nTextStartIdx
+ || (*pEndIdx == nTextStartIdx
+ && nAttrStartIdx == nTextStartIdx)))
+ {
+ if ( RES_TXTATR_REFMARK != nWhich )
+ {
+ // attribute in the area => copy
+ SwTextAttr *const pNewHt =
+ pDest->InsertItem( pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
+ if ( pNewHt )
+ {
+ lcl_CopyHint( nWhich, pHt, pNewHt,
+ pOtherDoc, pDest );
+ }
+ }
+ else if( !pOtherDoc
+ ? GetDoc().IsCopyIsMove()
+ : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) )
+ {
+ pDest->InsertItem(
+ pHt->GetAttr(), nOldPos, nOldPos, SetAttrMode::IS_COPY);
+ }
+ }
+ }
+ }
+ }
+
+ if( this != pDest )
+ {
+ // notify layout frames, to prevent disappearance of footnote numbers
+ SwUpdateAttr aHint(
+ nOldPos,
+ nOldPos,
+ 0);
+
+ pDest->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+}
+
+/// copy text and attributes to node pDest
+void SwTextNode::CopyText( SwTextNode *const pDest,
+ const SwIndex &rStart,
+ const sal_Int32 nLen,
+ const bool bForceCopyOfAllAttrs )
+{
+ SwIndex const aIdx( pDest, pDest->m_Text.getLength() );
+ CopyText( pDest, aIdx, rStart, nLen, bForceCopyOfAllAttrs );
+}
+
+void SwTextNode::CopyText( SwTextNode *const pDest,
+ const SwIndex &rDestStart,
+ const SwIndex &rStart,
+ sal_Int32 nLen,
+ const bool bForceCopyOfAllAttrs )
+{
+ CHECK_SWPHINTS_IF_FRM(this);
+ CHECK_SWPHINTS(pDest);
+ sal_Int32 nTextStartIdx = rStart.GetIndex();
+ sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
+
+ if (pDest->GetDoc().IsClipBoard() && GetNum())
+ {
+ // #i111677# cache expansion of source (for clipboard)
+ pDest->m_oNumStringCache = (nTextStartIdx != 0)
+ ? OUString() // fdo#49076: numbering only if copy from para start
+ : GetNumString();
+ }
+
+ if( !nLen )
+ {
+ // if no length is given, copy attributes at position rStart
+ CopyAttr( pDest, nTextStartIdx, nDestStart );
+
+ // copy hard attributes on whole paragraph
+ if( HasSwAttrSet() )
+ {
+ // i#96213 all or just the Char attributes?
+ if ( !bForceCopyOfAllAttrs &&
+ ( nDestStart ||
+ pDest->HasSwAttrSet() ||
+ nLen != pDest->GetText().getLength()))
+ {
+ SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
+ aCharSet( pDest->GetDoc().GetAttrPool() );
+ aCharSet.Put( *GetpSwAttrSet() );
+ if( aCharSet.Count() )
+ {
+ pDest->SetAttr( aCharSet, nDestStart, nDestStart );
+ }
+ }
+ else
+ {
+ GetpSwAttrSet()->CopyToModify( *pDest );
+ }
+ }
+ return;
+ }
+
+ // 1. copy text
+ const sal_Int32 oldLen = pDest->m_Text.getLength();
+ // JP 15.02.96: missing attribute handling at the end!
+ // hence call InsertText and don't modify m_Text directly
+ pDest->InsertText( m_Text.copy(nTextStartIdx, nLen), rDestStart,
+ SwInsertFlags::EMPTYEXPAND );
+
+ // update with actual new size
+ nLen = pDest->m_Text.getLength() - oldLen;
+ if ( !nLen ) // string not longer?
+ return;
+
+ SwDoc* const pOtherDoc = (&pDest->GetDoc() != &GetDoc()) ? &pDest->GetDoc() : nullptr;
+
+ // copy hard attributes on whole paragraph
+ if( HasSwAttrSet() )
+ {
+ // i#96213 all or just the Char attributes?
+ if ( !bForceCopyOfAllAttrs &&
+ ( nDestStart ||
+ pDest->HasSwAttrSet() ||
+ nLen != pDest->GetText().getLength()))
+ {
+ SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
+ aCharSet( pDest->GetDoc().GetAttrPool() );
+ aCharSet.Put( *GetpSwAttrSet() );
+ if( aCharSet.Count() )
+ {
+ pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
+ }
+ }
+ else
+ {
+ GetpSwAttrSet()->CopyToModify( *pDest );
+ }
+ }
+
+ bool const bUndoNodes = !pOtherDoc
+ && GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
+
+ // Fetch end only now, because copying into self updates the start index
+ // and all attributes
+ nTextStartIdx = rStart.GetIndex();
+ const sal_Int32 nEnd = nTextStartIdx + nLen;
+
+ // 2. copy attributes
+ // Iterate over attribute array until the start of the attribute
+ // is behind the copied range
+ const size_t nSize = m_pSwpHints ? m_pSwpHints->Count() : 0;
+
+ // If copying into self, inserting can delete attributes!
+ // Hence first copy into temp-array, and then move that into hints array.
+ SwpHts aArr;
+
+ // Del-Array for all RefMarks without extent
+ SwpHts aRefMrkArr;
+
+ std::vector<std::pair<sal_Int32, sal_Int32>> metaFieldRanges;
+ sal_Int32 nDeletedDummyChars(0);
+ for (size_t n = 0; n < nSize; ++n)
+ {
+ SwTextAttr * const pHt = m_pSwpHints->Get(n);
+
+ const sal_Int32 nAttrStartIdx = pHt->GetStart();
+ if ( nAttrStartIdx >= nEnd )
+ break;
+
+ const sal_Int32 * const pEndIdx = pHt->GetEnd();
+ const sal_uInt16 nWhich = pHt->Which();
+
+ // JP 26.04.94: RefMarks are never copied. If the refmark doesn't have
+ // an extent, there is a dummy char in the text, which
+ // must be removed. So we first copy the attribute,
+ // but remember it, and when we're done delete it,
+ // which also deletes the dummy character!
+ // JP 14.08.95: May RefMarks be moved?
+ const bool bCopyRefMark = RES_TXTATR_REFMARK == nWhich
+ && ( bUndoNodes
+ || ( !pOtherDoc
+ ? GetDoc().IsCopyIsMove()
+ : nullptr == pOtherDoc->GetRefMark( pHt->GetRefMark().GetRefName() ) ) );
+
+ if ( pEndIdx
+ && RES_TXTATR_REFMARK == nWhich
+ && !bCopyRefMark )
+ {
+ continue;
+ }
+
+ // Input Fields are only copied, if completely covered by copied text
+ if ( nWhich == RES_TXTATR_INPUTFIELD )
+ {
+ assert(pEndIdx != nullptr &&
+ "<SwTextNode::CopyText(..)> - RES_TXTATR_INPUTFIELD without EndIndex!" );
+ if ( nAttrStartIdx < nTextStartIdx
+ || ( pEndIdx != nullptr
+ && *pEndIdx > nEnd ) )
+ {
+ continue;
+ }
+ }
+
+ if (nWhich == RES_TXTATR_METAFIELD)
+ {
+ // Skip metadata fields. Also remember the range to strip the text later.
+ metaFieldRanges.emplace_back(nAttrStartIdx, pEndIdx ? *pEndIdx : nEnd);
+ continue;
+ }
+
+ sal_Int32 nAttrStt = 0;
+ sal_Int32 nAttrEnd = 0;
+
+ if( nAttrStartIdx < nTextStartIdx )
+ {
+ // start is before selection
+ // copy hints with end and CH_TXTATR only if dummy char is copied
+ if ( pEndIdx && (*pEndIdx > nTextStartIdx) && !pHt->HasDummyChar() )
+ {
+ // attribute with extent and the end is in the selection
+ nAttrStt = nDestStart;
+ nAttrEnd = (*pEndIdx > nEnd)
+ ? rDestStart.GetIndex()
+ : nDestStart + (*pEndIdx) - nTextStartIdx;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else
+ {
+ // start is in the selection
+ nAttrStt = nDestStart + ( nAttrStartIdx - nTextStartIdx );
+ if( pEndIdx )
+ {
+ nAttrEnd = *pEndIdx > nEnd
+ ? rDestStart.GetIndex()
+ : nDestStart + ( *pEndIdx - nTextStartIdx );
+ }
+ else
+ {
+ nAttrEnd = nAttrStt;
+ }
+ }
+
+ SwTextAttr * pNewHt = nullptr;
+
+ if( pDest == this )
+ {
+ // copy the hint here, but insert it later
+ pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
+ nAttrStt, nAttrEnd, CopyOrNewType::Copy, pDest );
+
+ lcl_CopyHint(nWhich, pHt, pNewHt, nullptr, pDest);
+ aArr.push_back( pNewHt );
+ }
+ else
+ {
+ pNewHt = pDest->InsertItem(
+ pHt->GetAttr(),
+ nAttrStt - nDeletedDummyChars,
+ nAttrEnd - nDeletedDummyChars,
+ SetAttrMode::NOTXTATRCHR | SetAttrMode::IS_COPY);
+ if (pNewHt)
+ {
+ lcl_CopyHint( nWhich, pHt, pNewHt, pOtherDoc, pDest );
+ }
+ else if (pHt->HasDummyChar())
+ {
+ // The attribute that has failed to be copied would insert
+ // dummy char, so positions of the following attributes have
+ // to be shifted by one to compensate for that missing char.
+ ++nDeletedDummyChars;
+ }
+ }
+
+ if( RES_TXTATR_REFMARK == nWhich && !pEndIdx && !bCopyRefMark )
+ {
+ aRefMrkArr.push_back( pNewHt );
+ }
+ }
+
+ // Strip the metadata fields, since we don't copy the RDF entries
+ // yet and so they are inconsistent upon copy/pasting.
+ if (!metaFieldRanges.empty())
+ {
+ // Reverse to remove without messing the offsets.
+ std::reverse(metaFieldRanges.begin(), metaFieldRanges.end());
+ for (const auto& pair : metaFieldRanges)
+ {
+ const SwIndex aIdx(pDest, pair.first);
+ pDest->EraseText(aIdx, pair.second - pair.first);
+ }
+ }
+
+ // this can only happen when copying into self
+ for (SwTextAttr* i : aArr)
+ {
+ InsertHint( i, SetAttrMode::NOTXTATRCHR );
+ }
+
+ if( pDest->GetpSwpHints() )
+ {
+ for (SwTextAttr* pNewHt : aRefMrkArr)
+ {
+ if( pNewHt->GetEnd() )
+ {
+ pDest->GetpSwpHints()->Delete( pNewHt );
+ pDest->DestroyAttr( pNewHt );
+ }
+ else
+ {
+ const SwIndex aIdx( pDest, pNewHt->GetStart() );
+ pDest->EraseText( aIdx, 1 );
+ }
+ }
+ }
+
+ CHECK_SWPHINTS_IF_FRM(this);
+ CHECK_SWPHINTS(pDest);
+}
+
+OUString SwTextNode::InsertText( const OUString & rStr, const SwIndex & rIdx,
+ const SwInsertFlags nMode )
+{
+ assert(rIdx <= m_Text.getLength()); // invalid index
+
+ const sal_Int32 aPos = rIdx.GetIndex();
+ sal_Int32 nLen = m_Text.getLength() - aPos;
+ sal_Int32 const nOverflow(rStr.getLength() - GetSpaceLeft());
+ SAL_WARN_IF(nOverflow > 0, "sw.core",
+ "SwTextNode::InsertText: node text with insertion > capacity.");
+ OUString const sInserted(
+ (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
+ if (sInserted.isEmpty())
+ {
+ return sInserted;
+ }
+ m_Text = m_Text.replaceAt(aPos, 0, sInserted);
+ assert(GetSpaceLeft()>=0);
+ nLen = m_Text.getLength() - aPos - nLen;
+ assert(nLen != 0);
+
+ bool bOldExpFlg = IsIgnoreDontExpand();
+ if (nMode & SwInsertFlags::FORCEHINTEXPAND)
+ {
+ SetIgnoreDontExpand( true );
+ }
+
+ Update( rIdx, nLen ); // text content changed!
+
+ if (nMode & SwInsertFlags::FORCEHINTEXPAND)
+ {
+ SetIgnoreDontExpand( bOldExpFlg );
+ }
+
+ if ( HasWriterListeners() )
+ { // send this before messing with hints, which will send RES_UPDATE_ATTR
+ SwInsText const aHint(sw::MakeSwInsText(*this, aPos, nLen));
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ }
+
+ if ( HasHints() )
+ {
+ m_pSwpHints->SortIfNeedBe();
+ bool const bHadHints(!m_pSwpHints->CanBeDeleted());
+ bool bMergePortionsNeeded(false);
+ for ( size_t i = 0; i < m_pSwpHints->Count() &&
+ rIdx >= m_pSwpHints->GetWithoutResorting(i)->GetStart(); ++i )
+ {
+ SwTextAttr * const pHt = m_pSwpHints->GetWithoutResorting( i );
+ const sal_Int32 * const pEndIdx = pHt->GetEnd();
+ if( !pEndIdx )
+ continue;
+
+ if( rIdx == *pEndIdx )
+ {
+ if ( (nMode & SwInsertFlags::NOHINTEXPAND) ||
+ (!(nMode & SwInsertFlags::FORCEHINTEXPAND)
+ && pHt->DontExpand()) )
+ {
+ m_pSwpHints->DeleteAtPos(i);
+ // on empty attributes also adjust Start
+ if( rIdx == pHt->GetStart() )
+ pHt->SetStart( pHt->GetStart() - nLen );
+ pHt->SetEnd(*pEndIdx - nLen);
+ // could be that pHt has IsFormatIgnoreEnd set, and it's
+ // not a RSID-only hint - now we have the inserted text
+ // between pHt and its continuation... which we don't know.
+ // punt the job to MergePortions below.
+ if (pHt->IsFormatIgnoreEnd())
+ {
+ bMergePortionsNeeded = true;
+ }
+ InsertHint( pHt, SetAttrMode::NOHINTADJUST );
+ }
+ // empty hints at insert position?
+ else if ( (nMode & SwInsertFlags::EMPTYEXPAND)
+ && (*pEndIdx == pHt->GetStart()) )
+ {
+ m_pSwpHints->DeleteAtPos(i);
+ pHt->SetStart( pHt->GetStart() - nLen );
+ const size_t nCurrentLen = m_pSwpHints->Count();
+ InsertHint( pHt/* AUTOSTYLES:, SetAttrMode::NOHINTADJUST*/ );
+ if ( nCurrentLen > m_pSwpHints->Count() && i )
+ {
+ --i;
+ }
+ continue;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ if ( !(nMode & SwInsertFlags::NOHINTEXPAND) &&
+ rIdx == nLen && pHt->GetStart() == rIdx.GetIndex() &&
+ !pHt->IsDontExpandStartAttr() )
+ {
+ // no field, at paragraph start, HintExpand
+ m_pSwpHints->DeleteAtPos(i);
+ pHt->SetStart( pHt->GetStart() - nLen );
+ // no effect on format ignore flags here (para start)
+ InsertHint( pHt, SetAttrMode::NOHINTADJUST );
+ }
+ }
+ if (bMergePortionsNeeded)
+ {
+ m_pSwpHints->MergePortions(*this);
+ }
+ SAL_WARN_IF(bHadHints && m_pSwpHints->CanBeDeleted(), "sw.core",
+ "SwTextNode::InsertText: unexpected loss of hints");
+ }
+
+ // By inserting a character, the hidden flags
+ // at the TextNode can become invalid:
+ SetCalcHiddenCharFlags();
+
+ CHECK_SWPHINTS(this);
+ return sInserted;
+}
+
+void SwTextNode::CutText( SwTextNode * const pDest,
+ const SwIndex & rStart, const sal_Int32 nLen )
+{
+ assert(pDest); // Cut requires a destination
+ SwIndex aDestStt(pDest, pDest->GetText().getLength());
+ CutImpl( pDest, aDestStt, rStart, nLen, false );
+}
+
+void SwTextNode::CutImpl( SwTextNode * const pDest, const SwIndex & rDestStart,
+ const SwIndex & rStart, sal_Int32 nLen, const bool bUpdate )
+{
+ assert(pDest); // Cut requires a destination
+
+ assert(&GetDoc() == &pDest->GetDoc()); // must be same document
+
+ assert(pDest != this); // destination must be different node
+
+ if( !nLen )
+ {
+ // if no length is given, copy attributes at position rStart
+ CopyAttr( pDest, rStart.GetIndex(), rDestStart.GetIndex() );
+ return;
+ }
+
+ sal_Int32 nTextStartIdx = rStart.GetIndex();
+ sal_Int32 nDestStart = rDestStart.GetIndex(); // remember old Pos
+ const sal_Int32 nInitSize = pDest->m_Text.getLength();
+
+ if (pDest->GetSpaceLeft() < nLen)
+ { // FIXME: could only happen when called from SwRangeRedline::Show.
+ // unfortunately can't really do anything here to handle that...
+ abort();
+ }
+ pDest->m_Text = pDest->m_Text.replaceAt(nDestStart, 0,
+ m_Text.subView(nTextStartIdx, nLen));
+ OUString const newText = m_Text.replaceAt(nTextStartIdx, nLen, u"");
+ nLen = pDest->m_Text.getLength() - nInitSize; // update w/ current size!
+ if (!nLen) // String didn't grow?
+ return;
+
+ if (bUpdate)
+ {
+ // Update all SwIndex
+ pDest->Update( rDestStart, nLen, false, false/*??? why was it true*/);
+ }
+
+ CHECK_SWPHINTS(pDest);
+
+ const sal_Int32 nEnd = rStart.GetIndex() + nLen;
+ bool const bUndoNodes =
+ GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
+
+ // copy hard attributes on whole paragraph
+ if (HasSwAttrSet())
+ {
+ bool hasSwAttrSet = pDest->HasSwAttrSet();
+ if (hasSwAttrSet)
+ {
+ // if we have our own property set it doesn't mean
+ // that this set defines any style different to Standard one.
+ hasSwAttrSet = false;
+
+ // so, let's check deeper if property set has defined any property
+ if (pDest->GetpSwAttrSet())
+ {
+ // check all items in the property set
+ SfxItemIter aIter( *pDest->GetpSwAttrSet() );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ // check current item
+ const sal_uInt16 nWhich = IsInvalidItem( pItem )
+ ? pDest->GetpSwAttrSet()->GetWhichByPos( aIter.GetCurPos() )
+ : pItem->Which();
+ if( RES_FRMATR_STYLE_NAME != nWhich &&
+ RES_FRMATR_CONDITIONAL_STYLE_NAME != nWhich &&
+ RES_PAGEDESC != nWhich &&
+ RES_BREAK != nWhich &&
+ SfxItemState::SET == pDest->GetpSwAttrSet()->GetItemState( nWhich, false ) )
+ {
+ // check if parent value (original value in style) has the same value as in [pItem]
+ const SfxPoolItem& rParentItem = pDest->GetpSwAttrSet()->GetParent()->Get( nWhich, true );
+
+ hasSwAttrSet = (rParentItem != *pItem);
+
+ // property set is not empty => no need to make anymore checks
+ if (hasSwAttrSet)
+ break;
+ }
+
+ // let's check next item
+ pItem = aIter.NextItem();
+ } while (pItem);
+ }
+ }
+
+ // all or just the Char attributes?
+ if( nInitSize || hasSwAttrSet ||
+ nLen != pDest->GetText().getLength())
+ {
+ SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_CHRATR_END - 1,
+ RES_TXTATR_INETFMT, RES_TXTATR_CHARFMT,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1>
+ aCharSet( pDest->GetDoc().GetAttrPool() );
+ aCharSet.Put( *GetpSwAttrSet() );
+ if( aCharSet.Count() )
+ pDest->SetAttr( aCharSet, nDestStart, nDestStart + nLen );
+ }
+ else
+ {
+ // Copy all attrs except RES_PARATR_LIST_LEVEL: it was initialized before
+ // and current SwTextNode can contain not suitable for pDest value
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_PARATR_LIST_LEVEL - 1,
+ RES_PARATR_LIST_LEVEL + 1, HINT_END>
+ aCharSet(pDest->GetDoc().GetAttrPool());
+ aCharSet.Put(*GetpSwAttrSet());
+ if (aCharSet.Count())
+ pDest->SetAttr(aCharSet, nDestStart, nDestStart + nLen);
+ }
+ }
+
+ // notify frames - before moving hints, because footnotes
+ // want to find their anchor text frame in the follow chain
+ // (also ignore fieldmarks, the caller will recreate frames)
+ SwInsText const aInsHint(nDestStart, nLen, false, false);
+ pDest->TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, &aInsHint));
+ const sw::MoveText aMoveHint(pDest, nDestStart, nTextStartIdx, nLen);
+ CallSwClientNotify(aMoveHint);
+ const SwDelText aDelHint(nTextStartIdx, nLen);
+ TriggerNodeUpdate(sw::LegacyModifyHint(nullptr, &aDelHint));
+
+ // 2. move attributes
+ // Iterate over attribute array until the start of the attribute
+ // is behind the moved range
+ bool bMergePortionsNeeded(false);
+ size_t nAttrCnt = 0;
+ while (m_pSwpHints && (nAttrCnt < m_pSwpHints->Count()))
+ {
+ SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
+ const sal_Int32 nAttrStartIdx = pHt->GetStart();
+ if ( nAttrStartIdx >= nEnd )
+ break;
+ const sal_Int32 * const pEndIdx = pHt->GetEnd();
+ const sal_uInt16 nWhich = pHt->Which();
+ SwTextAttr *pNewHt = nullptr;
+
+ // if the hint has a dummy character, then it must not be split!
+ if(nAttrStartIdx < nTextStartIdx)
+ {
+ // start is before the range
+ if (!pHt->HasDummyChar() && ( RES_TXTATR_REFMARK != nWhich
+ || bUndoNodes ) && pEndIdx && *pEndIdx > nTextStartIdx)
+ {
+ // attribute with extent and end of attribute is in the range
+ pNewHt = MakeTextAttr( pDest->GetDoc(), pHt->GetAttr(),
+ nDestStart,
+ nDestStart + (
+ *pEndIdx > nEnd
+ ? nLen
+ : *pEndIdx - nTextStartIdx ) );
+ }
+ }
+ else
+ {
+ // start is inside the range
+ if (!pEndIdx || *pEndIdx < nEnd ||
+ (!bUndoNodes && RES_TXTATR_REFMARK == nWhich)
+ || pHt->HasDummyChar() )
+ {
+ // do not delete note and later add it -> sidebar flickering
+ if (GetDoc().GetDocShell())
+ {
+ GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
+ }
+ // move attribute
+ m_pSwpHints->Delete( pHt );
+ // reset start/end indexes
+ if (pHt->IsFormatIgnoreStart() || pHt->IsFormatIgnoreEnd())
+ {
+ bMergePortionsNeeded = true;
+ }
+ pHt->SetStart(nDestStart + (nAttrStartIdx - nTextStartIdx));
+ if (pEndIdx)
+ {
+ pHt->SetEnd( nDestStart + (
+ *pEndIdx > nEnd
+ ? nLen
+ : *pEndIdx - nTextStartIdx ) );
+ }
+ pDest->InsertHint( pHt,
+ SetAttrMode::NOTXTATRCHR
+ | SetAttrMode::DONTREPLACE );
+ if (GetDoc().GetDocShell())
+ {
+ GetDoc().GetDocShell()->Broadcast( SfxHint(SfxHintId::SwSplitNodeOperation));
+ }
+ continue; // iterate while loop, no ++ !
+ }
+ // the end is behind the range
+ else if (RES_TXTATR_REFMARK != nWhich || bUndoNodes)
+ {
+ pNewHt = MakeTextAttr( GetDoc(), pHt->GetAttr(),
+ nDestStart + (nAttrStartIdx - nTextStartIdx),
+ nDestStart + (*pEndIdx > nEnd
+ ? nLen
+ : *pEndIdx - nTextStartIdx));
+ }
+ }
+ if (pNewHt)
+ {
+ const bool bSuccess( pDest->InsertHint( pNewHt,
+ SetAttrMode::NOTXTATRCHR
+ | SetAttrMode::DONTREPLACE
+ | SetAttrMode::IS_COPY) );
+ if (bSuccess)
+ {
+ lcl_CopyHint( nWhich, pHt, pNewHt, nullptr, pDest );
+ }
+ }
+ ++nAttrCnt;
+ }
+ // If there are still empty attributes around, they have a higher priority
+ // than any attributes that become empty due to the move.
+ // So temporarily remove them and Update the array, then re-insert the
+ // removed ones so they overwrite the others.
+ if (m_pSwpHints && nAttrCnt < m_pSwpHints->Count())
+ {
+ SwpHts aArr;
+ while (nAttrCnt < m_pSwpHints->Count())
+ {
+ SwTextAttr * const pHt = m_pSwpHints->Get(nAttrCnt);
+ if (nEnd != pHt->GetStart())
+ break;
+ const sal_Int32 * const pEndIdx = pHt->GetEnd();
+ if (pEndIdx && *pEndIdx == nEnd)
+ {
+ aArr.push_back( pHt );
+ m_pSwpHints->Delete( pHt );
+ }
+ else
+ {
+ ++nAttrCnt;
+ }
+ }
+ Update( rStart, nLen, true, true );
+
+ for (SwTextAttr* pHt : aArr)
+ {
+ pHt->SetStart( rStart.GetIndex() );
+ pHt->SetEnd( rStart.GetIndex() );
+ InsertHint( pHt );
+ }
+ }
+ else
+ {
+ Update( rStart, nLen, true, true );
+ }
+
+ // set after moving hints
+ m_Text = newText;
+
+ if (bMergePortionsNeeded)
+ {
+ m_pSwpHints->MergePortions(*this);
+ }
+
+ CHECK_SWPHINTS(this);
+
+ TryDeleteSwpHints();
+}
+
+void SwTextNode::EraseText(const SwIndex &rIdx, const sal_Int32 nCount,
+ const SwInsertFlags nMode )
+{
+ assert(rIdx <= m_Text.getLength()); // invalid index
+
+ const sal_Int32 nStartIdx = rIdx.GetIndex();
+ const sal_Int32 nCnt = (nCount==SAL_MAX_INT32)
+ ? m_Text.getLength() - nStartIdx : nCount;
+ const sal_Int32 nEndIdx = nStartIdx + nCnt;
+ if (nEndIdx <= m_Text.getLength())
+ m_Text = m_Text.replaceAt(nStartIdx, nCnt, u"");
+
+ // GCAttr(); don't remove all empty ones, just the ones that are in the
+ // range but not at the end of the range.
+
+ for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
+ {
+ SwTextAttr *pHt = m_pSwpHints->Get(i);
+
+ const sal_Int32 nHintStart = pHt->GetStart();
+
+ if ( nHintStart < nStartIdx )
+ continue;
+
+ if ( nHintStart > nEndIdx )
+ break; // hints are sorted by end, so break here
+
+ const sal_Int32* pHtEndIdx = pHt->GetEnd();
+ const sal_uInt16 nWhich = pHt->Which();
+
+ if( !pHtEndIdx )
+ {
+ // attribute with neither end nor CH_TXTATR?
+ assert(pHt->HasDummyChar());
+ if (isTXTATR(nWhich) && (nHintStart < nEndIdx))
+ {
+ m_pSwpHints->DeleteAtPos(i);
+ DestroyAttr( pHt );
+ --i;
+ }
+ continue;
+ }
+
+ assert(!( (nHintStart < nEndIdx) && (*pHtEndIdx > nEndIdx)
+ && pHt->HasDummyChar() )
+ // next line: deleting exactly dummy char: DeleteAttributes
+ || ((nHintStart == nStartIdx) && (nHintStart + 1 == nEndIdx)));
+ // "ERROR: deleting left-overlapped attribute with CH_TXTATR");
+
+ // Delete the hint if:
+ // 1. The hint ends before the deletion end position or
+ // 2. The hint ends at the deletion end position and
+ // we are not in empty expand mode and
+ // the hint is a [toxmark|refmark|ruby|inputfield] text attribute
+ // 3. deleting exactly the dummy char of an hint with end and dummy
+ // char deletes the hint
+ if ( (*pHtEndIdx < nEndIdx)
+ || ( (*pHtEndIdx == nEndIdx) &&
+ !(SwInsertFlags::EMPTYEXPAND & nMode) &&
+ ( (RES_TXTATR_TOXMARK == nWhich) ||
+ (RES_TXTATR_REFMARK == nWhich) ||
+ (RES_TXTATR_CJK_RUBY == nWhich) ||
+ (RES_TXTATR_INPUTFIELD == nWhich) ) )
+ || ( (nHintStart < nEndIdx) &&
+ pHt->HasDummyChar() )
+ )
+ {
+ m_pSwpHints->DeleteAtPos(i);
+ DestroyAttr( pHt );
+ --i;
+ }
+ }
+
+ OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
+
+ TryDeleteSwpHints();
+
+ Update( rIdx, nCnt, true );
+
+ if( 1 == nCnt )
+ {
+ SwDelChr aHint( nStartIdx );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ }
+ else
+ {
+ SwDelText aHint( nStartIdx, nCnt );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ }
+
+ OSL_ENSURE(rIdx.GetIndex() == nStartIdx, "huh? start index has changed?");
+
+ // By deleting a character, the hidden flags
+ // at the TextNode can become invalid:
+ SetCalcHiddenCharFlags();
+
+ CHECK_SWPHINTS(this);
+}
+
+void SwTextNode::GCAttr()
+{
+ if ( !HasHints() )
+ return;
+
+ bool bChanged = false;
+ sal_Int32 nMin = m_Text.getLength();
+ sal_Int32 nMax = 0;
+ const bool bAll = nMin != 0; // on empty paragraphs only remove INetFormats
+
+ for ( size_t i = 0; m_pSwpHints && i < m_pSwpHints->Count(); ++i )
+ {
+ SwTextAttr * const pHt = m_pSwpHints->Get(i);
+
+ // if end and start are equal, delete it
+ const sal_Int32 * const pEndIdx = pHt->GetEnd();
+ if (pEndIdx && !pHt->HasDummyChar() && (*pEndIdx == pHt->GetStart())
+ && ( bAll || pHt->Which() == RES_TXTATR_INETFMT ) )
+ {
+ bChanged = true;
+ nMin = std::min( nMin, pHt->GetStart() );
+ nMax = std::max( nMax, *pHt->GetEnd() );
+ DestroyAttr( m_pSwpHints->Cut(i) );
+ --i;
+ }
+ else
+ {
+ pHt->SetDontExpand( false );
+ }
+ }
+ TryDeleteSwpHints();
+
+ if(bChanged)
+ {
+ // textframes react to aHint, others to aNew
+ SwUpdateAttr aHint(
+ nMin,
+ nMax,
+ 0);
+
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ SwFormatChg aNew( GetTextColl() );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
+ }
+}
+
+SwNumRule* SwTextNode::GetNumRule(bool bInParent) const
+{
+ SwNumRule* pRet = nullptr;
+
+ const SfxPoolItem* pItem = GetNoCondAttr( RES_PARATR_NUMRULE, bInParent );
+ bool bNoNumRule = false;
+ if ( pItem )
+ {
+ OUString sNumRuleName =
+ static_cast<const SwNumRuleItem *>(pItem)->GetValue();
+ if (!sNumRuleName.isEmpty())
+ {
+ pRet = GetDoc().FindNumRulePtr( sNumRuleName );
+ }
+ else // numbering is turned off
+ bNoNumRule = true;
+ }
+
+ if ( !bNoNumRule )
+ {
+ if ( pRet && pRet == GetDoc().GetOutlineNumRule() &&
+ ( !HasSwAttrSet() ||
+ SfxItemState::SET !=
+ GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) ) )
+ {
+ SwTextFormatColl* pColl = GetTextColl();
+ if ( pColl )
+ {
+ const SwNumRuleItem& rDirectItem = pColl->GetNumRule( false );
+ if ( rDirectItem.GetValue().isEmpty() )
+ {
+ pRet = nullptr;
+ }
+ }
+ }
+ }
+
+ return pRet;
+}
+
+void SwTextNode::NumRuleChgd()
+{
+ if ( IsInList() )
+ {
+ SwNumRule* pNumRule = GetNumRule();
+ if ( pNumRule && pNumRule != GetNum()->GetNumRule() )
+ {
+ mpNodeNum->ChangeNumRule( *pNumRule );
+ if (mpNodeNumRLHidden)
+ {
+ mpNodeNumRLHidden->ChangeNumRule(*pNumRule);
+ }
+ }
+ }
+
+ // Sending "noop" modify in order to cause invalidations of registered
+ // <SwTextFrame> instances to get the list style change respectively the change
+ // in the list tree reflected in the layout.
+ // Important note:
+ {
+ SvxLRSpaceItem& rLR = const_cast<SvxLRSpaceItem&>(GetSwAttrSet().GetLRSpace());
+ CallSwClientNotify(sw::LegacyModifyHint(&rLR, &rLR));
+ }
+
+ SetWordCountDirty( true );
+}
+
+// -> #i27615#
+bool SwTextNode::IsNumbered(SwRootFrame const*const pLayout) const
+{
+ SwNumRule* pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
+ return pRule && IsCountedInList();
+}
+
+bool SwTextNode::HasMarkedLabel() const
+{
+ bool bResult = false;
+
+ if ( IsInList() )
+ {
+ bResult =
+ GetDoc().getIDocumentListsAccess().getListByName( GetListId() )->IsListLevelMarked( GetActualListLevel() );
+ }
+
+ return bResult;
+}
+// <- #i27615#
+
+SwTextNode* SwTextNode::MakeNewTextNode( const SwNodeIndex& rPos, bool bNext,
+ bool bChgFollow )
+{
+ // ignore hard PageBreak/PageDesc/ColumnBreak from Auto-Set
+ std::unique_ptr<SwAttrSet> pNewAttrSet;
+ // #i75353#
+ bool bClearHardSetNumRuleWhenFormatCollChanges( false );
+ if( HasSwAttrSet() )
+ {
+ pNewAttrSet.reset(new SwAttrSet( *GetpSwAttrSet() ));
+ const SfxItemSet* pTmpSet = GetpSwAttrSet();
+
+ if (bNext) // successor doesn't inherit breaks!
+ pTmpSet = pNewAttrSet.get();
+
+ // !bNext: remove PageBreaks/PageDesc/ColBreak from this
+ bool bRemoveFromCache = false;
+ std::vector<sal_uInt16> aClearWhichIds;
+ if ( bNext )
+ bRemoveFromCache = ( 0 != pNewAttrSet->ClearItem( RES_PAGEDESC ) );
+ else
+ aClearWhichIds.push_back( RES_PAGEDESC );
+
+ if( SfxItemState::SET == pTmpSet->GetItemState( RES_BREAK, false ) )
+ {
+ if ( bNext )
+ pNewAttrSet->ClearItem( RES_BREAK );
+ else
+ aClearWhichIds.push_back( RES_BREAK );
+ bRemoveFromCache = true;
+ }
+ if( SfxItemState::SET == pTmpSet->GetItemState( RES_KEEP, false ) )
+ {
+ if ( bNext )
+ pNewAttrSet->ClearItem( RES_KEEP );
+ else
+ aClearWhichIds.push_back( RES_KEEP );
+ bRemoveFromCache = true;
+ }
+ if( SfxItemState::SET == pTmpSet->GetItemState( RES_PARATR_SPLIT, false ) )
+ {
+ if ( bNext )
+ pNewAttrSet->ClearItem( RES_PARATR_SPLIT );
+ else
+ aClearWhichIds.push_back( RES_PARATR_SPLIT );
+ bRemoveFromCache = true;
+ }
+ if(SfxItemState::SET == pTmpSet->GetItemState(RES_PARATR_NUMRULE, false))
+ {
+ SwNumRule * pRule = GetNumRule();
+
+ if (pRule && IsOutline())
+ {
+ if ( bNext )
+ pNewAttrSet->ClearItem(RES_PARATR_NUMRULE);
+ else
+ {
+ // #i75353#
+ // No clear of hard set numbering rule at an outline paragraph at this point.
+ // Only if the paragraph style changes - see below.
+ bClearHardSetNumRuleWhenFormatCollChanges = true;
+ }
+ bRemoveFromCache = true;
+ }
+ }
+
+ if ( !aClearWhichIds.empty() )
+ bRemoveFromCache = 0 != ClearItemsFromAttrSet( aClearWhichIds );
+
+ if( !bNext && bRemoveFromCache )
+ {
+ InvalidateInSwCache(RES_OBJECTDYING);
+ }
+ }
+ SwNodes& rNds = GetNodes();
+
+ SwTextFormatColl* pColl = GetTextColl();
+
+ SwTextNode *pNode = new SwTextNode( rPos, pColl, pNewAttrSet.get() );
+
+ pNewAttrSet.reset();
+
+ const SwNumRule* pRule = GetNumRule();
+ if( pRule && pRule == pNode->GetNumRule() && rNds.IsDocNodes() )
+ {
+ // #i55459#
+ // - correction: parameter <bNext> has to be checked, as it was in the
+ // previous implementation.
+ if ( !bNext && !IsCountedInList() )
+ SetCountedInList(true);
+ }
+
+ // In case the numbering caused a style from the pool to be assigned to
+ // the new node, don't overwrite that here!
+ if( pColl != pNode->GetTextColl() ||
+ ( bChgFollow && pColl != GetTextColl() ))
+ return pNode; // that ought to be enough?
+
+ pNode->ChgTextCollUpdateNum( nullptr, pColl ); // for numbering/outline
+ if( bNext || !bChgFollow )
+ return pNode;
+
+ SwTextFormatColl *pNextColl = &pColl->GetNextTextFormatColl();
+ // i#101870 perform action on different paragraph styles before applying
+ // the new paragraph style
+ if (pNextColl != pColl)
+ {
+ // #i75353#
+ if ( bClearHardSetNumRuleWhenFormatCollChanges )
+ {
+ if ( ClearItemsFromAttrSet( { RES_PARATR_NUMRULE } ) != 0 )
+ {
+ InvalidateInSwCache(RES_ATTRSET_CHG);
+ }
+ }
+ }
+ ChgFormatColl( pNextColl );
+
+ return pNode;
+}
+
+SwContentNode* SwTextNode::AppendNode( const SwPosition & rPos )
+{
+ // position behind which it will be inserted
+ SwNodeIndex aIdx( rPos.nNode, 1 );
+ SwTextNode* pNew = MakeNewTextNode( aIdx );
+
+ // reset list attributes at appended text node
+ pNew->ResetAttr( RES_PARATR_LIST_ISRESTART );
+ pNew->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ pNew->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+ if ( pNew->GetNumRule() == nullptr )
+ {
+ pNew->ResetAttr( RES_PARATR_LIST_ID );
+ pNew->ResetAttr( RES_PARATR_LIST_LEVEL );
+ }
+
+ if (!IsInList() && GetNumRule() && !GetListId().isEmpty())
+ {
+ AddToList();
+ }
+
+ if( HasWriterListeners() )
+ MakeFramesForAdjacentContentNode(*pNew);
+ return pNew;
+}
+
+SwTextAttr * SwTextNode::GetTextAttrForCharAt(
+ const sal_Int32 nIndex,
+ const sal_uInt16 nWhich ) const
+{
+ assert(nWhich >= RES_TXTATR_BEGIN && nWhich <= RES_TXTATR_END);
+ if ( HasHints() )
+ {
+ for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
+ {
+ SwTextAttr * const pHint = m_pSwpHints->Get(i);
+ const sal_Int32 nStartPos = pHint->GetStart();
+ if ( nIndex < nStartPos )
+ {
+ return nullptr;
+ }
+ if ( (nIndex == nStartPos) && pHint->HasDummyChar() )
+ {
+ return ( RES_TXTATR_END == nWhich || nWhich == pHint->Which() )
+ ? pHint : nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+SwTextAttr* SwTextNode::GetTextAttrForEndCharAt(sal_Int32 nIndex, sal_uInt16 nWhich) const
+{
+ SwTextAttr* pAttr = GetTextAttrAt(nIndex, nWhich, SwTextNode::EXPAND);
+ if (!pAttr)
+ {
+ return nullptr;
+ }
+
+ if (!pAttr->End())
+ {
+ return nullptr;
+ }
+
+ // The start-end range covers the end dummy character.
+ if (*pAttr->End() - 1 != nIndex)
+ {
+ return nullptr;
+ }
+
+ return pAttr;
+}
+
+namespace
+{
+
+sal_uInt16 lcl_BoundListLevel(const int nActualLevel)
+{
+ return o3tl::narrowing<sal_uInt16>( std::clamp( nActualLevel, 0, MAXLEVEL-1 ) );
+}
+
+}
+
+// -> #i29560#
+bool SwTextNode::HasNumber(SwRootFrame const*const pLayout) const
+{
+ bool bResult = false;
+
+ const SwNumRule *const pRule = GetNum(pLayout) ? GetNum(pLayout)->GetNumRule() : nullptr;
+ if ( pRule )
+ {
+ const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
+
+ // #i40041#
+ bResult = aFormat.IsEnumeration();
+ }
+
+ return bResult;
+}
+
+bool SwTextNode::HasBullet() const
+{
+ bool bResult = false;
+
+ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if ( pRule )
+ {
+ const SwNumFormat& aFormat(pRule->Get(lcl_BoundListLevel(GetActualListLevel())));
+
+ bResult = aFormat.IsItemize();
+ }
+
+ return bResult;
+}
+// <- #i29560#
+
+// #128041# - introduce parameter <_bInclPrefixAndSuffixStrings>
+//i53420 added max outline parameter
+OUString SwTextNode::GetNumString( const bool _bInclPrefixAndSuffixStrings,
+ const unsigned int _nRestrictToThisLevel,
+ SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
+{
+ if (GetDoc().IsClipBoard() && m_oNumStringCache)
+ {
+ // #i111677# do not expand number strings in clipboard documents
+ return *m_oNumStringCache;
+ }
+ const SwNumRule* pRule = GetNum(pLayout, eRedline) ? GetNum(pLayout, eRedline)->GetNumRule() : nullptr;
+ if ( pRule &&
+ IsCountedInList() )
+ {
+ SvxNumberType const& rNumberType(
+ pRule->Get( lcl_BoundListLevel(GetActualListLevel(eRedline)) ) );
+ if (rNumberType.IsTextFormat() ||
+
+ (style::NumberingType::NUMBER_NONE == rNumberType.GetNumberingType()))
+ {
+ return pRule->MakeNumString( GetNum(pLayout, eRedline)->GetNumberVector(),
+ _bInclPrefixAndSuffixStrings,
+ _nRestrictToThisLevel,
+ nullptr,
+ GetLang(0));
+ }
+ }
+
+ return OUString();
+}
+
+tools::Long SwTextNode::GetLeftMarginWithNum( bool bTextLeft ) const
+{
+ tools::Long nRet = 0;
+ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if( pRule )
+ {
+ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
+
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ nRet = rFormat.GetAbsLSpace();
+
+ if( !bTextLeft )
+ {
+ if( 0 > rFormat.GetFirstLineOffset() &&
+ nRet > -rFormat.GetFirstLineOffset() )
+ nRet = nRet + rFormat.GetFirstLineOffset();
+ else
+ nRet = 0;
+ }
+
+ if( pRule->IsAbsSpaces() )
+ nRet = nRet - GetSwAttrSet().GetLRSpace().GetLeft();
+ }
+ else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ if ( AreListLevelIndentsApplicable() )
+ {
+ nRet = rFormat.GetIndentAt();
+ // #i90401#
+ // Only negative first line indents have consider for the left margin
+ if ( !bTextLeft &&
+ rFormat.GetFirstLineIndent() < 0 )
+ {
+ nRet = nRet + rFormat.GetFirstLineIndent();
+ }
+ }
+ }
+ }
+
+ return nRet;
+}
+
+bool SwTextNode::GetFirstLineOfsWithNum( short& rFLOffset ) const
+{
+ // #i95907#
+ rFLOffset = 0;
+
+ // #i51089#
+ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if ( pRule )
+ {
+ if ( IsCountedInList() )
+ {
+ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ rFLOffset = rFormat.GetFirstLineOffset(); //TODO: overflow
+
+ if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
+ {
+ SvxLRSpaceItem aItem = GetSwAttrSet().GetLRSpace();
+ rFLOffset = rFLOffset + aItem.GetTextFirstLineOffset();
+ }
+ }
+ else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ if ( AreListLevelIndentsApplicable() )
+ {
+ rFLOffset = rFormat.GetFirstLineIndent();
+ }
+ else if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
+ {
+ SvxLRSpaceItem aItem = GetSwAttrSet().GetLRSpace();
+ rFLOffset = aItem.GetTextFirstLineOffset();
+ }
+ }
+ }
+
+ return true;
+ }
+
+ rFLOffset = GetSwAttrSet().GetLRSpace().GetTextFirstLineOffset();
+ return false;
+}
+
+SwTwips SwTextNode::GetAdditionalIndentForStartingNewList() const
+{
+ SwTwips nAdditionalIndent = 0;
+
+ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if ( pRule )
+ {
+ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
+
+ if (getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
+ {
+ nAdditionalIndent = nAdditionalIndent -
+ GetSwAttrSet().GetLRSpace().GetTextFirstLineOffset();
+ }
+ }
+ else if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ if ( AreListLevelIndentsApplicable() )
+ {
+ nAdditionalIndent = rFormat.GetIndentAt() + rFormat.GetFirstLineIndent();
+ }
+ else
+ {
+ nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
+ if (getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
+ {
+ nAdditionalIndent = nAdditionalIndent -
+ GetSwAttrSet().GetLRSpace().GetTextFirstLineOffset();
+ }
+ }
+ }
+ }
+ else
+ {
+ nAdditionalIndent = GetSwAttrSet().GetLRSpace().GetLeft();
+ }
+
+ return nAdditionalIndent;
+}
+
+// #i96772#
+void SwTextNode::ClearLRSpaceItemDueToListLevelIndents( std::shared_ptr<SvxLRSpaceItem>& o_rLRSpaceItem ) const
+{
+ if ( AreListLevelIndentsApplicable() )
+ {
+ const SwNumRule* pRule = GetNumRule();
+ if ( pRule && GetActualListLevel() >= 0 )
+ {
+ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ o_rLRSpaceItem = std::make_shared<SvxLRSpaceItem>(RES_LR_SPACE);
+ }
+ }
+ }
+}
+
+// #i91133#
+tools::Long SwTextNode::GetLeftMarginForTabCalculation() const
+{
+ tools::Long nLeftMarginForTabCalc = 0;
+
+ bool bLeftMarginForTabCalcSetToListLevelIndent( false );
+ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if( pRule )
+ {
+ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ if ( AreListLevelIndentsApplicable() )
+ {
+ nLeftMarginForTabCalc = rFormat.GetIndentAt();
+ bLeftMarginForTabCalcSetToListLevelIndent = true;
+ }
+ }
+ }
+ if ( !bLeftMarginForTabCalcSetToListLevelIndent )
+ {
+ nLeftMarginForTabCalc = GetSwAttrSet().GetLRSpace().GetTextLeft();
+ }
+
+ return nLeftMarginForTabCalc;
+}
+
+static void Replace0xFF(
+ SwTextNode const& rNode,
+ OUStringBuffer & rText,
+ sal_Int32 & rTextStt,
+ sal_Int32 nEndPos )
+{
+ if (!rNode.GetpSwpHints())
+ return;
+
+ sal_Unicode cSrchChr = CH_TXTATR_BREAKWORD;
+ for( int nSrchIter = 0; 2 > nSrchIter; ++nSrchIter, cSrchChr = CH_TXTATR_INWORD )
+ {
+ sal_Int32 nPos = rText.indexOf(cSrchChr);
+ while (-1 != nPos && nPos < nEndPos)
+ {
+ const SwTextAttr* const pAttr =
+ rNode.GetTextAttrForCharAt(rTextStt + nPos);
+ if( pAttr )
+ {
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ rText.remove(nPos, 1);
+ ++rTextStt;
+ break;
+
+ case RES_TXTATR_FTN:
+ rText.remove(nPos, 1);
+ ++rTextStt;
+ break;
+
+ default:
+ rText.remove(nPos, 1);
+ ++rTextStt;
+ }
+ }
+ else
+ {
+ ++nPos;
+ ++nEndPos;
+ }
+ nPos = rText.indexOf(cSrchChr, nPos);
+ }
+ }
+}
+
+// Expand fields
+// #i83479# - handling of new parameters
+OUString SwTextNode::GetExpandText(SwRootFrame const*const pLayout,
+ const sal_Int32 nIdx,
+ const sal_Int32 nLen,
+ const bool bWithNum,
+ const bool bAddSpaceAfterListLabelStr,
+ const bool bWithSpacesForLevel,
+ const ExpandMode eAdditionalMode) const
+
+{
+ ExpandMode eMode = ExpandMode::ExpandFields | eAdditionalMode;
+ if (pLayout && pLayout->IsHideRedlines())
+ {
+ eMode |= ExpandMode::HideDeletions;
+ }
+
+ ModelToViewHelper aConversionMap(*this, pLayout, eMode);
+ const OUString aExpandText = aConversionMap.getViewText();
+ const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nIdx );
+ sal_Int32 nEnd = nLen == -1 ? GetText().getLength() : nIdx + nLen;
+ const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nEnd );
+ OUStringBuffer aText(aExpandText.subView(nExpandBegin, nExpandEnd-nExpandBegin));
+
+ // remove dummy characters of Input Fields
+ comphelper::string::remove(aText, CH_TXT_ATR_INPUTFIELDSTART);
+ comphelper::string::remove(aText, CH_TXT_ATR_INPUTFIELDEND);
+ comphelper::string::remove(aText, CH_TXTATR_BREAKWORD);
+
+ if( bWithNum )
+ {
+ if (!GetNumString(true, MAXLEVEL, pLayout).isEmpty())
+ {
+ if ( bAddSpaceAfterListLabelStr )
+ {
+ const sal_Unicode aSpace = ' ';
+ aText.insert(0, aSpace);
+ }
+ aText.insert(0, GetNumString(true, MAXLEVEL, pLayout));
+ }
+ }
+
+ if (bWithSpacesForLevel)
+ {
+ const sal_Unicode aSpace = ' ';
+ for (int nLevel = GetActualListLevel(); nLevel > 0; --nLevel)
+ {
+ aText.insert(0, aSpace);
+ aText.insert(0, aSpace);
+ }
+ }
+
+ return aText.makeStringAndClear();
+}
+
+bool SwTextNode::CopyExpandText(SwTextNode& rDestNd, const SwIndex* pDestIdx,
+ sal_Int32 nIdx, sal_Int32 nLen,
+ SwRootFrame const*const pLayout, bool bWithNum,
+ bool bWithFootnote, bool bReplaceTabsWithSpaces ) const
+{
+ if( &rDestNd == this )
+ return false;
+
+ SwIndex aDestIdx(&rDestNd, rDestNd.GetText().getLength());
+ if( pDestIdx )
+ aDestIdx = *pDestIdx;
+ const sal_Int32 nDestStt = aDestIdx.GetIndex();
+
+ // first, start with the text
+ OUStringBuffer buf(GetText());
+ if( bReplaceTabsWithSpaces )
+ buf.replace('\t', ' ');
+
+ // mask hidden characters
+ const sal_Unicode cChar = CH_TXTATR_BREAKWORD;
+ SwScriptInfo::MaskHiddenRanges(*this, buf, 0, buf.getLength(), cChar);
+
+ buf.remove(0, nIdx);
+ if (nLen != -1)
+ {
+ buf.truncate(nLen);
+ }
+ // remove dummy characters of Input Fields
+ {
+ comphelper::string::remove(buf, CH_TXT_ATR_INPUTFIELDSTART);
+ comphelper::string::remove(buf, CH_TXT_ATR_INPUTFIELDEND);
+ }
+ rDestNd.InsertText(buf.makeStringAndClear(), aDestIdx);
+ nLen = aDestIdx.GetIndex() - nDestStt;
+
+ // set all char attributes with Symbol font
+ if ( HasHints() )
+ {
+ sal_Int32 nInsPos = nDestStt - nIdx;
+ for ( size_t i = 0; i < m_pSwpHints->Count(); ++i )
+ {
+ const SwTextAttr* pHt = m_pSwpHints->Get(i);
+ const sal_Int32 nAttrStartIdx = pHt->GetStart();
+ const sal_uInt16 nWhich = pHt->Which();
+ if (nIdx + nLen <= nAttrStartIdx)
+ break; // behind end of text
+
+ const sal_Int32 *pEndIdx = pHt->End();
+ if( pEndIdx && *pEndIdx > nIdx &&
+ ( RES_CHRATR_FONT == nWhich ||
+ RES_TXTATR_CHARFMT == nWhich ||
+ RES_TXTATR_AUTOFMT == nWhich ))
+ {
+ const SvxFontItem* const pFont =
+ CharFormat::GetItem( *pHt, RES_CHRATR_FONT );
+ if ( pFont && RTL_TEXTENCODING_SYMBOL == pFont->GetCharSet() )
+ {
+ // attribute in area => copy
+ rDestNd.InsertItem( *const_cast<SvxFontItem*>(pFont),
+ nInsPos + nAttrStartIdx, nInsPos + *pEndIdx );
+ }
+ }
+ else if ( pHt->HasDummyChar() && (nAttrStartIdx >= nIdx) )
+ {
+ aDestIdx = nInsPos + nAttrStartIdx;
+ switch( nWhich )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ {
+ OUString const aExpand(
+ static_txtattr_cast<SwTextField const*>(pHt)->GetFormatField().GetField()->ExpandField(true, pLayout));
+ if (!aExpand.isEmpty())
+ {
+ ++aDestIdx; // insert behind
+ OUString const ins(
+ rDestNd.InsertText( aExpand, aDestIdx));
+ SAL_INFO_IF(ins.getLength() != aExpand.getLength(),
+ "sw.core", "GetExpandText lossage");
+ aDestIdx = nInsPos + nAttrStartIdx;
+ nInsPos += ins.getLength();
+ }
+ rDestNd.EraseText( aDestIdx, 1 );
+ --nInsPos;
+ }
+ break;
+
+ case RES_TXTATR_FTN:
+ {
+ if ( bWithFootnote )
+ {
+ const SwFormatFootnote& rFootnote = pHt->GetFootnote();
+ OUString sExpand;
+ auto const number(pLayout && pLayout->IsHideRedlines()
+ ? rFootnote.GetNumberRLHidden()
+ : rFootnote.GetNumber());
+ if( !rFootnote.GetNumStr().isEmpty() )
+ sExpand = rFootnote.GetNumStr();
+ else if( rFootnote.IsEndNote() )
+ sExpand = GetDoc().GetEndNoteInfo().m_aFormat.
+ GetNumStr(number);
+ else
+ sExpand = GetDoc().GetFootnoteInfo().m_aFormat.
+ GetNumStr(number);
+ if( !sExpand.isEmpty() )
+ {
+ ++aDestIdx; // insert behind
+ SvxEscapementItem aItem( SvxEscapement::Superscript, RES_CHRATR_ESCAPEMENT );
+ rDestNd.InsertItem(
+ aItem,
+ aDestIdx.GetIndex(),
+ aDestIdx.GetIndex() );
+ OUString const ins( rDestNd.InsertText(sExpand, aDestIdx, SwInsertFlags::EMPTYEXPAND));
+ SAL_INFO_IF(ins.getLength() != sExpand.getLength(),
+ "sw.core", "GetExpandText lossage");
+ aDestIdx = nInsPos + nAttrStartIdx;
+ nInsPos += ins.getLength();
+ }
+ }
+ rDestNd.EraseText( aDestIdx, 1 );
+ --nInsPos;
+ }
+ break;
+
+ default:
+ rDestNd.EraseText( aDestIdx, 1 );
+ --nInsPos;
+ }
+ }
+ }
+ }
+
+ if( bWithNum )
+ {
+ aDestIdx = nDestStt;
+ rDestNd.InsertText( GetNumString(true, MAXLEVEL, pLayout), aDestIdx );
+ }
+
+ aDestIdx = 0;
+ sal_Int32 nStartDelete(-1);
+ while (aDestIdx < rDestNd.GetText().getLength())
+ {
+ sal_Unicode const cur(rDestNd.GetText()[aDestIdx.GetIndex()]);
+ if ( (cChar == cur) // filter substituted hidden text
+ || (CH_TXT_ATR_FIELDSTART == cur) // filter all fieldmarks
+ || (CH_TXT_ATR_FIELDSEP == cur)
+ || (CH_TXT_ATR_FIELDEND == cur)
+ || (CH_TXT_ATR_FORMELEMENT == cur))
+ {
+ if (-1 == nStartDelete)
+ {
+ nStartDelete = aDestIdx.GetIndex(); // start deletion range
+ }
+ ++aDestIdx;
+ if (aDestIdx < rDestNd.GetText().getLength())
+ {
+ continue;
+ } // else: end of paragraph => delete, see below
+ }
+ else
+ {
+ if (-1 == nStartDelete)
+ {
+ ++aDestIdx;
+ continue;
+ } // else: delete, see below
+ }
+ assert(-1 != nStartDelete); // without delete range, would have continued
+ rDestNd.EraseText(
+ SwIndex(&rDestNd, nStartDelete),
+ aDestIdx.GetIndex() - nStartDelete);
+ assert(aDestIdx.GetIndex() == nStartDelete);
+ nStartDelete = -1; // reset
+ }
+
+ return true;
+}
+
+OUString SwTextNode::GetRedlineText() const
+{
+ std::vector<sal_Int32> aRedlArr;
+ const SwDoc& rDoc = GetDoc();
+ SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *this, RedlineType::Delete );
+ if( SwRedlineTable::npos != nRedlPos )
+ {
+ // some redline-delete object exists for the node
+ const SwNodeOffset nNdIdx = GetIndex();
+ for( ; nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ; ++nRedlPos )
+ {
+ const SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ];
+ if( RedlineType::Delete == pTmp->GetType() )
+ {
+ const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
+ if( pRStt->nNode < nNdIdx )
+ {
+ if( pREnd->nNode > nNdIdx )
+ // paragraph is fully deleted
+ return OUString();
+ else if( pREnd->nNode == nNdIdx )
+ {
+ // deleted from 0 to nContent
+ aRedlArr.push_back( 0 );
+ aRedlArr.push_back( pREnd->nContent.GetIndex() );
+ }
+ }
+ else if( pRStt->nNode == nNdIdx )
+ {
+ //aRedlArr.Insert( pRStt->nContent.GetIndex(), aRedlArr.Count() );
+ aRedlArr.push_back( pRStt->nContent.GetIndex() );
+ if( pREnd->nNode == nNdIdx )
+ aRedlArr.push_back( pREnd->nContent.GetIndex() );
+ else
+ {
+ aRedlArr.push_back(GetText().getLength());
+ break; // that was all
+ }
+ }
+ else
+ break; // that was all
+ }
+ }
+ }
+
+ OUStringBuffer aText(GetText());
+
+ sal_Int32 nTextStt = 0;
+ sal_Int32 nIdxEnd = aText.getLength();
+ for( size_t n = 0; n < aRedlArr.size(); n += 2 )
+ {
+ sal_Int32 nStt = aRedlArr[ n ];
+ sal_Int32 nEnd = aRedlArr[ n+1 ];
+ if( ( 0 <= nStt && nStt <= nIdxEnd ) ||
+ ( 0 <= nEnd && nEnd <= nIdxEnd ))
+ {
+ if( nStt < 0 ) nStt = 0;
+ if( nIdxEnd < nEnd ) nEnd = nIdxEnd;
+ const sal_Int32 nDelCnt = nEnd - nStt;
+ aText.remove(nStt - nTextStt, nDelCnt);
+ Replace0xFF(*this, aText, nTextStt, nStt - nTextStt);
+ nTextStt += nDelCnt;
+ }
+ else if( nStt >= nIdxEnd )
+ break;
+ }
+ Replace0xFF(*this, aText, nTextStt, aText.getLength());
+
+ return aText.makeStringAndClear();
+}
+
+void SwTextNode::ReplaceText( const SwIndex& rStart, const sal_Int32 nDelLen,
+ const OUString & rStr)
+{
+ assert( rStart.GetIndex() < m_Text.getLength() // index out of bounds
+ && rStart.GetIndex() + nDelLen <= m_Text.getLength());
+
+ sal_Int32 const nOverflow(rStr.getLength() - nDelLen - GetSpaceLeft());
+ SAL_WARN_IF(nOverflow > 0, "sw.core",
+ "SwTextNode::ReplaceText: node text with insertion > node capacity.");
+ OUString const sInserted(
+ (nOverflow > 0) ? rStr.copy(0, rStr.getLength() - nOverflow) : rStr);
+ if (sInserted.isEmpty() && 0 == nDelLen)
+ {
+ return; // nothing to do
+ }
+
+ const sal_Int32 nStartPos = rStart.GetIndex();
+ sal_Int32 nEndPos = nStartPos + nDelLen;
+ sal_Int32 nLen = nDelLen;
+ for( sal_Int32 nPos = nStartPos; nPos < nEndPos; ++nPos )
+ {
+ if ((CH_TXTATR_BREAKWORD == m_Text[nPos]) ||
+ (CH_TXTATR_INWORD == m_Text[nPos]))
+ {
+ SwTextAttr *const pHint = GetTextAttrForCharAt( nPos );
+ if (pHint)
+ {
+ assert(!( pHint->GetEnd() && pHint->HasDummyChar()
+ && (pHint->GetStart() < nEndPos)
+ && (*pHint->GetEnd() > nEndPos) ));
+ // "deleting left-overlapped attribute with CH_TXTATR"
+ DeleteAttribute( pHint );
+ --nEndPos;
+ --nLen;
+ }
+ }
+ }
+
+ bool bOldExpFlg = IsIgnoreDontExpand();
+ SetIgnoreDontExpand( true );
+
+ if (nLen && sInserted.getLength())
+ {
+ // Replace the 1st char, then delete the rest and insert.
+ // This way the attributes of the 1st char are expanded!
+ m_Text = m_Text.replaceAt(nStartPos, 1, sInserted.subView(0, 1));
+
+ ++const_cast<SwIndex&>(rStart);
+ m_Text = m_Text.replaceAt(rStart.GetIndex(), nLen - 1, u"");
+ Update( rStart, nLen - 1, true );
+
+ std::u16string_view aTmpText( sInserted.subView(1) );
+ m_Text = m_Text.replaceAt(rStart.GetIndex(), 0, aTmpText);
+ Update( rStart, aTmpText.size() );
+ }
+ else
+ {
+ m_Text = m_Text.replaceAt(nStartPos, nLen, u"");
+ Update( rStart, nLen, true );
+
+ m_Text = m_Text.replaceAt(nStartPos, 0, sInserted);
+ Update( rStart, sInserted.getLength() );
+ }
+
+ SetIgnoreDontExpand( bOldExpFlg );
+ SwDelText aDelHint( nStartPos, nDelLen );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aDelHint));
+
+ if (sInserted.getLength())
+ {
+ SwInsText const aHint(sw::MakeSwInsText(*this, nStartPos, sInserted.getLength()));
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ }
+}
+
+namespace {
+ void lcl_ResetParAttrs( SwTextNode &rTextNode )
+ {
+ const o3tl::sorted_vector<sal_uInt16> aAttrs{ RES_PARATR_LIST_ID, RES_PARATR_LIST_LEVEL,
+ RES_PARATR_LIST_ISRESTART,
+ RES_PARATR_LIST_RESTARTVALUE,
+ RES_PARATR_LIST_ISCOUNTED };
+ SwPaM aPam( rTextNode );
+ // #i96644#
+ // suppress side effect "send data changed events"
+ rTextNode.GetDoc().ResetAttrs( aPam, false, aAttrs, false );
+ }
+
+ // Helper method for special handling of modified attributes at text node.
+ // The following is handled:
+ // (1) on changing the paragraph style - RES_FMT_CHG:
+ // Check, if list style of the text node is changed. If yes, add respectively
+ // remove the text node to the corresponding list.
+ // (2) on changing the attributes - RES_ATTRSET_CHG:
+ // Same as (1).
+ // (3) on changing the list style - RES_PARATR_NUMRULE:
+ // Same as (1).
+ void HandleModifyAtTextNode( SwTextNode& rTextNode,
+ const SfxPoolItem* pOldValue,
+ const SfxPoolItem* pNewValue )
+ {
+ const sal_uInt16 nWhich = pOldValue ? pOldValue->Which() :
+ pNewValue ? pNewValue->Which() : 0 ;
+ bool bNumRuleSet = false;
+ bool bParagraphStyleChanged = false;
+ OUString sNumRule;
+ OUString sOldNumRule;
+ switch ( nWhich )
+ {
+ case RES_FMT_CHG:
+ {
+ bParagraphStyleChanged = true;
+ if( rTextNode.GetNodes().IsDocNodes() )
+ {
+ const SwNumRule* pFormerNumRuleAtTextNode =
+ rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
+ if ( pFormerNumRuleAtTextNode )
+ {
+ sOldNumRule = pFormerNumRuleAtTextNode->GetName();
+ }
+ if ( rTextNode.IsEmptyListStyleDueToSetOutlineLevelAttr() )
+ {
+ const SwNumRuleItem& rNumRuleItem = rTextNode.GetTextColl()->GetNumRule();
+ if ( !rNumRuleItem.GetValue().isEmpty() )
+ {
+ rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ }
+ }
+ const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
+ if ( pNumRuleAtTextNode )
+ {
+ bNumRuleSet = true;
+ sNumRule = pNumRuleAtTextNode->GetName();
+ }
+ }
+ break;
+ }
+ case RES_ATTRSET_CHG:
+ {
+ const SwNumRule* pFormerNumRuleAtTextNode =
+ rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
+ if ( pFormerNumRuleAtTextNode )
+ {
+ sOldNumRule = pFormerNumRuleAtTextNode->GetName();
+ }
+
+ const SwAttrSetChg* pSet = dynamic_cast<const SwAttrSetChg*>(pNewValue);
+ if ( pSet && pSet->GetChgSet()->GetItemState( RES_PARATR_NUMRULE, false ) ==
+ SfxItemState::SET )
+ {
+ // #i70748#
+ rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ bNumRuleSet = true;
+ }
+ // #i70748#
+ // The new list style set at the paragraph.
+ const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
+ if ( pNumRuleAtTextNode )
+ {
+ sNumRule = pNumRuleAtTextNode->GetName();
+ }
+ break;
+ }
+ case RES_PARATR_NUMRULE:
+ {
+ if ( rTextNode.GetNodes().IsDocNodes() )
+ {
+ const SwNumRule* pFormerNumRuleAtTextNode =
+ rTextNode.GetNum() ? rTextNode.GetNum()->GetNumRule() : nullptr;
+ if ( pFormerNumRuleAtTextNode )
+ {
+ sOldNumRule = pFormerNumRuleAtTextNode->GetName();
+ }
+
+ if ( pNewValue )
+ {
+ // #i70748#
+ rTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ bNumRuleSet = true;
+ }
+ // #i70748#
+ // The new list style set at the paragraph.
+ const SwNumRule* pNumRuleAtTextNode = rTextNode.GetNumRule();
+ if ( pNumRuleAtTextNode )
+ {
+ sNumRule = pNumRuleAtTextNode->GetName();
+ }
+ }
+ break;
+ }
+ }
+ if ( sNumRule != sOldNumRule )
+ {
+ if ( bNumRuleSet )
+ {
+ if (sNumRule.isEmpty())
+ {
+ rTextNode.RemoveFromList();
+ if ( bParagraphStyleChanged )
+ {
+ lcl_ResetParAttrs(rTextNode);
+ }
+ }
+ else
+ {
+ rTextNode.RemoveFromList();
+ // If new list style is the outline style, apply outline
+ // level as the list level.
+ if (sNumRule==SwNumRule::GetOutlineRuleName())
+ {
+ // #i70748#
+ OSL_ENSURE( rTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle(),
+ "<HandleModifyAtTextNode()> - text node with outline style, but its paragraph style is not assigned to outline style." );
+ const int nNewListLevel =
+ rTextNode.GetTextColl()->GetAssignedOutlineStyleLevel();
+ if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
+ {
+ rTextNode.SetAttrListLevel( nNewListLevel );
+ }
+ }
+ rTextNode.AddToList();
+ }
+ }
+ else // <sNumRule.Len() == 0 && sOldNumRule.Len() != 0>
+ {
+ rTextNode.RemoveFromList();
+ if ( bParagraphStyleChanged )
+ {
+ lcl_ResetParAttrs(rTextNode);
+ // #i70748#
+ if ( dynamic_cast<const SfxUInt16Item &>(rTextNode.GetAttr( RES_PARATR_OUTLINELEVEL, false )).GetValue() > 0 )
+ {
+ rTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
+ }
+ }
+ }
+ }
+ else if (!sNumRule.isEmpty() && !rTextNode.IsInList())
+ {
+ rTextNode.AddToList();
+ }
+ }
+ // End of method <HandleModifyAtTextNode>
+}
+
+SwFormatColl* SwTextNode::ChgFormatColl( SwFormatColl *pNewColl )
+{
+ OSL_ENSURE( pNewColl,"ChgFormatColl: Collectionpointer has value 0." );
+ assert( dynamic_cast<const SwTextFormatColl *>(pNewColl) && "ChgFormatColl: is not a Text Collection pointer." );
+
+ SwTextFormatColl *pOldColl = GetTextColl();
+ if( pNewColl != pOldColl )
+ {
+ SetCalcHiddenCharFlags();
+ SwContentNode::ChgFormatColl( pNewColl );
+ OSL_ENSURE( !mbInSetOrResetAttr,
+ "DEBUG OSL_ENSURE(ON - <SwTextNode::ChgFormatColl(..)> called during <Set/ResetAttr(..)>" );
+ if ( !mbInSetOrResetAttr )
+ {
+ SwFormatChg aTmp1( pOldColl );
+ SwFormatChg aTmp2( pNewColl );
+ HandleModifyAtTextNode( *this, &aTmp1, &aTmp2 );
+ }
+
+ // reset fill information on parent style change
+ if(maFillAttributes)
+ {
+ maFillAttributes.reset();
+ }
+ }
+
+ // only for real nodes-array
+ if( GetNodes().IsDocNodes() )
+ {
+ ChgTextCollUpdateNum( pOldColl, static_cast<SwTextFormatColl *>(pNewColl) );
+ }
+
+ return pOldColl;
+}
+
+const SwNodeNum* SwTextNode::GetNum(SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
+{
+ // invariant: it's only in list in Hide mode if it's in list in normal mode
+ assert(mpNodeNum || !mpNodeNumRLHidden);
+ return (pLayout && pLayout->IsHideRedlines()) || SwListRedlineType::HIDDEN == eRedline
+ ? mpNodeNumRLHidden.get()
+ : ( SwListRedlineType::ORIGTEXT == eRedline ? mpNodeNumOrig.get() : mpNodeNum.get() );
+}
+
+void SwTextNode::DoNum(std::function<void (SwNodeNum &)> const& rFunc)
+{
+ // temp. clear because GetActualListLevel() may be called and the assert
+ // there triggered during update, which is unhelpful
+ std::unique_ptr<SwNodeNum> pBackup = std::move(mpNodeNumRLHidden);
+ std::unique_ptr<SwNodeNum> pBackup2 = std::move(mpNodeNumOrig);
+ assert(mpNodeNum);
+ rFunc(*mpNodeNum);
+ if (pBackup)
+ {
+ mpNodeNumRLHidden = std::move(pBackup);
+ rFunc(*mpNodeNumRLHidden);
+ }
+ if (pBackup2)
+ {
+ mpNodeNumOrig = std::move(pBackup2);
+ rFunc(*mpNodeNumOrig);
+ }
+}
+
+SwNumberTree::tNumberVector
+SwTextNode::GetNumberVector(SwRootFrame const*const pLayout, SwListRedlineType eRedline) const
+{
+ if (SwNodeNum const*const pNum = GetNum(pLayout, eRedline))
+ {
+ return pNum->GetNumberVector();
+ }
+ else
+ {
+ SwNumberTree::tNumberVector aResult;
+ return aResult;
+ }
+}
+
+bool SwTextNode::IsOutline() const
+{
+ bool bResult = false;
+
+ if ( GetAttrOutlineLevel() > 0 )
+ {
+ bResult = !IsInRedlines();
+ }
+ else
+ {
+ const SwNumRule* pRule( GetNum() ? GetNum()->GetNumRule() : nullptr );
+ if ( pRule && pRule->IsOutlineRule() )
+ {
+ bResult = !IsInRedlines();
+ }
+ }
+
+ return bResult;
+}
+
+bool SwTextNode::IsOutlineStateChanged() const
+{
+ return IsOutline() != m_bLastOutlineState;
+}
+
+void SwTextNode::UpdateOutlineState()
+{
+ m_bLastOutlineState = IsOutline();
+}
+
+int SwTextNode::GetAttrOutlineLevel() const
+{
+ return GetAttr(RES_PARATR_OUTLINELEVEL).GetValue();
+}
+
+void SwTextNode::SetAttrOutlineLevel(int nLevel)
+{
+ assert(0 <= nLevel && nLevel <= MAXLEVEL); // Level Out Of Range
+ if ( 0 <= nLevel && nLevel <= MAXLEVEL )
+ {
+ SetAttr( SfxUInt16Item( RES_PARATR_OUTLINELEVEL,
+ o3tl::narrowing<sal_uInt16>(nLevel) ) );
+ }
+}
+
+void SwTextNode::GetAttrOutlineContentVisible(bool& bOutlineContentVisibleAttr)
+{
+ const SfxGrabBagItem & rGrabBagItem = dynamic_cast<const SfxGrabBagItem&>(GetAttr(RES_PARATR_GRABBAG));
+ auto it = rGrabBagItem.GetGrabBag().find("OutlineContentVisibleAttr");
+ if (it != rGrabBagItem.GetGrabBag().end())
+ it->second >>= bOutlineContentVisibleAttr;
+}
+
+void SwTextNode::SetAttrOutlineContentVisible(bool bVisible)
+{
+ SfxGrabBagItem aGrabBagItem(RES_PARATR_GRABBAG);
+ aGrabBagItem.GetGrabBag()["OutlineContentVisibleAttr"] <<= bVisible;
+ SetAttr(aGrabBagItem);
+}
+
+// #i70748#
+
+void SwTextNode::SetEmptyListStyleDueToSetOutlineLevelAttr()
+{
+ if ( !mbEmptyListStyleSetDueToSetOutlineLevelAttr )
+ {
+ SetAttr( SwNumRuleItem() );
+ mbEmptyListStyleSetDueToSetOutlineLevelAttr = true;
+ }
+}
+
+void SwTextNode::ResetEmptyListStyleDueToResetOutlineLevelAttr()
+{
+ if ( mbEmptyListStyleSetDueToSetOutlineLevelAttr )
+ {
+ ResetAttr( RES_PARATR_NUMRULE );
+ mbEmptyListStyleSetDueToSetOutlineLevelAttr = false;
+ }
+}
+
+void SwTextNode::SetAttrListLevel( int nLevel )
+{
+ if ( nLevel < 0 || nLevel >= MAXLEVEL )
+ {
+ assert(false); // invalid level
+ return;
+ }
+
+ SfxInt16Item aNewListLevelItem( RES_PARATR_LIST_LEVEL,
+ static_cast<sal_Int16>(nLevel) );
+ SetAttr( aNewListLevelItem );
+}
+
+bool SwTextNode::HasAttrListLevel() const
+{
+ return GetpSwAttrSet() &&
+ GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_LEVEL, false ) == SfxItemState::SET;
+}
+
+int SwTextNode::GetAttrListLevel() const
+{
+ int nAttrListLevel = 0;
+
+ const SfxInt16Item& aListLevelItem =
+ dynamic_cast<const SfxInt16Item&>(GetAttr( RES_PARATR_LIST_LEVEL ));
+ nAttrListLevel = static_cast<int>(aListLevelItem.GetValue());
+
+ return nAttrListLevel;
+}
+
+int SwTextNode::GetActualListLevel(SwListRedlineType eRedline) const
+{
+ assert(SwListRedlineType::SHOW != eRedline ||
+ !GetNum(nullptr, SwListRedlineType::SHOW) || !mpNodeNumRLHidden || // must be in sync
+ GetNum(nullptr, SwListRedlineType::SHOW)->GetLevelInListTree() ==
+ mpNodeNumRLHidden->GetLevelInListTree());
+ return GetNum(nullptr, eRedline) ? GetNum(nullptr, eRedline)->GetLevelInListTree() : -1;
+}
+
+void SwTextNode::SetListRestart( bool bRestart )
+{
+ if ( !bRestart )
+ {
+ // attribute not contained in paragraph style's attribute set. Thus,
+ // it can be reset to the attribute pool default by resetting the attribute.
+ ResetAttr( RES_PARATR_LIST_ISRESTART );
+ }
+ else
+ {
+ SfxBoolItem aNewIsRestartItem( RES_PARATR_LIST_ISRESTART,
+ true );
+ SetAttr( aNewIsRestartItem );
+ }
+}
+
+bool SwTextNode::IsListRestart() const
+{
+ const SfxBoolItem& aIsRestartItem = GetAttr( RES_PARATR_LIST_ISRESTART );
+
+ return aIsRestartItem.GetValue();
+}
+
+/** Returns if the paragraph has a visible numbering or bullet.
+ This includes all kinds of numbering/bullet/outlines.
+ The concrete list label string has to be checked, too.
+ */
+bool SwTextNode::HasVisibleNumberingOrBullet() const
+{
+ const SwNumRule* pRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if ( pRule && IsCountedInList())
+ {
+ const SwNumFormat& rFormat = pRule->Get(lcl_BoundListLevel(GetActualListLevel()));
+ if (getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY))
+ // True if we have something in label text or there is a non-empty
+ // FollowedBy separator (space, tab or whatsoever)
+ return rFormat.GetLabelFollowedBy() != SvxNumberFormat::LabelFollowedBy::NOTHING ||
+ !pRule->MakeNumString(*GetNum()).isEmpty();
+ else
+ // #i87154#
+ // Correction of #newlistlevelattrs#:
+ // The numbering type has to be checked for bullet lists.
+ return SVX_NUM_NUMBER_NONE != rFormat.GetNumberingType() ||
+ !pRule->MakeNumString(*(GetNum())).isEmpty();
+ }
+
+ return false;
+}
+
+void SwTextNode::SetAttrListRestartValue( SwNumberTree::tSwNumTreeNumber nNumber )
+{
+ const bool bChanged( HasAttrListRestartValue()
+ ? GetAttrListRestartValue() != nNumber
+ : nNumber != USHRT_MAX );
+
+ if ( !bChanged && HasAttrListRestartValue() )
+ return;
+
+ if ( nNumber == USHRT_MAX )
+ {
+ ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
+ }
+ else
+ {
+ SfxInt16Item aNewListRestartValueItem( RES_PARATR_LIST_RESTARTVALUE,
+ static_cast<sal_Int16>(nNumber) );
+ SetAttr( aNewListRestartValueItem );
+ }
+}
+
+bool SwTextNode::HasAttrListRestartValue() const
+{
+ return GetpSwAttrSet() &&
+ GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_RESTARTVALUE, false ) == SfxItemState::SET;
+}
+SwNumberTree::tSwNumTreeNumber SwTextNode::GetAttrListRestartValue() const
+{
+ OSL_ENSURE( HasAttrListRestartValue(),
+ "<SwTextNode::GetAttrListRestartValue()> - only ask for list restart value, if attribute is set at text node." );
+
+ const SfxInt16Item& aListRestartValueItem =
+ dynamic_cast<const SfxInt16Item&>(GetAttr( RES_PARATR_LIST_RESTARTVALUE ));
+ return static_cast<SwNumberTree::tSwNumTreeNumber>(aListRestartValueItem.GetValue());
+}
+
+SwNumberTree::tSwNumTreeNumber SwTextNode::GetActualListStartValue() const
+{
+ SwNumberTree::tSwNumTreeNumber nListRestartValue = 1;
+
+ if ( IsListRestart() && HasAttrListRestartValue() )
+ {
+ nListRestartValue = GetAttrListRestartValue();
+ }
+ else
+ {
+ SwNumRule* pRule = GetNumRule();
+ if ( pRule )
+ {
+ const SwNumFormat* pFormat =
+ pRule->GetNumFormat( o3tl::narrowing<sal_uInt16>(GetAttrListLevel()) );
+ if ( pFormat )
+ {
+ nListRestartValue = pFormat->GetStart();
+ }
+ }
+ }
+
+ return nListRestartValue;
+}
+
+bool SwTextNode::IsNotifiable() const
+{
+ return m_bNotifiable && IsNotificationEnabled();
+}
+
+bool SwTextNode::IsNotificationEnabled() const
+{
+ const SwDoc& rDoc = GetDoc();
+ return !rDoc.IsInReading() && !rDoc.IsInDtor();
+}
+
+void SwTextNode::SetCountedInList( bool bCounted )
+{
+ if ( bCounted )
+ {
+ // attribute not contained in paragraph style's attribute set. Thus,
+ // it can be reset to the attribute pool default by resetting the attribute.
+ ResetAttr( RES_PARATR_LIST_ISCOUNTED );
+ }
+ else
+ {
+ SfxBoolItem aIsCountedInListItem( RES_PARATR_LIST_ISCOUNTED, false );
+ SetAttr( aIsCountedInListItem );
+ }
+}
+
+bool SwTextNode::IsCountedInList() const
+{
+ const SfxBoolItem& aIsCountedInListItem = GetAttr( RES_PARATR_LIST_ISCOUNTED );
+
+ return aIsCountedInListItem.GetValue();
+}
+
+static SwList * FindList(SwTextNode *const pNode)
+{
+ const OUString sListId = pNode->GetListId();
+ if (!sListId.isEmpty())
+ {
+ auto & rIDLA(pNode->GetDoc().getIDocumentListsAccess());
+ SwList* pList = rIDLA.getListByName( sListId );
+ if ( pList == nullptr )
+ {
+ // Create corresponding list.
+ SwNumRule* pNumRule = pNode->GetNumRule();
+ if ( pNumRule )
+ {
+ pList = rIDLA.createList(sListId, pNode->GetNumRule()->GetName());
+ }
+ }
+ OSL_ENSURE( pList != nullptr,
+ "<SwTextNode::AddToList()> - no list for given list id. Serious defect" );
+ return pList;
+ }
+ return nullptr;
+}
+
+void SwTextNode::AddToList()
+{
+ if ( IsInList() )
+ {
+ OSL_FAIL( "<SwTextNode::AddToList()> - the text node is already added to a list. Serious defect" );
+ return;
+ }
+
+ SwList *const pList(FindList(this));
+ if (!(pList && GetNodes().IsDocNodes())) // not for undo nodes
+ return;
+
+ assert(!mpNodeNum);
+ mpNodeNum.reset(new SwNodeNum(this, false));
+ pList->InsertListItem(*mpNodeNum, SwListRedlineType::SHOW, GetAttrListLevel(), GetDoc());
+
+ // set redline lists
+ // "default" list: visible items in Show Changes mode (tracked insertions and deletions)
+ // "hidden" list: visible items in Hide Changes mode (tracked insertions, but not deletions)
+ // "orig" list: visible items rejecting all changes (no tracked insertions and deletions)
+ bool bRecordChanges = GetDoc().GetDocShell() && GetDoc().GetDocShell()->IsChangeRecording();
+ if (!bRecordChanges || GetDoc().IsInXMLImport() || GetDoc().IsInWriterfilterImport() )
+ {
+ const SwRedlineTable& rRedTable = GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type nRedlPos = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Insert);
+ // paragraph start is not in a tracked insertion
+ if ( SwRedlineTable::npos == nRedlPos || GetIndex() <= rRedTable[nRedlPos]->Start()->nNode.GetNode().GetIndex() )
+ {
+ AddToListOrig();
+
+ // if the paragraph is not deleted, add to the "hidden" list, too
+ SwRedlineTable::size_type nRedlPosDel = GetDoc().getIDocumentRedlineAccess().GetRedlinePos(*this, RedlineType::Delete);
+ if ( SwRedlineTable::npos == nRedlPosDel )
+ AddToListRLHidden();
+ }
+ // inserted paragraph, e.g. during file load, add to the "hidden" list
+ else if ( SwRedlineTable::npos != nRedlPos )
+ AddToListRLHidden();
+ }
+ else if ( bRecordChanges )
+ AddToListRLHidden();
+
+ // iterate all frames & if there's one with hidden layout...
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> iter(*this);
+ for (SwTextFrame* pFrame = iter.First(); pFrame && !mpNodeNumRLHidden; pFrame = iter.Next())
+ {
+ if (pFrame->getRootFrame()->IsHideRedlines())
+ {
+ if (pFrame->GetTextNodeForParaProps() == this)
+ {
+ AddToListRLHidden();
+ }
+ break; // assume it's consistent, need to check only once
+ }
+ }
+}
+
+void SwTextNode::AddToListRLHidden()
+{
+ if (mpNodeNumRLHidden)
+ return;
+
+ SwList *const pList(FindList(this));
+ if (pList)
+ {
+ assert(!mpNodeNumRLHidden);
+ mpNodeNumRLHidden.reset(new SwNodeNum(this, true));
+ pList->InsertListItem(*mpNodeNumRLHidden, SwListRedlineType::HIDDEN, GetAttrListLevel(), GetDoc());
+ }
+}
+
+void SwTextNode::AddToListOrig()
+{
+ if (mpNodeNumOrig)
+ return;
+
+ SwList *const pList(FindList(this));
+ if (pList)
+ {
+ assert(!mpNodeNumOrig);
+ mpNodeNumOrig.reset(new SwNodeNum(this, true));
+ pList->InsertListItem(*mpNodeNumOrig, SwListRedlineType::ORIGTEXT, GetAttrListLevel(), GetDoc());
+ }
+}
+
+void SwTextNode::RemoveFromList()
+{
+ // sw_redlinehide: ensure it's removed from the other half too!
+ RemoveFromListRLHidden();
+ RemoveFromListOrig();
+ if ( IsInList() )
+ {
+ SwList::RemoveListItem(*mpNodeNum, GetDoc());
+ mpNodeNum.reset();
+
+ SetWordCountDirty( true );
+ }
+}
+
+void SwTextNode::RemoveFromListRLHidden()
+{
+ if (mpNodeNumRLHidden) // direct access because RemoveFromList doesn't have layout
+ {
+ assert(mpNodeNumRLHidden->GetParent() || !GetNodes().IsDocNodes());
+ SwList::RemoveListItem(*mpNodeNumRLHidden, GetDoc());
+ mpNodeNumRLHidden.reset();
+
+ SetWordCountDirty( true );
+ }
+}
+
+void SwTextNode::RemoveFromListOrig()
+{
+ if (mpNodeNumOrig) // direct access because RemoveFromList doesn't have layout
+ {
+ assert(mpNodeNumOrig->GetParent() || !GetNodes().IsDocNodes());
+ SwList::RemoveListItem(*mpNodeNumOrig, GetDoc());
+ mpNodeNumOrig.reset();
+
+ SetWordCountDirty( true );
+ }
+}
+
+bool SwTextNode::IsInList() const
+{
+ return GetNum() != nullptr && GetNum()->GetParent() != nullptr;
+}
+
+bool SwTextNode::IsFirstOfNumRule(SwRootFrame const& rLayout) const
+{
+ bool bResult = false;
+
+ SwNodeNum const*const pNum(GetNum(&rLayout));
+ if (pNum && pNum->GetNumRule())
+ bResult = pNum->IsFirst();
+
+ return bResult;
+}
+
+void SwTextNode::SetListId(OUString const& rListId)
+{
+ const SfxStringItem& rListIdItem =
+ dynamic_cast<const SfxStringItem&>(GetAttr( RES_PARATR_LIST_ID ));
+ if (rListIdItem.GetValue() != rListId)
+ {
+ if (rListId.isEmpty())
+ {
+ ResetAttr( RES_PARATR_LIST_ID );
+ }
+ else
+ {
+ SfxStringItem aNewListIdItem(RES_PARATR_LIST_ID, rListId);
+ SetAttr( aNewListIdItem );
+ }
+ }
+}
+
+OUString SwTextNode::GetListId() const
+{
+ const SfxStringItem& rListIdItem =
+ dynamic_cast<const SfxStringItem&>(GetAttr( RES_PARATR_LIST_ID ));
+ const OUString& sListId {rListIdItem.GetValue()};
+
+ // As long as no explicit list id attribute is set, use the list id of
+ // the list, which has been created for the applied list style.
+ if (sListId.isEmpty())
+ {
+ SwNumRule* pRule = GetNumRule();
+ if ( pRule )
+ {
+ return pRule->GetDefaultListId();
+ }
+ }
+
+ return sListId;
+}
+
+/** Determines, if the list level indent attributes can be applied to the
+ paragraph.
+
+ The list level indents can be applied to the paragraph under the one
+ of following conditions:
+ - the list style is directly applied to the paragraph and the paragraph
+ has no own indent attributes.
+ - the list style is applied to the paragraph through one of its paragraph
+ styles, the paragraph has no own indent attributes and on the paragraph
+ style hierarchy from the paragraph to the paragraph style with the
+ list style no indent attributes are found.
+
+ @return boolean
+*/
+bool SwTextNode::AreListLevelIndentsApplicable() const
+{
+ bool bAreListLevelIndentsApplicable( true );
+
+ if ( !GetNum() || !GetNum()->GetNumRule() )
+ {
+ // no list style applied to paragraph
+ bAreListLevelIndentsApplicable = false;
+ }
+ else if ( HasSwAttrSet() &&
+ GetpSwAttrSet()->GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
+ {
+ // paragraph has hard-set indent attributes
+ bAreListLevelIndentsApplicable = false;
+ }
+ else if ( HasSwAttrSet() &&
+ GetpSwAttrSet()->GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ {
+ // list style is directly applied to paragraph and paragraph has no
+ // hard-set indent attributes
+ bAreListLevelIndentsApplicable = true;
+ }
+ else
+ {
+ // list style is applied through one of the paragraph styles and
+ // paragraph has no hard-set indent attributes
+
+ // check, paragraph's
+ const SwTextFormatColl* pColl = GetTextColl();
+ while ( pColl )
+ {
+ if ( pColl->GetAttrSet().GetItemState( RES_LR_SPACE, false ) == SfxItemState::SET )
+ {
+ // indent attributes found in the paragraph style hierarchy.
+ bAreListLevelIndentsApplicable = false;
+ break;
+ }
+
+ if ( pColl->GetAttrSet().GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET )
+ {
+ // paragraph style with the list style found and until now no
+ // indent attributes are found in the paragraph style hierarchy.
+ bAreListLevelIndentsApplicable = true;
+ break;
+ }
+
+ pColl = dynamic_cast<const SwTextFormatColl*>(pColl->DerivedFrom());
+ OSL_ENSURE( pColl,
+ "<SwTextNode::AreListLevelIndentsApplicable()> - something wrong in paragraph's style hierarchy. The applied list style is not found." );
+ }
+ }
+
+ return bAreListLevelIndentsApplicable;
+}
+
+/** Retrieves the list tab stop position, if the paragraph's list level defines
+ one and this list tab stop has to merged into the tap stops of the paragraph
+
+ @param nListTabStopPosition
+ output parameter - containing the list tab stop position
+
+ @return boolean - indicating, if a list tab stop position is provided
+*/
+bool SwTextNode::GetListTabStopPosition( tools::Long& nListTabStopPosition ) const
+{
+ bool bListTabStopPositionProvided(false);
+
+ const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
+ {
+ const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT &&
+ rFormat.GetLabelFollowedBy() == SvxNumberFormat::LISTTAB )
+ {
+ bListTabStopPositionProvided = true;
+ nListTabStopPosition = rFormat.GetListtabPos();
+
+ if ( getIDocumentSettingAccess()->get(DocumentSettingId::TABS_RELATIVE_TO_INDENT) )
+ {
+ // tab stop position are treated to be relative to the "before text"
+ // indent value of the paragraph. Thus, adjust <nListTabStopPos>.
+ if ( AreListLevelIndentsApplicable() )
+ {
+ nListTabStopPosition -= rFormat.GetIndentAt();
+ }
+ else if (!getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING))
+ {
+ SvxLRSpaceItem aItem = GetSwAttrSet().GetLRSpace();
+ nListTabStopPosition -= aItem.GetTextLeft();
+ }
+ }
+ }
+ }
+
+ return bListTabStopPositionProvided;
+}
+
+OUString SwTextNode::GetLabelFollowedBy() const
+{
+ const SwNumRule* pNumRule = GetNum() ? GetNum()->GetNumRule() : nullptr;
+ if ( pNumRule && HasVisibleNumberingOrBullet() && GetActualListLevel() >= 0 )
+ {
+ const SwNumFormat& rFormat = pNumRule->Get( o3tl::narrowing<sal_uInt16>(GetActualListLevel()) );
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ return rFormat.GetLabelFollowedByAsString();
+ }
+ }
+
+ return OUString();
+}
+
+void SwTextNode::CalcHiddenCharFlags() const
+{
+ sal_Int32 nStartPos;
+ sal_Int32 nEndPos;
+ // Update of the flags is done inside GetBoundsOfHiddenRange()
+ SwScriptInfo::GetBoundsOfHiddenRange( *this, 0, nStartPos, nEndPos );
+}
+
+// #i12836# enhanced pdf export
+bool SwTextNode::IsHidden() const
+{
+ if ( IsHiddenByParaField() || HasHiddenCharAttribute( true ) )
+ return true;
+
+ const SwSectionNode* pSectNd = FindSectionNode();
+ return pSectNd && pSectNd->GetSection().IsHiddenFlag();
+}
+
+namespace {
+ // Helper class for special handling of setting attributes at text node:
+ // In constructor an instance of the helper class recognize whose attributes
+ // are set and perform corresponding actions before the intrinsic set of
+ // attributes has been taken place.
+ // In the destructor - after the attributes have been set at the text
+ // node - corresponding actions are performed.
+ // The following is handled:
+ // (1) When the list style attribute - RES_PARATR_NUMRULE - is set,
+ // (A) list style attribute is empty -> the text node is removed from
+ // its list.
+ // (B) list style attribute is not empty
+ // (a) text node has no list style -> add text node to its list after
+ // the attributes have been set.
+ // (b) text node has list style -> change of list style is notified
+ // after the attributes have been set.
+ // (2) When the list id attribute - RES_PARATR_LIST_ID - is set and changed,
+ // the text node is removed from its current list before the attributes
+ // are set and added to its new list after the attributes have been set.
+ // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is set
+ // and changed after the attributes have been set
+ // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is set
+ // and changed after the attributes have been set
+ // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE -
+ // is set and changed after the attributes have been set
+ // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is set
+ // and changed after the attributes have been set
+ // (7) Set or Reset empty list style due to changed outline level - RES_PARATR_OUTLINELEVEL.
+ class HandleSetAttrAtTextNode
+ {
+ public:
+ HandleSetAttrAtTextNode( SwTextNode& rTextNode,
+ const SfxPoolItem& pItem );
+ HandleSetAttrAtTextNode( SwTextNode& rTextNode,
+ const SfxItemSet& rItemSet );
+ ~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
+
+ private:
+ SwTextNode& mrTextNode;
+ bool mbAddTextNodeToList;
+ bool mbUpdateListLevel;
+ bool mbUpdateListRestart;
+ bool mbUpdateListCount;
+ // #i70748#
+ bool mbOutlineLevelSet;
+ };
+
+ HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
+ const SfxPoolItem& pItem )
+ : mrTextNode( rTextNode ),
+ mbAddTextNodeToList( false ),
+ mbUpdateListLevel( false ),
+ mbUpdateListRestart( false ),
+ mbUpdateListCount( false ),
+ // #i70748#
+ mbOutlineLevelSet( false )
+ {
+ switch ( pItem.Which() )
+ {
+ // handle RES_PARATR_NUMRULE
+ case RES_PARATR_NUMRULE:
+ {
+ mrTextNode.RemoveFromList();
+
+ const SwNumRuleItem& rNumRuleItem =
+ dynamic_cast<const SwNumRuleItem&>(pItem);
+ if ( !rNumRuleItem.GetValue().isEmpty() )
+ {
+ mbAddTextNodeToList = true;
+ // #i105562#
+
+ mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ }
+ }
+ break;
+
+ // handle RES_PARATR_LIST_ID
+ case RES_PARATR_LIST_ID:
+ {
+ const SfxStringItem& rListIdItem =
+ dynamic_cast<const SfxStringItem&>(pItem);
+ OSL_ENSURE( rListIdItem.GetValue().getLength() > 0,
+ "<HandleSetAttrAtTextNode(..)> - empty list id attribute not expected. Serious defect." );
+ const OUString sListIdOfTextNode = rTextNode.GetListId();
+ if ( rListIdItem.GetValue() != sListIdOfTextNode )
+ {
+ mbAddTextNodeToList = true;
+ if ( mrTextNode.IsInList() )
+ {
+ mrTextNode.RemoveFromList();
+ }
+ }
+ }
+ break;
+
+ // handle RES_PARATR_LIST_LEVEL
+ case RES_PARATR_LIST_LEVEL:
+ {
+ const SfxInt16Item& aListLevelItem =
+ dynamic_cast<const SfxInt16Item&>(pItem);
+ if ( aListLevelItem.GetValue() != mrTextNode.GetAttrListLevel() )
+ {
+ mbUpdateListLevel = true;
+ }
+ }
+ break;
+
+ // handle RES_PARATR_LIST_ISRESTART
+ case RES_PARATR_LIST_ISRESTART:
+ {
+ const SfxBoolItem& aListIsRestartItem =
+ dynamic_cast<const SfxBoolItem&>(pItem);
+ if ( aListIsRestartItem.GetValue() !=
+ mrTextNode.IsListRestart() )
+ {
+ mbUpdateListRestart = true;
+ }
+ }
+ break;
+
+ // handle RES_PARATR_LIST_RESTARTVALUE
+ case RES_PARATR_LIST_RESTARTVALUE:
+ {
+ const SfxInt16Item& aListRestartValueItem =
+ dynamic_cast<const SfxInt16Item&>(pItem);
+ if ( !mrTextNode.HasAttrListRestartValue() ||
+ aListRestartValueItem.GetValue() != mrTextNode.GetAttrListRestartValue() )
+ {
+ mbUpdateListRestart = true;
+ }
+ }
+ break;
+
+ // handle RES_PARATR_LIST_ISCOUNTED
+ case RES_PARATR_LIST_ISCOUNTED:
+ {
+ const SfxBoolItem& aIsCountedInListItem =
+ dynamic_cast<const SfxBoolItem&>(pItem);
+ if ( aIsCountedInListItem.GetValue() !=
+ mrTextNode.IsCountedInList() )
+ {
+ mbUpdateListCount = true;
+ }
+ }
+ break;
+
+ // #i70748#
+ // handle RES_PARATR_OUTLINELEVEL
+ case RES_PARATR_OUTLINELEVEL:
+ {
+ const SfxUInt16Item& aOutlineLevelItem =
+ dynamic_cast<const SfxUInt16Item&>(pItem);
+ if ( aOutlineLevelItem.GetValue() != mrTextNode.GetAttrOutlineLevel() )
+ {
+ mbOutlineLevelSet = true;
+ }
+ }
+ break;
+ }
+
+ }
+
+ HandleSetAttrAtTextNode::HandleSetAttrAtTextNode( SwTextNode& rTextNode,
+ const SfxItemSet& rItemSet )
+ : mrTextNode( rTextNode ),
+ mbAddTextNodeToList( false ),
+ mbUpdateListLevel( false ),
+ mbUpdateListRestart( false ),
+ mbUpdateListCount( false ),
+ // #i70748#
+ mbOutlineLevelSet( false )
+ {
+ // handle RES_PARATR_NUMRULE
+ if ( const SwNumRuleItem* pNumRuleItem = rItemSet.GetItemIfSet( RES_PARATR_NUMRULE, false ) )
+ {
+ mrTextNode.RemoveFromList();
+
+ if ( !pNumRuleItem->GetValue().isEmpty() )
+ {
+ mbAddTextNodeToList = true;
+ // #i70748#
+ mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ }
+ }
+
+ // handle RES_PARATR_LIST_ID
+ if ( const SfxStringItem* pListIdItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ID, false ) )
+ {
+ const OUString sListIdOfTextNode = mrTextNode.GetListId();
+ if ( pListIdItem->GetValue() != sListIdOfTextNode )
+ {
+ mbAddTextNodeToList = true;
+ if ( mrTextNode.IsInList() )
+ {
+ mrTextNode.RemoveFromList();
+ }
+ }
+ }
+
+ // handle RES_PARATR_LIST_LEVEL
+ if ( const SfxInt16Item* pListLevelItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_LEVEL, false ) )
+ {
+ if (pListLevelItem->GetValue() != mrTextNode.GetAttrListLevel())
+ {
+ mbUpdateListLevel = true;
+ }
+ }
+
+ // handle RES_PARATR_LIST_ISRESTART
+ if ( const SfxBoolItem* pListIsRestartItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ISRESTART, false ) )
+ {
+ if (pListIsRestartItem->GetValue() != mrTextNode.IsListRestart())
+ {
+ mbUpdateListRestart = true;
+ }
+ }
+
+ // handle RES_PARATR_LIST_RESTARTVALUE
+ if ( const SfxInt16Item* pListRestartValueItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_RESTARTVALUE, false ) )
+ {
+ if ( !mrTextNode.HasAttrListRestartValue() ||
+ pListRestartValueItem->GetValue() != mrTextNode.GetAttrListRestartValue() )
+ {
+ mbUpdateListRestart = true;
+ }
+ }
+
+ // handle RES_PARATR_LIST_ISCOUNTED
+ if ( const SfxBoolItem* pIsCountedInListItem = rItemSet.GetItemIfSet( RES_PARATR_LIST_ISCOUNTED, false ) )
+ {
+ if (pIsCountedInListItem->GetValue() != mrTextNode.IsCountedInList())
+ {
+ mbUpdateListCount = true;
+ }
+ }
+
+ // #i70748#
+ // handle RES_PARATR_OUTLINELEVEL
+ if ( const SfxUInt16Item* pOutlineLevelItem = rItemSet.GetItemIfSet( RES_PARATR_OUTLINELEVEL, false ) )
+ {
+ if (pOutlineLevelItem->GetValue() != mrTextNode.GetAttrOutlineLevel())
+ {
+ mbOutlineLevelSet = true;
+ }
+ }
+ }
+
+ HandleSetAttrAtTextNode::~HandleSetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
+ {
+ if ( mbAddTextNodeToList )
+ {
+ SwNumRule* pNumRuleAtTextNode = mrTextNode.GetNumRule();
+ if ( pNumRuleAtTextNode )
+ {
+ mrTextNode.AddToList();
+ }
+ }
+ else
+ {
+ if ( mbUpdateListLevel && mrTextNode.IsInList() )
+ {
+ auto const nLevel(mrTextNode.GetAttrListLevel());
+ const SwDoc& rDoc(mrTextNode.GetDoc());
+ mrTextNode.DoNum(
+ [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
+ }
+
+ if ( mbUpdateListRestart && mrTextNode.IsInList() )
+ {
+ const SwDoc& rDoc(mrTextNode.GetDoc());
+ mrTextNode.DoNum(
+ [&rDoc](SwNodeNum & rNum) {
+ rNum.InvalidateMe();
+ rNum.NotifyInvalidSiblings(rDoc);
+ });
+ }
+
+ if (mbUpdateListCount && mrTextNode.IsInList() && HasNumberingWhichNeedsLayoutUpdate(mrTextNode))
+ {
+ // Repaint all text frames that belong to this numbering to avoid outdated generated
+ // numbers.
+ const SwDoc& rDoc(mrTextNode.GetDoc());
+ mrTextNode.DoNum(
+ [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
+ }
+ }
+
+ // #i70748#
+ if (!mbOutlineLevelSet)
+ return;
+
+ mrTextNode.GetNodes().UpdateOutlineNode(mrTextNode);
+ if (mrTextNode.GetAttrOutlineLevel() == 0)
+ {
+ mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ }
+ else
+ {
+ if ( mrTextNode.GetSwAttrSet().GetItemState( RES_PARATR_NUMRULE )
+ != SfxItemState::SET )
+ {
+ mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
+ }
+ }
+ }
+ // End of class <HandleSetAttrAtTextNode>
+}
+
+bool SwTextNode::SetAttr( const SfxPoolItem& pItem )
+{
+ const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
+ mbInSetOrResetAttr = true;
+
+ HandleSetAttrAtTextNode aHandleSetAttr( *this, pItem );
+
+ bool bRet = SwContentNode::SetAttr( pItem );
+
+ mbInSetOrResetAttr = bOldIsSetOrResetAttr;
+
+ return bRet;
+}
+
+bool SwTextNode::SetAttr( const SfxItemSet& rSet )
+{
+ const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
+ mbInSetOrResetAttr = true;
+
+ HandleSetAttrAtTextNode aHandleSetAttr( *this, rSet );
+
+ bool bRet = SwContentNode::SetAttr( rSet );
+
+ mbInSetOrResetAttr = bOldIsSetOrResetAttr;
+
+ return bRet;
+}
+
+void SwTextNode::SetInSwUndo(bool bInUndo)
+{
+ m_bInUndo = bInUndo;
+}
+
+namespace {
+ // Helper class for special handling of resetting attributes at text node:
+ // In constructor an instance of the helper class recognize whose attributes
+ // are reset and perform corresponding actions before the intrinsic reset of
+ // attributes has been taken place.
+ // In the destructor - after the attributes have been reset at the text
+ // node - corresponding actions are performed.
+ // The following is handled:
+ // (1) When the list style attribute - RES_PARATR_NUMRULE - is reset,
+ // the text is removed from its list before the attributes have been reset.
+ // (2) When the list id attribute - RES_PARATR_LIST_ID - is reset,
+ // the text is removed from its list before the attributes have been reset.
+ // (3) Notify list tree, if list level - RES_PARATR_LIST_LEVEL - is reset.
+ // (4) Notify list tree, if list restart - RES_PARATR_LIST_ISRESTART - is reset.
+ // (5) Notify list tree, if list restart value - RES_PARATR_LIST_RESTARTVALUE - is reset.
+ // (6) Notify list tree, if count in list - RES_PARATR_LIST_ISCOUNTED - is reset.
+ // (7) Reset empty list style, if outline level attribute - RES_PARATR_OUTLINELEVEL - is reset.
+ class HandleResetAttrAtTextNode
+ {
+ public:
+ HandleResetAttrAtTextNode( SwTextNode& rTextNode,
+ const sal_uInt16 nWhich1,
+ sal_uInt16 nWhich2 );
+ HandleResetAttrAtTextNode( SwTextNode& rTextNode,
+ const std::vector<sal_uInt16>& rWhichArr );
+ explicit HandleResetAttrAtTextNode( SwTextNode& rTextNode );
+
+ ~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE;
+
+ private:
+ SwTextNode& mrTextNode;
+ bool mbListStyleOrIdReset;
+ bool mbUpdateListLevel;
+ bool mbUpdateListRestart;
+ bool mbUpdateListCount;
+
+ void init( const std::vector<sal_uInt16>& rWhichArr );
+ };
+
+ HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
+ const sal_uInt16 nWhich1,
+ sal_uInt16 nWhich2 )
+ : mrTextNode( rTextNode ),
+ mbListStyleOrIdReset( false ),
+ mbUpdateListLevel( false ),
+ mbUpdateListRestart( false ),
+ mbUpdateListCount( false )
+ {
+ if ( nWhich2 < nWhich1 )
+ nWhich2 = nWhich1;
+ std::vector<sal_uInt16> rWhichArr;
+ for ( sal_uInt16 nWhich = nWhich1; nWhich <= nWhich2; ++nWhich )
+ rWhichArr.push_back( nWhich );
+
+ init( rWhichArr );
+ }
+
+ HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode,
+ const std::vector<sal_uInt16>& rWhichArr )
+ : mrTextNode( rTextNode ),
+ mbListStyleOrIdReset( false ),
+ mbUpdateListLevel( false ),
+ mbUpdateListRestart( false ),
+ mbUpdateListCount( false )
+ {
+ init( rWhichArr );
+ }
+
+ HandleResetAttrAtTextNode::HandleResetAttrAtTextNode( SwTextNode& rTextNode )
+ : mrTextNode( rTextNode ),
+ mbListStyleOrIdReset( true ),
+ mbUpdateListLevel( false ),
+ mbUpdateListRestart( false ),
+ mbUpdateListCount( false )
+ {
+ if ( rTextNode.IsInList() )
+ {
+ rTextNode.RemoveFromList();
+ }
+ // #i70748#
+ mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ }
+
+ void HandleResetAttrAtTextNode::init( const std::vector<sal_uInt16>& rWhichArr )
+ {
+ bool bRemoveFromList( false );
+ {
+ for (const auto& rWhich : rWhichArr)
+ {
+ if ( rWhich == RES_PARATR_NUMRULE )
+ {
+ bRemoveFromList = bRemoveFromList ||
+ mrTextNode.GetNumRule() != nullptr;
+ mbListStyleOrIdReset = true;
+ }
+ else if ( rWhich == RES_PARATR_LIST_ID )
+ {
+ bRemoveFromList = bRemoveFromList ||
+ ( mrTextNode.GetpSwAttrSet() &&
+ mrTextNode.GetpSwAttrSet()->GetItemState( RES_PARATR_LIST_ID, false ) == SfxItemState::SET );
+ mbListStyleOrIdReset = true;
+ }
+ else if ( rWhich == RES_PARATR_OUTLINELEVEL )
+ mrTextNode.ResetEmptyListStyleDueToResetOutlineLevelAttr();
+ else if ( rWhich == RES_BACKGROUND )
+ mrTextNode.ResetAttr( XATTR_FILL_FIRST, XATTR_FILL_LAST );
+
+ if ( !bRemoveFromList )
+ {
+ // RES_PARATR_LIST_LEVEL
+ mbUpdateListLevel = mbUpdateListLevel ||
+ ( rWhich == RES_PARATR_LIST_LEVEL &&
+ mrTextNode.HasAttrListLevel() );
+
+ // RES_PARATR_LIST_ISRESTART and RES_PARATR_LIST_RESTARTVALUE
+ mbUpdateListRestart = mbUpdateListRestart ||
+ ( rWhich == RES_PARATR_LIST_ISRESTART &&
+ mrTextNode.IsListRestart() ) ||
+ ( rWhich == RES_PARATR_LIST_RESTARTVALUE &&
+ mrTextNode.HasAttrListRestartValue() );
+
+ // RES_PARATR_LIST_ISCOUNTED
+ mbUpdateListCount = mbUpdateListCount ||
+ ( rWhich == RES_PARATR_LIST_ISCOUNTED &&
+ !mrTextNode.IsCountedInList() );
+ }
+ }
+ }
+
+ if ( bRemoveFromList && mrTextNode.IsInList() )
+ {
+ mrTextNode.RemoveFromList();
+ }
+ }
+
+ HandleResetAttrAtTextNode::~HandleResetAttrAtTextNode() COVERITY_NOEXCEPT_FALSE
+ {
+ if ( mbListStyleOrIdReset && !mrTextNode.IsInList() )
+ {
+ // check, if in spite of the reset of the list style or the list id
+ // the paragraph still has to be added to a list.
+ if (mrTextNode.GetNumRule() && !mrTextNode.GetListId().isEmpty())
+ {
+ // #i96062#
+ // If paragraph has no list level attribute set and list style
+ // is the outline style, apply outline level as the list level.
+ if ( !mrTextNode.HasAttrListLevel() &&
+ mrTextNode.GetNumRule()->GetName()==SwNumRule::GetOutlineRuleName() &&
+ mrTextNode.GetTextColl()->IsAssignedToListLevelOfOutlineStyle() )
+ {
+ int nNewListLevel = mrTextNode.GetTextColl()->GetAssignedOutlineStyleLevel();
+ if ( 0 <= nNewListLevel && nNewListLevel < MAXLEVEL )
+ {
+ mrTextNode.SetAttrListLevel( nNewListLevel );
+ }
+ }
+ mrTextNode.AddToList();
+ }
+ // #i70748#
+ // #i105562#
+ else
+ {
+ if (mrTextNode.GetpSwAttrSet()
+ && mrTextNode.GetAttr(RES_PARATR_OUTLINELEVEL, false).GetValue() > 0)
+ {
+ mrTextNode.SetEmptyListStyleDueToSetOutlineLevelAttr();
+ }
+ }
+ }
+
+ if ( !mrTextNode.IsInList() )
+ return;
+
+ if ( mbUpdateListLevel )
+ {
+ auto const nLevel(mrTextNode.GetAttrListLevel());
+ const SwDoc& rDoc(mrTextNode.GetDoc());
+ mrTextNode.DoNum(
+ [nLevel, &rDoc](SwNodeNum & rNum) { rNum.SetLevelInListTree(nLevel, rDoc); });
+ }
+
+ if ( mbUpdateListRestart )
+ {
+ const SwDoc& rDoc(mrTextNode.GetDoc());
+ mrTextNode.DoNum(
+ [&rDoc](SwNodeNum & rNum) {
+ rNum.InvalidateMe();
+ rNum.NotifyInvalidSiblings(rDoc);
+ });
+ }
+
+ if ( mbUpdateListCount )
+ {
+ const SwDoc& rDoc(mrTextNode.GetDoc());
+ mrTextNode.DoNum(
+ [&rDoc](SwNodeNum & rNum) { rNum.InvalidateAndNotifyTree(rDoc); });
+ }
+ }
+ // End of class <HandleResetAttrAtTextNode>
+}
+
+bool SwTextNode::ResetAttr( sal_uInt16 nWhich1, sal_uInt16 nWhich2 )
+{
+ const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
+ mbInSetOrResetAttr = true;
+
+ HandleResetAttrAtTextNode aHandleResetAttr( *this, nWhich1, nWhich2 );
+
+ bool bRet = SwContentNode::ResetAttr( nWhich1, nWhich2 );
+
+ mbInSetOrResetAttr = bOldIsSetOrResetAttr;
+
+ return bRet;
+}
+
+bool SwTextNode::ResetAttr( const std::vector<sal_uInt16>& rWhichArr )
+{
+ const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
+ mbInSetOrResetAttr = true;
+
+ HandleResetAttrAtTextNode aHandleResetAttr( *this, rWhichArr );
+
+ bool bRet = SwContentNode::ResetAttr( rWhichArr );
+
+ mbInSetOrResetAttr = bOldIsSetOrResetAttr;
+
+ return bRet;
+}
+
+sal_uInt16 SwTextNode::ResetAllAttr()
+{
+ const bool bOldIsSetOrResetAttr( mbInSetOrResetAttr );
+ mbInSetOrResetAttr = true;
+
+ HandleResetAttrAtTextNode aHandleResetAttr( *this );
+
+ const sal_uInt16 nRet = SwContentNode::ResetAllAttr();
+
+ mbInSetOrResetAttr = bOldIsSetOrResetAttr;
+
+ return nRet;
+}
+
+void SwTextNode::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextNode"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(sal_Int32(GetIndex())).getStr()));
+
+ OUString sText = GetText();
+ for (int i = 0; i < 32; ++i)
+ sText = sText.replace(i, '*');
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_Text"));
+ (void)xmlTextWriterWriteString(pWriter, BAD_CAST(sText.toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (GetFormatColl())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextFormatColl"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetFormatColl()->GetName().toUtf8().getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (HasSwAttrSet())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwAttrSet"));
+ GetSwAttrSet().dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (HasHints())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwpHints"));
+ const SwpHints& rHints = GetSwpHints();
+ for (size_t i = 0; i < rHints.Count(); ++i)
+ rHints.Get(i)->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (GetNumRule())
+ GetNumRule()->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+sal_uInt32 SwTextNode::GetRsid( sal_Int32 nStt, sal_Int32 nEnd ) const
+{
+ SfxItemSetFixed<RES_CHRATR_RSID, RES_CHRATR_RSID> aSet( const_cast<SwAttrPool&>((GetDoc().GetAttrPool())) );
+ if (GetParaAttr(aSet, nStt, nEnd))
+ {
+ const SvxRsidItem* pRsid = aSet.GetItem<SvxRsidItem>(RES_CHRATR_RSID);
+ if( pRsid )
+ return pRsid->GetValue();
+ }
+
+ return 0;
+}
+
+sal_uInt32 SwTextNode::GetParRsid() const
+{
+ return reinterpret_cast<const SvxRsidItem&>(GetAttr( RES_PARATR_RSID )).GetValue();
+}
+
+bool SwTextNode::CompareParRsid( const SwTextNode &rTextNode ) const
+{
+ sal_uInt32 nThisRsid = GetParRsid();
+ sal_uInt32 nRsid = rTextNode.GetParRsid();
+
+ return nThisRsid == nRsid;
+}
+
+bool SwTextNode::CompareRsid( const SwTextNode &rTextNode, sal_Int32 nStt1, sal_Int32 nStt2 ) const
+{
+ sal_uInt32 nThisRsid = GetRsid( nStt1, nStt1 );
+ sal_uInt32 nRsid = rTextNode.GetRsid( nStt2, nStt2 );
+
+ return nThisRsid == nRsid;
+}
+
+// sw::Metadatable
+::sfx2::IXmlIdRegistry& SwTextNode::GetRegistry()
+{
+ return GetDoc().GetXmlIdRegistry();
+}
+
+bool SwTextNode::IsInClipboard() const
+{
+ return GetDoc().IsClipBoard();
+}
+
+bool SwTextNode::IsInUndo() const
+{
+ return GetDoc().GetIDocumentUndoRedo().IsUndoNodes(GetNodes());
+}
+
+bool SwTextNode::IsInContent() const
+{
+ return !GetDoc().IsInHeaderFooter( SwNodeIndex(*this) );
+}
+
+void SwTextNode::TriggerNodeUpdate(const sw::LegacyModifyHint& rHint)
+{
+ const auto pOldValue = rHint.m_pOld;
+ const auto pNewValue = rHint.m_pNew;
+ bool bWasNotifiable = m_bNotifiable;
+ m_bNotifiable = false;
+
+ // Override Modify so that deleting styles works properly (outline
+ // numbering!).
+ // Never call ChgTextCollUpdateNum for Nodes in Undo.
+ if( pOldValue
+ && pNewValue
+ && RES_FMT_CHG == pOldValue->Which()
+ && GetRegisteredIn() == static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat
+ && GetNodes().IsDocNodes() )
+ {
+ assert(dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pOldValue)->pChangedFormat));
+ assert(dynamic_cast<SwTextFormatColl const*>(static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat));
+ ChgTextCollUpdateNum(
+ static_cast<const SwTextFormatColl*>(static_cast<const SwFormatChg*>(pOldValue)->pChangedFormat),
+ static_cast<const SwTextFormatColl*>(static_cast<const SwFormatChg*>(pNewValue)->pChangedFormat) );
+ }
+
+ // reset fill information
+ if (maFillAttributes && pNewValue)
+ {
+ const sal_uInt16 nWhich = pNewValue->Which();
+ bool bReset(RES_FMT_CHG == nWhich); // ..on format change (e.g. style changed)
+
+ if(!bReset && RES_ATTRSET_CHG == nWhich) // ..on ItemChange from DrawingLayer FillAttributes
+ {
+ SfxItemIter aIter(*static_cast<const SwAttrSetChg*>(pNewValue)->GetChgSet());
+
+ for(const SfxPoolItem* pItem = aIter.GetCurItem(); pItem && !bReset; pItem = aIter.NextItem())
+ {
+ bReset = !IsInvalidItem(pItem) && pItem->Which() >= XATTR_FILL_FIRST && pItem->Which() <= XATTR_FILL_LAST;
+ }
+ }
+
+ if(bReset)
+ {
+ maFillAttributes.reset();
+ }
+ }
+
+ if ( !mbInSetOrResetAttr )
+ {
+ HandleModifyAtTextNode( *this, pOldValue, pNewValue );
+ }
+
+ SwContentNode::SwClientNotify(*this, rHint);
+
+ SwDoc& rDoc = GetDoc();
+ // #125329# - assure that text node is in document nodes array
+ if ( !rDoc.IsInDtor() && &rDoc.GetNodes() == &GetNodes() )
+ {
+ rDoc.GetNodes().UpdateOutlineNode(*this);
+ }
+
+ m_bNotifiable = bWasNotifiable;
+
+ if (pOldValue && (RES_REMOVE_UNO_OBJECT == pOldValue->Which()))
+ { // invalidate cached uno object
+ SetXParagraph(css::uno::Reference<css::text::XTextContent>(nullptr));
+ }
+}
+
+void SwTextNode::SwClientNotify( const SwModify& rModify, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ TriggerNodeUpdate(*pLegacyHint);
+ }
+ else if (dynamic_cast<const SwAttrHint*>(&rHint))
+ {
+ if (&rModify == GetRegisteredIn())
+ ChkCondColl();
+ }
+}
+
+uno::Reference< rdf::XMetadatable >
+SwTextNode::MakeUnoObject()
+{
+ const uno::Reference<rdf::XMetadatable> xMeta(
+ SwXParagraph::CreateXParagraph(GetDoc(), this), uno::UNO_QUERY);
+ return xMeta;
+}
+
+drawinglayer::attribute::SdrAllFillAttributesHelperPtr SwTextNode::getSdrAllFillAttributesHelper() const
+{
+ // create SdrAllFillAttributesHelper on demand
+ if(!maFillAttributes)
+ {
+ const_cast< SwTextNode* >(this)->maFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(GetSwAttrSet());
+ }
+
+ return maFillAttributes;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/swfntcch.cxx b/sw/source/core/txtnode/swfntcch.cxx
new file mode 100644
index 000000000..100a0e7a7
--- /dev/null
+++ b/sw/source/core/txtnode/swfntcch.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <shellio.hxx>
+#include <viewsh.hxx>
+#include <swfntcch.hxx>
+#include <fmtcol.hxx>
+#include <fntcache.hxx>
+#include <swfont.hxx>
+
+// from atrstck.cxx
+extern const sal_uInt8 StackPos[];
+
+// FontCache is created in txtinit.cxx TextInit_ and deleted in TextFinit
+SwFontCache *pSwFontCache = nullptr;
+
+SwFontObj::SwFontObj( const void *pOwn, SwViewShell *pSh ) :
+ SwCacheObj( pOwn ),
+ m_aSwFont( &static_cast<SwTextFormatColl const *>(pOwn)->GetAttrSet(), pSh ? &pSh->getIDocumentSettingAccess() : nullptr )
+{
+ m_aSwFont.AllocFontCacheId( pSh, m_aSwFont.GetActual() );
+ const SwAttrSet& rAttrSet = static_cast<SwTextFormatColl const *>(pOwn)->GetAttrSet();
+ for (sal_uInt16 i = RES_CHRATR_BEGIN; i < RES_CHRATR_END; i++)
+ m_pDefaultArray[ StackPos[ i ] ] = &rAttrSet.Get( i );
+}
+
+SwFontObj::~SwFontObj()
+{
+}
+
+SwFontAccess::SwFontAccess( const void *pOwn, SwViewShell *pSh ) :
+ SwCacheAccess( *pSwFontCache, pOwn,
+ static_cast<const SwTextFormatColl*>(pOwn)->IsInSwFntCache() ),
+ m_pShell( pSh )
+{
+}
+
+SwFontObj *SwFontAccess::Get( )
+{
+ return static_cast<SwFontObj *>( SwCacheAccess::Get( ) );
+}
+
+SwCacheObj *SwFontAccess::NewObj( )
+{
+ const_cast<SwTextFormatColl*>(static_cast<const SwTextFormatColl*>(m_pOwner))->SetInSwFntCache();
+ return new SwFontObj( m_pOwner, m_pShell );
+}
+
+SAL_DLLPUBLIC_EXPORT void FlushFontCache()
+{
+ if (pSwFontCache)
+ pSwFontCache->Flush();
+ if (pFntCache)
+ pFntCache->Flush();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/swfont.cxx b/sw/source/core/txtnode/swfont.cxx
new file mode 100644
index 000000000..0ed7baf87
--- /dev/null
+++ b/sw/source/core/txtnode/swfont.cxx
@@ -0,0 +1,1469 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <vcl/outdev.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <editeng/twolinesitem.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <IDocumentSettingAccess.hxx>
+#include <charatr.hxx>
+#include <viewsh.hxx>
+#include <swfont.hxx>
+#include <fntcache.hxx>
+#include <txtfrm.hxx>
+#include <scriptinfo.hxx>
+
+#ifdef DBG_UTIL
+// global Variable
+SvStatistics g_SvStat;
+#endif
+
+using namespace ::com::sun::star;
+
+// set background brush, depending on character formatting
+void SwFont::SetBackColor( std::optional<Color> xNewColor )
+{
+ mxBackColor = xNewColor;
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+void SwFont::SetTopBorder( const editeng::SvxBorderLine* pTopBorder )
+{
+ if( pTopBorder )
+ m_aTopBorder = *pTopBorder;
+ else
+ {
+ m_aTopBorder.reset();
+ m_nTopBorderDist = 0;
+ }
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+void SwFont::SetBottomBorder( const editeng::SvxBorderLine* pBottomBorder )
+{
+ if( pBottomBorder )
+ m_aBottomBorder = *pBottomBorder;
+ else
+ {
+ m_aBottomBorder.reset();
+ m_nBottomBorderDist = 0;
+ }
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+void SwFont::SetRightBorder( const editeng::SvxBorderLine* pRightBorder )
+{
+ if( pRightBorder )
+ m_aRightBorder = *pRightBorder;
+ else
+ {
+ m_aRightBorder.reset();
+ m_nRightBorderDist = 0;
+ }
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+void SwFont::SetLeftBorder( const editeng::SvxBorderLine* pLeftBorder )
+{
+ if( pLeftBorder )
+ m_aLeftBorder = *pLeftBorder;
+ else
+ {
+ m_aLeftBorder.reset();
+ m_nLeftBorderDist = 0;
+ }
+ m_bFontChg = true;
+ m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
+}
+
+const std::optional<editeng::SvxBorderLine>&
+SwFont::GetAbsTopBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
+{
+ switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
+ {
+ case 0 :
+ return m_aTopBorder;
+ case 900 :
+ return m_aRightBorder;
+ case 1800 :
+ return m_aBottomBorder;
+ case 2700 :
+ return m_aLeftBorder;
+ default :
+ assert(false);
+ return m_aTopBorder;
+ }
+}
+
+const std::optional<editeng::SvxBorderLine>&
+SwFont::GetAbsBottomBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
+{
+ switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
+ {
+ case 0 :
+ return m_aBottomBorder;
+ case 900 :
+ return m_aLeftBorder;
+ case 1800 :
+ return m_aTopBorder;
+ case 2700 :
+ return m_aRightBorder;
+ default :
+ assert(false);
+ return m_aBottomBorder;
+ }
+}
+
+const std::optional<editeng::SvxBorderLine>&
+SwFont::GetAbsLeftBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
+{
+ switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
+ {
+ case 0 :
+ return m_aLeftBorder;
+ case 900 :
+ return m_aTopBorder;
+ case 1800 :
+ return m_aRightBorder;
+ case 2700 :
+ return m_aBottomBorder;
+ default :
+ assert(false);
+ return m_aLeftBorder;
+ }
+}
+
+const std::optional<editeng::SvxBorderLine>&
+SwFont::GetAbsRightBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
+{
+ switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
+ {
+ case 0 :
+ return m_aRightBorder;
+ case 900 :
+ return m_aBottomBorder;
+ case 1800 :
+ return m_aLeftBorder;
+ case 2700 :
+ return m_aTopBorder;
+ default :
+ assert(false);
+ return m_aRightBorder;
+ }
+}
+
+SvxShadowLocation SwFont::GetAbsShadowLocation(const bool bVertLayout,
+ const bool bVertLayoutLRBT) const
+{
+ SvxShadowLocation aLocation = SvxShadowLocation::NONE;
+ switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
+ {
+ case 0:
+ aLocation = m_aShadowLocation;
+ break;
+
+ case 900:
+ switch ( m_aShadowLocation )
+ {
+ case SvxShadowLocation::TopLeft:
+ aLocation = SvxShadowLocation::BottomLeft;
+ break;
+ case SvxShadowLocation::TopRight:
+ aLocation = SvxShadowLocation::TopLeft;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ aLocation = SvxShadowLocation::BottomRight;
+ break;
+ case SvxShadowLocation::BottomRight:
+ aLocation = SvxShadowLocation::TopRight;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ aLocation = m_aShadowLocation;
+ break;
+ }
+ break;
+
+ case 1800:
+ switch ( m_aShadowLocation )
+ {
+ case SvxShadowLocation::TopLeft:
+ aLocation = SvxShadowLocation::BottomRight;
+ break;
+ case SvxShadowLocation::TopRight:
+ aLocation = SvxShadowLocation::BottomLeft;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ aLocation = SvxShadowLocation::TopRight;
+ break;
+ case SvxShadowLocation::BottomRight:
+ aLocation = SvxShadowLocation::TopLeft;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ aLocation = m_aShadowLocation;
+ break;
+ }
+ break;
+
+ case 2700:
+ switch ( m_aShadowLocation )
+ {
+ case SvxShadowLocation::TopLeft:
+ aLocation = SvxShadowLocation::TopRight;
+ break;
+ case SvxShadowLocation::TopRight:
+ aLocation = SvxShadowLocation::BottomRight;
+ break;
+ case SvxShadowLocation::BottomLeft:
+ aLocation = SvxShadowLocation::TopLeft;
+ break;
+ case SvxShadowLocation::BottomRight:
+ aLocation = SvxShadowLocation::BottomLeft;
+ break;
+ case SvxShadowLocation::NONE:
+ case SvxShadowLocation::End:
+ aLocation = m_aShadowLocation;
+ break;
+ }
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+ return aLocation;
+}
+
+sal_uInt16 SwFont::CalcShadowSpace(const SvxShadowItemSide nShadow, const bool bVertLayout,
+ const bool bVertLayoutLRBT, const bool bSkipLeft,
+ const bool bSkipRight) const
+{
+ sal_uInt16 nSpace = 0;
+ const Degree10 nOrient = GetOrientation(bVertLayout, bVertLayoutLRBT);
+ const SvxShadowLocation aLoc = GetAbsShadowLocation(bVertLayout, bVertLayoutLRBT);
+ switch( nShadow )
+ {
+ case SvxShadowItemSide::TOP:
+ if(( aLoc == SvxShadowLocation::TopLeft ||
+ aLoc == SvxShadowLocation::TopRight ) &&
+ ( nOrient == 0_deg10 || nOrient == 1800_deg10 ||
+ ( nOrient == 900_deg10 && !bSkipRight ) ||
+ ( nOrient == 2700_deg10 && !bSkipLeft )))
+ {
+ nSpace = m_nShadowWidth;
+ }
+ break;
+
+ case SvxShadowItemSide::BOTTOM:
+ if(( aLoc == SvxShadowLocation::BottomLeft ||
+ aLoc == SvxShadowLocation::BottomRight ) &&
+ ( nOrient == 0_deg10 || nOrient == 1800_deg10 ||
+ ( nOrient == 900_deg10 && !bSkipLeft ) ||
+ ( nOrient == 2700_deg10 && !bSkipRight )))
+ {
+ nSpace = m_nShadowWidth;
+ }
+ break;
+
+ case SvxShadowItemSide::LEFT:
+ if(( aLoc == SvxShadowLocation::TopLeft ||
+ aLoc == SvxShadowLocation::BottomLeft ) &&
+ ( nOrient == 900_deg10 || nOrient == 2700_deg10 ||
+ ( nOrient == 0_deg10 && !bSkipLeft ) ||
+ ( nOrient == 1800_deg10 && !bSkipRight )))
+ {
+ nSpace = m_nShadowWidth;
+ }
+ break;
+
+ case SvxShadowItemSide::RIGHT:
+ if(( aLoc == SvxShadowLocation::TopRight ||
+ aLoc == SvxShadowLocation::BottomRight ) &&
+ ( nOrient == 900_deg10 || nOrient == 2700_deg10 ||
+ ( nOrient == 0_deg10 && !bSkipRight ) ||
+ ( nOrient == 1800_deg10 && !bSkipLeft )))
+ {
+ nSpace = m_nShadowWidth;
+ }
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ return nSpace;
+}
+
+// maps directions for vertical layout
+static Degree10 MapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
+{
+ if ( bVertFormat )
+ {
+ switch ( nDir.get() )
+ {
+ case 0 :
+ if (bVertFormatLRBT)
+ nDir = 900_deg10;
+ else
+ nDir = 2700_deg10;
+ break;
+ case 900 :
+ nDir = 0_deg10;
+ break;
+ case 2700 :
+ nDir = 1800_deg10;
+ break;
+#if OSL_DEBUG_LEVEL > 0
+ default :
+ OSL_FAIL( "Unsupported direction" );
+ break;
+#endif
+ }
+ }
+ return nDir;
+}
+
+// maps the absolute direction set at the font to its logical counterpart
+// in the rotated environment
+Degree10 UnMapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
+{
+ if (bVertFormatLRBT)
+ {
+ switch (nDir.get())
+ {
+ case 900:
+ nDir = 0_deg10;
+ break;
+ default:
+ SAL_WARN("sw.core", "unsupported direction for VertLRBT");
+ break;
+ }
+ return nDir;
+ }
+
+ if ( bVertFormat )
+ {
+ switch ( nDir.get() )
+ {
+ case 0 :
+ nDir = 900_deg10;
+ break;
+ case 1800 :
+ nDir = 2700_deg10;
+ break;
+ case 2700 :
+ nDir = 0_deg10;
+ break;
+#if OSL_DEBUG_LEVEL > 0
+ default :
+ OSL_FAIL( "Unsupported direction" );
+ break;
+#endif
+ }
+ }
+ return nDir;
+}
+
+Degree10 SwFont::GetOrientation(const bool bVertFormat, const bool bVertFormatLRBT) const
+{
+ return UnMapDirection(m_aSub[m_nActual].GetOrientation(), bVertFormat, bVertFormatLRBT);
+}
+
+void SwFont::SetVertical(Degree10 nDir, const bool bVertFormat, const bool bVertLayoutLRBT)
+{
+ // map direction if frame has vertical layout
+ nDir = MapDirection(nDir, bVertFormat, bVertLayoutLRBT);
+
+ if( nDir != m_aSub[SwFontScript::Latin].GetOrientation() )
+ {
+ m_bFontChg = true;
+ bool bVertical = bVertFormat && !bVertLayoutLRBT;
+ m_aSub[SwFontScript::Latin].SetVertical(nDir, bVertical);
+ m_aSub[SwFontScript::CJK].SetVertical(nDir, bVertical);
+ m_aSub[SwFontScript::CTL].SetVertical(nDir, bVertical);
+ }
+}
+
+/*
+ Escapement:
+ frEsc: Fraction, ratio of Escapements
+ Esc = resulting Escapement
+ A1 = original Ascent (nOrgAscent)
+ A2 = shrunk Ascent (nEscAscent)
+ Ax = resulting Ascent (GetAscent())
+ H1 = original Height (nOrgHeight)
+ H2 = shrunk Height (nEscHeight)
+ Hx = resulting Height (GetHeight())
+ Bx = resulting Baseline for Text (CalcPos())
+ (Attention: Y - A1!)
+
+ Escapement:
+ Esc = H1 * frEsc;
+
+ Superscript:
+ Ax = A2 + Esc;
+ Hx = H2 + Esc;
+ Bx = A1 - Esc;
+
+ Subscript:
+ Ax = A1;
+ Hx = A1 + Esc + (H2 - A2);
+ Bx = A1 + Esc;
+*/
+
+// nEsc is the percentage
+sal_uInt16 SwSubFont::CalcEscAscent( const sal_uInt16 nOldAscent ) const
+{
+ if( DFLT_ESC_AUTO_SUPER != GetEscapement() &&
+ DFLT_ESC_AUTO_SUB != GetEscapement() )
+ {
+ const tools::Long nAscent = nOldAscent +
+ ( static_cast<tools::Long>(m_nOrgHeight) * GetEscapement() ) / 100;
+ if ( nAscent>0 )
+ return std::max<sal_uInt16>( nAscent, m_nOrgAscent );
+ }
+ return m_nOrgAscent;
+}
+
+void SwFont::SetDiffFnt( const SfxItemSet *pAttrSet,
+ const IDocumentSettingAccess *pIDocumentSettingAccess )
+{
+ mxBackColor.reset();
+
+ if( pAttrSet )
+ {
+
+ if( const SvxFontItem* pFont = pAttrSet->GetItemIfSet( RES_CHRATR_FONT ) )
+ {
+ m_aSub[SwFontScript::Latin].SetFamily( pFont->GetFamily() );
+ m_aSub[SwFontScript::Latin].Font::SetFamilyName( pFont->GetFamilyName() );
+ m_aSub[SwFontScript::Latin].Font::SetStyleName( pFont->GetStyleName() );
+ m_aSub[SwFontScript::Latin].Font::SetPitch( pFont->GetPitch() );
+ m_aSub[SwFontScript::Latin].Font::SetCharSet( pFont->GetCharSet() );
+ }
+ if( const SvxFontHeightItem *pHeight = pAttrSet->GetItemIfSet( RES_CHRATR_FONTSIZE ) )
+ {
+ m_aSub[SwFontScript::Latin].SvxFont::SetPropr( 100 );
+ m_aSub[SwFontScript::Latin].m_aSize = m_aSub[SwFontScript::Latin].Font::GetFontSize();
+ Size aTmpSize = m_aSub[SwFontScript::Latin].m_aSize;
+ aTmpSize.setHeight( pHeight->GetHeight() );
+ m_aSub[SwFontScript::Latin].SetSize( aTmpSize );
+ }
+ if( const SvxPostureItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_POSTURE ) )
+ m_aSub[SwFontScript::Latin].Font::SetItalic( pItem->GetPosture() );
+ if( const SvxWeightItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_WEIGHT ) )
+ m_aSub[SwFontScript::Latin].Font::SetWeight( pItem->GetWeight() );
+ if( const SvxLanguageItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_LANGUAGE ) )
+ m_aSub[SwFontScript::Latin].SetLanguage( pItem->GetLanguage() );
+
+ if( const SvxFontItem* pFont = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_FONT ) )
+ {
+ m_aSub[SwFontScript::CJK].SetFamily( pFont->GetFamily() );
+ m_aSub[SwFontScript::CJK].Font::SetFamilyName( pFont->GetFamilyName() );
+ m_aSub[SwFontScript::CJK].Font::SetStyleName( pFont->GetStyleName() );
+ m_aSub[SwFontScript::CJK].Font::SetPitch( pFont->GetPitch() );
+ m_aSub[SwFontScript::CJK].Font::SetCharSet( pFont->GetCharSet() );
+ }
+ if( const SvxFontHeightItem* pHeight = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_FONTSIZE) )
+ {
+ m_aSub[SwFontScript::CJK].SvxFont::SetPropr( 100 );
+ m_aSub[SwFontScript::CJK].m_aSize = m_aSub[SwFontScript::CJK].Font::GetFontSize();
+ Size aTmpSize = m_aSub[SwFontScript::CJK].m_aSize;
+ aTmpSize.setHeight( pHeight->GetHeight() );
+ m_aSub[SwFontScript::CJK].SetSize( aTmpSize );
+ }
+ if( const SvxPostureItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_POSTURE ) )
+ m_aSub[SwFontScript::CJK].Font::SetItalic( pItem->GetPosture() );
+ if( const SvxWeightItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_WEIGHT ) )
+ m_aSub[SwFontScript::CJK].Font::SetWeight( pItem->GetWeight() );
+ if( const SvxLanguageItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CJK_LANGUAGE ) )
+ {
+ LanguageType eNewLang = pItem->GetLanguage();
+ m_aSub[SwFontScript::CJK].SetLanguage( eNewLang );
+ m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
+ m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
+ m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
+ }
+
+ if( const SvxFontItem* pFont = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_FONT ) )
+ {
+ m_aSub[SwFontScript::CTL].SetFamily( pFont->GetFamily() );
+ m_aSub[SwFontScript::CTL].Font::SetFamilyName( pFont->GetFamilyName() );
+ m_aSub[SwFontScript::CTL].Font::SetStyleName( pFont->GetStyleName() );
+ m_aSub[SwFontScript::CTL].Font::SetPitch( pFont->GetPitch() );
+ m_aSub[SwFontScript::CTL].Font::SetCharSet( pFont->GetCharSet() );
+ }
+ if( const SvxFontHeightItem* pHeight = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_FONTSIZE ) )
+ {
+ m_aSub[SwFontScript::CTL].SvxFont::SetPropr( 100 );
+ m_aSub[SwFontScript::CTL].m_aSize = m_aSub[SwFontScript::CTL].Font::GetFontSize();
+ Size aTmpSize = m_aSub[SwFontScript::CTL].m_aSize;
+ aTmpSize.setHeight( pHeight->GetHeight() );
+ m_aSub[SwFontScript::CTL].SetSize( aTmpSize );
+ }
+ if( const SvxPostureItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_POSTURE ) )
+ m_aSub[SwFontScript::CTL].Font::SetItalic(pItem->GetPosture() );
+ if( const SvxWeightItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_WEIGHT ) )
+ m_aSub[SwFontScript::CTL].Font::SetWeight( pItem->GetWeight() );
+ if( const SvxLanguageItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CTL_LANGUAGE ) )
+ m_aSub[SwFontScript::CTL].SetLanguage( pItem->GetLanguage() );
+
+ if( const SvxUnderlineItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_UNDERLINE ) )
+ {
+ SetUnderline( pItem->GetLineStyle() );
+ SetUnderColor( pItem->GetColor() );
+ }
+ if( const SvxOverlineItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_OVERLINE ) )
+ {
+ SetOverline( pItem->GetLineStyle() );
+ SetOverColor( pItem->GetColor() );
+ }
+ if( const SvxCrossedOutItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CROSSEDOUT ) )
+ SetStrikeout( pItem->GetStrikeout() );
+ if( const SvxColorItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_COLOR ) )
+ SetColor( pItem->GetValue() );
+ if( const SvxEmphasisMarkItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_EMPHASIS_MARK ))
+ SetEmphasisMark( pItem->GetEmphasisMark() );
+
+ SetTransparent( true );
+ SetAlign( ALIGN_BASELINE );
+ if( const SvxContourItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CONTOUR ) )
+ SetOutline( pItem->GetValue() );
+ if( const SvxShadowedItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOWED ) )
+ SetShadow( pItem->GetValue() );
+ if( const SvxCharReliefItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_RELIEF ) )
+ SetRelief( pItem->GetValue() );
+ if( const SvxShadowedItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOWED ))
+ SetPropWidth( pItem->GetValue() ? 50 : 100 );
+ if( const SvxAutoKernItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_AUTOKERN ) )
+ {
+ if( pItem->GetValue() )
+ {
+ SetAutoKern( ( !pIDocumentSettingAccess ||
+ !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ?
+ FontKerning::FontSpecific :
+ FontKerning::Asian );
+ }
+ else
+ SetAutoKern( FontKerning::NONE );
+ }
+ if( const SvxWordLineModeItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_WORDLINEMODE ) )
+ SetWordLineMode( pItem->GetValue() );
+
+ if( const SvxEscapementItem* pEsc = pAttrSet->GetItemIfSet( RES_CHRATR_ESCAPEMENT ) )
+ {
+ SetEscapement( pEsc->GetEsc() );
+ if( m_aSub[SwFontScript::Latin].IsEsc() )
+ SetProportion( pEsc->GetProportionalHeight() );
+ }
+ if( const SvxCaseMapItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_CASEMAP ) )
+ SetCaseMap( pItem->GetCaseMap() );
+ if( const SvxKerningItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_KERNING ) )
+ SetFixKerning( pItem->GetValue() );
+ if( const SvxCharRotateItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_ROTATE ) )
+ SetVertical( pItem->GetValue() );
+ if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_BACKGROUND ) )
+ mxBackColor = pItem->GetColor();
+ if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_HIGHLIGHT ) )
+ SetHighlightColor(pItem->GetColor());
+ if( const SvxBoxItem* pBoxItem = pAttrSet->GetItemIfSet( RES_CHRATR_BOX ) )
+ {
+ SetTopBorder(pBoxItem->GetTop());
+ SetBottomBorder(pBoxItem->GetBottom());
+ SetRightBorder(pBoxItem->GetRight());
+ SetLeftBorder(pBoxItem->GetLeft());
+ SetTopBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::TOP));
+ SetBottomBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::BOTTOM));
+ SetRightBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::RIGHT));
+ SetLeftBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::LEFT));
+ }
+ if( const SvxShadowItem* pShadowItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOW ) )
+ {
+ SetShadowColor(pShadowItem->GetColor());
+ SetShadowWidth(pShadowItem->GetWidth());
+ SetShadowLocation(pShadowItem->GetLocation());
+ }
+ const SvxTwoLinesItem* pTwoLinesItem = pAttrSet->GetItemIfSet( RES_CHRATR_TWO_LINES );
+ if( pTwoLinesItem && pTwoLinesItem->GetValue() )
+ SetVertical( 0_deg10 );
+ }
+ else
+ {
+ Invalidate();
+ }
+ m_bPaintBlank = false;
+ OSL_ENSURE( m_aSub[SwFontScript::Latin].IsTransparent(), "SwFont: Transparent revolution" );
+}
+
+SwFont::SwFont( const SwFont &rFont )
+ : m_aSub(rFont.m_aSub)
+{
+ m_nActual = rFont.m_nActual;
+ mxBackColor = rFont.mxBackColor;
+ m_aHighlightColor = rFont.m_aHighlightColor;
+ m_aTopBorder = rFont.m_aTopBorder;
+ m_aBottomBorder = rFont.m_aBottomBorder;
+ m_aRightBorder = rFont.m_aRightBorder;
+ m_aLeftBorder = rFont.m_aLeftBorder;
+ m_nTopBorderDist = rFont.m_nTopBorderDist;
+ m_nBottomBorderDist = rFont.m_nBottomBorderDist;
+ m_nRightBorderDist = rFont.m_nRightBorderDist;
+ m_nLeftBorderDist = rFont.m_nLeftBorderDist;
+ m_aShadowColor = rFont.m_aShadowColor;
+ m_nShadowWidth = rFont.m_nShadowWidth;
+ m_aShadowLocation = rFont.m_aShadowLocation;
+ m_aUnderColor = rFont.GetUnderColor();
+ m_aOverColor = rFont.GetOverColor();
+ m_nToxCount = 0;
+ m_nRefCount = 0;
+ m_nMetaCount = 0;
+ m_nContentControlCount = 0;
+ m_nInputFieldCount = 0;
+ m_bFontChg = rFont.m_bFontChg;
+ m_bOrgChg = rFont.m_bOrgChg;
+ m_bPaintBlank = rFont.m_bPaintBlank;
+ m_bGreyWave = rFont.m_bGreyWave;
+}
+
+SwFont::SwFont( const SwAttrSet* pAttrSet,
+ const IDocumentSettingAccess* pIDocumentSettingAccess )
+ : m_aSub()
+{
+ m_nActual = SwFontScript::Latin;
+ m_nToxCount = 0;
+ m_nRefCount = 0;
+ m_nMetaCount = 0;
+ m_nContentControlCount = 0;
+ m_nInputFieldCount = 0;
+ m_bPaintBlank = false;
+ m_bGreyWave = false;
+ m_bOrgChg = true;
+ {
+ const SvxFontItem& rFont = pAttrSet->GetFont();
+ m_aSub[SwFontScript::Latin].SetFamily( rFont.GetFamily() );
+ m_aSub[SwFontScript::Latin].SetFamilyName( rFont.GetFamilyName() );
+ m_aSub[SwFontScript::Latin].SetStyleName( rFont.GetStyleName() );
+ m_aSub[SwFontScript::Latin].SetPitch( rFont.GetPitch() );
+ m_aSub[SwFontScript::Latin].SetCharSet( rFont.GetCharSet() );
+ m_aSub[SwFontScript::Latin].SvxFont::SetPropr( 100 ); // 100% of FontSize
+ Size aTmpSize = m_aSub[SwFontScript::Latin].m_aSize;
+ aTmpSize.setHeight( pAttrSet->GetSize().GetHeight() );
+ m_aSub[SwFontScript::Latin].SetSize( aTmpSize );
+ m_aSub[SwFontScript::Latin].SetItalic( pAttrSet->GetPosture().GetPosture() );
+ m_aSub[SwFontScript::Latin].SetWeight( pAttrSet->GetWeight().GetWeight() );
+ m_aSub[SwFontScript::Latin].SetLanguage( pAttrSet->GetLanguage().GetLanguage() );
+ }
+
+ {
+ const SvxFontItem& rFont = pAttrSet->GetCJKFont();
+ m_aSub[SwFontScript::CJK].SetFamily( rFont.GetFamily() );
+ m_aSub[SwFontScript::CJK].SetFamilyName( rFont.GetFamilyName() );
+ m_aSub[SwFontScript::CJK].SetStyleName( rFont.GetStyleName() );
+ m_aSub[SwFontScript::CJK].SetPitch( rFont.GetPitch() );
+ m_aSub[SwFontScript::CJK].SetCharSet( rFont.GetCharSet() );
+ m_aSub[SwFontScript::CJK].SvxFont::SetPropr( 100 ); // 100% of FontSize
+ Size aTmpSize = m_aSub[SwFontScript::CJK].m_aSize;
+ aTmpSize.setHeight( pAttrSet->GetCJKSize().GetHeight() );
+ m_aSub[SwFontScript::CJK].SetSize( aTmpSize );
+ m_aSub[SwFontScript::CJK].SetItalic( pAttrSet->GetCJKPosture().GetPosture() );
+ m_aSub[SwFontScript::CJK].SetWeight( pAttrSet->GetCJKWeight().GetWeight() );
+ LanguageType eNewLang = pAttrSet->GetCJKLanguage().GetLanguage();
+ m_aSub[SwFontScript::CJK].SetLanguage( eNewLang );
+ m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
+ m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
+ m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
+ }
+
+ {
+ const SvxFontItem& rFont = pAttrSet->GetCTLFont();
+ m_aSub[SwFontScript::CTL].SetFamily( rFont.GetFamily() );
+ m_aSub[SwFontScript::CTL].SetFamilyName( rFont.GetFamilyName() );
+ m_aSub[SwFontScript::CTL].SetStyleName( rFont.GetStyleName() );
+ m_aSub[SwFontScript::CTL].SetPitch( rFont.GetPitch() );
+ m_aSub[SwFontScript::CTL].SetCharSet( rFont.GetCharSet() );
+ m_aSub[SwFontScript::CTL].SvxFont::SetPropr( 100 ); // 100% of FontSize
+ Size aTmpSize = m_aSub[SwFontScript::CTL].m_aSize;
+ aTmpSize.setHeight( pAttrSet->GetCTLSize().GetHeight() );
+ m_aSub[SwFontScript::CTL].SetSize( aTmpSize );
+ m_aSub[SwFontScript::CTL].SetItalic( pAttrSet->GetCTLPosture().GetPosture() );
+ m_aSub[SwFontScript::CTL].SetWeight( pAttrSet->GetCTLWeight().GetWeight() );
+ m_aSub[SwFontScript::CTL].SetLanguage( pAttrSet->GetCTLLanguage().GetLanguage() );
+ }
+ if ( pAttrSet->GetCharHidden().GetValue() )
+ SetUnderline( LINESTYLE_DOTTED );
+ else
+ SetUnderline( pAttrSet->GetUnderline().GetLineStyle() );
+ SetUnderColor( pAttrSet->GetUnderline().GetColor() );
+ SetOverline( pAttrSet->GetOverline().GetLineStyle() );
+ SetOverColor( pAttrSet->GetOverline().GetColor() );
+ SetEmphasisMark( pAttrSet->GetEmphasisMark().GetEmphasisMark() );
+ SetStrikeout( pAttrSet->GetCrossedOut().GetStrikeout() );
+ SetColor( pAttrSet->GetColor().GetValue() );
+ SetTransparent( true );
+ SetAlign( ALIGN_BASELINE );
+ SetOutline( pAttrSet->GetContour().GetValue() );
+ SetShadow( pAttrSet->GetShadowed().GetValue() );
+ SetPropWidth( pAttrSet->GetCharScaleW().GetValue() );
+ SetRelief( pAttrSet->GetCharRelief().GetValue() );
+ if( pAttrSet->GetAutoKern().GetValue() )
+ {
+ SetAutoKern( ( !pIDocumentSettingAccess ||
+ !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ?
+ FontKerning::FontSpecific :
+ FontKerning::Asian );
+ }
+ else
+ SetAutoKern( FontKerning::NONE );
+ SetWordLineMode( pAttrSet->GetWordLineMode().GetValue() );
+ const SvxEscapementItem &rEsc = pAttrSet->GetEscapement();
+ SetEscapement( rEsc.GetEsc() );
+ if( m_aSub[SwFontScript::Latin].IsEsc() )
+ SetProportion( rEsc.GetProportionalHeight() );
+ SetCaseMap( pAttrSet->GetCaseMap().GetCaseMap() );
+ SetFixKerning( pAttrSet->GetKerning().GetValue() );
+ if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_BACKGROUND ) )
+ mxBackColor = pItem->GetColor();
+ if( const SvxBrushItem* pItem = pAttrSet->GetItemIfSet( RES_CHRATR_HIGHLIGHT ) )
+ SetHighlightColor(pItem->GetColor());
+ else
+ SetHighlightColor(COL_TRANSPARENT);
+ if( const SvxBoxItem* pBoxItem = pAttrSet->GetItemIfSet( RES_CHRATR_BOX ) )
+ {
+ SetTopBorder(pBoxItem->GetTop());
+ SetBottomBorder(pBoxItem->GetBottom());
+ SetRightBorder(pBoxItem->GetRight());
+ SetLeftBorder(pBoxItem->GetLeft());
+ SetTopBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::TOP));
+ SetBottomBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::BOTTOM));
+ SetRightBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::RIGHT));
+ SetLeftBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::LEFT));
+ }
+ else
+ {
+ SetTopBorder(nullptr);
+ SetBottomBorder(nullptr);
+ SetRightBorder(nullptr);
+ SetLeftBorder(nullptr);
+ SetTopBorderDist(0);
+ SetBottomBorderDist(0);
+ SetRightBorderDist(0);
+ SetLeftBorderDist(0);
+ }
+
+ if( const SvxShadowItem* pShadowItem = pAttrSet->GetItemIfSet( RES_CHRATR_SHADOW ) )
+ {
+ SetShadowColor(pShadowItem->GetColor());
+ SetShadowWidth(pShadowItem->GetWidth());
+ SetShadowLocation(pShadowItem->GetLocation());
+ }
+ else
+ {
+ SetShadowColor(COL_TRANSPARENT);
+ SetShadowWidth(0);
+ SetShadowLocation(SvxShadowLocation::NONE);
+ }
+
+ const SvxTwoLinesItem& rTwoLinesItem = pAttrSet->Get2Lines();
+ if ( ! rTwoLinesItem.GetValue() )
+ SetVertical( pAttrSet->GetCharRotate().GetValue() );
+ else
+ SetVertical( 0_deg10 );
+ if( pIDocumentSettingAccess && pIDocumentSettingAccess->get( DocumentSettingId::SMALL_CAPS_PERCENTAGE_66 ))
+ {
+ m_aSub[ SwFontScript::Latin ].m_bSmallCapsPercentage66 = true;
+ m_aSub[ SwFontScript::CJK ].m_bSmallCapsPercentage66 = true;
+ m_aSub[ SwFontScript::CTL ].m_bSmallCapsPercentage66 = true;
+ }
+}
+
+SwFont::~SwFont()
+{
+}
+
+SwFont& SwFont::operator=( const SwFont &rFont )
+{
+ if (this != &rFont)
+ {
+ m_aSub[SwFontScript::Latin] = rFont.m_aSub[SwFontScript::Latin];
+ m_aSub[SwFontScript::CJK] = rFont.m_aSub[SwFontScript::CJK];
+ m_aSub[SwFontScript::CTL] = rFont.m_aSub[SwFontScript::CTL];
+ m_nActual = rFont.m_nActual;
+ mxBackColor = rFont.mxBackColor;
+ m_aHighlightColor = rFont.m_aHighlightColor;
+ m_aTopBorder = rFont.m_aTopBorder;
+ m_aBottomBorder = rFont.m_aBottomBorder;
+ m_aRightBorder = rFont.m_aRightBorder;
+ m_aLeftBorder = rFont.m_aLeftBorder;
+ m_nTopBorderDist = rFont.m_nTopBorderDist;
+ m_nBottomBorderDist = rFont.m_nBottomBorderDist;
+ m_nRightBorderDist = rFont.m_nRightBorderDist;
+ m_nLeftBorderDist = rFont.m_nLeftBorderDist;
+ m_aShadowColor = rFont.m_aShadowColor;
+ m_nShadowWidth = rFont.m_nShadowWidth;
+ m_aShadowLocation = rFont.m_aShadowLocation;
+ m_aUnderColor = rFont.GetUnderColor();
+ m_aOverColor = rFont.GetOverColor();
+ m_nToxCount = 0;
+ m_nRefCount = 0;
+ m_nMetaCount = 0;
+ m_nContentControlCount = 0;
+ m_nInputFieldCount = 0;
+ m_bFontChg = rFont.m_bFontChg;
+ m_bOrgChg = rFont.m_bOrgChg;
+ m_bPaintBlank = rFont.m_bPaintBlank;
+ m_bGreyWave = rFont.m_bGreyWave;
+ }
+ return *this;
+}
+
+void SwFont::AllocFontCacheId( SwViewShell const *pSh, SwFontScript nWhich )
+{
+ SwFntAccess aFntAccess( m_aSub[nWhich].m_nFontCacheId, m_aSub[nWhich].m_nFontIndex,
+ &m_aSub[nWhich], pSh, true );
+}
+
+bool SwSubFont::IsSymbol( SwViewShell const *pSh )
+{
+ SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh, false );
+ return aFntAccess.Get()->IsSymbol();
+}
+
+bool SwSubFont::ChgFnt( SwViewShell const *pSh, OutputDevice& rOut )
+{
+ if ( pLastFont )
+ pLastFont->Unlock();
+ SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh, true );
+ SV_STAT( nChangeFont );
+
+ pLastFont = aFntAccess.Get();
+
+ pLastFont->SetDevFont( pSh, rOut );
+
+ pLastFont->Lock();
+ return LINESTYLE_NONE != GetUnderline() ||
+ LINESTYLE_NONE != GetOverline() ||
+ STRIKEOUT_NONE != GetStrikeout();
+}
+
+void SwFont::ChgPhysFnt( SwViewShell const *pSh, OutputDevice& rOut )
+{
+ if( m_bOrgChg && m_aSub[m_nActual].IsEsc() )
+ {
+ const sal_uInt8 nOldProp = m_aSub[m_nActual].GetPropr();
+ SetProportion( 100 );
+ ChgFnt( pSh, rOut );
+ SwFntAccess aFntAccess( m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex,
+ &m_aSub[m_nActual], pSh );
+ m_aSub[m_nActual].m_nOrgHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut );
+ m_aSub[m_nActual].m_nOrgAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
+ SetProportion( nOldProp );
+ m_bOrgChg = false;
+ }
+
+ if( m_bFontChg )
+ {
+ ChgFnt( pSh, rOut );
+ m_bFontChg = m_bOrgChg;
+ }
+ if( rOut.GetTextLineColor() != m_aUnderColor )
+ rOut.SetTextLineColor( m_aUnderColor );
+ if( rOut.GetOverlineColor() != m_aOverColor )
+ rOut.SetOverlineColor( m_aOverColor );
+}
+
+// Height = MaxAscent + MaxDescent
+// MaxAscent = Max (T1_ascent, T2_ascent + (Esc * T1_height) );
+// MaxDescent = Max (T1_height-T1_ascent,
+// T2_height-T2_ascent - (Esc * T1_height)
+sal_uInt16 SwSubFont::CalcEscHeight( const sal_uInt16 nOldHeight,
+ const sal_uInt16 nOldAscent ) const
+{
+ if( DFLT_ESC_AUTO_SUPER != GetEscapement() &&
+ DFLT_ESC_AUTO_SUB != GetEscapement() )
+ {
+ tools::Long nDescent = nOldHeight - nOldAscent -
+ ( static_cast<tools::Long>(m_nOrgHeight) * GetEscapement() ) / 100;
+ const sal_uInt16 nDesc = nDescent>0
+ ? std::max<sal_uInt16>( nDescent, m_nOrgHeight - m_nOrgAscent)
+ : m_nOrgHeight - m_nOrgAscent;
+ return ( nDesc + CalcEscAscent( nOldAscent ) );
+ }
+ return m_nOrgHeight;
+}
+
+short SwSubFont::CheckKerning_( )
+{
+ short nKernx = - short( Font::GetFontSize().Height() / 6 );
+
+ if ( nKernx < GetFixKerning() )
+ return GetFixKerning();
+ return nKernx;
+}
+
+sal_uInt16 SwSubFont::GetAscent( SwViewShell const *pSh, const OutputDevice& rOut )
+{
+ SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
+ const sal_uInt16 nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
+ return GetEscapement() ? CalcEscAscent( nAscent ) : nAscent;
+}
+
+sal_uInt16 SwSubFont::GetHeight( SwViewShell const *pSh, const OutputDevice& rOut )
+{
+ SV_STAT( nGetTextSize );
+ SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
+ const sal_uInt16 nHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut );
+ if ( GetEscapement() )
+ {
+ const sal_uInt16 nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
+ return CalcEscHeight( nHeight, nAscent ); // + nLeading;
+ }
+ return nHeight; // + nLeading;
+}
+
+sal_uInt16 SwSubFont::GetHangingBaseline( SwViewShell const *pSh, const OutputDevice& rOut )
+{
+ SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
+ return aFntAccess.Get()->GetFontHangingBaseline( pSh, rOut );
+}
+
+Size SwSubFont::GetTextSize_( SwDrawTextInfo& rInf )
+{
+ // Robust: the font is supposed to be set already, but better safe than
+ // sorry...
+ if ( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId ||
+ !IsSameInstance( rInf.GetpOut()->GetFont() ) )
+ ChgFnt( rInf.GetShell(), rInf.GetOut() );
+
+ SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
+
+ Size aTextSize;
+ TextFrameIndex const nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
+ ? TextFrameIndex(rInf.GetText().getLength())
+ : rInf.GetLen();
+ rInf.SetLen( nLn );
+ if( IsCapital() && nLn )
+ aTextSize = GetCapitalSize( rInf );
+ else
+ {
+ SV_STAT( nGetTextSize );
+ tools::Long nOldKern = rInf.GetKern();
+ const OUString oldText = rInf.GetText();
+ rInf.SetKern( CheckKerning() );
+ if ( !IsCaseMap() )
+ aTextSize = pLastFont->GetTextSize( rInf );
+ else
+ {
+ const OUString aTmp = CalcCaseMap( rInf.GetText() );
+ const OUString oldStr = rInf.GetText();
+ bool bCaseMapLengthDiffers(aTmp.getLength() != oldStr.getLength());
+
+ if(bCaseMapLengthDiffers && rInf.GetLen())
+ {
+ // If the length of the original string and the CaseMapped one
+ // are different, it is necessary to handle the given text part as
+ // a single snippet since its size may differ, too.
+ TextFrameIndex const nOldIdx(rInf.GetIdx());
+ TextFrameIndex const nOldLen(rInf.GetLen());
+ const OUString aSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldLen)));
+ const OUString aNewText(CalcCaseMap(aSnippet));
+
+ rInf.SetText( aNewText );
+ rInf.SetIdx( TextFrameIndex(0) );
+ rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
+
+ aTextSize = pLastFont->GetTextSize( rInf );
+
+ rInf.SetIdx( nOldIdx );
+ rInf.SetLen( nOldLen );
+ }
+ else
+ {
+ rInf.SetText( aTmp );
+ aTextSize = pLastFont->GetTextSize( rInf );
+ }
+
+ rInf.SetText(oldStr);
+ }
+ rInf.SetKern( nOldKern );
+ rInf.SetText(oldText);
+ // A word that's longer than one line, with escapement at the line
+ // break, must report its effective height.
+ if( GetEscapement() )
+ {
+ const sal_uInt16 nAscent = pLastFont->GetFontAscent( rInf.GetShell(),
+ rInf.GetOut() );
+ aTextSize.setHeight(
+ static_cast<tools::Long>(CalcEscHeight( o3tl::narrowing<sal_uInt16>(aTextSize.Height()), nAscent)) );
+ }
+ }
+
+ if (TextFrameIndex(1) == rInf.GetLen()
+ && CH_TXT_ATR_FIELDSTART == rInf.GetText()[sal_Int32(rInf.GetIdx())])
+ {
+ assert(!"this is presumably dead code");
+ TextFrameIndex const nOldIdx(rInf.GetIdx());
+ TextFrameIndex const nOldLen(rInf.GetLen());
+ const OUString aNewText(CH_TXT_ATR_SUBST_FIELDSTART);
+ rInf.SetText( aNewText );
+ rInf.SetIdx( TextFrameIndex(0) );
+ rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
+ aTextSize = pLastFont->GetTextSize( rInf );
+ rInf.SetIdx( nOldIdx );
+ rInf.SetLen( nOldLen );
+ }
+ else if (TextFrameIndex(1) == rInf.GetLen()
+ && CH_TXT_ATR_FIELDEND == rInf.GetText()[sal_Int32(rInf.GetIdx())])
+ {
+ assert(!"this is presumably dead code");
+ TextFrameIndex const nOldIdx(rInf.GetIdx());
+ TextFrameIndex const nOldLen(rInf.GetLen());
+ const OUString aNewText(CH_TXT_ATR_SUBST_FIELDEND);
+ rInf.SetText( aNewText );
+ rInf.SetIdx( TextFrameIndex(0) );
+ rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
+ aTextSize = pLastFont->GetTextSize( rInf );
+ rInf.SetIdx( nOldIdx );
+ rInf.SetLen( nOldLen );
+ }
+
+ return aTextSize;
+}
+
+void SwSubFont::DrawText_( SwDrawTextInfo &rInf, const bool bGrey )
+{
+ rInf.SetGreyWave( bGrey );
+ TextFrameIndex const nLn(rInf.GetText().getLength());
+ if( !rInf.GetLen() || !nLn )
+ return;
+ if (TextFrameIndex(COMPLETE_STRING) == rInf.GetLen())
+ rInf.SetLen( nLn );
+
+ FontLineStyle nOldUnder = LINESTYLE_NONE;
+ SwUnderlineFont* pUnderFnt = nullptr;
+
+ if( rInf.GetUnderFnt() )
+ {
+ nOldUnder = GetUnderline();
+ SetUnderline( LINESTYLE_NONE );
+ pUnderFnt = rInf.GetUnderFnt();
+ }
+
+ if( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
+ ChgFnt( rInf.GetShell(), rInf.GetOut() );
+
+ SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
+
+ const Point aOldPos(rInf.GetPos());
+ Point aPos( rInf.GetPos() );
+
+ if( GetEscapement() )
+ CalcEsc( rInf, aPos );
+
+ rInf.SetPos( aPos );
+ rInf.SetKern( CheckKerning() + rInf.GetCharacterSpacing() / SPACING_PRECISION_FACTOR );
+
+ if( IsCapital() )
+ DrawCapital( rInf );
+ else
+ {
+ SV_STAT( nDrawText );
+ if ( !IsCaseMap() )
+ pLastFont->DrawText( rInf );
+ else
+ {
+ const OUString oldStr = rInf.GetText();
+ const OUString aString( CalcCaseMap(oldStr) );
+ bool bCaseMapLengthDiffers(aString.getLength() != oldStr.getLength());
+
+ if(bCaseMapLengthDiffers && rInf.GetLen())
+ {
+ // If the length of the original string and the CaseMapped one
+ // are different, it is necessary to handle the given text part as
+ // a single snippet since its size may differ, too.
+ TextFrameIndex const nOldIdx(rInf.GetIdx());
+ TextFrameIndex const nOldLen(rInf.GetLen());
+ const OUString aSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldLen)));
+ const OUString aNewText = CalcCaseMap(aSnippet);
+
+ rInf.SetText( aNewText );
+ rInf.SetIdx( TextFrameIndex(0) );
+ rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
+
+ pLastFont->DrawText( rInf );
+
+ rInf.SetIdx( nOldIdx );
+ rInf.SetLen( nOldLen );
+ }
+ else
+ {
+ rInf.SetText( aString );
+ pLastFont->DrawText( rInf );
+ }
+
+ rInf.SetText(oldStr);
+ }
+ }
+
+ if( pUnderFnt && nOldUnder != LINESTYLE_NONE )
+ {
+ Size aFontSize = GetTextSize_( rInf );
+ const OUString oldStr = rInf.GetText();
+
+ TextFrameIndex const nOldIdx = rInf.GetIdx();
+ TextFrameIndex const nOldLen = rInf.GetLen();
+ tools::Long nSpace = 0;
+ if( rInf.GetSpace() )
+ {
+ TextFrameIndex nTmpEnd = nOldIdx + nOldLen;
+ if (nTmpEnd > TextFrameIndex(oldStr.getLength()))
+ nTmpEnd = TextFrameIndex(oldStr.getLength());
+
+ const SwScriptInfo* pSI = rInf.GetScriptInfo();
+
+ const bool bAsianFont =
+ ( rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() );
+ for (TextFrameIndex nTmp = nOldIdx; nTmp < nTmpEnd; ++nTmp)
+ {
+ if (CH_BLANK == oldStr[sal_Int32(nTmp)] || bAsianFont ||
+ (nTmp + TextFrameIndex(1) < TextFrameIndex(oldStr.getLength())
+ && pSI
+ && i18n::ScriptType::ASIAN == pSI->ScriptType(nTmp + TextFrameIndex(1))))
+ {
+ ++nSpace;
+ }
+ }
+
+ // if next portion if a hole portion we do not consider any
+ // extra space added because the last character was ASIAN
+ if ( nSpace && rInf.IsSpaceStop() && bAsianFont )
+ --nSpace;
+
+ nSpace *= rInf.GetSpace() / SPACING_PRECISION_FACTOR;
+ }
+
+ rInf.SetWidth( sal_uInt16(aFontSize.Width() + nSpace) );
+ rInf.SetTextIdxLen( " ", TextFrameIndex(0), TextFrameIndex(2) );
+ SetUnderline( nOldUnder );
+ rInf.SetUnderFnt( nullptr );
+
+ // set position for underline font
+ rInf.SetPos( pUnderFnt->GetPos() );
+
+ pUnderFnt->GetFont().DrawStretchText_( rInf );
+
+ rInf.SetUnderFnt( pUnderFnt );
+ rInf.SetTextIdxLen(oldStr, nOldIdx, nOldLen);
+ }
+
+ rInf.SetPos(aOldPos);
+}
+
+void SwSubFont::DrawStretchText_( SwDrawTextInfo &rInf )
+{
+ if( !rInf.GetLen() || !rInf.GetText().getLength() )
+ return;
+
+ FontLineStyle nOldUnder = LINESTYLE_NONE;
+ SwUnderlineFont* pUnderFnt = nullptr;
+
+ if( rInf.GetUnderFnt() )
+ {
+ nOldUnder = GetUnderline();
+ SetUnderline( LINESTYLE_NONE );
+ pUnderFnt = rInf.GetUnderFnt();
+ }
+
+ if ( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
+ ChgFnt( rInf.GetShell(), rInf.GetOut() );
+
+ SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
+
+ rInf.ApplyAutoColor();
+
+ const Point aOldPos(rInf.GetPos());
+ Point aPos( rInf.GetPos() );
+
+ if( GetEscapement() )
+ CalcEsc( rInf, aPos );
+
+ rInf.SetKern( CheckKerning() + rInf.GetCharacterSpacing() / SPACING_PRECISION_FACTOR );
+ rInf.SetPos( aPos );
+
+ if( IsCapital() )
+ DrawStretchCapital( rInf );
+ else
+ {
+ SV_STAT( nDrawStretchText );
+
+ if ( rInf.GetFrame() )
+ {
+ if ( rInf.GetFrame()->IsRightToLeft() )
+ rInf.GetFrame()->SwitchLTRtoRTL( aPos );
+
+ if ( rInf.GetFrame()->IsVertical() )
+ rInf.GetFrame()->SwitchHorizontalToVertical( aPos );
+
+ rInf.SetPos( aPos );
+ }
+
+ if ( !IsCaseMap() )
+ rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(),
+ rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ else
+ rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(),
+ CalcCaseMap(rInf.GetText()),
+ sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
+ }
+
+ if( pUnderFnt && nOldUnder != LINESTYLE_NONE )
+ {
+ const OUString oldStr = rInf.GetText();
+ TextFrameIndex const nOldIdx = rInf.GetIdx();
+ TextFrameIndex const nOldLen = rInf.GetLen();
+ rInf.SetTextIdxLen( " ", TextFrameIndex(0), TextFrameIndex(2) );
+ SetUnderline( nOldUnder );
+ rInf.SetUnderFnt( nullptr );
+
+ // set position for underline font
+ rInf.SetPos( pUnderFnt->GetPos() );
+
+ pUnderFnt->GetFont().DrawStretchText_( rInf );
+
+ rInf.SetUnderFnt( pUnderFnt );
+ rInf.SetTextIdxLen(oldStr, nOldIdx, nOldLen);
+ }
+
+ rInf.SetPos(aOldPos);
+}
+
+TextFrameIndex SwSubFont::GetModelPositionForViewPoint_( SwDrawTextInfo& rInf )
+{
+ if ( !pLastFont || pLastFont->GetOwner() != m_nFontCacheId )
+ ChgFnt( rInf.GetShell(), rInf.GetOut() );
+
+ SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
+
+ TextFrameIndex const nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
+ ? TextFrameIndex(rInf.GetText().getLength())
+ : rInf.GetLen();
+ rInf.SetLen( nLn );
+ TextFrameIndex nCursor(0);
+ if( IsCapital() && nLn )
+ nCursor = GetCapitalCursorOfst( rInf );
+ else
+ {
+ const OUString oldText = rInf.GetText();
+ tools::Long nOldKern = rInf.GetKern();
+ rInf.SetKern( CheckKerning() );
+ SV_STAT( nGetTextSize );
+ if ( !IsCaseMap() )
+ nCursor = pLastFont->GetModelPositionForViewPoint( rInf );
+ else
+ {
+ rInf.SetText( CalcCaseMap( rInf.GetText() ) );
+ nCursor = pLastFont->GetModelPositionForViewPoint( rInf );
+ }
+ rInf.SetKern( nOldKern );
+ rInf.SetText(oldText);
+ }
+ return nCursor;
+}
+
+void SwSubFont::CalcEsc( SwDrawTextInfo const & rInf, Point& rPos )
+{
+ tools::Long nOfst;
+
+ bool bVert = false;
+ bool bVertLRBT = false;
+ if (rInf.GetFrame())
+ {
+ bVert = rInf.GetFrame()->IsVertical();
+ bVertLRBT = rInf.GetFrame()->IsVertLRBT();
+ }
+ const Degree10 nDir = UnMapDirection(GetOrientation(), bVert, bVertLRBT);
+
+ switch ( GetEscapement() )
+ {
+ case DFLT_ESC_AUTO_SUB :
+ nOfst = m_nOrgHeight - m_nOrgAscent -
+ pLastFont->GetFontHeight( rInf.GetShell(), rInf.GetOut() ) +
+ pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() );
+
+ switch ( nDir.get() )
+ {
+ case 0 :
+ rPos.AdjustY(nOfst );
+ break;
+ case 900 :
+ rPos.AdjustX(nOfst );
+ break;
+ case 2700 :
+ rPos.AdjustX( -nOfst );
+ break;
+ }
+
+ break;
+ case DFLT_ESC_AUTO_SUPER :
+ nOfst = pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() ) -
+ m_nOrgAscent;
+
+ switch ( nDir.get() )
+ {
+ case 0 :
+ rPos.AdjustY(nOfst );
+ break;
+ case 900 :
+ rPos.AdjustX(nOfst );
+ break;
+ case 2700 :
+ rPos.AdjustX( -nOfst );
+ break;
+ }
+
+ break;
+ default :
+ nOfst = (static_cast<tools::Long>(m_nOrgHeight) * GetEscapement()) / 100;
+
+ switch ( nDir.get() )
+ {
+ case 0 :
+ rPos.AdjustY( -nOfst );
+ break;
+ case 900 :
+ rPos.AdjustX( -nOfst );
+ break;
+ case 2700 :
+ rPos.AdjustX(nOfst );
+ break;
+ }
+ }
+}
+
+// used during painting of small capitals
+void SwDrawTextInfo::Shift( Degree10 nDir )
+{
+#ifdef DBG_UTIL
+ OSL_ENSURE( m_bPos, "DrawTextInfo: Undefined Position" );
+ OSL_ENSURE( m_bSize, "DrawTextInfo: Undefined Width" );
+#endif
+
+ const bool bBidiPor = ( GetFrame() && GetFrame()->IsRightToLeft() ) !=
+ ( vcl::text::ComplexTextLayoutFlags::Default != ( vcl::text::ComplexTextLayoutFlags::BiDiRtl & GetpOut()->GetLayoutMode() ) );
+
+ bool bVert = false;
+ bool bVertLRBT = false;
+ if (GetFrame())
+ {
+ bVert = GetFrame()->IsVertical();
+ bVertLRBT = GetFrame()->IsVertLRBT();
+ }
+ nDir = bBidiPor ? 1800_deg10 : UnMapDirection(nDir, bVert, bVertLRBT);
+
+ switch ( nDir.get() )
+ {
+ case 0 :
+ m_aPos.AdjustX(GetSize().Width() );
+ break;
+ case 900 :
+ OSL_ENSURE( m_aPos.Y() >= GetSize().Width(), "Going underground" );
+ m_aPos.AdjustY( -(GetSize().Width()) );
+ break;
+ case 1800 :
+ m_aPos.AdjustX( -(GetSize().Width()) );
+ break;
+ case 2700 :
+ m_aPos.AdjustY(GetSize().Width() );
+ break;
+ }
+}
+
+/**
+ * @note Used for the "continuous underline" feature.
+ **/
+SwUnderlineFont::SwUnderlineFont(SwFont& rFnt, TextFrameIndex const nEnd, const Point& rPoint)
+ : m_aPos( rPoint ), m_nEnd( nEnd ), m_pFont( &rFnt )
+{
+};
+
+SwUnderlineFont::~SwUnderlineFont()
+{
+}
+
+/// Helper for filters to find true lineheight of a font
+tools::Long AttrSetToLineHeight( const IDocumentSettingAccess& rIDocumentSettingAccess,
+ const SwAttrSet &rSet,
+ const vcl::RenderContext &rOut, sal_Int16 nScript)
+{
+ SwFont aFont(&rSet, &rIDocumentSettingAccess);
+ SwFontScript nActual;
+ switch (nScript)
+ {
+ default:
+ case i18n::ScriptType::LATIN:
+ nActual = SwFontScript::Latin;
+ break;
+ case i18n::ScriptType::ASIAN:
+ nActual = SwFontScript::CJK;
+ break;
+ case i18n::ScriptType::COMPLEX:
+ nActual = SwFontScript::CTL;
+ break;
+ }
+ aFont.SetActual(nActual);
+
+ vcl::RenderContext &rMutableOut = const_cast<vcl::RenderContext &>(rOut);
+ const vcl::Font aOldFont(rMutableOut.GetFont());
+
+ rMutableOut.SetFont(aFont.GetActualFont());
+ tools::Long nHeight = rMutableOut.GetTextHeight();
+
+ rMutableOut.SetFont(aOldFont);
+ return nHeight;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/thints.cxx b/sw/source/core/txtnode/thints.cxx
new file mode 100644
index 000000000..f14c7d5ea
--- /dev/null
+++ b/sw/source/core/txtnode/thints.cxx
@@ -0,0 +1,3538 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <DocumentContentOperationsManager.hxx>
+#include <hintids.hxx>
+#include <editeng/rsiditem.hxx>
+#include <osl/diagnose.h>
+#include <svl/whiter.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/charhiddenitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <txtinet.hxx>
+#include <txtflcnt.hxx>
+#include <fmtfld.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtanchr.hxx>
+#include <fmtinfmt.hxx>
+#include <txtatr.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtftn.hxx>
+#include <txttxmrk.hxx>
+#include <txtrfmrk.hxx>
+#include <txtftn.hxx>
+#include <textlinebreak.hxx>
+#include <txtfld.hxx>
+#include <txtannotationfld.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <charfmt.hxx>
+#include <frmfmt.hxx>
+#include <ftnidx.hxx>
+#include <fmtruby.hxx>
+#include <fmtmeta.hxx>
+#include <formatcontentcontrol.hxx>
+#include <textcontentcontrol.hxx>
+#include <breakit.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <fldbas.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <rolbck.hxx>
+#include <ddefld.hxx>
+#include <docufld.hxx>
+#include <expfld.hxx>
+#include <usrfld.hxx>
+#include <poolfmt.hxx>
+#include <istyleaccess.hxx>
+#include <docsh.hxx>
+#include <algorithm>
+#include <map>
+#include <memory>
+
+#include <rdfhelper.hxx>
+#include <hints.hxx>
+
+#ifdef DBG_UTIL
+#define CHECK Check(true);
+#define CHECK_NOTMERGED Check(false);
+#else
+#define CHECK_NOTMERGED
+#endif
+
+using namespace ::com::sun::star::i18n;
+
+SwpHints::SwpHints(const SwTextNode& rParent)
+ : m_rParent(rParent)
+ , m_pHistory(nullptr)
+ , m_bInSplitNode(false)
+ , m_bCalcHiddenParaField(false)
+ , m_bHiddenByParaField(false)
+ , m_bFootnote(false)
+ , m_bDDEFields(false)
+ , m_bStartMapNeedsSorting(false)
+ , m_bEndMapNeedsSorting(false)
+ , m_bWhichMapNeedsSorting(false)
+{
+}
+
+static void TextAttrDelete( SwDoc & rDoc, SwTextAttr * const pAttr )
+{
+ if (RES_TXTATR_META == pAttr->Which() ||
+ RES_TXTATR_METAFIELD == pAttr->Which())
+ {
+ static_txtattr_cast<SwTextMeta *>(pAttr)->ChgTextNode(nullptr); // prevents ASSERT
+ }
+ else if (pAttr->Which() == RES_TXTATR_CONTENTCONTROL)
+ {
+ static_txtattr_cast<SwTextContentControl*>(pAttr)->ChgTextNode(nullptr);
+ }
+ SwTextAttr::Destroy( pAttr, rDoc.GetAttrPool() );
+}
+
+static bool TextAttrContains(const sal_Int32 nPos, const SwTextAttrEnd * const pAttr)
+{
+ return (pAttr->GetStart() < nPos) && (nPos < *pAttr->End());
+}
+
+// a: |-----|
+// b:
+// |---| => valid: b before a
+// |-----| => valid: start == end; b before a
+// |---------| => invalid: overlap (1)
+// |-----------| => valid: same end; b around a
+// |-----------------| => valid: b around a
+// |---| => valid; same start; b within a
+// |-----| => valid; same start and end; b around or within a?
+// |-----------| => valid: same start: b around a
+// |-| => valid: b within a
+// |---| => valid: same end; b within a
+// |---------| => invalid: overlap (2)
+// |-----| => valid: end == start; b after a
+// |---| => valid: b after a
+// ===> 2 invalid overlap cases
+static
+bool isOverlap(const sal_Int32 nStart1, const sal_Int32 nEnd1,
+ const sal_Int32 nStart2, const sal_Int32 nEnd2)
+{
+ return
+ ((nStart1 > nStart2) && (nStart1 < nEnd2) && (nEnd1 > nEnd2)) // (1)
+ || ((nStart1 < nStart2) && (nStart2 < nEnd1) && (nEnd1 < nEnd2)); // (2)
+}
+
+/// #i106930#: now asymmetric: empty hint1 is _not_ nested, but empty hint2 is
+static
+bool isNestedAny(const sal_Int32 nStart1, const sal_Int32 nEnd1,
+ const sal_Int32 nStart2, const sal_Int32 nEnd2)
+{
+ return ((nStart1 == nStart2) || (nEnd1 == nEnd2))
+ // same start/end: nested except if hint1 empty and hint2 not empty
+ ? (nStart1 != nEnd1) || (nStart2 == nEnd2)
+ : ((nStart1 < nStart2) ? (nEnd1 >= nEnd2) : (nEnd1 <= nEnd2));
+}
+
+static
+bool isSelfNestable(const sal_uInt16 nWhich)
+{
+ if ((RES_TXTATR_INETFMT == nWhich) ||
+ (RES_TXTATR_CJK_RUBY == nWhich) ||
+ (RES_TXTATR_INPUTFIELD == nWhich))
+ return false;
+ assert((RES_TXTATR_META == nWhich) ||
+ (RES_TXTATR_METAFIELD == nWhich) ||
+ (RES_TXTATR_CONTENTCONTROL == nWhich));
+ return true;
+}
+
+static
+bool isSplittable(const sal_uInt16 nWhich)
+{
+ if ((RES_TXTATR_INETFMT == nWhich) ||
+ (RES_TXTATR_CJK_RUBY == nWhich))
+ return true;
+ assert((RES_TXTATR_META == nWhich) ||
+ (RES_TXTATR_METAFIELD == nWhich) ||
+ (RES_TXTATR_INPUTFIELD == nWhich) ||
+ (RES_TXTATR_CONTENTCONTROL == nWhich));
+ return false;
+}
+
+namespace {
+
+enum Split_t { FAIL, SPLIT_NEW, SPLIT_OTHER };
+
+}
+
+/**
+ Calculate splitting policy for overlapping hints, based on what kind of
+ hint is inserted, and what kind of existing hint overlaps.
+ */
+static Split_t
+splitPolicy(const sal_uInt16 nWhichNew, const sal_uInt16 nWhichOther)
+{
+ if (!isSplittable(nWhichOther))
+ {
+ if (!isSplittable(nWhichNew))
+ return FAIL;
+ else
+ return SPLIT_NEW;
+ }
+ else
+ {
+ if ( RES_TXTATR_INPUTFIELD == nWhichNew )
+ return FAIL;
+ else if ( (RES_TXTATR_INETFMT == nWhichNew) &&
+ (RES_TXTATR_CJK_RUBY == nWhichOther) )
+ return SPLIT_NEW;
+ else
+ return SPLIT_OTHER;
+ }
+}
+
+void SwTextINetFormat::InitINetFormat(SwTextNode & rNode)
+{
+ ChgTextNode(&rNode);
+ SwCharFormat * const pFormat(
+ rNode.GetDoc().getIDocumentStylePoolAccess().GetCharFormatFromPool(RES_POOLCHR_INET_NORMAL) );
+ pFormat->Add( this );
+}
+
+void SwTextRuby::InitRuby(SwTextNode & rNode)
+{
+ ChgTextNode(&rNode);
+ SwCharFormat * const pFormat(
+ rNode.GetDoc().getIDocumentStylePoolAccess().GetCharFormatFromPool(RES_POOLCHR_RUBYTEXT) );
+ pFormat->Add( this );
+}
+
+/**
+ Create a new nesting text hint.
+ */
+static SwTextAttrNesting *
+MakeTextAttrNesting(SwTextNode & rNode, SwTextAttrNesting & rNesting,
+ const sal_Int32 nStart, const sal_Int32 nEnd)
+{
+ SwTextAttr * const pNew( MakeTextAttr(
+ rNode.GetDoc(), rNesting.GetAttr(), nStart, nEnd ) );
+ switch (pNew->Which())
+ {
+ case RES_TXTATR_INETFMT:
+ {
+ static_txtattr_cast<SwTextINetFormat*>(pNew)->InitINetFormat(rNode);
+ break;
+ }
+ case RES_TXTATR_CJK_RUBY:
+ {
+ static_txtattr_cast<SwTextRuby*>(pNew)->InitRuby(rNode);
+ break;
+ }
+ default:
+ assert(!"MakeTextAttrNesting: what the hell is that?");
+ break;
+ }
+ return static_txtattr_cast<SwTextAttrNesting*>(pNew);
+}
+
+typedef std::vector<SwTextAttrNesting *> NestList_t;
+
+static NestList_t::iterator
+lcl_DoSplitImpl(NestList_t & rSplits, SwTextNode & rNode,
+ NestList_t::iterator const iter, sal_Int32 const nSplitPos,
+ bool const bSplitAtStart, bool const bOtherDummy)
+{
+ const sal_Int32 nStartPos( // skip other's dummy character!
+ (bSplitAtStart && bOtherDummy) ? nSplitPos + 1 : nSplitPos );
+ SwTextAttrNesting * const pNew( MakeTextAttrNesting(
+ rNode, **iter, nStartPos, *(*iter)->GetEnd() ) );
+ (*iter)->SetEnd(nSplitPos);
+ return rSplits.insert(iter + 1, pNew);
+}
+
+static void
+lcl_DoSplitNew(NestList_t & rSplits, SwTextNode & rNode,
+ const sal_Int32 nNewStart,
+ const sal_Int32 nOtherStart, const sal_Int32 nOtherEnd, bool bOtherDummy)
+{
+ const bool bSplitAtStart(nNewStart < nOtherStart);
+ const sal_Int32 nSplitPos( bSplitAtStart ? nOtherStart : nOtherEnd );
+ // first find the portion that is split (not necessarily the last one!)
+ NestList_t::iterator const iter(
+ std::find_if( rSplits.begin(), rSplits.end(),
+ [nSplitPos](SwTextAttrEnd * const pAttr) {
+ return TextAttrContains(nSplitPos, pAttr);
+ } ) );
+ if (iter != rSplits.end()) // already split here?
+ {
+ lcl_DoSplitImpl(rSplits, rNode, iter, nSplitPos, bSplitAtStart, bOtherDummy);
+ }
+}
+
+/**
+ Insert nesting hint into the hints array. Also calls NoteInHistory.
+ @param rNewHint the hint to be inserted (must not overlap existing!)
+ */
+void SwpHints::InsertNesting(SwTextAttrNesting & rNewHint)
+{
+ Insert(& rNewHint);
+ NoteInHistory( & rNewHint, true );
+}
+
+/**
+
+The following hints correspond to well-formed XML elements in ODF:
+RES_TXTATR_INETFMT, RES_TXTATR_CJK_RUBY, RES_TXTATR_META, RES_TXTATR_METAFIELD,
+RES_TXTATR_CONTENTCONTROL
+
+The writer core must ensure that these do not overlap; if they did,
+the document would not be storable as ODF.
+
+Also, a Hyperlink must not be nested within another Hyperlink,
+and a Ruby must not be nested within another Ruby.
+
+The ODF export in xmloff will only put a hyperlink into a ruby, never a ruby
+into a hyperlink.
+
+Unfortunately the UNO API for Hyperlink and Ruby consists of the properties
+Hyperlink* and Ruby* of the css.text.CharacterProperties service. In other
+words, they are treated as formatting attributes, not as content entities.
+Furthermore, for API users it is not possible to easily test whether a certain
+range would be overlapping with other nested attributes, and most importantly,
+<em>which ones</em>, so we can hardly refuse to insert these in cases of
+overlap.
+
+It is possible to split Hyperlink and Ruby into multiple portions, such that
+the result is properly nested.
+
+meta and meta-field must not be split, because they have xml:id.
+
+content controls should not split, either.
+
+These constraints result in the following design:
+
+RES_TXTATR_INETFMT:
+ always succeeds
+ inserts n attributes split at RES_TXTATR_CJK_RUBY, RES_TXTATR_META,
+ RES_TXTATR_METAFIELD
+ may replace existing RES_TXTATR_INETFMT at overlap
+RES_TXTATR_CJK_RUBY:
+ always succeeds
+ inserts n attributes split at RES_TXTATR_META, RES_TXTATR_METAFIELD
+ may replace existing RES_TXTATR_CJK_RUBY at overlap
+ may split existing overlapping RES_TXTATR_INETFMT
+RES_TXTATR_META:
+ may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
+ may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
+ inserts 1 attribute
+RES_TXTATR_METAFIELD:
+ may fail if overlapping existing RES_TXTATR_META/RES_TXTATR_METAFIELD
+ may split existing overlapping RES_TXTATR_INETFMT or RES_TXTATR_CJK_RUBY
+ inserts 1 attribute
+
+The nesting is expressed by the position of the hints.
+RES_TXTATR_META and RES_TXTATR_METAFIELD have a CH_TXTATR, and there can
+only be one such hint starting and ending at a given position.
+Only RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY lack a CH_TXTATR.
+The interpretation given is that RES_TXTATR_CJK_RUBY is always around
+a RES_TXTATR_INETFMT at the same start and end position (which corresponds
+with the UNO API).
+Both of these are always around a nesting hint with CH_TXTATR at the same
+start and end position (if they should be inside, then the start should be
+after the CH_TXTATR).
+It would probably be a bad idea to add another nesting hint without
+CH_TXTATR; on the other hand, it would be difficult adding a CH_TXTATR to
+RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY, due to the overwriting and
+splitting of existing hints that is necessary for backward compatibility.
+
+ @param rNode the text node
+ @param rHint the hint to be inserted
+ @returns true iff hint was successfully inserted
+*/
+bool
+SwpHints::TryInsertNesting( SwTextNode & rNode, SwTextAttrNesting & rNewHint )
+{
+// INVARIANT: the nestable hints in the array are properly nested
+ const sal_uInt16 nNewWhich( rNewHint.Which() );
+ const sal_Int32 nNewStart( rNewHint.GetStart() );
+ const sal_Int32 nNewEnd ( *rNewHint.GetEnd() );
+ const bool bNewSelfNestable( isSelfNestable(nNewWhich) );
+
+ assert( (RES_TXTATR_INETFMT == nNewWhich) ||
+ (RES_TXTATR_CJK_RUBY == nNewWhich) ||
+ (RES_TXTATR_META == nNewWhich) ||
+ (RES_TXTATR_METAFIELD == nNewWhich) ||
+ (RES_TXTATR_CONTENTCONTROL == nNewWhich) ||
+ (RES_TXTATR_INPUTFIELD == nNewWhich));
+
+ NestList_t OverlappingExisting; // existing hints to be split
+ NestList_t OverwrittenExisting; // existing hints to be replaced
+ NestList_t SplitNew; // new hints to be inserted
+
+ SplitNew.push_back(& rNewHint);
+
+ // pass 1: split the inserted hint into fragments if necessary
+ for ( size_t i = 0; i < Count(); ++i )
+ {
+ SwTextAttr * const pOther = GetSortedByEnd(i);
+
+ if (pOther->IsNesting())
+ {
+ const sal_uInt16 nOtherWhich( pOther->Which() );
+ const sal_Int32 nOtherStart( pOther->GetStart() );
+ const sal_Int32 nOtherEnd ( *pOther->GetEnd() );
+ if (isOverlap(nNewStart, nNewEnd, nOtherStart, nOtherEnd ))
+ {
+ switch (splitPolicy(nNewWhich, nOtherWhich))
+ {
+ case FAIL:
+ SAL_INFO("sw.core", "cannot insert hint: overlap");
+ for (const auto& aSplit : SplitNew)
+ TextAttrDelete(rNode.GetDoc(), aSplit);
+ return false;
+ case SPLIT_NEW:
+ lcl_DoSplitNew(SplitNew, rNode, nNewStart,
+ nOtherStart, nOtherEnd, pOther->HasDummyChar());
+ break;
+ case SPLIT_OTHER:
+ OverlappingExisting.push_back(
+ static_txtattr_cast<SwTextAttrNesting*>(pOther));
+ break;
+ default:
+ assert(!"bad code monkey");
+ break;
+ }
+ }
+ else if (isNestedAny(nNewStart, nNewEnd, nOtherStart, nOtherEnd))
+ {
+ if (!bNewSelfNestable && (nNewWhich == nOtherWhich))
+ {
+ // ruby and hyperlink: if there is nesting, _overwrite_
+ OverwrittenExisting.push_back(
+ static_txtattr_cast<SwTextAttrNesting*>(pOther));
+ }
+ else if ((nNewStart == nOtherStart) && pOther->HasDummyChar())
+ {
+ if (rNewHint.HasDummyChar())
+ {
+ assert(!"ERROR: inserting duplicate CH_TXTATR hint");
+ return false;
+ } else if (nNewEnd < nOtherEnd) {
+ // other has dummy char, new is inside other, but
+ // new contains the other's dummy char?
+ // should be corrected because it may lead to problems
+ // in SwXMeta::createEnumeration
+ // SplitNew is sorted, so this is the first split
+ assert(SplitNew.front()->GetStart() == nNewStart);
+ SplitNew.front()->SetStart(nNewStart + 1);
+ }
+ }
+ }
+ }
+ }
+
+ // pass 1b: tragically need to check for fieldmarks here too
+ for (auto iter = SplitNew.begin(); iter != SplitNew.end(); ++iter)
+ {
+ SwPaM const temp(rNode, (*iter)->GetStart(), rNode, *(*iter)->GetEnd());
+ std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks;
+ sw::CalcBreaks(Breaks, temp, true);
+ if (!Breaks.empty())
+ {
+ if (!isSplittable(nNewWhich))
+ {
+ SAL_INFO("sw.core", "cannot insert hint: fieldmark overlap");
+ assert(SplitNew.size() == 1);
+ TextAttrDelete(rNode.GetDoc(), &rNewHint);
+ return false;
+ }
+ else
+ {
+ for (auto const& rPos : Breaks)
+ {
+ assert(rPos.first == rNode.GetIndex());
+ iter = lcl_DoSplitImpl(SplitNew, rNode, iter,
+ rPos.second, true, true);
+ }
+ }
+ }
+ }
+
+ assert((isSplittable(nNewWhich) || SplitNew.size() == 1) &&
+ "splitting the unsplittable ???");
+
+ // pass 2: split existing hints that overlap/nest with new hint
+ // do not iterate over hints array, but over remembered set of overlapping
+ // hints, to keep things simple w.r.t. insertion/removal
+ // N.B: if there is a hint that splits the inserted hint, then
+ // that hint would also have already split any hint in OverlappingExisting
+ // so any hint in OverlappingExisting can be split at most by one hint
+ // in SplitNew, or even not at all (this is not true for existing hints
+ // that go _around_ new hint, which is the reason d'^etre for pass 4)
+ for (auto& rpOther : OverlappingExisting)
+ {
+ const sal_Int32 nOtherStart( rpOther->GetStart() );
+ const sal_Int32 nOtherEnd ( *rpOther->GetEnd() );
+
+ for (const auto& rpNew : SplitNew)
+ {
+ const sal_Int32 nSplitNewStart( rpNew->GetStart() );
+ const sal_Int32 nSplitNewEnd ( *rpNew->GetEnd() );
+ // 4 cases: within, around, overlap l, overlap r, (OTHER: no action)
+ const bool bRemoveOverlap(
+ !bNewSelfNestable && (nNewWhich == rpOther->Which()) );
+
+ switch (ComparePosition(nSplitNewStart, nSplitNewEnd,
+ nOtherStart, nOtherEnd))
+ {
+ case SwComparePosition::Inside:
+ {
+ assert(!bRemoveOverlap &&
+ "this one should be in OverwrittenExisting?");
+ }
+ break;
+ case SwComparePosition::Outside:
+ case SwComparePosition::Equal:
+ {
+ assert(!"existing hint inside new hint: why?");
+ }
+ break;
+ case SwComparePosition::OverlapBefore:
+ {
+ Delete( rpOther ); // this also does NoteInHistory!
+ rpOther->SetStart(nSplitNewEnd);
+ InsertNesting( *rpOther );
+ if (!bRemoveOverlap)
+ {
+ if ( MAX_HINTS <= Count() )
+ {
+ SAL_INFO("sw.core", "hints array full :-(");
+ return false;
+ }
+ SwTextAttrNesting * const pOtherLeft(
+ MakeTextAttrNesting( rNode, *rpOther,
+ nOtherStart, nSplitNewEnd ) );
+ InsertNesting( *pOtherLeft );
+ }
+ }
+ break;
+ case SwComparePosition::OverlapBehind:
+ {
+ Delete( rpOther ); // this also does NoteInHistory!
+ rpOther->SetEnd(nSplitNewStart);
+ InsertNesting( *rpOther );
+ if (!bRemoveOverlap)
+ {
+ if ( MAX_HINTS <= Count() )
+ {
+ SAL_INFO("sw.core", "hints array full :-(");
+ return false;
+ }
+ SwTextAttrNesting * const pOtherRight(
+ MakeTextAttrNesting( rNode, *rpOther,
+ nSplitNewStart, nOtherEnd ) );
+ InsertNesting( *pOtherRight );
+ }
+ }
+ break;
+ default:
+ break; // overlap resolved by splitting new: nothing to do
+ }
+ }
+ }
+
+ if ( MAX_HINTS <= Count() || MAX_HINTS - Count() <= SplitNew.size() )
+ {
+ SAL_INFO("sw.core", "hints array full :-(");
+ return false;
+ }
+
+ // pass 3: insert new hints
+ for (const auto& rpHint : SplitNew)
+ {
+ InsertNesting(*rpHint);
+ }
+
+ // pass 4: handle overwritten hints
+ // RES_TXTATR_INETFMT and RES_TXTATR_CJK_RUBY should displace attributes
+ // of the same kind.
+ for (auto& rpOther : OverwrittenExisting)
+ {
+ const sal_Int32 nOtherStart( rpOther->GetStart() );
+ const sal_Int32 nOtherEnd ( *rpOther->GetEnd() );
+
+ // overwritten portion is given by start/end of inserted hint
+ if ((nNewStart <= nOtherStart) && (nOtherEnd <= nNewEnd))
+ {
+ Delete(rpOther);
+ rNode.DestroyAttr( rpOther );
+ }
+ else
+ {
+ assert((nOtherStart < nNewStart) || (nNewEnd < nOtherEnd));
+ // scenario: there is a RUBY, and contained within that a META;
+ // now a RUBY is inserted within the META => the existing RUBY is split:
+ // here it is not possible to simply insert the left/right fragment
+ // of the existing RUBY because they <em>overlap</em> with the META!
+ Delete( rpOther ); // this also does NoteInHistory!
+ if (nNewEnd < nOtherEnd)
+ {
+ SwTextAttrNesting * const pOtherRight(
+ MakeTextAttrNesting(
+ rNode, *rpOther, nNewEnd, nOtherEnd ) );
+ bool const bSuccess( TryInsertNesting(rNode, *pOtherRight) );
+ SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 1 failed?");
+ }
+ if (nOtherStart < nNewStart)
+ {
+ rpOther->SetEnd(nNewStart);
+ bool const bSuccess( TryInsertNesting(rNode, *rpOther) );
+ SAL_WARN_IF(!bSuccess, "sw.core", "recursive call 2 failed?");
+ }
+ else
+ {
+ rNode.DestroyAttr(rpOther);
+ }
+ }
+ }
+
+ return true;
+}
+
+// This function takes care for the following text attribute:
+// RES_TXTATR_CHARFMT, RES_TXTATR_AUTOFMT
+// These attributes have to be handled in a special way (Portion building).
+
+// The new attribute will be split by any existing RES_TXTATR_AUTOFMT or
+// RES_TXTATR_CHARFMT. The new attribute itself will
+// split any existing RES_TXTATR_AUTOFMT or RES_TXTATR_CHARFMT.
+
+void SwpHints::BuildPortions( SwTextNode& rNode, SwTextAttr& rNewHint,
+ const SetAttrMode nMode )
+{
+ const sal_uInt16 nWhich = rNewHint.Which();
+
+ const sal_Int32 nThisStart = rNewHint.GetStart();
+ const sal_Int32 nThisEnd = *rNewHint.GetEnd();
+ const bool bNoLengthAttribute = nThisStart == nThisEnd;
+
+ std::vector<SwTextAttr*> aInsDelHints;
+
+ assert( RES_TXTATR_CHARFMT == rNewHint.Which() ||
+ RES_TXTATR_AUTOFMT == rNewHint.Which() );
+
+ // 2. Find the hints which cover the start and end position
+ // of the new hint. These hints have to be split into two portions:
+
+ if ( !bNoLengthAttribute ) // nothing to do for no length attributes
+ {
+ for ( size_t i = 0; i < Count(); ++i )
+ {
+ // we're modifying stuff here which affects the sorting, and we
+ // don't want it changing underneath us
+ SwTextAttr* pOther = GetWithoutResorting(i);
+
+ if ( RES_TXTATR_CHARFMT != pOther->Which() &&
+ RES_TXTATR_AUTOFMT != pOther->Which() )
+ continue;
+
+ sal_Int32 nOtherStart = pOther->GetStart();
+ const sal_Int32 nOtherEnd = *pOther->GetEnd();
+
+ // Check if start of new attribute overlaps with pOther:
+ // Split pOther if necessary:
+ if ( nOtherStart < nThisStart && nThisStart < nOtherEnd )
+ {
+ SwTextAttr* pNewAttr = MakeTextAttr( rNode.GetDoc(),
+ pOther->GetAttr(), nOtherStart, nThisStart );
+ if ( RES_TXTATR_CHARFMT == pOther->Which() )
+ {
+ static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(
+ static_txtattr_cast<SwTextCharFormat*>(pOther)->GetSortNumber() );
+ }
+ aInsDelHints.push_back( pNewAttr );
+
+ NoteInHistory( pOther );
+ pOther->SetStart(nThisStart);
+ NoteInHistory( pOther, true );
+
+ nOtherStart = nThisStart;
+ }
+
+ // Check if end of new attribute overlaps with pOther:
+ // Split pOther if necessary:
+ if ( nOtherStart < nThisEnd && nThisEnd < nOtherEnd )
+ {
+ SwTextAttr* pNewAttr = MakeTextAttr( rNode.GetDoc(),
+ pOther->GetAttr(), nOtherStart, nThisEnd );
+ if ( RES_TXTATR_CHARFMT == pOther->Which() )
+ {
+ static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(
+ static_txtattr_cast<SwTextCharFormat*>(pOther)->GetSortNumber());
+ }
+ aInsDelHints.push_back( pNewAttr );
+
+ NoteInHistory( pOther );
+ pOther->SetStart(nThisEnd);
+ NoteInHistory( pOther, true );
+ }
+ }
+
+ // Insert the newly created attributes:
+ for ( const auto& rpHint : aInsDelHints )
+ {
+ Insert( rpHint );
+ NoteInHistory( rpHint, true );
+ }
+ }
+
+#ifdef DBG_UTIL
+ if( !rNode.GetDoc().IsInReading() )
+ CHECK_NOTMERGED; // ignore flags not set properly yet, don't check them
+#endif
+
+ // 4. Split rNewHint into 1 ... n new hints:
+
+ o3tl::sorted_vector<sal_Int32> aBounds;
+ aBounds.insert( nThisStart );
+ aBounds.insert( nThisEnd );
+
+ if ( !bNoLengthAttribute ) // nothing to do for no length attributes
+ {
+ for ( size_t i = 0; i < Count(); ++i )
+ {
+ const SwTextAttr* pOther = Get(i);
+
+ if ( RES_TXTATR_CHARFMT != pOther->Which() &&
+ RES_TXTATR_AUTOFMT != pOther->Which() )
+ continue;
+
+ const sal_Int32 nOtherStart = pOther->GetStart();
+ const sal_Int32 nOtherEnd = *pOther->End();
+
+ if (nThisStart <= nOtherStart && nOtherStart <= nThisEnd)
+ aBounds.insert( nOtherStart );
+ if (nThisStart <= nOtherEnd && nOtherEnd <= nThisEnd)
+ aBounds.insert( nOtherEnd );
+ }
+ }
+
+ auto aStartIter = aBounds.lower_bound( nThisStart );
+ auto aEndIter = aBounds.upper_bound( nThisEnd );
+ sal_Int32 nPorStart = *aStartIter;
+ ++aStartIter;
+ bool bDestroyHint = true;
+
+ // Insert the 1...n new parts of the new attribute:
+
+ while ( aStartIter != aEndIter || bNoLengthAttribute )
+ {
+ OSL_ENSURE( bNoLengthAttribute || nPorStart < *aStartIter, "AUTOSTYLES: BuildPortion trouble" );
+
+ const sal_Int32 nPorEnd = bNoLengthAttribute ? nPorStart : *aStartIter;
+ aInsDelHints.clear();
+
+ // Get all hints that are in [nPorStart, nPorEnd[:
+ for ( size_t i = 0; i < Count(); ++i )
+ {
+ // we get called from TryInsertHint, which changes ordering
+ SwTextAttr *pOther = GetWithoutResorting(i);
+
+ if ( RES_TXTATR_CHARFMT != pOther->Which() &&
+ RES_TXTATR_AUTOFMT != pOther->Which() )
+ continue;
+
+ const sal_Int32 nOtherStart = pOther->GetStart();
+
+ if ( nOtherStart > nPorStart )
+ break;
+
+ if ( pOther->GetEnd() && *pOther->GetEnd() == nPorEnd && nOtherStart == nPorStart )
+ {
+ OSL_ENSURE( *pOther->GetEnd() == nPorEnd, "AUTOSTYLES: BuildPortion trouble" );
+ aInsDelHints.push_back( pOther );
+ }
+ }
+
+ SwTextAttr* pNewAttr = nullptr;
+ if ( RES_TXTATR_CHARFMT == nWhich )
+ {
+ // pNewHint can be inserted after calculating the sort value.
+ // This should ensure, that pNewHint comes behind the already present
+ // character style
+ sal_uInt16 nCharStyleCount = 0;
+ for ( const auto& rpHint : aInsDelHints )
+ {
+ if ( RES_TXTATR_CHARFMT == rpHint->Which() )
+ {
+ // #i74589#
+ const SwFormatCharFormat& rOtherCharFormat = rpHint->GetCharFormat();
+ const SwFormatCharFormat& rThisCharFormat = rNewHint.GetCharFormat();
+ const bool bSameCharFormat = rOtherCharFormat.GetCharFormat() == rThisCharFormat.GetCharFormat();
+
+ // #i90311#
+ // Do not remove existing character format hint during XML import
+ if ( !rNode.GetDoc().IsInXMLImport() &&
+ ( !( SetAttrMode::DONTREPLACE & nMode ) ||
+ bNoLengthAttribute ||
+ bSameCharFormat ) )
+ {
+ // Remove old hint
+ Delete( rpHint );
+ rNode.DestroyAttr( rpHint );
+ }
+ else
+ ++nCharStyleCount;
+ }
+ else
+ {
+ // remove all attributes from auto styles, which are explicitly set in
+ // the new character format:
+ OSL_ENSURE( RES_TXTATR_AUTOFMT == rpHint->Which(), "AUTOSTYLES - Misc trouble" );
+ SwTextAttr* pOther = rpHint;
+ const std::shared_ptr<SfxItemSet> & pOldStyle = static_cast<const SwFormatAutoFormat&>(pOther->GetAttr()).GetStyleHandle();
+
+ // For each attribute in the automatic style check if it
+ // is also set the new character style:
+ SfxItemSet aNewSet( *pOldStyle->GetPool(),
+ aCharAutoFormatSetRange);
+ SfxItemIter aItemIter( *pOldStyle );
+ const SfxPoolItem* pItem = aItemIter.GetCurItem();
+ do
+ {
+ if ( !CharFormat::IsItemIncluded( pItem->Which(), &rNewHint ) )
+ {
+ aNewSet.Put( *pItem );
+ }
+
+ pItem = aItemIter.NextItem();
+ } while (pItem);
+
+ // Remove old hint
+ Delete( pOther );
+ rNode.DestroyAttr( pOther );
+
+ // Create new AutoStyle
+ if ( aNewSet.Count() )
+ {
+ pNewAttr = MakeTextAttr( rNode.GetDoc(),
+ aNewSet, nPorStart, nPorEnd );
+ Insert( pNewAttr );
+ NoteInHistory( pNewAttr, true );
+ }
+ }
+ }
+
+ // If there is no current hint and start and end of rNewHint
+ // is ok, we do not need to create a new txtattr.
+ if ( nPorStart == nThisStart &&
+ nPorEnd == nThisEnd &&
+ !nCharStyleCount )
+ {
+ pNewAttr = &rNewHint;
+ bDestroyHint = false;
+ }
+ else
+ {
+ pNewAttr = MakeTextAttr( rNode.GetDoc(), rNewHint.GetAttr(),
+ nPorStart, nPorEnd );
+ static_txtattr_cast<SwTextCharFormat*>(pNewAttr)->SetSortNumber(nCharStyleCount);
+ }
+ }
+ else
+ {
+ // Find the current autostyle. Mix attributes if necessary.
+ SwTextAttr* pCurrentAutoStyle = nullptr;
+ SwTextAttr* pCurrentCharFormat = nullptr;
+ for ( const auto& rpHint : aInsDelHints )
+ {
+ if ( RES_TXTATR_AUTOFMT == rpHint->Which() )
+ pCurrentAutoStyle = rpHint;
+ else if ( RES_TXTATR_CHARFMT == rpHint->Which() )
+ pCurrentCharFormat = rpHint;
+ }
+
+ std::shared_ptr<SfxItemSet> pNewStyle = static_cast<const SwFormatAutoFormat&>(rNewHint.GetAttr()).GetStyleHandle();
+ if ( pCurrentAutoStyle )
+ {
+ const std::shared_ptr<SfxItemSet> & pCurrentStyle = static_cast<const SwFormatAutoFormat&>(pCurrentAutoStyle->GetAttr()).GetStyleHandle();
+
+ // Merge attributes
+ SfxItemSet aNewSet( *pCurrentStyle );
+ aNewSet.Put( *pNewStyle );
+
+ // #i75750# Remove attributes already set at whole paragraph
+ // #i81764# This should not be applied for no length attributes!!! <--
+ if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && aNewSet.Count() )
+ {
+ SfxItemIter aIter2( aNewSet );
+ const SfxPoolItem* pItem = aIter2.GetCurItem();
+ const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
+
+ do
+ {
+ const SfxPoolItem* pTmpItem = nullptr;
+ if ( SfxItemState::SET == rWholeParaAttrSet.GetItemState( pItem->Which(), false, &pTmpItem ) &&
+ pTmpItem == pItem )
+ {
+ // Do not clear item if the attribute is set in a character format:
+ if ( !pCurrentCharFormat || nullptr == CharFormat::GetItem( *pCurrentCharFormat, pItem->Which() ) )
+ aIter2.ClearItem();
+ }
+ }
+ while ((pItem = aIter2.NextItem()));
+ }
+
+ // Remove old hint
+ Delete( pCurrentAutoStyle );
+ rNode.DestroyAttr( pCurrentAutoStyle );
+
+ // Create new AutoStyle
+ if ( aNewSet.Count() )
+ pNewAttr = MakeTextAttr( rNode.GetDoc(), aNewSet,
+ nPorStart, nPorEnd );
+ }
+ else
+ {
+ // Remove any attributes which are already set at the whole paragraph:
+ bool bOptimizeAllowed = true;
+
+ // #i75750# Remove attributes already set at whole paragraph
+ // #i81764# This should not be applied for no length attributes!!! <--
+ if ( !bNoLengthAttribute && rNode.HasSwAttrSet() && pNewStyle->Count() )
+ {
+ std::unique_ptr<SfxItemSet> pNewSet;
+
+ SfxItemIter aIter2( *pNewStyle );
+ const SfxPoolItem* pItem = aIter2.GetCurItem();
+ const SfxItemSet& rWholeParaAttrSet = rNode.GetSwAttrSet();
+
+ do
+ {
+ const SfxPoolItem* pTmpItem = nullptr;
+ if ( SfxItemState::SET == rWholeParaAttrSet.GetItemState( pItem->Which(), false, &pTmpItem ) &&
+ pTmpItem == pItem )
+ {
+ // Do not clear item if the attribute is set in a character format:
+ if ( !pCurrentCharFormat || nullptr == CharFormat::GetItem( *pCurrentCharFormat, pItem->Which() ) )
+ {
+ if ( !pNewSet )
+ pNewSet = pNewStyle->Clone();
+ pNewSet->ClearItem( pItem->Which() );
+ }
+ }
+ }
+ while ((pItem = aIter2.NextItem()));
+
+ if ( pNewSet )
+ {
+ bOptimizeAllowed = false;
+ if ( pNewSet->Count() )
+ pNewStyle = rNode.getIDocumentStyleAccess().getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR );
+ else
+ pNewStyle.reset();
+ }
+ }
+
+ // Create new AutoStyle
+ // If there is no current hint and start and end of rNewHint
+ // is ok, we do not need to create a new txtattr.
+ if ( bOptimizeAllowed &&
+ nPorStart == nThisStart &&
+ nPorEnd == nThisEnd )
+ {
+ pNewAttr = &rNewHint;
+ bDestroyHint = false;
+ }
+ else if ( pNewStyle )
+ {
+ pNewAttr = MakeTextAttr( rNode.GetDoc(), *pNewStyle,
+ nPorStart, nPorEnd );
+ }
+ }
+ }
+
+ if ( pNewAttr )
+ {
+ Insert( pNewAttr );
+// if ( bDestroyHint )
+ NoteInHistory( pNewAttr, true );
+ }
+
+ if ( !bNoLengthAttribute )
+ {
+ nPorStart = *aStartIter;
+ ++aStartIter;
+ }
+ else
+ break;
+ }
+
+ if ( bDestroyHint )
+ rNode.DestroyAttr( &rNewHint );
+}
+
+SwTextAttr* MakeRedlineTextAttr( SwDoc & rDoc, SfxPoolItem const & rAttr )
+{
+ // this is intended _only_ for special-purpose redline attributes!
+ switch (rAttr.Which())
+ {
+ case RES_CHRATR_COLOR:
+ case RES_CHRATR_WEIGHT:
+ case RES_CHRATR_CJK_WEIGHT:
+ case RES_CHRATR_CTL_WEIGHT:
+ case RES_CHRATR_POSTURE:
+ case RES_CHRATR_CJK_POSTURE:
+ case RES_CHRATR_CTL_POSTURE:
+ case RES_CHRATR_UNDERLINE:
+ case RES_CHRATR_CROSSEDOUT:
+ case RES_CHRATR_CASEMAP:
+ case RES_CHRATR_BACKGROUND:
+ break;
+ default:
+ assert(!"unsupported redline attribute");
+ break;
+ }
+
+ // Put new attribute into pool
+ // FIXME: this const_cast is evil!
+ SfxPoolItem& rNew =
+ const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
+ return new SwTextAttrEnd( rNew, 0, 0 );
+}
+
+// create new text attribute
+SwTextAttr* MakeTextAttr(
+ SwDoc & rDoc,
+ SfxPoolItem& rAttr,
+ sal_Int32 const nStt,
+ sal_Int32 const nEnd,
+ CopyOrNewType const bIsCopy,
+ SwTextNode *const pTextNode )
+{
+ if ( isCHRATR(rAttr.Which()) )
+ {
+ // Somebody wants to build a SwTextAttr for a character attribute.
+ // Sorry, this is not allowed any longer.
+ // You'll get a brand new autostyle attribute:
+ SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END> aItemSet( rDoc.GetAttrPool() );
+ aItemSet.Put( rAttr );
+ return MakeTextAttr( rDoc, aItemSet, nStt, nEnd );
+ }
+ else if ( RES_TXTATR_AUTOFMT == rAttr.Which() &&
+ static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle()->
+ GetPool() != &rDoc.GetAttrPool() )
+ {
+ // If the attribute is an auto-style which refers to a pool that is
+ // different from rDoc's pool, we have to correct this:
+ const std::shared_ptr<SfxItemSet> & pAutoStyle = static_cast<const SwFormatAutoFormat&>(rAttr).GetStyleHandle();
+ std::unique_ptr<const SfxItemSet> pNewSet(
+ pAutoStyle->SfxItemSet::Clone( true, &rDoc.GetAttrPool() ));
+ SwTextAttr* pNew = MakeTextAttr( rDoc, *pNewSet, nStt, nEnd );
+ return pNew;
+ }
+
+ // Put new attribute into pool
+ // FIXME: this const_cast is evil!
+ SfxPoolItem& rNew =
+ const_cast<SfxPoolItem&>( rDoc.GetAttrPool().Put( rAttr ) );
+
+ SwTextAttr* pNew = nullptr;
+ switch( rNew.Which() )
+ {
+ case RES_TXTATR_CHARFMT:
+ {
+ SwFormatCharFormat &rFormatCharFormat = static_cast<SwFormatCharFormat&>(rNew);
+ if( !rFormatCharFormat.GetCharFormat() )
+ {
+ rFormatCharFormat.SetCharFormat( rDoc.GetDfltCharFormat() );
+ }
+
+ pNew = new SwTextCharFormat( rFormatCharFormat, nStt, nEnd );
+ }
+ break;
+ case RES_TXTATR_INETFMT:
+ pNew = new SwTextINetFormat( static_cast<SwFormatINetFormat&>(rNew), nStt, nEnd );
+ break;
+
+ case RES_TXTATR_FIELD:
+ pNew = new SwTextField( static_cast<SwFormatField &>(rNew), nStt,
+ rDoc.IsClipBoard() );
+ break;
+
+ case RES_TXTATR_ANNOTATION:
+ {
+ pNew = new SwTextAnnotationField( static_cast<SwFormatField &>(rNew), nStt, rDoc.IsClipBoard() );
+ if (bIsCopy == CopyOrNewType::Copy)
+ {
+ // On copy of the annotation field do not keep the annotated text range by removing
+ // the relation to its annotation mark (relation established via annotation field's name).
+ // If the annotation mark is also copied, the relation and thus the annotated text range will be reestablished,
+ // when the annotation mark is created and inserted into the document.
+ auto& pField = const_cast<SwPostItField&>(dynamic_cast<const SwPostItField&>(*(pNew->GetFormatField().GetField())));
+ pField.SetName(OUString());
+ pField.SetPostItId();
+ }
+ }
+ break;
+
+ case RES_TXTATR_INPUTFIELD:
+ pNew = new SwTextInputField( static_cast<SwFormatField &>(rNew), nStt, nEnd,
+ rDoc.IsClipBoard() );
+ break;
+
+ case RES_TXTATR_FLYCNT:
+ {
+ // finally, copy the frame format (with content)
+ pNew = new SwTextFlyCnt( static_cast<SwFormatFlyCnt&>(rNew), nStt );
+ if ( static_cast<const SwFormatFlyCnt &>(rAttr).GetTextFlyCnt() )
+ {
+ // if it has an existing attr then the format must be copied
+ static_cast<SwTextFlyCnt *>(pNew)->CopyFlyFormat( rDoc );
+ }
+ }
+ break;
+ case RES_TXTATR_FTN:
+ pNew = new SwTextFootnote( static_cast<SwFormatFootnote&>(rNew), nStt );
+ // copy note's SeqNo
+ if( static_cast<SwFormatFootnote&>(rAttr).GetTextFootnote() )
+ static_cast<SwTextFootnote*>(pNew)->SetSeqNo( static_cast<SwFormatFootnote&>(rAttr).GetTextFootnote()->GetSeqRefNo() );
+ break;
+ case RES_TXTATR_REFMARK:
+ pNew = nStt == nEnd
+ ? new SwTextRefMark( static_cast<SwFormatRefMark&>(rNew), nStt )
+ : new SwTextRefMark( static_cast<SwFormatRefMark&>(rNew), nStt, &nEnd );
+ break;
+ case RES_TXTATR_TOXMARK:
+ {
+ SwTOXMark& rMark = static_cast<SwTOXMark&>(rNew);
+
+ // tdf#98868 if the SwTOXType is from a different document that the
+ // target, re-register the TOXMark against a matching SwTOXType from
+ // the target document instead
+ const SwTOXType* pTOXType = rMark.GetTOXType();
+ if (pTOXType && &pTOXType->GetDoc() != &rDoc)
+ {
+ SwTOXType* pToxType = SwHistorySetTOXMark::GetSwTOXType(rDoc, pTOXType->GetType(),
+ pTOXType->GetTypeName());
+ rMark.RegisterToTOXType(*pToxType);
+ }
+
+ pNew = new SwTextTOXMark(rMark, nStt, &nEnd);
+ break;
+ }
+ case RES_TXTATR_CJK_RUBY:
+ pNew = new SwTextRuby( static_cast<SwFormatRuby&>(rNew), nStt, nEnd );
+ break;
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ pNew = SwTextMeta::CreateTextMeta( rDoc.GetMetaFieldManager(), pTextNode,
+ static_cast<SwFormatMeta&>(rNew), nStt, nEnd, bIsCopy == CopyOrNewType::Copy );
+ break;
+ case RES_TXTATR_LINEBREAK:
+ pNew = new SwTextLineBreak(static_cast<SwFormatLineBreak&>(rNew), nStt);
+ break;
+ case RES_TXTATR_CONTENTCONTROL:
+ pNew = SwTextContentControl::CreateTextContentControl(
+ pTextNode, static_cast<SwFormatContentControl&>(rNew), nStt, nEnd,
+ bIsCopy == CopyOrNewType::Copy);
+ break;
+ default:
+ assert(RES_TXTATR_AUTOFMT == rNew.Which());
+ pNew = new SwTextAttrEnd( rNew, nStt, nEnd );
+ break;
+ }
+
+ return pNew;
+}
+
+SwTextAttr* MakeTextAttr( SwDoc & rDoc, const SfxItemSet& rSet,
+ sal_Int32 nStt, sal_Int32 nEnd )
+{
+ IStyleAccess& rStyleAccess = rDoc.GetIStyleAccess();
+ const std::shared_ptr<SfxItemSet> pAutoStyle = rStyleAccess.getAutomaticStyle( rSet, IStyleAccess::AUTO_STYLE_CHAR );
+ SwFormatAutoFormat aNewAutoFormat;
+ aNewAutoFormat.SetStyleHandle( pAutoStyle );
+ SwTextAttr* pNew = MakeTextAttr( rDoc, aNewAutoFormat, nStt, nEnd );
+ return pNew;
+}
+
+// delete the text attribute and unregister its item at the pool
+void SwTextNode::DestroyAttr( SwTextAttr* pAttr )
+{
+ if( !pAttr )
+ return;
+
+ // some things need to be done before deleting the formatting attribute
+ SwDoc& rDoc = GetDoc();
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_FLYCNT:
+ {
+ SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat();
+ if( pFormat ) // set to 0 by Undo?
+ rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+ }
+ break;
+
+ case RES_CHRATR_HIDDEN:
+ SetCalcHiddenCharFlags();
+ break;
+
+ case RES_TXTATR_FTN:
+ static_cast<SwTextFootnote*>(pAttr)->SetStartNode( nullptr );
+ static_cast<SwFormatFootnote&>(pAttr->GetAttr()).InvalidateFootnote();
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ if( !rDoc.IsInDtor() )
+ {
+ SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pAttr));
+ SwFieldType* pFieldType = pAttr->GetFormatField().GetField()->GetTyp();
+
+ if (SwFieldIds::Dde != pFieldType->Which()
+ && !pTextField->GetpTextNode())
+ {
+ break; // was not yet inserted
+ }
+
+ //JP 06-08-95: DDE-fields are an exception
+ assert(SwFieldIds::Dde == pFieldType->Which() ||
+ this == pTextField->GetpTextNode());
+
+ // certain fields must update the SwDoc's calculation flags
+
+ // Certain fields (like HiddenParaField) must trigger recalculation of visible flag
+ if (GetDoc().FieldCanHideParaWeight(pFieldType->Which()))
+ SetCalcHiddenParaField();
+
+ switch( pFieldType->Which() )
+ {
+ case SwFieldIds::HiddenPara:
+ case SwFieldIds::DbSetNumber:
+ case SwFieldIds::GetExp:
+ case SwFieldIds::Database:
+ case SwFieldIds::SetExp:
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ if( !rDoc.getIDocumentFieldsAccess().IsNewFieldLst() && GetNodes().IsDocNodes() )
+ rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField);
+ break;
+ case SwFieldIds::Dde:
+ if (GetNodes().IsDocNodes() && pTextField->GetpTextNode())
+ static_cast<SwDDEFieldType*>(pFieldType)->DecRefCnt();
+ break;
+ case SwFieldIds::Postit:
+ {
+ const_cast<SwFormatField&>(pAttr->GetFormatField()).Broadcast(
+ SwFormatFieldHint(&pTextField->GetFormatField(), SwFormatFieldHintWhich::REMOVED));
+ break;
+ }
+ default: break;
+ }
+ }
+ static_cast<SwFormatField&>(pAttr->GetAttr()).InvalidateField();
+ break;
+
+ case RES_TXTATR_TOXMARK:
+ static_cast<SwTOXMark&>(pAttr->GetAttr()).InvalidateTOXMark();
+ break;
+
+ case RES_TXTATR_REFMARK:
+ static_cast<SwFormatRefMark&>(pAttr->GetAttr()).InvalidateRefMark();
+ break;
+
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ {
+ auto pTextMeta = static_txtattr_cast<SwTextMeta*>(pAttr);
+ SwFormatMeta & rFormatMeta( static_cast<SwFormatMeta &>(pTextMeta->GetAttr()) );
+ if (::sw::Meta* pMeta = rFormatMeta.GetMeta())
+ {
+ if (SwDocShell* pDocSh = rDoc.GetDocShell())
+ {
+ static constexpr OUStringLiteral metaNS(u"urn:bails");
+ const css::uno::Reference<css::rdf::XResource> xSubject = pMeta->MakeUnoObject();
+ uno::Reference<frame::XModel> xModel = pDocSh->GetBaseModel();
+ SwRDFHelper::clearStatements(xModel, metaNS, xSubject);
+ }
+ }
+
+ static_txtattr_cast<SwTextMeta*>(pAttr)->ChgTextNode(nullptr);
+ }
+ break;
+ case RES_TXTATR_CONTENTCONTROL:
+ {
+ static_txtattr_cast<SwTextContentControl*>(pAttr)->ChgTextNode(nullptr);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ SwTextAttr::Destroy( pAttr, rDoc.GetAttrPool() );
+}
+
+SwTextAttr* SwTextNode::InsertItem(
+ SfxPoolItem& rAttr,
+ const sal_Int32 nStart,
+ const sal_Int32 nEnd,
+ const SetAttrMode nMode )
+{
+ // character attributes will be inserted as automatic styles:
+ assert( !isCHRATR(rAttr.Which()) && "AUTOSTYLES - "
+ "SwTextNode::InsertItem should not be called with character attributes");
+
+ SwTextAttr *const pNew =
+ MakeTextAttr(
+ GetDoc(),
+ rAttr,
+ nStart,
+ nEnd,
+ (nMode & SetAttrMode::IS_COPY) ? CopyOrNewType::Copy : CopyOrNewType::New,
+ this );
+
+ if ( pNew )
+ {
+ const bool bSuccess( InsertHint( pNew, nMode ) );
+ // N.B.: also check that the hint is actually in the hints array,
+ // because hints of certain types may be merged after successful
+ // insertion, and thus destroyed!
+ if (!bSuccess || !m_pSwpHints->Contains( pNew ))
+ {
+ return nullptr;
+ }
+ }
+
+ return pNew;
+}
+
+// take ownership of pAttr; if insertion fails, delete pAttr
+bool SwTextNode::InsertHint( SwTextAttr * const pAttr, const SetAttrMode nMode )
+{
+ bool bHiddenPara = false;
+
+ assert(pAttr && pAttr->GetStart() <= Len());
+ assert(!pAttr->GetEnd() || (*pAttr->GetEnd() <= Len()));
+
+ // translate from SetAttrMode to InsertMode (for hints with CH_TXTATR)
+ const SwInsertFlags nInsertFlags =
+ (nMode & SetAttrMode::NOHINTEXPAND)
+ ? SwInsertFlags::NOHINTEXPAND
+ : (nMode & SetAttrMode::FORCEHINTEXPAND)
+ ? (SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
+ : SwInsertFlags::EMPTYEXPAND;
+
+ // need this after TryInsertHint, when pAttr may be deleted
+ const sal_Int32 nStart( pAttr->GetStart() );
+ const bool bDummyChar( pAttr->HasDummyChar() );
+ if (bDummyChar)
+ {
+ SetAttrMode nInsMode = nMode;
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_FLYCNT:
+ {
+ SwTextFlyCnt *pFly = static_cast<SwTextFlyCnt *>(pAttr);
+ SwFrameFormat* pFormat = pAttr->GetFlyCnt().GetFrameFormat();
+ if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
+ {
+ // Need to insert char first, because SetAnchor() reads
+ // GetStart().
+ //JP 11.05.98: if the anchor is already set correctly,
+ // fix it after inserting the char, so that clients don't
+ // have to worry about it.
+ const SwFormatAnchor* pAnchor = pFormat->GetItemIfSet( RES_ANCHOR, false );
+
+ SwIndex aIdx( this, pAttr->GetStart() );
+ const OUString c(GetCharOfTextAttr(*pAttr));
+ OUString const ins( InsertText(c, aIdx, nInsertFlags) );
+ if (ins.isEmpty())
+ {
+ // do not record deletion of Format!
+ ::sw::UndoGuard const ug(
+ pFormat->GetDoc()->GetIDocumentUndoRedo());
+ DestroyAttr(pAttr);
+ return false; // text node full :(
+ }
+ nInsMode |= SetAttrMode::NOTXTATRCHR;
+
+ if (pAnchor &&
+ (RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) &&
+ pAnchor->GetContentAnchor() &&
+ pAnchor->GetContentAnchor()->nNode == *this &&
+ pAnchor->GetContentAnchor()->nContent == aIdx )
+ {
+ --const_cast<SwIndex&>(
+ pAnchor->GetContentAnchor()->nContent);
+ }
+ }
+ pFly->SetAnchor( this );
+
+ // format pointer could have changed in SetAnchor,
+ // when copying to other docs!
+ pFormat = pAttr->GetFlyCnt().GetFrameFormat();
+ SwDoc *pDoc = pFormat->GetDoc();
+
+ // OD 26.06.2003 - allow drawing objects in header/footer.
+ // But don't allow control objects in header/footer
+ if( RES_DRAWFRMFMT == pFormat->Which() &&
+ pDoc->IsInHeaderFooter( pFormat->GetAnchor().GetContentAnchor()->nNode ) )
+ {
+ bool bCheckControlLayer = false;
+ pFormat->CallSwClientNotify(sw::CheckDrawFrameFormatLayerHint(&bCheckControlLayer));
+ if( bCheckControlLayer )
+ {
+ // This should not be allowed, prevent it here.
+ // The dtor of the SwTextAttr does not delete the
+ // char, so delete it explicitly here.
+ if( SetAttrMode::NOTXTATRCHR & nInsMode )
+ {
+ // delete the char from the string
+ assert(CH_TXTATR_BREAKWORD == m_Text[pAttr->GetStart()]
+ || CH_TXTATR_INWORD == m_Text[pAttr->GetStart()]);
+ m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, u"");
+ // Update SwIndexes
+ SwIndex aTmpIdx( this, pAttr->GetStart() );
+ Update( aTmpIdx, 1, true );
+ }
+ // do not record deletion of Format!
+ ::sw::UndoGuard const ug(pDoc->GetIDocumentUndoRedo());
+ DestroyAttr( pAttr );
+ return false;
+ }
+ }
+ break;
+ }
+
+ case RES_TXTATR_FTN :
+ {
+ // Footnotes: create text node and put it into Inserts-section
+ SwDoc& rDoc = GetDoc();
+ SwNodes &rNodes = rDoc.GetNodes();
+
+ // check that footnote is inserted into body or redline section
+ if( StartOfSectionIndex() < rNodes.GetEndOfAutotext().GetIndex() )
+ {
+ // This should not be allowed, prevent it here.
+ // The dtor of the SwTextAttr does not delete the
+ // char, so delete it explicitly here.
+ if( SetAttrMode::NOTXTATRCHR & nInsMode )
+ {
+ // delete the char from the string
+ assert(CH_TXTATR_BREAKWORD == m_Text[pAttr->GetStart()]
+ || CH_TXTATR_INWORD == m_Text[pAttr->GetStart()]);
+ m_Text = m_Text.replaceAt(pAttr->GetStart(), 1, u"");
+ // Update SwIndexes
+ SwIndex aTmpIdx( this, pAttr->GetStart() );
+ Update( aTmpIdx, 1, true );
+ }
+ DestroyAttr( pAttr );
+ return false;
+ }
+
+ // is a new footnote being inserted?
+ bool bNewFootnote = nullptr == static_cast<SwTextFootnote*>(pAttr)->GetStartNode();
+ if( bNewFootnote )
+ {
+ static_cast<SwTextFootnote*>(pAttr)->MakeNewTextSection( GetNodes() );
+ SwRegHistory* pHist = GetpSwpHints()
+ ? GetpSwpHints()->GetHistory() : nullptr;
+ if( pHist )
+ pHist->ChangeNodeIndex( GetIndex() );
+ }
+ else if ( !GetpSwpHints() || !GetpSwpHints()->IsInSplitNode() )
+ {
+ // existing footnote: delete all layout frames of its
+ // footnote section
+ SwNodeOffset nSttIdx =
+ static_cast<SwTextFootnote*>(pAttr)->GetStartNode()->GetIndex();
+ SwNodeOffset nEndIdx = rNodes[ nSttIdx++ ]->EndOfSectionIndex();
+ for( ; nSttIdx < nEndIdx; ++nSttIdx )
+ {
+ SwContentNode* pCNd = rNodes[ nSttIdx ]->GetContentNode();
+ if( nullptr != pCNd )
+ pCNd->DelFrames(nullptr);
+ else if (SwTableNode *const pTable = rNodes[nSttIdx]->GetTableNode())
+ {
+ pTable->DelFrames();
+ }
+ }
+ }
+
+ if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
+ {
+ // must insert first, to prevent identical indexes
+ // that could later prevent insertion into SwDoc's
+ // footnote array
+ SwIndex aNdIdx( this, pAttr->GetStart() );
+ const OUString c(GetCharOfTextAttr(*pAttr));
+ OUString const ins( InsertText(c, aNdIdx, nInsertFlags) );
+ if (ins.isEmpty())
+ {
+ DestroyAttr(pAttr);
+ return false; // text node full :(
+ }
+ nInsMode |= SetAttrMode::NOTXTATRCHR;
+ }
+
+ // insert into SwDoc's footnote index array
+ SwTextFootnote* pTextFootnote = nullptr;
+ if( !bNewFootnote )
+ {
+ // moving an existing footnote (e.g. SplitNode)
+ for( size_t n = 0; n < rDoc.GetFootnoteIdxs().size(); ++n )
+ if( pAttr == rDoc.GetFootnoteIdxs()[n] )
+ {
+ // assign new index by removing and re-inserting
+ pTextFootnote = rDoc.GetFootnoteIdxs()[n];
+ rDoc.GetFootnoteIdxs().erase( rDoc.GetFootnoteIdxs().begin() + n );
+ break;
+ }
+ // if the Undo set the StartNode, the Index isn't
+ // in the doc's array yet!
+ }
+ if( !pTextFootnote )
+ pTextFootnote = static_cast<SwTextFootnote*>(pAttr);
+
+ // to update the numbers and for sorting, the Node must be set
+ static_cast<SwTextFootnote*>(pAttr)->ChgTextNode( this );
+
+ // do not insert footnote in redline section into footnote array
+ if( StartOfSectionIndex() > rNodes.GetEndOfRedlines().GetIndex() )
+ {
+ const bool bSuccess = rDoc.GetFootnoteIdxs().insert(pTextFootnote).second;
+ OSL_ENSURE( bSuccess, "FootnoteIdx not inserted." );
+ }
+ SwNodeIndex aTmpIndex( *this );
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aTmpIndex);
+ static_cast<SwTextFootnote*>(pAttr)->SetSeqRefNo();
+ }
+ break;
+
+ case RES_TXTATR_FIELD:
+ {
+ // trigger notification for relevant fields, like HiddenParaFields
+ if (GetDoc().FieldCanHideParaWeight(
+ pAttr->GetFormatField().GetField()->GetTyp()->Which()))
+ {
+ bHiddenPara = true;
+ }
+ }
+ break;
+ case RES_TXTATR_LINEBREAK :
+ {
+ static_cast<SwTextLineBreak*>(pAttr)->SetTextNode(this);
+ }
+ break;
+
+ }
+ // CH_TXTATR_* are inserted for SwTextHints without EndIndex
+ // If the caller is SwTextNode::Copy, the char has already been copied,
+ // and SETATTR_NOTXTATRCHR prevents inserting it again here.
+ if( !(SetAttrMode::NOTXTATRCHR & nInsMode) )
+ {
+ SwIndex aIdx( this, pAttr->GetStart() );
+ OUString const ins( InsertText(OUString(GetCharOfTextAttr(*pAttr)),
+ aIdx, nInsertFlags) );
+ if (ins.isEmpty())
+ {
+ DestroyAttr(pAttr);
+ return false; // text node full :(
+ }
+
+ // adjust end of hint to account for inserted CH_TXTATR
+ const sal_Int32* pEnd(pAttr->GetEnd());
+ if (pEnd)
+ {
+ pAttr->SetEnd(*pEnd + 1);
+ }
+
+ if (pAttr->Which() == RES_TXTATR_CONTENTCONTROL)
+ {
+ // Content controls have a dummy character at their end as well.
+ SwIndex aEndIdx(this, *pAttr->GetEnd());
+ OUString aEnd
+ = InsertText(OUString(GetCharOfTextAttr(*pAttr)), aEndIdx, nInsertFlags);
+ if (aEnd.isEmpty())
+ {
+ DestroyAttr(pAttr);
+ return false;
+ }
+
+ pEnd = pAttr->GetEnd();
+ if (pEnd)
+ {
+ pAttr->SetEnd(*pEnd + 1);
+ }
+ }
+ }
+ }
+
+ // handle attributes which provide content
+ sal_Int32 nEnd = nStart;
+ bool bInputFieldStartCharInserted = false;
+ bool bInputFieldEndCharInserted = false;
+ const bool bHasContent( pAttr->HasContent() );
+ if ( bHasContent )
+ {
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_INPUTFIELD:
+ {
+ SwTextInputField* pTextInputField = dynamic_cast<SwTextInputField*>(pAttr);
+ if ( pTextInputField )
+ {
+ if( !(SetAttrMode::NOTXTATRCHR & nMode) )
+ {
+ SwIndex aIdx( this, pAttr->GetStart() );
+ const OUString aContent = OUStringChar(CH_TXT_ATR_INPUTFIELDSTART)
+ + pTextInputField->GetFieldContent() + OUStringChar(CH_TXT_ATR_INPUTFIELDEND);
+ InsertText( aContent, aIdx, nInsertFlags );
+
+ const sal_Int32* const pEnd(pAttr->GetEnd());
+ assert(pEnd != nullptr);
+ pAttr->SetEnd(*pEnd + aContent.getLength());
+ nEnd = *pAttr->GetEnd();
+ }
+ else
+ {
+ // assure that CH_TXT_ATR_INPUTFIELDSTART and CH_TXT_ATR_INPUTFIELDEND are inserted.
+ if ( m_Text[ pAttr->GetStart() ] != CH_TXT_ATR_INPUTFIELDSTART )
+ {
+ SwIndex aIdx( this, pAttr->GetStart() );
+ InsertText( OUString(CH_TXT_ATR_INPUTFIELDSTART), aIdx, nInsertFlags );
+ bInputFieldStartCharInserted = true;
+ const sal_Int32* const pEnd(pAttr->GetEnd());
+ assert(pEnd != nullptr);
+ pAttr->SetEnd(*pEnd + 1);
+ nEnd = *pAttr->GetEnd();
+ }
+
+ const sal_Int32* const pEnd(pAttr->GetEnd());
+ assert(pEnd != nullptr);
+ if (m_Text[ *pEnd - 1 ] != CH_TXT_ATR_INPUTFIELDEND)
+ {
+ SwIndex aIdx( this, *pEnd );
+ InsertText( OUString(CH_TXT_ATR_INPUTFIELDEND), aIdx, nInsertFlags );
+ bInputFieldEndCharInserted = true;
+ pAttr->SetEnd(*pEnd + 1);
+ nEnd = *pAttr->GetEnd();
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ GetOrCreateSwpHints();
+
+ // handle overlap with an existing InputField
+ bool bInsertHint = true;
+ {
+ const SwTextInputField* pTextInputField = GetOverlappingInputField( *pAttr );
+ if ( pTextInputField != nullptr )
+ {
+ if ( pAttr->End() == nullptr )
+ {
+ bInsertHint = false;
+ DestroyAttr(pAttr);
+ }
+ else
+ {
+ if ( pAttr->GetStart() > pTextInputField->GetStart() )
+ {
+ pAttr->SetStart( pTextInputField->GetStart() );
+ }
+ if ( *(pAttr->End()) < *(pTextInputField->End()) )
+ {
+ pAttr->SetEnd(*(pTextInputField->End()));
+ }
+ }
+ }
+ }
+
+ const bool bRet = bInsertHint
+ && m_pSwpHints->TryInsertHint( pAttr, *this, nMode );
+
+ if ( !bRet )
+ {
+ if ( bDummyChar
+ && !(SetAttrMode::NOTXTATRCHR & nMode) )
+ {
+ // undo insertion of dummy character
+ // N.B. cannot insert the dummy character after inserting the hint,
+ // because if the hint has no extent it will be moved in InsertText,
+ // resulting in infinite recursion
+ assert((CH_TXTATR_BREAKWORD == m_Text[nStart] ||
+ CH_TXTATR_INWORD == m_Text[nStart] ));
+ SwIndex aIdx( this, nStart );
+ EraseText( aIdx, 1 );
+ }
+
+ if ( bHasContent )
+ {
+ if ( !(SetAttrMode::NOTXTATRCHR & nMode)
+ && (nEnd - nStart) > 0 )
+ {
+ SwIndex aIdx( this, nStart );
+ EraseText( aIdx, (nEnd - nStart) );
+ }
+ else
+ {
+ if ( bInputFieldEndCharInserted
+ && (nEnd - nStart) > 0 )
+ {
+ SwIndex aIdx( this, nEnd - 1 );
+ EraseText( aIdx, 1 );
+ }
+
+ if ( bInputFieldStartCharInserted )
+ {
+ SwIndex aIdx( this, nStart );
+ EraseText( aIdx, 1 );
+ }
+ }
+ }
+ }
+
+ if ( bHiddenPara )
+ {
+ SetCalcHiddenParaField();
+ }
+
+ return bRet;
+}
+
+void SwTextNode::DeleteAttribute( SwTextAttr * const pAttr )
+{
+ if ( !HasHints() )
+ {
+ OSL_FAIL("DeleteAttribute called, but text node without hints?");
+ return;
+ }
+
+ if ( pAttr->HasDummyChar() )
+ {
+ // copy index!
+ const SwIndex aIdx( this, pAttr->GetStart() );
+ // erase the CH_TXTATR, which will also delete pAttr
+ EraseText( aIdx, 1 );
+ }
+ else if ( pAttr->HasContent() )
+ {
+ const SwIndex aIdx( this, pAttr->GetStart() );
+ assert(pAttr->End() != nullptr);
+ EraseText( aIdx, *pAttr->End() - pAttr->GetStart() );
+ }
+ else
+ {
+ // create MsgHint before start/end become invalid
+ SwUpdateAttr aHint(
+ pAttr->GetStart(),
+ *pAttr->GetEnd(),
+ pAttr->Which());
+
+ m_pSwpHints->Delete( pAttr );
+ SwTextAttr::Destroy( pAttr, GetDoc().GetAttrPool() );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+
+ TryDeleteSwpHints();
+ }
+}
+
+//FIXME: this does NOT respect SORT NUMBER (for CHARFMT)!
+void SwTextNode::DeleteAttributes(
+ const sal_uInt16 nWhich,
+ const sal_Int32 nStart,
+ const sal_Int32 nEnd )
+{
+ if ( !HasHints() )
+ return;
+
+ for ( size_t nPos = 0; m_pSwpHints && nPos < m_pSwpHints->Count(); ++nPos )
+ {
+ SwTextAttr * const pTextHt = m_pSwpHints->Get( nPos );
+ const sal_Int32 nHintStart = pTextHt->GetStart();
+ if (nStart < nHintStart)
+ {
+ break; // sorted by start
+ }
+ else if ( (nStart == nHintStart) && (nWhich == pTextHt->Which()) )
+ {
+ if ( nWhich == RES_CHRATR_HIDDEN )
+ {
+ assert(!"hey, that's a CHRATR! how did that get in?");
+ SetCalcHiddenCharFlags();
+ }
+ else if ( nWhich == RES_TXTATR_CHARFMT )
+ {
+ // Check if character format contains hidden attribute:
+ const SwCharFormat* pFormat = pTextHt->GetCharFormat().GetCharFormat();
+ if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN ) )
+ SetCalcHiddenCharFlags();
+ }
+ // #i75430# Recalc hidden flags if necessary
+ else if ( nWhich == RES_TXTATR_AUTOFMT )
+ {
+ // Check if auto style contains hidden attribute:
+ const SfxPoolItem* pHiddenItem = CharFormat::GetItem( *pTextHt, RES_CHRATR_HIDDEN );
+ if ( pHiddenItem )
+ SetCalcHiddenCharFlags();
+ // for auto styles DeleteAttributes is only called from Undo
+ // so it shouldn't need to care about ignore start/end flags
+ }
+
+ sal_Int32 const * const pEndIdx = pTextHt->GetEnd();
+
+ if ( pTextHt->HasDummyChar() )
+ {
+ // copy index!
+ const SwIndex aIdx( this, nStart );
+ // erase the CH_TXTATR, which will also delete pTextHt
+ EraseText( aIdx, 1 );
+ }
+ else if ( pTextHt->HasContent() )
+ {
+ const SwIndex aIdx( this, nStart );
+ OSL_ENSURE( pTextHt->End() != nullptr, "<SwTextNode::DeleteAttributes(..)> - missing End() at <SwTextAttr> instance which has content" );
+ EraseText( aIdx, *pTextHt->End() - nStart );
+ }
+ else if( *pEndIdx == nEnd )
+ {
+ // Create MsgHint before Start and End are gone.
+ // For HiddenParaFields it's not necessary to call
+ // SetCalcHiddenParaField because the dtor does that.
+ SwUpdateAttr aHint(
+ nStart,
+ *pEndIdx,
+ nWhich);
+
+ m_pSwpHints->DeleteAtPos( nPos );
+ SwTextAttr::Destroy( pTextHt, GetDoc().GetAttrPool() );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ }
+ }
+ }
+ TryDeleteSwpHints();
+}
+
+void SwTextNode::DelSoftHyph( const sal_Int32 nStt, const sal_Int32 nEnd )
+{
+ sal_Int32 nFndPos = nStt;
+ sal_Int32 nEndPos = nEnd;
+ for (;;)
+ {
+ nFndPos = m_Text.indexOf(CHAR_SOFTHYPHEN, nFndPos);
+ if (nFndPos<0 || nFndPos>=nEndPos )
+ {
+ break;
+ }
+ const SwIndex aIdx( this, nFndPos );
+ EraseText( aIdx, 1 );
+ --nEndPos;
+ }
+}
+
+bool SwTextNode::IsIgnoredCharFormatForNumbering(const sal_uInt16 nWhich, bool bIsCharStyle)
+{
+ // LO can save the char background as either shading or highlight, so check which mode is currently chosen.
+ // Shading does not affect the numbering. Highlighting does (but isn't allowed in a char style).
+ if (nWhich == RES_CHRATR_BACKGROUND)
+ return bIsCharStyle || SvtFilterOptions::Get().IsCharBackground2Shading();
+
+ return (nWhich == RES_CHRATR_UNDERLINE
+ || nWhich == RES_CHRATR_ESCAPEMENT);
+}
+
+// Set these attributes on SwTextNode. If they apply to the entire paragraph
+// text, set them in the SwTextNode's item set (SwContentNode::SetAttr).
+bool SwTextNode::SetAttr(
+ const SfxItemSet& rSet,
+ const sal_Int32 nStt,
+ const sal_Int32 nEnd,
+ const SetAttrMode nMode,
+ SwTextAttr **ppNewTextAttr )
+{
+ if( !rSet.Count() )
+ return false;
+
+ // split sets (for selection in nodes)
+ const SfxItemSet* pSet = &rSet;
+ SfxItemSetFixed<RES_TXTATR_BEGIN, RES_TXTATR_END-1> aTextSet( *rSet.GetPool() );
+
+ // entire paragraph
+ if ( !nStt && (nEnd == m_Text.getLength()) &&
+ !(nMode & SetAttrMode::NOFORMATATTR ) )
+ {
+ // if the node already has CharFormat hints, the new attributes must
+ // be set as hints too to override those.
+ bool bHasCharFormats = false;
+ if ( HasHints() )
+ {
+ for ( size_t n = 0; n < m_pSwpHints->Count(); ++n )
+ {
+ if ( m_pSwpHints->Get( n )->IsCharFormatAttr() )
+ {
+ bHasCharFormats = true;
+ break;
+ }
+ }
+ }
+
+ if( !bHasCharFormats )
+ {
+ aTextSet.Put( rSet );
+ // If there are any character attributes in rSet,
+ // we want to set them at the paragraph:
+ if( aTextSet.Count() != rSet.Count() )
+ {
+ const bool bRet = SetAttr( rSet );
+ if( !aTextSet.Count() )
+ return bRet;
+ }
+
+ // check for auto style:
+ if ( const SwFormatAutoFormat* pItem = aTextSet.GetItemIfSet( RES_TXTATR_AUTOFMT, false ) )
+ {
+ const bool bRet = SetAttr( *pItem->GetStyleHandle() );
+ if( 1 == aTextSet.Count() )
+ return bRet;
+ }
+
+ // Continue with the text attributes:
+ pSet = &aTextSet;
+ }
+ }
+
+ GetOrCreateSwpHints();
+
+ SfxItemSet aCharSet( *rSet.GetPool(), aCharAutoFormatSetRange );
+
+ size_t nCount = 0;
+ SfxItemIter aIter( *pSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+
+ do
+ {
+ if (!IsInvalidItem(pItem))
+ {
+ const sal_uInt16 nWhich = pItem->Which();
+ OSL_ENSURE( isCHRATR(nWhich) || isTXTATR(nWhich),
+ "SwTextNode::SetAttr(): unknown attribute" );
+ if ( isCHRATR(nWhich) || isTXTATR(nWhich) )
+ {
+ if ((RES_TXTATR_CHARFMT == nWhich) &&
+ (GetDoc().GetDfltCharFormat() ==
+ static_cast<const SwFormatCharFormat*>(pItem)->GetCharFormat()))
+ {
+ SwIndex aIndex( this, nStt );
+ RstTextAttr( aIndex, nEnd - nStt, RES_TXTATR_CHARFMT );
+ DontExpandFormat( aIndex );
+ }
+ else
+ {
+ if (isCHRATR(nWhich) ||
+ (RES_TXTATR_UNKNOWN_CONTAINER == nWhich))
+ {
+ aCharSet.Put( *pItem );
+ }
+ else
+ {
+
+ SwTextAttr *const pNew = MakeTextAttr( GetDoc(),
+ const_cast<SfxPoolItem&>(*pItem), nStt, nEnd );
+ if ( pNew )
+ {
+ // store the first one we create into the pp
+ if (ppNewTextAttr && !*ppNewTextAttr)
+ *ppNewTextAttr = pNew;
+ if ( nEnd != nStt && !pNew->GetEnd() )
+ {
+ OSL_FAIL("Attribute without end, but area marked");
+ DestroyAttr( pNew ); // do not insert
+ }
+ else if ( InsertHint( pNew, nMode ) )
+ {
+ ++nCount;
+ }
+ }
+ }
+ }
+ }
+ }
+ pItem = aIter.NextItem();
+ } while(pItem);
+
+ if ( aCharSet.Count() )
+ {
+ SwTextAttr* pTmpNew = MakeTextAttr( GetDoc(), aCharSet, nStt, nEnd );
+ if ( InsertHint( pTmpNew, nMode ) )
+ {
+ ++nCount;
+ }
+ }
+
+ TryDeleteSwpHints();
+
+ return nCount != 0;
+}
+
+static void lcl_MergeAttr( SfxItemSet& rSet, const SfxPoolItem& rAttr )
+{
+ if ( RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pCFSet = CharFormat::GetItemSet( rAttr );
+ if ( !pCFSet )
+ return;
+ SfxWhichIter aIter( *pCFSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( ( nWhich < RES_CHRATR_END ||
+ RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) &&
+ ( SfxItemState::SET == aIter.GetItemState( true, &pItem ) ) )
+ rSet.Put( *pItem );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ else
+ rSet.Put( rAttr );
+}
+
+static void lcl_MergeAttr_ExpandChrFormat( SfxItemSet& rSet, const SfxPoolItem& rAttr )
+{
+ if( RES_TXTATR_CHARFMT == rAttr.Which() ||
+ RES_TXTATR_INETFMT == rAttr.Which() ||
+ RES_TXTATR_AUTOFMT == rAttr.Which() )
+ {
+ const SfxItemSet* pCFSet = CharFormat::GetItemSet( rAttr );
+
+ if ( pCFSet )
+ {
+ SfxWhichIter aIter( *pCFSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while( nWhich )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( ( nWhich < RES_CHRATR_END ||
+ ( RES_TXTATR_AUTOFMT == rAttr.Which() && RES_TXTATR_UNKNOWN_CONTAINER == nWhich ) ) &&
+ ( SfxItemState::SET == aIter.GetItemState( true, &pItem ) ) )
+ rSet.Put( *pItem );
+ nWhich = aIter.NextWhich();
+ }
+ }
+ }
+
+/* If multiple attributes overlap, the last one wins!
+ Probably this can only happen between a RES_TXTATR_INETFMT and one of the
+ other hints, because BuildPortions ensures that CHARFMT/AUTOFMT don't
+ overlap. But there may be multiple CHARFMT/AUTOFMT with exactly the same
+ start/end, sorted by BuildPortions, in which case the same logic applies.
+
+ 1234567890123456789
+ |------------| Font1
+ |------| Font2
+ ^ ^
+ |--| query range: -> Font2
+*/
+ // merge into set
+ rSet.Put( rAttr );
+}
+
+namespace {
+
+struct SwPoolItemEndPair
+{
+public:
+ const SfxPoolItem* mpItem;
+ sal_Int32 mnEndPos;
+
+ SwPoolItemEndPair() : mpItem( nullptr ), mnEndPos( 0 ) {};
+};
+
+}
+
+static void lcl_MergeListLevelIndentAsLRSpaceItem( const SwTextNode& rTextNode,
+ SfxItemSet& rSet )
+{
+ if ( !rTextNode.AreListLevelIndentsApplicable() )
+ return;
+
+ const SwNumRule* pRule = rTextNode.GetNumRule();
+ if ( pRule && rTextNode.GetActualListLevel() >= 0 )
+ {
+ const SwNumFormat& rFormat = pRule->Get(o3tl::narrowing<sal_uInt16>(rTextNode.GetActualListLevel()));
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ SvxLRSpaceItem aLR( RES_LR_SPACE );
+ aLR.SetTextLeft( rFormat.GetIndentAt() );
+ aLR.SetTextFirstLineOffset( static_cast<short>(rFormat.GetFirstLineIndent()) );
+ rSet.Put( aLR );
+ }
+ }
+}
+
+// request the attributes of the TextNode at the range
+bool SwTextNode::GetParaAttr(SfxItemSet& rSet, sal_Int32 nStt, sal_Int32 nEnd,
+ const bool bOnlyTextAttr, const bool bGetFromChrFormat,
+ const bool bMergeIndentValuesOfNumRule,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!rSet.Count()); // handled inconsistently, typically an error?
+
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (GetRedlineMergeFlag() == SwNode::Merge::Hidden)
+ {
+ return false; // ignore deleted node
+ }
+ }
+
+ // get the node's automatic attributes
+ SfxItemSet aFormatSet( *rSet.GetPool(), rSet.GetRanges() );
+ if (!bOnlyTextAttr)
+ {
+ SwTextNode const& rParaPropsNode(
+ sw::GetAttrMerged(aFormatSet, *this, pLayout));
+ if (bMergeIndentValuesOfNumRule)
+ {
+ lcl_MergeListLevelIndentAsLRSpaceItem(rParaPropsNode, aFormatSet);
+ }
+ }
+
+ if( HasHints() )
+ {
+ // First, check which text attributes are valid in the range.
+ // cases:
+ // Ambiguous, if
+ // * the attribute is wholly contained in the range
+ // * the attribute end is in the range
+ // * the attribute start is in the range
+ // Unambiguous (merge into set), if
+ // * the attribute wholly contains the range
+ // Ignored, if
+ // * the attribute is wholly outside the range
+
+ void (*fnMergeAttr)( SfxItemSet&, const SfxPoolItem& )
+ = bGetFromChrFormat ? &lcl_MergeAttr_ExpandChrFormat
+ : &lcl_MergeAttr;
+
+ const size_t nSize = m_pSwpHints->Count();
+
+ if (nStt == nEnd) // no range:
+ {
+ for (size_t n = 0; n < nSize; ++n)
+ {
+ const SwTextAttr* pHt = m_pSwpHints->Get(n);
+ const sal_Int32 nAttrStart = pHt->GetStart();
+ if (nAttrStart > nEnd) // behind the range
+ break;
+
+ const sal_Int32* pAttrEnd = pHt->End();
+ if ( ! pAttrEnd ) // no attributes without end
+ continue;
+
+ if( ( nAttrStart < nStt &&
+ ( pHt->DontExpand() ? nStt < *pAttrEnd
+ : nStt <= *pAttrEnd )) ||
+ ( nStt == nAttrStart &&
+ ( nAttrStart == *pAttrEnd || !nStt )))
+ (*fnMergeAttr)( rSet, pHt->GetAttr() );
+ }
+ }
+ else // a query range is defined
+ {
+ // #i75299#
+ std::optional< std::vector< SwPoolItemEndPair > > pAttrArr;
+
+ const size_t coArrSz = RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN;
+
+ for (size_t n = 0; n < nSize; ++n)
+ {
+ const SwTextAttr* pHt = m_pSwpHints->Get(n);
+ const sal_Int32 nAttrStart = pHt->GetStart();
+ if (nAttrStart > nEnd) // outside, behind
+ break;
+
+ const sal_Int32* pAttrEnd = pHt->End();
+ if ( ! pAttrEnd ) // no attributes without end
+ continue;
+
+ bool bChkInvalid = false;
+ if (nAttrStart <= nStt) // before or exactly Start
+ {
+ if (*pAttrEnd <= nStt) // outside, before
+ continue;
+
+ if (nEnd <= *pAttrEnd) // behind or exactly End
+ (*fnMergeAttr)( aFormatSet, pHt->GetAttr() );
+ else
+// else if( pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
+ // ambiguous
+ bChkInvalid = true;
+ }
+ else if (nAttrStart < nEnd // starts in the range
+)// && pHt->GetAttr() != aFormatSet.Get( pHt->Which() ) )
+ bChkInvalid = true;
+
+ if( bChkInvalid )
+ {
+ // ambiguous?
+ std::optional< SfxItemIter > oItemIter;
+ const SfxPoolItem* pItem = nullptr;
+
+ if ( RES_TXTATR_AUTOFMT == pHt->Which() )
+ {
+ const SfxItemSet* pAutoSet = CharFormat::GetItemSet( pHt->GetAttr() );
+ if ( pAutoSet )
+ {
+ oItemIter.emplace( *pAutoSet );
+ pItem = oItemIter->GetCurItem();
+ }
+ }
+ else
+ pItem = &pHt->GetAttr();
+
+ const sal_Int32 nHintEnd = *pAttrEnd;
+
+ for (; pItem; pItem = oItemIter ? oItemIter->NextItem() : nullptr)
+ {
+ const sal_uInt16 nHintWhich = pItem->Which();
+ OSL_ENSURE(!isUNKNOWNATR(nHintWhich),
+ "SwTextNode::GetAttr(): unknown attribute?");
+
+ if (!pAttrArr)
+ {
+ pAttrArr = std::vector< SwPoolItemEndPair >(coArrSz);
+ }
+
+ std::vector< SwPoolItemEndPair >::iterator pPrev = pAttrArr->begin();
+ if (isCHRATR(nHintWhich) ||
+ isTXTATR_WITHEND(nHintWhich))
+ {
+ pPrev += nHintWhich - RES_CHRATR_BEGIN;
+ }
+ else
+ {
+ pPrev = pAttrArr->end();
+ }
+
+ if( pPrev != pAttrArr->end() )
+ {
+ if( !pPrev->mpItem )
+ {
+ if ( bOnlyTextAttr || *pItem != aFormatSet.Get( nHintWhich ) )
+ {
+ if( nAttrStart > nStt )
+ {
+ rSet.InvalidateItem( nHintWhich );
+ pPrev->mpItem = INVALID_POOL_ITEM;
+ }
+ else
+ {
+ pPrev->mpItem = pItem;
+ pPrev->mnEndPos = nHintEnd;
+ }
+ }
+ }
+ else if( !IsInvalidItem(pPrev->mpItem) )
+ {
+ if( pPrev->mnEndPos == nAttrStart &&
+ *pPrev->mpItem == *pItem )
+ {
+ pPrev->mpItem = pItem;
+ pPrev->mnEndPos = nHintEnd;
+ }
+ else
+ {
+ rSet.InvalidateItem( nHintWhich );
+ pPrev->mpItem = INVALID_POOL_ITEM;
+ }
+ }
+ }
+ } // end while
+ }
+ }
+
+ if (pAttrArr)
+ {
+ for (size_t n = 0; n < coArrSz; ++n)
+ {
+ const SwPoolItemEndPair& rItemPair = (*pAttrArr)[ n ];
+ if( rItemPair.mpItem && !IsInvalidItem(rItemPair.mpItem) )
+ {
+ const sal_uInt16 nWh =
+ o3tl::narrowing<sal_uInt16>(n + RES_CHRATR_BEGIN);
+
+ if (nEnd <= rItemPair.mnEndPos) // behind or exactly end
+ {
+ if( *rItemPair.mpItem != aFormatSet.Get( nWh ) )
+ (*fnMergeAttr)( rSet, *rItemPair.mpItem );
+ }
+ else
+ // ambiguous
+ rSet.InvalidateItem( nWh );
+ }
+ }
+ }
+ }
+ if( aFormatSet.Count() )
+ {
+ // remove all from the format-set that are also set in the text-set
+ aFormatSet.Differentiate( rSet );
+ }
+ }
+
+ if (aFormatSet.Count())
+ {
+ // now "merge" everything
+ rSet.Put( aFormatSet );
+ }
+
+ return rSet.Count() != 0;
+}
+
+namespace
+{
+
+typedef std::pair<sal_Int32, sal_Int32> AttrSpan_t;
+typedef std::multimap<AttrSpan_t, const SwTextAttr*> AttrSpanMap_t;
+
+struct IsAutoStyle
+{
+ bool
+ operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
+ const
+ {
+ return i_rAttrSpan.second && i_rAttrSpan.second->Which() == RES_TXTATR_AUTOFMT;
+ }
+};
+
+/** Removes from io_rAttrSet all items that are set by style on the
+ given span.
+ */
+struct RemovePresentAttrs
+{
+ explicit RemovePresentAttrs(SfxItemSet& io_rAttrSet)
+ : m_rAttrSet(io_rAttrSet)
+ {
+ }
+
+ void
+ operator()(const AttrSpanMap_t::value_type& i_rAttrSpan)
+ const
+ {
+ if (!i_rAttrSpan.second)
+ {
+ return;
+ }
+
+ const SwTextAttr* const pAutoStyle(i_rAttrSpan.second);
+ SfxItemIter aIter(m_rAttrSet);
+ for (const SfxPoolItem* pItem(aIter.GetCurItem()); pItem; pItem = aIter.NextItem())
+ {
+ const sal_uInt16 nWhich(pItem->Which());
+ if (CharFormat::IsItemIncluded(nWhich, pAutoStyle))
+ {
+ aIter.ClearItem();
+ }
+ }
+ }
+
+private:
+ SfxItemSet& m_rAttrSet;
+};
+
+/** Collects all style-covered spans from i_rHints to o_rSpanMap. In
+ addition inserts dummy spans with pointer to format equal to 0 for
+ all gaps (i.e. spans not covered by any style). This simplifies
+ creation of autostyles for all needed spans, but it means all code
+ that tries to access the pointer has to check if it's non-null!
+ */
+void
+lcl_CollectHintSpans(const SwpHints& i_rHints, const sal_Int32 nLength,
+ AttrSpanMap_t& o_rSpanMap)
+{
+ sal_Int32 nLastEnd(0);
+
+ for (size_t i = 0; i < i_rHints.Count(); ++i)
+ {
+ const SwTextAttr* pHint = i_rHints.Get(i);
+ const sal_uInt16 nWhich(pHint->Which());
+ if (nWhich == RES_TXTATR_CHARFMT || nWhich == RES_TXTATR_AUTOFMT)
+ {
+ const AttrSpan_t aSpan(pHint->GetStart(), *pHint->End());
+ o_rSpanMap.emplace(aSpan, pHint);
+
+ // < not != because there may be multiple CHARFMT at same range
+ if (nLastEnd < aSpan.first)
+ {
+ // insert dummy span covering the gap
+ o_rSpanMap.emplace( AttrSpan_t(nLastEnd, aSpan.first), nullptr );
+ }
+
+ nLastEnd = aSpan.second;
+ }
+ }
+
+ // no hints at the end (special case: no hints at all in i_rHints)
+ if (nLastEnd != nLength && nLength != 0)
+ {
+ o_rSpanMap.emplace(AttrSpan_t(nLastEnd, nLength), nullptr);
+ }
+}
+
+void
+lcl_FillWhichIds(const SfxItemSet& i_rAttrSet, std::vector<sal_uInt16>& o_rClearIds)
+{
+ o_rClearIds.reserve(i_rAttrSet.Count());
+ SfxItemIter aIter(i_rAttrSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ o_rClearIds.push_back(pItem->Which());
+ }
+}
+
+struct SfxItemSetClearer
+{
+ SfxItemSet & m_rItemSet;
+ explicit SfxItemSetClearer(SfxItemSet & rItemSet) : m_rItemSet(rItemSet) { }
+ void operator()(sal_uInt16 const nWhich) { m_rItemSet.ClearItem(nWhich); }
+};
+
+}
+
+/** Does the hard work of SwTextNode::FormatToTextAttr: the real conversion
+ of items to automatic styles.
+ */
+void
+SwTextNode::impl_FormatToTextAttr(const SfxItemSet& i_rAttrSet)
+{
+ typedef AttrSpanMap_t::iterator AttrSpanMap_iterator_t;
+ AttrSpanMap_t aAttrSpanMap;
+
+ if (i_rAttrSet.Count() == 0)
+ {
+ return;
+ }
+
+ // 1. Identify all spans in hints' array
+
+ lcl_CollectHintSpans(*m_pSwpHints, m_Text.getLength(), aAttrSpanMap);
+
+ // 2. Go through all spans and insert new attrs
+
+ AttrSpanMap_iterator_t aCurRange(aAttrSpanMap.begin());
+ const AttrSpanMap_iterator_t aEnd(aAttrSpanMap.end());
+ while (aCurRange != aEnd)
+ {
+ typedef std::pair<AttrSpanMap_iterator_t, AttrSpanMap_iterator_t>
+ AttrSpanMapRange_t;
+ AttrSpanMapRange_t aRange(aAttrSpanMap.equal_range(aCurRange->first));
+
+ // 2a. Collect attributes to insert
+
+ SfxItemSet aCurSet(i_rAttrSet);
+ std::for_each(aRange.first, aRange.second, RemovePresentAttrs(aCurSet));
+
+ // 2b. Insert automatic style containing the collected attributes
+
+ if (aCurSet.Count() != 0)
+ {
+ AttrSpanMap_iterator_t aAutoStyleIt(
+ std::find_if(aRange.first, aRange.second, IsAutoStyle()));
+ if (aAutoStyleIt != aRange.second)
+ {
+ // there already is an automatic style on that span:
+ // create new one and remove the original one
+ SwTextAttr* const pAutoStyle(const_cast<SwTextAttr*>(aAutoStyleIt->second));
+ const std::shared_ptr<SfxItemSet> pOldStyle(
+ static_cast<const SwFormatAutoFormat&>(
+ pAutoStyle->GetAttr()).GetStyleHandle());
+ aCurSet.Put(*pOldStyle);
+
+ // remove the old hint
+ m_pSwpHints->Delete(pAutoStyle);
+ DestroyAttr(pAutoStyle);
+ }
+ m_pSwpHints->Insert(
+ MakeTextAttr(GetDoc(), aCurSet,
+ aCurRange->first.first, aCurRange->first.second));
+ }
+
+ aCurRange = aRange.second;
+ }
+
+ // hints were directly inserted, so need to fix the Ignore flags now
+ m_pSwpHints->MergePortions(*this);
+
+ // 3. Clear items from the node
+ std::vector<sal_uInt16> aClearedIds;
+ lcl_FillWhichIds(i_rAttrSet, aClearedIds);
+ ClearItemsFromAttrSet(aClearedIds);
+}
+
+void SwTextNode::FormatToTextAttr( SwTextNode* pNd )
+{
+ SfxItemSet aThisSet( GetDoc().GetAttrPool(), aCharFormatSetRange );
+ if( HasSwAttrSet() && GetpSwAttrSet()->Count() )
+ aThisSet.Put( *GetpSwAttrSet() );
+
+ GetOrCreateSwpHints();
+
+ if( pNd == this )
+ {
+ impl_FormatToTextAttr(aThisSet);
+ }
+ else
+ {
+ // There are five possible combinations of items from this and
+ // pNd (pNd is the 'main' node):
+
+ // case pNd this action
+
+ // 1 - - do nothing
+ // 2 - a convert item to attr of this
+ // 3 a - convert item to attr of pNd
+ // 4 a a clear item in this
+ // 5 a b convert item to attr of this
+
+ SfxItemSet aNdSet( pNd->GetDoc().GetAttrPool(), aCharFormatSetRange );
+ if( pNd->HasSwAttrSet() && pNd->GetpSwAttrSet()->Count() )
+ aNdSet.Put( *pNd->GetpSwAttrSet() );
+
+ pNd->GetOrCreateSwpHints();
+
+ std::vector<sal_uInt16> aProcessedIds;
+
+ if( aThisSet.Count() )
+ {
+ SfxItemIter aIter( aThisSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem(), *pNdItem = nullptr;
+ SfxItemSet aConvertSet( GetDoc().GetAttrPool(), aCharFormatSetRange );
+ std::vector<sal_uInt16> aClearWhichIds;
+
+ do
+ {
+ if( SfxItemState::SET == aNdSet.GetItemState( pItem->Which(), false, &pNdItem ) )
+ {
+ if (*pItem == *pNdItem) // 4
+ {
+ aClearWhichIds.push_back( pItem->Which() );
+ }
+ else // 5
+ {
+ aConvertSet.Put(*pItem);
+ }
+ aProcessedIds.push_back(pItem->Which());
+ }
+ else // 2
+ {
+ aConvertSet.Put(*pItem);
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+
+ // 4/ clear items of this that are set with the same value on pNd
+ ClearItemsFromAttrSet( aClearWhichIds );
+
+ // 2, 5/ convert all other items to attrs
+ impl_FormatToTextAttr(aConvertSet);
+ }
+
+ {
+ std::for_each(aProcessedIds.begin(), aProcessedIds.end(),
+ SfxItemSetClearer(aNdSet));
+
+ // 3/ convert items to attrs
+ pNd->impl_FormatToTextAttr(aNdSet);
+
+ if( aNdSet.Count() )
+ {
+ SwFormatChg aTmp1( pNd->GetFormatColl() );
+ pNd->CallSwClientNotify(sw::LegacyModifyHint(&aTmp1, &aTmp1));
+ }
+ }
+ }
+
+ SetCalcHiddenCharFlags();
+
+ pNd->TryDeleteSwpHints();
+}
+
+void SwpHints::CalcFlags()
+{
+ m_bDDEFields = m_bFootnote = false;
+ const size_t nSize = Count();
+ for( size_t nPos = 0; nPos < nSize; ++nPos )
+ {
+ const SwTextAttr* pAttr = Get( nPos );
+ switch( pAttr->Which() )
+ {
+ case RES_TXTATR_FTN:
+ m_bFootnote = true;
+ if ( m_bDDEFields )
+ return;
+ break;
+ case RES_TXTATR_FIELD:
+ {
+ const SwField* pField = pAttr->GetFormatField().GetField();
+ if( SwFieldIds::Dde == pField->GetTyp()->Which() )
+ {
+ m_bDDEFields = true;
+ if ( m_bFootnote )
+ return;
+ }
+ }
+ break;
+ }
+ }
+}
+
+bool SwpHints::CalcHiddenParaField() const
+{
+ m_bCalcHiddenParaField = false;
+ const bool bOldHiddenByParaField = m_bHiddenByParaField;
+ bool bNewHiddenByParaField = false;
+ int nNewResultWeight = 0;
+ const size_t nSize = Count();
+ const SwTextAttr* pTextHt;
+
+ for (size_t nPos = 0; nPos < nSize; ++nPos)
+ {
+ pTextHt = Get(nPos);
+ const sal_uInt16 nWhich = pTextHt->Which();
+
+ if (RES_TXTATR_FIELD == nWhich)
+ {
+ // see also SwTextFrame::IsHiddenNow()
+ const SwFormatField& rField = pTextHt->GetFormatField();
+ int nCurWeight = m_rParent.GetDoc().FieldCanHideParaWeight(rField.GetField()->GetTyp()->Which());
+ if (nCurWeight > nNewResultWeight)
+ {
+ nNewResultWeight = nCurWeight;
+ bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
+ }
+ else if (nCurWeight == nNewResultWeight && bNewHiddenByParaField)
+ {
+ // Currently, for both supported hiding types (HiddenPara, Database), "Don't hide"
+ // takes precedence - i.e., if there's a "Don't hide" field of that weight, we only
+ // care about fields of higher weight.
+ bNewHiddenByParaField = m_rParent.GetDoc().FieldHidesPara(*rField.GetField());
+ }
+ }
+ }
+ SetHiddenByParaField(bNewHiddenByParaField);
+ return bOldHiddenByParaField != bNewHiddenByParaField;
+}
+
+void SwpHints::NoteInHistory( SwTextAttr *pAttr, const bool bNew )
+{
+ if ( m_pHistory ) { m_pHistory->AddHint( pAttr, bNew ); }
+}
+
+namespace {
+struct Portion {
+ SwTextAttr* pTextAttr;
+ int nKey;
+ bool isRsidOnlyAutoFormat;
+};
+typedef std::vector< Portion > PortionMap;
+enum MergeResult { MATCH, DIFFER_ONLY_RSID, DIFFER };
+}
+
+static MergeResult lcl_Compare_Attributes(
+ int i, int j,
+ const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange1,
+ const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange2,
+ std::vector<bool>& RsidOnlyAutoFormatFlagMap);
+
+bool SwpHints::MergePortions( SwTextNode& rNode )
+{
+ if ( !Count() )
+ return false;
+
+ // sort before merging
+ Resort();
+
+ bool bRet = false;
+ PortionMap aPortionMap;
+ aPortionMap.reserve(Count() + 1);
+ std::vector<bool> RsidOnlyAutoFormatFlagMap;
+ RsidOnlyAutoFormatFlagMap.resize(Count() + 1);
+ sal_Int32 nLastPorStart = COMPLETE_STRING;
+ sal_Int32 nKey = 0;
+
+ // get portions by start position:
+ for ( size_t i = 0; i < Count(); ++i )
+ {
+ SwTextAttr *pHt = Get( i );
+ if ( RES_TXTATR_CHARFMT != pHt->Which() &&
+ RES_TXTATR_AUTOFMT != pHt->Which() )
+ //&&
+ //RES_TXTATR_INETFMT != pHt->Which() )
+ continue;
+
+ bool isRsidOnlyAutoFormat(false);
+ // check for RSID-only AUTOFMT
+ if (RES_TXTATR_AUTOFMT == pHt->Which())
+ {
+ std::shared_ptr<SfxItemSet> const & pSet(
+ pHt->GetAutoFormat().GetStyleHandle());
+ if ((pSet->Count() == 1) && pSet->GetItem(RES_CHRATR_RSID, false))
+ {
+ // fdo#70201: eliminate no-extent RSID-only AUTOFMT
+ // could be produced by ReplaceText or (maybe?) RstAttr
+ if (pHt->GetStart() == *pHt->GetEnd())
+ {
+ DeleteAtPos(i); // kill it without History!
+ SwTextAttr::Destroy(pHt, rNode.GetDoc().GetAttrPool());
+ --i;
+ continue;
+ }
+ // fdo#52028: this one has _only_ RSID => ignore it completely
+ if (!pHt->IsFormatIgnoreStart() || !pHt->IsFormatIgnoreEnd())
+ {
+ NoteInHistory(pHt);
+ pHt->SetFormatIgnoreStart(true);
+ pHt->SetFormatIgnoreEnd (true);
+ NoteInHistory(pHt, true);
+ }
+ isRsidOnlyAutoFormat = true;
+ }
+ }
+
+ if (pHt->GetStart() == *pHt->GetEnd())
+ {
+ // no-length hints are a disease. ignore them here.
+ // the SwAttrIter::SeekFwd will not call Rst/Chg for them.
+ continue;
+ }
+
+ const sal_Int32 nPorStart = pHt->GetStart();
+ if (nPorStart != nLastPorStart)
+ ++nKey;
+ nLastPorStart = nPorStart;
+ aPortionMap.push_back(Portion {pHt, nKey, isRsidOnlyAutoFormat});
+ RsidOnlyAutoFormatFlagMap[nKey] = isRsidOnlyAutoFormat;
+ }
+
+ // we add data strictly in-order, so we can binary-search the vector
+ auto equal_range = [&aPortionMap](int i)
+ {
+ Portion key { nullptr, i, false };
+ return std::equal_range(aPortionMap.begin(), aPortionMap.end(), key,
+ [] (const Portion& lhs, const Portion& rhs) -> bool
+ {
+ return lhs.nKey < rhs.nKey;
+ });
+ };
+
+ // check if portion i can be merged with portion i+1:
+ // note: need to include i=0 to set IgnoreStart and j=nKey+1 to reset
+ // IgnoreEnd at first / last portion
+ int i = 0;
+ int j = i + 1;
+ while ( i <= nKey )
+ {
+ std::pair< PortionMap::const_iterator, PortionMap::const_iterator > aRange1 = equal_range( i );
+ std::pair< PortionMap::const_iterator, PortionMap::const_iterator > aRange2 = equal_range( j );
+
+ MergeResult eMerge = lcl_Compare_Attributes(i, j, aRange1, aRange2, RsidOnlyAutoFormatFlagMap);
+
+ if (MATCH == eMerge)
+ {
+ // important: delete second range so any IgnoreStart on the first
+ // range is still valid
+ // erase all elements with key i + 1
+ sal_Int32 nNewPortionEnd = 0;
+ for ( auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2 )
+ {
+ SwTextAttr *const p2 = aIter2->pTextAttr;
+ nNewPortionEnd = *p2->GetEnd();
+
+ const size_t nCountBeforeDelete = Count();
+ Delete( p2 );
+
+ // robust: check if deletion actually took place before destroying attribute:
+ if ( Count() < nCountBeforeDelete )
+ rNode.DestroyAttr( p2 );
+ }
+ aPortionMap.erase( aRange2.first, aRange2.second );
+ ++j;
+
+ // change all attributes with key i
+ aRange1 = equal_range( i );
+ for ( auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1 )
+ {
+ SwTextAttr *const p1 = aIter1->pTextAttr;
+ NoteInHistory( p1 );
+ p1->SetEnd(nNewPortionEnd);
+ NoteInHistory( p1, true );
+ bRet = true;
+ }
+
+ if (bRet)
+ {
+ Resort();
+ }
+ }
+ else
+ {
+ // when not merging the ignore flags need to be either set or reset
+ // (reset too in case one of the autofmts was recently changed)
+ bool const bSetIgnoreFlag(DIFFER_ONLY_RSID == eMerge);
+ for (auto aIter1 = aRange1.first; aIter1 != aRange1.second; ++aIter1)
+ {
+ if (!aIter1->isRsidOnlyAutoFormat) // already set above, don't change
+ {
+ SwTextAttr *const pCurrent(aIter1->pTextAttr);
+ if (pCurrent->IsFormatIgnoreEnd() != bSetIgnoreFlag)
+ {
+ NoteInHistory(pCurrent);
+ pCurrent->SetFormatIgnoreEnd(bSetIgnoreFlag);
+ NoteInHistory(pCurrent, true);
+ }
+ }
+ }
+ for (auto aIter2 = aRange2.first; aIter2 != aRange2.second; ++aIter2)
+ {
+ if (!aIter2->isRsidOnlyAutoFormat) // already set above, don't change
+ {
+ SwTextAttr *const pCurrent(aIter2->pTextAttr);
+ if (pCurrent->IsFormatIgnoreStart() != bSetIgnoreFlag)
+ {
+ NoteInHistory(pCurrent);
+ pCurrent->SetFormatIgnoreStart(bSetIgnoreFlag);
+ NoteInHistory(pCurrent, true);
+ }
+ }
+ }
+ i = j; // ++i not enough: i + 1 may have been deleted (MATCH)!
+ ++j;
+ }
+ }
+
+ return bRet;
+}
+
+
+static MergeResult lcl_Compare_Attributes(
+ int i, int j,
+ const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange1,
+ const std::pair< PortionMap::const_iterator, PortionMap::const_iterator >& aRange2,
+ std::vector<bool>& RsidOnlyAutoFormatFlagMap)
+{
+ PortionMap::const_iterator aIter1 = aRange1.first;
+ PortionMap::const_iterator aIter2 = aRange2.first;
+
+ size_t const nAttributesInPor1 = std::distance(aRange1.first, aRange1.second);
+ size_t const nAttributesInPor2 = std::distance(aRange2.first, aRange2.second);
+ bool const isRsidOnlyAutoFormat1 = i < sal_Int32(RsidOnlyAutoFormatFlagMap.size()) && RsidOnlyAutoFormatFlagMap[i];
+ bool const isRsidOnlyAutoFormat2 = j < sal_Int32(RsidOnlyAutoFormatFlagMap.size()) && RsidOnlyAutoFormatFlagMap[j];
+
+ // if both have one they could be equal, but not if only one has it
+ bool const bSkipRsidOnlyAutoFormat(nAttributesInPor1 != nAttributesInPor2);
+
+ // this loop needs to handle the case where one has a CHARFMT and the
+ // other CHARFMT + RSID-only AUTOFMT, so...
+ // want to skip over RSID-only AUTOFMT here, hence the -1
+ if (!((nAttributesInPor1 - (isRsidOnlyAutoFormat1 ? 1 : 0)) ==
+ (nAttributesInPor2 - (isRsidOnlyAutoFormat2 ? 1 : 0))
+ && (nAttributesInPor1 != 0 || nAttributesInPor2 != 0)))
+ {
+ return DIFFER;
+ }
+
+ MergeResult eMerge(MATCH);
+
+ // _if_ there is one element more either in aRange1 or aRange2
+ // it _must_ be an RSID-only AUTOFMT, which can be ignored here...
+ // But if both have RSID-only AUTOFMT they could be equal, no skip!
+ while (aIter1 != aRange1.second || aIter2 != aRange2.second)
+ {
+ // first of all test if there's no gap (before skipping stuff!)
+ if (aIter1 != aRange1.second && aIter2 != aRange2.second &&
+ *aIter1->pTextAttr->End() < aIter2->pTextAttr->GetStart())
+ {
+ return DIFFER;
+ }
+ // skip it - cannot be equal if bSkipRsidOnlyAutoFormat is set
+ if (bSkipRsidOnlyAutoFormat
+ && aIter1 != aRange1.second && aIter1->isRsidOnlyAutoFormat)
+ {
+ assert(DIFFER != eMerge);
+ eMerge = DIFFER_ONLY_RSID;
+ ++aIter1;
+ continue;
+ }
+ if (bSkipRsidOnlyAutoFormat
+ && aIter2 != aRange2.second && aIter2->isRsidOnlyAutoFormat)
+ {
+ assert(DIFFER != eMerge);
+ eMerge = DIFFER_ONLY_RSID;
+ ++aIter2;
+ continue;
+ }
+ assert(aIter1 != aRange1.second && aIter2 != aRange2.second);
+ SwTextAttr const*const p1 = aIter1->pTextAttr;
+ SwTextAttr const*const p2 = aIter2->pTextAttr;
+ if (p1->Which() != p2->Which())
+ {
+ return DIFFER;
+ }
+ if (!(*p1 == *p2))
+ {
+ // fdo#52028: for auto styles, check if they differ only
+ // in the RSID, which should have no effect on text layout
+ if (RES_TXTATR_AUTOFMT != p1->Which())
+ return DIFFER;
+
+ const SfxItemSet& rSet1 = *p1->GetAutoFormat().GetStyleHandle();
+ const SfxItemSet& rSet2 = *p2->GetAutoFormat().GetStyleHandle();
+
+ // sadly SfxItemSet::operator== does not seem to work?
+ SfxItemIter iter1(rSet1);
+ SfxItemIter iter2(rSet2);
+ for (SfxPoolItem const* pItem1 = iter1.GetCurItem(),
+ * pItem2 = iter2.GetCurItem();;)
+ {
+ if (pItem1 && pItem1->Which() == RES_CHRATR_RSID)
+ pItem1 = iter1.NextItem();
+ if (pItem2 && pItem2->Which() == RES_CHRATR_RSID)
+ pItem2 = iter2.NextItem();
+ if (!pItem1 && !pItem2)
+ {
+ eMerge = DIFFER_ONLY_RSID;
+ break;
+ }
+ if (!pItem1 || !pItem2)
+ {
+ return DIFFER;
+ }
+ if (pItem1 != pItem2) // all are poolable
+ {
+ assert(IsInvalidItem(pItem1) || IsInvalidItem(pItem2) || pItem1->Which() != pItem2->Which() || *pItem1 != *pItem2);
+ return DIFFER;
+ }
+ pItem1 = iter1.NextItem();
+ pItem2 = iter2.NextItem();
+ }
+ }
+ ++aIter1;
+ ++aIter2;
+ }
+ return eMerge;
+}
+
+
+// check if there is already a character format and adjust the sort numbers
+static void lcl_CheckSortNumber( const SwpHints& rHints, SwTextCharFormat& rNewCharFormat )
+{
+ const sal_Int32 nHtStart = rNewCharFormat.GetStart();
+ const sal_Int32 nHtEnd = *rNewCharFormat.GetEnd();
+ sal_uInt16 nSortNumber = 0;
+
+ for ( size_t i = 0; i < rHints.Count(); ++i )
+ {
+ const SwTextAttr* pOtherHt = rHints.Get(i);
+
+ const sal_Int32 nOtherStart = pOtherHt->GetStart();
+
+ if ( nOtherStart > nHtStart )
+ break;
+
+ if ( RES_TXTATR_CHARFMT == pOtherHt->Which() )
+ {
+ const sal_Int32 nOtherEnd = *pOtherHt->End();
+
+ if ( nOtherStart == nHtStart && nOtherEnd == nHtEnd )
+ {
+ nSortNumber = static_txtattr_cast<const SwTextCharFormat*>(pOtherHt)->GetSortNumber() + 1;
+ }
+ }
+ }
+
+ if ( nSortNumber > 0 )
+ rNewCharFormat.SetSortNumber( nSortNumber );
+}
+
+/*
+ * Try to insert the new hint.
+ * Depending on the type of the hint, this either always succeeds, or may fail.
+ * Depending on the type of the hint, other hints may be deleted or
+ * overwritten.
+ * The return value indicates successful insertion.
+ */
+bool SwpHints::TryInsertHint(
+ SwTextAttr* const pHint,
+ SwTextNode &rNode,
+ const SetAttrMode nMode )
+{
+ if ( MAX_HINTS <= Count() ) // we're sorry, this flight is overbooked...
+ {
+ OSL_FAIL("hints array full :-(");
+ rNode.DestroyAttr(pHint);
+ return false;
+ }
+
+ const sal_Int32 *pHtEnd = pHint->GetEnd();
+ const sal_uInt16 nWhich = pHint->Which();
+ std::vector<sal_uInt16> aWhichSublist;
+
+ switch( nWhich )
+ {
+ case RES_TXTATR_CHARFMT:
+ {
+ // Check if character format contains hidden attribute:
+ const SwCharFormat* pFormat = pHint->GetCharFormat().GetCharFormat();
+ if ( SfxItemState::SET == pFormat->GetItemState( RES_CHRATR_HIDDEN ) )
+ rNode.SetCalcHiddenCharFlags();
+
+ static_txtattr_cast<SwTextCharFormat*>(pHint)->ChgTextNode( &rNode );
+ break;
+ }
+ // #i75430# Recalc hidden flags if necessary
+ case RES_TXTATR_AUTOFMT:
+ {
+ std::shared_ptr<SfxItemSet> const & pSet( pHint->GetAutoFormat().GetStyleHandle() );
+ if (pHint->GetStart() == *pHint->GetEnd())
+ {
+ if (pSet->Count() == 1 && pSet->GetItem(RES_CHRATR_RSID, false))
+ { // empty range RSID-only hints could cause trouble, there's no
+ rNode.DestroyAttr(pHint); // need for them so don't insert
+ return false;
+ }
+ }
+ // Check if auto style contains hidden attribute:
+ const SfxPoolItem* pHiddenItem = CharFormat::GetItem( *pHint, RES_CHRATR_HIDDEN );
+ if ( pHiddenItem )
+ rNode.SetCalcHiddenCharFlags();
+
+ // fdo#71556: populate aWhichFormatAttr member of SwMsgPoolItem
+ const WhichRangesContainer& pRanges = pSet->GetRanges();
+ for(auto const & rPair : pRanges)
+ {
+ const sal_uInt16 nBeg = rPair.first;
+ const sal_uInt16 nEnd = rPair.second;
+ for( sal_uInt16 nSubElem = nBeg; nSubElem <= nEnd; ++nSubElem )
+ if( pSet->HasItem( nSubElem ) )
+ aWhichSublist.push_back( nSubElem );
+ }
+ break;
+ }
+ case RES_TXTATR_INETFMT:
+ static_txtattr_cast<SwTextINetFormat*>(pHint)->InitINetFormat(rNode);
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ {
+ SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
+ bool bDelFirst = nullptr != pTextField->GetpTextNode();
+ pTextField->ChgTextNode( &rNode );
+ SwDoc& rDoc = rNode.GetDoc();
+ const SwField* pField = pTextField->GetFormatField().GetField();
+
+ if( !rDoc.getIDocumentFieldsAccess().IsNewFieldLst() )
+ {
+ // certain fields must update the SwDoc's calculation flags
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::Database:
+ case SwFieldIds::SetExp:
+ case SwFieldIds::HiddenPara:
+ case SwFieldIds::HiddenText:
+ case SwFieldIds::DbNumSet:
+ case SwFieldIds::DbNextSet:
+ {
+ if( bDelFirst )
+ rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(false, *pTextField);
+ if( rNode.GetNodes().IsDocNodes() )
+ rDoc.getIDocumentFieldsAccess().InsDelFieldInFieldLst(true, *pTextField);
+ }
+ break;
+ case SwFieldIds::Dde:
+ if( rNode.GetNodes().IsDocNodes() )
+ static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
+ break;
+ default: break;
+ }
+ }
+
+ // insert into real document's nodes-array?
+ if( rNode.GetNodes().IsDocNodes() )
+ {
+ bool bInsFieldType = false;
+ switch( pField->GetTyp()->Which() )
+ {
+ case SwFieldIds::SetExp:
+ bInsFieldType = static_cast<SwSetExpFieldType*>(pField->GetTyp())->IsDeleted();
+ if( nsSwGetSetExpType::GSE_SEQ & static_cast<SwSetExpFieldType*>(pField->GetTyp())->GetType() )
+ {
+ // register the field at its FieldType before setting
+ // the sequence reference number!
+ SwSetExpFieldType* pFieldType = static_cast<SwSetExpFieldType*>(
+ rDoc.getIDocumentFieldsAccess().InsertFieldType( *pField->GetTyp() ) );
+ if( pFieldType != pField->GetTyp() )
+ {
+ SwFormatField* pFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+ pFormatField->RegisterToFieldType( *pFieldType );
+ pFormatField->GetField()->ChgTyp( pFieldType );
+ }
+ pFieldType->SetSeqRefNo( *const_cast<SwSetExpField*>(static_cast<const SwSetExpField*>(pField)) );
+ }
+ break;
+ case SwFieldIds::User:
+ bInsFieldType = static_cast<SwUserFieldType*>(pField->GetTyp())->IsDeleted();
+ break;
+
+ case SwFieldIds::Dde:
+ if( rDoc.getIDocumentFieldsAccess().IsNewFieldLst() )
+ static_cast<SwDDEFieldType*>(pField->GetTyp())->IncRefCnt();
+ bInsFieldType = static_cast<SwDDEFieldType*>(pField->GetTyp())->IsDeleted();
+ break;
+
+ case SwFieldIds::Postit:
+ if ( rDoc.GetDocShell() )
+ {
+ rDoc.GetDocShell()->Broadcast( SwFormatFieldHint(
+ &pTextField->GetFormatField(), SwFormatFieldHintWhich::INSERTED));
+ }
+ break;
+ default: break;
+ }
+ if( bInsFieldType )
+ rDoc.getIDocumentFieldsAccess().InsDeletedFieldType( *pField->GetTyp() );
+ }
+ }
+ break;
+ case RES_TXTATR_FTN :
+ static_cast<SwTextFootnote*>(pHint)->ChgTextNode( &rNode );
+ break;
+ case RES_TXTATR_REFMARK:
+ static_txtattr_cast<SwTextRefMark*>(pHint)->ChgTextNode( &rNode );
+ if( rNode.GetNodes().IsDocNodes() )
+ {
+ // search for a reference with the same name
+ SwTextAttr* pTmpHt;
+ for( size_t n = 0, nEnd = Count(); n < nEnd; ++n )
+ {
+ const sal_Int32 *pTmpHtEnd;
+ const sal_Int32 *pTmpHintEnd;
+ if (RES_TXTATR_REFMARK == (pTmpHt = Get(n))->Which() &&
+ pHint->GetAttr() == pTmpHt->GetAttr() &&
+ nullptr != ( pTmpHtEnd = pTmpHt->GetEnd() ) &&
+ nullptr != ( pTmpHintEnd = pHint->GetEnd() ) )
+ {
+ SwComparePosition eCmp = ::ComparePosition(
+ pTmpHt->GetStart(), *pTmpHtEnd,
+ pHint->GetStart(), *pTmpHintEnd );
+ bool bDelOld = true, bChgStart = false, bChgEnd = false;
+ switch( eCmp )
+ {
+ case SwComparePosition::Before:
+ case SwComparePosition::Behind: bDelOld = false; break;
+
+ case SwComparePosition::Outside: bChgStart = bChgEnd = true; break;
+
+ case SwComparePosition::CollideEnd:
+ case SwComparePosition::OverlapBefore: bChgStart = true; break;
+ case SwComparePosition::CollideStart:
+ case SwComparePosition::OverlapBehind: bChgEnd = true; break;
+ default: break;
+ }
+
+ if( bChgStart )
+ {
+ pHint->SetStart( pTmpHt->GetStart() );
+ }
+ if( bChgEnd )
+ pHint->SetEnd(*pTmpHtEnd);
+
+ if( bDelOld )
+ {
+ NoteInHistory( pTmpHt );
+ rNode.DestroyAttr( Cut( n-- ) );
+ --nEnd;
+ }
+ }
+ }
+ }
+ break;
+ case RES_TXTATR_TOXMARK:
+ static_txtattr_cast<SwTextTOXMark*>(pHint)->ChgTextNode( &rNode );
+ break;
+
+ case RES_TXTATR_CJK_RUBY:
+ static_txtattr_cast<SwTextRuby*>(pHint)->InitRuby(rNode);
+ break;
+
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ static_txtattr_cast<SwTextMeta *>(pHint)->ChgTextNode( &rNode );
+ break;
+
+ case RES_TXTATR_CONTENTCONTROL:
+ static_txtattr_cast<SwTextContentControl*>(pHint)->ChgTextNode( &rNode );
+ break;
+
+ case RES_CHRATR_HIDDEN:
+ rNode.SetCalcHiddenCharFlags();
+ break;
+ }
+
+ if( SetAttrMode::DONTEXPAND & nMode )
+ pHint->SetDontExpand( true );
+
+ // special handling for SwTextAttrs without end:
+ // 1) they cannot overlap
+ // 2) if two fields are adjacent, they must not be merged into one
+ // this is guaranteed by inserting a CH_TXTATR_* into the paragraph text!
+ sal_Int32 nHtStart = pHint->GetStart();
+ if( !pHtEnd )
+ {
+ Insert( pHint );
+ NoteInHistory(pHint, true);
+ CalcFlags();
+#ifdef DBG_UTIL
+ if( !rNode.GetDoc().IsInReading() )
+ CHECK;
+#endif
+ // ... and notify listeners
+ if(rNode.HasWriterListeners())
+ {
+ SwUpdateAttr aHint(
+ nHtStart,
+ nHtStart,
+ nWhich);
+
+ rNode.TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+
+ return true;
+ }
+
+ // from here on, pHint is known to have an end index!
+
+ if( *pHtEnd < nHtStart )
+ {
+ assert(*pHtEnd >= nHtStart);
+
+ // just swap the nonsense:
+ pHint->SetStart(*pHtEnd);
+ pHint->SetEnd(nHtStart);
+ nHtStart = pHint->GetStart();
+ }
+
+ // I need this value later on for notification but the pointer may become invalid
+ const sal_Int32 nHintEnd = *pHtEnd;
+ const bool bNoHintAdjustMode = bool(SetAttrMode::NOHINTADJUST & nMode);
+
+ // handle nesting attributes: inserting may fail due to overlap!
+ if (pHint->IsNesting())
+ {
+ const bool bRet(
+ TryInsertNesting(rNode, *static_txtattr_cast<SwTextAttrNesting*>(pHint)));
+ if (!bRet) return false;
+ }
+ // Currently REFMARK and TOXMARK have OverlapAllowed set to true.
+ // These attributes may be inserted directly.
+ // Also attributes without length may be inserted directly.
+ // SETATTR_NOHINTADJUST is set e.g., during undo.
+ // Portion building in not necessary during XML import.
+ else if ( !bNoHintAdjustMode &&
+ !pHint->IsOverlapAllowedAttr() &&
+ !rNode.GetDoc().IsInXMLImport() &&
+ ( RES_TXTATR_AUTOFMT == nWhich ||
+ RES_TXTATR_CHARFMT == nWhich ) )
+ {
+ assert( nWhich != RES_TXTATR_AUTOFMT ||
+ static_cast<const SwFormatAutoFormat&>(pHint->GetAttr()).GetStyleHandle()->GetPool() ==
+ &rNode.GetDoc().GetAttrPool());
+
+ BuildPortions( rNode, *pHint, nMode );
+
+ if ( nHtStart < nHintEnd ) // skip merging for 0-length attributes
+ MergePortions( rNode );
+ }
+ else
+ {
+ // There may be more than one character style at the current position.
+ // Take care of the sort number.
+ // FME 2007-11-08 #i82989# in NOHINTADJUST mode, we want to insert
+ // character attributes directly
+ if (!bNoHintAdjustMode
+ && ( (RES_TXTATR_CHARFMT == nWhich)
+ // tdf#149978 also for import of automatic styles, could be produced by non-LO application
+ || (RES_TXTATR_AUTOFMT == nWhich && rNode.GetDoc().IsInXMLImport())))
+ {
+ BuildPortions( rNode, *pHint, nMode );
+ }
+ else
+ {
+ // #i82989# Check sort numbers in NoHintAdjustMode
+ if ( RES_TXTATR_CHARFMT == nWhich )
+ lcl_CheckSortNumber(*this, *static_txtattr_cast<SwTextCharFormat*>(pHint));
+
+ Insert( pHint );
+ NoteInHistory( pHint, true );
+ }
+ }
+
+ // ... and notify listeners
+ if ( rNode.HasWriterListeners() )
+ {
+ const SwUpdateAttr aHint(nHtStart, nHintEnd, nWhich, std::move(aWhichSublist));
+ rNode.TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
+ }
+
+#ifdef DBG_UTIL
+ if( !bNoHintAdjustMode && !rNode.GetDoc().IsInReading() )
+ CHECK;
+#endif
+
+ return true;
+}
+
+void SwpHints::DeleteAtPos( const size_t nPos )
+{
+ assert(!m_bStartMapNeedsSorting && "deleting at pos and the list needs sorting?");
+
+ SwTextAttr *pHint = Get(nPos);
+ assert( pHint->m_pHints == this );
+ // ChainDelete( pHint );
+ NoteInHistory( pHint );
+
+ // optimization: nPos is the position in the Starts array
+ SwTextAttr *pHt = m_HintsByStart[ nPos ];
+ m_HintsByStart.erase( m_HintsByStart.begin() + nPos );
+
+ if (m_bStartMapNeedsSorting)
+ ResortStartMap();
+ if (m_bEndMapNeedsSorting)
+ ResortEndMap();
+ if (m_bWhichMapNeedsSorting)
+ ResortWhichMap();
+
+ auto findIt = std::lower_bound(m_HintsByEnd.begin(), m_HintsByEnd.end(), pHt, CompareSwpHtEnd());
+ assert(*findIt == pHt);
+ m_HintsByEnd.erase(findIt);
+
+ auto findIt2 = std::lower_bound(m_HintsByWhichAndStart.begin(), m_HintsByWhichAndStart.end(), pHt, CompareSwpHtWhichStart());
+ assert(*findIt2 == pHt);
+ m_HintsByWhichAndStart.erase(findIt2);
+
+ pHt->m_pHints = nullptr;
+
+ if( pHint->Which() == RES_TXTATR_FIELD )
+ {
+ SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
+ const SwFieldType* pFieldTyp = pTextField->GetFormatField().GetField()->GetTyp();
+ if( SwFieldIds::Dde == pFieldTyp->Which() )
+ {
+ const SwTextNode* pNd = pTextField->GetpTextNode();
+ if( pNd && pNd->GetNodes().IsDocNodes() )
+ const_cast<SwDDEFieldType*>(static_cast<const SwDDEFieldType*>(pFieldTyp))->DecRefCnt();
+ pTextField->ChgTextNode(nullptr);
+ }
+ else if (m_bHiddenByParaField
+ && m_rParent.GetDoc().FieldCanHideParaWeight(pFieldTyp->Which()))
+ {
+ m_bCalcHiddenParaField = true;
+ }
+ }
+ else if ( pHint->Which() == RES_TXTATR_ANNOTATION )
+ {
+ SwTextField *const pTextField(static_txtattr_cast<SwTextField*>(pHint));
+ const_cast<SwFormatField&>(pTextField->GetFormatField()).Broadcast(
+ SwFormatFieldHint(&pTextField->GetFormatField(), SwFormatFieldHintWhich::REMOVED));
+ }
+
+ CalcFlags();
+ CHECK_NOTMERGED; // called from BuildPortions
+}
+
+/// delete the hint
+/// precondition: pTextHt must be in this array
+void SwpHints::Delete( SwTextAttr const * pTextHt )
+{
+ const size_t nPos = GetIndexOf( pTextHt );
+ assert(SAL_MAX_SIZE != nPos);
+ if( SAL_MAX_SIZE != nPos )
+ DeleteAtPos( nPos );
+}
+
+void SwTextNode::ClearSwpHintsArr( bool bDelFields )
+{
+ if ( !HasHints() )
+ return;
+
+ size_t nPos = 0;
+ while ( nPos < m_pSwpHints->Count() )
+ {
+ SwTextAttr* pDel = m_pSwpHints->Get( nPos );
+ bool bDel = false;
+
+ switch( pDel->Which() )
+ {
+ case RES_TXTATR_FLYCNT:
+ case RES_TXTATR_FTN:
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ if( bDelFields )
+ bDel = true;
+ break;
+ default:
+ bDel = true; break;
+ }
+
+ if( bDel )
+ {
+ m_pSwpHints->DeleteAtPos( nPos );
+ DestroyAttr( pDel );
+ }
+ else
+ ++nPos;
+ }
+}
+
+LanguageType SwTextNode::GetLang( const sal_Int32 nBegin, const sal_Int32 nLen,
+ sal_uInt16 nScript ) const
+{
+ LanguageType nRet = LANGUAGE_DONTKNOW;
+
+ if ( ! nScript )
+ {
+ nScript = g_pBreakIt->GetRealScriptOfText( m_Text, nBegin );
+ }
+
+ // #i91465# Consider nScript if pSwpHints == 0
+ const sal_uInt16 nWhichId = GetWhichOfScript( RES_CHRATR_LANGUAGE, nScript );
+
+ if ( HasHints() )
+ {
+ const sal_Int32 nEnd = nBegin + nLen;
+ const size_t nSize = m_pSwpHints->Count();
+ for ( size_t i = 0; i < nSize; ++i )
+ {
+ const SwTextAttr *pHt = m_pSwpHints->Get(i);
+ const sal_Int32 nAttrStart = pHt->GetStart();
+ if( nEnd < nAttrStart )
+ break;
+
+ const sal_uInt16 nWhich = pHt->Which();
+
+ if( nWhichId == nWhich ||
+ ( ( pHt->IsCharFormatAttr() || RES_TXTATR_AUTOFMT == nWhich ) && CharFormat::IsItemIncluded( nWhichId, pHt ) ) )
+ {
+ const sal_Int32 *pEndIdx = pHt->End();
+ // do the attribute and the range overlap?
+ if( !pEndIdx )
+ continue;
+ if( nLen )
+ {
+ if( nAttrStart >= nEnd || nBegin >= *pEndIdx )
+ continue;
+ }
+ else if( nBegin != nAttrStart || ( nAttrStart != *pEndIdx && nBegin ))
+ {
+ if( nAttrStart >= nBegin )
+ continue;
+ if( pHt->DontExpand() ? nBegin >= *pEndIdx : nBegin > *pEndIdx)
+ continue;
+ }
+ const SfxPoolItem* pItem = CharFormat::GetItem( *pHt, nWhichId );
+ const LanguageType nLng = static_cast<const SvxLanguageItem*>(pItem)->GetLanguage();
+
+ // does the attribute completely cover the range?
+ if( nAttrStart <= nBegin && nEnd <= *pEndIdx )
+ nRet = nLng;
+ else if( LANGUAGE_DONTKNOW == nRet )
+ nRet = nLng; // partial overlap, the first one wins
+ }
+ }
+ }
+ if( LANGUAGE_DONTKNOW == nRet )
+ {
+ nRet = static_cast<const SvxLanguageItem&>(GetSwAttrSet().Get( nWhichId )).GetLanguage();
+ if( LANGUAGE_DONTKNOW == nRet )
+ nRet = GetAppLanguage();
+ }
+ return nRet;
+}
+
+sal_Unicode GetCharOfTextAttr( const SwTextAttr& rAttr )
+{
+ sal_Unicode cRet = CH_TXTATR_BREAKWORD;
+ switch ( rAttr.Which() )
+ {
+ case RES_TXTATR_REFMARK:
+ case RES_TXTATR_TOXMARK:
+ case RES_TXTATR_ANNOTATION:
+ cRet = CH_TXTATR_INWORD;
+ break;
+
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_FLYCNT:
+ case RES_TXTATR_FTN:
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ case RES_TXTATR_CONTENTCONTROL:
+ {
+ cRet = CH_TXTATR_BREAKWORD;
+ }
+ break;
+ case RES_TXTATR_LINEBREAK:
+ {
+ cRet = CH_TXTATR_NEWLINE;
+ }
+ break;
+
+ default:
+ assert(!"GetCharOfTextAttr: unknown attr");
+ break;
+ }
+ return cRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/txatbase.cxx b/sw/source/core/txtnode/txatbase.cxx
new file mode 100644
index 000000000..c846084a3
--- /dev/null
+++ b/sw/source/core/txtnode/txatbase.cxx
@@ -0,0 +1,181 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <optional>
+#include <libxml/xmlwriter.h>
+#include <svl/itempool.hxx>
+#include <txatbase.hxx>
+#include <fmtfld.hxx>
+
+SwTextAttr::SwTextAttr( SfxPoolItem& rAttr, sal_Int32 nStart )
+ : m_pAttr( &rAttr )
+ , m_nStart( nStart )
+ , m_bDontExpand( false )
+ , m_bLockExpandFlag( false )
+ , m_bDontMoveAttr( false )
+ , m_bCharFormatAttr( false )
+ , m_bOverlapAllowedAttr( false )
+ , m_bPriorityAttr( false )
+ , m_bDontExpandStart( false )
+ , m_bNesting( false )
+ , m_bHasDummyChar( false )
+ , m_bFormatIgnoreStart(false)
+ , m_bFormatIgnoreEnd(false)
+ , m_bHasContent( false )
+{
+}
+
+SwTextAttr::~SwTextAttr() COVERITY_NOEXCEPT_FALSE
+{
+}
+
+const sal_Int32* SwTextAttr::GetEnd() const
+{
+ return nullptr;
+}
+
+void SwTextAttr::SetEnd(sal_Int32 )
+{
+ assert(false);
+}
+
+void SwTextAttr::Destroy( SwTextAttr * pToDestroy, SfxItemPool& rPool )
+{
+ if (!pToDestroy) return;
+ SfxPoolItem * const pAttr = pToDestroy->m_pAttr;
+ delete pToDestroy;
+ rPool.Remove( *pAttr );
+}
+
+bool SwTextAttr::operator==( const SwTextAttr& rAttr ) const
+{
+ return GetAttr() == rAttr.GetAttr();
+}
+
+SwTextAttrEnd::SwTextAttrEnd( SfxPoolItem& rAttr,
+ sal_Int32 nStart, sal_Int32 nEnd ) :
+ SwTextAttr( rAttr, nStart ), m_nEnd( nEnd )
+{
+}
+
+const sal_Int32* SwTextAttrEnd::GetEnd() const
+{
+ return & m_nEnd;
+}
+
+void SwTextAttrEnd::SetEnd(sal_Int32 n)
+{
+ m_nEnd = n;
+ if (m_pHints)
+ m_pHints->EndPosChanged();
+}
+
+void SwTextAttr::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwTextAttr"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s",
+ BAD_CAST(typeid(*this).name()));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("start"), BAD_CAST(OString::number(m_nStart).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("dont-expand"),
+ BAD_CAST(OString::boolean(m_bDontExpand).getStr()));
+ if (End())
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("end"), BAD_CAST(OString::number(*End()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("m_pAttr"), "%p", m_pAttr);
+ const char* pWhich = nullptr;
+ std::optional<OString> oValue;
+ switch (Which())
+ {
+ case RES_TXTATR_AUTOFMT:
+ pWhich = "autofmt";
+ break;
+ case RES_TXTATR_ANNOTATION:
+ pWhich = "annotation";
+ break;
+ case RES_TXTATR_FLYCNT:
+ pWhich = "fly content";
+ break;
+ case RES_TXTATR_CHARFMT:
+ {
+ pWhich = "character format";
+ if (SwCharFormat* pCharFormat = GetCharFormat().GetCharFormat())
+ oValue = OString("name: " + OUStringToOString(pCharFormat->GetName(), RTL_TEXTENCODING_UTF8));
+ break;
+ }
+ case RES_TXTATR_INETFMT:
+ {
+ pWhich = "inet format";
+ const SwFormatINetFormat& rFormat = GetINetFormat();
+ oValue = OString("url: " + rFormat.GetValue().toUtf8());
+ break;
+ }
+ case RES_TXTATR_CJK_RUBY:
+ {
+ pWhich = "ruby";
+ const SwFormatRuby& rFormat = GetRuby();
+ oValue = OString("rubytext: " + rFormat.GetText().toUtf8());
+ break;
+ }
+ case RES_TXTATR_META:
+ {
+ pWhich = "meta";
+ break;
+ }
+ case RES_TXTATR_FIELD:
+ {
+ pWhich = "field";
+ break;
+ }
+ default:
+ break;
+ }
+ if (pWhich)
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("which"), BAD_CAST(pWhich));
+ if (oValue)
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(oValue->getStr()));
+ switch (Which())
+ {
+ case RES_TXTATR_AUTOFMT:
+ GetAutoFormat().dumpAsXml(pWriter);
+ break;
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_INPUTFIELD:
+ GetFormatField().dumpAsXml(pWriter);
+ break;
+ case RES_TXTATR_FTN:
+ break;
+ case RES_TXTATR_LINEBREAK:
+ GetLineBreak().dumpAsXml(pWriter);
+ break;
+ case RES_TXTATR_META:
+ break;
+ case RES_TXTATR_CONTENTCONTROL:
+ GetContentControl().dumpAsXml(pWriter);
+ break;
+ default:
+ SAL_WARN("sw.core", "Unhandled TXTATR");
+ break;
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/txatritr.cxx b/sw/source/core/txtnode/txatritr.cxx
new file mode 100644
index 000000000..883c09760
--- /dev/null
+++ b/sw/source/core/txtnode/txatritr.cxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <txatritr.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <fchrfmt.hxx>
+#include <charfmt.hxx>
+#include <breakit.hxx>
+#include <ndtxt.hxx>
+#include <txatbase.hxx>
+
+using namespace ::com::sun::star;
+
+SwScriptIterator::SwScriptIterator(
+ const OUString& rStr, sal_Int32 nStt, bool const bFrwrd)
+ : m_rText(rStr)
+ , m_nChgPos(rStr.getLength())
+ , m_nCurScript(i18n::ScriptType::WEAK)
+ , m_bForward(bFrwrd)
+{
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ if ( ! bFrwrd && nStt )
+ --nStt;
+
+ sal_Int32 nPos = nStt;
+ m_nCurScript = g_pBreakIt->GetBreakIter()->getScriptType(m_rText, nPos);
+ if( i18n::ScriptType::WEAK == m_nCurScript )
+ {
+ if( nPos )
+ {
+ nPos = g_pBreakIt->GetBreakIter()->beginOfScript(
+ m_rText, nPos, m_nCurScript);
+ if (nPos > 0 && nPos < m_rText.getLength())
+ {
+ nStt = --nPos;
+ m_nCurScript =
+ g_pBreakIt->GetBreakIter()->getScriptType(m_rText,nPos);
+ }
+ }
+ }
+
+ m_nChgPos = m_bForward
+ ? g_pBreakIt->GetBreakIter()->endOfScript(
+ m_rText, nStt, m_nCurScript)
+ : g_pBreakIt->GetBreakIter()->beginOfScript(
+ m_rText, nStt, m_nCurScript);
+}
+
+void SwScriptIterator::Next()
+{
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ if (m_bForward && m_nChgPos >= 0 && m_nChgPos < m_rText.getLength())
+ {
+ m_nCurScript =
+ g_pBreakIt->GetBreakIter()->getScriptType(m_rText, m_nChgPos);
+ m_nChgPos = g_pBreakIt->GetBreakIter()->endOfScript(
+ m_rText, m_nChgPos, m_nCurScript);
+ }
+ else if (!m_bForward && m_nChgPos > 0)
+ {
+ --m_nChgPos;
+ m_nCurScript =
+ g_pBreakIt->GetBreakIter()->getScriptType(m_rText, m_nChgPos);
+ m_nChgPos = g_pBreakIt->GetBreakIter()->beginOfScript(
+ m_rText, m_nChgPos, m_nCurScript);
+ }
+}
+
+SwLanguageIterator::SwLanguageIterator( const SwTextNode& rTNd,
+ sal_Int32 nStt )
+ : m_aScriptIter( rTNd.GetText(), nStt ),
+ m_rTextNode( rTNd ),
+ m_pParaItem( nullptr ),
+ m_nAttrPos( 0 ),
+ m_nChgPos( nStt )
+{
+ SearchNextChg();
+}
+
+bool SwLanguageIterator::Next()
+{
+ bool bRet = false;
+ if (m_nChgPos < m_aScriptIter.GetText().getLength())
+ {
+ bRet = true;
+ if( !m_aStack.empty() )
+ {
+ do {
+ const SwTextAttr* pHt = m_aStack.front();
+ const sal_Int32 nEndPos = *pHt->End();
+ if( m_nChgPos >= nEndPos )
+ m_aStack.pop_front();
+ else
+ break;
+ } while( !m_aStack.empty() );
+ }
+
+ if( !m_aStack.empty() )
+ {
+ const size_t nSavePos = m_nAttrPos;
+ SearchNextChg();
+ if( !m_aStack.empty() )
+ {
+ const SwTextAttr* pHt = m_aStack.front();
+ const sal_Int32 nEndPos = *pHt->End();
+ if( m_nChgPos >= nEndPos )
+ {
+ m_nChgPos = nEndPos;
+ m_nAttrPos = nSavePos;
+
+ if( RES_TXTATR_CHARFMT == pHt->Which() )
+ {
+ const sal_uInt16 nWId = GetWhichOfScript( RES_CHRATR_LANGUAGE, m_aScriptIter.GetCurrScript() );
+ m_pCurrentItem = &pHt->GetCharFormat().GetCharFormat()->GetFormatAttr(nWId);
+ }
+ else
+ m_pCurrentItem = &pHt->GetAttr();
+
+ m_aStack.pop_front();
+ }
+ }
+ }
+ else
+ SearchNextChg();
+ }
+ return bRet;
+}
+
+void SwLanguageIterator::AddToStack( const SwTextAttr& rAttr )
+{
+ size_t nIns = 0;
+ const sal_Int32 nEndPos = *rAttr.End();
+ for( ; nIns < m_aStack.size(); ++nIns )
+ if( *m_aStack[ nIns ]->End() > nEndPos )
+ break;
+
+ m_aStack.insert( m_aStack.begin() + nIns, &rAttr );
+}
+
+void SwLanguageIterator::SearchNextChg()
+{
+ sal_uInt16 nWh = 0;
+ if( m_nChgPos == m_aScriptIter.GetScriptChgPos() )
+ {
+ m_aScriptIter.Next();
+ m_pParaItem = nullptr;
+ m_nAttrPos = 0; // must be restart at the beginning, because
+ // some attributes can start before or inside
+ // the current scripttype!
+ m_aStack.clear();
+ }
+ if( !m_pParaItem )
+ {
+ nWh = GetWhichOfScript( RES_CHRATR_LANGUAGE, m_aScriptIter.GetCurrScript() );
+ m_pParaItem = &m_rTextNode.GetSwAttrSet().Get( nWh );
+ }
+
+ sal_Int32 nStt = m_nChgPos;
+ m_nChgPos = m_aScriptIter.GetScriptChgPos();
+ m_pCurrentItem = m_pParaItem;
+
+ const SwpHints* pHts = m_rTextNode.GetpSwpHints();
+ if( !pHts )
+ return;
+
+ if( !nWh )
+ {
+ nWh = GetWhichOfScript( RES_CHRATR_LANGUAGE, m_aScriptIter.GetCurrScript() );
+ }
+
+ const SfxPoolItem* pItem = nullptr;
+ for( ; m_nAttrPos < pHts->Count(); ++m_nAttrPos )
+ {
+ const SwTextAttr* pHt = pHts->Get( m_nAttrPos );
+ const sal_Int32* pEnd = pHt->End();
+ const sal_Int32 nHtStt = pHt->GetStart();
+ if( nHtStt < nStt && ( !pEnd || *pEnd <= nStt ))
+ continue;
+
+ if( nHtStt >= m_nChgPos )
+ break;
+
+ pItem = CharFormat::GetItem( *pHt, nWh );
+ if ( pItem )
+ {
+ if( nHtStt > nStt )
+ {
+ if( m_nChgPos > nHtStt )
+ m_nChgPos = nHtStt;
+ break;
+ }
+ AddToStack( *pHt );
+ m_pCurrentItem = pItem;
+ if( *pEnd < m_nChgPos )
+ m_nChgPos = *pEnd;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/txtatr2.cxx b/sw/source/core/txtnode/txtatr2.cxx
new file mode 100644
index 000000000..2b51b45ff
--- /dev/null
+++ b/sw/source/core/txtnode/txtatr2.cxx
@@ -0,0 +1,302 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <osl/diagnose.h>
+#include <txtinet.hxx>
+#include <txtatr.hxx>
+#include <fchrfmt.hxx>
+#include <fmtinfmt.hxx>
+#include <charfmt.hxx>
+#include <ndtxt.hxx>
+#include <poolfmt.hxx>
+#include <doc.hxx>
+#include <fmtruby.hxx>
+#include <fmtmeta.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+
+
+SwTextCharFormat::SwTextCharFormat( SwFormatCharFormat& rAttr,
+ sal_Int32 nStt, sal_Int32 nEnd )
+ : SwTextAttr( rAttr, nStt )
+ , SwTextAttrEnd( rAttr, nStt, nEnd )
+ , m_pTextNode( nullptr )
+ , m_nSortNumber( 0 )
+{
+ rAttr.m_pTextAttribute = this;
+ SetCharFormatAttr( true );
+}
+
+SwTextCharFormat::~SwTextCharFormat( )
+{
+}
+
+void SwTextCharFormat::TriggerNodeUpdate(const sw::LegacyModifyHint& rHint)
+{
+ const auto nWhich = rHint.GetWhich();
+ SAL_WARN_IF(
+ !isCHRATR(nWhich) &&
+ RES_OBJECTDYING != nWhich &&
+ RES_ATTRSET_CHG != nWhich &&
+ RES_FMT_CHG != nWhich, "sw.core", "SwTextCharFormat::TriggerNodeUpdate: unknown hint type");
+
+ if(m_pTextNode)
+ {
+ SwUpdateAttr aUpdateAttr(
+ GetStart(),
+ *GetEnd(),
+ nWhich);
+ m_pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aUpdateAttr, &aUpdateAttr));
+ }
+}
+
+bool SwTextCharFormat::GetInfo( SfxPoolItem const & rInfo ) const
+{
+ return RES_AUTOFMT_DOCNODE != rInfo.Which() || !m_pTextNode ||
+ &m_pTextNode->GetNodes() != static_cast<SwAutoFormatGetDocNode const &>(rInfo).pNodes;
+}
+
+SwTextAttrNesting::SwTextAttrNesting( SfxPoolItem & i_rAttr,
+ const sal_Int32 i_nStart, const sal_Int32 i_nEnd )
+ : SwTextAttr( i_rAttr, i_nStart )
+ , SwTextAttrEnd( i_rAttr, i_nStart, i_nEnd )
+{
+ SetDontExpand( true ); // never expand this attribute
+ // lock the expand flag: simple guarantee that nesting will not be
+ // invalidated by expand operations
+ SetLockExpandFlag( true );
+ SetDontExpandStartAttr( true );
+ SetNesting( true );
+}
+
+SwTextAttrNesting::~SwTextAttrNesting()
+{
+}
+
+SwTextINetFormat::SwTextINetFormat( SwFormatINetFormat& rAttr,
+ sal_Int32 nStart, sal_Int32 nEnd )
+ : SwTextAttr( rAttr, nStart )
+ , SwTextAttrNesting( rAttr, nStart, nEnd )
+ , SwClient( nullptr )
+ , m_pTextNode( nullptr )
+ , m_bVisited( false )
+ , m_bVisitedValid( false )
+{
+ rAttr.mpTextAttr = this;
+ SetCharFormatAttr( true );
+}
+
+SwTextINetFormat::~SwTextINetFormat( )
+{
+}
+
+SwCharFormat* SwTextINetFormat::GetCharFormat()
+{
+ const SwFormatINetFormat& rFormat = SwTextAttrEnd::GetINetFormat();
+ SwCharFormat* pRet = nullptr;
+
+ if (!rFormat.GetValue().isEmpty())
+ {
+ SwDoc& rDoc = GetTextNode().GetDoc();
+ if( !IsVisitedValid() )
+ {
+ SetVisited( rDoc.IsVisitedURL( rFormat.GetValue() ) );
+ SetVisitedValid( true );
+ }
+
+ const sal_uInt16 nId = IsVisited() ? rFormat.GetVisitedFormatId() : rFormat.GetINetFormatId();
+ const OUString& rStr = IsVisited() ? rFormat.GetVisitedFormat() : rFormat.GetINetFormat();
+ if (rStr.isEmpty())
+ {
+ OSL_ENSURE( false, "<SwTextINetFormat::GetCharFormat()> - missing character format at hyperlink attribute");
+ }
+
+ // JP 10.02.2000, Bug 72806: don't modify the doc for getting the
+ // correct charstyle.
+ bool bModifiedEnabled = rDoc.getIDocumentState().IsEnableSetModified();
+ rDoc.getIDocumentState().SetEnableSetModified(false);
+
+ pRet = IsPoolUserFormat( nId )
+ ? rDoc.FindCharFormatByName( rStr )
+ : rDoc.getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
+
+ rDoc.getIDocumentState().SetEnableSetModified(bModifiedEnabled);
+ }
+
+ if ( pRet )
+ pRet->Add( this );
+ else
+ EndListeningAll();
+
+ return pRet;
+}
+
+void SwTextINetFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const auto nWhich = pLegacy->GetWhich();
+ OSL_ENSURE(isCHRATR(nWhich) || (RES_OBJECTDYING == nWhich)
+ || (RES_ATTRSET_CHG == nWhich) || (RES_FMT_CHG == nWhich),
+ "SwTextINetFormat::SwClientNotify: unknown hint.");
+ if(!m_pTextNode)
+ return;
+
+ const SwUpdateAttr aUpdateAttr(GetStart(), *GetEnd(), nWhich);
+ m_pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aUpdateAttr, &aUpdateAttr));
+}
+
+bool SwTextINetFormat::GetInfo( SfxPoolItem& rInfo ) const
+{
+ return RES_AUTOFMT_DOCNODE != rInfo.Which() || !m_pTextNode ||
+ &m_pTextNode->GetNodes() != static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes;
+}
+
+bool SwTextINetFormat::IsProtect( ) const
+{
+ return m_pTextNode && m_pTextNode->IsProtect();
+}
+
+SwTextRuby::SwTextRuby( SwFormatRuby& rAttr,
+ sal_Int32 nStart, sal_Int32 nEnd )
+ : SwTextAttr( rAttr, nStart )
+ , SwTextAttrNesting( rAttr, nStart, nEnd )
+ , SwClient( nullptr )
+ , m_pTextNode( nullptr )
+{
+ rAttr.m_pTextAttr = this;
+}
+
+SwTextRuby::~SwTextRuby()
+{
+}
+
+void SwTextRuby::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ const auto nWhich = pLegacy->GetWhich();
+ SAL_WARN_IF( !isCHRATR(nWhich)
+ && (RES_OBJECTDYING == nWhich)
+ && (RES_ATTRSET_CHG == nWhich)
+ && (RES_FMT_CHG == nWhich), "sw.core", "SwTextRuby::SwClientNotify(): unknown legacy hint");
+ if(!m_pTextNode)
+ return;
+ SwUpdateAttr aUpdateAttr(GetStart(), *GetEnd(), nWhich);
+ m_pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aUpdateAttr, &aUpdateAttr));
+}
+
+bool SwTextRuby::GetInfo( SfxPoolItem& rInfo ) const
+{
+ return RES_AUTOFMT_DOCNODE != rInfo.Which() || !m_pTextNode ||
+ &m_pTextNode->GetNodes() != static_cast<SwAutoFormatGetDocNode&>(rInfo).pNodes;
+}
+
+SwCharFormat* SwTextRuby::GetCharFormat()
+{
+ const SwFormatRuby& rFormat = SwTextAttrEnd::GetRuby();
+ SwCharFormat* pRet = nullptr;
+
+ if( !rFormat.GetText().isEmpty() )
+ {
+ const SwDoc& rDoc = GetTextNode().GetDoc();
+ const OUString& rStr = rFormat.GetCharFormatName();
+ const sal_uInt16 nId = rStr.isEmpty()
+ ? o3tl::narrowing<sal_uInt16>(RES_POOLCHR_RUBYTEXT)
+ : rFormat.GetCharFormatId();
+
+ // JP 10.02.2000, Bug 72806: don't modify the doc for getting the
+ // correct charstyle.
+ const bool bResetMod = !rDoc.getIDocumentState().IsModified();
+ Link<bool,void> aOle2Lnk;
+ if( bResetMod )
+ {
+ aOle2Lnk = rDoc.GetOle2Link();
+ const_cast<SwDoc&>(rDoc).SetOle2Link( Link<bool,void>() );
+ }
+
+ pRet = IsPoolUserFormat( nId )
+ ? rDoc.FindCharFormatByName( rStr )
+ : const_cast<SwDoc&>(rDoc).getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
+
+ if( bResetMod )
+ {
+ const_cast<SwDoc&>(rDoc).getIDocumentState().ResetModified();
+ const_cast<SwDoc&>(rDoc).SetOle2Link( aOle2Lnk );
+ }
+ }
+
+ if( pRet )
+ pRet->Add( this );
+ else
+ EndListeningAll();
+
+ return pRet;
+}
+
+SwTextMeta *
+SwTextMeta::CreateTextMeta(
+ ::sw::MetaFieldManager & i_rTargetDocManager,
+ SwTextNode *const i_pTargetTextNode,
+ SwFormatMeta & i_rAttr,
+ sal_Int32 const i_nStart,
+ sal_Int32 const i_nEnd,
+ bool const i_bIsCopy)
+{
+ if (i_bIsCopy)
+ { // i_rAttr is already cloned, now call DoCopy to copy the sw::Meta
+ OSL_ENSURE(i_pTargetTextNode, "cannot copy Meta without target node");
+ i_rAttr.DoCopy(i_rTargetDocManager, *i_pTargetTextNode);
+ }
+ SwTextMeta *const pTextMeta(new SwTextMeta(i_rAttr, i_nStart, i_nEnd));
+ return pTextMeta;
+}
+
+SwTextMeta::SwTextMeta( SwFormatMeta & i_rAttr,
+ const sal_Int32 i_nStart, const sal_Int32 i_nEnd )
+ : SwTextAttr( i_rAttr, i_nStart )
+ , SwTextAttrNesting( i_rAttr, i_nStart, i_nEnd )
+{
+ i_rAttr.SetTextAttr( this );
+ SetHasDummyChar(true);
+}
+
+SwTextMeta::~SwTextMeta()
+{
+ SwFormatMeta & rFormatMeta( static_cast<SwFormatMeta &>(GetAttr()) );
+ if (rFormatMeta.GetTextAttr() == this)
+ {
+ rFormatMeta.SetTextAttr(nullptr);
+ }
+}
+
+void SwTextMeta::ChgTextNode(SwTextNode * const pNode)
+{
+ SwFormatMeta & rFormatMeta( static_cast<SwFormatMeta &>(GetAttr()) );
+ if (rFormatMeta.GetTextAttr() == this)
+ {
+ rFormatMeta.NotifyChangeTextNode(pNode);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/txtnode/txtedt.cxx b/sw/source/core/txtnode/txtedt.cxx
new file mode 100644
index 000000000..e7dac71ad
--- /dev/null
+++ b/sw/source/core/txtnode/txtedt.cxx
@@ -0,0 +1,2373 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <hintids.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/languageoptions.hxx>
+#include <editeng/splwrap.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/hangulhanja.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <officecfg/Office/Writer.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <sal/log.hxx>
+#include <swmodule.hxx>
+#include <splargs.hxx>
+#include <viewopt.hxx>
+#include <acmplwrd.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docsh.hxx>
+#include <txtfld.hxx>
+#include <txatbase.hxx>
+#include <charatr.hxx>
+#include <pam.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <rootfrm.hxx>
+#include <swscanner.hxx>
+
+#include <breakit.hxx>
+#include <UndoOverwrite.hxx>
+#include <txatritr.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <scriptinfo.hxx>
+#include <docstat.hxx>
+#include <editsh.hxx>
+#include <unotextmarkup.hxx>
+#include <txtatr.hxx>
+#include <fmtautofmt.hxx>
+#include <istyleaccess.hxx>
+#include <unicode/uchar.h>
+#include <DocumentSettingManager.hxx>
+
+#include <com/sun/star/i18n/WordType.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+
+#include <vector>
+#include <utility>
+
+#include <unotextrange.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::smarttags;
+
+namespace
+{
+ void DetectAndMarkMissingDictionaries( SwDoc& rDoc,
+ const uno::Reference< XSpellChecker1 >& xSpell,
+ const LanguageType eActLang )
+ {
+ if( xSpell.is() && !xSpell->hasLanguage( eActLang.get() ) )
+ rDoc.SetMissingDictionaries( true );
+ else
+ rDoc.SetMissingDictionaries( false );
+ }
+}
+
+struct SwParaIdleData_Impl
+{
+ std::unique_ptr<SwWrongList> pWrong; // for spell checking
+ std::unique_ptr<SwGrammarMarkUp> pGrammarCheck; // for grammar checking / proof reading
+ std::unique_ptr<SwWrongList> pSmartTags;
+ sal_uLong nNumberOfWords;
+ sal_uLong nNumberOfAsianWords;
+ sal_uLong nNumberOfChars;
+ sal_uLong nNumberOfCharsExcludingSpaces;
+ bool bWordCountDirty;
+ SwTextNode::WrongState eWrongDirty; ///< online spell checking needed/done?
+ bool bGrammarCheckDirty;
+ bool bSmartTagDirty;
+ bool bAutoComplDirty; ///< auto complete list dirty
+
+ SwParaIdleData_Impl() :
+ nNumberOfWords ( 0 ),
+ nNumberOfAsianWords ( 0 ),
+ nNumberOfChars ( 0 ),
+ nNumberOfCharsExcludingSpaces ( 0 ),
+ bWordCountDirty ( true ),
+ eWrongDirty ( SwTextNode::WrongState::TODO ),
+ bGrammarCheckDirty ( true ),
+ bSmartTagDirty ( true ),
+ bAutoComplDirty ( true ) {};
+};
+
+static bool lcl_HasComments(const SwTextNode& rNode)
+{
+ sal_Int32 nPosition = rNode.GetText().indexOf(CH_TXTATR_INWORD);
+ while (nPosition != -1)
+ {
+ const SwTextAttr* pAttr = rNode.GetTextAttrForCharAt(nPosition);
+ if (pAttr && pAttr->Which() == RES_TXTATR_ANNOTATION)
+ return true;
+ nPosition = rNode.GetText().indexOf(CH_TXTATR_INWORD, nPosition + 1);
+ }
+ return false;
+}
+
+/*
+ * This has basically the same function as SwScriptInfo::MaskHiddenRanges,
+ * only for deleted redlines
+ */
+
+static sal_Int32
+lcl_MaskRedlines( const SwTextNode& rNode, OUStringBuffer& rText,
+ sal_Int32 nStt, sal_Int32 nEnd,
+ const sal_Unicode cChar )
+{
+ sal_Int32 nNumOfMaskedRedlines = 0;
+
+ const SwDoc& rDoc = rNode.GetDoc();
+
+ for ( SwRedlineTable::size_type nAct = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rNode, RedlineType::Any ); nAct < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); ++nAct )
+ {
+ const SwRangeRedline* pRed = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nAct ];
+
+ if ( pRed->Start()->nNode > rNode.GetIndex() )
+ break;
+
+ if( RedlineType::Delete == pRed->GetType() )
+ {
+ sal_Int32 nRedlineEnd;
+ sal_Int32 nRedlineStart;
+
+ pRed->CalcStartEnd( rNode.GetIndex(), nRedlineStart, nRedlineEnd );
+
+ if ( nRedlineEnd < nStt || nRedlineStart > nEnd )
+ continue;
+
+ while ( nRedlineStart < nRedlineEnd && nRedlineStart < nEnd )
+ {
+ if (nRedlineStart >= nStt)
+ {
+ rText[nRedlineStart] = cChar;
+ ++nNumOfMaskedRedlines;
+ }
+ ++nRedlineStart;
+ }
+ }
+ }
+
+ return nNumOfMaskedRedlines;
+}
+
+/**
+ * Used for spell checking. Deleted redlines and hidden characters are masked
+ */
+static bool
+lcl_MaskRedlinesAndHiddenText( const SwTextNode& rNode, OUStringBuffer& rText,
+ sal_Int32 nStt, sal_Int32 nEnd,
+ const sal_Unicode cChar = CH_TXTATR_INWORD )
+{
+ sal_Int32 nRedlinesMasked = 0;
+ sal_Int32 nHiddenCharsMasked = 0;
+
+ const SwDoc& rDoc = rNode.GetDoc();
+ const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+
+ // If called from word count or from spell checking, deleted redlines
+ // should be masked:
+ if ( bShowChg )
+ {
+ nRedlinesMasked = lcl_MaskRedlines( rNode, rText, nStt, nEnd, cChar );
+ }
+
+ const bool bHideHidden = !SW_MOD()->GetViewOption(rDoc.GetDocumentSettingManager().get(DocumentSettingId::HTML_MODE))->IsShowHiddenChar();
+
+ // If called from word count, we want to mask the hidden ranges even
+ // if they are visible:
+ if ( bHideHidden )
+ {
+ nHiddenCharsMasked =
+ SwScriptInfo::MaskHiddenRanges( rNode, rText, nStt, nEnd, cChar );
+ }
+
+ return (nRedlinesMasked > 0) || (nHiddenCharsMasked > 0);
+}
+
+/**
+ * Used for spell checking. Calculates a rectangle for repaint.
+ */
+static SwRect lcl_CalculateRepaintRect(
+ const SwTextFrame & rTextFrame, const SwTextNode & rNode,
+ sal_Int32 const nChgStart, sal_Int32 const nChgEnd)
+{
+ TextFrameIndex const iChgStart(rTextFrame.MapModelToView(&rNode, nChgStart));
+ TextFrameIndex const iChgEnd(rTextFrame.MapModelToView(&rNode, nChgEnd));
+
+ SwRect aRect = rTextFrame.GetPaintArea();
+ SwRect aTmp = rTextFrame.GetPaintArea();
+
+ const SwTextFrame* pStartFrame = &rTextFrame;
+ while( pStartFrame->HasFollow() &&
+ iChgStart >= pStartFrame->GetFollow()->GetOffset())
+ pStartFrame = pStartFrame->GetFollow();
+ const SwTextFrame* pEndFrame = pStartFrame;
+ while( pEndFrame->HasFollow() &&
+ iChgEnd >= pEndFrame->GetFollow()->GetOffset())
+ pEndFrame = pEndFrame->GetFollow();
+
+ bool bSameFrame = true;
+
+ if( rTextFrame.HasFollow() )
+ {
+ if( pEndFrame != pStartFrame )
+ {
+ bSameFrame = false;
+ SwRect aStFrame( pStartFrame->GetPaintArea() );
+ {
+ SwRectFnSet aRectFnSet(pStartFrame);
+ aRectFnSet.SetLeft( aTmp, aRectFnSet.GetLeft(aStFrame) );
+ aRectFnSet.SetRight( aTmp, aRectFnSet.GetRight(aStFrame) );
+ aRectFnSet.SetBottom( aTmp, aRectFnSet.GetBottom(aStFrame) );
+ }
+ aStFrame = pEndFrame->GetPaintArea();
+ {
+ SwRectFnSet aRectFnSet(pEndFrame);
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetTop(aStFrame) );
+ aRectFnSet.SetLeft( aRect, aRectFnSet.GetLeft(aStFrame) );
+ aRectFnSet.SetRight( aRect, aRectFnSet.GetRight(aStFrame) );
+ }
+ aRect.Union( aTmp );
+ while( true )
+ {
+ pStartFrame = pStartFrame->GetFollow();
+ if( pStartFrame == pEndFrame )
+ break;
+ aRect.Union( pStartFrame->GetPaintArea() );
+ }
+ }
+ }
+ if( bSameFrame )
+ {
+ SwRectFnSet aRectFnSet(pStartFrame);
+ if( aRectFnSet.GetTop(aTmp) == aRectFnSet.GetTop(aRect) )
+ aRectFnSet.SetLeft( aRect, aRectFnSet.GetLeft(aTmp) );
+ else
+ {
+ SwRect aStFrame( pStartFrame->GetPaintArea() );
+ aRectFnSet.SetLeft( aRect, aRectFnSet.GetLeft(aStFrame) );
+ aRectFnSet.SetRight( aRect, aRectFnSet.GetRight(aStFrame) );
+ aRectFnSet.SetTop( aRect, aRectFnSet.GetTop(aTmp) );
+ }
+
+ if( aTmp.Height() > aRect.Height() )
+ aRect.Height( aTmp.Height() );
+ }
+
+ return aRect;
+}
+
+/**
+ * Used for automatic styles. Used during RstAttr.
+ */
+static bool lcl_HaveCommonAttributes( IStyleAccess& rStyleAccess,
+ const SfxItemSet* pSet1,
+ sal_uInt16 nWhichId,
+ const SfxItemSet& rSet2,
+ std::shared_ptr<SfxItemSet>& pStyleHandle )
+{
+ bool bRet = false;
+
+ std::unique_ptr<SfxItemSet> pNewSet;
+
+ if ( !pSet1 )
+ {
+ OSL_ENSURE( nWhichId, "lcl_HaveCommonAttributes not used correctly" );
+ if ( SfxItemState::SET == rSet2.GetItemState( nWhichId, false ) )
+ {
+ pNewSet = rSet2.Clone();
+ pNewSet->ClearItem( nWhichId );
+ }
+ }
+ else if ( pSet1->Count() )
+ {
+ SfxItemIter aIter( *pSet1 );
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if ( SfxItemState::SET == rSet2.GetItemState( pItem->Which(), false ) )
+ {
+ if ( !pNewSet )
+ pNewSet = rSet2.Clone();
+ pNewSet->ClearItem( pItem->Which() );
+ }
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+ }
+
+ if ( pNewSet )
+ {
+ if ( pNewSet->Count() )
+ pStyleHandle = rStyleAccess.getAutomaticStyle( *pNewSet, IStyleAccess::AUTO_STYLE_CHAR );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+/** Delete all attributes
+ *
+ * 5 cases:
+ * 1) The attribute is completely in the deletion range:
+ * -> delete it
+ * 2) The end of the attribute is in the deletion range:
+ * -> delete it, then re-insert it with new end
+ * 3) The start of the attribute is in the deletion range:
+ * -> delete it, then re-insert it with new start
+ * 4) The attribute contains the deletion range:
+ * Split, i.e.,
+ * -> Delete, re-insert from old start to start of deletion range
+ * -> insert new attribute from end of deletion range to old end
+ * 5) The attribute is outside the deletion range
+ * -> nothing to do
+ *
+ * @param rIdx starting position
+ * @param nLen length of the deletion
+ * @param nthat ???
+ * @param pSet ???
+ * @param bInclRefToxMark ???
+ */
+
+void SwTextNode::RstTextAttr(
+ const SwIndex &rIdx,
+ const sal_Int32 nLen,
+ const sal_uInt16 nWhich,
+ const SfxItemSet* pSet,
+ const bool bInclRefToxMark,
+ const bool bExactRange )
+{
+ if ( !GetpSwpHints() )
+ return;
+
+ sal_Int32 nStt = rIdx.GetIndex();
+ sal_Int32 nEnd = nStt + nLen;
+ {
+ // enlarge range for the reset of text attributes in case of an overlapping input field
+ const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt( nStt, RES_TXTATR_INPUTFIELD, PARENT ));
+ if ( pTextInputField == nullptr )
+ {
+ pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextAttrAt(nEnd, RES_TXTATR_INPUTFIELD, PARENT ));
+ }
+ if ( pTextInputField != nullptr )
+ {
+ if ( nStt > pTextInputField->GetStart() )
+ {
+ nStt = pTextInputField->GetStart();
+ }
+ if ( nEnd < *(pTextInputField->End()) )
+ {
+ nEnd = *(pTextInputField->End());
+ }
+ }
+ }
+
+ bool bChanged = false;
+
+ // nMin and nMax initialized to maximum / minimum (inverse)
+ sal_Int32 nMin = m_Text.getLength();
+ sal_Int32 nMax = nStt;
+ const bool bNoLen = nMin == 0;
+
+ // We have to remember the "new" attributes that have
+ // been introduced by splitting surrounding attributes (case 2,3,4).
+ std::vector<SwTextAttr *> newAttributes;
+ std::vector<SwTextAttr *> delAttributes;
+
+ // iterate over attribute array until start of attribute is behind deletion range
+ m_pSwpHints->SortIfNeedBe(); // trigger sorting now, we don't want it during iteration
+ size_t i = 0;
+ sal_Int32 nAttrStart = sal_Int32();
+ SwTextAttr *pHt = nullptr;
+ while ( (i < m_pSwpHints->Count())
+ && ( ( ( nAttrStart = m_pSwpHints->GetWithoutResorting(i)->GetStart()) < nEnd )
+ || nLen==0 ) && !bExactRange)
+ {
+ pHt = m_pSwpHints->GetWithoutResorting(i);
+
+ // attributes without end stay in!
+ // but consider <bInclRefToxMark> used by Undo
+ const sal_Int32* const pAttrEnd = pHt->GetEnd();
+ const bool bKeepAttrWithoutEnd =
+ pAttrEnd == nullptr
+ && ( !bInclRefToxMark
+ || ( RES_TXTATR_REFMARK != pHt->Which()
+ && RES_TXTATR_TOXMARK != pHt->Which()
+ && RES_TXTATR_META != pHt->Which()
+ && RES_TXTATR_METAFIELD != pHt->Which() ) );
+ if ( bKeepAttrWithoutEnd )
+ {
+
+ i++;
+ continue;
+ }
+ // attributes with content stay in
+ if ( pHt->HasContent() )
+ {
+ ++i;
+ continue;
+ }
+
+ // Default behavior is to process all attributes:
+ bool bSkipAttr = false;
+ std::shared_ptr<SfxItemSet> pStyleHandle;
+
+ // 1. case: We want to reset only the attributes listed in pSet:
+ if ( pSet )
+ {
+ bSkipAttr = SfxItemState::SET != pSet->GetItemState( pHt->Which(), false );
+ if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() )
+ {
+ // if the current attribute is an autostyle, we have to check if the autostyle
+ // and pSet have any attributes in common. If so, pStyleHandle will contain
+ // a handle to AutoStyle / pSet:
+ bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), pSet, 0, *static_cast<const SwFormatAutoFormat&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle );
+ }
+ }
+ else if ( nWhich )
+ {
+ // 2. case: We want to reset only the attributes with WhichId nWhich:
+ bSkipAttr = nWhich != pHt->Which();
+ if ( bSkipAttr && RES_TXTATR_AUTOFMT == pHt->Which() )
+ {
+ bSkipAttr = !lcl_HaveCommonAttributes( getIDocumentStyleAccess(), nullptr, nWhich, *static_cast<const SwFormatAutoFormat&>(pHt->GetAttr()).GetStyleHandle(), pStyleHandle );
+ }
+ }
+ else if ( !bInclRefToxMark )
+ {
+ // 3. case: Reset all attributes except from ref/toxmarks:
+ // skip hints with CH_TXTATR here
+ // (deleting those is ONLY allowed for UNDO!)
+ bSkipAttr = RES_TXTATR_REFMARK == pHt->Which()
+ || RES_TXTATR_TOXMARK == pHt->Which()
+ || RES_TXTATR_META == pHt->Which()
+ || RES_TXTATR_METAFIELD == pHt->Which();
+ }
+
+ if ( bSkipAttr )
+ {
+ i++;
+ continue;
+ }
+
+ if (nStt <= nAttrStart) // Case: 1,3,5
+ {
+ const sal_Int32 nAttrEnd = pAttrEnd != nullptr
+ ? *pAttrEnd
+ : nAttrStart;
+ if (nEnd > nAttrStart
+ || (nEnd == nAttrEnd && nEnd == nAttrStart)) // Case: 1,3
+ {
+ if ( nMin > nAttrStart )
+ nMin = nAttrStart;
+ if ( nMax < nAttrEnd )
+ nMax = nAttrEnd;
+ // If only a no-extent hint is deleted, no resorting is needed
+ bChanged = bChanged || nEnd > nAttrStart || bNoLen;
+ if (nAttrEnd <= nEnd) // Case: 1
+ {
+ delAttributes.push_back(pHt);
+
+ if ( pStyleHandle )
+ {
+ SwTextAttr* pNew = MakeTextAttr( GetDoc(),
+ *pStyleHandle, nAttrStart, nAttrEnd );
+ newAttributes.push_back(pNew);
+ }
+ }
+ else // Case: 3
+ {
+ bChanged = true;
+ m_pSwpHints->NoteInHistory( pHt );
+ // UGLY: this may temporarily destroy the sorting!
+ pHt->SetStart(nEnd);
+ m_pSwpHints->NoteInHistory( pHt, true );
+
+ if ( pStyleHandle && nAttrStart < nEnd )
+ {
+ SwTextAttr* pNew = MakeTextAttr( GetDoc(),
+ *pStyleHandle, nAttrStart, nEnd );
+ newAttributes.push_back(pNew);
+ }
+ }
+ }
+ }
+ else if (pAttrEnd != nullptr) // Case: 2,4,5
+ {
+ if (*pAttrEnd > nStt) // Case: 2,4
+ {
+ if (*pAttrEnd < nEnd) // Case: 2
+ {
+ if ( nMin > nAttrStart )
+ nMin = nAttrStart;
+ if ( nMax < *pAttrEnd )
+ nMax = *pAttrEnd;
+ bChanged = true;
+
+ const sal_Int32 nAttrEnd = *pAttrEnd;
+
+ m_pSwpHints->NoteInHistory( pHt );
+ // UGLY: this may temporarily destroy the sorting!
+ pHt->SetEnd(nStt);
+ m_pSwpHints->NoteInHistory( pHt, true );
+
+ if ( pStyleHandle )
+ {
+ SwTextAttr* pNew = MakeTextAttr( GetDoc(),
+ *pStyleHandle, nStt, nAttrEnd );
+ newAttributes.push_back(pNew);
+ }
+ }
+ else if (nLen) // Case: 4
+ {
+ // for Length 0 both hints would be merged again by
+ // InsertHint, so leave them alone!
+ if ( nMin > nAttrStart )
+ nMin = nAttrStart;
+ if ( nMax < *pAttrEnd )
+ nMax = *pAttrEnd;
+ bChanged = true;
+ const sal_Int32 nTmpEnd = *pAttrEnd;
+ m_pSwpHints->NoteInHistory( pHt );
+ // UGLY: this may temporarily destroy the sorting!
+ pHt->SetEnd(nStt);
+ m_pSwpHints->NoteInHistory( pHt, true );
+
+ if ( pStyleHandle && nStt < nEnd )
+ {
+ SwTextAttr* pNew = MakeTextAttr( GetDoc(),
+ *pStyleHandle, nStt, nEnd );
+ newAttributes.push_back(pNew);
+ }
+
+ if( nEnd < nTmpEnd )
+ {
+ SwTextAttr* pNew = MakeTextAttr( GetDoc(),
+ pHt->GetAttr(), nEnd, nTmpEnd );
+ if ( pNew )
+ {
+ SwTextCharFormat* pCharFormat = dynamic_cast<SwTextCharFormat*>(pHt);
+ if ( pCharFormat )
+ static_txtattr_cast<SwTextCharFormat*>(pNew)->SetSortNumber(pCharFormat->GetSortNumber());
+
+ newAttributes.push_back(pNew);
+ }
+ }
+ }
+ }
+ }
+ ++i;
+ }
+
+ if (bExactRange)
+ {
+ // Only delete the hints which start at nStt and end at nEnd.
+ for (i = 0; i < m_pSwpHints->Count(); ++i)
+ {
+ SwTextAttr* pHint = m_pSwpHints->Get(i);
+ if ( (isTXTATR_WITHEND(pHint->Which()) && RES_TXTATR_AUTOFMT != pHint->Which())
+ || pHint->GetStart() != nStt)
+ continue;
+
+ const sal_Int32* pHintEnd = pHint->GetEnd();
+ if (!pHintEnd || *pHintEnd != nEnd)
+ continue;
+
+ delAttributes.push_back(pHint);
+ }
+ }
+
+ if (bChanged && !delAttributes.empty())
+ { // Delete() calls GetStartOf() - requires sorted hints!
+ m_pSwpHints->Resort();
+ }
+
+ // delay deleting the hints because it re-sorts the hints array
+ for (SwTextAttr *const pDel : delAttributes)
+ {
+ m_pSwpHints->Delete(pDel);
+ DestroyAttr(pDel);
+ }
+
+ // delay inserting the hints because it re-sorts the hints array
+ for (SwTextAttr *const pNew : newAttributes)
+ {
+ InsertHint(pNew, SetAttrMode::NOHINTADJUST);
+ }
+
+ TryDeleteSwpHints();
+
+ if (!bChanged)
+ return;
+
+ if ( HasHints() )
+ { // possibly sometimes Resort would be sufficient, but...
+ m_pSwpHints->MergePortions(*this);
+ }
+
+ // TextFrame's respond to aHint, others to aNew
+ SwUpdateAttr aHint(
+ nMin,
+ nMax,
+ 0);
+
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+ SwFormatChg aNew( GetFormatColl() );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
+}
+
+static sal_Int32 clipIndexBounds(const OUString &rStr, sal_Int32 nPos)
+{
+ if (nPos < 0)
+ return 0;
+ if (nPos > rStr.getLength())
+ return rStr.getLength();
+ return nPos;
+}
+
+// Return current word:
+// Search from left to right, so find the word before nPos.
+// Except if at the start of the paragraph, then return the first word.
+// If the first word consists only of whitespace, return an empty string.
+OUString SwTextFrame::GetCurWord(SwPosition const& rPos) const
+{
+ TextFrameIndex const nPos(MapModelToViewPos(rPos));
+ SwTextNode *const pTextNode(rPos.nNode.GetNode().GetTextNode());
+ assert(pTextNode);
+ OUString const& rText(GetText());
+ assert(sal_Int32(nPos) <= rText.getLength()); // invalid index
+
+ if (rText.isEmpty() || IsHiddenNow())
+ return OUString();
+
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+ const uno::Reference< XBreakIterator > &rxBreak = g_pBreakIt->GetBreakIter();
+ sal_Int16 nWordType = WordType::DICTIONARY_WORD;
+ lang::Locale aLocale( g_pBreakIt->GetLocale(pTextNode->GetLang(rPos.nContent.GetIndex())) );
+ Boundary aBndry =
+ rxBreak->getWordBoundary(rText, sal_Int32(nPos), aLocale, nWordType, true);
+
+ // if no word was found use previous word (if any)
+ if (aBndry.startPos == aBndry.endPos)
+ {
+ aBndry = rxBreak->previousWord(rText, sal_Int32(nPos), aLocale, nWordType);
+ }
+
+ // check if word was found and if it uses a symbol font, if so
+ // enforce returning an empty string
+ if (aBndry.endPos != aBndry.startPos
+ && IsSymbolAt(TextFrameIndex(aBndry.startPos)))
+ {
+ aBndry.endPos = aBndry.startPos;
+ }
+
+ // can have -1 as start/end of bounds not found
+ aBndry.startPos = clipIndexBounds(rText, aBndry.startPos);
+ aBndry.endPos = clipIndexBounds(rText, aBndry.endPos);
+
+ return rText.copy(aBndry.startPos,
+ aBndry.endPos - aBndry.startPos);
+}
+
+SwScanner::SwScanner( const SwTextNode& rNd, const OUString& rText,
+ const LanguageType* pLang, const ModelToViewHelper& rConvMap,
+ sal_uInt16 nType, sal_Int32 nStart, sal_Int32 nEnd, bool bClp )
+ : SwScanner(
+ [&rNd](sal_Int32 const nBegin, sal_uInt16 const nScript, bool const bNoChar)
+ { return rNd.GetLang(nBegin, bNoChar ? 0 : 1, nScript); }
+ , rText, pLang, rConvMap, nType, nStart, nEnd, bClp)
+{
+}
+
+SwScanner::SwScanner(std::function<LanguageType(sal_Int32, sal_Int32, bool)> const& pGetLangOfChar,
+ const OUString& rText, const LanguageType* pLang,
+ const ModelToViewHelper& rConvMap, sal_uInt16 nType, sal_Int32 nStart,
+ sal_Int32 nEnd, bool bClp)
+ : m_pGetLangOfChar(pGetLangOfChar)
+ , m_aPreDashReplacementText(rText)
+ , m_pLanguage(pLang)
+ , m_ModelToView(rConvMap)
+ , m_nLength(0)
+ , m_nOverriddenDashCount(0)
+ , m_nWordType(nType)
+ , m_bClip(bClp)
+{
+ m_nStartPos = m_nBegin = nStart;
+ m_nEndPos = nEnd;
+
+ //MSWord f.e has special emdash and endash behaviour in that they break
+ //words for the purposes of word counting, while a hyphen etc. doesn't.
+
+ //The default configuration treats emdash/endash as a word break, but
+ //additional ones can be added in under tools->options
+ if (m_nWordType == i18n::WordType::WORD_COUNT)
+ {
+ OUString sDashes = officecfg::Office::Writer::WordCount::AdditionalSeparators::get();
+ OUStringBuffer aBuf(m_aPreDashReplacementText);
+ for (sal_Int32 i = m_nStartPos; i < m_nEndPos; ++i)
+ {
+ if (i < 0)
+ continue;
+ sal_Unicode cChar = aBuf[i];
+ if (sDashes.indexOf(cChar) != -1)
+ {
+ aBuf[i] = ' ';
+ ++m_nOverriddenDashCount;
+ }
+ }
+ m_aText = aBuf.makeStringAndClear();
+ }
+ else
+ m_aText = m_aPreDashReplacementText;
+
+ assert(m_aPreDashReplacementText.getLength() == m_aText.getLength());
+
+ if ( m_pLanguage )
+ {
+ m_aCurrentLang = *m_pLanguage;
+ }
+ else
+ {
+ ModelToViewHelper::ModelPosition aModelBeginPos =
+ m_ModelToView.ConvertToModelPosition( m_nBegin );
+ m_aCurrentLang = m_pGetLangOfChar(aModelBeginPos.mnPos, 0, true);
+ }
+}
+
+namespace
+{
+ //fdo#45271 for Asian words count characters instead of words
+ sal_Int32 forceEachAsianCodePointToWord(const OUString &rText, sal_Int32 nBegin, sal_Int32 nLen)
+ {
+ if (nLen > 1)
+ {
+ const uno::Reference< XBreakIterator > &rxBreak = g_pBreakIt->GetBreakIter();
+
+ sal_uInt16 nCurrScript = rxBreak->getScriptType( rText, nBegin );
+
+ sal_Int32 indexUtf16 = nBegin;
+ rText.iterateCodePoints(&indexUtf16);
+
+ //First character is Asian, consider it a word :-(
+ if (nCurrScript == i18n::ScriptType::ASIAN)
+ {
+ nLen = indexUtf16 - nBegin;
+ return nLen;
+ }
+
+ //First character was not Asian, consider appearance of any Asian character
+ //to be the end of the word
+ while (indexUtf16 < nBegin + nLen)
+ {
+ nCurrScript = rxBreak->getScriptType( rText, indexUtf16 );
+ if (nCurrScript == i18n::ScriptType::ASIAN)
+ {
+ nLen = indexUtf16 - nBegin;
+ return nLen;
+ }
+ rText.iterateCodePoints(&indexUtf16);
+ }
+ }
+ return nLen;
+ }
+}
+
+bool SwScanner::NextWord()
+{
+ m_nBegin = m_nBegin + m_nLength;
+ Boundary aBound;
+
+ const CharClass* pCC = &GetAppCharClass();
+ std::optional<CharClass> xLocalCharClass;
+
+ while ( true )
+ {
+ // skip non-letter characters:
+ while (m_nBegin < m_aText.getLength())
+ {
+ if (m_nBegin >= 0 && !u_isspace(m_aText[m_nBegin]))
+ {
+ if ( !m_pLanguage )
+ {
+ const sal_uInt16 nNextScriptType = g_pBreakIt->GetBreakIter()->getScriptType( m_aText, m_nBegin );
+ ModelToViewHelper::ModelPosition aModelBeginPos =
+ m_ModelToView.ConvertToModelPosition( m_nBegin );
+ m_aCurrentLang = m_pGetLangOfChar(aModelBeginPos.mnPos, nNextScriptType, false);
+ }
+
+ if ( m_nWordType != i18n::WordType::WORD_COUNT )
+ {
+ xLocalCharClass.emplace(LanguageTag( g_pBreakIt->GetLocale( m_aCurrentLang ) ));
+ pCC = &*xLocalCharClass;
+ if ( pCC->isLetterNumeric(OUString(m_aText[m_nBegin])) )
+ break;
+ }
+ else
+ break;
+ }
+ ++m_nBegin;
+ }
+
+ if ( m_nBegin >= m_aText.getLength() || m_nBegin >= m_nEndPos )
+ return false;
+
+ // get the word boundaries
+ aBound = g_pBreakIt->GetBreakIter()->getWordBoundary( m_aText, m_nBegin,
+ g_pBreakIt->GetLocale( m_aCurrentLang ), m_nWordType, true );
+ OSL_ENSURE( aBound.endPos >= aBound.startPos, "broken aBound result" );
+
+ // we don't want to include preceding text
+ // to count words in text with mixed script punctuation correctly,
+ // but we want to include preceding symbols (eg. percent sign, section sign,
+ // degree sign defined by dict_word_hu to spell check their affixed forms).
+ if (m_nWordType == i18n::WordType::WORD_COUNT && aBound.startPos < m_nBegin)
+ aBound.startPos = m_nBegin;
+
+ //no word boundaries could be found
+ if(aBound.endPos == aBound.startPos)
+ return false;
+
+ //if a word before is found it has to be searched for the next
+ if(aBound.endPos == m_nBegin)
+ ++m_nBegin;
+ else
+ break;
+ } // end while( true )
+
+ // #i89042, as discussed with HDU: don't evaluate script changes for word count. Use whole word.
+ if ( m_nWordType == i18n::WordType::WORD_COUNT )
+ {
+ m_nBegin = std::max(aBound.startPos, m_nBegin);
+ m_nLength = 0;
+ if (aBound.endPos > m_nBegin)
+ m_nLength = aBound.endPos - m_nBegin;
+ }
+ else
+ {
+ // we have to differentiate between these cases:
+ if ( aBound.startPos <= m_nBegin )
+ {
+ OSL_ENSURE( aBound.endPos >= m_nBegin, "Unexpected aBound result" );
+
+ // restrict boundaries to script boundaries and nEndPos
+ const sal_uInt16 nCurrScript = g_pBreakIt->GetBreakIter()->getScriptType( m_aText, m_nBegin );
+ OUString aTmpWord = m_aText.copy( m_nBegin, aBound.endPos - m_nBegin );
+ const sal_Int32 nScriptEnd = m_nBegin +
+ g_pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
+ const sal_Int32 nEnd = std::min( aBound.endPos, nScriptEnd );
+
+ // restrict word start to last script change position
+ sal_Int32 nScriptBegin = 0;
+ if ( aBound.startPos < m_nBegin )
+ {
+ // search from nBegin backwards until the next script change
+ aTmpWord = m_aText.copy( aBound.startPos,
+ m_nBegin - aBound.startPos + 1 );
+ nScriptBegin = aBound.startPos +
+ g_pBreakIt->GetBreakIter()->beginOfScript( aTmpWord, m_nBegin - aBound.startPos,
+ nCurrScript );
+ }
+
+ m_nBegin = std::max( aBound.startPos, nScriptBegin );
+ m_nLength = nEnd - m_nBegin;
+ }
+ else
+ {
+ const sal_uInt16 nCurrScript = g_pBreakIt->GetBreakIter()->getScriptType( m_aText, aBound.startPos );
+ OUString aTmpWord = m_aText.copy( aBound.startPos,
+ aBound.endPos - aBound.startPos );
+ const sal_Int32 nScriptEnd = aBound.startPos +
+ g_pBreakIt->GetBreakIter()->endOfScript( aTmpWord, 0, nCurrScript );
+ const sal_Int32 nEnd = std::min( aBound.endPos, nScriptEnd );
+ m_nBegin = aBound.startPos;
+ m_nLength = nEnd - m_nBegin;
+ }
+ }
+
+ // optionally clip the result of getWordBoundaries:
+ if ( m_bClip )
+ {
+ aBound.startPos = std::max( aBound.startPos, m_nStartPos );
+ aBound.endPos = std::min( aBound.endPos, m_nEndPos );
+ if (aBound.endPos < aBound.startPos)
+ {
+ m_nBegin = m_nEndPos;
+ m_nLength = 0; // found word is outside of search interval
+ }
+ else
+ {
+ m_nBegin = aBound.startPos;
+ m_nLength = aBound.endPos - m_nBegin;
+ }
+ }
+
+ if( ! m_nLength )
+ return false;
+
+ if ( m_nWordType == i18n::WordType::WORD_COUNT )
+ m_nLength = forceEachAsianCodePointToWord(m_aText, m_nBegin, m_nLength);
+
+ m_aWord = m_aPreDashReplacementText.copy( m_nBegin, m_nLength );
+
+ return true;
+}
+
+// Note: this is a clone of SwTextFrame::AutoSpell_, so keep them in sync when fixing things!
+bool SwTextNode::Spell(SwSpellArgs* pArgs)
+{
+ // modify string according to redline information and hidden text
+ const OUString aOldText( m_Text );
+ OUStringBuffer buf(m_Text);
+ const bool bContainsComments = lcl_HasComments(*this);
+ const bool bRestoreString =
+ lcl_MaskRedlinesAndHiddenText(*this, buf, 0, m_Text.getLength());
+ if (bRestoreString)
+ { // ??? UGLY: is it really necessary to modify m_Text here?
+ m_Text = buf.makeStringAndClear();
+ }
+
+ sal_Int32 nBegin = ( pArgs->pStartNode != this )
+ ? 0
+ : pArgs->pStartIdx->GetIndex();
+
+ sal_Int32 nEnd = ( pArgs->pEndNode != this )
+ ? m_Text.getLength()
+ : pArgs->pEndIdx->GetIndex();
+
+ pArgs->xSpellAlt = nullptr;
+
+ // 4 cases:
+
+ // 1. IsWrongDirty = 0 and GetWrong = 0
+ // Everything is checked and correct
+ // 2. IsWrongDirty = 0 and GetWrong = 1
+ // Everything is checked and errors are identified in the wrong list
+ // 3. IsWrongDirty = 1 and GetWrong = 0
+ // Nothing has been checked
+ // 4. IsWrongDirty = 1 and GetWrong = 1
+ // Text has been checked but there is an invalid range in the wrong list
+
+ // Nothing has to be done for case 1.
+ if ( ( IsWrongDirty() || GetWrong() ) && m_Text.getLength() )
+ {
+ if (nBegin > m_Text.getLength())
+ {
+ nBegin = m_Text.getLength();
+ }
+ if (nEnd > m_Text.getLength())
+ {
+ nEnd = m_Text.getLength();
+ }
+
+ if(!IsWrongDirty())
+ {
+ const sal_Int32 nTemp = GetWrong()->NextWrong( nBegin );
+ if(nTemp > nEnd)
+ {
+ // reset original text
+ if ( bRestoreString )
+ {
+ m_Text = aOldText;
+ }
+ return false;
+ }
+ if(nTemp > nBegin)
+ nBegin = nTemp;
+
+ }
+
+ // In case 2. we pass the wrong list to the scanned, because only
+ // the words in the wrong list have to be checked
+ SwScanner aScanner( *this, m_Text, nullptr, ModelToViewHelper(),
+ WordType::DICTIONARY_WORD,
+ nBegin, nEnd );
+ while( !pArgs->xSpellAlt.is() && aScanner.NextWord() )
+ {
+ const OUString& rWord = aScanner.GetWord();
+
+ // get next language for next word, consider language attributes
+ // within the word
+ LanguageType eActLang = aScanner.GetCurrentLanguage();
+ DetectAndMarkMissingDictionaries( GetTextNode()->GetDoc(), pArgs->xSpeller, eActLang );
+
+ if( rWord.getLength() > 0 && LANGUAGE_NONE != eActLang )
+ {
+ if (pArgs->xSpeller.is())
+ {
+ SvxSpellWrapper::CheckSpellLang( pArgs->xSpeller, eActLang );
+ pArgs->xSpellAlt = pArgs->xSpeller->spell( rWord, static_cast<sal_uInt16>(eActLang),
+ Sequence< PropertyValue >() );
+ }
+ if( pArgs->xSpellAlt.is() )
+ {
+ if ( IsSymbolAt(aScanner.GetBegin()) ||
+ // redlines can leave "in word" character within word,
+ // we must remove them before spell checking
+ // to avoid false alarm
+ ( (bRestoreString || bContainsComments) && pArgs->xSpeller->isValid( rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), ""),
+ static_cast<sal_uInt16>(eActLang), Sequence< PropertyValue >() ) ) )
+ {
+ pArgs->xSpellAlt = nullptr;
+ }
+ else
+ {
+ // make sure the selection build later from the data
+ // below does not include "in word" character to the
+ // left and right in order to preserve those. Therefore
+ // count those "in words" in order to modify the
+ // selection accordingly.
+ const sal_Unicode* pChar = rWord.getStr();
+ sal_Int32 nLeft = 0;
+ while (*pChar++ == CH_TXTATR_INWORD)
+ ++nLeft;
+ pChar = rWord.getLength() ? rWord.getStr() + rWord.getLength() - 1 : nullptr;
+ sal_Int32 nRight = 0;
+ while (pChar && *pChar-- == CH_TXTATR_INWORD)
+ ++nRight;
+
+ pArgs->pStartNode = this;
+ pArgs->pEndNode = this;
+ pArgs->pStartIdx->Assign(this, aScanner.GetEnd() - nRight );
+ pArgs->pEndIdx->Assign(this, aScanner.GetBegin() + nLeft );
+ }
+ }
+ }
+ }
+ }
+
+ // reset original text
+ if ( bRestoreString )
+ {
+ m_Text = aOldText;
+ }
+
+ return pArgs->xSpellAlt.is();
+}
+
+void SwTextNode::SetLanguageAndFont( const SwPaM &rPaM,
+ LanguageType nLang, sal_uInt16 nLangWhichId,
+ const vcl::Font *pFont, sal_uInt16 nFontWhichId )
+{
+ SwEditShell *pEditShell = GetDoc().GetEditShell();
+ if (!pEditShell)
+ return;
+ SfxItemSet aSet(pEditShell->GetAttrPool(), nLangWhichId, nLangWhichId );
+ if (pFont)
+ aSet.MergeRange(nFontWhichId, nFontWhichId); // Keep it sorted
+ aSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
+
+ OSL_ENSURE( pFont, "target font missing?" );
+ if (pFont)
+ {
+ SvxFontItem aFontItem = static_cast<const SvxFontItem&>( aSet.Get( nFontWhichId ) );
+ aFontItem.SetFamilyName( pFont->GetFamilyName());
+ aFontItem.SetFamily( pFont->GetFamilyType());
+ aFontItem.SetStyleName( pFont->GetStyleName());
+ aFontItem.SetPitch( pFont->GetPitch());
+ aFontItem.SetCharSet( pFont->GetCharSet() );
+ aSet.Put( aFontItem );
+ }
+
+ GetDoc().getIDocumentContentOperations().InsertItemSet( rPaM, aSet );
+ // SetAttr( aSet ); <- Does not set language attribute of empty paragraphs correctly,
+ // <- because since there is no selection the flag to garbage
+ // <- collect all attributes is set, and therefore attributes spanned
+ // <- over empty selection are removed.
+
+}
+
+bool SwTextNode::Convert( SwConversionArgs &rArgs )
+{
+ // get range of text within node to be converted
+ // (either all the text or the text within the selection
+ // when the conversion was started)
+ const sal_Int32 nTextBegin = ( rArgs.pStartNode == this )
+ ? std::min(rArgs.pStartIdx->GetIndex(), m_Text.getLength())
+ : 0;
+
+ const sal_Int32 nTextEnd = ( rArgs.pEndNode == this )
+ ? std::min(rArgs.pEndIdx->GetIndex(), m_Text.getLength())
+ : m_Text.getLength();
+
+ rArgs.aConvText.clear();
+
+ // modify string according to redline information and hidden text
+ const OUString aOldText( m_Text );
+ OUStringBuffer buf(m_Text);
+ const bool bRestoreString =
+ lcl_MaskRedlinesAndHiddenText(*this, buf, 0, m_Text.getLength());
+ if (bRestoreString)
+ { // ??? UGLY: is it really necessary to modify m_Text here?
+ m_Text = buf.makeStringAndClear();
+ }
+
+ bool bFound = false;
+ sal_Int32 nBegin = nTextBegin;
+ sal_Int32 nLen = 0;
+ LanguageType nLangFound = LANGUAGE_NONE;
+ if (m_Text.isEmpty())
+ {
+ if (rArgs.bAllowImplicitChangesForNotConvertibleText)
+ {
+ // create SwPaM with mark & point spanning empty paragraph
+ //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
+ SwPaM aCurPaM( *this, 0 );
+
+ SetLanguageAndFont( aCurPaM,
+ rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE,
+ rArgs.pTargetFont, RES_CHRATR_CJK_FONT );
+ }
+ }
+ else
+ {
+ SwLanguageIterator aIter( *this, nBegin );
+
+ // Implicit changes require setting new attributes, which in turn destroys
+ // the attribute sequence on that aIter iterates. We store the necessary
+ // coordinates and apply those changes after iterating through the text.
+ typedef std::pair<sal_Int32, sal_Int32> ImplicitChangesRange;
+ std::vector<ImplicitChangesRange> aImplicitChanges;
+
+ // find non zero length text portion of appropriate language
+ do {
+ nLangFound = aIter.GetLanguage();
+ bool bLangOk = (nLangFound == rArgs.nConvSrcLang) ||
+ (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
+ editeng::HangulHanjaConversion::IsChinese( rArgs.nConvSrcLang ));
+
+ sal_Int32 nChPos = aIter.GetChgPos();
+ // the position at the end of the paragraph is COMPLETE_STRING and
+ // thus must be cut to the end of the actual string.
+ assert(nChPos != -1);
+ if (nChPos == -1 || nChPos == COMPLETE_STRING)
+ {
+ nChPos = m_Text.getLength();
+ }
+
+ nLen = nChPos - nBegin;
+ bFound = bLangOk && nLen > 0;
+ if (!bFound)
+ {
+ // create SwPaM with mark & point spanning the attributed text
+ //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
+ SwPaM aCurPaM( *this, nBegin );
+ aCurPaM.SetMark();
+ aCurPaM.GetPoint()->nContent = nBegin + nLen;
+
+ // check script type of selected text
+ if (SwEditShell *pEditShell = GetDoc().GetEditShell())
+ {
+ pEditShell->Push(); // save current cursor on stack
+ pEditShell->SetSelection( aCurPaM );
+ bool bIsAsianScript = (SvtScriptType::ASIAN == pEditShell->GetScriptType());
+ pEditShell->Pop(SwCursorShell::PopMode::DeleteCurrent); // restore cursor from stack
+
+ if (!bIsAsianScript && rArgs.bAllowImplicitChangesForNotConvertibleText)
+ {
+ // Store for later use
+ aImplicitChanges.emplace_back(nBegin, nBegin+nLen);
+ }
+ }
+ nBegin = nChPos; // start of next language portion
+ }
+ } while (!bFound && aIter.Next()); /* loop while nothing was found and still sth is left to be searched */
+
+ // Apply implicit changes, if any, now that aIter is no longer used
+ for (const auto& rImplicitChange : aImplicitChanges)
+ {
+ SwPaM aPaM( *this, rImplicitChange.first );
+ aPaM.SetMark();
+ aPaM.GetPoint()->nContent = rImplicitChange.second;
+ SetLanguageAndFont( aPaM, rArgs.nConvTargetLang, RES_CHRATR_CJK_LANGUAGE, rArgs.pTargetFont, RES_CHRATR_CJK_FONT );
+ }
+
+ }
+
+ // keep resulting text within selection / range of text to be converted
+ if (nBegin < nTextBegin)
+ nBegin = nTextBegin;
+ if (nBegin + nLen > nTextEnd)
+ nLen = nTextEnd - nBegin;
+ bool bInSelection = nBegin < nTextEnd;
+
+ if (bFound && bInSelection) // convertible text found within selection/range?
+ {
+ OSL_ENSURE( !m_Text.isEmpty(), "convertible text portion missing!" );
+ rArgs.aConvText = m_Text.copy(nBegin, nLen);
+ rArgs.nConvTextLang = nLangFound;
+
+ // position where to start looking in next iteration (after current ends)
+ rArgs.pStartNode = this;
+ rArgs.pStartIdx->Assign(this, nBegin + nLen );
+ // end position (when we have travelled over the whole document)
+ rArgs.pEndNode = this;
+ rArgs.pEndIdx->Assign(this, nBegin );
+ }
+
+ // restore original text
+ if ( bRestoreString )
+ {
+ m_Text = aOldText;
+ }
+
+ return !rArgs.aConvText.isEmpty();
+}
+
+// Note: this is a clone of SwTextNode::Spell, so keep them in sync when fixing things!
+SwRect SwTextFrame::AutoSpell_(SwTextNode & rNode, sal_Int32 nActPos)
+{
+ SwRect aRect;
+ assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
+ SwTextNode *const pNode(&rNode);
+ if (!nActPos)
+ nActPos = COMPLETE_STRING;
+
+ SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
+
+ // modify string according to redline information and hidden text
+ const OUString aOldText( pNode->GetText() );
+ OUStringBuffer buf(pNode->m_Text);
+ const bool bContainsComments = lcl_HasComments(rNode);
+ const bool bRestoreString =
+ lcl_MaskRedlinesAndHiddenText(*pNode, buf, 0, pNode->GetText().getLength());
+ if (bRestoreString)
+ { // ??? UGLY: is it really necessary to modify m_Text here? just for GetLang()?
+ pNode->m_Text = buf.makeStringAndClear();
+ }
+
+ // a change of data indicates that at least one word has been modified
+
+ sal_Int32 nBegin = 0;
+ sal_Int32 nEnd = pNode->GetText().getLength();
+ sal_Int32 nInsertPos = 0;
+ sal_Int32 nChgStart = COMPLETE_STRING;
+ sal_Int32 nChgEnd = 0;
+ sal_Int32 nInvStart = COMPLETE_STRING;
+ sal_Int32 nInvEnd = 0;
+
+ const bool bAddAutoCmpl = pNode->IsAutoCompleteWordDirty() &&
+ SwViewOption::IsAutoCompleteWords();
+
+ if( pNode->GetWrong() )
+ {
+ nBegin = pNode->GetWrong()->GetBeginInv();
+ if( COMPLETE_STRING != nBegin )
+ {
+ nEnd = std::max(pNode->GetWrong()->GetEndInv(), pNode->GetText().getLength());
+ }
+
+ // get word around nBegin, we start at nBegin - 1
+ if ( COMPLETE_STRING != nBegin )
+ {
+ if ( nBegin )
+ --nBegin;
+
+ LanguageType eActLang = pNode->GetLang( nBegin );
+ Boundary aBound =
+ g_pBreakIt->GetBreakIter()->getWordBoundary( pNode->GetText(), nBegin,
+ g_pBreakIt->GetLocale( eActLang ),
+ WordType::DICTIONARY_WORD, true );
+ nBegin = aBound.startPos;
+ }
+
+ // get the position in the wrong list
+ nInsertPos = pNode->GetWrong()->GetWrongPos( nBegin );
+
+ // sometimes we have to skip one entry
+ if( nInsertPos < pNode->GetWrong()->Count() &&
+ nBegin == pNode->GetWrong()->Pos( nInsertPos ) +
+ pNode->GetWrong()->Len( nInsertPos ) )
+ nInsertPos++;
+ }
+
+ bool bFresh = nBegin < nEnd;
+ bool bPending(false);
+
+ if( bFresh )
+ {
+ uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() );
+ SwDoc& rDoc = pNode->GetDoc();
+
+ SwScanner aScanner( *pNode, pNode->GetText(), nullptr, ModelToViewHelper(),
+ WordType::DICTIONARY_WORD, nBegin, nEnd);
+
+ while( aScanner.NextWord() )
+ {
+ const OUString& rWord = aScanner.GetWord();
+ nBegin = aScanner.GetBegin();
+ sal_Int32 nLen = aScanner.GetLen();
+
+ // get next language for next word, consider language attributes
+ // within the word
+ LanguageType eActLang = aScanner.GetCurrentLanguage();
+ DetectAndMarkMissingDictionaries( rDoc, xSpell, eActLang );
+
+ bool bSpell = xSpell.is() && xSpell->hasLanguage( static_cast<sal_uInt16>(eActLang) );
+ if( bSpell && !rWord.isEmpty() )
+ {
+ // check for: bAlter => xHyphWord.is()
+ OSL_ENSURE(!bSpell || xSpell.is(), "NULL pointer");
+ if( !xSpell->isValid( rWord, static_cast<sal_uInt16>(eActLang), Sequence< PropertyValue >() ) &&
+ // redlines can leave "in word" character within word,
+ // we must remove them before spell checking
+ // to avoid false alarm
+ ((!bRestoreString && !bContainsComments) || !xSpell->isValid( rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), ""),
+ static_cast<sal_uInt16>(eActLang), Sequence< PropertyValue >() ) ) )
+ {
+ sal_Int32 nSmartTagStt = nBegin;
+ sal_Int32 nDummy = 1;
+ if ( !pNode->GetSmartTags() || !pNode->GetSmartTags()->InWrongWord( nSmartTagStt, nDummy ) )
+ {
+ if( !pNode->GetWrong() )
+ {
+ pNode->SetWrong( std::make_unique<SwWrongList>( WRONGLIST_SPELL ) );
+ pNode->GetWrong()->SetInvalid( 0, nEnd );
+ }
+ SwWrongList::FreshState const eState(pNode->GetWrong()->Fresh(
+ nChgStart, nChgEnd, nBegin, nLen, nInsertPos, nActPos));
+ switch (eState)
+ {
+ case SwWrongList::FreshState::FRESH:
+ pNode->GetWrong()->Insert(OUString(), nullptr, nBegin, nLen, nInsertPos++);
+ break;
+ case SwWrongList::FreshState::CURSOR:
+ bPending = true;
+ [[fallthrough]]; // to mark as invalid
+ case SwWrongList::FreshState::NOTHING:
+ nInvStart = nBegin;
+ nInvEnd = nBegin + nLen;
+ break;
+ }
+ }
+ }
+ else if( bAddAutoCmpl && rACW.GetMinWordLen() <= rWord.getLength() )
+ {
+ // tdf#119695 only add the word if the cursor position is outside the word
+ // so that the incomplete words are not added as autocomplete candidates
+ bool bCursorOutsideWord = nActPos > nBegin + nLen || nActPos < nBegin;
+ if (bCursorOutsideWord)
+ rACW.InsertWord(rWord, rDoc);
+ }
+ }
+ }
+ }
+
+ // reset original text
+ // i63141 before calling GetCharRect(..) with formatting!
+ if ( bRestoreString )
+ {
+ pNode->m_Text = aOldText;
+ }
+ if( pNode->GetWrong() )
+ {
+ if( bFresh )
+ pNode->GetWrong()->Fresh( nChgStart, nChgEnd,
+ nEnd, 0, nInsertPos, nActPos );
+
+ // Calculate repaint area:
+
+ if( nChgStart < nChgEnd )
+ {
+ aRect = lcl_CalculateRepaintRect(*this, rNode, nChgStart, nChgEnd);
+
+ // fdo#71558 notify misspelled word to accessibility
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ SwViewShell* pViewSh = getRootFrame() ? getRootFrame()->GetCurrShell() : nullptr;
+ if( pViewSh )
+ pViewSh->InvalidateAccessibleParaAttrs( *this );
+#endif
+ }
+
+ pNode->GetWrong()->SetInvalid( nInvStart, nInvEnd );
+ pNode->SetWrongDirty(
+ (COMPLETE_STRING != pNode->GetWrong()->GetBeginInv())
+ ? (bPending
+ ? SwTextNode::WrongState::PENDING
+ : SwTextNode::WrongState::TODO)
+ : SwTextNode::WrongState::DONE);
+ if( !pNode->GetWrong()->Count() && ! pNode->IsWrongDirty() )
+ pNode->ClearWrong();
+ }
+ else
+ pNode->SetWrongDirty(SwTextNode::WrongState::DONE);
+
+ if( bAddAutoCmpl )
+ pNode->SetAutoCompleteWordDirty( false );
+
+ return aRect;
+}
+
+/** Function: SmartTagScan
+
+ Function scans words in current text and checks them in the
+ smarttag libraries. If the check returns true to bounds of the
+ recognized words are stored into a list that is used later for drawing
+ the underline.
+
+ @return SwRect Repaint area
+*/
+SwRect SwTextFrame::SmartTagScan(SwTextNode & rNode)
+{
+ SwRect aRet;
+
+ assert(sw::FrameContainsNode(*this, rNode.GetIndex()));
+ SwTextNode *const pNode = &rNode;
+ const OUString& rText = pNode->GetText();
+
+ // Iterate over language portions
+ SmartTagMgr& rSmartTagMgr = SwSmartTagMgr::Get();
+
+ SwWrongList* pSmartTagList = pNode->GetSmartTags();
+
+ sal_Int32 nBegin = 0;
+ sal_Int32 nEnd = rText.getLength();
+
+ if ( pSmartTagList )
+ {
+ if ( pSmartTagList->GetBeginInv() != COMPLETE_STRING )
+ {
+ nBegin = pSmartTagList->GetBeginInv();
+ nEnd = std::min( pSmartTagList->GetEndInv(), rText.getLength() );
+
+ if ( nBegin < nEnd )
+ {
+ const LanguageType aCurrLang = pNode->GetLang( nBegin );
+ const css::lang::Locale aCurrLocale = g_pBreakIt->GetLocale( aCurrLang );
+ nBegin = g_pBreakIt->GetBreakIter()->beginOfSentence( rText, nBegin, aCurrLocale );
+ nEnd = g_pBreakIt->GetBreakIter()->endOfSentence(rText, nEnd, aCurrLocale);
+ if (nEnd > rText.getLength() || nEnd < 0)
+ nEnd = rText.getLength();
+ }
+ }
+ }
+
+ const sal_uInt16 nNumberOfEntries = pSmartTagList ? pSmartTagList->Count() : 0;
+ sal_uInt16 nNumberOfRemovedEntries = 0;
+ sal_uInt16 nNumberOfInsertedEntries = 0;
+
+ // clear smart tag list between nBegin and nEnd:
+ if ( 0 != nNumberOfEntries )
+ {
+ sal_Int32 nChgStart = COMPLETE_STRING;
+ sal_Int32 nChgEnd = 0;
+ const sal_uInt16 nCurrentIndex = pSmartTagList->GetWrongPos( nBegin );
+ pSmartTagList->Fresh( nChgStart, nChgEnd, nBegin, nEnd - nBegin, nCurrentIndex, COMPLETE_STRING );
+ nNumberOfRemovedEntries = nNumberOfEntries - pSmartTagList->Count();
+ }
+
+ if ( nBegin < nEnd )
+ {
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pNode, getRootFrame() /*TODO - replace or expand fields for smart tags?*/);
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ // Ownership ov ConversionMap is passed to SwXTextMarkup object!
+ uno::Reference<text::XTextMarkup> const xTextMarkup =
+ new SwXTextMarkup(pNode, aConversionMap);
+
+ css::uno::Reference< css::frame::XController > xController = pNode->GetDoc().GetDocShell()->GetController();
+
+ SwPosition start(*pNode, nBegin);
+ SwPosition end (*pNode, nEnd);
+ Reference< css::text::XTextRange > xRange = SwXTextRange::CreateXTextRange(pNode->GetDoc(), start, &end);
+
+ rSmartTagMgr.RecognizeTextRange(xRange, xTextMarkup, xController);
+
+ sal_Int32 nLangBegin = nBegin;
+ sal_Int32 nLangEnd;
+
+ // smart tag recognition has to be done for each language portion:
+ SwLanguageIterator aIter( *pNode, nLangBegin );
+
+ do
+ {
+ const LanguageType nLang = aIter.GetLanguage();
+ const css::lang::Locale aLocale = g_pBreakIt->GetLocale( nLang );
+ nLangEnd = std::min<sal_Int32>( nEnd, aIter.GetChgPos() );
+
+ const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nLangBegin );
+ const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nLangEnd );
+
+ rSmartTagMgr.RecognizeString(aExpandText, xTextMarkup, xController, aLocale, nExpandBegin, nExpandEnd - nExpandBegin );
+
+ nLangBegin = nLangEnd;
+ }
+ while ( aIter.Next() && nLangEnd < nEnd );
+
+ pSmartTagList = pNode->GetSmartTags();
+
+ const sal_uInt16 nNumberOfEntriesAfterRecognize = pSmartTagList ? pSmartTagList->Count() : 0;
+ nNumberOfInsertedEntries = nNumberOfEntriesAfterRecognize - ( nNumberOfEntries - nNumberOfRemovedEntries );
+ }
+
+ if( pSmartTagList )
+ {
+ // Update WrongList stuff
+ pSmartTagList->SetInvalid( COMPLETE_STRING, 0 );
+ pNode->SetSmartTagDirty( COMPLETE_STRING != pSmartTagList->GetBeginInv() );
+
+ if( !pSmartTagList->Count() && !pNode->IsSmartTagDirty() )
+ pNode->ClearSmartTags();
+
+ // Calculate repaint area:
+ if ( nBegin < nEnd && ( 0 != nNumberOfRemovedEntries ||
+ 0 != nNumberOfInsertedEntries ) )
+ {
+ aRet = lcl_CalculateRepaintRect(*this, rNode, nBegin, nEnd);
+ }
+ }
+ else
+ pNode->SetSmartTagDirty( false );
+
+ return aRet;
+}
+
+void SwTextFrame::CollectAutoCmplWrds(SwTextNode & rNode, sal_Int32 nActPos)
+{
+ assert(sw::FrameContainsNode(*this, rNode.GetIndex())); (void) this;
+ SwTextNode *const pNode(&rNode);
+ if (!nActPos)
+ nActPos = COMPLETE_STRING;
+
+ SwDoc& rDoc = pNode->GetDoc();
+ SwAutoCompleteWord& rACW = SwDoc::GetAutoCompleteWords();
+
+ sal_Int32 nBegin = 0;
+ sal_Int32 nEnd = pNode->GetText().getLength();
+ sal_Int32 nLen;
+ bool bACWDirty = false;
+
+ if( nBegin < nEnd )
+ {
+ int nCnt = 200;
+ SwScanner aScanner( *pNode, pNode->GetText(), nullptr, ModelToViewHelper(),
+ WordType::DICTIONARY_WORD, nBegin, nEnd );
+ while( aScanner.NextWord() )
+ {
+ nBegin = aScanner.GetBegin();
+ nLen = aScanner.GetLen();
+ if( rACW.GetMinWordLen() <= nLen )
+ {
+ const OUString& rWord = aScanner.GetWord();
+
+ if( nActPos < nBegin || ( nBegin + nLen ) < nActPos )
+ {
+ if( rACW.GetMinWordLen() <= rWord.getLength() )
+ rACW.InsertWord( rWord, rDoc );
+ }
+ else
+ bACWDirty = true;
+ }
+ if( !--nCnt )
+ {
+ // don't wait for TIMER here, so we can finish big paragraphs
+ if (Application::AnyInput(VCL_INPUT_ANY & VclInputFlags(~VclInputFlags::TIMER)))
+ return;
+ nCnt = 100;
+ }
+ }
+ }
+
+ if (!bACWDirty)
+ pNode->SetAutoCompleteWordDirty( false );
+}
+
+SwInterHyphInfoTextFrame::SwInterHyphInfoTextFrame(
+ SwTextFrame const& rFrame, SwTextNode const& rNode,
+ SwInterHyphInfo const& rHyphInfo)
+ : m_nStart(rFrame.MapModelToView(&rNode, rHyphInfo.m_nStart))
+ , m_nEnd(rFrame.MapModelToView(&rNode, rHyphInfo.m_nEnd))
+ , m_nWordStart(0)
+ , m_nWordLen(0)
+{
+}
+
+void SwInterHyphInfoTextFrame::UpdateTextNodeHyphInfo(SwTextFrame const& rFrame,
+ SwTextNode const& rNode, SwInterHyphInfo & o_rHyphInfo)
+{
+ std::pair<SwTextNode const*, sal_Int32> const wordStart(rFrame.MapViewToModel(m_nWordStart));
+ std::pair<SwTextNode const*, sal_Int32> const wordEnd(rFrame.MapViewToModel(m_nWordStart+m_nWordLen));
+ if (wordStart.first != &rNode || wordEnd.first != &rNode)
+ { // not sure if this can happen since nStart/nEnd are in rNode
+ SAL_WARN("sw.core", "UpdateTextNodeHyphInfo: outside of node");
+ return;
+ }
+ o_rHyphInfo.m_nWordStart = wordStart.second;
+ o_rHyphInfo.m_nWordLen = wordEnd.second - wordStart.second;
+ o_rHyphInfo.SetHyphWord(m_xHyphWord);
+}
+
+/// Find the SwTextFrame and call its Hyphenate
+bool SwTextNode::Hyphenate( SwInterHyphInfo &rHyphInf )
+{
+ // shortcut: paragraph doesn't have a language set:
+ if ( LANGUAGE_NONE == GetSwAttrSet().GetLanguage().GetLanguage()
+ && LanguageType(USHRT_MAX) == GetLang(0, m_Text.getLength()))
+ {
+ return false;
+ }
+
+ SwTextFrame *pFrame = ::sw::SwHyphIterCacheLastTextFrame(this,
+ [&rHyphInf, this]() {
+ std::pair<Point, bool> tmp;
+ Point const*const pPoint = rHyphInf.GetCursorPos();
+ if (pPoint)
+ {
+ tmp.first = *pPoint;
+ tmp.second = true;
+ }
+ return static_cast<SwTextFrame*>(this->getLayoutFrame(
+ this->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, pPoint ? &tmp : nullptr));
+ });
+ if (!pFrame)
+ {
+ // There was a comment here that claimed that the following assertion
+ // shouldn't exist as it's triggered by "Trennung ueber Sonderbereiche",
+ // (hyphenation across special sections?), whatever that means.
+ OSL_ENSURE( pFrame, "!SwTextNode::Hyphenate: can't find any frame" );
+ return false;
+ }
+ SwInterHyphInfoTextFrame aHyphInfo(*pFrame, *this, rHyphInf);
+
+ pFrame = &(pFrame->GetFrameAtOfst( aHyphInfo.m_nStart ));
+
+ while( pFrame )
+ {
+ if (pFrame->Hyphenate(aHyphInfo))
+ {
+ // The layout is not robust wrt. "direct formatting"
+ // cf. layact.cxx, SwLayAction::TurboAction_(), if( !pCnt->IsValid() ...
+ pFrame->SetCompletePaint();
+ aHyphInfo.UpdateTextNodeHyphInfo(*pFrame, *this, rHyphInf);
+ return true;
+ }
+ pFrame = pFrame->GetFollow();
+ if( pFrame )
+ {
+ aHyphInfo.m_nEnd = aHyphInfo.m_nEnd - (pFrame->GetOffset() - aHyphInfo.m_nStart);
+ aHyphInfo.m_nStart = pFrame->GetOffset();
+ }
+ }
+ return false;
+}
+
+namespace
+{
+ struct swTransliterationChgData
+ {
+ sal_Int32 nStart;
+ sal_Int32 nLen;
+ OUString sChanged;
+ Sequence< sal_Int32 > aOffsets;
+ };
+}
+
+// change text to Upper/Lower/Hiragana/Katakana/...
+void SwTextNode::TransliterateText(
+ utl::TransliterationWrapper& rTrans,
+ sal_Int32 nStt, sal_Int32 nEnd,
+ SwUndoTransliterate* pUndo, bool bUseRedlining )
+{
+ if (nStt >= nEnd)
+ return;
+
+ const sal_Int32 selStart = nStt;
+ const sal_Int32 selEnd = nEnd;
+
+ // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
+ // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
+ // occasionally miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
+ // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
+ // proper thing to do.
+ const sal_Int16 nWordType = WordType::ANYWORD_IGNOREWHITESPACES;
+
+ // In order to have less trouble with changing text size, e.g. because
+ // of ligatures or German small sz being resolved, we need to process
+ // the text replacements from end to start.
+ // This way the offsets for the yet to be changed words will be
+ // left unchanged by the already replaced text.
+ // For this we temporarily save the changes to be done in this vector
+ std::vector< swTransliterationChgData > aChanges;
+ swTransliterationChgData aChgData;
+
+ if (rTrans.getType() == TransliterationFlags::TITLE_CASE)
+ {
+ // for 'capitalize every word' we need to iterate over each word
+
+ Boundary aSttBndry;
+ Boundary aEndBndry;
+ aSttBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ GetText(), nStt,
+ g_pBreakIt->GetLocale( GetLang( nStt ) ),
+ nWordType,
+ true /*prefer forward direction*/);
+ aEndBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
+ GetText(), nEnd,
+ g_pBreakIt->GetLocale( GetLang( nEnd ) ),
+ nWordType,
+ false /*prefer backward direction*/);
+
+ // prevent backtracking to the previous word if selection is at word boundary
+ if (aSttBndry.endPos <= nStt)
+ {
+ aSttBndry = g_pBreakIt->GetBreakIter()->nextWord(
+ GetText(), aSttBndry.endPos,
+ g_pBreakIt->GetLocale( GetLang( aSttBndry.endPos ) ),
+ nWordType);
+ }
+ // prevent advancing to the next word if selection is at word boundary
+ if (aEndBndry.startPos >= nEnd)
+ {
+ aEndBndry = g_pBreakIt->GetBreakIter()->previousWord(
+ GetText(), aEndBndry.startPos,
+ g_pBreakIt->GetLocale( GetLang( aEndBndry.startPos ) ),
+ nWordType);
+ }
+
+ /* Nothing to do if user selection lies entirely outside of word start and end boundary computed above.
+ * Skip this node, because otherwise the below logic for constraining to the selection will fail */
+ if (aSttBndry.startPos >= selEnd || aEndBndry.endPos <= selStart) {
+ return;
+ }
+
+ // prevent going outside of the user's selection, which may
+ // start in the middle of a word
+ aSttBndry.startPos = std::max(aSttBndry.startPos, selStart);
+ aEndBndry.startPos = std::max(aSttBndry.startPos, aEndBndry.startPos);
+
+ Boundary aCurWordBndry( aSttBndry );
+ while (aCurWordBndry.startPos <= aEndBndry.startPos)
+ {
+ nStt = aCurWordBndry.startPos;
+ nEnd = aCurWordBndry.endPos;
+ const sal_Int32 nLen = nEnd - nStt;
+ OSL_ENSURE( nLen > 0, "invalid word length of 0" );
+
+ Sequence <sal_Int32> aOffsets;
+ OUString const sChgd( rTrans.transliterate(
+ GetText(), GetLang(nStt), nStt, nLen, &aOffsets) );
+
+ assert(nStt < m_Text.getLength());
+ if (0 != rtl_ustr_shortenedCompare_WithLength(
+ m_Text.getStr() + nStt, m_Text.getLength() - nStt,
+ sChgd.getStr(), sChgd.getLength(), nLen))
+ {
+ aChgData.nStart = nStt;
+ aChgData.nLen = nLen;
+ aChgData.sChanged = sChgd;
+ aChgData.aOffsets = aOffsets;
+ aChanges.push_back( aChgData );
+ }
+
+ aCurWordBndry = g_pBreakIt->GetBreakIter()->nextWord(
+ GetText(), nStt,
+ g_pBreakIt->GetLocale(GetLang(nStt, 1)),
+ nWordType);
+ }
+ }
+ else if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE)
+ {
+ // For 'sentence case' we need to iterate sentence by sentence.
+ // nLastStart and nLastEnd are the boundaries of the last sentence in
+ // the user's selection.
+ sal_Int32 nLastStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ GetText(), nEnd,
+ g_pBreakIt->GetLocale( GetLang( nEnd ) ) );
+ sal_Int32 nLastEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
+ GetText(), nLastStart,
+ g_pBreakIt->GetLocale( GetLang( nLastStart ) ) );
+
+ // Begin with the starting point of the user's selection (it may not be
+ // the beginning of a sentence)...
+ sal_Int32 nCurrentStart = nStt;
+ // ...And extend to the end of the first sentence
+ sal_Int32 nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
+ GetText(), nCurrentStart,
+ g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
+
+ // prevent backtracking to the previous sentence if selection starts at end of a sentence
+ if (nCurrentEnd <= nStt)
+ {
+ // now nCurrentStart is probably located on a non-letter word. (unless we
+ // are in Asian text with no spaces...)
+ // Thus to get the real sentence start we should locate the next real word,
+ // that is one found by DICTIONARY_WORD
+ i18n::Boundary aBndry = g_pBreakIt->GetBreakIter()->nextWord(
+ GetText(), nCurrentEnd,
+ g_pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
+ i18n::WordType::DICTIONARY_WORD);
+
+ // now get new current sentence boundaries
+ nCurrentStart = g_pBreakIt->GetBreakIter()->beginOfSentence(
+ GetText(), aBndry.startPos,
+ g_pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
+ nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
+ GetText(), nCurrentStart,
+ g_pBreakIt->GetLocale( GetLang( nCurrentStart) ) );
+ }
+ // prevent advancing to the next sentence if selection ends at start of a sentence
+ if (nLastStart >= nEnd)
+ {
+ // now nCurrentStart is probably located on a non-letter word. (unless we
+ // are in Asian text with no spaces...)
+ // Thus to get the real sentence start we should locate the previous real word,
+ // that is one found by DICTIONARY_WORD
+ i18n::Boundary aBndry = g_pBreakIt->GetBreakIter()->previousWord(
+ GetText(), nLastStart,
+ g_pBreakIt->GetLocale( GetLang( nLastStart) ),
+ i18n::WordType::DICTIONARY_WORD);
+ nLastEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
+ GetText(), aBndry.startPos,
+ g_pBreakIt->GetLocale( GetLang( aBndry.startPos) ) );
+ if (nCurrentEnd > nLastEnd)
+ nCurrentEnd = nLastEnd;
+ }
+
+ // Prevent going outside of the user's selection
+ nCurrentStart = std::max(selStart, nCurrentStart);
+ nCurrentEnd = std::min(selEnd, nCurrentEnd);
+ nLastEnd = std::min(selEnd, nLastEnd);
+
+ while (nCurrentStart < nLastEnd)
+ {
+ sal_Int32 nLen = nCurrentEnd - nCurrentStart;
+ OSL_ENSURE( nLen > 0, "invalid word length of 0" );
+
+ Sequence <sal_Int32> aOffsets;
+ OUString const sChgd( rTrans.transliterate(GetText(),
+ GetLang(nCurrentStart), nCurrentStart, nLen, &aOffsets) );
+
+ assert(nStt < m_Text.getLength());
+ if (0 != rtl_ustr_shortenedCompare_WithLength(
+ m_Text.getStr() + nStt, m_Text.getLength() - nStt,
+ sChgd.getStr(), sChgd.getLength(), nLen))
+ {
+ aChgData.nStart = nCurrentStart;
+ aChgData.nLen = nLen;
+ aChgData.sChanged = sChgd;
+ aChgData.aOffsets = aOffsets;
+ aChanges.push_back( aChgData );
+ }
+
+ Boundary aFirstWordBndry = g_pBreakIt->GetBreakIter()->nextWord(
+ GetText(), nCurrentEnd,
+ g_pBreakIt->GetLocale( GetLang( nCurrentEnd ) ),
+ nWordType);
+ nCurrentStart = aFirstWordBndry.startPos;
+ nCurrentEnd = g_pBreakIt->GetBreakIter()->endOfSentence(
+ GetText(), nCurrentStart,
+ g_pBreakIt->GetLocale( GetLang( nCurrentStart ) ) );
+ }
+ }
+ else
+ {
+ // here we may transliterate over complete language portions...
+
+ std::unique_ptr<SwLanguageIterator> pIter;
+ if( rTrans.needLanguageForTheMode() )
+ pIter.reset(new SwLanguageIterator( *this, nStt ));
+
+ sal_Int32 nEndPos = 0;
+ LanguageType nLang = LANGUAGE_NONE;
+ do {
+ if( pIter )
+ {
+ nLang = pIter->GetLanguage();
+ nEndPos = pIter->GetChgPos();
+ if( nEndPos > nEnd )
+ nEndPos = nEnd;
+ }
+ else
+ {
+ nLang = LANGUAGE_SYSTEM;
+ nEndPos = nEnd;
+ }
+ const sal_Int32 nLen = nEndPos - nStt;
+
+ Sequence <sal_Int32> aOffsets;
+ OUString const sChgd( rTrans.transliterate(
+ m_Text, nLang, nStt, nLen, &aOffsets) );
+
+ assert(nStt < m_Text.getLength());
+ if (0 != rtl_ustr_shortenedCompare_WithLength(
+ m_Text.getStr() + nStt, m_Text.getLength() - nStt,
+ sChgd.getStr(), sChgd.getLength(), nLen))
+ {
+ aChgData.nStart = nStt;
+ aChgData.nLen = nLen;
+ aChgData.sChanged = sChgd;
+ aChgData.aOffsets = aOffsets;
+ aChanges.push_back( aChgData );
+ }
+
+ nStt = nEndPos;
+ } while( nEndPos < nEnd && pIter && pIter->Next() );
+ }
+
+ if (aChanges.empty())
+ return;
+
+ // now apply the changes from end to start to leave the offsets of the
+ // yet unchanged text parts remain the same.
+ size_t nSum(0);
+
+ for (size_t i = 0; i < aChanges.size(); ++i)
+ { // check this here since AddChanges cannot be moved below
+ // call to ReplaceTextOnly
+ swTransliterationChgData & rData =
+ aChanges[ aChanges.size() - 1 - i ];
+
+ nSum += rData.sChanged.getLength() - rData.nLen;
+ if (nSum > o3tl::make_unsigned(GetSpaceLeft()))
+ {
+ SAL_WARN("sw.core", "SwTextNode::ReplaceTextOnly: "
+ "node text with insertion > node capacity.");
+ return;
+ }
+
+ if ( bUseRedlining )
+ {
+ // create SwPaM with mark & point spanning the attributed text
+ //SwPaM aCurPaM( *this, *this, nBegin, nBegin + nLen ); <-- wrong c-tor, does sth different
+ SwPaM aCurPaM( *this, rData.nStart );
+ aCurPaM.SetMark();
+ aCurPaM.GetPoint()->nContent = rData.nStart + rData.nLen;
+ // replace the changed words
+ if ( aCurPaM.GetText() != rData.sChanged )
+ GetDoc().getIDocumentContentOperations().ReplaceRange( aCurPaM, rData.sChanged, false );
+ }
+ else
+ {
+ if (pUndo)
+ pUndo->AddChanges( *this, rData.nStart, rData.nLen, rData.aOffsets );
+ ReplaceTextOnly( rData.nStart, rData.nLen, rData.sChanged, rData.aOffsets );
+ }
+ }
+}
+
+void SwTextNode::ReplaceTextOnly( sal_Int32 nPos, sal_Int32 nLen,
+ const OUString & rText,
+ const Sequence<sal_Int32>& rOffsets )
+{
+ assert(rText.getLength() - nLen <= GetSpaceLeft());
+
+ m_Text = m_Text.replaceAt(nPos, nLen, rText);
+
+ sal_Int32 nTLen = rText.getLength();
+ const sal_Int32* pOffsets = rOffsets.getConstArray();
+ // now look for no 1-1 mapping -> move the indices!
+ sal_Int32 nMyOff = nPos;
+ for( sal_Int32 nI = 0; nI < nTLen; ++nI )
+ {
+ const sal_Int32 nOff = pOffsets[ nI ];
+ if( nOff < nMyOff )
+ {
+ // something is inserted
+ sal_Int32 nCnt = 1;
+ while( nI + nCnt < nTLen && nOff == pOffsets[ nI + nCnt ] )
+ ++nCnt;
+
+ Update( SwIndex( this, nMyOff ), nCnt );
+ nMyOff = nOff;
+ //nMyOff -= nCnt;
+ nI += nCnt - 1;
+ }
+ else if( nOff > nMyOff )
+ {
+ // something is deleted
+ Update( SwIndex( this, nMyOff+1 ), nOff - nMyOff, true );
+ nMyOff = nOff;
+ }
+ ++nMyOff;
+ }
+ if( nMyOff < nLen )
+ // something is deleted at the end
+ Update( SwIndex( this, nMyOff ), nLen - nMyOff, true );
+
+ // notify the layout!
+ SwDelText aDelHint( nPos, nTLen );
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aDelHint));
+
+ SwInsText const aHint(sw::MakeSwInsText(*this, nPos, nTLen));
+ CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aHint));
+}
+
+// the return values allows us to see if we did the heavy-
+// lifting required to actually break and count the words.
+bool SwTextNode::CountWords( SwDocStat& rStat,
+ sal_Int32 nStt, sal_Int32 nEnd ) const
+{
+ if( nStt > nEnd )
+ { // bad call
+ return false;
+ }
+ if (IsInRedlines())
+ { //not counting txtnodes used to hold deleted redline content
+ return false;
+ }
+ bool bCountAll = ( (0 == nStt) && (GetText().getLength() == nEnd) );
+ ++rStat.nAllPara; // #i93174#: count _all_ paragraphs
+ if ( IsHidden() )
+ { // not counting hidden paras
+ return false;
+ }
+ // count words in numbering string if started at beginning of para:
+ bool bCountNumbering = nStt == 0;
+ bool bHasBullet = false, bHasNumbering = false;
+ OUString sNumString;
+ if (bCountNumbering)
+ {
+ sNumString = GetNumString();
+ bHasNumbering = !sNumString.isEmpty();
+ if (!bHasNumbering)
+ bHasBullet = HasBullet();
+ bCountNumbering = bHasNumbering || bHasBullet;
+ }
+
+ if( nStt == nEnd && !bCountNumbering)
+ { // unnumbered empty node or empty selection
+ if (bCountAll)
+ {
+ SetWordCountDirty( false ); // reset flag to speed up DoIdleJob
+ }
+ return false;
+ }
+
+ // count of non-empty paras
+ ++rStat.nPara;
+
+ // Shortcut when counting whole paragraph and current count is clean
+ if ( bCountAll && !IsWordCountDirty() )
+ {
+ // accumulate into DocStat record to return the values
+ if (m_pParaIdleData_Impl)
+ {
+ rStat.nWord += m_pParaIdleData_Impl->nNumberOfWords;
+ rStat.nAsianWord += m_pParaIdleData_Impl->nNumberOfAsianWords;
+ rStat.nChar += m_pParaIdleData_Impl->nNumberOfChars;
+ rStat.nCharExcludingSpaces += m_pParaIdleData_Impl->nNumberOfCharsExcludingSpaces;
+ }
+ return false;
+ }
+
+ // ConversionMap to expand fields, remove invisible and redline deleted text for scanner
+ const ModelToViewHelper aConversionMap(*this,
+ getIDocumentLayoutAccess().GetCurrentLayout(),
+ ExpandMode::ExpandFields | ExpandMode::ExpandFootnote | ExpandMode::HideInvisible | ExpandMode::HideDeletions | ExpandMode::HideFieldmarkCommands);
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ if (aExpandText.isEmpty() && !bCountNumbering)
+ {
+ if (bCountAll)
+ {
+ SetWordCountDirty( false ); // reset flag to speed up DoIdleJob
+ }
+ return false;
+ }
+
+ // map start and end points onto the ConversionMap
+ const sal_Int32 nExpandBegin = aConversionMap.ConvertToViewPosition( nStt );
+ const sal_Int32 nExpandEnd = aConversionMap.ConvertToViewPosition( nEnd );
+
+ //do the count
+ // all counts exclude hidden paras and hidden+redlined within para
+ // definition of space/white chars in SwScanner (and BreakIter!)
+ // uses both u_isspace and BreakIter getWordBoundary in SwScanner
+ sal_uInt32 nTmpWords = 0; // count of all words
+ sal_uInt32 nTmpAsianWords = 0; //count of all Asian codepoints
+ sal_uInt32 nTmpChars = 0; // count of all chars
+ sal_uInt32 nTmpCharsExcludingSpaces = 0; // all non-white chars
+
+ // count words in masked and expanded text:
+ if (!aExpandText.isEmpty())
+ {
+ assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is());
+
+ // zero is NULL for pLanguage -----------v last param = true for clipping
+ SwScanner aScanner( *this, aExpandText, nullptr, aConversionMap, i18n::WordType::WORD_COUNT,
+ nExpandBegin, nExpandEnd, true );
+
+ // used to filter out scanner returning almost empty strings (len=1; unichar=0x0001)
+ const OUString aBreakWord( CH_TXTATR_BREAKWORD );
+
+ while ( aScanner.NextWord() )
+ {
+ if( !aExpandText.match(aBreakWord, aScanner.GetBegin() ))
+ {
+ ++nTmpWords;
+ const OUString &rWord = aScanner.GetWord();
+ if (g_pBreakIt->GetBreakIter()->getScriptType(rWord, 0) == i18n::ScriptType::ASIAN)
+ ++nTmpAsianWords;
+ nTmpCharsExcludingSpaces += g_pBreakIt->getGraphemeCount(rWord);
+ }
+ }
+
+ nTmpCharsExcludingSpaces += aScanner.getOverriddenDashCount();
+
+ nTmpChars = g_pBreakIt->getGraphemeCount(aExpandText, nExpandBegin, nExpandEnd);
+ }
+
+ // no nTmpCharsExcludingSpaces adjust needed neither for blanked out MaskedChars
+ // nor for mid-word selection - set scanner bClip = true at creation
+
+ // count outline number label - ? no expansion into map
+ // always counts all of number-ish label
+ if (bHasNumbering) // count words in numbering string
+ {
+ LanguageType aLanguage = GetLang( 0 );
+
+ SwScanner aScanner( *this, sNumString, &aLanguage, ModelToViewHelper(),
+ i18n::WordType::WORD_COUNT, 0, sNumString.getLength(), true );
+
+ while ( aScanner.NextWord() )
+ {
+ ++nTmpWords;
+ const OUString &rWord = aScanner.GetWord();
+ if (g_pBreakIt->GetBreakIter()->getScriptType(rWord, 0) == i18n::ScriptType::ASIAN)
+ ++nTmpAsianWords;
+ nTmpCharsExcludingSpaces += g_pBreakIt->getGraphemeCount(rWord);
+ }
+
+ nTmpCharsExcludingSpaces += aScanner.getOverriddenDashCount();
+ nTmpChars += g_pBreakIt->getGraphemeCount(sNumString);
+ }
+ else if ( bHasBullet )
+ {
+ ++nTmpWords;
+ ++nTmpChars;
+ ++nTmpCharsExcludingSpaces;
+ }
+
+ // If counting the whole para then update cached values and mark clean
+ if ( bCountAll )
+ {
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->nNumberOfWords = nTmpWords;
+ m_pParaIdleData_Impl->nNumberOfAsianWords = nTmpAsianWords;
+ m_pParaIdleData_Impl->nNumberOfChars = nTmpChars;
+ m_pParaIdleData_Impl->nNumberOfCharsExcludingSpaces = nTmpCharsExcludingSpaces;
+ }
+ SetWordCountDirty( false );
+ }
+ // accumulate into DocStat record to return the values
+ rStat.nWord += nTmpWords;
+ rStat.nAsianWord += nTmpAsianWords;
+ rStat.nChar += nTmpChars;
+ rStat.nCharExcludingSpaces += nTmpCharsExcludingSpaces;
+
+ return true;
+}
+
+// Paragraph statistics start -->
+
+void SwTextNode::InitSwParaStatistics( bool bNew )
+{
+ if ( bNew )
+ {
+ m_pParaIdleData_Impl = new SwParaIdleData_Impl;
+ }
+ else if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->pWrong.reset();
+ m_pParaIdleData_Impl->pGrammarCheck.reset();
+ m_pParaIdleData_Impl->pSmartTags.reset();
+ delete m_pParaIdleData_Impl;
+ m_pParaIdleData_Impl = nullptr;
+ }
+}
+
+void SwTextNode::SetWrong( std::unique_ptr<SwWrongList> pNew )
+{
+ if ( m_pParaIdleData_Impl )
+ m_pParaIdleData_Impl->pWrong = std::move(pNew);
+}
+
+void SwTextNode::ClearWrong()
+{
+ if ( m_pParaIdleData_Impl )
+ m_pParaIdleData_Impl->pWrong.reset();
+}
+
+std::unique_ptr<SwWrongList> SwTextNode::ReleaseWrong()
+{
+ return m_pParaIdleData_Impl ? std::move(m_pParaIdleData_Impl->pWrong) : nullptr;
+}
+
+SwWrongList* SwTextNode::GetWrong()
+{
+ return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong.get() : nullptr;
+}
+
+// #i71360#
+const SwWrongList* SwTextNode::GetWrong() const
+{
+ return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pWrong.get() : nullptr;
+}
+
+void SwTextNode::SetGrammarCheck( std::unique_ptr<SwGrammarMarkUp> pNew )
+{
+ if ( m_pParaIdleData_Impl )
+ m_pParaIdleData_Impl->pGrammarCheck = std::move(pNew);
+}
+
+void SwTextNode::ClearGrammarCheck()
+{
+ if ( m_pParaIdleData_Impl )
+ m_pParaIdleData_Impl->pGrammarCheck.reset();
+}
+
+std::unique_ptr<SwGrammarMarkUp> SwTextNode::ReleaseGrammarCheck()
+{
+ return m_pParaIdleData_Impl ? std::move(m_pParaIdleData_Impl->pGrammarCheck) : nullptr;
+}
+
+SwGrammarMarkUp* SwTextNode::GetGrammarCheck()
+{
+ return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pGrammarCheck.get() : nullptr;
+}
+
+SwWrongList const* SwTextNode::GetGrammarCheck() const
+{
+ return static_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetGrammarCheck());
+}
+
+void SwTextNode::SetSmartTags( std::unique_ptr<SwWrongList> pNew )
+{
+ OSL_ENSURE( !pNew || SwSmartTagMgr::Get().IsSmartTagsEnabled(),
+ "Weird - we have a smart tag list without any recognizers?" );
+
+ if ( m_pParaIdleData_Impl )
+ m_pParaIdleData_Impl->pSmartTags = std::move(pNew);
+}
+
+void SwTextNode::ClearSmartTags()
+{
+ if ( m_pParaIdleData_Impl )
+ m_pParaIdleData_Impl->pSmartTags.reset();
+}
+
+std::unique_ptr<SwWrongList> SwTextNode::ReleaseSmartTags()
+{
+ return m_pParaIdleData_Impl ? std::move(m_pParaIdleData_Impl->pSmartTags) : nullptr;
+}
+
+SwWrongList* SwTextNode::GetSmartTags()
+{
+ return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->pSmartTags.get() : nullptr;
+}
+
+SwWrongList const* SwTextNode::GetSmartTags() const
+{
+ return const_cast<SwWrongList const*>(const_cast<SwTextNode*>(this)->GetSmartTags());
+}
+
+void SwTextNode::SetWordCountDirty( bool bNew ) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->bWordCountDirty = bNew;
+ }
+}
+
+bool SwTextNode::IsWordCountDirty() const
+{
+ return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bWordCountDirty;
+}
+
+void SwTextNode::SetWrongDirty(WrongState eNew) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->eWrongDirty = eNew;
+ }
+}
+
+auto SwTextNode::GetWrongDirty() const -> WrongState
+{
+ return m_pParaIdleData_Impl ? m_pParaIdleData_Impl->eWrongDirty : WrongState::DONE;
+}
+
+bool SwTextNode::IsWrongDirty() const
+{
+ return m_pParaIdleData_Impl && m_pParaIdleData_Impl->eWrongDirty != WrongState::DONE;
+}
+
+void SwTextNode::SetGrammarCheckDirty( bool bNew ) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->bGrammarCheckDirty = bNew;
+ }
+}
+
+bool SwTextNode::IsGrammarCheckDirty() const
+{
+ return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bGrammarCheckDirty;
+}
+
+void SwTextNode::SetSmartTagDirty( bool bNew ) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->bSmartTagDirty = bNew;
+ }
+}
+
+bool SwTextNode::IsSmartTagDirty() const
+{
+ return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bSmartTagDirty;
+}
+
+void SwTextNode::SetAutoCompleteWordDirty( bool bNew ) const
+{
+ if ( m_pParaIdleData_Impl )
+ {
+ m_pParaIdleData_Impl->bAutoComplDirty = bNew;
+ }
+}
+
+bool SwTextNode::IsAutoCompleteWordDirty() const
+{
+ return m_pParaIdleData_Impl && m_pParaIdleData_Impl->bAutoComplDirty;
+}
+
+// <-- Paragraph statistics end
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwRewriter.cxx b/sw/source/core/undo/SwRewriter.cxx
new file mode 100644
index 000000000..58e6d06e9
--- /dev/null
+++ b/sw/source/core/undo/SwRewriter.cxx
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <algorithm>
+#include <SwRewriter.hxx>
+
+SwRewriter::SwRewriter() {}
+
+void SwRewriter::AddRule(SwUndoArg eWhat, const OUString& rWith)
+{
+ SwRewriteRule aRule(eWhat, rWith);
+
+ std::vector<SwRewriteRule>::iterator aIt
+ = find_if(mRules.begin(), mRules.end(),
+ [&aRule](SwRewriteRule const& a) { return a.first == aRule.first; });
+
+ if (aIt != mRules.end())
+ *aIt = aRule;
+ else
+ mRules.push_back(aRule);
+}
+
+OUString SwRewriter::Apply(const OUString& rStr) const
+{
+ OUString aResult = rStr;
+
+ for (const auto& rRule : mRules)
+ {
+ aResult = aResult.replaceAll(GetPlaceHolder(rRule.first), rRule.second);
+ }
+
+ return aResult;
+}
+
+OUString SwRewriter::GetPlaceHolder(SwUndoArg eId)
+{
+ switch (eId)
+ {
+ case UndoArg1:
+ return "$1";
+ case UndoArg2:
+ return "$2";
+ case UndoArg3:
+ return "$3";
+ default:
+ break;
+ }
+ return "$1";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoField.cxx b/sw/source/core/undo/SwUndoField.cxx
new file mode 100644
index 000000000..353070852
--- /dev/null
+++ b/sw/source/core/undo/SwUndoField.cxx
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include <SwUndoField.hxx>
+#include <swundo.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentFieldsManager.hxx>
+#include <txtfld.hxx>
+#include <fldbas.hxx>
+#include <fmtfld.hxx>
+#include <docsh.hxx>
+#include <pam.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star::uno;
+
+SwUndoField::SwUndoField(const SwPosition & rPos )
+ : SwUndo(SwUndoId::FIELD, &rPos.GetDoc())
+{
+ m_nNodeIndex = rPos.nNode.GetIndex();
+ m_nOffset = rPos.nContent.GetIndex();
+ m_pDoc = &rPos.GetDoc();
+}
+
+SwUndoField::~SwUndoField()
+{
+}
+
+SwPosition SwUndoField::GetPosition()
+{
+ SwNode * pNode = m_pDoc->GetNodes()[m_nNodeIndex];
+ SwNodeIndex aNodeIndex(*pNode);
+ SwIndex aIndex(pNode->GetContentNode(), m_nOffset);
+ SwPosition aResult(aNodeIndex, aIndex);
+
+ return aResult;
+}
+
+SwUndoFieldFromDoc::SwUndoFieldFromDoc(const SwPosition & rPos,
+ const SwField & rOldField,
+ const SwField & rNewField,
+ SwMsgPoolItem * _pHint, bool _bUpdate)
+ : SwUndoField(rPos)
+ , m_pOldField(rOldField.CopyField())
+ , m_pNewField(rNewField.CopyField())
+ , m_pHint(_pHint)
+ , m_bUpdate(_bUpdate)
+{
+ OSL_ENSURE(m_pOldField, "No old field!");
+ OSL_ENSURE(m_pNewField, "No new field!");
+ OSL_ENSURE(m_pDoc, "No document!");
+}
+
+SwUndoFieldFromDoc::~SwUndoFieldFromDoc()
+{
+}
+
+void SwUndoFieldFromDoc::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition());
+ const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr;
+
+ if (pField)
+ {
+ m_pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *m_pOldField, m_pHint, m_bUpdate);
+ }
+}
+
+void SwUndoFieldFromDoc::DoImpl()
+{
+ SwTextField * pTextField = sw::DocumentFieldsManager::GetTextFieldAtPos(GetPosition());
+ const SwField * pField = pTextField ? pTextField->GetFormatField().GetField() : nullptr;
+
+ if (pField)
+ {
+ m_pDoc->getIDocumentFieldsAccess().UpdateField(pTextField, *m_pNewField, m_pHint, m_bUpdate);
+ SwFormatField* pDstFormatField = const_cast<SwFormatField*>(&pTextField->GetFormatField());
+
+ if (m_pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(), false) == pDstFormatField->GetField()->GetTyp())
+ m_pDoc->GetDocShell()->Broadcast( SwFormatFieldHint( pDstFormatField, SwFormatFieldHintWhich::INSERTED ) );
+ }
+}
+
+void SwUndoFieldFromDoc::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoFieldFromDoc::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwUndoFieldFromAPI::SwUndoFieldFromAPI(const SwPosition & rPos,
+ const Any & rOldVal, const Any & rNewVal,
+ sal_uInt16 _nWhich)
+ : SwUndoField(rPos), m_aOldVal(rOldVal), m_aNewVal(rNewVal), m_nWhich(_nWhich)
+{
+}
+
+SwUndoFieldFromAPI::~SwUndoFieldFromAPI()
+{
+}
+
+void SwUndoFieldFromAPI::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition());
+
+ if (pField)
+ pField->PutValue(m_aOldVal, m_nWhich);
+}
+
+void SwUndoFieldFromAPI::DoImpl()
+{
+ SwField * pField = sw::DocumentFieldsManager::GetFieldAtPos(GetPosition());
+
+ if (pField)
+ pField->PutValue(m_aNewVal, m_nWhich);
+}
+
+void SwUndoFieldFromAPI::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoFieldFromAPI::RepeatImpl(::sw::RepeatContext &)
+{
+ DoImpl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoFmt.cxx b/sw/source/core/undo/SwUndoFmt.cxx
new file mode 100644
index 000000000..110f138f5
--- /dev/null
+++ b/sw/source/core/undo/SwUndoFmt.cxx
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <poolfmt.hxx>
+#include <charfmt.hxx>
+#include <frmfmt.hxx>
+#include <SwUndoFmt.hxx>
+#include <SwRewriter.hxx>
+#include <swundo.hxx>
+#include <undobj.hxx>
+#include <fmtcol.hxx>
+#include <doc.hxx>
+#include <strings.hrc>
+
+SwUndoFormatCreate::SwUndoFormatCreate
+(SwUndoId nUndoId, SwFormat * _pNew, SwFormat const * _pDerivedFrom, SwDoc& rDoc)
+ : SwUndo(nUndoId, &rDoc), m_pNew(_pNew),
+ m_rDoc(rDoc), m_nId(0), m_bAuto(false)
+{
+ if (_pDerivedFrom)
+ m_sDerivedFrom = _pDerivedFrom->GetName();
+}
+
+SwUndoFormatCreate::~SwUndoFormatCreate()
+{
+}
+
+void SwUndoFormatCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (!m_pNew)
+ return;
+
+ if (m_sNewName.isEmpty())
+ m_sNewName = m_pNew->GetName();
+
+ if (!m_sNewName.isEmpty())
+ m_pNew = Find(m_sNewName);
+
+ if (m_pNew)
+ {
+ m_pNewSet.reset(new SfxItemSet(m_pNew->GetAttrSet()));
+ m_nId = m_pNew->GetPoolFormatId() & COLL_GET_RANGE_BITS;
+ m_bAuto = m_pNew->IsAuto();
+
+ Delete();
+ }
+}
+
+void SwUndoFormatCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pDerivedFrom = Find(m_sDerivedFrom);
+ SwFormat * pFormat = Create(pDerivedFrom);
+
+ if (pFormat && m_pNewSet)
+ {
+ pFormat->SetAuto(m_bAuto);
+ m_rDoc.ChgFormat(*pFormat, *m_pNewSet);
+ pFormat->SetPoolFormatId((pFormat->GetPoolFormatId()
+ & ~COLL_GET_RANGE_BITS)
+ | m_nId);
+
+ m_pNew = pFormat;
+ }
+ else
+ m_pNew = nullptr;
+}
+
+SwRewriter SwUndoFormatCreate::GetRewriter() const
+{
+ if (m_sNewName.isEmpty() && m_pNew)
+ m_sNewName = m_pNew->GetName();
+
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sNewName);
+
+ return aRewriter;
+}
+
+SwUndoFormatDelete::SwUndoFormatDelete
+(SwUndoId nUndoId, SwFormat const * _pOld, SwDoc& rDoc)
+ : SwUndo(nUndoId, &rDoc),
+ m_rDoc(rDoc), m_sOldName(_pOld->GetName()),
+ m_aOldSet(_pOld->GetAttrSet())
+{
+ m_sDerivedFrom = _pOld->DerivedFrom()->GetName();
+ m_nId = _pOld->GetPoolFormatId() & COLL_GET_RANGE_BITS;
+ m_bAuto = _pOld->IsAuto();
+}
+
+SwUndoFormatDelete::~SwUndoFormatDelete()
+{
+}
+
+void SwUndoFormatDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pDerivedFrom = Find(m_sDerivedFrom);
+
+ SwFormat * pFormat = Create(pDerivedFrom);
+
+ if (pFormat)
+ {
+ m_rDoc.ChgFormat(*pFormat, m_aOldSet);
+ pFormat->SetAuto(m_bAuto);
+ pFormat->SetPoolFormatId((pFormat->GetPoolFormatId() &
+ ~COLL_GET_RANGE_BITS)
+ | m_nId);
+ }
+}
+
+void SwUndoFormatDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pOld = Find(m_sOldName);
+
+ if (pOld)
+ {
+ Delete(pOld);
+ }
+}
+
+SwRewriter SwUndoFormatDelete::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sOldName);
+
+ return aRewriter;
+}
+
+SwUndoRenameFormat::SwUndoRenameFormat(SwUndoId nUndoId,
+ const OUString & _sOldName,
+ const OUString & _sNewName,
+ SwDoc& rDoc)
+ : SwUndo(nUndoId, &rDoc), m_sOldName(_sOldName),
+ m_sNewName(_sNewName), m_rDoc(rDoc)
+{
+}
+
+SwUndoRenameFormat::~SwUndoRenameFormat()
+{
+}
+
+void SwUndoRenameFormat::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pFormat = Find(m_sNewName);
+
+ if (pFormat)
+ {
+ m_rDoc.RenameFormat(*pFormat, m_sOldName, true);
+ }
+}
+
+void SwUndoRenameFormat::RedoImpl(::sw::UndoRedoContext &)
+{
+ SwFormat * pFormat = Find(m_sOldName);
+
+ if (pFormat)
+ {
+ m_rDoc.RenameFormat(*pFormat, m_sNewName, true);
+ }
+}
+
+SwRewriter SwUndoRenameFormat::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sOldName);
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, m_sNewName);
+
+ return aRewriter;
+}
+
+SwUndoTextFormatCollCreate::SwUndoTextFormatCollCreate
+(SwTextFormatColl * _pNew, SwTextFormatColl const * _pDerivedFrom, SwDoc& rDoc)
+ : SwUndoFormatCreate(SwUndoId::TXTFMTCOL_CREATE, _pNew, _pDerivedFrom, rDoc)
+{
+}
+
+SwFormat * SwUndoTextFormatCollCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+void SwUndoTextFormatCollCreate::Delete()
+{
+ m_rDoc.DelTextFormatColl(static_cast<SwTextFormatColl *>(m_pNew), true);
+}
+
+SwFormat * SwUndoTextFormatCollCreate::Find(const OUString & rName) const
+{
+ return m_rDoc.FindTextFormatCollByName(rName);
+}
+
+SwUndoTextFormatCollDelete::SwUndoTextFormatCollDelete(SwTextFormatColl const * _pOld,
+ SwDoc& rDoc)
+ : SwUndoFormatDelete(SwUndoId::TXTFMTCOL_DELETE, _pOld, rDoc)
+{
+}
+
+SwFormat * SwUndoTextFormatCollDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+void SwUndoTextFormatCollDelete::Delete(SwFormat * pOld)
+{
+ m_rDoc.DelTextFormatColl(static_cast<SwTextFormatColl *>(pOld), true);
+}
+
+SwFormat * SwUndoTextFormatCollDelete::Find(const OUString & rName) const
+{
+ return m_rDoc.FindTextFormatCollByName(rName);
+}
+
+SwUndoCondTextFormatCollCreate::SwUndoCondTextFormatCollCreate(SwConditionTextFormatColl *_pNew,
+ SwTextFormatColl const *_pDerivedFrom, SwDoc& rDoc)
+ : SwUndoTextFormatCollCreate(_pNew, _pDerivedFrom, rDoc)
+{
+}
+
+SwFormat * SwUndoCondTextFormatCollCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCondTextFormatColl(m_sNewName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+SwUndoCondTextFormatCollDelete::SwUndoCondTextFormatCollDelete(SwTextFormatColl const * _pOld,
+ SwDoc& rDoc)
+ : SwUndoTextFormatCollDelete(_pOld, rDoc)
+{
+}
+
+SwFormat * SwUndoCondTextFormatCollDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCondTextFormatColl(m_sOldName, static_cast<SwTextFormatColl *>(pDerivedFrom), true);
+}
+
+SwUndoRenameFormatColl::SwUndoRenameFormatColl(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc& rDoc)
+ : SwUndoRenameFormat(SwUndoId::TXTFMTCOL_RENAME, sInitOldName, sInitNewName, rDoc)
+{
+}
+
+SwFormat * SwUndoRenameFormatColl::Find(const OUString & rName) const
+{
+ return m_rDoc.FindTextFormatCollByName(rName);
+}
+
+SwUndoCharFormatCreate::SwUndoCharFormatCreate(SwCharFormat * pNewFormat,
+ SwCharFormat const * pDerivedFrom,
+ SwDoc& rDocument)
+ : SwUndoFormatCreate(SwUndoId::CHARFMT_CREATE, pNewFormat, pDerivedFrom, rDocument)
+{
+}
+
+SwFormat * SwUndoCharFormatCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCharFormat(m_sNewName, static_cast<SwCharFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoCharFormatCreate::Delete()
+{
+ m_rDoc.DelCharFormat(static_cast<SwCharFormat *>(m_pNew), true);
+}
+
+SwFormat * SwUndoCharFormatCreate::Find(const OUString & rName) const
+{
+ return m_rDoc.FindCharFormatByName(rName);
+}
+
+SwUndoCharFormatDelete::SwUndoCharFormatDelete(SwCharFormat const * pOld, SwDoc& rDocument)
+ : SwUndoFormatDelete(SwUndoId::CHARFMT_DELETE, pOld, rDocument)
+{
+}
+
+SwFormat * SwUndoCharFormatDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeCharFormat(m_sOldName, static_cast<SwCharFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoCharFormatDelete::Delete(SwFormat * pFormat)
+{
+ m_rDoc.DelCharFormat(static_cast<SwCharFormat *>(pFormat), true);
+}
+
+SwFormat * SwUndoCharFormatDelete::Find(const OUString & rName) const
+{
+ return m_rDoc.FindCharFormatByName(rName);
+}
+
+SwUndoRenameCharFormat::SwUndoRenameCharFormat(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc& rDocument)
+ : SwUndoRenameFormat(SwUndoId::CHARFMT_RENAME, sInitOldName, sInitNewName, rDocument)
+{
+}
+
+SwFormat * SwUndoRenameCharFormat::Find(const OUString & rName) const
+{
+ return m_rDoc.FindCharFormatByName(rName);
+}
+
+SwUndoFrameFormatCreate::SwUndoFrameFormatCreate(SwFrameFormat * pNewFormat,
+ SwFrameFormat const * pDerivedFrom,
+ SwDoc& rDocument)
+ : SwUndoFormatCreate(SwUndoId::FRMFMT_CREATE, pNewFormat, pDerivedFrom, rDocument)
+{
+}
+
+SwFormat * SwUndoFrameFormatCreate::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeFrameFormat(m_sNewName, static_cast<SwFrameFormat *>(pDerivedFrom), true, m_pNew->IsAuto());
+}
+
+void SwUndoFrameFormatCreate::Delete()
+{
+ m_rDoc.DelFrameFormat(static_cast<SwFrameFormat *>(m_pNew), true);
+}
+
+SwFormat * SwUndoFrameFormatCreate::Find(const OUString & rName) const
+{
+ return m_rDoc.FindFrameFormatByName(rName);
+}
+
+SwUndoFrameFormatDelete::SwUndoFrameFormatDelete(SwFrameFormat const * pOld, SwDoc& rDocument)
+ : SwUndoFormatDelete(SwUndoId::FRMFMT_DELETE, pOld, rDocument)
+{
+}
+
+SwFormat * SwUndoFrameFormatDelete::Create(SwFormat * pDerivedFrom)
+{
+ return m_rDoc.MakeFrameFormat(m_sOldName, static_cast<SwFrameFormat *>(pDerivedFrom), true);
+}
+
+void SwUndoFrameFormatDelete::Delete(SwFormat * pFormat)
+{
+ m_rDoc.DelFrameFormat(static_cast<SwFrameFormat *>(pFormat), true);
+}
+
+SwFormat * SwUndoFrameFormatDelete::Find(const OUString & rName) const
+{
+ return m_rDoc.FindFrameFormatByName(rName);
+}
+
+SwUndoRenameFrameFormat::SwUndoRenameFrameFormat(const OUString & sInitOldName,
+ const OUString & sInitNewName,
+ SwDoc& rDocument)
+ : SwUndoRenameFormat(SwUndoId::FRMFMT_RENAME, sInitOldName, sInitNewName, rDocument)
+{
+}
+
+SwFormat * SwUndoRenameFrameFormat::Find(const OUString & rName) const
+{
+ return m_rDoc.FindFrameFormatByName(rName);
+}
+
+SwUndoNumruleCreate::SwUndoNumruleCreate(const SwNumRule * _pNew,
+ SwDoc& rDoc)
+ : SwUndo(SwUndoId::NUMRULE_CREATE, &rDoc), m_pNew(_pNew), m_aNew(*_pNew), m_rDoc(rDoc),
+ m_bInitialized(false)
+{
+}
+
+void SwUndoNumruleCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (! m_bInitialized)
+ {
+ m_aNew = *m_pNew;
+ m_bInitialized = true;
+ }
+
+ m_rDoc.DelNumRule(m_aNew.GetName(), true);
+}
+
+void SwUndoNumruleCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.MakeNumRule(m_aNew.GetName(), &m_aNew, true);
+}
+
+SwRewriter SwUndoNumruleCreate::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (! m_bInitialized)
+ {
+ m_aNew = *m_pNew;
+ m_bInitialized = true;
+ }
+
+ aResult.AddRule(UndoArg1, m_aNew.GetName());
+
+ return aResult;
+}
+
+SwUndoNumruleDelete::SwUndoNumruleDelete(const SwNumRule & rRule,
+ SwDoc& rDoc)
+ : SwUndo(SwUndoId::NUMRULE_DELETE, &rDoc), m_aOld(rRule), m_rDoc(rDoc)
+{
+}
+
+void SwUndoNumruleDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.MakeNumRule(m_aOld.GetName(), &m_aOld, true);
+}
+
+void SwUndoNumruleDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.DelNumRule(m_aOld.GetName(), true);
+}
+
+SwRewriter SwUndoNumruleDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+
+ return aResult;
+}
+
+SwUndoNumruleRename::SwUndoNumruleRename(const OUString & _aOldName,
+ const OUString & _aNewName,
+ SwDoc& rDoc)
+ : SwUndo(SwUndoId::NUMRULE_RENAME, &rDoc), m_aOldName(_aOldName), m_aNewName(_aNewName),
+ m_rDoc(rDoc)
+{
+}
+
+void SwUndoNumruleRename::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.RenameNumRule(m_aNewName, m_aOldName, true);
+}
+
+void SwUndoNumruleRename::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_rDoc.RenameNumRule(m_aOldName, m_aNewName, true);
+}
+
+SwRewriter SwUndoNumruleRename::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_aOldName);
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, m_aNewName);
+
+ return aRewriter;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoPageDesc.cxx b/sw/source/core/undo/SwUndoPageDesc.cxx
new file mode 100644
index 000000000..eec1300d5
--- /dev/null
+++ b/sw/source/core/undo/SwUndoPageDesc.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <editsh.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <swundo.hxx>
+#include <pagedesc.hxx>
+#include <SwUndoPageDesc.hxx>
+#include <SwRewriter.hxx>
+#include <undobj.hxx>
+#include <strings.hrc>
+#include <fmtcntnt.hxx>
+#include <fmthdft.hxx>
+#include <osl/diagnose.h>
+
+SwUndoPageDesc::SwUndoPageDesc(const SwPageDesc & _aOld,
+ const SwPageDesc & _aNew,
+ SwDoc * _pDoc)
+ : SwUndo( _aOld.GetName() != _aNew.GetName() ?
+ SwUndoId::RENAME_PAGEDESC :
+ SwUndoId::CHANGE_PAGEDESC,
+ _pDoc ),
+ m_aOld(_aOld, _pDoc), m_aNew(_aNew, _pDoc), m_pDoc(_pDoc), m_bExchange( false )
+{
+ OSL_ENSURE(nullptr != m_pDoc, "no document?");
+
+ /*
+ The page description changes.
+ If there are no header/footer content changes like header on/off or change from shared content
+ to unshared etc., there is no reason to duplicate the content nodes (Crash i55547)
+ But this happens, this Undo Ctor will destroy the unnecessary duplicate and manipulate the
+ content pointer of the both page descriptions.
+ */
+ SwPageDesc &rOldDesc = m_aOld.m_PageDesc;
+ SwPageDesc &rNewDesc = m_aNew.m_PageDesc;
+ const SwFormatHeader& rOldHead = rOldDesc.GetMaster().GetHeader();
+ const SwFormatHeader& rNewHead = rNewDesc.GetMaster().GetHeader();
+ const SwFormatFooter& rOldFoot = rOldDesc.GetMaster().GetFooter();
+ const SwFormatFooter& rNewFoot = rNewDesc.GetMaster().GetFooter();
+ /* bExchange must not be set, if the old page descriptor will stay active.
+ Two known situations:
+ #i67735#: renaming a page descriptor
+ #i67334#: changing the follow style
+ If header/footer will be activated or deactivated, this undo will not work.
+ */
+ m_bExchange = ( m_aOld.GetName() == m_aNew.GetName() ) &&
+ ( _aOld.GetFollow() == _aNew.GetFollow() ) &&
+ ( rOldHead.IsActive() == rNewHead.IsActive() ) &&
+ ( rOldFoot.IsActive() == rNewFoot.IsActive() );
+ if( rOldHead.IsActive() && ( rOldDesc.IsHeaderShared() != rNewDesc.IsHeaderShared() ) )
+ m_bExchange = false;
+ if( rOldFoot.IsActive() && ( rOldDesc.IsFooterShared() != rNewDesc.IsFooterShared() ) )
+ m_bExchange = false;
+ if( ( rOldHead.IsActive() || rOldFoot.IsActive() ) && ( rOldDesc.IsFirstShared() != rNewDesc.IsFirstShared() ) )
+ m_bExchange = false;
+ if( !m_bExchange )
+ return;
+
+ if( rNewHead.IsActive() )
+ {
+ SwFrameFormat* pFormat = new SwFrameFormat( *rNewHead.GetHeaderFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatHeader aFormatHeader(pFormat);
+ (void)aFormatHeader;
+ if (!rNewDesc.IsHeaderShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetLeft().GetHeader().GetHeaderFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatHeader aLeftHeader(pFormat);
+ (void)aLeftHeader;
+ }
+ if (!rNewDesc.IsFirstShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetFirstMaster().GetHeader().GetHeaderFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatHeader aFirstHeader(pFormat);
+ (void)aFirstHeader;
+ }
+ }
+ // Same procedure for footers...
+ if( rNewFoot.IsActive() )
+ {
+ SwFrameFormat* pFormat = new SwFrameFormat( *rNewFoot.GetFooterFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatFooter aFormatFooter(pFormat);
+ (void)aFormatFooter;
+ if (!rNewDesc.IsFooterShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetLeft().GetFooter().GetFooterFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatFooter aLeftFooter(pFormat);
+ (void)aLeftFooter;
+ }
+ if (!rNewDesc.IsFirstShared())
+ {
+ pFormat = new SwFrameFormat( *rNewDesc.GetFirstMaster().GetFooter().GetFooterFormat() );
+ // The Ctor of this object will remove the duplicate!
+ SwFormatFooter aFirstFooter(pFormat);
+ (void)aFirstFooter;
+ }
+ }
+
+ // After this exchange method the old page description will point to zero,
+ // the new one will point to the node position of the original content nodes.
+ ExchangeContentNodes( m_aOld.m_PageDesc, m_aNew.m_PageDesc );
+}
+
+SwUndoPageDesc::~SwUndoPageDesc()
+{
+}
+
+void SwUndoPageDesc::ExchangeContentNodes( SwPageDesc& rSource, SwPageDesc &rDest )
+{
+ OSL_ENSURE( m_bExchange, "You shouldn't do that." );
+ const SwFormatHeader& rDestHead = rDest.GetMaster().GetHeader();
+ const SwFormatHeader& rSourceHead = rSource.GetMaster().GetHeader();
+ if( rDestHead.IsActive() )
+ {
+ // Let the destination page description point to the source node position,
+ // from now on this descriptor is responsible for the content nodes!
+ const SwFormatHeader* pItem = rDest.GetMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ std::unique_ptr<SwFormatHeader> pNewItem(pItem->Clone());
+ SwFrameFormat* pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceHead.GetHeaderFormat()->GetContent() );
+
+ // Let the source page description point to zero node position,
+ // it loses the responsible and can be destroyed without removing the content nodes.
+ pItem = rSource.GetMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+
+ if( !rDest.IsHeaderShared() )
+ {
+ // Same procedure for unshared header...
+ const SwFormatHeader& rSourceLeftHead = rSource.GetLeft().GetHeader();
+ pItem = rDest.GetLeft().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceLeftHead.GetHeaderFormat()->GetContent() );
+ pItem = rSource.GetLeft().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ if (!rDest.IsFirstShared())
+ {
+ // Same procedure for unshared header...
+ const SwFormatHeader& rSourceFirstMasterHead = rSource.GetFirstMaster().GetHeader();
+ pItem = rDest.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( rSourceFirstMasterHead.GetHeaderFormat()->GetContent() );
+ pItem = rSource.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_HEADER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetHeaderFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ }
+ // Same procedure for footers...
+ const SwFormatFooter& rDestFoot = rDest.GetMaster().GetFooter();
+ const SwFormatFooter& rSourceFoot = rSource.GetMaster().GetFooter();
+ if( !rDestFoot.IsActive() )
+ return;
+
+ const SwFormatFooter* pItem;
+ pItem = rDest.GetMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ std::unique_ptr<SwFormatFooter> pNewItem(pItem->Clone());
+ SwFrameFormat *pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceFoot.GetFooterFormat()->GetContent() );
+
+ pItem = rSource.GetMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+
+ if( !rDest.IsFooterShared() )
+ {
+ const SwFormatFooter& rSourceLeftFoot = rSource.GetLeft().GetFooter();
+ pItem = rDest.GetLeft().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceLeftFoot.GetFooterFormat()->GetContent() );
+ pItem = rSource.GetLeft().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+ }
+ if (rDest.IsFirstShared())
+ return;
+
+ const SwFormatFooter& rSourceFirstMasterFoot = rSource.GetFirstMaster().GetFooter();
+ pItem = rDest.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( rSourceFirstMasterFoot.GetFooterFormat()->GetContent() );
+ pItem = rSource.GetFirstMaster().GetAttrSet().GetItemIfSet( RES_FOOTER, false );
+ pNewItem.reset(pItem->Clone());
+ pNewFormat = pNewItem->GetFooterFormat();
+ pNewFormat->SetFormatAttr( SwFormatContent() );
+}
+
+void SwUndoPageDesc::ExitHeaderFooterEdit()
+{
+ SwEditShell* pESh = m_pDoc->GetEditShell();
+ if (!pESh)
+ return;
+ if (pESh->IsHeaderFooterEdit())
+ pESh->ToggleHeaderFooterEdit();
+}
+
+void SwUndoPageDesc::UndoImpl(::sw::UndoRedoContext &)
+{
+ // Move (header/footer)content node responsibility from new page descriptor to old one again.
+ if( m_bExchange )
+ ExchangeContentNodes( m_aNew.m_PageDesc, m_aOld.m_PageDesc );
+ m_pDoc->ChgPageDesc(m_aOld.GetName(), m_aOld);
+ ExitHeaderFooterEdit();
+}
+
+void SwUndoPageDesc::RedoImpl(::sw::UndoRedoContext &)
+{
+ // Move (header/footer)content node responsibility from old page descriptor to new one again.
+ if( m_bExchange )
+ ExchangeContentNodes( m_aOld.m_PageDesc, m_aNew.m_PageDesc );
+ m_pDoc->ChgPageDesc(m_aNew.GetName(), m_aNew);
+ ExitHeaderFooterEdit();
+}
+
+SwRewriter SwUndoPageDesc::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+ aResult.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aResult.AddRule(UndoArg3, m_aNew.GetName());
+
+ return aResult;
+}
+
+SwUndoPageDescCreate::SwUndoPageDescCreate(const SwPageDesc * pNew,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::CREATE_PAGEDESC, _pDoc), m_pDesc(pNew), m_aNew(*pNew, _pDoc),
+ m_pDoc(_pDoc)
+{
+ OSL_ENSURE(nullptr != m_pDoc, "no document?");
+}
+
+SwUndoPageDescCreate::~SwUndoPageDescCreate()
+{
+}
+
+void SwUndoPageDescCreate::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pDesc)
+ {
+ m_aNew = *m_pDesc;
+ m_pDesc = nullptr;
+ }
+
+ m_pDoc->DelPageDesc(m_aNew.GetName(), true);
+}
+
+void SwUndoPageDescCreate::DoImpl()
+{
+ SwPageDesc aPageDesc = m_aNew;
+ m_pDoc->MakePageDesc(m_aNew.GetName(), &aPageDesc, false, true);
+}
+
+void SwUndoPageDescCreate::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoPageDescCreate::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwRewriter SwUndoPageDescCreate::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (m_pDesc)
+ aResult.AddRule(UndoArg1, m_pDesc->GetName());
+ else
+ aResult.AddRule(UndoArg1, m_aNew.GetName());
+
+ return aResult;
+}
+
+SwUndoPageDescDelete::SwUndoPageDescDelete(const SwPageDesc & _aOld,
+ SwDoc * _pDoc)
+ : SwUndo(SwUndoId::DELETE_PAGEDESC, _pDoc), m_aOld(_aOld, _pDoc), m_pDoc(_pDoc)
+{
+ OSL_ENSURE(nullptr != m_pDoc, "no document?");
+}
+
+SwUndoPageDescDelete::~SwUndoPageDescDelete()
+{
+}
+
+void SwUndoPageDescDelete::UndoImpl(::sw::UndoRedoContext &)
+{
+ SwPageDesc aPageDesc = m_aOld;
+ m_pDoc->MakePageDesc(m_aOld.GetName(), &aPageDesc, false, true);
+}
+
+void SwUndoPageDescDelete::DoImpl()
+{
+ m_pDoc->DelPageDesc(m_aOld.GetName(), true);
+}
+
+void SwUndoPageDescDelete::RedoImpl(::sw::UndoRedoContext &)
+{
+ DoImpl();
+}
+
+void SwUndoPageDescDelete::RepeatImpl(::sw::RepeatContext &)
+{
+ ::sw::UndoGuard const undoGuard(m_pDoc->GetIDocumentUndoRedo());
+ DoImpl();
+}
+
+SwRewriter SwUndoPageDescDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_aOld.GetName());
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/SwUndoTOXChange.cxx b/sw/source/core/undo/SwUndoTOXChange.cxx
new file mode 100644
index 000000000..5223e9993
--- /dev/null
+++ b/sw/source/core/undo/SwUndoTOXChange.cxx
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <SwUndoTOXChange.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
+#include <doctxm.hxx>
+#include <doc.hxx>
+#include <node.hxx>
+
+namespace
+{
+ SwNodeOffset GetSectionNodeIndex(SwTOXBaseSection const& rTOX)
+ {
+ const SwSectionNode* pSectNd = rTOX.GetFormat()->GetSectionNode();
+ assert(pSectNd);
+ return pSectNd->GetIndex();
+ }
+}
+
+SwUndoTOXChange::SwUndoTOXChange(const SwDoc& rDoc,
+ SwTOXBaseSection const& rTOX, SwTOXBase const& rNew)
+ : SwUndo(SwUndoId::TOXCHANGE, &rDoc)
+ , m_Old(rTOX)
+ , m_New(rNew)
+ , m_nNodeIndex(GetSectionNodeIndex(rTOX))
+{
+}
+
+SwUndoTOXChange::~SwUndoTOXChange()
+{
+}
+
+// get the current ToXBase, which is not necessarily the same instance that existed there before
+static SwTOXBase & GetTOX(SwDoc & rDoc, SwNodeOffset const nNodeIndex)
+{
+ SwSectionNode *const pNode(rDoc.GetNodes()[nNodeIndex]->GetSectionNode());
+ assert(pNode);
+ assert(dynamic_cast<SwTOXBaseSection*>(&pNode->GetSection()));
+ auto & rTOX(static_cast<SwTOXBaseSection&>(pNode->GetSection()));
+ return rTOX;
+}
+
+void SwUndoTOXChange::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ SwTOXBase & rTOX(GetTOX(rDoc, m_nNodeIndex));
+ rTOX = m_Old;
+}
+
+void SwUndoTOXChange::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ SwTOXBase & rTOX(GetTOX(rDoc, m_nNodeIndex));
+ rTOX = m_New;
+}
+
+void SwUndoTOXChange::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ SwTOXBase *const pTOX(SwDoc::GetCurTOX(*rContext.GetRepeatPaM().GetPoint()));
+ if (pTOX)
+ {
+ rDoc.ChangeTOX(*pTOX, m_New);
+ // intentionally limited to not Update because we'd need layout
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx
new file mode 100644
index 000000000..50db51c04
--- /dev/null
+++ b/sw/source/core/undo/docundo.cxx
@@ -0,0 +1,839 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoManager.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <drawdoc.hxx>
+#include <ndarr.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <UndoCore.hxx>
+#include <editsh.hxx>
+#include <unobaseclass.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <comphelper/lok.hxx>
+#include <assert.h>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/temporary.hxx>
+
+#include <UndoInsert.hxx>
+
+using namespace ::com::sun::star;
+
+// the undo array should never grow beyond this limit:
+#define UNDO_ACTION_LIMIT (USHRT_MAX - 1000)
+
+namespace sw {
+
+UndoManager::UndoManager(std::shared_ptr<SwNodes> const & xUndoNodes,
+ IDocumentDrawModelAccess & rDrawModelAccess,
+ IDocumentRedlineAccess & rRedlineAccess,
+ IDocumentState & rState)
+ : m_rDrawModelAccess(rDrawModelAccess)
+ , m_rRedlineAccess(rRedlineAccess)
+ , m_rState(rState)
+ , m_xUndoNodes(xUndoNodes)
+ , m_bGroupUndo(true)
+ , m_bDrawUndo(true)
+ , m_bRepair(false)
+ , m_bLockUndoNoModifiedPosition(false)
+ , m_isAddWithIgnoreRepeat(false)
+ , m_UndoSaveMark(MARK_INVALID)
+ , m_pDocShell(nullptr)
+ , m_pView(nullptr)
+{
+ assert(bool(m_xUndoNodes));
+ // writer expects it to be disabled initially
+ // Undo is enabled by SwEditShell constructor
+ SdrUndoManager::EnableUndo(false);
+}
+
+SwNodes const& UndoManager::GetUndoNodes() const
+{
+ return *m_xUndoNodes;
+}
+
+SwNodes & UndoManager::GetUndoNodes()
+{
+ return *m_xUndoNodes;
+}
+
+bool UndoManager::IsUndoNodes(SwNodes const& rNodes) const
+{
+ return & rNodes == m_xUndoNodes.get();
+}
+
+void UndoManager::SetDocShell(SwDocShell* pDocShell)
+{
+ m_pDocShell = pDocShell;
+}
+
+void UndoManager::SetView(SwView* pView)
+{
+ m_pView = pView;
+}
+
+size_t UndoManager::GetUndoActionCount(const bool bCurrentLevel) const
+{
+ size_t nRet = SdrUndoManager::GetUndoActionCount(bCurrentLevel);
+ if (!comphelper::LibreOfficeKit::isActive() || !m_pView)
+ return nRet;
+
+ if (!nRet || !SdrUndoManager::GetUndoActionCount())
+ return nRet;
+
+ const SfxUndoAction* pAction = SdrUndoManager::GetUndoAction();
+ if (!pAction)
+ return nRet;
+
+ if (!m_bRepair)
+ {
+ // If another view created the last undo action, prevent undoing it from this view.
+ ViewShellId nViewShellId = m_pView->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ nRet = 0;
+ }
+
+ return nRet;
+}
+
+size_t UndoManager::GetRedoActionCount(const bool bCurrentLevel) const
+{
+ size_t nRet = SdrUndoManager::GetRedoActionCount(bCurrentLevel);
+ if (!comphelper::LibreOfficeKit::isActive() || !m_pView)
+ return nRet;
+
+ if (!nRet || !SdrUndoManager::GetRedoActionCount())
+ return nRet;
+
+ const SfxUndoAction* pAction = SdrUndoManager::GetRedoAction();
+ if (!pAction)
+ return nRet;
+
+ if (!m_bRepair)
+ {
+ // If another view created the first redo action, prevent redoing it from this view.
+ ViewShellId nViewShellId = m_pView->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ nRet = 0;
+ }
+
+ return nRet;
+}
+
+void UndoManager::DoUndo(bool const bDoUndo)
+{
+ if(!isTextEditActive())
+ {
+ EnableUndo(bDoUndo);
+
+ SwDrawModel*const pSdrModel = m_rDrawModelAccess.GetDrawModel();
+ if( pSdrModel )
+ {
+ pSdrModel->EnableUndo(bDoUndo);
+ }
+ }
+}
+
+bool UndoManager::DoesUndo() const
+{
+ if(isTextEditActive())
+ {
+ return false;
+ }
+ else
+ {
+ return IsUndoEnabled();
+ }
+}
+
+void UndoManager::DoGroupUndo(bool const bDoUndo)
+{
+ m_bGroupUndo = bDoUndo;
+}
+
+bool UndoManager::DoesGroupUndo() const
+{
+ return m_bGroupUndo;
+}
+
+void UndoManager::DoDrawUndo(bool const bDoUndo)
+{
+ m_bDrawUndo = bDoUndo;
+}
+
+bool UndoManager::DoesDrawUndo() const
+{
+ return m_bDrawUndo;
+}
+
+void UndoManager::DoRepair(bool bRepair)
+{
+ m_bRepair = bRepair;
+}
+
+bool UndoManager::DoesRepair() const
+{
+ return m_bRepair;
+}
+
+bool UndoManager::IsUndoNoResetModified() const
+{
+ return MARK_INVALID == m_UndoSaveMark;
+}
+
+void UndoManager::SetUndoNoResetModified()
+{
+ if (MARK_INVALID != m_UndoSaveMark)
+ {
+ RemoveMark(m_UndoSaveMark);
+ m_UndoSaveMark = MARK_INVALID;
+ }
+}
+
+void UndoManager::SetUndoNoModifiedPosition()
+{
+ if (!m_bLockUndoNoModifiedPosition)
+ {
+ m_UndoSaveMark = MarkTopUndoAction();
+ }
+}
+
+void UndoManager::LockUndoNoModifiedPosition()
+{
+ m_bLockUndoNoModifiedPosition = true;
+}
+
+void UndoManager::UnLockUndoNoModifiedPosition()
+{
+ m_bLockUndoNoModifiedPosition = false;
+}
+
+SwUndo* UndoManager::GetLastUndo()
+{
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ return nullptr;
+ }
+ SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
+ return dynamic_cast<SwUndo*>(pAction);
+}
+
+void UndoManager::AppendUndo(std::unique_ptr<SwUndo> pUndo)
+{
+ AddUndoAction(std::move(pUndo));
+}
+
+void UndoManager::ClearRedo()
+{
+ return SdrUndoManager::ImplClearRedo_NoLock(TopLevel);
+}
+
+void UndoManager::DelAllUndoObj()
+{
+ ::sw::UndoGuard const undoGuard(*this);
+
+ SdrUndoManager::ClearAllLevels();
+
+ m_UndoSaveMark = MARK_INVALID;
+}
+
+SwUndoId
+UndoManager::StartUndo(SwUndoId const i_eUndoId,
+ SwRewriter const*const pRewriter)
+{
+ if (!IsUndoEnabled())
+ {
+ return SwUndoId::EMPTY;
+ }
+
+ SwUndoId const eUndoId( (i_eUndoId == SwUndoId::EMPTY) ? SwUndoId::START : i_eUndoId );
+
+ assert(SwUndoId::END != eUndoId);
+ OUString comment( (SwUndoId::START == eUndoId)
+ ? OUString("??")
+ : GetUndoComment(eUndoId) );
+ if (pRewriter)
+ {
+ assert(SwUndoId::START != eUndoId);
+ comment = pRewriter->Apply(comment);
+ }
+
+ ViewShellId nViewShellId(-1);
+ if (m_pDocShell)
+ {
+ if (const SwView* pView = m_pDocShell->GetView())
+ nViewShellId = pView->GetViewShellId();
+ }
+ SdrUndoManager::EnterListAction(comment, comment, static_cast<sal_uInt16>(eUndoId), nViewShellId);
+
+ return eUndoId;
+}
+
+SwUndoId
+UndoManager::EndUndo(SwUndoId eUndoId, SwRewriter const*const pRewriter)
+{
+ if (!IsUndoEnabled())
+ {
+ return SwUndoId::EMPTY;
+ }
+
+ if ((eUndoId == SwUndoId::EMPTY) || (SwUndoId::START == eUndoId))
+ eUndoId = SwUndoId::END;
+ OSL_ENSURE(!((SwUndoId::END == eUndoId) && pRewriter),
+ "EndUndo(): no Undo ID, but rewriter given?");
+
+ SfxUndoAction *const pLastUndo(
+ (0 == SdrUndoManager::GetUndoActionCount())
+ ? nullptr : SdrUndoManager::GetUndoAction() );
+
+ int const nCount = LeaveListAction();
+
+ if (nCount) // otherwise: empty list action not inserted!
+ {
+ assert(pLastUndo);
+ assert(SwUndoId::START != eUndoId);
+ auto pListAction = dynamic_cast<SfxListUndoAction*>(SdrUndoManager::GetUndoAction());
+ assert(pListAction);
+ if (SwUndoId::END != eUndoId)
+ {
+ OSL_ENSURE(static_cast<SwUndoId>(pListAction->GetId()) == eUndoId,
+ "EndUndo(): given ID different from StartUndo()");
+ // comment set by caller of EndUndo
+ OUString comment = GetUndoComment(eUndoId);
+ if (pRewriter)
+ {
+ comment = pRewriter->Apply(comment);
+ }
+ pListAction->SetComment(comment);
+ }
+ else if (SwUndoId::START != static_cast<SwUndoId>(pListAction->GetId()))
+ {
+ // comment set by caller of StartUndo: nothing to do here
+ }
+ else if (pLastUndo)
+ {
+ // comment was not set at StartUndo or EndUndo:
+ // take comment of last contained action
+ // (note that this works recursively, i.e. the last contained
+ // action may be a list action created by StartUndo/EndUndo)
+ OUString const comment(pLastUndo->GetComment());
+ pListAction->SetComment(comment);
+ }
+ else
+ {
+ OSL_ENSURE(false, "EndUndo(): no comment?");
+ }
+ }
+
+ return eUndoId;
+}
+
+/**
+ * Checks if the topmost undo action owned by pView is independent from the topmost action undo
+ * action.
+ */
+bool UndoManager::IsViewUndoActionIndependent(const SwView* pView, sal_uInt16& rOffset) const
+{
+ if (GetUndoActionCount() <= 1)
+ {
+ // Single or less undo, owned by another view.
+ return false;
+ }
+
+ if (!pView)
+ {
+ return false;
+ }
+
+ // Last undo action that doesn't belong to the view.
+ const SfxUndoAction* pTopAction = GetUndoAction();
+
+ ViewShellId nViewId = pView->GetViewShellId();
+
+ // Earlier undo action that belongs to the view, but is not the top one.
+ const SfxUndoAction* pViewAction = nullptr;
+ size_t nOffset = 0;
+ for (size_t i = 0; i < GetUndoActionCount(); ++i)
+ {
+ const SfxUndoAction* pAction = GetUndoAction(i);
+ if (pAction->GetViewShellId() == nViewId)
+ {
+ pViewAction = pAction;
+ nOffset = i;
+ break;
+ }
+ }
+
+ if (!pViewAction)
+ {
+ // Found no earlier undo action that belongs to the view.
+ return false;
+ }
+
+ auto pTopSwAction = dynamic_cast<const SwUndo*>(pTopAction);
+ if (!pTopSwAction || pTopSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ auto pViewSwAction = dynamic_cast<const SwUndo*>(pViewAction);
+ if (!pViewSwAction || pViewSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ const auto& rTopInsert = *static_cast<const SwUndoInsert*>(pTopSwAction);
+ const auto& rViewInsert = *static_cast<const SwUndoInsert*>(pViewSwAction);
+
+ for (size_t i = 0; i < GetRedoActionCount(); ++i)
+ {
+ auto pRedoAction = dynamic_cast<const SwUndo*>(GetRedoAction(i));
+ if (!pRedoAction || pViewSwAction->GetId() != SwUndoId::TYPING)
+ {
+ return false;
+ }
+
+ const auto& rRedoInsert = *static_cast<const SwUndoInsert*>(pRedoAction);
+ if (!rViewInsert.IsIndependent(rRedoInsert) && rRedoInsert.GetViewShellId() != nViewId)
+ {
+ // Dependent redo action and owned by another view.
+ return false;
+ }
+ }
+
+ if (!rViewInsert.IsIndependent(rTopInsert))
+ {
+ return false;
+ }
+
+ rOffset = nOffset;
+ return true;
+}
+
+bool
+UndoManager::GetLastUndoInfo(
+ OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const
+{
+ // this is actually expected to work on the current level,
+ // but that was really not obvious from the previous implementation...
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ return false;
+ }
+
+ SfxUndoAction *const pAction( SdrUndoManager::GetUndoAction() );
+
+ if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
+ {
+ // If another view created the undo action, prevent undoing it from this view.
+ ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
+ // Unless we know that the other view's undo action is independent from us.
+ if (pAction->GetViewShellId() != nViewShellId
+ && !IsViewUndoActionIndependent(pView, o3tl::temporary(sal_uInt16())))
+ {
+ if (o_pId)
+ {
+ *o_pId = SwUndoId::CONFLICT;
+ }
+ return false;
+ }
+ }
+
+ if (o_pStr)
+ {
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
+ *o_pId = static_cast<SwUndoId>(pListAction->GetId());
+ else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
+ *o_pId = pSwAction->GetId();
+ else
+ *o_pId = SwUndoId::EMPTY;
+ }
+
+ return true;
+}
+
+SwUndoComments_t UndoManager::GetUndoComments() const
+{
+ OSL_ENSURE(!SdrUndoManager::IsInListAction(),
+ "GetUndoComments() called while in list action?");
+
+ SwUndoComments_t ret;
+ const size_t nUndoCount(SdrUndoManager::GetUndoActionCount(TopLevel));
+ for (size_t n = 0; n < nUndoCount; ++n)
+ {
+ OUString const comment(
+ SdrUndoManager::GetUndoActionComment(n, TopLevel));
+ ret.push_back(comment);
+ }
+
+ return ret;
+}
+
+bool UndoManager::GetFirstRedoInfo(OUString *const o_pStr,
+ SwUndoId *const o_pId,
+ const SwView* pView) const
+{
+ if (!SdrUndoManager::GetRedoActionCount())
+ {
+ return false;
+ }
+
+ SfxUndoAction *const pAction( SdrUndoManager::GetRedoAction() );
+ if ( pAction == nullptr )
+ {
+ return false;
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && !m_bRepair)
+ {
+ // If another view created the undo action, prevent redoing it from this view.
+ ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
+ if (pAction->GetViewShellId() != nViewShellId)
+ {
+ if (o_pId)
+ {
+ *o_pId = SwUndoId::CONFLICT;
+ }
+ return false;
+ }
+ }
+
+ if (o_pStr)
+ {
+ *o_pStr = pAction->GetComment();
+ }
+ if (o_pId)
+ {
+ if (auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction))
+ *o_pId = static_cast<SwUndoId>(pListAction->GetId());
+ else if (auto pSwAction = dynamic_cast<const SwUndo*>(pAction))
+ *o_pId = pSwAction->GetId();
+ else
+ *o_pId = SwUndoId::EMPTY;
+ }
+
+ return true;
+}
+
+SwUndoComments_t UndoManager::GetRedoComments() const
+{
+ OSL_ENSURE(!SdrUndoManager::IsInListAction(),
+ "GetRedoComments() called while in list action?");
+
+ SwUndoComments_t ret;
+ const size_t nRedoCount(SdrUndoManager::GetRedoActionCount(TopLevel));
+ for (size_t n = 0; n < nRedoCount; ++n)
+ {
+ OUString const comment(
+ SdrUndoManager::GetRedoActionComment(n, TopLevel));
+ ret.push_back(comment);
+ }
+
+ return ret;
+}
+
+SwUndoId UndoManager::GetRepeatInfo(OUString *const o_pStr) const
+{
+ SwUndoId nRepeatId(SwUndoId::EMPTY);
+ GetLastUndoInfo(o_pStr, & nRepeatId);
+ if( SwUndoId::REPEAT_START <= nRepeatId && SwUndoId::REPEAT_END > nRepeatId )
+ {
+ return nRepeatId;
+ }
+ if (o_pStr) // not repeatable -> clear comment
+ {
+ o_pStr->clear();
+ }
+ return SwUndoId::EMPTY;
+}
+
+SwUndo * UndoManager::RemoveLastUndo()
+{
+ if (SdrUndoManager::GetRedoActionCount() ||
+ SdrUndoManager::GetRedoActionCount(TopLevel))
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): there are Redo actions?");
+ return nullptr;
+ }
+ if (!SdrUndoManager::GetUndoActionCount())
+ {
+ OSL_ENSURE(false, "RemoveLastUndoAction(): no Undo actions");
+ return nullptr;
+ }
+ SfxUndoAction *const pLastUndo(GetUndoAction());
+ SdrUndoManager::RemoveLastUndoAction();
+ return dynamic_cast<SwUndo *>(pLastUndo);
+}
+
+// SfxUndoManager
+
+void UndoManager::AddUndoAction(std::unique_ptr<SfxUndoAction> pAction, bool bTryMerge)
+{
+ SwUndo *const pUndo( dynamic_cast<SwUndo *>(pAction.get()) );
+ if (pUndo)
+ {
+ if (RedlineFlags::NONE == pUndo->GetRedlineFlags())
+ {
+ pUndo->SetRedlineFlags( m_rRedlineAccess.GetRedlineFlags() );
+ }
+ if (m_isAddWithIgnoreRepeat)
+ {
+ pUndo->IgnoreRepeat();
+ }
+ }
+ SdrUndoManager::AddUndoAction(std::move(pAction), bTryMerge);
+ if (m_pDocShell)
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst( m_pDocShell );
+ while ( pViewFrame )
+ {
+ pViewFrame->GetBindings().Invalidate( SID_UNDO );
+ pViewFrame->GetBindings().Invalidate( SID_REDO );
+ pViewFrame = SfxViewFrame::GetNext( *pViewFrame, m_pDocShell );
+ }
+ }
+
+ // if the undo nodes array is too large, delete some actions
+ while (UNDO_ACTION_LIMIT < sal_Int32(GetUndoNodes().Count()))
+ {
+ RemoveOldestUndoAction();
+ }
+}
+
+namespace {
+
+class CursorGuard
+{
+public:
+ CursorGuard(SwEditShell & rShell, bool const bSave)
+ : m_rShell(rShell)
+ , m_bSaveCursor(bSave)
+ {
+ if (m_bSaveCursor)
+ {
+ m_rShell.Push(); // prevent modification of current cursor
+ }
+ }
+ ~CursorGuard() COVERITY_NOEXCEPT_FALSE
+ {
+ if (m_bSaveCursor)
+ {
+ m_rShell.Pop(SwCursorShell::PopMode::DeleteCurrent);
+ }
+ }
+private:
+ SwEditShell & m_rShell;
+ bool const m_bSaveCursor;
+};
+
+}
+
+bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo, size_t nUndoOffset)
+{
+ SwDoc & rDoc(GetUndoNodes().GetDoc());
+
+ UnoActionContext c(& rDoc); // exception-safe StartAllAction/EndAllAction
+
+ SwEditShell *const pEditShell(rDoc.GetEditShell());
+ OSL_ENSURE(pEditShell, "sw::UndoManager needs a SwEditShell!");
+ if (!pEditShell)
+ {
+ throw uno::RuntimeException();
+ }
+
+ // in case the model has controllers locked, the Undo should not
+ // change the view cursors!
+ bool const bSaveCursors(pEditShell->CursorsLocked());
+ CursorGuard aCursorGuard(*pEditShell, bSaveCursors);
+ if (!bSaveCursors)
+ {
+ // (in case Undo was called via API) clear the cursors:
+ pEditShell->KillPams();
+ pEditShell->SetMark();
+ pEditShell->ClearMark();
+ }
+
+ bool bRet(false);
+
+ ::sw::UndoRedoContext context(rDoc, *pEditShell);
+ context.SetUndoOffset(nUndoOffset);
+
+ // N.B. these may throw!
+ if (UndoOrRedoType::Undo == undoOrRedo)
+ {
+ bRet = SdrUndoManager::UndoWithContext(context);
+ }
+ else
+ {
+ bRet = SdrUndoManager::RedoWithContext(context);
+ }
+
+ if (bRet)
+ {
+ // if we are at the "last save" position, the document is not modified
+ if (SdrUndoManager::HasTopUndoActionMark(m_UndoSaveMark))
+ {
+ m_rState.ResetModified();
+ }
+ else
+ {
+ m_rState.SetModified();
+ }
+ }
+
+ pEditShell->HandleUndoRedoContext(context);
+
+ return bRet;
+}
+
+bool UndoManager::Undo() { return UndoWithOffset(0); }
+
+bool UndoManager::UndoWithOffset(size_t nUndoOffset)
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Undo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Undo, nUndoOffset);
+ }
+}
+
+bool UndoManager::Redo()
+{
+ if(isTextEditActive())
+ {
+ return SdrUndoManager::Redo();
+ }
+ else
+ {
+ return impl_DoUndoRedo(UndoOrRedoType::Redo, /*nUndoOffset=*/0);
+ }
+}
+
+void UndoManager::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("swUndoManager"));
+ SdrUndoManager::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_xUndoNodes"));
+ m_xUndoNodes->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void UndoManager::EmptyActionsChanged()
+{
+ if (m_pDocShell)
+ {
+ m_pDocShell->Broadcast(SfxHint(SfxHintId::DocumentRepair));
+ }
+}
+
+/** N.B.: this does _not_ call SdrUndoManager::Repeat because it is not
+ possible to wrap a list action around it:
+ calling EnterListAction here will cause SdrUndoManager::Repeat
+ to repeat the list action!
+ */
+bool UndoManager::Repeat(::sw::RepeatContext & rContext,
+ sal_uInt16 const nRepeatCount)
+{
+ if (SdrUndoManager::IsInListAction())
+ {
+ OSL_ENSURE(false, "repeat in open list action???");
+ return false;
+ }
+ if (!SdrUndoManager::GetUndoActionCount(TopLevel))
+ {
+ return false;
+ }
+ SfxUndoAction *const pRepeatAction(GetUndoAction());
+ assert(pRepeatAction);
+ if (!pRepeatAction->CanRepeat(rContext))
+ {
+ return false;
+ }
+
+ OUString const comment(pRepeatAction->GetComment());
+ OUString const rcomment(pRepeatAction->GetRepeatComment(rContext));
+ SwUndoId nId;
+ if (auto const* const pSwAction = dynamic_cast<SwUndo*>(pRepeatAction))
+ nId = pSwAction->GetId();
+ else if (auto const* const pListAction = dynamic_cast<SfxListUndoAction*>(pRepeatAction))
+ nId = static_cast<SwUndoId>(pListAction->GetId());
+ else
+ return false;
+ if (DoesUndo())
+ {
+ ViewShellId nViewShellId(-1);
+ if (m_pDocShell)
+ {
+ if (const SwView* pView = m_pDocShell->GetView())
+ nViewShellId = pView->GetViewShellId();
+ }
+ EnterListAction(comment, rcomment, static_cast<sal_uInt16>(nId), nViewShellId);
+ }
+
+ SwPaM* pTmp = rContext.m_pCurrentPaM;
+ for(SwPaM& rPaM : rContext.GetRepeatPaM().GetRingContainer())
+ { // iterate over ring
+ rContext.m_pCurrentPaM = &rPaM;
+ if (DoesUndo() && & rPaM != pTmp)
+ {
+ m_isAddWithIgnoreRepeat = true;
+ }
+ for (sal_uInt16 nRptCnt = nRepeatCount; nRptCnt > 0; --nRptCnt)
+ {
+ pRepeatAction->Repeat(rContext);
+ }
+ if (DoesUndo() && & rPaM != pTmp)
+ {
+ m_isAddWithIgnoreRepeat = false;
+ }
+ rContext.m_bDeleteRepeated = false; // reset for next PaM
+ }
+ rContext.m_pCurrentPaM = pTmp;
+
+ if (DoesUndo())
+ {
+ LeaveListAction();
+ }
+ return true;
+}
+
+} // namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/rolbck.cxx b/sw/source/core/undo/rolbck.cxx
new file mode 100644
index 000000000..b46645b91
--- /dev/null
+++ b/sw/source/core/undo/rolbck.cxx
@@ -0,0 +1,1537 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <rolbck.hxx>
+
+#include <libxml/xmlwriter.h>
+
+#include <svl/itemiter.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <hints.hxx>
+#include <hintids.hxx>
+#include <fmtftn.hxx>
+#include <fchrfmt.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtrfmrk.hxx>
+#include <fmtfld.hxx>
+#include <fmtpdsc.hxx>
+#include <txtfld.hxx>
+#include <txtrfmrk.hxx>
+#include <txttxmrk.hxx>
+#include <txtftn.hxx>
+#include <txtflcnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcnct.hxx>
+#include <frmfmt.hxx>
+#include <ftnidx.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <cellatr.hxx>
+#include <fldbas.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <UndoCore.hxx>
+#include <IMark.hxx>
+#include <charfmt.hxx>
+#include <strings.hrc>
+#include <bookmark.hxx>
+#include <frameformats.hxx>
+#include <memory>
+
+OUString SwHistoryHint::GetDescription() const
+{
+ return OUString();
+}
+
+void SwHistoryHint::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryHint"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_eWhichId"),
+ BAD_CAST(OString::number(m_eWhichId).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwHistorySetFormat::SwHistorySetFormat( const SfxPoolItem* pFormatHt, SwNodeOffset nNd )
+ : SwHistoryHint( HSTRY_SETFMTHNT )
+ , m_pAttr( pFormatHt->Clone() )
+ , m_nNodeIndex( nNd )
+{
+ switch ( m_pAttr->Which() )
+ {
+ case RES_PAGEDESC:
+ static_cast<SwFormatPageDesc&>(*m_pAttr).ChgDefinedIn( nullptr );
+ break;
+ case RES_PARATR_DROP:
+ static_cast<SwFormatDrop&>(*m_pAttr).ChgDefinedIn(nullptr);
+ break;
+ case RES_BOXATR_FORMULA:
+ {
+ // save formulas always in plain text
+ SwTableBoxFormula& rNew = static_cast<SwTableBoxFormula&>(*m_pAttr);
+ if ( rNew.IsIntrnlName() )
+ {
+ const SwTableBoxFormula& rOld =
+ *static_cast<const SwTableBoxFormula*>(pFormatHt);
+ const SwNode* pNd = rOld.GetNodeOfFormula();
+ if ( pNd )
+ {
+ const SwTableNode* pTableNode = pNd->FindTableNode();
+ if (pTableNode)
+ {
+ SwTableFormulaUpdate aMsgHint( &pTableNode->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ rNew.ChgDefinedIn( rOld.GetDefinedIn() );
+ rNew.ChangeState( &aMsgHint );
+ }
+ }
+ }
+ rNew.ChgDefinedIn( nullptr );
+ }
+ break;
+ }
+}
+
+OUString SwHistorySetFormat::GetDescription() const
+{
+ OUString aResult;
+
+ switch (m_pAttr->Which())
+ {
+ case RES_BREAK:
+ switch (static_cast<SvxFormatBreakItem &>(*m_pAttr).GetBreak())
+ {
+ case SvxBreak::PageBefore:
+ case SvxBreak::PageAfter:
+ case SvxBreak::PageBoth:
+ aResult = SwResId(STR_UNDO_PAGEBREAKS);
+
+ break;
+ case SvxBreak::ColumnBefore:
+ case SvxBreak::ColumnAfter:
+ case SvxBreak::ColumnBoth:
+ aResult = SwResId(STR_UNDO_COLBRKS);
+
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return aResult;
+}
+
+void SwHistorySetFormat::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistorySetFormat"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nNodeIndex"),
+ BAD_CAST(OString::number(sal_Int32(m_nNodeIndex)).getStr()));
+ SwHistoryHint::dumpAsXml(pWriter);
+
+ if (m_pAttr)
+ {
+ m_pAttr->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwHistorySetFormat::SetInDoc( SwDoc* pDoc, bool bTmpSet )
+{
+ SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
+ if ( pNode->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNode)->SetAttr( *m_pAttr );
+ }
+ else if ( pNode->IsTableNode() )
+ {
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->SetFormatAttr(
+ *m_pAttr );
+ }
+ else if ( pNode->IsStartNode() && (SwTableBoxStartNode ==
+ static_cast<SwStartNode*>(pNode)->GetStartNodeType()) )
+ {
+ SwTableNode* pTNd = pNode->FindTableNode();
+ if ( pTNd )
+ {
+ SwTableBox* pBox = pTNd->GetTable().GetTableBox( m_nNodeIndex );
+ if (pBox)
+ {
+ pBox->ClaimFrameFormat()->SetFormatAttr( *m_pAttr );
+ }
+ }
+ }
+
+ if ( !bTmpSet )
+ {
+ m_pAttr.reset();
+ }
+}
+
+SwHistorySetFormat::~SwHistorySetFormat()
+{
+}
+
+SwHistoryResetFormat::SwHistoryResetFormat(const SfxPoolItem* pFormatHt, SwNodeOffset nNodeIdx)
+ : SwHistoryHint( HSTRY_RESETFMTHNT )
+ , m_nNodeIndex( nNodeIdx )
+ , m_nWhich( pFormatHt->Which() )
+{
+}
+
+void SwHistoryResetFormat::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
+ if ( pNode->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNode)->ResetAttr( m_nWhich );
+ }
+ else if ( pNode->IsTableNode() )
+ {
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->
+ ResetFormatAttr( m_nWhich );
+ }
+}
+
+SwHistorySetText::SwHistorySetText( SwTextAttr* pTextHt, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_SETTXTHNT )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextHt->GetStart() )
+ , m_nEnd( pTextHt->GetAnyEnd() )
+ , m_bFormatIgnoreStart(pTextHt->IsFormatIgnoreStart())
+ , m_bFormatIgnoreEnd (pTextHt->IsFormatIgnoreEnd ())
+{
+ // Caution: the following attributes generate no format attributes:
+ // - NoLineBreak, NoHyphen, Inserted, Deleted
+ // These cases must be handled separately !!!
+
+ // a little bit complicated but works: first assign a copy of the
+ // default value and afterwards the values from text attribute
+ if ( RES_TXTATR_CHARFMT == pTextHt->Which() )
+ {
+ m_pAttr.reset( new SwFormatCharFormat( pTextHt->GetCharFormat().GetCharFormat() ) );
+ }
+ else
+ {
+ m_pAttr.reset( pTextHt->GetAttr().Clone() );
+ }
+}
+
+SwHistorySetText::~SwHistorySetText()
+{
+}
+
+void SwHistorySetText::SetInDoc( SwDoc* pDoc, bool )
+{
+ if (!m_pAttr)
+ return;
+
+ if ( RES_TXTATR_CHARFMT == m_pAttr->Which() )
+ {
+ // ask the Doc if the CharFormat still exists
+ if (!pDoc->GetCharFormats()->ContainsFormat(static_cast<SwFormatCharFormat&>(*m_pAttr).GetCharFormat()))
+ return; // do not set, format does not exist
+ }
+
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetText::SetInDoc: not a TextNode" );
+
+ if ( !pTextNd )
+ return;
+
+ SwTextAttr *const pAttr = pTextNd->InsertItem(*m_pAttr, m_nStart, m_nEnd,
+ SetAttrMode::NOTXTATRCHR |
+ SetAttrMode::NOHINTADJUST );
+ // shouldn't be possible to hit any error/merging path from here
+ assert(pAttr);
+ if (m_bFormatIgnoreStart)
+ {
+ pAttr->SetFormatIgnoreStart(true);
+ }
+ if (m_bFormatIgnoreEnd)
+ {
+ pAttr->SetFormatIgnoreEnd(true);
+ }
+}
+
+SwHistorySetTextField::SwHistorySetTextField( const SwTextField* pTextField, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_SETTXTFLDHNT )
+ , m_pField( new SwFormatField( *pTextField->GetFormatField().GetField() ) )
+{
+ // only copy if not Sys-FieldType
+ SwDoc& rDoc = pTextField->GetTextNode().GetDoc();
+
+ m_nFieldWhich = m_pField->GetField()->GetTyp()->Which();
+ if (m_nFieldWhich == SwFieldIds::Database ||
+ m_nFieldWhich == SwFieldIds::User ||
+ m_nFieldWhich == SwFieldIds::SetExp ||
+ m_nFieldWhich == SwFieldIds::Dde ||
+ !rDoc.getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich ))
+ {
+ m_pFieldType = m_pField->GetField()->GetTyp()->Copy();
+ m_pField->GetField()->ChgTyp( m_pFieldType.get() ); // change field type
+ }
+ m_nNodeIndex = nNodePos;
+ m_nPos = pTextField->GetStart();
+}
+
+OUString SwHistorySetTextField::GetDescription() const
+{
+ return m_pField->GetField()->GetDescription();
+}
+
+SwHistorySetTextField::~SwHistorySetTextField()
+{
+}
+
+void SwHistorySetTextField::SetInDoc( SwDoc* pDoc, bool )
+{
+ if (!m_pField)
+ return;
+
+ SwFieldType* pNewFieldType = m_pFieldType.get();
+ if ( !pNewFieldType )
+ {
+ pNewFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType( m_nFieldWhich );
+ }
+ else
+ {
+ // register type with the document
+ pNewFieldType = pDoc->getIDocumentFieldsAccess().InsertFieldType( *m_pFieldType );
+ }
+
+ m_pField->GetField()->ChgTyp( pNewFieldType ); // change field type
+
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetTextField: no TextNode" );
+
+ if ( pTextNd )
+ {
+ pTextNd->InsertItem( *m_pField, m_nPos, m_nPos,
+ SetAttrMode::NOTXTATRCHR );
+ }
+}
+
+SwHistorySetRefMark::SwHistorySetRefMark( const SwTextRefMark* pTextHt, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_SETREFMARKHNT )
+ , m_RefName( pTextHt->GetRefMark().GetRefName() )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextHt->GetStart() )
+ , m_nEnd( pTextHt->GetAnyEnd() )
+{
+}
+
+void SwHistorySetRefMark::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetRefMark: no TextNode" );
+ if ( !pTextNd )
+ return;
+
+ SwFormatRefMark aRefMark( m_RefName );
+
+ // if a reference mark without an end already exists here: must not insert!
+ if ( m_nStart != m_nEnd ||
+ !pTextNd->GetTextAttrForCharAt( m_nStart, RES_TXTATR_REFMARK ) )
+ {
+ pTextNd->InsertItem( aRefMark, m_nStart, m_nEnd,
+ SetAttrMode::NOTXTATRCHR );
+ }
+}
+
+SwHistorySetTOXMark::SwHistorySetTOXMark( const SwTextTOXMark* pTextHt, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_SETTOXMARKHNT )
+ , m_TOXMark( pTextHt->GetTOXMark() )
+ , m_TOXName( m_TOXMark.GetTOXType()->GetTypeName() )
+ , m_eTOXTypes( m_TOXMark.GetTOXType()->GetType() )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextHt->GetStart() )
+ , m_nEnd( pTextHt->GetAnyEnd() )
+{
+ static_cast<SvtListener*>(&m_TOXMark)->EndListeningAll();
+}
+
+SwTOXType* SwHistorySetTOXMark::GetSwTOXType(SwDoc& rDoc, TOXTypes eTOXTypes, const OUString& rTOXName)
+{
+ // search for respective TOX type
+ const sal_uInt16 nCnt = rDoc.GetTOXTypeCount(eTOXTypes);
+ SwTOXType* pToxType = nullptr;
+ for ( sal_uInt16 n = 0; n < nCnt; ++n )
+ {
+ pToxType = const_cast<SwTOXType*>(rDoc.GetTOXType(eTOXTypes, n));
+ if (pToxType->GetTypeName() == rTOXName)
+ break;
+ pToxType = nullptr;
+ }
+
+ if ( !pToxType ) // TOX type not found, create new
+ {
+ pToxType = const_cast<SwTOXType*>(
+ rDoc.InsertTOXType(SwTOXType(rDoc, eTOXTypes, rTOXName)));
+ }
+
+ return pToxType;
+}
+
+void SwHistorySetTOXMark::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetTOXMark: no TextNode" );
+ if ( !pTextNd )
+ return;
+
+ SwTOXType* pToxType = GetSwTOXType(*pDoc, m_eTOXTypes, m_TOXName);
+
+ SwTOXMark aNew( m_TOXMark );
+ aNew.RegisterToTOXType( *pToxType );
+
+ pTextNd->InsertItem( aNew, m_nStart, m_nEnd,
+ SetAttrMode::NOTXTATRCHR );
+}
+
+bool SwHistorySetTOXMark::IsEqual( const SwTOXMark& rCmp ) const
+{
+ return m_TOXName == rCmp.GetTOXType()->GetTypeName() &&
+ m_eTOXTypes == rCmp.GetTOXType()->GetType() &&
+ m_TOXMark.GetAlternativeText() == rCmp.GetAlternativeText() &&
+ ( (TOX_INDEX == m_eTOXTypes)
+ ? ( m_TOXMark.GetPrimaryKey() == rCmp.GetPrimaryKey() &&
+ m_TOXMark.GetSecondaryKey() == rCmp.GetSecondaryKey() )
+ : m_TOXMark.GetLevel() == rCmp.GetLevel()
+ );
+}
+
+SwHistoryResetText::SwHistoryResetText( sal_uInt16 nWhich,
+ sal_Int32 nAttrStart, sal_Int32 nAttrEnd, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_RESETTXTHNT )
+ , m_nNodeIndex( nNodePos ), m_nStart( nAttrStart ), m_nEnd( nAttrEnd )
+ , m_nAttr( nWhich )
+{
+}
+
+void SwHistoryResetText::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistoryResetText: no TextNode" );
+ if ( pTextNd )
+ {
+ pTextNd->DeleteAttributes( m_nAttr, m_nStart, m_nEnd );
+ }
+}
+
+SwHistorySetFootnote::SwHistorySetFootnote( SwTextFootnote* pTextFootnote, SwNodeOffset nNodePos )
+ : SwHistoryHint( HSTRY_SETFTNHNT )
+ , m_pUndo( new SwUndoSaveSection )
+ , m_FootnoteNumber( pTextFootnote->GetFootnote().GetNumStr() )
+ , m_nNodeIndex( nNodePos )
+ , m_nStart( pTextFootnote->GetStart() )
+ , m_bEndNote( pTextFootnote->GetFootnote().IsEndNote() )
+{
+ OSL_ENSURE( pTextFootnote->GetStartNode(),
+ "SwHistorySetFootnote: Footnote without Section" );
+
+ // keep the old NodePos (because who knows what later will be saved/deleted
+ // in SaveSection)
+ SwDoc& rDoc = const_cast<SwDoc&>(pTextFootnote->GetTextNode().GetDoc());
+ SwNode* pSaveNd = rDoc.GetNodes()[ m_nNodeIndex ];
+
+ // keep pointer to StartNode of FootnoteSection and reset its attribute for now
+ // (as a result, its/all Frames will be deleted automatically)
+ SwNodeIndex aSttIdx( *pTextFootnote->GetStartNode() );
+ pTextFootnote->SetStartNode( nullptr, false );
+
+ m_pUndo->SaveSection( aSttIdx );
+ m_nNodeIndex = pSaveNd->GetIndex();
+}
+
+SwHistorySetFootnote::SwHistorySetFootnote( const SwTextFootnote &rTextFootnote )
+ : SwHistoryHint( HSTRY_SETFTNHNT )
+ , m_FootnoteNumber( rTextFootnote.GetFootnote().GetNumStr() )
+ , m_nNodeIndex( SwTextFootnote_GetIndex( (&rTextFootnote) ) )
+ , m_nStart( rTextFootnote.GetStart() )
+ , m_bEndNote( rTextFootnote.GetFootnote().IsEndNote() )
+{
+ OSL_ENSURE( rTextFootnote.GetStartNode(),
+ "SwHistorySetFootnote: Footnote without Section" );
+}
+
+OUString SwHistorySetFootnote::GetDescription() const
+{
+ return SwResId(STR_FOOTNOTE);
+}
+
+SwHistorySetFootnote::~SwHistorySetFootnote()
+{
+}
+
+void SwHistorySetFootnote::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwTextNode * pTextNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "SwHistorySetFootnote: no TextNode" );
+ if ( !pTextNd )
+ return;
+
+ if (m_pUndo)
+ {
+ // set the footnote in the TextNode
+ SwFormatFootnote aTemp( m_bEndNote );
+ SwFormatFootnote& rNew = const_cast<SwFormatFootnote&>(
+ pDoc->GetAttrPool().Put(aTemp) );
+ if ( !m_FootnoteNumber.isEmpty() )
+ {
+ rNew.SetNumStr( m_FootnoteNumber );
+ }
+ SwTextFootnote* pTextFootnote = new SwTextFootnote( rNew, m_nStart );
+
+ // create the section of the Footnote
+ SwNodeIndex aIdx( *pTextNd );
+ m_pUndo->RestoreSection( pDoc, &aIdx, SwFootnoteStartNode );
+ pTextFootnote->SetStartNode( &aIdx );
+ if ( m_pUndo->GetHistory() )
+ {
+ // create frames only now
+ m_pUndo->GetHistory()->Rollback( pDoc );
+ }
+
+ pTextNd->InsertHint( pTextFootnote );
+ }
+ else
+ {
+ SwTextFootnote * const pFootnote =
+ static_cast<SwTextFootnote*>(
+ pTextNd->GetTextAttrForCharAt( m_nStart ));
+ assert(pFootnote);
+ SwFormatFootnote &rFootnote = const_cast<SwFormatFootnote&>(pFootnote->GetFootnote());
+ rFootnote.SetNumStr( m_FootnoteNumber );
+ if ( rFootnote.IsEndNote() != m_bEndNote )
+ {
+ rFootnote.SetEndNote( m_bEndNote );
+ pFootnote->CheckCondColl();
+ }
+ }
+}
+
+SwHistoryChangeFormatColl::SwHistoryChangeFormatColl( SwFormatColl* pFormatColl, SwNodeOffset nNd,
+ SwNodeType nNodeWhich )
+ : SwHistoryHint( HSTRY_CHGFMTCOLL )
+ , m_pColl( pFormatColl )
+ , m_nNodeIndex( nNd )
+ , m_nNodeType( nNodeWhich )
+{
+}
+
+void SwHistoryChangeFormatColl::SetInDoc( SwDoc* pDoc, bool )
+{
+ SwContentNode * pContentNd = pDoc->GetNodes()[ m_nNodeIndex ]->GetContentNode();
+ OSL_ENSURE( pContentNd, "SwHistoryChangeFormatColl: no ContentNode" );
+
+ // before setting the format, check if it is still available in the
+ // document. if it has been deleted, there is no undo!
+ if ( !(pContentNd && m_nNodeType == pContentNd->GetNodeType()) )
+ return;
+
+ if ( SwNodeType::Text == m_nNodeType )
+ {
+ if (pDoc->GetTextFormatColls()->IsAlive(static_cast<SwTextFormatColl *>(m_pColl)))
+ {
+ pContentNd->ChgFormatColl( m_pColl );
+ }
+ }
+ else if (pDoc->GetGrfFormatColls()->IsAlive(static_cast<SwGrfFormatColl *>(m_pColl)))
+ {
+ pContentNd->ChgFormatColl( m_pColl );
+ }
+}
+
+SwHistoryTextFlyCnt::SwHistoryTextFlyCnt( SwFrameFormat* const pFlyFormat )
+ : SwHistoryHint( HSTRY_FLYCNT )
+ , m_pUndo( new SwUndoDelLayFormat( pFlyFormat ) )
+{
+ OSL_ENSURE( pFlyFormat, "SwHistoryTextFlyCnt: no Format" );
+ m_pUndo->ChgShowSel( false );
+}
+
+SwHistoryTextFlyCnt::~SwHistoryTextFlyCnt()
+{
+}
+
+void SwHistoryTextFlyCnt::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::IShellCursorSupplier *const pISCS(pDoc->GetIShellCursorSupplier());
+ assert(pISCS);
+ ::sw::UndoRedoContext context(*pDoc, *pISCS);
+ m_pUndo->UndoImpl(context);
+}
+
+void SwHistoryTextFlyCnt::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistoryTextFlyCnt"));
+ SwHistoryHint::dumpAsXml(pWriter);
+
+ if (m_pUndo)
+ {
+ m_pUndo->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwHistoryBookmark::SwHistoryBookmark(
+ const ::sw::mark::IMark& rBkmk,
+ bool bSavePos,
+ bool bSaveOtherPos)
+ : SwHistoryHint(HSTRY_BOOKMARK)
+ , m_aName(rBkmk.GetName())
+ , m_bHidden(false)
+ , m_nNode(bSavePos ?
+ rBkmk.GetMarkPos().nNode.GetIndex() : SwNodeOffset(0))
+ , m_nOtherNode(bSaveOtherPos ?
+ rBkmk.GetOtherMarkPos().nNode.GetIndex() : SwNodeOffset(0))
+ , m_nContent(bSavePos ?
+ rBkmk.GetMarkPos().nContent.GetIndex() : 0)
+ , m_nOtherContent(bSaveOtherPos ?
+ rBkmk.GetOtherMarkPos().nContent.GetIndex() :0)
+ , m_bSavePos(bSavePos)
+ , m_bSaveOtherPos(bSaveOtherPos)
+ , m_bHadOtherPos(rBkmk.IsExpanded())
+ , m_eBkmkType(IDocumentMarkAccess::GetType(rBkmk))
+{
+ const ::sw::mark::IBookmark* const pBookmark = dynamic_cast< const ::sw::mark::IBookmark* >(&rBkmk);
+ if(!pBookmark)
+ return;
+
+ m_aKeycode = pBookmark->GetKeyCode();
+ m_aShortName = pBookmark->GetShortName();
+ m_bHidden = pBookmark->IsHidden();
+ m_aHideCondition = pBookmark->GetHideCondition();
+
+ ::sfx2::Metadatable const*const pMetadatable(
+ dynamic_cast< ::sfx2::Metadatable const* >(pBookmark));
+ if (pMetadatable)
+ {
+ m_pMetadataUndo = pMetadatable->CreateUndo();
+ }
+}
+
+void SwHistoryBookmark::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+ std::optional<SwPaM> pPam;
+ ::sw::mark::IMark* pMark = nullptr;
+
+ if(m_bSavePos)
+ {
+ SwContentNode* const pContentNd = rNds[m_nNode]->GetContentNode();
+ OSL_ENSURE(pContentNd,
+ "<SwHistoryBookmark::SetInDoc(..)>"
+ " - wrong node for a mark");
+
+ // #111660# don't crash when nNode1 doesn't point to content node.
+ if(pContentNd)
+ pPam.emplace(*pContentNd, m_nContent);
+ }
+ else
+ {
+ pMark = *pMarkAccess->findMark(m_aName);
+ pPam.emplace(pMark->GetMarkPos());
+ }
+
+ if(m_bSaveOtherPos)
+ {
+ SwContentNode* const pContentNd = rNds[m_nOtherNode]->GetContentNode();
+ OSL_ENSURE(pContentNd,
+ "<SwHistoryBookmark::SetInDoc(..)>"
+ " - wrong node for a mark");
+
+ if (pPam && pContentNd)
+ {
+ pPam->SetMark();
+ pPam->GetMark()->nNode = m_nOtherNode;
+ pPam->GetMark()->nContent.Assign(pContentNd, m_nOtherContent);
+ }
+ }
+ else if(m_bHadOtherPos)
+ {
+ if(!pMark)
+ pMark = *pMarkAccess->findMark(m_aName);
+ OSL_ENSURE(pMark->IsExpanded(),
+ "<SwHistoryBookmark::SetInDoc(..)>"
+ " - missing pos on old mark");
+ pPam->SetMark();
+ *pPam->GetMark() = pMark->GetOtherMarkPos();
+ }
+
+ if (!pPam)
+ return;
+
+ if ( pMark != nullptr )
+ {
+ pMarkAccess->deleteMark( pMark );
+ }
+ ::sw::mark::IBookmark* const pBookmark =
+ dynamic_cast<::sw::mark::IBookmark*>(
+ pMarkAccess->makeMark(*pPam, m_aName, m_eBkmkType, sw::mark::InsertMode::New));
+ if ( pBookmark == nullptr )
+ return;
+
+ pBookmark->SetKeyCode(m_aKeycode);
+ pBookmark->SetShortName(m_aShortName);
+ pBookmark->Hide(m_bHidden);
+ pBookmark->SetHideCondition(m_aHideCondition);
+
+ if (m_pMetadataUndo)
+ {
+ ::sfx2::Metadatable * const pMeta(
+ dynamic_cast< ::sfx2::Metadatable* >(pBookmark));
+ OSL_ENSURE(pMeta, "metadata undo, but not metadatable?");
+ if (pMeta)
+ {
+ pMeta->RestoreMetadata(m_pMetadataUndo);
+ }
+ }
+}
+
+bool SwHistoryBookmark::IsEqualBookmark(const ::sw::mark::IMark& rBkmk)
+{
+ return m_nNode == rBkmk.GetMarkPos().nNode.GetIndex()
+ && m_nContent == rBkmk.GetMarkPos().nContent.GetIndex()
+ && m_aName == rBkmk.GetName();
+}
+
+SwHistoryNoTextFieldmark::SwHistoryNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
+ : SwHistoryHint(HSTRY_NOTEXTFIELDMARK)
+ , m_sType(rFieldMark.GetFieldname())
+ , m_nNode(rFieldMark.GetMarkStart().nNode.GetIndex())
+ , m_nContent(rFieldMark.GetMarkStart().nContent.GetIndex())
+{
+}
+
+void SwHistoryNoTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+ std::optional<SwPaM> pPam;
+
+ const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
+ if(pContentNd)
+ pPam.emplace(*pContentNd, m_nContent);
+
+ if (pPam)
+ {
+ IDocumentMarkAccess* pMarkAccess = pDoc->getIDocumentMarkAccess();
+ pMarkAccess->makeNoTextFieldBookmark(*pPam, OUString(), m_sType);
+ }
+}
+
+void SwHistoryNoTextFieldmark::ResetInDoc(SwDoc& rDoc)
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNodes& rNds = rDoc.GetNodes();
+ std::optional<SwPaM> pPam;
+
+ const SwContentNode* pContentNd = rNds[m_nNode]->GetContentNode();
+ assert(pContentNd);
+ pPam.emplace(*pContentNd, m_nContent);
+
+ if (pPam)
+ {
+ IDocumentMarkAccess* pMarkAccess = rDoc.getIDocumentMarkAccess();
+ pMarkAccess->deleteFieldmarkAt(*pPam->GetPoint());
+ }
+}
+
+SwHistoryTextFieldmark::SwHistoryTextFieldmark(const ::sw::mark::IFieldmark& rFieldMark)
+ : SwHistoryHint(HSTRY_TEXTFIELDMARK)
+ , m_sName(rFieldMark.GetName())
+ , m_sType(rFieldMark.GetFieldname())
+ , m_nStartNode(rFieldMark.GetMarkStart().nNode.GetIndex())
+ , m_nStartContent(rFieldMark.GetMarkStart().nContent.GetIndex())
+ , m_nEndNode(rFieldMark.GetMarkEnd().nNode.GetIndex())
+ , m_nEndContent(rFieldMark.GetMarkEnd().nContent.GetIndex())
+{
+ SwPosition const sepPos(sw::mark::FindFieldSep(rFieldMark));
+ m_nSepNode = sepPos.nNode.GetIndex();
+ m_nSepContent = sepPos.nContent.GetIndex();
+}
+
+void SwHistoryTextFieldmark::SetInDoc(SwDoc* pDoc, bool)
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNodes& rNds = pDoc->GetNodes();
+
+ assert(rNds[m_nStartNode]->IsContentNode());
+ assert(rNds[m_nEndNode]->IsContentNode());
+ assert(rNds[m_nSepNode]->IsContentNode());
+
+ SwPaM const pam(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent,
+ *rNds[m_nEndNode]->GetContentNode(),
+ // subtract 1 for the CH_TXT_ATR_FIELDEND itself,
+ // plus more if same node as other CH_TXT_ATR
+ m_nStartNode == m_nEndNode
+ ? (m_nEndContent - 3)
+ : m_nSepNode == m_nEndNode
+ ? (m_nEndContent - 2)
+ : (m_nEndContent - 1));
+ SwPosition const sepPos(*rNds[m_nSepNode]->GetContentNode(),
+ m_nStartNode == m_nSepNode ? (m_nSepContent - 1) : m_nSepContent);
+
+ IDocumentMarkAccess & rMarksAccess(*pDoc->getIDocumentMarkAccess());
+ rMarksAccess.makeFieldBookmark(pam, m_sName, m_sType, &sepPos);
+}
+
+void SwHistoryTextFieldmark::ResetInDoc(SwDoc& rDoc)
+{
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNodes& rNds = rDoc.GetNodes();
+
+ assert(rNds[m_nStartNode]->IsContentNode());
+ assert(rNds[m_nEndNode]->IsContentNode());
+ assert(rNds[m_nSepNode]->IsContentNode());
+
+ SwPosition const pos(*rNds[m_nStartNode]->GetContentNode(), m_nStartContent);
+
+ IDocumentMarkAccess & rMarksAccess(*rDoc.getIDocumentMarkAccess());
+ rMarksAccess.deleteFieldmarkAt(pos);
+}
+
+SwHistorySetAttrSet::SwHistorySetAttrSet( const SfxItemSet& rSet,
+ SwNodeOffset nNodePos, const o3tl::sorted_vector<sal_uInt16> &rSetArr )
+ : SwHistoryHint( HSTRY_SETATTRSET )
+ , m_OldSet( rSet )
+ , m_ResetArray( 0, 4 )
+ , m_nNodeIndex( nNodePos )
+{
+ SfxItemIter aIter( m_OldSet ), aOrigIter( rSet );
+ const SfxPoolItem* pItem = aIter.GetCurItem(),
+ * pOrigItem = aOrigIter.GetCurItem();
+ while (pItem && pOrigItem)
+ {
+ if( !rSetArr.count( pOrigItem->Which() ))
+ {
+ m_ResetArray.push_back( pOrigItem->Which() );
+ m_OldSet.ClearItem( pOrigItem->Which() );
+ }
+ else
+ {
+ switch ( pItem->Which() )
+ {
+ case RES_PAGEDESC:
+ static_cast<SwFormatPageDesc*>(
+ const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn( nullptr );
+ break;
+
+ case RES_PARATR_DROP:
+ static_cast<SwFormatDrop*>(
+ const_cast<SfxPoolItem*>(pItem))->ChgDefinedIn(nullptr);
+ break;
+
+ case RES_BOXATR_FORMULA:
+ {
+ // When a formula is set, never save the value. It
+ // possibly must be recalculated!
+ // Save formulas always in plain text
+ m_OldSet.ClearItem( RES_BOXATR_VALUE );
+
+ SwTableBoxFormula& rNew =
+ *static_cast<SwTableBoxFormula*>(
+ const_cast<SfxPoolItem*>(pItem));
+ if ( rNew.IsIntrnlName() )
+ {
+ const SwTableBoxFormula& rOld =
+ rSet.Get( RES_BOXATR_FORMULA );
+ const SwNode* pNd = rOld.GetNodeOfFormula();
+ if ( pNd )
+ {
+ const SwTableNode* pTableNode
+ = pNd->FindTableNode();
+ if (pTableNode)
+ {
+ SwTableFormulaUpdate aMsgHint(
+ &pTableNode->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ rNew.ChgDefinedIn( rOld.GetDefinedIn() );
+ rNew.ChangeState( &aMsgHint );
+ }
+ }
+ }
+ rNew.ChgDefinedIn( nullptr );
+ }
+ break;
+ }
+ }
+
+ pItem = aIter.NextItem();
+ pOrigItem = aOrigIter.NextItem();
+ }
+}
+
+void SwHistorySetAttrSet::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ SwNode * pNode = pDoc->GetNodes()[ m_nNodeIndex ];
+ if ( pNode->IsContentNode() )
+ {
+ static_cast<SwContentNode*>(pNode)->SetAttr( m_OldSet );
+ if ( !m_ResetArray.empty() )
+ {
+ static_cast<SwContentNode*>(pNode)->ResetAttr( m_ResetArray );
+ }
+ }
+ else if ( pNode->IsTableNode() )
+ {
+ SwFormat& rFormat =
+ *static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat();
+ rFormat.SetFormatAttr( m_OldSet );
+ if ( !m_ResetArray.empty() )
+ {
+ rFormat.ResetFormatAttr( m_ResetArray.front() );
+ }
+ }
+}
+
+SwHistoryChangeFlyAnchor::SwHistoryChangeFlyAnchor( SwFrameFormat& rFormat )
+ : SwHistoryHint( HSTRY_CHGFLYANCHOR )
+ , m_rFormat( rFormat )
+ , m_nOldNodeIndex( rFormat.GetAnchor().GetContentAnchor()->nNode.GetIndex() )
+ , m_nOldContentIndex( (RndStdIds::FLY_AT_CHAR == rFormat.GetAnchor().GetAnchorId())
+ ? rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex()
+ : COMPLETE_STRING )
+{
+}
+
+void SwHistoryChangeFlyAnchor::SetInDoc( SwDoc* pDoc, bool )
+{
+ ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
+
+ if (!pDoc->GetSpzFrameFormats()->IsAlive(&m_rFormat)) // Format does still exist
+ return;
+
+ SwFormatAnchor aTmp( m_rFormat.GetAnchor() );
+
+ SwNode* pNd = pDoc->GetNodes()[ m_nOldNodeIndex ];
+ SwContentNode* pCNd = pNd->GetContentNode();
+ SwPosition aPos( *pNd );
+ if ( COMPLETE_STRING != m_nOldContentIndex )
+ {
+ OSL_ENSURE(pCNd, "SwHistoryChangeFlyAnchor: no ContentNode");
+ if (pCNd)
+ {
+ aPos.nContent.Assign( pCNd, m_nOldContentIndex );
+ }
+ }
+ aTmp.SetAnchor( &aPos );
+
+ // so the Layout does not get confused
+ if (!pCNd || !pCNd->getLayoutFrame(pDoc->getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr))
+ {
+ m_rFormat.DelFrames();
+ }
+
+ m_rFormat.SetFormatAttr( aTmp );
+}
+
+SwHistoryChangeFlyChain::SwHistoryChangeFlyChain( SwFlyFrameFormat& rFormat,
+ const SwFormatChain& rAttr )
+ : SwHistoryHint( HSTRY_CHGFLYCHAIN )
+ , m_pPrevFormat( rAttr.GetPrev() )
+ , m_pNextFormat( rAttr.GetNext() )
+ , m_pFlyFormat( &rFormat )
+{
+}
+
+void SwHistoryChangeFlyChain::SetInDoc( SwDoc* pDoc, bool )
+{
+ if (!pDoc->GetSpzFrameFormats()->IsAlive(m_pFlyFormat))
+ return;
+
+ SwFormatChain aChain;
+
+ if (m_pPrevFormat &&
+ pDoc->GetSpzFrameFormats()->IsAlive(m_pPrevFormat))
+ {
+ aChain.SetPrev( m_pPrevFormat );
+ SwFormatChain aTmp( m_pPrevFormat->GetChain() );
+ aTmp.SetNext( m_pFlyFormat );
+ m_pPrevFormat->SetFormatAttr( aTmp );
+ }
+
+ if (m_pNextFormat &&
+ pDoc->GetSpzFrameFormats()->IsAlive(m_pNextFormat))
+ {
+ aChain.SetNext( m_pNextFormat );
+ SwFormatChain aTmp( m_pNextFormat->GetChain() );
+ aTmp.SetPrev( m_pFlyFormat );
+ m_pNextFormat->SetFormatAttr( aTmp );
+ }
+
+ if ( aChain.GetNext() || aChain.GetPrev() )
+ {
+ m_pFlyFormat->SetFormatAttr( aChain );
+ }
+}
+
+// -> #i27615#
+SwHistoryChangeCharFormat::SwHistoryChangeCharFormat(const SfxItemSet & rSet,
+ const OUString & sFormat)
+ : SwHistoryHint(HSTRY_CHGCHARFMT)
+ , m_OldSet(rSet), m_Format(sFormat)
+{
+}
+
+void SwHistoryChangeCharFormat::SetInDoc(SwDoc * pDoc, bool )
+{
+ SwCharFormat * pCharFormat = pDoc->FindCharFormatByName(m_Format);
+
+ if (pCharFormat)
+ {
+ pCharFormat->SetFormatAttr(m_OldSet);
+ }
+}
+// <- #i27615#
+
+SwHistory::SwHistory()
+ : m_nEndDiff( 0 )
+{
+}
+
+SwHistory::~SwHistory()
+{
+}
+
+void SwHistory::Add(
+ const SfxPoolItem* pOldValue,
+ const SfxPoolItem* pNewValue,
+ SwNodeOffset nNodeIdx)
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+ const sal_uInt16 nWhich(pNewValue->Which());
+
+ // excluded values
+ if(nWhich == RES_TXTATR_FIELD || nWhich == RES_TXTATR_ANNOTATION)
+ {
+ return;
+ }
+
+ // no default Attribute?
+ std::unique_ptr<SwHistoryHint> pHt;
+
+ // To be able to include the DrawingLayer FillItems something more
+ // general has to be done to check if an Item is default than to check
+ // if its pointer equals that in Writer's global PoolDefaults (held in
+ // aAttrTab and used to fill the pool defaults in Writer - looks as if
+ // Writer is *older* than the SfxItemPool ?). I checked the possibility to
+ // get the SfxItemPool here (works), but decided to use the SfxPoolItem's
+ // global tooling aka IsDefaultItem(const SfxPoolItem*) for now
+ if(pOldValue && !IsDefaultItem(pOldValue))
+ {
+ pHt.reset( new SwHistorySetFormat( pOldValue, nNodeIdx ) );
+ }
+ else
+ {
+ pHt.reset( new SwHistoryResetFormat( pNewValue, nNodeIdx ) );
+ }
+
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+// FIXME: refactor the following "Add" methods (DRY)?
+void SwHistory::Add( SwTextAttr* pHint, SwNodeOffset nNodeIdx, bool bNewAttr )
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ std::unique_ptr<SwHistoryHint> pHt;
+ if( !bNewAttr )
+ {
+ switch ( pHint->Which() )
+ {
+ case RES_TXTATR_FTN:
+ pHt.reset( new SwHistorySetFootnote(
+ static_cast<SwTextFootnote*>(pHint), nNodeIdx ) );
+ break;
+ case RES_TXTATR_FLYCNT:
+ pHt.reset( new SwHistoryTextFlyCnt( static_cast<SwTextFlyCnt*>(pHint)
+ ->GetFlyCnt().GetFrameFormat() ) );
+ break;
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ pHt.reset( new SwHistorySetTextField(
+ static_txtattr_cast<SwTextField*>(pHint), nNodeIdx) );
+ break;
+ case RES_TXTATR_TOXMARK:
+ pHt.reset( new SwHistorySetTOXMark(
+ static_txtattr_cast<SwTextTOXMark*>(pHint), nNodeIdx) );
+ break;
+ case RES_TXTATR_REFMARK:
+ pHt.reset( new SwHistorySetRefMark(
+ static_txtattr_cast<SwTextRefMark*>(pHint), nNodeIdx) );
+ break;
+ default:
+ pHt.reset( new SwHistorySetText( pHint, nNodeIdx ) );
+ }
+ }
+ else
+ {
+ pHt.reset( new SwHistoryResetText( pHint->Which(), pHint->GetStart(),
+ pHint->GetAnyEnd(), nNodeIdx ) );
+ }
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::Add( SwFormatColl* pColl, SwNodeOffset nNodeIdx, SwNodeType nWhichNd )
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ std::unique_ptr<SwHistoryHint> pHt(
+ new SwHistoryChangeFormatColl( pColl, nNodeIdx, nWhichNd ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::Add(const ::sw::mark::IMark& rBkmk, bool bSavePos, bool bSaveOtherPos)
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ std::unique_ptr<SwHistoryHint> pHt;
+
+ switch (IDocumentMarkAccess::GetType(rBkmk))
+ {
+ case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
+ assert(bSavePos && bSaveOtherPos); // must be deleted completely!
+ pHt.reset(new SwHistoryTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
+ break;
+ case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK:
+ case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK:
+ assert(bSavePos && bSaveOtherPos); // must be deleted completely!
+ pHt.reset(new SwHistoryNoTextFieldmark(dynamic_cast<sw::mark::IFieldmark const&>(rBkmk)));
+ break;
+ default:
+ pHt.reset(new SwHistoryBookmark(rBkmk, bSavePos, bSaveOtherPos));
+ break;
+ }
+
+ assert(pHt);
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::AddChangeFlyAnchor(SwFrameFormat& rFormat)
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeFlyAnchor( rFormat ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+void SwHistory::AddDeleteFly(SwFrameFormat& rFormat, sal_uInt16& rSetPos)
+{
+ OSL_ENSURE( !m_nEndDiff, "History was not deleted after REDO" );
+
+ const sal_uInt16 nWh = rFormat.Which();
+ (void) nWh;
+ // only Flys!
+ assert((RES_FLYFRMFMT == nWh && dynamic_cast<SwFlyFrameFormat*>(&rFormat))
+ || (RES_DRAWFRMFMT == nWh && dynamic_cast<SwDrawFrameFormat*>(&rFormat)));
+ {
+ std::unique_ptr<SwHistoryHint> pHint(new SwHistoryTextFlyCnt( &rFormat ));
+ m_SwpHstry.push_back( std::move(pHint) );
+
+ if( const SwFormatChain* pChainItem = rFormat.GetItemIfSet( RES_CHAIN, false ) )
+ {
+ assert(RES_FLYFRMFMT == nWh); // not supported on SdrObjects
+ if( pChainItem->GetNext() || pChainItem->GetPrev() )
+ {
+ std::unique_ptr<SwHistoryHint> pHt(
+ new SwHistoryChangeFlyChain(static_cast<SwFlyFrameFormat&>(rFormat), *pChainItem));
+ m_SwpHstry.insert( m_SwpHstry.begin() + rSetPos++, std::move(pHt) );
+ if ( pChainItem->GetNext() )
+ {
+ SwFormatChain aTmp( pChainItem->GetNext()->GetChain() );
+ aTmp.SetPrev( nullptr );
+ pChainItem->GetNext()->SetFormatAttr( aTmp );
+ }
+ if ( pChainItem->GetPrev() )
+ {
+ SwFormatChain aTmp( pChainItem->GetPrev()->GetChain() );
+ aTmp.SetNext( nullptr );
+ pChainItem->GetPrev()->SetFormatAttr( aTmp );
+ }
+ }
+ rFormat.ResetFormatAttr( RES_CHAIN );
+ }
+ }
+}
+
+void SwHistory::Add( const SwTextFootnote& rFootnote )
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistorySetFootnote( rFootnote ));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+// #i27615#
+void SwHistory::Add(const SfxItemSet & rSet, const SwCharFormat & rFormat)
+{
+ std::unique_ptr<SwHistoryHint> pHt(new SwHistoryChangeCharFormat(rSet, rFormat.GetName()));
+ m_SwpHstry.push_back( std::move(pHt) );
+}
+
+bool SwHistory::Rollback( SwDoc* pDoc, sal_uInt16 nStart )
+{
+ if ( !Count() )
+ return false;
+
+ for ( sal_uInt16 i = Count(); i > nStart ; )
+ {
+ SwHistoryHint * pHHt = m_SwpHstry[ --i ].get();
+ pHHt->SetInDoc( pDoc, false );
+ }
+ m_SwpHstry.erase( m_SwpHstry.begin() + nStart, m_SwpHstry.end() );
+ m_nEndDiff = 0;
+ return true;
+}
+
+bool SwHistory::TmpRollback( SwDoc* pDoc, sal_uInt16 nStart, bool bToFirst )
+{
+ sal_uInt16 nEnd = Count() - m_nEndDiff;
+ if ( !Count() || !nEnd || nStart >= nEnd )
+ return false;
+
+ if ( bToFirst )
+ {
+ for ( ; nEnd > nStart; ++m_nEndDiff )
+ {
+ SwHistoryHint* pHHt = m_SwpHstry[ --nEnd ].get();
+ pHHt->SetInDoc( pDoc, true );
+ }
+ }
+ else
+ {
+ for ( ; nStart < nEnd; ++m_nEndDiff, ++nStart )
+ {
+ SwHistoryHint* pHHt = m_SwpHstry[ nStart ].get();
+ pHHt->SetInDoc( pDoc, true );
+ }
+ }
+ return true;
+}
+
+sal_uInt16 SwHistory::SetTmpEnd( sal_uInt16 nNewTmpEnd )
+{
+ OSL_ENSURE( nNewTmpEnd <= Count(), "SwHistory::SetTmpEnd: out of bounds" );
+
+ const sal_uInt16 nOld = Count() - m_nEndDiff;
+ m_nEndDiff = Count() - nNewTmpEnd;
+
+ // for every SwHistoryFlyCnt, call the Redo of its UndoObject.
+ // this saves the formats of the flys!
+ for ( sal_uInt16 n = nOld; n < nNewTmpEnd; n++ )
+ {
+ if ( HSTRY_FLYCNT == (*this)[ n ]->Which() )
+ {
+ static_cast<SwHistoryTextFlyCnt*>((*this)[ n ])
+ ->GetUDelLFormat()->RedoForRollback();
+ }
+ }
+
+ return nOld;
+}
+
+void SwHistory::CopyFormatAttr(
+ const SfxItemSet& rSet,
+ SwNodeOffset nNodeIdx)
+{
+ if(!rSet.Count())
+ return;
+
+ SfxItemIter aIter(rSet);
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if(!IsInvalidItem(pItem))
+ {
+ Add(pItem, pItem, nNodeIdx);
+ }
+
+ pItem = aIter.NextItem();
+
+ } while(pItem);
+}
+
+void SwHistory::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwHistory"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_SwpHstry"));
+ for (const auto& pHistory : m_SwpHstry)
+ {
+ pHistory->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwHistory::CopyAttr(
+ SwpHints const * pHts,
+ const SwNodeOffset nNodeIdx,
+ const sal_Int32 nStart,
+ const sal_Int32 nEnd,
+ const bool bCopyFields )
+{
+ if( !pHts )
+ return;
+
+ // copy all attributes of the TextNode in the area from nStart to nEnd
+ SwTextAttr* pHt;
+ for( size_t n = 0; n < pHts->Count(); ++n )
+ {
+ // nAttrStt must even be set when !pEndIdx
+ pHt = pHts->Get(n);
+ const sal_Int32 nAttrStt = pHt->GetStart();
+ const sal_Int32 * pEndIdx = pHt->GetEnd();
+ if( nullptr != pEndIdx && nAttrStt > nEnd )
+ break;
+
+ // never copy Flys and Footnote !!
+ bool bNextAttr = false;
+ switch( pHt->Which() )
+ {
+ case RES_TXTATR_FIELD:
+ case RES_TXTATR_ANNOTATION:
+ case RES_TXTATR_INPUTFIELD:
+ if( !bCopyFields )
+ bNextAttr = true;
+ break;
+ case RES_TXTATR_FLYCNT:
+ case RES_TXTATR_FTN:
+ bNextAttr = true;
+ break;
+ }
+
+ if( bNextAttr )
+ continue;
+
+ // save all attributes that are somehow in this area
+ if ( nStart <= nAttrStt )
+ {
+ if ( nEnd > nAttrStt )
+ {
+ Add( pHt, nNodeIdx, false );
+ }
+ }
+ else if ( pEndIdx && nStart < *pEndIdx )
+ {
+ Add( pHt, nNodeIdx, false );
+ }
+ }
+}
+
+// Class to register the history at a Node, Format, HintsArray, ...
+SwRegHistory::SwRegHistory( SwHistory* pHst )
+ : SwClient( nullptr )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( NODE_OFFSET_MAX )
+{
+ MakeSetWhichIds();
+}
+
+SwRegHistory::SwRegHistory( sw::BroadcastingModify* pRegIn, const SwNode& rNd,
+ SwHistory* pHst )
+ : SwClient( pRegIn )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( rNd.GetIndex() )
+{
+ MakeSetWhichIds();
+}
+
+SwRegHistory::SwRegHistory( const SwNode& rNd, SwHistory* pHst )
+ : SwClient( nullptr )
+ , m_pHistory( pHst )
+ , m_nNodeIndex( rNd.GetIndex() )
+{
+ MakeSetWhichIds();
+}
+
+void SwRegHistory::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if ( !(m_pHistory && pLegacyHint->m_pNew && pLegacyHint->m_pOld != pLegacyHint->m_pNew) )
+ return;
+
+ if ( pLegacyHint->m_pNew->Which() < POOLATTR_END )
+ {
+ if(RES_UPDATE_ATTR == pLegacyHint->m_pNew->Which())
+ {
+ m_pHistory->Add(pLegacyHint->m_pOld, pLegacyHint->m_pNew, m_nNodeIndex);
+ }
+ else
+ {
+ OSL_ENSURE(false, "Unexpected update attribute (!)");
+ }
+ }
+ else if (pLegacyHint->m_pOld && RES_ATTRSET_CHG == pLegacyHint->m_pNew->Which())
+ {
+ std::unique_ptr<SwHistoryHint> pNewHstr;
+ const SfxItemSet& rSet = *static_cast< const SwAttrSetChg* >(pLegacyHint->m_pOld)->GetChgSet();
+
+ if ( 1 < rSet.Count() )
+ {
+ pNewHstr.reset( new SwHistorySetAttrSet( rSet, m_nNodeIndex, m_WhichIdSet ) );
+ }
+ else if (const SfxPoolItem* pItem = SfxItemIter(rSet).GetCurItem())
+ {
+ if ( m_WhichIdSet.count( pItem->Which() ) )
+ {
+ pNewHstr.reset( new SwHistorySetFormat( pItem, m_nNodeIndex ) );
+ }
+ else
+ {
+ pNewHstr.reset( new SwHistoryResetFormat( pItem, m_nNodeIndex ) );
+ }
+ }
+
+ if (pNewHstr)
+ m_pHistory->m_SwpHstry.push_back( std::move(pNewHstr) );
+ }
+}
+
+void SwRegHistory::AddHint( SwTextAttr* pHt, const bool bNew )
+{
+ m_pHistory->Add( pHt, m_nNodeIndex, bNew );
+}
+
+bool SwRegHistory::InsertItems( const SfxItemSet& rSet,
+ sal_Int32 const nStart, sal_Int32 const nEnd, SetAttrMode const nFlags,
+ SwTextAttr **ppNewTextAttr )
+{
+ if( !rSet.Count() )
+ return false;
+
+ SwTextNode * const pTextNode =
+ dynamic_cast<SwTextNode *>(GetRegisteredIn());
+
+ OSL_ENSURE(pTextNode, "SwRegHistory not registered at text node?");
+ if (!pTextNode)
+ return false;
+
+ if (m_pHistory)
+ {
+ pTextNode->GetOrCreateSwpHints().Register(this);
+ }
+
+ const bool bInserted = pTextNode->SetAttr( rSet, nStart, nEnd, nFlags, ppNewTextAttr );
+
+ // Caution: The array can be deleted when inserting an attribute!
+ // This can happen when the value that should be added first deletes
+ // an existing attribute but does not need to be added itself because
+ // the paragraph attributes are identical
+ // ( -> bForgetAttr in SwpHints::Insert )
+ if ( pTextNode->GetpSwpHints() && m_pHistory )
+ {
+ pTextNode->GetpSwpHints()->DeRegister();
+ }
+
+#ifndef NDEBUG
+ if ( m_pHistory && bInserted )
+ {
+ SfxItemIter aIter(rSet);
+ for (SfxPoolItem const* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ { // check that the history recorded a hint to reset every item
+ sal_uInt16 const nWhich(pItem->Which());
+ sal_uInt16 const nExpected(
+ (isCHRATR(nWhich) || RES_TXTATR_UNKNOWN_CONTAINER == nWhich)
+ ? RES_TXTATR_AUTOFMT
+ : nWhich);
+ if (RES_TXTATR_AUTOFMT == nExpected)
+ continue; // special case, may get set on text node itself
+ // tdf#105077 even worse, node's set could cause
+ // nothing at all to be inserted
+ assert(std::any_of(
+ m_pHistory->m_SwpHstry.begin(), m_pHistory->m_SwpHstry.end(),
+ [nExpected](std::unique_ptr<SwHistoryHint> const& pHint) -> bool {
+ SwHistoryResetText const*const pReset(
+ dynamic_cast<SwHistoryResetText const*>(pHint.get()));
+ return pReset && (pReset->GetWhich() == nExpected);
+ }));
+ }
+ }
+#endif
+
+ return bInserted;
+}
+
+void SwRegHistory::RegisterInModify( sw::BroadcastingModify* pRegIn, const SwNode& rNd )
+{
+ if ( m_pHistory && pRegIn )
+ {
+ pRegIn->Add( this );
+ m_nNodeIndex = rNd.GetIndex();
+ MakeSetWhichIds();
+ }
+ else
+ {
+ m_WhichIdSet.clear();
+ }
+}
+
+void SwRegHistory::MakeSetWhichIds()
+{
+ if (!m_pHistory) return;
+
+ m_WhichIdSet.clear();
+
+ if( !GetRegisteredIn() )
+ return;
+
+ const SfxItemSet* pSet = nullptr;
+ if( auto pContentNode = dynamic_cast< const SwContentNode *>( GetRegisteredIn() ) )
+ {
+ pSet = pContentNode->GetpSwAttrSet();
+ }
+ else if ( auto pSwFormat = dynamic_cast< const SwFormat *>( GetRegisteredIn() ) )
+ {
+ pSet = &pSwFormat->GetAttrSet();
+ }
+ if( pSet && pSet->Count() )
+ {
+ SfxItemIter aIter( *pSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ sal_uInt16 nW = pItem->Which();
+ m_WhichIdSet.insert( nW );
+ }
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unattr.cxx b/sw/source/core/undo/unattr.cxx
new file mode 100644
index 000000000..6119b49d7
--- /dev/null
+++ b/sw/source/core/undo/unattr.cxx
@@ -0,0 +1,1067 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <utility>
+
+#include <UndoAttribute.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/tstpitem.hxx>
+#include <svx/svdobj.hxx>
+#include <osl/diagnose.h>
+#include <hintids.hxx>
+#include <fmtflcnt.hxx>
+#include <txtftn.hxx>
+#include <fmtanchr.hxx>
+#include <fmtfsize.hxx>
+#include <frmfmt.hxx>
+#include <fmtcntnt.hxx>
+#include <ftnidx.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <docary.hxx>
+#include <swcrsr.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <swtblfmt.hxx>
+#include <UndoCore.hxx>
+#include <hints.hxx>
+#include <rolbck.hxx>
+#include <ndnotxt.hxx>
+#include <ftninfo.hxx>
+#include <redline.hxx>
+#include <section.hxx>
+#include <charfmt.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+
+SwUndoFormatAttrHelper::SwUndoFormatAttrHelper(SwFormat& rFormat, bool bSvDrwPt)
+ : SwClient(&rFormat)
+ , m_rFormat(rFormat)
+ , m_bSaveDrawPt(bSvDrwPt)
+{
+}
+
+void SwUndoFormatAttrHelper::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pOld)
+ return;
+ assert(pLegacy->m_pOld->Which() != RES_OBJECTDYING);
+ if(!pLegacy->m_pNew)
+ return;
+ const SwDoc& rDoc = *m_rFormat.GetDoc();
+ auto pOld = pLegacy->m_pOld;
+ if(POOLATTR_END >= pLegacy->m_pOld->Which()) {
+ if(!GetUndo())
+ m_pUndo.reset(new SwUndoFormatAttr(*pOld, m_rFormat, m_bSaveDrawPt));
+ else
+ m_pUndo->PutAttr(*pOld, rDoc);
+ } else if(RES_ATTRSET_CHG == pOld->Which()) {
+ auto& rChgSet = *static_cast<const SwAttrSetChg*>(pOld)->GetChgSet();
+ if(!GetUndo())
+ m_pUndo.reset(new SwUndoFormatAttr(SfxItemSet(rChgSet), m_rFormat, m_bSaveDrawPt));
+ else {
+ SfxItemIter aIter(rChgSet);
+ for(auto pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ m_pUndo->PutAttr(*pItem, rDoc);
+ }
+ }
+}
+
+SwUndoFormatAttr::SwUndoFormatAttr( SfxItemSet&& rOldSet,
+ SwFormat& rChgFormat,
+ bool bSaveDrawPt )
+ : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() )
+ , m_sFormatName ( rChgFormat.GetName() )
+ // #i56253#
+ , m_oOldSet( std::move( rOldSet ) )
+ , m_nNodeIndex( 0 )
+ , m_nFormatWhich( rChgFormat.Which() )
+ , m_bSaveDrawPt( bSaveDrawPt )
+{
+ assert(m_sFormatName.getLength());
+
+ Init( rChgFormat );
+}
+
+SwUndoFormatAttr::SwUndoFormatAttr( const SfxPoolItem& rItem, SwFormat& rChgFormat,
+ bool bSaveDrawPt )
+ : SwUndo( SwUndoId::INSFMTATTR, rChgFormat.GetDoc() )
+ , m_sFormatName(rChgFormat.GetName())
+ , m_oOldSet( rChgFormat.GetAttrSet().CloneAsValue( false ) )
+ , m_nNodeIndex( 0 )
+ , m_nFormatWhich( rChgFormat.Which() )
+ , m_bSaveDrawPt( bSaveDrawPt )
+{
+ assert(m_sFormatName.getLength());
+
+ m_oOldSet->Put( rItem );
+ Init( rChgFormat );
+}
+
+void SwUndoFormatAttr::Init( const SwFormat & rFormat )
+{
+ // tdf#126017 never save SwNodeIndex, it will go stale
+ m_oOldSet->ClearItem(RES_CNTNT);
+ // treat change of anchor specially
+ if ( SfxItemState::SET == m_oOldSet->GetItemState( RES_ANCHOR, false )) {
+ SaveFlyAnchor( &rFormat, m_bSaveDrawPt );
+ } else if ( RES_FRMFMT == m_nFormatWhich ) {
+ const SwDoc* pDoc = rFormat.GetDoc();
+ if (pDoc->GetTableFrameFormats()->ContainsFormat(dynamic_cast<const SwFrameFormat&>(rFormat)))
+ {
+ // Table Format: save table position, table formats are volatile!
+ SwTable * pTable = SwIterator<SwTable,SwFormat>( rFormat ).First();
+ if ( pTable ) {
+ m_nNodeIndex = pTable->GetTabSortBoxes()[ 0 ]->GetSttNd()
+ ->FindTableNode()->GetIndex();
+ }
+ } else if (dynamic_cast<const SwSectionFormat*>(&rFormat)) {
+ m_nNodeIndex = rFormat.GetContent().GetContentIdx()->GetIndex();
+ } else if(auto pBoxFormat = dynamic_cast<const SwTableBoxFormat*>(&rFormat))
+ {
+ auto pTableBox = pBoxFormat->GetTableBox();
+ if(pTableBox)
+ m_nNodeIndex = pTableBox->GetSttIdx();
+ }
+ }
+}
+
+SwUndoFormatAttr::~SwUndoFormatAttr()
+{
+}
+
+void SwUndoFormatAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ // OD 2004-10-26 #i35443#
+ // Important note: <Undo(..)> also called by <ReDo(..)>
+
+ if (!m_oOldSet)
+ return;
+
+ SwFormat * pFormat = GetFormat(rContext.GetDoc());
+ if (!pFormat)
+ return;
+
+ // #i35443# - If anchor attribute has been successful
+ // restored, all other attributes are also restored.
+ // Thus, keep track of its restoration
+ bool bAnchorAttrRestored( false );
+ if ( SfxItemState::SET == m_oOldSet->GetItemState( RES_ANCHOR, false )) {
+ bAnchorAttrRestored = RestoreFlyAnchor(rContext);
+ if ( bAnchorAttrRestored ) {
+ // Anchor attribute successful restored.
+ // Thus, keep anchor position for redo
+ SaveFlyAnchor(pFormat);
+ } else {
+ // Anchor attribute not restored due to invalid anchor position.
+ // Thus, delete anchor attribute.
+ m_oOldSet->ClearItem( RES_ANCHOR );
+ }
+ }
+
+ if ( bAnchorAttrRestored ) return;
+
+ SwUndoFormatAttrHelper aTmp( *pFormat, m_bSaveDrawPt );
+ pFormat->SetFormatAttr( *m_oOldSet );
+ if ( aTmp.GetUndo() ) {
+ // transfer ownership of helper object's old set
+ if (aTmp.GetUndo()->m_oOldSet)
+ m_oOldSet.emplace(std::move(*aTmp.GetUndo()->m_oOldSet));
+ else
+ m_oOldSet.reset();
+ } else {
+ m_oOldSet->ClearItem();
+ }
+
+ if ( RES_FLYFRMFMT == m_nFormatWhich || RES_DRAWFRMFMT == m_nFormatWhich ) {
+ rContext.SetSelections(static_cast<SwFrameFormat*>(pFormat), nullptr);
+ }
+}
+
+// Check if it is still in Doc
+SwFormat* SwUndoFormatAttr::GetFormat( const SwDoc& rDoc )
+{
+ switch (m_nFormatWhich)
+ {
+ case RES_TXTFMTCOLL:
+ case RES_CONDTXTFMTCOLL:
+ return rDoc.FindTextFormatCollByName(m_sFormatName);
+
+ case RES_GRFFMTCOLL:
+ return rDoc.GetGrfFormatColls()->FindFormatByName(m_sFormatName);
+
+ case RES_CHRFMT:
+ return rDoc.FindCharFormatByName(m_sFormatName);
+
+ case RES_FRMFMT:
+ if (m_nNodeIndex && (m_nNodeIndex < rDoc.GetNodes().Count()))
+ {
+ SwNode* pNd = rDoc.GetNodes()[m_nNodeIndex];
+ if (pNd->IsTableNode())
+ {
+ return static_cast<SwTableNode*>(pNd)->GetTable().GetFrameFormat();
+ }
+ else if (pNd->IsSectionNode())
+ {
+ return static_cast<SwSectionNode*>(pNd)->GetSection().GetFormat();
+ }
+ else if (pNd->IsStartNode() && (SwTableBoxStartNode ==
+ static_cast<SwStartNode*>(pNd)->GetStartNodeType()))
+ {
+ SwTableNode* pTableNode = pNd->FindTableNode();
+ if (pTableNode)
+ {
+ SwTableBox* pBox = pTableNode->GetTable().GetTableBox(m_nNodeIndex);
+ if (pBox)
+ {
+ return pBox->GetFrameFormat();
+ }
+ }
+ }
+ }
+ [[fallthrough]];
+ case RES_DRAWFRMFMT:
+ case RES_FLYFRMFMT:
+ {
+ SwFormat * pFormat = rDoc.GetSpzFrameFormats()->FindFormatByName(m_sFormatName);
+ if (pFormat)
+ return pFormat;
+ pFormat = rDoc.GetFrameFormats()->FindFormatByName(m_sFormatName);
+ if (pFormat)
+ return pFormat;
+ }
+ break;
+ }
+
+ return nullptr;
+}
+
+void SwUndoFormatAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // #i35443# - Because the undo stores the attributes for
+ // redo, the same code as for <Undo(..)> can be applied for <Redo(..)>
+ UndoImpl(rContext);
+}
+
+void SwUndoFormatAttr::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ if (!m_oOldSet)
+ return;
+
+ SwDoc & rDoc(rContext.GetDoc());
+
+ SwFormat * pFormat = GetFormat(rDoc);
+ if (!pFormat)
+ return;
+
+ switch ( m_nFormatWhich ) {
+ case RES_GRFFMTCOLL: {
+ SwNoTextNode *const pNd =
+ rContext.GetRepeatPaM().GetNode().GetNoTextNode();
+ if( pNd ) {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() );
+ }
+ }
+ break;
+
+ case RES_TXTFMTCOLL:
+ case RES_CONDTXTFMTCOLL:
+ {
+ SwTextNode *const pNd =
+ rContext.GetRepeatPaM().GetNode().GetTextNode();
+ if( pNd ) {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pNd->GetFormatColl() );
+ }
+ }
+ break;
+
+ case RES_FLYFRMFMT: {
+ // Check if the cursor is in a flying frame
+ // Steps: search in all FlyFrameFormats for the FlyContent attribute
+ // and validate if the cursor is in the respective section
+ SwFrameFormat *const pFly =
+ rContext.GetRepeatPaM().GetNode().GetFlyFormat();
+ if( pFly ) {
+ // Bug 43672: do not set all attributes!
+ if (SfxItemState::SET ==
+ pFormat->GetAttrSet().GetItemState( RES_CNTNT )) {
+ SfxItemSet aTmpSet( pFormat->GetAttrSet() );
+ aTmpSet.ClearItem( RES_CNTNT );
+ if( aTmpSet.Count() ) {
+ rDoc.SetAttr( aTmpSet, *pFly );
+ }
+ } else {
+ rDoc.SetAttr( pFormat->GetAttrSet(), *pFly );
+ }
+ }
+ break;
+ }
+ }
+}
+
+SwRewriter SwUndoFormatAttr::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_sFormatName);
+
+ return aRewriter;
+}
+
+void SwUndoFormatAttr::PutAttr( const SfxPoolItem& rItem, const SwDoc& rDoc )
+{
+ if (RES_CNTNT == rItem.Which())
+ {
+ return; // tdf#126017 never save SwNodeIndex, it will go stale
+ }
+ m_oOldSet->Put( rItem );
+ if ( RES_ANCHOR == rItem.Which() )
+ {
+ SwFormat * pFormat = GetFormat( rDoc );
+ SaveFlyAnchor( pFormat, m_bSaveDrawPt );
+ }
+}
+
+void SwUndoFormatAttr::SaveFlyAnchor( const SwFormat * pFormat, bool bSvDrwPt )
+{
+ // Format is valid, otherwise you would not reach this point here
+ if( bSvDrwPt ) {
+ if ( RES_DRAWFRMFMT == pFormat->Which() ) {
+ Point aPt( static_cast<const SwFrameFormat*>(pFormat)->FindSdrObject()
+ ->GetRelativePos() );
+ // store old value as attribute, to keep SwUndoFormatAttr small
+ m_oOldSet->Put( SwFormatFrameSize( SwFrameSize::Variable, aPt.X(), aPt.Y() ) );
+ }
+ }
+
+ const SwFormatAnchor& rAnchor =
+ m_oOldSet->Get( RES_ANCHOR, false );
+ if( !rAnchor.GetContentAnchor() )
+ return;
+
+ sal_Int32 nContent = 0;
+ switch( rAnchor.GetAnchorId() ) {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ nContent = rAnchor.GetContentAnchor()->nContent.GetIndex();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNodeIndex = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ break;
+ default:
+ return;
+ }
+
+ SwFormatAnchor aAnchor( rAnchor.GetAnchorId(), nContent );
+ m_oOldSet->Put( aAnchor );
+}
+
+// #i35443# - Add return value, type <bool>.
+// Return value indicates, if anchor attribute is restored.
+// Note: If anchor attribute is restored, all other existing attributes
+// are also restored.
+bool SwUndoFormatAttr::RestoreFlyAnchor(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwFrameFormat* pFrameFormat = static_cast<SwFrameFormat*>( GetFormat( *pDoc ) );
+ const SwFormatAnchor& rAnchor =
+ m_oOldSet->Get( RES_ANCHOR, false );
+
+ SwFormatAnchor aNewAnchor( rAnchor.GetAnchorId() );
+ if (RndStdIds::FLY_AT_PAGE != rAnchor.GetAnchorId()) {
+ SwNode* pNd = pDoc->GetNodes()[ m_nNodeIndex ];
+
+ if ( (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId())
+ ? ( !pNd->IsStartNode() || (SwFlyStartNode !=
+ static_cast<SwStartNode*>(pNd)->GetStartNodeType()) )
+ : !pNd->IsTextNode() ) {
+ // #i35443# - invalid position.
+ // Thus, anchor attribute not restored
+ return false;
+ }
+
+ SwPosition aPos( *pNd );
+ if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())) {
+ aPos.nContent.Assign( static_cast<SwTextNode*>(pNd), rAnchor.GetPageNum() );
+ if ( aPos.nContent.GetIndex() > pNd->GetTextNode()->GetText().getLength()) {
+ // #i35443# - invalid position.
+ // Thus, anchor attribute not restored
+ return false;
+ }
+ }
+ aNewAnchor.SetAnchor( &aPos );
+ } else
+ aNewAnchor.SetPageNum( rAnchor.GetPageNum() );
+
+ Point aDrawSavePt, aDrawOldPt;
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() ) {
+ if( RES_DRAWFRMFMT == pFrameFormat->Which() ) {
+ // get the old cached value
+ const SwFormatFrameSize& rOldSize = m_oOldSet->Get( RES_FRM_SIZE );
+ aDrawSavePt.setX( rOldSize.GetWidth() );
+ aDrawSavePt.setY( rOldSize.GetHeight() );
+ m_oOldSet->ClearItem( RES_FRM_SIZE );
+
+ // write the current value into cache
+ aDrawOldPt = pFrameFormat->FindSdrObject()->GetRelativePos();
+ } else {
+ pFrameFormat->DelFrames(); // delete Frames
+ }
+ }
+
+ const SwFormatAnchor &rOldAnch = pFrameFormat->GetAnchor();
+ // #i54336#
+ // Consider case, that as-character anchored object has moved its anchor position.
+ if (RndStdIds::FLY_AS_CHAR == rOldAnch.GetAnchorId()) {
+ // With InContents it's tricky: the text attribute needs to be deleted.
+ // Unfortunately, this not only destroys the Frames but also the format.
+ // To prevent that, first detach the connection between attribute and
+ // format.
+ const SwPosition *pPos = rOldAnch.GetContentAnchor();
+ SwTextNode *pTextNode = static_cast<SwTextNode*>(&pPos->nNode.GetNode());
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing Hint.");
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == pFrameFormat,
+ "Wrong TextFlyCnt-Hint." );
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // Connection is now detached, therefore the attribute can be deleted
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+
+ {
+ m_oOldSet->Put( aNewAnchor );
+ SwUndoFormatAttrHelper aTmp( *pFrameFormat, m_bSaveDrawPt );
+ pFrameFormat->SetFormatAttr( *m_oOldSet );
+ if ( aTmp.GetUndo() ) {
+ m_nNodeIndex = aTmp.GetUndo()->m_nNodeIndex;
+ // transfer ownership of helper object's old set
+ if (aTmp.GetUndo()->m_oOldSet)
+ m_oOldSet.emplace(std::move(*aTmp.GetUndo()->m_oOldSet));
+ else
+ m_oOldSet.reset();
+ } else {
+ m_oOldSet->ClearItem();
+ }
+ }
+
+ if ( RES_DRAWFRMFMT == pFrameFormat->Which() )
+ {
+ // The Draw model also prepared an Undo object for its right positioning
+ // which unfortunately is relative. Therefore block here a position
+ // change of the Contact object by setting the anchor.
+ pFrameFormat->CallSwClientNotify(sw::RestoreFlyAnchorHint(aDrawSavePt));
+ // cache the old value again
+ m_oOldSet->Put(SwFormatFrameSize(SwFrameSize::Variable, aDrawOldPt.X(), aDrawOldPt.Y()));
+ }
+
+ if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId()) {
+ const SwPosition* pPos = aNewAnchor.GetContentAnchor();
+ SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "no Text Node at position." );
+ SwFormatFlyCnt aFormat( pFrameFormat );
+ pTextNd->InsertItem( aFormat, pPos->nContent.GetIndex(), 0 );
+ }
+
+ if (RES_DRAWFRMFMT != pFrameFormat->Which())
+ pFrameFormat->MakeFrames();
+ else
+ {
+ pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::POST_RESTORE_FLY_ANCHOR));
+ }
+
+ rContext.SetSelections(pFrameFormat, nullptr);
+
+ // #i35443# - anchor attribute restored.
+ return true;
+}
+
+SwUndoFormatResetAttr::SwUndoFormatResetAttr( SwFormat& rChangedFormat,
+ const sal_uInt16 nWhichId )
+ : SwUndo( SwUndoId::RESETATTR, rChangedFormat.GetDoc() )
+ , m_pChangedFormat( &rChangedFormat )
+ , m_nWhichId( nWhichId )
+{
+ const SfxPoolItem* pItem = nullptr;
+ if (rChangedFormat.GetItemState(nWhichId, false, &pItem ) == SfxItemState::SET && pItem) {
+ m_pOldItem.reset( pItem->Clone() );
+ }
+}
+
+SwUndoFormatResetAttr::~SwUndoFormatResetAttr()
+{
+}
+
+void SwUndoFormatResetAttr::UndoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pOldItem)
+ {
+ m_pChangedFormat->SetFormatAttr( *m_pOldItem );
+ }
+}
+
+void SwUndoFormatResetAttr::RedoImpl(::sw::UndoRedoContext &)
+{
+ if (m_pOldItem)
+ {
+ m_pChangedFormat->ResetFormatAttr( m_nWhichId );
+ }
+}
+
+SwUndoResetAttr::SwUndoResetAttr( const SwPaM& rRange, sal_uInt16 nFormatId )
+ : SwUndo( SwUndoId::RESETATTR, &rRange.GetDoc() ), SwUndRng( rRange )
+ , m_pHistory( new SwHistory )
+ , m_nFormatId( nFormatId )
+{
+}
+
+SwUndoResetAttr::SwUndoResetAttr( const SwPosition& rPos, sal_uInt16 nFormatId )
+ : SwUndo( SwUndoId::RESETATTR, &rPos.GetDoc() )
+ , m_pHistory( new SwHistory )
+ , m_nFormatId( nFormatId )
+{
+ m_nSttNode = m_nEndNode = rPos.nNode.GetIndex();
+ m_nSttContent = m_nEndContent = rPos.nContent.GetIndex();
+}
+
+SwUndoResetAttr::~SwUndoResetAttr()
+{
+}
+
+void SwUndoResetAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ // reset old values
+ SwDoc & rDoc = rContext.GetDoc();
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ if ((RES_CONDTXTFMTCOLL == m_nFormatId) &&
+ (m_nSttNode == m_nEndNode) && (m_nSttContent == m_nEndContent)) {
+ SwTextNode* pTNd = rDoc.GetNodes()[ m_nSttNode ]->GetTextNode();
+ if( pTNd ) {
+ SwIndex aIdx( pTNd, m_nSttContent );
+ pTNd->DontExpandFormat( aIdx, false );
+ }
+ }
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoResetAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ switch ( m_nFormatId ) {
+ case RES_CHRFMT:
+ rDoc.RstTextAttrs(rPam);
+ break;
+ case RES_TXTFMTCOLL:
+ rDoc.ResetAttrs(rPam, false, m_Ids );
+ break;
+ case RES_CONDTXTFMTCOLL:
+ rDoc.ResetAttrs(rPam, true, m_Ids );
+
+ break;
+ case RES_TXTATR_TOXMARK:
+ // special treatment for TOXMarks
+ {
+ SwTOXMarks aArr;
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode );
+ SwPosition aPos( aIdx, SwIndex( aIdx.GetNode().GetContentNode(),
+ m_nSttContent ));
+
+ sal_uInt16 nCnt = SwDoc::GetCurTOXMark( aPos, aArr );
+ if( nCnt ) {
+ if( 1 < nCnt ) {
+ // search for the right one
+ SwHistoryHint* pHHint = (GetHistory())[ 0 ];
+ if( pHHint && HSTRY_SETTOXMARKHNT == pHHint->Which() ) {
+ while( nCnt ) {
+ if ( static_cast<SwHistorySetTOXMark*>(pHHint)
+ ->IsEqual( *aArr[ --nCnt ] ) ) {
+ ++nCnt;
+ break;
+ }
+ }
+ } else
+ nCnt = 0;
+ }
+ // found one, thus delete it
+ if( nCnt-- ) {
+ rDoc.DeleteTOXMark( aArr[ nCnt ] );
+ }
+ }
+ }
+ break;
+ }
+}
+
+void SwUndoResetAttr::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ if (m_nFormatId < RES_FMT_BEGIN) {
+ return;
+ }
+
+ switch ( m_nFormatId ) {
+ case RES_CHRFMT:
+ rContext.GetDoc().RstTextAttrs(rContext.GetRepeatPaM());
+ break;
+ case RES_TXTFMTCOLL:
+ rContext.GetDoc().ResetAttrs(rContext.GetRepeatPaM(), false, m_Ids);
+ break;
+ case RES_CONDTXTFMTCOLL:
+ rContext.GetDoc().ResetAttrs(rContext.GetRepeatPaM(), true, m_Ids);
+ break;
+ }
+}
+
+void SwUndoResetAttr::SetAttrs( o3tl::sorted_vector<sal_uInt16> && rAttrs )
+{
+ m_Ids = std::move(rAttrs);
+}
+
+SwUndoAttr::SwUndoAttr( const SwPaM& rRange, const SfxPoolItem& rAttr,
+ const SetAttrMode nFlags )
+ : SwUndo( SwUndoId::INSATTR, &rRange.GetDoc() ), SwUndRng( rRange )
+ , m_AttrSet( rRange.GetDoc().GetAttrPool(), rAttr.Which(), rAttr.Which() )
+ , m_pHistory( new SwHistory )
+ , m_nNodeIndex( NODE_OFFSET_MAX )
+ , m_nInsertFlags( nFlags )
+{
+ m_AttrSet.Put( rAttr );
+
+ // Save character style as a style name, not as a reference
+ const SfxPoolItem* pItem = m_AttrSet.GetItem(RES_TXTATR_CHARFMT);
+ if (pItem)
+ {
+ uno::Any aValue;
+ pItem->QueryValue(aValue, RES_TXTATR_CHARFMT);
+ aValue >>= m_aChrFormatName;
+ }
+}
+
+SwUndoAttr::SwUndoAttr( const SwPaM& rRange, const SfxItemSet& rSet,
+ const SetAttrMode nFlags )
+ : SwUndo( SwUndoId::INSATTR, &rRange.GetDoc() ), SwUndRng( rRange )
+ , m_AttrSet( rSet )
+ , m_pHistory( new SwHistory )
+ , m_nNodeIndex( NODE_OFFSET_MAX )
+ , m_nInsertFlags( nFlags )
+{
+ // Save character style as a style name, not as a reference
+ const SfxPoolItem* pItem = m_AttrSet.GetItem(RES_TXTATR_CHARFMT);
+ if (pItem)
+ {
+ uno::Any aValue;
+ pItem->QueryValue(aValue, RES_TXTATR_CHARFMT);
+ aValue >>= m_aChrFormatName;
+ }
+}
+
+SwUndoAttr::~SwUndoAttr()
+{
+}
+
+void SwUndoAttr::SaveRedlineData( const SwPaM& rPam, bool bIsContent )
+{
+ SwDoc& rDoc = rPam.GetDoc();
+ if ( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) {
+ m_pRedlineData.reset( new SwRedlineData( bIsContent
+ ? RedlineType::Insert
+ : RedlineType::Format,
+ rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ }
+
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if ( !FillSaveDataForFormat( rPam, *m_pRedlineSaveData ))
+ m_pRedlineSaveData.reset();
+
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ if ( bIsContent ) {
+ m_nNodeIndex = rPam.GetPoint()->nNode.GetIndex();
+ }
+}
+
+void SwUndoAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ RemoveIdx( *pDoc );
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) {
+ SwPaM aPam(pDoc->GetNodes().GetEndOfContent());
+ if ( NODE_OFFSET_MAX != m_nNodeIndex ) {
+ aPam.DeleteMark();
+ aPam.GetPoint()->nNode = m_nNodeIndex;
+ aPam.GetPoint()->nContent.Assign( aPam.GetContentNode(), m_nSttContent );
+ aPam.SetMark();
+ ++aPam.GetPoint()->nContent;
+ pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any);
+ } else {
+ // remove all format redlines, will be recreated if needed
+ SetPaM(aPam);
+ pDoc->getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Format);
+ if (m_pRedlineSaveData)
+ {
+ SetSaveData( *pDoc, *m_pRedlineSaveData );
+ }
+ }
+ }
+
+ const bool bToLast = (1 == m_AttrSet.Count())
+ && (RES_TXTATR_FIELD <= m_AttrSet.GetRanges()[0].first)
+ && (m_AttrSet.GetRanges()[0].first <= RES_TXTATR_ANNOTATION);
+
+ // restore old values
+ m_pHistory->TmpRollback( pDoc, 0, !bToLast );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ // set cursor onto Undo area
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoAttr::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // RefMarks are not repeat capable
+ if ( SfxItemState::SET != m_AttrSet.GetItemState( RES_TXTATR_REFMARK, false ) ) {
+ rContext.GetDoc().getIDocumentContentOperations().InsertItemSet( rContext.GetRepeatPaM(),
+ m_AttrSet, m_nInsertFlags );
+ } else if ( 1 < m_AttrSet.Count() ) {
+ SfxItemSet aTmpSet( m_AttrSet );
+ aTmpSet.ClearItem( RES_TXTATR_REFMARK );
+ rContext.GetDoc().getIDocumentContentOperations().InsertItemSet( rContext.GetRepeatPaM(),
+ aTmpSet, m_nInsertFlags );
+ }
+}
+
+void SwUndoAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ // Restore pointer to char format from name
+ if (!m_aChrFormatName.isEmpty())
+ {
+ SwCharFormat* pCharFormat = rDoc.FindCharFormatByName(m_aChrFormatName);
+ if (pCharFormat)
+ {
+ SwFormatCharFormat aFormat(pCharFormat);
+ m_AttrSet.Put(aFormat);
+ }
+ }
+
+ if ( m_pRedlineData &&
+ IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) ) {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore );
+ rDoc.getIDocumentContentOperations().InsertItemSet( rPam, m_AttrSet, m_nInsertFlags );
+
+ if ( NODE_OFFSET_MAX != m_nNodeIndex ) {
+ rPam.SetMark();
+ if ( rPam.Move( fnMoveBackward ) ) {
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ),
+ true);
+ }
+ rPam.DeleteMark();
+ } else {
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true);
+ }
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ } else {
+ rDoc.getIDocumentContentOperations().InsertItemSet( rPam, m_AttrSet, m_nInsertFlags );
+ }
+}
+
+void SwUndoAttr::RemoveIdx( SwDoc& rDoc )
+{
+ if ( SfxItemState::SET != m_AttrSet.GetItemState( RES_TXTATR_FTN, false ))
+ return ;
+
+ SwNodes& rNds = rDoc.GetNodes();
+ for ( sal_uInt16 n = 0; n < m_pHistory->Count(); ++n ) {
+ sal_Int32 nContent = 0;
+ SwNodeOffset nNode(0);
+ SwHistoryHint* pHstHint = (*m_pHistory)[ n ];
+ switch ( pHstHint->Which() ) {
+ case HSTRY_RESETTXTHNT: {
+ SwHistoryResetText * pHistoryHint
+ = static_cast<SwHistoryResetText*>(pHstHint);
+ if ( RES_TXTATR_FTN == pHistoryHint->GetWhich() ) {
+ nNode = pHistoryHint->GetNode();
+ nContent = pHistoryHint->GetContent();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if( nNode ) {
+ SwTextNode* pTextNd = rNds[ nNode ]->GetTextNode();
+ if( pTextNd ) {
+ SwTextAttr *const pTextHt =
+ pTextNd->GetTextAttrForCharAt(nContent, RES_TXTATR_FTN);
+ if( pTextHt ) {
+ // ok, so get values
+ SwTextFootnote* pFootnote = static_cast<SwTextFootnote*>(pTextHt);
+ RemoveIdxFromSection( rDoc, pFootnote->GetStartNode()->GetIndex() );
+ return ;
+ }
+ }
+ }
+ }
+}
+
+SwUndoDefaultAttr::SwUndoDefaultAttr( const SfxItemSet& rSet, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::SETDEFTATTR, &rDoc )
+{
+ const SvxTabStopItem* pItem = rSet.GetItemIfSet( RES_PARATR_TABSTOP, false );
+ if( pItem )
+ {
+ // store separately, because it may change!
+ m_pTabStop.reset(pItem->Clone());
+ if ( 1 != rSet.Count() ) { // are there more attributes?
+ m_oOldSet.emplace( rSet );
+ }
+ } else {
+ m_oOldSet.emplace( rSet );
+ }
+}
+
+SwUndoDefaultAttr::~SwUndoDefaultAttr()
+{
+}
+
+void SwUndoDefaultAttr::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (m_oOldSet)
+ {
+ SwUndoFormatAttrHelper aTmp(
+ *rDoc.GetDfltTextFormatColl() );
+ rDoc.SetDefault( *m_oOldSet );
+ m_oOldSet.reset();
+ if ( aTmp.GetUndo() ) {
+ // transfer ownership of helper object's old set
+ if (aTmp.GetUndo()->m_oOldSet)
+ m_oOldSet.emplace(std::move(*aTmp.GetUndo()->m_oOldSet));
+ }
+ }
+ if (m_pTabStop)
+ {
+ std::unique_ptr<SvxTabStopItem> pOld(rDoc.GetDefault(RES_PARATR_TABSTOP).Clone());
+ rDoc.SetDefault( *m_pTabStop );
+ m_pTabStop = std::move( pOld );
+ }
+}
+
+void SwUndoDefaultAttr::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoImpl(rContext);
+}
+
+SwUndoMoveLeftMargin::SwUndoMoveLeftMargin(
+ const SwPaM& rPam, bool bFlag, bool bMod )
+ : SwUndo( bFlag ? SwUndoId::INC_LEFTMARGIN : SwUndoId::DEC_LEFTMARGIN, &rPam.GetDoc() )
+ , SwUndRng( rPam )
+ , m_pHistory( new SwHistory )
+ , m_bModulus( bMod )
+{
+}
+
+SwUndoMoveLeftMargin::~SwUndoMoveLeftMargin()
+{
+}
+
+void SwUndoMoveLeftMargin::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // restore old values
+ m_pHistory->TmpRollback( & rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoMoveLeftMargin::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ rDoc.MoveLeftMargin( rPam,
+ GetId() == SwUndoId::INC_LEFTMARGIN, m_bModulus,
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+}
+
+void SwUndoMoveLeftMargin::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ rDoc.MoveLeftMargin(rContext.GetRepeatPaM(), GetId() == SwUndoId::INC_LEFTMARGIN,
+ m_bModulus, rDoc.getIDocumentLayoutAccess().GetCurrentLayout());
+}
+
+SwUndoChangeFootNote::SwUndoChangeFootNote(
+ const SwPaM& rRange, const OUString& rText,
+ bool const bIsEndNote)
+ : SwUndo( SwUndoId::CHGFTN, &rRange.GetDoc() ), SwUndRng( rRange )
+ , m_pHistory( new SwHistory() )
+ , m_Text( rText )
+ , m_bEndNote( bIsEndNote )
+{
+}
+
+SwUndoChangeFootNote::~SwUndoChangeFootNote()
+{
+}
+
+void SwUndoChangeFootNote::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ rDoc.GetFootnoteIdxs().UpdateAllFootnote();
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoChangeFootNote::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc( rContext.GetDoc() );
+ SwPaM & rPaM = AddUndoRedoPaM(rContext);
+ rDoc.SetCurFootnote(rPaM, m_Text, m_bEndNote);
+ SetPaM(rPaM);
+}
+
+void SwUndoChangeFootNote::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ rDoc.SetCurFootnote(rContext.GetRepeatPaM(), m_Text, m_bEndNote);
+}
+
+SwUndoFootNoteInfo::SwUndoFootNoteInfo( const SwFootnoteInfo &rInfo, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::FTNINFO, &rDoc )
+ , m_pFootNoteInfo( new SwFootnoteInfo( rInfo ) )
+{
+}
+
+SwUndoFootNoteInfo::~SwUndoFootNoteInfo()
+{
+}
+
+void SwUndoFootNoteInfo::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwFootnoteInfo *pInf = new SwFootnoteInfo( rDoc.GetFootnoteInfo() );
+ rDoc.SetFootnoteInfo( *m_pFootNoteInfo );
+ m_pFootNoteInfo.reset( pInf );
+}
+
+void SwUndoFootNoteInfo::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwFootnoteInfo *pInf = new SwFootnoteInfo( rDoc.GetFootnoteInfo() );
+ rDoc.SetFootnoteInfo( *m_pFootNoteInfo );
+ m_pFootNoteInfo.reset( pInf );
+}
+
+SwUndoEndNoteInfo::SwUndoEndNoteInfo( const SwEndNoteInfo &rInfo, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::FTNINFO, &rDoc )
+ , m_pEndNoteInfo( new SwEndNoteInfo( rInfo ) )
+{
+}
+
+SwUndoEndNoteInfo::~SwUndoEndNoteInfo()
+{
+}
+
+void SwUndoEndNoteInfo::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwEndNoteInfo *pInf = new SwEndNoteInfo( rDoc.GetEndNoteInfo() );
+ rDoc.SetEndNoteInfo( *m_pEndNoteInfo );
+ m_pEndNoteInfo.reset( pInf );
+}
+
+void SwUndoEndNoteInfo::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwEndNoteInfo *pInf = new SwEndNoteInfo( rDoc.GetEndNoteInfo() );
+ rDoc.SetEndNoteInfo( *m_pEndNoteInfo );
+ m_pEndNoteInfo.reset( pInf );
+}
+
+SwUndoDontExpandFormat::SwUndoDontExpandFormat( const SwPosition& rPos )
+ : SwUndo( SwUndoId::DONTEXPAND, &rPos.GetDoc() )
+ , m_nNodeIndex( rPos.nNode.GetIndex() )
+ , m_nContentIndex( rPos.nContent.GetIndex() )
+{
+}
+
+void SwUndoDontExpandFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ SwPosition& rPos = *pPam->GetPoint();
+ rPos.nNode = m_nNodeIndex;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContentIndex);
+ pDoc->DontExpandFormat( rPos, false );
+}
+
+void SwUndoDontExpandFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ SwPosition& rPos = *pPam->GetPoint();
+ rPos.nNode = m_nNodeIndex;
+ rPos.nContent.Assign( rPos.nNode.GetNode().GetContentNode(), m_nContentIndex);
+ pDoc->DontExpandFormat( rPos );
+}
+
+void SwUndoDontExpandFormat::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM & rPam = rContext.GetRepeatPaM();
+ SwDoc & rDoc = rContext.GetDoc();
+ rDoc.DontExpandFormat( *rPam.GetPoint() );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unbkmk.cxx b/sw/source/core/undo/unbkmk.cxx
new file mode 100644
index 000000000..f7d55c2d2
--- /dev/null
+++ b/sw/source/core/undo/unbkmk.cxx
@@ -0,0 +1,222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <UndoBookmark.hxx>
+
+#include <strings.hrc>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+
+#include <UndoCore.hxx>
+#include <IMark.hxx>
+#include <rolbck.hxx>
+
+#include <SwRewriter.hxx>
+
+SwUndoBookmark::SwUndoBookmark( SwUndoId nUndoId,
+ const ::sw::mark::IMark& rBkmk )
+ : SwUndo( nUndoId, &rBkmk.GetMarkPos().GetDoc() )
+ , m_pHistoryBookmark(new SwHistoryBookmark(rBkmk, true, rBkmk.IsExpanded()))
+{
+}
+
+SwUndoBookmark::~SwUndoBookmark()
+{
+}
+
+void SwUndoBookmark::SetInDoc( SwDoc* pDoc )
+{
+ m_pHistoryBookmark->SetInDoc( pDoc, false );
+}
+
+void SwUndoBookmark::ResetInDoc( SwDoc& rDoc )
+{
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ for ( IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->getAllMarksBegin();
+ ppBkmk != pMarkAccess->getAllMarksEnd();
+ ++ppBkmk )
+ {
+ if ( m_pHistoryBookmark->IsEqualBookmark( **ppBkmk ) )
+ {
+ pMarkAccess->deleteMark(ppBkmk, false);
+ break;
+ }
+ }
+}
+
+SwRewriter SwUndoBookmark::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, m_pHistoryBookmark->GetName());
+
+ return aResult;
+}
+
+SwUndoInsBookmark::SwUndoInsBookmark( const ::sw::mark::IMark& rBkmk )
+ : SwUndoBookmark( SwUndoId::INSBOOKMARK, rBkmk )
+{
+}
+
+void SwUndoInsBookmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ ResetInDoc( rContext.GetDoc() );
+}
+
+void SwUndoInsBookmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetInDoc( &rContext.GetDoc() );
+}
+
+SwUndoDeleteBookmark::SwUndoDeleteBookmark( const ::sw::mark::IMark& rBkmk )
+ : SwUndoBookmark( SwUndoId::DELBOOKMARK, rBkmk )
+{
+}
+
+void SwUndoDeleteBookmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetInDoc( &rContext.GetDoc() );
+}
+
+void SwUndoDeleteBookmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ ResetInDoc( rContext.GetDoc() );
+}
+
+SwUndoRenameBookmark::SwUndoRenameBookmark( const OUString& rOldName, const OUString& rNewName, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::BOOKMARK_RENAME, &rDoc )
+ , m_sOldName( rOldName )
+ , m_sNewName( rNewName )
+{
+}
+
+SwUndoRenameBookmark::~SwUndoRenameBookmark()
+{
+}
+
+static OUString lcl_QuoteName(std::u16string_view rName)
+{
+ static const OUString sStart = SwResId(STR_START_QUOTE);
+ static const OUString sEnd = SwResId(STR_END_QUOTE);
+ return sStart + rName + sEnd;
+}
+
+SwRewriter SwUndoRenameBookmark::GetRewriter() const
+{
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, lcl_QuoteName(m_sOldName));
+ aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
+ aRewriter.AddRule(UndoArg3, lcl_QuoteName(m_sNewName));
+ return aRewriter;
+}
+
+void SwUndoRenameBookmark::Rename(::sw::UndoRedoContext const & rContext, const OUString& sFrom, const OUString& sTo)
+{
+ IDocumentMarkAccess* const pMarkAccess = rContext.GetDoc().getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findMark(sFrom);
+ if (ppBkmk != pMarkAccess->getAllMarksEnd())
+ {
+ pMarkAccess->renameMark( *ppBkmk, sTo );
+ }
+}
+
+void SwUndoRenameBookmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ Rename(rContext, m_sNewName, m_sOldName);
+}
+
+void SwUndoRenameBookmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ Rename(rContext, m_sOldName, m_sNewName);
+}
+
+SwUndoInsNoTextFieldmark::SwUndoInsNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::INSERT, &rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryNoTextFieldmark(new SwHistoryNoTextFieldmark(rFieldmark))
+{
+}
+
+void SwUndoInsNoTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->ResetInDoc(rContext.GetDoc());
+}
+
+void SwUndoInsNoTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+SwUndoDelNoTextFieldmark::SwUndoDelNoTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::DELETE, &rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryNoTextFieldmark(new SwHistoryNoTextFieldmark(rFieldmark))
+{
+}
+
+SwUndoDelNoTextFieldmark::~SwUndoDelNoTextFieldmark() = default;
+
+void SwUndoDelNoTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+void SwUndoDelNoTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryNoTextFieldmark->ResetInDoc(rContext.GetDoc());
+}
+
+SwUndoInsTextFieldmark::SwUndoInsTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::INSERT, &rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryTextFieldmark(new SwHistoryTextFieldmark(rFieldmark))
+{
+}
+
+void SwUndoInsTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->ResetInDoc(rContext.GetDoc());
+}
+
+void SwUndoInsTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+SwUndoDelTextFieldmark::SwUndoDelTextFieldmark(const ::sw::mark::IFieldmark& rFieldmark)
+ : SwUndo(SwUndoId::DELETE, &rFieldmark.GetMarkPos().GetDoc())
+ , m_pHistoryTextFieldmark(new SwHistoryTextFieldmark(rFieldmark))
+{
+}
+
+SwUndoDelTextFieldmark::~SwUndoDelTextFieldmark() = default;
+
+void SwUndoDelTextFieldmark::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->SetInDoc(&rContext.GetDoc(), false);
+}
+
+void SwUndoDelTextFieldmark::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pHistoryTextFieldmark->ResetInDoc(rContext.GetDoc());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undel.cxx b/sw/source/core/undo/undel.cxx
new file mode 100644
index 000000000..95c9d612b
--- /dev/null
+++ b/sw/source/core/undo/undel.cxx
@@ -0,0 +1,1348 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoDelete.hxx>
+
+#include <libxml/xmlwriter.h>
+#include <editeng/formatbreakitem.hxx>
+
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <unotools/charclass.hxx>
+#include <frmfmt.hxx>
+#include <fmtanchr.hxx>
+#include <doc.hxx>
+#include <UndoManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <swtable.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <poolfmt.hxx>
+#include <mvsave.hxx>
+#include <docary.hxx>
+#include <frmtool.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <fmtpdsc.hxx>
+#include <vector>
+
+// DELETE
+/* lcl_MakeAutoFrames has to call MakeFrames for objects bounded "AtChar"
+ ( == AUTO ), if the anchor frame has be moved via MoveNodes(..) and
+ DelFrames(..)
+*/
+static void lcl_MakeAutoFrames( const SwFrameFormats& rSpzArr, SwNodeOffset nMovedIndex )
+{
+ for( size_t n = 0; n < rSpzArr.size(); ++n )
+ {
+ SwFrameFormat * pFormat = rSpzArr[n];
+ const SwFormatAnchor* pAnchor = &pFormat->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ const SwPosition* pAPos = pAnchor->GetContentAnchor();
+ if( pAPos && nMovedIndex == pAPos->nNode.GetIndex() )
+ pFormat->MakeFrames();
+ }
+ }
+}
+
+static SwTextNode * FindFirstAndNextNode(SwDoc & rDoc, SwUndRng const& rRange,
+ SwRedlineSaveDatas const& rRedlineSaveData,
+ SwTextNode *& o_rpFirstMergedDeletedTextNode)
+{
+ // redlines are corrected now to exclude the deleted node
+ assert(rRange.m_nEndContent == 0);
+ SwNodeOffset nEndOfRedline(0);
+ for (size_t i = 0; i < rRedlineSaveData.size(); ++i)
+ {
+ auto const& rRedline(rRedlineSaveData[i]);
+ if (rRedline.m_nSttNode <= rRange.m_nSttNode
+ // coverity[copy_paste_error : FALSE] : m_nEndNode is intentional here
+ && rRedline.m_nSttNode < rRange.m_nEndNode
+ && rRange.m_nEndNode <= rRedline.m_nEndNode
+ && rRedline.GetType() == RedlineType::Delete)
+ {
+ nEndOfRedline = rRedline.m_nEndNode;
+ o_rpFirstMergedDeletedTextNode = rDoc.GetNodes()[rRedline.m_nSttNode]->GetTextNode();
+ assert(rRange.m_nSttNode == rRange.m_nEndNode - 1); // otherwise this needs to iterate more RL to find the first node?
+ break;
+ }
+ }
+ if (nEndOfRedline)
+ {
+ assert(o_rpFirstMergedDeletedTextNode);
+ SwTextNode * pNextNode(nullptr);
+ for (SwNodeOffset i = rRange.m_nEndNode; /* i <= nEndOfRedline */; ++i)
+ {
+ SwNode *const pNode(rDoc.GetNodes()[i]);
+ assert(!pNode->IsEndNode()); // cannot be both leaving section here *and* overlapping redline
+ if (pNode->IsStartNode())
+ {
+ i = pNode->EndOfSectionIndex(); // will be incremented again
+ }
+ else if (pNode->IsTextNode())
+ {
+ pNextNode = pNode->GetTextNode();
+ break;
+ }
+ }
+ assert(pNextNode);
+ return pNextNode;
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+static void DelFullParaMoveFrames(SwDoc & rDoc, SwUndRng const& rRange,
+ SwRedlineSaveDatas const& rRedlineSaveData)
+{
+ SwTextNode * pFirstMergedDeletedTextNode(nullptr);
+ SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, rRange,
+ rRedlineSaveData, pFirstMergedDeletedTextNode);
+ if (!pNextNode)
+ return;
+
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pFirstMergedDeletedTextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ assert(pFrame->GetMergedPara());
+ assert(pFrame->GetMergedPara()->pFirstNode == pFirstMergedDeletedTextNode);
+ assert(pNextNode->GetIndex() <= pFrame->GetMergedPara()->pLastNode->GetIndex());
+ frames.push_back(pFrame);
+ }
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ // sw_redlinehide: don't need FrameMode::Existing here
+ // because everything from pNextNode onwards is already
+ // correctly hidden
+ pFrame->RegisterToNode(*pNextNode, true);
+ }
+}
+
+// SwUndoDelete has to perform a deletion and to record anything that is needed
+// to restore the situation before the deletion. Unfortunately a part of the
+// deletion will be done after calling this Ctor, this has to be kept in mind!
+// In this Ctor only the complete paragraphs will be deleted, the joining of
+// the first and last paragraph of the selection will be handled outside this
+// function.
+// Here are the main steps of the function:
+// 1. Deletion/recording of content indices of the selection: footnotes, fly
+// frames and bookmarks
+// Step 1 could shift all nodes by deletion of footnotes => nNdDiff will be set.
+// 2. If the paragraph where the selection ends, is the last content of a
+// section so that this section becomes empty when the paragraphs will be
+// joined we have to do some smart actions ;-) The paragraph will be moved
+// outside the section and replaced by a dummy text node, the complete
+// section will be deleted in step 3. The difference between replacement
+// dummy and original is nReplacementDummy.
+// 3. Moving complete selected nodes into the UndoArray. Before this happens the
+// selection has to be extended if there are sections which would become
+// empty otherwise. BTW: sections will be moved into the UndoArray if they
+// are complete part of the selection. Sections starting or ending outside
+// of the selection will not be removed from the DocNodeArray even they got
+// a "dummy"-copy in the UndoArray.
+// 4. We have to anticipate the joining of the two paragraphs if the start
+// paragraph is inside a section and the end paragraph not. Then we have to
+// move the paragraph into this section and to record this in nSectDiff.
+SwUndoDelete::SwUndoDelete(
+ SwPaM& rPam,
+ SwDeleteFlags const flags,
+ bool bFullPara,
+ bool bCalledByTableCpy )
+ : SwUndo(SwUndoId::DELETE, &rPam.GetDoc()),
+ SwUndRng( rPam ),
+ m_nNode(0),
+ m_nNdDiff(0),
+ m_nSectDiff(0),
+ m_nReplaceDummy(0),
+ m_nSetPos(0),
+ m_bGroup( false ),
+ m_bBackSp( false ),
+ m_bJoinNext( false ),
+ m_bTableDelLastNd( false ),
+ // bFullPara is set e.g. if an empty paragraph before a table is deleted
+ m_bDelFullPara( bFullPara ),
+ m_bResetPgDesc( false ),
+ m_bResetPgBrk( false ),
+ m_bFromTableCopy( bCalledByTableCpy )
+ , m_DeleteFlags(flags)
+{
+ assert(!m_bDelFullPara || !(m_DeleteFlags & SwDeleteFlags::ArtificialSelection));
+
+ m_bCacheComment = false;
+
+ SwDoc& rDoc = rPam.GetDoc();
+
+ if( !rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ m_pRedlSaveData.reset(new SwRedlineSaveDatas);
+ if( !FillSaveData( rPam, *m_pRedlSaveData ))
+ {
+ m_pRedlSaveData.reset();
+ }
+ }
+
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+
+ // delete all footnotes for now
+ const SwPosition *pStt = rPam.Start(),
+ *pEnd = rPam.End();
+
+ // Step 1. deletion/record of content indices
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ DelBookmarks(pStt->nNode, pEnd->nNode);
+ }
+ else
+ {
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask
+ | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0)));
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ if (m_nEndNode - m_nSttNode > SwNodeOffset(1)) // check for fully selected nodes
+ {
+ SwNodeIndex const start(pStt->nNode, +1);
+ DelBookmarks(start, pEnd->nNode);
+ }
+ }
+
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ // Is already anything deleted?
+ m_nNdDiff = m_nSttNode - pStt->nNode.GetIndex();
+
+ m_bJoinNext = !bFullPara && pEnd == rPam.GetPoint();
+ m_bBackSp = !bFullPara && !m_bJoinNext;
+
+ SwTextNode *pSttTextNd = nullptr, *pEndTextNd = nullptr;
+ if( !bFullPara )
+ {
+ pSttTextNd = pStt->nNode.GetNode().GetTextNode();
+ pEndTextNd = m_nSttNode == m_nEndNode
+ ? pSttTextNd
+ : pEnd->nNode.GetNode().GetTextNode();
+ }
+ else if (m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
+ }
+
+ bool bMoveNds = *pStt != *pEnd // any area still existent?
+ && ( SaveContent( pStt, pEnd, pSttTextNd, pEndTextNd ) || m_bFromTableCopy );
+
+ if( pSttTextNd && pEndTextNd && pSttTextNd != pEndTextNd )
+ {
+ // two different TextNodes, thus save also the TextFormatCollection
+ m_pHistory->Add( pSttTextNd->GetTextColl(),pStt->nNode.GetIndex(), SwNodeType::Text );
+ m_pHistory->Add( pEndTextNd->GetTextColl(),pEnd->nNode.GetIndex(), SwNodeType::Text );
+
+ if( !m_bJoinNext ) // Selection from bottom to top
+ {
+ // When using JoinPrev() all AUTO-PageBreak's will be copied
+ // correctly. To restore them with UNDO, Auto-PageBreak of the
+ // EndNode needs to be reset. Same for PageDesc and ColBreak.
+ if( pEndTextNd->HasSwAttrSet() )
+ {
+ SwRegHistory aRegHist( *pEndTextNd, m_pHistory.get() );
+ if( SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
+ RES_BREAK, false ) )
+ pEndTextNd->ResetAttr( RES_BREAK );
+ if( pEndTextNd->HasSwAttrSet() &&
+ SfxItemState::SET == pEndTextNd->GetpSwAttrSet()->GetItemState(
+ RES_PAGEDESC, false ) )
+ pEndTextNd->ResetAttr( RES_PAGEDESC );
+ }
+ }
+ }
+
+ // Move now also the PaM. The SPoint is at the beginning of a SSelection.
+ if( pEnd == rPam.GetPoint() && ( !bFullPara || pSttTextNd || pEndTextNd ) )
+ rPam.Exchange();
+
+ if( !pSttTextNd && !pEndTextNd )
+ --rPam.GetPoint()->nNode;
+ rPam.DeleteMark(); // the SPoint is in the selection
+
+ if( !pEndTextNd )
+ m_nEndContent = 0;
+ if( !pSttTextNd )
+ m_nSttContent = 0;
+
+ if( bMoveNds ) // Do Nodes exist that need to be moved?
+ {
+ SwNodes& rNds = rDoc.GetUndoManager().GetUndoNodes();
+ SwNodes& rDocNds = rDoc.GetNodes();
+ SwNodeRange aRg( rDocNds, m_nSttNode - m_nNdDiff,
+ rDocNds, m_nEndNode - m_nNdDiff );
+ if( !bFullPara && !pEndTextNd &&
+ &aRg.aEnd.GetNode() != &rDoc.GetNodes().GetEndOfContent() )
+ {
+ SwNode* pNode = aRg.aEnd.GetNode().StartOfSectionNode();
+ if( pNode->GetIndex() >= m_nSttNode - m_nNdDiff )
+ ++aRg.aEnd; // Deletion of a complete table
+ }
+ SwNode* pTmpNd;
+ // Step 2: Expand selection if necessary
+ if( m_bJoinNext || bFullPara )
+ {
+ // If all content of a section will be moved into Undo, the section
+ // itself should be moved completely.
+ while( aRg.aEnd.GetIndex() + 2 < rDocNds.Count() &&
+ ( (pTmpNd = rDocNds[ aRg.aEnd.GetIndex()+1 ])->IsEndNode() &&
+ pTmpNd->StartOfSectionNode()->IsSectionNode() &&
+ pTmpNd->StartOfSectionNode()->GetIndex() >= aRg.aStart.GetIndex() ) )
+ ++aRg.aEnd;
+ m_nReplaceDummy = aRg.aEnd.GetIndex() + m_nNdDiff - m_nEndNode;
+ if( m_nReplaceDummy )
+ { // The selection has been expanded, because
+ ++aRg.aEnd;
+ if( pEndTextNd )
+ {
+ // The end text node has to leave the (expanded) selection
+ // The dummy is needed because MoveNodes deletes empty
+ // sections
+ ++m_nReplaceDummy;
+ SwNodeRange aMvRg( *pEndTextNd, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
+ SwPosition aSplitPos( *pEndTextNd );
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
+ --aRg.aEnd;
+ }
+ else
+ m_nReplaceDummy = SwNodeOffset(0);
+ }
+ }
+ if( m_bBackSp || bFullPara )
+ {
+ // See above, the selection has to be expanded if there are "nearly
+ // empty" sections and a replacement dummy has to be set if needed.
+ while( SwNodeOffset(1) < aRg.aStart.GetIndex() &&
+ ( (pTmpNd = rDocNds[ aRg.aStart.GetIndex()-1 ])->IsSectionNode() &&
+ pTmpNd->EndOfSectionIndex() < aRg.aEnd.GetIndex() ) )
+ --aRg.aStart;
+ if( pSttTextNd )
+ {
+ m_nReplaceDummy = m_nSttNode - m_nNdDiff - aRg.aStart.GetIndex();
+ if( m_nReplaceDummy )
+ {
+ SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
+ SwPosition aSplitPos( *pSttTextNd );
+ ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().SplitNode( aSplitPos, false );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
+ --aRg.aStart;
+ }
+ }
+ }
+
+ if( m_bFromTableCopy )
+ {
+ if( !pEndTextNd )
+ {
+ if( pSttTextNd )
+ ++aRg.aStart;
+ else if( !bFullPara && !aRg.aEnd.GetNode().IsContentNode() )
+ --aRg.aEnd;
+ }
+ }
+ else if (pSttTextNd && (pEndTextNd || pSttTextNd->GetText().getLength()))
+ ++aRg.aStart;
+
+ // Step 3: Moving into UndoArray...
+ m_nNode = rNds.GetEndOfContent().GetIndex();
+ rDocNds.MoveNodes( aRg, rNds, SwNodeIndex( rNds.GetEndOfContent() ));
+ m_pMvStt.reset( new SwNodeIndex( rNds, m_nNode ) );
+ // remember difference!
+ m_nNode = rNds.GetEndOfContent().GetIndex() - m_nNode;
+
+ if( pSttTextNd && pEndTextNd )
+ {
+ //Step 4: Moving around sections
+ m_nSectDiff = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ // nSect is the number of sections which starts(ends) between start
+ // and end node of the selection. The "loser" paragraph has to be
+ // moved into the section(s) of the "winner" paragraph
+ if( m_nSectDiff )
+ {
+ if( m_bJoinNext )
+ {
+ SwNodeRange aMvRg( *pEndTextNd, SwNodeOffset(0), *pEndTextNd, SwNodeOffset(1) );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aStart );
+ }
+ else
+ {
+ SwNodeRange aMvRg( *pSttTextNd, SwNodeOffset(0), *pSttTextNd, SwNodeOffset(1) );
+ rDocNds.MoveNodes( aMvRg, rDocNds, aRg.aEnd );
+ }
+ }
+ }
+ if( m_nSectDiff || m_nReplaceDummy )
+ lcl_MakeAutoFrames( *rDoc.GetSpzFrameFormats(),
+ m_bJoinNext ? pEndTextNd->GetIndex() : pSttTextNd->GetIndex() );
+ }
+ else
+ m_nNode = SwNodeOffset(0); // moved no node -> no difference at the end
+
+ // Are there any Nodes that got deleted before that (FootNotes
+ // have ContentNodes)?
+ if( !pSttTextNd && !pEndTextNd )
+ {
+ m_nNdDiff = m_nSttNode - rPam.GetPoint()->nNode.GetIndex() - (bFullPara ? 0 : 1);
+ rPam.Move( fnMoveForward, GoInNode );
+ }
+ else
+ {
+ m_nNdDiff = m_nSttNode;
+ if( m_nSectDiff && m_bBackSp )
+ m_nNdDiff += m_nSectDiff;
+ m_nNdDiff -= rPam.GetPoint()->nNode.GetIndex();
+ }
+
+ if( !rPam.GetNode().IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ // is a history necessary here at all?
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+}
+
+bool SwUndoDelete::SaveContent( const SwPosition* pStt, const SwPosition* pEnd,
+ SwTextNode* pSttTextNd, SwTextNode* pEndTextNd )
+{
+ SwNodeOffset nNdIdx = pStt->nNode.GetIndex();
+ // 1 - copy start in Start-String
+ if( pSttTextNd )
+ {
+ bool bOneNode = m_nSttNode == m_nEndNode;
+ SwRegHistory aRHst( *pSttTextNd, m_pHistory.get() );
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pSttTextNd->GetpSwpHints(), nNdIdx,
+ 0, pSttTextNd->GetText().getLength(), true );
+ if( !bOneNode && pSttTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pSttTextNd->GetpSwAttrSet(), nNdIdx );
+
+ // the length might have changed (!!Fields!!)
+ sal_Int32 nLen = (bOneNode
+ ? pEnd->nContent.GetIndex()
+ : pSttTextNd->GetText().getLength())
+ - pStt->nContent.GetIndex();
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aSttStr = pSttTextNd->GetText().copy(m_nSttContent, nLen);
+ pSttTextNd->EraseText( pStt->nContent, nLen );
+ if( pSttTextNd->GetpSwpHints() )
+ pSttTextNd->GetpSwpHints()->DeRegister();
+
+ // METADATA: store
+ bool emptied( !m_aSttStr->isEmpty() && !pSttTextNd->Len() );
+ if (!bOneNode || emptied) // merging may overwrite xmlids...
+ {
+ m_pMetadataUndoStart = emptied
+ ? pSttTextNd->CreateUndoForDelete()
+ : pSttTextNd->CreateUndo();
+ }
+
+ if( bOneNode )
+ return false; // stop moving more nodes
+ }
+
+ // 2 - copy end into End-String
+ if( pEndTextNd )
+ {
+ SwIndex aEndIdx( pEndTextNd );
+ nNdIdx = pEnd->nNode.GetIndex();
+ SwRegHistory aRHst( *pEndTextNd, m_pHistory.get() );
+
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), nNdIdx, 0,
+ pEndTextNd->GetText().getLength(), true );
+
+ if( pEndTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), nNdIdx );
+
+ // delete now also the text (all attribute changes are added to
+ // UNDO history)
+ m_aEndStr = pEndTextNd->GetText().copy( 0, pEnd->nContent.GetIndex() );
+ pEndTextNd->EraseText( aEndIdx, pEnd->nContent.GetIndex() );
+ if( pEndTextNd->GetpSwpHints() )
+ pEndTextNd->GetpSwpHints()->DeRegister();
+
+ // METADATA: store
+ bool emptied = !m_aEndStr->isEmpty() && !pEndTextNd->Len();
+
+ m_pMetadataUndoEnd = emptied
+ ? pEndTextNd->CreateUndoForDelete()
+ : pEndTextNd->CreateUndo();
+ }
+
+ // if there are only two Nodes then we're done
+ if( ( pSttTextNd || pEndTextNd ) && m_nSttNode + 1 == m_nEndNode )
+ return false; // do not move any Node
+
+ return true; // move Nodes lying in between
+}
+
+bool SwUndoDelete::CanGrouping( SwDoc& rDoc, const SwPaM& rDelPam )
+{
+ // Is Undo greater than one Node (that is Start and EndString)?
+ if( !m_aSttStr || m_aSttStr->isEmpty() || m_aEndStr )
+ return false;
+
+ // only the deletion of single char's can be condensed
+ if( m_nSttNode != m_nEndNode || ( !m_bGroup && m_nSttContent+1 != m_nEndContent ))
+ return false;
+
+ const SwPosition *pStt = rDelPam.Start(),
+ *pEnd = rDelPam.End();
+
+ if( pStt->nNode != pEnd->nNode ||
+ pStt->nContent.GetIndex()+1 != pEnd->nContent.GetIndex() ||
+ pEnd->nNode != m_nSttNode )
+ return false;
+
+ // Distinguish between BackSpace and Delete because the Undo array needs to
+ // be constructed differently!
+ if( pEnd->nContent == m_nSttContent )
+ {
+ if( m_bGroup && !m_bBackSp ) return false;
+ m_bBackSp = true;
+ }
+ else if( pStt->nContent == m_nSttContent )
+ {
+ if( m_bGroup && m_bBackSp ) return false;
+ m_bBackSp = false;
+ }
+ else
+ return false;
+
+ // are both Nodes (Node/Undo array) TextNodes at all?
+ SwTextNode * pDelTextNd = pStt->nNode.GetNode().GetTextNode();
+ if( !pDelTextNd ) return false;
+
+ sal_Int32 nUChrPos = m_bBackSp ? 0 : m_aSttStr->getLength()-1;
+ sal_Unicode cDelChar = pDelTextNd->GetText()[ pStt->nContent.GetIndex() ];
+ CharClass& rCC = GetAppCharClass();
+ if( ( CH_TXTATR_BREAKWORD == cDelChar || CH_TXTATR_INWORD == cDelChar ) ||
+ rCC.isLetterNumeric( OUString( cDelChar ), 0 ) !=
+ rCC.isLetterNumeric( *m_aSttStr, nUChrPos ) )
+ return false;
+
+ // tdf#132725 - if at-char/at-para flys would be deleted, don't group!
+ // DelContentIndex() would be called at the wrong time here, the indexes
+ // in the stored SwHistoryTextFlyCnt would be wrong when Undo is invoked
+ if (IsFlySelectedByCursor(rDoc, *pStt, *pEnd))
+ {
+ return false;
+ }
+
+ {
+ SwRedlineSaveDatas aTmpSav;
+ const bool bSaved = FillSaveData( rDelPam, aTmpSav, false );
+
+ bool bOk = ( !m_pRedlSaveData && !bSaved ) ||
+ ( m_pRedlSaveData && bSaved &&
+ SwUndo::CanRedlineGroup( *m_pRedlSaveData, aTmpSav, m_bBackSp ));
+ // aTmpSav.DeleteAndDestroyAll();
+ if( !bOk )
+ return false;
+
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( rDelPam, false, RedlineType::Any );
+ }
+
+ // Both 'deletes' can be consolidated, so 'move' the related character
+ if( m_bBackSp )
+ m_nSttContent--; // BackSpace: add char to array!
+ else
+ {
+ m_nEndContent++; // Delete: attach char at the end
+ nUChrPos++;
+ }
+ m_aSttStr = m_aSttStr->replaceAt( nUChrPos, 0, rtl::OUStringChar(cDelChar) );
+ pDelTextNd->EraseText( pStt->nContent, 1 );
+
+ m_bGroup = true;
+ return true;
+}
+
+SwUndoDelete::~SwUndoDelete()
+{
+ if( m_pMvStt ) // Delete also the selection from UndoNodes array
+ {
+ // Insert saves content in IconSection
+ m_pMvStt->GetNode().GetNodes().Delete( *m_pMvStt, m_nNode );
+ m_pMvStt.reset();
+ }
+ m_pRedlSaveData.reset();
+}
+
+static SwRewriter lcl_RewriterFromHistory(SwHistory & rHistory)
+{
+ SwRewriter aRewriter;
+
+ bool bDone = false;
+
+ for ( sal_uInt16 n = 0; n < rHistory.Count(); n++)
+ {
+ OUString aDescr = rHistory[n]->GetDescription();
+
+ if (!aDescr.isEmpty())
+ {
+ aRewriter.AddRule(UndoArg2, aDescr);
+
+ bDone = true;
+ break;
+ }
+ }
+
+ if (! bDone)
+ {
+ aRewriter.AddRule(UndoArg2, SwResId(STR_FIELD));
+ }
+
+ return aRewriter;
+}
+
+static bool lcl_IsSpecialCharacter(sal_Unicode nChar)
+{
+ switch (nChar)
+ {
+ case CH_TXTATR_BREAKWORD:
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_TAB:
+ case CH_TXTATR_NEWLINE:
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static OUString lcl_DenotedPortion(std::u16string_view rStr, sal_Int32 nStart, sal_Int32 nEnd, bool bQuoted)
+{
+ OUString aResult;
+
+ auto nCount = nEnd - nStart;
+ if (nCount > 0)
+ {
+ sal_Unicode cLast = rStr[nEnd - 1];
+ if (lcl_IsSpecialCharacter(cLast))
+ {
+ switch(cLast)
+ {
+ case CH_TXTATR_TAB:
+ aResult = SwResId(STR_UNDO_TABS, nCount);
+
+ break;
+ case CH_TXTATR_NEWLINE:
+ aResult = SwResId(STR_UNDO_NLS, nCount);
+
+ break;
+
+ case CH_TXTATR_INWORD:
+ case CH_TXTATR_BREAKWORD:
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+ break;
+
+ case CH_TXT_ATR_INPUTFIELDSTART:
+ case CH_TXT_ATR_INPUTFIELDEND:
+ case CH_TXT_ATR_FORMELEMENT:
+ case CH_TXT_ATR_FIELDSTART:
+ case CH_TXT_ATR_FIELDSEP:
+ case CH_TXT_ATR_FIELDEND:
+ break; // nothing?
+
+ default:
+ assert(!"unexpected special character");
+ break;
+ }
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, OUString::number(nCount));
+ aResult = aRewriter.Apply(aResult);
+ }
+ else if (bQuoted)
+ {
+ aResult = SwResId(STR_START_QUOTE) +
+ rStr.substr(nStart, nCount) +
+ SwResId(STR_END_QUOTE);
+ }
+ else
+ aResult = rStr.substr(nStart, nCount);
+ }
+
+ return aResult;
+}
+
+OUString DenoteSpecialCharacters(const OUString & rStr, bool bQuoted)
+{
+ OUStringBuffer aResult;
+
+ if (!rStr.isEmpty())
+ {
+ bool bStart = false;
+ sal_Int32 nStart = 0;
+ sal_Unicode cLast = 0;
+
+ for( sal_Int32 i = 0; i < rStr.getLength(); i++)
+ {
+ if (lcl_IsSpecialCharacter(rStr[i]))
+ {
+ if (cLast != rStr[i])
+ bStart = true;
+
+ }
+ else
+ {
+ if (lcl_IsSpecialCharacter(cLast))
+ bStart = true;
+ }
+
+ if (bStart)
+ {
+ aResult.append(lcl_DenotedPortion(rStr, nStart, i, bQuoted));
+
+ nStart = i;
+ bStart = false;
+ }
+
+ cLast = rStr[i];
+ }
+
+ aResult.append(lcl_DenotedPortion(rStr, nStart, rStr.getLength(), bQuoted));
+ }
+ else
+ aResult = SwRewriter::GetPlaceHolder(UndoArg2);
+
+ return aResult.makeStringAndClear();
+}
+
+SwRewriter SwUndoDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ if (m_nNode != SwNodeOffset(0))
+ {
+ if (!m_sTableName.isEmpty())
+ {
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
+ aRewriter.AddRule(UndoArg2, m_sTableName);
+ aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
+
+ OUString sTmp = aRewriter.Apply(SwResId(STR_TABLE_NAME));
+ aResult.AddRule(UndoArg1, sTmp);
+ }
+ else
+ aResult.AddRule(UndoArg1, SwResId(STR_PARAGRAPHS));
+ }
+ else
+ {
+ OUString aStr;
+
+ if (m_aSttStr && m_aEndStr && m_aSttStr->isEmpty() &&
+ m_aEndStr->isEmpty())
+ {
+ aStr = SwResId(STR_PARAGRAPH_UNDO);
+ }
+ else
+ {
+ std::optional<OUString> aTmpStr;
+ if (m_aSttStr)
+ aTmpStr = m_aSttStr;
+ else if (m_aEndStr)
+ aTmpStr = m_aEndStr;
+
+ if (aTmpStr)
+ {
+ aStr = DenoteSpecialCharacters(*aTmpStr);
+ }
+ else
+ {
+ aStr = SwRewriter::GetPlaceHolder(UndoArg2);
+ }
+ }
+
+ aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS));
+ if (m_pHistory)
+ {
+ SwRewriter aRewriter = lcl_RewriterFromHistory(*m_pHistory);
+ aStr = aRewriter.Apply(aStr);
+ }
+
+ aResult.AddRule(UndoArg1, aStr);
+ }
+
+ return aResult;
+}
+
+// Every object, anchored "AtContent" will be reanchored at rPos
+static void lcl_ReAnchorAtContentFlyFrames( const SwFrameFormats& rSpzArr, const SwPosition &rPos, SwNodeOffset nOldIdx )
+{
+ if( rSpzArr.empty() )
+ return;
+
+ SwFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+ const SwPosition* pAPos;
+ for( size_t n = 0; n < rSpzArr.size(); ++n )
+ {
+ pFormat = rSpzArr[n];
+ pAnchor = &pFormat->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ {
+ pAPos = pAnchor->GetContentAnchor();
+ if( pAPos && nOldIdx == pAPos->nNode.GetIndex() )
+ {
+ SwFormatAnchor aAnch( *pAnchor );
+ aAnch.SetAnchor( &rPos );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+}
+
+void SwUndoDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+
+ SwNodeOffset nCalcStt = m_nSttNode - m_nNdDiff;
+
+ if( m_nSectDiff && m_bBackSp )
+ nCalcStt += m_nSectDiff;
+
+ SwNodeIndex aIdx(rDoc.GetNodes(), nCalcStt);
+ SwNode* pInsNd = &aIdx.GetNode();
+ SwNode* pMovedNode = nullptr;
+
+ { // code block so that SwPosition is detached when deleting a Node
+ SwPosition aPos( aIdx );
+ if( !m_bDelFullPara )
+ {
+ assert(!m_bTableDelLastNd || pInsNd->IsTextNode());
+ if( pInsNd->IsTableNode() )
+ {
+ pInsNd = rDoc.GetNodes().MakeTextNode( aIdx,
+ rDoc.GetDfltTextFormatColl() );
+ --aIdx;
+ aPos.nNode = aIdx;
+ aPos.nContent.Assign( pInsNd->GetContentNode(), m_nSttContent );
+ }
+ else
+ {
+ if( pInsNd->IsContentNode() )
+ aPos.nContent.Assign( static_cast<SwContentNode*>(pInsNd), m_nSttContent );
+ if( !m_bTableDelLastNd )
+ pInsNd = nullptr; // do not delete Node!
+ }
+ }
+ else
+ pInsNd = nullptr; // do not delete Node!
+
+ bool bNodeMove = SwNodeOffset(0) != m_nNode;
+
+ if( m_aEndStr )
+ {
+ // discard attributes since they all saved!
+ SwTextNode * pTextNd;
+ if (!m_bDelFullPara && aPos.nNode.GetNode().IsSectionNode())
+ { // tdf#134250 section node wasn't deleted; but aPos must point to it in bNodeMove case below
+ assert(m_nSttContent == 0);
+ assert(!m_aSttStr);
+ pTextNd = rDoc.GetNodes()[aPos.nNode.GetIndex() + 1]->GetTextNode();
+ }
+ else
+ {
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+ }
+
+ if( pTextNd && pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd && pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ if( m_aSttStr && !m_bFromTableCopy )
+ {
+ SwNodeOffset nOldIdx = aPos.nNode.GetIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ // After the split all objects are anchored at the first
+ // paragraph, but the pHistory of the fly frame formats relies
+ // on anchoring at the start of the selection
+ // => selection backwards needs a correction.
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ pTextNd = aPos.nNode.GetNode().GetTextNode();
+ }
+ assert(pTextNd); // else where does m_aEndStr come from?
+ if( pTextNd )
+ {
+ OUString const ins( pTextNd->InsertText(*m_aEndStr, aPos.nContent,
+ SwInsertFlags::NOHINTEXPAND) );
+ assert(ins.getLength() == m_aEndStr->getLength()); // must succeed
+ (void) ins;
+ // METADATA: restore
+ pTextNd->RestoreMetadata(m_pMetadataUndoEnd);
+ }
+ }
+ else if (m_aSttStr && bNodeMove && pInsNd == nullptr)
+ {
+ SwTextNode * pNd = aPos.nNode.GetNode().GetTextNode();
+ if( pNd )
+ {
+ if (m_nSttContent < pNd->GetText().getLength())
+ {
+ SwNodeOffset nOldIdx = aPos.nNode.GetIndex();
+ rDoc.getIDocumentContentOperations().SplitNode( aPos, false );
+ if( m_bBackSp )
+ lcl_ReAnchorAtContentFlyFrames(*rDoc.GetSpzFrameFormats(), aPos, nOldIdx);
+ }
+ else
+ ++aPos.nNode;
+ }
+ }
+ if( m_nSectDiff )
+ {
+ SwNodeOffset nMoveIndex = aPos.nNode.GetIndex();
+ SwNodeOffset nDiff(0);
+ if( m_bJoinNext )
+ {
+ nMoveIndex += m_nSectDiff + 1;
+ pMovedNode = &aPos.nNode.GetNode();
+ }
+ else
+ {
+ nMoveIndex -= m_nSectDiff + 1;
+ ++nDiff;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.nNode, SwNodeOffset(0) - nDiff, aPos.nNode, SwNodeOffset(1) - nDiff );
+ --aPos.nNode;
+ if( !m_bJoinNext )
+ pMovedNode = &aPos.nNode.GetNode();
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx);
+ ++aPos.nNode;
+ }
+
+ if( bNodeMove )
+ {
+ SwNodeRange aRange( *m_pMvStt, SwNodeOffset(0), *m_pMvStt, m_nNode );
+ SwNodeIndex aCopyIndex( aPos.nNode, -1 );
+ rDoc.GetUndoManager().GetUndoNodes().Copy_(aRange, aPos.nNode,
+ // sw_redlinehide: delay creating frames: the flags on the
+ // nodes aren't necessarily up-to-date, and the redlines
+ // from m_pRedlSaveData aren't applied yet...
+ false);
+
+ if( m_nReplaceDummy )
+ {
+ SwNodeOffset nMoveIndex;
+ if( m_bJoinNext )
+ {
+ nMoveIndex = m_nEndNode - m_nNdDiff;
+ aPos.nNode = nMoveIndex + m_nReplaceDummy;
+ }
+ else
+ {
+ aPos = SwPosition( aCopyIndex );
+ nMoveIndex = aPos.nNode.GetIndex() + m_nReplaceDummy + 1;
+ }
+ SwNodeIndex aMvIdx(rDoc.GetNodes(), nMoveIndex);
+ SwNodeRange aRg( aPos.nNode, SwNodeOffset(0), aPos.nNode, SwNodeOffset(1) );
+ pMovedNode = &aPos.nNode.GetNode();
+ // tdf#131684 without deleting frames
+ rDoc.GetNodes().MoveNodes(aRg, rDoc.GetNodes(), aMvIdx, false);
+ rDoc.GetNodes().Delete( aMvIdx);
+ }
+ }
+
+ if( m_aSttStr )
+ {
+ aPos.nNode = m_nSttNode - m_nNdDiff + ( m_bJoinNext ? SwNodeOffset(0) : m_nReplaceDummy );
+ SwTextNode * pTextNd = aPos.nNode.GetNode().GetTextNode();
+ // If more than a single Node got deleted, also all "Node"
+ // attributes were saved
+ if (pTextNd != nullptr)
+ {
+ if( pTextNd->HasSwAttrSet() && bNodeMove && !m_aEndStr )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ // SectionNode mode and selection from top to bottom:
+ // -> in StartNode is still the rest of the Join => delete
+ aPos.nContent.Assign( pTextNd, m_nSttContent );
+ pTextNd->SetInSwUndo(true);
+ OUString const ins( pTextNd->InsertText(*m_aSttStr, aPos.nContent,
+ SwInsertFlags::NOHINTEXPAND) );
+ pTextNd->SetInSwUndo(false);
+ assert(ins.getLength() == m_aSttStr->getLength()); // must succeed
+ (void) ins;
+ // METADATA: restore
+ pTextNd->RestoreMetadata(m_pMetadataUndoStart);
+ }
+ }
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback(&rDoc, m_nSetPos, false);
+ if( m_nSetPos ) // there were Footnodes/FlyFrames
+ {
+ // are there others than these ones?
+ if( m_nSetPos < m_pHistory->Count() )
+ {
+ // if so save the attributes of the others
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
+ m_pHistory->Rollback(&rDoc);
+ m_pHistory->Move( 0, &aHstr );
+ }
+ else
+ {
+ m_pHistory->Rollback(&rDoc);
+ m_pHistory.reset();
+ }
+ }
+ }
+
+ if( m_bResetPgDesc || m_bResetPgBrk )
+ {
+ sal_uInt16 nStt = m_bResetPgDesc ? sal_uInt16(RES_PAGEDESC) : sal_uInt16(RES_BREAK);
+ sal_uInt16 nEnd = m_bResetPgBrk ? sal_uInt16(RES_BREAK) : sal_uInt16(RES_PAGEDESC);
+
+ SwNode* pNode = rDoc.GetNodes()[ m_nEndNode + 1 ];
+ if( pNode->IsContentNode() )
+ static_cast<SwContentNode*>(pNode)->ResetAttr( nStt, nEnd );
+ else if( pNode->IsTableNode() )
+ static_cast<SwTableNode*>(pNode)->GetTable().GetFrameFormat()->ResetFormatAttr( nStt, nEnd );
+ }
+ }
+ // delete the temporarily added Node
+ if (pInsNd && !m_bTableDelLastNd)
+ {
+ assert(&aIdx.GetNode() == pInsNd);
+ rDoc.GetNodes().Delete( aIdx );
+ }
+ if( m_pRedlSaveData )
+ SetSaveData(rDoc, *m_pRedlSaveData);
+
+ SwNodeOffset delFullParaEndNode(m_nEndNode);
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ SwTextNode * pFirstMergedDeletedTextNode(nullptr);
+ SwTextNode *const pNextNode = FindFirstAndNextNode(rDoc, *this,
+ *m_pRedlSaveData, pFirstMergedDeletedTextNode);
+ if (pNextNode)
+ {
+ bool bNonMerged(false);
+ std::vector<SwTextFrame*> frames;
+ SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNextNode);
+ for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ if (pFrame->getRootFrame()->HasMergedParas())
+ {
+ frames.push_back(pFrame);
+ }
+ else
+ {
+ bNonMerged = true;
+ }
+ }
+ for (SwTextFrame *const pFrame : frames)
+ {
+ // could either destroy the text frames, or move them...
+ // destroying them would have the advantage that we don't
+ // need special code to *exclude* pFirstMergedDeletedTextNode
+ // from MakeFrames for the layouts in Hide mode but not
+ // layouts in Show mode ...
+ // ... except that MakeFrames won't create them then :(
+ pFrame->RegisterToNode(*pFirstMergedDeletedTextNode);
+ assert(pFrame->GetMergedPara());
+ assert(!bNonMerged); // delFullParaEndNode is such an awful hack
+ (void) bNonMerged;
+ delFullParaEndNode = pFirstMergedDeletedTextNode->GetIndex();
+ }
+ }
+ }
+ else if (m_aSttStr && (!m_bFromTableCopy || SwNodeOffset(0) != m_nNode))
+ {
+ // only now do we have redlines in the document again; fix up the split
+ // frames
+ SwTextNode *const pStartNode(aIdx.GetNodes()[m_nSttNode]->GetTextNode());
+ assert(pStartNode);
+ sw::RecreateStartTextFrames(*pStartNode);
+ }
+
+ // create frames after SetSaveData has recreated redlines
+ if (SwNodeOffset(0) != m_nNode)
+ {
+ // tdf#136453 only if section nodes at the start
+ if (m_bBackSp && m_nReplaceDummy != SwNodeOffset(0))
+ {
+ // tdf#134252 *first* create outer section frames
+ // note: text node m_nSttNode currently has frame with an upper;
+ // there's a hack in InsertCnt_() to move it below new section frame
+ SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode - m_nReplaceDummy);
+ SwNodeIndex const end(rDoc.GetNodes(), m_nSttNode); // exclude m_nSttNode
+ ::MakeFrames(&rDoc, start, end);
+ }
+ // tdf#121031 if the start node is a text node, it already has a frame;
+ // if it's a table, it does not
+ // tdf#109376 exception: end on non-text-node -> start node was inserted
+ assert(!m_bDelFullPara || (m_nSectDiff == SwNodeOffset(0)));
+ SwNodeIndex const start(rDoc.GetNodes(), m_nSttNode +
+ ((m_bDelFullPara || !rDoc.GetNodes()[m_nSttNode]->IsTextNode() || pInsNd)
+ ? 0 : 1));
+ // don't include end node in the range: it may have been merged already
+ // by the start node, or it may be merged by one of the moved nodes,
+ // but if it isn't merged, its current frame(s) should be good...
+ SwNodeIndex const end(rDoc.GetNodes(), m_bDelFullPara
+ ? delFullParaEndNode
+ // tdf#147310 SwDoc::DeleteRowCol() may delete whole table - end must be node following table!
+ : (m_nEndNode + (rDoc.GetNodes()[m_nSttNode]->IsTableNode() && rDoc.GetNodes()[m_nEndNode]->IsEndNode() ? 1 : 0)));
+ ::MakeFrames(&rDoc, start, end);
+ }
+
+ if (pMovedNode)
+ { // probably better do this after creating all frames
+ lcl_MakeAutoFrames(*rDoc.GetSpzFrameFormats(), pMovedNode->GetIndex());
+ }
+
+ // tdf#134021 only after MakeFrames(), because it may be the only node
+ // that has layout frames
+ if (pInsNd && m_bTableDelLastNd)
+ {
+ assert(&aIdx.GetNode() == pInsNd);
+ SwPaM tmp(aIdx, aIdx);
+ rDoc.getIDocumentContentOperations().DelFullPara(tmp);
+ }
+
+ AddUndoRedoPaM(rContext, true);
+}
+
+void SwUndoDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+ SwDoc& rDoc = rPam.GetDoc();
+
+ if( m_pRedlSaveData )
+ {
+ const bool bSuccess = FillSaveData(rPam, *m_pRedlSaveData);
+ OSL_ENSURE(bSuccess,
+ "SwUndoDelete::Redo: used to have redline data, but now none?");
+ if (!bSuccess)
+ {
+ m_pRedlSaveData.reset();
+ }
+ }
+
+ if( !m_bDelFullPara )
+ {
+ // tdf#128739 correct cursors but do not delete bookmarks yet
+ ::PaMCorrAbs(rPam, *rPam.End());
+ SetPaM(rPam);
+
+ if( !m_bJoinNext ) // then restore selection from bottom to top
+ rPam.Exchange();
+ }
+
+ if( m_pHistory ) // are the attributes saved?
+ {
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get() );
+
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ DelBookmarks(rPam.GetMark()->nNode, rPam.GetPoint()->nNode);
+ }
+ else
+ {
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask
+ | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0)));
+ }
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+
+ m_pHistory->Move( m_nSetPos, &aHstr );
+ }
+ else
+ {
+ if( m_bDelFullPara )
+ {
+ OSL_ENSURE( rPam.HasMark(), "PaM without Mark" );
+ DelContentIndex( *rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType(DelContentType::AllMask | DelContentType::CheckNoCntnt) );
+
+ DelBookmarks( rPam.GetMark()->nNode, rPam.GetPoint()->nNode );
+ }
+ else
+ {
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask
+ | ((m_DeleteFlags & SwDeleteFlags::ArtificialSelection) ? DelContentType::Replace : DelContentType(0)));
+ }
+ m_nSetPos = m_pHistory ? m_pHistory->Count() : 0;
+ }
+
+ if( !m_aSttStr && !m_aEndStr )
+ {
+ if (m_bDelFullPara && m_pRedlSaveData)
+ {
+ DelFullParaMoveFrames(rDoc, *this, *m_pRedlSaveData);
+ }
+
+ SwNodeIndex aSttIdx = ( m_bDelFullPara || m_bJoinNext )
+ ? rPam.GetMark()->nNode
+ : rPam.GetPoint()->nNode;
+ SwTableNode* pTableNd = aSttIdx.GetNode().GetTableNode();
+ if( pTableNd )
+ {
+ if( m_bTableDelLastNd )
+ {
+ // than add again a Node at the end
+ const SwNodeIndex aTmpIdx( *pTableNd->EndOfSectionNode(), 1 );
+ rDoc.GetNodes().MakeTextNode( aTmpIdx,
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD ) );
+ }
+
+ SwContentNode* pNextNd = rDoc.GetNodes()[
+ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+ }
+ pTableNd->DelFrames();
+ }
+ else if (*rPam.GetMark() == *rPam.GetPoint())
+ { // paragraph with only footnote or as-char fly, delete that
+ // => DelContentIndex has already deleted it! nothing to do here
+ assert(m_nEndNode == m_nSttNode);
+ return;
+ }
+
+ // avoid asserts from ~SwIndexReg for deleted nodes
+ SwPaM aTmp(*rPam.End());
+ if (!aTmp.Move(fnMoveForward, GoInNode))
+ {
+ *aTmp.GetPoint() = *rPam.Start();
+ aTmp.Move(fnMoveBackward, GoInNode);
+ }
+ assert(aTmp.GetPoint()->nNode != rPam.GetPoint()->nNode
+ && aTmp.GetPoint()->nNode != rPam.GetMark()->nNode);
+ ::PaMCorrAbs(rPam, *aTmp.GetPoint());
+
+ rPam.DeleteMark();
+
+ rDoc.GetNodes().Delete( aSttIdx, m_nEndNode - m_nSttNode );
+ }
+ else if( m_bDelFullPara )
+ {
+ assert(!"dead code");
+ // The Pam was incremented by one at Point (== end) to provide space
+ // for UNDO. This now needs to be reverted!
+ --rPam.End()->nNode;
+ if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
+ *rPam.GetMark() = *rPam.GetPoint();
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ }
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(rPam, m_DeleteFlags);
+}
+
+void SwUndoDelete::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // this action does not seem idempotent,
+ // so make sure it is only executed once on repeat
+ if (rContext.m_bDeleteRepeated)
+ return;
+
+ SwPaM & rPam = rContext.GetRepeatPaM();
+ SwDoc& rDoc = rPam.GetDoc();
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ if( !rPam.HasMark() )
+ {
+ rPam.SetMark();
+ rPam.Move( fnMoveForward, GoInContent );
+ }
+ if( m_bDelFullPara )
+ rDoc.getIDocumentContentOperations().DelFullPara( rPam );
+ else
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( rPam );
+ rContext.m_bDeleteRepeated = true;
+}
+
+void SwUndoDelete::SetTableName(const OUString & rName)
+{
+ m_sTableName = rName;
+}
+
+void SwUndoDelete::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDelete"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ SwUndo::dumpAsXml(pWriter);
+ SwUndoSaveContent::dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undobj.cxx b/sw/source/core/undo/undobj.cxx
new file mode 100644
index 000000000..c23aeba3b
--- /dev/null
+++ b/sw/source/core/undo/undobj.cxx
@@ -0,0 +1,1718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <IShellCursorSupplier.hxx>
+#include <txtftn.hxx>
+#include <fmtanchr.hxx>
+#include <ftnidx.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <UndoManager.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <docary.hxx>
+#include <swcrsr.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <ndnotxt.hxx>
+#include <IMark.hxx>
+#include <mvsave.hxx>
+#include <redline.hxx>
+#include <crossrefbookmark.hxx>
+#include <strings.hrc>
+#include <docsh.hxx>
+#include <view.hxx>
+#include <frameformats.hxx>
+#include <o3tl/deleter.hxx>
+#include <sal/log.hxx>
+
+// This class saves the Pam as integers and can recompose those into a PaM
+SwUndRng::SwUndRng()
+ : m_nSttNode( 0 ), m_nEndNode( 0 ), m_nSttContent( 0 ), m_nEndContent( 0 )
+{
+}
+
+SwUndRng::SwUndRng( const SwPaM& rPam )
+{
+ SetValues( rPam );
+}
+
+void SwUndRng::SetValues( const SwPaM& rPam )
+{
+ const SwPosition *pStt = rPam.Start();
+ if( rPam.HasMark() )
+ {
+ const SwPosition *pEnd = rPam.End();
+ m_nEndNode = pEnd->nNode.GetIndex();
+ m_nEndContent = pEnd->nContent.GetIndex();
+ }
+ else
+ {
+ // no selection !!
+ m_nEndNode = SwNodeOffset(0);
+ m_nEndContent = COMPLETE_STRING;
+ }
+
+ m_nSttNode = pStt->nNode.GetIndex();
+ m_nSttContent = pStt->nContent.GetIndex();
+}
+
+void SwUndRng::SetPaM( SwPaM & rPam, bool bCorrToContent ) const
+{
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nSttNode;
+ SwNode& rNd = rPam.GetNode();
+ if( rNd.IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( rNd.GetContentNode(), m_nSttContent );
+ else if( bCorrToContent )
+ rPam.Move( fnMoveForward, GoInContent );
+ else
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ if( !m_nEndNode && COMPLETE_STRING == m_nEndContent ) // no selection
+ return ;
+
+ rPam.SetMark();
+ if( m_nSttNode == m_nEndNode && m_nSttContent == m_nEndContent )
+ return; // nothing left to do
+
+ rPam.GetPoint()->nNode = m_nEndNode;
+ if( rPam.GetNode().IsContentNode() )
+ rPam.GetPoint()->nContent.Assign( rPam.GetNode().GetContentNode(), m_nEndContent );
+ else if( bCorrToContent )
+ rPam.Move( fnMoveBackward, GoInContent );
+ else
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+}
+
+SwPaM & SwUndRng::AddUndoRedoPaM(
+ ::sw::UndoRedoContext & rContext, bool const bCorrToContent) const
+{
+ SwCursor & rPaM( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ SetPaM( rPaM, bCorrToContent );
+ return rPaM;
+}
+
+void SwUndo::RemoveIdxFromSection( SwDoc& rDoc, SwNodeOffset nSttIdx,
+ const SwNodeOffset* pEndIdx )
+{
+ SwNodeIndex aIdx( rDoc.GetNodes(), nSttIdx );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(), pEndIdx ? *pEndIdx
+ : aIdx.GetNode().EndOfSectionIndex() );
+ SwPosition aPos( rDoc.GetNodes().GetEndOfPostIts() );
+ SwDoc::CorrAbs( aIdx, aEndIdx, aPos, true );
+}
+
+void SwUndo::RemoveIdxFromRange( SwPaM& rPam, bool bMoveNext )
+{
+ const SwPosition* pEnd = rPam.End();
+ if( bMoveNext )
+ {
+ if( pEnd != rPam.GetPoint() )
+ rPam.Exchange();
+
+ SwNodeIndex aStt( rPam.GetMark()->nNode );
+ SwNodeIndex aEnd( rPam.GetPoint()->nNode );
+
+ if( !rPam.Move( fnMoveForward ) )
+ {
+ rPam.Exchange();
+ if( !rPam.Move( fnMoveBackward ) )
+ {
+ rPam.GetPoint()->nNode = rPam.GetDoc().GetNodes().GetEndOfPostIts();
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ }
+ }
+
+ SwDoc::CorrAbs( aStt, aEnd, *rPam.GetPoint(), true );
+ }
+ else
+ SwDoc::CorrAbs( rPam, *pEnd, true );
+}
+
+void SwUndo::RemoveIdxRel( SwNodeOffset nIdx, const SwPosition& rPos )
+{
+ // Move only the Cursor. Bookmarks/TOXMarks/etc. are done by the corresponding
+ // JoinNext/JoinPrev
+ SwNodeIndex aIdx( rPos.nNode.GetNode().GetNodes(), nIdx );
+ ::PaMCorrRel( aIdx, rPos );
+}
+
+SwUndo::SwUndo(SwUndoId const nId, const SwDoc* pDoc)
+ : m_nId(nId), m_nOrigRedlineFlags(RedlineFlags::NONE)
+ , m_nViewShellId(CreateViewShellId(pDoc))
+ , m_isRepeatIgnored(false)
+ , m_bCacheComment(true)
+{
+}
+
+ViewShellId SwUndo::CreateViewShellId(const SwDoc* pDoc)
+{
+ ViewShellId nRet(-1);
+
+ if (const SwDocShell* pDocShell = pDoc->GetDocShell())
+ {
+ if (const SwView* pView = pDocShell->GetView())
+ nRet = pView->GetViewShellId();
+ }
+
+ return nRet;
+}
+
+bool SwUndo::IsDelBox() const
+{
+ return GetId() == SwUndoId::COL_DELETE || GetId() == SwUndoId::ROW_DELETE ||
+ GetId() == SwUndoId::TABLE_DELBOX;
+}
+
+SwUndo::~SwUndo()
+{
+}
+
+namespace {
+
+class UndoRedoRedlineGuard
+{
+public:
+ UndoRedoRedlineGuard(::sw::UndoRedoContext const & rContext, SwUndo const & rUndo)
+ : m_rRedlineAccess(rContext.GetDoc().getIDocumentRedlineAccess())
+ , m_eMode(m_rRedlineAccess.GetRedlineFlags())
+ {
+ RedlineFlags const eTmpMode = rUndo.GetRedlineFlags();
+ if ((RedlineFlags::ShowMask & eTmpMode) != (RedlineFlags::ShowMask & m_eMode))
+ {
+ m_rRedlineAccess.SetRedlineFlags( eTmpMode );
+ }
+ m_rRedlineAccess.SetRedlineFlags_intern( eTmpMode | RedlineFlags::Ignore );
+ }
+ ~UndoRedoRedlineGuard()
+ {
+ m_rRedlineAccess.SetRedlineFlags(m_eMode);
+ }
+private:
+ IDocumentRedlineAccess & m_rRedlineAccess;
+ RedlineFlags const m_eMode;
+};
+
+}
+
+void SwUndo::Undo()
+{
+ assert(false); // SwUndo::Undo(): ERROR: must call UndoWithContext instead
+}
+
+void SwUndo::Redo()
+{
+ assert(false); // SwUndo::Redo(): ERROR: must call RedoWithContext instead
+}
+
+void SwUndo::UndoWithContext(SfxUndoContext & rContext)
+{
+ ::sw::UndoRedoContext *const pContext(
+ dynamic_cast< ::sw::UndoRedoContext * >(& rContext));
+ assert(pContext);
+ const UndoRedoRedlineGuard aUndoRedoRedlineGuard(*pContext, *this);
+ UndoImpl(*pContext);
+}
+
+void SwUndo::RedoWithContext(SfxUndoContext & rContext)
+{
+ ::sw::UndoRedoContext *const pContext(
+ dynamic_cast< ::sw::UndoRedoContext * >(& rContext));
+ assert(pContext);
+ const UndoRedoRedlineGuard aUndoRedoRedlineGuard(*pContext, *this);
+ RedoImpl(*pContext);
+}
+
+void SwUndo::Repeat(SfxRepeatTarget & rContext)
+{
+ if (m_isRepeatIgnored)
+ {
+ return; // ignore Repeat for multi-selections
+ }
+ ::sw::RepeatContext *const pRepeatContext(
+ dynamic_cast< ::sw::RepeatContext * >(& rContext));
+ assert(pRepeatContext);
+ RepeatImpl(*pRepeatContext);
+}
+
+bool SwUndo::CanRepeat(SfxRepeatTarget & rContext) const
+{
+ assert(dynamic_cast< ::sw::RepeatContext * >(& rContext));
+ (void)rContext;
+ // a MultiSelection action that doesn't do anything must still return true
+ return (SwUndoId::REPEAT_START <= GetId()) && (GetId() < SwUndoId::REPEAT_END);
+}
+
+void SwUndo::RepeatImpl( ::sw::RepeatContext & )
+{
+}
+
+OUString GetUndoComment(SwUndoId eId)
+{
+ TranslateId pId;
+ switch (eId)
+ {
+ case SwUndoId::EMPTY:
+ pId = STR_CANT_UNDO;
+ break;
+ case SwUndoId::START:
+ case SwUndoId::END:
+ break;
+ case SwUndoId::DELETE:
+ pId = STR_DELETE_UNDO;
+ break;
+ case SwUndoId::INSERT:
+ pId = STR_INSERT_UNDO;
+ break;
+ case SwUndoId::OVERWRITE:
+ pId = STR_OVR_UNDO;
+ break;
+ case SwUndoId::SPLITNODE:
+ pId = STR_SPLITNODE_UNDO;
+ break;
+ case SwUndoId::INSATTR:
+ pId = STR_INSATTR_UNDO;
+ break;
+ case SwUndoId::SETFMTCOLL:
+ pId = STR_SETFMTCOLL_UNDO;
+ break;
+ case SwUndoId::RESETATTR:
+ pId = STR_RESET_ATTR_UNDO;
+ break;
+ case SwUndoId::INSFMTATTR:
+ pId = STR_INSFMT_ATTR_UNDO;
+ break;
+ case SwUndoId::INSDOKUMENT:
+ pId = STR_INSERT_DOC_UNDO;
+ break;
+ case SwUndoId::COPY:
+ pId = STR_COPY_UNDO;
+ break;
+ case SwUndoId::INSTABLE:
+ pId = STR_INSTABLE_UNDO;
+ break;
+ case SwUndoId::TABLETOTEXT:
+ pId = STR_TABLETOTEXT_UNDO;
+ break;
+ case SwUndoId::TEXTTOTABLE:
+ pId = STR_TEXTTOTABLE_UNDO;
+ break;
+ case SwUndoId::SORT_TXT:
+ pId = STR_SORT_TXT;
+ break;
+ case SwUndoId::INSLAYFMT:
+ pId = STR_INSERTFLY;
+ break;
+ case SwUndoId::TABLEHEADLINE:
+ pId = STR_TABLEHEADLINE;
+ break;
+ case SwUndoId::INSSECTION:
+ pId = STR_INSERTSECTION;
+ break;
+ case SwUndoId::OUTLINE_LR:
+ pId = STR_OUTLINE_LR;
+ break;
+ case SwUndoId::OUTLINE_UD:
+ pId = STR_OUTLINE_UD;
+ break;
+ case SwUndoId::OUTLINE_EDIT:
+ pId = STR_OUTLINE_EDIT;
+ break;
+ case SwUndoId::INSNUM:
+ pId = STR_INSNUM;
+ break;
+ case SwUndoId::NUMUP:
+ pId = STR_NUMUP;
+ break;
+ case SwUndoId::MOVENUM:
+ pId = STR_MOVENUM;
+ break;
+ case SwUndoId::INSDRAWFMT:
+ pId = STR_INSERTDRAW;
+ break;
+ case SwUndoId::NUMORNONUM:
+ pId = STR_NUMORNONUM;
+ break;
+ case SwUndoId::INC_LEFTMARGIN:
+ pId = STR_INC_LEFTMARGIN;
+ break;
+ case SwUndoId::DEC_LEFTMARGIN:
+ pId = STR_DEC_LEFTMARGIN;
+ break;
+ case SwUndoId::INSERTLABEL:
+ pId = STR_INSERTLABEL;
+ break;
+ case SwUndoId::SETNUMRULESTART:
+ pId = STR_SETNUMRULESTART;
+ break;
+ case SwUndoId::CHGFTN:
+ pId = STR_CHANGEFTN;
+ break;
+ case SwUndoId::REDLINE:
+ SAL_INFO("sw.core", "Should NEVER be used/translated");
+ return "$1";
+ case SwUndoId::ACCEPT_REDLINE:
+ pId = STR_ACCEPT_REDLINE;
+ break;
+ case SwUndoId::REJECT_REDLINE:
+ pId = STR_REJECT_REDLINE;
+ break;
+ case SwUndoId::SPLIT_TABLE:
+ pId = STR_SPLIT_TABLE;
+ break;
+ case SwUndoId::DONTEXPAND:
+ pId = STR_DONTEXPAND;
+ break;
+ case SwUndoId::AUTOCORRECT:
+ pId = STR_AUTOCORRECT;
+ break;
+ case SwUndoId::MERGE_TABLE:
+ pId = STR_MERGE_TABLE;
+ break;
+ case SwUndoId::TRANSLITERATE:
+ pId = STR_TRANSLITERATE;
+ break;
+ case SwUndoId::PASTE_CLIPBOARD:
+ pId = STR_PASTE_CLIPBOARD_UNDO;
+ break;
+ case SwUndoId::TYPING:
+ pId = STR_TYPING_UNDO;
+ break;
+ case SwUndoId::MOVE:
+ pId = STR_MOVE_UNDO;
+ break;
+ case SwUndoId::INSGLOSSARY:
+ pId = STR_INSERT_GLOSSARY;
+ break;
+ case SwUndoId::DELBOOKMARK:
+ pId = STR_DELBOOKMARK;
+ break;
+ case SwUndoId::INSBOOKMARK:
+ pId = STR_INSBOOKMARK;
+ break;
+ case SwUndoId::SORT_TBL:
+ pId = STR_SORT_TBL;
+ break;
+ case SwUndoId::DELLAYFMT:
+ pId = STR_DELETEFLY;
+ break;
+ case SwUndoId::AUTOFORMAT:
+ pId = STR_AUTOFORMAT;
+ break;
+ case SwUndoId::REPLACE:
+ pId = STR_REPLACE;
+ break;
+ case SwUndoId::DELSECTION:
+ pId = STR_DELETESECTION;
+ break;
+ case SwUndoId::CHGSECTION:
+ pId = STR_CHANGESECTION;
+ break;
+ case SwUndoId::SETDEFTATTR:
+ pId = STR_CHANGEDEFATTR;
+ break;
+ case SwUndoId::DELNUM:
+ pId = STR_DELNUM;
+ break;
+ case SwUndoId::DRAWUNDO:
+ pId = STR_DRAWUNDO;
+ break;
+ case SwUndoId::DRAWGROUP:
+ pId = STR_DRAWGROUP;
+ break;
+ case SwUndoId::DRAWUNGROUP:
+ pId = STR_DRAWUNGROUP;
+ break;
+ case SwUndoId::DRAWDELETE:
+ pId = STR_DRAWDELETE;
+ break;
+ case SwUndoId::REREAD:
+ pId = STR_REREAD;
+ break;
+ case SwUndoId::DELGRF:
+ pId = STR_DELGRF;
+ break;
+ case SwUndoId::TABLE_ATTR:
+ pId = STR_TABLE_ATTR;
+ break;
+ case SwUndoId::TABLE_AUTOFMT:
+ pId = STR_UNDO_TABLE_AUTOFMT;
+ break;
+ case SwUndoId::TABLE_INSCOL:
+ pId = STR_UNDO_TABLE_INSCOL;
+ break;
+ case SwUndoId::TABLE_INSROW:
+ pId = STR_UNDO_TABLE_INSROW;
+ break;
+ case SwUndoId::TABLE_DELBOX:
+ pId = STR_UNDO_TABLE_DELBOX;
+ break;
+ case SwUndoId::TABLE_SPLIT:
+ pId = STR_UNDO_TABLE_SPLIT;
+ break;
+ case SwUndoId::TABLE_MERGE:
+ pId = STR_UNDO_TABLE_MERGE;
+ break;
+ case SwUndoId::TBLNUMFMT:
+ pId = STR_TABLE_NUMFORMAT;
+ break;
+ case SwUndoId::INSTOX:
+ pId = STR_INSERT_TOX;
+ break;
+ case SwUndoId::CLEARTOXRANGE:
+ pId = STR_CLEAR_TOX_RANGE;
+ break;
+ case SwUndoId::TBLCPYTBL:
+ pId = STR_TABLE_TBLCPYTBL;
+ break;
+ case SwUndoId::CPYTBL:
+ pId = STR_TABLE_CPYTBL;
+ break;
+ case SwUndoId::INS_FROM_SHADOWCRSR:
+ pId = STR_INS_FROM_SHADOWCRSR;
+ break;
+ case SwUndoId::CHAINE:
+ pId = STR_UNDO_CHAIN;
+ break;
+ case SwUndoId::UNCHAIN:
+ pId = STR_UNDO_UNCHAIN;
+ break;
+ case SwUndoId::FTNINFO:
+ pId = STR_UNDO_FTNINFO;
+ break;
+ case SwUndoId::COMPAREDOC:
+ pId = STR_UNDO_COMPAREDOC;
+ break;
+ case SwUndoId::SETFLYFRMFMT:
+ pId = STR_UNDO_SETFLYFRMFMT;
+ break;
+ case SwUndoId::SETRUBYATTR:
+ pId = STR_UNDO_SETRUBYATTR;
+ break;
+ case SwUndoId::TOXCHANGE:
+ pId = STR_TOXCHANGE;
+ break;
+ case SwUndoId::CREATE_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC_CREATE;
+ break;
+ case SwUndoId::CHANGE_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC;
+ break;
+ case SwUndoId::DELETE_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC_DELETE;
+ break;
+ case SwUndoId::HEADER_FOOTER:
+ pId = STR_UNDO_HEADER_FOOTER;
+ break;
+ case SwUndoId::FIELD:
+ pId = STR_UNDO_FIELD;
+ break;
+ case SwUndoId::TXTFMTCOL_CREATE:
+ pId = STR_UNDO_TXTFMTCOL_CREATE;
+ break;
+ case SwUndoId::TXTFMTCOL_DELETE:
+ pId = STR_UNDO_TXTFMTCOL_DELETE;
+ break;
+ case SwUndoId::TXTFMTCOL_RENAME:
+ pId = STR_UNDO_TXTFMTCOL_RENAME;
+ break;
+ case SwUndoId::CHARFMT_CREATE:
+ pId = STR_UNDO_CHARFMT_CREATE;
+ break;
+ case SwUndoId::CHARFMT_DELETE:
+ pId = STR_UNDO_CHARFMT_DELETE;
+ break;
+ case SwUndoId::CHARFMT_RENAME:
+ pId = STR_UNDO_CHARFMT_RENAME;
+ break;
+ case SwUndoId::FRMFMT_CREATE:
+ pId = STR_UNDO_FRMFMT_CREATE;
+ break;
+ case SwUndoId::FRMFMT_DELETE:
+ pId = STR_UNDO_FRMFMT_DELETE;
+ break;
+ case SwUndoId::FRMFMT_RENAME:
+ pId = STR_UNDO_FRMFMT_RENAME;
+ break;
+ case SwUndoId::NUMRULE_CREATE:
+ pId = STR_UNDO_NUMRULE_CREATE;
+ break;
+ case SwUndoId::NUMRULE_DELETE:
+ pId = STR_UNDO_NUMRULE_DELETE;
+ break;
+ case SwUndoId::NUMRULE_RENAME:
+ pId = STR_UNDO_NUMRULE_RENAME;
+ break;
+ case SwUndoId::BOOKMARK_RENAME:
+ pId = STR_UNDO_BOOKMARK_RENAME;
+ break;
+ case SwUndoId::INDEX_ENTRY_INSERT:
+ pId = STR_UNDO_INDEX_ENTRY_INSERT;
+ break;
+ case SwUndoId::INDEX_ENTRY_DELETE:
+ pId = STR_UNDO_INDEX_ENTRY_DELETE;
+ break;
+ case SwUndoId::COL_DELETE:
+ pId = STR_UNDO_COL_DELETE;
+ break;
+ case SwUndoId::ROW_DELETE:
+ pId = STR_UNDO_ROW_DELETE;
+ break;
+ case SwUndoId::RENAME_PAGEDESC:
+ pId = STR_UNDO_PAGEDESC_RENAME;
+ break;
+ case SwUndoId::NUMDOWN:
+ pId = STR_NUMDOWN;
+ break;
+ case SwUndoId::FLYFRMFMT_TITLE:
+ pId = STR_UNDO_FLYFRMFMT_TITLE;
+ break;
+ case SwUndoId::FLYFRMFMT_DESCRIPTION:
+ pId = STR_UNDO_FLYFRMFMT_DESCRIPTION;
+ break;
+ case SwUndoId::TBLSTYLE_CREATE:
+ pId = STR_UNDO_TBLSTYLE_CREATE;
+ break;
+ case SwUndoId::TBLSTYLE_DELETE:
+ pId = STR_UNDO_TBLSTYLE_DELETE;
+ break;
+ case SwUndoId::TBLSTYLE_UPDATE:
+ pId = STR_UNDO_TBLSTYLE_UPDATE;
+ break;
+ case SwUndoId::UI_REPLACE:
+ pId = STR_REPLACE_UNDO;
+ break;
+ case SwUndoId::UI_INSERT_PAGE_BREAK:
+ pId = STR_INSERT_PAGE_BREAK_UNDO;
+ break;
+ case SwUndoId::UI_INSERT_COLUMN_BREAK:
+ pId = STR_INSERT_COLUMN_BREAK_UNDO;
+ break;
+ case SwUndoId::UI_INSERT_ENVELOPE:
+ pId = STR_INSERT_ENV_UNDO;
+ break;
+ case SwUndoId::UI_DRAG_AND_COPY:
+ pId = STR_DRAG_AND_COPY;
+ break;
+ case SwUndoId::UI_DRAG_AND_MOVE:
+ pId = STR_DRAG_AND_MOVE;
+ break;
+ case SwUndoId::UI_INSERT_CHART:
+ pId = STR_INSERT_CHART;
+ break;
+ case SwUndoId::UI_INSERT_FOOTNOTE:
+ pId = STR_INSERT_FOOTNOTE;
+ break;
+ case SwUndoId::UI_INSERT_URLBTN:
+ pId = STR_INSERT_URLBTN;
+ break;
+ case SwUndoId::UI_INSERT_URLTXT:
+ pId = STR_INSERT_URLTXT;
+ break;
+ case SwUndoId::UI_DELETE_INVISIBLECNTNT:
+ pId = STR_DELETE_INVISIBLECNTNT;
+ break;
+ case SwUndoId::UI_REPLACE_STYLE:
+ pId = STR_REPLACE_STYLE;
+ break;
+ case SwUndoId::UI_DELETE_PAGE_BREAK:
+ pId = STR_DELETE_PAGE_BREAK;
+ break;
+ case SwUndoId::UI_TEXT_CORRECTION:
+ pId = STR_TEXT_CORRECTION;
+ break;
+ case SwUndoId::UI_TABLE_DELETE:
+ pId = STR_UNDO_TABLE_DELETE;
+ break;
+ case SwUndoId::CONFLICT:
+ break;
+ case SwUndoId::PARA_SIGN_ADD:
+ pId = STR_PARAGRAPH_SIGN_UNDO;
+ break;
+ case SwUndoId::INSERT_FORM_FIELD:
+ pId = STR_UNDO_INSERT_FORM_FIELD;
+ break;
+ }
+
+ assert(pId);
+ return SwResId(pId);
+}
+
+OUString SwUndo::GetComment() const
+{
+ OUString aResult;
+
+ if (m_bCacheComment)
+ {
+ if (! maComment)
+ {
+ maComment = GetUndoComment(GetId());
+
+ SwRewriter aRewriter = GetRewriter();
+
+ maComment = aRewriter.Apply(*maComment);
+ }
+
+ aResult = *maComment;
+ }
+ else
+ {
+ aResult = GetUndoComment(GetId());
+
+ SwRewriter aRewriter = GetRewriter();
+
+ aResult = aRewriter.Apply(aResult);
+ }
+
+ return aResult;
+}
+
+ViewShellId SwUndo::GetViewShellId() const
+{
+ return m_nViewShellId;
+}
+
+SwRewriter SwUndo::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ return aResult;
+}
+
+SwUndoSaveContent::SwUndoSaveContent()
+{}
+
+SwUndoSaveContent::~SwUndoSaveContent() COVERITY_NOEXCEPT_FALSE
+{
+}
+
+void SwUndoSaveContent::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoSaveContent"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ if (m_pHistory)
+ {
+ m_pHistory->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// This is needed when deleting content. For REDO all contents will be moved
+// into the UndoNodesArray. These methods always create a new node to insert
+// content. As a result, the attributes will not be expanded.
+// - MoveTo moves from NodesArray into UndoNodesArray
+// - MoveFrom moves from UndoNodesArray into NodesArray
+
+// If pEndNdIdx is given, Undo/Redo calls -Ins/DelFly. In that case the whole
+// section should be moved.
+void SwUndoSaveContent::MoveToUndoNds( SwPaM& rPaM, SwNodeIndex* pNodeIdx,
+ SwNodeOffset* pEndNdIdx )
+{
+ SwDoc& rDoc = rPaM.GetDoc();
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwNoTextNode* pCpyNd = rPaM.GetNode().GetNoTextNode();
+
+ // here comes the actual delete (move)
+ SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
+ SwPosition aPos( pEndNdIdx ? rNds.GetEndOfPostIts()
+ : rNds.GetEndOfExtras() );
+
+ const SwPosition* pStt = rPaM.Start(), *pEnd = rPaM.End();
+
+ SwNodeOffset nTmpMvNode = aPos.nNode.GetIndex();
+
+ if( pCpyNd || pEndNdIdx )
+ {
+ SwNodeRange aRg( pStt->nNode, SwNodeOffset(0), pEnd->nNode, SwNodeOffset(1) );
+ rDoc.GetNodes().MoveNodes( aRg, rNds, aPos.nNode, true );
+ aPos.nContent = 0;
+ --aPos.nNode;
+ }
+ else
+ {
+ rDoc.GetNodes().MoveRange( rPaM, aPos, rNds );
+ }
+ if( pEndNdIdx )
+ *pEndNdIdx = aPos.nNode.GetIndex();
+
+ // old position
+ aPos.nNode = nTmpMvNode;
+ if( pNodeIdx )
+ *pNodeIdx = aPos.nNode;
+}
+
+void SwUndoSaveContent::MoveFromUndoNds( SwDoc& rDoc, SwNodeOffset nNodeIdx,
+ SwPosition& rInsPos,
+ const SwNodeOffset* pEndNdIdx, bool const bForceCreateFrames)
+{
+ // here comes the recovery
+ SwNodes & rNds = rDoc.GetUndoManager().GetUndoNodes();
+ if( nNodeIdx == rNds.GetEndOfPostIts().GetIndex() )
+ return; // nothing saved
+
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ SwPaM aPaM( rInsPos );
+ if( pEndNdIdx ) // than get the section from it
+ aPaM.GetPoint()->nNode.Assign( rNds, *pEndNdIdx );
+ else
+ {
+ aPaM.GetPoint()->nNode = rNds.GetEndOfExtras();
+ GoInContent( aPaM, fnMoveBackward );
+ }
+
+ SwTextNode* pTextNd = aPaM.GetNode().GetTextNode();
+ if (!pEndNdIdx && pTextNd)
+ {
+ aPaM.SetMark();
+ aPaM.GetPoint()->nNode = nNodeIdx;
+ aPaM.GetPoint()->nContent.Assign(aPaM.GetContentNode(), 0);
+
+ SaveRedlEndPosForRestore aRedlRest( rInsPos.nNode, rInsPos.nContent.GetIndex() );
+
+ rNds.MoveRange( aPaM, rInsPos, rDoc.GetNodes() );
+
+ // delete the last Node as well
+ if( !aPaM.GetPoint()->nContent.GetIndex() ||
+ ( aPaM.GetPoint()->nNode++ && // still empty Nodes at the end?
+ &rNds.GetEndOfExtras() != &aPaM.GetPoint()->nNode.GetNode() ))
+ {
+ aPaM.GetPoint()->nContent.Assign( nullptr, 0 );
+ aPaM.SetMark();
+ rNds.Delete( aPaM.GetPoint()->nNode,
+ rNds.GetEndOfExtras().GetIndex() -
+ aPaM.GetPoint()->nNode.GetIndex() );
+ }
+
+ aRedlRest.Restore();
+ }
+ else
+ {
+ SwNodeRange aRg( rNds, nNodeIdx, rNds, (pEndNdIdx
+ ? ((*pEndNdIdx) + 1)
+ : rNds.GetEndOfExtras().GetIndex() ) );
+ rNds.MoveNodes(aRg, rDoc.GetNodes(), rInsPos.nNode, nullptr == pEndNdIdx || bForceCreateFrames);
+
+ }
+}
+
+// These two methods move the Point of Pam backwards/forwards. With that, one
+// can span an area for a Undo/Redo. (The Point is then positioned in front of
+// the area to manipulate!)
+// The flag indicates if there is still content in front of Point.
+bool SwUndoSaveContent::MovePtBackward( SwPaM& rPam )
+{
+ rPam.SetMark();
+ if( rPam.Move( fnMoveBackward ))
+ return true;
+
+ // If there is no content onwards, set Point simply to the previous position
+ // (Node and Content, so that Content will be detached!)
+ --rPam.GetPoint()->nNode;
+ rPam.GetPoint()->nContent.Assign( nullptr, 0 );
+ return false;
+}
+
+void SwUndoSaveContent::MovePtForward( SwPaM& rPam, bool bMvBkwrd )
+{
+ // Was there content before this position?
+ if( bMvBkwrd )
+ rPam.Move( fnMoveForward );
+ else
+ {
+ ++rPam.GetPoint()->nNode;
+ SwContentNode* pCNd = rPam.GetContentNode();
+ if( pCNd )
+ pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
+ else
+ rPam.Move( fnMoveForward );
+ }
+}
+
+// Delete all objects that have ContentIndices to the given area.
+// Currently (1994) these exist:
+// - Footnotes
+// - Flys
+// - Bookmarks
+
+// #i81002# - extending method
+// delete certain (not all) cross-reference bookmarks at text node of <rMark>
+// and at text node of <rPoint>, if these text nodes aren't the same.
+void SwUndoSaveContent::DelContentIndex( const SwPosition& rMark,
+ const SwPosition& rPoint,
+ DelContentType nDelContentType )
+{
+ const SwPosition *pStt = rMark < rPoint ? &rMark : &rPoint,
+ *pEnd = &rMark == pStt ? &rPoint : &rMark;
+
+ SwDoc& rDoc = rMark.nNode.GetNode().GetDoc();
+
+ // if it's not in the doc array, probably missing some invalidation somewhere
+ assert(&rPoint.nNode.GetNodes() == &rDoc.GetNodes());
+ assert(&rMark.nNode.GetNodes() == &rDoc.GetNodes());
+
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+
+ // 1. Footnotes
+ if( DelContentType::Ftn & nDelContentType )
+ {
+ SwFootnoteIdxs& rFootnoteArr = rDoc.GetFootnoteIdxs();
+ if( !rFootnoteArr.empty() )
+ {
+ const SwNode* pFootnoteNd;
+ size_t nPos = 0;
+ rFootnoteArr.SeekEntry( pStt->nNode, &nPos );
+ SwTextFootnote* pSrch;
+
+ // for now delete all that come afterwards
+ while( nPos < rFootnoteArr.size() && ( pFootnoteNd =
+ &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex()
+ <= pEnd->nNode.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( (DelContentType::CheckNoCntnt & nDelContentType )
+ ? (&pEnd->nNode.GetNode() == pFootnoteNd )
+ : (( &pStt->nNode.GetNode() == pFootnoteNd &&
+ pStt->nContent.GetIndex() > nFootnoteSttIdx) ||
+ ( &pEnd->nNode.GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEnd->nContent.GetIndex() )) )
+ {
+ ++nPos; // continue searching
+ continue;
+ }
+
+// FIXME: duplicated code here and below -> refactor?
+ // Unfortunately an index needs to be created. Otherwise there
+ // will be problems with TextNode because the index will be
+ // deleted in the DTOR of SwFootnote!
+ SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd));
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextAttr* const pFootnoteHint =
+ pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx );
+ assert(pFootnoteHint);
+ SwIndex aIdx( pTextNd, nFootnoteSttIdx );
+ m_pHistory->Add( pFootnoteHint, pTextNd->GetIndex(), false );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+
+ while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )->
+ GetTextNode())->GetIndex() >= pStt->nNode.GetIndex() )
+ {
+ const sal_Int32 nFootnoteSttIdx = pSrch->GetStart();
+ if( !(DelContentType::CheckNoCntnt & nDelContentType) && (
+ ( &pStt->nNode.GetNode() == pFootnoteNd &&
+ pStt->nContent.GetIndex() > nFootnoteSttIdx ) ||
+ ( &pEnd->nNode.GetNode() == pFootnoteNd &&
+ nFootnoteSttIdx >= pEnd->nContent.GetIndex() )))
+ continue; // continue searching
+
+ // Unfortunately an index needs to be created. Otherwise there
+ // will be problems with TextNode because the index will be
+ // deleted in the DTOR of SwFootnote!
+ SwTextNode* pTextNd = const_cast<SwTextNode*>(static_cast<const SwTextNode*>(pFootnoteNd));
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextAttr* const pFootnoteHint =
+ pTextNd->GetTextAttrForCharAt( nFootnoteSttIdx );
+ assert(pFootnoteHint);
+ SwIndex aIdx( pTextNd, nFootnoteSttIdx );
+ m_pHistory->Add( pFootnoteHint, pTextNd->GetIndex(), false );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ }
+
+ // 2. Flys
+ if( DelContentType::Fly & nDelContentType )
+ {
+ sal_uInt16 nChainInsPos = m_pHistory ? m_pHistory->Count() : 0;
+ const SwFrameFormats& rSpzArr = *rDoc.GetSpzFrameFormats();
+ if( !rSpzArr.empty() )
+ {
+ SwFrameFormat* pFormat;
+ const SwFormatAnchor* pAnchor;
+ size_t n = rSpzArr.size();
+ const SwPosition* pAPos;
+
+ while( n && !rSpzArr.empty() )
+ {
+ pFormat = rSpzArr[--n];
+ pAnchor = &pFormat->GetAnchor();
+ switch( pAnchor->GetAnchorId() )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ (( DelContentType::CheckNoCntnt & nDelContentType )
+ ? ( pStt->nNode <= pAPos->nNode &&
+ pAPos->nNode < pEnd->nNode )
+ : ( *pStt <= *pAPos && *pAPos < *pEnd )) )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwTextNode *const pTextNd =
+ pAPos->nNode.GetNode().GetTextNode();
+ SwTextAttr* const pFlyHint = pTextNd->GetTextAttrForCharAt(
+ pAPos->nContent.GetIndex());
+ assert(pFlyHint);
+ m_pHistory->Add( pFlyHint, SwNodeOffset(0), false );
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ {
+ pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode)
+ {
+ if (!m_pHistory)
+ m_pHistory.reset( new SwHistory );
+
+ if (!(DelContentType::Replace & nDelContentType)
+ && IsSelectFrameAnchoredAtPara(*pAPos, *pStt, *pEnd, nDelContentType))
+ {
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ // Moving the anchor?
+ else if (!((DelContentType::CheckNoCntnt|DelContentType::ExcludeFlyAtStartEnd)
+ & nDelContentType) &&
+ // for SwUndoDelete: rPoint is the node that
+ // will be Joined - so anchor should be moved
+ // off it - but UndoImpl() split will insert
+ // new node *before* existing one so a no-op
+ // may need to be done here to add it to
+ // history for Undo.
+ (rPoint.nNode.GetIndex() == pAPos->nNode.GetIndex()
+ || pStt->nNode.GetIndex() == pAPos->nNode.GetIndex())
+ // Do not try to move the anchor to a table!
+ && rMark.nNode.GetNode().IsTextNode())
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ SwFormatAnchor aAnch( *pAnchor );
+ SwPosition aPos( rMark.nNode );
+ aAnch.SetAnchor( &aPos );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ break;
+ case RndStdIds::FLY_AT_CHAR:
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ ( pStt->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode ) )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ if (!(DelContentType::Replace & nDelContentType)
+ && IsDestroyFrameAnchoredAtChar(
+ *pAPos, *pStt, *pEnd, nDelContentType))
+ {
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ else if (!((DelContentType::CheckNoCntnt |
+ DelContentType::ExcludeFlyAtStartEnd)
+ & nDelContentType))
+ {
+ if( *pStt <= *pAPos && *pAPos < *pEnd )
+ {
+ // These are the objects anchored
+ // between section start and end position
+ // Do not try to move the anchor to a table!
+ if( rMark.nNode.GetNode().GetTextNode() )
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ SwFormatAnchor aAnch( *pAnchor );
+ aAnch.SetAnchor( &rMark );
+ pFormat->SetFormatAttr( aAnch );
+ }
+ }
+ }
+ }
+ break;
+ case RndStdIds::FLY_AT_FLY:
+
+ if( nullptr != (pAPos = pAnchor->GetContentAnchor() ) &&
+ pStt->nNode == pAPos->nNode )
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+
+ m_pHistory->AddDeleteFly(*pFormat, nChainInsPos);
+
+ // reset n so that no Format is skipped
+ n = n >= rSpzArr.size() ? rSpzArr.size() : n+1;
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+
+ // 3. Bookmarks
+ if( !(DelContentType::Bkm & nDelContentType) )
+ return;
+
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ if( !pMarkAccess->getAllMarksCount() )
+ return;
+
+ for( sal_Int32 n = 0; n < pMarkAccess->getAllMarksCount(); ++n )
+ {
+ // #i81002#
+ bool bSavePos = false;
+ bool bSaveOtherPos = false;
+ const ::sw::mark::IMark *const pBkmk = pMarkAccess->getAllMarksBegin()[n];
+ auto const type(IDocumentMarkAccess::GetType(*pBkmk));
+
+ if( DelContentType::CheckNoCntnt & nDelContentType )
+ {
+ if ( pStt->nNode <= pBkmk->GetMarkPos().nNode
+ && pBkmk->GetMarkPos().nNode < pEnd->nNode )
+ {
+ bSavePos = true;
+ }
+ if ( pBkmk->IsExpanded()
+ && pStt->nNode <= pBkmk->GetOtherMarkPos().nNode
+ && pBkmk->GetOtherMarkPos().nNode < pEnd->nNode )
+ {
+ bSaveOtherPos = true;
+ }
+ }
+ else
+ {
+ // #i92125#
+ // keep cross-reference bookmarks, if content inside one paragraph is deleted.
+ if ( rMark.nNode == rPoint.nNode
+ && ( type == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK
+ || type == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK))
+ {
+ continue;
+ }
+
+ bool bMaybe = false;
+ if ( *pStt <= pBkmk->GetMarkPos() && pBkmk->GetMarkPos() <= *pEnd )
+ {
+ if ( pBkmk->GetMarkPos() == *pEnd
+ || ( *pStt == pBkmk->GetMarkPos() && pBkmk->IsExpanded() ) )
+ bMaybe = true;
+ else
+ bSavePos = true;
+ }
+ if( pBkmk->IsExpanded() &&
+ *pStt <= pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() <= *pEnd )
+ {
+ assert(!bSaveOtherPos);
+ if ( bSavePos
+ || (*pStt < pBkmk->GetOtherMarkPos() && pBkmk->GetOtherMarkPos() < *pEnd)
+ || (bMaybe
+ && ( type == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
+ || type == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK
+ || type == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK
+ || type == IDocumentMarkAccess::MarkType::DATE_FIELDMARK)))
+ {
+ if( bMaybe )
+ bSavePos = true;
+ bSaveOtherPos = true;
+ }
+ }
+
+ if ( !bSavePos && !bSaveOtherPos
+ && dynamic_cast< const ::sw::mark::CrossRefBookmark* >(pBkmk) )
+ {
+ // certain special handling for cross-reference bookmarks
+ const bool bDifferentTextNodesAtMarkAndPoint =
+ rMark.nNode != rPoint.nNode
+ && rMark.nNode.GetNode().GetTextNode()
+ && rPoint.nNode.GetNode().GetTextNode();
+ if ( bDifferentTextNodesAtMarkAndPoint )
+ {
+ // delete cross-reference bookmark at <pStt>, if only part of
+ // <pEnd> text node content is deleted.
+ if( pStt->nNode == pBkmk->GetMarkPos().nNode
+ && pEnd->nContent.GetIndex() != pEnd->nNode.GetNode().GetTextNode()->Len() )
+ {
+ bSavePos = true;
+ bSaveOtherPos = false; // cross-reference bookmarks are not expanded
+ }
+ // delete cross-reference bookmark at <pEnd>, if only part of
+ // <pStt> text node content is deleted.
+ else if( pEnd->nNode == pBkmk->GetMarkPos().nNode &&
+ pStt->nContent.GetIndex() != 0 )
+ {
+ bSavePos = true;
+ bSaveOtherPos = false; // cross-reference bookmarks are not expanded
+ }
+ }
+ }
+ else if (type == IDocumentMarkAccess::MarkType::ANNOTATIONMARK)
+ {
+ // delete annotation marks, if its end position is covered by the deletion
+ const SwPosition& rAnnotationEndPos = pBkmk->GetMarkEnd();
+ if ( *pStt < rAnnotationEndPos && rAnnotationEndPos <= *pEnd )
+ {
+ bSavePos = true;
+ bSaveOtherPos = pBkmk->IsExpanded(); //tdf#90138, only save the other pos if there is one
+ }
+ }
+ }
+
+ if ( bSavePos || bSaveOtherPos )
+ {
+ if (type != IDocumentMarkAccess::MarkType::UNO_BOOKMARK)
+ {
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->Add( *pBkmk, bSavePos, bSaveOtherPos );
+ }
+ if ( bSavePos
+ && ( bSaveOtherPos
+ || !pBkmk->IsExpanded() ) )
+ {
+ pMarkAccess->deleteMark(pMarkAccess->getAllMarksBegin()+n, false);
+ n--;
+ }
+ }
+ }
+}
+
+// save a complete section into UndoNodes array
+SwUndoSaveSection::SwUndoSaveSection()
+ : m_nMoveLen( 0 ), m_nStartPos( NODE_OFFSET_MAX )
+{
+}
+
+SwUndoSaveSection::~SwUndoSaveSection()
+{
+ if (m_pMovedStart) // delete also the section from UndoNodes array
+ {
+ // SaveSection saves the content in the PostIt section.
+ SwNodes& rUNds = m_pMovedStart->GetNode().GetNodes();
+ // cid#1486004 Uncaught exception
+ suppress_fun_call_w_exception(rUNds.Delete(*m_pMovedStart, m_nMoveLen));
+
+ m_pMovedStart.reset();
+ }
+ m_pRedlineSaveData.reset();
+}
+
+void SwUndoSaveSection::SaveSection( const SwNodeIndex& rSttIdx )
+{
+ SwNodeRange aRg( rSttIdx.GetNode(), *rSttIdx.GetNode().EndOfSectionNode() );
+ SaveSection( aRg );
+}
+
+void SwUndoSaveSection::SaveSection(
+ const SwNodeRange& rRange, bool const bExpandNodes)
+{
+ SwPaM aPam( rRange.aStart, rRange.aEnd );
+
+ // delete all footnotes, fly frames, bookmarks
+ DelContentIndex( *aPam.GetMark(), *aPam.GetPoint() );
+
+ // redlines *before* CorrAbs, because DelBookmarks will make them 0-length
+ // but *after* DelContentIndex because that also may use FillSaveData (in
+ // flys) and that will be restored *after* this one...
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if (!SwUndo::FillSaveData( aPam, *m_pRedlineSaveData ))
+ {
+ m_pRedlineSaveData.reset();
+ }
+
+ {
+ // move certain indexes out of deleted range
+ SwNodeIndex aSttIdx( aPam.Start()->nNode.GetNode() );
+ SwNodeIndex aEndIdx( aPam.End()->nNode.GetNode() );
+ SwNodeIndex aMvStt( aEndIdx, 1 );
+ SwDoc::CorrAbs( aSttIdx, aEndIdx, SwPosition( aMvStt ), true );
+ }
+
+ m_nStartPos = rRange.aStart.GetIndex();
+
+ if (bExpandNodes)
+ {
+ --aPam.GetPoint()->nNode;
+ ++aPam.GetMark()->nNode;
+ }
+
+ SwContentNode* pCNd = aPam.GetContentNode( false );
+ if( pCNd )
+ aPam.GetMark()->nContent.Assign( pCNd, 0 );
+ pCNd = aPam.GetContentNode();
+ if( nullptr != pCNd )
+ aPam.GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ // Keep positions as SwIndex so that this section can be deleted in DTOR
+ SwNodeOffset nEnd;
+ m_pMovedStart.reset(new SwNodeIndex(rRange.aStart));
+ MoveToUndoNds(aPam, m_pMovedStart.get(), &nEnd);
+ m_nMoveLen = nEnd - m_pMovedStart->GetIndex() + 1;
+}
+
+void SwUndoSaveSection::RestoreSection( SwDoc* pDoc, SwNodeIndex* pIdx,
+ sal_uInt16 nSectType )
+{
+ if( NODE_OFFSET_MAX == m_nStartPos ) // was there any content?
+ return;
+
+ // check if the content is at the old position
+ SwNodeIndex aSttIdx( pDoc->GetNodes(), m_nStartPos );
+
+ // move the content from UndoNodes array into Fly
+ SwStartNode* pSttNd = SwNodes::MakeEmptySection( aSttIdx,
+ static_cast<SwStartNodeType>(nSectType) );
+
+ RestoreSection( pDoc, SwNodeIndex( *pSttNd->EndOfSectionNode() ));
+
+ if( pIdx )
+ *pIdx = *pSttNd;
+}
+
+void SwUndoSaveSection::RestoreSection(
+ SwDoc *const pDoc, const SwNodeIndex& rInsPos, bool bForceCreateFrames)
+{
+ if( NODE_OFFSET_MAX == m_nStartPos ) // was there any content?
+ return;
+
+ SwPosition aInsPos( rInsPos );
+ SwNodeOffset nEnd = m_pMovedStart->GetIndex() + m_nMoveLen - 1;
+ MoveFromUndoNds(*pDoc, m_pMovedStart->GetIndex(), aInsPos, &nEnd, bForceCreateFrames);
+
+ // destroy indices again, content was deleted from UndoNodes array
+ m_pMovedStart.reset();
+ m_nMoveLen = SwNodeOffset(0);
+
+ if( m_pRedlineSaveData )
+ {
+ SwUndo::SetSaveData( *pDoc, *m_pRedlineSaveData );
+ m_pRedlineSaveData.reset();
+ }
+}
+
+void SwUndoSaveSection::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ SwUndoSaveContent::dumpAsXml(pWriter);
+}
+
+// save and set the RedlineData
+SwRedlineSaveData::SwRedlineSaveData(
+ SwComparePosition eCmpPos,
+ const SwPosition& rSttPos,
+ const SwPosition& rEndPos,
+ SwRangeRedline& rRedl,
+ bool bCopyNext )
+ : SwUndRng( rRedl )
+ , SwRedlineData( rRedl.GetRedlineData(), bCopyNext )
+{
+ assert( SwComparePosition::Outside == eCmpPos ||
+ !rRedl.GetContentIdx() ); // "Redline with Content"
+
+ switch (eCmpPos)
+ {
+ case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at the beginning
+ m_nEndNode = rEndPos.nNode.GetIndex();
+ m_nEndContent = rEndPos.nContent.GetIndex();
+ break;
+
+ case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at the end
+ m_nSttNode = rSttPos.nNode.GetIndex();
+ m_nSttContent = rSttPos.nContent.GetIndex();
+ break;
+
+ case SwComparePosition::Inside: // Pos1 lays completely in Pos2
+ m_nSttNode = rSttPos.nNode.GetIndex();
+ m_nSttContent = rSttPos.nContent.GetIndex();
+ m_nEndNode = rEndPos.nNode.GetIndex();
+ m_nEndContent = rEndPos.nContent.GetIndex();
+ break;
+
+ case SwComparePosition::Outside: // Pos2 lays completely in Pos1
+ if ( rRedl.GetContentIdx() )
+ {
+ // than move section into UndoArray and memorize it
+ SaveSection( *rRedl.GetContentIdx() );
+ rRedl.SetContentIdx( nullptr );
+ }
+ break;
+
+ case SwComparePosition::Equal: // Pos1 is exactly as big as Pos2
+ break;
+
+ default:
+ assert(false);
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ m_nRedlineCount = rSttPos.nNode.GetNode().GetDoc().getIDocumentRedlineAccess().GetRedlineTable().size();
+ m_bRedlineMoved = rRedl.IsMoved();
+#endif
+}
+
+SwRedlineSaveData::~SwRedlineSaveData()
+{
+}
+
+void SwRedlineSaveData::RedlineToDoc( SwPaM const & rPam )
+{
+ SwDoc& rDoc = rPam.GetDoc();
+ SwRangeRedline* pRedl = new SwRangeRedline( *this, rPam );
+
+ if( GetMvSttIdx() )
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes() );
+ RestoreSection( &rDoc, &aIdx, SwNormalStartNode );
+ if( GetHistory() )
+ GetHistory()->Rollback( &rDoc );
+ pRedl->SetContentIdx( &aIdx );
+ }
+ SetPaM( *pRedl );
+ // First, delete the "old" so that in an Append no unexpected things will
+ // happen, e.g. a delete in an insert. In the latter case the just restored
+ // content will be deleted and not the one you originally wanted.
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pRedl, false, RedlineType::Any );
+
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::DontCombineRedlines );
+ //#i92154# let UI know about a new redline with comment
+ if (rDoc.GetDocShell() && (!pRedl->GetComment().isEmpty()) )
+ rDoc.GetDocShell()->Broadcast(SwRedlineHint());
+
+ auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline(pRedl, true));
+ assert(result != IDocumentRedlineAccess::AppendResult::IGNORED); // SwRedlineSaveData::RedlineToDoc: insert redline failed
+ (void) result; // unused in non-debug
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwUndo::FillSaveData(
+ const SwPaM& rRange,
+ SwRedlineSaveDatas& rSData,
+ bool bDelRange,
+ bool bCopyNext )
+{
+ rSData.clear();
+
+ const SwPosition* pStt = rRange.Start();
+ const SwPosition* pEnd = rRange.End();
+ const SwRedlineTable& rTable = rRange.GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type n = 0;
+ rRange.GetDoc().getIDocumentRedlineAccess().GetRedline( *pStt, &n );
+ for ( ; n < rTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+
+ const SwComparePosition eCmpPos =
+ ComparePosition( *pStt, *pEnd, *pRedl->Start(), *pRedl->End() );
+ if ( eCmpPos != SwComparePosition::Before
+ && eCmpPos != SwComparePosition::Behind
+ && eCmpPos != SwComparePosition::CollideEnd
+ && eCmpPos != SwComparePosition::CollideStart )
+ {
+
+ rSData.push_back(std::unique_ptr<SwRedlineSaveData>(new SwRedlineSaveData(eCmpPos, *pStt, *pEnd, *pRedl, bCopyNext)));
+ }
+ }
+ if( !rSData.empty() && bDelRange )
+ {
+ rRange.GetDoc().getIDocumentRedlineAccess().DeleteRedline( rRange, false, RedlineType::Any );
+ }
+ return !rSData.empty();
+}
+
+bool SwUndo::FillSaveDataForFormat(
+ const SwPaM& rRange,
+ SwRedlineSaveDatas& rSData )
+{
+ rSData.clear();
+
+ const SwPosition *pStt = rRange.Start(), *pEnd = rRange.End();
+ const SwRedlineTable& rTable = rRange.GetDoc().getIDocumentRedlineAccess().GetRedlineTable();
+ SwRedlineTable::size_type n = 0;
+ rRange.GetDoc().getIDocumentRedlineAccess().GetRedline( *pStt, &n );
+ for ( ; n < rTable.size(); ++n )
+ {
+ SwRangeRedline* pRedl = rTable[n];
+ if ( RedlineType::Format == pRedl->GetType() )
+ {
+ const SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRedl->Start(), *pRedl->End() );
+ if ( eCmpPos != SwComparePosition::Before
+ && eCmpPos != SwComparePosition::Behind
+ && eCmpPos != SwComparePosition::CollideEnd
+ && eCmpPos != SwComparePosition::CollideStart )
+ {
+ rSData.push_back(std::unique_ptr<SwRedlineSaveData>(new SwRedlineSaveData(eCmpPos, *pStt, *pEnd, *pRedl, true)));
+ }
+
+ }
+ }
+ return !rSData.empty();
+}
+
+
+void SwUndo::SetSaveData( SwDoc& rDoc, SwRedlineSaveDatas& rSData )
+{
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+ SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
+
+ for( size_t n = rSData.size(); n; )
+ rSData[ --n ].RedlineToDoc( aPam );
+
+#if OSL_DEBUG_LEVEL > 0
+ // check redline count against count saved in RedlineSaveData object
+ // except in the case of moved redlines
+ assert(rSData.empty() || rSData[0].m_bRedlineMoved ||
+ (rSData[0].m_nRedlineCount == rDoc.getIDocumentRedlineAccess().GetRedlineTable().size()));
+ // "redline count not restored properly"
+#endif
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+bool SwUndo::HasHiddenRedlines( const SwRedlineSaveDatas& rSData )
+{
+ for( size_t n = rSData.size(); n; )
+ if( rSData[ --n ].GetMvSttIdx() )
+ return true;
+ return false;
+}
+
+bool SwUndo::CanRedlineGroup( SwRedlineSaveDatas& rCurr,
+ const SwRedlineSaveDatas& rCheck, bool bCurrIsEnd )
+{
+ if( rCurr.size() != rCheck.size() )
+ return false;
+
+ for( size_t n = 0; n < rCurr.size(); ++n )
+ {
+ const SwRedlineSaveData& rSet = rCurr[ n ];
+ const SwRedlineSaveData& rGet = rCheck[ n ];
+ if( rSet.m_nSttNode != rGet.m_nSttNode ||
+ rSet.GetMvSttIdx() || rGet.GetMvSttIdx() ||
+ ( bCurrIsEnd ? rSet.m_nSttContent != rGet.m_nEndContent
+ : rSet.m_nEndContent != rGet.m_nSttContent ) ||
+ !rGet.CanCombine( rSet ) )
+ {
+ return false;
+ }
+ }
+
+ for( size_t n = 0; n < rCurr.size(); ++n )
+ {
+ SwRedlineSaveData& rSet = rCurr[ n ];
+ const SwRedlineSaveData& rGet = rCheck[ n ];
+ if( bCurrIsEnd )
+ rSet.m_nSttContent = rGet.m_nSttContent;
+ else
+ rSet.m_nEndContent = rGet.m_nEndContent;
+ }
+ return true;
+}
+
+OUString ShortenString(const OUString & rStr, sal_Int32 nLength, const OUString & rFillStr)
+{
+ assert(nLength - rFillStr.getLength() >= 2);
+
+ if (rStr.getLength() <= nLength)
+ return rStr;
+
+ nLength -= rFillStr.getLength();
+ if ( nLength < 2 )
+ nLength = 2;
+
+ const sal_Int32 nFrontLen = nLength - nLength / 2;
+ const sal_Int32 nBackLen = nLength - nFrontLen;
+
+ return rStr.subView(0, nFrontLen)
+ + rFillStr
+ + rStr.subView(rStr.getLength() - nBackLen);
+}
+
+static bool IsAtEndOfSection(SwPosition const& rAnchorPos)
+{
+ SwNodeIndex node(*rAnchorPos.nNode.GetNode().EndOfSectionNode());
+ SwContentNode *const pNode(SwNodes::GoPrevious(&node));
+ assert(pNode);
+ assert(rAnchorPos.nNode <= node); // last valid anchor pos is last content
+ return node == rAnchorPos.nNode
+ // at-para fly has no SwIndex!
+ && (rAnchorPos.nContent == pNode->Len() || rAnchorPos.nContent.GetIdxReg() == nullptr);
+}
+
+static bool IsAtStartOfSection(SwPosition const& rAnchorPos)
+{
+ SwNodes const& rNodes(rAnchorPos.nNode.GetNodes());
+ SwNodeIndex node(*rAnchorPos.nNode.GetNode().StartOfSectionNode());
+ SwContentNode *const pNode(rNodes.GoNext(&node));
+ assert(pNode);
+ (void) pNode;
+ assert(node <= rAnchorPos.nNode);
+ return node == rAnchorPos.nNode && rAnchorPos.nContent == 0;
+}
+
+/// passed start / end position could be on section start / end node
+static bool IsAtEndOfSection2(SwPosition const& rPos)
+{
+ return rPos.nNode.GetNode().IsEndNode()
+ || IsAtEndOfSection(rPos);
+}
+
+static bool IsAtStartOfSection2(SwPosition const& rPos)
+{
+ return rPos.nNode.GetNode().IsStartNode()
+ || IsAtStartOfSection(rPos);
+}
+
+static bool IsNotBackspaceHeuristic(
+ SwPosition const& rStart, SwPosition const& rEnd)
+{
+ // check if the selection is backspace/delete created by DelLeft/DelRight
+ if (rStart.nNode.GetIndex() + 1 != rEnd.nNode.GetIndex())
+ return true;
+ if (rEnd.nContent != 0)
+ return true;
+ const SwTextNode* pTextNode = rStart.nNode.GetNode().GetTextNode();
+ if (!pTextNode || rStart.nContent != pTextNode->Len())
+ return true;
+ return false;
+}
+
+bool IsDestroyFrameAnchoredAtChar(SwPosition const & rAnchorPos,
+ SwPosition const & rStart, SwPosition const & rEnd,
+ DelContentType const nDelContentType)
+{
+ assert(rStart <= rEnd);
+
+ // CheckNoCntnt means DelFullPara which is obvious to handle
+ if (DelContentType::CheckNoCntnt & nDelContentType)
+ { // exclude selection end node because it won't be deleted
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode <= rAnchorPos.nNode);
+ }
+
+ if ((nDelContentType & DelContentType::WriterfilterHack)
+ && rAnchorPos.GetDoc().IsInWriterfilterImport())
+ { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific?
+ return (rStart < rAnchorPos) && (rAnchorPos < rEnd);
+ }
+
+ if (nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ { // exclude selection start and end node
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode < rAnchorPos.nNode);
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart < rAnchorPos)
+ || (rStart == rAnchorPos
+ // special case: fully deleted node
+ && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0
+ // but not if the selection is backspace/delete!
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtStartOfSection(rAnchorPos) && IsAtEndOfSection2(rEnd)))))
+ && ((rAnchorPos < rEnd)
+ || (rAnchorPos == rEnd
+ // special case: fully deleted node
+ && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len()
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtEndOfSection(rAnchorPos) && IsAtStartOfSection2(rStart)))));
+}
+
+bool IsSelectFrameAnchoredAtPara(SwPosition const & rAnchorPos,
+ SwPosition const & rStart, SwPosition const & rEnd,
+ DelContentType const nDelContentType)
+{
+ assert(rStart <= rEnd);
+
+ // CheckNoCntnt means DelFullPara which is obvious to handle
+ if (DelContentType::CheckNoCntnt & nDelContentType)
+ { // exclude selection end node because it won't be deleted
+ return (rAnchorPos.nNode < rEnd.nNode)
+ && (rStart.nNode <= rAnchorPos.nNode);
+ }
+
+ if ((nDelContentType & DelContentType::WriterfilterHack)
+ && rAnchorPos.GetDoc().IsInWriterfilterImport())
+ { // FIXME hack for writerfilter RemoveLastParagraph() and MakeFlyAndMove(); can't test file format more specific?
+ // but it MUST NOT be done during the SetRedlineFlags at the end of ODF
+ // import, where the IsInXMLImport() cannot be checked because the
+ // stupid code temp. overrides it - instead rely on setting the ALLFLYS
+ // flag in MoveFromSection() and converting that to CheckNoCntnt with
+ // adjusted cursor!
+ return (rStart.nNode < rAnchorPos.nNode) && (rAnchorPos.nNode < rEnd.nNode);
+ }
+
+ // in general, exclude the start and end position
+ return ((rStart.nNode < rAnchorPos.nNode)
+ || (rStart.nNode == rAnchorPos.nNode
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rStart.nNode != rEnd.nNode && rStart.nContent == 0
+ // but not if the selection is backspace/delete!
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtStartOfSection2(rStart) && IsAtEndOfSection2(rEnd)))))
+ && ((rAnchorPos.nNode < rEnd.nNode)
+ || (rAnchorPos.nNode == rEnd.nNode
+ && !(nDelContentType & DelContentType::ExcludeFlyAtStartEnd)
+ // special case: fully deleted node
+ && ((rEnd.nNode != rStart.nNode && rEnd.nContent == rEnd.nNode.GetNode().GetTextNode()->Len()
+ && IsNotBackspaceHeuristic(rStart, rEnd))
+ || (IsAtEndOfSection2(rEnd) && IsAtStartOfSection2(rStart)))));
+}
+
+bool IsFlySelectedByCursor(SwDoc const & rDoc,
+ SwPosition const & rStart, SwPosition const & rEnd)
+{
+ for (SwFrameFormat const*const pFly : *rDoc.GetSpzFrameFormats())
+ {
+ SwFormatAnchor const& rAnchor(pFly->GetAnchor());
+ switch (rAnchor.GetAnchorId())
+ {
+ case RndStdIds::FLY_AT_CHAR:
+ case RndStdIds::FLY_AT_PARA:
+ {
+ SwPosition const*const pAnchorPos(rAnchor.GetContentAnchor());
+ // can this really be null?
+ if (pAnchorPos != nullptr
+ && ((rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ ? IsDestroyFrameAnchoredAtChar(*pAnchorPos, rStart, rEnd)
+ : IsSelectFrameAnchoredAtPara(*pAnchorPos, rStart, rEnd)))
+ {
+ return true;
+ }
+ }
+ break;
+ default: // other types not relevant
+ break;
+ }
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undobj1.cxx b/sw/source/core/undo/undobj1.cxx
new file mode 100644
index 000000000..2b989872f
--- /dev/null
+++ b/sw/source/core/undo/undobj1.cxx
@@ -0,0 +1,728 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <svl/itemiter.hxx>
+#include <svx/svdundo.hxx>
+#include <osl/diagnose.h>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <fmtflcnt.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <txtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <ndole.hxx>
+#include <frameformats.hxx>
+#include <svx/svdobj.hxx>
+
+SwUndoFlyBase::SwUndoFlyBase( SwFrameFormat* pFormat, SwUndoId nUndoId )
+ : SwUndo(nUndoId, pFormat->GetDoc())
+ , m_pFrameFormat(pFormat)
+ , m_nNodePagePos(0)
+ , m_nContentPos(0)
+ , m_nRndId(RndStdIds::FLY_AT_PARA)
+ , m_bDelFormat(false)
+{
+}
+
+SwUndoFlyBase::~SwUndoFlyBase()
+{
+ if( m_bDelFormat ) // delete during an Undo?
+ {
+ if (const auto& pTextBoxes = m_pFrameFormat->GetOtherTextBoxFormats())
+ {
+ // Clear and unregister before release.
+ if (m_pFrameFormat->Which() == RES_FLYFRMFMT)
+ pTextBoxes->DelTextBox(m_pFrameFormat);
+
+ if (m_pFrameFormat->Which() == RES_DRAWFRMFMT)
+ pTextBoxes->ClearAll();
+
+ // clear that before delete
+ m_pFrameFormat->SetOtherTextBoxFormats(nullptr);
+ }
+ delete m_pFrameFormat;
+ }
+}
+
+void SwUndoFlyBase::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoFlyBase"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nNodePagePos"),
+ BAD_CAST(OString::number(sal_Int32(m_nNodePagePos)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nContentPos"),
+ BAD_CAST(OString::number(m_nContentPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_nRndId"),
+ BAD_CAST(OString::number(static_cast<int>(m_nRndId)).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("m_bDelFormat"),
+ BAD_CAST(OString::boolean(m_bDelFormat).getStr()));
+
+ SwUndo::dumpAsXml(pWriter);
+ SwUndoSaveSection::dumpAsXml(pWriter);
+
+ if (m_pFrameFormat)
+ {
+ m_pFrameFormat->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SwUndoFlyBase::InsFly(::sw::UndoRedoContext & rContext, bool bShowSelFrame)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ // add again into array
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.push_back( m_pFrameFormat );
+
+ // OD 26.06.2003 #108784# - insert 'master' drawing object into drawing page
+ if ( RES_DRAWFRMFMT == m_pFrameFormat->Which() )
+ m_pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREP_INSERT_FLY));
+
+ SwFormatAnchor aAnchor( m_nRndId );
+
+ if (RndStdIds::FLY_AT_PAGE == m_nRndId)
+ {
+ aAnchor.SetPageNum( o3tl::narrowing<sal_uInt16>(sal_Int32(m_nNodePagePos)) );
+ }
+ else
+ {
+ SwPosition aNewPos(pDoc->GetNodes().GetEndOfContent());
+ aNewPos.nNode = m_nNodePagePos;
+ if ((RndStdIds::FLY_AS_CHAR == m_nRndId) || (RndStdIds::FLY_AT_CHAR == m_nRndId))
+ {
+ aNewPos.nContent.Assign( aNewPos.nNode.GetNode().GetContentNode(),
+ m_nContentPos );
+ }
+ aAnchor.SetAnchor( &aNewPos );
+ }
+
+ m_pFrameFormat->SetFormatAttr( aAnchor ); // reset anchor
+
+ if( RES_DRAWFRMFMT != m_pFrameFormat->Which() )
+ {
+ // get Content and reset ContentAttribute
+ SwNodeIndex aIdx( pDoc->GetNodes() );
+ RestoreSection( pDoc, &aIdx, SwFlyStartNode );
+ m_pFrameFormat->SetFormatAttr( SwFormatContent( aIdx.GetNode().GetStartNode() ));
+ }
+
+ // Set InContentAttribute not until there is content!
+ // Otherwise the layout would format the Fly beforehand but would not find
+ // content; this happened with graphics from the internet.
+ if (RndStdIds::FLY_AS_CHAR == m_nRndId)
+ {
+ // there must be at least the attribute in a TextNode
+ SwContentNode* pCNd = aAnchor.GetContentAnchor()->nNode.GetNode().GetContentNode();
+ OSL_ENSURE( pCNd->IsTextNode(), "no Text Node at position." );
+ SwFormatFlyCnt aFormat( m_pFrameFormat );
+ pCNd->GetTextNode()->InsertItem(aFormat, m_nContentPos, m_nContentPos, SetAttrMode::NOHINTEXPAND);
+ }
+
+ if (m_pFrameFormat->GetOtherTextBoxFormats())
+ {
+ // recklessly assume that this thing will live longer than the
+ // SwUndoFlyBase - not sure what could be done if that isn't the case...
+ m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->SetOtherTextBoxFormats(
+ m_pFrameFormat->GetOtherTextBoxFormats());
+
+ SdrObject* pSdrObject
+ = m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->FindSdrObject();
+ if (pSdrObject && m_pFrameFormat->Which() == RES_FLYFRMFMT)
+ m_pFrameFormat->GetOtherTextBoxFormats()->AddTextBox(pSdrObject, m_pFrameFormat);
+
+ if (m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->Which() == RES_DRAWFRMFMT)
+ {
+
+ if (pSdrObject)
+ {
+ // Make sure the old UNO wrapper is no longer cached after changing the shape +
+ // textframe pair. Otherwise we would have a wrapper which doesn't know about its
+ // textframe, even if it's there.
+ pSdrObject->setUnoShape(nullptr);
+ }
+ }
+ if (m_pFrameFormat->Which() == RES_FLYFRMFMT)
+ {
+ SwFrameFormat* pShapeFormat = m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape();
+ pShapeFormat->SetFormatAttr(m_pFrameFormat->GetContent());
+ }
+ }
+
+ m_pFrameFormat->MakeFrames();
+
+ if( bShowSelFrame )
+ {
+ rContext.SetSelections(m_pFrameFormat, nullptr);
+ }
+
+ if( GetHistory() )
+ GetHistory()->Rollback( pDoc );
+
+ switch( m_nRndId )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ m_nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex();
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ {
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nNodePagePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ }
+ break;
+ case RndStdIds::FLY_AT_PAGE:
+ break;
+ default: break;
+ }
+ m_bDelFormat = false;
+}
+
+void SwUndoFlyBase::DelFly( SwDoc* pDoc )
+{
+ m_bDelFormat = true; // delete Format in DTOR
+ m_pFrameFormat->DelFrames(); // destroy Frames
+
+ if (m_pFrameFormat->GetOtherTextBoxFormats())
+ { // tdf#108867 clear that pointer
+ m_pFrameFormat->GetOtherTextBoxFormats()->GetOwnerShape()->SetOtherTextBoxFormats(nullptr);
+ }
+
+ // all Uno objects should now log themselves off
+ m_pFrameFormat->RemoveAllUnos();
+
+ if ( RES_DRAWFRMFMT != m_pFrameFormat->Which() )
+ {
+ // if there is content than save it
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "Fly without content" );
+
+ SaveSection( *rContent.GetContentIdx() );
+ const_cast<SwFormatContent&>(rContent).SetNewContentIdx( nullptr );
+ }
+ // OD 02.07.2003 #108784# - remove 'master' drawing object from drawing page
+ else
+ m_pFrameFormat->CallSwClientNotify(sw::DrawFrameFormatHint(sw::DrawFrameFormatHintId::PREP_DELETE_FLY));
+
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ const SwPosition* pPos = rAnchor.GetContentAnchor();
+ // The positions in Nodes array got shifted.
+ m_nRndId = rAnchor.GetAnchorId();
+ if (RndStdIds::FLY_AS_CHAR == m_nRndId)
+ {
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ m_nContentPos = pPos->nContent.GetIndex();
+ SwTextNode *const pTextNd = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "No Textnode found" );
+ SwTextFlyCnt* const pAttr = static_cast<SwTextFlyCnt*>(
+ pTextNd->GetTextAttrForCharAt( m_nContentPos, RES_TXTATR_FLYCNT ) );
+ // attribute is still in TextNode, delete
+ if( pAttr && pAttr->GetFlyCnt().GetFrameFormat() == m_pFrameFormat )
+ {
+ // Pointer to 0, do not delete
+ const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
+ SwIndex aIdx( pPos->nContent );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ else if (RndStdIds::FLY_AT_CHAR == m_nRndId)
+ {
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ m_nContentPos = pPos->nContent.GetIndex();
+ }
+ else if ((RndStdIds::FLY_AT_PARA == m_nRndId) || (RndStdIds::FLY_AT_FLY == m_nRndId))
+ {
+ m_nNodePagePos = pPos->nNode.GetIndex();
+ }
+ else
+ {
+ m_nNodePagePos = SwNodeOffset(rAnchor.GetPageNum());
+ }
+
+ m_pFrameFormat->ResetFormatAttr( RES_ANCHOR ); // delete anchor
+
+ // delete from array
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( m_pFrameFormat );
+}
+
+SwUndoInsLayFormat::SwUndoInsLayFormat( SwFrameFormat* pFormat, SwNodeOffset nNodeIdx, sal_Int32 nCntIdx )
+ : SwUndoFlyBase( pFormat, RES_DRAWFRMFMT == pFormat->Which() ?
+ SwUndoId::INSDRAWFMT : SwUndoId::INSLAYFMT ),
+ mnCursorSaveIndexPara( nNodeIdx ), mnCursorSaveIndexPos( nCntIdx )
+{
+ const SwFormatAnchor& rAnchor = m_pFrameFormat->GetAnchor();
+ m_nRndId = rAnchor.GetAnchorId();
+ m_bDelFormat = false;
+ // note: SwUndoInsLayFormat is called with the content being fully inserted
+ // from most places but with only an empty content section from
+ // CopyLayoutFormat(); it's not necessary here to init m_nNodePagePos
+ // because Undo will do it.
+}
+
+SwUndoInsLayFormat::~SwUndoInsLayFormat()
+{
+}
+
+void SwUndoInsLayFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() ) // no content
+ {
+ assert(&rContent.GetContentIdx()->GetNodes() == &rDoc.GetNodes());
+ if( mnCursorSaveIndexPara > SwNodeOffset(0) )
+ {
+ SwTextNode *const pNode =
+ rDoc.GetNodes()[mnCursorSaveIndexPara]->GetTextNode();
+ if( pNode )
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(),
+ rContent.GetContentIdx()->GetIndex() );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(),
+ aIdx.GetNode().EndOfSectionIndex() );
+ SwIndex aIndex( pNode, mnCursorSaveIndexPos );
+ SwPosition aPos( *pNode, aIndex );
+ // don't delete bookmarks here, DelFly() will save them in history
+ ::PaMCorrAbs(SwPaM(aIdx, aEndIdx), aPos);
+ // TODO: is aPos actually a sensible pos for e.g. SwXText* ?
+ }
+ }
+ }
+ DelFly(& rDoc);
+}
+
+void SwUndoInsLayFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ InsFly(rContext);
+}
+
+void SwUndoInsLayFormat::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ // get anchor and reset it
+ SwFormatAnchor aAnchor( m_pFrameFormat->GetAnchor() );
+ if ((RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == aAnchor.GetAnchorId()))
+ {
+ SwPosition aPos( *rContext.GetRepeatPaM().GetPoint() );
+ if (RndStdIds::FLY_AT_PARA == aAnchor.GetAnchorId())
+ {
+ aPos.nContent.Assign( nullptr, 0 );
+ }
+ aAnchor.SetAnchor( &aPos );
+ }
+ else if( RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId() )
+ {
+ SwStartNode const*const pSttNd =
+ rContext.GetRepeatPaM().GetNode().FindFlyStartNode();
+ if( pSttNd )
+ {
+ SwPosition aPos( *pSttNd );
+ aAnchor.SetAnchor( &aPos );
+ }
+ else
+ {
+ return ;
+ }
+ }
+ else if (RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId())
+ {
+ aAnchor.SetPageNum( pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->GetCurrPage( &rContext.GetRepeatPaM() ));
+ }
+ else {
+ OSL_FAIL( "What kind of anchor is this?" );
+ }
+
+ (void) pDoc->getIDocumentLayoutAccess().CopyLayoutFormat( *m_pFrameFormat, aAnchor, true, true );
+}
+
+OUString SwUndoInsLayFormat::GetComment() const
+{
+ OUString aResult;
+
+ // HACK: disable caching:
+ // the SfxUndoManager calls GetComment() too early: the pFrameFormat does not
+ // have a SwDrawContact yet, so it will fall back to SwUndo::GetComment(),
+ // which sets pComment to a wrong value.
+// if (! pComment)
+ if ((true))
+ {
+ /*
+ If frame format is present and has an SdrObject use the undo
+ comment of the SdrObject. Otherwise use the default comment.
+ */
+ bool bDone = false;
+ if (m_pFrameFormat)
+ {
+ const SdrObject * pSdrObj = m_pFrameFormat->FindSdrObject();
+ if ( pSdrObj )
+ {
+ aResult = SdrUndoNewObj::GetComment( *pSdrObj );
+ bDone = true;
+ }
+ }
+
+ if (! bDone)
+ aResult = SwUndo::GetComment();
+ }
+ else
+ aResult = *maComment;
+
+ return aResult;
+}
+
+static SwUndoId
+lcl_GetSwUndoId(SwFrameFormat const *const pFrameFormat)
+{
+ if (RES_DRAWFRMFMT != pFrameFormat->Which())
+ {
+ const SwFormatContent& rContent = pFrameFormat->GetContent();
+ OSL_ENSURE( rContent.GetContentIdx(), "Fly without content" );
+
+ SwNodeIndex firstNode(*rContent.GetContentIdx(), 1);
+ SwNoTextNode *const pNoTextNode(firstNode.GetNode().GetNoTextNode());
+ if (pNoTextNode && pNoTextNode->IsGrfNode())
+ {
+ return SwUndoId::DELGRF;
+ }
+ else if (pNoTextNode && pNoTextNode->IsOLENode())
+ {
+ // surprisingly not SwUndoId::DELOLE, which does not seem to work
+ return SwUndoId::DELETE;
+ }
+ }
+ return SwUndoId::DELLAYFMT;
+}
+
+SwUndoDelLayFormat::SwUndoDelLayFormat( SwFrameFormat* pFormat )
+ : SwUndoFlyBase( pFormat, lcl_GetSwUndoId(pFormat) )
+ , m_bShowSelFrame( true )
+{
+ SwDoc* pDoc = pFormat->GetDoc();
+ DelFly( pDoc );
+}
+
+SwRewriter SwUndoDelLayFormat::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ SwDoc * pDoc = m_pFrameFormat->GetDoc();
+
+ if (pDoc)
+ {
+ SwNodeIndex* pIdx = GetMvSttIdx();
+ if( SwNodeOffset(1) == GetMvNodeCnt() && pIdx)
+ {
+ SwNode *const pNd = & pIdx->GetNode();
+
+ if ( pNd->IsNoTextNode() && pNd->IsOLENode())
+ {
+ SwOLENode * pOLENd = pNd->GetOLENode();
+
+ aRewriter.AddRule(UndoArg1, pOLENd->GetDescription());
+ }
+ }
+ }
+
+ return aRewriter;
+}
+
+void SwUndoDelLayFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ InsFly( rContext, m_bShowSelFrame );
+}
+
+void SwUndoDelLayFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() ) // no content
+ {
+ RemoveIdxFromSection(rDoc, rContent.GetContentIdx()->GetIndex());
+ }
+
+ DelFly(& rDoc);
+}
+
+void SwUndoDelLayFormat::RedoForRollback()
+{
+ const SwFormatContent& rContent = m_pFrameFormat->GetContent();
+ if( rContent.GetContentIdx() ) // no content
+ RemoveIdxFromSection( *m_pFrameFormat->GetDoc(),
+ rContent.GetContentIdx()->GetIndex() );
+
+ DelFly( m_pFrameFormat->GetDoc() );
+}
+
+SwUndoSetFlyFormat::SwUndoSetFlyFormat( SwFrameFormat& rFlyFormat, const SwFrameFormat& rNewFrameFormat )
+ : SwUndo( SwUndoId::SETFLYFRMFMT, rFlyFormat.GetDoc() ), SwClient( &rFlyFormat ), m_pFrameFormat( &rFlyFormat ),
+ m_DerivedFromFormatName( rFlyFormat.IsDefault() ? "" : rFlyFormat.DerivedFrom()->GetName() ),
+ m_NewFormatName( rNewFrameFormat.GetName() ),
+ m_oItemSet( std::in_place, *rFlyFormat.GetAttrSet().GetPool(),
+ rFlyFormat.GetAttrSet().GetRanges() ),
+ m_nOldNode( 0 ), m_nNewNode( 0 ),
+ m_nOldContent( 0 ), m_nNewContent( 0 ),
+ m_nOldAnchorType( RndStdIds::FLY_AT_PARA ), m_nNewAnchorType( RndStdIds::FLY_AT_PARA ), m_bAnchorChanged( false )
+{
+}
+
+SwRewriter SwUndoSetFlyFormat::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, m_NewFormatName);
+
+ return aRewriter;
+}
+
+SwUndoSetFlyFormat::~SwUndoSetFlyFormat()
+{
+}
+
+void SwUndoSetFlyFormat::GetAnchor( SwFormatAnchor& rAnchor,
+ SwNodeOffset nNode, sal_Int32 nContent )
+{
+ RndStdIds nAnchorTyp = rAnchor.GetAnchorId();
+ if (RndStdIds::FLY_AT_PAGE != nAnchorTyp)
+ {
+ SwNode* pNd = m_pFrameFormat->GetDoc()->GetNodes()[ nNode ];
+
+ if( RndStdIds::FLY_AT_FLY == nAnchorTyp
+ ? ( !pNd->IsStartNode() || SwFlyStartNode !=
+ static_cast<SwStartNode*>(pNd)->GetStartNodeType() )
+ : !pNd->IsTextNode() )
+ {
+ pNd = nullptr; // invalid position
+ }
+ else
+ {
+ SwPosition aPos( *pNd );
+ if ((RndStdIds::FLY_AS_CHAR == nAnchorTyp) ||
+ (RndStdIds::FLY_AT_CHAR == nAnchorTyp))
+ {
+ if (nContent > pNd->GetTextNode()->GetText().getLength())
+ {
+ pNd = nullptr; // invalid position
+ }
+ else
+ {
+ aPos.nContent.Assign(pNd->GetTextNode(), nContent);
+ }
+ }
+ if ( pNd )
+ {
+ rAnchor.SetAnchor( &aPos );
+ }
+ }
+
+ if( !pNd )
+ {
+ // invalid position - assign first page
+ rAnchor.SetType( RndStdIds::FLY_AT_PAGE );
+ rAnchor.SetPageNum( 1 );
+ }
+ }
+ else
+ rAnchor.SetPageNum( nContent );
+}
+
+void SwUndoSetFlyFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // Is the new Format still existent?
+ SwFrameFormat* pDerivedFromFrameFormat = rDoc.FindFrameFormatByName(m_DerivedFromFormatName);
+ if (!pDerivedFromFrameFormat)
+ return;
+
+ if( m_bAnchorChanged )
+ m_pFrameFormat->DelFrames();
+
+ if( m_pFrameFormat->DerivedFrom() != pDerivedFromFrameFormat)
+ m_pFrameFormat->SetDerivedFrom(pDerivedFromFrameFormat);
+
+ SfxItemIter aIter( *m_oItemSet );
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if( IsInvalidItem( pItem ))
+ m_pFrameFormat->ResetFormatAttr( m_oItemSet->GetWhichByPos(
+ aIter.GetCurPos() ));
+ else
+ m_pFrameFormat->SetFormatAttr( *pItem );
+ }
+
+ if( m_bAnchorChanged )
+ {
+ const SwFormatAnchor& rOldAnch = m_pFrameFormat->GetAnchor();
+ if (RndStdIds::FLY_AS_CHAR == rOldAnch.GetAnchorId())
+ {
+ // With InContents it's tricky: the text attribute needs to be
+ // deleted. Unfortunately, this not only destroys the Frames but
+ // also the format. To prevent that, first detach the
+ // connection between attribute and format.
+ const SwPosition *pPos = rOldAnch.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNode->HasHints(), "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * pHint = pTextNode->GetTextAttrForCharAt(
+ nIdx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing Hint.");
+ OSL_ENSURE( pHint->Which() == RES_TXTATR_FLYCNT,
+ "Missing FlyInCnt-Hint." );
+ OSL_ENSURE( pHint->GetFlyCnt().GetFrameFormat() == m_pFrameFormat,
+ "Wrong TextFlyCnt-Hint." );
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt()).SetFlyFormat();
+
+ // Connection is now detached, therefore the attribute can be
+ // deleted
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+
+ // reposition anchor
+ SwFormatAnchor aNewAnchor( m_nOldAnchorType );
+ GetAnchor( aNewAnchor, m_nOldNode, m_nOldContent );
+ m_pFrameFormat->SetFormatAttr( aNewAnchor );
+
+ if (RndStdIds::FLY_AS_CHAR == aNewAnchor.GetAnchorId())
+ {
+ const SwPosition* pPos = aNewAnchor.GetContentAnchor();
+ SwFormatFlyCnt aFormat( m_pFrameFormat );
+ pPos->nNode.GetNode().GetTextNode()->InsertItem( aFormat,
+ m_nOldContent, 0 );
+ }
+
+ m_pFrameFormat->MakeFrames();
+ }
+ rContext.SetSelections(m_pFrameFormat, nullptr);
+}
+
+void SwUndoSetFlyFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // Is the new Format still existent?
+ SwFrameFormat* pNewFrameFormat = rDoc.FindFrameFormatByName(m_NewFormatName);
+ if (!pNewFrameFormat)
+ return;
+
+ if( m_bAnchorChanged )
+ {
+ SwFormatAnchor aNewAnchor( m_nNewAnchorType );
+ GetAnchor( aNewAnchor, m_nNewNode, m_nNewContent );
+ SfxItemSet aSet( rDoc.GetAttrPool(), aFrameFormatSetRange );
+ aSet.Put( aNewAnchor );
+ rDoc.SetFrameFormatToFly( *m_pFrameFormat, *pNewFrameFormat, &aSet );
+ }
+ else
+ rDoc.SetFrameFormatToFly( *m_pFrameFormat, *pNewFrameFormat);
+
+ rContext.SetSelections(m_pFrameFormat, nullptr);
+}
+
+void SwUndoSetFlyFormat::PutAttr( sal_uInt16 nWhich, const SfxPoolItem* pItem )
+{
+ if( pItem && pItem != GetDfltAttr( nWhich ) )
+ {
+ // Special treatment for this anchor
+ if( RES_ANCHOR == nWhich )
+ {
+ // only keep the first change
+ OSL_ENSURE( !m_bAnchorChanged, "multiple changes of an anchor are not allowed!" );
+
+ m_bAnchorChanged = true;
+
+ const SwFormatAnchor* pAnchor = static_cast<const SwFormatAnchor*>(pItem);
+ m_nOldAnchorType = pAnchor->GetAnchorId();
+ switch( m_nOldAnchorType )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ m_nOldContent = pAnchor->GetContentAnchor()->nContent.GetIndex();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nOldNode = pAnchor->GetContentAnchor()->nNode.GetIndex();
+ break;
+
+ default:
+ m_nOldContent = pAnchor->GetPageNum();
+ }
+
+ pAnchor = &m_pFrameFormat->GetAnchor();
+ m_nNewAnchorType = pAnchor->GetAnchorId();
+ switch( m_nNewAnchorType )
+ {
+ case RndStdIds::FLY_AS_CHAR:
+ case RndStdIds::FLY_AT_CHAR:
+ m_nNewContent = pAnchor->GetContentAnchor()->nContent.GetIndex();
+ [[fallthrough]];
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_FLY:
+ m_nNewNode = pAnchor->GetContentAnchor()->nNode.GetIndex();
+ break;
+
+ default:
+ m_nNewContent = pAnchor->GetPageNum();
+ }
+ }
+ else
+ m_oItemSet->Put( *pItem );
+ }
+ else
+ m_oItemSet->InvalidateItem( nWhich );
+}
+
+void SwUndoSetFlyFormat::SwClientNotify(const SwModify&, const SfxHint& rHint)
+{
+ if (rHint.GetId() != SfxHintId::SwLegacyModify)
+ return;
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(!pLegacy->m_pOld)
+ return;
+ const sal_uInt16 nWhich = pLegacy->m_pOld->Which();
+ if(nWhich < POOLATTR_END)
+ PutAttr(nWhich, pLegacy->m_pOld);
+ else if(RES_ATTRSET_CHG == nWhich)
+ {
+ SfxItemIter aIter(*static_cast<const SwAttrSetChg*>(pLegacy->m_pOld)->GetChgSet());
+ for(const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ PutAttr(pItem->Which(), pItem);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undoflystrattr.cxx b/sw/source/core/undo/undoflystrattr.cxx
new file mode 100644
index 000000000..1a811ad21
--- /dev/null
+++ b/sw/source/core/undo/undoflystrattr.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <undoflystrattr.hxx>
+#include <frmfmt.hxx>
+
+
+SwUndoFlyStrAttr::SwUndoFlyStrAttr( SwFlyFrameFormat& rFlyFrameFormat,
+ const SwUndoId eUndoId,
+ const OUString& sOldStr,
+ const OUString& sNewStr )
+ : SwUndo( eUndoId, rFlyFrameFormat.GetDoc() ),
+ mrFlyFrameFormat( rFlyFrameFormat ),
+ msOldStr( sOldStr ),
+ msNewStr( sNewStr )
+{
+ assert(eUndoId == SwUndoId::FLYFRMFMT_TITLE
+ || eUndoId == SwUndoId::FLYFRMFMT_DESCRIPTION);
+}
+
+SwUndoFlyStrAttr::~SwUndoFlyStrAttr()
+{
+}
+
+void SwUndoFlyStrAttr::UndoImpl(::sw::UndoRedoContext &)
+{
+ switch ( GetId() )
+ {
+ case SwUndoId::FLYFRMFMT_TITLE:
+ {
+ mrFlyFrameFormat.SetObjTitle( msOldStr, true );
+ }
+ break;
+ case SwUndoId::FLYFRMFMT_DESCRIPTION:
+ {
+ mrFlyFrameFormat.SetObjDescription( msOldStr, true );
+ }
+ break;
+ default:
+ {
+ }
+ }
+}
+
+void SwUndoFlyStrAttr::RedoImpl(::sw::UndoRedoContext &)
+{
+ switch ( GetId() )
+ {
+ case SwUndoId::FLYFRMFMT_TITLE:
+ {
+ mrFlyFrameFormat.SetObjTitle( msNewStr, true );
+ }
+ break;
+ case SwUndoId::FLYFRMFMT_DESCRIPTION:
+ {
+ mrFlyFrameFormat.SetObjDescription( msNewStr, true );
+ }
+ break;
+ default:
+ {
+ }
+ }
+}
+
+SwRewriter SwUndoFlyStrAttr::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule( UndoArg1, mrFlyFrameFormat.GetName() );
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/undraw.cxx b/sw/source/core/undo/undraw.cxx
new file mode 100644
index 000000000..9eb5502ec
--- /dev/null
+++ b/sw/source/core/undo/undraw.cxx
@@ -0,0 +1,682 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoDraw.hxx>
+
+#include <svx/svdogrp.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdview.hxx>
+#include <osl/diagnose.h>
+
+#include <hintids.hxx>
+#include <fmtanchr.hxx>
+#include <fmtflcnt.hxx>
+#include <txtflcnt.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <dcontact.hxx>
+#include <viewsh.hxx>
+#include <frameformats.hxx>
+#include <textboxhelper.hxx>
+
+struct SwUndoGroupObjImpl
+{
+ SwDrawFrameFormat* pFormat;
+ SdrObject* pObj;
+ SwNodeOffset nNodeIdx;
+};
+
+// Draw-Objecte
+
+void SwDoc::AddDrawUndo( std::unique_ptr<SdrUndoAction> pUndo )
+{
+ if (GetIDocumentUndoRedo().DoesUndo() &&
+ GetIDocumentUndoRedo().DoesDrawUndo())
+ {
+ const SdrMarkList* pMarkList = nullptr;
+ SwViewShell* pSh = getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pSh && pSh->HasDrawView() )
+ pMarkList = &pSh->GetDrawView()->GetMarkedObjectList();
+
+ GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwSdrUndo>(std::move(pUndo), pMarkList, *this) );
+ }
+}
+
+SwSdrUndo::SwSdrUndo( std::unique_ptr<SdrUndoAction> pUndo, const SdrMarkList* pMrkLst, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::DRAWUNDO, &rDoc ), m_pSdrUndo( std::move(pUndo) )
+{
+ if( pMrkLst && pMrkLst->GetMarkCount() )
+ m_pMarkList.reset( new SdrMarkList( *pMrkLst ) );
+}
+
+SwSdrUndo::~SwSdrUndo()
+{
+ m_pSdrUndo.reset();
+ m_pMarkList.reset();
+}
+
+void SwSdrUndo::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pSdrUndo->Undo();
+ rContext.SetSelections(nullptr, m_pMarkList.get());
+}
+
+void SwSdrUndo::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pSdrUndo->Redo();
+ rContext.SetSelections(nullptr, m_pMarkList.get());
+}
+
+OUString SwSdrUndo::GetComment() const
+{
+ return m_pSdrUndo->GetComment();
+}
+
+static void lcl_SaveAnchor( SwFrameFormat* pFormat, SwNodeOffset& rNodePos )
+{
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if (!((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())))
+ return;
+
+ rNodePos = rAnchor.GetContentAnchor()->nNode.GetIndex();
+ sal_Int32 nContentPos = 0;
+
+ if (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())
+ {
+ nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex();
+
+ // destroy TextAttribute
+ SwTextNode *pTextNd = pFormat->GetDoc()->GetNodes()[ rNodePos ]->GetTextNode();
+ OSL_ENSURE( pTextNd, "No text node found!" );
+ SwTextFlyCnt* pAttr = static_cast<SwTextFlyCnt*>(
+ pTextNd->GetTextAttrForCharAt( nContentPos, RES_TXTATR_FLYCNT ));
+ // attribute still in text node, delete
+ if( pAttr && pAttr->GetFlyCnt().GetFrameFormat() == pFormat )
+ {
+ // just set pointer to 0, don't delete
+ const_cast<SwFormatFlyCnt&>(pAttr->GetFlyCnt()).SetFlyFormat();
+ SwIndex aIdx( pTextNd, nContentPos );
+ pTextNd->EraseText( aIdx, 1 );
+ }
+ }
+ else if (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId())
+ {
+ nContentPos = rAnchor.GetContentAnchor()->nContent.GetIndex();
+ }
+
+ pFormat->SetFormatAttr( SwFormatAnchor( rAnchor.GetAnchorId(), nContentPos ) );
+}
+
+static void lcl_RestoreAnchor( SwFrameFormat* pFormat, SwNodeOffset nNodePos )
+{
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ if (!((RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_FLY == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId())))
+ return;
+
+ const sal_Int32 nContentPos = rAnchor.GetPageNum();
+ SwNodes& rNds = pFormat->GetDoc()->GetNodes();
+
+ SwNodeIndex aIdx( rNds, nNodePos );
+ SwPosition aPos( aIdx );
+
+ SwFormatAnchor aTmp( rAnchor.GetAnchorId() );
+ if ((RndStdIds::FLY_AS_CHAR == rAnchor.GetAnchorId()) ||
+ (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()))
+ {
+ aPos.nContent.Assign( aIdx.GetNode().GetContentNode(), nContentPos );
+ }
+ aTmp.SetAnchor( &aPos );
+ RndStdIds nAnchorId = rAnchor.GetAnchorId();
+ pFormat->SetFormatAttr( aTmp );
+
+ if (RndStdIds::FLY_AS_CHAR == nAnchorId)
+ {
+ SwTextNode *pTextNd = aIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "no Text Node" );
+ SwFormatFlyCnt aFormat( pFormat );
+ pTextNd->InsertItem( aFormat, nContentPos, nContentPos );
+ }
+}
+
+SwUndoDrawGroup::SwUndoDrawGroup( sal_uInt16 nCnt, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::DRAWGROUP, &rDoc ), m_nSize( nCnt + 1 ), m_bDeleteFormat( true )
+{
+ m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] );
+}
+
+SwUndoDrawGroup::~SwUndoDrawGroup()
+{
+ if( m_bDeleteFormat )
+ {
+ SwUndoGroupObjImpl* pTmp = m_pObjArray.get() + 1;
+ for( sal_uInt16 n = 1; n < m_nSize; ++n, ++pTmp )
+ delete pTmp->pFormat;
+ }
+ else
+ delete m_pObjArray[0].pFormat;
+}
+
+void SwUndoDrawGroup::UndoImpl(::sw::UndoRedoContext &)
+{
+ m_bDeleteFormat = false;
+
+ // save group object
+ SwDrawFrameFormat* pFormat = m_pObjArray[0].pFormat;
+
+ pFormat->CallSwClientNotify(sw::ContactChangedHint(&m_pObjArray[0].pObj));
+ auto pObj = m_pObjArray[0].pObj;
+ pObj->SetUserCall(nullptr);
+
+ // This will store the textboxes what were owned by this group
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+ if (auto pOldTextBoxNode = pFormat->GetOtherTextBoxFormats())
+ {
+ if (auto pChildren = pObj->getChildrenOfSdrObject())
+ {
+ for (size_t idx = 0; idx < pChildren->GetObjCount(); idx++)
+ {
+ auto pChild = pChildren->GetObj(idx);
+
+ if (auto pTextBox = pOldTextBoxNode->GetTextBox(pChild))
+ vTextBoxes.push_back(std::pair(pChild, pTextBox));
+ }
+ }
+ }
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx );
+ rFlyFormats.push_back( rSave.pFormat );
+
+ pObj = rSave.pObj;
+
+ SwDrawContact *pContact = new SwDrawContact( rSave.pFormat, pObj );
+ pContact->ConnectToLayout();
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( pObj );
+
+ for (auto& rElem : vTextBoxes)
+ {
+ if (rElem.first == pObj)
+ {
+ auto pNewTextBoxNode = std::make_shared<SwTextBoxNode>(SwTextBoxNode(rSave.pFormat));
+ rSave.pFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ pNewTextBoxNode->AddTextBox(rElem.first, rElem.second);
+ rElem.second->SetOtherTextBoxFormats(pNewTextBoxNode);
+ break;
+ }
+ }
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ }
+}
+
+void SwUndoDrawGroup::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_bDeleteFormat = true;
+
+ // remove from array
+ SwDoc* pDoc = m_pObjArray[0].pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+
+ // This will store the textboxes from the ex-group-shapes
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ SdrObject* pObj = rSave.pObj;
+
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+
+ // Save the textboxes
+ if (auto pOldTextBoxNode = rSave.pFormat->GetOtherTextBoxFormats())
+ {
+ if (auto pTextBox = pOldTextBoxNode->GetTextBox(pObj))
+ vTextBoxes.push_back(std::pair(pObj, pTextBox));
+ }
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx );
+
+ rSave.pFormat->RemoveAllUnos();
+
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), rSave.pFormat ));
+ }
+
+ // re-insert group object
+ ::lcl_RestoreAnchor( m_pObjArray[0].pFormat, m_pObjArray[0].nNodeIdx );
+ rFlyFormats.push_back( m_pObjArray[0].pFormat );
+
+ SwDrawContact *pContact = new SwDrawContact( m_pObjArray[0].pFormat, m_pObjArray[0].pObj );
+ // #i26791# - correction: connect object to layout
+ pContact->ConnectToLayout();
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( m_pObjArray[0].pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = m_pObjArray[0].pFormat;
+
+ // Restore the textboxes
+ if (vTextBoxes.size())
+ {
+ auto pNewTextBoxNode = std::make_shared<SwTextBoxNode>(SwTextBoxNode(m_pObjArray[0].pFormat));
+ for (auto& rElem : vTextBoxes)
+ {
+ pNewTextBoxNode->AddTextBox(rElem.first, rElem.second);
+ rElem.second->SetOtherTextBoxFormats(pNewTextBoxNode);
+ }
+ m_pObjArray[0].pFormat->SetOtherTextBoxFormats(pNewTextBoxNode);
+ }
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+}
+
+void SwUndoDrawGroup::AddObj( sal_uInt16 nPos, SwDrawFrameFormat* pFormat, SdrObject* pObj )
+{
+ SwUndoGroupObjImpl& rSave = m_pObjArray[nPos + 1];
+ rSave.pObj = pObj;
+ rSave.pFormat = pFormat;
+ ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+}
+
+void SwUndoDrawGroup::SetGroupFormat( SwDrawFrameFormat* pFormat )
+{
+ m_pObjArray[0].pObj = nullptr;
+ m_pObjArray[0].pFormat = pFormat;
+}
+
+SwUndoDrawUnGroup::SwUndoDrawUnGroup( SdrObjGroup* pObj, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::DRAWUNGROUP, &rDoc ), m_bDeleteFormat( false )
+{
+ m_nSize = o3tl::narrowing<sal_uInt16>(pObj->GetSubList()->GetObjCount()) + 1;
+ m_pObjArray.reset( new SwUndoGroupObjImpl[ m_nSize ] );
+
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ SwDrawFrameFormat* pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+
+ m_pObjArray[0].pObj = pObj;
+ m_pObjArray[0].pFormat = pFormat;
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwFrameFormats& rFlyFormats = *pFormat->GetDoc()->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+}
+
+SwUndoDrawUnGroup::~SwUndoDrawUnGroup()
+{
+ if( m_bDeleteFormat )
+ {
+ SwUndoGroupObjImpl* pTmp = m_pObjArray.get() + 1;
+ for( sal_uInt16 n = 1; n < m_nSize; ++n, ++pTmp )
+ delete pTmp->pFormat;
+ }
+ else
+ delete m_pObjArray[0].pFormat;
+}
+
+void SwUndoDrawUnGroup::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_bDeleteFormat = true;
+
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+
+ // This will store the textboxes what were owned by this group
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+
+ // remove from array
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_SaveAnchor( rSave.pFormat, rSave.nNodeIdx );
+
+ // copy the textboxes for later use to this vector
+ if (auto pTxBxNd = rSave.pFormat->GetOtherTextBoxFormats())
+ {
+ if (auto pGroupObj = m_pObjArray[0].pObj)
+ {
+ if (auto pChildren = pGroupObj->getChildrenOfSdrObject())
+ {
+ for (size_t idx = 0; idx < pChildren->GetObjCount(); idx++)
+ {
+ auto pChild = pChildren->GetObj(idx);
+ if (auto pTextBox = pTxBxNd->GetTextBox(pChild))
+ vTextBoxes.push_back(std::pair(pChild, pTextBox));
+ }
+ }
+ }
+ }
+
+ rSave.pFormat->RemoveAllUnos();
+
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), rSave.pFormat ));
+ }
+
+ // re-insert group object
+ ::lcl_RestoreAnchor( m_pObjArray[0].pFormat, m_pObjArray[0].nNodeIdx );
+ rFlyFormats.push_back( m_pObjArray[0].pFormat );
+
+ SwDrawContact *pContact = new SwDrawContact( m_pObjArray[0].pFormat, m_pObjArray[0].pObj );
+ pContact->ConnectToLayout();
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( m_pObjArray[0].pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = m_pObjArray[0].pFormat;
+
+ // Restore the vector content for the new formats
+ if (vTextBoxes.size())
+ {
+ auto pNewTxBxNd = std::make_shared<SwTextBoxNode>( SwTextBoxNode(m_pObjArray[0].pFormat));
+ for (auto& rElem : vTextBoxes)
+ {
+ pNewTxBxNd->AddTextBox(rElem.first, rElem.second);
+ rElem.second->SetOtherTextBoxFormats(pNewTxBxNd);
+ }
+ m_pObjArray[0].pFormat->SetOtherTextBoxFormats(pNewTxBxNd);
+ }
+
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+}
+
+void SwUndoDrawUnGroup::RedoImpl(::sw::UndoRedoContext &)
+{
+ m_bDeleteFormat = false;
+
+ // save group object
+ SwDrawFrameFormat* pFormat = m_pObjArray[0].pFormat;
+ pFormat->CallSwClientNotify(sw::ContactChangedHint(&(m_pObjArray[0].pObj)));
+ m_pObjArray[0].pObj->SetUserCall( nullptr );
+
+ ::lcl_SaveAnchor( pFormat, m_pObjArray[0].nNodeIdx );
+
+ // Store the textboxes in this vector for later use.
+ std::vector<std::pair<SdrObject*, SwFrameFormat*>> vTextBoxes;
+ if (auto pTextBoxNode = pFormat->GetOtherTextBoxFormats())
+ {
+ auto pMasterObj = m_pObjArray[0].pObj;
+
+ if (auto pObjList = pMasterObj->getChildrenOfSdrObject())
+ for (size_t idx = 0; idx < pObjList->GetObjCount(); idx++)
+ {
+ vTextBoxes.push_back(std::pair(pObjList->GetObj(idx), pTextBoxNode->GetTextBox(pObjList->GetObj(idx))));
+ }
+ }
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ for( sal_uInt16 n = 1; n < m_nSize; ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+
+ ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx );
+ rFlyFormats.push_back( rSave.pFormat );
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+
+ // Restore the textboxes for the restored group shape.
+ for (auto& pElem : vTextBoxes)
+ {
+ if (pElem.first == rSave.pObj)
+ {
+ auto pTmpTxBxNd = std::make_shared<SwTextBoxNode>(SwTextBoxNode(rSave.pFormat));
+ pTmpTxBxNd->AddTextBox(rSave.pObj, pElem.second);
+ pFormat->SetOtherTextBoxFormats(pTmpTxBxNd);
+ pElem.second->SetOtherTextBoxFormats(pTmpTxBxNd);
+ break;
+ }
+ }
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object" );
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ }
+}
+
+void SwUndoDrawUnGroup::AddObj( sal_uInt16 nPos, SwDrawFrameFormat* pFormat )
+{
+ SwUndoGroupObjImpl& rSave = m_pObjArray[ nPos + 1 ];
+ rSave.pFormat = pFormat;
+ rSave.pObj = nullptr;
+}
+
+SwUndoDrawUnGroupConnectToLayout::SwUndoDrawUnGroupConnectToLayout(const SwDoc& rDoc)
+ : SwUndo( SwUndoId::DRAWUNGROUP, &rDoc )
+{
+}
+
+SwUndoDrawUnGroupConnectToLayout::~SwUndoDrawUnGroupConnectToLayout()
+{
+}
+
+void
+SwUndoDrawUnGroupConnectToLayout::UndoImpl(::sw::UndoRedoContext &)
+{
+ for (const std::pair< SwDrawFrameFormat*, SdrObject* > & rPair : m_aDrawFormatsAndObjs)
+ {
+ SdrObject* pObj( rPair.second );
+ SwDrawContact* pDrawContact( dynamic_cast<SwDrawContact*>(pObj->GetUserCall()) );
+ OSL_ENSURE( pDrawContact,
+ "<SwUndoDrawUnGroupConnectToLayout::Undo(..)> -- missing SwDrawContact instance" );
+ if ( pDrawContact )
+ {
+ // deletion of instance <pDrawContact> and thus disconnection from
+ // the Writer layout.
+ pDrawContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+ }
+ }
+}
+
+void
+SwUndoDrawUnGroupConnectToLayout::RedoImpl(::sw::UndoRedoContext &)
+{
+ for (const std::pair< SwDrawFrameFormat*, SdrObject* > & rPair : m_aDrawFormatsAndObjs)
+ {
+ SwDrawFrameFormat* pFormat( rPair.first );
+ SdrObject* pObj( rPair.second );
+ SwDrawContact *pContact = new SwDrawContact( pFormat, pObj );
+ pContact->ConnectToLayout();
+ pContact->MoveObjToVisibleLayer( pObj );
+ }
+}
+
+void SwUndoDrawUnGroupConnectToLayout::AddFormatAndObj( SwDrawFrameFormat* pDrawFrameFormat,
+ SdrObject* pDrawObject )
+{
+ m_aDrawFormatsAndObjs.emplace_back( pDrawFrameFormat, pDrawObject );
+}
+
+SwUndoDrawDelete::SwUndoDrawDelete( sal_uInt16 nCnt, const SwDoc& rDoc )
+ : SwUndo( SwUndoId::DRAWDELETE, &rDoc ), m_bDeleteFormat( true )
+{
+ m_pObjArray.reset( new SwUndoGroupObjImpl[ nCnt ] );
+ m_pMarkList.reset( new SdrMarkList() );
+}
+
+SwUndoDrawDelete::~SwUndoDrawDelete()
+{
+ if( m_bDeleteFormat )
+ {
+ SwUndoGroupObjImpl* pTmp = m_pObjArray.get();
+ for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n, ++pTmp )
+ delete pTmp->pFormat;
+ }
+}
+
+void SwUndoDrawDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_bDeleteFormat = false;
+ SwFrameFormats & rFlyFormats = *rContext.GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+ ::lcl_RestoreAnchor( rSave.pFormat, rSave.nNodeIdx );
+ rFlyFormats.push_back( rSave.pFormat );
+ SdrObject *pObj = rSave.pObj;
+ SwDrawContact *pContact = new SwDrawContact( rSave.pFormat, pObj );
+ pContact->Changed_( *pObj, SdrUserCallType::Inserted, nullptr );
+ // #i45718# - follow-up of #i35635# move object to visible layer
+ pContact->MoveObjToVisibleLayer( pObj );
+
+ SwDrawFrameFormat* pDrawFrameFormat = rSave.pFormat;
+ if (pDrawFrameFormat->GetOtherTextBoxFormats())
+ {
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ SwTextBoxHelper::changeAnchor, pDrawFrameFormat, pObj);
+ }
+
+ // #i45952# - notify that position attributes are already set
+ OSL_ENSURE(pDrawFrameFormat,
+ "<SwUndoDrawGroup::Undo(..)> - wrong type of frame format for drawing object");
+ if (pDrawFrameFormat)
+ pDrawFrameFormat->PosAttrSet();
+ }
+ rContext.SetSelections(nullptr, m_pMarkList.get());
+}
+
+void SwUndoDrawDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_bDeleteFormat = true;
+ SwFrameFormats & rFlyFormats = *rContext.GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < m_pMarkList->GetMarkCount(); ++n )
+ {
+ SwUndoGroupObjImpl& rSave = m_pObjArray[n];
+ SdrObject *pObj = rSave.pObj;
+ SwDrawContact *pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
+ SwDrawFrameFormat *pFormat = static_cast<SwDrawFrameFormat*>(pContact->GetFormat());
+
+ // object will destroy itself
+ pContact->Changed( *pObj, SdrUserCallType::Delete, pObj->GetLastBoundRect() );
+ pObj->SetUserCall( nullptr );
+
+ pFormat->RemoveAllUnos();
+
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+ ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx );
+ }
+}
+
+void SwUndoDrawDelete::AddObj( SwDrawFrameFormat* pFormat,
+ const SdrMark& rMark )
+{
+ SwUndoGroupObjImpl& rSave = m_pObjArray[ m_pMarkList->GetMarkCount() ];
+ rSave.pObj = rMark.GetMarkedSdrObj();
+ rSave.pFormat = pFormat;
+ ::lcl_SaveAnchor( pFormat, rSave.nNodeIdx );
+
+ pFormat->RemoveAllUnos();
+
+ // remove from array
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwFrameFormats& rFlyFormats = *pDoc->GetSpzFrameFormats();
+ rFlyFormats.erase( std::find( rFlyFormats.begin(), rFlyFormats.end(), pFormat ));
+
+ m_pMarkList->InsertEntry( rMark );
+}
+
+void SwUndoDrawDelete::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoDrawDelete"));
+
+ for (size_t i = 0; i < m_pMarkList->GetMarkCount(); ++i)
+ {
+ SwUndoGroupObjImpl& rObj = m_pObjArray[i];
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoGroupObjImpl"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"),
+ BAD_CAST(OString::number(i).getStr()));
+
+ if (rObj.pFormat)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pFormat"));
+ rObj.pFormat->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unfmco.cxx b/sw/source/core/undo/unfmco.cxx
new file mode 100644
index 000000000..7ea43d0d5
--- /dev/null
+++ b/sw/source/core/undo/unfmco.cxx
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+
+SwUndoFormatColl::SwUndoFormatColl( const SwPaM& rRange,
+ const SwFormatColl* pColl,
+ const bool bReset,
+ const bool bResetListAttrs )
+ : SwUndo( SwUndoId::SETFMTCOLL, &rRange.GetDoc() ),
+ SwUndRng( rRange ),
+ mpHistory( new SwHistory ),
+ mbReset( bReset ),
+ mbResetListAttrs( bResetListAttrs )
+{
+ // #i31191#
+ if ( pColl )
+ maFormatName = pColl->GetName();
+}
+
+SwUndoFormatColl::~SwUndoFormatColl()
+{
+}
+
+void SwUndoFormatColl::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ // restore old values
+ mpHistory->TmpRollback(& rContext.GetDoc(), 0);
+ mpHistory->SetTmpEnd( mpHistory->Count() );
+
+ // create cursor for undo range
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoFormatColl::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam = AddUndoRedoPaM(rContext);
+
+ DoSetFormatColl(rContext.GetDoc(), rPam);
+}
+
+void SwUndoFormatColl::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ DoSetFormatColl(rContext.GetDoc(), rContext.GetRepeatPaM());
+}
+
+void SwUndoFormatColl::DoSetFormatColl(SwDoc & rDoc, SwPaM const & rPaM)
+{
+ // Only one TextFrameColl can be applied to a section, thus request only in
+ // this array.
+ SwTextFormatColl * pFormatColl = rDoc.FindTextFormatCollByName(maFormatName);
+ if (pFormatColl)
+ {
+ rDoc.SetTextFormatColl(rPaM, pFormatColl, mbReset, mbResetListAttrs);
+ }
+}
+
+SwRewriter SwUndoFormatColl::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ aResult.AddRule(UndoArg1, maFormatName );
+
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unins.cxx b/sw/source/core/undo/unins.cxx
new file mode 100644
index 000000000..5c4c8c947
--- /dev/null
+++ b/sw/source/core/undo/unins.cxx
@@ -0,0 +1,1049 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoInsert.hxx>
+
+#include <hintids.hxx>
+#include <unotools/charclass.hxx>
+#include <editeng/keepitem.hxx>
+#include <svx/svdobj.hxx>
+#include <osl/diagnose.h>
+
+#include <fmtcntnt.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <UndoDelete.hxx>
+#include <UndoAttribute.hxx>
+#include <rolbck.hxx>
+#include <ndgrf.hxx>
+#include <ndole.hxx>
+#include <grfatr.hxx>
+#include <cntfrm.hxx>
+#include <flyfrm.hxx>
+#include <swcrsr.hxx>
+#include <swtable.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <acorrect.hxx>
+
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+
+// INSERT
+
+std::optional<OUString> SwUndoInsert::GetTextFromDoc() const
+{
+ std::optional<OUString> aResult;
+
+ SwNodeIndex aNd( m_pDoc->GetNodes(), m_nNode);
+ SwContentNode* pCNd = aNd.GetNode().GetContentNode();
+
+ if( pCNd->IsTextNode() )
+ {
+ OUString sText = pCNd->GetTextNode()->GetText();
+
+ sal_Int32 nStart = m_nContent-m_nLen;
+ sal_Int32 nLength = m_nLen;
+
+ if (nStart < 0)
+ {
+ nLength += nStart;
+ nStart = 0;
+ }
+
+ aResult = sText.copy(nStart, nLength);
+ }
+
+ return aResult;
+}
+
+void SwUndoInsert::Init(const SwNodeIndex & rNd)
+{
+ // consider Redline
+ m_pDoc = &rNd.GetNode().GetDoc();
+ if( m_pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlData.reset( new SwRedlineData( RedlineType::Insert,
+ m_pDoc->getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ maUndoText = GetTextFromDoc();
+
+ m_bCacheComment = false;
+}
+
+SwUndoInsert::SwUndoInsert( const SwNodeIndex& rNd, sal_Int32 nCnt,
+ sal_Int32 nL,
+ const SwInsertFlags nInsertFlags,
+ bool bWDelim )
+ : SwUndo(SwUndoId::TYPING, &rNd.GetNode().GetDoc()),
+ m_nNode( rNd.GetIndex() ), m_nContent(nCnt), m_nLen(nL),
+ m_bIsWordDelim( bWDelim ), m_bIsAppend( false )
+ , m_bWithRsid(false)
+ , m_nInsertFlags(nInsertFlags)
+{
+ Init(rNd);
+}
+
+SwUndoInsert::SwUndoInsert( const SwNodeIndex& rNd )
+ : SwUndo(SwUndoId::SPLITNODE, &rNd.GetNode().GetDoc()),
+ m_nNode( rNd.GetIndex() ), m_nContent(0), m_nLen(1),
+ m_bIsWordDelim( false ), m_bIsAppend( true )
+ , m_bWithRsid(false)
+ , m_nInsertFlags(SwInsertFlags::EMPTYEXPAND)
+{
+ Init(rNd);
+}
+
+// Check if the next Insert can be combined with the current one. If so
+// change the length and InsPos. As a result, SwDoc::Inser will not add a
+// new object into the Undo list.
+
+bool SwUndoInsert::CanGrouping( sal_Unicode cIns )
+{
+ if( !m_bIsAppend && m_bIsWordDelim ==
+ !GetAppCharClass().isLetterNumeric( OUString( cIns )) )
+ {
+ m_nLen++;
+ m_nContent++;
+
+ if (maUndoText)
+ (*maUndoText) += OUStringChar(cIns);
+
+ return true;
+ }
+ return false;
+}
+
+bool SwUndoInsert::CanGrouping( const SwPosition& rPos )
+{
+ bool bRet = false;
+ if( m_nNode == rPos.nNode.GetIndex() &&
+ m_nContent == rPos.nContent.GetIndex() )
+ {
+ // consider Redline
+ SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
+ if( ( ~RedlineFlags::ShowMask & rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ) ==
+ ( ~RedlineFlags::ShowMask & GetRedlineFlags() ) )
+ {
+ bRet = true;
+
+ // then there is or was still an active Redline:
+ // Check if there is another Redline at the InsPosition. If the
+ // same exists only once, it can be combined.
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ if( !rTable.empty() )
+ {
+ SwRedlineData aRData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() );
+ const SwIndexReg* pIReg = rPos.nContent.GetIdxReg();
+ for(SwRangeRedline* pRedl : rTable)
+ {
+ SwIndex* pIdx = &pRedl->End()->nContent;
+ if( pIReg == pIdx->GetIdxReg() &&
+ m_nContent == pIdx->GetIndex() )
+ {
+ if( !pRedl->HasMark() || !m_pRedlData ||
+ *pRedl != *m_pRedlData || *pRedl != aRData )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+SwUndoInsert::~SwUndoInsert()
+{
+ if (m_pUndoNodeIndex) // delete the section from UndoNodes array
+ {
+ // Insert saves the content in IconSection
+ SwNodes& rUNds = m_pUndoNodeIndex->GetNodes();
+ rUNds.Delete(*m_pUndoNodeIndex,
+ rUNds.GetEndOfExtras().GetIndex() - m_pUndoNodeIndex->GetIndex());
+ m_pUndoNodeIndex.reset();
+ }
+ else // the inserted text
+ {
+ maText.reset();
+ }
+ m_pRedlData.reset();
+}
+
+void SwUndoInsert::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pTmpDoc = & rContext.GetDoc();
+ SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ if( m_bIsAppend )
+ {
+ pPam->GetPoint()->nNode = m_nNode;
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), 0 );
+ pPam->SetMark();
+ pPam->Move( fnMoveBackward );
+ pPam->Exchange();
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( *pPam, true, RedlineType::Any );
+ }
+ pPam->DeleteMark();
+ pTmpDoc->getIDocumentContentOperations().DelFullPara( *pPam );
+ pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), 0 );
+ }
+ else
+ {
+ SwNodeOffset nNd = m_nNode;
+ sal_Int32 nCnt = m_nContent;
+ if( m_nLen )
+ {
+ SwNodeIndex aNd( pTmpDoc->GetNodes(), m_nNode);
+ SwContentNode* pCNd = aNd.GetNode().GetContentNode();
+ SwPaM aPaM( *pCNd, m_nContent );
+
+ aPaM.SetMark();
+
+ SwTextNode * const pTextNode( pCNd->GetTextNode() );
+ if ( pTextNode )
+ {
+ aPaM.GetPoint()->nContent -= m_nLen;
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any );
+ if (m_bWithRsid)
+ {
+ // RSID was added: remove any CHARFMT/AUTOFMT that may be
+ // set on the deleted text; EraseText will leave empty
+ // ones behind otherwise
+ pTextNode->DeleteAttributes(RES_TXTATR_AUTOFMT,
+ aPaM.GetPoint()->nContent.GetIndex(),
+ aPaM.GetMark()->nContent.GetIndex());
+ pTextNode->DeleteAttributes(RES_TXTATR_CHARFMT,
+ aPaM.GetPoint()->nContent.GetIndex(),
+ aPaM.GetMark()->nContent.GetIndex());
+ }
+ RemoveIdxFromRange( aPaM, false );
+ maText = pTextNode->GetText().copy(m_nContent-m_nLen, m_nLen);
+ pTextNode->EraseText( aPaM.GetPoint()->nContent, m_nLen );
+ }
+ else // otherwise Graphics/OLE/Text/...
+ {
+ aPaM.Move(fnMoveBackward);
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ pTmpDoc->getIDocumentRedlineAccess().DeleteRedline( aPaM, true, RedlineType::Any );
+ RemoveIdxFromRange( aPaM, false );
+ }
+
+ nNd = aPaM.GetPoint()->nNode.GetIndex();
+ nCnt = aPaM.GetPoint()->nContent.GetIndex();
+
+ if (!maText)
+ {
+ m_pUndoNodeIndex.reset(
+ new SwNodeIndex(m_pDoc->GetNodes().GetEndOfContent()));
+ MoveToUndoNds(aPaM, m_pUndoNodeIndex.get());
+ }
+ m_nNode = aPaM.GetPoint()->nNode.GetIndex();
+ m_nContent = aPaM.GetPoint()->nContent.GetIndex();
+ }
+
+ // set cursor to Undo range
+ pPam->DeleteMark();
+
+ pPam->GetPoint()->nNode = nNd;
+ pPam->GetPoint()->nContent.Assign(
+ pPam->GetPoint()->nNode.GetNode().GetContentNode(), nCnt );
+ }
+
+ maUndoText.reset();
+}
+
+void SwUndoInsert::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pTmpDoc = & rContext.GetDoc();
+ SwCursor *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+
+ if( m_bIsAppend )
+ {
+ pPam->GetPoint()->nNode = m_nNode - 1;
+ pTmpDoc->getIDocumentContentOperations().AppendTextNode( *pPam->GetPoint() );
+
+ pPam->SetMark();
+ pPam->Move( fnMoveBackward );
+ pPam->Exchange();
+
+ if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+ pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, *pPam ), true);
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ pTmpDoc->getIDocumentRedlineAccess().SplitRedline( *pPam );
+
+ pPam->DeleteMark();
+ }
+ else
+ {
+ pPam->GetPoint()->nNode = m_nNode;
+ SwContentNode *const pCNd =
+ pPam->GetPoint()->nNode.GetNode().GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, m_nContent );
+
+ if( m_nLen )
+ {
+ const bool bMvBkwrd = MovePtBackward( *pPam );
+
+ if (maText)
+ {
+ SwTextNode *const pTextNode = pCNd->GetTextNode();
+ OSL_ENSURE( pTextNode, "where is my textnode ?" );
+ OUString const ins(
+ pTextNode->InsertText( *maText, pPam->GetMark()->nContent,
+ m_nInsertFlags) );
+ assert(ins.getLength() == maText->getLength()); // must succeed
+ maText.reset();
+ if (m_bWithRsid) // re-insert RSID
+ {
+ SwPaM pam(*pPam->GetMark(), nullptr); // mark -> point
+ pTmpDoc->UpdateRsid(pam, ins.getLength());
+ }
+ }
+ else
+ {
+ // re-insert content again (first detach m_pUndoNodeIndex!)
+ SwNodeOffset const nMvNd = m_pUndoNodeIndex->GetIndex();
+ m_pUndoNodeIndex.reset();
+ MoveFromUndoNds(*pTmpDoc, nMvNd, *pPam->GetMark());
+ }
+ m_nNode = pPam->GetMark()->nNode.GetIndex();
+ m_nContent = pPam->GetMark()->nContent.GetIndex();
+
+ MovePtForward( *pPam, bMvBkwrd );
+ pPam->Exchange();
+ if( m_pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = pTmpDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+ pTmpDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData,
+ *pPam ), true);
+ pTmpDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !pTmpDoc->getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ pTmpDoc->getIDocumentRedlineAccess().SplitRedline(*pPam);
+ }
+ }
+
+ maUndoText = GetTextFromDoc();
+}
+
+void SwUndoInsert::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ if( !m_nLen )
+ return;
+
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aNd( rDoc.GetNodes(), m_nNode );
+ SwContentNode* pCNd = aNd.GetNode().GetContentNode();
+
+ if( !m_bIsAppend && 1 == m_nLen ) // >1 than always Text, otherwise Graphics/OLE/Text/...
+ {
+ SwPaM aPaM( *pCNd, m_nContent );
+ aPaM.SetMark();
+ aPaM.Move(fnMoveBackward);
+ pCNd = aPaM.GetContentNode();
+ }
+
+// What happens with the possible selected range ???
+
+ switch( pCNd->GetNodeType() )
+ {
+ case SwNodeType::Text:
+ if( m_bIsAppend )
+ {
+ rDoc.getIDocumentContentOperations().AppendTextNode( *rContext.GetRepeatPaM().GetPoint() );
+ }
+ else
+ {
+ OUString const aText( pCNd->GetTextNode()->GetText() );
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().InsertString( rContext.GetRepeatPaM(),
+ aText.copy(m_nContent - m_nLen, m_nLen) );
+ }
+ break;
+ case SwNodeType::Grf:
+ {
+ SwGrfNode* pGrfNd = static_cast<SwGrfNode*>(pCNd);
+ OUString sFile;
+ OUString sFilter;
+ if( pGrfNd->IsGrfLink() )
+ pGrfNd->GetFileFilterNms( &sFile, &sFilter );
+
+ rDoc.getIDocumentContentOperations().InsertGraphic(
+ rContext.GetRepeatPaM(), sFile, sFilter,
+ &pGrfNd->GetGrf(),
+ nullptr/* Graphics collection*/, nullptr, nullptr );
+ }
+ break;
+
+ case SwNodeType::Ole:
+ {
+ // StarView does not yet provide an option to copy a StarOBJ
+ SwOLEObj& rSwOLE = static_cast<SwOLENode*>(pCNd)->GetOLEObj();
+
+ // temporary storage until object is inserted
+ // TODO/MBA: seems that here a physical copy is done - not as in drawing layer! Testing!
+ // TODO/LATER: Copying through the container would copy the replacement image as well
+ comphelper::EmbeddedObjectContainer aCnt;
+ OUString aName = aCnt.CreateUniqueObjectName();
+ if (aCnt.StoreEmbeddedObject(rSwOLE.GetOleRef(), aName, true, OUString(), OUString()))
+ {
+ uno::Reference < embed::XEmbeddedObject > aNew = aCnt.GetEmbeddedObject( aName );
+ rDoc.getIDocumentContentOperations().InsertEmbObject(
+ rContext.GetRepeatPaM(),
+ svt::EmbeddedObjectRef( aNew,
+ static_cast<SwOLENode*>(pCNd)->GetAspect() ),
+ nullptr );
+ }
+
+ break;
+ }
+
+ default: break;
+ }
+}
+
+SwRewriter SwUndoInsert::GetRewriter() const
+{
+ SwRewriter aResult;
+ std::optional<OUString> aStr;
+ bool bDone = false;
+
+ if (maText)
+ aStr = maText;
+ else if (maUndoText)
+ aStr = maUndoText;
+
+ if (aStr)
+ {
+ OUString aString = ShortenString(DenoteSpecialCharacters(*aStr),
+ nUndoStringLength,
+ SwResId(STR_LDOTS));
+
+ aResult.AddRule(UndoArg1, aString);
+
+ bDone = true;
+ }
+
+ if ( ! bDone )
+ {
+ aResult.AddRule(UndoArg1, "??");
+ }
+
+ return aResult;
+}
+
+bool SwUndoInsert::IsIndependent(const SwUndoInsert& rOther) const
+{
+ return m_nNode != rOther.m_nNode;
+}
+
+class SwUndoReplace::Impl
+ : private SwUndoSaveContent
+{
+ OUString m_sOld;
+ OUString m_sIns;
+ SwNodeOffset m_nSttNd, m_nEndNd, m_nOffset;
+ sal_Int32 m_nSttCnt, m_nEndCnt, m_nSetPos, m_nSelEnd;
+ bool m_bSplitNext : 1;
+ bool m_bRegExp : 1;
+ // metadata references for paragraph and following para (if m_bSplitNext)
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;
+
+public:
+ Impl(SwPaM const& rPam, OUString const& rIns, bool const bRegExp);
+
+ void UndoImpl( ::sw::UndoRedoContext & );
+ void RedoImpl( ::sw::UndoRedoContext & );
+
+ void SetEnd(SwPaM const& rPam);
+
+ OUString const& GetOld() const { return m_sOld; }
+ OUString const& GetIns() const { return m_sIns; }
+};
+
+SwUndoReplace::SwUndoReplace(SwPaM const& rPam,
+ OUString const& rIns, bool const bRegExp)
+ : SwUndo( SwUndoId::REPLACE, &rPam.GetDoc() )
+ , m_pImpl(std::make_unique<Impl>(rPam, rIns, bRegExp))
+{
+}
+
+SwUndoReplace::~SwUndoReplace()
+{
+}
+
+void SwUndoReplace::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pImpl->UndoImpl(rContext);
+}
+
+void SwUndoReplace::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pImpl->RedoImpl(rContext);
+}
+
+SwRewriter
+MakeUndoReplaceRewriter(sal_uLong const occurrences,
+ OUString const& sOld, OUString const& sNew)
+{
+ SwRewriter aResult;
+
+ if (1 < occurrences)
+ {
+ aResult.AddRule(UndoArg1, OUString::number(occurrences));
+ aResult.AddRule(UndoArg2, SwResId(STR_OCCURRENCES_OF));
+
+ OUString aTmpStr = SwResId(STR_START_QUOTE);
+ aTmpStr += ShortenString(sOld, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg3, aTmpStr);
+ }
+ else if (1 == occurrences)
+ {
+ {
+ OUString aTmpStr = SwResId(STR_START_QUOTE);
+ // #i33488 #
+ aTmpStr += ShortenString(sOld, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg1, aTmpStr);
+ }
+
+ aResult.AddRule(UndoArg2, SwResId(STR_YIELDS));
+
+ {
+ OUString aTmpStr = SwResId(STR_START_QUOTE);
+ // #i33488 #
+ aTmpStr += ShortenString(sNew, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ aResult.AddRule(UndoArg3, aTmpStr);
+ }
+ }
+
+ return aResult;
+}
+
+SwRewriter SwUndoReplace::GetRewriter() const
+{
+ return MakeUndoReplaceRewriter(1, m_pImpl->GetOld(), m_pImpl->GetIns());
+}
+
+void SwUndoReplace::SetEnd(SwPaM const& rPam)
+{
+ m_pImpl->SetEnd(rPam);
+}
+
+SwUndoReplace::Impl::Impl(
+ SwPaM const& rPam, OUString const& rIns, bool const bRegExp)
+ : m_sIns( rIns )
+ , m_nOffset( 0 )
+ , m_bRegExp(bRegExp)
+{
+
+ const SwPosition * pStt( rPam.Start() );
+ const SwPosition * pEnd( rPam.End() );
+
+ m_nSttNd = m_nEndNd = pStt->nNode.GetIndex();
+ m_nSttCnt = pStt->nContent.GetIndex();
+ m_nSelEnd = m_nEndCnt = pEnd->nContent.GetIndex();
+
+ m_bSplitNext = m_nSttNd != pEnd->nNode.GetIndex();
+
+ SwTextNode* pNd = pStt->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+
+ m_pHistory.reset( new SwHistory );
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
+
+ m_nSetPos = m_pHistory->Count();
+
+ SwNodeOffset nNewPos = pStt->nNode.GetIndex();
+ m_nOffset = m_nSttNd - nNewPos;
+
+ if ( pNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pNd->GetpSwpHints(), nNewPos, 0,
+ pNd->GetText().getLength(), true );
+ }
+
+ if ( m_bSplitNext )
+ {
+ if( pNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNewPos );
+ m_pHistory->Add( pNd->GetTextColl(), nNewPos, SwNodeType::Text );
+
+ SwTextNode* pNext = pEnd->nNode.GetNode().GetTextNode();
+ SwNodeOffset nTmp = pNext->GetIndex();
+ m_pHistory->CopyAttr( pNext->GetpSwpHints(), nTmp, 0,
+ pNext->GetText().getLength(), true );
+ if( pNext->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pNext->GetpSwAttrSet(), nTmp );
+ m_pHistory->Add( pNext->GetTextColl(),nTmp, SwNodeType::Text );
+ // METADATA: store
+ m_pMetadataUndoStart = pNd ->CreateUndo();
+ m_pMetadataUndoEnd = pNext->CreateUndo();
+ }
+
+ if( !m_pHistory->Count() )
+ {
+ m_pHistory.reset();
+ }
+
+ const sal_Int32 nECnt = m_bSplitNext ? pNd->GetText().getLength()
+ : pEnd->nContent.GetIndex();
+ m_sOld = pNd->GetText().copy( m_nSttCnt, nECnt - m_nSttCnt );
+}
+
+void SwUndoReplace::Impl::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwCursor & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ rPam.DeleteMark();
+
+ SwTextNode* pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+
+ SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord();
+ if( pACEWord )
+ {
+ if ((1 == m_sIns.getLength()) && (1 == m_sOld.getLength()))
+ {
+ SwPosition aPos( *pNd ); aPos.nContent.Assign( pNd, m_nSttCnt );
+ pACEWord->CheckChar( aPos, m_sOld[ 0 ] );
+ }
+ pDoc->SetAutoCorrExceptWord( nullptr );
+ }
+
+ // don't look at m_sIns for deletion, maybe it was not completely inserted
+ {
+ rPam.GetPoint()->nNode = *pNd;
+ rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt );
+ rPam.SetMark();
+ rPam.GetPoint()->nNode = m_nSttNd - m_nOffset;
+ rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(), m_nSttNd == m_nEndNd ? m_nEndCnt : pNd->Len());
+
+ // replace only in start node, without regex
+ bool const ret = pDoc->getIDocumentContentOperations().ReplaceRange(rPam, m_sOld, false);
+ assert(ret); (void)ret;
+ if (m_nSttNd != m_nEndNd)
+ { // in case of regex inserting paragraph breaks, join nodes...
+ assert(rPam.GetMark()->nContent == rPam.GetMark()->nNode.GetNode().GetTextNode()->Len());
+ rPam.GetPoint()->nNode = m_nEndNd - m_nOffset;
+ rPam.GetPoint()->nContent.Assign(rPam.GetContentNode(true), m_nEndCnt);
+ pDoc->getIDocumentContentOperations().DeleteAndJoin(rPam);
+ }
+ rPam.DeleteMark();
+ pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+ }
+
+ if( m_bSplitNext )
+ {
+ assert(m_nSttCnt + m_sOld.getLength() <= pNd->Len());
+ SwPosition aPos(*pNd, m_nSttCnt + m_sOld.getLength());
+ pDoc->getIDocumentContentOperations().SplitNode( aPos, false );
+ pNd->RestoreMetadata(m_pMetadataUndoEnd);
+ pNd = pDoc->GetNodes()[ m_nSttNd - m_nOffset ]->GetTextNode();
+ // METADATA: restore
+ pNd->RestoreMetadata(m_pMetadataUndoStart);
+ }
+
+ if( m_pHistory )
+ {
+ if( pNd->GetpSwpHints() )
+ pNd->ClearSwpHintsArr( true );
+
+ m_pHistory->TmpRollback( pDoc, m_nSetPos, false );
+ if ( m_nSetPos ) // there were footnotes/FlyFrames
+ {
+ // are there others than these?
+ if( m_nSetPos < m_pHistory->Count() )
+ {
+ // than save those attributes as well
+ SwHistory aHstr;
+ aHstr.Move( 0, m_pHistory.get(), m_nSetPos );
+ m_pHistory->Rollback( pDoc );
+ m_pHistory->Move( 0, &aHstr );
+ }
+ else
+ {
+ m_pHistory->Rollback( pDoc );
+ m_pHistory.reset();
+ }
+ }
+ }
+
+ rPam.GetPoint()->nNode = m_nSttNd;
+ rPam.GetPoint()->nContent.Assign(rPam.GetPoint()->nNode.GetNode().GetTextNode(), m_nSttCnt);
+}
+
+void SwUndoReplace::Impl::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwCursor & rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nSttNd;
+
+ SwTextNode* pNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pNd, "Dude, where's my TextNode?" );
+ rPam.GetPoint()->nContent.Assign( pNd, m_nSttCnt );
+ rPam.SetMark();
+ if( m_bSplitNext )
+ {
+ rPam.GetPoint()->nNode = m_nSttNd + 1;
+ pNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ }
+ rPam.GetPoint()->nContent.Assign( pNd, m_nSelEnd );
+
+ if( m_pHistory )
+ {
+ auto xSave = std::make_unique<SwHistory>();
+ std::swap(m_pHistory, xSave);
+
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
+ m_nSetPos = m_pHistory->Count();
+
+ std::swap(xSave, m_pHistory);
+ m_pHistory->Move(0, xSave.get());
+ }
+ else
+ {
+ m_pHistory.reset( new SwHistory );
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(), DelContentType::AllMask | DelContentType::Replace);
+ m_nSetPos = m_pHistory->Count();
+ if( !m_nSetPos )
+ {
+ m_pHistory.reset();
+ }
+ }
+
+ rDoc.getIDocumentContentOperations().ReplaceRange( rPam, m_sIns, m_bRegExp );
+ rPam.DeleteMark();
+}
+
+void SwUndoReplace::Impl::SetEnd(SwPaM const& rPam)
+{
+ const SwPosition* pEnd = rPam.End();
+ m_nEndNd = m_nOffset + pEnd->nNode.GetIndex();
+ m_nEndCnt = pEnd->nContent.GetIndex();
+}
+
+SwUndoReRead::SwUndoReRead( const SwPaM& rPam, const SwGrfNode& rGrfNd )
+ : SwUndo( SwUndoId::REREAD, &rPam.GetDoc() ), mnPosition( rPam.GetPoint()->nNode.GetIndex() )
+{
+ SaveGraphicData( rGrfNd );
+}
+
+SwUndoReRead::~SwUndoReRead()
+{
+}
+
+void SwUndoReRead::SetAndSave(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwGrfNode* pGrfNd = rDoc.GetNodes()[ mnPosition ]->GetGrfNode();
+
+ if( !pGrfNd )
+ return ;
+
+ // cache the old values
+ std::unique_ptr<Graphic> pOldGrf( mpGraphic ? new Graphic(*mpGraphic) : nullptr);
+ std::optional<OUString> aOldNm = maNm;
+ MirrorGraph nOldMirr = mnMirror;
+ // since all of them are cleared/modified by SaveGraphicData:
+ SaveGraphicData( *pGrfNd );
+
+ if( aOldNm )
+ {
+ pGrfNd->ReRead( *aOldNm, maFltr ? *maFltr : OUString() );
+ }
+ else
+ {
+ pGrfNd->ReRead( OUString(), OUString(), pOldGrf.get() );
+ }
+
+ if( MirrorGraph::Dont != nOldMirr )
+ pGrfNd->SetAttr( SwMirrorGrf() );
+
+ rContext.SetSelections(pGrfNd->GetFlyFormat(), nullptr);
+}
+
+void SwUndoReRead::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetAndSave(rContext);
+}
+
+void SwUndoReRead::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SetAndSave(rContext);
+}
+
+void SwUndoReRead::SaveGraphicData( const SwGrfNode& rGrfNd )
+{
+ if( rGrfNd.IsGrfLink() )
+ {
+ maNm = OUString();
+ maFltr = OUString();
+ rGrfNd.GetFileFilterNms(&*maNm, &*maFltr);
+ mpGraphic.reset();
+ }
+ else
+ {
+ mpGraphic.reset( new Graphic( rGrfNd.GetGrf(true) ) );
+ maNm.reset();
+ maFltr.reset();
+ }
+ mnMirror = rGrfNd.GetSwAttrSet().GetMirrorGrf().GetValue();
+}
+
+SwUndoInsertLabel::SwUndoInsertLabel( const SwLabelType eTyp,
+ const OUString &rText,
+ const OUString& rSeparator,
+ const OUString& rNumberSeparator,
+ const bool bBef,
+ const sal_uInt16 nInitId,
+ const OUString& rCharacterStyle,
+ const bool bCpyBorder,
+ const SwDoc* pDoc )
+ : SwUndo( SwUndoId::INSERTLABEL, pDoc ),
+ m_sText( rText ),
+ m_sSeparator( rSeparator ),
+ m_sNumberSeparator( rNumberSeparator ),//#i61007# order of captions
+ m_sCharacterStyle( rCharacterStyle ),
+ m_nFieldId( nInitId ),
+ m_eType( eTyp ),
+ m_nLayerId( 0 ),
+ m_bBefore( bBef ),
+ m_bCopyBorder( bCpyBorder )
+{
+ m_bUndoKeep = false;
+ OBJECT.pUndoFly = nullptr;
+ OBJECT.pUndoAttr = nullptr;
+}
+
+SwUndoInsertLabel::~SwUndoInsertLabel()
+{
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ delete OBJECT.pUndoFly;
+ delete OBJECT.pUndoAttr;
+ }
+ else
+ delete NODE.pUndoInsNd;
+}
+
+void SwUndoInsertLabel::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" );
+ SwFrameFormat* pFormat;
+ SdrObject *pSdrObj = nullptr;
+ if( OBJECT.pUndoAttr &&
+ nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) &&
+ ( SwLabelType::Draw != m_eType ||
+ nullptr != (pSdrObj = pFormat->FindSdrObject()) ) )
+ {
+ OBJECT.pUndoAttr->UndoImpl(rContext);
+ OBJECT.pUndoFly->UndoImpl(rContext);
+ if( SwLabelType::Draw == m_eType )
+ {
+ pSdrObj->SetLayer( m_nLayerId );
+ }
+ }
+ }
+ else if( NODE.nNode )
+ {
+ if ( m_eType == SwLabelType::Table && m_bUndoKeep )
+ {
+ SwTableNode *pNd = rDoc.GetNodes()[
+ rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode();
+ if ( pNd )
+ pNd->GetTable().GetFrameFormat()->ResetFormatAttr( RES_KEEP );
+ }
+ SwPaM aPam( rDoc.GetNodes().GetEndOfContent() );
+ aPam.GetPoint()->nNode = NODE.nNode;
+ aPam.SetMark();
+ aPam.GetPoint()->nNode = NODE.nNode + 1;
+ NODE.pUndoInsNd = new SwUndoDelete(aPam, SwDeleteFlags::Default, true);
+ }
+}
+
+void SwUndoInsertLabel::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ OSL_ENSURE( OBJECT.pUndoAttr && OBJECT.pUndoFly, "Pointer not initialized" );
+ SwFrameFormat* pFormat;
+ SdrObject *pSdrObj = nullptr;
+ if( OBJECT.pUndoAttr &&
+ nullptr != (pFormat = static_cast<SwFrameFormat*>(OBJECT.pUndoAttr->GetFormat( rDoc ))) &&
+ ( SwLabelType::Draw != m_eType ||
+ nullptr != (pSdrObj = pFormat->FindSdrObject()) ) )
+ {
+ OBJECT.pUndoFly->RedoImpl(rContext);
+ OBJECT.pUndoAttr->RedoImpl(rContext);
+ if( SwLabelType::Draw == m_eType )
+ {
+ pSdrObj->SetLayer( m_nLayerId );
+ if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetHellId() )
+ pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetHeavenId() );
+ // OD 02.07.2003 #108784#
+ else if( pSdrObj->GetLayer() == rDoc.getIDocumentDrawModelAccess().GetInvisibleHellId() )
+ pSdrObj->SetLayer( rDoc.getIDocumentDrawModelAccess().GetInvisibleHeavenId() );
+ }
+ }
+ }
+ else if( NODE.pUndoInsNd )
+ {
+ if ( m_eType == SwLabelType::Table && m_bUndoKeep )
+ {
+ SwTableNode *pNd = rDoc.GetNodes()[
+ rDoc.GetNodes()[NODE.nNode-1]->StartOfSectionIndex()]->GetTableNode();
+ if ( pNd )
+ pNd->GetTable().GetFrameFormat()->SetFormatAttr( SvxFormatKeepItem(true, RES_KEEP) );
+ }
+ NODE.pUndoInsNd->UndoImpl(rContext);
+ delete NODE.pUndoInsNd;
+ NODE.pUndoInsNd = nullptr;
+ }
+}
+
+void SwUndoInsertLabel::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ const SwPosition& rPos = *rContext.GetRepeatPaM().GetPoint();
+
+ SwNodeOffset nIdx(0);
+
+ SwContentNode* pCNd = rPos.nNode.GetNode().GetContentNode();
+ if( pCNd )
+ switch( m_eType )
+ {
+ case SwLabelType::Table:
+ {
+ const SwTableNode* pTNd = pCNd->FindTableNode();
+ if( pTNd )
+ nIdx = pTNd->GetIndex();
+ }
+ break;
+
+ case SwLabelType::Fly:
+ case SwLabelType::Object:
+ {
+ SwFlyFrame* pFly;
+ SwContentFrame *pCnt = pCNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() );
+ if( pCnt && nullptr != ( pFly = pCnt->FindFlyFrame() ) )
+ nIdx = pFly->GetFormat()->GetContent().GetContentIdx()->GetIndex();
+ }
+ break;
+ case SwLabelType::Draw:
+ break;
+ }
+
+ if( nIdx )
+ {
+ rDoc.InsertLabel( m_eType, m_sText, m_sSeparator, m_sNumberSeparator, m_bBefore,
+ m_nFieldId, nIdx, m_sCharacterStyle, m_bCopyBorder );
+ }
+}
+
+SwRewriter SwUndoInsertLabel::GetRewriter() const
+{
+ return CreateRewriter(m_sText);
+}
+
+SwRewriter SwUndoInsertLabel::CreateRewriter(const OUString &rStr)
+{
+ SwRewriter aRewriter;
+
+ OUString aTmpStr;
+
+ if (!rStr.isEmpty())
+ {
+ aTmpStr += SwResId(STR_START_QUOTE);
+ aTmpStr += ShortenString(rStr, nUndoStringLength,
+ SwResId(STR_LDOTS));
+ aTmpStr += SwResId(STR_END_QUOTE);
+ }
+
+ aRewriter.AddRule(UndoArg1, aTmpStr);
+
+ return aRewriter;
+}
+
+void SwUndoInsertLabel::SetFlys( SwFrameFormat& rOldFly, SfxItemSet const & rChgSet,
+ SwFrameFormat& rNewFly )
+{
+ if( SwLabelType::Object == m_eType || SwLabelType::Draw == m_eType )
+ {
+ SwUndoFormatAttrHelper aTmp( rOldFly, false );
+ rOldFly.SetFormatAttr( rChgSet );
+ if ( aTmp.GetUndo() )
+ {
+ OBJECT.pUndoAttr = aTmp.ReleaseUndo().release();
+ }
+ OBJECT.pUndoFly = new SwUndoInsLayFormat( &rNewFly, SwNodeOffset(0), 0 );
+ }
+}
+
+void SwUndoInsertLabel::SetDrawObj( SdrLayerID nLId )
+{
+ if( SwLabelType::Draw == m_eType )
+ {
+ m_nLayerId = nLId;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unmove.cxx b/sw/source/core/undo/unmove.cxx
new file mode 100644
index 000000000..b727e565d
--- /dev/null
+++ b/sw/source/core/undo/unmove.cxx
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoSplitMove.hxx>
+#include <doc.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+
+// MOVE
+SwUndoMove::SwUndoMove( const SwPaM& rRange, const SwPosition& rMvPos )
+ : SwUndo( SwUndoId::MOVE, &rRange.GetDoc() )
+ , SwUndRng( rRange )
+ , m_nDestStartNode(0)
+ , m_nDestEndNode(0)
+ , m_nInsPosNode(0)
+ , m_nMoveDestNode(rMvPos.nNode.GetIndex())
+ , m_nDestStartContent(0)
+ , m_nDestEndContent(0)
+ , m_nInsPosContent(0)
+ , m_nMoveDestContent(rMvPos.nContent.GetIndex())
+ , m_bJoinNext(false)
+ , m_bMoveRange(false)
+ , m_bMoveRedlines(false)
+{
+ // get StartNode from footnotes before delete!
+ SwDoc& rDoc = rRange.GetDoc();
+ SwTextNode* pTextNd = rDoc.GetNodes()[ m_nSttNode ]->GetTextNode();
+ SwTextNode* pEndTextNd = rDoc.GetNodes()[ m_nEndNode ]->GetTextNode();
+
+ m_pHistory.reset( new SwHistory );
+
+ if( pTextNd )
+ {
+ m_pHistory->Add( pTextNd->GetTextColl(), m_nSttNode, SwNodeType::Text );
+ if ( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nSttNode,
+ 0, pTextNd->GetText().getLength(), false );
+ }
+ if( pTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nSttNode );
+ }
+ if( pEndTextNd && pEndTextNd != pTextNd )
+ {
+ m_pHistory->Add( pEndTextNd->GetTextColl(), m_nEndNode, SwNodeType::Text );
+ if ( pEndTextNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pEndTextNd->GetpSwpHints(), m_nEndNode,
+ 0, pEndTextNd->GetText().getLength(), false );
+ }
+ if( pEndTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pEndTextNd->GetpSwAttrSet(), m_nEndNode );
+ }
+
+ pTextNd = rMvPos.nNode.GetNode().GetTextNode();
+ if (nullptr != pTextNd)
+ {
+ m_pHistory->Add( pTextNd->GetTextColl(), m_nMoveDestNode, SwNodeType::Text );
+ if ( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nMoveDestNode,
+ 0, pTextNd->GetText().getLength(), false );
+ }
+ if( pTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nMoveDestNode );
+ }
+
+ m_nFootnoteStart = m_pHistory->Count();
+ DelFootnote( rRange );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+}
+
+SwUndoMove::SwUndoMove( SwDoc& rDoc, const SwNodeRange& rRg,
+ const SwNodeIndex& rMvPos )
+ : SwUndo(SwUndoId::MOVE, &rDoc)
+ , m_nDestStartNode(0)
+ , m_nDestEndNode(0)
+ , m_nInsPosNode(0)
+ , m_nMoveDestNode(rMvPos.GetIndex())
+ , m_nDestStartContent(0)
+ , m_nDestEndContent(0)
+ , m_nInsPosContent(0)
+ , m_bMoveRedlines(false)
+{
+ m_bMoveRange = true;
+ m_bJoinNext = false;
+
+ m_nSttContent = m_nEndContent = m_nMoveDestContent = COMPLETE_STRING;
+
+ m_nSttNode = rRg.aStart.GetIndex();
+ m_nEndNode = rRg.aEnd.GetIndex();
+
+// DelFootnote( rRange );
+// FIXME: duplication of the method body of DelFootnote below
+
+ // is the current move from ContentArea into the special section?
+ SwNodeOffset nContentStt = rDoc.GetNodes().GetEndOfAutotext().GetIndex();
+ if( m_nMoveDestNode < nContentStt && rRg.aStart.GetIndex() > nContentStt )
+ {
+ // delete all footnotes since they are undesired there
+ SwPosition aPtPos( rRg.aEnd );
+ SwContentNode* pCNd = rRg.aEnd.GetNode().GetContentNode();
+ if( pCNd )
+ aPtPos.nContent.Assign( pCNd, pCNd->Len() );
+ SwPosition aMkPos( rRg.aStart );
+ pCNd = aMkPos.nNode.GetNode().GetContentNode();
+ if( nullptr != pCNd )
+ aMkPos.nContent.Assign( pCNd, 0 );
+
+ DelContentIndex( aMkPos, aPtPos, DelContentType::Ftn );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ m_pHistory.reset();
+ }
+
+ m_nFootnoteStart = 0;
+}
+
+void SwUndoMove::SetDestRange( const SwNodeIndex& rStt,
+ const SwNodeIndex& rEnd,
+ const SwNodeIndex& rInsPos )
+{
+ m_nDestStartNode = rStt.GetIndex();
+ m_nDestEndNode = rEnd.GetIndex();
+ if( m_nDestStartNode > m_nDestEndNode )
+ {
+ m_nDestStartNode = m_nDestEndNode;
+ m_nDestEndNode = rStt.GetIndex();
+ }
+ m_nInsPosNode = rInsPos.GetIndex();
+
+ m_nDestStartContent = m_nDestEndContent = m_nInsPosContent = COMPLETE_STRING;
+}
+
+void SwUndoMove::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ // Block so that we can jump out of it
+ do {
+ // create index position and section based on the existing values
+ SwNodeIndex aIdx( pDoc->GetNodes(), m_nDestStartNode );
+
+ if( m_bMoveRange )
+ {
+ // only a move with SwRange
+ SwNodeRange aRg( aIdx, aIdx );
+ aRg.aEnd = m_nDestEndNode;
+ aIdx = m_nInsPosNode;
+ bool bSuccess = pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIdx,
+ SwMoveFlags::DEFAULT );
+ if (!bSuccess)
+ break;
+ }
+ else
+ {
+ SwPaM aPam( aIdx.GetNode(), m_nDestStartContent,
+ *pDoc->GetNodes()[ m_nDestEndNode ], m_nDestEndContent );
+
+ // #i17764# if redlines are to be moved, we may not remove them
+ // before pDoc->Move gets a chance to handle them
+ if( ! m_bMoveRedlines )
+ RemoveIdxFromRange( aPam, false );
+
+ SwPosition aPos( *pDoc->GetNodes()[ m_nInsPosNode] );
+ SwContentNode* pCNd = aPos.nNode.GetNode().GetContentNode();
+ aPos.nContent.Assign( pCNd, m_nInsPosContent );
+
+ if( pCNd->HasSwAttrSet() )
+ pCNd->ResetAllAttr();
+
+ if( pCNd->IsTextNode() && static_cast<SwTextNode*>(pCNd)->GetpSwpHints() )
+ static_cast<SwTextNode*>(pCNd)->ClearSwpHintsArr( false );
+
+ // first delete all attributes at InsertPos
+ const bool bSuccess = pDoc->getIDocumentContentOperations().MoveRange( aPam, aPos, m_bMoveRedlines
+ ? SwMoveFlags::REDLINES
+ : SwMoveFlags::DEFAULT );
+ if (!bSuccess)
+ break;
+
+ aPam.Exchange();
+ aPam.DeleteMark();
+ if( aPam.GetNode().IsContentNode() )
+ aPam.GetNode().GetContentNode()->ResetAllAttr();
+ // the Pam will be dropped now
+ }
+
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ if( m_bJoinNext )
+ {
+ {
+ RemoveIdxRel( aIdx.GetIndex() + 1, SwPosition( aIdx,
+ SwIndex(pTextNd, pTextNd->GetText().getLength())));
+ }
+ // Are there any Pams in the next TextNode?
+ pTextNd->JoinNext();
+ }
+
+ } while( false );
+
+ if( m_pHistory )
+ {
+ if( m_nFootnoteStart != m_pHistory->Count() )
+ m_pHistory->Rollback( pDoc, m_nFootnoteStart );
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ // set the cursor onto Undo area
+ if( !m_bMoveRange )
+ {
+ AddUndoRedoPaM(rContext);
+ }
+}
+
+void SwUndoMove::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM& rPam = AddUndoRedoPaM(rContext);
+ SwDoc& rDoc = rContext.GetDoc();
+
+ SwNodes& rNds = rDoc.GetNodes();
+ SwNodeIndex aIdx( rNds, m_nMoveDestNode );
+
+ if( m_bMoveRange )
+ {
+ // only a move with SwRange
+ SwNodeRange aRg( rNds, m_nSttNode, rNds, m_nEndNode );
+ rDoc.getIDocumentContentOperations().MoveNodeRange( aRg, aIdx, m_bMoveRedlines
+ ? SwMoveFlags::REDLINES
+ : SwMoveFlags::DEFAULT );
+ }
+ else
+ {
+ SwPaM aPam(*rPam.GetPoint());
+ SetPaM( aPam );
+ SwPosition aMvPos( aIdx, SwIndex( aIdx.GetNode().GetContentNode(),
+ m_nMoveDestContent ));
+
+ DelFootnote( aPam );
+ RemoveIdxFromRange( aPam, false );
+
+ aIdx = aPam.Start()->nNode;
+ bool bJoinText = aIdx.GetNode().IsTextNode();
+
+ --aIdx;
+ rDoc.getIDocumentContentOperations().MoveRange( aPam, aMvPos,
+ SwMoveFlags::DEFAULT );
+
+ if( m_nSttNode != m_nEndNode && bJoinText )
+ {
+ ++aIdx;
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+ if( pTextNd && pTextNd->CanJoinNext() )
+ {
+ {
+ RemoveIdxRel( aIdx.GetIndex() + 1, SwPosition( aIdx,
+ SwIndex(pTextNd, pTextNd->GetText().getLength())));
+ }
+ pTextNd->JoinNext();
+ }
+ }
+ *rPam.GetPoint() = *aPam.GetPoint();
+ rPam.SetMark();
+ *rPam.GetMark() = *aPam.GetMark();
+ }
+}
+
+void SwUndoMove::DelFootnote( const SwPaM& rRange )
+{
+ // is the current move from ContentArea into the special section?
+ SwDoc& rDoc = rRange.GetDoc();
+ SwNodeOffset nContentStt = rDoc.GetNodes().GetEndOfAutotext().GetIndex();
+ if( m_nMoveDestNode < nContentStt &&
+ rRange.GetPoint()->nNode.GetIndex() >= nContentStt )
+ {
+ // delete all footnotes since they are undesired there
+ DelContentIndex( *rRange.GetMark(), *rRange.GetPoint(),
+ DelContentType::Ftn );
+
+ if( m_pHistory && !m_pHistory->Count() )
+ {
+ m_pHistory.reset();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unnum.cxx b/sw/source/core/undo/unnum.cxx
new file mode 100644
index 000000000..be0d6f4f4
--- /dev/null
+++ b/sw/source/core/undo/unnum.cxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoNumbering.hxx>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <osl/diagnose.h>
+
+SwUndoInsNum::SwUndoInsNum( const SwNumRule& rOldRule,
+ const SwNumRule& rNewRule,
+ const SwDoc& rDoc,
+ SwUndoId nUndoId)
+ : SwUndo( nUndoId, &rDoc ),
+ m_aNumRule( rNewRule ),
+ m_pOldNumRule( new SwNumRule( rOldRule )), m_nLRSavePos( 0 )
+{
+}
+
+SwUndoInsNum::SwUndoInsNum( const SwPaM& rPam, const SwNumRule& rRule )
+ : SwUndo( SwUndoId::INSNUM, &rPam.GetDoc() ), SwUndRng( rPam ),
+ m_aNumRule( rRule ),
+ m_nLRSavePos( 0 )
+{
+}
+
+SwUndoInsNum::SwUndoInsNum( const SwPosition& rPos, const SwNumRule& rRule,
+ const OUString& rReplaceRule )
+ : SwUndo( SwUndoId::INSNUM, &rPos.nNode.GetNode().GetDoc() ),
+ m_aNumRule( rRule ),
+ m_sReplaceRule( rReplaceRule ), m_nLRSavePos( 0 )
+{
+ // No selection!
+ m_nEndNode = SwNodeOffset(0);
+ m_nEndContent = COMPLETE_STRING;
+ m_nSttNode = rPos.nNode.GetIndex();
+ m_nSttContent = rPos.nContent.GetIndex();
+}
+
+SwUndoInsNum::~SwUndoInsNum()
+{
+ m_pHistory.reset();
+ m_pOldNumRule.reset();
+}
+
+SwRewriter SwUndoInsNum::GetRewriter() const
+{
+ SwRewriter aResult;
+ if( SwUndoId::INSFMTATTR == GetId() )
+ aResult.AddRule(UndoArg1, m_aNumRule.GetName());
+ return aResult;
+}
+
+void SwUndoInsNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if( m_pOldNumRule )
+ rDoc.ChgNumRuleFormats( *m_pOldNumRule );
+
+ if( m_pHistory )
+ {
+ if( m_nLRSavePos )
+ {
+ // Update immediately so that potential "old" LRSpaces will be valid again.
+ m_pHistory->TmpRollback( &rDoc, m_nLRSavePos );
+
+ }
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ if (m_nSttNode)
+ {
+ AddUndoRedoPaM(rContext);
+ }
+}
+
+void SwUndoInsNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if ( m_pOldNumRule )
+ rDoc.ChgNumRuleFormats( m_aNumRule );
+ else if ( m_pHistory )
+ {
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ if( !m_sReplaceRule.isEmpty() )
+ {
+ rDoc.ReplaceNumRule( *rPam.GetPoint(), m_sReplaceRule, m_aNumRule.GetName() );
+ }
+ else
+ {
+ // #i42921# - adapt to changed signature
+ rDoc.SetNumRule(rPam, m_aNumRule, false);
+ }
+ }
+}
+
+void SwUndoInsNum::SetLRSpaceEndPos()
+{
+ if( m_pHistory )
+ m_nLRSavePos = m_pHistory->Count();
+}
+
+void SwUndoInsNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc( rContext.GetDoc() );
+ if ( m_nSttNode )
+ {
+ if( m_sReplaceRule.isEmpty() )
+ {
+ // #i42921# - adapt to changed signature
+ rDoc.SetNumRule(rContext.GetRepeatPaM(), m_aNumRule, false);
+ }
+ }
+ else
+ {
+ rDoc.ChgNumRuleFormats( m_aNumRule );
+ }
+}
+
+SwHistory* SwUndoInsNum::GetHistory()
+{
+ if( !m_pHistory )
+ m_pHistory.reset(new SwHistory);
+ return m_pHistory.get();
+}
+
+void SwUndoInsNum::SaveOldNumRule( const SwNumRule& rOld )
+{
+ if( !m_pOldNumRule )
+ m_pOldNumRule.reset(new SwNumRule( rOld ));
+}
+
+SwUndoDelNum::SwUndoDelNum( const SwPaM& rPam )
+ : SwUndo( SwUndoId::DELNUM, &rPam.GetDoc() ), SwUndRng( rPam )
+{
+ if (m_nEndNode > m_nSttNode)
+ m_aNodes.reserve( std::min<sal_Int32>(sal_Int32(m_nEndNode - m_nSttNode), 255) );
+ m_pHistory.reset( new SwHistory );
+}
+
+SwUndoDelNum::~SwUndoDelNum()
+{
+}
+
+void SwUndoDelNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+
+ for( const auto& rNode : m_aNodes )
+ {
+ SwTextNode* pNd = rDoc.GetNodes()[ rNode.index ]->GetTextNode();
+ OSL_ENSURE( pNd, "Where has the TextNode gone?" );
+ pNd->SetAttrListLevel( rNode.level );
+
+ if( pNd->GetCondFormatColl() )
+ pNd->ChkCondColl();
+ }
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoDelNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().DelNumRules(rPam);
+}
+
+void SwUndoDelNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().DelNumRules(rContext.GetRepeatPaM());
+}
+
+void SwUndoDelNum::AddNode( const SwTextNode& rNd )
+{
+ if( rNd.GetNumRule() )
+ {
+ m_aNodes.emplace_back( rNd.GetIndex(), rNd.GetActualListLevel() );
+ }
+}
+
+SwUndoMoveNum::SwUndoMoveNum( const SwPaM& rPam, SwNodeOffset nOff, bool bIsOutlMv )
+ : SwUndo( bIsOutlMv ? SwUndoId::OUTLINE_UD : SwUndoId::MOVENUM, &rPam.GetDoc() ),
+ SwUndRng( rPam ),
+ m_nNewStart( 0 ), m_nOffset( nOff )
+{
+ // nOffset: Down => 1
+ // Up => -1
+}
+
+void SwUndoMoveNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeOffset nTmpStt = m_nSttNode, nTmpEnd = m_nEndNode;
+
+ if (m_nEndNode || m_nEndContent != COMPLETE_STRING) // section?
+ {
+ if( m_nNewStart < m_nSttNode ) // moved forwards
+ m_nEndNode = m_nEndNode - ( m_nSttNode - m_nNewStart );
+ else
+ m_nEndNode = m_nEndNode + ( m_nNewStart - m_nSttNode );
+ }
+ m_nSttNode = m_nNewStart;
+
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().MoveParagraph( rPam, -m_nOffset,
+ SwUndoId::OUTLINE_UD == GetId() );
+ m_nSttNode = nTmpStt;
+ m_nEndNode = nTmpEnd;
+}
+
+void SwUndoMoveNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().MoveParagraph(rPam, m_nOffset, SwUndoId::OUTLINE_UD == GetId());
+}
+
+void SwUndoMoveNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if( SwUndoId::OUTLINE_UD == GetId() )
+ {
+ rDoc.MoveOutlinePara(rContext.GetRepeatPaM(),
+ SwNodeOffset(0) < m_nOffset ? 1 : -1 );
+ }
+ else
+ {
+ rDoc.MoveParagraph(rContext.GetRepeatPaM(), m_nOffset);
+ }
+}
+
+SwUndoNumUpDown::SwUndoNumUpDown( const SwPaM& rPam, short nOff )
+ : SwUndo( nOff > 0 ? SwUndoId::NUMUP : SwUndoId::NUMDOWN, &rPam.GetDoc() ),
+ SwUndRng( rPam ),
+ m_nOffset( nOff )
+{
+ // nOffset: Down => 1
+ // Up => -1
+}
+
+void SwUndoNumUpDown::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().NumUpDown(rPam, 1 != m_nOffset );
+}
+
+void SwUndoNumUpDown::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().NumUpDown(rPam, 1 == m_nOffset);
+}
+
+void SwUndoNumUpDown::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().NumUpDown(rContext.GetRepeatPaM(), 1 == m_nOffset);
+}
+
+SwUndoNumOrNoNum::SwUndoNumOrNoNum( const SwNodeIndex& rIdx, bool bOldNum,
+ bool bNewNum)
+ : SwUndo( SwUndoId::NUMORNONUM, &rIdx.GetNode().GetDoc() ),
+ m_nIndex( rIdx.GetIndex() ), mbNewNum(bNewNum),
+ mbOldNum(bOldNum)
+{
+}
+
+// #115901#, #i40034#
+void SwUndoNumOrNoNum::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), m_nIndex );
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+
+ if (nullptr != pTextNd)
+ {
+ pTextNd->SetCountedInList(mbOldNum);
+ }
+}
+
+// #115901#, #i40034#
+void SwUndoNumOrNoNum::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwNodeIndex aIdx( rContext.GetDoc().GetNodes(), m_nIndex );
+ SwTextNode * pTextNd = aIdx.GetNode().GetTextNode();
+
+ if (nullptr != pTextNd)
+ {
+ pTextNd->SetCountedInList(mbNewNum);
+ }
+}
+
+void SwUndoNumOrNoNum::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (mbOldNum && ! mbNewNum)
+ {
+ rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->nNode);
+ }
+ else if ( ! mbOldNum && mbNewNum )
+ {
+ rDoc.NumOrNoNum(rContext.GetRepeatPaM().GetPoint()->nNode, true);
+ }
+}
+
+SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, bool bFlg )
+ : SwUndo( SwUndoId::SETNUMRULESTART, &rPos.GetDoc() ),
+ m_nIndex( rPos.nNode.GetIndex() ), m_nOldStart( USHRT_MAX ),
+ m_nNewStart( USHRT_MAX ), m_bSetStartValue( false ), m_bFlag( bFlg )
+{
+}
+
+SwUndoNumRuleStart::SwUndoNumRuleStart( const SwPosition& rPos, sal_uInt16 nStt )
+ : SwUndo(SwUndoId::SETNUMRULESTART, &rPos.GetDoc())
+ , m_nIndex(rPos.nNode.GetIndex())
+ , m_nOldStart(USHRT_MAX)
+ , m_nNewStart(nStt)
+ , m_bSetStartValue(true)
+ , m_bFlag(false)
+{
+ SwTextNode* pTextNd = rPos.nNode.GetNode().GetTextNode();
+ if ( pTextNd )
+ {
+ if ( pTextNd->HasAttrListRestartValue() )
+ {
+ m_nOldStart = o3tl::narrowing<sal_uInt16>(pTextNd->GetAttrListRestartValue());
+ }
+ else
+ {
+ m_nOldStart = USHRT_MAX; // indicating, that the list restart value is not set
+ }
+ }
+}
+
+void SwUndoNumRuleStart::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPosition const aPos( *rDoc.GetNodes()[ m_nIndex ] );
+ if( m_bSetStartValue )
+ {
+ rDoc.SetNodeNumStart( aPos, m_nOldStart );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( aPos, !m_bFlag );
+ }
+}
+
+void SwUndoNumRuleStart::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPosition const aPos( *rDoc.GetNodes()[ m_nIndex ] );
+ if( m_bSetStartValue )
+ {
+ rDoc.SetNodeNumStart( aPos, m_nNewStart );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( aPos, m_bFlag );
+ }
+}
+
+void SwUndoNumRuleStart::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if( m_bSetStartValue )
+ {
+ rDoc.SetNodeNumStart(*rContext.GetRepeatPaM().GetPoint(), m_nNewStart);
+ }
+ else
+ {
+ rDoc.SetNumRuleStart(*rContext.GetRepeatPaM().GetPoint(), m_bFlag);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unoutl.cxx b/sw/source/core/undo/unoutl.cxx
new file mode 100644
index 000000000..2144c7dd7
--- /dev/null
+++ b/sw/source/core/undo/unoutl.cxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+
+#include <UndoCore.hxx>
+
+SwUndoOutlineLeftRight::SwUndoOutlineLeftRight( const SwPaM& rPam,
+ short nOff )
+ : SwUndo( SwUndoId::OUTLINE_LR, &rPam.GetDoc() ), SwUndRng( rPam ), m_nOffset( nOff )
+{
+}
+
+void SwUndoOutlineLeftRight::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPaM( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().OutlineUpDown(rPaM, -m_nOffset);
+}
+
+void SwUndoOutlineLeftRight::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPaM( AddUndoRedoPaM(rContext) );
+ rContext.GetDoc().OutlineUpDown(rPaM, m_nOffset);
+}
+
+void SwUndoOutlineLeftRight::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().OutlineUpDown(rContext.GetRepeatPaM(), m_nOffset);
+}
+
+
+SwUndoOutlineEdit::SwUndoOutlineEdit(const SwNumRule& rOldRule, const SwNumRule& rNewRule,
+ const SwDoc& rDoc)
+ : SwUndo(SwUndoId::OUTLINE_EDIT, &rDoc)
+ , m_aNewNumRule(rNewRule)
+ , m_aOldNumRule(rOldRule)
+{
+}
+
+void SwUndoOutlineEdit::UndoImpl(::sw::UndoRedoContext& rContext)
+{
+ rContext.GetDoc().SetOutlineNumRule(m_aOldNumRule);
+}
+
+void SwUndoOutlineEdit::RedoImpl(::sw::UndoRedoContext& rContext)
+{
+ rContext.GetDoc().SetOutlineNumRule(m_aNewNumRule);
+}
+
+void SwUndoOutlineEdit::RepeatImpl(::sw::RepeatContext& rContext)
+{
+ rContext.GetDoc().SetOutlineNumRule(m_aNewNumRule);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unovwr.cxx b/sw/source/core/undo/unovwr.cxx
new file mode 100644
index 000000000..20fa7a5b9
--- /dev/null
+++ b/sw/source/core/undo/unovwr.cxx
@@ -0,0 +1,475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoOverwrite.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <swcrsr.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <acorrect.hxx>
+#include <docary.hxx>
+#include <strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+SwUndoOverwrite::SwUndoOverwrite( SwDoc& rDoc, SwPosition& rPos,
+ sal_Unicode cIns )
+ : SwUndo(SwUndoId::OVERWRITE, &rDoc),
+ m_bGroup( false )
+{
+ SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
+ assert(pTextNd);
+ sal_Int32 const nTextNdLen = pTextNd->GetText().getLength();
+
+ m_nStartNode = rPos.nNode.GetIndex();
+ m_nStartContent = rPos.nContent.GetIndex();
+
+ if( !rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(),
+ rPos.nNode, rPos.nContent.GetIndex()+1 );
+ m_pRedlSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( aPam, *m_pRedlSaveData, false ))
+ {
+ m_pRedlSaveData.reset();
+ }
+ if (m_nStartContent < nTextNdLen)
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(aPam, false, RedlineType::Any);
+ }
+ }
+
+ m_bInsChar = true;
+ if( m_nStartContent < nTextNdLen ) // no pure insert?
+ {
+ m_aDelStr += OUStringChar( pTextNd->GetText()[m_nStartContent] );
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ SwRegHistory aRHst( *pTextNd, m_pHistory.get() );
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nStartNode, 0,
+ nTextNdLen, false );
+ ++rPos.nContent;
+ m_bInsChar = false;
+ }
+
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ pTextNd->InsertText( OUString(cIns), rPos.nContent,
+ SwInsertFlags::EMPTYEXPAND );
+ m_aInsStr += OUStringChar( cIns );
+
+ if( !m_bInsChar )
+ {
+ const SwIndex aTmpIndex( rPos.nContent, -2 );
+ pTextNd->EraseText( aTmpIndex, 1 );
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ m_bCacheComment = false;
+}
+
+SwUndoOverwrite::~SwUndoOverwrite()
+{
+}
+
+bool SwUndoOverwrite::CanGrouping( SwDoc& rDoc, SwPosition& rPos,
+ sal_Unicode cIns )
+{
+// What is with only inserted characters?
+
+ // Only deletion of single chars can be combined.
+ if( rPos.nNode != m_nStartNode || m_aInsStr.isEmpty() ||
+ ( !m_bGroup && m_aInsStr.getLength() != 1 ))
+ return false;
+
+ // Is the node a TextNode at all?
+ SwTextNode * pDelTextNd = rPos.nNode.GetNode().GetTextNode();
+ if( !pDelTextNd ||
+ (pDelTextNd->GetText().getLength() != rPos.nContent.GetIndex() &&
+ rPos.nContent.GetIndex() != ( m_nStartContent + m_aInsStr.getLength() )))
+ return false;
+
+ CharClass& rCC = GetAppCharClass();
+
+ // ask the char that should be inserted
+ if (( CH_TXTATR_BREAKWORD == cIns || CH_TXTATR_INWORD == cIns ) ||
+ rCC.isLetterNumeric( OUString( cIns ), 0 ) !=
+ rCC.isLetterNumeric( m_aInsStr, m_aInsStr.getLength()-1 ) )
+ return false;
+
+ if (!m_bInsChar && rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength())
+ {
+ SwRedlineSaveDatas aTmpSav;
+ SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(),
+ rPos.nNode, rPos.nContent.GetIndex()+1 );
+
+ const bool bSaved = FillSaveData( aPam, aTmpSav, false );
+
+ bool bOk = ( !m_pRedlSaveData && !bSaved ) ||
+ ( m_pRedlSaveData && bSaved &&
+ SwUndo::CanRedlineGroup( *m_pRedlSaveData, aTmpSav,
+ m_nStartContent > rPos.nContent.GetIndex() ));
+ // aTmpSav.DeleteAndDestroyAll();
+ if( !bOk )
+ return false;
+
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, false, RedlineType::Any );
+ }
+
+ // both 'overwrites' can be combined so 'move' the corresponding character
+ if( !m_bInsChar )
+ {
+ if (rPos.nContent.GetIndex() < pDelTextNd->GetText().getLength())
+ {
+ m_aDelStr += OUStringChar( pDelTextNd->GetText()[rPos.nContent.GetIndex()] );
+ ++rPos.nContent;
+ }
+ else
+ m_bInsChar = true;
+ }
+
+ bool bOldExpFlg = pDelTextNd->IsIgnoreDontExpand();
+ pDelTextNd->SetIgnoreDontExpand( true );
+
+ OUString const ins( pDelTextNd->InsertText(OUString(cIns), rPos.nContent,
+ SwInsertFlags::EMPTYEXPAND) );
+ assert(ins.getLength() == 1); // check in SwDoc::Overwrite => cannot fail
+ (void) ins;
+ m_aInsStr += OUStringChar( cIns );
+
+ if( !m_bInsChar )
+ {
+ const SwIndex aTmpIndex( rPos.nContent, -2 );
+ pDelTextNd->EraseText( aTmpIndex, 1 );
+ }
+ pDelTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ m_bGroup = true;
+ return true;
+}
+
+void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwCursor& rCurrentPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ rCurrentPam.DeleteMark();
+ rCurrentPam.GetPoint()->nNode = m_nStartNode;
+ SwTextNode* pTextNd = rCurrentPam.GetNode().GetTextNode();
+ assert(pTextNd);
+ SwIndex& rIdx = rCurrentPam.GetPoint()->nContent;
+ rIdx.Assign( pTextNd, m_nStartContent );
+
+ SwAutoCorrExceptWord* pACEWord = rDoc.GetAutoCorrExceptWord();
+ if( pACEWord )
+ {
+ if( 1 == m_aInsStr.getLength() && 1 == m_aDelStr.getLength() )
+ pACEWord->CheckChar( *rCurrentPam.GetPoint(), m_aDelStr[0] );
+ rDoc.SetAutoCorrExceptWord( nullptr );
+ }
+
+ // If there was not only an overwrite but also an insert, delete the surplus
+ if( m_aInsStr.getLength() > m_aDelStr.getLength() )
+ {
+ rIdx += m_aDelStr.getLength();
+ pTextNd->EraseText( rIdx, m_aInsStr.getLength() - m_aDelStr.getLength() );
+ rIdx = m_nStartContent;
+ }
+
+ if( !m_aDelStr.isEmpty() )
+ {
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ ++rIdx;
+ for( sal_Int32 n = 0; n < m_aDelStr.getLength(); n++ )
+ {
+ // do it individually, to keep the attributes!
+ OUString aTmpStr(m_aDelStr[n]);
+ OUString const ins( pTextNd->InsertText(aTmpStr, rIdx) );
+ assert(ins.getLength() == 1); // cannot fail
+ (void) ins;
+ rIdx -= 2;
+ pTextNd->EraseText( rIdx, 1 );
+ rIdx += 2;
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+ --rIdx;
+ }
+
+ if( m_pHistory )
+ {
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( false );
+ m_pHistory->TmpRollback( &rDoc, 0, false );
+ }
+
+ if( rCurrentPam.GetMark()->nContent.GetIndex() != m_nStartContent )
+ {
+ rCurrentPam.SetMark();
+ rCurrentPam.GetMark()->nContent = m_nStartContent;
+ }
+
+ if( m_pRedlSaveData )
+ SetSaveData( rDoc, *m_pRedlSaveData );
+}
+
+void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM& rCurrentPam = rContext.GetRepeatPaM();
+ if( m_aInsStr.isEmpty() || rCurrentPam.HasMark() )
+ return;
+
+ SwDoc & rDoc = rContext.GetDoc();
+
+ {
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().Overwrite(rCurrentPam, OUString(m_aInsStr[0]));
+ }
+ for( sal_Int32 n = 1; n < m_aInsStr.getLength(); ++n )
+ rDoc.getIDocumentContentOperations().Overwrite(rCurrentPam, OUString(m_aInsStr[n]));
+}
+
+void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwCursor& rCurrentPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ rCurrentPam.DeleteMark();
+ rCurrentPam.GetPoint()->nNode = m_nStartNode;
+ SwTextNode* pTextNd = rCurrentPam.GetNode().GetTextNode();
+ assert(pTextNd);
+ SwIndex& rIdx = rCurrentPam.GetPoint()->nContent;
+
+ if( m_pRedlSaveData )
+ {
+ rIdx.Assign( pTextNd, m_nStartContent );
+ rCurrentPam.SetMark();
+ rCurrentPam.GetMark()->nContent += m_aDelStr.getLength();
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( rCurrentPam, false, RedlineType::Any );
+ rCurrentPam.DeleteMark();
+ }
+ rIdx.Assign( pTextNd, !m_aDelStr.isEmpty() ? m_nStartContent+1 : m_nStartContent );
+
+ bool bOldExpFlg = pTextNd->IsIgnoreDontExpand();
+ pTextNd->SetIgnoreDontExpand( true );
+
+ for( sal_Int32 n = 0; n < m_aInsStr.getLength(); n++ )
+ {
+ // do it individually, to keep the attributes!
+ OUString const ins(
+ pTextNd->InsertText( OUString(m_aInsStr[n]), rIdx,
+ SwInsertFlags::EMPTYEXPAND) );
+ assert(ins.getLength() == 1); // cannot fail
+ (void) ins;
+ if( n < m_aDelStr.getLength() )
+ {
+ rIdx -= 2;
+ pTextNd->EraseText( rIdx, 1 );
+ rIdx += n+1 < m_aDelStr.getLength() ? 2 : 1;
+ }
+ }
+ pTextNd->SetIgnoreDontExpand( bOldExpFlg );
+
+ // get back old start position from UndoNodes array
+ if( m_pHistory )
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ if( rCurrentPam.GetMark()->nContent.GetIndex() != m_nStartContent )
+ {
+ rCurrentPam.SetMark();
+ rCurrentPam.GetMark()->nContent = m_nStartContent;
+ }
+}
+
+SwRewriter SwUndoOverwrite::GetRewriter() const
+{
+ SwRewriter aResult;
+
+ OUString aString = SwResId(STR_START_QUOTE) +
+ ShortenString(m_aInsStr, nUndoStringLength, SwResId(STR_LDOTS)) +
+ SwResId(STR_END_QUOTE);
+
+ aResult.AddRule(UndoArg1, aString);
+
+ return aResult;
+}
+
+struct UndoTransliterate_Data
+{
+ OUString sText;
+ std::unique_ptr<SwHistory> pHistory;
+ std::unique_ptr<Sequence< sal_Int32 >> pOffsets;
+ SwNodeOffset nNdIdx;
+ sal_Int32 nStart, nLen;
+
+ UndoTransliterate_Data( SwNodeOffset nNd, sal_Int32 nStt, sal_Int32 nStrLen, const OUString& rText )
+ : sText( rText ),
+ nNdIdx( nNd ), nStart( nStt ), nLen( nStrLen )
+ {}
+
+ void SetChangeAtNode( SwDoc& rDoc );
+};
+
+SwUndoTransliterate::SwUndoTransliterate(
+ const SwPaM& rPam,
+ const utl::TransliterationWrapper& rTrans )
+ : SwUndo( SwUndoId::TRANSLITERATE, &rPam.GetDoc() ), SwUndRng( rPam ), m_nType( rTrans.getType() )
+{
+}
+
+SwUndoTransliterate::~SwUndoTransliterate()
+{
+}
+
+void SwUndoTransliterate::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ // since the changes were added to the vector from the end of the string/node towards
+ // the start, we need to revert them from the start towards the end now to keep the
+ // offset information of the undo data in sync with the changing text.
+ // Thus we need to iterate from the end of the vector to the start
+ for (sal_Int32 i = m_aChanges.size() - 1; i >= 0; --i)
+ m_aChanges[i]->SetChangeAtNode( rDoc );
+
+ AddUndoRedoPaM(rContext, true);
+}
+
+void SwUndoTransliterate::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ DoTransliterate(rContext.GetDoc(), rPam);
+}
+
+void SwUndoTransliterate::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ DoTransliterate(rContext.GetDoc(), rContext.GetRepeatPaM());
+}
+
+void SwUndoTransliterate::DoTransliterate(SwDoc & rDoc, SwPaM const & rPam)
+{
+ utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), m_nType );
+ rDoc.getIDocumentContentOperations().TransliterateText( rPam, aTrans );
+}
+
+void SwUndoTransliterate::AddChanges( SwTextNode& rTNd,
+ sal_Int32 nStart, sal_Int32 nLen,
+ uno::Sequence <sal_Int32> const & rOffsets )
+{
+ tools::Long nOffsLen = rOffsets.getLength();
+ UndoTransliterate_Data* pNew = new UndoTransliterate_Data(
+ rTNd.GetIndex(), nStart, static_cast<sal_Int32>(nOffsLen),
+ rTNd.GetText().copy(nStart, nLen));
+
+ m_aChanges.push_back( std::unique_ptr<UndoTransliterate_Data>(pNew) );
+
+ const sal_Int32* pOffsets = rOffsets.getConstArray();
+ // where did we need less memory ?
+ const sal_Int32* p = pOffsets;
+ for( tools::Long n = 0; n < nOffsLen; ++n, ++p )
+ if( *p != ( nStart + n ))
+ {
+ // create the Offset array
+ pNew->pOffsets.reset( new Sequence <sal_Int32> ( nLen ) );
+ sal_Int32* pIdx = pNew->pOffsets->getArray();
+ p = pOffsets;
+ tools::Long nMyOff, nNewVal = nStart;
+ for( n = 0, nMyOff = nStart; n < nOffsLen; ++p, ++n, ++nMyOff )
+ {
+ if( *p < nMyOff )
+ {
+ // something is deleted
+ nMyOff = *p;
+ *(pIdx-1) = nNewVal++;
+ }
+ else if( *p > nMyOff )
+ {
+ for( ; *p > nMyOff; ++nMyOff )
+ *pIdx++ = nNewVal;
+ --nMyOff;
+ --n;
+ --p;
+ }
+ else
+ *pIdx++ = nNewVal++;
+ }
+
+ // and then we need to save the attributes/bookmarks
+ // but this data must moved every time to the last in the chain!
+ for (size_t i = 0; i + 1 < m_aChanges.size(); ++i) // check all changes but not the current one
+ {
+ UndoTransliterate_Data* pD = m_aChanges[i].get();
+ if( pD->nNdIdx == pNew->nNdIdx && pD->pHistory )
+ {
+ // same node and have a history?
+ pNew->pHistory = std::move(pD->pHistory);
+ break; // more can't exist
+ }
+ }
+
+ if( !pNew->pHistory )
+ {
+ pNew->pHistory.reset( new SwHistory );
+ SwRegHistory aRHst( rTNd, pNew->pHistory.get() );
+ pNew->pHistory->CopyAttr( rTNd.GetpSwpHints(),
+ pNew->nNdIdx, 0, rTNd.GetText().getLength(), false );
+ }
+ break;
+ }
+}
+
+void UndoTransliterate_Data::SetChangeAtNode( SwDoc& rDoc )
+{
+ SwTextNode* pTNd = rDoc.GetNodes()[ nNdIdx ]->GetTextNode();
+ if( !pTNd )
+ return;
+
+ Sequence <sal_Int32> aOffsets( pOffsets ? pOffsets->getLength() : nLen );
+ if( pOffsets )
+ aOffsets = *pOffsets;
+ else
+ {
+ sal_Int32* p = aOffsets.getArray();
+ for( sal_Int32 n = 0; n < nLen; ++n, ++p )
+ *p = n + nStart;
+ }
+ pTNd->ReplaceTextOnly( nStart, nLen, sText, aOffsets );
+
+ if( pHistory )
+ {
+ if( pTNd->GetpSwpHints() )
+ pTNd->ClearSwpHintsArr( false );
+ pHistory->TmpRollback( &rDoc, 0, false );
+ pHistory->SetTmpEnd( pHistory->Count() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unredln.cxx b/sw/source/core/undo/unredln.cxx
new file mode 100644
index 000000000..1970eff02
--- /dev/null
+++ b/sw/source/core/undo/unredln.cxx
@@ -0,0 +1,602 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoRedline.hxx>
+#include <hintids.hxx>
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <txtfrm.hxx>
+#include <mvsave.hxx>
+#include <rolbck.hxx>
+#include <UndoCore.hxx>
+#include <UndoDelete.hxx>
+#include <strings.hrc>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <sortopt.hxx>
+#include <docedt.hxx>
+
+SwUndoRedline::SwUndoRedline( SwUndoId nUsrId, const SwPaM& rRange )
+ : SwUndo( SwUndoId::REDLINE, &rRange.GetDoc() ), SwUndRng( rRange ),
+ mnUserId( nUsrId ),
+ mbHiddenRedlines( false )
+{
+ // consider Redline
+ SwDoc& rDoc = rRange.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ switch( mnUserId )
+ {
+ case SwUndoId::DELETE:
+ case SwUndoId::REPLACE:
+ mpRedlData.reset( new SwRedlineData( RedlineType::Delete, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ break;
+ default:
+ ;
+ }
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ SwNodeOffset nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+
+ mpRedlSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( rRange, *mpRedlSaveData, false, SwUndoId::REJECT_REDLINE != mnUserId ))
+ {
+ mpRedlSaveData.reset();
+ }
+ else
+ {
+ mbHiddenRedlines = HasHiddenRedlines( *mpRedlSaveData );
+ if( mbHiddenRedlines ) // then the NodeIndices of SwUndRng need to be corrected
+ {
+ nEndExtra -= rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ m_nSttNode -= nEndExtra;
+ m_nEndNode -= nEndExtra;
+ }
+ }
+}
+
+SwUndoRedline::~SwUndoRedline()
+{
+ mpRedlData.reset();
+ mpRedlSaveData.reset();
+}
+
+sal_uInt16 SwUndoRedline::GetRedlSaveCount() const
+{
+ return mpRedlSaveData ? mpRedlSaveData->size() : 0;
+}
+
+void SwUndoRedline::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ // fix PaM for deletions shown in margin
+ bool bIsDeletion = dynamic_cast<SwUndoRedlineDelete*>(this);
+ const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ sal_uInt32 nMaxId = SAL_MAX_UINT32;
+ if ( bIsDeletion && rTable.size() > 0 )
+ {
+ // Nodes of the deletion range are in the newest invisible redlines.
+ // Set all redlines visible and recover the original deletion range.
+ for (SwNodeOffset nNodes(0); nNodes < m_nEndNode - m_nSttNode + 1; ++nNodes)
+ {
+ SwRedlineTable::size_type nCurRedlinePos = 0;
+ SwRangeRedline * pRedline(rTable[nCurRedlinePos]);
+
+ // search last but nNodes redline by its nth biggest id
+ for( SwRedlineTable::size_type n = 1; n < rTable.size(); ++n )
+ {
+ SwRangeRedline *pRed(rTable[n]);
+ if ( !pRed->HasMark() && pRedline->GetId() < pRed->GetId() && pRed->GetId() < nMaxId )
+ {
+ nCurRedlinePos = n;
+ pRedline = pRed;
+ }
+ }
+
+ nMaxId = pRedline->GetId();
+
+ if ( !pRedline->IsVisible() && !pRedline->HasMark() )
+ {
+ // set it visible
+ pRedline->Show(0, rTable.GetPos(pRedline), /*bForced=*/true);
+ pRedline->Show(1, rTable.GetPos(pRedline), /*bForced=*/true);
+
+ // extend the range
+ if ( nNodes == SwNodeOffset(0) )
+ rPam = *pRedline;
+ else
+ {
+ rPam.SetMark();
+ *rPam.GetMark() = *pRedline->GetMark();
+ }
+ }
+ }
+ }
+
+ UndoRedlineImpl(rDoc, rPam);
+
+ if( mpRedlSaveData )
+ {
+ SwNodeOffset nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ SetSaveData(rDoc, *mpRedlSaveData);
+ if( mbHiddenRedlines )
+ {
+ mpRedlSaveData->clear();
+
+ nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex() - nEndExtra;
+ m_nSttNode += nEndExtra;
+ m_nEndNode += nEndExtra;
+ }
+ SetPaM(rPam, true);
+ }
+
+ // update frames after calling SetSaveData
+ if ( bIsDeletion )
+ {
+ sw::UpdateFramesForRemoveDeleteRedline(rDoc, rPam);
+ }
+ else if (dynamic_cast<SwUndoAcceptRedline*>(this)
+ || dynamic_cast<SwUndoRejectRedline*>(this))
+ { // (can't check here if there's a delete redline being accepted)
+ sw::UpdateFramesForAddDeleteRedline(rDoc, rPam);
+ }
+}
+
+void SwUndoRedline::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On );
+
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ if( mpRedlSaveData && mbHiddenRedlines )
+ {
+ SwNodeOffset nEndExtra = rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ FillSaveData(rPam, *mpRedlSaveData, false, SwUndoId::REJECT_REDLINE != mnUserId );
+
+ nEndExtra -= rDoc.GetNodes().GetEndOfExtras().GetIndex();
+ m_nSttNode -= nEndExtra;
+ m_nEndNode -= nEndExtra;
+ }
+
+ RedoRedlineImpl(rDoc, rPam);
+
+ SetPaM(rPam, true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+}
+
+void SwUndoRedline::UndoRedlineImpl(SwDoc &, SwPaM &)
+{
+}
+
+// default: remove redlines
+void SwUndoRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+}
+
+SwUndoRedlineDelete::SwUndoRedlineDelete(
+ const SwPaM& rRange, SwUndoId const nUsrId, SwDeleteFlags const flags)
+ : SwUndoRedline( nUsrId != SwUndoId::EMPTY ? nUsrId : SwUndoId::DELETE, rRange ),
+ m_bCanGroup( false ), m_bIsDelim( false ), m_bIsBackspace( false )
+{
+ const SwTextNode* pTNd;
+ SetRedlineText(rRange.GetText());
+ if( SwUndoId::DELETE == mnUserId &&
+ m_nSttNode == m_nEndNode && m_nSttContent + 1 == m_nEndContent &&
+ nullptr != (pTNd = rRange.GetNode().GetTextNode()) )
+ {
+ sal_Unicode const cCh = pTNd->GetText()[m_nSttContent];
+ if( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh )
+ {
+ m_bCanGroup = true;
+ m_bIsDelim = !GetAppCharClass().isLetterNumeric( pTNd->GetText(),
+ m_nSttContent );
+ m_bIsBackspace = m_nSttContent == rRange.GetPoint()->nContent.GetIndex();
+ }
+ }
+
+ m_bCacheComment = false;
+ if (flags & SwDeleteFlags::ArtificialSelection)
+ {
+ InitHistory(rRange);
+ }
+}
+
+void SwUndoRedlineDelete::InitHistory(SwPaM const& rRedline)
+{
+ m_pHistory.reset(new SwHistory);
+ // try to rely on direction of rPam here so it works for
+ // backspacing/deleting consecutive characters
+ SaveFlyArr flys;
+ SaveFlyInRange(rRedline, *rRedline.GetMark(), flys, false, m_pHistory.get());
+ RestFlyInRange(flys, *rRedline.GetPoint(), &rRedline.GetPoint()->nNode, true);
+ if (m_pHistory->Count())
+ {
+ m_bCanGroup = false; // how to group history?
+ }
+}
+
+// bit of a hack, replace everything...
+SwRewriter SwUndoRedlineDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+ OUString aStr = DenoteSpecialCharacters(m_sRedlineText);
+ aStr = ShortenString(aStr, nUndoStringLength, SwResId(STR_LDOTS));
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, aStr);
+ OUString ret = aRewriter.Apply(SwResId(STR_UNDO_REDLINE_DELETE));
+ aResult.AddRule(UndoArg1, ret);
+ return aResult;
+}
+
+void SwUndoRedlineDelete::SetRedlineText(const OUString & rText)
+{
+ m_sRedlineText = rText;
+}
+
+void SwUndoRedlineDelete::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+ if (m_pHistory)
+ {
+ m_pHistory->TmpRollback(&rDoc, 0);
+ }
+}
+
+void SwUndoRedlineDelete::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ if (rPam.GetPoint() != rPam.GetMark())
+ {
+ if (m_pHistory) // if it was created before, it must be recreated now
+ {
+ rPam.Normalize(m_bIsBackspace); // to check the correct edge
+ InitHistory(rPam);
+ }
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline(*mpRedlData, rPam), false );
+ }
+ sw::UpdateFramesForAddDeleteRedline(rDoc, rPam);
+}
+
+bool SwUndoRedlineDelete::CanGrouping( const SwUndoRedlineDelete& rNext )
+{
+ bool bRet = false;
+ if( SwUndoId::DELETE == mnUserId && mnUserId == rNext.mnUserId &&
+ m_bCanGroup && rNext.m_bCanGroup &&
+ m_bIsDelim == rNext.m_bIsDelim &&
+ m_bIsBackspace == rNext.m_bIsBackspace &&
+ m_nSttNode == m_nEndNode &&
+ rNext.m_nSttNode == m_nSttNode &&
+ rNext.m_nEndNode == m_nEndNode )
+ {
+ int bIsEnd = 0;
+ if( rNext.m_nSttContent == m_nEndContent )
+ bIsEnd = 1;
+ else if( rNext.m_nEndContent == m_nSttContent )
+ bIsEnd = -1;
+
+ if( bIsEnd &&
+ (( !mpRedlSaveData && !rNext.mpRedlSaveData ) ||
+ ( mpRedlSaveData && rNext.mpRedlSaveData &&
+ SwUndo::CanRedlineGroup( *mpRedlSaveData,
+ *rNext.mpRedlSaveData, 1 != bIsEnd )
+ )))
+ {
+ if( 1 == bIsEnd )
+ m_nEndContent = rNext.m_nEndContent;
+ else
+ m_nSttContent = rNext.m_nSttContent;
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+SwUndoRedlineSort::SwUndoRedlineSort( const SwPaM& rRange,
+ const SwSortOptions& rOpt )
+ : SwUndoRedline( SwUndoId::SORT_TXT, rRange ),
+ m_pOpt( new SwSortOptions( rOpt ) ),
+ m_nSaveEndNode( m_nEndNode ), m_nSaveEndContent( m_nEndContent )
+{
+}
+
+SwUndoRedlineSort::~SwUndoRedlineSort()
+{
+}
+
+void SwUndoRedlineSort::UndoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ // rPam contains the sorted range
+ // aSaveRange contains copied (i.e. original) range
+
+ SwPosition *const pStart = rPam.Start();
+ SwPosition *const pEnd = rPam.End();
+
+ SwNodeIndex aPrevIdx( pStart->nNode, -1 );
+ SwNodeOffset nOffsetTemp = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex();
+
+ if( !( RedlineFlags::ShowDelete & rDoc.getIDocumentRedlineAccess().GetRedlineFlags()) )
+ {
+ // Search both Redline objects and make them visible to make the nodes
+ // consistent again. The 'delete' one is hidden, thus search for the
+ // 'insert' Redline object. The former is located directly after the latter.
+ SwRedlineTable::size_type nFnd = rDoc.getIDocumentRedlineAccess().GetRedlinePos(
+ *rDoc.GetNodes()[ m_nSttNode + 1 ],
+ RedlineType::Insert );
+ OSL_ENSURE( SwRedlineTable::npos != nFnd && nFnd+1 < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(),
+ "could not find an Insert object" );
+ ++nFnd;
+ rDoc.getIDocumentRedlineAccess().GetRedlineTable()[nFnd]->Show(1, nFnd);
+ }
+
+ {
+ SwPaM aTmp( *rPam.GetMark() );
+ aTmp.GetMark()->nContent = 0;
+ aTmp.SetMark();
+ aTmp.GetPoint()->nNode = m_nSaveEndNode;
+ aTmp.GetPoint()->nContent.Assign( aTmp.GetContentNode(), m_nSaveEndContent );
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aTmp, true, RedlineType::Any );
+ }
+
+ rDoc.getIDocumentContentOperations().DelFullPara(rPam);
+
+ SwPaM *const pPam = & rPam;
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 );
+ SwContentNode* pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign(pCNd, 0 );
+ pPam->SetMark();
+
+ pPam->GetPoint()->nNode += nOffsetTemp;
+ pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ SetValues( *pPam );
+
+ SetPaM(rPam);
+}
+
+void SwUndoRedlineSort::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ SwPaM* pPam = &rPam;
+ SwPosition* pStart = pPam->Start();
+ SwPosition* pEnd = pPam->End();
+
+ SwNodeIndex aPrevIdx( pStart->nNode, -1 );
+ SwNodeOffset nOffsetTemp = pEnd->nNode.GetIndex() - pStart->nNode.GetIndex();
+ const sal_Int32 nCntStt = pStart->nContent.GetIndex();
+
+ rDoc.SortText(rPam, *m_pOpt);
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 );
+ SwContentNode* pCNd = pPam->GetContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if( nLen > nCntStt )
+ nLen = nCntStt;
+ pPam->GetPoint()->nContent.Assign(pCNd, nLen );
+ pPam->SetMark();
+
+ pPam->GetPoint()->nNode += nOffsetTemp;
+ pCNd = pPam->GetContentNode();
+ pPam->GetPoint()->nContent.Assign( pCNd, pCNd->Len() );
+
+ SetValues( rPam );
+
+ SetPaM( rPam );
+ rPam.GetPoint()->nNode = m_nSaveEndNode;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), m_nSaveEndContent );
+}
+
+void SwUndoRedlineSort::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().SortText( rContext.GetRepeatPaM(), *m_pOpt );
+}
+
+void SwUndoRedlineSort::SetSaveRange( const SwPaM& rRange )
+{
+ const SwPosition& rPos = *rRange.End();
+ m_nSaveEndNode = rPos.nNode.GetIndex();
+ m_nSaveEndContent = rPos.nContent.GetIndex();
+}
+
+SwUndoAcceptRedline::SwUndoAcceptRedline( const SwPaM& rRange )
+ : SwUndoRedline( SwUndoId::ACCEPT_REDLINE, rRange )
+{
+}
+
+void SwUndoAcceptRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().AcceptRedline(rPam, false);
+}
+
+void SwUndoAcceptRedline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentRedlineAccess().AcceptRedline(rContext.GetRepeatPaM(), true);
+}
+
+SwUndoRejectRedline::SwUndoRejectRedline( const SwPaM& rRange )
+ : SwUndoRedline( SwUndoId::REJECT_REDLINE, rRange )
+{
+}
+
+void SwUndoRejectRedline::RedoRedlineImpl(SwDoc & rDoc, SwPaM & rPam)
+{
+ rDoc.getIDocumentRedlineAccess().RejectRedline(rPam, false);
+}
+
+void SwUndoRejectRedline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentRedlineAccess().RejectRedline(rContext.GetRepeatPaM(), true);
+}
+
+SwUndoCompDoc::SwUndoCompDoc( const SwPaM& rRg, bool bIns )
+ : SwUndo( SwUndoId::COMPAREDOC, &rRg.GetDoc() ), SwUndRng( rRg ),
+ m_bInsert( bIns )
+{
+ SwDoc& rDoc = rRg.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ RedlineType eTyp = m_bInsert ? RedlineType::Insert : RedlineType::Delete;
+ m_pRedlineData.reset( new SwRedlineData( eTyp, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+}
+
+SwUndoCompDoc::SwUndoCompDoc( const SwRangeRedline& rRedl )
+ : SwUndo( SwUndoId::COMPAREDOC, &rRedl.GetDoc() ), SwUndRng( rRedl ),
+ // for MergeDoc the corresponding inverse is needed
+ m_bInsert( RedlineType::Delete == rRedl.GetType() )
+{
+ SwDoc& rDoc = rRedl.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( rRedl.GetRedlineData() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ m_pRedlineSaveDatas.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( rRedl, *m_pRedlineSaveDatas, false ))
+ {
+ m_pRedlineSaveDatas.reset();
+ }
+}
+
+SwUndoCompDoc::~SwUndoCompDoc()
+{
+ m_pRedlineData.reset();
+ m_pUndoDelete.reset();
+ m_pUndoDelete2.reset();
+ m_pRedlineSaveDatas.reset();
+}
+
+void SwUndoCompDoc::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ if( !m_bInsert )
+ {
+ // delete Redlines
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On);
+
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+
+ // per definition Point is end (in SwUndRng!)
+ SwContentNode* pCSttNd = rPam.GetContentNode(false);
+ SwContentNode* pCEndNd = rPam.GetContentNode();
+
+ // if start- and end-content is zero, then the doc-compare moves
+ // complete nodes into the current doc. And then the selection
+ // must be from end to start, so the delete join into the right
+ // direction.
+ if( !m_nSttContent && !m_nEndContent )
+ rPam.Exchange();
+
+ bool bJoinText, bJoinPrev;
+ sw_GetJoinFlags(rPam, bJoinText, bJoinPrev);
+
+ m_pUndoDelete.reset(new SwUndoDelete(rPam, SwDeleteFlags::Default, false));
+
+ if( bJoinText )
+ sw_JoinText(rPam, bJoinPrev);
+
+ if( pCSttNd && !pCEndNd)
+ {
+ // #112139# Do not step behind the end of content.
+ SwNode & rTmp = rPam.GetNode();
+ SwNode * pEnd = rDoc.GetNodes().DocumentSectionEndNode(&rTmp);
+
+ if (&rTmp != pEnd)
+ {
+ rPam.SetMark();
+ ++rPam.GetPoint()->nNode;
+ rPam.GetBound().nContent.Assign( nullptr, 0 );
+ rPam.GetBound( false ).nContent.Assign( nullptr, 0 );
+ m_pUndoDelete2.reset(new SwUndoDelete(rPam, SwDeleteFlags::Default, true));
+ }
+ }
+ rPam.DeleteMark();
+ }
+ else
+ {
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+
+ if( m_pRedlineSaveDatas )
+ SetSaveData(rDoc, *m_pRedlineSaveDatas);
+ }
+ SetPaM(rPam, true);
+ }
+}
+
+void SwUndoCompDoc::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+
+ if( m_bInsert )
+ {
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ SwRangeRedline* pTmp = new SwRangeRedline(*m_pRedlineData, rPam);
+ rDoc.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp );
+ pTmp->InvalidateRange(SwRangeRedline::Invalidation::Add);
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ rDoc.getIDocumentRedlineAccess().SplitRedline(rPam);
+ }
+ SetPaM(rPam, true);
+ }
+ else
+ {
+ if( m_pUndoDelete2 )
+ {
+ m_pUndoDelete2->UndoImpl(rContext);
+ m_pUndoDelete2.reset();
+ }
+ m_pUndoDelete->UndoImpl(rContext);
+ m_pUndoDelete.reset();
+
+ // note: don't call SetPaM before executing Undo of members
+ SwPaM& rPam(AddUndoRedoPaM(rContext));
+
+ SwRangeRedline* pTmp = new SwRangeRedline(*m_pRedlineData, rPam);
+ rDoc.getIDocumentRedlineAccess().GetRedlineTable().Insert( pTmp );
+ pTmp->InvalidateRange(SwRangeRedline::Invalidation::Add);
+
+ SetPaM(rPam, true);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unsect.cxx b/sw/source/core/undo/unsect.cxx
new file mode 100644
index 000000000..d7cd60ba3
--- /dev/null
+++ b/sw/source/core/undo/unsect.cxx
@@ -0,0 +1,610 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <UndoSection.hxx>
+
+#include <editeng/protitem.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/scopeguard.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <fmtcntnt.hxx>
+#include <doc.hxx>
+#include <IDocumentLinksAdministration.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <poolfmt.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <section.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <doctxm.hxx>
+#include <ftnidx.hxx>
+#include <rootfrm.hxx>
+#include <editsh.hxx>
+/// OD 04.10.2002 #102894#
+/// class Calc needed for calculation of the hidden condition of a section.
+#include <calc.hxx>
+
+static std::optional<SfxItemSet> lcl_GetAttrSet( const SwSection& rSect )
+{
+ // save attributes of the format (columns, color, ...)
+ // Content and Protect items are not interesting since they are already
+ // stored in Section, thus delete them.
+ std::optional<SfxItemSet> oAttr;
+ if( rSect.GetFormat() )
+ {
+ sal_uInt16 nCnt = 1;
+ if( rSect.IsProtect() )
+ ++nCnt;
+
+ if( nCnt < rSect.GetFormat()->GetAttrSet().Count() )
+ {
+ oAttr.emplace( rSect.GetFormat()->GetAttrSet() );
+ oAttr->ClearItem( RES_PROTECT );
+ oAttr->ClearItem( RES_CNTNT );
+ if( !oAttr->Count() )
+ {
+ oAttr.reset();
+ }
+ }
+ }
+ return oAttr;
+}
+
+SwUndoInsSection::SwUndoInsSection(
+ SwPaM const& rPam, SwSectionData const& rNewData,
+ SfxItemSet const*const pSet,
+ std::tuple<SwTOXBase const*, sw::RedlineMode, sw::FieldmarkMode> const*const pTOXBase)
+ : SwUndo( SwUndoId::INSSECTION, &rPam.GetDoc() ), SwUndRng( rPam )
+ , m_pSectionData(new SwSectionData(rNewData))
+ , m_pAttrSet( (pSet && pSet->Count()) ? new SfxItemSet( *pSet ) : nullptr )
+ , m_nSectionNodePos(0)
+ , m_bSplitAtStart(false)
+ , m_bSplitAtEnd(false)
+ , m_bUpdateFootnote(false)
+{
+ if (pTOXBase)
+ m_xTOXBase.emplace(
+ std::make_unique<SwTOXBase>(*std::get<0>(*pTOXBase)),
+ std::get<1>(*pTOXBase),
+ std::get<2>(*pTOXBase));
+
+ SwDoc& rDoc = rPam.GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlData.reset(new SwRedlineData( RedlineType::Insert,
+ rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ));
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+ m_pRedlineSaveData.reset( new SwRedlineSaveDatas );
+ if( !FillSaveData( rPam, *m_pRedlineSaveData, false ))
+ m_pRedlineSaveData.reset();
+
+ if( rPam.HasMark() )
+ return;
+
+ const SwContentNode* pCNd = rPam.GetPoint()->nNode.GetNode().GetContentNode();
+ if( pCNd && pCNd->HasSwAttrSet() && (
+ !rPam.GetPoint()->nContent.GetIndex() ||
+ rPam.GetPoint()->nContent.GetIndex() == pCNd->Len() ))
+ {
+ SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange );
+ aBrkSet.Put( *pCNd->GetpSwAttrSet() );
+ if( aBrkSet.Count() )
+ {
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->CopyFormatAttr( aBrkSet, pCNd->GetIndex() );
+ }
+ }
+}
+
+SwUndoInsSection::~SwUndoInsSection()
+{
+}
+
+void SwUndoInsSection::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ RemoveIdxFromSection( rDoc, m_nSectionNodePos );
+
+ SwSectionNode *const pNd =
+ rDoc.GetNodes()[ m_nSectionNodePos ]->GetSectionNode();
+ OSL_ENSURE( pNd, "where is my SectionNode?" );
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pNd, true, RedlineType::Any );
+
+ // no selection?
+ SwNodeIndex aIdx( *pNd );
+ if( ( !m_nEndNode && COMPLETE_STRING == m_nEndContent ) ||
+ ( m_nSttNode == m_nEndNode && m_nSttContent == m_nEndContent ))
+ // delete simply all nodes
+ rDoc.GetNodes().Delete( aIdx, pNd->EndOfSectionIndex() -
+ aIdx.GetIndex() );
+ else
+ // just delete format, rest happens automatically
+ rDoc.DelSectionFormat( pNd->GetSection().GetFormat() );
+
+ // do we need to consolidate?
+ if (m_bSplitAtStart)
+ {
+ Join( rDoc, m_nSttNode );
+ }
+
+ if (m_bSplitAtEnd)
+ {
+ Join( rDoc, m_nEndNode );
+ }
+
+ if (m_pHistory)
+ {
+ m_pHistory->TmpRollback( &rDoc, 0, false );
+ }
+
+ if (m_bUpdateFootnote)
+ {
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aIdx );
+ }
+
+ AddUndoRedoPaM(rContext);
+
+ if (m_pRedlineSaveData)
+ SetSaveData( rDoc, *m_pRedlineSaveData );
+}
+
+void SwUndoInsSection::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+
+ const SwTOXBaseSection* pUpdateTOX = nullptr;
+ if (m_xTOXBase)
+ {
+ SwRootFrame const* pLayout(nullptr);
+ SwRootFrame * pLayoutToReset(nullptr);
+ sw::FieldmarkMode eFieldmarkMode{};
+ comphelper::ScopeGuard g([&]() {
+ if (pLayoutToReset)
+ {
+ pLayoutToReset->SetHideRedlines(std::get<1>(*m_xTOXBase) == sw::RedlineMode::Shown);
+ pLayoutToReset->SetFieldmarkMode(eFieldmarkMode);
+ }
+ });
+ o3tl::sorted_vector<SwRootFrame *> layouts(rDoc.GetAllLayouts());
+ for (SwRootFrame const*const p : layouts)
+ {
+ if ((std::get<1>(*m_xTOXBase) == sw::RedlineMode::Hidden) == p->IsHideRedlines()
+ && std::get<2>(*m_xTOXBase) == p->GetFieldmarkMode())
+ {
+ pLayout = p;
+ break;
+ }
+ }
+ if (!pLayout)
+ {
+ assert(!layouts.empty()); // must have one layout
+ pLayoutToReset = *layouts.begin();
+ eFieldmarkMode = pLayoutToReset->GetFieldmarkMode();
+ pLayoutToReset->SetHideRedlines(std::get<1>(*m_xTOXBase) == sw::RedlineMode::Hidden);
+ pLayoutToReset->SetFieldmarkMode(std::get<2>(*m_xTOXBase));
+ pLayout = pLayoutToReset;
+ }
+ pUpdateTOX = rDoc.InsertTableOf( *rPam.GetPoint(),
+ // don't expand: will be done by SwUndoUpdateIndex::RedoImpl()
+ *std::get<0>(*m_xTOXBase), m_pAttrSet.get(), false, pLayout);
+ }
+ else
+ {
+ rDoc.InsertSwSection(rPam, *m_pSectionData, nullptr, m_pAttrSet.get());
+ }
+
+ if (m_pHistory)
+ {
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ SwSectionNode *const pSectNd =
+ rDoc.GetNodes()[ m_nSectionNodePos ]->GetSectionNode();
+ if (m_pRedlData &&
+ IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags()))
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+
+ SwPaM aPam( *pSectNd->EndOfSectionNode(), *pSectNd, SwNodeOffset(1) );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlData, aPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ {
+ SwPaM aPam( *pSectNd->EndOfSectionNode(), *pSectNd, SwNodeOffset(1) );
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+ }
+
+ if( pUpdateTOX )
+ {
+ // initiate formatting
+ if (SwEditShell* pESh = rDoc.GetEditShell())
+ pESh->CalcLayout();
+
+ // insert page numbers
+ const_cast<SwTOXBaseSection*>(pUpdateTOX)->UpdatePageNum();
+ }
+}
+
+void SwUndoInsSection::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if (m_xTOXBase)
+ {
+ rDoc.InsertTableOf(*rContext.GetRepeatPaM().GetPoint(),
+ *std::get<0>(*m_xTOXBase), m_pAttrSet.get(), true,
+ rDoc.getIDocumentLayoutAccess().GetCurrentLayout()); // TODO add shell to RepeatContext?
+ }
+ else
+ {
+ rDoc.InsertSwSection(rContext.GetRepeatPaM(),
+ *m_pSectionData, nullptr, m_pAttrSet.get());
+ }
+}
+
+void SwUndoInsSection::Join( SwDoc& rDoc, SwNodeOffset nNode )
+{
+ SwNodeIndex aIdx( rDoc.GetNodes(), nNode );
+ SwTextNode* pTextNd = aIdx.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "Where is my TextNode?" );
+
+ {
+ RemoveIdxRel(
+ nNode + 1,
+ SwPosition( aIdx, SwIndex( pTextNd, pTextNd->GetText().getLength() ) ) );
+ }
+ pTextNd->JoinNext();
+
+ if (m_pHistory)
+ {
+ SwIndex aCntIdx( pTextNd, 0 );
+ pTextNd->RstTextAttr( aCntIdx, pTextNd->Len(), 0, nullptr, true );
+ }
+}
+
+void
+SwUndoInsSection::SaveSplitNode(SwTextNode *const pTextNd, bool const bAtStart)
+{
+ if( pTextNd->GetpSwpHints() )
+ {
+ if (!m_pHistory)
+ {
+ m_pHistory.reset( new SwHistory );
+ }
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), pTextNd->GetIndex(), 0,
+ pTextNd->GetText().getLength(), false );
+ }
+
+ if (bAtStart)
+ {
+ m_bSplitAtStart = true;
+ }
+ else
+ {
+ m_bSplitAtEnd = true;
+ }
+}
+
+class SwUndoDelSection
+ : public SwUndo
+{
+private:
+ std::unique_ptr<SwSectionData> const m_pSectionData; /// section not TOX
+ std::unique_ptr<SwTOXBase> const m_pTOXBase; /// set iff section is TOX
+ std::optional<SfxItemSet> const m_oAttrSet;
+ std::shared_ptr< ::sfx2::MetadatableUndo > const m_pMetadataUndo;
+ SwNodeOffset const m_nStartNode;
+ SwNodeOffset const m_nEndNode;
+
+public:
+ SwUndoDelSection(
+ SwSectionFormat const&, SwSection const&, SwNodeIndex const*const);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+std::unique_ptr<SwUndo> MakeUndoDelSection(SwSectionFormat const& rFormat)
+{
+ return std::make_unique<SwUndoDelSection>(rFormat, *rFormat.GetSection(),
+ rFormat.GetContent().GetContentIdx());
+}
+
+SwUndoDelSection::SwUndoDelSection(
+ SwSectionFormat const& rSectionFormat, SwSection const& rSection,
+ SwNodeIndex const*const pIndex)
+ : SwUndo( SwUndoId::DELSECTION, rSectionFormat.GetDoc() )
+ , m_pSectionData( new SwSectionData(rSection) )
+ , m_pTOXBase( dynamic_cast<const SwTOXBaseSection*>( &rSection) != nullptr
+ ? new SwTOXBase(static_cast<SwTOXBaseSection const&>(rSection))
+ : nullptr )
+ , m_oAttrSet( ::lcl_GetAttrSet(rSection) )
+ , m_pMetadataUndo( rSectionFormat.CreateUndo() )
+ , m_nStartNode( pIndex->GetIndex() )
+ , m_nEndNode( pIndex->GetNode().EndOfSectionIndex() )
+{
+}
+
+void SwUndoDelSection::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if (m_pTOXBase)
+ {
+ // sw_redlinehide: this should work as-is; there will be another undo for the update
+ rDoc.InsertTableOf(m_nStartNode, m_nEndNode-2, *m_pTOXBase,
+ m_oAttrSet ? &*m_oAttrSet : nullptr);
+ }
+ else
+ {
+ SwNodeIndex aStt( rDoc.GetNodes(), m_nStartNode );
+ SwNodeIndex aEnd( rDoc.GetNodes(), m_nEndNode-2 );
+ SwSectionFormat* pFormat = rDoc.MakeSectionFormat();
+ if (m_oAttrSet)
+ {
+ pFormat->SetFormatAttr( *m_oAttrSet );
+ }
+
+ /// OD 04.10.2002 #102894#
+ /// remember inserted section node for further calculations
+ SwSectionNode* pInsertedSectNd = rDoc.GetNodes().InsertTextSection(
+ aStt, *pFormat, *m_pSectionData, nullptr, & aEnd);
+
+ if( SfxItemState::SET == pFormat->GetItemState( RES_FTN_AT_TXTEND ) ||
+ SfxItemState::SET == pFormat->GetItemState( RES_END_AT_TXTEND ))
+ {
+ rDoc.GetFootnoteIdxs().UpdateFootnote( aStt );
+ }
+
+ /// OD 04.10.2002 #102894#
+ /// consider that section is hidden by condition.
+ /// If section is hidden by condition,
+ /// recalculate condition and update hidden condition flag.
+ /// Recalculation is necessary, because fields, on which the hide
+ /// condition depends, can be changed - fields changes aren't undoable.
+ /// NOTE: setting hidden condition flag also creates/deletes corresponding
+ /// frames, if the hidden condition flag changes.
+ SwSection& aInsertedSect = pInsertedSectNd->GetSection();
+ if ( aInsertedSect.IsHidden() &&
+ !aInsertedSect.GetCondition().isEmpty() )
+ {
+ SwCalc aCalc( rDoc );
+ rDoc.getIDocumentFieldsAccess().FieldsToCalc(aCalc, pInsertedSectNd->GetIndex(), SAL_MAX_INT32);
+ bool bRecalcCondHidden =
+ aCalc.Calculate( aInsertedSect.GetCondition() ).GetBool();
+ aInsertedSect.SetCondHidden( bRecalcCondHidden );
+ }
+
+ pFormat->RestoreMetadata(m_pMetadataUndo);
+ }
+}
+
+void SwUndoDelSection::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwSectionNode *const pNd =
+ rDoc.GetNodes()[ m_nStartNode ]->GetSectionNode();
+ OSL_ENSURE( pNd, "Where is my SectionNode?" );
+ // just delete format, rest happens automatically
+ rDoc.DelSectionFormat( pNd->GetSection().GetFormat() );
+}
+
+namespace {
+
+class SwUndoUpdateSection
+ : public SwUndo
+{
+private:
+ std::unique_ptr<SwSectionData> m_pSectionData;
+ std::optional<SfxItemSet> m_oAttrSet;
+ SwNodeOffset const m_nStartNode;
+ bool const m_bOnlyAttrChanged;
+
+public:
+ SwUndoUpdateSection(
+ SwSection const&, SwNodeIndex const*const, bool const bOnlyAttr);
+
+ virtual void UndoImpl( ::sw::UndoRedoContext & ) override;
+ virtual void RedoImpl( ::sw::UndoRedoContext & ) override;
+};
+
+}
+
+std::unique_ptr<SwUndo>
+MakeUndoUpdateSection(SwSectionFormat const& rFormat, bool const bOnlyAttr)
+{
+ return std::make_unique<SwUndoUpdateSection>(*rFormat.GetSection(),
+ rFormat.GetContent().GetContentIdx(), bOnlyAttr);
+}
+
+SwUndoUpdateSection::SwUndoUpdateSection(
+ SwSection const& rSection, SwNodeIndex const*const pIndex,
+ bool const bOnlyAttr)
+ : SwUndo( SwUndoId::CHGSECTION, &pIndex->GetNode().GetDoc() )
+ , m_pSectionData( new SwSectionData(rSection) )
+ , m_oAttrSet( ::lcl_GetAttrSet(rSection) )
+ , m_nStartNode( pIndex->GetIndex() )
+ , m_bOnlyAttrChanged( bOnlyAttr )
+{
+}
+
+void SwUndoUpdateSection::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwSectionNode *const pSectNd =
+ rDoc.GetNodes()[ m_nStartNode ]->GetSectionNode();
+ OSL_ENSURE( pSectNd, "Where is my SectionNode?" );
+
+ SwSection& rNdSect = pSectNd->GetSection();
+ SwFormat* pFormat = rNdSect.GetFormat();
+
+ std::optional<SfxItemSet> oCur = ::lcl_GetAttrSet( rNdSect );
+ if (m_oAttrSet)
+ {
+ // The Content and Protect items must persist
+ m_oAttrSet->Put( pFormat->GetFormatAttr( RES_CNTNT ));
+ if( const SvxProtectItem* pItem = pFormat->GetItemIfSet( RES_PROTECT ))
+ {
+ m_oAttrSet->Put( *pItem );
+ }
+ pFormat->DelDiffs( *m_oAttrSet );
+ m_oAttrSet->ClearItem( RES_CNTNT );
+ pFormat->SetFormatAttr( *m_oAttrSet );
+ }
+ else
+ {
+ // than the old ones need to be deleted
+ pFormat->ResetFormatAttr( RES_FRMATR_BEGIN, RES_BREAK );
+ pFormat->ResetFormatAttr( RES_HEADER, RES_OPAQUE );
+ pFormat->ResetFormatAttr( RES_SURROUND, RES_FRMATR_END-1 );
+ }
+ if (oCur)
+ m_oAttrSet.emplace(std::move(*oCur));
+ else
+ m_oAttrSet.reset();
+
+ if (m_bOnlyAttrChanged)
+ return;
+
+ const bool bUpdate =
+ (!rNdSect.IsLinkType() && m_pSectionData->IsLinkType())
+ || ( !m_pSectionData->GetLinkFileName().isEmpty()
+ && (m_pSectionData->GetLinkFileName() !=
+ rNdSect.GetLinkFileName()));
+
+ // swap stored section data with live section data
+ SwSectionData *const pOld( new SwSectionData(rNdSect) );
+ rNdSect.SetSectionData(*m_pSectionData);
+ m_pSectionData.reset(pOld);
+
+ if( bUpdate )
+ rNdSect.CreateLink( LinkCreateType::Update );
+ else if( SectionType::Content == rNdSect.GetType() && rNdSect.IsConnected() )
+ {
+ rNdSect.Disconnect();
+ rDoc.getIDocumentLinksAdministration().GetLinkManager().Remove( &rNdSect.GetBaseLink() );
+ }
+}
+
+void SwUndoUpdateSection::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoImpl(rContext);
+}
+
+
+SwUndoUpdateIndex::SwUndoUpdateIndex(SwTOXBaseSection & rTOX)
+ : SwUndo(SwUndoId::INSSECTION, rTOX.GetFormat()->GetDoc())
+ , m_pSaveSectionOriginal(new SwUndoSaveSection)
+ , m_pSaveSectionUpdated(new SwUndoSaveSection)
+ , m_nStartIndex(rTOX.GetFormat()->GetSectionNode()->GetIndex() + 1)
+{
+ SwDoc & rDoc(*rTOX.GetFormat()->GetDoc());
+ assert(rDoc.GetNodes()[m_nStartIndex-1]->IsSectionNode());
+ assert(rDoc.GetNodes()[rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex()-1]->IsTextNode()); // -1 for extra empty node
+ // note: title is optional
+ assert(rDoc.GetNodes()[m_nStartIndex]->IsTextNode()
+ || rDoc.GetNodes()[m_nStartIndex]->IsSectionNode());
+ SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex);
+ if (first.GetNode().IsSectionNode())
+ {
+ SwSectionFormat & rSectionFormat(*first.GetNode().GetSectionNode()->GetSection().GetFormat());
+ // note: DelSectionFormat will create & append SwUndoDelSection!
+ rDoc.DelSectionFormat(& rSectionFormat); // remove inner section nodes
+ }
+ assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty
+ SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 2); // skip empty node
+ assert(last.GetNode().IsTextNode());
+ m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false);
+}
+
+SwUndoUpdateIndex::~SwUndoUpdateIndex() = default;
+
+void SwUndoUpdateIndex::TitleSectionInserted(SwSectionFormat & rFormat)
+{
+ SwNodeIndex const tmp(rFormat.GetDoc()->GetNodes(), m_nStartIndex); // title inserted before empty node
+ assert(tmp.GetNode().IsSectionNode());
+ assert(tmp.GetNode().GetSectionNode()->GetSection().GetFormat() == &rFormat);
+ m_pTitleSectionUpdated.reset(static_cast<SwUndoDelSection*>(MakeUndoDelSection(rFormat).release()));
+}
+
+void SwUndoUpdateIndex::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ if (m_pTitleSectionUpdated)
+ {
+ m_pTitleSectionUpdated->RedoImpl(rContext);
+ }
+ SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex);
+ assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty
+ SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 1);
+ assert(last.GetNode().IsTextNode());
+ // dummy node so that SaveSection doesn't remove ToX section...
+ SwTextNode *const pDeletionPrevention = rDoc.GetNodes().MakeTextNode(
+ SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode()),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT));
+ m_pSaveSectionUpdated->SaveSection(SwNodeRange(first, last), false);
+ m_pSaveSectionOriginal->RestoreSection(&rDoc, first, true);
+ // delete before restoring nested undo, so its node indexes match
+ SwNodeIndex const del(*pDeletionPrevention);
+ SwDoc::CorrAbs(del, del, SwPosition(SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode())), true);
+ rDoc.GetNodes().Delete(del);
+ // original title section will be restored by next Undo, see ctor!
+}
+
+void SwUndoUpdateIndex::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc(rContext.GetDoc());
+ // original title section was deleted by previous Undo, see ctor!
+ SwNodeIndex const first(rDoc.GetNodes(), m_nStartIndex);
+ assert(first.GetNode().IsTextNode()); // invariant: ToX section is *never* empty
+ SwNodeIndex const last(rDoc.GetNodes(), rDoc.GetNodes()[m_nStartIndex]->EndOfSectionIndex() - 1);
+ assert(last.GetNode().IsTextNode());
+ // dummy node so that SaveSection doesn't remove ToX section...
+ SwTextNode *const pDeletionPrevention = rDoc.GetNodes().MakeTextNode(
+ SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode()),
+ rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_TEXT));
+ m_pSaveSectionOriginal->SaveSection(SwNodeRange(first, last), false);
+ m_pSaveSectionUpdated->RestoreSection(&rDoc, first, true);
+ // delete before restoring nested undo, so its node indexes match
+ SwNodeIndex const del(*pDeletionPrevention);
+ SwDoc::CorrAbs(del, del, SwPosition(SwNodeIndex(*rDoc.GetNodes()[m_nStartIndex]->EndOfSectionNode())), true);
+ rDoc.GetNodes().Delete(del);
+ if (m_pTitleSectionUpdated)
+ {
+ m_pTitleSectionUpdated->UndoImpl(rContext);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unsort.cxx b/sw/source/core/undo/unsort.cxx
new file mode 100644
index 000000000..44468c165
--- /dev/null
+++ b/sw/source/core/undo/unsort.cxx
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <UndoSort.hxx>
+#include <doc.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <UndoTable.hxx>
+#include <sortopt.hxx>
+#include <docsort.hxx>
+#include <node2lay.hxx>
+
+// Undo for Sorting
+SwSortUndoElement::~SwSortUndoElement()
+{
+ // are there string pointers saved?
+ if( 0xffffffff != SORT_TXT_TBL.TXT.nID )
+ {
+ delete SORT_TXT_TBL.TBL.pSource;
+ delete SORT_TXT_TBL.TBL.pTarget;
+ }
+}
+
+SwUndoSort::SwUndoSort(const SwPaM& rRg, const SwSortOptions& rOpt)
+ : SwUndo(SwUndoId::SORT_TXT, &rRg.GetDoc())
+ , SwUndRng(rRg)
+ , m_nTableNode(0)
+{
+ m_pSortOptions.reset( new SwSortOptions(rOpt) );
+}
+
+SwUndoSort::SwUndoSort( SwNodeOffset nStt, SwNodeOffset nEnd, const SwTableNode& rTableNd,
+ const SwSortOptions& rOpt, bool bSaveTable )
+ : SwUndo(SwUndoId::SORT_TBL, &rTableNd.GetDoc())
+{
+ m_nSttNode = nStt;
+ m_nEndNode = nEnd;
+ m_nTableNode = rTableNd.GetIndex();
+
+ m_pSortOptions.reset( new SwSortOptions(rOpt) );
+ if( bSaveTable )
+ m_pUndoAttrTable.reset( new SwUndoAttrTable( rTableNd ) );
+}
+
+SwUndoSort::~SwUndoSort()
+{
+ m_pSortOptions.reset();
+ m_pUndoAttrTable.reset();
+}
+
+void SwUndoSort::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ if(m_pSortOptions->bTable)
+ {
+ // Undo for Table
+ RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode );
+
+ if( m_pUndoAttrTable )
+ {
+ m_pUndoAttrTable->UndoImpl(rContext);
+ }
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
+
+ pTableNd->DelFrames();
+ const SwTable& rTable = pTableNd->GetTable();
+
+ SwMovedBoxes aMovedList;
+ for (const std::unique_ptr<SwSortUndoElement> & i : m_SortList)
+ {
+ const SwTableBox* pSource = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pSource );
+ const SwTableBox* pTarget = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pTarget );
+
+ // move back
+ MoveCell(&rDoc, pTarget, pSource,
+ USHRT_MAX != aMovedList.GetPos(pSource) );
+
+ // store moved entry in list
+ aMovedList.push_back(pTarget);
+ }
+
+ // Restore table frames:
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ const SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
+ }
+ else
+ {
+ // Undo for Text
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ RemoveIdxFromRange(rPam, true);
+
+ // create index for (sorted) positions
+ // The IndexList must be created based on (asc.) sorted SourcePosition.
+ std::vector<SwNodeIndex> aIdxList;
+ aIdxList.reserve(m_SortList.size());
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ for (const std::unique_ptr<SwSortUndoElement> & j : m_SortList)
+ {
+ if (j->SORT_TXT_TBL.TXT.nSource == sal_Int32(m_nSttNode + SwNodeOffset(i)))
+ {
+ aIdxList.push_back( SwNodeIndex( rDoc.GetNodes(),
+ j->SORT_TXT_TBL.TXT.nTarget ) );
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + SwNodeOffset(i) );
+ SwNodeRange aRg( aIdxList[i], SwNodeOffset(0), aIdxList[i], SwNodeOffset(1) );
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx,
+ SwMoveFlags::DEFAULT);
+ }
+ // delete indices
+ aIdxList.clear();
+ SetPaM(rPam, true);
+ }
+}
+
+void SwUndoSort::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ if(m_pSortOptions->bTable)
+ {
+ // Redo for Table
+ RemoveIdxFromSection( rDoc, m_nSttNode, &m_nEndNode );
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
+
+ pTableNd->DelFrames();
+ const SwTable& rTable = pTableNd->GetTable();
+
+ SwMovedBoxes aMovedList;
+ for (const std::unique_ptr<SwSortUndoElement> & i : m_SortList)
+ {
+ const SwTableBox* pSource = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pSource );
+ const SwTableBox* pTarget = rTable.GetTableBox(
+ *i->SORT_TXT_TBL.TBL.pTarget );
+
+ // move back
+ MoveCell(&rDoc, pSource, pTarget,
+ USHRT_MAX != aMovedList.GetPos( pTarget ) );
+ // store moved entry in list
+ aMovedList.push_back( pSource );
+ }
+
+ if( m_pUndoAttrTable )
+ {
+ m_pUndoAttrTable->RedoImpl(rContext);
+ }
+
+ // Restore table frames:
+ // #i37739# A simple 'MakeFrames' after the node sorting
+ // does not work if the table is inside a frame and has no prev/next.
+ const SwNodeOffset nIdx = pTableNd->GetIndex();
+ aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(), nIdx, nIdx + 1 );
+ }
+ else
+ {
+ // Redo for Text
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ SetPaM(rPam);
+ RemoveIdxFromRange(rPam, true);
+
+ std::vector<SwNodeIndex> aIdxList;
+ aIdxList.reserve(m_SortList.size());
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ { // current position is starting point
+ aIdxList.push_back( SwNodeIndex( rDoc.GetNodes(),
+ m_SortList[i]->SORT_TXT_TBL.TXT.nSource) );
+ }
+
+ for (size_t i = 0; i < m_SortList.size(); ++i)
+ {
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode + SwNodeOffset(i));
+ SwNodeRange aRg( aIdxList[i], SwNodeOffset(0), aIdxList[i], SwNodeOffset(1) );
+ rDoc.getIDocumentContentOperations().MoveNodeRange(aRg, aIdx,
+ SwMoveFlags::DEFAULT);
+ }
+ // delete indices
+ aIdxList.clear();
+ SetPaM(rPam, true);
+ SwTextNode const*const pTNd = rPam.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ rPam.GetPoint()->nContent = pTNd->GetText().getLength();
+ }
+ }
+}
+
+void SwUndoSort::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // table not repeat capable
+ if(!m_pSortOptions->bTable)
+ {
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwDoc& rDoc = pPam->GetDoc();
+
+ if( !rDoc.IsIdxInTable( pPam->Start()->nNode ) )
+ rDoc.SortText(*pPam, *m_pSortOptions);
+ }
+}
+
+void SwUndoSort::Insert( const OUString& rOrgPos, const OUString& rNewPos)
+{
+ m_SortList.push_back(std::make_unique< SwSortUndoElement>(rOrgPos, rNewPos));
+}
+
+void SwUndoSort::Insert( SwNodeOffset nOrgPos, SwNodeOffset nNewPos)
+{
+ m_SortList.push_back(std::make_unique<SwSortUndoElement>(nOrgPos, nNewPos));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/unspnd.cxx b/sw/source/core/undo/unspnd.cxx
new file mode 100644
index 000000000..2642ae5f0
--- /dev/null
+++ b/sw/source/core/undo/unspnd.cxx
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <UndoSplitMove.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <pam.hxx>
+#include <swtable.hxx>
+#include <ndtxt.hxx>
+#include <swcrsr.hxx>
+#include <swundo.hxx>
+#include <frmfmt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <docary.hxx>
+#include <fmtpdsc.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <osl/diagnose.h>
+#include <editeng/formatbreakitem.hxx>
+
+// SPLITNODE
+
+SwUndoSplitNode::SwUndoSplitNode( SwDoc& rDoc, const SwPosition& rPos,
+ bool bChkTable )
+ : SwUndo( SwUndoId::SPLITNODE, &rDoc ), m_nNode( rPos.nNode.GetIndex() ),
+ m_nContent( rPos.nContent.GetIndex() ),
+ m_bTableFlag( false ), m_bCheckTableStart( bChkTable )
+{
+ SwTextNode *const pTextNd = rPos.nNode.GetNode().GetTextNode();
+ OSL_ENSURE( pTextNd, "only for TextNode" );
+ if( pTextNd->GetpSwpHints() )
+ {
+ m_pHistory.reset(new SwHistory);
+ m_pHistory->CopyAttr(pTextNd->GetpSwpHints(), m_nNode, 0,
+ pTextNd->GetText().getLength(), false );
+ if (!m_pHistory->Count())
+ {
+ m_pHistory.reset();
+ }
+ }
+ // consider Redline
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ m_nParRsid = pTextNd->GetParRsid();
+}
+
+SwUndoSplitNode::~SwUndoSplitNode()
+{
+ m_pHistory.reset();
+ m_pRedlineData.reset();
+}
+
+void SwUndoSplitNode::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwCursor & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.DeleteMark();
+ if( m_bTableFlag )
+ {
+ // than a TextNode was added directly before the current table
+ SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
+ rIdx = m_nNode;
+ SwTextNode* pTNd;
+ SwNode* pCurrNd = pDoc->GetNodes()[ m_nNode + 1 ];
+ SwTableNode* pTableNd = pCurrNd->FindTableNode();
+ if( pCurrNd->IsContentNode() && pTableNd &&
+ nullptr != ( pTNd = pDoc->GetNodes()[ pTableNd->GetIndex()-1 ]->GetTextNode() ))
+ {
+ // move break attributes
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+ const SfxItemSet* pNdSet = pTNd->GetpSwAttrSet();
+ if( pNdSet )
+ {
+ if( const SwFormatPageDesc* pItem = pNdSet->GetItemIfSet( RES_PAGEDESC, false ) )
+ pTableFormat->SetFormatAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pNdSet->GetItemIfSet( RES_BREAK, false ) )
+ pTableFormat->SetFormatAttr( *pItem );
+ }
+
+ // than delete it again
+ SwNodeIndex aDelNd( *pTableNd, -1 );
+ rPam.GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pCurrNd), 0 );
+ RemoveIdxRel( aDelNd.GetIndex(), *rPam.GetPoint() );
+ pDoc->GetNodes().Delete( aDelNd );
+ }
+ }
+ else
+ {
+ SwTextNode * pTNd = pDoc->GetNodes()[ m_nNode ]->GetTextNode();
+ if( pTNd )
+ {
+ rPam.GetPoint()->nNode = *pTNd;
+ rPam.GetPoint()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ rPam.SetMark();
+ ++rPam.GetMark()->nNode;
+ rPam.GetMark()->nContent.Assign( rPam.GetMark()->
+ nNode.GetNode().GetContentNode(), 0 );
+ pDoc->getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any );
+ rPam.DeleteMark();
+ }
+
+ RemoveIdxRel( m_nNode+1, *rPam.GetPoint() );
+
+ pTNd->JoinNext();
+ if (m_pHistory)
+ {
+ rPam.GetPoint()->nContent = 0;
+ rPam.SetMark();
+ rPam.GetPoint()->nContent = pTNd->GetText().getLength();
+
+ pDoc->RstTextAttrs( rPam, true );
+ m_pHistory->TmpRollback( pDoc, 0, false );
+ }
+
+ pDoc->UpdateParRsid( pTNd, m_nParRsid );
+ }
+ }
+
+ // also set the cursor onto undo section
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nNode;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), m_nContent );
+}
+
+void SwUndoSplitNode::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwCursor & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.GetPoint()->nNode = m_nNode;
+ SwTextNode * pTNd = rPam.GetNode().GetTextNode();
+ OSL_ENSURE(pTNd, "SwUndoSplitNode::RedoImpl(): SwTextNode expected");
+ if (!pTNd)
+ return;
+
+ rPam.GetPoint()->nContent.Assign( pTNd, m_nContent );
+
+ SwDoc& rDoc = rPam.GetDoc();
+ rDoc.getIDocumentContentOperations().SplitNode( *rPam.GetPoint(), m_bCheckTableStart );
+
+ if (m_pHistory)
+ {
+ m_pHistory->SetTmpEnd(m_pHistory->Count());
+ }
+
+ if( !(( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) ||
+ ( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )))
+ return;
+
+ rPam.SetMark();
+ if( rPam.Move( fnMoveBackward ))
+ {
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( rPam );
+ rPam.Exchange();
+ }
+ rPam.DeleteMark();
+}
+
+void SwUndoSplitNode::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().getIDocumentContentOperations().SplitNode(
+ *rContext.GetRepeatPaM().GetPoint(), m_bCheckTableStart );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untbl.cxx b/sw/source/core/undo/untbl.cxx
new file mode 100644
index 000000000..49453108b
--- /dev/null
+++ b/sw/source/core/undo/untbl.cxx
@@ -0,0 +1,3225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <UndoTable.hxx>
+#include <UndoRedline.hxx>
+#include <UndoDelete.hxx>
+#include <UndoSplitMove.hxx>
+#include <UndoCore.hxx>
+#include <fesh.hxx>
+#include <fmtpdsc.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <doc.hxx>
+#include <docredln.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <rootfrm.hxx>
+#include <editsh.hxx>
+#include <docary.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <pam.hxx>
+#include <tblsel.hxx>
+#include <swundo.hxx>
+#include <rolbck.hxx>
+#include <ddefld.hxx>
+#include <tabfrm.hxx>
+#include <tblafmt.hxx>
+#include <poolfmt.hxx>
+#include <mvsave.hxx>
+#include <cellatr.hxx>
+#include <swtblfmt.hxx>
+#include <swddetbl.hxx>
+#include <redline.hxx>
+#include <node2lay.hxx>
+#include <tblrwcl.hxx>
+#include <fmtanchr.hxx>
+#include <strings.hrc>
+#include <unochart.hxx>
+#include <calbck.hxx>
+#include <frameformats.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <osl/diagnose.h>
+#include <docsh.hxx>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#ifdef DBG_UTIL
+#define CHECK_TABLE(t) (t).CheckConsistency();
+#else
+#define CHECK_TABLE(t)
+#endif
+
+#ifdef DBG_UTIL
+ #define DEBUG_REDLINE( pDoc ) sw_DebugRedline( pDoc );
+#else
+ #define DEBUG_REDLINE( pDoc )
+#endif
+
+typedef std::vector<std::shared_ptr<SfxItemSet> > SfxItemSets;
+
+struct UndoTableCpyTable_Entry
+{
+ SwNodeOffset nBoxIdx, nOffset;
+ std::unique_ptr<SfxItemSet> pBoxNumAttr;
+ std::unique_ptr<SwUndo> pUndo;
+
+ // Was the last paragraph of the new and the first paragraph of the old content joined?
+ bool bJoin; // For redlining only
+
+ explicit UndoTableCpyTable_Entry( const SwTableBox& rBox );
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+};
+
+namespace {
+
+class SaveBox;
+class SaveLine;
+
+void KillEmptyFrameFormat(SwFrameFormat& rFormat)
+{
+ if(!rFormat.HasWriterListeners())
+ delete &rFormat;
+};
+
+}
+
+class SaveTable
+{
+ friend SaveBox;
+ friend SaveLine;
+ SfxItemSet m_aTableSet;
+ std::unique_ptr<SaveLine> m_pLine;
+ const SwTable* m_pSwTable;
+ SfxItemSets m_aSets;
+ SwFrameFormatsV m_aFrameFormats;
+ sal_uInt16 m_nLineCount;
+ bool m_bModifyBox : 1;
+ bool m_bSaveFormula : 1;
+ bool m_bNewModel : 1;
+
+ SaveTable(const SaveTable&) = delete;
+ SaveTable& operator=(const SaveTable&) = delete;
+ SwFrameFormat& CreateNewFormat(SwFrameFormat& rFormat, sal_uInt16 nFormatPos);
+
+public:
+ SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt = USHRT_MAX,
+ bool bSaveFormula = true );
+
+ sal_uInt16 AddFormat( SwFrameFormat* pFormat, bool bIsLine );
+ void NewFrameFormatForLine(const SwTableLine&, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat);
+ void NewFrameFormatForBox(const SwTableBox&, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat);
+
+ void RestoreAttr( SwTable& rTable, bool bModifyBox = false );
+ void SaveContentAttrs( SwDoc* pDoc );
+ void CreateNew( SwTable& rTable, bool bCreateFrames = true,
+ bool bRestoreChart = true );
+ bool IsNewModel() const { return m_bNewModel; }
+};
+
+namespace {
+
+class SaveLine
+{
+ friend SaveTable;
+ friend class SaveBox;
+
+ SaveLine* m_pNext;
+ SaveBox* m_pBox;
+ sal_uInt16 m_nItemSet;
+
+ SaveLine(const SaveLine&) = delete;
+ SaveLine& operator=(const SaveLine&) = delete;
+
+public:
+ SaveLine( SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable );
+ ~SaveLine();
+
+ void RestoreAttr( SwTableLine& rLine, SaveTable& rSTable );
+ void SaveContentAttrs( SwDoc* pDoc );
+
+ void CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable );
+};
+
+class SaveBox
+{
+ friend class SaveLine;
+
+ SaveBox* m_pNext;
+ SwNodeOffset m_nStartNode;
+ sal_Int32 m_nRowSpan;
+ sal_uInt16 m_nItemSet;
+ union
+ {
+ SfxItemSets* pContentAttrs;
+ SaveLine* pLine;
+ } m_Ptrs;
+
+public:
+ SaveBox( SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable );
+ ~SaveBox();
+
+ void RestoreAttr( SwTableBox& rBox, SaveTable& rSTable );
+ void SaveContentAttrs( SwDoc* pDoc );
+
+ void CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable );
+};
+
+}
+
+#if OSL_DEBUG_LEVEL > 0
+static void CheckTable( const SwTable& );
+#define CHECKTABLE(t) CheckTable( t );
+#else
+#define CHECKTABLE(t)
+#endif
+
+/* #130880: Crash in undo of table to text when the table has (freshly) merged cells
+The order of cell content nodes in the nodes array is not given by the recursive table structure.
+The algorithm must not rely on this even it holds for a fresh loaded table in odt file format.
+So we need to remember not only the start node position but the end node position as well.
+*/
+
+struct SwTableToTextSave
+{
+ SwNodeOffset m_nSttNd;
+ SwNodeOffset m_nEndNd;
+ sal_Int32 m_nContent;
+ std::unique_ptr<SwHistory> m_pHstry;
+ // metadata references for first and last paragraph in cell
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
+ std::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;
+
+ SwTableToTextSave( SwDoc& rDoc, SwNodeOffset nNd, SwNodeOffset nEndIdx, sal_Int32 nContent );
+
+private:
+ SwTableToTextSave(const SwTableToTextSave&) = delete;
+ SwTableToTextSave& operator=(const SwTableToTextSave&) = delete;
+
+};
+
+WhichRangesContainer const aSave_BoxContentSet(svl::Items<
+ RES_CHRATR_COLOR, RES_CHRATR_CROSSEDOUT,
+ RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
+ RES_CHRATR_POSTURE, RES_CHRATR_POSTURE,
+ RES_CHRATR_SHADOWED, RES_CHRATR_WEIGHT,
+ RES_PARATR_ADJUST, RES_PARATR_ADJUST>);
+
+SwUndoInsTable::SwUndoInsTable( const SwPosition& rPos, sal_uInt16 nCl, sal_uInt16 nRw,
+ sal_uInt16 nAdj, const SwInsertTableOptions& rInsTableOpts,
+ const SwTableAutoFormat* pTAFormat,
+ const std::vector<sal_uInt16> *pColArr,
+ const OUString & rName)
+ : SwUndo( SwUndoId::INSTABLE, &rPos.GetDoc() ),
+ m_aInsTableOptions( rInsTableOpts ),
+ m_nStartNode( rPos.nNode.GetIndex() ), m_nRows( nRw ), m_nColumns( nCl ), m_nAdjust( nAdj )
+{
+ if( pColArr )
+ {
+ m_pColumnWidth.reset( new std::vector<sal_uInt16>(*pColArr) );
+ }
+ if( pTAFormat )
+ m_pAutoFormat.reset( new SwTableAutoFormat( *pTAFormat ) );
+
+ // consider redline
+ SwDoc& rDoc = rPos.nNode.GetNode().GetDoc();
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+
+ m_sTableName = rName;
+}
+
+SwUndoInsTable::~SwUndoInsTable()
+{
+ m_pDDEFieldType.reset();
+ m_pColumnWidth.reset();
+ m_pRedlineData.reset();
+ m_pAutoFormat.reset();
+}
+
+void SwUndoInsTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nStartNode );
+
+ SwTableNode* pTableNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+ pTableNd->DelFrames();
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *pTableNd, true, RedlineType::Any );
+ RemoveIdxFromSection( rDoc, m_nStartNode );
+
+ // move hard page breaks into next node
+ SwContentNode* pNextNd = rDoc.GetNodes()[ pTableNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat();
+
+ if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ ::sw::NotifyTableCollapsedParagraph(pNextNd, nullptr);
+ }
+
+ m_sTableName = pTableNd->GetTable().GetFrameFormat()->GetName();
+ if( auto pDDETable = dynamic_cast<const SwDDETable *>(&pTableNd->GetTable()) )
+ m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release()));
+
+ rDoc.GetNodes().Delete( aIdx, pTableNd->EndOfSectionIndex() -
+ aIdx.GetIndex() + 1 );
+
+ SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = aIdx;
+ rPam.GetPoint()->nContent.Assign( rPam.GetContentNode(), 0 );
+}
+
+void SwUndoInsTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwEditShell *const pEditShell(rDoc.GetEditShell());
+ OSL_ENSURE(pEditShell, "SwUndoInsTable::RedoImpl needs a SwEditShell!");
+ if (!pEditShell)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwPosition const aPos(SwNodeIndex(rDoc.GetNodes(), m_nStartNode));
+ const SwTable* pTable = rDoc.InsertTable( m_aInsTableOptions, aPos, m_nRows, m_nColumns,
+ m_nAdjust,
+ m_pAutoFormat.get(), m_pColumnWidth.get() );
+ pEditShell->MoveTable( GotoPrevTable, fnTableStart );
+ static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetName( m_sTableName );
+ SwTableNode* pTableNode = rDoc.GetNodes()[m_nStartNode]->GetTableNode();
+
+ if( m_pDDEFieldType )
+ {
+ SwDDEFieldType* pNewType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ *m_pDDEFieldType));
+ std::unique_ptr<SwDDETable> pDDETable(new SwDDETable( pTableNode->GetTable(), pNewType ));
+ pTableNode->SetNewTable( std::move(pDDETable) );
+ m_pDDEFieldType.reset();
+ }
+
+ if( !((m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() )) ||
+ ( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )))
+ return;
+
+ SwPaM aPam( *pTableNode->EndOfSectionNode(), *pTableNode, SwNodeOffset(1) );
+ SwContentNode* pCNd = aPam.GetContentNode( false );
+ if( pCNd )
+ aPam.GetMark()->nContent.Assign( pCNd, 0 );
+
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) )
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld & ~RedlineFlags::Ignore);
+
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, aPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else
+ rDoc.getIDocumentRedlineAccess().SplitRedline( aPam );
+}
+
+void SwUndoInsTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ rContext.GetDoc().InsertTable(
+ m_aInsTableOptions, *rContext.GetRepeatPaM().GetPoint(),
+ m_nRows, m_nColumns, m_nAdjust, m_pAutoFormat.get(), m_pColumnWidth.get() );
+}
+
+SwRewriter SwUndoInsTable::GetRewriter() const
+{
+ SwRewriter aRewriter;
+
+ aRewriter.AddRule(UndoArg1, SwResId(STR_START_QUOTE));
+ aRewriter.AddRule(UndoArg2, m_sTableName);
+ aRewriter.AddRule(UndoArg3, SwResId(STR_END_QUOTE));
+
+ return aRewriter;
+}
+
+SwTableToTextSave::SwTableToTextSave( SwDoc& rDoc, SwNodeOffset nNd, SwNodeOffset nEndIdx, sal_Int32 nCnt )
+ : m_nSttNd( nNd ), m_nEndNd( nEndIdx), m_nContent( nCnt )
+{
+ // keep attributes of the joined node
+ SwTextNode* pNd = rDoc.GetNodes()[ nNd ]->GetTextNode();
+ if( pNd )
+ {
+ m_pHstry.reset( new SwHistory );
+
+ m_pHstry->Add( pNd->GetTextColl(), nNd, SwNodeType::Text );
+ if ( pNd->GetpSwpHints() )
+ {
+ m_pHstry->CopyAttr( pNd->GetpSwpHints(), nNd, 0,
+ pNd->GetText().getLength(), false );
+ }
+ if( pNd->HasSwAttrSet() )
+ m_pHstry->CopyFormatAttr( *pNd->GetpSwAttrSet(), nNd );
+
+ if( !m_pHstry->Count() )
+ {
+ m_pHstry.reset();
+ }
+
+ // METADATA: store
+ m_pMetadataUndoStart = pNd->CreateUndo();
+ }
+
+ // we also need to store the metadata reference of the _last_ paragraph
+ // we subtract 1 to account for the removed cell start/end node pair
+ // (after SectionUp, the end of the range points to the node after the cell)
+ if ( nEndIdx - 1 > nNd )
+ {
+ SwTextNode* pLastNode( rDoc.GetNodes()[ nEndIdx - 1 ]->GetTextNode() );
+ if( pLastNode )
+ {
+ // METADATA: store
+ m_pMetadataUndoEnd = pLastNode->CreateUndo();
+ }
+ }
+}
+
+SwUndoTableToText::SwUndoTableToText( const SwTable& rTable, sal_Unicode cCh )
+ : SwUndo( SwUndoId::TABLETOTEXT, rTable.GetFrameFormat()->GetDoc() ),
+ m_sTableName( rTable.GetFrameFormat()->GetName() ),
+ m_nStartNode( 0 ), m_nEndNode( 0 ),
+ m_cSeparator( cCh ), m_nHeadlineRepeat( rTable.GetRowsToRepeat() )
+{
+ m_pTableSave.reset( new SaveTable( rTable ) );
+ m_vBoxSaves.reserve(rTable.GetTabSortBoxes().size());
+
+ if( auto pDDETable = dynamic_cast<const SwDDETable *>(&rTable) )
+ m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release()));
+
+ m_bCheckNumFormat = rTable.GetFrameFormat()->GetDoc()->IsInsTableFormatNum();
+
+ m_pHistory.reset(new SwHistory);
+ const SwTableNode* pTableNd = rTable.GetTableNode();
+ SwNodeOffset nTableStt = pTableNd->GetIndex(), nTableEnd = pTableNd->EndOfSectionIndex();
+
+ const SwFrameFormats& rFrameFormatTable = *pTableNd->GetDoc().GetSpzFrameFormats();
+ for( size_t n = 0; n < rFrameFormatTable.size(); ++n )
+ {
+ SwFrameFormat* pFormat = rFrameFormatTable[ n ];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos &&
+ ((RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
+ (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) &&
+ nTableStt <= pAPos->nNode.GetIndex() &&
+ pAPos->nNode.GetIndex() < nTableEnd )
+ {
+ m_pHistory->AddChangeFlyAnchor(*pFormat);
+ }
+ }
+
+ if( !m_pHistory->Count() )
+ {
+ m_pHistory.reset();
+ }
+}
+
+SwUndoTableToText::~SwUndoTableToText()
+{
+ m_pDDEFieldType.reset();
+ m_pTableSave.reset();
+ m_vBoxSaves.clear();
+ m_pHistory.reset();
+}
+
+void SwUndoTableToText::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ SwNodeIndex aFrameIdx( rDoc.GetNodes(), m_nStartNode );
+ SwNodeIndex aEndIdx( rDoc.GetNodes(), m_nEndNode );
+
+ pPam->GetPoint()->nNode = aFrameIdx;
+ pPam->SetMark();
+ pPam->GetPoint()->nNode = aEndIdx;
+ rDoc.DelNumRules( *pPam );
+ pPam->DeleteMark();
+
+ // now collect all Uppers
+ SwNode2LayoutSaveUpperFrames aNode2Layout(aFrameIdx.GetNode());
+
+ // create TableNode structure
+ SwTableNode* pTableNd = rDoc.GetNodes().UndoTableToText( m_nStartNode, m_nEndNode, m_vBoxSaves );
+ pTableNd->GetTable().SetTableModel( m_pTableSave->IsNewModel() );
+ SwTableFormat* pTableFormat = rDoc.MakeTableFrameFormat( m_sTableName, rDoc.GetDfltFrameFormat() );
+ pTableNd->GetTable().RegisterToFormat( *pTableFormat );
+ pTableNd->GetTable().SetRowsToRepeat( m_nHeadlineRepeat );
+
+ // create old table structure
+ m_pTableSave->CreateNew( pTableNd->GetTable() );
+
+ if( m_pDDEFieldType )
+ {
+ SwDDEFieldType* pNewType = static_cast<SwDDEFieldType*>(rDoc.getIDocumentFieldsAccess().InsertFieldType(
+ *m_pDDEFieldType));
+ std::unique_ptr<SwDDETable> pDDETable( new SwDDETable( pTableNd->GetTable(), pNewType ) );
+ pTableNd->SetNewTable( std::move(pDDETable), false );
+ m_pDDEFieldType.reset();
+ }
+
+ if( m_bCheckNumFormat )
+ {
+ SwTableSortBoxes& rBxs = pTableNd->GetTable().GetTabSortBoxes();
+ for (size_t nBoxes = rBxs.size(); nBoxes; )
+ {
+ rDoc.ChkBoxNumFormat( *rBxs[ --nBoxes ], false );
+ }
+ }
+
+ if( m_pHistory )
+ {
+ sal_uInt16 nTmpEnd = m_pHistory->GetTmpEnd();
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( nTmpEnd );
+ }
+
+ aNode2Layout.RestoreUpperFrames( rDoc.GetNodes(),
+ pTableNd->GetIndex(), pTableNd->GetIndex()+1 );
+
+ // Is a table selection requested?
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
+ pPam->SetMark();
+ pPam->GetPoint()->nNode = *pPam->GetNode().StartOfSectionNode();
+ pPam->Move( fnMoveForward, GoInContent );
+ pPam->Exchange();
+ pPam->Move( fnMoveBackward, GoInContent );
+
+ ClearFEShellTabCols(rDoc, nullptr);
+}
+
+// located in untbl.cxx and only an Undo object is allowed to call it
+SwTableNode* SwNodes::UndoTableToText( SwNodeOffset nSttNd, SwNodeOffset nEndNd,
+ const SwTableToTextSaves& rSavedData )
+{
+ SwNodeIndex aSttIdx( *this, nSttNd );
+ SwNodeIndex aEndIdx( *this, nEndNd+1 );
+
+ SwTableNode * pTableNd = new SwTableNode( aSttIdx );
+ SwEndNode* pEndNd = new SwEndNode( aEndIdx, *pTableNd );
+
+ aEndIdx = *pEndNd;
+
+ /* Set pTableNd as start of section for all nodes in [nSttNd, nEndNd].
+ Delete all Frames attached to the nodes in that range. */
+ SwNode* pNd;
+ {
+ SwNodeOffset n, nTmpEnd = aEndIdx.GetIndex();
+ for( n = pTableNd->GetIndex() + 1; n < nTmpEnd; ++n )
+ {
+ if( ( pNd = (*this)[ n ] )->IsContentNode() )
+ static_cast<SwContentNode*>(pNd)->DelFrames(nullptr);
+ pNd->m_pStartOfSection = pTableNd;
+ }
+ }
+
+ // than create table structure partially. First a single line that contains
+ // all boxes. The correct structure is then taken from SaveStruct.
+ SwTableBoxFormat* pBoxFormat = GetDoc().MakeTableBoxFormat();
+ SwTableLineFormat* pLineFormat = GetDoc().MakeTableLineFormat();
+ SwTableLine* pLine = new SwTableLine( pLineFormat, rSavedData.size(), nullptr );
+ pTableNd->GetTable().GetTabLines().insert( pTableNd->GetTable().GetTabLines().begin(), pLine );
+
+ const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create());
+ for( size_t n = rSavedData.size(); n; )
+ {
+ const SwTableToTextSave *const pSave = rSavedData[ --n ].get();
+ // if the start node was merged with last from prev. cell,
+ // subtract 1 from index to get the merged paragraph, and split that
+ aSttIdx = pSave->m_nSttNd - ( ( SAL_MAX_INT32 != pSave->m_nContent ) ? 1 : 0);
+ SwTextNode* pTextNd = aSttIdx.GetNode().GetTextNode();
+
+ if( SAL_MAX_INT32 != pSave->m_nContent )
+ {
+ // split at ContentPosition, delete previous char (= separator)
+ OSL_ENSURE( pTextNd, "Where is my TextNode?" );
+ SwIndex aCntPos( pTextNd, pSave->m_nContent - 1 );
+
+ pTextNd->EraseText( aCntPos, 1 );
+
+ std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc(
+ [&](SwTextNode *const pNewNode, sw::mark::RestoreMode const eMode, bool)
+ {
+ if (!pContentStore->Empty())
+ {
+ pContentStore->Restore(*pNewNode, pSave->m_nContent, pSave->m_nContent + 1, eMode);
+ }
+ });
+ pTextNd->SplitContentNode(
+ SwPosition(aSttIdx, aCntPos), &restoreFunc);
+ }
+ else
+ {
+ pContentStore->Clear();
+ if( pTextNd )
+ {
+ pContentStore->Save(GetDoc(), aSttIdx.GetIndex(), SAL_MAX_INT32);
+ }
+ }
+
+ if( pTextNd )
+ {
+ // METADATA: restore
+ pTextNd->GetTextNode()->RestoreMetadata(pSave->m_pMetadataUndoStart);
+ if( pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() )
+ pTextNd->ClearSwpHintsArr( false );
+ }
+
+ if( pSave->m_pHstry )
+ {
+ sal_uInt16 nTmpEnd = pSave->m_pHstry->GetTmpEnd();
+ pSave->m_pHstry->TmpRollback( &GetDoc(), 0 );
+ pSave->m_pHstry->SetTmpEnd( nTmpEnd );
+ }
+
+ // METADATA: restore
+ // end points to node after cell
+ if ( pSave->m_nEndNd - 1 > pSave->m_nSttNd )
+ {
+ SwTextNode* pLastNode = (*this)[ pSave->m_nEndNd - 1 ]->GetTextNode();
+ if (pLastNode)
+ {
+ pLastNode->RestoreMetadata(pSave->m_pMetadataUndoEnd);
+ }
+ }
+
+ aEndIdx = pSave->m_nEndNd;
+ SwStartNode* pSttNd = new SwStartNode( aSttIdx, SwNodeType::Start,
+ SwTableBoxStartNode );
+ pSttNd->m_pStartOfSection = pTableNd;
+ new SwEndNode( aEndIdx, *pSttNd );
+
+ for( SwNodeOffset i = aSttIdx.GetIndex(); i < aEndIdx.GetIndex()-1; ++i )
+ {
+ pNd = (*this)[ i ];
+ pNd->m_pStartOfSection = pSttNd;
+ if( pNd->IsStartNode() )
+ i = pNd->EndOfSectionIndex();
+ }
+
+ SwTableBox* pBox = new SwTableBox( pBoxFormat, *pSttNd, pLine );
+ pLine->GetTabBoxes().insert( pLine->GetTabBoxes().begin(), pBox );
+ }
+ return pTableNd;
+}
+
+void SwUndoTableToText::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->GetPoint()->nNode = m_nStartNode;
+ pPam->GetPoint()->nContent.Assign( nullptr, 0 );
+ SwNodeIndex aSaveIdx( pPam->GetPoint()->nNode, -1 );
+
+ pPam->SetMark(); // log off all indices
+ pPam->DeleteMark();
+
+ SwTableNode* pTableNd = pPam->GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "Could not find any TableNode" );
+
+ if( auto pDDETable = dynamic_cast<const SwDDETable *>(&pTableNd->GetTable()) )
+ m_pDDEFieldType.reset(static_cast<SwDDEFieldType*>(pDDETable->GetDDEFieldType()->Copy().release()));
+
+ rDoc.TableToText( pTableNd, m_cSeparator );
+
+ ++aSaveIdx;
+ SwContentNode* pCNd = aSaveIdx.GetNode().GetContentNode();
+ if( !pCNd && nullptr == ( pCNd = rDoc.GetNodes().GoNext( &aSaveIdx ) ) &&
+ nullptr == ( pCNd = SwNodes::GoPrevious( &aSaveIdx )) )
+ {
+ OSL_FAIL( "Where is the TextNode now?" );
+ }
+
+ pPam->GetPoint()->nNode = aSaveIdx;
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+
+ pPam->SetMark(); // log off all indices
+ pPam->DeleteMark();
+}
+
+void SwUndoTableToText::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwTableNode *const pTableNd = pPam->GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ // move cursor out of table
+ pPam->GetPoint()->nNode = *pTableNd->EndOfSectionNode();
+ pPam->Move( fnMoveForward, GoInContent );
+ pPam->SetMark();
+ pPam->DeleteMark();
+
+ rContext.GetDoc().TableToText( pTableNd, m_cSeparator );
+ }
+}
+
+void SwUndoTableToText::SetRange( const SwNodeRange& rRg )
+{
+ m_nStartNode = rRg.aStart.GetIndex();
+ m_nEndNode = rRg.aEnd.GetIndex();
+}
+
+void SwUndoTableToText::AddBoxPos( SwDoc& rDoc, SwNodeOffset nNdIdx, SwNodeOffset nEndIdx, sal_Int32 nContentIdx )
+{
+ m_vBoxSaves.push_back(std::make_unique<SwTableToTextSave>(rDoc, nNdIdx, nEndIdx, nContentIdx));
+}
+
+SwUndoTextToTable::SwUndoTextToTable( const SwPaM& rRg,
+ const SwInsertTableOptions& rInsTableOpts,
+ sal_Unicode cCh, sal_uInt16 nAdj,
+ const SwTableAutoFormat* pAFormat )
+ : SwUndo( SwUndoId::TEXTTOTABLE, &rRg.GetDoc() ), SwUndRng( rRg ), m_aInsertTableOpts( rInsTableOpts ),
+ m_pHistory( nullptr ), m_cSeparator( cCh ), m_nAdjust( nAdj )
+{
+ if( pAFormat )
+ m_pAutoFormat.reset( new SwTableAutoFormat( *pAFormat ) );
+
+ const SwPosition* pEnd = rRg.End();
+ SwNodes& rNds = rRg.GetDoc().GetNodes();
+ m_bSplitEnd = pEnd->nContent.GetIndex() && ( pEnd->nContent.GetIndex()
+ != pEnd->nNode.GetNode().GetContentNode()->Len() ||
+ pEnd->nNode.GetIndex() >= rNds.GetEndOfContent().GetIndex()-1 );
+}
+
+SwUndoTextToTable::~SwUndoTextToTable()
+{
+ m_pAutoFormat.reset();
+}
+
+void SwUndoTextToTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwNodeOffset nTableNd = m_nSttNode;
+ if( m_nSttContent )
+ ++nTableNd; // Node was split previously
+ SwNodeIndex aIdx( rDoc.GetNodes(), nTableNd );
+ SwTableNode *const pTNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTNd, "Could not find a TableNode" );
+
+ RemoveIdxFromSection( rDoc, nTableNd );
+
+ m_sTableName = pTNd->GetTable().GetFrameFormat()->GetName();
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ if( !mvDelBoxes.empty() )
+ {
+ pTNd->DelFrames();
+ SwTable& rTable = pTNd->GetTable();
+ for( size_t n = mvDelBoxes.size(); n; )
+ {
+ SwTableBox* pBox = rTable.GetTableBox( mvDelBoxes[ --n ] );
+ if( pBox )
+ ::DeleteBox_( rTable, pBox, nullptr, false, false );
+ else {
+ OSL_ENSURE( false, "Where is my box?" );
+ }
+ }
+ }
+
+ rDoc.TableToText( pTNd, 0x0b == m_cSeparator ? 0x09 : m_cSeparator );
+
+ // join again at start?
+ SwPaM aPam(rDoc.GetNodes().GetEndOfContent());
+ SwPosition *const pPos = aPam.GetPoint();
+ if( m_nSttContent )
+ {
+ pPos->nNode = nTableNd;
+ pPos->nContent.Assign(pPos->nNode.GetNode().GetContentNode(), 0);
+ if (aPam.Move(fnMoveBackward, GoInContent))
+ {
+ SwNodeIndex & rIdx = aPam.GetPoint()->nNode;
+
+ // than move, relatively, the Cursor/etc. again
+ RemoveIdxRel( rIdx.GetIndex()+1, *pPos );
+
+ rIdx.GetNode().GetContentNode()->JoinNext();
+ }
+ }
+
+ // join again at end?
+ if( m_bSplitEnd )
+ {
+ SwNodeIndex& rIdx = pPos->nNode;
+ rIdx = m_nEndNode;
+ SwTextNode* pTextNd = rIdx.GetNode().GetTextNode();
+ if( pTextNd && pTextNd->CanJoinNext() )
+ {
+ aPam.GetMark()->nContent.Assign( nullptr, 0 );
+ aPam.GetPoint()->nContent.Assign( nullptr, 0 );
+
+ // than move, relatively, the Cursor/etc. again
+ pPos->nContent.Assign(pTextNd, pTextNd->GetText().getLength());
+ RemoveIdxRel( m_nEndNode + 1, *pPos );
+
+ pTextNd->JoinNext();
+ }
+ }
+
+ AddUndoRedoPaM(rContext);
+}
+
+void SwUndoTextToTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ RemoveIdxFromRange(rPam, false);
+ SetPaM(rPam);
+
+ SwTable const*const pTable = rContext.GetDoc().TextToTable(
+ m_aInsertTableOpts, rPam, m_cSeparator, m_nAdjust, m_pAutoFormat.get() );
+ static_cast<SwFrameFormat*>(pTable->GetFrameFormat())->SetName( m_sTableName );
+}
+
+void SwUndoTextToTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ // no Table In Table
+ if (!rContext.GetRepeatPaM().GetNode().FindTableNode())
+ {
+ rContext.GetDoc().TextToTable( m_aInsertTableOpts, rContext.GetRepeatPaM(),
+ m_cSeparator, m_nAdjust,
+ m_pAutoFormat.get() );
+ }
+}
+
+void SwUndoTextToTable::AddFillBox( const SwTableBox& rBox )
+{
+ mvDelBoxes.push_back( rBox.GetSttIdx() );
+}
+
+SwHistory& SwUndoTextToTable::GetHistory()
+{
+ if( !m_pHistory )
+ m_pHistory = new SwHistory;
+ return *m_pHistory;
+}
+
+SwUndoTableHeadline::SwUndoTableHeadline( const SwTable& rTable, sal_uInt16 nOldHdl,
+ sal_uInt16 nNewHdl )
+ : SwUndo( SwUndoId::TABLEHEADLINE, rTable.GetFrameFormat()->GetDoc() ),
+ m_nOldHeadline( nOldHdl ),
+ m_nNewHeadline( nNewHdl )
+{
+ OSL_ENSURE( !rTable.GetTabSortBoxes().empty(), "Table without content" );
+ const SwStartNode *pSttNd = rTable.GetTabSortBoxes()[ 0 ]->GetSttNd();
+ OSL_ENSURE( pSttNd, "Box without content" );
+
+ m_nTableNode = pSttNd->StartOfSectionIndex();
+}
+
+void SwUndoTableHeadline::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+ OSL_ENSURE( pTNd, "could not find any TableNode" );
+
+ rDoc.SetRowsToRepeat( pTNd->GetTable(), m_nOldHeadline );
+}
+
+void SwUndoTableHeadline::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+ OSL_ENSURE( pTNd, "could not find any TableNode" );
+
+ rDoc.SetRowsToRepeat( pTNd->GetTable(), m_nNewHeadline );
+}
+
+void SwUndoTableHeadline::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwTableNode *const pTableNd =
+ rContext.GetRepeatPaM().GetNode().FindTableNode();
+ if( pTableNd )
+ {
+ rContext.GetDoc().SetRowsToRepeat( pTableNd->GetTable(), m_nNewHeadline );
+ }
+}
+
+SaveTable::SaveTable( const SwTable& rTable, sal_uInt16 nLnCnt, bool bSaveFormula )
+ : m_aTableSet(*rTable.GetFrameFormat()->GetAttrSet().GetPool(), aTableSetRange),
+ m_pSwTable(&rTable), m_nLineCount(nLnCnt), m_bSaveFormula(bSaveFormula)
+{
+ m_bModifyBox = false;
+ m_bNewModel = rTable.IsNewModel();
+ m_aTableSet.Put(rTable.GetFrameFormat()->GetAttrSet());
+ m_pLine.reset( new SaveLine( nullptr, *rTable.GetTabLines()[ 0 ], *this ) );
+
+ SaveLine* pLn = m_pLine.get();
+ if( USHRT_MAX == nLnCnt )
+ nLnCnt = rTable.GetTabLines().size();
+ for( sal_uInt16 n = 1; n < nLnCnt; ++n )
+ pLn = new SaveLine( pLn, *rTable.GetTabLines()[ n ], *this );
+
+ m_aFrameFormats.clear();
+ m_pSwTable = nullptr;
+}
+
+sal_uInt16 SaveTable::AddFormat( SwFrameFormat* pFormat, bool bIsLine )
+{
+ size_t nRet = m_aFrameFormats.GetPos(pFormat);
+ if( SIZE_MAX == nRet )
+ {
+ // Create copy of ItemSet
+ auto pSet = std::make_shared<SfxItemSet>( *pFormat->GetAttrSet().GetPool(),
+ bIsLine ? aTableLineSetRange : aTableBoxSetRange );
+ pSet->Put( pFormat->GetAttrSet() );
+ // When a formula is set, never save the value. It possibly must be
+ // recalculated.
+ // Save formulas always in plain text.
+ if( const SwTableBoxFormula* pItem = pSet->GetItemIfSet( RES_BOXATR_FORMULA ))
+ {
+ pSet->ClearItem( RES_BOXATR_VALUE );
+ if (m_pSwTable && m_bSaveFormula)
+ {
+ SwTableFormulaUpdate aMsgHint(m_pSwTable);
+ aMsgHint.m_eFlags = TBL_BOXNAME;
+ SwTableBoxFormula* pFormulaItem = const_cast<SwTableBoxFormula*>(pItem);
+ pFormulaItem->ChgDefinedIn( pFormat );
+ pFormulaItem->ChangeState( &aMsgHint );
+ pFormulaItem->ChgDefinedIn( nullptr );
+ }
+ }
+ nRet = m_aSets.size();
+ m_aSets.push_back(pSet);
+ m_aFrameFormats.insert(m_aFrameFormats.begin() + nRet, pFormat);
+ }
+ return o3tl::narrowing<sal_uInt16>(nRet);
+}
+
+void SaveTable::RestoreAttr( SwTable& rTable, bool bMdfyBox )
+{
+ m_bModifyBox = bMdfyBox;
+
+ FndBox_ aTmpBox( nullptr, nullptr );
+ bool bHideChanges = rTable.GetFrameFormat()->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
+ // TODO delete/make frames only at changing line attribute TextChangesOnly (RES_PRINT) to true again
+ if ( bHideChanges )
+ aTmpBox.DelFrames( rTable );
+
+ // first, get back attributes of TableFrameFormat
+ SwFrameFormat* pFormat = rTable.GetFrameFormat();
+ SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet()));
+ rFormatSet.ClearItem();
+ rFormatSet.Put(m_aTableSet);
+
+ pFormat->InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // table without table frame
+ bool bHiddenTable = true;
+
+ // for safety, invalidate all TableFrames
+ SwIterator<SwTabFrame,SwFormat> aIter( *pFormat );
+ for( SwTabFrame* pLast = aIter.First(); pLast; pLast = aIter.Next() )
+ {
+ if( pLast->GetTable() == &rTable )
+ {
+ pLast->InvalidateAll();
+ pLast->SetCompletePaint();
+ bHiddenTable = false;
+ }
+ }
+
+ // fill FrameFormats with defaults (0)
+ pFormat = nullptr;
+ for (size_t n = m_aSets.size(); n; --n)
+ m_aFrameFormats.push_back(pFormat);
+
+ const size_t nLnCnt = (USHRT_MAX == m_nLineCount)
+ ? rTable.GetTabLines().size()
+ : m_nLineCount;
+
+ SaveLine* pLn = m_pLine.get();
+ for (size_t n = 0; n < nLnCnt; ++n, pLn = pLn->m_pNext)
+ {
+ if( !pLn )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ break;
+ }
+
+ pLn->RestoreAttr( *rTable.GetTabLines()[ n ], *this );
+ }
+
+ m_aFrameFormats.clear();
+ m_bModifyBox = false;
+
+ if ( bHideChanges )
+ {
+ if ( bHiddenTable )
+ {
+ SwTableNode* pTableNode = rTable.GetTableNode();
+ pTableNode->DelFrames();
+ SwNodeIndex aTableIdx( *pTableNode->EndOfSectionNode(), 1 );
+ pTableNode->MakeOwnFrames(&aTableIdx);
+ }
+ else
+ {
+ aTmpBox.MakeFrames( rTable );
+ }
+ }
+}
+
+void SaveTable::SaveContentAttrs( SwDoc* pDoc )
+{
+ m_pLine->SaveContentAttrs(pDoc);
+}
+
+void SaveTable::CreateNew( SwTable& rTable, bool bCreateFrames,
+ bool bRestoreChart )
+{
+ FndBox_ aTmpBox( nullptr, nullptr );
+ aTmpBox.DelFrames( rTable );
+
+ // first, get back attributes of TableFrameFormat
+ SwFrameFormat* pFormat = rTable.GetFrameFormat();
+ SfxItemSet& rFormatSet = const_cast<SfxItemSet&>(static_cast<SfxItemSet const &>(pFormat->GetAttrSet()));
+ rFormatSet.ClearItem();
+ rFormatSet.Put(m_aTableSet);
+
+ pFormat->InvalidateInSwCache(RES_ATTRSET_CHG);
+
+ // SwTableBox must have a format - the SwTableBox takes ownership of it
+ SwTableBoxFormat *const pNewFormat(pFormat->GetDoc()->MakeTableBoxFormat());
+ SwTableBox aParent(pNewFormat, rTable.GetTabLines().size(), nullptr);
+
+ // fill FrameFormats with defaults (0)
+ pFormat = nullptr;
+ for( size_t n = m_aSets.size(); n; --n )
+ m_aFrameFormats.push_back(pFormat);
+
+ m_pLine->CreateNew(rTable, aParent, *this);
+ m_aFrameFormats.clear();
+
+ // add new lines, delete old ones
+ const size_t nOldLines = (USHRT_MAX == m_nLineCount)
+ ? rTable.GetTabLines().size()
+ : m_nLineCount;
+
+ SwDoc *pDoc = rTable.GetFrameFormat()->GetDoc();
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ size_t n = 0;
+ for( ; n < aParent.GetTabLines().size(); ++n )
+ {
+ SwTableLine* pLn = aParent.GetTabLines()[ n ];
+ pLn->SetUpper( nullptr );
+ if( n < nOldLines )
+ {
+ SwTableLine* pOld = rTable.GetTabLines()[ n ];
+
+ // TL_CHART2: notify chart about boxes to be removed
+ const SwTableBoxes &rBoxes = pOld->GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+ for (size_t k = 0; k < nBoxes; ++k)
+ {
+ SwTableBox *pBox = rBoxes[k];
+ if (pPCD)
+ pPCD->DeleteBox( &rTable, *pBox );
+ }
+
+ rTable.GetTabLines()[n] = pLn;
+ delete pOld;
+ }
+ else
+ rTable.GetTabLines().insert( rTable.GetTabLines().begin() + n, pLn );
+ }
+
+ if( n < nOldLines )
+ {
+ // remove remaining lines...
+ for (size_t k1 = 0; k1 < nOldLines - n; ++k1)
+ {
+ const SwTableBoxes &rBoxes = rTable.GetTabLines()[n + k1]->GetTabBoxes();
+ const size_t nBoxes = rBoxes.size();
+ for (size_t k2 = 0; k2 < nBoxes; ++k2)
+ {
+ SwTableBox *pBox = rBoxes[k2];
+ // TL_CHART2: notify chart about boxes to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &rTable, *pBox );
+ }
+ }
+
+ for( SwTableLines::const_iterator it = rTable.GetTabLines().begin() + n;
+ it != rTable.GetTabLines().begin() + nOldLines; ++it )
+ delete *it;
+ rTable.GetTabLines().erase( rTable.GetTabLines().begin() + n, rTable.GetTabLines().begin() + nOldLines );
+ }
+
+ aParent.GetTabLines().erase( aParent.GetTabLines().begin(), aParent.GetTabLines().begin() + n );
+ assert(aParent.GetTabLines().empty());
+
+ if( bCreateFrames )
+ aTmpBox.MakeFrames( rTable );
+ if( bRestoreChart )
+ {
+ // TL_CHART2: need to inform chart of probably changed cell names
+ pDoc->UpdateCharts( rTable.GetFrameFormat()->GetName() );
+ }
+}
+
+SwFrameFormat& SaveTable::CreateNewFormat(SwFrameFormat& rFormat, sal_uInt16 nFormatPos)
+{
+ rFormat.SetFormatAttr(*m_aSets[nFormatPos]);
+ m_aFrameFormats[nFormatPos] = &rFormat;
+ return rFormat;
+}
+
+void SaveTable::NewFrameFormatForLine(const SwTableLine& rTableLn, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat)
+{
+ SwFrameFormat* pFormat = m_aFrameFormats[nFormatPos];
+ if(!pFormat)
+ pFormat = &CreateNewFormat(*pOldFormat->GetDoc()->MakeTableLineFormat(), nFormatPos);
+ pOldFormat->CallSwClientNotify(sw::MoveTableLineHint(*pFormat, rTableLn));
+ pFormat->Add(const_cast<SwTableLine*>(&rTableLn));
+ KillEmptyFrameFormat(*pOldFormat);
+}
+
+void SaveTable::NewFrameFormatForBox(const SwTableBox& rTableBx, sal_uInt16 nFormatPos, SwFrameFormat* pOldFormat)
+{
+ SwFrameFormat* pFormat = m_aFrameFormats[nFormatPos];
+ if(!pFormat)
+ pFormat = &CreateNewFormat(*pOldFormat->GetDoc()->MakeTableBoxFormat(), nFormatPos);
+ pOldFormat->CallSwClientNotify(sw::MoveTableBoxHint(*pFormat, rTableBx));
+ pFormat->MoveTableBox(*const_cast<SwTableBox*>(&rTableBx), m_bModifyBox ? pOldFormat : nullptr);
+ KillEmptyFrameFormat(*pOldFormat);
+}
+
+SaveLine::SaveLine(SaveLine* pPrev, const SwTableLine& rLine, SaveTable& rSTable)
+ : m_pNext(nullptr)
+{
+ if( pPrev )
+ pPrev->m_pNext = this;
+
+ m_nItemSet = rSTable.AddFormat(rLine.GetFrameFormat(), true);
+
+ m_pBox = new SaveBox(nullptr, *rLine.GetTabBoxes()[0], rSTable);
+ SaveBox* pBx = m_pBox;
+ for( size_t n = 1; n < rLine.GetTabBoxes().size(); ++n )
+ pBx = new SaveBox( pBx, *rLine.GetTabBoxes()[ n ], rSTable );
+}
+
+SaveLine::~SaveLine()
+{
+ delete m_pBox;
+ delete m_pNext;
+}
+
+void SaveLine::RestoreAttr( SwTableLine& rLine, SaveTable& rSTable )
+{
+ rSTable.NewFrameFormatForLine(rLine, m_nItemSet, rLine.GetFrameFormat());
+
+ SaveBox* pBx = m_pBox;
+ for (size_t n = 0; n < rLine.GetTabBoxes().size(); ++n, pBx = pBx->m_pNext)
+ {
+ if( !pBx )
+ {
+ OSL_ENSURE( false, "Number of boxes changed" );
+ break;
+ }
+ pBx->RestoreAttr( *rLine.GetTabBoxes()[ n ], rSTable );
+ }
+}
+
+void SaveLine::SaveContentAttrs( SwDoc* pDoc )
+{
+ m_pBox->SaveContentAttrs(pDoc);
+ if (m_pNext)
+ m_pNext->SaveContentAttrs(pDoc);
+}
+
+void SaveLine::CreateNew( SwTable& rTable, SwTableBox& rParent, SaveTable& rSTable )
+{
+ SwTableLineFormat* pFormat
+ = static_cast<SwTableLineFormat*>(rSTable.m_aFrameFormats[m_nItemSet]);
+ if( !pFormat )
+ {
+ SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc();
+ pFormat = pDoc->MakeTableLineFormat();
+ pFormat->SetFormatAttr(*rSTable.m_aSets[m_nItemSet]);
+ rSTable.m_aFrameFormats[m_nItemSet] = pFormat;
+ }
+ SwTableLine* pNew = new SwTableLine( pFormat, 1, &rParent );
+
+ rParent.GetTabLines().push_back( pNew );
+
+ m_pBox->CreateNew(rTable, *pNew, rSTable);
+
+ if (m_pNext)
+ m_pNext->CreateNew(rTable, rParent, rSTable);
+}
+
+SaveBox::SaveBox(SaveBox* pPrev, const SwTableBox& rBox, SaveTable& rSTable)
+ : m_pNext(nullptr)
+ , m_nStartNode(NODE_OFFSET_MAX)
+ , m_nRowSpan(0)
+{
+ m_Ptrs.pLine = nullptr;
+
+ if( pPrev )
+ pPrev->m_pNext = this;
+
+ m_nItemSet = rSTable.AddFormat(rBox.GetFrameFormat(), false);
+
+ if( rBox.GetSttNd() )
+ {
+ m_nStartNode = rBox.GetSttIdx();
+ m_nRowSpan = rBox.getRowSpan();
+ }
+ else
+ {
+ m_Ptrs.pLine = new SaveLine(nullptr, *rBox.GetTabLines()[0], rSTable);
+
+ SaveLine* pLn = m_Ptrs.pLine;
+ for( size_t n = 1; n < rBox.GetTabLines().size(); ++n )
+ pLn = new SaveLine( pLn, *rBox.GetTabLines()[ n ], rSTable );
+ }
+}
+
+SaveBox::~SaveBox()
+{
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ delete m_Ptrs.pLine;
+ else
+ delete m_Ptrs.pContentAttrs;
+ delete m_pNext;
+}
+
+void SaveBox::RestoreAttr( SwTableBox& rBox, SaveTable& rSTable )
+{
+ rSTable.NewFrameFormatForBox(rBox, m_nItemSet, rBox.GetFrameFormat());
+
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ {
+ if( rBox.GetTabLines().empty() )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ }
+ else
+ {
+ SaveLine* pLn = m_Ptrs.pLine;
+ for (size_t n = 0; n < rBox.GetTabLines().size(); ++n, pLn = pLn->m_pNext)
+ {
+ if( !pLn )
+ {
+ OSL_ENSURE( false, "Number of lines changed" );
+ break;
+ }
+
+ pLn->RestoreAttr( *rBox.GetTabLines()[ n ], rSTable );
+ }
+ }
+ }
+ else if (rBox.GetSttNd() && rBox.GetSttIdx() == m_nStartNode)
+ {
+ if (m_Ptrs.pContentAttrs)
+ {
+ SwNodes& rNds = rBox.GetFrameFormat()->GetDoc()->GetNodes();
+ sal_uInt16 nSet = 0;
+ SwNodeOffset nEnd = rBox.GetSttNd()->EndOfSectionIndex();
+ for (SwNodeOffset n = m_nStartNode + 1; n < nEnd; ++n)
+ {
+ SwContentNode* pCNd = rNds[ n ]->GetContentNode();
+ if( pCNd )
+ {
+ std::shared_ptr<SfxItemSet> pSet((*m_Ptrs.pContentAttrs)[nSet++]);
+ if( pSet )
+ {
+ for( const WhichPair& rPair : aSave_BoxContentSet )
+ pCNd->ResetAttr( rPair.first, rPair.second );
+ pCNd->SetAttr( *pSet );
+ }
+ else
+ pCNd->ResetAllAttr();
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_ENSURE( false, "Box not anymore at the same node" );
+ }
+}
+
+void SaveBox::SaveContentAttrs( SwDoc* pDoc )
+{
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ {
+ // continue in current line
+ m_Ptrs.pLine->SaveContentAttrs(pDoc);
+ }
+ else
+ {
+ SwNodeOffset nEnd = pDoc->GetNodes()[m_nStartNode]->EndOfSectionIndex();
+ m_Ptrs.pContentAttrs = new SfxItemSets;
+ for (SwNodeOffset n = m_nStartNode + 1; n < nEnd; ++n)
+ {
+ SwContentNode* pCNd = pDoc->GetNodes()[ n ]->GetContentNode();
+ if( pCNd )
+ {
+ std::shared_ptr<SfxItemSet> pSet;
+ if( pCNd->HasSwAttrSet() )
+ {
+ pSet = std::make_shared<SfxItemSet>( pDoc->GetAttrPool(),
+ aSave_BoxContentSet );
+ pSet->Put( *pCNd->GetpSwAttrSet() );
+ }
+
+ m_Ptrs.pContentAttrs->push_back(pSet);
+ }
+ }
+ }
+ if (m_pNext)
+ m_pNext->SaveContentAttrs(pDoc);
+}
+
+void SaveBox::CreateNew( SwTable& rTable, SwTableLine& rParent, SaveTable& rSTable )
+{
+ SwTableBoxFormat* pFormat = static_cast<SwTableBoxFormat*>(rSTable.m_aFrameFormats[m_nItemSet]);
+ if( !pFormat )
+ {
+ SwDoc* pDoc = rTable.GetFrameFormat()->GetDoc();
+ pFormat = pDoc->MakeTableBoxFormat();
+ pFormat->SetFormatAttr(*rSTable.m_aSets[m_nItemSet]);
+ rSTable.m_aFrameFormats[m_nItemSet] = pFormat;
+ }
+
+ if (NODE_OFFSET_MAX == m_nStartNode) // no EndBox
+ {
+ SwTableBox* pNew = new SwTableBox( pFormat, 1, &rParent );
+ rParent.GetTabBoxes().push_back( pNew );
+
+ m_Ptrs.pLine->CreateNew(rTable, *pNew, rSTable);
+ }
+ else
+ {
+ // search box for StartNode in old table
+ SwTableBox* pBox = rTable.GetTableBox(m_nStartNode);
+ if (pBox)
+ {
+ SwFrameFormat* pOld = pBox->GetFrameFormat();
+ pBox->RegisterToFormat( *pFormat );
+ if( !pOld->HasWriterListeners() )
+ delete pOld;
+
+ pBox->setRowSpan(m_nRowSpan);
+
+ SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes();
+ pTBoxes->erase( std::find( pTBoxes->begin(), pTBoxes->end(), pBox ) );
+
+ pBox->SetUpper( &rParent );
+ pTBoxes = &rParent.GetTabBoxes();
+ pTBoxes->push_back( pBox );
+ }
+ }
+
+ if (m_pNext)
+ m_pNext->CreateNew(rTable, rParent, rSTable);
+}
+
+// UndoObject for attribute changes on table
+SwUndoAttrTable::SwUndoAttrTable( const SwTableNode& rTableNd, bool bClearTabCols )
+ : SwUndo( SwUndoId::TABLE_ATTR, &rTableNd.GetDoc() ),
+ m_nStartNode( rTableNd.GetIndex() )
+{
+ m_bClearTableCol = bClearTabCols;
+ m_pSaveTable.reset( new SaveTable( rTableNd.GetTable() ) );
+}
+
+SwUndoAttrTable::~SwUndoAttrTable()
+{
+}
+
+void SwUndoAttrTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nStartNode ]->GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ if (pTableNd)
+ {
+ SaveTable* pOrig = new SaveTable( pTableNd->GetTable() );
+ m_pSaveTable->RestoreAttr( pTableNd->GetTable() );
+ m_pSaveTable.reset( pOrig );
+ }
+
+ if( m_bClearTableCol )
+ {
+ ClearFEShellTabCols(rDoc, nullptr);
+ }
+}
+
+void SwUndoAttrTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoImpl(rContext);
+}
+
+// UndoObject for AutoFormat on Table
+SwUndoTableAutoFormat::SwUndoTableAutoFormat( const SwTableNode& rTableNd,
+ const SwTableAutoFormat& rAFormat )
+ : SwUndo( SwUndoId::TABLE_AUTOFMT, &rTableNd.GetDoc() )
+ , m_TableStyleName(rTableNd.GetTable().GetTableStyleName())
+ , m_nStartNode( rTableNd.GetIndex() )
+ , m_bSaveContentAttr( false )
+ , m_nRepeatHeading(rTableNd.GetTable().GetRowsToRepeat())
+{
+ m_pSaveTable.reset( new SaveTable( rTableNd.GetTable() ) );
+
+ if( rAFormat.IsFont() || rAFormat.IsJustify() )
+ {
+ // then also go over the ContentNodes of the EndBoxes and collect
+ // all paragraph attributes
+ m_pSaveTable->SaveContentAttrs( &const_cast<SwDoc&>(rTableNd.GetDoc()) );
+ m_bSaveContentAttr = true;
+ }
+}
+
+SwUndoTableAutoFormat::~SwUndoTableAutoFormat()
+{
+}
+
+void SwUndoTableAutoFormat::SaveBoxContent( const SwTableBox& rBox )
+{
+ m_Undos.push_back(std::make_shared<SwUndoTableNumFormat>(rBox));
+}
+
+void
+SwUndoTableAutoFormat::UndoRedo(bool const bUndo, ::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nStartNode ]->GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ SwTable& table = pTableNd->GetTable();
+ if (table.GetTableStyleName() != m_TableStyleName)
+ {
+ OUString const temp(table.GetTableStyleName());
+ table.SetTableStyleName(m_TableStyleName);
+ m_TableStyleName = temp;
+ }
+ SaveTable* pOrig = new SaveTable( table );
+ // then go also over the ContentNodes of the EndBoxes and collect
+ // all paragraph attributes
+ if( m_bSaveContentAttr )
+ pOrig->SaveContentAttrs( &rDoc );
+
+ if (bUndo)
+ {
+ for (size_t n = m_Undos.size(); 0 < n; --n)
+ {
+ m_Undos.at(n-1)->UndoImpl(rContext);
+ }
+
+ table.SetRowsToRepeat(m_nRepeatHeading);
+ }
+
+ m_pSaveTable->RestoreAttr( pTableNd->GetTable(), !bUndo );
+ m_pSaveTable.reset( pOrig );
+}
+
+void SwUndoTableAutoFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoRedo(true, rContext);
+}
+
+void SwUndoTableAutoFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ UndoRedo(false, rContext);
+}
+
+SwUndoTableNdsChg::SwUndoTableNdsChg( SwUndoId nAction,
+ const SwSelBoxes& rBoxes,
+ const SwTableNode& rTableNd,
+ tools::Long nMn, tools::Long nMx,
+ sal_uInt16 nCnt, bool bFlg, bool bSmHght )
+ : SwUndo( nAction, &rTableNd.GetDoc() ),
+ m_nMin( nMn ), m_nMax( nMx ),
+ m_nSttNode( rTableNd.GetIndex() ),
+ m_nCount( nCnt ),
+ m_bFlag( bFlg ),
+ m_bSameHeight( bSmHght )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ m_pSaveTable.reset( new SaveTable( rTable ) );
+
+ // and remember selection
+ ReNewBoxes( rBoxes );
+}
+
+void SwUndoTableNdsChg::ReNewBoxes( const SwSelBoxes& rBoxes )
+{
+ if (rBoxes.size() != m_Boxes.size())
+ {
+ m_Boxes.clear();
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ m_Boxes.insert( rBoxes[n]->GetSttIdx() );
+ }
+ }
+}
+
+SwUndoTableNdsChg::~SwUndoTableNdsChg()
+{
+}
+
+void SwUndoTableNdsChg::SaveNewBoxes( const SwTableNode& rTableNd,
+ const SwTableSortBoxes& rOld )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes();
+
+ OSL_ENSURE( ! IsDelBox(), "wrong Action" );
+ m_xNewSttNds.emplace();
+
+ size_t i = 0;
+ for (size_t n = 0; n < rOld.size(); ++i)
+ {
+ if( rOld[ n ] == rTableBoxes[ i ] )
+ ++n;
+ else
+ // new box: insert sorted
+ m_xNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) );
+ }
+
+ for( ; i < rTableBoxes.size(); ++i )
+ // new box: insert sorted
+ m_xNewSttNds->insert( BoxMove(rTableBoxes[ i ]->GetSttIdx()) );
+}
+
+static SwTableLine* lcl_FindTableLine( const SwTable& rTable,
+ const SwTableBox& rBox )
+{
+ SwTableLine* pRet = nullptr;
+ // i63949: For nested cells we have to take nLineNo - 1, too, not 0!
+ const SwTableLines &rTableLines = ( rBox.GetUpper()->GetUpper() != nullptr ) ?
+ rBox.GetUpper()->GetUpper()->GetTabLines()
+ : rTable.GetTabLines();
+ const SwTableLine* pLine = rBox.GetUpper();
+ sal_uInt16 nLineNo = rTableLines.GetPos( pLine );
+ pRet = rTableLines[nLineNo - 1];
+
+ return pRet;
+}
+
+static const SwTableLines& lcl_FindParentLines( const SwTable& rTable,
+ const SwTableBox& rBox )
+{
+ const SwTableLines& rRet =
+ ( rBox.GetUpper()->GetUpper() != nullptr ) ?
+ rBox.GetUpper()->GetUpper()->GetTabLines() :
+ rTable.GetTabLines();
+
+ return rRet;
+}
+
+void SwUndoTableNdsChg::SaveNewBoxes( const SwTableNode& rTableNd,
+ const SwTableSortBoxes& rOld,
+ const SwSelBoxes& rBoxes,
+ const std::vector<SwNodeOffset> &rNodeCnts )
+{
+ const SwTable& rTable = rTableNd.GetTable();
+ const SwTableSortBoxes& rTableBoxes = rTable.GetTabSortBoxes();
+
+ OSL_ENSURE( ! IsDelBox(), "wrong Action" );
+ m_xNewSttNds.emplace();
+
+ OSL_ENSURE( rTable.IsNewModel() || rOld.size() + m_nCount * rBoxes.size() == rTableBoxes.size(),
+ "unexpected boxes" );
+ OSL_ENSURE( rOld.size() <= rTableBoxes.size(), "more unexpected boxes" );
+ for (size_t n = 0, i = 0; i < rTableBoxes.size(); ++i)
+ {
+ if( ( n < rOld.size() ) &&
+ ( rOld[ n ] == rTableBoxes[ i ] ) )
+ {
+ // box already known? Then nothing to be done.
+ ++n;
+ }
+ else
+ {
+ // new box found: insert (obey sort order)
+ const SwTableBox* pBox = rTableBoxes[ i ];
+
+ // find the source box. It must be one in rBoxes.
+ // We found the right one if it's in the same column as pBox.
+ // No, if more than one selected cell in the same column has been split,
+ // we have to look for the nearest one (i65201)!
+ const SwTableBox* pSourceBox = nullptr;
+ const SwTableBox* pCheckBox = nullptr;
+ const SwTableLine* pBoxLine = pBox->GetUpper();
+ sal_uInt16 nLineDiff = lcl_FindParentLines(rTable,*pBox).GetPos(pBoxLine);
+ sal_uInt16 nLineNo = 0;
+ for (size_t j = 0; j < rBoxes.size(); ++j)
+ {
+ pCheckBox = rBoxes[j];
+ if( pCheckBox->GetUpper()->GetUpper() == pBox->GetUpper()->GetUpper() )
+ {
+ const SwTableLine* pCheckLine = pCheckBox->GetUpper();
+ sal_uInt16 nCheckLine = lcl_FindParentLines( rTable, *pCheckBox ).
+ GetPos( pCheckLine );
+ if( ( !pSourceBox || nCheckLine > nLineNo ) && nCheckLine < nLineDiff )
+ {
+ nLineNo = nCheckLine;
+ pSourceBox = pCheckBox;
+ }
+ }
+ }
+
+ // find the line number difference
+ // (to help determine bNodesMoved flag below)
+ nLineDiff = nLineDiff - nLineNo;
+ OSL_ENSURE( pSourceBox, "Split source box not found!" );
+ // find out how many nodes the source box used to have
+ // (to help determine bNodesMoved flag below)
+ size_t nNdsPos = 0;
+ while( rBoxes[ nNdsPos ] != pSourceBox )
+ ++nNdsPos;
+ SwNodeOffset nNodes = rNodeCnts[ nNdsPos ];
+
+ // When a new table cell is created, it either gets a new
+ // node, or it gets node(s) from elsewhere. The undo must
+ // know, of course, and thus we must determine here just
+ // where pBox's nodes are from:
+ // If 1) the source box has lost nodes, and
+ // 2) we're in the node range that got nodes
+ // then pBox received nodes from elsewhere.
+ // If bNodesMoved is set for pBox the undo must move the
+ // boxes back, otherwise it must delete them.
+ bool bNodesMoved = pSourceBox &&
+ ( nNodes != ( pSourceBox->GetSttNd()->EndOfSectionIndex() -
+ pSourceBox->GetSttIdx() ) )
+ && ( nNodes - 1 > SwNodeOffset(nLineDiff) );
+ m_xNewSttNds->insert( BoxMove(pBox->GetSttIdx(), bNodesMoved) );
+ }
+ }
+}
+
+void SwUndoTableNdsChg::SaveSection( SwStartNode* pSttNd )
+{
+ OSL_ENSURE( IsDelBox(), "wrong Action" );
+ if (m_pDelSects == nullptr)
+ m_pDelSects.reset(new SwUndoSaveSections);
+
+ SwTableNode* pTableNd = pSttNd->FindTableNode();
+ std::unique_ptr<SwUndoSaveSection, o3tl::default_delete<SwUndoSaveSection>> pSave(new SwUndoSaveSection);
+ pSave->SaveSection( SwNodeIndex( *pSttNd ));
+
+ m_pDelSects->push_back(std::move(pSave));
+ m_nSttNode = pTableNd->GetIndex();
+}
+
+void SwUndoTableNdsChg::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nSttNode );
+
+ SwTableNode *const pTableNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ CHECK_TABLE( pTableNd->GetTable() )
+
+ FndBox_ aTmpBox( nullptr, nullptr );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ SwSelBoxes aDelBoxes;
+ std::vector< std::pair<SwTableBox *, SwNodeOffset> > aDelNodes;
+ if( IsDelBox() )
+ {
+ // Trick: add missing boxes in any line, they will be connected
+ // correctly when calling CreateNew
+ SwTableBox* pCpyBox = pTableNd->GetTable().GetTabSortBoxes()[0];
+ SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes();
+
+ // restore sections
+ for (size_t n = m_pDelSects->size(); n; )
+ {
+ SwUndoSaveSection *const pSave = (*m_pDelSects)[ --n ].get();
+ pSave->RestoreSection( &rDoc, &aIdx, SwTableBoxStartNode );
+ if( pSave->GetHistory() )
+ pSave->GetHistory()->Rollback( &rDoc );
+ SwTableBox* pBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pCpyBox->GetFrameFormat()), aIdx,
+ pCpyBox->GetUpper() );
+ rLnBoxes.push_back( pBox );
+ }
+ m_pDelSects->clear();
+ }
+ else if( !m_xNewSttNds->empty() )
+ {
+ // Then the nodes have be moved and not deleted!
+ // But for that we need a temp array.
+ std::vector<BoxMove> aTmp( m_xNewSttNds->begin(), m_xNewSttNds->end() );
+
+ // backwards
+ for (size_t n = aTmp.size(); n > 0 ; )
+ {
+ --n;
+ // delete box from table structure
+ SwNodeOffset nIdx = aTmp[n].index;
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nIdx );
+ OSL_ENSURE( pBox, "Where is my TableBox?" );
+
+ // TL_CHART2: notify chart about box to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *pBox );
+
+ // insert _before_ deleting the section - otherwise the box
+ // has no start node so all boxes sort equal in SwSelBoxes
+ aDelBoxes.insert(pBox);
+
+ if( aTmp[n].hasMoved )
+ {
+ SwNodeRange aRg( *pBox->GetSttNd(), SwNodeOffset(1),
+ *pBox->GetSttNd()->EndOfSectionNode() );
+
+ SwTableLine* pLine = lcl_FindTableLine( pTableNd->GetTable(), *pBox );
+ SwNodeIndex aInsPos( *(pLine->GetTabBoxes()[0]->GetSttNd()), 2 );
+
+ // adjust all StartNode indices
+ size_t i = n;
+ SwNodeOffset nSttIdx = aInsPos.GetIndex() - 2,
+ nNdCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
+ while( i && aTmp[ --i ].index > nSttIdx )
+ aTmp[ i ].index += nNdCnt;
+
+ // first delete box
+ delete pBox;
+ // than move nodes
+ rDoc.GetNodes().MoveNodes( aRg, rDoc.GetNodes(), aInsPos, false );
+ }
+ else
+ {
+ aDelNodes.emplace_back(pBox, nIdx);
+ }
+ }
+ }
+ else
+ {
+ // Remove nodes from nodes array (backwards!)
+ std::set<BoxMove>::reverse_iterator it;
+ for( it = m_xNewSttNds->rbegin(); it != m_xNewSttNds->rend(); ++it )
+ {
+ SwNodeOffset nIdx = (*it).index;
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nIdx );
+ OSL_ENSURE( pBox, "Where's my table box?" );
+ // TL_CHART2: notify chart about box to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *pBox );
+ aDelBoxes.insert(pBox);
+ aDelNodes.emplace_back(pBox, nIdx);
+ }
+ }
+
+ // fdo#57197: before deleting the SwTableBoxes, delete the SwTabFrames
+ aTmpBox.SetTableLines(aDelBoxes, pTableNd->GetTable());
+ aTmpBox.DelFrames(pTableNd->GetTable());
+
+ // do this _after_ deleting Frames because disposing SwAccessible requires
+ // connection to the nodes, see SwAccessibleChild::IsAccessible()
+ for (const std::pair<SwTableBox *, SwNodeOffset> & rDelNode : aDelNodes)
+ {
+ // first disconnect box from node, otherwise ~SwTableBox would
+ // access pBox->pSttNd, deleted by DeleteSection
+ rDelNode.first->RemoveFromTable();
+ rDoc.getIDocumentContentOperations().DeleteSection(rDoc.GetNodes()[ rDelNode.second ]);
+ }
+
+ // Remove boxes from table structure
+ for( size_t n = 0; n < aDelBoxes.size(); ++n )
+ {
+ SwTableBox* pCurrBox = aDelBoxes[n];
+ SwTableBoxes* pTBoxes = &pCurrBox->GetUpper()->GetTabBoxes();
+ pTBoxes->erase( std::find( pTBoxes->begin(), pTBoxes->end(), pCurrBox ) );
+ delete pCurrBox;
+ }
+
+ m_pSaveTable->CreateNew( pTableNd->GetTable(), true, false );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ rDoc.UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
+ if (SwFEShell* pFEShell = rDoc.GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTableNd);
+ if( IsDelBox() )
+ m_nSttNode = pTableNd->GetIndex();
+ ClearFEShellTabCols(rDoc, nullptr);
+ CHECK_TABLE( pTableNd->GetTable() )
+}
+
+void SwUndoTableNdsChg::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+
+ SwTableNode* pTableNd = rDoc.GetNodes()[ m_nSttNode ]->GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+ CHECK_TABLE( pTableNd->GetTable() )
+
+ SwSelBoxes aSelBoxes;
+ for (const auto& rBox : m_Boxes)
+ {
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( rBox );
+ aSelBoxes.insert( pBox );
+ }
+
+ // create SelBoxes and call InsertCell/-Row/SplitTable
+ switch( GetId() )
+ {
+ case SwUndoId::TABLE_INSCOL:
+ rDoc.InsertCol( aSelBoxes, m_nCount, m_bFlag );
+ break;
+
+ case SwUndoId::TABLE_INSROW:
+ rDoc.InsertRow( aSelBoxes, m_nCount, m_bFlag );
+ break;
+
+ case SwUndoId::TABLE_SPLIT:
+ rDoc.SplitTable( aSelBoxes, m_bFlag, m_nCount, m_bSameHeight );
+ break;
+ case SwUndoId::TABLE_DELBOX:
+ case SwUndoId::ROW_DELETE:
+ case SwUndoId::COL_DELETE:
+ {
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+ SwTable &rTable = pTableNd->GetTable();
+ if( m_nMax > m_nMin && rTable.IsNewModel() )
+ rTable.PrepareDeleteCol( m_nMin, m_nMax );
+ rTable.DeleteSel( &rDoc, aSelBoxes, nullptr, this, true, true );
+ m_nSttNode = pTableNd->GetIndex();
+ }
+ break;
+ default:
+ ;
+ }
+ ClearFEShellTabCols(rDoc, nullptr);
+ CHECK_TABLE( pTableNd->GetTable() )
+}
+
+SwUndoTableMerge::SwUndoTableMerge( const SwPaM& rTableSel )
+ : SwUndo( SwUndoId::TABLE_MERGE, &rTableSel.GetDoc() ), SwUndRng( rTableSel )
+{
+ const SwTableNode* pTableNd = rTableSel.GetNode().FindTableNode();
+ OSL_ENSURE( pTableNd, "Where is the TableNode?" );
+ m_pSaveTable.reset( new SaveTable( pTableNd->GetTable() ) );
+ m_nTableNode = pTableNd->GetIndex();
+}
+
+SwUndoTableMerge::~SwUndoTableMerge()
+{
+ m_pSaveTable.reset();
+ m_vMoves.clear();
+ m_pHistory.reset();
+}
+
+void SwUndoTableMerge::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwNodeIndex aIdx( rDoc.GetNodes(), m_nTableNode );
+
+ SwTableNode *const pTableNd = aIdx.GetNode().GetTableNode();
+ OSL_ENSURE( pTableNd, "no TableNode" );
+
+ SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ // 1. restore deleted boxes:
+ // Trick: add missing boxes in any line, they will be connected
+ // correctly when calling CreateNew
+ SwTableBox *pBox, *pCpyBox = pTableNd->GetTable().GetTabSortBoxes()[0];
+ SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes();
+
+ CHECKTABLE(pTableNd->GetTable())
+
+ SwSelBoxes aSelBoxes;
+ SwTextFormatColl* pColl = rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD );
+
+ for (const auto& rBox : m_Boxes)
+ {
+ aIdx = rBox;
+ SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection( aIdx,
+ SwTableBoxStartNode, pColl );
+ pBox = new SwTableBox( static_cast<SwTableBoxFormat*>(pCpyBox->GetFrameFormat()), *pSttNd,
+ pCpyBox->GetUpper() );
+ rLnBoxes.push_back( pBox );
+
+ aSelBoxes.insert( pBox );
+ }
+
+ CHECKTABLE(pTableNd->GetTable())
+
+ SwChartDataProvider *pPCD = rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ // 2. deleted the inserted boxes
+ // delete nodes (from last to first)
+ for( size_t n = m_aNewStartNodes.size(); n; )
+ {
+ // remove box from table structure
+ SwNodeOffset nIdx = m_aNewStartNodes[ --n ];
+
+ if( !nIdx && n )
+ {
+ nIdx = m_aNewStartNodes[ --n ];
+ pBox = pTableNd->GetTable().GetTableBox( nIdx );
+ OSL_ENSURE( pBox, "Where is my TableBox?" );
+
+ if( !m_pSaveTable->IsNewModel() )
+ rDoc.GetNodes().MakeTextNode( SwNodeIndex(
+ *pBox->GetSttNd()->EndOfSectionNode() ), pColl );
+
+ // this was the separator -> restore moved ones
+ for (size_t i = m_vMoves.size(); i; )
+ {
+ SwTextNode* pTextNd = nullptr;
+ sal_Int32 nDelPos = 0;
+ SwUndoMove *const pUndo = m_vMoves[ --i ].get();
+ if( !pUndo->IsMoveRange() )
+ {
+ pTextNd = rDoc.GetNodes()[ pUndo->GetDestSttNode() ]->GetTextNode();
+ nDelPos = pUndo->GetDestSttContent() - 1;
+ }
+ pUndo->UndoImpl(rContext);
+ if( pUndo->IsMoveRange() )
+ {
+ // delete the unnecessary node
+ aIdx = pUndo->GetEndNode();
+ SwContentNode *pCNd = aIdx.GetNode().GetContentNode();
+ if( pCNd )
+ {
+ SwNodeIndex aTmp( aIdx, -1 );
+ SwContentNode *pMove = aTmp.GetNode().GetContentNode();
+ if( pMove )
+ pCNd->MoveTo( *pMove );
+ }
+ rDoc.GetNodes().Delete( aIdx );
+ }
+ else if( pTextNd )
+ {
+ // also delete not needed attributes
+ SwIndex aTmpIdx( pTextNd, nDelPos );
+ if( pTextNd->GetpSwpHints() && pTextNd->GetpSwpHints()->Count() )
+ pTextNd->RstTextAttr( aTmpIdx, pTextNd->GetText().getLength() - nDelPos + 1 );
+ // delete separator
+ pTextNd->EraseText( aTmpIdx, 1 );
+ }
+ }
+ nIdx = pBox->GetSttIdx();
+ }
+ else
+ pBox = pTableNd->GetTable().GetTableBox( nIdx );
+
+ if( !m_pSaveTable->IsNewModel() )
+ {
+ // TL_CHART2: notify chart about box to be removed
+ if (pPCD)
+ pPCD->DeleteBox( &pTableNd->GetTable(), *pBox );
+
+ SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes();
+ pTBoxes->erase( std::find(pTBoxes->begin(), pTBoxes->end(), pBox ) );
+
+ // delete indices from section
+ {
+ SwNodeIndex aTmpIdx( *pBox->GetSttNd() );
+ SwDoc::CorrAbs( SwNodeIndex( aTmpIdx, 1 ),
+ SwNodeIndex( *aTmpIdx.GetNode().EndOfSectionNode() ),
+ SwPosition( aTmpIdx, SwIndex( nullptr, 0 )), true );
+ }
+
+ delete pBox;
+ rDoc.getIDocumentContentOperations().DeleteSection( rDoc.GetNodes()[ nIdx ] );
+ }
+ }
+ CHECKTABLE(pTableNd->GetTable())
+
+ m_pSaveTable->CreateNew( pTableNd->GetTable(), true, false );
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ rDoc.UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nSttNode;
+ pPam->GetPoint()->nContent.Assign( pPam->GetContentNode(), m_nSttContent );
+ pPam->SetMark();
+ pPam->DeleteMark();
+
+ CHECKTABLE(pTableNd->GetTable())
+ ClearFEShellTabCols(rDoc, nullptr);
+}
+
+void SwUndoTableMerge::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM & rPam( AddUndoRedoPaM(rContext) );
+ rDoc.MergeTable(rPam);
+}
+
+void SwUndoTableMerge::MoveBoxContent( SwDoc& rDoc, SwNodeRange& rRg, SwNodeIndex& rPos )
+{
+ SwNodeIndex aTmp( rRg.aStart, -1 ), aTmp2( rPos, -1 );
+ std::unique_ptr<SwUndoMove> pUndo(new SwUndoMove( rDoc, rRg, rPos ));
+ ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ rDoc.getIDocumentContentOperations().MoveNodeRange( rRg, rPos, m_pSaveTable->IsNewModel() ?
+ SwMoveFlags::NO_DELFRMS :
+ SwMoveFlags::DEFAULT );
+ ++aTmp;
+ ++aTmp2;
+ pUndo->SetDestRange( aTmp2, rPos, aTmp );
+
+ m_vMoves.push_back(std::move(pUndo));
+}
+
+void SwUndoTableMerge::SetSelBoxes( const SwSelBoxes& rBoxes )
+{
+ // memorize selection
+ for (size_t n = 0; n < rBoxes.size(); ++n)
+ {
+ m_Boxes.insert(rBoxes[n]->GetSttIdx());
+ }
+
+ // as separator for inserts of new boxes after shifting
+ m_aNewStartNodes.push_back( SwNodeOffset(0) );
+
+ // The new table model does not delete overlapped cells (by row span),
+ // so the rBoxes array might be empty even some cells have been merged.
+ if( !rBoxes.empty() )
+ m_nTableNode = rBoxes[ 0 ]->GetSttNd()->FindTableNode()->GetIndex();
+}
+
+void SwUndoTableMerge::SaveCollection( const SwTableBox& rBox )
+{
+ if( !m_pHistory )
+ m_pHistory.reset(new SwHistory);
+
+ SwNodeIndex aIdx( *rBox.GetSttNd(), 1 );
+ SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
+ if( !pCNd )
+ pCNd = aIdx.GetNodes().GoNext( &aIdx );
+
+ m_pHistory->Add( pCNd->GetFormatColl(), aIdx.GetIndex(), pCNd->GetNodeType());
+ if( pCNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pCNd->GetpSwAttrSet(), aIdx.GetIndex() );
+}
+
+SwUndoTableNumFormat::SwUndoTableNumFormat( const SwTableBox& rBox,
+ const SfxItemSet* pNewSet )
+ : SwUndo(SwUndoId::TBLNUMFMT, rBox.GetFrameFormat()->GetDoc())
+ , m_nFormatIdx(getSwDefaultTextFormat())
+ , m_nNewFormatIdx(0)
+ , m_fNum(0.0)
+ , m_fNewNum(0.0)
+ , m_bNewFormat(false)
+ , m_bNewFormula(false)
+ , m_bNewValue(false)
+{
+ m_nNode = rBox.GetSttIdx();
+
+ m_nNodePos = rBox.IsValidNumTextNd( nullptr == pNewSet );
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+
+ if( NODE_OFFSET_MAX != m_nNodePos )
+ {
+ SwTextNode* pTNd = pDoc->GetNodes()[ m_nNodePos ]->GetTextNode();
+
+ m_pHistory.reset(new SwHistory);
+ SwRegHistory aRHst( *rBox.GetSttNd(), m_pHistory.get() );
+ // always save all text atttibutes because of possibly overlapping
+ // areas of on/off
+ m_pHistory->CopyAttr( pTNd->GetpSwpHints(), m_nNodePos, 0,
+ pTNd->GetText().getLength(), true );
+
+ if( pTNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTNd->GetpSwAttrSet(), m_nNodePos );
+
+ m_aStr = pTNd->GetText();
+ if( pTNd->GetpSwpHints() )
+ pTNd->GetpSwpHints()->DeRegister();
+ }
+
+ m_pBoxSet.reset( new SfxItemSet( pDoc->GetAttrPool(), aTableBoxSetRange ) );
+ m_pBoxSet->Put( rBox.GetFrameFormat()->GetAttrSet() );
+
+ if( pNewSet )
+ {
+ if( const SwTableBoxNumFormat* pItem = pNewSet->GetItemIfSet( RES_BOXATR_FORMAT,
+ false ))
+ {
+ m_bNewFormat = true;
+ m_nNewFormatIdx = pItem->GetValue();
+ }
+ if( const SwTableBoxFormula* pItem = pNewSet->GetItemIfSet( RES_BOXATR_FORMULA,
+ false ))
+ {
+ m_bNewFormula = true;
+ m_aNewFormula = pItem->GetFormula();
+ }
+ if( const SwTableBoxValue* pItem = pNewSet->GetItemIfSet( RES_BOXATR_VALUE,
+ false ))
+ {
+ m_bNewValue = true;
+ m_fNewNum = pItem->GetValue();
+ }
+ }
+
+ // is a history needed at all?
+ if (m_pHistory && !m_pHistory->Count())
+ {
+ m_pHistory.reset();
+ }
+}
+
+SwUndoTableNumFormat::~SwUndoTableNumFormat()
+{
+ m_pHistory.reset();
+ m_pBoxSet.reset();
+}
+
+void SwUndoTableNumFormat::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ OSL_ENSURE( m_pBoxSet, "Where's the stored item set?" );
+
+ SwDoc & rDoc = rContext.GetDoc();
+ SwStartNode* pSttNd = rDoc.GetNodes()[ m_nNode ]->
+ FindSttNodeByType( SwTableBoxStartNode );
+ OSL_ENSURE( pSttNd, "without StartNode no TableBox" );
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "found no TableBox" );
+
+ SwTableBoxFormat* pFormat = rDoc.MakeTableBoxFormat();
+ pFormat->SetFormatAttr( *m_pBoxSet );
+ pBox->ChgFrameFormat( pFormat );
+
+ if( NODE_OFFSET_MAX == m_nNodePos )
+ return;
+
+ SwTextNode* pTextNd = rDoc.GetNodes()[ m_nNodePos ]->GetTextNode();
+ // If more than one node was deleted then all "node" attributes were also
+ // saved
+ if( pTextNd->HasSwAttrSet() )
+ pTextNd->ResetAllAttr();
+
+ if( pTextNd->GetpSwpHints() && !m_aStr.isEmpty() )
+ pTextNd->ClearSwpHintsArr( true );
+
+ // ChgTextToNum(..) only acts when the strings are different. We need to do
+ // the same here.
+ if( pTextNd->GetText() != m_aStr )
+ {
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( *( pBox->GetSttNd() ), false, RedlineType::Any );
+
+ SwIndex aIdx( pTextNd, 0 );
+ if( !m_aStr.isEmpty() )
+ {
+ pTextNd->EraseText( aIdx );
+ pTextNd->InsertText( m_aStr, aIdx,
+ SwInsertFlags::NOHINTEXPAND );
+ }
+ }
+
+ if( m_pHistory )
+ {
+ sal_uInt16 nTmpEnd = m_pHistory->GetTmpEnd();
+ m_pHistory->TmpRollback( &rDoc, 0 );
+ m_pHistory->SetTmpEnd( nTmpEnd );
+ }
+
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nNode + 1;
+ pPam->GetPoint()->nContent.Assign( pTextNd, 0 );
+}
+
+namespace {
+
+/** switch the RedlineFlags on the given document, using
+ * SetRedlineFlags_intern. This class set the mode in the constructor,
+ * and changes it back in the destructor, i.e. it uses the
+ * initialization-is-resource-acquisition idiom.
+ */
+class RedlineFlagsInternGuard
+{
+ SwDoc& mrDoc;
+ RedlineFlags meOldRedlineFlags;
+
+public:
+ RedlineFlagsInternGuard(
+ SwDoc& rDoc, // change mode of this document
+ RedlineFlags eNewRedlineFlags, // new redline mode
+ RedlineFlags eRedlineFlagsMask /*change only bits set in this mask*/);
+
+ ~RedlineFlagsInternGuard();
+};
+
+}
+
+RedlineFlagsInternGuard::RedlineFlagsInternGuard(
+ SwDoc& rDoc,
+ RedlineFlags eNewRedlineFlags,
+ RedlineFlags eRedlineFlagsMask )
+ : mrDoc( rDoc ),
+ meOldRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() )
+{
+ mrDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( meOldRedlineFlags & ~eRedlineFlagsMask ) |
+ ( eNewRedlineFlags & eRedlineFlagsMask ) );
+}
+
+RedlineFlagsInternGuard::~RedlineFlagsInternGuard()
+{
+ mrDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( meOldRedlineFlags );
+}
+
+void SwUndoTableNumFormat::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // Could the box be changed?
+ if( !m_pBoxSet )
+ return ;
+
+ SwDoc & rDoc = rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nNode;
+
+ SwNode * pNd = & pPam->GetPoint()->nNode.GetNode();
+ SwStartNode* pSttNd = pNd->FindSttNodeByType( SwTableBoxStartNode );
+ assert(pSttNd && "without StartNode no TableBox");
+ SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTableBox(
+ pSttNd->GetIndex() );
+ OSL_ENSURE( pBox, "found no TableBox" );
+
+ SwFrameFormat* pBoxFormat = pBox->ClaimFrameFormat();
+ if( m_bNewFormat || m_bNewFormula || m_bNewValue )
+ {
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( rDoc.GetAttrPool() );
+
+ // Resetting attributes is not enough. In addition, take care that the
+ // text will be also formatted correctly.
+ pBoxFormat->LockModify();
+
+ if( m_bNewFormula )
+ aBoxSet.Put( SwTableBoxFormula( m_aNewFormula ));
+ else
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ if( m_bNewFormat )
+ aBoxSet.Put( SwTableBoxNumFormat( m_nNewFormatIdx ));
+ else
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT );
+ if( m_bNewValue )
+ aBoxSet.Put( SwTableBoxValue( m_fNewNum ));
+ else
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
+ pBoxFormat->UnlockModify();
+
+ // dvo: When redlining is (was) enabled, setting the attribute
+ // will also change the cell content. To allow this, the
+ // RedlineFlags::Ignore flag must be removed during Redo. #108450#
+ RedlineFlagsInternGuard aGuard( rDoc, RedlineFlags::NONE, RedlineFlags::Ignore );
+ pBoxFormat->SetFormatAttr( aBoxSet );
+ }
+ else if( getSwDefaultTextFormat() != m_nFormatIdx )
+ {
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aBoxSet( rDoc.GetAttrPool() );
+
+ aBoxSet.Put( SwTableBoxNumFormat( m_nFormatIdx ));
+ aBoxSet.Put( SwTableBoxValue( m_fNum ));
+
+ // Resetting attributes is not enough. In addition, take care that the
+ // text will be also formatted correctly.
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ pBoxFormat->UnlockModify();
+
+ // dvo: When redlining is (was) enabled, setting the attribute
+ // will also change the cell content. To allow this, the
+ // RedlineFlags::Ignore flag must be removed during Redo. #108450#
+ RedlineFlagsInternGuard aGuard( rDoc, RedlineFlags::NONE, RedlineFlags::Ignore );
+ pBoxFormat->SetFormatAttr( aBoxSet );
+ }
+ else
+ {
+ // it's no number
+
+ // Resetting attributes is not enough. In addition, take care that the
+ // text will be also formatted correctly.
+ pBoxFormat->SetFormatAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));
+
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ }
+
+ if( m_bNewFormula )
+ {
+ // No matter what was set, an update of the table is always a good idea
+ SwTableFormulaUpdate aTableUpdate( &pSttNd->FindTableNode()->GetTable() );
+ rDoc.getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+ }
+
+ if( !pNd->IsContentNode() )
+ pNd = rDoc.GetNodes().GoNext( &pPam->GetPoint()->nNode );
+ pPam->GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pNd), 0 );
+}
+
+void SwUndoTableNumFormat::SetBox( const SwTableBox& rBox )
+{
+ m_nNode = rBox.GetSttIdx();
+}
+
+UndoTableCpyTable_Entry::UndoTableCpyTable_Entry( const SwTableBox& rBox )
+ : nBoxIdx( rBox.GetSttIdx() ), nOffset( 0 ),
+ bJoin( false )
+{
+}
+
+void UndoTableCpyTable_Entry::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("UndoTableCpyTable_Entry"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("nBoxIdx"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(sal_Int32(nBoxIdx)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("nOffset"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::number(sal_Int32(nOffset)).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (pBoxNumAttr)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pBoxNumAttr"));
+ pBoxNumAttr->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (pUndo)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pUndo"));
+ pUndo->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("bJoin"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"),
+ BAD_CAST(OString::boolean(bJoin).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwUndoTableCpyTable::SwUndoTableCpyTable(const SwDoc& rDoc)
+ : SwUndo( SwUndoId::TBLCPYTBL, &rDoc )
+{
+}
+
+SwUndoTableCpyTable::~SwUndoTableCpyTable()
+{
+ m_vArr.clear();
+ m_pInsRowUndo.reset();
+}
+
+void SwUndoTableCpyTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ DEBUG_REDLINE( &rDoc )
+
+ SwTableNode* pTableNd = nullptr;
+ for (size_t n = m_vArr.size(); n; )
+ {
+ UndoTableCpyTable_Entry *const pEntry = m_vArr[ --n ].get();
+ SwNodeOffset nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
+ SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
+ if( !pTableNd )
+ pTableNd = pSNd->FindTableNode();
+
+ SwTableBox* pBox = pTableNd->GetTable().GetTableBox( nSttPos );
+ if (!pBox)
+ {
+ SAL_WARN("sw.core",
+ "SwUndoTableCpyTable::UndoImpl: invalid start node index for table box");
+ continue;
+ }
+
+ SwTableBox& rBox = *pBox;
+
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+ rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() );
+
+ // b62341295: Redline for copying tables
+ const SwNode *pEndNode = rBox.GetSttNd()->EndOfSectionNode();
+ SwPaM aPam( aInsIdx.GetNode(), *pEndNode );
+ std::unique_ptr<SwUndoDelete> pUndo;
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) )
+ {
+ bool bDeleteCompleteParagraph = false;
+ bool bShiftPam = false;
+ // There are a couple of different situations to consider during redlining
+ if( pEntry->pUndo )
+ {
+ SwUndoDelete *const pUndoDelete =
+ dynamic_cast<SwUndoDelete*>(pEntry->pUndo.get());
+ SwUndoRedlineDelete *const pUndoRedlineDelete =
+ dynamic_cast<SwUndoRedlineDelete*>(pEntry->pUndo.get());
+ assert(pUndoDelete || pUndoRedlineDelete);
+ if (pUndoRedlineDelete)
+ {
+ // The old content was not empty or he has been merged with the new content
+ bDeleteCompleteParagraph = !pEntry->bJoin; // bJoin is set when merged
+ // Set aTmpIdx to the beginning of the old content
+ SwNodeIndex aTmpIdx( *pEndNode,
+ pUndoRedlineDelete->NodeDiff()-1 );
+ SwTextNode *pText = aTmpIdx.GetNode().GetTextNode();
+ if( pText )
+ {
+ aPam.GetPoint()->nNode = *pText;
+ aPam.GetPoint()->nContent.Assign( pText,
+ pUndoRedlineDelete->ContentStart() );
+ }
+ else
+ *aPam.GetPoint() = SwPosition( aTmpIdx );
+ }
+ else if (pUndoDelete && pUndoDelete->IsDelFullPara())
+ {
+ // When the old content was an empty paragraph, but could not be joined
+ // with the new content (e.g. because of a section or table)
+ // We "save" the aPam.Point, we go one step backwards (because later on the
+ // empty paragraph will be inserted by the undo) and set the "ShiftPam-flag
+ // for step forward later on.
+ bDeleteCompleteParagraph = true;
+ bShiftPam = true;
+ SwNodeIndex aTmpIdx( *pEndNode, -1 );
+ SwTextNode *pText = aTmpIdx.GetNode().GetTextNode();
+ if( pText )
+ {
+ aPam.GetPoint()->nNode = *pText;
+ aPam.GetPoint()->nContent.Assign( pText, 0 );
+ }
+ else
+ *aPam.GetPoint() = SwPosition( aTmpIdx );
+ }
+ }
+ rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any );
+
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ pEntry->pUndo.reset();
+ }
+ if( bShiftPam )
+ {
+ // The aPam.Point is at the moment at the last position of the new content and has to be
+ // moved to the first position of the old content for the SwUndoDelete operation
+ SwNodeIndex aTmpIdx( aPam.GetPoint()->nNode, 1 );
+ SwTextNode *pText = aTmpIdx.GetNode().GetTextNode();
+ if( pText )
+ {
+ aPam.GetPoint()->nNode = *pText;
+ aPam.GetPoint()->nContent.Assign( pText, 0 );
+ }
+ else
+ *aPam.GetPoint() = SwPosition( aTmpIdx );
+ }
+ pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, bDeleteCompleteParagraph, true);
+ }
+ else
+ {
+ pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true);
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ pEntry->pUndo.reset();
+ }
+ }
+ pEntry->pUndo = std::move(pUndo);
+
+ aInsIdx = rBox.GetSttIdx() + 1;
+ rDoc.GetNodes().Delete( aInsIdx );
+
+ SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>
+ aTmpSet(rDoc.GetAttrPool());
+ aTmpSet.Put( rBox.GetFrameFormat()->GetAttrSet() );
+ if( aTmpSet.Count() )
+ {
+ SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ pBoxFormat->ResetFormatAttr( RES_VERT_ORIENT );
+ }
+
+ if( pEntry->pBoxNumAttr )
+ {
+ rBox.ClaimFrameFormat()->SetFormatAttr( *pEntry->pBoxNumAttr );
+ pEntry->pBoxNumAttr.reset();
+ }
+
+ if( aTmpSet.Count() )
+ {
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>>(rDoc.GetAttrPool());
+ pEntry->pBoxNumAttr->Put( aTmpSet );
+ }
+
+ pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
+ }
+
+ if( m_pInsRowUndo )
+ {
+ m_pInsRowUndo->UndoImpl(rContext);
+ }
+ DEBUG_REDLINE( &rDoc )
+}
+
+void SwUndoTableCpyTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ DEBUG_REDLINE( &rDoc )
+
+ if( m_pInsRowUndo )
+ {
+ m_pInsRowUndo->RedoImpl(rContext);
+ }
+
+ SwTableNode* pTableNd = nullptr;
+ for (size_t n = 0; n < m_vArr.size(); ++n)
+ {
+ UndoTableCpyTable_Entry *const pEntry = m_vArr[ n ].get();
+ SwNodeOffset nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
+ SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
+ if( !pTableNd )
+ pTableNd = pSNd->FindTableNode();
+
+ SwTableBox& rBox = *pTableNd->GetTable().GetTableBox( nSttPos );
+
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+
+ // b62341295: Redline for copying tables - Start.
+ rDoc.GetNodes().MakeTextNode( aInsIdx, rDoc.GetDfltTextFormatColl() );
+ SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode());
+ std::unique_ptr<SwUndo> pUndo(IDocumentRedlineAccess::IsRedlineOn(GetRedlineFlags())
+ ? nullptr
+ : std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true));
+ if( pEntry->pUndo )
+ {
+ pEntry->pUndo->UndoImpl(rContext);
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ) )
+ {
+ // PrepareRedline has to be called with the beginning of the old content
+ // When new and old content has been joined, the rIter.pAktPam has been set
+ // by the Undo operation to this point.
+ // Otherwise aInsIdx has been moved during the Undo operation
+ if( pEntry->bJoin )
+ {
+ SwPaM& rLastPam =
+ rContext.GetCursorSupplier().GetCurrentShellCursor();
+ pUndo = PrepareRedline( &rDoc, rBox, *rLastPam.GetPoint(),
+ pEntry->bJoin, true );
+ }
+ else
+ {
+ SwPosition aTmpPos( aInsIdx );
+ pUndo = PrepareRedline( &rDoc, rBox, aTmpPos, pEntry->bJoin, true );
+ }
+ }
+ pEntry->pUndo.reset();
+ }
+ pEntry->pUndo = std::move(pUndo);
+ // b62341295: Redline for copying tables - End.
+
+ aInsIdx = rBox.GetSttIdx() + 1;
+ rDoc.GetNodes().Delete( aInsIdx );
+
+ SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aTmpSet(rDoc.GetAttrPool());
+ aTmpSet.Put( rBox.GetFrameFormat()->GetAttrSet() );
+ if( aTmpSet.Count() )
+ {
+ SwFrameFormat* pBoxFormat = rBox.ClaimFrameFormat();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
+ pBoxFormat->ResetFormatAttr( RES_VERT_ORIENT );
+ }
+ if( pEntry->pBoxNumAttr )
+ {
+ rBox.ClaimFrameFormat()->SetFormatAttr( *pEntry->pBoxNumAttr );
+ pEntry->pBoxNumAttr.reset();
+ }
+
+ if( aTmpSet.Count() )
+ {
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>>(rDoc.GetAttrPool());
+ pEntry->pBoxNumAttr->Put( aTmpSet );
+ }
+
+ pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
+ }
+ DEBUG_REDLINE( &rDoc )
+}
+
+void SwUndoTableCpyTable::AddBoxBefore( const SwTableBox& rBox, bool bDelContent )
+{
+ if (!m_vArr.empty() && !bDelContent)
+ return;
+
+ UndoTableCpyTable_Entry* pEntry = new UndoTableCpyTable_Entry( rBox );
+ m_vArr.push_back(std::unique_ptr<UndoTableCpyTable_Entry>(pEntry));
+
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+ DEBUG_REDLINE( pDoc )
+ if( bDelContent )
+ {
+ SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
+ pDoc->GetNodes().MakeTextNode( aInsIdx, pDoc->GetDfltTextFormatColl() );
+ SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() );
+
+ if( !pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ pEntry->pUndo = std::make_unique<SwUndoDelete>(aPam, SwDeleteFlags::Default, true);
+ }
+
+ pEntry->pBoxNumAttr = std::make_unique<SfxItemSetFixed<
+ RES_VERT_ORIENT, RES_VERT_ORIENT,
+ RES_BOXATR_FORMAT, RES_BOXATR_VALUE>>(pDoc->GetAttrPool());
+ pEntry->pBoxNumAttr->Put( rBox.GetFrameFormat()->GetAttrSet() );
+ if( !pEntry->pBoxNumAttr->Count() )
+ {
+ pEntry->pBoxNumAttr.reset();
+ }
+ DEBUG_REDLINE( pDoc )
+}
+
+void SwUndoTableCpyTable::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex& rIdx, bool bDelContent )
+{
+ UndoTableCpyTable_Entry *const pEntry = m_vArr.back().get();
+
+ // If the content was deleted then remove also the temporarily created node
+ if( bDelContent )
+ {
+ SwDoc* pDoc = rBox.GetFrameFormat()->GetDoc();
+ DEBUG_REDLINE( pDoc )
+
+ if( pDoc->getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ SwPosition aTmpPos( rIdx );
+ pEntry->pUndo = PrepareRedline( pDoc, rBox, aTmpPos, pEntry->bJoin, false );
+ }
+ SwNodeIndex aDelIdx( *rBox.GetSttNd(), 1 );
+ rBox.GetFrameFormat()->GetDoc()->GetNodes().Delete( aDelIdx );
+ DEBUG_REDLINE( pDoc )
+ }
+
+ pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
+}
+
+// PrepareRedline is called from AddBoxAfter() and from Redo() in slightly different situations.
+// bRedo is set by calling from Redo()
+// rJoin is false by calling from AddBoxAfter() and will be set if the old and new content has
+// been merged.
+// rJoin is true if Redo() is calling and the content has already been merged
+
+std::unique_ptr<SwUndo> SwUndoTableCpyTable::PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox,
+ SwPosition& rPos, bool& rJoin, bool bRedo )
+{
+ std::unique_ptr<SwUndo> pUndo;
+ // b62341295: Redline for copying tables
+ // What's to do?
+ // Mark the cell content before rIdx as insertion,
+ // mark the cell content behind rIdx as deletion
+ // merge text nodes at rIdx if possible
+ RedlineFlags eOld = pDoc->getIDocumentRedlineAccess().GetRedlineFlags();
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld | RedlineFlags::DontCombineRedlines ) & ~RedlineFlags::Ignore );
+ SwPosition aInsertEnd( rPos );
+ SwTextNode* pText;
+ if( !rJoin )
+ {
+ // If the content is not merged, the end of the insertion is at the end of the node
+ // _before_ the given position rPos
+ --aInsertEnd.nNode;
+ pText = aInsertEnd.nNode.GetNode().GetTextNode();
+ if( pText )
+ {
+ aInsertEnd.nContent.Assign(pText, pText->GetText().getLength());
+ if( !bRedo && rPos.nNode.GetNode().GetTextNode() )
+ { // Try to merge, if not called by Redo()
+ rJoin = true;
+
+ // Park this somewhere else so nothing points to the to-be-deleted node.
+ rPos.nContent.Assign(pText, 0);
+
+ pText->JoinNext();
+ }
+ }
+ else
+ aInsertEnd.nContent.Assign(nullptr, 0);
+ }
+ // For joined (merged) contents the start of deletion and end of insertion are identical
+ // otherwise adjacent nodes.
+ SwPosition aDeleteStart( rJoin ? aInsertEnd : rPos );
+ if( !rJoin )
+ {
+ pText = aDeleteStart.nNode.GetNode().GetTextNode();
+ if( pText )
+ aDeleteStart.nContent.Assign( pText, 0 );
+ }
+ SwPosition aCellEnd( SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode(), -1 ) );
+ pText = aCellEnd.nNode.GetNode().GetTextNode();
+ if( pText )
+ aCellEnd.nContent.Assign(pText, pText->GetText().getLength());
+ if( aDeleteStart != aCellEnd )
+ { // If the old (deleted) part is not empty, here we are...
+ SwPaM aDeletePam( aDeleteStart, aCellEnd );
+ pUndo = std::make_unique<SwUndoRedlineDelete>( aDeletePam, SwUndoId::DELETE );
+ pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDeletePam ), true );
+ }
+ else if( !rJoin ) // If the old part is empty and joined, we are finished
+ { // if it is not joined, we have to delete this empty paragraph
+ aCellEnd = SwPosition(
+ SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode() ));
+ SwPaM aTmpPam( aDeleteStart, aCellEnd );
+ pUndo = std::make_unique<SwUndoDelete>(aTmpPam, SwDeleteFlags::Default, true);
+ }
+ SwPosition aCellStart( SwNodeIndex( *rBox.GetSttNd(), 2 ) );
+ pText = aCellStart.nNode.GetNode().GetTextNode();
+ if( pText )
+ aCellStart.nContent.Assign( pText, 0 );
+ if( aCellStart != aInsertEnd ) // An empty insertion will not been marked
+ {
+ SwPaM aTmpPam( aCellStart, aInsertEnd );
+ pDoc->getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aTmpPam ), true );
+ }
+
+ pDoc->getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ return pUndo;
+}
+
+bool SwUndoTableCpyTable::InsertRow( SwTable& rTable, const SwSelBoxes& rBoxes,
+ sal_uInt16 nCnt )
+{
+ SwTableNode* pTableNd = const_cast<SwTableNode*>(rTable.GetTabSortBoxes()[0]->
+ GetSttNd()->FindTableNode());
+
+ m_pInsRowUndo.reset( new SwUndoTableNdsChg( SwUndoId::TABLE_INSROW, rBoxes, *pTableNd,
+ 0, 0, nCnt, true, false ) );
+ SwTableSortBoxes aTmpLst( rTable.GetTabSortBoxes() );
+
+ bool bRet = rTable.InsertRow( rTable.GetFrameFormat()->GetDoc(), rBoxes, nCnt, /*bBehind*/true );
+ if( bRet )
+ m_pInsRowUndo->SaveNewBoxes( *pTableNd, aTmpLst );
+ else
+ {
+ m_pInsRowUndo.reset();
+ }
+ return bRet;
+}
+
+void SwUndoTableCpyTable::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoTableCpyTable"));
+
+ for (const auto& pEntry : m_vArr)
+ {
+ pEntry->dumpAsXml(pWriter);
+ }
+
+ if (m_pInsRowUndo)
+ {
+ m_pInsRowUndo->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SwUndoTableCpyTable::IsEmpty() const
+{
+ return !m_pInsRowUndo && m_vArr.empty();
+}
+
+SwUndoCpyTable::SwUndoCpyTable(const SwDoc& rDoc)
+ : SwUndo( SwUndoId::CPYTBL, &rDoc ), m_nTableNode( 0 )
+{
+}
+
+SwUndoCpyTable::~SwUndoCpyTable()
+{
+}
+
+void SwUndoCpyTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc & rDoc = rContext.GetDoc();
+ SwTableNode* pTNd = rDoc.GetNodes()[ m_nTableNode ]->GetTableNode();
+
+ // move hard page breaks into next node
+ SwContentNode* pNextNd = rDoc.GetNodes()[ pTNd->EndOfSectionIndex()+1 ]->GetContentNode();
+ if( pNextNd )
+ {
+ SwFrameFormat* pTableFormat = pTNd->GetTable().GetFrameFormat();
+
+ if( const SwFormatPageDesc* pItem = pTableFormat->GetItemIfSet( RES_PAGEDESC,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+
+ if( const SvxFormatBreakItem* pItem = pTableFormat->GetItemIfSet( RES_BREAK,
+ false ) )
+ pNextNd->SetAttr( *pItem );
+ }
+
+ SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), SwNodeOffset(0) , SwNodeOffset(1) );
+ m_pDelete.reset(new SwUndoDelete(aPam, SwDeleteFlags::Default, true));
+}
+
+void SwUndoCpyTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pDelete->UndoImpl(rContext);
+ m_pDelete.reset();
+}
+
+SwUndoSplitTable::SwUndoSplitTable( const SwTableNode& rTableNd,
+ std::unique_ptr<SwSaveRowSpan> pRowSp, SplitTable_HeadlineOption eMode, bool bNewSize )
+ : SwUndo( SwUndoId::SPLIT_TABLE, &rTableNd.GetDoc() ),
+ m_nTableNode( rTableNd.GetIndex() ), m_nOffset( 0 ), mpSaveRowSpan( std::move(pRowSp) ),
+ m_nMode( eMode ), m_nFormulaEnd( 0 ), m_bCalcNewSize( bNewSize )
+{
+ switch( m_nMode )
+ {
+ case SplitTable_HeadlineOption::BoxAttrAllCopy:
+ m_pHistory.reset(new SwHistory);
+ [[fallthrough]];
+ case SplitTable_HeadlineOption::BorderCopy:
+ case SplitTable_HeadlineOption::BoxAttrCopy:
+ m_pSavedTable.reset(new SaveTable( rTableNd.GetTable(), 1, false ));
+ break;
+ default: break;
+ }
+}
+
+SwUndoSplitTable::~SwUndoSplitTable()
+{
+ m_pSavedTable.reset();
+ m_pHistory.reset();
+ mpSaveRowSpan.reset();
+}
+
+void SwUndoSplitTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ SwNodeIndex& rIdx = pPam->GetPoint()->nNode;
+ rIdx = m_nTableNode + m_nOffset;
+ pPam->GetPoint()->nContent.Assign(rIdx.GetNode().GetContentNode(), 0);
+ assert(rIdx.GetNode().GetContentNode()->Len() == 0); // empty para inserted
+
+ {
+ // avoid asserts from ~SwIndexReg
+ SwNodeIndex const idx(pDoc->GetNodes(), m_nTableNode + m_nOffset);
+ {
+ SwPaM pam(idx);
+ pam.Move(fnMoveBackward, GoInContent);
+ ::PaMCorrAbs(*pPam, *pam.GetPoint());
+ }
+
+ // remove implicitly created paragraph again
+ pDoc->GetNodes().Delete( idx );
+ }
+
+ rIdx = m_nTableNode + m_nOffset;
+ SwTableNode* pTableNd = rIdx.GetNode().GetTableNode();
+ SwTable& rTable = pTableNd->GetTable();
+
+ SwTableFormulaUpdate aMsgHint( &rTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ switch( m_nMode )
+ {
+ case SplitTable_HeadlineOption::BoxAttrAllCopy:
+ if( m_pHistory )
+ m_pHistory->TmpRollback( pDoc, m_nFormulaEnd );
+ [[fallthrough]];
+ case SplitTable_HeadlineOption::BoxAttrCopy:
+ case SplitTable_HeadlineOption::BorderCopy:
+ {
+ m_pSavedTable->CreateNew( rTable, false );
+ m_pSavedTable->RestoreAttr( rTable );
+ }
+ break;
+
+ case SplitTable_HeadlineOption::ContentCopy:
+ // the created first line has to be removed again
+ {
+ SwSelBoxes aSelBoxes;
+ SwTableBox* pBox = rTable.GetTableBox( m_nTableNode + m_nOffset + 1 );
+ SwTable::SelLineFromBox( pBox, aSelBoxes );
+ FndBox_ aTmpBox( nullptr, nullptr );
+ aTmpBox.SetTableLines( aSelBoxes, rTable );
+ aTmpBox.DelFrames( rTable );
+ rTable.DeleteSel( pDoc, aSelBoxes, nullptr, nullptr, false, false );
+ }
+ break;
+ default: break;
+ }
+
+ pDoc->GetNodes().MergeTable( rIdx );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+ if( mpSaveRowSpan )
+ {
+ pTableNd = rIdx.GetNode().FindTableNode();
+ if( pTableNd )
+ pTableNd->GetTable().RestoreRowSpan( *mpSaveRowSpan );
+ }
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoSplitTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nTableNode;
+ pDoc->SplitTable( *pPam->GetPoint(), m_nMode, m_bCalcNewSize );
+
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoSplitTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+ SwDoc *const pDoc = & rContext.GetDoc();
+
+ pDoc->SplitTable( *pPam->GetPoint(), m_nMode, m_bCalcNewSize );
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoSplitTable::SaveFormula( SwHistory& rHistory )
+{
+ if( !m_pHistory )
+ m_pHistory.reset(new SwHistory);
+
+ m_nFormulaEnd = rHistory.Count();
+ m_pHistory->Move( 0, &rHistory );
+}
+
+SwUndoMergeTable::SwUndoMergeTable( const SwTableNode& rTableNd,
+ const SwTableNode& rDelTableNd,
+ bool bWithPrv, sal_uInt16 nMd )
+ : SwUndo( SwUndoId::MERGE_TABLE, &rTableNd.GetDoc() ),
+ m_nMode( nMd ), m_bWithPrev( bWithPrv )
+{
+ // memorize end node of the last table cell that'll stay in position
+ if( m_bWithPrev )
+ m_nTableNode = rDelTableNd.EndOfSectionIndex() - 1;
+ else
+ m_nTableNode = rTableNd.EndOfSectionIndex() - 1;
+
+ m_aName = rDelTableNd.GetTable().GetFrameFormat()->GetName();
+ m_pSaveTable.reset(new SaveTable( rDelTableNd.GetTable() ));
+
+ if (m_bWithPrev)
+ m_pSaveHdl.reset( new SaveTable( rTableNd.GetTable(), 1 ) );
+}
+
+SwUndoMergeTable::~SwUndoMergeTable()
+{
+ m_pSaveTable.reset();
+ m_pSaveHdl.reset();
+ m_pHistory.reset();
+}
+
+void SwUndoMergeTable::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ SwNodeIndex& rIdx = pPam->GetPoint()->nNode;
+ rIdx = m_nTableNode;
+
+ SwTableNode* pTableNd = rIdx.GetNode().FindTableNode();
+ SwTable* pTable = &pTableNd->GetTable();
+
+ SwTableFormulaUpdate aMsgHint( pTable );
+ aMsgHint.m_eFlags = TBL_BOXPTR;
+ pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
+
+ // get lines for layout update
+ FndBox_ aFndBox( nullptr, nullptr );
+ aFndBox.SetTableLines( *pTable );
+ aFndBox.DelFrames( *pTable );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ SwTableNode* pNew = pDoc->GetNodes().SplitTable( rIdx );
+
+ // update layout
+ aFndBox.MakeFrames( *pTable );
+ // ? TL_CHART2: notification or locking of controller required ?
+
+ if( m_bWithPrev )
+ {
+ // move name
+ pNew->GetTable().GetFrameFormat()->SetName( pTable->GetFrameFormat()->GetName() );
+ m_pSaveHdl->RestoreAttr( pNew->GetTable() );
+ }
+ else
+ pTable = &pNew->GetTable();
+
+ pTable->GetFrameFormat()->SetName( m_aName );
+ m_pSaveTable->RestoreAttr( *pTable );
+
+ if( m_pHistory )
+ {
+ m_pHistory->TmpRollback( pDoc, 0 );
+ m_pHistory->SetTmpEnd( m_pHistory->Count() );
+ }
+
+ // create frames for the new table
+ SwNodeIndex aTmpIdx( *pNew );
+ pNew->MakeOwnFrames(&aTmpIdx);
+
+ // position cursor somewhere in content
+ SwContentNode* pCNd = pDoc->GetNodes().GoNext( &rIdx );
+ pPam->GetPoint()->nContent.Assign( pCNd, 0 );
+
+ ClearFEShellTabCols(*pDoc, nullptr);
+
+ // TL_CHART2: need to inform chart of probably changed cell names
+ SwChartDataProvider *pPCD = pDoc->getIDocumentChartDataProviderAccess().GetChartDataProvider();
+ if (pPCD)
+ {
+ pDoc->UpdateCharts( pTable->GetFrameFormat()->GetName() );
+ pDoc->UpdateCharts( pNew->GetTable().GetFrameFormat()->GetName() );
+ }
+}
+
+void SwUndoMergeTable::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
+
+ pPam->DeleteMark();
+ pPam->GetPoint()->nNode = m_nTableNode;
+ if( m_bWithPrev )
+ pPam->GetPoint()->nNode = m_nTableNode + 3;
+ else
+ pPam->GetPoint()->nNode = m_nTableNode;
+
+ pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev, m_nMode );
+
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoMergeTable::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwDoc *const pDoc = & rContext.GetDoc();
+ SwPaM *const pPam = & rContext.GetRepeatPaM();
+
+ pDoc->MergeTable( *pPam->GetPoint(), m_bWithPrev, m_nMode );
+ ClearFEShellTabCols(*pDoc, nullptr);
+}
+
+void SwUndoMergeTable::SaveFormula( SwHistory& rHistory )
+{
+ if( !m_pHistory )
+ m_pHistory.reset( new SwHistory );
+ m_pHistory->Move( 0, &rHistory );
+}
+
+void InsertSort( std::vector<sal_uInt16>& rArr, sal_uInt16 nIdx )
+{
+ size_t nO = rArr.size();
+ size_t nU = 0;
+ if( nO > 0 )
+ {
+ nO--;
+ while( nU <= nO )
+ {
+ const size_t nM = nU + ( nO - nU ) / 2;
+ if ( rArr[nM] == nIdx )
+ {
+ OSL_FAIL( "Index already exists. This should never happen." );
+ return;
+ }
+ if( rArr[nM] < nIdx )
+ nU = nM + 1;
+ else if( nM == 0 )
+ break;
+ else
+ nO = nM - 1;
+ }
+ }
+ rArr.insert( rArr.begin() + nU, nIdx );
+}
+
+#if OSL_DEBUG_LEVEL > 0
+void CheckTable( const SwTable& rTable )
+{
+ const SwNodes& rNds = rTable.GetFrameFormat()->GetDoc()->GetNodes();
+ const SwTableSortBoxes& rSrtArr = rTable.GetTabSortBoxes();
+ for (size_t n = 0; n < rSrtArr.size(); ++n)
+ {
+ const SwTableBox* pBox = rSrtArr[ n ];
+ const SwNode* pNd = pBox->GetSttNd();
+ OSL_ENSURE( rNds[ pBox->GetSttIdx() ] == pNd, "Box with wrong StartNode" );
+ }
+}
+#endif
+
+SwUndoTableStyleMake::SwUndoTableStyleMake(const OUString& rName, const SwDoc& rDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_CREATE, &rDoc),
+ m_sName(rName)
+{ }
+
+SwUndoTableStyleMake::~SwUndoTableStyleMake()
+{ }
+
+void SwUndoTableStyleMake::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ m_pAutoFormat = rContext.GetDoc().DelTableStyle(m_sName, true);
+}
+
+void SwUndoTableStyleMake::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ if (m_pAutoFormat)
+ {
+ SwTableAutoFormat* pFormat = rContext.GetDoc().MakeTableStyle(m_sName, true);
+ if (pFormat)
+ {
+ *pFormat = *m_pAutoFormat;
+ m_pAutoFormat.reset();
+ }
+ }
+}
+
+SwRewriter SwUndoTableStyleMake::GetRewriter() const
+{
+ SwRewriter aResult;
+ aResult.AddRule(UndoArg1, m_sName);
+ return aResult;
+}
+
+SwUndoTableStyleDelete::SwUndoTableStyleDelete(std::unique_ptr<SwTableAutoFormat> pAutoFormat, std::vector<SwTable*>&& rAffectedTables, const SwDoc& rDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_DELETE, &rDoc),
+ m_pAutoFormat(std::move(pAutoFormat)),
+ m_rAffectedTables(std::move(rAffectedTables))
+{ }
+
+SwUndoTableStyleDelete::~SwUndoTableStyleDelete()
+{ }
+
+void SwUndoTableStyleDelete::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwTableAutoFormat* pNewFormat = rContext.GetDoc().MakeTableStyle(m_pAutoFormat->GetName(), true);
+ *pNewFormat = *m_pAutoFormat;
+ for (size_t i=0; i < m_rAffectedTables.size(); i++)
+ m_rAffectedTables[i]->SetTableStyleName(m_pAutoFormat->GetName());
+}
+
+void SwUndoTableStyleDelete::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // Don't need to remember deleted table style nor affected tables, because they must be the same as these already known.
+ rContext.GetDoc().DelTableStyle(m_pAutoFormat->GetName());
+}
+
+SwRewriter SwUndoTableStyleDelete::GetRewriter() const
+{
+ SwRewriter aResult;
+ aResult.AddRule(UndoArg1, m_pAutoFormat->GetName());
+ return aResult;
+}
+
+SwUndoTableStyleUpdate::SwUndoTableStyleUpdate(const SwTableAutoFormat& rNewFormat, const SwTableAutoFormat& rOldFormat, const SwDoc& rDoc)
+ : SwUndo(SwUndoId::TBLSTYLE_UPDATE, &rDoc)
+ , m_pOldFormat(new SwTableAutoFormat(rOldFormat))
+ , m_pNewFormat(new SwTableAutoFormat(rNewFormat))
+{ }
+
+SwUndoTableStyleUpdate::~SwUndoTableStyleUpdate()
+{ }
+
+void SwUndoTableStyleUpdate::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ rContext.GetDoc().ChgTableStyle(m_pNewFormat->GetName(), *m_pOldFormat);
+}
+
+void SwUndoTableStyleUpdate::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ rContext.GetDoc().ChgTableStyle(m_pNewFormat->GetName(), *m_pNewFormat);
+}
+
+SwRewriter SwUndoTableStyleUpdate::GetRewriter() const
+{
+ SwRewriter aResult;
+ aResult.AddRule(UndoArg1, m_pNewFormat->GetName());
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/undo/untblk.cxx b/sw/source/core/undo/untblk.cxx
new file mode 100644
index 000000000..2e53ff2d9
--- /dev/null
+++ b/sw/source/core/undo/untblk.cxx
@@ -0,0 +1,487 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <libxml/xmlwriter.h>
+
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IShellCursorSupplier.hxx>
+#include <docary.hxx>
+#include <swcrsr.hxx>
+#include <swundo.hxx>
+#include <pam.hxx>
+#include <mvsave.hxx>
+#include <ndtxt.hxx>
+#include <UndoCore.hxx>
+#include <rolbck.hxx>
+#include <redline.hxx>
+#include <frameformats.hxx>
+
+namespace sw {
+
+std::optional<std::vector<SwFrameFormat*>>
+GetFlysAnchoredAt(SwDoc & rDoc, SwNodeOffset const nSttNode)
+{
+ std::optional<std::vector<SwFrameFormat*>> pFrameFormats;
+ const size_t nArrLen = rDoc.GetSpzFrameFormats()->size();
+ for (size_t n = 0; n < nArrLen; ++n)
+ {
+ SwFrameFormat *const pFormat = (*rDoc.GetSpzFrameFormats())[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ SwPosition const*const pAPos = pAnchor->GetContentAnchor();
+ if (pAPos
+ && nSttNode == pAPos->nNode.GetIndex()
+ && ((pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ || (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)))
+ {
+ if (!pFrameFormats)
+ pFrameFormats.emplace();
+ pFrameFormats->push_back( pFormat );
+ }
+ }
+ return pFrameFormats;
+}
+
+} // namespace sw
+
+//note: parameter is SwPam just so we can init SwUndRng, the End is ignored!
+SwUndoInserts::SwUndoInserts( SwUndoId nUndoId, const SwPaM& rPam )
+ : SwUndo( nUndoId, &rPam.GetDoc() )
+ , SwUndRng( rPam )
+ , m_pTextFormatColl(nullptr)
+ , m_pLastNodeColl(nullptr)
+ , m_nDeleteTextNodes(1)
+ , m_nNodeDiff(0)
+ , m_nSetPos(0)
+{
+ m_pHistory.reset( new SwHistory );
+ SwDoc& rDoc = rPam.GetDoc();
+
+ SwTextNode* pTextNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ {
+ m_pTextFormatColl = pTextNd->GetTextColl();
+ assert(m_pTextFormatColl);
+ m_pHistory->CopyAttr( pTextNd->GetpSwpHints(), m_nSttNode,
+ 0, pTextNd->GetText().getLength(), false );
+ if( pTextNd->HasSwAttrSet() )
+ m_pHistory->CopyFormatAttr( *pTextNd->GetpSwAttrSet(), m_nSttNode );
+
+ // We may have some flys anchored to paragraph where we inserting.
+ // These flys will be saved in pFrameFormats array (only flys which exist BEFORE insertion!)
+ // Then in SwUndoInserts::SetInsertRange the flys saved in pFrameFormats will NOT create Undos.
+ // m_FlyUndos will only be filled with newly inserted flys.
+ m_pFrameFormats = sw::GetFlysAnchoredAt(rDoc, m_nSttNode);
+ }
+ // consider Redline
+ if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() )
+ {
+ m_pRedlineData.reset( new SwRedlineData( RedlineType::Insert, rDoc.getIDocumentRedlineAccess().GetRedlineAuthor() ) );
+ SetRedlineFlags( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
+ }
+}
+
+// This method does two things:
+// 1. Adjusts SwUndoRng members, required for Undo.
+// Members are:
+// SwUndoRng::nSttNode - all nodes starting from this node will be deleted during Undo (in SwUndoInserts::UndoImpl)
+// SwUndoRng::nSttContent - corresponding content index in SwUndoRng::nSttNode
+// SwUndoRng::nEndNode - end node for deletion
+// SwUndoRng::nEndContent - end content index
+// All these members are filled in during construction of SwUndoInserts instance, and can be adjusted using this method
+//
+// 2. Fills in m_FlyUndos array with flys anchored ONLY to first and last paragraphs (first == rPam.Start(), last == rPam.End())
+// Flys, anchored to any paragraph, but not first and last, are handled by DelContentIndex (see SwUndoInserts::UndoImpl) and are not stored in m_FlyUndos.
+
+void SwUndoInserts::SetInsertRange( const SwPaM& rPam, bool bScanFlys,
+ SwNodeOffset const nDeleteTextNodes)
+{
+ const SwPosition* pTmpPos = rPam.End();
+ m_nEndNode = pTmpPos->nNode.GetIndex();
+ m_nEndContent = pTmpPos->nContent.GetIndex();
+ if( rPam.HasMark() )
+ {
+ if( pTmpPos == rPam.GetPoint() )
+ pTmpPos = rPam.GetMark();
+ else
+ pTmpPos = rPam.GetPoint();
+
+ m_nSttNode = pTmpPos->nNode.GetIndex();
+ m_nSttContent = pTmpPos->nContent.GetIndex();
+
+ m_nDeleteTextNodes = nDeleteTextNodes;
+ if (m_nDeleteTextNodes == SwNodeOffset(0)) // if a table selection is added...
+ {
+ ++m_nSttNode; // ... then the CopyPam is not fully correct
+ }
+ }
+
+ // Fill m_FlyUndos with flys anchored to first and last paragraphs
+
+ if( !bScanFlys)
+ return;
+
+ // than collect all new Flys
+ SwDoc& rDoc = rPam.GetDoc();
+ const size_t nArrLen = rDoc.GetSpzFrameFormats()->size();
+ for( size_t n = 0; n < nArrLen; ++n )
+ {
+ SwFrameFormat* pFormat = (*rDoc.GetSpzFrameFormats())[n];
+ SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
+ if (IsCreateUndoForNewFly(*pAnchor, m_nSttNode, m_nEndNode))
+ {
+ std::vector<SwFrameFormat*>::iterator it;
+ if( !m_pFrameFormats ||
+ m_pFrameFormats->end() == ( it = std::find( m_pFrameFormats->begin(), m_pFrameFormats->end(), pFormat ) ) )
+ {
+ std::shared_ptr<SwUndoInsLayFormat> const pFlyUndo =
+ std::make_shared<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0);
+ m_FlyUndos.push_back(pFlyUndo);
+ }
+ else
+ m_pFrameFormats->erase( it );
+ }
+ }
+ m_pFrameFormats.reset();
+}
+
+/** This is not the same as IsDestroyFrameAnchoredAtChar()
+ and intentionally so: because the SwUndoInserts::UndoImpl() must remove
+ the flys at the start/end position that were inserted but not the ones
+ at the start/insert position that were already there;
+ handle all at-char flys at start/end node like this, even if they're
+ not *on* the start/end position, because it makes it easier to ensure
+ that the Undo/Redo run in inverse order.
+ */
+bool SwUndoInserts::IsCreateUndoForNewFly(SwFormatAnchor const& rAnchor,
+ SwNodeOffset const nStartNode, SwNodeOffset const nEndNode)
+{
+ assert(nStartNode <= nEndNode);
+
+ // check all at-char flys at the start/end nodes:
+ // ExcludeFlyAtStartEnd will exclude them!
+ SwPosition const*const pAnchorPos = rAnchor.GetContentAnchor();
+ return pAnchorPos != nullptr
+ && ( rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA
+ || rAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ && ( nStartNode == pAnchorPos->nNode.GetIndex()
+ || nEndNode == pAnchorPos->nNode.GetIndex());
+}
+
+void SwUndoInserts::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwUndoInserts"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s",
+ BAD_CAST(typeid(*this).name()));
+
+ SwUndo::dumpAsXml(pWriter);
+ SwUndoSaveContent::dumpAsXml(pWriter);
+
+ if (m_pFrameFormats)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_pFrameFormats"));
+ for (const auto& pFormat : *m_pFrameFormats)
+ {
+ pFormat->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ if (!m_FlyUndos.empty())
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("m_FlyUndos"));
+ for (const auto& pFly : m_FlyUndos)
+ {
+ pFly->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SwUndoInserts::~SwUndoInserts()
+{
+ if (m_pUndoNodeIndex) // delete also the section from UndoNodes array
+ {
+ // Insert saves content in IconSection
+ SwNodes& rUNds = m_pUndoNodeIndex->GetNodes();
+ rUNds.Delete(*m_pUndoNodeIndex,
+ rUNds.GetEndOfExtras().GetIndex() - m_pUndoNodeIndex->GetIndex());
+ m_pUndoNodeIndex.reset();
+ }
+ m_pFrameFormats.reset();
+ m_pRedlineData.reset();
+}
+
+// Undo Insert operation
+// It's important to note that Undo stores absolute node indexes. I.e. if during insertion, you insert nodes 31 to 33,
+// during Undo nodes with indices from 31 to 33 will be deleted. Undo doesn't check that nodes 31 to 33 are the same nodes which were inserted.
+// It just deletes them.
+// This may seem as bad programming practice, but Undo actions are strongly ordered. If you change your document in some way, a new Undo action is added.
+// During Undo most recent actions will be executed first. So during execution of particular Undo action indices will be correct.
+// But storing absolute indices leads to crashes if some action in Undo fails to roll back some modifications.
+
+// Has following main steps:
+// 1. m_FlyUndos removes flys anchored to first and last paragraph in Undo range.
+// This array may be empty.
+// 2. DelContentIndex to delete footnotes, flys, bookmarks (see comment for this function)
+// Deleted flys are stored in pHistory array.
+// First and last paragraphs flys are not deleted by DelContentIndex!
+// For flys anchored to last paragraph, DelContentIndex re-anchors them to
+// the last paragraph that will remain after Undo.
+// 3. MoveToUndoNds moves nodes to Undo nodes array and removes them from document.
+// 4. Lastly (starting from if(pTextNode)), text from last paragraph is joined to last remaining paragraph and FormatColl for last paragraph is restored.
+// Format coll for last paragraph is removed during execution of UndoImpl
+
+void SwUndoInserts::UndoImpl(::sw::UndoRedoContext & rContext)
+{
+ SwDoc& rDoc = rContext.GetDoc();
+ SwPaM& rPam = AddUndoRedoPaM(rContext);
+
+ m_nNodeDiff = SwNodeOffset(0);
+
+ if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ rDoc.getIDocumentRedlineAccess().DeleteRedline(rPam, true, RedlineType::Any);
+
+ // if Point and Mark are different text nodes so a JoinNext has to be done
+ bool bJoinNext = m_nSttNode != m_nEndNode &&
+ rPam.GetMark()->nNode.GetNode().GetTextNode() &&
+ rPam.GetPoint()->nNode.GetNode().GetTextNode();
+
+ // Is there any content? (loading from template does not have content)
+ if( m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent )
+ {
+ if( m_nSttNode != m_nEndNode )
+ {
+ SwTextNode* pTextNd = rDoc.GetNodes()[ m_nEndNode ]->GetTextNode();
+ if (pTextNd && pTextNd->GetText().getLength() == m_nEndContent)
+ m_pLastNodeColl = pTextNd->GetTextColl();
+ }
+
+ // tdf#128739 correct cursors but do not delete bookmarks yet
+ ::PaMCorrAbs(rPam, *rPam.End());
+
+ SetPaM(rPam);
+ }
+
+ // ... for consistency with the Insert File code in shellio.cxx, which
+ // creates separate SwUndoInsLayFormat for mysterious reasons, do this
+ // *before* anything else:
+ // after SetPaM but before MoveToUndoNds and DelContentIndex.
+ // note: there isn't an order dep wrt. initial Copy action because Undo
+ // overwrites the indexes but there is wrt. Redo because that uses the
+ // indexes
+ if (!m_FlyUndos.empty())
+ {
+ SwNodeOffset nTmp = rPam.GetPoint()->nNode.GetIndex();
+ for (size_t n = m_FlyUndos.size(); 0 < n; --n)
+ {
+ m_FlyUndos[ n-1 ]->UndoImpl(rContext);
+ }
+ m_nNodeDiff += nTmp - rPam.GetPoint()->nNode.GetIndex();
+ }
+
+ if (m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent)
+ {
+ // are there Footnotes or ContentFlyFrames in text?
+ m_nSetPos = m_pHistory->Count();
+ SwNodeOffset nTmp = rPam.GetMark()->nNode.GetIndex();
+ DelContentIndex(*rPam.GetMark(), *rPam.GetPoint(),
+ DelContentType::AllMask|DelContentType::ExcludeFlyAtStartEnd);
+ m_nNodeDiff += nTmp - rPam.GetMark()->nNode.GetIndex();
+ if( *rPam.GetPoint() != *rPam.GetMark() )
+ {
+ m_pUndoNodeIndex.reset(
+ new SwNodeIndex(rDoc.GetNodes().GetEndOfContent()));
+ MoveToUndoNds(rPam, m_pUndoNodeIndex.get());
+
+ if (m_nDeleteTextNodes == SwNodeOffset(0))
+ {
+ rPam.Move( fnMoveBackward, GoInContent );
+ }
+ }
+ }
+
+ SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
+ SwTextNode* pTextNode = rIdx.GetNode().GetTextNode();
+ if( !pTextNode )
+ return;
+
+ if( !m_pTextFormatColl ) // if 0 than it's no TextNode -> delete
+ {
+ SwNodeIndex aDelIdx( rIdx );
+ assert(SwNodeOffset(0) < m_nDeleteTextNodes && m_nDeleteTextNodes < SwNodeOffset(3));
+ for (SwNodeOffset i(0); i < m_nDeleteTextNodes; ++i)
+ {
+ rPam.Move(fnMoveForward, GoInNode);
+ }
+ rPam.DeleteMark();
+
+ for (SwNodeOffset i(0); i < m_nDeleteTextNodes; ++i)
+ {
+ RemoveIdxRel(aDelIdx.GetIndex() + i, *rPam.GetPoint());
+ }
+
+ rDoc.GetNodes().Delete( aDelIdx, m_nDeleteTextNodes );
+ }
+ else
+ {
+ if( bJoinNext && pTextNode->CanJoinNext())
+ {
+ {
+ RemoveIdxRel( rIdx.GetIndex()+1, SwPosition( rIdx,
+ SwIndex( pTextNode, pTextNode->GetText().getLength() )));
+ }
+ pTextNode->JoinNext();
+ }
+ // reset all text attributes in the paragraph!
+ pTextNode->RstTextAttr( SwIndex(pTextNode, 0), pTextNode->Len(), 0, nullptr, true );
+
+ pTextNode->ResetAllAttr();
+
+ if (rDoc.GetTextFormatColls()->IsAlive(m_pTextFormatColl))
+ m_pTextFormatColl = static_cast<SwTextFormatColl*>(pTextNode->ChgFormatColl( m_pTextFormatColl )) ;
+
+ m_pHistory->SetTmpEnd( m_nSetPos );
+ m_pHistory->TmpRollback(&rDoc, 0, false);
+ }
+}
+
+// See SwUndoInserts::UndoImpl comments
+// All actions here should be done in reverse order to what is done in SwUndoInserts::UndoImpl!
+
+void SwUndoInserts::RedoImpl(::sw::UndoRedoContext & rContext)
+{
+ // position cursor onto REDO section
+ SwCursor& rPam(rContext.GetCursorSupplier().CreateNewShellCursor());
+ SwDoc& rDoc = rPam.GetDoc();
+ rPam.DeleteMark();
+ rPam.GetPoint()->nNode = m_nSttNode - m_nNodeDiff;
+ SwContentNode* pCNd = rPam.GetContentNode();
+ rPam.GetPoint()->nContent.Assign( pCNd, m_nSttContent );
+
+ SwTextFormatColl* pSavTextFormatColl = m_pTextFormatColl;
+ if( m_pTextFormatColl && pCNd && pCNd->IsTextNode() )
+ pSavTextFormatColl = static_cast<SwTextNode*>(pCNd)->GetTextColl();
+
+ m_pHistory->SetTmpEnd( m_nSetPos );
+
+ // retrieve start position for rollback
+ if( ( m_nSttNode != m_nEndNode || m_nSttContent != m_nEndContent ) && m_pUndoNodeIndex)
+ {
+ auto const pFlysAtInsPos(sw::GetFlysAnchoredAt(rDoc,
+ rPam.GetPoint()->nNode.GetIndex()));
+
+ const bool bMvBkwrd = MovePtBackward(rPam);
+
+ // re-insert content again (first detach m_pUndoNodeIndex!)
+ SwNodeOffset const nMvNd = m_pUndoNodeIndex->GetIndex();
+ m_pUndoNodeIndex.reset();
+ MoveFromUndoNds(rDoc, nMvNd, *rPam.GetMark());
+ if (m_nDeleteTextNodes != SwNodeOffset(0))
+ {
+ MovePtForward(rPam, bMvBkwrd);
+ }
+ rPam.Exchange();
+
+ // at-char anchors post SplitNode are on index 0 of 2nd node and will
+ // remain there - move them back to the start (end would also work?)
+ if (pFlysAtInsPos)
+ {
+ for (SwFrameFormat * pFly : *pFlysAtInsPos)
+ {
+ SwFormatAnchor const*const pAnchor = &pFly->GetAnchor();
+ if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR)
+ {
+ SwFormatAnchor anchor(*pAnchor);
+ anchor.SetAnchor( rPam.GetMark() );
+ pFly->SetFormatAttr(anchor);
+ }
+ }
+ }
+ }
+
+ if (m_pTextFormatColl && rDoc.GetTextFormatColls()->IsAlive(m_pTextFormatColl))
+ {
+ SwTextNode* pTextNd = rPam.GetMark()->nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ pTextNd->ChgFormatColl( m_pTextFormatColl );
+ }
+ m_pTextFormatColl = pSavTextFormatColl;
+
+ if (m_pLastNodeColl && rDoc.GetTextFormatColls()->IsAlive(m_pLastNodeColl)
+ && rPam.GetPoint()->nNode != rPam.GetMark()->nNode)
+ {
+ SwTextNode* pTextNd = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ if( pTextNd )
+ pTextNd->ChgFormatColl( m_pLastNodeColl );
+ }
+
+ // tdf#108124 the SwHistoryChangeFlyAnchor/SwHistoryFlyCnt must run before
+ // m_FlyUndos as they were created by DelContentIndex()
+ m_pHistory->Rollback( &rDoc, m_nSetPos );
+
+ // tdf#108124 (10/25/2017)
+ // During UNDO we call SwUndoInsLayFormat::UndoImpl in reverse order,
+ // firstly for m_FlyUndos[ m_FlyUndos.size()-1 ], etc.
+ // As absolute node index of fly stored in SwUndoFlyBase::nNdPgPos we
+ // should recover from Undo in direct order (last should be recovered first)
+ // During REDO we should recover Flys (Images) in direct order,
+ // firstly m_FlyUndos[0], then with m_FlyUndos[1] index, etc.
+
+ for (size_t n = 0; m_FlyUndos.size() > n; ++n)
+ {
+ m_FlyUndos[n]->RedoImpl(rContext);
+ }
+
+ if( m_pRedlineData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineFlags() ))
+ {
+ RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags();
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld & ~RedlineFlags::Ignore );
+ rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( *m_pRedlineData, rPam ), true);
+ rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld );
+ }
+ else if( !( RedlineFlags::Ignore & GetRedlineFlags() ) &&
+ !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )
+ rDoc.getIDocumentRedlineAccess().SplitRedline(rPam);
+}
+
+void SwUndoInserts::RepeatImpl(::sw::RepeatContext & rContext)
+{
+ SwPaM aPam( rContext.GetDoc().GetNodes().GetEndOfContent() );
+ SetPaM( aPam );
+ SwPaM & rRepeatPaM( rContext.GetRepeatPaM() );
+ aPam.GetDoc().getIDocumentContentOperations().CopyRange( aPam, *rRepeatPaM.GetPoint(), SwCopyFlags::CheckPosInFly);
+}
+
+SwUndoInsDoc::SwUndoInsDoc( const SwPaM& rPam )
+ : SwUndoInserts( SwUndoId::INSDOKUMENT, rPam )
+{
+}
+
+SwUndoCpyDoc::SwUndoCpyDoc( const SwPaM& rPam )
+ : SwUndoInserts( SwUndoId::COPY, rPam )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/SwXTextDefaults.cxx b/sw/source/core/unocore/SwXTextDefaults.cxx
new file mode 100644
index 000000000..b4107f5f1
--- /dev/null
+++ b/sw/source/core/unocore/SwXTextDefaults.cxx
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <svl/itemprop.hxx>
+
+#include <SwXTextDefaults.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <fchrfmt.hxx>
+#include <charfmt.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <docstyle.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <unomap.hxx>
+#include <unomid.h>
+#include <paratr.hxx>
+#include <unocrsrhelper.hxx>
+#include <hintids.hxx>
+#include <fmtpdsc.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+SwXTextDefaults::SwXTextDefaults ( SwDoc * pNewDoc ) :
+ m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_TEXT_DEFAULT ) ),
+ m_pDoc ( pNewDoc )
+{
+}
+
+SwXTextDefaults::~SwXTextDefaults ()
+{
+}
+
+uno::Reference< XPropertySetInfo > SAL_CALL SwXTextDefaults::getPropertySetInfo( )
+{
+ static uno::Reference < XPropertySetInfo > xRef = m_pPropSet->getPropertySetInfo();
+ return xRef;
+}
+
+void SAL_CALL SwXTextDefaults::setPropertyValue( const OUString& rPropertyName, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if (!m_pDoc)
+ throw RuntimeException();
+ const SfxItemPropertyMapEntry *pMap = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if (!pMap)
+ throw UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ if ( pMap->nFlags & PropertyAttribute::READONLY)
+ throw PropertyVetoException ( "Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ const SfxPoolItem& rItem = m_pDoc->GetDefault(pMap->nWID);
+ if (RES_PAGEDESC == pMap->nWID && MID_PAGEDESC_PAGEDESCNAME == pMap->nMemberId)
+ {
+ SfxItemSetFixed<RES_PAGEDESC, RES_PAGEDESC> aSet( m_pDoc->GetAttrPool() );
+ aSet.Put(rItem);
+ SwUnoCursorHelper::SetPageDesc( aValue, *m_pDoc, aSet );
+ m_pDoc->SetDefault(aSet.Get(RES_PAGEDESC));
+ }
+ else if ((RES_PARATR_DROP == pMap->nWID && MID_DROPCAP_CHAR_STYLE_NAME == pMap->nMemberId) ||
+ (RES_TXTATR_CHARFMT == pMap->nWID))
+ {
+ OUString uStyle;
+ if(!(aValue >>= uStyle))
+ throw lang::IllegalArgumentException();
+
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle, SwGetPoolIdFromName::ChrFmt );
+ SwDocStyleSheet* pStyle =
+ static_cast<SwDocStyleSheet*>(m_pDoc->GetDocShell()->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
+ std::unique_ptr<SwFormatDrop> pDrop;
+ std::unique_ptr<SwFormatCharFormat> pCharFormat;
+ if(!pStyle)
+ throw lang::IllegalArgumentException();
+
+ rtl::Reference< SwDocStyleSheet > xStyle( new SwDocStyleSheet( *pStyle ) );
+ if (xStyle->GetCharFormat() == m_pDoc->GetDfltCharFormat())
+ return; // don't SetCharFormat with formats from mpDfltCharFormat
+
+ if (RES_PARATR_DROP == pMap->nWID)
+ {
+ pDrop.reset(static_cast<SwFormatDrop*>(rItem.Clone())); // because rItem is const...
+ pDrop->SetCharFormat(xStyle->GetCharFormat());
+ m_pDoc->SetDefault(*pDrop);
+ }
+ else // RES_TXTATR_CHARFMT == pMap->nWID
+ {
+ pCharFormat.reset(static_cast<SwFormatCharFormat*>(rItem.Clone())); // because rItem is const...
+ pCharFormat->SetCharFormat(xStyle->GetCharFormat());
+ m_pDoc->SetDefault(*pCharFormat);
+ }
+ }
+ else
+ {
+ std::unique_ptr<SfxPoolItem> pNewItem(rItem.Clone());
+ pNewItem->PutValue( aValue, pMap->nMemberId);
+ m_pDoc->SetDefault(*pNewItem);
+ }
+}
+
+Any SAL_CALL SwXTextDefaults::getPropertyValue( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if (!m_pDoc)
+ throw RuntimeException();
+ const SfxItemPropertyMapEntry *pMap = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if (!pMap)
+ throw UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ Any aRet;
+ const SfxPoolItem& rItem = m_pDoc->GetDefault(pMap->nWID);
+ rItem.QueryValue( aRet, pMap->nMemberId );
+ return aRet;
+}
+
+void SAL_CALL SwXTextDefaults::addPropertyChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL ( "not implemented" );
+}
+
+void SAL_CALL SwXTextDefaults::removePropertyChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL ( "not implemented" );
+}
+
+void SAL_CALL SwXTextDefaults::addVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL ( "not implemented" );
+}
+
+void SAL_CALL SwXTextDefaults::removeVetoableChangeListener( const OUString& /*rPropertyName*/, const uno::Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL ( "not implemented" );
+}
+
+// XPropertyState
+PropertyState SAL_CALL SwXTextDefaults::getPropertyState( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ PropertyState eRet = PropertyState_DIRECT_VALUE;
+ if (!m_pDoc)
+ throw RuntimeException();
+ const SfxItemPropertyMapEntry *pMap = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if (!pMap)
+ throw UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ const SfxPoolItem& rItem = m_pDoc->GetDefault(pMap->nWID);
+ if (IsStaticDefaultItem ( &rItem ) )
+ eRet = PropertyState_DEFAULT_VALUE;
+ return eRet;
+}
+
+Sequence< PropertyState > SAL_CALL SwXTextDefaults::getPropertyStates( const Sequence< OUString >& rPropertyNames )
+{
+ const sal_Int32 nCount = rPropertyNames.getLength();
+ Sequence < PropertyState > aRet ( nCount );
+
+ std::transform(rPropertyNames.begin(), rPropertyNames.end(), aRet.getArray(),
+ [this](const OUString& rName) -> PropertyState { return getPropertyState(rName); });
+
+ return aRet;
+}
+
+void SAL_CALL SwXTextDefaults::setPropertyToDefault( const OUString& rPropertyName )
+{
+ if (!m_pDoc)
+ throw RuntimeException();
+ const SfxItemPropertyMapEntry *pMap = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if (!pMap)
+ throw UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ if ( pMap->nFlags & PropertyAttribute::READONLY)
+ throw RuntimeException( "setPropertyToDefault: property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ SfxItemPool& rSet (m_pDoc->GetAttrPool());
+ rSet.ResetPoolDefaultItem ( pMap->nWID );
+}
+
+Any SAL_CALL SwXTextDefaults::getPropertyDefault( const OUString& rPropertyName )
+{
+ if (!m_pDoc)
+ throw RuntimeException();
+ const SfxItemPropertyMapEntry *pMap = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if (!pMap)
+ throw UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ Any aRet;
+ SfxItemPool& rSet (m_pDoc->GetAttrPool());
+ SfxPoolItem const*const pItem = rSet.GetPoolDefaultItem(pMap->nWID);
+ if (pItem)
+ {
+ pItem->QueryValue( aRet, pMap->nMemberId );
+ }
+ return aRet;
+}
+
+OUString SAL_CALL SwXTextDefaults::getImplementationName( )
+{
+ return "SwXTextDefaults";
+}
+
+sal_Bool SAL_CALL SwXTextDefaults::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwXTextDefaults::getSupportedServiceNames( )
+{
+ return { "com.sun.star.text.Defaults",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/TextCursorHelper.cxx b/sw/source/core/unocore/TextCursorHelper.cxx
new file mode 100644
index 000000000..7c2e10be0
--- /dev/null
+++ b/sw/source/core/unocore/TextCursorHelper.cxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <TextCursorHelper.hxx>
+#include <comphelper/servicehelper.hxx>
+
+using namespace ::com::sun::star;
+
+const uno::Sequence< sal_Int8 > & OTextCursorHelper::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theOTextCursorHelperUnoTunnelId;
+ return theOTextCursorHelperUnoTunnelId.getSeq();
+}
+
+//XUnoTunnel
+sal_Int64 SAL_CALL OTextCursorHelper::getSomething(
+ const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/XMLRangeHelper.cxx b/sw/source/core/unocore/XMLRangeHelper.cxx
new file mode 100644
index 000000000..6464543d5
--- /dev/null
+++ b/sw/source/core/unocore/XMLRangeHelper.cxx
@@ -0,0 +1,387 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include "XMLRangeHelper.hxx"
+#include <rtl/character.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/string_view.hxx>
+#include <algorithm>
+
+namespace
+{
+/** unary function that escapes backslashes and single quotes in a sal_Unicode
+ array (which you can get from an OUString with getStr()) and puts the result
+ into the OUStringBuffer given in the CTOR
+ */
+class lcl_Escape
+{
+public:
+ explicit lcl_Escape( OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {}
+ void operator() ( sal_Unicode aChar )
+ {
+ static const sal_Unicode s_aQuote( '\'' );
+ static const sal_Unicode s_aBackslash( '\\' );
+
+ if( aChar == s_aQuote ||
+ aChar == s_aBackslash )
+ m_aResultBuffer.append( s_aBackslash );
+ m_aResultBuffer.append( aChar );
+ }
+
+private:
+ OUStringBuffer & m_aResultBuffer;
+};
+
+/** unary function that removes backslash escapes in a sal_Unicode array (which
+ you can get from an OUString with getStr()) and puts the result into the
+ OUStringBuffer given in the CTOR
+ */
+class lcl_UnEscape
+{
+public:
+ explicit lcl_UnEscape( OUStringBuffer & aResultBuffer ) : m_aResultBuffer( aResultBuffer ) {}
+ void operator() ( sal_Unicode aChar )
+ {
+ static const sal_Unicode s_aBackslash( '\\' );
+
+ if( aChar != s_aBackslash )
+ m_aResultBuffer.append( aChar );
+ }
+
+private:
+ OUStringBuffer & m_aResultBuffer;
+};
+
+void lcl_getXMLStringForCell( const /*::chart::*/XMLRangeHelper::Cell & rCell, OUStringBuffer * output )
+{
+ assert(output != nullptr);
+
+ if( rCell.empty())
+ return;
+
+ sal_Int32 nCol = rCell.nColumn;
+ output->append( '.' );
+ if( ! rCell.bRelativeColumn )
+ output->append( '$' );
+
+ // get A, B, C, ..., AA, AB, ... representation of column number
+ if( nCol < 26 )
+ output->append( static_cast<sal_Unicode>('A' + nCol) );
+ else if( nCol < 702 )
+ {
+ output->append( static_cast<sal_Unicode>('A' + nCol / 26 - 1 ));
+ output->append( static_cast<sal_Unicode>('A' + nCol % 26) );
+ }
+ else // works for nCol <= 18,278
+ {
+ output->append( static_cast<sal_Unicode>('A' + nCol / 702 - 1 ));
+ output->append( static_cast<sal_Unicode>('A' + (nCol % 702) / 26 ));
+ output->append( static_cast<sal_Unicode>('A' + nCol % 26) );
+ }
+
+ // write row number as number
+ if( ! rCell.bRelativeRow )
+ output->append( '$' );
+ output->append( rCell.nRow + sal_Int32(1) );
+}
+
+void lcl_getSingleCellAddressFromXMLString(
+ std::u16string_view rXMLString,
+ sal_Int32 nStartPos, sal_Int32 nEndPos,
+ /*::chart::*/XMLRangeHelper::Cell & rOutCell )
+{
+ // expect "\$?[a-zA-Z]+\$?[1-9][0-9]*"
+ static const sal_Unicode aDollar( '$' );
+ static const sal_Unicode aLetterA( 'A' );
+
+ OUString aCellStr = OUString(rXMLString.substr( nStartPos, nEndPos - nStartPos + 1 )).toAsciiUpperCase();
+ const sal_Unicode* pStrArray = aCellStr.getStr();
+ sal_Int32 nLength = aCellStr.getLength();
+ sal_Int32 i = nLength - 1, nColumn = 0;
+
+ // parse number for row
+ while( rtl::isAsciiDigit( pStrArray[ i ] ) && i >= 0 )
+ i--;
+ rOutCell.nRow = (o3tl::toInt32(aCellStr.subView( i + 1 ))) - 1;
+ // a dollar in XML means absolute (whereas in UI it means relative)
+ if( pStrArray[ i ] == aDollar )
+ {
+ i--;
+ rOutCell.bRelativeRow = false;
+ }
+ else
+ rOutCell.bRelativeRow = true;
+
+ // parse rest for column
+ sal_Int32 nPower = 1;
+ while( rtl::isAsciiAlpha( pStrArray[ i ] ))
+ {
+ nColumn += (pStrArray[ i ] - aLetterA + 1) * nPower;
+ i--;
+ nPower *= 26;
+ }
+ rOutCell.nColumn = nColumn - 1;
+
+ rOutCell.bRelativeColumn = true;
+ if( i >= 0 &&
+ pStrArray[ i ] == aDollar )
+ rOutCell.bRelativeColumn = false;
+ rOutCell.bIsEmpty = false;
+}
+
+bool lcl_getCellAddressFromXMLString(
+ const OUString& rXMLString,
+ sal_Int32 nStartPos, sal_Int32 nEndPos,
+ /*::chart::*/XMLRangeHelper::Cell & rOutCell,
+ OUString& rOutTableName )
+{
+ static const sal_Unicode aDot( '.' );
+ static const sal_Unicode aQuote( '\'' );
+ static const sal_Unicode aBackslash( '\\' );
+
+ sal_Int32 nNextDelimiterPos = nStartPos;
+
+ sal_Int32 nDelimiterPos = nStartPos;
+ bool bInQuotation = false;
+ // parse table name
+ while( nDelimiterPos < nEndPos &&
+ ( bInQuotation || rXMLString[ nDelimiterPos ] != aDot ))
+ {
+ // skip escaped characters (with backslash)
+ if( rXMLString[ nDelimiterPos ] == aBackslash )
+ ++nDelimiterPos;
+ // toggle quotation mode when finding single quotes
+ else if( rXMLString[ nDelimiterPos ] == aQuote )
+ bInQuotation = ! bInQuotation;
+
+ ++nDelimiterPos;
+ }
+
+ if( nDelimiterPos == -1 ||
+ nDelimiterPos >= nEndPos )
+ {
+ return false;
+ }
+ if( nDelimiterPos > nStartPos )
+ {
+ // there is a table name before the address
+
+ OUStringBuffer aTableNameBuffer;
+ const sal_Unicode * pTableName = rXMLString.getStr();
+
+ // remove escapes from table name
+ std::for_each( pTableName + nStartPos,
+ pTableName + nDelimiterPos,
+ lcl_UnEscape( aTableNameBuffer ));
+
+ // unquote quoted table name
+ const sal_Unicode * pBuf = aTableNameBuffer.getStr();
+ if( pBuf[ 0 ] == aQuote &&
+ pBuf[ aTableNameBuffer.getLength() - 1 ] == aQuote )
+ {
+ OUString aName = aTableNameBuffer.makeStringAndClear();
+ rOutTableName = aName.copy( 1, aName.getLength() - 2 );
+ }
+ else
+ rOutTableName = aTableNameBuffer.makeStringAndClear();
+ }
+
+ for( sal_Int32 i = 0;
+ nNextDelimiterPos < nEndPos;
+ nDelimiterPos = nNextDelimiterPos, i++ )
+ {
+ nNextDelimiterPos = rXMLString.indexOf( aDot, nDelimiterPos + 1 );
+ if( nNextDelimiterPos == -1 ||
+ nNextDelimiterPos > nEndPos )
+ nNextDelimiterPos = nEndPos + 1;
+
+ if( i==0 )
+ // only take first cell
+ lcl_getSingleCellAddressFromXMLString(
+ rXMLString, nDelimiterPos + 1, nNextDelimiterPos - 1, rOutCell );
+ }
+
+ return true;
+}
+
+bool lcl_getCellRangeAddressFromXMLString(
+ const OUString& rXMLString,
+ sal_Int32 nStartPos, sal_Int32 nEndPos,
+ /*::chart::*/XMLRangeHelper::CellRange & rOutRange )
+{
+ bool bResult = true;
+ static const sal_Unicode aColon( ':' );
+ static const sal_Unicode aQuote( '\'' );
+ static const sal_Unicode aBackslash( '\\' );
+
+ sal_Int32 nDelimiterPos = nStartPos;
+ bool bInQuotation = false;
+ // parse table name
+ while( nDelimiterPos < nEndPos &&
+ ( bInQuotation || rXMLString[ nDelimiterPos ] != aColon ))
+ {
+ // skip escaped characters (with backslash)
+ if( rXMLString[ nDelimiterPos ] == aBackslash )
+ ++nDelimiterPos;
+ // toggle quotation mode when finding single quotes
+ else if( rXMLString[ nDelimiterPos ] == aQuote )
+ bInQuotation = ! bInQuotation;
+
+ ++nDelimiterPos;
+ }
+
+ if( nDelimiterPos == nEndPos )
+ {
+ // only one cell
+ bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nEndPos,
+ rOutRange.aUpperLeft,
+ rOutRange.aTableName );
+ }
+ else
+ {
+ // range (separated by a colon)
+ bResult = lcl_getCellAddressFromXMLString( rXMLString, nStartPos, nDelimiterPos - 1,
+ rOutRange.aUpperLeft,
+ rOutRange.aTableName );
+ OUString sTableSecondName;
+ if( bResult )
+ {
+ bResult = lcl_getCellAddressFromXMLString( rXMLString, nDelimiterPos + 1, nEndPos,
+ rOutRange.aLowerRight,
+ sTableSecondName );
+ }
+ if( bResult &&
+ !sTableSecondName.isEmpty() &&
+ sTableSecondName != rOutRange.aTableName )
+ bResult = false;
+ }
+
+ return bResult;
+}
+
+} // anonymous namespace
+
+namespace XMLRangeHelper
+{
+
+CellRange getCellRangeFromXMLString( const OUString & rXMLString )
+{
+ static const sal_Unicode aSpace( ' ' );
+ static const sal_Unicode aQuote( '\'' );
+ static const sal_Unicode aDollar( '$' );
+ static const sal_Unicode aBackslash( '\\' );
+
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = nStartPos;
+ const sal_Int32 nLength = rXMLString.getLength();
+
+ // reset
+ CellRange aResult;
+
+ // iterate over different ranges
+ for( sal_Int32 i = 0;
+ nEndPos < nLength;
+ nStartPos = ++nEndPos, i++ )
+ {
+ // find start point of next range
+
+ // ignore leading '$'
+ if( rXMLString[ nEndPos ] == aDollar)
+ nEndPos++;
+
+ bool bInQuotation = false;
+ // parse range
+ while( nEndPos < nLength &&
+ ( bInQuotation || rXMLString[ nEndPos ] != aSpace ))
+ {
+ // skip escaped characters (with backslash)
+ if( rXMLString[ nEndPos ] == aBackslash )
+ ++nEndPos;
+ // toggle quotation mode when finding single quotes
+ else if( rXMLString[ nEndPos ] == aQuote )
+ bInQuotation = ! bInQuotation;
+
+ ++nEndPos;
+ }
+
+ if( ! lcl_getCellRangeAddressFromXMLString(
+ rXMLString,
+ nStartPos, nEndPos - 1,
+ aResult ))
+ {
+ // if an error occurred, bail out
+ return CellRange();
+ }
+ }
+
+ return aResult;
+}
+
+OUString getXMLStringFromCellRange( const CellRange & rRange )
+{
+ static const sal_Unicode aSpace( ' ' );
+ static const sal_Unicode aQuote( '\'' );
+
+ OUStringBuffer aBuffer;
+
+ if( !rRange.aTableName.isEmpty())
+ {
+ bool bNeedsEscaping = ( rRange.aTableName.indexOf( aQuote ) > -1 );
+ bool bNeedsQuoting = bNeedsEscaping || ( rRange.aTableName.indexOf( aSpace ) > -1 );
+
+ // quote table name if it contains spaces or quotes
+ if( bNeedsQuoting )
+ {
+ // leading quote
+ aBuffer.append( aQuote );
+
+ // escape existing quotes
+ if( bNeedsEscaping )
+ {
+ const sal_Unicode * pTableNameBeg = rRange.aTableName.getStr();
+
+ // append the quoted string at the buffer
+ std::for_each( pTableNameBeg,
+ pTableNameBeg + rRange.aTableName.getLength(),
+ lcl_Escape( aBuffer ) );
+ }
+ else
+ aBuffer.append( rRange.aTableName );
+
+ // final quote
+ aBuffer.append( aQuote );
+ }
+ else
+ aBuffer.append( rRange.aTableName );
+ }
+ lcl_getXMLStringForCell( rRange.aUpperLeft, &aBuffer );
+
+ if( ! rRange.aLowerRight.empty())
+ {
+ // we have a range (not a single cell)
+ aBuffer.append( ':' );
+ lcl_getXMLStringForCell( rRange.aLowerRight, &aBuffer );
+ }
+
+ return aBuffer.makeStringAndClear();
+}
+
+} // namespace XMLRangeHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/XMLRangeHelper.hxx b/sw/source/core/unocore/XMLRangeHelper.hxx
new file mode 100644
index 000000000..51966860f
--- /dev/null
+++ b/sw/source/core/unocore/XMLRangeHelper.hxx
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+//!!
+//!! This file is an exact copy of the same file in chart2 project
+//!!
+
+#ifndef INCLUDED_SW_SOURCE_CORE_UNOCORE_XMLRANGEHELPER_HXX
+#define INCLUDED_SW_SOURCE_CORE_UNOCORE_XMLRANGEHELPER_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+namespace XMLRangeHelper
+{
+
+struct Cell
+{
+ sal_Int32 nColumn;
+ sal_Int32 nRow;
+ bool bRelativeColumn;
+ bool bRelativeRow;
+ bool bIsEmpty;
+
+ Cell() :
+ nColumn(0),
+ nRow(0),
+ bRelativeColumn(false),
+ bRelativeRow(false),
+ bIsEmpty(true)
+ {}
+
+ bool empty() const { return bIsEmpty; }
+};
+
+struct CellRange
+{
+ Cell aUpperLeft;
+ Cell aLowerRight;
+ OUString aTableName;
+};
+
+CellRange getCellRangeFromXMLString( const OUString & rXMLString );
+
+OUString getXMLStringFromCellRange( const CellRange & rRange );
+
+} // namespace XMLRangeHelper
+
+// INCLUDED_SW_SOURCE_CORE_UNOCORE_XMLRANGEHELPER_HXX
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/swunohelper.cxx b/sw/source/core/unocore/swunohelper.cxx
new file mode 100644
index 000000000..b574693e2
--- /dev/null
+++ b/sw/source/core/unocore/swunohelper.cxx
@@ -0,0 +1,337 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/ucb/XContentIdentifier.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/extract.hxx>
+#include <o3tl/any.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/datetime.hxx>
+#include <tools/diagnose_ex.h>
+#include <rtl/ustring.hxx>
+#include <osl/diagnose.h>
+#include <ucbhelper/contentidentifier.hxx>
+#include <ucbhelper/content.hxx>
+#include <swunohelper.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <editeng/memberids.h>
+#include <svl/itemset.hxx>
+
+using namespace com::sun::star;
+
+namespace SWUnoHelper
+{
+
+sal_Int32 GetEnumAsInt32( const css::uno::Any& rVal )
+{
+ sal_Int32 nReturn = 0;
+ if (! ::cppu::enum2int(nReturn,rVal) )
+ OSL_FAIL( "can't get EnumAsInt32" );
+ return nReturn;
+}
+
+// methods for UCB actions
+bool UCB_DeleteFile( const OUString& rURL )
+{
+ bool bRemoved;
+ try
+ {
+ ucbhelper::Content aTempContent( rURL,
+ css::uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ aTempContent.executeCommand("delete", css::uno::Any( true ) );
+ bRemoved = true;
+ }
+ catch( css::uno::Exception& )
+ {
+ bRemoved = false;
+ TOOLS_WARN_EXCEPTION( "sw", "Exception from executeCommand( delete )" );
+ }
+ return bRemoved;
+}
+
+bool UCB_MoveFile( const OUString& rURL, std::u16string_view rNewURL )
+{
+ bool bCopyCompleted = true;
+ try
+ {
+ INetURLObject aURL( rNewURL );
+ const OUString sName(aURL.GetLastName());
+ aURL.removeSegment();
+ const OUString sMainURL( aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE) );
+
+ ucbhelper::Content aTempContent( sMainURL,
+ css::uno::Reference< css::ucb::XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+
+ css::ucb::TransferInfo aInfo;
+ aInfo.NameClash = css::ucb::NameClash::ERROR;
+ aInfo.NewTitle = sName;
+ aInfo.SourceURL = rURL;
+ aInfo.MoveData = true;
+ aTempContent.executeCommand( "transfer", uno::Any(aInfo) );
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "Exception from executeCommand( transfer )" );
+ bCopyCompleted = false;
+ }
+ return bCopyCompleted;
+}
+
+bool UCB_IsCaseSensitiveFileName( std::u16string_view rURL )
+{
+ bool bCaseSensitive;
+ try
+ {
+ INetURLObject aTempObj( rURL );
+ aTempObj.SetBase( aTempObj.GetBase().toAsciiLowerCase() );
+ css::uno::Reference< css::ucb::XContentIdentifier > xRef1 = new
+ ucbhelper::ContentIdentifier( aTempObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ));
+
+ aTempObj.SetBase(aTempObj.GetBase().toAsciiUpperCase());
+ css::uno::Reference< css::ucb::XContentIdentifier > xRef2 = new
+ ucbhelper::ContentIdentifier( aTempObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ));
+
+ css::uno::Reference< css::ucb::XUniversalContentBroker > xUcb =
+ css::ucb::UniversalContentBroker::create(comphelper::getProcessComponentContext());
+
+ sal_Int32 nCompare = xUcb->compareContentIds( xRef1, xRef2 );
+ bCaseSensitive = 0 != nCompare;
+ }
+ catch( css::uno::Exception& )
+ {
+ bCaseSensitive = false;
+ TOOLS_WARN_EXCEPTION( "sw", "compareContentIds()" );
+ }
+ return bCaseSensitive;
+}
+
+bool UCB_IsReadOnlyFileName( const OUString& rURL )
+{
+ bool bIsReadOnly = false;
+ try
+ {
+ ucbhelper::Content aCnt( rURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ css::uno::Any aAny = aCnt.getPropertyValue("IsReadOnly");
+ if(aAny.hasValue())
+ bIsReadOnly = *o3tl::doAccess<bool>(aAny);
+ }
+ catch( css::uno::Exception& )
+ {
+ bIsReadOnly = false;
+ }
+ return bIsReadOnly;
+}
+
+bool UCB_IsFile( const OUString& rURL )
+{
+ bool bExists = false;
+ try
+ {
+ ::ucbhelper::Content aContent( rURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ bExists = aContent.isDocument();
+ }
+ catch (css::uno::Exception &)
+ {
+ }
+ return bExists;
+}
+
+bool UCB_IsDirectory( const OUString& rURL )
+{
+ bool bExists = false;
+ try
+ {
+ ::ucbhelper::Content aContent( rURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ bExists = aContent.isFolder();
+ }
+ catch (css::uno::Exception &)
+ {
+ }
+ return bExists;
+}
+
+ // get a list of files from the folder of the URL
+ // options: pExtension = 0 -> all, else this specific extension
+ // pDateTime != 0 -> returns also the modified date/time of
+ // the files in a std::vector<OUString> -->
+ // !! objects must be deleted from the caller!!
+bool UCB_GetFileListOfFolder( const OUString& rURL,
+ std::vector<OUString>& rList,
+ const OUString* pExtension,
+ std::vector< ::DateTime >* pDateTimeList )
+{
+ bool bOk = false;
+ try
+ {
+ ucbhelper::Content aCnt( rURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ css::uno::Reference< css::sdbc::XResultSet > xResultSet;
+
+ const sal_Int32 nSeqSize = pDateTimeList ? 2 : 1;
+ css::uno::Sequence < OUString > aProps( nSeqSize );
+ OUString* pProps = aProps.getArray();
+ pProps[ 0 ] = "Title";
+ if( pDateTimeList )
+ pProps[ 1 ] = "DateModified";
+
+ try
+ {
+ xResultSet = aCnt.createCursor( aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY );
+ }
+ catch( css::uno::Exception& )
+ {
+ OSL_FAIL( "create cursor failed!" );
+ }
+
+ if( xResultSet.is() )
+ {
+ css::uno::Reference< css::sdbc::XRow > xRow( xResultSet, css::uno::UNO_QUERY );
+ const sal_Int32 nExtLen = pExtension ? pExtension->getLength() : 0;
+ try
+ {
+ if( xResultSet->first() )
+ {
+ do {
+ const OUString sTitle( xRow->getString( 1 ) );
+ if( !nExtLen ||
+ ( sTitle.getLength() > nExtLen &&
+ sTitle.endsWith( *pExtension )) )
+ {
+ rList.push_back( sTitle );
+
+ if( pDateTimeList )
+ {
+ css::util::DateTime aStamp = xRow->getTimestamp(2);
+ ::DateTime aDateTime(
+ ::Date( aStamp.Day,
+ aStamp.Month,
+ aStamp.Year ),
+ ::tools::Time( aStamp.Hours,
+ aStamp.Minutes,
+ aStamp.Seconds,
+ aStamp.NanoSeconds ));
+ pDateTimeList->push_back( aDateTime );
+ }
+ }
+
+ } while( xResultSet->next() );
+ }
+ bOk = true;
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "" );
+ }
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "sw", "" );
+ bOk = false;
+ }
+ return bOk;
+}
+
+bool needToMapFillItemsToSvxBrushItemTypes(const SfxItemSet& rSet,
+ sal_uInt16 const nMID)
+{
+ const XFillStyleItem* pXFillStyleItem(rSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, false));
+
+ if(!pXFillStyleItem)
+ {
+ return false;
+ }
+
+ // here different FillStyles can be excluded for export; it will depend on the
+ // quality these fallbacks can reach. That again is done in getSvxBrushItemFromSourceSet,
+ // take a look there how the superset of DrawObject FillStyles is mapped to SvxBrushItem.
+ const drawing::FillStyle eFill = pXFillStyleItem->GetValue();
+ switch (eFill)
+ {
+ case drawing::FillStyle_NONE:
+ // claim that BackColor and BackTransparent are available so that
+ // fo:background="transparent" attribute is exported to override
+ // the parent style in case it is != NONE
+ switch (nMID)
+ {
+ case MID_BACK_COLOR:
+ case MID_BACK_COLOR_R_G_B:
+ case MID_GRAPHIC_TRANSPARENT: // this is *BackTransparent
+ return true;
+ default:
+ return false;
+ }
+ break;
+ case drawing::FillStyle_SOLID:
+ case drawing::FillStyle_GRADIENT: // gradient and hatch don't exist in
+ case drawing::FillStyle_HATCH: // SvxBrushItem so average color is emulated
+ switch (nMID)
+ {
+ case MID_BACK_COLOR:
+ case MID_GRAPHIC_TRANSPARENT: // this is *BackTransparent
+ // Gradient/Hatch always have emulated color
+ return (drawing::FillStyle_SOLID != eFill)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLCOLOR)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLTRANSPARENCE)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE);
+ case MID_BACK_COLOR_R_G_B:
+ // Gradient/Hatch always have emulated color
+ return (drawing::FillStyle_SOLID != eFill)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLCOLOR);
+ case MID_BACK_COLOR_TRANSPARENCY:
+ return SfxItemState::SET == rSet.GetItemState(XATTR_FILLTRANSPARENCE)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE);
+ }
+ break;
+ case drawing::FillStyle_BITMAP:
+ switch (nMID)
+ {
+ case MID_GRAPHIC:
+ return SfxItemState::SET == rSet.GetItemState(XATTR_FILLBITMAP);
+ case MID_GRAPHIC_POSITION:
+ return SfxItemState::SET == rSet.GetItemState(XATTR_FILLBMP_STRETCH)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLBMP_TILE)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLBMP_POS);
+ case MID_GRAPHIC_TRANSPARENT:
+ case MID_GRAPHIC_TRANSPARENCY:
+ return SfxItemState::SET == rSet.GetItemState(XATTR_FILLTRANSPARENCE)
+ || SfxItemState::SET == rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE);
+ }
+ break;
+ default:
+ assert(false);
+ }
+
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unobkm.cxx b/sw/source/core/unocore/unobkm.cxx
new file mode 100644
index 000000000..567cf8a20
--- /dev/null
+++ b/sw/source/core/unocore/unobkm.cxx
@@ -0,0 +1,878 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unobookmark.hxx>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/listener.hxx>
+#include <vcl/svapp.hxx>
+#include <xmloff/odffields.hxx>
+
+#include <TextCursorHelper.hxx>
+#include <unotextrange.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <IMark.hxx>
+#include <crossrefbookmark.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+
+using namespace ::sw::mark;
+using namespace ::com::sun::star;
+
+class SwXBookmark::Impl
+ : public SvtListener
+{
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper3
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ SwDoc* m_pDoc;
+ ::sw::mark::IMark* m_pRegisteredBookmark;
+ OUString m_sMarkName;
+ bool m_bHidden;
+ OUString m_HideCondition;
+
+ Impl( SwDoc *const pDoc )
+ : m_pDoc(pDoc)
+ , m_pRegisteredBookmark(nullptr)
+ , m_bHidden(false)
+ {
+ // DO NOT registerInMark here! (because SetXBookmark would delete rThis)
+ }
+
+ void registerInMark(SwXBookmark & rThis, ::sw::mark::IMark *const pBkmk);
+protected:
+ virtual void Notify(const SfxHint&) override;
+
+};
+
+void SwXBookmark::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pRegisteredBookmark = nullptr;
+ m_pDoc = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ }
+}
+
+void SwXBookmark::Impl::registerInMark(SwXBookmark& rThis,
+ ::sw::mark::IMark* const pBkmk)
+{
+ const uno::Reference<text::XTextContent> xBookmark(&rThis);
+ if (pBkmk)
+ {
+ EndListeningAll();
+ StartListening(pBkmk->GetNotifier());
+ ::sw::mark::MarkBase *const pMarkBase(dynamic_cast< ::sw::mark::MarkBase * >(pBkmk));
+ OSL_ENSURE(pMarkBase, "registerInMark: no MarkBase?");
+ if (pMarkBase)
+ {
+ pMarkBase->SetXBookmark(xBookmark);
+ }
+ assert(m_pDoc == nullptr || m_pDoc == &pBkmk->GetMarkPos().GetDoc());
+ m_pDoc = &pBkmk->GetMarkPos().GetDoc();
+ }
+ else if (m_pRegisteredBookmark)
+ {
+ m_sMarkName = m_pRegisteredBookmark->GetName();
+
+ // the following applies only to bookmarks (not to fieldmarks)
+ IBookmark* pBookmark = dynamic_cast<IBookmark*>(m_pRegisteredBookmark);
+ if (pBookmark)
+ {
+ m_bHidden = pBookmark->IsHidden();
+ m_HideCondition = pBookmark->GetHideCondition();
+ }
+ EndListeningAll();
+ }
+ m_pRegisteredBookmark = pBkmk;
+ // need a permanent Reference to initialize m_wThis
+ m_wThis = xBookmark;
+}
+
+void SwXBookmark::registerInMark(SwXBookmark & rThis,
+ ::sw::mark::IMark *const pBkmk)
+{
+ m_pImpl->registerInMark( rThis, pBkmk );
+}
+
+::sw::mark::IMark* SwXBookmark::GetBookmark() const
+{
+ return m_pImpl->m_pRegisteredBookmark;
+}
+
+IDocumentMarkAccess* SwXBookmark::GetIDocumentMarkAccess()
+{
+ return m_pImpl->m_pDoc->getIDocumentMarkAccess();
+}
+
+SwDoc * SwXBookmark::GetDoc()
+{
+ return m_pImpl->m_pDoc;
+}
+
+SwXBookmark::SwXBookmark(SwDoc *const pDoc)
+ : m_pImpl( new SwXBookmark::Impl(pDoc) )
+{
+}
+
+SwXBookmark::SwXBookmark()
+ : m_pImpl( new SwXBookmark::Impl(nullptr) )
+{
+}
+
+SwXBookmark::~SwXBookmark()
+{
+}
+
+uno::Reference<text::XTextContent> SwXBookmark::CreateXBookmark(
+ SwDoc & rDoc,
+ ::sw::mark::IMark *const pBookmark)
+{
+ // #i105557#: do not iterate over the registered clients: race condition
+ ::sw::mark::MarkBase *const pMarkBase(dynamic_cast< ::sw::mark::MarkBase * >(pBookmark));
+ OSL_ENSURE(!pBookmark || pMarkBase, "CreateXBookmark: no MarkBase?");
+ uno::Reference<text::XTextContent> xBookmark;
+ if (pMarkBase)
+ {
+ xBookmark = pMarkBase->GetXBookmark();
+ }
+ if (!xBookmark.is())
+ {
+ OSL_ENSURE(!pBookmark ||
+ dynamic_cast< ::sw::mark::IBookmark* >(pBookmark) ||
+ IDocumentMarkAccess::GetType(*pBookmark) == IDocumentMarkAccess::MarkType::ANNOTATIONMARK,
+ "<SwXBookmark::GetObject(..)>"
+ "SwXBookmark requested for non-bookmark mark and non-annotation mark.");
+ SwXBookmark *const pXBookmark =
+ pBookmark ? new SwXBookmark(&rDoc) : new SwXBookmark;
+ xBookmark.set(pXBookmark);
+ pXBookmark->m_pImpl->registerInMark(*pXBookmark, pMarkBase);
+ }
+ return xBookmark;
+}
+
+::sw::mark::IMark const* SwXBookmark::GetBookmarkInDoc(SwDoc const*const pDoc,
+ const uno::Reference< lang::XUnoTunnel> & xUT)
+{
+ SwXBookmark *const pXBkm(
+ comphelper::getFromUnoTunnel<SwXBookmark>(xUT));
+ if (pXBkm && (pDoc == pXBkm->m_pImpl->m_pDoc))
+ {
+ return pXBkm->m_pImpl->m_pRegisteredBookmark;
+ }
+ return nullptr;
+}
+
+const uno::Sequence< sal_Int8 > & SwXBookmark::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXBookmarkUnoTunnelId;
+ return theSwXBookmarkUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXBookmark::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl<SwXBookmark>(rId, this);
+}
+
+void SwXBookmark::attachToRangeEx(
+ const uno::Reference< text::XTextRange > & xTextRange,
+ IDocumentMarkAccess::MarkType eType,
+ bool const isFieldmarkSeparatorAtStart)
+{
+ if (m_pImpl->m_pRegisteredBookmark)
+ {
+ throw uno::RuntimeException();
+ }
+
+ const uno::Reference<lang::XUnoTunnel> xRangeTunnel(
+ xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ SwDoc *const pDoc =
+ pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
+ if (!pDoc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ m_pImpl->m_pDoc = pDoc;
+ SwUnoInternalPaM aPam(*m_pImpl->m_pDoc);
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+ UnoActionContext aCont(m_pImpl->m_pDoc);
+ if (m_pImpl->m_sMarkName.isEmpty())
+ {
+ m_pImpl->m_sMarkName = "Bookmark";
+ }
+ if ((eType == IDocumentMarkAccess::MarkType::BOOKMARK) &&
+ ::sw::mark::CrossRefNumItemBookmark::IsLegalName(m_pImpl->m_sMarkName))
+ {
+ eType = IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK;
+ }
+ else if ((eType == IDocumentMarkAccess::MarkType::BOOKMARK) &&
+ ::sw::mark::CrossRefHeadingBookmark::IsLegalName(m_pImpl->m_sMarkName) &&
+ IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( aPam ) )
+ {
+ eType = IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK;
+ }
+ m_pImpl->registerInMark(*this,
+ m_pImpl->m_pDoc->getIDocumentMarkAccess()->makeMark(
+ aPam, m_pImpl->m_sMarkName, eType, ::sw::mark::InsertMode::New,
+ // note: aPam will be moved fwd by inserting start char, so sep
+ // will be directly following start
+ isFieldmarkSeparatorAtStart ? aPam.Start() : nullptr));
+ // #i81002#
+ // Check, if bookmark has been created.
+ // E.g., the creation of a cross-reference bookmark is suppress,
+ // if the PaM isn't a valid one for cross-reference bookmarks.
+ if (!m_pImpl->m_pRegisteredBookmark)
+ {
+ OSL_FAIL("<SwXBookmark::attachToRange(..)>"
+ " - could not create Mark.");
+ throw lang::IllegalArgumentException();
+ }
+}
+
+void SwXBookmark::attachToRange( const uno::Reference< text::XTextRange > & xTextRange )
+{
+ attachToRangeEx(xTextRange, IDocumentMarkAccess::MarkType::BOOKMARK);
+}
+
+void SAL_CALL SwXBookmark::attach( const uno::Reference< text::XTextRange > & xTextRange )
+{
+ SolarMutexGuard aGuard;
+ attachToRange( xTextRange );
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SwXBookmark::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_pRegisteredBookmark)
+ {
+ throw uno::RuntimeException();
+ }
+ return SwXTextRange::CreateXTextRange(
+ *m_pImpl->m_pDoc,
+ m_pImpl->m_pRegisteredBookmark->GetMarkPos(),
+ (m_pImpl->m_pRegisteredBookmark->IsExpanded())
+ ? &m_pImpl->m_pRegisteredBookmark->GetOtherMarkPos() : nullptr);
+}
+
+void SAL_CALL SwXBookmark::dispose()
+{
+ SolarMutexGuard aGuard;
+ if (m_pImpl->m_pRegisteredBookmark)
+ {
+ m_pImpl->m_pDoc->getIDocumentMarkAccess()->deleteMark( m_pImpl->m_pRegisteredBookmark );
+ }
+}
+
+void SAL_CALL SwXBookmark::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXBookmark::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+OUString SAL_CALL SwXBookmark::getName()
+{
+ SolarMutexGuard aGuard;
+
+ return (m_pImpl->m_pRegisteredBookmark)
+ ? m_pImpl->m_pRegisteredBookmark->GetName()
+ : m_pImpl->m_sMarkName;
+}
+
+void SAL_CALL SwXBookmark::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_pRegisteredBookmark)
+ {
+ m_pImpl->m_sMarkName = rName;
+ }
+ if (!m_pImpl->m_pRegisteredBookmark || (getName() == rName))
+ {
+ return;
+ }
+ IDocumentMarkAccess *const pMarkAccess =
+ m_pImpl->m_pDoc->getIDocumentMarkAccess();
+ if(pMarkAccess->findMark(rName) != pMarkAccess->getAllMarksEnd())
+ {
+ throw uno::RuntimeException("setName(): name already in use",
+ static_cast<::cppu::OWeakObject*>(this));
+ }
+
+ SwPaM aPam(m_pImpl->m_pRegisteredBookmark->GetMarkPos());
+ if (m_pImpl->m_pRegisteredBookmark->IsExpanded())
+ {
+ aPam.SetMark();
+ *aPam.GetMark() = m_pImpl->m_pRegisteredBookmark->GetOtherMarkPos();
+ }
+
+ pMarkAccess->renameMark(m_pImpl->m_pRegisteredBookmark, rName);
+}
+
+OUString SAL_CALL
+SwXBookmark::getImplementationName()
+{
+ return "SwXBookmark";
+}
+
+sal_Bool SAL_CALL SwXBookmark::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXBookmark::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.Bookmark",
+ "com.sun.star.document.LinkTarget"
+ };
+}
+
+// MetadatableMixin
+::sfx2::Metadatable* SwXBookmark::GetCoreObject()
+{
+ return dynamic_cast< ::sfx2::Metadatable* >(m_pImpl->m_pRegisteredBookmark);
+}
+
+uno::Reference<frame::XModel> SwXBookmark::GetModel()
+{
+ if (m_pImpl->m_pDoc)
+ {
+ SwDocShell const * const pShell( m_pImpl->m_pDoc->GetDocShell() );
+ return pShell ? pShell->GetModel() : nullptr;
+ }
+ return nullptr;
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXBookmark::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef(
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_BOOKMARK)
+ ->getPropertySetInfo() );
+ return xRef;
+}
+
+void SAL_CALL
+SwXBookmark::setPropertyValue(const OUString& PropertyName,
+ const uno::Any& rValue)
+{
+ SolarMutexGuard g;
+
+ if (PropertyName == UNO_NAME_BOOKMARK_HIDDEN)
+ {
+ bool bNewValue = false;
+ if (!(rValue >>= bNewValue))
+ throw lang::IllegalArgumentException("Property BookmarkHidden requires value of type boolean", nullptr, 0);
+
+ IBookmark* pBookmark = dynamic_cast<IBookmark*>(m_pImpl->m_pRegisteredBookmark);
+ if (pBookmark)
+ {
+ pBookmark->Hide(bNewValue);
+ }
+ else
+ {
+ m_pImpl->m_bHidden = bNewValue;
+ }
+ return;
+ }
+ else if (PropertyName == UNO_NAME_BOOKMARK_CONDITION)
+ {
+ OUString newValue;
+ if (!(rValue >>= newValue))
+ throw lang::IllegalArgumentException("Property BookmarkCondition requires value of type string", nullptr, 0);
+
+ IBookmark* pBookmark = dynamic_cast<IBookmark*>(m_pImpl->m_pRegisteredBookmark);
+ if (pBookmark)
+ {
+ pBookmark->SetHideCondition(newValue);
+ }
+ else
+ {
+ m_pImpl->m_HideCondition = newValue;
+ }
+ return;
+ }
+
+ // nothing to set here
+ throw lang::IllegalArgumentException("Property is read-only: "
+ + PropertyName, static_cast< cppu::OWeakObject * >(this), 0 );
+}
+
+uno::Any SAL_CALL SwXBookmark::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard g;
+
+ uno::Any aRet;
+ if (! ::sw::GetDefaultTextContentValue(aRet, rPropertyName))
+ {
+ if(rPropertyName == UNO_LINK_DISPLAY_NAME)
+ {
+ aRet <<= getName();
+ }
+ else if (rPropertyName == UNO_NAME_BOOKMARK_HIDDEN)
+ {
+ IBookmark* pBookmark = dynamic_cast<IBookmark*>(m_pImpl->m_pRegisteredBookmark);
+ if (pBookmark)
+ {
+ aRet <<= pBookmark->IsHidden();
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_bHidden;
+ }
+ }
+ else if (rPropertyName == UNO_NAME_BOOKMARK_CONDITION)
+ {
+ IBookmark* pBookmark = dynamic_cast<IBookmark*>(m_pImpl->m_pRegisteredBookmark);
+ if (pBookmark)
+ {
+ aRet <<= pBookmark->GetHideCondition();
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_HideCondition;
+ }
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL
+SwXBookmark::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXBookmark::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXBookmark::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXBookmark::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXBookmark::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXBookmark::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXBookmark::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXBookmark::removeVetoableChangeListener(): not implemented");
+}
+
+
+void SwXFieldmarkParameters::insertByName(const OUString& aName, const uno::Any& aElement)
+{
+ SolarMutexGuard aGuard;
+ IFieldmark::parameter_map_t* pParameters = getCoreParameters();
+ if(pParameters->find(aName) != pParameters->end())
+ throw container::ElementExistException();
+ (*pParameters)[aName] = aElement;
+}
+
+void SwXFieldmarkParameters::removeByName(const OUString& aName)
+{
+ SolarMutexGuard aGuard;
+ if(!getCoreParameters()->erase(aName))
+ throw container::NoSuchElementException();
+}
+
+void SwXFieldmarkParameters::replaceByName(const OUString& aName, const uno::Any& aElement)
+{
+ SolarMutexGuard aGuard;
+ IFieldmark::parameter_map_t* pParameters = getCoreParameters();
+ IFieldmark::parameter_map_t::iterator pEntry = pParameters->find(aName);
+ if(pEntry == pParameters->end())
+ throw container::NoSuchElementException();
+ pEntry->second = aElement;
+}
+
+uno::Any SwXFieldmarkParameters::getByName(const OUString& aName)
+{
+ SolarMutexGuard aGuard;
+ IFieldmark::parameter_map_t* pParameters = getCoreParameters();
+ IFieldmark::parameter_map_t::iterator pEntry = pParameters->find(aName);
+ if(pEntry == pParameters->end())
+ throw container::NoSuchElementException();
+ return pEntry->second;
+}
+
+uno::Sequence<OUString> SwXFieldmarkParameters::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ IFieldmark::parameter_map_t* pParameters = getCoreParameters();
+ return comphelper::mapKeysToSequence(*pParameters);
+}
+
+sal_Bool SwXFieldmarkParameters::hasByName(const OUString& aName)
+{
+ SolarMutexGuard aGuard;
+ IFieldmark::parameter_map_t* pParameters = getCoreParameters();
+ return (pParameters->find(aName) != pParameters->end());
+}
+
+uno::Type SwXFieldmarkParameters::getElementType()
+{
+ return ::cppu::UnoType<void>::get();
+}
+
+sal_Bool SwXFieldmarkParameters::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return !getCoreParameters()->empty();
+}
+
+void SwXFieldmarkParameters::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pFieldmark = nullptr;
+}
+
+IFieldmark::parameter_map_t* SwXFieldmarkParameters::getCoreParameters()
+{
+ if(!m_pFieldmark)
+ throw uno::RuntimeException();
+ return m_pFieldmark->GetParameters();
+}
+
+SwXFieldmark::SwXFieldmark(bool const isReplacementObject, SwDoc *const pDoc)
+ : SwXFieldmark_Base(pDoc)
+ , m_bReplacementObject(isReplacementObject)
+{
+}
+
+OUString SAL_CALL
+SwXFieldmark::getImplementationName()
+{
+ return "SwXFieldmark";
+}
+
+uno::Sequence<OUString> SAL_CALL
+SwXFieldmark::getSupportedServiceNames()
+{
+ // is const, no lock needed
+ if (m_bReplacementObject)
+ {
+ return {"com.sun.star.text.TextContent",
+ "com.sun.star.text.Bookmark",
+ "com.sun.star.text.FormFieldmark"};
+ }
+ else
+ {
+ return {"com.sun.star.text.TextContent",
+ "com.sun.star.text.Bookmark",
+ "com.sun.star.text.Fieldmark"};
+ }
+}
+
+void SwXFieldmark::attachToRange( const uno::Reference < text::XTextRange >& xTextRange )
+{
+
+ attachToRangeEx( xTextRange,
+ (m_bReplacementObject ? IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK : IDocumentMarkAccess::MarkType::TEXT_FIELDMARK),
+ m_isFieldmarkSeparatorAtStart);
+}
+
+OUString SwXFieldmark::getFieldType()
+{
+ SolarMutexGuard aGuard;
+ const IFieldmark *pBkm = dynamic_cast<const IFieldmark*>(GetBookmark());
+ if(!pBkm)
+ throw uno::RuntimeException();
+ return pBkm->GetFieldname();
+}
+
+void SwXFieldmark::setFieldType(const OUString & fieldType)
+{
+ SolarMutexGuard aGuard;
+ IFieldmark *pBkm = dynamic_cast<IFieldmark*>(GetBookmark());
+ if(!pBkm)
+ throw uno::RuntimeException();
+
+ OUString const oldFieldType(getFieldType());
+ if (fieldType == oldFieldType)
+ return;
+
+ // note: this must not change between point-fieldmarks and range-fieldmarks
+ if(fieldType == ODF_FORMDROPDOWN || fieldType == ODF_FORMCHECKBOX || fieldType == ODF_FORMDATE)
+ {
+ ::sw::mark::IFieldmark* pNewFieldmark = GetIDocumentMarkAccess()->changeFormFieldmarkType(pBkm, fieldType);
+ if (pNewFieldmark)
+ {
+ registerInMark(*this, pNewFieldmark);
+ return;
+ }
+ }
+
+ if ((!m_bReplacementObject && (fieldType == ODF_UNHANDLED
+ || fieldType == ODF_FORMDATE
+ || fieldType == ODF_FORMTEXT))
+ || (m_bReplacementObject && (fieldType == ODF_FORMCHECKBOX
+ || fieldType == ODF_FORMDROPDOWN)))
+ {
+ pBkm->SetFieldname(fieldType);
+ return;
+ }
+
+ throw uno::RuntimeException("changing to that type isn't implemented");
+}
+
+uno::Reference<container::XNameContainer> SwXFieldmark::getParameters()
+{
+ SolarMutexGuard aGuard;
+ IFieldmark *pBkm = dynamic_cast<IFieldmark*>(GetBookmark());
+ if(!pBkm)
+ throw uno::RuntimeException();
+ return uno::Reference<container::XNameContainer>(new SwXFieldmarkParameters(pBkm));
+}
+
+uno::Reference<text::XTextContent>
+SwXFieldmark::CreateXFieldmark(SwDoc & rDoc, ::sw::mark::IMark *const pMark,
+ bool const isReplacementObject)
+{
+ // #i105557#: do not iterate over the registered clients: race condition
+ ::sw::mark::MarkBase *const pMarkBase(
+ dynamic_cast< ::sw::mark::MarkBase * >(pMark));
+ assert(!pMark || pMarkBase);
+ uno::Reference<text::XTextContent> xMark;
+ if (pMarkBase)
+ {
+ xMark = pMarkBase->GetXBookmark();
+ }
+ if (!xMark.is())
+ {
+ // FIXME: These belong in XTextFieldsSupplier
+ rtl::Reference<SwXFieldmark> pXBkmk;
+ if (dynamic_cast< ::sw::mark::TextFieldmark* >(pMark))
+ pXBkmk = new SwXFieldmark(false, &rDoc);
+ else if (dynamic_cast< ::sw::mark::CheckboxFieldmark* >(pMark))
+ pXBkmk = new SwXFieldmark(true, &rDoc);
+ else if (dynamic_cast< ::sw::mark::DropDownFieldmark* >(pMark))
+ pXBkmk = new SwXFieldmark(true, &rDoc);
+ else if (dynamic_cast< ::sw::mark::DateFieldmark* >(pMark))
+ pXBkmk = new SwXFieldmark(false, &rDoc);
+ else
+ pXBkmk = new SwXFieldmark(isReplacementObject, &rDoc);
+
+ xMark.set(static_cast<::cppu::OWeakObject*>(pXBkmk.get()), uno::UNO_QUERY); // work around ambiguous base
+ pXBkmk->registerInMark(*pXBkmk, pMarkBase);
+ }
+ return xMark;
+}
+
+::sw::mark::ICheckboxFieldmark*
+SwXFieldmark::getCheckboxFieldmark()
+{
+ ::sw::mark::ICheckboxFieldmark* pCheckboxFm = nullptr;
+ if ( getFieldType() == ODF_FORMCHECKBOX )
+ {
+ pCheckboxFm = dynamic_cast< ::sw::mark::ICheckboxFieldmark* >( GetBookmark());
+ assert( GetBookmark() == nullptr || pCheckboxFm != nullptr );
+ // unclear to me whether GetBookmark() can be null here
+ }
+ return pCheckboxFm;
+
+}
+
+// support 'hidden' "Checked" property ( note: this property is just for convenience to support
+// docx import filter thus not published via PropertySet info )
+
+void SAL_CALL
+SwXFieldmark::setPropertyValue(const OUString& PropertyName,
+ const uno::Any& rValue)
+{
+ SolarMutexGuard g;
+ if ( PropertyName == "Checked" )
+ {
+ ::sw::mark::ICheckboxFieldmark* pCheckboxFm = getCheckboxFieldmark();
+ bool bChecked( false );
+ if ( !(pCheckboxFm && ( rValue >>= bChecked )) )
+ throw uno::RuntimeException();
+
+ pCheckboxFm->SetChecked( bChecked );
+ }
+ else if (PropertyName == "PrivateSeparatorAtStart")
+ {
+ bool isFieldmarkSeparatorAtStart{};
+ if (rValue >>= isFieldmarkSeparatorAtStart)
+ {
+ m_isFieldmarkSeparatorAtStart = isFieldmarkSeparatorAtStart;
+ }
+ }
+ // this doesn't support any SwXBookmark property
+}
+
+// support 'hidden' "Checked" property ( note: this property is just for convenience to support
+// docx import filter thus not published via PropertySet info )
+
+uno::Any SAL_CALL SwXFieldmark::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard g;
+ if ( rPropertyName == "Checked" )
+ {
+ ::sw::mark::ICheckboxFieldmark* pCheckboxFm = getCheckboxFieldmark();
+ if ( !pCheckboxFm )
+ throw uno::RuntimeException();
+
+ return uno::Any( pCheckboxFm->IsChecked() );
+ }
+ return uno::Any(); // this doesn't support any SwXBookmark property
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL
+SwXFieldmark::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference<beans::XPropertySetInfo> const xRef(
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_FIELDMARK)
+ ->getPropertySetInfo() );
+ return xRef;
+}
+
+// XComponent
+void SAL_CALL SwXFieldmark::dispose()
+{
+ return SwXBookmark::dispose();
+}
+void SAL_CALL SwXFieldmark::addEventListener(
+ uno::Reference<lang::XEventListener> const& xListener)
+{
+ return SwXBookmark::addEventListener(xListener);
+}
+void SAL_CALL SwXFieldmark::removeEventListener(
+ uno::Reference<lang::XEventListener> const& xListener)
+{
+ return SwXBookmark::removeEventListener(xListener);
+}
+
+// XTextContent
+void SAL_CALL SwXFieldmark::attach(
+ uno::Reference<text::XTextRange> const& xTextRange)
+{
+ return SwXBookmark::attach(xTextRange);
+}
+
+uno::Reference<text::XTextRange> SAL_CALL SwXFieldmark::getAnchor()
+{
+ return SwXBookmark::getAnchor();
+}
+
+uno::Reference<text::XTextRange>
+SwXFieldmark::GetCommand(IFieldmark const& rMark)
+{
+ SwPosition const sepPos(sw::mark::FindFieldSep(rMark));
+ SwPosition start(rMark.GetMarkStart());
+ ++start.nContent;
+ return SwXTextRange::CreateXTextRange(*GetDoc(), start, &sepPos);
+}
+
+uno::Reference<text::XTextRange>
+SwXFieldmark::GetResult(IFieldmark const& rMark)
+{
+ SwPosition sepPos(sw::mark::FindFieldSep(rMark));
+ ++sepPos.nContent;
+ SwPosition const& rEnd(rMark.GetMarkEnd());
+ return SwXTextRange::CreateXTextRange(*GetDoc(), sepPos, &rEnd);
+}
+
+// XTextField
+OUString SAL_CALL
+SwXFieldmark::getPresentation(sal_Bool const bShowCommand)
+{
+ SolarMutexGuard g;
+
+ IFieldmark const*const pMark(dynamic_cast<IFieldmark*>(GetBookmark()));
+ if (!pMark)
+ {
+ throw lang::DisposedException();
+ }
+
+ if (bShowCommand)
+ {
+ if (m_bReplacementObject)
+ {
+ return OUString();
+ }
+ else
+ { // also for ODF_FORMDATE, which shouldn't be a fieldmark...
+ uno::Reference<text::XTextRange> const xCommand(GetCommand(*pMark));
+ return xCommand->getString();
+ }
+ }
+ else
+ {
+ OUString const type(getFieldType());
+ if (type == ODF_FORMCHECKBOX || type == ODF_FORMDROPDOWN)
+ {
+ return sw::mark::ExpandFieldmark(const_cast<IFieldmark *>(pMark));
+ }
+ else
+ {
+ assert(!m_bReplacementObject);
+ uno::Reference<text::XTextRange> const xResult(GetResult(*pMark));
+ return xResult->getString();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unochart.cxx b/sw/source/core/unocore/unochart.cxx
new file mode 100644
index 000000000..89352f4af
--- /dev/null
+++ b/sw/source/core/unocore/unochart.cxx
@@ -0,0 +1,2722 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <algorithm>
+#include <string_view>
+
+#include <com/sun/star/chart/ChartDataRowSource.hpp>
+#include <com/sun/star/chart2/data/LabelOrigin.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/deleter.hxx>
+#include <o3tl/string_view.hxx>
+#include <mutex>
+#include <vcl/svapp.hxx>
+
+#include "XMLRangeHelper.hxx"
+#include <unochart.hxx>
+#include <swtable.hxx>
+#include <unoprnms.hxx>
+#include <unomap.hxx>
+#include <unocrsr.hxx>
+#include <unotbl.hxx>
+#include <doc.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <frmfmt.hxx>
+#include <ndole.hxx>
+#include <swtypes.hxx>
+#include <strings.hrc>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <svl/itemprop.hxx>
+
+using namespace ::com::sun::star;
+
+void SwChartHelper::DoUpdateAllCharts( SwDoc* pDoc )
+{
+ if (!pDoc)
+ return;
+
+ SwOLENode *pONd;
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
+ pONd->GetOLEObj().GetObject().IsChart() )
+ {
+ // Load the object and set modified
+
+ uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
+ if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
+ {
+ try
+ {
+ uno::Reference< util::XModifiable > xModif( xIP->getComponent(), uno::UNO_QUERY_THROW );
+ xModif->setModified( true );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ }
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+}
+
+SwChartLockController_Helper::SwChartLockController_Helper( SwDoc *pDocument ) :
+ m_pDoc( pDocument )
+ , m_aUnlockTimer( "sw::SwChartLockController_Helper aUnlockTimer" )
+ , m_bIsLocked( false )
+{
+ m_aUnlockTimer.SetTimeout( 1500 );
+ m_aUnlockTimer.SetInvokeHandler( LINK( this, SwChartLockController_Helper, DoUnlockAllCharts ));
+}
+
+SwChartLockController_Helper::~SwChartLockController_Helper()
+{
+ if (m_pDoc) // still connected?
+ suppress_fun_call_w_exception(Disconnect());
+}
+
+void SwChartLockController_Helper::StartOrContinueLocking()
+{
+ if (!m_bIsLocked)
+ LockAllCharts();
+ m_aUnlockTimer.Start(); // start or continue time of locking
+}
+
+void SwChartLockController_Helper::Disconnect()
+{
+ m_aUnlockTimer.Stop();
+ UnlockAllCharts();
+ m_pDoc = nullptr;
+}
+
+void SwChartLockController_Helper::LockUnlockAllCharts( bool bLock )
+{
+ if (!m_pDoc)
+ return;
+
+ uno::Reference< frame::XModel > xRes;
+ SwOLENode *pONd;
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *m_pDoc->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ if (nullptr != ( pONd = aIdx.GetNode().GetOLENode() ) &&
+ !pONd->GetChartTableName().isEmpty() /* is chart object? */)
+ {
+ uno::Reference < embed::XEmbeddedObject > xIP = pONd->GetOLEObj().GetOleRef();
+ if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
+ {
+ xRes.set( xIP->getComponent(), uno::UNO_QUERY );
+ if (xRes.is())
+ {
+ if (bLock)
+ xRes->lockControllers();
+ else
+ xRes->unlockControllers();
+ }
+ }
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+
+ m_bIsLocked = bLock;
+}
+
+IMPL_LINK_NOARG( SwChartLockController_Helper, DoUnlockAllCharts, Timer *, void )
+{
+ UnlockAllCharts();
+}
+
+static std::mutex & GetChartMutex()
+{
+ static std::mutex aMutex;
+ return aMutex;
+}
+
+static void LaunchModifiedEvent(
+ ::comphelper::OInterfaceContainerHelper4<util::XModifyListener> &rICH,
+ const uno::Reference< uno::XInterface > &rxI )
+{
+ lang::EventObject aEvtObj( rxI );
+ std::unique_lock aGuard(GetChartMutex());
+ rICH.notifyEach( aGuard, &util::XModifyListener::modified, aEvtObj );
+}
+
+/**
+ * rCellRangeName needs to be of one of the following formats:
+ * - e.g. "A2:E5" or
+ * - e.g. "Table1.A2:E5"
+ */
+bool FillRangeDescriptor(
+ SwRangeDescriptor &rDesc,
+ std::u16string_view rCellRangeName )
+{
+ sal_Int32 nToken = std::u16string_view::npos == rCellRangeName.find('.') ? 0 : 1;
+ std::u16string_view aCellRangeNoTableName( o3tl::getToken(rCellRangeName, nToken, '.' ) );
+ OUString aTLName( o3tl::getToken(aCellRangeNoTableName, 0, ':') ); // name of top left cell
+ OUString aBRName( o3tl::getToken(aCellRangeNoTableName, 1, ':') ); // name of bottom right cell
+ if(aTLName.isEmpty() || aBRName.isEmpty())
+ return false;
+
+ rDesc.nTop = rDesc.nLeft = rDesc.nBottom = rDesc.nRight = -1;
+ SwXTextTable::GetCellPosition( aTLName, rDesc.nLeft, rDesc.nTop );
+ SwXTextTable::GetCellPosition( aBRName, rDesc.nRight, rDesc.nBottom );
+ rDesc.Normalize();
+ OSL_ENSURE( rDesc.nTop != -1 &&
+ rDesc.nLeft != -1 &&
+ rDesc.nBottom != -1 &&
+ rDesc.nRight != -1,
+ "failed to get range descriptor" );
+ OSL_ENSURE( rDesc.nTop <= rDesc.nBottom && rDesc.nLeft <= rDesc.nRight,
+ "invalid range descriptor");
+ return true;
+}
+
+static OUString GetCellRangeName( const SwFrameFormat &rTableFormat, SwUnoCursor &rTableCursor )
+{
+ OUString aRes;
+
+ //!! see also SwXTextTableCursor::getRangeName
+
+ SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&rTableCursor);
+ if (!pUnoTableCursor)
+ return OUString();
+
+ // tdf#132714 empty outdated selection cache to avoid crashing in ActualizeSelection()
+ size_t nCount = pUnoTableCursor->GetSelectedBoxesCount();
+ while (nCount--)
+ pUnoTableCursor->DeleteBox(nCount);
+
+ pUnoTableCursor->MakeBoxSels();
+
+ const SwStartNode* pStart;
+ const SwTableBox* pStartBox = nullptr;
+ const SwTableBox* pEndBox = nullptr;
+
+ pStart = pUnoTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ if (pStart)
+ {
+ const SwTable* pTable = SwTable::FindTable( &rTableFormat );
+ pEndBox = pTable->GetTableBox( pStart->GetIndex());
+ aRes = pEndBox->GetName();
+
+ if(pUnoTableCursor->HasMark())
+ {
+ pStart = pUnoTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
+ pStartBox = pTable->GetTableBox( pStart->GetIndex());
+ }
+ OSL_ENSURE( pStartBox, "start box not found" );
+ OSL_ENSURE( pEndBox, "end box not found" );
+
+ // need to switch start and end?
+ if (*pUnoTableCursor->GetPoint() < *pUnoTableCursor->GetMark())
+ {
+ const SwTableBox* pTmpBox = pStartBox;
+ pStartBox = pEndBox;
+ pEndBox = pTmpBox;
+ }
+
+ if (!pStartBox)
+ return aRes;
+
+ aRes = pStartBox->GetName() + ":";
+ if (pEndBox)
+ aRes += pEndBox->GetName();
+ else
+ aRes += pStartBox->GetName();
+ }
+
+ return aRes;
+}
+
+static OUString GetRangeRepFromTableAndCells( std::u16string_view rTableName,
+ std::u16string_view rStartCell, std::u16string_view rEndCell,
+ bool bForceEndCellName )
+{
+ OSL_ENSURE( !rTableName.empty(), "table name missing" );
+ OSL_ENSURE( !rStartCell.empty(), "cell name missing" );
+ OUString aRes = OUString::Concat(rTableName) + "." + rStartCell;
+
+ if (!rEndCell.empty())
+ {
+ aRes += OUString::Concat(":") + rEndCell;
+ }
+ else if (bForceEndCellName)
+ {
+ aRes += OUString::Concat(":") + rStartCell;
+ }
+
+ return aRes;
+}
+
+static bool GetTableAndCellsFromRangeRep(
+ std::u16string_view rRangeRepresentation,
+ OUString &rTableName,
+ OUString &rStartCell,
+ OUString &rEndCell,
+ bool bSortStartEndCells = true )
+{
+ // parse range representation for table name and cell/range names
+ // accepted format sth like: "Table1.A2:C5" , "Table2.A2.1:B3.2"
+ OUString aTableName; // table name
+ OUString aStartCell; // name of top left cell
+ OUString aEndCell; // name of bottom right cell
+ size_t nIdx = rRangeRepresentation.find( '.' );
+ if (nIdx != std::u16string_view::npos)
+ {
+ aTableName = rRangeRepresentation.substr( 0, nIdx );
+ std::u16string_view aRange = rRangeRepresentation.substr( nIdx + 1 ); // cell range
+ size_t nPos = aRange.find( ':' );
+ if (nPos != std::u16string_view::npos) // a cell-range like "Table1.A2:D4"
+ {
+ aStartCell = aRange.substr( 0, nPos );
+ aEndCell = aRange.substr( nPos + 1 );
+
+ // need to switch start and end cell ?
+ // (does not check for normalization here)
+ if (bSortStartEndCells && 1 == sw_CompareCellsByColFirst( aStartCell, aEndCell ))
+ {
+ OUString aTmp( aStartCell );
+ aStartCell = aEndCell;
+ aEndCell = aTmp;
+ }
+ }
+ else // a single cell like in "Table1.B3"
+ {
+ aStartCell = aEndCell = aRange;
+ }
+ }
+
+ bool bSuccess = !aTableName.isEmpty() &&
+ !aStartCell.isEmpty() && !aEndCell.isEmpty();
+ if (bSuccess)
+ {
+ rTableName = aTableName;
+ rStartCell = aStartCell;
+ rEndCell = aEndCell;
+ }
+ return bSuccess;
+}
+
+static void GetTableByName( const SwDoc &rDoc, std::u16string_view rTableName,
+ SwFrameFormat **ppTableFormat, SwTable **ppTable)
+{
+ SwFrameFormat *pTableFormat = nullptr;
+
+ // find frame format of table
+ //! see SwXTextTables::getByName
+ const size_t nCount = rDoc.GetTableFrameFormatCount(true);
+ for (size_t i = 0; i < nCount && !pTableFormat; ++i)
+ {
+ SwFrameFormat& rTableFormat = rDoc.GetTableFrameFormat(i, true);
+ if(rTableName == rTableFormat.GetName())
+ pTableFormat = &rTableFormat;
+ }
+
+ if (ppTableFormat)
+ *ppTableFormat = pTableFormat;
+
+ if (ppTable)
+ *ppTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
+}
+
+static void GetFormatAndCreateCursorFromRangeRep(
+ const SwDoc *pDoc,
+ std::u16string_view rRangeRepresentation, // must be a single range (i.e. so called sub-range)
+ SwFrameFormat **ppTableFormat, // will be set to the table format of the table used in the range representation
+ std::shared_ptr<SwUnoCursor>& rpUnoCursor ) // will be set to cursor spanning the cell range (cursor will be created!)
+{
+ OUString aTableName; // table name
+ OUString aStartCell; // name of top left cell
+ OUString aEndCell; // name of bottom right cell
+ bool bNamesFound = GetTableAndCellsFromRangeRep( rRangeRepresentation,
+ aTableName, aStartCell, aEndCell );
+
+ if (!bNamesFound)
+ {
+ if (ppTableFormat)
+ *ppTableFormat = nullptr;
+ rpUnoCursor.reset();
+ }
+ else
+ {
+ SwFrameFormat *pTableFormat = nullptr;
+
+ // is the correct table format already provided?
+ if (*ppTableFormat != nullptr && (*ppTableFormat)->GetName() == aTableName)
+ pTableFormat = *ppTableFormat;
+ else
+ GetTableByName( *pDoc, aTableName, &pTableFormat, nullptr );
+
+ *ppTableFormat = pTableFormat;
+
+ rpUnoCursor.reset(); // default result in case of failure
+
+ SwTable *pTable = pTableFormat ? SwTable::FindTable( pTableFormat ) : nullptr;
+ // create new SwUnoCursor spanning the specified range
+ //! see also SwXTextTable::GetRangeByName
+ // #i80314#
+ // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
+ const SwTableBox* pTLBox =
+ pTable ? pTable->GetTableBox( aStartCell, true ) : nullptr;
+ if(pTLBox)
+ {
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+
+ // set cursor to top left box of range
+ auto pUnoCursor = pTableFormat->GetDoc()->CreateUnoCursor(aPos, true);
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ pUnoCursor->SetRemainInSection( false );
+
+ // #i80314#
+ // perform validation check. Thus, pass <true> as 2nd parameter to <SwTable::GetTableBox(..)>
+ const SwTableBox* pBRBox = pTable->GetTableBox( aEndCell, true );
+ if(pBRBox)
+ {
+ pUnoCursor->SetMark();
+ pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ SwUnoTableCursor& rCursor =
+ dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ // HACK: remove pending actions for old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ rCursor.MakeBoxSels();
+ rpUnoCursor = pUnoCursor;
+ }
+ }
+ }
+}
+
+static bool GetSubranges( std::u16string_view rRangeRepresentation,
+ uno::Sequence< OUString > &rSubRanges, bool bNormalize )
+{
+ bool bRes = true;
+ const sal_Int32 nLen = comphelper::string::getTokenCount(rRangeRepresentation, ';');
+ uno::Sequence< OUString > aRanges( nLen );
+
+ sal_Int32 nCnt = 0;
+ if (nLen != 0)
+ {
+ OUString *pRanges = aRanges.getArray();
+ OUString aFirstTable;
+ sal_Int32 nPos = 0;
+ for( sal_Int32 i = 0; i < nLen && bRes; ++i )
+ {
+ const OUString aRange( o3tl::getToken(rRangeRepresentation, 0, ';', nPos ) );
+ if (!aRange.isEmpty())
+ {
+ pRanges[nCnt] = aRange;
+
+ OUString aTableName, aStartCell, aEndCell;
+ if (!GetTableAndCellsFromRangeRep( aRange,
+ aTableName, aStartCell, aEndCell ))
+ bRes = false;
+
+ if (bNormalize)
+ {
+ sw_NormalizeRange( aStartCell, aEndCell );
+ pRanges[nCnt] = GetRangeRepFromTableAndCells( aTableName,
+ aStartCell, aEndCell, true );
+ }
+
+ // make sure to use only a single table
+ if (nCnt == 0)
+ aFirstTable = aTableName;
+ else
+ if (aFirstTable != aTableName) bRes = false;
+
+ ++nCnt;
+ }
+ }
+ }
+ aRanges.realloc( nCnt );
+
+ rSubRanges = aRanges;
+ return bRes;
+}
+
+static void SortSubranges( uno::Sequence< OUString > &rSubRanges, bool bCmpByColumn )
+{
+ sal_Int32 nLen = rSubRanges.getLength();
+ OUString *pSubRanges = rSubRanges.getArray();
+
+ OUString aSmallestTableName;
+ OUString aSmallestStartCell;
+ OUString aSmallestEndCell;
+
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ sal_Int32 nIdxOfSmallest = i;
+ GetTableAndCellsFromRangeRep( pSubRanges[nIdxOfSmallest],
+ aSmallestTableName, aSmallestStartCell, aSmallestEndCell );
+ if (aSmallestEndCell.isEmpty())
+ aSmallestEndCell = aSmallestStartCell;
+
+ for (sal_Int32 k = i+1; k < nLen; ++k)
+ {
+ // get cell names for sub range
+ OUString aTableName;
+ OUString aStartCell;
+ OUString aEndCell;
+ GetTableAndCellsFromRangeRep( pSubRanges[k],
+ aTableName, aStartCell, aEndCell );
+ if (aEndCell.isEmpty())
+ aEndCell = aStartCell;
+
+ // compare cell ranges ( is the new one smaller? )
+ if (-1 == sw_CompareCellRanges( aStartCell, aEndCell,
+ aSmallestStartCell, aSmallestEndCell, bCmpByColumn ))
+ {
+ nIdxOfSmallest = k;
+ aSmallestTableName = aTableName;
+ aSmallestStartCell = aStartCell;
+ aSmallestEndCell = aEndCell;
+ }
+ }
+
+ // move smallest element to the start of the not sorted area
+ const OUString aTmp( pSubRanges[ nIdxOfSmallest ] );
+ pSubRanges[ nIdxOfSmallest ] = pSubRanges[ i ];
+ pSubRanges[ i ] = aTmp;
+ }
+}
+
+SwChartDataProvider::SwChartDataProvider( const SwDoc& rSwDoc ) :
+ m_pDoc( &rSwDoc )
+{
+ m_bDisposed = false;
+}
+
+SwChartDataProvider::~SwChartDataProvider()
+{
+}
+
+uno::Reference< chart2::data::XDataSource > SwChartDataProvider::Impl_createDataSource(
+ const uno::Sequence< beans::PropertyValue >& rArguments, bool bTestOnly )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ uno::Reference< chart2::data::XDataSource > xRes;
+
+ if (!m_pDoc)
+ throw uno::RuntimeException("Not connected to a document.");
+
+ // get arguments
+ OUString aRangeRepresentation;
+ uno::Sequence< sal_Int32 > aSequenceMapping;
+ bool bFirstIsLabel = false;
+ bool bDtaSrcIsColumns = true; // true : DataSource will be sequence of columns
+ // false: DataSource will be sequence of rows
+
+ OUString aChartOleObjectName; //work around wrong writer ranges ( see Issue 58464 )
+ sal_Int32 nArgs = rArguments.getLength();
+ OSL_ENSURE( nArgs != 0, "no properties provided" );
+ if (nArgs == 0)
+ return xRes;
+ for (const beans::PropertyValue& rArg : rArguments)
+ {
+ if ( rArg.Name == "DataRowSource" )
+ {
+ chart::ChartDataRowSource eSource;
+ if (!(rArg.Value >>= eSource))
+ {
+ sal_Int32 nTmp = 0;
+ if (!(rArg.Value >>= nTmp))
+ throw lang::IllegalArgumentException();
+ eSource = static_cast< chart::ChartDataRowSource >( nTmp );
+ }
+ bDtaSrcIsColumns = eSource == chart::ChartDataRowSource_COLUMNS;
+ }
+ else if ( rArg.Name == "FirstCellAsLabel" )
+ {
+ if (!(rArg.Value >>= bFirstIsLabel))
+ throw lang::IllegalArgumentException();
+ }
+ else if ( rArg.Name == "CellRangeRepresentation" )
+ {
+ if (!(rArg.Value >>= aRangeRepresentation))
+ throw lang::IllegalArgumentException();
+ }
+ else if ( rArg.Name == "SequenceMapping" )
+ {
+ if (!(rArg.Value >>= aSequenceMapping))
+ throw lang::IllegalArgumentException();
+ }
+ else if ( rArg.Name == "ChartOleObjectName" )
+ {
+ if (!(rArg.Value >>= aChartOleObjectName))
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ uno::Sequence< OUString > aSubRanges;
+ // get sub-ranges and check that they all are from the very same table
+ bool bOk = GetSubranges( aRangeRepresentation, aSubRanges, true );
+
+ if (!bOk && m_pDoc && !aChartOleObjectName.isEmpty() )
+ {
+ //try to correct the range here
+ //work around wrong writer ranges ( see Issue 58464 )
+ OUString aChartTableName;
+
+ const SwNodes& rNodes = m_pDoc->GetNodes();
+ for( SwNodeOffset nN = rNodes.Count(); nN--; )
+ {
+ SwNode* pNode = rNodes[nN];
+ if( !pNode )
+ continue;
+ const SwOLENode* pOleNode = pNode->GetOLENode();
+ if( !pOleNode )
+ continue;
+ const SwOLEObj& rOObj = pOleNode->GetOLEObj();
+ if( aChartOleObjectName == rOObj.GetCurrentPersistName() )
+ {
+ aChartTableName = pOleNode->GetChartTableName();
+ break;
+ }
+ }
+
+ if( !aChartTableName.isEmpty() )
+ {
+ //the wrong range is still shifted one row down
+ //thus the first row is missing and an invalid row at the end is added.
+ //Therefore we need to shift the range one row up
+ SwRangeDescriptor aDesc;
+ if (aRangeRepresentation.isEmpty())
+ return xRes; // we can't handle this thus returning an empty references
+
+ aRangeRepresentation = aRangeRepresentation.copy( 1 ); // get rid of '.' to have only the cell range left
+ FillRangeDescriptor( aDesc, aRangeRepresentation );
+ aDesc.Normalize();
+
+ if (aDesc.nTop <= 0) // no chance to shift the range one row up?
+ return xRes; // we can't handle this thus returning an empty references
+
+ aDesc.nTop -= 1;
+ aDesc.nBottom -= 1;
+
+ OUString aNewStartCell( sw_GetCellName( aDesc.nLeft, aDesc.nTop ) );
+ OUString aNewEndCell( sw_GetCellName( aDesc.nRight, aDesc.nBottom ) );
+ aRangeRepresentation = GetRangeRepFromTableAndCells(
+ aChartTableName, aNewStartCell, aNewEndCell, true );
+ bOk = GetSubranges( aRangeRepresentation, aSubRanges, true );
+ }
+ }
+ if (!bOk) // different tables used, or incorrect range specifiers
+ throw lang::IllegalArgumentException();
+
+ SortSubranges( aSubRanges, bDtaSrcIsColumns );
+
+ // get table format for that single table from above
+ SwFrameFormat *pTableFormat = nullptr; // pointer to table format
+ std::shared_ptr<SwUnoCursor> pUnoCursor; // here required to check if the cells in the range do actually exist
+ if (aSubRanges.hasElements())
+ GetFormatAndCreateCursorFromRangeRep( m_pDoc, aSubRanges[0], &pTableFormat, pUnoCursor );
+
+ if (!pTableFormat || !pUnoCursor)
+ throw lang::IllegalArgumentException();
+
+ SwTable* pTable = SwTable::FindTable(pTableFormat);
+ if (pTable->IsTableComplex())
+ return xRes; // we can't handle this thus returning an empty references
+
+ // get a character map in the size of the table to mark
+ // all the ranges to use in
+ sal_Int32 nRows = pTable->GetTabLines().size();
+ sal_Int32 nCols = 0;
+ // As per tdf#149718 one should know that some cells can be merged together.
+ // Therefore, the number of columns (boxes in each row) are not necessarily
+ // equal. Here, we calculate the maximum number of columns in all rows.
+ for (sal_Int32 i = 0; i < nRows; ++i)
+ nCols = std::max(nCols, static_cast<sal_Int32>(pTable->GetTabLines()[i]->GetTabBoxes().size()));
+
+ std::vector<std::vector<char>> aMap(nRows);
+ for (sal_Int32 i = 0; i < nRows; ++i)
+ aMap[i].resize(nCols);
+
+ // iterate over subranges and mark used cells in above map
+ //!! by proceeding this way we automatically get rid of
+ //!! multiple listed or overlapping cell ranges which should
+ //!! just be ignored silently
+ for (const OUString& rSubRange : std::as_const(aSubRanges))
+ {
+ OUString aTableName, aStartCell, aEndCell;
+ bool bOk2 = GetTableAndCellsFromRangeRep(
+ rSubRange, aTableName, aStartCell, aEndCell );
+ OSL_ENSURE(bOk2, "failed to get table and start/end cells");
+
+ sal_Int32 nStartRow, nStartCol, nEndRow, nEndCol;
+ SwXTextTable::GetCellPosition(aStartCell, nStartCol, nStartRow);
+ SwXTextTable::GetCellPosition(aEndCell, nEndCol, nEndRow);
+ OSL_ENSURE( nStartRow <= nEndRow && nStartCol <= nEndCol,
+ "cell range not normalized");
+
+ // test if the ranges span more than the available cells
+ if( nStartRow < 0 || nEndRow >= nRows ||
+ nStartCol < 0 || nEndCol >= nCols )
+ {
+ throw lang::IllegalArgumentException();
+ }
+ for (sal_Int32 k1 = nStartRow; k1 <= nEndRow; ++k1)
+ {
+ for (sal_Int32 k2 = nStartCol; k2 <= nEndCol; ++k2)
+ aMap[k1][k2] = 'x';
+ }
+ }
+
+ // find label and data sequences to use
+
+ sal_Int32 oi; // outer index (slower changing index)
+ sal_Int32 ii; // inner index (faster changing index)
+ sal_Int32 oiEnd = bDtaSrcIsColumns ? nCols : nRows;
+ sal_Int32 iiEnd = bDtaSrcIsColumns ? nRows : nCols;
+ std::vector<sal_Int32> aLabelIdx(oiEnd);
+ std::vector<sal_Int32> aDataStartIdx(oiEnd);
+ std::vector<sal_Int32> aDataLen(oiEnd);
+ for (oi = 0; oi < oiEnd; ++oi)
+ {
+ aLabelIdx[oi] = -1;
+ aDataStartIdx[oi] = -1;
+ aDataLen[oi] = 0;
+ }
+
+ for (oi = 0; oi < oiEnd; ++oi)
+ {
+ ii = 0;
+ while (ii < iiEnd)
+ {
+ char &rChar = bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii];
+
+ // label should be used but is not yet found?
+ if (rChar == 'x' && bFirstIsLabel && aLabelIdx[oi] == -1)
+ {
+ aLabelIdx[oi] = ii;
+ rChar = 'L'; // setting a different char for labels here
+ // makes the test for the data sequence below
+ // easier
+ }
+
+ // find data sequence
+ if (rChar == 'x' && aDataStartIdx[oi] == -1)
+ {
+ aDataStartIdx[oi] = ii;
+
+ // get length of data sequence
+ sal_Int32 nL = 0;
+ while (ii< iiEnd && 'x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
+ {
+ ++nL; ++ii;
+ }
+ aDataLen[oi] = nL;
+
+ // check that there is no other separate sequence of data
+ // to be found because that is not supported
+ while (ii < iiEnd)
+ {
+ if ('x' == (bDtaSrcIsColumns ? aMap[ii][oi] : aMap[oi][ii]))
+ throw lang::IllegalArgumentException();
+ ++ii;
+ }
+ }
+ else
+ ++ii;
+ }
+ }
+
+ // make some other consistency checks while calculating
+ // the number of XLabeledDataSequence to build:
+ // - labels should always be used or not at all
+ // - the data sequences should have equal non-zero length
+ sal_Int32 nNumLDS = 0;
+ if (oiEnd > 0)
+ {
+ sal_Int32 nFirstSeqLen = 0;
+ sal_Int32 nFirstSeqLabelIdx = -1;
+ for (oi = 0; oi < oiEnd; ++oi)
+ {
+ bool bFirstFound = false;
+ // row/col used at all?
+ if (aDataStartIdx[oi] != -1 &&
+ (!bFirstIsLabel || aLabelIdx[oi] != -1))
+ {
+ ++nNumLDS;
+ if (!bFirstFound)
+ {
+ nFirstSeqLen = aDataLen[oi];
+ nFirstSeqLabelIdx = aLabelIdx[oi];
+ bFirstFound = true;
+ }
+ else
+ {
+ if (nFirstSeqLen != aDataLen[oi] ||
+ nFirstSeqLabelIdx != aLabelIdx[oi])
+ throw lang::IllegalArgumentException();
+ }
+ }
+ }
+ }
+ if (nNumLDS == 0)
+ throw lang::IllegalArgumentException();
+
+ // now we should have all necessary data to build a proper DataSource
+ // thus if we came this far there should be no further problem
+ if (bTestOnly)
+ return xRes; // have createDataSourcePossible return true
+
+ // create data source from found label and data sequences
+ uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aLabelSeqs(nNumLDS);
+ uno::Reference<chart2::data::XDataSequence>* pLabelSeqs = aLabelSeqs.getArray();
+ uno::Sequence<uno::Reference<chart2::data::XDataSequence>> aDataSeqs(nNumLDS);
+ uno::Reference<chart2::data::XDataSequence>* pDataSeqs = aDataSeqs.getArray();
+ sal_Int32 nSeqsIdx = 0;
+ for (oi = 0; oi < oiEnd; ++oi)
+ {
+ // row/col not used? (see if-statement above where nNumLDS was counted)
+ if (!(aDataStartIdx[oi] != -1 &&
+ (!bFirstIsLabel || aLabelIdx[oi] != -1)))
+ continue;
+
+ // get cell ranges for label and data
+
+ SwRangeDescriptor aLabelDesc;
+ SwRangeDescriptor aDataDesc;
+ if (bDtaSrcIsColumns) // use columns
+ {
+ aLabelDesc.nTop = aLabelIdx[oi];
+ aLabelDesc.nLeft = oi;
+ aLabelDesc.nBottom = aLabelDesc.nTop;
+ aLabelDesc.nRight = oi;
+
+ aDataDesc.nTop = aDataStartIdx[oi];
+ aDataDesc.nLeft = oi;
+ aDataDesc.nBottom = aDataDesc.nTop + aDataLen[oi] - 1;
+ aDataDesc.nRight = oi;
+ }
+ else // use rows
+ {
+ aLabelDesc.nTop = oi;
+ aLabelDesc.nLeft = aLabelIdx[oi];
+ aLabelDesc.nBottom = oi;
+ aLabelDesc.nRight = aLabelDesc.nLeft;
+
+ aDataDesc.nTop = oi;
+ aDataDesc.nLeft = aDataStartIdx[oi];
+ aDataDesc.nBottom = oi;
+ aDataDesc.nRight = aDataDesc.nLeft + aDataLen[oi] - 1;
+ }
+ const OUString aBaseName = pTableFormat->GetName() + ".";
+
+ OUString aLabelRange;
+ if (aLabelIdx[oi] != -1)
+ {
+ aLabelRange = aBaseName
+ + sw_GetCellName( aLabelDesc.nLeft, aLabelDesc.nTop )
+ + ":" + sw_GetCellName( aLabelDesc.nRight, aLabelDesc.nBottom );
+ }
+
+ OUString aDataRange = aBaseName
+ + sw_GetCellName( aDataDesc.nLeft, aDataDesc.nTop )
+ + ":" + sw_GetCellName( aDataDesc.nRight, aDataDesc.nBottom );
+
+ // get cursors spanning the cell ranges for label and data
+ std::shared_ptr<SwUnoCursor> pLabelUnoCursor;
+ std::shared_ptr<SwUnoCursor> pDataUnoCursor;
+ GetFormatAndCreateCursorFromRangeRep(m_pDoc, aLabelRange, &pTableFormat, pLabelUnoCursor);
+ GetFormatAndCreateCursorFromRangeRep(m_pDoc, aDataRange, &pTableFormat, pDataUnoCursor);
+
+ // create XDataSequence's from cursors
+ if (pLabelUnoCursor)
+ pLabelSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pLabelUnoCursor);
+ OSL_ENSURE(pDataUnoCursor, "pointer to data sequence missing");
+ if (pDataUnoCursor)
+ pDataSeqs[nSeqsIdx] = new SwChartDataSequence(*this, *pTableFormat, pDataUnoCursor);
+ if (pLabelUnoCursor || pDataUnoCursor)
+ ++nSeqsIdx;
+ }
+ OSL_ENSURE(nSeqsIdx == nNumLDS, "mismatch between sequence size and num,ber of entries");
+
+ // build data source from data and label sequences
+ uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aLDS(nNumLDS);
+ uno::Reference<chart2::data::XLabeledDataSequence>* pLDS = aLDS.getArray();
+ for (sal_Int32 i = 0; i < nNumLDS; ++i)
+ {
+ rtl::Reference<SwChartLabeledDataSequence> pLabeledDtaSeq = new SwChartLabeledDataSequence;
+ pLabeledDtaSeq->setLabel(pLabelSeqs[i]);
+ pLabeledDtaSeq->setValues(pDataSeqs[i]);
+ pLDS[i] = pLabeledDtaSeq;
+ }
+
+ // apply 'SequenceMapping' if it was provided
+ if (aSequenceMapping.hasElements())
+ {
+ uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> aOld_LDS(aLDS);
+ uno::Reference<chart2::data::XLabeledDataSequence>* pOld_LDS = aOld_LDS.getArray();
+
+ sal_Int32 nNewCnt = 0;
+ for (sal_Int32 nIdx : std::as_const(aSequenceMapping))
+ {
+ // check that index to be used is valid
+ // and has not yet been used
+ if (0 <= nIdx && nIdx < nNumLDS && pOld_LDS[nIdx].is())
+ {
+ pLDS[nNewCnt++] = pOld_LDS[nIdx];
+
+ // mark index as being used already (avoids duplicate entries)
+ pOld_LDS[nIdx].clear();
+ }
+ }
+ // add not yet used 'old' sequences to new one
+ for (sal_Int32 i = 0; i < nNumLDS; ++i)
+ {
+ if (pOld_LDS[i].is())
+ pLDS[nNewCnt++] = pOld_LDS[i];
+ }
+ OSL_ENSURE(nNewCnt == nNumLDS, "unexpected size of resulting sequence");
+ }
+
+ xRes = new SwChartDataSource(aLDS);
+ return xRes;
+}
+
+sal_Bool SAL_CALL SwChartDataProvider::createDataSourcePossible(
+ const uno::Sequence< beans::PropertyValue >& rArguments )
+{
+ SolarMutexGuard aGuard;
+
+ bool bPossible = true;
+ try
+ {
+ Impl_createDataSource( rArguments, true );
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ bPossible = false;
+ }
+
+ return bPossible;
+}
+
+uno::Reference< chart2::data::XDataSource > SAL_CALL SwChartDataProvider::createDataSource(
+ const uno::Sequence< beans::PropertyValue >& rArguments )
+{
+ SolarMutexGuard aGuard;
+ return Impl_createDataSource( rArguments );
+}
+
+/**
+ * Fix for #i79009
+ * we need to return a property that has the same value as the property
+ * 'CellRangeRepresentation' but for all rows which are increased by one.
+ * E.g. Table1.A1:D5 -> Table1,A2:D6
+ * Since the problem is only for old charts which did not support multiple
+ * we do not need to provide that property/string if the 'CellRangeRepresentation'
+ * contains multiple ranges.
+ */
+OUString SwChartDataProvider::GetBrokenCellRangeForExport(
+ std::u16string_view rCellRangeRepresentation )
+{
+ // check that we do not have multiple ranges
+ if (std::u16string_view::npos == rCellRangeRepresentation.find( ';' ))
+ {
+ // get current cell and table names
+ OUString aTableName, aStartCell, aEndCell;
+ GetTableAndCellsFromRangeRep( rCellRangeRepresentation,
+ aTableName, aStartCell, aEndCell, false );
+ sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
+ SwXTextTable::GetCellPosition( aStartCell, nStartCol, nStartRow );
+ SwXTextTable::GetCellPosition( aEndCell, nEndCol, nEndRow );
+
+ // get new cell names
+ ++nStartRow;
+ ++nEndRow;
+ aStartCell = sw_GetCellName( nStartCol, nStartRow );
+ aEndCell = sw_GetCellName( nEndCol, nEndRow );
+
+ return GetRangeRepFromTableAndCells( aTableName,
+ aStartCell, aEndCell, false );
+ }
+
+ return OUString();
+}
+
+uno::Sequence< beans::PropertyValue > SAL_CALL SwChartDataProvider::detectArguments(
+ const uno::Reference< chart2::data::XDataSource >& xDataSource )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ uno::Sequence< beans::PropertyValue > aResult;
+ if (!xDataSource.is())
+ return aResult;
+
+ const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aDS_LDS( xDataSource->getDataSequences() );
+ const uno::Reference< chart2::data::XLabeledDataSequence > *pDS_LDS = aDS_LDS.getConstArray();
+ sal_Int32 nNumDS_LDS = aDS_LDS.getLength();
+
+ if (nNumDS_LDS == 0)
+ {
+ OSL_FAIL( "XLabeledDataSequence in data source contains 0 entries" );
+ return aResult;
+ }
+
+ SwFrameFormat *pTableFormat = nullptr;
+ SwTable *pTable = nullptr;
+ OUString aTableName;
+ sal_Int32 nTableRows = 0;
+ sal_Int32 nTableCols = 0;
+
+ // data used to build 'CellRangeRepresentation' from later on
+ std::vector< std::vector< char > > aMap;
+
+ uno::Sequence< sal_Int32 > aSequenceMapping( nNumDS_LDS );
+ sal_Int32 *pSequenceMapping = aSequenceMapping.getArray();
+
+ OUString aCellRanges;
+ sal_Int16 nDtaSrcIsColumns = -1;// -1: don't know yet, 0: false, 1: true -2: neither
+ sal_Int32 nLabelSeqLen = -1; // used to see if labels are always used or not and have
+ // the expected size of 1 (i.e. if FirstCellAsLabel can
+ // be determined)
+ // -1: don't know yet, 0: not used, 1: always a single labe cell, ...
+ // -2: neither/failed
+ for (sal_Int32 nDS1 = 0; nDS1 < nNumDS_LDS; ++nDS1)
+ {
+ uno::Reference< chart2::data::XLabeledDataSequence > xLabeledDataSequence( pDS_LDS[nDS1] );
+ if( !xLabeledDataSequence.is() )
+ {
+ OSL_FAIL("got NULL for XLabeledDataSequence from Data source");
+ continue;
+ }
+ const uno::Reference< chart2::data::XDataSequence > xCurLabel = xLabeledDataSequence->getLabel();
+ const uno::Reference< chart2::data::XDataSequence > xCurValues = xLabeledDataSequence->getValues();
+
+ // get sequence lengths for label and values.
+ // (0 length is Ok)
+ sal_Int32 nCurLabelSeqLen = -1;
+ sal_Int32 nCurValuesSeqLen = -1;
+ if (xCurLabel.is())
+ nCurLabelSeqLen = xCurLabel->getData().getLength();
+ if (xCurValues.is())
+ nCurValuesSeqLen = xCurValues->getData().getLength();
+
+ // check for consistent use of 'first cell as label'
+ if (nLabelSeqLen == -1) // set initial value to compare with below further on
+ nLabelSeqLen = nCurLabelSeqLen;
+ if (nLabelSeqLen != nCurLabelSeqLen)
+ nLabelSeqLen = -2; // failed / no consistent use of label cells
+
+ // get table and cell names for label and values data sequences
+ // (start and end cell will be sorted, i.e. start cell <= end cell)
+ OUString aLabelTableName, aLabelStartCell, aLabelEndCell;
+ OUString aValuesTableName, aValuesStartCell, aValuesEndCell;
+ OUString aLabelRange, aValuesRange;
+ if (xCurLabel.is())
+ aLabelRange = xCurLabel->getSourceRangeRepresentation();
+ if (xCurValues.is())
+ aValuesRange = xCurValues->getSourceRangeRepresentation();
+ if ((!aLabelRange.isEmpty() && !GetTableAndCellsFromRangeRep( aLabelRange,
+ aLabelTableName, aLabelStartCell, aLabelEndCell )) ||
+ !GetTableAndCellsFromRangeRep( aValuesRange,
+ aValuesTableName, aValuesStartCell, aValuesEndCell ))
+ {
+ return aResult; // failed -> return empty property sequence
+ }
+
+ // make sure all sequences use the same table
+ if (aTableName.isEmpty())
+ aTableName = aValuesTableName; // get initial value to compare with
+ if (aTableName.isEmpty() ||
+ aTableName != aValuesTableName ||
+ (!aLabelTableName.isEmpty() && aTableName != aLabelTableName))
+ {
+ return aResult; // failed -> return empty property sequence
+ }
+
+ // try to get 'DataRowSource' value (ROWS or COLUMNS) from inspecting
+ // first and last cell used in both sequences
+
+ sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
+ const OUString aCell( !aLabelStartCell.isEmpty() ? aLabelStartCell : aValuesStartCell );
+ OSL_ENSURE( !aCell.isEmpty() , "start cell missing?" );
+ SwXTextTable::GetCellPosition( aCell, nFirstCol, nFirstRow);
+ SwXTextTable::GetCellPosition( aValuesEndCell, nLastCol, nLastRow);
+
+ sal_Int16 nDirection = -1; // -1: not yet set, 0: columns, 1: rows, -2: failed
+ if (nFirstCol == nLastCol && nFirstRow == nLastRow) // a single cell...
+ {
+ OSL_ENSURE( nCurLabelSeqLen == 0 && nCurValuesSeqLen == 1,
+ "trying to determine 'DataRowSource': something's fishy... should have been a single cell");
+ nDirection = 0; // default direction for a single cell should be 'columns'
+ }
+ else // more than one cell is available (in values and label together!)
+ {
+ if (nFirstCol == nLastCol && nFirstRow != nLastRow)
+ nDirection = 1;
+ else if (nFirstCol != nLastCol && nFirstRow == nLastRow)
+ nDirection = 0;
+ else
+ {
+ OSL_FAIL( "trying to determine 'DataRowSource': unexpected case found" );
+ nDirection = -2;
+ }
+ }
+ // check for consistent direction of data source
+ if (nDtaSrcIsColumns == -1) // set initial value to compare with below
+ nDtaSrcIsColumns = nDirection;
+ if (nDtaSrcIsColumns != nDirection)
+ {
+ nDtaSrcIsColumns = -2; // failed
+ }
+
+ if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
+ {
+ // build data to obtain 'SequenceMapping' later on
+
+ OSL_ENSURE( nDtaSrcIsColumns == 0 || /* rows */
+ nDtaSrcIsColumns == 1, /* columns */
+ "unexpected value for 'nDtaSrcIsColumns'" );
+ pSequenceMapping[nDS1] = nDtaSrcIsColumns ? nFirstCol : nFirstRow;
+
+ // build data used to determine 'CellRangeRepresentation' later on
+
+ GetTableByName( *m_pDoc, aTableName, &pTableFormat, &pTable );
+ if (!pTable || pTable->IsTableComplex())
+ return aResult; // failed -> return empty property sequence
+ nTableRows = pTable->GetTabLines().size();
+ nTableCols = pTable->GetTabLines().front()->GetTabBoxes().size();
+ aMap.resize( nTableRows );
+ for (sal_Int32 i = 0; i < nTableRows; ++i)
+ aMap[i].resize( nTableCols );
+
+ if (!aLabelStartCell.isEmpty() && !aLabelEndCell.isEmpty())
+ {
+ sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
+ SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow );
+ SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow );
+ if (nStartRow < 0 || nEndRow >= nTableRows ||
+ nStartCol < 0 || nEndCol >= nTableCols)
+ {
+ return aResult; // failed -> return empty property sequence
+ }
+ for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
+ {
+ for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
+ {
+ char &rChar = aMap[i][k];
+ if (rChar == '\0') // check for overlapping values and/or labels
+ rChar = 'L';
+ else
+ return aResult; // failed -> return empty property sequence
+ }
+ }
+ }
+ if (!aValuesStartCell.isEmpty() && !aValuesEndCell.isEmpty())
+ {
+ sal_Int32 nStartCol = -1, nStartRow = -1, nEndCol = -1, nEndRow = -1;
+ SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow );
+ SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow );
+ if (nStartRow < 0 || nEndRow >= nTableRows ||
+ nStartCol < 0 || nEndCol >= nTableCols)
+ {
+ return aResult; // failed -> return empty property sequence
+ }
+ for (sal_Int32 i = nStartRow; i <= nEndRow; ++i)
+ {
+ for (sal_Int32 k = nStartCol; k <= nEndCol; ++k)
+ {
+ char &rChar = aMap[i][k];
+ if (rChar == '\0') // check for overlapping values and/or labels
+ rChar = 'x';
+ else
+ return aResult; // failed -> return empty property sequence
+ }
+ }
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ // do some extra sanity checking that the length of the sequences
+ // matches their range representation
+ {
+ sal_Int32 nStartRow = -1, nStartCol = -1, nEndRow = -1, nEndCol = -1;
+ if (xCurLabel.is())
+ {
+ SwXTextTable::GetCellPosition( aLabelStartCell, nStartCol, nStartRow);
+ SwXTextTable::GetCellPosition( aLabelEndCell, nEndCol, nEndRow);
+ OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurLabel->getData().getLength()) ||
+ (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurLabel->getData().getLength()),
+ "label sequence length does not match range representation!" );
+ }
+ if (xCurValues.is())
+ {
+ SwXTextTable::GetCellPosition( aValuesStartCell, nStartCol, nStartRow);
+ SwXTextTable::GetCellPosition( aValuesEndCell, nEndCol, nEndRow);
+ OSL_ENSURE( (nStartCol == nEndCol && (nEndRow - nStartRow + 1) == xCurValues->getData().getLength()) ||
+ (nStartRow == nEndRow && (nEndCol - nStartCol + 1) == xCurValues->getData().getLength()),
+ "value sequence length does not match range representation!" );
+ }
+ }
+#endif
+ } // for
+
+ // build value for 'CellRangeRepresentation'
+
+ const OUString aCellRangeBase = aTableName + ".";
+ OUString aCurRange;
+ for (sal_Int32 i = 0; i < nTableRows; ++i)
+ {
+ for (sal_Int32 k = 0; k < nTableCols; ++k)
+ {
+ if (aMap[i][k] != '\0') // top-left cell of a sub-range found
+ {
+ // find rectangular sub-range to use
+ sal_Int32 nRowIndex1 = i; // row index
+ sal_Int32 nColIndex1 = k; // column index
+ sal_Int32 nRowSubLen = 0;
+ sal_Int32 nColSubLen = 0;
+ while (nRowIndex1 < nTableRows && aMap[nRowIndex1++][k] != '\0')
+ ++nRowSubLen;
+ // be aware of shifted sequences!
+ // (according to the checks done prior the length should be ok)
+ while (nColIndex1 < nTableCols && aMap[i][nColIndex1] != '\0'
+ && aMap[i + nRowSubLen-1][nColIndex1] != '\0')
+ {
+ ++nColIndex1;
+ ++nColSubLen;
+ }
+ OUString aStartCell( sw_GetCellName( k, i ) );
+ OUString aEndCell( sw_GetCellName( k + nColSubLen - 1, i + nRowSubLen - 1) );
+ aCurRange = aCellRangeBase + aStartCell + ":" + aEndCell;
+ if (!aCellRanges.isEmpty())
+ aCellRanges += ";";
+ aCellRanges += aCurRange;
+
+ // clear already found sub-range from map
+ for (sal_Int32 nRowIndex2 = 0; nRowIndex2 < nRowSubLen; ++nRowIndex2)
+ for (sal_Int32 nColumnIndex2 = 0; nColumnIndex2 < nColSubLen; ++nColumnIndex2)
+ aMap[i + nRowIndex2][k + nColumnIndex2] = '\0';
+ }
+ }
+ }
+ // to be nice to the user we now sort the cell ranges according to
+ // rows or columns depending on the direction used in the data source
+ uno::Sequence< OUString > aSortedRanges;
+ GetSubranges( aCellRanges, aSortedRanges, false /*sub ranges should already be normalized*/ );
+ SortSubranges( aSortedRanges, (nDtaSrcIsColumns == 1) );
+ OUString aSortedCellRanges;
+ for (const OUString& rSortedRange : std::as_const(aSortedRanges))
+ {
+ if (!aSortedCellRanges.isEmpty())
+ aSortedCellRanges += ";";
+ aSortedCellRanges += rSortedRange;
+ }
+
+ // build value for 'SequenceMapping'
+
+ uno::Sequence< sal_Int32 > aSortedMapping( aSequenceMapping );
+ auto [begin, end] = asNonConstRange(aSortedMapping);
+ std::sort(begin, end);
+ bool bNeedSequenceMapping = false;
+ for (sal_Int32 i = 0; i < aSequenceMapping.getLength(); ++i)
+ {
+ auto it = std::find( std::cbegin(aSortedMapping), std::cend(aSortedMapping),
+ aSequenceMapping[i] );
+ pSequenceMapping[i] = std::distance(std::cbegin(aSortedMapping), it);
+
+ if (i != std::as_const(aSequenceMapping)[i])
+ bNeedSequenceMapping = true;
+ }
+
+ // check if 'SequenceMapping' is actually not required...
+ // (don't write unnecessary properties to the XML file)
+ if (!bNeedSequenceMapping)
+ aSequenceMapping.realloc(0);
+
+ // build resulting properties
+
+ OSL_ENSURE(nLabelSeqLen >= 0 || nLabelSeqLen == -2 /*not used*/,
+ "unexpected value for 'nLabelSeqLen'" );
+ bool bFirstCellIsLabel = false; // default value if 'nLabelSeqLen' could not properly determined
+ if (nLabelSeqLen > 0) // == 0 means no label sequence in use
+ bFirstCellIsLabel = true;
+
+ OSL_ENSURE( !aSortedCellRanges.isEmpty(), "CellRangeRepresentation missing" );
+ const OUString aBrokenCellRangeForExport( GetBrokenCellRangeForExport( aSortedCellRanges ) );
+
+ aResult.realloc(5);
+ auto pResult = aResult.getArray();
+ sal_Int32 nProps = 0;
+ pResult[nProps ].Name = "FirstCellAsLabel";
+ pResult[nProps++].Value <<= bFirstCellIsLabel;
+ pResult[nProps ].Name = "CellRangeRepresentation";
+ pResult[nProps++].Value <<= aSortedCellRanges;
+ if (!aBrokenCellRangeForExport.isEmpty())
+ {
+ pResult[nProps ].Name = "BrokenCellRangeForExport";
+ pResult[nProps++].Value <<= aBrokenCellRangeForExport;
+ }
+ if (nDtaSrcIsColumns == 0 || nDtaSrcIsColumns == 1)
+ {
+ chart::ChartDataRowSource eDataRowSource = (nDtaSrcIsColumns == 1) ?
+ chart::ChartDataRowSource_COLUMNS : chart::ChartDataRowSource_ROWS;
+ pResult[nProps ].Name = "DataRowSource";
+ pResult[nProps++].Value <<= eDataRowSource;
+
+ if (aSequenceMapping.hasElements())
+ {
+ pResult[nProps ].Name = "SequenceMapping";
+ pResult[nProps++].Value <<= aSequenceMapping;
+ }
+ }
+ aResult.realloc( nProps );
+
+ return aResult;
+}
+
+uno::Reference< chart2::data::XDataSequence > SwChartDataProvider::Impl_createDataSequenceByRangeRepresentation(
+ std::u16string_view rRangeRepresentation, bool bTestOnly )
+{
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ SwFrameFormat *pTableFormat = nullptr; // pointer to table format
+ std::shared_ptr<SwUnoCursor> pUnoCursor; // pointer to new created cursor spanning the cell range
+ GetFormatAndCreateCursorFromRangeRep( m_pDoc, rRangeRepresentation,
+ &pTableFormat, pUnoCursor );
+ if (!pTableFormat || !pUnoCursor)
+ throw lang::IllegalArgumentException();
+
+ // check that cursors point and mark are in a single row or column.
+ OUString aCellRange( GetCellRangeName( *pTableFormat, *pUnoCursor ) );
+ SwRangeDescriptor aDesc;
+ FillRangeDescriptor( aDesc, aCellRange );
+ if (aDesc.nTop != aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
+ throw lang::IllegalArgumentException();
+
+ OSL_ENSURE( pTableFormat && pUnoCursor, "table format or cursor missing" );
+ uno::Reference< chart2::data::XDataSequence > xDataSeq;
+ if (!bTestOnly)
+ xDataSeq = new SwChartDataSequence( *this, *pTableFormat, pUnoCursor );
+
+ return xDataSeq;
+}
+
+sal_Bool SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentationPossible(
+ const OUString& rRangeRepresentation )
+{
+ SolarMutexGuard aGuard;
+
+ bool bPossible = true;
+ try
+ {
+ Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation, true );
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ bPossible = false;
+ }
+
+ return bPossible;
+}
+
+uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartDataProvider::createDataSequenceByRangeRepresentation(
+ const OUString& rRangeRepresentation )
+{
+ SolarMutexGuard aGuard;
+ return Impl_createDataSequenceByRangeRepresentation( rRangeRepresentation );
+}
+
+uno::Reference< sheet::XRangeSelection > SAL_CALL SwChartDataProvider::getRangeSelection( )
+{
+ // note: it is no error to return nothing here
+ return uno::Reference< sheet::XRangeSelection >();
+}
+
+uno::Reference<css::chart2::data::XDataSequence> SAL_CALL
+ SwChartDataProvider::createDataSequenceByValueArray(
+ const OUString& /*aRole*/, const OUString& /*aRangeRepresentation*/,
+ const OUString& /*aRoleQualifier*/ )
+{
+ return uno::Reference<css::chart2::data::XDataSequence>();
+}
+
+void SAL_CALL SwChartDataProvider::dispose( )
+{
+ bool bMustDispose( false );
+ {
+ std::unique_lock aGuard( GetChartMutex() );
+ bMustDispose = !m_bDisposed;
+ if (!m_bDisposed)
+ m_bDisposed = true;
+ }
+ if (!bMustDispose)
+ return;
+
+ // dispose all data-sequences
+ for (const auto& rEntry : m_aDataSequences)
+ {
+ DisposeAllDataSequences( rEntry.first );
+ }
+ // release all references to data-sequences
+ m_aDataSequences.clear();
+
+ // require listeners to release references to this object
+ lang::EventObject aEvtObj( static_cast< chart2::data::XDataProvider * >(this) );
+ std::unique_lock aGuard( GetChartMutex() );
+ m_aEventListeners.disposeAndClear( aGuard, aEvtObj );
+}
+
+void SAL_CALL SwChartDataProvider::addEventListener(
+ const uno::Reference< lang::XEventListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aEventListeners.addInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartDataProvider::removeEventListener(
+ const uno::Reference< lang::XEventListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aEventListeners.removeInterface( aGuard, rxListener );
+}
+
+OUString SAL_CALL SwChartDataProvider::getImplementationName( )
+{
+ return "SwChartDataProvider";
+}
+
+sal_Bool SAL_CALL SwChartDataProvider::supportsService(const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwChartDataProvider::getSupportedServiceNames( )
+{
+ return { "com.sun.star.chart2.data.DataProvider"};
+}
+
+void SwChartDataProvider::AddDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > const &rxDataSequence )
+{
+ m_aDataSequences[ &rTable ].insert( rxDataSequence );
+}
+
+void SwChartDataProvider::RemoveDataSequence( const SwTable &rTable, uno::Reference< chart2::data::XDataSequence > const &rxDataSequence )
+{
+ m_aDataSequences[ &rTable ].erase( rxDataSequence );
+}
+
+void SwChartDataProvider::InvalidateTable( const SwTable *pTable, bool bImmediate )
+{
+ OSL_ENSURE( pTable, "table pointer is NULL" );
+ if (!pTable)
+ return;
+
+ if (!m_bDisposed)
+ pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
+
+ const Set_DataSequenceRef_t &rSet = m_aDataSequences[ pTable ];
+ for (const auto& rItem : rSet)
+ {
+ uno::Reference< chart2::data::XDataSequence > xTemp(rItem); // temporary needed for g++ 3.3.5
+ uno::Reference< util::XModifiable > xRef( xTemp, uno::UNO_QUERY );
+ if (xRef.is())
+ {
+ // mark the sequence as 'dirty' and notify listeners
+ xRef->setModified( true );
+ }
+ }
+
+ // tdf#122995 added Immediate-mode to allow non-timer-delayed Chart invalidation
+ if (bImmediate && !m_bDisposed)
+ pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().Disconnect();
+}
+
+void SwChartDataProvider::DeleteBox( const SwTable *pTable, const SwTableBox &rBox )
+{
+ OSL_ENSURE( pTable, "table pointer is NULL" );
+ if (!pTable)
+ return;
+
+ if (!m_bDisposed)
+ pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
+
+ Set_DataSequenceRef_t &rSet = m_aDataSequences[ pTable ];
+
+ // iterate over all data-sequences for that table...
+ Set_DataSequenceRef_t::iterator aIt( rSet.begin() );
+ Set_DataSequenceRef_t::iterator aEndIt( rSet.end() );
+ Set_DataSequenceRef_t::iterator aDelIt; // iterator used for deletion when appropriate
+ while (aIt != aEndIt)
+ {
+ SwChartDataSequence *pDataSeq = nullptr;
+ bool bNowEmpty = false;
+ bool bSeqDisposed = false;
+
+ // check if weak reference is still valid...
+ uno::Reference< chart2::data::XDataSequence > xTemp(*aIt);
+ if (xTemp.is())
+ {
+ // then delete that table box (check if implementation cursor needs to be adjusted)
+ pDataSeq = static_cast< SwChartDataSequence * >( xTemp.get() );
+ if (pDataSeq)
+ {
+ try
+ {
+ bNowEmpty = pDataSeq->DeleteBox( rBox );
+ }
+ catch (const lang::DisposedException&)
+ {
+ bNowEmpty = true;
+ bSeqDisposed = true;
+ }
+
+ if (bNowEmpty)
+ aDelIt = aIt;
+ }
+ }
+ ++aIt;
+
+ if (bNowEmpty)
+ {
+ rSet.erase( aDelIt );
+ if (pDataSeq && !bSeqDisposed)
+ pDataSeq->dispose(); // the current way to tell chart that sth. got removed
+ }
+ }
+}
+
+void SwChartDataProvider::DisposeAllDataSequences( const SwTable *pTable )
+{
+ OSL_ENSURE( pTable, "table pointer is NULL" );
+ if (!pTable)
+ return;
+
+ if (!m_bDisposed)
+ pTable->GetFrameFormat()->GetDoc()->getIDocumentChartDataProviderAccess().GetChartControllerHelper().StartOrContinueLocking();
+
+ //! make a copy of the STL container!
+ //! This is necessary since calling 'dispose' will implicitly remove an element
+ //! of the original container, and thus any iterator in the original container
+ //! would become invalid.
+ const Set_DataSequenceRef_t aSet( m_aDataSequences[ pTable ] );
+
+ for (const auto& rItem : aSet)
+ {
+ uno::Reference< chart2::data::XDataSequence > xTemp(rItem); // temporary needed for g++ 3.3.5
+ uno::Reference< lang::XComponent > xRef( xTemp, uno::UNO_QUERY );
+ if (xRef.is())
+ {
+ xRef->dispose();
+ }
+ }
+}
+
+/**
+ * SwChartDataProvider::AddRowCols tries to notify charts of added columns
+ * or rows and extends the value sequence respectively (if possible).
+ * If those can be added to the end of existing value data-sequences those
+ * sequences get modified accordingly and will send a modification
+ * notification (calling 'setModified
+ *
+ * Since this function is a work-around for non existent Writer core functionality
+ * (no arbitrary multi-selection in tables that can be used to define a
+ * data-sequence) this function will be somewhat unreliable.
+ * For example we will only try to adapt value sequences. For this we assume
+ * that a sequence of length 1 is a label sequence and those with length >= 2
+ * we presume to be value sequences. Also new cells can only be added in the
+ * direction the value sequence is already pointing (rows / cols) and at the
+ * start or end of the values data-sequence.
+ * Nothing needs to be done if the new cells are in between the table cursors
+ * point and mark since data-sequence are considered to consist of all cells
+ * between those.
+ * New rows/cols need to be added already to the table before calling
+ * this function.
+ */
+void SwChartDataProvider::AddRowCols(
+ const SwTable &rTable,
+ const SwSelBoxes& rBoxes,
+ sal_uInt16 nLines, bool bBehind )
+{
+ if (rTable.IsTableComplex())
+ return;
+
+ const size_t nBoxes = rBoxes.size();
+ if (nBoxes < 1 || nLines < 1)
+ return;
+
+ SwTableBox* pFirstBox = rBoxes[0];
+ SwTableBox* pLastBox = rBoxes.back();
+
+ if (!(pFirstBox && pLastBox))
+ return;
+
+ sal_Int32 nFirstCol = -1, nFirstRow = -1, nLastCol = -1, nLastRow = -1;
+ SwXTextTable::GetCellPosition( pFirstBox->GetName(), nFirstCol, nFirstRow );
+ SwXTextTable::GetCellPosition( pLastBox->GetName(), nLastCol, nLastRow );
+
+ bool bAddCols = false; // default; also to be used if nBoxes == 1 :-/
+ if (nFirstCol == nLastCol && nFirstRow != nLastRow)
+ bAddCols = true;
+ if (nFirstCol != nLastCol && nFirstRow != nLastRow)
+ return;
+
+ //get range of indices in col/rows for new cells
+ sal_Int32 nFirstNewCol = nFirstCol;
+ sal_Int32 nFirstNewRow = bBehind ? nFirstRow + 1 : nFirstRow - nLines;
+ if (bAddCols)
+ {
+ OSL_ENSURE( nFirstCol == nLastCol, "column indices seem broken" );
+ nFirstNewCol = bBehind ? nFirstCol + 1 : nFirstCol - nLines;
+ nFirstNewRow = nFirstRow;
+ }
+
+ // iterate over all data-sequences for the table
+ const Set_DataSequenceRef_t &rSet = m_aDataSequences[ &rTable ];
+ for (const auto& rItem : rSet)
+ {
+ uno::Reference< chart2::data::XDataSequence > xTemp(rItem); // temporary needed for g++ 3.3.5
+ uno::Reference< chart2::data::XTextualDataSequence > xRef( xTemp, uno::UNO_QUERY );
+ if (xRef.is())
+ {
+ const sal_Int32 nLen = xRef->getTextualData().getLength();
+ if (nLen > 1) // value data-sequence ?
+ {
+ auto pDataSeq = comphelper::getFromUnoTunnel<SwChartDataSequence>(xRef);
+ if (pDataSeq)
+ {
+ SwRangeDescriptor aDesc;
+ pDataSeq->FillRangeDesc( aDesc );
+
+ chart::ChartDataRowSource eDRSource = chart::ChartDataRowSource_COLUMNS;
+ if (aDesc.nTop == aDesc.nBottom && aDesc.nLeft != aDesc.nRight)
+ eDRSource = chart::ChartDataRowSource_ROWS;
+
+ if (!bAddCols && eDRSource == chart::ChartDataRowSource_COLUMNS)
+ {
+ // add rows: extend affected columns by newly added row cells
+ pDataSeq->ExtendTo( true, nFirstNewRow, nLines );
+ }
+ else if (bAddCols && eDRSource == chart::ChartDataRowSource_ROWS)
+ {
+ // add cols: extend affected rows by newly added column cells
+ pDataSeq->ExtendTo( false, nFirstNewCol, nLines );
+ }
+ }
+ }
+ }
+ }
+}
+
+// XRangeXMLConversion
+OUString SAL_CALL SwChartDataProvider::convertRangeToXML( const OUString& rRangeRepresentation )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (rRangeRepresentation.isEmpty())
+ return OUString();
+
+ OUStringBuffer aRes;
+
+ // multiple ranges are delimited by a ';' like in
+ // "Table1.A1:A4;Table1.C2:C5" the same table must be used in all ranges!
+ SwTable* pFirstFoundTable = nullptr; // to check that only one table will be used
+ sal_Int32 nPos = 0;
+ do {
+ const OUString aRange( rRangeRepresentation.getToken(0, ';', nPos) );
+ SwFrameFormat *pTableFormat = nullptr; // pointer to table format
+ std::shared_ptr<SwUnoCursor> pCursor;
+ GetFormatAndCreateCursorFromRangeRep( m_pDoc, aRange, &pTableFormat, pCursor );
+ if (!pTableFormat)
+ throw lang::IllegalArgumentException();
+ SwTable* pTable = SwTable::FindTable( pTableFormat );
+ if (pTable->IsTableComplex())
+ throw uno::RuntimeException("Table too complex.");
+
+ // check that there is only one table used in all ranges
+ if (!pFirstFoundTable)
+ pFirstFoundTable = pTable;
+ if (pTable != pFirstFoundTable)
+ throw lang::IllegalArgumentException();
+
+ OUString aTableName;
+ OUString aStartCell;
+ OUString aEndCell;
+ if (!GetTableAndCellsFromRangeRep( aRange, aTableName, aStartCell, aEndCell ))
+ throw lang::IllegalArgumentException();
+
+ sal_Int32 nCol, nRow;
+ SwXTextTable::GetCellPosition( aStartCell, nCol, nRow );
+ if (nCol < 0 || nRow < 0)
+ throw uno::RuntimeException("Cell not found.");
+
+ //!! following objects/functions are implemented in XMLRangeHelper.?xx
+ //!! which is a copy of the respective file from chart2 !!
+ XMLRangeHelper::CellRange aCellRange;
+ aCellRange.aTableName = aTableName;
+ aCellRange.aUpperLeft.nColumn = nCol;
+ aCellRange.aUpperLeft.nRow = nRow;
+ aCellRange.aUpperLeft.bIsEmpty = false;
+ if (aStartCell != aEndCell && !aEndCell.isEmpty())
+ {
+ SwXTextTable::GetCellPosition( aEndCell, nCol, nRow );
+ if (nCol < 0 || nRow < 0)
+ throw uno::RuntimeException("Cell not found.");
+
+ aCellRange.aLowerRight.nColumn = nCol;
+ aCellRange.aLowerRight.nRow = nRow;
+ aCellRange.aLowerRight.bIsEmpty = false;
+ }
+ OUString aTmp( XMLRangeHelper::getXMLStringFromCellRange( aCellRange ) );
+ if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
+ aRes.append(" ");
+ aRes.append(aTmp);
+ }
+ while (nPos>0);
+
+ return aRes.makeStringAndClear();
+}
+
+OUString SAL_CALL SwChartDataProvider::convertRangeFromXML( const OUString& rXMLRange )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (rXMLRange.isEmpty())
+ return OUString();
+
+ OUStringBuffer aRes;
+
+ // multiple ranges are delimited by a ' ' like in
+ // "Table1.$A$1:.$A$4 Table1.$C$2:.$C$5" the same table must be used in all ranges!
+ OUString aFirstFoundTable; // to check that only one table will be used
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString aRange( rXMLRange.getToken(0, ' ', nPos) );
+
+ //!! following objects and function are implemented in XMLRangeHelper.?xx
+ //!! which is a copy of the respective file from chart2 !!
+ XMLRangeHelper::CellRange aCellRange( XMLRangeHelper::getCellRangeFromXMLString( aRange ));
+
+ // check that there is only one table used in all ranges
+ if (aFirstFoundTable.isEmpty())
+ aFirstFoundTable = aCellRange.aTableName;
+ if (aCellRange.aTableName != aFirstFoundTable)
+ throw lang::IllegalArgumentException();
+
+ OUString aTmp = aCellRange.aTableName + "." +
+ sw_GetCellName( aCellRange.aUpperLeft.nColumn,
+ aCellRange.aUpperLeft.nRow );
+ // does cell range consist of more than a single cell?
+ if (!aCellRange.aLowerRight.bIsEmpty)
+ {
+ aTmp += ":" + sw_GetCellName( aCellRange.aLowerRight.nColumn,
+ aCellRange.aLowerRight.nRow );
+ }
+
+ if (!aRes.isEmpty()) // in case of multiple ranges add delimiter
+ aRes.append(";");
+ aRes.append(aTmp);
+ }
+ while (nPos>0);
+
+ return aRes.makeStringAndClear();
+}
+
+SwChartDataSource::SwChartDataSource(
+ const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > &rLDS ) :
+ m_aLDS( rLDS )
+{
+}
+
+SwChartDataSource::~SwChartDataSource()
+{
+}
+
+uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > SAL_CALL SwChartDataSource::getDataSequences( )
+{
+ SolarMutexGuard aGuard;
+ return m_aLDS;
+}
+
+OUString SAL_CALL SwChartDataSource::getImplementationName( )
+{
+ return "SwChartDataSource";
+}
+
+sal_Bool SAL_CALL SwChartDataSource::supportsService(const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwChartDataSource::getSupportedServiceNames( )
+{
+ return { "com.sun.star.chart2.data.DataSource" };
+}
+
+SwChartDataSequence::SwChartDataSequence(
+ SwChartDataProvider& rProvider,
+ SwFrameFormat& rTableFormat,
+ const std::shared_ptr<SwUnoCursor>& pTableCursor ) :
+ m_pFormat(&rTableFormat),
+ m_aRowLabelText( SwResId( STR_CHART2_ROW_LABEL_TEXT ) ),
+ m_aColLabelText( SwResId( STR_CHART2_COL_LABEL_TEXT ) ),
+ m_xDataProvider( &rProvider ),
+ m_pTableCursor( pTableCursor ),
+ m_pPropSet( aSwMapProvider.GetPropertySet( PROPERTY_MAP_CHART2_DATA_SEQUENCE ) )
+{
+ StartListening(rTableFormat.GetNotifier());
+ m_bDisposed = false;
+
+ acquire();
+ try
+ {
+ const SwTable* pTable = SwTable::FindTable( &rTableFormat );
+ if (pTable)
+ {
+ uno::Reference< chart2::data::XDataSequence > xRef(this);
+ m_xDataProvider->AddDataSequence( *pTable, xRef );
+ m_xDataProvider->addEventListener( static_cast< lang::XEventListener * >(this) );
+ }
+ else {
+ OSL_FAIL( "table missing" );
+ }
+ }
+ catch (uno::RuntimeException &)
+ {
+ // TODO: shouldn't there be a call to release() here?
+ throw;
+ }
+ catch (uno::Exception &)
+ {
+ }
+ release();
+
+#if OSL_DEBUG_LEVEL > 0
+ // check if it can properly convert into a SwUnoTableCursor
+ // which is required for some functions
+ SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
+ OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
+#endif
+}
+
+SwChartDataSequence::SwChartDataSequence( const SwChartDataSequence &rObj ) :
+ SwChartDataSequenceBaseClass(rObj),
+ SvtListener(),
+ m_pFormat( rObj.m_pFormat ),
+ m_aRole( rObj.m_aRole ),
+ m_aRowLabelText( SwResId(STR_CHART2_ROW_LABEL_TEXT) ),
+ m_aColLabelText( SwResId(STR_CHART2_COL_LABEL_TEXT) ),
+ m_xDataProvider( rObj.m_xDataProvider ),
+ m_pTableCursor( rObj.m_pTableCursor ),
+ m_pPropSet( rObj.m_pPropSet )
+{
+ if(m_pFormat)
+ StartListening(m_pFormat->GetNotifier());
+ m_bDisposed = false;
+
+ acquire();
+ try
+ {
+ const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
+ if (pTable)
+ {
+ uno::Reference< chart2::data::XDataSequence > xRef(this);
+ m_xDataProvider->AddDataSequence( *pTable, xRef );
+ m_xDataProvider->addEventListener( static_cast< lang::XEventListener * >(this) );
+ }
+ else {
+ OSL_FAIL( "table missing" );
+ }
+ }
+ catch (uno::RuntimeException &)
+ {
+ // TODO: shouldn't there be a call to release() here?
+ throw;
+ }
+ catch (uno::Exception &)
+ {
+ }
+ release();
+
+#if OSL_DEBUG_LEVEL > 0
+ // check if it can properly convert into a SwUnoTableCursor
+ // which is required for some functions
+ SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
+ OSL_ENSURE(pUnoTableCursor, "SwChartDataSequence: cursor not SwUnoTableCursor");
+#endif
+}
+
+SwChartDataSequence::~SwChartDataSequence()
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwChartDataSequence::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwChartDataSequenceUnoTunnelId;
+ return theSwChartDataSequenceUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwChartDataSequence::getSomething( const uno::Sequence< sal_Int8 > &rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+
+OUString SAL_CALL SwChartDataSequence::getSourceRangeRepresentation( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ OUString aRes;
+ SwFrameFormat* pTableFormat = GetFrameFormat();
+ if (pTableFormat)
+ {
+ const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
+ OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
+ aRes = pTableFormat->GetName() + "." + aCellRange;
+ }
+ return aRes;
+}
+
+uno::Sequence< OUString > SAL_CALL SwChartDataSequence::generateLabel(
+ chart2::data::LabelOrigin eLabelOrigin )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ uno::Sequence< OUString > aLabels;
+
+ {
+ SwRangeDescriptor aDesc;
+ bool bOk = false;
+ SwFrameFormat* pTableFormat = GetFrameFormat();
+ if (!pTableFormat)
+ throw uno::RuntimeException("No table format found.");
+ SwTable* pTable = SwTable::FindTable( pTableFormat );
+ if (!pTable)
+ throw uno::RuntimeException("No table found.");
+ if (pTable->IsTableComplex())
+ throw uno::RuntimeException("Table too complex.");
+
+ const OUString aCellRange( GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
+ OSL_ENSURE( !aCellRange.isEmpty(), "failed to get cell range" );
+ bOk = FillRangeDescriptor( aDesc, aCellRange );
+ OSL_ENSURE( bOk, "failed to get SwRangeDescriptor" );
+
+ if (bOk)
+ {
+ aDesc.Normalize();
+ sal_Int32 nColSpan = aDesc.nRight - aDesc.nLeft + 1;
+ sal_Int32 nRowSpan = aDesc.nBottom - aDesc.nTop + 1;
+ OSL_ENSURE( nColSpan == 1 || nRowSpan == 1,
+ "unexpected range of selected cells" );
+
+ OUString aText; // label text to be returned
+ bool bReturnEmptyText = false;
+ bool bUseCol = true;
+ if (eLabelOrigin == chart2::data::LabelOrigin_COLUMN)
+ bUseCol = true;
+ else if (eLabelOrigin == chart2::data::LabelOrigin_ROW)
+ bUseCol = false;
+ else if (eLabelOrigin == chart2::data::LabelOrigin_SHORT_SIDE)
+ {
+ bUseCol = nColSpan < nRowSpan;
+ bReturnEmptyText = nColSpan == nRowSpan;
+ }
+ else if (eLabelOrigin == chart2::data::LabelOrigin_LONG_SIDE)
+ {
+ bUseCol = nColSpan > nRowSpan;
+ bReturnEmptyText = nColSpan == nRowSpan;
+ }
+ else {
+ OSL_FAIL( "unexpected case" );
+ }
+
+ // build label sequence
+
+ sal_Int32 nSeqLen = bUseCol ? nColSpan : nRowSpan;
+ aLabels.realloc( nSeqLen );
+ OUString *pLabels = aLabels.getArray();
+ for (sal_Int32 i = 0; i < nSeqLen; ++i)
+ {
+ if (!bReturnEmptyText)
+ {
+ aText = bUseCol ? m_aColLabelText : m_aRowLabelText;
+ sal_Int32 nCol = aDesc.nLeft;
+ sal_Int32 nRow = aDesc.nTop;
+ if (bUseCol)
+ nCol = nCol + i;
+ else
+ nRow = nRow + i;
+ OUString aCellName( sw_GetCellName( nCol, nRow ) );
+
+ sal_Int32 nLen = aCellName.getLength();
+ if (nLen)
+ {
+ const sal_Unicode *pBuf = aCellName.getStr();
+ const sal_Unicode *pEnd = pBuf + nLen;
+ while (pBuf < pEnd && ('0' > *pBuf || *pBuf > '9'))
+ ++pBuf;
+ // start of number found?
+ if (pBuf < pEnd && ('0' <= *pBuf && *pBuf <= '9'))
+ {
+ OUString aRplc;
+ std::u16string_view aNew;
+ if (bUseCol)
+ {
+ aRplc = "%COLUMNLETTER";
+ aNew = aCellName.subView(0, pBuf - aCellName.getStr());
+ }
+ else
+ {
+ aRplc = "%ROWNUMBER";
+ aNew = std::u16string_view(pBuf, (aCellName.getStr() + nLen) - pBuf);
+ }
+ aText = aText.replaceFirst( aRplc, aNew );
+ }
+ }
+ }
+ pLabels[i] = aText;
+ }
+ }
+ }
+
+ return aLabels;
+}
+
+::sal_Int32 SAL_CALL SwChartDataSequence::getNumberFormatKeyByIndex(
+ ::sal_Int32 /*nIndex*/ )
+{
+ return 0;
+}
+
+std::vector< css::uno::Reference< css::table::XCell > > SwChartDataSequence::GetCells()
+{
+ if (m_bDisposed)
+ throw lang::DisposedException();
+ auto pTableFormat(GetFrameFormat());
+ if(!pTableFormat)
+ return std::vector< css::uno::Reference< css::table::XCell > >();
+ auto pTable(SwTable::FindTable(pTableFormat));
+ if(pTable->IsTableComplex())
+ return std::vector< css::uno::Reference< css::table::XCell > >();
+ SwRangeDescriptor aDesc;
+ if(!FillRangeDescriptor(aDesc, GetCellRangeName(*pTableFormat, *m_pTableCursor)))
+ return std::vector< css::uno::Reference< css::table::XCell > >();
+ return SwXCellRange::CreateXCellRange(m_pTableCursor, *pTableFormat, aDesc)->GetCells();
+}
+
+uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getTextualData()
+{
+ SolarMutexGuard aGuard;
+ auto vCells(GetCells());
+ uno::Sequence< OUString > vTextData(vCells.size());
+ std::transform(vCells.begin(),
+ vCells.end(),
+ vTextData.getArray(),
+ [] (decltype(vCells)::value_type& xCell)
+ { return static_cast<SwXCell*>(xCell.get())->getString(); });
+ return vTextData;
+}
+
+uno::Sequence< uno::Any > SAL_CALL SwChartDataSequence::getData()
+{
+ SolarMutexGuard aGuard;
+ auto vCells(GetCells());
+ uno::Sequence< uno::Any > vAnyData(vCells.size());
+ std::transform(vCells.begin(),
+ vCells.end(),
+ vAnyData.getArray(),
+ [] (decltype(vCells)::value_type& xCell)
+ { return static_cast<SwXCell*>(xCell.get())->GetAny(); });
+ return vAnyData;
+}
+
+uno::Sequence< double > SAL_CALL SwChartDataSequence::getNumericalData()
+{
+ SolarMutexGuard aGuard;
+ auto vCells(GetCells());
+ uno::Sequence< double > vNumData(vCells.size());
+ std::transform(vCells.begin(),
+ vCells.end(),
+ vNumData.getArray(),
+ [] (decltype(vCells)::value_type& xCell)
+ { return static_cast<SwXCell*>(xCell.get())->GetForcedNumericalValue(); });
+ return vNumData;
+}
+
+uno::Reference< util::XCloneable > SAL_CALL SwChartDataSequence::createClone( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+ return new SwChartDataSequence( *this );
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL SwChartDataSequence::getPropertySetInfo( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ static uno::Reference< beans::XPropertySetInfo > xRes = m_pPropSet->getPropertySetInfo();
+ return xRes;
+}
+
+void SAL_CALL SwChartDataSequence::setPropertyValue(
+ const OUString& rPropertyName,
+ const uno::Any& rValue )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (rPropertyName != UNO_NAME_ROLE)
+ throw beans::UnknownPropertyException(rPropertyName);
+
+ if ( !(rValue >>= m_aRole) )
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL SwChartDataSequence::getPropertyValue(
+ const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (!(rPropertyName == UNO_NAME_ROLE))
+ throw beans::UnknownPropertyException(rPropertyName);
+
+ return uno::Any(m_aRole);
+}
+
+void SAL_CALL SwChartDataSequence::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL( "not implemented" );
+}
+
+void SAL_CALL SwChartDataSequence::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL( "not implemented" );
+}
+
+void SAL_CALL SwChartDataSequence::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL( "not implemented" );
+}
+
+void SAL_CALL SwChartDataSequence::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL( "not implemented" );
+}
+
+OUString SAL_CALL SwChartDataSequence::getImplementationName( )
+{
+ return "SwChartDataSequence";
+}
+
+sal_Bool SAL_CALL SwChartDataSequence::supportsService(const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwChartDataSequence::getSupportedServiceNames( )
+{
+ return { "com.sun.star.chart2.data.DataSequence" };
+}
+
+void SwChartDataSequence::Notify( const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pFormat = nullptr;
+ if(!m_pFormat || !m_pTableCursor)
+ {
+ m_pFormat = nullptr;
+ m_pTableCursor.reset(nullptr);
+ dispose();
+ }
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ setModified( true );
+ }
+}
+
+sal_Bool SAL_CALL SwChartDataSequence::isModified( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ return true;
+}
+
+void SAL_CALL SwChartDataSequence::setModified(
+ sal_Bool bModified )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (bModified)
+ LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
+}
+
+void SAL_CALL SwChartDataSequence::addModifyListener(
+ const uno::Reference< util::XModifyListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aModifyListeners.addInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartDataSequence::removeModifyListener(
+ const uno::Reference< util::XModifyListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aModifyListeners.removeInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartDataSequence::disposing( const lang::EventObject& rSource )
+{
+ if (m_bDisposed)
+ throw lang::DisposedException();
+ if (rSource.Source == static_cast<cppu::OWeakObject*>(m_xDataProvider.get()))
+ {
+ m_xDataProvider.clear();
+ }
+}
+
+void SAL_CALL SwChartDataSequence::dispose( )
+{
+ bool bMustDispose( false );
+ {
+ std::unique_lock aGuard( GetChartMutex() );
+ bMustDispose = !m_bDisposed;
+ if (!m_bDisposed)
+ m_bDisposed = true;
+ }
+ if (!bMustDispose)
+ return;
+
+ m_bDisposed = true;
+ if (m_xDataProvider.is())
+ {
+ const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
+ if (pTable)
+ {
+ uno::Reference< chart2::data::XDataSequence > xRef(this);
+ m_xDataProvider->RemoveDataSequence( *pTable, xRef );
+ }
+ else {
+ OSL_FAIL( "table missing" );
+ }
+
+ //#i119653# The bug is crashed for an exception thrown by
+ //SwCharDataSequence::setModified() because
+ //the SwCharDataSequence object has been disposed.
+
+ //Actually, the former design of SwClient will disconnect itself
+ //from the notification list in its destructor.
+
+ //But the SwCharDataSequence won't be destructed but disposed in code
+ //(the data member SwChartDataSequence::bDisposed will be set to
+ //TRUE), the relationship between client and modification is not
+ //released.
+
+ //So any notification from modify object will lead to said
+ //exception threw out. Recorrect the logic of code in
+ //SwChartDataSequence::Dispose(), release the relationship
+ //here...
+ if (m_pFormat && m_pFormat->HasWriterListeners())
+ {
+ EndListeningAll();
+ m_pFormat = nullptr;
+ m_pTableCursor.reset(nullptr);
+ }
+ }
+
+ // require listeners to release references to this object
+ lang::EventObject aEvtObj( static_cast< chart2::data::XDataSequence * >(this) );
+ std::unique_lock aGuard( GetChartMutex() );
+ m_aModifyListeners.disposeAndClear( aGuard, aEvtObj );
+ m_aEvtListeners.disposeAndClear( aGuard, aEvtObj );
+}
+
+void SAL_CALL SwChartDataSequence::addEventListener(
+ const uno::Reference< lang::XEventListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aEvtListeners.addInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartDataSequence::removeEventListener(
+ const uno::Reference< lang::XEventListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aEvtListeners.removeInterface( aGuard, rxListener );
+}
+
+bool SwChartDataSequence::DeleteBox( const SwTableBox &rBox )
+{
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ // to be set if the last box of the data-sequence was removed here
+ bool bNowEmpty = false;
+
+ // if the implementation cursor gets affected (i.e. the box where it is located
+ // in gets removed) we need to move it before that... (otherwise it does not need to change)
+
+ const SwStartNode* pPointStartNode = m_pTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ const SwStartNode* pMarkStartNode = m_pTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
+
+ if (!m_pTableCursor->HasMark() || (pPointStartNode == rBox.GetSttNd() && pMarkStartNode == rBox.GetSttNd()))
+ {
+ bNowEmpty = true;
+ }
+ else if (pPointStartNode == rBox.GetSttNd() || pMarkStartNode == rBox.GetSttNd())
+ {
+ sal_Int32 nPointRow = -1, nPointCol = -1;
+ sal_Int32 nMarkRow = -1, nMarkCol = -1;
+ const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
+ OUString aPointCellName( pTable->GetTableBox( pPointStartNode->GetIndex() )->GetName() );
+ OUString aMarkCellName( pTable->GetTableBox( pMarkStartNode->GetIndex() )->GetName() );
+
+ SwXTextTable::GetCellPosition( aPointCellName, nPointCol, nPointRow );
+ SwXTextTable::GetCellPosition( aMarkCellName, nMarkCol, nMarkRow );
+ OSL_ENSURE( nPointRow >= 0 && nPointCol >= 0, "invalid row and col" );
+ OSL_ENSURE( nMarkRow >= 0 && nMarkCol >= 0, "invalid row and col" );
+
+ // move vertical or horizontal?
+ OSL_ENSURE( nPointRow == nMarkRow || nPointCol == nMarkCol,
+ "row/col indices not matching" );
+ OSL_ENSURE( nPointRow != nMarkRow || nPointCol != nMarkCol,
+ "point and mark are identical" );
+ bool bMoveVertical = (nPointCol == nMarkCol);
+ bool bMoveHorizontal = (nPointRow == nMarkRow);
+
+ // get movement direction
+ bool bMoveLeft = false; // move left or right?
+ bool bMoveUp = false; // move up or down?
+ if (bMoveVertical)
+ {
+ if (pPointStartNode == rBox.GetSttNd()) // move point?
+ bMoveUp = nPointRow > nMarkRow;
+ else // move mark
+ bMoveUp = nMarkRow > nPointRow;
+ }
+ else if (bMoveHorizontal)
+ {
+ if (pPointStartNode == rBox.GetSttNd()) // move point?
+ bMoveLeft = nPointCol > nMarkCol;
+ else // move mark
+ bMoveLeft = nMarkCol > nPointCol;
+ }
+ else {
+ OSL_FAIL( "neither vertical nor horizontal movement" );
+ }
+
+ // get new box (position) to use...
+ sal_Int32 nRow = (pPointStartNode == rBox.GetSttNd()) ? nPointRow : nMarkRow;
+ sal_Int32 nCol = (pPointStartNode == rBox.GetSttNd()) ? nPointCol : nMarkCol;
+ if (bMoveVertical)
+ nRow += bMoveUp ? -1 : +1;
+ if (bMoveHorizontal)
+ nCol += bMoveLeft ? -1 : +1;
+ const OUString aNewCellName = sw_GetCellName( nCol, nRow );
+ SwTableBox* pNewBox = const_cast<SwTableBox*>(pTable->GetTableBox( aNewCellName ));
+
+ if (pNewBox) // set new position (cell range) to use
+ {
+ // This is how you get the first content node of a row:
+ // First get a SwNodeIndex pointing to the node after SwStartNode of the box...
+ SwNodeIndex aIdx( *pNewBox->GetSttNd(), +1 );
+ // This can be a SwContentNode, but might also be a table or section node,
+ // therefore call GoNext
+ SwContentNode *pCNd = aIdx.GetNode().GetContentNode();
+ if (!pCNd)
+ pCNd = GetFrameFormat()->GetDoc()->GetNodes().GoNext( &aIdx );
+ // and then one can e.g. create a SwPosition:
+ SwPosition aNewPos( *pCNd ); // new position to be used with cursor
+
+ // if the mark is to be changed, make sure there is one
+ if (pMarkStartNode == rBox.GetSttNd() && !m_pTableCursor->HasMark())
+ m_pTableCursor->SetMark();
+
+ // set cursor to new position
+ SwPosition *pPos = (pPointStartNode == rBox.GetSttNd()) ?
+ m_pTableCursor->GetPoint() : m_pTableCursor->GetMark();
+ if (pPos)
+ {
+ pPos->nNode = aNewPos.nNode;
+ pPos->nContent = aNewPos.nContent;
+ }
+ else {
+ OSL_FAIL( "neither point nor mark available for change" );
+ }
+ }
+ else {
+ OSL_FAIL( "failed to get position" );
+ }
+ }
+
+ return bNowEmpty;
+}
+
+void SwChartDataSequence::FillRangeDesc( SwRangeDescriptor &rRangeDesc ) const
+{
+ SwFrameFormat* pTableFormat = GetFrameFormat();
+ if(pTableFormat)
+ {
+ SwTable* pTable = SwTable::FindTable( pTableFormat );
+ if(!pTable->IsTableComplex())
+ {
+ FillRangeDescriptor( rRangeDesc, GetCellRangeName( *pTableFormat, *m_pTableCursor ) );
+ }
+ }
+}
+
+/**
+ * Extends the data-sequence by new cells added at the end of the direction
+ * the data-sequence points to.
+ * If the cells are already within the range of the sequence nothing needs
+ * to be done.
+ * If the cells are beyond the end of the sequence (are not adjacent to the
+ * current last cell) nothing can be done. Only if the cells are adjacent to
+ * the last cell they can be added.
+ *
+ * @returns true if the data-sequence was changed.
+ * @param bExtendCols - specifies if columns or rows are to be extended
+ * @param nFirstNew - index of first new row/col to be included in data-sequence
+ * @param nLastNew - index of last new row/col to be included in data-sequence
+ */
+void SwChartDataSequence::ExtendTo( bool bExtendCol,
+ sal_Int32 nFirstNew, sal_Int32 nCount )
+{
+ SwUnoTableCursor* pUnoTableCursor = dynamic_cast<SwUnoTableCursor*>(&(*m_pTableCursor));
+ if (!pUnoTableCursor)
+ return;
+
+ const SwStartNode *pStartNd = nullptr;
+ const SwTableBox *pStartBox = nullptr;
+ const SwTableBox *pEndBox = nullptr;
+
+ const SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
+ OSL_ENSURE( !pTable->IsTableComplex(), "table too complex" );
+ if (nCount < 1 || nFirstNew < 0 || pTable->IsTableComplex())
+ return;
+
+ // get range descriptor (cell range) for current data-sequence
+
+ pStartNd = pUnoTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ pEndBox = pTable->GetTableBox( pStartNd->GetIndex() );
+ const OUString aEndBox( pEndBox->GetName() );
+
+ pStartNd = pUnoTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
+ pStartBox = pTable->GetTableBox( pStartNd->GetIndex() );
+ const OUString aStartBox( pStartBox->GetName() );
+
+ SwRangeDescriptor aDesc;
+ // note that cell range here takes the newly added rows/cols already into account
+ OUString sDescrip = aStartBox + ":" + aEndBox;
+ FillRangeDescriptor( aDesc, sDescrip );
+
+ bool bChanged = false;
+ OUString aNewStartCell;
+ OUString aNewEndCell;
+ if (bExtendCol && aDesc.nBottom + 1 == nFirstNew)
+ {
+ // new column cells adjacent to the bottom of the
+ // current data-sequence to be added...
+ OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
+ aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
+ aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom + nCount);
+ bChanged = true;
+ }
+ else if (bExtendCol && aDesc.nTop - nCount == nFirstNew)
+ {
+ // new column cells adjacent to the top of the
+ // current data-sequence to be added...
+ OSL_ENSURE( aDesc.nLeft == aDesc.nRight, "data-sequence is not a column" );
+ aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop - nCount);
+ aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
+ bChanged = true;
+ }
+ else if (!bExtendCol && aDesc.nRight + 1 == nFirstNew)
+ {
+ // new row cells adjacent to the right of the
+ // current data-sequence to be added...
+ OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
+ aNewStartCell = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
+ aNewEndCell = sw_GetCellName(aDesc.nRight + nCount, aDesc.nBottom);
+ bChanged = true;
+ }
+ else if (!bExtendCol && aDesc.nLeft - nCount == nFirstNew)
+ {
+ // new row cells adjacent to the left of the
+ // current data-sequence to be added...
+ OSL_ENSURE( aDesc.nTop == aDesc.nBottom, "data-sequence is not a row" );
+ aNewStartCell = sw_GetCellName(aDesc.nLeft - nCount, aDesc.nTop);
+ aNewEndCell = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
+ bChanged = true;
+ }
+
+ if (bChanged)
+ {
+ // move table cursor to new start and end of data-sequence
+ const SwTableBox *pNewStartBox = pTable->GetTableBox( aNewStartCell );
+ const SwTableBox *pNewEndBox = pTable->GetTableBox( aNewEndCell );
+ pUnoTableCursor->SetMark();
+ pUnoTableCursor->GetPoint()->nNode = *pNewEndBox->GetSttNd();
+ pUnoTableCursor->GetMark()->nNode = *pNewStartBox->GetSttNd();
+ pUnoTableCursor->Move( fnMoveForward, GoInNode );
+ pUnoTableCursor->MakeBoxSels();
+ }
+}
+
+SwChartLabeledDataSequence::SwChartLabeledDataSequence()
+{
+ m_bDisposed = false;
+}
+
+SwChartLabeledDataSequence::~SwChartLabeledDataSequence()
+{
+}
+
+uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getValues( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+ return m_xData;
+}
+
+void SwChartLabeledDataSequence::SetDataSequence(
+ uno::Reference< chart2::data::XDataSequence >& rxDest,
+ const uno::Reference< chart2::data::XDataSequence >& rxSource)
+{
+ uno::Reference< util::XModifyListener > xML(this);
+ uno::Reference< lang::XEventListener > xEL(this);
+
+ // stop listening to old data-sequence
+ uno::Reference< util::XModifyBroadcaster > xMB( rxDest, uno::UNO_QUERY );
+ if (xMB.is())
+ xMB->removeModifyListener( xML );
+ uno::Reference< lang::XComponent > xC( rxDest, uno::UNO_QUERY );
+ if (xC.is())
+ xC->removeEventListener( xEL );
+
+ rxDest = rxSource;
+
+ // start listening to new data-sequence
+ xC.set( rxDest, uno::UNO_QUERY );
+ if (xC.is())
+ xC->addEventListener( xEL );
+ xMB.set( rxDest, uno::UNO_QUERY );
+ if (xMB.is())
+ xMB->addModifyListener( xML );
+}
+
+void SAL_CALL SwChartLabeledDataSequence::setValues(
+ const uno::Reference< chart2::data::XDataSequence >& rxSequence )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (m_xData != rxSequence)
+ {
+ SetDataSequence( m_xData, rxSequence );
+ // inform listeners of changes
+ LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
+ }
+}
+
+uno::Reference< chart2::data::XDataSequence > SAL_CALL SwChartLabeledDataSequence::getLabel( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+ return m_xLabels;
+}
+
+void SAL_CALL SwChartLabeledDataSequence::setLabel(
+ const uno::Reference< chart2::data::XDataSequence >& rxSequence )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ if (m_xLabels != rxSequence)
+ {
+ SetDataSequence( m_xLabels, rxSequence );
+ // inform listeners of changes
+ LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
+ }
+}
+
+uno::Reference< util::XCloneable > SAL_CALL SwChartLabeledDataSequence::createClone( )
+{
+ SolarMutexGuard aGuard;
+ if (m_bDisposed)
+ throw lang::DisposedException();
+
+ uno::Reference< util::XCloneable > xDataCloneable( m_xData, uno::UNO_QUERY );
+ uno::Reference< util::XCloneable > xLabelsCloneable( m_xLabels, uno::UNO_QUERY );
+ rtl::Reference<SwChartLabeledDataSequence > pRes = new SwChartLabeledDataSequence();
+ if (xDataCloneable.is())
+ {
+ uno::Reference< chart2::data::XDataSequence > xDataClone( xDataCloneable->createClone(), uno::UNO_QUERY );
+ pRes->setValues( xDataClone );
+ }
+
+ if (xLabelsCloneable.is())
+ {
+ uno::Reference< chart2::data::XDataSequence > xLabelsClone( xLabelsCloneable->createClone(), uno::UNO_QUERY );
+ pRes->setLabel( xLabelsClone );
+ }
+ return pRes;
+}
+
+OUString SAL_CALL SwChartLabeledDataSequence::getImplementationName( )
+{
+ return "SwChartLabeledDataSequence";
+}
+
+sal_Bool SAL_CALL SwChartLabeledDataSequence::supportsService(
+ const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwChartLabeledDataSequence::getSupportedServiceNames( )
+{
+ return { "com.sun.star.chart2.data.LabeledDataSequence" };
+}
+
+void SAL_CALL SwChartLabeledDataSequence::disposing(
+ const lang::EventObject& rSource )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ uno::Reference< uno::XInterface > xRef( rSource.Source );
+ if (xRef == m_xData)
+ m_xData.clear();
+ if (xRef == m_xLabels)
+ m_xLabels.clear();
+ if (!m_xData.is() && !m_xLabels.is())
+ {
+ aGuard.unlock();
+ dispose();
+ }
+}
+
+void SAL_CALL SwChartLabeledDataSequence::modified(
+ const lang::EventObject& rEvent )
+{
+ if (rEvent.Source == m_xData || rEvent.Source == m_xLabels)
+ {
+ LaunchModifiedEvent( m_aModifyListeners, static_cast< XModifyBroadcaster * >(this) );
+ }
+}
+
+void SAL_CALL SwChartLabeledDataSequence::addModifyListener(
+ const uno::Reference< util::XModifyListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aModifyListeners.addInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartLabeledDataSequence::removeModifyListener(
+ const uno::Reference< util::XModifyListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aModifyListeners.removeInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartLabeledDataSequence::dispose( )
+{
+ bool bMustDispose( false );
+ {
+ std::unique_lock aGuard( GetChartMutex() );
+ bMustDispose = !m_bDisposed;
+ if (!m_bDisposed)
+ m_bDisposed = true;
+ }
+ if (bMustDispose)
+ {
+ m_bDisposed = true;
+
+ // require listeners to release references to this object
+ lang::EventObject aEvtObj( static_cast< chart2::data::XLabeledDataSequence * >(this) );
+ std::unique_lock aGuard( GetChartMutex() );
+ m_aModifyListeners.disposeAndClear( aGuard, aEvtObj );
+ m_aEventListeners.disposeAndClear( aGuard, aEvtObj );
+ }
+}
+
+void SAL_CALL SwChartLabeledDataSequence::addEventListener(
+ const uno::Reference< lang::XEventListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aEventListeners.addInterface( aGuard, rxListener );
+}
+
+void SAL_CALL SwChartLabeledDataSequence::removeEventListener(
+ const uno::Reference< lang::XEventListener >& rxListener )
+{
+ std::unique_lock aGuard( GetChartMutex() );
+ if (!m_bDisposed && rxListener.is())
+ m_aEventListeners.removeInterface( aGuard, rxListener );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unocoll.cxx b/sw/source/core/unocore/unocoll.cxx
new file mode 100644
index 000000000..fe2043e37
--- /dev/null
+++ b/sw/source/core/unocore/unocoll.cxx
@@ -0,0 +1,1947 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_features.h>
+
+#include <hintids.hxx>
+#include <doc.hxx>
+#include <IDocumentChartDataProviderAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <docary.hxx>
+#include <unocoll.hxx>
+#include <unosett.hxx>
+#include <section.hxx>
+#include <IMark.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <com/sun/star/text/XTextTable.hpp>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <svtools/unoimap.hxx>
+#include <svtools/unoevent.hxx>
+#include <svx/SvxXTextColumns.hxx>
+#include <unotbl.hxx>
+#include <unostyle.hxx>
+#include <unofield.hxx>
+#include <unoidx.hxx>
+#include <unoframe.hxx>
+#include <textboxhelper.hxx>
+#include <unofootnote.hxx>
+#include <unolinebreak.hxx>
+#include <vcl/svapp.hxx>
+#include <fmtcntnt.hxx>
+#include <authfld.hxx>
+#include <SwXTextDefaults.hxx>
+#include <unochart.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unosection.hxx>
+#include <unoparagraph.hxx>
+#include <unobookmark.hxx>
+#include <unorefmark.hxx>
+#include <unometa.hxx>
+#include <unocontentcontrol.hxx>
+#include <docsh.hxx>
+#include <hints.hxx>
+#include <frameformats.hxx>
+#include <com/sun/star/document/XCodeNameQuery.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/form/XFormsSupplier.hpp>
+#include <com/sun/star/script/ModuleInfo.hpp>
+#include <com/sun/star/script/ModuleType.hpp>
+#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <vbahelper/vbaaccesshelper.hxx>
+#include <basic/basmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/event.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+#if HAVE_FEATURE_SCRIPTING
+
+namespace {
+
+class SwVbaCodeNameProvider : public ::cppu::WeakImplHelper< document::XCodeNameQuery >
+{
+ SwDocShell* mpDocShell;
+ OUString msThisDocumentCodeName;
+public:
+ explicit SwVbaCodeNameProvider( SwDocShell* pDocShell ) : mpDocShell( pDocShell ) {}
+ // XCodeNameQuery
+
+ OUString SAL_CALL getCodeNameForContainer( const uno::Reference< uno::XInterface >& /*xIf*/ ) override
+ {
+ // #FIXME not implemented...
+ return OUString();
+ }
+
+ OUString SAL_CALL getCodeNameForObject( const uno::Reference< uno::XInterface >& xIf ) override
+ {
+ // Initialise the code name
+ if ( msThisDocumentCodeName.isEmpty() )
+ {
+ try
+ {
+ uno::Reference< beans::XPropertySet > xProps( mpDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XNameAccess > xLibContainer( xProps->getPropertyValue("BasicLibraries"), uno::UNO_QUERY_THROW );
+ OUString sProjectName( "Standard");
+ if ( !mpDocShell->GetBasicManager()->GetName().isEmpty() )
+ {
+ sProjectName = mpDocShell->GetBasicManager()->GetName();
+ }
+ uno::Reference< container::XNameAccess > xLib( xLibContainer->getByName( sProjectName ), uno::UNO_QUERY_THROW );
+ const uno::Sequence< OUString > sModuleNames = xLib->getElementNames();
+ uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
+
+ auto pModuleName = std::find_if(sModuleNames.begin(), sModuleNames.end(), [&xVBAModuleInfo](const OUString& rName) {
+ return xVBAModuleInfo->hasModuleInfo(rName)
+ && xVBAModuleInfo->getModuleInfo(rName).ModuleType == script::ModuleType::DOCUMENT; });
+ if (pModuleName != sModuleNames.end())
+ msThisDocumentCodeName = *pModuleName;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ OUString sCodeName;
+ if ( mpDocShell )
+ {
+ // need to find the page ( and index ) for this control
+ uno::Reference< drawing::XDrawPageSupplier > xSupplier( mpDocShell->GetModel(), uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xIndex( xSupplier->getDrawPage(), uno::UNO_QUERY_THROW );
+
+ try
+ {
+ uno::Reference< form::XFormsSupplier > xFormSupplier( xIndex, uno::UNO_QUERY_THROW );
+ uno::Reference< container::XIndexAccess > xFormIndex( xFormSupplier->getForms(), uno::UNO_QUERY_THROW );
+ // get the www-standard container
+ uno::Reference< container::XIndexAccess > xFormControls( xFormIndex->getByIndex(0), uno::UNO_QUERY_THROW );
+ sal_Int32 nCntrls = xFormControls->getCount();
+ for( sal_Int32 cIndex = 0; cIndex < nCntrls; ++cIndex )
+ {
+ uno::Reference< uno::XInterface > xControl( xFormControls->getByIndex( cIndex ), uno::UNO_QUERY_THROW );
+ bool bMatched = ( xControl == xIf );
+ if ( bMatched )
+ {
+ sCodeName = msThisDocumentCodeName;
+ break;
+ }
+ }
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ // #TODO Probably should throw here ( if !bMatched )
+ return sCodeName;
+ }
+};
+
+}
+
+typedef std::unordered_map< OUString, OUString > StringHashMap;
+
+namespace {
+
+class SwVbaProjectNameProvider : public ::cppu::WeakImplHelper< container::XNameContainer >
+{
+ StringHashMap mTemplateToProject;
+public:
+ SwVbaProjectNameProvider()
+ {
+ }
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ return ( mTemplateToProject.find( aName ) != mTemplateToProject.end() );
+ }
+ virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ if ( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+ return uno::Any( mTemplateToProject.find( aName )->second );
+ }
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ return comphelper::mapKeysToSequence( mTemplateToProject );
+ }
+
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+
+ OUString sProjectName;
+ aElement >>= sProjectName;
+ SAL_INFO("sw.uno", "Template cache inserting template name " << aName
+ << " with project " << sProjectName);
+ mTemplateToProject[ aName ] = sProjectName;
+ }
+
+ virtual void SAL_CALL removeByName( const OUString& Name ) override
+ {
+ if ( !hasByName( Name ) )
+ throw container::NoSuchElementException();
+ mTemplateToProject.erase( Name );
+ }
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override
+ {
+ if ( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+ insertByName( aName, aElement ); // insert will overwrite
+ }
+ // XElemenAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override
+ {
+ return ::cppu::UnoType<OUString>::get();
+ }
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+
+ return ( !mTemplateToProject.empty() );
+ }
+
+};
+
+class SwVbaObjectForCodeNameProvider : public ::cppu::WeakImplHelper< container::XNameAccess >
+{
+ SwDocShell* mpDocShell;
+public:
+ explicit SwVbaObjectForCodeNameProvider( SwDocShell* pDocShell ) : mpDocShell( pDocShell )
+ {
+ // #FIXME #TODO is the code name for ThisDocument read anywhere?
+ }
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override
+ {
+ // #FIXME #TODO we really need to be checking against the codename for
+ // ThisDocument
+ if ( aName == "ThisDocument" )
+ return true;
+ return false;
+ }
+
+ css::uno::Any SAL_CALL getByName( const OUString& aName ) override
+ {
+ if ( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+ uno::Sequence< uno::Any > aArgs{ uno::Any(uno::Reference< uno::XInterface >()),
+ uno::Any(mpDocShell->GetModel()) };
+ uno::Reference< uno::XInterface > xDocObj = ooo::vba::createVBAUnoAPIServiceWithArgs( mpDocShell, "ooo.vba.word.Document" , aArgs );
+ SAL_INFO("sw.uno",
+ "Creating Object ( ooo.vba.word.Document ) 0x" << xDocObj.get());
+ return uno::Any( xDocObj );
+ }
+ virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override
+ {
+ uno::Sequence< OUString > aNames;
+ return aNames;
+ }
+ // XElemenAccess
+ virtual css::uno::Type SAL_CALL getElementType( ) override { return uno::Type(); }
+ virtual sal_Bool SAL_CALL hasElements( ) override { return true; }
+
+};
+
+}
+
+#endif
+
+namespace {
+
+struct ProvNamesId_Type
+{
+ const char * pName;
+ SwServiceType nType;
+};
+
+}
+
+// note: this thing is indexed as an array, so do not insert/remove entries!
+const ProvNamesId_Type aProvNamesId[] =
+{
+ { "com.sun.star.text.TextTable", SwServiceType::TypeTextTable },
+ { "com.sun.star.text.TextFrame", SwServiceType::TypeTextFrame },
+ { "com.sun.star.text.GraphicObject", SwServiceType::TypeGraphic },
+ { "com.sun.star.text.TextEmbeddedObject", SwServiceType::TypeOLE },
+ { "com.sun.star.text.Bookmark", SwServiceType::TypeBookmark },
+ { "com.sun.star.text.Footnote", SwServiceType::TypeFootnote },
+ { "com.sun.star.text.Endnote", SwServiceType::TypeEndnote },
+ { "com.sun.star.text.DocumentIndexMark", SwServiceType::TypeIndexMark },
+ { "com.sun.star.text.DocumentIndex", SwServiceType::TypeIndex },
+ { "com.sun.star.text.ReferenceMark", SwServiceType::ReferenceMark },
+ { "com.sun.star.style.CharacterStyle", SwServiceType::StyleCharacter },
+ { "com.sun.star.style.ParagraphStyle", SwServiceType::StyleParagraph },
+ { "com.sun.star.style.FrameStyle", SwServiceType::StyleFrame },
+ { "com.sun.star.style.PageStyle", SwServiceType::StylePage },
+ { "com.sun.star.style.NumberingStyle", SwServiceType::StyleNumbering },
+ { "com.sun.star.text.ContentIndexMark", SwServiceType::ContentIndexMark },
+ { "com.sun.star.text.ContentIndex", SwServiceType::ContentIndex },
+ { "com.sun.star.text.UserIndexMark", SwServiceType::UserIndexMark },
+ { "com.sun.star.text.UserIndex", SwServiceType::UserIndex },
+ { "com.sun.star.text.TextSection", SwServiceType::TextSection },
+ { "com.sun.star.text.TextField.DateTime", SwServiceType::FieldTypeDateTime },
+ { "com.sun.star.text.TextField.User", SwServiceType::FieldTypeUser },
+ { "com.sun.star.text.TextField.SetExpression", SwServiceType::FieldTypeSetExp },
+ { "com.sun.star.text.TextField.GetExpression", SwServiceType::FieldTypeGetExp },
+ { "com.sun.star.text.TextField.FileName", SwServiceType::FieldTypeFileName },
+ { "com.sun.star.text.TextField.PageNumber", SwServiceType::FieldTypePageNum },
+ { "com.sun.star.text.TextField.Author", SwServiceType::FieldTypeAuthor },
+ { "com.sun.star.text.TextField.Chapter", SwServiceType::FieldTypeChapter },
+ { "", SwServiceType::FieldTypeDummy0 },
+ { "com.sun.star.text.TextField.GetReference", SwServiceType::FieldTypeGetReference },
+ { "com.sun.star.text.TextField.ConditionalText", SwServiceType::FieldTypeConditionedText },
+ { "com.sun.star.text.TextField.Annotation", SwServiceType::FieldTypeAnnotation },
+ { "com.sun.star.text.TextField.Input", SwServiceType::FieldTypeInput },
+ { "com.sun.star.text.TextField.Macro", SwServiceType::FieldTypeMacro },
+ { "com.sun.star.text.TextField.DDE", SwServiceType::FieldTypeDDE },
+ { "com.sun.star.text.TextField.HiddenParagraph", SwServiceType::FieldTypeHiddenPara },
+ { "" /*com.sun.star.text.TextField.DocumentInfo"*/, SwServiceType::FieldTypeDocInfo },
+ { "com.sun.star.text.TextField.TemplateName", SwServiceType::FieldTypeTemplateName },
+ { "com.sun.star.text.TextField.ExtendedUser", SwServiceType::FieldTypeUserExt },
+ { "com.sun.star.text.TextField.ReferencePageSet", SwServiceType::FieldTypeRefPageSet },
+ { "com.sun.star.text.TextField.ReferencePageGet", SwServiceType::FieldTypeRefPageGet },
+ { "com.sun.star.text.TextField.JumpEdit", SwServiceType::FieldTypeJumpEdit },
+ { "com.sun.star.text.TextField.Script", SwServiceType::FieldTypeScript },
+ { "com.sun.star.text.TextField.DatabaseNextSet", SwServiceType::FieldTypeDatabaseNextSet },
+ { "com.sun.star.text.TextField.DatabaseNumberOfSet", SwServiceType::FieldTypeDatabaseNumSet },
+ { "com.sun.star.text.TextField.DatabaseSetNumber", SwServiceType::FieldTypeDatabaseSetNum },
+ { "com.sun.star.text.TextField.Database", SwServiceType::FieldTypeDatabase },
+ { "com.sun.star.text.TextField.DatabaseName", SwServiceType::FieldTypeDatabaseName },
+ { "com.sun.star.text.TextField.TableFormula", SwServiceType::FieldTypeTableFormula },
+ { "com.sun.star.text.TextField.PageCount", SwServiceType::FieldTypePageCount },
+ { "com.sun.star.text.TextField.ParagraphCount", SwServiceType::FieldTypeParagraphCount },
+ { "com.sun.star.text.TextField.WordCount", SwServiceType::FieldTypeWordCount },
+ { "com.sun.star.text.TextField.CharacterCount", SwServiceType::FieldTypeCharacterCount },
+ { "com.sun.star.text.TextField.TableCount", SwServiceType::FieldTypeTableCount },
+ { "com.sun.star.text.TextField.GraphicObjectCount", SwServiceType::FieldTypeGraphicObjectCount },
+ { "com.sun.star.text.TextField.EmbeddedObjectCount", SwServiceType::FieldTypeEmbeddedObjectCount },
+ { "com.sun.star.text.TextField.DocInfo.ChangeAuthor", SwServiceType::FieldTypeDocInfoChangeAuthor },
+ { "com.sun.star.text.TextField.DocInfo.ChangeDateTime", SwServiceType::FieldTypeDocInfoChangeDateTime },
+ { "com.sun.star.text.TextField.DocInfo.EditTime", SwServiceType::FieldTypeDocInfoEditTime },
+ { "com.sun.star.text.TextField.DocInfo.Description", SwServiceType::FieldTypeDocInfoDescription },
+ { "com.sun.star.text.TextField.DocInfo.CreateAuthor", SwServiceType::FieldTypeDocInfoCreateAuthor },
+ { "com.sun.star.text.TextField.DocInfo.CreateDateTime", SwServiceType::FieldTypeDocInfoCreateDateTime },
+ { "", SwServiceType::FieldTypeDummy0 },
+ { "", SwServiceType::FieldTypeDummy1 },
+ { "", SwServiceType::FieldTypeDummy2 },
+ { "", SwServiceType::FieldTypeDummy3 },
+ { "com.sun.star.text.TextField.DocInfo.Custom", SwServiceType::FieldTypeDocInfoCustom },
+ { "com.sun.star.text.TextField.DocInfo.PrintAuthor", SwServiceType::FieldTypeDocInfoPrintAuthor },
+ { "com.sun.star.text.TextField.DocInfo.PrintDateTime", SwServiceType::FieldTypeDocInfoPrintDateTime },
+ { "com.sun.star.text.TextField.DocInfo.KeyWords", SwServiceType::FieldTypeDocInfoKeywords },
+ { "com.sun.star.text.TextField.DocInfo.Subject", SwServiceType::FieldTypeDocInfoSubject },
+ { "com.sun.star.text.TextField.DocInfo.Title", SwServiceType::FieldTypeDocInfoTitle },
+ { "com.sun.star.text.TextField.DocInfo.Revision", SwServiceType::FieldTypeDocInfoRevision },
+ { "com.sun.star.text.TextField.Bibliography", SwServiceType::FieldTypeBibliography },
+ { "com.sun.star.text.TextField.CombinedCharacters", SwServiceType::FieldTypeCombinedCharacters },
+ { "com.sun.star.text.TextField.DropDown", SwServiceType::FieldTypeDropdown },
+ { "com.sun.star.text.textfield.MetadataField", SwServiceType::FieldTypeMetafield },
+ { "", SwServiceType::FieldTypeDummy4 },
+ { "", SwServiceType::FieldTypeDummy5 },
+ { "", SwServiceType::FieldTypeDummy6 },
+ { "", SwServiceType::FieldTypeDummy7 },
+ { "com.sun.star.text.FieldMaster.User", SwServiceType::FieldMasterUser },
+ { "com.sun.star.text.FieldMaster.DDE", SwServiceType::FieldMasterDDE },
+ { "com.sun.star.text.FieldMaster.SetExpression", SwServiceType::FieldMasterSetExp },
+ { "com.sun.star.text.FieldMaster.Database", SwServiceType::FieldMasterDatabase },
+ { "com.sun.star.text.FieldMaster.Bibliography", SwServiceType::FieldMasterBibliography },
+ { "", SwServiceType::FieldMasterDummy2 },
+ { "", SwServiceType::FieldMasterDummy3 },
+ { "", SwServiceType::FieldMasterDummy4 },
+ { "", SwServiceType::FieldMasterDummy5 },
+ { "com.sun.star.text.IllustrationsIndex", SwServiceType::IndexIllustrations },
+ { "com.sun.star.text.ObjectIndex", SwServiceType::IndexObjects },
+ { "com.sun.star.text.TableIndex", SwServiceType::IndexTables },
+ { "com.sun.star.text.Bibliography", SwServiceType::IndexBibliography },
+ { "com.sun.star.text.Paragraph", SwServiceType::Paragraph },
+ { "com.sun.star.text.TextField.InputUser", SwServiceType::FieldTypeInputUser },
+ { "com.sun.star.text.TextField.HiddenText", SwServiceType::FieldTypeHiddenText },
+ { "com.sun.star.style.ConditionalParagraphStyle", SwServiceType::StyleConditionalParagraph },
+ { "com.sun.star.text.NumberingRules", SwServiceType::NumberingRules },
+ { "com.sun.star.text.TextColumns", SwServiceType::TextColumns },
+ { "com.sun.star.text.IndexHeaderSection", SwServiceType::IndexHeaderSection },
+ { "com.sun.star.text.Defaults", SwServiceType::Defaults },
+ { "com.sun.star.image.ImageMapRectangleObject", SwServiceType::IMapRectangle },
+ { "com.sun.star.image.ImageMapCircleObject", SwServiceType::IMapCircle },
+ { "com.sun.star.image.ImageMapPolygonObject", SwServiceType::IMapPolygon },
+ { "com.sun.star.text.TextGraphicObject", SwServiceType::TypeTextGraphic },
+ { "com.sun.star.chart2.data.DataProvider", SwServiceType::Chart2DataProvider },
+ { "com.sun.star.text.Fieldmark", SwServiceType::TypeFieldMark },
+ { "com.sun.star.text.FormFieldmark", SwServiceType::TypeFormFieldMark },
+ { "com.sun.star.text.InContentMetadata", SwServiceType::TypeMeta },
+ { "ooo.vba.VBAObjectModuleObjectProvider", SwServiceType::VbaObjectProvider },
+ { "ooo.vba.VBACodeNameProvider", SwServiceType::VbaCodeNameProvider },
+ { "ooo.vba.VBAProjectNameProvider", SwServiceType::VbaProjectNameProvider },
+ { "ooo.vba.VBAGlobals", SwServiceType::VbaGlobals },
+
+ // case-correct versions of the service names (see #i67811)
+ { CSS_TEXT_TEXTFIELD_DATE_TIME, SwServiceType::FieldTypeDateTime },
+ { CSS_TEXT_TEXTFIELD_USER, SwServiceType::FieldTypeUser },
+ { CSS_TEXT_TEXTFIELD_SET_EXPRESSION, SwServiceType::FieldTypeSetExp },
+ { CSS_TEXT_TEXTFIELD_GET_EXPRESSION, SwServiceType::FieldTypeGetExp },
+ { CSS_TEXT_TEXTFIELD_FILE_NAME, SwServiceType::FieldTypeFileName },
+ { CSS_TEXT_TEXTFIELD_PAGE_NUMBER, SwServiceType::FieldTypePageNum },
+ { CSS_TEXT_TEXTFIELD_AUTHOR, SwServiceType::FieldTypeAuthor },
+ { CSS_TEXT_TEXTFIELD_CHAPTER, SwServiceType::FieldTypeChapter },
+ { CSS_TEXT_TEXTFIELD_GET_REFERENCE, SwServiceType::FieldTypeGetReference },
+ { CSS_TEXT_TEXTFIELD_CONDITIONAL_TEXT, SwServiceType::FieldTypeConditionedText },
+ { CSS_TEXT_TEXTFIELD_ANNOTATION, SwServiceType::FieldTypeAnnotation },
+ { CSS_TEXT_TEXTFIELD_INPUT, SwServiceType::FieldTypeInput },
+ { CSS_TEXT_TEXTFIELD_MACRO, SwServiceType::FieldTypeMacro },
+ { CSS_TEXT_TEXTFIELD_DDE, SwServiceType::FieldTypeDDE },
+ { CSS_TEXT_TEXTFIELD_HIDDEN_PARAGRAPH, SwServiceType::FieldTypeHiddenPara },
+ { CSS_TEXT_TEXTFIELD_TEMPLATE_NAME, SwServiceType::FieldTypeTemplateName },
+ { CSS_TEXT_TEXTFIELD_EXTENDED_USER, SwServiceType::FieldTypeUserExt },
+ { CSS_TEXT_TEXTFIELD_REFERENCE_PAGE_SET, SwServiceType::FieldTypeRefPageSet },
+ { CSS_TEXT_TEXTFIELD_REFERENCE_PAGE_GET, SwServiceType::FieldTypeRefPageGet },
+ { CSS_TEXT_TEXTFIELD_JUMP_EDIT, SwServiceType::FieldTypeJumpEdit },
+ { CSS_TEXT_TEXTFIELD_SCRIPT, SwServiceType::FieldTypeScript },
+ { CSS_TEXT_TEXTFIELD_DATABASE_NEXT_SET, SwServiceType::FieldTypeDatabaseNextSet },
+ { CSS_TEXT_TEXTFIELD_DATABASE_NUMBER_OF_SET, SwServiceType::FieldTypeDatabaseNumSet },
+ { CSS_TEXT_TEXTFIELD_DATABASE_SET_NUMBER, SwServiceType::FieldTypeDatabaseSetNum },
+ { CSS_TEXT_TEXTFIELD_DATABASE, SwServiceType::FieldTypeDatabase },
+ { CSS_TEXT_TEXTFIELD_DATABASE_NAME, SwServiceType::FieldTypeDatabaseName },
+ { CSS_TEXT_TEXTFIELD_TABLE_FORMULA, SwServiceType::FieldTypeTableFormula },
+ { CSS_TEXT_TEXTFIELD_PAGE_COUNT, SwServiceType::FieldTypePageCount },
+ { CSS_TEXT_TEXTFIELD_PARAGRAPH_COUNT, SwServiceType::FieldTypeParagraphCount },
+ { CSS_TEXT_TEXTFIELD_WORD_COUNT, SwServiceType::FieldTypeWordCount },
+ { CSS_TEXT_TEXTFIELD_CHARACTER_COUNT, SwServiceType::FieldTypeCharacterCount },
+ { CSS_TEXT_TEXTFIELD_TABLE_COUNT, SwServiceType::FieldTypeTableCount },
+ { CSS_TEXT_TEXTFIELD_GRAPHIC_OBJECT_COUNT, SwServiceType::FieldTypeGraphicObjectCount },
+ { CSS_TEXT_TEXTFIELD_EMBEDDED_OBJECT_COUNT, SwServiceType::FieldTypeEmbeddedObjectCount },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_CHANGE_AUTHOR, SwServiceType::FieldTypeDocInfoChangeAuthor },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_CHANGE_DATE_TIME, SwServiceType::FieldTypeDocInfoChangeDateTime },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_EDIT_TIME, SwServiceType::FieldTypeDocInfoEditTime },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_DESCRIPTION, SwServiceType::FieldTypeDocInfoDescription },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_CREATE_AUTHOR, SwServiceType::FieldTypeDocInfoCreateAuthor },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_CREATE_DATE_TIME, SwServiceType::FieldTypeDocInfoCreateDateTime },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_PRINT_AUTHOR, SwServiceType::FieldTypeDocInfoPrintAuthor },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_PRINT_DATE_TIME, SwServiceType::FieldTypeDocInfoPrintDateTime },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_KEY_WORDS, SwServiceType::FieldTypeDocInfoKeywords },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_SUBJECT, SwServiceType::FieldTypeDocInfoSubject },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_TITLE, SwServiceType::FieldTypeDocInfoTitle },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_REVISION, SwServiceType::FieldTypeDocInfoRevision },
+ { CSS_TEXT_TEXTFIELD_DOCINFO_CUSTOM, SwServiceType::FieldTypeDocInfoCustom },
+ { CSS_TEXT_TEXTFIELD_BIBLIOGRAPHY, SwServiceType::FieldTypeBibliography },
+ { CSS_TEXT_TEXTFIELD_COMBINED_CHARACTERS, SwServiceType::FieldTypeCombinedCharacters },
+ { CSS_TEXT_TEXTFIELD_DROP_DOWN, SwServiceType::FieldTypeDropdown },
+ { CSS_TEXT_TEXTFIELD_INPUT_USER, SwServiceType::FieldTypeInputUser },
+ { CSS_TEXT_TEXTFIELD_HIDDEN_TEXT, SwServiceType::FieldTypeHiddenText },
+ { CSS_TEXT_FIELDMASTER_USER, SwServiceType::FieldMasterUser },
+ { CSS_TEXT_FIELDMASTER_DDE, SwServiceType::FieldMasterDDE },
+ { CSS_TEXT_FIELDMASTER_SET_EXPRESSION, SwServiceType::FieldMasterSetExp },
+ { CSS_TEXT_FIELDMASTER_DATABASE, SwServiceType::FieldMasterDatabase },
+ { CSS_TEXT_FIELDMASTER_BIBLIOGRAPHY, SwServiceType::FieldMasterBibliography },
+ { "com.sun.star.style.TableStyle", SwServiceType::StyleTable },
+ { "com.sun.star.style.CellStyle", SwServiceType::StyleCell },
+ { "com.sun.star.text.LineBreak", SwServiceType::LineBreak },
+ { "com.sun.star.text.ContentControl", SwServiceType::ContentControl }
+};
+
+const SvEventDescription* sw_GetSupportedMacroItems()
+{
+ static const SvEventDescription aMacroDescriptionsImpl[] =
+ {
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::NONE, nullptr }
+ };
+
+ return aMacroDescriptionsImpl;
+}
+
+OUString SwXServiceProvider::GetProviderName(SwServiceType nObjectType)
+{
+ SolarMutexGuard aGuard;
+ OUString sRet;
+ const sal_uInt16 nEntries = SAL_N_ELEMENTS(aProvNamesId);
+ if(static_cast<sal_uInt16>(nObjectType) < nEntries)
+ sRet = OUString::createFromAscii(aProvNamesId[static_cast<sal_uInt16>(nObjectType)].pName);
+ return sRet;
+}
+
+uno::Sequence<OUString> SwXServiceProvider::GetAllServiceNames()
+{
+ const sal_uInt16 nEntries = SAL_N_ELEMENTS(aProvNamesId);
+ uno::Sequence<OUString> aRet(nEntries);
+ OUString* pArray = aRet.getArray();
+ sal_uInt16 n = 0;
+ for(const ProvNamesId_Type & i : aProvNamesId)
+ {
+ OUString sProv(OUString::createFromAscii(i.pName));
+ if(!sProv.isEmpty())
+ {
+ pArray[n] = sProv;
+ n++;
+ }
+ }
+ aRet.realloc(n);
+ return aRet;
+
+}
+
+SwServiceType SwXServiceProvider::GetProviderType(std::u16string_view rServiceName)
+{
+ for(const ProvNamesId_Type & i : aProvNamesId)
+ {
+ if (o3tl::equalsAscii(rServiceName, i.pName))
+ return i.nType;
+ }
+ return SwServiceType::Invalid;
+}
+
+uno::Reference<uno::XInterface>
+SwXServiceProvider::MakeInstance(SwServiceType nObjectType, SwDoc & rDoc)
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< uno::XInterface > xRet;
+ switch(nObjectType)
+ {
+ case SwServiceType::TypeTextTable:
+ {
+ xRet = SwXTextTable::CreateXTextTable(nullptr);
+ }
+ break;
+ case SwServiceType::TypeTextFrame:
+ {
+ xRet = SwXTextFrame::CreateXTextFrame(rDoc, nullptr);
+ }
+ break;
+ case SwServiceType::TypeGraphic :
+ case SwServiceType::TypeTextGraphic /* #i47503# */ :
+ {
+ xRet = SwXTextGraphicObject::CreateXTextGraphicObject(rDoc, nullptr);
+
+ }
+ break;
+ case SwServiceType::TypeOLE :
+ {
+ xRet = SwXTextEmbeddedObject::CreateXTextEmbeddedObject(rDoc, nullptr);
+ }
+ break;
+ case SwServiceType::TypeBookmark :
+ {
+ xRet = SwXBookmark::CreateXBookmark(rDoc, nullptr);
+ }
+ break;
+ case SwServiceType::TypeFieldMark :
+ {
+ xRet = SwXFieldmark::CreateXFieldmark(rDoc, nullptr);
+ }
+ break;
+ case SwServiceType::TypeFormFieldMark :
+ {
+ xRet = SwXFieldmark::CreateXFieldmark(rDoc, nullptr, true);
+ }
+ break;
+ case SwServiceType::VbaObjectProvider :
+#if HAVE_FEATURE_SCRIPTING
+ {
+ xRet = static_cast<cppu::OWeakObject*>(new SwVbaObjectForCodeNameProvider(rDoc.GetDocShell()));
+ }
+#endif
+ break;
+ case SwServiceType::VbaCodeNameProvider :
+#if HAVE_FEATURE_SCRIPTING
+ {
+ if (rDoc.GetDocShell() && ooo::vba::isAlienWordDoc(*rDoc.GetDocShell()))
+ {
+ xRet = static_cast<cppu::OWeakObject*>(new SwVbaCodeNameProvider(rDoc.GetDocShell()));
+ }
+ }
+#endif
+ break;
+ case SwServiceType::VbaProjectNameProvider :
+#if HAVE_FEATURE_SCRIPTING
+ {
+ uno::Reference< container::XNameContainer > xProjProv = rDoc.GetVBATemplateToProjectCache();
+ if (!xProjProv.is() && rDoc.GetDocShell()
+ && ooo::vba::isAlienWordDoc(*rDoc.GetDocShell()))
+ {
+ xProjProv = new SwVbaProjectNameProvider;
+ rDoc.SetVBATemplateToProjectCache(xProjProv);
+ }
+ xRet = xProjProv;
+ }
+#endif
+ break;
+ case SwServiceType::VbaGlobals :
+#if HAVE_FEATURE_SCRIPTING
+ {
+ uno::Any aGlobs;
+ BasicManager *pBasicMan = rDoc.GetDocShell()->GetBasicManager();
+ if (pBasicMan && !pBasicMan->GetGlobalUNOConstant("VBAGlobals", aGlobs))
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(rDoc.GetDocShell()->GetModel()) };
+ aGlobs <<= ::comphelper::getProcessServiceFactory()->createInstanceWithArguments( "ooo.vba.word.Globals", aArgs );
+ pBasicMan->SetGlobalUNOConstant( "VBAGlobals", aGlobs );
+ }
+ aGlobs >>= xRet;
+ }
+#endif
+ break;
+
+ case SwServiceType::TypeFootnote :
+ xRet = SwXFootnote::CreateXFootnote(rDoc, nullptr);
+ break;
+ case SwServiceType::TypeEndnote :
+ xRet = SwXFootnote::CreateXFootnote(rDoc, nullptr, true);
+ break;
+ case SwServiceType::ContentIndexMark :
+ case SwServiceType::UserIndexMark :
+ case SwServiceType::TypeIndexMark:
+ {
+ TOXTypes eType = TOX_INDEX;
+ if(SwServiceType::ContentIndexMark== nObjectType)
+ eType = TOX_CONTENT;
+ else if(SwServiceType::UserIndexMark == nObjectType)
+ eType = TOX_USER;
+ xRet = SwXDocumentIndexMark::CreateXDocumentIndexMark(rDoc, nullptr, eType);
+ }
+ break;
+ case SwServiceType::ContentIndex :
+ case SwServiceType::UserIndex :
+ case SwServiceType::TypeIndex :
+ case SwServiceType::IndexIllustrations:
+ case SwServiceType::IndexObjects :
+ case SwServiceType::IndexTables:
+ case SwServiceType::IndexBibliography :
+ {
+ TOXTypes eType = TOX_INDEX;
+ if(SwServiceType::ContentIndex == nObjectType)
+ eType = TOX_CONTENT;
+ else if(SwServiceType::UserIndex == nObjectType)
+ eType = TOX_USER;
+ else if(SwServiceType::IndexIllustrations == nObjectType)
+ {
+ eType = TOX_ILLUSTRATIONS;
+ }
+ else if(SwServiceType::IndexObjects == nObjectType)
+ {
+ eType = TOX_OBJECTS;
+ }
+ else if(SwServiceType::IndexBibliography == nObjectType)
+ {
+ eType = TOX_AUTHORITIES;
+ }
+ else if(SwServiceType::IndexTables == nObjectType)
+ {
+ eType = TOX_TABLES;
+ }
+ xRet = SwXDocumentIndex::CreateXDocumentIndex(rDoc, nullptr, eType);
+ }
+ break;
+ case SwServiceType::IndexHeaderSection :
+ case SwServiceType::TextSection :
+ xRet = SwXTextSection::CreateXTextSection(nullptr,
+ (SwServiceType::IndexHeaderSection == nObjectType));
+
+ break;
+ case SwServiceType::ReferenceMark :
+ xRet = SwXReferenceMark::CreateXReferenceMark(rDoc, nullptr);
+ break;
+ case SwServiceType::StyleCharacter:
+ case SwServiceType::StyleParagraph:
+ case SwServiceType::StyleConditionalParagraph:
+ case SwServiceType::StyleFrame:
+ case SwServiceType::StylePage:
+ case SwServiceType::StyleNumbering:
+ case SwServiceType::StyleTable:
+ case SwServiceType::StyleCell:
+ {
+ SfxStyleFamily eFamily = SfxStyleFamily::Char;
+ switch(nObjectType)
+ {
+ case SwServiceType::StyleParagraph:
+ eFamily = SfxStyleFamily::Para;
+ break;
+ case SwServiceType::StyleConditionalParagraph:
+ eFamily = SfxStyleFamily::Para;
+ xRet = SwXStyleFamilies::CreateStyleCondParagraph(rDoc);
+ break;
+ case SwServiceType::StyleFrame:
+ eFamily = SfxStyleFamily::Frame;
+ break;
+ case SwServiceType::StylePage:
+ eFamily = SfxStyleFamily::Page;
+ break;
+ case SwServiceType::StyleNumbering:
+ eFamily = SfxStyleFamily::Pseudo;
+ break;
+ case SwServiceType::StyleTable:
+ eFamily = SfxStyleFamily::Table;
+ break;
+ case SwServiceType::StyleCell:
+ eFamily = SfxStyleFamily::Cell;
+ break;
+ default: break;
+ }
+ if(!xRet.is())
+ xRet = SwXStyleFamilies::CreateStyle(eFamily, rDoc);
+ }
+ break;
+ case SwServiceType::FieldTypeDateTime:
+ case SwServiceType::FieldTypeUser:
+ case SwServiceType::FieldTypeSetExp:
+ case SwServiceType::FieldTypeGetExp:
+ case SwServiceType::FieldTypeFileName:
+ case SwServiceType::FieldTypePageNum:
+ case SwServiceType::FieldTypeAuthor:
+ case SwServiceType::FieldTypeChapter:
+ case SwServiceType::FieldTypeGetReference:
+ case SwServiceType::FieldTypeConditionedText:
+ case SwServiceType::FieldTypeInput:
+ case SwServiceType::FieldTypeMacro:
+ case SwServiceType::FieldTypeDDE:
+ case SwServiceType::FieldTypeHiddenPara:
+ case SwServiceType::FieldTypeDocInfo:
+ case SwServiceType::FieldTypeTemplateName:
+ case SwServiceType::FieldTypeUserExt:
+ case SwServiceType::FieldTypeRefPageSet:
+ case SwServiceType::FieldTypeRefPageGet:
+ case SwServiceType::FieldTypeJumpEdit:
+ case SwServiceType::FieldTypeScript:
+ case SwServiceType::FieldTypeDatabaseNextSet:
+ case SwServiceType::FieldTypeDatabaseNumSet:
+ case SwServiceType::FieldTypeDatabaseSetNum:
+ case SwServiceType::FieldTypeDatabase:
+ case SwServiceType::FieldTypeDatabaseName:
+ case SwServiceType::FieldTypePageCount:
+ case SwServiceType::FieldTypeParagraphCount:
+ case SwServiceType::FieldTypeWordCount:
+ case SwServiceType::FieldTypeCharacterCount:
+ case SwServiceType::FieldTypeTableCount:
+ case SwServiceType::FieldTypeGraphicObjectCount:
+ case SwServiceType::FieldTypeEmbeddedObjectCount:
+ case SwServiceType::FieldTypeDocInfoChangeAuthor:
+ case SwServiceType::FieldTypeDocInfoChangeDateTime:
+ case SwServiceType::FieldTypeDocInfoEditTime:
+ case SwServiceType::FieldTypeDocInfoDescription:
+ case SwServiceType::FieldTypeDocInfoCreateAuthor:
+ case SwServiceType::FieldTypeDocInfoCreateDateTime:
+ case SwServiceType::FieldTypeDocInfoCustom:
+ case SwServiceType::FieldTypeDocInfoPrintAuthor:
+ case SwServiceType::FieldTypeDocInfoPrintDateTime:
+ case SwServiceType::FieldTypeDocInfoKeywords:
+ case SwServiceType::FieldTypeDocInfoSubject:
+ case SwServiceType::FieldTypeDocInfoTitle:
+ case SwServiceType::FieldTypeDocInfoRevision:
+ case SwServiceType::FieldTypeBibliography:
+ case SwServiceType::FieldTypeInputUser:
+ case SwServiceType::FieldTypeHiddenText:
+ case SwServiceType::FieldTypeCombinedCharacters:
+ case SwServiceType::FieldTypeDropdown:
+ case SwServiceType::FieldTypeTableFormula:
+ // NOTE: the sw.SwXAutoTextEntry unoapi test depends on pDoc = 0
+ xRet = SwXTextField::CreateXTextField(nullptr, nullptr, nObjectType);
+ break;
+ case SwServiceType::FieldTypeAnnotation:
+ xRet = SwXTextField::CreateXTextField(&rDoc, nullptr, nObjectType);
+ break;
+ case SwServiceType::FieldMasterUser:
+ case SwServiceType::FieldMasterDDE:
+ case SwServiceType::FieldMasterSetExp :
+ case SwServiceType::FieldMasterDatabase:
+ {
+ SwFieldIds nResId = SwFieldIds::Unknown;
+ switch(nObjectType)
+ {
+ case SwServiceType::FieldMasterUser: nResId = SwFieldIds::User; break;
+ case SwServiceType::FieldMasterDDE: nResId = SwFieldIds::Dde; break;
+ case SwServiceType::FieldMasterSetExp : nResId = SwFieldIds::SetExp; break;
+ case SwServiceType::FieldMasterDatabase: nResId = SwFieldIds::Database; break;
+ default: break;
+ }
+ xRet = SwXFieldMaster::CreateXFieldMaster(&rDoc, nullptr, nResId);
+ }
+ break;
+ case SwServiceType::FieldMasterBibliography:
+ {
+ SwFieldType* pType = rDoc.getIDocumentFieldsAccess().GetFieldType(SwFieldIds::TableOfAuthorities, OUString(), true);
+ if(!pType)
+ {
+ SwAuthorityFieldType aType(&rDoc);
+ pType = rDoc.getIDocumentFieldsAccess().InsertFieldType(aType);
+ }
+ xRet = SwXFieldMaster::CreateXFieldMaster(&rDoc, pType);
+ }
+ break;
+ case SwServiceType::Paragraph:
+ xRet = SwXParagraph::CreateXParagraph(rDoc, nullptr);
+ break;
+ case SwServiceType::NumberingRules:
+ xRet = static_cast<cppu::OWeakObject*>(new SwXNumberingRules(rDoc));
+ break;
+ case SwServiceType::TextColumns:
+ xRet = SvxXTextColumns_createInstance();
+ break;
+ case SwServiceType::Defaults:
+ xRet = static_cast<cppu::OWeakObject*>(new SwXTextDefaults(&rDoc));
+ break;
+ case SwServiceType::IMapRectangle:
+ xRet = SvUnoImageMapRectangleObject_createInstance( sw_GetSupportedMacroItems() );
+ break;
+ case SwServiceType::IMapCircle:
+ xRet = SvUnoImageMapCircleObject_createInstance( sw_GetSupportedMacroItems() );
+ break;
+ case SwServiceType::IMapPolygon:
+ xRet = SvUnoImageMapPolygonObject_createInstance( sw_GetSupportedMacroItems() );
+ break;
+ case SwServiceType::Chart2DataProvider:
+ // #i64497# If a chart is in a temporary document during clipboard
+ // paste, there should be no data provider, so that own data is used
+ // This should not happen during copy/paste, as this will unlink
+ // charts using table data.
+ if (rDoc.GetDocShell()->GetCreateMode() != SfxObjectCreateMode::EMBEDDED)
+ xRet = static_cast<cppu::OWeakObject*>(rDoc.getIDocumentChartDataProviderAccess().GetChartDataProvider( true /* create - if not yet available */ ));
+ else
+ SAL_WARN("sw.uno",
+ "not creating chart data provider for embedded object");
+
+ break;
+ case SwServiceType::TypeMeta:
+ xRet = SwXMeta::CreateXMeta(rDoc, false);
+ break;
+ case SwServiceType::FieldTypeMetafield:
+ xRet = SwXMeta::CreateXMeta(rDoc, true);
+ break;
+ case SwServiceType::LineBreak:
+ xRet = SwXLineBreak::CreateXLineBreak(nullptr);
+ break;
+ case SwServiceType::ContentControl:
+ xRet = SwXContentControl::CreateXContentControl(rDoc);
+ break;
+ default:
+ throw uno::RuntimeException();
+ }
+ return xRet;
+}
+
+//SMART_UNO_IMPLEMENTATION( SwXTextTables, UsrObject );
+SwXTextTables::SwXTextTables(SwDoc* pDc) :
+ SwUnoCollection(pDc)
+{
+
+}
+
+SwXTextTables::~SwXTextTables()
+{
+
+}
+
+sal_Int32 SwXTextTables::getCount()
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nRet = 0;
+ if(IsValid())
+ nRet = static_cast<sal_Int32>(GetDoc()->GetTableFrameFormatCount(true));
+ return nRet;
+}
+
+uno::Any SAL_CALL SwXTextTables::getByIndex(sal_Int32 nInputIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if (!IsValid())
+ throw uno::RuntimeException();
+
+ if (nInputIndex < 0)
+ throw IndexOutOfBoundsException();
+
+ SwAutoFormatGetDocNode aGetHt( &GetDoc()->GetNodes() );
+ size_t nIndex = static_cast<size_t>(nInputIndex);
+ size_t nCurrentIndex = 0;
+
+ for (SwFrameFormat* const & pFormat : *GetDoc()->GetTableFrameFormats())
+ {
+ if (!pFormat->GetInfo(aGetHt))
+ {
+ if (nCurrentIndex == nIndex)
+ {
+ uno::Reference<XTextTable> xTable = SwXTextTables::GetObject(*pFormat);
+ aRet <<= xTable;
+ return aRet;
+ }
+ else
+ nCurrentIndex++;
+ }
+ }
+ throw IndexOutOfBoundsException();
+}
+
+uno::Any SwXTextTables::getByName(const OUString& rItemName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ const size_t nCount = GetDoc()->GetTableFrameFormatCount(true);
+ uno::Reference< XTextTable > xTable;
+ for( size_t i = 0; i < nCount; ++i)
+ {
+ SwFrameFormat& rFormat = GetDoc()->GetTableFrameFormat(i, true);
+ if (rItemName == rFormat.GetName())
+ {
+ xTable = SwXTextTables::GetObject(rFormat);
+ aRet <<= xTable;
+ break;
+ }
+ }
+ if(!xTable.is())
+ throw NoSuchElementException();
+
+ return aRet;
+}
+
+uno::Sequence< OUString > SwXTextTables::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const size_t nCount = GetDoc()->GetTableFrameFormatCount(true);
+ uno::Sequence<OUString> aSeq(static_cast<sal_Int32>(nCount));
+ if(nCount)
+ {
+ OUString* pArray = aSeq.getArray();
+ for( size_t i = 0; i < nCount; ++i)
+ {
+ SwFrameFormat& rFormat = GetDoc()->GetTableFrameFormat(i, true);
+
+ pArray[i] = rFormat.GetName();
+ }
+ }
+ return aSeq;
+}
+
+sal_Bool SwXTextTables::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ bool bRet= false;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ const size_t nCount = GetDoc()->GetTableFrameFormatCount(true);
+ for( size_t i = 0; i < nCount; ++i)
+ {
+ SwFrameFormat& rFormat = GetDoc()->GetTableFrameFormat(i, true);
+ if (rName == rFormat.GetName())
+ {
+ bRet = true;
+ break;
+ }
+ }
+ return bRet;
+}
+
+uno::Type SAL_CALL
+ SwXTextTables::getElementType( )
+{
+ return cppu::UnoType<XTextTable>::get();
+}
+
+sal_Bool SwXTextTables::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return 0 != GetDoc()->GetTableFrameFormatCount(true);
+}
+
+OUString SwXTextTables::getImplementationName()
+{
+ return "SwXTextTables";
+}
+
+sal_Bool SwXTextTables::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextTables::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextTables" };
+}
+
+uno::Reference<text::XTextTable> SwXTextTables::GetObject(SwFrameFormat& rFormat)
+{
+ return SwXTextTable::CreateXTextTable(& rFormat);
+}
+
+namespace
+{
+ template<FlyCntType T> struct UnoFrameWrap_traits {};
+
+ template<>
+ struct UnoFrameWrap_traits<FLYCNTTYPE_FRM>
+ {
+ static uno::Any wrapFrame(SwFrameFormat & rFrameFormat)
+ {
+ uno::Reference<text::XTextFrame> const xRet(
+ SwXTextFrame::CreateXTextFrame(*rFrameFormat.GetDoc(), &rFrameFormat));
+ return uno::Any(xRet);
+ }
+ static bool filter(const SwNode* const pNode) { return !pNode->IsNoTextNode(); };
+ };
+
+ template<>
+ struct UnoFrameWrap_traits<FLYCNTTYPE_GRF>
+ {
+ static uno::Any wrapFrame(SwFrameFormat & rFrameFormat)
+ {
+ uno::Reference<text::XTextContent> const xRet(
+ SwXTextGraphicObject::CreateXTextGraphicObject(*rFrameFormat.GetDoc(), &rFrameFormat));
+ return uno::Any(xRet);
+ }
+ static bool filter(const SwNode* const pNode) { return pNode->IsGrfNode(); };
+ };
+
+ template<>
+ struct UnoFrameWrap_traits<FLYCNTTYPE_OLE>
+ {
+ static uno::Any wrapFrame(SwFrameFormat & rFrameFormat)
+ {
+ uno::Reference<text::XTextContent> const xRet(
+ SwXTextEmbeddedObject::CreateXTextEmbeddedObject(*rFrameFormat.GetDoc(), &rFrameFormat));
+ return uno::Any(xRet);
+ }
+ static bool filter(const SwNode* const pNode) { return pNode->IsOLENode(); };
+ };
+
+ template<FlyCntType T>
+ uno::Any lcl_UnoWrapFrame(SwFrameFormat* pFormat)
+ {
+ return UnoFrameWrap_traits<T>::wrapFrame(*pFormat);
+ }
+
+ // runtime adapter for lcl_UnoWrapFrame
+ /// @throws uno::RuntimeException
+ uno::Any lcl_UnoWrapFrame(SwFrameFormat* pFormat, FlyCntType eType)
+ {
+ switch(eType)
+ {
+ case FLYCNTTYPE_FRM:
+ return lcl_UnoWrapFrame<FLYCNTTYPE_FRM>(pFormat);
+ case FLYCNTTYPE_GRF:
+ return lcl_UnoWrapFrame<FLYCNTTYPE_GRF>(pFormat);
+ case FLYCNTTYPE_OLE:
+ return lcl_UnoWrapFrame<FLYCNTTYPE_OLE>(pFormat);
+ default:
+ throw uno::RuntimeException();
+ }
+ }
+
+ template<FlyCntType T>
+ class SwXFrameEnumeration
+ : public SwSimpleEnumeration_Base
+ {
+ private:
+ std::vector< Any > m_aFrames;
+ protected:
+ virtual ~SwXFrameEnumeration() override {};
+ public:
+ SwXFrameEnumeration(const SwDoc& rDoc);
+
+ //XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual Any SAL_CALL nextElement() override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+ };
+}
+
+template<FlyCntType T>
+SwXFrameEnumeration<T>::SwXFrameEnumeration(const SwDoc& rDoc)
+{
+ SolarMutexGuard aGuard;
+ const SwFrameFormats* const pFormats = rDoc.GetSpzFrameFormats();
+ if (pFormats->empty())
+ return;
+ // #i104937#
+ const size_t nSize = pFormats->size();
+ // #i104937#
+ SwFrameFormat* pFormat( nullptr );
+ for( size_t i = 0; i < nSize; ++i )
+ {
+ // #i104937#
+ pFormat = (*pFormats)[i];
+ if(pFormat->Which() != RES_FLYFRMFMT || SwTextBoxHelper::isTextBox(pFormat, RES_FLYFRMFMT))
+ continue;
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(!pIdx || !pIdx->GetNodes().IsDocNodes())
+ continue;
+ const SwNode* pNd = rDoc.GetNodes()[ pIdx->GetIndex() + 1 ];
+ if(UnoFrameWrap_traits<T>::filter(pNd))
+ m_aFrames.push_back(lcl_UnoWrapFrame<T>(pFormat));
+ }
+}
+
+template<FlyCntType T>
+sal_Bool SwXFrameEnumeration<T>::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return !m_aFrames.empty();
+}
+
+template<FlyCntType T>
+Any SwXFrameEnumeration<T>::nextElement()
+{
+ SolarMutexGuard aGuard;
+ if(m_aFrames.empty())
+ throw NoSuchElementException();
+
+ Any aResult = m_aFrames.back();
+ m_aFrames.pop_back();
+ return aResult;
+}
+
+template<FlyCntType T>
+OUString SwXFrameEnumeration<T>::getImplementationName()
+{
+ return "SwXFrameEnumeration";
+}
+
+template<FlyCntType T>
+sal_Bool SwXFrameEnumeration<T>::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+template<FlyCntType T>
+Sequence< OUString > SwXFrameEnumeration<T>::getSupportedServiceNames()
+{
+ return { OUString("com.sun.star.container.XEnumeration") };
+}
+
+OUString SwXFrames::getImplementationName()
+{
+ return "SwXFrames";
+}
+
+sal_Bool SwXFrames::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence<OUString> SwXFrames::getSupportedServiceNames()
+{
+ return { OUString("com.sun.star.text.TextFrames") };
+}
+
+SwXFrames::SwXFrames(SwDoc* _pDoc, FlyCntType eSet) :
+ SwUnoCollection(_pDoc),
+ m_eType(eSet)
+{}
+
+SwXFrames::~SwXFrames()
+{}
+
+uno::Reference<container::XEnumeration> SwXFrames::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ switch(m_eType)
+ {
+ case FLYCNTTYPE_FRM:
+ return uno::Reference< container::XEnumeration >(
+ new SwXFrameEnumeration<FLYCNTTYPE_FRM>(*GetDoc()));
+ case FLYCNTTYPE_GRF:
+ return uno::Reference< container::XEnumeration >(
+ new SwXFrameEnumeration<FLYCNTTYPE_GRF>(*GetDoc()));
+ case FLYCNTTYPE_OLE:
+ return uno::Reference< container::XEnumeration >(
+ new SwXFrameEnumeration<FLYCNTTYPE_OLE>(*GetDoc()));
+ default:
+ throw uno::RuntimeException();
+ }
+}
+
+sal_Int32 SwXFrames::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ // Ignore TextBoxes for TextFrames.
+ return static_cast<sal_Int32>(GetDoc()->GetFlyCount(m_eType, /*bIgnoreTextBoxes=*/m_eType == FLYCNTTYPE_FRM));
+}
+
+uno::Any SwXFrames::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ if(nIndex < 0)
+ throw IndexOutOfBoundsException();
+ // Ignore TextBoxes for TextFrames.
+ SwFrameFormat* pFormat = GetDoc()->GetFlyNum(static_cast<size_t>(nIndex), m_eType, /*bIgnoreTextBoxes=*/m_eType == FLYCNTTYPE_FRM);
+ if(!pFormat)
+ throw IndexOutOfBoundsException();
+ return lcl_UnoWrapFrame(pFormat, m_eType);
+}
+
+uno::Any SwXFrames::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const SwFrameFormat* pFormat;
+ switch(m_eType)
+ {
+ case FLYCNTTYPE_GRF:
+ pFormat = GetDoc()->FindFlyByName(rName, SwNodeType::Grf);
+ break;
+ case FLYCNTTYPE_OLE:
+ pFormat = GetDoc()->FindFlyByName(rName, SwNodeType::Ole);
+ break;
+ default:
+ pFormat = GetDoc()->FindFlyByName(rName, SwNodeType::Text);
+ break;
+ }
+ if(!pFormat)
+ throw NoSuchElementException();
+ return lcl_UnoWrapFrame(const_cast<SwFrameFormat*>(pFormat), m_eType);
+}
+
+uno::Sequence<OUString> SwXFrames::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const Reference<XEnumeration> xEnum = createEnumeration();
+ std::vector<OUString> vNames;
+ while(xEnum->hasMoreElements())
+ {
+ Reference<container::XNamed> xNamed;
+ xEnum->nextElement() >>= xNamed;
+ if(xNamed.is())
+ vNames.push_back(xNamed->getName());
+ }
+ return ::comphelper::containerToSequence(vNames);
+}
+
+sal_Bool SwXFrames::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ switch(m_eType)
+ {
+ case FLYCNTTYPE_GRF:
+ return GetDoc()->FindFlyByName(rName, SwNodeType::Grf) != nullptr;
+ case FLYCNTTYPE_OLE:
+ return GetDoc()->FindFlyByName(rName, SwNodeType::Ole) != nullptr;
+ default:
+ return GetDoc()->FindFlyByName(rName, SwNodeType::Text) != nullptr;
+ }
+}
+
+uno::Type SAL_CALL SwXFrames::getElementType()
+{
+ SolarMutexGuard aGuard;
+ switch(m_eType)
+ {
+ case FLYCNTTYPE_FRM:
+ return cppu::UnoType<XTextFrame>::get();
+ case FLYCNTTYPE_GRF:
+ return cppu::UnoType<XTextContent>::get();
+ case FLYCNTTYPE_OLE:
+ return cppu::UnoType<XEmbeddedObjectSupplier>::get();
+ default:
+ return uno::Type();
+ }
+}
+
+sal_Bool SwXFrames::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return GetDoc()->GetFlyCount(m_eType) > 0;
+}
+
+
+OUString SwXTextFrames::getImplementationName()
+{
+ return "SwXTextFrames";
+}
+
+sal_Bool SwXTextFrames::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXTextFrames::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextFrames" };
+}
+
+SwXTextFrames::SwXTextFrames(SwDoc* _pDoc) :
+ SwXFrames(_pDoc, FLYCNTTYPE_FRM)
+{
+}
+
+SwXTextFrames::~SwXTextFrames()
+{
+}
+
+OUString SwXTextGraphicObjects::getImplementationName()
+{
+ return "SwXTextGraphicObjects";
+}
+
+sal_Bool SwXTextGraphicObjects::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXTextGraphicObjects::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextGraphicObjects" };
+}
+
+SwXTextGraphicObjects::SwXTextGraphicObjects(SwDoc* _pDoc) :
+ SwXFrames(_pDoc, FLYCNTTYPE_GRF)
+{
+}
+
+SwXTextGraphicObjects::~SwXTextGraphicObjects()
+{
+}
+
+OUString SwXTextEmbeddedObjects::getImplementationName()
+{
+ return "SwXTextEmbeddedObjects";
+}
+
+sal_Bool SwXTextEmbeddedObjects::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXTextEmbeddedObjects::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextEmbeddedObjects" };
+}
+
+SwXTextEmbeddedObjects::SwXTextEmbeddedObjects(SwDoc* _pDoc) :
+ SwXFrames(_pDoc, FLYCNTTYPE_OLE)
+{
+}
+
+SwXTextEmbeddedObjects::~SwXTextEmbeddedObjects()
+{
+}
+
+OUString SwXTextSections::getImplementationName()
+{
+ return "SwXTextSections";
+}
+
+sal_Bool SwXTextSections::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXTextSections::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextSections" };
+}
+
+SwXTextSections::SwXTextSections(SwDoc* _pDoc) :
+ SwUnoCollection(_pDoc)
+{
+}
+
+SwXTextSections::~SwXTextSections()
+{
+}
+
+sal_Int32 SwXTextSections::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const SwSectionFormats& rSectFormats = GetDoc()->GetSections();
+ size_t nCount = rSectFormats.size();
+ for(size_t i = nCount; i; --i)
+ {
+ if( !rSectFormats[i - 1]->IsInNodesArr())
+ nCount--;
+ }
+ return nCount;
+}
+
+uno::Any SwXTextSections::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< XTextSection > xRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ SwSectionFormats& rFormats = GetDoc()->GetSections();
+
+ const SwSectionFormats& rSectFormats = GetDoc()->GetSections();
+ const size_t nCount = rSectFormats.size();
+ for(size_t i = 0; i < nCount; ++i)
+ {
+ if( !rSectFormats[i]->IsInNodesArr())
+ nIndex ++;
+ else if(static_cast<size_t>(nIndex) == i)
+ break;
+ if(static_cast<size_t>(nIndex) == i)
+ break;
+ }
+ if(nIndex < 0 || o3tl::make_unsigned(nIndex) >= rFormats.size())
+ throw IndexOutOfBoundsException();
+
+ SwSectionFormat* pFormat = rFormats[nIndex];
+ xRet = GetObject(*pFormat);
+
+ return Any(xRet);
+}
+
+uno::Any SwXTextSections::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ SwSectionFormats& rFormats = GetDoc()->GetSections();
+ uno::Reference< XTextSection > xSect;
+ for(size_t i = 0; i < rFormats.size(); ++i)
+ {
+ SwSectionFormat* pFormat = rFormats[i];
+ if (pFormat->IsInNodesArr()
+ && (rName == pFormat->GetSection()->GetSectionName()))
+ {
+ xSect = GetObject(*pFormat);
+ aRet <<= xSect;
+ break;
+ }
+ }
+ if(!xSect.is())
+ throw NoSuchElementException();
+
+ return aRet;
+}
+
+uno::Sequence< OUString > SwXTextSections::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ size_t nCount = GetDoc()->GetSections().size();
+ SwSectionFormats& rSectFormats = GetDoc()->GetSections();
+ for(size_t i = nCount; i; --i)
+ {
+ if( !rSectFormats[i - 1]->IsInNodesArr())
+ nCount--;
+ }
+
+ uno::Sequence<OUString> aSeq(nCount);
+ if(nCount)
+ {
+ SwSectionFormats& rFormats = GetDoc()->GetSections();
+ OUString* pArray = aSeq.getArray();
+ size_t nIndex = 0;
+ for( size_t i = 0; i < nCount; ++i, ++nIndex)
+ {
+ const SwSectionFormat* pFormat = rFormats[nIndex];
+ while(!pFormat->IsInNodesArr())
+ {
+ pFormat = rFormats[++nIndex];
+ }
+ pArray[i] = pFormat->GetSection()->GetSectionName();
+ }
+ }
+ return aSeq;
+}
+
+sal_Bool SwXTextSections::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ bool bRet = false;
+ if(IsValid())
+ {
+ SwSectionFormats& rFormats = GetDoc()->GetSections();
+ for(size_t i = 0; i < rFormats.size(); ++i)
+ {
+ const SwSectionFormat* pFormat = rFormats[i];
+ if (rName == pFormat->GetSection()->GetSectionName())
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // special handling for dbg_ methods
+ if( !rName.startsWith("dbg_"))
+ throw uno::RuntimeException();
+ }
+ return bRet;
+}
+
+uno::Type SAL_CALL SwXTextSections::getElementType()
+{
+ return cppu::UnoType<XTextSection>::get();
+}
+
+sal_Bool SwXTextSections::hasElements()
+{
+ SolarMutexGuard aGuard;
+ size_t nCount = 0;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ SwSectionFormats& rFormats = GetDoc()->GetSections();
+ nCount = rFormats.size();
+
+ return nCount > 0;
+}
+
+uno::Reference< XTextSection > SwXTextSections::GetObject( SwSectionFormat& rFormat )
+{
+ return SwXTextSection::CreateXTextSection(&rFormat);
+}
+
+OUString SwXBookmarks::getImplementationName()
+{
+ return "SwXBookmarks";
+}
+
+sal_Bool SwXBookmarks::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXBookmarks::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.Bookmarks" };
+}
+
+SwXBookmarks::SwXBookmarks(SwDoc* _pDoc) :
+ SwUnoCollection(_pDoc)
+{ }
+
+SwXBookmarks::~SwXBookmarks()
+{ }
+
+sal_Int32 SwXBookmarks::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ sal_Int32 count(0);
+ IDocumentMarkAccess* const pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ for (IDocumentMarkAccess::const_iterator_t ppMark =
+ pMarkAccess->getBookmarksBegin();
+ ppMark != pMarkAccess->getBookmarksEnd(); ++ppMark)
+ {
+ if (IDocumentMarkAccess::MarkType::BOOKMARK ==
+ IDocumentMarkAccess::GetType(**ppMark))
+ {
+ ++count; // only count real bookmarks
+ }
+ }
+ return count;
+}
+
+uno::Any SwXBookmarks::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ IDocumentMarkAccess* const pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ if(nIndex < 0 || nIndex >= pMarkAccess->getBookmarksCount())
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 count(0);
+ for (IDocumentMarkAccess::const_iterator_t ppMark =
+ pMarkAccess->getBookmarksBegin();
+ ppMark != pMarkAccess->getBookmarksEnd(); ++ppMark)
+ {
+ if (IDocumentMarkAccess::MarkType::BOOKMARK ==
+ IDocumentMarkAccess::GetType(**ppMark))
+ {
+ if (count == nIndex)
+ {
+ uno::Any aRet;
+ const uno::Reference< text::XTextContent > xRef =
+ SwXBookmark::CreateXBookmark(*GetDoc(), *ppMark);
+ aRet <<= xRef;
+ return aRet;
+ }
+ ++count; // only count real bookmarks
+ }
+ }
+ throw IndexOutOfBoundsException();
+}
+
+uno::Any SwXBookmarks::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ IDocumentMarkAccess* const pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ IDocumentMarkAccess::const_iterator_t ppBkmk = pMarkAccess->findBookmark(rName);
+ if(ppBkmk == pMarkAccess->getBookmarksEnd())
+ throw NoSuchElementException();
+
+ uno::Any aRet;
+ const uno::Reference< text::XTextContent > xRef =
+ SwXBookmark::CreateXBookmark(*GetDoc(), *ppBkmk);
+ aRet <<= xRef;
+ return aRet;
+}
+
+uno::Sequence< OUString > SwXBookmarks::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ std::vector< OUString > ret;
+ IDocumentMarkAccess* const pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ for (IDocumentMarkAccess::const_iterator_t ppMark =
+ pMarkAccess->getBookmarksBegin();
+ ppMark != pMarkAccess->getBookmarksEnd(); ++ppMark)
+ {
+ if (IDocumentMarkAccess::MarkType::BOOKMARK ==
+ IDocumentMarkAccess::GetType(**ppMark))
+ {
+ ret.push_back((*ppMark)->GetName()); // only add real bookmarks
+ }
+ }
+ return comphelper::containerToSequence(ret);
+}
+
+sal_Bool SwXBookmarks::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ IDocumentMarkAccess* const pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ return pMarkAccess->findBookmark(rName) != pMarkAccess->getBookmarksEnd();
+}
+
+uno::Type SAL_CALL SwXBookmarks::getElementType()
+{
+ return cppu::UnoType<XTextContent>::get();
+}
+
+sal_Bool SwXBookmarks::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ IDocumentMarkAccess* const pMarkAccess = GetDoc()->getIDocumentMarkAccess();
+ for (IDocumentMarkAccess::const_iterator_t ppMark =
+ pMarkAccess->getBookmarksBegin();
+ ppMark != pMarkAccess->getBookmarksEnd(); ++ppMark)
+ {
+ if (IDocumentMarkAccess::MarkType::BOOKMARK ==
+ IDocumentMarkAccess::GetType(**ppMark))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+SwXNumberingRulesCollection::SwXNumberingRulesCollection( SwDoc* _pDoc ) :
+ SwUnoCollection(_pDoc)
+{
+}
+
+SwXNumberingRulesCollection::~SwXNumberingRulesCollection()
+{
+}
+
+sal_Int32 SwXNumberingRulesCollection::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return GetDoc()->GetNumRuleTable().size();
+}
+
+uno::Any SwXNumberingRulesCollection::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ uno::Reference< XIndexReplace > xRef;
+ if ( o3tl::make_unsigned(nIndex) < GetDoc()->GetNumRuleTable().size() )
+ {
+ xRef = new SwXNumberingRules( *GetDoc()->GetNumRuleTable()[ nIndex ], GetDoc());
+ aRet <<= xRef;
+ }
+
+ if(!xRef.is())
+ throw IndexOutOfBoundsException();
+
+ return aRet;
+}
+
+uno::Type SAL_CALL SwXNumberingRulesCollection::getElementType()
+{
+ return cppu::UnoType<XIndexReplace>::get();
+}
+
+sal_Bool SwXNumberingRulesCollection::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return !GetDoc()->GetNumRuleTable().empty();
+}
+
+OUString SwXFootnotes::getImplementationName()
+{
+ return "SwXFootnotes";
+}
+
+sal_Bool SwXFootnotes::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXFootnotes::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.Footnotes" };
+}
+
+SwXFootnotes::SwXFootnotes(bool bEnd, SwDoc* _pDoc)
+ : SwUnoCollection(_pDoc)
+ , m_bEndnote(bEnd)
+{
+}
+
+SwXFootnotes::~SwXFootnotes()
+{
+}
+
+sal_Int32 SwXFootnotes::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ sal_Int32 nCount = 0;
+ const size_t nFootnoteCnt = GetDoc()->GetFootnoteIdxs().size();
+ SwTextFootnote* pTextFootnote;
+ for( size_t n = 0; n < nFootnoteCnt; ++n )
+ {
+ pTextFootnote = GetDoc()->GetFootnoteIdxs()[ n ];
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if ( rFootnote.IsEndNote() != m_bEndnote )
+ continue;
+ nCount++;
+ }
+ return nCount;
+}
+
+uno::Any SwXFootnotes::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ sal_Int32 nCount = 0;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ const size_t nFootnoteCnt = GetDoc()->GetFootnoteIdxs().size();
+ SwTextFootnote* pTextFootnote;
+ uno::Reference< XFootnote > xRef;
+ for( size_t n = 0; n < nFootnoteCnt; ++n )
+ {
+ pTextFootnote = GetDoc()->GetFootnoteIdxs()[ n ];
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ if ( rFootnote.IsEndNote() != m_bEndnote )
+ continue;
+
+ if(nCount == nIndex)
+ {
+ xRef = SwXFootnote::CreateXFootnote(*GetDoc(),
+ &const_cast<SwFormatFootnote&>(rFootnote));
+ aRet <<= xRef;
+ break;
+ }
+ nCount++;
+ }
+ if(!xRef.is())
+ throw IndexOutOfBoundsException();
+
+ return aRet;
+}
+
+uno::Type SAL_CALL SwXFootnotes::getElementType()
+{
+ return cppu::UnoType<XFootnote>::get();
+}
+
+sal_Bool SwXFootnotes::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return !GetDoc()->GetFootnoteIdxs().empty();
+}
+
+Reference<XFootnote> SwXFootnotes::GetObject( SwDoc& rDoc, const SwFormatFootnote& rFormat )
+{
+ return SwXFootnote::CreateXFootnote(rDoc, &const_cast<SwFormatFootnote&>(rFormat));
+}
+
+OUString SwXReferenceMarks::getImplementationName()
+{
+ return "SwXReferenceMarks";
+}
+
+sal_Bool SwXReferenceMarks::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXReferenceMarks::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.ReferenceMarks" };
+}
+
+SwXReferenceMarks::SwXReferenceMarks(SwDoc* _pDoc) :
+ SwUnoCollection(_pDoc)
+{
+}
+
+SwXReferenceMarks::~SwXReferenceMarks()
+{
+}
+
+sal_Int32 SwXReferenceMarks::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return GetDoc()->GetRefMarks();
+}
+
+uno::Any SwXReferenceMarks::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ uno::Reference< XTextContent > xRef;
+ if(0 <= nIndex && nIndex < SAL_MAX_UINT16)
+ {
+ SwFormatRefMark *const pMark = const_cast<SwFormatRefMark*>(
+ GetDoc()->GetRefMark(o3tl::narrowing<sal_uInt16>(nIndex)));
+ if(pMark)
+ {
+ xRef = SwXReferenceMark::CreateXReferenceMark(*GetDoc(), pMark);
+ aRet <<= xRef;
+ }
+ }
+ if(!xRef.is())
+ throw IndexOutOfBoundsException();
+ return aRet;
+}
+
+uno::Any SwXReferenceMarks::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ SwFormatRefMark *const pMark =
+ const_cast<SwFormatRefMark*>(GetDoc()->GetRefMark(rName));
+ if(!pMark)
+ throw NoSuchElementException();
+
+ uno::Reference<XTextContent> const xRef =
+ SwXReferenceMark::CreateXReferenceMark(*GetDoc(), pMark);
+ aRet <<= xRef;
+
+ return aRet;
+}
+
+uno::Sequence< OUString > SwXReferenceMarks::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<OUString> aRet;
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ std::vector<OUString> aStrings;
+ const sal_uInt16 nCount = GetDoc()->GetRefMarks( &aStrings );
+ aRet.realloc(nCount);
+ OUString* pNames = aRet.getArray();
+ for(sal_uInt16 i = 0; i < nCount; i++)
+ pNames[i] = aStrings[i];
+
+ return aRet;
+}
+
+sal_Bool SwXReferenceMarks::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return nullptr != GetDoc()->GetRefMark( rName);
+}
+
+uno::Type SAL_CALL SwXReferenceMarks::getElementType()
+{
+ return cppu::UnoType<XTextContent>::get();
+}
+
+sal_Bool SwXReferenceMarks::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return 0 != GetDoc()->GetRefMarks();
+}
+
+void SwUnoCollection::Invalidate()
+{
+ m_bObjectValid = false;
+ m_pDoc = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unocontentcontrol.cxx b/sw/source/core/unocore/unocontentcontrol.cxx
new file mode 100644
index 000000000..ccaddf0d0
--- /dev/null
+++ b/sw/source/core/unocore/unocontentcontrol.cxx
@@ -0,0 +1,1149 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unocontentcontrol.hxx>
+
+#include <mutex>
+
+#include <com/sun/star/text/XWordCursor.hpp>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <formatcontentcontrol.hxx>
+#include <ndtxt.hxx>
+#include <textcontentcontrol.hxx>
+#include <unotext.hxx>
+#include <unotextcursor.hxx>
+#include <unotextrange.hxx>
+#include <doc.hxx>
+#include <unoport.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/// UNO API wrapper around the text inside an SwXContentControl.
+class SwXContentControlText : public cppu::OWeakObject, public SwXText
+{
+private:
+ SwXContentControl& m_rContentControl;
+
+ void PrepareForAttach(uno::Reference<text::XTextRange>& xRange, const SwPaM& rPam) override;
+
+protected:
+ const SwStartNode* GetStartNode() const override;
+ uno::Reference<text::XTextCursor> CreateCursor() override;
+
+public:
+ SwXContentControlText(SwDoc& rDoc, SwXContentControl& rContentControl);
+
+ /// SwXText::Invalidate() is protected.
+ using SwXText::Invalidate;
+
+ // XInterface
+ void SAL_CALL acquire() noexcept override { cppu::OWeakObject::acquire(); }
+ void SAL_CALL release() noexcept override { cppu::OWeakObject::release(); }
+
+ // XTypeProvider
+ uno::Sequence<sal_Int8> SAL_CALL getImplementationId() override;
+
+ // XText
+ uno::Reference<text::XTextCursor> SAL_CALL createTextCursor() override;
+ uno::Reference<text::XTextCursor> SAL_CALL
+ createTextCursorByRange(const uno::Reference<text::XTextRange>& xTextPosition) override;
+};
+}
+
+SwXContentControlText::SwXContentControlText(SwDoc& rDoc, SwXContentControl& rContentControl)
+ : SwXText(&rDoc, CursorType::ContentControl)
+ , m_rContentControl(rContentControl)
+{
+}
+
+const SwStartNode* SwXContentControlText::GetStartNode() const
+{
+ auto pParent = dynamic_cast<SwXText*>(m_rContentControl.GetParentText().get());
+ return pParent ? pParent->GetStartNode() : nullptr;
+}
+
+void SwXContentControlText::PrepareForAttach(uno::Reference<text::XTextRange>& xRange,
+ const SwPaM& rPam)
+{
+ // Create a new cursor to prevent modifying SwXTextRange.
+ xRange = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), &m_rContentControl, CursorType::ContentControl,
+ *rPam.GetPoint(), (rPam.HasMark()) ? rPam.GetMark() : nullptr));
+}
+
+uno::Reference<text::XTextCursor> SwXContentControlText::CreateCursor()
+{
+ uno::Reference<text::XTextCursor> xRet;
+ if (IsValid())
+ {
+ SwTextNode* pTextNode;
+ sal_Int32 nContentControlStart;
+ sal_Int32 nContentControlEnd;
+ bool bSuccess = m_rContentControl.SetContentRange(pTextNode, nContentControlStart,
+ nContentControlEnd);
+ if (bSuccess)
+ {
+ SwPosition aPos(*pTextNode, nContentControlStart);
+ xRet = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), &m_rContentControl, CursorType::ContentControl, aPos));
+ }
+ }
+ return xRet;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL SwXContentControlText::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XText
+uno::Reference<text::XTextCursor> SAL_CALL SwXContentControlText::createTextCursor()
+{
+ return CreateCursor();
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL SwXContentControlText::createTextCursorByRange(
+ const uno::Reference<text::XTextRange>& xTextPosition)
+{
+ const uno::Reference<text::XTextCursor> xCursor(CreateCursor());
+ xCursor->gotoRange(xTextPosition, false);
+ return xCursor;
+}
+
+/**
+ * The inner part SwXContentControl, which is deleted with a locked SolarMutex.
+ *
+ * The content control has a cached list of text portions for its contents. This list is created by
+ * SwXTextPortionEnumeration. The content control listens at the SwTextNode and throws away the
+ * cache when the text node changes.
+ */
+class SwXContentControl::Impl : public SvtListener
+{
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ // Just for OInterfaceContainerHelper4.
+ std::mutex m_Mutex;
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ std::unique_ptr<const TextRangeList_t> m_pTextPortions;
+ // 3 possible states: not attached, attached, disposed
+ bool m_bIsDisposed;
+ bool m_bIsDescriptor;
+ uno::Reference<text::XText> m_xParentText;
+ rtl::Reference<SwXContentControlText> m_xText;
+ SwContentControl* m_pContentControl;
+ bool m_bShowingPlaceHolder;
+ bool m_bCheckbox;
+ bool m_bChecked;
+ OUString m_aCheckedState;
+ OUString m_aUncheckedState;
+ std::vector<SwContentControlListItem> m_aListItems;
+ bool m_bPicture;
+ bool m_bDate;
+ OUString m_aDateFormat;
+ OUString m_aDateLanguage;
+ OUString m_aCurrentDate;
+ OUString m_aPlaceholderDocPart;
+ OUString m_aDataBindingPrefixMappings;
+ OUString m_aDataBindingXpath;
+ OUString m_aDataBindingStoreItemID;
+ OUString m_aColor;
+
+ Impl(SwXContentControl& rThis, SwDoc& rDoc, SwContentControl* pContentControl,
+ const uno::Reference<text::XText>& xParentText,
+ std::unique_ptr<const TextRangeList_t> pPortions)
+ : m_pTextPortions(std::move(pPortions))
+ , m_bIsDisposed(false)
+ , m_bIsDescriptor(pContentControl == nullptr)
+ , m_xParentText(xParentText)
+ , m_xText(new SwXContentControlText(rDoc, rThis))
+ , m_pContentControl(pContentControl)
+ , m_bShowingPlaceHolder(false)
+ , m_bCheckbox(false)
+ , m_bChecked(false)
+ , m_bPicture(false)
+ , m_bDate(false)
+ {
+ if (m_pContentControl)
+ {
+ StartListening(m_pContentControl->GetNotifier());
+ }
+ }
+
+ const SwContentControl* GetContentControl() const;
+
+protected:
+ void Notify(const SfxHint& rHint) override;
+};
+
+const SwContentControl* SwXContentControl::Impl::GetContentControl() const
+{
+ return m_pContentControl;
+}
+
+// sw::BroadcastingModify
+void SwXContentControl::Impl::Notify(const SfxHint& rHint)
+{
+ // throw away cache (SwTextNode changed)
+ m_pTextPortions.reset();
+
+ if (rHint.GetId() != SfxHintId::Dying && rHint.GetId() != SfxHintId::Deinitializing)
+ return;
+
+ m_bIsDisposed = true;
+ m_pContentControl = nullptr;
+ m_xText->Invalidate();
+ uno::Reference<uno::XInterface> xThis(m_wThis);
+ if (!xThis.is())
+ {
+ // If UNO object is already dead, don't refer to it in an event.
+ return;
+ }
+ lang::EventObject aEvent(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, aEvent);
+}
+
+const uno::Reference<text::XText>& SwXContentControl::GetParentText() const
+{
+ return m_pImpl->m_xParentText;
+}
+
+SwXContentControl::SwXContentControl(SwDoc* pDoc, SwContentControl* pContentControl,
+ const uno::Reference<text::XText>& xParentText,
+ std::unique_ptr<const TextRangeList_t> pPortions)
+ : m_pImpl(new SwXContentControl::Impl(*this, *pDoc, pContentControl, xParentText,
+ std::move(pPortions)))
+{
+}
+
+SwXContentControl::SwXContentControl(SwDoc* pDoc)
+ : m_pImpl(new SwXContentControl::Impl(*this, *pDoc, nullptr, nullptr, nullptr))
+{
+}
+
+SwXContentControl::~SwXContentControl() {}
+
+uno::Reference<text::XTextContent> SwXContentControl::CreateXContentControl(SwDoc& rDoc)
+{
+ rtl::Reference<SwXContentControl> xContentControl(new SwXContentControl(&rDoc));
+ uno::Reference<text::XTextContent> xTextContent(xContentControl);
+ xContentControl->m_pImpl->m_wThis = xTextContent;
+ return xContentControl;
+}
+
+uno::Reference<text::XTextContent>
+SwXContentControl::CreateXContentControl(SwContentControl& rContentControl,
+ const uno::Reference<text::XText>& xParent,
+ std::unique_ptr<const TextRangeList_t>&& pPortions)
+{
+ // re-use existing SwXContentControl
+ uno::Reference<text::XTextContent> xContentControl(rContentControl.GetXContentControl());
+ if (xContentControl.is())
+ {
+ if (pPortions)
+ {
+ // Set the cache in the XContentControl to the given portions.
+ auto pXContentControl
+ = comphelper::getFromUnoTunnel<SwXContentControl>(xContentControl);
+ assert(pXContentControl);
+ // The content control must always be created with the complete content. If
+ // SwXTextPortionEnumeration is created for a selection, it must be checked that the
+ // content control is contained in the selection.
+ pXContentControl->m_pImpl->m_pTextPortions = std::move(pPortions);
+ if (pXContentControl->m_pImpl->m_xParentText.get() != xParent.get())
+ {
+ SAL_WARN("sw.uno", "SwXContentControl with different parent");
+ pXContentControl->m_pImpl->m_xParentText.set(xParent);
+ }
+ }
+ return xContentControl;
+ }
+
+ // Create new SwXContentControl.
+ SwTextNode* pTextNode = rContentControl.GetTextNode();
+ if (!pTextNode)
+ {
+ SAL_WARN("sw.uno", "CreateXContentControl: no text node");
+ return nullptr;
+ }
+ uno::Reference<text::XText> xParentText(xParent);
+ if (!xParentText.is())
+ {
+ SwTextContentControl* pTextAttr = rContentControl.GetTextAttr();
+ if (!pTextAttr)
+ {
+ SAL_WARN("sw.uno", "CreateXContentControl: no text attr");
+ return nullptr;
+ }
+ SwPosition aPos(*pTextNode, pTextAttr->GetStart());
+ xParentText.set(sw::CreateParentXText(pTextNode->GetDoc(), aPos));
+ }
+ if (!xParentText.is())
+ {
+ return nullptr;
+ }
+ rtl::Reference<SwXContentControl> pXContentControl = new SwXContentControl(
+ &pTextNode->GetDoc(), &rContentControl, xParentText, std::move(pPortions));
+ xContentControl.set(pXContentControl);
+ rContentControl.SetXContentControl(xContentControl);
+ pXContentControl->m_pImpl->m_wThis = xContentControl;
+ return xContentControl;
+}
+
+bool SwXContentControl::SetContentRange(SwTextNode*& rpNode, sal_Int32& rStart,
+ sal_Int32& rEnd) const
+{
+ const SwContentControl* pContentControl = m_pImpl->GetContentControl();
+ if (pContentControl)
+ {
+ const SwTextContentControl* pTextAttr = pContentControl->GetTextAttr();
+ if (pTextAttr)
+ {
+ rpNode = pContentControl->GetTextNode();
+ if (rpNode)
+ {
+ // rStart points at the first position within the content control.
+ rStart = pTextAttr->GetStart() + 1;
+ // rEnd points at the last position within the content control.
+ rEnd = *pTextAttr->End() - 1;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+const uno::Sequence<sal_Int8>& SwXContentControl::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXContentControlUnoTunnelId;
+ return theSwXContentControlUnoTunnelId.getSeq();
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL SwXContentControl::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl<SwXContentControl>(rId, this);
+}
+
+// XServiceInfo
+OUString SAL_CALL SwXContentControl::getImplementationName() { return "SwXContentControl"; }
+
+sal_Bool SAL_CALL SwXContentControl::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SwXContentControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextContent", "com.sun.star.text.ContentControl" };
+}
+
+// XComponent
+void SAL_CALL
+SwXContentControl::addEventListener(const uno::Reference<lang::XEventListener>& xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL
+SwXContentControl::removeEventListener(const uno::Reference<lang::XEventListener>& xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXContentControl::dispose()
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pTextPortions.reset();
+ lang::EventObject aEvent(static_cast<::cppu::OWeakObject&>(*this));
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.disposeAndClear(aGuard, aEvent);
+ m_pImpl->m_bIsDisposed = true;
+ m_pImpl->m_xText->Invalidate();
+ }
+ else if (!m_pImpl->m_bIsDisposed)
+ {
+ SwTextNode* pTextNode;
+ sal_Int32 nContentControlStart;
+ sal_Int32 nContentControlEnd;
+ bool bSuccess = SetContentRange(pTextNode, nContentControlStart, nContentControlEnd);
+ if (!bSuccess)
+ {
+ SAL_WARN("sw.core", "SwXContentControl::dispose: no pam");
+ }
+ else
+ {
+ // -1 because of CH_TXTATR
+ SwPaM aPam(*pTextNode, nContentControlStart - 1, *pTextNode, nContentControlEnd);
+ SwDoc& rDoc(pTextNode->GetDoc());
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(aPam);
+
+ // removal should call Modify and do the dispose
+ assert(m_pImpl->m_bIsDisposed);
+ }
+ }
+}
+
+void SwXContentControl::AttachImpl(const uno::Reference<text::XTextRange>& xTextRange,
+ sal_uInt16 nWhich)
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pImpl->m_bIsDisposed)
+ {
+ throw lang::DisposedException();
+ }
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException("SwXContentControl::AttachImpl(): already attached",
+ static_cast<::cppu::OWeakObject*>(this));
+ }
+
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel(xTextRange, uno::UNO_QUERY);
+ if (!xRangeTunnel.is())
+ {
+ throw lang::IllegalArgumentException(
+ "SwXContentControl::AttachImpl(): argument is no XUnoTunnel",
+ static_cast<::cppu::OWeakObject*>(this), 0);
+ }
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor
+ = pRange ? nullptr : comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+ if (!pRange && !pCursor)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXContentControl::AttachImpl(): argument not supported type",
+ static_cast<::cppu::OWeakObject*>(this), 0);
+ }
+
+ SwDoc* pDoc = pRange ? &pRange->GetDoc() : pCursor->GetDoc();
+ if (!pDoc)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXContentControl::AttachImpl(): argument has no SwDoc",
+ static_cast<::cppu::OWeakObject*>(this), 0);
+ }
+
+ SwUnoInternalPaM aPam(*pDoc);
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+
+ UnoActionContext aContext(pDoc);
+
+ auto pTextCursor = dynamic_cast<SwXTextCursor*>(pCursor);
+ bool bForceExpandHints = pTextCursor && pTextCursor->IsAtEndOfContentControl();
+ SetAttrMode nInsertFlags = bForceExpandHints
+ ? (SetAttrMode::FORCEHINTEXPAND | SetAttrMode::DONTEXPAND)
+ : SetAttrMode::DONTEXPAND;
+
+ auto pContentControl = std::make_shared<SwContentControl>(nullptr);
+
+ pContentControl->SetShowingPlaceHolder(m_pImpl->m_bShowingPlaceHolder);
+ pContentControl->SetCheckbox(m_pImpl->m_bCheckbox);
+ pContentControl->SetChecked(m_pImpl->m_bChecked);
+ pContentControl->SetCheckedState(m_pImpl->m_aCheckedState);
+ pContentControl->SetUncheckedState(m_pImpl->m_aUncheckedState);
+ pContentControl->SetListItems(m_pImpl->m_aListItems);
+ pContentControl->SetPicture(m_pImpl->m_bPicture);
+ pContentControl->SetDate(m_pImpl->m_bDate);
+ pContentControl->SetDateFormat(m_pImpl->m_aDateFormat);
+ pContentControl->SetDateLanguage(m_pImpl->m_aDateLanguage);
+ pContentControl->SetCurrentDate(m_pImpl->m_aCurrentDate);
+ pContentControl->SetPlaceholderDocPart(m_pImpl->m_aPlaceholderDocPart);
+ pContentControl->SetDataBindingPrefixMappings(m_pImpl->m_aDataBindingPrefixMappings);
+ pContentControl->SetDataBindingXpath(m_pImpl->m_aDataBindingXpath);
+ pContentControl->SetDataBindingStoreItemID(m_pImpl->m_aDataBindingStoreItemID);
+ pContentControl->SetColor(m_pImpl->m_aColor);
+
+ SwFormatContentControl aContentControl(pContentControl, nWhich);
+ bool bSuccess
+ = pDoc->getIDocumentContentOperations().InsertPoolItem(aPam, aContentControl, nInsertFlags);
+ SwTextAttr* pTextAttr = pContentControl->GetTextAttr();
+ if (!bSuccess)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXContentControl::AttachImpl(): cannot create content control: invalid range",
+ static_cast<::cppu::OWeakObject*>(this), 1);
+ }
+ if (!pTextAttr)
+ {
+ SAL_WARN("sw.core", "content control inserted, but has no text attribute?");
+ throw uno::RuntimeException(
+ "SwXContentControl::AttachImpl(): cannot create content control",
+ static_cast<::cppu::OWeakObject*>(this));
+ }
+
+ m_pImpl->EndListeningAll();
+ m_pImpl->m_pContentControl = pContentControl.get();
+ m_pImpl->StartListening(pContentControl->GetNotifier());
+ pContentControl->SetXContentControl(uno::Reference<text::XTextContent>(this));
+
+ m_pImpl->m_xParentText = sw::CreateParentXText(*pDoc, *aPam.GetPoint());
+
+ m_pImpl->m_bIsDescriptor = false;
+}
+
+// XTextContent
+void SAL_CALL SwXContentControl::attach(const uno::Reference<text::XTextRange>& xTextRange)
+{
+ return SwXContentControl::AttachImpl(xTextRange, RES_TXTATR_CONTENTCONTROL);
+}
+
+uno::Reference<text::XTextRange> SAL_CALL SwXContentControl::getAnchor()
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDisposed)
+ {
+ throw lang::DisposedException();
+ }
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException("SwXContentControl::getAnchor(): not inserted",
+ static_cast<::cppu::OWeakObject*>(this));
+ }
+
+ SwTextNode* pTextNode;
+ sal_Int32 nContentControlStart;
+ sal_Int32 nContentControlEnd;
+ bool bSuccess = SetContentRange(pTextNode, nContentControlStart, nContentControlEnd);
+ if (!bSuccess)
+ {
+ SAL_WARN("sw.core", "no pam");
+ throw lang::DisposedException("SwXContentControl::getAnchor(): not attached",
+ static_cast<::cppu::OWeakObject*>(this));
+ }
+
+ SwPosition aStart(*pTextNode, nContentControlStart - 1); // -1 due to CH_TXTATR
+ SwPosition aEnd(*pTextNode, nContentControlEnd);
+ return SwXTextRange::CreateXTextRange(pTextNode->GetDoc(), aStart, &aEnd);
+}
+
+// XTextRange
+uno::Reference<text::XText> SAL_CALL SwXContentControl::getText() { return this; }
+
+uno::Reference<text::XTextRange> SAL_CALL SwXContentControl::getStart()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->getStart();
+}
+
+uno::Reference<text::XTextRange> SAL_CALL SwXContentControl::getEnd()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->getEnd();
+}
+
+OUString SAL_CALL SwXContentControl::getString()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->getString();
+}
+
+void SAL_CALL SwXContentControl::setString(const OUString& rString)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->setString(rString);
+}
+
+// XSimpleText
+uno::Reference<text::XTextCursor> SAL_CALL SwXContentControl::createTextCursor()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->createTextCursor();
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL
+SwXContentControl::createTextCursorByRange(const uno::Reference<text::XTextRange>& xTextPosition)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->createTextCursorByRange(xTextPosition);
+}
+
+void SAL_CALL SwXContentControl::insertString(const uno::Reference<text::XTextRange>& xRange,
+ const OUString& rString, sal_Bool bAbsorb)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->insertString(xRange, rString, bAbsorb);
+}
+
+void SAL_CALL SwXContentControl::insertControlCharacter(
+ const uno::Reference<text::XTextRange>& xRange, sal_Int16 nControlCharacter, sal_Bool bAbsorb)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->insertControlCharacter(xRange, nControlCharacter, bAbsorb);
+}
+
+// XText
+void SAL_CALL SwXContentControl::insertTextContent(
+ const uno::Reference<text::XTextRange>& xRange,
+ const uno::Reference<text::XTextContent>& xContent, sal_Bool bAbsorb)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->insertTextContent(xRange, xContent, bAbsorb);
+}
+
+void SAL_CALL
+SwXContentControl::removeTextContent(const uno::Reference<text::XTextContent>& xContent)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->removeTextContent(xContent);
+}
+
+// XPropertySet
+uno::Reference<beans::XPropertySetInfo> SAL_CALL SwXContentControl::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+
+ static uno::Reference<beans::XPropertySetInfo> xRet
+ = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CONTENTCONTROL)->getPropertySetInfo();
+ return xRet;
+}
+
+void SAL_CALL SwXContentControl::setPropertyValue(const OUString& rPropertyName,
+ const css::uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ if (rPropertyName == UNO_NAME_SHOWING_PLACE_HOLDER)
+ {
+ bool bValue;
+ if (rValue >>= bValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_bShowingPlaceHolder = bValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetShowingPlaceHolder(bValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CHECKBOX)
+ {
+ bool bValue;
+ if (rValue >>= bValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_bCheckbox = bValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetCheckbox(bValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CHECKED)
+ {
+ bool bValue;
+ if (rValue >>= bValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_bChecked = bValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetChecked(bValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CHECKED_STATE)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aCheckedState = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetCheckedState(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_UNCHECKED_STATE)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aUncheckedState = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetUncheckedState(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_LIST_ITEMS)
+ {
+ std::vector<SwContentControlListItem> aItems
+ = SwContentControlListItem::ItemsFromAny(rValue);
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aListItems = aItems;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetListItems(aItems);
+ }
+ }
+ else if (rPropertyName == UNO_NAME_PICTURE)
+ {
+ bool bValue;
+ if (rValue >>= bValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_bPicture = bValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetPicture(bValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATE)
+ {
+ bool bValue;
+ if (rValue >>= bValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_bDate = bValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetDate(bValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATE_FORMAT)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aDateFormat = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetDateFormat(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATE_LANGUAGE)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aDateLanguage = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetDateLanguage(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CURRENT_DATE)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aCurrentDate = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetCurrentDate(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_PLACEHOLDER_DOC_PART)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aPlaceholderDocPart = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetPlaceholderDocPart(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aDataBindingPrefixMappings = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetDataBindingPrefixMappings(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATA_BINDING_XPATH)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aDataBindingXpath = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetDataBindingXpath(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATA_BINDING_STORE_ITEM_ID)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aDataBindingStoreItemID = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetDataBindingStoreItemID(aValue);
+ }
+ }
+ }
+ else if (rPropertyName == UNO_NAME_COLOR)
+ {
+ OUString aValue;
+ if (rValue >>= aValue)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_aColor = aValue;
+ }
+ else
+ {
+ m_pImpl->m_pContentControl->SetColor(aValue);
+ }
+ }
+ }
+ else
+ {
+ throw beans::UnknownPropertyException();
+ }
+}
+
+uno::Any SAL_CALL SwXContentControl::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ if (rPropertyName == UNO_NAME_SHOWING_PLACE_HOLDER)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_bShowingPlaceHolder;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetShowingPlaceHolder();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CHECKBOX)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_bCheckbox;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetCheckbox();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CHECKED)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_bChecked;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetChecked();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CHECKED_STATE)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aCheckedState;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetCheckedState();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_UNCHECKED_STATE)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aUncheckedState;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetUncheckedState();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_LIST_ITEMS)
+ {
+ std::vector<SwContentControlListItem> aItems;
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aItems = m_pImpl->m_aListItems;
+ }
+ else
+ {
+ aItems = m_pImpl->m_pContentControl->GetListItems();
+ }
+ SwContentControlListItem::ItemsToAny(aItems, aRet);
+ }
+ else if (rPropertyName == UNO_NAME_PICTURE)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_bPicture;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetPicture();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATE)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_bDate;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetDate();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATE_FORMAT)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aDateFormat;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetDateFormat();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATE_LANGUAGE)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aDateLanguage;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetDateLanguage();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_CURRENT_DATE)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aCurrentDate;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetCurrentDate();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_PLACEHOLDER_DOC_PART)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aPlaceholderDocPart;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetCurrentDate();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aDataBindingPrefixMappings;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetDataBindingPrefixMappings();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATA_BINDING_XPATH)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aDataBindingXpath;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetDataBindingXpath();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_DATA_BINDING_STORE_ITEM_ID)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aDataBindingStoreItemID;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetDataBindingStoreItemID();
+ }
+ }
+ else if (rPropertyName == UNO_NAME_COLOR)
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ aRet <<= m_pImpl->m_aColor;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pContentControl->GetColor();
+ }
+ }
+ else
+ {
+ throw beans::UnknownPropertyException();
+ }
+
+ return aRet;
+}
+
+void SAL_CALL SwXContentControl::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXContentControl::addPropertyChangeListener: not implemented");
+}
+
+void SAL_CALL SwXContentControl::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXContentControl::removePropertyChangeListener: not implemented");
+}
+
+void SAL_CALL SwXContentControl::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXContentControl::addVetoableChangeListener: not implemented");
+}
+
+void SAL_CALL SwXContentControl::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXContentControl::removeVetoableChangeListener: not implemented");
+}
+
+// XElementAccess
+uno::Type SAL_CALL SwXContentControl::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXContentControl::hasElements()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_pContentControl != nullptr;
+}
+
+// XEnumerationAccess
+uno::Reference<container::XEnumeration> SAL_CALL SwXContentControl::createEnumeration()
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDisposed)
+ {
+ throw lang::DisposedException();
+ }
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException("createEnumeration(): not inserted",
+ static_cast<::cppu::OWeakObject*>(this));
+ }
+
+ SwTextNode* pTextNode;
+ sal_Int32 nContentControlStart;
+ sal_Int32 nContentControlEnd;
+ bool bSuccess = SetContentRange(pTextNode, nContentControlStart, nContentControlEnd);
+ if (!bSuccess)
+ {
+ SAL_WARN("sw.core", "no pam");
+ throw lang::DisposedException();
+ }
+
+ SwPaM aPam(*pTextNode, nContentControlStart);
+
+ if (!m_pImpl->m_pTextPortions)
+ {
+ return new SwXTextPortionEnumeration(aPam, GetParentText(), nContentControlStart,
+ nContentControlEnd);
+ }
+ else
+ {
+ return new SwXTextPortionEnumeration(aPam, std::deque(*m_pImpl->m_pTextPortions));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unocrsr.cxx b/sw/source/core/unocore/unocrsr.cxx
new file mode 100644
index 000000000..bbb70f153
--- /dev/null
+++ b/sw/source/core/unocore/unocrsr.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unocrsr.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <swtable.hxx>
+#include <rootfrm.hxx>
+
+sw::UnoCursorHint::~UnoCursorHint() {}
+
+SwUnoCursor::SwUnoCursor( const SwPosition &rPos )
+ : SwCursor( rPos, nullptr )
+ , m_bRemainInSection(true)
+ , m_bSkipOverHiddenSections(false)
+ , m_bSkipOverProtectSections(false)
+{}
+
+SwUnoCursor::~SwUnoCursor()
+{
+ SwDoc& rDoc = GetDoc();
+ if( !rDoc.IsInDtor() )
+ {
+ assert(!m_aNotifier.HasListeners());
+ }
+
+ // delete the whole ring
+ while( GetNext() != this )
+ {
+ Ring* pNxt = GetNextInRing();
+ pNxt->MoveTo(nullptr); // remove from chain
+ delete pNxt; // and delete
+ }
+}
+
+bool SwUnoCursor::IsReadOnlyAvailable() const
+{
+ return true;
+}
+
+const SwContentFrame*
+SwUnoCursor::DoSetBidiLevelLeftRight( bool &, bool, bool )
+{
+ return nullptr; // not for uno cursor
+}
+
+void SwUnoCursor::DoSetBidiLevelUpDown()
+{
+ // not for uno cursor
+}
+
+bool SwUnoCursor::IsSelOvr( SwCursorSelOverFlags eFlags )
+{
+ if (m_bRemainInSection)
+ {
+ SwDoc& rDoc = GetDoc();
+ SwNodeIndex aOldIdx( *rDoc.GetNodes()[ GetSavePos()->nNode ] );
+ SwNodeIndex& rPtIdx = GetPoint()->nNode;
+ SwStartNode *pOldSttNd = aOldIdx.GetNode().StartOfSectionNode(),
+ *pNewSttNd = rPtIdx.GetNode().StartOfSectionNode();
+ if( pOldSttNd != pNewSttNd )
+ {
+ bool bMoveDown = GetSavePos()->nNode < rPtIdx.GetIndex();
+ bool bValidPos = false;
+
+ // search the correct surrounded start node - which the index
+ // can't leave.
+ while( pOldSttNd->IsSectionNode() )
+ pOldSttNd = pOldSttNd->StartOfSectionNode();
+
+ // is the new index inside this surrounded section?
+ if( rPtIdx > *pOldSttNd &&
+ rPtIdx < pOldSttNd->EndOfSectionIndex() )
+ {
+ // check if it a valid move inside this section
+ // (only over SwSection's !)
+ const SwStartNode* pInvalidNode;
+ do {
+ pInvalidNode = nullptr;
+ pNewSttNd = rPtIdx.GetNode().StartOfSectionNode();
+
+ const SwStartNode *pSttNd = pNewSttNd, *pEndNd = pOldSttNd;
+ if( pSttNd->EndOfSectionIndex() >
+ pEndNd->EndOfSectionIndex() )
+ {
+ pEndNd = pNewSttNd;
+ pSttNd = pOldSttNd;
+ }
+
+ while( pSttNd->GetIndex() > pEndNd->GetIndex() )
+ {
+ if( !pSttNd->IsSectionNode() )
+ pInvalidNode = pSttNd;
+ pSttNd = pSttNd->StartOfSectionNode();
+ }
+ if( pInvalidNode )
+ {
+ if( bMoveDown )
+ {
+ rPtIdx.Assign( *pInvalidNode->EndOfSectionNode(), 1 );
+
+ if( !rPtIdx.GetNode().IsContentNode() &&
+ ( !rDoc.GetNodes().GoNextSection( &rPtIdx ) ||
+ rPtIdx > pOldSttNd->EndOfSectionIndex() ) )
+ break;
+ }
+ else
+ {
+ rPtIdx.Assign( *pInvalidNode, -1 );
+
+ if( !rPtIdx.GetNode().IsContentNode() &&
+ ( !SwNodes::GoPrevSection( &rPtIdx ) ||
+ rPtIdx < *pOldSttNd ) )
+ break;
+ }
+ }
+ else
+ bValidPos = true;
+ } while ( pInvalidNode );
+ }
+
+ if( bValidPos )
+ {
+ SwContentNode* pCNd = GetContentNode();
+ GetPoint()->nContent.Assign( pCNd, (pCNd && !bMoveDown) ? pCNd->Len() : 0);
+ }
+ else
+ {
+ rPtIdx = GetSavePos()->nNode;
+ GetPoint()->nContent.Assign( GetContentNode(), GetSavePos()->nContent );
+ return true;
+ }
+ }
+ }
+ return SwCursor::IsSelOvr( eFlags );
+}
+
+SwUnoTableCursor::SwUnoTableCursor(const SwPosition& rPos)
+ : SwCursor(rPos, nullptr)
+ , SwUnoCursor(rPos)
+ , SwTableCursor(rPos)
+ , m_aTableSel(rPos, nullptr)
+{
+ SetRemainInSection(false);
+}
+
+SwUnoTableCursor::~SwUnoTableCursor()
+{
+ while (m_aTableSel.GetNext() != &m_aTableSel)
+ delete m_aTableSel.GetNext();
+}
+
+bool SwUnoTableCursor::IsSelOvr( SwCursorSelOverFlags eFlags )
+{
+ bool bRet = SwUnoCursor::IsSelOvr( eFlags );
+ if( !bRet )
+ {
+ const SwTableNode* pTNd = GetPoint()->nNode.GetNode().FindTableNode();
+ bRet = !(pTNd == GetDoc().GetNodes()[ GetSavePos()->nNode ]->
+ FindTableNode() && (!HasMark() ||
+ pTNd == GetMark()->nNode.GetNode().FindTableNode() ));
+ }
+ return bRet;
+}
+
+void SwUnoTableCursor::MakeBoxSels()
+{
+ const SwContentNode* pCNd;
+ bool bMakeTableCursors = true;
+ if( GetPoint()->nNode.GetIndex() && GetMark()->nNode.GetIndex() &&
+ nullptr != ( pCNd = GetContentNode() ) && pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) &&
+ nullptr != ( pCNd = GetContentNode(false) ) && pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() ) )
+ bMakeTableCursors = GetDoc().getIDocumentLayoutAccess().GetCurrentLayout()->MakeTableCursors( *this );
+
+ if ( !bMakeTableCursors )
+ {
+ SwSelBoxes const& rTmpBoxes = GetSelectedBoxes();
+ while (!rTmpBoxes.empty())
+ {
+ DeleteBox(0);
+ }
+ }
+
+ if( IsChgd() )
+ {
+ SwTableCursor::MakeBoxSels( &m_aTableSel );
+ if (!GetSelectedBoxesCount())
+ {
+ const SwTableBox* pBox;
+ const SwNode* pBoxNd = GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ const SwTableNode* pTableNd = pBoxNd ? pBoxNd->FindTableNode() : nullptr;
+ if( pTableNd && nullptr != ( pBox = pTableNd->GetTable().GetTableBox( pBoxNd->GetIndex() )) )
+ InsertBox( *pBox );
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unocrsrhelper.cxx b/sw/source/core/unocore/unocrsrhelper.cxx
new file mode 100644
index 000000000..b176138a0
--- /dev/null
+++ b/sw/source/core/unocore/unocrsrhelper.cxx
@@ -0,0 +1,1551 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unocrsrhelper.hxx>
+
+#include <map>
+#include <algorithm>
+#include <memory>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XStorage.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <svx/unoshape.hxx>
+
+#include <cmdid.h>
+#include <hintids.hxx>
+#include <unotextrange.hxx>
+#include <unodraw.hxx>
+#include <unofootnote.hxx>
+#include <unobookmark.hxx>
+#include <unomap.hxx>
+#include <unorefmark.hxx>
+#include <unoidx.hxx>
+#include <unofield.hxx>
+#include <unotbl.hxx>
+#include <unosett.hxx>
+#include <unoframe.hxx>
+#include <unocrsr.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fmtftn.hxx>
+#include <charfmt.hxx>
+#include <pagedesc.hxx>
+#include <docstyle.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <section.hxx>
+#include <shellio.hxx>
+#include <edimp.hxx>
+#include <swundo.hxx>
+#include <cntfrm.hxx>
+#include <pagefrm.hxx>
+#include <svl/eitem.hxx>
+#include <svl/lngmisc.hxx>
+#include <swtable.hxx>
+#include <tox.hxx>
+#include <doctxm.hxx>
+#include <fchrfmt.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/flstitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <vcl/metric.hxx>
+#include <svtools/ctrltool.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svl/stritem.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <redline.hxx>
+#include <numrule.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <SwNodeNum.hxx>
+#include <fmtmeta.hxx>
+#include <txtfld.hxx>
+#include <unoparagraph.hxx>
+#include <poolfmt.hxx>
+#include <paratr.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+namespace SwUnoCursorHelper
+{
+
+static SwPaM* lcl_createPamCopy(const SwPaM& rPam)
+{
+ SwPaM *const pRet = new SwPaM(*rPam.GetPoint());
+ ::sw::DeepCopyPaM(rPam, *pRet);
+ return pRet;
+}
+
+void GetSelectableFromAny(uno::Reference<uno::XInterface> const& xIfc,
+ SwDoc & rTargetDoc,
+ SwPaM *& o_rpPaM, std::pair<OUString, FlyCntType> & o_rFrame,
+ OUString & o_rTableName, SwUnoTableCursor const*& o_rpTableCursor,
+ ::sw::mark::IMark const*& o_rpMark,
+ std::vector<SdrObject *> & o_rSdrObjects)
+{
+ uno::Reference<drawing::XShapes> const xShapes(xIfc, UNO_QUERY);
+ if (xShapes.is())
+ {
+ sal_Int32 nShapes(xShapes->getCount());
+ for (sal_Int32 i = 0; i < nShapes; ++i)
+ {
+ uno::Reference<lang::XUnoTunnel> xShape;
+ xShapes->getByIndex(i) >>= xShape;
+ if (xShape.is())
+ {
+ SvxShape *const pSvxShape(
+ comphelper::getFromUnoTunnel<SvxShape>(xShape));
+ if (pSvxShape)
+ {
+ SdrObject *const pSdrObject = pSvxShape->GetSdrObject();
+ if (pSdrObject)
+ { // hmm... needs view to verify it's in right doc...
+ o_rSdrObjects.push_back(pSdrObject);
+ }
+ }
+ }
+ }
+ return;
+ }
+
+ uno::Reference<lang::XUnoTunnel> const xTunnel(xIfc, UNO_QUERY);
+ if (!xTunnel.is()) // everything below needs tunnel
+ {
+ return;
+ }
+
+ SwXShape *const pShape(comphelper::getFromUnoTunnel<SwXShape>(xTunnel));
+ if (pShape)
+ {
+ uno::Reference<uno::XAggregation> const xAgg(
+ pShape->GetAggregationInterface());
+ if (xAgg.is())
+ {
+ SvxShape *const pSvxShape(
+ comphelper::getFromUnoTunnel<SvxShape>(xTunnel));
+ if (pSvxShape)
+ {
+ SdrObject *const pSdrObject = pSvxShape->GetSdrObject();
+ if (pSdrObject)
+ { // hmm... needs view to verify it's in right doc...
+ o_rSdrObjects.push_back(pSdrObject);
+ }
+ }
+ }
+ return;
+ }
+
+ OTextCursorHelper *const pCursor(
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xTunnel));
+ if (pCursor)
+ {
+ if (pCursor->GetDoc() == &rTargetDoc)
+ {
+ o_rpPaM = lcl_createPamCopy(*pCursor->GetPaM());
+ }
+ return;
+ }
+
+ SwXTextRanges* const pRanges(
+ comphelper::getFromUnoTunnel<SwXTextRanges>(xTunnel));
+ if (pRanges)
+ {
+ SwUnoCursor const* pUnoCursor = pRanges->GetCursor();
+ if (pUnoCursor && &pUnoCursor->GetDoc() == &rTargetDoc)
+ {
+ o_rpPaM = lcl_createPamCopy(*pUnoCursor);
+ }
+ return;
+ }
+
+ // check these before Range to prevent misinterpretation of text frames
+ // and cells also implement XTextRange
+ SwXFrame *const pFrame(
+ comphelper::getFromUnoTunnel<SwXFrame>(xTunnel));
+ if (pFrame)
+ {
+ const SwFrameFormat *const pFrameFormat(pFrame->GetFrameFormat());
+ if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc)
+ {
+ o_rFrame = std::make_pair(pFrameFormat->GetName(), pFrame->GetFlyCntType());
+ }
+ return;
+ }
+
+ SwXTextTable *const pTextTable(
+ comphelper::getFromUnoTunnel<SwXTextTable>(xTunnel));
+ if (pTextTable)
+ {
+ SwFrameFormat *const pFrameFormat(pTextTable->GetFrameFormat());
+ if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc)
+ {
+ o_rTableName = pFrameFormat->GetName();
+ }
+ return;
+ }
+
+ SwXCell *const pCell(
+ comphelper::getFromUnoTunnel<SwXCell>(xTunnel));
+ if (pCell)
+ {
+ SwFrameFormat *const pFrameFormat(pCell->GetFrameFormat());
+ if (pFrameFormat && pFrameFormat->GetDoc() == &rTargetDoc)
+ {
+ SwTableBox * pBox = pCell->GetTableBox();
+ SwTable *const pTable = SwTable::FindTable(pFrameFormat);
+ // ??? what's the benefit of setting pBox in this convoluted way?
+ pBox = pCell->FindBox(pTable, pBox);
+ if (pBox)
+ {
+ SwPosition const aPos(*pBox->GetSttNd());
+ SwPaM aPam(aPos);
+ aPam.Move(fnMoveForward, GoInNode);
+ o_rpPaM = lcl_createPamCopy(aPam);
+ }
+ }
+ return;
+ }
+
+ uno::Reference<text::XTextRange> const xTextRange(xTunnel, UNO_QUERY);
+ if (xTextRange.is())
+ {
+ SwUnoInternalPaM aPam(rTargetDoc);
+ if (::sw::XTextRangeToSwPaM(aPam, xTextRange))
+ {
+ o_rpPaM = lcl_createPamCopy(aPam);
+ }
+ return;
+ }
+
+ SwXCellRange *const pCellRange(
+ comphelper::getFromUnoTunnel<SwXCellRange>(xTunnel));
+ if (pCellRange)
+ {
+ SwUnoCursor const*const pUnoCursor(pCellRange->GetTableCursor());
+ if (pUnoCursor && &pUnoCursor->GetDoc() == &rTargetDoc)
+ {
+ // probably can't copy it to o_rpPaM for this since it's
+ // a SwTableCursor
+ o_rpTableCursor = dynamic_cast<SwUnoTableCursor const*>(pUnoCursor);
+ }
+ return;
+ }
+
+ ::sw::mark::IMark const*const pMark(
+ SwXBookmark::GetBookmarkInDoc(& rTargetDoc, xTunnel));
+ if (pMark)
+ {
+ o_rpMark = pMark;
+ return;
+ }
+}
+
+uno::Reference<text::XTextContent>
+GetNestedTextContent(SwTextNode const & rTextNode, sal_Int32 const nIndex,
+ bool const bParent)
+{
+ // these should be unambiguous because of the dummy character
+ SwTextNode::GetTextAttrMode const eMode( bParent
+ ? SwTextNode::PARENT : SwTextNode::EXPAND );
+ SwTextAttr *const pMetaTextAttr =
+ rTextNode.GetTextAttrAt(nIndex, RES_TXTATR_META, eMode);
+ SwTextAttr *const pMetaFieldTextAttr =
+ rTextNode.GetTextAttrAt(nIndex, RES_TXTATR_METAFIELD, eMode);
+ // which is innermost?
+ SwTextAttr *const pTextAttr = pMetaTextAttr
+ ? (pMetaFieldTextAttr
+ ? ((pMetaFieldTextAttr->GetStart() >
+ pMetaTextAttr->GetStart())
+ ? pMetaFieldTextAttr : pMetaTextAttr)
+ : pMetaTextAttr)
+ : pMetaFieldTextAttr;
+ uno::Reference<XTextContent> xRet;
+ if (pTextAttr)
+ {
+ ::sw::Meta *const pMeta(
+ static_cast<SwFormatMeta &>(pTextAttr->GetAttr()).GetMeta());
+ assert(pMeta);
+ xRet.set(pMeta->MakeUnoObject(), uno::UNO_QUERY);
+ }
+ return xRet;
+}
+
+static uno::Any GetParaListAutoFormat(SwTextNode const& rNode)
+{
+ SwFormatAutoFormat const*const pFormat(
+ rNode.GetSwAttrSet().GetItem<SwFormatAutoFormat>(RES_PARATR_LIST_AUTOFMT, false));
+ if (!pFormat)
+ {
+ return uno::Any();
+ }
+ const auto pSet(pFormat->GetStyleHandle());
+ if (!pSet)
+ return {};
+ SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
+ SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
+ std::vector<beans::NamedValue> props;
+ // have to iterate the map, not the item set?
+ for (auto const pEntry : rMap.getPropertyEntries())
+ {
+ if (rPropSet.getPropertyState(*pEntry, *pSet) == PropertyState_DIRECT_VALUE)
+ {
+ Any value;
+ rPropSet.getPropertyValue(*pEntry, *pSet, value);
+ props.emplace_back(pEntry->aName, value);
+ }
+ }
+ return uno::Any(comphelper::containerToSequence(props));
+}
+
+// Read the special properties of the cursor
+bool getCursorPropertyValue(const SfxItemPropertyMapEntry& rEntry
+ , SwPaM& rPam
+ , Any *pAny
+ , PropertyState& eState
+ , const SwTextNode* pNode )
+{
+ PropertyState eNewState = PropertyState_DIRECT_VALUE;
+ bool bDone = true;
+ switch(rEntry.nWID)
+ {
+ case FN_UNO_PARA_CONT_PREV_SUBTREE:
+ if (pAny)
+ {
+ const SwTextNode * pTmpNode = pNode;
+
+ if (!pTmpNode)
+ pTmpNode = rPam.GetNode().GetTextNode();
+
+ bool bRet = false;
+
+ if ( pTmpNode &&
+ pTmpNode->GetNum() &&
+ pTmpNode->GetNum()->IsContinueingPreviousSubTree() )
+ {
+ bRet = true;
+ }
+
+ *pAny <<= bRet;
+ }
+ break;
+ case FN_UNO_PARA_NUM_STRING:
+ if (pAny)
+ {
+ const SwTextNode * pTmpNode = pNode;
+
+ if (!pTmpNode)
+ pTmpNode = rPam.GetNode().GetTextNode();
+
+ OUString sRet;
+ if ( pTmpNode && pTmpNode->GetNum() )
+ {
+ sRet = pTmpNode->GetNumString();
+ }
+
+ *pAny <<= sRet;
+ }
+ break;
+ case RES_PARATR_OUTLINELEVEL:
+ if (pAny)
+ {
+ const SwTextNode * pTmpNode = pNode;
+
+ if (!pTmpNode)
+ pTmpNode = rPam.GetNode().GetTextNode();
+
+ sal_Int16 nRet = -1;
+ if ( pTmpNode )
+ nRet = sal::static_int_cast< sal_Int16 >( pTmpNode->GetAttrOutlineLevel() );
+
+ *pAny <<= nRet;
+ }
+ break;
+ case FN_UNO_PARA_CONDITIONAL_STYLE_NAME:
+ case FN_UNO_PARA_STYLE :
+ {
+ SwFormatColl* pFormat = nullptr;
+ if(pNode)
+ pFormat = FN_UNO_PARA_CONDITIONAL_STYLE_NAME == rEntry.nWID
+ ? pNode->GetFormatColl() : &pNode->GetAnyFormatColl();
+ else
+ {
+ pFormat = SwUnoCursorHelper::GetCurTextFormatColl(rPam,
+ FN_UNO_PARA_CONDITIONAL_STYLE_NAME == rEntry.nWID);
+ }
+ if(pFormat)
+ {
+ if( pAny )
+ {
+ OUString sVal;
+ SwStyleNameMapper::FillProgName(pFormat->GetName(), sVal, SwGetPoolIdFromName::TxtColl );
+ *pAny <<= sVal;
+ }
+ }
+ else
+ eNewState = PropertyState_AMBIGUOUS_VALUE;
+ }
+ break;
+ case FN_UNO_PAGE_STYLE :
+ {
+ OUString sVal;
+ GetCurPageStyle(rPam, sVal);
+ if( pAny )
+ *pAny <<= sVal;
+ if(sVal.isEmpty())
+ eNewState = PropertyState_AMBIGUOUS_VALUE;
+ }
+ break;
+ case FN_UNO_NUM_START_VALUE :
+ if( pAny )
+ {
+ sal_Int16 nValue = IsNodeNumStart(rPam, eNewState);
+ *pAny <<= nValue;
+ }
+ break;
+ case FN_UNO_NUM_LEVEL :
+ case FN_UNO_IS_NUMBER :
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ case FN_NUMBER_NEWSTART:
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ if (!pAny)
+ {
+ break;
+ }
+ // a multi selection is not considered
+ const SwTextNode* pTextNd = rPam.GetNode().GetTextNode();
+ if ( pTextNd && pTextNd->IsInList() )
+ {
+ switch (rEntry.nWID)
+ {
+ case FN_UNO_NUM_LEVEL:
+ {
+ *pAny <<= static_cast<sal_Int16>(pTextNd->GetActualListLevel());
+ break;
+ }
+ case FN_UNO_IS_NUMBER:
+ {
+ *pAny <<= pTextNd->IsCountedInList();
+ break;
+ }
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ {
+ *pAny <<= pTextNd->GetListId();
+ break;
+ }
+ case FN_NUMBER_NEWSTART:
+ {
+ *pAny <<= pTextNd->IsListRestart();
+ break;
+ }
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ *pAny = GetParaListAutoFormat(*pTextNd);
+ break;
+ }
+ default:
+ assert(false);
+ }
+ }
+ else
+ {
+ eNewState = PropertyState_DEFAULT_VALUE;
+
+ // #i30838# set default values for default properties
+ switch (rEntry.nWID)
+ {
+ case FN_UNO_NUM_LEVEL:
+ {
+ *pAny <<= static_cast<sal_Int16>( 0 );
+ break;
+ }
+ case FN_UNO_IS_NUMBER:
+ {
+ *pAny <<= false;
+ break;
+ }
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ {
+ *pAny <<= OUString();
+ break;
+ }
+ case FN_NUMBER_NEWSTART:
+ {
+ *pAny <<= false;
+ break;
+ }
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ break; // void
+ }
+ default:
+ assert(false);
+ }
+ }
+ //PROPERTY_MAYBEVOID!
+ }
+ break;
+ case FN_UNO_NUM_RULES :
+ if( pAny )
+ getNumberingProperty(rPam, eNewState, pAny);
+ else
+ {
+ if( !SwDoc::GetNumRuleAtPos( *rPam.GetPoint() ) )
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_DOCUMENT_INDEX_MARK:
+ {
+ std::vector<SwTextAttr *> marks;
+ if (rPam.GetNode().IsTextNode())
+ {
+ marks = rPam.GetNode().GetTextNode()->GetTextAttrsAt(
+ rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_TOXMARK);
+ }
+ if (!marks.empty())
+ {
+ if( pAny )
+ { // hmm... can only return 1 here
+ SwTOXMark & rMark =
+ static_cast<SwTOXMark &>((*marks.begin())->GetAttr());
+ const uno::Reference< text::XDocumentIndexMark > xRef =
+ SwXDocumentIndexMark::CreateXDocumentIndexMark(
+ rPam.GetDoc(), &rMark);
+ (*pAny) <<= xRef;
+ }
+ }
+ else
+ //also here - indistinguishable
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_DOCUMENT_INDEX:
+ {
+ SwTOXBase* pBase = SwDoc::GetCurTOX(
+ *rPam.Start() );
+ if( pBase )
+ {
+ if( pAny )
+ {
+ const uno::Reference< text::XDocumentIndex > xRef =
+ SwXDocumentIndex::CreateXDocumentIndex(rPam.GetDoc(),
+ static_cast<SwTOXBaseSection *>(pBase));
+ (*pAny) <<= xRef;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_FIELD:
+ {
+ const SwPosition *pPos = rPam.Start();
+ const SwTextNode *pTextNd =
+ rPam.GetDoc().GetNodes()[pPos->nNode.GetIndex()]->GetTextNode();
+ const SwTextAttr* pTextAttr = pTextNd
+ ? pTextNd->GetFieldTextAttrAt( pPos->nContent.GetIndex(), true )
+ : nullptr;
+ if ( pTextAttr != nullptr )
+ {
+ if( pAny )
+ {
+ uno::Reference<text::XTextField> const xField(
+ SwXTextField::CreateXTextField(&rPam.GetDoc(),
+ &pTextAttr->GetFormatField()));
+ *pAny <<= xField;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_TABLE:
+ case FN_UNO_CELL:
+ {
+ SwStartNode* pSttNode = rPam.GetNode().StartOfSectionNode();
+ SwStartNodeType eType = pSttNode->GetStartNodeType();
+ if(SwTableBoxStartNode == eType)
+ {
+ if( pAny )
+ {
+ const SwTableNode* pTableNode = pSttNode->FindTableNode();
+ SwFrameFormat* pTableFormat = pTableNode->GetTable().GetFrameFormat();
+ //SwTable& rTable = static_cast<SwTableNode*>(pSttNode)->GetTable();
+ if(FN_UNO_TEXT_TABLE == rEntry.nWID)
+ {
+ uno::Reference< XTextTable > xTable = SwXTextTables::GetObject(*pTableFormat);
+ *pAny <<= xTable;
+ }
+ else
+ {
+ SwTableBox* pBox = pSttNode->GetTableBox();
+ uno::Reference< XCell > xCell = SwXCell::CreateXCell(pTableFormat, pBox);
+ *pAny <<= xCell;
+ }
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_FRAME:
+ {
+ SwStartNode* pSttNode = rPam.GetNode().StartOfSectionNode();
+ SwStartNodeType eType = pSttNode->GetStartNodeType();
+
+ SwFrameFormat* pFormat;
+ if(eType == SwFlyStartNode && nullptr != (pFormat = pSttNode->GetFlyFormat()))
+ {
+ // Create a wrapper only for text frames, not for graphic or OLE nodes.
+ if (pAny && !rPam.GetNode().IsNoTextNode())
+ {
+ uno::Reference<XTextFrame> const xFrame(
+ SwXTextFrame::CreateXTextFrame(*pFormat->GetDoc(), pFormat));
+ (*pAny) <<= xFrame;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_SECTION:
+ {
+ SwSection* pSect = SwDoc::GetCurrSection(*rPam.GetPoint());
+ if(pSect)
+ {
+ if( pAny )
+ {
+ uno::Reference< XTextSection > xSect = SwXTextSections::GetObject( *pSect->GetFormat() );
+ *pAny <<= xSect;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_TEXT_PARAGRAPH:
+ {
+ SwTextNode* pTextNode = rPam.GetPoint()->nNode.GetNode().GetTextNode();
+ if (pTextNode)
+ {
+ if (pAny)
+ {
+ uno::Reference<text::XTextContent> xParagraph = SwXParagraph::CreateXParagraph(pTextNode->GetDoc(), pTextNode);
+ *pAny <<= xParagraph;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_ENDNOTE:
+ case FN_UNO_FOOTNOTE:
+ {
+ SwTextAttr *const pTextAttr = rPam.GetNode().IsTextNode() ?
+ rPam.GetNode().GetTextNode()->GetTextAttrForCharAt(
+ rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_FTN) : nullptr;
+ if(pTextAttr)
+ {
+ const SwFormatFootnote& rFootnote = pTextAttr->GetFootnote();
+ if(rFootnote.IsEndNote() == (FN_UNO_ENDNOTE == rEntry.nWID))
+ {
+ if( pAny )
+ {
+ const uno::Reference< text::XFootnote > xFootnote =
+ SwXFootnote::CreateXFootnote(rPam.GetDoc(),
+ &const_cast<SwFormatFootnote&>(rFootnote));
+ *pAny <<= xFootnote;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_REFERENCE_MARK:
+ {
+ std::vector<SwTextAttr *> marks;
+ if (rPam.GetNode().IsTextNode())
+ {
+ marks = rPam.GetNode().GetTextNode()->GetTextAttrsAt(
+ rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK);
+ }
+ if (!marks.empty())
+ {
+ if( pAny )
+ { // hmm... can only return 1 here
+ const SwFormatRefMark& rRef = (*marks.begin())->GetRefMark();
+ uno::Reference<XTextContent> const xRef =
+ SwXReferenceMark::CreateXReferenceMark(rPam.GetDoc(),
+ const_cast<SwFormatRefMark*>(&rRef));
+ *pAny <<= xRef;
+ }
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case FN_UNO_NESTED_TEXT_CONTENT:
+ {
+ uno::Reference<XTextContent> const xRet(rPam.GetNode().IsTextNode()
+ ? GetNestedTextContent(*rPam.GetNode().GetTextNode(),
+ rPam.GetPoint()->nContent.GetIndex(), false)
+ : nullptr);
+ if (xRet.is())
+ {
+ if (pAny)
+ {
+ (*pAny) <<= xRet;
+ }
+ }
+ else
+ {
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ break;
+ case FN_UNO_CHARFMT_SEQUENCE:
+ {
+
+ SwTextNode *const pTextNode = rPam.GetNode().GetTextNode();
+ if (&rPam.GetNode() == &rPam.GetNode(false)
+ && pTextNode && pTextNode->GetpSwpHints())
+ {
+ sal_Int32 nPaMStart = rPam.Start()->nContent.GetIndex();
+ sal_Int32 nPaMEnd = rPam.End()->nContent.GetIndex();
+ Sequence< OUString> aCharStyles;
+ SwpHints* pHints = pTextNode->GetpSwpHints();
+ for( size_t nAttr = 0; nAttr < pHints->Count(); ++nAttr )
+ {
+ SwTextAttr* pAttr = pHints->Get( nAttr );
+ if(pAttr->Which() != RES_TXTATR_CHARFMT)
+ continue;
+ const sal_Int32 nAttrStart = pAttr->GetStart();
+ const sal_Int32 nAttrEnd = *pAttr->GetEnd();
+ //check if the attribute touches the selection
+ if( ( nAttrEnd > nPaMStart && nAttrStart < nPaMEnd ) ||
+ ( !nAttrStart && !nAttrEnd && !nPaMStart && !nPaMEnd ) )
+ {
+ //check for overlapping
+ if(nAttrStart > nPaMStart ||
+ nAttrEnd < nPaMEnd)
+ {
+ aCharStyles.realloc(0);
+ break;
+ }
+ else
+ {
+ //now the attribute should start before or at the selection
+ //and it should end at the end of the selection or behind
+ OSL_ENSURE(nAttrStart <= nPaMStart && nAttrEnd >=nPaMEnd,
+ "attribute overlaps or is outside");
+ //now the name of the style has to be added to the sequence
+ aCharStyles.realloc(aCharStyles.getLength() + 1);
+ OSL_ENSURE(pAttr->GetCharFormat().GetCharFormat(), "no character format set");
+ aCharStyles.getArray()[aCharStyles.getLength() - 1] =
+ SwStyleNameMapper::GetProgName(
+ pAttr->GetCharFormat().GetCharFormat()->GetName(), SwGetPoolIdFromName::ChrFmt);
+ }
+ }
+
+ }
+ eNewState =
+ aCharStyles.hasElements() ?
+ PropertyState_DIRECT_VALUE : PropertyState_DEFAULT_VALUE;
+ if(pAny)
+ (*pAny) <<= aCharStyles;
+ }
+ else
+ eNewState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case RES_TXTATR_CHARFMT:
+ // no break here!
+ default: bDone = false;
+ }
+ if( bDone )
+ eState = eNewState;
+ return bDone;
+};
+
+sal_Int16 IsNodeNumStart(SwPaM const & rPam, PropertyState& eState)
+{
+ const SwTextNode* pTextNd = rPam.GetNode().GetTextNode();
+ // correction: check, if restart value is set at the text node and use
+ // new method <SwTextNode::GetAttrListRestartValue()> to retrieve the value
+ if ( pTextNd && pTextNd->GetNumRule() && pTextNd->IsListRestart() &&
+ pTextNd->HasAttrListRestartValue() )
+ {
+ eState = PropertyState_DIRECT_VALUE;
+ sal_Int16 nTmp = sal::static_int_cast< sal_Int16 >(pTextNd->GetAttrListRestartValue());
+ return nTmp;
+ }
+ eState = PropertyState_DEFAULT_VALUE;
+ return -1;
+}
+
+void setNumberingProperty(const Any& rValue, SwPaM& rPam)
+{
+ uno::Reference<XIndexReplace> xIndexReplace;
+ if(rValue >>= xIndexReplace)
+ {
+ auto pSwNum = comphelper::getFromUnoTunnel<SwXNumberingRules>(xIndexReplace);
+ if(pSwNum)
+ {
+ SwDoc& rDoc = rPam.GetDoc();
+ if(pSwNum->GetNumRule())
+ {
+ SwNumRule aRule(*pSwNum->GetNumRule());
+ const OUString* pNewCharStyles = pSwNum->GetNewCharStyleNames();
+ const OUString* pBulletFontNames = pSwNum->GetBulletFontNames();
+ for(sal_uInt16 i = 0; i < MAXLEVEL; i++)
+ {
+ SwNumFormat aFormat(aRule.Get( i ));
+ if (!pNewCharStyles[i].isEmpty() &&
+ !SwXNumberingRules::isInvalidStyle(pNewCharStyles[i]) &&
+ (!aFormat.GetCharFormat() || pNewCharStyles[i] != aFormat.GetCharFormat()->GetName()))
+ {
+ if (pNewCharStyles[i].isEmpty())
+ {
+ // FIXME
+ // Is something missing/wrong here?
+ // if condition is always false due to outer check!
+ aFormat.SetCharFormat(nullptr);
+ }
+ else
+ {
+
+ // get CharStyle and set the rule
+ const size_t nChCount = rDoc.GetCharFormats()->size();
+ SwCharFormat* pCharFormat = nullptr;
+ for(size_t nCharFormat = 0; nCharFormat < nChCount; ++nCharFormat)
+ {
+ SwCharFormat& rChFormat = *((*(rDoc.GetCharFormats()))[nCharFormat]);
+ if(rChFormat.GetName() == pNewCharStyles[i])
+ {
+ pCharFormat = &rChFormat;
+ break;
+ }
+ }
+
+ if(!pCharFormat)
+ {
+ SfxStyleSheetBasePool* pPool = rDoc.GetDocShell()->GetStyleSheetPool();
+ SfxStyleSheetBase* pBase;
+ pBase = pPool->Find(pNewCharStyles[i], SfxStyleFamily::Char);
+ // shall it really be created?
+ if(!pBase)
+ pBase = &pPool->Make(pNewCharStyles[i], SfxStyleFamily::Page);
+ pCharFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat();
+ }
+ if(pCharFormat)
+ aFormat.SetCharFormat(pCharFormat);
+ }
+ }
+ //Now again for fonts
+ if(
+ !pBulletFontNames[i].isEmpty() &&
+ !SwXNumberingRules::isInvalidStyle(pBulletFontNames[i]) &&
+ (!aFormat.GetBulletFont() || aFormat.GetBulletFont()->GetFamilyName() != pBulletFontNames[i])
+ )
+ {
+ const SvxFontListItem* pFontListItem =
+ static_cast<const SvxFontListItem* >(rDoc.GetDocShell()
+ ->GetItem( SID_ATTR_CHAR_FONTLIST ));
+ const FontList* pList = pFontListItem->GetFontList();
+
+ FontMetric aFontMetric = pList->Get(
+ pBulletFontNames[i],WEIGHT_NORMAL, ITALIC_NONE);
+ vcl::Font aFont(aFontMetric);
+ aFormat.SetBulletFont(&aFont);
+ }
+ aRule.Set( i, aFormat );
+ }
+ UnoActionContext aAction(&rDoc);
+
+ if( rPam.GetNext() != &rPam ) // Multiple selection?
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( rPam );
+ SwPaM aPam( *rPam.GetPoint() );
+ for ( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ // no start of a new list
+ rDoc.SetNumRule( aRangeArr.SetPam( n, aPam ), aRule, false );
+ }
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ {
+ // no start of a new list
+ rDoc.SetNumRule( rPam, aRule, false );
+ }
+
+ }
+ else if(!pSwNum->GetCreatedNumRuleName().isEmpty())
+ {
+ UnoActionContext aAction( &rDoc );
+ SwNumRule* pRule = rDoc.FindNumRulePtr( pSwNum->GetCreatedNumRuleName() );
+ if ( !pRule )
+ throw RuntimeException();
+ // no start of a new list
+ rDoc.SetNumRule( rPam, *pRule, false );
+ }
+ else
+ {
+ // #i103817#
+ // outline numbering
+ UnoActionContext aAction(&rDoc);
+ SwNumRule* pRule = rDoc.GetOutlineNumRule();
+ if(!pRule)
+ throw RuntimeException();
+ rDoc.SetNumRule( rPam, *pRule, false );
+ }
+ }
+ }
+ else if ( rValue.getValueType() == cppu::UnoType<void>::get() )
+ {
+ rPam.GetDoc().DelNumRules(rPam);
+ }
+}
+
+void getNumberingProperty(SwPaM& rPam, PropertyState& eState, Any * pAny )
+{
+ const SwNumRule* pNumRule = SwDoc::GetNumRuleAtPos( *rPam.GetPoint() );
+ if(pNumRule)
+ {
+ uno::Reference< XIndexReplace > xNum = new SwXNumberingRules(*pNumRule);
+ if ( pAny )
+ *pAny <<= xNum;
+ eState = PropertyState_DIRECT_VALUE;
+ }
+ else
+ eState = PropertyState_DEFAULT_VALUE;
+}
+
+void GetCurPageStyle(SwPaM const & rPaM, OUString &rString)
+{
+ if (!rPaM.GetContentNode())
+ return; // TODO: is there an easy way to get it for tables/sections?
+ SwRootFrame* pLayout = rPaM.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout();
+ // Consider the position inside the content node, since the node may span over multiple pages
+ // with different page styles.
+ SwContentFrame* pFrame = rPaM.GetContentNode()->getLayoutFrame(pLayout, rPaM.GetPoint());
+ if(pFrame)
+ {
+ const SwPageFrame* pPage = pFrame->FindPageFrame();
+ if(pPage)
+ {
+ SwStyleNameMapper::FillProgName(pPage->GetPageDesc()->GetName(),
+ rString, SwGetPoolIdFromName::PageDesc);
+ }
+ }
+}
+
+// reset special properties of the cursor
+void resetCursorPropertyValue(const SfxItemPropertyMapEntry& rEntry, SwPaM& rPam)
+{
+ SwDoc& rDoc = rPam.GetDoc();
+ switch(rEntry.nWID)
+ {
+ case FN_UNO_PARA_STYLE :
+ break;
+ case FN_UNO_PAGE_STYLE :
+ break;
+ case FN_UNO_NUM_START_VALUE :
+ {
+ UnoActionContext aAction(&rDoc);
+
+ if( rPam.GetNext() != &rPam ) // Multiple selection?
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( rPam );
+ SwPaM aPam( *rPam.GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ rDoc.SetNodeNumStart( *aRangeArr.SetPam( n, aPam ).GetPoint(), 1 );
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ rDoc.SetNodeNumStart( *rPam.GetPoint(), 0 );
+ }
+
+ break;
+ case FN_UNO_NUM_LEVEL :
+ break;
+ case FN_UNO_NUM_RULES:
+ break;
+ case FN_UNO_CHARFMT_SEQUENCE:
+ {
+ rDoc.ResetAttrs(rPam, true, { RES_TXTATR_CHARFMT });
+ }
+ break;
+ }
+}
+
+void InsertFile(SwUnoCursor* pUnoCursor, const OUString& rURL,
+ const uno::Sequence< beans::PropertyValue >& rOptions)
+{
+ if (SwTextNode const*const pTextNode = pUnoCursor->GetPoint()->nNode.GetNode().GetTextNode())
+ {
+ // TODO: check meta field here too in case it ever grows a 2nd char
+ if (pTextNode->GetTextAttrAt(pUnoCursor->GetPoint()->nContent.GetIndex(),
+ RES_TXTATR_INPUTFIELD, SwTextNode::PARENT))
+ {
+ throw uno::RuntimeException("cannot insert file inside input field");
+ }
+
+ if (pTextNode->GetTextAttrAt(pUnoCursor->GetPoint()->nContent.GetIndex(),
+ RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT))
+ {
+ throw uno::RuntimeException("cannot insert file inside content controls");
+ }
+ }
+
+ std::unique_ptr<SfxMedium> pMed;
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ SwDocShell* pDocSh = rDoc.GetDocShell();
+ utl::MediaDescriptor aMediaDescriptor( rOptions );
+ OUString sFileName = rURL;
+ OUString sFilterName, sFilterOptions, sPassword, sBaseURL;
+ uno::Reference < io::XStream > xStream;
+ uno::Reference < io::XInputStream > xInputStream;
+
+ if( sFileName.isEmpty() )
+ aMediaDescriptor[utl::MediaDescriptor::PROP_URL] >>= sFileName;
+ if( sFileName.isEmpty() )
+ aMediaDescriptor[utl::MediaDescriptor::PROP_FILENAME] >>= sFileName;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_STREAM] >>= xStream;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] >>= sFilterName;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_FILTEROPTIONS] >>= sFilterOptions;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_PASSWORD] >>= sPassword;
+ aMediaDescriptor[utl::MediaDescriptor::PROP_DOCUMENTBASEURL ] >>= sBaseURL;
+ if ( !xInputStream.is() && xStream.is() )
+ xInputStream = xStream->getInputStream();
+
+ if(!pDocSh || (sFileName.isEmpty() && !xInputStream.is()))
+ return;
+
+ SfxObjectFactory& rFact = pDocSh->GetFactory();
+ std::shared_ptr<const SfxFilter> pFilter = rFact.GetFilterContainer()->GetFilter4FilterName( sFilterName );
+ uno::Reference < embed::XStorage > xReadStorage;
+ if( xInputStream.is() )
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(xInputStream),
+ uno::Any(embed::ElementModes::READ) };
+ try
+ {
+ xReadStorage.set( ::comphelper::OStorageHelper::GetStorageFactory()->createInstanceWithArguments( aArgs ),
+ uno::UNO_QUERY );
+ }
+ catch( const io::IOException&) {}
+ }
+ if ( !pFilter )
+ {
+ if( xInputStream.is() && !xReadStorage.is())
+ {
+ pMed.reset(new SfxMedium);
+ pMed->setStreamToLoadFrom(xInputStream, true );
+ }
+ else
+ pMed.reset(xReadStorage.is() ?
+ new SfxMedium(xReadStorage, sBaseURL ) :
+ new SfxMedium(sFileName, StreamMode::READ ));
+ if( !sBaseURL.isEmpty() )
+ pMed->GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, sBaseURL ) );
+
+ SfxFilterMatcher aMatcher( rFact.GetFilterContainer()->GetName() );
+ ErrCode nErr = aMatcher.GuessFilter(*pMed, pFilter, SfxFilterFlags::NONE);
+ if ( nErr || !pFilter)
+ return;
+ pMed->SetFilter( pFilter );
+ }
+ else
+ {
+ if( xInputStream.is() && !xReadStorage.is())
+ {
+ pMed.reset(new SfxMedium);
+ pMed->setStreamToLoadFrom(xInputStream, true );
+ pMed->SetFilter( pFilter );
+ }
+ else
+ {
+ if( xReadStorage.is() )
+ {
+ pMed.reset(new SfxMedium(xReadStorage, sBaseURL ));
+ pMed->SetFilter( pFilter );
+ }
+ else
+ pMed.reset(new SfxMedium(sFileName, StreamMode::READ, pFilter, nullptr));
+ }
+ if(!sFilterOptions.isEmpty())
+ pMed->GetItemSet()->Put( SfxStringItem( SID_FILE_FILTEROPTIONS, sFilterOptions ) );
+ if(!sBaseURL.isEmpty())
+ pMed->GetItemSet()->Put( SfxStringItem( SID_DOC_BASEURL, sBaseURL ) );
+ }
+
+ // this sourcecode is not responsible for the lifetime of the shell, SfxObjectShellLock should not be used
+ SfxObjectShellRef aRef( pDocSh );
+
+ pMed->Download(); // if necessary: start the download
+ if( !(aRef.is() && 1 < aRef->GetRefCount()) ) // Ref still valid?
+ return;
+
+ SwReaderPtr pRdr;
+ SfxItemSet* pSet = pMed->GetItemSet();
+ pSet->Put(SfxBoolItem(FN_API_CALL, true));
+ if(!sPassword.isEmpty())
+ pSet->Put(SfxStringItem(SID_PASSWORD, sPassword));
+ Reader *pRead = pDocSh->StartConvertFrom( *pMed, pRdr, nullptr, pUnoCursor);
+ if( !pRead )
+ return;
+
+ UnoActionContext aContext(&rDoc);
+
+ if(pUnoCursor->HasMark())
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(*pUnoCursor);
+
+ SwNodeIndex aSave( pUnoCursor->GetPoint()->nNode, -1 );
+ sal_Int32 nContent = pUnoCursor->GetPoint()->nContent.GetIndex();
+
+ ErrCode nErrno = pRdr->Read( *pRead ); // and paste the document
+
+ if(!nErrno)
+ {
+ ++aSave;
+ pUnoCursor->SetMark();
+ pUnoCursor->GetMark()->nNode = aSave;
+
+ SwContentNode* pCntNode = aSave.GetNode().GetContentNode();
+ if( !pCntNode )
+ nContent = 0;
+ pUnoCursor->GetMark()->nContent.Assign( pCntNode, nContent );
+ }
+}
+
+// insert text and scan for CR characters in order to insert
+// paragraph breaks at those positions by calling SplitNode
+bool DocInsertStringSplitCR(
+ SwDoc &rDoc,
+ const SwPaM &rNewCursor,
+ const OUString &rText,
+ const bool bForceExpandHints )
+{
+ bool bOK = true;
+
+ for (sal_Int32 i = 0; i < rText.getLength(); ++i)
+ {
+ sal_Unicode const ch(rText[i]);
+ if (linguistic::IsControlChar(ch)
+ && ch != '\r' && ch != '\n' && ch != '\t')
+ {
+ SAL_WARN("sw.uno", "DocInsertStringSplitCR: refusing to insert control character " << int(ch));
+ return false;
+ }
+ }
+
+ const SwInsertFlags nInsertFlags =
+ bForceExpandHints
+ ? ( SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
+ : SwInsertFlags::EMPTYEXPAND;
+
+ // grouping done in InsertString is intended for typing, not API calls
+ ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
+ SwTextNode* const pTextNd =
+ rNewCursor.GetPoint()->nNode.GetNode().GetTextNode();
+ if (!pTextNd)
+ {
+ SAL_INFO("sw.uno", "DocInsertStringSplitCR: need a text node");
+ return false;
+ }
+ OUString aText;
+ sal_Int32 nStartIdx = 0;
+ const sal_Int32 nMaxLength = COMPLETE_STRING - pTextNd->GetText().getLength();
+
+ sal_Int32 nIdx = rText.indexOf( '\r', nStartIdx );
+ if( ( nIdx == -1 && nMaxLength < rText.getLength() ) ||
+ ( nIdx != -1 && nMaxLength < nIdx ) )
+ {
+ nIdx = nMaxLength;
+ }
+ while (nIdx != -1 )
+ {
+ OSL_ENSURE( nIdx - nStartIdx >= 0, "index negative!" );
+ aText = rText.copy( nStartIdx, nIdx - nStartIdx );
+ if (!aText.isEmpty() &&
+ !rDoc.getIDocumentContentOperations().InsertString( rNewCursor, aText, nInsertFlags ))
+ {
+ OSL_FAIL( "Doc->Insert(Str) failed." );
+ bOK = false;
+ }
+ if (!rDoc.getIDocumentContentOperations().SplitNode( *rNewCursor.GetPoint(), false ) )
+ {
+ OSL_FAIL( "SplitNode failed" );
+ bOK = false;
+ }
+ nStartIdx = nIdx + 1;
+ nIdx = rText.indexOf( '\r', nStartIdx );
+ }
+ aText = rText.copy( nStartIdx );
+ if (!aText.isEmpty() &&
+ !rDoc.getIDocumentContentOperations().InsertString( rNewCursor, aText, nInsertFlags ))
+ {
+ OSL_FAIL( "Doc->Insert(Str) failed." );
+ bOK = false;
+ }
+
+ return bOK;
+}
+
+void makeRedline( SwPaM const & rPaM,
+ std::u16string_view rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ IDocumentRedlineAccess& rRedlineAccess = rPaM.GetDoc().getIDocumentRedlineAccess();
+
+ RedlineType eType;
+ if ( rRedlineType == u"Insert" )
+ eType = RedlineType::Insert;
+ else if ( rRedlineType == u"Delete" )
+ eType = RedlineType::Delete;
+ else if ( rRedlineType == u"Format" )
+ eType = RedlineType::Format;
+ else if ( rRedlineType == u"TextTable" )
+ eType = RedlineType::Table;
+ else if ( rRedlineType == u"ParagraphFormat" )
+ eType = RedlineType::ParagraphFormat;
+ else
+ throw lang::IllegalArgumentException();
+
+ //todo: what about REDLINE_FMTCOLL?
+ comphelper::SequenceAsHashMap aPropMap( rRedlineProperties );
+ std::size_t nAuthor = 0;
+ OUString sAuthor;
+ if( aPropMap.getValue("RedlineAuthor") >>= sAuthor )
+ nAuthor = rRedlineAccess.InsertRedlineAuthor(sAuthor);
+
+ OUString sComment;
+ SwRedlineData aRedlineData( eType, nAuthor );
+ if( aPropMap.getValue("RedlineComment") >>= sComment )
+ aRedlineData.SetComment( sComment );
+
+ ::util::DateTime aStamp;
+ if( aPropMap.getValue("RedlineDateTime") >>= aStamp )
+ {
+ aRedlineData.SetTimeStamp( DateTime( aStamp));
+ }
+
+ std::unique_ptr<SwRedlineExtraData_FormatColl> xRedlineExtraData;
+
+ // Read the 'Redline Revert Properties' from the parameters
+ uno::Sequence< beans::PropertyValue > aRevertProperties;
+ // Check if the value exists
+ if ( aPropMap.getValue("RedlineRevertProperties") >>= aRevertProperties )
+ {
+ int nMap = 0;
+ // Make sure that paragraph format gets its own map, otherwise e.g. fill attributes are not preserved.
+ if (eType == RedlineType::ParagraphFormat)
+ {
+ nMap = PROPERTY_MAP_PARAGRAPH;
+ if (!aRevertProperties.hasElements())
+ {
+ // to reject the paragraph style change, use standard style
+ xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl( "", RES_POOLCOLL_STANDARD, nullptr ));
+ }
+ }
+ else
+ nMap = PROPERTY_MAP_TEXTPORTION_EXTENSIONS;
+ SfxItemPropertySet const& rPropSet = *aSwMapProvider.GetPropertySet(nMap);
+
+ // Check if there are any properties
+ if (aRevertProperties.hasElements())
+ {
+ SwDoc& rDoc = rPaM.GetDoc();
+
+ // Build set of attributes we want to fetch
+ WhichRangesContainer aWhichPairs;
+ std::vector<SfxItemPropertyMapEntry const*> aEntries;
+ std::vector<uno::Any> aValues;
+ aEntries.reserve(aRevertProperties.getLength());
+ sal_uInt16 nStyleId = USHRT_MAX;
+ sal_uInt16 nNumId = USHRT_MAX;
+ for (const auto& rRevertProperty : std::as_const(aRevertProperties))
+ {
+ const OUString &rPropertyName = rRevertProperty.Name;
+ SfxItemPropertyMapEntry const* pEntry = rPropSet.getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ {
+ // unknown property
+ break;
+ }
+ else if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ break;
+ }
+ else if (rPropertyName == "NumberingRules")
+ {
+ aWhichPairs = aWhichPairs.MergeRange(RES_PARATR_NUMRULE, RES_PARATR_NUMRULE);
+ nNumId = aEntries.size();
+ }
+ else
+ {
+ aWhichPairs = aWhichPairs.MergeRange(pEntry->nWID, pEntry->nWID);
+ if (rPropertyName == "ParaStyleName")
+ nStyleId = aEntries.size();
+ }
+ aEntries.push_back(pEntry);
+ aValues.push_back(rRevertProperty.Value);
+ }
+
+ if (!aWhichPairs.empty())
+ {
+ sal_uInt16 nStylePoolId = USHRT_MAX;
+ OUString sParaStyleName, sUIStyle;
+ SfxItemSet aItemSet(rDoc.GetAttrPool(), aWhichPairs);
+
+ for (size_t i = 0; i < aEntries.size(); ++i)
+ {
+ const uno::Any &rValue = aValues[i];
+ if (i == nNumId)
+ {
+ uno::Reference<container::XNamed> xNumberingRules;
+ rValue >>= xNumberingRules;
+ if (xNumberingRules.is())
+ {
+ aItemSet.Put( SwNumRuleItem( xNumberingRules->getName() ));
+ // keep it during export
+ SwNumRule* pRule = rDoc.FindNumRulePtr(
+ xNumberingRules->getName());
+ if (pRule)
+ pRule->SetUsedByRedline(true);
+ }
+ }
+ else
+ {
+ SfxItemPropertyMapEntry const*const pEntry = aEntries[i];
+ rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
+ if (i == nStyleId)
+ rValue >>= sParaStyleName;
+ }
+ }
+
+ if (eType == RedlineType::ParagraphFormat && sParaStyleName.isEmpty())
+ nStylePoolId = RES_POOLCOLL_STANDARD;
+
+ // tdf#149747 Get UI style name from programmatic style name
+ SwStyleNameMapper::FillUIName(sParaStyleName, sUIStyle,
+ SwGetPoolIdFromName::TxtColl);
+ xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl(
+ sUIStyle.isEmpty() ? sParaStyleName : sUIStyle, nStylePoolId, &aItemSet));
+ }
+ else if (eType == RedlineType::ParagraphFormat)
+ xRedlineExtraData.reset(new SwRedlineExtraData_FormatColl( "", RES_POOLCOLL_STANDARD, nullptr ));
+ }
+ }
+
+ SwRangeRedline* pRedline = new SwRangeRedline( aRedlineData, rPaM );
+
+ // set IsMoved bit of the redline to show and handle moved text
+ bool bIsMoved;
+ if( (aPropMap.getValue("RedlineMoved") >>= bIsMoved) && bIsMoved )
+ pRedline->SetMoved();
+
+ RedlineFlags nPrevMode = rRedlineAccess.GetRedlineFlags( );
+ // xRedlineExtraData is copied here
+ pRedline->SetExtraData( xRedlineExtraData.get() );
+
+ rRedlineAccess.SetRedlineFlags_intern(RedlineFlags::On);
+ auto const result(rRedlineAccess.AppendRedline(pRedline, false));
+ rRedlineAccess.SetRedlineFlags_intern( nPrevMode );
+ if (IDocumentRedlineAccess::AppendResult::IGNORED == result)
+ throw lang::IllegalArgumentException();
+}
+
+void makeTableRowRedline( SwTableLine& rTableLine,
+ std::u16string_view rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ SwDoc* pDoc = rTableLine.GetFrameFormat()->GetDoc();
+ IDocumentRedlineAccess* pRedlineAccess = &pDoc->getIDocumentRedlineAccess();
+
+ RedlineType eType;
+ if ( rRedlineType == u"TableRowInsert" )
+ {
+ eType = RedlineType::TableRowInsert;
+ }
+ else if ( rRedlineType == u"TableRowDelete" )
+ {
+ eType = RedlineType::TableRowDelete;
+ }
+ else
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ // set table row property "HasTextChangesOnly" to false
+ // to handle tracked deletion or insertion of the table row on the UI
+ const SvxPrintItem *pHasTextChangesOnlyProp =
+ rTableLine.GetFrameFormat()->GetAttrSet().GetItem<SvxPrintItem>(RES_PRINT);
+ if ( !pHasTextChangesOnlyProp || pHasTextChangesOnlyProp->GetValue() )
+ {
+ SvxPrintItem aSetTracking(RES_PRINT, false);
+ SwNodeIndex aInsPos( *(rTableLine.GetTabBoxes()[0]->GetSttNd()), 1 );
+ // as a workaround for the rows without text content,
+ // add a redline with invisible text CH_TXT_TRACKED_DUMMY_CHAR
+ if ( rTableLine.IsEmpty() )
+ {
+ SwPaM aPaM(aInsPos);
+ pDoc->getIDocumentContentOperations().InsertString( aPaM,
+ OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
+ aPaM.SetMark();
+ aPaM.GetMark()->nContent.Assign(aPaM.GetContentNode(), 0);
+ makeRedline(aPaM, RedlineType::TableRowInsert == eType
+ ? u"Insert"
+ : u"Delete", rRedlineProperties);
+ }
+ SwCursor aCursor( SwPosition(aInsPos), nullptr );
+ pDoc->SetRowNotTracked( aCursor, aSetTracking );
+ }
+
+ comphelper::SequenceAsHashMap aPropMap( rRedlineProperties );
+ std::size_t nAuthor = 0;
+ OUString sAuthor;
+ if( aPropMap.getValue("RedlineAuthor") >>= sAuthor )
+ nAuthor = pRedlineAccess->InsertRedlineAuthor(sAuthor);
+
+ OUString sComment;
+ SwRedlineData aRedlineData( eType, nAuthor );
+ if( aPropMap.getValue("RedlineComment") >>= sComment )
+ aRedlineData.SetComment( sComment );
+
+ ::util::DateTime aStamp;
+ if( aPropMap.getValue("RedlineDateTime") >>= aStamp )
+ {
+ aRedlineData.SetTimeStamp(
+ DateTime( Date( aStamp.Day, aStamp.Month, aStamp.Year ), tools::Time( aStamp.Hours, aStamp.Minutes, aStamp.Seconds ) ) );
+ }
+
+ SwTableRowRedline* pRedline = new SwTableRowRedline( aRedlineData, rTableLine );
+ RedlineFlags nPrevMode = pRedlineAccess->GetRedlineFlags( );
+ pRedline->SetExtraData( nullptr );
+
+ pRedlineAccess->SetRedlineFlags_intern(RedlineFlags::On);
+ bool bRet = pRedlineAccess->AppendTableRowRedline( pRedline );
+ pRedlineAccess->SetRedlineFlags_intern( nPrevMode );
+ if( !bRet )
+ throw lang::IllegalArgumentException();
+}
+
+void makeTableCellRedline( SwTableBox& rTableBox,
+ std::u16string_view rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ IDocumentRedlineAccess* pRedlineAccess = &rTableBox.GetFrameFormat()->GetDoc()->getIDocumentRedlineAccess();
+
+ RedlineType eType;
+ if ( rRedlineType == u"TableCellInsert" )
+ {
+ eType = RedlineType::TableCellInsert;
+ }
+ else if ( rRedlineType == u"TableCellDelete" )
+ {
+ eType = RedlineType::TableCellDelete;
+ }
+ else
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ comphelper::SequenceAsHashMap aPropMap( rRedlineProperties );
+ std::size_t nAuthor = 0;
+ OUString sAuthor;
+ if( aPropMap.getValue("RedlineAuthor") >>= sAuthor )
+ nAuthor = pRedlineAccess->InsertRedlineAuthor(sAuthor);
+
+ OUString sComment;
+ SwRedlineData aRedlineData( eType, nAuthor );
+ if( aPropMap.getValue("RedlineComment") >>= sComment )
+ aRedlineData.SetComment( sComment );
+
+ ::util::DateTime aStamp;
+ if( aPropMap.getValue("RedlineDateTime") >>= aStamp )
+ {
+ aRedlineData.SetTimeStamp(
+ DateTime( Date( aStamp.Day, aStamp.Month, aStamp.Year ), tools::Time( aStamp.Hours, aStamp.Minutes, aStamp.Seconds ) ) );
+ }
+
+ SwTableCellRedline* pRedline = new SwTableCellRedline( aRedlineData, rTableBox );
+ RedlineFlags nPrevMode = pRedlineAccess->GetRedlineFlags( );
+ pRedline->SetExtraData( nullptr );
+
+ pRedlineAccess->SetRedlineFlags_intern(RedlineFlags::On);
+ bool bRet = pRedlineAccess->AppendTableCellRedline( pRedline );
+ pRedlineAccess->SetRedlineFlags_intern( nPrevMode );
+ if( !bRet )
+ throw lang::IllegalArgumentException();
+}
+
+void SwAnyMapHelper::SetValue( sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& rAny )
+{
+ sal_uInt32 nKey = (nWhichId << 16) + nMemberId;
+ m_Map[nKey] = rAny;
+}
+
+bool SwAnyMapHelper::FillValue( sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& pAny )
+{
+ bool bRet = false;
+ sal_uInt32 nKey = (nWhichId << 16) + nMemberId;
+ auto aIt = m_Map.find( nKey );
+ if (aIt != m_Map.end())
+ {
+ pAny = & aIt->second;
+ bRet = true;
+ }
+ return bRet;
+}
+
+}//namespace SwUnoCursorHelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unodraw.cxx b/sw/source/core/unocore/unodraw.cxx
new file mode 100644
index 000000000..a3d5888e6
--- /dev/null
+++ b/sw/source/core/unocore/unodraw.cxx
@@ -0,0 +1,2887 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <initializer_list>
+#include <memory>
+#include <string_view>
+
+#include <sal/log.hxx>
+
+#include <cmdid.h>
+#include <unomid.h>
+
+#include <drawdoc.hxx>
+#include <unodraw.hxx>
+#include <unoframe.hxx>
+#include <unoparagraph.hxx>
+#include <unotextrange.hxx>
+#include <svx/svditer.hxx>
+#include <swunohelper.hxx>
+#include <textboxhelper.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fmtcntnt.hxx>
+#include <fmtflcnt.hxx>
+#include <txatbase.hxx>
+#include <docsh.hxx>
+#include <unomap.hxx>
+#include <unoport.hxx>
+#include <TextCursorHelper.hxx>
+#include <dflyobj.hxx>
+#include <ndtxt.hxx>
+#include <svx/svdview.hxx>
+#include <svx/unoshape.hxx>
+#include <dcontact.hxx>
+#include <fmtornt.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <rootfrm.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/shapepropertynotifier.hxx>
+#include <crstate.hxx>
+#include <comphelper/extract.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svx/scene3d.hxx>
+#include <tools/UnitConversion.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+using namespace ::com::sun::star;
+
+class SwShapeDescriptor_Impl
+{
+ bool m_isInReading;
+ std::unique_ptr<SwFormatHoriOrient> m_pHOrient;
+ std::unique_ptr<SwFormatVertOrient> m_pVOrient;
+ std::unique_ptr<SwFormatAnchor> m_pAnchor;
+ std::unique_ptr<SwFormatSurround> m_pSurround;
+ std::unique_ptr<SvxULSpaceItem> m_pULSpace;
+ std::unique_ptr<SvxLRSpaceItem> m_pLRSpace;
+ bool m_bOpaque;
+ uno::Reference< text::XTextRange > m_xTextRange;
+ // #i26791#
+ std::unique_ptr<SwFormatFollowTextFlow> m_pFollowTextFlow;
+ // #i28701#
+ std::unique_ptr<SwFormatWrapInfluenceOnObjPos> m_pWrapInfluenceOnObjPos;
+ // #i28749#
+ sal_Int16 mnPositionLayoutDir;
+
+ SwShapeDescriptor_Impl(const SwShapeDescriptor_Impl&) = delete;
+ SwShapeDescriptor_Impl& operator=(const SwShapeDescriptor_Impl&) = delete;
+
+public:
+ bool m_bInitializedPropertyNotifier;
+
+public:
+ SwShapeDescriptor_Impl(SwDoc const*const pDoc)
+ : m_isInReading(pDoc && pDoc->IsInReading())
+ // #i32349# - no defaults, in order to determine on
+ // adding a shape, if positioning attributes are set or not.
+ , m_bOpaque(false)
+ // #i26791#
+ , m_pFollowTextFlow( new SwFormatFollowTextFlow(false) )
+ // #i28701# #i35017#
+ , m_pWrapInfluenceOnObjPos( new SwFormatWrapInfluenceOnObjPos(
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT) )
+ // #i28749#
+ , mnPositionLayoutDir(text::PositionLayoutDir::PositionInLayoutDirOfAnchor)
+ , m_bInitializedPropertyNotifier(false)
+ {}
+
+ SwFormatAnchor* GetAnchor(bool bCreate = false)
+ {
+ if (bCreate && !m_pAnchor)
+ {
+ m_pAnchor.reset(new SwFormatAnchor(RndStdIds::FLY_AS_CHAR));
+ }
+ return m_pAnchor.get();
+ }
+ SwFormatHoriOrient* GetHOrient(bool bCreate = false)
+ {
+ if (bCreate && !m_pHOrient)
+ {
+ // #i26791#
+ m_pHOrient.reset(new SwFormatHoriOrient(0, text::HoriOrientation::NONE, text::RelOrientation::FRAME));
+ }
+ return m_pHOrient.get();
+ }
+ SwFormatVertOrient* GetVOrient(bool bCreate = false)
+ {
+ if (bCreate && !m_pVOrient)
+ {
+ if (m_isInReading && // tdf#113938 extensions might rely on old default
+ (!GetAnchor(true) || m_pAnchor->GetAnchorId() == RndStdIds::FLY_AS_CHAR))
+ { // for as-char, NONE ("from-top") is not a good default
+ m_pVOrient.reset(new SwFormatVertOrient(0, text::VertOrientation::TOP, text::RelOrientation::FRAME));
+ }
+ else
+ { // #i26791#
+ m_pVOrient.reset(new SwFormatVertOrient(0, text::VertOrientation::NONE, text::RelOrientation::FRAME));
+ }
+ }
+ return m_pVOrient.get();
+ }
+
+ SwFormatSurround* GetSurround(bool bCreate = false)
+ {
+ if (bCreate && !m_pSurround)
+ {
+ m_pSurround.reset(new SwFormatSurround());
+ }
+ return m_pSurround.get();
+ }
+ SvxLRSpaceItem* GetLRSpace(bool bCreate = false)
+ {
+ if (bCreate && !m_pLRSpace)
+ {
+ m_pLRSpace.reset(new SvxLRSpaceItem(RES_LR_SPACE));
+ }
+ return m_pLRSpace.get();
+ }
+ SvxULSpaceItem* GetULSpace(bool bCreate = false)
+ {
+ if (bCreate && !m_pULSpace)
+ {
+ m_pULSpace.reset(new SvxULSpaceItem(RES_UL_SPACE));
+ }
+ return m_pULSpace.get();
+ }
+ uno::Reference< text::XTextRange > & GetTextRange()
+ {
+ return m_xTextRange;
+ }
+ bool IsOpaque() const
+ {
+ return m_bOpaque;
+ }
+ const bool& GetOpaque() const
+ {
+ return m_bOpaque;
+ }
+ void RemoveHOrient() { m_pHOrient.reset(); }
+ void RemoveVOrient() { m_pVOrient.reset(); }
+ void RemoveAnchor() { m_pAnchor.reset(); }
+ void RemoveSurround() { m_pSurround.reset(); }
+ void RemoveULSpace() { m_pULSpace.reset(); }
+ void RemoveLRSpace() { m_pLRSpace.reset(); }
+ void SetOpaque(bool bSet){m_bOpaque = bSet;}
+
+ // #i26791#
+ SwFormatFollowTextFlow* GetFollowTextFlow( bool _bCreate = false )
+ {
+ if (_bCreate && !m_pFollowTextFlow)
+ {
+ m_pFollowTextFlow.reset(new SwFormatFollowTextFlow(false));
+ }
+ return m_pFollowTextFlow.get();
+ }
+ void RemoveFollowTextFlow()
+ {
+ m_pFollowTextFlow.reset();
+ }
+
+ // #i28749#
+ sal_Int16 GetPositionLayoutDir() const
+ {
+ return mnPositionLayoutDir;
+ }
+ void SetPositionLayoutDir( sal_Int16 _nPositionLayoutDir )
+ {
+ switch ( _nPositionLayoutDir )
+ {
+ case text::PositionLayoutDir::PositionInHoriL2R:
+ case text::PositionLayoutDir::PositionInLayoutDirOfAnchor:
+ {
+ mnPositionLayoutDir = _nPositionLayoutDir;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwShapeDescriptor_Impl::SetPositionLayoutDir(..)> - invalid attribute value." );
+ }
+ }
+ }
+
+ // #i28701#
+ SwFormatWrapInfluenceOnObjPos* GetWrapInfluenceOnObjPos(
+ const bool _bCreate = false )
+ {
+ if (_bCreate && !m_pWrapInfluenceOnObjPos)
+ {
+ m_pWrapInfluenceOnObjPos.reset(new SwFormatWrapInfluenceOnObjPos(
+ // #i35017#
+ text::WrapInfluenceOnPosition::ONCE_CONCURRENT));
+ }
+ return m_pWrapInfluenceOnObjPos.get();
+ }
+ void RemoveWrapInfluenceOnObjPos()
+ {
+ m_pWrapInfluenceOnObjPos.reset();
+ }
+};
+
+SwFmDrawPage::SwFmDrawPage( SdrPage* pPage ) :
+ SvxFmDrawPage( pPage ), m_pPageView(nullptr)
+{
+}
+
+SwFmDrawPage::~SwFmDrawPage() noexcept
+{
+ while (!m_vShapes.empty())
+ m_vShapes.back()->dispose();
+ RemovePageView();
+}
+
+const SdrMarkList& SwFmDrawPage::PreGroup(const uno::Reference< drawing::XShapes > & xShapes)
+{
+ SelectObjectsInView( xShapes, GetPageView() );
+ const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
+ return rMarkList;
+}
+
+void SwFmDrawPage::PreUnGroup(const uno::Reference< drawing::XShapeGroup >& rShapeGroup)
+{
+ SelectObjectInView( rShapeGroup, GetPageView() );
+}
+
+SdrPageView* SwFmDrawPage::GetPageView()
+{
+ if(!m_pPageView)
+ m_pPageView = mpView->ShowSdrPage( mpPage );
+ return m_pPageView;
+}
+
+void SwFmDrawPage::RemovePageView()
+{
+ if(m_pPageView && mpView)
+ mpView->HideSdrPage();
+ m_pPageView = nullptr;
+}
+
+uno::Reference<drawing::XShape> SwFmDrawPage::GetShape(SdrObject* pObj)
+{
+ if(!pObj)
+ return nullptr;
+ SwFrameFormat* pFormat = ::FindFrameFormat( pObj );
+ SwFmDrawPage* pPage = dynamic_cast<SwFmDrawPage*>(pFormat);
+ if(!pPage || pPage->m_vShapes.empty())
+ return uno::Reference<drawing::XShape>(pObj->getUnoShape(), uno::UNO_QUERY);
+ for(auto pShape : pPage->m_vShapes)
+ {
+ SvxShape* pSvxShape = pShape->GetSvxShape();
+ if (pSvxShape && pSvxShape->GetSdrObject() == pObj)
+ return uno::Reference<drawing::XShape>(static_cast<::cppu::OWeakObject*>(pShape), uno::UNO_QUERY);
+ }
+ return nullptr;
+}
+
+uno::Reference<drawing::XShapeGroup> SwFmDrawPage::GetShapeGroup(SdrObject* pObj)
+{
+ return uno::Reference<drawing::XShapeGroup>(GetShape(pObj), uno::UNO_QUERY);
+}
+
+uno::Reference< drawing::XShape > SwFmDrawPage::CreateShape( SdrObject *pObj ) const
+{
+ uno::Reference< drawing::XShape > xRet;
+ if(dynamic_cast<const SwVirtFlyDrawObj*>( pObj) != nullptr || pObj->GetObjInventor() == SdrInventor::Swg)
+ {
+ SwFlyDrawContact* pFlyContact = static_cast<SwFlyDrawContact*>(pObj->GetUserCall());
+ if(pFlyContact)
+ {
+ SwFrameFormat* pFlyFormat = pFlyContact->GetFormat();
+ SwDoc* pDoc = pFlyFormat->GetDoc();
+ const SwNodeIndex* pIdx;
+ if( RES_FLYFRMFMT == pFlyFormat->Which()
+ && nullptr != ( pIdx = pFlyFormat->GetContent().GetContentIdx() )
+ && pIdx->GetNodes().IsDocNodes()
+ )
+ {
+ const SwNode* pNd = pDoc->GetNodes()[ pIdx->GetIndex() + 1 ];
+ if(!pNd->IsNoTextNode())
+ {
+ xRet.set(SwXTextFrame::CreateXTextFrame(*pDoc, pFlyFormat),
+ uno::UNO_QUERY);
+ }
+ else if( pNd->IsGrfNode() )
+ {
+ xRet.set(SwXTextGraphicObject::CreateXTextGraphicObject(
+ *pDoc, pFlyFormat), uno::UNO_QUERY);
+ }
+ else if( pNd->IsOLENode() )
+ {
+ xRet.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pDoc, pFlyFormat), uno::UNO_QUERY);
+ }
+ }
+ else
+ {
+ OSL_FAIL( "<SwFmDrawPage::CreateShape(..)> - could not retrieve type. Thus, no shape created." );
+ return xRet;
+ }
+ }
+ }
+ else
+ {
+ // own block - temporary object has to be destroyed before
+ // the delegator is set #81670#
+ {
+ xRet = SvxFmDrawPage::CreateShape( pObj );
+ }
+ uno::Reference< XUnoTunnel > xShapeTunnel(xRet, uno::UNO_QUERY);
+ //don't create an SwXShape if it already exists
+ rtl::Reference<SwXShape> pShape = comphelper::getFromUnoTunnel<SwXShape>(xShapeTunnel);
+ if(!pShape)
+ {
+ xShapeTunnel = nullptr;
+ uno::Reference< uno::XInterface > xCreate(xRet, uno::UNO_QUERY);
+ xRet = nullptr;
+ if ( pObj->IsGroupObject() && (!pObj->Is3DObj() || (dynamic_cast<const E3dScene*>( pObj) != nullptr)) )
+ pShape = new SwXGroupShape(xCreate, nullptr);
+ else
+ pShape = new SwXShape(xCreate, nullptr);
+ xRet = pShape;
+ }
+ const_cast<std::vector<SwXShape*>*>(&m_vShapes)->push_back(pShape.get());
+ pShape->m_pPage = this;
+ }
+ return xRet;
+}
+
+namespace
+{
+ class SwXShapesEnumeration
+ : public SwSimpleEnumeration_Base
+ {
+ private:
+ std::vector< css::uno::Any > m_aShapes;
+ protected:
+ virtual ~SwXShapesEnumeration() override {};
+ public:
+ explicit SwXShapesEnumeration(SwXDrawPage* const pDrawPage);
+
+ //XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual uno::Any SAL_CALL nextElement() override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ };
+}
+
+SwXShapesEnumeration::SwXShapesEnumeration(SwXDrawPage* const pDrawPage)
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nCount = pDrawPage->getCount();
+ m_aShapes.reserve(nCount);
+ for(sal_Int32 nIdx = 0; nIdx < nCount; nIdx++)
+ {
+ uno::Reference<drawing::XShape> xShape(pDrawPage->getByIndex(nIdx), uno::UNO_QUERY);
+ m_aShapes.push_back(uno::Any(xShape));
+ }
+}
+
+sal_Bool SwXShapesEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return !m_aShapes.empty();
+}
+
+uno::Any SwXShapesEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+ if(m_aShapes.empty())
+ throw container::NoSuchElementException();
+ uno::Any aResult = m_aShapes.back();
+ m_aShapes.pop_back();
+ return aResult;
+}
+
+OUString SwXShapesEnumeration::getImplementationName()
+{
+ return "SwXShapeEnumeration";
+}
+
+sal_Bool SwXShapesEnumeration::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SwXShapesEnumeration::getSupportedServiceNames()
+{
+ return { OUString("com.sun.star.container.XEnumeration") };
+}
+
+uno::Reference< container::XEnumeration > SwXDrawPage::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ return uno::Reference< container::XEnumeration >(
+ new SwXShapesEnumeration(this));
+}
+
+OUString SwXDrawPage::getImplementationName()
+{
+ return "SwXDrawPage";
+}
+
+sal_Bool SwXDrawPage::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXDrawPage::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.GenericDrawPage" };
+}
+
+SwXDrawPage::SwXDrawPage(SwDoc* pDc) :
+ m_pDoc(pDc)
+{
+}
+
+SwXDrawPage::~SwXDrawPage()
+{
+ if(m_pDrawPage.is())
+ {
+ uno::Reference< uno::XInterface > xInt;
+ m_pDrawPage->setDelegator(xInt);
+ }
+}
+
+uno::Any SwXDrawPage::queryInterface( const uno::Type& aType )
+{
+ uno::Any aRet = SwXDrawPageBaseClass::queryInterface(aType);
+ if(!aRet.hasValue())
+ {
+ // secure with checking if page exists. This may not be the case
+ // either for new SW docs with no yet graphics usage or when
+ // the doc is closed and someone else still holds a UNO reference
+ // to the XDrawPage (in that case, pDoc is set to 0)
+ SwFmDrawPage* pPage = GetSvxPage();
+
+ if(pPage)
+ {
+ aRet = pPage->queryAggregation(aType);
+ }
+ }
+ return aRet;
+}
+
+uno::Sequence< uno::Type > SwXDrawPage::getTypes()
+{
+ return comphelper::concatSequences(
+ SwXDrawPageBaseClass::getTypes(),
+ GetSvxPage()->getTypes(),
+ uno::Sequence { cppu::UnoType<form::XFormsSupplier2>::get() });
+}
+
+sal_Int32 SwXDrawPage::getCount()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(!m_pDoc->getIDocumentDrawModelAccess().GetDrawModel())
+ return 0;
+ else
+ {
+ GetSvxPage();
+ return SwTextBoxHelper::getCount(m_pDrawPage->GetSdrPage());
+ }
+}
+
+uno::Any SwXDrawPage::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(!m_pDoc->getIDocumentDrawModelAccess().GetDrawModel())
+ throw lang::IndexOutOfBoundsException();
+
+ GetSvxPage();
+ return SwTextBoxHelper::getByIndex(m_pDrawPage->GetSdrPage(), nIndex);
+}
+
+uno::Type SwXDrawPage::getElementType()
+{
+ return cppu::UnoType<drawing::XShape>::get();
+}
+
+sal_Bool SwXDrawPage::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(!m_pDoc->getIDocumentDrawModelAccess().GetDrawModel())
+ return false;
+ else
+ return GetSvxPage()->hasElements();
+}
+
+void SwXDrawPage::add(const uno::Reference< drawing::XShape > & xShape)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ uno::Reference< lang::XUnoTunnel > xShapeTunnel(xShape, uno::UNO_QUERY);
+ SwXShape* pShape = comphelper::getFromUnoTunnel<SwXShape>(xShapeTunnel);
+ SvxShape* pSvxShape = comphelper::getFromUnoTunnel<SvxShape>(xShapeTunnel);
+
+ // this is not a writer shape
+ if(!pShape)
+ throw uno::RuntimeException("illegal object",
+ static_cast< cppu::OWeakObject * > ( this ) );
+
+ // we're already registered in the model / SwXDrawPage::add() already called
+ if(pShape->m_pPage || pShape->m_pFormat || !pShape->m_bDescriptor )
+ return;
+
+ // we're inserted elsewhere already
+ if ( pSvxShape->GetSdrObject() )
+ {
+ if ( pSvxShape->GetSdrObject()->IsInserted() )
+ {
+ return;
+ }
+ }
+ GetSvxPage()->add(xShape);
+
+ OSL_ENSURE(pSvxShape, "Why is here no SvxShape?");
+ // this position is definitely in 1/100 mm
+ awt::Point aMM100Pos(pSvxShape->getPosition());
+
+ // now evaluate the properties of SwShapeDescriptor_Impl
+ SwShapeDescriptor_Impl* pDesc = pShape->GetDescImpl();
+
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END-1> aSet( m_pDoc->GetAttrPool() );
+ SwFormatAnchor aAnchor( RndStdIds::FLY_AS_CHAR );
+ bool bOpaque = false;
+ if( pDesc )
+ {
+ if(pDesc->GetSurround())
+ aSet.Put( *pDesc->GetSurround());
+ // all items are already in Twip
+ if(pDesc->GetLRSpace())
+ {
+ aSet.Put(*pDesc->GetLRSpace());
+ }
+ if(pDesc->GetULSpace())
+ {
+ aSet.Put(*pDesc->GetULSpace());
+ }
+ if(pDesc->GetAnchor())
+ aAnchor = *pDesc->GetAnchor();
+
+ // #i32349# - if no horizontal position exists, create one
+ if ( !pDesc->GetHOrient() )
+ {
+ SwFormatHoriOrient* pHori = pDesc->GetHOrient( true );
+ SwTwips nHoriPos = o3tl::toTwips(aMM100Pos.X, o3tl::Length::mm100);
+ pHori->SetPos( nHoriPos );
+ }
+ {
+ if(pDesc->GetHOrient()->GetHoriOrient() == text::HoriOrientation::NONE)
+ aMM100Pos.X = convertTwipToMm100(pDesc->GetHOrient()->GetPos());
+ aSet.Put( *pDesc->GetHOrient() );
+ }
+ // #i32349# - if no vertical position exists, create one
+ if ( !pDesc->GetVOrient() )
+ {
+ SwFormatVertOrient* pVert = pDesc->GetVOrient( true );
+ SwTwips nVertPos = o3tl::toTwips(aMM100Pos.Y, o3tl::Length::mm100);
+ pVert->SetPos( nVertPos );
+ }
+ {
+ if(pDesc->GetVOrient()->GetVertOrient() == text::VertOrientation::NONE)
+ aMM100Pos.Y = convertTwipToMm100(pDesc->GetVOrient()->GetPos());
+ aSet.Put( *pDesc->GetVOrient() );
+ }
+
+ if(pDesc->GetSurround())
+ aSet.Put( *pDesc->GetSurround());
+ bOpaque = pDesc->IsOpaque();
+
+ // #i26791#
+ if ( pDesc->GetFollowTextFlow() )
+ {
+ aSet.Put( *pDesc->GetFollowTextFlow() );
+ }
+
+ // #i28701#
+ if ( pDesc->GetWrapInfluenceOnObjPos() )
+ {
+ aSet.Put( *pDesc->GetWrapInfluenceOnObjPos() );
+ }
+ }
+
+ pSvxShape->setPosition(aMM100Pos);
+ SdrObject* pObj = pSvxShape->GetSdrObject();
+ // #108784# - set layer of new drawing object to corresponding
+ // invisible layer.
+ if(SdrInventor::FmForm != pObj->GetObjInventor())
+ pObj->SetLayer( bOpaque ? m_pDoc->getIDocumentDrawModelAccess().GetInvisibleHeavenId() : m_pDoc->getIDocumentDrawModelAccess().GetInvisibleHellId() );
+ else
+ pObj->SetLayer(m_pDoc->getIDocumentDrawModelAccess().GetInvisibleControlsId());
+
+ std::optional<SwPaM> pPam(m_pDoc->GetNodes().GetEndOfContent());
+ std::unique_ptr<SwUnoInternalPaM> pInternalPam;
+ uno::Reference< text::XTextRange > xRg;
+ if( pDesc && (xRg = pDesc->GetTextRange()).is() )
+ {
+ pInternalPam.reset(new SwUnoInternalPaM(*m_pDoc));
+ if (!::sw::XTextRangeToSwPaM(*pInternalPam, xRg))
+ throw uno::RuntimeException();
+
+ if(RndStdIds::FLY_AT_FLY == aAnchor.GetAnchorId() &&
+ !pInternalPam->GetNode().FindFlyStartNode())
+ {
+ aAnchor.SetType(RndStdIds::FLY_AS_CHAR);
+ }
+ else if (RndStdIds::FLY_AT_PAGE == aAnchor.GetAnchorId()
+ && 0 == aAnchor.GetPageNum())
+ {
+ aAnchor.SetAnchor(pInternalPam->Start());
+ aAnchor.SetType(RndStdIds::FLY_AT_CHAR); // convert invalid at-page
+ }
+
+ }
+ else if ((aAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE) && m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout())
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ Point aTmp(o3tl::toTwips(aMM100Pos.X, o3tl::Length::mm100), o3tl::toTwips(aMM100Pos.Y, o3tl::Length::mm100));
+ m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( pPam->GetPoint(), aTmp, &aState );
+ aAnchor.SetAnchor( pPam->GetPoint() );
+
+ // #i32349# - adjustment of vertical positioning
+ // attributes no longer needed, because it's already got a default.
+ }
+ else
+ {
+ aAnchor.SetType(RndStdIds::FLY_AT_PAGE);
+
+ // #i32349# - adjustment of vertical positioning
+ // attributes no longer needed, because it's already got a default.
+ }
+ aSet.Put(aAnchor);
+ SwPaM* pTemp = pInternalPam.get();
+ if ( !pTemp )
+ pTemp = &*pPam;
+ UnoActionContext aAction(m_pDoc);
+ m_pDoc->getIDocumentContentOperations().InsertDrawObj( *pTemp, *pObj, aSet );
+
+ if (pSvxShape->GetSdrObject()->GetName().isEmpty())
+ {
+ pSvxShape->GetSdrObject()->SetName(m_pDoc->GetUniqueShapeName());
+ }
+
+ SwFrameFormat* pFormat = ::FindFrameFormat( pObj );
+ if (pFormat)
+ {
+ if (pFormat->GetName().isEmpty())
+ {
+ pFormat->SetName(pSvxShape->GetSdrObject()->GetName(), false);
+ }
+ pShape->SetFrameFormat(pFormat);
+ }
+ pShape->m_bDescriptor = false;
+
+ pPam.reset();
+ pInternalPam.reset();
+}
+
+void SwXDrawPage::remove(const uno::Reference< drawing::XShape > & xShape)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ // tdf#41466 remove TextFrame too which is belonged to the actual shape
+ auto xTextFrame = SwTextBoxHelper::getUnoTextFrame(xShape);
+ if (xTextFrame)
+ {
+ uno::Reference<lang::XComponent> xComp(xTextFrame, uno::UNO_QUERY);
+ if (xComp)
+ xComp->dispose();
+ }
+ // remove shape
+ uno::Reference<lang::XComponent> xComp(xShape, uno::UNO_QUERY);
+ xComp->dispose();
+}
+
+uno::Reference< drawing::XShapeGroup > SwXDrawPage::group(const uno::Reference< drawing::XShapes > & xShapes)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc || !xShapes.is())
+ throw uno::RuntimeException();
+ uno::Reference< drawing::XShapeGroup > xRet;
+ if(m_pDrawPage.is())
+ {
+
+ SwFmDrawPage* pPage = GetSvxPage();
+ if(pPage) //TODO: can this be Null?
+ {
+ // mark and return MarkList
+ const SdrMarkList& rMarkList = pPage->PreGroup(xShapes);
+ if ( rMarkList.GetMarkCount() > 0 )
+ {
+ for (size_t i = 0; i < rMarkList.GetMarkCount(); ++i)
+ {
+ const SdrObject *pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ if (RndStdIds::FLY_AS_CHAR == ::FindFrameFormat(const_cast<SdrObject*>(
+ pObj))->GetAnchor().GetAnchorId())
+ {
+ throw lang::IllegalArgumentException(
+ "Shape must not have 'as character' anchor!", nullptr, 0);
+ }
+ }
+
+ UnoActionContext aContext(m_pDoc);
+ m_pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+
+ SwDrawContact* pContact = m_pDoc->GroupSelection( *pPage->GetDrawView() );
+ m_pDoc->ChgAnchor(
+ pPage->GetDrawView()->GetMarkedObjectList(),
+ RndStdIds::FLY_AT_PARA,
+ true, false );
+
+ pPage->GetDrawView()->UnmarkAll();
+ if(pContact)
+ xRet = SwFmDrawPage::GetShapeGroup( pContact->GetMaster() );
+ m_pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ pPage->RemovePageView();
+ }
+ }
+ return xRet;
+}
+
+void SwXDrawPage::ungroup(const uno::Reference< drawing::XShapeGroup > & rShapeGroup)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(!m_pDrawPage.is())
+ return;
+
+ SwFmDrawPage* pPage = GetSvxPage();
+ if(!pPage) //TODO: can this be Null?
+ return;
+
+ pPage->PreUnGroup(rShapeGroup);
+ UnoActionContext aContext(m_pDoc);
+ m_pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+
+ m_pDoc->UnGroupSelection( *pPage->GetDrawView() );
+ m_pDoc->ChgAnchor( pPage->GetDrawView()->GetMarkedObjectList(),
+ RndStdIds::FLY_AT_PARA,
+ true, false );
+ m_pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ pPage->RemovePageView();
+}
+
+SwFmDrawPage* SwXDrawPage::GetSvxPage()
+{
+ if(!m_pDrawPage.is() && m_pDoc)
+ {
+ SolarMutexGuard aGuard;
+ // #i52858#
+ SwDrawModel* pModel = m_pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ SdrPage* pPage = pModel->GetPage( 0 );
+
+ {
+ // We need a Ref to the object during queryInterface or else
+ // it will be deleted
+ m_pDrawPage = new SwFmDrawPage(pPage);
+ }
+ if( m_pDrawPage.is() )
+ m_pDrawPage->setDelegator( static_cast<cppu::OWeakObject*>(this) );
+ }
+ return m_pDrawPage.get();
+}
+
+/**
+ * Renamed and outlined to detect where it's called
+ */
+void SwXDrawPage::InvalidateSwDoc()
+{
+ m_pDoc = nullptr;
+}
+
+const uno::Sequence< sal_Int8 > & SwXShape::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXShapeUnoTunnelId;
+ return theSwXShapeUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXShape::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ if( comphelper::isUnoTunnelId<SwXShape>(rId) )
+ {
+ return comphelper::getSomething_cast(this);
+ }
+
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rTunnelType = cppu::UnoType<lang::XUnoTunnel>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rTunnelType );
+ if(auto xAggTunnel = o3tl::tryAccess<uno::Reference<lang::XUnoTunnel>>(
+ aAgg))
+ {
+ if(xAggTunnel->is())
+ return (*xAggTunnel)->getSomething(rId);
+ }
+ }
+ return 0;
+}
+namespace
+{
+ void lcl_addShapePropertyEventFactories( SdrObject& _rObj, SwXShape& _rShape )
+ {
+ auto pProvider = std::make_unique<svx::PropertyValueProvider>( _rShape, "AnchorType" );
+ _rObj.getShapePropertyChangeNotifier().registerProvider( svx::ShapePropertyProviderId::TextDocAnchor, std::move(pProvider) );
+ }
+}
+
+SwXShape::SwXShape(
+ uno::Reference<uno::XInterface> & xShape,
+ SwDoc const*const pDoc)
+ : m_pPage(nullptr)
+ , m_pFormat(nullptr)
+ , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_SHAPE))
+ , m_pPropertyMapEntries(aSwMapProvider.GetPropertyMapEntries(PROPERTY_MAP_TEXT_SHAPE))
+ , m_pImpl(new SwShapeDescriptor_Impl(pDoc))
+ , m_bDescriptor(true)
+{
+ if(!xShape.is()) // default Ctor
+ return;
+
+ const uno::Type& rAggType = cppu::UnoType<uno::XAggregation>::get();
+ //aAgg contains a reference of the SvxShape!
+ {
+ uno::Any aAgg = xShape->queryInterface(rAggType);
+ aAgg >>= m_xShapeAgg;
+ // #i31698#
+ if ( m_xShapeAgg.is() )
+ {
+ m_xShapeAgg->queryAggregation( cppu::UnoType<drawing::XShape>::get()) >>= mxShape;
+ OSL_ENSURE( mxShape.is(),
+ "<SwXShape::SwXShape(..)> - no XShape found at <xShapeAgg>" );
+ }
+ }
+ xShape = nullptr;
+ osl_atomic_increment(&m_refCount);
+ if( m_xShapeAgg.is() )
+ m_xShapeAgg->setDelegator( static_cast<cppu::OWeakObject*>(this) );
+ osl_atomic_decrement(&m_refCount);
+
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(m_xShapeAgg);
+ if(pObj)
+ {
+ auto pFormat = ::FindFrameFormat( pObj );
+ if(pFormat)
+ SetFrameFormat(pFormat);
+
+ lcl_addShapePropertyEventFactories( *pObj, *this );
+ m_pImpl->m_bInitializedPropertyNotifier = true;
+ }
+
+}
+
+void SwXShape::AddExistingShapeToFormat( SdrObject const & _rObj )
+{
+ SdrObjListIter aIter( _rObj, SdrIterMode::DeepNoGroups );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pCurrent = aIter.Next();
+ OSL_ENSURE( pCurrent, "SwXShape::AddExistingShapeToFormat: invalid object list element!" );
+ if ( !pCurrent )
+ continue;
+
+ auto pSwShape = comphelper::getFromUnoTunnel<SwXShape>(pCurrent->getWeakUnoShape());
+ if ( pSwShape )
+ {
+ if ( pSwShape->m_bDescriptor )
+ {
+ auto pFormat = ::FindFrameFormat( pCurrent );
+ if ( pFormat )
+ pSwShape->SetFrameFormat(pFormat);
+ pSwShape->m_bDescriptor = false;
+ }
+
+ if ( !pSwShape->m_pImpl->m_bInitializedPropertyNotifier )
+ {
+ lcl_addShapePropertyEventFactories( *pCurrent, *pSwShape );
+ pSwShape->m_pImpl->m_bInitializedPropertyNotifier = true;
+ }
+ }
+ }
+}
+
+SwXShape::~SwXShape()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_xShapeAgg.is())
+ {
+ uno::Reference< uno::XInterface > xRef;
+ m_xShapeAgg->setDelegator(xRef);
+ }
+ m_pImpl.reset();
+ EndListeningAll();
+ if(m_pPage)
+ const_cast<SwFmDrawPage*>(m_pPage)->RemoveShape(this);
+ m_pPage = nullptr;
+}
+
+uno::Any SwXShape::queryInterface( const uno::Type& aType )
+{
+ uno::Any aRet;
+ SdrObject* pObj = nullptr;
+
+ if ((aType == cppu::UnoType<text::XText>::get())
+ || (aType == cppu::UnoType<text::XTextRange>::get())
+ || (aType == cppu::UnoType<text::XTextAppend>::get()))
+ {
+ pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+
+ aRet = SwTextBoxHelper::queryInterface(GetFrameFormat(), aType, pObj);
+ if (aRet.hasValue())
+ return aRet;
+ }
+ aRet = SwXShapeBaseClass::queryInterface(aType);
+ // #i53320# - follow-up of #i31698#
+ // interface drawing::XShape is overloaded. Thus, provide
+ // correct object instance.
+ if(!aRet.hasValue() && m_xShapeAgg.is())
+ {
+ if(aType == cppu::UnoType<XShape>::get())
+ aRet <<= uno::Reference<XShape>(this);
+ else
+ aRet = m_xShapeAgg->queryAggregation(aType);
+ }
+ return aRet;
+}
+
+uno::Sequence< uno::Type > SwXShape::getTypes( )
+{
+ uno::Sequence< uno::Type > aRet = SwXShapeBaseClass::getTypes();
+ if(m_xShapeAgg.is())
+ {
+ uno::Any aProv = m_xShapeAgg->queryAggregation(cppu::UnoType<XTypeProvider>::get());
+ if(aProv.hasValue())
+ {
+ uno::Reference< XTypeProvider > xAggProv;
+ aProv >>= xAggProv;
+ return comphelper::concatSequences(aRet, xAggProv->getTypes());
+ }
+ }
+ return aRet;
+}
+
+uno::Sequence< sal_Int8 > SwXShape::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXShape::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ if (!mxPropertySetInfo)
+ {
+ if(m_xShapeAgg.is())
+ {
+ const uno::Type& rPropSetType = cppu::UnoType<beans::XPropertySet>::get();
+ uno::Any aPSet = m_xShapeAgg->queryAggregation( rPropSetType );
+ if(auto xPrSet = o3tl::tryAccess<uno::Reference<beans::XPropertySet>>(
+ aPSet))
+ {
+ uno::Reference< beans::XPropertySetInfo > xInfo = (*xPrSet)->getPropertySetInfo();
+ // Expand PropertySetInfo!
+ const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
+ mxPropertySetInfo = new SfxExtItemPropertySetInfo( m_pPropertyMapEntries, aPropSeq );
+ }
+ }
+ if(!mxPropertySetInfo)
+ mxPropertySetInfo = m_pPropSet->getPropertySetInfo();
+ }
+ return mxPropertySetInfo;
+}
+
+void SwXShape::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if(!m_xShapeAgg.is())
+ return;
+
+ if(pEntry)
+ {
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException ("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ // with the layout it is possible to move the anchor without changing the position
+ if(pFormat)
+ {
+ SwAttrSet aSet(pFormat->GetAttrSet());
+ SwDoc* pDoc = pFormat->GetDoc();
+ if(RES_ANCHOR == pEntry->nWID && MID_ANCHOR_ANCHORFRAME == pEntry->nMemberId)
+ {
+ bool bDone = false;
+ uno::Reference<text::XTextFrame> xFrame;
+ if(aValue >>= xFrame)
+ {
+ SwXFrame* pFrame = comphelper::getFromUnoTunnel<SwXFrame>(xFrame);
+ if(pFrame && pFrame->GetFrameFormat() &&
+ pFrame->GetFrameFormat()->GetDoc() == pDoc)
+ {
+ UnoActionContext aCtx(pDoc);
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aItemSet( pDoc->GetAttrPool() );
+ aItemSet.SetParent(&pFormat->GetAttrSet());
+ SwFormatAnchor aAnchor = static_cast<const SwFormatAnchor&>(aItemSet.Get(pEntry->nWID));
+ SwPosition aPos(*pFrame->GetFrameFormat()->GetContent().GetContentIdx());
+ aAnchor.SetAnchor(&aPos);
+ aAnchor.SetType(RndStdIds::FLY_AT_FLY);
+ aItemSet.Put(aAnchor);
+ pFormat->SetFormatAttr(aItemSet);
+ bDone = true;
+ }
+ }
+ if(!bDone)
+ throw lang::IllegalArgumentException();
+ }
+ else if(RES_OPAQUE == pEntry->nWID)
+ {
+ SvxShape* pSvxShape = GetSvxShape();
+ SAL_WARN_IF(!pSvxShape, "sw.uno", "No SvxShape found!");
+ if(pSvxShape)
+ {
+ SdrObject* pObj = pSvxShape->GetSdrObject();
+ // set layer of new drawing
+ // object to corresponding invisible layer.
+ bool bIsVisible = pDoc->getIDocumentDrawModelAccess().IsVisibleLayerId( pObj->GetLayer() );
+ if(SdrInventor::FmForm != pObj->GetObjInventor())
+ {
+ pObj->SetLayer( *o3tl::doAccess<bool>(aValue)
+ ? ( bIsVisible ? pDoc->getIDocumentDrawModelAccess().GetHeavenId() : pDoc->getIDocumentDrawModelAccess().GetInvisibleHeavenId() )
+ : ( bIsVisible ? pDoc->getIDocumentDrawModelAccess().GetHellId() : pDoc->getIDocumentDrawModelAccess().GetInvisibleHellId() ));
+ }
+ else
+ {
+ pObj->SetLayer( bIsVisible ? pDoc->getIDocumentDrawModelAccess().GetControlsId() : pDoc->getIDocumentDrawModelAccess().GetInvisibleControlsId());
+ }
+
+ }
+
+ }
+ // #i26791# - special handling for property FN_TEXT_RANGE
+ else if ( FN_TEXT_RANGE == pEntry->nWID )
+ {
+ SwFormatAnchor aAnchor( aSet.Get( RES_ANCHOR ) );
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ // set property <TextRange> not valid for to-page anchored shapes
+ throw lang::IllegalArgumentException();
+ }
+
+ std::unique_ptr<SwUnoInternalPaM> pInternalPam(
+ new SwUnoInternalPaM( *(pFormat->GetDoc()) ));
+ uno::Reference< text::XTextRange > xRg;
+ aValue >>= xRg;
+ if (!::sw::XTextRangeToSwPaM(*pInternalPam, xRg) )
+ {
+ throw uno::RuntimeException();
+ }
+
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ //delete old SwFormatFlyCnt
+ //With AnchorAsCharacter the current TextAttribute has to be deleted.
+ //Tbis removes the frame format too.
+ //To prevent this the connection between format and attribute has to be broken before.
+ const SwPosition *pPos = aAnchor.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ SAL_WARN_IF( !pTextNode->HasHints(), "sw.uno", "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt(
+ nIdx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing Hint.");
+ SAL_WARN_IF( pHint->Which() != RES_TXTATR_FLYCNT,
+ "sw.uno", "Missing FlyInCnt-Hint." );
+ SAL_WARN_IF( pHint->GetFlyCnt().GetFrameFormat() != pFormat,
+ "sw.uno", "Wrong TextFlyCnt-Hint." );
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt())
+ .SetFlyFormat();
+
+ //The connection is removed now the attribute can be deleted.
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx );
+ //create a new one
+ SwTextNode *pNd = pInternalPam->GetNode().GetTextNode();
+ SAL_WARN_IF( !pNd, "sw.uno", "Cursor not at TextNode." );
+ SwFormatFlyCnt aFormat( pFormat );
+ pNd->InsertItem(aFormat, pInternalPam->GetPoint()
+ ->nContent.GetIndex(), 0 );
+ //Refetch in case SwTextNode::InsertItem causes it to be deleted
+ pFormat = GetFrameFormat();
+ }
+ else
+ {
+ aAnchor.SetAnchor( pInternalPam->GetPoint() );
+ aSet.Put(aAnchor);
+ pFormat->SetFormatAttr(aSet);
+ }
+ }
+ else if (pEntry->nWID == FN_TEXT_BOX)
+ {
+ auto pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if (pEntry->nMemberId == MID_TEXT_BOX)
+ {
+ bool bValue(false);
+ aValue >>= bValue;
+
+ if (bValue)
+ SwTextBoxHelper::create(pFormat, pObj);
+ else
+ SwTextBoxHelper::destroy(pFormat, pObj);
+ }
+ else if (pEntry->nMemberId == MID_TEXT_BOX_CONTENT)
+ {
+ if (aValue.getValueType()
+ == cppu::UnoType<uno::Reference<text::XTextFrame>>::get())
+ SwTextBoxHelper::set(pFormat, pObj,
+ aValue.get<uno::Reference<text::XTextFrame>>());
+ else
+ SAL_WARN( "sw.uno", "This is not a TextFrame!" );
+ }
+ }
+ else if (pEntry->nWID == RES_CHAIN)
+ {
+ if (pEntry->nMemberId == MID_CHAIN_NEXTNAME || pEntry->nMemberId == MID_CHAIN_PREVNAME)
+ SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue,
+ SdrObject::getSdrObjectFromXShape(mxShape));
+ }
+ // #i28749#
+ else if ( FN_SHAPE_POSITION_LAYOUT_DIR == pEntry->nWID )
+ {
+ sal_Int16 nPositionLayoutDir = 0;
+ aValue >>= nPositionLayoutDir;
+ pFormat->SetPositionLayoutDir( nPositionLayoutDir );
+ }
+ else if( pDoc->getIDocumentLayoutAccess().GetCurrentLayout())
+ {
+ UnoActionContext aCtx(pDoc);
+ if(RES_ANCHOR == pEntry->nWID && MID_ANCHOR_ANCHORTYPE == pEntry->nMemberId)
+ {
+ SdrObject* pObj = pFormat->FindSdrObject();
+ SdrMarkList aList;
+ SdrMark aMark(pObj);
+ aList.InsertEntry(aMark);
+ sal_Int32 nAnchor = 0;
+ cppu::enum2int( nAnchor, aValue );
+ pDoc->ChgAnchor( aList, static_cast<RndStdIds>(nAnchor),
+ false, true );
+ }
+ else
+ {
+ m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
+ pFormat->SetFormatAttr(aSet);
+ }
+ }
+ else if( RES_FRM_SIZE == pEntry->nWID &&
+ ( pEntry->nMemberId == MID_FRMSIZE_REL_HEIGHT || pEntry->nMemberId == MID_FRMSIZE_REL_WIDTH
+ || pEntry->nMemberId == MID_FRMSIZE_REL_HEIGHT_RELATION
+ || pEntry->nMemberId == MID_FRMSIZE_REL_WIDTH_RELATION ) )
+ {
+ SvxShape* pSvxShape = GetSvxShape();
+ SAL_WARN_IF(!pSvxShape, "sw.uno", "No SvxShape found!");
+ if(pSvxShape)
+ {
+ SdrObject* pObj = pSvxShape->GetSdrObject();
+ sal_Int16 nPercent(100);
+ aValue >>= nPercent;
+ switch (pEntry->nMemberId)
+ {
+ case MID_FRMSIZE_REL_WIDTH:
+ pObj->SetRelativeWidth( nPercent / 100.0 );
+ break;
+ case MID_FRMSIZE_REL_HEIGHT:
+ pObj->SetRelativeHeight( nPercent / 100.0 );
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ pObj->SetRelativeWidthRelation(nPercent);
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ pObj->SetRelativeHeightRelation(nPercent);
+ break;
+ }
+ }
+ }
+ else if (pEntry->nWID == RES_HORI_ORIENT
+ && pEntry->nMemberId == MID_HORIORIENT_RELATION
+ && aSet.Get(RES_ANCHOR).GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ uno::Any value(aValue);
+ sal_Int16 nRelOrient(text::RelOrientation::PAGE_FRAME);
+ aValue >>= nRelOrient;
+ if (sw::GetAtPageRelOrientation(nRelOrient, true))
+ {
+ SAL_WARN("sw.core", "SwXShape: fixing invalid horizontal RelOrientation for at-page anchor");
+ value <<= nRelOrient;
+ }
+ m_pPropSet->setPropertyValue( *pEntry, value, aSet );
+ pFormat->SetFormatAttr(aSet);
+ }
+ else
+ {
+ m_pPropSet->setPropertyValue( *pEntry, aValue, aSet );
+
+ if(RES_ANCHOR == pEntry->nWID && MID_ANCHOR_ANCHORTYPE == pEntry->nMemberId)
+ {
+ bool bSetAttr = true;
+ text::TextContentAnchorType eNewAnchor = static_cast<text::TextContentAnchorType>(SWUnoHelper::GetEnumAsInt32( aValue ));
+
+ //if old anchor was in_cntnt the related text attribute has to be removed
+ const SwFormatAnchor& rOldAnchor = pFormat->GetAnchor();
+ RndStdIds eOldAnchorId = rOldAnchor.GetAnchorId();
+ SdrObject* pObj = pFormat->FindSdrObject();
+ SwFrameFormat *pFlyFormat = FindFrameFormat( pObj );
+ pFlyFormat->DelFrames();
+ if( text::TextContentAnchorType_AS_CHARACTER != eNewAnchor &&
+ (RndStdIds::FLY_AS_CHAR == eOldAnchorId))
+ {
+ //With AnchorAsCharacter the current TextAttribute has to be deleted.
+ //Tbis removes the frame format too.
+ //To prevent this the connection between format and attribute has to be broken before.
+ const SwPosition *pPos = rOldAnchor.GetContentAnchor();
+ SwTextNode *pTextNode = pPos->nNode.GetNode().GetTextNode();
+ SAL_WARN_IF( !pTextNode->HasHints(), "sw.uno", "Missing FlyInCnt-Hint." );
+ const sal_Int32 nIdx = pPos->nContent.GetIndex();
+ SwTextAttr * const pHint =
+ pTextNode->GetTextAttrForCharAt(
+ nIdx, RES_TXTATR_FLYCNT );
+ assert(pHint && "Missing Hint.");
+ SAL_WARN_IF( pHint->Which() != RES_TXTATR_FLYCNT,
+ "sw.uno", "Missing FlyInCnt-Hint." );
+ SAL_WARN_IF( pHint->GetFlyCnt().GetFrameFormat() != pFlyFormat,
+ "sw.uno", "Wrong TextFlyCnt-Hint." );
+ const_cast<SwFormatFlyCnt&>(pHint->GetFlyCnt())
+ .SetFlyFormat();
+
+ //The connection is removed now the attribute can be deleted.
+ pTextNode->DeleteAttributes(RES_TXTATR_FLYCNT, nIdx);
+ }
+ else if( text::TextContentAnchorType_AT_PAGE != eNewAnchor &&
+ (RndStdIds::FLY_AT_PAGE == eOldAnchorId))
+ {
+ SwFormatAnchor aNewAnchor( dynamic_cast< const SwFormatAnchor& >( aSet.Get( RES_ANCHOR ) ) );
+ //if the fly has been anchored at page then it needs to be connected
+ //to the content position
+ SwPaM aPam(pDoc->GetNodes().GetEndOfContent());
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() )
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ Point aTmp( pObj->GetSnapRect().TopLeft() );
+ pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aTmp, &aState );
+ }
+ else
+ {
+ //without access to the layout the last node of the body will be used as anchor position
+ aPam.Move( fnMoveBackward, GoInDoc );
+ }
+ //anchor position has to be inserted after the text attribute has been inserted
+ aNewAnchor.SetAnchor( aPam.GetPoint() );
+ aSet.Put( aNewAnchor );
+ pFormat->SetFormatAttr(aSet);
+ bSetAttr = false;
+ }
+ if( text::TextContentAnchorType_AS_CHARACTER == eNewAnchor &&
+ (RndStdIds::FLY_AS_CHAR != eOldAnchorId))
+ {
+ SwPaM aPam(pDoc->GetNodes().GetEndOfContent());
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() )
+ {
+ SwCursorMoveState aState( CursorMoveState::SetOnlyText );
+ Point aTmp( pObj->GetSnapRect().TopLeft() );
+ pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->GetModelPositionForViewPoint( aPam.GetPoint(), aTmp, &aState );
+ }
+ else
+ {
+ //without access to the layout the last node of the body will be used as anchor position
+ aPam.Move( fnMoveBackward, GoInDoc );
+ }
+ //the RES_TXTATR_FLYCNT needs to be added now
+ SwTextNode *pNd = aPam.GetNode().GetTextNode();
+ SAL_WARN_IF( !pNd, "sw.uno", "Cursor is not in a TextNode." );
+ SwFormatFlyCnt aFormat( pFlyFormat );
+ pNd->InsertItem(aFormat,
+ aPam.GetPoint()->nContent.GetIndex(), 0 );
+ --aPam.GetPoint()->nContent; // InsertItem moved it
+ SwFormatAnchor aNewAnchor(
+ dynamic_cast<const SwFormatAnchor&>(
+ aSet.Get(RES_ANCHOR)));
+ aNewAnchor.SetAnchor( aPam.GetPoint() );
+ aSet.Put( aNewAnchor );
+ }
+ if( bSetAttr )
+ pFormat->SetFormatAttr(aSet);
+
+ // If this property is an anchor change, and there is a group shape with textboxes
+ // do anchor sync in time unless the anchor sync in the porfly will cause crash during
+ // layout calculation (When importing an inline shape in docx via dmapper).
+ if (pFormat->Which() == RES_DRAWFRMFMT && pFormat->GetOtherTextBoxFormats()
+ && pFormat->GetOtherTextBoxFormats()->GetTextBoxCount()
+ > o3tl::make_unsigned(1))
+ SwTextBoxHelper::synchronizeGroupTextBoxProperty(
+ SwTextBoxHelper::changeAnchor, pFormat,
+ SdrObject::getSdrObjectFromXShape(mxShape));
+ }
+ else
+ pFormat->SetFormatAttr(aSet);
+ }
+
+ // We have a pFormat and a pEntry as well: try to sync TextBox property.
+ SwTextBoxHelper::syncProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aValue,
+ SdrObject::getSdrObjectFromXShape(mxShape));
+ }
+ else
+ {
+ SfxPoolItem* pItem = nullptr;
+ switch(pEntry->nWID)
+ {
+ case RES_ANCHOR:
+ pItem = m_pImpl->GetAnchor(true);
+ break;
+ case RES_HORI_ORIENT:
+ pItem = m_pImpl->GetHOrient(true);
+ break;
+ case RES_VERT_ORIENT:
+ pItem = m_pImpl->GetVOrient(true);
+ break;
+ case RES_LR_SPACE:
+ pItem = m_pImpl->GetLRSpace(true);
+ break;
+ case RES_UL_SPACE:
+ pItem = m_pImpl->GetULSpace(true);
+ break;
+ case RES_SURROUND:
+ pItem = m_pImpl->GetSurround(true);
+ break;
+ case FN_TEXT_RANGE:
+ if(auto tr = o3tl::tryAccess<
+ uno::Reference<text::XTextRange>>(aValue))
+ {
+ uno::Reference< text::XTextRange > & rRange = m_pImpl->GetTextRange();
+ rRange = *tr;
+ }
+ break;
+ case RES_OPAQUE :
+ m_pImpl->SetOpaque(*o3tl::doAccess<bool>(aValue));
+ break;
+ // #i26791#
+ case RES_FOLLOW_TEXT_FLOW:
+ {
+ pItem = m_pImpl->GetFollowTextFlow( true );
+ }
+ break;
+ // #i28701#
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ {
+ pItem = m_pImpl->GetWrapInfluenceOnObjPos( true );
+ }
+ break;
+ // #i28749#
+ case FN_SHAPE_POSITION_LAYOUT_DIR :
+ {
+ sal_Int16 nPositionLayoutDir = 0;
+ aValue >>= nPositionLayoutDir;
+ m_pImpl->SetPositionLayoutDir( nPositionLayoutDir );
+ }
+ break;
+ }
+ if(pItem)
+ pItem->PutValue(aValue, pEntry->nMemberId);
+ }
+ }
+ else
+ {
+ const uno::Type& rPSetType =
+ cppu::UnoType<beans::XPropertySet>::get();
+ uno::Any aPSet = m_xShapeAgg->queryAggregation(rPSetType);
+ auto xPrSet = o3tl::tryAccess<uno::Reference<beans::XPropertySet>>(
+ aPSet);
+ if(!xPrSet)
+ throw uno::RuntimeException();
+ // #i31698# - setting the caption point of a
+ // caption object doesn't have to change the object position.
+ // Thus, keep the position, before the caption point is set and
+ // restore it afterwards.
+ awt::Point aKeepedPosition( 0, 0 );
+ if ( rPropertyName == "CaptionPoint" && getShapeType() == "com.sun.star.drawing.CaptionShape" )
+ {
+ aKeepedPosition = getPosition();
+ }
+ if( pFormat && pFormat->GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ {
+ UnoActionContext aCtx(pFormat->GetDoc());
+ (*xPrSet)->setPropertyValue(rPropertyName, aValue);
+ }
+ else
+ (*xPrSet)->setPropertyValue(rPropertyName, aValue);
+
+ if (pFormat)
+ {
+ // We have a pFormat (but no pEntry): try to sync TextBox property.
+ SwTextBoxHelper::syncProperty(pFormat, rPropertyName, aValue,
+ SdrObject::getSdrObjectFromXShape(mxShape));
+ }
+
+ // #i31698# - restore object position, if caption point is set.
+ if ( rPropertyName == "CaptionPoint" && getShapeType() == "com.sun.star.drawing.CaptionShape" )
+ {
+ setPosition( aKeepedPosition );
+ }
+ }
+}
+
+uno::Any SwXShape::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(m_xShapeAgg.is())
+ {
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if(pEntry)
+ {
+ if(pFormat)
+ {
+ if(RES_OPAQUE == pEntry->nWID)
+ {
+ SvxShape* pSvxShape = GetSvxShape();
+ OSL_ENSURE(pSvxShape, "No SvxShape found!");
+ if(pSvxShape)
+ {
+ SdrObject* pObj = pSvxShape->GetSdrObject();
+ // consider invisible layers
+ aRet <<=
+ ( pObj->GetLayer() != pFormat->GetDoc()->getIDocumentDrawModelAccess().GetHellId() &&
+ pObj->GetLayer() != pFormat->GetDoc()->getIDocumentDrawModelAccess().GetInvisibleHellId() );
+ }
+ }
+ else if(FN_ANCHOR_POSITION == pEntry->nWID)
+ {
+ SvxShape* pSvxShape = GetSvxShape();
+ OSL_ENSURE(pSvxShape, "No SvxShape found!");
+ if(pSvxShape)
+ {
+ SdrObject* pObj = pSvxShape->GetSdrObject();
+ Point aPt = pObj->GetAnchorPos();
+ awt::Point aPoint( convertTwipToMm100( aPt.X() ),
+ convertTwipToMm100( aPt.Y() ) );
+ aRet <<= aPoint;
+ }
+ }
+ // #i26791# - special handling for FN_TEXT_RANGE
+ else if ( FN_TEXT_RANGE == pEntry->nWID )
+ {
+ const SwFormatAnchor aAnchor = pFormat->GetAnchor();
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PAGE)
+ {
+ // return nothing, because property <TextRange> isn't
+ // valid for to-page anchored shapes
+ uno::Any aAny;
+ aRet = aAny;
+ }
+ else
+ {
+ if ( aAnchor.GetContentAnchor() )
+ {
+ const uno::Reference< text::XTextRange > xTextRange
+ = SwXTextRange::CreateXTextRange(
+ *pFormat->GetDoc(),
+ *aAnchor.GetContentAnchor(),
+ nullptr );
+ aRet <<= xTextRange;
+ }
+ else
+ {
+ // return nothing
+ uno::Any aAny;
+ aRet = aAny;
+ }
+ }
+ }
+ else if (pEntry->nWID == FN_TEXT_BOX)
+ {
+ if (pEntry->nMemberId == MID_TEXT_BOX)
+ {
+ auto pSvxShape = GetSvxShape();
+ bool bValue = SwTextBoxHelper::isTextBox(
+ pFormat, RES_DRAWFRMFMT,
+ ((pSvxShape && pSvxShape->GetSdrObject()) ? pSvxShape->GetSdrObject()
+ : pFormat->FindRealSdrObject()));
+ aRet <<= bValue;
+ }
+ else if (pEntry->nMemberId == MID_TEXT_BOX_CONTENT)
+ {
+ auto pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ auto xRange = SwTextBoxHelper::queryInterface(
+ pFormat, cppu::UnoType<text::XText>::get(),
+ pObj ? pObj : pFormat->FindRealSdrObject());
+ uno::Reference<text::XTextFrame> xFrame(xRange, uno::UNO_QUERY);
+ if (xFrame.is())
+ aRet <<= xFrame;
+ }
+ }
+ else if (pEntry->nWID == RES_CHAIN)
+ {
+ switch (pEntry->nMemberId)
+ {
+ case MID_CHAIN_PREVNAME:
+ case MID_CHAIN_NEXTNAME:
+ case MID_CHAIN_NAME:
+ SwTextBoxHelper::getProperty(pFormat, pEntry->nWID, pEntry->nMemberId, aRet);
+ break;
+ }
+ }
+ // #i28749#
+ else if ( FN_SHAPE_TRANSFORMATION_IN_HORI_L2R == pEntry->nWID )
+ {
+ // get property <::drawing::Shape::Transformation>
+ // without conversion to layout direction as below
+ aRet = _getPropAtAggrObj( "Transformation" );
+ }
+ else if ( FN_SHAPE_POSITION_LAYOUT_DIR == pEntry->nWID )
+ {
+ aRet <<= pFormat->GetPositionLayoutDir();
+ }
+ // #i36248#
+ else if ( FN_SHAPE_STARTPOSITION_IN_HORI_L2R == pEntry->nWID )
+ {
+ // get property <::drawing::Shape::StartPosition>
+ // without conversion to layout direction as below
+ aRet = _getPropAtAggrObj( "StartPosition" );
+ }
+ else if ( FN_SHAPE_ENDPOSITION_IN_HORI_L2R == pEntry->nWID )
+ {
+ // get property <::drawing::Shape::EndPosition>
+ // without conversion to layout direction as below
+ aRet = _getPropAtAggrObj( "EndPosition" );
+ }
+ else if (pEntry->nWID == RES_FRM_SIZE &&
+ (pEntry->nMemberId == MID_FRMSIZE_REL_HEIGHT ||
+ pEntry->nMemberId == MID_FRMSIZE_REL_WIDTH ||
+ pEntry->nMemberId == MID_FRMSIZE_REL_HEIGHT_RELATION ||
+ pEntry->nMemberId == MID_FRMSIZE_REL_WIDTH_RELATION))
+ {
+ SvxShape* pSvxShape = GetSvxShape();
+ SAL_WARN_IF(!pSvxShape, "sw.uno", "No SvxShape found!");
+ sal_Int16 nRet = 0;
+ if (pSvxShape)
+ {
+ SdrObject* pObj = pSvxShape->GetSdrObject();
+ switch (pEntry->nMemberId)
+ {
+ case MID_FRMSIZE_REL_WIDTH:
+ if (pObj->GetRelativeWidth())
+ nRet = *pObj->GetRelativeWidth() * 100;
+ break;
+ case MID_FRMSIZE_REL_HEIGHT:
+ if (pObj->GetRelativeHeight())
+ nRet = *pObj->GetRelativeHeight() * 100;
+ break;
+ case MID_FRMSIZE_REL_WIDTH_RELATION:
+ nRet = pObj->GetRelativeWidthRelation();
+ break;
+ case MID_FRMSIZE_REL_HEIGHT_RELATION:
+ nRet = pObj->GetRelativeHeightRelation();
+ break;
+ }
+ }
+ aRet <<= nRet;
+ }
+ else
+ {
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ m_pPropSet->getPropertyValue(*pEntry, rSet, aRet);
+ }
+ }
+ else
+ {
+ SfxPoolItem* pItem = nullptr;
+ switch(pEntry->nWID)
+ {
+ case RES_ANCHOR:
+ pItem = m_pImpl->GetAnchor();
+ break;
+ case RES_HORI_ORIENT:
+ pItem = m_pImpl->GetHOrient();
+ break;
+ case RES_VERT_ORIENT:
+ pItem = m_pImpl->GetVOrient();
+ break;
+ case RES_LR_SPACE:
+ pItem = m_pImpl->GetLRSpace();
+ break;
+ case RES_UL_SPACE:
+ pItem = m_pImpl->GetULSpace();
+ break;
+ case RES_SURROUND:
+ pItem = m_pImpl->GetSurround();
+ break;
+ case FN_TEXT_RANGE :
+ aRet <<= m_pImpl->GetTextRange();
+ break;
+ case RES_OPAQUE :
+ aRet <<= m_pImpl->GetOpaque();
+ break;
+ case FN_ANCHOR_POSITION :
+ {
+ aRet <<= awt::Point();
+ }
+ break;
+ // #i26791#
+ case RES_FOLLOW_TEXT_FLOW :
+ {
+ pItem = m_pImpl->GetFollowTextFlow();
+ }
+ break;
+ // #i28701#
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ {
+ pItem = m_pImpl->GetWrapInfluenceOnObjPos();
+ }
+ break;
+ // #i28749#
+ case FN_SHAPE_TRANSFORMATION_IN_HORI_L2R:
+ {
+ // get property <::drawing::Shape::Transformation>
+ // without conversion to layout direction as below
+ aRet = _getPropAtAggrObj( "Transformation" );
+ }
+ break;
+ case FN_SHAPE_POSITION_LAYOUT_DIR:
+ {
+ aRet <<= m_pImpl->GetPositionLayoutDir();
+ }
+ break;
+ // #i36248#
+ case FN_SHAPE_STARTPOSITION_IN_HORI_L2R:
+ {
+ // get property <::drawing::Shape::StartPosition>
+ // without conversion to layout direction as below
+ aRet = _getPropAtAggrObj( "StartPosition" );
+ }
+ break;
+ case FN_SHAPE_ENDPOSITION_IN_HORI_L2R:
+ {
+ // get property <::drawing::Shape::StartPosition>
+ // without conversion to layout direction as below
+ aRet = _getPropAtAggrObj( "EndPosition" );
+ }
+ break;
+ }
+ if(pItem)
+ pItem->QueryValue(aRet, pEntry->nMemberId);
+ }
+ }
+ else
+ {
+ aRet = _getPropAtAggrObj( rPropertyName );
+
+ // #i31698# - convert the position (translation)
+ // of the drawing object in the transformation
+ if ( rPropertyName == "Transformation" )
+ {
+ drawing::HomogenMatrix3 aMatrix;
+ aRet >>= aMatrix;
+ aRet <<= ConvertTransformationToLayoutDir( aMatrix );
+ }
+ // #i36248#
+ else if ( rPropertyName == "StartPosition" )
+ {
+ awt::Point aStartPos;
+ aRet >>= aStartPos;
+ // #i59051#
+ aRet <<= ConvertStartOrEndPosToLayoutDir( aStartPos );
+ }
+ else if ( rPropertyName == "EndPosition" )
+ {
+ awt::Point aEndPos;
+ aRet >>= aEndPos;
+ // #i59051#
+ aRet <<= ConvertStartOrEndPosToLayoutDir( aEndPos );
+ }
+ // #i59051#
+ else if ( rPropertyName == "PolyPolygonBezier" )
+ {
+ drawing::PolyPolygonBezierCoords aPath;
+ aRet >>= aPath;
+ aRet <<= ConvertPolyPolygonBezierToLayoutDir( aPath );
+ }
+ else if (rPropertyName == "ZOrder")
+ {
+ // Convert the real draw page position to the logical one that ignores textboxes.
+ if (pFormat)
+ {
+ const SdrObject* pObj = pFormat->FindRealSdrObject();
+ if (pObj)
+ {
+ bool bConvert = true;
+ if (SvxShape* pSvxShape = GetSvxShape())
+ // In case of group shapes, pSvxShape points to the child shape, while pObj points to the outermost group shape.
+ if (pSvxShape->GetSdrObject() != pObj)
+ // Textboxes are not expected inside group shapes, so no conversion is necessary there.
+ bConvert = false;
+ if (bConvert)
+ {
+ aRet <<= SwTextBoxHelper::getOrdNum(pObj);
+ }
+ }
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+uno::Any SwXShape::_getPropAtAggrObj( const OUString& _rPropertyName )
+{
+ uno::Any aRet;
+
+ const uno::Type& rPSetType =
+ cppu::UnoType<beans::XPropertySet>::get();
+ uno::Any aPSet = m_xShapeAgg->queryAggregation(rPSetType);
+ auto xPrSet = o3tl::tryAccess<uno::Reference<beans::XPropertySet>>(aPSet);
+ if ( !xPrSet )
+ {
+ throw uno::RuntimeException();
+ }
+ aRet = (*xPrSet)->getPropertyValue( _rPropertyName );
+
+ return aRet;
+}
+
+beans::PropertyState SwXShape::getPropertyState( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< OUString > aNames { rPropertyName };
+ uno::Sequence< beans::PropertyState > aStates = getPropertyStates(aNames);
+ return aStates.getConstArray()[0];
+}
+
+uno::Sequence< beans::PropertyState > SwXShape::getPropertyStates(
+ const uno::Sequence< OUString >& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ uno::Sequence< beans::PropertyState > aRet(aPropertyNames.getLength());
+ if(!m_xShapeAgg.is())
+ throw uno::RuntimeException();
+
+ SvxShape* pSvxShape = GetSvxShape();
+ bool bGroupMember = false;
+ bool bFormControl = false;
+ SdrObject* pObject = pSvxShape ? pSvxShape->GetSdrObject() : nullptr;
+ if(pObject)
+ {
+ bGroupMember = pObject->getParentSdrObjectFromSdrObject() != nullptr;
+ bFormControl = pObject->GetObjInventor() == SdrInventor::FmForm;
+ }
+ const OUString* pNames = aPropertyNames.getConstArray();
+ beans::PropertyState* pRet = aRet.getArray();
+ uno::Reference< XPropertyState > xShapePrState;
+ for(sal_Int32 nProperty = 0; nProperty < aPropertyNames.getLength(); nProperty++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName( pNames[nProperty] );
+ if(pEntry)
+ {
+ if(RES_OPAQUE == pEntry->nWID)
+ pRet[nProperty] = bFormControl ?
+ beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ else if(FN_ANCHOR_POSITION == pEntry->nWID)
+ pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
+ else if(FN_TEXT_RANGE == pEntry->nWID)
+ pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
+ else if(bGroupMember)
+ pRet[nProperty] = beans::PropertyState_DEFAULT_VALUE;
+ else if (pEntry->nWID == RES_FRM_SIZE &&
+ (pEntry->nMemberId == MID_FRMSIZE_REL_HEIGHT_RELATION ||
+ pEntry->nMemberId == MID_FRMSIZE_REL_WIDTH_RELATION))
+ pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
+ else if (pEntry->nWID == FN_TEXT_BOX)
+ {
+ // The TextBox property is set, if we can find a textbox for this shape.
+ if (pFormat
+ && SwTextBoxHelper::isTextBox(pFormat, RES_DRAWFRMFMT,
+ SdrObject::getSdrObjectFromXShape(mxShape)))
+ pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
+ else
+ pRet[nProperty] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else if(pFormat)
+ {
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ SfxItemState eItemState = rSet.GetItemState(pEntry->nWID, false);
+
+ if(SfxItemState::SET == eItemState)
+ pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
+ else if(SfxItemState::DEFAULT == eItemState)
+ pRet[nProperty] = beans::PropertyState_DEFAULT_VALUE;
+ else
+ pRet[nProperty] = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ else
+ {
+ SfxPoolItem* pItem = nullptr;
+ switch(pEntry->nWID)
+ {
+ case RES_ANCHOR:
+ pItem = m_pImpl->GetAnchor();
+ break;
+ case RES_HORI_ORIENT:
+ pItem = m_pImpl->GetHOrient();
+ break;
+ case RES_VERT_ORIENT:
+ pItem = m_pImpl->GetVOrient();
+ break;
+ case RES_LR_SPACE:
+ pItem = m_pImpl->GetLRSpace();
+ break;
+ case RES_UL_SPACE:
+ pItem = m_pImpl->GetULSpace();
+ break;
+ case RES_SURROUND:
+ pItem = m_pImpl->GetSurround();
+ break;
+ // #i28701#
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ {
+ pItem = m_pImpl->GetWrapInfluenceOnObjPos();
+ }
+ break;
+ }
+ if(pItem)
+ pRet[nProperty] = beans::PropertyState_DIRECT_VALUE;
+ else
+ pRet[nProperty] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else
+ {
+ if(!xShapePrState.is())
+ {
+ const uno::Type& rPStateType = cppu::UnoType<XPropertyState>::get();
+ uno::Any aPState = m_xShapeAgg->queryAggregation(rPStateType);
+ auto ps = o3tl::tryAccess<uno::Reference<XPropertyState>>(
+ aPState);
+ if(!ps)
+ throw uno::RuntimeException();
+ xShapePrState = *ps;
+ }
+ pRet[nProperty] = xShapePrState->getPropertyState(pNames[nProperty]);
+ }
+ }
+
+ return aRet;
+}
+
+void SwXShape::setPropertyToDefault( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!m_xShapeAgg.is())
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if(pEntry)
+ {
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw uno::RuntimeException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ if(pFormat)
+ {
+ const SfxItemSet& rSet = pFormat->GetAttrSet();
+ SfxItemSet aSet(pFormat->GetDoc()->GetAttrPool(), pEntry->nWID, pEntry->nWID);
+ aSet.SetParent(&rSet);
+ aSet.ClearItem(pEntry->nWID);
+ pFormat->GetDoc()->SetAttr(aSet, *pFormat);
+ }
+ else
+ {
+ switch(pEntry->nWID)
+ {
+ case RES_ANCHOR: m_pImpl->RemoveAnchor(); break;
+ case RES_HORI_ORIENT: m_pImpl->RemoveHOrient(); break;
+ case RES_VERT_ORIENT: m_pImpl->RemoveVOrient(); break;
+ case RES_LR_SPACE: m_pImpl->RemoveLRSpace(); break;
+ case RES_UL_SPACE: m_pImpl->RemoveULSpace(); break;
+ case RES_SURROUND: m_pImpl->RemoveSurround();break;
+ case RES_OPAQUE : m_pImpl->SetOpaque(false); break;
+ case FN_TEXT_RANGE :
+ break;
+ // #i26791#
+ case RES_FOLLOW_TEXT_FLOW:
+ {
+ m_pImpl->RemoveFollowTextFlow();
+ }
+ break;
+ // #i28701#
+ case RES_WRAP_INFLUENCE_ON_OBJPOS:
+ {
+ m_pImpl->RemoveWrapInfluenceOnObjPos();
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ const uno::Type& rPStateType = cppu::UnoType<XPropertyState>::get();
+ uno::Any aPState = m_xShapeAgg->queryAggregation(rPStateType);
+ auto xShapePrState = o3tl::tryAccess<uno::Reference<XPropertyState>>(
+ aPState);
+ if(!xShapePrState)
+ throw uno::RuntimeException();
+ (*xShapePrState)->setPropertyToDefault( rPropertyName );
+ }
+
+}
+
+uno::Any SwXShape::getPropertyDefault( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ uno::Any aRet;
+ if(!m_xShapeAgg.is())
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName( rPropertyName );
+ if(pEntry)
+ {
+ if(!(pEntry->nWID < RES_FRMATR_END && pFormat))
+ throw uno::RuntimeException();
+
+ const SfxPoolItem& rDefItem =
+ pFormat->GetDoc()->GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(aRet, pEntry->nMemberId);
+
+ }
+ else
+ {
+ const uno::Type& rPStateType = cppu::UnoType<XPropertyState>::get();
+ uno::Any aPState = m_xShapeAgg->queryAggregation(rPStateType);
+ auto xShapePrState = o3tl::tryAccess<uno::Reference<XPropertyState>>(
+ aPState);
+ if(!xShapePrState)
+ throw uno::RuntimeException();
+ (*xShapePrState)->getPropertyDefault( rPropertyName );
+ }
+
+ return aRet;
+}
+
+void SwXShape::addPropertyChangeListener(
+ const OUString& _propertyName,
+ const uno::Reference< beans::XPropertyChangeListener > & _listener )
+{
+ if ( !m_xShapeAgg.is() )
+ throw uno::RuntimeException("no shape aggregate", *this );
+
+ // must be handled by the aggregate
+ uno::Reference< beans::XPropertySet > xShapeProps;
+ if ( m_xShapeAgg->queryAggregation( cppu::UnoType<beans::XPropertySet>::get() ) >>= xShapeProps )
+ xShapeProps->addPropertyChangeListener( _propertyName, _listener );
+}
+
+void SwXShape::removePropertyChangeListener(
+ const OUString& _propertyName,
+ const uno::Reference< beans::XPropertyChangeListener > & _listener)
+{
+ if ( !m_xShapeAgg.is() )
+ throw uno::RuntimeException("no shape aggregate", *this );
+
+ // must be handled by the aggregate
+ uno::Reference< beans::XPropertySet > xShapeProps;
+ if ( m_xShapeAgg->queryAggregation( cppu::UnoType<beans::XPropertySet>::get() ) >>= xShapeProps )
+ xShapeProps->removePropertyChangeListener( _propertyName, _listener );
+}
+
+void SwXShape::addVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/ )
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXShape::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXShape::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pFormat = nullptr;
+ EndListeningAll();
+ }
+}
+
+void SwXShape::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard aGuard;
+
+ // get access to SwDoc
+ // (see also SwXTextRange::XTextRangeToSwPaM)
+ const SwDoc* pDoc = nullptr;
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ if(xRangeTunnel.is())
+ {
+ if (auto pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel))
+ pDoc = &pRange->GetDoc();
+ else if (auto pText = comphelper::getFromUnoTunnel<SwXText>(xRangeTunnel))
+ pDoc = pText->GetDoc();
+ else if (auto pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel))
+ pDoc = pCursor->GetDoc();
+ else if (auto pPortion = comphelper::getFromUnoTunnel<SwXTextPortion>(xRangeTunnel))
+ pDoc = &pPortion->GetCursor().GetDoc();
+ else if (auto pParagraph = comphelper::getFromUnoTunnel<SwXParagraph>(xRangeTunnel);
+ pParagraph && pParagraph->GetTextNode())
+ pDoc = &pParagraph->GetTextNode()->GetDoc();
+
+ }
+
+ if(!pDoc)
+ throw uno::RuntimeException();
+ const SwDocShell* pDocSh = pDoc->GetDocShell();
+ if (!pDocSh)
+ return;
+
+ uno::Reference<frame::XModel> xModel = pDocSh->GetModel();
+ uno::Reference< drawing::XDrawPageSupplier > xDPS(xModel, uno::UNO_QUERY);
+ if (xDPS.is())
+ {
+ uno::Reference< drawing::XDrawPage > xDP( xDPS->getDrawPage() );
+ if (xDP.is())
+ {
+ uno::Any aPos;
+ aPos <<= xTextRange;
+ setPropertyValue("TextRange", aPos);
+ uno::Reference< drawing::XShape > xTemp( static_cast<cppu::OWeakObject*>(this), uno::UNO_QUERY );
+ xDP->add( xTemp );
+ }
+ }
+}
+
+uno::Reference< text::XTextRange > SwXShape::getAnchor()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< text::XTextRange > aRef;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ // return an anchor for non-page bound frames
+ // and for page bound frames that have a page no == NULL and a content position
+ if ((rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE) ||
+ (rAnchor.GetContentAnchor() && !rAnchor.GetPageNum()))
+ {
+ const SwPosition &rPos = *(pFormat->GetAnchor().GetContentAnchor());
+ if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ { // ensure that SwXTextRange has SwIndex
+ aRef = SwXTextRange::CreateXTextRange(*pFormat->GetDoc(), SwPosition(rPos.nNode), nullptr);
+ }
+ else
+ {
+ aRef = SwXTextRange::CreateXTextRange(*pFormat->GetDoc(), rPos, nullptr);
+ }
+ }
+ }
+ else
+ aRef = m_pImpl->GetTextRange();
+ return aRef;
+}
+
+void SwXShape::dispose()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ // determine correct <SdrObject>
+ SvxShape* pSvxShape = GetSvxShape();
+ SdrObject* pObj = pSvxShape ? pSvxShape->GetSdrObject() : nullptr;
+ // safety assertion:
+ // <pObj> must be the same as <pFormat->FindSdrObject()>, if <pObj> isn't
+ // a 'virtual' drawing object.
+ // correct assertion and refine it for safety reason.
+ OSL_ENSURE( !pObj ||
+ dynamic_cast<const SwDrawVirtObj*>( pObj) != nullptr ||
+ pObj->getParentSdrObjectFromSdrObject() ||
+ pObj == pFormat->FindSdrObject(),
+ "<SwXShape::dispose(..) - different 'master' drawing objects!!" );
+ // perform delete of draw frame format *not*
+ // for 'virtual' drawing objects.
+ // no delete of draw format for members
+ // of a group
+ if ( pObj &&
+ dynamic_cast<const SwDrawVirtObj*>( pObj) == nullptr &&
+ !pObj->getParentSdrObjectFromSdrObject() &&
+ pObj->IsInserted() )
+ {
+ if (pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ const SwPosition &rPos = *(pFormat->GetAnchor().GetContentAnchor());
+ SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
+ const sal_Int32 nIdx = rPos.nContent.GetIndex();
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx );
+ }
+ else
+ pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat( pFormat );
+ }
+ }
+ if(m_xShapeAgg.is())
+ {
+ uno::Any aAgg(m_xShapeAgg->queryAggregation( cppu::UnoType<XComponent>::get()));
+ uno::Reference<XComponent> xComp;
+ aAgg >>= xComp;
+ if(xComp.is())
+ xComp->dispose();
+ }
+ if(m_pPage)
+ const_cast<SwFmDrawPage*>(m_pPage)->RemoveShape(this);
+ m_pPage = nullptr;
+}
+
+void SwXShape::addEventListener(
+ const uno::Reference< lang::XEventListener > & aListener)
+{
+ SvxShape* pSvxShape = GetSvxShape();
+ if(pSvxShape)
+ pSvxShape->addEventListener(aListener);
+}
+
+void SwXShape::removeEventListener(
+ const uno::Reference< lang::XEventListener > & aListener)
+{
+ SvxShape* pSvxShape = GetSvxShape();
+ if(pSvxShape)
+ pSvxShape->removeEventListener(aListener);
+}
+
+OUString SwXShape::getImplementationName()
+{
+ return "SwXShape";
+}
+
+sal_Bool SwXShape::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXShape::getSupportedServiceNames()
+{
+ uno::Sequence< OUString > aSeq;
+ if (SvxShape* pSvxShape = GetSvxShape())
+ aSeq = pSvxShape->getSupportedServiceNames();
+ return comphelper::concatSequences(
+ aSeq, std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape" });
+}
+
+SvxShape* SwXShape::GetSvxShape()
+{
+ if(m_xShapeAgg.is())
+ return comphelper::getFromUnoTunnel<SvxShape>(m_xShapeAgg);
+ return nullptr;
+}
+
+// #i31698#
+// implementation of virtual methods from drawing::XShape
+awt::Point SAL_CALL SwXShape::getPosition()
+{
+ awt::Point aPos( GetAttrPosition() );
+
+ // handle group members
+ SvxShape* pSvxShape = GetSvxShape();
+ if ( pSvxShape )
+ {
+ SdrObject* pTopGroupObj = GetTopGroupObj( pSvxShape );
+ if ( pTopGroupObj )
+ {
+ // #i34750# - get attribute position of top group
+ // shape and add offset between top group object and group member
+ uno::Reference< drawing::XShape > xGroupShape( pTopGroupObj->getUnoShape(), uno::UNO_QUERY );
+ aPos = xGroupShape->getPosition();
+ // add offset between top group object and group member
+ // to the determined attribute position
+ // #i34750#:
+ // consider the layout direction
+ const tools::Rectangle aMemberObjRect = GetSvxShape()->GetSdrObject()->GetSnapRect();
+ const tools::Rectangle aGroupObjRect = pTopGroupObj->GetSnapRect();
+ // #i53320# - relative position of group member and
+ // top group object is always given in horizontal left-to-right layout.
+ awt::Point aOffset( 0, 0 );
+ {
+ aOffset.X = ( aMemberObjRect.Left() - aGroupObjRect.Left() );
+ aOffset.Y = ( aMemberObjRect.Top() - aGroupObjRect.Top() );
+ }
+ aOffset.X = convertTwipToMm100(aOffset.X);
+ aOffset.Y = convertTwipToMm100(aOffset.Y);
+ aPos.X += aOffset.X;
+ aPos.Y += aOffset.Y;
+ }
+ }
+
+ return aPos;
+}
+
+void SAL_CALL SwXShape::setPosition( const awt::Point& aPosition )
+{
+ SdrObject* pTopGroupObj = GetTopGroupObj();
+ if ( !pTopGroupObj )
+ {
+ // #i37877# - no adjustment of position attributes,
+ // if the position also has to be applied at the drawing object and
+ // a contact object is already registered at the drawing object.
+ bool bApplyPosAtDrawObj(false);
+ bool bNoAdjustOfPosProp(false);
+ // #i35798# - apply position also to drawing object,
+ // if drawing object has no anchor position set.
+ if ( mxShape.is() )
+ {
+ SvxShape* pSvxShape = GetSvxShape();
+ if ( pSvxShape )
+ {
+ const SdrObject* pObj = pSvxShape->GetSdrObject();
+ if ( pObj &&
+ pObj->GetAnchorPos().X() == 0 &&
+ pObj->GetAnchorPos().Y() == 0 )
+ {
+ bApplyPosAtDrawObj = true;
+ if ( pObj->GetUserCall() &&
+ dynamic_cast<const SwDrawContact*>( pObj->GetUserCall()) != nullptr )
+ {
+ bNoAdjustOfPosProp = true;
+ }
+ }
+ }
+ }
+ // shape isn't a group member. Thus, set positioning attributes
+ if ( !bNoAdjustOfPosProp )
+ {
+ AdjustPositionProperties( aPosition );
+ }
+ if ( bApplyPosAtDrawObj )
+ {
+ mxShape->setPosition( aPosition );
+ }
+ }
+ else if ( mxShape.is() )
+ {
+ // shape is a member of a group. Thus, set its position.
+ awt::Point aNewPos( aPosition );
+ // The given position is given in the according layout direction. Thus,
+ // it has to be converted to a position in horizontal left-to-right
+ // layout.
+ // convert given absolute attribute position in layout direction into
+ // position in horizontal left-to-right layout.
+ {
+ aNewPos = ConvertPositionToHoriL2R( aNewPos, getSize() );
+ }
+ // Convert given absolute position in horizontal left-to-right
+ // layout into relative position in horizontal left-to-right layout.
+ uno::Reference< drawing::XShape > xGroupShape( pTopGroupObj->getUnoShape(), uno::UNO_QUERY );
+ {
+ // #i34750#
+ // use method <xGroupShape->getPosition()> to get the correct
+ // position of the top group object.
+ awt::Point aAttrPosInHoriL2R(
+ ConvertPositionToHoriL2R( xGroupShape->getPosition(),
+ xGroupShape->getSize() ) );
+ aNewPos.X = o3tl::saturating_sub(aNewPos.X, aAttrPosInHoriL2R.X);
+ aNewPos.Y = o3tl::saturating_sub(aNewPos.Y, aAttrPosInHoriL2R.Y);
+ }
+ // convert relative position in horizontal left-to-right layout into
+ // absolute position in horizontal left-to-right layout
+ {
+ // #i34750#
+ // use method <SvxShape->getPosition()> to get the correct
+ // 'Drawing layer' position of the top group shape.
+ auto pSvxGroupShape = comphelper::getFromUnoTunnel<SvxShape>(pTopGroupObj->getUnoShape());
+ const awt::Point aGroupPos = pSvxGroupShape->getPosition();
+ aNewPos.X = o3tl::saturating_add(aNewPos.X, aGroupPos.X);
+ aNewPos.Y = o3tl::saturating_add(aNewPos.Y, aGroupPos.Y);
+ }
+ // set position
+ mxShape->setPosition( aNewPos );
+ }
+}
+
+awt::Size SAL_CALL SwXShape::getSize()
+{
+ awt::Size aSize;
+ if ( mxShape.is() )
+ {
+ aSize = mxShape->getSize();
+ }
+ return aSize;
+}
+
+void SAL_CALL SwXShape::setSize( const awt::Size& aSize )
+{
+ comphelper::ProfileZone aZone("SwXShape::setSize");
+
+ if ( mxShape.is() )
+ {
+ mxShape->setSize( aSize );
+ }
+ SwTextBoxHelper::syncProperty(GetFrameFormat(), RES_FRM_SIZE, MID_FRMSIZE_SIZE, uno::Any(aSize));
+}
+// #i31698#
+// implementation of virtual methods from drawing::XShapeDescriptor
+OUString SAL_CALL SwXShape::getShapeType()
+{
+ if ( mxShape.is() )
+ {
+ return mxShape->getShapeType();
+ }
+ return OUString();
+}
+/** method to determine top group object
+ #i31698#
+*/
+SdrObject* SwXShape::GetTopGroupObj( SvxShape* _pSvxShape )
+{
+ SdrObject* pTopGroupObj( nullptr );
+
+ SvxShape* pSvxShape = _pSvxShape ? _pSvxShape : GetSvxShape();
+ if ( pSvxShape )
+ {
+ SdrObject* pSdrObj = pSvxShape->GetSdrObject();
+ if ( pSdrObj && pSdrObj->getParentSdrObjectFromSdrObject() )
+ {
+ pTopGroupObj = pSdrObj->getParentSdrObjectFromSdrObject();
+ while ( pTopGroupObj->getParentSdrObjectFromSdrObject() )
+ {
+ pTopGroupObj = pTopGroupObj->getParentSdrObjectFromSdrObject();
+ }
+ }
+ }
+
+ return pTopGroupObj;
+}
+
+/** method to determine position according to the positioning attributes
+ #i31698#
+*/
+awt::Point SwXShape::GetAttrPosition()
+{
+ awt::Point aAttrPos;
+
+ uno::Any aHoriPos( getPropertyValue("HoriOrientPosition") );
+ aHoriPos >>= aAttrPos.X;
+ uno::Any aVertPos( getPropertyValue("VertOrientPosition") );
+ aVertPos >>= aAttrPos.Y;
+ // #i35798# - fallback, if attribute position is (0,0)
+ // and no anchor position is applied to the drawing object
+ SvxShape* pSvxShape = GetSvxShape();
+ if ( pSvxShape )
+ {
+ const SdrObject* pObj = pSvxShape->GetSdrObject();
+ if ( pObj &&
+ pObj->GetAnchorPos().X() == 0 &&
+ pObj->GetAnchorPos().Y() == 0 &&
+ aAttrPos.X == 0 && aAttrPos.Y == 0 )
+ {
+ const tools::Rectangle aObjRect = pObj->GetSnapRect();
+ aAttrPos.X = convertTwipToMm100(aObjRect.Left());
+ aAttrPos.Y = convertTwipToMm100(aObjRect.Top());
+ }
+ }
+ // #i35007# - If drawing object is anchored as-character,
+ // it's x-position isn't sensible. Thus, return the x-position as zero in this case.
+ text::TextContentAnchorType eTextAnchorType =
+ text::TextContentAnchorType_AT_PARAGRAPH;
+ {
+ uno::Any aAny = getPropertyValue( "AnchorType" );
+ aAny >>= eTextAnchorType;
+ }
+ if ( eTextAnchorType == text::TextContentAnchorType_AS_CHARACTER )
+ {
+ aAttrPos.X = 0;
+ }
+
+ return aAttrPos;
+}
+
+/** method to convert the position (translation) of the drawing object to
+ the layout direction horizontal left-to-right.
+ #i31698#
+*/
+awt::Point SwXShape::ConvertPositionToHoriL2R( const awt::Point& rObjPos,
+ const awt::Size& rObjSize )
+{
+ awt::Point aObjPosInHoriL2R( rObjPos );
+
+ SwFrameFormat* pFrameFormat = GetFrameFormat();
+ if ( pFrameFormat )
+ {
+ SwFrameFormat::tLayoutDir eLayoutDir = pFrameFormat->GetLayoutDir();
+ switch ( eLayoutDir )
+ {
+ case SwFrameFormat::HORI_L2R:
+ {
+ // nothing to do
+ }
+ break;
+ case SwFrameFormat::HORI_R2L:
+ {
+ aObjPosInHoriL2R.X = -rObjPos.X - rObjSize.Width;
+ }
+ break;
+ case SwFrameFormat::VERT_R2L:
+ {
+ aObjPosInHoriL2R.X = -rObjPos.Y - rObjSize.Width;
+ aObjPosInHoriL2R.Y = rObjPos.X;
+ }
+ break;
+ default:
+ {
+ OSL_FAIL( "<SwXShape::ConvertPositionToHoriL2R(..)> - unsupported layout direction" );
+ }
+ }
+ }
+
+ return aObjPosInHoriL2R;
+}
+
+/** method to convert the transformation of the drawing object to the layout
+ direction, the drawing object is in
+ #i31698#
+*/
+drawing::HomogenMatrix3 SwXShape::ConvertTransformationToLayoutDir(
+ const drawing::HomogenMatrix3& rMatrixInHoriL2R )
+{
+ drawing::HomogenMatrix3 aMatrix(rMatrixInHoriL2R);
+
+ // #i44334#, #i44681# - direct manipulation of the
+ // transformation structure isn't valid, if it contains rotation.
+ SvxShape* pSvxShape = GetSvxShape();
+ OSL_ENSURE( pSvxShape,
+ "<SwXShape::ConvertTransformationToLayoutDir(..)> - no SvxShape found!");
+ if ( pSvxShape )
+ {
+ const SdrObject* pObj = pSvxShape->GetSdrObject();
+ OSL_ENSURE( pObj,
+ "<SwXShape::ConvertTransformationToLayoutDir(..)> - no SdrObject found!");
+ if ( pObj )
+ {
+ // get position of object in Writer coordinate system.
+ awt::Point aPos( getPosition() );
+ // get position of object in Drawing layer coordinate system
+ const Point aTmpObjPos( pObj->GetSnapRect().TopLeft() );
+ const awt::Point aObjPos(
+ convertTwipToMm100( aTmpObjPos.X() - pObj->GetAnchorPos().X() ),
+ convertTwipToMm100( aTmpObjPos.Y() - pObj->GetAnchorPos().Y() ) );
+ // determine difference between these positions according to the
+ // Writer coordinate system
+ const awt::Point aTranslateDiff( aPos.X - aObjPos.X,
+ aPos.Y - aObjPos.Y );
+ // apply translation difference to transformation matrix.
+ if ( aTranslateDiff.X != 0 || aTranslateDiff.Y != 0 )
+ {
+ // #i73079# - use correct matrix type
+ ::basegfx::B2DHomMatrix aTempMatrix;
+
+ aTempMatrix.set(0, 0, aMatrix.Line1.Column1 );
+ aTempMatrix.set(0, 1, aMatrix.Line1.Column2 );
+ aTempMatrix.set(0, 2, aMatrix.Line1.Column3 );
+ aTempMatrix.set(1, 0, aMatrix.Line2.Column1 );
+ aTempMatrix.set(1, 1, aMatrix.Line2.Column2 );
+ aTempMatrix.set(1, 2, aMatrix.Line2.Column3 );
+ aTempMatrix.set(2, 0, aMatrix.Line3.Column1 );
+ aTempMatrix.set(2, 1, aMatrix.Line3.Column2 );
+ aTempMatrix.set(2, 2, aMatrix.Line3.Column3 );
+ // #i73079#
+ aTempMatrix.translate( aTranslateDiff.X, aTranslateDiff.Y );
+ aMatrix.Line1.Column1 = aTempMatrix.get(0, 0);
+ aMatrix.Line1.Column2 = aTempMatrix.get(0, 1);
+ aMatrix.Line1.Column3 = aTempMatrix.get(0, 2);
+ aMatrix.Line2.Column1 = aTempMatrix.get(1, 0);
+ aMatrix.Line2.Column2 = aTempMatrix.get(1, 1);
+ aMatrix.Line2.Column3 = aTempMatrix.get(1, 2);
+ aMatrix.Line3.Column1 = aTempMatrix.get(2, 0);
+ aMatrix.Line3.Column2 = aTempMatrix.get(2, 1);
+ aMatrix.Line3.Column3 = aTempMatrix.get(2, 2);
+ }
+ }
+ }
+
+ return aMatrix;
+}
+
+/** method to adjust the positioning properties
+ #i31698#
+*/
+void SwXShape::AdjustPositionProperties( const awt::Point& rPosition )
+{
+ // handle x-position
+ // #i35007# - no handling of x-position, if drawing
+ // object is anchored as-character, because it doesn't make sense.
+ text::TextContentAnchorType eTextAnchorType =
+ text::TextContentAnchorType_AT_PARAGRAPH;
+ {
+ uno::Any aAny = getPropertyValue( "AnchorType" );
+ aAny >>= eTextAnchorType;
+ }
+ if ( eTextAnchorType != text::TextContentAnchorType_AS_CHARACTER )
+ {
+ // determine current x-position
+ static const OUStringLiteral aHoriPosPropStr(u"HoriOrientPosition");
+ uno::Any aHoriPos( getPropertyValue( aHoriPosPropStr ) );
+ sal_Int32 dCurrX = 0;
+ aHoriPos >>= dCurrX;
+ // change x-position attribute, if needed
+ if ( dCurrX != rPosition.X )
+ {
+ // adjust x-position orientation to text::HoriOrientation::NONE, if needed
+ // Note: has to be done before setting x-position attribute
+ static const OUStringLiteral aHoriOrientPropStr(u"HoriOrient");
+ uno::Any aHoriOrient( getPropertyValue( aHoriOrientPropStr ) );
+ sal_Int16 eHoriOrient;
+ if (aHoriOrient >>= eHoriOrient) // may be void
+ {
+ if ( eHoriOrient != text::HoriOrientation::NONE )
+ {
+ eHoriOrient = text::HoriOrientation::NONE;
+ aHoriOrient <<= eHoriOrient;
+ setPropertyValue( aHoriOrientPropStr, aHoriOrient );
+ }
+ }
+ // set x-position attribute
+ aHoriPos <<= rPosition.X;
+ setPropertyValue( aHoriPosPropStr, aHoriPos );
+ }
+ }
+
+ // handle y-position
+ {
+ // determine current y-position
+ static const OUStringLiteral aVertPosPropStr(u"VertOrientPosition");
+ uno::Any aVertPos( getPropertyValue( aVertPosPropStr ) );
+ sal_Int32 dCurrY = 0;
+ aVertPos >>= dCurrY;
+ // change y-position attribute, if needed
+ if ( dCurrY != rPosition.Y )
+ {
+ // adjust y-position orientation to text::VertOrientation::NONE, if needed
+ // Note: has to be done before setting y-position attribute
+ static const OUStringLiteral aVertOrientPropStr(u"VertOrient");
+ uno::Any aVertOrient( getPropertyValue( aVertOrientPropStr ) );
+ sal_Int16 eVertOrient;
+ if (aVertOrient >>= eVertOrient) // may be void
+ {
+ if ( eVertOrient != text::VertOrientation::NONE )
+ {
+ eVertOrient = text::VertOrientation::NONE;
+ aVertOrient <<= eVertOrient;
+ setPropertyValue( aVertOrientPropStr, aVertOrient );
+ }
+ }
+ // set y-position attribute
+ aVertPos <<= rPosition.Y;
+ setPropertyValue( aVertPosPropStr, aVertPos );
+ }
+ }
+}
+
+/** method to convert start or end position of the drawing object to the
+ Writer specific position, which is the attribute position in layout direction
+ #i59051#
+*/
+css::awt::Point SwXShape::ConvertStartOrEndPosToLayoutDir(
+ const css::awt::Point& aStartOrEndPos )
+{
+ awt::Point aConvertedPos( aStartOrEndPos );
+
+ SvxShape* pSvxShape = GetSvxShape();
+ OSL_ENSURE( pSvxShape,
+ "<SwXShape::ConvertStartOrEndPosToLayoutDir(..)> - no SvxShape found!");
+ if ( pSvxShape )
+ {
+ const SdrObject* pObj = pSvxShape->GetSdrObject();
+ OSL_ENSURE( pObj,
+ "<SwXShape::ConvertStartOrEndPosToLayoutDir(..)> - no SdrObject found!");
+ if ( pObj )
+ {
+ // get position of object in Writer coordinate system.
+ awt::Point aPos( getPosition() );
+ // get position of object in Drawing layer coordinate system
+ const Point aTmpObjPos( pObj->GetSnapRect().TopLeft() );
+ const awt::Point aObjPos(
+ convertTwipToMm100( aTmpObjPos.X() - pObj->GetAnchorPos().X() ),
+ convertTwipToMm100( aTmpObjPos.Y() - pObj->GetAnchorPos().Y() ) );
+ // determine difference between these positions according to the
+ // Writer coordinate system
+ const awt::Point aTranslateDiff( aPos.X - aObjPos.X,
+ aPos.Y - aObjPos.Y );
+ // apply translation difference to transformation matrix.
+ if ( aTranslateDiff.X != 0 || aTranslateDiff.Y != 0 )
+ {
+ aConvertedPos.X = aConvertedPos.X + aTranslateDiff.X;
+ aConvertedPos.Y = aConvertedPos.Y + aTranslateDiff.Y;
+ }
+ }
+ }
+
+ return aConvertedPos;
+}
+
+css::drawing::PolyPolygonBezierCoords SwXShape::ConvertPolyPolygonBezierToLayoutDir(
+ const css::drawing::PolyPolygonBezierCoords& aPath )
+{
+ drawing::PolyPolygonBezierCoords aConvertedPath( aPath );
+
+ SvxShape* pSvxShape = GetSvxShape();
+ OSL_ENSURE( pSvxShape,
+ "<SwXShape::ConvertStartOrEndPosToLayoutDir(..)> - no SvxShape found!");
+ if ( pSvxShape )
+ {
+ const SdrObject* pObj = pSvxShape->GetSdrObject();
+ OSL_ENSURE( pObj,
+ "<SwXShape::ConvertStartOrEndPosToLayoutDir(..)> - no SdrObject found!");
+ if ( pObj )
+ {
+ // get position of object in Writer coordinate system.
+ awt::Point aPos( getPosition() );
+ // get position of object in Drawing layer coordinate system
+ const Point aTmpObjPos( pObj->GetSnapRect().TopLeft() );
+ const awt::Point aObjPos(
+ convertTwipToMm100( aTmpObjPos.X() - pObj->GetAnchorPos().X() ),
+ convertTwipToMm100( aTmpObjPos.Y() - pObj->GetAnchorPos().Y() ) );
+ // determine difference between these positions according to the
+ // Writer coordinate system
+ const awt::Point aTranslateDiff( aPos.X - aObjPos.X,
+ aPos.Y - aObjPos.Y );
+ // apply translation difference to PolyPolygonBezier.
+ if ( aTranslateDiff.X != 0 || aTranslateDiff.Y != 0 )
+ {
+ const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
+ aTranslateDiff.X, aTranslateDiff.Y));
+
+ for(drawing::PointSequence& rInnerSequence : asNonConstRange(aConvertedPath.Coordinates))
+ {
+ for(awt::Point& rPoint : asNonConstRange(rInnerSequence))
+ {
+ basegfx::B2DPoint aNewCoordinatePair(rPoint.X, rPoint.Y);
+ aNewCoordinatePair *= aMatrix;
+ rPoint.X = basegfx::fround(aNewCoordinatePair.getX());
+ rPoint.Y = basegfx::fround(aNewCoordinatePair.getY());
+ }
+ }
+ }
+ }
+ }
+
+ return aConvertedPath;
+}
+
+SwXGroupShape::SwXGroupShape(uno::Reference<XInterface> & xShape,
+ SwDoc const*const pDoc)
+ : SwXShape(xShape, pDoc)
+{
+#if OSL_DEBUG_LEVEL > 0
+ uno::Reference<XShapes> xShapes(m_xShapeAgg, uno::UNO_QUERY);
+ OSL_ENSURE(xShapes.is(), "no SvxShape found or shape is not a group shape");
+#endif
+}
+
+SwXGroupShape::~SwXGroupShape()
+{
+}
+
+uno::Any SwXGroupShape::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet;
+ if(rType == cppu::UnoType<XShapes>::get())
+ aRet <<= uno::Reference<XShapes>(this);
+ else
+ aRet = SwXShape::queryInterface(rType);
+ return aRet;
+}
+
+void SwXGroupShape::acquire( ) noexcept
+{
+ SwXShape::acquire();
+}
+
+void SwXGroupShape::release( ) noexcept
+{
+ SwXShape::release();
+}
+
+void SwXGroupShape::add( const uno::Reference< XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+ SvxShape* pSvxShape = GetSvxShape();
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!(pSvxShape && pFormat))
+ throw uno::RuntimeException();
+
+ uno::Reference<XShapes> xShapes;
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rType = cppu::UnoType<XShapes>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rType );
+ aAgg >>= xShapes;
+ }
+ if(!xShapes.is())
+ throw uno::RuntimeException();
+
+ xShapes->add(xShape);
+
+
+ uno::Reference<lang::XUnoTunnel> xTunnel(xShape, uno::UNO_QUERY);
+ SwXShape* pSwShape = comphelper::getFromUnoTunnel<SwXShape>(xTunnel);
+ if(!(pSwShape && pSwShape->m_bDescriptor))
+ return;
+
+ SvxShape* pAddShape = comphelper::getFromUnoTunnel<SvxShape>(xTunnel);
+ if(pAddShape)
+ {
+ SdrObject* pObj = pAddShape->GetSdrObject();
+ if(pObj)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ // set layer of new drawing
+ // object to corresponding invisible layer.
+ if( SdrInventor::FmForm != pObj->GetObjInventor())
+ {
+ pObj->SetLayer( pSwShape->m_pImpl->GetOpaque()
+ ? pDoc->getIDocumentDrawModelAccess().GetInvisibleHeavenId()
+ : pDoc->getIDocumentDrawModelAccess().GetInvisibleHellId() );
+ }
+ else
+ {
+ pObj->SetLayer(pDoc->getIDocumentDrawModelAccess().GetInvisibleControlsId());
+ }
+ }
+ }
+ pSwShape->m_bDescriptor = false;
+ //add the group member to the format of the group
+ SwFrameFormat* pShapeFormat = ::FindFrameFormat( pSvxShape->GetSdrObject() );
+
+ if(pShapeFormat)
+ pSwShape->SetFrameFormat(pShapeFormat);
+
+}
+
+void SwXGroupShape::remove( const uno::Reference< XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<XShapes> xShapes;
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rType = cppu::UnoType<XShapes>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rType );
+ aAgg >>= xShapes;
+ }
+ if(!xShapes.is())
+ throw uno::RuntimeException();
+ xShapes->remove(xShape);
+}
+
+sal_Int32 SwXGroupShape::getCount()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<XIndexAccess> xAcc;
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rType = cppu::UnoType<XIndexAccess>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rType );
+ aAgg >>= xAcc;
+ }
+ if(!xAcc.is())
+ throw uno::RuntimeException();
+ return xAcc->getCount();
+}
+
+uno::Any SwXGroupShape::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<XIndexAccess> xAcc;
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rType = cppu::UnoType<XIndexAccess>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rType );
+ aAgg >>= xAcc;
+ }
+ if(!xAcc.is())
+ throw uno::RuntimeException();
+ return xAcc->getByIndex(nIndex);
+}
+
+uno::Type SwXGroupShape::getElementType( )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<XIndexAccess> xAcc;
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rType = cppu::UnoType<XIndexAccess>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rType );
+ aAgg >>= xAcc;
+ }
+ if(!xAcc.is())
+ throw uno::RuntimeException();
+ return xAcc->getElementType();
+}
+
+sal_Bool SwXGroupShape::hasElements( )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<XIndexAccess> xAcc;
+ if( m_xShapeAgg.is() )
+ {
+ const uno::Type& rType = cppu::UnoType<XIndexAccess>::get();
+ uno::Any aAgg = m_xShapeAgg->queryAggregation( rType );
+ aAgg >>= xAcc;
+ }
+ if(!xAcc.is())
+ throw uno::RuntimeException();
+ return xAcc->hasElements();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoevent.cxx b/sw/source/core/unocore/unoevent.cxx
new file mode 100644
index 000000000..09b1a089f
--- /dev/null
+++ b/sw/source/core/unocore/unoevent.cxx
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+// HINTIDs must be on top; it is required for the macitem.hxx header
+#include <hintids.hxx>
+#include <unoevent.hxx>
+#include <unoframe.hxx>
+#include <unostyle.hxx>
+#include <fmtinfmt.hxx>
+#include <svl/macitem.hxx>
+#include <sfx2/event.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+using ::com::sun::star::container::XNameReplace;
+
+// tables of allowed events for specific objects
+
+const struct SvEventDescription aGraphicEvents[] =
+{
+ { SvMacroItemId::SwObjectSelect, "OnSelect" },
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnClick, "OnClick" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::OnImageLoadDone, "OnLoadDone" },
+ { SvMacroItemId::OnImageLoadCancel, "OnLoadCancel" },
+ { SvMacroItemId::OnImageLoadError, "OnLoadError" },
+ { SvMacroItemId::NONE, nullptr }
+};
+
+const struct SvEventDescription aFrameEvents[] =
+{
+ { SvMacroItemId::SwObjectSelect, "OnSelect" },
+ { SvMacroItemId::SwFrmKeyInputAlpha, "OnAlphaCharInput" },
+ { SvMacroItemId::SwFrmKeyInputNoAlpha, "OnNonAlphaCharInput" },
+ { SvMacroItemId::SwFrmResize, "OnResize" },
+ { SvMacroItemId::SwFrmMove, "OnMove" },
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnClick, "OnClick" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::NONE, nullptr }
+};
+
+const struct SvEventDescription aOLEEvents[] =
+{
+ { SvMacroItemId::SwObjectSelect, "OnSelect" },
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnClick, "OnClick" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::NONE, nullptr }
+};
+
+const struct SvEventDescription aHyperlinkEvents[] =
+{
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnClick, "OnClick" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::NONE, nullptr }
+};
+
+const struct SvEventDescription aFrameStyleEvents[] =
+{
+ { SvMacroItemId::SwObjectSelect, "OnSelect" },
+ { SvMacroItemId::SwFrmKeyInputAlpha, "OnAlphaCharInput" },
+ { SvMacroItemId::SwFrmKeyInputNoAlpha, "OnNonAlphaCharInput" },
+ { SvMacroItemId::SwFrmResize, "OnResize" },
+ { SvMacroItemId::SwFrmMove, "OnMove" },
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnClick, "OnClick" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::OnImageLoadDone, "OnLoadDone" },
+ { SvMacroItemId::OnImageLoadCancel, "OnLoadCancel" },
+ { SvMacroItemId::OnImageLoadError, "OnLoadError" },
+ { SvMacroItemId::NONE, nullptr }
+};
+
+SwHyperlinkEventDescriptor::SwHyperlinkEventDescriptor() :
+ SvDetachedEventDescriptor(aHyperlinkEvents)
+{
+}
+
+SwHyperlinkEventDescriptor::~SwHyperlinkEventDescriptor()
+{
+}
+
+OUString SwHyperlinkEventDescriptor::getImplementationName()
+{
+ return "SwHyperlinkEventDescriptor";
+}
+
+void SwHyperlinkEventDescriptor::copyMacrosFromINetFormat(
+ const SwFormatINetFormat& aFormat)
+{
+ for(sal_uInt16 i = 0; mpSupportedMacroItems[i].mnEvent != SvMacroItemId::NONE; ++i)
+ {
+ const SvMacroItemId nEvent = mpSupportedMacroItems[i].mnEvent;
+ const SvxMacro* aMacro = aFormat.GetMacro(nEvent);
+ if (nullptr != aMacro)
+ replaceByName(nEvent, *aMacro);
+ }
+}
+
+void SwHyperlinkEventDescriptor::copyMacrosIntoINetFormat(
+ SwFormatINetFormat& aFormat)
+{
+ for(sal_uInt16 i = 0; mpSupportedMacroItems[i].mnEvent != SvMacroItemId::NONE; ++i)
+ {
+ const SvMacroItemId nEvent = mpSupportedMacroItems[i].mnEvent;
+ if (hasById(nEvent))
+ {
+ SvxMacro aMacro("", "");
+ getByName(aMacro, nEvent);
+ aFormat.SetMacro(nEvent, aMacro);
+ }
+ }
+}
+
+void SwHyperlinkEventDescriptor::copyMacrosFromNameReplace(
+ uno::Reference<
+ container::XNameReplace> const & xReplace)
+{
+ // iterate over all names (all names that *we* support)
+ const Sequence<OUString> aNames = getElementNames();
+ for(const OUString& rName : aNames)
+ {
+ // copy element for that name
+ if (xReplace->hasByName(rName))
+ {
+ SvBaseEventDescriptor::replaceByName(rName,
+ xReplace->getByName(rName));
+ }
+ }
+}
+
+// use double cast in superclass constructor to avoid ambiguous cast
+SwFrameEventDescriptor::SwFrameEventDescriptor(
+ SwXTextFrame& rFrameRef ) :
+ SvEventDescriptor(static_cast<text::XTextFrame&>(rFrameRef), aFrameEvents),
+ m_rFrame(rFrameRef)
+{
+}
+
+SwFrameEventDescriptor::SwFrameEventDescriptor(
+ SwXTextGraphicObject& rGraphicRef ) :
+ SvEventDescriptor(static_cast<text::XTextContent&>(rGraphicRef), aGraphicEvents),
+ m_rFrame(static_cast<SwXFrame&>(rGraphicRef))
+{
+}
+
+SwFrameEventDescriptor::SwFrameEventDescriptor(
+ SwXTextEmbeddedObject& rObjectRef ) :
+ SvEventDescriptor(static_cast<text::XTextContent&>(rObjectRef), aOLEEvents),
+ m_rFrame(static_cast<SwXFrame&>(rObjectRef))
+{
+}
+
+SwFrameEventDescriptor::~SwFrameEventDescriptor()
+{
+}
+
+void SwFrameEventDescriptor::setMacroItem(const SvxMacroItem& rItem)
+{
+ m_rFrame.GetFrameFormat()->SetFormatAttr(rItem);
+}
+
+const SvxMacroItem& SwFrameEventDescriptor::getMacroItem()
+{
+ return m_rFrame.GetFrameFormat()->GetFormatAttr(RES_FRMMACRO);
+}
+
+sal_uInt16 SwFrameEventDescriptor::getMacroItemWhich() const
+{
+ return RES_FRMMACRO;
+}
+
+OUString SwFrameEventDescriptor::getImplementationName()
+{
+ return "SwFrameEventDescriptor";
+}
+
+SwFrameStyleEventDescriptor::SwFrameStyleEventDescriptor(
+ sw::ICoreFrameStyle& rStyle ) :
+ SvEventDescriptor(rStyle.GetEventsSupplier(),
+ aFrameStyleEvents),
+ m_rStyle(rStyle)
+{
+}
+
+SwFrameStyleEventDescriptor::~SwFrameStyleEventDescriptor()
+{
+}
+
+void SwFrameStyleEventDescriptor::setMacroItem(const SvxMacroItem& rItem)
+{
+ m_rStyle.SetItem(RES_FRMMACRO, rItem);
+}
+
+const SvxMacroItem aEmptyMacroItem(RES_FRMMACRO);
+
+const SvxMacroItem& SwFrameStyleEventDescriptor::getMacroItem()
+{
+ const SfxPoolItem* pItem(m_rStyle.GetItem(RES_FRMMACRO));
+ return pItem ? static_cast<const SvxMacroItem&>(*pItem) : aEmptyMacroItem;
+}
+
+OUString SwFrameStyleEventDescriptor::getImplementationName()
+{
+ return "SwFrameStyleEventDescriptor";
+}
+
+sal_uInt16 SwFrameStyleEventDescriptor::getMacroItemWhich() const
+{
+ return RES_FRMMACRO;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unofield.cxx b/sw/source/core/unocore/unofield.cxx
new file mode 100644
index 000000000..e6320e9d2
--- /dev/null
+++ b/sw/source/core/unocore/unofield.cxx
@@ -0,0 +1,3019 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <algorithm>
+#include <memory>
+
+#include <unofield.hxx>
+#include <unofieldcoll.hxx>
+#include <unobookmark.hxx>
+#include <swtypes.hxx>
+#include <cmdid.h>
+#include <doc.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentStatistics.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <fmtfld.hxx>
+#include <txtfld.hxx>
+#include <ndtxt.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unocoll.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <editsh.hxx>
+#include <viewsh.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+
+//undef to prevent error (from sfx2/docfile.cxx)
+#undef SEQUENCE
+#include <com/sun/star/text/SetVariableType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <authfld.hxx>
+#include <flddat.hxx>
+#include <dbfld.hxx>
+#include <usrfld.hxx>
+#include <docufld.hxx>
+#include <expfld.hxx>
+#include <chpfld.hxx>
+#include <flddropdown.hxx>
+#include <poolfmt.hxx>
+#include <strings.hrc>
+#include <pagedesc.hxx>
+#include <docary.hxx>
+#include <reffld.hxx>
+#include <ddefld.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <swunohelper.hxx>
+#include <unofldmid.h>
+#include <scriptinfo.hxx>
+#include <tools/datetime.hxx>
+#include <tools/urlobj.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/listener.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <mutex>
+#include <vcl/svapp.hxx>
+#include <textapi.hxx>
+#include <fmtmeta.hxx>
+#include <vector>
+
+using namespace ::com::sun::star;
+using namespace nsSwDocInfoSubType;
+
+// case-corrected version of the first part for the service names (see #i67811)
+constexpr OUStringLiteral COM_TEXT_FLDMASTER_CC = u"com.sun.star.text.fieldmaster.";
+
+// note: this thing is indexed as an array, so do not insert/remove entries!
+const sal_uInt16 aDocInfoSubTypeFromService[] =
+{
+ DI_CHANGE | DI_SUB_AUTHOR, //PROPERTY_MAP_FLDTYP_DOCINFO_CHANGE_AUTHOR
+ DI_CHANGE | DI_SUB_DATE, //PROPERTY_MAP_FLDTYP_DOCINFO_CHANGE_DATE_TIME
+ DI_EDIT | DI_SUB_TIME, //PROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME
+ DI_COMMENT, //PROPERTY_MAP_FLDTYP_DOCINFO_DESCRIPTION
+ DI_CREATE | DI_SUB_AUTHOR, //PROPERTY_MAP_FLDTYP_DOCINFO_CREATE_AUTHOR
+ DI_CREATE | DI_SUB_DATE, //PROPERTY_MAP_FLDTYP_DOCINFO_CREATE_DATE_TIME
+ 0, //DUMMY
+ 0, //DUMMY
+ 0, //DUMMY
+ 0, //DUMMY
+ DI_CUSTOM, //PROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM
+ DI_PRINT | DI_SUB_AUTHOR, //PROPERTY_MAP_FLDTYP_DOCINFO_PRINT_AUTHOR
+ DI_PRINT | DI_SUB_DATE, //PROPERTY_MAP_FLDTYP_DOCINFO_PRINT_DATE_TIME
+ DI_KEYS, //PROPERTY_MAP_FLDTYP_DOCINFO_KEY_WORDS
+ DI_SUBJECT, //PROPERTY_MAP_FLDTYP_DOCINFO_SUBJECT
+ DI_TITLE, //PROPERTY_MAP_FLDTYP_DOCINFO_TITLE
+ DI_DOCNO //PROPERTY_MAP_FLDTYP_DOCINFO_REVISION
+};
+
+namespace {
+
+struct ServiceIdResId
+{
+ SwFieldIds nResId;
+ SwServiceType nServiceId;
+};
+
+}
+
+const ServiceIdResId aServiceToRes[] =
+{
+ {SwFieldIds::DateTime, SwServiceType::FieldTypeDateTime },
+ {SwFieldIds::User, SwServiceType::FieldTypeUser },
+ {SwFieldIds::SetExp, SwServiceType::FieldTypeSetExp },
+ {SwFieldIds::GetExp, SwServiceType::FieldTypeGetExp },
+ {SwFieldIds::Filename, SwServiceType::FieldTypeFileName },
+ {SwFieldIds::PageNumber, SwServiceType::FieldTypePageNum },
+ {SwFieldIds::Author, SwServiceType::FieldTypeAuthor },
+ {SwFieldIds::Chapter, SwServiceType::FieldTypeChapter },
+ {SwFieldIds::GetRef, SwServiceType::FieldTypeGetReference },
+ {SwFieldIds::HiddenText, SwServiceType::FieldTypeConditionedText },
+ {SwFieldIds::Postit, SwServiceType::FieldTypeAnnotation },
+ {SwFieldIds::Input, SwServiceType::FieldTypeInput },
+ {SwFieldIds::Macro, SwServiceType::FieldTypeMacro },
+ {SwFieldIds::Dde, SwServiceType::FieldTypeDDE },
+ {SwFieldIds::HiddenPara, SwServiceType::FieldTypeHiddenPara },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfo },
+ {SwFieldIds::TemplateName, SwServiceType::FieldTypeTemplateName },
+ {SwFieldIds::ExtUser, SwServiceType::FieldTypeUserExt },
+ {SwFieldIds::RefPageSet, SwServiceType::FieldTypeRefPageSet },
+ {SwFieldIds::RefPageGet, SwServiceType::FieldTypeRefPageGet },
+ {SwFieldIds::JumpEdit, SwServiceType::FieldTypeJumpEdit },
+ {SwFieldIds::Script, SwServiceType::FieldTypeScript },
+ {SwFieldIds::DbNextSet, SwServiceType::FieldTypeDatabaseNextSet },
+ {SwFieldIds::DbNumSet, SwServiceType::FieldTypeDatabaseNumSet },
+ {SwFieldIds::DbSetNumber, SwServiceType::FieldTypeDatabaseSetNum },
+ {SwFieldIds::Database, SwServiceType::FieldTypeDatabase },
+ {SwFieldIds::DatabaseName, SwServiceType::FieldTypeDatabaseName },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypePageCount },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypeParagraphCount },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypeWordCount },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypeCharacterCount },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypeTableCount },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypeGraphicObjectCount },
+ {SwFieldIds::DocStat, SwServiceType::FieldTypeEmbeddedObjectCount },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoChangeAuthor },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoChangeDateTime },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoEditTime },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoDescription },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoCreateAuthor },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoCreateDateTime },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoCustom },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoPrintAuthor },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoPrintDateTime },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoKeywords },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoSubject },
+ {SwFieldIds::DocInfo, SwServiceType::FieldTypeDocInfoTitle },
+ {SwFieldIds::Input, SwServiceType::FieldTypeInputUser },
+ {SwFieldIds::HiddenText, SwServiceType::FieldTypeHiddenText },
+ {SwFieldIds::TableOfAuthorities, SwServiceType::FieldTypeBibliography },
+ {SwFieldIds::CombinedChars, SwServiceType::FieldTypeCombinedCharacters },
+ {SwFieldIds::Dropdown, SwServiceType::FieldTypeDropdown },
+ {SwFieldIds::Table, SwServiceType::FieldTypeTableFormula }
+};
+
+static SwFieldIds lcl_ServiceIdToResId(SwServiceType nServiceId)
+{
+ for (size_t i=0; i<SAL_N_ELEMENTS(aServiceToRes); ++i)
+ if (aServiceToRes[i].nServiceId == nServiceId)
+ return aServiceToRes[i].nResId;
+#if OSL_DEBUG_LEVEL > 0
+ OSL_FAIL("service id not found");
+#endif
+ return SwFieldIds::Unknown;
+}
+
+static SwServiceType lcl_GetServiceForField( const SwField& rField )
+{
+ const SwFieldIds nWhich = rField.Which();
+ SwServiceType nSrvId = SwServiceType::Invalid;
+ //special handling for some fields
+ switch( nWhich )
+ {
+ case SwFieldIds::Input:
+ if( INP_USR == (rField.GetSubType() & 0x00ff) )
+ nSrvId = SwServiceType::FieldTypeInputUser;
+ break;
+
+ case SwFieldIds::DocInfo:
+ {
+ const sal_uInt16 nSubType = rField.GetSubType();
+ switch( nSubType & 0xff )
+ {
+ case DI_CHANGE:
+ nSrvId = ((nSubType&0x300) == DI_SUB_AUTHOR)
+ ? SwServiceType::FieldTypeDocInfoChangeAuthor
+ : SwServiceType::FieldTypeDocInfoChangeDateTime;
+ break;
+ case DI_CREATE:
+ nSrvId = ((nSubType&0x300) == DI_SUB_AUTHOR)
+ ? SwServiceType::FieldTypeDocInfoCreateAuthor
+ : SwServiceType::FieldTypeDocInfoCreateDateTime;
+ break;
+ case DI_PRINT:
+ nSrvId = ((nSubType&0x300) == DI_SUB_AUTHOR)
+ ? SwServiceType::FieldTypeDocInfoPrintAuthor
+ : SwServiceType::FieldTypeDocInfoPrintDateTime;
+ break;
+ case DI_EDIT: nSrvId = SwServiceType::FieldTypeDocInfoEditTime;break;
+ case DI_COMMENT:nSrvId = SwServiceType::FieldTypeDocInfoDescription;break;
+ case DI_KEYS: nSrvId = SwServiceType::FieldTypeDocInfoKeywords;break;
+ case DI_SUBJECT:nSrvId = SwServiceType::FieldTypeDocInfoSubject; break;
+ case DI_TITLE: nSrvId = SwServiceType::FieldTypeDocInfoTitle; break;
+ case DI_DOCNO: nSrvId = SwServiceType::FieldTypeDocInfoRevision; break;
+ case DI_CUSTOM: nSrvId = SwServiceType::FieldTypeDocInfoCustom; break;
+ }
+ }
+ break;
+
+ case SwFieldIds::HiddenText:
+ nSrvId = SwFieldTypesEnum::ConditionalText == static_cast<SwFieldTypesEnum>(rField.GetSubType())
+ ? SwServiceType::FieldTypeConditionedText
+ : SwServiceType::FieldTypeHiddenText;
+ break;
+
+ case SwFieldIds::DocStat:
+ {
+ switch( rField.GetSubType() )
+ {
+ case DS_PAGE: nSrvId = SwServiceType::FieldTypePageCount; break;
+ case DS_PARA: nSrvId = SwServiceType::FieldTypeParagraphCount; break;
+ case DS_WORD: nSrvId = SwServiceType::FieldTypeWordCount ; break;
+ case DS_CHAR: nSrvId = SwServiceType::FieldTypeCharacterCount; break;
+ case DS_TBL: nSrvId = SwServiceType::FieldTypeTableCount ; break;
+ case DS_GRF: nSrvId = SwServiceType::FieldTypeGraphicObjectCount; break;
+ case DS_OLE: nSrvId = SwServiceType::FieldTypeEmbeddedObjectCount; break;
+ }
+ }
+ break;
+ default: break;
+ }
+ if( SwServiceType::Invalid == nSrvId )
+ {
+ for( const ServiceIdResId* pMap = aServiceToRes;
+ SwFieldIds::Unknown != pMap->nResId; ++pMap )
+ if( nWhich == pMap->nResId )
+ {
+ nSrvId = pMap->nServiceId;
+ break;
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ if( SwServiceType::Invalid == nSrvId )
+ OSL_FAIL("resid not found");
+#endif
+ return nSrvId;
+}
+
+static sal_uInt16 lcl_GetPropMapIdForFieldType( SwFieldIds nWhich )
+{
+ sal_uInt16 nId;
+ switch( nWhich )
+ {
+ case SwFieldIds::User: nId = PROPERTY_MAP_FLDMSTR_USER; break;
+ case SwFieldIds::Database: nId = PROPERTY_MAP_FLDMSTR_DATABASE; break;
+ case SwFieldIds::SetExp: nId = PROPERTY_MAP_FLDMSTR_SET_EXP; break;
+ case SwFieldIds::Dde: nId = PROPERTY_MAP_FLDMSTR_DDE; break;
+ case SwFieldIds::TableOfAuthorities:
+ nId = PROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY; break;
+ default: nId = PROPERTY_MAP_FLDMSTR_DUMMY0;
+ }
+ return nId;
+}
+
+static sal_Int32 lcl_PropName2TokenPos(std::u16string_view rPropertyName)
+{
+ if (rPropertyName == u"" UNO_NAME_DDE_COMMAND_TYPE)
+ return 0;
+
+ if (rPropertyName == u"" UNO_NAME_DDE_COMMAND_FILE)
+ return 1;
+
+ if (rPropertyName == u"" UNO_NAME_DDE_COMMAND_ELEMENT)
+ return 2;
+
+ if (rPropertyName == u"" UNO_NAME_IS_AUTOMATIC_UPDATE)
+ return 3;
+
+ return SAL_MAX_INT32;
+}
+
+static sal_uInt16 GetFieldTypeMId( std::u16string_view rProperty, const SwFieldType& rTyp )
+{
+ sal_uInt16 nId = lcl_GetPropMapIdForFieldType( rTyp.Which() );
+ const SfxItemPropertySet* pSet = aSwMapProvider.GetPropertySet( nId );
+ if( !pSet )
+ nId = USHRT_MAX;
+ else
+ {
+ const SfxItemPropertyMapEntry* pEntry = pSet->getPropertyMap().getByName(rProperty);
+ nId = pEntry ? pEntry->nWID : USHRT_MAX;
+ }
+ return nId;
+}
+
+static sal_uInt16 lcl_GetPropertyMapOfService( SwServiceType nServiceId )
+{
+ sal_uInt16 nRet;
+ switch ( nServiceId)
+ {
+ case SwServiceType::FieldTypeDateTime: nRet = PROPERTY_MAP_FLDTYP_DATETIME; break;
+ case SwServiceType::FieldTypeUser: nRet = PROPERTY_MAP_FLDTYP_USER; break;
+ case SwServiceType::FieldTypeSetExp: nRet = PROPERTY_MAP_FLDTYP_SET_EXP; break;
+ case SwServiceType::FieldTypeGetExp: nRet = PROPERTY_MAP_FLDTYP_GET_EXP; break;
+ case SwServiceType::FieldTypeFileName: nRet = PROPERTY_MAP_FLDTYP_FILE_NAME; break;
+ case SwServiceType::FieldTypePageNum: nRet = PROPERTY_MAP_FLDTYP_PAGE_NUM; break;
+ case SwServiceType::FieldTypeAuthor: nRet = PROPERTY_MAP_FLDTYP_AUTHOR; break;
+ case SwServiceType::FieldTypeChapter: nRet = PROPERTY_MAP_FLDTYP_CHAPTER; break;
+ case SwServiceType::FieldTypeGetReference: nRet = PROPERTY_MAP_FLDTYP_GET_REFERENCE; break;
+ case SwServiceType::FieldTypeConditionedText: nRet = PROPERTY_MAP_FLDTYP_CONDITIONED_TEXT; break;
+ case SwServiceType::FieldTypeAnnotation: nRet = PROPERTY_MAP_FLDTYP_ANNOTATION; break;
+ case SwServiceType::FieldTypeInputUser:
+ case SwServiceType::FieldTypeInput: nRet = PROPERTY_MAP_FLDTYP_INPUT; break;
+ case SwServiceType::FieldTypeMacro: nRet = PROPERTY_MAP_FLDTYP_MACRO; break;
+ case SwServiceType::FieldTypeDDE: nRet = PROPERTY_MAP_FLDTYP_DDE; break;
+ case SwServiceType::FieldTypeHiddenPara: nRet = PROPERTY_MAP_FLDTYP_HIDDEN_PARA; break;
+ case SwServiceType::FieldTypeDocInfo: nRet = PROPERTY_MAP_FLDTYP_DOC_INFO; break;
+ case SwServiceType::FieldTypeTemplateName: nRet = PROPERTY_MAP_FLDTYP_TEMPLATE_NAME; break;
+ case SwServiceType::FieldTypeUserExt: nRet = PROPERTY_MAP_FLDTYP_USER_EXT; break;
+ case SwServiceType::FieldTypeRefPageSet: nRet = PROPERTY_MAP_FLDTYP_REF_PAGE_SET; break;
+ case SwServiceType::FieldTypeRefPageGet: nRet = PROPERTY_MAP_FLDTYP_REF_PAGE_GET; break;
+ case SwServiceType::FieldTypeJumpEdit: nRet = PROPERTY_MAP_FLDTYP_JUMP_EDIT; break;
+ case SwServiceType::FieldTypeScript: nRet = PROPERTY_MAP_FLDTYP_SCRIPT; break;
+ case SwServiceType::FieldTypeDatabaseNextSet: nRet = PROPERTY_MAP_FLDTYP_DATABASE_NEXT_SET; break;
+ case SwServiceType::FieldTypeDatabaseNumSet: nRet = PROPERTY_MAP_FLDTYP_DATABASE_NUM_SET; break;
+ case SwServiceType::FieldTypeDatabaseSetNum: nRet = PROPERTY_MAP_FLDTYP_DATABASE_SET_NUM; break;
+ case SwServiceType::FieldTypeDatabase: nRet = PROPERTY_MAP_FLDTYP_DATABASE; break;
+ case SwServiceType::FieldTypeDatabaseName: nRet = PROPERTY_MAP_FLDTYP_DATABASE_NAME; break;
+ case SwServiceType::FieldTypeTableFormula: nRet = PROPERTY_MAP_FLDTYP_TABLE_FORMULA; break;
+ case SwServiceType::FieldTypePageCount:
+ case SwServiceType::FieldTypeParagraphCount:
+ case SwServiceType::FieldTypeWordCount:
+ case SwServiceType::FieldTypeCharacterCount:
+ case SwServiceType::FieldTypeTableCount:
+ case SwServiceType::FieldTypeGraphicObjectCount:
+ case SwServiceType::FieldTypeEmbeddedObjectCount: nRet = PROPERTY_MAP_FLDTYP_DOCSTAT; break;
+ case SwServiceType::FieldTypeDocInfoChangeAuthor:
+ case SwServiceType::FieldTypeDocInfoCreateAuthor:
+ case SwServiceType::FieldTypeDocInfoPrintAuthor: nRet = PROPERTY_MAP_FLDTYP_DOCINFO_AUTHOR; break;
+ case SwServiceType::FieldTypeDocInfoChangeDateTime:
+ case SwServiceType::FieldTypeDocInfoCreateDateTime:
+ case SwServiceType::FieldTypeDocInfoPrintDateTime: nRet = PROPERTY_MAP_FLDTYP_DOCINFO_DATE_TIME; break;
+ case SwServiceType::FieldTypeDocInfoEditTime: nRet = PROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME; break;
+ case SwServiceType::FieldTypeDocInfoCustom: nRet = PROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM; break;
+ case SwServiceType::FieldTypeDocInfoDescription:
+ case SwServiceType::FieldTypeDocInfoKeywords:
+ case SwServiceType::FieldTypeDocInfoSubject:
+ case SwServiceType::FieldTypeDocInfoTitle: nRet = PROPERTY_MAP_FLDTYP_DOCINFO_MISC; break;
+ case SwServiceType::FieldTypeDocInfoRevision: nRet = PROPERTY_MAP_FLDTYP_DOCINFO_REVISION; break;
+ case SwServiceType::FieldTypeBibliography: nRet = PROPERTY_MAP_FLDTYP_BIBLIOGRAPHY; break;
+ case SwServiceType::FieldTypeDummy0:
+ case SwServiceType::FieldTypeCombinedCharacters: nRet = PROPERTY_MAP_FLDTYP_COMBINED_CHARACTERS; break;
+ case SwServiceType::FieldTypeDropdown: nRet = PROPERTY_MAP_FLDTYP_DROPDOWN; break;
+ case SwServiceType::FieldTypeDummy4:
+ case SwServiceType::FieldTypeDummy5:
+ case SwServiceType::FieldTypeDummy6:
+ case SwServiceType::FieldTypeDummy7:
+ nRet = PROPERTY_MAP_FLDTYP_DUMMY_0; break;
+ case SwServiceType::FieldMasterUser: nRet = PROPERTY_MAP_FLDMSTR_USER; break;
+ case SwServiceType::FieldMasterDDE: nRet = PROPERTY_MAP_FLDMSTR_DDE; break;
+ case SwServiceType::FieldMasterSetExp: nRet = PROPERTY_MAP_FLDMSTR_SET_EXP; break;
+ case SwServiceType::FieldMasterDatabase: nRet = PROPERTY_MAP_FLDMSTR_DATABASE; break;
+ case SwServiceType::FieldMasterBibliography: nRet = PROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY; break;
+ case SwServiceType::FieldMasterDummy2:
+ case SwServiceType::FieldMasterDummy3:
+ case SwServiceType::FieldMasterDummy4:
+ case SwServiceType::FieldMasterDummy5: nRet = PROPERTY_MAP_FLDMSTR_DUMMY0; break;
+ case SwServiceType::FieldTypeHiddenText: nRet = PROPERTY_MAP_FLDTYP_HIDDEN_TEXT; break;
+ default:
+ nRet = USHRT_MAX;
+ }
+ assert(nRet != USHRT_MAX && "wrong service id");
+ return nRet;
+}
+
+class SwXFieldMaster::Impl
+ : public SvtListener
+{
+public:
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+
+ uno::WeakReference<uno::XInterface> m_wThis;
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+
+ SwDoc* m_pDoc;
+ SwFieldType* m_pType;
+
+ SwFieldIds m_nResTypeId;
+
+ OUString m_sParam1; // Content / Database / NumberingSeparator
+ OUString m_sParam2; // - /DataTablename
+ OUString m_sParam3; // - /DataFieldName
+ OUString m_sParam5; // - /DataBaseURL
+ double m_fParam1; // Value / -
+ sal_Int8 m_nParam1; // ChapterNumberingLevel
+ bool m_bParam1; // IsExpression
+ sal_Int32 m_nParam2;
+
+ Impl(SwPageDesc* const pPageDesc, SwDoc* pDoc, SwFieldIds nResId)
+ : m_pDoc(pDoc)
+ , m_pType(nullptr)
+ , m_nResTypeId(nResId)
+ , m_fParam1(0.0)
+ , m_nParam1(-1)
+ , m_bParam1(false)
+ , m_nParam2(0)
+ {
+ StartListening(pPageDesc->GetNotifier());
+ }
+
+ Impl(SwFieldType* const pType, SwDoc* pDoc, SwFieldIds nResId)
+ : m_pDoc(pDoc)
+ , m_pType(pType)
+ , m_nResTypeId(nResId)
+ , m_fParam1(0.0)
+ , m_nParam1(-1)
+ , m_bParam1(false)
+ , m_nParam2(0)
+ {
+ StartListening(m_pType->GetNotifier());
+ }
+ void SetFieldType(SwFieldType* pType)
+ {
+ EndListeningAll();
+ m_pType = pType;
+ StartListening(m_pType->GetNotifier());
+ }
+protected:
+ virtual void Notify(const SfxHint& rHint) override;
+};
+
+const uno::Sequence< sal_Int8 > & SwXFieldMaster::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXFieldMasterUnoTunnelId;
+ return theSwXFieldMasterUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXFieldMaster::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXFieldMaster>(rId, this);
+}
+
+OUString SAL_CALL
+SwXFieldMaster::getImplementationName()
+{
+ return "SwXFieldMaster";
+}
+
+namespace
+{
+
+OUString getServiceName(const SwFieldIds aId)
+{
+ const char* pEntry;
+ switch (aId)
+ {
+ case SwFieldIds::User:
+ pEntry = "User";
+ break;
+ case SwFieldIds::Database:
+ pEntry = "Database";
+ break;
+ case SwFieldIds::SetExp:
+ pEntry = "SetExpression";
+ break;
+ case SwFieldIds::Dde:
+ pEntry = "DDE";
+ break;
+ case SwFieldIds::TableOfAuthorities:
+ pEntry = "Bibliography";
+ break;
+ default:
+ return OUString();
+ }
+
+ return "com.sun.star.text.fieldmaster." + OUString::createFromAscii(pEntry);
+}
+
+}
+
+sal_Bool SAL_CALL SwXFieldMaster::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXFieldMaster::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextFieldMaster", getServiceName(m_pImpl->m_nResTypeId) };
+}
+
+SwXFieldMaster::SwXFieldMaster(SwDoc *const pDoc, SwFieldIds const nResId)
+ : m_pImpl(new Impl(pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD), pDoc, nResId))
+{
+}
+
+SwXFieldMaster::SwXFieldMaster(SwFieldType& rType, SwDoc * pDoc)
+ : m_pImpl(new Impl(&rType, pDoc, rType.Which()))
+{
+}
+
+SwXFieldMaster::~SwXFieldMaster()
+{
+}
+
+uno::Reference<beans::XPropertySet>
+SwXFieldMaster::CreateXFieldMaster(SwDoc * pDoc, SwFieldType *const pType,
+ SwFieldIds nResId)
+{
+ // re-use existing SwXFieldMaster
+ uno::Reference<beans::XPropertySet> xFM;
+ if (pType)
+ {
+ xFM = pType->GetXObject();
+ }
+ if (!xFM.is())
+ {
+ SwXFieldMaster *const pFM( pType
+ ? new SwXFieldMaster(*pType, pDoc)
+ : new SwXFieldMaster(pDoc, nResId));
+ xFM.set(pFM);
+ if (pType)
+ {
+ pType->SetXObject(xFM);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pFM->m_pImpl->m_wThis = xFM;
+ }
+ return xFM;
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL
+SwXFieldMaster::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< beans::XPropertySetInfo > aRef =
+ aSwMapProvider.GetPropertySet(
+ lcl_GetPropMapIdForFieldType(m_pImpl->m_nResTypeId))->getPropertySetInfo();
+ return aRef;
+}
+
+void SAL_CALL SwXFieldMaster::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+ SwFieldType* pType = GetFieldType(true);
+ if(pType)
+ {
+ bool bSetValue = true;
+ if( rPropertyName == UNO_NAME_SUB_TYPE )
+ {
+ const std::vector<OUString>& rExtraArr(
+ SwStyleNameMapper::GetExtraUINameArray());
+ const OUString sTypeName = pType->GetName();
+ static sal_uInt16 nIds[] =
+ {
+ RES_POOLCOLL_LABEL_DRAWING - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_ABB - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_TABLE - RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_FRAME- RES_POOLCOLL_EXTRA_BEGIN,
+ RES_POOLCOLL_LABEL_FIGURE - RES_POOLCOLL_EXTRA_BEGIN,
+ 0
+ };
+ for(const sal_uInt16 * pIds = nIds; *pIds; ++pIds)
+ {
+ if(sTypeName == rExtraArr[ *pIds ] )
+ {
+ bSetValue = false;
+ break;
+ }
+ }
+ }
+ if ( bSetValue )
+ {
+ // nothing special to be done here for the properties
+ // UNO_NAME_DATA_BASE_NAME and UNO_NAME_DATA_BASE_URL.
+ // We just call PutValue (empty string is allowed).
+ // Thus the last property set will be used as Data Source.
+
+ const sal_uInt16 nMemberValueId = GetFieldTypeMId( rPropertyName, *pType );
+ if ( USHRT_MAX == nMemberValueId )
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast< cppu::OWeakObject * >( this ) );
+ }
+
+ pType->PutValue( rValue, nMemberValueId );
+ if ( pType->Which() == SwFieldIds::User )
+ {
+ // trigger update of User field in order to get depending Input Fields updated.
+ pType->UpdateFields();
+ }
+
+ }
+ }
+ else if (m_pImpl->m_pDoc && rPropertyName == UNO_NAME_NAME)
+ {
+ OUString sTypeName;
+ rValue >>= sTypeName;
+ SwFieldType * pType2 = m_pImpl->m_pDoc->getIDocumentFieldsAccess().GetFieldType(
+ m_pImpl->m_nResTypeId, sTypeName, false);
+
+ if(pType2 ||
+ (SwFieldIds::SetExp == m_pImpl->m_nResTypeId &&
+ ( sTypeName == SwResId(STR_POOLCOLL_LABEL_TABLE) ||
+ sTypeName == SwResId(STR_POOLCOLL_LABEL_DRAWING) ||
+ sTypeName == SwResId(STR_POOLCOLL_LABEL_FRAME) ||
+ sTypeName == SwResId(STR_POOLCOLL_LABEL_ABB) ||
+ sTypeName == SwResId(STR_POOLCOLL_LABEL_FIGURE) )))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ switch (m_pImpl->m_nResTypeId)
+ {
+ case SwFieldIds::User :
+ {
+ SwUserFieldType aType(m_pImpl->m_pDoc, sTypeName);
+ pType2 = m_pImpl->m_pDoc->getIDocumentFieldsAccess().InsertFieldType(aType);
+ static_cast<SwUserFieldType*>(pType2)->SetContent(m_pImpl->m_sParam1);
+ static_cast<SwUserFieldType*>(pType2)->SetValue(m_pImpl->m_fParam1);
+ static_cast<SwUserFieldType*>(pType2)->SetType(m_pImpl->m_bParam1
+ ? nsSwGetSetExpType::GSE_EXPR : nsSwGetSetExpType::GSE_STRING);
+ }
+ break;
+ case SwFieldIds::Dde :
+ {
+ SwDDEFieldType aType(sTypeName, m_pImpl->m_sParam1,
+ m_pImpl->m_bParam1 ? SfxLinkUpdateMode::ALWAYS : SfxLinkUpdateMode::ONCALL);
+ pType2 = m_pImpl->m_pDoc->getIDocumentFieldsAccess().InsertFieldType(aType);
+ }
+ break;
+ case SwFieldIds::SetExp :
+ {
+ SwSetExpFieldType aType(m_pImpl->m_pDoc, sTypeName);
+ if (!m_pImpl->m_sParam1.isEmpty())
+ aType.SetDelimiter(OUString(m_pImpl->m_sParam1[0]));
+ if (m_pImpl->m_nParam1 > -1 && m_pImpl->m_nParam1 < MAXLEVEL)
+ aType.SetOutlineLvl(m_pImpl->m_nParam1);
+ pType2 = m_pImpl->m_pDoc->getIDocumentFieldsAccess().InsertFieldType(aType);
+ }
+ break;
+ case SwFieldIds::Database :
+ {
+ rValue >>= m_pImpl->m_sParam3;
+ pType2 = GetFieldType();
+ }
+ break;
+ default: break;
+ }
+ if (!pType2)
+ {
+ throw uno::RuntimeException("no field type found!", *this);
+ }
+ m_pImpl->SetFieldType(pType2);
+ }
+ else
+ {
+ switch (m_pImpl->m_nResTypeId)
+ {
+ case SwFieldIds::User:
+ if(rPropertyName == UNO_NAME_CONTENT)
+ rValue >>= m_pImpl->m_sParam1;
+ else if(rPropertyName == UNO_NAME_VALUE)
+ {
+ if(rValue.getValueType() != ::cppu::UnoType<double>::get())
+ throw lang::IllegalArgumentException();
+ rValue >>= m_pImpl->m_fParam1;
+ }
+ else if(rPropertyName == UNO_NAME_IS_EXPRESSION)
+ {
+ if(rValue.getValueType() != cppu::UnoType<bool>::get())
+ throw lang::IllegalArgumentException();
+ rValue >>= m_pImpl->m_bParam1;
+ }
+
+ break;
+ case SwFieldIds::Database:
+ if(rPropertyName == UNO_NAME_DATA_BASE_NAME)
+ rValue >>= m_pImpl->m_sParam1;
+ else if(rPropertyName == UNO_NAME_DATA_TABLE_NAME)
+ rValue >>= m_pImpl->m_sParam2;
+ else if(rPropertyName == UNO_NAME_DATA_COLUMN_NAME)
+ rValue >>= m_pImpl->m_sParam3;
+ else if(rPropertyName == UNO_NAME_DATA_COMMAND_TYPE)
+ rValue >>= m_pImpl->m_nParam2;
+ if(rPropertyName == UNO_NAME_DATA_BASE_URL)
+ rValue >>= m_pImpl->m_sParam5;
+
+ if ( ( !m_pImpl->m_sParam1.isEmpty()
+ || !m_pImpl->m_sParam5.isEmpty())
+ && !m_pImpl->m_sParam2.isEmpty()
+ && !m_pImpl->m_sParam3.isEmpty())
+ {
+ GetFieldType();
+ }
+ break;
+ case SwFieldIds::SetExp:
+ if(rPropertyName == UNO_NAME_NUMBERING_SEPARATOR)
+ rValue >>= m_pImpl->m_sParam1;
+ else if(rPropertyName == UNO_NAME_CHAPTER_NUMBERING_LEVEL)
+ rValue >>= m_pImpl->m_nParam1;
+ break;
+ case SwFieldIds::Dde:
+ {
+ sal_Int32 nPart = lcl_PropName2TokenPos(rPropertyName);
+ if(nPart < 3 )
+ {
+ if (m_pImpl->m_sParam1.isEmpty())
+ {
+ m_pImpl->m_sParam1
+ = OUStringChar(sfx2::cTokenSeparator)
+ + OUStringChar(sfx2::cTokenSeparator);
+ }
+ OUString sTmp;
+ rValue >>= sTmp;
+ sal_Int32 nIndex(0);
+ sal_Int32 nStart(0);
+ while (nIndex < m_pImpl->m_sParam1.getLength())
+ {
+ if (m_pImpl->m_sParam1[nIndex] == sfx2::cTokenSeparator)
+ {
+ if (0 == nPart)
+ break;
+ nStart = nIndex + 1;
+ --nPart;
+ }
+ ++nIndex;
+ }
+ assert(0 == nPart);
+ m_pImpl->m_sParam1 = m_pImpl->m_sParam1.replaceAt(
+ nStart, nIndex - nStart, sTmp);
+ }
+ else if(3 == nPart)
+ {
+ rValue >>= m_pImpl->m_bParam1;
+ }
+ }
+ break;
+ default:
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ }
+ }
+}
+
+SwFieldType* SwXFieldMaster::GetFieldType(bool const bDontCreate) const
+{
+ if (!bDontCreate && SwFieldIds::Database == m_pImpl->m_nResTypeId
+ && !m_pImpl->m_pType && m_pImpl->m_pDoc)
+ {
+ SwDBData aData;
+
+ // set DataSource
+ svx::ODataAccessDescriptor aAcc;
+ if (!m_pImpl->m_sParam1.isEmpty())
+ aAcc[svx::DataAccessDescriptorProperty::DataSource] <<= m_pImpl->m_sParam1; // DataBaseName
+ else if (!m_pImpl->m_sParam5.isEmpty())
+ aAcc[svx::DataAccessDescriptorProperty::DatabaseLocation] <<= m_pImpl->m_sParam5; // DataBaseURL
+ aData.sDataSource = aAcc.getDataSource();
+
+ aData.sCommand = m_pImpl->m_sParam2;
+ aData.nCommandType = m_pImpl->m_nParam2;
+
+ SwDBFieldType aType(m_pImpl->m_pDoc, m_pImpl->m_sParam3, aData);
+ SwFieldType *const pType = m_pImpl->m_pDoc->getIDocumentFieldsAccess().InsertFieldType(aType);
+ m_pImpl->SetFieldType(pType);
+ }
+ return m_pImpl->m_pType;
+}
+
+uno::Any SAL_CALL
+SwXFieldMaster::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwFieldType* pType = GetFieldType(true);
+ if( rPropertyName == UNO_NAME_INSTANCE_NAME )
+ {
+ OUString sName;
+ if(pType)
+ SwXTextFieldMasters::getInstanceName(*pType, sName);
+ aRet <<= sName;
+ }
+ else if(pType)
+ {
+ if(rPropertyName == UNO_NAME_NAME)
+ {
+ aRet <<= SwXFieldMaster::GetProgrammaticName(*pType, *m_pImpl->m_pDoc);
+ }
+ else if(rPropertyName == UNO_NAME_DEPENDENT_TEXT_FIELDS)
+ {
+ //fill all text fields into a sequence
+ std::vector<SwFormatField*> vpFields;
+ pType->GatherFields(vpFields);
+ uno::Sequence<uno::Reference <text::XDependentTextField> > aSeq(vpFields.size());
+ std::transform(vpFields.begin(), vpFields.end(), aSeq.getArray(),
+ [this](SwFormatField* pF) { return uno::Reference<text::XDependentTextField>(SwXTextField::CreateXTextField(m_pImpl->m_pDoc, pF), uno::UNO_QUERY); });
+ aRet <<= aSeq;
+ }
+ else
+ {
+ //TODO: add properties for the other field types
+ const sal_uInt16 nMId = GetFieldTypeMId( rPropertyName, *pType );
+ if (USHRT_MAX == nMId)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+ pType->QueryValue( aRet, nMId );
+
+ if (rPropertyName == UNO_NAME_DATA_BASE_NAME ||
+ rPropertyName == UNO_NAME_DATA_BASE_URL)
+ {
+ OUString aDataSource;
+ aRet >>= aDataSource;
+ aRet <<= OUString();
+
+ OUString *pStr = nullptr; // only one of this properties will return
+ // a non-empty string.
+ INetURLObject aObj;
+ aObj.SetURL( aDataSource );
+ bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
+ if (bIsURL && rPropertyName == UNO_NAME_DATA_BASE_URL)
+ pStr = &aDataSource; // DataBaseURL
+ else if (!bIsURL && rPropertyName == UNO_NAME_DATA_BASE_NAME)
+ pStr = &aDataSource; // DataBaseName
+
+ if (pStr)
+ aRet <<= *pStr;
+ }
+ }
+ }
+ else
+ {
+ if(rPropertyName == UNO_NAME_DATA_COMMAND_TYPE)
+ aRet <<= m_pImpl->m_nParam2;
+ else if(rPropertyName == UNO_NAME_DEPENDENT_TEXT_FIELDS )
+ {
+ uno::Sequence<uno::Reference <text::XDependentTextField> > aRetSeq(0);
+ aRet <<= aRetSeq;
+ }
+ else
+ {
+ switch (m_pImpl->m_nResTypeId)
+ {
+ case SwFieldIds::User:
+ if( rPropertyName == UNO_NAME_CONTENT )
+ aRet <<= m_pImpl->m_sParam1;
+ else if(rPropertyName == UNO_NAME_VALUE)
+ aRet <<= m_pImpl->m_fParam1;
+ else if(rPropertyName == UNO_NAME_IS_EXPRESSION)
+ aRet <<= m_pImpl->m_bParam1;
+ break;
+ case SwFieldIds::Database:
+ if(rPropertyName == UNO_NAME_DATA_BASE_NAME ||
+ rPropertyName == UNO_NAME_DATA_BASE_URL)
+ {
+ // only one of these properties returns a non-empty string.
+ INetURLObject aObj;
+ aObj.SetURL(m_pImpl->m_sParam5); // SetSmartURL
+ bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid;
+ if (bIsURL && rPropertyName == UNO_NAME_DATA_BASE_URL)
+ aRet <<= m_pImpl->m_sParam5; // DataBaseURL
+ else if ( rPropertyName == UNO_NAME_DATA_BASE_NAME)
+ aRet <<= m_pImpl->m_sParam1; // DataBaseName
+ }
+ else if(rPropertyName == UNO_NAME_DATA_TABLE_NAME)
+ aRet <<= m_pImpl->m_sParam2;
+ else if(rPropertyName == UNO_NAME_DATA_COLUMN_NAME)
+ aRet <<= m_pImpl->m_sParam3;
+ break;
+ case SwFieldIds::SetExp:
+ if(rPropertyName == UNO_NAME_NUMBERING_SEPARATOR)
+ aRet <<= m_pImpl->m_sParam1;
+ else if(rPropertyName == UNO_NAME_CHAPTER_NUMBERING_LEVEL)
+ aRet <<= m_pImpl->m_nParam1;
+ break;
+ case SwFieldIds::Dde:
+ {
+ const sal_Int32 nPart = lcl_PropName2TokenPos(rPropertyName);
+ if(nPart < 3 )
+ aRet <<= m_pImpl->m_sParam1.getToken(nPart, sfx2::cTokenSeparator);
+ else if(3 == nPart)
+ aRet <<= m_pImpl->m_bParam1;
+ }
+ break;
+ default:
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ }
+ }
+ }
+ return aRet;
+}
+
+void SwXFieldMaster::addPropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFieldMaster::removePropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFieldMaster::addVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFieldMaster::removeVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL SwXFieldMaster::dispose()
+{
+ SolarMutexGuard aGuard;
+ SwFieldType *const pFieldType = GetFieldType(true);
+ if (!pFieldType)
+ throw uno::RuntimeException();
+ size_t nTypeIdx = SIZE_MAX;
+ const SwFieldTypes* pTypes = m_pImpl->m_pDoc->getIDocumentFieldsAccess().GetFieldTypes();
+ for( size_t i = 0; i < pTypes->size(); i++ )
+ {
+ if((*pTypes)[i].get()== pFieldType)
+ nTypeIdx = i;
+ }
+
+ // first delete all fields
+ std::vector<SwFormatField*> vpFields;
+ pFieldType->GatherFields(vpFields);
+ for(auto pField : vpFields)
+ SwTextField::DeleteTextField(*pField->GetTextField());
+ // then delete FieldType
+ m_pImpl->m_pDoc->getIDocumentFieldsAccess().RemoveFieldType(nTypeIdx);
+}
+
+void SAL_CALL SwXFieldMaster::addEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXFieldMaster::removeEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+void SwXFieldMaster::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pDoc = nullptr;
+ m_pType = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ }
+}
+
+OUString SwXFieldMaster::GetProgrammaticName(const SwFieldType& rType, SwDoc& rDoc)
+{
+ const OUString sName(rType.GetName());
+ if(SwFieldIds::SetExp == rType.Which())
+ {
+ const SwFieldTypes* pTypes = rDoc.getIDocumentFieldsAccess().GetFieldTypes();
+ for( size_t i = 0; i <= o3tl::make_unsigned(INIT_FLDTYPES); i++ )
+ {
+ if((*pTypes)[i].get() == &rType)
+ {
+ return SwStyleNameMapper::GetProgName( sName, SwGetPoolIdFromName::TxtColl );
+ }
+ }
+ }
+ return sName;
+}
+
+OUString SwXFieldMaster::LocalizeFormula(
+ const SwSetExpField& rField,
+ const OUString& rFormula,
+ bool bQuery)
+{
+ const OUString sTypeName(rField.GetTyp()->GetName());
+ const OUString sProgName(
+ SwStyleNameMapper::GetProgName(sTypeName, SwGetPoolIdFromName::TxtColl ));
+ if(sProgName != sTypeName)
+ {
+ const OUString sSource = bQuery ? sTypeName : sProgName;
+ const OUString sDest = bQuery ? sProgName : sTypeName;
+ if(rFormula.startsWith(sSource))
+ {
+ return sDest + rFormula.subView(sSource.getLength());
+ }
+ }
+ return rFormula;
+}
+
+namespace {
+
+struct SwFieldProperties_Impl
+{
+ OUString sPar1;
+ OUString sPar2;
+ OUString sPar3;
+ OUString sPar4;
+ Date aDate;
+ double fDouble;
+ uno::Sequence<beans::PropertyValue> aPropSeq;
+ uno::Sequence<OUString> aStrings;
+ std::unique_ptr<util::DateTime> pDateTime;
+
+ sal_Int32 nSubType;
+ sal_Int32 nFormat;
+ sal_uInt16 nUSHORT1;
+ sal_uInt16 nUSHORT2;
+ sal_Int16 nSHORT1;
+ sal_Int8 nByte1;
+ bool bFormatIsDefault;
+ bool bBool1;
+ bool bBool2;
+ bool bBool3;
+ bool bBool4;
+
+ SwFieldProperties_Impl():
+ aDate( Date::EMPTY ),
+ fDouble(0.),
+ nSubType(0),
+ nFormat(0),
+ nUSHORT1(0),
+ nUSHORT2(0),
+ nSHORT1(0),
+ nByte1(0),
+ bFormatIsDefault(true),
+ bBool1(false),
+ bBool2(false),
+ bBool3(false),
+ bBool4(true) //Automatic language
+ {}
+};
+
+}
+
+class SwXTextField::Impl
+ : public SvtListener
+{
+public:
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ SwFieldType* m_pFieldType;
+ SwFormatField* m_pFormatField;
+
+ uno::WeakReference<uno::XInterface> m_wThis;
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+
+ SwDoc* m_pDoc;
+ rtl::Reference<SwTextAPIObject> m_xTextObject;
+ bool m_bIsDescriptor;
+ bool m_bCallUpdate;
+ SwServiceType m_nServiceId;
+ OUString m_sTypeName;
+ std::unique_ptr<SwFieldProperties_Impl> m_pProps;
+
+ Impl(SwDoc *const pDoc, SwFormatField *const pFormat, SwServiceType nServiceId)
+ : m_pFieldType(nullptr)
+ , m_pFormatField(pFormat)
+ , m_pDoc(pDoc)
+ , m_bIsDescriptor(pFormat == nullptr)
+ , m_bCallUpdate(false)
+ , m_nServiceId(pFormat
+ ? lcl_GetServiceForField(*pFormat->GetField())
+ : nServiceId)
+ , m_pProps(pFormat ? nullptr : new SwFieldProperties_Impl)
+ {
+ if(m_pFormatField)
+ StartListening(m_pFormatField->GetNotifier());
+ }
+
+ virtual ~Impl() override
+ {
+ if (m_xTextObject.is())
+ {
+ m_xTextObject->DisposeEditSource();
+ }
+ }
+
+ void SetFormatField(SwFormatField* pFormatField, SwDoc* pDoc)
+ {
+ m_pFormatField = pFormatField;
+ m_pDoc = pDoc;
+ if(m_pFormatField)
+ {
+ EndListeningAll();
+ StartListening(m_pFormatField->GetNotifier());
+ }
+ }
+ SwFormatField* GetFormatField()
+ {
+ return m_pFormatField;
+ }
+ bool IsDescriptor() const
+ {
+ // ideally should be: !m_pFormatField && m_pDoc
+ // but: SwXServiceProvider::MakeInstance() passes nullptr SwDoc, see comment there
+ return m_bIsDescriptor;
+ }
+ void Invalidate();
+
+ const SwField* GetField() const;
+
+ SwFieldType* GetFieldType() const
+ {
+ if(!m_pDoc && !IsDescriptor())
+ throw uno::RuntimeException();
+ else if (IsDescriptor())
+ return m_pFieldType;
+
+ return m_pFormatField->GetField()->GetTyp();
+ }
+ void SetFieldType(SwFieldType& rType)
+ {
+ EndListeningAll();
+ m_pFieldType = &rType;
+ StartListening(m_pFieldType->GetNotifier());
+ }
+ void ClearFieldType()
+ {
+ SvtListener::EndListeningAll();
+ m_pFieldType = nullptr;
+ }
+ virtual void Notify(const SfxHint&) override;
+};
+
+const uno::Sequence< sal_Int8 > & SwXTextField::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextFieldUnoTunnelId;
+ return theSwXTextFieldUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXTextField::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXTextField>(rId, this);
+}
+
+SwXTextField::SwXTextField(
+ SwServiceType nServiceId,
+ SwDoc* pDoc)
+ : m_pImpl(new Impl(pDoc, nullptr, nServiceId))
+{
+ //Set visible as default!
+ if ( SwServiceType::FieldTypeSetExp == nServiceId
+ || SwServiceType::FieldTypeDatabaseSetNum == nServiceId
+ || SwServiceType::FieldTypeDatabase == nServiceId
+ || SwServiceType::FieldTypeDatabaseName == nServiceId )
+ {
+ m_pImpl->m_pProps->bBool2 = true;
+ }
+ else if(SwServiceType::FieldTypeTableFormula == nServiceId)
+ {
+ m_pImpl->m_pProps->bBool1 = true;
+ }
+ if(SwServiceType::FieldTypeSetExp == nServiceId)
+ {
+ m_pImpl->m_pProps->nUSHORT2 = USHRT_MAX;
+ }
+}
+
+SwXTextField::SwXTextField(SwFormatField& rFormat, SwDoc & rDoc)
+ : m_pImpl(new Impl(&rDoc, &rFormat, SwServiceType::Invalid))
+{
+}
+
+SwXTextField::~SwXTextField()
+{
+}
+
+uno::Reference<text::XTextField>
+SwXTextField::CreateXTextField(SwDoc *const pDoc, SwFormatField const* pFormat,
+ SwServiceType nServiceId)
+{
+ assert(!pFormat || pDoc);
+ assert(pFormat || nServiceId != SwServiceType::Invalid);
+ // re-use existing SwXTextField
+ uno::Reference<text::XTextField> xField;
+ if (pFormat)
+ {
+ xField = pFormat->GetXTextField();
+ }
+ if (!xField.is())
+ {
+ SwXTextField *const pField( pFormat
+ ? new SwXTextField(const_cast<SwFormatField&>(*pFormat), *pDoc)
+ : new SwXTextField(nServiceId, pDoc));
+ xField.set(pField);
+ if (pFormat)
+ {
+ const_cast<SwFormatField *>(pFormat)->SetXTextField(xField);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pField->m_pImpl->m_wThis = xField;
+ }
+ return xField;
+}
+
+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()
+ ? comphelper::getFromUnoTunnel<SwXTextField>(uno::Reference<lang::XUnoTunnel>(xField, uno::UNO_QUERY_THROW))
+ : nullptr;
+ if (pXField)
+ pXField->m_pImpl->SetFormatField(nullptr, 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 (pXField)
+ {
+ pXField->m_pImpl->SetFormatField(const_cast<SwFormatField*>(&rNewFormat), &rNode.GetDoc());
+ const_cast<SwFormatField&>(rNewFormat).SetXTextField(xField);
+ }
+}
+
+void SAL_CALL SwXTextField::attachTextFieldMaster(
+ const uno::Reference< beans::XPropertySet > & xFieldMaster)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->IsDescriptor())
+ throw uno::RuntimeException();
+ uno::Reference< lang::XUnoTunnel > xMasterTunnel(xFieldMaster, uno::UNO_QUERY);
+ if (!xMasterTunnel.is())
+ throw lang::IllegalArgumentException();
+ SwXFieldMaster* pMaster = comphelper::getFromUnoTunnel<SwXFieldMaster>(xMasterTunnel);
+
+ SwFieldType* pFieldType = pMaster ? pMaster->GetFieldType() : nullptr;
+ if (!pFieldType ||
+ pFieldType->Which() != lcl_ServiceIdToResId(m_pImpl->m_nServiceId))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ m_pImpl->m_sTypeName = pFieldType->GetName();
+ m_pImpl->SetFieldType(*pFieldType);
+}
+
+uno::Reference< beans::XPropertySet > SAL_CALL
+SwXTextField::getTextFieldMaster()
+{
+ SolarMutexGuard aGuard;
+
+ SwFieldType* pType = m_pImpl->GetFieldType();
+ if (!pType && !m_pImpl->m_pDoc) // tdf#152619
+ return nullptr;
+ uno::Reference<beans::XPropertySet> const xRet(
+ SwXFieldMaster::CreateXFieldMaster(m_pImpl->m_pDoc, pType));
+ return xRet;
+}
+
+OUString SAL_CALL SwXTextField::getPresentation(sal_Bool bShowCommand)
+{
+ SolarMutexGuard aGuard;
+
+ SwField const*const pField = m_pImpl->GetField();
+ if (!pField)
+ {
+ throw uno::RuntimeException();
+ }
+ return bShowCommand ? pField->GetFieldName() : pField->ExpandField(true, nullptr);
+}
+
+void SAL_CALL SwXTextField::attach(
+ const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard aGuard;
+ if (m_pImpl->IsDescriptor())
+ {
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ SwDoc* pDoc = pRange ? &pRange->GetDoc() : pCursor ? pCursor->GetDoc() : nullptr;
+ // if a FieldMaster was attached, then the document is already fixed!
+ // NOTE: sw.SwXAutoTextEntry unoapi test depends on m_pDoc = 0 being valid
+ if (!pDoc || (m_pImpl->m_pDoc && m_pImpl->m_pDoc != pDoc))
+ throw lang::IllegalArgumentException();
+
+ SwUnoInternalPaM aPam(*pDoc);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+ std::unique_ptr<SwField> xField;
+ switch (m_pImpl->m_nServiceId)
+ {
+ case SwServiceType::FieldTypeAnnotation:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Postit);
+
+ DateTime aDateTime( DateTime::EMPTY );
+ if (m_pImpl->m_pProps->pDateTime)
+ {
+ aDateTime = *(m_pImpl->m_pProps->pDateTime);
+ }
+ SwPostItField* pPostItField = new SwPostItField(
+ static_cast<SwPostItFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar1, // author
+ m_pImpl->m_pProps->sPar2, // content
+ m_pImpl->m_pProps->sPar3, // author's initials
+ m_pImpl->m_pProps->sPar4, // name
+ aDateTime,
+ m_pImpl->m_pProps->bBool1 // resolvedflag
+ );
+ if ( m_pImpl->m_xTextObject.is() )
+ {
+ pPostItField->SetTextObject( m_pImpl->m_xTextObject->CreateText() );
+ pPostItField->SetPar2(m_pImpl->m_xTextObject->GetText());
+ }
+ xField.reset(pPostItField);
+ }
+ break;
+ case SwServiceType::FieldTypeScript:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Script);
+ xField.reset(new SwScriptField(static_cast<SwScriptFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar1, m_pImpl->m_pProps->sPar2,
+ m_pImpl->m_pProps->bBool1));
+ }
+ break;
+ case SwServiceType::FieldTypeDateTime:
+ {
+ sal_uInt16 nSub = 0;
+ if (m_pImpl->m_pProps->bBool1)
+ nSub |= FIXEDFLD;
+ if (m_pImpl->m_pProps->bBool2)
+ nSub |= DATEFLD;
+ else
+ nSub |= TIMEFLD;
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DateTime);
+ SwDateTimeField *const pDTField = new SwDateTimeField(
+ static_cast<SwDateTimeFieldType*>(pFieldType),
+ nSub, m_pImpl->m_pProps->nFormat);
+ xField.reset(pDTField);
+ if (m_pImpl->m_pProps->fDouble > 0.)
+ {
+ pDTField->SetValue(m_pImpl->m_pProps->fDouble);
+ }
+ if (m_pImpl->m_pProps->pDateTime)
+ {
+ uno::Any aVal; aVal <<= *m_pImpl->m_pProps->pDateTime;
+ xField->PutValue( aVal, FIELD_PROP_DATE_TIME );
+ }
+ pDTField->SetOffset(m_pImpl->m_pProps->nSubType);
+ }
+ break;
+ case SwServiceType::FieldTypeFileName:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Filename);
+ sal_Int32 nFormat = m_pImpl->m_pProps->nFormat;
+ if (m_pImpl->m_pProps->bBool2)
+ nFormat |= FF_FIXED;
+ SwFileNameField *const pFNField = new SwFileNameField(
+ static_cast<SwFileNameFieldType*>(pFieldType), nFormat);
+ xField.reset(pFNField);
+ if (!m_pImpl->m_pProps->sPar3.isEmpty())
+ pFNField->SetExpansion(m_pImpl->m_pProps->sPar3);
+ uno::Any aFormat;
+ aFormat <<= m_pImpl->m_pProps->nFormat;
+ xField->PutValue( aFormat, FIELD_PROP_FORMAT );
+ }
+ break;
+ case SwServiceType::FieldTypeTemplateName:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::TemplateName);
+ xField.reset(new SwTemplNameField(static_cast<SwTemplNameFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nFormat));
+ uno::Any aFormat;
+ aFormat <<= m_pImpl->m_pProps->nFormat;
+ xField->PutValue(aFormat, FIELD_PROP_FORMAT);
+ }
+ break;
+ case SwServiceType::FieldTypeChapter:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Chapter);
+ SwChapterField *const pChapterField = new SwChapterField(
+ static_cast<SwChapterFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nUSHORT1);
+ xField.reset(pChapterField);
+ pChapterField->SetLevel(m_pImpl->m_pProps->nByte1);
+ uno::Any aVal;
+ aVal <<= static_cast<sal_Int16>(m_pImpl->m_pProps->nUSHORT1);
+ xField->PutValue(aVal, FIELD_PROP_USHORT1 );
+ }
+ break;
+ case SwServiceType::FieldTypeAuthor:
+ {
+ tools::Long nFormat = m_pImpl->m_pProps->bBool1 ? AF_NAME : AF_SHORTCUT;
+ if (m_pImpl->m_pProps->bBool2)
+ nFormat |= AF_FIXED;
+
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Author);
+ SwAuthorField *const pAuthorField = new SwAuthorField(
+ static_cast<SwAuthorFieldType*>(pFieldType), nFormat);
+ xField.reset(pAuthorField);
+ pAuthorField->SetExpansion(m_pImpl->m_pProps->sPar1);
+ }
+ break;
+ case SwServiceType::FieldTypeConditionedText:
+ case SwServiceType::FieldTypeHiddenText:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenText);
+ SwHiddenTextField *const pHTField = new SwHiddenTextField(
+ static_cast<SwHiddenTextFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar1,
+ m_pImpl->m_pProps->sPar2, m_pImpl->m_pProps->sPar3,
+ SwServiceType::FieldTypeHiddenText == m_pImpl->m_nServiceId ?
+ SwFieldTypesEnum::HiddenText : SwFieldTypesEnum::ConditionalText);
+ xField.reset(pHTField);
+ pHTField->SetValue(m_pImpl->m_pProps->bBool1);
+ uno::Any aVal;
+ aVal <<= m_pImpl->m_pProps->sPar4;
+ xField->PutValue(aVal, FIELD_PROP_PAR4 );
+ }
+ break;
+ case SwServiceType::FieldTypeHiddenPara:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenPara);
+ SwHiddenParaField *const pHPField = new SwHiddenParaField(
+ static_cast<SwHiddenParaFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar1);
+ xField.reset(pHPField);
+ pHPField->SetHidden(m_pImpl->m_pProps->bBool1);
+ }
+ break;
+ case SwServiceType::FieldTypeGetReference:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetRef);
+ xField.reset(new SwGetRefField(static_cast<SwGetRefFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar1,
+ m_pImpl->m_pProps->sPar4,
+ 0,
+ 0,
+ 0));
+ if (!m_pImpl->m_pProps->sPar3.isEmpty())
+ static_cast<SwGetRefField*>(xField.get())->SetExpand(m_pImpl->m_pProps->sPar3);
+ uno::Any aVal;
+ aVal <<= static_cast<sal_Int16>(m_pImpl->m_pProps->nUSHORT1);
+ xField->PutValue(aVal, FIELD_PROP_USHORT1 );
+ aVal <<= static_cast<sal_Int16>(m_pImpl->m_pProps->nUSHORT2);
+ xField->PutValue(aVal, FIELD_PROP_USHORT2 );
+ aVal <<= m_pImpl->m_pProps->nSHORT1;
+ xField->PutValue(aVal, FIELD_PROP_SHORT1 );
+ }
+ break;
+ case SwServiceType::FieldTypeJumpEdit:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::JumpEdit);
+ xField.reset(new SwJumpEditField(static_cast<SwJumpEditFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nUSHORT1, m_pImpl->m_pProps->sPar2,
+ m_pImpl->m_pProps->sPar1));
+ }
+ break;
+ case SwServiceType::FieldTypeDocInfoChangeAuthor:
+ case SwServiceType::FieldTypeDocInfoChangeDateTime:
+ case SwServiceType::FieldTypeDocInfoEditTime:
+ case SwServiceType::FieldTypeDocInfoDescription:
+ case SwServiceType::FieldTypeDocInfoCreateAuthor:
+ case SwServiceType::FieldTypeDocInfoCreateDateTime:
+ case SwServiceType::FieldTypeDocInfoCustom:
+ case SwServiceType::FieldTypeDocInfoPrintAuthor:
+ case SwServiceType::FieldTypeDocInfoPrintDateTime:
+ case SwServiceType::FieldTypeDocInfoKeywords:
+ case SwServiceType::FieldTypeDocInfoSubject:
+ case SwServiceType::FieldTypeDocInfoTitle:
+ case SwServiceType::FieldTypeDocInfoRevision:
+ case SwServiceType::FieldTypeDocInfo:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocInfo);
+ sal_uInt16 nSubType = aDocInfoSubTypeFromService[
+ static_cast<sal_uInt16>(m_pImpl->m_nServiceId) - sal_uInt16(SwServiceType::FieldTypeDocInfoChangeAuthor)];
+ if (SwServiceType::FieldTypeDocInfoChangeDateTime == m_pImpl->m_nServiceId ||
+ SwServiceType::FieldTypeDocInfoCreateDateTime == m_pImpl->m_nServiceId ||
+ SwServiceType::FieldTypeDocInfoPrintDateTime == m_pImpl->m_nServiceId ||
+ SwServiceType::FieldTypeDocInfoEditTime == m_pImpl->m_nServiceId)
+ {
+ if (m_pImpl->m_pProps->bBool2) //IsDate
+ {
+ nSubType &= 0xf0ff;
+ nSubType |= DI_SUB_DATE;
+ }
+ else
+ {
+ nSubType &= 0xf0ff;
+ nSubType |= DI_SUB_TIME;
+ }
+ }
+ if (m_pImpl->m_pProps->bBool1)
+ nSubType |= DI_SUB_FIXED;
+ xField.reset(new SwDocInfoField(
+ static_cast<SwDocInfoFieldType*>(pFieldType), nSubType,
+ m_pImpl->m_pProps->sPar4, m_pImpl->m_pProps->nFormat));
+ if (!m_pImpl->m_pProps->sPar3.isEmpty())
+ static_cast<SwDocInfoField*>(xField.get())->SetExpansion(m_pImpl->m_pProps->sPar3);
+ }
+ break;
+ case SwServiceType::FieldTypeUserExt:
+ {
+ sal_Int32 nFormat = 0;
+ if (m_pImpl->m_pProps->bBool1)
+ nFormat = AF_FIXED;
+
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::ExtUser);
+ SwExtUserField *const pEUField = new SwExtUserField(
+ static_cast<SwExtUserFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nUSHORT1, nFormat);
+ xField.reset(pEUField);
+ pEUField->SetExpansion(m_pImpl->m_pProps->sPar1);
+ }
+ break;
+ case SwServiceType::FieldTypeUser:
+ {
+ SwFieldType* pFieldType =
+ pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::User, m_pImpl->m_sTypeName, true);
+ if (!pFieldType)
+ throw uno::RuntimeException();
+ sal_uInt16 nUserSubType = (m_pImpl->m_pProps->bBool1)
+ ? nsSwExtendedSubType::SUB_INVISIBLE : 0;
+ if (m_pImpl->m_pProps->bBool2)
+ nUserSubType |= nsSwExtendedSubType::SUB_CMD;
+ if (m_pImpl->m_pProps->bFormatIsDefault &&
+ nsSwGetSetExpType::GSE_STRING == static_cast<SwUserFieldType*>(pFieldType)->GetType())
+ {
+ m_pImpl->m_pProps->nFormat = -1;
+ }
+ xField.reset(new SwUserField(static_cast<SwUserFieldType*>(pFieldType),
+ nUserSubType,
+ m_pImpl->m_pProps->nFormat));
+ }
+ break;
+ case SwServiceType::FieldTypeRefPageSet:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::RefPageSet);
+ xField.reset(new SwRefPageSetField( static_cast<SwRefPageSetFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nUSHORT1,
+ m_pImpl->m_pProps->bBool1 ));
+ }
+ break;
+ case SwServiceType::FieldTypeRefPageGet:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::RefPageGet);
+ SwRefPageGetField *const pRGField = new SwRefPageGetField(
+ static_cast<SwRefPageGetFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nUSHORT1 );
+ xField.reset(pRGField);
+ pRGField->SetText(m_pImpl->m_pProps->sPar1, nullptr);
+ }
+ break;
+ case SwServiceType::FieldTypePageNum:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::PageNumber);
+ SwPageNumberField *const pPNField = new SwPageNumberField(
+ static_cast<SwPageNumberFieldType*>(pFieldType), PG_RANDOM,
+ m_pImpl->m_pProps->nFormat,
+ m_pImpl->m_pProps->nUSHORT1);
+ xField.reset(pPNField);
+ pPNField->SetUserString(m_pImpl->m_pProps->sPar1);
+ uno::Any aVal;
+ aVal <<= m_pImpl->m_pProps->nSubType;
+ xField->PutValue( aVal, FIELD_PROP_SUBTYPE );
+ }
+ break;
+ case SwServiceType::FieldTypeDDE:
+ {
+ SwFieldType* pFieldType =
+ pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Dde, m_pImpl->m_sTypeName, true);
+ if (!pFieldType)
+ throw uno::RuntimeException();
+ xField.reset(new SwDDEField( static_cast<SwDDEFieldType*>(pFieldType) ));
+ }
+ break;
+ case SwServiceType::FieldTypeDatabaseName:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DatabaseName);
+ SwDBData aData;
+ aData.sDataSource = m_pImpl->m_pProps->sPar1;
+ aData.sCommand = m_pImpl->m_pProps->sPar2;
+ aData.nCommandType = m_pImpl->m_pProps->nSHORT1;
+ xField.reset(new SwDBNameField(static_cast<SwDBNameFieldType*>(pFieldType), aData));
+ sal_uInt16 nSubType = xField->GetSubType();
+ if (m_pImpl->m_pProps->bBool2)
+ nSubType &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ nSubType |= nsSwExtendedSubType::SUB_INVISIBLE;
+ xField->SetSubType(nSubType);
+ }
+ break;
+ case SwServiceType::FieldTypeDatabaseNextSet:
+ {
+ SwDBData aData;
+ aData.sDataSource = m_pImpl->m_pProps->sPar1;
+ aData.sCommand = m_pImpl->m_pProps->sPar2;
+ aData.nCommandType = m_pImpl->m_pProps->nSHORT1;
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DbNextSet);
+ xField.reset(new SwDBNextSetField(static_cast<SwDBNextSetFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar3, aData));
+ }
+ break;
+ case SwServiceType::FieldTypeDatabaseNumSet:
+ {
+ SwDBData aData;
+ aData.sDataSource = m_pImpl->m_pProps->sPar1;
+ aData.sCommand = m_pImpl->m_pProps->sPar2;
+ aData.nCommandType = m_pImpl->m_pProps->nSHORT1;
+ xField.reset(new SwDBNumSetField( static_cast<SwDBNumSetFieldType*>(
+ pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DbNumSet)),
+ m_pImpl->m_pProps->sPar3,
+ OUString::number(m_pImpl->m_pProps->nFormat),
+ aData ));
+ }
+ break;
+ case SwServiceType::FieldTypeDatabaseSetNum:
+ {
+ SwDBData aData;
+ aData.sDataSource = m_pImpl->m_pProps->sPar1;
+ aData.sCommand = m_pImpl->m_pProps->sPar2;
+ aData.nCommandType = m_pImpl->m_pProps->nSHORT1;
+ SwDBSetNumberField *const pDBSNField =
+ new SwDBSetNumberField(static_cast<SwDBSetNumberFieldType*>(
+ pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DbSetNumber)), aData,
+ m_pImpl->m_pProps->nUSHORT1);
+ xField.reset(pDBSNField);
+ pDBSNField->SetSetNumber(m_pImpl->m_pProps->nFormat);
+ sal_uInt16 nSubType = xField->GetSubType();
+ if (m_pImpl->m_pProps->bBool2)
+ nSubType &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ nSubType |= nsSwExtendedSubType::SUB_INVISIBLE;
+ xField->SetSubType(nSubType);
+ }
+ break;
+ case SwServiceType::FieldTypeDatabase:
+ {
+ SwFieldType* pFieldType =
+ pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Database, m_pImpl->m_sTypeName, false);
+ if (!pFieldType)
+ throw uno::RuntimeException();
+ xField.reset(new SwDBField(static_cast<SwDBFieldType*>(pFieldType),
+ m_pImpl->m_pProps->nFormat));
+ static_cast<SwDBField*>(xField.get())->InitContent(m_pImpl->m_pProps->sPar1);
+ sal_uInt16 nSubType = xField->GetSubType();
+ if (m_pImpl->m_pProps->bBool2)
+ nSubType &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ nSubType |= nsSwExtendedSubType::SUB_INVISIBLE;
+ xField->SetSubType(nSubType);
+ }
+ break;
+ case SwServiceType::FieldTypeSetExp:
+ {
+ SwFieldType* pFieldType =
+ pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::SetExp, m_pImpl->m_sTypeName, true);
+ if (!pFieldType)
+ throw uno::RuntimeException();
+ // detect the field type's sub type and set an appropriate number format
+ if (m_pImpl->m_pProps->bFormatIsDefault &&
+ nsSwGetSetExpType::GSE_STRING == static_cast<SwSetExpFieldType*>(pFieldType)->GetType())
+ {
+ m_pImpl->m_pProps->nFormat = -1;
+ }
+ SwSetExpField *const pSEField = new SwSetExpField(
+ static_cast<SwSetExpFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar2,
+ m_pImpl->m_pProps->nUSHORT2 != USHRT_MAX ? //#i79471# the field can have a number format or a number_ing_ format
+ m_pImpl->m_pProps->nUSHORT2 : m_pImpl->m_pProps->nFormat);
+ xField.reset(pSEField);
+
+ sal_uInt16 nSubType = xField->GetSubType();
+ if (m_pImpl->m_pProps->bBool2)
+ nSubType &= ~nsSwExtendedSubType::SUB_INVISIBLE;
+ else
+ nSubType |= nsSwExtendedSubType::SUB_INVISIBLE;
+ if (m_pImpl->m_pProps->bBool3)
+ nSubType |= nsSwExtendedSubType::SUB_CMD;
+ else
+ nSubType &= ~nsSwExtendedSubType::SUB_CMD;
+ xField->SetSubType(nSubType);
+ pSEField->SetSeqNumber(m_pImpl->m_pProps->nUSHORT1);
+ pSEField->SetInputFlag(m_pImpl->m_pProps->bBool1);
+ pSEField->SetPromptText(m_pImpl->m_pProps->sPar3);
+ if (!m_pImpl->m_pProps->sPar4.isEmpty())
+ pSEField->ChgExpStr(m_pImpl->m_pProps->sPar4, nullptr);
+
+ }
+ break;
+ case SwServiceType::FieldTypeGetExp:
+ {
+ sal_uInt16 nSubType;
+ switch (m_pImpl->m_pProps->nSubType)
+ {
+ case text::SetVariableType::STRING: nSubType = nsSwGetSetExpType::GSE_STRING; break;
+ case text::SetVariableType::VAR: nSubType = nsSwGetSetExpType::GSE_EXPR; break;
+ //case text::SetVariableType::SEQUENCE: nSubType = nsSwGetSetExpType::GSE_SEQ; break;
+ case text::SetVariableType::FORMULA: nSubType = nsSwGetSetExpType::GSE_FORMULA; break;
+ default:
+ OSL_FAIL("wrong value");
+ nSubType = nsSwGetSetExpType::GSE_EXPR;
+ }
+ //make sure the SubType matches the field type
+ SwFieldType* pSetExpField = pDoc->getIDocumentFieldsAccess().GetFieldType(
+ SwFieldIds::SetExp, m_pImpl->m_pProps->sPar1, false);
+ bool bSetGetExpFieldUninitialized = false;
+ if (pSetExpField)
+ {
+ if (nSubType != nsSwGetSetExpType::GSE_STRING &&
+ static_cast< SwSetExpFieldType* >(pSetExpField)->GetType() == nsSwGetSetExpType::GSE_STRING)
+ nSubType = nsSwGetSetExpType::GSE_STRING;
+ }
+ else
+ bSetGetExpFieldUninitialized = true; // #i82544#
+
+ if (m_pImpl->m_pProps->bBool2)
+ nSubType |= nsSwExtendedSubType::SUB_CMD;
+ else
+ nSubType &= ~nsSwExtendedSubType::SUB_CMD;
+ SwGetExpField *const pGEField = new SwGetExpField(
+ static_cast<SwGetExpFieldType*>(
+ pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::GetExp)),
+ m_pImpl->m_pProps->sPar1, nSubType,
+ m_pImpl->m_pProps->nFormat);
+ xField.reset(pGEField);
+ //TODO: evaluate SubType!
+ if (!m_pImpl->m_pProps->sPar4.isEmpty())
+ pGEField->ChgExpStr(m_pImpl->m_pProps->sPar4, nullptr);
+ // #i82544#
+ if (bSetGetExpFieldUninitialized)
+ pGEField->SetLateInitialization();
+ }
+ break;
+ case SwServiceType::FieldTypeInputUser:
+ case SwServiceType::FieldTypeInput:
+ {
+ SwFieldType* pFieldType =
+ pDoc->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Input, m_pImpl->m_sTypeName, true);
+ if (!pFieldType)
+ throw uno::RuntimeException();
+ sal_uInt16 nInpSubType =
+ sal::static_int_cast<sal_uInt16>(
+ SwServiceType::FieldTypeInputUser == m_pImpl->m_nServiceId
+ ? INP_USR : INP_TXT);
+ SwInputField * pTextField =
+ new SwInputField(static_cast<SwInputFieldType*>(pFieldType),
+ m_pImpl->m_pProps->sPar1,
+ m_pImpl->m_pProps->sPar2,
+ nInpSubType);
+ pTextField->SetHelp(m_pImpl->m_pProps->sPar3);
+ pTextField->SetToolTip(m_pImpl->m_pProps->sPar4);
+
+ xField.reset(pTextField);
+ }
+ break;
+ case SwServiceType::FieldTypeMacro:
+ {
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Macro);
+ OUString aName;
+
+ // support for Scripting Framework macros
+ if (!m_pImpl->m_pProps->sPar4.isEmpty())
+ {
+ aName = m_pImpl->m_pProps->sPar4;
+ }
+ else
+ {
+ SwMacroField::CreateMacroString(aName,
+ m_pImpl->m_pProps->sPar1, m_pImpl->m_pProps->sPar3);
+ }
+ xField.reset(new SwMacroField(static_cast<SwMacroFieldType*>(pFieldType), aName,
+ m_pImpl->m_pProps->sPar2));
+ }
+ break;
+ case SwServiceType::FieldTypePageCount:
+ case SwServiceType::FieldTypeParagraphCount:
+ case SwServiceType::FieldTypeWordCount:
+ case SwServiceType::FieldTypeCharacterCount:
+ case SwServiceType::FieldTypeTableCount:
+ case SwServiceType::FieldTypeGraphicObjectCount:
+ case SwServiceType::FieldTypeEmbeddedObjectCount:
+ {
+ sal_uInt16 nSubType = DS_PAGE;
+ switch (m_pImpl->m_nServiceId)
+ {
+ case SwServiceType::FieldTypeParagraphCount : nSubType = DS_PARA; break;
+ case SwServiceType::FieldTypeWordCount : nSubType = DS_WORD; break;
+ case SwServiceType::FieldTypeCharacterCount : nSubType = DS_CHAR; break;
+ case SwServiceType::FieldTypeTableCount : nSubType = DS_TBL; break;
+ case SwServiceType::FieldTypeGraphicObjectCount : nSubType = DS_GRF; break;
+ case SwServiceType::FieldTypeEmbeddedObjectCount : nSubType = DS_OLE; break;
+ default: break;
+ }
+ SwFieldType* pFieldType = pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::DocStat);
+ xField.reset(new SwDocStatField(
+ static_cast<SwDocStatFieldType*>(pFieldType),
+ nSubType, m_pImpl->m_pProps->nUSHORT2));
+ }
+ break;
+ case SwServiceType::FieldTypeBibliography:
+ {
+ SwAuthorityFieldType const type(pDoc);
+ xField.reset(new SwAuthorityField(static_cast<SwAuthorityFieldType*>(
+ pDoc->getIDocumentFieldsAccess().InsertFieldType(type)),
+ u""));
+ if (m_pImpl->m_pProps->aPropSeq.hasElements())
+ {
+ uno::Any aVal;
+ aVal <<= m_pImpl->m_pProps->aPropSeq;
+ xField->PutValue( aVal, FIELD_PROP_PROP_SEQ );
+ }
+ }
+ break;
+ case SwServiceType::FieldTypeCombinedCharacters:
+ // create field
+ xField.reset(new SwCombinedCharField( static_cast<SwCombinedCharFieldType*>(
+ pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::CombinedChars)),
+ m_pImpl->m_pProps->sPar1));
+ break;
+ case SwServiceType::FieldTypeDropdown:
+ {
+ SwDropDownField *const pDDField = new SwDropDownField(
+ static_cast<SwDropDownFieldType *>(
+ pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Dropdown)));
+ xField.reset(pDDField);
+
+ pDDField->SetItems(m_pImpl->m_pProps->aStrings);
+ pDDField->SetSelectedItem(m_pImpl->m_pProps->sPar1);
+ pDDField->SetName(m_pImpl->m_pProps->sPar2);
+ pDDField->SetHelp(m_pImpl->m_pProps->sPar3);
+ pDDField->SetToolTip(m_pImpl->m_pProps->sPar4);
+ }
+ break;
+
+ case SwServiceType::FieldTypeTableFormula:
+ {
+ // create field
+ sal_uInt16 nType = nsSwGetSetExpType::GSE_FORMULA;
+ if (m_pImpl->m_pProps->bBool1)
+ {
+ nType |= nsSwExtendedSubType::SUB_CMD;
+ if (m_pImpl->m_pProps->bFormatIsDefault)
+ m_pImpl->m_pProps->nFormat = -1;
+ }
+ xField.reset(new SwTableField( static_cast<SwTableFieldType*>(
+ pDoc->getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::Table)),
+ m_pImpl->m_pProps->sPar2,
+ nType,
+ m_pImpl->m_pProps->nFormat));
+ static_cast<SwTableField*>(xField.get())->ChgExpStr(m_pImpl->m_pProps->sPar1);
+ }
+ break;
+ default: OSL_FAIL("What kind of type is that?");
+ }
+
+ if (!xField)
+ throw uno::RuntimeException("no SwField created?");
+
+ xField->SetAutomaticLanguage(!m_pImpl->m_pProps->bBool4);
+ SwFormatField aFormat(*xField);
+
+ UnoActionContext aCont(pDoc);
+ if (aPam.HasMark() &&
+ m_pImpl->m_nServiceId != SwServiceType::FieldTypeAnnotation)
+ {
+ pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
+ }
+
+ SwXTextCursor const*const pTextCursor(dynamic_cast<SwXTextCursor*>(pCursor));
+ const bool bForceExpandHints(
+ pTextCursor
+ && pTextCursor->IsAtEndOfMeta() );
+ const SetAttrMode nInsertFlags =
+ bForceExpandHints
+ ? SetAttrMode::FORCEHINTEXPAND
+ : SetAttrMode::DEFAULT;
+
+ if (*aPam.GetPoint() != *aPam.GetMark() &&
+ m_pImpl->m_nServiceId == SwServiceType::FieldTypeAnnotation)
+ {
+ // Make sure we always insert the field at the end
+ SwPaM aEnd(*aPam.End(), *aPam.End());
+ pDoc->getIDocumentContentOperations().InsertPoolItem(aEnd, aFormat, nInsertFlags);
+ }
+ else
+ pDoc->getIDocumentContentOperations().InsertPoolItem(aPam, aFormat, nInsertFlags);
+
+ SwTextAttr* pTextAttr = aPam.GetNode().GetTextNode()->GetFieldTextAttrAt( aPam.GetPoint()->nContent.GetIndex()-1, true );
+
+ // What about updating the fields? (see fldmgr.cxx)
+ if (!pTextAttr)
+ throw uno::RuntimeException("no SwTextAttr inserted?"); // could theoretically happen, if paragraph is full
+
+ m_pImpl->ClearFieldType();
+ const SwFormatField& rField = pTextAttr->GetFormatField();
+ m_pImpl->SetFormatField(const_cast<SwFormatField*>(&rField), pDoc);
+
+ if ( pTextAttr->Which() == RES_TXTATR_ANNOTATION
+ && *aPam.GetPoint() != *aPam.GetMark() )
+ {
+ // create annotation mark
+ const SwPostItField* pPostItField = dynamic_cast< const SwPostItField* >(pTextAttr->GetFormatField().GetField());
+ OSL_ENSURE( pPostItField != nullptr, "<SwXTextField::attachToRange(..)> - annotation field missing!" );
+ if ( pPostItField != nullptr )
+ {
+ IDocumentMarkAccess* pMarksAccess = pDoc->getIDocumentMarkAccess();
+ pMarksAccess->makeAnnotationMark( aPam, pPostItField->GetName() );
+ }
+ }
+
+ xField.reset();
+
+ assert(m_pImpl->GetFormatField());
+ m_pImpl->m_pDoc = pDoc;
+ m_pImpl->GetFormatField()->SetXTextField(this);
+ m_pImpl->m_wThis = *this;
+ m_pImpl->m_bIsDescriptor = false;
+ m_pImpl->m_pProps.reset();
+ if (m_pImpl->m_bCallUpdate)
+ update();
+ }
+ else if ( !m_pImpl->IsDescriptor()
+ && m_pImpl->m_pDoc != nullptr
+ && m_pImpl->m_nServiceId == SwServiceType::FieldTypeAnnotation )
+ {
+ SwDoc* pDoc = m_pImpl->m_pDoc;
+ SwUnoInternalPaM aIntPam( *pDoc );
+ if ( !::sw::XTextRangeToSwPaM( aIntPam, xTextRange ) )
+ throw lang::IllegalArgumentException();
+
+ // Nothing to do, if the text range has a separate start and end, but they have the same
+ // value.
+ if (!aIntPam.HasMark() || *aIntPam.Start() != *aIntPam.End())
+ {
+ UnoActionContext aCont( pDoc );
+ // insert copy of annotation at new text range
+ std::unique_ptr<SwPostItField> pPostItField(static_cast< SwPostItField* >(m_pImpl->GetFormatField()->GetField()->CopyField().release()));
+ SwFormatField aFormatField( *pPostItField );
+ pPostItField.reset();
+ SwPaM aEnd( *aIntPam.End(), *aIntPam.End() );
+ pDoc->getIDocumentContentOperations().InsertPoolItem( aEnd, aFormatField );
+ // delete former annotation
+ {
+ const SwTextField* pTextField = m_pImpl->GetFormatField()->GetTextField();
+ SwTextNode& rTextNode = *pTextField->GetpTextNode();
+ SwPaM aPam( rTextNode, pTextField->GetStart() );
+ aPam.SetMark();
+ aPam.Move();
+ pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
+ }
+ // keep inserted annotation
+ {
+ SwTextField* pTextAttr = aEnd.GetNode().GetTextNode()->GetFieldTextAttrAt( aEnd.End()->nContent.GetIndex()-1, true );
+ if ( pTextAttr != nullptr )
+ {
+ m_pImpl->SetFormatField(const_cast<SwFormatField*>(&pTextAttr->GetFormatField()), pDoc);
+
+ if ( *aIntPam.GetPoint() != *aIntPam.GetMark() )
+ {
+ // create annotation mark
+ const SwPostItField* pField = dynamic_cast< const SwPostItField* >(pTextAttr->GetFormatField().GetField());
+ OSL_ENSURE( pField != nullptr, "<SwXTextField::attach(..)> - annotation field missing!" );
+ if ( pField != nullptr )
+ {
+ IDocumentMarkAccess* pMarksAccess = aIntPam.GetDoc().getIDocumentMarkAccess();
+ pMarksAccess->makeAnnotationMark( aIntPam, pField->GetName() );
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else
+ throw lang::IllegalArgumentException();
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextField::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ SwField const*const pField = m_pImpl->GetField();
+ if (!pField)
+ return nullptr;
+
+ const SwTextField* pTextField = m_pImpl->GetFormatField()->GetTextField();
+ if (!pTextField)
+ throw uno::RuntimeException();
+
+ std::shared_ptr< SwPaM > pPamForTextField;
+ SwTextField::GetPamForTextField(*pTextField, pPamForTextField);
+ if (pPamForTextField == nullptr)
+ return nullptr;
+
+ // If this is a postit field, then return the range of its annotation mark if it has one.
+ if (pField->Which() == SwFieldIds::Postit)
+ {
+ const SwPostItField* pPostItField = static_cast<const SwPostItField*>(pField);
+ IDocumentMarkAccess* pMarkAccess = m_pImpl->m_pDoc->getIDocumentMarkAccess();
+ for (IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAnnotationMarksBegin(); ppMark != pMarkAccess->getAnnotationMarksEnd(); ++ppMark)
+ {
+ if ((*ppMark)->GetName() == pPostItField->GetName())
+ {
+ pPamForTextField = std::make_shared<SwPaM>((*ppMark)->GetMarkStart(), (*ppMark)->GetMarkEnd());
+ break;
+ }
+ }
+ }
+
+ uno::Reference<text::XTextRange> xRange = SwXTextRange::CreateXTextRange(
+ *m_pImpl->m_pDoc, *(pPamForTextField->GetPoint()), pPamForTextField->GetMark());
+ return xRange;
+}
+
+void SAL_CALL SwXTextField::dispose()
+{
+ SolarMutexGuard aGuard;
+ SwField const*const pField = m_pImpl->GetField();
+ if(pField && m_pImpl->m_pDoc)
+ {
+ UnoActionContext aContext(m_pImpl->m_pDoc);
+ assert(m_pImpl->GetFormatField()->GetTextField() && "<SwXTextField::dispose()> - missing <SwTextField> --> crash");
+ SwTextField::DeleteTextField(*(m_pImpl->GetFormatField()->GetTextField()));
+ }
+
+ if (m_pImpl->m_xTextObject.is())
+ {
+ m_pImpl->m_xTextObject->DisposeEditSource();
+ m_pImpl->m_xTextObject.clear();
+ }
+ m_pImpl->Invalidate();
+}
+
+void SAL_CALL SwXTextField::addEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXTextField::removeEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXTextField::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ // no static
+ uno::Reference< beans::XPropertySetInfo > aRef;
+ if (m_pImpl->m_nServiceId == SwServiceType::Invalid)
+ {
+ throw uno::RuntimeException();
+ }
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(
+ lcl_GetPropertyMapOfService(m_pImpl->m_nServiceId));
+ const uno::Reference<beans::XPropertySetInfo>& xInfo = pPropSet->getPropertySetInfo();
+ // extend PropertySetInfo!
+ const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
+ aRef = new SfxExtItemPropertySetInfo(
+ aSwMapProvider.GetPropertyMapEntries(PROPERTY_MAP_PARAGRAPH_EXTENSIONS),
+ aPropSeq );
+ return aRef;
+}
+
+void SAL_CALL
+SwXTextField::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+ SwField const*const pField = m_pImpl->GetField();
+ const SfxItemPropertySet* _pPropSet = aSwMapProvider.GetPropertySet(
+ lcl_GetPropertyMapOfService(m_pImpl->m_nServiceId));
+ const SfxItemPropertyMapEntry* pEntry = _pPropSet->getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException( "Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if(pField)
+ {
+ // special treatment for mail merge fields
+ const SwFieldIds nWhich = pField->Which();
+ if( SwFieldIds::Database == nWhich &&
+ (rPropertyName == UNO_NAME_DATA_BASE_NAME ||
+ rPropertyName == UNO_NAME_DATA_BASE_URL||
+ rPropertyName == UNO_NAME_DATA_TABLE_NAME||
+ rPropertyName == UNO_NAME_DATA_COLUMN_NAME))
+ {
+ // here a new field type must be created and the field must
+ // be registered at the new type
+ OSL_FAIL("not implemented");
+ }
+ else
+ {
+ SwDoc * pDoc = m_pImpl->m_pDoc;
+ assert(pDoc);
+ const SwTextField* pTextField = m_pImpl->GetFormatField()->GetTextField();
+ if(!pTextField)
+ throw uno::RuntimeException();
+ SwPosition aPosition( pTextField->GetTextNode() );
+ aPosition.nContent = pTextField->GetStart();
+ pDoc->getIDocumentFieldsAccess().PutValueToField( aPosition, rValue, pEntry->nWID);
+ }
+
+ //#i100374# notify SwPostIt about new field content
+ assert(m_pImpl->GetFormatField());
+ if (SwFieldIds::Postit == nWhich)
+ {
+ m_pImpl->GetFormatField()->Broadcast(
+ SwFormatFieldHint( nullptr, SwFormatFieldHintWhich::CHANGED ));
+ }
+
+ // fdo#42073 notify SwTextField about changes of the expanded string
+ if (m_pImpl->GetFormatField()->GetTextField())
+ {
+ m_pImpl->GetFormatField()->GetTextField()->ExpandTextField();
+ }
+
+ //#i100374# changing a document field should set the modify flag
+ SwDoc* pDoc = m_pImpl->m_pDoc;
+ if (pDoc)
+ pDoc->getIDocumentState().SetModified();
+
+ }
+ else if (m_pImpl->m_pProps)
+ {
+ bool* pBool = nullptr;
+ switch(pEntry->nWID)
+ {
+ case FIELD_PROP_PAR1:
+ rValue >>= m_pImpl->m_pProps->sPar1;
+ break;
+ case FIELD_PROP_PAR2:
+ rValue >>= m_pImpl->m_pProps->sPar2;
+ break;
+ case FIELD_PROP_PAR3:
+ rValue >>= m_pImpl->m_pProps->sPar3;
+ break;
+ case FIELD_PROP_PAR4:
+ rValue >>= m_pImpl->m_pProps->sPar4;
+ break;
+ case FIELD_PROP_FORMAT:
+ rValue >>= m_pImpl->m_pProps->nFormat;
+ m_pImpl->m_pProps->bFormatIsDefault = false;
+ break;
+ case FIELD_PROP_SUBTYPE:
+ m_pImpl->m_pProps->nSubType = SWUnoHelper::GetEnumAsInt32(rValue);
+ break;
+ case FIELD_PROP_BYTE1 :
+ rValue >>= m_pImpl->m_pProps->nByte1;
+ break;
+ case FIELD_PROP_BOOL1 :
+ pBool = &m_pImpl->m_pProps->bBool1;
+ break;
+ case FIELD_PROP_BOOL2 :
+ pBool = &m_pImpl->m_pProps->bBool2;
+ break;
+ case FIELD_PROP_BOOL3 :
+ pBool = &m_pImpl->m_pProps->bBool3;
+ break;
+ case FIELD_PROP_BOOL4:
+ pBool = &m_pImpl->m_pProps->bBool4;
+ break;
+ case FIELD_PROP_DATE :
+ {
+ auto aTemp = o3tl::tryAccess<util::Date>(rValue);
+ if(!aTemp)
+ throw lang::IllegalArgumentException();
+
+ m_pImpl->m_pProps->aDate = Date(aTemp->Day, aTemp->Month, aTemp->Year);
+ }
+ break;
+ case FIELD_PROP_USHORT1:
+ case FIELD_PROP_USHORT2:
+ {
+ sal_Int16 nVal = 0;
+ rValue >>= nVal;
+ if( FIELD_PROP_USHORT1 == pEntry->nWID)
+ m_pImpl->m_pProps->nUSHORT1 = nVal;
+ else
+ m_pImpl->m_pProps->nUSHORT2 = nVal;
+ }
+ break;
+ case FIELD_PROP_SHORT1:
+ rValue >>= m_pImpl->m_pProps->nSHORT1;
+ break;
+ case FIELD_PROP_DOUBLE:
+ if(rValue.getValueType() != ::cppu::UnoType<double>::get())
+ throw lang::IllegalArgumentException();
+ rValue >>= m_pImpl->m_pProps->fDouble;
+ break;
+
+ case FIELD_PROP_DATE_TIME :
+ if (!m_pImpl->m_pProps->pDateTime)
+ m_pImpl->m_pProps->pDateTime.reset( new util::DateTime );
+ rValue >>= *m_pImpl->m_pProps->pDateTime;
+ break;
+ case FIELD_PROP_PROP_SEQ:
+ rValue >>= m_pImpl->m_pProps->aPropSeq;
+ break;
+ case FIELD_PROP_STRINGS:
+ rValue >>= m_pImpl->m_pProps->aStrings;
+ break;
+ }
+ if (pBool)
+ {
+ auto b = o3tl::tryAccess<bool>(rValue);
+ if( !b )
+ throw lang::IllegalArgumentException();
+ *pBool = *b;
+
+ }
+ }
+ else
+ throw uno::RuntimeException();
+}
+
+uno::Any SAL_CALL SwXTextField::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwField const*const pField = m_pImpl->GetField();
+ const SfxItemPropertySet* _pPropSet = aSwMapProvider.GetPropertySet(
+ lcl_GetPropertyMapOfService(m_pImpl->m_nServiceId));
+ const SfxItemPropertyMapEntry* pEntry = _pPropSet->getPropertyMap().getByName(rPropertyName);
+ if(!pEntry )
+ {
+ const SfxItemPropertySet* _pParaPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH_EXTENSIONS);
+ pEntry = _pParaPropSet->getPropertyMap().getByName(rPropertyName);
+ }
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ switch( pEntry->nWID )
+ {
+ case FN_UNO_TEXT_WRAP:
+ aRet <<= text::WrapTextMode_NONE;
+ break;
+ case FN_UNO_ANCHOR_TYPE:
+ aRet <<= text::TextContentAnchorType_AS_CHARACTER;
+ break;
+ case FN_UNO_ANCHOR_TYPES:
+ {
+ uno::Sequence<text::TextContentAnchorType> aTypes { text::TextContentAnchorType_AS_CHARACTER };
+ aRet <<= aTypes;
+ }
+ break;
+
+ default:
+ if( pField )
+ {
+ if (FIELD_PROP_IS_FIELD_USED == pEntry->nWID ||
+ FIELD_PROP_IS_FIELD_DISPLAYED == pEntry->nWID)
+ {
+ bool bIsFieldUsed = false;
+ bool bIsFieldDisplayed = false;
+
+ // in order to have the information about fields
+ // correctly evaluated the document needs a layout
+ // (has to be already formatted)
+ SwDoc *pDoc = m_pImpl->m_pDoc;
+ SwViewShell *pViewShell = nullptr;
+ SwEditShell *pEditShell = nullptr;
+ if( pDoc )
+ {
+ pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ pEditShell = pDoc->GetEditShell();
+ }
+
+ if (pEditShell)
+ pEditShell->CalcLayout();
+ else if (pViewShell) // a page preview has no SwEditShell it should only have a view shell
+ pViewShell->CalcLayout();
+ else
+ throw uno::RuntimeException();
+
+ // get text node for the text field
+ const SwFormatField *pFieldFormat =
+ (m_pImpl->GetField()) ? m_pImpl->GetFormatField() : nullptr;
+ const SwTextField* pTextField = pFieldFormat
+ ? m_pImpl->GetFormatField()->GetTextField() : nullptr;
+ if(!pTextField)
+ throw uno::RuntimeException();
+ const SwTextNode& rTextNode = pTextField->GetTextNode();
+
+ // skip fields that are currently not in the document
+ // e.g. fields in undo or redo array
+ if (rTextNode.GetNodes().IsDocNodes())
+ {
+ bool bFrame = 0 != rTextNode.FindLayoutRect().Width(); // or so
+ bool bHidden = rTextNode.IsHidden();
+ if ( !bHidden )
+ {
+ sal_Int32 nHiddenStart;
+ sal_Int32 nHiddenEnd;
+ bHidden = SwScriptInfo::GetBoundsOfHiddenRange( pTextField->GetTextNode(),
+ pTextField->GetStart(),
+ nHiddenStart, nHiddenEnd );
+ }
+
+ // !bFrame && !bHidden: most probably a field in an unused page style
+
+ // FME: Problem: hidden field in unused page template =>
+ // bIsFieldUsed = true
+ // bIsFieldDisplayed = false
+ bIsFieldUsed = bFrame || bHidden;
+ bIsFieldDisplayed = bIsFieldUsed && !bHidden;
+ }
+ aRet <<= (FIELD_PROP_IS_FIELD_USED == pEntry->nWID) ? bIsFieldUsed : bIsFieldDisplayed;
+ }
+ else
+ pField->QueryValue( aRet, pEntry->nWID );
+ }
+ else if (m_pImpl->m_pProps) // currently just a descriptor...
+ {
+ switch(pEntry->nWID)
+ {
+ case FIELD_PROP_TEXT:
+ {
+ if (!m_pImpl->m_xTextObject.is())
+ {
+ m_pImpl->m_xTextObject
+ = new SwTextAPIObject( std::make_unique<SwTextAPIEditSource>(m_pImpl->m_pDoc) );
+ }
+
+ uno::Reference<text::XText> xText(m_pImpl->m_xTextObject);
+ aRet <<= xText;
+ break;
+ }
+ case FIELD_PROP_PAR1:
+ aRet <<= m_pImpl->m_pProps->sPar1;
+ break;
+ case FIELD_PROP_PAR2:
+ aRet <<= m_pImpl->m_pProps->sPar2;
+ break;
+ case FIELD_PROP_PAR3:
+ aRet <<= m_pImpl->m_pProps->sPar3;
+ break;
+ case FIELD_PROP_PAR4:
+ aRet <<= m_pImpl->m_pProps->sPar4;
+ break;
+ case FIELD_PROP_FORMAT:
+ aRet <<= m_pImpl->m_pProps->nFormat;
+ break;
+ case FIELD_PROP_SUBTYPE:
+ aRet <<= m_pImpl->m_pProps->nSubType;
+ break;
+ case FIELD_PROP_BYTE1 :
+ aRet <<= m_pImpl->m_pProps->nByte1;
+ break;
+ case FIELD_PROP_BOOL1 :
+ aRet <<= m_pImpl->m_pProps->bBool1;
+ break;
+ case FIELD_PROP_BOOL2 :
+ aRet <<= m_pImpl->m_pProps->bBool2;
+ break;
+ case FIELD_PROP_BOOL3 :
+ aRet <<= m_pImpl->m_pProps->bBool3;
+ break;
+ case FIELD_PROP_BOOL4 :
+ aRet <<= m_pImpl->m_pProps->bBool4;
+ break;
+ case FIELD_PROP_DATE :
+ aRet <<= m_pImpl->m_pProps->aDate.GetUNODate();
+ break;
+ case FIELD_PROP_USHORT1:
+ aRet <<= static_cast<sal_Int16>(m_pImpl->m_pProps->nUSHORT1);
+ break;
+ case FIELD_PROP_USHORT2:
+ aRet <<= static_cast<sal_Int16>(m_pImpl->m_pProps->nUSHORT2);
+ break;
+ case FIELD_PROP_SHORT1:
+ aRet <<= m_pImpl->m_pProps->nSHORT1;
+ break;
+ case FIELD_PROP_DOUBLE:
+ aRet <<= m_pImpl->m_pProps->fDouble;
+ break;
+ case FIELD_PROP_DATE_TIME :
+ if (m_pImpl->m_pProps->pDateTime)
+ aRet <<= *m_pImpl->m_pProps->pDateTime;
+ break;
+ case FIELD_PROP_PROP_SEQ:
+ aRet <<= m_pImpl->m_pProps->aPropSeq;
+ break;
+ case FIELD_PROP_STRINGS:
+ aRet <<= m_pImpl->m_pProps->aStrings;
+ break;
+ case FIELD_PROP_IS_FIELD_USED:
+ case FIELD_PROP_IS_FIELD_DISPLAYED:
+ aRet <<= false;
+ break;
+ }
+ }
+ else
+ throw uno::RuntimeException();
+ }
+ return aRet;
+}
+
+void SwXTextField::addPropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextField::removePropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextField::addVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextField::removeVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SAL_CALL SwXTextField::update()
+{
+ SolarMutexGuard aGuard;
+ SwField * pField = const_cast<SwField*>(m_pImpl->GetField());
+ if (pField)
+ {
+ switch(pField->Which())
+ {
+ case SwFieldIds::DateTime:
+ static_cast<SwDateTimeField*>(pField)->SetDateTime( ::DateTime( ::DateTime::SYSTEM ) );
+ break;
+
+ case SwFieldIds::ExtUser:
+ {
+ SwExtUserField* pExtUserField = static_cast<SwExtUserField*>(pField);
+ pExtUserField->SetExpansion( SwExtUserFieldType::Expand(
+ pExtUserField->GetSubType() ) );
+ }
+ break;
+
+ case SwFieldIds::Author:
+ {
+ SwAuthorField* pAuthorField = static_cast<SwAuthorField*>(pField);
+ pAuthorField->SetExpansion( SwAuthorFieldType::Expand(
+ pAuthorField->GetFormat() ) );
+ }
+ break;
+
+ case SwFieldIds::Filename:
+ {
+ SwFileNameField* pFileNameField = static_cast<SwFileNameField*>(pField);
+ pFileNameField->SetExpansion( static_cast<SwFileNameFieldType*>(pField->GetTyp())->Expand(
+ pFileNameField->GetFormat() ) );
+ }
+ break;
+
+ case SwFieldIds::DocInfo:
+ {
+ SwDocInfoField* pDocInfField = static_cast<SwDocInfoField*>(pField);
+ pDocInfField->SetExpansion( static_cast<SwDocInfoFieldType*>(pField->GetTyp())->Expand(
+ pDocInfField->GetSubType(),
+ pDocInfField->GetFormat(),
+ pDocInfField->GetLanguage(),
+ pDocInfField->GetName() ) );
+ }
+ break;
+ default: break;
+ }
+ // Text formatting has to be triggered.
+ m_pImpl->GetFormatField()->UpdateTextNode(nullptr, nullptr);
+ }
+ else
+ m_pImpl->m_bCallUpdate = true;
+}
+
+OUString SAL_CALL SwXTextField::getImplementationName()
+{
+ return "SwXTextField";
+}
+
+static OUString OldNameToNewName_Impl( const OUString &rOld )
+{
+ static const char aOldNamePart1[] = ".TextField.DocInfo.";
+ static const char aOldNamePart2[] = ".TextField.";
+ OUString sServiceNameCC( rOld );
+ sal_Int32 nIdx = sServiceNameCC.indexOf( aOldNamePart1 );
+ if (nIdx >= 0)
+ sServiceNameCC = sServiceNameCC.replaceAt( nIdx, strlen(aOldNamePart1), u".textfield.docinfo." );
+ nIdx = sServiceNameCC.indexOf( aOldNamePart2 );
+ if (nIdx >= 0)
+ sServiceNameCC = sServiceNameCC.replaceAt( nIdx, strlen(aOldNamePart2), u".textfield." );
+ return sServiceNameCC;
+}
+
+sal_Bool SAL_CALL SwXTextField::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SwXTextField::getSupportedServiceNames()
+{
+ const OUString sServiceName =
+ SwXServiceProvider::GetProviderName(m_pImpl->m_nServiceId);
+
+ // case-corrected version of service-name (see #i67811)
+ // (need to supply both because of compatibility to older versions)
+ const OUString sServiceNameCC( OldNameToNewName_Impl( sServiceName ) );
+ sal_Int32 nLen = sServiceName == sServiceNameCC ? 2 : 3;
+
+ uno::Sequence< OUString > aRet( nLen );
+ OUString* pArray = aRet.getArray();
+ *pArray++ = sServiceName;
+ if (nLen == 3)
+ *pArray++ = sServiceNameCC;
+ *pArray++ = "com.sun.star.text.TextContent";
+ return aRet;
+}
+
+void SwXTextField::Impl::Invalidate()
+{
+ EndListeningAll();
+ m_pFormatField = nullptr;
+ m_pDoc = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+}
+
+void SwXTextField::Impl::Notify(const SfxHint& rHint)
+{
+
+ if(rHint.GetId() == SfxHintId::Dying)
+ Invalidate();
+ else if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ switch(pLegacyHint->m_pOld ? pLegacyHint->m_pOld->Which() : 0)
+ {
+ case RES_REMOVE_UNO_OBJECT:
+ case RES_OBJECTDYING:
+ Invalidate();
+ break;
+ }
+ }
+}
+
+const SwField* SwXTextField::Impl::GetField() const
+{
+ return m_pFormatField ? m_pFormatField->GetField() : nullptr;
+}
+
+OUString SwXTextFieldMasters::getImplementationName()
+{
+ return "SwXTextFieldMasters";
+}
+
+sal_Bool SwXTextFieldMasters::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextFieldMasters::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aRet { "com.sun.star.text.TextFieldMasters" };
+ return aRet;
+}
+
+SwXTextFieldMasters::SwXTextFieldMasters(SwDoc* _pDoc) :
+ SwUnoCollection(_pDoc)
+{
+}
+
+SwXTextFieldMasters::~SwXTextFieldMasters()
+{
+
+}
+
+/*
+ Iteration over non-standard field types
+ USER/SETEXP/DDE/DATABASE
+ Thus the names are:
+ "com.sun.star.text.fieldmaster.User" + <field type name>
+ "com.sun.star.text.fieldmaster.DDE" + <field type name>
+ "com.sun.star.text.fieldmaster.SetExpression" + <field type name>
+ "com.sun.star.text.fieldmaster.DataBase" + <field type name>
+
+ If too much, maybe one could leave out the "com.sun.star.text".
+ */
+static SwFieldIds lcl_GetIdByName( OUString& rName, OUString& rTypeName )
+{
+ if (rName.startsWithIgnoreAsciiCase(COM_TEXT_FLDMASTER_CC))
+ rName = rName.copy(COM_TEXT_FLDMASTER_CC.getLength());
+
+ SwFieldIds nResId = SwFieldIds::Unknown;
+ sal_Int32 nIdx = 0;
+ rTypeName = rName.getToken( 0, '.', nIdx );
+ if (rTypeName == "User")
+ nResId = SwFieldIds::User;
+ else if (rTypeName == "DDE")
+ nResId = SwFieldIds::Dde;
+ else if (rTypeName == "SetExpression")
+ {
+ nResId = SwFieldIds::SetExp;
+
+ const OUString sFieldTypName( rName.getToken( 0, '.', nIdx ));
+ const OUString sUIName( SwStyleNameMapper::GetSpecialExtraUIName( sFieldTypName ) );
+
+ if( sUIName != sFieldTypName )
+ rName = comphelper::string::setToken(rName, 1, '.', sUIName);
+ }
+ else if (rTypeName.equalsIgnoreAsciiCase("DataBase"))
+ {
+ rName = rName.copy(RTL_CONSTASCII_LENGTH("DataBase."));
+ if (!rName.isEmpty())
+ {
+ // #i51815#
+ rName = "DataBase." + rName;
+ nResId = SwFieldIds::Database;
+ }
+ }
+ else if (rTypeName == "Bibliography")
+ nResId = SwFieldIds::TableOfAuthorities;
+ return nResId;
+}
+
+uno::Any SwXTextFieldMasters::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!GetDoc())
+ throw uno::RuntimeException();
+
+ OUString sName(rName), sTypeName;
+ const SwFieldIds nResId = lcl_GetIdByName( sName, sTypeName );
+ if( SwFieldIds::Unknown == nResId )
+ throw container::NoSuchElementException(
+ "SwXTextFieldMasters::getByName(" + rName + ")",
+ css::uno::Reference<css::uno::XInterface>());
+
+ sName = sName.copy(std::min(sTypeName.getLength()+1, sName.getLength()));
+ SwFieldType* pType = GetDoc()->getIDocumentFieldsAccess().GetFieldType(nResId, sName, true);
+ if(!pType)
+ throw container::NoSuchElementException(
+ "SwXTextFieldMasters::getByName(" + rName + ")",
+ css::uno::Reference<css::uno::XInterface>());
+
+ uno::Reference<beans::XPropertySet> const xRet(
+ SwXFieldMaster::CreateXFieldMaster(GetDoc(), pType));
+ return uno::Any(xRet);
+}
+
+bool SwXTextFieldMasters::getInstanceName(
+ const SwFieldType& rFieldType, OUString& rName)
+{
+ OUString sField;
+
+ switch( rFieldType.Which() )
+ {
+ case SwFieldIds::User:
+ sField = "User." + rFieldType.GetName();
+ break;
+ case SwFieldIds::Dde:
+ sField = "DDE." + rFieldType.GetName();
+ break;
+
+ case SwFieldIds::SetExp:
+ sField = "SetExpression." + SwStyleNameMapper::GetSpecialExtraProgName( rFieldType.GetName() );
+ break;
+
+ case SwFieldIds::Database:
+ sField = "DataBase." + rFieldType.GetName().replaceAll(OUStringChar(DB_DELIM), ".");
+ break;
+
+ case SwFieldIds::TableOfAuthorities:
+ sField = "Bibliography";
+ break;
+
+ default:
+ return false;
+ }
+
+ rName += COM_TEXT_FLDMASTER_CC + sField;
+ return true;
+}
+
+uno::Sequence< OUString > SwXTextFieldMasters::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if(!GetDoc())
+ throw uno::RuntimeException();
+
+ const SwFieldTypes* pFieldTypes = GetDoc()->getIDocumentFieldsAccess().GetFieldTypes();
+ const size_t nCount = pFieldTypes->size();
+
+ std::vector<OUString> aFieldNames;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwFieldType& rFieldType = *((*pFieldTypes)[i]);
+
+ OUString sFieldName;
+ if (SwXTextFieldMasters::getInstanceName(rFieldType, sFieldName))
+ {
+ aFieldNames.push_back(sFieldName);
+ }
+ }
+
+ return comphelper::containerToSequence(aFieldNames);
+}
+
+sal_Bool SwXTextFieldMasters::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!GetDoc())
+ throw uno::RuntimeException();
+
+ OUString sName(rName), sTypeName;
+ const SwFieldIds nResId = lcl_GetIdByName( sName, sTypeName );
+ bool bRet = false;
+ if( SwFieldIds::Unknown != nResId )
+ {
+ sName = sName.copy(std::min(sTypeName.getLength()+1, sName.getLength()));
+ bRet = nullptr != GetDoc()->getIDocumentFieldsAccess().GetFieldType(nResId, sName, true);
+ }
+ return bRet;
+}
+
+uno::Type SwXTextFieldMasters::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+
+}
+
+sal_Bool SwXTextFieldMasters::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return true;
+}
+
+class SwXTextFieldTypes::Impl
+{
+public:
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper3
+ ::comphelper::OInterfaceContainerHelper4<util::XRefreshListener> m_RefreshListeners;
+};
+
+OUString SwXTextFieldTypes::getImplementationName()
+{
+ return "SwXTextFieldTypes";
+}
+
+sal_Bool SwXTextFieldTypes::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextFieldTypes::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aRet { "com.sun.star.text.TextFields" };
+ return aRet;
+}
+
+SwXTextFieldTypes::SwXTextFieldTypes(SwDoc* _pDoc)
+ : SwUnoCollection (_pDoc)
+ , m_pImpl(new Impl)
+{
+}
+
+SwXTextFieldTypes::~SwXTextFieldTypes()
+{
+}
+
+void SwXTextFieldTypes::Invalidate()
+{
+ SwUnoCollection::Invalidate();
+ lang::EventObject const ev(static_cast< ::cppu::OWeakObject&>(*this));
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_RefreshListeners.disposeAndClear(aGuard, ev);
+}
+
+uno::Reference< container::XEnumeration > SwXTextFieldTypes::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return new SwXFieldEnumeration(*GetDoc());
+}
+
+uno::Type SwXTextFieldTypes::getElementType()
+{
+ return cppu::UnoType<text::XDependentTextField>::get();
+}
+
+sal_Bool SwXTextFieldTypes::hasElements()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return true; // they always exist
+}
+
+void SAL_CALL SwXTextFieldTypes::refresh()
+{
+ {
+ SolarMutexGuard aGuard;
+ if (!IsValid())
+ throw uno::RuntimeException();
+ UnoActionContext aContext(GetDoc());
+ GetDoc()->getIDocumentStatistics().UpdateDocStat( false, true );
+ GetDoc()->getIDocumentFieldsAccess().UpdateFields(false);
+ }
+ // call refresh listeners (without SolarMutex locked)
+ lang::EventObject const event(static_cast< ::cppu::OWeakObject*>(this));
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_RefreshListeners.notifyEach(aGuard,
+ & util::XRefreshListener::refreshed, event);
+}
+
+void SAL_CALL SwXTextFieldTypes::addRefreshListener(
+ const uno::Reference<util::XRefreshListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_RefreshListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXTextFieldTypes::removeRefreshListener(
+ const uno::Reference<util::XRefreshListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_RefreshListeners.removeInterface(aGuard, xListener);
+}
+
+class SwXFieldEnumeration::Impl
+ : public SvtListener
+{
+public:
+ SwDoc* m_pDoc;
+ std::vector<uno::Reference<text::XTextField>> m_Items;
+ sal_Int32 m_nNextIndex; ///< index of next element to be returned
+
+ explicit Impl(SwDoc& rDoc)
+ : m_pDoc(&rDoc)
+ , m_nNextIndex(0)
+ {
+ StartListening(rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+ }
+
+ virtual void Notify(const SfxHint& rHint) override
+ {
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pDoc = nullptr;
+ }
+};
+
+OUString SAL_CALL
+SwXFieldEnumeration::getImplementationName()
+{
+ return "SwXFieldEnumeration";
+}
+
+sal_Bool SAL_CALL SwXFieldEnumeration::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL
+SwXFieldEnumeration::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.FieldEnumeration" };
+}
+
+SwXFieldEnumeration::SwXFieldEnumeration(SwDoc & rDoc)
+ : m_pImpl(new Impl(rDoc))
+{
+ // build sequence
+ m_pImpl->m_Items.clear();
+
+ const SwFieldTypes* pFieldTypes = m_pImpl->m_pDoc->getIDocumentFieldsAccess().GetFieldTypes();
+ const size_t nCount = pFieldTypes->size();
+ for(size_t nType = 0; nType < nCount; ++nType)
+ {
+ const SwFieldType* pCurType = (*pFieldTypes)[nType].get();
+ std::vector<SwFormatField*> vFormatFields;
+ pCurType->GatherFields(vFormatFields);
+ std::for_each(vFormatFields.begin(), vFormatFields.end(),
+ [this](SwFormatField* pF) { m_pImpl->m_Items.push_back(SwXTextField::CreateXTextField(m_pImpl->m_pDoc, pF)); });
+ }
+ // now handle meta-fields, which are not SwFields
+ const std::vector< uno::Reference<text::XTextField> > MetaFields(
+ m_pImpl->m_pDoc->GetMetaFieldManager().getMetaFields() );
+ for (const auto & rMetaField : MetaFields)
+ {
+ m_pImpl->m_Items.push_back( rMetaField );
+ }
+ // also add fieldmarks
+ IDocumentMarkAccess& rMarksAccess(*rDoc.getIDocumentMarkAccess());
+ for (auto iter = rMarksAccess.getFieldmarksBegin(); iter != rMarksAccess.getFieldmarksEnd(); ++iter)
+ {
+ m_pImpl->m_Items.emplace_back(SwXFieldmark::CreateXFieldmark(rDoc, *iter), uno::UNO_QUERY);
+ }
+}
+
+SwXFieldEnumeration::~SwXFieldEnumeration()
+{
+}
+
+sal_Bool SAL_CALL SwXFieldEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+
+ return m_pImpl->m_nNextIndex < static_cast<sal_Int32>(m_pImpl->m_Items.size());
+}
+
+uno::Any SAL_CALL SwXFieldEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pImpl->m_nNextIndex >= static_cast<sal_Int32>(m_pImpl->m_Items.size()))
+ throw container::NoSuchElementException(
+ "SwXFieldEnumeration::nextElement",
+ css::uno::Reference<css::uno::XInterface>());
+
+ uno::Reference< text::XTextField > &rxField =
+ m_pImpl->m_Items[ m_pImpl->m_nNextIndex++ ];
+ uno::Any aRet;
+ aRet <<= rxField;
+ rxField = nullptr; // free memory for item that is no longer used
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoflatpara.cxx b/sw/source/core/unocore/unoflatpara.cxx
new file mode 100644
index 000000000..5d628d680
--- /dev/null
+++ b/sw/source/core/unocore/unoflatpara.cxx
@@ -0,0 +1,588 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unobaseclass.hxx>
+#include <unocrsrhelper.hxx>
+#include <unoflatpara.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <unotextmarkup.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <viewsh.hxx>
+#include <viewimp.hxx>
+#include <breakit.hxx>
+#include <pam.hxx>
+#include <unotextrange.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <IGrammarContact.hxx>
+#include <viewopt.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+using namespace ::com::sun::star;
+
+namespace SwUnoCursorHelper {
+
+uno::Reference<text::XFlatParagraphIterator>
+CreateFlatParagraphIterator(SwDoc & rDoc, sal_Int32 const nTextMarkupType,
+ bool const bAutomatic)
+{
+ return new SwXFlatParagraphIterator(rDoc, nTextMarkupType, bAutomatic);
+}
+
+}
+
+SwXFlatParagraph::SwXFlatParagraph( SwTextNode& rTextNode, const OUString& aExpandText, const ModelToViewHelper& rMap )
+ : SwXFlatParagraph_Base(& rTextNode, rMap)
+ , maExpandText(aExpandText)
+{
+}
+
+SwXFlatParagraph::~SwXFlatParagraph()
+{
+}
+
+
+// XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXFlatParagraph::getPropertySetInfo()
+{
+ static const comphelper::PropertyMapEntry s_Entries[] = {
+ { OUString("FieldPositions"), -1, ::cppu::UnoType<uno::Sequence<sal_Int32>>::get(), beans::PropertyAttribute::READONLY, 0 },
+ { OUString("FootnotePositions"), -1, ::cppu::UnoType<uno::Sequence<sal_Int32>>::get(), beans::PropertyAttribute::READONLY, 0 },
+ };
+ return new comphelper::PropertySetInfo(s_Entries);
+}
+
+void SAL_CALL
+SwXFlatParagraph::setPropertyValue(const OUString&, const uno::Any&)
+{
+ throw lang::IllegalArgumentException("no values can be set",
+ static_cast< ::cppu::OWeakObject*>(this), 0);
+}
+
+uno::Any SAL_CALL
+SwXFlatParagraph::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard g;
+
+ if (rPropertyName == "FieldPositions")
+ {
+ return uno::Any( comphelper::containerToSequence( GetConversionMap().getFieldPositions() ) );
+ }
+ else if (rPropertyName == "FootnotePositions")
+ {
+ return uno::Any( comphelper::containerToSequence( GetConversionMap().getFootnotePositions() ) );
+ }
+ return uno::Any();
+}
+
+void SAL_CALL
+SwXFlatParagraph::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ SAL_WARN("sw.uno",
+ "SwXFlatParagraph::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXFlatParagraph::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ SAL_WARN("sw.uno",
+ "SwXFlatParagraph::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXFlatParagraph::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ SAL_WARN("sw.uno",
+ "SwXFlatParagraph::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXFlatParagraph::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ SAL_WARN("sw.uno",
+ "SwXFlatParagraph::removeVetoableChangeListener(): not implemented");
+}
+
+
+css::uno::Reference< css::container::XStringKeyMap > SAL_CALL SwXFlatParagraph::getMarkupInfoContainer()
+{
+ return SwXTextMarkup::getMarkupInfoContainer();
+}
+
+void SAL_CALL SwXFlatParagraph::commitTextRangeMarkup(::sal_Int32 nType, const OUString & aIdentifier, const uno::Reference< text::XTextRange> & xRange,
+ const css::uno::Reference< css::container::XStringKeyMap > & xMarkupInfoContainer)
+{
+ SolarMutexGuard aGuard;
+ SwXTextMarkup::commitTextRangeMarkup( nType, aIdentifier, xRange, xMarkupInfoContainer );
+}
+
+void SAL_CALL SwXFlatParagraph::commitStringMarkup(::sal_Int32 nType, const OUString & rIdentifier, ::sal_Int32 nStart, ::sal_Int32 nLength, const css::uno::Reference< css::container::XStringKeyMap > & rxMarkupInfoContainer)
+{
+ SolarMutexGuard aGuard;
+ SwXTextMarkup::commitStringMarkup( nType, rIdentifier, nStart, nLength, rxMarkupInfoContainer );
+}
+
+// text::XFlatParagraph:
+OUString SAL_CALL SwXFlatParagraph::getText()
+{
+ return maExpandText;
+}
+
+// text::XFlatParagraph:
+void SAL_CALL SwXFlatParagraph::setChecked( ::sal_Int32 nType, sal_Bool bVal )
+{
+ SolarMutexGuard aGuard;
+
+ if (!GetTextNode())
+ return;
+
+ if ( text::TextMarkupType::SPELLCHECK == nType )
+ {
+ GetTextNode()->SetWrongDirty(
+ bVal ? SwTextNode::WrongState::DONE : SwTextNode::WrongState::TODO);
+ }
+ else if ( text::TextMarkupType::SMARTTAG == nType )
+ GetTextNode()->SetSmartTagDirty( !bVal );
+ else if( text::TextMarkupType::PROOFREADING == nType )
+ {
+ GetTextNode()->SetGrammarCheckDirty( !bVal );
+ if( bVal )
+ ::finishGrammarCheck( *GetTextNode() );
+ }
+}
+
+// text::XFlatParagraph:
+sal_Bool SAL_CALL SwXFlatParagraph::isChecked( ::sal_Int32 nType )
+{
+ SolarMutexGuard aGuard;
+ if (GetTextNode())
+ {
+ if ( text::TextMarkupType::SPELLCHECK == nType )
+ return !GetTextNode()->IsWrongDirty();
+ else if ( text::TextMarkupType::PROOFREADING == nType )
+ return !GetTextNode()->IsGrammarCheckDirty();
+ else if ( text::TextMarkupType::SMARTTAG == nType )
+ return !GetTextNode()->IsSmartTagDirty();
+ }
+
+ return true;
+}
+
+// text::XFlatParagraph:
+sal_Bool SAL_CALL SwXFlatParagraph::isModified()
+{
+ SolarMutexGuard aGuard;
+ return nullptr == GetTextNode();
+}
+
+// text::XFlatParagraph:
+lang::Locale SAL_CALL SwXFlatParagraph::getLanguageOfText(::sal_Int32 nPos, ::sal_Int32 nLen)
+{
+ SolarMutexGuard aGuard;
+ if (!GetTextNode())
+ return LanguageTag::convertToLocale( LANGUAGE_NONE );
+
+ const lang::Locale aLocale( SW_BREAKITER()->GetLocale( GetTextNode()->GetLang(nPos, nLen) ) );
+ return aLocale;
+}
+
+// text::XFlatParagraph:
+lang::Locale SAL_CALL SwXFlatParagraph::getPrimaryLanguageOfText(::sal_Int32 nPos, ::sal_Int32 nLen)
+{
+ SolarMutexGuard aGuard;
+
+ if (!GetTextNode())
+ return LanguageTag::convertToLocale( LANGUAGE_NONE );
+
+ const lang::Locale aLocale( SW_BREAKITER()->GetLocale( GetTextNode()->GetLang(nPos, nLen) ) );
+ return aLocale;
+}
+
+// text::XFlatParagraph:
+void SAL_CALL SwXFlatParagraph::changeText(::sal_Int32 nPos, ::sal_Int32 nLen, const OUString & aNewText, const css::uno::Sequence< css::beans::PropertyValue > & aAttributes)
+{
+ SolarMutexGuard aGuard;
+
+ if (!GetTextNode())
+ return;
+
+ SwTextNode *const pOldTextNode = GetTextNode();
+
+ if (nPos < 0 || pOldTextNode->Len() < nPos || nLen < 0 || o3tl::make_unsigned(pOldTextNode->Len()) < static_cast<sal_uInt32>(nPos) + nLen)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwPaM aPaM( *GetTextNode(), nPos, *GetTextNode(), nPos+nLen );
+
+ UnoActionContext aAction( &GetTextNode()->GetDoc() );
+
+ const uno::Reference< text::XTextRange > xRange =
+ SwXTextRange::CreateXTextRange(
+ GetTextNode()->GetDoc(), *aPaM.GetPoint(), aPaM.GetMark() );
+ uno::Reference< beans::XPropertySet > xPropSet( xRange, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ for ( const auto& rAttribute : aAttributes )
+ xPropSet->setPropertyValue( rAttribute.Name, rAttribute.Value );
+ }
+
+ IDocumentContentOperations& rIDCO = pOldTextNode->getIDocumentContentOperations();
+ rIDCO.ReplaceRange( aPaM, aNewText, false );
+
+ ClearTextNode(); // TODO: is this really needed?
+}
+
+// text::XFlatParagraph:
+void SAL_CALL SwXFlatParagraph::changeAttributes(::sal_Int32 nPos, ::sal_Int32 nLen, const css::uno::Sequence< css::beans::PropertyValue > & aAttributes)
+{
+ SolarMutexGuard aGuard;
+
+ if (!GetTextNode())
+ return;
+
+ if (nPos < 0 || GetTextNode()->Len() < nPos || nLen < 0 || o3tl::make_unsigned(GetTextNode()->Len()) < static_cast<sal_uInt32>(nPos) + nLen)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwPaM aPaM( *GetTextNode(), nPos, *GetTextNode(), nPos+nLen );
+
+ UnoActionContext aAction( &GetTextNode()->GetDoc() );
+
+ const uno::Reference< text::XTextRange > xRange =
+ SwXTextRange::CreateXTextRange(
+ GetTextNode()->GetDoc(), *aPaM.GetPoint(), aPaM.GetMark() );
+ uno::Reference< beans::XPropertySet > xPropSet( xRange, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ for ( const auto& rAttribute : aAttributes )
+ xPropSet->setPropertyValue( rAttribute.Name, rAttribute.Value );
+ }
+
+ ClearTextNode(); // TODO: is this really needed?
+}
+
+// text::XFlatParagraph:
+css::uno::Sequence< ::sal_Int32 > SAL_CALL SwXFlatParagraph::getLanguagePortions()
+{
+ return css::uno::Sequence< ::sal_Int32>();
+}
+
+const uno::Sequence< sal_Int8 >&
+SwXFlatParagraph::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXFlatParagraphUnoTunnelId;
+ return theSwXFlatParagraphUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXFlatParagraph::getSomething(
+ const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+SwXFlatParagraphIterator::SwXFlatParagraphIterator( SwDoc& rDoc, sal_Int32 nType, bool bAutomatic )
+ : mpDoc( &rDoc ),
+ mnType( nType ),
+ mbAutomatic( bAutomatic ),
+ mnCurrentNode( 0 ),
+ mnEndNode( rDoc.GetNodes().Count() )
+{
+ //mnStartNode = mnCurrentNode = get node from current cursor TODO!
+
+ // register as listener and get notified when document is closed
+ StartListening(mpDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( RES_POOLPAGE_STANDARD )->GetNotifier());
+}
+
+SwXFlatParagraphIterator::~SwXFlatParagraphIterator()
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+}
+
+void SwXFlatParagraphIterator::Notify( const SfxHint& rHint )
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ SolarMutexGuard aGuard;
+ mpDoc = nullptr;
+ }
+}
+
+uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getFirstPara()
+{
+ return getNextPara(); // TODO
+}
+
+uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getNextPara()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XFlatParagraph > xRet;
+ if (!mpDoc)
+ return xRet;
+
+ SwTextNode* pRet = nullptr;
+ if ( mbAutomatic )
+ {
+ SwViewShell* pViewShell = mpDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+
+ SwPageFrame* pCurrentPage = pViewShell ? pViewShell->Imp()->GetFirstVisPage(pViewShell->GetOut()) : nullptr;
+ SwPageFrame* pStartPage = pCurrentPage;
+ SwPageFrame* pStopPage = nullptr;
+
+ while ( pCurrentPage && pCurrentPage != pStopPage )
+ {
+ if (mnType != text::TextMarkupType::SPELLCHECK || pCurrentPage->IsInvalidSpelling() )
+ {
+ // this method is supposed to return an empty paragraph in case Online Checking is disabled
+ if ( ( mnType == text::TextMarkupType::PROOFREADING || mnType == text::TextMarkupType::SPELLCHECK )
+ && !pViewShell->GetViewOptions()->IsOnlineSpell() )
+ return xRet;
+
+ // search for invalid content:
+ SwContentFrame* pCnt = pCurrentPage->ContainsContent();
+
+ while( pCnt && pCurrentPage->IsAnLower( pCnt ) )
+ {
+ if (pCnt->IsTextFrame())
+ {
+ SwTextFrame const*const pText(static_cast<SwTextFrame const*>(pCnt));
+ if (sw::MergedPara const*const pMergedPara = pText->GetMergedPara()
+ )
+ {
+ SwTextNode * pTextNode(nullptr);
+ for (auto const& e : pMergedPara->extents)
+ {
+ if (e.pNode != pTextNode)
+ {
+ pTextNode = e.pNode;
+ if ((mnType == text::TextMarkupType::SPELLCHECK
+ && pTextNode->IsWrongDirty()) ||
+ (mnType == text::TextMarkupType::PROOFREADING
+ && pTextNode->IsGrammarCheckDirty()))
+ {
+ pRet = pTextNode;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ SwTextNode const*const pTextNode(pText->GetTextNodeFirst());
+ if ((mnType == text::TextMarkupType::SPELLCHECK
+ && pTextNode->IsWrongDirty()) ||
+ (mnType == text::TextMarkupType::PROOFREADING
+ && pTextNode->IsGrammarCheckDirty()))
+
+ {
+ pRet = const_cast<SwTextNode*>(pTextNode);
+ }
+ }
+
+ if (pRet)
+ {
+ break;
+ }
+ }
+
+ pCnt = pCnt->GetNextContentFrame();
+ }
+ }
+
+ if ( pRet )
+ break;
+
+ // if there is no invalid text node on the current page,
+ // we validate the page
+ pCurrentPage->ValidateSpelling();
+
+ // proceed with next page, wrap at end of document if required:
+ pCurrentPage = static_cast<SwPageFrame*>(pCurrentPage->GetNext());
+
+ if ( !pCurrentPage && !pStopPage )
+ {
+ pStopPage = pStartPage;
+ pCurrentPage = static_cast<SwPageFrame*>(pViewShell->GetLayout()->Lower());
+ }
+ }
+ }
+ else // non-automatic checking
+ {
+ const SwNodes& rNodes = mpDoc->GetNodes();
+ const SwNodeOffset nMaxNodes = rNodes.Count();
+
+ while ( mnCurrentNode < mnEndNode && mnCurrentNode < nMaxNodes )
+ {
+ SwNode* pNd = rNodes[ mnCurrentNode ];
+
+ ++mnCurrentNode;
+
+ pRet = dynamic_cast<SwTextNode*>(pNd);
+ if ( pRet )
+ break;
+
+ if ( mnCurrentNode == mnEndNode )
+ {
+ mnCurrentNode = SwNodeOffset(0);
+ mnEndNode = SwNodeOffset(0);
+ }
+ }
+ }
+
+ if ( pRet )
+ {
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pRet, mpDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ xRet = new SwXFlatParagraph( *pRet, aExpandText, aConversionMap );
+ // keep hard references...
+ m_aFlatParaList.insert( xRet );
+ }
+
+ return xRet;
+}
+
+uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getLastPara()
+{
+ return getNextPara();
+}
+
+uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getParaAfter(const uno::Reference< text::XFlatParagraph > & xPara)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XFlatParagraph > xRet;
+ if (!mpDoc)
+ return xRet;
+
+ const uno::Reference<lang::XUnoTunnel> xFPTunnel(xPara, uno::UNO_QUERY);
+ SAL_WARN_IF(!xFPTunnel.is(), "sw.core", "invalid argument");
+ SwXFlatParagraph* const pFlatParagraph(comphelper::getFromUnoTunnel<SwXFlatParagraph>(xFPTunnel));
+
+ if ( !pFlatParagraph )
+ return xRet;
+
+ SwTextNode const*const pCurrentNode = pFlatParagraph->GetTextNode();
+
+ if ( !pCurrentNode )
+ return xRet;
+
+ SwTextNode* pNextTextNode = nullptr;
+ const SwNodes& rNodes = pCurrentNode->GetDoc().GetNodes();
+
+ for( SwNodeOffset nCurrentNode = pCurrentNode->GetIndex() + 1; nCurrentNode < rNodes.Count(); ++nCurrentNode )
+ {
+ SwNode* pNd = rNodes[ nCurrentNode ];
+ pNextTextNode = dynamic_cast<SwTextNode*>(pNd);
+ if ( pNextTextNode )
+ break;
+ }
+
+ if ( pNextTextNode )
+ {
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pNextTextNode, mpDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ xRet = new SwXFlatParagraph( *pNextTextNode, aExpandText, aConversionMap );
+ // keep hard references...
+ m_aFlatParaList.insert( xRet );
+ }
+
+ return xRet;
+}
+
+uno::Reference< text::XFlatParagraph > SwXFlatParagraphIterator::getParaBefore(const uno::Reference< text::XFlatParagraph > & xPara )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XFlatParagraph > xRet;
+ if (!mpDoc)
+ return xRet;
+
+ const uno::Reference<lang::XUnoTunnel> xFPTunnel(xPara, uno::UNO_QUERY);
+
+ SAL_WARN_IF(!xFPTunnel.is(), "sw.core", "invalid argument");
+ SwXFlatParagraph* const pFlatParagraph(comphelper::getFromUnoTunnel<SwXFlatParagraph>(xFPTunnel));
+
+ if ( !pFlatParagraph )
+ return xRet;
+
+ SwTextNode const*const pCurrentNode = pFlatParagraph->GetTextNode();
+
+ if ( !pCurrentNode )
+ return xRet;
+
+ SwTextNode* pPrevTextNode = nullptr;
+ const SwNodes& rNodes = pCurrentNode->GetDoc().GetNodes();
+
+ for( SwNodeOffset nCurrentNode = pCurrentNode->GetIndex() - 1; nCurrentNode > SwNodeOffset(0); --nCurrentNode )
+ {
+ SwNode* pNd = rNodes[ nCurrentNode ];
+ pPrevTextNode = dynamic_cast<SwTextNode*>(pNd);
+ if ( pPrevTextNode )
+ break;
+ }
+
+ if ( pPrevTextNode )
+ {
+ // Expand the string:
+ const ModelToViewHelper aConversionMap(*pPrevTextNode, mpDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ const OUString& aExpandText = aConversionMap.getViewText();
+
+ xRet = new SwXFlatParagraph( *pPrevTextNode, aExpandText, aConversionMap );
+ // keep hard references...
+ m_aFlatParaList.insert( xRet );
+ }
+
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoframe.cxx b/sw/source/core/unocore/unoframe.cxx
new file mode 100644
index 000000000..bd1e64b17
--- /dev/null
+++ b/sw/source/core/unocore/unoframe.cxx
@@ -0,0 +1,3701 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/frame/XTitle.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <o3tl/any.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/xflclit.hxx>
+#include <tools/globname.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/memberids.h>
+#include <swtypes.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <memory>
+#include <utility>
+#include <cntfrm.hxx>
+#include <doc.hxx>
+#include <drawdoc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docsh.hxx>
+#include <editsh.hxx>
+#include <ndindex.hxx>
+#include <pam.hxx>
+#include <ndnotxt.hxx>
+#include <svx/unomid.hxx>
+#include <unocrsr.hxx>
+#include <unocrsrhelper.hxx>
+#include <docstyle.hxx>
+#include <dcontact.hxx>
+#include <fmtcnct.hxx>
+#include <ndole.hxx>
+#include <frmfmt.hxx>
+#include <frame.hxx>
+#include <textboxhelper.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unoparagraph.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unoevent.hxx>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <tools/poly.hxx>
+#include <swundo.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/protitem.hxx>
+#include <fmtornt.hxx>
+#include <fmteiro.hxx>
+#include <fmturl.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/opaqitem.hxx>
+#include <editeng/prntitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <fmtsrnd.hxx>
+#include <fmtfsize.hxx>
+#include <grfatr.hxx>
+#include <unoframe.hxx>
+#include <fmtanchr.hxx>
+#include <fmtclds.hxx>
+#include <fmtcntnt.hxx>
+#include <frmatr.hxx>
+#include <ndtxt.hxx>
+#include <ndgrf.hxx>
+#include <mutex>
+#include <vcl/svapp.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <poolfmt.hxx>
+#include <pagedesc.hxx>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <editeng/frmdiritem.hxx>
+#include <fmtfollowtextflow.hxx>
+#include <fmtwrapinfluenceonobjpos.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <vcl/errinf.hxx>
+
+#include <svx/unobrushitemhelper.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xgrscit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbmpit.hxx>
+#include <svx/xflbmsxy.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xsflclit.hxx>
+#include <svx/xflbmsli.hxx>
+#include <svx/xflbtoxy.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflboxy.hxx>
+#include <svx/xflbckit.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xfltrit.hxx>
+#include <swunohelper.hxx>
+#include <fefly.hxx>
+
+using namespace ::com::sun::star;
+
+using ::com::sun::star::frame::XModel;
+using ::com::sun::star::container::XNameAccess;
+using ::com::sun::star::style::XStyleFamiliesSupplier;
+
+class BaseFrameProperties_Impl
+{
+ SwUnoCursorHelper::SwAnyMapHelper m_aAnyMap;
+
+public:
+ virtual ~BaseFrameProperties_Impl();
+
+ void SetProperty(sal_uInt16 nWID, sal_uInt8 nMemberId, const uno::Any& rVal);
+ bool GetProperty(sal_uInt16 nWID, sal_uInt8 nMemberId, const uno::Any*& pAny );
+ bool FillBaseProperties(SfxItemSet& rToSet, const SfxItemSet &rFromSet, bool& rSizeFound);
+
+ virtual bool AnyToItemSet( SwDoc* pDoc, SfxItemSet& rFrameSet, SfxItemSet& rSet, bool& rSizeFound) = 0;
+};
+
+BaseFrameProperties_Impl::~BaseFrameProperties_Impl()
+{
+}
+
+void BaseFrameProperties_Impl::SetProperty(sal_uInt16 nWID, sal_uInt8 nMemberId, const uno::Any& rVal)
+{
+ m_aAnyMap.SetValue( nWID, nMemberId, rVal );
+}
+
+bool BaseFrameProperties_Impl::GetProperty(sal_uInt16 nWID, sal_uInt8 nMemberId, const uno::Any*& rpAny)
+{
+ return m_aAnyMap.FillValue( nWID, nMemberId, rpAny );
+}
+
+bool BaseFrameProperties_Impl::FillBaseProperties(SfxItemSet& rToSet, const SfxItemSet& rFromSet, bool& rSizeFound)
+{
+ // assert when the target SfxItemSet has no parent. It *should* have the pDfltFrameFormat
+ // from SwDoc set as parent (or similar) to have the necessary XFILL_NONE in the ItemSet
+ if(!rToSet.GetParent())
+ {
+ OSL_ENSURE(false, "OOps, target SfxItemSet *should* have a parent which contains XFILL_NONE as XFillStyleItem (!)");
+ }
+
+ bool bRet = true;
+ // always add an anchor to the set
+ SwFormatAnchor aAnchor ( rFromSet.Get ( RES_ANCHOR ) );
+ {
+ const ::uno::Any* pAnchorPgNo;
+ if(GetProperty(RES_ANCHOR, MID_ANCHOR_PAGENUM, pAnchorPgNo))
+ bRet &= static_cast<SfxPoolItem&>(aAnchor).PutValue(*pAnchorPgNo, MID_ANCHOR_PAGENUM);
+ const ::uno::Any* pAnchorType;
+ if(GetProperty(RES_ANCHOR, MID_ANCHOR_ANCHORTYPE, pAnchorType))
+ bRet &= static_cast<SfxPoolItem&>(aAnchor).PutValue(*pAnchorType, MID_ANCHOR_ANCHORTYPE);
+ }
+
+ rToSet.Put(aAnchor);
+
+ // check for SvxBrushItem (RES_BACKGROUND) properties
+ const ::uno::Any* pCol = nullptr; GetProperty(RES_BACKGROUND, MID_BACK_COLOR, pCol );
+ const ::uno::Any* pRGBCol = nullptr; GetProperty(RES_BACKGROUND, MID_BACK_COLOR_R_G_B, pRGBCol );
+ const ::uno::Any* pColTrans = nullptr; GetProperty(RES_BACKGROUND, MID_BACK_COLOR_TRANSPARENCY, pColTrans);
+ const ::uno::Any* pTrans = nullptr; GetProperty(RES_BACKGROUND, MID_GRAPHIC_TRANSPARENT, pTrans );
+ const ::uno::Any* pGrLoc = nullptr; GetProperty(RES_BACKGROUND, MID_GRAPHIC_POSITION, pGrLoc );
+ const ::uno::Any* pGraphic = nullptr; GetProperty(RES_BACKGROUND, MID_GRAPHIC, pGraphic );
+ const ::uno::Any* pGrFilter = nullptr; GetProperty(RES_BACKGROUND, MID_GRAPHIC_FILTER, pGrFilter );
+ const ::uno::Any* pGraphicURL = nullptr; GetProperty(RES_BACKGROUND, MID_GRAPHIC_URL, pGraphicURL );
+ const ::uno::Any* pGrTransparency = nullptr; GetProperty(RES_BACKGROUND, MID_GRAPHIC_TRANSPARENCY, pGrTransparency );
+ const bool bSvxBrushItemPropertiesUsed(
+ pCol ||
+ pTrans ||
+ pGraphic ||
+ pGraphicURL ||
+ pGrFilter ||
+ pGrLoc ||
+ pGrTransparency ||
+ pColTrans ||
+ pRGBCol);
+
+ // check for FillStyle properties in the range XATTR_FILL_FIRST, XATTR_FILL_LAST
+ const uno::Any* pXFillStyleItem = nullptr; GetProperty(XATTR_FILLSTYLE, 0, pXFillStyleItem);
+ const uno::Any* pXFillColorItem = nullptr; GetProperty(XATTR_FILLCOLOR, 0, pXFillColorItem);
+
+ // XFillGradientItem: two possible slots supported in UNO API
+ const uno::Any* pXFillGradientItem = nullptr; GetProperty(XATTR_FILLGRADIENT, MID_FILLGRADIENT, pXFillGradientItem);
+ const uno::Any* pXFillGradientNameItem = nullptr; GetProperty(XATTR_FILLGRADIENT, MID_NAME, pXFillGradientNameItem);
+
+ // XFillHatchItem: two possible slots supported in UNO API
+ const uno::Any* pXFillHatchItem = nullptr; GetProperty(XATTR_FILLHATCH, MID_FILLHATCH, pXFillHatchItem);
+ const uno::Any* pXFillHatchNameItem = nullptr; GetProperty(XATTR_FILLHATCH, MID_NAME, pXFillHatchNameItem);
+
+ // XFillBitmapItem: three possible slots supported in UNO API
+ const uno::Any* pXFillBitmapItem = nullptr; GetProperty(XATTR_FILLBITMAP, MID_BITMAP, pXFillBitmapItem);
+ const uno::Any* pXFillBitmapNameItem = nullptr; GetProperty(XATTR_FILLBITMAP, MID_NAME, pXFillBitmapNameItem);
+
+ const uno::Any* pXFillTransparenceItem = nullptr; GetProperty(XATTR_FILLTRANSPARENCE, 0, pXFillTransparenceItem);
+ const uno::Any* pXGradientStepCountItem = nullptr; GetProperty(XATTR_GRADIENTSTEPCOUNT, 0, pXGradientStepCountItem);
+ const uno::Any* pXFillBmpPosItem = nullptr; GetProperty(XATTR_FILLBMP_POS, 0, pXFillBmpPosItem);
+ const uno::Any* pXFillBmpSizeXItem = nullptr; GetProperty(XATTR_FILLBMP_SIZEX, 0, pXFillBmpSizeXItem);
+ const uno::Any* pXFillBmpSizeYItem = nullptr; GetProperty(XATTR_FILLBMP_SIZEY, 0, pXFillBmpSizeYItem);
+
+ // XFillFloatTransparenceItem: two possible slots supported in UNO API
+ const uno::Any* pXFillFloatTransparenceItem = nullptr; GetProperty(XATTR_FILLFLOATTRANSPARENCE, MID_FILLGRADIENT, pXFillFloatTransparenceItem);
+ const uno::Any* pXFillFloatTransparenceNameItem = nullptr; GetProperty(XATTR_FILLFLOATTRANSPARENCE, MID_NAME, pXFillFloatTransparenceNameItem);
+
+ const uno::Any* pXSecondaryFillColorItem = nullptr; GetProperty(XATTR_SECONDARYFILLCOLOR, 0, pXSecondaryFillColorItem);
+ const uno::Any* pXFillBmpSizeLogItem = nullptr; GetProperty(XATTR_FILLBMP_SIZELOG, 0, pXFillBmpSizeLogItem);
+ const uno::Any* pXFillBmpTileOffsetXItem = nullptr; GetProperty(XATTR_FILLBMP_TILEOFFSETX, 0, pXFillBmpTileOffsetXItem);
+ const uno::Any* pXFillBmpTileOffsetYItem = nullptr; GetProperty(XATTR_FILLBMP_TILEOFFSETY, 0, pXFillBmpTileOffsetYItem);
+ const uno::Any* pXFillBmpPosOffsetXItem = nullptr; GetProperty(XATTR_FILLBMP_POSOFFSETX, 0, pXFillBmpPosOffsetXItem);
+ const uno::Any* pXFillBmpPosOffsetYItem = nullptr; GetProperty(XATTR_FILLBMP_POSOFFSETY, 0, pXFillBmpPosOffsetYItem);
+ const uno::Any* pXFillBackgroundItem = nullptr; GetProperty(XATTR_FILLBACKGROUND, 0, pXFillBackgroundItem);
+ const uno::Any* pOwnAttrFillBmpItem = nullptr; GetProperty(OWN_ATTR_FILLBMP_MODE, 0, pOwnAttrFillBmpItem);
+
+ // tdf#91140: ignore SOLID fill style for determining if fill style is used
+ // but there is a Graphic
+ const bool bFillStyleUsed(pXFillStyleItem && pXFillStyleItem->hasValue() &&
+ (pXFillStyleItem->get<drawing::FillStyle>() != drawing::FillStyle_SOLID || (!pGraphic || !pGraphicURL) ));
+ SAL_INFO_IF(pXFillStyleItem && pXFillStyleItem->hasValue() && !bFillStyleUsed,
+ "sw.uno", "FillBaseProperties: ignoring invalid FillStyle");
+ const bool bXFillStyleItemUsed(
+ bFillStyleUsed ||
+ pXFillColorItem ||
+ pXFillGradientItem || pXFillGradientNameItem ||
+ pXFillHatchItem || pXFillHatchNameItem ||
+ pXFillBitmapItem || pXFillBitmapNameItem ||
+ pXFillTransparenceItem ||
+ pXGradientStepCountItem ||
+ pXFillBmpPosItem ||
+ pXFillBmpSizeXItem ||
+ pXFillBmpSizeYItem ||
+ pXFillFloatTransparenceItem || pXFillFloatTransparenceNameItem ||
+ pXSecondaryFillColorItem ||
+ pXFillBmpSizeLogItem ||
+ pXFillBmpTileOffsetXItem ||
+ pXFillBmpTileOffsetYItem ||
+ pXFillBmpPosOffsetXItem ||
+ pXFillBmpPosOffsetYItem ||
+ pXFillBackgroundItem ||
+ pOwnAttrFillBmpItem);
+
+ // use brush items, but *only* if no FillStyle properties are used; if both are used and when applying both
+ // in the obvious order some attributes may be wrong since they are set by the 1st set, but not
+ // redefined as needed by the 2nd set when they are default (and thus no tset) in the 2nd set. If
+ // it is necessary for any reason to set both (it should not) an in-between step will be needed
+ // that resets the items for FillAttributes in rToSet to default.
+ // Note: There are other mechanisms in XMLOFF to pre-sort this relationship already, but this version
+ // was used initially, is tested and works. Keep it to be able to react when another feed adds attributes
+ // from both sets.
+ if(bSvxBrushItemPropertiesUsed && !bXFillStyleItemUsed)
+ {
+ // create a temporary SvxBrushItem, fill the attributes to it and use it to set
+ // the corresponding FillAttributes
+ SvxBrushItem aBrush(RES_BACKGROUND);
+
+ if(pCol)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pCol,MID_BACK_COLOR );
+ }
+
+ if(pColTrans)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pColTrans, MID_BACK_COLOR_TRANSPARENCY);
+ }
+
+ if(pRGBCol)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pRGBCol, MID_BACK_COLOR_R_G_B);
+ }
+
+ if(pTrans)
+ {
+ // don't overwrite transparency with a non-transparence flag
+ if(!pColTrans || Any2Bool( *pTrans ))
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pTrans, MID_GRAPHIC_TRANSPARENT);
+ }
+
+ if (pGraphic)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pGraphic, MID_GRAPHIC);
+ }
+
+ if (pGraphicURL)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pGraphicURL, MID_GRAPHIC_URL);
+ }
+
+ if(pGrFilter)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pGrFilter, MID_GRAPHIC_FILTER);
+ }
+
+ if(pGrLoc)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pGrLoc, MID_GRAPHIC_POSITION);
+ }
+
+ if(pGrTransparency)
+ {
+ bRet &= static_cast<SfxPoolItem&>(aBrush).PutValue(*pGrTransparency, MID_GRAPHIC_TRANSPARENCY);
+ }
+
+ setSvxBrushItemAsFillAttributesToTargetSet(aBrush, rToSet);
+ }
+
+ if(bXFillStyleItemUsed)
+ {
+ XFillStyleItem aXFillStyleItem;
+ std::unique_ptr<SvxBrushItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND));
+
+ if(pXFillStyleItem)
+ {
+ aXFillStyleItem.PutValue(*pXFillStyleItem, 0);
+ rToSet.Put(aXFillStyleItem);
+ }
+
+ if(pXFillColorItem)
+ {
+ const Color aNullCol(COL_DEFAULT_SHAPE_FILLING);
+ XFillColorItem aXFillColorItem(OUString(), aNullCol);
+
+ aXFillColorItem.PutValue(*pXFillColorItem, 0);
+ rToSet.Put(aXFillColorItem);
+ //set old-school brush color if we later encounter the
+ //MID_BACK_COLOR_TRANSPARENCY case below
+ aBrush = getSvxBrushItemFromSourceSet(rToSet, RES_BACKGROUND, false);
+ }
+ else if (aXFillStyleItem.GetValue() == drawing::FillStyle_SOLID && (pCol || pRGBCol))
+ {
+ // Fill style is set to solid, but no fill color is given.
+ // On the other hand, we have a BackColor, so use that.
+ if (pCol)
+ aBrush->PutValue(*pCol, MID_BACK_COLOR);
+ else
+ aBrush->PutValue(*pRGBCol, MID_BACK_COLOR_R_G_B);
+ setSvxBrushItemAsFillAttributesToTargetSet(*aBrush, rToSet);
+ }
+
+ if(pXFillGradientItem || pXFillGradientNameItem)
+ {
+ if(pXFillGradientItem)
+ {
+ const XGradient aNullGrad(COL_BLACK, COL_WHITE);
+ XFillGradientItem aXFillGradientItem(aNullGrad);
+
+ aXFillGradientItem.PutValue(*pXFillGradientItem, MID_FILLGRADIENT);
+ rToSet.Put(aXFillGradientItem);
+ }
+
+ if(pXFillGradientNameItem)
+ {
+ OUString aTempName;
+
+ if(!(*pXFillGradientNameItem >>= aTempName ))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ bool const bSuccess = SvxShape::SetFillAttribute(
+ XATTR_FILLGRADIENT, aTempName, rToSet);
+ if (aXFillStyleItem.GetValue() == drawing::FillStyle_GRADIENT)
+ { // tdf#90946 ignore invalid gradient-name if SOLID
+ bRet &= bSuccess;
+ }
+ else
+ {
+ SAL_INFO_IF(!bSuccess, "sw.uno",
+ "FillBaseProperties: ignoring invalid FillGradientName");
+ }
+ }
+ }
+
+ if(pXFillHatchItem || pXFillHatchNameItem)
+ {
+ if(pXFillHatchItem)
+ {
+ const Color aNullCol(COL_DEFAULT_SHAPE_STROKE);
+ const XHatch aNullHatch(aNullCol);
+ XFillHatchItem aXFillHatchItem(aNullHatch);
+
+ aXFillHatchItem.PutValue(*pXFillHatchItem, MID_FILLHATCH);
+ rToSet.Put(aXFillHatchItem);
+ }
+
+ if(pXFillHatchNameItem)
+ {
+ OUString aTempName;
+
+ if(!(*pXFillHatchNameItem >>= aTempName ))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ bRet &= SvxShape::SetFillAttribute(XATTR_FILLHATCH, aTempName, rToSet);
+ }
+ }
+
+ if (pXFillBitmapItem || pXFillBitmapNameItem)
+ {
+ if(pXFillBitmapItem)
+ {
+ const Graphic aNullGraphic;
+ XFillBitmapItem aXFillBitmapItem(aNullGraphic);
+
+ aXFillBitmapItem.PutValue(*pXFillBitmapItem, MID_BITMAP);
+ rToSet.Put(aXFillBitmapItem);
+ }
+
+ if(pXFillBitmapNameItem)
+ {
+ OUString aTempName;
+
+ if(!(*pXFillBitmapNameItem >>= aTempName ))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ bRet &= SvxShape::SetFillAttribute(XATTR_FILLBITMAP, aTempName, rToSet);
+ }
+ }
+
+ if (pXFillTransparenceItem)
+ {
+ XFillTransparenceItem aXFillTransparenceItem;
+ aXFillTransparenceItem.PutValue(*pXFillTransparenceItem, 0);
+ rToSet.Put(aXFillTransparenceItem);
+ }
+ else if (pColTrans &&
+ !pXFillFloatTransparenceItem && !pXFillFloatTransparenceNameItem)
+ {
+ // No fill transparency is given. On the other hand, we have a
+ // BackColorTransparency, so use that.
+ // tdf#90640 tdf#90130: this is necessary for LO 4.4.0 - 4.4.2
+ // that forgot to write draw:opacity into documents
+ // but: the value was *always* wrong for bitmaps! => ignore it
+ sal_Int8 nGraphicTransparency(0);
+ *pColTrans >>= nGraphicTransparency;
+ if (aXFillStyleItem.GetValue() != drawing::FillStyle_BITMAP)
+ {
+ rToSet.Put(XFillTransparenceItem(nGraphicTransparency));
+ }
+ if (aXFillStyleItem.GetValue() == drawing::FillStyle_SOLID)
+ {
+ aBrush->PutValue(*pColTrans, MID_BACK_COLOR_TRANSPARENCY);
+ setSvxBrushItemAsFillAttributesToTargetSet(*aBrush, rToSet);
+ }
+ }
+
+ if(pXGradientStepCountItem)
+ {
+ XGradientStepCountItem aXGradientStepCountItem;
+
+ aXGradientStepCountItem.PutValue(*pXGradientStepCountItem, 0);
+ rToSet.Put(aXGradientStepCountItem);
+ }
+
+ if(pXFillBmpPosItem)
+ {
+ XFillBmpPosItem aXFillBmpPosItem;
+
+ aXFillBmpPosItem.PutValue(*pXFillBmpPosItem, 0);
+ rToSet.Put(aXFillBmpPosItem);
+ }
+
+ if(pXFillBmpSizeXItem)
+ {
+ XFillBmpSizeXItem aXFillBmpSizeXItem;
+
+ aXFillBmpSizeXItem.PutValue(*pXFillBmpSizeXItem, 0);
+ rToSet.Put(aXFillBmpSizeXItem);
+ }
+
+ if(pXFillBmpSizeYItem)
+ {
+ XFillBmpSizeYItem aXFillBmpSizeYItem;
+
+ aXFillBmpSizeYItem.PutValue(*pXFillBmpSizeYItem, 0);
+ rToSet.Put(aXFillBmpSizeYItem);
+ }
+
+ if(pXFillFloatTransparenceItem || pXFillFloatTransparenceNameItem)
+ {
+ if(pXFillFloatTransparenceItem)
+ {
+ const XGradient aNullGrad(COL_BLACK, COL_WHITE);
+ XFillFloatTransparenceItem aXFillFloatTransparenceItem(aNullGrad, false);
+
+ aXFillFloatTransparenceItem.PutValue(*pXFillFloatTransparenceItem, MID_FILLGRADIENT);
+ rToSet.Put(aXFillFloatTransparenceItem);
+ }
+
+ if(pXFillFloatTransparenceNameItem)
+ {
+ OUString aTempName;
+
+ if(!(*pXFillFloatTransparenceNameItem >>= aTempName ))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ bRet &= SvxShape::SetFillAttribute(XATTR_FILLFLOATTRANSPARENCE, aTempName, rToSet);
+ }
+ }
+
+ if(pXSecondaryFillColorItem)
+ {
+ const Color aNullCol(COL_DEFAULT_SHAPE_FILLING);
+ XSecondaryFillColorItem aXSecondaryFillColorItem(OUString(), aNullCol);
+
+ aXSecondaryFillColorItem.PutValue(*pXSecondaryFillColorItem, 0);
+ rToSet.Put(aXSecondaryFillColorItem);
+ }
+
+ if(pXFillBmpSizeLogItem)
+ {
+ XFillBmpSizeLogItem aXFillBmpSizeLogItem;
+
+ aXFillBmpSizeLogItem.PutValue(*pXFillBmpSizeLogItem, 0);
+ rToSet.Put(aXFillBmpSizeLogItem);
+ }
+
+ if(pXFillBmpTileOffsetXItem)
+ {
+ XFillBmpTileOffsetXItem aXFillBmpTileOffsetXItem;
+
+ aXFillBmpTileOffsetXItem.PutValue(*pXFillBmpTileOffsetXItem, 0);
+ rToSet.Put(aXFillBmpTileOffsetXItem);
+ }
+
+ if(pXFillBmpTileOffsetYItem)
+ {
+ XFillBmpTileOffsetYItem aXFillBmpTileOffsetYItem;
+
+ aXFillBmpTileOffsetYItem.PutValue(*pXFillBmpTileOffsetYItem, 0);
+ rToSet.Put(aXFillBmpTileOffsetYItem);
+ }
+
+ if(pXFillBmpPosOffsetXItem)
+ {
+ XFillBmpPosOffsetXItem aXFillBmpPosOffsetXItem;
+
+ aXFillBmpPosOffsetXItem.PutValue(*pXFillBmpPosOffsetXItem, 0);
+ rToSet.Put(aXFillBmpPosOffsetXItem);
+ }
+
+ if(pXFillBmpPosOffsetYItem)
+ {
+ XFillBmpPosOffsetYItem aXFillBmpPosOffsetYItem;
+
+ aXFillBmpPosOffsetYItem.PutValue(*pXFillBmpPosOffsetYItem, 0);
+ rToSet.Put(aXFillBmpPosOffsetYItem);
+ }
+
+ if(pXFillBackgroundItem)
+ {
+ XFillBackgroundItem aXFillBackgroundItem;
+
+ aXFillBackgroundItem.PutValue(*pXFillBackgroundItem, 0);
+ rToSet.Put(aXFillBackgroundItem);
+ }
+
+ if(pOwnAttrFillBmpItem)
+ {
+ drawing::BitmapMode eMode;
+
+ if(!(*pOwnAttrFillBmpItem >>= eMode))
+ {
+ sal_Int32 nMode = 0;
+
+ if(!(*pOwnAttrFillBmpItem >>= nMode))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ eMode = static_cast<drawing::BitmapMode>(nMode);
+ }
+
+ rToSet.Put(XFillBmpStretchItem(drawing::BitmapMode_STRETCH == eMode));
+ rToSet.Put(XFillBmpTileItem(drawing::BitmapMode_REPEAT == eMode));
+ }
+ }
+ {
+ const ::uno::Any* pCont = nullptr;
+ GetProperty(RES_PROTECT, MID_PROTECT_CONTENT, pCont );
+ const ::uno::Any* pPos = nullptr;
+ GetProperty(RES_PROTECT,MID_PROTECT_POSITION, pPos );
+ const ::uno::Any* pName = nullptr;
+ GetProperty(RES_PROTECT, MID_PROTECT_SIZE, pName );
+ if(pCont||pPos||pName)
+ {
+ SvxProtectItem aProt ( rFromSet.Get ( RES_PROTECT ) );
+ if(pCont)
+ bRet &= static_cast<SfxPoolItem&>(aProt).PutValue(*pCont, MID_PROTECT_CONTENT);
+ if(pPos )
+ bRet &= static_cast<SfxPoolItem&>(aProt).PutValue(*pPos, MID_PROTECT_POSITION);
+ if(pName)
+ bRet &= static_cast<SfxPoolItem&>(aProt).PutValue(*pName, MID_PROTECT_SIZE);
+ rToSet.Put(aProt);
+ }
+ }
+ {
+ const ::uno::Any* pHori = nullptr;
+ GetProperty(RES_HORI_ORIENT, MID_HORIORIENT_ORIENT, pHori );
+ const ::uno::Any* pHoriP = nullptr;
+ GetProperty(RES_HORI_ORIENT, MID_HORIORIENT_POSITION|CONVERT_TWIPS, pHoriP );
+ const ::uno::Any* pHoriR = nullptr;
+ GetProperty(RES_HORI_ORIENT, MID_HORIORIENT_RELATION, pHoriR );
+ const ::uno::Any* pPageT = nullptr;
+ GetProperty(RES_HORI_ORIENT, MID_HORIORIENT_PAGETOGGLE, pPageT);
+ if(pHori||pHoriP||pHoriR||pPageT)
+ {
+ SwFormatHoriOrient aOrient ( rFromSet.Get ( RES_HORI_ORIENT ) );
+ if(pHori )
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pHori, MID_HORIORIENT_ORIENT);
+ if(pHoriP)
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pHoriP, MID_HORIORIENT_POSITION|CONVERT_TWIPS);
+ if(pHoriR)
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pHoriR, MID_HORIORIENT_RELATION);
+ if(pPageT)
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pPageT, MID_HORIORIENT_PAGETOGGLE);
+ rToSet.Put(aOrient);
+ }
+ }
+
+ {
+ const ::uno::Any* pVert = nullptr;
+ GetProperty(RES_VERT_ORIENT, MID_VERTORIENT_ORIENT, pVert);
+ const ::uno::Any* pVertP = nullptr;
+ GetProperty(RES_VERT_ORIENT, MID_VERTORIENT_POSITION|CONVERT_TWIPS, pVertP );
+ const ::uno::Any* pVertR = nullptr;
+ GetProperty(RES_VERT_ORIENT, MID_VERTORIENT_RELATION, pVertR );
+ if(pVert||pVertP||pVertR)
+ {
+ SwFormatVertOrient aOrient ( rFromSet.Get ( RES_VERT_ORIENT ) );
+ if(pVert )
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pVert, MID_VERTORIENT_ORIENT);
+ if(pVertP)
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pVertP, MID_VERTORIENT_POSITION|CONVERT_TWIPS);
+ if(pVertR)
+ bRet &= static_cast<SfxPoolItem&>(aOrient).PutValue(*pVertR, MID_VERTORIENT_RELATION);
+ rToSet.Put(aOrient);
+ }
+ }
+ {
+ const ::uno::Any* pURL = nullptr;
+ GetProperty(RES_URL, MID_URL_URL, pURL );
+ const ::uno::Any* pTarget = nullptr;
+ GetProperty(RES_URL, MID_URL_TARGET, pTarget );
+ const ::uno::Any* pHyLNm = nullptr;
+ GetProperty(RES_URL, MID_URL_HYPERLINKNAME, pHyLNm );
+ const ::uno::Any* pHySMp = nullptr;
+ GetProperty(RES_URL, MID_URL_SERVERMAP, pHySMp );
+ if(pURL||pTarget||pHyLNm||pHySMp)
+ {
+ SwFormatURL aURL ( rFromSet.Get ( RES_URL ) );
+ if(pURL)
+ bRet &= static_cast<SfxPoolItem&>(aURL).PutValue(*pURL, MID_URL_URL);
+ if(pTarget)
+ bRet &= static_cast<SfxPoolItem&>(aURL).PutValue(*pTarget, MID_URL_TARGET);
+ if(pHyLNm)
+ bRet &= static_cast<SfxPoolItem&>(aURL).PutValue(*pHyLNm, MID_URL_HYPERLINKNAME );
+ if(pHySMp)
+ bRet &= static_cast<SfxPoolItem&>(aURL).PutValue(*pHySMp, MID_URL_SERVERMAP);
+ rToSet.Put(aURL);
+ }
+ }
+ const ::uno::Any* pL = nullptr;
+ GetProperty(RES_LR_SPACE, MID_L_MARGIN|CONVERT_TWIPS, pL );
+ const ::uno::Any* pR = nullptr;
+ GetProperty(RES_LR_SPACE, MID_R_MARGIN|CONVERT_TWIPS, pR );
+ if(pL||pR)
+ {
+ SvxLRSpaceItem aLR ( rFromSet.Get ( RES_LR_SPACE ) );
+ if(pL)
+ bRet &= static_cast<SfxPoolItem&>(aLR).PutValue(*pL, MID_L_MARGIN|CONVERT_TWIPS);
+ if(pR)
+ bRet &= static_cast<SfxPoolItem&>(aLR).PutValue(*pR, MID_R_MARGIN|CONVERT_TWIPS);
+ rToSet.Put(aLR);
+ }
+ const ::uno::Any* pT = nullptr;
+ GetProperty(RES_UL_SPACE, MID_UP_MARGIN|CONVERT_TWIPS, pT );
+ const ::uno::Any* pB = nullptr;
+ GetProperty(RES_UL_SPACE, MID_LO_MARGIN|CONVERT_TWIPS, pB );
+ if(pT||pB)
+ {
+ SvxULSpaceItem aTB ( rFromSet.Get ( RES_UL_SPACE ) );
+ if(pT)
+ bRet &= static_cast<SfxPoolItem&>(aTB).PutValue(*pT, MID_UP_MARGIN|CONVERT_TWIPS);
+ if(pB)
+ bRet &= static_cast<SfxPoolItem&>(aTB).PutValue(*pB, MID_LO_MARGIN|CONVERT_TWIPS);
+ rToSet.Put(aTB);
+ }
+ const ::uno::Any* pOp;
+ if(GetProperty(RES_OPAQUE, 0, pOp))
+ {
+ SvxOpaqueItem aOp ( rFromSet.Get ( RES_OPAQUE ) );
+ bRet &= static_cast<SfxPoolItem&>(aOp).PutValue(*pOp, 0);
+ rToSet.Put(aOp);
+ }
+ const ::uno::Any* pPrt;
+ if(GetProperty(RES_PRINT, 0, pPrt))
+ {
+ SvxPrintItem aPrt ( rFromSet.Get ( RES_PRINT ) );
+ bRet &= static_cast<SfxPoolItem&>(aPrt).PutValue(*pPrt, 0);
+ rToSet.Put(aPrt);
+ }
+ const ::uno::Any* pSh;
+ if(GetProperty(RES_SHADOW, CONVERT_TWIPS, pSh))
+ {
+ SvxShadowItem aSh ( rFromSet.Get ( RES_SHADOW ) );
+ bRet &= static_cast<SfxPoolItem&>(aSh).PutValue(*pSh, CONVERT_TWIPS);
+ rToSet.Put(aSh);
+ }
+ const ::uno::Any* pShTr;
+ if(GetProperty(RES_SHADOW, MID_SHADOW_TRANSPARENCE, pShTr) && rToSet.HasItem(RES_SHADOW))
+ {
+ SvxShadowItem aSh(rToSet.Get(RES_SHADOW));
+ bRet &= aSh.PutValue(*pShTr, MID_SHADOW_TRANSPARENCE);
+ rToSet.Put(aSh);
+ }
+ const ::uno::Any* pSur = nullptr;
+ GetProperty(RES_SURROUND, MID_SURROUND_SURROUNDTYPE, pSur);
+ const ::uno::Any* pSurCont = nullptr;
+ GetProperty(RES_SURROUND, MID_SURROUND_CONTOUR, pSurCont);
+ const ::uno::Any* pSurAnch = nullptr;
+ GetProperty(RES_SURROUND, MID_SURROUND_ANCHORONLY, pSurAnch);
+ if(pSur || pSurAnch)
+ {
+ SwFormatSurround aSrnd ( rFromSet.Get ( RES_SURROUND ) );
+ if(pSur)
+ bRet &= static_cast<SfxPoolItem&>(aSrnd).PutValue(*pSur, MID_SURROUND_SURROUNDTYPE);
+ if(pSurCont)
+ bRet &= static_cast<SfxPoolItem&>(aSrnd).PutValue(*pSurCont, MID_SURROUND_CONTOUR);
+ if(pSurAnch)
+ bRet &= static_cast<SfxPoolItem&>(aSrnd).PutValue(*pSurAnch, MID_SURROUND_ANCHORONLY);
+ rToSet.Put(aSrnd);
+ }
+ const ::uno::Any* pLeft = nullptr;
+ GetProperty(RES_BOX, LEFT_BORDER |CONVERT_TWIPS, pLeft );
+ const ::uno::Any* pRight = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|RIGHT_BORDER , pRight );
+ const ::uno::Any* pTop = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|TOP_BORDER , pTop );
+ const ::uno::Any* pBottom = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|BOTTOM_BORDER, pBottom);
+ const ::uno::Any* pDistance = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|BORDER_DISTANCE, pDistance);
+ const ::uno::Any* pLeftDistance = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|LEFT_BORDER_DISTANCE, pLeftDistance);
+ const ::uno::Any* pRightDistance = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|RIGHT_BORDER_DISTANCE, pRightDistance);
+ const ::uno::Any* pTopDistance = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|TOP_BORDER_DISTANCE, pTopDistance);
+ const ::uno::Any* pBottomDistance = nullptr;
+ GetProperty(RES_BOX, CONVERT_TWIPS|BOTTOM_BORDER_DISTANCE, pBottomDistance);
+ const ::uno::Any* pLineStyle = nullptr;
+ GetProperty(RES_BOX, LINE_STYLE, pLineStyle);
+ const ::uno::Any* pLineWidth = nullptr;
+ GetProperty(RES_BOX, LINE_WIDTH, pLineWidth);
+ if( pLeft || pRight || pTop || pBottom || pDistance ||
+ pLeftDistance || pRightDistance || pTopDistance || pBottomDistance ||
+ pLineStyle || pLineWidth )
+ {
+ SvxBoxItem aBox ( rFromSet.Get ( RES_BOX ) );
+ if( pLeft )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pLeft, CONVERT_TWIPS|LEFT_BORDER );
+ if( pRight )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pRight, CONVERT_TWIPS|RIGHT_BORDER );
+ if( pTop )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pTop, CONVERT_TWIPS|TOP_BORDER);
+ if( pBottom )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pBottom, CONVERT_TWIPS|BOTTOM_BORDER);
+ if( pDistance )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pDistance, CONVERT_TWIPS|BORDER_DISTANCE);
+ if( pLeftDistance )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pLeftDistance, CONVERT_TWIPS|LEFT_BORDER_DISTANCE);
+ if( pRightDistance )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pRightDistance, CONVERT_TWIPS|RIGHT_BORDER_DISTANCE);
+ if( pTopDistance )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pTopDistance, CONVERT_TWIPS|TOP_BORDER_DISTANCE);
+ if( pBottomDistance )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pBottomDistance, CONVERT_TWIPS|BOTTOM_BORDER_DISTANCE);
+ if( pLineStyle )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pLineStyle, LINE_STYLE);
+ if( pLineWidth )
+ bRet &= static_cast<SfxPoolItem&>(aBox).PutValue(*pLineWidth, LINE_WIDTH|CONVERT_TWIPS);
+ rToSet.Put(aBox);
+ }
+ {
+ const ::uno::Any* pRelH = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_REL_HEIGHT, pRelH);
+ const ::uno::Any* pRelHRelation = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_REL_HEIGHT_RELATION, pRelHRelation);
+ const ::uno::Any* pRelW = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_REL_WIDTH, pRelW);
+ const ::uno::Any* pRelWRelation = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_REL_WIDTH_RELATION, pRelWRelation);
+ const ::uno::Any* pSyncWidth = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT, pSyncWidth);
+ const ::uno::Any* pSyncHeight = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH, pSyncHeight);
+ const ::uno::Any* pWidth = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS, pWidth);
+ const ::uno::Any* pHeight = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS, pHeight);
+ const ::uno::Any* pSize = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_SIZE|CONVERT_TWIPS, pSize);
+ const ::uno::Any* pSizeType = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_SIZE_TYPE, pSizeType);
+ const ::uno::Any* pWidthType = nullptr;
+ GetProperty(RES_FRM_SIZE, MID_FRMSIZE_WIDTH_TYPE, pWidthType);
+ if( pWidth || pHeight ||pRelH || pRelHRelation || pRelW || pRelWRelation || pSize ||pSizeType ||
+ pWidthType ||pSyncWidth || pSyncHeight )
+ {
+ rSizeFound = true;
+ SwFormatFrameSize aFrameSz ( rFromSet.Get ( RES_FRM_SIZE ) );
+ if(pWidth)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pWidth, MID_FRMSIZE_WIDTH|CONVERT_TWIPS);
+ if(pHeight)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pHeight, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS);
+ if(pRelH )
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pRelH, MID_FRMSIZE_REL_HEIGHT);
+ if (pRelHRelation)
+ bRet &= aFrameSz.PutValue(*pRelHRelation, MID_FRMSIZE_REL_HEIGHT_RELATION);
+ if(pRelW )
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pRelW, MID_FRMSIZE_REL_WIDTH);
+ if (pRelWRelation)
+ bRet &= aFrameSz.PutValue(*pRelWRelation, MID_FRMSIZE_REL_WIDTH_RELATION);
+ if(pSyncWidth)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pSyncWidth, MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT);
+ if(pSyncHeight)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pSyncHeight, MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH);
+ if(pSize)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pSize, MID_FRMSIZE_SIZE|CONVERT_TWIPS);
+ if(pSizeType)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pSizeType, MID_FRMSIZE_SIZE_TYPE);
+ if(pWidthType)
+ bRet &= static_cast<SfxPoolItem&>(aFrameSz).PutValue(*pWidthType, MID_FRMSIZE_WIDTH_TYPE);
+ if(!aFrameSz.GetWidth())
+ aFrameSz.SetWidth(MINFLY);
+ if(!aFrameSz.GetHeight())
+ aFrameSz.SetHeight(MINFLY);
+ rToSet.Put(aFrameSz);
+ }
+ else
+ {
+ rSizeFound = false;
+ SwFormatFrameSize aFrameSz;
+ constexpr sal_Int32 constTwips_1cm = o3tl::toTwips(1, o3tl::Length::cm);
+ awt::Size aSize;
+ aSize.Width = constTwips_1cm;
+ aSize.Height = constTwips_1cm;
+ ::uno::Any aSizeVal;
+ aSizeVal <<= aSize;
+ static_cast<SfxPoolItem&>(aFrameSz).PutValue(aSizeVal, MID_FRMSIZE_SIZE|CONVERT_TWIPS);
+ rToSet.Put(aFrameSz);
+ }
+ }
+ const ::uno::Any* pFrameDirection = nullptr;
+ GetProperty(RES_FRAMEDIR, 0, pFrameDirection);
+ if(pFrameDirection)
+ {
+ SvxFrameDirectionItem aAttr(SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR);
+ aAttr.PutValue(*pFrameDirection, 0);
+ rToSet.Put(aAttr);
+ }
+ const ::uno::Any* pUnknown = nullptr;
+ GetProperty(RES_UNKNOWNATR_CONTAINER, 0, pUnknown);
+ if(pUnknown)
+ {
+ SvXMLAttrContainerItem aAttr(RES_UNKNOWNATR_CONTAINER);
+ aAttr.PutValue(*pUnknown, 0);
+ rToSet.Put(aAttr);
+ }
+
+ // #i18732#
+ const ::uno::Any* pFollowTextFlow = nullptr;
+ GetProperty(RES_FOLLOW_TEXT_FLOW, MID_FOLLOW_TEXT_FLOW, pFollowTextFlow);
+
+ if (pFollowTextFlow)
+ {
+ SwFormatFollowTextFlow aFormatFollowTextFlow;
+ if( pFollowTextFlow )
+ {
+ aFormatFollowTextFlow.PutValue(*pFollowTextFlow, MID_FOLLOW_TEXT_FLOW);
+ }
+
+ rToSet.Put(aFormatFollowTextFlow);
+ }
+
+ // #i28701# - RES_WRAP_INFLUENCE_ON_OBJPOS
+ const ::uno::Any* pWrapInfluenceOnObjPos = nullptr;
+ GetProperty(RES_WRAP_INFLUENCE_ON_OBJPOS, MID_WRAP_INFLUENCE, pWrapInfluenceOnObjPos);
+ const ::uno::Any* pAllowOverlap = nullptr;
+ GetProperty(RES_WRAP_INFLUENCE_ON_OBJPOS, MID_ALLOW_OVERLAP, pAllowOverlap);
+ if ( pWrapInfluenceOnObjPos || pAllowOverlap )
+ {
+ SwFormatWrapInfluenceOnObjPos aFormatWrapInfluenceOnObjPos;
+ if (pWrapInfluenceOnObjPos)
+ aFormatWrapInfluenceOnObjPos.PutValue( *pWrapInfluenceOnObjPos, MID_WRAP_INFLUENCE );
+ if (pAllowOverlap)
+ aFormatWrapInfluenceOnObjPos.PutValue( *pAllowOverlap, MID_ALLOW_OVERLAP );
+ rToSet.Put(aFormatWrapInfluenceOnObjPos);
+ }
+
+ {
+ const ::uno::Any* pTextVertAdjust = nullptr;
+ GetProperty(RES_TEXT_VERT_ADJUST, 0, pTextVertAdjust);
+ if ( pTextVertAdjust )
+ {
+ SdrTextVertAdjustItem aTextVertAdjust( rFromSet.Get ( RES_TEXT_VERT_ADJUST ) );
+ bRet &= static_cast<SfxPoolItem&>(aTextVertAdjust).PutValue(*pTextVertAdjust, 0);
+ rToSet.Put(aTextVertAdjust);
+ }
+ }
+
+ return bRet;
+}
+
+namespace {
+
+class SwFrameProperties_Impl : public BaseFrameProperties_Impl
+{
+public:
+ SwFrameProperties_Impl();
+
+ bool AnyToItemSet( SwDoc* pDoc, SfxItemSet& rFrameSet, SfxItemSet& rSet, bool& rSizeFound) override;
+};
+
+}
+
+SwFrameProperties_Impl::SwFrameProperties_Impl():
+ BaseFrameProperties_Impl(/*aSwMapProvider.GetPropertyMap(PROPERTY_MAP_TEXT_FRAME)*/ )
+{
+}
+
+static void lcl_FillCol ( SfxItemSet &rToSet, const ::SfxItemSet &rFromSet, const ::uno::Any *pAny)
+{
+ if ( pAny )
+ {
+ SwFormatCol aCol ( rFromSet.Get ( RES_COL ) );
+ static_cast<SfxPoolItem&>(aCol).PutValue( *pAny, MID_COLUMNS);
+ rToSet.Put(aCol);
+ }
+}
+
+bool SwFrameProperties_Impl::AnyToItemSet(SwDoc *pDoc, SfxItemSet& rSet, SfxItemSet&, bool& rSizeFound)
+{
+ // Properties for all frames
+ const ::uno::Any *pStyleName;
+ SwDocStyleSheet* pStyle = nullptr;
+ bool bRet;
+
+ if ( GetProperty ( FN_UNO_FRAME_STYLE_NAME, 0, pStyleName ) )
+ {
+ OUString sStyle;
+ *pStyleName >>= sStyle;
+ SwStyleNameMapper::FillUIName(sStyle, sStyle, SwGetPoolIdFromName::FrmFmt);
+ pStyle = static_cast<SwDocStyleSheet*>(pDoc->GetDocShell()->GetStyleSheetPool()->Find(sStyle,
+ SfxStyleFamily::Frame));
+ }
+
+ const ::uno::Any* pColumns = nullptr;
+ GetProperty (RES_COL, MID_COLUMNS, pColumns);
+ if ( pStyle )
+ {
+ rtl::Reference< SwDocStyleSheet > xStyle( new SwDocStyleSheet( *pStyle ) );
+ const ::SfxItemSet *pItemSet = &xStyle->GetItemSet();
+ bRet = FillBaseProperties( rSet, *pItemSet, rSizeFound );
+ lcl_FillCol ( rSet, *pItemSet, pColumns );
+ }
+ else
+ {
+ const ::SfxItemSet *pItemSet = &pDoc->getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_FRAME )->GetAttrSet();
+ bRet = FillBaseProperties( rSet, *pItemSet, rSizeFound );
+ lcl_FillCol ( rSet, *pItemSet, pColumns );
+ }
+ const ::uno::Any* pEdit;
+ if(GetProperty(RES_EDIT_IN_READONLY, 0, pEdit))
+ {
+ SwFormatEditInReadonly item(RES_EDIT_IN_READONLY);
+ item.PutValue(*pEdit, 0);
+ rSet.Put(item);
+ }
+ return bRet;
+}
+
+namespace {
+
+class SwGraphicProperties_Impl : public BaseFrameProperties_Impl
+{
+public:
+ SwGraphicProperties_Impl();
+
+ virtual bool AnyToItemSet( SwDoc* pDoc, SfxItemSet& rFrameSet, SfxItemSet& rSet, bool& rSizeFound) override;
+};
+
+}
+
+SwGraphicProperties_Impl::SwGraphicProperties_Impl( ) :
+ BaseFrameProperties_Impl(/*aSwMapProvider.GetPropertyMap(PROPERTY_MAP_TEXT_GRAPHIC)*/ )
+{
+}
+
+static void lcl_FillMirror ( SfxItemSet &rToSet, const ::SfxItemSet &rFromSet, const ::uno::Any *pHEvenMirror, const ::uno::Any *pHOddMirror, const ::uno::Any *pVMirror, bool &rRet )
+{
+ if(pHEvenMirror || pHOddMirror || pVMirror )
+ {
+ SwMirrorGrf aMirror ( rFromSet.Get ( RES_GRFATR_MIRRORGRF ) );
+ if(pHEvenMirror)
+ rRet &= static_cast<SfxPoolItem&>(aMirror).PutValue(*pHEvenMirror, MID_MIRROR_HORZ_EVEN_PAGES);
+ if(pHOddMirror)
+ rRet &= static_cast<SfxPoolItem&>(aMirror).PutValue(*pHOddMirror, MID_MIRROR_HORZ_ODD_PAGES);
+ if(pVMirror)
+ rRet &= static_cast<SfxPoolItem&>(aMirror).PutValue(*pVMirror, MID_MIRROR_VERT);
+ rToSet.Put(aMirror);
+ }
+}
+
+bool SwGraphicProperties_Impl::AnyToItemSet(
+ SwDoc* pDoc,
+ SfxItemSet& rFrameSet,
+ SfxItemSet& rGrSet,
+ bool& rSizeFound)
+{
+ // Properties for all frames
+ bool bRet;
+ const ::uno::Any *pStyleName;
+ SwDocStyleSheet* pStyle = nullptr;
+
+ if ( GetProperty ( FN_UNO_FRAME_STYLE_NAME, 0, pStyleName ) )
+ {
+ OUString sStyle;
+ *pStyleName >>= sStyle;
+ SwStyleNameMapper::FillUIName(sStyle, sStyle, SwGetPoolIdFromName::FrmFmt);
+ pStyle = static_cast<SwDocStyleSheet*>(pDoc->GetDocShell()->GetStyleSheetPool()->Find(sStyle,
+ SfxStyleFamily::Frame));
+ }
+
+ const ::uno::Any* pHEvenMirror = nullptr;
+ const ::uno::Any* pHOddMirror = nullptr;
+ const ::uno::Any* pVMirror = nullptr;
+ GetProperty(RES_GRFATR_MIRRORGRF, MID_MIRROR_HORZ_EVEN_PAGES, pHEvenMirror);
+ GetProperty(RES_GRFATR_MIRRORGRF, MID_MIRROR_HORZ_ODD_PAGES, pHOddMirror);
+ GetProperty(RES_GRFATR_MIRRORGRF, MID_MIRROR_VERT, pVMirror);
+
+ if ( pStyle )
+ {
+ rtl::Reference< SwDocStyleSheet > xStyle( new SwDocStyleSheet(*pStyle) );
+ const ::SfxItemSet *pItemSet = &xStyle->GetItemSet();
+ bRet = FillBaseProperties(rFrameSet, *pItemSet, rSizeFound);
+ lcl_FillMirror ( rGrSet, *pItemSet, pHEvenMirror, pHOddMirror, pVMirror, bRet );
+ }
+ else
+ {
+ const ::SfxItemSet *pItemSet = &pDoc->getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC )->GetAttrSet();
+ bRet = FillBaseProperties(rFrameSet, *pItemSet, rSizeFound);
+ lcl_FillMirror ( rGrSet, *pItemSet, pHEvenMirror, pHOddMirror, pVMirror, bRet );
+ }
+
+ static const ::sal_uInt16 nIDs[] =
+ {
+ RES_GRFATR_CROPGRF,
+ RES_GRFATR_ROTATION,
+ RES_GRFATR_LUMINANCE,
+ RES_GRFATR_CONTRAST,
+ RES_GRFATR_CHANNELR,
+ RES_GRFATR_CHANNELG,
+ RES_GRFATR_CHANNELB,
+ RES_GRFATR_GAMMA,
+ RES_GRFATR_INVERT,
+ RES_GRFATR_TRANSPARENCY,
+ RES_GRFATR_DRAWMODE,
+ 0
+ };
+ const ::uno::Any* pAny;
+ for(sal_Int16 nIndex = 0; nIDs[nIndex]; nIndex++)
+ {
+ sal_uInt8 nMId = RES_GRFATR_CROPGRF == nIDs[nIndex] ? CONVERT_TWIPS : 0;
+ if(GetProperty(nIDs[nIndex], nMId, pAny ))
+ {
+ std::unique_ptr<SfxPoolItem> pItem(::GetDfltAttr( nIDs[nIndex] )->Clone());
+ bRet &= pItem->PutValue(*pAny, nMId );
+ rGrSet.Put(std::move(pItem));
+ }
+ }
+
+ return bRet;
+}
+
+namespace {
+
+class SwOLEProperties_Impl : public SwFrameProperties_Impl
+{
+public:
+ SwOLEProperties_Impl() {}
+
+ virtual bool AnyToItemSet( SwDoc* pDoc, SfxItemSet& rFrameSet, SfxItemSet& rSet, bool& rSizeFound) override;
+};
+
+}
+
+bool SwOLEProperties_Impl::AnyToItemSet(
+ SwDoc* pDoc, SfxItemSet& rFrameSet, SfxItemSet& rSet, bool& rSizeFound)
+{
+ const ::uno::Any* pTemp;
+ if(!GetProperty(FN_UNO_CLSID, 0, pTemp) && !GetProperty(FN_UNO_STREAM_NAME, 0, pTemp)
+ && !GetProperty(FN_EMBEDDED_OBJECT, 0, pTemp)
+ && !GetProperty(FN_UNO_VISIBLE_AREA_WIDTH, 0, pTemp)
+ && !GetProperty(FN_UNO_VISIBLE_AREA_HEIGHT, 0, pTemp) )
+ return false;
+ SwFrameProperties_Impl::AnyToItemSet( pDoc, rFrameSet, rSet, rSizeFound);
+
+ return true;
+}
+
+class SwXFrame::Impl
+{
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+};
+
+const ::uno::Sequence< sal_Int8 > & SwXFrame::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXFrameUnoTunnelId;
+ return theSwXFrameUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXFrame::getSomething( const ::uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+
+OUString SwXFrame::getImplementationName()
+{
+ return "SwXFrame";
+}
+
+sal_Bool SwXFrame::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXFrame::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.BaseFrame", "com.sun.star.text.TextContent", "com.sun.star.document.LinkTarget" };
+}
+
+SwXFrame::SwXFrame(FlyCntType eSet, const ::SfxItemPropertySet* pSet, SwDoc *pDoc)
+ : m_pImpl(new Impl)
+ , m_pFrameFormat(nullptr)
+ , m_pPropSet(pSet)
+ , m_pDoc(pDoc)
+ , m_eType(eSet)
+ , m_bIsDescriptor(true)
+ , m_nDrawAspect(embed::Aspects::MSOLE_CONTENT)
+ , m_nVisibleAreaWidth(0)
+ , m_nVisibleAreaHeight(0)
+{
+ // Register ourselves as a listener to the document (via the page descriptor)
+ StartListening(pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+ // get the property set for the default style data
+ // First get the model
+ uno::Reference < XModel > xModel = pDoc->GetDocShell()->GetBaseModel();
+ // Ask the model for its family supplier interface
+ uno::Reference < XStyleFamiliesSupplier > xFamilySupplier ( xModel, uno::UNO_QUERY );
+ // Get the style families
+ uno::Reference < XNameAccess > xFamilies = xFamilySupplier->getStyleFamilies();
+ // Get the Frame family (and keep it for later)
+ const ::uno::Any aAny = xFamilies->getByName ("FrameStyles");
+ aAny >>= mxStyleFamily;
+ // In the derived class, we'll ask mxStyleFamily for the relevant default style
+ // mxStyleFamily is initialised in the SwXFrame constructor
+ switch(m_eType)
+ {
+ case FLYCNTTYPE_FRM:
+ {
+ uno::Any aAny2 = mxStyleFamily->getByName ("Frame");
+ aAny2 >>= mxStyleData;
+ m_pProps.reset(new SwFrameProperties_Impl);
+ }
+ break;
+ case FLYCNTTYPE_GRF:
+ {
+ uno::Any aAny2 = mxStyleFamily->getByName ("Graphics");
+ aAny2 >>= mxStyleData;
+ m_pProps.reset(new SwGraphicProperties_Impl);
+ }
+ break;
+ case FLYCNTTYPE_OLE:
+ {
+ uno::Any aAny2 = mxStyleFamily->getByName ("OLE");
+ aAny2 >>= mxStyleData;
+ m_pProps.reset(new SwOLEProperties_Impl);
+ }
+ break;
+
+ default:
+ m_pProps.reset();
+ break;
+ }
+}
+
+SwXFrame::SwXFrame(SwFrameFormat& rFrameFormat, FlyCntType eSet, const ::SfxItemPropertySet* pSet)
+ : m_pImpl(new Impl)
+ , m_pFrameFormat(&rFrameFormat)
+ , m_pPropSet(pSet)
+ , m_pDoc(nullptr)
+ , m_eType(eSet)
+ , m_bIsDescriptor(false)
+ , m_nDrawAspect(embed::Aspects::MSOLE_CONTENT)
+ , m_nVisibleAreaWidth(0)
+ , m_nVisibleAreaHeight(0)
+{
+ StartListening(rFrameFormat.GetNotifier());
+}
+
+SwXFrame::~SwXFrame()
+{
+ SolarMutexGuard aGuard;
+ m_pProps.reset();
+ EndListeningAll();
+}
+
+template<class Interface, class NameLookupIsHard>
+uno::Reference<Interface>
+SwXFrame::CreateXFrame(SwDoc & rDoc, SwFrameFormat *const pFrameFormat)
+{
+ assert(!pFrameFormat || &rDoc == pFrameFormat->GetDoc());
+ uno::Reference<Interface> xFrame;
+ if (pFrameFormat)
+ {
+ xFrame.set(pFrameFormat->GetXObject(), uno::UNO_QUERY); // cached?
+ }
+ if (!xFrame.is())
+ {
+ NameLookupIsHard *const pNew(pFrameFormat
+ ? new NameLookupIsHard(*pFrameFormat)
+ : new NameLookupIsHard(&rDoc));
+ xFrame.set(pNew);
+ if (pFrameFormat)
+ {
+ pFrameFormat->SetXObject(xFrame);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pNew->SwXFrame::m_pImpl->m_wThis = xFrame;
+ }
+ return xFrame;
+}
+
+OUString SwXFrame::getName()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ return pFormat->GetName();
+ if(!m_bIsDescriptor)
+ throw uno::RuntimeException();
+ return m_sName;
+}
+
+void SwXFrame::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ pFormat->GetDoc()->SetFlyName(static_cast<SwFlyFrameFormat&>(*pFormat), rName);
+ if(pFormat->GetName() != rName)
+ {
+ throw uno::RuntimeException("SwXFrame::setName(): Illegal object name. Duplicate name?");
+ }
+ }
+ else if(m_bIsDescriptor)
+ m_sName = rName;
+ else
+ throw uno::RuntimeException();
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXFrame::getPropertySetInfo()
+{
+ uno::Reference< beans::XPropertySetInfo > xRef;
+ static uno::Reference< beans::XPropertySetInfo > xFrameRef;
+ static uno::Reference< beans::XPropertySetInfo > xGrfRef;
+ static uno::Reference< beans::XPropertySetInfo > xOLERef;
+ switch(m_eType)
+ {
+ case FLYCNTTYPE_FRM:
+ if( !xFrameRef.is() )
+ xFrameRef = m_pPropSet->getPropertySetInfo();
+ xRef = xFrameRef;
+ break;
+ case FLYCNTTYPE_GRF:
+ if( !xGrfRef.is() )
+ xGrfRef = m_pPropSet->getPropertySetInfo();
+ xRef = xGrfRef;
+ break;
+ case FLYCNTTYPE_OLE:
+ if( !xOLERef.is() )
+ xOLERef = m_pPropSet->getPropertySetInfo();
+ xRef = xOLERef;
+ break;
+ default:
+ ;
+ }
+ return xRef;
+}
+
+SdrObject *SwXFrame::GetOrCreateSdrObject(SwFlyFrameFormat &rFormat)
+{
+ SdrObject* pObject = rFormat.FindSdrObject();
+ if( !pObject )
+ {
+ SwDoc *pDoc = rFormat.GetDoc();
+ // #i52858# - method name changed
+ SwFlyDrawContact* pContactObject(rFormat.GetOrCreateContact());
+ pObject = pContactObject->GetMaster();
+
+ const ::SwFormatSurround& rSurround = rFormat.GetSurround();
+ pObject->SetLayer(
+ ( css::text::WrapTextMode_THROUGH == rSurround.GetSurround() &&
+ !rFormat.GetOpaque().GetValue() ) ? pDoc->getIDocumentDrawModelAccess().GetHellId()
+ : pDoc->getIDocumentDrawModelAccess().GetHeavenId() );
+ SwDrawModel* pDrawModel = pDoc->getIDocumentDrawModelAccess().GetOrCreateDrawModel();
+ pDrawModel->GetPage(0)->InsertObject( pObject );
+ }
+
+ return pObject;
+}
+
+static SwFrameFormat *lcl_GetFrameFormat( const ::uno::Any& rValue, SwDoc *pDoc )
+{
+ SwFrameFormat *pRet = nullptr;
+ SwDocShell* pDocSh = pDoc->GetDocShell();
+ if(pDocSh)
+ {
+ OUString uTemp;
+ rValue >>= uTemp;
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uTemp, sStyle,
+ SwGetPoolIdFromName::FrmFmt);
+ SwDocStyleSheet* pStyle =
+ static_cast<SwDocStyleSheet*>(pDocSh->GetStyleSheetPool()->Find(sStyle,
+ SfxStyleFamily::Frame));
+ if(pStyle)
+ pRet = pStyle->GetFrameFormat();
+ }
+
+ return pRet;
+}
+
+void SwXFrame::setPropertyValue(const OUString& rPropertyName, const ::uno::Any& _rValue)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+
+ // Hack to support hidden property to transfer textDirection
+ if(rPropertyName == "FRMDirection")
+ {
+ if (pFormat)
+ {
+ SvxFrameDirectionItem aItem(SvxFrameDirection::Environment, RES_FRAMEDIR);
+ aItem.PutValue(_rValue, 0);
+ GetFrameFormat()->SetFormatAttr(aItem);
+ }
+ else if(IsDescriptor())
+ {
+ m_pProps->SetProperty(o3tl::narrowing<sal_uInt16>(RES_FRAMEDIR), 0, _rValue);
+ }
+ return;
+ }
+
+ const ::SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ {
+ // Hack to skip the dummy CursorNotIgnoreTables property
+ if (rPropertyName != "CursorNotIgnoreTables")
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast <cppu::OWeakObject*> (this));
+ return;
+ }
+
+ const sal_uInt8 nMemberId(pEntry->nMemberId);
+ uno::Any aValue(_rValue);
+
+ // check for needed metric translation
+ if(pEntry->nMoreFlags & PropertyMoreFlags::METRIC_ITEM)
+ {
+ bool bDoIt(true);
+
+ if(XATTR_FILLBMP_SIZEX == pEntry->nWID || XATTR_FILLBMP_SIZEY == pEntry->nWID)
+ {
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ sal_Int32 nValue = 0;
+
+ if(aValue >>= nValue)
+ {
+ bDoIt = nValue > 0;
+ }
+ }
+
+ if(bDoIt)
+ {
+ const SwDoc* pDoc = (IsDescriptor() ? m_pDoc : GetFrameFormat()->GetDoc());
+ const SfxItemPool& rPool = pDoc->GetAttrPool();
+ const MapUnit eMapUnit(rPool.GetMetric(pEntry->nWID));
+
+ if(eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertFromMM(eMapUnit, aValue);
+ }
+ }
+ }
+
+ if(pFormat)
+ {
+ bool bNextFrame = false;
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ if ( ((m_eType == FLYCNTTYPE_GRF) && isGRFATR(pEntry->nWID)) ||
+ (FN_PARAM_CONTOUR_PP == pEntry->nWID) ||
+ (FN_UNO_IS_AUTOMATIC_CONTOUR == pEntry->nWID) ||
+ (FN_UNO_IS_PIXEL_CONTOUR == pEntry->nWID) )
+ {
+ const ::SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwNoTextNode* pNoText = aIdx.GetNode().GetNoTextNode();
+ if(pEntry->nWID == FN_PARAM_CONTOUR_PP)
+ {
+ drawing::PointSequenceSequence aParam;
+ if(!aValue.hasValue())
+ pNoText->SetContour(nullptr);
+ else if(aValue >>= aParam)
+ {
+ tools::PolyPolygon aPoly(o3tl::narrowing<sal_uInt16>(aParam.getLength()));
+ for(const ::drawing::PointSequence& rPointSeq : std::as_const(aParam))
+ {
+ sal_Int32 nPoints = rPointSeq.getLength();
+ const ::awt::Point* pPoints = rPointSeq.getConstArray();
+ tools::Polygon aSet( o3tl::narrowing<sal_uInt16>(nPoints) );
+ for(sal_Int32 j = 0; j < nPoints; j++)
+ {
+ Point aPoint(pPoints[j].X, pPoints[j].Y);
+ aSet.SetPoint(aPoint, o3tl::narrowing<sal_uInt16>(j));
+ }
+ // Close polygon if it isn't closed already.
+ aSet.Optimize( PolyOptimizeFlags::CLOSE );
+ aPoly.Insert( aSet );
+ }
+ pNoText->SetContourAPI( &aPoly );
+ }
+ else
+ throw lang::IllegalArgumentException();
+ }
+ else if(pEntry->nWID == FN_UNO_IS_AUTOMATIC_CONTOUR )
+ {
+ pNoText->SetAutomaticContour( *o3tl::doAccess<bool>(aValue) );
+ }
+ else if(pEntry->nWID == FN_UNO_IS_PIXEL_CONTOUR )
+ {
+ // The IsPixelContour property can only be set if there
+ // is no contour, or if the contour has been set by the
+ // API itself (or in other words, if the contour isn't
+ // used already).
+ if( pNoText->HasContour_() && pNoText->IsContourMapModeValid() )
+ throw lang::IllegalArgumentException();
+
+ pNoText->SetPixelContour( *o3tl::doAccess<bool>(aValue) );
+
+ }
+ else
+ {
+ SfxItemSet aSet(pNoText->GetSwAttrSet());
+ m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
+ pNoText->SetAttr(aSet);
+ }
+ }
+ }
+ // New attribute Title
+ else if( FN_UNO_TITLE == pEntry->nWID )
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ OUString sTitle;
+ aValue >>= sTitle;
+ // assure that <SdrObject> instance exists.
+ GetOrCreateSdrObject(rFlyFormat);
+ rFlyFormat.GetDoc()->SetFlyFrameTitle(rFlyFormat, sTitle);
+ }
+ else if (pEntry->nWID == FN_UNO_TOOLTIP)
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ OUString sTooltip;
+ aValue >>= sTooltip;
+ rFlyFormat.SetObjTooltip(sTooltip);
+ }
+ // New attribute Description
+ else if( FN_UNO_DESCRIPTION == pEntry->nWID )
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ OUString sDescription;
+ aValue >>= sDescription;
+ // assure that <SdrObject> instance exists.
+ GetOrCreateSdrObject(rFlyFormat);
+ rFlyFormat.GetDoc()->SetFlyFrameDescription(rFlyFormat, sDescription);
+ }
+ else if(FN_UNO_FRAME_STYLE_NAME == pEntry->nWID)
+ {
+ SwFrameFormat *pFrameFormat = lcl_GetFrameFormat( aValue, pFormat->GetDoc() );
+ if( !pFrameFormat )
+ throw lang::IllegalArgumentException();
+
+ UnoActionContext aAction(pFormat->GetDoc());
+
+ std::optional<SfxItemSet> pSet;
+ // #i31771#, #i25798# - No adjustment of
+ // anchor ( no call of method <sw_ChkAndSetNewAnchor(..)> ),
+ // if document is currently in reading mode.
+ if ( !pFormat->GetDoc()->IsInReading() )
+ {
+ // see SwFEShell::SetFrameFormat( SwFrameFormat *pNewFormat, bool bKeepOrient, Point* pDocPos )
+ SwFlyFrame *pFly = nullptr;
+ if (auto pFlyFrameFormat = dynamic_cast<const SwFlyFrameFormat*>(pFormat) )
+ pFly = pFlyFrameFormat->GetFrame();
+ if ( pFly )
+ {
+ if( const SwFormatAnchor* pItem = pFrameFormat->GetItemIfSet( RES_ANCHOR, false ))
+ {
+ pSet.emplace( pDoc->GetAttrPool(), aFrameFormatSetRange );
+ pSet->Put( *pItem );
+ if ( pFormat->GetDoc()->GetEditShell() != nullptr
+ && !sw_ChkAndSetNewAnchor( *pFly, *pSet ) )
+ {
+ pSet.reset();
+ }
+ }
+ }
+ }
+
+ pFormat->GetDoc()->SetFrameFormatToFly( *pFormat, *pFrameFormat, pSet ? &*pSet : nullptr );
+ }
+ else if (FN_UNO_GRAPHIC_FILTER == pEntry->nWID)
+ {
+ OUString sGrfName;
+ OUString sFltName;
+ SwDoc::GetGrfNms( *static_cast<SwFlyFrameFormat*>(pFormat), &sGrfName, &sFltName );
+ aValue >>= sFltName;
+ UnoActionContext aAction(pFormat->GetDoc());
+ const ::SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if (pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwGrfNode* pGrfNode = aIdx.GetNode().GetGrfNode();
+ if(!pGrfNode)
+ {
+ throw uno::RuntimeException();
+ }
+ SwPaM aGrfPaM(*pGrfNode);
+ pFormat->GetDoc()->getIDocumentContentOperations().ReRead(aGrfPaM, sGrfName, sFltName, nullptr);
+ }
+ }
+ else if (FN_UNO_GRAPHIC == pEntry->nWID || FN_UNO_GRAPHIC_URL == pEntry->nWID)
+ {
+ Graphic aGraphic;
+ if (aValue.has<OUString>())
+ {
+ OUString aURL = aValue.get<OUString>();
+ if (!aURL.isEmpty())
+ {
+ aGraphic = vcl::graphic::loadFromURL(aURL);
+ }
+ }
+ else if (aValue.has<uno::Reference<graphic::XGraphic>>())
+ {
+ uno::Reference<graphic::XGraphic> xGraphic = aValue.get<uno::Reference<graphic::XGraphic>>();
+ if (xGraphic.is())
+ {
+ aGraphic = Graphic(xGraphic);
+ }
+ }
+
+ if (!aGraphic.IsNone())
+ {
+ const ::SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if (pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwGrfNode* pGrfNode = aIdx.GetNode().GetGrfNode();
+ if (!pGrfNode)
+ {
+ throw uno::RuntimeException();
+ }
+ SwPaM aGrfPaM(*pGrfNode);
+ pFormat->GetDoc()->getIDocumentContentOperations().ReRead(aGrfPaM, OUString(), OUString(), &aGraphic);
+ }
+ }
+ }
+ else if (FN_UNO_REPLACEMENT_GRAPHIC == pEntry->nWID || FN_UNO_REPLACEMENT_GRAPHIC_URL == pEntry->nWID)
+ {
+ Graphic aGraphic;
+ if (aValue.has<OUString>())
+ {
+ OUString aURL = aValue.get<OUString>();
+ if (!aURL.isEmpty())
+ {
+ aGraphic = vcl::graphic::loadFromURL(aURL);
+ }
+ }
+ else if (aValue.has<uno::Reference<graphic::XGraphic>>())
+ {
+ uno::Reference<graphic::XGraphic> xGraphic = aValue.get<uno::Reference<graphic::XGraphic>>();
+ if (xGraphic.is())
+ {
+ aGraphic = Graphic(xGraphic);
+ }
+ }
+
+ if (!aGraphic.IsNone())
+ {
+ const ::SwFormatContent* pCnt = &pFormat->GetContent();
+ if ( pCnt->GetContentIdx() && pDoc->GetNodes()[ pCnt->GetContentIdx()->GetIndex() + 1 ] )
+ {
+ SwOLENode* pOleNode = pDoc->GetNodes()[ pCnt->GetContentIdx()->GetIndex() + 1 ]->GetOLENode();
+
+ if ( pOleNode )
+ {
+ svt::EmbeddedObjectRef &rEmbeddedObject = pOleNode->GetOLEObj().GetObject();
+ rEmbeddedObject.SetGraphic(aGraphic, OUString() );
+ }
+ }
+ }
+ }
+ else if((bNextFrame = (rPropertyName == UNO_NAME_CHAIN_NEXT_NAME))
+ || rPropertyName == UNO_NAME_CHAIN_PREV_NAME)
+ {
+ OUString sChainName;
+ aValue >>= sChainName;
+ if (sChainName.isEmpty())
+ {
+ if(bNextFrame)
+ pDoc->Unchain(*pFormat);
+ else
+ {
+ const SwFormatChain& aChain( pFormat->GetChain() );
+ SwFrameFormat *pPrev = aChain.GetPrev();
+ if(pPrev)
+ pDoc->Unchain(*pPrev);
+ }
+ }
+ else
+ {
+ const size_t nCount = pDoc->GetFlyCount(FLYCNTTYPE_FRM);
+
+ SwFrameFormat* pChain = nullptr;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SwFrameFormat* pFormat2 = pDoc->GetFlyNum(i, FLYCNTTYPE_FRM);
+ if(sChainName == pFormat2->GetName() )
+ {
+ pChain = pFormat2;
+ break;
+ }
+ }
+ if(pChain)
+ {
+ SwFrameFormat* pSource = bNextFrame ? pFormat : pChain;
+ SwFrameFormat* pDest = bNextFrame ? pChain: pFormat;
+ pDoc->Chain(*pSource, *pDest);
+ }
+ }
+ }
+ else if(FN_UNO_Z_ORDER == pEntry->nWID)
+ {
+ sal_Int32 nZOrder = - 1;
+ aValue >>= nZOrder;
+
+ // Don't set an explicit ZOrder on TextBoxes.
+ if( nZOrder >= 0 && !SwTextBoxHelper::isTextBox(pFormat, RES_FLYFRMFMT) )
+ {
+ SdrObject* pObject =
+ GetOrCreateSdrObject( static_cast<SwFlyFrameFormat&>(*pFormat) );
+ SwDrawModel *pDrawModel = pDoc->getIDocumentDrawModelAccess().GetDrawModel();
+ pDrawModel->GetPage(0)->
+ SetObjectOrdNum(pObject->GetOrdNum(), nZOrder);
+ }
+ }
+ else if(RES_ANCHOR == pEntry->nWID && MID_ANCHOR_ANCHORFRAME == nMemberId)
+ {
+ bool bDone = false;
+ uno::Reference<text::XTextFrame> xFrame;
+ if(aValue >>= xFrame)
+ {
+ SwXFrame* pFrame = comphelper::getFromUnoTunnel<SwXFrame>(xFrame);
+ if(pFrame && this != pFrame && pFrame->GetFrameFormat() && pFrame->GetFrameFormat()->GetDoc() == pDoc)
+ {
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aSet( pDoc->GetAttrPool() );
+ aSet.SetParent(&pFormat->GetAttrSet());
+ SwFormatAnchor aAnchor = static_cast<const SwFormatAnchor&>(aSet.Get(pEntry->nWID));
+
+ SwPosition aPos(*pFrame->GetFrameFormat()->GetContent().GetContentIdx());
+ aAnchor.SetAnchor(&aPos);
+ aAnchor.SetType(RndStdIds::FLY_AT_FLY);
+ aSet.Put(aAnchor);
+ pDoc->SetFlyFrameAttr( *pFormat, aSet );
+ bDone = true;
+ }
+ }
+ if(!bDone)
+ throw lang::IllegalArgumentException();
+ }
+ else
+ {
+ // standard UNO API write attributes
+ // adapt former attr from SvxBrushItem::PutValue to new items XATTR_FILL_FIRST, XATTR_FILL_LAST
+ SfxItemSetFixed
+ <RES_FRMATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER,
+
+ // FillAttribute support
+ XATTR_FILL_FIRST, XATTR_FILL_LAST>
+ aSet( pDoc->GetAttrPool());
+ bool bDone(false);
+
+ aSet.SetParent(&pFormat->GetAttrSet());
+
+ if(RES_BACKGROUND == pEntry->nWID)
+ {
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND, true, pDoc->IsInXMLImport()));
+ std::unique_ptr<SvxBrushItem> aChangedBrushItem(aOriginalBrushItem->Clone());
+
+ aChangedBrushItem->PutValue(aValue, nMemberId);
+
+ if(*aChangedBrushItem != *aOriginalBrushItem)
+ {
+ setSvxBrushItemAsFillAttributesToTargetSet(*aChangedBrushItem, aSet);
+ pFormat->GetDoc()->SetFlyFrameAttr( *pFormat, aSet );
+ }
+
+ bDone = true;
+ }
+ else if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID)
+ {
+ drawing::BitmapMode eMode;
+
+ if(!(aValue >>= eMode))
+ {
+ sal_Int32 nMode = 0;
+
+ if(!(aValue >>= nMode))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ eMode = static_cast<drawing::BitmapMode>(nMode);
+ }
+
+ aSet.Put(XFillBmpStretchItem(drawing::BitmapMode_STRETCH == eMode));
+ aSet.Put(XFillBmpTileItem(drawing::BitmapMode_REPEAT == eMode));
+ pFormat->GetDoc()->SetFlyFrameAttr( *pFormat, aSet );
+ bDone = true;
+ }
+
+ switch(nMemberId)
+ {
+ case MID_NAME:
+ {
+ // when named items get set, replace these with the NameOrIndex items
+ // which exist already in the pool
+ switch(pEntry->nWID)
+ {
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ OUString aTempName;
+
+ if(!(aValue >>= aTempName ))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ bDone = SvxShape::SetFillAttribute(pEntry->nWID, aTempName, aSet);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ break;
+ }
+ case MID_BITMAP:
+ {
+ switch(pEntry->nWID)
+ {
+ case XATTR_FILLBITMAP:
+ {
+ const Graphic aNullGraphic;
+ XFillBitmapItem aXFillBitmapItem(aNullGraphic);
+
+ aXFillBitmapItem.PutValue(aValue, nMemberId);
+ aSet.Put(aXFillBitmapItem);
+ bDone = true;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if(!bDone)
+ {
+ m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
+ }
+
+ if(RES_ANCHOR == pEntry->nWID && MID_ANCHOR_ANCHORTYPE == nMemberId)
+ {
+ SwFormatAnchor aAnchor = static_cast<const SwFormatAnchor&>(aSet.Get(pEntry->nWID));
+ if(aAnchor.GetAnchorId() == RndStdIds::FLY_AT_FLY)
+ {
+ const ::SwPosition* pPosition = aAnchor.GetContentAnchor();
+ SwFrameFormat* pFlyFormat = pPosition ? pPosition->nNode.GetNode().GetFlyFormat() : nullptr;
+ if(!pFlyFormat || pFlyFormat->Which() == RES_DRAWFRMFMT)
+ {
+ lang::IllegalArgumentException aExcept;
+ aExcept.Message = "Anchor to frame: no frame found";
+ throw aExcept;
+ }
+ else
+ {
+ SwPosition aPos = *pPosition;
+ aPos.nNode = *pFlyFormat->GetContent().GetContentIdx();
+ aAnchor.SetAnchor(&aPos);
+ aSet.Put(aAnchor);
+ }
+ }
+ else if ((aAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE) &&
+ !aAnchor.GetContentAnchor())
+ {
+ SwNode& rNode = pDoc->GetNodes().GetEndOfContent();
+ SwPaM aPam(rNode);
+ aPam.Move( fnMoveBackward, GoInDoc );
+ aAnchor.SetAnchor( aPam.Start() );
+ aSet.Put(aAnchor);
+ }
+
+ // #i31771#, #i25798# - No adjustment of
+ // anchor ( no call of method <sw_ChkAndSetNewAnchor(..)> ),
+ // if document is currently in reading mode.
+ if ( !pFormat->GetDoc()->IsInReading() )
+ {
+ // see SwFEShell::SetFlyFrameAttr( SfxItemSet& rSet )
+ SwFlyFrame *pFly = nullptr;
+ if (auto pFrameFormat = dynamic_cast<SwFlyFrameFormat*>( pFormat) )
+ pFly = pFrameFormat->GetFrame();
+ if (pFly)
+ {
+ if( const SwFormatAnchor* pItem = aSet.GetItemIfSet( RES_ANCHOR, false ))
+ {
+ aSet.Put( *pItem );
+ if ( pFormat->GetDoc()->GetEditShell() != nullptr )
+ {
+ sw_ChkAndSetNewAnchor( *pFly, aSet );
+ }
+ }
+ }
+ }
+
+ pFormat->GetDoc()->SetFlyFrameAttr( *pFormat, aSet );
+ }
+ else if(FN_UNO_CLSID == pEntry->nWID || FN_UNO_STREAM_NAME == pEntry->nWID || FN_EMBEDDED_OBJECT == pEntry->nWID)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ else
+ {
+ pFormat->SetFormatAttr(aSet);
+ }
+ }
+ }
+ else if(IsDescriptor())
+ {
+ m_pProps->SetProperty(pEntry->nWID, nMemberId, aValue);
+ if( FN_UNO_FRAME_STYLE_NAME == pEntry->nWID )
+ {
+ OUString sStyleName;
+ aValue >>= sStyleName;
+ try
+ {
+ uno::Any aAny = mxStyleFamily->getByName ( sStyleName );
+ aAny >>= mxStyleData;
+ }
+ catch ( container::NoSuchElementException const & )
+ {
+ }
+ catch ( lang::WrappedTargetException const & )
+ {
+ }
+ catch ( uno::RuntimeException const & )
+ {
+ }
+ }
+ else if (FN_UNO_DRAW_ASPECT == pEntry->nWID)
+ {
+ OUString sAspect = "";
+ aValue >>= sAspect;
+
+ if (sAspect == "Icon")
+ m_nDrawAspect = embed::Aspects::MSOLE_ICON;
+ else if (sAspect == "Content")
+ m_nDrawAspect = embed::Aspects::MSOLE_CONTENT;
+ }
+ else if (FN_UNO_VISIBLE_AREA_WIDTH == pEntry->nWID)
+ {
+ OUString sAspect = "";
+ aValue >>= sAspect;
+ m_nVisibleAreaWidth = sAspect.toInt64();
+ }
+ else if (FN_UNO_VISIBLE_AREA_HEIGHT == pEntry->nWID)
+ {
+ OUString sAspect = "";
+ aValue >>= sAspect;
+ m_nVisibleAreaHeight = sAspect.toInt64();
+ }
+ }
+ else
+ throw uno::RuntimeException();
+}
+
+namespace
+{
+/// Redirect error popups to developer warnings for the duration of the UNO API call.
+class DisplayLockGuard
+{
+ bool m_bLock;
+
+public:
+ DisplayLockGuard()
+ {
+ m_bLock = ErrorRegistry::GetLock();
+ ErrorRegistry::SetLock(true);
+ }
+
+ ~DisplayLockGuard() { ErrorRegistry::SetLock(m_bLock); }
+};
+}
+
+uno::Any SwXFrame::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ DisplayLockGuard aDisplayGuard;
+ uno::Any aAny;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ const sal_uInt8 nMemberId(pEntry->nMemberId);
+
+ if(FN_UNO_ANCHOR_TYPES == pEntry->nWID)
+ {
+ uno::Sequence<text::TextContentAnchorType> aTypes
+ {
+ text::TextContentAnchorType_AT_PARAGRAPH,
+ text::TextContentAnchorType_AS_CHARACTER,
+ text::TextContentAnchorType_AT_PAGE,
+ text::TextContentAnchorType_AT_FRAME,
+ text::TextContentAnchorType_AT_CHARACTER
+ };
+ aAny <<= aTypes;
+ }
+ else if(pFormat)
+ {
+ if( ((m_eType == FLYCNTTYPE_GRF) || (m_eType == FLYCNTTYPE_OLE)) &&
+ (isGRFATR(pEntry->nWID) ||
+ pEntry->nWID == FN_PARAM_CONTOUR_PP ||
+ pEntry->nWID == FN_UNO_IS_AUTOMATIC_CONTOUR ||
+ pEntry->nWID == FN_UNO_IS_PIXEL_CONTOUR ))
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwNoTextNode* pNoText = aIdx.GetNode().GetNoTextNode();
+ if(pEntry->nWID == FN_PARAM_CONTOUR_PP)
+ {
+ tools::PolyPolygon aContour;
+ if( pNoText->GetContourAPI( aContour ) )
+ {
+ drawing::PointSequenceSequence aPtSeq(aContour.Count());
+ drawing::PointSequence* pPSeq = aPtSeq.getArray();
+ for(sal_uInt16 i = 0; i < aContour.Count(); i++)
+ {
+ const tools::Polygon& rPoly = aContour.GetObject(i);
+ pPSeq[i].realloc(rPoly.GetSize());
+ awt::Point* pPoints = pPSeq[i].getArray();
+ for(sal_uInt16 j = 0; j < rPoly.GetSize(); j++)
+ {
+ const Point& rPoint = rPoly.GetPoint(j);
+ pPoints[j].X = rPoint.X();
+ pPoints[j].Y = rPoint.Y();
+ }
+ }
+ aAny <<= aPtSeq;
+ }
+ }
+ else if(pEntry->nWID == FN_UNO_IS_AUTOMATIC_CONTOUR )
+ {
+ aAny <<= pNoText->HasAutomaticContour();
+ }
+ else if(pEntry->nWID == FN_UNO_IS_PIXEL_CONTOUR )
+ {
+ aAny <<= pNoText->IsPixelContour();
+ }
+ else
+ {
+ const SfxItemSet& aSet(pNoText->GetSwAttrSet());
+ m_pPropSet->getPropertyValue(*pEntry, aSet, aAny);
+ }
+ }
+ }
+ else if (FN_UNO_REPLACEMENT_GRAPHIC == pEntry->nWID)
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ if (pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwGrfNode* pGrfNode = aIdx.GetNode().GetGrfNode();
+ if (!pGrfNode)
+ throw uno::RuntimeException();
+
+ const GraphicObject* pGraphicObject = pGrfNode->GetReplacementGrfObj();
+
+ if (pGraphicObject)
+ {
+ xGraphic = pGraphicObject->GetGraphic().GetXGraphic();
+ }
+ }
+ aAny <<= xGraphic;
+ }
+ else if( FN_UNO_GRAPHIC_FILTER == pEntry->nWID )
+ {
+ OUString sFltName;
+ SwDoc::GetGrfNms( *static_cast<SwFlyFrameFormat*>(pFormat), nullptr, &sFltName );
+ aAny <<= sFltName;
+ }
+ else if( FN_UNO_GRAPHIC_URL == pEntry->nWID )
+ {
+ throw uno::RuntimeException("Getting from this property is not supported");
+ }
+ else if( FN_UNO_GRAPHIC == pEntry->nWID )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwGrfNode* pGrfNode = aIdx.GetNode().GetGrfNode();
+ if(!pGrfNode)
+ throw uno::RuntimeException();
+ aAny <<= pGrfNode->GetGrf().GetXGraphic();
+ }
+ }
+ else if( FN_UNO_TRANSFORMED_GRAPHIC == pEntry->nWID
+ || FN_UNO_GRAPHIC_PREVIEW == pEntry->nWID )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwGrfNode* pGrfNode = aIdx.GetNode().GetGrfNode();
+ if(!pGrfNode)
+ throw uno::RuntimeException();
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ if (pDoc)
+ {
+ if (const SwEditShell* pEditShell = pDoc->GetEditShell())
+ {
+ SwFrame* pCurrFrame = pEditShell->GetCurrFrame(false);
+ GraphicAttr aGraphicAttr;
+ pGrfNode->GetGraphicAttr( aGraphicAttr, pCurrFrame );
+ const GraphicObject aGraphicObj = pGrfNode->GetGrfObj();
+
+ awt::Size aFrameSize = getSize();
+ Size aSize100thmm(aFrameSize.Width, aFrameSize.Height);
+ Size aSize = OutputDevice::LogicToLogic(aSize100thmm, MapMode(MapUnit::Map100thMM), aGraphicObj.GetPrefMapMode());
+
+ if (FN_UNO_GRAPHIC_PREVIEW == pEntry->nWID)
+ {
+ double fX = static_cast<double>(aSize.getWidth()) / 1280;
+ double fY = static_cast<double>(aSize.getHeight()) / 720;
+ double fFactor = fX > fY ? fX : fY;
+ if (fFactor > 1.0)
+ {
+ aSize.setWidth(aSize.getWidth() / fFactor);
+ aSize.setHeight(aSize.getHeight() / fFactor);
+ }
+ }
+
+ Graphic aGraphic = aGraphicObj.GetTransformedGraphic(aSize, aGraphicObj.GetPrefMapMode(), aGraphicAttr);
+ aAny <<= aGraphic.GetXGraphic();
+ }
+ }
+ }
+ }
+ else if(FN_UNO_FRAME_STYLE_NAME == pEntry->nWID)
+ {
+ aAny <<= SwStyleNameMapper::GetProgName(pFormat->DerivedFrom()->GetName(), SwGetPoolIdFromName::FrmFmt );
+ }
+ // #i73249#
+ else if( FN_UNO_TITLE == pEntry->nWID )
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ // assure that <SdrObject> instance exists.
+ GetOrCreateSdrObject(rFlyFormat);
+ aAny <<= rFlyFormat.GetObjTitle();
+ }
+ else if (pEntry->nWID == FN_UNO_TOOLTIP)
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ aAny <<= rFlyFormat.GetObjTooltip();
+ }
+ // New attribute Description
+ else if( FN_UNO_DESCRIPTION == pEntry->nWID )
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ // assure that <SdrObject> instance exists.
+ GetOrCreateSdrObject(rFlyFormat);
+ aAny <<= rFlyFormat.GetObjDescription();
+ }
+ else if(m_eType == FLYCNTTYPE_GRF &&
+ (rPropertyName == UNO_NAME_ACTUAL_SIZE))
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ Size aActSize = aIdx.GetNode().GetNoTextNode()->GetTwipSize();
+ awt::Size aTmp;
+ aTmp.Width = convertTwipToMm100(aActSize.Width());
+ aTmp.Height = convertTwipToMm100(aActSize.Height());
+ aAny <<= aTmp;
+ }
+ }
+ else if(FN_PARAM_LINK_DISPLAY_NAME == pEntry->nWID)
+ {
+ aAny <<= pFormat->GetName();
+ }
+ else if(FN_UNO_Z_ORDER == pEntry->nWID)
+ {
+ const SdrObject* pObj = pFormat->FindRealSdrObject();
+ if( pObj == nullptr )
+ pObj = pFormat->FindSdrObject();
+ if( pObj )
+ {
+ aAny <<= static_cast<sal_Int32>(pObj->GetOrdNum());
+ }
+ }
+ else if(FN_UNO_CLSID == pEntry->nWID || FN_UNO_MODEL == pEntry->nWID||
+ FN_UNO_COMPONENT == pEntry->nWID ||FN_UNO_STREAM_NAME == pEntry->nWID||
+ FN_EMBEDDED_OBJECT == pEntry->nWID)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ const SwFormatContent* pCnt = &pFormat->GetContent();
+ OSL_ENSURE( pCnt->GetContentIdx() &&
+ pDoc->GetNodes()[ pCnt->GetContentIdx()->
+ GetIndex() + 1 ]->GetOLENode(), "no OLE-Node?");
+
+ SwOLENode* pOleNode = pDoc->GetNodes()[ pCnt->GetContentIdx()
+ ->GetIndex() + 1 ]->GetOLENode();
+ uno::Reference < embed::XEmbeddedObject > xIP = pOleNode->GetOLEObj().GetOleRef();
+ OUString aHexCLSID;
+ {
+ SvGlobalName aClassName( xIP->getClassID() );
+ aHexCLSID = aClassName.GetHexName();
+ if(FN_UNO_CLSID != pEntry->nWID)
+ {
+ if ( svt::EmbeddedObjectRef::TryRunningState( xIP ) )
+ {
+ uno::Reference < lang::XComponent > xComp( xIP->getComponent(), uno::UNO_QUERY );
+ uno::Reference < frame::XModel > xModel( xComp, uno::UNO_QUERY );
+ if ( FN_EMBEDDED_OBJECT == pEntry->nWID )
+ {
+ // when exposing the EmbeddedObject, ensure it has a client site
+ OSL_ENSURE( pDoc->GetDocShell(), "no doc shell => no client site" );
+ if ( pDoc->GetDocShell() )
+ pDoc->GetDocShell()->GetIPClient( svt::EmbeddedObjectRef( xIP, embed::Aspects::MSOLE_CONTENT ) );
+ aAny <<= xIP;
+ }
+ else if ( xModel.is() )
+ aAny <<= xModel;
+ else if ( FN_UNO_COMPONENT == pEntry->nWID )
+ aAny <<= xComp;
+ }
+ }
+ }
+
+ if(FN_UNO_CLSID == pEntry->nWID)
+ aAny <<= aHexCLSID;
+ else if(FN_UNO_STREAM_NAME == pEntry->nWID)
+ {
+ aAny <<= pOleNode->GetOLEObj().GetCurrentPersistName();
+ }
+ else if(FN_EMBEDDED_OBJECT == pEntry->nWID)
+ {
+ aAny <<= pOleNode->GetOLEObj().GetOleRef();
+ }
+ }
+ else if(WID_LAYOUT_SIZE == pEntry->nWID)
+ {
+ // format document completely in order to get correct value (no EditShell for ole embedded case)
+ if (SwEditShell* pEditShell = pFormat->GetDoc()->GetEditShell())
+ pEditShell->CalcLayout();
+
+ SwFrame* pTmpFrame = SwIterator<SwFrame,SwFormat>( *pFormat ).First();
+ if ( pTmpFrame )
+ {
+ OSL_ENSURE( pTmpFrame->isFrameAreaDefinitionValid(), "frame not valid" );
+ const SwRect &rRect = pTmpFrame->getFrameArea();
+ Size aMM100Size = o3tl::convert(
+ Size( rRect.Width(), rRect.Height() ),
+ o3tl::Length::twip, o3tl::Length::mm100 );
+ aAny <<= awt::Size( aMM100Size.Width(), aMM100Size.Height() );
+ }
+ }
+ else if(pEntry->nWID == FN_UNO_PARENT_TEXT)
+ {
+ if (!m_xParentText.is())
+ {
+ const SwPosition* pContentAnchor = pFormat->GetAnchor().GetContentAnchor();
+ if (pContentAnchor)
+ {
+ m_xParentText = sw::CreateParentXText(*pFormat->GetDoc(), *pContentAnchor);
+ }
+ }
+ aAny <<= m_xParentText;
+ }
+ else
+ {
+ // standard UNO API read attributes
+ // adapt former attr from SvxBrushItem::PutValue to new items XATTR_FILL_FIRST, XATTR_FILL_LAST
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ bool bDone(false);
+
+ if(RES_BACKGROUND == pEntry->nWID)
+ {
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
+
+ if(!aOriginalBrushItem->QueryValue(aAny, nMemberId))
+ {
+ OSL_ENSURE(false, "Error getting attribute from RES_BACKGROUND (!)");
+ }
+
+ bDone = true;
+ }
+ else if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID)
+ {
+ if (rSet.Get(XATTR_FILLBMP_TILE).GetValue())
+ {
+ aAny <<= drawing::BitmapMode_REPEAT;
+ }
+ else if (rSet.Get(XATTR_FILLBMP_STRETCH).GetValue())
+ {
+ aAny <<= drawing::BitmapMode_STRETCH;
+ }
+ else
+ {
+ aAny <<= drawing::BitmapMode_NO_REPEAT;
+ }
+
+ bDone = true;
+ }
+
+ if(!bDone)
+ {
+ m_pPropSet->getPropertyValue(*pEntry, rSet, aAny);
+ }
+ }
+ }
+ else if(IsDescriptor())
+ {
+ if ( ! m_pDoc )
+ throw uno::RuntimeException();
+ if(WID_LAYOUT_SIZE != pEntry->nWID) // there is no LayoutSize in a descriptor
+ {
+ const uno::Any* pAny = nullptr;
+ if (!m_pProps->GetProperty(pEntry->nWID, nMemberId, pAny))
+ aAny = mxStyleData->getPropertyValue( rPropertyName );
+ else if ( pAny )
+ aAny = *pAny;
+ }
+ }
+ else
+ throw uno::RuntimeException();
+
+ if (pEntry->aType == ::cppu::UnoType<sal_Int16>::get() && pEntry->aType != aAny.getValueType())
+ {
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ sal_Int32 nValue = 0;
+ aAny >>= nValue;
+ aAny <<= static_cast<sal_Int16>(nValue);
+ }
+
+ // check for needed metric translation
+ if(pEntry->nMoreFlags & PropertyMoreFlags::METRIC_ITEM)
+ {
+ bool bDoIt(true);
+
+ if(XATTR_FILLBMP_SIZEX == pEntry->nWID || XATTR_FILLBMP_SIZEY == pEntry->nWID)
+ {
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ sal_Int32 nValue = 0;
+
+ if(aAny >>= nValue)
+ {
+ bDoIt = nValue > 0;
+ }
+ }
+
+ if(bDoIt)
+ {
+ const SwDoc* pDoc = (IsDescriptor() ? m_pDoc : GetFrameFormat()->GetDoc());
+ const SfxItemPool& rPool = pDoc->GetAttrPool();
+ const MapUnit eMapUnit(rPool.GetMetric(pEntry->nWID));
+
+ if(eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertToMM(eMapUnit, aAny);
+ }
+ }
+ }
+
+ return aAny;
+}
+
+void SwXFrame::addPropertyChangeListener(const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFrame::removePropertyChangeListener(const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFrame::addVetoableChangeListener(const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFrame::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+beans::PropertyState SwXFrame::getPropertyState( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< OUString > aPropertyNames { rPropertyName };
+ uno::Sequence< beans::PropertyState > aStates = getPropertyStates(aPropertyNames);
+ return aStates.getConstArray()[0];
+}
+
+uno::Sequence< beans::PropertyState > SwXFrame::getPropertyStates(
+ const uno::Sequence< OUString >& aPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< beans::PropertyState > aStates(aPropertyNames.getLength());
+ auto [pStates, end] = asNonConstRange(aStates);
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ const OUString* pNames = aPropertyNames.getConstArray();
+ const SwAttrSet& rFormatSet = pFormat->GetAttrSet();
+ for(int i = 0; i < aPropertyNames.getLength(); i++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(pNames[i]);
+ if (!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + pNames[i], static_cast < cppu::OWeakObject * > ( this ) );
+
+ if(pEntry->nWID == FN_UNO_ANCHOR_TYPES||
+ pEntry->nWID == FN_PARAM_LINK_DISPLAY_NAME||
+ FN_UNO_FRAME_STYLE_NAME == pEntry->nWID||
+ FN_UNO_GRAPHIC == pEntry->nWID||
+ FN_UNO_GRAPHIC_URL == pEntry->nWID||
+ FN_UNO_GRAPHIC_FILTER == pEntry->nWID||
+ FN_UNO_ACTUAL_SIZE == pEntry->nWID||
+ FN_UNO_ALTERNATIVE_TEXT == pEntry->nWID)
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ else if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID)
+ {
+ if(SfxItemState::SET == rFormatSet.GetItemState(XATTR_FILLBMP_STRETCH, false)
+ || SfxItemState::SET == rFormatSet.GetItemState(XATTR_FILLBMP_TILE, false))
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ }
+ // for FlyFrames we need to mark the used properties from type RES_BACKGROUND
+ // as beans::PropertyState_DIRECT_VALUE to let users of this property call
+ // getPropertyValue where the member properties will be mapped from the
+ // fill attributes to the according SvxBrushItem entries
+ else if (RES_BACKGROUND == pEntry->nWID)
+ {
+ if (SWUnoHelper::needToMapFillItemsToSvxBrushItemTypes(rFormatSet, pEntry->nMemberId))
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ else
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ if ((m_eType == FLYCNTTYPE_GRF) && isGRFATR(pEntry->nWID))
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwNoTextNode* pNoText = aIdx.GetNode().GetNoTextNode();
+ const SfxItemSet& aSet(pNoText->GetSwAttrSet());
+ aSet.GetItemState(pEntry->nWID);
+ if(SfxItemState::SET == aSet.GetItemState( pEntry->nWID, false ))
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ else
+ {
+ if(SfxItemState::SET == rFormatSet.GetItemState( pEntry->nWID, false ))
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ else
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ }
+ }
+ else if(IsDescriptor())
+ {
+ std::fill(pStates, end, beans::PropertyState_DIRECT_VALUE);
+ }
+ else
+ throw uno::RuntimeException();
+ return aStates;
+}
+
+void SwXFrame::setPropertyToDefault( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw uno::RuntimeException("setPropertyToDefault: property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet(pDoc->GetAttrPool());
+ aSet.SetParent(&pFormat->GetAttrSet());
+
+ aSet.ClearItem(XATTR_FILLBMP_STRETCH);
+ aSet.ClearItem(XATTR_FILLBMP_TILE);
+
+ pFormat->SetFormatAttr(aSet);
+ }
+ else if( pEntry->nWID &&
+ pEntry->nWID != FN_UNO_ANCHOR_TYPES &&
+ pEntry->nWID != FN_PARAM_LINK_DISPLAY_NAME)
+ {
+ if ( (m_eType == FLYCNTTYPE_GRF) && isGRFATR(pEntry->nWID) )
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ if(pIdx)
+ {
+ SwNodeIndex aIdx(*pIdx, 1);
+ SwNoTextNode* pNoText = aIdx.GetNode().GetNoTextNode();
+ {
+ SfxItemSet aSet(pNoText->GetSwAttrSet());
+ aSet.ClearItem(pEntry->nWID);
+ pNoText->SetAttr(aSet);
+ }
+ }
+ }
+ // #i73249#
+ else if( FN_UNO_TITLE == pEntry->nWID )
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ // assure that <SdrObject> instance exists.
+ GetOrCreateSdrObject(rFlyFormat);
+ rFlyFormat.GetDoc()->SetFlyFrameTitle(rFlyFormat, OUString());
+ }
+ // New attribute Description
+ else if( FN_UNO_DESCRIPTION == pEntry->nWID )
+ {
+ SwFlyFrameFormat& rFlyFormat = dynamic_cast<SwFlyFrameFormat&>(*pFormat);
+ // assure that <SdrObject> instance exists.
+ GetOrCreateSdrObject(rFlyFormat);
+ rFlyFormat.GetDoc()->SetFlyFrameDescription(rFlyFormat, OUString());
+ }
+ else
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ SfxItemSetFixed<RES_FRMATR_BEGIN, RES_FRMATR_END - 1> aSet( pDoc->GetAttrPool() );
+ aSet.SetParent(&pFormat->GetAttrSet());
+ aSet.ClearItem(pEntry->nWID);
+ if(rPropertyName != UNO_NAME_ANCHOR_TYPE)
+ pFormat->SetFormatAttr(aSet);
+ }
+ }
+ else
+ {
+ bool bNextFrame = rPropertyName == UNO_NAME_CHAIN_NEXT_NAME;
+ if( bNextFrame || rPropertyName == UNO_NAME_CHAIN_PREV_NAME )
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ if(bNextFrame)
+ pDoc->Unchain(*pFormat);
+ else
+ {
+ const SwFormatChain& aChain( pFormat->GetChain() );
+ SwFrameFormat *pPrev = aChain.GetPrev();
+ if(pPrev)
+ pDoc->Unchain(*pPrev);
+ }
+ }
+ }
+ }
+ else if(!IsDescriptor())
+ throw uno::RuntimeException();
+
+}
+
+uno::Any SwXFrame::getPropertyDefault( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if(!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if ( pEntry->nWID < RES_FRMATR_END )
+ {
+ const SfxPoolItem& rDefItem =
+ pFormat->GetDoc()->GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(aRet, pEntry->nMemberId);
+ }
+
+ }
+ else if(!IsDescriptor())
+ throw uno::RuntimeException();
+ return aRet;
+}
+
+void SAL_CALL SwXFrame::addEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXFrame::removeEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+void SwXFrame::DisposeInternal()
+{
+ mxStyleData.clear();
+ mxStyleFamily.clear();
+ m_pDoc = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_pImpl->m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ {
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.disposeAndClear(aGuard, ev);
+ }
+ m_pFrameFormat = nullptr;
+ EndListeningAll();
+}
+void SwXFrame::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ DisposeInternal();
+}
+
+void SwXFrame::dispose()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if (!pFormat)
+ return;
+
+ DisposeInternal();
+ SdrObject* pObj = pFormat->FindSdrObject();
+ // OD 11.09.2003 #112039# - add condition to perform delete of
+ // format/anchor sign, not only if the object is inserted, but also
+ // if a contact object is registered, which isn't in the destruction.
+ if ( pObj &&
+ ( pObj->IsInserted() ||
+ ( pObj->GetUserCall() &&
+ !static_cast<SwContact*>(pObj->GetUserCall())->IsInDTOR() ) ) )
+ {
+ if (pFormat->GetAnchor().GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ const SwPosition &rPos = *(pFormat->GetAnchor().GetContentAnchor());
+ SwTextNode *pTextNode = rPos.nNode.GetNode().GetTextNode();
+ const sal_Int32 nIdx = rPos.nContent.GetIndex();
+ pTextNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
+ }
+ else
+ pFormat->GetDoc()->getIDocumentLayoutAccess().DelLayoutFormat(pFormat);
+ }
+
+}
+
+uno::Reference< text::XTextRange > SwXFrame::getAnchor()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< text::XTextRange > aRef;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!pFormat)
+ throw uno::RuntimeException();
+
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ // return an anchor for non-page bound frames
+ // and for page bound frames that have a page no == NULL and a content position
+ if ((rAnchor.GetAnchorId() != RndStdIds::FLY_AT_PAGE) ||
+ (rAnchor.GetContentAnchor() && !rAnchor.GetPageNum()))
+ {
+ const SwPosition &rPos = *(rAnchor.GetContentAnchor());
+ if (rAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA)
+ { // ensure that SwXTextRange has SwIndex
+ aRef = SwXTextRange::CreateXTextRange(*pFormat->GetDoc(), SwPosition(rPos.nNode), nullptr);
+ }
+ else
+ {
+ aRef = SwXTextRange::CreateXTextRange(*pFormat->GetDoc(), rPos, nullptr);
+ }
+ }
+
+ return aRef;
+}
+
+void SwXFrame::ResetDescriptor()
+{
+ m_bIsDescriptor = false;
+ mxStyleData.clear();
+ mxStyleFamily.clear();
+ m_pProps.reset();
+}
+
+void SwXFrame::attachToRange(uno::Reference<text::XTextRange> const& xTextRange,
+ SwPaM const*const pCopySource)
+{
+ SolarMutexGuard aGuard;
+ if(!IsDescriptor())
+ throw uno::RuntimeException();
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ SwDoc* pDoc = pRange ? &pRange->GetDoc() : pCursor ? pCursor->GetDoc() : nullptr;
+ if(!pDoc)
+ throw lang::IllegalArgumentException();
+
+ SwUnoInternalPaM aIntPam(*pDoc);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aIntPam, xTextRange);
+
+ SwNode& rNode = pDoc->GetNodes().GetEndOfContent();
+ SwPaM aPam(rNode);
+ aPam.Move( fnMoveBackward, GoInDoc );
+
+ SfxItemSetFixed<RES_GRFATR_BEGIN, RES_GRFATR_END-1> aGrSet(pDoc->GetAttrPool());
+
+ SfxItemSetFixed<
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER,
+
+ // FillAttribute support
+ XATTR_FILL_FIRST, XATTR_FILL_LAST,
+
+ SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>
+ aFrameSet(pDoc->GetAttrPool() );
+
+ // set correct parent to get the XFILL_NONE FillStyle as needed
+ aFrameSet.SetParent(&pDoc->GetDfltFrameFormat()->GetAttrSet());
+
+ // no the related items need to be added to the set
+ bool bSizeFound;
+ if (!m_pProps->AnyToItemSet(pDoc, aFrameSet, aGrSet, bSizeFound))
+ throw lang::IllegalArgumentException();
+ // a TextRange is handled separately
+ *aPam.GetPoint() = *aIntPam.GetPoint();
+ if(aIntPam.HasMark())
+ {
+ aPam.SetMark();
+ *aPam.GetMark() = *aIntPam.GetMark();
+ }
+
+ RndStdIds eAnchorId = RndStdIds::FLY_AT_PARA;
+ if(const SwFormatAnchor* pItem = aFrameSet.GetItemIfSet(RES_ANCHOR, false) )
+ {
+ eAnchorId = pItem->GetAnchorId();
+ if( RndStdIds::FLY_AT_FLY == eAnchorId &&
+ !aPam.GetNode().FindFlyStartNode())
+ {
+ // framebound only where a frame exists
+ SwFormatAnchor aAnchor(RndStdIds::FLY_AT_PARA);
+ aFrameSet.Put(aAnchor);
+ }
+ else if ((RndStdIds::FLY_AT_PAGE == eAnchorId) &&
+ 0 == pItem->GetPageNum() )
+ {
+ SwFormatAnchor aAnchor( *pItem );
+ aAnchor.SetType(RndStdIds::FLY_AT_CHAR); // convert invalid at-page
+ aAnchor.SetAnchor( aPam.GetPoint() );
+ aFrameSet.Put(aAnchor);
+ }
+
+ if (eAnchorId == RndStdIds::FLY_AT_PAGE)
+ {
+ sal_Int16 nRelOrient(aFrameSet.Get(RES_HORI_ORIENT).GetRelationOrient());
+ if (sw::GetAtPageRelOrientation(nRelOrient, true))
+ {
+ SAL_WARN("sw.core", "SwXFrame: fixing invalid horizontal RelOrientation for at-page anchor");
+
+ SwFormatHoriOrient item(aFrameSet.Get(RES_HORI_ORIENT));
+ item.SetRelationOrient(nRelOrient);
+ aFrameSet.Put(item);
+ }
+ }
+ }
+
+ const ::uno::Any* pStyle;
+ SwFrameFormat *pParentFrameFormat = nullptr;
+ if (m_pProps->GetProperty(FN_UNO_FRAME_STYLE_NAME, 0, pStyle))
+ pParentFrameFormat = lcl_GetFrameFormat( *pStyle, pDoc );
+
+ SwFlyFrameFormat* pFormat = nullptr;
+ if( m_eType == FLYCNTTYPE_FRM)
+ {
+ UnoActionContext aCont(pDoc);
+ if (pCopySource)
+ {
+ std::unique_ptr<SwFormatAnchor> pAnchorItem;
+ // the frame is inserted bound to page
+ // to prevent conflicts if the to-be-anchored position is part of the to-be-copied text
+ if (eAnchorId != RndStdIds::FLY_AT_PAGE)
+ {
+ pAnchorItem.reset(aFrameSet.Get(RES_ANCHOR).Clone());
+ aFrameSet.Put( SwFormatAnchor( RndStdIds::FLY_AT_PAGE, 1 ));
+ }
+
+ // park these no longer needed PaMs somewhere safe so MakeFlyAndMove
+ // can delete what it likes without any assert these are pointing to
+ // that content
+ aPam.DeleteMark();
+ aIntPam.DeleteMark();
+ *aPam.GetPoint() = *aIntPam.GetPoint() = SwPosition(pDoc->GetNodes());
+
+ pFormat = pDoc->MakeFlyAndMove( *pCopySource, aFrameSet,
+ nullptr,
+ pParentFrameFormat );
+ if(pAnchorItem && pFormat)
+ {
+ pFormat->DelFrames();
+ pAnchorItem->SetAnchor( pCopySource->Start() );
+ SfxItemSetFixed<RES_ANCHOR, RES_ANCHOR> aAnchorSet( pDoc->GetAttrPool() );
+ aAnchorSet.Put( std::move(pAnchorItem) );
+ pDoc->SetFlyFrameAttr( *pFormat, aAnchorSet );
+ }
+ }
+ else
+ {
+ pFormat = pDoc->MakeFlySection( RndStdIds::FLY_AT_PARA, aPam.GetPoint(),
+ &aFrameSet, pParentFrameFormat );
+ }
+ if(pFormat)
+ {
+ EndListeningAll();
+ m_pFrameFormat = pFormat;
+ StartListening(pFormat->GetNotifier());
+ if(!m_sName.isEmpty())
+ pDoc->SetFlyName(*pFormat, m_sName);
+ }
+ // wake up the SwXTextFrame
+ static_cast<SwXTextFrame*>(this)->SetDoc( m_bIsDescriptor ? m_pDoc : GetFrameFormat()->GetDoc() );
+ }
+ else if( m_eType == FLYCNTTYPE_GRF)
+ {
+ UnoActionContext aActionContext(pDoc);
+ Graphic aGraphic;
+
+ // Read graphic URL from the descriptor, if it has any.
+ const ::uno::Any* pGraphicURL;
+ if (m_pProps->GetProperty(FN_UNO_GRAPHIC_URL, 0, pGraphicURL))
+ {
+ OUString sGraphicURL;
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (((*pGraphicURL) >>= sGraphicURL) && !sGraphicURL.isEmpty())
+ aGraphic = vcl::graphic::loadFromURL(sGraphicURL);
+ else if ((*pGraphicURL) >>= xBitmap)
+ {
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ if (xGraphic.is())
+ aGraphic = xGraphic;
+ }
+ }
+
+ const ::uno::Any* pGraphicAny;
+ const bool bHasGraphic = m_pProps->GetProperty(FN_UNO_GRAPHIC, 0, pGraphicAny);
+ if (bHasGraphic)
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ (*pGraphicAny) >>= xGraphic;
+ aGraphic = Graphic(xGraphic);
+ }
+
+ OUString sFilterName;
+ const uno::Any* pFilterAny;
+ if (m_pProps->GetProperty(FN_UNO_GRAPHIC_FILTER, 0, pFilterAny))
+ {
+ (*pFilterAny) >>= sFilterName;
+ }
+
+ pFormat = pDoc->getIDocumentContentOperations().InsertGraphic(
+ aPam, OUString(), sFilterName, &aGraphic, &aFrameSet, &aGrSet, pParentFrameFormat);
+ if (pFormat)
+ {
+ SwGrfNode *pGrfNd = pDoc->GetNodes()[ pFormat->GetContent().GetContentIdx()
+ ->GetIndex()+1 ]->GetGrfNode();
+ if (pGrfNd)
+ pGrfNd->SetChgTwipSize( !bSizeFound );
+ m_pFrameFormat = pFormat;
+ EndListeningAll();
+ StartListening(m_pFrameFormat->GetNotifier());
+ if(!m_sName.isEmpty())
+ pDoc->SetFlyName(*pFormat, m_sName);
+
+ }
+ const ::uno::Any* pSurroundContour;
+ if (m_pProps->GetProperty(RES_SURROUND, MID_SURROUND_CONTOUR, pSurroundContour))
+ setPropertyValue(UNO_NAME_SURROUND_CONTOUR, *pSurroundContour);
+ const ::uno::Any* pContourOutside;
+ if (m_pProps->GetProperty(RES_SURROUND, MID_SURROUND_CONTOUROUTSIDE, pContourOutside))
+ setPropertyValue(UNO_NAME_CONTOUR_OUTSIDE, *pContourOutside);
+ const ::uno::Any* pContourPoly;
+ if (m_pProps->GetProperty(FN_PARAM_CONTOUR_PP, 0, pContourPoly))
+ setPropertyValue(UNO_NAME_CONTOUR_POLY_POLYGON, *pContourPoly);
+ const ::uno::Any* pPixelContour;
+ if (m_pProps->GetProperty(FN_UNO_IS_PIXEL_CONTOUR, 0, pPixelContour))
+ setPropertyValue(UNO_NAME_IS_PIXEL_CONTOUR, *pPixelContour);
+ const ::uno::Any* pAutoContour;
+ if (m_pProps->GetProperty(FN_UNO_IS_AUTOMATIC_CONTOUR, 0, pAutoContour))
+ setPropertyValue(UNO_NAME_IS_AUTOMATIC_CONTOUR, *pAutoContour);
+ }
+ else
+ {
+ const ::uno::Any* pCLSID = nullptr;
+ const ::uno::Any* pStreamName = nullptr;
+ const ::uno::Any* pEmbeddedObject = nullptr;
+ if (!m_pProps->GetProperty(FN_UNO_CLSID, 0, pCLSID)
+ && !m_pProps->GetProperty(FN_UNO_STREAM_NAME, 0, pStreamName)
+ && !m_pProps->GetProperty(FN_EMBEDDED_OBJECT, 0, pEmbeddedObject))
+ {
+ throw uno::RuntimeException();
+ }
+ if(pCLSID)
+ {
+ OUString aCLSID;
+ SvGlobalName aClassName;
+ uno::Reference < embed::XEmbeddedObject > xIPObj;
+ std::unique_ptr < comphelper::EmbeddedObjectContainer > pCnt;
+ if( (*pCLSID) >>= aCLSID )
+ {
+ if( !aClassName.MakeId( aCLSID ) )
+ {
+ lang::IllegalArgumentException aExcept;
+ aExcept.Message = "CLSID invalid";
+ throw aExcept;
+ }
+
+ pCnt.reset( new comphelper::EmbeddedObjectContainer );
+ OUString aName;
+
+ OUString sDocumentBaseURL = pDoc->GetPersist()->getDocumentBaseURL();
+ xIPObj = pCnt->CreateEmbeddedObject(aClassName.GetByteSequence(), aName,
+ &sDocumentBaseURL);
+ }
+ if ( xIPObj.is() )
+ {
+ UnoActionContext aAction(pDoc);
+ pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+
+ // tdf#99631 set imported VisibleArea settings of embedded XLSX OLE objects
+ if ( m_nDrawAspect == embed::Aspects::MSOLE_CONTENT
+ && m_nVisibleAreaWidth && m_nVisibleAreaHeight )
+ {
+ sal_Int64 nAspect = m_nDrawAspect;
+ MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xIPObj->getMapUnit( nAspect ) );
+ Size aSize( OutputDevice::LogicToLogic(Size( m_nVisibleAreaWidth, m_nVisibleAreaHeight),
+ MapMode(MapUnit::MapTwip), MapMode(aUnit)));
+ awt::Size aSz;
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xIPObj->setVisualAreaSize(m_nDrawAspect, aSz);
+ }
+
+ if(!bSizeFound)
+ {
+ //TODO/LATER: how do I transport it to the OLENode?
+ sal_Int64 nAspect = m_nDrawAspect;
+
+ // TODO/LEAN: VisualArea still needs running state
+ svt::EmbeddedObjectRef::TryRunningState( xIPObj );
+
+ // set parent to get correct VisArea(in case of object needing parent printer)
+ uno::Reference < container::XChild > xChild( xIPObj, uno::UNO_QUERY );
+ if ( xChild.is() )
+ xChild->setParent( pDoc->GetDocShell()->GetModel() );
+
+ //The Size should be suggested by the OLE server if not manually set
+ MapUnit aRefMap = VCLUnoHelper::UnoEmbed2VCLMapUnit( xIPObj->getMapUnit( nAspect ) );
+ awt::Size aSize;
+ try
+ {
+ aSize = xIPObj->getVisualAreaSize( nAspect );
+ }
+ catch ( embed::NoVisualAreaSizeException& )
+ {
+ // the default size will be set later
+ }
+
+ Size aSz( aSize.Width, aSize.Height );
+ if ( !aSz.Width() || !aSz.Height() )
+ {
+ aSz.setWidth(5000);
+ aSz.setHeight(5000);
+ aSz = OutputDevice::LogicToLogic(aSz,
+ MapMode(MapUnit::Map100thMM), MapMode(aRefMap));
+ }
+ MapMode aMyMap( MapUnit::MapTwip );
+ aSz = OutputDevice::LogicToLogic(aSz, MapMode(aRefMap), aMyMap);
+ SwFormatFrameSize aFrameSz;
+ aFrameSz.SetSize(aSz);
+ aFrameSet.Put(aFrameSz);
+ }
+ SwFlyFrameFormat* pFormat2 = nullptr;
+
+ ::svt::EmbeddedObjectRef xObjRef( xIPObj, m_nDrawAspect);
+ pFormat2 = pDoc->getIDocumentContentOperations().InsertEmbObject(
+ aPam, xObjRef, &aFrameSet );
+
+ // store main document name to show in the title bar
+ uno::Reference< frame::XTitle > xModelTitle( pDoc->GetDocShell()->GetModel(), css::uno::UNO_QUERY );
+ if( xModelTitle.is() )
+ xIPObj->setContainerName( xModelTitle->getTitle() );
+
+ assert(pFormat2 && "Doc->Insert(notxt) failed.");
+
+ pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+ m_pFrameFormat = pFormat2;
+ EndListeningAll();
+ StartListening(m_pFrameFormat->GetNotifier());
+ if(!m_sName.isEmpty())
+ pDoc->SetFlyName(*pFormat2, m_sName);
+ }
+ }
+ else if( pStreamName )
+ {
+ OUString sStreamName;
+ (*pStreamName) >>= sStreamName;
+ pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+
+ SwFlyFrameFormat* pFrameFormat = pDoc->getIDocumentContentOperations().InsertOLE(
+ aPam, sStreamName, m_nDrawAspect, &aFrameSet, nullptr);
+
+ // store main document name to show in the title bar
+ SwOLENode* pNd = nullptr;
+ const SwNodeIndex* pIdx = pFrameFormat->GetContent().GetContentIdx();
+ if( pIdx )
+ {
+ SwNodeIndex aIdx( *pIdx, 1 );
+ SwNoTextNode* pNoText = aIdx.GetNode().GetNoTextNode();
+ pNd = pNoText->GetOLENode();
+ }
+ if( pNd )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = pNd->GetOLEObj().GetOleRef();
+ if( xObj.is() )
+ {
+ uno::Reference< frame::XTitle > xModelTitle( pDoc->GetDocShell()->GetModel(), css::uno::UNO_QUERY );
+ if( xModelTitle.is() )
+ xObj->setContainerName( xModelTitle->getTitle() );
+ }
+ }
+
+ pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+ m_pFrameFormat = pFrameFormat;
+ EndListeningAll();
+ StartListening(m_pFrameFormat->GetNotifier());
+ if(!m_sName.isEmpty())
+ pDoc->SetFlyName(*pFrameFormat, m_sName);
+ }
+ else if (pEmbeddedObject)
+ {
+ uno::Reference< embed::XEmbeddedObject > obj;
+ (*pEmbeddedObject) >>= obj;
+ svt::EmbeddedObjectRef xObj;
+ xObj.Assign( obj, embed::Aspects::MSOLE_CONTENT );
+
+ pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+
+ // Do not call here container::XChild(obj)->setParent() and
+ // pDoc->GetPersist()->GetEmbeddedObjectContainer().InsertEmbeddedObject:
+ // they are called indirectly by pDoc->getIDocumentContentOperations().InsertEmbObject
+ // below. Calling them twice will add the same object twice to EmbeddedObjectContainer's
+ // pImpl->maNameToObjectMap, and then it will misbehave in
+ // EmbeddedObjectContainer::StoreAsChildren and SfxObjectShell::SaveCompletedChildren.
+
+ SwFlyFrameFormat* pFrameFormat
+ = pDoc->getIDocumentContentOperations().InsertEmbObject(aPam, xObj, &aFrameSet);
+ pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+ m_pFrameFormat = pFrameFormat;
+ EndListeningAll();
+ StartListening(m_pFrameFormat->GetNotifier());
+ if(!m_sName.isEmpty())
+ pDoc->SetFlyName(*pFrameFormat, m_sName);
+ }
+ }
+ if( pFormat && pDoc->getIDocumentDrawModelAccess().GetDrawModel() )
+ GetOrCreateSdrObject(*pFormat);
+ const ::uno::Any* pOrder;
+ if (m_pProps->GetProperty(FN_UNO_Z_ORDER, 0, pOrder))
+ setPropertyValue(UNO_NAME_Z_ORDER, *pOrder);
+ const ::uno::Any* pReplacement;
+ if (m_pProps->GetProperty(FN_UNO_REPLACEMENT_GRAPHIC, 0, pReplacement))
+ setPropertyValue(UNO_NAME_GRAPHIC, *pReplacement);
+ // new attribute Title
+ const ::uno::Any* pTitle;
+ if (m_pProps->GetProperty(FN_UNO_TITLE, 0, pTitle))
+ {
+ setPropertyValue(UNO_NAME_TITLE, *pTitle);
+ }
+ // new attribute Description
+ const ::uno::Any* pDescription;
+ if (m_pProps->GetProperty(FN_UNO_DESCRIPTION, 0, pDescription))
+ {
+ setPropertyValue(UNO_NAME_DESCRIPTION, *pDescription);
+ }
+
+ // For grabbag
+ const uno::Any* pFrameIntropgrabbagItem;
+ if (m_pProps->GetProperty(RES_FRMATR_GRABBAG, 0, pFrameIntropgrabbagItem))
+ {
+ setPropertyValue(UNO_NAME_FRAME_INTEROP_GRAB_BAG, *pFrameIntropgrabbagItem);
+ }
+
+ // reset the flag and delete Descriptor pointer
+ ResetDescriptor();
+}
+
+void SwXFrame::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard g;
+
+ if(IsDescriptor())
+ {
+ attachToRange(xTextRange);
+ return;
+ }
+
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if( !pFormat )
+ return;
+
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwUnoInternalPaM aIntPam(*pDoc);
+ if (!::sw::XTextRangeToSwPaM(aIntPam, xTextRange))
+ throw lang::IllegalArgumentException();
+
+ SfxItemSetFixed<RES_ANCHOR, RES_ANCHOR> aSet( pDoc->GetAttrPool() );
+ aSet.SetParent(&pFormat->GetAttrSet());
+ SwFormatAnchor aAnchor = aSet.Get(RES_ANCHOR);
+
+ if (aAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXFrame::attach(): re-anchoring AS_CHAR not supported",
+ *this, 0);
+ }
+
+ aAnchor.SetAnchor( aIntPam.Start() );
+ aSet.Put(aAnchor);
+ pDoc->SetFlyFrameAttr( *pFormat, aSet );
+}
+
+awt::Point SwXFrame::getPosition()
+{
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = "position cannot be determined with this method";
+ throw aRuntime;
+}
+
+void SwXFrame::setPosition(const awt::Point& /*aPosition*/)
+{
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = "position cannot be changed with this method";
+ throw aRuntime;
+}
+
+awt::Size SwXFrame::getSize()
+{
+ const ::uno::Any aVal = getPropertyValue("Size");
+ awt::Size const * pRet = o3tl::doAccess<awt::Size>(aVal);
+ return *pRet;
+}
+
+void SwXFrame::setSize(const awt::Size& aSize)
+{
+ const ::uno::Any aVal(&aSize, ::cppu::UnoType<awt::Size>::get());
+ setPropertyValue("Size", aVal);
+}
+
+OUString SwXFrame::getShapeType()
+{
+ return "FrameShape";
+}
+
+SwXTextFrame::SwXTextFrame( SwDoc *_pDoc ) :
+ SwXTextFrameBaseClass(FLYCNTTYPE_FRM, aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_FRAME), _pDoc ),
+ SwXText(nullptr, CursorType::Frame)
+{
+}
+
+SwXTextFrame::SwXTextFrame(SwFrameFormat& rFormat) :
+ SwXTextFrameBaseClass(rFormat, FLYCNTTYPE_FRM, aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_FRAME)),
+ SwXText(rFormat.GetDoc(), CursorType::Frame)
+{
+
+}
+
+SwXTextFrame::~SwXTextFrame()
+{
+}
+
+uno::Reference<text::XTextFrame>
+SwXTextFrame::CreateXTextFrame(SwDoc & rDoc, SwFrameFormat *const pFrameFormat)
+{
+ return CreateXFrame<text::XTextFrame, SwXTextFrame>(rDoc, pFrameFormat);
+}
+
+void SAL_CALL SwXTextFrame::acquire( )noexcept
+{
+ SwXFrame::acquire();
+}
+
+void SAL_CALL SwXTextFrame::release( )noexcept
+{
+ SwXFrame::release();
+}
+
+::uno::Any SAL_CALL SwXTextFrame::queryInterface( const uno::Type& aType )
+{
+ ::uno::Any aRet = SwXFrame::queryInterface(aType);
+ if(aRet.getValueType() == cppu::UnoType<void>::get())
+ aRet = SwXText::queryInterface(aType);
+ if(aRet.getValueType() == cppu::UnoType<void>::get())
+ aRet = SwXTextFrameBaseClass::queryInterface(aType);
+ return aRet;
+}
+
+uno::Sequence< uno::Type > SAL_CALL SwXTextFrame::getTypes( )
+{
+ return comphelper::concatSequences(
+ SwXTextFrameBaseClass::getTypes(),
+ SwXFrame::getTypes(),
+ SwXText::getTypes()
+ );
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwXTextFrame::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Reference< text::XText > SwXTextFrame::getText()
+{
+ return this;
+}
+
+const SwStartNode *SwXTextFrame::GetStartNode() const
+{
+ const SwStartNode *pSttNd = nullptr;
+
+ const SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ const SwFormatContent& rFlyContent = pFormat->GetContent();
+ if( rFlyContent.GetContentIdx() )
+ pSttNd = rFlyContent.GetContentIdx()->GetNode().GetStartNode();
+ }
+
+ return pSttNd;
+}
+
+uno::Reference< text::XTextCursor >
+SwXTextFrame::CreateCursor()
+{
+ return createTextCursor();
+}
+
+uno::Reference< text::XTextCursor > SwXTextFrame::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!pFormat)
+ throw uno::RuntimeException();
+
+ //save current start node to be able to check if there is content after the table -
+ //otherwise the cursor would be in the body text!
+ const SwNode& rNode = pFormat->GetContent().GetContentIdx()->GetNode();
+ const SwStartNode* pOwnStartNode = rNode.FindSttNodeByType(SwFlyStartNode);
+
+ SwPaM aPam(rNode);
+ aPam.Move(fnMoveForward, GoInNode);
+ SwTableNode* pTableNode = aPam.GetNode().FindTableNode();
+ SwContentNode* pCont = nullptr;
+ while( pTableNode )
+ {
+ aPam.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
+ pCont = GetDoc()->GetNodes().GoNext(&aPam.GetPoint()->nNode);
+ pTableNode = pCont->FindTableNode();
+ }
+ if(pCont)
+ aPam.GetPoint()->nContent.Assign(pCont, 0);
+
+ const SwStartNode* pNewStartNode =
+ aPam.GetNode().FindSttNodeByType(SwFlyStartNode);
+ if(!pNewStartNode || pNewStartNode != pOwnStartNode)
+ {
+ uno::RuntimeException aExcept;
+ aExcept.Message = "no text available";
+ throw aExcept;
+ }
+
+ return static_cast<text::XWordCursor*>(new SwXTextCursor(
+ *pFormat->GetDoc(), this, CursorType::Frame, *aPam.GetPoint()));
+}
+
+uno::Reference< text::XTextCursor > SwXTextFrame::createTextCursorByRange(const uno::Reference< text::XTextRange > & aTextPosition)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if (!pFormat)
+ throw uno::RuntimeException();
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (!::sw::XTextRangeToSwPaM(aPam, aTextPosition))
+ throw uno::RuntimeException();
+
+ uno::Reference<text::XTextCursor> aRef;
+ SwNode& rNode = pFormat->GetContent().GetContentIdx()->GetNode();
+ if(aPam.GetNode().FindFlyStartNode() == rNode.FindFlyStartNode())
+ {
+ aRef = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*pFormat->GetDoc(), this, CursorType::Frame,
+ *aPam.GetPoint(), aPam.GetMark()));
+ }
+
+ return aRef;
+}
+
+uno::Reference< container::XEnumeration > SwXTextFrame::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!pFormat)
+ return nullptr;
+ SwPosition aPos(pFormat->GetContent().GetContentIdx()->GetNode());
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Frame);
+}
+
+uno::Type SwXTextFrame::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SwXTextFrame::hasElements()
+{
+ return true;
+}
+
+void SwXTextFrame::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SwXFrame::attach(xTextRange);
+}
+
+uno::Reference< text::XTextRange > SwXTextFrame::getAnchor()
+{
+ SolarMutexGuard aGuard;
+ return SwXFrame::getAnchor();
+}
+
+void SwXTextFrame::dispose()
+{
+ SolarMutexGuard aGuard;
+ SwXFrame::dispose();
+}
+
+void SwXTextFrame::addEventListener(const uno::Reference< lang::XEventListener > & aListener)
+{
+ SwXFrame::addEventListener(aListener);
+}
+
+void SwXTextFrame::removeEventListener(const uno::Reference< lang::XEventListener > & aListener)
+{
+ SwXFrame::removeEventListener(aListener);
+}
+
+OUString SwXTextFrame::getImplementationName()
+{
+ return "SwXTextFrame";
+}
+
+sal_Bool SwXTextFrame::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextFrame::getSupportedServiceNames()
+{
+ uno::Sequence < OUString > aRet = SwXFrame::getSupportedServiceNames();
+ aRet.realloc(aRet.getLength() + 2);
+ OUString* pArray = aRet.getArray();
+ pArray[aRet.getLength() - 2] = "com.sun.star.text.TextFrame";
+ pArray[aRet.getLength() - 1] = "com.sun.star.text.Text";
+ return aRet;
+}
+
+uno::Reference<container::XNameReplace > SAL_CALL SwXTextFrame::getEvents()
+{
+ return new SwFrameEventDescriptor( *this );
+}
+
+sal_Int64 SAL_CALL SwXTextFrame::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ sal_Int64 nRet = SwXFrame::getSomething( rId );
+ if( !nRet )
+ nRet = SwXText::getSomething( rId );
+
+ return nRet;
+}
+
+::uno::Any SwXTextFrame::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ ::uno::Any aRet;
+ if(rPropertyName == UNO_NAME_START_REDLINE||
+ rPropertyName == UNO_NAME_END_REDLINE)
+ {
+ //redline can only be returned if it's a living object
+ if(!IsDescriptor())
+ aRet = SwXText::getPropertyValue(rPropertyName);
+ }
+ else
+ aRet = SwXFrame::getPropertyValue(rPropertyName);
+ return aRet;
+}
+
+SwXTextGraphicObject::SwXTextGraphicObject( SwDoc *pDoc )
+ : SwXTextGraphicObjectBaseClass(FLYCNTTYPE_GRF,
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_GRAPHIC), pDoc)
+{
+}
+
+SwXTextGraphicObject::SwXTextGraphicObject(SwFrameFormat& rFormat)
+ : SwXTextGraphicObjectBaseClass(rFormat, FLYCNTTYPE_GRF,
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_GRAPHIC))
+{
+}
+
+SwXTextGraphicObject::~SwXTextGraphicObject()
+{
+}
+
+uno::Reference<text::XTextContent>
+SwXTextGraphicObject::CreateXTextGraphicObject(SwDoc & rDoc, SwFrameFormat *const pFrameFormat)
+{
+ return CreateXFrame<text::XTextContent, SwXTextGraphicObject>(rDoc, pFrameFormat);
+}
+
+OUString SwXTextGraphicObject::getImplementationName()
+{
+ return "SwXTextGraphicObject";
+}
+
+sal_Bool SwXTextGraphicObject::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextGraphicObject::getSupportedServiceNames()
+{
+ uno::Sequence < OUString > aRet = SwXFrame::getSupportedServiceNames();
+ aRet.realloc(aRet.getLength() + 1);
+ OUString* pArray = aRet.getArray();
+ pArray[aRet.getLength() - 1] = "com.sun.star.text.TextGraphicObject";
+ return aRet;
+}
+
+uno::Reference<container::XNameReplace> SAL_CALL
+ SwXTextGraphicObject::getEvents()
+{
+ return new SwFrameEventDescriptor( *this );
+}
+
+SwXTextEmbeddedObject::SwXTextEmbeddedObject( SwDoc *pDoc )
+ : SwXTextEmbeddedObjectBaseClass(FLYCNTTYPE_OLE,
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_EMBEDDED_OBJECT), pDoc)
+{
+}
+
+SwXTextEmbeddedObject::SwXTextEmbeddedObject(SwFrameFormat& rFormat)
+ : SwXTextEmbeddedObjectBaseClass(rFormat, FLYCNTTYPE_OLE,
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_EMBEDDED_OBJECT))
+{
+}
+
+SwXTextEmbeddedObject::~SwXTextEmbeddedObject()
+{
+}
+
+uno::Reference<text::XTextContent>
+SwXTextEmbeddedObject::CreateXTextEmbeddedObject(SwDoc & rDoc, SwFrameFormat *const pFrameFormat)
+{
+ return CreateXFrame<text::XTextContent, SwXTextEmbeddedObject>(rDoc, pFrameFormat);
+}
+
+uno::Reference< lang::XComponent > SwXTextEmbeddedObject::getEmbeddedObject()
+{
+ uno::Reference<embed::XEmbeddedObject> xObj(getExtendedControlOverEmbeddedObject());
+ return xObj.is() ? uno::Reference<lang::XComponent>(xObj->getComponent(), uno::UNO_QUERY) : nullptr;
+}
+
+uno::Reference< embed::XEmbeddedObject > SAL_CALL SwXTextEmbeddedObject::getExtendedControlOverEmbeddedObject()
+{
+ uno::Reference< embed::XEmbeddedObject > xResult;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ const SwFormatContent* pCnt = &pFormat->GetContent();
+ OSL_ENSURE( pCnt->GetContentIdx() &&
+ pDoc->GetNodes()[ pCnt->GetContentIdx()->
+ GetIndex() + 1 ]->GetOLENode(), "no OLE-Node?");
+
+ SwOLENode* pOleNode = pDoc->GetNodes()[ pCnt->GetContentIdx()
+ ->GetIndex() + 1 ]->GetOLENode();
+ xResult = pOleNode->GetOLEObj().GetOleRef();
+ if ( svt::EmbeddedObjectRef::TryRunningState( xResult ) )
+ {
+ // TODO/LATER: the listener registered after client creation should be able to handle scaling, after that the client is not necessary here
+ if ( pDoc->GetDocShell() )
+ pDoc->GetDocShell()->GetIPClient( svt::EmbeddedObjectRef( xResult, embed::Aspects::MSOLE_CONTENT ) );
+
+ uno::Reference < lang::XComponent > xComp( xResult->getComponent(), uno::UNO_QUERY );
+ uno::Reference< util::XModifyBroadcaster > xBrdcst( xComp, uno::UNO_QUERY);
+ uno::Reference< frame::XModel > xModel( xComp, uno::UNO_QUERY);
+ if(xBrdcst.is() && xModel.is() && !m_xOLEListener.is())
+ {
+ m_xOLEListener = new SwXOLEListener(*pFormat, xModel);
+ xBrdcst->addModifyListener( m_xOLEListener );
+ }
+ }
+ }
+ return xResult;
+}
+
+sal_Int64 SAL_CALL SwXTextEmbeddedObject::getAspect()
+{
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ const SwFormatContent* pCnt = &pFormat->GetContent();
+ OSL_ENSURE( pCnt->GetContentIdx() &&
+ pDoc->GetNodes()[ pCnt->GetContentIdx()->
+ GetIndex() + 1 ]->GetOLENode(), "no OLE-Node?");
+
+ return pDoc->GetNodes()[ pCnt->GetContentIdx()->GetIndex() + 1 ]->GetOLENode()->GetAspect();
+ }
+
+ return embed::Aspects::MSOLE_CONTENT; // return the default value
+}
+
+void SAL_CALL SwXTextEmbeddedObject::setAspect( sal_Int64 nAspect )
+{
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ const SwFormatContent* pCnt = &pFormat->GetContent();
+ OSL_ENSURE( pCnt->GetContentIdx() &&
+ pDoc->GetNodes()[ pCnt->GetContentIdx()->
+ GetIndex() + 1 ]->GetOLENode(), "no OLE-Node?");
+
+ pDoc->GetNodes()[ pCnt->GetContentIdx()->GetIndex() + 1 ]->GetOLENode()->SetAspect( nAspect );
+ }
+}
+
+uno::Reference< graphic::XGraphic > SAL_CALL SwXTextEmbeddedObject::getReplacementGraphic()
+{
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ const SwFormatContent* pCnt = &pFormat->GetContent();
+ OSL_ENSURE( pCnt->GetContentIdx() &&
+ pDoc->GetNodes()[ pCnt->GetContentIdx()->
+ GetIndex() + 1 ]->GetOLENode(), "no OLE-Node?");
+
+ const Graphic* pGraphic = pDoc->GetNodes()[ pCnt->GetContentIdx()->GetIndex() + 1 ]->GetOLENode()->GetGraphic();
+ if ( pGraphic )
+ return pGraphic->GetXGraphic();
+ }
+
+ return uno::Reference< graphic::XGraphic >();
+}
+
+OUString SwXTextEmbeddedObject::getImplementationName()
+{
+ return "SwXTextEmbeddedObject";
+}
+
+sal_Bool SwXTextEmbeddedObject::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextEmbeddedObject::getSupportedServiceNames()
+{
+ uno::Sequence < OUString > aRet = SwXFrame::getSupportedServiceNames();
+ aRet.realloc(aRet.getLength() + 1);
+ OUString* pArray = aRet.getArray();
+ pArray[aRet.getLength() - 1] = "com.sun.star.text.TextEmbeddedObject";
+ return aRet;
+}
+
+uno::Reference<container::XNameReplace> SAL_CALL
+ SwXTextEmbeddedObject::getEvents()
+{
+ return new SwFrameEventDescriptor( *this );
+}
+
+namespace
+{
+ SwOLENode* lcl_GetOLENode(const SwFormat* pFormat)
+ {
+ if(!pFormat)
+ return nullptr;
+ const SwNodeIndex* pIdx(pFormat->GetContent().GetContentIdx());
+ if(!pIdx)
+ return nullptr;
+ const SwNodeIndex aIdx(*pIdx, 1);
+ return aIdx.GetNode().GetNoTextNode()->GetOLENode();
+ }
+}
+
+SwXOLEListener::SwXOLEListener( SwFormat& rOLEFormat, uno::Reference< XModel > const & xOLE)
+ : m_pOLEFormat(&rOLEFormat)
+ , m_xOLEModel(xOLE)
+{
+ StartListening(m_pOLEFormat->GetNotifier());
+}
+
+SwXOLEListener::~SwXOLEListener()
+{}
+
+void SwXOLEListener::modified( const lang::EventObject& /*rEvent*/ )
+{
+ SolarMutexGuard aGuard;
+ const auto pNd = lcl_GetOLENode(m_pOLEFormat);
+ if(!pNd)
+ throw uno::RuntimeException();
+ const auto xIP = pNd->GetOLEObj().GetOleRef();
+ if(xIP.is())
+ {
+ sal_Int32 nState = xIP->getCurrentState();
+ if(nState == embed::EmbedStates::INPLACE_ACTIVE || nState == embed::EmbedStates::UI_ACTIVE)
+ // if the OLE-Node is UI-Active do nothing
+ return;
+ }
+ pNd->SetOLESizeInvalid(true);
+ pNd->GetDoc().SetOLEObjModified();
+}
+
+void SwXOLEListener::disposing( const lang::EventObject& rEvent )
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<util::XModifyListener> xListener( this );
+ uno::Reference<frame::XModel> xModel(rEvent.Source, uno::UNO_QUERY);
+ uno::Reference<util::XModifyBroadcaster> xBrdcst(xModel, uno::UNO_QUERY);
+ if(!xBrdcst.is())
+ return;
+ try
+ {
+ xBrdcst->removeModifyListener(xListener);
+ }
+ catch(uno::Exception const &)
+ {
+ OSL_FAIL("OLE Listener couldn't be removed");
+ }
+}
+
+void SwXOLEListener::Notify( const SfxHint& rHint )
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_xOLEModel = nullptr;
+ m_pOLEFormat = nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoftn.cxx b/sw/source/core/unocore/unoftn.cxx
new file mode 100644
index 000000000..ca012bbb0
--- /dev/null
+++ b/sw/source/core/unocore/unoftn.cxx
@@ -0,0 +1,569 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/listener.hxx>
+#include <mutex>
+
+#include <unofootnote.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unoparagraph.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <doc.hxx>
+#include <ftnidx.hxx>
+#include <fmtftn.hxx>
+#include <txtftn.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <svl/itemprop.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+uno::Sequence< OUString >
+GetSupportedServiceNamesImpl(
+ size_t const nServices, char const*const pServices[])
+{
+ uno::Sequence< OUString > ret(static_cast<sal_Int32>(nServices));
+
+ std::transform(pServices, pServices + nServices, ret.getArray(),
+ [](const char* pService) -> OUString { return OUString::createFromAscii(pService); });
+
+ return ret;
+}
+
+}
+
+class SwXFootnote::Impl
+ : public SvtListener
+{
+public:
+
+ SwXFootnote& m_rThis;
+ uno::WeakReference<uno::XInterface> m_wThis;
+ const bool m_bIsEndnote;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ bool m_bIsDescriptor;
+ SwFormatFootnote* m_pFormatFootnote;
+ OUString m_sLabel;
+
+ Impl(SwXFootnote& rThis,
+ SwFormatFootnote* const pFootnote,
+ const bool bIsEndnote)
+ : m_rThis(rThis)
+ , m_bIsEndnote(bIsEndnote)
+ , m_bIsDescriptor(nullptr == pFootnote)
+ , m_pFormatFootnote(pFootnote)
+ {
+ m_pFormatFootnote && StartListening(m_pFormatFootnote->GetNotifier());
+ }
+
+ const SwFormatFootnote* GetFootnoteFormat() const {
+ return m_rThis.GetDoc() ? m_pFormatFootnote : nullptr;
+ }
+
+ SwFormatFootnote const& GetFootnoteFormatOrThrow() const {
+ SwFormatFootnote const*const pFootnote( GetFootnoteFormat() );
+ if (!pFootnote) {
+ throw uno::RuntimeException("SwXFootnote: disposed or invalid", nullptr);
+ }
+ return *pFootnote;
+ }
+
+ void Invalidate();
+protected:
+ void Notify(const SfxHint& rHint) override;
+
+};
+
+void SwXFootnote::Impl::Invalidate()
+{
+ EndListeningAll();
+ m_pFormatFootnote = nullptr;
+ m_rThis.SetDoc(nullptr);
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+}
+
+void SwXFootnote::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ Invalidate();
+}
+
+SwXFootnote::SwXFootnote(const bool bEndnote)
+ : SwXText(nullptr, CursorType::Footnote)
+ , m_pImpl( new SwXFootnote::Impl(*this, nullptr, bEndnote) )
+{
+}
+
+SwXFootnote::SwXFootnote(SwDoc & rDoc, SwFormatFootnote & rFormat)
+ : SwXText(& rDoc, CursorType::Footnote)
+ , m_pImpl( new SwXFootnote::Impl(*this, &rFormat, rFormat.IsEndNote()) )
+{
+}
+
+SwXFootnote::~SwXFootnote()
+{
+}
+
+uno::Reference<text::XFootnote>
+SwXFootnote::CreateXFootnote(SwDoc & rDoc, SwFormatFootnote *const pFootnoteFormat,
+ bool const isEndnote)
+{
+ // i#105557: do not iterate over the registered clients: race condition
+ uno::Reference<text::XFootnote> xNote;
+ if (pFootnoteFormat)
+ {
+ xNote = pFootnoteFormat->GetXFootnote();
+ }
+ if (!xNote.is())
+ {
+ SwXFootnote *const pNote(pFootnoteFormat
+ ? new SwXFootnote(rDoc, *pFootnoteFormat)
+ : new SwXFootnote(isEndnote));
+ xNote.set(pNote);
+ if (pFootnoteFormat)
+ {
+ pFootnoteFormat->SetXFootnote(xNote);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pNote->m_pImpl->m_wThis = xNote;
+ }
+ return xNote;
+}
+
+const uno::Sequence< sal_Int8 > & SwXFootnote::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXFootnoteUnoTunnelId;
+ return theSwXFootnoteUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXFootnote::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ const sal_Int64 nRet( comphelper::getSomethingImpl<SwXFootnote>(rId, this) );
+ return nRet ? nRet : SwXText::getSomething(rId);
+}
+
+OUString SAL_CALL
+SwXFootnote::getImplementationName()
+{
+ return "SwXFootnote";
+}
+
+char const*const g_ServicesFootnote[] =
+{
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.Footnote",
+ "com.sun.star.text.Text",
+ "com.sun.star.text.Endnote", // NB: only supported for endnotes!
+};
+
+const size_t g_nServicesEndnote( SAL_N_ELEMENTS(g_ServicesFootnote) );
+
+const size_t g_nServicesFootnote( g_nServicesEndnote - 1 ); // NB: omit!
+
+sal_Bool SAL_CALL SwXFootnote::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXFootnote::getSupportedServiceNames()
+{
+ SolarMutexGuard g;
+ return GetSupportedServiceNamesImpl(
+ (m_pImpl->m_bIsEndnote) ? g_nServicesEndnote : g_nServicesFootnote,
+ g_ServicesFootnote);
+}
+
+uno::Sequence< uno::Type > SAL_CALL
+SwXFootnote::getTypes()
+{
+ const uno::Sequence< uno::Type > aTypes = SwXFootnote_Base::getTypes();
+ const uno::Sequence< uno::Type > aTextTypes = SwXText::getTypes();
+ return ::comphelper::concatSequences(aTypes, aTextTypes);
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL
+SwXFootnote::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Any SAL_CALL
+SwXFootnote::queryInterface(const uno::Type& rType)
+{
+ const uno::Any ret = SwXFootnote_Base::queryInterface(rType);
+ return (ret.getValueType() == cppu::UnoType<void>::get())
+ ? SwXText::queryInterface(rType)
+ : ret;
+}
+
+OUString SAL_CALL SwXFootnote::getLabel()
+{
+ SolarMutexGuard aGuard;
+
+ OUString sRet;
+ SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
+ if(pFormat)
+ {
+ sRet = pFormat->GetNumStr();
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ sRet = m_pImpl->m_sLabel;
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+ return sRet;
+}
+
+void SAL_CALL
+SwXFootnote::setLabel(const OUString& aLabel)
+{
+ SolarMutexGuard aGuard;
+ OUString newLabel(aLabel);
+ //new line must not occur as footnote label
+ if(newLabel.indexOf('\n') >=0 )
+ {
+ newLabel = newLabel.replace('\n', ' ');
+ }
+ SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
+ if(pFormat)
+ {
+ const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote();
+ OSL_ENSURE(pTextFootnote, "No TextNode?");
+ SwTextNode& rTextNode = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
+
+ SwPaM aPam(rTextNode, pTextFootnote->GetStart());
+ GetDoc()->SetCurFootnote(aPam, newLabel, pFormat->IsEndNote());
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_sLabel = newLabel;
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+}
+
+void SAL_CALL
+SwXFootnote::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<lang::XUnoTunnel> xRangeTunnel(
+ xTextRange, uno::UNO_QUERY);
+ SwXTextRange *const pRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+ SwDoc *const pNewDoc =
+ pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
+ if (!pNewDoc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwUnoInternalPaM aPam(*pNewDoc);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+
+ UnoActionContext aCont(pNewDoc);
+ pNewDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
+ aPam.DeleteMark();
+ SwFormatFootnote aFootNote(m_pImpl->m_bIsEndnote);
+ if (!m_pImpl->m_sLabel.isEmpty())
+ {
+ aFootNote.SetNumStr(m_pImpl->m_sLabel);
+ }
+
+ SwXTextCursor const*const pTextCursor(
+ dynamic_cast<SwXTextCursor*>(pCursor));
+ const bool bForceExpandHints( pTextCursor && pTextCursor->IsAtEndOfMeta() );
+ const SetAttrMode nInsertFlags = bForceExpandHints
+ ? SetAttrMode::FORCEHINTEXPAND
+ : SetAttrMode::DEFAULT;
+
+ pNewDoc->getIDocumentContentOperations().InsertPoolItem(aPam, aFootNote, nInsertFlags);
+
+ SwTextFootnote *const pTextAttr = static_cast<SwTextFootnote*>(
+ aPam.GetNode().GetTextNode()->GetTextAttrForCharAt(
+ aPam.GetPoint()->nContent.GetIndex()-1, RES_TXTATR_FTN ));
+
+ if (pTextAttr)
+ {
+ m_pImpl->EndListeningAll();
+ SwFormatFootnote* pFootnote = const_cast<SwFormatFootnote*>(&pTextAttr->GetFootnote());
+ m_pImpl->m_pFormatFootnote = pFootnote;
+ m_pImpl->StartListening(pFootnote->GetNotifier());
+ // force creation of sequence id - is used for references
+ if (pNewDoc->IsInReading())
+ {
+ pTextAttr->SetSeqNo(pNewDoc->GetFootnoteIdxs().size());
+ }
+ else
+ {
+ pTextAttr->SetSeqRefNo();
+ }
+ }
+ m_pImpl->m_bIsDescriptor = false;
+ SetDoc(pNewDoc);
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXFootnote::getAnchor()
+{
+ SolarMutexGuard aGuard;
+ return m_pImpl->GetFootnoteFormatOrThrow().getAnchor(*GetDoc());
+}
+
+void SAL_CALL SwXFootnote::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
+
+ SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
+ OSL_ENSURE(pTextFootnote, "no TextNode?");
+ SwTextNode& rTextNode = const_cast<SwTextNode&>(pTextFootnote->GetTextNode());
+ const sal_Int32 nPos = pTextFootnote->GetStart();
+ SwPaM aPam(rTextNode, nPos, rTextNode, nPos+1);
+ GetDoc()->getIDocumentContentOperations().DeleteAndJoin( aPam );
+}
+
+void SAL_CALL
+SwXFootnote::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL
+SwXFootnote::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+const SwStartNode *SwXFootnote::GetStartNode() const
+{
+ SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
+ if(pFormat)
+ {
+ const SwTextFootnote* pTextFootnote = pFormat->GetTextFootnote();
+ if( pTextFootnote )
+ {
+ return pTextFootnote->GetStartNode()->GetNode().GetStartNode();
+ }
+ }
+ return nullptr;
+}
+
+uno::Reference< text::XTextCursor >
+SwXFootnote::CreateCursor()
+{
+ return createTextCursor();
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXFootnote::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+
+ SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
+
+ SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
+ SwPosition aPos( *pTextFootnote->GetStartNode() );
+ rtl::Reference<SwXTextCursor> pXCursor =
+ new SwXTextCursor(*GetDoc(), this, CursorType::Footnote, aPos);
+ auto& rUnoCursor(pXCursor->GetCursor());
+ rUnoCursor.Move(fnMoveForward, GoInNode);
+ return static_cast<text::XWordCursor*>(pXCursor.get());
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXFootnote::createTextCursorByRange(
+ const uno::Reference< text::XTextRange > & xTextPosition)
+{
+ SolarMutexGuard aGuard;
+
+ SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
+
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (!::sw::XTextRangeToSwPaM(aPam, xTextPosition))
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
+ SwNode const*const pFootnoteStartNode = &pTextFootnote->GetStartNode()->GetNode();
+
+ const SwNode* pStart = aPam.GetNode().FindFootnoteStartNode();
+ if (pStart != pFootnoteStartNode)
+ {
+ throw uno::RuntimeException();
+ }
+
+ const uno::Reference< text::XTextCursor > xRet =
+ static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), this, CursorType::Footnote,
+ *aPam.GetPoint(), aPam.GetMark()));
+ return xRet;
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXFootnote::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+
+ SwFormatFootnote const& rFormat( m_pImpl->GetFootnoteFormatOrThrow() );
+
+ SwTextFootnote const*const pTextFootnote = rFormat.GetTextFootnote();
+ SwPosition aPos( *pTextFootnote->GetStartNode() );
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Footnote);
+}
+
+uno::Type SAL_CALL SwXFootnote::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXFootnote::hasElements()
+{
+ return true;
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXFootnote::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+ static uno::Reference< beans::XPropertySetInfo > xRet =
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_FOOTNOTE)
+ ->getPropertySetInfo();
+ return xRet;
+}
+
+void SAL_CALL
+SwXFootnote::setPropertyValue(const OUString&, const uno::Any&)
+{
+ //no values to be set
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL
+SwXFootnote::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ if (! ::sw::GetDefaultTextContentValue(aRet, rPropertyName))
+ {
+ if (rPropertyName == UNO_NAME_START_REDLINE ||
+ rPropertyName == UNO_NAME_END_REDLINE)
+ {
+ //redline can only be returned if it's a living object
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ aRet = SwXText::getPropertyValue(rPropertyName);
+ }
+ }
+ else if (rPropertyName == UNO_NAME_REFERENCE_ID)
+ {
+ SwFormatFootnote const*const pFormat = m_pImpl->GetFootnoteFormat();
+ if (pFormat)
+ {
+ SwTextFootnote const*const pTextFootnote = pFormat->GetTextFootnote();
+ OSL_ENSURE(pTextFootnote, "no TextNode?");
+ aRet <<= static_cast<sal_Int16>(pTextFootnote->GetSeqRefNo());
+ }
+ }
+ else
+ {
+ beans::UnknownPropertyException aExcept;
+ aExcept.Message = rPropertyName;
+ throw aExcept;
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL
+SwXFootnote::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXFootnote::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXFootnote::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXFootnote::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXFootnote::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXFootnote::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXFootnote::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXFootnote::removeVetoableChangeListener(): not implemented");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoidx.cxx b/sw/source/core/unocore/unoidx.cxx
new file mode 100644
index 000000000..b2d521c6d
--- /dev/null
+++ b/sw/source/core/unocore/unoidx.cxx
@@ -0,0 +1,3094 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <unoidx.hxx>
+#include <unoidxcoll.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/ChapterFormat.hpp>
+#include <com/sun/star/text/ReferenceFieldPart.hpp>
+#include <com/sun/star/text/BibliographyDataField.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+
+#include <osl/mutex.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/multicontainer2.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/svapp.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <editeng/memberids.h>
+#include <swtypes.hxx>
+#include <shellres.hxx>
+#include <viewsh.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <docary.hxx>
+#include <fmtcntnt.hxx>
+#include <unomap.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unosection.hxx>
+#include <doctxm.hxx>
+#include <txttxmrk.hxx>
+#include <ndtxt.hxx>
+#include <docsh.hxx>
+#include <chpfld.hxx>
+#include <editsh.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <strings.hrc>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/listener.hxx>
+#include <mutex>
+
+using namespace ::com::sun::star;
+
+/// @throws lang::IllegalArgumentException
+template<typename T>
+static T
+lcl_AnyToType(uno::Any const& rVal)
+{
+ T aRet{};
+ if(!(rVal >>= aRet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ return aRet;
+}
+
+/// @throws lang::IllegalArgumentException
+template<typename T>
+static void lcl_AnyToBitMask(uno::Any const& rValue,
+ T & rBitMask, const T nBit)
+{
+ rBitMask = lcl_AnyToType<bool>(rValue)
+ ? (rBitMask | nBit)
+ : (rBitMask & ~nBit);
+}
+
+template<typename T>
+static void lcl_BitMaskToAny(uno::Any & o_rValue,
+ const T nBitMask, const T nBit)
+{
+ const bool bRet(nBitMask & nBit);
+ o_rValue <<= bRet;
+}
+
+static void
+lcl_ReAssignTOXType(SwDoc& rDoc, SwTOXBase& rTOXBase, const OUString& rNewName)
+{
+ const sal_uInt16 nUserCount = rDoc.GetTOXTypeCount( TOX_USER );
+ const SwTOXType* pNewType = nullptr;
+ for(sal_uInt16 nUser = 0; nUser < nUserCount; nUser++)
+ {
+ const SwTOXType* pType = rDoc.GetTOXType( TOX_USER, nUser );
+ if (pType->GetTypeName()==rNewName)
+ {
+ pNewType = pType;
+ break;
+ }
+ }
+ if(!pNewType)
+ {
+ SwTOXType aNewType(rDoc, TOX_USER, rNewName);
+ pNewType = rDoc.InsertTOXType( aNewType );
+ }
+
+ rTOXBase.RegisterToTOXType( *const_cast<SwTOXType*>(pNewType) );
+}
+
+constexpr OUStringLiteral cUserDefined = u"User-Defined";
+const char cUserSuffix[] = " (user)";
+#define USER_LEN 12
+#define USER_AND_SUFFIXLEN 19
+
+static void lcl_ConvertTOUNameToProgrammaticName(OUString& rTmp)
+{
+ ShellResource* pShellRes = SwViewShell::GetShellRes();
+
+ if(rTmp==pShellRes->aTOXUserName)
+ {
+ rTmp = cUserDefined;
+ }
+ // if the version is not English but the alternative index's name is
+ // "User-Defined" a " (user)" is appended
+ else if(rTmp == cUserDefined)
+ {
+ rTmp += cUserSuffix;
+ }
+}
+
+static void
+lcl_ConvertTOUNameToUserName(OUString& rTmp)
+{
+ ShellResource* pShellRes = SwViewShell::GetShellRes();
+ if (rTmp == cUserDefined)
+ {
+ rTmp = pShellRes->aTOXUserName;
+ }
+ else if (pShellRes->aTOXUserName != cUserDefined &&
+ USER_AND_SUFFIXLEN == rTmp.getLength())
+ {
+ //make sure that in non-English versions the " (user)" suffix is removed
+ if (rTmp.startsWith(cUserDefined) &&
+ rTmp.match(cUserSuffix, USER_LEN))
+ {
+ rTmp = cUserDefined;
+ }
+ }
+}
+
+typedef ::cppu::WeakImplHelper
+< lang::XServiceInfo
+, container::XIndexReplace
+> SwXDocumentIndexStyleAccess_Base;
+
+class SwXDocumentIndex::StyleAccess_Impl
+ : public SwXDocumentIndexStyleAccess_Base
+{
+
+private:
+ /// can be destroyed threadsafely, so no UnoImplPtr here
+ ::rtl::Reference<SwXDocumentIndex> m_xParent;
+
+ virtual ~StyleAccess_Impl() override;
+
+public:
+ explicit StyleAccess_Impl(SwXDocumentIndex& rParentIdx);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL
+ supportsService(const OUString& rServiceName) override;
+ virtual uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override;
+
+ // XIndexReplace
+ virtual void SAL_CALL
+ replaceByIndex(sal_Int32 Index, const uno::Any& rElement) override;
+
+};
+
+typedef ::cppu::WeakImplHelper
+< lang::XServiceInfo
+, container::XIndexReplace
+> SwXDocumentIndexTokenAccess_Base;
+
+class SwXDocumentIndex::TokenAccess_Impl
+ : public SwXDocumentIndexTokenAccess_Base
+{
+
+private:
+ /// can be destroyed threadsafely, so no UnoImplPtr here
+ ::rtl::Reference<SwXDocumentIndex> m_xParent;
+
+ virtual ~TokenAccess_Impl() override;
+
+public:
+
+ explicit TokenAccess_Impl(SwXDocumentIndex& rParentIdx);
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL
+ supportsService(const OUString& rServiceName) override;
+ virtual uno::Sequence< OUString > SAL_CALL
+ getSupportedServiceNames() override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override;
+
+ // XIndexReplace
+ virtual void SAL_CALL
+ replaceByIndex(sal_Int32 Index, const uno::Any& rElement) override;
+
+};
+
+namespace {
+
+class SwDocIndexDescriptorProperties_Impl
+{
+private:
+ std::unique_ptr<SwTOXBase> m_pTOXBase;
+ OUString m_sUserTOXTypeName;
+
+public:
+ explicit SwDocIndexDescriptorProperties_Impl(SwTOXType const*const pType);
+
+ SwTOXBase & GetTOXBase() { return *m_pTOXBase; }
+ const OUString& GetTypeName() const { return m_sUserTOXTypeName; }
+ void SetTypeName(const OUString& rSet) { m_sUserTOXTypeName = rSet; }
+};
+
+}
+
+SwDocIndexDescriptorProperties_Impl::SwDocIndexDescriptorProperties_Impl(
+ SwTOXType const*const pType)
+{
+ SwForm aForm(pType->GetType());
+ m_pTOXBase.reset(new SwTOXBase(pType, aForm,
+ SwTOXElement::Mark, pType->GetTypeName()));
+ if(pType->GetType() == TOX_CONTENT || pType->GetType() == TOX_USER)
+ {
+ m_pTOXBase->SetLevel(MAXLEVEL);
+ }
+ m_sUserTOXTypeName = pType->GetTypeName();
+}
+
+static sal_uInt16
+lcl_TypeToPropertyMap_Index(const TOXTypes eType)
+{
+ switch (eType)
+ {
+ case TOX_INDEX: return PROPERTY_MAP_INDEX_IDX;
+ case TOX_CONTENT: return PROPERTY_MAP_INDEX_CNTNT;
+ case TOX_TABLES: return PROPERTY_MAP_INDEX_TABLES;
+ case TOX_ILLUSTRATIONS: return PROPERTY_MAP_INDEX_ILLUSTRATIONS;
+ case TOX_OBJECTS: return PROPERTY_MAP_INDEX_OBJECTS;
+ case TOX_AUTHORITIES: return PROPERTY_MAP_BIBLIOGRAPHY;
+ //case TOX_USER:
+ default:
+ return PROPERTY_MAP_INDEX_USER;
+ }
+}
+
+class SwXDocumentIndex::Impl final: public SvtListener
+{
+private:
+ ::osl::Mutex m_Mutex; // just for OInterfaceContainerHelper2
+ SwSectionFormat* m_pFormat;
+
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ ::comphelper::OMultiTypeInterfaceContainerHelper2 m_Listeners;
+ SfxItemPropertySet const& m_rPropSet;
+ const TOXTypes m_eTOXType;
+ bool m_bIsDescriptor;
+ SwDoc* m_pDoc;
+ std::unique_ptr<SwDocIndexDescriptorProperties_Impl> m_pProps;
+ uno::WeakReference<container::XIndexReplace> m_wStyleAccess;
+ uno::WeakReference<container::XIndexReplace> m_wTokenAccess;
+
+ Impl(SwDoc& rDoc, const TOXTypes eType, SwTOXBaseSection *const pBaseSection)
+ : m_pFormat(pBaseSection ? pBaseSection->GetFormat() : nullptr)
+ , m_Listeners(m_Mutex)
+ , m_rPropSet(*aSwMapProvider.GetPropertySet(lcl_TypeToPropertyMap_Index(eType)))
+ , m_eTOXType(eType)
+ , m_bIsDescriptor(nullptr == pBaseSection)
+ , m_pDoc(&rDoc)
+ , m_pProps(m_bIsDescriptor
+ ? new SwDocIndexDescriptorProperties_Impl(rDoc.GetTOXType(eType, 0))
+ : nullptr)
+ {
+ if(m_pFormat)
+ StartListening(m_pFormat->GetNotifier());
+ }
+
+ void SetSectionFormat(SwSectionFormat& rFormat)
+ {
+ EndListeningAll();
+ m_pFormat = &rFormat;
+ StartListening(rFormat.GetNotifier());
+ }
+
+ SwSectionFormat* GetSectionFormat() const {
+ return m_pFormat;
+ }
+
+ SwTOXBase & GetTOXSectionOrThrow() const
+ {
+ SwSectionFormat *const pSectionFormat(GetSectionFormat());
+ SwTOXBase *const pTOXSection( m_bIsDescriptor
+ ? &m_pProps->GetTOXBase()
+ : (pSectionFormat
+ ? static_cast<SwTOXBaseSection*>(pSectionFormat->GetSection())
+ : nullptr));
+ if (!pTOXSection)
+ {
+ throw uno::RuntimeException(
+ "SwXDocumentIndex: disposed or invalid", nullptr);
+ }
+ return *pTOXSection;
+ }
+
+ sal_Int32 GetFormMax() const
+ {
+ SwTOXBase & rSection( GetTOXSectionOrThrow() );
+ return m_bIsDescriptor
+ ? SwForm::GetFormMaxLevel(m_eTOXType)
+ : rSection.GetTOXForm().GetFormMax();
+ }
+ virtual void Notify(const SfxHint&) override;
+
+};
+
+void SwXDocumentIndex::Impl::Notify(const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ if(pLegacy->m_pOld && pLegacy->m_pOld->Which() == RES_REMOVE_UNO_OBJECT)
+ m_pFormat = nullptr;
+ }
+ else if(rHint.GetId() == SfxHintId::Dying)
+ m_pFormat = nullptr;
+ if(!m_pFormat)
+ {
+ EndListeningAll();
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ m_Listeners.disposeAndClear(ev);
+ }
+}
+
+SwXDocumentIndex::SwXDocumentIndex(
+ SwTOXBaseSection & rBaseSection, SwDoc & rDoc)
+ : m_pImpl( new SwXDocumentIndex::Impl(
+ rDoc, rBaseSection.SwTOXBase::GetType(), & rBaseSection) )
+{
+}
+
+SwXDocumentIndex::SwXDocumentIndex(const TOXTypes eType, SwDoc& rDoc)
+ : m_pImpl( new SwXDocumentIndex::Impl(rDoc, eType, nullptr) )
+{
+}
+
+SwXDocumentIndex::~SwXDocumentIndex()
+{
+}
+
+uno::Reference<text::XDocumentIndex>
+SwXDocumentIndex::CreateXDocumentIndex(
+ SwDoc & rDoc, SwTOXBaseSection * pSection, TOXTypes const eTypes)
+{
+ // re-use existing SwXDocumentIndex
+ // #i105557#: do not iterate over the registered clients: race condition
+ uno::Reference<text::XDocumentIndex> xIndex;
+ if (pSection)
+ {
+ SwSectionFormat const *const pFormat = pSection->GetFormat();
+ xIndex.set(pFormat->GetXObject(), uno::UNO_QUERY);
+ }
+ if (!xIndex.is())
+ {
+ SwXDocumentIndex *const pIndex(pSection
+ ? new SwXDocumentIndex(*pSection, rDoc)
+ : new SwXDocumentIndex(eTypes, rDoc));
+ xIndex.set(pIndex);
+ if (pSection)
+ {
+ pSection->GetFormat()->SetXObject(xIndex);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pIndex->m_pImpl->m_wThis = xIndex;
+ }
+ return xIndex;
+}
+
+const uno::Sequence< sal_Int8 > & SwXDocumentIndex::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXDocumentIndexUnoTunnelId;
+ return theSwXDocumentIndexUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXDocumentIndex::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXDocumentIndex>(rId, this);
+}
+
+OUString SAL_CALL
+SwXDocumentIndex::getImplementationName()
+{
+ return "SwXDocumentIndex";
+}
+
+sal_Bool SAL_CALL
+SwXDocumentIndex::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXDocumentIndex::getSupportedServiceNames()
+{
+ SolarMutexGuard g;
+
+ uno::Sequence< OUString > aRet(2);
+ OUString* pArray = aRet.getArray();
+ pArray[0] = "com.sun.star.text.BaseIndex";
+ switch (m_pImpl->m_eTOXType)
+ {
+ case TOX_INDEX:
+ pArray[1] = "com.sun.star.text.DocumentIndex";
+ break;
+ case TOX_CONTENT:
+ pArray[1] = "com.sun.star.text.ContentIndex";
+ break;
+ case TOX_TABLES:
+ pArray[1] = "com.sun.star.text.TableIndex";
+ break;
+ case TOX_ILLUSTRATIONS:
+ pArray[1] = "com.sun.star.text.IllustrationsIndex";
+ break;
+ case TOX_OBJECTS:
+ pArray[1] = "com.sun.star.text.ObjectIndex";
+ break;
+ case TOX_AUTHORITIES:
+ pArray[1] = "com.sun.star.text.Bibliography";
+ break;
+ //case TOX_USER:
+ default:
+ pArray[1] = "com.sun.star.text.UserDefinedIndex";
+ }
+ return aRet;
+}
+
+OUString SAL_CALL SwXDocumentIndex::getServiceName()
+{
+ SolarMutexGuard g;
+
+ SwServiceType nObjectType = SwServiceType::TypeIndex;
+ switch (m_pImpl->m_eTOXType)
+ {
+ case TOX_USER: nObjectType = SwServiceType::UserIndex;
+ break;
+ case TOX_CONTENT: nObjectType = SwServiceType::ContentIndex;
+ break;
+ case TOX_ILLUSTRATIONS: nObjectType = SwServiceType::IndexIllustrations;
+ break;
+ case TOX_OBJECTS: nObjectType = SwServiceType::IndexObjects;
+ break;
+ case TOX_TABLES: nObjectType = SwServiceType::IndexTables;
+ break;
+ case TOX_AUTHORITIES: nObjectType = SwServiceType::IndexBibliography;
+ break;
+ default:
+ break;
+ }
+ return SwXServiceProvider::GetProviderName(nObjectType);
+}
+
+void SAL_CALL SwXDocumentIndex::update()
+{
+ return refresh(); // update is from deprecated XDocumentIndex
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXDocumentIndex::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ const uno::Reference< beans::XPropertySetInfo > xRef =
+ m_pImpl->m_rPropSet.getPropertySetInfo();
+ return xRef;
+}
+
+void SAL_CALL
+SwXDocumentIndex::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw beans::PropertyVetoException(
+ "Property is read-only: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ SwSectionFormat *const pSectionFormat(m_pImpl->GetSectionFormat());
+ SwTOXBase & rTOXBase( m_pImpl->GetTOXSectionOrThrow() );
+
+ SwTOXElement nCreate = rTOXBase.GetCreateType();
+ SwTOOElements nOLEOptions = rTOXBase.GetOLEOptions();
+ const TOXTypes eTxBaseType = rTOXBase.GetTOXType()->GetType();
+ SwTOIOptions nTOIOptions = (eTxBaseType == TOX_INDEX)
+ ? rTOXBase.GetOptions() : SwTOIOptions::NONE;
+ SwForm aForm(rTOXBase.GetTOXForm());
+ bool bForm = false;
+ switch (pEntry->nWID)
+ {
+ case WID_IDX_TITLE:
+ {
+ OUString sNewName;
+ if (!(rValue >>= sNewName))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rTOXBase.SetTitle(sNewName);
+ }
+ break;
+ case WID_IDX_NAME:
+ {
+ OUString sNewName;
+ if (!(rValue >>= sNewName))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rTOXBase.SetTOXName(sNewName);
+ }
+ break;
+ case WID_USER_IDX_NAME:
+ {
+ OUString sNewName;
+ if (!(rValue >>= sNewName))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ lcl_ConvertTOUNameToUserName(sNewName);
+ OSL_ENSURE(TOX_USER == eTxBaseType,
+ "tox type name can only be changed for user indexes");
+ if (pSectionFormat)
+ {
+ if (rTOXBase.GetTOXType()->GetTypeName() != sNewName)
+ {
+ lcl_ReAssignTOXType(*pSectionFormat->GetDoc(),
+ rTOXBase, sNewName);
+ }
+ }
+ else
+ {
+ m_pImpl->m_pProps->SetTypeName(sNewName);
+ }
+ }
+ break;
+ case WID_IDX_LOCALE:
+ {
+ lang::Locale aLocale;
+ if (!(rValue>>= aLocale))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rTOXBase.SetLanguage( LanguageTag::convertToLanguageType(aLocale));
+ }
+ break;
+ case WID_IDX_SORT_ALGORITHM:
+ {
+ OUString sTmp;
+ if (!(rValue >>= sTmp))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rTOXBase.SetSortAlgorithm(sTmp);
+ }
+ break;
+ case WID_LEVEL:
+ {
+ rTOXBase.SetLevel(lcl_AnyToType<sal_Int16>(rValue));
+ }
+ break;
+ case WID_TOC_BOOKMARK:
+ {
+ rTOXBase.SetBookmarkName(lcl_AnyToType<OUString>(rValue));
+ nCreate = SwTOXElement::Bookmark;
+ rTOXBase.SetCreate(nCreate);
+ }
+ break;
+ case WID_CREATE_FROM_MARKS:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Mark);
+ break;
+ case WID_CREATE_FROM_OUTLINE:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::OutlineLevel);
+ break;
+ case WID_TOC_PARAGRAPH_OUTLINE_LEVEL:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::ParagraphOutlineLevel);
+ break;
+ case WID_TAB_IN_TOC:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::TableInToc);
+ break;
+ case WID_TOC_NEWLINE:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Newline);
+ break;
+// case WID_PARAGRAPH_STYLE_NAMES :OSL_FAIL("not implemented")
+// break;
+ case WID_HIDE_TABLEADER_PAGENUMBERS:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::TableLeader);
+ break ;
+ case WID_CREATE_FROM_CHAPTER:
+ rTOXBase.SetFromChapter(lcl_AnyToType<bool>(rValue));
+ break;
+ case WID_CREATE_FROM_LABELS:
+ rTOXBase.SetFromObjectNames(! lcl_AnyToType<bool>(rValue));
+ break;
+ case WID_PROTECTED:
+ {
+ bool bSet = lcl_AnyToType<bool>(rValue);
+ rTOXBase.SetProtected(bSet);
+ if (pSectionFormat)
+ {
+ static_cast<SwTOXBaseSection &>(rTOXBase).SetProtect(bSet);
+ }
+ }
+ break;
+ case WID_USE_ALPHABETICAL_SEPARATORS:
+ lcl_AnyToBitMask(rValue, nTOIOptions,
+ SwTOIOptions::AlphaDelimiter);
+ break;
+ case WID_USE_KEY_AS_ENTRY:
+ lcl_AnyToBitMask(rValue, nTOIOptions,
+ SwTOIOptions::KeyAsEntry);
+ break;
+ case WID_USE_COMBINED_ENTRIES:
+ lcl_AnyToBitMask(rValue, nTOIOptions,
+ SwTOIOptions::SameEntry);
+ break;
+ case WID_IS_CASE_SENSITIVE:
+ lcl_AnyToBitMask(rValue, nTOIOptions,
+ SwTOIOptions::CaseSensitive);
+ break;
+ case WID_USE_P_P:
+ lcl_AnyToBitMask(rValue, nTOIOptions, SwTOIOptions::FF);
+ break;
+ case WID_USE_DASH:
+ lcl_AnyToBitMask(rValue, nTOIOptions, SwTOIOptions::Dash);
+ break;
+ case WID_USE_UPPER_CASE:
+ lcl_AnyToBitMask(rValue, nTOIOptions,
+ SwTOIOptions::InitialCaps);
+ break;
+ case WID_IS_COMMA_SEPARATED:
+ bForm = true;
+ aForm.SetCommaSeparated(lcl_AnyToType<bool>(rValue));
+ break;
+ case WID_LABEL_CATEGORY:
+ {
+ // convert file-format/API/external programmatic english name
+ // to internal UI name before usage
+ rTOXBase.SetSequenceName( SwStyleNameMapper::GetSpecialExtraUIName(
+ lcl_AnyToType<OUString>(rValue) ) );
+ }
+ break;
+ case WID_LABEL_DISPLAY_TYPE:
+ {
+ const sal_Int16 nVal = lcl_AnyToType<sal_Int16>(rValue);
+ sal_uInt16 nSet = CAPTION_COMPLETE;
+ switch (nVal)
+ {
+ case text::ReferenceFieldPart::TEXT:
+ nSet = CAPTION_COMPLETE;
+ break;
+ case text::ReferenceFieldPart::CATEGORY_AND_NUMBER:
+ nSet = CAPTION_NUMBER;
+ break;
+ case text::ReferenceFieldPart::ONLY_CAPTION:
+ nSet = CAPTION_TEXT;
+ break;
+ default:
+ throw lang::IllegalArgumentException();
+ }
+ rTOXBase.SetCaptionDisplay(static_cast<SwCaptionDisplay>(nSet));
+ }
+ break;
+ case WID_USE_LEVEL_FROM_SOURCE:
+ rTOXBase.SetLevelFromChapter(lcl_AnyToType<bool>(rValue));
+ break;
+ case WID_MAIN_ENTRY_CHARACTER_STYLE_NAME:
+ {
+ OUString aString;
+ SwStyleNameMapper::FillUIName(lcl_AnyToType<OUString>(rValue),
+ aString, SwGetPoolIdFromName::ChrFmt);
+ rTOXBase.SetMainEntryCharStyle( aString );
+ }
+ break;
+ case WID_CREATE_FROM_TABLES:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Table);
+ break;
+ case WID_CREATE_FROM_TEXT_FRAMES:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Frame);
+ break;
+ case WID_CREATE_FROM_GRAPHIC_OBJECTS:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Graphic);
+ break;
+ case WID_CREATE_FROM_EMBEDDED_OBJECTS:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Ole);
+ break;
+ case WID_CREATE_FROM_STAR_MATH:
+ lcl_AnyToBitMask(rValue, nOLEOptions, SwTOOElements::Math);
+ break;
+ case WID_CREATE_FROM_STAR_CHART:
+ lcl_AnyToBitMask(rValue, nOLEOptions, SwTOOElements::Chart);
+ break;
+ case WID_CREATE_FROM_STAR_CALC:
+ lcl_AnyToBitMask(rValue, nOLEOptions, SwTOOElements::Calc);
+ break;
+ case WID_CREATE_FROM_STAR_DRAW:
+ lcl_AnyToBitMask(rValue, nOLEOptions,
+ SwTOOElements::DrawImpress);
+ break;
+ case WID_CREATE_FROM_OTHER_EMBEDDED_OBJECTS:
+ lcl_AnyToBitMask(rValue, nOLEOptions, SwTOOElements::Other);
+ break;
+ case WID_PARA_HEAD:
+ {
+ OUString aString;
+ SwStyleNameMapper::FillUIName( lcl_AnyToType<OUString>(rValue),
+ aString, SwGetPoolIdFromName::TxtColl);
+ bForm = true;
+ // Header is on Pos 0
+ aForm.SetTemplate( 0, aString );
+ }
+ break;
+ case WID_IS_RELATIVE_TABSTOPS:
+ bForm = true;
+ aForm.SetRelTabPos(lcl_AnyToType<bool>(rValue));
+ break;
+ case WID_PARA_SEP:
+ {
+ OUString aString;
+ bForm = true;
+ SwStyleNameMapper::FillUIName( lcl_AnyToType<OUString>(rValue),
+ aString, SwGetPoolIdFromName::TxtColl);
+ aForm.SetTemplate( 1, aString );
+ }
+ break;
+ case WID_CREATE_FROM_PARAGRAPH_STYLES:
+ lcl_AnyToBitMask(rValue, nCreate, SwTOXElement::Template);
+ break;
+
+ case WID_PARA_LEV1:
+ case WID_PARA_LEV2:
+ case WID_PARA_LEV3:
+ case WID_PARA_LEV4:
+ case WID_PARA_LEV5:
+ case WID_PARA_LEV6:
+ case WID_PARA_LEV7:
+ case WID_PARA_LEV8:
+ case WID_PARA_LEV9:
+ case WID_PARA_LEV10:
+ {
+ bForm = true;
+ // in sdbcx::Index Label 1 begins at Pos 2 otherwise at Pos 1
+ const sal_uInt16 nLPos = rTOXBase.GetType() == TOX_INDEX ? 2 : 1;
+ OUString aString;
+ SwStyleNameMapper::FillUIName( lcl_AnyToType<OUString>(rValue),
+ aString, SwGetPoolIdFromName::TxtColl);
+ aForm.SetTemplate(nLPos + pEntry->nWID - WID_PARA_LEV1, aString );
+ }
+ break;
+ default:
+ //this is for items only
+ if (WID_PRIMARY_KEY > pEntry->nWID)
+ {
+ const SwAttrSet& rSet =
+ SwDoc::GetTOXBaseAttrSet(rTOXBase);
+ SfxItemSet aAttrSet(rSet);
+ m_pImpl->m_rPropSet.setPropertyValue(
+ rPropertyName, rValue, aAttrSet);
+
+ const SwSectionFormats& rSects = m_pImpl->m_pDoc->GetSections();
+ for (size_t i = 0; i < rSects.size(); ++i)
+ {
+ const SwSectionFormat* pTmpFormat = rSects[ i ];
+ if (pTmpFormat == pSectionFormat)
+ {
+ SwSectionData tmpData(
+ static_cast<SwTOXBaseSection&>(rTOXBase));
+ m_pImpl->m_pDoc->UpdateSection(i, tmpData, & aAttrSet);
+ break;
+ }
+ }
+ }
+ }
+ rTOXBase.SetCreate(nCreate);
+ rTOXBase.SetOLEOptions(nOLEOptions);
+ if (rTOXBase.GetTOXType()->GetType() == TOX_INDEX)
+ {
+ rTOXBase.SetOptions(nTOIOptions);
+ }
+ if (bForm)
+ {
+ rTOXBase.SetTOXForm(aForm);
+ }
+}
+
+uno::Any SAL_CALL
+SwXDocumentIndex::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast< cppu::OWeakObject * >(this));
+ }
+ // TODO: is this the best approach to tell API clients about the change?
+ if (pEntry->nWID == RES_BACKGROUND && pEntry->nMemberId == MID_GRAPHIC_URL)
+ {
+ throw uno::RuntimeException("Getting GraphicURL property is not supported");
+ }
+
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ SwTOXBase* pTOXBase = nullptr;
+ if (pSectionFormat)
+ {
+ pTOXBase = static_cast<SwTOXBaseSection*>(pSectionFormat->GetSection());
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ pTOXBase = &m_pImpl->m_pProps->GetTOXBase();
+ }
+ if(pTOXBase)
+ {
+ const SwTOXElement nCreate = pTOXBase->GetCreateType();
+ const SwTOOElements nOLEOptions = pTOXBase->GetOLEOptions();
+ const SwTOIOptions nTOIOptions =
+ (pTOXBase->GetTOXType()->GetType() == TOX_INDEX)
+ ? pTOXBase->GetOptions()
+ : SwTOIOptions::NONE;
+ const SwForm& rForm = pTOXBase->GetTOXForm();
+ switch(pEntry->nWID)
+ {
+ case WID_IDX_CONTENT_SECTION:
+ case WID_IDX_HEADER_SECTION :
+ if(WID_IDX_CONTENT_SECTION == pEntry->nWID)
+ {
+ const uno::Reference <text::XTextSection> xContentSect =
+ SwXTextSection::CreateXTextSection( pSectionFormat );
+ aRet <<= xContentSect;
+ }
+ else if (pSectionFormat)
+ {
+ SwSections aSectArr;
+ pSectionFormat->GetChildSections(aSectArr,
+ SectionSort::Not, false);
+ for(SwSection* pSect : aSectArr)
+ {
+ if(pSect->GetType() == SectionType::ToxHeader)
+ {
+ const uno::Reference <text::XTextSection> xHeader =
+ SwXTextSection::CreateXTextSection(
+ pSect->GetFormat() );
+ aRet <<= xHeader;
+ break;
+ }
+ }
+ }
+ break;
+ case WID_IDX_TITLE :
+ {
+ aRet <<= pTOXBase->GetTitle();
+ break;
+ }
+ case WID_IDX_NAME:
+ aRet <<= pTOXBase->GetTOXName();
+ break;
+ case WID_USER_IDX_NAME:
+ {
+ OUString sTmp((!m_pImpl->m_bIsDescriptor)
+ ? pTOXBase->GetTOXType()->GetTypeName()
+ : m_pImpl->m_pProps->GetTypeName());
+ //I18N
+ lcl_ConvertTOUNameToProgrammaticName(sTmp);
+ aRet <<= sTmp;
+ }
+ break;
+ case WID_IDX_LOCALE:
+ aRet <<= LanguageTag(pTOXBase->GetLanguage()).getLocale();
+ break;
+ case WID_IDX_SORT_ALGORITHM:
+ aRet <<= pTOXBase->GetSortAlgorithm();
+ break;
+ case WID_LEVEL :
+ aRet <<= static_cast<sal_Int16>(pTOXBase->GetLevel());
+ break;
+ case WID_TOC_BOOKMARK :
+ aRet <<= pTOXBase->GetBookmarkName();
+ break;
+ case WID_CREATE_FROM_MARKS:
+ lcl_BitMaskToAny(aRet, nCreate, SwTOXElement::Mark);
+ break;
+ case WID_CREATE_FROM_OUTLINE:
+ lcl_BitMaskToAny(aRet, nCreate,
+ SwTOXElement::OutlineLevel);
+ break;
+ case WID_CREATE_FROM_CHAPTER:
+ {
+ const bool bRet = pTOXBase->IsFromChapter();
+ aRet <<= bRet;
+ }
+ break;
+ case WID_CREATE_FROM_LABELS:
+ {
+ const bool bRet = ! pTOXBase->IsFromObjectNames();
+ aRet <<= bRet;
+ }
+ break;
+ case WID_PROTECTED:
+ {
+ const bool bRet = pTOXBase->IsProtected();
+ aRet <<= bRet;
+ }
+ break;
+ case WID_USE_ALPHABETICAL_SEPARATORS:
+ lcl_BitMaskToAny(aRet, nTOIOptions,
+ SwTOIOptions::AlphaDelimiter);
+ break;
+ case WID_USE_KEY_AS_ENTRY:
+ lcl_BitMaskToAny(aRet, nTOIOptions,
+ SwTOIOptions::KeyAsEntry);
+ break;
+ case WID_USE_COMBINED_ENTRIES:
+ lcl_BitMaskToAny(aRet, nTOIOptions,
+ SwTOIOptions::SameEntry);
+ break;
+ case WID_IS_CASE_SENSITIVE:
+ lcl_BitMaskToAny(aRet, nTOIOptions,
+ SwTOIOptions::CaseSensitive);
+ break;
+ case WID_USE_P_P:
+ lcl_BitMaskToAny(aRet, nTOIOptions, SwTOIOptions::FF);
+ break;
+ case WID_USE_DASH:
+ lcl_BitMaskToAny(aRet, nTOIOptions, SwTOIOptions::Dash);
+ break;
+ case WID_USE_UPPER_CASE:
+ lcl_BitMaskToAny(aRet, nTOIOptions,
+ SwTOIOptions::InitialCaps);
+ break;
+ case WID_IS_COMMA_SEPARATED:
+ {
+ const bool bRet = rForm.IsCommaSeparated();
+ aRet <<= bRet;
+ }
+ break;
+ case WID_LABEL_CATEGORY:
+ {
+ // convert internal UI name to
+ // file-format/API/external programmatic english name
+ // before usage
+ aRet <<= SwStyleNameMapper::GetSpecialExtraProgName(
+ pTOXBase->GetSequenceName() );
+ }
+ break;
+ case WID_LABEL_DISPLAY_TYPE:
+ {
+ sal_Int16 nSet = text::ReferenceFieldPart::TEXT;
+ switch (pTOXBase->GetCaptionDisplay())
+ {
+ case CAPTION_COMPLETE:
+ nSet = text::ReferenceFieldPart::TEXT;
+ break;
+ case CAPTION_NUMBER:
+ nSet = text::ReferenceFieldPart::CATEGORY_AND_NUMBER;
+ break;
+ case CAPTION_TEXT:
+ nSet = text::ReferenceFieldPart::ONLY_CAPTION;
+ break;
+ }
+ aRet <<= nSet;
+ }
+ break;
+ case WID_USE_LEVEL_FROM_SOURCE:
+ {
+ const bool bRet = pTOXBase->IsLevelFromChapter();
+ aRet <<= bRet;
+ }
+ break;
+ case WID_LEVEL_FORMAT:
+ {
+ uno::Reference< container::XIndexReplace > xTokenAccess(
+ m_pImpl->m_wTokenAccess);
+ if (!xTokenAccess.is())
+ {
+ xTokenAccess = new TokenAccess_Impl(*this);
+ m_pImpl->m_wTokenAccess = xTokenAccess;
+ }
+ aRet <<= xTokenAccess;
+ }
+ break;
+ case WID_LEVEL_PARAGRAPH_STYLES:
+ {
+ uno::Reference< container::XIndexReplace > xStyleAccess(
+ m_pImpl->m_wStyleAccess);
+ if (!xStyleAccess.is())
+ {
+ xStyleAccess = new StyleAccess_Impl(*this);
+ m_pImpl->m_wStyleAccess = xStyleAccess;
+ }
+ aRet <<= xStyleAccess;
+ }
+ break;
+ case WID_MAIN_ENTRY_CHARACTER_STYLE_NAME:
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName(
+ pTOXBase->GetMainEntryCharStyle(),
+ aString,
+ SwGetPoolIdFromName::ChrFmt);
+ aRet <<= aString;
+ }
+ break;
+ case WID_CREATE_FROM_TABLES:
+ lcl_BitMaskToAny(aRet, nCreate, SwTOXElement::Table);
+ break;
+ case WID_CREATE_FROM_TEXT_FRAMES:
+ lcl_BitMaskToAny(aRet, nCreate, SwTOXElement::Frame);
+ break;
+ case WID_CREATE_FROM_GRAPHIC_OBJECTS:
+ lcl_BitMaskToAny(aRet, nCreate, SwTOXElement::Graphic);
+ break;
+ case WID_CREATE_FROM_EMBEDDED_OBJECTS:
+ lcl_BitMaskToAny(aRet, nCreate, SwTOXElement::Ole);
+ break;
+ case WID_CREATE_FROM_STAR_MATH:
+ lcl_BitMaskToAny(aRet, nOLEOptions, SwTOOElements::Math);
+ break;
+ case WID_CREATE_FROM_STAR_CHART:
+ lcl_BitMaskToAny(aRet, nOLEOptions, SwTOOElements::Chart);
+ break;
+ case WID_CREATE_FROM_STAR_CALC:
+ lcl_BitMaskToAny(aRet, nOLEOptions, SwTOOElements::Calc);
+ break;
+ case WID_CREATE_FROM_STAR_DRAW:
+ lcl_BitMaskToAny(aRet, nOLEOptions,
+ SwTOOElements::DrawImpress);
+ break;
+ case WID_CREATE_FROM_OTHER_EMBEDDED_OBJECTS:
+ lcl_BitMaskToAny(aRet, nOLEOptions, SwTOOElements::Other);
+ break;
+ case WID_CREATE_FROM_PARAGRAPH_STYLES:
+ lcl_BitMaskToAny(aRet, nCreate, SwTOXElement::Template);
+ break;
+ case WID_PARA_HEAD:
+ {
+ //Header is at position 0
+ OUString aString;
+ SwStyleNameMapper::FillProgName(rForm.GetTemplate( 0 ), aString,
+ SwGetPoolIdFromName::TxtColl );
+ aRet <<= aString;
+ }
+ break;
+ case WID_PARA_SEP:
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName(
+ rForm.GetTemplate( 1 ),
+ aString,
+ SwGetPoolIdFromName::TxtColl);
+ aRet <<= aString;
+ }
+ break;
+ case WID_PARA_LEV1:
+ case WID_PARA_LEV2:
+ case WID_PARA_LEV3:
+ case WID_PARA_LEV4:
+ case WID_PARA_LEV5:
+ case WID_PARA_LEV6:
+ case WID_PARA_LEV7:
+ case WID_PARA_LEV8:
+ case WID_PARA_LEV9:
+ case WID_PARA_LEV10:
+ {
+ // in sdbcx::Index Label 1 begins at Pos 2 otherwise at Pos 1
+ const sal_uInt16 nLPos = pTOXBase->GetType() == TOX_INDEX ? 2 : 1;
+ OUString aString;
+ SwStyleNameMapper::FillProgName(
+ rForm.GetTemplate(nLPos + pEntry->nWID - WID_PARA_LEV1),
+ aString,
+ SwGetPoolIdFromName::TxtColl);
+ aRet <<= aString;
+ }
+ break;
+ case WID_IS_RELATIVE_TABSTOPS:
+ {
+ const bool bRet = rForm.IsRelTabPos();
+ aRet <<= bRet;
+ }
+ break;
+ case WID_INDEX_MARKS:
+ {
+ SwTOXMarks aMarks;
+ const SwTOXType* pType = pTOXBase->GetTOXType();
+ pType->CollectTextMarks(aMarks);
+ uno::Sequence< uno::Reference<text::XDocumentIndexMark> > aXMarks(aMarks.size());
+ uno::Reference<text::XDocumentIndexMark>* pxMarks = aXMarks.getArray();
+ for(size_t i = 0; i < aMarks.size(); ++i)
+ {
+ SwTOXMark* pMark = aMarks[i];
+ pxMarks[i] = SwXDocumentIndexMark::CreateXDocumentIndexMark(
+ *m_pImpl->m_pDoc, pMark);
+ }
+ aRet <<= aXMarks;
+ }
+ break;
+ default:
+ //this is for items only
+ if(WID_PRIMARY_KEY > pEntry->nWID)
+ {
+ const SwAttrSet& rSet =
+ SwDoc::GetTOXBaseAttrSet(*pTOXBase);
+ aRet = m_pImpl->m_rPropSet.getPropertyValue(
+ rPropertyName, rSet);
+ }
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL
+SwXDocumentIndex::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndex::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXDocumentIndex::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndex::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXDocumentIndex::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndex::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXDocumentIndex::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndex::removeVetoableChangeListener(): not implemented");
+}
+
+static void lcl_CalcLayout(SwDoc *pDoc)
+{
+ SwViewShell *pViewShell = nullptr;
+ SwEditShell* pEditShell = nullptr;
+ if( pDoc )
+ {
+ pViewShell = pDoc->getIDocumentLayoutAccess().GetCurrentViewShell();
+ pEditShell = pDoc->GetEditShell();
+ }
+
+ if (pEditShell)
+ {
+ pEditShell->CalcLayout();
+ }
+ else if (pViewShell)
+ {
+ pViewShell->CalcLayout();
+ }
+}
+
+// XRefreshable
+void SAL_CALL SwXDocumentIndex::refresh()
+{
+ {
+ SolarMutexGuard g;
+
+ SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
+ SwTOXBaseSection *const pTOXBase = pFormat ?
+ static_cast<SwTOXBaseSection*>(pFormat->GetSection()) : nullptr;
+ if (!pTOXBase)
+ {
+ throw uno::RuntimeException(
+ "SwXDocumentIndex::refresh: must be in attached state",
+ static_cast< ::cppu::OWeakObject*>(this));
+ }
+ pTOXBase->Update(nullptr, m_pImpl->m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+
+ // the insertion of TOC will affect the document layout
+ lcl_CalcLayout(m_pImpl->m_pDoc);
+
+ // page numbers
+ pTOXBase->UpdatePageNum();
+ }
+
+ ::comphelper::OInterfaceContainerHelper2 *const pContainer(
+ m_pImpl->m_Listeners.getContainer(
+ cppu::UnoType<util::XRefreshListener>::get()));
+ if (pContainer)
+ {
+ lang::EventObject const event(static_cast< ::cppu::OWeakObject*>(this));
+ pContainer->notifyEach(& util::XRefreshListener::refreshed, event);
+ }
+}
+
+void SAL_CALL SwXDocumentIndex::addRefreshListener(
+ const uno::Reference<util::XRefreshListener>& xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ m_pImpl->m_Listeners.addInterface(
+ cppu::UnoType<util::XRefreshListener>::get(), xListener);
+}
+
+void SAL_CALL SwXDocumentIndex::removeRefreshListener(
+ const uno::Reference<util::XRefreshListener>& xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ m_pImpl->m_Listeners.removeInterface(
+ cppu::UnoType<util::XRefreshListener>::get(), xListener);
+}
+
+void SAL_CALL
+SwXDocumentIndex::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ SwXTextRange *const pRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ SwDoc *const pDoc =
+ pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
+ if (!pDoc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwUnoInternalPaM aPam(*pDoc);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+
+ const SwTOXBase* pOld = SwDoc::GetCurTOX( *aPam.Start() );
+ if (pOld)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ UnoActionContext aAction(pDoc);
+
+ SwTOXBase & rTOXBase = m_pImpl->m_pProps->GetTOXBase();
+ SwTOXType const*const pTOXType = rTOXBase.GetTOXType();
+ if ((TOX_USER == pTOXType->GetType()) &&
+ m_pImpl->m_pProps->GetTypeName() != pTOXType->GetTypeName())
+ {
+ lcl_ReAssignTOXType(*pDoc, rTOXBase, m_pImpl->m_pProps->GetTypeName());
+ }
+ //TODO: apply Section attributes (columns and background)
+ SwTOXBaseSection *const pTOX =
+ pDoc->InsertTableOf( aPam, rTOXBase, nullptr, false,
+ m_pImpl->m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+
+ pDoc->SetTOXBaseName(*pTOX, m_pImpl->m_pProps->GetTOXBase().GetTOXName());
+
+ // update page numbers
+ m_pImpl->SetSectionFormat(*pTOX->GetFormat());
+ pTOX->GetFormat()->SetXObject(static_cast< ::cppu::OWeakObject*>(this));
+ pTOX->UpdatePageNum();
+
+ m_pImpl->m_pProps.reset();
+ m_pImpl->m_pDoc = pDoc;
+ m_pImpl->m_bIsDescriptor = false;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXDocumentIndex::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ if (!pSectionFormat)
+ {
+ throw uno::RuntimeException();
+ }
+
+ uno::Reference< text::XTextRange > xRet;
+ SwNodeIndex const*const pIdx( pSectionFormat->GetContent().GetContentIdx() );
+ if (pIdx && pIdx->GetNode().GetNodes().IsDocNodes())
+ {
+ SwPaM aPaM(*pIdx);
+ aPaM.Move( fnMoveForward, GoInContent );
+ aPaM.SetMark();
+ aPaM.GetPoint()->nNode = *pIdx->GetNode().EndOfSectionNode();
+ aPaM.Move( fnMoveBackward, GoInContent );
+ xRet = SwXTextRange::CreateXTextRange(*pSectionFormat->GetDoc(),
+ *aPaM.GetMark(), aPaM.GetPoint());
+ }
+ return xRet;
+}
+
+void SAL_CALL SwXDocumentIndex::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ if (pSectionFormat)
+ {
+ pSectionFormat->GetDoc()->DeleteTOX(
+ *static_cast<SwTOXBaseSection*>(pSectionFormat->GetSection()),
+ true);
+ }
+}
+
+void SAL_CALL
+SwXDocumentIndex::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ m_pImpl->m_Listeners.addInterface(
+ cppu::UnoType<lang::XEventListener>::get(), xListener);
+}
+
+void SAL_CALL
+SwXDocumentIndex::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ m_pImpl->m_Listeners.removeInterface(
+ cppu::UnoType<lang::XEventListener>::get(), xListener);
+}
+
+OUString SAL_CALL SwXDocumentIndex::getName()
+{
+ SolarMutexGuard g;
+
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ return m_pImpl->m_pProps->GetTOXBase().GetTOXName();
+ }
+
+ if(!pSectionFormat)
+ {
+ throw uno::RuntimeException();
+ }
+
+ return pSectionFormat->GetSection()->GetSectionName();
+}
+
+void SAL_CALL
+SwXDocumentIndex::setName(const OUString& rName)
+{
+ SolarMutexGuard g;
+
+ if (rName.isEmpty())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->GetTOXBase().SetTOXName(rName);
+ }
+ else if (pSectionFormat)
+ {
+ const bool bSuccess = pSectionFormat->GetDoc()->SetTOXBaseName(
+ *static_cast<SwTOXBaseSection*>(pSectionFormat->GetSection()), rName);
+ if (!bSuccess)
+ {
+ throw uno::RuntimeException();
+ }
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+}
+
+// MetadatableMixin
+::sfx2::Metadatable* SwXDocumentIndex::GetCoreObject()
+{
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ return pSectionFormat;
+}
+
+uno::Reference<frame::XModel> SwXDocumentIndex::GetModel()
+{
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ if (pSectionFormat)
+ {
+ SwDocShell const*const pShell( pSectionFormat->GetDoc()->GetDocShell() );
+ return pShell ? pShell->GetModel() : nullptr;
+ }
+ return nullptr;
+}
+
+static sal_uInt16
+lcl_TypeToPropertyMap_Mark(const TOXTypes eType)
+{
+ switch (eType)
+ {
+ case TOX_INDEX: return PROPERTY_MAP_INDEX_MARK;
+ case TOX_CONTENT: return PROPERTY_MAP_CNTIDX_MARK;
+ case TOX_CITATION : return PROPERTY_MAP_FLDTYP_BIBLIOGRAPHY;
+ //case TOX_USER:
+ default:
+ return PROPERTY_MAP_USER_MARK;
+ }
+}
+
+class SwXDocumentIndexMark::Impl final: public SvtListener
+{
+private:
+ SwXDocumentIndexMark & m_rThis;
+ bool m_bInReplaceMark;
+
+public:
+
+ uno::WeakReference<uno::XInterface> m_wThis;
+ SfxItemPropertySet const& m_rPropSet;
+ const TOXTypes m_eTOXType;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ bool m_bIsDescriptor;
+ const SwTOXType* m_pTOXType;
+ const SwTOXMark* m_pTOXMark;
+ SwDoc* m_pDoc;
+
+ bool m_bMainEntry;
+ sal_uInt16 m_nLevel;
+ OUString m_aBookmarkName;
+ OUString m_sAltText;
+ OUString m_sPrimaryKey;
+ OUString m_sSecondaryKey;
+ OUString m_sTextReading;
+ OUString m_sPrimaryKeyReading;
+ OUString m_sSecondaryKeyReading;
+ OUString m_sUserIndexName;
+
+ Impl(SwXDocumentIndexMark& rThis,
+ SwDoc* const pDoc,
+ const enum TOXTypes eType,
+ const SwTOXType* pType,
+ SwTOXMark const* pMark)
+ : m_rThis(rThis)
+ , m_bInReplaceMark(false)
+ , m_rPropSet(
+ *aSwMapProvider.GetPropertySet(lcl_TypeToPropertyMap_Mark(eType)))
+ , m_eTOXType(eType)
+ , m_bIsDescriptor(nullptr == pMark)
+ , m_pTOXType(pType)
+ , m_pTOXMark(pMark)
+ , m_pDoc(pDoc)
+ , m_bMainEntry(false)
+ , m_nLevel(0)
+ {
+ auto pMarkNonConst = const_cast<SwTOXMark*>(m_pTOXMark);
+ auto pTypeNonConst = const_cast<SwTOXType*>(m_pTOXType);
+
+ if(pMarkNonConst)
+ StartListening(pMarkNonConst->GetNotifier());
+ if(pTypeNonConst)
+ StartListening(pTypeNonConst->GetNotifier());
+ }
+
+ SwTOXType* GetTOXType() const {
+ return const_cast<SwTOXType*>(m_pTOXType);
+ }
+
+ void DeleteTOXMark()
+ {
+ m_pDoc->DeleteTOXMark(m_pTOXMark);
+ Invalidate();
+ }
+
+ void InsertTOXMark(const SwTOXType & rTOXType, SwTOXMark & rMark, SwPaM & rPam,
+ SwXTextCursor const*const pTextCursor);
+
+ void ReplaceTOXMark(const SwTOXType & rTOXType, SwTOXMark & rMark, SwPaM & rPam)
+ {
+ m_bInReplaceMark = true;
+ DeleteTOXMark();
+ m_bInReplaceMark = false;
+ try {
+ InsertTOXMark(rTOXType, rMark, rPam, nullptr);
+ } catch (...) {
+ OSL_FAIL("ReplaceTOXMark() failed!");
+ lang::EventObject const ev(
+ static_cast< ::cppu::OWeakObject&>(m_rThis));
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ throw;
+ }
+ }
+
+ void Invalidate();
+ virtual void Notify(const SfxHint&) override;
+};
+
+void SwXDocumentIndexMark::Impl::Invalidate()
+{
+ if (!m_bInReplaceMark) // #i109983# only dispose on delete, not on replace!
+ {
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ // fdo#72695: if UNO object is already dead, don't revive it with event
+ if (xThis.is())
+ {
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ }
+ }
+ EndListeningAll();
+ m_pDoc = nullptr;
+ m_pTOXMark = nullptr;
+ m_pTOXType = nullptr;
+}
+
+void SwXDocumentIndexMark::Impl::Notify(const SfxHint& rHint)
+{
+ if(auto pModifyChangedHint = dynamic_cast<const sw::ModifyChangedHint*>(&rHint))
+ {
+ if(auto pNewType = dynamic_cast<const SwTOXType*>(pModifyChangedHint->m_pNew))
+ m_pTOXType = pNewType;
+
+ else
+ Invalidate();
+ }
+}
+
+SwXDocumentIndexMark::SwXDocumentIndexMark(const TOXTypes eToxType)
+ : m_pImpl( new SwXDocumentIndexMark::Impl(*this, nullptr, eToxType, nullptr, nullptr) )
+{
+}
+
+SwXDocumentIndexMark::SwXDocumentIndexMark(SwDoc & rDoc,
+ const SwTOXType & rType, const SwTOXMark & rMark)
+ : m_pImpl( new SwXDocumentIndexMark::Impl(*this, &rDoc, rType.GetType(),
+ &rType, &rMark) )
+{
+}
+
+SwXDocumentIndexMark::~SwXDocumentIndexMark()
+{
+}
+
+uno::Reference<text::XDocumentIndexMark>
+SwXDocumentIndexMark::CreateXDocumentIndexMark(
+ SwDoc & rDoc, SwTOXMark *const pMark, TOXTypes const eType)
+{
+ // re-use existing SwXDocumentIndexMark
+ // NB: xmloff depends on this caching to generate ID from the address!
+ // #i105557#: do not iterate over the registered clients: race condition
+ uno::Reference<text::XDocumentIndexMark> xTOXMark;
+ if (pMark)
+ {
+ xTOXMark = pMark->GetXTOXMark();
+ }
+ if (!xTOXMark.is())
+ {
+ SwXDocumentIndexMark *const pNew(pMark
+ ? new SwXDocumentIndexMark(rDoc,
+ *const_cast<SwTOXType*>(pMark->GetTOXType()), *pMark)
+ : new SwXDocumentIndexMark(eType));
+ xTOXMark.set(pNew);
+ if (pMark)
+ {
+ pMark->SetXTOXMark(xTOXMark);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pNew->m_pImpl->m_wThis = xTOXMark;
+ }
+ return xTOXMark;
+}
+
+namespace
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwXDocumentIndexMark::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXDocumentIndexMarkUnoTunnelId;
+ return theSwXDocumentIndexMarkUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXDocumentIndexMark::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXDocumentIndexMark>(rId, this);
+}
+
+OUString SAL_CALL
+SwXDocumentIndexMark::getImplementationName()
+{
+ return "SwXDocumentIndexMark";
+}
+
+sal_Bool SAL_CALL SwXDocumentIndexMark::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXDocumentIndexMark::getSupportedServiceNames()
+{
+ SolarMutexGuard g;
+
+ const sal_Int32 nCnt = (m_pImpl->m_eTOXType == TOX_INDEX) ? 4 : 3;
+ uno::Sequence< OUString > aRet(nCnt);
+ OUString* pArray = aRet.getArray();
+ pArray[0] = "com.sun.star.text.BaseIndexMark";
+ pArray[1] = "com.sun.star.text.TextContent";
+ switch (m_pImpl->m_eTOXType)
+ {
+ case TOX_USER:
+ pArray[2] = "com.sun.star.text.UserIndexMark";
+ break;
+ case TOX_CONTENT:
+ pArray[2] = "com.sun.star.text.ContentIndexMark";
+ break;
+ case TOX_INDEX:
+ pArray[2] = "com.sun.star.text.DocumentIndexMark";
+ pArray[3] = "com.sun.star.text.DocumentIndexMarkAsian";
+ break;
+
+ default:
+ ;
+ }
+ return aRet;
+}
+
+OUString SAL_CALL
+SwXDocumentIndexMark::getMarkEntry()
+{
+ SolarMutexGuard aGuard;
+
+ SwTOXType *const pType = m_pImpl->GetTOXType();
+ if (pType && m_pImpl->m_pTOXMark)
+ {
+ return m_pImpl->m_pTOXMark->GetAlternativeText();
+ }
+
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ return m_pImpl->m_sAltText;
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::setMarkEntry(const OUString& rIndexEntry)
+{
+ SolarMutexGuard aGuard;
+
+ SwTOXType *const pType = m_pImpl->GetTOXType();
+ if (pType && m_pImpl->m_pTOXMark)
+ {
+ SwTOXMark aMark(*m_pImpl->m_pTOXMark);
+ aMark.SetAlternativeText(rIndexEntry);
+ SwTextTOXMark const*const pTextMark =
+ m_pImpl->m_pTOXMark->GetTextTOXMark();
+ SwPaM aPam(pTextMark->GetTextNode(), pTextMark->GetStart());
+ aPam.SetMark();
+ if(pTextMark->End())
+ {
+ aPam.GetPoint()->nContent = *pTextMark->End();
+ }
+ else
+ ++aPam.GetPoint()->nContent;
+
+ m_pImpl->ReplaceTOXMark(*pType, aMark, aPam);
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_sAltText = rIndexEntry;
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::attach(
+ const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ const uno::Reference<XUnoTunnel> xRangeTunnel(xTextRange, uno::UNO_QUERY);
+ SwXTextRange *const pRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+ SwDoc *const pDoc =
+ pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
+ if (!pDoc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ const SwTOXType* pTOXType = nullptr;
+ switch (m_pImpl->m_eTOXType)
+ {
+ case TOX_INDEX:
+ case TOX_CONTENT:
+ case TOX_CITATION:
+ pTOXType = pDoc->GetTOXType( m_pImpl->m_eTOXType, 0 );
+ break;
+ case TOX_USER:
+ {
+ if (m_pImpl->m_sUserIndexName.isEmpty())
+ {
+ pTOXType = pDoc->GetTOXType( m_pImpl->m_eTOXType, 0 );
+ }
+ else
+ {
+ const sal_uInt16 nCount =
+ pDoc->GetTOXTypeCount(m_pImpl->m_eTOXType);
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ SwTOXType const*const pTemp =
+ pDoc->GetTOXType( m_pImpl->m_eTOXType, i );
+ if (m_pImpl->m_sUserIndexName == pTemp->GetTypeName())
+ {
+ pTOXType = pTemp;
+ break;
+ }
+ }
+ if (!pTOXType)
+ {
+ SwTOXType aUserType(*pDoc, TOX_USER, m_pImpl->m_sUserIndexName);
+ pTOXType = pDoc->InsertTOXType(aUserType);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (!pTOXType)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwUnoInternalPaM aPam(*pDoc);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+ SwTOXMark aMark (pTOXType);
+ if (!m_pImpl->m_sAltText.isEmpty())
+ {
+ aMark.SetAlternativeText(m_pImpl->m_sAltText);
+ }
+ switch (m_pImpl->m_eTOXType)
+ {
+ case TOX_INDEX:
+ if (!m_pImpl->m_sPrimaryKey.isEmpty())
+ {
+ aMark.SetPrimaryKey(m_pImpl->m_sPrimaryKey);
+ }
+ if (!m_pImpl->m_sSecondaryKey.isEmpty())
+ {
+ aMark.SetSecondaryKey(m_pImpl->m_sSecondaryKey);
+ }
+ if (!m_pImpl->m_sTextReading.isEmpty())
+ {
+ aMark.SetTextReading(m_pImpl->m_sTextReading);
+ }
+ if (!m_pImpl->m_sPrimaryKeyReading.isEmpty())
+ {
+ aMark.SetPrimaryKeyReading(m_pImpl->m_sPrimaryKeyReading);
+ }
+ if (!m_pImpl->m_sSecondaryKeyReading.isEmpty())
+ {
+ aMark.SetSecondaryKeyReading(m_pImpl->m_sSecondaryKeyReading);
+ }
+ aMark.SetMainEntry(m_pImpl->m_bMainEntry);
+ break;
+ case TOX_CITATION:
+ aMark.SetMainEntry(m_pImpl->m_bMainEntry);
+ break;
+ case TOX_USER:
+ case TOX_CONTENT:
+ if (USHRT_MAX != m_pImpl->m_nLevel)
+ {
+ aMark.SetLevel(m_pImpl->m_nLevel+1);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ m_pImpl->InsertTOXMark(*const_cast<SwTOXType *>(pTOXType), aMark, aPam,
+ dynamic_cast<SwXTextCursor const*>(pCursor));
+
+ m_pImpl->m_bIsDescriptor = false;
+}
+
+namespace {
+
+template<typename T> struct NotContainedIn
+{
+ std::vector<T> const& m_rVector;
+ explicit NotContainedIn(std::vector<T> const& rVector)
+ : m_rVector(rVector) { }
+ bool operator() (T const& rT) {
+ return std::find(m_rVector.begin(), m_rVector.end(), rT)
+ == m_rVector.end();
+ }
+};
+
+}
+
+void SwXDocumentIndexMark::Impl::InsertTOXMark(
+ const SwTOXType & rTOXType, SwTOXMark & rMark, SwPaM & rPam,
+ SwXTextCursor const*const pTextCursor)
+{
+ SwDoc& rDoc(rPam.GetDoc());
+ UnoActionContext aAction(&rDoc);
+ bool bMark = *rPam.GetPoint() != *rPam.GetMark();
+ // n.b.: toxmarks must have either alternative text or an extent
+ if (bMark && !rMark.GetAlternativeText().isEmpty())
+ {
+ rPam.Normalize();
+ rPam.DeleteMark();
+ bMark = false;
+ }
+ // Marks without alternative text and without selected text cannot be inserted,
+ // thus use a space - is this really the ideal solution?
+ if (!bMark && rMark.GetAlternativeText().isEmpty())
+ {
+ rMark.SetAlternativeText( " " );
+ }
+
+ const bool bForceExpandHints( !bMark && pTextCursor && pTextCursor->IsAtEndOfMeta() );
+ const SetAttrMode nInsertFlags = bForceExpandHints
+ ? ( SetAttrMode::FORCEHINTEXPAND
+ | SetAttrMode::DONTEXPAND)
+ : SetAttrMode::DONTEXPAND;
+
+ // rMark gets copied into the document pool;
+ // pNewTextAttr comes back with the real format
+ SwTextAttr *pNewTextAttr = nullptr;
+ rDoc.getIDocumentContentOperations().InsertPoolItem(rPam, rMark, nInsertFlags,
+ /*pLayout*/nullptr, &pNewTextAttr);
+ if (bMark && *rPam.GetPoint() > *rPam.GetMark())
+ {
+ rPam.Exchange();
+ }
+
+ if (!pNewTextAttr)
+ {
+ throw uno::RuntimeException(
+ "SwXDocumentIndexMark::InsertTOXMark(): cannot insert attribute",
+ nullptr);
+ }
+
+ m_pDoc = &rDoc;
+ m_pTOXMark = &pNewTextAttr->GetTOXMark();
+ m_pTOXType = &rTOXType;
+ EndListeningAll();
+ StartListening(const_cast<SwTOXMark*>(m_pTOXMark)->GetNotifier());
+ StartListening(const_cast<SwTOXType*>(m_pTOXType)->GetNotifier());
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXDocumentIndexMark::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ SwTOXType *const pType = m_pImpl->GetTOXType();
+ if (!pType || !m_pImpl->m_pTOXMark)
+ {
+ throw uno::RuntimeException();
+ }
+ if (!m_pImpl->m_pTOXMark->GetTextTOXMark())
+ {
+ throw uno::RuntimeException();
+ }
+ const SwTextTOXMark* pTextMark = m_pImpl->m_pTOXMark->GetTextTOXMark();
+ SwPaM aPam(pTextMark->GetTextNode(), pTextMark->GetStart());
+ aPam.SetMark();
+ if(pTextMark->End())
+ {
+ aPam.GetPoint()->nContent = *pTextMark->End();
+ }
+ else
+ {
+ ++aPam.GetPoint()->nContent;
+ }
+ const uno::Reference< frame::XModel > xModel =
+ m_pImpl->m_pDoc->GetDocShell()->GetBaseModel();
+ const uno::Reference< text::XTextDocument > xTDoc(xModel, uno::UNO_QUERY);
+ const uno::Reference< text::XTextRange > xRet =
+ new SwXTextRange(aPam, xTDoc->getText());
+
+ return xRet;
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ SwTOXType *const pType = m_pImpl->GetTOXType();
+ if (pType && m_pImpl->m_pTOXMark)
+ {
+ m_pImpl->DeleteTOXMark(); // call Invalidate() via modify!
+ }
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXDocumentIndexMark::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xInfos[3];
+ int nPos = 0;
+ switch (m_pImpl->m_eTOXType)
+ {
+ case TOX_INDEX: nPos = 0; break;
+ case TOX_CONTENT: nPos = 1; break;
+ case TOX_USER: nPos = 2; break;
+ default:
+ ;
+ }
+ if(!xInfos[nPos].is())
+ {
+ const uno::Reference< beans::XPropertySetInfo > xInfo =
+ m_pImpl->m_rPropSet.getPropertySetInfo();
+ // extend PropertySetInfo!
+ const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
+ xInfos[nPos] = new SfxExtItemPropertySetInfo(
+ aSwMapProvider.GetPropertyMapEntries(
+ PROPERTY_MAP_PARAGRAPH_EXTENSIONS),
+ aPropSeq );
+ }
+ return xInfos[nPos];
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw beans::PropertyVetoException(
+ "Property is read-only: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ SwTOXType *const pType = m_pImpl->GetTOXType();
+ if (pType && m_pImpl->m_pTOXMark)
+ {
+ SwTOXMark aMark(*m_pImpl->m_pTOXMark);
+ switch(pEntry->nWID)
+ {
+ case WID_ALT_TEXT:
+ aMark.SetAlternativeText(lcl_AnyToType<OUString>(rValue));
+ break;
+ case WID_LEVEL:
+ aMark.SetLevel(std::min( static_cast<sal_Int8>( MAXLEVEL ),
+ static_cast<sal_Int8>(lcl_AnyToType<sal_Int16>(rValue)+1)));
+ break;
+ case WID_TOC_BOOKMARK :
+ aMark.SetBookmarkName(lcl_AnyToType<OUString>(rValue));
+ break;
+ case WID_PRIMARY_KEY :
+ aMark.SetPrimaryKey(lcl_AnyToType<OUString>(rValue));
+ break;
+ case WID_SECONDARY_KEY:
+ aMark.SetSecondaryKey(lcl_AnyToType<OUString>(rValue));
+ break;
+ case WID_MAIN_ENTRY:
+ aMark.SetMainEntry(lcl_AnyToType<bool>(rValue));
+ break;
+ case WID_TEXT_READING:
+ aMark.SetTextReading(lcl_AnyToType<OUString>(rValue));
+ break;
+ case WID_PRIMARY_KEY_READING:
+ aMark.SetPrimaryKeyReading(lcl_AnyToType<OUString>(rValue));
+ break;
+ case WID_SECONDARY_KEY_READING:
+ aMark.SetSecondaryKeyReading(lcl_AnyToType<OUString>(rValue));
+ break;
+ }
+ SwTextTOXMark const*const pTextMark =
+ m_pImpl->m_pTOXMark->GetTextTOXMark();
+ SwPaM aPam(pTextMark->GetTextNode(), pTextMark->GetStart());
+ aPam.SetMark();
+ if(pTextMark->End())
+ {
+ aPam.GetPoint()->nContent = *pTextMark->End();
+ }
+ else
+ {
+ ++aPam.GetPoint()->nContent;
+ }
+
+ m_pImpl->ReplaceTOXMark(*pType, aMark, aPam);
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ switch(pEntry->nWID)
+ {
+ case WID_ALT_TEXT:
+ m_pImpl->m_sAltText = lcl_AnyToType<OUString>(rValue);
+ break;
+ case WID_LEVEL:
+ {
+ const sal_Int16 nVal = lcl_AnyToType<sal_Int16>(rValue);
+ if(nVal < 0 || nVal >= MAXLEVEL)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ m_pImpl->m_nLevel = nVal;
+ }
+ break;
+ case WID_TOC_BOOKMARK :
+ {
+ m_pImpl->m_aBookmarkName = lcl_AnyToType<OUString>(rValue);
+ }
+ break;
+ case WID_PRIMARY_KEY:
+ m_pImpl->m_sPrimaryKey = lcl_AnyToType<OUString>(rValue);
+ break;
+ case WID_SECONDARY_KEY:
+ m_pImpl->m_sSecondaryKey = lcl_AnyToType<OUString>(rValue);
+ break;
+ case WID_TEXT_READING:
+ m_pImpl->m_sTextReading = lcl_AnyToType<OUString>(rValue);
+ break;
+ case WID_PRIMARY_KEY_READING:
+ m_pImpl->m_sPrimaryKeyReading = lcl_AnyToType<OUString>(rValue);
+ break;
+ case WID_SECONDARY_KEY_READING:
+ m_pImpl->m_sSecondaryKeyReading = lcl_AnyToType<OUString>(rValue);
+ break;
+ case WID_USER_IDX_NAME:
+ {
+ OUString sTmp(lcl_AnyToType<OUString>(rValue));
+ lcl_ConvertTOUNameToUserName(sTmp);
+ m_pImpl->m_sUserIndexName = sTmp;
+ }
+ break;
+ case WID_MAIN_ENTRY:
+ m_pImpl->m_bMainEntry = lcl_AnyToType<bool>(rValue);
+ break;
+ case PROPERTY_MAP_INDEX_OBJECTS:
+ // unsupported
+ break;
+ }
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+}
+
+uno::Any SAL_CALL
+SwXDocumentIndexMark::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+ if (::sw::GetDefaultTextContentValue(aRet, rPropertyName, pEntry->nWID))
+ {
+ return aRet;
+ }
+
+ SwTOXType *const pType = m_pImpl->GetTOXType();
+ if (pType && m_pImpl->m_pTOXMark)
+ {
+ switch(pEntry->nWID)
+ {
+ case WID_ALT_TEXT:
+ aRet <<= m_pImpl->m_pTOXMark->GetAlternativeText();
+ break;
+ case WID_LEVEL:
+ aRet <<= static_cast<sal_Int16>(
+ m_pImpl->m_pTOXMark->GetLevel() - 1);
+ break;
+ case WID_TOC_BOOKMARK :
+ aRet <<= m_pImpl->m_pTOXMark->GetBookmarkName();
+ break;
+ case WID_PRIMARY_KEY :
+ aRet <<= m_pImpl->m_pTOXMark->GetPrimaryKey();
+ break;
+ case WID_SECONDARY_KEY:
+ aRet <<= m_pImpl->m_pTOXMark->GetSecondaryKey();
+ break;
+ case WID_TEXT_READING:
+ aRet <<= m_pImpl->m_pTOXMark->GetTextReading();
+ break;
+ case WID_PRIMARY_KEY_READING:
+ aRet <<= m_pImpl->m_pTOXMark->GetPrimaryKeyReading();
+ break;
+ case WID_SECONDARY_KEY_READING:
+ aRet <<= m_pImpl->m_pTOXMark->GetSecondaryKeyReading();
+ break;
+ case WID_USER_IDX_NAME :
+ {
+ OUString sTmp(pType->GetTypeName());
+ lcl_ConvertTOUNameToProgrammaticName(sTmp);
+ aRet <<= sTmp;
+ }
+ break;
+ case WID_MAIN_ENTRY:
+ {
+ const bool bTemp = m_pImpl->m_pTOXMark->IsMainEntry();
+ aRet <<= bTemp;
+ }
+ break;
+ }
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ switch(pEntry->nWID)
+ {
+ case WID_ALT_TEXT:
+ aRet <<= m_pImpl->m_sAltText;
+ break;
+ case WID_LEVEL:
+ aRet <<= static_cast<sal_Int16>(m_pImpl->m_nLevel);
+ break;
+ case WID_TOC_BOOKMARK :
+ aRet <<= m_pImpl->m_aBookmarkName;
+ break;
+ case WID_PRIMARY_KEY:
+ aRet <<= m_pImpl->m_sPrimaryKey;
+ break;
+ case WID_SECONDARY_KEY:
+ aRet <<= m_pImpl->m_sSecondaryKey;
+ break;
+ case WID_TEXT_READING:
+ aRet <<= m_pImpl->m_sTextReading;
+ break;
+ case WID_PRIMARY_KEY_READING:
+ aRet <<= m_pImpl->m_sPrimaryKeyReading;
+ break;
+ case WID_SECONDARY_KEY_READING:
+ aRet <<= m_pImpl->m_sSecondaryKeyReading;
+ break;
+ case WID_USER_IDX_NAME :
+ aRet <<= m_pImpl->m_sUserIndexName;
+ break;
+ case WID_MAIN_ENTRY:
+ aRet <<= m_pImpl->m_bMainEntry;
+ break;
+ }
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+ return aRet;
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndexMark::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndexMark::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndexMark::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXDocumentIndexMark::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXDocumentIndexMark::removeVetoableChangeListener(): not implemented");
+}
+
+SwXDocumentIndexes::SwXDocumentIndexes(SwDoc *const _pDoc)
+ : SwUnoCollection(_pDoc)
+{
+}
+
+SwXDocumentIndexes::~SwXDocumentIndexes()
+{
+}
+
+OUString SAL_CALL
+SwXDocumentIndexes::getImplementationName()
+{
+ return "SwXDocumentIndexes";
+}
+
+sal_Bool SAL_CALL SwXDocumentIndexes::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXDocumentIndexes::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.DocumentIndexes" };
+}
+
+sal_Int32 SAL_CALL
+SwXDocumentIndexes::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ sal_uInt32 nRet = 0;
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ const SwSection* pSect = rFormats[ n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode() )
+ {
+ ++nRet;
+ }
+ }
+ return nRet;
+}
+
+uno::Any SAL_CALL
+SwXDocumentIndexes::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ sal_Int32 nIdx = 0;
+
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ SwSection* pSect = rFormats[ n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode() &&
+ nIdx++ == nIndex )
+ {
+ const uno::Reference< text::XDocumentIndex > xTmp =
+ SwXDocumentIndex::CreateXDocumentIndex(
+ *GetDoc(), static_cast<SwTOXBaseSection *>(pSect));
+ uno::Any aRet;
+ aRet <<= xTmp;
+ return aRet;
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Any SAL_CALL
+SwXDocumentIndexes::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ SwSection* pSect = rFormats[ n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode() &&
+ (static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()
+ == rName))
+ {
+ const uno::Reference< text::XDocumentIndex > xTmp =
+ SwXDocumentIndex::CreateXDocumentIndex(
+ *GetDoc(), static_cast<SwTOXBaseSection *>(pSect));
+ uno::Any aRet;
+ aRet <<= xTmp;
+ return aRet;
+ }
+ }
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXDocumentIndexes::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ sal_Int32 nCount = 0;
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ SwSection const*const pSect = rFormats[ n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode() )
+ {
+ ++nCount;
+ }
+ }
+
+ uno::Sequence< OUString > aRet(nCount);
+ OUString* pArray = aRet.getArray();
+ sal_Int32 nCnt = 0;
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ SwSection const*const pSect = rFormats[ n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode())
+ {
+ pArray[nCnt++] = static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName();
+ }
+ }
+ return aRet;
+}
+
+sal_Bool SAL_CALL
+SwXDocumentIndexes::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ const SwSectionFormats& rFormats = GetDoc()->GetSections();
+ for( size_t n = 0; n < rFormats.size(); ++n )
+ {
+ SwSection const*const pSect = rFormats[ n ]->GetSection();
+ if( SectionType::ToxContent == pSect->GetType() &&
+ pSect->GetFormat()->GetSectionNode())
+ {
+ if (static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()
+ == rName)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+uno::Type SAL_CALL
+SwXDocumentIndexes::getElementType()
+{
+ return cppu::UnoType<text::XDocumentIndex>::get();
+}
+
+sal_Bool SAL_CALL
+SwXDocumentIndexes::hasElements()
+{
+ return 0 != getCount();
+}
+
+SwXDocumentIndex::StyleAccess_Impl::StyleAccess_Impl(
+ SwXDocumentIndex& rParentIdx)
+ : m_xParent(&rParentIdx)
+{
+}
+
+SwXDocumentIndex::StyleAccess_Impl::~StyleAccess_Impl()
+{
+}
+
+OUString SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::getImplementationName()
+{
+ return "SwXDocumentIndex::StyleAccess_Impl";
+}
+
+sal_Bool SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.DocumentIndexParagraphStyles" };
+}
+
+void SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::replaceByIndex(
+ sal_Int32 nIndex, const uno::Any& rElement)
+{
+ SolarMutexGuard aGuard;
+
+ if(nIndex < 0 || nIndex >= MAXLEVEL)
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ SwTOXBase & rTOXBase( m_xParent->m_pImpl->GetTOXSectionOrThrow() );
+
+ uno::Sequence<OUString> aSeq;
+ if(!(rElement >>= aSeq))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ const sal_Int32 nStyles = aSeq.getLength();
+ const OUString* pStyles = aSeq.getConstArray();
+ OUStringBuffer sSetStyles;
+ OUString aString;
+ for(sal_Int32 i = 0; i < nStyles; i++)
+ {
+ if(i)
+ {
+ sSetStyles.append(TOX_STYLE_DELIMITER);
+ }
+ SwStyleNameMapper::FillUIName(pStyles[i], aString,
+ SwGetPoolIdFromName::TxtColl);
+ sSetStyles.append(aString);
+ }
+ rTOXBase.SetStyleNames(sSetStyles.makeStringAndClear(), o3tl::narrowing<sal_uInt16>(nIndex));
+}
+
+sal_Int32 SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::getCount()
+{
+ return MAXLEVEL;
+}
+
+uno::Any SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+
+ if(nIndex < 0 || nIndex >= MAXLEVEL)
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ SwTOXBase & rTOXBase( m_xParent->m_pImpl->GetTOXSectionOrThrow() );
+
+ const OUString& rStyles =
+ rTOXBase.GetStyleNames(o3tl::narrowing<sal_uInt16>(nIndex));
+ const sal_Int32 nStyles = comphelper::string::getTokenCount(rStyles, TOX_STYLE_DELIMITER);
+ uno::Sequence<OUString> aStyles(nStyles);
+ OUString* pStyles = aStyles.getArray();
+ OUString aString;
+ sal_Int32 nPos = 0;
+ for(sal_Int32 i = 0; i < nStyles; ++i)
+ {
+ SwStyleNameMapper::FillProgName(
+ rStyles.getToken(0, TOX_STYLE_DELIMITER, nPos),
+ aString,
+ SwGetPoolIdFromName::TxtColl);
+ pStyles[i] = aString;
+ }
+ uno::Any aRet(&aStyles, cppu::UnoType<uno::Sequence<OUString>>::get());
+ return aRet;
+}
+
+uno::Type SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::getElementType()
+{
+ return cppu::UnoType<uno::Sequence<OUString>>::get();
+}
+
+sal_Bool SAL_CALL
+SwXDocumentIndex::StyleAccess_Impl::hasElements()
+{
+ return true;
+}
+
+SwXDocumentIndex::TokenAccess_Impl::TokenAccess_Impl(
+ SwXDocumentIndex& rParentIdx)
+ : m_xParent(&rParentIdx)
+{
+}
+
+SwXDocumentIndex::TokenAccess_Impl::~TokenAccess_Impl()
+{
+}
+
+OUString SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::getImplementationName()
+{
+ return "SwXDocumentIndex::TokenAccess_Impl";
+}
+
+sal_Bool SAL_CALL SwXDocumentIndex::TokenAccess_Impl::supportsService(
+ const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.DocumentIndexLevelFormat" };
+}
+
+namespace {
+
+struct TokenType_ {
+ const char *pName;
+ enum FormTokenType eTokenType;
+};
+
+}
+
+const struct TokenType_ g_TokenTypes[] =
+{
+ { "TokenEntryNumber", TOKEN_ENTRY_NO },
+ { "TokenEntryText", TOKEN_ENTRY_TEXT },
+ { "TokenTabStop", TOKEN_TAB_STOP },
+ { "TokenText", TOKEN_TEXT },
+ { "TokenPageNumber", TOKEN_PAGE_NUMS },
+ { "TokenChapterInfo", TOKEN_CHAPTER_INFO },
+ { "TokenHyperlinkStart", TOKEN_LINK_START },
+ { "TokenHyperlinkEnd", TOKEN_LINK_END },
+ { "TokenBibliographyDataField", TOKEN_AUTHORITY },
+ { nullptr, static_cast<enum FormTokenType>(0) }
+};
+
+void SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::replaceByIndex(
+ sal_Int32 nIndex, const uno::Any& rElement)
+{
+ SolarMutexGuard aGuard;
+
+ SwTOXBase & rTOXBase( m_xParent->m_pImpl->GetTOXSectionOrThrow() );
+
+ if ((nIndex < 0) || (nIndex > rTOXBase.GetTOXForm().GetFormMax()))
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ uno::Sequence<beans::PropertyValues> aSeq;
+ if(!(rElement >>= aSeq))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ OUStringBuffer sPattern;
+ for(const beans::PropertyValues& rToken : std::as_const(aSeq))
+ {
+ const beans::PropertyValue* pProperties = rToken.getConstArray();
+ const sal_Int32 nProperties = rToken.getLength();
+ //create an invalid token
+ SwFormToken aToken(TOKEN_END);
+ for(sal_Int32 j = 0; j < nProperties; j++)
+ {
+ if ( pProperties[j].Name == "TokenType" )
+ {
+ const OUString sTokenType =
+ lcl_AnyToType<OUString>(pProperties[j].Value);
+ for (TokenType_ const* pTokenType = g_TokenTypes;
+ pTokenType->pName; ++pTokenType)
+ {
+ if (sTokenType.equalsAscii(pTokenType->pName))
+ {
+ aToken.eTokenType = pTokenType->eTokenType;
+ break;
+ }
+ }
+ }
+ else if ( pProperties[j].Name == "CharacterStyleName" )
+ {
+ OUString sCharStyleName;
+ SwStyleNameMapper::FillUIName(
+ lcl_AnyToType<OUString>(pProperties[j].Value),
+ sCharStyleName,
+ SwGetPoolIdFromName::ChrFmt);
+ aToken.sCharStyleName = sCharStyleName;
+ aToken.nPoolId = SwStyleNameMapper::GetPoolIdFromUIName (
+ sCharStyleName, SwGetPoolIdFromName::ChrFmt );
+ }
+ else if ( pProperties[j].Name == "TabStopRightAligned" )
+ {
+ const bool bRight = lcl_AnyToType<bool>(pProperties[j].Value);
+ aToken.eTabAlign = bRight ?
+ SvxTabAdjust::End : SvxTabAdjust::Left;
+ }
+ else if ( pProperties[j].Name == "TabStopPosition" )
+ {
+ sal_Int32 nPosition = 0;
+ if (!(pProperties[j].Value >>= nPosition))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ nPosition = o3tl::toTwips(nPosition, o3tl::Length::mm100);
+ if(nPosition < 0)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ aToken.nTabStopPosition = nPosition;
+ }
+ else if ( pProperties[j].Name == "TabStopFillCharacter" )
+ {
+ const OUString sFillChar =
+ lcl_AnyToType<OUString>(pProperties[j].Value);
+ if (sFillChar.getLength() > 1)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ aToken.cTabFillChar =
+ sFillChar.isEmpty() ? ' ' : sFillChar[0];
+ }
+ else if ( pProperties[j].Name == "Text" )
+ {
+ aToken.sText = lcl_AnyToType<OUString>(pProperties[j].Value);
+ }
+ else if ( pProperties[j].Name == "ChapterFormat" )
+ {
+ sal_Int16 nFormat = lcl_AnyToType<sal_Int16>(pProperties[j].Value);
+ switch(nFormat)
+ {
+ case text::ChapterFormat::NUMBER:
+ nFormat = CF_NUMBER;
+ break;
+ case text::ChapterFormat::NAME:
+ nFormat = CF_TITLE;
+ break;
+ case text::ChapterFormat::NAME_NUMBER:
+ nFormat = CF_NUM_TITLE;
+ break;
+ case text::ChapterFormat::NO_PREFIX_SUFFIX:
+ nFormat = CF_NUMBER_NOPREPST;
+ break;
+ case text::ChapterFormat::DIGIT:
+ nFormat = CF_NUM_NOPREPST_TITLE;
+ break;
+ default:
+ throw lang::IllegalArgumentException();
+ }
+ aToken.nChapterFormat = nFormat;
+ }
+// #i53420#
+ else if ( pProperties[j].Name == "ChapterLevel" )
+ {
+ const sal_Int16 nLevel = lcl_AnyToType<sal_Int16>(pProperties[j].Value);
+ if( nLevel < 1 || nLevel > MAXLEVEL )
+ {
+ throw lang::IllegalArgumentException();
+ }
+ aToken.nOutlineLevel = nLevel;
+ }
+ else if ( pProperties[j].Name == "BibliographyDataField" )
+ {
+ sal_Int16 nType = 0;
+ pProperties[j].Value >>= nType;
+ if(nType < 0 || nType > text::BibliographyDataField::LOCAL_URL)
+ {
+ lang::IllegalArgumentException aExcept;
+ aExcept.Message = "BibliographyDataField - wrong value";
+ aExcept.ArgumentPosition = static_cast< sal_Int16 >(j);
+ throw aExcept;
+ }
+ aToken.nAuthorityField = nType;
+ }
+ // #i21237#
+ else if ( pProperties[j].Name == "WithTab" )
+ {
+ aToken.bWithTab = lcl_AnyToType<bool>(pProperties[j].Value);
+ }
+
+ }
+ //exception if wrong TokenType
+ if(TOKEN_END <= aToken.eTokenType )
+ {
+ throw lang::IllegalArgumentException();
+ }
+ // set TokenType from TOKEN_ENTRY_TEXT to TOKEN_ENTRY if it is
+ // not a content index
+ if(TOKEN_ENTRY_TEXT == aToken.eTokenType &&
+ (TOX_CONTENT != rTOXBase.GetType()))
+ {
+ aToken.eTokenType = TOKEN_ENTRY;
+ }
+// #i53420#
+// check for chapter format allowed values if it was TOKEN_ENTRY_NO type
+// only allowed value are CF_NUMBER and CF_NUM_NOPREPST_TITLE
+// reading from file
+ if( TOKEN_ENTRY_NO == aToken.eTokenType )
+ {
+ switch(aToken.nChapterFormat)
+ {
+ case CF_NUMBER:
+ case CF_NUM_NOPREPST_TITLE:
+ break;
+ default:
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ if (rTOXBase.GetType() == TOX_CONTENT)
+ {
+ if (aToken.eTokenType == TOKEN_LINK_START && aToken.sCharStyleName.isEmpty())
+ {
+ aToken.sCharStyleName = SwResId(STR_POOLCHR_TOXJUMP);
+ }
+ }
+
+ sPattern.append(aToken.GetString());
+ }
+ SwForm aForm(rTOXBase.GetTOXForm());
+ aForm.SetPattern(o3tl::narrowing<sal_uInt16>(nIndex), sPattern.makeStringAndClear());
+ rTOXBase.SetTOXForm(aForm);
+}
+
+sal_Int32 SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ const sal_Int32 nRet = m_xParent->m_pImpl->GetFormMax();
+ return nRet;
+}
+
+uno::Any SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+
+ SwTOXBase & rTOXBase( m_xParent->m_pImpl->GetTOXSectionOrThrow() );
+
+ if ((nIndex < 0) || (nIndex > rTOXBase.GetTOXForm().GetFormMax()))
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+
+ // #i21237#
+ SwFormTokens aPattern = rTOXBase.GetTOXForm().
+ GetPattern(o3tl::narrowing<sal_uInt16>(nIndex));
+
+ sal_Int32 nTokenCount = 0;
+ uno::Sequence< beans::PropertyValues > aRetSeq;
+ OUString aProgCharStyle;
+ for(const SwFormToken& aToken : aPattern) // #i21237#
+ {
+ nTokenCount++;
+ aRetSeq.realloc(nTokenCount);
+ beans::PropertyValues* pTokenProps = aRetSeq.getArray();
+
+ uno::Sequence< beans::PropertyValue >& rCurTokenSeq =
+ pTokenProps[nTokenCount-1];
+ SwStyleNameMapper::FillProgName(
+ aToken.sCharStyleName,
+ aProgCharStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ switch(aToken.eTokenType)
+ {
+ case TOKEN_ENTRY_NO:
+ {
+// #i53420#
+// writing to file (from doc to properties)
+ sal_Int32 nElements = 2;
+ sal_Int32 nCurrentElement = 0;
+
+ // check for default value
+ if (aToken.nChapterFormat != CF_NUMBER)
+ {
+ nElements++;//we need the element
+ }
+ if( aToken.nOutlineLevel != MAXLEVEL )
+ {
+ nElements++;
+ }
+
+ rCurTokenSeq.realloc( nElements );
+
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[nCurrentElement].Name = "TokenType";
+ pArr[nCurrentElement++].Value <<=
+ OUString("TokenEntryNumber");
+
+ pArr[nCurrentElement].Name = "CharacterStyleName";
+ pArr[nCurrentElement++].Value <<= aProgCharStyle;
+ if( aToken.nChapterFormat != CF_NUMBER )
+ {
+ pArr[nCurrentElement].Name = "ChapterFormat";
+ sal_Int16 nVal;
+// the allowed values for chapter format, when used as entry number,
+// are CF_NUMBER and CF_NUM_NOPREPST_TITLE only, all else forced to
+//CF_NUMBER
+ switch(aToken.nChapterFormat)
+ {
+ default:
+ case CF_NUMBER:
+ nVal = text::ChapterFormat::NUMBER;
+ break;
+ case CF_NUM_NOPREPST_TITLE:
+ nVal = text::ChapterFormat::DIGIT;
+ break;
+ }
+ pArr[nCurrentElement++].Value <<= nVal;
+ }
+
+ // only a ChapterLevel != MAXLEVEL is registered
+ if (aToken.nOutlineLevel != MAXLEVEL)
+ {
+ pArr[nCurrentElement].Name = "ChapterLevel";
+ pArr[nCurrentElement].Value <<= aToken.nOutlineLevel;
+ }
+ }
+ break;
+ case TOKEN_ENTRY: // no difference between Entry and Entry Text
+ case TOKEN_ENTRY_TEXT:
+ {
+ rCurTokenSeq.realloc( 2 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<= OUString("TokenEntryText");
+
+ pArr[1].Name = "CharacterStyleName";
+ pArr[1].Value <<= aProgCharStyle;
+ }
+ break;
+ case TOKEN_TAB_STOP:
+ {
+ rCurTokenSeq.realloc(5); // #i21237#
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<= OUString("TokenTabStop");
+
+ if(SvxTabAdjust::End == aToken.eTabAlign)
+ {
+ pArr[1].Name = "TabStopRightAligned";
+ pArr[1].Value <<= true;
+ }
+ else
+ {
+ pArr[1].Name = "TabStopPosition";
+ sal_Int32 nPos = convertTwipToMm100(aToken.nTabStopPosition);
+ if(nPos < 0)
+ nPos = 0;
+ pArr[1].Value <<= nPos;
+ }
+ pArr[2].Name = "TabStopFillCharacter";
+ pArr[2].Value <<= OUString(aToken.cTabFillChar);
+ pArr[3].Name = "CharacterStyleName";
+ pArr[3].Value <<= aProgCharStyle;
+ // #i21237#
+ pArr[4].Name = "WithTab";
+ pArr[4].Value <<= aToken.bWithTab;
+ }
+ break;
+ case TOKEN_TEXT:
+ {
+ rCurTokenSeq.realloc( 3 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<= OUString("TokenText");
+
+ pArr[1].Name = "CharacterStyleName";
+ pArr[1].Value <<= aProgCharStyle;
+
+ pArr[2].Name = "Text";
+ pArr[2].Value <<= aToken.sText;
+ }
+ break;
+ case TOKEN_PAGE_NUMS:
+ {
+ rCurTokenSeq.realloc( 2 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<= OUString("TokenPageNumber");
+
+ pArr[1].Name = "CharacterStyleName";
+ pArr[1].Value <<= aProgCharStyle;
+ }
+ break;
+ case TOKEN_CHAPTER_INFO:
+ {
+ rCurTokenSeq.realloc( 4 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<= OUString("TokenChapterInfo");
+
+ pArr[1].Name = "CharacterStyleName";
+ pArr[1].Value <<= aProgCharStyle;
+
+ pArr[2].Name = "ChapterFormat";
+ sal_Int16 nVal = text::ChapterFormat::NUMBER;
+ switch(aToken.nChapterFormat)
+ {
+ case CF_NUMBER:
+ nVal = text::ChapterFormat::NUMBER;
+ break;
+ case CF_TITLE:
+ nVal = text::ChapterFormat::NAME;
+ break;
+ case CF_NUM_TITLE:
+ nVal = text::ChapterFormat::NAME_NUMBER;
+ break;
+ case CF_NUMBER_NOPREPST:
+ nVal = text::ChapterFormat::NO_PREFIX_SUFFIX;
+ break;
+ case CF_NUM_NOPREPST_TITLE:
+ nVal = text::ChapterFormat::DIGIT;
+ break;
+ }
+ pArr[2].Value <<= nVal;
+// #i53420#
+ pArr[3].Name = "ChapterLevel";
+ pArr[3].Value <<= aToken.nOutlineLevel;
+ }
+ break;
+ case TOKEN_LINK_START:
+ {
+ rCurTokenSeq.realloc( 2 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<=
+ OUString("TokenHyperlinkStart");
+ pArr[1].Name = "CharacterStyleName";
+ pArr[1].Value <<= aProgCharStyle;
+ }
+ break;
+ case TOKEN_LINK_END:
+ {
+ rCurTokenSeq.realloc( 1 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<=
+ OUString("TokenHyperlinkEnd");
+ }
+ break;
+ case TOKEN_AUTHORITY:
+ {
+ rCurTokenSeq.realloc( 3 );
+ beans::PropertyValue* pArr = rCurTokenSeq.getArray();
+
+ pArr[0].Name = "TokenType";
+ pArr[0].Value <<=
+ OUString("TokenBibliographyDataField");
+
+ pArr[1].Name = "CharacterStyleName";
+ pArr[1].Value <<= aProgCharStyle;
+
+ pArr[2].Name = "BibliographyDataField";
+ pArr[2].Value <<= sal_Int16(aToken.nAuthorityField);
+ }
+ break;
+
+ default:
+ ;
+ }
+ }
+
+ uno::Any aRet;
+ aRet <<= aRetSeq;
+ return aRet;
+}
+
+uno::Type SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::getElementType()
+{
+ return cppu::UnoType<uno::Sequence< beans::PropertyValues >>::get();
+}
+
+sal_Bool SAL_CALL
+SwXDocumentIndex::TokenAccess_Impl::hasElements()
+{
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unolinebreak.cxx b/sw/source/core/unocore/unolinebreak.cxx
new file mode 100644
index 000000000..b7f8a174a
--- /dev/null
+++ b/sw/source/core/unocore/unolinebreak.cxx
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unolinebreak.hxx>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weakref.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <sal/log.hxx>
+#include <svl/listener.hxx>
+#include <svl/itemprop.hxx>
+
+#include <IDocumentContentOperations.hxx>
+#include <doc.hxx>
+#include <formatlinebreak.hxx>
+#include <unotextrange.hxx>
+#include <ndtxt.hxx>
+#include <textlinebreak.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+
+using namespace com::sun::star;
+
+/// The inner part SwXLineBreak, which is deleted with a locked SolarMutex.
+class SwXLineBreak::Impl : public SvtListener
+{
+public:
+ bool m_bIsDescriptor;
+ SwFormatLineBreak* m_pFormatLineBreak;
+ SwLineBreakClear m_eClear;
+
+ Impl(SwFormatLineBreak* const pLineBreak)
+ : m_bIsDescriptor(pLineBreak == nullptr)
+ , m_pFormatLineBreak(pLineBreak)
+ , m_eClear(SwLineBreakClear::NONE)
+ {
+ if (m_pFormatLineBreak)
+ {
+ StartListening(m_pFormatLineBreak->GetNotifier());
+ }
+ }
+
+ const SwFormatLineBreak* GetLineBreakFormat() const;
+
+ const SwFormatLineBreak& GetLineBreakFormatOrThrow() const;
+
+ void Invalidate();
+
+protected:
+ void Notify(const SfxHint& rHint) override;
+};
+
+const SwFormatLineBreak* SwXLineBreak::Impl::GetLineBreakFormat() const
+{
+ return m_pFormatLineBreak;
+}
+
+const SwFormatLineBreak& SwXLineBreak::Impl::GetLineBreakFormatOrThrow() const
+{
+ const SwFormatLineBreak* pLineBreak(GetLineBreakFormat());
+ if (!pLineBreak)
+ {
+ throw uno::RuntimeException("SwXLineBreak: disposed or invalid", nullptr);
+ }
+
+ return *pLineBreak;
+}
+
+void SwXLineBreak::Impl::Invalidate()
+{
+ EndListeningAll();
+ m_pFormatLineBreak = nullptr;
+}
+
+void SwXLineBreak::Impl::Notify(const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::Dying)
+ {
+ Invalidate();
+ }
+}
+
+SwXLineBreak::SwXLineBreak(SwFormatLineBreak& rFormat)
+ : m_pImpl(new SwXLineBreak::Impl(&rFormat))
+{
+}
+
+SwXLineBreak::SwXLineBreak()
+ : m_pImpl(new SwXLineBreak::Impl(nullptr))
+{
+}
+
+SwXLineBreak::~SwXLineBreak() {}
+
+uno::Reference<text::XTextContent>
+SwXLineBreak::CreateXLineBreak(SwFormatLineBreak* pLineBreakFormat)
+{
+ uno::Reference<text::XTextContent> xLineBreak;
+ if (pLineBreakFormat)
+ {
+ xLineBreak = pLineBreakFormat->GetXTextContent();
+ }
+ if (!xLineBreak.is())
+ {
+ SwXLineBreak* const pLineBreak(pLineBreakFormat ? new SwXLineBreak(*pLineBreakFormat)
+ : new SwXLineBreak);
+ xLineBreak.set(pLineBreak);
+ if (pLineBreakFormat)
+ {
+ pLineBreakFormat->SetXLineBreak(xLineBreak);
+ }
+ }
+ return xLineBreak;
+}
+
+OUString SAL_CALL SwXLineBreak::getImplementationName() { return "SwXLineBreak"; }
+
+sal_Bool SAL_CALL SwXLineBreak::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL SwXLineBreak::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.LineBreak" };
+}
+
+void SAL_CALL SwXLineBreak::attach(const uno::Reference<text::XTextRange>& xTextRange)
+{
+ SolarMutexGuard aGuard;
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ auto pRange = dynamic_cast<SwXTextRange*>(xTextRange.get());
+ if (!pRange)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwDoc& rNewDoc = pRange->GetDoc();
+ SwUnoInternalPaM aPam(rNewDoc);
+ sw::XTextRangeToSwPaM(aPam, xTextRange);
+ UnoActionContext aContext(&rNewDoc);
+ SwFormatLineBreak aLineBreak(m_pImpl->m_eClear);
+ SetAttrMode nInsertFlags = SetAttrMode::DEFAULT;
+ rNewDoc.getIDocumentContentOperations().InsertPoolItem(aPam, aLineBreak, nInsertFlags);
+ auto pTextAttr
+ = static_cast<SwTextLineBreak*>(aPam.GetNode().GetTextNode()->GetTextAttrForCharAt(
+ aPam.GetPoint()->nContent.GetIndex() - 1, RES_TXTATR_LINEBREAK));
+ if (pTextAttr)
+ {
+ m_pImpl->EndListeningAll();
+ auto pLineBreak = const_cast<SwFormatLineBreak*>(&pTextAttr->GetLineBreak());
+ m_pImpl->m_pFormatLineBreak = pLineBreak;
+ m_pImpl->StartListening(pLineBreak->GetNotifier());
+ }
+ m_pImpl->m_bIsDescriptor = false;
+}
+
+uno::Reference<text::XTextRange> SAL_CALL SwXLineBreak::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ return m_pImpl->GetLineBreakFormatOrThrow().GetAnchor();
+}
+
+void SAL_CALL SwXLineBreak::dispose()
+{
+ SAL_WARN("sw.uno", "SwXLineBreak::dispose: not implemented");
+}
+
+void SAL_CALL
+SwXLineBreak::addEventListener(const uno::Reference<lang::XEventListener>& /*xListener*/)
+{
+}
+
+void SAL_CALL
+SwXLineBreak::removeEventListener(const uno::Reference<lang::XEventListener>& /*xListener*/)
+{
+}
+
+uno::Reference<beans::XPropertySetInfo> SAL_CALL SwXLineBreak::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+
+ static uno::Reference<beans::XPropertySetInfo> xRet
+ = aSwMapProvider.GetPropertySet(PROPERTY_MAP_LINEBREAK)->getPropertySetInfo();
+ return xRet;
+}
+
+void SAL_CALL SwXLineBreak::setPropertyValue(const OUString& rPropertyName,
+ const css::uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ if (rPropertyName != UNO_NAME_CLEAR)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ sal_Int16 eValue{};
+ if (rValue >>= eValue)
+ {
+ m_pImpl->m_eClear = static_cast<SwLineBreakClear>(eValue);
+ }
+ }
+ else
+ {
+ m_pImpl->m_pFormatLineBreak->PutValue(rValue, 0);
+ }
+}
+
+uno::Any SAL_CALL SwXLineBreak::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ if (sw::GetDefaultTextContentValue(aRet, rPropertyName))
+ {
+ return aRet;
+ }
+
+ if (rPropertyName != UNO_NAME_CLEAR)
+ {
+ beans::UnknownPropertyException aExcept;
+ aExcept.Message = rPropertyName;
+ throw aExcept;
+ }
+
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ auto eValue = static_cast<sal_Int16>(m_pImpl->m_eClear);
+ aRet <<= eValue;
+ }
+ else
+ {
+ aRet <<= m_pImpl->m_pFormatLineBreak->GetEnumValue();
+ }
+ return aRet;
+}
+
+void SAL_CALL SwXLineBreak::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXLineBreak::addPropertyChangeListener: not implemented");
+}
+
+void SAL_CALL SwXLineBreak::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXLineBreak::removePropertyChangeListener: not implemented");
+}
+
+void SAL_CALL SwXLineBreak::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXLineBreak::addVetoableChangeListener: not implemented");
+}
+
+void SAL_CALL SwXLineBreak::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/)
+{
+ SAL_WARN("sw.uno", "SwXLineBreak::removeVetoableChangeListener: not implemented");
+}
+
+sal_Int64 SAL_CALL SwXLineBreak::getSomething(const css::uno::Sequence<sal_Int8>& rIdentifier)
+{
+ return comphelper::getSomethingImpl<SwXLineBreak>(rIdentifier, this);
+}
+
+const uno::Sequence<sal_Int8>& SwXLineBreak::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXLineBreakUnoTunnelId;
+ return theSwXLineBreakUnoTunnelId.getSeq();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unomap.cxx b/sw/source/core/unocore/unomap.cxx
new file mode 100644
index 000000000..e07481448
--- /dev/null
+++ b/sw/source/core/unocore/unomap.cxx
@@ -0,0 +1,1575 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/i18n/XForbiddenCharacters.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+#include <com/sun/star/table/BorderLine.hpp>
+#include <com/sun/star/text/PageNumberType.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XDependentTextField.hpp>
+#include <com/sun/star/text/XDocumentIndexMark.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <com/sun/star/script/XLibraryContainer.hpp>
+#include <com/sun/star/drawing/HomogenMatrix3.hpp>
+#include <osl/diagnose.h>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unomid.h>
+#include <cmdid.h>
+#include <unofldmid.h>
+#include <editeng/memberids.h>
+#include <editeng/unoprnms.hxx>
+#include <svl/itemprop.hxx>
+#include "unomapproperties.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+#define COMMON_FLDTYP_PROPERTIES \
+ { u"" UNO_NAME_IS_FIELD_USED, FIELD_PROP_IS_FIELD_USED, cppu::UnoType<float>::get(), PropertyAttribute::READONLY, 0},\
+ { u"" UNO_NAME_IS_FIELD_DISPLAYED, FIELD_PROP_IS_FIELD_DISPLAYED, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::READONLY, 0},\
+ { u"" UNO_NAME_TITLE, FIELD_PROP_TITLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},\
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetPropertyMapEntries(sal_uInt16 nPropertyId)
+{
+ OSL_ENSURE(nPropertyId < PROPERTY_MAP_END, "Id ?" );
+ if( !m_aMapEntriesArr[ nPropertyId ] )
+ {
+ switch(nPropertyId)
+ {
+ case PROPERTY_MAP_TEXT_CURSOR:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetTextCursorPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_ACCESSIBILITY_TEXT_ATTRIBUTE:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetAccessibilityTextAttrPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_PARAGRAPH:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetParagraphPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_PARA_AUTO_STYLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetAutoParaStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_CHAR_STYLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetCharStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_CHAR_AUTO_STYLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetAutoCharStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_RUBY_AUTO_STYLE :
+ {
+ static SfxItemPropertyMapEntry const aAutoRubyStyleMap [] =
+ {
+ { u"" UNO_NAME_RUBY_ADJUST, RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ADJUST },
+ { u"" UNO_NAME_RUBY_IS_ABOVE, RES_TXTATR_CJK_RUBY, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ABOVE },
+ { u"" UNO_NAME_RUBY_POSITION, RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_POSITION },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aAutoRubyStyleMap;
+ }
+ break;
+ case PROPERTY_MAP_PARA_STYLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetParaStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_CONDITIONAL_PARA_STYLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetConditionalParaStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_FRAME_STYLE:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetFrameStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_PAGE_STYLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetPageStylePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_NUM_STYLE :
+ {
+ static SfxItemPropertyMapEntry const aNumStyleMap [] =
+ {
+ { UNO_NAME_NUMBERING_RULES, FN_UNO_NUM_RULES, cppu::UnoType<css::container::XIndexReplace>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_IS_PHYSICAL, FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_DISPLAY_NAME, FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_HIDDEN, FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_STYLE_INTEROP_GRAB_BAG, FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aNumStyleMap;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_TABLE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetTablePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_TABLE_CELL :
+ {
+ static SfxItemPropertyMapEntry const aCellMap_Impl[] =
+ {
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_NUMBER_FORMAT, RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID ,0 },
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_TEXT_SECTION, FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_IS_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), 0, MID_PROTECT_CONTENT},
+ { u"" UNO_NAME_CELL_NAME, FN_UNO_CELL_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY,0},
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT },
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_ROW_SPAN, FN_UNO_CELL_ROW_SPAN, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"" UNO_NAME_CELL_INTEROP_GRAB_BAG, RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_PARENT_TEXT, FN_UNO_PARENT_TEXT, cppu::UnoType<text::XText>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 },
+ REDLINE_NODE_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aCellMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_TABLE_RANGE:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetRangePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_SECTION:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetSectionPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_TEXT_SEARCH:
+ {
+ static SfxItemPropertyMapEntry const aSearchPropertyMap_Impl[] =
+ {
+ { u"" UNO_NAME_SEARCH_ALL, WID_SEARCH_ALL, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_BACKWARDS, WID_BACKWARDS, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_CASE_SENSITIVE, WID_CASE_SENSITIVE, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_REGULAR_EXPRESSION, WID_REGULAR_EXPRESSION, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_SIMILARITY, WID_SIMILARITY, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_SIMILARITY_ADD, WID_SIMILARITY_ADD, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_SIMILARITY_EXCHANGE, WID_SIMILARITY_EXCHANGE,cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_SIMILARITY_RELAX, WID_SIMILARITY_RELAX, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_SIMILARITY_REMOVE, WID_SIMILARITY_REMOVE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_STYLES, WID_STYLES, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEARCH_WORDS, WID_WORDS, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aSearchPropertyMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_FRAME:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetFramePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_TEXT_GRAPHIC:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetGraphicPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_EMBEDDED_OBJECT:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetEmbeddedPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_TEXT_SHAPE:
+ {
+ static SfxItemPropertyMapEntry const aShapeMap_Impl[] =
+ {
+ { u"" UNO_NAME_ANCHOR_PAGE_NO, RES_ANCHOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_ANCHOR_PAGENUM },
+ { u"" UNO_NAME_ANCHOR_TYPE, RES_ANCHOR, cppu::UnoType<css::text::TextContentAnchorType>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_ANCHOR_ANCHORTYPE},
+ { u"" UNO_NAME_ANCHOR_FRAME, RES_ANCHOR, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID, MID_ANCHOR_ANCHORFRAME},
+ { u"" UNO_NAME_HORI_ORIENT, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_HORIORIENT_ORIENT },
+ { u"" UNO_NAME_HORI_ORIENT_POSITION, RES_HORI_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_HORIORIENT_POSITION|CONVERT_TWIPS },
+ { u"" UNO_NAME_HORI_ORIENT_RELATION, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_HORIORIENT_RELATION },
+ { u"" UNO_NAME_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_SURROUND, RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_SURROUND_SURROUNDTYPE },
+ { u"" UNO_NAME_TEXT_WRAP, RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE },
+ { u"" UNO_NAME_SURROUND_ANCHORONLY, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID, MID_SURROUND_ANCHORONLY },
+ { u"" UNO_NAME_SURROUND_CONTOUR, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR },
+ { u"" UNO_NAME_CONTOUR_OUTSIDE, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE },
+ { u"" UNO_NAME_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_VERTORIENT_ORIENT },
+ { u"" UNO_NAME_VERT_ORIENT_POSITION, RES_VERT_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_VERTORIENT_POSITION|CONVERT_TWIPS },
+ { u"" UNO_NAME_VERT_ORIENT_RELATION, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE|PropertyAttribute::MAYBEVOID ,MID_VERTORIENT_RELATION },
+ { u"" UNO_NAME_TEXT_RANGE, FN_TEXT_RANGE, cppu::UnoType<css::text::XTextRange>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_OPAQUE, RES_OPAQUE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_ANCHOR_POSITION, FN_ANCHOR_POSITION, cppu::UnoType<css::awt::Point>::get(), PropertyAttribute::READONLY, 0},
+ // #i26791#
+ { u"" UNO_NAME_IS_FOLLOWING_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FOLLOW_TEXT_FLOW},
+ // #i28701#
+ { u"" UNO_NAME_WRAP_INFLUENCE_ON_POSITION, RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_WRAP_INFLUENCE},
+ { u"" UNO_NAME_ALLOW_OVERLAP, RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_ALLOW_OVERLAP},
+ // #i28749#
+ { u"" UNO_NAME_TRANSFORMATION_IN_HORI_L2R,
+ FN_SHAPE_TRANSFORMATION_IN_HORI_L2R,
+ cppu::UnoType<css::drawing::HomogenMatrix3>::get(),
+ PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_POSITION_LAYOUT_DIR,
+ FN_SHAPE_POSITION_LAYOUT_DIR,
+ cppu::UnoType<sal_Int16>::get(),
+ PROPERTY_NONE, 0},
+ // #i36248#
+ { u"" UNO_NAME_STARTPOSITION_IN_HORI_L2R,
+ FN_SHAPE_STARTPOSITION_IN_HORI_L2R,
+ cppu::UnoType<css::awt::Point>::get(),
+ PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_ENDPOSITION_IN_HORI_L2R,
+ FN_SHAPE_ENDPOSITION_IN_HORI_L2R,
+ cppu::UnoType<css::awt::Point>::get(),
+ PropertyAttribute::READONLY, 0},
+ // #i71182#
+ // missing map entry for property <PageToggle>
+ { u"" UNO_NAME_PAGE_TOGGLE, RES_HORI_ORIENT, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_HORIORIENT_PAGETOGGLE },
+ { u"" UNO_NAME_RELATIVE_HEIGHT, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT },
+ { u"" UNO_NAME_RELATIVE_HEIGHT_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION },
+ { u"" UNO_NAME_RELATIVE_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH },
+ { u"" UNO_NAME_RELATIVE_WIDTH_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION },
+ { u"" UNO_NAME_TEXT_BOX, FN_TEXT_BOX, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TEXT_BOX},
+ { u"" UNO_NAME_TEXT_BOX_CONTENT, FN_TEXT_BOX, cppu::UnoType<text::XTextFrame>::get(), PROPERTY_NONE, MID_TEXT_BOX_CONTENT},
+ { u"" UNO_NAME_CHAIN_NEXT_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NEXTNAME},
+ { u"" UNO_NAME_CHAIN_PREV_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_PREVNAME},
+ { u"" UNO_NAME_CHAIN_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NAME },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aShapeMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_MARK:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetIndexMarkPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_CNTIDX_MARK:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetContentMarkPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_USER_MARK:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetUserMarkPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_INDEX_IDX:
+ {
+ static SfxItemPropertyMapEntry const aTOXIndexMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_CREATE_FROM_CHAPTER, WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_ALPHABETICAL_SEPARATORS, WID_USE_ALPHABETICAL_SEPARATORS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_KEY_AS_ENTRY, WID_USE_KEY_AS_ENTRY , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_COMBINED_ENTRIES, WID_USE_COMBINED_ENTRIES , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_CASE_SENSITIVE, WID_IS_CASE_SENSITIVE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_P_P, WID_USE_P_P , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_DASH, WID_USE_DASH , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_UPPER_CASE, WID_USE_UPPER_CASE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_MAIN_ENTRY_CHARACTER_STYLE_NAME, WID_MAIN_ENTRY_CHARACTER_STYLE_NAME , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLESEPARATOR, WID_PARA_SEP, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL2, WID_PARA_LEV2, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL3, WID_PARA_LEV3, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_IS_COMMA_SEPARATED, WID_IS_COMMA_SEPARATED, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_DOCUMENT_INDEX_MARKS, WID_INDEX_MARKS, cppu::UnoType< cppu::UnoSequenceType<css::text::XDocumentIndexMark> >::get(), PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_IS_RELATIVE_TABSTOPS, WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LOCALE, WID_IDX_LOCALE, cppu::UnoType<css::lang::Locale>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SORT_ALGORITHM, WID_IDX_SORT_ALGORITHM, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTOXIndexMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_CNTNT:
+ {
+ static SfxItemPropertyMapEntry const aTOXContentMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_LEVEL, WID_LEVEL , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_MARKS, WID_CREATE_FROM_MARKS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_HIDE_TAB_LEADER_AND_PAGE_NUMBERS, WID_HIDE_TABLEADER_PAGENUMBERS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TAB_IN_TOC, WID_TAB_IN_TOC, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TOC_BOOKMARK, WID_TOC_BOOKMARK, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TOC_NEWLINE, WID_TOC_NEWLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TOC_PARAGRAPH_OUTLINE_LEVEL, WID_TOC_PARAGRAPH_OUTLINE_LEVEL, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_OUTLINE, WID_CREATE_FROM_OUTLINE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_CHAPTER, WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_PARAGRAPH_STYLES, WID_LEVEL_PARAGRAPH_STYLES , cppu::UnoType<css::container::XIndexReplace>::get() , PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_CREATE_FROM_LEVEL_PARAGRAPH_STYLES, WID_CREATE_FROM_PARAGRAPH_STYLES, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL2, WID_PARA_LEV2, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL3, WID_PARA_LEV3, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL4, WID_PARA_LEV4, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL5, WID_PARA_LEV5, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL6, WID_PARA_LEV6, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL7, WID_PARA_LEV7, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL8, WID_PARA_LEV8, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL9, WID_PARA_LEV9, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL10, WID_PARA_LEV10, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_IS_RELATIVE_TABSTOPS, WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DOCUMENT_INDEX_MARKS, WID_INDEX_MARKS, cppu::UnoType< cppu::UnoSequenceType<css::text::XDocumentIndexMark> >::get(), PropertyAttribute::READONLY ,0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTOXContentMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_USER:
+ {
+ static SfxItemPropertyMapEntry const aTOXUserMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_CREATE_FROM_MARKS, WID_CREATE_FROM_MARKS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_HIDE_TAB_LEADER_AND_PAGE_NUMBERS, WID_HIDE_TABLEADER_PAGENUMBERS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TAB_IN_TOC, WID_TAB_IN_TOC, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TOC_BOOKMARK, WID_TOC_BOOKMARK, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TOC_NEWLINE, WID_TOC_NEWLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TOC_PARAGRAPH_OUTLINE_LEVEL, WID_TOC_PARAGRAPH_OUTLINE_LEVEL, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_CHAPTER, WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_LEVEL_FROM_SOURCE, WID_USE_LEVEL_FROM_SOURCE , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0},
+ { u"" UNO_NAME_LEVEL_PARAGRAPH_STYLES, WID_LEVEL_PARAGRAPH_STYLES , cppu::UnoType<css::container::XIndexReplace>::get() , PropertyAttribute::READONLY,0},
+ { u"" UNO_NAME_CREATE_FROM_LEVEL_PARAGRAPH_STYLES, WID_CREATE_FROM_PARAGRAPH_STYLES, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_TABLES, WID_CREATE_FROM_TABLES , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_TEXT_FRAMES, WID_CREATE_FROM_TEXT_FRAMES , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_GRAPHIC_OBJECTS, WID_CREATE_FROM_GRAPHIC_OBJECTS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_EMBEDDED_OBJECTS, WID_CREATE_FROM_EMBEDDED_OBJECTS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL2, WID_PARA_LEV2, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL3, WID_PARA_LEV3, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL4, WID_PARA_LEV4, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL5, WID_PARA_LEV5, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL6, WID_PARA_LEV6, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL7, WID_PARA_LEV7, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL8, WID_PARA_LEV8, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL9, WID_PARA_LEV9, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL10, WID_PARA_LEV10, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_DOCUMENT_INDEX_MARKS, WID_INDEX_MARKS, cppu::UnoType< cppu::UnoSequenceType<css::text::XDocumentIndexMark> >::get(), PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_IS_RELATIVE_TABSTOPS, WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USER_INDEX_NAME, WID_USER_IDX_NAME, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTOXUserMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_TABLES:
+ {
+ static SfxItemPropertyMapEntry const aTOXTablesMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_CREATE_FROM_CHAPTER, WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_LABELS, WID_CREATE_FROM_LABELS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LABEL_CATEGORY, WID_LABEL_CATEGORY , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LABEL_DISPLAY_TYPE, WID_LABEL_DISPLAY_TYPE , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_IS_RELATIVE_TABSTOPS, WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTOXTablesMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_OBJECTS:
+ {
+ static SfxItemPropertyMapEntry const aTOXObjectsMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_CREATE_FROM_CHAPTER, WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_ALPHABETICAL_SEPARATORS, WID_USE_ALPHABETICAL_SEPARATORS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0},
+ { u"" UNO_NAME_CREATE_FROM_STAR_MATH, WID_CREATE_FROM_STAR_MATH , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_STAR_CHART, WID_CREATE_FROM_STAR_CHART , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_STAR_CALC, WID_CREATE_FROM_STAR_CALC , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_STAR_DRAW, WID_CREATE_FROM_STAR_DRAW , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_OTHER_EMBEDDED_OBJECTS, WID_CREATE_FROM_OTHER_EMBEDDED_OBJECTS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_IS_RELATIVE_TABSTOPS, WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTOXObjectsMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_ILLUSTRATIONS:
+ {
+ static SfxItemPropertyMapEntry const aTOXIllustrationsMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_CREATE_FROM_CHAPTER, WID_CREATE_FROM_CHAPTER , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CREATE_FROM_LABELS, WID_CREATE_FROM_LABELS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USE_ALPHABETICAL_SEPARATORS, WID_USE_ALPHABETICAL_SEPARATORS , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LABEL_CATEGORY, WID_LABEL_CATEGORY , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LABEL_DISPLAY_TYPE, WID_LABEL_DISPLAY_TYPE , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_IS_RELATIVE_TABSTOPS, WID_IS_RELATIVE_TABSTOPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTOXIllustrationsMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_TABLE_ROW:
+ {
+ static SfxItemPropertyMapEntry const aTableRowPropertyMap_Impl[] =
+ {
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_TABLE_COLUMN_SEPARATORS, FN_UNO_TABLE_COLUMN_SEPARATORS, cppu::UnoType< cppu::UnoSequenceType<css::text::TableColumnSeparator> >::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_HEIGHT, FN_UNO_ROW_HEIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,CONVERT_TWIPS },
+ { u"" UNO_NAME_IS_AUTO_HEIGHT, FN_UNO_ROW_AUTO_HEIGHT, cppu::UnoType<bool>::get(), PROPERTY_NONE , 0 },
+ { u"" UNO_NAME_SIZE_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE },
+ { u"" UNO_NAME_WIDTH_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE },
+ { u"" UNO_NAME_IS_SPLIT_ALLOWED, RES_ROW_SPLIT, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_HAS_TEXT_CHANGES_ONLY, RES_PRINT, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_ROW_INTEROP_GRAB_BAG, RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ m_aMapEntriesArr[nPropertyId] = aTableRowPropertyMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_TABLE_CURSOR:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetTextTableCursorPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_BOOKMARK:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetBookmarkPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_FIELDMARK:
+ {
+ static SfxItemPropertyMapEntry const aFieldmarkMap_Impl[] =
+ {
+ // FIXME: is this supposed to actually exist as UNO property, or is it supposed to be in the "parameters" of the field?
+ { u"Checked", 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aFieldmarkMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_PARAGRAPH_EXTENSIONS:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetParagraphExtensionsPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_BIBLIOGRAPHY :
+ {
+ static SfxItemPropertyMapEntry const aBibliographyMap_Impl[] =
+ {
+ BASE_INDEX_PROPERTIES_
+ { u"" UNO_NAME_IS_PROTECTED, WID_PROTECTED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_PARA_STYLEHEADING, WID_PARA_HEAD, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_PARA_STYLELEVEL1, WID_PARA_LEV1, cppu::UnoType<OUString>::get() , 0, 0},
+ { u"" UNO_NAME_LEVEL_FORMAT, WID_LEVEL_FORMAT , cppu::UnoType<css::container::XIndexReplace>::get() , PROPERTY_NONE,0},
+ { u"" UNO_NAME_LOCALE, WID_IDX_LOCALE, cppu::UnoType<css::lang::Locale>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SORT_ALGORITHM, WID_IDX_SORT_ALGORITHM, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aBibliographyMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_DOCUMENT:
+ {
+ static SfxItemPropertyMapEntry const aDocMap_Impl[] =
+ {
+ { u"" UNO_NAME_BASIC_LIBRARIES, WID_DOC_BASIC_LIBRARIES, cppu::UnoType<css::script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_CHAR_FONT_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME },
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME },
+ { u"" UNO_NAME_CHAR_FONT_FAMILY, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY },
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET },
+ { u"" UNO_NAME_CHAR_FONT_PITCH, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH },
+ { u"" UNO_NAME_CHAR_FONT_NAME_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME },
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME },
+ { u"" UNO_NAME_CHAR_FONT_FAMILY_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY },
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET },
+ { u"" UNO_NAME_CHAR_FONT_PITCH_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH },
+ { u"" UNO_NAME_CHAR_FONT_NAME_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME },
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME },
+ { u"" UNO_NAME_CHAR_FONT_FAMILY_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY },
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET },
+ { u"" UNO_NAME_CHAR_FONT_PITCH_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH },
+ { u"" UNO_NAME_CHAR_LOCALE, RES_CHRATR_LANGUAGE , cppu::UnoType<css::lang::Locale>::get(), PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE },
+ { u"" UNO_NAME_CHARACTER_COUNT, WID_DOC_CHAR_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_DIALOG_LIBRARIES, WID_DOC_DIALOG_LIBRARIES, cppu::UnoType<css::script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_VBA_DOCOBJ, WID_DOC_VBA_DOCOBJ, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_INDEX_AUTO_MARK_FILE_U_R_L, WID_DOC_AUTO_MARK_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PARAGRAPH_COUNT, WID_DOC_PARA_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_RECORD_CHANGES, WID_DOC_CHANGES_RECORD, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SHOW_CHANGES, WID_DOC_CHANGES_SHOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_WORD_COUNT, WID_DOC_WORD_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_IS_TEMPLATE, WID_DOC_ISTEMPLATEID, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_WORD_SEPARATOR, WID_DOC_WORD_SEPARATOR, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_HIDE_FIELD_TIPS, WID_DOC_HIDE_TIPS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_REDLINE_DISPLAY_TYPE, WID_DOC_REDLINE_DISPLAY, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_REDLINE_PROTECTION_KEY, WID_DOC_CHANGES_PASSWORD, cppu::UnoType< cppu::UnoSequenceType<sal_Int8> >::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_FORBIDDEN_CHARACTERS, WID_DOC_FORBIDDEN_CHARS, cppu::UnoType<css::i18n::XForbiddenCharacters>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TWO_DIGIT_YEAR, WID_DOC_TWO_DIGIT_YEAR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_AUTOMATIC_CONTROL_FOCUS, WID_DOC_AUTOMATIC_CONTROL_FOCUS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_APPLY_FORM_DESIGN_MODE, WID_DOC_APPLY_FORM_DESIGN_MODE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_RUNTIME_UID, WID_DOC_RUNTIME_UID, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_LOCK_UPDATES, WID_DOC_LOCK_UPDATES, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"UndocumentedWriterfilterHack", WID_DOC_WRITERFILTER, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_HAS_VALID_SIGNATURES, WID_DOC_HAS_VALID_SIGNATURES, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_BUILDID, WID_DOC_BUILDID, cppu::UnoType<OUString>::get(), 0, 0},
+ { u"" UNO_NAME_DOC_INTEROP_GRAB_BAG, WID_DOC_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DEFAULT_PAGE_MODE, WID_DOC_DEFAULT_PAGE_MODE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_LINK_TARGET:
+ {
+ static SfxItemPropertyMapEntry const aLinkTargetMap_Impl[] =
+ {
+ { u"" UNO_LINK_DISPLAY_BITMAP, 0, cppu::UnoType<css::awt::XBitmap>::get(), PropertyAttribute::READONLY, 0xbf},
+ { u"" UNO_LINK_DISPLAY_NAME, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aLinkTargetMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_AUTO_TEXT_GROUP :
+ {
+ static SfxItemPropertyMapEntry const aAutoTextGroupMap_Impl[] =
+ {
+ { u"" UNO_NAME_FILE_PATH, WID_GROUP_PATH, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_TITLE, WID_GROUP_TITLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aAutoTextGroupMap_Impl;
+ }
+ break;
+ case PROPERTY_MAP_TEXTPORTION_EXTENSIONS:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetTextPortionExtensionPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_FOOTNOTE:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetFootnotePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_REDLINE :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetRedlinePropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_TEXT_DEFAULT :
+ {
+ SfxItemPropertyMapEntry* aTextDefaultMap_Impl = GetTextDefaultPropertyMap();
+ m_aMapEntriesArr[nPropertyId] = aTextDefaultMap_Impl;
+ for( SfxItemPropertyMapEntry * pMap = aTextDefaultMap_Impl;
+ !pMap->aName.isEmpty(); ++pMap )
+ {
+ // UNO_NAME_PAGE_DESC_NAME should keep its MAYBEVOID flag
+ if (RES_PAGEDESC != pMap->nWID || MID_PAGEDESC_PAGEDESCNAME != pMap->nMemberId)
+ pMap->nFlags &= ~PropertyAttribute::MAYBEVOID;
+ }
+ }
+ break;
+ case PROPERTY_MAP_REDLINE_PORTION :
+ {
+ m_aMapEntriesArr[nPropertyId] = GetRedlinePortionPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATETIME:
+ {
+ static SfxItemPropertyMapEntry const aDateTimeFieldPropMap[] =
+ {
+ {u"" UNO_NAME_ADJUST, FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATE_TIME_VALUE, FIELD_PROP_DATE_TIME, cppu::UnoType<css::util::DateTime>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ {u"" UNO_NAME_IS_DATE, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDateTimeFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_USER :
+ {
+ static SfxItemPropertyMapEntry const aUserFieldPropMap[] =
+ {
+ {u"" UNO_NAME_IS_SHOW_FORMULA, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_VISIBLE, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ m_aMapEntriesArr[nPropertyId] = aUserFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_SET_EXP :
+ {
+ static SfxItemPropertyMapEntry const aSetExpFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_HINT, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBERING_TYPE, FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_INPUT, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ // #i69733# wrong name - UNO_NAME_IS_INPUT expanded to "Input" instead of "IsInput"
+ {u"" UNO_NAME_INPUT, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_SHOW_FORMULA, FIELD_PROP_BOOL3, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_VISIBLE, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SEQUENCE_VALUE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SUB_TYPE, FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_VALUE, FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_VARIABLE_NAME, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aSetExpFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_GET_EXP :
+ {
+ static SfxItemPropertyMapEntry const aGetExpFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_SHOW_FORMULA, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SUB_TYPE, FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_VALUE, FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_VARIABLE_SUBTYPE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aGetExpFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_FILE_NAME:
+ {
+ static SfxItemPropertyMapEntry const aFileNameFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_FILE_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aFileNameFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_PAGE_NUM :
+ {
+ static SfxItemPropertyMapEntry const aPageNumFieldPropMap [] =
+ {
+ {u"" UNO_NAME_NUMBERING_TYPE, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_OFFSET, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SUB_TYPE, FIELD_PROP_SUBTYPE, cppu::UnoType<css::text::PageNumberType>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_USERTEXT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aPageNumFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_AUTHOR :
+ {
+ static SfxItemPropertyMapEntry const aAuthorFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_FULL_NAME,FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aAuthorFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_CHAPTER :
+ {
+ static SfxItemPropertyMapEntry const aChapterFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CHAPTER_FORMAT,FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_LEVEL,FIELD_PROP_BYTE1, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aChapterFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_GET_REFERENCE :
+ {
+ static SfxItemPropertyMapEntry const aGetRefFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_REFERENCE_FIELD_PART,FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_REFERENCE_FIELD_SOURCE,FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SEQUENCE_NUMBER, FIELD_PROP_SHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SOURCE_NAME, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_REFERENCE_FIELD_LANGUAGE, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aGetRefFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_CONDITIONED_TEXT :
+ {
+ static SfxItemPropertyMapEntry const aConditionedTextFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONDITION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_FALSE_CONTENT, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_CONDITION_TRUE , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_TRUE_CONTENT , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aConditionedTextFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_HIDDEN_TEXT :
+ {
+ static SfxItemPropertyMapEntry const aHiddenTextFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONDITION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CONTENT , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_HIDDEN , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aHiddenTextFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_ANNOTATION :
+ {
+ static SfxItemPropertyMapEntry const aAnnotationFieldPropMap [] =
+ {
+ {u"" UNO_NAME_AUTHOR, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INITIALS, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_RESOLVED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATE_TIME_VALUE, FIELD_PROP_DATE_TIME, cppu::UnoType<css::util::DateTime>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATE, FIELD_PROP_DATE, cppu::UnoType<css::util::Date>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_TEXT_RANGE, FIELD_PROP_TEXT, cppu::UnoType<css::uno::XInterface>::get(), PropertyAttribute::READONLY, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aAnnotationFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_INPUT:
+ {
+ static SfxItemPropertyMapEntry const aInputFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_HINT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_HELP, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_TOOLTIP, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {UNO_NAME_MISC_OBJ_INTEROPGRABBAG, FIELD_PROP_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aInputFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_MACRO :
+ {
+ static SfxItemPropertyMapEntry const aMacroFieldPropMap [] =
+ {
+ {u"" UNO_NAME_HINT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_MACRO_NAME,FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_MACRO_LIBRARY,FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(),PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SCRIPT_URL,FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(),PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aMacroFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DDE :
+ {
+ static SfxItemPropertyMapEntry const aDDEFieldPropMap [] =
+ {
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDDEFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DROPDOWN :
+ {
+ static SfxItemPropertyMapEntry const aDropDownMap [] =
+ {
+ {u"" UNO_NAME_ITEMS, FIELD_PROP_STRINGS, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SELITEM, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_HELP, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_TOOLTIP, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDropDownMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_HIDDEN_PARA :
+ {
+ static SfxItemPropertyMapEntry const aHiddenParaFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONDITION,FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_HIDDEN , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aHiddenParaFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOC_INFO :
+ {
+ static SfxItemPropertyMapEntry const aDocInfoFieldPropMap [] =
+ {
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INFO_FORMAT, FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INFO_TYPE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_TEMPLATE_NAME :
+ {
+ static SfxItemPropertyMapEntry const aTmplNameFieldPropMap [] =
+ {
+ {u"" UNO_NAME_FILE_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTmplNameFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_USER_EXT :
+ {
+ static SfxItemPropertyMapEntry const aUsrExtFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_USER_DATA_TYPE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId]= aUsrExtFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_REF_PAGE_SET :
+ {
+ static SfxItemPropertyMapEntry const aRefPgSetFieldPropMap [] =
+ {
+ {u"" UNO_NAME_OFFSET, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_ON, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aRefPgSetFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_REF_PAGE_GET :
+ {
+ static SfxItemPropertyMapEntry const aRefPgGetFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBERING_TYPE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aRefPgGetFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_JUMP_EDIT :
+ {
+ static SfxItemPropertyMapEntry const aJumpEdtFieldPropMap [] =
+ {
+ {u"" UNO_NAME_HINT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_PLACEHOLDER, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_PLACEHOLDER_TYPE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aJumpEdtFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_SCRIPT :
+ {
+ static SfxItemPropertyMapEntry const aScriptFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SCRIPT_TYPE, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_URL_CONTENT, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aScriptFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_NEXT_SET :
+ {
+ static SfxItemPropertyMapEntry const aDBNextSetFieldPropMap [] =
+ {
+ // Note: DATA_BASE_NAME and DATA_BASE_URL
+ // are mapped to the same nMId, because internally we only use
+ // them as DataSource and it does not matter which one it is.
+
+ {u"" UNO_NAME_DATA_BASE_NAME , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_TABLE_NAME , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CONDITION , FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_BASE_URL , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_COMMAND_TYPE, FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDBNextSetFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_NUM_SET :
+ {
+ static SfxItemPropertyMapEntry const aDBNumSetFieldPropMap [] =
+ {
+ // Note: DATA_BASE_NAME and DATA_BASE_URL
+ // are mapped to the same nMId, because internally we only use
+ // them as DataSource and it does not matter which one it is.
+
+ {u"" UNO_NAME_DATA_BASE_NAME , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_TABLE_NAME, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CONDITION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_BASE_URL , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_COMMAND_TYPE, FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SET_NUMBER, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDBNumSetFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_SET_NUM :
+ {
+ static SfxItemPropertyMapEntry const aDBSetNumFieldPropMap [] =
+ {
+ // Note: DATA_BASE_NAME and DATA_BASE_URL
+ // are mapped to the same nMId, because internally we only use
+ // them as DataSource and it does not matter which one it is.
+
+ {u"" UNO_NAME_DATA_BASE_NAME , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_TABLE_NAME , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_BASE_URL , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_COMMAND_TYPE, FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBERING_TYPE, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SET_NUMBER, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_VISIBLE, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDBSetNumFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE :
+ {
+ static SfxItemPropertyMapEntry const aDBFieldPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_FIELD_CODE, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_DATA_BASE_FORMAT,FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_VISIBLE, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDBFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_NAME :
+ {
+ static SfxItemPropertyMapEntry const aDBNameFieldPropMap [] =
+ {
+ // Note: DATA_BASE_NAME and DATA_BASE_URL
+ // are mapped to the same nMId, because internally we only use
+ // them as DataSource and it does not matter which one it is.
+
+ {u"" UNO_NAME_DATA_BASE_NAME , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_TABLE_NAME , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_BASE_URL , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_COMMAND_TYPE, FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_VISIBLE, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDBNameFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCSTAT:
+ {
+ static SfxItemPropertyMapEntry const aDocstatFieldPropMap [] =
+ {
+ {u"" UNO_NAME_NUMBERING_TYPE, FIELD_PROP_USHORT2, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ // {UNO_NAME_STATISTIC_TYPE_ID,FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocstatFieldPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_AUTHOR:
+ {
+ static SfxItemPropertyMapEntry const aDocInfoAuthorPropMap [] =
+ {
+ {u"" UNO_NAME_AUTHOR, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoAuthorPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_DATE_TIME:
+ {
+ static SfxItemPropertyMapEntry const aDocInfoDateTimePropMap [] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATE_TIME_VALUE, FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_IS_DATE, FIELD_PROP_BOOL2, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ {u"" UNO_NAME_NUMBER_FORMAT,FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoDateTimePropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME :
+ {
+ static SfxItemPropertyMapEntry const aDocInfoEditTimePropMap [] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATE_TIME_VALUE, FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_NUMBER_FORMAT,FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoEditTimePropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_MISC:
+ {
+ static SfxItemPropertyMapEntry const aDocInfoStringContentPropMap [] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoStringContentPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM:
+ {
+ static SfxItemPropertyMapEntry const aDocInfoCustomPropMap [] =
+ {
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED_LANGUAGE, FIELD_PROP_BOOL4, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoCustomPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_REVISION :
+ {
+ static SfxItemPropertyMapEntry const aDocInfoRevisionPropMap [] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_REVISION, FIELD_PROP_USHORT1, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_FIXED, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get() , PROPERTY_NONE,0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDocInfoRevisionPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_COMBINED_CHARACTERS:
+ {
+ static SfxItemPropertyMapEntry const aCombinedCharactersPropMap[] =
+ {
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aCombinedCharactersPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_TABLE_FORMULA:
+ {
+ static SfxItemPropertyMapEntry const aTableFormulaPropMap[] =
+ {
+ {u"" UNO_NAME_CURRENT_PRESENTATION, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_SHOW_FORMULA, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBER_FORMAT, FIELD_PROP_FORMAT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTableFormulaPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DUMMY_0:
+ {
+ static SfxItemPropertyMapEntry const aEmptyPropMap [] =
+ {
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aEmptyPropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_USER :
+ {
+ static SfxItemPropertyMapEntry const aUserFieldTypePropMap[] =
+ {
+ {u"" UNO_NAME_DEPENDENT_TEXT_FIELDS, FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_IS_EXPRESSION, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ {u"" UNO_NAME_VALUE, FIELD_PROP_DOUBLE, cppu::UnoType<double>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INSTANCE_NAME, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aUserFieldTypePropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_DDE :
+ {
+ static SfxItemPropertyMapEntry const aDDEFieldTypePropMap[] =
+ {
+ {u"" UNO_NAME_DDE_COMMAND_ELEMENT, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DDE_COMMAND_FILE, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DDE_COMMAND_TYPE, FIELD_PROP_SUBTYPE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DEPENDENT_TEXT_FIELDS, FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_IS_AUTOMATIC_UPDATE, FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INSTANCE_NAME, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_CONTENT, FIELD_PROP_PAR5, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDDEFieldTypePropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_SET_EXP :
+ {
+ static SfxItemPropertyMapEntry const aSetExpFieldTypePropMap[] =
+ {
+ {u"" UNO_NAME_CHAPTER_NUMBERING_LEVEL,FIELD_PROP_SHORT1, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DEPENDENT_TEXT_FIELDS, FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NUMBERING_SEPARATOR, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SUB_TYPE, FIELD_PROP_SUBTYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INSTANCE_NAME, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aSetExpFieldTypePropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_DATABASE :
+ {
+ static SfxItemPropertyMapEntry const aDBFieldTypePropMap [] =
+ {
+ // Note: DATA_BASE_NAME and DATA_BASE_URL
+ // are mapped to the same nMId, because internally we only use
+ // them as DataSource and it does not matter which one it is.
+
+ {u"" UNO_NAME_DATA_BASE_NAME , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_NAME, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ {u"" UNO_NAME_DATA_TABLE_NAME, FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_COLUMN_NAME, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INSTANCE_NAME, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_DATA_BASE_URL , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DATA_COMMAND_TYPE, FIELD_PROP_SHORT1, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_DEPENDENT_TEXT_FIELDS, FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aDBFieldTypePropMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_DUMMY0 :
+ {
+ static SfxItemPropertyMapEntry const aStandardFieldMasterMap[] =
+ {
+ {u"" UNO_NAME_DEPENDENT_TEXT_FIELDS, 0, cppu::UnoType< cppu::UnoSequenceType<css::text::XDependentTextField> >::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_NAME, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INSTANCE_NAME, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aStandardFieldMasterMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_BIBLIOGRAPHY:
+ {
+ static SfxItemPropertyMapEntry const aBibliographyFieldMap[] =
+ {
+ {u"" UNO_NAME_FIELDS , FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(),PROPERTY_NONE, 0},
+ COMMON_FLDTYP_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aBibliographyFieldMap;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY:
+ {
+ static SfxItemPropertyMapEntry const aBibliographyFieldMasterMap[] =
+ {
+ {u"" UNO_NAME_BRACKET_BEFORE , FIELD_PROP_PAR1, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_BRACKET_AFTER , FIELD_PROP_PAR2, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_NUMBER_ENTRIES , FIELD_PROP_BOOL1, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_SORT_BY_POSITION , FIELD_PROP_BOOL2, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_LOCALE, FIELD_PROP_LOCALE, cppu::UnoType<css::lang::Locale>::get() , PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SORT_ALGORITHM, FIELD_PROP_PAR3, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_SORT_KEYS , FIELD_PROP_PROP_SEQ, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValues> >::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_INSTANCE_NAME, FIELD_PROP_PAR4, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aBibliographyFieldMasterMap;
+ }
+ break;
+ case PROPERTY_MAP_TEXT :
+ {
+ static SfxItemPropertyMapEntry const aTextMap[] =
+ {
+ REDLINE_NODE_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTextMap;
+ }
+ break;
+ case PROPERTY_MAP_MAILMERGE :
+ {
+ static SfxItemPropertyMapEntry const aMailMergeMap[] =
+ {
+ { u"" UNO_NAME_SELECTION, WID_SELECTION, cppu::UnoType< cppu::UnoSequenceType<css::uno::Any> >::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_RESULT_SET, WID_RESULT_SET, cppu::UnoType<css::sdbc::XResultSet>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CONNECTION, WID_CONNECTION, cppu::UnoType<css::sdbc::XConnection>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_MODEL, WID_MODEL, cppu::UnoType<css::frame::XModel>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_DATA_SOURCE_NAME, WID_DATA_SOURCE_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DAD_COMMAND, WID_DATA_COMMAND, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_FILTER, WID_FILTER, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DOCUMENT_URL, WID_DOCUMENT_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_OUTPUT_URL, WID_OUTPUT_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DAD_COMMAND_TYPE, WID_DATA_COMMAND_TYPE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_OUTPUT_TYPE, WID_OUTPUT_TYPE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_ESCAPE_PROCESSING, WID_ESCAPE_PROCESSING, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SINGLE_PRINT_JOBS, WID_SINGLE_PRINT_JOBS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_FILE_NAME_FROM_COLUMN, WID_FILE_NAME_FROM_COLUMN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_FILE_NAME_PREFIX, WID_FILE_NAME_PREFIX, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SUBJECT, WID_MAIL_SUBJECT, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_ADDRESS_FROM_COLUMN, WID_ADDRESS_FROM_COLUMN, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEND_AS_HTML, WID_SEND_AS_HTML, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEND_AS_ATTACHMENT, WID_SEND_AS_ATTACHMENT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_MAIL_BODY, WID_MAIL_BODY, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_ATTACHMENT_NAME, WID_ATTACHMENT_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_ATTACHMENT_FILTER, WID_ATTACHMENT_FILTER, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PRINT_OPTIONS, WID_PRINT_OPTIONS, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SAVE_AS_SINGLE_FILE, WID_SAVE_AS_SINGLE_FILE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SAVE_FILTER, WID_SAVE_FILTER, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SAVE_FILTER_OPTIONS, WID_SAVE_FILTER_OPTIONS, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SAVE_FILTER_DATA, WID_SAVE_FILTER_DATA, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_COPIES_TO, WID_COPIES_TO, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_BLIND_COPIES_TO, WID_BLIND_COPIES_TO, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IN_SERVER_PASSWORD, WID_IN_SERVER_PASSWORD, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_OUT_SERVER_PASSWORD, WID_OUT_SERVER_PASSWORD, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aMailMergeMap;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_VIEW :
+ {
+ static SfxItemPropertyMapEntry pTextViewMap[] =
+ {
+ {u"" UNO_NAME_PAGE_COUNT, WID_PAGE_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_LINE_COUNT, WID_LINE_COUNT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_IS_CONSTANT_SPELLCHECK, WID_IS_CONSTANT_SPELLCHECK, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ {u"" UNO_NAME_IS_HIDE_SPELL_MARKS, WID_IS_HIDE_SPELL_MARKS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, // deprecated #i91949
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = pTextViewMap;
+ }
+ break;
+ case PROPERTY_MAP_CHART2_DATA_SEQUENCE :
+ {
+ static SfxItemPropertyMapEntry const aChart2DataSequenceMap[] =
+ {
+ {u"" UNO_NAME_ROLE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aChart2DataSequenceMap;
+ }
+ break;
+ case PROPERTY_MAP_METAFIELD:
+ {
+ static SfxItemPropertyMapEntry const aMetaFieldMap[] =
+ {
+ { u"" UNO_NAME_NUMBER_FORMAT, 0,
+ cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_IS_FIXED_LANGUAGE, 0,
+ cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aMetaFieldMap;
+ }
+ break;
+ case PROPERTY_MAP_TABLE_STYLE:
+ {
+ static SfxItemPropertyMapEntry const aTableStyleMap[] =
+ {
+ { u"" UNO_NAME_TABLE_FIRST_ROW_END_COLUMN, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_TABLE_FIRST_ROW_START_COLUMN, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_TABLE_LAST_ROW_END_COLUMN, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_TABLE_LAST_ROW_START_COLUMN, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DISPLAY_NAME, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aTableStyleMap;
+ }
+ break;
+ case PROPERTY_MAP_CELL_STYLE:
+ {
+ static SfxItemPropertyMapEntry const aCellStyleMap[] =
+ {
+ // SvxBrushItem
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 },
+ // SvxBoxItem
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ // SwFormatVertOrient
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_VERTORIENT_ORIENT },
+ // SvxFrameDirectionItem
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ // SvNumberformat
+ { u"" UNO_NAME_NUMBER_FORMAT, RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(),PropertyAttribute::MAYBEVOID, 0 },
+ // SvxAdjustItem
+ { u"" UNO_NAME_PARA_ADJUST, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::MAYBEVOID, MID_PARA_ADJUST },
+ // SvxColorItem
+ { UNO_NAME_CHAR_COLOR, RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0 },
+ // SvxShadowedItem
+ { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ // SvxContouredItem
+ { u"" UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ // SvxCrossedOutItem
+ { u"" UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::MAYBEVOID, MID_CROSS_OUT },
+ // SvxUnderlineItem
+ { UNO_NAME_CHAR_UNDERLINE, RES_CHRATR_UNDERLINE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE },
+ { u"" UNO_NAME_CHAR_UNDERLINE_COLOR, RES_CHRATR_UNDERLINE,cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR },
+ { u"" UNO_NAME_CHAR_UNDERLINE_HAS_COLOR, RES_CHRATR_UNDERLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR },
+ // standard font
+ // SvxFontHeightItem
+ { UNO_NAME_CHAR_HEIGHT, RES_CHRATR_FONTSIZE, cppu::UnoType<float>::get(),PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS },
+ // SvxWeightItem
+ { UNO_NAME_CHAR_WEIGHT, RES_CHRATR_WEIGHT, cppu::UnoType<float>::get(),PropertyAttribute::MAYBEVOID, MID_WEIGHT },
+ // SvxPostureItem
+ { UNO_NAME_CHAR_POSTURE, RES_CHRATR_POSTURE, cppu::UnoType<css::awt::FontSlant>::get(),PropertyAttribute::MAYBEVOID, MID_POSTURE },
+ // SvxFontItem
+ { u"" UNO_NAME_CHAR_FONT_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME },
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME },
+ { u"" UNO_NAME_CHAR_FONT_FAMILY, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY },
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET },
+ { u"" UNO_NAME_CHAR_FONT_PITCH, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH },
+ // cjk font
+ { u"" UNO_NAME_CHAR_HEIGHT_ASIAN, RES_CHRATR_CJK_FONTSIZE, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_WEIGHT_ASIAN, RES_CHRATR_CJK_WEIGHT, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT },
+ { u"" UNO_NAME_CHAR_POSTURE_ASIAN, RES_CHRATR_CJK_POSTURE, cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE },
+ { u"" UNO_NAME_CHAR_FONT_NAME_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME },
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME },
+ { u"" UNO_NAME_CHAR_FONT_FAMILY_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY },
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET },
+ { u"" UNO_NAME_CHAR_FONT_PITCH_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH },
+ // ctl font
+ { u"" UNO_NAME_CHAR_HEIGHT_COMPLEX, RES_CHRATR_CTL_FONTSIZE, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_WEIGHT_COMPLEX, RES_CHRATR_CTL_WEIGHT, cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT },
+ { u"" UNO_NAME_CHAR_POSTURE_COMPLEX, RES_CHRATR_CTL_POSTURE, cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE },
+ { u"" UNO_NAME_CHAR_FONT_NAME_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME },
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME },
+ { u"" UNO_NAME_CHAR_FONT_FAMILY_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY },
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET },
+ { u"" UNO_NAME_CHAR_FONT_PITCH_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ m_aMapEntriesArr[nPropertyId] = aCellStyleMap;
+ }
+ break;
+ case PROPERTY_MAP_LINEBREAK:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetLineBreakPropertyMap();
+ }
+ break;
+ case PROPERTY_MAP_CONTENTCONTROL:
+ {
+ m_aMapEntriesArr[nPropertyId] = GetContentControlPropertyMap();
+ }
+ break;
+
+ default:
+ OSL_FAIL( "unexpected property map ID" );
+ }
+ }
+ return m_aMapEntriesArr[nPropertyId];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unomap1.cxx b/sw/source/core/unocore/unomap1.cxx
new file mode 100644
index 000000000..145a443be
--- /dev/null
+++ b/sw/source/core/unocore/unomap1.cxx
@@ -0,0 +1,1717 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+
+#include <svx/unomid.hxx>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/drawing/ColorMode.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/RectanglePoint.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/style/BreakType.hpp>
+#include <com/sun/star/style/DropCapFormat.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+#include <com/sun/star/style/LineSpacing.hpp>
+#include <com/sun/star/style/PageStyleLayout.hpp>
+#include <com/sun/star/style/TabStop.hpp>
+#include <com/sun/star/table/BorderLine.hpp>
+#include <com/sun/star/table/ShadowFormat.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/TableBorder2.hpp>
+#include <com/sun/star/table/TableBorderDistances.hpp>
+#include <com/sun/star/table/XCell.hpp>
+#include <com/sun/star/text/GraphicCrop.hpp>
+#include <com/sun/star/text/SectionFileLink.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/XDocumentIndex.hpp>
+#include <com/sun/star/text/XDocumentIndexMark.hpp>
+#include <com/sun/star/text/XFootnote.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/text/XTextContent.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <com/sun/star/text/XTextFrame.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <com/sun/star/text/XTextTable.hpp>
+#include <com/sun/star/util/DateTime.hpp>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unomid.h>
+#include <cmdid.h>
+#include <editeng/memberids.h>
+#include <editeng/unoprnms.hxx>
+#include <svl/itemprop.hxx>
+#include <svx/xdef.hxx>
+#include "unomapproperties.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+SwUnoPropertyMapProvider aSwMapProvider;
+
+SwUnoPropertyMapProvider::SwUnoPropertyMapProvider()
+{
+ for( sal_uInt16 i = 0; i < PROPERTY_MAP_END; i++ )
+ {
+ m_aMapEntriesArr[i] = nullptr;
+ m_aPropertySetArr[i] = nullptr;
+ }
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextCursorPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aCharAndParaMap_Impl[] =
+ {
+ COMPLETE_TEXT_CURSOR_MAP
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aCharAndParaMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetAccessibilityTextAttrPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aAccessibilityTextAttrMap_Impl[] =
+ {
+ COMMON_ACCESSIBILITY_TEXT_ATTRIBUTE
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aAccessibilityTextAttrMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetParagraphPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aParagraphMap_Impl[] =
+ {
+ COMMON_CRSR_PARA_PROPERTIES_2
+ TABSTOPS_MAP_ENTRY
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"" UNO_NAME_CHAR_STYLE_NAME, RES_TXTATR_CHARFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_CHAR_STYLE_NAMES, FN_UNO_CHARFMT_SEQUENCE, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PropertyAttribute::MAYBEVOID, 0},
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to style import/export
+ // Added for paragraph backgrounds, this is for paragraph itself
+ FILL_PROPERTIES_SW
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aParagraphMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetAutoParaStylePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aAutoParaStyleMap [] =
+ {
+ { u"" UNO_NAME_PARA_STYLE_NAME, RES_FRMATR_STYLE_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_PAGE_STYLE_NAME, FN_UNO_PAGE_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_NUMBERING_IS_NUMBER, FN_UNO_IS_NUMBER, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0},
+ { UNO_NAME_NUMBERING_LEVEL, FN_UNO_NUM_LEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_NUMBERING_START_VALUE, FN_UNO_NUM_START_VALUE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS},
+ { u"" UNO_NAME_DOCUMENT_INDEX, FN_UNO_DOCUMENT_INDEX, cppu::UnoType<css::text::XDocumentIndex>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_TEXT_TABLE, FN_UNO_TEXT_TABLE, cppu::UnoType<css::text::XTextTable>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_CELL, FN_UNO_CELL, cppu::UnoType<css::table::XCell>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_TEXT_FRAME, FN_UNO_TEXT_FRAME, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_TEXT_SECTION, FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_PARA_CHAPTER_NUMBERING_LEVEL, FN_UNO_PARA_CHAPTER_NUMBERING_LEVEL,cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_PARA_CONDITIONAL_STYLE_NAME, RES_FRMATR_CONDITIONAL_STYLE_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_PARA_IS_NUMBERING_RESTART, FN_NUMBER_NEWSTART, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ // TODO add RES_PARATR_LIST_AUTOFMT?
+ { u"" UNO_NAME_OUTLINE_LEVEL, RES_PARATR_OUTLINELEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_OUTLINE_CONTENT_VISIBLE, RES_PARATR_GRABBAG, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN
+ TABSTOPS_MAP_ENTRY
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"" UNO_NAME_PARA_AUTO_STYLE_NAME, RES_AUTO_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to style import/export
+ // Added for paragraph backgrounds, this is for Paragraph AutoStyles
+ FILL_PROPERTIES_SW
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aAutoParaStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetCharStylePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aCharStyleMap [] =
+ {
+ { u"" UNO_NAME_CHAR_AUTO_KERNING, RES_CHRATR_AUTOKERN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_BACK_TRANSPARENT, RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_CHAR_HIGHLIGHT, RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_BACK_COLOR },
+ { u"" UNO_NAME_CHAR_CASE_MAP, RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { UNO_NAME_CHAR_COLOR, RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_TRANSPARENCE, RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA},
+ { u"" UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT},
+ { u"" UNO_NAME_CHAR_CROSSED_OUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_ESCAPEMENT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC },
+ { u"" UNO_NAME_CHAR_ESCAPEMENT_HEIGHT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get() , PROPERTY_NONE, MID_ESC_HEIGHT},
+ { u"" UNO_NAME_CHAR_FLASH, RES_CHRATR_BLINK , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_HIDDEN, RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ STANDARD_FONT_PROPERTIES
+ CJK_FONT_PROPERTIES
+ CTL_FONT_PROPERTIES
+ { UNO_NAME_CHAR_UNDERLINE, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},
+ { u"" UNO_NAME_CHAR_UNDERLINE_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},
+ { u"" UNO_NAME_CHAR_UNDERLINE_HAS_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},
+ { u"" UNO_NAME_CHAR_OVERLINE, RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},
+ { u"" UNO_NAME_CHAR_OVERLINE_COLOR, RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},
+ { u"" UNO_NAME_CHAR_OVERLINE_HAS_COLOR, RES_CHRATR_OVERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},
+ { u"" UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_WORD_MODE, RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_PHYSICAL, FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_HIDDEN, FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_STYLE_INTEROP_GRAB_BAG, FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DISPLAY_NAME, FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_CHAR_COMBINE_IS_ON, RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TWOLINES},
+ { u"" UNO_NAME_CHAR_COMBINE_PREFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_START_BRACKET},
+ { u"" UNO_NAME_CHAR_COMBINE_SUFFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_END_BRACKET},
+ { u"" UNO_NAME_CHAR_EMPHASIS, RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS},
+ PROP_DIFF_FONTHEIGHT
+ { u"" UNO_NAME_CHAR_ROTATION, RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ROTATE },
+ { u"" UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE, RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FITTOLINE },
+ { u"" UNO_NAME_CHAR_SCALE_WIDTH, RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CHAR_RELIEF, RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_RELIEF },
+ { u"" UNO_NAME_CHAR_LEFT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_TOP_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_LEFT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_TOP_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_SHADOW_FORMAT, RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_LINK_STYLE, FN_UNO_LINK_STYLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aCharStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetAutoCharStylePropertyMap()
+{
+ // same as PROPERTY_MAP_TEXTPORTION_EXTENSIONS
+ static SfxItemPropertyMapEntry const aAutoCharStyleMap [] =
+ {
+ { u"" UNO_NAME_CHAR_AUTO_KERNING, RES_CHRATR_AUTOKERN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_BACK_TRANSPARENT, RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_CHAR_HIGHLIGHT, RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_BACK_COLOR },
+ { u"" UNO_NAME_CHAR_CASE_MAP, RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { UNO_NAME_CHAR_COLOR, RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_TRANSPARENCE, RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA},
+ { u"" UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT},
+ { u"" UNO_NAME_CHAR_CROSSED_OUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_ESCAPEMENT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC },
+ { u"" UNO_NAME_CHAR_ESCAPEMENT_HEIGHT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get() , PROPERTY_NONE, MID_ESC_HEIGHT},
+ { u"" UNO_NAME_CHAR_FLASH, RES_CHRATR_BLINK , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_HIDDEN, RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ STANDARD_FONT_PROPERTIES
+ CJK_FONT_PROPERTIES
+ CTL_FONT_PROPERTIES
+ { UNO_NAME_CHAR_UNDERLINE, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},
+ { u"" UNO_NAME_CHAR_UNDERLINE_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},
+ { u"" UNO_NAME_CHAR_UNDERLINE_HAS_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},
+ { u"" UNO_NAME_CHAR_OVERLINE, RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},
+ { u"" UNO_NAME_CHAR_OVERLINE_COLOR, RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},
+ { u"" UNO_NAME_CHAR_OVERLINE_HAS_COLOR, RES_CHRATR_OVERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},
+ { u"" UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_WORD_MODE, RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_TEXT_USER_DEFINED_ATTRIBUTES, RES_TXTATR_UNKNOWN_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_PHYSICAL, FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_DISPLAY_NAME, FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_CHAR_COMBINE_IS_ON, RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TWOLINES},
+ { u"" UNO_NAME_CHAR_COMBINE_PREFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_START_BRACKET},
+ { u"" UNO_NAME_CHAR_COMBINE_SUFFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_END_BRACKET},
+ { u"" UNO_NAME_CHAR_EMPHASIS, RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS},
+ { u"" UNO_NAME_CHAR_ROTATION, RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ROTATE },
+ { u"" UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE, RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FITTOLINE },
+ { u"" UNO_NAME_CHAR_SCALE_WIDTH, RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CHAR_RELIEF, RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_RELIEF },
+ { u"" UNO_NAME_CHAR_AUTO_STYLE_NAME, RES_TXTATR_AUTOFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_CHAR_SHADING_VALUE, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE },
+ { u"" UNO_NAME_CHAR_LEFT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_TOP_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_LEFT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_TOP_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_CHAR_SHADOW_FORMAT, RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aAutoCharStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetParaStylePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aParaStyleMap [] =
+ {
+ COMMON_PARA_STYLE_PROPERTIES
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to style import/export
+ // Added for paragraph backgrounds, this is for Paragraph Styles
+ FILL_PROPERTIES_SW
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aParaStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetConditionalParaStylePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aParaStyleMap [] =
+ {
+ COMMON_PARA_STYLE_PROPERTIES
+ { u"" UNO_NAME_PARA_STYLE_CONDITIONS, FN_UNO_PARA_STYLE_CONDITIONS, cppu::UnoType< cppu::UnoSequenceType<css::beans::NamedValue> >::get(), PropertyAttribute::MAYBEVOID, 0},
+
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to style import/export
+ // Added for paragraph backgrounds, this is for Paragraph Styles
+ FILL_PROPERTIES_SW
+
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aParaStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetFrameStylePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aFrameStyleMap [] =
+ {
+ { u"" UNO_NAME_ANCHOR_PAGE_NO, RES_ANCHOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ANCHOR_PAGENUM },
+ { u"" UNO_NAME_ANCHOR_TYPE, RES_ANCHOR, cppu::UnoType<css::text::TextContentAnchorType>::get(), PROPERTY_NONE, MID_ANCHOR_ANCHORTYPE},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_COLOR_R_G_B, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR_R_G_B},
+ { u"" UNO_NAME_BACK_COLOR_TRANSPARENCY, RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_BACK_COLOR_TRANSPARENCY},
+ { u"" UNO_NAME_FRAME_INTEROP_GRAB_BAG, RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+ // { UNO_NAME_CHAIN_NEXT_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_CHAIN_NEXTNAME},
+ // { UNO_NAME_CHAIN_PREV_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_CHAIN_PREVNAME},
+ /*not impl*/ { u"" UNO_NAME_CLIENT_MAP, RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_CLIENTMAP },
+ { u"" UNO_NAME_CONTENT_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_CONTENT },
+ { u"" UNO_NAME_EDIT_IN_READONLY, RES_EDIT_IN_READONLY, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ // #i50322# - add missing map entry for transparency of graphic background
+ { u"" UNO_NAME_BACK_GRAPHIC_TRANSPARENCY, RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENCY},
+ { u"" UNO_NAME_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_HORI_ORIENT, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_ORIENT },
+ { u"" UNO_NAME_HORI_ORIENT_POSITION, RES_HORI_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_HORIORIENT_POSITION|CONVERT_TWIPS },
+ { u"" UNO_NAME_HORI_ORIENT_RELATION, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_RELATION },
+ { u"" UNO_NAME_HYPER_LINK_U_R_L, RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_URL},
+ { u"" UNO_NAME_HYPER_LINK_TARGET, RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_TARGET},
+ { u"" UNO_NAME_HYPER_LINK_NAME, RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_HYPERLINKNAME },
+ { u"" UNO_NAME_OPAQUE, RES_OPAQUE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PAGE_TOGGLE, RES_HORI_ORIENT, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_HORIORIENT_PAGETOGGLE },
+ { u"" UNO_NAME_POSITION_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_POSITION},
+ { u"" UNO_NAME_PRINT, RES_PRINT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS },
+ { u"" UNO_NAME_HEIGHT, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_RELATIVE_HEIGHT, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT },
+ { u"" UNO_NAME_RELATIVE_HEIGHT_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION },
+ { u"" UNO_NAME_RELATIVE_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH },
+ { u"" UNO_NAME_RELATIVE_WIDTH_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION },
+ { u"" UNO_NAME_SIZE_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE },
+ { u"" UNO_NAME_WIDTH_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE },
+ { u"" UNO_NAME_SIZE, RES_FRM_SIZE, cppu::UnoType<css::awt::Size>::get(), PROPERTY_NONE, MID_FRMSIZE_SIZE|CONVERT_TWIPS},
+ { u"" UNO_NAME_IS_SYNC_WIDTH_TO_HEIGHT, RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT },
+ { u"" UNO_NAME_IS_SYNC_HEIGHT_TO_WIDTH, RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH },
+ // { UNO_NAME_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH },
+ { u"" UNO_NAME_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_SHADOW_TRANSPARENCE, RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE},
+ { u"" UNO_NAME_SERVER_MAP, RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_SERVERMAP },
+ { u"" UNO_NAME_SIZE_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_SIZE },
+ // We keep Surround, as we delivered it with 5.1, although it's identical to text::WrapTextMode
+ { u"" UNO_NAME_SURROUND, RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE },
+ { u"" UNO_NAME_TEXT_WRAP, RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE },
+ { u"" UNO_NAME_SURROUND_ANCHORONLY, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_ANCHORONLY },
+ { u"" UNO_NAME_SURROUND_CONTOUR, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR },
+ { u"" UNO_NAME_CONTOUR_OUTSIDE, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE },
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT },
+ { u"" UNO_NAME_VERT_ORIENT_POSITION, RES_VERT_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_VERTORIENT_POSITION|CONVERT_TWIPS },
+ { u"" UNO_NAME_VERT_ORIENT_RELATION, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_RELATION },
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_PHYSICAL, FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_IS_AUTO_UPDATE, FN_UNO_IS_AUTO_UPDATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DISPLAY_NAME, FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ // #i18732#
+ { u"" UNO_NAME_IS_FOLLOWING_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FOLLOW_TEXT_FLOW},
+ // #i28701#
+ { u"" UNO_NAME_WRAP_INFLUENCE_ON_POSITION, RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_WRAP_INFLUENCE},
+ { u"" UNO_NAME_ALLOW_OVERLAP, RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_ALLOW_OVERLAP},
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_HIDDEN, FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_VERT_ADJUST, RES_TEXT_VERT_ADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), PROPERTY_NONE ,0},
+
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to style import/export
+ FILL_PROPERTIES_SW
+
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aFrameStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetPageStylePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aPageStyleMap [] =
+ {
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_GUTTER_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GUTTER_MARGIN | CONVERT_TWIPS},
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_SHADOW_TRANSPARENCE, RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE},
+
+ //UUU use real WhichIDs for Header, no longer use extra-defined WhichIDs which make handling harder as needed.
+ // The implementation will decide if these are part of Header/Footer or PageStyle depending on the SlotName,
+ // more precisely on the first characters. Thus it is necessary that these are 'Header' for the Header slots
+ { u"" UNO_NAME_HEADER_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_HEADER_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_HEADER_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_HEADER_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_HEADER_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_HEADER_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_HEADER_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_HEADER_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_HEADER_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_HEADER_BODY_DISTANCE, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_LO_MARGIN|CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_IS_DYNAMIC_HEIGHT, SID_ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_HEADER_IS_SHARED, SID_ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_HEADER_HEIGHT, SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_SIZE_HEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_HEADER_IS_ON, SID_ATTR_PAGE_ON, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_HEADER_DYNAMIC_SPACING, RES_HEADER_FOOTER_EAT_SPACING, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID ,0 },
+
+
+ { u"" UNO_NAME_FIRST_IS_SHARED, SID_ATTR_PAGE_SHARED_FIRST, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+
+ //UUU use real WhichIDs for Footer, see Header (above) for more infos
+ { u"" UNO_NAME_FOOTER_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_FOOTER_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_FOOTER_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_FOOTER_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_FOOTER_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_FOOTER_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_FOOTER_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_FOOTER_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_FOOTER_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},
+ { u"" UNO_NAME_FOOTER_BODY_DISTANCE, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_UP_MARGIN|CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_IS_DYNAMIC_HEIGHT, SID_ATTR_PAGE_DYNAMIC, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_FOOTER_IS_SHARED, SID_ATTR_PAGE_SHARED, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_FOOTER_HEIGHT, SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_SIZE_HEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTER_IS_ON, SID_ATTR_PAGE_ON, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },
+ { u"" UNO_NAME_FOOTER_DYNAMIC_SPACING, RES_HEADER_FOOTER_EAT_SPACING, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID ,0 },
+
+ { u"" UNO_NAME_IS_LANDSCAPE, SID_ATTR_PAGE, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_PAGE_ORIENTATION },
+ { u"" UNO_NAME_NUMBERING_TYPE, SID_ATTR_PAGE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_PAGE_NUMTYPE },
+ { u"" UNO_NAME_PAGE_STYLE_LAYOUT, SID_ATTR_PAGE, cppu::UnoType<css::style::PageStyleLayout>::get(), PROPERTY_NONE ,MID_PAGE_LAYOUT },
+ { u"" UNO_NAME_PRINTER_PAPER_TRAY, RES_PAPER_BIN, cppu::UnoType<OUString>::get(), PROPERTY_NONE , 0 },
+// { UNO_NAME_REGISTER_MODE_ACTIVE, SID_SWREGISTER_MODE, cppu::UnoType<bool>::get(), PROPERTY_NONE , 0 },
+ { u"" UNO_NAME_REGISTER_PARAGRAPH_STYLE, SID_SWREGISTER_COLLECTION, cppu::UnoType<OUString>::get(), PROPERTY_NONE , 0 },
+ { u"" UNO_NAME_SIZE, SID_ATTR_PAGE_SIZE, cppu::UnoType<css::awt::Size>::get(), PROPERTY_NONE, MID_SIZE_SIZE|CONVERT_TWIPS},
+ { u"" UNO_NAME_WIDTH, SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_SIZE_WIDTH|CONVERT_TWIPS},
+ { u"" UNO_NAME_HEIGHT, SID_ATTR_PAGE_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_SIZE_HEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_TEXT_VERT_ADJUST, RES_TEXT_VERT_ADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_HEADER_TEXT, FN_UNO_HEADER, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_HEADER_TEXT_LEFT, FN_UNO_HEADER_LEFT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_HEADER_TEXT_RIGHT, FN_UNO_HEADER_RIGHT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_HEADER_TEXT_FIRST, FN_UNO_HEADER_FIRST, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_FOOTER_TEXT, FN_UNO_FOOTER, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_FOOTER_TEXT_LEFT, FN_UNO_FOOTER_LEFT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_FOOTER_TEXT_RIGHT, FN_UNO_FOOTER_RIGHT, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_FOOTER_TEXT_FIRST, FN_UNO_FOOTER_FIRST, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_FOLLOW_STYLE, FN_UNO_FOLLOW_STYLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_PHYSICAL, FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_DISPLAY_NAME, FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_FOOTNOTE_HEIGHT, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_FTN_HEIGHT|CONVERT_TWIPS},
+ { u"" UNO_NAME_FOOTNOTE_LINE_WEIGHT, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_LINE_WEIGHT|CONVERT_TWIPS},
+ { u"" UNO_NAME_FOOTNOTE_LINE_COLOR, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_LINE_COLOR},
+ { u"" UNO_NAME_FOOTNOTE_LINE_STYLE, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE , MID_FTN_LINE_STYLE},
+ { u"" UNO_NAME_FOOTNOTE_LINE_RELATIVE_WIDTH, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE , MID_LINE_RELWIDTH },
+ { u"" UNO_NAME_FOOTNOTE_LINE_ADJUST, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_LINE_ADJUST },
+ { u"" UNO_NAME_FOOTNOTE_LINE_TEXT_DISTANCE, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_LINE_TEXT_DIST |CONVERT_TWIPS },
+ { u"" UNO_NAME_FOOTNOTE_LINE_DISTANCE, FN_PARAM_FTN_INFO, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_LINE_FOOTNOTE_DIST|CONVERT_TWIPS},
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ // writing grid
+ { u"" UNO_NAME_GRID_COLOR, RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_COLOR},
+ { u"" UNO_NAME_GRID_LINES, RES_TEXTGRID, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_GRID_LINES},
+ { u"" UNO_NAME_GRID_BASE_HEIGHT, RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_BASEHEIGHT|CONVERT_TWIPS},
+ { u"" UNO_NAME_GRID_RUBY_HEIGHT, RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_RUBYHEIGHT|CONVERT_TWIPS},
+ { u"" UNO_NAME_GRID_MODE, RES_TEXTGRID, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_GRID_TYPE},
+ { u"" UNO_NAME_GRID_RUBY_BELOW, RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_RUBY_BELOW},
+ { u"" UNO_NAME_GRID_PRINT, RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_PRINT},
+ { u"" UNO_NAME_GRID_DISPLAY, RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_DISPLAY},
+ { u"" UNO_NAME_GRID_BASE_WIDTH, RES_TEXTGRID, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_GRID_BASEWIDTH|CONVERT_TWIPS},
+ { u"" UNO_NAME_GRID_SNAP_TO_CHARS, RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_SNAPTOCHARS},
+ { u"" UNO_NAME_GRID_STANDARD_PAGE_MODE, RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_STANDARD_MODE},
+ { u"" UNO_NAME_HIDDEN, FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to style import/export
+ FILL_PROPERTIES_SW
+ { u"BackgroundFullSize", RES_BACKGROUND_FULL_SIZE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"RtlGutter", RES_RTL_GUTTER, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+
+ // Added DrawingLayer FillStyle Properties for Header. These need an own unique name,
+ // but reuse the same WhichIDs as the regular fill. The implementation will decide to which
+ // group of fill properties it belongs based on the start of the name (was already done in
+ // the implementation partially), thus all SlotNames *have* to start with 'Header'
+ { u"" UNO_NAME_HEADER_FILLBMP_LOGICAL_SIZE, XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_OFFSET_X, XATTR_FILLBMP_TILEOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_OFFSET_Y, XATTR_FILLBMP_TILEOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_POSITION_OFFSET_X, XATTR_FILLBMP_POSOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_POSITION_OFFSET_Y, XATTR_FILLBMP_POSOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_RECTANGLE_POINT, XATTR_FILLBMP_POS, cppu::UnoType<css::drawing::RectanglePoint>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_SIZE_X, XATTR_FILLBMP_SIZEX, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"" UNO_NAME_HEADER_FILLBMP_SIZE_Y, XATTR_FILLBMP_SIZEY, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"" UNO_NAME_HEADER_FILLBMP_STRETCH, XATTR_FILLBMP_STRETCH, cppu::UnoType<bool>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_TILE, XATTR_FILLBMP_TILE, cppu::UnoType<bool>::get() , 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBMP_MODE, OWN_ATTR_FILLBMP_MODE, cppu::UnoType<css::drawing::BitmapMode>::get(), 0, 0},
+ { u"" UNO_NAME_HEADER_FILLCOLOR, XATTR_FILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBACKGROUND, XATTR_FILLBACKGROUND, cppu::UnoType<bool>::get(), 0, 0},
+ { u"" UNO_NAME_HEADER_FILLBITMAP, XATTR_FILLBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), 0, MID_BITMAP},
+ { u"" UNO_NAME_HEADER_FILLBITMAPNAME, XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_HEADER_FILLGRADIENTSTEPCOUNT, XATTR_GRADIENTSTEPCOUNT, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_HEADER_FILLGRADIENT, XATTR_FILLGRADIENT, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT},
+ { u"" UNO_NAME_HEADER_FILLGRADIENTNAME, XATTR_FILLGRADIENT, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_HEADER_FILLHATCH, XATTR_FILLHATCH, cppu::UnoType<css::drawing::Hatch>::get(), 0, MID_FILLHATCH},
+ { u"" UNO_NAME_HEADER_FILLHATCHNAME, XATTR_FILLHATCH, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_HEADER_FILLSTYLE, XATTR_FILLSTYLE, cppu::UnoType<css::drawing::FillStyle>::get(), 0, 0},
+ { u"" UNO_NAME_HEADER_FILL_TRANSPARENCE, XATTR_FILLTRANSPARENCE, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_HEADER_FILLTRANSPARENCEGRADIENT, XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT},
+ { u"" UNO_NAME_HEADER_FILLTRANSPARENCEGRADIENTNAME, XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_HEADER_FILLCOLOR_2, XATTR_SECONDARYFILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0},
+
+ // Added DrawingLayer FillStyle Properties for Footer, similar as for Header (see there)
+ { u"" UNO_NAME_FOOTER_FILLBMP_LOGICAL_SIZE, XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_OFFSET_X, XATTR_FILLBMP_TILEOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_OFFSET_Y, XATTR_FILLBMP_TILEOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_POSITION_OFFSET_X, XATTR_FILLBMP_POSOFFSETX, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_POSITION_OFFSET_Y, XATTR_FILLBMP_POSOFFSETY, cppu::UnoType<sal_Int32>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_RECTANGLE_POINT, XATTR_FILLBMP_POS, cppu::UnoType<css::drawing::RectanglePoint>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_SIZE_X, XATTR_FILLBMP_SIZEX, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"" UNO_NAME_FOOTER_FILLBMP_SIZE_Y, XATTR_FILLBMP_SIZEY, cppu::UnoType<sal_Int32>::get() , 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"" UNO_NAME_FOOTER_FILLBMP_STRETCH, XATTR_FILLBMP_STRETCH, cppu::UnoType<bool>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_TILE, XATTR_FILLBMP_TILE, cppu::UnoType<bool>::get() , 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBMP_MODE, OWN_ATTR_FILLBMP_MODE, cppu::UnoType<css::drawing::BitmapMode>::get(), 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLCOLOR, XATTR_FILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBACKGROUND, XATTR_FILLBACKGROUND, cppu::UnoType<bool>::get(), 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLBITMAP, XATTR_FILLBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), 0, MID_BITMAP},
+ { u"" UNO_NAME_FOOTER_FILLBITMAPNAME, XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_FOOTER_FILLGRADIENTSTEPCOUNT, XATTR_GRADIENTSTEPCOUNT, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLGRADIENT, XATTR_FILLGRADIENT, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT},
+ { u"" UNO_NAME_FOOTER_FILLGRADIENTNAME, XATTR_FILLGRADIENT, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_FOOTER_FILLHATCH, XATTR_FILLHATCH, cppu::UnoType<css::drawing::Hatch>::get(), 0, MID_FILLHATCH},
+ { u"" UNO_NAME_FOOTER_FILLHATCHNAME, XATTR_FILLHATCH, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_FOOTER_FILLSTYLE, XATTR_FILLSTYLE, cppu::UnoType<css::drawing::FillStyle>::get(), 0, 0},
+ { u"" UNO_NAME_FOOTER_FILL_TRANSPARENCE, XATTR_FILLTRANSPARENCE, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_FOOTER_FILLTRANSPARENCEGRADIENT, XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT},
+ { u"" UNO_NAME_FOOTER_FILLTRANSPARENCEGRADIENTNAME, XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<OUString>::get(), 0, MID_NAME },
+ { u"" UNO_NAME_FOOTER_FILLCOLOR_2, XATTR_SECONDARYFILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0},
+
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aPageStyleMap;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTablePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aTablePropertyMap_Impl[] =
+ {
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE,MID_BACK_COLOR },
+ { u"" UNO_NAME_BREAK_TYPE, RES_BREAK, cppu::UnoType<css::style::BreakType>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_HORI_ORIENT, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_ORIENT },
+ { u"" UNO_NAME_KEEP_TOGETHER, RES_KEEP, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SPLIT, RES_LAYOUT_SPLIT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PAGE_NUMBER_OFFSET, RES_PAGEDESC, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGENUMOFFSET},
+ { u"" UNO_NAME_PAGE_DESC_NAME, RES_PAGEDESC, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0xbf},
+ { u"" UNO_NAME_RELATIVE_WIDTH, FN_TABLE_RELATIVE_WIDTH,cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0xbf },
+ { u"" UNO_NAME_REPEAT_HEADLINE, FN_TABLE_HEADLINE_REPEAT,cppu::UnoType<bool>::get(), PROPERTY_NONE, 0xbf},
+ { u"" UNO_NAME_HEADER_ROW_COUNT, FN_TABLE_HEADLINE_COUNT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0xbf},
+ { u"" UNO_NAME_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SHADOW_TRANSPARENCE, RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE},
+ { u"" UNO_NAME_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_WIDTH, FN_TABLE_WIDTH, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, 0xbf},
+ { u"" UNO_NAME_IS_WIDTH_RELATIVE, FN_TABLE_IS_RELATIVE_WIDTH, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0xbf},
+ { u"" UNO_NAME_CHART_ROW_AS_LABEL, FN_UNO_RANGE_ROW_LABEL, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHART_COLUMN_AS_LABEL, FN_UNO_RANGE_COL_LABEL, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TABLE_BORDER, FN_UNO_TABLE_BORDER, cppu::UnoType<css::table::TableBorder>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS },
+ { u"" UNO_NAME_TABLE_BORDER2, FN_UNO_TABLE_BORDER2, cppu::UnoType<css::table::TableBorder2>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS },
+ { u"" UNO_NAME_TABLE_BORDER_DISTANCES, FN_UNO_TABLE_BORDER_DISTANCES, cppu::UnoType<css::table::TableBorderDistances>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS },
+ { u"" UNO_NAME_TABLE_COLUMN_SEPARATORS, FN_UNO_TABLE_COLUMN_SEPARATORS, cppu::UnoType< cppu::UnoSequenceType<css::text::TableColumnSeparator> >::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_TABLE_COLUMN_RELATIVE_SUM, FN_UNO_TABLE_COLUMN_RELATIVE_SUM, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::READONLY, 0 },
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"" UNO_LINK_DISPLAY_NAME, FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf},
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_TEXT_SECTION, FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_TABLE_NAME, FN_UNO_TABLE_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_PAGE_STYLE_NAME, RES_PAGEDESC, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TABLE_TEMPLATE_NAME, FN_UNO_TABLE_TEMPLATE_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ // #i29550#
+ { u"" UNO_NAME_COLLAPSING_BORDERS, RES_COLLAPSING_BORDERS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ REDLINE_NODE_PROPERTIES
+ { u"" UNO_NAME_TABLE_INTEROP_GRAB_BAG, RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aTablePropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetRangePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aRangePropertyMap_Impl[] =
+ {
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN_01
+ TABSTOPS_MAP_ENTRY
+ { u"" UNO_NAME_BACK_COLOR, FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_TRANSPARENT, FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_NUMBER_FORMAT, RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID ,0 },
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT },
+ { u"" UNO_NAME_CHART_ROW_AS_LABEL, FN_UNO_RANGE_ROW_LABEL, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_CHART_COLUMN_AS_LABEL, FN_UNO_RANGE_COL_LABEL, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aRangePropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetSectionPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aSectionPropertyMap_Impl[] =
+ {
+ { u"" UNO_NAME_CONDITION, WID_SECT_CONDITION, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DDE_COMMAND_FILE, WID_SECT_DDE_TYPE, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DDE_COMMAND_TYPE, WID_SECT_DDE_FILE, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DDE_COMMAND_ELEMENT, WID_SECT_DDE_ELEMENT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_AUTOMATIC_UPDATE, WID_SECT_DDE_AUTOUPDATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_FILE_LINK, WID_SECT_LINK , cppu::UnoType<css::text::SectionFileLink>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_VISIBLE, WID_SECT_VISIBLE , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_PROTECTED, WID_SECT_PROTECTED, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_EDIT_IN_READONLY, WID_SECT_EDIT_IN_READONLY, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LINK_REGION, WID_SECT_REGION , cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_LINK_DISPLAY_NAME, FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf},
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_FOOTNOTE_IS_COLLECT_AT_TEXT_END, RES_FTN_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_COLLECT },
+ { u"" UNO_NAME_FOOTNOTE_IS_RESTART_NUMBERING, RES_FTN_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_RESTART_NUM },
+ { u"" UNO_NAME_FOOTNOTE_RESTART_NUMBERING_AT, RES_FTN_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_NUM_START_AT},
+ { u"" UNO_NAME_FOOTNOTE_IS_OWN_NUMBERING, RES_FTN_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_OWN_NUM },
+ { u"" UNO_NAME_FOOTNOTE_NUMBERING_TYPE, RES_FTN_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_NUM_TYPE },
+ { u"" UNO_NAME_FOOTNOTE_NUMBERING_PREFIX, RES_FTN_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_PREFIX },
+ { u"" UNO_NAME_FOOTNOTE_NUMBERING_SUFFIX, RES_FTN_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_SUFFIX },
+ { u"" UNO_NAME_ENDNOTE_IS_COLLECT_AT_TEXT_END, RES_END_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_COLLECT },
+ { u"" UNO_NAME_ENDNOTE_IS_RESTART_NUMBERING, RES_END_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_RESTART_NUM },
+ { u"" UNO_NAME_ENDNOTE_RESTART_NUMBERING_AT, RES_END_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_NUM_START_AT },
+ { u"" UNO_NAME_ENDNOTE_IS_OWN_NUMBERING, RES_END_AT_TXTEND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_OWN_NUM },
+ { u"" UNO_NAME_ENDNOTE_NUMBERING_TYPE, RES_END_AT_TXTEND, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_NUM_TYPE },
+ { u"" UNO_NAME_ENDNOTE_NUMBERING_PREFIX, RES_END_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_PREFIX },
+ { u"" UNO_NAME_ENDNOTE_NUMBERING_SUFFIX, RES_END_AT_TXTEND, cppu::UnoType<OUString>::get() , PROPERTY_NONE, MID_SUFFIX },
+ { u"" UNO_NAME_DOCUMENT_INDEX, WID_SECT_DOCUMENT_INDEX, cppu::UnoType<css::text::XDocumentIndex>::get(), PropertyAttribute::READONLY | PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_GLOBAL_DOCUMENT_SECTION, WID_SECT_IS_GLOBAL_DOC_SECTION, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0 },
+ { u"" UNO_NAME_PROTECTION_KEY, WID_SECT_PASSWORD, cppu::UnoType< cppu::UnoSequenceType<sal_Int8> >::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DONT_BALANCE_TEXT_COLUMNS, RES_COLUMNBALANCE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ COMMON_TEXT_CONTENT_PROPERTIES
+ REDLINE_NODE_PROPERTIES
+ { u"" UNO_NAME_IS_CURRENTLY_VISIBLE, WID_SECT_CURRENTLY_VISIBLE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_SECT_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_L_MARGIN|CONVERT_TWIPS},
+ { u"" UNO_NAME_SECT_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aSectionPropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetFramePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aFramePropertyMap_Impl[] =
+ { //
+ // TODO: We should consider completely removing SvxBrushItem() stuff
+ // add support for XATTR_FILL_FIRST, XATTR_FILL_LAST
+ // COMMON_FRAME_PROPERTIES currently hosts the RES_BACKGROUND entries from SvxBrushItem
+ COMMON_FRAME_PROPERTIES
+ REDLINE_NODE_PROPERTIES
+ { u"" UNO_NAME_CHAIN_NEXT_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_NEXTNAME},
+ { u"" UNO_NAME_CHAIN_PREV_NAME, RES_CHAIN, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_CHAIN_PREVNAME},
+ /*not impl*/ { u"" UNO_NAME_CLIENT_MAP, RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_CLIENTMAP },
+ { u"" UNO_NAME_EDIT_IN_READONLY, RES_EDIT_IN_READONLY, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_COLUMNS, RES_COL, cppu::UnoType<css::text::XTextColumns>::get(), PROPERTY_NONE, MID_COLUMNS},
+ //next elements are part of the service description
+ { u"" UNO_NAME_FRAME_HEIGHT_ABSOLUTE, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS },
+ { u"" UNO_NAME_FRAME_HEIGHT_PERCENT, RES_FRM_SIZE, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT },
+ { u"" UNO_NAME_FRAME_ISAUTOMATIC_HEIGHT, RES_FRM_SIZE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FRMSIZE_IS_AUTO_HEIGHT },
+ { u"" UNO_NAME_FRAME_WIDTH_ABSOLUTE, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS },
+ { u"" UNO_NAME_FRAME_WIDTH_PERCENT, RES_FRM_SIZE, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH },
+ { u"" UNO_NAME_SIZE_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_SIZE_TYPE },
+ { u"" UNO_NAME_WIDTH_TYPE, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH_TYPE },
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to FlyFrame import/export
+ FILL_PROPERTIES_SW
+
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aFramePropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetGraphicPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aGraphicPropertyMap_Impl[] =
+ {
+ // TODO: We should consider completely removing SvxBrushItem() stuff
+ // add support for XATTR_FILL_FIRST, XATTR_FILL_LAST
+ // COMMON_FRAME_PROPERTIES currently hosts the RES_BACKGROUND entries from SvxBrushItem
+ COMMON_FRAME_PROPERTIES
+ { u"" UNO_NAME_SURROUND_CONTOUR, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR },
+ { u"" UNO_NAME_CONTOUR_OUTSIDE, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE },
+ { u"" UNO_NAME_GRAPHIC_CROP, RES_GRFATR_CROPGRF, cppu::UnoType<css::text::GraphicCrop>::get(), PROPERTY_NONE, CONVERT_TWIPS },
+ { u"" UNO_NAME_HORI_MIRRORED_ON_EVEN_PAGES, RES_GRFATR_MIRRORGRF, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_MIRROR_HORZ_EVEN_PAGES },
+ { u"" UNO_NAME_HORI_MIRRORED_ON_ODD_PAGES, RES_GRFATR_MIRRORGRF, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_MIRROR_HORZ_ODD_PAGES },
+ { u"" UNO_NAME_VERT_MIRRORED, RES_GRFATR_MIRRORGRF, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_MIRROR_VERT },
+ { u"" UNO_NAME_REPLACEMENT_GRAPHIC, FN_UNO_REPLACEMENT_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { u"" UNO_NAME_GRAPHIC_FILTER, FN_UNO_GRAPHIC_FILTER, cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"" UNO_NAME_GRAPHIC, FN_UNO_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { u"" UNO_NAME_GRAPHIC_URL, FN_UNO_GRAPHIC_URL, cppu::UnoType<css::uno::Any>::get(), 0, 0 },
+ { u"" UNO_NAME_TRANSFORMED_GRAPHIC, FN_UNO_TRANSFORMED_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { u"" UNO_NAME_GRAPHIC_PREVIEW, FN_UNO_GRAPHIC_PREVIEW, cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { u"" UNO_NAME_ACTUAL_SIZE, FN_UNO_ACTUAL_SIZE, cppu::UnoType<css::awt::Size>::get(), PropertyAttribute::READONLY, CONVERT_TWIPS},
+ { u"" UNO_NAME_CONTOUR_POLY_POLYGON, FN_PARAM_CONTOUR_PP, cppu::UnoType<css::drawing::PointSequenceSequence>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_PIXEL_CONTOUR, FN_UNO_IS_PIXEL_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_IS_AUTOMATIC_CONTOUR, FN_UNO_IS_AUTOMATIC_CONTOUR , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_GRAPHIC_ROTATION, RES_GRFATR_ROTATION, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_ADJUST_LUMINANCE, RES_GRFATR_LUMINANCE, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_ADJUST_CONTRAST, RES_GRFATR_CONTRAST, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_ADJUST_RED, RES_GRFATR_CHANNELR, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_ADJUST_GREEN, RES_GRFATR_CHANNELG, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_ADJUST_BLUE, RES_GRFATR_CHANNELB, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"" UNO_NAME_GAMMA, RES_GRFATR_GAMMA, cppu::UnoType<double>::get(), 0, 0},
+ { u"" UNO_NAME_GRAPHIC_IS_INVERTED, RES_GRFATR_INVERT, cppu::UnoType<bool>::get(), 0, 0},
+ { u"" UNO_NAME_TRANSPARENCY, RES_GRFATR_TRANSPARENCY, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_GRAPHIC_COLOR_MODE, RES_GRFATR_DRAWMODE, cppu::UnoType<css::drawing::ColorMode>::get(), 0, 0},
+
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to Writer GraphicObject import/export
+ FILL_PROPERTIES_SW
+
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aGraphicPropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetEmbeddedPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aEmbeddedPropertyMap_Impl[] =
+ { //
+ // TODO: We should consider completely removing SvxBrushItem() stuff
+ // add support for XATTR_FILL_FIRST, XATTR_FILL_LAST
+ // COMMON_FRAME_PROPERTIES currently hosts the RES_BACKGROUND entries from SvxBrushItem
+ COMMON_FRAME_PROPERTIES
+ { u"" UNO_NAME_SURROUND_CONTOUR, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUR },
+ { u"" UNO_NAME_CONTOUR_OUTSIDE, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_CONTOUROUTSIDE},
+ { u"" UNO_NAME_CONTOUR_POLY_POLYGON, FN_PARAM_CONTOUR_PP, cppu::UnoType<css::drawing::PointSequenceSequence>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_IS_PIXEL_CONTOUR, FN_UNO_IS_PIXEL_CONTOUR, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_IS_AUTOMATIC_CONTOUR, FN_UNO_IS_AUTOMATIC_CONTOUR , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CLSID, FN_UNO_CLSID, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_STREAM_NAME, FN_UNO_STREAM_NAME, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_MODEL, FN_UNO_MODEL, cppu::UnoType<css::frame::XModel>::get(), PropertyAttribute::READONLY|PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_GRAPHIC_URL, FN_UNO_REPLACEMENT_GRAPHIC_URL, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_GRAPHIC, FN_UNO_REPLACEMENT_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_COMPONENT,FN_UNO_COMPONENT, cppu::UnoType<css::lang::XComponent>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_EMBEDDED_OBJECT,FN_EMBEDDED_OBJECT, cppu::UnoType<css::embed::XEmbeddedObject>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DRAW_ASPECT,FN_UNO_DRAW_ASPECT, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_VISIBLE_AREA_WIDTH,FN_UNO_VISIBLE_AREA_WIDTH, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_VISIBLE_AREA_HEIGHT,FN_UNO_VISIBLE_AREA_HEIGHT, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ // added FillProperties for SW, same as FILL_PROPERTIES in svx
+ // but need own defines in Writer due to later association of strings
+ // and uno types (see loop at end of this method and definition of SW_PROP_NMID)
+ // This entry is for adding that properties to OLE/EmbeddedObject import/export
+ FILL_PROPERTIES_SW
+
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aEmbeddedPropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetIndexMarkPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aIdxMarkMap_Impl[] =
+ {
+ { u"" UNO_NAME_ALTERNATIVE_TEXT, WID_ALT_TEXT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PRIMARY_KEY, WID_PRIMARY_KEY, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SECONDARY_KEY, WID_SECONDARY_KEY, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_TEXT_READING, WID_TEXT_READING, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PRIMARY_KEY_READING, WID_PRIMARY_KEY_READING, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SECONDARY_KEY_READING, WID_SECONDARY_KEY_READING, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_MAIN_ENTRY, WID_MAIN_ENTRY, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aIdxMarkMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetContentMarkPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aContentMarkMap_Impl[] =
+ {
+ { u"" UNO_NAME_ALTERNATIVE_TEXT, WID_ALT_TEXT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL, WID_LEVEL , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aContentMarkMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetUserMarkPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aUserMarkMap_Impl[] =
+ {
+ { u"" UNO_NAME_ALTERNATIVE_TEXT, WID_ALT_TEXT, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_LEVEL, WID_LEVEL , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_USER_INDEX_NAME, WID_USER_IDX_NAME, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aUserMarkMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextTableCursorPropertyMap()
+{
+ // The PropertySet corresponds to the Range without Chart properties
+ static SfxItemPropertyMapEntry const aTableCursorPropertyMap_Impl [] =
+ {
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN_01
+ TABSTOPS_MAP_ENTRY
+
+ // attributes from PROPERTY_MAP_TABLE_CELL:
+ { u"" UNO_NAME_BACK_COLOR, FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE , MID_BACK_COLOR },
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC },
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION},
+ { u"" UNO_NAME_NUMBER_FORMAT, RES_BOXATR_FORMAT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID ,0 },
+ { u"" UNO_NAME_BACK_TRANSPARENT, FN_UNO_TABLE_CELL_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE , MID_GRAPHIC_TRANSPARENT },
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },
+ { u"" UNO_NAME_TEXT_SECTION, FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_IS_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), 0, MID_PROTECT_CONTENT},
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aTableCursorPropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetBookmarkPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aBookmarkPropertyMap_Impl [] =
+ {
+ { u"" UNO_LINK_DISPLAY_NAME, FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf},
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"" UNO_NAME_BOOKMARK_HIDDEN, FN_BOOKMARK_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_BOOKMARK_CONDITION, FN_BOOKMARK_CONDITION, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aBookmarkPropertyMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetParagraphExtensionsPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aParagraphExtensionsMap_Impl[] =
+ {
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aParagraphExtensionsMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextPortionExtensionPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aTextPortionExtensionMap_Impl[] =
+ {
+ COMPLETE_TEXT_CURSOR_MAP
+ {u"" UNO_NAME_BOOKMARK, FN_UNO_BOOKMARK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ {u"" UNO_NAME_CONTROL_CHARACTER, FN_UNO_CONTROL_CHARACTER, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, MID_HYPHEN_MIN_LEAD },
+ {u"" UNO_NAME_IS_COLLAPSED, FN_UNO_IS_COLLAPSED, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
+ {u"" UNO_NAME_IS_START, FN_UNO_IS_START, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
+ //REDLINE_PROPERTIES
+ {u"" UNO_NAME_TEXT_PORTION_TYPE, FN_UNO_TEXT_PORTION_TYPE, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_META, FN_UNO_META, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
+ { u"" UNO_NAME_LINEBREAK, FN_UNO_LINEBREAK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ { u"" UNO_NAME_CONTENT_CONTROL, FN_UNO_CONTENT_CONTROL, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aTextPortionExtensionMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetFootnotePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aFootnoteMap_Impl[] =
+ {
+ {u"" UNO_NAME_REFERENCE_ID, 0, cppu::UnoType<sal_Int16>::get(),PropertyAttribute::READONLY|PropertyAttribute::MAYBEVOID, 0},
+ COMMON_TEXT_CONTENT_PROPERTIES
+ REDLINE_NODE_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aFootnoteMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetLineBreakPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aLineBreakMap_Impl[] =
+ {
+ { u"" UNO_NAME_CLEAR, 0, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ COMMON_TEXT_CONTENT_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aLineBreakMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetContentControlPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aContentControlMap_Impl[] =
+ {
+ { u"" UNO_NAME_SHOWING_PLACE_HOLDER, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CHECKBOX, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CHECKED, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CHECKED_STATE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_UNCHECKED_STATE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_LIST_ITEMS, 0, cppu::UnoType<uno::Sequence<uno::Sequence<beans::PropertyValue>>>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_PICTURE, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DATE, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DATE_FORMAT, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DATE_LANGUAGE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_CURRENT_DATE, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_PLACEHOLDER_DOC_PART, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DATA_BINDING_PREFIX_MAPPINGS, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DATA_BINDING_XPATH, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_DATA_BINDING_STORE_ITEM_ID, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"" UNO_NAME_COLOR, 0, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aContentControlMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetRedlinePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aRedlineMap_Impl[] =
+ {
+ REDLINE_PROPERTIES(PropertyAttribute::READONLY)
+ REDLINE_NODE_PROPERTIES
+ {u"" UNO_NAME_REDLINE_START, 0, cppu::UnoType<css::uno::XInterface>::get(), PropertyAttribute::READONLY, 0},
+ {u"" UNO_NAME_REDLINE_END, 0, cppu::UnoType<css::uno::XInterface>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aRedlineMap_Impl;
+}
+
+SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetTextDefaultPropertyMap()
+{
+ static SfxItemPropertyMapEntry aTextDefaultMap_Impl[] =
+ {
+ { u"" UNO_NAME_TAB_STOP_DISTANCE, RES_PARATR_TABSTOP, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_STD_TAB | CONVERT_TWIPS},
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN
+ COMMON_HYPERLINK_PROPERTIES
+ { u"" UNO_NAME_CHAR_STYLE_NAME, RES_TXTATR_CHARFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+ { u"" UNO_NAME_IS_SPLIT_ALLOWED, RES_ROW_SPLIT, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0},
+ // #i29550#
+ { u"" UNO_NAME_COLLAPSING_BORDERS, RES_COLLAPSING_BORDERS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+
+ //text grid enhancement for better CJK support. 2007-04-01
+ //just export the default page mode property, other properties are not handled in this version
+ { u"" UNO_NAME_GRID_STANDARD_PAGE_MODE, RES_TEXTGRID, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_GRID_STANDARD_MODE},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aTextDefaultMap_Impl;
+}
+
+const SfxItemPropertyMapEntry* SwUnoPropertyMapProvider::GetRedlinePortionPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aRedlinePortionMap_Impl[] =
+ {
+ COMPLETE_TEXT_CURSOR_MAP
+ {u"" UNO_NAME_BOOKMARK, FN_UNO_BOOKMARK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },
+ {u"" UNO_NAME_CONTROL_CHARACTER, FN_UNO_CONTROL_CHARACTER, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, MID_HYPHEN_MIN_LEAD },
+ {u"" UNO_NAME_IS_COLLAPSED, FN_UNO_IS_COLLAPSED, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
+ {u"" UNO_NAME_IS_START, FN_UNO_IS_START, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },
+ REDLINE_PROPERTIES(0)
+ {u"" UNO_NAME_TEXT_PORTION_TYPE, FN_UNO_TEXT_PORTION_TYPE, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aRedlinePortionMap_Impl;
+}
+
+const SfxItemPropertySet* SwUnoPropertyMapProvider::GetPropertySet( sal_uInt16 nPropertyId)
+{
+ if( !m_aPropertySetArr[nPropertyId] )
+ {
+ const SfxItemPropertyMapEntry* pEntries = GetPropertyMapEntries(nPropertyId);
+ switch( nPropertyId )
+ {
+ case PROPERTY_MAP_TEXT_CURSOR:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_CURSOR(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_CURSOR;
+ }
+ break;
+ case PROPERTY_MAP_CHAR_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CHAR_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CHAR_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_PARA_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_PARA_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_PARA_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_FRAME_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FRAME_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FRAME_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_PAGE_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_PAGE_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_PAGE_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_NUM_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_NUM_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_NUM_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_SECTION:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_SECTION(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_SECTION;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_TABLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_TABLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_TABLE;
+ }
+ break;
+ case PROPERTY_MAP_TABLE_CELL:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TABLE_CELL(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TABLE_CELL;
+ }
+ break;
+ case PROPERTY_MAP_TABLE_RANGE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TABLE_RANGE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TABLE_RANGE;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_SEARCH:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_SEARCH(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_SEARCH;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_FRAME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_FRAME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_FRAME;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_GRAPHIC:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_GRAPHIC(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_GRAPHIC;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_SHAPE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_SHAPE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_SHAPE;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_USER:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_USER(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_USER;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_CNTNT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_CNTNT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_CNTNT;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_IDX:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_IDX(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_IDX;
+ }
+ break;
+ case PROPERTY_MAP_USER_MARK:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_USER_MARK(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_USER_MARK;
+ }
+ break;
+ case PROPERTY_MAP_CNTIDX_MARK:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CNTIDX_MARK(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CNTIDX_MARK;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_MARK:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_MARK(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_MARK;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_TABLE_ROW:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_TABLE_ROW(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_TABLE_ROW;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_SHAPE_DESCRIPTOR:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_SHAPE_DESCRIPTOR(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_SHAPE_DESCRIPTOR;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_TABLE_CURSOR:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_TABLE_CURSOR(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_TABLE_CURSOR;
+ }
+ break;
+ case PROPERTY_MAP_BOOKMARK:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_BOOKMARK(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_BOOKMARK;
+ }
+ break;
+ case PROPERTY_MAP_FIELDMARK:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FIELDMARK(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FIELDMARK;
+ }
+ break;
+ case PROPERTY_MAP_PARAGRAPH_EXTENSIONS:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_PARAGRAPH_EXTENSIONS(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_PARAGRAPH_EXTENSIONS;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_ILLUSTRATIONS:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_ILLUSTRATIONS(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_ILLUSTRATIONS;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_OBJECTS:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_OBJECTS(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_OBJECTS;
+ }
+ break;
+ case PROPERTY_MAP_INDEX_TABLES:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_INDEX_TABLES(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_INDEX_TABLES;
+ }
+ break;
+ case PROPERTY_MAP_BIBLIOGRAPHY :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_BIBLIOGRAPHY(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_BIBLIOGRAPHY;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_DOCUMENT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_DOCUMENT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_DOCUMENT;
+ }
+ break;
+ case PROPERTY_MAP_LINK_TARGET :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_LINK_TARGET(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_LINK_TARGET;
+ }
+ break;
+ case PROPERTY_MAP_AUTO_TEXT_GROUP :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_AUTO_TEXT_GROUP(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_AUTO_TEXT_GROUP;
+ }
+ break;
+ case PROPERTY_MAP_TEXTPORTION_EXTENSIONS :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXTPORTION_EXTENSIONS(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXTPORTION_EXTENSIONS;
+ }
+ break;
+ case PROPERTY_MAP_FOOTNOTE :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FOOTNOTE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FOOTNOTE;
+ }
+ break;
+ case PROPERTY_MAP_PARAGRAPH :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_PARAGRAPH(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_PARAGRAPH;
+ }
+ break;
+ case PROPERTY_MAP_EMBEDDED_OBJECT :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_EMBEDDED_OBJECT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_EMBEDDED_OBJECT;
+ }
+ break;
+ case PROPERTY_MAP_REDLINE :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_REDLINE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_REDLINE;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_DEFAULT :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_DEFAULT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_DEFAULT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATETIME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DATETIME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DATETIME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_USER:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_USER(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_USER;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_SET_EXP:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_SET_EXP(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_SET_EXP;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_GET_EXP:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_GET_EXP(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_GET_EXP;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_FILE_NAME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_FILE_NAME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_FILE_NAME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_PAGE_NUM:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_PAGE_NUM(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_PAGE_NUM;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_AUTHOR:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_AUTHOR(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_AUTHOR;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_CHAPTER:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_CHAPTER(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_CHAPTER;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_GET_REFERENCE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_GET_REFERENCE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_GET_REFERENCE;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_CONDITIONED_TEXT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_CONDITIONED_TEXT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_CONDITIONED_TEXT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_HIDDEN_TEXT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_HIDDEN_TEXT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_HIDDEN_TEXT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_ANNOTATION :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_ANNOTATION(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_ANNOTATION;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_INPUT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_INPUT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_INPUT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_MACRO:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_MACRO(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_MACRO;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DDE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DDE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DDE;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_HIDDEN_PARA:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_HIDDEN_PARA(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_HIDDEN_PARA;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOC_INFO :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOC_INFO(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOC_INFO;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_TEMPLATE_NAME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_TEMPLATE_NAME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_TEMPLATE_NAME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_USER_EXT :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_USER_EXT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_USER_EXT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_REF_PAGE_SET:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_REF_PAGE_SET(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_REF_PAGE_SET;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_REF_PAGE_GET:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_REF_PAGE_GET(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_REF_PAGE_GET;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_JUMP_EDIT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_JUMP_EDIT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_JUMP_EDIT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_SCRIPT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_SCRIPT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_SCRIPT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_NEXT_SET:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DATABASE_NEXT_SET(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DATABASE_NEXT_SET;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_NUM_SET:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DATABASE_NUM_SET(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DATABASE_NUM_SET;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_SET_NUM:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DATABASE_SET_NUM(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DATABASE_SET_NUM;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DATABASE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DATABASE;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DATABASE_NAME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DATABASE_NAME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DATABASE_NAME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCSTAT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCSTAT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCSTAT;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_AUTHOR:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_AUTHOR(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_AUTHOR;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_DATE_TIME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_DATE_TIME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_DATE_TIME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_CHANGE_DATE_TIME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_CHANGE_DATE_TIME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_CHANGE_DATE_TIME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_CREATE_DATE_TIME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_CREATE_DATE_TIME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_CREATE_DATE_TIME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_EDIT_TIME;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_MISC :
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_MISC(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_MISC;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_REVISION:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_REVISION(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_REVISION;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_COMBINED_CHARACTERS:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_COMBINED_CHARACTERS(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_COMBINED_CHARACTERS;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DUMMY_0:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DUMMY_0(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DUMMY_0;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_TABLE_FORMULA:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_TABLE_FORMULA(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_TABLE_FORMULA;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_USER:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDMSTR_USER(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDMSTR_USER;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_DDE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDMSTR_DDE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDMSTR_DDE;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_SET_EXP:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDMSTR_SET_EXP(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDMSTR_SET_EXP;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_DATABASE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDMSTR_DATABASE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDMSTR_DATABASE;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_DUMMY0:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDMSTR_DUMMY0(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDMSTR_DUMMY0;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_BIBLIOGRAPHY:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_BIBLIOGRAPHY(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_BIBLIOGRAPHY;
+ }
+ break;
+ case PROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDMSTR_BIBLIOGRAPHY;
+ }
+ break;
+ case PROPERTY_MAP_TEXT:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT;
+ }
+ break;
+ case PROPERTY_MAP_REDLINE_PORTION:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_REDLINE_PORTION(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_REDLINE_PORTION;
+ }
+ break;
+ case PROPERTY_MAP_MAILMERGE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_MAILMERGE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_MAILMERGE;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DROPDOWN:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DROPDOWN(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DROPDOWN;
+ }
+ break;
+ case PROPERTY_MAP_CHART2_DATA_SEQUENCE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CHART2_DATA_SEQUENCE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CHART2_DATA_SEQUENCE;
+ }
+ break;
+ case PROPERTY_MAP_TEXT_VIEW:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TEXT_VIEW(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TEXT_VIEW;
+ }
+ break;
+ case PROPERTY_MAP_CONDITIONAL_PARA_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CONDITIONAL_PARA_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CONDITIONAL_PARA_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_CHAR_AUTO_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CHAR_AUTO_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CHAR_AUTO_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_RUBY_AUTO_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_RUBY_AUTO_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_RUBY_AUTO_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_PARA_AUTO_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_PARA_AUTO_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_PARA_AUTO_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_FLDTYP_DOCINFO_CUSTOM;
+ }
+ break;
+ case PROPERTY_MAP_METAFIELD:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_METAFIELD(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_METAFIELD;
+ }
+ break;
+ case PROPERTY_MAP_TABLE_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_TABLE_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_TABLE_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_CELL_STYLE:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CELL_STYLE(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CELL_STYLE;
+ }
+ break;
+ case PROPERTY_MAP_LINEBREAK:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_LINEBREAK(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_LINEBREAK;
+ }
+ break;
+ case PROPERTY_MAP_CONTENTCONTROL:
+ {
+ static SfxItemPropertySet aPROPERTY_MAP_CONTENTCONTROL(pEntries);
+ m_aPropertySetArr[nPropertyId] = &aPROPERTY_MAP_CONTENTCONTROL;
+ }
+ break;
+ }
+ }
+ return m_aPropertySetArr[nPropertyId];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unomapproperties.hxx b/sw/source/core/unocore/unomapproperties.hxx
new file mode 100644
index 000000000..da2d4d907
--- /dev/null
+++ b/sw/source/core/unocore/unomapproperties.hxx
@@ -0,0 +1,539 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#ifndef INCLUDED_SW_INC_UNOMAPPROPERTIES_HXX
+#define INCLUDED_SW_INC_UNOMAPPROPERTIES_HXX
+
+// These are the unomap common properties used by unomap?.cxx
+
+#ifndef MID_TXT_LMARGIN
+#define MID_TXT_LMARGIN 11
+#endif
+
+#define STANDARD_FONT_PROPERTIES \
+ { UNO_NAME_CHAR_HEIGHT, RES_CHRATR_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS}, \
+ { UNO_NAME_CHAR_WEIGHT, RES_CHRATR_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \
+ { u"" UNO_NAME_CHAR_FONT_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, \
+ { u"" UNO_NAME_CHAR_FONT_FAMILY, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, \
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, \
+ { u"" UNO_NAME_CHAR_FONT_PITCH, RES_CHRATR_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, \
+ { UNO_NAME_CHAR_POSTURE, RES_CHRATR_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \
+ { u"" UNO_NAME_RSID, RES_CHRATR_RSID, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_LOCALE, RES_CHRATR_LANGUAGE, cppu::UnoType<css::lang::Locale>::get(), PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE }, \
+ { u"" UNO_NAME_CHAR_INTEROP_GRAB_BAG, RES_CHRATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, \
+
+#define CJK_FONT_PROPERTIES \
+ { u"" UNO_NAME_CHAR_HEIGHT_ASIAN, RES_CHRATR_CJK_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_CHAR_WEIGHT_ASIAN, RES_CHRATR_CJK_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \
+ { u"" UNO_NAME_CHAR_FONT_NAME_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, \
+ { u"" UNO_NAME_CHAR_FONT_FAMILY_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, \
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, \
+ { u"" UNO_NAME_CHAR_FONT_PITCH_ASIAN, RES_CHRATR_CJK_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, \
+ { u"" UNO_NAME_CHAR_POSTURE_ASIAN, RES_CHRATR_CJK_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \
+ { u"" UNO_NAME_CHAR_LOCALE_ASIAN, RES_CHRATR_CJK_LANGUAGE , cppu::UnoType<css::lang::Locale>::get() , PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE },
+
+#define CTL_FONT_PROPERTIES \
+ { u"" UNO_NAME_CHAR_HEIGHT_COMPLEX, RES_CHRATR_CTL_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS},\
+ { u"" UNO_NAME_CHAR_WEIGHT_COMPLEX, RES_CHRATR_CTL_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \
+ { u"" UNO_NAME_CHAR_FONT_NAME_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \
+ { u"" UNO_NAME_CHAR_FONT_STYLE_NAME_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_STYLE_NAME }, \
+ { u"" UNO_NAME_CHAR_FONT_FAMILY_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY }, \
+ { u"" UNO_NAME_CHAR_FONT_CHAR_SET_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_CHAR_SET }, \
+ { u"" UNO_NAME_CHAR_FONT_PITCH_COMPLEX, RES_CHRATR_CTL_FONT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_PITCH }, \
+ { u"" UNO_NAME_CHAR_POSTURE_COMPLEX, RES_CHRATR_CTL_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \
+ { u"" UNO_NAME_CHAR_LOCALE_COMPLEX, RES_CHRATR_CTL_LANGUAGE , cppu::UnoType<css::lang::Locale>::get() , PropertyAttribute::MAYBEVOID, MID_LANG_LOCALE },
+
+#define REDLINE_NODE_PROPERTIES \
+ { u"" UNO_NAME_START_REDLINE, FN_UNO_REDLINE_NODE_START , cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0xbf }, \
+ { u"" UNO_NAME_END_REDLINE, FN_UNO_REDLINE_NODE_END , cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0xbf },
+
+#define REDLINE_PROPERTIES(readonly) \
+ {u"" UNO_NAME_REDLINE_AUTHOR, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_REDLINE_DATE_TIME, 0, cppu::UnoType<css::util::DateTime>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_REDLINE_COMMENT, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|readonly, 0},\
+ {u"" UNO_NAME_REDLINE_DESCRIPTION, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0}, \
+ {u"" UNO_NAME_REDLINE_TYPE, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_REDLINE_SUCCESSOR_DATA, 0, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_REDLINE_IDENTIFIER, 0, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_IS_IN_HEADER_FOOTER, 0, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_REDLINE_TEXT, 0, cppu::UnoType<css::text::XText>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+ {u"" UNO_NAME_MERGE_LAST_PARA, 0, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},
+
+#define COMMON_CRSR_PARA_PROPERTIES_FN_ONLY \
+ { u"" UNO_NAME_PARA_STYLE_NAME, FN_UNO_PARA_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_PAGE_STYLE_NAME, FN_UNO_PAGE_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0}, \
+ { u"" UNO_NAME_NUMBERING_IS_NUMBER, FN_UNO_IS_NUMBER, cppu::UnoType<bool>::get() , PropertyAttribute::MAYBEVOID, 0}, \
+ { UNO_NAME_NUMBERING_LEVEL, FN_UNO_NUM_LEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { UNO_NAME_NUMBERING_RULES, FN_UNO_NUM_RULES, cppu::UnoType<css::container::XIndexReplace>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \
+ { u"" UNO_NAME_NUMBERING_START_VALUE, FN_UNO_NUM_START_VALUE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \
+ { u"" UNO_NAME_DOCUMENT_INDEX, FN_UNO_DOCUMENT_INDEX, cppu::UnoType<css::text::XDocumentIndex>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \
+ { u"" UNO_NAME_TEXT_TABLE, FN_UNO_TEXT_TABLE, cppu::UnoType<css::text::XTextTable>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \
+ { u"" UNO_NAME_CELL, FN_UNO_CELL, cppu::UnoType<css::table::XCell>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \
+ { u"" UNO_NAME_TEXT_FRAME, FN_UNO_TEXT_FRAME, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \
+ { u"" UNO_NAME_TEXT_SECTION, FN_UNO_TEXT_SECTION, cppu::UnoType<css::text::XTextSection>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \
+ { u"" UNO_NAME_TEXT_PARAGRAPH, FN_UNO_TEXT_PARAGRAPH, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 }, \
+ { u"" UNO_NAME_PARA_CHAPTER_NUMBERING_LEVEL, FN_UNO_PARA_CHAPTER_NUMBERING_LEVEL,cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_PARA_CONDITIONAL_STYLE_NAME, FN_UNO_PARA_CONDITIONAL_STYLE_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0}, \
+ { u"" UNO_NAME_LIST_ID, FN_UNO_LIST_ID, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_PARA_IS_NUMBERING_RESTART, FN_NUMBER_NEWSTART, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_CONTINUEING_PREVIOUS_SUB_TREE, FN_UNO_PARA_CONT_PREV_SUBTREE, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0 }, \
+ { u"" UNO_NAME_PARA_LIST_LABEL_STRING, FN_UNO_PARA_NUM_STRING, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 }, \
+ { u"" UNO_NAME_PARA_LIST_AUTO_FORMAT, FN_UNO_PARA_NUM_AUTO_FORMAT, cppu::UnoType<cppu::UnoSequenceType<css::beans::NamedValue>>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_OUTLINE_LEVEL, RES_PARATR_OUTLINELEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_OUTLINE_CONTENT_VISIBLE, RES_PARATR_GRABBAG, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 },
+
+#define COMMON_HYPERLINK_PROPERTIES \
+ { u"" UNO_NAME_HYPER_LINK_U_R_L, RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_URL}, \
+ { u"" UNO_NAME_HYPER_LINK_TARGET, RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_TARGET}, \
+ { u"" UNO_NAME_HYPER_LINK_NAME, RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_HYPERLINKNAME }, \
+ { u"" UNO_NAME_UNVISITED_CHAR_STYLE_NAME, RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_UNVISITED_FMT }, \
+ { u"" UNO_NAME_VISITED_CHAR_STYLE_NAME, RES_TXTATR_INETFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID ,MID_URL_VISITED_FMT },
+
+// same as COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN but without
+// UNO_NAME_BREAK_TYPE and UNO_NAME_PAGE_DESC_NAME which can not be used
+// by the SwXTextTableCursor
+#define COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN_01 \
+ { u"" UNO_NAME_PARRSID, RES_PARATR_RSID, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_IS_HYPHENATION, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_IS_HYPHEN }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_NO_CAPS, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_CAPS }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_LAST_WORD }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_LEAD }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_TRAIL }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_WORD_LENGTH }, \
+ { u"" UNO_NAME_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE}, \
+ { u"" UNO_NAME_CHAR_AUTO_KERNING, RES_CHRATR_AUTOKERN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \
+ { u"" UNO_NAME_CHAR_HIGHLIGHT, RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \
+ { u"" UNO_NAME_PARA_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_BACK_COLOR }, \
+ { u"" UNO_NAME_CHAR_CASE_MAP, RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { UNO_NAME_CHAR_COLOR, RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_TRANSPARENCE, RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_COLOR_ALPHA }, \
+ { u"" UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT }, \
+ { u"" UNO_NAME_CHAR_CROSSED_OUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_CROSSED_OUT }, \
+ { u"" UNO_NAME_CHAR_ESCAPEMENT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_ESC }, \
+ { u"" UNO_NAME_CHAR_ESCAPEMENT_HEIGHT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, MID_ESC_HEIGHT }, \
+ { u"" UNO_NAME_CHAR_AUTO_ESCAPEMENT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_AUTO_ESC }, \
+ { u"" UNO_NAME_CHAR_FLASH, RES_CHRATR_BLINK, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_HIDDEN, RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { UNO_NAME_CHAR_UNDERLINE, RES_CHRATR_UNDERLINE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_TL_STYLE }, \
+ { u"" UNO_NAME_CHAR_UNDERLINE_COLOR, RES_CHRATR_UNDERLINE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TL_COLOR }, \
+ { u"" UNO_NAME_CHAR_UNDERLINE_HAS_COLOR, RES_CHRATR_UNDERLINE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_TL_HASCOLOR }, \
+ { u"" UNO_NAME_CHAR_OVERLINE, RES_CHRATR_OVERLINE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_TL_STYLE }, \
+ { u"" UNO_NAME_CHAR_OVERLINE_COLOR, RES_CHRATR_OVERLINE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TL_COLOR }, \
+ { u"" UNO_NAME_CHAR_OVERLINE_HAS_COLOR, RES_CHRATR_OVERLINE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_TL_HASCOLOR }, \
+ { u"" UNO_NAME_PARA_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_URL }, \
+ { u"" UNO_NAME_PARA_GRAPHIC, RES_BACKGROUND, cppu::UnoType<css::graphic::XGraphic>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC }, \
+ { u"" UNO_NAME_PARA_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_FILTER }, \
+ { u"" UNO_NAME_PARA_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_POSITION }, \
+ { u"" UNO_NAME_PARA_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TXT_LMARGIN | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_PARA_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT, RES_LR_SPACE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_FIRST_AUTO }, \
+ { u"" UNO_NAME_PARA_FIRST_LINE_INDENT, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_FIRST_LINE_INDENT | CONVERT_TWIPS }, \
+ STANDARD_FONT_PROPERTIES \
+ CJK_FONT_PROPERTIES \
+ CTL_FONT_PROPERTIES \
+ { u"" UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_DROP_CAP_FORMAT, RES_PARATR_DROP, cppu::UnoType<css::style::DropCapFormat>::get(), PropertyAttribute::MAYBEVOID, MID_DROPCAP_FORMAT | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_DROP_CAP_WHOLE_WORD, RES_PARATR_DROP, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_DROPCAP_WHOLE_WORD }, \
+ { u"" UNO_NAME_DROP_CAP_CHAR_STYLE_NAME, RES_PARATR_DROP, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_DROPCAP_CHAR_STYLE_NAME }, \
+ { u"" UNO_NAME_PARA_KEEP_TOGETHER, RES_KEEP, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_SPLIT, RES_PARATR_SPLIT, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_WIDOWS, RES_PARATR_WIDOWS, cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_ORPHANS, RES_PARATR_ORPHANS, cppu::UnoType<sal_Int8>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PAGE_NUMBER_OFFSET, RES_PAGEDESC, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGENUMOFFSET }, \
+ { u"" UNO_NAME_PARA_ADJUST, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PARA_ADJUST }, \
+ { u"" UNO_NAME_PARA_EXPAND_SINGLE_WORD, RES_PARATR_ADJUST, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_EXPAND_SINGLE }, \
+ { u"" UNO_NAME_PARA_LAST_LINE_ADJUST, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_LAST_LINE_ADJUST }, \
+ { u"" UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_COUNT }, \
+ { u"" UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LINENUMBER_STARTVALUE }, \
+ { u"" UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \
+ { u"" UNO_NAME_PARA_REGISTER_MODE_ACTIVE, RES_PARATR_REGISTER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_UP_MARGIN | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_PARA_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LO_MARGIN | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_PARA_CONTEXT_MARGIN, RES_UL_SPACE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_CTX_MARGIN }, \
+ { u"" UNO_NAME_CHAR_BACK_TRANSPARENT, RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_TRANSPARENT }, \
+ { u"" UNO_NAME_PARA_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_GRAPHIC_TRANSPARENT }, \
+ { u"" UNO_NAME_NUMBERING_STYLE_NAME, RES_PARATR_NUMRULE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_WORD_MODE, RES_CHRATR_WORDLINEMODE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_LEFT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_TOP_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_LEFT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_TOP_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_SHADOW_FORMAT, RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS }, \
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, LEFT_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, RIGHT_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, TOP_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, BOTTOM_BORDER_DISTANCE | CONVERT_TWIPS }, \
+ { u"" UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_TEXT_USER_DEFINED_ATTRIBUTES, RES_TXTATR_UNKNOWN_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS }, \
+ { u"" UNO_NAME_CHAR_COMBINE_IS_ON, RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_TWOLINES }, \
+ { u"" UNO_NAME_CHAR_COMBINE_PREFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_START_BRACKET }, \
+ { u"" UNO_NAME_CHAR_COMBINE_SUFFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_END_BRACKET }, \
+ { u"" UNO_NAME_CHAR_EMPHASIS, RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_EMPHASIS }, \
+ { u"" UNO_NAME_PARA_IS_HANGING_PUNCTUATION, RES_PARATR_HANGINGPUNCTUATION, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_IS_CHARACTER_DISTANCE, RES_PARATR_SCRIPTSPACE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_IS_FORBIDDEN_RULES, RES_PARATR_FORBIDDEN_RULES, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_VERT_ALIGNMENT, RES_PARATR_VERTALIGN, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_CHAR_ROTATION, RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_ROTATE }, \
+ { u"" UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE, RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_FITTOLINE }, \
+ { u"" UNO_NAME_CHAR_SCALE_WIDTH, RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_RUBY_TEXT, RES_TXTATR_CJK_RUBY, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_TEXT }, \
+ { u"" UNO_NAME_RUBY_ADJUST, RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ADJUST }, \
+ { u"" UNO_NAME_RUBY_CHAR_STYLE_NAME, RES_TXTATR_CJK_RUBY, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_CHARSTYLE }, \
+ { u"" UNO_NAME_RUBY_POSITION, RES_TXTATR_CJK_RUBY, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_POSITION}, \
+ { u"" UNO_NAME_RUBY_IS_ABOVE, RES_TXTATR_CJK_RUBY, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_RUBY_ABOVE }, \
+ { u"" UNO_NAME_CHAR_RELIEF, RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_RELIEF }, \
+ { u"" UNO_NAME_SNAP_TO_GRID, RES_PARATR_SNAPTOGRID, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_PARA_IS_CONNECT_BORDER, RES_PARATR_CONNECT_BORDER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 }, \
+ { u"" UNO_NAME_CHAR_SHADING_VALUE, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE }, \
+ { u"" UNO_NAME_PARA_INTEROP_GRAB_BAG, RES_PARATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0 }, \
+
+#define COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN \
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN_01 \
+ { u"" UNO_NAME_BREAK_TYPE, RES_BREAK, cppu::UnoType<css::style::BreakType>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_PAGE_DESC_NAME, RES_PAGEDESC, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGEDESCNAME },
+
+#define TABSTOPS_MAP_ENTRY { u"" UNO_NAME_TABSTOPS, RES_PARATR_TABSTOP, cppu::UnoType< cppu::UnoSequenceType<css::style::TabStop> >::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS},
+
+#define COMMON_CRSR_PARA_PROPERTIES \
+ COMMON_CRSR_PARA_PROPERTIES_FN_ONLY \
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN \
+ COMMON_HYPERLINK_PROPERTIES \
+ { u"" UNO_NAME_CHAR_STYLE_NAME, RES_TXTATR_CHARFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\
+ { u"" UNO_NAME_CHAR_STYLE_NAMES, FN_UNO_CHARFMT_SEQUENCE, cppu::UnoType< cppu::UnoSequenceType<OUString> >::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_CHAR_AUTO_STYLE_NAME, RES_TXTATR_AUTOFMT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\
+ { u"" UNO_NAME_PARA_AUTO_STYLE_NAME, RES_AUTO_STYLE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},
+
+#define COMMON_CRSR_PARA_PROPERTIES_2 \
+ COMMON_CRSR_PARA_PROPERTIES_FN_ONLY \
+ COMMON_CRSR_PARA_PROPERTIES_WITHOUT_FN
+
+#define COMPLETE_TEXT_CURSOR_MAP\
+ COMMON_CRSR_PARA_PROPERTIES\
+ { u"" UNO_NAME_DOCUMENT_INDEX_MARK, FN_UNO_DOCUMENT_INDEX_MARK, cppu::UnoType<css::text::XDocumentIndexMark>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\
+ { u"" UNO_NAME_TEXT_FIELD, FN_UNO_TEXT_FIELD, cppu::UnoType<css::text::XTextField>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\
+ { u"" UNO_NAME_REFERENCE_MARK, FN_UNO_REFERENCE_MARK, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },\
+ { u"" UNO_NAME_FOOTNOTE, FN_UNO_FOOTNOTE, cppu::UnoType<css::text::XFootnote>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\
+ { u"" UNO_NAME_ENDNOTE, FN_UNO_ENDNOTE, cppu::UnoType<css::text::XFootnote>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY ,0 },\
+ { u"" UNO_NAME_HYPER_LINK_EVENTS, RES_TXTATR_INETFMT, cppu::UnoType<css::container::XNameReplace>::get(), PropertyAttribute::MAYBEVOID, MID_URL_HYPERLINKEVENTS},\
+ { u"" UNO_NAME_NESTED_TEXT_CONTENT, FN_UNO_NESTED_TEXT_CONTENT, cppu::UnoType<css::text::XTextContent>::get(), PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0 },\
+ TABSTOPS_MAP_ENTRY
+
+#define BASE_INDEX_PROPERTIES_\
+ { u"" UNO_NAME_TITLE, WID_IDX_TITLE, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_NAME, WID_IDX_NAME, cppu::UnoType<OUString>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CONTENT_SECTION, WID_IDX_CONTENT_SECTION, cppu::UnoType<css::text::XTextSection>::get() , PropertyAttribute::READONLY, 0},\
+ { u"" UNO_NAME_HEADER_SECTION, WID_IDX_HEADER_SECTION, cppu::UnoType<css::text::XTextSection>::get() , PropertyAttribute::MAYBEVOID|PropertyAttribute::READONLY, 0},\
+
+#define ANCHOR_TYPES_PROPERTY { u"" UNO_NAME_ANCHOR_TYPES, FN_UNO_ANCHOR_TYPES, cppu::UnoType< cppu::UnoSequenceType<css::text::TextContentAnchorType> >::get(),PropertyAttribute::READONLY, 0xbf},
+
+// #i18732# #i28701# #i73249#
+// all users of COMMON_FRAME_PROPERTIES add the new XATTR_FILL_FIRST, XATTR_FILL_LAST FillStyle,
+// thus it may be possible to remove the RES_BACKGROUND entries from SvxBrushItem completely (this includes
+// all using UNO_NAME_BACK_* slots) in the future
+#define COMMON_FRAME_PROPERTIES \
+ { u"" UNO_NAME_ANCHOR_PAGE_NO, RES_ANCHOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ANCHOR_PAGENUM }, \
+ { u"" UNO_NAME_ANCHOR_TYPE, RES_ANCHOR, cppu::UnoType<css::text::TextContentAnchorType>::get(), PROPERTY_NONE, MID_ANCHOR_ANCHORTYPE}, \
+ { u"" UNO_NAME_ANCHOR_FRAME, RES_ANCHOR, cppu::UnoType<css::text::XTextFrame>::get(), PropertyAttribute::MAYBEVOID, MID_ANCHOR_ANCHORFRAME}, \
+ ANCHOR_TYPES_PROPERTY\
+ { u"" UNO_NAME_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, \
+ { u"" UNO_NAME_BACK_COLOR_R_G_B, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR_R_G_B}, \
+ { u"" UNO_NAME_BACK_COLOR_TRANSPARENCY, RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_BACK_COLOR_TRANSPARENCY}, \
+ { u"" UNO_NAME_FRAME_INTEROP_GRAB_BAG, RES_FRMATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_CONTENT_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_CONTENT }, \
+ { u"" UNO_NAME_FRAME_STYLE_NAME, FN_UNO_FRAME_STYLE_NAME,cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_BACK_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL }, \
+ { u"" UNO_NAME_BACK_GRAPHIC, RES_BACKGROUND, cppu::UnoType<css::graphic::XGraphic>::get(), PROPERTY_NONE, MID_GRAPHIC }, \
+ { u"" UNO_NAME_BACK_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER }, \
+ { u"" UNO_NAME_BACK_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, \
+ { u"" UNO_NAME_BACK_GRAPHIC_TRANSPARENCY, RES_BACKGROUND, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENCY}, \
+ { u"" UNO_NAME_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_L_MARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_WIDTH|CONVERT_TWIPS},\
+ { u"" UNO_NAME_HEIGHT, RES_FRM_SIZE, cppu::UnoType<sal_Int32>::get() , PROPERTY_NONE, MID_FRMSIZE_HEIGHT|CONVERT_TWIPS},\
+ { u"" UNO_NAME_HORI_ORIENT, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_ORIENT }, \
+ { u"" UNO_NAME_HORI_ORIENT_POSITION, RES_HORI_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_HORIORIENT_POSITION|CONVERT_TWIPS }, \
+ { u"" UNO_NAME_HORI_ORIENT_RELATION, RES_HORI_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_HORIORIENT_RELATION }, \
+ { u"" UNO_NAME_HYPER_LINK_U_R_L, RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_URL}, \
+ { u"" UNO_NAME_HYPER_LINK_TARGET, RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_TARGET}, \
+ { u"" UNO_NAME_HYPER_LINK_NAME, RES_URL, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_URL_HYPERLINKNAME }, \
+ { u"" UNO_NAME_OPAQUE, RES_OPAQUE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_PAGE_TOGGLE, RES_HORI_ORIENT, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_HORIORIENT_PAGETOGGLE }, \
+ { u"" UNO_NAME_POSITION_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_POSITION}, \
+ { u"" UNO_NAME_PRINT, RES_PRINT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_RELATIVE_HEIGHT, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT }, \
+ { u"" UNO_NAME_RELATIVE_HEIGHT_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_HEIGHT_RELATION }, \
+ { u"" UNO_NAME_RELATIVE_WIDTH, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH }, \
+ { u"" UNO_NAME_RELATIVE_WIDTH_RELATION, RES_FRM_SIZE, cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, MID_FRMSIZE_REL_WIDTH_RELATION }, \
+ { u"" UNO_NAME_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS}, \
+ { u"" UNO_NAME_SHADOW_TRANSPARENCE, RES_SHADOW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_SHADOW_TRANSPARENCE}, \
+ { u"" UNO_NAME_IMAGE_MAP, RES_URL, cppu::UnoType<css::container::XIndexContainer>::get(), PROPERTY_NONE, MID_URL_CLIENTMAP}, \
+ { u"" UNO_NAME_SERVER_MAP, RES_URL, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_URL_SERVERMAP }, \
+ { u"" UNO_NAME_SIZE, RES_FRM_SIZE, cppu::UnoType<css::awt::Size>::get(), PROPERTY_NONE, MID_FRMSIZE_SIZE|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_SIZE_PROTECTED, RES_PROTECT, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_PROTECT_SIZE }, \
+ { u"" UNO_NAME_IS_SYNC_WIDTH_TO_HEIGHT, RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_WIDTH_TO_HEIGHT }, \
+ { u"" UNO_NAME_IS_SYNC_HEIGHT_TO_WIDTH, RES_FRM_SIZE, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_FRMSIZE_IS_SYNC_HEIGHT_TO_WIDTH }, \
+ { u"" UNO_NAME_TEXT_WRAP, RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, \
+ { u"" UNO_NAME_SURROUND, RES_SURROUND, cppu::UnoType<css::text::WrapTextMode>::get(), PROPERTY_NONE, MID_SURROUND_SURROUNDTYPE }, \
+ { u"" UNO_NAME_SURROUND_ANCHORONLY, RES_SURROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_SURROUND_ANCHORONLY }, \
+ { u"" UNO_NAME_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT }, \
+ { u"" UNO_NAME_VERT_ORIENT, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_ORIENT }, \
+ { u"" UNO_NAME_VERT_ORIENT_POSITION, RES_VERT_ORIENT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_VERTORIENT_POSITION|CONVERT_TWIPS }, \
+ { u"" UNO_NAME_VERT_ORIENT_RELATION, RES_VERT_ORIENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE ,MID_VERTORIENT_RELATION }, \
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS }, \
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS }, \
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS }, \
+ { u"" UNO_LINK_DISPLAY_NAME, FN_PARAM_LINK_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0xbf}, \
+ { u"" UNO_NAME_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },\
+ { u"" UNO_NAME_Z_ORDER, FN_UNO_Z_ORDER, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_IS_FOLLOWING_TEXT_FLOW, RES_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FOLLOW_TEXT_FLOW}, \
+ { u"" UNO_NAME_PARENT_TEXT, FN_UNO_PARENT_TEXT, cppu::UnoType<text::XText>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 }, \
+ { u"" UNO_NAME_WRAP_INFLUENCE_ON_POSITION, RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<sal_Int8>::get(), PROPERTY_NONE, MID_WRAP_INFLUENCE}, \
+ { u"" UNO_NAME_ALLOW_OVERLAP, RES_WRAP_INFLUENCE_ON_OBJPOS, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_ALLOW_OVERLAP}, \
+ { u"" UNO_NAME_TITLE, FN_UNO_TITLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_TOOLTIP, FN_UNO_TOOLTIP, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_DESCRIPTION, FN_UNO_DESCRIPTION, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_LAYOUT_SIZE, WID_LAYOUT_SIZE, cppu::UnoType<css::awt::Size>::get(), PropertyAttribute::MAYBEVOID | PropertyAttribute::READONLY, 0 }, \
+ { u"" UNO_NAME_LINE_STYLE, RES_BOX, cppu::UnoType<css::drawing::LineStyle>::get(), 0, LINE_STYLE }, \
+ { u"" UNO_NAME_LINE_WIDTH, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LINE_WIDTH |CONVERT_TWIPS }, \
+ { u"" UNO_NAME_TEXT_VERT_ADJUST, RES_TEXT_VERT_ADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), PROPERTY_NONE ,0},
+
+#define COMMON_TEXT_CONTENT_PROPERTIES \
+ { u"" UNO_NAME_ANCHOR_TYPE, FN_UNO_ANCHOR_TYPE, cppu::UnoType<css::text::TextContentAnchorType>::get(), PropertyAttribute::READONLY, MID_ANCHOR_ANCHORTYPE},\
+ ANCHOR_TYPES_PROPERTY\
+ { u"" UNO_NAME_TEXT_WRAP, FN_UNO_TEXT_WRAP, cppu::UnoType<css::text::WrapTextMode>::get(), PropertyAttribute::READONLY, MID_SURROUND_SURROUNDTYPE },
+
+#define PROP_DIFF_FONTHEIGHT \
+ { u"" UNO_NAME_CHAR_PROP_HEIGHT, RES_CHRATR_FONTSIZE , cppu::UnoType<float>::get(), PROPERTY_NONE , MID_FONTHEIGHT_PROP},\
+ { u"" UNO_NAME_CHAR_DIFF_HEIGHT, RES_CHRATR_FONTSIZE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_FONTHEIGHT_DIFF|CONVERT_TWIPS},\
+ { u"" UNO_NAME_CHAR_PROP_HEIGHT_ASIAN, RES_CHRATR_CJK_FONTSIZE , cppu::UnoType<float>::get(), PROPERTY_NONE , MID_FONTHEIGHT_PROP},\
+ { u"" UNO_NAME_CHAR_DIFF_HEIGHT_ASIAN, RES_CHRATR_CJK_FONTSIZE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_FONTHEIGHT_DIFF|CONVERT_TWIPS},\
+ { u"" UNO_NAME_CHAR_PROP_HEIGHT_COMPLEX, RES_CHRATR_CTL_FONTSIZE , cppu::UnoType<float>::get(), PROPERTY_NONE , MID_FONTHEIGHT_PROP},\
+ { u"" UNO_NAME_CHAR_DIFF_HEIGHT_COMPLEX, RES_CHRATR_CTL_FONTSIZE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , MID_FONTHEIGHT_DIFF|CONVERT_TWIPS},
+
+#define COMMON_PARA_STYLE_PROPERTIES \
+ { u"" UNO_NAME_BREAK_TYPE, RES_BREAK, cppu::UnoType<css::style::BreakType>::get(), PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_PAGE_DESC_NAME, RES_PAGEDESC, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGEDESCNAME },\
+ { u"" UNO_NAME_PAGE_NUMBER_OFFSET, RES_PAGEDESC, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PAGEDESC_PAGENUMOFFSET},\
+ { u"" UNO_NAME_CHAR_AUTO_KERNING, RES_CHRATR_AUTOKERN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CHAR_BACK_TRANSPARENT, RES_CHRATR_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },\
+ { u"" UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },\
+ { u"" UNO_NAME_CHAR_HIGHLIGHT, RES_CHRATR_HIGHLIGHT, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },\
+ { u"" UNO_NAME_PARA_BACK_COLOR, RES_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR },\
+ { u"" UNO_NAME_PARA_BACK_TRANSPARENT, RES_BACKGROUND, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_GRAPHIC_TRANSPARENT },\
+ { u"" UNO_NAME_PARA_GRAPHIC_URL, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_URL },\
+ { u"" UNO_NAME_PARA_GRAPHIC, RES_BACKGROUND, cppu::UnoType<css::graphic::XGraphic>::get(), PROPERTY_NONE ,MID_GRAPHIC },\
+ { u"" UNO_NAME_PARA_GRAPHIC_FILTER, RES_BACKGROUND, cppu::UnoType<OUString>::get(), PROPERTY_NONE ,MID_GRAPHIC_FILTER },\
+ { u"" UNO_NAME_PARA_GRAPHIC_LOCATION, RES_BACKGROUND, cppu::UnoType<css::style::GraphicLocation>::get(), PROPERTY_NONE ,MID_GRAPHIC_POSITION}, \
+ { u"" UNO_NAME_CHAR_CASE_MAP, RES_CHRATR_CASEMAP, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},\
+ { UNO_NAME_CHAR_COLOR, RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CHAR_TRANSPARENCE, RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA},\
+ { u"" UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT},\
+ { u"" UNO_NAME_CHAR_CROSSED_OUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CHAR_ESCAPEMENT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC },\
+ { u"" UNO_NAME_CHAR_ESCAPEMENT_HEIGHT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int8>::get() , PROPERTY_NONE, MID_ESC_HEIGHT},\
+ { u"" UNO_NAME_CHAR_FLASH, RES_CHRATR_BLINK , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CHAR_HIDDEN, RES_CHRATR_HIDDEN, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ STANDARD_FONT_PROPERTIES\
+ CJK_FONT_PROPERTIES\
+ CTL_FONT_PROPERTIES\
+ { UNO_NAME_CHAR_UNDERLINE, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},\
+ { u"" UNO_NAME_CHAR_UNDERLINE_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},\
+ { u"" UNO_NAME_CHAR_UNDERLINE_HAS_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},\
+ { u"" UNO_NAME_CHAR_OVERLINE, RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_TL_STYLE},\
+ { u"" UNO_NAME_CHAR_OVERLINE_COLOR, RES_CHRATR_OVERLINE , cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TL_COLOR},\
+ { u"" UNO_NAME_CHAR_OVERLINE_HAS_COLOR, RES_CHRATR_OVERLINE , cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TL_HASCOLOR},\
+ { u"" UNO_NAME_PARA_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_TXT_LMARGIN|CONVERT_TWIPS},\
+ { u"" UNO_NAME_PARA_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_R_MARGIN|CONVERT_TWIPS},\
+ { u"" UNO_NAME_PARA_LEFT_MARGIN_RELATIVE, RES_LR_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_L_REL_MARGIN},\
+ { u"" UNO_NAME_PARA_RIGHT_MARGIN_RELATIVE, RES_LR_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_R_REL_MARGIN},\
+ { u"" UNO_NAME_PARA_IS_AUTO_FIRST_LINE_INDENT, RES_LR_SPACE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FIRST_AUTO},\
+ { u"" UNO_NAME_PARA_FIRST_LINE_INDENT, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FIRST_LINE_INDENT|CONVERT_TWIPS},\
+ { u"" UNO_NAME_PARA_FIRST_LINE_INDENT_RELATIVE, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_FIRST_LINE_REL_INDENT|CONVERT_TWIPS},\
+ { u"" UNO_NAME_CHAR_KERNING, RES_CHRATR_KERNING , cppu::UnoType<sal_Int16>::get() , PROPERTY_NONE, CONVERT_TWIPS},\
+ { u"" UNO_NAME_CHAR_NO_HYPHENATION, RES_CHRATR_NOHYPHEN , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_DROP_CAP_FORMAT, RES_PARATR_DROP, cppu::UnoType<css::style::DropCapFormat>::get() , PROPERTY_NONE, MID_DROPCAP_FORMAT|CONVERT_TWIPS },\
+ { u"" UNO_NAME_DROP_CAP_WHOLE_WORD, RES_PARATR_DROP, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_DROPCAP_WHOLE_WORD },\
+ { u"" UNO_NAME_DROP_CAP_CHAR_STYLE_NAME, RES_PARATR_DROP, cppu::UnoType<OUString>::get() , PropertyAttribute::MAYBEVOID, MID_DROPCAP_CHAR_STYLE_NAME },\
+ { u"" UNO_NAME_PARA_KEEP_TOGETHER, RES_KEEP, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_PARA_SPLIT, RES_PARATR_SPLIT, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_PARA_WIDOWS, RES_PARATR_WIDOWS, cppu::UnoType<sal_Int8>::get(),PropertyAttribute::MAYBEVOID, 0},\
+ { u"" UNO_NAME_PARA_ORPHANS, RES_PARATR_ORPHANS, cppu::UnoType<sal_Int8>::get(),PropertyAttribute::MAYBEVOID, 0},\
+ { u"" UNO_NAME_PARA_EXPAND_SINGLE_WORD, RES_PARATR_ADJUST, cppu::UnoType<bool>::get() , PROPERTY_NONE, MID_EXPAND_SINGLE },\
+ { u"" UNO_NAME_PARA_LAST_LINE_ADJUST, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_LAST_LINE_ADJUST},\
+ { u"" UNO_NAME_PARA_LINE_NUMBER_COUNT, RES_LINENUMBER, cppu::UnoType<bool>::get(), PROPERTY_NONE ,MID_LINENUMBER_COUNT },\
+ { u"" UNO_NAME_PARA_LINE_NUMBER_START_VALUE, RES_LINENUMBER, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_LINENUMBER_STARTVALUE},\
+ { u"" UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(),PROPERTY_NONE, CONVERT_TWIPS},\
+ { u"" UNO_NAME_PARA_ADJUST, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_PARA_ADJUST},\
+ { u"" UNO_NAME_PARA_REGISTER_MODE_ACTIVE, RES_PARATR_REGISTER, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_PARA_TOP_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_UP_MARGIN|CONVERT_TWIPS},\
+ { u"" UNO_NAME_PARA_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_LO_MARGIN|CONVERT_TWIPS},\
+ { u"" UNO_NAME_PARA_CONTEXT_MARGIN, RES_UL_SPACE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_CTX_MARGIN},\
+ { u"" UNO_NAME_PARA_TOP_MARGIN_RELATIVE, RES_UL_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_UP_REL_MARGIN},\
+ { u"" UNO_NAME_PARA_BOTTOM_MARGIN_RELATIVE, RES_UL_SPACE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_LO_REL_MARGIN},\
+ TABSTOPS_MAP_ENTRY\
+ { u"" UNO_NAME_CHAR_WORD_MODE, RES_CHRATR_WORDLINEMODE,cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_CHAR_SHADING_VALUE, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, MID_SHADING_VALUE }, \
+ { u"" UNO_NAME_CHAR_LEFT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, LEFT_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, RIGHT_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_TOP_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, TOP_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER, RES_CHRATR_BOX, cppu::UnoType<css::table::BorderLine>::get(), PROPERTY_NONE, BOTTOM_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_LEFT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_RIGHT_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_TOP_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, TOP_BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_BOTTOM_BORDER_DISTANCE, RES_CHRATR_BOX, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },\
+ { u"" UNO_NAME_CHAR_SHADOW_FORMAT, RES_CHRATR_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},\
+ { u"" UNO_NAME_LEFT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, LEFT_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_RIGHT_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, RIGHT_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_TOP_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, TOP_BORDER |CONVERT_TWIPS },\
+ { u"" UNO_NAME_BOTTOM_BORDER, RES_BOX, cppu::UnoType<css::table::BorderLine>::get(), 0, BOTTOM_BORDER|CONVERT_TWIPS },\
+ { u"" UNO_NAME_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BORDER_DISTANCE|CONVERT_TWIPS },\
+ { u"" UNO_NAME_LEFT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, LEFT_BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_RIGHT_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, RIGHT_BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_TOP_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, TOP_BORDER_DISTANCE |CONVERT_TWIPS },\
+ { u"" UNO_NAME_BOTTOM_BORDER_DISTANCE, RES_BOX, cppu::UnoType<sal_Int32>::get(), 0, BOTTOM_BORDER_DISTANCE|CONVERT_TWIPS },\
+ { u"" UNO_NAME_PARA_IS_HYPHENATION, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_IS_HYPHEN },\
+ { u"" UNO_NAME_PARA_HYPHENATION_NO_CAPS, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_CAPS },\
+ { u"" UNO_NAME_PARA_HYPHENATION_NO_LAST_WORD, RES_PARATR_HYPHENZONE, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_NO_LAST_WORD },\
+ { u"" UNO_NAME_PARA_HYPHENATION_MAX_LEADING_CHARS, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_LEAD },\
+ { u"" UNO_NAME_PARA_HYPHENATION_MAX_TRAILING_CHARS, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_TRAIL },\
+ { u"" UNO_NAME_PARA_HYPHENATION_MAX_HYPHENS, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MAX_HYPHENS},\
+ { u"" UNO_NAME_PARA_HYPHENATION_MIN_WORD_LENGTH, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_MIN_WORD_LENGTH},\
+ { u"" UNO_NAME_PARA_HYPHENATION_ZONE, RES_PARATR_HYPHENZONE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_HYPHEN_ZONE},\
+ { u"" UNO_NAME_NUMBERING_STYLE_NAME, RES_PARATR_NUMRULE, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, 0},\
+ { UNO_NAME_NUMBERING_LEVEL, RES_PARATR_LIST_LEVEL, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0},\
+ { u"" UNO_NAME_PARA_USER_DEFINED_ATTRIBUTES, RES_UNKNOWNATR_CONTAINER, cppu::UnoType<css::container::XNameContainer>::get(), PropertyAttribute::MAYBEVOID, 0 },\
+ { u"" UNO_NAME_PARA_SHADOW_FORMAT, RES_SHADOW, cppu::UnoType<css::table::ShadowFormat>::get(), PROPERTY_NONE, CONVERT_TWIPS},\
+ { u"" UNO_NAME_CHAR_COMBINE_IS_ON, RES_CHRATR_TWO_LINES, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_TWOLINES},\
+ { u"" UNO_NAME_CHAR_COMBINE_PREFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_START_BRACKET},\
+ { u"" UNO_NAME_CHAR_COMBINE_SUFFIX, RES_CHRATR_TWO_LINES, cppu::UnoType<OUString>::get(), PROPERTY_NONE, MID_END_BRACKET},\
+ { u"" UNO_NAME_CHAR_EMPHASIS, RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS},\
+ { u"" UNO_NAME_PARA_IS_HANGING_PUNCTUATION, RES_PARATR_HANGINGPUNCTUATION, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },\
+ { u"" UNO_NAME_PARA_IS_CHARACTER_DISTANCE, RES_PARATR_SCRIPTSPACE, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },\
+ { u"" UNO_NAME_PARA_IS_FORBIDDEN_RULES, RES_PARATR_FORBIDDEN_RULES, cppu::UnoType<bool>::get(), PROPERTY_NONE ,0 },\
+ { u"" UNO_NAME_PARA_VERT_ALIGNMENT, RES_PARATR_VERTALIGN, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , 0 },\
+ { u"" UNO_NAME_CHAR_ROTATION, RES_CHRATR_ROTATE, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ROTATE },\
+ { u"" UNO_NAME_CHAR_ROTATION_IS_FIT_TO_LINE, RES_CHRATR_ROTATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, MID_FITTOLINE },\
+ { u"" UNO_NAME_CHAR_SCALE_WIDTH, RES_CHRATR_SCALEW, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },\
+ { u"" UNO_NAME_CHAR_RELIEF, RES_CHRATR_RELIEF, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_RELIEF },\
+ PROP_DIFF_FONTHEIGHT\
+ { u"" UNO_NAME_FOLLOW_STYLE, FN_UNO_FOLLOW_STYLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_LINK_STYLE, FN_UNO_LINK_STYLE, cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_IS_PHYSICAL, FN_UNO_IS_PHYSICAL, cppu::UnoType<bool>::get(), PropertyAttribute::READONLY, 0},\
+ { u"" UNO_NAME_IS_AUTO_UPDATE, FN_UNO_IS_AUTO_UPDATE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},\
+ { u"" UNO_NAME_DISPLAY_NAME, FN_UNO_DISPLAY_NAME, cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},\
+ { u"" UNO_NAME_CATEGORY, FN_UNO_CATEGORY, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE , 0 },\
+ { u"" UNO_NAME_WRITING_MODE, RES_FRAMEDIR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },\
+ { u"" UNO_NAME_PARA_IS_CONNECT_BORDER, RES_PARATR_CONNECT_BORDER, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0},\
+ { u"" UNO_NAME_SNAP_TO_GRID, RES_PARATR_SNAPTOGRID, cppu::UnoType<bool>::get(), PropertyAttribute::MAYBEVOID, 0 }, \
+ { u"" UNO_NAME_OUTLINE_LEVEL, RES_PARATR_OUTLINELEVEL,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { u"" UNO_NAME_HIDDEN, FN_UNO_HIDDEN, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_STYLE_INTEROP_GRAB_BAG, FN_UNO_STYLE_INTEROP_GRAB_BAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_PARA_INTEROP_GRAB_BAG, RES_PARATR_GRABBAG, cppu::UnoType< cppu::UnoSequenceType<css::beans::PropertyValue> >::get(), PROPERTY_NONE, 0},
+
+#define COMMON_ACCESSIBILITY_TEXT_ATTRIBUTE \
+ { u"" UNO_NAME_CHAR_BACK_COLOR, RES_CHRATR_BACKGROUND, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE ,MID_BACK_COLOR }, \
+ { UNO_NAME_CHAR_COLOR, RES_CHRATR_COLOR, cppu::UnoType<sal_Int32>::get(), PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_CHAR_TRANSPARENCE, RES_CHRATR_COLOR, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_COLOR_ALPHA }, \
+ { u"" UNO_NAME_CHAR_CONTOURED, RES_CHRATR_CONTOUR, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_CHAR_EMPHASIS, RES_CHRATR_EMPHASIS_MARK, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_EMPHASIS}, \
+ { u"" UNO_NAME_CHAR_ESCAPEMENT, RES_CHRATR_ESCAPEMENT, cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, MID_ESC }, \
+ { u"" UNO_NAME_CHAR_FONT_NAME, RES_CHRATR_FONT, cppu::UnoType<OUString>::get(), PropertyAttribute::MAYBEVOID, MID_FONT_FAMILY_NAME }, \
+ { UNO_NAME_CHAR_HEIGHT, RES_CHRATR_FONTSIZE , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_FONTHEIGHT|CONVERT_TWIPS}, \
+ { UNO_NAME_CHAR_POSTURE, RES_CHRATR_POSTURE , cppu::UnoType<css::awt::FontSlant>::get(), PropertyAttribute::MAYBEVOID, MID_POSTURE}, \
+ { UNO_NAME_CHAR_SHADOWED, RES_CHRATR_SHADOWED , cppu::UnoType<bool>::get() , PROPERTY_NONE, 0}, \
+ { u"" UNO_NAME_CHAR_STRIKEOUT, RES_CHRATR_CROSSEDOUT, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_CROSS_OUT}, \
+ { u"" UNO_NAME_CHAR_UNDERLINE_COLOR, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TL_COLOR}, \
+ { UNO_NAME_CHAR_WEIGHT, RES_CHRATR_WEIGHT , cppu::UnoType<float>::get(), PropertyAttribute::MAYBEVOID, MID_WEIGHT}, \
+ { UNO_NAME_NUMBERING_LEVEL, RES_PARATR_LIST_LEVEL,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, 0}, \
+ { UNO_NAME_CHAR_UNDERLINE, RES_CHRATR_UNDERLINE , cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_TL_STYLE}, \
+ { UNO_NAME_NUMBERING_RULES, RES_PARATR_NUMRULE,cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \
+ { u"" UNO_NAME_PARA_ADJUST, RES_PARATR_ADJUST, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::MAYBEVOID, MID_PARA_ADJUST}, \
+ { u"" UNO_NAME_PARA_BOTTOM_MARGIN, RES_UL_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_LO_MARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_PARA_FIRST_LINE_INDENT, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_FIRST_LINE_INDENT|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_PARA_LEFT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_TXT_LMARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_PARA_LINE_SPACING, RES_PARATR_LINESPACING, cppu::UnoType<css::style::LineSpacing>::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \
+ { u"" UNO_NAME_PARA_RIGHT_MARGIN, RES_LR_SPACE, cppu::UnoType<sal_Int32>::get(), PropertyAttribute::MAYBEVOID, MID_R_MARGIN|CONVERT_TWIPS}, \
+ { u"" UNO_NAME_TABSTOPS, RES_PARATR_TABSTOP, cppu::UnoType< cppu::UnoSequenceType<css::style::TabStop> >::get(), PropertyAttribute::MAYBEVOID, CONVERT_TWIPS}, \
+
+#define FILL_PROPERTIES_SW_BMP \
+ { UNO_NAME_SW_FILLBMP_LOGICAL_SIZE, XATTR_FILLBMP_SIZELOG, cppu::UnoType<bool>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_OFFSET_X, XATTR_FILLBMP_TILEOFFSETX, cppu::UnoType<sal_Int32>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_OFFSET_Y, XATTR_FILLBMP_TILEOFFSETY, cppu::UnoType<sal_Int32>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_POSITION_OFFSET_X, XATTR_FILLBMP_POSOFFSETX, cppu::UnoType<sal_Int32>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_POSITION_OFFSET_Y, XATTR_FILLBMP_POSOFFSETY, cppu::UnoType<sal_Int32>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_RECTANGLE_POINT, XATTR_FILLBMP_POS, cppu::UnoType<css::drawing::RectanglePoint>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_SIZE_X, XATTR_FILLBMP_SIZEX, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM}, \
+ { UNO_NAME_SW_FILLBMP_SIZE_Y, XATTR_FILLBMP_SIZEY, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM}, \
+ { UNO_NAME_SW_FILLBMP_STRETCH, XATTR_FILLBMP_STRETCH, cppu::UnoType<bool>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBMP_TILE, XATTR_FILLBMP_TILE, cppu::UnoType<bool>::get(), 0, 0},\
+ { UNO_NAME_SW_FILLBMP_MODE, OWN_ATTR_FILLBMP_MODE, cppu::UnoType<drawing::BitmapMode>::get(), 0, 0}, \
+
+#define FILL_PROPERTIES_SW_DEFAULTS \
+ { UNO_NAME_SW_FILLCOLOR, XATTR_FILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0}, \
+
+#define FILL_PROPERTIES_SW \
+ FILL_PROPERTIES_SW_BMP \
+ FILL_PROPERTIES_SW_DEFAULTS \
+ { UNO_NAME_SW_FILLBACKGROUND, XATTR_FILLBACKGROUND, cppu::UnoType<bool>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLBITMAP, XATTR_FILLBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), 0, MID_BITMAP}, \
+ { UNO_NAME_SW_FILLBITMAPURL, XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_BITMAP }, \
+ { UNO_NAME_SW_FILLBITMAPNAME, XATTR_FILLBITMAP, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \
+ { UNO_NAME_SW_FILLGRADIENTSTEPCOUNT, XATTR_GRADIENTSTEPCOUNT, cppu::UnoType<sal_Int16>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLGRADIENT, XATTR_FILLGRADIENT, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, \
+ { UNO_NAME_SW_FILLGRADIENTNAME, XATTR_FILLGRADIENT, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \
+ { UNO_NAME_SW_FILLHATCH, XATTR_FILLHATCH, cppu::UnoType<css::drawing::Hatch>::get(), 0, MID_FILLHATCH}, \
+ { UNO_NAME_SW_FILLHATCHNAME, XATTR_FILLHATCH, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \
+ { UNO_NAME_SW_FILLSTYLE, XATTR_FILLSTYLE, cppu::UnoType<css::drawing::FillStyle>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILL_TRANSPARENCE, XATTR_FILLTRANSPARENCE, cppu::UnoType<sal_Int16>::get(), 0, 0}, \
+ { UNO_NAME_SW_FILLTRANSPARENCEGRADIENT, XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<css::awt::Gradient>::get(), 0, MID_FILLGRADIENT}, \
+ { UNO_NAME_SW_FILLTRANSPARENCEGRADIENTNAME, XATTR_FILLFLOATTRANSPARENCE, cppu::UnoType<OUString>::get(), 0, MID_NAME }, \
+ { UNO_NAME_SW_FILLCOLOR_2, XATTR_SECONDARYFILLCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0}, \
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoobj.cxx b/sw/source/core/unocore/unoobj.cxx
new file mode 100644
index 000000000..f1c7e7528
--- /dev/null
+++ b/sw/source/core/unocore/unoobj.cxx
@@ -0,0 +1,3018 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/table/TableSortField.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itemprop.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/endian.h>
+#include <unotools/collatorwrapper.hxx>
+#include <swtypes.hxx>
+#include <hintids.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <hints.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <istyleaccess.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <unocrsrhelper.hxx>
+#include <swundo.hxx>
+#include <rootfrm.hxx>
+#include <paratr.hxx>
+#include <pam.hxx>
+#include <shellio.hxx>
+#include <fmtruby.hxx>
+#include <docsh.hxx>
+#include <docstyle.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <edimp.hxx>
+#include <fchrfmt.hxx>
+#include <fmtautofmt.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unometa.hxx>
+#include <unocontentcontrol.hxx>
+#include <unotext.hxx>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <vcl/svapp.hxx>
+#include <unotools/syslocale.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <sortopt.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/i18n/WordType.hpp>
+#include <memory>
+#include <unoparaframeenum.hxx>
+#include <unoparagraph.hxx>
+#include <iodetect.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/profilezone.hxx>
+
+using namespace ::com::sun::star;
+
+// Helper classes
+SwUnoInternalPaM::SwUnoInternalPaM(SwDoc& rDoc) :
+ SwPaM(rDoc.GetNodes())
+{
+}
+
+SwUnoInternalPaM::~SwUnoInternalPaM()
+{
+ while( GetNext() != this)
+ {
+ delete GetNext();
+ }
+}
+
+SwUnoInternalPaM& SwUnoInternalPaM::operator=(const SwPaM& rPaM)
+{
+ const SwPaM* pTmp = &rPaM;
+ *GetPoint() = *rPaM.GetPoint();
+ if(rPaM.HasMark())
+ {
+ SetMark();
+ *GetMark() = *rPaM.GetMark();
+ }
+ else
+ DeleteMark();
+ while(&rPaM != (pTmp = pTmp->GetNext()))
+ {
+ if(pTmp->HasMark())
+ new SwPaM(*pTmp->GetMark(), *pTmp->GetPoint(), this);
+ else
+ new SwPaM(*pTmp->GetPoint(), this);
+ }
+ return *this;
+}
+
+void SwUnoCursorHelper::SelectPam(SwPaM & rPam, const bool bExpand)
+{
+ if (bExpand)
+ {
+ if (!rPam.HasMark())
+ {
+ rPam.SetMark();
+ }
+ }
+ else if (rPam.HasMark())
+ {
+ rPam.DeleteMark();
+ }
+}
+
+void SwUnoCursorHelper::GetTextFromPam(SwPaM & rPam, OUString & rBuffer,
+ SwRootFrame const*const pLayout)
+{
+ if (!rPam.HasMark())
+ {
+ return;
+ }
+ SvMemoryStream aStream;
+#ifdef OSL_BIGENDIAN
+ aStream.SetEndian( SvStreamEndian::BIG );
+#else
+ aStream.SetEndian( SvStreamEndian::LITTLE );
+#endif
+ WriterRef xWrt;
+ // TODO/MBA: looks like a BaseURL doesn't make sense here
+ SwReaderWriter::GetWriter( FILTER_TEXT_DLG, OUString(), xWrt );
+ if( !xWrt.is() )
+ return;
+
+ SwWriter aWriter( aStream, rPam );
+ xWrt->m_bASCII_NoLastLineEnd = true;
+ xWrt->m_bExportParagraphNumbering = false;
+ SwAsciiOptions aOpt = xWrt->GetAsciiOptions();
+ aOpt.SetCharSet( RTL_TEXTENCODING_UNICODE );
+ xWrt->SetAsciiOptions( aOpt );
+ xWrt->m_bUCS2_WithStartChar = false;
+ // #i68522#
+ const bool bOldShowProgress = xWrt->m_bShowProgress;
+ xWrt->m_bShowProgress = false;
+ xWrt->m_bHideDeleteRedlines = pLayout && pLayout->IsHideRedlines();
+
+ if( ! aWriter.Write( xWrt ).IsError() )
+ {
+ const sal_uInt64 lUniLen = aStream.GetSize()/sizeof( sal_Unicode );
+ if (lUniLen < o3tl::make_unsigned(SAL_MAX_INT32-1))
+ {
+ aStream.WriteUInt16( '\0' );
+
+ aStream.Seek( 0 );
+ aStream.ResetError();
+
+ rtl_uString *pStr = rtl_uString_alloc(lUniLen);
+ aStream.ReadBytes(pStr->buffer, lUniLen * sizeof(sal_Unicode));
+ rBuffer = OUString(pStr, SAL_NO_ACQUIRE);
+ }
+ }
+ xWrt->m_bShowProgress = bOldShowProgress;
+
+}
+
+/// @throws lang::IllegalArgumentException
+/// @throws uno::RuntimeException
+static void
+lcl_setCharStyle(SwDoc& rDoc, const uno::Any & rValue, SfxItemSet & rSet)
+{
+ SwDocShell *const pDocSh = rDoc.GetDocShell();
+ if(!pDocSh)
+ return;
+
+ OUString uStyle;
+ if (!(rValue >>= uStyle))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
+ pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
+ if (!pStyle)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ const SwFormatCharFormat aFormat(pStyle->GetCharFormat());
+ rSet.Put(aFormat);
+};
+
+/// @throws lang::IllegalArgumentException
+static void
+lcl_setAutoStyle(IStyleAccess & rStyleAccess, const uno::Any & rValue,
+ SfxItemSet & rSet, const bool bPara)
+{
+ OUString uStyle;
+ if (!(rValue >>= uStyle))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ std::shared_ptr<SfxItemSet> pStyle = bPara ?
+ rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_PARA ):
+ rStyleAccess.getByName(uStyle, IStyleAccess::AUTO_STYLE_CHAR );
+ if(!pStyle)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwFormatAutoFormat aFormat( bPara
+ ? sal::static_int_cast< sal_uInt16 >(RES_AUTO_STYLE)
+ : sal::static_int_cast< sal_uInt16 >(RES_TXTATR_AUTOFMT) );
+ aFormat.SetStyleHandle( pStyle );
+ rSet.Put(aFormat);
+};
+
+/// Tries to map rValue to RES_PARATR_LIST_AUTOFMT on the current paragraph, returns true on
+/// success.
+static bool lcl_setListAutoStyle(SwPaM& rPam, const uno::Any& rValue, SfxItemSet& rItemSet)
+{
+ // See if this is an empty range at the end of a paragraph.
+ if (rPam.Start()->nNode.GetIndex() != rPam.End()->nNode.GetIndex())
+ {
+ return false;
+ }
+
+ if (rPam.Start()->nContent.GetIndex() != rPam.End()->nContent.GetIndex())
+ {
+ return false;
+ }
+
+ SwTextNode* pTextNode = rPam.GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return false;
+ }
+
+ if (rPam.Start()->nContent.GetIndex() != pTextNode->Len())
+ {
+ return false;
+ }
+
+ // Look up the style content based on the name.
+ OUString sStyle;
+ if (!(rValue >>= sStyle))
+ {
+ return false;
+ }
+
+ IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
+ std::shared_ptr<SfxItemSet> pStyle
+ = rStyleAccess.getByName(sStyle, IStyleAccess::AUTO_STYLE_CHAR);
+ if (!pStyle)
+ {
+ return false;
+ }
+
+ // Set the style on the text node.
+ SwFormatAutoFormat aItem(RES_PARATR_LIST_AUTOFMT);
+ aItem.SetStyleHandle(pStyle);
+ pTextNode->SetAttr(aItem);
+ // Clear the style from the hints array. Without clearing, it would contain some style which
+ // happened to be there previously.
+ rItemSet.ClearItem(RES_TXTATR_AUTOFMT);
+ return true;
+}
+
+void
+SwUnoCursorHelper::SetTextFormatColl(const uno::Any & rAny, SwPaM & rPaM)
+{
+ SwDoc& rDoc = rPaM.GetDoc();
+ SwDocShell *const pDocSh = rDoc.GetDocShell();
+ if(!pDocSh)
+ return;
+ OUString uStyle;
+ rAny >>= uStyle;
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle,
+ SwGetPoolIdFromName::TxtColl );
+ SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
+ pDocSh->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Para));
+ if (!pStyle)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwTextFormatColl *const pLocal = pStyle->GetCollection();
+ UnoActionContext aAction(&rDoc);
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPaM *pTmpCursor = &rPaM;
+ do {
+ rDoc.SetTextFormatColl(*pTmpCursor, pLocal);
+ pTmpCursor = pTmpCursor->GetNext();
+ } while ( pTmpCursor != &rPaM );
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+}
+
+bool
+SwUnoCursorHelper::SetPageDesc(
+ const uno::Any& rValue, SwDoc & rDoc, SfxItemSet & rSet)
+{
+ OUString uDescName;
+ if (!(rValue >>= uDescName))
+ {
+ return false;
+ }
+ std::unique_ptr<SwFormatPageDesc> pNewDesc;
+ if(const SwFormatPageDesc* pItem = rSet.GetItemIfSet( RES_PAGEDESC ))
+ {
+ pNewDesc.reset(new SwFormatPageDesc(*pItem));
+ }
+ if (!pNewDesc)
+ {
+ pNewDesc.reset(new SwFormatPageDesc());
+ }
+ OUString sDescName;
+ SwStyleNameMapper::FillUIName(uDescName, sDescName,
+ SwGetPoolIdFromName::PageDesc);
+ if (!pNewDesc->GetPageDesc() ||
+ (pNewDesc->GetPageDesc()->GetName() != sDescName))
+ {
+ bool bPut = false;
+ if (!sDescName.isEmpty())
+ {
+ SwPageDesc *const pPageDesc = SwPageDesc::GetByName(rDoc, sDescName);
+ if (!pPageDesc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ pNewDesc->RegisterToPageDesc(*pPageDesc);
+ bPut = true;
+ }
+ if(!bPut)
+ {
+ rSet.ClearItem(RES_BREAK);
+ rSet.Put(SwFormatPageDesc());
+ }
+ else
+ {
+ rSet.Put(std::move(pNewDesc));
+ }
+ }
+ return true;
+}
+
+static void
+lcl_SetNodeNumStart(SwPaM & rCursor, uno::Any const& rValue)
+{
+ sal_Int16 nTmp = 1;
+ rValue >>= nTmp;
+ sal_uInt16 nStt = (nTmp < 0 ? USHRT_MAX : o3tl::narrowing<sal_uInt16>(nTmp));
+ SwDoc& rDoc = rCursor.GetDoc();
+ UnoActionContext aAction(&rDoc);
+
+ if( rCursor.GetNext() != &rCursor ) // MultiSelection?
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ SwPamRanges aRangeArr( rCursor );
+ SwPaM aPam( *rCursor.GetPoint() );
+ for( size_t n = 0; n < aRangeArr.Count(); ++n )
+ {
+ rDoc.SetNumRuleStart(*aRangeArr.SetPam( n, aPam ).GetPoint());
+ rDoc.SetNodeNumStart(*aRangeArr.SetPam( n, aPam ).GetPoint(),
+ nStt );
+ }
+ rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+ else
+ {
+ rDoc.SetNumRuleStart( *rCursor.GetPoint());
+ rDoc.SetNodeNumStart( *rCursor.GetPoint(), nStt );
+ }
+}
+
+static bool
+lcl_setCharFormatSequence(SwPaM & rPam, uno::Any const& rValue)
+{
+ uno::Sequence<OUString> aCharStyles;
+ if (!(rValue >>= aCharStyles))
+ {
+ return false;
+ }
+
+ for (sal_Int32 nStyle = 0; nStyle < aCharStyles.getLength(); nStyle++)
+ {
+ uno::Any aStyle;
+ rPam.GetDoc().GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
+ aStyle <<= aCharStyles.getConstArray()[nStyle];
+ // create a local set and apply each format directly
+ SfxItemSetFixed<RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT> aSet(rPam.GetDoc().GetAttrPool());
+ lcl_setCharStyle(rPam.GetDoc(), aStyle, aSet);
+ // the first style should replace the current attributes,
+ // all other have to be added
+ SwUnoCursorHelper::SetCursorAttr(rPam, aSet, nStyle
+ ? SetAttrMode::DONTREPLACE
+ : SetAttrMode::DEFAULT);
+ rPam.GetDoc().GetIDocumentUndoRedo().EndUndo(SwUndoId::START, nullptr);
+ }
+ return true;
+}
+
+static void
+lcl_setDropcapCharStyle(SwPaM const & rPam, SfxItemSet & rItemSet,
+ uno::Any const& rValue)
+{
+ OUString uStyle;
+ if (!(rValue >>= uStyle))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(uStyle, sStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ SwDoc& rDoc = rPam.GetDoc();
+ //default character style must not be set as default format
+ SwDocStyleSheet *const pStyle = static_cast<SwDocStyleSheet*>(
+ rDoc.GetDocShell()
+ ->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char));
+ if (!pStyle || pStyle->GetCharFormat() == rDoc.GetDfltCharFormat())
+ {
+ throw lang::IllegalArgumentException();
+ }
+ std::unique_ptr<SwFormatDrop> pDrop;
+ if (const SwFormatDrop* pItem = rItemSet.GetItemIfSet(RES_PARATR_DROP))
+ {
+ pDrop.reset(new SwFormatDrop(*pItem));
+ }
+ if (!pDrop)
+ {
+ pDrop.reset(new SwFormatDrop);
+ }
+ const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*pStyle));
+ pDrop->SetCharFormat(xStyle->GetCharFormat());
+ rItemSet.Put(std::move(pDrop));
+}
+
+static void
+lcl_setRubyCharstyle(SfxItemSet & rItemSet, uno::Any const& rValue)
+{
+ OUString sTmp;
+ if (!(rValue >>= sTmp))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ std::unique_ptr<SwFormatRuby> pRuby;
+ if (const SwFormatRuby* pItem = rItemSet.GetItemIfSet(RES_TXTATR_CJK_RUBY))
+ {
+ pRuby.reset(new SwFormatRuby(*pItem));
+ }
+ if (!pRuby)
+ {
+ pRuby.reset(new SwFormatRuby(OUString()));
+ }
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(sTmp, sStyle,
+ SwGetPoolIdFromName::ChrFmt);
+ pRuby->SetCharFormatName(sStyle);
+ pRuby->SetCharFormatId(0);
+ if (!sStyle.isEmpty())
+ {
+ const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(
+ sStyle, SwGetPoolIdFromName::ChrFmt);
+ pRuby->SetCharFormatId(nId);
+ }
+ rItemSet.Put(std::move(pRuby));
+}
+
+bool
+SwUnoCursorHelper::SetCursorPropertyValue(
+ SfxItemPropertyMapEntry const& rEntry, const uno::Any& rValue,
+ SwPaM & rPam, SfxItemSet & rItemSet)
+{
+ if (!(rEntry.nFlags & beans::PropertyAttribute::MAYBEVOID) &&
+ (rValue.getValueType() == cppu::UnoType<void>::get()))
+ {
+ return false;
+ }
+ bool bRet = true;
+ switch (rEntry.nWID)
+ {
+ case RES_TXTATR_CHARFMT:
+ lcl_setCharStyle(rPam.GetDoc(), rValue, rItemSet);
+ break;
+ case RES_TXTATR_AUTOFMT:
+ if (lcl_setListAutoStyle(rPam, rValue, rItemSet))
+ {
+ break;
+ }
+
+ lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
+ rValue, rItemSet, false);
+ break;
+ case FN_UNO_CHARFMT_SEQUENCE:
+ lcl_setCharFormatSequence(rPam, rValue);
+ break;
+ case FN_UNO_PARA_STYLE :
+ SwUnoCursorHelper::SetTextFormatColl(rValue, rPam);
+ break;
+ case RES_AUTO_STYLE:
+ lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
+ rValue, rItemSet, true);
+ break;
+ case FN_UNO_PAGE_STYLE:
+ //FIXME nothing here?
+ break;
+ case FN_UNO_NUM_START_VALUE:
+ lcl_SetNodeNumStart( rPam, rValue );
+ break;
+ case FN_UNO_NUM_LEVEL:
+ // #i91601#
+ case FN_UNO_LIST_ID:
+ case FN_UNO_IS_NUMBER:
+ case FN_UNO_PARA_NUM_AUTO_FORMAT:
+ {
+ // multi selection is not considered
+ SwTextNode *const pTextNd = rPam.GetNode().GetTextNode();
+ if (!pTextNd)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (FN_UNO_NUM_LEVEL == rEntry.nWID)
+ {
+ sal_Int16 nLevel = 0;
+ if (rValue >>= nLevel)
+ {
+ if (nLevel < 0 || MAXLEVEL <= nLevel)
+ {
+ throw lang::IllegalArgumentException(
+ "invalid NumberingLevel", nullptr, 0);
+ }
+ pTextNd->SetAttrListLevel(nLevel);
+ }
+ }
+ // #i91601#
+ else if (FN_UNO_LIST_ID == rEntry.nWID)
+ {
+ OUString sListId;
+ if (rValue >>= sListId)
+ {
+ pTextNd->SetListId( sListId );
+ }
+ }
+ else if (FN_UNO_IS_NUMBER == rEntry.nWID)
+ {
+ bool bIsNumber(false);
+ if ((rValue >>= bIsNumber) && !bIsNumber)
+ {
+ pTextNd->SetCountedInList( false );
+ }
+ }
+ else if (FN_UNO_PARA_NUM_AUTO_FORMAT == rEntry.nWID)
+ {
+ uno::Sequence<beans::NamedValue> props;
+ if (rValue >>= props)
+ {
+ // TODO create own map for this, it contains UNO_NAME_DISPLAY_NAME? or make property readable so ODF export can map it to a automatic style?
+ SfxItemPropertySet const& rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE));
+ SfxItemPropertyMap const& rMap(rPropSet.getPropertyMap());
+ SfxItemSetFixed
+ <RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1>
+ items( rPam.GetDoc().GetAttrPool() );
+
+ for (beans::NamedValue const & prop : std::as_const(props))
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName(prop.Name);
+ if (!pEntry)
+ {
+ if (prop.Name == "CharStyleName")
+ {
+ lcl_setCharStyle(rPam.GetDoc(), prop.Value, items);
+ continue;
+ }
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + prop.Name);
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw beans::PropertyVetoException(
+ "Property is read-only: " + prop.Name);
+ }
+ rPropSet.setPropertyValue(*pEntry, prop.Value, items);
+ }
+
+ IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
+ // Add it to the autostyle pool, needed by the ODT export.
+ const std::shared_ptr<SfxItemSet> pAutoStyle
+ = rStyleAccess.getAutomaticStyle(items, IStyleAccess::AUTO_STYLE_CHAR);
+ SwFormatAutoFormat item(RES_PARATR_LIST_AUTOFMT);
+ // note: paragraph auto styles have ParaStyleName property for the parent style; character auto styles currently do not because there's a separate hint, but for this it would be a good way to add it in order to export it as style:parent-style-name, see XMLTextParagraphExport::Add()
+ item.SetStyleHandle(pAutoStyle);
+ pTextNd->SetAttr(item);
+ }
+ }
+ //PROPERTY_MAYBEVOID!
+ }
+ break;
+ case FN_NUMBER_NEWSTART:
+ {
+ bool bVal = false;
+ if (!(rValue >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rPam.GetDoc().SetNumRuleStart(*rPam.GetPoint(), bVal);
+ }
+ break;
+ case FN_UNO_NUM_RULES:
+ SwUnoCursorHelper::setNumberingProperty(rValue, rPam);
+ break;
+ case RES_PARATR_DROP:
+ {
+ if (MID_DROPCAP_CHAR_STYLE_NAME == rEntry.nMemberId)
+ {
+ lcl_setDropcapCharStyle(rPam, rItemSet, rValue);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ {
+ if (MID_RUBY_CHARSTYLE == rEntry.nMemberId)
+ {
+ lcl_setRubyCharstyle(rItemSet, rValue);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ break;
+ case RES_PAGEDESC:
+ {
+ if (MID_PAGEDESC_PAGEDESCNAME == rEntry.nMemberId)
+ {
+ SwUnoCursorHelper::SetPageDesc(
+ rValue, rPam.GetDoc(), rItemSet);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ break;
+ default:
+ bRet = false;
+ }
+ return bRet;
+}
+
+SwFormatColl *
+SwUnoCursorHelper::GetCurTextFormatColl(SwPaM & rPaM, const bool bConditional)
+{
+ static const sal_uLong nMaxLookup = 1000;
+ SwFormatColl *pFormat = nullptr;
+ bool bError = false;
+ SwPaM *pTmpCursor = &rPaM;
+ do
+ {
+ const SwNodeOffset nSttNd = pTmpCursor->Start()->nNode.GetIndex();
+ const SwNodeOffset nEndNd = pTmpCursor->End()->nNode.GetIndex();
+
+ if( nEndNd - nSttNd >= SwNodeOffset(nMaxLookup) )
+ {
+ pFormat = nullptr;
+ break;
+ }
+
+ const SwNodes& rNds = rPaM.GetDoc().GetNodes();
+ for( SwNodeOffset n = nSttNd; n <= nEndNd; ++n )
+ {
+ SwTextNode const*const pNd = rNds[ n ]->GetTextNode();
+ if( pNd )
+ {
+ SwFormatColl *const pNdFormat = bConditional
+ ? pNd->GetFormatColl() : &pNd->GetAnyFormatColl();
+ if( !pFormat )
+ {
+ pFormat = pNdFormat;
+ }
+ else if( pFormat != pNdFormat )
+ {
+ bError = true;
+ break;
+ }
+ }
+ }
+
+ pTmpCursor = pTmpCursor->GetNext();
+ } while ( pTmpCursor != &rPaM );
+ return bError ? nullptr : pFormat;
+}
+
+SwUnoCursor& SwXTextCursor::GetCursor()
+ { return *m_pUnoCursor; }
+
+SwPaM const* SwXTextCursor::GetPaM() const
+ { return m_pUnoCursor.get(); }
+
+SwPaM* SwXTextCursor::GetPaM()
+ { return m_pUnoCursor.get(); }
+
+SwDoc const* SwXTextCursor::GetDoc() const
+ { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
+
+SwDoc* SwXTextCursor::GetDoc()
+ { return m_pUnoCursor ? &m_pUnoCursor->GetDoc() : nullptr; }
+
+SwXTextCursor::SwXTextCursor(
+ SwDoc & rDoc,
+ uno::Reference< text::XText > const& xParent,
+ const CursorType eType,
+ const SwPosition& rPos,
+ SwPosition const*const pMark)
+ : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
+ , m_eType(eType)
+ , m_xParentText(xParent)
+ , m_pUnoCursor(rDoc.CreateUnoCursor(rPos))
+{
+ if (pMark)
+ {
+ m_pUnoCursor->SetMark();
+ *m_pUnoCursor->GetMark() = *pMark;
+ }
+}
+
+SwXTextCursor::SwXTextCursor(uno::Reference< text::XText > const& xParent,
+ SwPaM const& rSourceCursor, const CursorType eType)
+ : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
+ , m_eType(eType)
+ , m_xParentText(xParent)
+ , m_pUnoCursor(rSourceCursor.GetDoc().CreateUnoCursor(*rSourceCursor.GetPoint()))
+{
+ if (rSourceCursor.HasMark())
+ {
+ m_pUnoCursor->SetMark();
+ *m_pUnoCursor->GetMark() = *rSourceCursor.GetMark();
+ }
+}
+
+SwXTextCursor::~SwXTextCursor()
+{
+ SolarMutexGuard g; // #i105557#: call dtor with locked solar mutex
+ m_pUnoCursor.reset(nullptr); // need to delete this with SolarMutex held
+}
+
+void SwXTextCursor::DeleteAndInsert(const OUString& rText,
+ const bool bForceExpandHints)
+{
+ auto pUnoCursor = static_cast<SwCursor*>(m_pUnoCursor.get());
+ if (!pUnoCursor)
+ return;
+
+ // Start/EndAction
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ UnoActionContext aAction(&rDoc);
+ const sal_Int32 nTextLen = rText.getLength();
+ rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+ auto pCurrent = pUnoCursor;
+ do
+ {
+ if (pCurrent->HasMark())
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(*pCurrent);
+ }
+ if(nTextLen)
+ {
+ const bool bSuccess(
+ SwUnoCursorHelper::DocInsertStringSplitCR(
+ rDoc, *pCurrent, rText, bForceExpandHints ) );
+ OSL_ENSURE( bSuccess, "Doc->Insert(Str) failed." );
+
+ SwUnoCursorHelper::SelectPam(*pUnoCursor, true);
+ pCurrent->Left(rText.getLength());
+ }
+ pCurrent = pCurrent->GetNext();
+ } while (pCurrent != pUnoCursor);
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+}
+
+namespace {
+
+enum ForceIntoMetaMode { META_CHECK_BOTH, META_INIT_START, META_INIT_END };
+
+}
+
+static bool
+lcl_ForceIntoMeta(SwPaM & rCursor,
+ uno::Reference<text::XText> const & xParentText,
+ const enum ForceIntoMetaMode eMode)
+{
+ bool bRet( true ); // means not forced in META_CHECK_BOTH
+ SwXMeta const * const pXMeta( dynamic_cast<SwXMeta*>(xParentText.get()) );
+ OSL_ENSURE(pXMeta, "no parent?");
+ if (!pXMeta)
+ throw uno::RuntimeException();
+ SwTextNode * pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ const bool bSuccess( pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (!bSuccess)
+ throw uno::RuntimeException();
+ // force the cursor back into the meta if it has moved outside
+ SwPosition start(*pTextNode, nStart);
+ SwPosition end(*pTextNode, nEnd);
+ switch (eMode)
+ {
+ case META_INIT_START:
+ *rCursor.GetPoint() = start;
+ break;
+ case META_INIT_END:
+ *rCursor.GetPoint() = end;
+ break;
+ case META_CHECK_BOTH:
+ if (*rCursor.Start() < start)
+ {
+ *rCursor.Start() = start;
+ bRet = false;
+ }
+ if (*rCursor.End() > end)
+ {
+ *rCursor.End() = end;
+ bRet = false;
+ }
+ break;
+ }
+ return bRet;
+}
+
+bool SwXTextCursor::IsAtEndOfMeta() const
+{
+ if (CursorType::Meta == m_eType)
+ {
+ auto pCursor( m_pUnoCursor );
+ SwXMeta const*const pXMeta(
+ dynamic_cast<SwXMeta*>(m_xParentText.get()) );
+ OSL_ENSURE(pXMeta, "no meta?");
+ if (pCursor && pXMeta)
+ {
+ SwTextNode * pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ const bool bSuccess(
+ pXMeta->SetContentRange(pTextNode, nStart, nEnd) );
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (bSuccess)
+ {
+ const SwPosition end(*pTextNode, nEnd);
+ if ( (*pCursor->GetPoint() == end)
+ || (*pCursor->GetMark() == end))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SwXTextCursor::IsAtEndOfContentControl() const
+{
+ if (CursorType::ContentControl == m_eType)
+ {
+ auto pCursor( m_pUnoCursor );
+ auto pXContentControl(
+ dynamic_cast<SwXContentControl*>(m_xParentText.get()) );
+ if (!pXContentControl)
+ {
+ SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no content control");
+ }
+ if (pCursor && pXContentControl)
+ {
+ SwTextNode * pTextNode;
+ sal_Int32 nStart;
+ sal_Int32 nEnd;
+ const bool bSuccess(
+ pXContentControl->SetContentRange(pTextNode, nStart, nEnd) );
+ if (!bSuccess)
+ {
+ SAL_WARN("sw.core", "SwXTextCursor::IsAtEndOfContentControl: no pam");
+ }
+ else
+ {
+ const SwPosition end(*pTextNode, nEnd);
+ if ( (*pCursor->GetPoint() == end)
+ || (*pCursor->GetMark() == end))
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+OUString SwXTextCursor::getImplementationName()
+{
+ return "SwXTextCursor";
+}
+
+sal_Bool SAL_CALL SwXTextCursor::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextCursor::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextCursor",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex",
+ "com.sun.star.text.TextSortable"
+ };
+}
+
+const uno::Sequence< sal_Int8 > & SwXTextCursor::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextCursorUnoTunnelId;
+ return theSwXTextCursorUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXTextCursor::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ const sal_Int64 nRet( comphelper::getSomethingImpl<SwXTextCursor>(rId, this) );
+ return nRet ? nRet : OTextCursorHelper::getSomething(rId);
+}
+
+void SAL_CALL SwXTextCursor::collapseToStart()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (rUnoCursor.HasMark())
+ {
+ if (*rUnoCursor.GetPoint() > *rUnoCursor.GetMark())
+ {
+ rUnoCursor.Exchange();
+ }
+ rUnoCursor.DeleteMark();
+ }
+}
+
+void SAL_CALL SwXTextCursor::collapseToEnd()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (rUnoCursor.HasMark())
+ {
+ if (*rUnoCursor.GetPoint() < *rUnoCursor.GetMark())
+ {
+ rUnoCursor.Exchange();
+ }
+ rUnoCursor.DeleteMark();
+ }
+}
+
+sal_Bool SAL_CALL SwXTextCursor::isCollapsed()
+{
+ SolarMutexGuard aGuard;
+
+ bool bRet = true;
+ auto pUnoCursor(m_pUnoCursor);
+ if(pUnoCursor && pUnoCursor->GetMark())
+ {
+ bRet = (*pUnoCursor->GetPoint() == *pUnoCursor->GetMark());
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::goLeft(sal_Int16 nCount, sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.Left( nCount);
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::goRight(sal_Int16 nCount, sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.Right(nCount);
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+void SAL_CALL
+SwXTextCursor::gotoStart(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("gotoStart");
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (CursorType::Body == m_eType)
+ {
+ rUnoCursor.Move( fnMoveBackward, GoInDoc );
+ //check, that the cursor is not in a table
+ SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
+ SwContentNode * pCNode = nullptr;
+ while (pTableNode)
+ {
+ rUnoCursor.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
+ pCNode = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode);
+ pTableNode = pCNode ? pCNode->FindTableNode() : nullptr;
+ }
+ if (pCNode)
+ {
+ rUnoCursor.GetPoint()->nContent.Assign(pCNode, 0);
+ }
+ SwStartNode const*const pTmp =
+ rUnoCursor.GetNode().StartOfSectionNode();
+ if (pTmp->IsSectionNode())
+ {
+ SwSectionNode const*const pSectionStartNode =
+ static_cast<SwSectionNode const*>(pTmp);
+ if (pSectionStartNode->GetSection().IsHiddenFlag())
+ {
+ pCNode = GetDoc()->GetNodes().GoNextSection(
+ &rUnoCursor.GetPoint()->nNode, true, false);
+ if (pCNode)
+ {
+ rUnoCursor.GetPoint()->nContent.Assign(pCNode, 0);
+ }
+ }
+ }
+ }
+ else if ( (CursorType::Frame == m_eType)
+ || (CursorType::TableText == m_eType)
+ || (CursorType::Header == m_eType)
+ || (CursorType::Footer == m_eType)
+ || (CursorType::Footnote== m_eType)
+ || (CursorType::Redline == m_eType))
+ {
+ rUnoCursor.MoveSection(GoCurrSection, fnSectionStart);
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_START);
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::gotoEnd(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("gotoEnd");
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (CursorType::Body == m_eType)
+ {
+ rUnoCursor.Move( fnMoveForward, GoInDoc );
+ }
+ else if ( (CursorType::Frame == m_eType)
+ || (CursorType::TableText == m_eType)
+ || (CursorType::Header == m_eType)
+ || (CursorType::Footer == m_eType)
+ || (CursorType::Footnote== m_eType)
+ || (CursorType::Redline == m_eType))
+ {
+ rUnoCursor.MoveSection( GoCurrSection, fnSectionEnd);
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ lcl_ForceIntoMeta(rUnoCursor, m_xParentText, META_INIT_END);
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::gotoRange(
+ const uno::Reference< text::XTextRange > & xRange, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+
+ if (!xRange.is())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwUnoCursor & rOwnCursor( GetCursorOrThrow() );
+
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ if (!pRange && !pCursor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwPaM aPam(GetDoc()->GetNodes());
+ const SwPaM * pPam(nullptr);
+ if (pCursor)
+ {
+ pPam = pCursor->GetPaM();
+ }
+ else if (pRange)
+ {
+ if (pRange->GetPositions(aPam))
+ {
+ pPam = & aPam;
+ }
+ }
+
+ if (!pPam)
+ {
+ throw uno::RuntimeException();
+ }
+
+ {
+ SwStartNodeType eSearchNodeType = SwNormalStartNode;
+ switch (m_eType)
+ {
+ case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
+ case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
+ case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
+ case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
+ case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
+ //case CURSOR_INVALID:
+ //case CursorType::Body:
+ default:
+ ;
+ }
+
+ const SwStartNode* pOwnStartNode = rOwnCursor.GetNode().FindSttNodeByType(eSearchNodeType);
+ while ( pOwnStartNode != nullptr
+ && pOwnStartNode->IsSectionNode())
+ {
+ pOwnStartNode = pOwnStartNode->StartOfSectionNode();
+ }
+
+ const SwStartNode* pTmp =
+ pPam->GetNode().FindSttNodeByType(eSearchNodeType);
+ while ( pTmp != nullptr
+ && pTmp->IsSectionNode() )
+ {
+ pTmp = pTmp->StartOfSectionNode();
+ }
+
+ if ( eSearchNodeType == SwTableBoxStartNode )
+ {
+ if (!pOwnStartNode || !pTmp)
+ {
+ throw uno::RuntimeException();
+ }
+
+ if ( pOwnStartNode->FindTableNode() != pTmp->FindTableNode() )
+ {
+ throw uno::RuntimeException();
+ }
+ }
+ else
+ {
+ if ( pOwnStartNode != pTmp )
+ {
+ throw uno::RuntimeException();
+ }
+ }
+ }
+
+ if (CursorType::Meta == m_eType)
+ {
+ SwPaM CopyPam(*pPam->GetMark(), *pPam->GetPoint());
+ const bool bNotForced( lcl_ForceIntoMeta(
+ CopyPam, m_xParentText, META_CHECK_BOTH) );
+ if (!bNotForced)
+ {
+ throw uno::RuntimeException(
+ "gotoRange: parameter range not contained in nesting"
+ " text content for which this cursor was created",
+ static_cast<text::XWordCursor*>(this));
+ }
+ }
+
+ // selection has to be expanded here
+ if(bExpand)
+ {
+ // cursor should include its previous range plus the given range
+ const SwPosition aOwnLeft(*rOwnCursor.Start());
+ const SwPosition aOwnRight(*rOwnCursor.End());
+ SwPosition const& rParamLeft = *pPam->Start();
+ SwPosition const& rParamRight = *pPam->End();
+
+ // now there are four SwPositions,
+ // two of them are going to be used, but which ones?
+ if (aOwnRight > rParamRight)
+ *rOwnCursor.GetPoint() = aOwnRight;
+ else
+ *rOwnCursor.GetPoint() = rParamRight;
+ rOwnCursor.SetMark();
+ if (aOwnLeft < rParamLeft)
+ *rOwnCursor.GetMark() = aOwnLeft;
+ else
+ *rOwnCursor.GetMark() = rParamLeft;
+ }
+ else
+ {
+ // cursor should be the given range
+ *rOwnCursor.GetPoint() = *pPam->GetPoint();
+ if (pPam->HasMark())
+ {
+ rOwnCursor.SetMark();
+ *rOwnCursor.GetMark() = *pPam->GetMark();
+ }
+ else
+ {
+ rOwnCursor.DeleteMark();
+ }
+ }
+}
+
+sal_Bool SAL_CALL SwXTextCursor::isStartOfWord()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet =
+ rUnoCursor.IsStartWordWT( i18n::WordType::DICTIONARY_WORD );
+ return bRet;
+}
+
+sal_Bool SAL_CALL SwXTextCursor::isEndOfWord()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet =
+ rUnoCursor.IsEndWordWT( i18n::WordType::DICTIONARY_WORD );
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoNextWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // problems arise when a paragraph starts with something other than a word
+ bool bRet = false;
+ // remember old position to check if cursor has moved
+ // since the called functions are sometimes a bit unreliable
+ // in specific cases...
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode *const pOldNode = &pPoint->nNode.GetNode();
+ sal_Int32 const nOldIndex = pPoint->nContent.GetIndex();
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // end of paragraph
+ if (rUnoCursor.GetContentNode() &&
+ (pPoint->nContent == rUnoCursor.GetContentNode()->Len()))
+ {
+ rUnoCursor.Right(1);
+ }
+ else
+ {
+ const bool bTmp =
+ rUnoCursor.GoNextWordWT( i18n::WordType::DICTIONARY_WORD );
+ // if there is no next word within the current paragraph
+ // try to go to the start of the next paragraph
+ if (!bTmp)
+ {
+ rUnoCursor.MovePara(GoNextPara, fnParaStart);
+ }
+ }
+
+ // return true if cursor has moved
+ bRet = (&pPoint->nNode.GetNode() != pOldNode) ||
+ (pPoint->nContent.GetIndex() != nOldIndex);
+ if (bRet && (CursorType::Meta == m_eType))
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoPreviousWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // white spaces create problems on the paragraph start
+ bool bRet = false;
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode *const pOldNode = &pPoint->nNode.GetNode();
+ sal_Int32 const nOldIndex = pPoint->nContent.GetIndex();
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // start of paragraph?
+ if (pPoint->nContent == 0)
+ {
+ rUnoCursor.Left(1);
+ }
+ else
+ {
+ rUnoCursor.GoPrevWordWT( i18n::WordType::DICTIONARY_WORD );
+ if (pPoint->nContent == 0)
+ {
+ rUnoCursor.Left(1);
+ }
+ }
+
+ // return true if cursor has moved
+ bRet = (&pPoint->nNode.GetNode() != pOldNode) ||
+ (pPoint->nContent.GetIndex() != nOldIndex);
+ if (bRet && (CursorType::Meta == m_eType))
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoEndOfWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ bool bRet = false;
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode & rOldNode = pPoint->nNode.GetNode();
+ sal_Int32 const nOldIndex = pPoint->nContent.GetIndex();
+
+ const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (!rUnoCursor.IsEndWordWT( nWordType ))
+ {
+ rUnoCursor.GoEndWordWT( nWordType );
+ }
+
+ // restore old cursor if we are not at the end of a word by now
+ // otherwise use current one
+ bRet = rUnoCursor.IsEndWordWT( nWordType );
+ if (!bRet)
+ {
+ pPoint->nNode = rOldNode;
+ pPoint->nContent = nOldIndex;
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoStartOfWord(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ bool bRet = false;
+ SwPosition *const pPoint = rUnoCursor.GetPoint();
+ SwNode & rOldNode = pPoint->nNode.GetNode();
+ sal_Int32 const nOldIndex = pPoint->nContent.GetIndex();
+
+ const sal_Int16 nWordType = i18n::WordType::DICTIONARY_WORD;
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ if (!rUnoCursor.IsStartWordWT( nWordType ))
+ {
+ rUnoCursor.GoStartWordWT( nWordType );
+ }
+
+ // restore old cursor if we are not at the start of a word by now
+ // otherwise use current one
+ bRet = rUnoCursor.IsStartWordWT( nWordType );
+ if (!bRet)
+ {
+ pPoint->nNode = rOldNode;
+ pPoint->nContent = nOldIndex;
+ }
+ else if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH);
+ }
+
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isStartOfSentence()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // start of paragraph?
+ bool bRet = rUnoCursor.GetPoint()->nContent == 0;
+ // with mark ->no sentence start
+ // (check if cursor is no selection, i.e. it does not have
+ // a mark or else point and mark are identical)
+ if (!bRet && (!rUnoCursor.HasMark() ||
+ *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
+ {
+ SwCursor aCursor(*rUnoCursor.GetPoint(),nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence(SwCursor::START_SENT );
+ bRet = aOrigPos == *aCursor.GetPoint();
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isEndOfSentence()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // end of paragraph?
+ bool bRet = rUnoCursor.GetContentNode() &&
+ (rUnoCursor.GetPoint()->nContent == rUnoCursor.GetContentNode()->Len());
+ // with mark->no sentence end
+ // (check if cursor is no selection, i.e. it does not have
+ // a mark or else point and mark are identical)
+ if (!bRet && (!rUnoCursor.HasMark() ||
+ *rUnoCursor.GetPoint() == *rUnoCursor.GetMark()))
+ {
+ SwCursor aCursor(*rUnoCursor.GetPoint(), nullptr);
+ SwPosition aOrigPos = *aCursor.GetPoint();
+ aCursor.GoSentence(SwCursor::END_SENT);
+ bRet = aOrigPos == *aCursor.GetPoint();
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoNextSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bWasEOS = isEndOfSentence();
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.GoSentence(SwCursor::NEXT_SENT);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
+ }
+
+ // if at the end of the sentence (i.e. at the space after the '.')
+ // advance to next word in order for GoSentence to work properly
+ // next time and have isStartOfSentence return true after this call
+ if (!rUnoCursor.IsStartWordWT(css::i18n::WordType::ANYWORD_IGNOREWHITESPACES))
+ {
+ const bool bNextWord = rUnoCursor.GoNextWordWT(i18n::WordType::ANYWORD_IGNOREWHITESPACES);
+ if (bWasEOS && !bNextWord)
+ {
+ bRet = false;
+ }
+ }
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoPreviousSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = rUnoCursor.GoSentence(SwCursor::PREV_SENT);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
+ if (bRet)
+ {
+ rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
+ // at the end of a paragraph move to the sentence end again
+ rUnoCursor.GoSentence(SwCursor::PREV_SENT);
+ }
+ }
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoStartOfSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // if we're at the para start then we won't move
+ // but bRet is also true if GoSentence failed but
+ // the start of the sentence is reached
+ bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor)
+ || rUnoCursor.GoSentence(SwCursor::START_SENT)
+ || SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoEndOfSentence(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ // bRet is true if GoSentence() succeeded or if the
+ // MovePara() succeeded while the end of the para is
+ // not reached already
+ bool bAlreadyParaEnd = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
+ bool bRet = !bAlreadyParaEnd
+ && (rUnoCursor.GoSentence(SwCursor::END_SENT)
+ || rUnoCursor.MovePara(GoCurrPara, fnParaEnd));
+ if (CursorType::Meta == m_eType)
+ {
+ bRet = lcl_ForceIntoMeta(rUnoCursor, m_xParentText,
+ META_CHECK_BOTH)
+ && bRet;
+ }
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isStartOfParagraph()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::isEndOfParagraph()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoStartOfParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = SwUnoCursorHelper::IsStartOfPara(rUnoCursor);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoCurrPara, fnParaStart);
+ }
+
+ // since MovePara(GoCurrPara, fnParaStart) only returns false
+ // if we were already at the start of the paragraph this function
+ // should always complete successfully.
+ OSL_ENSURE( bRet, "gotoStartOfParagraph failed" );
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoEndOfParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ bool bRet = SwUnoCursorHelper::IsEndOfPara(rUnoCursor);
+ if (!bRet)
+ {
+ bRet = rUnoCursor.MovePara(GoCurrPara, fnParaEnd);
+ }
+
+ // since MovePara(GoCurrPara, fnParaEnd) only returns false
+ // if we were already at the end of the paragraph this function
+ // should always complete successfully.
+ OSL_ENSURE( bRet, "gotoEndOfParagraph failed" );
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoNextParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ const bool bRet = rUnoCursor.MovePara(GoNextPara, fnParaStart);
+ return bRet;
+}
+
+sal_Bool SAL_CALL
+SwXTextCursor::gotoPreviousParagraph(sal_Bool Expand)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (CursorType::Meta == m_eType)
+ {
+ return false;
+ }
+ SwUnoCursorHelper::SelectPam(rUnoCursor, Expand);
+ const bool bRet = rUnoCursor.MovePara(GoPrevPara, fnParaStart);
+ return bRet;
+}
+
+uno::Reference< text::XText > SAL_CALL
+SwXTextCursor::getText()
+{
+ SolarMutexGuard g;
+
+ return m_xParentText;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextCursor::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ uno::Reference< text::XTextRange > xRet;
+ SwPaM aPam(*rUnoCursor.Start());
+ const uno::Reference< text::XText > xParent = getText();
+ if (CursorType::Meta == m_eType)
+ {
+ // return cursor to prevent modifying SwXTextRange for META
+ rtl::Reference<SwXTextCursor> pXCursor(
+ new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
+ *rUnoCursor.GetPoint()) );
+ pXCursor->gotoStart(false);
+ xRet = static_cast<text::XWordCursor*>(pXCursor.get());
+ }
+ else
+ {
+ xRet = new SwXTextRange(aPam, xParent);
+ }
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextCursor::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ uno::Reference< text::XTextRange > xRet;
+ SwPaM aPam(*rUnoCursor.End());
+ const uno::Reference< text::XText > xParent = getText();
+ if (CursorType::Meta == m_eType)
+ {
+ // return cursor to prevent modifying SwXTextRange for META
+ rtl::Reference<SwXTextCursor> pXCursor(
+ new SwXTextCursor(rUnoCursor.GetDoc(), xParent, CursorType::Meta,
+ *rUnoCursor.GetPoint()) );
+ pXCursor->gotoEnd(false);
+ xRet = static_cast<text::XWordCursor*>(pXCursor.get());
+ }
+ else
+ {
+ xRet = new SwXTextRange(aPam, xParent);
+ }
+ return xRet;
+}
+
+OUString SAL_CALL SwXTextCursor::getString()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ OUString aText;
+ SwUnoCursorHelper::GetTextFromPam(rUnoCursor, aText);
+ return aText;
+}
+
+void SAL_CALL
+SwXTextCursor::setString(const OUString& aString)
+{
+ SolarMutexGuard aGuard;
+
+ GetCursorOrThrow(); // just to check if valid
+
+ const bool bForceExpandHints( (CursorType::Meta == m_eType)
+ && dynamic_cast<SwXMeta&>(*m_xParentText)
+ .CheckForOwnMemberMeta(*GetPaM(), true) );
+ DeleteAndInsert(aString, bForceExpandHints);
+}
+
+uno::Any SwUnoCursorHelper::GetPropertyValue(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ std::u16string_view rPropertyName)
+{
+ uno::Any aAny;
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("Unknown property: ") + rPropertyName,
+ static_cast<cppu::OWeakObject *>(nullptr));
+ }
+
+ beans::PropertyState eTemp;
+ const bool bDone = SwUnoCursorHelper::getCursorPropertyValue(
+ *pEntry, rPaM, &aAny, eTemp );
+
+ if (!bDone)
+ {
+ SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>
+ aSet(rPaM.GetDoc().GetAttrPool());
+
+ SwUnoCursorHelper::GetCursorAttr(rPaM, aSet);
+
+ rPropSet.getPropertyValue(*pEntry, aSet, aAny);
+ }
+
+ return aAny;
+}
+
+void SwUnoCursorHelper::SetPropertyValue(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const OUString& rPropertyName,
+ const uno::Any& rValue,
+ const SetAttrMode nAttrMode)
+{
+ uno::Sequence< beans::PropertyValue > aValues{ comphelper::makePropertyValue(rPropertyName,
+ rValue)};
+ SetPropertyValues(rPaM, rPropSet, aValues, nAttrMode);
+}
+
+// FN_UNO_PARA_STYLE is known to set attributes for nodes, inside
+// SwUnoCursorHelper::SetTextFormatColl, instead of extending item set.
+// We need to get them from nodes in next call to GetCursorAttr.
+// The rest could cause similar problems in theory, so we just list them here.
+static bool propertyCausesSideEffectsInNodes(sal_uInt16 nWID)
+{
+ return nWID == FN_UNO_PARA_STYLE ||
+ nWID == FN_UNO_CHARFMT_SEQUENCE ||
+ nWID == FN_UNO_NUM_START_VALUE ||
+ nWID == FN_UNO_NUM_RULES;
+}
+
+void SwUnoCursorHelper::SetPropertyValues(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const uno::Sequence< beans::PropertyValue > &rPropertyValues,
+ const SetAttrMode nAttrMode)
+{
+ if (!rPropertyValues.hasElements())
+ return;
+
+ SwDoc& rDoc = rPaM.GetDoc();
+ OUString aUnknownExMsg, aPropertyVetoExMsg;
+
+ // Build set of attributes we want to fetch
+ WhichRangesContainer aRanges;
+ std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aEntries;
+ aEntries.reserve(rPropertyValues.getLength());
+ for (const auto& rPropVal : rPropertyValues)
+ {
+ const OUString &rPropertyName = rPropVal.Name;
+
+ SfxItemPropertyMapEntry const* pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+
+ // Queue up any exceptions until the end ...
+ if (!pEntry)
+ {
+ aUnknownExMsg += "Unknown property: '" + rPropertyName + "' ";
+ continue;
+ }
+ else if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ aPropertyVetoExMsg += "Property is read-only: '" + rPropertyName + "' ";
+ continue;
+ }
+ aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID);
+ aEntries.emplace_back(pEntry, rPropVal.Value);
+ }
+
+ if (!aEntries.empty())
+ {
+ // Fetch, overwrite, and re-set the attributes from the core
+ SfxItemSet aItemSet(rDoc.GetAttrPool(), aRanges);
+
+ bool bPreviousPropertyCausesSideEffectsInNodes = false;
+ for (size_t i = 0; i < aEntries.size(); ++i)
+ {
+ SfxItemPropertyMapEntry const*const pEntry = aEntries[i].first;
+ bool bPropertyCausesSideEffectsInNodes =
+ propertyCausesSideEffectsInNodes(pEntry->nWID);
+
+ // we need to get up-to-date item set from nodes
+ if (i == 0 || bPreviousPropertyCausesSideEffectsInNodes)
+ {
+ aItemSet.ClearItem();
+ SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet);
+ }
+
+ const uno::Any &rValue = aEntries[i].second;
+ // this can set some attributes in nodes' mpAttrSet
+ if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet))
+ rPropSet.setPropertyValue(*pEntry, rValue, aItemSet);
+
+ if (i + 1 == aEntries.size() || bPropertyCausesSideEffectsInNodes)
+ SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false/*bTableMode*/);
+
+ bPreviousPropertyCausesSideEffectsInNodes = bPropertyCausesSideEffectsInNodes;
+ }
+ }
+
+ if (!aUnknownExMsg.isEmpty())
+ throw beans::UnknownPropertyException(aUnknownExMsg, static_cast<cppu::OWeakObject *>(nullptr));
+ if (!aPropertyVetoExMsg.isEmpty())
+ throw beans::PropertyVetoException(aPropertyVetoExMsg, static_cast<cppu::OWeakObject *>(nullptr));
+}
+
+namespace
+{
+ bool NotInRange(sal_uInt16 nWID, sal_uInt16 nStart, sal_uInt16 nEnd)
+ {
+ return nWID < nStart || nWID > nEnd;
+ }
+}
+
+uno::Sequence< beans::PropertyState >
+SwUnoCursorHelper::GetPropertyStates(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const uno::Sequence< OUString >& rPropertyNames,
+ const SwGetPropertyStatesCaller eCaller)
+{
+ const OUString* pNames = rPropertyNames.getConstArray();
+ uno::Sequence< beans::PropertyState > aRet(rPropertyNames.getLength());
+ beans::PropertyState* pStates = aRet.getArray();
+ const SfxItemPropertyMap &rMap = rPropSet.getPropertyMap();
+ std::optional<SfxItemSet> oSet;
+ std::optional<SfxItemSet> oSetParent;
+
+ for (sal_Int32 i = 0, nEnd = rPropertyNames.getLength(); i < nEnd; i++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName( pNames[i] );
+ if(!pEntry)
+ {
+ if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
+ pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ continue;
+ }
+ else if (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT ==
+ eCaller)
+ {
+ //this values marks the element as unknown property
+ pStates[i] = beans::PropertyState::PropertyState_MAKE_FIXED_SIZE;
+ continue;
+ }
+ else
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pNames[i],
+ static_cast<cppu::OWeakObject *>(nullptr));
+ }
+ }
+ if (((SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION == eCaller) ||
+ (SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT == eCaller)) &&
+ NotInRange(pEntry->nWID, FN_UNO_RANGE_BEGIN, FN_UNO_RANGE_END) &&
+ NotInRange(pEntry->nWID, RES_CHRATR_BEGIN, RES_TXTATR_END) )
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ if ( pEntry->nWID >= FN_UNO_RANGE_BEGIN &&
+ pEntry->nWID <= FN_UNO_RANGE_END )
+ {
+ (void)SwUnoCursorHelper::getCursorPropertyValue(
+ *pEntry, rPaM, nullptr, pStates[i] );
+ }
+ else
+ {
+ if (!oSet)
+ {
+ switch ( eCaller )
+ {
+ case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT:
+ case SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION:
+ oSet.emplace( rPaM.GetDoc().GetAttrPool(),
+ svl::Items<RES_CHRATR_BEGIN, RES_TXTATR_END> );
+ break;
+ case SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY:
+ oSet.emplace( rPaM.GetDoc().GetAttrPool(),
+ pEntry->nWID, pEntry->nWID );
+ break;
+ default:
+ oSet.emplace(
+ rPaM.GetDoc().GetAttrPool(),
+ svl::Items<
+ RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER,
+ RES_UNKNOWNATR_CONTAINER>);
+ }
+ // #i63870#
+ SwUnoCursorHelper::GetCursorAttr( rPaM, *oSet );
+ }
+
+ pStates[i] = ( oSet->Count() )
+ ? rPropSet.getPropertyState( *pEntry, *oSet )
+ : beans::PropertyState_DEFAULT_VALUE;
+
+ //try again to find out if a value has been inherited
+ if( beans::PropertyState_DIRECT_VALUE == pStates[i] )
+ {
+ if (!oSetParent)
+ {
+ oSetParent.emplace(oSet->CloneAsValue( false ));
+ // #i63870#
+ SwUnoCursorHelper::GetCursorAttr(
+ rPaM, *oSetParent, true, false );
+ }
+
+ pStates[i] = ( oSetParent->Count() )
+ ? rPropSet.getPropertyState( *pEntry, *oSetParent )
+ : beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+beans::PropertyState SwUnoCursorHelper::GetPropertyState(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ const OUString& rPropertyName)
+{
+ uno::Sequence< OUString > aStrings { rPropertyName };
+ uno::Sequence< beans::PropertyState > aSeq =
+ GetPropertyStates(rPaM, rPropSet, aStrings,
+ SW_PROPERTY_STATE_CALLER_SINGLE_VALUE_ONLY );
+ return aSeq[0];
+}
+
+static void
+lcl_SelectParaAndReset( SwPaM &rPaM, SwDoc & rDoc,
+ o3tl::sorted_vector<sal_uInt16> const &rWhichIds )
+{
+ // if we are resetting paragraph attributes, we need to select the full paragraph first
+ SwPosition aStart = *rPaM.Start();
+ SwPosition aEnd = *rPaM.End();
+ auto pTemp ( rDoc.CreateUnoCursor(aStart) );
+ if(!SwUnoCursorHelper::IsStartOfPara(*pTemp))
+ {
+ pTemp->MovePara(GoCurrPara, fnParaStart);
+ }
+ pTemp->SetMark();
+ *pTemp->GetPoint() = aEnd;
+ SwUnoCursorHelper::SelectPam(*pTemp, true);
+ if(!SwUnoCursorHelper::IsEndOfPara(*pTemp))
+ {
+ pTemp->MovePara(GoCurrPara, fnParaEnd);
+ }
+ rDoc.ResetAttrs(*pTemp, true, rWhichIds);
+}
+
+void SwUnoCursorHelper::SetPropertyToDefault(
+ SwPaM& rPaM, const SfxItemPropertySet& rPropSet,
+ std::u16string_view rPropertyName)
+{
+ SwDoc& rDoc = rPaM.GetDoc();
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("Unknown property: ") + rPropertyName,
+ static_cast<cppu::OWeakObject *>(nullptr));
+ }
+
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw uno::RuntimeException(
+ OUString::Concat("setPropertyToDefault: property is read-only: ")
+ + rPropertyName, nullptr);
+ }
+
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ const o3tl::sorted_vector<sal_uInt16> aWhichIds{ pEntry->nWID };
+ if (pEntry->nWID < RES_PARATR_BEGIN)
+ {
+ rDoc.ResetAttrs(rPaM, true, aWhichIds);
+ }
+ else
+ {
+ lcl_SelectParaAndReset ( rPaM, rDoc, aWhichIds );
+ }
+ }
+ else
+ {
+ SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rPaM);
+ }
+}
+
+uno::Any SwUnoCursorHelper::GetPropertyDefault(
+ SwPaM const & rPaM, const SfxItemPropertySet& rPropSet,
+ std::u16string_view rPropertyName)
+{
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ OUString::Concat("Unknown property: ") + rPropertyName,
+ static_cast<cppu::OWeakObject *>(nullptr));
+ }
+
+ uno::Any aRet;
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ SwDoc& rDoc = rPaM.GetDoc();
+ const SfxPoolItem& rDefItem =
+ rDoc.GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(aRet, pEntry->nMemberId);
+ }
+ return aRet;
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXTextCursor::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef = [&]()
+ {
+ static SfxItemPropertyMapEntry const aCursorExtMap_Impl[] =
+ {
+ { u"" UNO_NAME_IS_SKIP_HIDDEN_TEXT, FN_SKIP_HIDDEN_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_SKIP_PROTECTED_TEXT, FN_SKIP_PROTECTED_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_NO_FORMAT_ATTR, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ const uno::Reference< beans::XPropertySetInfo > xInfo =
+ m_rPropSet.getPropertySetInfo();
+ // extend PropertySetInfo!
+ const uno::Sequence<beans::Property> aPropSeq = xInfo->getProperties();
+ return rtl::Reference<SfxExtItemPropertySetInfo>(new SfxExtItemPropertySetInfo(
+ aCursorExtMap_Impl,
+ aPropSeq ));
+ }();
+ return xRef;
+}
+
+void SAL_CALL
+SwXTextCursor::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rUnoCursor.SetSkipOverHiddenSections(bSet);
+ }
+ else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ rUnoCursor.SetSkipOverProtectSections(bSet);
+ }
+ else if (rPropertyName == UNO_NAME_RESET_PARAGRAPH_LIST_ATTRIBUTES)
+ {
+ SwTextNode* pTextNode= GetPaM()->GetNode().GetTextNode();
+
+ if(pTextNode)
+ {
+ pTextNode->ResetAttr(RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END);
+ }
+ }
+ else if (rPropertyName == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (bSet)
+ {
+ m_nAttrMode = SetAttrMode::NOFORMATATTR;
+ }
+ else
+ {
+ m_nAttrMode = SetAttrMode::DEFAULT;
+ }
+ }
+ else
+ {
+ SwUnoCursorHelper::SetPropertyValue(rUnoCursor,
+ m_rPropSet, rPropertyName, rValue, m_nAttrMode);
+ }
+}
+
+uno::Any SAL_CALL
+SwXTextCursor::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ uno::Any aAny;
+ if (rPropertyName == UNO_NAME_IS_SKIP_HIDDEN_TEXT)
+ {
+ const bool bSet = rUnoCursor.IsSkipOverHiddenSections();
+ aAny <<= bSet;
+ }
+ else if (rPropertyName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ {
+ const bool bSet = rUnoCursor.IsSkipOverProtectSections();
+ aAny <<= bSet;
+ }
+ else
+ {
+ aAny = SwUnoCursorHelper::GetPropertyValue(rUnoCursor,
+ m_rPropSet, rPropertyName);
+ }
+ return aAny;
+}
+
+void SAL_CALL
+SwXTextCursor::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextCursor::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextCursor::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextCursor::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextCursor::removeVetoableChangeListener(): not implemented");
+}
+
+beans::PropertyState SAL_CALL
+SwXTextCursor::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const beans::PropertyState eRet = SwUnoCursorHelper::GetPropertyState(
+ rUnoCursor, m_rPropSet, rPropertyName);
+ return eRet;
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL
+SwXTextCursor::getPropertyStates(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ return SwUnoCursorHelper::GetPropertyStates(
+ rUnoCursor, m_rPropSet, rPropertyNames);
+}
+
+void SAL_CALL
+SwXTextCursor::setPropertyToDefault(const OUString& rPropertyName)
+{
+ // forward: need no solar mutex here
+ uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
+ setPropertiesToDefault ( aSequence );
+}
+
+uno::Any SAL_CALL
+SwXTextCursor::getPropertyDefault(const OUString& rPropertyName)
+{
+ // forward: need no solar mutex here
+ const uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
+ return getPropertyDefaults ( aSequence ).getConstArray()[0];
+}
+
+void SAL_CALL SwXTextCursor::setPropertyValues(
+ const uno::Sequence< OUString >& aPropertyNames,
+ const uno::Sequence< uno::Any >& aValues )
+{
+ if( aValues.getLength() != aPropertyNames.getLength() )
+ {
+ OSL_FAIL( "mis-matched property value sequences" );
+ throw lang::IllegalArgumentException();
+ }
+
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ // a little lame to have to copy into this.
+ uno::Sequence< beans::PropertyValue > aPropertyValues( aValues.getLength() );
+ auto aPropertyValuesRange = asNonConstRange(aPropertyValues);
+ for ( sal_Int32 i = 0; i < aPropertyNames.getLength(); i++ )
+ {
+ if ( aPropertyNames[ i ] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ aPropertyNames[ i ] == UNO_NAME_IS_SKIP_PROTECTED_TEXT )
+ {
+ // the behaviour of these is hard to model in a group
+ OSL_FAIL("invalid property name for batch setting");
+ throw lang::IllegalArgumentException();
+ }
+ aPropertyValuesRange[ i ].Name = aPropertyNames[ i ];
+ aPropertyValuesRange[ i ].Value = aValues[ i ];
+ }
+ try
+ {
+ SwUnoCursorHelper::SetPropertyValues( rUnoCursor, m_rPropSet, aPropertyValues );
+ }
+ catch (const css::beans::UnknownPropertyException& e)
+ {
+ uno::Any a(cppu::getCaughtException());
+ throw lang::WrappedTargetException(
+ "wrapped Exception " + e.Message,
+ uno::Reference<uno::XInterface>(), a);
+ }
+}
+
+uno::Sequence< uno::Any > SAL_CALL
+SwXTextCursor::getPropertyValues( const uno::Sequence< OUString >& aPropertyNames )
+{
+ // a banal implementation for now
+ uno::Sequence< uno::Any > aValues( aPropertyNames.getLength() );
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aValues.getArray(),
+ [this](const OUString& rName) -> uno::Any { return getPropertyValue( rName ); });
+ return aValues;
+}
+
+void SAL_CALL SwXTextCursor::addPropertiesChangeListener(
+ const uno::Sequence< OUString >& /* aPropertyNames */,
+ const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("SwXTextCursor::addPropertiesChangeListener(): not implemented");
+}
+void SAL_CALL SwXTextCursor::removePropertiesChangeListener(
+ const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("SwXTextCursor::removePropertiesChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXTextCursor::firePropertiesChangeEvent(
+ const uno::Sequence< OUString >& /* aPropertyNames */,
+ const uno::Reference< css::beans::XPropertiesChangeListener >& /* xListener */ )
+{
+ OSL_FAIL("SwXTextCursor::firePropertiesChangeEvent(): not implemented");
+}
+
+// para specific attribute ranges
+static sal_uInt16 g_ParaResetableSetRange[] = {
+ RES_FRMATR_BEGIN, RES_FRMATR_END-1,
+ RES_PARATR_BEGIN, RES_PARATR_END-1,
+ RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END-1,
+ RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1,
+ 0
+};
+
+// selection specific attribute ranges
+static sal_uInt16 g_ResetableSetRange[] = {
+ RES_CHRATR_BEGIN, RES_CHRATR_END-1,
+ RES_TXTATR_INETFMT, RES_TXTATR_INETFMT,
+ RES_TXTATR_CHARFMT, RES_TXTATR_CHARFMT,
+ RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY,
+ RES_TXTATR_UNKNOWN_CONTAINER, RES_TXTATR_UNKNOWN_CONTAINER,
+ 0
+};
+
+static void
+lcl_EnumerateIds(sal_uInt16 const* pIdRange, o3tl::sorted_vector<sal_uInt16> &rWhichIds)
+{
+ while (*pIdRange)
+ {
+ const sal_uInt16 nStart = *pIdRange++;
+ const sal_uInt16 nEnd = *pIdRange++;
+ for (sal_uInt16 nId = nStart + 1; nId <= nEnd; ++nId)
+ {
+ rWhichIds.insert( nId );
+ }
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::setAllPropertiesToDefault()
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
+ o3tl::sorted_vector<sal_uInt16> aWhichIds;
+ lcl_EnumerateIds(g_ParaResetableSetRange, aParaWhichIds);
+ lcl_EnumerateIds(g_ResetableSetRange, aWhichIds);
+ if (!aParaWhichIds.empty())
+ {
+ lcl_SelectParaAndReset(rUnoCursor, rUnoCursor.GetDoc(),
+ aParaWhichIds);
+ }
+ if (!aWhichIds.empty())
+ {
+ rUnoCursor.GetDoc().ResetAttrs(rUnoCursor, true, aWhichIds);
+ }
+}
+
+void SAL_CALL
+SwXTextCursor::setPropertiesToDefault(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if ( !rPropertyNames.hasElements() )
+ return;
+
+ SwDoc& rDoc = rUnoCursor.GetDoc();
+ o3tl::sorted_vector<sal_uInt16> aWhichIds;
+ o3tl::sorted_vector<sal_uInt16> aParaWhichIds;
+ for (const OUString& rName : rPropertyNames)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_rPropSet.getPropertyMap().getByName( rName );
+ if (!pEntry)
+ {
+ if (rName == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ rName == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ {
+ continue;
+ }
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw uno::RuntimeException(
+ "setPropertiesToDefault: property is read-only: " + rName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ if (pEntry->nWID < RES_PARATR_BEGIN)
+ {
+ aWhichIds.insert( pEntry->nWID );
+ }
+ else
+ {
+ aParaWhichIds.insert( pEntry->nWID );
+ }
+ }
+ else if (pEntry->nWID == FN_UNO_NUM_START_VALUE)
+ {
+ SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, rUnoCursor);
+ }
+ }
+
+ if (!aParaWhichIds.empty())
+ {
+ lcl_SelectParaAndReset(rUnoCursor, rDoc, aParaWhichIds);
+ }
+ if (!aWhichIds.empty())
+ {
+ rDoc.ResetAttrs(rUnoCursor, true, aWhichIds);
+ }
+}
+
+uno::Sequence< uno::Any > SAL_CALL
+SwXTextCursor::getPropertyDefaults(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ const sal_Int32 nCount = rPropertyNames.getLength();
+ uno::Sequence< uno::Any > aRet(nCount);
+ if ( nCount )
+ {
+ SwDoc& rDoc = rUnoCursor.GetDoc();
+ const OUString *pNames = rPropertyNames.getConstArray();
+ uno::Any *pAny = aRet.getArray();
+ for (sal_Int32 i = 0; i < nCount; i++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_rPropSet.getPropertyMap().getByName( pNames[i] );
+ if (!pEntry)
+ {
+ if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
+ pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
+ pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ continue;
+ }
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pNames[i],
+ static_cast<cppu::OWeakObject *>(nullptr));
+ }
+ if (pEntry->nWID < RES_FRMATR_END)
+ {
+ const SfxPoolItem& rDefItem =
+ rDoc.GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(pAny[i], pEntry->nMemberId);
+ }
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL SwXTextCursor::invalidateMarkings(::sal_Int32 nType)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwNode& node = rUnoCursor.GetNode();
+
+ SwTextNode* txtNode = node.GetTextNode();
+
+ if (txtNode == nullptr) return;
+
+ if ( text::TextMarkupType::SPELLCHECK == nType )
+ {
+ txtNode->SetWrongDirty(SwTextNode::WrongState::TODO);
+ txtNode->ClearWrong();
+ }
+ else if( text::TextMarkupType::PROOFREADING == nType )
+ {
+ txtNode->SetGrammarCheckDirty(true);
+ txtNode->ClearGrammarCheck();
+ }
+ else if ( text::TextMarkupType::SMARTTAG == nType )
+ {
+ txtNode->SetSmartTagDirty(true);
+ txtNode->ClearSmartTags();
+ }
+ else return;
+
+ SwFormatColl* fmtColl=txtNode->GetFormatColl();
+
+ if (fmtColl == nullptr) return;
+
+ SwFormatChg aNew( fmtColl );
+ txtNode->CallSwClientNotify(sw::LegacyModifyHint(nullptr, &aNew));
+}
+
+void SAL_CALL
+SwXTextCursor::makeRedline(
+ const OUString& rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::makeRedline(rUnoCursor, rRedlineType, rRedlineProperties);
+}
+
+void SAL_CALL SwXTextCursor::insertDocumentFromURL(const OUString& rURL,
+ const uno::Sequence< beans::PropertyValue >& rOptions)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwUnoCursorHelper::InsertFile(&rUnoCursor, rURL, rOptions);
+}
+
+uno::Sequence< beans::PropertyValue >
+SwUnoCursorHelper::CreateSortDescriptor(const bool bFromTable)
+{
+ uno::Sequence< beans::PropertyValue > aRet(5);
+ beans::PropertyValue* pArray = aRet.getArray();
+
+ uno::Any aVal;
+ aVal <<= bFromTable;
+ pArray[0] = beans::PropertyValue("IsSortInTable", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= u' ';
+ pArray[1] = beans::PropertyValue("Delimiter", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= false;
+ pArray[2] = beans::PropertyValue("IsSortColumns", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ aVal <<= sal_Int32(3);
+ pArray[3] = beans::PropertyValue("MaxSortFieldsCount", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ lang::Locale aLang( SvtSysLocale().GetLanguageTag().getLocale());
+ // get collator algorithm to be used for the locale
+ uno::Sequence< OUString > aSeq(
+ GetAppCollator().listCollatorAlgorithms( aLang ) );
+ const bool bHasElements = aSeq.hasElements();
+ OSL_ENSURE( bHasElements, "list of collator algorithms is empty!");
+ OUString aCollAlg;
+ if (bHasElements)
+ {
+ aCollAlg = aSeq.getConstArray()[0];
+ }
+
+ uno::Sequence< table::TableSortField > aFields
+ {
+ // Field, IsAscending, IsCaseSensitive, FieldType, CollatorLocale, CollatorAlgorithm
+ { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
+ { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg },
+ { 1, true, false, table::TableSortFieldType_ALPHANUMERIC, aLang, aCollAlg }
+ };
+
+ aVal <<= aFields;
+ pArray[4] = beans::PropertyValue("SortFields", -1, aVal,
+ beans::PropertyState_DIRECT_VALUE);
+
+ return aRet;
+}
+
+uno::Sequence< beans::PropertyValue > SAL_CALL
+SwXTextCursor::createSortDescriptor()
+{
+ SolarMutexGuard aGuard;
+
+ return SwUnoCursorHelper::CreateSortDescriptor(false);
+}
+
+bool SwUnoCursorHelper::ConvertSortProperties(
+ const uno::Sequence< beans::PropertyValue >& rDescriptor,
+ SwSortOptions& rSortOpt)
+{
+ bool bRet = true;
+
+ rSortOpt.bTable = false;
+ rSortOpt.cDeli = ' ';
+ rSortOpt.eDirection = SwSortDirection::Columns; //!! UI text may be contrary though !!
+
+ SwSortKey aKey1;
+ aKey1.nColumnId = USHRT_MAX;
+ aKey1.bIsNumeric = true;
+ aKey1.eSortOrder = SwSortOrder::Ascending;
+
+ SwSortKey aKey2;
+ aKey2.nColumnId = USHRT_MAX;
+ aKey2.bIsNumeric = true;
+ aKey2.eSortOrder = SwSortOrder::Ascending;
+
+ SwSortKey aKey3;
+ aKey3.nColumnId = USHRT_MAX;
+ aKey3.bIsNumeric = true;
+ aKey3.eSortOrder = SwSortOrder::Ascending;
+ SwSortKey* aKeys[3] = {&aKey1, &aKey2, &aKey3};
+
+ bool bOldSortdescriptor(false);
+ bool bNewSortdescriptor(false);
+
+ for (const beans::PropertyValue& rProperty : rDescriptor)
+ {
+ uno::Any aValue( rProperty.Value );
+ const OUString& rPropName = rProperty.Name;
+
+ // old and new sortdescriptor
+ if ( rPropName == "IsSortInTable" )
+ {
+ if (auto b = o3tl::tryAccess<bool>(aValue))
+ {
+ rSortOpt.bTable = *b;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "Delimiter" )
+ {
+ sal_Unicode uChar;
+ sal_uInt16 nChar;
+ if (aValue >>= uChar)
+ {
+ rSortOpt.cDeli = uChar;
+ }
+ else if (aValue >>= nChar)
+ {
+ // For compatibility with BASIC, also accept an ANY containing
+ // an UNSIGNED SHORT:
+ rSortOpt.cDeli = nChar;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ // old sortdescriptor
+ else if ( rPropName == "SortColumns" )
+ {
+ bOldSortdescriptor = true;
+ bool bTemp(false);
+ if (aValue >>= bTemp)
+ {
+ rSortOpt.eDirection = bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "IsCaseSensitive" )
+ {
+ bOldSortdescriptor = true;
+ bool bTemp(false);
+ if (aValue >>= bTemp)
+ {
+ rSortOpt.bIgnoreCase = !bTemp;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "CollatorLocale" )
+ {
+ bOldSortdescriptor = true;
+ lang::Locale aLocale;
+ if (aValue >>= aLocale)
+ {
+ rSortOpt.nLanguage = LanguageTag::convertToLanguageType( aLocale);
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("CollatorAlgorithm") &&
+ rPropName.getLength() == 18 &&
+ (rPropName[17] >= '0' && rPropName[17] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[17];
+ nIndex -= '0';
+ OUString aText;
+ if ((aValue >>= aText) && nIndex < 3)
+ {
+ aKeys[nIndex]->sSortType = aText;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("SortRowOrColumnNo") &&
+ rPropName.getLength() == 18 &&
+ (rPropName[17] >= '0' && rPropName[17] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[17];
+ nIndex -= '0';
+ sal_Int16 nCol = -1;
+ if (aValue.getValueType() == ::cppu::UnoType<sal_Int16>::get()
+ && nIndex < 3)
+ {
+ aValue >>= nCol;
+ }
+ if (nCol >= 0)
+ {
+ aKeys[nIndex]->nColumnId = nCol;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("IsSortNumeric") &&
+ rPropName.getLength() == 14 &&
+ (rPropName[13] >= '0' && rPropName[13] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[13];
+ nIndex = nIndex - '0';
+ auto bTemp = o3tl::tryAccess<bool>(aValue);
+ if (bTemp && nIndex < 3)
+ {
+ aKeys[nIndex]->bIsNumeric = *bTemp;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if (rPropName.startsWith("IsSortAscending") &&
+ rPropName.getLength() == 16 &&
+ (rPropName[15] >= '0' && rPropName[15] <= '9'))
+ {
+ bOldSortdescriptor = true;
+ sal_uInt16 nIndex = rPropName[15];
+ nIndex -= '0';
+ auto bTemp = o3tl::tryAccess<bool>(aValue);
+ if (bTemp && nIndex < 3)
+ {
+ aKeys[nIndex]->eSortOrder = (*bTemp)
+ ? SwSortOrder::Ascending : SwSortOrder::Descending;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ // new sortdescriptor
+ else if ( rPropName == "IsSortColumns" )
+ {
+ bNewSortdescriptor = true;
+ if (auto bTemp = o3tl::tryAccess<bool>(aValue))
+ {
+ rSortOpt.eDirection = *bTemp ? SwSortDirection::Columns : SwSortDirection::Rows;
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else if ( rPropName == "SortFields" )
+ {
+ bNewSortdescriptor = true;
+ uno::Sequence < table::TableSortField > aFields;
+ if (aValue >>= aFields)
+ {
+ sal_Int32 nCount(aFields.getLength());
+ if (nCount <= 3)
+ {
+ table::TableSortField* pFields = aFields.getArray();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ rSortOpt.bIgnoreCase = !pFields[i].IsCaseSensitive;
+ rSortOpt.nLanguage =
+ LanguageTag::convertToLanguageType( pFields[i].CollatorLocale );
+ aKeys[i]->sSortType = pFields[i].CollatorAlgorithm;
+ aKeys[i]->nColumnId =
+ o3tl::narrowing<sal_uInt16>(pFields[i].Field);
+ aKeys[i]->bIsNumeric = (pFields[i].FieldType ==
+ table::TableSortFieldType_NUMERIC);
+ aKeys[i]->eSortOrder = (pFields[i].IsAscending)
+ ? SwSortOrder::Ascending : SwSortOrder::Descending;
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ else
+ {
+ bRet = false;
+ }
+ }
+ }
+
+ if (bNewSortdescriptor && bOldSortdescriptor)
+ {
+ OSL_FAIL("someone tried to set the old deprecated and "
+ "the new sortdescriptor");
+ bRet = false;
+ }
+
+ if (aKey1.nColumnId != USHRT_MAX)
+ {
+ rSortOpt.aKeys.push_back(aKey1);
+ }
+ if (aKey2.nColumnId != USHRT_MAX)
+ {
+ rSortOpt.aKeys.push_back(aKey2);
+ }
+ if (aKey3.nColumnId != USHRT_MAX)
+ {
+ rSortOpt.aKeys.push_back(aKey3);
+ }
+
+ return bRet && !rSortOpt.aKeys.empty();
+}
+
+void SAL_CALL
+SwXTextCursor::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ if (!rUnoCursor.HasMark())
+ return;
+
+ SwSortOptions aSortOpt;
+ if (!SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
+ {
+ throw uno::RuntimeException("Bad sort properties");
+ }
+ UnoActionContext aContext( &rUnoCursor.GetDoc() );
+
+ SwPosition & rStart = *rUnoCursor.Start();
+ SwPosition & rEnd = *rUnoCursor.End();
+
+ SwNodeIndex aPrevIdx( rStart.nNode, -1 );
+ const SwNodeOffset nOffset = rEnd.nNode.GetIndex() - rStart.nNode.GetIndex();
+ const sal_Int32 nCntStt = rStart.nContent.GetIndex();
+
+ rUnoCursor.GetDoc().SortText(rUnoCursor, aSortOpt);
+
+ // update selection
+ rUnoCursor.DeleteMark();
+ rUnoCursor.GetPoint()->nNode.Assign( aPrevIdx.GetNode(), +1 );
+ SwContentNode *const pCNd = rUnoCursor.GetContentNode();
+ sal_Int32 nLen = pCNd->Len();
+ if (nLen > nCntStt)
+ {
+ nLen = nCntStt;
+ }
+ rUnoCursor.GetPoint()->nContent.Assign(pCNd, nLen );
+ rUnoCursor.SetMark();
+
+ rUnoCursor.GetPoint()->nNode += nOffset;
+ SwContentNode *const pCNd2 = rUnoCursor.GetContentNode();
+ rUnoCursor.GetPoint()->nContent.Assign( pCNd2, pCNd2->Len() );
+
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXTextCursor::createContentEnumeration(const OUString& rServiceName)
+{
+ SolarMutexGuard g;
+ if (rServiceName != "com.sun.star.text.TextContent")
+ throw uno::RuntimeException();
+ SwUnoCursor& rUnoCursor( GetCursorOrThrow() );
+ return SwXParaFrameEnumeration::Create(rUnoCursor, PARAFRAME_PORTION_TEXTRANGE);
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXTextCursor::createEnumeration()
+{
+ SolarMutexGuard g;
+
+ SwUnoCursor & rUnoCursor( GetCursorOrThrow() );
+
+ SwXText* pParentText = comphelper::getFromUnoTunnel<SwXText>(m_xParentText);
+ OSL_ENSURE(pParentText, "parent is not a SwXText");
+ if (!pParentText)
+ {
+ throw uno::RuntimeException();
+ }
+
+ auto pNewCursor(rUnoCursor.GetDoc().CreateUnoCursor(*rUnoCursor.GetPoint()) );
+ if (rUnoCursor.HasMark())
+ {
+ pNewCursor->SetMark();
+ *pNewCursor->GetMark() = *rUnoCursor.GetMark();
+ }
+ const CursorType eSetType = (CursorType::TableText == m_eType)
+ ? CursorType::SelectionInTable : CursorType::Selection;
+ return SwXParagraphEnumeration::Create(pParentText, pNewCursor, eSetType);
+}
+
+uno::Type SAL_CALL
+SwXTextCursor::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXTextCursor::hasElements()
+{
+ return true;
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextCursor::getAvailableServiceNames()
+{
+ uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
+ return aRet;
+}
+
+IMPLEMENT_FORWARD_REFCOUNT( SwXTextCursor,SwXTextCursor_Base )
+
+uno::Any SAL_CALL
+SwXTextCursor::queryInterface(const uno::Type& rType)
+{
+ return (rType == cppu::UnoType<lang::XUnoTunnel>::get())
+ ? OTextCursorHelper::queryInterface(rType)
+ : SwXTextCursor_Base::queryInterface(rType);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoobj2.cxx b/sw/source/core/unocore/unoobj2.cxx
new file mode 100644
index 000000000..ddf4cca69
--- /dev/null
+++ b/sw/source/core/unocore/unoobj2.cxx
@@ -0,0 +1,1853 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/listener.hxx>
+#include <svx/svdobj.hxx>
+#include <vcl/svapp.hxx>
+
+#include <anchoredobject.hxx>
+#include <swtypes.hxx>
+#include <hintids.hxx>
+#include <IMark.hxx>
+#include <frmfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <textboxhelper.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <swundo.hxx>
+#include <rootfrm.hxx>
+#include <ftnidx.hxx>
+#include <pam.hxx>
+#include <swtblfmt.hxx>
+#include <docsh.hxx>
+#include <pagedesc.hxx>
+#include <cntfrm.hxx>
+#include <unoparaframeenum.hxx>
+#include <unofootnote.hxx>
+#include <unotextbodyhf.hxx>
+#include <unotextrange.hxx>
+#include <unoparagraph.hxx>
+#include <unomap.hxx>
+#include <unoport.hxx>
+#include <unocrsrhelper.hxx>
+#include <unotbl.hxx>
+#include <fmtanchr.hxx>
+#include <flypos.hxx>
+#include <txtftn.hxx>
+#include <fmtftn.hxx>
+#include <fmtcntnt.hxx>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unoframe.hxx>
+#include <fmthdft.hxx>
+#include <fmtflcnt.hxx>
+#include <vector>
+#include <sortedobjs.hxx>
+#include <frameformats.hxx>
+#include <algorithm>
+#include <iterator>
+
+using namespace ::com::sun::star;
+
+namespace sw {
+
+void DeepCopyPaM(SwPaM const & rSource, SwPaM & rTarget)
+{
+ rTarget = rSource;
+
+ if (rSource.GetNext() == &rSource)
+ return;
+
+ SwPaM *pPam = const_cast<SwPaM*>(rSource.GetNext());
+ do
+ {
+ // create new PaM
+ SwPaM *const pNew = new SwPaM(*pPam, nullptr);
+ // insert into ring
+ pNew->MoveTo(&rTarget);
+ pPam = pPam->GetNext();
+ }
+ while (pPam != &rSource);
+}
+
+} // namespace sw
+
+namespace {
+
+struct FrameClientSortListLess
+{
+ bool operator() (FrameClientSortListEntry const& r1,
+ FrameClientSortListEntry const& r2) const
+ {
+ return (r1.nIndex < r2.nIndex)
+ || ((r1.nIndex == r2.nIndex) && (r1.nOrder < r2.nOrder));
+ }
+};
+
+ void lcl_CollectFrameAtNodeWithLayout(const SwContentFrame* pCFrame,
+ FrameClientSortList_t& rFrames,
+ const RndStdIds nAnchorType)
+ {
+ auto pObjs = pCFrame->GetDrawObjs();
+ if(!pObjs)
+ return;
+ for(const auto pAnchoredObj : *pObjs)
+ {
+ SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
+ // Filter out textboxes, which are not interesting at a UNO level.
+ if(SwTextBoxHelper::isTextBox(&rFormat, RES_FLYFRMFMT))
+ continue;
+ if(rFormat.GetAnchor().GetAnchorId() == nAnchorType)
+ {
+ const auto nIdx =
+ rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex();
+ const auto nOrder = rFormat.GetAnchor().GetOrder();
+ FrameClientSortListEntry entry(nIdx, nOrder, std::make_shared<sw::FrameClient>(&rFormat));
+ rFrames.push_back(entry);
+ }
+ }
+ }
+}
+
+
+void CollectFrameAtNode( const SwNodeIndex& rIdx,
+ FrameClientSortList_t& rFrames,
+ const bool bAtCharAnchoredObjs )
+{
+ // _bAtCharAnchoredObjs:
+ // <true>: at-character anchored objects are collected
+ // <false>: at-paragraph anchored objects are collected
+
+ // search all borders, images, and OLEs that are connected to the paragraph
+ SwDoc& rDoc = rIdx.GetNode().GetDoc();
+
+ const auto nChkType = bAtCharAnchoredObjs ? RndStdIds::FLY_AT_CHAR : RndStdIds::FLY_AT_PARA;
+ const SwContentFrame* pCFrame;
+ const SwContentNode* pCNd;
+ if( rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() &&
+ nullptr != (pCNd = rIdx.GetNode().GetContentNode()) &&
+ nullptr != (pCFrame = pCNd->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout())) )
+ {
+ lcl_CollectFrameAtNodeWithLayout(pCFrame, rFrames, nChkType);
+ }
+ else
+ {
+ const SwFrameFormats& rFormats = *rDoc.GetSpzFrameFormats();
+ const size_t nSize = rFormats.size();
+ for ( size_t i = 0; i < nSize; i++)
+ {
+ const SwFrameFormat* pFormat = rFormats[ i ];
+ const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
+ const SwPosition* pAnchorPos;
+ if( rAnchor.GetAnchorId() == nChkType &&
+ nullptr != (pAnchorPos = rAnchor.GetContentAnchor()) &&
+ pAnchorPos->nNode == rIdx )
+ {
+
+ // OD 2004-05-07 #i28701# - determine insert position for
+ // sorted <rFrameArr>
+ const sal_Int32 nIndex = pAnchorPos->nContent.GetIndex();
+ sal_uInt32 nOrder = rAnchor.GetOrder();
+
+ FrameClientSortListEntry entry(nIndex, nOrder, std::make_shared<sw::FrameClient>(const_cast<SwFrameFormat*>(pFormat)));
+ rFrames.push_back(entry);
+ }
+ }
+ std::sort(rFrames.begin(), rFrames.end(), FrameClientSortListLess());
+ }
+}
+
+UnoActionContext::UnoActionContext(SwDoc *const pDoc)
+ : m_pDoc(pDoc)
+{
+ SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if (pRootFrame)
+ {
+ pRootFrame->StartAllAction();
+ }
+}
+
+UnoActionContext::~UnoActionContext() COVERITY_NOEXCEPT_FALSE
+{
+ // Doc may already have been removed here
+ if (m_pDoc)
+ {
+ SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if (pRootFrame)
+ {
+ pRootFrame->EndAllAction();
+ }
+ }
+}
+
+static void lcl_RemoveImpl(SwDoc *const pDoc)
+{
+ assert(pDoc);
+ SwRootFrame *const pRootFrame = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if (pRootFrame)
+ {
+ pRootFrame->UnoRemoveAllActions();
+ }
+}
+
+UnoActionRemoveContext::UnoActionRemoveContext(SwDoc *const pDoc)
+ : m_pDoc(pDoc)
+{
+ lcl_RemoveImpl(m_pDoc);
+}
+
+static SwDoc * lcl_IsNewStyleTable(SwUnoTableCursor const& rCursor)
+{
+ SwTableNode *const pTableNode = rCursor.GetNode().FindTableNode();
+ return (pTableNode && !pTableNode->GetTable().IsNewModel())
+ ? &rCursor.GetDoc()
+ : nullptr;
+}
+
+UnoActionRemoveContext::UnoActionRemoveContext(SwUnoTableCursor const& rCursor)
+ : m_pDoc(lcl_IsNewStyleTable(rCursor))
+{
+ // this insanity is only necessary for old-style tables
+ // because SwRootFrame::MakeTableCursors() creates the table cursor for these
+ if (m_pDoc)
+ {
+ lcl_RemoveImpl(m_pDoc);
+ }
+}
+
+UnoActionRemoveContext::~UnoActionRemoveContext() COVERITY_NOEXCEPT_FALSE
+{
+ if (m_pDoc)
+ {
+ SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+ if (pRootFrame)
+ {
+ pRootFrame->UnoRestoreAllActions();
+ }
+ }
+}
+
+void SwUnoCursorHelper::SetCursorAttr(SwPaM & rPam,
+ const SfxItemSet& rSet,
+ const SetAttrMode nAttrMode, const bool bTableMode)
+{
+ const SetAttrMode nFlags = nAttrMode | SetAttrMode::APICALL;
+ SwDoc& rDoc = rPam.GetDoc();
+ //StartEndAction
+ UnoActionContext aAction(&rDoc);
+ if (rPam.GetNext() != &rPam) // Ring of Cursors
+ {
+ rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSATTR, nullptr);
+
+ for(SwPaM& rCurrent : rPam.GetRingContainer())
+ {
+ if (rCurrent.HasMark() &&
+ ( bTableMode ||
+ (*rCurrent.GetPoint() != *rCurrent.GetMark()) ))
+ {
+ rDoc.getIDocumentContentOperations().InsertItemSet(rCurrent, rSet, nFlags);
+ }
+ }
+
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr);
+ }
+ else
+ {
+ rDoc.getIDocumentContentOperations().InsertItemSet( rPam, rSet, nFlags );
+ }
+
+ if( rSet.GetItemState( RES_PARATR_OUTLINELEVEL, false ) >= SfxItemState::DEFAULT )
+ {
+ SwTextNode * pTmpNode = rPam.GetNode().GetTextNode();
+ if ( pTmpNode )
+ {
+ rPam.GetDoc().GetNodes().UpdateOutlineNode( *pTmpNode );
+ }
+ }
+}
+
+// #i63870#
+// split third parameter <bCurrentAttrOnly> into new parameters <bOnlyTextAttr>
+// and <bGetFromChrFormat> to get better control about resulting <SfxItemSet>
+void SwUnoCursorHelper::GetCursorAttr(SwPaM & rPam,
+ SfxItemSet & rSet, const bool bOnlyTextAttr, const bool bGetFromChrFormat)
+{
+ static const sal_uLong nMaxLookup = 1000;
+ SfxItemSet aSet( *rSet.GetPool(), rSet.GetRanges() );
+ SfxItemSet *pSet = &rSet;
+ for(SwPaM& rCurrent : rPam.GetRingContainer())
+ {
+ SwPosition const & rStart( *rCurrent.Start() );
+ SwPosition const & rEnd( *rCurrent.End() );
+ const SwNodeOffset nSttNd = rStart.nNode.GetIndex();
+ const SwNodeOffset nEndNd = rEnd .nNode.GetIndex();
+
+ if (nEndNd - nSttNd >= SwNodeOffset(nMaxLookup))
+ {
+ rSet.ClearItem();
+ rSet.InvalidateAllItems();
+ return;// uno::Any();
+ }
+
+ // the first node inserts the values into the get set
+ // all other nodes merge their values into the get set
+ for (SwNodeOffset n = nSttNd; n <= nEndNd; ++n)
+ {
+ SwNode *const pNd = rPam.GetDoc().GetNodes()[ n ];
+ switch (pNd->GetNodeType())
+ {
+ case SwNodeType::Text:
+ {
+ const sal_Int32 nStart = (n == nSttNd)
+ ? rStart.nContent.GetIndex() : 0;
+ const sal_Int32 nEnd = (n == nEndNd)
+ ? rEnd.nContent.GetIndex()
+ : pNd->GetTextNode()->GetText().getLength();
+ pNd->GetTextNode()->GetParaAttr(*pSet, nStart, nEnd, bOnlyTextAttr, bGetFromChrFormat);
+ }
+ break;
+
+ case SwNodeType::Grf:
+ case SwNodeType::Ole:
+ static_cast<SwContentNode*>(pNd)->GetAttr( *pSet );
+ break;
+
+ default:
+ continue; // skip this node
+ }
+
+ if (pSet != &rSet)
+ {
+ rSet.MergeValues( aSet );
+ }
+ else
+ {
+ pSet = &aSet;
+ }
+
+ if (aSet.Count())
+ {
+ aSet.ClearItem();
+ }
+ }
+ }
+}
+
+namespace {
+
+struct SwXParagraphEnumerationImpl final : public SwXParagraphEnumeration
+{
+ uno::Reference< text::XText > const m_xParentText;
+ const CursorType m_eCursorType;
+ /// Start node of the cell _or_ table the enumeration belongs to.
+ /// Used to restrict the movement of the UNO cursor to the cell and its
+ /// embedded tables.
+ SwStartNode const*const m_pOwnStartNode;
+ SwTable const*const m_pOwnTable;
+ const SwNodeOffset m_nEndIndex;
+ sal_Int32 m_nFirstParaStart;
+ sal_Int32 m_nLastParaEnd;
+ bool m_bFirstParagraph;
+ uno::Reference< text::XTextContent > m_xNextPara;
+ sw::UnoCursorPointer m_pCursor;
+
+ SwXParagraphEnumerationImpl(
+ uno::Reference< text::XText > const& xParent,
+ const std::shared_ptr<SwUnoCursor>& pCursor,
+ const CursorType eType,
+ SwStartNode const*const pStartNode, SwTable const*const pTable)
+ : m_xParentText( xParent )
+ , m_eCursorType( eType )
+ // remember table and start node for later travelling
+ // (used in export of tables in tables)
+ , m_pOwnStartNode( pStartNode )
+ // for import of tables in tables we have to remember the actual
+ // table and start node of the current position in the enumeration.
+ , m_pOwnTable( pTable )
+ , m_nEndIndex( pCursor->End()->nNode.GetIndex() )
+ , m_nFirstParaStart( -1 )
+ , m_nLastParaEnd( -1 )
+ , m_bFirstParagraph( true )
+ , m_pCursor(pCursor)
+ {
+ OSL_ENSURE(m_xParentText.is(), "SwXParagraphEnumeration: no parent?");
+ assert( !((CursorType::SelectionInTable == eType)
+ || (CursorType::TableText == eType))
+ || (m_pOwnTable && m_pOwnStartNode));
+
+ if ((CursorType::Selection == m_eCursorType) ||
+ (CursorType::SelectionInTable == m_eCursorType))
+ {
+ SwUnoCursor & rCursor = GetCursor();
+ rCursor.Normalize();
+ m_nFirstParaStart = rCursor.GetPoint()->nContent.GetIndex();
+ m_nLastParaEnd = rCursor.GetMark()->nContent.GetIndex();
+ rCursor.DeleteMark();
+ }
+ }
+
+ virtual ~SwXParagraphEnumerationImpl() override
+ { m_pCursor.reset(nullptr); }
+ virtual void SAL_CALL release() noexcept override
+ {
+ SolarMutexGuard g;
+ OWeakObject::release();
+ }
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ { return "SwXParagraphEnumeration"; }
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName) override
+ { return cppu::supportsService(this, rServiceName); };
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
+ { return {"com.sun.star.text.ParagraphEnumeration"}; };
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual css::uno::Any SAL_CALL nextElement() override;
+
+ SwUnoCursor& GetCursor()
+ { return *m_pCursor; }
+ /// @throws container::NoSuchElementException
+ /// @throws lang::WrappedTargetException
+ /// @throws uno::RuntimeException
+ uno::Reference< text::XTextContent > NextElement_Impl();
+
+ /**
+ * Determines if the last element in the enumeration should be ignored or
+ * not.
+ */
+ bool IgnoreLastElement(SwUnoCursor& rCursor, bool bMovedFromTable);
+};
+
+}
+
+rtl::Reference<SwXParagraphEnumeration> SwXParagraphEnumeration::Create(
+ uno::Reference< text::XText > const& xParent,
+ const std::shared_ptr<SwUnoCursor>& pCursor,
+ const CursorType eType,
+ SwTableBox const*const pTableBox)
+{
+ SwStartNode const* pStartNode(nullptr);
+ SwTable const* pTable(nullptr);
+ assert((eType == CursorType::TableText) == (pTableBox != nullptr));
+ switch (eType)
+ {
+ case CursorType::TableText:
+ {
+ pStartNode = pTableBox->GetSttNd();
+ pTable = & pStartNode->FindTableNode()->GetTable();
+ break;
+ }
+ case CursorType::SelectionInTable:
+ {
+ SwTableNode const*const pTableNode(
+ pCursor->GetPoint()->nNode.GetNode().FindTableNode());
+ pStartNode = pTableNode;
+ pTable = & pTableNode->GetTable();
+ break;
+ }
+ default:
+ break;
+ }
+ return new SwXParagraphEnumerationImpl(xParent, pCursor, eType, pStartNode, pTable);
+}
+
+sal_Bool SAL_CALL
+SwXParagraphEnumerationImpl::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ return m_bFirstParagraph || m_xNextPara.is();
+}
+
+//!! compare to SwShellTableCursor::FillRects() in viscrs.cxx
+static SwTableNode *
+lcl_FindTopLevelTable(
+ SwTableNode *const pTableNode, SwTable const*const pOwnTable)
+{
+ // find top-most table in current context (section) level
+
+ SwTableNode * pLast = pTableNode;
+ for (SwTableNode* pTmp = pLast;
+ pTmp != nullptr && &pTmp->GetTable() != pOwnTable; /* we must not go up higher than the own table! */
+ pTmp = pTmp->StartOfSectionNode()->FindTableNode() )
+ {
+ pLast = pTmp;
+ }
+ return pLast;
+}
+
+static bool
+lcl_CursorIsInSection(
+ SwUnoCursor const*const pUnoCursor, SwStartNode const*const pOwnStartNode)
+{
+ // returns true if the cursor is in the section (or in a sub section!)
+ // represented by pOwnStartNode
+
+ bool bRes = true;
+ if (pUnoCursor && pOwnStartNode)
+ {
+ const SwEndNode * pOwnEndNode = pOwnStartNode->EndOfSectionNode();
+ bRes = pOwnStartNode->GetIndex() <= pUnoCursor->Start()->nNode.GetIndex() &&
+ pUnoCursor->End()->nNode.GetIndex() <= pOwnEndNode->GetIndex();
+ }
+ return bRes;
+}
+
+bool SwXParagraphEnumerationImpl::IgnoreLastElement(SwUnoCursor& rCursor, bool bMovedFromTable)
+{
+ // Ignore the last element of a selection enumeration if this is a stub
+ // paragraph (directly after table, selection ends at paragraph start).
+
+ if (rCursor.Start()->nNode.GetIndex() != m_nEndIndex)
+ return false;
+
+ if (m_eCursorType != CursorType::Selection)
+ return false;
+
+ if (!bMovedFromTable)
+ return false;
+
+ return m_nLastParaEnd == 0;
+}
+
+uno::Reference< text::XTextContent >
+SwXParagraphEnumerationImpl::NextElement_Impl()
+{
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ // check for exceeding selections
+ if (!m_bFirstParagraph &&
+ ((CursorType::Selection == m_eCursorType) ||
+ (CursorType::SelectionInTable == m_eCursorType)))
+ {
+ SwPosition* pStart = rUnoCursor.Start();
+ auto aNewCursor(rUnoCursor.GetDoc().CreateUnoCursor(*pStart));
+ // one may also go into tables here
+ if (CursorType::SelectionInTable != m_eCursorType)
+ {
+ aNewCursor->SetRemainInSection( false );
+ }
+
+ // os 2005-01-14: This part is only necessary to detect movements out
+ // of a selection; if there is no selection we don't have to care
+ SwTableNode *const pTableNode = aNewCursor->GetNode().FindTableNode();
+ bool bMovedFromTable = false;
+ if (CursorType::SelectionInTable != m_eCursorType && pTableNode)
+ {
+ aNewCursor->GetPoint()->nNode = pTableNode->EndOfSectionIndex();
+ aNewCursor->Move(fnMoveForward, GoInNode);
+ bMovedFromTable = true;
+ }
+ else
+ {
+ aNewCursor->MovePara(GoNextPara, fnParaStart);
+ }
+ if (m_nEndIndex < aNewCursor->Start()->nNode.GetIndex())
+ {
+ return nullptr;
+ }
+
+ if (IgnoreLastElement(*aNewCursor, bMovedFromTable))
+ {
+ return nullptr;
+ }
+ }
+
+ bool bInTable = false;
+ if (!m_bFirstParagraph)
+ {
+ rUnoCursor.SetRemainInSection( false );
+ // what to do if already in a table?
+ SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
+ pTableNode = lcl_FindTopLevelTable( pTableNode, m_pOwnTable );
+ if (pTableNode && (&pTableNode->GetTable() != m_pOwnTable))
+ {
+ // this is a foreign table: go to end
+ rUnoCursor.GetPoint()->nNode = pTableNode->EndOfSectionIndex();
+ if (!rUnoCursor.Move(fnMoveForward, GoInNode))
+ {
+ return nullptr;
+ }
+ bInTable = true;
+ }
+ }
+
+ uno::Reference< text::XTextContent > xRef;
+ // the cursor must remain in the current section or a subsection
+ // before AND after the movement...
+ if (lcl_CursorIsInSection( &rUnoCursor, m_pOwnStartNode ) &&
+ (m_bFirstParagraph || bInTable ||
+ (rUnoCursor.MovePara(GoNextPara, fnParaStart) &&
+ lcl_CursorIsInSection( &rUnoCursor, m_pOwnStartNode ))))
+ {
+ if (m_eCursorType == CursorType::Selection || m_eCursorType == CursorType::SelectionInTable)
+ {
+ // This is a selection, check if the cursor would go past the end
+ // of the selection.
+ if (rUnoCursor.Start()->nNode.GetIndex() > m_nEndIndex)
+ return nullptr;
+ }
+
+ SwPosition* pStart = rUnoCursor.Start();
+ const sal_Int32 nFirstContent =
+ m_bFirstParagraph ? m_nFirstParaStart : -1;
+ const sal_Int32 nLastContent =
+ (m_nEndIndex == pStart->nNode.GetIndex()) ? m_nLastParaEnd : -1;
+
+ // position in a table, or in a simple paragraph?
+ SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
+ pTableNode = lcl_FindTopLevelTable( pTableNode, m_pOwnTable );
+ if (/*CursorType::TableText != eCursorType && CursorType::SelectionInTable != eCursorType && */
+ pTableNode && (&pTableNode->GetTable() != m_pOwnTable))
+ {
+ // this is a foreign table
+ SwFrameFormat* pTableFormat = pTableNode->GetTable().GetFrameFormat();
+ xRef = SwXTextTable::CreateXTextTable(pTableFormat);
+ }
+ else
+ {
+ text::XText *const pText = m_xParentText.get();
+ xRef = SwXParagraph::CreateXParagraph(rUnoCursor.GetDoc(),
+ pStart->nNode.GetNode().GetTextNode(),
+ static_cast<SwXText*>(pText), nFirstContent, nLastContent);
+ }
+ }
+
+ return xRef;
+}
+
+uno::Any SAL_CALL SwXParagraphEnumerationImpl::nextElement()
+{
+ SolarMutexGuard aGuard;
+ if (m_bFirstParagraph)
+ {
+ m_xNextPara = NextElement_Impl();
+ m_bFirstParagraph = false;
+ }
+ const uno::Reference< text::XTextContent > xRef = m_xNextPara;
+ if (!xRef.is())
+ {
+ throw container::NoSuchElementException();
+ }
+ m_xNextPara = NextElement_Impl();
+
+ uno::Any aRet;
+ aRet <<= xRef;
+ return aRet;
+}
+
+class SwXTextRange::Impl
+ : public SvtListener
+{
+public:
+ const SfxItemPropertySet& m_rPropSet;
+ const enum RangePosition m_eRangePosition;
+ SwDoc& m_rDoc;
+ uno::Reference<text::XText> m_xParentText;
+ const SwFrameFormat* m_pTableOrSectionFormat;
+ const ::sw::mark::IMark* m_pMark;
+
+ Impl(SwDoc& rDoc, const enum RangePosition eRange,
+ SwFrameFormat* const pTableOrSectionFormat,
+ const uno::Reference<text::XText>& xParent = nullptr)
+ : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
+ , m_eRangePosition(eRange)
+ , m_rDoc(rDoc)
+ , m_xParentText(xParent)
+ , m_pTableOrSectionFormat(pTableOrSectionFormat)
+ , m_pMark(nullptr)
+ {
+ if (m_pTableOrSectionFormat)
+ {
+ assert(m_eRangePosition == RANGE_IS_TABLE || m_eRangePosition == RANGE_IS_SECTION);
+ StartListening(pTableOrSectionFormat->GetNotifier());
+ }
+ else
+ {
+ assert(m_eRangePosition != RANGE_IS_TABLE && m_eRangePosition != RANGE_IS_SECTION);
+ }
+ }
+
+ virtual ~Impl() override
+ {
+ // Impl owns the bookmark; delete it here: SolarMutex is locked
+ Invalidate();
+ }
+
+ void Invalidate()
+ {
+ if (m_pMark)
+ {
+ m_rDoc.getIDocumentMarkAccess()->deleteMark(m_pMark);
+ m_pMark = nullptr;
+ }
+ m_pTableOrSectionFormat = nullptr;
+ EndListeningAll();
+ }
+
+ const ::sw::mark::IMark* GetBookmark() const { return m_pMark; }
+ void SetMark(::sw::mark::IMark& rMark)
+ {
+ EndListeningAll();
+ m_pTableOrSectionFormat = nullptr;
+ m_pMark = &rMark;
+ StartListening(rMark.GetNotifier());
+ }
+
+protected:
+ virtual void Notify(const SfxHint&) override;
+};
+
+void SwXTextRange::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ EndListeningAll();
+ m_pTableOrSectionFormat = nullptr;
+ m_pMark = nullptr;
+ }
+}
+
+SwXTextRange::SwXTextRange(SwPaM const & rPam,
+ const uno::Reference< text::XText > & xParent,
+ const enum RangePosition eRange)
+ : m_pImpl( new SwXTextRange::Impl(rPam.GetDoc(), eRange, nullptr, xParent) )
+{
+ SetPositions(rPam);
+}
+
+SwXTextRange::SwXTextRange(SwTableFormat& rTableFormat)
+ : m_pImpl(
+ new SwXTextRange::Impl(*rTableFormat.GetDoc(), RANGE_IS_TABLE, &rTableFormat) )
+{
+ SwTable *const pTable = SwTable::FindTable( &rTableFormat );
+ SwTableNode *const pTableNode = pTable->GetTableNode();
+ SwPosition aPosition( *pTableNode );
+ SwPaM aPam( aPosition );
+
+ SetPositions( aPam );
+}
+
+SwXTextRange::SwXTextRange(SwSectionFormat& rSectionFormat)
+ : m_pImpl(
+ new SwXTextRange::Impl(*rSectionFormat.GetDoc(), RANGE_IS_SECTION, &rSectionFormat) )
+{
+ // no SetPositions here for now
+}
+
+SwXTextRange::~SwXTextRange()
+{
+}
+
+const SwDoc& SwXTextRange::GetDoc() const
+{
+ return m_pImpl->m_rDoc;
+}
+
+SwDoc& SwXTextRange::GetDoc()
+{
+ return m_pImpl->m_rDoc;
+}
+
+void SwXTextRange::Invalidate()
+{
+ m_pImpl->Invalidate();
+}
+
+void SwXTextRange::SetPositions(const SwPaM& rPam)
+{
+ m_pImpl->Invalidate();
+ IDocumentMarkAccess* const pMA = m_pImpl->m_rDoc.getIDocumentMarkAccess();
+ auto pMark = pMA->makeMark(rPam, OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK, sw::mark::InsertMode::New);
+ m_pImpl->SetMark(*pMark);
+}
+
+static void DeleteTable(SwDoc & rDoc, SwTable& rTable)
+{
+ SwSelBoxes aSelBoxes;
+ for (auto& rBox : rTable.GetTabSortBoxes())
+ {
+ aSelBoxes.insert(rBox);
+ }
+ // note: if the table is the content in the section, this will create
+ // a new text node - that's desirable here
+ rDoc.DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected);
+}
+
+void SwXTextRange::DeleteAndInsert(
+ const OUString& rText, const bool bForceExpandHints)
+{
+ if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
+ {
+ // setString on table not allowed
+ throw uno::RuntimeException("not possible for table");
+ }
+
+ const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
+ SwCursor aCursor(aPos, nullptr);
+
+ UnoActionContext aAction(& m_pImpl->m_rDoc);
+
+ if (RANGE_IS_SECTION == m_pImpl->m_eRangePosition)
+ {
+ SwSectionNode const* pSectionNode = m_pImpl->m_pTableOrSectionFormat ?
+ static_cast<SwSectionFormat const*>(m_pImpl->m_pTableOrSectionFormat)->GetSectionNode() :
+ nullptr;
+ if (!pSectionNode)
+ {
+ throw uno::RuntimeException("disposed?");
+ }
+ m_pImpl->m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+ SwNodeIndex const start(*pSectionNode);
+ SwNodeIndex const end(*start.GetNode().EndOfSectionNode());
+
+ // delete tables at start
+ for (SwNodeIndex i = start; i < end; )
+ {
+ SwNode & rNode(i.GetNode());
+ if (rNode.IsSectionNode())
+ {
+ ++i;
+ continue;
+ }
+ else if (SwTableNode *const pTableNode = rNode.GetTableNode())
+ {
+ DeleteTable(m_pImpl->m_rDoc, pTableNode->GetTable());
+ // where does that leave index? presumably behind?
+ }
+ else
+ {
+ assert(rNode.IsTextNode());
+ break;
+ }
+ }
+ // delete tables at end
+ for (SwNodeIndex i = end; start <= i; )
+ {
+ --i;
+ SwNode & rNode(i.GetNode());
+ if (rNode.IsEndNode())
+ {
+ if (SwTableNode *const pTableNode = rNode.StartOfSectionNode()->GetTableNode())
+ {
+ DeleteTable(m_pImpl->m_rDoc, pTableNode->GetTable());
+ }
+ else
+ {
+ assert(rNode.StartOfSectionNode()->IsSectionNode());
+ continue;
+ }
+ }
+ else
+ {
+ assert(rNode.IsTextNode());
+ break;
+ }
+ }
+ // now there should be a text node at the start and end of it!
+ *aCursor.GetPoint() = SwPosition(start);
+ aCursor.Move( fnMoveForward, GoInContent );
+ assert(aCursor.GetPoint()->nNode <= end);
+ aCursor.SetMark();
+ *aCursor.GetPoint() = SwPosition(end);
+ aCursor.Move( fnMoveBackward, GoInContent );
+ assert(start <= aCursor.GetPoint()->nNode);
+ }
+ else
+ {
+ if (!GetPositions(aCursor))
+ return;
+ m_pImpl->m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+ }
+
+ if (aCursor.HasMark())
+ {
+ m_pImpl->m_rDoc.getIDocumentContentOperations().DeleteAndJoin(aCursor);
+ }
+
+ if (!rText.isEmpty())
+ {
+ SwUnoCursorHelper::DocInsertStringSplitCR(
+ m_pImpl->m_rDoc, aCursor, rText, bForceExpandHints);
+
+ SwUnoCursorHelper::SelectPam(aCursor, true);
+ aCursor.Left(rText.getLength());
+ }
+ SetPositions(aCursor);
+ m_pImpl->m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+}
+
+const uno::Sequence< sal_Int8 > & SwXTextRange::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextRangeUnoTunnelId;
+ return theSwXTextRangeUnoTunnelId.getSeq();
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL
+SwXTextRange::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXTextRange>(rId, this);
+}
+
+OUString SAL_CALL
+SwXTextRange::getImplementationName()
+{
+ return "SwXTextRange";
+}
+
+sal_Bool SAL_CALL SwXTextRange::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextRange::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextRange",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex"
+ };
+}
+
+uno::Reference< text::XText > SAL_CALL
+SwXTextRange::getText()
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_xParentText.is() && m_pImpl->m_pTableOrSectionFormat)
+ {
+ std::optional<SwPosition> oPosition;
+ if (m_pImpl->m_eRangePosition == RANGE_IS_TABLE)
+ {
+ SwTable const*const pTable = SwTable::FindTable( m_pImpl->m_pTableOrSectionFormat );
+ SwTableNode const*const pTableNode = pTable->GetTableNode();
+ oPosition.emplace(*pTableNode);
+ }
+ else
+ {
+ assert(m_pImpl->m_eRangePosition == RANGE_IS_SECTION);
+ auto const pSectFormat(static_cast<SwSectionFormat const*>(m_pImpl->m_pTableOrSectionFormat));
+ oPosition.emplace(pSectFormat->GetContent().GetContentIdx()->GetNode());
+ }
+ m_pImpl->m_xParentText =
+ ::sw::CreateParentXText(m_pImpl->m_rDoc, *oPosition);
+ }
+ OSL_ENSURE(m_pImpl->m_xParentText.is(), "SwXTextRange::getText: no text");
+ return m_pImpl->m_xParentText;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextRange::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRet;
+ ::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
+ if (!m_pImpl->m_xParentText.is())
+ {
+ getText();
+ }
+ if(pBkmk)
+ {
+ SwPaM aPam(pBkmk->GetMarkStart());
+ xRet = new SwXTextRange(aPam, m_pImpl->m_xParentText);
+ }
+ else if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
+ {
+ // start and end are this, if it's a table
+ xRet = this;
+ }
+ else if (RANGE_IS_SECTION == m_pImpl->m_eRangePosition
+ && m_pImpl->m_pTableOrSectionFormat)
+ {
+ auto const pSectFormat(static_cast<SwSectionFormat const*>(m_pImpl->m_pTableOrSectionFormat));
+ SwPaM aPaM(*pSectFormat->GetContent().GetContentIdx());
+ aPaM.Move( fnMoveForward, GoInContent );
+ assert(aPaM.GetPoint()->nNode < *pSectFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionNode());
+ xRet = new SwXTextRange(aPaM, m_pImpl->m_xParentText);
+ }
+ else
+ {
+ throw uno::RuntimeException("disposed?");
+ }
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextRange::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRet;
+ ::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
+ if (!m_pImpl->m_xParentText.is())
+ {
+ getText();
+ }
+ if(pBkmk)
+ {
+ SwPaM aPam(pBkmk->GetMarkEnd());
+ xRet = new SwXTextRange(aPam, m_pImpl->m_xParentText);
+ }
+ else if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
+ {
+ // start and end are this, if it's a table
+ xRet = this;
+ }
+ else if (RANGE_IS_SECTION == m_pImpl->m_eRangePosition
+ && m_pImpl->m_pTableOrSectionFormat)
+ {
+ auto const pSectFormat(static_cast<SwSectionFormat const*>(m_pImpl->m_pTableOrSectionFormat));
+ SwPaM aPaM(*pSectFormat->GetContent().GetContentIdx()->GetNode().EndOfSectionNode());
+ aPaM.Move( fnMoveBackward, GoInContent );
+ assert(*pSectFormat->GetContent().GetContentIdx() < aPaM.GetPoint()->nNode);
+ xRet = new SwXTextRange(aPaM, m_pImpl->m_xParentText);
+ }
+ else
+ {
+ throw uno::RuntimeException("disposed?");
+ }
+ return xRet;
+}
+
+OUString SAL_CALL SwXTextRange::getString()
+{
+ SolarMutexGuard aGuard;
+
+ OUString sRet;
+ // for tables there is no bookmark, thus also no text
+ // one could export the table as ASCII here maybe?
+ SwPaM aPaM(GetDoc().GetNodes());
+ if (GetPositions(aPaM, sw::TextRangeMode::AllowNonTextNode) && aPaM.HasMark())
+ {
+ SwUnoCursorHelper::GetTextFromPam(aPaM, sRet);
+ }
+ return sRet;
+}
+
+void SAL_CALL SwXTextRange::setString(const OUString& rString)
+{
+ SolarMutexGuard aGuard;
+
+ DeleteAndInsert(rString, false);
+}
+
+bool SwXTextRange::GetPositions(SwPaM& rToFill, ::sw::TextRangeMode const eMode) const
+{
+ if (RANGE_IS_SECTION == m_pImpl->m_eRangePosition
+ && eMode == ::sw::TextRangeMode::AllowNonTextNode)
+ {
+ if (auto const pSectFormat = static_cast<SwSectionFormat const*>(m_pImpl->m_pTableOrSectionFormat))
+ {
+ SwNodeIndex const*const pSectionNode(pSectFormat->GetContent().GetContentIdx());
+ assert(pSectionNode);
+ assert(pSectionNode->GetNodes().IsDocNodes());
+ rToFill.GetPoint()->nNode = *pSectionNode;
+ ++rToFill.GetPoint()->nNode;
+ rToFill.GetPoint()->nContent.Assign(rToFill.GetPoint()->nNode.GetNode().GetContentNode(), 0);
+ rToFill.SetMark();
+ rToFill.GetMark()->nNode = *pSectionNode->GetNode().EndOfSectionNode();
+ --rToFill.GetMark()->nNode;
+ rToFill.GetMark()->nContent.Assign(rToFill.GetMark()->nNode.GetNode().GetContentNode(),
+ rToFill.GetMark()->nNode.GetNode().GetContentNode() ? rToFill.GetMark()->nNode.GetNode().GetContentNode()->Len() : 0);
+ return true;
+ }
+ }
+ ::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
+ if(pBkmk)
+ {
+ *rToFill.GetPoint() = pBkmk->GetMarkPos();
+ if(pBkmk->IsExpanded())
+ {
+ rToFill.SetMark();
+ *rToFill.GetMark() = pBkmk->GetOtherMarkPos();
+ }
+ else
+ {
+ rToFill.DeleteMark();
+ }
+ return true;
+ }
+ return false;
+}
+
+namespace sw {
+
+bool XTextRangeToSwPaM( SwUnoInternalPaM & rToFill,
+ const uno::Reference<text::XTextRange> & xTextRange,
+ ::sw::TextRangeMode const eMode)
+{
+ bool bRet = false;
+
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+ SwXTextPortion* pPortion = comphelper::getFromUnoTunnel<SwXTextPortion>(xRangeTunnel);
+ SwXText* pText = comphelper::getFromUnoTunnel<SwXText>(xRangeTunnel);
+ SwXParagraph* pPara = comphelper::getFromUnoTunnel<SwXParagraph>(xRangeTunnel);
+ SwXHeadFootText* pHeadText
+ = eMode == TextRangeMode::AllowTableNode ? dynamic_cast<SwXHeadFootText*>(pText) : nullptr;
+
+ // if it's a text then create a temporary cursor there and re-use
+ // the pCursor variable
+ // #i108489#: Reference in outside scope to keep cursor alive
+ uno::Reference< text::XTextCursor > xTextCursor;
+ if (pHeadText)
+ {
+ // if it is a header / footer text, and eMode == TextRangeMode::AllowTableNode
+ // then set the cursor to the beginning of the text
+ // if it is started with a table then set into the table
+ xTextCursor.set(pHeadText->CreateTextCursor(true));
+ xTextCursor->gotoEnd(true);
+ pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xTextCursor);
+ pCursor->GetPaM()->Normalize();
+ }
+ else
+ if (pText)
+ {
+ xTextCursor.set( pText->CreateCursor() );
+ xTextCursor->gotoEnd(true);
+ pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xTextCursor);
+ }
+ if(pRange && &pRange->GetDoc() == &rToFill.GetDoc())
+ {
+ bRet = pRange->GetPositions(rToFill, eMode);
+ }
+ else
+ {
+ if (pPara)
+ {
+ bRet = pPara->SelectPaM(rToFill);
+ }
+ else
+ {
+ SwDoc* const pDoc = pCursor ? pCursor->GetDoc()
+ : (pPortion ? &pPortion->GetCursor().GetDoc() : nullptr);
+ const SwPaM* const pUnoCursor = pCursor ? pCursor->GetPaM()
+ : (pPortion ? &pPortion->GetCursor() : nullptr);
+ if (pUnoCursor && pDoc == &rToFill.GetDoc())
+ {
+ OSL_ENSURE(!pUnoCursor->IsMultiSelection(),
+ "what to do about rings?");
+ bRet = true;
+ *rToFill.GetPoint() = *pUnoCursor->GetPoint();
+ if (pUnoCursor->HasMark())
+ {
+ rToFill.SetMark();
+ *rToFill.GetMark() = *pUnoCursor->GetMark();
+ }
+ else
+ rToFill.DeleteMark();
+ }
+ }
+ }
+ return bRet;
+}
+
+static bool
+lcl_IsStartNodeInFormat(const bool bHeader, SwStartNode const *const pSttNode,
+ SwFrameFormat const*const pFrameFormat, SwFrameFormat*& rpFormat)
+{
+ bool bRet = false;
+ const SfxItemSet& rSet = pFrameFormat->GetAttrSet();
+ const SfxPoolItem* pItem;
+ if (SfxItemState::SET == rSet.GetItemState(
+ bHeader ? sal_uInt16(RES_HEADER) : sal_uInt16(RES_FOOTER),
+ true, &pItem))
+ {
+ SfxPoolItem *const pItemNonConst(const_cast<SfxPoolItem *>(pItem));
+ SwFrameFormat *const pHeadFootFormat = bHeader ?
+ static_cast<SwFormatHeader*>(pItemNonConst)->GetHeaderFormat() :
+ static_cast<SwFormatFooter*>(pItemNonConst)->GetFooterFormat();
+ if (pHeadFootFormat)
+ {
+ const SwFormatContent& rFlyContent = pHeadFootFormat->GetContent();
+ // tdf#146248 avoid Undo crash at shared first page
+ if ( !rFlyContent.GetContentIdx() )
+ return false;
+ const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
+ SwStartNode const*const pCurSttNode = rNode.FindSttNodeByType(
+ bHeader ? SwHeaderStartNode : SwFooterStartNode);
+ if (pCurSttNode && (pCurSttNode == pSttNode))
+ {
+ rpFormat = pHeadFootFormat;
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+} // namespace sw
+
+uno::Reference< text::XTextRange >
+SwXTextRange::CreateXTextRange(
+ SwDoc & rDoc, const SwPosition& rPos, const SwPosition *const pMark)
+{
+ const uno::Reference<text::XText> xParentText(
+ ::sw::CreateParentXText(rDoc, rPos));
+ const auto pNewCursor(rDoc.CreateUnoCursor(rPos));
+ if(pMark)
+ {
+ pNewCursor->SetMark();
+ *pNewCursor->GetMark() = *pMark;
+ }
+ const bool isCell( dynamic_cast<SwXCell*>(xParentText.get()) );
+ const uno::Reference< text::XTextRange > xRet(
+ new SwXTextRange(*pNewCursor, xParentText,
+ isCell ? RANGE_IN_CELL : RANGE_IN_TEXT) );
+ return xRet;
+}
+
+namespace sw {
+
+uno::Reference< text::XText >
+CreateParentXText(SwDoc & rDoc, const SwPosition& rPos)
+{
+ uno::Reference< text::XText > xParentText;
+ SwStartNode* pSttNode = rPos.nNode.GetNode().StartOfSectionNode();
+ while(pSttNode && pSttNode->IsSectionNode())
+ {
+ pSttNode = pSttNode->StartOfSectionNode();
+ }
+ SwStartNodeType eType = pSttNode ? pSttNode->GetStartNodeType() : SwNormalStartNode;
+ switch(eType)
+ {
+ case SwTableBoxStartNode:
+ {
+ SwTableNode const*const pTableNode = pSttNode->FindTableNode();
+ SwFrameFormat *const pTableFormat =
+ pTableNode->GetTable().GetFrameFormat();
+ SwTableBox *const pBox = pSttNode->GetTableBox();
+
+ xParentText = pBox
+ ? SwXCell::CreateXCell( pTableFormat, pBox )
+ : new SwXCell( pTableFormat, *pSttNode );
+ }
+ break;
+ case SwFlyStartNode:
+ {
+ SwFrameFormat *const pFormat = pSttNode->GetFlyFormat();
+ if (nullptr != pFormat)
+ {
+ xParentText.set(SwXTextFrame::CreateXTextFrame(rDoc, pFormat),
+ uno::UNO_QUERY);
+ }
+ }
+ break;
+ case SwHeaderStartNode:
+ case SwFooterStartNode:
+ {
+ const bool bHeader = (SwHeaderStartNode == eType);
+ const size_t nPDescCount = rDoc.GetPageDescCnt();
+ for(size_t i = 0; i < nPDescCount; i++)
+ {
+ const SwPageDesc& rDesc = rDoc.GetPageDesc( i );
+
+ const SwFrameFormat* pFrameFormatMaster = &rDesc.GetMaster();
+ const SwFrameFormat* pFrameFormatLeft = &rDesc.GetLeft();
+ const SwFrameFormat* pFrameFormatFirstMaster = &rDesc.GetFirstMaster();
+ const SwFrameFormat* pFrameFormatFirstLeft = &rDesc.GetFirstLeft();
+
+ SwFrameFormat* pHeadFootFormat = nullptr;
+ if (!lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatMaster,
+ pHeadFootFormat))
+ {
+ if (!lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatLeft,
+ pHeadFootFormat))
+ {
+ if (!lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatFirstMaster,
+ pHeadFootFormat))
+ {
+ lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatFirstLeft, pHeadFootFormat);
+ }
+ }
+ }
+
+ if (pHeadFootFormat)
+ {
+ xParentText = SwXHeadFootText::CreateXHeadFootText(
+ *pHeadFootFormat, bHeader);
+ }
+ }
+ }
+ break;
+ case SwFootnoteStartNode:
+ {
+ const size_t nFootnoteCnt = rDoc.GetFootnoteIdxs().size();
+ for (size_t n = 0; n < nFootnoteCnt; ++n )
+ {
+ const SwTextFootnote* pTextFootnote = rDoc.GetFootnoteIdxs()[ n ];
+ const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
+ pTextFootnote = rFootnote.GetTextFootnote();
+
+ if (pSttNode == pTextFootnote->GetStartNode()->GetNode().
+ FindSttNodeByType(SwFootnoteStartNode))
+ {
+ xParentText.set(SwXFootnote::CreateXFootnote(rDoc,
+ &const_cast<SwFormatFootnote&>(rFootnote)), uno::UNO_QUERY);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ {
+ // then it is the body text
+ const uno::Reference<frame::XModel> xModel =
+ rDoc.GetDocShell()->GetBaseModel();
+ const uno::Reference< text::XTextDocument > xDoc(
+ xModel, uno::UNO_QUERY);
+ xParentText = xDoc->getText();
+ }
+ }
+ OSL_ENSURE(xParentText.is(), "no parent text?");
+ return xParentText;
+}
+
+} // namespace sw
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXTextRange::createContentEnumeration(const OUString& rServiceName)
+{
+ SolarMutexGuard g;
+
+ if ( rServiceName != "com.sun.star.text.TextContent" )
+ {
+ throw uno::RuntimeException("unsupported service");
+ }
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
+ const auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos));
+ if (!GetPositions(*pNewCursor))
+ {
+ throw uno::RuntimeException("range has no positions");
+ }
+
+ return SwXParaFrameEnumeration::Create(*pNewCursor, PARAFRAME_PORTION_TEXTRANGE);
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXTextRange::createEnumeration()
+{
+ SolarMutexGuard g;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
+ auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos));
+ if (!GetPositions(*pNewCursor))
+ {
+ throw uno::RuntimeException("range has no positions");
+ }
+ if (!m_pImpl->m_xParentText.is())
+ {
+ getText();
+ }
+
+ const CursorType eSetType = (RANGE_IN_CELL == m_pImpl->m_eRangePosition)
+ ? CursorType::SelectionInTable : CursorType::Selection;
+ return SwXParagraphEnumeration::Create(m_pImpl->m_xParentText, pNewCursor, eSetType);
+}
+
+uno::Type SAL_CALL SwXTextRange::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXTextRange::hasElements()
+{
+ return true;
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextRange::getAvailableServiceNames()
+{
+ uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
+ return aRet;
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXTextRange::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef =
+ m_pImpl->m_rPropSet.getPropertySetInfo();
+ return xRef;
+}
+
+void SAL_CALL
+SwXTextRange::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ GetPositions(aPaM);
+ SwUnoCursorHelper::SetPropertyValue(aPaM, m_pImpl->m_rPropSet,
+ rPropertyName, rValue);
+}
+
+uno::Any SAL_CALL
+SwXTextRange::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ GetPositions(aPaM);
+ return SwUnoCursorHelper::GetPropertyValue(aPaM, m_pImpl->m_rPropSet,
+ rPropertyName);
+}
+
+void SAL_CALL
+SwXTextRange::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextRange::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextRange::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextRange::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextRange::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextRange::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextRange::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextRange::removeVetoableChangeListener(): not implemented");
+}
+
+beans::PropertyState SAL_CALL
+SwXTextRange::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ GetPositions(aPaM);
+ return SwUnoCursorHelper::GetPropertyState(aPaM, m_pImpl->m_rPropSet,
+ rPropertyName);
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL
+SwXTextRange::getPropertyStates(const uno::Sequence< OUString >& rPropertyName)
+{
+ SolarMutexGuard g;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ GetPositions(aPaM);
+ return SwUnoCursorHelper::GetPropertyStates(aPaM, m_pImpl->m_rPropSet,
+ rPropertyName);
+}
+
+void SAL_CALL SwXTextRange::setPropertyToDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ GetPositions(aPaM);
+ SwUnoCursorHelper::SetPropertyToDefault(aPaM, m_pImpl->m_rPropSet,
+ rPropertyName);
+}
+
+uno::Any SAL_CALL
+SwXTextRange::getPropertyDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ GetPositions(aPaM);
+ return SwUnoCursorHelper::GetPropertyDefault(aPaM, m_pImpl->m_rPropSet,
+ rPropertyName);
+}
+
+void SAL_CALL
+SwXTextRange::makeRedline(
+ const OUString& rRedlineType,
+ const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->GetBookmark())
+ {
+ throw uno::RuntimeException("range has no mark (table?)");
+ }
+ SwPaM aPaM(GetDoc().GetNodes());
+ SwXTextRange::GetPositions(aPaM);
+ SwUnoCursorHelper::makeRedline( aPaM, rRedlineType, rRedlineProperties );
+}
+
+namespace {
+
+struct SwXTextRangesImpl final : public SwXTextRanges
+{
+
+ // XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ { return "SwXTextRanges"; };
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName) override
+ { return cppu::supportsService(this, rServiceName); };
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
+ { return { "com.sun.star.text.TextRanges" }; };
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override
+ { return cppu::UnoType<text::XTextRange>::get(); };
+ virtual sal_Bool SAL_CALL hasElements() override
+ { return getCount() > 0; };
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override;
+
+ explicit SwXTextRangesImpl(SwPaM *const pPaM)
+ {
+ if (pPaM)
+ {
+ m_pUnoCursor.reset(pPaM->GetDoc().CreateUnoCursor(*pPaM->GetPoint()));
+ ::sw::DeepCopyPaM(*pPaM, *GetCursor());
+ }
+ MakeRanges();
+ }
+ virtual void SAL_CALL release() noexcept override
+ {
+ SolarMutexGuard g;
+ OWeakObject::release();
+ }
+ virtual SwUnoCursor* GetCursor() override
+ { return &(*m_pUnoCursor); };
+ void MakeRanges();
+ std::vector< uno::Reference< text::XTextRange > > m_Ranges;
+ sw::UnoCursorPointer m_pUnoCursor;
+};
+
+}
+
+void SwXTextRangesImpl::MakeRanges()
+{
+ if (!GetCursor())
+ return;
+
+ for(SwPaM& rTmpCursor : GetCursor()->GetRingContainer())
+ {
+ const uno::Reference< text::XTextRange > xRange(
+ SwXTextRange::CreateXTextRange(
+ rTmpCursor.GetDoc(),
+ *rTmpCursor.GetPoint(), rTmpCursor.GetMark()));
+ if (xRange.is())
+ {
+ m_Ranges.push_back(xRange);
+ }
+ }
+}
+
+rtl::Reference<SwXTextRanges> SwXTextRanges::Create(SwPaM *const pPaM)
+ { return new SwXTextRangesImpl(pPaM); }
+
+const uno::Sequence< sal_Int8 > & SwXTextRanges::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextRangesUnoTunnelId;
+ return theSwXTextRangesUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXTextRangesImpl::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXTextRanges>(rId, this);
+}
+
+/*
+ * Text positions
+ * Up to the first access to a text position, only a SwCursor is stored.
+ * Afterwards, an array with uno::Reference<XTextPosition> will be created.
+ */
+
+sal_Int32 SAL_CALL SwXTextRangesImpl::getCount()
+{
+ SolarMutexGuard aGuard;
+ return static_cast<sal_Int32>(m_Ranges.size());
+}
+
+uno::Any SAL_CALL SwXTextRangesImpl::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= m_Ranges.size()))
+ throw lang::IndexOutOfBoundsException();
+ uno::Any ret;
+ ret <<= m_Ranges.at(nIndex);
+ return ret;
+}
+
+void SwUnoCursorHelper::SetString(SwCursor & rCursor, const OUString& rString)
+{
+ // Start/EndAction
+ SwDoc& rDoc = rCursor.GetDoc();
+ UnoActionContext aAction(&rDoc);
+ rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+ if (rCursor.HasMark())
+ {
+ rDoc.getIDocumentContentOperations().DeleteAndJoin(rCursor);
+ }
+ if (!rString.isEmpty())
+ {
+ const bool bSuccess( SwUnoCursorHelper::DocInsertStringSplitCR(
+ rDoc, rCursor, rString, false ) );
+ OSL_ENSURE( bSuccess, "DocInsertStringSplitCR" );
+ SwUnoCursorHelper::SelectPam(rCursor, true);
+ rCursor.Left(rString.getLength());
+ }
+ rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+}
+
+namespace {
+
+struct SwXParaFrameEnumerationImpl final : public SwXParaFrameEnumeration
+{
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ { return "SwXParaFrameEnumeration"; };
+ virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override
+ { return cppu::supportsService(this, rServiceName); };
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
+ { return {"com.sun.star.util.ContentEnumeration"}; };
+
+ // XEnumeration
+ virtual sal_Bool SAL_CALL hasMoreElements() override;
+ virtual css::uno::Any SAL_CALL nextElement() override;
+
+ SwXParaFrameEnumerationImpl(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat);
+ virtual void SAL_CALL release() noexcept override
+ {
+ SolarMutexGuard g;
+ OWeakObject::release();
+ }
+ SwUnoCursor& GetCursor()
+ { return *m_pUnoCursor; }
+ void PurgeFrameClients()
+ {
+ if(!m_pUnoCursor)
+ {
+ m_vFrames.clear();
+ m_xNextObject = nullptr;
+ }
+ else
+ {
+ // removing orphaned Clients
+ const auto iter = std::remove_if(m_vFrames.begin(), m_vFrames.end(),
+ [] (std::shared_ptr<sw::FrameClient>& rEntry) -> bool { return !rEntry->GetRegisteredIn(); });
+ m_vFrames.erase(iter, m_vFrames.end());
+ }
+ }
+ void FillFrame();
+ bool CreateNextObject();
+ uno::Reference< text::XTextContent > m_xNextObject;
+ FrameClientList_t m_vFrames;
+ ::sw::UnoCursorPointer m_pUnoCursor;
+};
+
+}
+
+rtl::Reference<SwXParaFrameEnumeration> SwXParaFrameEnumeration::Create(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat)
+ { return new SwXParaFrameEnumerationImpl(rPaM, eParaFrameMode, pFormat); }
+
+SwXParaFrameEnumerationImpl::SwXParaFrameEnumerationImpl(
+ const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode,
+ SwFrameFormat* const pFormat)
+ : m_pUnoCursor(rPaM.GetDoc().CreateUnoCursor(*rPaM.GetPoint()))
+{
+ if (rPaM.HasMark())
+ {
+ GetCursor().SetMark();
+ *GetCursor().GetMark() = *rPaM.GetMark();
+ }
+ if (PARAFRAME_PORTION_PARAGRAPH == eParaFrameMode)
+ {
+ FrameClientSortList_t vFrames;
+ ::CollectFrameAtNode(rPaM.GetPoint()->nNode, vFrames, false);
+ std::transform(vFrames.begin(), vFrames.end(),
+ std::back_inserter(m_vFrames),
+ [] (const FrameClientSortListEntry& rEntry) { return rEntry.pFrameClient; });
+ }
+ else if (pFormat)
+ {
+ m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFormat));
+ }
+ else if ((PARAFRAME_PORTION_CHAR == eParaFrameMode) ||
+ (PARAFRAME_PORTION_TEXTRANGE == eParaFrameMode))
+ {
+ if (PARAFRAME_PORTION_TEXTRANGE == eParaFrameMode)
+ {
+ //get all frames that are bound at paragraph or at character
+ for(const auto& pFlyFrame : rPaM.GetDoc().GetAllFlyFormats(&GetCursor(), false, true))
+ {
+ const auto pFrameFormat = const_cast<SwFrameFormat*>(&pFlyFrame->GetFormat());
+ m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFrameFormat));
+ }
+ }
+ FillFrame();
+ }
+}
+
+// Search for a FLYCNT text attribute at the cursor point and fill the frame
+// into the array
+void SwXParaFrameEnumerationImpl::FillFrame()
+{
+ if(!m_pUnoCursor->GetNode().IsTextNode())
+ return;
+ // search for objects at the cursor - anchored at/as char
+ const auto pTextAttr = m_pUnoCursor->GetNode().GetTextNode()->GetTextAttrForCharAt(
+ m_pUnoCursor->GetPoint()->nContent.GetIndex(), RES_TXTATR_FLYCNT);
+ if(!pTextAttr)
+ return;
+ const SwFormatFlyCnt& rFlyCnt = pTextAttr->GetFlyCnt();
+ SwFrameFormat* const pFrameFormat = rFlyCnt.GetFrameFormat();
+ m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFrameFormat));
+}
+
+bool SwXParaFrameEnumerationImpl::CreateNextObject()
+{
+ if (m_vFrames.empty())
+ return false;
+
+ SwFrameFormat* const pFormat = static_cast<SwFrameFormat*>(
+ m_vFrames.front()->GetRegisteredIn());
+ m_vFrames.pop_front();
+ // the format should be valid here, otherwise the client
+ // would have been removed by PurgeFrameClients
+ // check for a shape first
+ if(pFormat->Which() == RES_DRAWFRMFMT)
+ {
+ SdrObject* pObject(nullptr);
+ pFormat->CallSwClientNotify(sw::FindSdrObjectHint(pObject));
+ if(pObject)
+ m_xNextObject.set(pObject->getUnoShape(), uno::UNO_QUERY);
+ }
+ else
+ {
+ const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
+ OSL_ENSURE(pIdx, "where is the index?");
+ SwNode const*const pNd =
+ m_pUnoCursor->GetDoc().GetNodes()[ pIdx->GetIndex() + 1 ];
+
+ if (!pNd->IsNoTextNode())
+ {
+ m_xNextObject.set(SwXTextFrame::CreateXTextFrame(
+ *pFormat->GetDoc(), pFormat));
+ }
+ else if (pNd->IsGrfNode())
+ {
+ m_xNextObject.set(SwXTextGraphicObject::CreateXTextGraphicObject(
+ *pFormat->GetDoc(), pFormat));
+ }
+ else
+ {
+ assert(pNd->IsOLENode());
+ m_xNextObject.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
+ *pFormat->GetDoc(), pFormat));
+ }
+ }
+ return m_xNextObject.is();
+}
+
+sal_Bool SAL_CALL
+SwXParaFrameEnumerationImpl::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+ PurgeFrameClients();
+ return m_xNextObject.is() || CreateNextObject();
+}
+
+uno::Any SAL_CALL SwXParaFrameEnumerationImpl::nextElement()
+{
+ SolarMutexGuard aGuard;
+ PurgeFrameClients();
+ if (!m_xNextObject.is() && !m_vFrames.empty())
+ CreateNextObject();
+ if (!m_xNextObject.is())
+ throw container::NoSuchElementException();
+ uno::Any aRet;
+ aRet <<= m_xNextObject;
+ m_xNextObject = nullptr;
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoparagraph.cxx b/sw/source/core/unocore/unoparagraph.cxx
new file mode 100644
index 000000000..7485b98b1
--- /dev/null
+++ b/sw/source/core/unocore/unoparagraph.cxx
@@ -0,0 +1,1419 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unoparagraph.hxx>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include <cmdid.h>
+#include <unomid.h>
+#include <unoparaframeenum.hxx>
+#include <unotext.hxx>
+#include <unotextrange.hxx>
+#include <unoport.hxx>
+#include <unomap.hxx>
+#include <unocrsr.hxx>
+#include <unoprnms.hxx>
+#include <unocrsrhelper.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <vcl/svapp.hxx>
+#include <docsh.hxx>
+#include <swunohelper.hxx>
+
+#include <com/sun/star/beans/SetPropertyTolerantFailed.hpp>
+#include <com/sun/star/beans/GetPropertyTolerantResult.hpp>
+#include <com/sun/star/beans/TolerantPropertySetResultType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <editeng/unoipset.hxx>
+#include <svl/listener.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SwParaSelection
+{
+ SwCursor & m_rCursor;
+public:
+ explicit SwParaSelection(SwCursor & rCursor);
+ ~SwParaSelection();
+};
+
+}
+
+SwParaSelection::SwParaSelection(SwCursor & rCursor)
+ : m_rCursor(rCursor)
+{
+ if (m_rCursor.HasMark())
+ {
+ m_rCursor.DeleteMark();
+ }
+ // is it at the start?
+ if (m_rCursor.GetPoint()->nContent != 0)
+ {
+ m_rCursor.MovePara(GoCurrPara, fnParaStart);
+ }
+ // or at the end already?
+ if (m_rCursor.GetPoint()->nContent != m_rCursor.GetContentNode()->Len())
+ {
+ m_rCursor.SetMark();
+ m_rCursor.MovePara(GoCurrPara, fnParaEnd);
+ }
+}
+
+SwParaSelection::~SwParaSelection()
+{
+ if (m_rCursor.GetPoint()->nContent != 0)
+ {
+ m_rCursor.DeleteMark();
+ m_rCursor.MovePara(GoCurrPara, fnParaStart);
+ }
+}
+
+/// @throws beans::UnknownPropertyException
+/// @throws uno::RuntimeException
+static beans::PropertyState lcl_SwXParagraph_getPropertyState(
+ const SwTextNode& rTextNode,
+ const SwAttrSet** ppSet,
+ const SfxItemPropertyMapEntry& rEntry,
+ bool &rAttrSetFetched );
+
+class SwXParagraph::Impl
+ : public SvtListener
+{
+public:
+ SwXParagraph& m_rThis;
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ SfxItemPropertySet const& m_rPropSet;
+ bool m_bIsDescriptor;
+ sal_Int32 m_nSelectionStartPos;
+ sal_Int32 m_nSelectionEndPos;
+ OUString m_sText;
+ uno::Reference<text::XText> m_xParentText;
+ SwTextNode* m_pTextNode;
+
+ Impl(SwXParagraph& rThis,
+ SwTextNode* const pTextNode = nullptr, uno::Reference<text::XText> const& xParent = nullptr,
+ const sal_Int32 nSelStart = -1, const sal_Int32 nSelEnd = -1)
+ : m_rThis(rThis)
+ , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH))
+ , m_bIsDescriptor(nullptr == pTextNode)
+ , m_nSelectionStartPos(nSelStart)
+ , m_nSelectionEndPos(nSelEnd)
+ , m_xParentText(xParent)
+ , m_pTextNode(pTextNode)
+ {
+ m_pTextNode && StartListening(m_pTextNode->GetNotifier());
+ }
+
+ SwTextNode* GetTextNode() {
+ return m_pTextNode;
+ }
+
+ SwTextNode& GetTextNodeOrThrow() {
+ if (!m_pTextNode) {
+ throw uno::RuntimeException("SwXParagraph: disposed or invalid", nullptr);
+ }
+ return *m_pTextNode;
+ }
+
+ bool IsDescriptor() const { return m_bIsDescriptor; }
+
+ /// @throws beans::UnknownPropertyException
+ /// @throws beans::PropertyVetoException
+ /// @throws lang::IllegalArgumentException
+ /// @throws lang::WrappedTargetException
+ /// @throws uno::RuntimeException
+ void SetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues);
+
+ /// @throws beans::UnknownPropertyException
+ /// @throws lang::WrappedTargetException
+ /// @throws uno::RuntimeException
+ uno::Sequence< uno::Any >
+ GetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames);
+
+ /// @throws uno::RuntimeException
+ void GetSinglePropertyValue_Impl(
+ const SfxItemPropertyMapEntry& rEntry,
+ const SfxItemSet& rSet,
+ uno::Any& rAny ) const;
+
+ /// @throws uno::RuntimeException
+ uno::Sequence< beans::GetDirectPropertyTolerantResult >
+ GetPropertyValuesTolerant_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ bool bDirectValuesOnly);
+protected:
+ virtual void Notify(const SfxHint& rHint) override;
+
+};
+
+void SwXParagraph::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pTextNode = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ }
+}
+
+SwXParagraph::SwXParagraph()
+ : m_pImpl( new SwXParagraph::Impl(*this) )
+{
+}
+
+SwXParagraph::SwXParagraph(
+ uno::Reference< text::XText > const & xParent,
+ SwTextNode & rTextNode,
+ const sal_Int32 nSelStart, const sal_Int32 nSelEnd)
+ : m_pImpl(
+ new SwXParagraph::Impl(*this, &rTextNode, xParent, nSelStart, nSelEnd))
+{
+}
+
+SwXParagraph::~SwXParagraph()
+{
+}
+
+const SwTextNode * SwXParagraph::GetTextNode() const
+{
+ return m_pImpl->GetTextNode();
+}
+
+bool SwXParagraph::IsDescriptor() const
+{
+ return m_pImpl->IsDescriptor();
+}
+
+uno::Reference<text::XTextContent>
+SwXParagraph::CreateXParagraph(SwDoc & rDoc, SwTextNode *const pTextNode,
+ uno::Reference< text::XText> const& i_xParent,
+ const sal_Int32 nSelStart, const sal_Int32 nSelEnd)
+{
+ // re-use existing SwXParagraph
+ // #i105557#: do not iterate over the registered clients: race condition
+ uno::Reference<text::XTextContent> xParagraph;
+ if (pTextNode && (-1 == nSelStart) && (-1 == nSelEnd))
+ { // only use cache if no selection!
+ xParagraph.set(pTextNode->GetXParagraph());
+ }
+ if (xParagraph.is())
+ {
+ return xParagraph;
+ }
+
+ // create new SwXParagraph
+ uno::Reference<text::XText> xParentText(i_xParent);
+ if (!xParentText.is() && pTextNode)
+ {
+ SwPosition Pos(*pTextNode);
+ xParentText.set(::sw::CreateParentXText( rDoc, Pos ));
+ }
+ SwXParagraph *const pXPara( pTextNode
+ ? new SwXParagraph(xParentText, *pTextNode, nSelStart, nSelEnd)
+ : new SwXParagraph);
+ // this is why the constructor is private: need to acquire pXPara here
+ xParagraph.set(pXPara);
+ // in order to initialize the weak pointer cache in the core object
+ if (pTextNode && (-1 == nSelStart) && (-1 == nSelEnd))
+ {
+ pTextNode->SetXParagraph(xParagraph);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pXPara->m_pImpl->m_wThis = xParagraph;
+ return xParagraph;
+}
+
+bool SwXParagraph::SelectPaM(SwPaM & rPaM)
+{
+ SwTextNode const*const pTextNode( GetTextNode() );
+
+ if (!pTextNode)
+ {
+ return false;
+ }
+
+ *rPaM.GetPoint() = SwPosition( *pTextNode );
+ // set selection to the whole paragraph
+ rPaM.SetMark();
+ rPaM.GetMark()->nContent = pTextNode->GetText().getLength();
+ return true;
+}
+
+const uno::Sequence< sal_Int8 > & SwXParagraph::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXParagraphUnoTunnelId;
+ return theSwXParagraphUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXParagraph::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXParagraph>(rId, this);
+}
+
+OUString SAL_CALL
+SwXParagraph::getImplementationName()
+{
+ return "SwXParagraph";
+}
+
+sal_Bool SAL_CALL
+SwXParagraph::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXParagraph::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.Paragraph",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex"
+ };
+}
+
+void
+SwXParagraph::attachToText(SwXText & rParent, SwTextNode & rTextNode)
+{
+ OSL_ENSURE(m_pImpl->m_bIsDescriptor, "Paragraph is not a descriptor");
+ if (!m_pImpl->m_bIsDescriptor)
+ return;
+
+ m_pImpl->m_bIsDescriptor = false;
+ m_pImpl->EndListeningAll();
+ m_pImpl->StartListening(rTextNode.GetNotifier());
+ rTextNode.SetXParagraph(uno::Reference<text::XTextContent>(this));
+ m_pImpl->m_xParentText = &rParent;
+ if (!m_pImpl->m_sText.isEmpty())
+ {
+ try { setString(m_pImpl->m_sText); }
+ catch(...){}
+ m_pImpl->m_sText.clear();
+ }
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXParagraph::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef =
+ m_pImpl->m_rPropSet.getPropertySetInfo();
+ return xRef;
+}
+
+void SAL_CALL
+SwXParagraph::setPropertyValue(const OUString& rPropertyName,
+ const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+ m_pImpl->SetPropertyValues_Impl( { rPropertyName }, { rValue } );
+}
+
+uno::Any
+SwXParagraph::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<OUString> aPropertyNames { rPropertyName };
+ const uno::Sequence< uno::Any > aRet =
+ m_pImpl->GetPropertyValues_Impl(aPropertyNames);
+ return aRet.getConstArray()[0];
+}
+
+void SwXParagraph::Impl::SetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues )
+{
+ SwTextNode & rTextNode(GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ const OUString* pPropertyNames = rPropertyNames.getConstArray();
+ const uno::Any* pValues = rValues.getConstArray();
+ const SfxItemPropertyMap &rMap = m_rPropSet.getPropertyMap();
+ SwParaSelection aParaSel( aCursor );
+
+ uno::Sequence< beans::PropertyValue > aValues( rPropertyNames.getLength() );
+ auto aValuesRange = asNonConstRange(aValues);
+ for (sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName( pPropertyNames[nProp] );
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pPropertyNames[nProp],
+ static_cast< cppu::OWeakObject * >(&m_rThis));
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw beans::PropertyVetoException(
+ "Property is read-only: " + pPropertyNames[nProp],
+ static_cast< cppu::OWeakObject * >(&m_rThis));
+ }
+ aValuesRange[nProp].Name = pPropertyNames[nProp];
+ aValuesRange[nProp].Value = pValues[nProp];
+ }
+ SwUnoCursorHelper::SetPropertyValues(aCursor, m_rPropSet, aValues);
+}
+
+void SAL_CALL SwXParagraph::setPropertyValues(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues )
+{
+ SolarMutexGuard aGuard;
+
+ // workaround for bad designed API
+ try
+ {
+ m_pImpl->SetPropertyValues_Impl( rPropertyNames, rValues );
+ }
+ catch (const beans::UnknownPropertyException &rException)
+ {
+ // wrap the original (here not allowed) exception in
+ // a lang::WrappedTargetException that gets thrown instead.
+ lang::WrappedTargetException aWExc;
+ aWExc.TargetException <<= rException;
+ throw aWExc;
+ }
+}
+
+// Support for DrawingLayer FillStyles for GetPropertyValue() usages
+void SwXParagraph::Impl::GetSinglePropertyValue_Impl(
+ const SfxItemPropertyMapEntry& rEntry,
+ const SfxItemSet& rSet,
+ uno::Any& rAny ) const
+{
+ bool bDone(false);
+
+ switch(rEntry.nWID)
+ {
+ case RES_BACKGROUND:
+ {
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
+
+ if(!aOriginalBrushItem->QueryValue(rAny, rEntry.nMemberId))
+ {
+ OSL_ENSURE(false, "Error getting attribute from RES_BACKGROUND (!)");
+ }
+
+ bDone = true;
+ break;
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ if (rSet.Get(XATTR_FILLBMP_TILE).GetValue())
+ {
+ rAny <<= drawing::BitmapMode_REPEAT;
+ }
+ else if (rSet.Get(XATTR_FILLBMP_STRETCH).GetValue())
+ {
+ rAny <<= drawing::BitmapMode_STRETCH;
+ }
+ else
+ {
+ rAny <<= drawing::BitmapMode_NO_REPEAT;
+ }
+
+ bDone = true;
+ break;
+ }
+ default: break;
+ }
+
+ if(bDone)
+ return;
+
+ // fallback to standard get value implementation used before this helper was created
+ m_rPropSet.getPropertyValue(rEntry, rSet, rAny);
+
+ if(rEntry.aType == cppu::UnoType<sal_Int16>::get() && rEntry.aType != rAny.getValueType())
+ {
+ // since the sfx uInt16 item now exports a sal_Int32, we may have to fix this here
+ sal_Int32 nValue(0);
+
+ if (rAny >>= nValue)
+ {
+ rAny <<= static_cast<sal_Int16>(nValue);
+ }
+ }
+
+ // check for needed metric translation
+ if(!(rEntry.nMoreFlags & PropertyMoreFlags::METRIC_ITEM))
+ return;
+
+ bool bDoIt(true);
+
+ if(XATTR_FILLBMP_SIZEX == rEntry.nWID || XATTR_FILLBMP_SIZEY == rEntry.nWID)
+ {
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ sal_Int32 nValue = 0;
+
+ if(rAny >>= nValue)
+ {
+ bDoIt = nValue > 0;
+ }
+ }
+
+ if(bDoIt)
+ {
+ const MapUnit eMapUnit(rSet.GetPool()->GetMetric(rEntry.nWID));
+
+ if(eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertToMM(eMapUnit, rAny);
+ }
+ }
+}
+
+uno::Sequence< uno::Any > SwXParagraph::Impl::GetPropertyValues_Impl(
+ const uno::Sequence< OUString > & rPropertyNames )
+{
+ SwTextNode & rTextNode(GetTextNodeOrThrow());
+
+ uno::Sequence< uno::Any > aValues(rPropertyNames.getLength());
+ SwPosition aPos( rTextNode );
+ SwPaM aPam( aPos );
+ uno::Any* pValues = aValues.getArray();
+ const OUString* pPropertyNames = rPropertyNames.getConstArray();
+ const SfxItemPropertyMap &rMap = m_rPropSet.getPropertyMap();
+ const SwAttrSet& rAttrSet( rTextNode.GetSwAttrSet() );
+ for (sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName( pPropertyNames[nProp] );
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pPropertyNames[nProp],
+ static_cast< cppu::OWeakObject * >(&m_rThis));
+ }
+ if (! ::sw::GetDefaultTextContentValue(
+ pValues[nProp], pPropertyNames[nProp], pEntry->nWID))
+ {
+ beans::PropertyState eTemp;
+ const bool bDone = SwUnoCursorHelper::getCursorPropertyValue(
+ *pEntry, aPam, &(pValues[nProp]), eTemp, &rTextNode );
+ if (!bDone)
+ {
+ GetSinglePropertyValue_Impl(*pEntry, rAttrSet, pValues[nProp]);
+ }
+ }
+ }
+ return aValues;
+}
+
+uno::Sequence< uno::Any > SAL_CALL
+SwXParagraph::getPropertyValues(const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< uno::Any > aValues;
+
+ // workaround for bad designed API
+ try
+ {
+ aValues = m_pImpl->GetPropertyValues_Impl( rPropertyNames );
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("Unknown property exception caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("WrappedTargetException caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+
+ return aValues;
+}
+
+void SAL_CALL SwXParagraph::addPropertiesChangeListener(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL("SwXParagraph::addPropertiesChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXParagraph::removePropertiesChangeListener(
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL("SwXParagraph::removePropertiesChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXParagraph::firePropertiesChangeEvent(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL("SwXParagraph::firePropertiesChangeEvent(): not implemented");
+}
+
+/* disabled for #i46921# */
+
+uno::Sequence< beans::SetPropertyTolerantFailed > SAL_CALL
+SwXParagraph::setPropertyValuesTolerant(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues )
+{
+ SolarMutexGuard aGuard;
+
+ if (rPropertyNames.getLength() != rValues.getLength())
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ //SwNode& rTextNode = pUnoCursor->GetPoint()->nNode.GetNode();
+ //const SwAttrSet& rAttrSet = static_cast<SwTextNode&>(rTextNode).GetSwAttrSet();
+ //sal_uInt16 nAttrCount = rAttrSet.Count();
+
+ const sal_Int32 nProps = rPropertyNames.getLength();
+ const OUString *pProp = rPropertyNames.getConstArray();
+
+ //sal_Int32 nVals = rValues.getLength();
+ const uno::Any *pValue = rValues.getConstArray();
+
+ sal_Int32 nFailed = 0;
+ uno::Sequence< beans::SetPropertyTolerantFailed > aFailed( nProps );
+ beans::SetPropertyTolerantFailed *pFailed = aFailed.getArray();
+
+ // get entry to start with
+ const SfxItemPropertyMap &rPropMap =
+ m_pImpl->m_rPropSet.getPropertyMap();
+
+ SwPosition aPos( rTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ SwParaSelection aParaSel( aCursor );
+ for (sal_Int32 i = 0; i < nProps; ++i)
+ {
+ try
+ {
+ pFailed[ nFailed ].Name = pProp[i];
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropMap.getByName( pProp[i] );
+ if (!pEntry)
+ {
+ pFailed[ nFailed++ ].Result =
+ beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ else
+ {
+ // set property value
+ // (compare to SwXParagraph::setPropertyValues)
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ pFailed[ nFailed++ ].Result =
+ beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ }
+ else
+ {
+ SwUnoCursorHelper::SetPropertyValue(
+ aCursor, m_pImpl->m_rPropSet, pProp[i], pValue[i]);
+ }
+ }
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ // should not occur because property was searched for before
+ TOOLS_WARN_EXCEPTION( "sw", "unexpected exception caught" );
+ pFailed[ nFailed++ ].Result =
+ beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ pFailed[ nFailed++ ].Result =
+ beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT;
+ }
+ catch (beans::PropertyVetoException &)
+ {
+ pFailed[ nFailed++ ].Result =
+ beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ pFailed[ nFailed++ ].Result =
+ beans::TolerantPropertySetResultType::WRAPPED_TARGET;
+ }
+ }
+
+ aFailed.realloc( nFailed );
+ return aFailed;
+}
+
+uno::Sequence< beans::GetPropertyTolerantResult > SAL_CALL
+SwXParagraph::getPropertyValuesTolerant(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ const uno::Sequence< beans::GetDirectPropertyTolerantResult > aTmpRes(
+ m_pImpl->GetPropertyValuesTolerant_Impl( rPropertyNames, false ) );
+
+ // copy temporary result to final result type
+ const sal_Int32 nLen = aTmpRes.getLength();
+ uno::Sequence< beans::GetPropertyTolerantResult > aRes( nLen );
+ std::copy(aTmpRes.begin(), aTmpRes.end(), aRes.getArray());
+ return aRes;
+}
+
+uno::Sequence< beans::GetDirectPropertyTolerantResult > SAL_CALL
+SwXParagraph::getDirectPropertyValuesTolerant(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ return m_pImpl->GetPropertyValuesTolerant_Impl( rPropertyNames, true );
+}
+
+uno::Sequence< beans::GetDirectPropertyTolerantResult >
+SwXParagraph::Impl::GetPropertyValuesTolerant_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ bool bDirectValuesOnly )
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(GetTextNodeOrThrow());
+
+ // #i46786# Use SwAttrSet pointer for determining the state.
+ // Use the value SwAttrSet (from the paragraph OR the style)
+ // for determining the actual value(s).
+ const SwAttrSet* pAttrSet = rTextNode.GetpSwAttrSet();
+ const SwAttrSet& rValueAttrSet = rTextNode.GetSwAttrSet();
+
+ sal_Int32 nProps = rPropertyNames.getLength();
+
+ uno::Sequence< beans::GetDirectPropertyTolerantResult > aResult( nProps );
+ beans::GetDirectPropertyTolerantResult *pResult = aResult.getArray();
+ sal_Int32 nIdx = 0;
+
+ // get entry to start with
+ const SfxItemPropertyMap &rPropMap = m_rPropSet.getPropertyMap();
+
+ for (const OUString& rProp : rPropertyNames)
+ {
+ OSL_ENSURE( nIdx < nProps, "index out of bounds" );
+ beans::GetDirectPropertyTolerantResult &rResult = pResult[nIdx];
+
+ try
+ {
+ rResult.Name = rProp;
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ rPropMap.getByName( rProp );
+ if (!pEntry) // property available?
+ {
+ rResult.Result =
+ beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ else
+ {
+ // get property state
+ // (compare to SwXParagraph::getPropertyState)
+ bool bAttrSetFetched = true;
+ beans::PropertyState eState = lcl_SwXParagraph_getPropertyState(
+ rTextNode, &pAttrSet, *pEntry, bAttrSetFetched );
+ rResult.State = eState;
+
+ rResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_FAILURE;
+ if (!bDirectValuesOnly ||
+ (beans::PropertyState_DIRECT_VALUE == eState))
+ {
+ // get property value
+ // (compare to SwXParagraph::getPropertyValue(s))
+ uno::Any aValue;
+ if (! ::sw::GetDefaultTextContentValue(
+ aValue, rProp, pEntry->nWID ) )
+ {
+ SwPosition aPos( rTextNode );
+ SwPaM aPam( aPos );
+ // handle properties that are not part of the attribute
+ // and thus only pretended to be paragraph attributes
+ beans::PropertyState eTemp;
+ const bool bDone =
+ SwUnoCursorHelper::getCursorPropertyValue(
+ *pEntry, aPam, &aValue, eTemp, &rTextNode );
+
+ // if not found try the real paragraph attributes...
+ if (!bDone)
+ {
+ GetSinglePropertyValue_Impl(*pEntry, rValueAttrSet, aValue);
+ }
+ }
+
+ rResult.Value = aValue;
+ rResult.Result = beans::TolerantPropertySetResultType::SUCCESS;
+
+ nIdx++;
+ }
+ // this assertion should never occur!
+ OSL_ENSURE( nIdx < 1 || pResult[nIdx - 1].Result != beans::TolerantPropertySetResultType::UNKNOWN_FAILURE,
+ "unknown failure while retrieving property" );
+
+ }
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ // should not occur because property was searched for before
+ TOOLS_WARN_EXCEPTION( "sw", "unexpected exception caught" );
+ rResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ rResult.Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT;
+ }
+ catch (beans::PropertyVetoException &)
+ {
+ rResult.Result = beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ rResult.Result = beans::TolerantPropertySetResultType::WRAPPED_TARGET;
+ }
+ }
+
+ // resize to actually used size
+ aResult.realloc( nIdx );
+
+ return aResult;
+}
+
+bool ::sw::GetDefaultTextContentValue(
+ uno::Any& rAny, std::u16string_view rPropertyName, sal_uInt16 nWID)
+{
+ if(!nWID)
+ {
+ if(rPropertyName == u"" UNO_NAME_ANCHOR_TYPE)
+ nWID = FN_UNO_ANCHOR_TYPE;
+ else if(rPropertyName == u"" UNO_NAME_ANCHOR_TYPES)
+ nWID = FN_UNO_ANCHOR_TYPES;
+ else if(rPropertyName == u"" UNO_NAME_TEXT_WRAP)
+ nWID = FN_UNO_TEXT_WRAP;
+ else
+ return false;
+ }
+
+ switch(nWID)
+ {
+ case FN_UNO_TEXT_WRAP: rAny <<= text::WrapTextMode_NONE; break;
+ case FN_UNO_ANCHOR_TYPE: rAny <<= text::TextContentAnchorType_AT_PARAGRAPH; break;
+ case FN_UNO_ANCHOR_TYPES:
+ { uno::Sequence<text::TextContentAnchorType> aTypes { text::TextContentAnchorType_AT_PARAGRAPH };
+ rAny <<= aTypes;
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void SAL_CALL
+SwXParagraph::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXParagraph::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXParagraph::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXParagraph::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXParagraph::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXParagraph::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXParagraph::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXParagraph::removeVetoableChangeListener(): not implemented");
+}
+
+static beans::PropertyState lcl_SwXParagraph_getPropertyState(
+ const SwTextNode& rTextNode,
+ const SwAttrSet** ppSet,
+ const SfxItemPropertyMapEntry& rEntry,
+ bool &rAttrSetFetched)
+{
+ beans::PropertyState eRet(beans::PropertyState_DEFAULT_VALUE);
+
+ if(!(*ppSet) && !rAttrSetFetched)
+ {
+ (*ppSet) = rTextNode.GetpSwAttrSet();
+ rAttrSetFetched = true;
+ }
+
+ SwPosition aPos(rTextNode);
+ SwPaM aPam(aPos);
+ bool bDone(false);
+
+ switch(rEntry.nWID)
+ {
+ case FN_UNO_NUM_RULES:
+ {
+ // if numbering is set, return it; else do nothing
+ SwUnoCursorHelper::getNumberingProperty(aPam,eRet,nullptr);
+ bDone = true;
+ break;
+ }
+ case FN_UNO_LIST_ID:
+ {
+ SwNumRule* pNumRule = rTextNode.GetNumRule();
+ if (pNumRule && pNumRule->HasContinueList())
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ bDone = true;
+ break;
+ }
+ case FN_UNO_ANCHOR_TYPES:
+ {
+ bDone = true;
+ break;
+ }
+ case RES_ANCHOR:
+ {
+ bDone = (MID_SURROUND_SURROUNDTYPE == rEntry.nMemberId);
+ break;
+ }
+ case RES_SURROUND:
+ {
+ bDone = (MID_ANCHOR_ANCHORTYPE == rEntry.nMemberId);
+ break;
+ }
+ case FN_UNO_PARA_STYLE:
+ case FN_UNO_PARA_CONDITIONAL_STYLE_NAME:
+ {
+ SwFormatColl* pFormat = SwUnoCursorHelper::GetCurTextFormatColl(aPam,rEntry.nWID == FN_UNO_PARA_CONDITIONAL_STYLE_NAME);
+ eRet = pFormat ? beans::PropertyState_DIRECT_VALUE : beans::PropertyState_AMBIGUOUS_VALUE;
+ bDone = true;
+ break;
+ }
+ case FN_UNO_PAGE_STYLE:
+ {
+ OUString sVal;
+ SwUnoCursorHelper::GetCurPageStyle( aPam, sVal );
+ eRet = !sVal.isEmpty() ? beans::PropertyState_DIRECT_VALUE
+ : beans::PropertyState_AMBIGUOUS_VALUE;
+ bDone = true;
+ break;
+ }
+
+ // DrawingLayer PropertyStyle support
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ if(*ppSet)
+ {
+ if(SfxItemState::SET == (*ppSet)->GetItemState(XATTR_FILLBMP_STRETCH, false)
+ || SfxItemState::SET == (*ppSet)->GetItemState(XATTR_FILLBMP_TILE, false))
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ eRet = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+
+ bDone = true;
+ }
+ break;
+ }
+ case RES_BACKGROUND:
+ {
+ if(*ppSet)
+ {
+ if (SWUnoHelper::needToMapFillItemsToSvxBrushItemTypes(**ppSet,
+ rEntry.nMemberId))
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ bDone = true;
+ }
+ break;
+ }
+ }
+
+ if(!bDone)
+ {
+ if((*ppSet) && SfxItemState::SET == (*ppSet)->GetItemState(rEntry.nWID, false))
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+
+ return eRet;
+}
+
+beans::PropertyState SAL_CALL
+SwXParagraph::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ const SwAttrSet* pSet = nullptr;
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+ bool bDummy = false;
+ const beans::PropertyState eRet =
+ lcl_SwXParagraph_getPropertyState(rTextNode, &pSet, *pEntry, bDummy);
+ return eRet;
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL
+SwXParagraph::getPropertyStates(
+ const uno::Sequence< OUString >& PropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ const OUString* pNames = PropertyNames.getConstArray();
+ uno::Sequence< beans::PropertyState > aRet(PropertyNames.getLength());
+ beans::PropertyState* pStates = aRet.getArray();
+ const SfxItemPropertyMap &rMap = m_pImpl->m_rPropSet.getPropertyMap();
+ const SwAttrSet* pSet = nullptr;
+ bool bAttrSetFetched = false;
+
+ for (sal_Int32 i = 0, nEnd = PropertyNames.getLength(); i < nEnd;
+ ++i, ++pStates, ++pNames)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ rMap.getByName( *pNames );
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + *pNames,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ if (bAttrSetFetched && !pSet && isATR(pEntry->nWID))
+ {
+ *pStates = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ *pStates = lcl_SwXParagraph_getPropertyState(
+ rTextNode, &pSet, *pEntry, bAttrSetFetched );
+ }
+ }
+
+ return aRet;
+}
+
+void SAL_CALL
+SwXParagraph::setPropertyToDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ if (rPropertyName == UNO_NAME_ANCHOR_TYPE ||
+ rPropertyName == UNO_NAME_ANCHOR_TYPES ||
+ rPropertyName == UNO_NAME_TEXT_WRAP)
+ {
+ return;
+ }
+
+ // select paragraph
+ SwParaSelection aParaSel( aCursor );
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName( rPropertyName );
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw uno::RuntimeException(
+ "Property is read-only: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ const bool bBelowFrameAtrEnd(pEntry->nWID < RES_FRMATR_END);
+ const bool bDrawingLayerRange(XATTR_FILL_FIRST <= pEntry->nWID && XATTR_FILL_LAST >= pEntry->nWID);
+
+ if(bBelowFrameAtrEnd || bDrawingLayerRange)
+ {
+ o3tl::sorted_vector<sal_uInt16> aWhichIds;
+
+ // For FillBitmapMode two IDs have to be reset (!)
+ if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID)
+ {
+ aWhichIds.insert(XATTR_FILLBMP_STRETCH);
+ aWhichIds.insert(XATTR_FILLBMP_TILE);
+ }
+ else
+ {
+ aWhichIds.insert(pEntry->nWID);
+ }
+
+ if (pEntry->nWID < RES_PARATR_BEGIN)
+ {
+ aCursor.GetDoc().ResetAttrs(aCursor, true, aWhichIds);
+ }
+ else
+ {
+ // for paragraph attributes the selection must be extended
+ // to paragraph boundaries
+ SwPosition aStart( *aCursor.Start() );
+ SwPosition aEnd ( *aCursor.End() );
+ auto pTemp( aCursor.GetDoc().CreateUnoCursor(aStart) );
+ if(!SwUnoCursorHelper::IsStartOfPara(*pTemp))
+ {
+ pTemp->MovePara(GoCurrPara, fnParaStart);
+ }
+
+ pTemp->SetMark();
+ *pTemp->GetPoint() = aEnd;
+
+ SwUnoCursorHelper::SelectPam(*pTemp, true);
+
+ if (!SwUnoCursorHelper::IsEndOfPara(*pTemp))
+ {
+ pTemp->MovePara(GoCurrPara, fnParaEnd);
+ }
+
+
+ pTemp->GetDoc().ResetAttrs(*pTemp, true, aWhichIds);
+ }
+ }
+ else
+ {
+ SwUnoCursorHelper::resetCursorPropertyValue(*pEntry, aCursor);
+ }
+}
+
+uno::Any SAL_CALL
+SwXParagraph::getPropertyDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard g;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ uno::Any aRet;
+ if (::sw::GetDefaultTextContentValue(aRet, rPropertyName))
+ {
+ return aRet;
+ }
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ const bool bBelowFrameAtrEnd(pEntry->nWID < RES_FRMATR_END);
+ const bool bDrawingLayerRange(XATTR_FILL_FIRST <= pEntry->nWID && XATTR_FILL_LAST >= pEntry->nWID);
+
+ if(bBelowFrameAtrEnd || bDrawingLayerRange)
+ {
+ const SfxPoolItem& rDefItem = rTextNode.GetDoc().GetAttrPool().GetDefaultItem(pEntry->nWID);
+
+ rDefItem.QueryValue(aRet, pEntry->nMemberId);
+ }
+
+ return aRet;
+}
+
+void SAL_CALL
+SwXParagraph::attach(const uno::Reference< text::XTextRange > & /*xTextRange*/)
+{
+ // SwXParagraph will only created in order to be inserted by
+ // 'insertTextContentBefore' or 'insertTextContentAfter' therefore
+ // they cannot be attached
+ throw uno::RuntimeException();
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXParagraph::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ // select paragraph
+ SwParaSelection aParaSel( aCursor );
+ const uno::Reference< text::XTextRange > xRet =
+ new SwXTextRange(aCursor, m_pImpl->m_xParentText);
+ return xRet;
+}
+
+void SAL_CALL SwXParagraph::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode *const pTextNode( m_pImpl->GetTextNode() );
+ if (pTextNode)
+ {
+ SwCursor aCursor( SwPosition( *pTextNode ), nullptr );
+ pTextNode->GetDoc().getIDocumentContentOperations().DelFullPara(aCursor);
+ lang::EventObject const ev(static_cast< ::cppu::OWeakObject&>(*this));
+ std::unique_lock aGuard2(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.disposeAndClear(aGuard2, ev);
+ }
+}
+
+void SAL_CALL SwXParagraph::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXParagraph::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXParagraph::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwPaM aPam ( aPos );
+ const uno::Reference< container::XEnumeration > xRef =
+ new SwXTextPortionEnumeration(aPam, m_pImpl->m_xParentText,
+ m_pImpl->m_nSelectionStartPos, m_pImpl->m_nSelectionEndPos);
+ return xRef;
+}
+
+uno::Type SAL_CALL SwXParagraph::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXParagraph::hasElements()
+{
+ SolarMutexGuard aGuard;
+ return GetTextNode() != nullptr;
+}
+
+uno::Reference< text::XText > SAL_CALL
+SwXParagraph::getText()
+{
+ SolarMutexGuard g;
+
+ return m_pImpl->m_xParentText;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXParagraph::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ SwParaSelection aParaSel( aCursor );
+ SwPaM aPam( *aCursor.Start() );
+ uno::Reference< text::XText > xParent = getText();
+ const uno::Reference< text::XTextRange > xRet =
+ new SwXTextRange(aPam, xParent);
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXParagraph::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ SwParaSelection aParaSel( aCursor );
+ SwPaM aPam( *aCursor.End() );
+ uno::Reference< text::XText > xParent = getText();
+ const uno::Reference< text::XTextRange > xRet =
+ new SwXTextRange(aPam, xParent);
+ return xRet;
+}
+
+OUString SAL_CALL SwXParagraph::getString()
+{
+ SolarMutexGuard aGuard;
+ OUString aRet;
+ SwTextNode const*const pTextNode( GetTextNode() );
+ if (pTextNode)
+ {
+ SwPosition aPos( *pTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ SwParaSelection aParaSel( aCursor );
+ SwUnoCursorHelper::GetTextFromPam(aCursor, aRet);
+ }
+ else if (m_pImpl->IsDescriptor())
+ {
+ aRet = m_pImpl->m_sText;
+ }
+ else
+ {
+ // Seems object is being disposed or some other problem occurs.
+ // Anyway from user point of view object still exist, so on that level this is not an error
+ SAL_WARN("sw.uno", "getString() for invalid paragraph called. Returning empty string.");
+ }
+ return aRet;
+}
+
+void SAL_CALL SwXParagraph::setString(const OUString& aString)
+{
+ SolarMutexGuard aGuard;
+
+ SwTextNode const*const pTextNode( GetTextNode() );
+ if (pTextNode)
+ {
+ SwPosition aPos( *pTextNode );
+ SwCursor aCursor( aPos, nullptr );
+ if (!SwUnoCursorHelper::IsStartOfPara(aCursor)) {
+ aCursor.MovePara(GoCurrPara, fnParaStart);
+ }
+ SwUnoCursorHelper::SelectPam(aCursor, true);
+ if (pTextNode->GetText().getLength()) {
+ aCursor.MovePara(GoCurrPara, fnParaEnd);
+ }
+ SwUnoCursorHelper::SetString(aCursor, aString);
+ SwUnoCursorHelper::SelectPam(aCursor, false);
+ }
+ else if (m_pImpl->IsDescriptor())
+ {
+ m_pImpl->m_sText = aString;
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXParagraph::createContentEnumeration(const OUString& rServiceName)
+{
+ SolarMutexGuard g;
+
+ if ( rServiceName != "com.sun.star.text.TextContent" )
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwTextNode & rTextNode(m_pImpl->GetTextNodeOrThrow());
+
+ SwPosition aPos( rTextNode );
+ SwPaM aPam( aPos );
+ uno::Reference< container::XEnumeration > xRet =
+ SwXParaFrameEnumeration::Create(aPam, PARAFRAME_PORTION_PARAGRAPH);
+ return xRet;
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXParagraph::getAvailableServiceNames()
+{
+ uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
+ return aRet;
+}
+
+// MetadatableMixin
+::sfx2::Metadatable* SwXParagraph::GetCoreObject()
+{
+ SwTextNode *const pTextNode( m_pImpl->GetTextNode() );
+ return pTextNode;
+}
+
+uno::Reference<frame::XModel> SwXParagraph::GetModel()
+{
+ SwTextNode *const pTextNode( m_pImpl->GetTextNode() );
+ if (pTextNode)
+ {
+ SwDocShell const*const pShell( pTextNode->GetDoc().GetDocShell() );
+ return pShell ? pShell->GetModel() : nullptr;
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoport.cxx b/sw/source/core/unocore/unoport.cxx
new file mode 100644
index 000000000..9988bd6cf
--- /dev/null
+++ b/sw/source/core/unocore/unoport.cxx
@@ -0,0 +1,871 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unoport.hxx>
+
+#include <cmdid.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/itemprop.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <unocrsrhelper.hxx>
+#include <unoparaframeenum.hxx>
+#include <unotextrange.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unomid.h>
+#include <txtatr.hxx>
+#include <ndtxt.hxx>
+#include <doc.hxx>
+#include <frmfmt.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/SetPropertyTolerantFailed.hpp>
+#include <com/sun/star/beans/GetPropertyTolerantResult.hpp>
+#include <com/sun/star/beans/TolerantPropertySetResultType.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/text/XFootnote.hpp>
+#include <com/sun/star/text/XTextField.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace ::com::sun::star;
+
+void SwXTextPortion::init(const SwUnoCursor* pPortionCursor)
+{
+ m_pUnoCursor = pPortionCursor->GetDoc().CreateUnoCursor(*pPortionCursor->GetPoint());
+ if (pPortionCursor->HasMark())
+ {
+ m_pUnoCursor->SetMark();
+ *m_pUnoCursor->GetMark() = *pPortionCursor->GetMark();
+ }
+}
+
+SwXTextPortion::SwXTextPortion(
+ const SwUnoCursor* pPortionCursor,
+ uno::Reference< text::XText > const& rParent,
+ SwTextPortionType eType)
+ : m_pPropSet(aSwMapProvider.GetPropertySet(
+ (PORTION_REDLINE_START == eType ||
+ PORTION_REDLINE_END == eType)
+ ? PROPERTY_MAP_REDLINE_PORTION
+ : PROPERTY_MAP_TEXTPORTION_EXTENSIONS))
+ , m_xParentText(rParent)
+ , m_pFrameFormat(nullptr)
+ , m_ePortionType(eType != PORTION_LIST_AUTOFMT ? eType : PORTION_TEXT)
+ , m_bIsCollapsed(false)
+ , m_bIsListAutoFormat(false)
+{
+ if (eType == PORTION_LIST_AUTOFMT)
+ {
+ m_bIsListAutoFormat = true;
+ }
+ init( pPortionCursor);
+}
+
+SwXTextPortion::SwXTextPortion(
+ const SwUnoCursor* pPortionCursor,
+ uno::Reference< text::XText > const& rParent,
+ SwFrameFormat& rFormat )
+ : m_pPropSet(aSwMapProvider.GetPropertySet(
+ PROPERTY_MAP_TEXTPORTION_EXTENSIONS))
+ , m_xParentText(rParent)
+ , m_pFrameFormat(&rFormat)
+ , m_ePortionType(PORTION_FRAME)
+ , m_bIsCollapsed(false)
+ , m_bIsListAutoFormat(false)
+{
+ StartListening(rFormat.GetNotifier());
+ init( pPortionCursor);
+}
+
+SwXTextPortion::SwXTextPortion(
+ const SwUnoCursor* pPortionCursor,
+ SwTextRuby const& rAttr,
+ uno::Reference< text::XText > const& xParent,
+ bool bIsEnd )
+ : m_pPropSet(aSwMapProvider.GetPropertySet(
+ PROPERTY_MAP_TEXTPORTION_EXTENSIONS))
+ , m_xParentText(xParent)
+ , m_pRubyText ( bIsEnd ? nullptr : new uno::Any )
+ , m_pRubyStyle ( bIsEnd ? nullptr : new uno::Any )
+ , m_pRubyAdjust ( bIsEnd ? nullptr : new uno::Any )
+ , m_pRubyIsAbove( bIsEnd ? nullptr : new uno::Any )
+ , m_pRubyPosition( bIsEnd ? nullptr : new uno::Any )
+ , m_pFrameFormat(nullptr)
+ , m_ePortionType( bIsEnd ? PORTION_RUBY_END : PORTION_RUBY_START )
+ , m_bIsCollapsed(false)
+ , m_bIsListAutoFormat(false)
+{
+ init( pPortionCursor);
+
+ if (!bIsEnd)
+ {
+ const SfxPoolItem& rItem = rAttr.GetAttr();
+ rItem.QueryValue(*m_pRubyText);
+ rItem.QueryValue(*m_pRubyStyle, MID_RUBY_CHARSTYLE);
+ rItem.QueryValue(*m_pRubyAdjust, MID_RUBY_ADJUST);
+ rItem.QueryValue(*m_pRubyIsAbove, MID_RUBY_ABOVE);
+ rItem.QueryValue(*m_pRubyPosition, MID_RUBY_POSITION);
+ }
+}
+
+SwXTextPortion::~SwXTextPortion()
+{
+ SolarMutexGuard aGuard;
+ m_pUnoCursor.reset(nullptr);
+ EndListeningAll();
+}
+
+uno::Reference< text::XText > SwXTextPortion::getText()
+{
+ return m_xParentText;
+}
+
+uno::Reference< text::XTextRange > SwXTextPortion::getStart()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< text::XTextRange > xRet;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ SwPaM aPam(*rUnoCursor.Start());
+ uno::Reference< text::XText > xParent = getText();
+ xRet = new SwXTextRange(aPam, xParent);
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SwXTextPortion::getEnd()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< text::XTextRange > xRet;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ SwPaM aPam(*rUnoCursor.End());
+ uno::Reference< text::XText > xParent = getText();
+ xRet = new SwXTextRange(aPam, xParent);
+ return xRet;
+}
+
+OUString SwXTextPortion::getString()
+{
+ SolarMutexGuard aGuard;
+ OUString aText;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ // TextPortions are always within a paragraph
+ SwTextNode* pTextNd = rUnoCursor.GetNode().GetTextNode();
+ if ( pTextNd )
+ {
+ const sal_Int32 nStt = rUnoCursor.Start()->nContent.GetIndex();
+ aText = pTextNd->GetExpandText(nullptr, nStt,
+ rUnoCursor.End()->nContent.GetIndex() - nStt,
+ false, false, false, ExpandMode::ExpandFootnote);
+ }
+ return aText;
+}
+
+void SwXTextPortion::setString(const OUString& aString)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ SwUnoCursorHelper::SetString(rUnoCursor, aString);
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXTextPortion::getPropertySetInfo()
+{
+ SolarMutexGuard aGuard;
+ //! PropertySetInfo for text portion extensions
+ static uno::Reference< beans::XPropertySetInfo >
+ xTextPorExtRef = aSwMapProvider.GetPropertySet(
+ PROPERTY_MAP_TEXTPORTION_EXTENSIONS)->getPropertySetInfo();
+ //! PropertySetInfo for redline portions
+ static uno::Reference< beans::XPropertySetInfo >
+ xRedlPorRef = aSwMapProvider.GetPropertySet(
+ PROPERTY_MAP_REDLINE_PORTION)->getPropertySetInfo();
+
+ return (PORTION_REDLINE_START == m_ePortionType ||
+ PORTION_REDLINE_END == m_ePortionType) ? xRedlPorRef : xTextPorExtRef;
+}
+
+void SwXTextPortion::setPropertyValue(const OUString& rPropertyName,
+ const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ SwUnoCursorHelper::SetPropertyValue(rUnoCursor, *m_pPropSet,
+ rPropertyName, aValue);
+}
+
+void SwXTextPortion::GetPropertyValue(
+ uno::Any &rVal,
+ const SfxItemPropertyMapEntry& rEntry,
+ SwUnoCursor *pUnoCursor,
+ std::unique_ptr<SfxItemSet> &pSet )
+{
+ OSL_ENSURE( pUnoCursor, "UNO cursor missing" );
+ if (!pUnoCursor)
+ return;
+ switch(rEntry.nWID)
+ {
+ case FN_UNO_TEXT_PORTION_TYPE:
+ {
+ const char* pRet;
+ switch (m_ePortionType)
+ {
+ case PORTION_TEXT: pRet = "Text";break;
+ case PORTION_FIELD: pRet = "TextField";break;
+ case PORTION_FRAME: pRet = "Frame";break;
+ case PORTION_FOOTNOTE: pRet = "Footnote";break;
+ case PORTION_REFMARK_START:
+ case PORTION_REFMARK_END: pRet = UNO_NAME_REFERENCE_MARK;break;
+ case PORTION_TOXMARK_START:
+ case PORTION_TOXMARK_END: pRet = UNO_NAME_DOCUMENT_INDEX_MARK;break;
+ case PORTION_BOOKMARK_START :
+ case PORTION_BOOKMARK_END : pRet = UNO_NAME_BOOKMARK;break;
+ case PORTION_REDLINE_START:
+ case PORTION_REDLINE_END: pRet = "Redline";break;
+ case PORTION_RUBY_START:
+ case PORTION_RUBY_END: pRet = "Ruby";break;
+ case PORTION_SOFT_PAGEBREAK:pRet = "SoftPageBreak";break;
+ case PORTION_META: pRet = UNO_NAME_META; break;
+ case PORTION_FIELD_START:pRet = "TextFieldStart";break;
+ case PORTION_FIELD_SEP: pRet = "TextFieldSeparator";break;
+ case PORTION_FIELD_END:pRet = "TextFieldEnd";break;
+ case PORTION_FIELD_START_END:pRet = "TextFieldStartEnd";break;
+ case PORTION_ANNOTATION:
+ pRet = "Annotation";
+ break;
+ case PORTION_ANNOTATION_END:
+ pRet = "AnnotationEnd";
+ break;
+ case PORTION_LINEBREAK:
+ pRet = "LineBreak";
+ break;
+ case PORTION_CONTENT_CONTROL:
+ pRet = UNO_NAME_CONTENT_CONTROL;
+ break;
+ default:
+ pRet = nullptr;
+ }
+
+ OUString sRet;
+ if( pRet )
+ sRet = OUString::createFromAscii( pRet );
+ rVal <<= sRet;
+ }
+ break;
+ case FN_UNO_CONTROL_CHARACTER: // obsolete!
+ break;
+ case FN_UNO_DOCUMENT_INDEX_MARK:
+ rVal <<= m_xTOXMark;
+ break;
+ case FN_UNO_REFERENCE_MARK:
+ rVal <<= m_xRefMark;
+ break;
+ case FN_UNO_BOOKMARK:
+ rVal <<= m_xBookmark;
+ break;
+ case FN_UNO_FOOTNOTE:
+ rVal <<= m_xFootnote;
+ break;
+ case FN_UNO_TEXT_FIELD:
+ rVal <<= m_xTextField;
+ break;
+ case FN_UNO_META:
+ rVal <<= m_xMeta;
+ break;
+ case FN_UNO_LINEBREAK:
+ rVal <<= m_xLineBreak;
+ break;
+ case FN_UNO_CONTENT_CONTROL:
+ rVal <<= m_xContentControl;
+ break;
+ case FN_UNO_IS_COLLAPSED:
+ {
+ switch (m_ePortionType)
+ {
+ case PORTION_REFMARK_START:
+ case PORTION_BOOKMARK_START :
+ case PORTION_TOXMARK_START:
+ case PORTION_REFMARK_END:
+ case PORTION_TOXMARK_END:
+ case PORTION_BOOKMARK_END :
+ case PORTION_REDLINE_START :
+ case PORTION_REDLINE_END :
+ case PORTION_RUBY_START:
+ case PORTION_RUBY_END:
+ case PORTION_FIELD_START:
+ case PORTION_FIELD_SEP:
+ case PORTION_FIELD_END:
+ rVal <<= m_bIsCollapsed;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case FN_UNO_IS_START:
+ {
+ bool bStart = true, bPut = true;
+ switch (m_ePortionType)
+ {
+ case PORTION_REFMARK_START:
+ case PORTION_BOOKMARK_START:
+ case PORTION_TOXMARK_START:
+ case PORTION_REDLINE_START:
+ case PORTION_RUBY_START:
+ case PORTION_FIELD_START:
+ break;
+
+ case PORTION_REFMARK_END:
+ case PORTION_TOXMARK_END:
+ case PORTION_BOOKMARK_END:
+ case PORTION_REDLINE_END:
+ case PORTION_RUBY_END:
+ case PORTION_FIELD_SEP:
+ case PORTION_FIELD_END:
+ bStart = false;
+ break;
+ default:
+ bPut = false;
+ }
+ if(bPut)
+ rVal <<= bStart;
+ }
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ {
+ const uno::Any* pToSet = nullptr;
+ switch(rEntry.nMemberId)
+ {
+ case MID_RUBY_TEXT : pToSet = m_pRubyText.get(); break;
+ case MID_RUBY_ADJUST : pToSet = m_pRubyAdjust.get(); break;
+ case MID_RUBY_CHARSTYLE:pToSet = m_pRubyStyle.get(); break;
+ case MID_RUBY_ABOVE : pToSet = m_pRubyIsAbove.get();break;
+ case MID_RUBY_POSITION: pToSet = m_pRubyPosition.get();break;
+ }
+ if(pToSet)
+ rVal = *pToSet;
+ }
+ break;
+ default:
+ beans::PropertyState eTemp;
+ bool bDone = false;
+ if (m_bIsListAutoFormat)
+ {
+ SwTextNode* pTextNode = pUnoCursor->GetNode().GetTextNode();
+ std::shared_ptr<SfxItemSet> pListSet
+ = pTextNode->GetAttr(RES_PARATR_LIST_AUTOFMT).GetStyleHandle();
+ if (pListSet)
+ {
+ m_pPropSet->getPropertyValue(rEntry, *pListSet, rVal);
+ bDone = true;
+ }
+ }
+ if (!bDone)
+ {
+ bDone = SwUnoCursorHelper::getCursorPropertyValue(
+ rEntry, *pUnoCursor, &rVal, eTemp );
+ }
+ if(!bDone)
+ {
+ if(!pSet)
+ {
+ pSet = std::make_unique<SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER,
+ RES_UNKNOWNATR_CONTAINER>>(pUnoCursor->GetDoc().GetAttrPool());
+ SwUnoCursorHelper::GetCursorAttr(*pUnoCursor, *pSet);
+ }
+ m_pPropSet->getPropertyValue(rEntry, *pSet, rVal);
+ }
+ }
+}
+
+uno::Sequence< uno::Any > SwXTextPortion::GetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ sal_Int32 nLength = rPropertyNames.getLength();
+ const OUString *pPropertyNames = rPropertyNames.getConstArray();
+ uno::Sequence< uno::Any > aValues(nLength);
+ uno::Any *pValues = aValues.getArray();
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ {
+ std::unique_ptr<SfxItemSet> pSet;
+ // get starting point for the look-up, either the provided one or else
+ // from the beginning of the map
+ const SfxItemPropertyMap& rMap = m_pPropSet->getPropertyMap();
+ for(sal_Int32 nProp = 0; nProp < nLength; nProp++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(pPropertyNames[nProp]);
+ if(!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + pPropertyNames[nProp], static_cast < cppu::OWeakObject * > ( this ) );
+ GetPropertyValue( pValues[nProp], *pEntry, &rUnoCursor, pSet );
+ }
+ }
+ return aValues;
+}
+
+uno::Any SwXTextPortion::getPropertyValue(
+ const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< OUString > aPropertyNames { rPropertyName };
+ return GetPropertyValues_Impl(aPropertyNames).getConstArray()[0];
+}
+
+void SwXTextPortion::SetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues )
+{
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ {
+ const OUString* pPropertyNames = rPropertyNames.getConstArray();
+ const uno::Any* pValues = rValues.getConstArray();
+ const SfxItemPropertyMap& rMap = m_pPropSet->getPropertyMap();
+ uno::Sequence< beans::PropertyValue > aValues( rPropertyNames.getLength() );
+ auto aValuesRange = asNonConstRange(aValues);
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); nProp++)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(pPropertyNames[nProp]);
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + pPropertyNames[nProp], static_cast < cppu::OWeakObject * > ( this ) );
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException ("Property is read-only: " + pPropertyNames[nProp], static_cast < cppu::OWeakObject * > ( this ) );
+
+ aValuesRange[nProp].Name = pPropertyNames[nProp];
+ aValuesRange[nProp].Value = pValues[nProp];
+ }
+ SwUnoCursorHelper::SetPropertyValues( rUnoCursor, *m_pPropSet, aValues );
+ }
+}
+
+void SwXTextPortion::setPropertyValues(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues )
+{
+ SolarMutexGuard aGuard;
+
+ // workaround for bad designed API
+ try
+ {
+ SetPropertyValues_Impl( rPropertyNames, rValues );
+ }
+ catch (const beans::UnknownPropertyException &rException)
+ {
+ // wrap the original (here not allowed) exception in
+ // a lang::WrappedTargetException that gets thrown instead.
+ lang::WrappedTargetException aWExc;
+ aWExc.TargetException <<= rException;
+ throw aWExc;
+ }
+}
+
+uno::Sequence< uno::Any > SwXTextPortion::getPropertyValues(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< uno::Any > aValues;
+
+ // workaround for bad designed API
+ try
+ {
+ aValues = GetPropertyValues_Impl( rPropertyNames );
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("Unknown property exception caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("WrappedTargetException caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+
+ return aValues;
+}
+
+/* disabled for #i46921# */
+uno::Sequence< beans::SetPropertyTolerantFailed > SAL_CALL SwXTextPortion::setPropertyValuesTolerant(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues )
+{
+ SolarMutexGuard aGuard;
+
+ if (rPropertyNames.getLength() != rValues.getLength())
+ throw lang::IllegalArgumentException();
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ sal_Int32 nProps = rPropertyNames.getLength();
+ const OUString *pProp = rPropertyNames.getConstArray();
+
+ //sal_Int32 nVals = rValues.getLength();
+ const uno::Any *pValue = rValues.getConstArray();
+
+ sal_Int32 nFailed = 0;
+ uno::Sequence< beans::SetPropertyTolerantFailed > aFailed( nProps );
+ beans::SetPropertyTolerantFailed *pFailed = aFailed.getArray();
+
+ const SfxItemPropertyMap& rPropMap = m_pPropSet->getPropertyMap();
+
+ for (sal_Int32 i = 0; i < nProps; ++i)
+ {
+ try
+ {
+ pFailed[ nFailed ].Name = pProp[i];
+
+ const SfxItemPropertyMapEntry* pEntry = rPropMap.getByName( pProp[i] );
+ if (!pEntry)
+ pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ else
+ {
+ // set property value
+ // (compare to SwXTextPortion::setPropertyValues)
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ else
+ {
+ SwUnoCursorHelper::SetPropertyValue(
+ rUnoCursor, *m_pPropSet, pProp[i], pValue[i] );
+ }
+ }
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ // should not occur because property was searched for before
+ TOOLS_WARN_EXCEPTION( "sw", "" );
+ pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT;
+ }
+ catch (beans::PropertyVetoException &)
+ {
+ pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ pFailed[ nFailed++ ].Result = beans::TolerantPropertySetResultType::WRAPPED_TARGET;
+ }
+ }
+
+ aFailed.realloc( nFailed );
+ return aFailed;
+}
+
+uno::Sequence< beans::GetPropertyTolerantResult > SAL_CALL SwXTextPortion::getPropertyValuesTolerant(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ SolarMutexGuard aGuard;
+
+ const uno::Sequence< beans::GetDirectPropertyTolerantResult > aTmpRes(
+ GetPropertyValuesTolerant_Impl( rPropertyNames, false ) );
+
+ // copy temporary result to final result type
+ sal_Int32 nLen = aTmpRes.getLength();
+ uno::Sequence< beans::GetPropertyTolerantResult > aRes( nLen );
+ std::copy(aTmpRes.begin(), aTmpRes.end(), aRes.getArray());
+ return aRes;
+}
+
+uno::Sequence< beans::GetDirectPropertyTolerantResult > SAL_CALL SwXTextPortion::getDirectPropertyValuesTolerant(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ return GetPropertyValuesTolerant_Impl( rPropertyNames, true );
+}
+
+uno::Sequence< beans::GetDirectPropertyTolerantResult > SwXTextPortion::GetPropertyValuesTolerant_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ bool bDirectValuesOnly )
+{
+ SolarMutexGuard aGuard;
+
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ std::vector< beans::GetDirectPropertyTolerantResult > aResultVector;
+
+ try
+ {
+ sal_Int32 nProps = rPropertyNames.getLength();
+ const OUString *pProp = rPropertyNames.getConstArray();
+
+ std::unique_ptr<SfxItemSet> pSet;
+
+ const SfxItemPropertyMap& rPropMap = m_pPropSet->getPropertyMap();
+
+
+ uno::Sequence< beans::PropertyState > aPropertyStates;
+ if (m_bIsListAutoFormat)
+ {
+ SwTextNode* pTextNode = rUnoCursor.GetNode().GetTextNode();
+ std::shared_ptr<SfxItemSet> pListSet
+ = pTextNode->GetAttr(RES_PARATR_LIST_AUTOFMT).GetStyleHandle();
+ if (pListSet)
+ {
+ std::vector<beans::PropertyState> aStates;
+ for (const auto& rPropertyName : rPropertyNames)
+ {
+ aStates.push_back(m_pPropSet->getPropertyState(rPropertyName, *pListSet));
+ }
+ aPropertyStates = comphelper::containerToSequence(aStates);
+ }
+ }
+ if (!aPropertyStates.hasElements())
+ {
+ aPropertyStates =
+ SwUnoCursorHelper::GetPropertyStates(
+ rUnoCursor, *m_pPropSet,
+ rPropertyNames,
+ SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION_TOLERANT );
+ }
+ const beans::PropertyState* pPropertyStates = aPropertyStates.getConstArray();
+
+ for (sal_Int32 i = 0; i < nProps; ++i)
+ {
+ beans::GetDirectPropertyTolerantResult aResult;
+ try
+ {
+ aResult.Name = pProp[i];
+ if(pPropertyStates[i] == beans::PropertyState::PropertyState_MAKE_FIXED_SIZE) // property unknown?
+ {
+ if( bDirectValuesOnly )
+ continue;
+ else
+ aResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ else
+ {
+ const SfxItemPropertyMapEntry* pEntry = rPropMap.getByName( pProp[i] );
+ if (!pEntry)
+ throw beans::UnknownPropertyException( "Unknown property: " + pProp[i], static_cast < cppu::OWeakObject * > ( this ) );
+ aResult.State = pPropertyStates[i];
+
+ aResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_FAILURE;
+ //#i104499# ruby portion attributes need special handling:
+ if( pEntry->nWID == RES_TXTATR_CJK_RUBY &&
+ m_ePortionType == PORTION_RUBY_START )
+ {
+ aResult.State = beans::PropertyState_DIRECT_VALUE;
+ }
+ if (!bDirectValuesOnly || beans::PropertyState_DIRECT_VALUE == aResult.State)
+ {
+ // get property value
+ // (compare to SwXTextPortion::getPropertyValue(s))
+ GetPropertyValue( aResult.Value, *pEntry, &rUnoCursor, pSet );
+ aResult.Result = beans::TolerantPropertySetResultType::SUCCESS;
+ aResultVector.push_back( aResult );
+ }
+ }
+ }
+ catch (const beans::UnknownPropertyException &)
+ {
+ // should not occur because property was searched for before
+ TOOLS_WARN_EXCEPTION( "sw", "unexpected exception caught" );
+ aResult.Result = beans::TolerantPropertySetResultType::UNKNOWN_PROPERTY;
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ aResult.Result = beans::TolerantPropertySetResultType::ILLEGAL_ARGUMENT;
+ }
+ catch (const beans::PropertyVetoException &)
+ {
+ aResult.Result = beans::TolerantPropertySetResultType::PROPERTY_VETO;
+ }
+ catch (const lang::WrappedTargetException &)
+ {
+ aResult.Result = beans::TolerantPropertySetResultType::WRAPPED_TARGET;
+ }
+ }
+ }
+ catch (const uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const uno::Exception& e)
+ {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::lang::WrappedTargetRuntimeException(
+ "wrapped Exception " + e.Message,
+ css::uno::Reference<css::uno::XInterface>(), a);
+ }
+
+ return comphelper::containerToSequence(aResultVector);
+}
+
+void SwXTextPortion::addPropertiesChangeListener(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{}
+
+void SwXTextPortion::removePropertiesChangeListener(
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{}
+
+void SwXTextPortion::firePropertiesChangeEvent(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{}
+
+void SwXTextPortion::addPropertyChangeListener(
+ const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextPortion::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextPortion::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextPortion::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+beans::PropertyState SwXTextPortion::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ beans::PropertyState eRet = beans::PropertyState_DEFAULT_VALUE;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ if (GetTextPortionType() == PORTION_RUBY_START &&
+ rPropertyName.startsWith("Ruby"))
+ {
+ eRet = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ eRet = SwUnoCursorHelper::GetPropertyState(rUnoCursor, *m_pPropSet,
+ rPropertyName);
+ }
+ return eRet;
+}
+
+uno::Sequence< beans::PropertyState > SwXTextPortion::getPropertyStates(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ uno::Sequence< beans::PropertyState > aRet =
+ SwUnoCursorHelper::GetPropertyStates(rUnoCursor, *m_pPropSet,
+ rPropertyNames, SW_PROPERTY_STATE_CALLER_SWX_TEXT_PORTION);
+
+ if(GetTextPortionType() == PORTION_RUBY_START)
+ {
+ const OUString* pNames = rPropertyNames.getConstArray();
+ beans::PropertyState* pStates = aRet.getArray();
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength();nProp++)
+ {
+ if (pNames[nProp].startsWith("Ruby"))
+ pStates[nProp] = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ return aRet;
+}
+
+void SwXTextPortion::setPropertyToDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ SwUnoCursorHelper::SetPropertyToDefault(
+ rUnoCursor, *m_pPropSet, rPropertyName);
+}
+
+uno::Any SwXTextPortion::getPropertyDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ aRet = SwUnoCursorHelper::GetPropertyDefault(rUnoCursor, *m_pPropSet,
+ rPropertyName);
+ return aRet;
+}
+
+uno::Reference< container::XEnumeration > SwXTextPortion::createContentEnumeration(const OUString& /*aServiceName*/)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ return SwXParaFrameEnumeration::Create(rUnoCursor, PARAFRAME_PORTION_CHAR, m_pFrameFormat);
+}
+
+const uno::Sequence< sal_Int8 > & SwXTextPortion::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextPortionUnoTunnelId;
+ return theSwXTextPortionUnoTunnelId.getSeq();
+}
+
+sal_Int64 SwXTextPortion::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+uno::Sequence< OUString > SwXTextPortion::getAvailableServiceNames()
+{
+ return { "com.sun.star.text.TextContent" };
+}
+
+OUString SwXTextPortion::getImplementationName()
+{
+ return { "SwXTextPortion" };
+}
+
+sal_Bool SwXTextPortion::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextPortion::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextPortion",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex" };
+}
+
+void SwXTextPortion::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pFrameFormat = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoportenum.cxx b/sw/source/core/unocore/unoportenum.cxx
new file mode 100644
index 000000000..32b17f781
--- /dev/null
+++ b/sw/source/core/unocore/unoportenum.cxx
@@ -0,0 +1,1548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <utility>
+
+#include <unoport.hxx>
+#include <IMark.hxx>
+#include <crossrefbookmark.hxx>
+#include <annotationmark.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <txatbase.hxx>
+#include <txtatr.hxx>
+#include <ndhints.hxx>
+#include <ndtxt.hxx>
+#include <unocrsr.hxx>
+#include <docary.hxx>
+#include <textboxhelper.hxx>
+#include <tox.hxx>
+#include <unoparaframeenum.hxx>
+#include <unocrsrhelper.hxx>
+#include <unorefmark.hxx>
+#include <unobookmark.hxx>
+#include <unofield.hxx>
+#include <unometa.hxx>
+#include <unolinebreak.hxx>
+#include <unocontentcontrol.hxx>
+#include <fmtfld.hxx>
+#include <fldbas.hxx>
+#include <fmtmeta.hxx>
+#include <fmtanchr.hxx>
+#include <fmtrfmrk.hxx>
+#include <frmfmt.hxx>
+#include <fmtflcnt.hxx>
+#include <unoidx.hxx>
+#include <unocoll.hxx>
+#include <redline.hxx>
+#include <txtannotationfld.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <stack>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::text;
+using namespace ::std;
+
+typedef std::pair< TextRangeList_t * const, SwTextAttr const * const > PortionList_t;
+typedef std::stack< PortionList_t > PortionStack_t;
+
+static void lcl_CreatePortions(
+ TextRangeList_t & i_rPortions,
+ uno::Reference< text::XText > const& i_xParentText,
+ SwUnoCursor* pUnoCursor,
+ FrameClientSortList_t & i_rFrames,
+ const sal_Int32 i_nStartPos, const sal_Int32 i_nEndPos );
+
+namespace
+{
+ enum class BkmType {
+ Start, End, StartEnd
+ };
+
+ struct SwXBookmarkPortion_Impl
+ {
+ Reference<XTextContent> xBookmark;
+ BkmType nBkmType;
+ const SwPosition aPosition;
+
+ SwXBookmarkPortion_Impl(uno::Reference<text::XTextContent> const& xMark,
+ const BkmType nType, SwPosition const& rPosition)
+ : xBookmark ( xMark )
+ , nBkmType ( nType )
+ , aPosition ( rPosition )
+ {
+ }
+ sal_Int32 getIndex () const
+ {
+ return aPosition.nContent.GetIndex();
+ }
+ };
+ typedef std::shared_ptr < SwXBookmarkPortion_Impl > SwXBookmarkPortion_ImplSharedPtr;
+ struct BookmarkCompareStruct
+ {
+ bool operator () ( const SwXBookmarkPortion_ImplSharedPtr &r1,
+ const SwXBookmarkPortion_ImplSharedPtr &r2 ) const
+ {
+ // #i16896# for bookmark portions at the same position, the start should
+ // always precede the end. Hence compare positions, and use bookmark type
+ // as tie-breaker for same position.
+ // return ( r1->nIndex == r2->nIndex )
+ // ? ( r1->nBkmType < r2->nBkmType )
+ // : ( r1->nIndex < r2->nIndex );
+
+ // Note that the above code does not correctly handle
+ // the case when one bookmark ends, and another begins in the same
+ // position. When this occurs, the above code will return the
+ // start of the 2nd bookmark BEFORE the end of the first bookmark
+ // See bug #i58438# for more details. The below code is correct and
+ // fixes both #i58438 and #i16896#
+ return r1->aPosition < r2->aPosition;
+ }
+ };
+ typedef std::multiset < SwXBookmarkPortion_ImplSharedPtr, BookmarkCompareStruct > SwXBookmarkPortion_ImplList;
+
+ /// Inserts pBkmk to rBkmArr in case it starts or ends at nOwnNode
+ void lcl_FillBookmark(sw::mark::IMark* const pBkmk, const SwNodeIndex& nOwnNode, SwDoc& rDoc, SwXBookmarkPortion_ImplList& rBkmArr)
+ {
+ bool const hasOther = pBkmk->IsExpanded();
+
+ const SwPosition& rStartPos = pBkmk->GetMarkStart();
+ if(rStartPos.nNode == nOwnNode)
+ {
+ // #i109272#: cross reference marks: need special handling!
+ ::sw::mark::CrossRefBookmark *const pCrossRefMark(dynamic_cast< ::sw::mark::CrossRefBookmark*>(pBkmk));
+ BkmType const nType = (hasOther || pCrossRefMark)
+ ? BkmType::Start : BkmType::StartEnd;
+ rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
+ SwXBookmark::CreateXBookmark(rDoc, pBkmk),
+ nType, rStartPos));
+ }
+
+ const SwPosition& rEndPos = pBkmk->GetMarkEnd();
+ if(rEndPos.nNode != nOwnNode)
+ return;
+
+ unique_ptr<SwPosition> pCrossRefEndPos;
+ const SwPosition* pEndPos = nullptr;
+ ::sw::mark::CrossRefBookmark *const pCrossRefMark(dynamic_cast< ::sw::mark::CrossRefBookmark*>(pBkmk));
+ if(hasOther)
+ {
+ pEndPos = &rEndPos;
+ }
+ else if (pCrossRefMark)
+ {
+ // Crossrefbookmarks only remember the start position but have to span the whole paragraph
+ pCrossRefEndPos = std::make_unique<SwPosition>(rEndPos);
+ pCrossRefEndPos->nContent = pCrossRefEndPos->nNode.GetNode().GetTextNode()->Len();
+ pEndPos = pCrossRefEndPos.get();
+ }
+ if(pEndPos)
+ {
+ rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
+ SwXBookmark::CreateXBookmark(rDoc, pBkmk),
+ BkmType::End, *pEndPos));
+ }
+ }
+
+ void lcl_FillBookmarkArray(SwDoc& rDoc, SwUnoCursor& rUnoCursor, SwXBookmarkPortion_ImplList& rBkmArr)
+ {
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ if(!pMarkAccess->getBookmarksCount())
+ return;
+
+ const SwNodeIndex nOwnNode = rUnoCursor.GetPoint()->nNode;
+ SwTextNode* pTextNode = nOwnNode.GetNode().GetTextNode();
+ assert(pTextNode);
+ // A text node already knows its marks via its SwIndexes.
+ o3tl::sorted_vector<const sw::mark::IMark*> aSeenMarks;
+ for (const SwIndex* pIndex = pTextNode->GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
+ {
+ // Need a non-cost mark here, as we'll create a UNO wrapper around it.
+ sw::mark::IMark* pBkmk = const_cast<sw::mark::IMark*>(pIndex->GetMark());
+ if (!pBkmk)
+ continue;
+ IDocumentMarkAccess::MarkType eType = IDocumentMarkAccess::GetType(*pBkmk);
+ // These are the types stored in the container otherwise accessible via getBookmarks*()
+ if (eType != IDocumentMarkAccess::MarkType::BOOKMARK && eType != IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK &&
+ eType != IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK)
+ continue;
+ // Only handle bookmarks once, if they start and end at this node as well.
+ if (!aSeenMarks.insert(pBkmk).second)
+ continue;
+ lcl_FillBookmark(pBkmk, nOwnNode, rDoc, rBkmArr);
+ }
+ }
+
+ struct SwAnnotationStartPortion_Impl
+ {
+
+ uno::Reference< text::XTextField > mxAnnotationField;
+ const SwPosition maPosition;
+
+ SwAnnotationStartPortion_Impl(
+ uno::Reference< text::XTextField > const& xAnnotationField,
+ SwPosition const& rPosition)
+ : mxAnnotationField ( xAnnotationField )
+ , maPosition ( rPosition )
+ {
+ }
+
+ sal_Int32 getIndex () const
+ {
+ return maPosition.nContent.GetIndex();
+ }
+ };
+ typedef std::shared_ptr < SwAnnotationStartPortion_Impl > SwAnnotationStartPortion_ImplSharedPtr;
+ struct AnnotationStartCompareStruct
+ {
+ bool operator () ( const SwAnnotationStartPortion_ImplSharedPtr &r1,
+ const SwAnnotationStartPortion_ImplSharedPtr &r2 )
+ const
+ {
+ return r1->maPosition < r2->maPosition;
+ }
+ };
+ typedef std::multiset < SwAnnotationStartPortion_ImplSharedPtr, AnnotationStartCompareStruct > SwAnnotationStartPortion_ImplList;
+
+ void lcl_FillAnnotationStartArray(
+ SwDoc& rDoc,
+ SwUnoCursor& rUnoCursor,
+ SwAnnotationStartPortion_ImplList& rAnnotationStartArr )
+ {
+ IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
+ if ( pMarkAccess->getAnnotationMarksCount() == 0 )
+ {
+ return;
+ }
+
+ // no need to consider annotation marks starting after aEndOfPara
+ SwPosition aEndOfPara(*rUnoCursor.GetPoint());
+ aEndOfPara.nContent = aEndOfPara.nNode.GetNode().GetTextNode()->Len();
+ const IDocumentMarkAccess::const_iterator_t pCandidatesEnd =
+ pMarkAccess->findFirstAnnotationStartsAfter(aEndOfPara);
+
+ // search for all annotation marks that have its start position in this paragraph
+ const SwNodeIndex nOwnNode = rUnoCursor.GetPoint()->nNode;
+ for( IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAnnotationMarksBegin();
+ ppMark != pCandidatesEnd;
+ ++ppMark )
+ {
+ ::sw::mark::AnnotationMark* const pAnnotationMark =
+ dynamic_cast< ::sw::mark::AnnotationMark* >(*ppMark);
+
+ if (!pAnnotationMark)
+ continue;
+
+ const SwPosition& rStartPos = pAnnotationMark->GetMarkStart();
+ if (rStartPos.nNode != nOwnNode)
+ continue;
+
+ const SwFormatField* pAnnotationFormatField = pAnnotationMark->GetAnnotationFormatField();
+ if (!pAnnotationFormatField)
+ {
+ SAL_WARN("sw.core", "missing annotation format field");
+ continue;
+ }
+
+ rAnnotationStartArr.insert(
+ std::make_shared<SwAnnotationStartPortion_Impl>(
+ SwXTextField::CreateXTextField(&rDoc,
+ pAnnotationFormatField),
+ rStartPos));
+ }
+ }
+}
+
+const uno::Sequence< sal_Int8 > & SwXTextPortionEnumeration::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextPortionEnumerationUnoTunnelId;
+ return theSwXTextPortionEnumerationUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXTextPortionEnumeration::getSomething(
+ const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+OUString SwXTextPortionEnumeration::getImplementationName()
+{
+ return "SwXTextPortionEnumeration";
+}
+
+sal_Bool
+SwXTextPortionEnumeration::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXTextPortionEnumeration::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextPortionEnumeration" };
+}
+
+SwXTextPortionEnumeration::SwXTextPortionEnumeration(
+ SwPaM& rParaCursor,
+ uno::Reference< XText > const & xParentText,
+ const sal_Int32 nStart,
+ const sal_Int32 nEnd )
+{
+ m_pUnoCursor = rParaCursor.GetDoc().CreateUnoCursor(*rParaCursor.GetPoint());
+
+ OSL_ENSURE(nEnd == -1 || (nStart <= nEnd &&
+ nEnd <= m_pUnoCursor->Start()->nNode.GetNode().GetTextNode()->GetText().getLength()),
+ "start or end value invalid!");
+
+ // find all frames, graphics and OLEs that are bound AT character in para
+ FrameClientSortList_t frames;
+ ::CollectFrameAtNode(m_pUnoCursor->GetPoint()->nNode, frames, true);
+ lcl_CreatePortions(m_Portions, xParentText, &*m_pUnoCursor, frames, nStart, nEnd);
+}
+
+SwXTextPortionEnumeration::SwXTextPortionEnumeration(
+ SwPaM& rParaCursor,
+ TextRangeList_t && rPortions )
+ : m_Portions( std::move(rPortions) )
+{
+ m_pUnoCursor = rParaCursor.GetDoc().CreateUnoCursor(*rParaCursor.GetPoint());
+}
+
+SwXTextPortionEnumeration::~SwXTextPortionEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if( m_pUnoCursor )
+ {
+ m_pUnoCursor->GetDoc().cleanupUnoCursorTable();
+ m_pUnoCursor.reset(nullptr);
+ }
+}
+
+sal_Bool SwXTextPortionEnumeration::hasMoreElements()
+{
+ SolarMutexGuard aGuard;
+
+ return !m_Portions.empty();
+}
+
+uno::Any SwXTextPortionEnumeration::nextElement()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_Portions.empty())
+ throw container::NoSuchElementException();
+
+ Any any;
+ any <<= m_Portions.front();
+ m_Portions.pop_front();
+ return any;
+}
+
+static void
+lcl_FillFieldMarkArray(std::deque<sal_Int32> & rFieldMarks, SwUnoCursor const & rUnoCursor,
+ const sal_Int32 i_nStartPos)
+{
+ const SwTextNode * const pTextNode =
+ rUnoCursor.GetPoint()->nNode.GetNode().GetTextNode();
+ if (!pTextNode) return;
+
+ const sal_Unicode fld[] = {
+ CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP, CH_TXT_ATR_FIELDEND, CH_TXT_ATR_FORMELEMENT, 0 };
+ sal_Int32 pos = std::max(static_cast<sal_Int32>(0), i_nStartPos);
+ while ((pos = ::comphelper::string::indexOfAny(pTextNode->GetText(), fld, pos)) != -1)
+ {
+ rFieldMarks.push_back(pos);
+ ++pos;
+ }
+}
+
+static uno::Reference<text::XTextRange>
+lcl_ExportFieldMark(
+ uno::Reference< text::XText > const & i_xParentText,
+ SwUnoCursor * const pUnoCursor,
+ const SwTextNode * const pTextNode )
+{
+ uno::Reference<text::XTextRange> xRef;
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ // maybe it's a good idea to add a special hint to the hints array and rely on the hint segmentation...
+ const sal_Int32 start = pUnoCursor->Start()->nContent.GetIndex();
+ OSL_ENSURE(pUnoCursor->End()->nContent.GetIndex() == start,
+ "hmm --- why is this different");
+
+ pUnoCursor->Right(1);
+ if ( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
+ {
+ OSL_FAIL("cannot move cursor?");
+ return nullptr;
+ }
+
+ const sal_Unicode Char = pTextNode->GetText()[start];
+ if (CH_TXT_ATR_FIELDSTART == Char)
+ {
+ ::sw::mark::IFieldmark* pFieldmark = nullptr;
+ pFieldmark = rDoc.getIDocumentMarkAccess()->
+ getFieldmarkAt(*pUnoCursor->GetMark());
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(
+ pUnoCursor, i_xParentText, PORTION_FIELD_START);
+ xRef = pPortion;
+ if (pFieldmark)
+ {
+ pPortion->SetBookmark(
+ SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
+ }
+ }
+ else if (CH_TXT_ATR_FIELDSEP == Char)
+ {
+ // TODO how to get the field?
+ xRef = new SwXTextPortion(
+ pUnoCursor, i_xParentText, PORTION_FIELD_SEP);
+ }
+ else if (CH_TXT_ATR_FIELDEND == Char)
+ {
+ ::sw::mark::IFieldmark* pFieldmark = nullptr;
+ pFieldmark = rDoc.getIDocumentMarkAccess()->
+ getFieldmarkAt(*pUnoCursor->GetMark());
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(
+ pUnoCursor, i_xParentText, PORTION_FIELD_END);
+ xRef = pPortion;
+ if (pFieldmark)
+ {
+ pPortion->SetBookmark(
+ SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
+ }
+ }
+ else if (CH_TXT_ATR_FORMELEMENT == Char)
+ {
+ ::sw::mark::IFieldmark* pFieldmark =
+ rDoc.getIDocumentMarkAccess()->getFieldmarkAt(*pUnoCursor->GetMark());
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(
+ pUnoCursor, i_xParentText, PORTION_FIELD_START_END);
+ xRef = pPortion;
+ if (pFieldmark)
+ {
+ pPortion->SetBookmark(
+ SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
+ }
+ }
+ else
+ {
+ OSL_FAIL("no fieldmark found?");
+ }
+ return xRef;
+}
+
+static Reference<XTextRange>
+lcl_CreateRefMarkPortion(
+ Reference<XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ const SwTextAttr & rAttr, const bool bEnd)
+{
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ SwFormatRefMark& rRefMark = const_cast<SwFormatRefMark&>(
+ static_cast<const SwFormatRefMark&>(rAttr.GetAttr()));
+ Reference<XTextContent> xContent;
+ if (!xContent.is())
+ {
+ xContent = SwXReferenceMark::CreateXReferenceMark(rDoc, &rRefMark);
+ }
+
+ rtl::Reference<SwXTextPortion> pPortion;
+ if (!bEnd)
+ {
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_REFMARK_START);
+ pPortion->SetRefMark(xContent);
+ pPortion->SetCollapsed(rAttr.End() == nullptr);
+ }
+ else
+ {
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_REFMARK_END);
+ pPortion->SetRefMark(xContent);
+ }
+ return pPortion;
+}
+
+static void
+lcl_InsertRubyPortion(
+ TextRangeList_t & rPortions,
+ Reference<XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ const SwTextAttr & rAttr, const bool bEnd)
+{
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(pUnoCursor,
+ static_txtattr_cast<const SwTextRuby&>(rAttr), xParent, bEnd);
+ rPortions.emplace_back(pPortion);
+ pPortion->SetCollapsed(rAttr.End() == nullptr);
+}
+
+static Reference<XTextRange>
+lcl_CreateTOXMarkPortion(
+ Reference<XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwTextAttr & rAttr, const bool bEnd)
+{
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ SwTOXMark & rTOXMark = static_cast<SwTOXMark&>(rAttr.GetAttr());
+
+ const Reference<XTextContent> xContent =
+ SwXDocumentIndexMark::CreateXDocumentIndexMark(rDoc, & rTOXMark);
+
+ rtl::Reference<SwXTextPortion> pPortion;
+ if (!bEnd)
+ {
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_TOXMARK_START);
+ pPortion->SetTOXMark(xContent);
+ pPortion->SetCollapsed(rAttr.GetEnd() == nullptr);
+ }
+ else
+ {
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_TOXMARK_END);
+ pPortion->SetTOXMark(xContent);
+ }
+ return pPortion;
+}
+
+static uno::Reference<text::XTextRange>
+lcl_CreateMetaPortion(
+ uno::Reference<text::XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwTextAttr & rAttr, std::unique_ptr<TextRangeList_t const> && pPortions)
+{
+ const uno::Reference<rdf::XMetadatable> xMeta( SwXMeta::CreateXMeta(
+ *static_cast<SwFormatMeta &>(rAttr.GetAttr()).GetMeta(),
+ xParent, std::move(pPortions)));
+ rtl::Reference<SwXTextPortion> pPortion;
+ if (RES_TXTATR_META == rAttr.Which())
+ {
+ const uno::Reference<text::XTextContent> xContent(xMeta,
+ uno::UNO_QUERY);
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_META);
+ pPortion->SetMeta(xContent);
+ }
+ else
+ {
+ const uno::Reference<text::XTextField> xField(xMeta, uno::UNO_QUERY);
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_FIELD);
+ pPortion->SetTextField(xField);
+ }
+ return pPortion;
+}
+
+/// Creates a text portion that has a non-empty ContentControl property.
+static uno::Reference<text::XTextRange>
+lcl_CreateContentControlPortion(const uno::Reference<text::XText>& xParent,
+ const SwUnoCursor* pUnoCursor, SwTextAttr& rAttr,
+ std::unique_ptr<const TextRangeList_t>&& pPortions)
+{
+ uno::Reference<text::XTextContent> xContentControl = SwXContentControl::CreateXContentControl(
+ *static_cast<SwFormatContentControl&>(rAttr.GetAttr()).GetContentControl(), xParent,
+ std::move(pPortions));
+ rtl::Reference<SwXTextPortion> pPortion;
+ pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_CONTENT_CONTROL);
+ pPortion->SetContentControl(xContentControl);
+ return pPortion;
+}
+
+/**
+ * Exports all bookmarks from rBkmArr into rPortions that have the same start
+ * or end position as nIndex.
+ *
+ * @param rBkmArr the array of bookmarks. If bOnlyFrameStarts is true, then
+ * this is only read, otherwise consumed entries are removed.
+ *
+ * @param rFramePositions the list of positions where there is an at-char /
+ * anchored frame.
+ *
+ * @param bOnlyFrameStarts If true: export only the start of the bookmarks
+ * which cover an at-char anchored frame. If false: export the end of the same
+ * bookmarks and everything else.
+ */
+static void lcl_ExportBookmark(
+ TextRangeList_t & rPortions,
+ Reference<XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwXBookmarkPortion_ImplList& rBkmArr,
+ const sal_Int32 nIndex,
+ const o3tl::sorted_vector<sal_Int32>& rFramePositions,
+ bool bOnlyFrameStarts)
+{
+ for ( SwXBookmarkPortion_ImplList::iterator aIter = rBkmArr.begin(), aEnd = rBkmArr.end(); aIter != aEnd; )
+ {
+ const SwXBookmarkPortion_ImplSharedPtr& pPtr = *aIter;
+ if ( nIndex > pPtr->getIndex() )
+ {
+ if (bOnlyFrameStarts)
+ ++aIter;
+ else
+ aIter = rBkmArr.erase(aIter);
+ continue;
+ }
+ if ( nIndex < pPtr->getIndex() )
+ break;
+
+ if ((BkmType::Start == pPtr->nBkmType && bOnlyFrameStarts) ||
+ (BkmType::StartEnd == pPtr->nBkmType))
+ {
+ bool bFrameStart = rFramePositions.find(nIndex) != rFramePositions.end();
+ bool bEnd = pPtr->nBkmType == BkmType::StartEnd && bFrameStart && !bOnlyFrameStarts;
+ if (pPtr->nBkmType == BkmType::Start || bFrameStart || !bOnlyFrameStarts)
+ {
+ // At this we create a text portion, due to one of these
+ // reasons:
+ // - this is the real start of a non-collapsed bookmark
+ // - this is the real position of a collapsed bookmark
+ // - this is the start or end (depending on bOnlyFrameStarts)
+ // of a collapsed bookmark at the same position as an at-char
+ // anchored frame
+ rtl::Reference<SwXTextPortion> pPortion =
+ new SwXTextPortion(pUnoCursor, xParent, bEnd ? PORTION_BOOKMARK_END : PORTION_BOOKMARK_START);
+ rPortions.emplace_back(pPortion);
+ pPortion->SetBookmark(pPtr->xBookmark);
+ pPortion->SetCollapsed( BkmType::StartEnd == pPtr->nBkmType && !bFrameStart );
+ }
+ }
+ else if (BkmType::End == pPtr->nBkmType && !bOnlyFrameStarts)
+ {
+ rtl::Reference<SwXTextPortion> pPortion =
+ new SwXTextPortion(pUnoCursor, xParent, PORTION_BOOKMARK_END);
+ rPortions.emplace_back(pPortion);
+ pPortion->SetBookmark(pPtr->xBookmark);
+ }
+
+ // next bookmark
+ if (bOnlyFrameStarts)
+ ++aIter;
+ else
+ aIter = rBkmArr.erase(aIter);
+ }
+}
+
+static void lcl_ExportSoftPageBreak(
+ TextRangeList_t & rPortions,
+ Reference<XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwSoftPageBreakList& rBreakArr,
+ const sal_Int32 nIndex)
+{
+ for ( auto aIter = rBreakArr.begin(); aIter != rBreakArr.end(); )
+ {
+ if ( nIndex > *aIter )
+ {
+ aIter = rBreakArr.erase(aIter);
+ continue;
+ }
+ if ( nIndex < *aIter )
+ break;
+
+ rPortions.push_back(
+ new SwXTextPortion(pUnoCursor, xParent, PORTION_SOFT_PAGEBREAK) );
+ aIter = rBreakArr.erase(aIter);
+ }
+}
+
+namespace {
+
+struct SwXRedlinePortion_Impl
+{
+ const SwRangeRedline* m_pRedline;
+ const bool m_bStart;
+
+ SwXRedlinePortion_Impl ( const SwRangeRedline* pRed, const bool bIsStart )
+ : m_pRedline(pRed)
+ , m_bStart(bIsStart)
+ {
+ }
+
+ sal_Int32 getRealIndex () const
+ {
+ return m_bStart ? m_pRedline->Start()->nContent.GetIndex()
+ : m_pRedline->End() ->nContent.GetIndex();
+ }
+};
+
+}
+
+typedef std::shared_ptr < SwXRedlinePortion_Impl >
+ SwXRedlinePortion_ImplSharedPtr;
+
+namespace {
+
+struct RedlineCompareStruct
+{
+ static const SwPosition& getPosition ( const SwXRedlinePortion_ImplSharedPtr &r )
+ {
+ return *(r->m_bStart ? r->m_pRedline->Start() : r->m_pRedline->End());
+ }
+
+ bool operator () ( const SwXRedlinePortion_ImplSharedPtr &r1,
+ const SwXRedlinePortion_ImplSharedPtr &r2 ) const
+ {
+ return getPosition ( r1 ) < getPosition ( r2 );
+ }
+};
+
+}
+
+typedef std::multiset < SwXRedlinePortion_ImplSharedPtr, RedlineCompareStruct >
+SwXRedlinePortion_ImplList;
+
+static Reference<XTextRange>
+lcl_ExportHints(
+ PortionStack_t & rPortionStack,
+ const Reference<XText> & xParent,
+ SwUnoCursor * const pUnoCursor,
+ SwpHints const * const pHints,
+ const sal_Int32 i_nStartPos,
+ const sal_Int32 i_nEndPos,
+ const sal_Int32 nCurrentIndex,
+ const bool bRightMoveForbidden,
+ bool & o_rbCursorMoved,
+ sal_Int32 & o_rNextAttrPosition)
+{
+ // if the attribute has a dummy character, then xRef is set (except META and CONTENT_CONTROL)
+ // otherwise, the portion for the attribute is inserted into rPortions!
+ Reference<XTextRange> xRef;
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+ //search for special text attributes - first some ends
+ size_t nEndIndex = 0;
+ sal_Int32 nNextEnd = 0;
+ const auto nHintsCount = pHints->Count();
+ while(nEndIndex < nHintsCount &&
+ (!pHints->GetSortedByEnd(nEndIndex)->GetEnd() ||
+ nCurrentIndex >= (nNextEnd = (*pHints->GetSortedByEnd(nEndIndex)->GetEnd()))))
+ {
+ if(pHints->GetSortedByEnd(nEndIndex)->GetEnd())
+ {
+ SwTextAttr * const pAttr = pHints->GetSortedByEnd(nEndIndex);
+ if (nNextEnd == nCurrentIndex)
+ {
+ const sal_uInt16 nWhich( pAttr->Which() );
+ switch (nWhich)
+ {
+ case RES_TXTATR_TOXMARK:
+ {
+ Reference<XTextRange> xTmp = lcl_CreateTOXMarkPortion(
+ xParent, pUnoCursor, *pAttr, true);
+ rPortionStack.top().first->push_back(xTmp);
+ }
+ break;
+ case RES_TXTATR_REFMARK:
+ {
+ Reference<XTextRange> xTmp = lcl_CreateRefMarkPortion(
+ xParent, pUnoCursor, *pAttr, true);
+ rPortionStack.top().first->push_back(xTmp);
+ }
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ //#i91534# GetEnd() == 0 mixes the order of ruby start/end
+ if( *pAttr->GetEnd() == pAttr->GetStart())
+ {
+ lcl_InsertRubyPortion( *rPortionStack.top().first,
+ xParent, pUnoCursor, *pAttr, false);
+ }
+ lcl_InsertRubyPortion( *rPortionStack.top().first,
+ xParent, pUnoCursor, *pAttr, true);
+ break;
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ {
+ OSL_ENSURE(pAttr->GetStart() != *pAttr->GetEnd(),
+ "empty meta?");
+ if ((i_nStartPos > 0) &&
+ (pAttr->GetStart() < i_nStartPos))
+ {
+ // force skip pAttr and rest of attribute ends
+ // at nCurrentIndex
+ // because they are not contained in the meta pAttr
+ // and the meta pAttr itself is outside selection!
+ // (necessary for SwXMeta::createEnumeration)
+ if (pAttr->GetStart() + 1 == i_nStartPos)
+ {
+ nEndIndex = pHints->Count() - 1;
+ }
+ break;
+ }
+ PortionList_t Top = rPortionStack.top();
+ if (Top.second != pAttr)
+ {
+ OSL_FAIL("ExportHints: stack error" );
+ }
+ else
+ {
+ std::unique_ptr<const TextRangeList_t>
+ pCurrentPortions(Top.first);
+ rPortionStack.pop();
+ const uno::Reference<text::XTextRange> xPortion(
+ lcl_CreateMetaPortion(xParent, pUnoCursor,
+ *pAttr, std::move(pCurrentPortions)));
+ rPortionStack.top().first->push_back(xPortion);
+ }
+ }
+ break;
+ case RES_TXTATR_CONTENTCONTROL:
+ {
+ if (pAttr->GetStart() == *pAttr->GetEnd())
+ {
+ SAL_WARN("sw.core", "lcl_ExportHints: empty content control");
+ }
+ if ((i_nStartPos > 0) && (pAttr->GetStart() < i_nStartPos))
+ {
+ // If the start pos is the start of the content of the content control,
+ // skip it: it'll be handled in SwXContentControl::createEnumeration().
+ if (pAttr->GetStart() + 1 == i_nStartPos)
+ {
+ nEndIndex = pHints->Count() - 1;
+ }
+ break;
+ }
+ PortionList_t Top = rPortionStack.top();
+ if (Top.second != pAttr)
+ {
+ SAL_WARN("sw.core", "lcl_ExportHints: content control is not at the "
+ "top of the portion stack");
+ }
+ else
+ {
+ std::unique_ptr<const TextRangeList_t> pCurrentPortions(Top.first);
+ rPortionStack.pop();
+ uno::Reference<text::XTextRange> xPortion(
+ lcl_CreateContentControlPortion(xParent, pUnoCursor, *pAttr,
+ std::move(pCurrentPortions)));
+ rPortionStack.top().first->push_back(xPortion);
+ }
+ }
+ break;
+ }
+ }
+ }
+ nEndIndex++;
+ }
+
+ // then some starts
+ size_t nStartIndex = 0;
+ sal_Int32 nNextStart = 0;
+ while(nStartIndex < nHintsCount &&
+ nCurrentIndex >= (nNextStart = pHints->Get(nStartIndex)->GetStart()))
+ {
+ SwTextAttr * const pAttr = pHints->Get(nStartIndex);
+ sal_uInt16 nAttrWhich = pAttr->Which();
+ if (nNextStart == nCurrentIndex)
+ {
+ switch( nAttrWhich )
+ {
+ case RES_TXTATR_FIELD:
+ if(!bRightMoveForbidden)
+ {
+ pUnoCursor->Right(1);
+ if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
+ break;
+ rtl::Reference<SwXTextPortion> pPortion =
+ new SwXTextPortion(
+ pUnoCursor, xParent, PORTION_FIELD);
+ xRef = pPortion;
+ Reference<XTextField> const xField =
+ SwXTextField::CreateXTextField(&rDoc,
+ &pAttr->GetFormatField());
+ pPortion->SetTextField(xField);
+ }
+ break;
+
+ case RES_TXTATR_ANNOTATION:
+ if(!bRightMoveForbidden)
+ {
+ pUnoCursor->Right(1);
+ if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
+ break;
+
+ const SwTextAnnotationField* pTextAnnotationField = dynamic_cast<const SwTextAnnotationField*>( pAttr );
+ ::sw::mark::IMark* pAnnotationMark = pTextAnnotationField ? pTextAnnotationField->GetAnnotationMark() : nullptr;
+ if ( pAnnotationMark != nullptr )
+ {
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION_END );
+ pPortion->SetBookmark(SwXBookmark::CreateXBookmark(
+ rDoc, pAnnotationMark));
+ xRef = pPortion;
+ }
+ else
+ {
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION );
+ Reference<XTextField> xField =
+ SwXTextField::CreateXTextField(&rDoc,
+ &pAttr->GetFormatField());
+ pPortion->SetTextField(xField);
+ xRef = pPortion;
+ }
+ }
+ break;
+
+ case RES_TXTATR_INPUTFIELD:
+ if(!bRightMoveForbidden)
+ {
+
+ pUnoCursor->Right(
+ pAttr->GetFormatField().GetField()->ExpandField(true, nullptr).getLength() + 2 );
+ if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
+ break;
+ rtl::Reference<SwXTextPortion> pPortion =
+ new SwXTextPortion( pUnoCursor, xParent, PORTION_FIELD);
+ xRef = pPortion;
+ Reference<XTextField> xField =
+ SwXTextField::CreateXTextField(&rDoc,
+ &pAttr->GetFormatField());
+ pPortion->SetTextField(xField);
+ }
+ break;
+
+ case RES_TXTATR_FLYCNT:
+ if(!bRightMoveForbidden)
+ {
+ pUnoCursor->Right(1);
+ if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
+ break; // Robust #i81708# content in covered cells
+
+ // Do not expose inline anchored textboxes.
+ if (SwTextBoxHelper::isTextBox(pAttr->GetFlyCnt().GetFrameFormat(), RES_FLYFRMFMT))
+ break;
+
+ pUnoCursor->Exchange();
+ xRef = new SwXTextPortion( pUnoCursor, xParent, PORTION_FRAME);
+ }
+ break;
+
+ case RES_TXTATR_FTN:
+ {
+ if(!bRightMoveForbidden)
+ {
+ pUnoCursor->Right(1);
+ if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
+ break;
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(
+ pUnoCursor, xParent, PORTION_FOOTNOTE);
+ xRef = pPortion;
+ Reference<XFootnote> xContent =
+ SwXFootnotes::GetObject(rDoc, pAttr->GetFootnote());
+ pPortion->SetFootnote(xContent);
+ }
+ }
+ break;
+
+ case RES_TXTATR_TOXMARK:
+ case RES_TXTATR_REFMARK:
+ {
+ bool bIsPoint = !(pAttr->GetEnd());
+ if (!bRightMoveForbidden || !bIsPoint)
+ {
+ if (bIsPoint)
+ {
+ pUnoCursor->Right(1);
+ }
+ Reference<XTextRange> xTmp =
+ (RES_TXTATR_REFMARK == nAttrWhich)
+ ? lcl_CreateRefMarkPortion(
+ xParent, pUnoCursor, *pAttr, false)
+ : lcl_CreateTOXMarkPortion(
+ xParent, pUnoCursor, *pAttr, false);
+ if (bIsPoint) // consume CH_TXTATR!
+ {
+ pUnoCursor->Normalize(false);
+ pUnoCursor->DeleteMark();
+ xRef = xTmp;
+ }
+ else // just insert it
+ {
+ rPortionStack.top().first->push_back(xTmp);
+ }
+ }
+ }
+ break;
+ case RES_TXTATR_CJK_RUBY:
+ //#i91534# GetEnd() == 0 mixes the order of ruby start/end
+ if(pAttr->GetEnd() && (*pAttr->GetEnd() != pAttr->GetStart()))
+ {
+ lcl_InsertRubyPortion( *rPortionStack.top().first,
+ xParent, pUnoCursor, *pAttr, false);
+ }
+ break;
+ case RES_TXTATR_META:
+ case RES_TXTATR_METAFIELD:
+ case RES_TXTATR_CONTENTCONTROL:
+ if (pAttr->GetStart() != *pAttr->GetEnd())
+ {
+ if (!bRightMoveForbidden)
+ {
+ pUnoCursor->Right(1);
+ o_rbCursorMoved = true;
+ // only if the end is included in selection!
+ if ((i_nEndPos < 0) ||
+ (*pAttr->GetEnd() <= i_nEndPos))
+ {
+ rPortionStack.push( std::make_pair(
+ new TextRangeList_t, pAttr ));
+ }
+ }
+ }
+ break;
+ case RES_TXTATR_LINEBREAK:
+ if (!bRightMoveForbidden)
+ {
+ pUnoCursor->Right(1);
+ if (*pUnoCursor->GetMark() == *pUnoCursor->GetPoint())
+ break;
+ rtl::Reference<SwXTextPortion> pPortion
+ = new SwXTextPortion(pUnoCursor, xParent, PORTION_LINEBREAK);
+ xRef = pPortion;
+ uno::Reference<text::XTextContent> xLineBreak
+ = SwXLineBreak::CreateXLineBreak(
+ &const_cast<SwFormatLineBreak&>(pAttr->GetLineBreak()));
+ pPortion->SetLineBreak(xLineBreak);
+ }
+ break;
+ case RES_TXTATR_AUTOFMT:
+ case RES_TXTATR_INETFMT:
+ case RES_TXTATR_CHARFMT:
+ break; // these are handled as properties of a "Text" portion
+ default:
+ OSL_FAIL("unknown attribute");
+ break;
+ }
+ }
+ nStartIndex++;
+ }
+
+ if (xRef.is()) // implies that we have moved the cursor
+ {
+ o_rbCursorMoved = true;
+ }
+ if (!o_rbCursorMoved)
+ {
+ // search for attribute changes behind the current cursor position
+ // break up at frames, bookmarks, redlines
+
+ nStartIndex = 0;
+ nNextStart = 0;
+ while(nStartIndex < pHints->Count() &&
+ nCurrentIndex >= (nNextStart = pHints->Get(nStartIndex)->GetStart()))
+ nStartIndex++;
+
+ nEndIndex = 0;
+ nNextEnd = 0;
+ while(nEndIndex < pHints->Count() &&
+ nCurrentIndex >= (nNextEnd = pHints->GetSortedByEnd(nEndIndex)->GetAnyEnd()))
+ nEndIndex++;
+
+ sal_Int32 nNextPos =
+ ((nNextStart > nCurrentIndex) && (nNextStart < nNextEnd))
+ ? nNextStart : nNextEnd;
+ if (nNextPos > nCurrentIndex)
+ {
+ o_rNextAttrPosition = nNextPos;
+ }
+ }
+ return xRef;
+}
+
+static void lcl_MoveCursor( SwUnoCursor * const pUnoCursor,
+ const sal_Int32 nCurrentIndex,
+ const sal_Int32 nNextFrameIndex,
+ const sal_Int32 nNextPortionIndex,
+ const sal_Int32 nNextAttrIndex,
+ const sal_Int32 nNextMarkIndex,
+ const sal_Int32 nEndPos )
+{
+ sal_Int32 nMovePos = pUnoCursor->GetContentNode()->Len();
+
+ if ((nEndPos >= 0) && (nEndPos < nMovePos))
+ {
+ nMovePos = nEndPos;
+ }
+
+ if ((nNextFrameIndex >= 0) && (nNextFrameIndex < nMovePos))
+ {
+ nMovePos = nNextFrameIndex;
+ }
+
+ if ((nNextPortionIndex >= 0) && (nNextPortionIndex < nMovePos))
+ {
+ nMovePos = nNextPortionIndex;
+ }
+
+ if ((nNextAttrIndex >= 0) && (nNextAttrIndex < nMovePos))
+ {
+ nMovePos = nNextAttrIndex;
+ }
+
+ if ((nNextMarkIndex >= 0) && (nNextMarkIndex < nMovePos))
+ {
+ nMovePos = nNextMarkIndex;
+ }
+
+ if (nMovePos > nCurrentIndex)
+ {
+ pUnoCursor->GetPoint()->nContent = nMovePos;
+ }
+}
+
+static void lcl_FillRedlineArray(
+ SwDoc const & rDoc,
+ SwUnoCursor const & rUnoCursor,
+ SwXRedlinePortion_ImplList& rRedArr )
+{
+ const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ const size_t nRedTableCount = rRedTable.size();
+
+ if ( nRedTableCount <= 0 )
+ return;
+
+ const SwPosition* pStart = rUnoCursor.GetPoint();
+ const SwNodeIndex nOwnNode = pStart->nNode;
+
+ SwRedlineTable::size_type nRed = rDoc.getIDocumentRedlineAccess().GetRedlinePos(nOwnNode.GetNode(), RedlineType::Any);
+ for(; nRed < nRedTableCount; ++nRed)
+ {
+ const SwRangeRedline* pRedline = rRedTable[nRed];
+ const SwPosition* pRedStart = pRedline->Start();
+ const SwNodeIndex nRedNode = pRedStart->nNode;
+ if ( nOwnNode == nRedNode )
+ rRedArr.insert( std::make_shared<SwXRedlinePortion_Impl>(
+ pRedline, true ) );
+ if( pRedline->HasMark() && pRedline->End()->nNode == nOwnNode )
+ rRedArr.insert( std::make_shared<SwXRedlinePortion_Impl>(
+ pRedline, false ) );
+ }
+}
+
+static void lcl_FillSoftPageBreakArray(
+ SwUnoCursor const & rUnoCursor,
+ SwSoftPageBreakList& rBreakArr )
+{
+ const SwTextNode *pTextNode =
+ rUnoCursor.GetPoint()->nNode.GetNode().GetTextNode();
+ if( pTextNode )
+ pTextNode->fillSoftPageBreakList( rBreakArr );
+}
+
+static void lcl_ExportRedline(
+ TextRangeList_t & rPortions,
+ Reference<XText> const& xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwXRedlinePortion_ImplList& rRedlineArr,
+ const sal_Int32 nIndex)
+{
+
+ // We want this loop to iterate over all red lines in this
+ // array. We will only insert the ones with index matches
+ for ( SwXRedlinePortion_ImplList::iterator aIter = rRedlineArr.begin(), aEnd = rRedlineArr.end();
+ aIter != aEnd; )
+ {
+ SwXRedlinePortion_ImplSharedPtr pPtr = *aIter;
+ sal_Int32 nRealIndex = pPtr->getRealIndex();
+ // If there are elements before nIndex, remove them
+ if ( nIndex > nRealIndex )
+ aIter = rRedlineArr.erase(aIter);
+ // If the elements match, and them to the list
+ else if ( nIndex == nRealIndex )
+ {
+ rPortions.push_back( new SwXRedlinePortion(
+ *pPtr->m_pRedline, pUnoCursor, xParent, pPtr->m_bStart));
+ aIter = rRedlineArr.erase(aIter);
+ }
+ // If we've iterated past nIndex, exit the loop
+ else
+ break;
+ }
+}
+
+static void lcl_ExportBkmAndRedline(
+ TextRangeList_t & rPortions,
+ Reference<XText> const & xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwXBookmarkPortion_ImplList& rBkmArr,
+ SwXRedlinePortion_ImplList& rRedlineArr,
+ SwSoftPageBreakList& rBreakArr,
+ const sal_Int32 nIndex,
+ const o3tl::sorted_vector<sal_Int32>& rFramePositions,
+ bool bOnlyFrameBookmarkStarts)
+{
+ if (!rBkmArr.empty())
+ lcl_ExportBookmark(rPortions, xParent, pUnoCursor, rBkmArr, nIndex, rFramePositions,
+ bOnlyFrameBookmarkStarts);
+
+ if (bOnlyFrameBookmarkStarts)
+ // Only exporting the start of some collapsed bookmarks: no export of
+ // other arrays.
+ return;
+
+ if (!rRedlineArr.empty())
+ lcl_ExportRedline(rPortions, xParent, pUnoCursor, rRedlineArr, nIndex);
+
+ if (!rBreakArr.empty())
+ lcl_ExportSoftPageBreak(rPortions, xParent, pUnoCursor, rBreakArr, nIndex);
+}
+
+/**
+ * Exports all start annotation marks from rAnnotationStartArr into rPortions that have the same
+ * start position as nIndex.
+ *
+ * @param rAnnotationStartArr the array of annotation marks. Consumed entries are removed.
+ *
+ * @param rFramePositions the list of positions where there is an at-char anchored frame.
+ *
+ * @param bOnlyFrame If true: export only the start of annotation marks which cover an at-char
+ * anchored frame. If false: export everything else.
+ */
+static void lcl_ExportAnnotationStarts(
+ TextRangeList_t & rPortions,
+ Reference<XText> const & xParent,
+ const SwUnoCursor * const pUnoCursor,
+ SwAnnotationStartPortion_ImplList& rAnnotationStartArr,
+ const sal_Int32 nIndex,
+ const o3tl::sorted_vector<sal_Int32>& rFramePositions,
+ bool bOnlyFrame)
+{
+ for ( SwAnnotationStartPortion_ImplList::iterator aIter = rAnnotationStartArr.begin(), aEnd = rAnnotationStartArr.end();
+ aIter != aEnd; )
+ {
+ SwAnnotationStartPortion_ImplSharedPtr pPtr = *aIter;
+ if ( nIndex > pPtr->getIndex() )
+ {
+ aIter = rAnnotationStartArr.erase(aIter);
+ continue;
+ }
+ if ( pPtr->getIndex() > nIndex )
+ {
+ break;
+ }
+
+ bool bFrameStart = rFramePositions.find(nIndex) != rFramePositions.end();
+ if (bFrameStart || !bOnlyFrame)
+ {
+ rtl::Reference<SwXTextPortion> pPortion =
+ new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION );
+ pPortion->SetTextField( pPtr->mxAnnotationField );
+ rPortions.emplace_back(pPortion);
+
+ aIter = rAnnotationStartArr.erase(aIter);
+ }
+ else
+ ++aIter;
+ }
+}
+
+/// Fills character positions from rFrames into rFramePositions.
+static void lcl_ExtractFramePositions(FrameClientSortList_t& rFrames, sal_Int32 nCurrentIndex,
+ o3tl::sorted_vector<sal_Int32>& rFramePositions)
+{
+ for (const auto& rFrame : rFrames)
+ {
+ if (rFrame.nIndex < nCurrentIndex)
+ continue;
+
+ if (rFrame.nIndex > nCurrentIndex)
+ break;
+
+ const auto pFrame = static_cast<const SwFrameFormat*>(rFrame.pFrameClient->GetRegisteredIn());
+ if (!pFrame)
+ continue;
+
+ auto& rFormat = *const_cast<SwFrameFormat*>(pFrame);
+ const SwFormatAnchor& rAnchor = rFormat.GetAnchor();
+ const SwPosition* pPosition = rAnchor.GetContentAnchor();
+ if (!pPosition)
+ continue;
+
+ rFramePositions.insert(pPosition->nContent.GetIndex());
+ }
+}
+
+/**
+ * Exports at-char anchored frames.
+ *
+ * @param i_rFrames the frames for this paragraph, frames at <= i_nCurrentIndex
+ * are removed from the container.
+ */
+static sal_Int32 lcl_ExportFrames(
+ TextRangeList_t & rPortions,
+ Reference<XText> const & i_xParent,
+ SwUnoCursor const * const i_pUnoCursor,
+ FrameClientSortList_t & i_rFrames,
+ sal_Int32 const i_nCurrentIndex)
+{
+ // Ignore frames which are not exported, as we are exporting a selection
+ // and they are anchored before the start of the selection.
+ while (!i_rFrames.empty() && i_rFrames.front().nIndex < i_nCurrentIndex)
+ i_rFrames.pop_front();
+
+ // find first Frame in (sorted) i_rFrames at current position
+ while (!i_rFrames.empty() && (i_rFrames.front().nIndex == i_nCurrentIndex))
+ // do not check for i_nEnd here; this is done implicitly by lcl_MoveCursor
+ {
+ auto pFrame = static_cast<SwFrameFormat*>(i_rFrames.front().pFrameClient->GetRegisteredIn());
+ if (pFrame) // Frame could be disposed
+ {
+ rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(i_pUnoCursor, i_xParent, *pFrame );
+ rPortions.emplace_back(pPortion);
+ }
+ i_rFrames.pop_front();
+ }
+
+ return !i_rFrames.empty() ? i_rFrames.front().nIndex : -1;
+}
+
+static sal_Int32 lcl_GetNextIndex(
+ SwXBookmarkPortion_ImplList const & rBkmArr,
+ SwXRedlinePortion_ImplList const & rRedlineArr,
+ SwSoftPageBreakList const & rBreakArr )
+{
+ sal_Int32 nRet = -1;
+ if(!rBkmArr.empty())
+ {
+ SwXBookmarkPortion_ImplSharedPtr pPtr = *rBkmArr.begin();
+ nRet = pPtr->getIndex();
+ }
+ if(!rRedlineArr.empty())
+ {
+ SwXRedlinePortion_ImplSharedPtr pPtr = *rRedlineArr.begin();
+ sal_Int32 nTmp = pPtr->getRealIndex();
+ if(nRet < 0 || nTmp < nRet)
+ nRet = nTmp;
+ }
+ if(!rBreakArr.empty())
+ {
+ if(nRet < 0 || *rBreakArr.begin() < nRet)
+ nRet = *rBreakArr.begin();
+ }
+ return nRet;
+};
+
+static void lcl_CreatePortions(
+ TextRangeList_t & i_rPortions,
+ uno::Reference< text::XText > const & i_xParentText,
+ SwUnoCursor * const pUnoCursor,
+ FrameClientSortList_t & i_rFrames,
+ const sal_Int32 i_nStartPos,
+ const sal_Int32 i_nEndPos )
+{
+ if (!pUnoCursor)
+ return;
+
+ // set the start if a selection should be exported
+ if ((i_nStartPos > 0) &&
+ (pUnoCursor->Start()->nContent.GetIndex() != i_nStartPos))
+ {
+ pUnoCursor->DeleteMark();
+ OSL_ENSURE(pUnoCursor->Start()->nNode.GetNode().GetTextNode() &&
+ (i_nStartPos <= pUnoCursor->Start()->nNode.GetNode().GetTextNode()->
+ GetText().getLength()), "Incorrect start position" );
+ // ??? should this be i_nStartPos - current position ?
+ pUnoCursor->Right(i_nStartPos);
+ }
+
+ SwDoc& rDoc = pUnoCursor->GetDoc();
+
+ std::deque<sal_Int32> FieldMarks;
+ lcl_FillFieldMarkArray(FieldMarks, *pUnoCursor, i_nStartPos);
+
+ SwXBookmarkPortion_ImplList Bookmarks;
+ lcl_FillBookmarkArray(rDoc, *pUnoCursor, Bookmarks);
+
+ SwXRedlinePortion_ImplList Redlines;
+ lcl_FillRedlineArray(rDoc, *pUnoCursor, Redlines);
+
+ SwSoftPageBreakList SoftPageBreaks;
+ lcl_FillSoftPageBreakArray(*pUnoCursor, SoftPageBreaks);
+
+ SwAnnotationStartPortion_ImplList AnnotationStarts;
+ lcl_FillAnnotationStartArray( rDoc, *pUnoCursor, AnnotationStarts );
+
+ PortionStack_t PortionStack;
+ PortionStack.push( PortionList_t(&i_rPortions, nullptr) );
+
+ bool bAtEnd( false );
+ while (!bAtEnd) // every iteration consumes at least current character!
+ {
+ if (pUnoCursor->HasMark())
+ {
+ pUnoCursor->Normalize(false);
+ pUnoCursor->DeleteMark();
+ }
+
+ SwTextNode * const pTextNode = pUnoCursor->GetNode().GetTextNode();
+ if (!pTextNode)
+ {
+ OSL_FAIL("lcl_CreatePortions: no TextNode - what now ?");
+ return;
+ }
+
+ SwpHints * const pHints = pTextNode->GetpSwpHints();
+ const sal_Int32 nCurrentIndex =
+ pUnoCursor->GetPoint()->nContent.GetIndex();
+ // this contains the portion which consumes the character in the
+ // text at nCurrentIndex; i.e. it must be set _once_ per iteration
+ uno::Reference< XTextRange > xRef;
+
+ SwUnoCursorHelper::SelectPam(*pUnoCursor, true); // set mark
+
+ // First remember the frame positions.
+ o3tl::sorted_vector<sal_Int32> aFramePositions;
+ lcl_ExtractFramePositions(i_rFrames, nCurrentIndex, aFramePositions);
+
+ // Then export start of collapsed bookmarks which "cover" at-char
+ // anchored frames.
+ lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
+ pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, /*bOnlyFrameBookmarkStarts=*/true );
+
+ lcl_ExportAnnotationStarts(
+ *PortionStack.top().first,
+ i_xParentText,
+ pUnoCursor,
+ AnnotationStarts,
+ nCurrentIndex,
+ aFramePositions,
+ /*bOnlyFrame=*/true );
+
+ const sal_Int32 nFirstFrameIndex =
+ lcl_ExportFrames( *PortionStack.top().first,
+ i_xParentText, pUnoCursor, i_rFrames, nCurrentIndex);
+
+ // Export ends of the previously started collapsed bookmarks + all
+ // other bookmarks, redlines, etc.
+ lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
+ pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, /*bOnlyFrameBookmarkStarts=*/false );
+
+ lcl_ExportAnnotationStarts(
+ *PortionStack.top().first,
+ i_xParentText,
+ pUnoCursor,
+ AnnotationStarts,
+ nCurrentIndex,
+ aFramePositions,
+ /*bOnlyFrame=*/false );
+
+ bool bCursorMoved( false );
+ sal_Int32 nNextAttrIndex = -1;
+ // #111716# the cursor must not move right at the
+ // end position of a selection!
+ bAtEnd = ((i_nEndPos >= 0) && (nCurrentIndex >= i_nEndPos))
+ || (nCurrentIndex >= pTextNode->Len());
+ if (pHints)
+ {
+ // N.B.: side-effects nNextAttrIndex, bCursorMoved; may move cursor
+ xRef = lcl_ExportHints(PortionStack, i_xParentText, pUnoCursor,
+ pHints, i_nStartPos, i_nEndPos, nCurrentIndex, bAtEnd,
+ bCursorMoved, nNextAttrIndex);
+ if (PortionStack.empty())
+ {
+ OSL_FAIL("CreatePortions: stack underflow");
+ return;
+ }
+ }
+
+ if (!xRef.is() && !bCursorMoved)
+ {
+ if (!bAtEnd &&
+ !FieldMarks.empty() && (FieldMarks.front() == nCurrentIndex))
+ {
+ // moves cursor
+ xRef = lcl_ExportFieldMark(i_xParentText, pUnoCursor, pTextNode);
+ FieldMarks.pop_front();
+ }
+ }
+ else
+ {
+ OSL_ENSURE(FieldMarks.empty() ||
+ (FieldMarks.front() != nCurrentIndex),
+ "fieldmark and hint with CH_TXTATR at same pos?");
+ }
+
+ if (!bAtEnd && !xRef.is() && !bCursorMoved)
+ {
+ const sal_Int32 nNextPortionIndex =
+ lcl_GetNextIndex(Bookmarks, Redlines, SoftPageBreaks);
+
+ sal_Int32 nNextMarkIndex = ( !FieldMarks.empty() ? FieldMarks.front() : -1 );
+ if ( !AnnotationStarts.empty()
+ && ( nNextMarkIndex == -1
+ || (*AnnotationStarts.begin())->getIndex() < nNextMarkIndex ) )
+ {
+ nNextMarkIndex = (*AnnotationStarts.begin())->getIndex();
+ }
+
+ lcl_MoveCursor(
+ pUnoCursor,
+ nCurrentIndex,
+ nFirstFrameIndex,
+ nNextPortionIndex,
+ nNextAttrIndex,
+ nNextMarkIndex,
+ i_nEndPos );
+
+ xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
+ }
+ else if (bAtEnd && !xRef.is() && !pTextNode->Len())
+ {
+ // special case: for an empty paragraph, we better put out a
+ // text portion because there may be a hyperlink attribute
+ xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
+ }
+ else if (bAtEnd && !xRef.is() && pTextNode->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT))
+ {
+ // We have explicit paragraph marker formatting, export it.
+ xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_LIST_AUTOFMT);
+ }
+ else if (bAtEnd && !xRef.is() && pHints)
+ {
+ // See if there is an empty autofmt at the paragraph end. If so, export it, since that
+ // affects the formatting of number portions.
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ const SwTextAttr* pHint = pHints->GetSortedByEnd(i);
+ if (pHint->GetStart() < pTextNode->Len())
+ {
+ break;
+ }
+ if (pHint->Which() == RES_TXTATR_AUTOFMT && pHint->GetEnd()
+ && pHint->GetStart() == *pHint->GetEnd()
+ && pHint->GetStart() == pTextNode->Len())
+ {
+ xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
+ break;
+ }
+ }
+ }
+
+ if (xRef.is())
+ {
+ PortionStack.top().first->push_back(xRef);
+ }
+ }
+
+ OSL_ENSURE((PortionStack.size() == 1) && !PortionStack.top().second,
+ "CreatePortions: stack error" );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoredline.cxx b/sw/source/core/unocore/unoredline.cxx
new file mode 100644
index 000000000..942243e0c
--- /dev/null
+++ b/sw/source/core/unocore/unoredline.cxx
@@ -0,0 +1,593 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <redline.hxx>
+#include <section.hxx>
+#include <unoprnms.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unoparagraph.hxx>
+#include <unocoll.hxx>
+#include <unomap.hxx>
+#include <unocrsr.hxx>
+#include <unoport.hxx>
+#include <unoredline.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+
+using namespace ::com::sun::star;
+
+SwXRedlineText::SwXRedlineText(SwDoc* _pDoc, const SwNodeIndex& aIndex) :
+ SwXText(_pDoc, CursorType::Redline),
+ m_aNodeIndex(aIndex)
+{
+}
+
+const SwStartNode* SwXRedlineText::GetStartNode() const
+{
+ return m_aNodeIndex.GetNode().GetStartNode();
+}
+
+uno::Any SwXRedlineText::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet;
+
+ if (cppu::UnoType<container::XEnumerationAccess>::get()== rType)
+ {
+ uno::Reference<container::XEnumerationAccess> aAccess = this;
+ aRet <<= aAccess;
+ }
+ else
+ {
+ // delegate to SwXText and OWeakObject
+ aRet = SwXText::queryInterface(rType);
+ if(!aRet.hasValue())
+ {
+ aRet = OWeakObject::queryInterface(rType);
+ }
+ }
+
+ return aRet;
+}
+
+uno::Sequence<uno::Type> SwXRedlineText::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ SwXText::getTypes()
+ ).getTypes();
+}
+
+uno::Sequence<sal_Int8> SwXRedlineText::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Reference<text::XTextCursor> SwXRedlineText::CreateCursor()
+{
+ return createTextCursor();
+}
+
+uno::Reference<text::XTextCursor> SwXRedlineText::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+
+ SwPosition aPos(m_aNodeIndex);
+ rtl::Reference<SwXTextCursor> pXCursor =
+ new SwXTextCursor(*GetDoc(), this, CursorType::Redline, aPos);
+ auto& rUnoCursor(pXCursor->GetCursor());
+ rUnoCursor.Move(fnMoveForward, GoInNode);
+
+ // #101929# prevent a newly created text cursor from running inside a table
+ // because table cells have their own XText.
+ // Patterned after SwXTextFrame::createTextCursor().
+
+ // skip all tables at the beginning
+ SwTableNode* pTableNode = rUnoCursor.GetNode().FindTableNode();
+ SwContentNode* pContentNode = nullptr;
+ bool bTable = pTableNode != nullptr;
+ while( pTableNode != nullptr )
+ {
+ rUnoCursor.GetPoint()->nNode = *(pTableNode->EndOfSectionNode());
+ pContentNode = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode);
+ pTableNode = pContentNode->FindTableNode();
+ }
+ if( pContentNode != nullptr )
+ rUnoCursor.GetPoint()->nContent.Assign( pContentNode, 0 );
+ if( bTable && rUnoCursor.GetNode().FindSttNodeByType( SwNormalStartNode )
+ != GetStartNode() )
+ {
+ // We have gone too far and have left our own redline. This means that
+ // no content node outside of a table could be found, and therefore we
+ // except.
+ uno::RuntimeException aExcept;
+ aExcept.Message =
+ "No content node found that is inside this change section "
+ "but outside of a table";
+ throw aExcept;
+ }
+
+ return static_cast<text::XWordCursor*>(pXCursor.get());
+}
+
+uno::Reference<text::XTextCursor> SwXRedlineText::createTextCursorByRange(
+ const uno::Reference<text::XTextRange> & aTextRange)
+{
+ uno::Reference<text::XTextCursor> xCursor = createTextCursor();
+ xCursor->gotoRange(aTextRange->getStart(), false);
+ xCursor->gotoRange(aTextRange->getEnd(), true);
+ return xCursor;
+}
+
+uno::Reference<container::XEnumeration> SwXRedlineText::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ SwPaM aPam(m_aNodeIndex);
+ aPam.Move(fnMoveForward, GoInNode);
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(*aPam.Start()));
+ return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Redline);
+}
+
+uno::Type SwXRedlineText::getElementType( )
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SwXRedlineText::hasElements( )
+{
+ return true; // we always have a content index
+}
+
+SwXRedlinePortion::SwXRedlinePortion(SwRangeRedline const& rRedline,
+ SwUnoCursor const*const pPortionCursor,
+ uno::Reference< text::XText > const& xParent, bool const bStart)
+ : SwXTextPortion(pPortionCursor, xParent,
+ bStart ? PORTION_REDLINE_START : PORTION_REDLINE_END)
+ , m_rRedline(rRedline)
+{
+ SetCollapsed(!m_rRedline.HasMark());
+}
+
+SwXRedlinePortion::~SwXRedlinePortion()
+{
+}
+
+static uno::Sequence<beans::PropertyValue> lcl_GetSuccessorProperties(const SwRangeRedline& rRedline)
+{
+ const SwRedlineData* pNext = rRedline.GetRedlineData().Next();
+ if(pNext)
+ {
+ return
+ {
+ // GetAuthorString(n) walks the SwRedlineData* chain;
+ // here we always need element 1
+ comphelper::makePropertyValue(UNO_NAME_REDLINE_AUTHOR, rRedline.GetAuthorString(1)),
+ comphelper::makePropertyValue(UNO_NAME_REDLINE_DATE_TIME, pNext->GetTimeStamp().GetUNODateTime()),
+ comphelper::makePropertyValue(UNO_NAME_REDLINE_COMMENT, pNext->GetComment()),
+ comphelper::makePropertyValue(UNO_NAME_REDLINE_TYPE, SwRedlineTypeToOUString(pNext->GetType()))
+ };
+ }
+ return uno::Sequence<beans::PropertyValue>(4);
+}
+
+uno::Any SwXRedlinePortion::getPropertyValue( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if (!Validate())
+ {
+ return uno::Any();
+ }
+ uno::Any aRet;
+ if(rPropertyName == UNO_NAME_REDLINE_TEXT)
+ {
+ SwNodeIndex* pNodeIdx = m_rRedline.GetContentIdx();
+ if(pNodeIdx )
+ {
+ if ( SwNodeOffset(1) < ( pNodeIdx->GetNode().EndOfSectionIndex() - pNodeIdx->GetNode().GetIndex() ) )
+ {
+ SwUnoCursor& rUnoCursor = GetCursor();
+ uno::Reference<text::XText> xRet = new SwXRedlineText(&rUnoCursor.GetDoc(), *pNodeIdx);
+ aRet <<= xRet;
+ }
+ else {
+ OSL_FAIL("Empty section in redline portion! (end node immediately follows start node)");
+ }
+ }
+ }
+ else
+ {
+ aRet = GetPropertyValue(rPropertyName, m_rRedline);
+ if(!aRet.hasValue() &&
+ rPropertyName != UNO_NAME_REDLINE_SUCCESSOR_DATA)
+ aRet = SwXTextPortion::getPropertyValue(rPropertyName);
+ }
+ return aRet;
+}
+
+bool SwXRedlinePortion::Validate()
+{
+ SwUnoCursor& rUnoCursor = GetCursor();
+ //search for the redline
+ SwDoc& rDoc = rUnoCursor.GetDoc();
+ const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+ bool bFound = false;
+ for(size_t nRed = 0; nRed < rRedTable.size() && !bFound; nRed++)
+ {
+ bFound = &m_rRedline == rRedTable[nRed];
+ }
+ return bFound;
+ // don't throw; the only caller can return void instead
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwXRedlinePortion::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Any SwXRedlinePortion::GetPropertyValue( std::u16string_view rPropertyName, const SwRangeRedline& rRedline )
+{
+ uno::Any aRet;
+ if(rPropertyName == u"" UNO_NAME_REDLINE_AUTHOR)
+ aRet <<= rRedline.GetAuthorString();
+ else if(rPropertyName == u"" UNO_NAME_REDLINE_DATE_TIME)
+ {
+ aRet <<= rRedline.GetTimeStamp().GetUNODateTime();
+ }
+ else if(rPropertyName == u"" UNO_NAME_REDLINE_COMMENT)
+ aRet <<= rRedline.GetComment();
+ else if(rPropertyName == u"" UNO_NAME_REDLINE_DESCRIPTION)
+ aRet <<= const_cast<SwRangeRedline&>(rRedline).GetDescr();
+ else if(rPropertyName == u"" UNO_NAME_REDLINE_TYPE)
+ {
+ aRet <<= SwRedlineTypeToOUString(rRedline.GetType());
+ }
+ else if(rPropertyName == u"" UNO_NAME_REDLINE_SUCCESSOR_DATA)
+ {
+ if(rRedline.GetRedlineData().Next())
+ aRet <<= lcl_GetSuccessorProperties(rRedline);
+ }
+ else if (rPropertyName == u"" UNO_NAME_REDLINE_IDENTIFIER)
+ {
+ aRet <<= OUString::number(
+ sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(&rRedline) ) );
+ }
+ else if (rPropertyName == u"" UNO_NAME_IS_IN_HEADER_FOOTER)
+ {
+ aRet <<= rRedline.GetDoc().IsInHeaderFooter( rRedline.GetPoint()->nNode );
+ }
+ else if (rPropertyName == u"" UNO_NAME_MERGE_LAST_PARA)
+ {
+ aRet <<= !rRedline.IsDelLastPara();
+ }
+ return aRet;
+}
+
+uno::Sequence< beans::PropertyValue > SwXRedlinePortion::CreateRedlineProperties(
+ const SwRangeRedline& rRedline, bool bIsStart )
+{
+ uno::Sequence< beans::PropertyValue > aRet(12);
+ const SwRedlineData* pNext = rRedline.GetRedlineData().Next();
+ beans::PropertyValue* pRet = aRet.getArray();
+
+ sal_Int32 nPropIdx = 0;
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_AUTHOR;
+ pRet[nPropIdx++].Value <<= rRedline.GetAuthorString();
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_DATE_TIME;
+ pRet[nPropIdx++].Value <<= rRedline.GetTimeStamp().GetUNODateTime();
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_COMMENT;
+ pRet[nPropIdx++].Value <<= rRedline.GetComment();
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_DESCRIPTION;
+ pRet[nPropIdx++].Value <<= const_cast<SwRangeRedline&>(rRedline).GetDescr();
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_TYPE;
+ pRet[nPropIdx++].Value <<= SwRedlineTypeToOUString(rRedline.GetType());
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_IDENTIFIER;
+ pRet[nPropIdx++].Value <<= OUString::number(
+ sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >(&rRedline) ) );
+ pRet[nPropIdx].Name = UNO_NAME_IS_COLLAPSED;
+ pRet[nPropIdx++].Value <<= !rRedline.HasMark();
+
+ pRet[nPropIdx].Name = UNO_NAME_IS_START;
+ pRet[nPropIdx++].Value <<= bIsStart;
+
+ pRet[nPropIdx].Name = UNO_NAME_MERGE_LAST_PARA;
+ pRet[nPropIdx++].Value <<= !rRedline.IsDelLastPara();
+
+ SwNodeIndex* pNodeIdx = rRedline.GetContentIdx();
+ if(pNodeIdx )
+ {
+ if ( SwNodeOffset(1) < ( pNodeIdx->GetNode().EndOfSectionIndex() - pNodeIdx->GetNode().GetIndex() ) )
+ {
+ uno::Reference<text::XText> xRet = new SwXRedlineText(&rRedline.GetDoc(), *pNodeIdx);
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_TEXT;
+ pRet[nPropIdx++].Value <<= xRet;
+ }
+ else {
+ OSL_FAIL("Empty section in redline portion! (end node immediately follows start node)");
+ }
+ }
+ if(pNext)
+ {
+ pRet[nPropIdx].Name = UNO_NAME_REDLINE_SUCCESSOR_DATA;
+ pRet[nPropIdx++].Value <<= lcl_GetSuccessorProperties(rRedline);
+ }
+ aRet.realloc(nPropIdx);
+ return aRet;
+}
+
+SwXRedline::SwXRedline(SwRangeRedline& rRedline, SwDoc& rDoc) :
+ SwXText(&rDoc, CursorType::Redline),
+ m_pDoc(&rDoc),
+ m_pRedline(&rRedline)
+{
+ StartListening(m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+}
+
+SwXRedline::~SwXRedline()
+{
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXRedline::getPropertySetInfo( )
+{
+ static uno::Reference< beans::XPropertySetInfo > xRef =
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_REDLINE)->getPropertySetInfo();
+ return xRef;
+}
+
+void SwXRedline::setPropertyValue( const OUString& rPropertyName, const uno::Any& aValue )
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(rPropertyName == UNO_NAME_REDLINE_AUTHOR)
+ {
+ OSL_FAIL("currently not available");
+ }
+ else if(rPropertyName == UNO_NAME_REDLINE_DATE_TIME)
+ {
+ OSL_FAIL("currently not available");
+ }
+ else if(rPropertyName == UNO_NAME_REDLINE_COMMENT)
+ {
+ OUString sTmp; aValue >>= sTmp;
+ m_pRedline->SetComment(sTmp);
+ }
+ else if(rPropertyName == UNO_NAME_REDLINE_DESCRIPTION)
+ {
+ SAL_WARN("sw.uno", "SwXRedline::setPropertyValue: can't set Description");
+ }
+ else if(rPropertyName == UNO_NAME_REDLINE_TYPE)
+ {
+ OSL_FAIL("currently not available");
+ OUString sTmp; aValue >>= sTmp;
+ if(sTmp.isEmpty())
+ throw lang::IllegalArgumentException();
+ }
+ else if(rPropertyName == UNO_NAME_REDLINE_SUCCESSOR_DATA)
+ {
+ OSL_FAIL("currently not available");
+ }
+ else
+ {
+ throw lang::IllegalArgumentException();
+ }
+}
+
+uno::Any SwXRedline::getPropertyValue( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ uno::Any aRet;
+ bool bStart = rPropertyName == UNO_NAME_REDLINE_START;
+ if(bStart ||
+ rPropertyName == UNO_NAME_REDLINE_END)
+ {
+ uno::Reference<XInterface> xRet;
+ SwNode* pNode = &m_pRedline->GetNode();
+ if(!bStart && m_pRedline->HasMark())
+ pNode = &m_pRedline->GetNode(false);
+ switch(pNode->GetNodeType())
+ {
+ case SwNodeType::Section:
+ {
+ SwSectionNode* pSectNode = pNode->GetSectionNode();
+ OSL_ENSURE(pSectNode, "No section node!");
+ xRet = SwXTextSections::GetObject( *pSectNode->GetSection().GetFormat() );
+ }
+ break;
+ case SwNodeType::Table :
+ {
+ SwTableNode* pTableNode = pNode->GetTableNode();
+ OSL_ENSURE(pTableNode, "No table node!");
+ SwTable& rTable = pTableNode->GetTable();
+ SwFrameFormat* pTableFormat = rTable.GetFrameFormat();
+ xRet = SwXTextTables::GetObject( *pTableFormat );
+ }
+ break;
+ case SwNodeType::Text :
+ {
+ SwPosition* pPoint = nullptr;
+ if(bStart || !m_pRedline->HasMark())
+ pPoint = m_pRedline->GetPoint();
+ else
+ pPoint = m_pRedline->GetMark();
+ const uno::Reference<text::XTextRange> xRange =
+ SwXTextRange::CreateXTextRange(*m_pDoc, *pPoint, nullptr);
+ xRet = xRange.get();
+ }
+ break;
+ default:
+ OSL_FAIL("illegal node type");
+ }
+ aRet <<= xRet;
+ }
+ else if(rPropertyName == UNO_NAME_REDLINE_TEXT)
+ {
+ SwNodeIndex* pNodeIdx = m_pRedline->GetContentIdx();
+ if( pNodeIdx )
+ {
+ if ( SwNodeOffset(1) < ( pNodeIdx->GetNode().EndOfSectionIndex() - pNodeIdx->GetNode().GetIndex() ) )
+ {
+ uno::Reference<text::XText> xRet = new SwXRedlineText(m_pDoc, *pNodeIdx);
+ aRet <<= xRet;
+ }
+ else {
+ OSL_FAIL("Empty section in redline portion! (end node immediately follows start node)");
+ }
+ }
+ }
+ else
+ aRet = SwXRedlinePortion::GetPropertyValue(rPropertyName, *m_pRedline);
+ return aRet;
+}
+
+void SwXRedline::addPropertyChangeListener(
+ const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXRedline::removePropertyChangeListener(
+ const OUString& /*aPropertyName*/, const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+}
+
+void SwXRedline::addVetoableChangeListener(
+ const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+void SwXRedline::removeVetoableChangeListener(
+ const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+void SwXRedline::Notify( const SfxHint& rHint )
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pDoc = nullptr;
+ m_pRedline = nullptr;
+ } else if(auto pHint = dynamic_cast<const sw::FindRedlineHint*>(&rHint)) {
+ if(!*pHint->m_ppXRedline && &pHint->m_rRedline == GetRedline())
+ *pHint->m_ppXRedline = this;
+ }
+}
+
+uno::Reference< container::XEnumeration > SwXRedline::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+
+ SwNodeIndex* pNodeIndex = m_pRedline->GetContentIdx();
+ if(!pNodeIndex)
+ return nullptr;
+ SwPaM aPam(*pNodeIndex);
+ aPam.Move(fnMoveForward, GoInNode);
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(*aPam.Start()));
+ return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Redline);
+}
+
+uno::Type SwXRedline::getElementType( )
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SwXRedline::hasElements( )
+{
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ return nullptr != m_pRedline->GetContentIdx();
+}
+
+uno::Reference< text::XTextCursor > SwXRedline::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+
+ SwNodeIndex* pNodeIndex = m_pRedline->GetContentIdx();
+ if(!pNodeIndex)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwPosition aPos(*pNodeIndex);
+ rtl::Reference<SwXTextCursor> pXCursor =
+ new SwXTextCursor(*m_pDoc, this, CursorType::Redline, aPos);
+ auto& rUnoCursor(pXCursor->GetCursor());
+ rUnoCursor.Move(fnMoveForward, GoInNode);
+
+ // is here a table?
+ SwTableNode* pTableNode = rUnoCursor.GetNode().FindTableNode();
+ SwContentNode* pCont = nullptr;
+ while( pTableNode )
+ {
+ rUnoCursor.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
+ pCont = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode);
+ pTableNode = pCont->FindTableNode();
+ }
+ if(pCont)
+ rUnoCursor.GetPoint()->nContent.Assign(pCont, 0);
+
+ return static_cast<text::XWordCursor*>(pXCursor.get());
+}
+
+uno::Reference< text::XTextCursor > SwXRedline::createTextCursorByRange(
+ const uno::Reference< text::XTextRange > & /*aTextPosition*/)
+{
+ throw uno::RuntimeException();
+}
+
+uno::Any SwXRedline::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = SwXText::queryInterface(rType);
+ if(!aRet.hasValue())
+ {
+ aRet = SwXRedlineBaseClass::queryInterface(rType);
+ }
+ return aRet;
+}
+
+uno::Sequence<uno::Type> SwXRedline::getTypes()
+{
+ return comphelper::concatSequences(
+ SwXText::getTypes(),
+ SwXRedlineBaseClass::getTypes()
+ );
+}
+
+uno::Sequence<sal_Int8> SwXRedline::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unoredlines.cxx b/sw/source/core/unocore/unoredlines.cxx
new file mode 100644
index 000000000..572bb7756
--- /dev/null
+++ b/sw/source/core/unocore/unoredlines.cxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/svapp.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <unoredlines.hxx>
+#include <unoredline.hxx>
+#include <pagedesc.hxx>
+#include <poolfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+#include <redline.hxx>
+
+using namespace ::com::sun::star;
+
+SwXRedlines::SwXRedlines(SwDoc* _pDoc) :
+ SwUnoCollection(_pDoc)
+{
+}
+
+SwXRedlines::~SwXRedlines()
+{
+}
+
+sal_Int32 SwXRedlines::getCount( )
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const SwRedlineTable& rRedTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ return rRedTable.size();
+}
+
+uno::Any SwXRedlines::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const SwRedlineTable& rRedTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ if ((nIndex < 0) || (rRedTable.size() <= o3tl::make_unsigned(nIndex)))
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Reference <beans::XPropertySet> xRet = SwXRedlines::GetObject( *rRedTable[nIndex], *GetDoc() );
+ return uno::Any(xRet);
+}
+
+uno::Reference< container::XEnumeration > SwXRedlines::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ return uno::Reference< container::XEnumeration >(new SwXRedlineEnumeration(*GetDoc()));
+}
+
+uno::Type SwXRedlines::getElementType( )
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+}
+
+sal_Bool SwXRedlines::hasElements( )
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ const SwRedlineTable& rRedTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ return !rRedTable.empty();
+}
+
+OUString SwXRedlines::getImplementationName()
+{
+ return "SwXRedlines";
+}
+
+sal_Bool SwXRedlines::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SwXRedlines::getSupportedServiceNames()
+{
+ OSL_FAIL("not implemented");
+ return uno::Sequence< OUString >();
+}
+
+beans::XPropertySet* SwXRedlines::GetObject( SwRangeRedline& rRedline, SwDoc& rDoc )
+{
+ SwXRedline* pXRedline(nullptr);
+ sw::FindRedlineHint aHint(rRedline, &pXRedline);
+ rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier().Broadcast(aHint);
+ return pXRedline ? pXRedline : new SwXRedline(rRedline, rDoc);
+}
+
+SwXRedlineEnumeration::SwXRedlineEnumeration(SwDoc& rDoc) :
+ m_pDoc(&rDoc),
+ m_nCurrentIndex(0)
+{
+ StartListening(m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+}
+
+SwXRedlineEnumeration::~SwXRedlineEnumeration()
+{
+}
+
+sal_Bool SwXRedlineEnumeration::hasMoreElements()
+{
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ return m_pDoc->getIDocumentRedlineAccess().GetRedlineTable().size() > m_nCurrentIndex;
+}
+
+uno::Any SwXRedlineEnumeration::nextElement()
+{
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ const SwRedlineTable& rRedTable = m_pDoc->getIDocumentRedlineAccess().GetRedlineTable();
+ if( rRedTable.size() <= m_nCurrentIndex )
+ throw container::NoSuchElementException();
+ uno::Reference <beans::XPropertySet> xRet = SwXRedlines::GetObject( *rRedTable[m_nCurrentIndex++], *m_pDoc );
+ uno::Any aRet;
+ aRet <<= xRet;
+ return aRet;
+}
+
+OUString SwXRedlineEnumeration::getImplementationName()
+{
+ return "SwXRedlineEnumeration";
+}
+
+sal_Bool SwXRedlineEnumeration::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SwXRedlineEnumeration::getSupportedServiceNames()
+{
+ return uno::Sequence< OUString >();
+}
+
+void SwXRedlineEnumeration::Notify( const SfxHint& rHint )
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pDoc = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unorefmk.cxx b/sw/source/core/unocore/unorefmk.cxx
new file mode 100644
index 000000000..1b1b8bdb0
--- /dev/null
+++ b/sw/source/core/unocore/unorefmk.cxx
@@ -0,0 +1,1528 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <utility>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <sal/config.h>
+#include <svl/listener.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+#include <unotextrange.hxx>
+#include <unorefmark.hxx>
+#include <unotextcursor.hxx>
+#include <unomap.hxx>
+#include <unocrsrhelper.hxx>
+#include <doc.hxx>
+#include <ndtxt.hxx>
+#include <fmtrfmrk.hxx>
+#include <txtrfmrk.hxx>
+#include <unometa.hxx>
+#include <unotext.hxx>
+#include <unoport.hxx>
+#include <txtatr.hxx>
+#include <fmtmeta.hxx>
+#include <docsh.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/rdf/Statement.hpp>
+#include <com/sun/star/rdf/URI.hpp>
+#include <com/sun/star/rdf/URIs.hpp>
+#include <com/sun/star/rdf/XLiteral.hpp>
+#include <com/sun/star/rdf/XRepositorySupplier.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+
+using namespace ::com::sun::star;
+
+class SwXReferenceMark::Impl
+ : public SvtListener
+{
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ bool m_bIsDescriptor;
+ SwDoc* m_pDoc;
+ const SwFormatRefMark* m_pMarkFormat;
+ OUString m_sMarkName;
+
+ Impl(SwDoc* const pDoc, SwFormatRefMark* const pRefMark)
+ : m_bIsDescriptor(nullptr == pRefMark)
+ , m_pDoc(pDoc)
+ , m_pMarkFormat(pRefMark)
+ {
+ if (pRefMark)
+ {
+ StartListening(pRefMark->GetNotifier());
+ m_sMarkName = pRefMark->GetRefName();
+ }
+ }
+
+ bool IsValid() const { return m_pMarkFormat; }
+ void InsertRefMark( SwPaM & rPam, SwXTextCursor const*const pCursor );
+ void Invalidate();
+protected:
+ virtual void Notify(const SfxHint&) override;
+
+};
+
+void SwXReferenceMark::Impl::Invalidate()
+{
+ EndListeningAll();
+ m_pDoc = nullptr;
+ m_pMarkFormat = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+}
+
+void SwXReferenceMark::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ Invalidate();
+}
+
+SwXReferenceMark::SwXReferenceMark(
+ SwDoc *const pDoc, SwFormatRefMark *const pRefMark)
+ : m_pImpl( new SwXReferenceMark::Impl(pDoc, pRefMark) )
+{
+}
+
+SwXReferenceMark::~SwXReferenceMark()
+{
+}
+
+uno::Reference<text::XTextContent>
+SwXReferenceMark::CreateXReferenceMark(
+ SwDoc & rDoc, SwFormatRefMark *const pMarkFormat)
+{
+ // i#105557: do not iterate over the registered clients: race condition
+ uno::Reference<text::XTextContent> xMark;
+ if (pMarkFormat)
+ {
+ xMark = pMarkFormat->GetXRefMark();
+ }
+ if (!xMark.is())
+ {
+ rtl::Reference<SwXReferenceMark> pMark(new SwXReferenceMark(&rDoc, pMarkFormat));
+ xMark = pMark;
+ if (pMarkFormat)
+ {
+ pMarkFormat->SetXRefMark(xMark);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pMark->m_pImpl->m_wThis = xMark;
+ }
+ return xMark;
+}
+
+const uno::Sequence< sal_Int8 > & SwXReferenceMark::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXReferenceMarkUnoTunnelId;
+ return theSwXReferenceMarkUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXReferenceMark::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXReferenceMark>(rId, this);
+}
+
+OUString SAL_CALL SwXReferenceMark::getImplementationName()
+{
+ return "SwXReferenceMark";
+}
+
+sal_Bool SAL_CALL
+SwXReferenceMark::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXReferenceMark::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.ReferenceMark"
+ };
+}
+
+namespace {
+
+template<typename T> struct NotContainedIn
+{
+ std::vector<T> const& m_rVector;
+ explicit NotContainedIn(std::vector<T> const& rVector)
+ : m_rVector(rVector) { }
+ bool operator() (T const& rT) {
+ return std::find(m_rVector.begin(), m_rVector.end(), rT)
+ == m_rVector.end();
+ }
+};
+
+}
+
+void SwXReferenceMark::Impl::InsertRefMark(SwPaM& rPam,
+ SwXTextCursor const*const pCursor)
+{
+ //! in some cases when this function is called the pDoc pointer member may have become
+ //! invalid/deleted thus we obtain the document pointer from rPaM where it should always
+ //! be valid.
+ SwDoc& rDoc2 = rPam.GetDoc();
+
+ UnoActionContext aCont(&rDoc2);
+ SwFormatRefMark aRefMark(m_sMarkName);
+ bool bMark = *rPam.GetPoint() != *rPam.GetMark();
+
+ const bool bForceExpandHints( !bMark && pCursor && pCursor->IsAtEndOfMeta() );
+ const SetAttrMode nInsertFlags = bForceExpandHints
+ ? ( SetAttrMode::FORCEHINTEXPAND
+ | SetAttrMode::DONTEXPAND)
+ : SetAttrMode::DONTEXPAND;
+
+ std::vector<SwTextAttr *> oldMarks;
+ if (bMark)
+ {
+ oldMarks = rPam.GetNode().GetTextNode()->GetTextAttrsAt(
+ rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK);
+ }
+
+ rDoc2.getIDocumentContentOperations().InsertPoolItem( rPam, aRefMark, nInsertFlags );
+
+ if( bMark && *rPam.GetPoint() > *rPam.GetMark())
+ {
+ rPam.Exchange();
+ }
+
+ // aRefMark was copied into the document pool; now retrieve real format...
+ SwTextAttr * pTextAttr(nullptr);
+ if (bMark)
+ {
+ // #i107672#
+ // ensure that we do not retrieve a different mark at the same position
+ std::vector<SwTextAttr *> const newMarks(
+ rPam.GetNode().GetTextNode()->GetTextAttrsAt(
+ rPam.GetPoint()->nContent.GetIndex(), RES_TXTATR_REFMARK));
+ std::vector<SwTextAttr *>::const_iterator const iter(
+ std::find_if(newMarks.begin(), newMarks.end(),
+ NotContainedIn<SwTextAttr *>(oldMarks)));
+ assert(newMarks.end() != iter);
+ if (newMarks.end() != iter)
+ {
+ pTextAttr = *iter;
+ }
+ }
+ else
+ {
+ SwTextNode *pTextNd = rPam.GetNode().GetTextNode();
+ assert(pTextNd);
+ pTextAttr = pTextNd ? rPam.GetNode().GetTextNode()->GetTextAttrForCharAt(
+ rPam.GetPoint()->nContent.GetIndex() - 1, RES_TXTATR_REFMARK) : nullptr;
+ }
+
+ if (!pTextAttr)
+ {
+ throw uno::RuntimeException(
+ "SwXReferenceMark::InsertRefMark(): cannot insert attribute", nullptr);
+ }
+
+ m_pMarkFormat = &pTextAttr->GetRefMark();
+ EndListeningAll();
+ StartListening(const_cast<SwFormatRefMark*>(m_pMarkFormat)->GetNotifier());
+}
+
+void SAL_CALL
+SwXReferenceMark::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard aGuard;
+
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+ SwDoc *const pDocument =
+ pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
+ if (!pDocument)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwUnoInternalPaM aPam(*pDocument);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+ m_pImpl->InsertRefMark(aPam, dynamic_cast<SwXTextCursor*>(pCursor));
+ m_pImpl->m_bIsDescriptor = false;
+ m_pImpl->m_pDoc = pDocument;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXReferenceMark::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pImpl->IsValid())
+ {
+ SwFormatRefMark const*const pNewMark =
+ m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
+ if (pNewMark && (pNewMark == m_pImpl->m_pMarkFormat))
+ {
+ SwTextRefMark const*const pTextMark =
+ m_pImpl->m_pMarkFormat->GetTextRefMark();
+ if (pTextMark &&
+ (&pTextMark->GetTextNode().GetNodes() ==
+ &m_pImpl->m_pDoc->GetNodes()))
+ {
+ SwTextNode const& rTextNode = pTextMark->GetTextNode();
+ std::optional<SwPaM> pPam;
+ if ( pTextMark->End() )
+ pPam.emplace( rTextNode, *pTextMark->End(),
+ rTextNode, pTextMark->GetStart());
+ else
+ pPam.emplace( rTextNode, pTextMark->GetStart());
+
+ return SwXTextRange::CreateXTextRange(
+ *m_pImpl->m_pDoc, *pPam->Start(), pPam->End());
+ }
+ }
+ }
+ return nullptr;
+}
+
+void SAL_CALL SwXReferenceMark::dispose()
+{
+ SolarMutexGuard aGuard;
+ if (m_pImpl->IsValid())
+ {
+ SwFormatRefMark const*const pNewMark =
+ m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
+ if (pNewMark && (pNewMark == m_pImpl->m_pMarkFormat))
+ {
+ SwTextRefMark const*const pTextMark =
+ m_pImpl->m_pMarkFormat->GetTextRefMark();
+ if (pTextMark &&
+ (&pTextMark->GetTextNode().GetNodes() ==
+ &m_pImpl->m_pDoc->GetNodes()))
+ {
+ SwTextNode const& rTextNode = pTextMark->GetTextNode();
+ const sal_Int32 nStt = pTextMark->GetStart();
+ const sal_Int32 nEnd = pTextMark->End()
+ ? *pTextMark->End()
+ : nStt + 1;
+
+ SwPaM aPam( rTextNode, nStt, rTextNode, nEnd );
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DeleteAndJoin( aPam );
+ }
+ }
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->Invalidate();
+ }
+}
+
+void SAL_CALL SwXReferenceMark::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXReferenceMark::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+OUString SAL_CALL SwXReferenceMark::getName()
+{
+ SolarMutexGuard aGuard;
+ if (!m_pImpl->IsValid() ||
+ !m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName))
+ {
+ throw uno::RuntimeException();
+ }
+ return m_pImpl->m_sMarkName;
+}
+
+void SAL_CALL SwXReferenceMark::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_sMarkName = rName;
+ }
+ else
+ {
+ if (!m_pImpl->IsValid()
+ || !m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName)
+ || m_pImpl->m_pDoc->GetRefMark(rName))
+ {
+ throw uno::RuntimeException();
+ }
+ SwFormatRefMark const*const pCurMark =
+ m_pImpl->m_pDoc->GetRefMark(m_pImpl->m_sMarkName);
+ if ((rName != m_pImpl->m_sMarkName)
+ && pCurMark && (pCurMark == m_pImpl->m_pMarkFormat))
+ {
+ const UnoActionContext aCont(m_pImpl->m_pDoc);
+ SwTextRefMark const*const pTextMark =
+ m_pImpl->m_pMarkFormat->GetTextRefMark();
+ if (pTextMark &&
+ (&pTextMark->GetTextNode().GetNodes() ==
+ &m_pImpl->m_pDoc->GetNodes()))
+ {
+ SwTextNode const& rTextNode = pTextMark->GetTextNode();
+ const sal_Int32 nStt = pTextMark->GetStart();
+ const sal_Int32 nEnd = pTextMark->End()
+ ? *pTextMark->End()
+ : nStt + 1;
+
+ SwPaM aPam( rTextNode, nStt, rTextNode, nEnd );
+ // deletes the m_pImpl->m_pDoc member in the SwXReferenceMark!
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DeleteAndJoin( aPam );
+ // The aPam will keep the correct and functional doc though
+
+ m_pImpl->m_sMarkName = rName;
+ //create a new one
+ m_pImpl->InsertRefMark( aPam, nullptr );
+ m_pImpl->m_pDoc = &aPam.GetDoc();
+ }
+ }
+ }
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXReferenceMark::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef =
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH_EXTENSIONS)
+ ->getPropertySetInfo();
+ return xRef;
+}
+
+void SAL_CALL SwXReferenceMark::setPropertyValue(
+ const OUString& /*rPropertyName*/, const uno::Any& /*rValue*/ )
+{
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL
+SwXReferenceMark::getPropertyValue(const OUString& rPropertyName)
+{
+ // does not seem to need SolarMutex
+ uno::Any aRet;
+ if (! ::sw::GetDefaultTextContentValue(aRet, rPropertyName))
+ {
+ throw beans::UnknownPropertyException(rPropertyName);
+ }
+ return aRet;
+}
+
+void SAL_CALL SwXReferenceMark::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXReferenceMark::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXReferenceMark::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXReferenceMark::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXReferenceMark::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXReferenceMark::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXReferenceMark::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXReferenceMark::removeVetoableChangeListener(): not implemented");
+}
+
+namespace {
+
+class SwXMetaText : public cppu::OWeakObject, public SwXText
+{
+private:
+ SwXMeta & m_rMeta;
+
+ virtual void PrepareForAttach(uno::Reference< text::XTextRange > & xRange,
+ const SwPaM & rPam) override;
+
+ virtual bool CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb) override;
+
+protected:
+ virtual const SwStartNode *GetStartNode() const override;
+ virtual uno::Reference< text::XTextCursor >
+ CreateCursor() override;
+
+public:
+ SwXMetaText(SwDoc & rDoc, SwXMeta & rMeta);
+
+ /// make available for SwXMeta
+ using SwXText::Invalidate;
+
+ // XInterface
+ virtual void SAL_CALL acquire() noexcept override { cppu::OWeakObject::acquire(); }
+ virtual void SAL_CALL release() noexcept override { cppu::OWeakObject::release(); }
+
+ // XTypeProvider
+ virtual uno::Sequence< sal_Int8 > SAL_CALL
+ getImplementationId() override;
+
+ // XText
+ virtual uno::Reference< text::XTextCursor > SAL_CALL
+ createTextCursor() override;
+ virtual uno::Reference< text::XTextCursor > SAL_CALL
+ createTextCursorByRange(
+ const uno::Reference< text::XTextRange > & xTextPosition) override;
+
+};
+
+}
+
+SwXMetaText::SwXMetaText(SwDoc & rDoc, SwXMeta & rMeta)
+ : SwXText(&rDoc, CursorType::Meta)
+ , m_rMeta(rMeta)
+{
+}
+
+const SwStartNode *SwXMetaText::GetStartNode() const
+{
+ SwXText const * const pParent(
+ dynamic_cast<SwXText*>(m_rMeta.GetParentText().get()));
+ return pParent ? pParent->GetStartNode() : nullptr;
+}
+
+void SwXMetaText::PrepareForAttach( uno::Reference<text::XTextRange> & xRange,
+ const SwPaM & rPam)
+{
+ // create a new cursor to prevent modifying SwXTextRange
+ xRange = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), &m_rMeta, CursorType::Meta, *rPam.GetPoint(),
+ (rPam.HasMark()) ? rPam.GetMark() : nullptr));
+}
+
+bool SwXMetaText::CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
+{
+ return m_rMeta.CheckForOwnMemberMeta(rPam, bAbsorb);
+}
+
+uno::Reference< text::XTextCursor > SwXMetaText::CreateCursor()
+{
+ uno::Reference< text::XTextCursor > xRet;
+ if (IsValid())
+ {
+ SwTextNode * pTextNode;
+ sal_Int32 nMetaStart;
+ sal_Int32 nMetaEnd;
+ const bool bSuccess(
+ m_rMeta.SetContentRange(pTextNode, nMetaStart, nMetaEnd) );
+ if (bSuccess)
+ {
+ SwPosition aPos(*pTextNode, nMetaStart);
+ xRet = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), &m_rMeta, CursorType::Meta, aPos));
+ }
+ }
+ return xRet;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL
+SwXMetaText::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XText
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXMetaText::createTextCursor()
+{
+ return CreateCursor();
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXMetaText::createTextCursorByRange(
+ const uno::Reference<text::XTextRange> & xTextPosition)
+{
+ const uno::Reference<text::XTextCursor> xCursor( CreateCursor() );
+ xCursor->gotoRange(xTextPosition, false);
+ return xCursor;
+}
+
+/**
+ * the Meta has a cached list of text portions for its contents
+ * this list is created by SwXTextPortionEnumeration
+ * the Meta listens at the SwTextNode and throws away the cache when it changes
+ *
+ * This inner part of SwXMeta is deleted with a locked SolarMutex.
+ */
+class SwXMeta::Impl : public SvtListener
+{
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ std::unique_ptr<const TextRangeList_t> m_pTextPortions;
+ // 3 possible states: not attached, attached, disposed
+ bool m_bIsDisposed;
+ bool m_bIsDescriptor;
+ uno::Reference<text::XText> m_xParentText;
+ rtl::Reference<SwXMetaText> m_xText;
+ sw::Meta* m_pMeta;
+
+ Impl(SwXMeta& rThis, SwDoc& rDoc,
+ ::sw::Meta* const pMeta,
+ uno::Reference<text::XText> const& xParentText,
+ std::unique_ptr<TextRangeList_t const> pPortions)
+ : m_pTextPortions(std::move(pPortions))
+ , m_bIsDisposed(false)
+ , m_bIsDescriptor(nullptr == pMeta)
+ , m_xParentText(xParentText)
+ , m_xText(new SwXMetaText(rDoc, rThis))
+ , m_pMeta(pMeta)
+ {
+ !m_bIsDescriptor && StartListening(m_pMeta->GetNotifier());
+ }
+
+ inline const ::sw::Meta* GetMeta() const;
+ // only for SwXMetaField!
+ inline const ::sw::MetaField* GetMetaField() const;
+protected:
+ virtual void Notify(const SfxHint& rHint) override;
+
+};
+
+inline const ::sw::Meta* SwXMeta::Impl::GetMeta() const
+{
+ return m_pMeta;
+}
+
+// sw::BroadcastingModify
+void SwXMeta::Impl::Notify(const SfxHint& rHint)
+{
+ m_pTextPortions.reset(); // throw away cache (SwTextNode changed)
+ if(rHint.GetId() != SfxHintId::Dying && rHint.GetId() != SfxHintId::Deinitializing)
+ return;
+
+ m_bIsDisposed = true;
+ m_pMeta = nullptr;
+ m_xText->Invalidate();
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+}
+
+uno::Reference<text::XText> const & SwXMeta::GetParentText() const
+{
+ return m_pImpl->m_xParentText;
+}
+
+SwXMeta::SwXMeta(SwDoc *const pDoc, ::sw::Meta *const pMeta,
+ uno::Reference<text::XText> const& xParentText,
+ std::unique_ptr<TextRangeList_t const> pPortions)
+ : m_pImpl( new SwXMeta::Impl(*this, *pDoc, pMeta, xParentText, std::move(pPortions)) )
+{
+}
+
+SwXMeta::SwXMeta(SwDoc *const pDoc)
+ : m_pImpl( new SwXMeta::Impl(*this, *pDoc, nullptr, nullptr, nullptr) )
+{
+}
+
+SwXMeta::~SwXMeta()
+{
+}
+
+uno::Reference<rdf::XMetadatable>
+SwXMeta::CreateXMeta(SwDoc & rDoc, bool const isField)
+{
+ SwXMeta *const pXMeta(isField
+ ? new SwXMetaField(& rDoc) : new SwXMeta(& rDoc));
+ // this is why the constructor is private: need to acquire pXMeta here
+ uno::Reference<rdf::XMetadatable> const xMeta(pXMeta);
+ // need a permanent Reference to initialize m_wThis
+ pXMeta->m_pImpl->m_wThis = xMeta;
+ return xMeta;
+}
+
+uno::Reference<rdf::XMetadatable>
+SwXMeta::CreateXMeta(::sw::Meta & rMeta,
+ uno::Reference<text::XText> const& i_xParent,
+ std::unique_ptr<TextRangeList_t const> && pPortions)
+{
+ // re-use existing SwXMeta
+ // #i105557#: do not iterate over the registered clients: race condition
+ uno::Reference<rdf::XMetadatable> xMeta(rMeta.GetXMeta());
+ if (xMeta.is())
+ {
+ if (pPortions) // set cache in the XMeta to the given portions
+ {
+ SwXMeta *const pXMeta(
+ comphelper::getFromUnoTunnel<SwXMeta>(xMeta));
+ assert(pXMeta);
+ // NB: the meta must always be created with the complete content
+ // if SwXTextPortionEnumeration is created for a selection,
+ // it must be checked that the Meta is contained in the selection!
+ pXMeta->m_pImpl->m_pTextPortions = std::move(pPortions);
+ // ??? is this necessary?
+ if (pXMeta->m_pImpl->m_xParentText.get() != i_xParent.get())
+ {
+ SAL_WARN("sw.uno", "SwXMeta with different parent?");
+ pXMeta->m_pImpl->m_xParentText.set(i_xParent);
+ }
+ }
+ return xMeta;
+ }
+
+ // create new SwXMeta
+ SwTextNode * const pTextNode( rMeta.GetTextNode() );
+ SAL_WARN_IF(!pTextNode, "sw.uno", "CreateXMeta: no text node?");
+ if (!pTextNode) { return nullptr; }
+ uno::Reference<text::XText> xParentText(i_xParent);
+ if (!xParentText.is())
+ {
+ SwTextMeta * const pTextAttr( rMeta.GetTextAttr() );
+ SAL_WARN_IF(!pTextAttr, "sw.uno", "CreateXMeta: no text attr?");
+ if (!pTextAttr) { return nullptr; }
+ const SwPosition aPos(*pTextNode, pTextAttr->GetStart());
+ xParentText.set( ::sw::CreateParentXText(pTextNode->GetDoc(), aPos) );
+ }
+ if (!xParentText.is()) { return nullptr; }
+ SwXMeta *const pXMeta( (RES_TXTATR_META == rMeta.GetFormatMeta()->Which())
+ ? new SwXMeta (&pTextNode->GetDoc(), &rMeta, xParentText,
+ std::move(pPortions))
+ : new SwXMetaField(&pTextNode->GetDoc(), &rMeta, xParentText,
+ std::move(pPortions)));
+ // this is why the constructor is private: need to acquire pXMeta here
+ xMeta.set(pXMeta);
+ // in order to initialize the weak pointer cache in the core object
+ rMeta.SetXMeta(xMeta);
+ // need a permanent Reference to initialize m_wThis
+ pXMeta->m_pImpl->m_wThis = xMeta;
+ return xMeta;
+}
+
+bool SwXMeta::SetContentRange(
+ SwTextNode *& rpNode, sal_Int32 & rStart, sal_Int32 & rEnd ) const
+{
+ ::sw::Meta const * const pMeta( m_pImpl->GetMeta() );
+ if (pMeta)
+ {
+ SwTextMeta const * const pTextAttr( pMeta->GetTextAttr() );
+ if (pTextAttr)
+ {
+ rpNode = pMeta->GetTextNode();
+ if (rpNode)
+ {
+ // rStart points at the first position _within_ the meta!
+ rStart = pTextAttr->GetStart() + 1;
+ rEnd = *pTextAttr->End();
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool SwXMeta::CheckForOwnMemberMeta(const SwPaM & rPam, const bool bAbsorb)
+{
+ SwTextNode * pTextNode;
+ sal_Int32 nMetaStart;
+ sal_Int32 nMetaEnd;
+ const bool bSuccess( SetContentRange(pTextNode, nMetaStart, nMetaEnd) );
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (!bSuccess)
+ throw lang::DisposedException();
+
+ SwPosition const * const pStartPos( rPam.Start() );
+ if (&pStartPos->nNode.GetNode() != pTextNode)
+ {
+ throw lang::IllegalArgumentException(
+ "trying to insert into a nesting text content, but start "
+ "of text range not in same paragraph as text content",
+ nullptr, 0);
+ }
+ bool bForceExpandHints(false);
+ const sal_Int32 nStartPos(pStartPos->nContent.GetIndex());
+ // not <= but < because nMetaStart is behind dummy char!
+ // not >= but > because == means insert at end!
+ if ((nStartPos < nMetaStart) || (nStartPos > nMetaEnd))
+ {
+ throw lang::IllegalArgumentException(
+ "trying to insert into a nesting text content, but start "
+ "of text range not inside text content",
+ nullptr, 0);
+ }
+ else if (nStartPos == nMetaEnd)
+ {
+ bForceExpandHints = true;
+ }
+ if (rPam.HasMark() && bAbsorb)
+ {
+ SwPosition const * const pEndPos( rPam.End() );
+ if (&pEndPos->nNode.GetNode() != pTextNode)
+ {
+ throw lang::IllegalArgumentException(
+ "trying to insert into a nesting text content, but end "
+ "of text range not in same paragraph as text content",
+ nullptr, 0);
+ }
+ const sal_Int32 nEndPos(pEndPos->nContent.GetIndex());
+ // not <= but < because nMetaStart is behind dummy char!
+ // not >= but > because == means insert at end!
+ if ((nEndPos < nMetaStart) || (nEndPos > nMetaEnd))
+ {
+ throw lang::IllegalArgumentException(
+ "trying to insert into a nesting text content, but end "
+ "of text range not inside text content",
+ nullptr, 0);
+ }
+ else if (nEndPos == nMetaEnd)
+ {
+ bForceExpandHints = true;
+ }
+ }
+ return bForceExpandHints;
+}
+
+const uno::Sequence< sal_Int8 > & SwXMeta::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXMetaUnoTunnelId;
+ return theSwXMetaUnoTunnelId.getSeq();
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL
+SwXMeta::getSomething( const uno::Sequence< sal_Int8 > & i_rId )
+{
+ return comphelper::getSomethingImpl<SwXMeta>(i_rId, this);
+}
+
+// XServiceInfo
+OUString SAL_CALL
+SwXMeta::getImplementationName()
+{
+ return "SwXMeta";
+}
+
+sal_Bool SAL_CALL
+SwXMeta::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXMeta::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.InContentMetadata"
+ };
+}
+
+// XComponent
+void SAL_CALL
+SwXMeta::addEventListener(
+ uno::Reference< lang::XEventListener> const & xListener )
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL
+SwXMeta::removeEventListener(
+ uno::Reference< lang::XEventListener> const & xListener )
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+void SAL_CALL
+SwXMeta::dispose()
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pTextPortions.reset();
+ lang::EventObject const ev(static_cast< ::cppu::OWeakObject&>(*this));
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.disposeAndClear(aGuard, ev);
+ m_pImpl->m_bIsDisposed = true;
+ m_pImpl->m_xText->Invalidate();
+ }
+ else if (!m_pImpl->m_bIsDisposed)
+ {
+ SwTextNode * pTextNode;
+ sal_Int32 nMetaStart;
+ sal_Int32 nMetaEnd;
+ const bool bSuccess(SetContentRange(pTextNode, nMetaStart, nMetaEnd));
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (bSuccess)
+ {
+ // -1 because of CH_TXTATR
+ SwPaM aPam( *pTextNode, nMetaStart - 1, *pTextNode, nMetaEnd );
+ SwDoc& rDoc( pTextNode->GetDoc() );
+ rDoc.getIDocumentContentOperations().DeleteAndJoin( aPam );
+
+ // removal should call Modify and do the dispose
+ assert(m_pImpl->m_bIsDisposed);
+ }
+ }
+}
+
+void
+SwXMeta::AttachImpl(const uno::Reference< text::XTextRange > & i_xTextRange,
+ const sal_uInt16 i_nWhich)
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDisposed)
+ {
+ throw lang::DisposedException();
+ }
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException(
+ "SwXMeta::attach(): already attached",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel(i_xTextRange, uno::UNO_QUERY);
+ if (!xRangeTunnel.is())
+ {
+ throw lang::IllegalArgumentException(
+ "SwXMeta::attach(): argument is no XUnoTunnel",
+ static_cast< ::cppu::OWeakObject* >(this), 0);
+ }
+ SwXTextRange *const pRange(
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel));
+ OTextCursorHelper *const pCursor( pRange ? nullptr :
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel));
+ if (!pRange && !pCursor)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXMeta::attach(): argument not supported type",
+ static_cast< ::cppu::OWeakObject* >(this), 0);
+ }
+
+ SwDoc * const pDoc(
+ pRange ? &pRange->GetDoc() : pCursor->GetDoc());
+ if (!pDoc)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXMeta::attach(): argument has no SwDoc",
+ static_cast< ::cppu::OWeakObject* >(this), 0);
+ }
+
+ SwUnoInternalPaM aPam(*pDoc);
+ ::sw::XTextRangeToSwPaM(aPam, i_xTextRange);
+
+ UnoActionContext aContext(pDoc);
+
+ SwXTextCursor const*const pTextCursor(
+ dynamic_cast<SwXTextCursor*>(pCursor));
+ const bool bForceExpandHints(pTextCursor && pTextCursor->IsAtEndOfMeta());
+ const SetAttrMode nInsertFlags( bForceExpandHints
+ ? ( SetAttrMode::FORCEHINTEXPAND
+ | SetAttrMode::DONTEXPAND)
+ : SetAttrMode::DONTEXPAND );
+
+ const std::shared_ptr< ::sw::Meta> pMeta( (RES_TXTATR_META == i_nWhich)
+ ? std::make_shared< ::sw::Meta>( nullptr )
+ : std::shared_ptr< ::sw::Meta>(
+ pDoc->GetMetaFieldManager().makeMetaField()) );
+ SwFormatMeta meta(pMeta, i_nWhich); // this is cloned by Insert!
+ const bool bSuccess( pDoc->getIDocumentContentOperations().InsertPoolItem( aPam, meta, nInsertFlags ) );
+ SwTextAttr * const pTextAttr( pMeta->GetTextAttr() );
+ if (!bSuccess)
+ {
+ throw lang::IllegalArgumentException(
+ "SwXMeta::attach(): cannot create meta: range invalid?",
+ static_cast< ::cppu::OWeakObject* >(this), 1);
+ }
+ if (!pTextAttr)
+ {
+ OSL_FAIL("meta inserted, but has no text attribute?");
+ throw uno::RuntimeException(
+ "SwXMeta::attach(): cannot create meta",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ m_pImpl->EndListeningAll();
+ m_pImpl->m_pMeta = pMeta.get();
+ m_pImpl->StartListening(pMeta->GetNotifier());
+ pMeta->SetXMeta(uno::Reference<rdf::XMetadatable>(this));
+
+ m_pImpl->m_xParentText = ::sw::CreateParentXText(*pDoc, *aPam.GetPoint());
+
+ m_pImpl->m_bIsDescriptor = false;
+}
+
+// XTextContent
+void SAL_CALL
+SwXMeta::attach(const uno::Reference< text::XTextRange > & i_xTextRange)
+{
+ return SwXMeta::AttachImpl(i_xTextRange, RES_TXTATR_META);
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXMeta::getAnchor()
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDisposed)
+ {
+ throw lang::DisposedException();
+ }
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException(
+ "SwXMeta::getAnchor(): not inserted",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ SwTextNode * pTextNode;
+ sal_Int32 nMetaStart;
+ sal_Int32 nMetaEnd;
+ const bool bSuccess(SetContentRange(pTextNode, nMetaStart, nMetaEnd));
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (!bSuccess)
+ {
+ throw lang::DisposedException(
+ "SwXMeta::getAnchor(): not attached",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ const SwPosition start(*pTextNode, nMetaStart - 1); // -1 due to CH_TXTATR
+ const SwPosition end(*pTextNode, nMetaEnd);
+ return SwXTextRange::CreateXTextRange(pTextNode->GetDoc(), start, &end);
+}
+
+// XTextRange
+uno::Reference< text::XText > SAL_CALL
+SwXMeta::getText()
+{
+ return this;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXMeta::getStart()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->getStart();
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXMeta::getEnd()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->getEnd();
+}
+
+OUString SAL_CALL
+SwXMeta::getString()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->getString();
+}
+
+void SAL_CALL
+SwXMeta::setString(const OUString& rString)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->setString(rString);
+}
+
+// XSimpleText
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXMeta::createTextCursor()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->createTextCursor();
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXMeta::createTextCursorByRange(
+ const uno::Reference<text::XTextRange> & xTextPosition)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->createTextCursorByRange(xTextPosition);
+}
+
+void SAL_CALL
+SwXMeta::insertString(const uno::Reference<text::XTextRange> & xRange,
+ const OUString& rString, sal_Bool bAbsorb)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->insertString(xRange, rString, bAbsorb);
+}
+
+void SAL_CALL
+SwXMeta::insertControlCharacter(const uno::Reference<text::XTextRange> & xRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->insertControlCharacter(xRange, nControlCharacter,
+ bAbsorb);
+}
+
+// XText
+void SAL_CALL
+SwXMeta::insertTextContent( const uno::Reference<text::XTextRange> & xRange,
+ const uno::Reference<text::XTextContent> & xContent, sal_Bool bAbsorb)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->insertTextContent(xRange, xContent, bAbsorb);
+}
+
+void SAL_CALL
+SwXMeta::removeTextContent(
+ const uno::Reference< text::XTextContent > & xContent)
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_xText->removeTextContent(xContent);
+}
+
+// XChild
+uno::Reference< uno::XInterface > SAL_CALL
+SwXMeta::getParent()
+{
+ SolarMutexGuard g;
+ SwTextNode * pTextNode;
+ sal_Int32 nMetaStart;
+ sal_Int32 nMetaEnd;
+ bool const bSuccess( SetContentRange(pTextNode, nMetaStart, nMetaEnd) );
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (!bSuccess) { throw lang::DisposedException(); }
+ // in order to prevent getting this meta, subtract 1 from nMetaStart;
+ // so we get the index of the dummy character, and we exclude it
+ // by calling GetTextAttrAt(_, _, PARENT) in GetNestedTextContent
+ uno::Reference<text::XTextContent> const xRet(
+ SwUnoCursorHelper::GetNestedTextContent(*pTextNode, nMetaStart - 1,
+ true) );
+ return xRet;
+}
+
+void SAL_CALL
+SwXMeta::setParent(uno::Reference< uno::XInterface > const& /*xParent*/)
+{
+ throw lang::NoSupportException("setting parent not supported", *this);
+}
+
+// XElementAccess
+uno::Type SAL_CALL
+SwXMeta::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL SwXMeta::hasElements()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_pMeta != nullptr;
+}
+
+// XEnumerationAccess
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXMeta::createEnumeration()
+{
+ SolarMutexGuard g;
+
+ if (m_pImpl->m_bIsDisposed)
+ {
+ throw lang::DisposedException();
+ }
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException(
+ "createEnumeration(): not inserted",
+ static_cast< ::cppu::OWeakObject* >(this));
+ }
+
+ SwTextNode * pTextNode;
+ sal_Int32 nMetaStart;
+ sal_Int32 nMetaEnd;
+ const bool bSuccess(SetContentRange(pTextNode, nMetaStart, nMetaEnd));
+ OSL_ENSURE(bSuccess, "no pam?");
+ if (!bSuccess)
+ throw lang::DisposedException();
+
+ SwPaM aPam(*pTextNode, nMetaStart);
+
+ if (!m_pImpl->m_pTextPortions)
+ {
+ return new SwXTextPortionEnumeration(
+ aPam, GetParentText(), nMetaStart, nMetaEnd);
+ }
+ else // cached!
+ {
+ return new SwXTextPortionEnumeration(aPam, std::deque(*m_pImpl->m_pTextPortions));
+ }
+}
+
+// MetadatableMixin
+::sfx2::Metadatable* SwXMeta::GetCoreObject()
+{
+ return const_cast< ::sw::Meta * >(m_pImpl->GetMeta());
+}
+
+uno::Reference<frame::XModel> SwXMeta::GetModel()
+{
+ ::sw::Meta const * const pMeta( m_pImpl->GetMeta() );
+ if (pMeta)
+ {
+ SwTextNode const * const pTextNode( pMeta->GetTextNode() );
+ if (pTextNode)
+ {
+ SwDocShell const * const pShell(pTextNode->GetDoc().GetDocShell());
+ return pShell ? pShell->GetModel() : nullptr;
+ }
+ }
+ return nullptr;
+}
+
+inline const ::sw::MetaField* SwXMeta::Impl::GetMetaField() const
+{
+ return dynamic_cast<sw::MetaField*>(m_pMeta);
+}
+
+SwXMetaField::SwXMetaField(SwDoc *const pDoc, ::sw::Meta *const pMeta,
+ uno::Reference<text::XText> const& xParentText,
+ std::unique_ptr<TextRangeList_t const> pPortions)
+ : SwXMetaField_Base(pDoc, pMeta, xParentText, std::move(pPortions))
+{
+ assert(dynamic_cast< ::sw::MetaField* >(pMeta) && "SwXMetaField created for wrong hint!");
+}
+
+SwXMetaField::SwXMetaField(SwDoc *const pDoc)
+ : SwXMetaField_Base(pDoc)
+{
+}
+
+SwXMetaField::~SwXMetaField()
+{
+}
+
+// XServiceInfo
+OUString SAL_CALL
+SwXMetaField::getImplementationName()
+{
+ return "SwXMetaField";
+}
+
+sal_Bool SAL_CALL
+SwXMetaField::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXMetaField::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.TextField",
+ "com.sun.star.text.textfield.MetadataField"
+ };
+}
+
+// XComponent
+void SAL_CALL
+SwXMetaField::addEventListener(
+ uno::Reference< lang::XEventListener> const & xListener )
+{
+ return SwXMeta::addEventListener(xListener);
+}
+
+void SAL_CALL
+SwXMetaField::removeEventListener(
+ uno::Reference< lang::XEventListener> const & xListener )
+{
+ return SwXMeta::removeEventListener(xListener);
+}
+
+void SAL_CALL
+SwXMetaField::dispose()
+{
+ return SwXMeta::dispose();
+}
+
+// XTextContent
+void SAL_CALL
+SwXMetaField::attach(const uno::Reference< text::XTextRange > & i_xTextRange)
+{
+ return SwXMeta::AttachImpl(i_xTextRange, RES_TXTATR_METAFIELD);
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXMetaField::getAnchor()
+{
+ return SwXMeta::getAnchor();
+}
+
+// XPropertySet
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXMetaField::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xRef(
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_METAFIELD)
+ ->getPropertySetInfo() );
+ return xRef;
+}
+
+void SAL_CALL
+SwXMetaField::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard g;
+
+ ::sw::MetaField * const pMeta(
+ const_cast< ::sw::MetaField * >(m_pImpl->GetMetaField()) );
+ if (!pMeta)
+ throw lang::DisposedException();
+
+ if ( rPropertyName == "NumberFormat" )
+ {
+ sal_Int32 nNumberFormat(0);
+ if (rValue >>= nNumberFormat)
+ {
+ pMeta->SetNumberFormat(static_cast<sal_uInt32>(nNumberFormat));
+ }
+ }
+ else if ( rPropertyName == "IsFixedLanguage" )
+ {
+ bool b(false);
+ if (rValue >>= b)
+ {
+ pMeta->SetIsFixedLanguage(b);
+ }
+ }
+ else
+ {
+ throw beans::UnknownPropertyException(rPropertyName);
+ }
+}
+
+uno::Any SAL_CALL
+SwXMetaField::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard g;
+
+ ::sw::MetaField const * const pMeta( m_pImpl->GetMetaField() );
+ if (!pMeta)
+ throw lang::DisposedException();
+
+ uno::Any any;
+
+ if ( rPropertyName == "NumberFormat" )
+ {
+ const OUString text( getPresentation(false) );
+ any <<= static_cast<sal_Int32>(pMeta->GetNumberFormat(text));
+ }
+ else if ( rPropertyName == "IsFixedLanguage" )
+ {
+ any <<= pMeta->IsFixedLanguage();
+ }
+ else
+ {
+ throw beans::UnknownPropertyException(rPropertyName);
+ }
+
+ return any;
+}
+
+void SAL_CALL
+SwXMetaField::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXMetaField::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXMetaField::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXMetaField::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXMetaField::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXMetaField::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXMetaField::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXMetaField::removeVetoableChangeListener(): not implemented");
+}
+
+static uno::Reference<rdf::XURI> const&
+lcl_getURI(const sal_Int16 eKnown)
+{
+ static uno::Reference< uno::XComponentContext > xContext(
+ ::comphelper::getProcessComponentContext());
+ static uno::Reference< rdf::XURI > xOdfPrefix(
+ rdf::URI::createKnown(xContext, rdf::URIs::ODF_PREFIX),
+ uno::UNO_SET_THROW);
+ static uno::Reference< rdf::XURI > xOdfSuffix(
+ rdf::URI::createKnown(xContext, rdf::URIs::ODF_SUFFIX),
+ uno::UNO_SET_THROW);
+ static uno::Reference< rdf::XURI > xOdfShading(
+ rdf::URI::createKnown(xContext, rdf::URIs::LO_EXT_SHADING),
+ uno::UNO_SET_THROW);
+ switch (eKnown)
+ {
+ case rdf::URIs::ODF_PREFIX:
+ return xOdfPrefix;
+ case rdf::URIs::ODF_SUFFIX:
+ return xOdfSuffix;
+ default:
+ return xOdfShading;
+ }
+}
+
+static OUString
+lcl_getPrefixOrSuffix(
+ uno::Reference<rdf::XRepository> const & xRepository,
+ uno::Reference<rdf::XResource> const & xMetaField,
+ uno::Reference<rdf::XURI> const & xPredicate)
+{
+ const uno::Reference<container::XEnumeration> xEnum(
+ xRepository->getStatements(xMetaField, xPredicate, nullptr),
+ uno::UNO_SET_THROW);
+ while (xEnum->hasMoreElements()) {
+ rdf::Statement stmt;
+ if (!(xEnum->nextElement() >>= stmt)) {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<rdf::XLiteral> xObject(stmt.Object,
+ uno::UNO_QUERY);
+ if (!xObject.is()) continue;
+ if (xEnum->hasMoreElements()) {
+ SAL_INFO("sw.uno", "ignoring other odf:Prefix/odf:Suffix statements");
+ }
+ return xObject->getValue();
+ }
+ return OUString();
+}
+
+void
+getPrefixAndSuffix(
+ const uno::Reference<frame::XModel>& xModel,
+ const uno::Reference<rdf::XMetadatable>& xMetaField,
+ OUString *const o_pPrefix, OUString *const o_pSuffix, OUString *const o_pShadingColor)
+{
+ try {
+ const uno::Reference<rdf::XRepositorySupplier> xRS(
+ xModel, uno::UNO_QUERY_THROW);
+ const uno::Reference<rdf::XRepository> xRepo(
+ xRS->getRDFRepository(), uno::UNO_SET_THROW);
+ const uno::Reference<rdf::XResource> xMeta(
+ xMetaField, uno::UNO_QUERY_THROW);
+ if (o_pPrefix)
+ {
+ *o_pPrefix = lcl_getPrefixOrSuffix(xRepo, xMeta, lcl_getURI(rdf::URIs::ODF_PREFIX));
+ }
+ if (o_pSuffix)
+ {
+ *o_pSuffix = lcl_getPrefixOrSuffix(xRepo, xMeta, lcl_getURI(rdf::URIs::ODF_SUFFIX));
+ }
+ if (o_pShadingColor)
+ {
+ *o_pShadingColor = lcl_getPrefixOrSuffix(xRepo, xMeta, lcl_getURI(rdf::URIs::LO_EXT_SHADING));
+ }
+ } catch (uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("getPrefixAndSuffix: exception", nullptr, anyEx);
+ }
+}
+
+// XTextField
+OUString SAL_CALL
+SwXMetaField::getPresentation(sal_Bool bShowCommand)
+{
+ SolarMutexGuard g;
+
+ if (bShowCommand)
+ {
+//FIXME ?
+ return OUString();
+ }
+ else
+ {
+ // getString should check if this is invalid
+ const OUString content( getString() );
+ OUString prefix;
+ OUString suffix;
+ getPrefixAndSuffix(GetModel(), this, &prefix, &suffix, nullptr);
+ return prefix + content + suffix;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unosect.cxx b/sw/source/core/unocore/unosect.cxx
new file mode 100644
index 000000000..292cfdb8e
--- /dev/null
+++ b/sw/source/core/unocore/unosect.cxx
@@ -0,0 +1,1742 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <memory>
+#include <unosection.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/text/SectionFileLink.hpp>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cmdid.h>
+#include <hintids.hxx>
+#include <svl/urihelper.hxx>
+#include <svl/listener.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <osl/diagnose.h>
+#include <vcl/svapp.hxx>
+#include <fmtclds.hxx>
+#include <unotextrange.hxx>
+#include <TextCursorHelper.hxx>
+#include <unoport.hxx>
+#include <redline.hxx>
+#include <unomap.hxx>
+#include <section.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <docsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <docary.hxx>
+#include <swundo.hxx>
+#include <tox.hxx>
+#include <unoidx.hxx>
+#include <doctxm.hxx>
+#include <fmtftntx.hxx>
+#include <fmtclbl.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <fmtcntnt.hxx>
+#include <editeng/lrspitem.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+struct SwTextSectionProperties_Impl
+{
+ uno::Sequence<sal_Int8> m_Password;
+ OUString m_sCondition;
+ OUString m_sLinkFileName;
+ OUString m_sSectionFilter;
+ OUString m_sSectionRegion;
+
+ std::unique_ptr<SwFormatCol> m_pColItem;
+ std::unique_ptr<SvxBrushItem> m_pBrushItem;
+ std::unique_ptr<SwFormatFootnoteAtTextEnd> m_pFootnoteItem;
+ std::unique_ptr<SwFormatEndAtTextEnd> m_pEndItem;
+ std::unique_ptr<SvXMLAttrContainerItem> m_pXMLAttr;
+ std::unique_ptr<SwFormatNoBalancedColumns> m_pNoBalanceItem;
+ std::unique_ptr<SvxFrameDirectionItem> m_pFrameDirItem;
+ std::unique_ptr<SvxLRSpaceItem> m_pLRSpaceItem;
+
+ bool m_bDDE;
+ bool m_bHidden;
+ bool m_bCondHidden;
+ bool m_bProtect;
+ bool m_bEditInReadonly;
+ bool m_bUpdateType;
+
+ SwTextSectionProperties_Impl()
+ : m_bDDE(false)
+ , m_bHidden(false)
+ , m_bCondHidden(false)
+ , m_bProtect(false)
+ , m_bEditInReadonly(false)
+ , m_bUpdateType(true)
+ {
+ }
+
+};
+
+}
+
+class SwXTextSection::Impl
+ : public SvtListener
+{
+public:
+ SwXTextSection & m_rThis;
+ uno::WeakReference<uno::XInterface> m_wThis;
+ const SfxItemPropertySet & m_rPropSet;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ const bool m_bIndexHeader;
+ bool m_bIsDescriptor;
+ OUString m_sName;
+ std::unique_ptr<SwTextSectionProperties_Impl> m_pProps;
+ SwSectionFormat* m_pFormat;
+
+ Impl( SwXTextSection& rThis,
+ SwSectionFormat* const pFormat, const bool bIndexHeader)
+ : m_rThis(rThis)
+ , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_SECTION))
+ , m_bIndexHeader(bIndexHeader)
+ , m_bIsDescriptor(nullptr == pFormat)
+ , m_pProps(pFormat ? nullptr : new SwTextSectionProperties_Impl())
+ , m_pFormat(pFormat)
+ {
+ if(m_pFormat)
+ StartListening(m_pFormat->GetNotifier());
+ }
+
+ void Attach(SwSectionFormat* pFormat)
+ {
+ EndListeningAll();
+ StartListening(pFormat->GetNotifier());
+ m_pFormat = pFormat;
+ }
+
+ SwSectionFormat* GetSectionFormat() const
+ { return m_pFormat; }
+
+ SwSectionFormat & GetSectionFormatOrThrow() const {
+ SwSectionFormat *const pFormat( GetSectionFormat() );
+ if (!pFormat) {
+ throw uno::RuntimeException("SwXTextSection: disposed or invalid", nullptr);
+ }
+ return *pFormat;
+ }
+
+ /// @throws beans::UnknownPropertyException
+ /// @throws beans::PropertyVetoException,
+ /// @throws lang::IllegalArgumentException
+ /// @throws lang::WrappedTargetException,
+ /// @throws uno::RuntimeException
+ void SetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& aValues);
+ /// @throws beans::UnknownPropertyException
+ /// @throws lang::WrappedTargetException,
+ /// @throws uno::RuntimeException
+ uno::Sequence< uno::Any >
+ GetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames);
+ virtual void Notify(const SfxHint& rHint) override;
+};
+
+void SwXTextSection::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pFormat = nullptr;
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (!xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ return;
+ }
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ }
+}
+
+SwSectionFormat * SwXTextSection::GetFormat() const
+{
+ return m_pImpl->GetSectionFormat();
+}
+
+uno::Reference< text::XTextSection >
+SwXTextSection::CreateXTextSection(
+ SwSectionFormat *const pFormat, const bool bIndexHeader)
+{
+ // re-use existing SwXTextSection
+ // #i105557#: do not iterate over the registered clients: race condition
+ uno::Reference< text::XTextSection > xSection;
+ if (pFormat)
+ {
+ xSection.set(pFormat->GetXTextSection());
+ }
+ if ( !xSection.is() )
+ {
+ rtl::Reference<SwXTextSection> pNew = new SwXTextSection(pFormat, bIndexHeader);
+ xSection.set(pNew);
+ if (pFormat)
+ {
+ pFormat->SetXTextSection(xSection);
+ }
+ // need a permanent Reference to initialize m_wThis
+ pNew->m_pImpl->m_wThis = xSection;
+ }
+ return xSection;
+}
+
+SwXTextSection::SwXTextSection(
+ SwSectionFormat *const pFormat, const bool bIndexHeader)
+ : m_pImpl( new SwXTextSection::Impl(*this, pFormat, bIndexHeader) )
+{
+}
+
+SwXTextSection::~SwXTextSection()
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwXTextSection::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextSectionUnoTunnelId;
+ return theSwXTextSectionUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXTextSection::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXTextSection>(rId, this);
+}
+
+uno::Reference< text::XTextSection > SAL_CALL
+SwXTextSection::getParentSection()
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat & rSectionFormat( m_pImpl->GetSectionFormatOrThrow() );
+
+ SwSectionFormat *const pParentFormat = rSectionFormat.GetParent();
+ const uno::Reference< text::XTextSection > xRet =
+ pParentFormat ? CreateXTextSection(pParentFormat) : nullptr;
+ return xRet;
+}
+
+uno::Sequence< uno::Reference< text::XTextSection > > SAL_CALL
+SwXTextSection::getChildSections()
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat & rSectionFormat( m_pImpl->GetSectionFormatOrThrow() );
+
+ SwSections aChildren;
+ rSectionFormat.GetChildSections(aChildren, SectionSort::Not, false);
+ uno::Sequence<uno::Reference<text::XTextSection> > aSeq(aChildren.size());
+ uno::Reference< text::XTextSection > * pArray = aSeq.getArray();
+ for (size_t i = 0; i < aChildren.size(); ++i)
+ {
+ SwSectionFormat *const pChild = aChildren[i]->GetFormat();
+ pArray[i] = CreateXTextSection(pChild);
+ }
+ return aSeq;
+}
+
+void SAL_CALL
+SwXTextSection::attach(const uno::Reference< text::XTextRange > & xTextRange)
+{
+ SolarMutexGuard g;
+
+ if (!m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper* pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ SwDoc *const pDoc =
+ pRange ? &pRange->GetDoc() : (pCursor ? pCursor->GetDoc() : nullptr);
+ if (!pDoc)
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SwUnoInternalPaM aPam(*pDoc);
+ // this has to return true now
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+ UnoActionContext aCont(pDoc);
+ pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::INSSECTION, nullptr );
+
+ if (m_pImpl->m_sName.isEmpty())
+ {
+ m_pImpl->m_sName = "TextSection";
+ }
+ SectionType eType(SectionType::FileLink);
+ if( m_pImpl->m_pProps->m_bDDE )
+ eType = SectionType::DdeLink;
+ else if( m_pImpl->m_pProps->m_sLinkFileName.isEmpty() && m_pImpl->m_pProps->m_sSectionRegion.isEmpty() )
+ eType = SectionType::Content;
+ // index header section?
+ if (m_pImpl->m_bIndexHeader)
+ {
+ // caller wants an index header section, but will only
+ // give him one if a) we are inside an index, and b) said
+ // index doesn't yet have a header section.
+ const SwTOXBase* pBase = SwDoc::GetCurTOX(*aPam.Start());
+
+ // are we inside an index?
+ if (pBase)
+ {
+ // get all child sections
+ SwSections aSectionsArr;
+ static_cast<const SwTOXBaseSection*>(pBase)->GetFormat()->
+ GetChildSections(aSectionsArr);
+
+ // and search for current header section
+ const size_t nCount = aSectionsArr.size();
+ bool bHeaderPresent = false;
+ for(size_t i = 0; i < nCount; ++i)
+ {
+ if (aSectionsArr[i]->GetType() == SectionType::ToxHeader)
+ bHeaderPresent = true;
+ }
+ if (! bHeaderPresent)
+ {
+ eType = SectionType::ToxHeader;
+ }
+ }
+ }
+
+ SwSectionData aSect(eType, pDoc->GetUniqueSectionName(&m_pImpl->m_sName));
+ aSect.SetCondition(m_pImpl->m_pProps->m_sCondition);
+ aSect.SetLinkFileName(m_pImpl->m_pProps->m_sLinkFileName +
+ OUStringChar(sfx2::cTokenSeparator) +
+ m_pImpl->m_pProps->m_sSectionFilter +
+ OUStringChar(sfx2::cTokenSeparator) +
+ m_pImpl->m_pProps->m_sSectionRegion);
+
+ aSect.SetHidden(m_pImpl->m_pProps->m_bHidden);
+ aSect.SetProtectFlag(m_pImpl->m_pProps->m_bProtect);
+ aSect.SetEditInReadonlyFlag(m_pImpl->m_pProps->m_bEditInReadonly);
+
+ SfxItemSetFixed<
+ RES_LR_SPACE, RES_LR_SPACE,
+ RES_BACKGROUND, RES_BACKGROUND,
+ RES_COL, RES_COL,
+ RES_FTN_AT_TXTEND, RES_FRAMEDIR,
+ RES_UNKNOWNATR_CONTAINER,RES_UNKNOWNATR_CONTAINER>
+ aSet(pDoc->GetAttrPool());
+ if (m_pImpl->m_pProps->m_pBrushItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pBrushItem);
+ }
+ if (m_pImpl->m_pProps->m_pColItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pColItem);
+ }
+ if (m_pImpl->m_pProps->m_pFootnoteItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pFootnoteItem);
+ }
+ if (m_pImpl->m_pProps->m_pEndItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pEndItem);
+ }
+ if (m_pImpl->m_pProps->m_pXMLAttr)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pXMLAttr);
+ }
+ if (m_pImpl->m_pProps->m_pNoBalanceItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pNoBalanceItem);
+ }
+ if (m_pImpl->m_pProps->m_pFrameDirItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pFrameDirItem);
+ }
+ if (m_pImpl->m_pProps->m_pLRSpaceItem)
+ {
+ aSet.Put(*m_pImpl->m_pProps->m_pLRSpaceItem);
+ }
+ // section password
+ if (m_pImpl->m_pProps->m_Password.hasElements())
+ {
+ aSect.SetPassword(m_pImpl->m_pProps->m_Password);
+ }
+
+ SwSection *const pRet =
+ pDoc->InsertSwSection( aPam, aSect, nullptr, aSet.Count() ? &aSet : nullptr );
+ if (!pRet) // fdo#42450 text range could partially overlap existing section
+ {
+ // shouldn't have created an undo object yet
+ pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSSECTION, nullptr );
+ throw lang::IllegalArgumentException(
+ "SwXTextSection::attach(): invalid TextRange",
+ static_cast< ::cppu::OWeakObject*>(this), 0);
+ }
+ m_pImpl->Attach(pRet->GetFormat());
+ pRet->GetFormat()->SetXObject(static_cast< ::cppu::OWeakObject*>(this));
+
+ // XML import must hide sections depending on their old
+ // condition status
+ if (!m_pImpl->m_pProps->m_sCondition.isEmpty())
+ {
+ pRet->SetCondHidden(m_pImpl->m_pProps->m_bCondHidden);
+ }
+
+ // set update type if DDE link (and connect, if necessary)
+ if (m_pImpl->m_pProps->m_bDDE)
+ {
+ if (! pRet->IsConnected())
+ {
+ pRet->CreateLink(LinkCreateType::Connect);
+ }
+ pRet->SetUpdateType( m_pImpl->m_pProps->m_bUpdateType ?
+ SfxLinkUpdateMode::ALWAYS : SfxLinkUpdateMode::ONCALL );
+ }
+
+ // end the Undo bracketing here
+ pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::INSSECTION, nullptr );
+ m_pImpl->m_pProps.reset();
+ m_pImpl->m_bIsDescriptor = false;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXTextSection::getAnchor()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< text::XTextRange > xRet;
+ SwSectionFormat *const pSectFormat = m_pImpl->GetSectionFormat();
+ if(pSectFormat)
+ {
+ const SwNodeIndex* pIdx;
+ if( nullptr != ( pSectFormat->GetSection() ) &&
+ nullptr != ( pIdx = pSectFormat->GetContent().GetContentIdx() ) &&
+ pIdx->GetNode().GetNodes().IsDocNodes() )
+ {
+ bool isMoveIntoTable(false);
+ SwPaM aPaM(*pIdx);
+ aPaM.Move( fnMoveForward, GoInContent );
+ assert(pIdx->GetNode().IsSectionNode());
+ if (aPaM.GetPoint()->nNode.GetNode().FindTableNode() != pIdx->GetNode().FindTableNode()
+ || aPaM.GetPoint()->nNode.GetNode().FindSectionNode() != &pIdx->GetNode())
+ {
+ isMoveIntoTable = true;
+ }
+
+ const SwEndNode* pEndNode = pIdx->GetNode().EndOfSectionNode();
+ SwPaM aEnd(*pEndNode);
+ aEnd.Move( fnMoveBackward, GoInContent );
+ if (aEnd.GetPoint()->nNode.GetNode().FindTableNode() != pIdx->GetNode().FindTableNode()
+ || aEnd.GetPoint()->nNode.GetNode().FindSectionNode() != &pIdx->GetNode())
+ {
+ isMoveIntoTable = true;
+ }
+ if (isMoveIntoTable)
+ {
+ uno::Reference<text::XText> const xParentText(
+ ::sw::CreateParentXText(*pSectFormat->GetDoc(), SwPosition(*pIdx)));
+ xRet = new SwXTextRange(*pSectFormat);
+ }
+ else // for compatibility, keep the old way in this case
+ {
+ xRet = SwXTextRange::CreateXTextRange(*pSectFormat->GetDoc(),
+ *aPaM.Start(), aEnd.Start());
+ }
+ }
+ }
+ return xRet;
+}
+
+void SAL_CALL SwXTextSection::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat *const pSectFormat = m_pImpl->GetSectionFormat();
+ if (pSectFormat)
+ {
+ pSectFormat->GetDoc()->DelSectionFormat( pSectFormat );
+ }
+}
+
+void SAL_CALL SwXTextSection::addEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXTextSection::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXTextSection::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+ return m_pImpl->m_rPropSet.getPropertySetInfo();
+}
+
+static void
+lcl_UpdateLinkType(SwSection & rSection, bool const bLinkUpdateAlways)
+{
+ if (rSection.GetType() == SectionType::DdeLink)
+ {
+ // set update type; needs an established link
+ if (!rSection.IsConnected())
+ {
+ rSection.CreateLink(LinkCreateType::Connect);
+ }
+ rSection.SetUpdateType( bLinkUpdateAlways
+ ? SfxLinkUpdateMode::ALWAYS : SfxLinkUpdateMode::ONCALL );
+ }
+}
+
+static void
+lcl_UpdateSection(SwSectionFormat *const pFormat,
+ std::unique_ptr<SwSectionData> const& pSectionData,
+ std::optional<SfxItemSet> const& oItemSet,
+ bool const bLinkModeChanged, bool const bLinkUpdateAlways = true)
+{
+ if (!pFormat)
+ return;
+
+ SwSection & rSection = *pFormat->GetSection();
+ SwDoc *const pDoc = pFormat->GetDoc();
+ SwSectionFormats const& rFormats = pDoc->GetSections();
+ UnoActionContext aContext(pDoc);
+ for (size_t i = 0; i < rFormats.size(); ++i)
+ {
+ if (rFormats[i]->GetSection()->GetSectionName()
+ == rSection.GetSectionName())
+ {
+ pDoc->UpdateSection(i, *pSectionData, oItemSet ? &*oItemSet : nullptr,
+ pDoc->IsInReading());
+ {
+ // temporarily remove actions to allow cursor update
+ // TODO: why? no table cursor here!
+ UnoActionRemoveContext aRemoveContext( pDoc );
+ }
+
+ if (bLinkModeChanged)
+ {
+ lcl_UpdateLinkType(rSection, bLinkUpdateAlways);
+ }
+ // section found and processed: break from loop
+ break;
+ }
+ }
+}
+
+void SwXTextSection::Impl::SetPropertyValues_Impl(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues)
+{
+ if(rPropertyNames.getLength() != rValues.getLength())
+ {
+ throw lang::IllegalArgumentException();
+ }
+ SwSectionFormat *const pFormat = GetSectionFormat();
+ if (!pFormat && !m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ std::unique_ptr<SwSectionData> const pSectionData(
+ pFormat ? new SwSectionData(*pFormat->GetSection()) : nullptr);
+
+ OUString const*const pPropertyNames = rPropertyNames.getConstArray();
+ uno::Any const*const pValues = rValues.getConstArray();
+ std::optional<SfxItemSet> oItemSet;
+ bool bLinkModeChanged = false;
+ bool bLinkMode = false;
+
+ for (sal_Int32 nProperty = 0; nProperty < rPropertyNames.getLength();
+ nProperty++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_rPropSet.getPropertyMap().getByName(pPropertyNames[nProperty]);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pPropertyNames[nProperty],
+ static_cast<cppu::OWeakObject *>(& m_rThis));
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw beans::PropertyVetoException(
+ "Property is read-only: " + pPropertyNames[nProperty],
+ static_cast<cppu::OWeakObject *>(& m_rThis));
+ }
+ switch (pEntry->nWID)
+ {
+ case WID_SECT_CONDITION:
+ {
+ OUString uTmp;
+ pValues[nProperty] >>= uTmp;
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_sCondition = uTmp;
+ }
+ else
+ {
+ pSectionData->SetCondition(uTmp);
+ }
+ }
+ break;
+ case WID_SECT_DDE_TYPE:
+ case WID_SECT_DDE_FILE:
+ case WID_SECT_DDE_ELEMENT:
+ {
+ OUString sTmp;
+ pValues[nProperty] >>= sTmp;
+ if (m_bIsDescriptor)
+ {
+ if (!m_pProps->m_bDDE)
+ {
+ m_pProps->m_sLinkFileName =
+ OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator);
+ m_pProps->m_bDDE = true;
+ }
+ m_pProps->m_sLinkFileName = comphelper::string::setToken(
+ m_pProps->m_sLinkFileName,
+ pEntry->nWID - WID_SECT_DDE_TYPE, sfx2::cTokenSeparator, sTmp);
+ }
+ else
+ {
+ OUString sLinkFileName(pSectionData->GetLinkFileName());
+ if (pSectionData->GetType() != SectionType::DdeLink)
+ {
+ sLinkFileName = OUStringChar(sfx2::cTokenSeparator) + OUStringChar(sfx2::cTokenSeparator);
+ pSectionData->SetType(SectionType::DdeLink);
+ }
+ sLinkFileName = comphelper::string::setToken(sLinkFileName,
+ pEntry->nWID - WID_SECT_DDE_TYPE,
+ sfx2::cTokenSeparator, sTmp);
+ pSectionData->SetLinkFileName(sLinkFileName);
+ }
+ }
+ break;
+ case WID_SECT_DDE_AUTOUPDATE:
+ {
+ bool bVal(false);
+ if (!(pValues[nProperty] >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bUpdateType = bVal;
+ }
+ else
+ {
+ bLinkModeChanged = true;
+ bLinkMode = bVal;
+ }
+ }
+ break;
+ case WID_SECT_LINK:
+ {
+ text::SectionFileLink aLink;
+ if (!(pValues[nProperty] >>= aLink))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bDDE = false;
+ m_pProps->m_sLinkFileName = aLink.FileURL;
+ m_pProps->m_sSectionFilter = aLink.FilterName;
+ }
+ else
+ {
+ if (pSectionData->GetType() != SectionType::FileLink &&
+ !aLink.FileURL.isEmpty())
+ {
+ pSectionData->SetType(SectionType::FileLink);
+ }
+ const OUString sTmp(!aLink.FileURL.isEmpty()
+ ? URIHelper::SmartRel2Abs(
+ pFormat->GetDoc()->GetDocShell()->GetMedium()->GetURLObject(),
+ aLink.FileURL, URIHelper::GetMaybeFileHdl())
+ : OUString());
+ const OUString sFileName(
+ sTmp + OUStringChar(sfx2::cTokenSeparator) +
+ aLink.FilterName + OUStringChar(sfx2::cTokenSeparator) +
+ o3tl::getToken(pSectionData->GetLinkFileName(), 2, sfx2::cTokenSeparator));
+ pSectionData->SetLinkFileName(sFileName);
+ if (sFileName.getLength() < 3)
+ {
+ pSectionData->SetType(SectionType::Content);
+ }
+ }
+ }
+ break;
+ case WID_SECT_REGION:
+ {
+ OUString sLink;
+ pValues[nProperty] >>= sLink;
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bDDE = false;
+ m_pProps->m_sSectionRegion = sLink;
+ }
+ else
+ {
+ if (pSectionData->GetType() != SectionType::FileLink &&
+ !sLink.isEmpty())
+ {
+ pSectionData->SetType(SectionType::FileLink);
+ }
+ OUString sSectLink(pSectionData->GetLinkFileName());
+ for (sal_Int32 i = comphelper::string::getTokenCount(sSectLink, sfx2::cTokenSeparator);
+ i < 3; ++i)
+ {
+ sSectLink += OUStringChar(sfx2::cTokenSeparator);
+ }
+ sSectLink = comphelper::string::setToken(sSectLink, 2, sfx2::cTokenSeparator, sLink);
+ pSectionData->SetLinkFileName(sSectLink);
+ if (sSectLink.getLength() < 3)
+ {
+ pSectionData->SetType(SectionType::Content);
+ }
+ }
+ }
+ break;
+ case WID_SECT_VISIBLE:
+ {
+ bool bVal(false);
+ if (!(pValues[nProperty] >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bHidden = !bVal;
+ }
+ else
+ {
+ pSectionData->SetHidden(!bVal);
+ }
+ }
+ break;
+ case WID_SECT_CURRENTLY_VISIBLE:
+ {
+ bool bVal(false);
+ if (!(pValues[nProperty] >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bCondHidden = !bVal;
+ }
+ else
+ {
+ if (!pSectionData->GetCondition().isEmpty())
+ {
+ pSectionData->SetCondHidden(!bVal);
+ }
+ }
+ }
+ break;
+ case WID_SECT_PROTECTED:
+ {
+ bool bVal(false);
+ if (!(pValues[nProperty] >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bProtect = bVal;
+ }
+ else
+ {
+ pSectionData->SetProtectFlag(bVal);
+ }
+ }
+ break;
+ case WID_SECT_EDIT_IN_READONLY:
+ {
+ bool bVal(false);
+ if (!(pValues[nProperty] >>= bVal))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_bEditInReadonly = bVal;
+ }
+ else
+ {
+ pSectionData->SetEditInReadonlyFlag(bVal);
+ }
+ }
+ break;
+ case WID_SECT_PASSWORD:
+ {
+ uno::Sequence<sal_Int8> aSeq;
+ pValues[nProperty] >>= aSeq;
+ if (m_bIsDescriptor)
+ {
+ m_pProps->m_Password = aSeq;
+ }
+ else
+ {
+ pSectionData->SetPassword(aSeq);
+ }
+ }
+ break;
+ default:
+ {
+ if (pFormat)
+ {
+ const SfxItemSet& rOldAttrSet = pFormat->GetAttrSet();
+ oItemSet.emplace(*rOldAttrSet.GetPool(), pEntry->nWID, pEntry->nWID);
+ oItemSet->Put(rOldAttrSet);
+ m_rPropSet.setPropertyValue(*pEntry,
+ pValues[nProperty], *oItemSet);
+ }
+ else
+ {
+ SfxPoolItem* pPutItem = nullptr;
+ if (RES_COL == pEntry->nWID)
+ {
+ if (!m_pProps->m_pColItem)
+ {
+ m_pProps->m_pColItem.reset(new SwFormatCol);
+ }
+ pPutItem = m_pProps->m_pColItem.get();
+ }
+ else if (RES_BACKGROUND == pEntry->nWID)
+ {
+ if (!m_pProps->m_pBrushItem)
+ {
+ m_pProps->m_pBrushItem.reset(
+ new SvxBrushItem(RES_BACKGROUND));
+ }
+ pPutItem = m_pProps->m_pBrushItem.get();
+ }
+ else if (RES_FTN_AT_TXTEND == pEntry->nWID)
+ {
+ if (!m_pProps->m_pFootnoteItem)
+ {
+ m_pProps->m_pFootnoteItem.reset(new SwFormatFootnoteAtTextEnd);
+ }
+ pPutItem = m_pProps->m_pFootnoteItem.get();
+ }
+ else if (RES_END_AT_TXTEND == pEntry->nWID)
+ {
+ if (!m_pProps->m_pEndItem)
+ {
+ m_pProps->m_pEndItem.reset(new SwFormatEndAtTextEnd);
+ }
+ pPutItem = m_pProps->m_pEndItem.get();
+ }
+ else if (RES_UNKNOWNATR_CONTAINER== pEntry->nWID)
+ {
+ if (!m_pProps->m_pXMLAttr)
+ {
+ m_pProps->m_pXMLAttr.reset(
+ new SvXMLAttrContainerItem(
+ RES_UNKNOWNATR_CONTAINER));
+ }
+ pPutItem = m_pProps->m_pXMLAttr.get();
+ }
+ else if (RES_COLUMNBALANCE== pEntry->nWID)
+ {
+ if (!m_pProps->m_pNoBalanceItem)
+ {
+ m_pProps->m_pNoBalanceItem.reset(
+ new SwFormatNoBalancedColumns(true));
+ }
+ pPutItem = m_pProps->m_pNoBalanceItem.get();
+ }
+ else if (RES_FRAMEDIR == pEntry->nWID)
+ {
+ if (!m_pProps->m_pFrameDirItem)
+ {
+ m_pProps->m_pFrameDirItem.reset(
+ new SvxFrameDirectionItem(
+ SvxFrameDirection::Horizontal_LR_TB, RES_FRAMEDIR));
+ }
+ pPutItem = m_pProps->m_pFrameDirItem.get();
+ }
+ else if (RES_LR_SPACE == pEntry->nWID)
+ {
+ if (!m_pProps->m_pLRSpaceItem)
+ {
+ m_pProps->m_pLRSpaceItem.reset(
+ new SvxLRSpaceItem( RES_LR_SPACE ));
+ }
+ pPutItem = m_pProps->m_pLRSpaceItem.get();
+ }
+ if (pPutItem)
+ {
+ pPutItem->PutValue(pValues[nProperty],
+ pEntry->nMemberId);
+ }
+ }
+ }
+ }
+ }
+
+ lcl_UpdateSection(pFormat, pSectionData, oItemSet, bLinkModeChanged,
+ bLinkMode);
+}
+
+void SAL_CALL
+SwXTextSection::setPropertyValues(
+ const uno::Sequence< OUString >& rPropertyNames,
+ const uno::Sequence< uno::Any >& rValues)
+{
+ SolarMutexGuard aGuard;
+
+ // workaround for bad designed API
+ try
+ {
+ m_pImpl->SetPropertyValues_Impl( rPropertyNames, rValues );
+ }
+ catch (const beans::UnknownPropertyException &rException)
+ {
+ // wrap the original (here not allowed) exception in
+ // a WrappedTargetException that gets thrown instead.
+ lang::WrappedTargetException aWExc;
+ aWExc.TargetException <<= rException;
+ throw aWExc;
+ }
+}
+
+void SwXTextSection::setPropertyValue(
+ const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+
+ m_pImpl->SetPropertyValues_Impl( { rPropertyName } , { rValue } );
+}
+
+uno::Sequence< uno::Any >
+SwXTextSection::Impl::GetPropertyValues_Impl(
+ const uno::Sequence< OUString > & rPropertyNames )
+{
+ SwSectionFormat *const pFormat = GetSectionFormat();
+ if (!pFormat && !m_bIsDescriptor)
+ {
+ throw uno::RuntimeException( "non-descriptor section without format");
+ }
+
+ uno::Sequence< uno::Any > aRet(rPropertyNames.getLength());
+ uno::Any* pRet = aRet.getArray();
+ SwSection *const pSect = pFormat ? pFormat->GetSection() : nullptr;
+ const OUString* pPropertyNames = rPropertyNames.getConstArray();
+
+ for (sal_Int32 nProperty = 0; nProperty < rPropertyNames.getLength();
+ nProperty++)
+ {
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_rPropSet.getPropertyMap().getByName(pPropertyNames[nProperty]);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pPropertyNames[nProperty],
+ static_cast<cppu::OWeakObject *>(& m_rThis));
+ }
+ switch(pEntry->nWID)
+ {
+ case WID_SECT_CONDITION:
+ {
+ const OUString uTmp( m_bIsDescriptor
+ ? m_pProps->m_sCondition
+ : pSect->GetCondition());
+ pRet[nProperty] <<= uTmp;
+ }
+ break;
+ case WID_SECT_DDE_TYPE:
+ case WID_SECT_DDE_FILE:
+ case WID_SECT_DDE_ELEMENT:
+ {
+ OUString sRet;
+ if (m_bIsDescriptor)
+ {
+ if (m_pProps->m_bDDE)
+ {
+ sRet = m_pProps->m_sLinkFileName;
+ }
+ }
+ else if (SectionType::DdeLink == pSect->GetType())
+ {
+ sRet = pSect->GetLinkFileName();
+ }
+ pRet[nProperty] <<= sRet.getToken(pEntry->nWID - WID_SECT_DDE_TYPE,
+ sfx2::cTokenSeparator);
+ }
+ break;
+ case WID_SECT_DDE_AUTOUPDATE:
+ {
+ // GetUpdateType() returns .._ALWAYS or .._ONCALL
+ if (pSect && pSect->IsLinkType() && pSect->IsConnected()) // #i73247#
+ {
+ const bool bTemp =
+ (pSect->GetUpdateType() == SfxLinkUpdateMode::ALWAYS);
+ pRet[nProperty] <<= bTemp;
+ }
+ }
+ break;
+ case WID_SECT_LINK :
+ {
+ text::SectionFileLink aLink;
+ if (m_bIsDescriptor)
+ {
+ if (!m_pProps->m_bDDE)
+ {
+ aLink.FileURL = m_pProps->m_sLinkFileName;
+ aLink.FilterName = m_pProps->m_sSectionFilter;
+ }
+ }
+ else if (SectionType::FileLink == pSect->GetType())
+ {
+ const OUString& sRet( pSect->GetLinkFileName() );
+ sal_Int32 nIndex(0);
+ aLink.FileURL =
+ sRet.getToken(0, sfx2::cTokenSeparator, nIndex);
+ aLink.FilterName =
+ sRet.getToken(0, sfx2::cTokenSeparator, nIndex);
+ }
+ pRet[nProperty] <<= aLink;
+ }
+ break;
+ case WID_SECT_REGION :
+ {
+ OUString sRet;
+ if (m_bIsDescriptor)
+ {
+ sRet = m_pProps->m_sSectionRegion;
+ }
+ else if (SectionType::FileLink == pSect->GetType())
+ {
+ sRet = pSect->GetLinkFileName().getToken(2,
+ sfx2::cTokenSeparator);
+ }
+ pRet[nProperty] <<= sRet;
+ }
+ break;
+ case WID_SECT_VISIBLE :
+ {
+ const bool bTemp = m_bIsDescriptor
+ ? !m_pProps->m_bHidden : !pSect->IsHidden();
+ pRet[nProperty] <<= bTemp;
+ }
+ break;
+ case WID_SECT_CURRENTLY_VISIBLE:
+ {
+ const bool bTemp = m_bIsDescriptor
+ ? !m_pProps->m_bCondHidden : !pSect->IsCondHidden();
+ pRet[nProperty] <<= bTemp;
+ }
+ break;
+ case WID_SECT_PROTECTED:
+ {
+ const bool bTemp = m_bIsDescriptor
+ ? m_pProps->m_bProtect : pSect->IsProtect();
+ pRet[nProperty] <<= bTemp;
+ }
+ break;
+ case WID_SECT_EDIT_IN_READONLY:
+ {
+ const bool bTemp = m_bIsDescriptor
+ ? m_pProps->m_bEditInReadonly : pSect->IsEditInReadonly();
+ pRet[nProperty] <<= bTemp;
+ }
+ break;
+ case FN_PARAM_LINK_DISPLAY_NAME:
+ {
+ if (pFormat)
+ {
+ pRet[nProperty] <<= pFormat->GetSection()->GetSectionName();
+ }
+ }
+ break;
+ case WID_SECT_DOCUMENT_INDEX:
+ {
+ // search enclosing index
+ SwSection* pEnclosingSection = pSect;
+ while ((pEnclosingSection != nullptr) &&
+ (SectionType::ToxContent != pEnclosingSection->GetType()))
+ {
+ pEnclosingSection = pEnclosingSection->GetParent();
+ }
+ SwTOXBaseSection* const pTOXBaseSect = pEnclosingSection ?
+ dynamic_cast<SwTOXBaseSection*>( pEnclosingSection ) : nullptr;
+ if (pTOXBaseSect)
+ {
+ // convert section to TOXBase and get SwXDocumentIndex
+ const uno::Reference<text::XDocumentIndex> xIndex =
+ SwXDocumentIndex::CreateXDocumentIndex(
+ *pTOXBaseSect->GetFormat()->GetDoc(), pTOXBaseSect);
+ pRet[nProperty] <<= xIndex;
+ }
+ // else: no enclosing index found -> empty return value
+ }
+ break;
+ case WID_SECT_IS_GLOBAL_DOC_SECTION:
+ {
+ const bool bRet = pFormat && (nullptr != pFormat->GetGlobalDocSection());
+ pRet[nProperty] <<= bRet;
+ }
+ break;
+ case FN_UNO_ANCHOR_TYPES:
+ case FN_UNO_TEXT_WRAP:
+ case FN_UNO_ANCHOR_TYPE:
+ ::sw::GetDefaultTextContentValue(
+ pRet[nProperty], u"", pEntry->nWID);
+ break;
+ case FN_UNO_REDLINE_NODE_START:
+ case FN_UNO_REDLINE_NODE_END:
+ {
+ if (!pFormat)
+ break; // #i73247#
+ SwNode* pSectNode = pFormat->GetSectionNode();
+ if (FN_UNO_REDLINE_NODE_END == pEntry->nWID)
+ {
+ pSectNode = pSectNode->EndOfSectionNode();
+ }
+ const SwRedlineTable& rRedTable =
+ pFormat->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ for (SwRangeRedline* pRedline : rRedTable)
+ {
+ const SwNode& rRedPointNode = pRedline->GetNode();
+ const SwNode& rRedMarkNode = pRedline->GetNode(false);
+ if ((&rRedPointNode == pSectNode) ||
+ (&rRedMarkNode == pSectNode))
+ {
+ const SwNode& rStartOfRedline =
+ (SwNodeIndex(rRedPointNode) <=
+ SwNodeIndex(rRedMarkNode))
+ ? rRedPointNode : rRedMarkNode;
+ const bool bIsStart = (&rStartOfRedline == pSectNode);
+ pRet[nProperty] <<=
+ SwXRedlinePortion::CreateRedlineProperties(
+ *pRedline, bIsStart);
+ break;
+ }
+ }
+ }
+ break;
+ case WID_SECT_PASSWORD:
+ {
+ pRet[nProperty] <<= m_bIsDescriptor
+ ? m_pProps->m_Password : pSect->GetPassword();
+ }
+ break;
+ default:
+ {
+ if (pFormat)
+ {
+ m_rPropSet.getPropertyValue(*pEntry,
+ pFormat->GetAttrSet(), pRet[nProperty]);
+ }
+ else
+ {
+ const SfxPoolItem* pQueryItem = nullptr;
+ if (RES_COL == pEntry->nWID)
+ {
+ if (!m_pProps->m_pColItem)
+ {
+ m_pProps->m_pColItem.reset(new SwFormatCol);
+ }
+ pQueryItem = m_pProps->m_pColItem.get();
+ }
+ else if (RES_BACKGROUND == pEntry->nWID)
+ {
+ if (!m_pProps->m_pBrushItem)
+ {
+ m_pProps->m_pBrushItem.reset(
+ new SvxBrushItem(RES_BACKGROUND));
+ }
+ pQueryItem = m_pProps->m_pBrushItem.get();
+ }
+ else if (RES_FTN_AT_TXTEND == pEntry->nWID)
+ {
+ if (!m_pProps->m_pFootnoteItem)
+ {
+ m_pProps->m_pFootnoteItem.reset(new SwFormatFootnoteAtTextEnd);
+ }
+ pQueryItem = m_pProps->m_pFootnoteItem.get();
+ }
+ else if (RES_END_AT_TXTEND == pEntry->nWID)
+ {
+ if (!m_pProps->m_pEndItem)
+ {
+ m_pProps->m_pEndItem.reset(new SwFormatEndAtTextEnd);
+ }
+ pQueryItem = m_pProps->m_pEndItem.get();
+ }
+ else if (RES_UNKNOWNATR_CONTAINER== pEntry->nWID)
+ {
+ if (!m_pProps->m_pXMLAttr)
+ {
+ m_pProps->m_pXMLAttr.reset(
+ new SvXMLAttrContainerItem);
+ }
+ pQueryItem = m_pProps->m_pXMLAttr.get();
+ }
+ else if (RES_COLUMNBALANCE== pEntry->nWID)
+ {
+ if (!m_pProps->m_pNoBalanceItem)
+ {
+ m_pProps->m_pNoBalanceItem.reset(
+ new SwFormatNoBalancedColumns);
+ }
+ pQueryItem = m_pProps->m_pNoBalanceItem.get();
+ }
+ else if (RES_FRAMEDIR == pEntry->nWID)
+ {
+ if (!m_pProps->m_pFrameDirItem)
+ {
+ m_pProps->m_pFrameDirItem.reset(
+ new SvxFrameDirectionItem(
+ SvxFrameDirection::Environment, RES_FRAMEDIR));
+ }
+ pQueryItem = m_pProps->m_pFrameDirItem.get();
+ }
+ else if (RES_LR_SPACE == pEntry->nWID)
+ {
+ if (!m_pProps->m_pLRSpaceItem)
+ {
+ m_pProps->m_pLRSpaceItem.reset(
+ new SvxLRSpaceItem( RES_LR_SPACE ));
+ }
+ pQueryItem = m_pProps->m_pLRSpaceItem.get();
+ }
+ if (pQueryItem)
+ {
+ pQueryItem->QueryValue(pRet[nProperty],
+ pEntry->nMemberId);
+ }
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+uno::Sequence< uno::Any > SAL_CALL
+SwXTextSection::getPropertyValues(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< uno::Any > aValues;
+
+ // workaround for bad designed API
+ try
+ {
+ aValues = m_pImpl->GetPropertyValues_Impl( rPropertyNames );
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("Unknown property exception caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("WrappedTargetException caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+
+ return aValues;
+}
+
+uno::Any SAL_CALL
+SwXTextSection::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > aPropertyNames { rPropertyName };
+ return m_pImpl->GetPropertyValues_Impl(aPropertyNames).getConstArray()[0];
+}
+
+void SAL_CALL SwXTextSection::addPropertiesChangeListener(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL("SwXTextSection::addPropertiesChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXTextSection::removePropertiesChangeListener(
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL("SwXTextSection::removePropertiesChangeListener(): not implemented");
+}
+
+void SAL_CALL SwXTextSection::firePropertiesChangeEvent(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+ OSL_FAIL("SwXTextSection::firePropertiesChangeEvent(): not implemented");
+}
+
+void SAL_CALL
+SwXTextSection::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextSection::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextSection::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextSection::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextSection::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextSection::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXTextSection::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXTextSection::removeVetoableChangeListener(): not implemented");
+}
+
+beans::PropertyState SAL_CALL
+SwXTextSection::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > aNames { rPropertyName };
+ return getPropertyStates(aNames).getConstArray()[0];
+}
+
+uno::Sequence< beans::PropertyState > SAL_CALL
+SwXTextSection::getPropertyStates(
+ const uno::Sequence< OUString >& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
+ if (!pFormat && !m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ uno::Sequence< beans::PropertyState > aStates(rPropertyNames.getLength());
+ beans::PropertyState *const pStates = aStates.getArray();
+ const OUString* pNames = rPropertyNames.getConstArray();
+ for (sal_Int32 i = 0; i < rPropertyNames.getLength(); i++)
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName( pNames[i]);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + pNames[i],
+ static_cast< cppu::OWeakObject* >(this));
+ }
+ switch (pEntry->nWID)
+ {
+ case WID_SECT_CONDITION:
+ case WID_SECT_DDE_TYPE:
+ case WID_SECT_DDE_FILE:
+ case WID_SECT_DDE_ELEMENT:
+ case WID_SECT_DDE_AUTOUPDATE:
+ case WID_SECT_LINK:
+ case WID_SECT_REGION :
+ case WID_SECT_VISIBLE:
+ case WID_SECT_PROTECTED:
+ case WID_SECT_EDIT_IN_READONLY:
+ case FN_PARAM_LINK_DISPLAY_NAME:
+ case FN_UNO_ANCHOR_TYPES:
+ case FN_UNO_TEXT_WRAP:
+ case FN_UNO_ANCHOR_TYPE:
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ break;
+ default:
+ {
+ if (pFormat)
+ {
+ pStates[i] = m_pImpl->m_rPropSet.getPropertyState(
+ pNames[i], pFormat->GetAttrSet());
+ }
+ else
+ {
+ if (RES_COL == pEntry->nWID)
+ {
+ if (!m_pImpl->m_pProps->m_pColItem)
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ else
+ {
+ if (!m_pImpl->m_pProps->m_pBrushItem)
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ return aStates;
+}
+
+void SAL_CALL
+SwXTextSection::setPropertyToDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
+ if (!pFormat && !m_pImpl->m_bIsDescriptor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast< cppu::OWeakObject* >(this));
+ }
+ if (pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ {
+ throw uno::RuntimeException(
+ "Property is read-only: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ std::unique_ptr<SwSectionData> const pSectionData(
+ pFormat ? new SwSectionData(*pFormat->GetSection()) : nullptr);
+
+ std::optional<SfxItemSet> oNewAttrSet;
+ bool bLinkModeChanged = false;
+
+ switch (pEntry->nWID)
+ {
+ case WID_SECT_CONDITION:
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->m_sCondition.clear();
+ }
+ else
+ {
+ pSectionData->SetCondition(OUString());
+ }
+ }
+ break;
+ case WID_SECT_DDE_TYPE :
+ case WID_SECT_DDE_FILE :
+ case WID_SECT_DDE_ELEMENT :
+ case WID_SECT_LINK :
+ case WID_SECT_REGION :
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->m_bDDE = false;
+ m_pImpl->m_pProps->m_sLinkFileName.clear();
+ m_pImpl->m_pProps->m_sSectionRegion.clear();
+ m_pImpl->m_pProps->m_sSectionFilter.clear();
+ }
+ else
+ {
+ pSectionData->SetType(SectionType::Content);
+ }
+ break;
+ case WID_SECT_DDE_AUTOUPDATE:
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->m_bUpdateType = true;
+ }
+ else
+ {
+ bLinkModeChanged = true;
+ }
+ break;
+ case WID_SECT_VISIBLE :
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->m_bHidden = false;
+ }
+ else
+ {
+ pSectionData->SetHidden(false);
+ }
+ }
+ break;
+ case WID_SECT_PROTECTED:
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->m_bProtect = false;
+ }
+ else
+ {
+ pSectionData->SetProtectFlag(false);
+ }
+ }
+ break;
+ case WID_SECT_EDIT_IN_READONLY:
+ {
+ if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_pProps->m_bEditInReadonly = false;
+ }
+ else
+ {
+ pSectionData->SetEditInReadonlyFlag(false);
+ }
+ }
+ break;
+
+ case FN_UNO_ANCHOR_TYPES:
+ case FN_UNO_TEXT_WRAP:
+ case FN_UNO_ANCHOR_TYPE:
+ break;
+ default:
+ {
+ if (SfxItemPool::IsWhich(pEntry->nWID))
+ {
+ if (pFormat)
+ {
+ const SfxItemSet& rOldAttrSet = pFormat->GetAttrSet();
+ oNewAttrSet.emplace(*rOldAttrSet.GetPool(), pEntry->nWID, pEntry->nWID);
+ oNewAttrSet->ClearItem(pEntry->nWID);
+ }
+ else
+ {
+ if (RES_COL == pEntry->nWID)
+ {
+ m_pImpl->m_pProps->m_pColItem.reset();
+ }
+ else if (RES_BACKGROUND == pEntry->nWID)
+ {
+ m_pImpl->m_pProps->m_pBrushItem.reset();
+ }
+ }
+ }
+ }
+ }
+
+ lcl_UpdateSection(pFormat, pSectionData, oNewAttrSet, bLinkModeChanged);
+}
+
+uno::Any SAL_CALL
+SwXTextSection::getPropertyDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Any aRet;
+ SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ throw beans::UnknownPropertyException(
+ "Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject *>(this));
+ }
+
+ switch(pEntry->nWID)
+ {
+ case WID_SECT_CONDITION:
+ case WID_SECT_DDE_TYPE :
+ case WID_SECT_DDE_FILE :
+ case WID_SECT_DDE_ELEMENT :
+ case WID_SECT_REGION :
+ case FN_PARAM_LINK_DISPLAY_NAME:
+ aRet <<= OUString();
+ break;
+ case WID_SECT_LINK :
+ aRet <<= text::SectionFileLink();
+ break;
+ case WID_SECT_DDE_AUTOUPDATE:
+ case WID_SECT_VISIBLE :
+ aRet <<= true;
+ break;
+ case WID_SECT_PROTECTED:
+ case WID_SECT_EDIT_IN_READONLY:
+ aRet <<= false;
+ break;
+ case FN_UNO_ANCHOR_TYPES:
+ case FN_UNO_TEXT_WRAP:
+ case FN_UNO_ANCHOR_TYPE:
+ ::sw::GetDefaultTextContentValue(aRet, u"", pEntry->nWID);
+ break;
+ default:
+ if(pFormat && SfxItemPool::IsWhich(pEntry->nWID))
+ {
+ SwDoc *const pDoc = pFormat->GetDoc();
+ const SfxPoolItem& rDefItem =
+ pDoc->GetAttrPool().GetDefaultItem(pEntry->nWID);
+ rDefItem.QueryValue(aRet, pEntry->nMemberId);
+ }
+ }
+ return aRet;
+}
+
+OUString SAL_CALL SwXTextSection::getName()
+{
+ SolarMutexGuard aGuard;
+
+ OUString sRet;
+ SwSectionFormat const*const pFormat = m_pImpl->GetSectionFormat();
+ if(pFormat)
+ {
+ sRet = pFormat->GetSection()->GetSectionName();
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ sRet = m_pImpl->m_sName;
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+ return sRet;
+}
+
+void SAL_CALL SwXTextSection::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+
+ SwSectionFormat *const pFormat = m_pImpl->GetSectionFormat();
+ if(pFormat)
+ {
+ SwSection *const pSect = pFormat->GetSection();
+ SwSectionData aSection(*pSect);
+ aSection.SetSectionName(rName);
+
+ const SwSectionFormats& rFormats = pFormat->GetDoc()->GetSections();
+ size_t nApplyPos = SIZE_MAX;
+ for( size_t i = 0; i < rFormats.size(); ++i )
+ {
+ if(rFormats[i]->GetSection() == pSect)
+ {
+ nApplyPos = i;
+ }
+ else if (rName == rFormats[i]->GetSection()->GetSectionName())
+ {
+ throw uno::RuntimeException();
+ }
+ }
+ if (nApplyPos != SIZE_MAX)
+ {
+ {
+ UnoActionContext aContext(pFormat->GetDoc());
+ pFormat->GetDoc()->UpdateSection(nApplyPos, aSection);
+ }
+ {
+ // temporarily remove actions to allow cursor update
+ // TODO: why? no table cursor here!
+ UnoActionRemoveContext aRemoveContext( pFormat->GetDoc() );
+ }
+ }
+ }
+ else if (m_pImpl->m_bIsDescriptor)
+ {
+ m_pImpl->m_sName = rName;
+ }
+ else
+ {
+ throw uno::RuntimeException();
+ }
+}
+
+OUString SAL_CALL
+SwXTextSection::getImplementationName()
+{
+ return "SwXTextSection";
+}
+
+sal_Bool SAL_CALL SwXTextSection::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXTextSection::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.TextSection",
+ "com.sun.star.document.LinkTarget"
+ };
+}
+
+// MetadatableMixin
+::sfx2::Metadatable* SwXTextSection::GetCoreObject()
+{
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ return pSectionFormat;
+}
+
+uno::Reference<frame::XModel> SwXTextSection::GetModel()
+{
+ SwSectionFormat *const pSectionFormat( m_pImpl->GetSectionFormat() );
+ if (pSectionFormat)
+ {
+ SwDocShell const*const pShell( pSectionFormat->GetDoc()->GetDocShell() );
+ return pShell ? pShell->GetModel() : nullptr;
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unosett.cxx b/sw/source/core/unocore/unosett.cxx
new file mode 100644
index 000000000..5a94663ff
--- /dev/null
+++ b/sw/source/core/unocore/unosett.cxx
@@ -0,0 +1,2143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <editeng/editids.hrc>
+#include <swtypes.hxx>
+#include <unomid.h>
+#include <hintids.hxx>
+#include <strings.hrc>
+#include <poolfmt.hxx>
+#include <fmtcol.hxx>
+#include <unomap.hxx>
+#include <unosett.hxx>
+#include <unoprnms.hxx>
+#include <ftninfo.hxx>
+#include <doc.hxx>
+#include <pagedesc.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <charfmt.hxx>
+#include <lineinfo.hxx>
+#include <docsh.hxx>
+#include <docary.hxx>
+#include <docstyle.hxx>
+#include <editeng/brushitem.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/FootnoteNumbering.hpp>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/style/LineNumberPosition.hpp>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <o3tl/any.hxx>
+#include <o3tl/enumarray.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/font.hxx>
+#include <editeng/flstitem.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <sfx2/docfile.hxx>
+#include <svtools/ctrltool.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/unofdesc.hxx>
+#include <fmtornt.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <com/sun/star/text/PositionAndSpaceMode.hpp>
+#include <com/sun/star/text/LabelFollow.hpp>
+#include <numrule.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/listener.hxx>
+#include <paratr.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::style;
+
+
+namespace
+{
+ SvtBroadcaster& GetPageDescNotifier(SwDoc* pDoc)
+ {
+ return pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier();
+ }
+}
+
+#define WID_PREFIX 0
+#define WID_SUFFIX 1
+#define WID_NUMBERING_TYPE 2
+#define WID_START_AT 3
+#define WID_FOOTNOTE_COUNTING 4
+#define WID_PARAGRAPH_STYLE 5
+#define WID_PAGE_STYLE 6
+#define WID_CHARACTER_STYLE 7
+#define WID_POSITION_END_OF_DOC 8
+#define WID_END_NOTICE 9
+#define WID_BEGIN_NOTICE 10
+#define WID_ANCHOR_CHARACTER_STYLE 11
+#define WID_NUM_ON 12
+#define WID_SEPARATOR_INTERVAL 13
+#define WID_NUMBER_POSITION 14
+#define WID_DISTANCE 15
+#define WID_INTERVAL 16
+#define WID_SEPARATOR_TEXT 17
+#define WID_COUNT_EMPTY_LINES 18
+#define WID_COUNT_LINES_IN_FRAMES 19
+#define WID_RESTART_AT_EACH_PAGE 20
+
+
+static const SfxItemPropertySet* GetFootnoteSet()
+{
+ static const SfxItemPropertyMapEntry aFootnoteMap_Impl[] =
+ {
+ { u"" UNO_NAME_ANCHOR_CHAR_STYLE_NAME,WID_ANCHOR_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_BEGIN_NOTICE, WID_BEGIN_NOTICE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_STYLE_NAME, WID_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_END_NOTICE, WID_END_NOTICE , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_FOOTNOTE_COUNTING, WID_FOOTNOTE_COUNTING, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_NUMBERING_TYPE, WID_NUMBERING_TYPE, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PAGE_STYLE_NAME, WID_PAGE_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PARA_STYLE_NAME, WID_PARAGRAPH_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_POSITION_END_OF_DOC, WID_POSITION_END_OF_DOC,cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PREFIX, WID_PREFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_START_AT, WID_START_AT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SUFFIX, WID_SUFFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static const SfxItemPropertySet aFootnoteSet_Impl(aFootnoteMap_Impl);
+ return &aFootnoteSet_Impl;
+}
+
+static const SfxItemPropertySet* GetEndnoteSet()
+{
+ static const SfxItemPropertyMapEntry aEndnoteMap_Impl[] =
+ {
+ { u"" UNO_NAME_ANCHOR_CHAR_STYLE_NAME,WID_ANCHOR_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_CHAR_STYLE_NAME, WID_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_NUMBERING_TYPE, WID_NUMBERING_TYPE, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PAGE_STYLE_NAME, WID_PAGE_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PARA_STYLE_NAME, WID_PARAGRAPH_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_PREFIX, WID_PREFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_START_AT, WID_START_AT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SUFFIX, WID_SUFFIX, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static const SfxItemPropertySet aEndnoteSet_Impl(aEndnoteMap_Impl);
+ return &aEndnoteSet_Impl;
+}
+
+static const SfxItemPropertySet* GetNumberingRulesSet()
+{
+ static const SfxItemPropertyMapEntry aNumberingRulesMap_Impl[] =
+ {
+ { u"" UNO_NAME_IS_ABSOLUTE_MARGINS, WID_IS_ABS_MARGINS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_AUTOMATIC, WID_IS_AUTOMATIC, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_CONTINUOUS_NUMBERING, WID_CONTINUOUS, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_NAME, WID_RULE_NAME , ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"" UNO_NAME_NUMBERING_IS_OUTLINE, WID_IS_OUTLINE, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DEFAULT_LIST_ID, WID_DEFAULT_LIST_ID, ::cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static const SfxItemPropertySet aNumberingRulesSet_Impl( aNumberingRulesMap_Impl );
+ return &aNumberingRulesSet_Impl;
+}
+
+static const SfxItemPropertySet* GetLineNumberingSet()
+{
+ static const SfxItemPropertyMapEntry aLineNumberingMap_Impl[] =
+ {
+ { u"" UNO_NAME_CHAR_STYLE_NAME, WID_CHARACTER_STYLE, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_COUNT_EMPTY_LINES, WID_COUNT_EMPTY_LINES , cppu::UnoType<bool>::get(),PROPERTY_NONE, 0},
+ { u"" UNO_NAME_COUNT_LINES_IN_FRAMES, WID_COUNT_LINES_IN_FRAMES, cppu::UnoType<bool>::get(),PROPERTY_NONE, 0},
+ { u"" UNO_NAME_DISTANCE, WID_DISTANCE , ::cppu::UnoType<sal_Int32>::get(),PROPERTY_NONE, 0},
+ { u"" UNO_NAME_IS_ON, WID_NUM_ON, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_INTERVAL, WID_INTERVAL , ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEPARATOR_TEXT, WID_SEPARATOR_TEXT, ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0},
+ { u"" UNO_NAME_NUMBER_POSITION, WID_NUMBER_POSITION, ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0},
+ { u"" UNO_NAME_NUMBERING_TYPE, WID_NUMBERING_TYPE , ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0},
+ { u"" UNO_NAME_RESTART_AT_EACH_PAGE, WID_RESTART_AT_EACH_PAGE, cppu::UnoType<bool>::get() , PROPERTY_NONE, 0},
+ { u"" UNO_NAME_SEPARATOR_INTERVAL, WID_SEPARATOR_INTERVAL, ::cppu::UnoType<sal_Int16>::get(),PROPERTY_NONE, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ static const SfxItemPropertySet aLineNumberingSet_Impl(aLineNumberingMap_Impl);
+ return &aLineNumberingSet_Impl;
+}
+
+static SwCharFormat* lcl_getCharFormat(SwDoc* pDoc, const uno::Any& aValue)
+{
+ SwCharFormat* pRet = nullptr;
+ OUString uTmp;
+ aValue >>= uTmp;
+ OUString sCharFormat;
+ SwStyleNameMapper::FillUIName(uTmp, sCharFormat, SwGetPoolIdFromName::ChrFmt);
+ if (sCharFormat != SwResId(STR_POOLCHR_STANDARD))
+ {
+ pRet = pDoc->FindCharFormatByName( sCharFormat );
+ }
+ if(!pRet)
+ {
+ const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(sCharFormat, SwGetPoolIdFromName::ChrFmt);
+ if(USHRT_MAX != nId)
+ pRet = pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( nId );
+ }
+ return pRet;
+}
+
+static SwTextFormatColl* lcl_GetParaStyle(SwDoc* pDoc, const uno::Any& aValue)
+{
+ OUString uTmp;
+ aValue >>= uTmp;
+ OUString sParaStyle;
+ SwStyleNameMapper::FillUIName(uTmp, sParaStyle, SwGetPoolIdFromName::TxtColl );
+ SwTextFormatColl* pRet = pDoc->FindTextFormatCollByName( sParaStyle );
+ if( !pRet )
+ {
+ const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName( sParaStyle, SwGetPoolIdFromName::TxtColl );
+ if( USHRT_MAX != nId )
+ pRet = pDoc->getIDocumentStylePoolAccess().GetTextCollFromPool( nId );
+ }
+ return pRet;
+}
+
+static SwPageDesc* lcl_GetPageDesc(SwDoc* pDoc, const uno::Any& aValue)
+{
+ OUString uTmp;
+ aValue >>= uTmp;
+ OUString sPageDesc;
+ SwStyleNameMapper::FillUIName(uTmp, sPageDesc, SwGetPoolIdFromName::PageDesc );
+ SwPageDesc* pRet = pDoc->FindPageDesc( sPageDesc );
+ if(!pRet)
+ {
+ const sal_uInt16 nId = SwStyleNameMapper::GetPoolIdFromUIName(sPageDesc, SwGetPoolIdFromName::PageDesc);
+ if(USHRT_MAX != nId)
+ pRet = pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool( nId );
+ }
+ return pRet;
+}
+
+// Numbering
+const o3tl::enumarray<SvxAdjust, unsigned short> aSvxToUnoAdjust
+{
+ text::HoriOrientation::LEFT, //3
+ text::HoriOrientation::RIGHT, //1
+ USHRT_MAX,
+ text::HoriOrientation::CENTER, //2
+ USHRT_MAX,
+ USHRT_MAX
+};
+
+const unsigned short aUnoToSvxAdjust[] =
+{
+ USHRT_MAX,
+ static_cast<unsigned short>(SvxAdjust::Right), // 1
+ static_cast<unsigned short>(SvxAdjust::Center), // 3
+ static_cast<unsigned short>(SvxAdjust::Left), // 0
+ USHRT_MAX,
+ USHRT_MAX
+};
+
+OUString SwXFootnoteProperties::getImplementationName()
+{
+ return "SwXFootnoteProperties";
+}
+
+sal_Bool SwXFootnoteProperties::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXFootnoteProperties::getSupportedServiceNames()
+{
+ Sequence<OUString> aRet { "com.sun.star.text.FootnoteSettings" };
+ return aRet;
+}
+
+SwXFootnoteProperties::SwXFootnoteProperties(SwDoc* pDc) :
+ m_pDoc(pDc),
+ m_pPropertySet(GetFootnoteSet())
+{
+}
+
+SwXFootnoteProperties::~SwXFootnoteProperties()
+{
+
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXFootnoteProperties::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef = m_pPropertySet->getPropertySetInfo();
+ return aRef;
+}
+
+void SwXFootnoteProperties::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropertySet->getPropertyMap().getByName( rPropertyName );
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if ( pEntry->nFlags & PropertyAttribute::READONLY)
+ throw PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ SwFootnoteInfo aFootnoteInfo(m_pDoc->GetFootnoteInfo());
+ switch(pEntry->nWID)
+ {
+ case WID_PREFIX:
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aFootnoteInfo.SetPrefix(uTmp);
+ }
+ break;
+ case WID_SUFFIX:
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aFootnoteInfo.SetSuffix(uTmp);
+ }
+ break;
+ case WID_NUMBERING_TYPE:
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ if(!(nTmp >= 0 &&
+ (nTmp <= SVX_NUM_ARABIC ||
+ nTmp > SVX_NUM_BITMAP)))
+ throw lang::IllegalArgumentException();
+
+ aFootnoteInfo.m_aFormat.SetNumberingType(static_cast<SvxNumType>(nTmp));
+
+ }
+ break;
+ case WID_START_AT:
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ aFootnoteInfo.m_nFootnoteOffset = nTmp;
+ }
+ break;
+ case WID_FOOTNOTE_COUNTING:
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ switch(nTmp)
+ {
+ case FootnoteNumbering::PER_PAGE:
+ aFootnoteInfo.m_eNum = FTNNUM_PAGE;
+ break;
+ case FootnoteNumbering::PER_CHAPTER:
+ aFootnoteInfo.m_eNum = FTNNUM_CHAPTER;
+ break;
+ case FootnoteNumbering::PER_DOCUMENT:
+ aFootnoteInfo.m_eNum = FTNNUM_DOC;
+ break;
+ }
+ }
+ break;
+ case WID_PARAGRAPH_STYLE:
+ {
+ SwTextFormatColl* pColl = lcl_GetParaStyle(m_pDoc, aValue);
+ if(pColl)
+ aFootnoteInfo.SetFootnoteTextColl(*pColl);
+ }
+ break;
+ case WID_PAGE_STYLE:
+ {
+ SwPageDesc* pDesc = lcl_GetPageDesc(m_pDoc, aValue);
+ if(pDesc)
+ aFootnoteInfo.ChgPageDesc( pDesc );
+ }
+ break;
+ case WID_ANCHOR_CHARACTER_STYLE:
+ case WID_CHARACTER_STYLE:
+ {
+ SwCharFormat* pFormat = lcl_getCharFormat(m_pDoc, aValue);
+ if(pFormat)
+ {
+ if(pEntry->nWID == WID_ANCHOR_CHARACTER_STYLE)
+ aFootnoteInfo.SetAnchorCharFormat(pFormat);
+ else
+ aFootnoteInfo.SetCharFormat(pFormat);
+ }
+ }
+ break;
+ case WID_POSITION_END_OF_DOC:
+ {
+ bool bVal = *o3tl::doAccess<bool>(aValue);
+ aFootnoteInfo.m_ePos = bVal ? FTNPOS_CHAPTER : FTNPOS_PAGE;
+ }
+ break;
+ case WID_END_NOTICE:
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aFootnoteInfo.m_aQuoVadis = uTmp;
+ }
+ break;
+ case WID_BEGIN_NOTICE:
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aFootnoteInfo.m_aErgoSum = uTmp;
+ }
+ break;
+ }
+ m_pDoc->SetFootnoteInfo(aFootnoteInfo);
+
+
+}
+
+uno::Any SwXFootnoteProperties::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropertySet->getPropertyMap().getByName( rPropertyName );
+ if(!pEntry)
+ throw UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ const SwFootnoteInfo& rFootnoteInfo = m_pDoc->GetFootnoteInfo();
+ switch(pEntry->nWID)
+ {
+ case WID_PREFIX:
+ {
+ aRet <<= rFootnoteInfo.GetPrefix();
+ }
+ break;
+ case WID_SUFFIX:
+ {
+ aRet <<= rFootnoteInfo.GetSuffix();
+ }
+ break;
+ case WID_NUMBERING_TYPE :
+ {
+ aRet <<= static_cast<sal_Int16>(rFootnoteInfo.m_aFormat.GetNumberingType());
+ }
+ break;
+ case WID_START_AT:
+ aRet <<= static_cast<sal_Int16>(rFootnoteInfo.m_nFootnoteOffset);
+ break;
+ case WID_FOOTNOTE_COUNTING :
+ {
+ sal_Int16 nRet = 0;
+ switch(rFootnoteInfo.m_eNum)
+ {
+ case FTNNUM_PAGE:
+ nRet = FootnoteNumbering::PER_PAGE;
+ break;
+ case FTNNUM_CHAPTER:
+ nRet = FootnoteNumbering::PER_CHAPTER;
+ break;
+ case FTNNUM_DOC:
+ nRet = FootnoteNumbering::PER_DOCUMENT;
+ break;
+ }
+ aRet <<= nRet;
+ }
+ break;
+ case WID_PARAGRAPH_STYLE :
+ {
+ SwTextFormatColl* pColl = rFootnoteInfo.GetFootnoteTextColl();
+ OUString aString;
+ if(pColl)
+ aString = pColl->GetName();
+ SwStyleNameMapper::FillProgName(aString, aString, SwGetPoolIdFromName::TxtColl);
+ aRet <<= aString;
+ }
+ break;
+ case WID_PAGE_STYLE :
+ {
+ OUString aString;
+ if( rFootnoteInfo.KnowsPageDesc() )
+ {
+ SwStyleNameMapper::FillProgName(
+ rFootnoteInfo.GetPageDesc( *m_pDoc )->GetName(),
+ aString,
+ SwGetPoolIdFromName::PageDesc);
+ }
+ aRet <<= aString;
+ }
+ break;
+ case WID_ANCHOR_CHARACTER_STYLE:
+ case WID_CHARACTER_STYLE:
+ {
+ OUString aString;
+ const SwCharFormat* pCharFormat = rFootnoteInfo.GetCurrentCharFormat(pEntry->nWID == WID_ANCHOR_CHARACTER_STYLE);
+ if( pCharFormat )
+ {
+ SwStyleNameMapper::FillProgName(
+ pCharFormat->GetName(),
+ aString,
+ SwGetPoolIdFromName::ChrFmt);
+ }
+ aRet <<= aString;
+ }
+ break;
+ case WID_POSITION_END_OF_DOC:
+ aRet <<= FTNPOS_CHAPTER == rFootnoteInfo.m_ePos;
+ break;
+ case WID_END_NOTICE :
+ aRet <<= rFootnoteInfo.m_aQuoVadis;
+ break;
+ case WID_BEGIN_NOTICE :
+ aRet <<= rFootnoteInfo.m_aErgoSum;
+ break;
+ }
+
+
+ return aRet;
+}
+
+void SwXFootnoteProperties::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFootnoteProperties::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFootnoteProperties::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXFootnoteProperties::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+OUString SwXEndnoteProperties::getImplementationName()
+{
+ return "SwXEndnoteProperties";
+}
+
+sal_Bool SwXEndnoteProperties::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXEndnoteProperties::getSupportedServiceNames()
+{
+ Sequence<OUString> aRet { "com.sun.star.text.FootnoteSettings" };
+ return aRet;
+}
+
+SwXEndnoteProperties::SwXEndnoteProperties(SwDoc* pDc) :
+ m_pDoc(pDc),
+ m_pPropertySet(GetEndnoteSet())
+{
+}
+
+SwXEndnoteProperties::~SwXEndnoteProperties()
+{
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXEndnoteProperties::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef = m_pPropertySet->getPropertySetInfo();
+ return aRef;
+}
+
+void SwXEndnoteProperties::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ return;
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropertySet->getPropertyMap().getByName( rPropertyName );
+ if(!pEntry)
+ throw UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if ( pEntry->nFlags & PropertyAttribute::READONLY)
+ throw PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ SwEndNoteInfo aEndInfo(m_pDoc->GetEndNoteInfo());
+ switch(pEntry->nWID)
+ {
+ case WID_PREFIX:
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aEndInfo.SetPrefix(uTmp);
+ }
+ break;
+ case WID_SUFFIX:
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aEndInfo.SetSuffix(uTmp);
+ }
+ break;
+ case WID_NUMBERING_TYPE :
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ aEndInfo.m_aFormat.SetNumberingType(static_cast<SvxNumType>(nTmp));
+ }
+ break;
+ case WID_START_AT:
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ aEndInfo.m_nFootnoteOffset = nTmp;
+ }
+ break;
+ case WID_PARAGRAPH_STYLE :
+ {
+ SwTextFormatColl* pColl = lcl_GetParaStyle(m_pDoc, aValue);
+ if(pColl)
+ aEndInfo.SetFootnoteTextColl(*pColl);
+ }
+ break;
+ case WID_PAGE_STYLE :
+ {
+ SwPageDesc* pDesc = lcl_GetPageDesc(m_pDoc, aValue);
+ if(pDesc)
+ aEndInfo.ChgPageDesc( pDesc );
+ }
+ break;
+ case WID_ANCHOR_CHARACTER_STYLE:
+ case WID_CHARACTER_STYLE :
+ {
+ SwCharFormat* pFormat = lcl_getCharFormat(m_pDoc, aValue);
+ if(pFormat)
+ {
+ if(pEntry->nWID == WID_ANCHOR_CHARACTER_STYLE)
+ aEndInfo.SetAnchorCharFormat(pFormat);
+ else
+ aEndInfo.SetCharFormat(pFormat);
+ }
+ }
+ break;
+ }
+ m_pDoc->SetEndNoteInfo(aEndInfo);
+}
+
+uno::Any SwXEndnoteProperties::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(m_pDoc)
+ {
+ const SfxItemPropertyMapEntry* pEntry = m_pPropertySet->getPropertyMap().getByName( rPropertyName );
+ if(!pEntry)
+ throw UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ const SwEndNoteInfo& rEndInfo = m_pDoc->GetEndNoteInfo();
+ switch(pEntry->nWID)
+ {
+ case WID_PREFIX:
+ aRet <<= rEndInfo.GetPrefix();
+ break;
+ case WID_SUFFIX:
+ aRet <<= rEndInfo.GetSuffix();
+ break;
+ case WID_NUMBERING_TYPE :
+ aRet <<= static_cast<sal_Int16>(rEndInfo.m_aFormat.GetNumberingType());
+ break;
+ case WID_START_AT:
+ aRet <<= static_cast<sal_Int16>(rEndInfo.m_nFootnoteOffset);
+ break;
+ case WID_PARAGRAPH_STYLE :
+ {
+ SwTextFormatColl* pColl = rEndInfo.GetFootnoteTextColl();
+ OUString aString;
+ if(pColl)
+ aString = pColl->GetName();
+ SwStyleNameMapper::FillProgName(
+ aString,
+ aString,
+ SwGetPoolIdFromName::TxtColl);
+ aRet <<= aString;
+
+ }
+ break;
+ case WID_PAGE_STYLE :
+ {
+ OUString aString;
+ if( rEndInfo.KnowsPageDesc() )
+ {
+ SwStyleNameMapper::FillProgName(
+ rEndInfo.GetPageDesc( *m_pDoc )->GetName(),
+ aString,
+ SwGetPoolIdFromName::PageDesc);
+ }
+ aRet <<= aString;
+ }
+ break;
+ case WID_ANCHOR_CHARACTER_STYLE:
+ case WID_CHARACTER_STYLE:
+ {
+ OUString aString;
+ const SwCharFormat* pCharFormat = rEndInfo.GetCurrentCharFormat( pEntry->nWID == WID_ANCHOR_CHARACTER_STYLE );
+ if( pCharFormat )
+ {
+ SwStyleNameMapper::FillProgName(
+ pCharFormat->GetName(),
+ aString,
+ SwGetPoolIdFromName::ChrFmt);
+ }
+ aRet <<= aString;
+ }
+ break;
+ }
+
+ }
+ return aRet;
+}
+
+void SwXEndnoteProperties::addPropertyChangeListener(
+ const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXEndnoteProperties::removePropertyChangeListener(const OUString& /*PropertyName*/,
+ const uno:: Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXEndnoteProperties::addVetoableChangeListener(const OUString& /*PropertyName*/,
+ const uno:: Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXEndnoteProperties::removeVetoableChangeListener(const OUString& /*PropertyName*/, const uno:: Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+OUString SwXLineNumberingProperties::getImplementationName()
+{
+ return "SwXLineNumberingProperties";
+}
+
+sal_Bool SwXLineNumberingProperties::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXLineNumberingProperties::getSupportedServiceNames()
+{
+ Sequence<OUString> aRet { "com.sun.star.text.LineNumberingProperties" };
+ return aRet;
+}
+
+SwXLineNumberingProperties::SwXLineNumberingProperties(SwDoc* pDc) :
+ m_pDoc(pDc),
+ m_pPropertySet(GetLineNumberingSet())
+{
+}
+
+SwXLineNumberingProperties::~SwXLineNumberingProperties()
+{
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXLineNumberingProperties::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef = m_pPropertySet->getPropertySetInfo();
+ return aRef;
+}
+
+void SwXLineNumberingProperties::setPropertyValue(
+ const OUString& rPropertyName, const Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropertySet->getPropertyMap().getByName( rPropertyName );
+ if(!pEntry)
+ throw UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if ( pEntry->nFlags & PropertyAttribute::READONLY)
+ throw PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ SwLineNumberInfo aFontMetric(m_pDoc->GetLineNumberInfo());
+ switch(pEntry->nWID)
+ {
+ case WID_NUM_ON:
+ {
+ bool bVal = *o3tl::doAccess<bool>(aValue);
+ aFontMetric.SetPaintLineNumbers(bVal);
+ }
+ break;
+ case WID_CHARACTER_STYLE :
+ {
+ SwCharFormat* pFormat = lcl_getCharFormat(m_pDoc, aValue);
+ if(pFormat)
+ aFontMetric.SetCharFormat(pFormat);
+ }
+ break;
+ case WID_NUMBERING_TYPE :
+ {
+ SvxNumberType aNumType(aFontMetric.GetNumType());
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ aNumType.SetNumberingType(static_cast<SvxNumType>(nTmp));
+ aFontMetric.SetNumType(aNumType);
+ }
+ break;
+ case WID_NUMBER_POSITION :
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ switch(nTmp)
+ {
+ case style::LineNumberPosition::LEFT:
+ aFontMetric.SetPos(LINENUMBER_POS_LEFT);
+ break;
+ case style::LineNumberPosition::RIGHT :
+ aFontMetric.SetPos(LINENUMBER_POS_RIGHT);
+ break;
+ case style::LineNumberPosition::INSIDE:
+ aFontMetric.SetPos(LINENUMBER_POS_INSIDE);
+ break;
+ case style::LineNumberPosition::OUTSIDE:
+ aFontMetric.SetPos(LINENUMBER_POS_OUTSIDE);
+ break;
+ }
+ }
+ break;
+ case WID_DISTANCE :
+ {
+ sal_Int32 nVal = 0;
+ aValue >>= nVal;
+ sal_Int32 nTmp = o3tl::toTwips(nVal, o3tl::Length::mm100);
+ if (nTmp > SAL_MAX_UINT16)
+ nTmp = SAL_MAX_UINT16;
+ aFontMetric.SetPosFromLeft(nTmp);
+ }
+ break;
+ case WID_INTERVAL :
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ if( nTmp > 0)
+ aFontMetric.SetCountBy(nTmp);
+ }
+ break;
+ case WID_SEPARATOR_TEXT :
+ {
+ OUString uTmp;
+ aValue >>= uTmp;
+ aFontMetric.SetDivider(uTmp);
+ }
+ break;
+ case WID_SEPARATOR_INTERVAL:
+ {
+ sal_Int16 nTmp = 0;
+ aValue >>= nTmp;
+ if( nTmp >= 0)
+ aFontMetric.SetDividerCountBy(nTmp);
+ }
+ break;
+ case WID_COUNT_EMPTY_LINES :
+ {
+ bool bVal = *o3tl::doAccess<bool>(aValue);
+ aFontMetric.SetCountBlankLines(bVal);
+ }
+ break;
+ case WID_COUNT_LINES_IN_FRAMES :
+ {
+ bool bVal = *o3tl::doAccess<bool>(aValue);
+ aFontMetric.SetCountInFlys(bVal);
+ }
+ break;
+ case WID_RESTART_AT_EACH_PAGE :
+ {
+ bool bVal = *o3tl::doAccess<bool>(aValue);
+ aFontMetric.SetRestartEachPage(bVal);
+ }
+ break;
+ }
+ m_pDoc->SetLineNumberInfo(aFontMetric);
+}
+
+Any SwXLineNumberingProperties::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ Any aRet;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropertySet->getPropertyMap().getByName( rPropertyName );
+ if(!pEntry)
+ throw UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ const SwLineNumberInfo& rInfo = m_pDoc->GetLineNumberInfo();
+ switch(pEntry->nWID)
+ {
+ case WID_NUM_ON:
+ aRet <<= rInfo.IsPaintLineNumbers();
+ break;
+ case WID_CHARACTER_STYLE :
+ {
+ OUString aString;
+ // return empty string if no char format is set
+ // otherwise it would be created here
+ if(rInfo.HasCharFormat())
+ {
+ SwStyleNameMapper::FillProgName(
+ rInfo.GetCharFormat(m_pDoc->getIDocumentStylePoolAccess())->GetName(),
+ aString,
+ SwGetPoolIdFromName::ChrFmt);
+ }
+ aRet <<= aString;
+ }
+ break;
+ case WID_NUMBERING_TYPE :
+ aRet <<= static_cast<sal_Int16>(rInfo.GetNumType().GetNumberingType());
+ break;
+ case WID_NUMBER_POSITION :
+ {
+ sal_Int16 nRet = 0;
+ switch(rInfo.GetPos())
+ {
+ case LINENUMBER_POS_LEFT:
+ nRet = style::LineNumberPosition::LEFT;
+ break;
+ case LINENUMBER_POS_RIGHT :
+ nRet = style::LineNumberPosition::RIGHT ;
+ break;
+ case LINENUMBER_POS_INSIDE:
+ nRet = style::LineNumberPosition::INSIDE ;
+ break;
+ case LINENUMBER_POS_OUTSIDE :
+ nRet = style::LineNumberPosition::OUTSIDE ;
+ break;
+ }
+ aRet <<= nRet;
+ }
+ break;
+ case WID_DISTANCE :
+ {
+ sal_uInt32 nPos = rInfo.GetPosFromLeft();
+ if(USHRT_MAX == nPos)
+ nPos = 0;
+ aRet <<= static_cast < sal_Int32 >(convertTwipToMm100(nPos));
+ }
+ break;
+ case WID_INTERVAL :
+ aRet <<= static_cast<sal_Int16>(rInfo.GetCountBy());
+ break;
+ case WID_SEPARATOR_TEXT :
+ aRet <<= rInfo.GetDivider();
+ break;
+ case WID_SEPARATOR_INTERVAL:
+ aRet <<= static_cast<sal_Int16>(rInfo.GetDividerCountBy());
+ break;
+ case WID_COUNT_EMPTY_LINES :
+ aRet <<= rInfo.IsCountBlankLines();
+ break;
+ case WID_COUNT_LINES_IN_FRAMES :
+ aRet <<= rInfo.IsCountInFlys();
+ break;
+ case WID_RESTART_AT_EACH_PAGE :
+ aRet <<= rInfo.IsRestartEachPage();
+ break;
+ }
+ return aRet;
+}
+
+void SwXLineNumberingProperties::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno:: Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+OSL_FAIL("not implemented");
+}
+
+void SwXLineNumberingProperties::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno:: Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+OSL_FAIL("not implemented");
+}
+
+void SwXLineNumberingProperties::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno:: Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+OSL_FAIL("not implemented");
+}
+
+void SwXLineNumberingProperties::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno:: Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+OSL_FAIL("not implemented");
+}
+
+constexpr OUStringLiteral aInvalidStyle = u"__XXX___invalid";
+
+class SwXNumberingRules::Impl
+ : public SvtListener
+{
+ SwXNumberingRules& m_rParent;
+ virtual void Notify(const SfxHint&) override;
+ public:
+ explicit Impl(SwXNumberingRules& rParent) : m_rParent(rParent) {}
+};
+
+bool SwXNumberingRules::isInvalidStyle(std::u16string_view rName)
+{
+ return rName == aInvalidStyle;
+}
+
+namespace
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwXNumberingRules::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXNumberingRulesUnoTunnelId;
+ return theSwXNumberingRulesUnoTunnelId.getSeq();
+}
+
+// return implementation specific data
+sal_Int64 SwXNumberingRules::getSomething( const uno::Sequence< sal_Int8 > & rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+OUString SwXNumberingRules::getImplementationName()
+{
+ return "SwXNumberingRules";
+}
+
+sal_Bool SwXNumberingRules::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXNumberingRules::getSupportedServiceNames()
+{
+ Sequence<OUString> aRet { "com.sun.star.text.NumberingRules" };
+ return aRet;
+}
+
+SwXNumberingRules::SwXNumberingRules(const SwNumRule& rRule, SwDoc* doc) :
+ m_pImpl(new SwXNumberingRules::Impl(*this)),
+ m_pDoc(doc),
+ m_pDocShell(nullptr),
+ m_pNumRule(new SwNumRule(rRule)),
+ m_pPropertySet(GetNumberingRulesSet()),
+ m_bOwnNumRuleCreated(true)
+{
+ // first organize the document - it is dependent on the set character formats
+ // if no format is set, it should work as well
+ for( sal_uInt16 i = 0; i < MAXLEVEL; ++i)
+ {
+ SwNumFormat rFormat(m_pNumRule->Get(i));
+ SwCharFormat* pCharFormat = rFormat.GetCharFormat();
+ if(pCharFormat)
+ {
+ m_pDoc = pCharFormat->GetDoc();
+ break;
+ }
+ }
+ if(m_pDoc)
+ m_pImpl->StartListening(GetPageDescNotifier(m_pDoc));
+ for(sal_uInt16 i = 0; i < MAXLEVEL; ++i)
+ {
+ m_sNewCharStyleNames[i] = aInvalidStyle;
+ m_sNewBulletFontNames[i] = aInvalidStyle;
+ }
+}
+
+SwXNumberingRules::SwXNumberingRules(SwDocShell& rDocSh) :
+ m_pImpl(new SwXNumberingRules::Impl(*this)),
+ m_pDoc(nullptr),
+ m_pDocShell(&rDocSh),
+ m_pNumRule(nullptr),
+ m_pPropertySet(GetNumberingRulesSet()),
+ m_bOwnNumRuleCreated(false)
+{
+ m_pImpl->StartListening(GetPageDescNotifier(m_pDocShell->GetDoc()));
+}
+
+SwXNumberingRules::SwXNumberingRules(SwDoc& rDoc) :
+ m_pImpl(new SwXNumberingRules::Impl(*this)),
+ m_pDoc(&rDoc),
+ m_pDocShell(nullptr),
+ m_pNumRule(nullptr),
+ m_pPropertySet(GetNumberingRulesSet()),
+ m_bOwnNumRuleCreated(false)
+{
+ m_pImpl->StartListening(GetPageDescNotifier(&rDoc));
+ m_sCreatedNumRuleName = rDoc.GetUniqueNumRuleName();
+ rDoc.MakeNumRule( m_sCreatedNumRuleName, nullptr, false,
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode() );
+}
+
+SwXNumberingRules::~SwXNumberingRules()
+{
+ SolarMutexGuard aGuard;
+ if(m_pDoc && !m_sCreatedNumRuleName.isEmpty())
+ m_pDoc->DelNumRule( m_sCreatedNumRuleName );
+ if( m_bOwnNumRuleCreated )
+ delete m_pNumRule;
+}
+
+void SwXNumberingRules::replaceByIndex(sal_Int32 nIndex, const uno::Any& rElement)
+{
+ SolarMutexGuard aGuard;
+ if(nIndex < 0 || MAXLEVEL <= nIndex)
+ throw lang::IndexOutOfBoundsException();
+
+ auto rProperties = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(
+ rElement);
+ if(!rProperties)
+ throw lang::IllegalArgumentException();
+ SwNumRule* pRule = nullptr;
+ if(m_pNumRule)
+ SwXNumberingRules::SetNumberingRuleByIndex( *m_pNumRule,
+ *rProperties, nIndex);
+ else if(m_pDocShell)
+ {
+ // #i87650# - correction of cws warnings:
+ SwNumRule aNumRule( *(m_pDocShell->GetDoc()->GetOutlineNumRule()) );
+ SwXNumberingRules::SetNumberingRuleByIndex( aNumRule,
+ *rProperties, nIndex);
+ // set character format if needed
+ // this code appears to be dead - except when a style is assigned for BITMAP numbering?
+ const SwCharFormats* pFormats = m_pDocShell->GetDoc()->GetCharFormats();
+ const size_t nChCount = pFormats->size();
+ for(sal_uInt16 i = 0; i < MAXLEVEL;i++)
+ {
+ SwNumFormat aFormat(aNumRule.Get( i ));
+ if (!m_sNewCharStyleNames[i].isEmpty() &&
+ m_sNewCharStyleNames[i] != UNO_NAME_CHARACTER_FORMAT_NONE &&
+ (!aFormat.GetCharFormat() || aFormat.GetCharFormat()->GetName()!= m_sNewCharStyleNames[i]))
+ {
+ SwCharFormat* pCharFormat = nullptr;
+ for(size_t j = 0; j< nChCount; ++j)
+ {
+ SwCharFormat* pTmp = (*pFormats)[j];
+ if(pTmp->GetName() == m_sNewCharStyleNames[i])
+ {
+ pCharFormat = pTmp;
+ break;
+ }
+ }
+ if(!pCharFormat)
+ {
+ SfxStyleSheetBase* pBase;
+ pBase = m_pDocShell->GetStyleSheetPool()->Find(m_sNewCharStyleNames[i],
+ SfxStyleFamily::Char);
+ if(!pBase)
+ pBase = &m_pDocShell->GetStyleSheetPool()->Make(m_sNewCharStyleNames[i], SfxStyleFamily::Char);
+ pCharFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat();
+
+ }
+ aFormat.SetCharFormat( pCharFormat );
+ aNumRule.Set( i, aFormat );
+ }
+ }
+ m_pDocShell->GetDoc()->SetOutlineNumRule( aNumRule );
+ }
+ else if(m_pDoc && !m_sCreatedNumRuleName.isEmpty() &&
+ nullptr != (pRule = m_pDoc->FindNumRulePtr( m_sCreatedNumRuleName )))
+ {
+ SwXNumberingRules::SetNumberingRuleByIndex( *pRule,
+ *rProperties, nIndex);
+
+ pRule->Validate(*m_pDoc);
+ }
+ else
+ throw uno::RuntimeException();
+}
+
+sal_Int32 SwXNumberingRules::getCount()
+{
+ return MAXLEVEL;
+}
+
+uno::Any SwXNumberingRules::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(nIndex < 0 || MAXLEVEL <= nIndex)
+ throw lang::IndexOutOfBoundsException();
+
+ uno::Any aVal;
+ const SwNumRule* pRule = m_pNumRule;
+ if(!pRule && m_pDoc && !m_sCreatedNumRuleName.isEmpty())
+ pRule = m_pDoc->FindNumRulePtr( m_sCreatedNumRuleName );
+ if(pRule)
+ {
+ uno::Sequence<beans::PropertyValue> aRet = GetNumberingRuleByIndex(
+ *pRule, nIndex);
+ aVal <<= aRet;
+
+ }
+ else if(m_pDocShell)
+ {
+ uno::Sequence<beans::PropertyValue> aRet = GetNumberingRuleByIndex(
+ *m_pDocShell->GetDoc()->GetOutlineNumRule(), nIndex);
+ aVal <<= aRet;
+ }
+ else
+ throw uno::RuntimeException();
+ return aVal;
+}
+
+uno::Type SwXNumberingRules::getElementType()
+{
+ return cppu::UnoType<uno::Sequence<beans::PropertyValue>>::get();
+}
+
+sal_Bool SwXNumberingRules::hasElements()
+{
+ return true;
+}
+
+const TranslateId STR_POOLCOLL_HEADLINE_ARY[]
+{
+ STR_POOLCOLL_HEADLINE1,
+ STR_POOLCOLL_HEADLINE2,
+ STR_POOLCOLL_HEADLINE3,
+ STR_POOLCOLL_HEADLINE4,
+ STR_POOLCOLL_HEADLINE5,
+ STR_POOLCOLL_HEADLINE6,
+ STR_POOLCOLL_HEADLINE7,
+ STR_POOLCOLL_HEADLINE8,
+ STR_POOLCOLL_HEADLINE9,
+ STR_POOLCOLL_HEADLINE10
+};
+
+uno::Sequence<beans::PropertyValue> SwXNumberingRules::GetNumberingRuleByIndex(
+ const SwNumRule& rNumRule, sal_Int32 nIndex) const
+{
+ SolarMutexGuard aGuard;
+ OSL_ENSURE( 0 <= nIndex && nIndex < MAXLEVEL, "index out of range" );
+
+ const SwNumFormat& rFormat = rNumRule.Get( o3tl::narrowing<sal_uInt16>(nIndex) );
+
+ SwCharFormat* pCharFormat = rFormat.GetCharFormat();
+ OUString CharStyleName;
+ if (pCharFormat)
+ CharStyleName = pCharFormat->GetName();
+
+ // Whether or not a style is present: the array entry overwrites this string
+ if (!m_sNewCharStyleNames[nIndex].isEmpty() &&
+ !SwXNumberingRules::isInvalidStyle(m_sNewCharStyleNames[nIndex]))
+ {
+ CharStyleName = m_sNewCharStyleNames[nIndex];
+ }
+
+ OUString aUString;
+ if (m_pDocShell) // -> Chapter Numbering
+ {
+ // template name
+ OUString sValue(SwResId(STR_POOLCOLL_HEADLINE_ARY[nIndex]));
+ const SwTextFormatColls* pColls = m_pDocShell->GetDoc()->GetTextFormatColls();
+ const size_t nCount = pColls->size();
+ for(size_t i = 0; i < nCount; ++i)
+ {
+ SwTextFormatColl &rTextColl = *pColls->operator[](i);
+ if(rTextColl.IsDefault())
+ continue;
+
+ const sal_Int16 nOutLevel = rTextColl.IsAssignedToListLevelOfOutlineStyle()
+ ? static_cast<sal_Int16>(rTextColl.GetAssignedOutlineStyleLevel())
+ : MAXLEVEL;
+ if ( nOutLevel == nIndex )
+ {
+ sValue = rTextColl.GetName();
+ break; // the style for the level in question has been found
+ }
+ else if( sValue==rTextColl.GetName() )
+ {
+ // if the default for the level is existing, but its
+ // level is different, then it cannot be the default.
+ sValue.clear();
+ }
+ }
+ SwStyleNameMapper::FillProgName(sValue, aUString, SwGetPoolIdFromName::TxtColl);
+ }
+
+ OUString referer;
+ if (m_pDoc != nullptr) {
+ auto const sh = m_pDoc->GetPersist();
+ if (sh != nullptr && sh->HasName()) {
+ referer = sh->GetMedium()->GetName();
+ }
+ }
+ return GetPropertiesForNumFormat(
+ rFormat, CharStyleName, m_pDocShell ? & aUString : nullptr, referer);
+
+}
+
+uno::Sequence<beans::PropertyValue> SwXNumberingRules::GetPropertiesForNumFormat(
+ const SwNumFormat& rFormat, OUString const& rCharFormatName,
+ OUString const*const pHeadingStyleName, OUString const & referer)
+{
+ bool bChapterNum = pHeadingStyleName != nullptr;
+
+ std::vector<PropertyValue> aPropertyValues;
+ aPropertyValues.reserve(32);
+ //fill all properties into the array
+
+ //adjust
+ SvxAdjust eAdj = rFormat.GetNumAdjust();
+ sal_Int16 nINT16 = aSvxToUnoAdjust[eAdj];
+ aPropertyValues.push_back(comphelper::makePropertyValue("Adjust", nINT16));
+
+ //parentnumbering
+ nINT16 = rFormat.GetIncludeUpperLevels();
+ aPropertyValues.push_back(comphelper::makePropertyValue("ParentNumbering", nINT16));
+
+ //prefix
+ OUString aUString = rFormat.GetPrefix();
+ aPropertyValues.push_back(comphelper::makePropertyValue("Prefix", aUString));
+
+ //suffix
+ aUString = rFormat.GetSuffix();
+ aPropertyValues.push_back(comphelper::makePropertyValue("Suffix", aUString));
+
+ //listformat
+ if (rFormat.HasListFormat())
+ {
+ aPropertyValues.push_back(comphelper::makePropertyValue("ListFormat", rFormat.GetListFormat()));
+ }
+
+ //char style name
+ aUString.clear();
+ SwStyleNameMapper::FillProgName( rCharFormatName, aUString, SwGetPoolIdFromName::ChrFmt);
+ aPropertyValues.push_back(comphelper::makePropertyValue("CharStyleName", aUString));
+
+ //startvalue
+ nINT16 = rFormat.GetStart();
+ aPropertyValues.push_back(comphelper::makePropertyValue("StartWith", nINT16));
+
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_WIDTH_AND_POSITION )
+ {
+ //leftmargin
+ sal_Int32 nINT32 = convertTwipToMm100(rFormat.GetAbsLSpace());
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_LEFT_MARGIN, nINT32));
+
+ //chartextoffset
+ nINT32 = convertTwipToMm100(rFormat.GetCharTextDistance());
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_SYMBOL_TEXT_DISTANCE, nINT32));
+
+ //firstlineoffset
+ nINT32 = convertTwipToMm100(rFormat.GetFirstLineOffset());
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_FIRST_LINE_OFFSET, nINT32));
+ }
+
+ // PositionAndSpaceMode
+ nINT16 = PositionAndSpaceMode::LABEL_WIDTH_AND_POSITION;
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ nINT16 = PositionAndSpaceMode::LABEL_ALIGNMENT;
+ }
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_POSITION_AND_SPACE_MODE, nINT16));
+
+ if ( rFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
+ {
+ // LabelFollowedBy
+ nINT16 = LabelFollow::LISTTAB;
+ if ( rFormat.GetLabelFollowedBy() == SvxNumberFormat::SPACE )
+ {
+ nINT16 = LabelFollow::SPACE;
+ }
+ else if ( rFormat.GetLabelFollowedBy() == SvxNumberFormat::NOTHING )
+ {
+ nINT16 = LabelFollow::NOTHING;
+ }
+ else if ( rFormat.GetLabelFollowedBy() == SvxNumberFormat::NEWLINE )
+ {
+ nINT16 = LabelFollow::NEWLINE;
+ }
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_LABEL_FOLLOWED_BY, nINT16));
+
+ // ListtabStopPosition
+ sal_Int32 nINT32 = convertTwipToMm100(rFormat.GetListtabPos());
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_LISTTAB_STOP_POSITION, nINT32));
+
+ // FirstLineIndent
+ nINT32 = convertTwipToMm100(rFormat.GetFirstLineIndent());
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_FIRST_LINE_INDENT, nINT32));
+
+ // IndentAt
+ nINT32 = convertTwipToMm100(rFormat.GetIndentAt());
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_INDENT_AT, nINT32));
+ }
+
+ //numberingtype
+ nINT16 = rFormat.GetNumberingType();
+ aPropertyValues.push_back(comphelper::makePropertyValue("NumberingType", nINT16));
+
+ if(!bChapterNum)
+ {
+ if(SVX_NUM_CHAR_SPECIAL == rFormat.GetNumberingType())
+ {
+ sal_UCS4 cBullet = rFormat.GetBulletChar();
+
+ //BulletId
+ nINT16 = cBullet;
+ aPropertyValues.push_back(comphelper::makePropertyValue("BulletId", nINT16));
+
+ std::optional<vcl::Font> pFont = rFormat.GetBulletFont();
+
+ //BulletChar
+ aUString = OUString(&cBullet, 1);
+ aPropertyValues.push_back(comphelper::makePropertyValue("BulletChar", aUString));
+
+ //BulletFontName
+ aUString = pFont ? pFont->GetStyleName() : OUString();
+ aPropertyValues.push_back(comphelper::makePropertyValue("BulletFontName", aUString));
+
+ //BulletFont
+ if(pFont)
+ {
+ awt::FontDescriptor aDesc;
+ SvxUnoFontDescriptor::ConvertFromFont( *pFont, aDesc );
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_BULLET_FONT, aDesc));
+ }
+ }
+ if (SVX_NUM_BITMAP == rFormat.GetNumberingType())
+ {
+ const SvxBrushItem* pBrush = rFormat.GetBrush();
+ const Graphic* pGraphic = pBrush ? pBrush->GetGraphic(referer) : nullptr;
+ if (pGraphic)
+ {
+ //GraphicBitmap
+ uno::Reference<awt::XBitmap> xBitmap(pGraphic->GetXGraphic(), uno::UNO_QUERY);
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_GRAPHIC_BITMAP, xBitmap));
+ }
+
+ Size aSize = rFormat.GetGraphicSize();
+ // #i101131#
+ // adjust conversion due to type mismatch between <Size> and <awt::Size>
+ awt::Size aAwtSize(convertTwipToMm100(aSize.Width()), convertTwipToMm100(aSize.Height()));
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_GRAPHIC_SIZE, aAwtSize));
+
+ const SwFormatVertOrient* pOrient = rFormat.GetGraphicOrientation();
+ if(pOrient)
+ {
+ uno::Any any;
+ pOrient->QueryValue(any);
+ aPropertyValues.emplace_back(
+ UNO_NAME_VERT_ORIENT, -1, any, PropertyState_DIRECT_VALUE);
+ }
+ }
+ }
+ else
+ {
+ aUString = *pHeadingStyleName;
+ aPropertyValues.push_back(comphelper::makePropertyValue(UNO_NAME_HEADING_STYLE_NAME, aUString));
+ }
+
+ return ::comphelper::containerToSequence(aPropertyValues);
+}
+
+void SwXNumberingRules::SetNumberingRuleByIndex(
+ SwNumRule& rNumRule,
+ const uno::Sequence<beans::PropertyValue>& rProperties, sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ OSL_ENSURE( 0 <= nIndex && nIndex < MAXLEVEL, "index out of range" );
+
+ SwNumFormat aFormat(rNumRule.Get( o3tl::narrowing<sal_uInt16>(nIndex) ));
+
+ OUString sHeadingStyleName;
+ OUString sParagraphStyleName;
+
+ SetPropertiesToNumFormat(aFormat, m_sNewCharStyleNames[nIndex],
+ &m_sNewBulletFontNames[nIndex],
+ &sHeadingStyleName, &sParagraphStyleName,
+ m_pDoc, m_pDocShell, rProperties);
+
+
+ if (m_pDoc && !sParagraphStyleName.isEmpty())
+ {
+ const SwTextFormatColls* pColls = m_pDoc->GetTextFormatColls();
+ const size_t nCount = pColls->size();
+ for (size_t k = 0; k < nCount; ++k)
+ {
+ SwTextFormatColl &rTextColl = *((*pColls)[k]);
+ if (rTextColl.GetName() == sParagraphStyleName)
+ rTextColl.SetFormatAttr( SwNumRuleItem( rNumRule.GetName()));
+ }
+ }
+
+ if (!sHeadingStyleName.isEmpty())
+ {
+ assert(m_pDocShell);
+ const SwTextFormatColls* pColls = m_pDocShell->GetDoc()->GetTextFormatColls();
+ const size_t nCount = pColls->size();
+ for (size_t k = 0; k < nCount; ++k)
+ {
+ SwTextFormatColl &rTextColl = *((*pColls)[k]);
+ if (rTextColl.IsDefault())
+ continue;
+ if (rTextColl.IsAssignedToListLevelOfOutlineStyle() &&
+ rTextColl.GetAssignedOutlineStyleLevel() == nIndex &&
+ rTextColl.GetName() != sHeadingStyleName)
+ {
+ rTextColl.DeleteAssignmentToListLevelOfOutlineStyle();
+ }
+ else if (rTextColl.GetName() == sHeadingStyleName)
+ {
+ rTextColl.AssignToListLevelOfOutlineStyle( nIndex );
+ }
+ }
+ }
+
+ rNumRule.Set(o3tl::narrowing<sal_uInt16>(nIndex), aFormat);
+}
+
+void SwXNumberingRules::SetPropertiesToNumFormat(
+ SwNumFormat & aFormat,
+ OUString & rCharStyleName, OUString *const pBulletFontName,
+ OUString *const pHeadingStyleName,
+ OUString *const pParagraphStyleName,
+ SwDoc *const pDoc,
+ SwDocShell *const pDocShell,
+ const uno::Sequence<beans::PropertyValue>& rProperties)
+{
+ assert(pDoc == nullptr || pDocShell == nullptr); // can't be both ordinary and chapter numbering
+
+ bool bWrongArg = false;
+ std::unique_ptr<SvxBrushItem> pSetBrush;
+ std::unique_ptr<Size> pSetSize;
+ std::unique_ptr<SwFormatVertOrient> pSetVOrient;
+ bool bCharStyleNameSet = false;
+
+ for (const beans::PropertyValue& rProp : rProperties)
+ {
+ if (rProp.Name == UNO_NAME_ADJUST)
+ {
+ sal_Int16 nValue = text::HoriOrientation::NONE;
+ rProp.Value >>= nValue;
+ if (nValue > text::HoriOrientation::NONE &&
+ nValue <= text::HoriOrientation::LEFT &&
+ USHRT_MAX != aUnoToSvxAdjust[nValue])
+ {
+ aFormat.SetNumAdjust(static_cast<SvxAdjust>(aUnoToSvxAdjust[nValue]));
+ }
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_PARENT_NUMBERING)
+ {
+ sal_Int16 nSet = 0;
+ rProp.Value >>= nSet;
+ if(nSet >= 0 && MAXLEVEL >= nSet)
+ aFormat.SetIncludeUpperLevels( static_cast< sal_uInt8 >(nSet) );
+ }
+ else if (rProp.Name == UNO_NAME_PREFIX)
+ {
+ OUString uTmp;
+ rProp.Value >>= uTmp;
+ aFormat.SetPrefix(uTmp);
+ }
+ else if (rProp.Name == UNO_NAME_SUFFIX)
+ {
+ OUString uTmp;
+ rProp.Value >>= uTmp;
+ aFormat.SetSuffix(uTmp);
+ }
+ else if (rProp.Name == UNO_NAME_CHAR_STYLE_NAME)
+ {
+ bCharStyleNameSet = true;
+ OUString uTmp;
+ rProp.Value >>= uTmp;
+ OUString sCharFormatName;
+ SwStyleNameMapper::FillUIName( uTmp, sCharFormatName, SwGetPoolIdFromName::ChrFmt );
+ SwDoc *const pLocalDoc = pDocShell ? pDocShell->GetDoc() : pDoc;
+ if (sCharFormatName == UNO_NAME_CHARACTER_FORMAT_NONE)
+ {
+ rCharStyleName = aInvalidStyle;
+ aFormat.SetCharFormat(nullptr);
+ }
+ else if (pLocalDoc)
+ {
+ SwCharFormat* pCharFormat = nullptr;
+ if (!sCharFormatName.isEmpty())
+ {
+ pCharFormat = pLocalDoc->FindCharFormatByName(sCharFormatName);
+ if(!pCharFormat)
+ {
+
+ SfxStyleSheetBase* pBase;
+ SfxStyleSheetBasePool* pPool = pLocalDoc->GetDocShell()->GetStyleSheetPool();
+ pBase = pPool->Find(sCharFormatName, SfxStyleFamily::Char);
+ if(!pBase)
+ pBase = &pPool->Make(sCharFormatName, SfxStyleFamily::Char);
+ pCharFormat = static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat();
+ }
+ }
+ aFormat.SetCharFormat( pCharFormat );
+ // #i51842#
+ // If the character format has been found its name should not be in the
+ // char style names array
+ rCharStyleName.clear();
+ }
+ else
+ rCharStyleName = sCharFormatName;
+ }
+ else if (rProp.Name == UNO_NAME_START_WITH)
+ {
+ sal_Int16 nVal = 0;
+ rProp.Value >>= nVal;
+ aFormat.SetStart(nVal);
+ }
+ else if (rProp.Name == UNO_NAME_LEFT_MARGIN)
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ // #i23727# nValue can be negative
+ aFormat.SetAbsLSpace(o3tl::toTwips(nValue, o3tl::Length::mm100));
+ }
+ else if (rProp.Name == UNO_NAME_SYMBOL_TEXT_DISTANCE)
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ if (nValue >= 0)
+ aFormat.SetCharTextDistance(o3tl::toTwips(nValue, o3tl::Length::mm100));
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_FIRST_LINE_OFFSET)
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ // #i23727# nValue can be positive
+ nValue = o3tl::toTwips(nValue, o3tl::Length::mm100);
+ aFormat.SetFirstLineOffset(nValue);
+ }
+ else if (rProp.Name == UNO_NAME_POSITION_AND_SPACE_MODE)
+ {
+ sal_Int16 nValue = 0;
+ rProp.Value >>= nValue;
+ if ( nValue == 0 )
+ {
+ aFormat.SetPositionAndSpaceMode( SvxNumberFormat::LABEL_WIDTH_AND_POSITION );
+ }
+ else if ( nValue == 1 )
+ {
+ aFormat.SetPositionAndSpaceMode( SvxNumberFormat::LABEL_ALIGNMENT );
+ }
+ else
+ {
+ bWrongArg = true;
+ }
+ }
+ else if (rProp.Name == UNO_NAME_LABEL_FOLLOWED_BY)
+ {
+ sal_Int16 nValue = 0;
+ rProp.Value >>= nValue;
+ if ( nValue == LabelFollow::LISTTAB )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::LISTTAB );
+ }
+ else if ( nValue == LabelFollow::SPACE )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::SPACE );
+ }
+ else if ( nValue == LabelFollow::NOTHING )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::NOTHING );
+ }
+ else if ( nValue == LabelFollow::NEWLINE )
+ {
+ aFormat.SetLabelFollowedBy( SvxNumberFormat::NEWLINE );
+ }
+ else
+ {
+ bWrongArg = true;
+ }
+ }
+ else if (rProp.Name == UNO_NAME_LISTTAB_STOP_POSITION)
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ nValue = o3tl::toTwips(nValue, o3tl::Length::mm100);
+ if ( nValue >= 0 )
+ {
+ aFormat.SetListtabPos( nValue );
+ }
+ else
+ {
+ bWrongArg = true;
+ }
+ }
+ else if (rProp.Name == UNO_NAME_FIRST_LINE_INDENT)
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ nValue = o3tl::toTwips(nValue, o3tl::Length::mm100);
+ aFormat.SetFirstLineIndent( nValue );
+ }
+ else if (rProp.Name == UNO_NAME_INDENT_AT)
+ {
+ sal_Int32 nValue = 0;
+ rProp.Value >>= nValue;
+ nValue = o3tl::toTwips(nValue, o3tl::Length::mm100);
+ aFormat.SetIndentAt( nValue );
+ }
+ else if (rProp.Name == UNO_NAME_NUMBERING_TYPE)
+ {
+ sal_Int16 nSet = 0;
+ rProp.Value >>= nSet;
+ if(nSet >= 0)
+ aFormat.SetNumberingType(static_cast<SvxNumType>(nSet));
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_PARAGRAPH_STYLE_NAME)
+ {
+ if (pParagraphStyleName)
+ {
+ OUString uTmp;
+ rProp.Value >>= uTmp;
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName(uTmp, sStyleName, SwGetPoolIdFromName::TxtColl );
+ *pParagraphStyleName = sStyleName;
+ }
+ }
+ else if (rProp.Name == UNO_NAME_BULLET_ID)
+ {
+ sal_Int16 nSet = 0;
+ if( rProp.Value >>= nSet )
+ aFormat.SetBulletChar(nSet);
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_BULLET_FONT)
+ {
+ awt::FontDescriptor desc;
+ if (rProp.Value >>= desc)
+ {
+ // #i93725#
+ // do not accept "empty" font
+ if (!desc.Name.isEmpty())
+ {
+ vcl::Font aFont;
+ SvxUnoFontDescriptor::ConvertToFont(desc, aFont);
+ aFormat.SetBulletFont(&aFont);
+ }
+ }
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_BULLET_FONT_NAME)
+ {
+ OUString sBulletFontName;
+ rProp.Value >>= sBulletFontName;
+ SwDocShell *const pLclDocShell = pDocShell ? pDocShell : pDoc ? pDoc->GetDocShell() : nullptr;
+ if (!sBulletFontName.isEmpty() && pLclDocShell)
+ {
+ const SvxFontListItem* pFontListItem =
+ static_cast<const SvxFontListItem* >(pLclDocShell
+ ->GetItem( SID_ATTR_CHAR_FONTLIST ));
+ const FontList* pList = pFontListItem->GetFontList();
+ FontMetric aFontMetric = pList->Get(
+ sBulletFontName, WEIGHT_NORMAL, ITALIC_NONE);
+ vcl::Font aFont(aFontMetric);
+ aFormat.SetBulletFont(&aFont);
+ }
+ else if (pBulletFontName)
+ *pBulletFontName = sBulletFontName;
+ }
+ else if (rProp.Name == UNO_NAME_BULLET_CHAR)
+ {
+ OUString aChar;
+ rProp.Value >>= aChar;
+ if (aChar.isEmpty())
+ {
+ // If w:lvlText's value is null - set bullet char to zero
+ aFormat.SetBulletChar(u'\0');
+ }
+ else
+ {
+ sal_Int32 nIndexUtf16 = 0;
+ sal_UCS4 cBullet = aChar.iterateCodePoints(&nIndexUtf16);
+ if (aChar.getLength() == nIndexUtf16)
+ aFormat.SetBulletChar(cBullet);
+ else
+ bWrongArg = true;
+ }
+ }
+ else if (rProp.Name == UNO_NAME_GRAPHIC)
+ {
+ uno::Reference<graphic::XGraphic> xGraphic;
+ if (rProp.Value >>= xGraphic)
+ {
+ if (!pSetBrush)
+ {
+ const SvxBrushItem* pOrigBrush = aFormat.GetBrush();
+ if(pOrigBrush)
+ pSetBrush.reset(new SvxBrushItem(*pOrigBrush));
+ else
+ pSetBrush.reset(new SvxBrushItem(OUString(), OUString(), GPOS_AREA, RES_BACKGROUND));
+ }
+ Graphic aGraphic(xGraphic);
+ pSetBrush->SetGraphic(aGraphic);
+ }
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_GRAPHIC_BITMAP)
+ {
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (rProp.Value >>= xBitmap)
+ {
+ if(!pSetBrush)
+ {
+ const SvxBrushItem* pOrigBrush = aFormat.GetBrush();
+ if(pOrigBrush)
+ pSetBrush.reset(new SvxBrushItem(*pOrigBrush));
+ else
+ pSetBrush.reset(new SvxBrushItem(OUString(), OUString(), GPOS_AREA, RES_BACKGROUND));
+ }
+
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ Graphic aGraphic(xGraphic);
+ pSetBrush->SetGraphic(aGraphic);
+ }
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_GRAPHIC_SIZE)
+ {
+ if(!pSetSize)
+ pSetSize.reset(new Size);
+ awt::Size size;
+ if (rProp.Value >>= size)
+ {
+ pSetSize->setWidth(o3tl::toTwips(size.Width, o3tl::Length::mm100));
+ pSetSize->setHeight(o3tl::toTwips(size.Height, o3tl::Length::mm100));
+ }
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_VERT_ORIENT)
+ {
+ if(!pSetVOrient)
+ {
+ if(aFormat.GetGraphicOrientation())
+ pSetVOrient.reset(aFormat.GetGraphicOrientation()->Clone());
+ else
+ pSetVOrient.reset(new SwFormatVertOrient);
+ }
+ pSetVOrient->PutValue(rProp.Value, MID_VERTORIENT_ORIENT);
+ }
+ else if (rProp.Name == UNO_NAME_HEADING_STYLE_NAME
+ && pDocShell) // only on chapter numbering
+ {
+ if (pHeadingStyleName)
+ {
+ OUString uTmp;
+ rProp.Value >>= uTmp;
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName(uTmp, sStyleName, SwGetPoolIdFromName::TxtColl );
+ *pHeadingStyleName = sStyleName;
+ }
+ }
+ else if (rProp.Name == UNO_NAME_BULLET_REL_SIZE)
+ {
+ // BulletRelSize - unsupported - only available in Impress
+ }
+ else if (rProp.Name == UNO_NAME_BULLET_COLOR)
+ {
+ // BulletColor - ignored too
+ }
+ else if (rProp.Name == UNO_NAME_GRAPHIC_URL)
+ {
+ OUString aURL;
+ if (rProp.Value >>= aURL)
+ {
+ if(!pSetBrush)
+ {
+ const SvxBrushItem* pOrigBrush = aFormat.GetBrush();
+ if(pOrigBrush)
+ pSetBrush.reset(new SvxBrushItem(*pOrigBrush));
+ else
+ pSetBrush.reset(new SvxBrushItem(OUString(), OUString(), GPOS_AREA, RES_BACKGROUND));
+ }
+
+ Graphic aGraphic = vcl::graphic::loadFromURL(aURL);
+ if (!aGraphic.IsNone())
+ pSetBrush->SetGraphic(aGraphic);
+ }
+ else
+ bWrongArg = true;
+ }
+ else if (rProp.Name == UNO_NAME_LIST_FORMAT)
+ {
+ OUString uTmp;
+ rProp.Value >>= uTmp;
+ aFormat.SetListFormat(uTmp);
+ }
+ else
+ {
+ // Invalid property name
+ SAL_WARN("sw.uno", "Unknown/incorrect property " << rProp.Name << ", failing");
+ throw uno::RuntimeException("Unknown/incorrect property " + rProp.Name);
+ }
+ }
+ if(!bWrongArg && (pSetBrush || pSetSize || pSetVOrient))
+ {
+ if(!pSetBrush && aFormat.GetBrush())
+ pSetBrush.reset(new SvxBrushItem(*aFormat.GetBrush()));
+
+ if(pSetBrush)
+ {
+ if(!pSetVOrient && aFormat.GetGraphicOrientation())
+ pSetVOrient.reset( new SwFormatVertOrient(*aFormat.GetGraphicOrientation()) );
+
+ if(!pSetSize)
+ {
+ pSetSize.reset(new Size(aFormat.GetGraphicSize()));
+ if(!pSetSize->Width() || !pSetSize->Height())
+ {
+ const Graphic* pGraphic = pSetBrush->GetGraphic();
+ if(pGraphic)
+ *pSetSize = ::GetGraphicSizeTwip(*pGraphic, nullptr);
+ }
+ }
+ sal_Int16 eOrient = pSetVOrient ?
+ pSetVOrient->GetVertOrient() : text::VertOrientation::NONE;
+ aFormat.SetGraphicBrush( pSetBrush.get(), pSetSize.get(), text::VertOrientation::NONE == eOrient ? nullptr : &eOrient );
+ }
+ }
+ if ((!bCharStyleNameSet || rCharStyleName.isEmpty())
+ && aFormat.GetNumberingType() == NumberingType::BITMAP
+ && !aFormat.GetCharFormat()
+ && !SwXNumberingRules::isInvalidStyle(rCharStyleName))
+ {
+ OUString tmp;
+ SwStyleNameMapper::FillProgName(RES_POOLCHR_BULLET_LEVEL, tmp);
+ rCharStyleName = tmp;
+ }
+
+ if(bWrongArg)
+ throw lang::IllegalArgumentException();
+}
+
+uno::Reference< XPropertySetInfo > SwXNumberingRules::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef = m_pPropertySet->getPropertySetInfo();
+ return aRef;
+}
+
+void SwXNumberingRules::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ SolarMutexGuard aGuard;
+ std::unique_ptr<SwNumRule> pDocRule;
+ SwNumRule* pCreatedRule = nullptr;
+ if(!m_pNumRule)
+ {
+ if(m_pDocShell)
+ {
+ pDocRule.reset(new SwNumRule(*m_pDocShell->GetDoc()->GetOutlineNumRule()));
+ }
+ else if(m_pDoc && !m_sCreatedNumRuleName.isEmpty())
+ {
+ pCreatedRule = m_pDoc->FindNumRulePtr(m_sCreatedNumRuleName);
+ }
+
+ }
+ if(!m_pNumRule && !pDocRule && !pCreatedRule)
+ throw RuntimeException();
+
+ if(rPropertyName == UNO_NAME_IS_AUTOMATIC)
+ {
+ bool bVal = *o3tl::doAccess<bool>(rValue);
+ if(!pCreatedRule)
+ pDocRule ? pDocRule->SetAutoRule(bVal) : m_pNumRule->SetAutoRule(bVal);
+ }
+ else if(rPropertyName == UNO_NAME_IS_CONTINUOUS_NUMBERING)
+ {
+ bool bVal = *o3tl::doAccess<bool>(rValue);
+ pDocRule ? pDocRule->SetContinusNum(bVal) :
+ pCreatedRule ? pCreatedRule->SetContinusNum(bVal) : m_pNumRule->SetContinusNum(bVal);
+ }
+ else if(rPropertyName == UNO_NAME_NAME)
+ {
+ throw IllegalArgumentException();
+ }
+ else if(rPropertyName == UNO_NAME_IS_ABSOLUTE_MARGINS)
+ {
+ bool bVal = *o3tl::doAccess<bool>(rValue);
+ pDocRule ? pDocRule->SetAbsSpaces(bVal) :
+ pCreatedRule ? pCreatedRule->SetAbsSpaces(bVal) : m_pNumRule->SetAbsSpaces(bVal);
+ }
+ else if(rPropertyName == UNO_NAME_NUMBERING_IS_OUTLINE)
+ {
+ bool bVal = *o3tl::doAccess<bool>(rValue);
+ SwNumRuleType eNumRuleType = bVal ? OUTLINE_RULE : NUM_RULE;
+ pDocRule ? pDocRule->SetRuleType(eNumRuleType) :
+ pCreatedRule ? pCreatedRule->SetRuleType(eNumRuleType) : m_pNumRule->SetRuleType(eNumRuleType);
+ }
+ else if(rPropertyName == UNO_NAME_DEFAULT_LIST_ID)
+ {
+ throw IllegalArgumentException();
+ }
+ else
+ throw UnknownPropertyException(rPropertyName);
+
+ if(pDocRule)
+ {
+ assert(m_pDocShell);
+ m_pDocShell->GetDoc()->SetOutlineNumRule(*pDocRule);
+ pDocRule.reset();
+ }
+ else if(pCreatedRule)
+ {
+ pCreatedRule->Validate(*m_pDoc);
+ }
+}
+
+Any SwXNumberingRules::getPropertyValue( const OUString& rPropertyName )
+{
+ Any aRet;
+ const SwNumRule* pRule = m_pNumRule;
+ if(!pRule && m_pDocShell)
+ pRule = m_pDocShell->GetDoc()->GetOutlineNumRule();
+ else if(m_pDoc && !m_sCreatedNumRuleName.isEmpty())
+ pRule = m_pDoc->FindNumRulePtr( m_sCreatedNumRuleName );
+ if(!pRule)
+ throw RuntimeException();
+
+ if(rPropertyName == UNO_NAME_IS_AUTOMATIC)
+ {
+ aRet <<= pRule->IsAutoRule();
+ }
+ else if(rPropertyName == UNO_NAME_IS_CONTINUOUS_NUMBERING)
+ {
+ aRet <<= pRule->IsContinusNum();
+ }
+ else if(rPropertyName == UNO_NAME_NAME)
+ aRet <<= pRule->GetName();
+ else if(rPropertyName == UNO_NAME_IS_ABSOLUTE_MARGINS)
+ {
+ aRet <<= pRule->IsAbsSpaces();
+ }
+ else if(rPropertyName == UNO_NAME_NUMBERING_IS_OUTLINE)
+ {
+ aRet <<= pRule->IsOutlineRule();
+ }
+ else if(rPropertyName == UNO_NAME_DEFAULT_LIST_ID)
+ {
+ OSL_ENSURE( !pRule->GetDefaultListId().isEmpty(),
+ "<SwXNumberingRules::getPropertyValue(..)> - no default list id found. Serious defect." );
+ aRet <<= pRule->GetDefaultListId();
+ }
+ else
+ throw UnknownPropertyException(rPropertyName);
+ return aRet;
+}
+
+void SwXNumberingRules::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXNumberingRules::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXNumberingRules::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXNumberingRules::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/, const uno::Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+}
+
+OUString SwXNumberingRules::getName()
+{
+ if(m_pNumRule)
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName(m_pNumRule->GetName(), aString, SwGetPoolIdFromName::NumRule );
+ return aString;
+ }
+ // consider chapter numbering <SwXNumberingRules>
+ if ( m_pDocShell )
+ {
+ OUString aString;
+ SwStyleNameMapper::FillProgName( m_pDocShell->GetDoc()->GetOutlineNumRule()->GetName(),
+ aString, SwGetPoolIdFromName::NumRule );
+ return aString;
+ }
+ return m_sCreatedNumRuleName;
+}
+
+void SwXNumberingRules::setName(const OUString& /*rName*/)
+{
+ RuntimeException aExcept;
+ aExcept.Message = "readonly";
+ throw aExcept;
+}
+
+void SwXNumberingRules::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ if(m_rParent.m_bOwnNumRuleCreated)
+ delete m_rParent.m_pNumRule;
+ m_rParent.m_pNumRule = nullptr;
+ m_rParent.m_pDoc = nullptr;
+ }
+}
+
+OUString SwXChapterNumbering::getImplementationName()
+{
+ return "SwXChapterNumbering";
+}
+
+sal_Bool SwXChapterNumbering::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SwXChapterNumbering::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.ChapterNumbering", "com.sun.star.text.NumberingRules" };
+}
+
+SwXChapterNumbering::SwXChapterNumbering(SwDocShell& rDocSh) :
+ SwXNumberingRules(rDocSh)
+{
+}
+
+SwXChapterNumbering::~SwXChapterNumbering()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unosrch.cxx b/sw/source/core/unocore/unosrch.cxx
new file mode 100644
index 000000000..91ddb3432
--- /dev/null
+++ b/sw/source/core/unocore/unosrch.cxx
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <hintids.hxx>
+#include <unosrch.hxx>
+#include <unomap.hxx>
+#include <swtypes.hxx>
+
+#include <osl/diagnose.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/searchopt.hxx>
+#include <o3tl/any.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/itempool.hxx>
+#include <memory>
+#include <unordered_map>
+
+using namespace ::com::sun::star;
+
+class SwSearchProperties_Impl
+{
+ std::unordered_map<OUString, beans::PropertyValue> maValues;
+ SfxItemPropertyMap mrMap;
+
+ SwSearchProperties_Impl(const SwSearchProperties_Impl&) = delete;
+ SwSearchProperties_Impl& operator=(const SwSearchProperties_Impl&) = delete;
+
+public:
+ SwSearchProperties_Impl();
+
+ /// @throws beans::UnknownPropertyException
+ /// @throws lang::IllegalArgumentException
+ /// @throws uno::RuntimeException
+ void SetProperties(const uno::Sequence< beans::PropertyValue >& aSearchAttribs);
+ uno::Sequence< beans::PropertyValue > GetProperties() const;
+
+ void FillItemSet(SfxItemSet& rSet, bool bIsValueSearch) const;
+ bool HasAttributes() const;
+};
+
+SwSearchProperties_Impl::SwSearchProperties_Impl() :
+ mrMap( aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR)->getPropertyMap() )
+{
+}
+
+void SwSearchProperties_Impl::SetProperties(const uno::Sequence< beans::PropertyValue >& aSearchAttribs)
+{
+ //delete all existing values
+ maValues.clear();
+
+ for(const beans::PropertyValue& rSearchAttrib : aSearchAttribs)
+ {
+ const OUString& sName = rSearchAttrib.Name;
+ if( !mrMap.hasPropertyByName(sName) )
+ throw beans::UnknownPropertyException(sName);
+ maValues[sName] = rSearchAttrib;
+ }
+}
+
+uno::Sequence< beans::PropertyValue > SwSearchProperties_Impl::GetProperties() const
+{
+ uno::Sequence< beans::PropertyValue > aRet(maValues.size());
+ beans::PropertyValue* pProps = aRet.getArray();
+ sal_Int32 nPropCount = 0;
+ for(auto const & rPair : maValues)
+ {
+ pProps[nPropCount++] = rPair.second;
+ }
+ return aRet;
+}
+
+void SwSearchProperties_Impl::FillItemSet(SfxItemSet& rSet, bool bIsValueSearch) const
+{
+
+ std::unique_ptr<SfxPoolItem> pBoxItem,
+ pCharBoxItem,
+ pBreakItem,
+ pAutoKernItem ,
+ pWLineItem ,
+ pTabItem ,
+ pSplitItem ,
+ pRegItem ,
+ pLineSpaceItem ,
+ pLineNumItem ,
+ pKeepItem ,
+ pLRItem ,
+ pULItem ,
+ pBackItem ,
+ pAdjItem ,
+ pDescItem ,
+ pInetItem ,
+ pDropItem ,
+ pWeightItem ,
+ pULineItem ,
+ pOLineItem ,
+ pCharFormatItem ,
+ pShadItem ,
+ pPostItem ,
+ pNHyphItem ,
+ pLangItem ,
+ pKernItem ,
+ pFontSizeItem ,
+ pFontItem ,
+ pBlinkItem ,
+ pEscItem ,
+ pCrossedOutItem ,
+ pContourItem ,
+ pCharColorItem ,
+ pCasemapItem ,
+ pBrushItem ,
+ pFontCJKItem,
+ pFontSizeCJKItem,
+ pCJKLangItem,
+ pCJKPostureItem,
+ pCJKWeightItem,
+ pFontCTLItem,
+ pFontSizeCTLItem,
+ pCTLLangItem,
+ pCTLPostureItem,
+ pCTLWeightItem,
+ pShadowItem ;
+
+ auto funcClone = [&rSet](sal_uInt16 nWID, std::unique_ptr<SfxPoolItem> & rpPoolItem)
+ {
+ if(!rpPoolItem)
+ rpPoolItem.reset(rSet.GetPool()->GetDefaultItem(nWID).Clone());
+ return rpPoolItem.get();
+ };
+ for(auto const & rPair : maValues)
+ {
+ SfxPoolItem* pTempItem = nullptr;
+ const SfxItemPropertyMapEntry* pPropEntry = mrMap.getByName(rPair.first);
+ assert(pPropEntry && "SetProperties only enters values into maValues if mrMap.hasPropertyByName() wass true");
+ const SfxItemPropertyMapEntry & rPropEntry = *pPropEntry;
+ sal_uInt16 nWID = rPropEntry.nWID;
+ switch(nWID)
+ {
+ case RES_BOX:
+ pTempItem = funcClone(nWID, pBoxItem);
+ break;
+ case RES_CHRATR_BOX:
+ pTempItem = funcClone(nWID, pCharBoxItem);
+ break;
+ case RES_BREAK:
+ pTempItem = funcClone(nWID, pBreakItem);
+ break;
+ case RES_CHRATR_AUTOKERN:
+ pTempItem = funcClone(nWID, pAutoKernItem);
+ break;
+ case RES_CHRATR_BACKGROUND:
+ pTempItem = funcClone(nWID, pBrushItem);
+ break;
+ case RES_CHRATR_CASEMAP:
+ pTempItem = funcClone(nWID, pCasemapItem);
+ break;
+ case RES_CHRATR_COLOR:
+ pTempItem = funcClone(nWID, pCharColorItem);
+ break;
+ case RES_CHRATR_CONTOUR:
+ pTempItem = funcClone(nWID, pContourItem);
+ break;
+ case RES_CHRATR_CROSSEDOUT:
+ pTempItem = funcClone(nWID, pCrossedOutItem);
+ break;
+ case RES_CHRATR_ESCAPEMENT:
+ pTempItem = funcClone(nWID, pEscItem);
+ break;
+ case RES_CHRATR_BLINK:
+ pTempItem = funcClone(nWID, pBlinkItem);
+ break;
+ case RES_CHRATR_FONT:
+ pTempItem = funcClone(nWID, pFontItem);
+ break;
+ case RES_CHRATR_FONTSIZE:
+ pTempItem = funcClone(nWID, pFontSizeItem);
+ break;
+ case RES_CHRATR_KERNING:
+ pTempItem = funcClone(nWID, pKernItem);
+ break;
+ case RES_CHRATR_LANGUAGE:
+ pTempItem = funcClone(nWID, pLangItem);
+ break;
+ case RES_CHRATR_NOHYPHEN:
+ pTempItem = funcClone(nWID, pNHyphItem);
+ break;
+ case RES_CHRATR_POSTURE:
+ pTempItem = funcClone(nWID, pPostItem);
+ break;
+ case RES_CHRATR_SHADOWED:
+ pTempItem = funcClone(nWID, pShadItem);
+ break;
+ case RES_TXTATR_CHARFMT:
+ pTempItem = funcClone(nWID, pCharFormatItem);
+ break;
+ case RES_CHRATR_UNDERLINE:
+ pTempItem = funcClone(nWID, pULineItem);
+ break;
+ case RES_CHRATR_OVERLINE:
+ pTempItem = funcClone(nWID, pOLineItem);
+ break;
+ case RES_CHRATR_WEIGHT:
+ pTempItem = funcClone(nWID, pWeightItem);
+ break;
+ case RES_PARATR_DROP:
+ pTempItem = funcClone(nWID, pDropItem);
+ break;
+ case RES_TXTATR_INETFMT:
+ pTempItem = funcClone(nWID, pInetItem);
+ break;
+ case RES_PAGEDESC:
+ pTempItem = funcClone(nWID, pDescItem);
+ break;
+ case RES_PARATR_ADJUST:
+ pTempItem = funcClone(nWID, pAdjItem);
+ break;
+ case RES_BACKGROUND:
+ pTempItem = funcClone(nWID, pBackItem);
+ break;
+ case RES_UL_SPACE:
+ pTempItem = funcClone(nWID, pULItem);
+ break;
+ case RES_LR_SPACE:
+ pTempItem = funcClone(nWID, pLRItem);
+ break;
+ case RES_KEEP:
+ pTempItem = funcClone(nWID, pKeepItem);
+ break;
+ case RES_LINENUMBER:
+ pTempItem = funcClone(nWID, pLineNumItem);
+ break;
+ case RES_PARATR_LINESPACING:
+ pTempItem = funcClone(nWID, pLineSpaceItem);
+ break;
+ case RES_PARATR_REGISTER:
+ pTempItem = funcClone(nWID, pRegItem);
+ break;
+ case RES_PARATR_SPLIT:
+ pTempItem = funcClone(nWID, pSplitItem);
+ break;
+ case RES_PARATR_TABSTOP:
+ pTempItem = funcClone(nWID, pTabItem);
+ break;
+ case RES_CHRATR_WORDLINEMODE:
+ pTempItem = funcClone(nWID, pWLineItem);
+ break;
+ case RES_CHRATR_CJK_FONT:
+ pTempItem = funcClone(nWID, pFontCJKItem);
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ pTempItem = funcClone(nWID, pFontSizeCJKItem);
+ break;
+ case RES_CHRATR_CJK_LANGUAGE:
+ pTempItem = funcClone(nWID, pCJKLangItem);
+ break;
+ case RES_CHRATR_CJK_POSTURE:
+ pTempItem = funcClone(nWID, pCJKPostureItem);
+ break;
+ case RES_CHRATR_CJK_WEIGHT:
+ pTempItem = funcClone(nWID, pCJKWeightItem);
+ break;
+ case RES_CHRATR_CTL_FONT:
+ pTempItem = funcClone(nWID, pFontCTLItem);
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ pTempItem = funcClone(nWID, pFontSizeCTLItem);
+ break;
+ case RES_CHRATR_CTL_LANGUAGE:
+ pTempItem = funcClone(nWID, pCTLLangItem);
+ break;
+ case RES_CHRATR_CTL_POSTURE:
+ pTempItem = funcClone(nWID, pCTLPostureItem);
+ break;
+ case RES_CHRATR_CTL_WEIGHT:
+ pTempItem = funcClone(nWID, pCTLWeightItem);
+ break;
+ case RES_CHRATR_SHADOW:
+ pTempItem = funcClone(nWID, pShadowItem);
+ break;
+ }
+ if(pTempItem)
+ {
+ if(bIsValueSearch)
+ {
+ pTempItem->PutValue(rPair.second.Value, rPropEntry.nMemberId);
+ rSet.Put(*pTempItem);
+ }
+ else
+ rSet.InvalidateItem( pTempItem->Which() );
+ }
+ }
+}
+
+bool SwSearchProperties_Impl::HasAttributes() const
+{
+ return !maValues.empty();
+}
+
+SwXTextSearch::SwXTextSearch() :
+ m_pSearchProperties( new SwSearchProperties_Impl),
+ m_pReplaceProperties( new SwSearchProperties_Impl),
+ m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_SEARCH)),
+ m_bAll(false),
+ m_bWord(false),
+ m_bBack(false),
+ m_bExpr(false),
+ m_bCase(false),
+ m_bStyles(false),
+ m_bSimilarity(false),
+ m_bLevRelax(false),
+ m_nLevExchange(2),
+ m_nLevAdd(2),
+ m_nLevRemove(2),
+ m_bIsValueSearch(true)
+{
+}
+
+SwXTextSearch::~SwXTextSearch()
+{
+ m_pSearchProperties.reset();
+ m_pReplaceProperties.reset();
+}
+
+namespace
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwXTextSearch::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextSearchUnoTunnelId;
+ return theSwXTextSearchUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXTextSearch::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+OUString SwXTextSearch::getSearchString()
+{
+ SolarMutexGuard aGuard;
+ return m_sSearchText;
+}
+
+void SwXTextSearch::setSearchString(const OUString& rString)
+{
+ SolarMutexGuard aGuard;
+ m_sSearchText = rString;
+}
+
+OUString SwXTextSearch::getReplaceString()
+{
+ SolarMutexGuard aGuard;
+ return m_sReplaceText;
+}
+
+void SwXTextSearch::setReplaceString(const OUString& rReplaceString)
+{
+ SolarMutexGuard aGuard;
+ m_sReplaceText = rReplaceString;
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXTextSearch::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > aRef = m_pPropSet->getPropertySetInfo();
+ return aRef;
+}
+
+void SwXTextSearch::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException ("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ bool bVal = false;
+ if(auto b = o3tl::tryAccess<bool>(aValue))
+ bVal = *b;
+ switch(pEntry->nWID)
+ {
+ case WID_SEARCH_ALL : m_bAll = bVal; break;
+ case WID_WORDS: m_bWord = bVal; break;
+ case WID_BACKWARDS : m_bBack = bVal; break;
+ case WID_REGULAR_EXPRESSION : m_bExpr = bVal; break;
+ case WID_CASE_SENSITIVE : m_bCase = bVal; break;
+ //case WID_IN_SELECTION : bInSel = bVal; break;
+ case WID_STYLES : m_bStyles = bVal; break;
+ case WID_SIMILARITY : m_bSimilarity = bVal; break;
+ case WID_SIMILARITY_RELAX: m_bLevRelax = bVal; break;
+ case WID_SIMILARITY_EXCHANGE: aValue >>= m_nLevExchange; break;
+ case WID_SIMILARITY_ADD: aValue >>= m_nLevAdd; break;
+ case WID_SIMILARITY_REMOVE : aValue >>= m_nLevRemove;break;
+ };
+
+}
+
+uno::Any SwXTextSearch::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ const SfxItemPropertyMapEntry* pEntry = m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ bool bSet = false;
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ sal_Int16 nSet = 0;
+ switch(pEntry->nWID)
+ {
+ case WID_SEARCH_ALL : bSet = m_bAll; goto SET_BOOL;
+ case WID_WORDS: bSet = m_bWord; goto SET_BOOL;
+ case WID_BACKWARDS : bSet = m_bBack; goto SET_BOOL;
+ case WID_REGULAR_EXPRESSION : bSet = m_bExpr; goto SET_BOOL;
+ case WID_CASE_SENSITIVE : bSet = m_bCase; goto SET_BOOL;
+ //case WID_IN_SELECTION : bSet = bInSel; goto SET_BOOL;
+ case WID_STYLES : bSet = m_bStyles; goto SET_BOOL;
+ case WID_SIMILARITY : bSet = m_bSimilarity; goto SET_BOOL;
+ case WID_SIMILARITY_RELAX: bSet = m_bLevRelax;
+SET_BOOL:
+ aRet <<= bSet;
+ break;
+ case WID_SIMILARITY_EXCHANGE: nSet = m_nLevExchange; goto SET_UINT16;
+ case WID_SIMILARITY_ADD: nSet = m_nLevAdd; goto SET_UINT16;
+ case WID_SIMILARITY_REMOVE : nSet = m_nLevRemove;
+SET_UINT16:
+ aRet <<= nSet;
+ break;
+ }
+
+ return aRet;
+}
+
+void SwXTextSearch::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextSearch::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextSearch::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+void SwXTextSearch::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+{
+ OSL_FAIL("not implemented");
+}
+
+sal_Bool SwXTextSearch::getValueSearch()
+{
+ SolarMutexGuard aGuard;
+ return m_bIsValueSearch;
+}
+
+void SwXTextSearch::setValueSearch(sal_Bool ValueSearch_)
+{
+ SolarMutexGuard aGuard;
+ m_bIsValueSearch = ValueSearch_;
+}
+
+uno::Sequence< beans::PropertyValue > SwXTextSearch::getSearchAttributes()
+{
+ return m_pSearchProperties->GetProperties();
+}
+
+void SwXTextSearch::setSearchAttributes(const uno::Sequence< beans::PropertyValue >& rSearchAttribs)
+{
+ m_pSearchProperties->SetProperties(rSearchAttribs);
+}
+
+uno::Sequence< beans::PropertyValue > SwXTextSearch::getReplaceAttributes()
+{
+ return m_pReplaceProperties->GetProperties();
+}
+
+void SwXTextSearch::setReplaceAttributes(const uno::Sequence< beans::PropertyValue >& rReplaceAttribs)
+{
+ m_pReplaceProperties->SetProperties(rReplaceAttribs);
+}
+
+void SwXTextSearch::FillSearchItemSet(SfxItemSet& rSet) const
+{
+ m_pSearchProperties->FillItemSet(rSet, m_bIsValueSearch);
+}
+
+void SwXTextSearch::FillReplaceItemSet(SfxItemSet& rSet) const
+{
+ m_pReplaceProperties->FillItemSet(rSet, m_bIsValueSearch);
+}
+
+bool SwXTextSearch::HasSearchAttributes() const
+{
+ return m_pSearchProperties->HasAttributes();
+}
+
+bool SwXTextSearch::HasReplaceAttributes() const
+{
+ return m_pReplaceProperties->HasAttributes();
+}
+
+OUString SwXTextSearch::getImplementationName()
+{
+ return "SwXTextSearch";
+}
+
+sal_Bool SwXTextSearch::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXTextSearch::getSupportedServiceNames()
+{
+ return { "com.sun.star.util.SearchDescriptor", "com.sun.star.util.ReplaceDescriptor" };
+}
+
+void SwXTextSearch::FillSearchOptions( i18nutil::SearchOptions2& rSearchOpt ) const
+{
+ if( m_bSimilarity )
+ {
+ rSearchOpt.algorithmType = util::SearchAlgorithms_APPROXIMATE;
+ rSearchOpt.AlgorithmType2 = util::SearchAlgorithms2::APPROXIMATE;
+ rSearchOpt.changedChars = m_nLevExchange;
+ rSearchOpt.deletedChars = m_nLevRemove;
+ rSearchOpt.insertedChars = m_nLevAdd;
+ if( m_bLevRelax )
+ rSearchOpt.searchFlag |= util::SearchFlags::LEV_RELAXED;
+ }
+ else if( m_bExpr )
+ {
+ rSearchOpt.algorithmType = util::SearchAlgorithms_REGEXP;
+ rSearchOpt.AlgorithmType2 = util::SearchAlgorithms2::REGEXP;
+ }
+ else
+ {
+ rSearchOpt.algorithmType = util::SearchAlgorithms_ABSOLUTE;
+ rSearchOpt.AlgorithmType2 = util::SearchAlgorithms2::ABSOLUTE;
+ }
+
+ rSearchOpt.Locale = GetAppLanguageTag().getLocale();
+ rSearchOpt.searchString = m_sSearchText;
+ rSearchOpt.replaceString = m_sReplaceText;
+
+ if( !m_bCase )
+ rSearchOpt.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ if( m_bWord )
+ rSearchOpt.searchFlag |= util::SearchFlags::NORM_WORD_ONLY;
+
+// bInSel: 1; // How is that possible?
+// TODO: pSearch->bStyles!
+// inSelection??
+// aSrchParam.SetSrchInSelection(TypeConversion::toBOOL(aVal));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unostyle.cxx b/sw/source/core/unocore/unostyle.cxx
new file mode 100644
index 000000000..66832979b
--- /dev/null
+++ b/sw/source/core/unocore/unostyle.cxx
@@ -0,0 +1,5645 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <hintids.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/hint.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svl/style.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/listener.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <svx/pageitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/paperinf.hxx>
+#include <editeng/wghtitem.hxx>
+#include <pagedesc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <docary.hxx>
+#include <charfmt.hxx>
+#include <cmdid.h>
+#include <unomid.h>
+#include <unomap.hxx>
+#include <unostyle.hxx>
+#include <unosett.hxx>
+#include <docsh.hxx>
+#include <paratr.hxx>
+#include <unoprnms.hxx>
+#include <shellio.hxx>
+#include <docstyle.hxx>
+#include <unotextbodyhf.hxx>
+#include <fmthdft.hxx>
+#include <fmtpdsc.hxx>
+#include <strings.hrc>
+#include <poolfmt.hxx>
+#include <unoevent.hxx>
+#include <fmtruby.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <sfx2/printer.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/style/ParagraphStyleCategory.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/drawing/BitmapMode.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/document/XEventsSupplier.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <istyleaccess.hxx>
+#include <fmtfsize.hxx>
+#include <numrule.hxx>
+#include <tblafmt.hxx>
+#include <frameformats.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+
+#include <svl/stylepool.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <editeng/unoipset.hxx>
+#include <editeng/memberids.h>
+#include <svx/unomid.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <swunohelper.hxx>
+#include <svx/xbtmpit.hxx>
+
+#include <ccoll.hxx>
+#include <hints.hxx>
+#include <uiitems.hxx>
+
+#include <cassert>
+#include <memory>
+#include <set>
+#include <string_view>
+#include <limits>
+
+using namespace css;
+using namespace css::io;
+using namespace css::lang;
+using namespace css::uno;
+
+namespace {
+
+class SwXStyle;
+class SwStyleProperties_Impl;
+
+ struct StyleFamilyEntry
+ {
+ using GetCountOrName_t = std::function<sal_Int32 (const SwDoc&, OUString*, sal_Int32)>;
+ using CreateStyle_t = std::function<uno::Reference<css::style::XStyle>(SfxStyleSheetBasePool*, SwDocShell*, const OUString&)>;
+ using TranslateIndex_t = std::function<sal_uInt16(const sal_uInt16)>;
+ SfxStyleFamily m_eFamily;
+ sal_uInt16 m_nPropMapType;
+ uno::Reference<beans::XPropertySetInfo> m_xPSInfo;
+ SwGetPoolIdFromName m_aPoolId;
+ OUString m_sName;
+ TranslateId m_pResId;
+ GetCountOrName_t m_fGetCountOrName;
+ CreateStyle_t m_fCreateStyle;
+ TranslateIndex_t m_fTranslateIndex;
+ StyleFamilyEntry(SfxStyleFamily eFamily, sal_uInt16 nPropMapType, SwGetPoolIdFromName aPoolId, OUString const& sName, TranslateId pResId, GetCountOrName_t const & fGetCountOrName, CreateStyle_t const & fCreateStyle, TranslateIndex_t const & fTranslateIndex)
+ : m_eFamily(eFamily)
+ , m_nPropMapType(nPropMapType)
+ , m_xPSInfo(aSwMapProvider.GetPropertySet(nPropMapType)->getPropertySetInfo())
+ , m_aPoolId(aPoolId)
+ , m_sName(sName)
+ , m_pResId(pResId)
+ , m_fGetCountOrName(fGetCountOrName)
+ , m_fCreateStyle(fCreateStyle)
+ , m_fTranslateIndex(fTranslateIndex)
+ { }
+ };
+ const std::vector<StyleFamilyEntry>* our_pStyleFamilyEntries;
+ // these should really be constexprs, but MSVC still is apparently too stupid for them
+ #define nPoolChrNormalRange (RES_POOLCHR_NORMAL_END - RES_POOLCHR_NORMAL_BEGIN)
+ #define nPoolChrHtmlRange (RES_POOLCHR_HTML_END - RES_POOLCHR_HTML_BEGIN)
+ #define nPoolCollTextRange ( RES_POOLCOLL_TEXT_END - RES_POOLCOLL_TEXT_BEGIN)
+ #define nPoolCollListsRange ( RES_POOLCOLL_LISTS_END - RES_POOLCOLL_LISTS_BEGIN)
+ #define nPoolCollExtraRange ( RES_POOLCOLL_EXTRA_END - RES_POOLCOLL_EXTRA_BEGIN)
+ #define nPoolCollRegisterRange ( RES_POOLCOLL_REGISTER_END - RES_POOLCOLL_REGISTER_BEGIN)
+ #define nPoolCollDocRange ( RES_POOLCOLL_DOC_END - RES_POOLCOLL_DOC_BEGIN)
+ #define nPoolCollHtmlRange ( RES_POOLCOLL_HTML_END - RES_POOLCOLL_HTML_BEGIN)
+ #define nPoolFrameRange ( RES_POOLFRM_END - RES_POOLFRM_BEGIN)
+ #define nPoolPageRange ( RES_POOLPAGE_END - RES_POOLPAGE_BEGIN)
+ #define nPoolNumRange ( RES_POOLNUMRULE_END - RES_POOLNUMRULE_BEGIN)
+ #define nPoolCollListsStackedStart ( nPoolCollTextRange)
+ #define nPoolCollExtraStackedStart ( nPoolCollListsStackedStart + nPoolCollListsRange)
+ #define nPoolCollRegisterStackedStart ( nPoolCollExtraStackedStart + nPoolCollExtraRange)
+ #define nPoolCollDocStackedStart ( nPoolCollRegisterStackedStart + nPoolCollRegisterRange)
+ #define nPoolCollHtmlStackedStart ( nPoolCollDocStackedStart + nPoolCollDocRange)
+ using paragraphstyle_t = std::remove_const<decltype(style::ParagraphStyleCategory::TEXT)>::type;
+ using collectionbits_t = sal_uInt16;
+ struct ParagraphStyleCategoryEntry
+ {
+ paragraphstyle_t m_eCategory;
+ SfxStyleSearchBits m_nSwStyleBits;
+ collectionbits_t m_nCollectionBits;
+ ParagraphStyleCategoryEntry(paragraphstyle_t eCategory, SfxStyleSearchBits nSwStyleBits, collectionbits_t nCollectionBits)
+ : m_eCategory(eCategory)
+ , m_nSwStyleBits(nSwStyleBits)
+ , m_nCollectionBits(nCollectionBits)
+ { }
+ };
+ const std::vector<ParagraphStyleCategoryEntry>* our_pParagraphStyleCategoryEntries;
+}
+static const std::vector<StyleFamilyEntry>* lcl_GetStyleFamilyEntries();
+
+using namespace ::com::sun::star;
+
+namespace sw
+{
+ namespace {
+
+ class XStyleFamily : public cppu::WeakImplHelper
+ <
+ container::XNameContainer,
+ lang::XServiceInfo,
+ container::XIndexAccess,
+ beans::XPropertySet
+ >
+ , public SfxListener
+ {
+ const StyleFamilyEntry& m_rEntry;
+ SfxStyleSheetBasePool* m_pBasePool;
+ SwDocShell* m_pDocShell;
+
+ SwXStyle* FindStyle(std::u16string_view rStyleName) const;
+ sal_Int32 GetCountOrName(OUString* pString, sal_Int32 nIndex = SAL_MAX_INT32)
+ { return m_rEntry.m_fGetCountOrName(*m_pDocShell->GetDoc(), pString, nIndex); };
+ static const StyleFamilyEntry& InitEntry(SfxStyleFamily eFamily)
+ {
+ auto pEntries = lcl_GetStyleFamilyEntries();
+ const auto pEntry = std::find_if(pEntries->begin(), pEntries->end(),
+ [eFamily] (const StyleFamilyEntry& e) { return e.m_eFamily == eFamily; });
+ assert(pEntry != pEntries->end());
+ return *pEntry;
+ }
+ public:
+ XStyleFamily(SwDocShell* pDocShell, const SfxStyleFamily eFamily)
+ : m_rEntry(InitEntry(eFamily))
+ , m_pBasePool(pDocShell->GetStyleSheetPool())
+ , m_pDocShell(pDocShell)
+ {
+ if (m_pBasePool) //tdf#124142 html docs can have no styles
+ StartListening(*m_pBasePool);
+ }
+
+ //XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override
+ {
+ SolarMutexGuard aGuard;
+ return GetCountOrName(nullptr);
+ };
+ virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override;
+
+ //XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override
+ { return cppu::UnoType<style::XStyle>::get(); };
+ virtual sal_Bool SAL_CALL hasElements( ) override
+ {
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ return true;
+ }
+
+ //XNameAccess
+ virtual uno::Any SAL_CALL getByName(const OUString& Name) override;
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName(const OUString& Name) override;
+
+ //XNameContainer
+ virtual void SAL_CALL insertByName(const OUString& Name, const uno::Any& Element) override;
+ virtual void SAL_CALL replaceByName(const OUString& Name, const uno::Any& Element) override;
+ virtual void SAL_CALL removeByName(const OUString& Name) override;
+
+ //XPropertySet
+ virtual uno::Reference< beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override
+ { return {}; };
+ virtual void SAL_CALL setPropertyValue( const OUString&, const uno::Any&) override
+ { SAL_WARN("sw.uno", "###unexpected!"); };
+ virtual uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString&, const uno::Reference<beans::XPropertyChangeListener>&) override
+ { SAL_WARN("sw.uno", "###unexpected!"); };
+ virtual void SAL_CALL removePropertyChangeListener( const OUString&, const uno::Reference<beans::XPropertyChangeListener>&) override
+ { SAL_WARN("sw.uno", "###unexpected!"); };
+ virtual void SAL_CALL addVetoableChangeListener(const OUString&, const uno::Reference<beans::XVetoableChangeListener>&) override
+ { SAL_WARN("sw.uno", "###unexpected!"); };
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString&, const uno::Reference<beans::XVetoableChangeListener>&) override
+ { SAL_WARN("sw.uno", "###unexpected!"); };
+
+ //SfxListener
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override
+ {
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pBasePool = nullptr;
+ m_pDocShell = nullptr;
+ EndListening(rBC);
+ }
+ }
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ { return {"XStyleFamily"}; };
+ virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override
+ { return cppu::supportsService(this, rServiceName); };
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
+ { return { "com.sun.star.style.StyleFamily" }; }
+ };
+
+ }
+}
+
+namespace {
+
+class SwStyleBase_Impl;
+class SwXStyle : public cppu::WeakImplHelper
+ <
+ css::style::XStyle,
+ css::beans::XPropertySet,
+ css::beans::XMultiPropertySet,
+ css::lang::XServiceInfo,
+ css::lang::XUnoTunnel,
+ css::beans::XPropertyState,
+ css::beans::XMultiPropertyStates
+ >
+ , public SfxListener
+ , public SvtListener
+{
+ SwDoc* m_pDoc;
+ OUString m_sStyleName;
+ const StyleFamilyEntry& m_rEntry;
+ bool m_bIsDescriptor;
+ bool m_bIsConditional;
+ OUString m_sParentStyleName;
+
+protected:
+ SfxStyleSheetBasePool* m_pBasePool;
+ std::unique_ptr<SwStyleProperties_Impl> m_pPropertiesImpl;
+ css::uno::Reference<css::container::XNameAccess> m_xStyleFamily;
+ css::uno::Reference<css::beans::XPropertySet> m_xStyleData;
+
+ template<sal_uInt16>
+ void SetPropertyValue(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any&, SwStyleBase_Impl&);
+ void SetPropertyValues_Impl( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues );
+ SfxStyleSheetBase* GetStyleSheetBase();
+ void PrepareStyleBase(SwStyleBase_Impl& rBase);
+ template<sal_uInt16>
+ uno::Any GetStyleProperty(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase);
+ uno::Any GetStyleProperty_Impl(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase);
+ uno::Any GetPropertyValue_Impl(const SfxItemPropertySet* pPropSet, SwStyleBase_Impl& rBase, const OUString& rPropertyName);
+
+public:
+ SwXStyle(SwDoc* pDoc, SfxStyleFamily eFam, bool bConditional = false);
+ SwXStyle(SfxStyleSheetBasePool* pPool, SfxStyleFamily eFamily, SwDoc* pDoc, const OUString& rStyleName);
+ virtual ~SwXStyle() override;
+
+
+ static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId();
+
+ //XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+ //XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName(const OUString& Name_) override;
+
+ //XStyle
+ virtual sal_Bool SAL_CALL isUserDefined() override;
+ virtual sal_Bool SAL_CALL isInUse() override;
+ virtual OUString SAL_CALL getParentStyle() override;
+ virtual void SAL_CALL setParentStyle(const OUString& aParentStyle) override;
+
+ //XPropertySet
+ virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& ) override
+ { OSL_FAIL("not implemented"); };
+ virtual void SAL_CALL removePropertyChangeListener( const OUString&, const css::uno::Reference< css::beans::XPropertyChangeListener >& ) override
+ { OSL_FAIL("not implemented"); };
+ virtual void SAL_CALL addVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& ) override
+ { OSL_FAIL("not implemented"); };
+ virtual void SAL_CALL removeVetoableChangeListener( const OUString&, const css::uno::Reference< css::beans::XVetoableChangeListener >& ) override
+ { OSL_FAIL("not implemented"); };
+
+ //XMultiPropertySet
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >&, const css::uno::Reference< css::beans::XPropertiesChangeListener >& ) override
+ {};
+ virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& ) override
+ {};
+ virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >&, const css::uno::Reference< css::beans::XPropertiesChangeListener >& ) override
+ {};
+
+ //XPropertyState
+ virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override;
+ virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( const css::uno::Sequence< OUString >& aPropertyName ) override;
+ virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override;
+ virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override;
+
+ //XMultiPropertyStates
+ virtual void SAL_CALL setAllPropertiesToDefault( ) override;
+ virtual void SAL_CALL setPropertiesToDefault( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyDefaults( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+
+ //XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override
+ { return {"SwXStyle"}; };
+ virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override
+ { return cppu::supportsService(this, rServiceName); };
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ //SfxListener
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ //SvtListener
+ virtual void Notify(const SfxHint&) override;
+ const OUString& GetStyleName() const { return m_sStyleName;}
+ SfxStyleFamily GetFamily() const {return m_rEntry.m_eFamily;}
+
+ bool IsDescriptor() const {return m_bIsDescriptor;}
+ bool IsConditional() const { return m_bIsConditional;}
+ const OUString& GetParentStyleName() const { return m_sParentStyleName;}
+ void SetDoc(SwDoc* pDc, SfxStyleSheetBasePool* pPool)
+ {
+ m_bIsDescriptor = false; m_pDoc = pDc;
+ m_pBasePool = pPool;
+ SfxListener::StartListening(*m_pBasePool);
+ }
+ SwDoc* GetDoc() const { return m_pDoc; }
+ void Invalidate();
+ void ApplyDescriptorProperties();
+ void SetStyleName(const OUString& rSet){ m_sStyleName = rSet;}
+ /// @throws beans::PropertyVetoException
+ /// @throws lang::IllegalArgumentException
+ /// @throws lang::WrappedTargetException
+ /// @throws uno::RuntimeException
+ void SetStyleProperty(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& rBase);
+ void PutItemToSet(const SvxSetItem* pSetItem, const SfxItemPropertySet& rPropSet, const SfxItemPropertyMapEntry& rEntry, const uno::Any& rVal, SwStyleBase_Impl& rBaseImpl);
+};
+
+class SwXFrameStyle
+ : public SwXStyle
+ , public css::document::XEventsSupplier
+ , public sw::ICoreFrameStyle
+{
+public:
+ SwXFrameStyle(SfxStyleSheetBasePool& rPool,
+ SwDoc* pDoc,
+ const OUString& rStyleName) :
+ SwXStyle(&rPool, SfxStyleFamily::Frame, pDoc, rStyleName){}
+ explicit SwXFrameStyle(SwDoc *pDoc);
+
+ virtual void SAL_CALL acquire( ) noexcept override {SwXStyle::acquire();}
+ virtual void SAL_CALL release( ) noexcept override {SwXStyle::release();}
+
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override;
+
+ //ICoreStyle
+ virtual void SetItem(sal_uInt16 eAtr, const SfxPoolItem& rItem) override;
+ virtual const SfxPoolItem* GetItem(sal_uInt16 eAtr) override;
+ virtual css::document::XEventsSupplier& GetEventsSupplier() override
+ { return *this; };
+};
+
+class SwXPageStyle
+ : public SwXStyle
+{
+protected:
+ void SetPropertyValues_Impl( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues );
+ css::uno::Sequence< css::uno::Any > GetPropertyValues_Impl( const css::uno::Sequence< OUString >& aPropertyNames );
+
+public:
+ SwXPageStyle(SfxStyleSheetBasePool& rPool, SwDocShell* pDocSh, const OUString& rStyleName);
+ explicit SwXPageStyle(SwDocShell* pDocSh);
+
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+ virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+};
+
+}
+
+using sw::XStyleFamily;
+
+OUString SwXStyleFamilies::getImplementationName()
+ { return {"SwXStyleFamilies"}; }
+
+sal_Bool SwXStyleFamilies::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SwXStyleFamilies::getSupportedServiceNames()
+ { return { "com.sun.star.style.StyleFamilies" }; }
+
+SwXStyleFamilies::SwXStyleFamilies(SwDocShell& rDocShell) :
+ SwUnoCollection(rDocShell.GetDoc()),
+ m_pDocShell(&rDocShell)
+ { }
+
+SwXStyleFamilies::~SwXStyleFamilies()
+ { }
+
+uno::Any SAL_CALL SwXStyleFamilies::getByName(const OUString& Name)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ throw uno::RuntimeException();
+ auto pEntries(lcl_GetStyleFamilyEntries());
+ const auto pEntry = std::find_if(pEntries->begin(), pEntries->end(),
+ [&Name] (const StyleFamilyEntry& e) { return e.m_sName == Name; });
+ if(pEntry == pEntries->end())
+ throw container::NoSuchElementException();
+ return getByIndex(pEntry-pEntries->begin());
+}
+
+uno::Sequence< OUString > SwXStyleFamilies::getElementNames()
+{
+ auto pEntries(lcl_GetStyleFamilyEntries());
+ uno::Sequence<OUString> aNames(pEntries->size());
+ std::transform(pEntries->begin(), pEntries->end(),
+ aNames.getArray(), [] (const StyleFamilyEntry& e) { return e.m_sName; });
+ return aNames;
+}
+
+sal_Bool SwXStyleFamilies::hasByName(const OUString& Name)
+{
+ auto pEntries(lcl_GetStyleFamilyEntries());
+ return std::any_of(pEntries->begin(), pEntries->end(),
+ [&Name] (const StyleFamilyEntry& e) { return e.m_sName == Name; });
+}
+
+sal_Int32 SwXStyleFamilies::getCount()
+{
+ return lcl_GetStyleFamilyEntries()->size();
+}
+
+uno::Any SwXStyleFamilies::getByIndex(sal_Int32 nIndex)
+{
+ auto pEntries(lcl_GetStyleFamilyEntries());
+ SolarMutexGuard aGuard;
+ if(nIndex < 0 || o3tl::make_unsigned(nIndex) >= pEntries->size())
+ throw lang::IndexOutOfBoundsException();
+ if(!IsValid())
+ throw uno::RuntimeException();
+ auto eFamily = (*pEntries)[nIndex].m_eFamily;
+ assert(eFamily != SfxStyleFamily::All);
+ auto& rxFamily = m_vFamilies[eFamily];
+ if(!rxFamily.is())
+ rxFamily = new XStyleFamily(m_pDocShell, eFamily);
+ return uno::Any(rxFamily);
+}
+
+uno::Type SwXStyleFamilies::getElementType()
+{
+ return cppu::UnoType<container::XNameContainer>::get();
+}
+
+sal_Bool SwXStyleFamilies::hasElements()
+ { return true; }
+
+void SwXStyleFamilies::loadStylesFromURL(const OUString& rURL,
+ const uno::Sequence< beans::PropertyValue >& aOptions)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid() || rURL.isEmpty())
+ throw uno::RuntimeException();
+ SwgReaderOption aOpt;
+ aOpt.SetFrameFormats(true);
+ aOpt.SetTextFormats(true);
+ aOpt.SetPageDescs(true);
+ aOpt.SetNumRules(true);
+ aOpt.SetMerge(false);
+ for(const auto& rProperty: aOptions)
+ {
+ bool bValue = false;
+ if(rProperty.Value.getValueType() == cppu::UnoType<bool>::get())
+ bValue = rProperty.Value.get<bool>();
+
+ if(rProperty.Name == UNO_NAME_OVERWRITE_STYLES)
+ aOpt.SetMerge(!bValue);
+ else if(rProperty.Name == UNO_NAME_LOAD_NUMBERING_STYLES)
+ aOpt.SetNumRules(bValue);
+ else if(rProperty.Name == UNO_NAME_LOAD_PAGE_STYLES)
+ aOpt.SetPageDescs(bValue);
+ else if(rProperty.Name == UNO_NAME_LOAD_FRAME_STYLES)
+ aOpt.SetFrameFormats(bValue);
+ else if(rProperty.Name == UNO_NAME_LOAD_TEXT_STYLES)
+ aOpt.SetTextFormats(bValue);
+ else if(rProperty.Name == "InputStream")
+ {
+ Reference<XInputStream> xInputStream;
+ if (!(rProperty.Value >>= xInputStream))
+ throw IllegalArgumentException("Parameter 'InputStream' could not be converted to "
+ "type 'com::sun::star::io::XInputStream'",
+ nullptr, 0);
+
+ aOpt.SetInputStream(xInputStream);
+
+ }
+ }
+ const ErrCode nErr = m_pDocShell->LoadStylesFromFile( rURL, aOpt, true );
+ if(nErr)
+ throw io::IOException();
+}
+
+uno::Sequence< beans::PropertyValue > SwXStyleFamilies::getStyleLoaderOptions()
+{
+ const uno::Any aVal(true);
+ return comphelper::InitPropertySequence({
+ { UNO_NAME_LOAD_TEXT_STYLES, aVal },
+ { UNO_NAME_LOAD_FRAME_STYLES, aVal },
+ { UNO_NAME_LOAD_PAGE_STYLES, aVal },
+ { UNO_NAME_LOAD_NUMBERING_STYLES, aVal },
+ { UNO_NAME_OVERWRITE_STYLES, aVal }
+ });
+}
+
+static bool lcl_GetHeaderFooterItem(
+ SfxItemSet const& rSet, std::u16string_view rPropName, bool const bFooter,
+ SvxSetItem const*& o_rpItem)
+{
+ o_rpItem = rSet.GetItemIfSet(
+ bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET,
+ false);
+ if (!o_rpItem &&
+ rPropName == u"" UNO_NAME_FIRST_IS_SHARED)
+ { // fdo#79269 header may not exist, check footer then
+ o_rpItem = rSet.GetItemIfSet(
+ (!bFooter) ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET,
+ false);
+ }
+ return o_rpItem;
+}
+
+template<enum SfxStyleFamily>
+static sal_Int32 lcl_GetCountOrName(const SwDoc&, OUString*, sal_Int32);
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Char>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ const sal_uInt16 nBaseCount = nPoolChrHtmlRange + nPoolChrNormalRange;
+ nIndex -= nBaseCount;
+ sal_Int32 nCount = 0;
+ for(auto pFormat : *rDoc.GetCharFormats())
+ {
+ if(pFormat->IsDefault() && pFormat != rDoc.GetDfltCharFormat())
+ continue;
+ if(!IsPoolUserFormat(pFormat->GetPoolFormatId()))
+ continue;
+ if(nIndex == nCount)
+ {
+ // the default character format needs to be set to "Default!"
+ if(rDoc.GetDfltCharFormat() == pFormat)
+ *pString = SwResId(STR_POOLCHR_STANDARD);
+ else
+ *pString = pFormat->GetName();
+ break;
+ }
+ ++nCount;
+ }
+ return nCount + nBaseCount;
+}
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Para>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ const sal_uInt16 nBaseCount = nPoolCollHtmlStackedStart + nPoolCollHtmlRange;
+ nIndex -= nBaseCount;
+ sal_Int32 nCount = 0;
+ for(auto pColl : *rDoc.GetTextFormatColls())
+ {
+ if(pColl->IsDefault())
+ continue;
+ if(!IsPoolUserFormat(pColl->GetPoolFormatId()))
+ continue;
+ if(nIndex == nCount)
+ {
+ *pString = pColl->GetName();
+ break;
+ }
+ ++nCount;
+ }
+ return nCount + nBaseCount;
+}
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Frame>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ nIndex -= nPoolFrameRange;
+ sal_Int32 nCount = 0;
+ for(const auto pFormat : *rDoc.GetFrameFormats())
+ {
+ if(pFormat->IsDefault() || pFormat->IsAuto())
+ continue;
+ if(!IsPoolUserFormat(pFormat->GetPoolFormatId()))
+ continue;
+ if(nIndex == nCount)
+ {
+ *pString = pFormat->GetName();
+ break;
+ }
+ ++nCount;
+ }
+ return nCount + nPoolFrameRange;
+}
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Page>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ nIndex -= nPoolPageRange;
+ sal_Int32 nCount = 0;
+ const size_t nArrLen = rDoc.GetPageDescCnt();
+ for(size_t i = 0; i < nArrLen; ++i)
+ {
+ const SwPageDesc& rDesc = rDoc.GetPageDesc(i);
+ if(!IsPoolUserFormat(rDesc.GetPoolFormatId()))
+ continue;
+ if(nIndex == nCount)
+ {
+ *pString = rDesc.GetName();
+ break;
+ }
+ ++nCount;
+ }
+ nCount += nPoolPageRange;
+ return nCount;
+}
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Pseudo>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ nIndex -= nPoolNumRange;
+ sal_Int32 nCount = 0;
+ for(const auto pRule : rDoc.GetNumRuleTable())
+ {
+ if(pRule->IsAutoRule())
+ continue;
+ if(!IsPoolUserFormat(pRule->GetPoolFormatId()))
+ continue;
+ if(nIndex == nCount)
+ {
+ *pString = pRule->GetName();
+ break;
+ }
+ ++nCount;
+ }
+ return nCount + nPoolNumRange;
+}
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Table>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ if (!rDoc.HasTableStyles())
+ return 0;
+
+ const auto pAutoFormats = &rDoc.GetTableStyles();
+ const sal_Int32 nCount = pAutoFormats->size();
+ if (0 <= nIndex && nIndex < nCount)
+ *pString = pAutoFormats->operator[](nIndex).GetName();
+
+ return nCount;
+}
+
+template<>
+sal_Int32 lcl_GetCountOrName<SfxStyleFamily::Cell>(const SwDoc& rDoc, OUString* pString, sal_Int32 nIndex)
+{
+ const auto& rAutoFormats = rDoc.GetTableStyles();
+ const auto& rTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap();
+ const sal_Int32 nUsedCellStylesCount = rAutoFormats.size() * rTableTemplateMap.size();
+ const sal_Int32 nCount = nUsedCellStylesCount + rDoc.GetCellStyles().size();
+ if (0 <= nIndex && nIndex < nCount)
+ {
+ if (nUsedCellStylesCount > nIndex)
+ {
+ const sal_Int32 nAutoFormat = nIndex / rTableTemplateMap.size();
+ const sal_Int32 nBoxFormat = rTableTemplateMap[nIndex % rTableTemplateMap.size()];
+ const SwTableAutoFormat& rTableFormat = rAutoFormats[nAutoFormat];
+ SwStyleNameMapper::FillProgName(rTableFormat.GetName(), *pString, SwGetPoolIdFromName::TabStyle);
+ *pString += rTableFormat.GetTableTemplateCellSubName(rTableFormat.GetBoxFormat(nBoxFormat));
+ }
+ else
+ *pString = rDoc.GetCellStyles()[nIndex-nUsedCellStylesCount].GetName();
+ }
+ return nCount;
+}
+
+template<SfxStyleFamily eFamily>
+static uno::Reference< css::style::XStyle> lcl_CreateStyle(SfxStyleSheetBasePool* pBasePool, SwDocShell* pDocShell, const OUString& sStyleName)
+ { return pBasePool ? new SwXStyle(pBasePool, eFamily, pDocShell->GetDoc(), sStyleName) : new SwXStyle(pDocShell->GetDoc(), eFamily, false); };
+
+template<>
+uno::Reference< css::style::XStyle> lcl_CreateStyle<SfxStyleFamily::Para>(SfxStyleSheetBasePool* pBasePool, SwDocShell* pDocShell, const OUString& sStyleName)
+ { return pBasePool ? new SwXStyle(pBasePool, SfxStyleFamily::Para, pDocShell->GetDoc(), sStyleName) : new SwXStyle(pDocShell->GetDoc(), SfxStyleFamily::Para, false); };
+template<>
+uno::Reference< css::style::XStyle> lcl_CreateStyle<SfxStyleFamily::Frame>(SfxStyleSheetBasePool* pBasePool, SwDocShell* pDocShell, const OUString& sStyleName)
+ { return pBasePool ? new SwXFrameStyle(*pBasePool, pDocShell->GetDoc(), sStyleName) : new SwXFrameStyle(pDocShell->GetDoc()); };
+
+template<>
+uno::Reference< css::style::XStyle> lcl_CreateStyle<SfxStyleFamily::Page>(SfxStyleSheetBasePool* pBasePool, SwDocShell* pDocShell, const OUString& sStyleName)
+ { return pBasePool ? new SwXPageStyle(*pBasePool, pDocShell, sStyleName) : new SwXPageStyle(pDocShell); };
+
+template<>
+uno::Reference< css::style::XStyle> lcl_CreateStyle<SfxStyleFamily::Table>(SfxStyleSheetBasePool* /*pBasePool*/, SwDocShell* pDocShell, const OUString& sStyleName)
+ { return SwXTextTableStyle::CreateXTextTableStyle(pDocShell, sStyleName); };
+
+template<>
+uno::Reference< css::style::XStyle> lcl_CreateStyle<SfxStyleFamily::Cell>(SfxStyleSheetBasePool* /*pBasePool*/, SwDocShell* pDocShell, const OUString& sStyleName)
+ { return SwXTextCellStyle::CreateXTextCellStyle(pDocShell, sStyleName); };
+
+uno::Reference<css::style::XStyle> SwXStyleFamilies::CreateStyle(SfxStyleFamily eFamily, SwDoc& rDoc)
+{
+ auto pEntries(lcl_GetStyleFamilyEntries());
+ const auto pEntry = std::find_if(pEntries->begin(), pEntries->end(),
+ [eFamily] (const StyleFamilyEntry& e) { return e.m_eFamily == eFamily; });
+ return pEntry == pEntries->end() ? nullptr : pEntry->m_fCreateStyle(nullptr, rDoc.GetDocShell(), "");
+}
+
+// FIXME: Ugly special casing that should die.
+uno::Reference<css::style::XStyle> SwXStyleFamilies::CreateStyleCondParagraph(SwDoc& rDoc)
+ { return new SwXStyle(&rDoc, SfxStyleFamily::Para, true); };
+
+template<enum SfxStyleFamily>
+static sal_uInt16 lcl_TranslateIndex(const sal_uInt16 nIndex);
+
+template<>
+sal_uInt16 lcl_TranslateIndex<SfxStyleFamily::Char>(const sal_uInt16 nIndex)
+{
+ static_assert(nPoolChrNormalRange > 0 && nPoolChrHtmlRange > 0, "invalid pool range");
+ if(nIndex < nPoolChrNormalRange)
+ return nIndex + RES_POOLCHR_NORMAL_BEGIN;
+ else if(nIndex < (nPoolChrHtmlRange+nPoolChrNormalRange))
+ return nIndex + RES_POOLCHR_HTML_BEGIN - nPoolChrNormalRange;
+ throw lang::IndexOutOfBoundsException();
+}
+
+template<>
+sal_uInt16 lcl_TranslateIndex<SfxStyleFamily::Para>(const sal_uInt16 nIndex)
+{
+ static_assert(nPoolCollTextRange > 0 && nPoolCollListsRange > 0 && nPoolCollExtraRange > 0 && nPoolCollRegisterRange > 0 && nPoolCollDocRange > 0 && nPoolCollHtmlRange > 0, "weird pool range");
+ if(nIndex < nPoolCollListsStackedStart)
+ return nIndex + RES_POOLCOLL_TEXT_BEGIN;
+ else if(nIndex < nPoolCollExtraStackedStart)
+ return nIndex + RES_POOLCOLL_LISTS_BEGIN - nPoolCollListsStackedStart;
+ else if(nIndex < nPoolCollRegisterStackedStart)
+ return nIndex + RES_POOLCOLL_EXTRA_BEGIN - nPoolCollExtraStackedStart;
+ else if(nIndex < nPoolCollDocStackedStart)
+ return nIndex + RES_POOLCOLL_REGISTER_BEGIN - nPoolCollRegisterStackedStart;
+ else if(nIndex < nPoolCollHtmlStackedStart)
+ return nIndex + RES_POOLCOLL_DOC_BEGIN - nPoolCollDocStackedStart;
+ else if(nIndex < nPoolCollHtmlStackedStart + nPoolCollTextRange)
+ return nIndex + RES_POOLCOLL_HTML_BEGIN - nPoolCollHtmlStackedStart;
+ throw lang::IndexOutOfBoundsException();
+}
+
+template<>
+sal_uInt16 lcl_TranslateIndex<SfxStyleFamily::Table>(const sal_uInt16 nIndex)
+{
+ return nIndex;
+}
+
+template<>
+sal_uInt16 lcl_TranslateIndex<SfxStyleFamily::Cell>(const sal_uInt16 nIndex)
+{
+ return nIndex;
+}
+
+template<sal_uInt16 nRangeBegin, sal_uInt16 nRangeSize>
+static sal_uInt16 lcl_TranslateIndexRange(const sal_uInt16 nIndex)
+{
+ if(nIndex < nRangeSize)
+ return nIndex + nRangeBegin;
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Any XStyleFamily::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ OUString sStyleName;
+ try
+ {
+ SwStyleNameMapper::FillUIName(m_rEntry.m_fTranslateIndex(nIndex), sStyleName);
+ } catch(...) {}
+ if (sStyleName.isEmpty())
+ GetCountOrName(&sStyleName, nIndex);
+ if(sStyleName.isEmpty())
+ throw lang::IndexOutOfBoundsException();
+ return getByName(sStyleName);
+}
+
+uno::Any XStyleFamily::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName(rName, sStyleName, m_rEntry.m_aPoolId);
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(sStyleName, m_rEntry.m_eFamily);
+ if(!pBase)
+ throw container::NoSuchElementException(rName);
+ uno::Reference<style::XStyle> xStyle = FindStyle(sStyleName);
+ if(!xStyle.is())
+ xStyle = m_rEntry.m_fCreateStyle(m_pBasePool, m_pDocShell, m_rEntry.m_eFamily == SfxStyleFamily::Frame ? pBase->GetName() : sStyleName);
+ return uno::Any(xStyle);
+}
+
+uno::Sequence<OUString> XStyleFamily::getElementNames()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ std::vector<OUString> vRet;
+ std::unique_ptr<SfxStyleSheetIterator> pIt = m_pBasePool->CreateIterator(m_rEntry.m_eFamily);
+ for (SfxStyleSheetBase* pStyle = pIt->First(); pStyle; pStyle = pIt->Next())
+ {
+ OUString sName;
+ SwStyleNameMapper::FillProgName(pStyle->GetName(), sName, m_rEntry.m_aPoolId);
+ vRet.push_back(sName);
+ }
+ return comphelper::containerToSequence(vRet);
+}
+
+sal_Bool XStyleFamily::hasByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName(rName, sStyleName, m_rEntry.m_aPoolId);
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(sStyleName, m_rEntry.m_eFamily);
+ return nullptr != pBase;
+}
+
+void XStyleFamily::insertByName(const OUString& rName, const uno::Any& rElement)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName(rName, sStyleName, m_rEntry.m_aPoolId);
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(sStyleName, m_rEntry.m_eFamily);
+ SfxStyleSheetBase* pUINameBase = m_pBasePool->Find(sStyleName, m_rEntry.m_eFamily);
+ if(pBase || pUINameBase)
+ throw container::ElementExistException();
+ if(rElement.getValueType().getTypeClass() != uno::TypeClass_INTERFACE)
+ throw lang::IllegalArgumentException();
+ if (SwGetPoolIdFromName::CellStyle == m_rEntry.m_aPoolId)
+ {
+ // handle cell style
+ uno::Reference<style::XStyle> xStyle = rElement.get<uno::Reference<style::XStyle>>();
+ SwXTextCellStyle* pNewStyle = dynamic_cast<SwXTextCellStyle*>(xStyle.get());
+ if (!pNewStyle)
+ throw lang::IllegalArgumentException();
+
+ pNewStyle->setName(sStyleName); // insertByName sets the element name
+ m_pDocShell->GetDoc()->GetCellStyles().AddBoxFormat(*pNewStyle->GetBoxFormat(), sStyleName);
+ pNewStyle->SetPhysical();
+ }
+ else if (SwGetPoolIdFromName::TabStyle == m_rEntry.m_aPoolId)
+ {
+ // handle table style
+ uno::Reference<style::XStyle> xStyle = rElement.get<uno::Reference<style::XStyle>>();
+ SwXTextTableStyle* pNewStyle = dynamic_cast<SwXTextTableStyle*>(xStyle.get());
+ if (!pNewStyle)
+ throw lang::IllegalArgumentException();
+
+ pNewStyle->setName(sStyleName); // insertByName sets the element name
+ m_pDocShell->GetDoc()->GetTableStyles().AddAutoFormat(*pNewStyle->GetTableFormat());
+ pNewStyle->SetPhysical();
+ }
+ else
+ {
+ uno::Reference<lang::XUnoTunnel> xStyleTunnel = rElement.get<uno::Reference<lang::XUnoTunnel>>();
+ SwXStyle* pNewStyle = comphelper::getFromUnoTunnel<SwXStyle>(xStyleTunnel);
+ if (!pNewStyle || !pNewStyle->IsDescriptor() || pNewStyle->GetFamily() != m_rEntry.m_eFamily)
+ throw lang::IllegalArgumentException();
+
+ SfxStyleSearchBits nMask = SfxStyleSearchBits::All;
+ if(m_rEntry.m_eFamily == SfxStyleFamily::Para && !pNewStyle->IsConditional())
+ nMask &= ~SfxStyleSearchBits::SwCondColl;
+ m_pBasePool->Make(sStyleName, m_rEntry.m_eFamily, nMask);
+ pNewStyle->SetDoc(m_pDocShell->GetDoc(), m_pBasePool);
+ pNewStyle->SetStyleName(sStyleName);
+ const OUString sParentStyleName(pNewStyle->GetParentStyleName());
+ if (!sParentStyleName.isEmpty())
+ {
+ SfxStyleSheetBase* pParentBase = m_pBasePool->Find(sParentStyleName, m_rEntry.m_eFamily);
+ if(pParentBase && pParentBase->GetFamily() == m_rEntry.m_eFamily &&
+ pParentBase->GetPool() == m_pBasePool)
+ m_pBasePool->SetParent(m_rEntry.m_eFamily, sStyleName, sParentStyleName);
+ }
+ // after all, we still need to apply the properties of the descriptor
+ pNewStyle->ApplyDescriptorProperties();
+ }
+}
+
+void XStyleFamily::replaceByName(const OUString& rName, const uno::Any& rElement)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ OUString sStyleName;
+ SwStyleNameMapper::FillUIName(rName, sStyleName, m_rEntry.m_aPoolId);
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(sStyleName, m_rEntry.m_eFamily);
+ // replacements only for userdefined styles
+ if(!pBase)
+ throw container::NoSuchElementException();
+ if (SwGetPoolIdFromName::CellStyle == m_rEntry.m_aPoolId)
+ {
+ // handle cell styles, don't call on assigned cell styles (TableStyle child)
+ OUString sParent;
+ SwBoxAutoFormat* pBoxAutoFormat = SwXTextCellStyle::GetBoxAutoFormat(m_pDocShell, sStyleName, &sParent);
+ if (pBoxAutoFormat && sParent.isEmpty())// if parent exists then this style is assigned to a table style. Don't replace.
+ {
+ uno::Reference<style::XStyle> xStyle = rElement.get<uno::Reference<style::XStyle>>();
+ SwXTextCellStyle* pStyleToReplaceWith = dynamic_cast<SwXTextCellStyle*>(xStyle.get());
+ if (!pStyleToReplaceWith)
+ throw lang::IllegalArgumentException();
+
+ pStyleToReplaceWith->setName(sStyleName);
+ *pBoxAutoFormat = *pStyleToReplaceWith->GetBoxFormat();
+ pStyleToReplaceWith->SetPhysical();
+ }
+ }
+ else if (SwGetPoolIdFromName::TabStyle == m_rEntry.m_aPoolId)
+ {
+ // handle table styles
+ SwTableAutoFormat* pTableAutoFormat = SwXTextTableStyle::GetTableAutoFormat(m_pDocShell, sStyleName);
+ if (pTableAutoFormat)
+ {
+ uno::Reference<style::XStyle> xStyle = rElement.get<uno::Reference<style::XStyle>>();
+ SwXTextTableStyle* pStyleToReplaceWith = dynamic_cast<SwXTextTableStyle*>(xStyle.get());
+ if (!pStyleToReplaceWith)
+ throw lang::IllegalArgumentException();
+
+ pStyleToReplaceWith->setName(sStyleName);
+ *pTableAutoFormat = *pStyleToReplaceWith->GetTableFormat();
+ pStyleToReplaceWith->SetPhysical();
+ }
+ }
+ else
+ {
+ if(!pBase->IsUserDefined())
+ throw lang::IllegalArgumentException();
+ //if there's an object available to this style then it must be invalidated
+ uno::Reference<style::XStyle> xStyle = FindStyle(pBase->GetName());
+ if(xStyle.is())
+ {
+ SwXStyle* pStyle = comphelper::getFromUnoTunnel<SwXStyle>(xStyle);
+ if(pStyle)
+ pStyle->Invalidate();
+ }
+ m_pBasePool->Remove(pBase);
+ insertByName(rName, rElement);
+ }
+}
+
+void XStyleFamily::removeByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ OUString sName;
+ SwStyleNameMapper::FillUIName(rName, sName, m_rEntry.m_aPoolId);
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(sName, m_rEntry.m_eFamily);
+ if(!pBase)
+ throw container::NoSuchElementException();
+ if (SwGetPoolIdFromName::CellStyle == m_rEntry.m_aPoolId)
+ {
+ // handle cell style
+ m_pDocShell->GetDoc()->GetCellStyles().RemoveBoxFormat(rName);
+ }
+ else if (SwGetPoolIdFromName::TabStyle == m_rEntry.m_aPoolId)
+ {
+ // handle table style
+ m_pDocShell->GetDoc()->GetTableStyles().EraseAutoFormat(rName);
+ }
+ else
+ m_pBasePool->Remove(pBase);
+}
+
+uno::Any SAL_CALL XStyleFamily::getPropertyValue( const OUString& sPropertyName )
+{
+ if(sPropertyName != "DisplayName")
+ throw beans::UnknownPropertyException( "unknown property: " + sPropertyName, static_cast<OWeakObject *>(this) );
+ SolarMutexGuard aGuard;
+ return uno::Any(SwResId(m_rEntry.m_pResId));
+}
+
+
+SwXStyle* XStyleFamily::FindStyle(std::u16string_view rStyleName) const
+{
+ const size_t nLCount = m_pBasePool->GetSizeOfVector();
+ for(size_t i = 0; i < nLCount; ++i)
+ {
+ SfxListener* pListener = m_pBasePool->GetListener(i);
+ SwXStyle* pTempStyle = dynamic_cast<SwXStyle*>(pListener);
+ if(pTempStyle && pTempStyle->GetFamily() == m_rEntry.m_eFamily && pTempStyle->GetStyleName() == rStyleName)
+ return pTempStyle;
+ }
+ return nullptr;
+}
+
+static const std::vector<StyleFamilyEntry>* lcl_GetStyleFamilyEntries()
+{
+ if(!our_pStyleFamilyEntries)
+ {
+ our_pStyleFamilyEntries = new std::vector<StyleFamilyEntry>{
+ { SfxStyleFamily::Char, PROPERTY_MAP_CHAR_STYLE, SwGetPoolIdFromName::ChrFmt, "CharacterStyles", STR_STYLE_FAMILY_CHARACTER, &lcl_GetCountOrName<SfxStyleFamily::Char>, &lcl_CreateStyle<SfxStyleFamily::Char>, &lcl_TranslateIndex<SfxStyleFamily::Char> },
+ { SfxStyleFamily::Para, PROPERTY_MAP_PARA_STYLE, SwGetPoolIdFromName::TxtColl, "ParagraphStyles", STR_STYLE_FAMILY_PARAGRAPH, &lcl_GetCountOrName<SfxStyleFamily::Para>, &lcl_CreateStyle<SfxStyleFamily::Para>, &lcl_TranslateIndex<SfxStyleFamily::Para> },
+ { SfxStyleFamily::Page, PROPERTY_MAP_PAGE_STYLE, SwGetPoolIdFromName::PageDesc, "PageStyles", STR_STYLE_FAMILY_PAGE, &lcl_GetCountOrName<SfxStyleFamily::Page>, &lcl_CreateStyle<SfxStyleFamily::Page>, &lcl_TranslateIndexRange<RES_POOLPAGE_BEGIN, nPoolPageRange> },
+ { SfxStyleFamily::Frame, PROPERTY_MAP_FRAME_STYLE, SwGetPoolIdFromName::FrmFmt, "FrameStyles", STR_STYLE_FAMILY_FRAME, &lcl_GetCountOrName<SfxStyleFamily::Frame>, &lcl_CreateStyle<SfxStyleFamily::Frame>, &lcl_TranslateIndexRange<RES_POOLFRM_BEGIN, nPoolFrameRange> },
+ { SfxStyleFamily::Pseudo, PROPERTY_MAP_NUM_STYLE, SwGetPoolIdFromName::NumRule, "NumberingStyles", STR_STYLE_FAMILY_NUMBERING, &lcl_GetCountOrName<SfxStyleFamily::Pseudo>, &lcl_CreateStyle<SfxStyleFamily::Pseudo>, &lcl_TranslateIndexRange<RES_POOLNUMRULE_BEGIN, nPoolNumRange> },
+ { SfxStyleFamily::Table, PROPERTY_MAP_TABLE_STYLE, SwGetPoolIdFromName::TabStyle, "TableStyles", STR_STYLE_FAMILY_TABLE, &lcl_GetCountOrName<SfxStyleFamily::Table>, &lcl_CreateStyle<SfxStyleFamily::Table>, &lcl_TranslateIndex<SfxStyleFamily::Table> },
+ { SfxStyleFamily::Cell, PROPERTY_MAP_CELL_STYLE, SwGetPoolIdFromName::CellStyle,"CellStyles", STR_STYLE_FAMILY_CELL, &lcl_GetCountOrName<SfxStyleFamily::Cell>, &lcl_CreateStyle<SfxStyleFamily::Cell>, &lcl_TranslateIndex<SfxStyleFamily::Cell> }
+ };
+ }
+ return our_pStyleFamilyEntries;
+}
+
+static const std::vector<ParagraphStyleCategoryEntry>* lcl_GetParagraphStyleCategoryEntries()
+{
+ if(!our_pParagraphStyleCategoryEntries)
+ {
+ our_pParagraphStyleCategoryEntries = new std::vector<ParagraphStyleCategoryEntry>{
+ { style::ParagraphStyleCategory::TEXT, SfxStyleSearchBits::SwText, COLL_TEXT_BITS },
+ { style::ParagraphStyleCategory::CHAPTER, SfxStyleSearchBits::SwChapter, COLL_DOC_BITS },
+ { style::ParagraphStyleCategory::LIST, SfxStyleSearchBits::SwList, COLL_LISTS_BITS },
+ { style::ParagraphStyleCategory::INDEX, SfxStyleSearchBits::SwIndex, COLL_REGISTER_BITS },
+ { style::ParagraphStyleCategory::EXTRA, SfxStyleSearchBits::SwExtra, COLL_EXTRA_BITS },
+ { style::ParagraphStyleCategory::HTML, SfxStyleSearchBits::SwHtml, COLL_HTML_BITS }
+ };
+ }
+ return our_pParagraphStyleCategoryEntries;
+}
+
+namespace {
+
+class SwStyleProperties_Impl
+{
+ const SfxItemPropertyMap& mrMap;
+ std::map<OUString, uno::Any> m_vPropertyValues;
+public:
+ explicit SwStyleProperties_Impl(const SfxItemPropertyMap& rMap)
+ : mrMap(rMap)
+ { }
+
+ bool AllowsKey(std::u16string_view rName)
+ {
+ return mrMap.hasPropertyByName(rName);
+ }
+ bool SetProperty(const OUString& rName, const uno::Any& rValue)
+ {
+ if(!AllowsKey(rName))
+ return false;
+ m_vPropertyValues[rName] = rValue;
+ return true;
+ }
+ void GetProperty(const OUString& rName, const uno::Any*& pAny)
+ {
+ if(!AllowsKey(rName))
+ {
+ pAny = nullptr;
+ return;
+ }
+ pAny = &m_vPropertyValues[rName];
+ return;
+ }
+ bool ClearProperty( const OUString& rName )
+ {
+ if(!AllowsKey(rName))
+ return false;
+ m_vPropertyValues[rName] = uno::Any();
+ return true;
+ }
+ void ClearAllProperties( )
+ { m_vPropertyValues.clear(); }
+ void Apply(SwXStyle& rStyle)
+ {
+ for(const auto& rPropertyPair : m_vPropertyValues)
+ {
+ if(rPropertyPair.second.hasValue())
+ rStyle.setPropertyValue(rPropertyPair.first, rPropertyPair.second);
+ }
+ }
+ static void GetProperty(const OUString &rPropertyName, const uno::Reference < beans::XPropertySet > &rxPropertySet, uno::Any& rAny )
+ {
+ rAny = rxPropertySet->getPropertyValue( rPropertyName );
+ }
+};
+
+}
+
+static SwGetPoolIdFromName lcl_GetSwEnumFromSfxEnum(SfxStyleFamily eFamily)
+{
+ auto pEntries(lcl_GetStyleFamilyEntries());
+ const auto pEntry = std::find_if(pEntries->begin(), pEntries->end(),
+ [eFamily] (const StyleFamilyEntry& e) { return e.m_eFamily == eFamily; });
+ if(pEntry != pEntries->end())
+ return pEntry->m_aPoolId;
+ SAL_WARN("sw.uno", "someone asking for all styles in unostyle.cxx!" );
+ return SwGetPoolIdFromName::ChrFmt;
+}
+
+namespace
+{
+}
+
+const uno::Sequence<sal_Int8>& SwXStyle::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXStyleUnoTunnelId;
+ return theSwXStyleUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXStyle::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+
+uno::Sequence< OUString > SwXStyle::getSupportedServiceNames()
+{
+ tools::Long nCount = 1;
+ if(SfxStyleFamily::Para == m_rEntry.m_eFamily)
+ {
+ nCount = 5;
+ if(m_bIsConditional)
+ nCount++;
+ }
+ else if(SfxStyleFamily::Char == m_rEntry.m_eFamily)
+ nCount = 5;
+ else if(SfxStyleFamily::Page == m_rEntry.m_eFamily)
+ nCount = 3;
+ uno::Sequence< OUString > aRet(nCount);
+ OUString* pArray = aRet.getArray();
+ pArray[0] = "com.sun.star.style.Style";
+ switch(m_rEntry.m_eFamily)
+ {
+ case SfxStyleFamily::Char:
+ pArray[1] = "com.sun.star.style.CharacterStyle";
+ pArray[2] = "com.sun.star.style.CharacterProperties";
+ pArray[3] = "com.sun.star.style.CharacterPropertiesAsian";
+ pArray[4] = "com.sun.star.style.CharacterPropertiesComplex";
+ break;
+ case SfxStyleFamily::Page:
+ pArray[1] = "com.sun.star.style.PageStyle";
+ pArray[2] = "com.sun.star.style.PageProperties";
+ break;
+ case SfxStyleFamily::Para:
+ pArray[1] = "com.sun.star.style.ParagraphStyle";
+ pArray[2] = "com.sun.star.style.ParagraphProperties";
+ pArray[3] = "com.sun.star.style.ParagraphPropertiesAsian";
+ pArray[4] = "com.sun.star.style.ParagraphPropertiesComplex";
+ if(m_bIsConditional)
+ pArray[5] = "com.sun.star.style.ConditionalParagraphStyle";
+ break;
+
+ default:
+ ;
+ }
+ return aRet;
+}
+
+static uno::Reference<beans::XPropertySet> lcl_InitStandardStyle(const SfxStyleFamily eFamily, uno::Reference<container::XNameAccess> const & rxStyleFamily)
+{
+ using return_t = decltype(lcl_InitStandardStyle(eFamily, rxStyleFamily));
+ if(eFamily != SfxStyleFamily::Para && eFamily != SfxStyleFamily::Page)
+ return {};
+ auto aResult(rxStyleFamily->getByName("Standard"));
+ if(!aResult.has<return_t>())
+ return {};
+ return aResult.get<return_t>();
+}
+
+static uno::Reference<container::XNameAccess> lcl_InitStyleFamily(SwDoc* pDoc, const StyleFamilyEntry& rEntry)
+{
+ using return_t = decltype(lcl_InitStyleFamily(pDoc, rEntry));
+ if(rEntry.m_eFamily != SfxStyleFamily::Char
+ && rEntry.m_eFamily != SfxStyleFamily::Para
+ && rEntry.m_eFamily != SfxStyleFamily::Page)
+ return {};
+ auto xModel(pDoc->GetDocShell()->GetBaseModel());
+ uno::Reference<style::XStyleFamiliesSupplier> xFamilySupplier(xModel, uno::UNO_QUERY);
+ auto xFamilies = xFamilySupplier->getStyleFamilies();
+ auto aResult(xFamilies->getByName(rEntry.m_sName));
+ if(!aResult.has<return_t>())
+ return {};
+ return aResult.get<return_t>();
+}
+
+static bool lcl_InitConditional(SfxStyleSheetBasePool* pBasePool, const SfxStyleFamily eFamily, const OUString& rStyleName)
+{
+ if(!pBasePool || eFamily != SfxStyleFamily::Para)
+ return false;
+ SfxStyleSheetBase* pBase = pBasePool->Find(rStyleName, eFamily);
+ SAL_WARN_IF(!pBase, "sw.uno", "where is the style?" );
+ if(!pBase)
+ return false;
+ const sal_uInt16 nId(SwStyleNameMapper::GetPoolIdFromUIName(rStyleName, SwGetPoolIdFromName::TxtColl));
+ if(nId != USHRT_MAX)
+ return ::IsConditionalByPoolId(nId);
+ return RES_CONDTXTFMTCOLL == static_cast<SwDocStyleSheet*>(pBase)->GetCollection()->Which();
+}
+
+static const StyleFamilyEntry& lcl_GetStyleEntry(const SfxStyleFamily eFamily)
+{
+ auto pEntries = lcl_GetStyleFamilyEntries();
+ const auto pEntry = std::find_if(pEntries->begin(), pEntries->end(),
+ [eFamily] (const StyleFamilyEntry& e) { return e.m_eFamily == eFamily; });
+ assert(pEntry != pEntries->end());
+ return *pEntry;
+}
+
+SwXStyle::SwXStyle(SwDoc* pDoc, SfxStyleFamily eFamily, bool bConditional)
+ : m_pDoc(pDoc)
+ , m_rEntry(lcl_GetStyleEntry(eFamily))
+ , m_bIsDescriptor(true)
+ , m_bIsConditional(bConditional)
+ , m_pBasePool(nullptr)
+ , m_xStyleFamily(lcl_InitStyleFamily(pDoc, m_rEntry))
+ , m_xStyleData(lcl_InitStandardStyle(eFamily, m_xStyleFamily))
+{
+ assert(!m_bIsConditional || m_rEntry.m_eFamily == SfxStyleFamily::Para); // only paragraph styles are conditional
+ // Register ourselves as a listener to the document (via the page descriptor)
+ SvtListener::StartListening(pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+ m_pPropertiesImpl = std::make_unique<SwStyleProperties_Impl>(
+ aSwMapProvider.GetPropertySet(m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType)->getPropertyMap());
+}
+
+SwXStyle::SwXStyle(SfxStyleSheetBasePool* pPool, SfxStyleFamily eFamily, SwDoc* pDoc, const OUString& rStyleName)
+ : m_pDoc(pDoc)
+ , m_sStyleName(rStyleName)
+ , m_rEntry(lcl_GetStyleEntry(eFamily))
+ , m_bIsDescriptor(false)
+ , m_bIsConditional(lcl_InitConditional(pPool, eFamily, rStyleName))
+ , m_pBasePool(pPool)
+{ }
+
+SwXStyle::~SwXStyle()
+{
+ SolarMutexGuard aGuard;
+ if(m_pBasePool)
+ SfxListener::EndListening(*m_pBasePool);
+ m_pPropertiesImpl.reset();
+ SvtListener::EndListeningAll();
+}
+
+void SwXStyle::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pDoc = nullptr;
+ m_xStyleData.clear();
+ m_xStyleFamily.clear();
+ }
+}
+
+OUString SwXStyle::getName()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ return m_sStyleName;
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ SAL_WARN_IF(!pBase, "sw.uno", "where is the style?");
+ if(!pBase)
+ throw uno::RuntimeException();
+ OUString aString;
+ SwStyleNameMapper::FillProgName(pBase->GetName(), aString, lcl_GetSwEnumFromSfxEnum ( m_rEntry.m_eFamily ));
+ return aString;
+}
+
+void SwXStyle::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ {
+ m_sStyleName = rName;
+ return;
+ }
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ SAL_WARN_IF(!pBase, "sw.uno", "where is the style?");
+ if(!pBase || !pBase->IsUserDefined())
+ throw uno::RuntimeException();
+ rtl::Reference<SwDocStyleSheet> xTmp(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ if(!xTmp->SetName(rName))
+ throw uno::RuntimeException();
+ m_sStyleName = rName;
+}
+
+sal_Bool SwXStyle::isUserDefined()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ //if it is not found it must be non user defined
+ return pBase && pBase->IsUserDefined();
+}
+
+sal_Bool SwXStyle::isInUse()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily, SfxStyleSearchBits::Used);
+ return pBase && pBase->IsUsed();
+}
+
+OUString SwXStyle::getParentStyle()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ {
+ if(!m_bIsDescriptor)
+ throw uno::RuntimeException();
+ return m_sParentStyleName;
+ }
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ OUString aString;
+ if(pBase)
+ aString = pBase->GetParent();
+ SwStyleNameMapper::FillProgName(aString, aString, lcl_GetSwEnumFromSfxEnum(m_rEntry.m_eFamily));
+ return aString;
+}
+
+void SwXStyle::setParentStyle(const OUString& rParentStyle)
+{
+ SolarMutexGuard aGuard;
+ OUString sParentStyle;
+ SwStyleNameMapper::FillUIName(rParentStyle, sParentStyle, lcl_GetSwEnumFromSfxEnum ( m_rEntry.m_eFamily ) );
+ if(!m_pBasePool)
+ {
+ if(!m_bIsDescriptor)
+ throw uno::RuntimeException();
+ m_sParentStyleName = sParentStyle;
+ try
+ {
+ const auto aAny = m_xStyleFamily->getByName(sParentStyle);
+ m_xStyleData = aAny.get<decltype(m_xStyleData)>();
+ }
+ catch(...)
+ { }
+ return;
+ }
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ if(!pBase)
+ throw uno::RuntimeException();
+ rtl::Reference<SwDocStyleSheet> xBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ //make it a 'real' style - necessary for pooled styles
+ xBase->GetItemSet();
+ if(xBase->GetParent() != sParentStyle)
+ {
+ if(!xBase->SetParent(sParentStyle))
+ throw uno::RuntimeException();
+ }
+}
+
+uno::Reference<beans::XPropertySetInfo> SwXStyle::getPropertySetInfo()
+{
+ if(m_bIsConditional)
+ {
+ assert(m_rEntry.m_eFamily == SfxStyleFamily::Para);
+ static uno::Reference<beans::XPropertySetInfo> xCondParaRef;
+ xCondParaRef = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CONDITIONAL_PARA_STYLE)->getPropertySetInfo();
+ return xCondParaRef;
+ }
+ return m_rEntry.m_xPSInfo;
+}
+
+void SwXStyle::ApplyDescriptorProperties()
+{
+ m_bIsDescriptor = false;
+ m_xStyleData.clear();
+ m_xStyleFamily.clear();
+ m_pPropertiesImpl->Apply(*this);
+}
+
+namespace {
+
+class SwStyleBase_Impl
+{
+private:
+ SwDoc& m_rDoc;
+ const SwPageDesc* m_pOldPageDesc;
+ rtl::Reference<SwDocStyleSheet> m_xNewBase;
+ SfxItemSet* m_pItemSet;
+ std::unique_ptr<SfxItemSet> m_pMyItemSet;
+ OUString m_rStyleName;
+ const SwAttrSet* m_pParentStyle;
+public:
+ SwStyleBase_Impl(SwDoc& rSwDoc, const OUString& rName, const SwAttrSet* pParentStyle)
+ : m_rDoc(rSwDoc)
+ , m_pOldPageDesc(nullptr)
+ , m_pItemSet(nullptr)
+ , m_rStyleName(rName)
+ , m_pParentStyle(pParentStyle)
+ { }
+
+ rtl::Reference<SwDocStyleSheet>& getNewBase()
+ {
+ return m_xNewBase;
+ }
+
+ void setNewBase(SwDocStyleSheet* pNew)
+ {
+ m_xNewBase = pNew;
+ }
+
+ bool HasItemSet() const
+ {
+ return m_xNewBase.is();
+ }
+
+ SfxItemSet& GetItemSet()
+ {
+ assert(m_xNewBase.is());
+ if(!m_pItemSet)
+ {
+ m_pMyItemSet.reset(new SfxItemSet(m_xNewBase->GetItemSet()));
+ m_pItemSet = m_pMyItemSet.get();
+
+ // set parent style to have the correct XFillStyle setting as XFILL_NONE
+ if(!m_pItemSet->GetParent() && m_pParentStyle)
+ m_pItemSet->SetParent(m_pParentStyle);
+ }
+ return *m_pItemSet;
+ }
+
+ const SwPageDesc* GetOldPageDesc();
+
+ // still a hack, but a bit more explicit and with a proper scope
+ struct ItemSetOverrider
+ {
+ SwStyleBase_Impl& m_rStyleBase;
+ SfxItemSet* m_pOldSet;
+ ItemSetOverrider(SwStyleBase_Impl& rStyleBase, SfxItemSet* pTemp)
+ : m_rStyleBase(rStyleBase)
+ , m_pOldSet(m_rStyleBase.m_pItemSet)
+ { m_rStyleBase.m_pItemSet = pTemp; }
+ ~ItemSetOverrider()
+ { m_rStyleBase.m_pItemSet = m_pOldSet; };
+ };
+};
+
+ const TranslateId STR_POOLPAGE_ARY[] =
+ {
+ // Page styles
+ STR_POOLPAGE_STANDARD,
+ STR_POOLPAGE_FIRST,
+ STR_POOLPAGE_LEFT,
+ STR_POOLPAGE_RIGHT,
+ STR_POOLPAGE_ENVELOPE,
+ STR_POOLPAGE_REGISTER,
+ STR_POOLPAGE_HTML,
+ STR_POOLPAGE_FOOTNOTE,
+ STR_POOLPAGE_ENDNOTE,
+ STR_POOLPAGE_LANDSCAPE
+ };
+}
+
+const SwPageDesc* SwStyleBase_Impl::GetOldPageDesc()
+{
+ if(!m_pOldPageDesc)
+ {
+ SwPageDesc *pd = m_rDoc.FindPageDesc(m_rStyleName);
+ if(pd)
+ m_pOldPageDesc = pd;
+
+ if(!m_pOldPageDesc)
+ {
+ for (size_t i = 0; i < SAL_N_ELEMENTS(STR_POOLPAGE_ARY); ++i)
+ {
+ if (SwResId(STR_POOLPAGE_ARY[i]) == m_rStyleName)
+ {
+ m_pOldPageDesc = m_rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_BEGIN + i);
+ break;
+ }
+ }
+ }
+ }
+ return m_pOldPageDesc;
+}
+
+
+
+static sal_uInt8 lcl_TranslateMetric(const SfxItemPropertyMapEntry& rEntry, SwDoc* pDoc, uno::Any& o_aValue)
+{
+ // check for needed metric translation
+ if(!(rEntry.nMoreFlags & PropertyMoreFlags::METRIC_ITEM))
+ return rEntry.nMemberId;
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ if((XATTR_FILLBMP_SIZEX == rEntry.nWID || XATTR_FILLBMP_SIZEY == rEntry.nWID)
+ && o_aValue.has<sal_Int32>()
+ && o_aValue.get<sal_Int32>() < 0)
+ return rEntry.nMemberId;
+ if(!pDoc)
+ return rEntry.nMemberId;
+
+ const SfxItemPool& rPool = pDoc->GetAttrPool();
+ const MapUnit eMapUnit(rPool.GetMetric(rEntry.nWID));
+ if(eMapUnit != MapUnit::Map100thMM)
+ SvxUnoConvertFromMM(eMapUnit, o_aValue);
+ return rEntry.nMemberId;
+}
+template<>
+void SwXStyle::SetPropertyValue<HINT_BEGIN>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ // default ItemSet handling
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ SfxItemSet aSet(*rStyleSet.GetPool(), rEntry.nWID, rEntry.nWID);
+ aSet.SetParent(&rStyleSet);
+ rPropSet.setPropertyValue(rEntry, rValue, aSet);
+ rStyleSet.Put(aSet);
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_HIDDEN>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ bool bHidden = false;
+ if(rValue >>= bHidden)
+ {
+ //make it a 'real' style - necessary for pooled styles
+ o_rStyleBase.getNewBase()->GetItemSet();
+ o_rStyleBase.getNewBase()->SetHidden(bHidden);
+ }
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase);
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_STYLE_INTEROP_GRAB_BAG>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ o_rStyleBase.getNewBase()->GetItemSet();
+ o_rStyleBase.getNewBase()->SetGrabBagItem(rValue);
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase);
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(XATTR_FILLGRADIENT)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ uno::Any aValue(rValue);
+ const auto nMemberId(lcl_TranslateMetric(rEntry, m_pDoc, aValue));
+ if(MID_NAME == nMemberId)
+ {
+ // add set commands for FillName items
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ if(!aValue.has<OUString>())
+ throw lang::IllegalArgumentException();
+ SvxShape::SetFillAttribute(rEntry.nWID, aValue.get<OUString>(), rStyleSet);
+ }
+ else if(MID_BITMAP == nMemberId)
+ {
+ if(sal_uInt16(XATTR_FILLBITMAP) == rEntry.nWID)
+ {
+ const Graphic aNullGraphic;
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ XFillBitmapItem aXFillBitmapItem(aNullGraphic);
+ aXFillBitmapItem.PutValue(aValue, nMemberId);
+ rStyleSet.Put(aXFillBitmapItem);
+ }
+ }
+ else
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, aValue, o_rStyleBase);
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_BACKGROUND)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(rStyleSet, RES_BACKGROUND, true, m_pDoc->IsInXMLImport()));
+ std::unique_ptr<SvxBrushItem> aChangedBrushItem(aOriginalBrushItem->Clone());
+
+ uno::Any aValue(rValue);
+ const auto nMemberId(lcl_TranslateMetric(rEntry, m_pDoc, aValue));
+ aChangedBrushItem->PutValue(aValue, nMemberId);
+
+ // 0xff is already the default - but if BackTransparent is set
+ // to true, it must be applied in the item set on ODF import
+ // to potentially override parent style, which is unknown yet
+ if(*aChangedBrushItem == *aOriginalBrushItem && (MID_GRAPHIC_TRANSPARENT != nMemberId || !aValue.has<bool>() || !aValue.get<bool>()))
+ return;
+
+ setSvxBrushItemAsFillAttributesToTargetSet(*aChangedBrushItem, rStyleSet);
+}
+template<>
+void SwXStyle::SetPropertyValue<OWN_ATTR_FILLBMP_MODE>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ drawing::BitmapMode eMode;
+ if(!(rValue >>= eMode))
+ {
+ if(!rValue.has<sal_Int32>())
+ throw lang::IllegalArgumentException();
+ eMode = static_cast<drawing::BitmapMode>(rValue.get<sal_Int32>());
+ }
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ rStyleSet.Put(XFillBmpStretchItem(drawing::BitmapMode_STRETCH == eMode));
+ rStyleSet.Put(XFillBmpTileItem(drawing::BitmapMode_REPEAT == eMode));
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_PAPER_BIN)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(!rValue.has<OUString>())
+ throw lang::IllegalArgumentException();
+ SfxPrinter* pPrinter = m_pDoc->getIDocumentDeviceAccess().getPrinter(true);
+ OUString sValue(rValue.get<OUString>());
+ using printeridx_t = decltype(pPrinter->GetPaperBinCount());
+ printeridx_t nBin = std::numeric_limits<printeridx_t>::max();
+ if(sValue == "[From printer settings]")
+ nBin = std::numeric_limits<printeridx_t>::max()-1;
+ else if(pPrinter)
+ {
+ for(sal_uInt16 i=0, nEnd = pPrinter->GetPaperBinCount(); i < nEnd; ++i)
+ {
+ if (sValue == pPrinter->GetPaperBinName(i))
+ {
+ nBin = i;
+ break;
+ }
+ }
+ }
+ if(nBin == std::numeric_limits<printeridx_t>::max())
+ throw lang::IllegalArgumentException();
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ SfxItemSet aSet(*rStyleSet.GetPool(), rEntry.nWID, rEntry.nWID);
+ aSet.SetParent(&rStyleSet);
+ rPropSet.setPropertyValue(rEntry, uno::Any(static_cast<sal_Int8>(nBin == std::numeric_limits<printeridx_t>::max()-1 ? -1 : nBin)), aSet);
+ rStyleSet.Put(aSet);
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_NUM_RULES>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(!rValue.has<uno::Reference<container::XIndexReplace>>() || !rValue.has<uno::Reference<lang::XUnoTunnel>>())
+ throw lang::IllegalArgumentException();
+ auto xNumberTunnel(rValue.get<uno::Reference<lang::XUnoTunnel>>());
+ SwXNumberingRules* pSwXRules = comphelper::getFromUnoTunnel<SwXNumberingRules>(xNumberTunnel);
+ if(!pSwXRules)
+ return;
+ SwNumRule aSetRule(*pSwXRules->GetNumRule());
+ for(sal_uInt16 i = 0; i < MAXLEVEL; ++i)
+ {
+ const SwNumFormat* pFormat = aSetRule.GetNumFormat(i);
+ if(!pFormat)
+ continue;
+ SwNumFormat aFormat(*pFormat);
+ const auto& rCharName(pSwXRules->GetNewCharStyleNames()[i]);
+ if(!rCharName.isEmpty()
+ && !SwXNumberingRules::isInvalidStyle(rCharName)
+ && (!pFormat->GetCharFormat() || pFormat->GetCharFormat()->GetName() != rCharName))
+ {
+ auto pCharFormatIt(std::find_if(m_pDoc->GetCharFormats()->begin(), m_pDoc->GetCharFormats()->end(),
+ [&rCharName] (SwCharFormat* pF) { return pF->GetName() == rCharName; }));
+ if(pCharFormatIt != m_pDoc->GetCharFormats()->end())
+ aFormat.SetCharFormat(*pCharFormatIt);
+ else if(m_pBasePool)
+ {
+ auto pBase(m_pBasePool->Find(rCharName, SfxStyleFamily::Char));
+ if(!pBase)
+ pBase = &m_pBasePool->Make(rCharName, SfxStyleFamily::Char);
+ aFormat.SetCharFormat(static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat());
+ }
+ else
+ aFormat.SetCharFormat(nullptr);
+ }
+ // same for fonts:
+ const auto& rBulletName(pSwXRules->GetBulletFontNames()[i]);
+ if(!rBulletName.isEmpty()
+ && !SwXNumberingRules::isInvalidStyle(rBulletName)
+ && (!pFormat->GetBulletFont() || pFormat->GetBulletFont()->GetFamilyName() != rBulletName))
+ {
+ const auto pFontListItem(static_cast<const SvxFontListItem*>(m_pDoc->GetDocShell()->GetItem(SID_ATTR_CHAR_FONTLIST)));
+ const auto pList(pFontListItem->GetFontList());
+ FontMetric aFontInfo(pList->Get(rBulletName, WEIGHT_NORMAL, ITALIC_NONE));
+ vcl::Font aFont(aFontInfo);
+ aFormat.SetBulletFont(&aFont);
+ }
+ aSetRule.Set(i, &aFormat);
+ }
+ o_rStyleBase.getNewBase()->SetNumRule(aSetRule);
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_OUTLINELEVEL)>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(!rValue.has<sal_Int16>())
+ return;
+ const auto nLevel(rValue.get<sal_Int16>());
+ if(0 <= nLevel && nLevel <= MAXLEVEL)
+ o_rStyleBase.getNewBase()->GetCollection()->SetAttrOutlineLevel(nLevel);
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_FOLLOW_STYLE>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(!rValue.has<OUString>())
+ return;
+ const auto sValue(rValue.get<OUString>());
+ OUString aString;
+ SwStyleNameMapper::FillUIName(sValue, aString, m_rEntry.m_aPoolId);
+ o_rStyleBase.getNewBase()->SetFollow(aString);
+}
+
+template <>
+void SwXStyle::SetPropertyValue<FN_UNO_LINK_STYLE>(const SfxItemPropertyMapEntry&,
+ const SfxItemPropertySet&,
+ const uno::Any& rValue,
+ SwStyleBase_Impl& o_rStyleBase)
+{
+ if (!rValue.has<OUString>())
+ return;
+ const auto sValue(rValue.get<OUString>());
+ OUString aString;
+ SwStyleNameMapper::FillUIName(sValue, aString, m_rEntry.m_aPoolId);
+ o_rStyleBase.getNewBase()->SetLink(aString);
+}
+
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_PAGEDESC)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(MID_PAGEDESC_PAGEDESCNAME != rEntry.nMemberId)
+ {
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase);
+ return;
+ }
+ if(!rValue.has<OUString>())
+ throw lang::IllegalArgumentException();
+ // special handling for RES_PAGEDESC
+ SfxItemSet& rStyleSet = o_rStyleBase.GetItemSet();
+ std::unique_ptr<SwFormatPageDesc> pNewDesc;
+ if(const SwFormatPageDesc* pItem = rStyleSet.GetItemIfSet(RES_PAGEDESC))
+ pNewDesc.reset(new SwFormatPageDesc(*pItem));
+ else
+ pNewDesc.reset(new SwFormatPageDesc);
+ const auto sValue(rValue.get<OUString>());
+ OUString sDescName;
+ SwStyleNameMapper::FillUIName(sValue, sDescName, SwGetPoolIdFromName::PageDesc);
+ if(pNewDesc->GetPageDesc() && pNewDesc->GetPageDesc()->GetName() == sDescName)
+ return;
+ if(sDescName.isEmpty())
+ {
+ rStyleSet.ClearItem(RES_BREAK);
+ rStyleSet.Put(SwFormatPageDesc());
+ }
+ else
+ {
+ SwPageDesc* pPageDesc(SwPageDesc::GetByName(*m_pDoc, sDescName));
+ if(!pPageDesc)
+ throw lang::IllegalArgumentException();
+ pNewDesc->RegisterToPageDesc(*pPageDesc);
+ rStyleSet.Put(std::move(pNewDesc));
+ }
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_TEXT_VERT_ADJUST)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(m_rEntry.m_eFamily != SfxStyleFamily::Page)
+ {
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase);
+ return;
+ }
+ if(!m_pDoc || !rValue.has<drawing::TextVerticalAdjust>() || !o_rStyleBase.GetOldPageDesc())
+ return;
+ SwPageDesc* pPageDesc = m_pDoc->FindPageDesc(o_rStyleBase.GetOldPageDesc()->GetName());
+ if(pPageDesc)
+ pPageDesc->SetVerticalAdjustment(rValue.get<drawing::TextVerticalAdjust>());
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_IS_AUTO_UPDATE>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(!rValue.has<bool>())
+ throw lang::IllegalArgumentException();
+ const bool bAuto(rValue.get<bool>());
+ if(SfxStyleFamily::Para == m_rEntry.m_eFamily)
+ o_rStyleBase.getNewBase()->GetCollection()->SetAutoUpdateFormat(bAuto);
+ else if(SfxStyleFamily::Frame == m_rEntry.m_eFamily)
+ o_rStyleBase.getNewBase()->GetFrameFormat()->SetAutoUpdateFormat(bAuto);
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_PARA_STYLE_CONDITIONS>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ static_assert(COND_COMMAND_COUNT == 28, "invalid size of command count?");
+ using expectedarg_t = uno::Sequence<beans::NamedValue>;
+ if(!rValue.has<expectedarg_t>() || !m_pBasePool)
+ throw lang::IllegalArgumentException();
+ SwCondCollItem aCondItem;
+ const auto aNamedValues = rValue.get<expectedarg_t>();
+ for(const auto& rNamedValue : aNamedValues)
+ {
+ if(!rNamedValue.Value.has<OUString>())
+ throw lang::IllegalArgumentException();
+
+ const OUString sValue(rNamedValue.Value.get<OUString>());
+ // get UI style name from programmatic style name
+ OUString aStyleName;
+ SwStyleNameMapper::FillUIName(sValue, aStyleName, lcl_GetSwEnumFromSfxEnum(m_rEntry.m_eFamily));
+
+ // check for correct context and style name
+ const auto nIdx(GetCommandContextIndex(rNamedValue.Name));
+ if (nIdx == -1)
+ throw lang::IllegalArgumentException();
+ bool bStyleFound = false;
+ for(auto pBase = m_pBasePool->First(SfxStyleFamily::Para); pBase; pBase = m_pBasePool->Next())
+ {
+ bStyleFound = pBase->GetName() == aStyleName;
+ if (bStyleFound)
+ break;
+ }
+ if (!bStyleFound)
+ throw lang::IllegalArgumentException();
+ aCondItem.SetStyle(&aStyleName, nIdx);
+ }
+ o_rStyleBase.GetItemSet().Put(aCondItem);
+}
+template<>
+void SwXStyle::SetPropertyValue<FN_UNO_CATEGORY>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(!o_rStyleBase.getNewBase()->IsUserDefined() || !rValue.has<paragraphstyle_t>())
+ throw lang::IllegalArgumentException();
+ static std::optional<std::map<paragraphstyle_t, SfxStyleSearchBits>> pUnoToCore;
+ if(!pUnoToCore)
+ {
+ pUnoToCore.emplace();
+ auto pEntries = lcl_GetParagraphStyleCategoryEntries();
+ std::transform(pEntries->begin(), pEntries->end(), std::inserter(*pUnoToCore, pUnoToCore->end()),
+ [] (const ParagraphStyleCategoryEntry& rEntry) { return std::pair<paragraphstyle_t, SfxStyleSearchBits>(rEntry.m_eCategory, rEntry.m_nSwStyleBits); });
+ }
+ const auto pUnoToCoreIt(pUnoToCore->find(rValue.get<paragraphstyle_t>()));
+ if(pUnoToCoreIt == pUnoToCore->end())
+ throw lang::IllegalArgumentException();
+ o_rStyleBase.getNewBase()->SetMask( pUnoToCoreIt->second|SfxStyleSearchBits::UserDefined );
+}
+template<>
+void SwXStyle::SetPropertyValue<SID_SWREGISTER_COLLECTION>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ OUString sName;
+ rValue >>= sName;
+ SwRegisterItem aReg(!sName.isEmpty());
+ aReg.SetWhich(SID_SWREGISTER_MODE);
+ o_rStyleBase.GetItemSet().Put(aReg);
+ OUString aString;
+ SwStyleNameMapper::FillUIName(sName, aString, SwGetPoolIdFromName::TxtColl);
+ o_rStyleBase.GetItemSet().Put(SfxStringItem(SID_SWREGISTER_COLLECTION, aString ) );
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_TXTATR_CJK_RUBY)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(MID_RUBY_CHARSTYLE != rEntry.nMemberId)
+ return;
+ if(!rValue.has<OUString>())
+ throw lang::IllegalArgumentException();
+ const auto sValue(rValue.get<OUString>());
+ SfxItemSet& rStyleSet(o_rStyleBase.GetItemSet());
+ std::unique_ptr<SwFormatRuby> pRuby;
+ if(const SwFormatRuby* pRubyItem = rStyleSet.GetItemIfSet(RES_TXTATR_CJK_RUBY))
+ pRuby.reset(new SwFormatRuby(*pRubyItem));
+ else
+ pRuby.reset(new SwFormatRuby(OUString()));
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(sValue, sStyle, SwGetPoolIdFromName::ChrFmt);
+ pRuby->SetCharFormatName(sValue);
+ pRuby->SetCharFormatId(0);
+ if(!sValue.isEmpty())
+ {
+ const sal_uInt16 nId(SwStyleNameMapper::GetPoolIdFromUIName(sValue, SwGetPoolIdFromName::ChrFmt));
+ pRuby->SetCharFormatId(nId);
+ }
+ rStyleSet.Put(std::move(pRuby));
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase);
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_DROP)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ if(MID_DROPCAP_CHAR_STYLE_NAME != rEntry.nMemberId)
+ {
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, rValue, o_rStyleBase);
+ return;
+ }
+ if(!rValue.has<OUString>())
+ throw lang::IllegalArgumentException();
+ SfxItemSet& rStyleSet(o_rStyleBase.GetItemSet());
+ std::unique_ptr<SwFormatDrop> pDrop;
+ if(const SwFormatDrop* pDropItem = rStyleSet.GetItemIfSet(RES_PARATR_DROP))
+ pDrop.reset(new SwFormatDrop(*pDropItem));
+ else
+ pDrop.reset(new SwFormatDrop);
+ const auto sValue(rValue.get<OUString>());
+ OUString sStyle;
+ SwStyleNameMapper::FillUIName(sValue, sStyle, SwGetPoolIdFromName::ChrFmt);
+ auto pStyle(static_cast<SwDocStyleSheet*>(m_pDoc->GetDocShell()->GetStyleSheetPool()->Find(sStyle, SfxStyleFamily::Char)));
+ //default character style must not be set as default format
+ if(!pStyle || pStyle->GetCharFormat() == m_pDoc->GetDfltCharFormat() )
+ {
+ throw lang::IllegalArgumentException();
+ }
+ pDrop->SetCharFormat(pStyle->GetCharFormat());
+ rStyleSet.Put(std::move(pDrop));
+}
+template<>
+void SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_NUMRULE)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& o_rStyleBase)
+{
+ uno::Any aValue(rValue);
+ lcl_TranslateMetric(rEntry, m_pDoc, aValue);
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, aValue, o_rStyleBase);
+ // --> OD 2006-10-18 #i70223#
+ if(SfxStyleFamily::Para == m_rEntry.m_eFamily &&
+ o_rStyleBase.getNewBase().is() && o_rStyleBase.getNewBase()->GetCollection() &&
+ //rBase.getNewBase()->GetCollection()->GetOutlineLevel() < MAXLEVEL /* assigned to list level of outline style */) //#outline level,removed by zhaojianwei
+ o_rStyleBase.getNewBase()->GetCollection()->IsAssignedToListLevelOfOutlineStyle()) ////<-end,add by zhaojianwei
+ {
+ OUString sNewNumberingRuleName;
+ aValue >>= sNewNumberingRuleName;
+ if(sNewNumberingRuleName.isEmpty() || sNewNumberingRuleName != m_pDoc->GetOutlineNumRule()->GetName())
+ o_rStyleBase.getNewBase()->GetCollection()->DeleteAssignmentToListLevelOfOutlineStyle();
+ }
+}
+
+void SwXStyle::SetStyleProperty(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, const uno::Any& rValue, SwStyleBase_Impl& rBase)
+{
+ using propertytype_t = decltype(rEntry.nWID);
+ using coresetter_t = std::function<void(SwXStyle&, const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, const uno::Any&, SwStyleBase_Impl&)>;
+ static std::optional<std::map<propertytype_t, coresetter_t>> pUnoToCore;
+ if(!pUnoToCore)
+ {
+ pUnoToCore = std::map<propertytype_t, coresetter_t> {
+ // these explicit std::mem_fn() calls shouldn't be needed, but apparently MSVC is currently too stupid for C++11 again
+ { FN_UNO_HIDDEN, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_HIDDEN>) },
+ { FN_UNO_STYLE_INTEROP_GRAB_BAG, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_STYLE_INTEROP_GRAB_BAG>) },
+ { XATTR_FILLGRADIENT, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(XATTR_FILLGRADIENT)>) },
+ { XATTR_FILLHATCH, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(XATTR_FILLGRADIENT)>) },
+ { XATTR_FILLBITMAP, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(XATTR_FILLGRADIENT)>) },
+ { XATTR_FILLFLOATTRANSPARENCE, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(XATTR_FILLGRADIENT)>) },
+ { RES_BACKGROUND, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_BACKGROUND)>) },
+ { OWN_ATTR_FILLBMP_MODE, std::mem_fn(&SwXStyle::SetPropertyValue<OWN_ATTR_FILLBMP_MODE>) },
+ { RES_PAPER_BIN, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_PAPER_BIN)>) },
+ { FN_UNO_NUM_RULES, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_NUM_RULES>) },
+ { RES_PARATR_OUTLINELEVEL, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_OUTLINELEVEL)>) },
+ { FN_UNO_FOLLOW_STYLE, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_FOLLOW_STYLE>) },
+ { FN_UNO_LINK_STYLE, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_LINK_STYLE>) },
+ { RES_PAGEDESC, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_PAGEDESC)>) },
+ { RES_TEXT_VERT_ADJUST, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_TEXT_VERT_ADJUST)>) },
+ { FN_UNO_IS_AUTO_UPDATE, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_IS_AUTO_UPDATE>) },
+ { FN_UNO_PARA_STYLE_CONDITIONS, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_PARA_STYLE_CONDITIONS>) },
+ { FN_UNO_CATEGORY, std::mem_fn(&SwXStyle::SetPropertyValue<FN_UNO_CATEGORY>) },
+ { SID_SWREGISTER_COLLECTION, std::mem_fn(&SwXStyle::SetPropertyValue<SID_SWREGISTER_COLLECTION>) },
+ { RES_TXTATR_CJK_RUBY, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_TXTATR_CJK_RUBY)>) },
+ { RES_PARATR_DROP, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_DROP)>) },
+ { RES_PARATR_NUMRULE, std::mem_fn(&SwXStyle::SetPropertyValue<sal_uInt16(RES_PARATR_NUMRULE)>) }
+ };
+ }
+ const auto pUnoToCoreIt(pUnoToCore->find(rEntry.nWID));
+ if(pUnoToCoreIt != pUnoToCore->end())
+ pUnoToCoreIt->second(*this, rEntry, rPropSet, rValue, rBase);
+ else
+ {
+ // adapted switch logic to a more readable state; removed goto's and made
+ // execution of standard setting of property in ItemSet dependent of this variable
+ uno::Any aValue(rValue);
+ lcl_TranslateMetric(rEntry, m_pDoc, aValue);
+ SetPropertyValue<HINT_BEGIN>(rEntry, rPropSet, aValue, rBase);
+ }
+}
+
+void SwXStyle::SetPropertyValues_Impl(const uno::Sequence<OUString>& rPropertyNames, const uno::Sequence<uno::Any>& rValues)
+{
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ sal_Int8 nPropSetId = m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType;
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap &rMap = pPropSet->getPropertyMap();
+ if(rPropertyNames.getLength() != rValues.getLength())
+ throw lang::IllegalArgumentException();
+
+ SwStyleBase_Impl aBaseImpl(*m_pDoc, m_sStyleName, &GetDoc()->GetDfltTextFormatColl()->GetAttrSet()); // add pDfltTextFormatColl as parent
+ if(m_pBasePool)
+ {
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ SAL_WARN_IF(!pBase, "sw.uno", "where is the style?");
+ if(!pBase)
+ throw uno::RuntimeException();
+ aBaseImpl.setNewBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ }
+ if(!aBaseImpl.getNewBase().is() && !m_bIsDescriptor)
+ throw uno::RuntimeException();
+
+ const OUString* pNames = rPropertyNames.getConstArray();
+ const uno::Any* pValues = rValues.getConstArray();
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); ++nProp)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(pNames[nProp]);
+ if(!pEntry || (!m_bIsConditional && pNames[nProp] == UNO_NAME_PARA_STYLE_CONDITIONS))
+ throw beans::UnknownPropertyException("Unknown property: " + pNames[nProp], static_cast<cppu::OWeakObject*>(this));
+ if(pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException ("Property is read-only: " + pNames[nProp], static_cast<cppu::OWeakObject*>(this));
+ if(aBaseImpl.getNewBase().is())
+ SetStyleProperty(*pEntry, *pPropSet, pValues[nProp], aBaseImpl);
+ else if(!m_pPropertiesImpl->SetProperty(pNames[nProp], pValues[nProp]))
+ throw lang::IllegalArgumentException();
+ }
+
+ if(aBaseImpl.HasItemSet())
+ aBaseImpl.getNewBase()->SetItemSet(aBaseImpl.GetItemSet());
+}
+
+void SwXStyle::setPropertyValues(const uno::Sequence<OUString>& rPropertyNames, const uno::Sequence<uno::Any>& rValues)
+{
+ SolarMutexGuard aGuard;
+ // workaround for bad designed API
+ try
+ {
+ SetPropertyValues_Impl( rPropertyNames, rValues );
+ }
+ catch (const beans::UnknownPropertyException &rException)
+ {
+ // wrap the original (here not allowed) exception in
+ // a lang::WrappedTargetException that gets thrown instead.
+ lang::WrappedTargetException aWExc;
+ aWExc.TargetException <<= rException;
+ throw aWExc;
+ }
+}
+
+SfxStyleSheetBase* SwXStyle::GetStyleSheetBase()
+{
+ if(!m_pBasePool)
+ return nullptr;
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+ return pBase;
+}
+void SwXStyle::PrepareStyleBase(SwStyleBase_Impl& rBase)
+{
+ SfxStyleSheetBase* pBase(GetStyleSheetBase());
+ if(!pBase)
+ throw uno::RuntimeException();
+ if(!rBase.getNewBase().is())
+ rBase.setNewBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+}
+
+template<>
+uno::Any SwXStyle::GetStyleProperty<HINT_BEGIN>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase);
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_IS_PHYSICAL>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&)
+{
+ SfxStyleSheetBase* pBase(GetStyleSheetBase());
+ if(!pBase)
+ return uno::Any(false);
+ bool bPhys = static_cast<SwDocStyleSheet*>(pBase)->IsPhysical();
+ // The standard character format is not existing physically
+ if( bPhys && SfxStyleFamily::Char == GetFamily() &&
+ static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat() &&
+ static_cast<SwDocStyleSheet*>(pBase)->GetCharFormat()->IsDefault() )
+ bPhys = false;
+ return uno::Any(bool(bPhys));
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_HIDDEN>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&)
+{
+ SfxStyleSheetBase* pBase(GetStyleSheetBase());
+ if(!pBase)
+ return uno::Any(false);
+ rtl::Reference<SwDocStyleSheet> xBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ return uno::Any(xBase->IsHidden());
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_STYLE_INTEROP_GRAB_BAG>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&)
+{
+ SfxStyleSheetBase* pBase(GetStyleSheetBase());
+ if(!pBase)
+ return uno::Any();
+ uno::Any aRet;
+ rtl::Reference<SwDocStyleSheet> xBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ xBase->GetGrabBagItem(aRet);
+ return aRet;
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_PAPER_BIN)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ SfxItemSet& rSet = rBase.GetItemSet();
+ uno::Any aValue;
+ rPropSet.getPropertyValue(rEntry, rSet, aValue);
+ sal_Int8 nBin(aValue.get<sal_Int8>());
+ if(nBin == -1)
+ return uno::Any(OUString("[From printer settings]"));
+ SfxPrinter* pPrinter = GetDoc()->getIDocumentDeviceAccess().getPrinter(false);
+ if(!pPrinter)
+ return uno::Any();
+ return uno::Any(pPrinter->GetPaperBinName(nBin));
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_NUM_RULES>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ const SwNumRule* pRule = rBase.getNewBase()->GetNumRule();
+ assert(pRule && "Where is the NumRule?");
+ uno::Reference<container::XIndexReplace> xRules(new SwXNumberingRules(*pRule, GetDoc()));
+ return uno::Any(xRules);
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_PARATR_OUTLINELEVEL)>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ SAL_WARN_IF(SfxStyleFamily::Para != GetFamily(), "sw.uno", "only paras");
+ return uno::Any(sal_Int16(rBase.getNewBase()->GetCollection()->GetAttrOutlineLevel()));
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_FOLLOW_STYLE>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ OUString aString;
+ SwStyleNameMapper::FillProgName(rBase.getNewBase()->GetFollow(), aString, lcl_GetSwEnumFromSfxEnum(GetFamily()));
+ return uno::Any(aString);
+}
+
+template <>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_LINK_STYLE>(const SfxItemPropertyMapEntry&,
+ const SfxItemPropertySet&,
+ SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ OUString aString;
+ SwStyleNameMapper::FillProgName(rBase.getNewBase()->GetLink(), aString,
+ lcl_GetSwEnumFromSfxEnum(GetFamily()));
+ return uno::Any(aString);
+}
+
+template<>
+uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_PAGEDESC)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ if(MID_PAGEDESC_PAGEDESCNAME != rEntry.nMemberId)
+ return GetStyleProperty<HINT_BEGIN>(rEntry, rPropSet, rBase);
+ // special handling for RES_PAGEDESC
+ const SwFormatPageDesc* pItem =
+ rBase.GetItemSet().GetItemIfSet(RES_PAGEDESC);
+ if(!pItem)
+ return uno::Any();
+ const SwPageDesc* pDesc = pItem->GetPageDesc();
+ if(!pDesc)
+ return uno::Any();
+ OUString aString;
+ SwStyleNameMapper::FillProgName(pDesc->GetName(), aString, SwGetPoolIdFromName::PageDesc);
+ return uno::Any(aString);
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_IS_AUTO_UPDATE>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ switch(GetFamily())
+ {
+ case SfxStyleFamily::Para : return uno::Any(rBase.getNewBase()->GetCollection()->IsAutoUpdateFormat());
+ case SfxStyleFamily::Frame: return uno::Any(rBase.getNewBase()->GetFrameFormat()->IsAutoUpdateFormat());
+ default: return uno::Any();
+ }
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_DISPLAY_NAME>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ return uno::Any(rBase.getNewBase()->GetName());
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_PARA_STYLE_CONDITIONS>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ static_assert(COND_COMMAND_COUNT == 28, "invalid size of command count?");
+ uno::Sequence<beans::NamedValue> aSeq(COND_COMMAND_COUNT);
+ sal_uInt16 nIndex = 0;
+ for(auto& rNV : asNonConstRange(aSeq))
+ {
+ rNV.Name = GetCommandContextByIndex(nIndex++);
+ rNV.Value <<= OUString();
+ }
+ SwFormat* pFormat = static_cast<SwDocStyleSheet*>(GetStyleSheetBase())->GetCollection();
+ if(pFormat && RES_CONDTXTFMTCOLL == pFormat->Which())
+ {
+ const CommandStruct* pCmds = SwCondCollItem::GetCmds();
+ beans::NamedValue* pSeq = aSeq.getArray();
+ for(sal_uInt16 n = 0; n < COND_COMMAND_COUNT; ++n)
+ {
+ const SwCollCondition* pCond = static_cast<SwConditionTextFormatColl*>(pFormat)->HasCondition(SwCollCondition(nullptr, pCmds[n].nCnd, pCmds[n].nSubCond));
+ if(!pCond || !pCond->GetTextFormatColl())
+ continue;
+ // get programmatic style name from UI style name
+ OUString aStyleName = pCond->GetTextFormatColl()->GetName();
+ SwStyleNameMapper::FillProgName(aStyleName, aStyleName, lcl_GetSwEnumFromSfxEnum(GetFamily()));
+ pSeq[n].Value <<= aStyleName;
+ }
+ }
+ return uno::Any(aSeq);
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<FN_UNO_CATEGORY>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ static std::optional<std::map<collectionbits_t, paragraphstyle_t>> pUnoToCore;
+ if(!pUnoToCore)
+ {
+ pUnoToCore.emplace();
+ auto pEntries = lcl_GetParagraphStyleCategoryEntries();
+ std::transform(pEntries->begin(), pEntries->end(), std::inserter(*pUnoToCore, pUnoToCore->end()),
+ [] (const ParagraphStyleCategoryEntry& rEntry) { return std::pair<collectionbits_t, paragraphstyle_t>(rEntry.m_nCollectionBits, rEntry.m_eCategory); });
+ }
+ const sal_uInt16 nPoolId = rBase.getNewBase()->GetCollection()->GetPoolFormatId();
+ const auto pUnoToCoreIt(pUnoToCore->find(COLL_GET_RANGE_BITS & nPoolId));
+ if(pUnoToCoreIt == pUnoToCore->end())
+ return uno::Any(sal_Int16(-1));
+ return uno::Any(pUnoToCoreIt->second);
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<SID_SWREGISTER_COLLECTION>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ const SwPageDesc *pPageDesc = rBase.getNewBase()->GetPageDesc();
+ if(!pPageDesc)
+ return uno::Any(OUString());
+ const SwTextFormatColl* pCol = pPageDesc->GetRegisterFormatColl();
+ if(!pCol)
+ return uno::Any(OUString());
+ OUString aName;
+ SwStyleNameMapper::FillProgName(pCol->GetName(), aName, SwGetPoolIdFromName::TxtColl);
+ return uno::Any(aName);
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<sal_uInt16(RES_BACKGROUND)>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ const SfxItemSet& rSet = rBase.GetItemSet();
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(rSet, RES_BACKGROUND));
+ uno::Any aResult;
+ if(!aOriginalBrushItem->QueryValue(aResult, rEntry.nMemberId))
+ SAL_WARN("sw.uno", "error getting attribute from RES_BACKGROUND.");
+ return aResult;
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<OWN_ATTR_FILLBMP_MODE>(const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ const SfxItemSet& rSet = rBase.GetItemSet();
+ if (rSet.Get(XATTR_FILLBMP_TILE).GetValue())
+ return uno::Any(drawing::BitmapMode_REPEAT);
+ if (rSet.Get(XATTR_FILLBMP_STRETCH).GetValue())
+ return uno::Any(drawing::BitmapMode_STRETCH);
+ return uno::Any(drawing::BitmapMode_NO_REPEAT);
+}
+template<>
+uno::Any SwXStyle::GetStyleProperty<HINT_BEGIN>(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase)
+{
+ PrepareStyleBase(rBase);
+ SfxItemSet& rSet = rBase.GetItemSet();
+ uno::Any aResult;
+ rPropSet.getPropertyValue(rEntry, rSet, aResult);
+ //
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ if(rEntry.aType == cppu::UnoType<sal_Int16>::get() && aResult.getValueType() == cppu::UnoType<sal_Int32>::get())
+ aResult <<= static_cast<sal_Int16>(aResult.get<sal_Int32>());
+ // check for needed metric translation
+ if(rEntry.nMoreFlags & PropertyMoreFlags::METRIC_ITEM && GetDoc())
+ {
+ const SfxItemPool& rPool = GetDoc()->GetAttrPool();
+ const MapUnit eMapUnit(rPool.GetMetric(rEntry.nWID));
+ bool bAllowedConvert(true);
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ if(XATTR_FILLBMP_SIZEX == rEntry.nWID || XATTR_FILLBMP_SIZEY == rEntry.nWID)
+ bAllowedConvert = !aResult.has<sal_Int32>() || aResult.get<sal_Int32>() > 0;
+ if(eMapUnit != MapUnit::Map100thMM && bAllowedConvert)
+ SvxUnoConvertToMM(eMapUnit, aResult);
+ }
+ return aResult;
+}
+
+uno::Any SwXStyle::GetStyleProperty_Impl(const SfxItemPropertyMapEntry& rEntry, const SfxItemPropertySet& rPropSet, SwStyleBase_Impl& rBase)
+{
+ using propertytype_t = decltype(rEntry.nWID);
+ using coresetter_t = std::function<uno::Any(SwXStyle&, const SfxItemPropertyMapEntry&, const SfxItemPropertySet&, SwStyleBase_Impl&)>;
+ static std::optional<std::map<propertytype_t, coresetter_t>> pUnoToCore;
+ if(!pUnoToCore)
+ {
+ pUnoToCore = std::map<propertytype_t, coresetter_t> {
+ // these explicit std::mem_fn() calls shouldn't be needed, but apparently MSVC is currently too stupid for C++11 again
+ { FN_UNO_IS_PHYSICAL, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_IS_PHYSICAL>) },
+ { FN_UNO_HIDDEN, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_HIDDEN>) },
+ { FN_UNO_STYLE_INTEROP_GRAB_BAG, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_STYLE_INTEROP_GRAB_BAG>) },
+ { RES_PAPER_BIN, std::mem_fn(&SwXStyle::GetStyleProperty<sal_uInt16(RES_PAPER_BIN)>) },
+ { FN_UNO_NUM_RULES, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_NUM_RULES>) },
+ { RES_PARATR_OUTLINELEVEL, std::mem_fn(&SwXStyle::GetStyleProperty<sal_uInt16(RES_PARATR_OUTLINELEVEL)>) },
+ { FN_UNO_FOLLOW_STYLE, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_FOLLOW_STYLE>) },
+ { FN_UNO_LINK_STYLE, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_LINK_STYLE>) },
+ { RES_PAGEDESC, std::mem_fn(&SwXStyle::GetStyleProperty<sal_uInt16(RES_PAGEDESC)>) },
+ { FN_UNO_IS_AUTO_UPDATE, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_IS_AUTO_UPDATE>) },
+ { FN_UNO_DISPLAY_NAME, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_DISPLAY_NAME>) },
+ { FN_UNO_PARA_STYLE_CONDITIONS, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_PARA_STYLE_CONDITIONS>) },
+ { FN_UNO_CATEGORY, std::mem_fn(&SwXStyle::GetStyleProperty<FN_UNO_CATEGORY>) },
+ { SID_SWREGISTER_COLLECTION, std::mem_fn(&SwXStyle::GetStyleProperty<SID_SWREGISTER_COLLECTION>) },
+ { RES_BACKGROUND, std::mem_fn(&SwXStyle::GetStyleProperty<sal_uInt16(RES_BACKGROUND)>) },
+ { OWN_ATTR_FILLBMP_MODE, std::mem_fn(&SwXStyle::GetStyleProperty<OWN_ATTR_FILLBMP_MODE>) }
+ };
+ }
+ const auto pUnoToCoreIt(pUnoToCore->find(rEntry.nWID));
+ if(pUnoToCoreIt != pUnoToCore->end())
+ return pUnoToCoreIt->second(*this, rEntry, rPropSet, rBase);
+ return GetStyleProperty<HINT_BEGIN>(rEntry, rPropSet, rBase);
+}
+
+uno::Any SwXStyle::GetPropertyValue_Impl(const SfxItemPropertySet* pPropSet, SwStyleBase_Impl& rBase, const OUString& rPropertyName)
+{
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(rPropertyName);
+ if(!pEntry || (!m_bIsConditional && rPropertyName == UNO_NAME_PARA_STYLE_CONDITIONS))
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ if(m_pBasePool)
+ return GetStyleProperty_Impl(*pEntry, *pPropSet, rBase);
+ const uno::Any* pAny = nullptr;
+ m_pPropertiesImpl->GetProperty(rPropertyName, pAny);
+ if(pAny->hasValue())
+ return *pAny;
+ uno::Any aValue;
+ switch(m_rEntry.m_eFamily)
+ {
+ case SfxStyleFamily::Pseudo:
+ throw uno::RuntimeException("No default value for: " + rPropertyName);
+ break;
+ case SfxStyleFamily::Para:
+ case SfxStyleFamily::Page:
+ SwStyleProperties_Impl::GetProperty(rPropertyName, m_xStyleData, aValue);
+ break;
+ case SfxStyleFamily::Char:
+ case SfxStyleFamily::Frame:
+ {
+ if(pEntry->nWID < POOLATTR_BEGIN || pEntry->nWID >= RES_UNKNOWNATR_END)
+ throw uno::RuntimeException("No default value for: " + rPropertyName);
+ SwFormat* pFormat;
+ if(m_rEntry.m_eFamily == SfxStyleFamily::Char)
+ pFormat = m_pDoc->GetDfltCharFormat();
+ else
+ pFormat = m_pDoc->GetDfltFrameFormat();
+ const SwAttrPool* pPool = pFormat->GetAttrSet().GetPool();
+ const SfxPoolItem& rItem = pPool->GetDefaultItem(pEntry->nWID);
+ rItem.QueryValue(aValue, pEntry->nMemberId);
+ }
+ break;
+ default:
+ ;
+ }
+ return aValue;
+}
+
+uno::Any SwXStyle::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(!m_pBasePool && !m_bIsDescriptor)
+ throw uno::RuntimeException();
+ sal_Int8 nPropSetId = m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType;
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ SwStyleBase_Impl aBase(*m_pDoc, m_sStyleName, &m_pDoc->GetDfltTextFormatColl()->GetAttrSet()); // add pDfltTextFormatColl as parent
+ return GetPropertyValue_Impl(pPropSet, aBase, rPropertyName);
+}
+
+uno::Sequence<uno::Any> SwXStyle::getPropertyValues(const uno::Sequence<OUString>& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ if(!m_pDoc)
+ throw uno::RuntimeException();
+ if(!m_pBasePool && !m_bIsDescriptor)
+ throw uno::RuntimeException();
+ sal_Int8 nPropSetId = m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType;
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ SwStyleBase_Impl aBase(*m_pDoc, m_sStyleName, &m_pDoc->GetDfltTextFormatColl()->GetAttrSet()); // add pDfltTextFormatColl as parent
+ uno::Sequence<uno::Any> aValues(rPropertyNames.getLength());
+ auto aValuesRange = asNonConstRange(aValues);
+ // workaround for bad designed API
+ try
+ {
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); ++nProp)
+ aValuesRange[nProp] = GetPropertyValue_Impl(pPropSet, aBase, rPropertyNames[nProp]);
+ }
+ catch(beans::UnknownPropertyException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("Unknown property exception caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+ catch(lang::WrappedTargetException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("WrappedTargetException caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+ return aValues;
+}
+
+void SwXStyle::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+ const uno::Sequence<OUString> aProperties(&rPropertyName, 1);
+ const uno::Sequence<uno::Any> aValues(&rValue, 1);
+ SetPropertyValues_Impl(aProperties, aValues);
+}
+
+beans::PropertyState SwXStyle::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<OUString> aNames{rPropertyName};
+ uno::Sequence<beans::PropertyState> aStates = getPropertyStates(aNames);
+ return aStates.getConstArray()[0];
+}
+
+// allow to retarget the SfxItemSet working on, default correctly. Only
+// use pSourceSet below this point (except in header/footer processing)
+static const SfxItemSet* lcl_GetItemsetForProperty(const SfxItemSet& rSet, SfxStyleFamily eFamily, std::u16string_view rPropertyName)
+{
+ if(eFamily != SfxStyleFamily::Page)
+ return &rSet;
+ const bool isFooter = o3tl::starts_with(rPropertyName, u"Footer");
+ if(!isFooter && !o3tl::starts_with(rPropertyName, u"Header") && rPropertyName != u"" UNO_NAME_FIRST_IS_SHARED)
+ return &rSet;
+ const SvxSetItem* pSetItem;
+ if(!lcl_GetHeaderFooterItem(rSet, rPropertyName, isFooter, pSetItem))
+ return nullptr;
+ return &pSetItem->GetItemSet();
+}
+uno::Sequence<beans::PropertyState> SwXStyle::getPropertyStates(const uno::Sequence<OUString>& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<beans::PropertyState> aRet(rPropertyNames.getLength());
+ beans::PropertyState* pStates = aRet.getArray();
+
+ if(!m_pBasePool)
+ throw uno::RuntimeException();
+ SfxStyleSheetBase* pBase = m_pBasePool->Find(m_sStyleName, m_rEntry.m_eFamily);
+
+ SAL_WARN_IF(!pBase, "sw.uno", "where is the style?");
+ if(!pBase)
+ throw uno::RuntimeException();
+
+ const OUString* pNames = rPropertyNames.getConstArray();
+ rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ sal_Int8 nPropSetId = m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType;
+
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+ const SfxItemSet& rSet = xStyle->GetItemSet();
+
+ for(sal_Int32 i = 0; i < rPropertyNames.getLength(); ++i)
+ {
+ const OUString sPropName = pNames[i];
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(sPropName);
+
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + sPropName, static_cast<cppu::OWeakObject*>(this));
+
+ if (FN_UNO_NUM_RULES == pEntry->nWID || FN_UNO_FOLLOW_STYLE == pEntry->nWID
+ || pEntry->nWID == FN_UNO_LINK_STYLE)
+ {
+ // handle NumRules first, done
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ continue;
+ }
+ const SfxItemSet* pSourceSet = lcl_GetItemsetForProperty(rSet, m_rEntry.m_eFamily, sPropName);
+ if(!pSourceSet)
+ {
+ // if no SetItem, value is ambiguous and we are done
+ pStates[i] = beans::PropertyState_AMBIGUOUS_VALUE;
+ continue;
+ }
+ switch(pEntry->nWID)
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ if(SfxItemState::SET == pSourceSet->GetItemState(XATTR_FILLBMP_STRETCH, false)
+ || SfxItemState::SET == pSourceSet->GetItemState(XATTR_FILLBMP_TILE, false))
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ }
+ break;
+ case RES_BACKGROUND:
+ {
+ // for FlyFrames we need to mark the used properties from type RES_BACKGROUND
+ // as beans::PropertyState_DIRECT_VALUE to let users of this property call
+ // getPropertyValue where the member properties will be mapped from the
+ // fill attributes to the according SvxBrushItem entries
+ if (SWUnoHelper::needToMapFillItemsToSvxBrushItemTypes(*pSourceSet, pEntry->nMemberId))
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ break;
+ default:
+ {
+ pStates[i] = pPropSet->getPropertyState(*pEntry, *pSourceSet);
+
+ if(SfxStyleFamily::Page == m_rEntry.m_eFamily && SID_ATTR_PAGE_SIZE == pEntry->nWID && beans::PropertyState_DIRECT_VALUE == pStates[i])
+ {
+ const SvxSizeItem& rSize = rSet.Get(SID_ATTR_PAGE_SIZE);
+ sal_uInt8 nMemberId = pEntry->nMemberId & 0x7f;
+
+ if((LONG_MAX == rSize.GetSize().Width() && (MID_SIZE_WIDTH == nMemberId || MID_SIZE_SIZE == nMemberId)) ||
+ (LONG_MAX == rSize.GetSize().Height() && MID_SIZE_HEIGHT == nMemberId))
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ }
+ }
+ }
+ return aRet;
+}
+
+void SwXStyle::setPropertyToDefault(const OUString& rPropertyName)
+{
+ const uno::Sequence<OUString> aSequence(&rPropertyName, 1);
+ setPropertiesToDefault(aSequence);
+}
+
+static SwFormat* lcl_GetFormatForStyle(SwDoc const * pDoc, const rtl::Reference<SwDocStyleSheet>& xStyle, const SfxStyleFamily eFamily)
+{
+ if(!xStyle.is())
+ return nullptr;
+ switch(eFamily)
+ {
+ case SfxStyleFamily::Char: return xStyle->GetCharFormat();
+ case SfxStyleFamily::Para: return xStyle->GetCollection();
+ case SfxStyleFamily::Frame: return xStyle->GetFrameFormat();
+ case SfxStyleFamily::Page:
+ {
+ SwPageDesc* pDesc(pDoc->FindPageDesc(xStyle->GetPageDesc()->GetName()));
+ if(pDesc)
+ return &pDesc->GetMaster();
+ }
+ break;
+ default: ;
+ }
+ return nullptr;
+}
+
+void SAL_CALL SwXStyle::setPropertiesToDefault(const uno::Sequence<OUString>& aPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(GetStyleSheetBase())));
+ SwFormat* pTargetFormat = lcl_GetFormatForStyle(m_pDoc, xStyle, m_rEntry.m_eFamily);
+ if(!pTargetFormat)
+ {
+ if(!m_bIsDescriptor)
+ return;
+ for(const auto& rName : aPropertyNames)
+ m_pPropertiesImpl->ClearProperty(rName);
+ return;
+ }
+ const sal_Int8 nPropSetId = m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType;
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap &rMap = pPropSet->getPropertyMap();
+ for(const auto& rName : aPropertyNames)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(rName);
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rName, static_cast<cppu::OWeakObject*>(this));
+ if (pEntry->nWID == FN_UNO_FOLLOW_STYLE || pEntry->nWID == FN_UNO_LINK_STYLE
+ || pEntry->nWID == FN_UNO_NUM_RULES)
+ throw uno::RuntimeException("Cannot reset: " + rName, static_cast<cppu::OWeakObject*>(this));
+ if(pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw uno::RuntimeException("setPropertiesToDefault: property is read-only: " + rName, static_cast<cppu::OWeakObject*>(this));
+ if(pEntry->nWID == RES_PARATR_OUTLINELEVEL)
+ {
+ static_cast<SwTextFormatColl*>(pTargetFormat)->DeleteAssignmentToListLevelOfOutlineStyle();
+ continue;
+ }
+ pTargetFormat->ResetFormatAttr(pEntry->nWID);
+ if(OWN_ATTR_FILLBMP_MODE == pEntry->nWID)
+ {
+ //
+ SwDoc* pDoc = pTargetFormat->GetDoc();
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet(pDoc->GetAttrPool());
+ aSet.SetParent(&pTargetFormat->GetAttrSet());
+
+ aSet.ClearItem(XATTR_FILLBMP_STRETCH);
+ aSet.ClearItem(XATTR_FILLBMP_TILE);
+
+ pTargetFormat->SetFormatAttr(aSet);
+ }
+ }
+}
+
+void SAL_CALL SwXStyle::setAllPropertiesToDefault()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pBasePool)
+ {
+ if(!m_bIsDescriptor)
+ throw uno::RuntimeException();
+ m_pPropertiesImpl->ClearAllProperties();
+ return;
+ }
+ const rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(GetStyleSheetBase())));
+ if(!xStyle.is())
+ throw uno::RuntimeException();
+ if(SfxStyleFamily::Page == m_rEntry.m_eFamily)
+ {
+ size_t nPgDscPos(0);
+ SwPageDesc* pDesc = m_pDoc->FindPageDesc(xStyle->GetPageDesc()->GetName(), &nPgDscPos);
+ SwFormat* pPageFormat(nullptr);
+ if(pDesc)
+ {
+ pPageFormat = &pDesc->GetMaster();
+ pDesc->SetUseOn(UseOnPage::All);
+ }
+ else
+ pPageFormat = lcl_GetFormatForStyle(m_pDoc, xStyle, m_rEntry.m_eFamily);
+ SwPageDesc& rPageDesc = m_pDoc->GetPageDesc(nPgDscPos);
+ rPageDesc.ResetAllMasterAttr();
+
+ pPageFormat->SetPageFormatToDefault();
+ SwPageDesc* pStdPgDsc = m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD);
+ std::shared_ptr<SwFormatFrameSize> aFrameSz(std::make_shared<SwFormatFrameSize>(SwFrameSize::Fixed));
+
+ if(RES_POOLPAGE_STANDARD == rPageDesc.GetPoolFormatId())
+ {
+ if(m_pDoc->getIDocumentDeviceAccess().getPrinter(false))
+ {
+ const Size aPhysSize( SvxPaperInfo::GetPaperSize(
+ static_cast<Printer*>(m_pDoc->getIDocumentDeviceAccess().getPrinter(false))));
+ aFrameSz->SetSize(aPhysSize);
+ }
+ else
+ {
+ aFrameSz->SetSize(SvxPaperInfo::GetDefaultPaperSize());
+ }
+
+ }
+ else
+ {
+ aFrameSz.reset(pStdPgDsc->GetMaster().GetFrameSize().Clone());
+ }
+
+ if(pStdPgDsc->GetLandscape())
+ {
+ SwTwips nTmp = aFrameSz->GetHeight();
+ aFrameSz->SetHeight(aFrameSz->GetWidth());
+ aFrameSz->SetWidth(nTmp);
+ }
+
+ pPageFormat->SetFormatAttr(*aFrameSz);
+ m_pDoc->ChgPageDesc(nPgDscPos, m_pDoc->GetPageDesc(nPgDscPos));
+ return;
+ }
+ if(SfxStyleFamily::Para == m_rEntry.m_eFamily)
+ {
+ if(xStyle->GetCollection())
+ xStyle->GetCollection()->DeleteAssignmentToListLevelOfOutlineStyle();
+ }
+ SwFormat* const pTargetFormat = lcl_GetFormatForStyle(m_pDoc, xStyle, m_rEntry.m_eFamily);
+ if(!pTargetFormat)
+ return;
+ pTargetFormat->ResetAllFormatAttr();
+}
+
+uno::Sequence<uno::Any> SAL_CALL SwXStyle::getPropertyDefaults(const uno::Sequence<OUString>& aPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nCount = aPropertyNames.getLength();
+ uno::Sequence<uno::Any> aRet(nCount);
+ if(!nCount)
+ return aRet;
+ auto pRet = aRet.getArray();
+ SfxStyleSheetBase* pBase = GetStyleSheetBase();
+ if(!pBase)
+ throw uno::RuntimeException();
+ rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ const sal_Int8 nPropSetId = m_bIsConditional ? PROPERTY_MAP_CONDITIONAL_PARA_STYLE : m_rEntry.m_nPropMapType;
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+
+ const SfxItemSet &rSet = xStyle->GetItemSet(), *pParentSet = rSet.GetParent();
+ for(sal_Int32 i = 0; i < nCount; ++i)
+ {
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(aPropertyNames[i]);
+
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + aPropertyNames[i], static_cast < cppu::OWeakObject * >(this));
+ // these cannot be in an item set, especially not the
+ // parent set, so the default value is void
+ if (pEntry->nWID >= RES_UNKNOWNATR_END)
+ continue;
+
+ if(pParentSet)
+ {
+ aSwMapProvider.GetPropertySet(nPropSetId)->getPropertyValue(aPropertyNames[i], *pParentSet, pRet[i]);
+ }
+ else if(pEntry->nWID != rSet.GetPool()->GetSlotId(pEntry->nWID))
+ {
+ const SfxPoolItem& rItem = rSet.GetPool()->GetDefaultItem(pEntry->nWID);
+ rItem.QueryValue(pRet[i], pEntry->nMemberId);
+ }
+ }
+ return aRet;
+}
+
+uno::Any SwXStyle::getPropertyDefault(const OUString& rPropertyName)
+{
+ const uno::Sequence<OUString> aSequence(&rPropertyName, 1);
+ return getPropertyDefaults(aSequence)[0];
+}
+
+void SwXStyle::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ if((rHint.GetId() == SfxHintId::Dying) || (rHint.GetId() == SfxHintId::StyleSheetErased))
+ {
+ m_pBasePool = nullptr;
+ SfxListener::EndListening(rBC);
+ }
+ else if(rHint.GetId() == SfxHintId::StyleSheetChanged)
+ {
+ SfxStyleSheetBasePool& rBP = static_cast<SfxStyleSheetBasePool&>(rBC);
+ SfxStyleSheetBase* pOwnBase = rBP.Find(m_sStyleName, m_rEntry.m_eFamily);
+ if(!pOwnBase)
+ {
+ SfxListener::EndListening(rBC);
+ Invalidate();
+ }
+ }
+}
+
+void SwXStyle::Invalidate()
+{
+ m_sStyleName.clear();
+ m_pBasePool = nullptr;
+ m_pDoc = nullptr;
+ m_xStyleData.clear();
+ m_xStyleFamily.clear();
+}
+
+SwXPageStyle::SwXPageStyle(SfxStyleSheetBasePool& rPool, SwDocShell* pDocSh, const OUString& rStyleName)
+ : SwXStyle(&rPool, SfxStyleFamily::Page, pDocSh->GetDoc(), rStyleName)
+{ }
+
+SwXPageStyle::SwXPageStyle(SwDocShell* pDocSh)
+ : SwXStyle(pDocSh->GetDoc(), SfxStyleFamily::Page)
+{ }
+
+void SwXStyle::PutItemToSet(const SvxSetItem* pSetItem, const SfxItemPropertySet& rPropSet, const SfxItemPropertyMapEntry& rEntry, const uno::Any& rVal, SwStyleBase_Impl& rBaseImpl)
+{
+ // create a new SvxSetItem and get it's ItemSet as new target
+ std::unique_ptr<SvxSetItem> pNewSetItem(pSetItem->Clone());
+ SfxItemSet& rSetSet = pNewSetItem->GetItemSet();
+
+ // set parent to ItemSet to ensure XFILL_NONE as XFillStyleItem
+ rSetSet.SetParent(&m_pDoc->GetDfltFrameFormat()->GetAttrSet());
+
+ // replace the used SfxItemSet at the SwStyleBase_Impl temporarily and use the
+ // default method to set the property
+ {
+ SwStyleBase_Impl::ItemSetOverrider o(rBaseImpl, &rSetSet);
+ SetStyleProperty(rEntry, rPropSet, rVal, rBaseImpl);
+ }
+
+ // reset parent at ItemSet from SetItem
+ rSetSet.SetParent(nullptr);
+
+ // set the new SvxSetItem at the real target and delete it
+ rBaseImpl.GetItemSet().Put(std::move(pNewSetItem));
+}
+
+void SwXPageStyle::SetPropertyValues_Impl(const uno::Sequence<OUString>& rPropertyNames, const uno::Sequence<uno::Any>& rValues)
+{
+ if(!GetDoc())
+ throw uno::RuntimeException();
+
+ if(rPropertyNames.getLength() != rValues.getLength())
+ throw lang::IllegalArgumentException();
+
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PAGE_STYLE);
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+ SwStyleBase_Impl aBaseImpl(*GetDoc(), GetStyleName(), &GetDoc()->GetDfltFrameFormat()->GetAttrSet()); // add pDfltFrameFormat as parent
+ if(!m_pBasePool)
+ {
+ if(!IsDescriptor())
+ throw uno::RuntimeException();
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); ++nProp)
+ if(!m_pPropertiesImpl->SetProperty(rPropertyNames[nProp], rValues[nProp]))
+ throw lang::IllegalArgumentException();
+ return;
+ }
+ SfxStyleSheetBase* pBase = GetStyleSheetBase();
+ if(!pBase)
+ throw uno::RuntimeException();
+ aBaseImpl.setNewBase(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); ++nProp)
+ {
+ const OUString& rPropName = rPropertyNames[nProp];
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(rPropName);
+
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropName, static_cast<cppu::OWeakObject*>(this));
+ if(pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException("Property is read-only: " + rPropName, static_cast<cppu::OWeakObject*>(this));
+
+ const bool bHeader(rPropName.startsWith("Header"));
+ const bool bFooter(rPropName.startsWith("Footer"));
+ const bool bFirstIsShared(rPropName == UNO_NAME_FIRST_IS_SHARED);
+ if(bHeader || bFooter || bFirstIsShared)
+ {
+ switch(pEntry->nWID)
+ {
+ case SID_ATTR_PAGE_ON:
+ case RES_BACKGROUND:
+ case RES_BOX:
+ case RES_LR_SPACE:
+ case RES_SHADOW:
+ case RES_UL_SPACE:
+ case SID_ATTR_PAGE_DYNAMIC:
+ case SID_ATTR_PAGE_SHARED:
+ case SID_ATTR_PAGE_SHARED_FIRST:
+ case SID_ATTR_PAGE_SIZE:
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ {
+ // it is a Header/Footer entry, access the SvxSetItem containing it's information
+ const SvxSetItem* pSetItem = nullptr;
+ if (lcl_GetHeaderFooterItem(aBaseImpl.GetItemSet(), rPropName, bFooter, pSetItem))
+ {
+ PutItemToSet(pSetItem, *pPropSet, *pEntry, rValues[nProp], aBaseImpl);
+
+ if (pEntry->nWID == SID_ATTR_PAGE_SHARED_FIRST)
+ {
+ // Need to add this to the other as well
+ pSetItem = aBaseImpl.GetItemSet().GetItemIfSet(
+ bFooter ? SID_ATTR_PAGE_HEADERSET : SID_ATTR_PAGE_FOOTERSET,
+ false);
+ if (pSetItem)
+ {
+ PutItemToSet(pSetItem, *pPropSet, *pEntry, rValues[nProp], aBaseImpl);
+ }
+ }
+ }
+ else if(pEntry->nWID == SID_ATTR_PAGE_ON && rValues[nProp].get<bool>())
+ {
+ // Header/footer gets switched on, create defaults and the needed SfxSetItem
+ SfxItemSetFixed
+ <RES_FRMATR_BEGIN,RES_FRMATR_END - 1, // [82
+
+ // FillAttribute support
+ XATTR_FILL_FIRST, XATTR_FILL_LAST, // [1014
+
+ SID_ATTR_BORDER_INNER,SID_ATTR_BORDER_INNER, // [10023
+ SID_ATTR_PAGE_SIZE,SID_ATTR_PAGE_SIZE, // [10051
+ SID_ATTR_PAGE_ON,SID_ATTR_PAGE_SHARED, // [10060
+ SID_ATTR_PAGE_SHARED_FIRST,SID_ATTR_PAGE_SHARED_FIRST>
+ aTempSet(*aBaseImpl.GetItemSet().GetPool());
+
+ // set correct parent to get the XFILL_NONE FillStyle as needed
+ aTempSet.SetParent(&GetDoc()->GetDfltFrameFormat()->GetAttrSet());
+
+ aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_ON, true));
+ constexpr tools::Long constTwips_5mm = o3tl::toTwips(5, o3tl::Length::mm);
+ aTempSet.Put(SvxSizeItem(SID_ATTR_PAGE_SIZE, Size(constTwips_5mm, constTwips_5mm)));
+ aTempSet.Put(SvxLRSpaceItem(RES_LR_SPACE));
+ aTempSet.Put(SvxULSpaceItem(RES_UL_SPACE));
+ aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_SHARED, true));
+ aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_SHARED_FIRST, true));
+ aTempSet.Put(SfxBoolItem(SID_ATTR_PAGE_DYNAMIC, true));
+
+ SvxSetItem aNewSetItem(bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, aTempSet);
+ aBaseImpl.GetItemSet().Put(aNewSetItem);
+ }
+ }
+ continue;
+ case XATTR_FILLBMP_SIZELOG:
+ case XATTR_FILLBMP_TILEOFFSETX:
+ case XATTR_FILLBMP_TILEOFFSETY:
+ case XATTR_FILLBMP_POSOFFSETX:
+ case XATTR_FILLBMP_POSOFFSETY:
+ case XATTR_FILLBMP_POS:
+ case XATTR_FILLBMP_SIZEX:
+ case XATTR_FILLBMP_SIZEY:
+ case XATTR_FILLBMP_STRETCH:
+ case XATTR_FILLBMP_TILE:
+ case OWN_ATTR_FILLBMP_MODE:
+ case XATTR_FILLCOLOR:
+ case XATTR_FILLBACKGROUND:
+ case XATTR_FILLBITMAP:
+ case XATTR_GRADIENTSTEPCOUNT:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLSTYLE:
+ case XATTR_FILLTRANSPARENCE:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ case XATTR_SECONDARYFILLCOLOR:
+ if(bFirstIsShared) // only special handling for headers/footers here
+ break;
+ {
+ const SvxSetItem* pSetItem =
+ aBaseImpl.GetItemSet().GetItemIfSet(bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, false);
+
+ if(pSetItem)
+ {
+ // create a new SvxSetItem and get it's ItemSet as new target
+ std::unique_ptr<SvxSetItem> pNewSetItem(pSetItem->Clone());
+ SfxItemSet& rSetSet = pNewSetItem->GetItemSet();
+
+ // set parent to ItemSet to ensure XFILL_NONE as XFillStyleItem
+ rSetSet.SetParent(&GetDoc()->GetDfltFrameFormat()->GetAttrSet());
+
+ // replace the used SfxItemSet at the SwStyleBase_Impl temporarily and use the
+ // default method to set the property
+ {
+ SwStyleBase_Impl::ItemSetOverrider o(aBaseImpl, &rSetSet);
+ SetStyleProperty(*pEntry, *pPropSet, rValues[nProp], aBaseImpl);
+ }
+
+ // reset parent at ItemSet from SetItem
+ rSetSet.SetParent(nullptr);
+
+ // set the new SvxSetItem at the real target and delete it
+ aBaseImpl.GetItemSet().Put(std::move(pNewSetItem));
+ }
+ }
+ continue;
+ default: ;
+ }
+ }
+ switch(pEntry->nWID)
+ {
+ case SID_ATTR_PAGE_DYNAMIC:
+ case SID_ATTR_PAGE_SHARED:
+ case SID_ATTR_PAGE_SHARED_FIRST:
+ case SID_ATTR_PAGE_ON:
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ // these slots are exclusive to Header/Footer, thus this is an error
+ throw beans::UnknownPropertyException("Unknown property: " + rPropName, static_cast<cppu::OWeakObject*>(this));
+ case FN_UNO_HEADER:
+ case FN_UNO_HEADER_LEFT:
+ case FN_UNO_HEADER_RIGHT:
+ case FN_UNO_HEADER_FIRST:
+ case FN_UNO_FOOTER:
+ case FN_UNO_FOOTER_LEFT:
+ case FN_UNO_FOOTER_RIGHT:
+ case FN_UNO_FOOTER_FIRST:
+ throw lang::IllegalArgumentException();
+ case FN_PARAM_FTN_INFO:
+ {
+ const SwPageFootnoteInfoItem& rItem = aBaseImpl.GetItemSet().Get(FN_PARAM_FTN_INFO);
+ std::unique_ptr<SfxPoolItem> pNewFootnoteItem(rItem.Clone());
+ if(!pNewFootnoteItem->PutValue(rValues[nProp], pEntry->nMemberId))
+ throw lang::IllegalArgumentException();
+ aBaseImpl.GetItemSet().Put(std::move(pNewFootnoteItem));
+ break;
+ }
+ default:
+ {
+ SetStyleProperty(*pEntry, *pPropSet, rValues[nProp], aBaseImpl);
+ break;
+ }
+ }
+ }
+
+ if(aBaseImpl.HasItemSet())
+ {
+ ::sw::UndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
+
+ if (undoGuard.UndoWasEnabled())
+ {
+ // Fix i64460: as long as Undo of page styles with header/footer causes trouble...
+ GetDoc()->GetIDocumentUndoRedo().DelAllUndoObj();
+ }
+
+ aBaseImpl.getNewBase()->SetItemSet(aBaseImpl.GetItemSet());
+ }
+}
+
+void SwXPageStyle::setPropertyValues(const uno::Sequence<OUString>& rPropertyNames, const uno::Sequence<uno::Any>& rValues)
+{
+ SolarMutexGuard aGuard;
+
+ // workaround for bad designed API
+ try
+ {
+ SetPropertyValues_Impl(rPropertyNames, rValues);
+ }
+ catch (const beans::UnknownPropertyException &rException)
+ {
+ // wrap the original (here not allowed) exception in
+ // a lang::WrappedTargetException that gets thrown instead.
+ lang::WrappedTargetException aWExc;
+ aWExc.TargetException <<= rException;
+ throw aWExc;
+ }
+}
+
+static uno::Reference<text::XText> lcl_makeHeaderFooter(const sal_uInt16 nRes, const bool bHeader, SwFrameFormat const*const pFrameFormat)
+{
+ if (!pFrameFormat)
+ return nullptr;
+ const SfxItemSet& rSet = pFrameFormat->GetAttrSet();
+ const SfxPoolItem* pItem;
+ if(SfxItemState::SET != rSet.GetItemState(nRes, true, &pItem))
+ return nullptr;
+ SwFrameFormat* const pHeadFootFormat = bHeader
+ ? static_cast<SwFormatHeader*>(const_cast<SfxPoolItem*>(pItem))->GetHeaderFormat()
+ : static_cast<SwFormatFooter*>(const_cast<SfxPoolItem*>(pItem))->GetFooterFormat();
+ if(!pHeadFootFormat)
+ return nullptr;
+ return SwXHeadFootText::CreateXHeadFootText(*pHeadFootFormat, bHeader);
+}
+
+uno::Sequence<uno::Any> SwXPageStyle::GetPropertyValues_Impl(const uno::Sequence<OUString>& rPropertyNames)
+{
+ if(!GetDoc())
+ throw uno::RuntimeException();
+
+ sal_Int32 nLength = rPropertyNames.getLength();
+ uno::Sequence<uno::Any> aRet (nLength);
+ auto aRetRange = asNonConstRange(aRet);
+ if(!m_pBasePool)
+ {
+ if(!IsDescriptor())
+ throw uno::RuntimeException();
+ for(sal_Int32 nProp = 0; nProp < rPropertyNames.getLength(); ++nProp)
+ {
+ const uno::Any* pAny = nullptr;
+ m_pPropertiesImpl->GetProperty(rPropertyNames[nProp], pAny);
+ if (!pAny->hasValue())
+ SwStyleProperties_Impl::GetProperty(rPropertyNames[nProp], m_xStyleData, aRetRange[nProp]);
+ else
+ aRetRange[nProp] = *pAny;
+ }
+ return aRet;
+ }
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PAGE_STYLE);
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+ SwStyleBase_Impl aBase(*GetDoc(), GetStyleName(), &GetDoc()->GetDfltFrameFormat()->GetAttrSet()); // add pDfltFrameFormat as parent
+ SfxStyleSheetBase* pBase = GetStyleSheetBase();
+ if(!pBase)
+ throw uno::RuntimeException();
+ for(sal_Int32 nProp = 0; nProp < nLength; ++nProp)
+ {
+ const OUString& rPropName = rPropertyNames[nProp];
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(rPropName);
+
+ if (!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropName, static_cast < cppu::OWeakObject * > ( this ) );
+ const bool bHeader(rPropName.startsWith("Header"));
+ const bool bFooter(rPropName.startsWith("Footer"));
+ const bool bFirstIsShared(rPropName == UNO_NAME_FIRST_IS_SHARED);
+ if(bHeader || bFooter || bFirstIsShared)
+ {
+ switch(pEntry->nWID)
+ {
+ case SID_ATTR_PAGE_ON:
+ case RES_BACKGROUND:
+ case RES_BOX:
+ case RES_LR_SPACE:
+ case RES_SHADOW:
+ case RES_UL_SPACE:
+ case SID_ATTR_PAGE_DYNAMIC:
+ case SID_ATTR_PAGE_SHARED:
+ case SID_ATTR_PAGE_SHARED_FIRST:
+ case SID_ATTR_PAGE_SIZE:
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ {
+ // slot is a Header/Footer slot
+ rtl::Reference< SwDocStyleSheet > xStyle( new SwDocStyleSheet( *static_cast<SwDocStyleSheet*>(pBase) ) );
+ const SfxItemSet& rSet = xStyle->GetItemSet();
+ const SvxSetItem* pSetItem;
+
+ if (lcl_GetHeaderFooterItem(rSet, rPropName, bFooter, pSetItem))
+ {
+ // get from SfxItemSet of the corresponding SfxSetItem
+ const SfxItemSet& rSetSet = pSetItem->GetItemSet();
+ {
+ SwStyleBase_Impl::ItemSetOverrider o(aBase, &const_cast< SfxItemSet& >(rSetSet));
+ aRetRange[nProp] = GetStyleProperty_Impl(*pEntry, *pPropSet, aBase);
+ }
+ }
+ else if(pEntry->nWID == SID_ATTR_PAGE_ON)
+ {
+ // header/footer is not available, thus off. Default is <false>, though
+ aRetRange[nProp] <<= false;
+ }
+ }
+ continue;
+ case XATTR_FILLBMP_SIZELOG:
+ case XATTR_FILLBMP_TILEOFFSETX:
+ case XATTR_FILLBMP_TILEOFFSETY:
+ case XATTR_FILLBMP_POSOFFSETX:
+ case XATTR_FILLBMP_POSOFFSETY:
+ case XATTR_FILLBMP_POS:
+ case XATTR_FILLBMP_SIZEX:
+ case XATTR_FILLBMP_SIZEY:
+ case XATTR_FILLBMP_STRETCH:
+ case XATTR_FILLBMP_TILE:
+ case OWN_ATTR_FILLBMP_MODE:
+ case XATTR_FILLCOLOR:
+ case XATTR_FILLBACKGROUND:
+ case XATTR_FILLBITMAP:
+ case XATTR_GRADIENTSTEPCOUNT:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLSTYLE:
+ case XATTR_FILLTRANSPARENCE:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ case XATTR_SECONDARYFILLCOLOR:
+ if(bFirstIsShared) // only special handling for headers/footers here
+ break;
+ {
+ rtl::Reference< SwDocStyleSheet > xStyle( new SwDocStyleSheet( *static_cast<SwDocStyleSheet*>(pBase) ) );
+ const SfxItemSet& rSet = xStyle->GetItemSet();
+ const SvxSetItem* pSetItem =
+ rSet.GetItemIfSet(bFooter ? SID_ATTR_PAGE_FOOTERSET : SID_ATTR_PAGE_HEADERSET, false);
+ if(pSetItem)
+ {
+ // set at SfxItemSet of the corresponding SfxSetItem
+ const SfxItemSet& rSetSet = pSetItem->GetItemSet();
+ {
+ SwStyleBase_Impl::ItemSetOverrider o(aBase, &const_cast<SfxItemSet&>(rSetSet));
+ aRetRange[nProp] = GetStyleProperty_Impl(*pEntry, *pPropSet, aBase);
+ }
+ }
+ }
+ continue;
+ default: ;
+ }
+ }
+ switch(pEntry->nWID)
+ {
+ // these slots are exclusive to Header/Footer, thus this is an error
+ case SID_ATTR_PAGE_DYNAMIC:
+ case SID_ATTR_PAGE_SHARED:
+ case SID_ATTR_PAGE_SHARED_FIRST:
+ case SID_ATTR_PAGE_ON:
+ case RES_HEADER_FOOTER_EAT_SPACING:
+ throw beans::UnknownPropertyException( "Unknown property: " + rPropName, static_cast < cppu::OWeakObject * > ( this ) );
+ case FN_UNO_HEADER:
+ case FN_UNO_HEADER_LEFT:
+ case FN_UNO_HEADER_FIRST:
+ case FN_UNO_HEADER_RIGHT:
+ case FN_UNO_FOOTER:
+ case FN_UNO_FOOTER_LEFT:
+ case FN_UNO_FOOTER_FIRST:
+ case FN_UNO_FOOTER_RIGHT:
+ {
+ bool bLeft(false);
+ bool bFirst(false);
+ sal_uInt16 nRes = 0;
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_HEADER: nRes = RES_HEADER; break;
+ case FN_UNO_HEADER_LEFT: nRes = RES_HEADER; bLeft = true; break;
+ case FN_UNO_HEADER_FIRST: nRes = RES_HEADER; bFirst = true; break;
+ case FN_UNO_HEADER_RIGHT: nRes = RES_HEADER; break;
+ case FN_UNO_FOOTER: nRes = RES_FOOTER; break;
+ case FN_UNO_FOOTER_LEFT: nRes = RES_FOOTER; bLeft = true; break;
+ case FN_UNO_FOOTER_FIRST: nRes = RES_FOOTER; bFirst = true; break;
+ case FN_UNO_FOOTER_RIGHT: nRes = RES_FOOTER; break;
+ default: ;
+ }
+
+ const SwPageDesc* pDesc = aBase.GetOldPageDesc();
+ assert(pDesc);
+ const SwFrameFormat* pFrameFormat = nullptr;
+ bool bShare = (nRes == RES_HEADER && pDesc->IsHeaderShared()) || (nRes == RES_FOOTER && pDesc->IsFooterShared());
+ bool bShareFirst = pDesc->IsFirstShared();
+ // TextLeft returns the left content if there is one,
+ // Text and TextRight return the master content.
+ // TextRight does the same as Text and is for
+ // compatibility only.
+ if(bLeft && !bShare)
+ pFrameFormat = &pDesc->GetLeft();
+ else if(bFirst && !bShareFirst)
+ {
+ pFrameFormat = &pDesc->GetFirstMaster();
+ // no need to make GetFirstLeft() accessible
+ // since it is always shared
+ }
+ else
+ pFrameFormat = &pDesc->GetMaster();
+ const uno::Reference<text::XText> xRet = lcl_makeHeaderFooter(nRes, nRes == RES_HEADER, pFrameFormat);
+ if (xRet.is())
+ aRetRange[nProp] <<= xRet;
+ }
+ break;
+ case FN_PARAM_FTN_INFO:
+ {
+ rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ const SfxItemSet& rSet = xStyle->GetItemSet();
+ const SfxPoolItem& rItem = rSet.Get(FN_PARAM_FTN_INFO);
+ rItem.QueryValue(aRetRange[nProp], pEntry->nMemberId);
+ }
+ break;
+ default:
+ aRetRange[nProp] = GetStyleProperty_Impl(*pEntry, *pPropSet, aBase);
+ }
+ }
+ return aRet;
+}
+
+uno::Sequence<uno::Any> SwXPageStyle::getPropertyValues(const uno::Sequence<OUString>& rPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<uno::Any> aValues;
+
+ // workaround for bad designed API
+ try
+ {
+ aValues = GetPropertyValues_Impl(rPropertyNames);
+ }
+ catch(beans::UnknownPropertyException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("Unknown property exception caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+ catch(lang::WrappedTargetException &)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("WrappedTargetException caught",
+ static_cast < cppu::OWeakObject * > ( this ), anyEx );
+ }
+
+ return aValues;
+}
+
+uno::Any SwXPageStyle::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ const uno::Sequence<OUString> aProperties(&rPropertyName, 1);
+ return GetPropertyValues_Impl(aProperties)[0];
+}
+
+void SwXPageStyle::setPropertyValue(const OUString& rPropertyName, const uno::Any& rValue)
+{
+ SolarMutexGuard aGuard;
+ const uno::Sequence<OUString> aProperties(&rPropertyName, 1);
+ const uno::Sequence<uno::Any> aValues(&rValue, 1);
+
+ // Trick: if the Domain Mapper changes the props of shared header/footer,
+ // store the old ones in time for later use.
+ const bool bIsHeader = rPropertyName == UNO_NAME_HEADER_IS_SHARED;
+ const bool bIsFooter = rPropertyName == UNO_NAME_FOOTER_IS_SHARED;
+ if ((bIsFooter || bIsHeader) && rValue == uno::Any(true))
+ {
+ // Find the matching page descriptor
+ for (size_t i = 0; i < GetDoc()->GetPageDescCnt(); i++)
+ {
+ auto pPageDesc = &GetDoc()->GetPageDesc(i);
+ // If we have the right page descriptor stash the necessary formats in import time.
+ if (pPageDesc->GetName() == GetStyleName())
+ {
+ auto pLeftHeader = pPageDesc->GetLeft().GetHeader().GetHeaderFormat();
+ if (bIsHeader && pLeftHeader)
+ {
+ pPageDesc->StashFrameFormat(pPageDesc->GetLeft(), true, true, false);
+ pPageDesc->StashFrameFormat(pPageDesc->GetFirstMaster(), true, false, true);
+ pPageDesc->StashFrameFormat(pPageDesc->GetFirstLeft(), true, true, true);
+ }
+ auto pLeftFooter = pPageDesc->GetLeft().GetFooter().GetFooterFormat();
+ if (bIsFooter && pLeftFooter)
+ {
+ pPageDesc->StashFrameFormat(pPageDesc->GetLeft(), false, true, false);
+ pPageDesc->StashFrameFormat(pPageDesc->GetFirstMaster(), false, false, true);
+ pPageDesc->StashFrameFormat(pPageDesc->GetFirstLeft(), false, true, true);
+ }
+ }
+ }
+ }
+ // And set the props... as we did it before.
+ SetPropertyValues_Impl(aProperties, aValues);
+}
+
+SwXFrameStyle::SwXFrameStyle(SwDoc *pDoc)
+ : SwXStyle(pDoc, SfxStyleFamily::Frame, false)
+{ }
+
+void SwXFrameStyle::SetItem(sal_uInt16 eAtr, const SfxPoolItem& rItem)
+{
+ assert(eAtr >= RES_FRMATR_BEGIN && eAtr < RES_FRMATR_END);
+ SfxStyleSheetBase* pBase = GetStyleSheetBase();
+ if(!pBase)
+ return;
+ rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ SfxItemSet& rStyleSet = xStyle->GetItemSet();
+ SfxItemSet aSet(*rStyleSet.GetPool(), sal_uInt16(eAtr), sal_uInt16(eAtr));
+ aSet.Put(rItem);
+ xStyle->SetItemSet(aSet);
+}
+
+const SfxPoolItem* SwXFrameStyle::GetItem(sal_uInt16 eAtr)
+{
+ assert(eAtr >= RES_FRMATR_BEGIN && eAtr < RES_FRMATR_END);
+ SfxStyleSheetBase* pBase = GetStyleSheetBase();
+ if(!pBase)
+ return nullptr;
+ rtl::Reference<SwDocStyleSheet> xStyle(new SwDocStyleSheet(*static_cast<SwDocStyleSheet*>(pBase)));
+ return &xStyle->GetItemSet().Get(eAtr);
+}
+
+uno::Sequence<uno::Type> SwXFrameStyle::getTypes()
+{
+ return cppu::OTypeCollection(
+ cppu::UnoType<XEventsSupplier>::get(),
+ SwXStyle::getTypes()
+ ).getTypes();
+}
+
+uno::Any SwXFrameStyle::queryInterface(const uno::Type& rType)
+{
+ if(rType == cppu::UnoType<XEventsSupplier>::get())
+ return uno::Any(uno::Reference<XEventsSupplier>(this));
+ return SwXStyle::queryInterface(rType);
+}
+
+uno::Reference<container::XNameReplace> SwXFrameStyle::getEvents()
+{
+ return new SwFrameStyleEventDescriptor(*this);
+}
+
+// Already implemented autostyle families: 3
+#define AUTOSTYLE_FAMILY_COUNT 3
+const IStyleAccess::SwAutoStyleFamily aAutoStyleByIndex[] =
+{
+ IStyleAccess::AUTO_STYLE_CHAR,
+ IStyleAccess::AUTO_STYLE_RUBY,
+ IStyleAccess::AUTO_STYLE_PARA
+};
+
+class SwAutoStylesEnumImpl
+{
+ std::vector<std::shared_ptr<SfxItemSet>> mAutoStyles;
+ std::vector<std::shared_ptr<SfxItemSet>>::iterator m_aIter;
+ SwDoc& m_rDoc;
+ IStyleAccess::SwAutoStyleFamily m_eFamily;
+public:
+ SwAutoStylesEnumImpl( SwDoc& rInitDoc, IStyleAccess::SwAutoStyleFamily eFam );
+ bool hasMoreElements() { return m_aIter != mAutoStyles.end(); }
+ std::shared_ptr<SfxItemSet> const & nextElement() { return *(m_aIter++); }
+ IStyleAccess::SwAutoStyleFamily getFamily() const { return m_eFamily; }
+ SwDoc& getDoc() const { return m_rDoc; }
+};
+
+SwXAutoStyles::SwXAutoStyles(SwDocShell& rDocShell) :
+ SwUnoCollection(rDocShell.GetDoc()), m_pDocShell( &rDocShell )
+{
+}
+
+SwXAutoStyles::~SwXAutoStyles()
+{
+}
+
+sal_Int32 SwXAutoStyles::getCount()
+{
+ return AUTOSTYLE_FAMILY_COUNT;
+}
+
+uno::Any SwXAutoStyles::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ if(nIndex < 0 || nIndex >= AUTOSTYLE_FAMILY_COUNT)
+ throw lang::IndexOutOfBoundsException();
+ if(!IsValid())
+ throw uno::RuntimeException();
+
+ uno::Reference< style::XAutoStyleFamily > aRef;
+ IStyleAccess::SwAutoStyleFamily nType = aAutoStyleByIndex[nIndex];
+ switch( nType )
+ {
+ case IStyleAccess::AUTO_STYLE_CHAR:
+ {
+ if(!m_xAutoCharStyles.is())
+ m_xAutoCharStyles = new SwXAutoStyleFamily(m_pDocShell, nType);
+ aRef = m_xAutoCharStyles;
+ }
+ break;
+ case IStyleAccess::AUTO_STYLE_RUBY:
+ {
+ if(!m_xAutoRubyStyles.is())
+ m_xAutoRubyStyles = new SwXAutoStyleFamily(m_pDocShell, nType );
+ aRef = m_xAutoRubyStyles;
+ }
+ break;
+ case IStyleAccess::AUTO_STYLE_PARA:
+ {
+ if(!m_xAutoParaStyles.is())
+ m_xAutoParaStyles = new SwXAutoStyleFamily(m_pDocShell, nType );
+ aRef = m_xAutoParaStyles;
+ }
+ break;
+
+ default:
+ ;
+ }
+ aRet <<= aRef;
+
+ return aRet;
+}
+
+uno::Type SwXAutoStyles::getElementType( )
+{
+ return cppu::UnoType<style::XAutoStyleFamily>::get();
+}
+
+sal_Bool SwXAutoStyles::hasElements( )
+{
+ return true;
+}
+
+uno::Any SwXAutoStyles::getByName(const OUString& Name)
+{
+ uno::Any aRet;
+ if(Name == "CharacterStyles")
+ aRet = getByIndex(0);
+ else if(Name == "RubyStyles")
+ aRet = getByIndex(1);
+ else if(Name == "ParagraphStyles")
+ aRet = getByIndex(2);
+ else
+ throw container::NoSuchElementException();
+ return aRet;
+}
+
+uno::Sequence< OUString > SwXAutoStyles::getElementNames()
+{
+ uno::Sequence< OUString > aNames(AUTOSTYLE_FAMILY_COUNT);
+ OUString* pNames = aNames.getArray();
+ pNames[0] = "CharacterStyles";
+ pNames[1] = "RubyStyles";
+ pNames[2] = "ParagraphStyles";
+ return aNames;
+}
+
+sal_Bool SwXAutoStyles::hasByName(const OUString& Name)
+{
+ if( Name == "CharacterStyles" ||
+ Name == "RubyStyles" ||
+ Name == "ParagraphStyles" )
+ return true;
+ else
+ return false;
+}
+
+SwXAutoStyleFamily::SwXAutoStyleFamily(SwDocShell* pDocSh, IStyleAccess::SwAutoStyleFamily nFamily) :
+ m_pDocShell( pDocSh ), m_eFamily(nFamily)
+{
+ // Register ourselves as a listener to the document (via the page descriptor)
+ StartListening(pDocSh->GetDoc()->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+}
+
+SwXAutoStyleFamily::~SwXAutoStyleFamily()
+{
+}
+
+void SwXAutoStyleFamily::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pDocShell = nullptr;
+}
+
+uno::Reference< style::XAutoStyle > SwXAutoStyleFamily::insertStyle(
+ const uno::Sequence< beans::PropertyValue >& Values )
+{
+ if (!m_pDocShell)
+ {
+ throw uno::RuntimeException();
+ }
+
+ WhichRangesContainer pRange;
+ const SfxItemPropertySet* pPropSet = nullptr;
+ switch( m_eFamily )
+ {
+ case IStyleAccess::AUTO_STYLE_CHAR:
+ {
+ pRange = aCharAutoFormatSetRange;
+ pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE);
+ break;
+ }
+ case IStyleAccess::AUTO_STYLE_RUBY:
+ {
+ pRange = WhichRangesContainer(RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY);
+ pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_RUBY_AUTO_STYLE);
+ break;
+ }
+ case IStyleAccess::AUTO_STYLE_PARA:
+ {
+ pRange = aTextNodeSetRange; // checked, already added support for [XATTR_FILL_FIRST, XATTR_FILL_LAST]
+ pPropSet = aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARA_AUTO_STYLE);
+ break;
+ }
+ default: ;
+ }
+
+ if( !pPropSet)
+ throw uno::RuntimeException();
+
+ SwAttrSet aSet( m_pDocShell->GetDoc()->GetAttrPool(), pRange );
+ const bool bTakeCareOfDrawingLayerFillStyle(IStyleAccess::AUTO_STYLE_PARA == m_eFamily);
+
+ if(!bTakeCareOfDrawingLayerFillStyle)
+ {
+ for( const beans::PropertyValue& rValue : Values )
+ {
+ try
+ {
+ pPropSet->setPropertyValue( rValue.Name, rValue.Value, aSet );
+ }
+ catch (const beans::UnknownPropertyException &)
+ {
+ OSL_FAIL( "Unknown property" );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ OSL_FAIL( "Illegal argument" );
+ }
+ }
+ }
+ else
+ {
+ // set parent to ItemSet to ensure XFILL_NONE as XFillStyleItem
+ // to make cases in RES_BACKGROUND work correct; target *is* a style
+ // where this is the case
+ aSet.SetParent(&m_pDocShell->GetDoc()->GetDfltTextFormatColl()->GetAttrSet());
+
+ // here the used DrawingLayer FillStyles are imported when family is
+ // equal to IStyleAccess::AUTO_STYLE_PARA, thus we will need to serve the
+ // used slots functionality here to do this correctly
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+
+ for( const beans::PropertyValue& rValue : Values )
+ {
+ const OUString& rPropName = rValue.Name;
+ uno::Any aValue(rValue.Value);
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(rPropName);
+
+ if (!pEntry)
+ {
+ SAL_WARN("sw.core", "SwXAutoStyleFamily::insertStyle: Unknown property: " << rPropName);
+ continue;
+ }
+
+ const sal_uInt8 nMemberId(pEntry->nMemberId);
+ bool bDone(false);
+
+ // check for needed metric translation
+ if(pEntry->nMoreFlags & PropertyMoreFlags::METRIC_ITEM)
+ {
+ bool bDoIt(true);
+
+ if(XATTR_FILLBMP_SIZEX == pEntry->nWID || XATTR_FILLBMP_SIZEY == pEntry->nWID)
+ {
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ sal_Int32 nValue = 0;
+
+ if(aValue >>= nValue)
+ {
+ bDoIt = nValue > 0;
+ }
+ }
+
+ if(bDoIt)
+ {
+ const SfxItemPool& rPool = m_pDocShell->GetDoc()->GetAttrPool();
+ const MapUnit eMapUnit(rPool.GetMetric(pEntry->nWID));
+
+ if(eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertFromMM(eMapUnit, aValue);
+ }
+ }
+ }
+
+ switch(pEntry->nWID)
+ {
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ // not yet needed; activate when LineStyle support may be added
+ // case XATTR_LINESTART:
+ // case XATTR_LINEEND:
+ // case XATTR_LINEDASH:
+ {
+ if(MID_NAME == nMemberId)
+ {
+ // add set commands for FillName items
+ OUString aTempName;
+
+ if(!(aValue >>= aTempName))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ SvxShape::SetFillAttribute(pEntry->nWID, aTempName, aSet);
+ bDone = true;
+ }
+ else if (MID_BITMAP == nMemberId)
+ {
+ if(XATTR_FILLBITMAP == pEntry->nWID)
+ {
+ const Graphic aNullGraphic;
+ XFillBitmapItem aXFillBitmapItem(aNullGraphic);
+
+ aXFillBitmapItem.PutValue(aValue, nMemberId);
+ aSet.Put(aXFillBitmapItem);
+ bDone = true;
+ }
+ }
+
+ break;
+ }
+ case RES_BACKGROUND:
+ {
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(aSet, RES_BACKGROUND, true, m_pDocShell->GetDoc()->IsInXMLImport()));
+ std::unique_ptr<SvxBrushItem> aChangedBrushItem(aOriginalBrushItem->Clone());
+
+ aChangedBrushItem->PutValue(aValue, nMemberId);
+
+ if(*aChangedBrushItem != *aOriginalBrushItem)
+ {
+ setSvxBrushItemAsFillAttributesToTargetSet(*aChangedBrushItem, aSet);
+ }
+
+ bDone = true;
+ break;
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ drawing::BitmapMode eMode;
+
+ if(!(aValue >>= eMode))
+ {
+ sal_Int32 nMode = 0;
+
+ if(!(aValue >>= nMode))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ eMode = static_cast<drawing::BitmapMode>(nMode);
+ }
+
+ aSet.Put(XFillBmpStretchItem(drawing::BitmapMode_STRETCH == eMode));
+ aSet.Put(XFillBmpTileItem(drawing::BitmapMode_REPEAT == eMode));
+
+ bDone = true;
+ break;
+ }
+ default: break;
+ }
+
+ if(!bDone)
+ {
+ try
+ {
+ pPropSet->setPropertyValue( rPropName, aValue, aSet );
+ }
+ catch (const beans::UnknownPropertyException &)
+ {
+ OSL_FAIL( "Unknown property" );
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ OSL_FAIL( "Illegal argument" );
+ }
+ }
+ }
+
+ // clear parent again
+ aSet.SetParent(nullptr);
+ }
+
+ // need to ensure uniqueness of evtl. added NameOrIndex items
+ // currently in principle only needed when bTakeCareOfDrawingLayerFillStyle,
+ // but does not hurt and is easily forgotten later eventually, so keep it
+ // as common case
+ m_pDocShell->GetDoc()->CheckForUniqueItemForLineFillNameOrIndex(aSet);
+
+ // AutomaticStyle creation
+ std::shared_ptr<SfxItemSet> pSet = m_pDocShell->GetDoc()->GetIStyleAccess().cacheAutomaticStyle( aSet, m_eFamily );
+ uno::Reference<style::XAutoStyle> xRet = new SwXAutoStyle(m_pDocShell->GetDoc(), pSet, m_eFamily);
+
+ return xRet;
+}
+
+uno::Reference< container::XEnumeration > SwXAutoStyleFamily::createEnumeration( )
+{
+ if( !m_pDocShell )
+ throw uno::RuntimeException();
+ return uno::Reference< container::XEnumeration >
+ (new SwXAutoStylesEnumerator( *m_pDocShell->GetDoc(), m_eFamily ));
+}
+
+uno::Type SwXAutoStyleFamily::getElementType( )
+{
+ return cppu::UnoType<style::XAutoStyle>::get();
+}
+
+sal_Bool SwXAutoStyleFamily::hasElements( )
+{
+ return false;
+}
+
+SwAutoStylesEnumImpl::SwAutoStylesEnumImpl( SwDoc& rInitDoc, IStyleAccess::SwAutoStyleFamily eFam )
+: m_rDoc( rInitDoc ), m_eFamily( eFam )
+{
+ // special case for ruby auto styles:
+ if ( IStyleAccess::AUTO_STYLE_RUBY == eFam )
+ {
+ std::set< std::pair< sal_uInt16, text::RubyAdjust > > aRubyMap;
+ SwAttrPool& rAttrPool = m_rDoc.GetAttrPool();
+
+ // do this in two phases otherwise we invalidate the iterators when we insert into the pool
+ std::vector<const SwFormatRuby*> vRubyItems;
+ for (const SfxPoolItem* pItem : rAttrPool.GetItemSurrogates(RES_TXTATR_CJK_RUBY))
+ {
+ auto pRubyItem = dynamic_cast<const SwFormatRuby*>(pItem);
+ if ( pRubyItem && pRubyItem->GetTextRuby() )
+ vRubyItems.push_back(pRubyItem);
+ }
+ for (const SwFormatRuby* pRubyItem : vRubyItems)
+ {
+ std::pair< sal_uInt16, text::RubyAdjust > aPair( pRubyItem->GetPosition(), pRubyItem->GetAdjustment() );
+ if ( aRubyMap.insert( aPair ).second )
+ {
+ auto pItemSet = std::make_shared<SfxItemSetFixed<RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY>>( rAttrPool );
+ pItemSet->Put( *pRubyItem );
+ mAutoStyles.push_back( pItemSet );
+ }
+ }
+ }
+ else
+ {
+ m_rDoc.GetIStyleAccess().getAllStyles( mAutoStyles, m_eFamily );
+ }
+
+ m_aIter = mAutoStyles.begin();
+}
+
+SwXAutoStylesEnumerator::SwXAutoStylesEnumerator( SwDoc& rDoc, IStyleAccess::SwAutoStyleFamily eFam )
+: m_pImpl( new SwAutoStylesEnumImpl( rDoc, eFam ) )
+{
+ // Register ourselves as a listener to the document (via the page descriptor)
+ StartListening(rDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+}
+
+SwXAutoStylesEnumerator::~SwXAutoStylesEnumerator()
+{
+}
+
+void SwXAutoStylesEnumerator::Notify( const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pImpl.reset();
+}
+
+sal_Bool SwXAutoStylesEnumerator::hasMoreElements( )
+{
+ if( !m_pImpl )
+ throw uno::RuntimeException();
+ return m_pImpl->hasMoreElements();
+}
+
+uno::Any SwXAutoStylesEnumerator::nextElement( )
+{
+ if( !m_pImpl )
+ throw uno::RuntimeException();
+ uno::Any aRet;
+ if( m_pImpl->hasMoreElements() )
+ {
+ std::shared_ptr<SfxItemSet> pNextSet = m_pImpl->nextElement();
+ uno::Reference< style::XAutoStyle > xAutoStyle = new SwXAutoStyle(&m_pImpl->getDoc(),
+ pNextSet, m_pImpl->getFamily());
+ aRet <<= xAutoStyle;
+ }
+ return aRet;
+}
+
+// SwXAutoStyle with the family IStyleAccess::AUTO_STYLE_PARA (or
+// PROPERTY_MAP_PARA_AUTO_STYLE) now uses DrawingLayer FillStyles to allow
+// unified paragraph background fill, thus the UNO API implementation has to
+// support the needed slots for these. This seems to be used only for reading
+// (no setPropertyValue implementation here), so maybe specialized for saving
+// the Writer Doc to ODF
+
+SwXAutoStyle::SwXAutoStyle(
+ SwDoc* pDoc,
+ std::shared_ptr<SfxItemSet> const & pInitSet,
+ IStyleAccess::SwAutoStyleFamily eFam)
+: mpSet(pInitSet),
+ meFamily(eFam),
+ mrDoc(*pDoc)
+{
+ // Register ourselves as a listener to the document (via the page descriptor)
+ //StartListening(mrDoc.getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
+}
+
+SwXAutoStyle::~SwXAutoStyle()
+{
+}
+
+void SwXAutoStyle::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ mpSet.reset();
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXAutoStyle::getPropertySetInfo( )
+{
+ uno::Reference< beans::XPropertySetInfo > xRet;
+ switch( meFamily )
+ {
+ case IStyleAccess::AUTO_STYLE_CHAR:
+ {
+ static uno::Reference< beans::XPropertySetInfo > xCharRef;
+ if(!xCharRef.is())
+ {
+ xCharRef = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CHAR_AUTO_STYLE)->getPropertySetInfo();
+ }
+ xRet = xCharRef;
+ }
+ break;
+ case IStyleAccess::AUTO_STYLE_RUBY:
+ {
+ static uno::Reference< beans::XPropertySetInfo > xRubyRef;
+ if(!xRubyRef.is())
+ {
+ const sal_uInt16 nMapId = PROPERTY_MAP_RUBY_AUTO_STYLE;
+ xRubyRef = aSwMapProvider.GetPropertySet(nMapId)->getPropertySetInfo();
+ }
+ xRet = xRubyRef;
+ }
+ break;
+ case IStyleAccess::AUTO_STYLE_PARA:
+ {
+ static uno::Reference< beans::XPropertySetInfo > xParaRef;
+ if(!xParaRef.is())
+ {
+ const sal_uInt16 nMapId = PROPERTY_MAP_PARA_AUTO_STYLE;
+ xParaRef = aSwMapProvider.GetPropertySet(nMapId)->getPropertySetInfo();
+ }
+ xRet = xParaRef;
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ return xRet;
+}
+
+void SwXAutoStyle::setPropertyValue( const OUString& /*rPropertyName*/, const uno::Any& /*rValue*/ )
+{
+}
+
+uno::Any SwXAutoStyle::getPropertyValue( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+ const uno::Sequence<OUString> aProperties(&rPropertyName, 1);
+ return GetPropertyValues_Impl(aProperties).getConstArray()[0];
+}
+
+void SwXAutoStyle::addPropertyChangeListener( const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXAutoStyle::removePropertyChangeListener( const OUString& /*aPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*aListener*/ )
+{
+}
+
+void SwXAutoStyle::addVetoableChangeListener( const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+void SwXAutoStyle::removeVetoableChangeListener( const OUString& /*PropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+void SwXAutoStyle::setPropertyValues(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Sequence< uno::Any >& /*aValues*/ )
+{
+}
+
+uno::Sequence< uno::Any > SwXAutoStyle::GetPropertyValues_Impl(
+ const uno::Sequence< OUString > & rPropertyNames )
+{
+ if( !mpSet )
+ {
+ throw uno::RuntimeException();
+ }
+
+ // query_item
+ sal_Int8 nPropSetId = PROPERTY_MAP_CHAR_AUTO_STYLE;
+ switch(meFamily)
+ {
+ case IStyleAccess::AUTO_STYLE_CHAR : nPropSetId = PROPERTY_MAP_CHAR_AUTO_STYLE; break;
+ case IStyleAccess::AUTO_STYLE_RUBY : nPropSetId = PROPERTY_MAP_RUBY_AUTO_STYLE; break;
+ case IStyleAccess::AUTO_STYLE_PARA : nPropSetId = PROPERTY_MAP_PARA_AUTO_STYLE; break;
+ default: ;
+ }
+
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ const sal_Int32 nLen(rPropertyNames.getLength());
+ uno::Sequence< uno::Any > aRet( nLen );
+ uno::Any* pValues = aRet.getArray();
+ const bool bTakeCareOfDrawingLayerFillStyle(IStyleAccess::AUTO_STYLE_PARA == meFamily);
+
+ for( sal_Int32 i = 0; i < nLen; ++i )
+ {
+ const OUString sPropName = pNames[i];
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(sPropName);
+ if(!pEntry)
+ {
+ throw beans::UnknownPropertyException("Unknown property: " + sPropName, static_cast < cppu::OWeakObject * > ( this ) );
+ }
+
+ uno::Any aTarget;
+ bool bDone(false);
+
+ if ( RES_TXTATR_AUTOFMT == pEntry->nWID || RES_AUTO_STYLE == pEntry->nWID )
+ {
+ OUString sName(StylePool::nameOf( mpSet ));
+ aTarget <<= sName;
+ bDone = true;
+ }
+ else if(bTakeCareOfDrawingLayerFillStyle)
+ {
+ // add support for DrawingLayer FillStyle slots
+ switch(pEntry->nWID)
+ {
+ case RES_BACKGROUND:
+ {
+ const std::unique_ptr<SvxBrushItem> aOriginalBrushItem(getSvxBrushItemFromSourceSet(*mpSet, RES_BACKGROUND));
+
+ if(!aOriginalBrushItem->QueryValue(aTarget, pEntry->nMemberId))
+ {
+ OSL_ENSURE(false, "Error getting attribute from RES_BACKGROUND (!)");
+ }
+
+ bDone = true;
+ break;
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ if (mpSet->Get(XATTR_FILLBMP_TILE).GetValue())
+ {
+ aTarget <<= drawing::BitmapMode_REPEAT;
+ }
+ else if (mpSet->Get(XATTR_FILLBMP_STRETCH).GetValue())
+ {
+ aTarget <<= drawing::BitmapMode_STRETCH;
+ }
+ else
+ {
+ aTarget <<= drawing::BitmapMode_NO_REPEAT;
+ }
+
+ bDone = true;
+ break;
+ }
+ }
+ }
+
+ if(!bDone)
+ {
+ pPropSet->getPropertyValue( *pEntry, *mpSet, aTarget );
+ }
+
+ if(bTakeCareOfDrawingLayerFillStyle)
+ {
+ if(pEntry->aType == cppu::UnoType<sal_Int16>::get() && pEntry->aType != aTarget.getValueType())
+ {
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ sal_Int32 nValue = 0;
+ if (aTarget >>= nValue)
+ {
+ aTarget <<= static_cast<sal_Int16>(nValue);
+ }
+ }
+
+ // check for needed metric translation
+ if(pEntry->nMoreFlags & PropertyMoreFlags::METRIC_ITEM)
+ {
+ bool bDoIt(true);
+
+ if(XATTR_FILLBMP_SIZEX == pEntry->nWID || XATTR_FILLBMP_SIZEY == pEntry->nWID)
+ {
+ // exception: If these ItemTypes are used, do not convert when these are negative
+ // since this means they are intended as percent values
+ sal_Int32 nValue = 0;
+
+ if(aTarget >>= nValue)
+ {
+ bDoIt = nValue > 0;
+ }
+ }
+
+ if(bDoIt)
+ {
+ const SfxItemPool& rPool = mrDoc.GetAttrPool();
+ const MapUnit eMapUnit(rPool.GetMetric(pEntry->nWID));
+
+ if(eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertToMM(eMapUnit, aTarget);
+ }
+ }
+ }
+ }
+
+ // add value
+ pValues[i] = aTarget;
+ }
+
+ return aRet;
+}
+
+uno::Sequence< uno::Any > SwXAutoStyle::getPropertyValues (
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence< uno::Any > aValues;
+
+ // workaround for bad designed API
+ try
+ {
+ aValues = GetPropertyValues_Impl( rPropertyNames );
+ }
+ catch (beans::UnknownPropertyException &)
+ {
+ css::uno::Any exc = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("Unknown property exception caught", static_cast < cppu::OWeakObject * > ( this ), exc );
+ }
+ catch (lang::WrappedTargetException &)
+ {
+ css::uno::Any exc = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException("WrappedTargetException caught", static_cast < cppu::OWeakObject * > ( this ), exc );
+ }
+
+ return aValues;
+}
+
+void SwXAutoStyle::addPropertiesChangeListener(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXAutoStyle::removePropertiesChangeListener(
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+void SwXAutoStyle::firePropertiesChangeEvent(
+ const uno::Sequence< OUString >& /*aPropertyNames*/,
+ const uno::Reference< beans::XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+beans::PropertyState SwXAutoStyle::getPropertyState( const OUString& rPropertyName )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Sequence< OUString > aNames { rPropertyName };
+ uno::Sequence< beans::PropertyState > aStates = getPropertyStates(aNames);
+ return aStates.getConstArray()[0];
+}
+
+void SwXAutoStyle::setPropertyToDefault( const OUString& /*PropertyName*/ )
+{
+}
+
+uno::Any SwXAutoStyle::getPropertyDefault( const OUString& rPropertyName )
+{
+ const uno::Sequence < OUString > aSequence ( &rPropertyName, 1 );
+ return getPropertyDefaults ( aSequence ).getConstArray()[0];
+}
+
+uno::Sequence< beans::PropertyState > SwXAutoStyle::getPropertyStates(
+ const uno::Sequence< OUString >& rPropertyNames )
+{
+ if (!mpSet)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SolarMutexGuard aGuard;
+ uno::Sequence< beans::PropertyState > aRet(rPropertyNames.getLength());
+ beans::PropertyState* pStates = aRet.getArray();
+ const OUString* pNames = rPropertyNames.getConstArray();
+
+ sal_Int8 nPropSetId = PROPERTY_MAP_CHAR_AUTO_STYLE;
+ switch(meFamily)
+ {
+ case IStyleAccess::AUTO_STYLE_CHAR : nPropSetId = PROPERTY_MAP_CHAR_AUTO_STYLE; break;
+ case IStyleAccess::AUTO_STYLE_RUBY : nPropSetId = PROPERTY_MAP_RUBY_AUTO_STYLE; break;
+ case IStyleAccess::AUTO_STYLE_PARA : nPropSetId = PROPERTY_MAP_PARA_AUTO_STYLE; break;
+ default: ;
+ }
+
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap& rMap = pPropSet->getPropertyMap();
+ const bool bTakeCareOfDrawingLayerFillStyle(IStyleAccess::AUTO_STYLE_PARA == meFamily);
+
+ for(sal_Int32 i = 0; i < rPropertyNames.getLength(); i++)
+ {
+ const OUString sPropName = pNames[i];
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(sPropName);
+ if(!pEntry)
+ {
+ throw beans::UnknownPropertyException("Unknown property: " + sPropName, static_cast < cppu::OWeakObject * > ( this ) );
+ }
+
+ bool bDone(false);
+
+ if(bTakeCareOfDrawingLayerFillStyle)
+ {
+ // DrawingLayer PropertyStyle support
+ switch(pEntry->nWID)
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ if(SfxItemState::SET == mpSet->GetItemState(XATTR_FILLBMP_STRETCH, false)
+ || SfxItemState::SET == mpSet->GetItemState(XATTR_FILLBMP_TILE, false))
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+
+ bDone = true;
+ break;
+ }
+ case RES_BACKGROUND:
+ {
+ if (SWUnoHelper::needToMapFillItemsToSvxBrushItemTypes(*mpSet,
+ pEntry->nMemberId))
+ {
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ pStates[i] = beans::PropertyState_DEFAULT_VALUE;
+ }
+ bDone = true;
+
+ break;
+ }
+ }
+ }
+
+ if(!bDone)
+ {
+ pStates[i] = pPropSet->getPropertyState(*pEntry, *mpSet );
+ }
+ }
+
+ return aRet;
+}
+
+void SwXAutoStyle::setAllPropertiesToDefault( )
+{
+}
+
+void SwXAutoStyle::setPropertiesToDefault(
+ const uno::Sequence< OUString >& /*rPropertyNames*/ )
+{
+}
+
+uno::Sequence< uno::Any > SwXAutoStyle::getPropertyDefaults(
+ const uno::Sequence< OUString >& /*aPropertyNames*/ )
+{
+ return { };
+}
+
+uno::Sequence< beans::PropertyValue > SwXAutoStyle::getProperties()
+{
+ if( !mpSet )
+ throw uno::RuntimeException();
+ SolarMutexGuard aGuard;
+ std::vector< beans::PropertyValue > aPropertyVector;
+
+ sal_Int8 nPropSetId = 0;
+ switch(meFamily)
+ {
+ case IStyleAccess::AUTO_STYLE_CHAR : nPropSetId = PROPERTY_MAP_CHAR_AUTO_STYLE; break;
+ case IStyleAccess::AUTO_STYLE_RUBY : nPropSetId = PROPERTY_MAP_RUBY_AUTO_STYLE; break;
+ case IStyleAccess::AUTO_STYLE_PARA : nPropSetId = PROPERTY_MAP_PARA_AUTO_STYLE; break;
+ default: ;
+ }
+
+ const SfxItemPropertySet* pPropSet = aSwMapProvider.GetPropertySet(nPropSetId);
+ const SfxItemPropertyMap &rMap = pPropSet->getPropertyMap();
+
+ SfxItemSet& rSet = *mpSet;
+ SfxItemIter aIter(rSet);
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ const sal_uInt16 nWID = pItem->Which();
+
+ // TODO: Optimize - and fix! the old iteration filled each WhichId
+ // only once but there are more properties than WhichIds
+ for( const auto pEntry : rMap.getPropertyEntries() )
+ {
+ if ( pEntry->nWID == nWID )
+ {
+ beans::PropertyValue aPropertyValue;
+ aPropertyValue.Name = pEntry->aName;
+ pItem->QueryValue( aPropertyValue.Value, pEntry->nMemberId );
+ aPropertyVector.push_back( aPropertyValue );
+ }
+ }
+ }
+
+ const sal_Int32 nCount = aPropertyVector.size();
+ uno::Sequence< beans::PropertyValue > aRet( nCount );
+ beans::PropertyValue* pProps = aRet.getArray();
+
+ for ( int i = 0; i < nCount; ++i, pProps++ )
+ {
+ *pProps = aPropertyVector[i];
+ }
+
+ return aRet;
+}
+
+SwXTextTableStyle::SwXTextTableStyle(SwDocShell* pDocShell, SwTableAutoFormat* pTableAutoFormat) :
+ m_pDocShell(pDocShell), m_pTableAutoFormat(pTableAutoFormat), m_bPhysical(true)
+{
+ UpdateCellStylesMapping();
+}
+
+SwXTextTableStyle::SwXTextTableStyle(SwDocShell* pDocShell, const OUString& rTableAutoFormatName) :
+ m_pDocShell(pDocShell), m_pTableAutoFormat_Impl(new SwTableAutoFormat(rTableAutoFormatName)), m_bPhysical(false)
+{
+ m_pTableAutoFormat = m_pTableAutoFormat_Impl.get();
+ UpdateCellStylesMapping();
+}
+
+uno::Reference<style::XStyle> SwXTextTableStyle::CreateXTextTableStyle(SwDocShell* pDocShell, const OUString& rTableAutoFormatName)
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<style::XStyle> xTextTableStyle;
+ SwTableAutoFormat* pAutoFormat = GetTableAutoFormat(pDocShell, rTableAutoFormatName);
+ if (pAutoFormat && pAutoFormat->GetName() == rTableAutoFormatName)
+ {
+ xTextTableStyle.set(pAutoFormat->GetXObject(), uno::UNO_QUERY);
+ if (!xTextTableStyle.is())
+ {
+ xTextTableStyle.set(new SwXTextTableStyle(pDocShell, pAutoFormat));
+ pAutoFormat->SetXObject(xTextTableStyle);
+ }
+ }
+
+ // If corresponding AutoFormat doesn't exist create a non physical style.
+ if (!xTextTableStyle.is())
+ {
+ xTextTableStyle.set(new SwXTextTableStyle(pDocShell, rTableAutoFormatName));
+ SAL_INFO("sw.uno", "creating SwXTextTableStyle for non existing SwTableAutoFormat");
+ }
+
+ return xTextTableStyle;
+}
+
+void SwXTextTableStyle::UpdateCellStylesMapping()
+{
+ const std::vector<sal_Int32> aTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap();
+ assert(aTableTemplateMap.size() == STYLE_COUNT && "can not map SwTableAutoFormat to a SwXTextTableStyle");
+ for (sal_Int32 i=0; i<STYLE_COUNT; ++i)
+ {
+ SwBoxAutoFormat* pBoxFormat = &m_pTableAutoFormat->GetBoxFormat(aTableTemplateMap[i]);
+ uno::Reference<style::XStyle> xCellStyle(pBoxFormat->GetXObject(), uno::UNO_QUERY);
+ if (!xCellStyle.is())
+ {
+ xCellStyle.set(new SwXTextCellStyle(m_pDocShell, pBoxFormat, m_pTableAutoFormat->GetName()));
+ pBoxFormat->SetXObject(xCellStyle);
+ }
+ m_aCellStyles[i] = xCellStyle;
+ }
+}
+
+const CellStyleNameMap& SwXTextTableStyle::GetCellStyleNameMap()
+{
+ static CellStyleNameMap const aMap
+ {
+ { "first-row" , FIRST_ROW_STYLE },
+ { "last-row" , LAST_ROW_STYLE },
+ { "first-column" , FIRST_COLUMN_STYLE },
+ { "last-column" , LAST_COLUMN_STYLE },
+ { "body" , BODY_STYLE },
+ { "even-rows" , EVEN_ROWS_STYLE },
+ { "odd-rows" , ODD_ROWS_STYLE },
+ { "even-columns" , EVEN_COLUMNS_STYLE },
+ { "odd-columns" , ODD_COLUMNS_STYLE },
+ { "background" , BACKGROUND_STYLE },
+ // loext namespace
+ { "first-row-start-column" , FIRST_ROW_START_COLUMN_STYLE },
+ { "first-row-end-column" , FIRST_ROW_END_COLUMN_STYLE },
+ { "last-row-start-column" , LAST_ROW_START_COLUMN_STYLE },
+ { "last-row-end-column" , LAST_ROW_END_COLUMN_STYLE },
+ { "first-row-even-column" , FIRST_ROW_EVEN_COLUMN_STYLE },
+ { "last-row-even-column" , LAST_ROW_EVEN_COLUMN_STYLE },
+ };
+ return aMap;
+}
+
+SwTableAutoFormat* SwXTextTableStyle::GetTableFormat()
+{
+ return m_pTableAutoFormat;
+}
+
+SwTableAutoFormat* SwXTextTableStyle::GetTableAutoFormat(SwDocShell* pDocShell, std::u16string_view sName)
+{
+ const size_t nStyles = pDocShell->GetDoc()->GetTableStyles().size();
+ for(size_t i=0; i < nStyles; ++i)
+ {
+ SwTableAutoFormat* pAutoFormat = &pDocShell->GetDoc()->GetTableStyles()[i];
+ if (pAutoFormat->GetName() == sName)
+ {
+ return pAutoFormat;
+ }
+ }
+ // not found
+ return nullptr;
+}
+
+void SwXTextTableStyle::SetPhysical()
+{
+ if (!m_bPhysical)
+ {
+ // find table format in doc
+ SwTableAutoFormat* pTableAutoFormat = GetTableAutoFormat(m_pDocShell, m_pTableAutoFormat->GetName());
+ if (pTableAutoFormat)
+ {
+ m_bPhysical = true;
+ /// take care of children, make SwXTextCellStyles use new core SwBoxAutoFormats
+ const std::vector<sal_Int32> aTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap();
+ for (size_t i=0; i<aTableTemplateMap.size(); ++i)
+ {
+ SwBoxAutoFormat* pOldBoxFormat = &m_pTableAutoFormat->GetBoxFormat(aTableTemplateMap[i]);
+ uno::Reference<style::XStyle> xCellStyle(pOldBoxFormat->GetXObject(), uno::UNO_QUERY);
+ if (!xCellStyle.is())
+ continue;
+ SwXTextCellStyle& rStyle = dynamic_cast<SwXTextCellStyle&>(*xCellStyle);
+ SwBoxAutoFormat& rNewBoxFormat = pTableAutoFormat->GetBoxFormat(aTableTemplateMap[i]);
+ rStyle.SetBoxFormat(&rNewBoxFormat);
+ rNewBoxFormat.SetXObject(xCellStyle);
+ }
+ m_pTableAutoFormat_Impl = nullptr;
+ m_pTableAutoFormat = pTableAutoFormat;
+ m_pTableAutoFormat->SetXObject(uno::Reference<style::XStyle>(this));
+ }
+ else
+ SAL_WARN("sw.uno", "setting style physical, but SwTableAutoFormat in document not found");
+ }
+ else
+ SAL_WARN("sw.uno", "calling SetPhysical on a physical SwXTextTableStyle");
+}
+
+// XStyle
+sal_Bool SAL_CALL SwXTextTableStyle::isUserDefined()
+{
+ SolarMutexGuard aGuard;
+ // only first style is not user defined
+ if (m_pDocShell->GetDoc()->GetTableStyles()[0].GetName() == m_pTableAutoFormat->GetName())
+ return false;
+
+ return true;
+}
+
+sal_Bool SAL_CALL SwXTextTableStyle::isInUse()
+{
+ SolarMutexGuard aGuard;
+ if (!m_bPhysical)
+ return false;
+
+ SwAutoFormatGetDocNode aGetHt( &m_pDocShell->GetDoc()->GetNodes() );
+
+ for (SwFrameFormat* const & pFormat : *m_pDocShell->GetDoc()->GetTableFrameFormats())
+ {
+ if (!pFormat->GetInfo(aGetHt))
+ {
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ if (pTable->GetTableStyleName() == m_pTableAutoFormat->GetName())
+ return true;
+ }
+ }
+
+ return false;
+}
+
+OUString SAL_CALL SwXTextTableStyle::getParentStyle()
+{
+ return OUString();
+}
+
+void SAL_CALL SwXTextTableStyle::setParentStyle(const OUString& /*aParentStyle*/)
+{ }
+
+//XNamed
+OUString SAL_CALL SwXTextTableStyle::getName()
+{
+ SolarMutexGuard aGuard;
+ OUString sProgName;
+ SwStyleNameMapper::FillProgName(m_pTableAutoFormat->GetName(), sProgName, SwGetPoolIdFromName::TabStyle);
+ return sProgName;
+}
+
+void SAL_CALL SwXTextTableStyle::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ m_pTableAutoFormat->SetName(rName);
+}
+
+//XPropertySet
+css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL SwXTextTableStyle::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> xRef(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_STYLE)->getPropertySetInfo());
+ return xRef;
+}
+
+void SAL_CALL SwXTextTableStyle::setPropertyValue(const OUString& /*rPropertyName*/, const css::uno::Any& /*aValue*/)
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+css::uno::Any SAL_CALL SwXTextTableStyle::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ bool bIsRow = false;
+
+ if (rPropertyName == UNO_NAME_TABLE_FIRST_ROW_END_COLUMN)
+ bIsRow = m_pTableAutoFormat->FirstRowEndColumnIsRow();
+ else if (rPropertyName == UNO_NAME_TABLE_FIRST_ROW_START_COLUMN)
+ bIsRow = m_pTableAutoFormat->FirstRowStartColumnIsRow();
+ else if (rPropertyName == UNO_NAME_TABLE_LAST_ROW_END_COLUMN)
+ bIsRow = m_pTableAutoFormat->LastRowEndColumnIsRow();
+ else if (rPropertyName == UNO_NAME_TABLE_LAST_ROW_START_COLUMN)
+ bIsRow = m_pTableAutoFormat->LastRowStartColumnIsRow();
+ else if (rPropertyName == UNO_NAME_DISPLAY_NAME)
+ return uno::Any(m_pTableAutoFormat->GetName());
+ else
+ throw css::beans::UnknownPropertyException(rPropertyName);
+
+ return uno::Any(bIsRow ? OUString("row") : OUString("column"));
+}
+
+void SAL_CALL SwXTextTableStyle::addPropertyChangeListener( const OUString& /*aPropertyName*/, const css::uno::Reference< css::beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextTableStyle::removePropertyChangeListener( const OUString& /*aPropertyName*/, const css::uno::Reference< css::beans::XPropertyChangeListener >& /*aListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextTableStyle::addVetoableChangeListener( const OUString& /*PropertyName*/, const css::uno::Reference< css::beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextTableStyle::removeVetoableChangeListener( const OUString& /*PropertyName*/, const css::uno::Reference< css::beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+//XNameAccess
+uno::Any SAL_CALL SwXTextTableStyle::getByName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ const CellStyleNameMap& rMap = GetCellStyleNameMap();
+ CellStyleNameMap::const_iterator iter = rMap.find(rName);
+ if(iter == rMap.end())
+ throw css::container::NoSuchElementException();
+
+ return css::uno::Any(m_aCellStyles[(*iter).second]);
+}
+
+css::uno::Sequence<OUString> SAL_CALL SwXTextTableStyle::getElementNames()
+{
+ return comphelper::mapKeysToSequence(GetCellStyleNameMap());
+}
+
+sal_Bool SAL_CALL SwXTextTableStyle::hasByName(const OUString& rName)
+{
+ const CellStyleNameMap& rMap = GetCellStyleNameMap();
+ CellStyleNameMap::const_iterator iter = rMap.find(rName);
+ return iter != rMap.end();
+}
+
+//XNameContainer
+void SAL_CALL SwXTextTableStyle::insertByName(const OUString& /*Name*/, const uno::Any& /*Element*/)
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextTableStyle::replaceByName(const OUString& rName, const uno::Any& rElement)
+{
+ SolarMutexGuard aGuard;
+ const CellStyleNameMap& rMap = GetCellStyleNameMap();
+ CellStyleNameMap::const_iterator iter = rMap.find(rName);
+ if(iter == rMap.end())
+ throw container::NoSuchElementException();
+ const sal_Int32 nCellStyle = iter->second;
+
+ uno::Reference<style::XStyle> xStyle = rElement.get<uno::Reference<style::XStyle>>();
+ if (!xStyle.is())
+ throw lang::IllegalArgumentException();
+
+ SwXTextCellStyle* pStyleToReplaceWith = dynamic_cast<SwXTextCellStyle*>(xStyle.get());
+ if (!pStyleToReplaceWith)
+ throw lang::IllegalArgumentException();
+
+ // replace only with physical ...
+ if (!pStyleToReplaceWith->IsPhysical())
+ throw lang::IllegalArgumentException();
+
+ const auto& rTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap();
+ const sal_Int32 nBoxFormat = rTableTemplateMap[nCellStyle];
+
+ // move SwBoxAutoFormat to dest. SwTableAutoFormat
+ m_pTableAutoFormat->SetBoxFormat(*pStyleToReplaceWith->GetBoxFormat(), nBoxFormat);
+ // remove unassigned SwBoxAutoFormat, which is not anymore in use anyways
+ m_pDocShell->GetDoc()->GetCellStyles().RemoveBoxFormat(xStyle->getName());
+ // make SwXTextCellStyle use new, moved SwBoxAutoFormat
+ pStyleToReplaceWith->SetBoxFormat(&m_pTableAutoFormat->GetBoxFormat(nBoxFormat));
+ m_pTableAutoFormat->GetBoxFormat(nBoxFormat).SetXObject(xStyle);
+ // make this SwXTextTableStyle use new SwXTextCellStyle
+ m_aCellStyles[nCellStyle] = xStyle;
+}
+
+void SAL_CALL SwXTextTableStyle::removeByName(const OUString& /*Name*/)
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+//XElementAccess
+uno::Type SAL_CALL SAL_CALL SwXTextTableStyle::getElementType()
+{
+ return cppu::UnoType<style::XStyle>::get();
+}
+
+sal_Bool SAL_CALL SAL_CALL SwXTextTableStyle::hasElements()
+{
+ return true;
+}
+
+//XServiceInfo
+OUString SAL_CALL SwXTextTableStyle::getImplementationName()
+{
+ return {"SwXTextTableStyle"};
+}
+
+sal_Bool SAL_CALL SwXTextTableStyle::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL SwXTextTableStyle::getSupportedServiceNames()
+{
+ return {"com.sun.star.style.Style"};
+}
+
+// SwXTextCellStyle
+SwXTextCellStyle::SwXTextCellStyle(SwDocShell* pDocShell, SwBoxAutoFormat* pBoxAutoFormat, const OUString& sParentStyle) :
+ m_pDocShell(pDocShell),
+ m_pBoxAutoFormat(pBoxAutoFormat),
+ m_sParentStyle(sParentStyle),
+ m_bPhysical(true)
+{ }
+
+SwXTextCellStyle::SwXTextCellStyle(SwDocShell* pDocShell, const OUString& sName) :
+ m_pDocShell(pDocShell),
+ m_pBoxAutoFormat_Impl(std::make_shared<SwBoxAutoFormat>()),
+ m_sName(sName),
+ m_bPhysical(false)
+{
+ m_pBoxAutoFormat = m_pBoxAutoFormat_Impl.get();
+}
+
+SwBoxAutoFormat* SwXTextCellStyle::GetBoxFormat()
+{
+ return m_pBoxAutoFormat;
+}
+
+void SwXTextCellStyle::SetBoxFormat(SwBoxAutoFormat* pBoxFormat)
+{
+ if (m_bPhysical)
+ m_pBoxAutoFormat = pBoxFormat;
+ else
+ SAL_INFO("sw.uno", "trying to call SwXTextCellStyle::SetBoxFormat on non physical style");
+}
+
+void SwXTextCellStyle::SetPhysical()
+{
+ if (!m_bPhysical)
+ {
+ SwBoxAutoFormat* pBoxAutoFormat = GetBoxAutoFormat(m_pDocShell, m_sName, &m_sParentStyle);
+ if (pBoxAutoFormat)
+ {
+ m_bPhysical = true;
+ m_pBoxAutoFormat_Impl = nullptr;
+ m_pBoxAutoFormat = pBoxAutoFormat;
+ m_pBoxAutoFormat->SetXObject(uno::Reference<style::XStyle>(this));
+ }
+ else
+ SAL_WARN("sw.uno", "setting style physical, but SwBoxAutoFormat in document not found");
+ }
+ else
+ SAL_WARN("sw.uno", "calling SetPhysical on a physical SwXTextCellStyle");
+}
+
+bool SwXTextCellStyle::IsPhysical() const
+{
+ return m_bPhysical;
+}
+
+SwBoxAutoFormat* SwXTextCellStyle::GetBoxAutoFormat(SwDocShell* pDocShell, std::u16string_view sName, OUString* pParentName)
+{
+ if (sName.empty())
+ return nullptr;
+
+ SwBoxAutoFormat* pBoxAutoFormat = pDocShell->GetDoc()->GetCellStyles().GetBoxFormat(sName);
+ if (!pBoxAutoFormat)
+ {
+ sal_Int32 nTemplateIndex;
+ OUString sParentName;
+ std::u16string_view sCellSubName;
+
+ size_t nSeparatorIndex = sName.rfind('.');
+ if (nSeparatorIndex == std::u16string_view::npos)
+ return nullptr;
+
+ sParentName = sName.substr(0, nSeparatorIndex);
+ sCellSubName = sName.substr(nSeparatorIndex+1);
+ nTemplateIndex = o3tl::toInt32(sCellSubName)-1; // -1 because cell styles names start from 1, but internally are indexed from 0
+ if (0 > nTemplateIndex)
+ return nullptr;
+
+ const auto& rTableTemplateMap = SwTableAutoFormat::GetTableTemplateMap();
+ if (rTableTemplateMap.size() <= o3tl::make_unsigned(nTemplateIndex))
+ return nullptr;
+
+ SwStyleNameMapper::FillUIName(sParentName, sParentName, SwGetPoolIdFromName::TabStyle);
+ SwTableAutoFormat* pTableAutoFormat = pDocShell->GetDoc()->GetTableStyles().FindAutoFormat(sParentName);
+ if (!pTableAutoFormat)
+ return nullptr;
+
+ if (pParentName)
+ *pParentName = sParentName;
+ sal_uInt32 nBoxIndex = rTableTemplateMap[nTemplateIndex];
+ pBoxAutoFormat = &pTableAutoFormat->GetBoxFormat(nBoxIndex);
+ }
+
+ return pBoxAutoFormat;
+}
+
+css::uno::Reference<css::style::XStyle> SwXTextCellStyle::CreateXTextCellStyle(SwDocShell* pDocShell, const OUString& sName)
+{
+ uno::Reference<style::XStyle> xTextCellStyle;
+
+ if (!sName.isEmpty()) // create a cell style for a physical box
+ {
+ OUString sParentName;
+ SwBoxAutoFormat* pBoxFormat = GetBoxAutoFormat(pDocShell, sName, &sParentName);
+
+ // something went wrong but we don't want a crash
+ if (!pBoxFormat)
+ {
+ // return a default-dummy style to prevent crash
+ static SwBoxAutoFormat aDefaultBoxFormat;
+ pBoxFormat = &aDefaultBoxFormat;
+ }
+
+ xTextCellStyle.set(pBoxFormat->GetXObject(), uno::UNO_QUERY);
+ if (!xTextCellStyle.is())
+ {
+ xTextCellStyle.set(new SwXTextCellStyle(pDocShell, pBoxFormat, sParentName));
+ pBoxFormat->SetXObject(xTextCellStyle);
+ }
+ }
+ else // create a non physical style
+ xTextCellStyle.set(new SwXTextCellStyle(pDocShell, sName));
+
+ return xTextCellStyle;
+}
+
+// XStyle
+sal_Bool SAL_CALL SwXTextCellStyle::isUserDefined()
+{
+ SolarMutexGuard aGuard;
+ // if this cell belong to first table style then its default style
+ if (&m_pDocShell->GetDoc()->GetTableStyles()[0] == m_pDocShell->GetDoc()->GetTableStyles().FindAutoFormat(m_sParentStyle))
+ return false;
+
+ return true;
+}
+
+sal_Bool SAL_CALL SwXTextCellStyle::isInUse()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<style::XStyleFamiliesSupplier> xFamiliesSupplier(m_pDocShell->GetModel(), uno::UNO_QUERY);
+ if (!xFamiliesSupplier.is())
+ return false;
+
+ uno::Reference<container::XNameAccess> xFamilies = xFamiliesSupplier->getStyleFamilies();
+ if (!xFamilies.is())
+ return false;
+
+ uno::Reference<container::XNameAccess> xTableStyles;
+ xFamilies->getByName("TableStyles") >>= xTableStyles;
+ if (!xTableStyles.is())
+ return false;
+
+ uno::Reference<style::XStyle> xStyle;
+ xTableStyles->getByName(m_sParentStyle) >>= xStyle;
+ if (!xStyle.is())
+ return false;
+
+ return xStyle->isInUse();
+}
+
+OUString SAL_CALL SwXTextCellStyle::getParentStyle()
+{
+ // Do not return name of the parent (which is a table style) because the parent should be a cell style.
+ return OUString();
+}
+
+void SAL_CALL SwXTextCellStyle::setParentStyle(const OUString& /*sParentStyle*/)
+{
+ // Changing parent to one which is unaware of it will lead to a something unexpected. getName() rely on a parent.
+ SAL_INFO("sw.uno", "Changing SwXTextCellStyle parent");
+}
+
+//XNamed
+OUString SAL_CALL SwXTextCellStyle::getName()
+{
+ SolarMutexGuard aGuard;
+ OUString sName;
+
+ // if style is physical then we request a name from doc
+ if (m_bPhysical)
+ {
+ SwTableAutoFormat* pTableFormat = m_pDocShell->GetDoc()->GetTableStyles().FindAutoFormat(m_sParentStyle);
+ if (!pTableFormat)
+ {
+ // if auto format is not found as a child of table formats, look in SwDoc cellstyles
+ sName = m_pDocShell->GetDoc()->GetCellStyles().GetBoxFormatName(*m_pBoxAutoFormat);
+ }
+ else
+ {
+ OUString sParentStyle;
+ SwStyleNameMapper::FillProgName(m_sParentStyle, sParentStyle, SwGetPoolIdFromName::TabStyle);
+ sName = sParentStyle + pTableFormat->GetTableTemplateCellSubName(*m_pBoxAutoFormat);
+ }
+ }
+ else
+ sName = m_sName;
+
+ return sName;
+}
+
+void SAL_CALL SwXTextCellStyle::setName(const OUString& sName)
+{
+ SolarMutexGuard aGuard;
+ // if style is physical then we can not rename it.
+ if (!m_bPhysical)
+ m_sName = sName;
+ // change name if style is unassigned (name is not generated automatically)
+ m_pDocShell->GetDoc()->GetCellStyles().ChangeBoxFormatName(getName(), sName);
+}
+
+//XPropertySet
+css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL SwXTextCellStyle::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> xRef(aSwMapProvider.GetPropertySet(PROPERTY_MAP_CELL_STYLE)->getPropertySetInfo());
+ return xRef;
+}
+
+void SAL_CALL SwXTextCellStyle::setPropertyValue(const OUString& rPropertyName, const css::uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ const SfxItemPropertyMapEntry *const pEntry = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CELL_STYLE)->getPropertyMap().getByName(rPropertyName);
+ if(pEntry)
+ {
+ switch(pEntry->nWID)
+ {
+ case RES_BACKGROUND:
+ {
+ SvxBrushItem rBrush = m_pBoxAutoFormat->GetBackground();
+ rBrush.PutValue(aValue, 0);
+ m_pBoxAutoFormat->SetBackground(rBrush);
+ return;
+ }
+ case RES_BOX:
+ {
+ SvxBoxItem rBox = m_pBoxAutoFormat->GetBox();
+ rBox.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetBox(rBox);
+ return;
+ }
+ case RES_VERT_ORIENT:
+ {
+ SwFormatVertOrient rVertOrient = m_pBoxAutoFormat->GetVerticalAlignment();
+ rVertOrient.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetVerticalAlignment(rVertOrient);
+ return;
+ }
+ case RES_FRAMEDIR:
+ {
+ SvxFrameDirectionItem rDirItem = m_pBoxAutoFormat->GetTextOrientation();
+ rDirItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetTextOrientation(rDirItem);
+ return;
+ }
+ case RES_BOXATR_FORMAT:
+ {
+ sal_uInt32 nKey;
+ if (aValue >>= nKey)
+ {
+ // FIXME: It's not working for old "automatic" currency formats, which are still in use by autotbl.fmt.
+ // Scenario:
+ // 1) Mark all styles present by default in autotbl.fmt as default.
+ // 2) convert all currencies present in autotbl.fmt before calling this code
+ const SvNumberformat* pNumFormat = m_pDocShell->GetDoc()->GetNumberFormatter()->GetEntry(nKey);
+ if (pNumFormat)
+ m_pBoxAutoFormat->SetValueFormat(pNumFormat->GetFormatstring(), pNumFormat->GetLanguage(), GetAppLanguage());
+ }
+ return;
+ }
+ // Paragraph attributes
+ case RES_PARATR_ADJUST:
+ {
+ SvxAdjustItem rAdjustItem = m_pBoxAutoFormat->GetAdjust();
+ rAdjustItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetAdjust(rAdjustItem);
+ return;
+ }
+ case RES_CHRATR_COLOR:
+ {
+ SvxColorItem rColorItem = m_pBoxAutoFormat->GetColor();
+ rColorItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetColor(rColorItem);
+ return;
+ }
+ case RES_CHRATR_SHADOWED:
+ {
+ SvxShadowedItem rShadowedItem = m_pBoxAutoFormat->GetShadowed();
+ bool bValue = false; aValue >>= bValue;
+ rShadowedItem.SetValue(bValue);
+ m_pBoxAutoFormat->SetShadowed(rShadowedItem);
+ return;
+ }
+ case RES_CHRATR_CONTOUR:
+ {
+ SvxContourItem rContourItem = m_pBoxAutoFormat->GetContour();
+ bool bValue = false; aValue >>= bValue;
+ rContourItem.SetValue(bValue);
+ m_pBoxAutoFormat->SetContour(rContourItem);
+ return;
+ }
+ case RES_CHRATR_CROSSEDOUT:
+ {
+ SvxCrossedOutItem rCrossedOutItem = m_pBoxAutoFormat->GetCrossedOut();
+ rCrossedOutItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCrossedOut(rCrossedOutItem);
+ return;
+ }
+ case RES_CHRATR_UNDERLINE:
+ {
+ SvxUnderlineItem rUnderlineItem = m_pBoxAutoFormat->GetUnderline();
+ rUnderlineItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetUnderline(rUnderlineItem);
+ return;
+ }
+ case RES_CHRATR_FONTSIZE:
+ {
+ SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetHeight();
+ rFontHeightItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetHeight(rFontHeightItem);
+ return;
+ }
+ case RES_CHRATR_WEIGHT:
+ {
+ SvxWeightItem rWeightItem = m_pBoxAutoFormat->GetWeight();
+ rWeightItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetWeight(rWeightItem);
+ return;
+ }
+ case RES_CHRATR_POSTURE:
+ {
+ SvxPostureItem rPostureItem = m_pBoxAutoFormat->GetPosture();
+ rPostureItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetPosture(rPostureItem);
+ return;
+ }
+ case RES_CHRATR_FONT:
+ {
+ SvxFontItem rFontItem = m_pBoxAutoFormat->GetFont();
+ rFontItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetFont(rFontItem);
+ return;
+ }
+ case RES_CHRATR_CJK_FONTSIZE:
+ {
+ SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetCJKHeight();
+ rFontHeightItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKHeight(rFontHeightItem);
+ return;
+ }
+ case RES_CHRATR_CJK_WEIGHT:
+ {
+ SvxWeightItem rWeightItem = m_pBoxAutoFormat->GetCJKWeight();
+ rWeightItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKWeight(rWeightItem);
+ return;
+ }
+ case RES_CHRATR_CJK_POSTURE:
+ {
+ SvxPostureItem rPostureItem = m_pBoxAutoFormat->GetCJKPosture();
+ rPostureItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKPosture(rPostureItem);
+ return;
+ }
+ case RES_CHRATR_CJK_FONT:
+ {
+ SvxFontItem rFontItem = m_pBoxAutoFormat->GetCJKFont();
+ rFontItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKFont(rFontItem);
+ return;
+ }
+ case RES_CHRATR_CTL_FONTSIZE:
+ {
+ SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetCTLHeight();
+ rFontHeightItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLHeight(rFontHeightItem);
+ return;
+ }
+ case RES_CHRATR_CTL_WEIGHT:
+ {
+ SvxWeightItem rWeightItem = m_pBoxAutoFormat->GetCTLWeight();
+ rWeightItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLWeight(rWeightItem);
+ return;
+ }
+ case RES_CHRATR_CTL_POSTURE:
+ {
+ SvxPostureItem rPostureItem = m_pBoxAutoFormat->GetCTLPosture();
+ rPostureItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLPosture(rPostureItem);
+ return;
+ }
+ case RES_CHRATR_CTL_FONT:
+ {
+ SvxFontItem rFontItem = m_pBoxAutoFormat->GetCTLFont();
+ rFontItem.PutValue(aValue, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLFont(rFontItem);
+ return;
+ }
+ default:
+ SAL_WARN("sw.uno", "SwXTextCellStyle unknown nWID");
+ throw css::uno::RuntimeException();
+ }
+ }
+
+ throw css::beans::UnknownPropertyException(rPropertyName);
+}
+
+css::uno::Any SAL_CALL SwXTextCellStyle::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ const SfxItemPropertyMapEntry *const pEntry = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CELL_STYLE)->getPropertyMap().getByName(rPropertyName);
+ if(pEntry)
+ {
+ switch(pEntry->nWID)
+ {
+ case RES_BACKGROUND:
+ {
+ const SvxBrushItem& rBrush = m_pBoxAutoFormat->GetBackground();
+ rBrush.QueryValue(aRet);
+ return aRet;
+ }
+ case RES_BOX:
+ {
+ const SvxBoxItem& rBox = m_pBoxAutoFormat->GetBox();
+ rBox.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_VERT_ORIENT:
+ {
+ const SwFormatVertOrient& rVertOrient = m_pBoxAutoFormat->GetVerticalAlignment();
+ rVertOrient.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_FRAMEDIR:
+ {
+ const SvxFrameDirectionItem& rDirItem = m_pBoxAutoFormat->GetTextOrientation();
+ rDirItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_BOXATR_FORMAT:
+ {
+ OUString sFormat;
+ LanguageType eLng, eSys;
+ m_pBoxAutoFormat->GetValueFormat(sFormat, eLng, eSys);
+ if(!sFormat.isEmpty())
+ {
+ SvNumFormatType nType; bool bNew; sal_Int32 nCheckPos;
+ sal_uInt32 nKey = m_pDocShell->GetDoc()->GetNumberFormatter()->GetIndexPuttingAndConverting(sFormat, eLng, eSys, nType, bNew, nCheckPos);
+ aRet <<= nKey;
+ }
+ return aRet;
+ }
+ // Paragraph attributes
+ case RES_PARATR_ADJUST:
+ {
+ const SvxAdjustItem& rAdjustItem = m_pBoxAutoFormat->GetAdjust();
+ rAdjustItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_COLOR:
+ {
+ const SvxColorItem& rColorItem = m_pBoxAutoFormat->GetColor();
+ rColorItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_SHADOWED:
+ {
+ const SvxShadowedItem& rShadowedItem = m_pBoxAutoFormat->GetShadowed();
+ aRet <<= rShadowedItem.GetValue();
+ return aRet;
+ }
+ case RES_CHRATR_CONTOUR:
+ {
+ const SvxContourItem& rContourItem = m_pBoxAutoFormat->GetContour();
+ aRet <<= rContourItem.GetValue();
+ return aRet;
+ }
+ case RES_CHRATR_CROSSEDOUT:
+ {
+ const SvxCrossedOutItem& rCrossedOutItem = m_pBoxAutoFormat->GetCrossedOut();
+ rCrossedOutItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_UNDERLINE:
+ {
+ const SvxUnderlineItem& rUnderlineItem = m_pBoxAutoFormat->GetUnderline();
+ rUnderlineItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_FONTSIZE:
+ {
+ const SvxFontHeightItem& rFontHeightItem = m_pBoxAutoFormat->GetHeight();
+ rFontHeightItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_WEIGHT:
+ {
+ const SvxWeightItem& rWeightItem = m_pBoxAutoFormat->GetWeight();
+ rWeightItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_POSTURE:
+ {
+ const SvxPostureItem& rPostureItem = m_pBoxAutoFormat->GetPosture();
+ rPostureItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_FONT:
+ {
+ const SvxFontItem rFontItem = m_pBoxAutoFormat->GetFont();
+ rFontItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CJK_FONTSIZE:
+ {
+ const SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetCJKHeight();
+ rFontHeightItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CJK_WEIGHT:
+ {
+ const SvxWeightItem& rWeightItem = m_pBoxAutoFormat->GetCJKWeight();
+ rWeightItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CJK_POSTURE:
+ {
+ const SvxPostureItem& rPostureItem = m_pBoxAutoFormat->GetCJKPosture();
+ rPostureItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CJK_FONT:
+ {
+ const SvxFontItem rFontItem = m_pBoxAutoFormat->GetCJKFont();
+ rFontItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CTL_FONTSIZE:
+ {
+ const SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetCTLHeight();
+ rFontHeightItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CTL_WEIGHT:
+ {
+ const SvxWeightItem& rWeightItem = m_pBoxAutoFormat->GetCTLWeight();
+ rWeightItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CTL_POSTURE:
+ {
+ const SvxPostureItem& rPostureItem = m_pBoxAutoFormat->GetCTLPosture();
+ rPostureItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ case RES_CHRATR_CTL_FONT:
+ {
+ const SvxFontItem rFontItem = m_pBoxAutoFormat->GetCTLFont();
+ rFontItem.QueryValue(aRet, pEntry->nMemberId);
+ return aRet;
+ }
+ default:
+ SAL_WARN("sw.uno", "SwXTextCellStyle unknown nWID");
+ throw css::uno::RuntimeException();
+ }
+ }
+
+ throw css::beans::UnknownPropertyException(rPropertyName);
+}
+
+void SAL_CALL SwXTextCellStyle::addPropertyChangeListener( const OUString& /*aPropertyName*/, const css::uno::Reference< css::beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextCellStyle::removePropertyChangeListener( const OUString& /*aPropertyName*/, const css::uno::Reference< css::beans::XPropertyChangeListener >& /*aListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextCellStyle::addVetoableChangeListener( const OUString& /*PropertyName*/, const css::uno::Reference< css::beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+void SAL_CALL SwXTextCellStyle::removeVetoableChangeListener( const OUString& /*PropertyName*/, const css::uno::Reference< css::beans::XVetoableChangeListener >& /*aListener*/ )
+{
+ SAL_WARN("sw.uno", "not implemented");
+}
+
+//XPropertyState
+css::beans::PropertyState SAL_CALL SwXTextCellStyle::getPropertyState(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<OUString> aNames { rPropertyName };
+ uno::Sequence<beans::PropertyState> aStates = getPropertyStates(aNames);
+ return aStates.getConstArray()[0];
+}
+
+css::uno::Sequence<css::beans::PropertyState> SAL_CALL SwXTextCellStyle::getPropertyStates(const css::uno::Sequence<OUString>& aPropertyNames)
+{
+ SolarMutexGuard aGuard;
+ uno::Sequence<beans::PropertyState> aRet(aPropertyNames.getLength());
+ beans::PropertyState* pStates = aRet.getArray();
+ const SwBoxAutoFormat& rDefaultBoxFormat = SwTableAutoFormat::GetDefaultBoxFormat();
+ const SfxItemPropertyMap& rMap = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CELL_STYLE)->getPropertyMap();
+ const OUString* pNames = aPropertyNames.getConstArray();
+ for(sal_Int32 i=0; i < aPropertyNames.getLength(); ++i)
+ {
+ const OUString sPropName = pNames[i];
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(sPropName);
+ if(pEntry)
+ {
+ uno::Any aAny1, aAny2;
+ switch(pEntry->nWID)
+ {
+ case RES_BACKGROUND:
+ m_pBoxAutoFormat->GetBackground().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetBackground().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_BOX:
+ m_pBoxAutoFormat->GetBox().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetBox().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_VERT_ORIENT:
+ m_pBoxAutoFormat->GetVerticalAlignment().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetVerticalAlignment().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_FRAMEDIR:
+ m_pBoxAutoFormat->GetTextOrientation().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetTextOrientation().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_BOXATR_FORMAT:
+ {
+ OUString sFormat;
+ LanguageType eLng, eSys;
+ m_pBoxAutoFormat->GetValueFormat(sFormat, eLng, eSys);
+ pStates[i] = sFormat.isEmpty() ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ }
+ case RES_PARATR_ADJUST:
+ m_pBoxAutoFormat->GetAdjust().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetAdjust().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_COLOR:
+ m_pBoxAutoFormat->GetColor().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetColor().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_SHADOWED:
+ m_pBoxAutoFormat->GetShadowed().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetShadowed().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CONTOUR:
+ m_pBoxAutoFormat->GetContour().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetContour().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CROSSEDOUT:
+ m_pBoxAutoFormat->GetCrossedOut().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCrossedOut().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_UNDERLINE:
+ m_pBoxAutoFormat->GetUnderline().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetUnderline().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_FONTSIZE:
+ m_pBoxAutoFormat->GetHeight().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetHeight().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_WEIGHT:
+ m_pBoxAutoFormat->GetWeight().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetWeight().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_POSTURE:
+ m_pBoxAutoFormat->GetPosture().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetPosture().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_FONT:
+ m_pBoxAutoFormat->GetFont().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetFont().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CJK_FONTSIZE:
+ m_pBoxAutoFormat->GetCJKHeight().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCJKHeight().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CJK_WEIGHT:
+ m_pBoxAutoFormat->GetCJKWeight().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCJKWeight().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CJK_POSTURE:
+ m_pBoxAutoFormat->GetCJKPosture().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCJKPosture().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CJK_FONT:
+ m_pBoxAutoFormat->GetCJKFont().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCJKFont().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CTL_FONTSIZE:
+ m_pBoxAutoFormat->GetCTLHeight().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCTLHeight().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CTL_WEIGHT:
+ m_pBoxAutoFormat->GetCTLWeight().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCTLWeight().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CTL_POSTURE:
+ m_pBoxAutoFormat->GetCTLPosture().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCTLPosture().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ case RES_CHRATR_CTL_FONT:
+ m_pBoxAutoFormat->GetCTLFont().QueryValue(aAny1, pEntry->nMemberId);
+ rDefaultBoxFormat.GetCTLFont().QueryValue(aAny2, pEntry->nMemberId);
+ pStates[i] = aAny1 == aAny2 ? beans::PropertyState_DEFAULT_VALUE : beans::PropertyState_DIRECT_VALUE;
+ break;
+ default:
+ // fallthrough to DIRECT_VALUE, to export properties for which getPropertyStates is not implemented
+ pStates[i] = beans::PropertyState_DIRECT_VALUE;
+ SAL_WARN("sw.uno", "SwXTextCellStyle getPropertyStates unknown nWID");
+ }
+ }
+ else
+ {
+ SAL_WARN("sw.uno", "SwXTextCellStyle unknown property:" + sPropName);
+ throw css::beans::UnknownPropertyException(sPropName);
+ }
+ }
+ return aRet;
+}
+
+void SAL_CALL SwXTextCellStyle::setPropertyToDefault(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ const SwBoxAutoFormat& rDefaultBoxFormat = SwTableAutoFormat::GetDefaultBoxFormat();
+ const SfxItemPropertyMap& rMap = aSwMapProvider.GetPropertySet(PROPERTY_MAP_CELL_STYLE)->getPropertyMap();
+ const SfxItemPropertyMapEntry* pEntry = rMap.getByName(rPropertyName);
+ if(!pEntry)
+ return;
+
+ uno::Any aAny;
+ switch(pEntry->nWID)
+ {
+ case RES_BACKGROUND:
+ {
+ SvxBrushItem rBrush = m_pBoxAutoFormat->GetBackground();
+ rDefaultBoxFormat.GetBackground().QueryValue(aAny, pEntry->nMemberId);
+ rBrush.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetBackground(rBrush);
+ break;
+ }
+ case RES_BOX:
+ {
+ SvxBoxItem rBox = m_pBoxAutoFormat->GetBox();
+ rDefaultBoxFormat.GetBox().QueryValue(aAny, pEntry->nMemberId);
+ rBox.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetBox(rBox);
+ break;
+ }
+ case RES_VERT_ORIENT:
+ {
+ SwFormatVertOrient rVertOrient = m_pBoxAutoFormat->GetVerticalAlignment();
+ rDefaultBoxFormat.GetVerticalAlignment().QueryValue(aAny, pEntry->nMemberId);
+ rVertOrient.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetVerticalAlignment(rVertOrient);
+ break;
+ }
+ case RES_FRAMEDIR:
+ {
+ SvxFrameDirectionItem rFrameDirectionItem = m_pBoxAutoFormat->GetTextOrientation();
+ rDefaultBoxFormat.GetTextOrientation().QueryValue(aAny, pEntry->nMemberId);
+ rFrameDirectionItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetTextOrientation(rFrameDirectionItem);
+ break;
+ }
+ case RES_BOXATR_FORMAT:
+ {
+ OUString sFormat;
+ LanguageType eLng, eSys;
+ rDefaultBoxFormat.GetValueFormat(sFormat, eLng, eSys);
+ m_pBoxAutoFormat->SetValueFormat(sFormat, eLng, eSys);
+ break;
+ }
+ case RES_PARATR_ADJUST:
+ {
+ SvxAdjustItem rAdjustItem = m_pBoxAutoFormat->GetAdjust();
+ rDefaultBoxFormat.GetAdjust().QueryValue(aAny, pEntry->nMemberId);
+ rAdjustItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetAdjust(rAdjustItem);
+ break;
+ }
+ case RES_CHRATR_COLOR:
+ {
+ SvxColorItem rColorItem = m_pBoxAutoFormat->GetColor();
+ rDefaultBoxFormat.GetColor().QueryValue(aAny, pEntry->nMemberId);
+ rColorItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetColor(rColorItem);
+ break;
+ }
+ case RES_CHRATR_SHADOWED:
+ {
+ SvxShadowedItem rShadowedItem = m_pBoxAutoFormat->GetShadowed();
+ rDefaultBoxFormat.GetShadowed().QueryValue(aAny, pEntry->nMemberId);
+ rShadowedItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetShadowed(rShadowedItem);
+ break;
+ }
+ case RES_CHRATR_CONTOUR:
+ {
+ SvxContourItem rContourItem = m_pBoxAutoFormat->GetContour();
+ rDefaultBoxFormat.GetContour().QueryValue(aAny, pEntry->nMemberId);
+ rContourItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetContour(rContourItem);
+ break;
+ }
+ case RES_CHRATR_CROSSEDOUT:
+ {
+ SvxCrossedOutItem rCrossedOutItem = m_pBoxAutoFormat->GetCrossedOut();
+ rDefaultBoxFormat.GetCrossedOut().QueryValue(aAny, pEntry->nMemberId);
+ rCrossedOutItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCrossedOut(rCrossedOutItem);
+ break;
+ }
+ case RES_CHRATR_UNDERLINE:
+ {
+ SvxUnderlineItem rUnderlineItem = m_pBoxAutoFormat->GetUnderline();
+ rDefaultBoxFormat.GetUnderline().QueryValue(aAny, pEntry->nMemberId);
+ rUnderlineItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetUnderline(rUnderlineItem);
+ break;
+ }
+ case RES_CHRATR_FONTSIZE:
+ {
+ SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetHeight();
+ rDefaultBoxFormat.GetHeight().QueryValue(aAny, pEntry->nMemberId);
+ rFontHeightItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetHeight(rFontHeightItem);
+ break;
+ }
+ case RES_CHRATR_WEIGHT:
+ {
+ SvxWeightItem rWeightItem = m_pBoxAutoFormat->GetWeight();
+ rDefaultBoxFormat.GetWeight().QueryValue(aAny, pEntry->nMemberId);
+ rWeightItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetWeight(rWeightItem);
+ break;
+ }
+ case RES_CHRATR_POSTURE:
+ {
+ SvxPostureItem rPostureItem = m_pBoxAutoFormat->GetPosture();
+ rDefaultBoxFormat.GetPosture().QueryValue(aAny, pEntry->nMemberId);
+ rPostureItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetPosture(rPostureItem);
+ break;
+ }
+ case RES_CHRATR_FONT:
+ {
+ SvxFontItem rFontItem = m_pBoxAutoFormat->GetFont();
+ rDefaultBoxFormat.GetFont().QueryValue(aAny, pEntry->nMemberId);
+ rFontItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetFont(rFontItem);
+ break;
+ }
+ case RES_CHRATR_CJK_FONTSIZE:
+ {
+ SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetCJKHeight();
+ rDefaultBoxFormat.GetCJKHeight().QueryValue(aAny, pEntry->nMemberId);
+ rFontHeightItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKHeight(rFontHeightItem);
+ break;
+ }
+ case RES_CHRATR_CJK_WEIGHT:
+ {
+ SvxWeightItem rWeightItem = m_pBoxAutoFormat->GetCJKWeight();
+ rDefaultBoxFormat.GetCJKWeight().QueryValue(aAny, pEntry->nMemberId);
+ rWeightItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKWeight(rWeightItem);
+ break;
+ }
+ case RES_CHRATR_CJK_POSTURE:
+ {
+ SvxPostureItem rPostureItem = m_pBoxAutoFormat->GetCJKPosture();
+ rDefaultBoxFormat.GetCJKPosture().QueryValue(aAny, pEntry->nMemberId);
+ rPostureItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKPosture(rPostureItem);
+ break;
+ }
+ case RES_CHRATR_CJK_FONT:
+ {
+ SvxFontItem rFontItem = m_pBoxAutoFormat->GetCJKFont();
+ rDefaultBoxFormat.GetCJKFont().QueryValue(aAny, pEntry->nMemberId);
+ rFontItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCJKFont(rFontItem);
+ break;
+ }
+ case RES_CHRATR_CTL_FONTSIZE:
+ {
+ SvxFontHeightItem rFontHeightItem = m_pBoxAutoFormat->GetCTLHeight();
+ rDefaultBoxFormat.GetCTLHeight().QueryValue(aAny, pEntry->nMemberId);
+ rFontHeightItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLHeight(rFontHeightItem);
+ break;
+ }
+ case RES_CHRATR_CTL_WEIGHT:
+ {
+ SvxWeightItem rWeightItem = m_pBoxAutoFormat->GetCTLWeight();
+ rDefaultBoxFormat.GetCTLWeight().QueryValue(aAny, pEntry->nMemberId);
+ rWeightItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLWeight(rWeightItem);
+ break;
+ }
+ case RES_CHRATR_CTL_POSTURE:
+ {
+ SvxPostureItem rPostureItem = m_pBoxAutoFormat->GetCTLPosture();
+ rDefaultBoxFormat.GetCTLPosture().QueryValue(aAny, pEntry->nMemberId);
+ rPostureItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLPosture(rPostureItem);
+ break;
+ }
+ case RES_CHRATR_CTL_FONT:
+ {
+ SvxFontItem rFontItem = m_pBoxAutoFormat->GetCTLFont();
+ rDefaultBoxFormat.GetCTLFont().QueryValue(aAny, pEntry->nMemberId);
+ rFontItem.PutValue(aAny, pEntry->nMemberId);
+ m_pBoxAutoFormat->SetCTLFont(rFontItem);
+ break;
+ }
+ default:
+ SAL_WARN("sw.uno", "SwXTextCellStyle setPropertyToDefault unknown nWID");
+ }
+}
+
+css::uno::Any SAL_CALL SwXTextCellStyle::getPropertyDefault(const OUString& /*aPropertyName*/)
+{
+ SAL_WARN("sw.uno", "not implemented");
+ uno::Any aRet;
+ return aRet;
+}
+
+//XServiceInfo
+OUString SAL_CALL SwXTextCellStyle::getImplementationName()
+{
+ return {"SwXTextCellStyle"};
+}
+
+sal_Bool SAL_CALL SwXTextCellStyle::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL SwXTextCellStyle::getSupportedServiceNames()
+{
+ return {"com.sun.star.style.Style"};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unotbl.cxx b/sw/source/core/unocore/unotbl.cxx
new file mode 100644
index 000000000..801f6cc95
--- /dev/null
+++ b/sw/source/core/unocore/unotbl.cxx
@@ -0,0 +1,4167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <tuple>
+#include <utility>
+#include <memory>
+#include <vector>
+#include <algorithm>
+#include <limits>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <tools/UnitConversion.hxx>
+#include <editeng/memberids.h>
+#include <float.h>
+#include <swtypes.hxx>
+#include <cmdid.h>
+#include <unocoll.hxx>
+#include <unomid.h>
+#include <unomap.hxx>
+#include <unotbl.hxx>
+#include <section.hxx>
+#include <unocrsr.hxx>
+#include <hints.hxx>
+#include <swtblfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentContentOperations.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentState.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <shellres.hxx>
+#include <docary.hxx>
+#include <ndole.hxx>
+#include <frame.hxx>
+#include <vcl/svapp.hxx>
+#include <fmtfsize.hxx>
+#include <tblafmt.hxx>
+#include <tabcol.hxx>
+#include <cellatr.hxx>
+#include <fmtpdsc.hxx>
+#include <pagedesc.hxx>
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <tabfrm.hxx>
+#include <redline.hxx>
+#include <unoport.hxx>
+#include <unocrsrhelper.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/WrapTextMode.hpp>
+#include <com/sun/star/text/TextContentAnchorType.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/XTextSection.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/TableBorder2.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/table/TableBorderDistances.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/chart/XChartDataChangeEventListener.hpp>
+#include <com/sun/star/chart/ChartDataChangeEvent.hpp>
+#include <com/sun/star/table/CellContentType.hpp>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unoparagraph.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <fmtornt.hxx>
+#include <editeng/keepitem.hxx>
+#include <fmtlsplt.hxx>
+#include <swundo.hxx>
+#include <SwStyleNameMapper.hxx>
+#include <frmatr.hxx>
+#include <sortopt.hxx>
+#include <sal/log.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <swtable.hxx>
+#include <docsh.hxx>
+#include <fesh.hxx>
+#include <itabenum.hxx>
+#include <frameformats.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+using ::editeng::SvxBorderLine;
+
+namespace
+{
+ template<typename Tcoretype, typename Tunotype>
+ struct FindUnoInstanceHint final : SfxHint
+ {
+ FindUnoInstanceHint(Tcoretype* pCore) : m_pCore(pCore), m_pResult(nullptr) {};
+ const Tcoretype* const m_pCore;
+ mutable rtl::Reference<Tunotype> m_pResult;
+ };
+ SwFrameFormat* lcl_EnsureCoreConnected(SwFrameFormat* pFormat, cppu::OWeakObject* pObject)
+ {
+ if(!pFormat)
+ throw uno::RuntimeException("Lost connection to core objects", pObject);
+ return pFormat;
+ }
+ SwTable* lcl_EnsureTableNotComplex(SwTable* pTable, cppu::OWeakObject* pObject)
+ {
+ if(pTable->IsTableComplex())
+ throw uno::RuntimeException("Table too complex", pObject);
+ return pTable;
+ }
+
+ chart::ChartDataChangeEvent createChartEvent(uno::Reference<uno::XInterface> const& xSource)
+ {
+ //TODO: find appropriate settings of the Event
+ chart::ChartDataChangeEvent event;
+ event.Source = xSource;
+ event.Type = chart::ChartDataChangeType_ALL;
+ event.StartColumn = 0;
+ event.EndColumn = 1;
+ event.StartRow = 0;
+ event.EndRow = 1;
+ return event;
+ }
+
+ void lcl_SendChartEvent(std::unique_lock<std::mutex>& rGuard,
+ uno::Reference<uno::XInterface> const& xSource,
+ ::comphelper::OInterfaceContainerHelper4<chart::XChartDataChangeEventListener> & rListeners)
+ {
+ if (rListeners.getLength(rGuard))
+ rListeners.notifyEach(rGuard,
+ &chart::XChartDataChangeEventListener::chartDataChanged,
+ createChartEvent(xSource));
+ }
+}
+
+#define UNO_TABLE_COLUMN_SUM 10000
+
+
+static bool lcl_LineToSvxLine(const table::BorderLine& rLine, SvxBorderLine& rSvxLine)
+{
+ rSvxLine.SetColor(Color(ColorTransparency, rLine.Color));
+
+ rSvxLine.GuessLinesWidths( SvxBorderLineStyle::NONE,
+ o3tl::toTwips(rLine.OuterLineWidth, o3tl::Length::mm100),
+ o3tl::toTwips(rLine.InnerLineWidth, o3tl::Length::mm100),
+ o3tl::toTwips(rLine.LineDistance, o3tl::Length::mm100) );
+
+ return rLine.InnerLineWidth > 0 || rLine.OuterLineWidth > 0;
+}
+
+/// @throws lang::IllegalArgumentException
+/// @throws uno::RuntimeException
+static void lcl_SetSpecialProperty(SwFrameFormat* pFormat,
+ const SfxItemPropertyMapEntry* pEntry,
+ const uno::Any& aValue)
+{
+ // special treatment for "non-items"
+ switch(pEntry->nWID)
+ {
+ case FN_TABLE_HEADLINE_REPEAT:
+ case FN_TABLE_HEADLINE_COUNT:
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ UnoActionContext aAction(pFormat->GetDoc());
+ if( pEntry->nWID == FN_TABLE_HEADLINE_REPEAT)
+ {
+ pFormat->GetDoc()->SetRowsToRepeat( *pTable, aValue.get<bool>() ? 1 : 0 );
+ }
+ else
+ {
+ sal_Int32 nRepeat = 0;
+ aValue >>= nRepeat;
+ if( nRepeat >= 0 && nRepeat < SAL_MAX_UINT16 )
+ pFormat->GetDoc()->SetRowsToRepeat( *pTable, o3tl::narrowing<sal_uInt16>(nRepeat) );
+ }
+ }
+ break;
+
+ case FN_TABLE_IS_RELATIVE_WIDTH:
+ case FN_TABLE_WIDTH:
+ case FN_TABLE_RELATIVE_WIDTH:
+ {
+ SwFormatFrameSize aSz( pFormat->GetFrameSize() );
+ if(FN_TABLE_WIDTH == pEntry->nWID)
+ {
+ sal_Int32 nWidth = 0;
+ aValue >>= nWidth;
+ aSz.SetWidthPercent(0);
+ aSz.SetWidth ( o3tl::toTwips(nWidth, o3tl::Length::mm100) );
+ }
+ else if(FN_TABLE_RELATIVE_WIDTH == pEntry->nWID)
+ {
+ sal_Int16 nSet = 0;
+ aValue >>= nSet;
+ if(nSet && nSet <=100)
+ aSz.SetWidthPercent( static_cast<sal_uInt8>(nSet) );
+ }
+ else if(FN_TABLE_IS_RELATIVE_WIDTH == pEntry->nWID)
+ {
+ if(!aValue.get<bool>())
+ aSz.SetWidthPercent(0);
+ else
+ {
+ lang::IllegalArgumentException aExcept;
+ aExcept.Message = "relative width cannot be switched on with this property";
+ throw aExcept;
+ }
+ }
+ pFormat->GetDoc()->SetAttr(aSz, *pFormat);
+ }
+ break;
+
+ case RES_PAGEDESC:
+ {
+ OUString sPageStyle;
+ aValue >>= sPageStyle;
+ const SwPageDesc* pDesc = nullptr;
+ if (!sPageStyle.isEmpty())
+ {
+ SwStyleNameMapper::FillUIName(sPageStyle, sPageStyle, SwGetPoolIdFromName::PageDesc);
+ pDesc = SwPageDesc::GetByName(*pFormat->GetDoc(), sPageStyle);
+ }
+ SwFormatPageDesc aDesc( pDesc );
+ pFormat->GetDoc()->SetAttr(aDesc, *pFormat);
+ }
+ break;
+
+ default:
+ throw lang::IllegalArgumentException();
+ }
+}
+
+static uno::Any lcl_GetSpecialProperty(SwFrameFormat* pFormat, const SfxItemPropertyMapEntry* pEntry )
+{
+ switch(pEntry->nWID)
+ {
+ case FN_TABLE_HEADLINE_REPEAT:
+ case FN_TABLE_HEADLINE_COUNT:
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ const sal_uInt16 nRepeat = pTable->GetRowsToRepeat();
+ if(pEntry->nWID == FN_TABLE_HEADLINE_REPEAT)
+ return uno::Any(nRepeat > 0);
+ return uno::Any(sal_Int32(nRepeat));
+ }
+
+ case FN_TABLE_WIDTH:
+ case FN_TABLE_IS_RELATIVE_WIDTH:
+ case FN_TABLE_RELATIVE_WIDTH:
+ {
+ uno::Any aRet;
+ const SwFormatFrameSize& rSz = pFormat->GetFrameSize();
+ if(FN_TABLE_WIDTH == pEntry->nWID)
+ rSz.QueryValue(aRet, MID_FRMSIZE_WIDTH|CONVERT_TWIPS);
+ else if(FN_TABLE_RELATIVE_WIDTH == pEntry->nWID)
+ rSz.QueryValue(aRet, MID_FRMSIZE_REL_WIDTH);
+ else
+ aRet <<= (0 != rSz.GetWidthPercent());
+ return aRet;
+ }
+
+ case RES_PAGEDESC:
+ {
+ const SfxItemSet& rSet = pFormat->GetAttrSet();
+ if(const SwFormatPageDesc* pItem = rSet.GetItemIfSet(RES_PAGEDESC, false))
+ {
+ const SwPageDesc* pDsc = pItem->GetPageDesc();
+ if(pDsc)
+ return uno::Any(SwStyleNameMapper::GetProgName(pDsc->GetName(), SwGetPoolIdFromName::PageDesc ));
+ }
+ return uno::Any(OUString());
+ }
+
+ case RES_ANCHOR:
+ return uno::Any(text::TextContentAnchorType_AT_PARAGRAPH);
+
+ case FN_UNO_ANCHOR_TYPES:
+ {
+ uno::Sequence<text::TextContentAnchorType> aTypes{text::TextContentAnchorType_AT_PARAGRAPH};
+ return uno::Any(aTypes);
+ }
+
+ case FN_UNO_WRAP :
+ return uno::Any(text::WrapTextMode_NONE);
+
+ case FN_PARAM_LINK_DISPLAY_NAME :
+ return uno::Any(pFormat->GetName());
+
+ case FN_UNO_REDLINE_NODE_START:
+ case FN_UNO_REDLINE_NODE_END:
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwNode* pTableNode = pTable->GetTableNode();
+ if(FN_UNO_REDLINE_NODE_END == pEntry->nWID)
+ pTableNode = pTableNode->EndOfSectionNode();
+ for(const SwRangeRedline* pRedline : pFormat->GetDoc()->getIDocumentRedlineAccess().GetRedlineTable())
+ {
+ const SwNode& rRedPointNode = pRedline->GetNode();
+ const SwNode& rRedMarkNode = pRedline->GetNode(false);
+ if(&rRedPointNode == pTableNode || &rRedMarkNode == pTableNode)
+ {
+ const SwNode& rStartOfRedline = SwNodeIndex(rRedPointNode) <= SwNodeIndex(rRedMarkNode) ?
+ rRedPointNode : rRedMarkNode;
+ bool bIsStart = &rStartOfRedline == pTableNode;
+ return uno::Any(SwXRedlinePortion::CreateRedlineProperties(*pRedline, bIsStart));
+ }
+ }
+ }
+ }
+ return uno::Any();
+}
+
+/** get position of a cell with a given name
+ *
+ * If everything was OK, the indices for column and row are changed (both >= 0).
+ * In case of errors, at least one of them is < 0.
+ *
+ * Also since the implementations of tables does not really have columns using
+ * this function is appropriate only for tables that are not complex (i.e.
+ * where IsTableComplex() returns false).
+ *
+ * @param rCellName e.g. A1..Z1, a1..z1, AA1..AZ1, Aa1..Az1, BA1..BZ1, Ba1..Bz1, ...
+ * @param [IN,OUT] o_rColumn (0-based)
+ * @param [IN,OUT] o_rRow (0-based)
+ */
+//TODO: potential for throwing proper exceptions instead of having every caller to check for errors
+void SwXTextTable::GetCellPosition(const OUString& rCellName, sal_Int32& o_rColumn, sal_Int32& o_rRow)
+{
+ o_rColumn = o_rRow = -1; // default return values indicating failure
+ const sal_Int32 nLen = rCellName.getLength();
+ if(!nLen)
+ {
+ SAL_WARN("sw.uno", "failed to get column or row index");
+ return;
+ }
+ sal_Int32 nRowPos = 0;
+ while (nRowPos<nLen)
+ {
+ if (rCellName[nRowPos]>='0' && rCellName[nRowPos]<='9')
+ {
+ break;
+ }
+ ++nRowPos;
+ }
+ if (nRowPos<=0 || nRowPos>=nLen)
+ return;
+
+ sal_Int32 nColIdx = 0;
+ for (sal_Int32 i = 0; i < nRowPos; ++i)
+ {
+ nColIdx *= 52;
+ if (i < nRowPos - 1)
+ ++nColIdx;
+ const sal_Unicode cChar = rCellName[i];
+ if ('A' <= cChar && cChar <= 'Z')
+ nColIdx += cChar - 'A';
+ else if ('a' <= cChar && cChar <= 'z')
+ nColIdx += 26 + cChar - 'a';
+ else
+ {
+ nColIdx = -1; // sth failed
+ break;
+ }
+ }
+
+ o_rColumn = nColIdx;
+ o_rRow = o3tl::toInt32(rCellName.subView(nRowPos)) - 1; // - 1 because indices ought to be 0 based
+}
+
+/** compare position of two cells (check rows first)
+ *
+ * @note this function probably also make sense only
+ * for cell names of non-complex tables
+ *
+ * @param rCellName1 e.g. "A1" (non-empty string with valid cell name)
+ * @param rCellName2 e.g. "A1" (non-empty string with valid cell name)
+ * @return -1 if cell_1 < cell_2; 0 if both cells are equal; +1 if cell_1 > cell_2
+ */
+int sw_CompareCellsByRowFirst( const OUString &rCellName1, const OUString &rCellName2 )
+{
+ sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1;
+ SwXTextTable::GetCellPosition( rCellName1, nCol1, nRow1 );
+ SwXTextTable::GetCellPosition( rCellName2, nCol2, nRow2 );
+
+ if (nRow1 < nRow2 || (nRow1 == nRow2 && nCol1 < nCol2))
+ return -1;
+ else if (nCol1 == nCol2 && nRow1 == nRow2)
+ return 0;
+ else
+ return +1;
+}
+
+/** compare position of two cells (check columns first)
+ *
+ * @note this function probably also make sense only
+ * for cell names of non-complex tables
+ *
+ * @param rCellName1 e.g. "A1" (non-empty string with valid cell name)
+ * @param rCellName2 e.g. "A1" (non-empty string with valid cell name)
+ * @return -1 if cell_1 < cell_2; 0 if both cells are equal; +1 if cell_1 > cell_2
+ */
+int sw_CompareCellsByColFirst( const OUString &rCellName1, const OUString &rCellName2 )
+{
+ sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1;
+ SwXTextTable::GetCellPosition( rCellName1, nCol1, nRow1 );
+ SwXTextTable::GetCellPosition( rCellName2, nCol2, nRow2 );
+
+ if (nCol1 < nCol2 || (nCol1 == nCol2 && nRow1 < nRow2))
+ return -1;
+ else if (nRow1 == nRow2 && nCol1 == nCol2)
+ return 0;
+ else
+ return +1;
+}
+
+/** compare position of two cell ranges
+ *
+ * @note this function probably also make sense only
+ * for cell names of non-complex tables
+ *
+ * @param rRange1StartCell e.g. "A1" (non-empty string with valid cell name)
+ * @param rRange1EndCell e.g. "A1" (non-empty string with valid cell name)
+ * @param rRange2StartCell e.g. "A1" (non-empty string with valid cell name)
+ * @param rRange2EndCell e.g. "A1" (non-empty string with valid cell name)
+ * @param bCmpColsFirst if <true> position in columns will be compared first before rows
+ *
+ * @return -1 if cell_range_1 < cell_range_2; 0 if both cell ranges are equal; +1 if cell_range_1 > cell_range_2
+ */
+int sw_CompareCellRanges(
+ const OUString &rRange1StartCell, const OUString &rRange1EndCell,
+ const OUString &rRange2StartCell, const OUString &rRange2EndCell,
+ bool bCmpColsFirst )
+{
+ int (*pCompareCells)( const OUString &, const OUString & ) =
+ bCmpColsFirst ? &sw_CompareCellsByColFirst : &sw_CompareCellsByRowFirst;
+
+ int nCmpResStartCells = pCompareCells( rRange1StartCell, rRange2StartCell );
+ if ((-1 == nCmpResStartCells ) ||
+ ( 0 == nCmpResStartCells &&
+ -1 == pCompareCells( rRange1EndCell, rRange2EndCell ) ))
+ return -1;
+ else if (0 == nCmpResStartCells &&
+ 0 == pCompareCells( rRange1EndCell, rRange2EndCell ))
+ return 0;
+ else
+ return +1;
+}
+
+/** get cell name at a specified coordinate
+ *
+ * @param nColumn column index (0-based)
+ * @param nRow row index (0-based)
+ * @return the cell name
+ */
+OUString sw_GetCellName( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ if (nColumn < 0 || nRow < 0)
+ return OUString();
+ OUString sCellName;
+ sw_GetTableBoxColStr( static_cast< sal_uInt16 >(nColumn), sCellName );
+ return sCellName + OUString::number( nRow + 1 );
+}
+
+/** Find the top left or bottom right corner box in given table.
+ Consider nested lines when finding the box.
+
+ @param rTableLines the table
+ @param i_bTopLeft if true, find top left box, otherwise find bottom
+ right box
+ */
+static const SwTableBox* lcl_FindCornerTableBox(const SwTableLines& rTableLines, const bool i_bTopLeft)
+{
+ const SwTableLines* pLines(&rTableLines);
+ while(true)
+ {
+ assert(!pLines->empty());
+ if(pLines->empty())
+ return nullptr;
+ const SwTableLine* pLine(i_bTopLeft ? pLines->front() : pLines->back());
+ assert(pLine);
+ const SwTableBoxes& rBoxes(pLine->GetTabBoxes());
+ assert(rBoxes.size() != 0);
+ const SwTableBox* pBox = i_bTopLeft ? rBoxes.front() : rBoxes.back();
+ assert(pBox);
+ if (pBox->GetSttNd())
+ return pBox;
+ pLines = &pBox->GetTabLines();
+ }
+}
+
+/** cleanup order in a range
+ *
+ * Sorts the input to a uniform format. I.e. for the four possible representation
+ * A1:C5, C5:A1, A5:C1, C1:A5
+ * the result will be always A1:C5.
+ *
+ * @param [IN,OUT] rCell1 cell name (will be modified to upper-left corner), e.g. "A1" (non-empty string with valid cell name)
+ * @param [IN,OUT] rCell2 cell name (will be modified to lower-right corner), e.g. "A1" (non-empty string with valid cell name)
+ */
+void sw_NormalizeRange(OUString &rCell1, OUString &rCell2)
+{
+ sal_Int32 nCol1 = -1, nRow1 = -1, nCol2 = -1, nRow2 = -1;
+ SwXTextTable::GetCellPosition( rCell1, nCol1, nRow1 );
+ SwXTextTable::GetCellPosition( rCell2, nCol2, nRow2 );
+ if (nCol2 < nCol1 || nRow2 < nRow1)
+ {
+ rCell1 = sw_GetCellName( std::min(nCol1, nCol2), std::min(nRow1, nRow2) );
+ rCell2 = sw_GetCellName( std::max(nCol1, nCol2), std::max(nRow1, nRow2) );
+ }
+}
+
+void SwRangeDescriptor::Normalize()
+{
+ if (nTop > nBottom)
+ std::swap(nBottom, nTop);
+ if (nLeft > nRight)
+ std::swap(nLeft, nRight);
+}
+
+static rtl::Reference<SwXCell> lcl_CreateXCell(SwFrameFormat* pFormat, sal_Int32 nColumn, sal_Int32 nRow)
+{
+ const OUString sCellName = sw_GetCellName(nColumn, nRow);
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox(sCellName));
+ if(!pBox)
+ return nullptr;
+ return SwXCell::CreateXCell(pFormat, pBox, pTable);
+}
+
+static void lcl_InspectLines(SwTableLines& rLines, std::vector<OUString>& rAllNames)
+{
+ for(auto pLine : rLines)
+ {
+ for(auto pBox : pLine->GetTabBoxes())
+ {
+ if(!pBox->GetName().isEmpty() && pBox->getRowSpan() > 0)
+ rAllNames.push_back(pBox->GetName());
+ SwTableLines& rBoxLines = pBox->GetTabLines();
+ if(!rBoxLines.empty())
+ lcl_InspectLines(rBoxLines, rAllNames);
+ }
+ }
+}
+
+static bool lcl_FormatTable(SwFrameFormat const * pTableFormat)
+{
+ bool bHasFrames = false;
+ SwIterator<SwFrame,SwFormat> aIter( *pTableFormat );
+ for(SwFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next())
+ {
+ vcl::RenderContext* pRenderContext = pFrame->getRootFrame()->GetCurrShell()->GetOut();
+ // mba: no TYPEINFO for SwTabFrame
+ if(!pFrame->IsTabFrame())
+ continue;
+ DisableCallbackAction a(*pFrame->getRootFrame());
+ SwTabFrame* pTabFrame = static_cast<SwTabFrame*>(pFrame);
+ if(pTabFrame->isFrameAreaDefinitionValid())
+ pTabFrame->InvalidatePos();
+ pTabFrame->SetONECalcLowers();
+ pTabFrame->Calc(pRenderContext);
+ bHasFrames = true;
+ }
+ return bHasFrames;
+}
+
+static void lcl_CursorSelect(SwPaM& rCursor, bool bExpand)
+{
+ if(bExpand)
+ {
+ if(!rCursor.HasMark())
+ rCursor.SetMark();
+ }
+ else if(rCursor.HasMark())
+ rCursor.DeleteMark();
+}
+
+static void lcl_GetTableSeparators(uno::Any& rRet, SwTable const * pTable, SwTableBox const * pBox, bool bRow)
+{
+ SwTabCols aCols;
+ aCols.SetLeftMin ( 0 );
+ aCols.SetLeft ( 0 );
+ aCols.SetRight ( UNO_TABLE_COLUMN_SUM );
+ aCols.SetRightMax( UNO_TABLE_COLUMN_SUM );
+
+ pTable->GetTabCols( aCols, pBox, false, bRow );
+
+ const size_t nSepCount = aCols.Count();
+ uno::Sequence< text::TableColumnSeparator> aColSeq(nSepCount);
+ text::TableColumnSeparator* pArray = aColSeq.getArray();
+ bool bError = false;
+ for(size_t i = 0; i < nSepCount; ++i)
+ {
+ pArray[i].Position = static_cast< sal_Int16 >(aCols[i]);
+ pArray[i].IsVisible = !aCols.IsHidden(i);
+ if(!bRow && !pArray[i].IsVisible)
+ {
+ bError = true;
+ break;
+ }
+ }
+ if(!bError)
+ rRet <<= aColSeq;
+
+}
+
+static void lcl_SetTableSeparators(const uno::Any& rVal, SwTable* pTable, SwTableBox const * pBox, bool bRow, SwDoc* pDoc)
+{
+ SwTabCols aOldCols;
+
+ aOldCols.SetLeftMin ( 0 );
+ aOldCols.SetLeft ( 0 );
+ aOldCols.SetRight ( UNO_TABLE_COLUMN_SUM );
+ aOldCols.SetRightMax( UNO_TABLE_COLUMN_SUM );
+
+ pTable->GetTabCols( aOldCols, pBox, false, bRow );
+ const size_t nOldCount = aOldCols.Count();
+ // there is no use in setting tab cols if there is only one column
+ if( !nOldCount )
+ return;
+
+ auto pSepSeq =
+ o3tl::tryAccess<uno::Sequence<text::TableColumnSeparator>>(rVal);
+ if(!pSepSeq || static_cast<size_t>(pSepSeq->getLength()) != nOldCount)
+ return;
+ SwTabCols aCols(aOldCols);
+ const text::TableColumnSeparator* pArray = pSepSeq->getConstArray();
+ tools::Long nLastValue = 0;
+ //sal_Int32 nTableWidth = aCols.GetRight() - aCols.GetLeft();
+ for(size_t i = 0; i < nOldCount; ++i)
+ {
+ aCols[i] = pArray[i].Position;
+ if(bool(pArray[i].IsVisible) == aCols.IsHidden(i) ||
+ (!bRow && aCols.IsHidden(i)) ||
+ aCols[i] < nLastValue ||
+ UNO_TABLE_COLUMN_SUM < aCols[i] )
+ return; // probably this should assert()
+ nLastValue = aCols[i];
+ }
+ pDoc->SetTabCols(*pTable, aCols, aOldCols, pBox, bRow );
+}
+
+/* non UNO function call to set string in SwXCell */
+void sw_setString( SwXCell &rCell, const OUString &rText,
+ bool bKeepNumberFormat = false )
+{
+ if(rCell.IsValid())
+ {
+ SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat();
+ pBoxFormat->LockModify();
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_FORMULA );
+ pBoxFormat->ResetFormatAttr( RES_BOXATR_VALUE );
+ if (!bKeepNumberFormat)
+ pBoxFormat->SetFormatAttr( SwTableBoxNumFormat(/*default Text*/) );
+ pBoxFormat->UnlockModify();
+ }
+ rCell.SwXText::setString(rText);
+}
+
+
+/* non UNO function call to set value in SwXCell */
+void sw_setValue( SwXCell &rCell, double nVal )
+{
+ if(!rCell.IsValid())
+ return;
+ // first this text (maybe) needs to be deleted
+ SwNodeOffset nNdPos = rCell.m_pBox->IsValidNumTextNd();
+ if(NODE_OFFSET_MAX != nNdPos)
+ sw_setString( rCell, OUString(), true ); // true == keep number format
+ SwDoc* pDoc = rCell.GetDoc();
+ UnoActionContext aAction(pDoc);
+ SwFrameFormat* pBoxFormat = rCell.m_pBox->ClaimFrameFormat();
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_VALUE> aSet(pDoc->GetAttrPool());
+
+ //!! do we need to set a new number format? Yes, if
+ // - there is no current number format
+ // - the current number format is not a number format according to the number formatter, but rather a text format
+ const SwTableBoxNumFormat* pNumFormat = pBoxFormat->GetAttrSet().GetItemIfSet(RES_BOXATR_FORMAT);
+ if(!pNumFormat
+ || pDoc->GetNumberFormatter()->IsTextFormat(pNumFormat->GetValue()))
+ {
+ aSet.Put(SwTableBoxNumFormat(0));
+ }
+
+ SwTableBoxValue aVal(nVal);
+ aSet.Put(aVal);
+ pDoc->SetTableBoxFormulaAttrs( *rCell.m_pBox, aSet );
+ // update table
+ SwTableFormulaUpdate aTableUpdate( SwTable::FindTable( rCell.GetFrameFormat() ));
+ pDoc->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+}
+
+
+SwXCell::SwXCell(SwFrameFormat* pTableFormat, SwTableBox* pBx, size_t const nPos) :
+ SwXText(pTableFormat->GetDoc(), CursorType::TableText),
+ m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_CELL)),
+ m_pBox(pBx),
+ m_pStartNode(nullptr),
+ m_pTableFormat(pTableFormat),
+ m_nFndPos(nPos)
+{
+ StartListening(pTableFormat->GetNotifier());
+}
+
+SwXCell::SwXCell(SwFrameFormat* pTableFormat, const SwStartNode& rStartNode) :
+ SwXText(pTableFormat->GetDoc(), CursorType::TableText),
+ m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_CELL)),
+ m_pBox(nullptr),
+ m_pStartNode(&rStartNode),
+ m_pTableFormat(pTableFormat),
+ m_nFndPos(NOTFOUND)
+{
+ StartListening(pTableFormat->GetNotifier());
+}
+
+SwXCell::~SwXCell()
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+}
+
+const uno::Sequence< sal_Int8 > & SwXCell::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXCellUnoTunnelId;
+ return theSwXCellUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXCell::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this, comphelper::FallbackToGetSomethingOf<SwXText>{});
+}
+
+uno::Sequence< uno::Type > SAL_CALL SwXCell::getTypes( )
+{
+ return comphelper::concatSequences(
+ SwXCellBaseClass::getTypes(),
+ SwXText::getTypes()
+ );
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SwXCell::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void SAL_CALL SwXCell::acquire( ) noexcept
+{
+ SwXCellBaseClass::acquire();
+}
+
+void SAL_CALL SwXCell::release( ) noexcept
+{
+ SolarMutexGuard aGuard;
+
+ SwXCellBaseClass::release();
+}
+
+uno::Any SAL_CALL SwXCell::queryInterface( const uno::Type& aType )
+{
+ uno::Any aRet = SwXCellBaseClass::queryInterface(aType);
+ if(aRet.getValueType() == cppu::UnoType<void>::get())
+ aRet = SwXText::queryInterface(aType);
+ return aRet;
+}
+
+const SwStartNode *SwXCell::GetStartNode() const
+{
+ const SwStartNode* pSttNd = nullptr;
+
+ if( m_pStartNode || IsValid() )
+ pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd();
+
+ return pSttNd;
+}
+
+uno::Reference< text::XTextCursor >
+SwXCell::CreateCursor()
+{
+ return createTextCursor();
+}
+
+bool SwXCell::IsValid() const
+{
+ // FIXME: this is now a const method, to make SwXText::IsValid invisible
+ // but the const_cast here are still ridiculous. TODO: find a better way.
+ SwFrameFormat* pTableFormat = m_pBox ? GetFrameFormat() : nullptr;
+ if(!pTableFormat)
+ {
+ const_cast<SwXCell*>(this)->m_pBox = nullptr;
+ }
+ else
+ {
+ SwTable* pTable = SwTable::FindTable( pTableFormat );
+ SwTableBox const*const pFoundBox =
+ const_cast<SwXCell*>(this)->FindBox(pTable, m_pBox);
+ if (!pFoundBox)
+ {
+ const_cast<SwXCell*>(this)->m_pBox = nullptr;
+ }
+ }
+ return nullptr != m_pBox;
+}
+
+OUString SwXCell::getFormula()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ return OUString();
+ SwTableBoxFormula aFormula( m_pBox->GetFrameFormat()->GetTableBoxFormula() );
+ SwTable* pTable = SwTable::FindTable( GetFrameFormat() );
+ aFormula.PtrToBoxNm( pTable );
+ return aFormula.GetFormula();
+}
+
+///@see sw_setValue (TODO: seems to be copy and paste programming here)
+void SwXCell::setFormula(const OUString& rFormula)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ return;
+ // first this text (maybe) needs to be deleted
+ SwNodeOffset nNdPos = m_pBox->IsValidNumTextNd();
+ if(SwNodeOffset(USHRT_MAX) == nNdPos)
+ sw_setString( *this, OUString(), true );
+ OUString sFormula(comphelper::string::stripStart(rFormula, ' '));
+ if( !sFormula.isEmpty() && '=' == sFormula[0] )
+ sFormula = sFormula.copy( 1 );
+ SwTableBoxFormula aFormula( sFormula );
+ SwDoc* pMyDoc = GetDoc();
+ UnoActionContext aAction(pMyDoc);
+ SfxItemSetFixed<RES_BOXATR_FORMAT, RES_BOXATR_FORMULA> aSet(pMyDoc->GetAttrPool());
+ SwFrameFormat* pBoxFormat = m_pBox->GetFrameFormat();
+ const SwTableBoxNumFormat* pNumFormat =
+ pBoxFormat->GetAttrSet().GetItemIfSet(RES_BOXATR_FORMAT);
+ if(!pNumFormat
+ || pMyDoc->GetNumberFormatter()->IsTextFormat(pNumFormat->GetValue()))
+ {
+ aSet.Put(SwTableBoxNumFormat(0));
+ }
+ aSet.Put(aFormula);
+ GetDoc()->SetTableBoxFormulaAttrs( *m_pBox, aSet );
+ // update table
+ SwTableFormulaUpdate aTableUpdate( SwTable::FindTable( GetFrameFormat() ));
+ pMyDoc->getIDocumentFieldsAccess().UpdateTableFields( &aTableUpdate );
+}
+
+double SwXCell::getValue()
+{
+ SolarMutexGuard aGuard;
+ // #i112652# a table cell may contain NaN as a value, do not filter that
+ if(IsValid() && !getString().isEmpty())
+ return m_pBox->GetFrameFormat()->GetTableBoxValue().GetValue();
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+void SwXCell::setValue(double rValue)
+{
+ SolarMutexGuard aGuard;
+ sw_setValue( *this, rValue );
+}
+
+table::CellContentType SwXCell::getType()
+{
+ SolarMutexGuard aGuard;
+
+ table::CellContentType nRes = table::CellContentType_EMPTY;
+ sal_uInt32 nNdPos = m_pBox->IsFormulaOrValueBox();
+ switch (nNdPos)
+ {
+ case 0 : nRes = table::CellContentType_TEXT; break;
+ case USHRT_MAX : nRes = table::CellContentType_EMPTY; break;
+ case RES_BOXATR_VALUE : nRes = table::CellContentType_VALUE; break;
+ case RES_BOXATR_FORMULA : nRes = table::CellContentType_FORMULA; break;
+ default :
+ OSL_FAIL( "unexpected case" );
+ }
+ return nRes;
+}
+
+void SwXCell::setString(const OUString& aString)
+{
+ SolarMutexGuard aGuard;
+ sw_setString( *this, aString );
+}
+
+sal_Int32 SwXCell::getError()
+{
+ SolarMutexGuard aGuard;
+ OUString sContent = getString();
+ return sal_Int32(sContent == SwViewShell::GetShellRes()->aCalc_Error);
+}
+
+uno::Reference<text::XTextCursor> SwXCell::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+ if(!m_pStartNode && !IsValid())
+ throw uno::RuntimeException();
+ const SwStartNode* pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ rtl::Reference<SwXTextCursor> const pXCursor =
+ new SwXTextCursor(*GetDoc(), this, CursorType::TableText, aPos);
+ auto& rUnoCursor(pXCursor->GetCursor());
+ rUnoCursor.Move(fnMoveForward, GoInNode);
+ return static_cast<text::XWordCursor*>(pXCursor.get());
+}
+
+uno::Reference<text::XTextCursor> SwXCell::createTextCursorByRange(const uno::Reference< text::XTextRange > & xTextPosition)
+{
+ SolarMutexGuard aGuard;
+ SwUnoInternalPaM aPam(*GetDoc());
+ if((!m_pStartNode && !IsValid()) || !::sw::XTextRangeToSwPaM(aPam, xTextPosition))
+ throw uno::RuntimeException();
+ const SwStartNode* pSttNd = m_pStartNode ? m_pStartNode : m_pBox->GetSttNd();
+ // skip sections
+ SwStartNode* p1 = aPam.GetNode().StartOfSectionNode();
+ while(p1->IsSectionNode())
+ p1 = p1->StartOfSectionNode();
+ if( p1 != pSttNd )
+ return nullptr;
+ return static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), this, CursorType::TableText,
+ *aPam.GetPoint(), aPam.GetMark()));
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXCell::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > xRef = m_pPropSet->getPropertySetInfo();
+ return xRef;
+}
+
+void SwXCell::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ return;
+ // Hack to support hidden property to transfer textDirection
+ if(rPropertyName == "FRMDirection")
+ {
+ SvxFrameDirectionItem aItem(SvxFrameDirection::Environment, RES_FRAMEDIR);
+ aItem.PutValue(aValue, 0);
+ m_pBox->GetFrameFormat()->SetFormatAttr(aItem);
+ }
+ else if(rPropertyName == "TableRedlineParams")
+ {
+ // Get the table row properties
+ uno::Sequence<beans::PropertyValue> tableCellProperties = aValue.get< uno::Sequence< beans::PropertyValue > >();
+ comphelper::SequenceAsHashMap aPropMap(tableCellProperties);
+ OUString sRedlineType;
+ if(!(aPropMap.getValue("RedlineType") >>= sRedlineType))
+ throw beans::UnknownPropertyException("No redline type property: ", static_cast<cppu::OWeakObject*>(this));
+
+ // Create a 'Table Cell Redline' object
+ SwUnoCursorHelper::makeTableCellRedline(*m_pBox, sRedlineType, tableCellProperties);
+
+
+ }
+ else
+ {
+ auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
+ if ( !pEntry )
+ {
+ // not a table property: ignore it, if it is a paragraph/character property
+ const SfxItemPropertySet& rParaPropSet = *aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH);
+ pEntry = rParaPropSet.getPropertyMap().getByName(rPropertyName);
+
+ if ( pEntry )
+ return;
+ }
+
+ if(!pEntry)
+ throw beans::UnknownPropertyException(rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ if(pEntry->nWID != FN_UNO_CELL_ROW_SPAN)
+ {
+ SwFrameFormat* pBoxFormat = m_pBox->ClaimFrameFormat();
+ SwAttrSet aSet(pBoxFormat->GetAttrSet());
+ m_pPropSet->setPropertyValue(rPropertyName, aValue, aSet);
+ pBoxFormat->GetDoc()->SetAttr(aSet, *pBoxFormat);
+ }
+ else if(aValue.isExtractableTo(cppu::UnoType<sal_Int32>::get()))
+ m_pBox->setRowSpan(aValue.get<sal_Int32>());
+ }
+}
+
+uno::Any SwXCell::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ return uno::Any();
+ auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
+ if(!pEntry)
+ throw beans::UnknownPropertyException(rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_CELL_ROW_SPAN:
+ return uno::Any(m_pBox->getRowSpan());
+ case FN_UNO_TEXT_SECTION:
+ {
+ SwFrameFormat* pTableFormat = GetFrameFormat();
+ SwTable* pTable = SwTable::FindTable(pTableFormat);
+ SwTableNode* pTableNode = pTable->GetTableNode();
+ SwSectionNode* pSectionNode = pTableNode->FindSectionNode();
+ if(!pSectionNode)
+ return uno::Any();
+ SwSection& rSect = pSectionNode->GetSection();
+ return uno::Any(SwXTextSections::GetObject(*rSect.GetFormat()));
+ }
+ break;
+ case FN_UNO_CELL_NAME:
+ return uno::Any(m_pBox->GetName());
+ case FN_UNO_REDLINE_NODE_START:
+ case FN_UNO_REDLINE_NODE_END:
+ {
+ //redline can only be returned if it's a living object
+ return SwXText::getPropertyValue(rPropertyName);
+ }
+ break;
+ case FN_UNO_PARENT_TEXT:
+ {
+ if (!m_xParentText.is())
+ {
+ const SwStartNode* pSttNd = m_pBox->GetSttNd();
+ if (!pSttNd)
+ return uno::Any();
+
+ const SwTableNode* pTableNode = pSttNd->FindTableNode();
+ if (!pTableNode)
+ return uno::Any();
+
+ SwPosition aPos(*pTableNode);
+ SwDoc& rDoc = aPos.GetDoc();
+ m_xParentText = sw::CreateParentXText(rDoc, aPos);
+ }
+
+ return uno::Any(m_xParentText);
+ }
+ break;
+ default:
+ {
+ const SwAttrSet& rSet = m_pBox->GetFrameFormat()->GetAttrSet();
+ uno::Any aResult;
+ m_pPropSet->getPropertyValue(rPropertyName, rSet, aResult);
+ return aResult;
+ }
+ }
+}
+
+void SwXCell::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXCell::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXCell::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXCell::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+uno::Reference<container::XEnumeration> SwXCell::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ if(!IsValid())
+ return uno::Reference<container::XEnumeration>();
+ const SwStartNode* pSttNd = m_pBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ // remember table and start node for later travelling
+ // (used in export of tables in tables)
+ return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::TableText, m_pBox);
+}
+
+uno::Type SAL_CALL SwXCell::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SwXCell::hasElements()
+{
+ return true;
+}
+
+void SwXCell::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pTableFormat = nullptr;
+ }
+ else if(auto pFindHint = dynamic_cast<const FindUnoInstanceHint<SwTableBox, SwXCell>*>(&rHint))
+ {
+ if(!pFindHint->m_pResult && pFindHint->m_pCore == GetTableBox())
+ pFindHint->m_pResult = this;
+ }
+}
+
+rtl::Reference<SwXCell> SwXCell::CreateXCell(SwFrameFormat* pTableFormat, SwTableBox* pBox, SwTable *pTable )
+{
+ if(!pTableFormat || !pBox)
+ return nullptr;
+ if(!pTable)
+ pTable = SwTable::FindTable(pTableFormat);
+ SwTableSortBoxes::const_iterator it = pTable->GetTabSortBoxes().find(pBox);
+ if(it == pTable->GetTabSortBoxes().end())
+ return nullptr;
+ size_t const nPos = it - pTable->GetTabSortBoxes().begin();
+ FindUnoInstanceHint<SwTableBox, SwXCell> aHint{pBox};
+ pTableFormat->GetNotifier().Broadcast(aHint);
+ return aHint.m_pResult ? aHint.m_pResult.get() : new SwXCell(pTableFormat, pBox, nPos);
+}
+
+/** search if a box exists in a table
+ *
+ * @param pTable the table to search in
+ * @param pBox2 box model to find
+ * @return the box if existent in pTable, 0 (!!!) if not found
+ */
+SwTableBox* SwXCell::FindBox(SwTable* pTable, SwTableBox* pBox2)
+{
+ // check if nFndPos happens to point to the right table box
+ if( m_nFndPos < pTable->GetTabSortBoxes().size() &&
+ pBox2 == pTable->GetTabSortBoxes()[ m_nFndPos ] )
+ return pBox2;
+
+ // if not, seek the entry (and return, if successful)
+ SwTableSortBoxes::const_iterator it = pTable->GetTabSortBoxes().find( pBox2 );
+ if( it != pTable->GetTabSortBoxes().end() )
+ {
+ m_nFndPos = it - pTable->GetTabSortBoxes().begin();
+ return pBox2;
+ }
+
+ // box not found: reset nFndPos pointer
+ m_nFndPos = NOTFOUND;
+ return nullptr;
+}
+
+double SwXCell::GetForcedNumericalValue() const
+{
+ if(table::CellContentType_TEXT != const_cast<SwXCell*>(this)->getType())
+ return getValue();
+ // now we'll try to get a useful numerical value
+ // from the text in the cell...
+ sal_uInt32 nFIndex;
+ SvNumberFormatter* pNumFormatter(const_cast<SvNumberFormatter*>(GetDoc()->GetNumberFormatter()));
+ // look for SwTableBoxNumFormat value in parents as well
+ auto pBoxFormat(GetTableBox()->GetFrameFormat());
+ const SwTableBoxNumFormat* pNumFormat = pBoxFormat->GetAttrSet().GetItemIfSet(RES_BOXATR_FORMAT);
+
+ if (pNumFormat)
+ {
+ // please note that the language of the numberformat
+ // is implicitly coded into the below value as well
+ nFIndex = pNumFormat->GetValue();
+
+ // since the current value indicates a text format but the call
+ // to 'IsNumberFormat' below won't work for text formats
+ // we need to get rid of the part that indicates the text format.
+ // According to ER this can be done like this:
+ nFIndex -= (nFIndex % SV_COUNTRY_LANGUAGE_OFFSET);
+ }
+ else
+ {
+ // system language is probably not the best possible choice
+ // but since we have to guess anyway (because the language of at
+ // the text is NOT the one used for the number format!)
+ // it is at least conform to what is used in
+ // SwTableShell::Execute when
+ // SID_ATTR_NUMBERFORMAT_VALUE is set...
+ LanguageType eLang = LANGUAGE_SYSTEM;
+ nFIndex = pNumFormatter->GetStandardIndex( eLang );
+ }
+ double fTmp;
+ if (!const_cast<SwDoc*>(GetDoc())->IsNumberFormat(const_cast<SwXCell*>(this)->getString(), nFIndex, fTmp))
+ return std::numeric_limits<double>::quiet_NaN();
+ return fTmp;
+}
+
+uno::Any SwXCell::GetAny() const
+{
+ if(!m_pBox)
+ throw uno::RuntimeException();
+ // check if table box value item is set
+ auto pBoxFormat(m_pBox->GetFrameFormat());
+ const bool bIsNum = pBoxFormat->GetItemState(RES_BOXATR_VALUE, false) == SfxItemState::SET;
+ return bIsNum ? uno::Any(getValue()) : uno::Any(const_cast<SwXCell*>(this)->getString());
+}
+
+OUString SwXCell::getImplementationName()
+ { return "SwXCell"; }
+
+sal_Bool SwXCell::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+uno::Sequence< OUString > SwXCell::getSupportedServiceNames()
+ { return {"com.sun.star.text.CellProperties"}; }
+
+OUString SwXTextTableRow::getImplementationName()
+ { return "SwXTextTableRow"; }
+
+sal_Bool SwXTextTableRow::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+uno::Sequence< OUString > SwXTextTableRow::getSupportedServiceNames()
+ { return {"com.sun.star.text.TextTableRow"}; }
+
+
+SwXTextTableRow::SwXTextTableRow(SwFrameFormat* pFormat, SwTableLine* pLn) :
+ m_pFormat(pFormat),
+ m_pLine(pLn),
+ m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_ROW))
+{
+ StartListening(m_pFormat->GetNotifier());
+}
+
+SwXTextTableRow::~SwXTextTableRow()
+{
+ SolarMutexGuard aGuard;
+ EndListeningAll();
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXTextTableRow::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> xRef = m_pPropSet->getPropertySetInfo();
+ return xRef;
+}
+
+void SwXTextTableRow::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwTableLine* pLn = SwXTextTableRow::FindLine(pTable, m_pLine);
+ if(!pLn)
+ return;
+
+ // Check for a specific property
+ if ( rPropertyName == "TableRedlineParams" )
+ {
+ // Get the table row properties
+ uno::Sequence< beans::PropertyValue > tableRowProperties = aValue.get< uno::Sequence< beans::PropertyValue > >();
+ comphelper::SequenceAsHashMap aPropMap( tableRowProperties );
+ OUString sRedlineType;
+ if( !(aPropMap.getValue("RedlineType") >>= sRedlineType) )
+ {
+ throw beans::UnknownPropertyException("No redline type property: ", static_cast < cppu::OWeakObject * > ( this ) );
+ }
+
+ // Create a 'Table Row Redline' object
+ SwUnoCursorHelper::makeTableRowRedline( *pLn, sRedlineType, tableRowProperties);
+
+ }
+ else
+ {
+ const SfxItemPropertyMapEntry* pEntry =
+ m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ SwDoc* pDoc = pFormat->GetDoc();
+ if (!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_ROW_HEIGHT:
+ case FN_UNO_ROW_AUTO_HEIGHT:
+ {
+ SwFormatFrameSize aFrameSize(pLn->GetFrameFormat()->GetFrameSize());
+ if(FN_UNO_ROW_AUTO_HEIGHT== pEntry->nWID)
+ {
+ bool bSet = *o3tl::doAccess<bool>(aValue);
+ aFrameSize.SetHeightSizeType(bSet ? SwFrameSize::Variable : SwFrameSize::Fixed);
+ }
+ else
+ {
+ sal_Int32 nHeight = 0;
+ aValue >>= nHeight;
+ Size aSz(aFrameSize.GetSize());
+ aSz.setHeight( o3tl::toTwips(nHeight, o3tl::Length::mm100) );
+ aFrameSize.SetSize(aSz);
+ }
+ pDoc->SetAttr(aFrameSize, *pLn->ClaimFrameFormat());
+ }
+ break;
+
+ case FN_UNO_TABLE_COLUMN_SEPARATORS:
+ {
+ UnoActionContext aContext(pDoc);
+ SwTable* pTable2 = SwTable::FindTable( pFormat );
+ lcl_SetTableSeparators(aValue, pTable2, m_pLine->GetTabBoxes()[0], true, pDoc);
+ }
+ break;
+
+ default:
+ {
+ SwFrameFormat* pLnFormat = pLn->ClaimFrameFormat();
+ SwAttrSet aSet(pLnFormat->GetAttrSet());
+ m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
+ pDoc->SetAttr(aSet, *pLnFormat);
+ }
+ }
+ }
+}
+
+uno::Any SwXTextTableRow::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwTableLine* pLn = SwXTextTableRow::FindLine(pTable, m_pLine);
+ if(pLn)
+ {
+ const SfxItemPropertyMapEntry* pEntry =
+ m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_ROW_HEIGHT:
+ case FN_UNO_ROW_AUTO_HEIGHT:
+ {
+ const SwFormatFrameSize& rSize = pLn->GetFrameFormat()->GetFrameSize();
+ if(FN_UNO_ROW_AUTO_HEIGHT== pEntry->nWID)
+ {
+ aRet <<= SwFrameSize::Variable == rSize.GetHeightSizeType();
+ }
+ else
+ aRet <<= static_cast<sal_Int32>(convertTwipToMm100(rSize.GetSize().Height()));
+ }
+ break;
+
+ case FN_UNO_TABLE_COLUMN_SEPARATORS:
+ {
+ lcl_GetTableSeparators(aRet, pTable, m_pLine->GetTabBoxes()[0], true);
+ }
+ break;
+
+ default:
+ {
+ const SwAttrSet& rSet = pLn->GetFrameFormat()->GetAttrSet();
+ m_pPropSet->getPropertyValue(*pEntry, rSet, aRet);
+ }
+ }
+ }
+ return aRet;
+}
+
+void SwXTextTableRow::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableRow::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableRow::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableRow::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableRow::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pFormat = nullptr;
+ } else if(auto pFindHint = dynamic_cast<const FindUnoInstanceHint<SwTableLine, SwXTextTableRow>*>(&rHint))
+ {
+ if(!pFindHint->m_pCore && pFindHint->m_pCore == m_pLine)
+ pFindHint->m_pResult = this;
+ }
+}
+
+SwTableLine* SwXTextTableRow::FindLine(SwTable* pTable, SwTableLine const * pLine)
+{
+ for(const auto& pCurrentLine : pTable->GetTabLines())
+ if(pCurrentLine == pLine)
+ return pCurrentLine;
+ return nullptr;
+}
+
+// SwXTextTableCursor
+
+OUString SwXTextTableCursor::getImplementationName()
+ { return "SwXTextTableCursor"; }
+
+sal_Bool SwXTextTableCursor::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+void SwXTextTableCursor::acquire() noexcept
+{
+ SwXTextTableCursor_Base::acquire();
+}
+
+void SwXTextTableCursor::release() noexcept
+{
+ SolarMutexGuard aGuard;
+ SwXTextTableCursor_Base::release();
+}
+
+css::uno::Any SAL_CALL
+SwXTextTableCursor::queryInterface( const css::uno::Type& _rType )
+{
+ css::uno::Any aReturn = SwXTextTableCursor_Base::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ aReturn = OTextCursorHelper::queryInterface( _rType );
+ return aReturn;
+}
+
+const SwPaM* SwXTextTableCursor::GetPaM() const { return &GetCursor(); }
+SwPaM* SwXTextTableCursor::GetPaM() { return &GetCursor(); }
+const SwDoc* SwXTextTableCursor::GetDoc() const { return GetFrameFormat()->GetDoc(); }
+SwDoc* SwXTextTableCursor::GetDoc() { return GetFrameFormat()->GetDoc(); }
+const SwUnoCursor& SwXTextTableCursor::GetCursor() const { return *m_pUnoCursor; }
+SwUnoCursor& SwXTextTableCursor::GetCursor() { return *m_pUnoCursor; }
+
+uno::Sequence<OUString> SwXTextTableCursor::getSupportedServiceNames()
+ { return {"com.sun.star.text.TextTableCursor"}; }
+
+SwXTextTableCursor::SwXTextTableCursor(SwFrameFormat* pFrameFormat, SwTableBox const* pBox)
+ : m_pFrameFormat(pFrameFormat)
+ , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_CURSOR))
+{
+ StartListening(m_pFrameFormat->GetNotifier());
+ SwDoc* pDoc = m_pFrameFormat->GetDoc();
+ const SwStartNode* pSttNd = pBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ m_pUnoCursor = pDoc->CreateUnoCursor(aPos, true);
+ m_pUnoCursor->Move( fnMoveForward, GoInNode );
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(*m_pUnoCursor);
+ rTableCursor.MakeBoxSels();
+}
+
+SwXTextTableCursor::SwXTextTableCursor(SwFrameFormat& rTableFormat, const SwTableCursor* pTableSelection)
+ : m_pFrameFormat(&rTableFormat)
+ , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE_CURSOR))
+{
+ StartListening(m_pFrameFormat->GetNotifier());
+ m_pUnoCursor = pTableSelection->GetDoc().CreateUnoCursor(*pTableSelection->GetPoint(), true);
+ if(pTableSelection->HasMark())
+ {
+ m_pUnoCursor->SetMark();
+ *m_pUnoCursor->GetMark() = *pTableSelection->GetMark();
+ }
+ const SwSelBoxes& rBoxes = pTableSelection->GetSelectedBoxes();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(*m_pUnoCursor);
+ for(auto pBox : rBoxes)
+ rTableCursor.InsertBox(*pBox);
+ rTableCursor.MakeBoxSels();
+}
+
+OUString SwXTextTableCursor::getRangeName()
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor* pTableCursor = dynamic_cast<SwUnoTableCursor*>(&rUnoCursor);
+ //!! see also SwChartDataSequence::getSourceRangeRepresentation
+ if(!pTableCursor)
+ return OUString();
+ pTableCursor->MakeBoxSels();
+ const SwStartNode* pNode = pTableCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
+ const SwTable* pTable = SwTable::FindTable(GetFrameFormat());
+ const SwTableBox* pEndBox = pTable->GetTableBox(pNode->GetIndex());
+ if(pTableCursor->HasMark())
+ {
+ pNode = pTableCursor->GetMark()->nNode.GetNode().FindTableBoxStartNode();
+ const SwTableBox* pStartBox = pTable->GetTableBox(pNode->GetIndex());
+ if(pEndBox != pStartBox)
+ {
+ // need to switch start and end?
+ if(*pTableCursor->GetPoint() < *pTableCursor->GetMark())
+ std::swap(pStartBox, pEndBox);
+ return pStartBox->GetName() + ":" + pEndBox->GetName();
+ }
+ }
+ return pEndBox->GetName();
+}
+
+sal_Bool SwXTextTableCursor::gotoCellByName(const OUString& sCellName, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ auto& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ return rTableCursor.GotoTableBox(sCellName);
+}
+
+sal_Bool SwXTextTableCursor::goLeft(sal_Int16 Count, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ return rTableCursor.Left(Count);
+}
+
+sal_Bool SwXTextTableCursor::goRight(sal_Int16 Count, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ return rTableCursor.Right(Count);
+}
+
+sal_Bool SwXTextTableCursor::goUp(sal_Int16 Count, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ return rTableCursor.UpDown(true, Count, nullptr, 0,
+ *rUnoCursor.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout());
+}
+
+sal_Bool SwXTextTableCursor::goDown(sal_Int16 Count, sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ return rTableCursor.UpDown(false, Count, nullptr, 0,
+ *rUnoCursor.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout());
+}
+
+void SwXTextTableCursor::gotoStart(sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ rTableCursor.MoveTable(GotoCurrTable, fnTableStart);
+}
+
+void SwXTextTableCursor::gotoEnd(sal_Bool bExpand)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ lcl_CursorSelect(rTableCursor, bExpand);
+ rTableCursor.MoveTable(GotoCurrTable, fnTableEnd);
+}
+
+sal_Bool SwXTextTableCursor::mergeRange()
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ {
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rTableCursor);
+ }
+ rTableCursor.MakeBoxSels();
+ bool bResult;
+ {
+ UnoActionContext aContext(&rUnoCursor.GetDoc());
+ bResult = TableMergeErr::Ok == rTableCursor.GetDoc().MergeTable(rTableCursor);
+ }
+ if(bResult)
+ {
+ size_t nCount = rTableCursor.GetSelectedBoxesCount();
+ while (nCount--)
+ rTableCursor.DeleteBox(nCount);
+ }
+ rTableCursor.MakeBoxSels();
+ return bResult;
+}
+
+sal_Bool SwXTextTableCursor::splitRange(sal_Int16 Count, sal_Bool Horizontal)
+{
+ SolarMutexGuard aGuard;
+ if (Count <= 0)
+ throw uno::RuntimeException("Illegal first argument: needs to be > 0", static_cast<cppu::OWeakObject*>(this));
+ SwUnoCursor& rUnoCursor = GetCursor();
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ {
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rTableCursor);
+ }
+ rTableCursor.MakeBoxSels();
+ bool bResult;
+ {
+ UnoActionContext aContext(&rUnoCursor.GetDoc());
+ bResult = rTableCursor.GetDoc().SplitTable(rTableCursor.GetSelectedBoxes(), !Horizontal, Count);
+ }
+ rTableCursor.MakeBoxSels();
+ return bResult;
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXTextTableCursor::getPropertySetInfo()
+{
+ static uno::Reference< beans::XPropertySetInfo > xRef = m_pPropSet->getPropertySetInfo();
+ return xRef;
+}
+
+void SwXTextTableCursor::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ if(pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ {
+ auto pSttNode = rUnoCursor.GetNode().StartOfSectionNode();
+ const SwTableNode* pTableNode = pSttNode->FindTableNode();
+ lcl_FormatTable(pTableNode->GetTable().GetFrameFormat());
+ }
+ auto& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ rTableCursor.MakeBoxSels();
+ SwDoc& rDoc = rUnoCursor.GetDoc();
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_TABLE_CELL_BACKGROUND:
+ {
+ std::unique_ptr<SfxPoolItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND));
+ SwDoc::GetBoxAttr(rUnoCursor, aBrush);
+ aBrush->PutValue(aValue, pEntry->nMemberId);
+ rDoc.SetBoxAttr(rUnoCursor, *aBrush);
+
+ }
+ break;
+ case RES_BOXATR_FORMAT:
+ {
+ SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT);
+ aNumberFormat.PutValue(aValue, 0);
+ rDoc.SetBoxAttr(rUnoCursor, aNumberFormat);
+ }
+ break;
+ case FN_UNO_PARA_STYLE:
+ SwUnoCursorHelper::SetTextFormatColl(aValue, rUnoCursor);
+ break;
+ default:
+ {
+ SfxItemSet aItemSet(rDoc.GetAttrPool(), pEntry->nWID, pEntry->nWID);
+ SwUnoCursorHelper::GetCursorAttr(rTableCursor.GetSelRing(),
+ aItemSet);
+
+ if (!SwUnoCursorHelper::SetCursorPropertyValue(
+ *pEntry, aValue, rTableCursor.GetSelRing(), aItemSet))
+ {
+ m_pPropSet->setPropertyValue(*pEntry, aValue, aItemSet);
+ }
+ SwUnoCursorHelper::SetCursorAttr(rTableCursor.GetSelRing(),
+ aItemSet, SetAttrMode::DEFAULT, true);
+ }
+ }
+}
+
+uno::Any SwXTextTableCursor::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ SwUnoCursor& rUnoCursor = GetCursor();
+ {
+ auto pSttNode = rUnoCursor.GetNode().StartOfSectionNode();
+ const SwTableNode* pTableNode = pSttNode->FindTableNode();
+ lcl_FormatTable(pTableNode->GetTable().GetFrameFormat());
+ }
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(rUnoCursor);
+ auto pEntry(m_pPropSet->getPropertyMap().getByName(rPropertyName));
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ rTableCursor.MakeBoxSels();
+ uno::Any aResult;
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_TABLE_CELL_BACKGROUND:
+ {
+ std::unique_ptr<SfxPoolItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND));
+ if (SwDoc::GetBoxAttr(rUnoCursor, aBrush))
+ aBrush->QueryValue(aResult, pEntry->nMemberId);
+ }
+ break;
+ case RES_BOXATR_FORMAT:
+ // TODO: GetAttr for table selections in a Doc is missing
+ throw uno::RuntimeException("Unknown property: " + rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ break;
+ case FN_UNO_PARA_STYLE:
+ {
+ auto pFormat(SwUnoCursorHelper::GetCurTextFormatColl(rUnoCursor, false));
+ if(pFormat)
+ aResult <<= pFormat->GetName();
+ }
+ break;
+ default:
+ {
+ SfxItemSetFixed
+ <RES_CHRATR_BEGIN, RES_FRMATR_END-1,
+ RES_UNKNOWNATR_CONTAINER, RES_UNKNOWNATR_CONTAINER>
+ aSet(rTableCursor.GetDoc().GetAttrPool());
+ SwUnoCursorHelper::GetCursorAttr(rTableCursor.GetSelRing(), aSet);
+ m_pPropSet->getPropertyValue(*pEntry, aSet, aResult);
+ }
+ }
+ return aResult;
+}
+
+void SwXTextTableCursor::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableCursor::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableCursor::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableCursor::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("not implemented", static_cast<cppu::OWeakObject*>(this)); };
+
+void SwXTextTableCursor::Notify( const SfxHint& rHint )
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pFrameFormat = nullptr;
+}
+
+
+// SwXTextTable ===========================================================
+
+namespace {
+
+class SwTableProperties_Impl
+{
+ SwUnoCursorHelper::SwAnyMapHelper m_aAnyMap;
+
+public:
+ SwTableProperties_Impl();
+
+ void SetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& aVal);
+ bool GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny);
+ void AddItemToSet(SfxItemSet& rSet, std::function<std::unique_ptr<SfxPoolItem>()> aItemFactory,
+ sal_uInt16 nWhich, std::initializer_list<sal_uInt16> vMember, bool bAddTwips = false);
+ void ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc);
+};
+
+}
+
+SwTableProperties_Impl::SwTableProperties_Impl()
+ { }
+
+void SwTableProperties_Impl::SetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any& rVal)
+ {
+ m_aAnyMap.SetValue(nWhichId, nMemberId, rVal);
+ }
+
+bool SwTableProperties_Impl::GetProperty(sal_uInt16 nWhichId, sal_uInt16 nMemberId, const uno::Any*& rpAny )
+ {
+ return m_aAnyMap.FillValue(nWhichId, nMemberId, rpAny);
+ }
+
+void SwTableProperties_Impl::AddItemToSet(SfxItemSet& rSet,
+ std::function<std::unique_ptr<SfxPoolItem>()> aItemFactory,
+ sal_uInt16 nWhich, std::initializer_list<sal_uInt16> vMember, bool bAddTwips)
+{
+ std::vector< std::pair<sal_uInt16, const uno::Any* > > vMemberAndAny;
+ for(sal_uInt16 nMember : vMember)
+ {
+ const uno::Any* pAny = nullptr;
+ GetProperty(nWhich, nMember, pAny);
+ if(pAny)
+ vMemberAndAny.emplace_back(nMember, pAny);
+ }
+ if(!vMemberAndAny.empty())
+ {
+ std::unique_ptr<SfxPoolItem> aItem(aItemFactory());
+ for(const auto& aMemberAndAny : vMemberAndAny)
+ aItem->PutValue(*aMemberAndAny.second, aMemberAndAny.first | (bAddTwips ? CONVERT_TWIPS : 0) );
+ rSet.Put(std::move(aItem));
+ }
+}
+void SwTableProperties_Impl::ApplyTableAttr(const SwTable& rTable, SwDoc& rDoc)
+{
+ SfxItemSetFixed<
+ RES_FRM_SIZE, RES_BREAK,
+ RES_HORI_ORIENT, RES_HORI_ORIENT,
+ RES_BACKGROUND, RES_BACKGROUND,
+ RES_SHADOW, RES_SHADOW,
+ RES_KEEP, RES_KEEP,
+ RES_LAYOUT_SPLIT, RES_LAYOUT_SPLIT>
+ aSet(rDoc.GetAttrPool());
+ const uno::Any* pRepHead;
+ const SwFrameFormat &rFrameFormat = *rTable.GetFrameFormat();
+ if(GetProperty(FN_TABLE_HEADLINE_REPEAT, 0xff, pRepHead ))
+ {
+ bool bVal(pRepHead->get<bool>());
+ const_cast<SwTable&>(rTable).SetRowsToRepeat( bVal ? 1 : 0 ); // TODO: MULTIHEADER
+ }
+
+ AddItemToSet(aSet, [&rFrameFormat]() { return rFrameFormat.makeBackgroundBrushItem(); }, RES_BACKGROUND, {
+ MID_BACK_COLOR,
+ MID_GRAPHIC_TRANSPARENT,
+ MID_GRAPHIC_POSITION,
+ MID_GRAPHIC,
+ MID_GRAPHIC_FILTER });
+
+ bool bPutBreak = true;
+ const uno::Any* pPage;
+ if(GetProperty(FN_UNO_PAGE_STYLE, 0, pPage) || GetProperty(RES_PAGEDESC, 0xff, pPage))
+ {
+ OUString sPageStyle = pPage->get<OUString>();
+ if(!sPageStyle.isEmpty())
+ {
+ SwStyleNameMapper::FillUIName(sPageStyle, sPageStyle, SwGetPoolIdFromName::PageDesc);
+ const SwPageDesc* pDesc = SwPageDesc::GetByName(rDoc, sPageStyle);
+ if(pDesc)
+ {
+ SwFormatPageDesc aDesc(pDesc);
+ const uno::Any* pPgNo;
+ if(GetProperty(RES_PAGEDESC, MID_PAGEDESC_PAGENUMOFFSET, pPgNo))
+ {
+ aDesc.SetNumOffset(pPgNo->get<sal_Int16>());
+ }
+ aSet.Put(aDesc);
+ bPutBreak = false;
+ }
+
+ }
+ }
+
+ if(bPutBreak)
+ AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr<SfxPoolItem>(rFrameFormat.GetBreak().Clone()); }, RES_BREAK, {0});
+ AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr<SfxPoolItem>(rFrameFormat.GetShadow().Clone()); }, RES_SHADOW, {0}, true);
+ AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr<SfxPoolItem>(rFrameFormat.GetKeep().Clone()); }, RES_KEEP, {0});
+ AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr<SfxPoolItem>(rFrameFormat.GetHoriOrient().Clone()); }, RES_HORI_ORIENT, {MID_HORIORIENT_ORIENT}, true);
+
+ const uno::Any* pSzRel(nullptr);
+ GetProperty(FN_TABLE_IS_RELATIVE_WIDTH, 0xff, pSzRel);
+ const uno::Any* pRelWidth(nullptr);
+ GetProperty(FN_TABLE_RELATIVE_WIDTH, 0xff, pRelWidth);
+ const uno::Any* pWidth(nullptr);
+ GetProperty(FN_TABLE_WIDTH, 0xff, pWidth);
+
+ bool bPutSize = pWidth != nullptr;
+ SwFormatFrameSize aSz(SwFrameSize::Variable);
+ if(pWidth)
+ {
+ aSz.PutValue(*pWidth, MID_FRMSIZE_WIDTH);
+ bPutSize = true;
+ }
+ if(pSzRel && pSzRel->get<bool>() && pRelWidth)
+ {
+ aSz.PutValue(*pRelWidth, MID_FRMSIZE_REL_WIDTH|CONVERT_TWIPS);
+ bPutSize = true;
+ }
+ if(bPutSize)
+ {
+ if(!aSz.GetWidth())
+ aSz.SetWidth(MINLAY);
+ aSet.Put(aSz);
+ }
+ AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr<SfxPoolItem>(rFrameFormat.GetLRSpace().Clone()); }, RES_LR_SPACE, {
+ MID_L_MARGIN|CONVERT_TWIPS,
+ MID_R_MARGIN|CONVERT_TWIPS });
+ AddItemToSet(aSet, [&rFrameFormat]() { return std::unique_ptr<SfxPoolItem>(rFrameFormat.GetULSpace().Clone()); }, RES_UL_SPACE, {
+ MID_UP_MARGIN|CONVERT_TWIPS,
+ MID_LO_MARGIN|CONVERT_TWIPS });
+ const::uno::Any* pSplit(nullptr);
+ if(GetProperty(RES_LAYOUT_SPLIT, 0, pSplit))
+ {
+ SwFormatLayoutSplit aSp(pSplit->get<bool>());
+ aSet.Put(aSp);
+ }
+ if(aSet.Count())
+ {
+ rDoc.SetAttr(aSet, *rTable.GetFrameFormat());
+ }
+}
+
+class SwXTextTable::Impl
+ : public SvtListener
+{
+private:
+ SwFrameFormat* m_pFrameFormat;
+
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_EventListeners;
+ ::comphelper::OInterfaceContainerHelper4<chart::XChartDataChangeEventListener> m_ChartListeners;
+
+ const SfxItemPropertySet * m_pPropSet;
+
+ css::uno::WeakReference<css::table::XTableRows> m_xRows;
+ css::uno::WeakReference<css::table::XTableColumns> m_xColumns;
+
+ bool m_bFirstRowAsLabel;
+ bool m_bFirstColumnAsLabel;
+
+ // Descriptor-interface
+ std::unique_ptr<SwTableProperties_Impl> m_pTableProps;
+ OUString m_sTableName;
+ unsigned short m_nRows;
+ unsigned short m_nColumns;
+
+ explicit Impl(SwFrameFormat* const pFrameFormat)
+ : m_pFrameFormat(pFrameFormat)
+ , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_TABLE))
+ , m_bFirstRowAsLabel(false)
+ , m_bFirstColumnAsLabel(false)
+ , m_pTableProps(pFrameFormat ? nullptr : new SwTableProperties_Impl)
+ , m_nRows(pFrameFormat ? 0 : 2)
+ , m_nColumns(pFrameFormat ? 0 : 2)
+ {
+ if(m_pFrameFormat)
+ StartListening(m_pFrameFormat->GetNotifier());
+ }
+
+ SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
+ void SetFrameFormat(SwFrameFormat& rFrameFormat)
+ {
+ EndListeningAll();
+ m_pFrameFormat = &rFrameFormat;
+ StartListening(m_pFrameFormat->GetNotifier());
+ }
+
+ bool IsDescriptor() const { return m_pTableProps != nullptr; }
+
+ // note: lock mutex before calling this to avoid concurrent update
+ static std::pair<sal_uInt16, sal_uInt16> ThrowIfComplex(SwXTextTable &rThis)
+ {
+ sal_uInt16 const nRowCount(rThis.m_pImpl->GetRowCount());
+ sal_uInt16 const nColCount(rThis.m_pImpl->GetColumnCount());
+ if (!nRowCount || !nColCount)
+ {
+ throw uno::RuntimeException("Table too complex",
+ static_cast<cppu::OWeakObject*>(&rThis));
+ }
+ return std::make_pair(nRowCount, nColCount);
+ }
+
+ sal_uInt16 GetRowCount();
+ sal_uInt16 GetColumnCount();
+
+ virtual void Notify(const SfxHint&) override;
+
+};
+
+const uno::Sequence< sal_Int8 > & SwXTextTable::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextTableUnoTunnelId;
+ return theSwXTextTableUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXTextTable::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+
+SwXTextTable::SwXTextTable()
+ : m_pImpl(new Impl(nullptr))
+{
+}
+
+SwXTextTable::SwXTextTable(SwFrameFormat& rFrameFormat)
+ : m_pImpl(new Impl(&rFrameFormat))
+{
+}
+
+SwXTextTable::~SwXTextTable()
+{
+}
+
+uno::Reference<text::XTextTable> SwXTextTable::CreateXTextTable(SwFrameFormat* const pFrameFormat)
+{
+ uno::Reference<text::XTextTable> xTable;
+ if(pFrameFormat)
+ xTable.set(pFrameFormat->GetXObject(), uno::UNO_QUERY); // cached?
+ if(xTable.is())
+ return xTable;
+ SwXTextTable* const pNew( pFrameFormat ? new SwXTextTable(*pFrameFormat) : new SwXTextTable());
+ xTable.set(pNew);
+ if(pFrameFormat)
+ pFrameFormat->SetXObject(xTable);
+ // need a permanent Reference to initialize m_wThis
+ pNew->m_pImpl->m_wThis = xTable;
+ return xTable;
+}
+
+SwFrameFormat* SwXTextTable::GetFrameFormat()
+{
+ return m_pImpl->GetFrameFormat();
+}
+
+void SwXTextTable::initialize(sal_Int32 nR, sal_Int32 nC)
+{
+ if (!m_pImpl->IsDescriptor() || nR <= 0 || nC <= 0 || nR >= SAL_MAX_UINT16 || nC >= SAL_MAX_UINT16)
+ throw uno::RuntimeException();
+ m_pImpl->m_nRows = o3tl::narrowing<sal_uInt16>(nR);
+ m_pImpl->m_nColumns = o3tl::narrowing<sal_uInt16>(nC);
+}
+
+uno::Reference<table::XTableRows> SAL_CALL SwXTextTable::getRows()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XTableRows> xResult(m_pImpl->m_xRows);
+ if(xResult.is())
+ return xResult;
+ if(SwFrameFormat* pFormat = GetFrameFormat())
+ m_pImpl->m_xRows = xResult = new SwXTableRows(*pFormat);
+ if(!xResult.is())
+ throw uno::RuntimeException();
+ return xResult;
+}
+
+uno::Reference<table::XTableColumns> SAL_CALL SwXTextTable::getColumns()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference<table::XTableColumns> xResult(m_pImpl->m_xColumns);
+ if(xResult.is())
+ return xResult;
+ if(SwFrameFormat* pFormat = GetFrameFormat())
+ m_pImpl->m_xColumns = xResult = new SwXTableColumns(*pFormat);
+ if(!xResult.is())
+ throw uno::RuntimeException();
+ return xResult;
+}
+
+uno::Reference<table::XCell> SwXTextTable::getCellByName(const OUString& sCellName)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox(sCellName));
+ if(!pBox)
+ return nullptr;
+ return SwXCell::CreateXCell(pFormat, pBox);
+}
+
+uno::Sequence<OUString> SwXTextTable::getCellNames()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat(GetFrameFormat());
+ if(!pFormat)
+ return {};
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ // exists at the table and at all boxes
+ SwTableLines& rTableLines = pTable->GetTabLines();
+ std::vector<OUString> aAllNames;
+ lcl_InspectLines(rTableLines, aAllNames);
+ return comphelper::containerToSequence(aAllNames);
+}
+
+uno::Reference<text::XTextTableCursor> SwXTextTable::createCursorByCellName(const OUString& sCellName)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ SwTableBox* pBox = const_cast<SwTableBox*>(pTable->GetTableBox(sCellName));
+ if(!pBox || pBox->getRowSpan() == 0)
+ throw uno::RuntimeException();
+ return new SwXTextTableCursor(pFormat, pBox);
+}
+
+void SAL_CALL
+SwXTextTable::attach(const uno::Reference<text::XTextRange> & xTextRange)
+{
+ SolarMutexGuard aGuard;
+
+ // attach() must only be called once
+ if (!m_pImpl->IsDescriptor()) /* already attached ? */
+ throw uno::RuntimeException("SwXTextTable: already attached to range.", static_cast<cppu::OWeakObject*>(this));
+
+ uno::Reference<XUnoTunnel> xRangeTunnel(xTextRange, uno::UNO_QUERY);
+ SwXTextRange* pRange(comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel));
+ OTextCursorHelper* pCursor(comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel));
+ SwDoc* pDoc = pRange ? &pRange->GetDoc() : pCursor ? pCursor->GetDoc() : nullptr;
+ if (!pDoc || !m_pImpl->m_nRows || !m_pImpl->m_nColumns)
+ throw lang::IllegalArgumentException();
+ SwUnoInternalPaM aPam(*pDoc);
+ // this now needs to return TRUE
+ ::sw::XTextRangeToSwPaM(aPam, xTextRange);
+ {
+ UnoActionContext aCont(pDoc);
+
+ pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr);
+ const SwTable* pTable(nullptr);
+ if( 0 != aPam.Start()->nContent.GetIndex() )
+ {
+ pDoc->getIDocumentContentOperations().SplitNode(*aPam.Start(), false);
+ }
+ //TODO: if it is the last paragraph than add another one!
+ if(aPam.HasMark())
+ {
+ pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
+ aPam.DeleteMark();
+ }
+ pTable = pDoc->InsertTable(SwInsertTableOptions( SwInsertTableFlags::Headline | SwInsertTableFlags::DefaultBorder | SwInsertTableFlags::SplitLayout, 0 ),
+ *aPam.GetPoint(),
+ m_pImpl->m_nRows,
+ m_pImpl->m_nColumns,
+ text::HoriOrientation::FULL);
+ if(pTable)
+ {
+ // here, the properties of the descriptor need to be analyzed
+ m_pImpl->m_pTableProps->ApplyTableAttr(*pTable, *pDoc);
+ SwFrameFormat* pTableFormat(pTable->GetFrameFormat());
+ lcl_FormatTable(pTableFormat);
+
+ m_pImpl->SetFrameFormat(*pTableFormat);
+
+ if (!m_pImpl->m_sTableName.isEmpty())
+ {
+ sal_uInt16 nIndex = 1;
+ OUString sTmpNameIndex(m_pImpl->m_sTableName);
+ while(pDoc->FindTableFormatByName(sTmpNameIndex, true) && nIndex < USHRT_MAX)
+ {
+ sTmpNameIndex = m_pImpl->m_sTableName + OUString::number(nIndex++);
+ }
+ pDoc->SetTableName( *pTableFormat, sTmpNameIndex);
+ }
+
+ const::uno::Any* pName;
+ if (m_pImpl->m_pTableProps->GetProperty(FN_UNO_TABLE_NAME, 0, pName))
+ setName(pName->get<OUString>());
+ m_pImpl->m_pTableProps.reset();
+ }
+ pDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
+ }
+}
+
+uno::Reference<text::XTextRange> SwXTextTable::getAnchor()
+{
+ SolarMutexGuard aGuard;
+ SwTableFormat *const pFormat = static_cast<SwTableFormat*>(
+ lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ return new SwXTextRange(*pFormat);
+}
+
+void SwXTextTable::dispose()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ SwSelBoxes aSelBoxes;
+ for(auto& rBox : pTable->GetTabSortBoxes() )
+ aSelBoxes.insert(rBox);
+ pFormat->GetDoc()->DeleteRowCol(aSelBoxes, SwDoc::RowColMode::DeleteProtected);
+}
+
+void SAL_CALL SwXTextTable::addEventListener(
+ const uno::Reference<lang::XEventListener> & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXTextTable::removeEventListener(
+ const uno::Reference< lang::XEventListener > & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_EventListeners.removeInterface(aGuard, xListener);
+}
+
+uno::Reference<table::XCell> SwXTextTable::getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat(GetFrameFormat());
+ // sheet is unimportant
+ if(nColumn >= 0 && nRow >= 0 && pFormat)
+ {
+ auto pXCell = lcl_CreateXCell(pFormat, nColumn, nRow);
+ if(pXCell)
+ return pXCell;
+ }
+ throw lang::IndexOutOfBoundsException();
+}
+
+namespace {
+
+uno::Reference<table::XCellRange> GetRangeByName(
+ SwFrameFormat* pFormat, SwTable const * pTable,
+ const OUString& rTLName, const OUString& rBRName,
+ SwRangeDescriptor const & rDesc)
+{
+ const SwTableBox* pTLBox = pTable->GetTableBox(rTLName);
+ if(!pTLBox)
+ return nullptr;
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor to the upper-left cell of the range
+ auto pUnoCursor(pFormat->GetDoc()->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ pUnoCursor->SetRemainInSection(false);
+ const SwTableBox* pBRBox(pTable->GetTableBox(rBRName));
+ if(!pBRBox)
+ return nullptr;
+ pUnoCursor->SetMark();
+ pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ rCursor.MakeBoxSels();
+ // pUnoCursor will be provided and will not be deleted
+ return SwXCellRange::CreateXCellRange(pUnoCursor, *pFormat, rDesc);
+}
+
+} // namespace
+
+uno::Reference<table::XCellRange> SwXTextTable::getCellRangeByPosition(sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat(GetFrameFormat());
+ if(pFormat &&
+ nLeft <= nRight && nTop <= nBottom &&
+ nLeft >= 0 && nRight >= 0 && nTop >= 0 && nBottom >= 0 )
+ {
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ if(!pTable->IsTableComplex())
+ {
+ SwRangeDescriptor aDesc;
+ aDesc.nTop = nTop;
+ aDesc.nBottom = nBottom;
+ aDesc.nLeft = nLeft;
+ aDesc.nRight = nRight;
+ const OUString sTLName = sw_GetCellName(aDesc.nLeft, aDesc.nTop);
+ const OUString sBRName = sw_GetCellName(aDesc.nRight, aDesc.nBottom);
+ // please note that according to the 'if' statement at the begin
+ // sTLName:sBRName already denotes the normalized range string
+ return GetRangeByName(pFormat, pTable, sTLName, sBRName, aDesc);
+ }
+ }
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Reference<table::XCellRange> SwXTextTable::getCellRangeByName(const OUString& sRange)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFormat), static_cast<cppu::OWeakObject*>(this));
+ sal_Int32 nPos = 0;
+ const OUString sTLName(sRange.getToken(0, ':', nPos));
+ const OUString sBRName(sRange.getToken(0, ':', nPos));
+ if(sTLName.isEmpty() || sBRName.isEmpty())
+ throw uno::RuntimeException();
+ SwRangeDescriptor aDesc;
+ aDesc.nTop = aDesc.nLeft = aDesc.nBottom = aDesc.nRight = -1;
+ SwXTextTable::GetCellPosition(sTLName, aDesc.nLeft, aDesc.nTop );
+ SwXTextTable::GetCellPosition(sBRName, aDesc.nRight, aDesc.nBottom );
+
+ // we should normalize the range now (e.g. A5:C1 will become A1:C5)
+ // since (depending on what is done later) it will be troublesome
+ // elsewhere when the cursor in the implementation does not
+ // point to the top-left and bottom-right cells
+ aDesc.Normalize();
+ return GetRangeByName(pFormat, pTable, sTLName, sBRName, aDesc);
+}
+
+uno::Sequence< uno::Sequence< uno::Any > > SAL_CALL SwXTextTable::getDataArray()
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<sheet::XCellRangeData> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ return xAllRange->getDataArray();
+}
+
+void SAL_CALL SwXTextTable::setDataArray(const uno::Sequence< uno::Sequence< uno::Any > >& rArray)
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<sheet::XCellRangeData> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ return xAllRange->setDataArray(rArray);
+}
+
+uno::Sequence< uno::Sequence< double > > SwXTextTable::getData()
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<chart::XChartDataArray> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
+ m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
+ return xAllRange->getData();
+}
+
+void SwXTextTable::setData(const uno::Sequence< uno::Sequence< double > >& rData)
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<chart::XChartDataArray> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
+ m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
+ xAllRange->setData(rData);
+ // this is rather inconsistent: setData on XTextTable sends events, but e.g. CellRanges do not
+ std::unique_lock aGuard2(m_pImpl->m_Mutex);
+ lcl_SendChartEvent(aGuard2, *this, m_pImpl->m_ChartListeners);
+}
+
+uno::Sequence<OUString> SwXTextTable::getRowDescriptions()
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<chart::XChartDataArray> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
+ m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
+ return xAllRange->getRowDescriptions();
+}
+
+void SwXTextTable::setRowDescriptions(const uno::Sequence<OUString>& rRowDesc)
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<chart::XChartDataArray> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
+ m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
+ xAllRange->setRowDescriptions(rRowDesc);
+}
+
+uno::Sequence<OUString> SwXTextTable::getColumnDescriptions()
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<chart::XChartDataArray> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
+ m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
+ return xAllRange->getColumnDescriptions();
+}
+
+void SwXTextTable::setColumnDescriptions(const uno::Sequence<OUString>& rColumnDesc)
+{
+ SolarMutexGuard aGuard;
+ std::pair<sal_uInt16, sal_uInt16> const RowsAndColumns(SwXTextTable::Impl::ThrowIfComplex(*this));
+ uno::Reference<chart::XChartDataArray> const xAllRange(
+ getCellRangeByPosition(0, 0, RowsAndColumns.second-1, RowsAndColumns.first-1),
+ uno::UNO_QUERY_THROW);
+ static_cast<SwXCellRange*>(xAllRange.get())->SetLabels(
+ m_pImpl->m_bFirstRowAsLabel, m_pImpl->m_bFirstColumnAsLabel);
+ return xAllRange->setColumnDescriptions(rColumnDesc);
+}
+
+void SAL_CALL SwXTextTable::addChartDataChangeEventListener(
+ const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_ChartListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXTextTable::removeChartDataChangeEventListener(
+ const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_ChartListeners.removeInterface(aGuard, xListener);
+}
+
+sal_Bool SwXTextTable::isNotANumber(double nNumber)
+{
+ // We use DBL_MIN because starcalc does (which uses it because chart
+ // wants it that way!)
+ return ( nNumber == DBL_MIN );
+}
+
+double SwXTextTable::getNotANumber()
+{
+ // We use DBL_MIN because starcalc does (which uses it because chart
+ // wants it that way!)
+ return DBL_MIN;
+}
+
+uno::Sequence< beans::PropertyValue > SwXTextTable::createSortDescriptor()
+{
+ SolarMutexGuard aGuard;
+
+ return SwUnoCursorHelper::CreateSortDescriptor(true);
+}
+
+void SwXTextTable::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
+{
+ SolarMutexGuard aGuard;
+ SwSortOptions aSortOpt;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!(pFormat &&
+ SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt)))
+ return;
+
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwSelBoxes aBoxes;
+ const SwTableSortBoxes& rTBoxes = pTable->GetTabSortBoxes();
+ for (size_t n = 0; n < rTBoxes.size(); ++n)
+ {
+ SwTableBox* pBox = rTBoxes[ n ];
+ aBoxes.insert( pBox );
+ }
+ UnoActionContext aContext( pFormat->GetDoc() );
+ pFormat->GetDoc()->SortTable(aBoxes, aSortOpt);
+}
+
+void SwXTextTable::autoFormat(const OUString& sAutoFormatName)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFormat), static_cast<cppu::OWeakObject*>(this));
+ SwTableAutoFormatTable aAutoFormatTable;
+ aAutoFormatTable.Load();
+ for (size_t i = aAutoFormatTable.size(); i;)
+ if( sAutoFormatName == aAutoFormatTable[ --i ].GetName() )
+ {
+ SwSelBoxes aBoxes;
+ const SwTableSortBoxes& rTBoxes = pTable->GetTabSortBoxes();
+ for (size_t n = 0; n < rTBoxes.size(); ++n)
+ {
+ SwTableBox* pBox = rTBoxes[ n ];
+ aBoxes.insert( pBox );
+ }
+ UnoActionContext aContext( pFormat->GetDoc() );
+ pFormat->GetDoc()->SetTableAutoFormat( aBoxes, aAutoFormatTable[i] );
+ break;
+ }
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXTextTable::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> xRef = m_pImpl->m_pPropSet->getPropertySetInfo();
+ return xRef;
+}
+
+void SwXTextTable::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(!aValue.hasValue())
+ throw lang::IllegalArgumentException();
+ const SfxItemPropertyMapEntry* pEntry =
+ m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if( !pEntry )
+ throw lang::IllegalArgumentException();
+ if(pFormat)
+ {
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if(0xBF == pEntry->nMemberId)
+ {
+ lcl_SetSpecialProperty(pFormat, pEntry, aValue);
+ }
+ else
+ {
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_TABLE_NAME :
+ {
+ OUString sName;
+ aValue >>= sName;
+ setName( sName );
+ }
+ break;
+
+ case FN_UNO_RANGE_ROW_LABEL:
+ {
+ bool bTmp = *o3tl::doAccess<bool>(aValue);
+ if (m_pImpl->m_bFirstRowAsLabel != bTmp)
+ {
+ std::unique_lock aGuard2(m_pImpl->m_Mutex);
+ lcl_SendChartEvent(aGuard2, *this, m_pImpl->m_ChartListeners);
+ m_pImpl->m_bFirstRowAsLabel = bTmp;
+ }
+ }
+ break;
+
+ case FN_UNO_RANGE_COL_LABEL:
+ {
+ bool bTmp = *o3tl::doAccess<bool>(aValue);
+ if (m_pImpl->m_bFirstColumnAsLabel != bTmp)
+ {
+ std::unique_lock aGuard2(m_pImpl->m_Mutex);
+ lcl_SendChartEvent(aGuard2, *this, m_pImpl->m_ChartListeners);
+ m_pImpl->m_bFirstColumnAsLabel = bTmp;
+ }
+ }
+ break;
+
+ case FN_UNO_TABLE_BORDER:
+ case FN_UNO_TABLE_BORDER2:
+ {
+ table::TableBorder oldBorder;
+ table::TableBorder2 aBorder;
+ SvxBorderLine aTopLine;
+ SvxBorderLine aBottomLine;
+ SvxBorderLine aLeftLine;
+ SvxBorderLine aRightLine;
+ SvxBorderLine aHoriLine;
+ SvxBorderLine aVertLine;
+ if (aValue >>= oldBorder)
+ {
+ aBorder.IsTopLineValid = oldBorder.IsTopLineValid;
+ aBorder.IsBottomLineValid = oldBorder.IsBottomLineValid;
+ aBorder.IsLeftLineValid = oldBorder.IsLeftLineValid;
+ aBorder.IsRightLineValid = oldBorder.IsRightLineValid;
+ aBorder.IsHorizontalLineValid = oldBorder.IsHorizontalLineValid;
+ aBorder.IsVerticalLineValid = oldBorder.IsVerticalLineValid;
+ aBorder.Distance = oldBorder.Distance;
+ aBorder.IsDistanceValid = oldBorder.IsDistanceValid;
+ lcl_LineToSvxLine(
+ oldBorder.TopLine, aTopLine);
+ lcl_LineToSvxLine(
+ oldBorder.BottomLine, aBottomLine);
+ lcl_LineToSvxLine(
+ oldBorder.LeftLine, aLeftLine);
+ lcl_LineToSvxLine(
+ oldBorder.RightLine, aRightLine);
+ lcl_LineToSvxLine(
+ oldBorder.HorizontalLine, aHoriLine);
+ lcl_LineToSvxLine(
+ oldBorder.VerticalLine, aVertLine);
+ }
+ else if (aValue >>= aBorder)
+ {
+ SvxBoxItem::LineToSvxLine(
+ aBorder.TopLine, aTopLine, true);
+ SvxBoxItem::LineToSvxLine(
+ aBorder.BottomLine, aBottomLine, true);
+ SvxBoxItem::LineToSvxLine(
+ aBorder.LeftLine, aLeftLine, true);
+ SvxBoxItem::LineToSvxLine(
+ aBorder.RightLine, aRightLine, true);
+ SvxBoxItem::LineToSvxLine(
+ aBorder.HorizontalLine, aHoriLine, true);
+ SvxBoxItem::LineToSvxLine(
+ aBorder.VerticalLine, aVertLine, true);
+ }
+ else
+ {
+ break; // something else
+ }
+ SwDoc* pDoc = pFormat->GetDoc();
+ if(!lcl_FormatTable(pFormat))
+ break;
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwTableLines &rLines = pTable->GetTabLines();
+
+ const SwTableBox* pTLBox = lcl_FindCornerTableBox(rLines, true);
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor to top left cell
+ auto pUnoCursor(pDoc->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ pUnoCursor->SetRemainInSection( false );
+
+ const SwTableBox* pBRBox = lcl_FindCornerTableBox(rLines, false);
+ pUnoCursor->SetMark();
+ pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ rCursor.MakeBoxSels();
+
+ SfxItemSetFixed<RES_BOX, RES_BOX,
+ SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>
+ aSet(pDoc->GetAttrPool());
+
+ SvxBoxItem aBox( RES_BOX );
+ SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER );
+
+ aBox.SetLine(aTopLine.isEmpty() ? nullptr : &aTopLine, SvxBoxItemLine::TOP);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, aBorder.IsTopLineValid);
+
+ aBox.SetLine(aBottomLine.isEmpty() ? nullptr : &aBottomLine, SvxBoxItemLine::BOTTOM);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::BOTTOM, aBorder.IsBottomLineValid);
+
+ aBox.SetLine(aLeftLine.isEmpty() ? nullptr : &aLeftLine, SvxBoxItemLine::LEFT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, aBorder.IsLeftLineValid);
+
+ aBox.SetLine(aRightLine.isEmpty() ? nullptr : &aRightLine, SvxBoxItemLine::RIGHT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::RIGHT, aBorder.IsRightLineValid);
+
+ aBoxInfo.SetLine(aHoriLine.isEmpty() ? nullptr : &aHoriLine, SvxBoxInfoItemLine::HORI);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI, aBorder.IsHorizontalLineValid);
+
+ aBoxInfo.SetLine(aVertLine.isEmpty() ? nullptr : &aVertLine, SvxBoxInfoItemLine::VERT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT, aBorder.IsVerticalLineValid);
+
+ aBox.SetAllDistances(o3tl::toTwips(aBorder.Distance, o3tl::Length::mm100));
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE, aBorder.IsDistanceValid);
+
+ aSet.Put(aBox);
+ aSet.Put(aBoxInfo);
+
+ pDoc->SetTabBorders(rCursor, aSet);
+ }
+ break;
+
+ case FN_UNO_TABLE_BORDER_DISTANCES:
+ {
+ table::TableBorderDistances aTableBorderDistances;
+ if( !(aValue >>= aTableBorderDistances) ||
+ (!aTableBorderDistances.IsLeftDistanceValid &&
+ !aTableBorderDistances.IsRightDistanceValid &&
+ !aTableBorderDistances.IsTopDistanceValid &&
+ !aTableBorderDistances.IsBottomDistanceValid ))
+ break;
+
+ const sal_uInt16 nLeftDistance = o3tl::toTwips(aTableBorderDistances.LeftDistance, o3tl::Length::mm100);
+ const sal_uInt16 nRightDistance = o3tl::toTwips(aTableBorderDistances.RightDistance, o3tl::Length::mm100);
+ const sal_uInt16 nTopDistance = o3tl::toTwips(aTableBorderDistances.TopDistance, o3tl::Length::mm100);
+ const sal_uInt16 nBottomDistance = o3tl::toTwips(aTableBorderDistances.BottomDistance, o3tl::Length::mm100);
+ SwDoc* pDoc = pFormat->GetDoc();
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwTableLines &rLines = pTable->GetTabLines();
+ pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
+ for(size_t i = 0; i < rLines.size(); ++i)
+ {
+ SwTableLine* pLine = rLines[i];
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for(size_t k = 0; k < rBoxes.size(); ++k)
+ {
+ SwTableBox* pBox = rBoxes[k];
+ const SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
+ const SvxBoxItem& rBox = pBoxFormat->GetBox();
+ if(
+ (aTableBorderDistances.IsLeftDistanceValid && nLeftDistance != rBox.GetDistance( SvxBoxItemLine::LEFT )) ||
+ (aTableBorderDistances.IsRightDistanceValid && nRightDistance != rBox.GetDistance( SvxBoxItemLine::RIGHT )) ||
+ (aTableBorderDistances.IsTopDistanceValid && nTopDistance != rBox.GetDistance( SvxBoxItemLine::TOP )) ||
+ (aTableBorderDistances.IsBottomDistanceValid && nBottomDistance != rBox.GetDistance( SvxBoxItemLine::BOTTOM )))
+ {
+ SvxBoxItem aSetBox( rBox );
+ SwFrameFormat* pSetBoxFormat = pBox->ClaimFrameFormat();
+ if( aTableBorderDistances.IsLeftDistanceValid )
+ aSetBox.SetDistance( nLeftDistance, SvxBoxItemLine::LEFT );
+ if( aTableBorderDistances.IsRightDistanceValid )
+ aSetBox.SetDistance( nRightDistance, SvxBoxItemLine::RIGHT );
+ if( aTableBorderDistances.IsTopDistanceValid )
+ aSetBox.SetDistance( nTopDistance, SvxBoxItemLine::TOP );
+ if( aTableBorderDistances.IsBottomDistanceValid )
+ aSetBox.SetDistance( nBottomDistance, SvxBoxItemLine::BOTTOM );
+ pDoc->SetAttr( aSetBox, *pSetBoxFormat );
+ }
+ }
+ }
+ pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ }
+ break;
+
+ case FN_UNO_TABLE_COLUMN_SEPARATORS:
+ {
+ UnoActionContext aContext(pFormat->GetDoc());
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ lcl_SetTableSeparators(aValue, pTable, pTable->GetTabLines()[0]->GetTabBoxes()[0], false, pFormat->GetDoc());
+ }
+ break;
+
+ case FN_UNO_TABLE_COLUMN_RELATIVE_SUM:/*_readonly_*/ break;
+
+ case FN_UNO_TABLE_TEMPLATE_NAME:
+ {
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ OUString sName;
+ if (!(aValue >>= sName))
+ break;
+ SwStyleNameMapper::FillUIName(sName, sName, SwGetPoolIdFromName::TabStyle);
+ pTable->SetTableStyleName(sName);
+ SwDoc* pDoc = pFormat->GetDoc();
+ if (SwFEShell* pFEShell = pDoc->GetDocShell()->GetFEShell())
+ pFEShell->UpdateTableStyleFormatting(pTable->GetTableNode());
+ }
+ break;
+
+ default:
+ {
+ SwAttrSet aSet(pFormat->GetAttrSet());
+ m_pImpl->m_pPropSet->setPropertyValue(*pEntry, aValue, aSet);
+ pFormat->GetDoc()->SetAttr(aSet, *pFormat);
+ }
+ }
+ }
+ }
+ else if (m_pImpl->IsDescriptor())
+ {
+ m_pImpl->m_pTableProps->SetProperty(pEntry->nWID, pEntry->nMemberId, aValue);
+ }
+ else
+ throw uno::RuntimeException();
+}
+
+uno::Any SwXTextTable::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ const SfxItemPropertyMapEntry* pEntry =
+ m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
+
+ if (!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if(pFormat)
+ {
+ if(0xBF == pEntry->nMemberId)
+ {
+ aRet = lcl_GetSpecialProperty(pFormat, pEntry );
+ }
+ else
+ {
+ switch(pEntry->nWID)
+ {
+ case FN_UNO_TABLE_NAME:
+ {
+ aRet <<= getName();
+ }
+ break;
+
+ case FN_UNO_ANCHOR_TYPES:
+ case FN_UNO_TEXT_WRAP:
+ case FN_UNO_ANCHOR_TYPE:
+ ::sw::GetDefaultTextContentValue(
+ aRet, u"", pEntry->nWID);
+ break;
+
+ case FN_UNO_RANGE_ROW_LABEL:
+ {
+ aRet <<= m_pImpl->m_bFirstRowAsLabel;
+ }
+ break;
+
+ case FN_UNO_RANGE_COL_LABEL:
+ aRet <<= m_pImpl->m_bFirstColumnAsLabel;
+ break;
+
+ case FN_UNO_TABLE_BORDER:
+ case FN_UNO_TABLE_BORDER2:
+ {
+ SwDoc* pDoc = pFormat->GetDoc();
+ // tables without layout (invisible header/footer?)
+ if(!lcl_FormatTable(pFormat))
+ break;
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwTableLines &rLines = pTable->GetTabLines();
+
+ const SwTableBox* pTLBox = lcl_FindCornerTableBox(rLines, true);
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor to top left cell
+ auto pUnoCursor(pDoc->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ pUnoCursor->SetRemainInSection( false );
+
+ const SwTableBox* pBRBox = lcl_FindCornerTableBox(rLines, false);
+ pUnoCursor->SetMark();
+ const SwStartNode* pLastNd = pBRBox->GetSttNd();
+ pUnoCursor->GetPoint()->nNode = *pLastNd;
+
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ rCursor.MakeBoxSels();
+
+ SfxItemSetFixed<RES_BOX, RES_BOX,
+ SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>
+ aSet(pDoc->GetAttrPool());
+ aSet.Put(SvxBoxInfoItem( SID_ATTR_BORDER_INNER ));
+ SwDoc::GetTabBorders(rCursor, aSet);
+ const SvxBoxInfoItem& rBoxInfoItem = aSet.Get(SID_ATTR_BORDER_INNER);
+ const SvxBoxItem& rBox = aSet.Get(RES_BOX);
+
+ if (FN_UNO_TABLE_BORDER == pEntry->nWID)
+ {
+ table::TableBorder aTableBorder;
+ aTableBorder.TopLine = SvxBoxItem::SvxLineToLine(rBox.GetTop(), true);
+ aTableBorder.IsTopLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP);
+ aTableBorder.BottomLine = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), true);
+ aTableBorder.IsBottomLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ aTableBorder.LeftLine = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), true);
+ aTableBorder.IsLeftLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ aTableBorder.RightLine = SvxBoxItem::SvxLineToLine(rBox.GetRight(), true);
+ aTableBorder.IsRightLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT );
+ aTableBorder.HorizontalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), true);
+ aTableBorder.IsHorizontalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI);
+ aTableBorder.VerticalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), true);
+ aTableBorder.IsVerticalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT);
+ aTableBorder.Distance = convertTwipToMm100(rBox.GetSmallestDistance());
+ aTableBorder.IsDistanceValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
+ aRet <<= aTableBorder;
+ }
+ else
+ {
+ table::TableBorder2 aTableBorder;
+ aTableBorder.TopLine = SvxBoxItem::SvxLineToLine(rBox.GetTop(), true);
+ aTableBorder.IsTopLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP);
+ aTableBorder.BottomLine = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), true);
+ aTableBorder.IsBottomLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ aTableBorder.LeftLine = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), true);
+ aTableBorder.IsLeftLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ aTableBorder.RightLine = SvxBoxItem::SvxLineToLine(rBox.GetRight(), true);
+ aTableBorder.IsRightLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT );
+ aTableBorder.HorizontalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), true);
+ aTableBorder.IsHorizontalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI);
+ aTableBorder.VerticalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), true);
+ aTableBorder.IsVerticalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT);
+ aTableBorder.Distance = convertTwipToMm100(rBox.GetSmallestDistance());
+ aTableBorder.IsDistanceValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
+ aRet <<= aTableBorder;
+ }
+ }
+ break;
+
+ case FN_UNO_TABLE_BORDER_DISTANCES :
+ {
+ table::TableBorderDistances aTableBorderDistances( 0, true, 0, true, 0, true, 0, true ) ;
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ const SwTableLines &rLines = pTable->GetTabLines();
+ bool bFirst = true;
+ sal_uInt16 nLeftDistance = 0;
+ sal_uInt16 nRightDistance = 0;
+ sal_uInt16 nTopDistance = 0;
+ sal_uInt16 nBottomDistance = 0;
+
+ for(size_t i = 0; i < rLines.size(); ++i)
+ {
+ const SwTableLine* pLine = rLines[i];
+ const SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ for(size_t k = 0; k < rBoxes.size(); ++k)
+ {
+ const SwTableBox* pBox = rBoxes[k];
+ SwFrameFormat* pBoxFormat = pBox->GetFrameFormat();
+ const SvxBoxItem& rBox = pBoxFormat->GetBox();
+ if( bFirst )
+ {
+ nLeftDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::LEFT ));
+ nRightDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::RIGHT ));
+ nTopDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::TOP ));
+ nBottomDistance = convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::BOTTOM ));
+ bFirst = false;
+ }
+ else
+ {
+ if( aTableBorderDistances.IsLeftDistanceValid &&
+ nLeftDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::LEFT )))
+ aTableBorderDistances.IsLeftDistanceValid = false;
+ if( aTableBorderDistances.IsRightDistanceValid &&
+ nRightDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::RIGHT )))
+ aTableBorderDistances.IsRightDistanceValid = false;
+ if( aTableBorderDistances.IsTopDistanceValid &&
+ nTopDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::TOP )))
+ aTableBorderDistances.IsTopDistanceValid = false;
+ if( aTableBorderDistances.IsBottomDistanceValid &&
+ nBottomDistance != convertTwipToMm100( rBox.GetDistance( SvxBoxItemLine::BOTTOM )))
+ aTableBorderDistances.IsBottomDistanceValid = false;
+ }
+
+ }
+ if( !aTableBorderDistances.IsLeftDistanceValid &&
+ !aTableBorderDistances.IsRightDistanceValid &&
+ !aTableBorderDistances.IsTopDistanceValid &&
+ !aTableBorderDistances.IsBottomDistanceValid )
+ break;
+ }
+ if( aTableBorderDistances.IsLeftDistanceValid)
+ aTableBorderDistances.LeftDistance = nLeftDistance;
+ if( aTableBorderDistances.IsRightDistanceValid)
+ aTableBorderDistances.RightDistance = nRightDistance;
+ if( aTableBorderDistances.IsTopDistanceValid)
+ aTableBorderDistances.TopDistance = nTopDistance;
+ if( aTableBorderDistances.IsBottomDistanceValid)
+ aTableBorderDistances.BottomDistance = nBottomDistance;
+
+ aRet <<= aTableBorderDistances;
+ }
+ break;
+
+ case FN_UNO_TABLE_COLUMN_SEPARATORS:
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ lcl_GetTableSeparators(aRet, pTable, pTable->GetTabLines()[0]->GetTabBoxes()[0], false);
+ }
+ break;
+
+ case FN_UNO_TABLE_COLUMN_RELATIVE_SUM:
+ aRet <<= sal_Int16(UNO_TABLE_COLUMN_SUM);
+ break;
+
+ case RES_ANCHOR:
+ // AnchorType is readonly and might be void (no return value)
+ break;
+
+ case FN_UNO_TEXT_SECTION:
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ SwTableNode* pTableNode = pTable->GetTableNode();
+ SwSectionNode* pSectionNode = pTableNode->FindSectionNode();
+ if(pSectionNode)
+ {
+ SwSection& rSect = pSectionNode->GetSection();
+ uno::Reference< text::XTextSection > xSect =
+ SwXTextSections::GetObject( *rSect.GetFormat() );
+ aRet <<= xSect;
+ }
+ }
+ break;
+
+ case FN_UNO_TABLE_TEMPLATE_NAME:
+ {
+ SwTable* pTable = SwTable::FindTable(pFormat);
+ OUString sName;
+ SwStyleNameMapper::FillProgName(pTable->GetTableStyleName(), sName, SwGetPoolIdFromName::TabStyle);
+ aRet <<= sName;
+ }
+ break;
+
+ default:
+ {
+ const SwAttrSet& rSet = pFormat->GetAttrSet();
+ m_pImpl->m_pPropSet->getPropertyValue(*pEntry, rSet, aRet);
+ }
+ }
+ }
+ }
+ else if (m_pImpl->IsDescriptor())
+ {
+ const uno::Any* pAny = nullptr;
+ if (!m_pImpl->m_pTableProps->GetProperty(pEntry->nWID, pEntry->nMemberId, pAny))
+ throw lang::IllegalArgumentException();
+ else if(pAny)
+ aRet = *pAny;
+ }
+ else
+ throw uno::RuntimeException();
+ return aRet;
+}
+
+void SwXTextTable::addPropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+void SwXTextTable::removePropertyChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+void SwXTextTable::addVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+void SwXTextTable::removeVetoableChangeListener(const OUString& /*rPropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*xListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+OUString SwXTextTable::getName()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if (!pFormat && !m_pImpl->IsDescriptor())
+ throw uno::RuntimeException();
+ if(pFormat)
+ {
+ return pFormat->GetName();
+ }
+ return m_pImpl->m_sTableName;
+}
+
+void SwXTextTable::setName(const OUString& rName)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if ((!pFormat && !m_pImpl->IsDescriptor()) ||
+ rName.isEmpty() ||
+ rName.indexOf('.')>=0 ||
+ rName.indexOf(' ')>=0 )
+ throw uno::RuntimeException();
+
+ if(pFormat)
+ {
+ const OUString aOldName( pFormat->GetName() );
+ const SwFrameFormats* pFrameFormats = pFormat->GetDoc()->GetTableFrameFormats();
+ for (size_t i = pFrameFormats->size(); i;)
+ {
+ const SwFrameFormat* pTmpFormat = (*pFrameFormats)[--i];
+ if( !pTmpFormat->IsDefault() &&
+ pTmpFormat->GetName() == rName &&
+ pFormat->GetDoc()->IsUsed( *pTmpFormat ))
+ {
+ throw uno::RuntimeException();
+ }
+ }
+
+ pFormat->SetName( rName );
+
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *pFormat->GetDoc()->GetNodes().GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwNode *const pNd = & aIdx.GetNode();
+ if ( pNd->IsOLENode() &&
+ aOldName == static_cast<const SwOLENode*>(pNd)->GetChartTableName() )
+ {
+ static_cast<SwOLENode*>(pNd)->SetChartTableName( rName );
+
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ //TL_CHART2: chart needs to be notified about name changes
+ pFormat->GetDoc()->UpdateCharts( pTable->GetFrameFormat()->GetName() );
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), + 1 );
+ }
+ pFormat->GetDoc()->getIDocumentState().SetModified();
+ }
+ else
+ m_pImpl->m_sTableName = rName;
+}
+
+sal_uInt16 SwXTextTable::Impl::GetRowCount()
+{
+ sal_uInt16 nRet = 0;
+ SwFrameFormat* pFormat = GetFrameFormat();
+ if(pFormat)
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ if(!pTable->IsTableComplex())
+ {
+ nRet = pTable->GetTabLines().size();
+ }
+ }
+ return nRet;
+}
+
+sal_uInt16 SwXTextTable::Impl::GetColumnCount()
+{
+ SwFrameFormat* pFormat = GetFrameFormat();
+ sal_uInt16 nRet = 0;
+ if(pFormat)
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ if(!pTable->IsTableComplex())
+ {
+ SwTableLines& rLines = pTable->GetTabLines();
+ SwTableLine* pLine = rLines.front();
+ nRet = pLine->GetTabBoxes().size();
+ }
+ }
+ return nRet;
+}
+
+void SwXTextTable::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pFrameFormat = nullptr;
+ EndListeningAll();
+ }
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if (xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ if(!m_pFrameFormat)
+ {
+ lang::EventObject const ev(xThis);
+ std::unique_lock aGuard(m_Mutex);
+ m_EventListeners.disposeAndClear(aGuard, ev);
+ m_ChartListeners.disposeAndClear(aGuard, ev);
+ }
+ else
+ {
+ std::unique_lock aGuard(m_Mutex);
+ lcl_SendChartEvent(aGuard, xThis, m_ChartListeners);
+ }
+ }
+}
+
+OUString SAL_CALL SwXTextTable::getImplementationName()
+ { return "SwXTextTable"; }
+
+sal_Bool SwXTextTable::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+uno::Sequence<OUString> SwXTextTable::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.document.LinkTarget",
+ "com.sun.star.text.TextTable",
+ "com.sun.star.text.TextContent",
+ "com.sun.star.text.TextSortable" };
+}
+
+
+class SwXCellRange::Impl
+ : public SvtListener
+{
+private:
+ SwFrameFormat* m_pFrameFormat;
+
+public:
+ uno::WeakReference<uno::XInterface> m_wThis;
+ std::mutex m_Mutex; // just for OInterfaceContainerHelper4
+ ::comphelper::OInterfaceContainerHelper4<chart::XChartDataChangeEventListener> m_ChartListeners;
+
+ sw::UnoCursorPointer m_pTableCursor;
+
+ SwRangeDescriptor m_RangeDescriptor;
+ const SfxItemPropertySet* m_pPropSet;
+
+ bool m_bFirstRowAsLabel;
+ bool m_bFirstColumnAsLabel;
+
+ Impl(sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat, SwRangeDescriptor const& rDesc)
+ : m_pFrameFormat(&rFrameFormat)
+ , m_pTableCursor(pCursor)
+ , m_RangeDescriptor(rDesc)
+ , m_pPropSet(aSwMapProvider.GetPropertySet(PROPERTY_MAP_TABLE_RANGE))
+ , m_bFirstRowAsLabel(false)
+ , m_bFirstColumnAsLabel(false)
+ {
+ StartListening(rFrameFormat.GetNotifier());
+ m_RangeDescriptor.Normalize();
+ }
+
+ SwFrameFormat* GetFrameFormat()
+ {
+ return m_pFrameFormat;
+ }
+
+ std::tuple<sal_uInt32, sal_uInt32, sal_uInt32, sal_uInt32> GetLabelCoordinates(bool bRow);
+
+ uno::Sequence<OUString> GetLabelDescriptions(SwXCellRange & rThis, bool bRow);
+
+ void SetLabelDescriptions(SwXCellRange & rThis,
+ const css::uno::Sequence<OUString>& rDesc, bool bRow);
+
+ sal_Int32 GetRowCount() const;
+ sal_Int32 GetColumnCount() const;
+
+ virtual void Notify(const SfxHint& ) override;
+
+};
+
+namespace
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwXCellRange::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXCellRangeUnoTunnelId;
+ return theSwXCellRangeUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SwXCellRange::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+
+OUString SwXCellRange::getImplementationName()
+ { return "SwXCellRange"; }
+
+sal_Bool SwXCellRange::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+uno::Sequence<OUString> SwXCellRange::getSupportedServiceNames()
+{
+ return {
+ "com.sun.star.text.CellRange",
+ "com.sun.star.style.CharacterProperties",
+ "com.sun.star.style.CharacterPropertiesAsian",
+ "com.sun.star.style.CharacterPropertiesComplex",
+ "com.sun.star.style.ParagraphProperties",
+ "com.sun.star.style.ParagraphPropertiesAsian",
+ "com.sun.star.style.ParagraphPropertiesComplex" };
+}
+
+SwXCellRange::SwXCellRange(sw::UnoCursorPointer const& pCursor,
+ SwFrameFormat& rFrameFormat, SwRangeDescriptor const & rDesc)
+ : m_pImpl(new Impl(pCursor, rFrameFormat, rDesc))
+{
+}
+
+SwXCellRange::~SwXCellRange()
+{
+}
+
+rtl::Reference<SwXCellRange> SwXCellRange::CreateXCellRange(
+ sw::UnoCursorPointer const& pCursor, SwFrameFormat& rFrameFormat,
+ SwRangeDescriptor const & rDesc)
+{
+ rtl::Reference<SwXCellRange> pCellRange(new SwXCellRange(pCursor, rFrameFormat, rDesc));
+ // need a permanent Reference to initialize m_wThis
+ pCellRange->m_pImpl->m_wThis = uno::Reference<table::XCellRange>(pCellRange);
+ return pCellRange;
+}
+
+void SwXCellRange::SetLabels(bool bFirstRowAsLabel, bool bFirstColumnAsLabel)
+{
+ m_pImpl->m_bFirstRowAsLabel = bFirstRowAsLabel;
+ m_pImpl->m_bFirstColumnAsLabel = bFirstColumnAsLabel;
+}
+
+std::vector< uno::Reference< table::XCell > > SwXCellRange::GetCells()
+{
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ const sal_Int32 nRowCount(m_pImpl->GetRowCount());
+ const sal_Int32 nColCount(m_pImpl->GetColumnCount());
+ std::vector< uno::Reference< table::XCell > > vResult;
+ vResult.reserve(static_cast<size_t>(nRowCount)*static_cast<size_t>(nColCount));
+ for(sal_Int32 nRow = 0; nRow < nRowCount; ++nRow)
+ for(sal_Int32 nCol = 0; nCol < nColCount; ++nCol)
+ vResult.emplace_back(lcl_CreateXCell(pFormat, m_pImpl->m_RangeDescriptor.nLeft + nCol, m_pImpl->m_RangeDescriptor.nTop + nRow));
+ return vResult;
+}
+
+uno::Reference<table::XCell> SAL_CALL
+SwXCellRange::getCellByPosition(sal_Int32 nColumn, sal_Int32 nRow)
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< table::XCell > aRet;
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ if(pFormat)
+ {
+ if(nColumn >= 0 && nRow >= 0 &&
+ m_pImpl->GetColumnCount() > nColumn && m_pImpl->GetRowCount() > nRow )
+ {
+ rtl::Reference<SwXCell> pXCell = lcl_CreateXCell(pFormat,
+ m_pImpl->m_RangeDescriptor.nLeft + nColumn,
+ m_pImpl->m_RangeDescriptor.nTop + nRow);
+ if(pXCell)
+ aRet = pXCell;
+ }
+ }
+ if(!aRet.is())
+ throw lang::IndexOutOfBoundsException();
+ return aRet;
+}
+
+uno::Reference<table::XCellRange> SAL_CALL
+SwXCellRange::getCellRangeByPosition(
+ sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom)
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< table::XCellRange > aRet;
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ if (pFormat && m_pImpl->GetColumnCount() > nRight
+ && m_pImpl->GetRowCount() > nBottom &&
+ nLeft <= nRight && nTop <= nBottom
+ && nLeft >= 0 && nRight >= 0 && nTop >= 0 && nBottom >= 0 )
+ {
+ SwTable* pTable = SwTable::FindTable( pFormat );
+ if(!pTable->IsTableComplex())
+ {
+ SwRangeDescriptor aNewDesc;
+ aNewDesc.nTop = nTop + m_pImpl->m_RangeDescriptor.nTop;
+ aNewDesc.nBottom = nBottom + m_pImpl->m_RangeDescriptor.nTop;
+ aNewDesc.nLeft = nLeft + m_pImpl->m_RangeDescriptor.nLeft;
+ aNewDesc.nRight = nRight + m_pImpl->m_RangeDescriptor.nLeft;
+ aNewDesc.Normalize();
+ const OUString sTLName = sw_GetCellName(aNewDesc.nLeft, aNewDesc.nTop);
+ const OUString sBRName = sw_GetCellName(aNewDesc.nRight, aNewDesc.nBottom);
+ const SwTableBox* pTLBox = pTable->GetTableBox( sTLName );
+ if(pTLBox)
+ {
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor in the upper-left cell of the range
+ auto pUnoCursor(pFormat->GetDoc()->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ pUnoCursor->SetRemainInSection( false );
+ const SwTableBox* pBRBox = pTable->GetTableBox( sBRName );
+ if(pBRBox)
+ {
+ pUnoCursor->SetMark();
+ pUnoCursor->GetPoint()->nNode = *pBRBox->GetSttNd();
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ rCursor.MakeBoxSels();
+ // pUnoCursor will be provided and will not be deleted
+ aRet = SwXCellRange::CreateXCellRange(pUnoCursor, *pFormat, aNewDesc).get();
+ }
+ }
+ }
+ }
+ if(!aRet.is())
+ throw lang::IndexOutOfBoundsException();
+ return aRet;
+}
+
+uno::Reference<table::XCellRange> SAL_CALL
+SwXCellRange::getCellRangeByName(const OUString& rRange)
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nPos = 0;
+ const OUString sTLName(rRange.getToken(0, ':', nPos));
+ const OUString sBRName(rRange.getToken(0, ':', nPos));
+ if(sTLName.isEmpty() || sBRName.isEmpty())
+ throw uno::RuntimeException();
+ SwRangeDescriptor aDesc;
+ aDesc.nTop = aDesc.nLeft = aDesc.nBottom = aDesc.nRight = -1;
+ SwXTextTable::GetCellPosition( sTLName, aDesc.nLeft, aDesc.nTop );
+ SwXTextTable::GetCellPosition( sBRName, aDesc.nRight, aDesc.nBottom );
+ aDesc.Normalize();
+ return getCellRangeByPosition(
+ aDesc.nLeft - m_pImpl->m_RangeDescriptor.nLeft,
+ aDesc.nTop - m_pImpl->m_RangeDescriptor.nTop,
+ aDesc.nRight - m_pImpl->m_RangeDescriptor.nLeft,
+ aDesc.nBottom - m_pImpl->m_RangeDescriptor.nTop);
+}
+
+uno::Reference< beans::XPropertySetInfo > SwXCellRange::getPropertySetInfo()
+{
+ static uno::Reference<beans::XPropertySetInfo> xRef = m_pImpl->m_pPropSet->getPropertySetInfo();
+ return xRef;
+}
+
+void SAL_CALL
+SwXCellRange::setPropertyValue(const OUString& rPropertyName, const uno::Any& aValue)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ if(!pFormat)
+ return;
+
+ const SfxItemPropertyMapEntry *const pEntry =
+ m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ if ( pEntry->nFlags & beans::PropertyAttribute::READONLY)
+ throw beans::PropertyVetoException("Property is read-only: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ SwDoc& rDoc = m_pImpl->m_pTableCursor->GetDoc();
+ SwUnoTableCursor& rCursor(dynamic_cast<SwUnoTableCursor&>(*m_pImpl->m_pTableCursor));
+ {
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ }
+ rCursor.MakeBoxSels();
+ switch(pEntry->nWID )
+ {
+ case FN_UNO_TABLE_CELL_BACKGROUND:
+ {
+ std::unique_ptr<SfxPoolItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND));
+ SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aBrush);
+ aBrush->PutValue(aValue, pEntry->nMemberId);
+ rDoc.SetBoxAttr(*m_pImpl->m_pTableCursor, *aBrush);
+
+ }
+ break;
+ case RES_BOX :
+ {
+ SfxItemSetFixed<RES_BOX, RES_BOX,
+ SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>
+ aSet(rDoc.GetAttrPool());
+ SvxBoxInfoItem aBoxInfo( SID_ATTR_BORDER_INNER );
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::ALL, false);
+ SvxBoxInfoItemValidFlags nValid = SvxBoxInfoItemValidFlags::NONE;
+ switch(pEntry->nMemberId & ~CONVERT_TWIPS)
+ {
+ case LEFT_BORDER : nValid = SvxBoxInfoItemValidFlags::LEFT; break;
+ case RIGHT_BORDER: nValid = SvxBoxInfoItemValidFlags::RIGHT; break;
+ case TOP_BORDER : nValid = SvxBoxInfoItemValidFlags::TOP; break;
+ case BOTTOM_BORDER: nValid = SvxBoxInfoItemValidFlags::BOTTOM; break;
+ case LEFT_BORDER_DISTANCE :
+ case RIGHT_BORDER_DISTANCE:
+ case TOP_BORDER_DISTANCE :
+ case BOTTOM_BORDER_DISTANCE:
+ nValid = SvxBoxInfoItemValidFlags::DISTANCE;
+ break;
+ }
+ aBoxInfo.SetValid(nValid);
+
+ aSet.Put(aBoxInfo);
+ SwDoc::GetTabBorders(rCursor, aSet);
+
+ aSet.Put(aBoxInfo);
+ SvxBoxItem aBoxItem(aSet.Get(RES_BOX));
+ static_cast<SfxPoolItem&>(aBoxItem).PutValue(aValue, pEntry->nMemberId);
+ aSet.Put(aBoxItem);
+ rDoc.SetTabBorders(*m_pImpl->m_pTableCursor, aSet);
+ }
+ break;
+ case RES_BOXATR_FORMAT:
+ {
+ SfxUInt32Item aNumberFormat(RES_BOXATR_FORMAT);
+ static_cast<SfxPoolItem&>(aNumberFormat).PutValue(aValue, 0);
+ rDoc.SetBoxAttr(rCursor, aNumberFormat);
+ }
+ break;
+ case FN_UNO_RANGE_ROW_LABEL:
+ {
+ bool bTmp = *o3tl::doAccess<bool>(aValue);
+ if (m_pImpl->m_bFirstRowAsLabel != bTmp)
+ {
+ std::unique_lock aGuard2(m_pImpl->m_Mutex);
+ lcl_SendChartEvent(aGuard2, *this, m_pImpl->m_ChartListeners);
+ m_pImpl->m_bFirstRowAsLabel = bTmp;
+ }
+ }
+ break;
+ case FN_UNO_RANGE_COL_LABEL:
+ {
+ bool bTmp = *o3tl::doAccess<bool>(aValue);
+ if (m_pImpl->m_bFirstColumnAsLabel != bTmp)
+ {
+ std::unique_lock aGuard2(m_pImpl->m_Mutex);
+ lcl_SendChartEvent(aGuard2, *this, m_pImpl->m_ChartListeners);
+ m_pImpl->m_bFirstColumnAsLabel = bTmp;
+ }
+ }
+ break;
+ case RES_VERT_ORIENT:
+ {
+ sal_Int16 nAlign = -1;
+ aValue >>= nAlign;
+ if( nAlign >= text::VertOrientation::NONE && nAlign <= text::VertOrientation::BOTTOM)
+ rDoc.SetBoxAlign( rCursor, nAlign );
+ }
+ break;
+ default:
+ {
+ SfxItemSet aItemSet( rDoc.GetAttrPool(), pEntry->nWID, pEntry->nWID );
+ SwUnoCursorHelper::GetCursorAttr(rCursor.GetSelRing(),
+ aItemSet);
+
+ if (!SwUnoCursorHelper::SetCursorPropertyValue(
+ *pEntry, aValue, rCursor.GetSelRing(), aItemSet))
+ {
+ m_pImpl->m_pPropSet->setPropertyValue(*pEntry, aValue, aItemSet);
+ }
+ SwUnoCursorHelper::SetCursorAttr(rCursor.GetSelRing(),
+ aItemSet, SetAttrMode::DEFAULT, true);
+ }
+ }
+}
+
+uno::Any SAL_CALL SwXCellRange::getPropertyValue(const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+ uno::Any aRet;
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ if(pFormat)
+ {
+ const SfxItemPropertyMapEntry *const pEntry =
+ m_pImpl->m_pPropSet->getPropertyMap().getByName(rPropertyName);
+ if(!pEntry)
+ throw beans::UnknownPropertyException("Unknown property: " + rPropertyName, static_cast < cppu::OWeakObject * > ( this ) );
+
+ switch(pEntry->nWID )
+ {
+ case FN_UNO_TABLE_CELL_BACKGROUND:
+ {
+ std::unique_ptr<SfxPoolItem> aBrush(std::make_unique<SvxBrushItem>(RES_BACKGROUND));
+ if (SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aBrush))
+ aBrush->QueryValue(aRet, pEntry->nMemberId);
+
+ }
+ break;
+ case RES_BOX :
+ {
+ SwDoc& rDoc = m_pImpl->m_pTableCursor->GetDoc();
+ SfxItemSetFixed<RES_BOX, RES_BOX,
+ SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER>
+ aSet(rDoc.GetAttrPool());
+ aSet.Put(SvxBoxInfoItem( SID_ATTR_BORDER_INNER ));
+ SwDoc::GetTabBorders(*m_pImpl->m_pTableCursor, aSet);
+ const SvxBoxItem& rBoxItem = aSet.Get(RES_BOX);
+ rBoxItem.QueryValue(aRet, pEntry->nMemberId);
+ }
+ break;
+ case RES_BOXATR_FORMAT:
+ OSL_FAIL("not implemented");
+ break;
+ case FN_UNO_PARA_STYLE:
+ {
+ SwFormatColl *const pTmpFormat =
+ SwUnoCursorHelper::GetCurTextFormatColl(*m_pImpl->m_pTableCursor, false);
+ OUString sRet;
+ if (pTmpFormat)
+ sRet = pTmpFormat->GetName();
+ aRet <<= sRet;
+ }
+ break;
+ case FN_UNO_RANGE_ROW_LABEL:
+ aRet <<= m_pImpl->m_bFirstRowAsLabel;
+ break;
+ case FN_UNO_RANGE_COL_LABEL:
+ aRet <<= m_pImpl->m_bFirstColumnAsLabel;
+ break;
+ case RES_VERT_ORIENT:
+ {
+ std::unique_ptr<SfxPoolItem> aVertOrient(
+ std::make_unique<SwFormatVertOrient>(RES_VERT_ORIENT));
+ if (SwDoc::GetBoxAttr(*m_pImpl->m_pTableCursor, aVertOrient))
+ {
+ aVertOrient->QueryValue( aRet, pEntry->nMemberId );
+ }
+ }
+ break;
+ default:
+ {
+ SfxItemSetFixed<
+ RES_CHRATR_BEGIN, RES_FRMATR_END - 1,
+ RES_UNKNOWNATR_CONTAINER,
+ RES_UNKNOWNATR_CONTAINER>
+ aSet(m_pImpl->m_pTableCursor->GetDoc().GetAttrPool());
+ // first look at the attributes of the cursor
+ SwUnoTableCursor *const pCursor =
+ dynamic_cast<SwUnoTableCursor*>(&(*m_pImpl->m_pTableCursor));
+ SwUnoCursorHelper::GetCursorAttr(pCursor->GetSelRing(), aSet);
+ m_pImpl->m_pPropSet->getPropertyValue(*pEntry, aSet, aRet);
+ }
+ }
+
+ }
+ return aRet;
+}
+
+void SwXCellRange::addPropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+void SwXCellRange::removePropertyChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XPropertyChangeListener > & /*aListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+void SwXCellRange::addVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+void SwXCellRange::removeVetoableChangeListener(const OUString& /*PropertyName*/, const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+///@see SwXCellRange::getData
+uno::Sequence<uno::Sequence<uno::Any>> SAL_CALL SwXCellRange::getDataArray()
+{
+ SolarMutexGuard aGuard;
+ const sal_Int32 nRowCount = m_pImpl->GetRowCount();
+ const sal_Int32 nColCount = m_pImpl->GetColumnCount();
+ if(!nRowCount || !nColCount)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ lcl_EnsureCoreConnected(m_pImpl->GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ uno::Sequence< uno::Sequence< uno::Any > > aRowSeq(nRowCount);
+ auto vCells(GetCells());
+ auto pCurrentCell(vCells.begin());
+ for(auto& rRow : asNonConstRange(aRowSeq))
+ {
+ rRow = uno::Sequence< uno::Any >(nColCount);
+ for(auto& rCellAny : asNonConstRange(rRow))
+ {
+ auto pCell(static_cast<SwXCell*>(pCurrentCell->get()));
+ if(!pCell)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ rCellAny = pCell->GetAny();
+ ++pCurrentCell;
+ }
+ }
+ return aRowSeq;
+}
+
+///@see SwXCellRange::setData
+void SAL_CALL SwXCellRange::setDataArray(const uno::Sequence< uno::Sequence< uno::Any > >& rArray)
+{
+ SolarMutexGuard aGuard;
+ const sal_Int32 nRowCount = m_pImpl->GetRowCount();
+ const sal_Int32 nColCount = m_pImpl->GetColumnCount();
+ if(!nRowCount || !nColCount)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ if(!pFormat)
+ return;
+ if(rArray.getLength() != nRowCount)
+ throw uno::RuntimeException("Row count mismatch. expected: " + OUString::number(nRowCount) + " got: " + OUString::number(rArray.getLength()), static_cast<cppu::OWeakObject*>(this));
+ auto vCells(GetCells());
+ auto pCurrentCell(vCells.begin());
+ for(const auto& rColSeq : rArray)
+ {
+ if(rColSeq.getLength() != nColCount)
+ throw uno::RuntimeException("Column count mismatch. expected: " + OUString::number(nColCount) + " got: " + OUString::number(rColSeq.getLength()), static_cast<cppu::OWeakObject*>(this));
+ for(const auto& aValue : rColSeq)
+ {
+ auto pCell(static_cast<SwXCell*>(pCurrentCell->get()));
+ if(!pCell || !pCell->GetTableBox())
+ throw uno::RuntimeException("Box for cell missing", static_cast<cppu::OWeakObject*>(this));
+ if(aValue.isExtractableTo(cppu::UnoType<OUString>::get()))
+ sw_setString(*pCell, aValue.get<OUString>());
+ else if(aValue.isExtractableTo(cppu::UnoType<double>::get()))
+ sw_setValue(*pCell, aValue.get<double>());
+ else
+ sw_setString(*pCell, OUString(), true);
+ ++pCurrentCell;
+ }
+ }
+}
+
+uno::Sequence<uno::Sequence<double>> SAL_CALL
+SwXCellRange::getData()
+{
+ SolarMutexGuard aGuard;
+ const sal_Int32 nRowCount = m_pImpl->GetRowCount();
+ const sal_Int32 nColCount = m_pImpl->GetColumnCount();
+ if(!nRowCount || !nColCount)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ if (m_pImpl->m_bFirstColumnAsLabel || m_pImpl->m_bFirstRowAsLabel)
+ {
+ uno::Reference<chart::XChartDataArray> const xDataRange(
+ getCellRangeByPosition((m_pImpl->m_bFirstColumnAsLabel) ? 1 : 0,
+ (m_pImpl->m_bFirstRowAsLabel) ? 1 : 0,
+ nColCount-1, nRowCount-1), uno::UNO_QUERY_THROW);
+ return xDataRange->getData();
+ }
+ uno::Sequence< uno::Sequence< double > > vRows(nRowCount);
+ auto vCells(GetCells());
+ auto pCurrentCell(vCells.begin());
+ for(auto& rRow : asNonConstRange(vRows))
+ {
+ rRow = uno::Sequence<double>(nColCount);
+ for(auto& rValue : asNonConstRange(rRow))
+ {
+ if(!(*pCurrentCell))
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ rValue = (*pCurrentCell)->getValue();
+ ++pCurrentCell;
+ }
+ }
+ return vRows;
+}
+
+void SAL_CALL
+SwXCellRange::setData(const uno::Sequence< uno::Sequence<double> >& rData)
+{
+ SolarMutexGuard aGuard;
+ const sal_Int32 nRowCount = m_pImpl->GetRowCount();
+ const sal_Int32 nColCount = m_pImpl->GetColumnCount();
+ if(!nRowCount || !nColCount)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ if (m_pImpl->m_bFirstColumnAsLabel || m_pImpl->m_bFirstRowAsLabel)
+ {
+ uno::Reference<chart::XChartDataArray> const xDataRange(
+ getCellRangeByPosition((m_pImpl->m_bFirstColumnAsLabel) ? 1 : 0,
+ (m_pImpl->m_bFirstRowAsLabel) ? 1 : 0,
+ nColCount-1, nRowCount-1), uno::UNO_QUERY_THROW);
+ return xDataRange->setData(rData);
+ }
+ lcl_EnsureCoreConnected(m_pImpl->GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ if(rData.getLength() != nRowCount)
+ throw uno::RuntimeException("Row count mismatch. expected: " + OUString::number(nRowCount) + " got: " + OUString::number(rData.getLength()), static_cast<cppu::OWeakObject*>(this));
+ auto vCells(GetCells());
+ auto pCurrentCell(vCells.begin());
+ for(const auto& rRow : rData)
+ {
+ if(rRow.getLength() != nColCount)
+ throw uno::RuntimeException("Column count mismatch. expected: " + OUString::number(nColCount) + " got: " + OUString::number(rRow.getLength()), static_cast<cppu::OWeakObject*>(this));
+ for(const auto& rValue : rRow)
+ {
+ uno::Reference<table::XCell>(*pCurrentCell, uno::UNO_SET_THROW)->setValue(rValue);
+ ++pCurrentCell;
+ }
+ }
+}
+
+std::tuple<sal_uInt32, sal_uInt32, sal_uInt32, sal_uInt32>
+SwXCellRange::Impl::GetLabelCoordinates(bool bRow)
+{
+ sal_uInt32 nLeft, nTop, nRight, nBottom;
+ nLeft = nTop = nRight = nBottom = 0;
+ if(bRow)
+ {
+ nTop = m_bFirstRowAsLabel ? 1 : 0;
+ nBottom = GetRowCount() - 1;
+ }
+ else
+ {
+ nLeft = m_bFirstColumnAsLabel ? 1 : 0;
+ nRight = GetColumnCount() - 1;
+ }
+ return std::make_tuple(nLeft, nTop, nRight, nBottom);
+}
+
+uno::Sequence<OUString>
+SwXCellRange::Impl::GetLabelDescriptions(SwXCellRange & rThis, bool bRow)
+{
+ SolarMutexGuard aGuard;
+ sal_uInt32 nLeft, nTop, nRight, nBottom;
+ std::tie(nLeft, nTop, nRight, nBottom) = GetLabelCoordinates(bRow);
+ if(!nRight && !nBottom)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(&rThis));
+ lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(&rThis));
+ if (!(bRow ? m_bFirstColumnAsLabel : m_bFirstRowAsLabel))
+ return {}; // without labels we have no descriptions
+ auto xLabelRange(rThis.getCellRangeByPosition(nLeft, nTop, nRight, nBottom));
+ auto vCells(static_cast<SwXCellRange*>(xLabelRange.get())->GetCells());
+ uno::Sequence<OUString> vResult(vCells.size());
+ std::transform(vCells.begin(), vCells.end(), vResult.getArray(),
+ [](uno::Reference<table::XCell> xCell) -> OUString { return uno::Reference<text::XText>(xCell, uno::UNO_QUERY_THROW)->getString(); });
+ return vResult;
+}
+
+uno::Sequence<OUString> SAL_CALL SwXCellRange::getRowDescriptions()
+{
+ return m_pImpl->GetLabelDescriptions(*this, true);
+}
+
+uno::Sequence<OUString> SAL_CALL SwXCellRange::getColumnDescriptions()
+{
+ return m_pImpl->GetLabelDescriptions(*this, false);
+}
+
+void SwXCellRange::Impl::SetLabelDescriptions(SwXCellRange & rThis,
+ const uno::Sequence<OUString>& rDesc, bool bRow)
+{
+ SolarMutexGuard aGuard;
+ lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(&rThis));
+ if (!(bRow ? m_bFirstColumnAsLabel : m_bFirstRowAsLabel))
+ return; // if there are no labels we cannot set descriptions
+ sal_uInt32 nLeft, nTop, nRight, nBottom;
+ std::tie(nLeft, nTop, nRight, nBottom) = GetLabelCoordinates(bRow);
+ if(!nRight && !nBottom)
+ throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(&rThis));
+ auto xLabelRange(rThis.getCellRangeByPosition(nLeft, nTop, nRight, nBottom));
+ if (!xLabelRange.is())
+ throw uno::RuntimeException("Missing Cell Range", static_cast<cppu::OWeakObject*>(&rThis));
+ auto vCells(static_cast<SwXCellRange*>(xLabelRange.get())->GetCells());
+ if (sal::static_int_cast<sal_uInt32>(rDesc.getLength()) != vCells.size())
+ throw uno::RuntimeException("Too few or too many descriptions", static_cast<cppu::OWeakObject*>(&rThis));
+ auto pDescIterator(rDesc.begin());
+ for(auto& xCell : vCells)
+ uno::Reference<text::XText>(xCell, uno::UNO_QUERY_THROW)->setString(*pDescIterator++);
+}
+
+void SAL_CALL SwXCellRange::setRowDescriptions(
+ const uno::Sequence<OUString>& rRowDesc)
+{
+ m_pImpl->SetLabelDescriptions(*this, rRowDesc, true);
+}
+
+void SAL_CALL SwXCellRange::setColumnDescriptions(
+ const uno::Sequence<OUString>& rColumnDesc)
+{
+ m_pImpl->SetLabelDescriptions(*this, rColumnDesc, false);
+}
+
+void SAL_CALL SwXCellRange::addChartDataChangeEventListener(
+ const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_ChartListeners.addInterface(aGuard, xListener);
+}
+
+void SAL_CALL SwXCellRange::removeChartDataChangeEventListener(
+ const uno::Reference<chart::XChartDataChangeEventListener> & xListener)
+{
+ // no need to lock here as m_pImpl is const and container threadsafe
+ std::unique_lock aGuard(m_pImpl->m_Mutex);
+ m_pImpl->m_ChartListeners.removeInterface(aGuard, xListener);
+}
+
+sal_Bool SwXCellRange::isNotANumber(double /*fNumber*/)
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+double SwXCellRange::getNotANumber()
+ { throw uno::RuntimeException("Not implemented", static_cast<cppu::OWeakObject*>(this)); }
+
+uno::Sequence< beans::PropertyValue > SwXCellRange::createSortDescriptor()
+{
+ SolarMutexGuard aGuard;
+ return SwUnoCursorHelper::CreateSortDescriptor(true);
+}
+
+void SAL_CALL SwXCellRange::sort(const uno::Sequence< beans::PropertyValue >& rDescriptor)
+{
+ SolarMutexGuard aGuard;
+ SwSortOptions aSortOpt;
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ if(pFormat && SwUnoCursorHelper::ConvertSortProperties(rDescriptor, aSortOpt))
+ {
+ SwUnoTableCursor& rTableCursor = dynamic_cast<SwUnoTableCursor&>(*m_pImpl->m_pTableCursor);
+ rTableCursor.MakeBoxSels();
+ UnoActionContext aContext(pFormat->GetDoc());
+ pFormat->GetDoc()->SortTable(rTableCursor.GetSelectedBoxes(), aSortOpt);
+ }
+}
+
+sal_Int32 SwXCellRange::Impl::GetColumnCount() const
+{
+ return m_RangeDescriptor.nRight - m_RangeDescriptor.nLeft + 1;
+}
+
+sal_Int32 SwXCellRange::Impl::GetRowCount() const
+{
+ return m_RangeDescriptor.nBottom - m_RangeDescriptor.nTop + 1;
+}
+
+const SwUnoCursor* SwXCellRange::GetTableCursor() const
+{
+ SwFrameFormat *const pFormat = m_pImpl->GetFrameFormat();
+ return pFormat ? &(*m_pImpl->m_pTableCursor) : nullptr;
+}
+
+void SwXCellRange::Impl::Notify( const SfxHint& rHint )
+{
+ uno::Reference<uno::XInterface> const xThis(m_wThis);
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pFrameFormat = nullptr;
+ m_pTableCursor.reset(nullptr);
+ }
+ if (xThis.is())
+ { // fdo#72695: if UNO object is already dead, don't revive it with event
+ if(m_pFrameFormat)
+ {
+ std::unique_lock aGuard(m_Mutex);
+ lcl_SendChartEvent(aGuard, xThis, m_ChartListeners);
+ }
+ else
+ {
+ std::unique_lock aGuard(m_Mutex);
+ m_ChartListeners.disposeAndClear(aGuard, lang::EventObject(xThis));
+ }
+ }
+}
+
+class SwXTableRows::Impl : public SvtListener
+{
+private:
+ SwFrameFormat* m_pFrameFormat;
+
+public:
+ explicit Impl(SwFrameFormat& rFrameFormat) : m_pFrameFormat(&rFrameFormat)
+ {
+ StartListening(rFrameFormat.GetNotifier());
+ }
+ SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
+ virtual void Notify(const SfxHint&) override;
+};
+
+// SwXTableRows
+
+OUString SwXTableRows::getImplementationName()
+ { return "SwXTableRows"; }
+
+sal_Bool SwXTableRows::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+uno::Sequence< OUString > SwXTableRows::getSupportedServiceNames()
+ { return { "com.sun.star.text.TableRows" }; }
+
+
+SwXTableRows::SwXTableRows(SwFrameFormat& rFrameFormat) :
+ m_pImpl(new SwXTableRows::Impl(rFrameFormat))
+{ }
+
+SwXTableRows::~SwXTableRows()
+{ }
+
+SwFrameFormat* SwXTableRows::GetFrameFormat()
+{
+ return m_pImpl->GetFrameFormat();
+}
+
+sal_Int32 SwXTableRows::getCount()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFrameFormat = GetFrameFormat();
+ if(!pFrameFormat)
+ throw uno::RuntimeException();
+ SwTable* pTable = SwTable::FindTable(pFrameFormat);
+ return pTable->GetTabLines().size();
+}
+
+///@see SwXCell::CreateXCell (TODO: seems to be copy and paste programming here)
+uno::Any SwXTableRows::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ if(nIndex < 0)
+ throw lang::IndexOutOfBoundsException();
+ SwTable* pTable = SwTable::FindTable( pFrameFormat );
+ if(o3tl::make_unsigned(nIndex) >= pTable->GetTabLines().size())
+ throw lang::IndexOutOfBoundsException();
+ SwTableLine* pLine = pTable->GetTabLines()[nIndex];
+ FindUnoInstanceHint<SwTableLine,SwXTextTableRow> aHint{pLine};
+ pFrameFormat->GetNotifier().Broadcast(aHint);
+ if(!aHint.m_pResult)
+ aHint.m_pResult = new SwXTextTableRow(pFrameFormat, pLine);
+ uno::Reference<beans::XPropertySet> xRet = static_cast<beans::XPropertySet*>(aHint.m_pResult.get());
+ return uno::Any(xRet);
+}
+
+uno::Type SAL_CALL SwXTableRows::getElementType()
+{
+ return cppu::UnoType<beans::XPropertySet>::get();
+}
+
+sal_Bool SwXTableRows::hasElements()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFrameFormat = GetFrameFormat();
+ if(!pFrameFormat)
+ throw uno::RuntimeException();
+ // a table always has rows
+ return true;
+}
+
+void SwXTableRows::insertByIndex(sal_Int32 nIndex, sal_Int32 nCount)
+{
+ SolarMutexGuard aGuard;
+ if (nCount == 0)
+ return;
+ SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
+ const size_t nRowCount = pTable->GetTabLines().size();
+ if (nCount <= 0 || 0 > nIndex || o3tl::make_unsigned(nIndex) > nRowCount)
+ throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
+ const OUString sTLName = sw_GetCellName(0, nIndex);
+ const SwTableBox* pTLBox = pTable->GetTableBox(sTLName);
+ bool bAppend = false;
+ if(!pTLBox)
+ {
+ bAppend = true;
+ // to append at the end the cursor must be in the last line
+ SwTableLines& rLines = pTable->GetTabLines();
+ SwTableLine* pLine = rLines.back();
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ pTLBox = rBoxes.front();
+ }
+ if(!pTLBox)
+ throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor to the upper-left cell of the range
+ UnoActionContext aAction(pFrameFormat->GetDoc());
+ std::shared_ptr<SwUnoTableCursor> const pUnoCursor(
+ std::dynamic_pointer_cast<SwUnoTableCursor>(
+ pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true)));
+ pUnoCursor->Move( fnMoveForward, GoInNode );
+ {
+ // remove actions - TODO: why?
+ UnoActionRemoveContext aRemoveContext(&pUnoCursor->GetDoc());
+ }
+ pFrameFormat->GetDoc()->InsertRow(*pUnoCursor, o3tl::narrowing<sal_uInt16>(nCount), bAppend);
+}
+
+void SwXTableRows::removeByIndex(sal_Int32 nIndex, sal_Int32 nCount)
+{
+ SolarMutexGuard aGuard;
+ if (nCount == 0)
+ return;
+ SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ if(nIndex < 0 || nCount <=0 )
+ throw uno::RuntimeException();
+ SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
+ OUString sTLName = sw_GetCellName(0, nIndex);
+ const SwTableBox* pTLBox = pTable->GetTableBox(sTLName);
+ if(!pTLBox)
+ throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor to the upper-left cell of the range
+ auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ pUnoCursor->SetRemainInSection( false );
+ const OUString sBLName = sw_GetCellName(0, nIndex + nCount - 1);
+ const SwTableBox* pBLBox = pTable->GetTableBox( sBLName );
+ if(!pBLBox)
+ throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
+ pUnoCursor->SetMark();
+ pUnoCursor->GetPoint()->nNode = *pBLBox->GetSttNd();
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ {
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ }
+ rCursor.MakeBoxSels();
+ { // these braces are important
+ UnoActionContext aAction(pFrameFormat->GetDoc());
+ pFrameFormat->GetDoc()->DeleteRow(*pUnoCursor);
+ pUnoCursor.reset();
+ }
+ {
+ // invalidate all actions - TODO: why?
+ UnoActionRemoveContext aRemoveContext(pFrameFormat->GetDoc());
+ }
+}
+
+void SwXTableRows::Impl::Notify( const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pFrameFormat = nullptr;
+}
+
+// SwXTableColumns
+
+class SwXTableColumns::Impl : public SvtListener
+{
+ SwFrameFormat* m_pFrameFormat;
+ public:
+ explicit Impl(SwFrameFormat& rFrameFormat) : m_pFrameFormat(&rFrameFormat)
+ {
+ StartListening(rFrameFormat.GetNotifier());
+ }
+ SwFrameFormat* GetFrameFormat() { return m_pFrameFormat; }
+ virtual void Notify(const SfxHint&) override;
+};
+
+OUString SwXTableColumns::getImplementationName()
+ { return "SwXTableColumns"; }
+
+sal_Bool SwXTableColumns::supportsService(const OUString& rServiceName)
+ { return cppu::supportsService(this, rServiceName); }
+
+uno::Sequence< OUString > SwXTableColumns::getSupportedServiceNames()
+ { return { "com.sun.star.text.TableColumns"}; }
+
+
+SwXTableColumns::SwXTableColumns(SwFrameFormat& rFrameFormat) :
+ m_pImpl(new SwXTableColumns::Impl(rFrameFormat))
+{ }
+
+SwXTableColumns::~SwXTableColumns()
+{ }
+
+SwFrameFormat* SwXTableColumns::GetFrameFormat() const
+{
+ return m_pImpl->GetFrameFormat();
+}
+
+sal_Int32 SwXTableColumns::getCount()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ SwTable* pTable = SwTable::FindTable( pFrameFormat );
+// if(!pTable->IsTableComplex())
+// throw uno::RuntimeException("Table too complex", static_cast<cppu::OWeakObject*>(this));
+ SwTableLines& rLines = pTable->GetTabLines();
+ SwTableLine* pLine = rLines.front();
+ return pLine->GetTabBoxes().size();
+}
+
+uno::Any SwXTableColumns::getByIndex(sal_Int32 nIndex)
+{
+ SolarMutexGuard aGuard;
+ if(nIndex < 0 || getCount() <= nIndex)
+ throw lang::IndexOutOfBoundsException();
+ return uno::Any(uno::Reference<uno::XInterface>()); // i#21699 not supported
+}
+
+uno::Type SAL_CALL SwXTableColumns::getElementType()
+{
+ return cppu::UnoType<uno::XInterface>::get();
+}
+
+sal_Bool SwXTableColumns::hasElements()
+{
+ SolarMutexGuard aGuard;
+ lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this));
+ return true;
+}
+
+///@see SwXTableRows::insertByIndex (TODO: seems to be copy and paste programming here)
+void SwXTableColumns::insertByIndex(sal_Int32 nIndex, sal_Int32 nCount)
+{
+ SolarMutexGuard aGuard;
+ if (nCount == 0)
+ return;
+ SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
+ SwTableLines& rLines = pTable->GetTabLines();
+ SwTableLine* pLine = rLines.front();
+ const size_t nColCount = pLine->GetTabBoxes().size();
+ if (nCount <= 0 || 0 > nIndex || o3tl::make_unsigned(nIndex) > nColCount)
+ throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
+ const OUString sTLName = sw_GetCellName(nIndex, 0);
+ const SwTableBox* pTLBox = pTable->GetTableBox( sTLName );
+ bool bAppend = false;
+ if(!pTLBox)
+ {
+ bAppend = true;
+ // to append at the end the cursor must be in the last line
+ SwTableBoxes& rBoxes = pLine->GetTabBoxes();
+ pTLBox = rBoxes.back();
+ }
+ if(!pTLBox)
+ throw uno::RuntimeException("Illegal arguments", static_cast<cppu::OWeakObject*>(this));
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ UnoActionContext aAction(pFrameFormat->GetDoc());
+ auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+
+ {
+ // remove actions - TODO: why?
+ UnoActionRemoveContext aRemoveContext(&pUnoCursor->GetDoc());
+ }
+
+ pFrameFormat->GetDoc()->InsertCol(*pUnoCursor, o3tl::narrowing<sal_uInt16>(nCount), bAppend);
+}
+
+///@see SwXTableRows::removeByIndex (TODO: seems to be copy and paste programming here)
+void SwXTableColumns::removeByIndex(sal_Int32 nIndex, sal_Int32 nCount)
+{
+ SolarMutexGuard aGuard;
+ if (nCount == 0)
+ return;
+ SwFrameFormat* pFrameFormat(lcl_EnsureCoreConnected(GetFrameFormat(), static_cast<cppu::OWeakObject*>(this)));
+ if(nIndex < 0 || nCount <=0 )
+ throw uno::RuntimeException();
+ SwTable* pTable = lcl_EnsureTableNotComplex(SwTable::FindTable(pFrameFormat), static_cast<cppu::OWeakObject*>(this));
+ const OUString sTLName = sw_GetCellName(nIndex, 0);
+ const SwTableBox* pTLBox = pTable->GetTableBox( sTLName );
+ if(!pTLBox)
+ throw uno::RuntimeException("Cell not found", static_cast<cppu::OWeakObject*>(this));
+ const SwStartNode* pSttNd = pTLBox->GetSttNd();
+ SwPosition aPos(*pSttNd);
+ // set cursor to the upper-left cell of the range
+ auto pUnoCursor(pFrameFormat->GetDoc()->CreateUnoCursor(aPos, true));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ pUnoCursor->SetRemainInSection(false);
+ const OUString sTRName = sw_GetCellName(nIndex + nCount - 1, 0);
+ const SwTableBox* pTRBox = pTable->GetTableBox(sTRName);
+ if(!pTRBox)
+ throw uno::RuntimeException("Cell not found", static_cast<cppu::OWeakObject*>(this));
+ pUnoCursor->SetMark();
+ pUnoCursor->GetPoint()->nNode = *pTRBox->GetSttNd();
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ SwUnoTableCursor& rCursor = dynamic_cast<SwUnoTableCursor&>(*pUnoCursor);
+ {
+ // HACK: remove pending actions for selecting old style tables
+ UnoActionRemoveContext aRemoveContext(rCursor);
+ }
+ rCursor.MakeBoxSels();
+ { // these braces are important
+ UnoActionContext aAction(pFrameFormat->GetDoc());
+ pFrameFormat->GetDoc()->DeleteCol(*pUnoCursor);
+ pUnoCursor.reset();
+ }
+ {
+ // invalidate all actions - TODO: why?
+ UnoActionRemoveContext aRemoveContext(pFrameFormat->GetDoc());
+ }
+}
+
+void SwXTableColumns::Impl::Notify(const SfxHint& rHint)
+{
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pFrameFormat = nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unotext.cxx b/sw/source/core/unocore/unotext.cxx
new file mode 100644
index 000000000..04803e0b9
--- /dev/null
+++ b/sw/source/core/unocore/unotext.cxx
@@ -0,0 +1,2837 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <stdlib.h>
+
+#include <memory>
+#include <set>
+
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/text/ControlCharacter.hpp>
+#include <com/sun/star/text/TableColumnSeparator.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+
+#include <svl/listener.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <cmdid.h>
+#include <unotextbodyhf.hxx>
+#include <unotext.hxx>
+#include <unotextrange.hxx>
+#include <unotextcursor.hxx>
+#include <unosection.hxx>
+#include <unobookmark.hxx>
+#include <unorefmark.hxx>
+#include <unoport.hxx>
+#include <unotbl.hxx>
+#include <unoidx.hxx>
+#include <unocoll.hxx>
+#include <unoframe.hxx>
+#include <unofield.hxx>
+#include <unometa.hxx>
+#include <unomap.hxx>
+#include <unoprnms.hxx>
+#include <unoparagraph.hxx>
+#include <unocrsrhelper.hxx>
+#include <docary.hxx>
+#include <doc.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <bookmark.hxx>
+#include <redline.hxx>
+#include <swundo.hxx>
+#include <section.hxx>
+#include <fmtanchr.hxx>
+#include <fmtcntnt.hxx>
+#include <ndtxt.hxx>
+#include <SwRewriter.hxx>
+#include <strings.hrc>
+#include <frameformats.hxx>
+#include <unocontentcontrol.hxx>
+
+using namespace ::com::sun::star;
+
+constexpr OUStringLiteral cInvalidObject = u"this object is invalid";
+
+class SwXText::Impl
+{
+
+public:
+ SwXText & m_rThis;
+ SfxItemPropertySet const& m_rPropSet;
+ const CursorType m_eType;
+ SwDoc * m_pDoc;
+ bool m_bIsValid;
+
+ Impl( SwXText & rThis,
+ SwDoc *const pDoc, const CursorType eType)
+ : m_rThis(rThis)
+ , m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT))
+ , m_eType(eType)
+ , m_pDoc(pDoc)
+ , m_bIsValid(nullptr != pDoc)
+ {
+ }
+
+ /// @throws lang::IllegalArgumentException
+ /// @throws uno::RuntimeException
+ uno::Reference< text::XTextRange >
+ finishOrAppendParagraph(
+ const uno::Sequence< beans::PropertyValue >&
+ rCharacterAndParagraphProperties,
+ const uno::Reference< text::XTextRange >& xInsertPosition);
+
+ /// @throws lang::IllegalArgumentException
+ /// @throws uno::RuntimeException
+ sal_Int16 ComparePositions(
+ const uno::Reference<text::XTextRange>& xPos1,
+ const uno::Reference<text::XTextRange>& xPos2);
+
+ /// @throws lang::IllegalArgumentException
+ /// @throws uno::RuntimeException
+ bool CheckForOwnMember(const SwPaM & rPaM);
+
+ void ConvertCell(
+ const uno::Sequence< uno::Reference< text::XTextRange > > & rCell,
+ std::vector<SwNodeRange> & rRowNodes,
+ SwNodeRange *const pLastCell);
+
+};
+
+SwXText::SwXText(SwDoc *const pDoc, const CursorType eType)
+ : m_pImpl( new SwXText::Impl(*this, pDoc, eType) )
+{
+}
+
+SwXText::~SwXText()
+{
+}
+
+const SwDoc * SwXText::GetDoc() const
+{
+ return m_pImpl->m_pDoc;
+}
+
+SwDoc * SwXText::GetDoc()
+{
+ return m_pImpl->m_pDoc;
+}
+
+bool SwXText::IsValid() const
+{
+ return m_pImpl->m_bIsValid;
+}
+
+void SwXText::Invalidate()
+{
+ m_pImpl->m_bIsValid = false;
+}
+
+void SwXText::SetDoc(SwDoc *const pDoc)
+{
+ OSL_ENSURE(!m_pImpl->m_pDoc || !pDoc,
+ "SwXText::SetDoc: already have a doc?");
+ m_pImpl->m_pDoc = pDoc;
+ m_pImpl->m_bIsValid = (nullptr != pDoc);
+}
+
+void
+SwXText::PrepareForAttach(uno::Reference< text::XTextRange > &, const SwPaM &)
+{
+}
+
+bool SwXText::CheckForOwnMemberMeta(const SwPaM &, const bool)
+{
+ OSL_ENSURE(CursorType::Meta != m_pImpl->m_eType, "should not be called!");
+ return false;
+}
+
+const SwStartNode *SwXText::GetStartNode() const
+{
+ return GetDoc()->GetNodes().GetEndOfContent().StartOfSectionNode();
+}
+
+uno::Reference< text::XTextCursor >
+SwXText::CreateCursor()
+{
+ uno::Reference< text::XTextCursor > xRet;
+ if(IsValid())
+ {
+ SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
+ SwPosition aPos(rNode);
+ xRet = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), this, m_pImpl->m_eType, aPos));
+ xRet->gotoStart(false);
+ }
+ return xRet;
+}
+
+uno::Any SAL_CALL
+SwXText::queryInterface(const uno::Type& rType)
+{
+ uno::Any aRet;
+ if (rType == cppu::UnoType<text::XText>::get())
+ {
+ aRet <<= uno::Reference< text::XText >(this);
+ }
+ else if (rType == cppu::UnoType<text::XSimpleText>::get())
+ {
+ aRet <<= uno::Reference< text::XSimpleText >(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextRange>::get())
+ {
+ aRet <<= uno::Reference< text::XTextRange>(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextRangeCompare>::get())
+ {
+ aRet <<= uno::Reference< text::XTextRangeCompare >(this);
+ }
+ else if (rType == cppu::UnoType<lang::XTypeProvider>::get())
+ {
+ aRet <<= uno::Reference< lang::XTypeProvider >(this);
+ }
+ else if (rType == cppu::UnoType<text::XRelativeTextContentInsert>::get())
+ {
+ aRet <<= uno::Reference< text::XRelativeTextContentInsert >(this);
+ }
+ else if (rType == cppu::UnoType<text::XRelativeTextContentRemove>::get())
+ {
+ aRet <<= uno::Reference< text::XRelativeTextContentRemove >(this);
+ }
+ else if (rType == cppu::UnoType<beans::XPropertySet>::get())
+ {
+ aRet <<= uno::Reference< beans::XPropertySet >(this);
+ }
+ else if (rType == cppu::UnoType<lang::XUnoTunnel>::get())
+ {
+ aRet <<= uno::Reference< lang::XUnoTunnel >(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextAppendAndConvert>::get())
+ {
+ aRet <<= uno::Reference< text::XTextAppendAndConvert >(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextAppend>::get())
+ {
+ aRet <<= uno::Reference< text::XTextAppend >(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextPortionAppend>::get())
+ {
+ aRet <<= uno::Reference< text::XTextPortionAppend >(this);
+ }
+ else if (rType == cppu::UnoType<text::XParagraphAppend>::get())
+ {
+ aRet <<= uno::Reference< text::XParagraphAppend >(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextConvert>::get() )
+ {
+ aRet <<= uno::Reference< text::XTextConvert >(this);
+ }
+ else if (rType == cppu::UnoType<text::XTextContentAppend>::get())
+ {
+ aRet <<= uno::Reference< text::XTextContentAppend >(this);
+ }
+ else if(rType == cppu::UnoType<text::XTextCopy>::get())
+ {
+ aRet <<= uno::Reference< text::XTextCopy >( this );
+ }
+ return aRet;
+}
+
+uno::Sequence< uno::Type > SAL_CALL
+SwXText::getTypes()
+{
+ static const uno::Sequence< uno::Type > aTypes {
+ cppu::UnoType<text::XText>::get(),
+ cppu::UnoType<text::XTextRangeCompare>::get(),
+ cppu::UnoType<text::XRelativeTextContentInsert>::get(),
+ cppu::UnoType<text::XRelativeTextContentRemove>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<text::XTextPortionAppend>::get(),
+ cppu::UnoType<text::XParagraphAppend>::get(),
+ cppu::UnoType<text::XTextContentAppend>::get(),
+ cppu::UnoType<text::XTextConvert>::get(),
+ cppu::UnoType<text::XTextAppend>::get(),
+ cppu::UnoType<text::XTextAppendAndConvert>::get()
+ };
+ return aTypes;
+}
+
+// belongs the range in the text ? insert it then.
+void SAL_CALL
+SwXText::insertString(const uno::Reference< text::XTextRange >& xTextRange,
+ const OUString& rString, sal_Bool bAbsorb)
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("SwXText::insertString");
+
+ if (!xTextRange.is())
+ {
+ throw uno::RuntimeException();
+ }
+ if (!GetDoc())
+ {
+ throw uno::RuntimeException();
+ }
+ const uno::Reference<lang::XUnoTunnel> xRangeTunnel(xTextRange,
+ uno::UNO_QUERY);
+ SwXTextRange *const pRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+ if ((!pRange || &pRange ->GetDoc() != GetDoc()) &&
+ (!pCursor || pCursor->GetDoc() != GetDoc()))
+ {
+ throw uno::RuntimeException();
+ }
+
+ const SwStartNode *const pOwnStartNode = GetStartNode();
+ SwPaM aPam(GetDoc()->GetNodes());
+ const SwPaM * pPam(nullptr);
+ if (pCursor)
+ {
+ pPam = pCursor->GetPaM();
+ }
+ else // pRange
+ {
+ if (pRange->GetPositions(aPam))
+ {
+ pPam = &aPam;
+ }
+ }
+ if (!pPam)
+ {
+ throw uno::RuntimeException();
+ }
+
+ const SwStartNode* pTmp(pPam->GetNode().StartOfSectionNode());
+ while (pTmp && pTmp->IsSectionNode())
+ {
+ pTmp = pTmp->StartOfSectionNode();
+ }
+ if (!pOwnStartNode || (pOwnStartNode != pTmp))
+ {
+ throw uno::RuntimeException();
+ }
+
+ bool bForceExpandHints( false );
+ if (CursorType::Meta == m_pImpl->m_eType)
+ {
+ try
+ {
+ bForceExpandHints = CheckForOwnMemberMeta(*pPam, bAbsorb);
+ }
+ catch (const lang::IllegalArgumentException& iae)
+ {
+ // stupid method not allowed to throw iae
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw lang::WrappedTargetRuntimeException( iae.Message,
+ uno::Reference< uno::XInterface >(), anyEx );
+ }
+ }
+ if (bAbsorb)
+ {
+ //!! scan for CR characters and inserting the paragraph breaks
+ //!! has to be done in the called function.
+ //!! Implemented in SwXTextRange::DeleteAndInsert
+ if (pCursor)
+ {
+ SwXTextCursor * const pTextCursor(
+ dynamic_cast<SwXTextCursor*>(pCursor) );
+ if (pTextCursor)
+ {
+ pTextCursor->DeleteAndInsert(rString, bForceExpandHints);
+ }
+ else
+ {
+ xTextRange->setString(rString);
+ }
+ }
+ else
+ {
+ pRange->DeleteAndInsert(rString, bForceExpandHints);
+ }
+ }
+ else
+ {
+ // create a PaM positioned before the parameter PaM,
+ // so the text is inserted before
+ UnoActionContext aContext(GetDoc());
+ SwPaM aInsertPam(*pPam->Start());
+ ::sw::GroupUndoGuard const undoGuard(GetDoc()->GetIDocumentUndoRedo());
+ SwUnoCursorHelper::DocInsertStringSplitCR(
+ *GetDoc(), aInsertPam, rString, bForceExpandHints );
+ }
+}
+
+void SAL_CALL
+SwXText::insertControlCharacter(
+ const uno::Reference< text::XTextRange > & xTextRange,
+ sal_Int16 nControlCharacter, sal_Bool bAbsorb)
+{
+ SolarMutexGuard aGuard;
+
+ if (!xTextRange.is())
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (!GetDoc())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (!::sw::XTextRangeToSwPaM(aPam, xTextRange))
+ {
+ throw uno::RuntimeException();
+ }
+ const bool bForceExpandHints(CheckForOwnMemberMeta(aPam, bAbsorb));
+
+ const SwInsertFlags nInsertFlags =
+ bForceExpandHints
+ ? ( SwInsertFlags::FORCEHINTEXPAND | SwInsertFlags::EMPTYEXPAND)
+ : SwInsertFlags::EMPTYEXPAND;
+
+ if (bAbsorb && aPam.HasMark())
+ {
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
+ aPam.DeleteMark();
+ }
+
+ sal_Unicode cIns = 0;
+ switch (nControlCharacter)
+ {
+ case text::ControlCharacter::PARAGRAPH_BREAK :
+ // a table cell now becomes an ordinary text cell!
+ m_pImpl->m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->nNode);
+ m_pImpl->m_pDoc->getIDocumentContentOperations().SplitNode(*aPam.GetPoint(), false);
+ break;
+ case text::ControlCharacter::APPEND_PARAGRAPH:
+ {
+ m_pImpl->m_pDoc->ClearBoxNumAttrs(aPam.GetPoint()->nNode);
+ m_pImpl->m_pDoc->getIDocumentContentOperations().AppendTextNode(*aPam.GetPoint());
+
+ const uno::Reference<lang::XUnoTunnel> xRangeTunnel(
+ xTextRange, uno::UNO_QUERY);
+ SwXTextRange *const pRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(
+ xRangeTunnel);
+ if (pRange)
+ {
+ pRange->SetPositions(aPam);
+ }
+ else if (pCursor)
+ {
+ SwPaM *const pCursorPam = pCursor->GetPaM();
+ *pCursorPam->GetPoint() = *aPam.GetPoint();
+ pCursorPam->DeleteMark();
+ }
+ }
+ break;
+ case text::ControlCharacter::LINE_BREAK: cIns = 10; break;
+ case text::ControlCharacter::SOFT_HYPHEN: cIns = CHAR_SOFTHYPHEN; break;
+ case text::ControlCharacter::HARD_HYPHEN: cIns = CHAR_HARDHYPHEN; break;
+ case text::ControlCharacter::HARD_SPACE: cIns = CHAR_HARDBLANK; break;
+ }
+ if (cIns)
+ {
+ m_pImpl->m_pDoc->getIDocumentContentOperations().InsertString(
+ aPam, OUString(cIns), nInsertFlags);
+ }
+
+ if (!bAbsorb)
+ return;
+
+ const uno::Reference<lang::XUnoTunnel> xRangeTunnel(
+ xTextRange, uno::UNO_QUERY);
+ SwXTextRange *const pRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel);
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel);
+
+ SwCursor aCursor(*aPam.GetPoint(), nullptr);
+ SwUnoCursorHelper::SelectPam(aCursor, true);
+ aCursor.Left(1);
+ // here, the PaM needs to be moved:
+ if (pRange)
+ {
+ pRange->SetPositions(aCursor);
+ }
+ else
+ {
+ SwPaM *const pUnoCursor = pCursor->GetPaM();
+ *pUnoCursor->GetPoint() = *aCursor.GetPoint();
+ if (aCursor.HasMark())
+ {
+ pUnoCursor->SetMark();
+ *pUnoCursor->GetMark() = *aCursor.GetMark();
+ }
+ else
+ {
+ pUnoCursor->DeleteMark();
+ }
+ }
+}
+
+void SAL_CALL
+SwXText::insertTextContent(
+ const uno::Reference< text::XTextRange > & xRange,
+ const uno::Reference< text::XTextContent > & xContent,
+ sal_Bool bAbsorb)
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("SwXText::insertTextContent");
+
+ if (!xRange.is())
+ {
+ lang::IllegalArgumentException aIllegal;
+ aIllegal.Message = "first parameter invalid;";
+ throw aIllegal;
+ }
+ if (!xContent.is())
+ {
+ lang::IllegalArgumentException aIllegal;
+ aIllegal.Message = "second parameter invalid";
+ throw aIllegal;
+ }
+ if(!GetDoc())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (!::sw::XTextRangeToSwPaM(aPam, xRange))
+ {
+ lang::IllegalArgumentException aIllegal;
+ aIllegal.Message = "first parameter invalid";
+ throw aIllegal;
+ }
+
+ // first test if the range is at the right position, then call
+ // xContent->attach
+ const SwStartNode* pOwnStartNode = GetStartNode();
+ SwStartNodeType eSearchNodeType = SwNormalStartNode;
+ switch (m_pImpl->m_eType)
+ {
+ case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
+ case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
+ case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
+ case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
+ case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
+ //case CURSOR_INVALID:
+ //case CursorType::Body:
+ default:
+ break;
+ }
+
+ const SwStartNode* pTmp =
+ aPam.GetNode().FindSttNodeByType(eSearchNodeType);
+
+ // ignore SectionNodes
+ while (pTmp && pTmp->IsSectionNode())
+ {
+ pTmp = pTmp->StartOfSectionNode();
+ }
+ // if the document starts with a section
+ while (pOwnStartNode && pOwnStartNode->IsSectionNode())
+ {
+ pOwnStartNode = pOwnStartNode->StartOfSectionNode();
+ }
+ // this checks if (this) and xRange are in the same text::XText interface
+ if (pOwnStartNode != pTmp)
+ {
+ uno::RuntimeException aRunException;
+ aRunException.Message = "text interface and cursor not related";
+ throw aRunException;
+ }
+
+ const bool bForceExpandHints(CheckForOwnMemberMeta(aPam, bAbsorb));
+
+ // special treatment for Contents that do not replace the range, but
+ // instead are "overlaid"
+ const uno::Reference<lang::XUnoTunnel> xContentTunnel(xContent,
+ uno::UNO_QUERY);
+ if (!xContentTunnel.is())
+ {
+ lang::IllegalArgumentException aArgException;
+ aArgException.Message = "text content does not support lang::XUnoTunnel";
+ throw aArgException;
+ }
+ SwXDocumentIndexMark *const pDocumentIndexMark =
+ comphelper::getFromUnoTunnel<SwXDocumentIndexMark>(xContentTunnel);
+ SwXTextSection *const pSection =
+ comphelper::getFromUnoTunnel<SwXTextSection>(xContentTunnel);
+ SwXBookmark *const pBookmark =
+ comphelper::getFromUnoTunnel<SwXBookmark>(xContentTunnel);
+ SwXReferenceMark *const pReferenceMark =
+ comphelper::getFromUnoTunnel<SwXReferenceMark>(xContentTunnel);
+ SwXMeta *const pMeta =
+ comphelper::getFromUnoTunnel<SwXMeta>(xContentTunnel);
+ auto* pContentControl = comphelper::getFromUnoTunnel<SwXContentControl>(xContentTunnel);
+ SwXTextField* pTextField =
+ comphelper::getFromUnoTunnel<SwXTextField>(xContentTunnel);
+ if (pTextField && pTextField->GetServiceId() != SwServiceType::FieldTypeAnnotation)
+ pTextField = nullptr;
+
+ const bool bAttribute = pBookmark || pDocumentIndexMark
+ || pSection || pReferenceMark || pMeta || pContentControl || pTextField;
+
+ if (bAbsorb && !bAttribute)
+ {
+ xRange->setString(OUString());
+ }
+ uno::Reference< text::XTextRange > xTempRange =
+ (bAttribute && bAbsorb) ? xRange : xRange->getStart();
+ if (bForceExpandHints)
+ {
+ // if necessary, replace xTempRange with a new SwXTextCursor
+ PrepareForAttach(xTempRange, aPam);
+ }
+ xContent->attach(xTempRange);
+}
+
+void SAL_CALL
+SwXText::insertTextContentBefore(
+ const uno::Reference< text::XTextContent>& xNewContent,
+ const uno::Reference< text::XTextContent>& xSuccessor)
+{
+ SolarMutexGuard aGuard;
+
+ if(!GetDoc())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ SwXParagraph *const pPara =
+ comphelper::getFromUnoTunnel<SwXParagraph>(xNewContent);
+ if (!pPara || !pPara->IsDescriptor() || !xSuccessor.is())
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ bool bRet = false;
+ const uno::Reference<lang::XUnoTunnel> xSuccTunnel(xSuccessor,
+ uno::UNO_QUERY);
+ SwXTextSection *const pXSection =
+ comphelper::getFromUnoTunnel<SwXTextSection>(xSuccTunnel);
+ SwXTextTable *const pXTable =
+ comphelper::getFromUnoTunnel<SwXTextTable>(xSuccTunnel);
+ SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
+ SwTextNode * pTextNode = nullptr;
+ if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
+ {
+ SwTable *const pTable = SwTable::FindTable( pTableFormat );
+ SwTableNode *const pTableNode = pTable->GetTableNode();
+
+ const SwNodeIndex aTableIdx( *pTableNode, -1 );
+ SwPosition aBefore(aTableIdx);
+ bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
+ pTextNode = aBefore.nNode.GetNode().GetTextNode();
+ }
+ else if (pXSection && pXSection->GetFormat() &&
+ pXSection->GetFormat()->GetDoc() == GetDoc())
+ {
+ SwSectionFormat *const pSectFormat = pXSection->GetFormat();
+ SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
+
+ const SwNodeIndex aSectIdx( *pSectNode, -1 );
+ SwPosition aBefore(aSectIdx);
+ bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
+ pTextNode = aBefore.nNode.GetNode().GetTextNode();
+ }
+ if (!bRet || !pTextNode)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ pPara->attachToText(*this, *pTextNode);
+}
+
+void SAL_CALL
+SwXText::insertTextContentAfter(
+ const uno::Reference< text::XTextContent>& xNewContent,
+ const uno::Reference< text::XTextContent>& xPredecessor)
+{
+ SolarMutexGuard aGuard;
+
+ if(!GetDoc())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwXParagraph *const pPara =
+ comphelper::getFromUnoTunnel<SwXParagraph>(xNewContent);
+ if(!pPara || !pPara->IsDescriptor() || !xPredecessor.is())
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ const uno::Reference<lang::XUnoTunnel> xPredTunnel(xPredecessor,
+ uno::UNO_QUERY);
+ SwXTextSection *const pXSection =
+ comphelper::getFromUnoTunnel<SwXTextSection>(xPredTunnel);
+ SwXTextTable *const pXTable =
+ comphelper::getFromUnoTunnel<SwXTextTable>(xPredTunnel);
+ SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
+ bool bRet = false;
+ SwTextNode * pTextNode = nullptr;
+ if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
+ {
+ SwTable *const pTable = SwTable::FindTable( pTableFormat );
+ SwTableNode *const pTableNode = pTable->GetTableNode();
+
+ SwEndNode *const pTableEnd = pTableNode->EndOfSectionNode();
+ SwPosition aTableEnd(*pTableEnd);
+ bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
+ pTextNode = aTableEnd.nNode.GetNode().GetTextNode();
+ }
+ else if (pXSection && pXSection->GetFormat() &&
+ pXSection->GetFormat()->GetDoc() == GetDoc())
+ {
+ SwSectionFormat *const pSectFormat = pXSection->GetFormat();
+ SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
+ SwEndNode *const pEnd = pSectNode->EndOfSectionNode();
+ SwPosition aEnd(*pEnd);
+ bRet = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
+ pTextNode = aEnd.nNode.GetNode().GetTextNode();
+ }
+ if (!bRet || !pTextNode)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ pPara->attachToText(*this, *pTextNode);
+}
+
+void SAL_CALL
+SwXText::removeTextContentBefore(
+ const uno::Reference< text::XTextContent>& xSuccessor)
+{
+ SolarMutexGuard aGuard;
+
+ if(!GetDoc())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ bool bRet = false;
+ const uno::Reference<lang::XUnoTunnel> xSuccTunnel(xSuccessor,
+ uno::UNO_QUERY);
+ SwXTextSection *const pXSection =
+ comphelper::getFromUnoTunnel<SwXTextSection>(xSuccTunnel);
+ SwXTextTable *const pXTable =
+ comphelper::getFromUnoTunnel<SwXTextTable>(xSuccTunnel);
+ SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
+ if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
+ {
+ SwTable *const pTable = SwTable::FindTable( pTableFormat );
+ SwTableNode *const pTableNode = pTable->GetTableNode();
+
+ const SwNodeIndex aTableIdx( *pTableNode, -1 );
+ if(aTableIdx.GetNode().IsTextNode())
+ {
+ SwPaM aBefore(aTableIdx);
+ bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aBefore );
+ }
+ }
+ else if (pXSection && pXSection->GetFormat() &&
+ pXSection->GetFormat()->GetDoc() == GetDoc())
+ {
+ SwSectionFormat *const pSectFormat = pXSection->GetFormat();
+ SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
+
+ const SwNodeIndex aSectIdx( *pSectNode, -1 );
+ if(aSectIdx.GetNode().IsTextNode())
+ {
+ SwPaM aBefore(aSectIdx);
+ bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aBefore );
+ }
+ }
+ if(!bRet)
+ {
+ throw lang::IllegalArgumentException();
+ }
+}
+
+void SAL_CALL
+SwXText::removeTextContentAfter(
+ const uno::Reference< text::XTextContent>& xPredecessor)
+{
+ SolarMutexGuard aGuard;
+
+ if(!GetDoc())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ bool bRet = false;
+ const uno::Reference<lang::XUnoTunnel> xPredTunnel(xPredecessor,
+ uno::UNO_QUERY);
+ SwXTextSection *const pXSection =
+ comphelper::getFromUnoTunnel<SwXTextSection>(xPredTunnel);
+ SwXTextTable *const pXTable =
+ comphelper::getFromUnoTunnel<SwXTextTable>(xPredTunnel);
+ SwFrameFormat *const pTableFormat = pXTable ? pXTable->GetFrameFormat() : nullptr;
+ if(pTableFormat && pTableFormat->GetDoc() == GetDoc())
+ {
+ SwTable *const pTable = SwTable::FindTable( pTableFormat );
+ SwTableNode *const pTableNode = pTable->GetTableNode();
+ SwEndNode *const pTableEnd = pTableNode->EndOfSectionNode();
+
+ const SwNodeIndex aTableIdx( *pTableEnd, 1 );
+ if(aTableIdx.GetNode().IsTextNode())
+ {
+ SwPaM aPaM(aTableIdx);
+ bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aPaM );
+ }
+ }
+ else if (pXSection && pXSection->GetFormat() &&
+ pXSection->GetFormat()->GetDoc() == GetDoc())
+ {
+ SwSectionFormat *const pSectFormat = pXSection->GetFormat();
+ SwSectionNode *const pSectNode = pSectFormat->GetSectionNode();
+ SwEndNode *const pEnd = pSectNode->EndOfSectionNode();
+ const SwNodeIndex aSectIdx( *pEnd, 1 );
+ if(aSectIdx.GetNode().IsTextNode())
+ {
+ SwPaM aAfter(aSectIdx);
+ bRet = GetDoc()->getIDocumentContentOperations().DelFullPara( aAfter );
+ }
+ }
+ if(!bRet)
+ {
+ throw lang::IllegalArgumentException();
+ }
+}
+
+void SAL_CALL
+SwXText::removeTextContent(
+ const uno::Reference< text::XTextContent > & xContent)
+{
+ // forward: need no solar mutex here
+ if(!xContent.is())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = "first parameter invalid";
+ throw aRuntime;
+ }
+ xContent->dispose();
+}
+
+uno::Reference< text::XText > SAL_CALL
+SwXText::getText()
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("SwXText::getText");
+
+ const uno::Reference< text::XText > xRet(this);
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::getStart()
+{
+ SolarMutexGuard aGuard;
+
+ const uno::Reference< text::XTextCursor > xRef = CreateCursor();
+ if(!xRef.is())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+ xRef->gotoStart(false);
+ return xRef;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::getEnd()
+{
+ SolarMutexGuard aGuard;
+
+ const uno::Reference< text::XTextCursor > xRef = CreateCursor();
+ if(!xRef.is())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+ xRef->gotoEnd(false);
+ return xRef;
+}
+
+OUString SAL_CALL SwXText::getString()
+{
+ SolarMutexGuard aGuard;
+
+ const uno::Reference< text::XTextCursor > xRet = CreateCursor();
+ if(!xRet.is())
+ {
+ SAL_WARN("sw.uno", "cursor was not created in getString() call. Returning empty string.");
+ return OUString();
+ }
+ xRet->gotoEnd(true);
+ return xRet->getString();
+}
+
+void SAL_CALL
+SwXText::setString(const OUString& rString)
+{
+ SolarMutexGuard aGuard;
+
+ if (!GetDoc())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ const SwStartNode* pStartNode = GetStartNode();
+ if (!pStartNode)
+ {
+ throw uno::RuntimeException();
+ }
+
+ GetDoc()->GetIDocumentUndoRedo().StartUndo(SwUndoId::START, nullptr);
+ //insert an empty paragraph at the start and at the end to ensure that
+ //all tables and sections can be removed by the selecting text::XTextCursor
+ if (CursorType::Meta != m_pImpl->m_eType)
+ {
+ SwPosition aStartPos(*pStartNode);
+ const SwEndNode* pEnd = pStartNode->EndOfSectionNode();
+ SwNodeIndex aEndIdx(*pEnd);
+ --aEndIdx;
+ //the inserting of nodes should only be done if really necessary
+ //to prevent #97924# (removes paragraph attributes when setting the text
+ //e.g. of a table cell
+ bool bInsertNodes = false;
+ SwNodeIndex aStartIdx(*pStartNode);
+ do
+ {
+ ++aStartIdx;
+ SwNode& rCurrentNode = aStartIdx.GetNode();
+ if(rCurrentNode.GetNodeType() == SwNodeType::Section
+ ||rCurrentNode.GetNodeType() == SwNodeType::Table)
+ {
+ bInsertNodes = true;
+ break;
+ }
+ }
+ while(aStartIdx < aEndIdx);
+ if(bInsertNodes)
+ {
+ GetDoc()->getIDocumentContentOperations().AppendTextNode( aStartPos );
+ SwPosition aEndPos(aEndIdx.GetNode());
+ SwPaM aPam(aEndPos);
+ GetDoc()->getIDocumentContentOperations().AppendTextNode( *aPam.Start() );
+ }
+ }
+
+ const uno::Reference< text::XTextCursor > xRet = CreateCursor();
+ if(!xRet.is())
+ {
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+ xRet->gotoEnd(true);
+ xRet->setString(rString);
+ GetDoc()->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+}
+
+//FIXME why is CheckForOwnMember duplicated in some insert methods?
+// Description: Checks if pRange/pCursor are member of the same text interface.
+// Only one of the pointers has to be set!
+bool SwXText::Impl::CheckForOwnMember(
+ const SwPaM & rPaM)
+{
+ const uno::Reference<text::XTextCursor> xOwnCursor(m_rThis.CreateCursor());
+
+ OTextCursorHelper *const pOwnCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xOwnCursor);
+ OSL_ENSURE(pOwnCursor, "OTextCursorHelper::getUnoTunnelId() ??? ");
+ const SwStartNode* pOwnStartNode =
+ pOwnCursor->GetPaM()->GetNode().StartOfSectionNode();
+ SwStartNodeType eSearchNodeType = SwNormalStartNode;
+ switch (m_eType)
+ {
+ case CursorType::Frame: eSearchNodeType = SwFlyStartNode; break;
+ case CursorType::TableText: eSearchNodeType = SwTableBoxStartNode; break;
+ case CursorType::Footnote: eSearchNodeType = SwFootnoteStartNode; break;
+ case CursorType::Header: eSearchNodeType = SwHeaderStartNode; break;
+ case CursorType::Footer: eSearchNodeType = SwFooterStartNode; break;
+ //case CURSOR_INVALID:
+ //case CursorType::Body:
+ default:
+ ;
+ }
+
+ const SwNode& rSrcNode = rPaM.GetNode();
+ const SwStartNode* pTmp = rSrcNode.FindSttNodeByType(eSearchNodeType);
+
+ // skip SectionNodes / TableNodes to be able to compare across table/section boundaries
+ while (pTmp
+ && (pTmp->IsSectionNode() || pTmp->IsTableNode()
+ || (m_eType != CursorType::TableText
+ && pTmp->GetStartNodeType() == SwTableBoxStartNode)))
+ {
+ pTmp = pTmp->StartOfSectionNode();
+ }
+
+ while (pOwnStartNode->IsSectionNode() || pOwnStartNode->IsTableNode()
+ || (m_eType != CursorType::TableText
+ && pOwnStartNode->GetStartNodeType() == SwTableBoxStartNode))
+ {
+ pOwnStartNode = pOwnStartNode->StartOfSectionNode();
+ }
+
+ //this checks if (this) and xRange are in the same text::XText interface
+ return (pOwnStartNode == pTmp);
+}
+
+sal_Int16
+SwXText::Impl::ComparePositions(
+ const uno::Reference<text::XTextRange>& xPos1,
+ const uno::Reference<text::XTextRange>& xPos2)
+{
+ SwUnoInternalPaM aPam1(*m_pDoc);
+ SwUnoInternalPaM aPam2(*m_pDoc);
+
+ if (!::sw::XTextRangeToSwPaM(aPam1, xPos1) ||
+ !::sw::XTextRangeToSwPaM(aPam2, xPos2))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (!CheckForOwnMember(aPam1) || !CheckForOwnMember(aPam2))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ sal_Int16 nCompare = 0;
+ SwPosition const*const pStart1 = aPam1.Start();
+ SwPosition const*const pStart2 = aPam2.Start();
+ if (*pStart1 < *pStart2)
+ {
+ nCompare = 1;
+ }
+ else if (*pStart1 > *pStart2)
+ {
+ nCompare = -1;
+ }
+ else
+ {
+ OSL_ENSURE(*pStart1 == *pStart2,
+ "SwPositions should be equal here");
+ nCompare = 0;
+ }
+
+ return nCompare;
+}
+
+sal_Int16 SAL_CALL
+SwXText::compareRegionStarts(
+ const uno::Reference<text::XTextRange>& xRange1,
+ const uno::Reference<text::XTextRange>& xRange2)
+{
+ SolarMutexGuard aGuard;
+
+ if (!xRange1.is() || !xRange2.is())
+ {
+ throw lang::IllegalArgumentException();
+ }
+ const uno::Reference<text::XTextRange> xStart1 = xRange1->getStart();
+ const uno::Reference<text::XTextRange> xStart2 = xRange2->getStart();
+
+ return m_pImpl->ComparePositions(xStart1, xStart2);
+}
+
+sal_Int16 SAL_CALL
+SwXText::compareRegionEnds(
+ const uno::Reference<text::XTextRange>& xRange1,
+ const uno::Reference<text::XTextRange>& xRange2)
+{
+ SolarMutexGuard aGuard;
+
+ if (!xRange1.is() || !xRange2.is())
+ {
+ throw lang::IllegalArgumentException();
+ }
+ uno::Reference<text::XTextRange> xEnd1 = xRange1->getEnd();
+ uno::Reference<text::XTextRange> xEnd2 = xRange2->getEnd();
+
+ return m_pImpl->ComparePositions(xEnd1, xEnd2);
+}
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL
+SwXText::getPropertySetInfo()
+{
+ SolarMutexGuard g;
+
+ static uno::Reference< beans::XPropertySetInfo > xInfo =
+ m_pImpl->m_rPropSet.getPropertySetInfo();
+ return xInfo;
+}
+
+void SAL_CALL
+SwXText::setPropertyValue(const OUString& /*aPropertyName*/,
+ const uno::Any& /*aValue*/)
+{
+ throw lang::IllegalArgumentException();
+}
+
+uno::Any SAL_CALL
+SwXText::getPropertyValue(
+ const OUString& rPropertyName)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SfxItemPropertyMapEntry const*const pEntry =
+ m_pImpl->m_rPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ {
+ beans::UnknownPropertyException aExcept;
+ aExcept.Message = "Unknown property: " + rPropertyName;
+ throw aExcept;
+ }
+
+ uno::Any aRet;
+ switch (pEntry->nWID)
+ {
+// no code necessary - the redline is always located at the end node
+// case FN_UNO_REDLINE_NODE_START:
+// break;
+ case FN_UNO_REDLINE_NODE_END:
+ {
+ const SwRedlineTable& rRedTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
+ const size_t nRedTableCount = rRedTable.size();
+ if (nRedTableCount > 0)
+ {
+ SwStartNode const*const pStartNode = GetStartNode();
+ const SwNodeOffset nOwnIndex = pStartNode->EndOfSectionIndex();
+ for (size_t nRed = 0; nRed < nRedTableCount; ++nRed)
+ {
+ SwRangeRedline const*const pRedline = rRedTable[nRed];
+ SwPosition const*const pRedStart = pRedline->Start();
+ const SwNodeIndex nRedNode = pRedStart->nNode;
+ if (nOwnIndex == nRedNode.GetIndex())
+ {
+ aRet <<= SwXRedlinePortion::CreateRedlineProperties(
+ *pRedline, true);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ return aRet;
+}
+
+void SAL_CALL
+SwXText::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXText::addPropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXText::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXText::removePropertyChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXText::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXText::addVetoableChangeListener(): not implemented");
+}
+
+void SAL_CALL
+SwXText::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
+{
+ OSL_FAIL("SwXText::removeVetoableChangeListener(): not implemented");
+}
+
+namespace
+{
+}
+
+const uno::Sequence< sal_Int8 > & SwXText::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSwXTextUnoTunnelId;
+ return theSwXTextUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL
+SwXText::getSomething(const uno::Sequence< sal_Int8 >& rId)
+{
+ return comphelper::getSomethingImpl<SwXText>(rId, this);
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::finishParagraph(
+ const uno::Sequence< beans::PropertyValue > & rProperties)
+{
+ SolarMutexGuard g;
+
+ return m_pImpl->finishOrAppendParagraph(rProperties, uno::Reference< text::XTextRange >());
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::finishParagraphInsert(
+ const uno::Sequence< beans::PropertyValue > & rProperties,
+ const uno::Reference< text::XTextRange >& xInsertPosition)
+{
+ SolarMutexGuard g;
+
+ return m_pImpl->finishOrAppendParagraph(rProperties, xInsertPosition);
+}
+
+uno::Reference< text::XTextRange >
+SwXText::Impl::finishOrAppendParagraph(
+ const uno::Sequence< beans::PropertyValue > & rProperties,
+ const uno::Reference< text::XTextRange >& xInsertPosition)
+{
+ if (!m_bIsValid)
+ {
+ throw uno::RuntimeException();
+ }
+
+ const SwStartNode* pStartNode = m_rThis.GetStartNode();
+ if(!pStartNode)
+ {
+ throw uno::RuntimeException();
+ }
+
+ uno::Reference< text::XTextRange > xRet;
+ bool bIllegalException = false;
+ bool bRuntimeException = false;
+ OUString sMessage;
+ m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::START , nullptr);
+ // find end node, go backward - don't skip tables because the new
+ // paragraph has to be the last node
+ //aPam.Move( fnMoveBackward, GoInNode );
+ SwPosition aInsertPosition(
+ SwNodeIndex( *pStartNode->EndOfSectionNode(), -1 ) );
+ SwPaM aPam(aInsertPosition);
+ // If we got a position reference, then the insert point is not the end of
+ // the document.
+ if (xInsertPosition.is())
+ {
+ SwUnoInternalPaM aStartPam(*m_rThis.GetDoc());
+ ::sw::XTextRangeToSwPaM(aStartPam, xInsertPosition);
+ aPam = aStartPam;
+ aPam.SetMark();
+ }
+ m_pDoc->getIDocumentContentOperations().AppendTextNode( *aPam.GetPoint() );
+ // remove attributes from the previous paragraph
+ m_pDoc->ResetAttrs(aPam);
+ // in case of finishParagraph the PaM needs to be moved to the
+ // previous paragraph
+ aPam.Move( fnMoveBackward, GoInNode );
+
+ try
+ {
+ SfxItemPropertySet const*const pParaPropSet =
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_PARAGRAPH);
+
+ SwUnoCursorHelper::SetPropertyValues(aPam, *pParaPropSet, rProperties);
+
+ // tdf#127616 keep direct character formatting of empty paragraphs,
+ // if character style of the paragraph sets also the same attributes
+ if (aPam.Start()->nNode.GetNode().GetTextNode()->Len() == 0)
+ {
+ auto itCharStyle = std::find_if(rProperties.begin(), rProperties.end(), [](const beans::PropertyValue& rValue)
+ {
+ return rValue.Name == "CharStyleName";
+ });
+ if ( itCharStyle != rProperties.end() )
+ {
+ for (const auto& rValue : rProperties)
+ {
+ if ( rValue != *itCharStyle && rValue.Name.startsWith("Char") )
+ {
+ SwUnoCursorHelper::SetPropertyValue(aPam, *pParaPropSet, rValue.Name, rValue.Value);
+ }
+ }
+ }
+ }
+ }
+ catch (const lang::IllegalArgumentException& rIllegal)
+ {
+ sMessage = rIllegal.Message;
+ bIllegalException = true;
+ }
+ catch (const uno::RuntimeException& rRuntime)
+ {
+ sMessage = rRuntime.Message;
+ bRuntimeException = true;
+ }
+ catch (const uno::Exception& rEx)
+ {
+ sMessage = rEx.Message;
+ bRuntimeException = true;
+ }
+
+ m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ if (bIllegalException || bRuntimeException)
+ {
+ m_pDoc->GetIDocumentUndoRedo().Undo();
+ if (bIllegalException)
+ {
+ lang::IllegalArgumentException aEx;
+ aEx.Message = sMessage;
+ throw aEx;
+ }
+ else
+ {
+ uno::RuntimeException aEx;
+ aEx.Message = sMessage;
+ throw aEx;
+ }
+ }
+ SwTextNode *const pTextNode( aPam.Start()->nNode.GetNode().GetTextNode() );
+ OSL_ENSURE(pTextNode, "no SwTextNode?");
+ if (pTextNode)
+ {
+ xRet.set(SwXParagraph::CreateXParagraph(*m_pDoc, pTextNode, &m_rThis),
+ uno::UNO_QUERY);
+ }
+
+ return xRet;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::insertTextPortion(
+ const OUString& rText,
+ const uno::Sequence< beans::PropertyValue > &
+ rCharacterAndParagraphProperties,
+ const uno::Reference<text::XTextRange>& xInsertPosition)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ {
+ throw uno::RuntimeException();
+ }
+ uno::Reference< text::XTextRange > xRet;
+ const uno::Reference<text::XTextCursor> xTextCursor = createTextCursorByRange(xInsertPosition);
+
+ const uno::Reference< lang::XUnoTunnel > xRangeTunnel(
+ xTextCursor, uno::UNO_QUERY_THROW );
+ SwXTextCursor *const pTextCursor =
+ comphelper::getFromUnoTunnel<SwXTextCursor>(xRangeTunnel);
+
+ bool bIllegalException = false;
+ bool bRuntimeException = false;
+ OUString sMessage;
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
+
+ auto& rCursor(pTextCursor->GetCursor());
+ m_pImpl->m_pDoc->DontExpandFormat( *rCursor.Start() );
+
+ if (!rText.isEmpty())
+ {
+ SwNodeIndex const nodeIndex(rCursor.GetPoint()->nNode, -1);
+ const sal_Int32 nContentPos = rCursor.GetPoint()->nContent.GetIndex();
+ SwUnoCursorHelper::DocInsertStringSplitCR(
+ *m_pImpl->m_pDoc, rCursor, rText, false);
+ SwUnoCursorHelper::SelectPam(rCursor, true);
+ rCursor.GetPoint()->nNode.Assign(nodeIndex.GetNode(), +1);
+ rCursor.GetPoint()->nContent = nContentPos;
+ }
+
+ try
+ {
+ SfxItemPropertySet const*const pCursorPropSet =
+ aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR);
+ SwUnoCursorHelper::SetPropertyValues(rCursor, *pCursorPropSet,
+ rCharacterAndParagraphProperties,
+ SetAttrMode::NOFORMATATTR);
+ }
+ catch (const lang::IllegalArgumentException& rIllegal)
+ {
+ sMessage = rIllegal.Message;
+ bIllegalException = true;
+ }
+ catch (const uno::RuntimeException& rRuntime)
+ {
+ sMessage = rRuntime.Message;
+ bRuntimeException = true;
+ }
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
+ if (bIllegalException || bRuntimeException)
+ {
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().Undo();
+ if (bIllegalException)
+ {
+ lang::IllegalArgumentException aEx;
+ aEx.Message = sMessage;
+ throw aEx;
+ }
+ else
+ {
+ uno::RuntimeException aEx;
+ aEx.Message = sMessage;
+ throw aEx;
+ }
+ }
+ xRet = new SwXTextRange(rCursor, this);
+ return xRet;
+}
+
+// Append text portions at the end of the last paragraph of the text interface.
+// Support of import filters.
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::appendTextPortion(
+ const OUString& rText,
+ const uno::Sequence< beans::PropertyValue > &
+ rCharacterAndParagraphProperties)
+{
+ // Right now this doesn't need a guard, as it's just calling the insert
+ // version, that has it already.
+ uno::Reference<text::XTextRange> xInsertPosition = getEnd();
+ return insertTextPortion(rText, rCharacterAndParagraphProperties, xInsertPosition);
+}
+
+// enable inserting/appending text contents like graphic objects, shapes and so on to
+// support import filters
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::insertTextContentWithProperties(
+ const uno::Reference< text::XTextContent >& xTextContent,
+ const uno::Sequence< beans::PropertyValue >&
+ rCharacterAndParagraphProperties,
+ const uno::Reference< text::XTextRange >& xInsertPosition)
+{
+ SolarMutexGuard aGuard;
+
+ if (!IsValid())
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (!::sw::XTextRangeToSwPaM(aPam, xInsertPosition))
+ {
+ throw lang::IllegalArgumentException("invalid position", nullptr, 2);
+ }
+
+ SwRewriter aRewriter;
+ aRewriter.AddRule(UndoArg1, SwResId(STR_UNDO_INSERT_TEXTBOX));
+
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, &aRewriter);
+
+ // Any direct formatting ending at the insert position (xRange) should not
+ // be expanded to cover the inserted content (xContent)
+ // (insertTextContent() shouldn't do this, only ...WithProperties()!)
+ GetDoc()->DontExpandFormat( *aPam.Start() );
+
+ // now attach the text content here
+ insertTextContent( xInsertPosition, xTextContent, false );
+ // now apply the properties to the anchor
+ if (rCharacterAndParagraphProperties.hasElements())
+ {
+ try
+ {
+ const uno::Reference< beans::XPropertySet > xAnchor(
+ xTextContent->getAnchor(), uno::UNO_QUERY);
+ if (xAnchor.is())
+ {
+ for (const auto& rProperty : rCharacterAndParagraphProperties)
+ {
+ xAnchor->setPropertyValue(rProperty.Name, rProperty.Value);
+ }
+ }
+ }
+ catch (const uno::Exception& e)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter);
+ throw lang::WrappedTargetRuntimeException( e.Message,
+ uno::Reference< uno::XInterface >(), anyEx );
+ }
+ }
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, &aRewriter);
+ return xInsertPosition;
+}
+
+uno::Reference< text::XTextRange > SAL_CALL
+SwXText::appendTextContent(
+ const uno::Reference< text::XTextContent >& xTextContent,
+ const uno::Sequence< beans::PropertyValue >& rCharacterAndParagraphProperties
+ )
+{
+ // Right now this doesn't need a guard, as it's just calling the insert
+ // version, that has it already.
+ uno::Reference<text::XTextRange> xInsertPosition = getEnd();
+ return insertTextContentWithProperties(xTextContent, rCharacterAndParagraphProperties, xInsertPosition);
+}
+
+// determine whether SwFrameFormat is a graphic node
+static bool isGraphicNode(const SwFrameFormat* pFrameFormat)
+{
+ // safety
+ if( !pFrameFormat->GetContent().GetContentIdx() )
+ {
+ return false;
+ }
+ auto index = *pFrameFormat->GetContent().GetContentIdx();
+ // consider the next node -> there is the graphic stored
+ index++;
+ return index.GetNode().IsGrfNode();
+}
+
+// move previously appended paragraphs into a text frames
+// to support import filters
+uno::Reference< text::XTextContent > SAL_CALL
+SwXText::convertToTextFrame(
+ const uno::Reference< text::XTextRange >& xStart,
+ const uno::Reference< text::XTextRange >& xEnd,
+ const uno::Sequence< beans::PropertyValue >& rFrameProperties)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ {
+ throw uno::RuntimeException();
+ }
+ // tdf#143384 recognize dummy property, that was set to make createTextCursor
+ // to not ignore tables.
+ // It is enough to use this hack only for the range start,
+ // because as far as I know, the range cannot end with table when this property is set.
+ ::sw::TextRangeMode eMode = ::sw::TextRangeMode::RequireTextNode;
+ for (const auto& rCellProperty : rFrameProperties)
+ {
+ if (rCellProperty.Name == "CursorNotIgnoreTables")
+ {
+ bool bAllowNonTextNode = false;
+ rCellProperty.Value >>= bAllowNonTextNode;
+ if (bAllowNonTextNode)
+ eMode = ::sw::TextRangeMode::AllowTableNode;
+ break;
+ }
+ }
+ uno::Reference< text::XTextContent > xRet;
+ std::optional<SwUnoInternalPaM> pTempStartPam(*GetDoc());
+ std::optional<SwUnoInternalPaM> pEndPam(*GetDoc());
+ if (!::sw::XTextRangeToSwPaM(*pTempStartPam, xStart, eMode)
+ || !::sw::XTextRangeToSwPaM(*pEndPam, xEnd))
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ auto pStartPam(GetDoc()->CreateUnoCursor(*pTempStartPam->GetPoint()));
+ if (pTempStartPam->HasMark())
+ {
+ pStartPam->SetMark();
+ *pStartPam->GetMark() = *pTempStartPam->GetMark();
+ }
+ pTempStartPam.reset();
+
+ SwXTextRange *const pStartRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xStart);
+ SwXTextRange *const pEndRange =
+ comphelper::getFromUnoTunnel<SwXTextRange>(xEnd);
+ // bookmarks have to be removed before the referenced text node
+ // is deleted in DelFullPara
+ if (pStartRange)
+ {
+ pStartRange->Invalidate();
+ }
+ if (pEndRange)
+ {
+ pEndRange->Invalidate();
+ }
+
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
+ bool bIllegalException = false;
+ bool bRuntimeException = false;
+ OUString sMessage;
+ SwStartNode* pStartStartNode = pStartPam->GetNode().StartOfSectionNode();
+ while (pStartStartNode && pStartStartNode->IsSectionNode())
+ {
+ pStartStartNode = pStartStartNode->StartOfSectionNode();
+ }
+ SwStartNode* pEndStartNode = pEndPam->GetNode().StartOfSectionNode();
+ while (pEndStartNode && pEndStartNode->IsSectionNode())
+ {
+ pEndStartNode = pEndStartNode->StartOfSectionNode();
+ }
+ bool bParaAfterInserted = false;
+ bool bParaBeforeInserted = false;
+ ::std::optional<SwPaM> oAnchorCheckPam;
+ oAnchorCheckPam.emplace(*pStartPam->Start(), *pEndPam->End());
+ if (
+ pStartStartNode && pEndStartNode &&
+ (pStartStartNode != pEndStartNode || pStartStartNode != GetStartNode())
+ )
+ {
+ // todo: if the start/end is in a table then insert a paragraph
+ // before/after, move the start/end nodes, then convert and
+ // remove the additional paragraphs in the end
+ SwTableNode * pStartTableNode(nullptr);
+ if (pStartStartNode->GetStartNodeType() == SwTableBoxStartNode)
+ {
+ pStartTableNode = pStartStartNode->FindTableNode();
+ // Is it the same table start node than the end?
+ SwTableNode *const pEndStartTableNode(pEndStartNode->FindTableNode());
+ while (pEndStartTableNode && pStartTableNode &&
+ pEndStartTableNode->GetIndex() < pStartTableNode->GetIndex())
+ {
+ SwStartNode* pStartStartTableNode = pStartTableNode->StartOfSectionNode();
+ pStartTableNode = pStartStartTableNode->FindTableNode();
+ }
+ }
+ if (pStartTableNode)
+ {
+ const SwNodeIndex aTableIdx( *pStartTableNode, -1 );
+ SwPosition aBefore(aTableIdx);
+ bParaBeforeInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aBefore );
+ pStartPam->DeleteMark();
+ *pStartPam->GetPoint() = aBefore;
+ pStartStartNode = pStartPam->GetNode().StartOfSectionNode();
+ }
+ if (pEndStartNode->GetStartNodeType() == SwTableBoxStartNode)
+ {
+ SwTableNode *const pEndTableNode = pEndStartNode->FindTableNode();
+ SwEndNode *const pTableEnd = pEndTableNode->EndOfSectionNode();
+ SwPosition aTableEnd(*pTableEnd);
+ bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aTableEnd );
+ pEndPam->DeleteMark();
+ *pEndPam->GetPoint() = aTableEnd;
+ pEndStartNode = pEndPam->GetNode().StartOfSectionNode();
+ }
+ // now we should have the positions in the same hierarchy
+ if ((pStartStartNode != pEndStartNode) ||
+ (pStartStartNode != GetStartNode()))
+ {
+ // if not - remove the additional paragraphs and throw
+ if (bParaBeforeInserted)
+ {
+ SwCursor aDelete(*pStartPam->GetPoint(), nullptr);
+ *pStartPam->GetPoint() = // park it because node is deleted
+ SwPosition(GetDoc()->GetNodes().GetEndOfContent());
+ aDelete.MovePara(GoCurrPara, fnParaStart);
+ aDelete.SetMark();
+ aDelete.MovePara(GoCurrPara, fnParaEnd);
+ GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
+ }
+ if (bParaAfterInserted)
+ {
+ SwCursor aDelete(*pEndPam->GetPoint(), nullptr);
+ *pEndPam->GetPoint() = // park it because node is deleted
+ SwPosition(GetDoc()->GetNodes().GetEndOfContent());
+ aDelete.MovePara(GoCurrPara, fnParaStart);
+ aDelete.SetMark();
+ aDelete.MovePara(GoCurrPara, fnParaEnd);
+ GetDoc()->getIDocumentContentOperations().DelFullPara(aDelete);
+ }
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ // make a selection from pStartPam to pEndPam
+ // If there is no content in the frame the shape is in
+ // it gets deleted in the DelFullPara call below,
+ // In this case insert a tmp text node ( we delete it later )
+ if (pStartPam->Start()->nNode == pEndPam->Start()->nNode
+ && pStartPam->End()->nNode == pEndPam->End()->nNode)
+ {
+ SwPosition aEnd(*pStartPam->End());
+ bParaAfterInserted = GetDoc()->getIDocumentContentOperations().AppendTextNode( aEnd );
+ pEndPam->DeleteMark();
+ *pEndPam->GetPoint() = aEnd;
+ *oAnchorCheckPam->End() = aEnd;
+ }
+ pStartPam->SetMark();
+ *pStartPam->End() = *pEndPam->End();
+ pEndPam.reset();
+
+ // see if there are frames already anchored to this node
+ // we have to work with the SdrObjects, as unique name is not guaranteed in their frame format
+ // tdf#115094: do nothing if we have a graphic node
+ o3tl::sorted_vector<const SdrObject*> aAnchoredObjectsByPtr;
+ std::set<OUString> aAnchoredObjectsByName;
+ for (size_t i = 0; i < m_pImpl->m_pDoc->GetSpzFrameFormats()->size(); ++i)
+ {
+ const SwFrameFormat* pFrameFormat = (*m_pImpl->m_pDoc->GetSpzFrameFormats())[i];
+ const SwFormatAnchor& rAnchor = pFrameFormat->GetAnchor();
+ // note: Word can do at-char anchors in text frames - sometimes!
+ // see testFlyInFly for why this checks only the edges of the selection,
+ // and testFloatingTablesAnchor for why it excludes pre/post table
+ // added nodes
+ if (!isGraphicNode(pFrameFormat)
+ && ( (RndStdIds::FLY_AT_PARA == rAnchor.GetAnchorId()
+ && ( oAnchorCheckPam->Start()->nNode.GetIndex() == rAnchor.GetContentAnchor()->nNode.GetIndex()
+ || oAnchorCheckPam->End()->nNode.GetIndex() == rAnchor.GetContentAnchor()->nNode.GetIndex()))
+ || (RndStdIds::FLY_AT_CHAR == rAnchor.GetAnchorId()
+ && ( *oAnchorCheckPam->Start() == *rAnchor.GetContentAnchor()
+ || *oAnchorCheckPam->End() == *rAnchor.GetContentAnchor()))))
+ {
+ if (pFrameFormat->GetName().isEmpty())
+ {
+ aAnchoredObjectsByPtr.insert(pFrameFormat->FindSdrObject());
+ }
+ else
+ {
+ aAnchoredObjectsByName.insert(pFrameFormat->GetName());
+ }
+ }
+ }
+ oAnchorCheckPam.reset(); // clear SwIndex before deleting nodes
+
+ const uno::Reference<text::XTextFrame> xNewFrame(
+ SwXTextFrame::CreateXTextFrame(*m_pImpl->m_pDoc, nullptr));
+ SwXTextFrame& rNewFrame = dynamic_cast<SwXTextFrame&>(*xNewFrame);
+ try
+ {
+ for (const beans::PropertyValue& rValue : rFrameProperties)
+ {
+ rNewFrame.SwXFrame::setPropertyValue(rValue.Name, rValue.Value);
+ }
+
+ { // has to be in a block to remove the SwIndexes before
+ // DelFullPara is called
+ const uno::Reference< text::XTextRange> xInsertTextRange =
+ new SwXTextRange(*pStartPam, this);
+ assert(rNewFrame.IsDescriptor());
+ rNewFrame.attachToRange(xInsertTextRange, pStartPam.get());
+ assert(!rNewFrame.getName().isEmpty());
+ }
+
+ SwTextNode *const pTextNode(pStartPam->GetNode().GetTextNode());
+ assert(pTextNode);
+ if (!pTextNode || !pTextNode->Len()) // don't remove if it contains text!
+ {
+ { // has to be in a block to remove the SwIndexes before
+ // DelFullPara is called
+ SwPaM aMovePam( pStartPam->GetNode() );
+ if (aMovePam.Move( fnMoveForward, GoInContent ))
+ {
+ // move the anchor to the next paragraph
+ SwFormatAnchor aNewAnchor(rNewFrame.GetFrameFormat()->GetAnchor());
+ aNewAnchor.SetAnchor( aMovePam.Start() );
+ m_pImpl->m_pDoc->SetAttr(
+ aNewAnchor, *rNewFrame.GetFrameFormat() );
+
+ // also move frames anchored to us
+ for (size_t i = 0; i < m_pImpl->m_pDoc->GetSpzFrameFormats()->size(); ++i)
+ {
+ SwFrameFormat* pFrameFormat = (*m_pImpl->m_pDoc->GetSpzFrameFormats())[i];
+ if ((!pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByName.find(pFrameFormat->GetName()) != aAnchoredObjectsByName.end() ) ||
+ ( pFrameFormat->GetName().isEmpty() && aAnchoredObjectsByPtr.find(pFrameFormat->FindSdrObject()) != aAnchoredObjectsByPtr.end()) )
+ {
+ // copy the anchor to the next paragraph
+ SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
+ aAnchor.SetAnchor(aMovePam.Start());
+ m_pImpl->m_pDoc->SetAttr(aAnchor, *pFrameFormat);
+ }
+ else
+ {
+ // if this frame is a textbox of a shape anchored to us, move this textbox too.
+ const auto& pTextBoxes = pFrameFormat->GetOtherTextBoxFormats();
+ if (pFrameFormat->Which() == RES_FLYFRMFMT && pTextBoxes
+ && pTextBoxes->GetOwnerShape())
+ {
+ const auto& rShapeAnchor = pTextBoxes->GetOwnerShape()->GetAnchor();
+ if (rShapeAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR
+ && rShapeAnchor.GetContentAnchor() && pFrameFormat->GetAnchor().GetContentAnchor()
+ && pStartPam->ContainsPosition(*pFrameFormat->GetAnchor().GetContentAnchor()))
+ {
+ const auto& rAnchorNode
+ = pFrameFormat->GetAnchor().GetContentAnchor()->nNode.GetNode();
+ if (!(rAnchorNode.FindFooterStartNode() || rAnchorNode.FindHeaderStartNode()))
+ {
+ SwFormatAnchor aAnchor(pFrameFormat->GetAnchor());
+ aAnchor.SetAnchor(aMovePam.Start());
+ m_pImpl->m_pDoc->SetAttr(aAnchor, *pFrameFormat);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pStartPam);
+ }
+ }
+ catch (const lang::IllegalArgumentException& rIllegal)
+ {
+ sMessage = rIllegal.Message;
+ bIllegalException = true;
+ }
+ catch (const uno::RuntimeException& rRuntime)
+ {
+ sMessage = rRuntime.Message;
+ bRuntimeException = true;
+ }
+ xRet = xNewFrame;
+ if (bParaBeforeInserted || bParaAfterInserted)
+ {
+ const uno::Reference<text::XTextCursor> xFrameTextCursor =
+ rNewFrame.createTextCursor();
+ SwXTextCursor *const pFrameCursor =
+ comphelper::getFromUnoTunnel<SwXTextCursor>(xFrameTextCursor);
+ if (bParaBeforeInserted)
+ {
+ // todo: remove paragraph before frame
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pFrameCursor->GetPaM());
+ }
+ if (bParaAfterInserted)
+ {
+ xFrameTextCursor->gotoEnd(false);
+ if (!bParaBeforeInserted)
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pFrameCursor->GetPaM());
+ else
+ {
+ // In case the frame has a table only, the cursor points to the end of the first cell of the table.
+ SwPaM aPaM(*pFrameCursor->GetPaM()->GetNode().FindSttNodeByType(SwFlyStartNode)->EndOfSectionNode());
+ // Now we have the end of the frame -- the node before that will be the paragraph we want to remove.
+ --aPaM.GetPoint()->nNode;
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(aPaM);
+ }
+ }
+ }
+
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::END, nullptr);
+ if (bIllegalException || bRuntimeException)
+ {
+ m_pImpl->m_pDoc->GetIDocumentUndoRedo().Undo();
+ if (bIllegalException)
+ {
+ lang::IllegalArgumentException aEx;
+ aEx.Message = sMessage;
+ throw aEx;
+ }
+ else
+ {
+ uno::RuntimeException aEx;
+ aEx.Message = sMessage;
+ throw aEx;
+ }
+ }
+ return xRet;
+}
+
+namespace {
+
+// Move previously imported paragraphs into a new text table.
+struct VerticallyMergedCell
+{
+ std::vector<uno::Reference< beans::XPropertySet > > aCells;
+ sal_Int32 nLeftPosition;
+ bool bOpen;
+
+ VerticallyMergedCell(uno::Reference< beans::XPropertySet > const& rxCell,
+ const sal_Int32 nLeft)
+ : nLeftPosition( nLeft )
+ , bOpen( true )
+ {
+ aCells.push_back( rxCell );
+ }
+};
+
+}
+
+#define COL_POS_FUZZY 2
+
+static bool lcl_SimilarPosition( const sal_Int32 nPos1, const sal_Int32 nPos2 )
+{
+ return abs( nPos1 - nPos2 ) < COL_POS_FUZZY;
+}
+
+void SwXText::Impl::ConvertCell(
+ const uno::Sequence< uno::Reference< text::XTextRange > > & rCell,
+ std::vector<SwNodeRange> & rRowNodes,
+ SwNodeRange *const pLastCell)
+{
+ if (rCell.getLength() != 2)
+ {
+ throw lang::IllegalArgumentException(
+ "rCell needs to contain 2 elements",
+ uno::Reference< text::XTextCopy >( &m_rThis ), sal_Int16( 2 ) );
+ }
+ const uno::Reference<text::XTextRange> xStartRange = rCell[0];
+ const uno::Reference<text::XTextRange> xEndRange = rCell[1];
+ SwUnoInternalPaM aStartCellPam(*m_pDoc);
+ SwUnoInternalPaM aEndCellPam(*m_pDoc);
+
+ // !!! TODO - PaMs in tables and sections do not work here -
+ // the same applies to PaMs in frames !!!
+
+ if (!::sw::XTextRangeToSwPaM(aStartCellPam, xStartRange) ||
+ !::sw::XTextRangeToSwPaM(aEndCellPam, xEndRange))
+ {
+ throw lang::IllegalArgumentException(
+ "Start or End range cannot be resolved to a SwPaM",
+ uno::Reference< text::XTextCopy >( &m_rThis ), sal_Int16( 2 ) );
+ }
+
+ SwNodeRange aTmpRange(aStartCellPam.Start()->nNode,
+ aEndCellPam.End()->nNode);
+ std::unique_ptr<SwNodeRange> pCorrectedRange =
+ m_pDoc->GetNodes().ExpandRangeForTableBox(aTmpRange);
+
+ if (pCorrectedRange)
+ {
+ SwPaM aNewStartPaM(pCorrectedRange->aStart, 0);
+ aStartCellPam = aNewStartPaM;
+
+ sal_Int32 nEndLen = 0;
+ SwTextNode * pTextNode = pCorrectedRange->aEnd.GetNode().GetTextNode();
+ if (pTextNode != nullptr)
+ nEndLen = pTextNode->Len();
+
+ SwPaM aNewEndPaM(pCorrectedRange->aEnd, nEndLen);
+ aEndCellPam = aNewEndPaM;
+
+ pCorrectedRange.reset();
+ }
+
+ /** check the nodes between start and end
+ it is allowed to have pairs of StartNode/EndNodes
+ */
+ if (aStartCellPam.Start()->nNode < aEndCellPam.End()->nNode)
+ {
+ // increment on each StartNode and decrement on each EndNode
+ // we must reach zero at the end and must not go below zero
+ tools::Long nOpenNodeBlock = 0;
+ SwNodeIndex aCellIndex = aStartCellPam.Start()->nNode;
+ while (aCellIndex < aEndCellPam.End()->nNode.GetIndex())
+ {
+ if (aCellIndex.GetNode().IsStartNode())
+ {
+ ++nOpenNodeBlock;
+ }
+ else if (aCellIndex.GetNode().IsEndNode())
+ {
+ --nOpenNodeBlock;
+ }
+ if (nOpenNodeBlock < 0)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ ++aCellIndex;
+ }
+ if (nOpenNodeBlock != 0)
+ {
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ /** The vector<vector> NodeRanges has to contain consecutive nodes.
+ In rTableRanges the ranges don't need to be full paragraphs but
+ they have to follow each other. To process the ranges they
+ have to be aligned on paragraph borders by inserting paragraph
+ breaks. Non-consecutive ranges must initiate an exception.
+ */
+ if (!pLastCell) // first cell?
+ {
+ // align the beginning - if necessary
+ if (aStartCellPam.Start()->nContent.GetIndex())
+ {
+ m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
+ }
+ }
+ else
+ {
+ // check the predecessor
+ const SwNodeOffset nStartCellNodeIndex =
+ aStartCellPam.Start()->nNode.GetIndex();
+ const SwNodeOffset nLastNodeEndIndex = pLastCell->aEnd.GetIndex();
+ if (nLastNodeEndIndex == nStartCellNodeIndex)
+ {
+ // same node as predecessor then equal nContent?
+ if (0 != aStartCellPam.Start()->nContent.GetIndex())
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ m_pDoc->getIDocumentContentOperations().SplitNode(*aStartCellPam.Start(), false);
+ SwNodeOffset const nNewIndex(aStartCellPam.Start()->nNode.GetIndex());
+ if (nNewIndex != nStartCellNodeIndex)
+ {
+ // aStartCellPam now points to the 2nd node
+ // the last cell may *also* point to 2nd node now - fix it!
+ assert(nNewIndex == nStartCellNodeIndex + 1);
+ if (pLastCell->aEnd.GetIndex() == nNewIndex)
+ {
+ --pLastCell->aEnd;
+ if (pLastCell->aStart.GetIndex() == nNewIndex)
+ {
+ --pLastCell->aStart;
+ }
+ }
+ }
+ }
+ else if (nStartCellNodeIndex == (nLastNodeEndIndex + 1))
+ {
+ // next paragraph - now the content index of the new should be 0
+ // and of the old one should be equal to the text length
+ // but if it isn't we don't care - the cell is being inserted on
+ // the node border anyway
+ }
+ else
+ {
+ throw lang::IllegalArgumentException();
+ }
+ }
+ // now check if there's a need to insert another paragraph break
+ if (aEndCellPam.End()->nContent.GetIndex() <
+ aEndCellPam.End()->nNode.GetNode().GetTextNode()->Len())
+ {
+ m_pDoc->getIDocumentContentOperations().SplitNode(*aEndCellPam.End(), false);
+ // take care that the new start/endcell is moved to the right position
+ // aStartCellPam has to point to the start of the new (previous) node
+ // aEndCellPam has to point to the end of the new (previous) node
+ aStartCellPam.DeleteMark();
+ aStartCellPam.Move(fnMoveBackward, GoInNode);
+ aStartCellPam.GetPoint()->nContent = 0;
+ aEndCellPam.DeleteMark();
+ aEndCellPam.Move(fnMoveBackward, GoInNode);
+ aEndCellPam.GetPoint()->nContent =
+ aEndCellPam.GetNode().GetTextNode()->Len();
+ }
+
+ assert(aStartCellPam.Start()->nContent.GetIndex() == 0);
+ assert(aEndCellPam.End()->nContent.GetIndex() == aEndCellPam.End()->nNode.GetNode().GetTextNode()->Len());
+ SwNodeRange aCellRange(aStartCellPam.Start()->nNode,
+ aEndCellPam.End()->nNode);
+ rRowNodes.push_back(aCellRange); // note: invalidates pLastCell!
+
+ // tdf#149649 delete any fieldmarks overlapping the cell
+ IDocumentMarkAccess & rIDMA(*m_pDoc->getIDocumentMarkAccess());
+ while (::sw::mark::IFieldmark *const pMark = rIDMA.getFieldmarkFor(*aStartCellPam.Start()))
+ {
+ if (pMark->GetMarkEnd() <= *aEndCellPam.End())
+ {
+ if (pMark->GetMarkStart() < *aStartCellPam.Start())
+ {
+ SAL_INFO("sw.uno", "deleting fieldmark overlapping table cell");
+ rIDMA.deleteMark(pMark);
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
+ if (*aStartCellPam.Start() <= sepPos && sepPos <= *aEndCellPam.End())
+ {
+ SAL_INFO("sw.uno", "deleting fieldmark with separator in table cell");
+ rIDMA.deleteMark(pMark);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ while (::sw::mark::IFieldmark *const pMark = rIDMA.getFieldmarkFor(*aEndCellPam.End()))
+ {
+ if (*aStartCellPam.Start() <= pMark->GetMarkStart())
+ {
+ if (*aEndCellPam.End() < pMark->GetMarkEnd())
+ {
+ SAL_INFO("sw.uno", "deleting fieldmark overlapping table cell");
+ rIDMA.deleteMark(pMark);
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ SwPosition const sepPos(::sw::mark::FindFieldSep(*pMark));
+ if (*aStartCellPam.Start() <= sepPos && sepPos <= *aEndCellPam.End())
+ {
+ SAL_INFO("sw.uno", "deleting fieldmark with separator in table cell");
+ rIDMA.deleteMark(pMark);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+}
+
+typedef uno::Sequence< text::TableColumnSeparator > TableColumnSeparators;
+
+static void
+lcl_ApplyRowProperties(
+ uno::Sequence<beans::PropertyValue> const& rRowProperties,
+ uno::Any const& rRow,
+ TableColumnSeparators & rRowSeparators)
+{
+ uno::Reference< beans::XPropertySet > xRow;
+ rRow >>= xRow;
+ for (const beans::PropertyValue& rProperty : rRowProperties)
+ {
+ if ( rProperty.Name == "TableColumnSeparators" )
+ {
+ // add the separators to access the cell's positions
+ // for vertical merging later
+ TableColumnSeparators aSeparators;
+ rProperty.Value >>= aSeparators;
+ rRowSeparators = aSeparators;
+ }
+ xRow->setPropertyValue(rProperty.Name, rProperty.Value);
+ }
+}
+
+static sal_Int32 lcl_GetLeftPos(sal_Int32 nCell, TableColumnSeparators const& rRowSeparators)
+{
+ if(!nCell)
+ return 0;
+ if (rRowSeparators.getLength() < nCell)
+ return -1;
+ return rRowSeparators[nCell - 1].Position;
+}
+
+static void
+lcl_ApplyCellProperties(
+ const sal_Int32 nLeftPos,
+ const uno::Sequence< beans::PropertyValue >& rCellProperties,
+ const uno::Reference< uno::XInterface >& xCell,
+ std::vector<VerticallyMergedCell> & rMergedCells)
+{
+ const uno::Reference< beans::XPropertySet > xCellPS(xCell, uno::UNO_QUERY);
+ for (const auto& rCellProperty : rCellProperties)
+ {
+ const OUString & rName = rCellProperty.Name;
+ const uno::Any & rValue = rCellProperty.Value;
+ if ( rName == "VerticalMerge" )
+ {
+ // determine left border position
+ // add the cell to a queue of merged cells
+ bool bMerge = false;
+ rValue >>= bMerge;
+ if (bMerge)
+ {
+ // 'close' all the cell with the same left position
+ // if separate vertical merges in the same column exist
+ for(auto& aMergedCell : rMergedCells)
+ {
+ if(lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
+ {
+ aMergedCell.bOpen = false;
+ }
+ }
+ // add the new group of merged cells
+ rMergedCells.emplace_back(xCellPS, nLeftPos);
+ }
+ else
+ {
+ bool bFound = false;
+ SAL_WARN_IF(rMergedCells.empty(), "sw.uno", "the first merged cell is missing");
+ for(auto& aMergedCell : rMergedCells)
+ {
+ if (aMergedCell.bOpen && lcl_SimilarPosition(aMergedCell.nLeftPosition, nLeftPos))
+ {
+ aMergedCell.aCells.push_back( xCellPS );
+ bFound = true;
+ }
+ }
+ SAL_WARN_IF(!bFound, "sw.uno", "couldn't find first vertically merged cell" );
+ }
+ }
+ else
+ {
+ try
+ {
+ xCellPS->setPropertyValue(rName, rValue);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting cell property " << rName );
+ }
+ }
+ }
+}
+
+static void
+lcl_MergeCells(std::vector<VerticallyMergedCell> & rMergedCells)
+{
+ for(auto& aMergedCell : rMergedCells)
+ {
+ // the first of the cells gets the number of cells set as RowSpan
+ // the others get the inverted number of remaining merged cells
+ // (3,-2,-1)
+ sal_Int32 nCellCount = static_cast<sal_Int32>(aMergedCell.aCells.size());
+ if(nCellCount<2)
+ {
+ SAL_WARN("sw.uno", "incomplete vertical cell merge");
+ continue;
+ }
+ aMergedCell.aCells.front()->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount--));
+ nCellCount*=-1;
+ for(auto pxPSet = aMergedCell.aCells.begin()+1; nCellCount<0; ++pxPSet, ++nCellCount)
+ (*pxPSet)->setPropertyValue(UNO_NAME_ROW_SPAN, uno::Any(nCellCount));
+ }
+}
+
+uno::Reference< text::XTextTable > SAL_CALL
+SwXText::convertToTable(
+ const uno::Sequence< uno::Sequence< uno::Sequence<
+ uno::Reference< text::XTextRange > > > >& rTableRanges,
+ const uno::Sequence< uno::Sequence< uno::Sequence<
+ beans::PropertyValue > > >& rCellProperties,
+ const uno::Sequence< uno::Sequence< beans::PropertyValue > >&
+ rRowProperties,
+ const uno::Sequence< beans::PropertyValue >& rTableProperties)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ {
+ throw uno::RuntimeException();
+ }
+
+ IDocumentRedlineAccess & rIDRA(m_pImpl->m_pDoc->getIDocumentRedlineAccess());
+ if (!IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags()))
+ {
+ throw uno::RuntimeException(
+ "cannot convertToTable if tracked changes are hidden!");
+ }
+
+ //at first collect the text ranges as SwPaMs
+ const uno::Sequence< uno::Sequence< uno::Reference< text::XTextRange > > >*
+ pTableRanges = rTableRanges.getConstArray();
+ std::vector< std::vector<SwNodeRange> > aTableNodes;
+ for (sal_Int32 nRow = 0; nRow < rTableRanges.getLength(); ++nRow)
+ {
+ std::vector<SwNodeRange> aRowNodes;
+ const uno::Sequence< uno::Reference< text::XTextRange > >* pRow =
+ pTableRanges[nRow].getConstArray();
+ const sal_Int32 nCells(pTableRanges[nRow].getLength());
+
+ if (0 == nCells) // this would lead to no pLastCell below
+ { // and make it impossible to detect node gaps
+ throw lang::IllegalArgumentException();
+ }
+
+ for (sal_Int32 nCell = 0; nCell < nCells; ++nCell)
+ {
+ SwNodeRange *const pLastCell(
+ (nCell == 0)
+ ? ((nRow == 0)
+ ? nullptr
+ : &*aTableNodes.rbegin()->rbegin())
+ : &*aRowNodes.rbegin());
+ m_pImpl->ConvertCell(pRow[nCell], aRowNodes, pLastCell);
+ }
+ assert(!aRowNodes.empty());
+ aTableNodes.push_back(aRowNodes);
+ }
+
+ std::vector< TableColumnSeparators >
+ aRowSeparators(rRowProperties.getLength());
+ std::vector<VerticallyMergedCell> aMergedCells;
+
+ SwTable const*const pTable = m_pImpl->m_pDoc->TextToTable( aTableNodes );
+
+ if (!pTable)
+ return uno::Reference< text::XTextTable >();
+
+ uno::Reference<text::XTextTable> const xRet =
+ SwXTextTable::CreateXTextTable(pTable->GetFrameFormat());
+ uno::Reference<beans::XPropertySet> const xPrSet(xRet, uno::UNO_QUERY);
+ // set properties to the table
+ // catch lang::WrappedTargetException and lang::IndexOutOfBoundsException
+ try
+ {
+ //apply table properties
+ for(const auto& rTableProperty : rTableProperties)
+ {
+ try
+ {
+ xPrSet->setPropertyValue(rTableProperty.Name, rTableProperty.Value);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.uno", "Exception when setting property: " << rTableProperty.Name );
+ }
+ }
+
+ //apply row properties
+ const auto xRows = xRet->getRows();
+ const sal_Int32 nLast = std::min(xRows->getCount(), rRowProperties.getLength());
+ SAL_WARN_IF(nLast != rRowProperties.getLength(), "sw.uno", "not enough rows for properties");
+ for(sal_Int32 nCnt = 0; nCnt < nLast; ++nCnt)
+ lcl_ApplyRowProperties(rRowProperties[nCnt], xRows->getByIndex(nCnt), aRowSeparators[nCnt]);
+
+ uno::Reference<table::XCellRange> const xCR(xRet, uno::UNO_QUERY_THROW);
+ //apply cell properties
+ sal_Int32 nRow = 0;
+ for(const auto& rCellPropertiesForRow : rCellProperties)
+ {
+ sal_Int32 nCell = 0;
+ for(const auto& rCellProps : rCellPropertiesForRow)
+ {
+ lcl_ApplyCellProperties(lcl_GetLeftPos(nCell, aRowSeparators[nRow]),
+ rCellProps,
+ xCR->getCellByPosition(nCell, nRow),
+ aMergedCells);
+ ++nCell;
+ }
+ ++nRow;
+ }
+
+ // now that the cell properties are set the vertical merge values
+ // have to be applied
+ lcl_MergeCells(aMergedCells);
+ }
+ catch (const lang::WrappedTargetException&)
+ {
+ }
+ catch (const lang::IndexOutOfBoundsException&)
+ {
+ }
+
+ assert(SwTable::FindTable(pTable->GetFrameFormat()) == pTable);
+ assert(pTable->GetFrameFormat() ==
+ dynamic_cast<SwXTextTable&>(*xRet).GetFrameFormat());
+ return xRet;
+}
+
+void SAL_CALL
+SwXText::copyText(
+ const uno::Reference< text::XTextCopy >& xSource )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<lang::XUnoTunnel> const xSourceTunnel(xSource,
+ uno::UNO_QUERY);
+ SwXText const* const pSource(comphelper::getFromUnoTunnel<SwXText>(xSourceTunnel));
+
+ uno::Reference< text::XText > const xText(xSource, uno::UNO_QUERY_THROW);
+ uno::Reference< text::XTextCursor > const xCursor =
+ xText->createTextCursor();
+ xCursor->gotoEnd( true );
+
+ uno::Reference< lang::XUnoTunnel > const xCursorTunnel(xCursor,
+ uno::UNO_QUERY_THROW);
+
+ OTextCursorHelper *const pCursor =
+ comphelper::getFromUnoTunnel<OTextCursorHelper>(xCursorTunnel);
+ if (!pCursor)
+ {
+ throw uno::RuntimeException();
+ }
+
+ SwNodeIndex rNdIndex( *GetStartNode( ), 1 );
+ SwPosition rPos( rNdIndex );
+ // tdf#112202 need SwXText because cursor cannot select table at the start
+ if (pSource)
+ {
+ SwTextNode * pFirstNode;
+ {
+ SwPaM temp(*pSource->GetStartNode(), *pSource->GetStartNode()->EndOfSectionNode(), SwNodeOffset(+1), SwNodeOffset(-1));
+ pFirstNode = temp.GetMark()->nNode.GetNode().GetTextNode();
+ if (pFirstNode)
+ {
+ pFirstNode->MakeStartIndex(&temp.GetMark()->nContent);
+ }
+ if (SwTextNode *const pNode = temp.GetPoint()->nNode.GetNode().GetTextNode())
+ {
+ pNode->MakeEndIndex(&temp.GetPoint()->nContent);
+ }
+ // Explicitly request copy text mode, so
+ // sw::DocumentContentOperationsManager::CopyFlyInFlyImpl() will copy shapes anchored to
+ // us, even if we have only a single paragraph.
+ m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(temp, rPos, SwCopyFlags::CheckPosInFly);
+ }
+ if (!pFirstNode)
+ { // the node at rPos was split; get rid of the first empty one so
+ // that the pasted table is first
+ auto pDelCursor(m_pImpl->m_pDoc->CreateUnoCursor(SwPosition(SwNodeIndex(*GetStartNode(), 1))));
+ m_pImpl->m_pDoc->getIDocumentContentOperations().DelFullPara(*pDelCursor);
+ }
+ }
+ else
+ {
+ m_pImpl->m_pDoc->getIDocumentContentOperations().CopyRange(*pCursor->GetPaM(), rPos, SwCopyFlags::CheckPosInFly);
+ }
+
+}
+
+SwXBodyText::SwXBodyText(SwDoc *const pDoc)
+ : SwXText(pDoc, CursorType::Body)
+{
+}
+
+SwXBodyText::~SwXBodyText()
+{
+}
+
+OUString SAL_CALL
+SwXBodyText::getImplementationName()
+{
+ return "SwXBodyText";
+}
+
+sal_Bool SAL_CALL SwXBodyText::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL
+SwXBodyText::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.Text" };
+}
+
+uno::Any SAL_CALL
+SwXBodyText::queryAggregation(const uno::Type& rType)
+{
+ uno::Any aRet;
+ if (rType == cppu::UnoType<container::XEnumerationAccess>::get())
+ {
+ aRet <<= uno::Reference< container::XEnumerationAccess >(this);
+ }
+ else if (rType == cppu::UnoType<container::XElementAccess>::get())
+ {
+ aRet <<= uno::Reference< container::XElementAccess >(this);
+ }
+ else if (rType == cppu::UnoType<lang::XServiceInfo>::get())
+ {
+ aRet <<= uno::Reference< lang::XServiceInfo >(this);
+ }
+ else
+ {
+ aRet = SwXText::queryInterface( rType );
+ }
+ if(aRet.getValueType() == cppu::UnoType<void>::get())
+ {
+ aRet = OWeakAggObject::queryAggregation( rType );
+ }
+ return aRet;
+}
+
+uno::Sequence< uno::Type > SAL_CALL
+SwXBodyText::getTypes()
+{
+ const uno::Sequence< uno::Type > aTypes = SwXBodyText_Base::getTypes();
+ const uno::Sequence< uno::Type > aTextTypes = SwXText::getTypes();
+ return ::comphelper::concatSequences(aTypes, aTextTypes);
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL
+SwXBodyText::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Any SAL_CALL
+SwXBodyText::queryInterface(const uno::Type& rType)
+{
+ const uno::Any ret = SwXText::queryInterface(rType);
+ return (ret.getValueType() == cppu::UnoType<void>::get())
+ ? SwXBodyText_Base::queryInterface(rType)
+ : ret;
+}
+
+rtl::Reference<SwXTextCursor> SwXBodyText::CreateTextCursor(const bool bIgnoreTables)
+{
+ if(!IsValid())
+ {
+ return nullptr;
+ }
+
+ // the cursor has to skip tables contained in this text
+ SwPaM aPam(GetDoc()->GetNodes().GetEndOfContent());
+ aPam.Move( fnMoveBackward, GoInDoc );
+ if (!bIgnoreTables)
+ {
+ SwTableNode * pTableNode = aPam.GetNode().FindTableNode();
+ SwContentNode * pCont = nullptr;
+ while (pTableNode)
+ {
+ aPam.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
+ pCont = GetDoc()->GetNodes().GoNext(&aPam.GetPoint()->nNode);
+ pTableNode = pCont->FindTableNode();
+ }
+ if (pCont)
+ {
+ aPam.GetPoint()->nContent.Assign(pCont, 0);
+ }
+ }
+ return new SwXTextCursor(*GetDoc(), this, CursorType::Body, *aPam.GetPoint());
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXBodyText::createTextCursor()
+{
+ SolarMutexGuard aGuard;
+
+ const uno::Reference< text::XTextCursor > xRef =
+ static_cast<text::XWordCursor*>(CreateTextCursor().get());
+ if (!xRef.is())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+ return xRef;
+}
+
+uno::Reference< text::XTextCursor > SAL_CALL
+SwXBodyText::createTextCursorByRange(
+ const uno::Reference< text::XTextRange > & xTextPosition)
+{
+ SolarMutexGuard aGuard;
+
+ if(!IsValid())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ uno::Reference< text::XTextCursor > aRef;
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (::sw::XTextRangeToSwPaM(aPam, xTextPosition))
+ {
+ if ( !aPam.GetNode().GetTextNode() )
+ throw uno::RuntimeException("Invalid text range" );
+
+ SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
+
+ SwStartNode* p1 = aPam.GetNode().StartOfSectionNode();
+ //document starts with a section?
+ while(p1->IsSectionNode())
+ {
+ p1 = p1->StartOfSectionNode();
+ }
+ SwStartNode *const p2 = rNode.StartOfSectionNode();
+
+ if(p1 == p2)
+ {
+ aRef = static_cast<text::XWordCursor*>(
+ new SwXTextCursor(*GetDoc(), this, CursorType::Body,
+ *aPam.GetPoint(), aPam.GetMark()));
+ }
+ }
+ if(!aRef.is())
+ {
+ throw uno::RuntimeException( "End of content node doesn't have the proper start node",
+ uno::Reference< uno::XInterface >( *this ) );
+ }
+ return aRef;
+}
+
+uno::Reference< container::XEnumeration > SAL_CALL
+SwXBodyText::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+
+ if (!IsValid())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ SwNode& rNode = GetDoc()->GetNodes().GetEndOfContent();
+ SwPosition aPos(rNode);
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
+ pUnoCursor->Move(fnMoveBackward, GoInDoc);
+ return SwXParagraphEnumeration::Create(this, pUnoCursor, CursorType::Body);
+}
+
+uno::Type SAL_CALL
+SwXBodyText::getElementType()
+{
+ return cppu::UnoType<text::XTextRange>::get();
+}
+
+sal_Bool SAL_CALL
+SwXBodyText::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ if (!IsValid())
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ return true;
+}
+
+class SwXHeadFootText::Impl
+ : public SvtListener
+{
+ public:
+ SwFrameFormat* m_pHeadFootFormat;
+ bool m_bIsHeader;
+
+ Impl(SwFrameFormat& rHeadFootFormat, const bool bIsHeader)
+ : m_pHeadFootFormat(&rHeadFootFormat)
+ , m_bIsHeader(bIsHeader)
+ {
+ StartListening(m_pHeadFootFormat->GetNotifier());
+ }
+
+ SwFrameFormat* GetHeadFootFormat() const {
+ return m_pHeadFootFormat;
+ }
+
+ SwFrameFormat& GetHeadFootFormatOrThrow() {
+ if (!m_pHeadFootFormat) {
+ throw uno::RuntimeException("SwXHeadFootText: disposed or invalid", nullptr);
+ }
+ return *m_pHeadFootFormat;
+ }
+ protected:
+ virtual void Notify(const SfxHint& rHint) override
+ {
+ if(rHint.GetId() == SfxHintId::Dying)
+ m_pHeadFootFormat = nullptr;
+ }
+};
+
+uno::Reference<text::XText> SwXHeadFootText::CreateXHeadFootText(
+ SwFrameFormat& rHeadFootFormat,
+ const bool bIsHeader)
+{
+ // re-use existing SwXHeadFootText
+ // #i105557#: do not iterate over the registered clients: race condition
+ uno::Reference<text::XText> xText(rHeadFootFormat.GetXObject(), uno::UNO_QUERY);
+ if(!xText.is())
+ {
+ xText = new SwXHeadFootText(rHeadFootFormat, bIsHeader);
+ rHeadFootFormat.SetXObject(xText);
+ }
+ return xText;
+}
+
+SwXHeadFootText::SwXHeadFootText(SwFrameFormat& rHeadFootFormat, const bool bIsHeader)
+ : SwXText(
+ rHeadFootFormat.GetDoc(),
+ bIsHeader ? CursorType::Header : CursorType::Footer)
+ , m_pImpl(new SwXHeadFootText::Impl(rHeadFootFormat, bIsHeader))
+{
+}
+
+SwXHeadFootText::~SwXHeadFootText()
+{ }
+
+OUString SAL_CALL
+SwXHeadFootText::getImplementationName()
+{
+ return {"SwXHeadFootText"};
+}
+
+sal_Bool SAL_CALL SwXHeadFootText::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL
+SwXHeadFootText::getSupportedServiceNames()
+{
+ return {"com.sun.star.text.Text"};
+}
+
+const SwStartNode* SwXHeadFootText::GetStartNode() const
+{
+ const SwStartNode* pSttNd = nullptr;
+ SwFrameFormat* const pHeadFootFormat = m_pImpl->GetHeadFootFormat();
+ if(pHeadFootFormat)
+ {
+ const SwFormatContent& rFlyContent = pHeadFootFormat->GetContent();
+ if(rFlyContent.GetContentIdx())
+ {
+ pSttNd = rFlyContent.GetContentIdx()->GetNode().GetStartNode();
+ }
+ }
+ return pSttNd;
+}
+
+uno::Reference<text::XTextCursor> SwXHeadFootText::CreateCursor()
+{
+ return createTextCursor();
+}
+
+uno::Sequence<uno::Type> SAL_CALL SwXHeadFootText::getTypes()
+{
+ return ::comphelper::concatSequences(
+ SwXHeadFootText_Base::getTypes(),
+ SwXText::getTypes());
+}
+
+uno::Sequence<sal_Int8> SAL_CALL SwXHeadFootText::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+uno::Any SAL_CALL SwXHeadFootText::queryInterface(const uno::Type& rType)
+{
+ const uno::Any ret = SwXHeadFootText_Base::queryInterface(rType);
+ return (ret.getValueType() == cppu::UnoType<void>::get())
+ ? SwXText::queryInterface(rType)
+ : ret;
+}
+
+uno::Reference<text::XTextCursor> SwXHeadFootText::CreateTextCursor(const bool bIgnoreTables)
+{
+ SolarMutexGuard aGuard;
+
+ SwFrameFormat & rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() );
+
+ const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent();
+ const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
+ SwPosition aPos(rNode);
+ rtl::Reference<SwXTextCursor> pXCursor = new SwXTextCursor(*GetDoc(), this,
+ (m_pImpl->m_bIsHeader) ? CursorType::Header : CursorType::Footer, aPos);
+ auto& rUnoCursor(pXCursor->GetCursor());
+ rUnoCursor.Move(fnMoveForward, GoInNode);
+
+ // save current start node to be able to check if there is content
+ // after the table - otherwise the cursor would be in the body text!
+ SwStartNode const*const pOwnStartNode = rNode.FindSttNodeByType(
+ (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
+
+ if (!bIgnoreTables)
+ {
+ // is there a table here?
+ SwTableNode* pTableNode = rUnoCursor.GetNode().FindTableNode();
+ SwContentNode* pCont = nullptr;
+ while (pTableNode)
+ {
+ rUnoCursor.GetPoint()->nNode = *pTableNode->EndOfSectionNode();
+ pCont = GetDoc()->GetNodes().GoNext(&rUnoCursor.GetPoint()->nNode);
+ pTableNode = pCont->FindTableNode();
+ }
+ if (pCont)
+ {
+ rUnoCursor.GetPoint()->nContent.Assign(pCont, 0);
+ }
+ }
+ SwStartNode const*const pNewStartNode = rUnoCursor.GetNode().FindSttNodeByType(
+ (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
+ if (!pNewStartNode || (pNewStartNode != pOwnStartNode))
+ {
+ uno::RuntimeException aExcept;
+ aExcept.Message = "no text available";
+ throw aExcept;
+ }
+ return static_cast<text::XWordCursor*>(pXCursor.get());
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL
+SwXHeadFootText::createTextCursor()
+{
+ return CreateTextCursor(false);
+}
+
+uno::Reference<text::XTextCursor> SAL_CALL SwXHeadFootText::createTextCursorByRange(
+ const uno::Reference<text::XTextRange>& xTextPosition)
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat& rHeadFootFormat( m_pImpl->GetHeadFootFormatOrThrow() );
+
+ SwUnoInternalPaM aPam(*GetDoc());
+ if (!sw::XTextRangeToSwPaM(aPam, xTextPosition))
+ {
+ uno::RuntimeException aRuntime;
+ aRuntime.Message = cInvalidObject;
+ throw aRuntime;
+ }
+
+ SwNode& rNode = rHeadFootFormat.GetContent().GetContentIdx()->GetNode();
+ SwPosition aPos(rNode);
+ SwPaM aHFPam(aPos);
+ aHFPam.Move(fnMoveForward, GoInNode);
+ SwStartNode* const pOwnStartNode = aHFPam.GetNode().FindSttNodeByType(
+ (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
+ SwStartNode* const p1 = aPam.GetNode().FindSttNodeByType(
+ (m_pImpl->m_bIsHeader) ? SwHeaderStartNode : SwFooterStartNode);
+ if (p1 == pOwnStartNode)
+ {
+ return static_cast<text::XWordCursor*>(
+ new SwXTextCursor(
+ *GetDoc(),
+ this,
+ (m_pImpl->m_bIsHeader) ? CursorType::Header : CursorType::Footer,
+ *aPam.GetPoint(), aPam.GetMark()));
+ }
+ return nullptr;
+}
+
+uno::Reference<container::XEnumeration> SAL_CALL SwXHeadFootText::createEnumeration()
+{
+ SolarMutexGuard aGuard;
+ SwFrameFormat& rHeadFootFormat(m_pImpl->GetHeadFootFormatOrThrow());
+
+ const SwFormatContent& rFlyContent = rHeadFootFormat.GetContent();
+ const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
+ SwPosition aPos(rNode);
+ auto pUnoCursor(GetDoc()->CreateUnoCursor(aPos));
+ pUnoCursor->Move(fnMoveForward, GoInNode);
+ return SwXParagraphEnumeration::Create(
+ this,
+ pUnoCursor,
+ (m_pImpl->m_bIsHeader)
+ ? CursorType::Header
+ : CursorType::Footer);
+}
+
+uno::Type SAL_CALL SwXHeadFootText::getElementType()
+ { return cppu::UnoType<text::XTextRange>::get(); }
+
+sal_Bool SAL_CALL SwXHeadFootText::hasElements()
+ { return true; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/unocore/unotextmarkup.cxx b/sw/source/core/unocore/unotextmarkup.cxx
new file mode 100644
index 000000000..0ea41f5f1
--- /dev/null
+++ b/sw/source/core/unocore/unotextmarkup.cxx
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <unotextmarkup.hxx>
+
+#include <comphelper/servicehelper.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <svl/listener.hxx>
+#include <vcl/svapp.hxx>
+#include <SwSmartTagMgr.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/text/TextMarkupType.hpp>
+#include <com/sun/star/text/TextMarkupDescriptor.hpp>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/XStringKeyMap.hpp>
+#include <ndtxt.hxx>
+#include <SwGrammarMarkUp.hxx>
+#include <TextCursorHelper.hxx>
+
+#include <IGrammarContact.hxx>
+
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+
+#include <pam.hxx>
+
+#include <unotextrange.hxx>
+#include <modeltoviewhelper.hxx>
+
+using namespace ::com::sun::star;
+
+struct SwXTextMarkup::Impl
+ : public SvtListener
+{
+ SwTextNode* m_pTextNode;
+ ModelToViewHelper const m_ConversionMap;
+
+ Impl(SwTextNode* const pTextNode, const ModelToViewHelper& rMap)
+ : m_pTextNode(pTextNode)
+ , m_ConversionMap(rMap)
+ {
+ if(m_pTextNode)
+ StartListening(pTextNode->GetNotifier());
+ }
+
+ virtual void Notify(const SfxHint& rHint) override;
+};
+
+SwXTextMarkup::SwXTextMarkup(
+ SwTextNode *const pTextNode, const ModelToViewHelper& rMap)
+ : m_pImpl(new Impl(pTextNode, rMap))
+{
+}
+
+SwXTextMarkup::~SwXTextMarkup()
+{
+}
+
+SwTextNode* SwXTextMarkup::GetTextNode()
+{
+ return m_pImpl->m_pTextNode;
+}
+
+void SwXTextMarkup::ClearTextNode()
+{
+ m_pImpl->m_pTextNode = nullptr;
+ m_pImpl->EndListeningAll();
+}
+
+const ModelToViewHelper& SwXTextMarkup::GetConversionMap() const
+{
+ return m_pImpl->m_ConversionMap;
+}
+
+uno::Reference< container::XStringKeyMap > SAL_CALL SwXTextMarkup::getMarkupInfoContainer()
+{
+ return new SwXStringKeyMap;
+}
+
+void SAL_CALL SwXTextMarkup::commitTextRangeMarkup(::sal_Int32 nType, const OUString & aIdentifier, const uno::Reference< text::XTextRange> & xRange,
+ const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference<lang::XUnoTunnel> xRangeTunnel( xRange, uno::UNO_QUERY);
+
+ if(!xRangeTunnel.is()) return;
+
+ if (auto pRange = comphelper::getFromUnoTunnel<SwXTextRange>(xRangeTunnel))
+ {
+ SwDoc& rDoc = pRange->GetDoc();
+
+ SwUnoInternalPaM aPam(rDoc);
+
+ ::sw::XTextRangeToSwPaM(aPam, xRange);
+
+ SwPosition* startPos = aPam.Start();
+ SwPosition* endPos = aPam.End();
+
+ commitStringMarkup (nType, aIdentifier, startPos->nContent.GetIndex(), endPos->nContent.GetIndex() - startPos->nContent.GetIndex(), xMarkupInfoContainer);
+ }
+ else if (auto pCursor = comphelper::getFromUnoTunnel<OTextCursorHelper>(xRangeTunnel))
+ {
+ SwPaM & rPam(*pCursor->GetPaM());
+
+ SwPosition* startPos = rPam.Start();
+ SwPosition* endPos = rPam.End();
+
+ commitStringMarkup (nType, aIdentifier, startPos->nContent.GetIndex(), endPos->nContent.GetIndex() - startPos->nContent.GetIndex(), xMarkupInfoContainer);
+ }
+}
+
+void SAL_CALL SwXTextMarkup::commitStringMarkup(
+ ::sal_Int32 nType,
+ const OUString & rIdentifier,
+ ::sal_Int32 nStart,
+ ::sal_Int32 nLength,
+ const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
+{
+ SolarMutexGuard aGuard;
+
+ // paragraph already dead or modified?
+ if (!m_pImpl->m_pTextNode || nLength <= 0)
+ return;
+
+ if ( nType == text::TextMarkupType::SMARTTAG &&
+ !SwSmartTagMgr::Get().IsSmartTagTypeEnabled( rIdentifier ) )
+ return;
+
+ // get appropriate list to use...
+ SwWrongList* pWList = nullptr;
+ bool bRepaint = false;
+ if ( nType == text::TextMarkupType::SPELLCHECK )
+ {
+ pWList = m_pImpl->m_pTextNode->GetWrong();
+ if ( !pWList )
+ {
+ pWList = new SwWrongList( WRONGLIST_SPELL );
+ m_pImpl->m_pTextNode->SetWrong( std::unique_ptr<SwWrongList>(pWList) );
+ }
+ }
+ else if ( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
+ {
+ IGrammarContact *pGrammarContact = getGrammarContact(*m_pImpl->m_pTextNode);
+ if( pGrammarContact )
+ {
+ pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true);
+ OSL_ENSURE( pWList, "GrammarContact _has_ to deliver a wrong list" );
+ }
+ else
+ {
+ pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
+ if ( !pWList )
+ {
+ m_pImpl->m_pTextNode->SetGrammarCheck( std::make_unique<SwGrammarMarkUp>() );
+ pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
+ }
+ }
+ bRepaint = pWList == m_pImpl->m_pTextNode->GetGrammarCheck();
+ if( pWList->GetBeginInv() < COMPLETE_STRING )
+ static_cast<SwGrammarMarkUp*>(pWList)->ClearGrammarList();
+ }
+ else if ( nType == text::TextMarkupType::SMARTTAG )
+ {
+ pWList = m_pImpl->m_pTextNode->GetSmartTags();
+ if ( !pWList )
+ {
+ pWList = new SwWrongList( WRONGLIST_SMARTTAG );
+ m_pImpl->m_pTextNode->SetSmartTags( std::unique_ptr<SwWrongList>(pWList) );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "Unknown mark-up type" );
+ return;
+ }
+
+ const ModelToViewHelper::ModelPosition aStartPos =
+ m_pImpl->m_ConversionMap.ConvertToModelPosition( nStart );
+ const ModelToViewHelper::ModelPosition aEndPos =
+ m_pImpl->m_ConversionMap.ConvertToModelPosition( nStart + nLength - 1);
+
+ const bool bStartInField = aStartPos.mbIsField;
+ const bool bEndInField = aEndPos.mbIsField;
+ bool bCommit = false;
+
+ if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos )
+ {
+ nStart = aStartPos.mnSubPos;
+ const sal_Int32 nFieldPosModel = aStartPos.mnPos;
+ const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
+
+ SwWrongList* pSubList = pWList->SubList( nInsertPos );
+ if ( !pSubList )
+ {
+ if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
+ pSubList = new SwGrammarMarkUp();
+ else
+ pSubList = new SwWrongList( pWList->GetWrongListType() );
+ pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
+ }
+
+ pWList = pSubList;
+ bCommit = true;
+ }
+ else if ( !bStartInField && !bEndInField )
+ {
+ nStart = aStartPos.mnPos;
+ bCommit = true;
+ nLength = aEndPos.mnPos + 1 - aStartPos.mnPos;
+ }
+ else if( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE )
+ {
+ bCommit = true;
+ nStart = aStartPos.mnPos;
+ sal_Int32 nEnd = aEndPos.mnPos;
+ if( bStartInField && nType != text::TextMarkupType::SENTENCE )
+ {
+ const sal_Int32 nFieldPosModel = aStartPos.mnPos;
+ const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
+ SwWrongList* pSubList = pWList->SubList( nInsertPos );
+ if ( !pSubList )
+ {
+ pSubList = new SwGrammarMarkUp();
+ pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
+ }
+ const sal_Int32 nTmpStart =
+ m_pImpl->m_ConversionMap.ConvertToViewPosition(aStartPos.mnPos);
+ const sal_Int32 nTmpLen =
+ m_pImpl->m_ConversionMap.ConvertToViewPosition(aStartPos.mnPos + 1)
+ - nTmpStart - aStartPos.mnSubPos;
+ if( nTmpLen > 0 )
+ {
+ pSubList->Insert( rIdentifier, xMarkupInfoContainer, aStartPos.mnSubPos, nTmpLen );
+ }
+ ++nStart;
+ }
+ if( bEndInField && nType != text::TextMarkupType::SENTENCE )
+ {
+ const sal_Int32 nFieldPosModel = aEndPos.mnPos;
+ const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
+ SwWrongList* pSubList = pWList->SubList( nInsertPos );
+ if ( !pSubList )
+ {
+ pSubList = new SwGrammarMarkUp();
+ pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
+ }
+ const sal_Int32 nTmpLen = aEndPos.mnSubPos + 1;
+ pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, nTmpLen );
+ }
+ else
+ ++nEnd;
+ if( nEnd > nStart )
+ nLength = nEnd - nStart;
+ else
+ bCommit = false;
+ }
+
+ if ( bCommit )
+ {
+ if( nType == text::TextMarkupType::SENTENCE )
+ static_cast<SwGrammarMarkUp*>(pWList)->setSentence( nStart );
+ else
+ pWList->Insert( rIdentifier, xMarkupInfoContainer, nStart, nLength );
+ }
+
+ if( bRepaint )
+ finishGrammarCheck(*m_pImpl->m_pTextNode);
+}
+
+static void lcl_commitGrammarMarkUp(
+ const ModelToViewHelper& rConversionMap,
+ SwGrammarMarkUp* pWList,
+ ::sal_Int32 nType,
+ const OUString & rIdentifier,
+ ::sal_Int32 nStart,
+ ::sal_Int32 nLength,
+ const uno::Reference< container::XStringKeyMap > & xMarkupInfoContainer)
+{
+ OSL_ENSURE( nType == text::TextMarkupType::PROOFREADING || nType == text::TextMarkupType::SENTENCE, "Wrong mark-up type" );
+ const ModelToViewHelper::ModelPosition aStartPos =
+ rConversionMap.ConvertToModelPosition( nStart );
+ const ModelToViewHelper::ModelPosition aEndPos =
+ rConversionMap.ConvertToModelPosition( nStart + nLength - 1);
+
+ const bool bStartInField = aStartPos.mbIsField;
+ const bool bEndInField = aEndPos.mbIsField;
+ bool bCommit = false;
+
+ if ( bStartInField && bEndInField && aStartPos.mnPos == aEndPos.mnPos )
+ {
+ nStart = aStartPos.mnSubPos;
+ const sal_Int32 nFieldPosModel = aStartPos.mnPos;
+ const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
+
+ SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos ));
+ if ( !pSubList )
+ {
+ pSubList = new SwGrammarMarkUp();
+ pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
+ }
+
+ pWList = pSubList;
+ bCommit = true;
+ }
+ else if ( !bStartInField && !bEndInField )
+ {
+ nStart = aStartPos.mnPos;
+ bCommit = true;
+ nLength = aEndPos.mnPos + 1 - aStartPos.mnPos;
+ }
+ else
+ {
+ bCommit = true;
+ nStart = aStartPos.mnPos;
+ sal_Int32 nEnd = aEndPos.mnPos;
+ if( bStartInField && nType != text::TextMarkupType::SENTENCE )
+ {
+ const sal_Int32 nFieldPosModel = aStartPos.mnPos;
+ const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
+ SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos ));
+ if ( !pSubList )
+ {
+ pSubList = new SwGrammarMarkUp();
+ pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
+ }
+ const sal_Int32 nTmpStart = rConversionMap.ConvertToViewPosition( aStartPos.mnPos );
+ const sal_Int32 nTmpLen = rConversionMap.ConvertToViewPosition( aStartPos.mnPos + 1 )
+ - nTmpStart - aStartPos.mnSubPos;
+ if( nTmpLen > 0 )
+ pSubList->Insert( rIdentifier, xMarkupInfoContainer, aStartPos.mnSubPos, nTmpLen );
+ ++nStart;
+ }
+ if( bEndInField && nType != text::TextMarkupType::SENTENCE )
+ {
+ const sal_Int32 nFieldPosModel = aEndPos.mnPos;
+ const sal_uInt16 nInsertPos = pWList->GetWrongPos( nFieldPosModel );
+ SwGrammarMarkUp* pSubList = static_cast<SwGrammarMarkUp*>(pWList->SubList( nInsertPos ));
+ if ( !pSubList )
+ {
+ pSubList = new SwGrammarMarkUp();
+ pWList->InsertSubList( nFieldPosModel, 1, nInsertPos, pSubList );
+ }
+ const sal_Int32 nTmpLen = aEndPos.mnSubPos + 1;
+ pSubList->Insert( rIdentifier, xMarkupInfoContainer, 0, nTmpLen );
+ }
+ else
+ ++nEnd;
+ if( nEnd > nStart )
+ nLength = nEnd - nStart;
+ else
+ bCommit = false;
+ }
+
+ if ( bCommit )
+ {
+ if( nType == text::TextMarkupType::SENTENCE )
+ pWList->setSentence( nStart+nLength );
+ else
+ pWList->Insert( rIdentifier, xMarkupInfoContainer, nStart, nLength );
+ }
+}
+
+void SAL_CALL SwXTextMarkup::commitMultiTextMarkup(
+ const uno::Sequence< text::TextMarkupDescriptor > &rMarkups )
+{
+ SolarMutexGuard aGuard;
+
+ // paragraph already dead or modified?
+ if (!m_pImpl->m_pTextNode)
+ return;
+
+ // for grammar checking there should be exactly one sentence markup
+ // and 0..n grammar markups.
+ // Different markups are not expected but may be applied anyway since
+ // that should be no problem...
+ // but it has to be implemented, at the moment only this function is for
+ // grammar markups and sentence markup only!
+ const text::TextMarkupDescriptor *pSentenceMarkUp = nullptr;
+ for( const text::TextMarkupDescriptor &rDesc : rMarkups )
+ {
+ if (rDesc.nType == text::TextMarkupType::SENTENCE)
+ {
+ if (pSentenceMarkUp != nullptr)
+ throw lang::IllegalArgumentException(); // there is already one sentence markup
+ pSentenceMarkUp = &rDesc;
+ }
+ else if( rDesc.nType != text::TextMarkupType::PROOFREADING )
+ return;
+ }
+
+ if( pSentenceMarkUp == nullptr )
+ return;
+
+ // get appropriate list to use...
+ SwGrammarMarkUp* pWList = nullptr;
+ bool bRepaint = false;
+ IGrammarContact *pGrammarContact = getGrammarContact(*m_pImpl->m_pTextNode);
+ if( pGrammarContact )
+ {
+ pWList = pGrammarContact->getGrammarCheck(*m_pImpl->m_pTextNode, true);
+ OSL_ENSURE( pWList, "GrammarContact _has_ to deliver a wrong list" );
+ }
+ else
+ {
+ pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
+ if ( !pWList )
+ {
+ m_pImpl->m_pTextNode->SetGrammarCheck( std::make_unique<SwGrammarMarkUp>() );
+ pWList = m_pImpl->m_pTextNode->GetGrammarCheck();
+ pWList->SetInvalid( 0, COMPLETE_STRING );
+ }
+ }
+ bRepaint = pWList == m_pImpl->m_pTextNode->GetGrammarCheck();
+
+ bool bAcceptGrammarError = false;
+ if( pWList->GetBeginInv() < COMPLETE_STRING )
+ {
+ const ModelToViewHelper::ModelPosition aSentenceEnd =
+ m_pImpl->m_ConversionMap.ConvertToModelPosition(
+ pSentenceMarkUp->nOffset + pSentenceMarkUp->nLength );
+ bAcceptGrammarError = aSentenceEnd.mnPos > pWList->GetBeginInv();
+ pWList->ClearGrammarList( aSentenceEnd.mnPos );
+ }
+
+ if( bAcceptGrammarError )
+ {
+ for( const text::TextMarkupDescriptor &rDesc : rMarkups )
+ {
+ lcl_commitGrammarMarkUp(m_pImpl->m_ConversionMap, pWList, rDesc.nType,
+ rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer );
+ }
+ }
+ else
+ {
+ bRepaint = false;
+ const text::TextMarkupDescriptor &rDesc = *pSentenceMarkUp;
+ lcl_commitGrammarMarkUp(m_pImpl->m_ConversionMap, pWList, rDesc.nType,
+ rDesc.aIdentifier, rDesc.nOffset, rDesc.nLength, rDesc.xMarkupInfoContainer );
+ }
+
+ if( bRepaint )
+ finishGrammarCheck(*m_pImpl->m_pTextNode);
+}
+
+void SwXTextMarkup::Impl::Notify(const SfxHint& rHint)
+{
+ DBG_TESTSOLARMUTEX();
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ m_pTextNode = nullptr;
+ }
+}
+
+SwXStringKeyMap::SwXStringKeyMap()
+{
+}
+
+uno::Any SAL_CALL SwXStringKeyMap::getValue(const OUString & aKey)
+{
+ std::map< OUString, uno::Any >::const_iterator aIter = maMap.find( aKey );
+ if ( aIter == maMap.end() )
+ throw container::NoSuchElementException();
+
+ return (*aIter).second;
+}
+
+sal_Bool SAL_CALL SwXStringKeyMap::hasValue(const OUString & aKey)
+{
+ return maMap.find( aKey ) != maMap.end();
+}
+
+void SAL_CALL SwXStringKeyMap::insertValue(const OUString & aKey, const uno::Any & aValue)
+{
+ std::map< OUString, uno::Any >::const_iterator aIter = maMap.find( aKey );
+ if ( aIter != maMap.end() )
+ throw container::ElementExistException();
+
+ maMap[ aKey ] = aValue;
+}
+
+::sal_Int32 SAL_CALL SwXStringKeyMap::getCount()
+{
+ return maMap.size();
+}
+
+OUString SAL_CALL SwXStringKeyMap::getKeyByIndex(::sal_Int32 nIndex)
+{
+ if ( o3tl::make_unsigned(nIndex) >= maMap.size() )
+ throw lang::IndexOutOfBoundsException();
+
+ return OUString();
+}
+
+uno::Any SAL_CALL SwXStringKeyMap::getValueByIndex(::sal_Int32 nIndex)
+{
+ if ( o3tl::make_unsigned(nIndex) >= maMap.size() )
+ throw lang::IndexOutOfBoundsException();
+
+ return uno::Any();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/dialoghelp.cxx b/sw/source/core/view/dialoghelp.cxx
new file mode 100644
index 000000000..88d12484d
--- /dev/null
+++ b/sw/source/core/view/dialoghelp.cxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <vcl/weld.hxx>
+
+#include <dialoghelp.hxx>
+#include <doc.hxx>
+#include <docsh.hxx>
+#include <view.hxx>
+
+weld::Window* GetFrameWeld(const SfxFrame* pFrame)
+{
+ return pFrame ? pFrame->GetWindow().GetFrameWeld() : nullptr;
+}
+
+weld::Window* GetFrameWeld(const SfxMedium* pMedium)
+{
+ return GetFrameWeld(pMedium ? pMedium->GetLoadTargetFrame() : nullptr);
+}
+
+weld::Window* GetFrameWeld(SwDocShell* pDocShell)
+{
+ if (!pDocShell)
+ return nullptr;
+ weld::Window* pRet = GetFrameWeld(pDocShell->GetMedium());
+ if (!pRet)
+ {
+ if (SwView* pView = pDocShell->GetView())
+ pRet = pView->GetFrameWeld();
+ }
+ return pRet;
+}
+
+weld::Window* GetFrameWeld(SwDoc* pDoc)
+{
+ return GetFrameWeld(pDoc ? pDoc->GetDocShell() : nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/sw/source/core/view/pagepreviewlayout.cxx b/sw/source/core/view/pagepreviewlayout.cxx
new file mode 100644
index 000000000..5fc740ebf
--- /dev/null
+++ b/sw/source/core/view/pagepreviewlayout.cxx
@@ -0,0 +1,1465 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <pagepreviewlayout.hxx>
+#include <prevwpage.hxx>
+
+#include <algorithm>
+#include <osl/diagnose.h>
+#include <tools/fract.hxx>
+#include <vcl/settings.hxx>
+
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewsh.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <swregion.hxx>
+#include <strings.hrc>
+#include <frmtool.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <printdata.hxx>
+#include <paintfrm.hxx>
+
+#include <IDocumentDeviceAccess.hxx>
+
+// methods to initialize page preview layout
+
+SwPagePreviewLayout::SwPagePreviewLayout( SwViewShell& _rParentViewShell,
+ const SwRootFrame& _rLayoutRootFrame )
+ : mrParentViewShell( _rParentViewShell ),
+ mrLayoutRootFrame ( _rLayoutRootFrame )
+{
+ Clear_();
+
+ mbBookPreview = false;
+ mbBookPreviewModeToggled = false;
+
+ mbPrintEmptyPages = mrParentViewShell.getIDocumentDeviceAccess().getPrintData().IsPrintEmptyPages();
+}
+
+void SwPagePreviewLayout::Clear_()
+{
+ mbLayoutInfoValid = mbLayoutSizesValid = mbPaintInfoValid = false;
+
+ maWinSize.setWidth( 0 );
+ maWinSize.setHeight( 0 );
+ mnCols = mnRows = 0;
+
+ ClearPreviewLayoutSizes();
+
+ mbDoesLayoutRowsFitIntoWindow = false;
+ mbDoesLayoutColsFitIntoWindow = false;
+
+ mnPaintPhyStartPageNum = 0;
+ mnPaintStartCol = mnPaintStartRow = 0;
+ mbNoPageVisible = false;
+ maPaintStartPageOffset.setX( 0 );
+ maPaintStartPageOffset.setY( 0 );
+ maPaintPreviewDocOffset.setX( 0 );
+ maPaintPreviewDocOffset.setY( 0 );
+ maAdditionalPaintOffset.setX( 0 );
+ maAdditionalPaintOffset.setY( 0 );
+ maPaintedPreviewDocRect.SetLeft( 0 );
+ maPaintedPreviewDocRect.SetTop( 0 );
+ maPaintedPreviewDocRect.SetRight( 0 );
+ maPaintedPreviewDocRect.SetBottom( 0 );
+ mnSelectedPageNum = 0;
+ ClearPreviewPageData();
+
+ mbInPaint = false;
+ mbNewLayoutDuringPaint = false;
+}
+
+void SwPagePreviewLayout::ClearPreviewLayoutSizes()
+{
+ mnPages = 0;
+
+ maMaxPageSize.setWidth( 0 );
+ maMaxPageSize.setHeight( 0 );
+ maPreviewDocRect.SetLeft( 0 );
+ maPreviewDocRect.SetTop( 0 );
+ maPreviewDocRect.SetRight( 0 );
+ maPreviewDocRect.SetBottom( 0 );
+ mnColWidth = mnRowHeight = 0;
+ mnPreviewLayoutWidth = mnPreviewLayoutHeight = 0;
+}
+
+void SwPagePreviewLayout::ClearPreviewPageData()
+{
+ maPreviewPages.clear();
+}
+
+/** calculate page preview layout sizes
+
+*/
+void SwPagePreviewLayout::CalcPreviewLayoutSizes()
+{
+ vcl::RenderContext* pRenderContext = mrParentViewShell.GetOut();
+ // calculate maximal page size; calculate also number of pages
+
+ const SwPageFrame* pPage = static_cast<const SwPageFrame*>(mrLayoutRootFrame.Lower());
+ while ( pPage )
+ {
+ if ( !mbBookPreview && !mbPrintEmptyPages && pPage->IsEmptyPage() )
+ {
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ continue;
+ }
+
+ ++mnPages;
+ pPage->Calc(pRenderContext);
+ const Size& rPageSize = pPage->getFrameArea().SSize();
+ if ( rPageSize.Width() > maMaxPageSize.Width() )
+ maMaxPageSize.setWidth( rPageSize.Width() );
+ if ( rPageSize.Height() > maMaxPageSize.Height() )
+ maMaxPageSize.setHeight( rPageSize.Height() );
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ // calculate and set column width and row height
+ mnColWidth = maMaxPageSize.Width() + gnXFree;
+ mnRowHeight = maMaxPageSize.Height() + gnYFree;
+
+ // calculate and set preview layout width and height
+ mnPreviewLayoutWidth = mnCols * mnColWidth + gnXFree;
+ mnPreviewLayoutHeight = mnRows * mnRowHeight + gnYFree;
+
+ // calculate document rectangle in preview layout
+ {
+ Size aDocSize;
+ // document width
+ aDocSize.setWidth( mnPreviewLayoutWidth );
+
+ // document height
+ // determine number of rows needed for <nPages> in preview layout
+ // use method <GetRowOfPage(..)>.
+ const sal_uInt16 nDocRows = GetRowOfPage( mnPages );
+ aDocSize.setHeight( nDocRows * maMaxPageSize.Height() +
+ (nDocRows+1) * gnYFree );
+ maPreviewDocRect.SetPos( Point( 0, 0 ) );
+ maPreviewDocRect.SetSize( aDocSize );
+ }
+}
+
+/** init page preview layout
+
+ initialize the page preview settings for a given layout.
+
+ side effects:
+ (1) If parameter <_bCalcScale> is true, mapping mode with calculated
+ scaling is set at the output device and the zoom at the view options of
+ the given view shell is set with the calculated scaling.
+*/
+void SwPagePreviewLayout::Init( const sal_uInt16 _nCols,
+ const sal_uInt16 _nRows,
+ const Size& _rPxWinSize
+ )
+{
+ // check environment and parameters
+ {
+ bool bColsRowsValid = (_nCols != 0) && (_nRows != 0);
+ OSL_ENSURE( bColsRowsValid, "preview layout parameters not correct - preview layout can *not* be initialized" );
+ if ( !bColsRowsValid )
+ return;
+
+ bool bPxWinSizeValid = (_rPxWinSize.Width() >= 0) &&
+ (_rPxWinSize.Height() >= 0);
+ OSL_ENSURE( bPxWinSizeValid, "no window size - preview layout can *not* be initialized" );
+ if ( !bPxWinSizeValid )
+ return;
+ }
+
+ // environment and parameters ok
+
+ // clear existing preview settings
+ Clear_();
+
+ // set layout information columns and rows
+ mnCols = _nCols;
+ mnRows = _nRows;
+
+ CalcPreviewLayoutSizes();
+
+ // validate layout information
+ mbLayoutInfoValid = true;
+
+ // calculate scaling
+ MapMode aMapMode( MapUnit::MapTwip );
+ Size aWinSize = mrParentViewShell.GetOut()->PixelToLogic( _rPxWinSize, aMapMode );
+ Fraction aXScale( aWinSize.Width(), mnPreviewLayoutWidth );
+ Fraction aYScale( aWinSize.Height(), mnPreviewLayoutHeight );
+ if( aXScale < aYScale )
+ aYScale = aXScale;
+ {
+ // adjust scaling for Drawing layer.
+ aYScale *= Fraction( 1000, 1 );
+ tools::Long nNewNuminator = aYScale.operator long();
+ if( nNewNuminator < 1 )
+ nNewNuminator = 1;
+ aYScale = Fraction( nNewNuminator, 1000 );
+ // propagate scaling as zoom percentage to view options for font cache
+ ApplyNewZoomAtViewShell( static_cast<sal_uInt8>(nNewNuminator/10) );
+
+ aMapMode.SetScaleY( aYScale );
+ aMapMode.SetScaleX( aYScale );
+ // set created mapping mode with calculated scaling at output device.
+ mrParentViewShell.GetOut()->SetMapMode( aMapMode );
+ // update statics for paint.
+ ::SwCalcPixStatics( mrParentViewShell.GetOut() );
+ }
+
+ // set window size in twips
+ maWinSize = mrParentViewShell.GetOut()->PixelToLogic( _rPxWinSize );
+ // validate layout sizes
+ mbLayoutSizesValid = true;
+}
+
+/** apply new zoom at given view shell */
+void SwPagePreviewLayout::ApplyNewZoomAtViewShell( sal_uInt8 _aNewZoom )
+{
+ SwViewOption aNewViewOptions = *(mrParentViewShell.GetViewOptions());
+ if ( aNewViewOptions.GetZoom() != _aNewZoom )
+ {
+ aNewViewOptions.SetZoom( _aNewZoom );
+ //#i19975# - consider zoom type.
+ aNewViewOptions.SetZoomType( SvxZoomType::PERCENT );
+ mrParentViewShell.ApplyViewOptions( aNewViewOptions );
+ }
+}
+
+/** method to adjust page preview layout to document changes
+
+*/
+void SwPagePreviewLayout::ReInit()
+{
+ // check environment and parameters
+ {
+ bool bLayoutSettingsValid = mbLayoutInfoValid && mbLayoutSizesValid;
+ OSL_ENSURE( bLayoutSettingsValid,
+ "no valid preview layout info/sizes - no re-init of page preview layout");
+ if ( !bLayoutSettingsValid )
+ return;
+ }
+
+ ClearPreviewLayoutSizes();
+ CalcPreviewLayoutSizes();
+}
+
+// methods to prepare paint of page preview
+
+/** prepare paint of page preview
+
+ delete parameter _onStartPageVirtNum
+
+ @note _nProposedStartPageNum, _onStartPageNum are absolute
+*/
+bool SwPagePreviewLayout::Prepare( const sal_uInt16 _nProposedStartPageNum,
+ const Point& rProposedStartPos,
+ const Size& _rPxWinSize,
+ sal_uInt16& _onStartPageNum,
+ tools::Rectangle& _orDocPreviewPaintRect,
+ const bool _bStartWithPageAtFirstCol
+ )
+{
+ sal_uInt16 nProposedStartPageNum = ConvertAbsoluteToRelativePageNum( _nProposedStartPageNum );
+ // check environment and parameters
+ {
+ bool bLayoutSettingsValid = mbLayoutInfoValid && mbLayoutSizesValid;
+ OSL_ENSURE( bLayoutSettingsValid,
+ "no valid preview layout info/sizes - no prepare of preview paint");
+ if ( !bLayoutSettingsValid )
+ return false;
+
+ bool bStartPageRangeValid = nProposedStartPageNum <= mnPages;
+ OSL_ENSURE( bStartPageRangeValid,
+ "proposed start page not existing - no prepare of preview paint");
+ if ( !bStartPageRangeValid )
+ return false;
+
+ bool bStartPosRangeValid =
+ rProposedStartPos.X() >= 0 && rProposedStartPos.Y() >= 0 &&
+ rProposedStartPos.X() <= maPreviewDocRect.Right() &&
+ rProposedStartPos.Y() <= maPreviewDocRect.Bottom();
+ OSL_ENSURE( bStartPosRangeValid,
+ "proposed start position out of range - no prepare of preview paint");
+ if ( !bStartPosRangeValid )
+ return false;
+
+ bool bWinSizeValid = !_rPxWinSize.IsEmpty();
+ OSL_ENSURE( bWinSizeValid, "no window size - no prepare of preview paint");
+ if ( !bWinSizeValid )
+ return false;
+
+ bool bStartInfoValid = _nProposedStartPageNum > 0 ||
+ rProposedStartPos != Point(0,0);
+ if ( !bStartInfoValid )
+ nProposedStartPageNum = 1;
+ }
+
+ // environment and parameter ok
+
+ // update window size at preview setting data
+ maWinSize = mrParentViewShell.GetOut()->PixelToLogic( _rPxWinSize );
+
+ mbNoPageVisible = false;
+ if ( nProposedStartPageNum > 0 )
+ {
+ // determine column and row of proposed start page in virtual preview layout
+ const sal_uInt16 nColOfProposed = GetColOfPage( nProposedStartPageNum );
+ const sal_uInt16 nRowOfProposed = GetRowOfPage( nProposedStartPageNum );
+ // determine start page
+ if ( _bStartWithPageAtFirstCol )
+ {
+ // leaving left-top-corner blank is
+ // controlled by <mbBookPreview>.
+ if ( mbBookPreview &&
+ ( nProposedStartPageNum == 1 || nRowOfProposed == 1 )
+ )
+ mnPaintPhyStartPageNum = 1;
+ else
+ mnPaintPhyStartPageNum = nProposedStartPageNum - (nColOfProposed-1);
+ }
+ else
+ mnPaintPhyStartPageNum = nProposedStartPageNum;
+
+ mnPaintPhyStartPageNum = ConvertRelativeToAbsolutePageNum( mnPaintPhyStartPageNum );
+
+ // set starting column
+ if ( _bStartWithPageAtFirstCol )
+ mnPaintStartCol = 1;
+ else
+ mnPaintStartCol = nColOfProposed;
+ // set starting row
+ mnPaintStartRow = nRowOfProposed;
+ // page offset == (-1,-1), indicating no offset and paint of free space.
+ maPaintStartPageOffset.setX( -1 );
+ maPaintStartPageOffset.setY( -1 );
+ // virtual preview document offset.
+ if ( _bStartWithPageAtFirstCol )
+ maPaintPreviewDocOffset.setX( 0 );
+ else
+ maPaintPreviewDocOffset.setX( (nColOfProposed-1) * mnColWidth );
+ maPaintPreviewDocOffset.setY( (nRowOfProposed-1) * mnRowHeight );
+ }
+ else
+ {
+ // determine column and row of proposed start position.
+ // Note: paint starts at point (0,0)
+ const sal_uInt16 nColOfProposed =
+ o3tl::narrowing<sal_uInt16>(rProposedStartPos.X() / mnColWidth) + 1;
+ const sal_uInt16 nRowOfProposed =
+ o3tl::narrowing<sal_uInt16>(rProposedStartPos.Y() / mnRowHeight) + 1;
+ // determine start page == page at proposed start position
+ // leaving left-top-corner blank is
+ // controlled by <mbBookPreview>.
+ if ( mbBookPreview &&
+ ( nRowOfProposed == 1 && nColOfProposed == 1 )
+ )
+ mnPaintPhyStartPageNum = 1;
+ else
+ {
+ // leaving left-top-corner blank is
+ // controlled by <mbBookPreview>.
+ mnPaintPhyStartPageNum = (nRowOfProposed-1) * mnCols + nColOfProposed;
+ if ( mbBookPreview )
+ --mnPaintPhyStartPageNum;
+ if ( mnPaintPhyStartPageNum > mnPages )
+ {
+ // no page will be visible, because shown part of document
+ // preview is the last row to the right of the last page
+ mnPaintPhyStartPageNum = mnPages;
+ mbNoPageVisible = true;
+ }
+ }
+ // set starting column and starting row
+ mnPaintStartCol = nColOfProposed;
+ mnPaintStartRow = nRowOfProposed;
+ // page offset
+ maPaintStartPageOffset.setX(
+ (rProposedStartPos.X() % mnColWidth) - gnXFree );
+ maPaintStartPageOffset.setY(
+ (rProposedStartPos.Y() % mnRowHeight) - gnYFree );
+ // virtual preview document offset.
+ maPaintPreviewDocOffset = rProposedStartPos;
+ }
+
+ // determine additional paint offset, if preview layout fits into window.
+ CalcAdditionalPaintOffset();
+
+ // determine rectangle to be painted from document preview
+ CalcDocPreviewPaintRect();
+ _orDocPreviewPaintRect = maPaintedPreviewDocRect;
+
+ // shift visible preview document area to the left,
+ // if on the right is an area left blank.
+ if ( !mbDoesLayoutColsFitIntoWindow &&
+ maPaintedPreviewDocRect.GetWidth() < maWinSize.Width() )
+ {
+ maPaintedPreviewDocRect.Move(
+ -(maWinSize.Width() - maPaintedPreviewDocRect.GetWidth()), 0 );
+ Prepare( 0, maPaintedPreviewDocRect.TopLeft(),
+ _rPxWinSize, _onStartPageNum,
+ _orDocPreviewPaintRect, _bStartWithPageAtFirstCol );
+ }
+
+ // shift visible preview document area to the top,
+ // if on the bottom is an area left blank.
+ if ( mbBookPreviewModeToggled &&
+ maPaintedPreviewDocRect.Bottom() == maPreviewDocRect.Bottom() &&
+ maPaintedPreviewDocRect.GetHeight() < maWinSize.Height() )
+ {
+ if ( mbDoesLayoutRowsFitIntoWindow )
+ {
+ if ( maPaintedPreviewDocRect.GetHeight() < mnPreviewLayoutHeight)
+ {
+ maPaintedPreviewDocRect.Move(
+ 0, -(mnPreviewLayoutHeight - maPaintedPreviewDocRect.GetHeight()) );
+ Prepare( 0, maPaintedPreviewDocRect.TopLeft(),
+ _rPxWinSize, _onStartPageNum,
+ _orDocPreviewPaintRect, _bStartWithPageAtFirstCol );
+ }
+ }
+ else
+ {
+ maPaintedPreviewDocRect.Move(
+ 0, -(maWinSize.Height() - maPaintedPreviewDocRect.GetHeight()) );
+ Prepare( 0, maPaintedPreviewDocRect.TopLeft(),
+ _rPxWinSize, _onStartPageNum,
+ _orDocPreviewPaintRect, _bStartWithPageAtFirstCol );
+ }
+ }
+
+ // determine preview pages - visible pages with needed data for paint and
+ // accessible pages with needed data.
+ CalcPreviewPages();
+
+ // OD 07.11.2003 #i22014# - indicate new layout, if print preview is in paint
+ if ( mbInPaint )
+ {
+ mbNewLayoutDuringPaint = true;
+ }
+
+ // validate paint data
+ mbPaintInfoValid = true;
+
+ // return start page
+ _onStartPageNum = mnPaintPhyStartPageNum;
+
+ return true;
+}
+
+/** calculate additional paint offset
+
+*/
+void SwPagePreviewLayout::CalcAdditionalPaintOffset()
+{
+ if ( mnPreviewLayoutWidth <= maWinSize.Width() &&
+ maPaintStartPageOffset.X() <= 0 )
+ {
+ mbDoesLayoutColsFitIntoWindow = true;
+ maAdditionalPaintOffset.setX( (maWinSize.Width() - mnPreviewLayoutWidth) / 2 );
+ }
+ else
+ {
+ mbDoesLayoutColsFitIntoWindow = false;
+ maAdditionalPaintOffset.setX( 0 );
+ }
+
+ if ( mnPreviewLayoutHeight <= maWinSize.Height() &&
+ maPaintStartPageOffset.Y() <= 0 )
+ {
+ mbDoesLayoutRowsFitIntoWindow = true;
+ maAdditionalPaintOffset.setY( (maWinSize.Height() - mnPreviewLayoutHeight) / 2 );
+ }
+ else
+ {
+ mbDoesLayoutRowsFitIntoWindow = false;
+ maAdditionalPaintOffset.setY( 0 );
+ }
+}
+
+/** calculate painted preview document rectangle
+
+*/
+void SwPagePreviewLayout::CalcDocPreviewPaintRect()
+{
+ Point aTopLeftPos = maPaintPreviewDocOffset;
+ maPaintedPreviewDocRect.SetPos( aTopLeftPos );
+
+ Size aSize;
+ if ( mbDoesLayoutColsFitIntoWindow )
+ aSize.setWidth( std::min( tools::Long(mnPreviewLayoutWidth),
+ maPreviewDocRect.GetWidth() - aTopLeftPos.X() ) );
+ else
+ aSize.setWidth( std::min( maPreviewDocRect.GetWidth() - aTopLeftPos.X(),
+ maWinSize.Width() - maAdditionalPaintOffset.X() ) );
+ if ( mbDoesLayoutRowsFitIntoWindow )
+ aSize.setHeight( std::min( tools::Long(mnPreviewLayoutHeight),
+ maPreviewDocRect.GetHeight() - aTopLeftPos.Y() ) );
+ else
+ aSize.setHeight( std::min( maPreviewDocRect.GetHeight() - aTopLeftPos.Y(),
+ maWinSize.Height() - maAdditionalPaintOffset.Y() ) );
+ maPaintedPreviewDocRect.SetSize( aSize );
+}
+
+/** calculate preview pages
+
+*/
+void SwPagePreviewLayout::CalcPreviewPages()
+{
+ vcl::RenderContext* pRenderContext = mrParentViewShell.GetOut();
+ ClearPreviewPageData();
+
+ if ( mbNoPageVisible )
+ return;
+
+ // determine start page frame
+ const SwPageFrame* pStartPage = mrLayoutRootFrame.GetPageByPageNum( mnPaintPhyStartPageNum );
+
+ // calculate initial paint offset
+ Point aInitialPaintOffset;
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL()){
+ if ( maPaintStartPageOffset != Point( -1, -1 ) )
+ aInitialPaintOffset = Point(0,0) - maPaintStartPageOffset;
+ else
+ aInitialPaintOffset = Point( gnXFree, gnYFree );
+ }
+ else {
+ if ( maPaintStartPageOffset != Point( -1, -1 ) )
+ aInitialPaintOffset = Point(0 + ((SwPagePreviewLayout::mnCols-1)*mnColWidth),0) - maPaintStartPageOffset;
+ else
+ aInitialPaintOffset = Point( gnXFree + ((SwPagePreviewLayout::mnCols-1)*mnColWidth), gnYFree );
+ }
+ aInitialPaintOffset += maAdditionalPaintOffset;
+
+ // prepare loop data
+ const SwPageFrame* pPage = pStartPage;
+ sal_uInt16 nCurrCol = mnPaintStartCol;
+ sal_uInt16 nConsideredRows = 0;
+ Point aCurrPaintOffset = aInitialPaintOffset;
+ // loop on pages to determine preview background rectangles
+ while ( pPage &&
+ (!mbDoesLayoutRowsFitIntoWindow || nConsideredRows < mnRows) &&
+ aCurrPaintOffset.Y() < maWinSize.Height()
+ )
+ {
+ if ( !mbBookPreview && !mbPrintEmptyPages && pPage->IsEmptyPage() )
+ {
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ continue;
+ }
+
+ pPage->Calc(pRenderContext);
+
+ // consider only pages, which have to be painted.
+ if ( nCurrCol < mnPaintStartCol )
+ {
+ // calculate data of unvisible page needed for accessibility
+ std::unique_ptr<PreviewPage> pPreviewPage(new PreviewPage);
+ Point aCurrAccOffset = aCurrPaintOffset -
+ Point( (mnPaintStartCol-nCurrCol) * mnColWidth, 0 );
+ CalcPreviewDataForPage( *pPage, aCurrAccOffset, pPreviewPage.get() );
+ pPreviewPage->bVisible = false;
+ maPreviewPages.push_back( std::move(pPreviewPage) );
+ // continue with next page and next column
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ ++nCurrCol;
+ continue;
+ }
+ if ( aCurrPaintOffset.X() < maWinSize.Width() )
+ {
+ // leaving left-top-corner blank is
+ // controlled by <mbBookPreview>.
+ if ( mbBookPreview && pPage->GetPhyPageNum() == 1 && mnCols != 1 && nCurrCol == 1
+ )
+ {
+ // first page in 2nd column
+ // --> continue with increased paint offset and next column
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL())
+ aCurrPaintOffset.AdjustX(mnColWidth );
+ else aCurrPaintOffset.AdjustX( -mnColWidth );
+ ++nCurrCol;
+ continue;
+ }
+
+ // calculate data of visible page
+ std::unique_ptr<PreviewPage> pPreviewPage(new PreviewPage);
+ CalcPreviewDataForPage( *pPage, aCurrPaintOffset, pPreviewPage.get() );
+ pPreviewPage->bVisible = true;
+ maPreviewPages.push_back( std::move(pPreviewPage) );
+ }
+ else
+ {
+ // calculate data of unvisible page needed for accessibility
+ std::unique_ptr<PreviewPage> pPreviewPage(new PreviewPage);
+ CalcPreviewDataForPage( *pPage, aCurrPaintOffset, pPreviewPage.get() );
+ pPreviewPage->bVisible = false;
+ maPreviewPages.push_back( std::move(pPreviewPage) );
+ }
+
+ // prepare data for next loop
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+
+ /// check whether RTL interface or not
+ if(!AllSettings::GetLayoutRTL())
+ aCurrPaintOffset.AdjustX(mnColWidth );
+ else aCurrPaintOffset.AdjustX( -mnColWidth );
+ ++nCurrCol;
+ if ( nCurrCol > mnCols )
+ {
+ ++nConsideredRows;
+ aCurrPaintOffset.setX( aInitialPaintOffset.X() );
+ nCurrCol = 1;
+ aCurrPaintOffset.AdjustY(mnRowHeight );
+ }
+ }
+}
+
+/** determines preview data for a given page and a given preview offset
+
+ OD 13.12.2002 #103492#
+*/
+void SwPagePreviewLayout::CalcPreviewDataForPage( const SwPageFrame& _rPage,
+ const Point& _rPreviewOffset,
+ PreviewPage* _opPreviewPage )
+{
+ // page frame
+ _opPreviewPage->pPage = &_rPage;
+ // size of page frame
+ if ( _rPage.IsEmptyPage() )
+ {
+ if ( _rPage.GetPhyPageNum() % 2 == 0 )
+ _opPreviewPage->aPageSize = _rPage.GetPrev()->getFrameArea().SSize();
+ else
+ _opPreviewPage->aPageSize = _rPage.GetNext()->getFrameArea().SSize();
+ }
+ else
+ _opPreviewPage->aPageSize = _rPage.getFrameArea().SSize();
+ // position of page in preview window
+ Point aPreviewWinOffset( _rPreviewOffset );
+ if ( _opPreviewPage->aPageSize.Width() < maMaxPageSize.Width() )
+ aPreviewWinOffset.AdjustX(( maMaxPageSize.Width() - _opPreviewPage->aPageSize.Width() ) / 2 );
+ if ( _opPreviewPage->aPageSize.Height() < maMaxPageSize.Height() )
+ aPreviewWinOffset.AdjustY(( maMaxPageSize.Height() - _opPreviewPage->aPageSize.Height() ) / 2 );
+ _opPreviewPage->aPreviewWinPos = aPreviewWinOffset;
+ // logic position of page and mapping offset for paint
+ if ( _rPage.IsEmptyPage() )
+ {
+ _opPreviewPage->aLogicPos = _opPreviewPage->aPreviewWinPos;
+ _opPreviewPage->aMapOffset = Point( 0, 0 );
+ }
+ else
+ {
+ _opPreviewPage->aLogicPos = _rPage.getFrameArea().Pos();
+ _opPreviewPage->aMapOffset = _opPreviewPage->aPreviewWinPos - _opPreviewPage->aLogicPos;
+ }
+}
+
+/** enable/disable book preview
+
+ OD 2004-03-04 #i18143#
+*/
+bool SwPagePreviewLayout::SetBookPreviewMode( const bool _bEnableBookPreview,
+ sal_uInt16& _onStartPageNum,
+ tools::Rectangle& _orDocPreviewPaintRect )
+{
+ if ( mbBookPreview != _bEnableBookPreview)
+ {
+ mbBookPreview = _bEnableBookPreview;
+ // re-initialize page preview layout
+ ReInit();
+ // re-prepare page preview layout
+ {
+ mbBookPreviewModeToggled = true;
+ Point aProposedStartPos( maPaintPreviewDocOffset );
+ // if proposed start position is below virtual preview document
+ // bottom, adjust it to the virtual preview document bottom
+ if ( aProposedStartPos.Y() > maPreviewDocRect.Bottom() )
+ {
+ aProposedStartPos.setY( maPreviewDocRect.Bottom() );
+ }
+ Prepare( 0, aProposedStartPos,
+ mrParentViewShell.GetOut()->LogicToPixel( maWinSize ),
+ _onStartPageNum, _orDocPreviewPaintRect );
+ mbBookPreviewModeToggled = false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// methods to determine new data for changing the current shown part of the
+// document preview.
+
+/** calculate start position for new scale
+
+*/
+Point SwPagePreviewLayout::GetPreviewStartPosForNewScale(
+ const Fraction& _aNewScale,
+ const Fraction& _aOldScale,
+ const Size& _aNewWinSize ) const
+{
+ Point aNewPaintStartPos = maPaintedPreviewDocRect.TopLeft();
+ if ( _aNewScale < _aOldScale )
+ {
+ // increase paint width by moving start point to left.
+ if ( mnPreviewLayoutWidth < _aNewWinSize.Width() )
+ aNewPaintStartPos.setX( 0 );
+ else if ( maPaintedPreviewDocRect.GetWidth() < _aNewWinSize.Width() )
+ {
+ aNewPaintStartPos.AdjustX( -(
+ (_aNewWinSize.Width() - maPaintedPreviewDocRect.GetWidth()) / 2) );
+ if ( aNewPaintStartPos.X() < 0)
+ aNewPaintStartPos.setX( 0 );
+ }
+
+ if ( !mbDoesLayoutRowsFitIntoWindow )
+ {
+ // increase paint height by moving start point to top.
+ if ( mnPreviewLayoutHeight < _aNewWinSize.Height() )
+ {
+ aNewPaintStartPos.setY(
+ (mnPaintStartRow - 1) * mnRowHeight );
+ }
+ else if ( maPaintedPreviewDocRect.GetHeight() < _aNewWinSize.Height() )
+ {
+ aNewPaintStartPos.AdjustY( -(
+ (_aNewWinSize.Height() - maPaintedPreviewDocRect.GetHeight()) / 2) );
+ if ( aNewPaintStartPos.Y() < 0)
+ aNewPaintStartPos.setY( 0 );
+ }
+ }
+ }
+ else
+ {
+ // decrease paint width by moving start point to right
+ if ( maPaintedPreviewDocRect.GetWidth() > _aNewWinSize.Width() )
+ aNewPaintStartPos.AdjustX(
+ (maPaintedPreviewDocRect.GetWidth() - _aNewWinSize.Width()) / 2 );
+ // decrease paint height by moving start point to bottom
+ if ( maPaintedPreviewDocRect.GetHeight() > _aNewWinSize.Height() )
+ {
+ aNewPaintStartPos.AdjustY(
+ (maPaintedPreviewDocRect.GetHeight() - _aNewWinSize.Height()) / 2 );
+ // check, if new y-position is outside document preview
+ if ( aNewPaintStartPos.Y() > maPreviewDocRect.Bottom() )
+ aNewPaintStartPos.setY(
+ std::max( tools::Long(0), maPreviewDocRect.Bottom() - mnPreviewLayoutHeight ) );
+ }
+ }
+
+ return aNewPaintStartPos;
+}
+
+/** determines, if page with given page number is visible in preview
+
+ @note _nPageNum is absolute
+*/
+bool SwPagePreviewLayout::IsPageVisible( const sal_uInt16 _nPageNum ) const
+{
+ const PreviewPage* pPreviewPage = GetPreviewPageByPageNum( _nPageNum );
+ return pPreviewPage && pPreviewPage->bVisible;
+}
+
+/** calculate data to bring new selected page into view.
+
+ @note IN/OUT parameters are absolute page numbers!!!
+*/
+void SwPagePreviewLayout::CalcStartValuesForSelectedPageMove(
+ const sal_Int16 _nHoriMove,
+ const sal_Int16 _nVertMove,
+ sal_uInt16& _orNewSelectedPage,
+ sal_uInt16& _orNewStartPage,
+ Point& _orNewStartPos ) const
+{
+ // determine position of current selected page
+ sal_uInt16 nTmpRelSelPageNum = ConvertAbsoluteToRelativePageNum( mnSelectedPageNum );
+ sal_uInt16 nNewRelSelectedPageNum = nTmpRelSelPageNum;
+
+ const sal_uInt16 nCurrRow = GetRowOfPage(nTmpRelSelPageNum);
+
+ // determine new selected page number
+ {
+ if ( _nHoriMove != 0 )
+ {
+ if ( (nNewRelSelectedPageNum + _nHoriMove) < 1 )
+ nNewRelSelectedPageNum = 1;
+ else if ( (nNewRelSelectedPageNum + _nHoriMove) > mnPages )
+ nNewRelSelectedPageNum = mnPages;
+ else
+ nNewRelSelectedPageNum = nNewRelSelectedPageNum + _nHoriMove;
+ }
+ if ( _nVertMove != 0 )
+ {
+ if ( (nNewRelSelectedPageNum + (_nVertMove * mnCols)) < 1 )
+ nNewRelSelectedPageNum = 1;
+ else if ( (nNewRelSelectedPageNum + (_nVertMove * mnCols)) > mnPages )
+ nNewRelSelectedPageNum = mnPages;
+ else
+ nNewRelSelectedPageNum += ( _nVertMove * mnCols );
+ }
+ }
+
+ sal_uInt16 nNewStartPage = mnPaintPhyStartPageNum;
+ Point aNewStartPos(0,0);
+
+ const sal_uInt16 nNewAbsSelectedPageNum = ConvertRelativeToAbsolutePageNum( nNewRelSelectedPageNum );
+ if ( !IsPageVisible( nNewAbsSelectedPageNum ) )
+ {
+ if ( _nHoriMove != 0 && _nVertMove != 0 )
+ {
+ OSL_FAIL( "missing implementation for moving preview selected page horizontal AND vertical");
+ return;
+ }
+
+ // new selected page has to be brought into view considering current
+ // visible preview.
+ const sal_uInt16 nTotalRows = GetRowOfPage( mnPages );
+ if ( (_nHoriMove > 0 || _nVertMove > 0) &&
+ mbDoesLayoutRowsFitIntoWindow &&
+ mbDoesLayoutColsFitIntoWindow &&
+ nCurrRow > nTotalRows - mnRows )
+ {
+ // new proposed start page = left-top-corner of last possible
+ // preview page.
+ nNewStartPage = (nTotalRows - mnRows) * mnCols + 1;
+ // leaving left-top-corner blank is controlled
+ // by <mbBookPreview>.
+ if ( mbBookPreview )
+ {
+ // Note: decrease new proposed start page number by one,
+ // because of blank left-top-corner
+ --nNewStartPage;
+ }
+ nNewStartPage = ConvertRelativeToAbsolutePageNum( nNewStartPage );
+ }
+ else
+ {
+ // new proposed start page = new selected page.
+ nNewStartPage = ConvertRelativeToAbsolutePageNum( nNewRelSelectedPageNum );
+ }
+ }
+
+ _orNewSelectedPage = nNewAbsSelectedPageNum;
+ _orNewStartPage = nNewStartPage;
+ _orNewStartPos = aNewStartPos;
+}
+
+namespace {
+
+/** checks, if given position is inside a shown document page */
+struct PreviewPosInsidePagePred
+{
+ const Point mnPreviewPos;
+ explicit PreviewPosInsidePagePred(const Point& rPreviewPos)
+ : mnPreviewPos( rPreviewPos )
+ {}
+ bool operator() ( const std::unique_ptr<PreviewPage> & _pPreviewPage )
+ {
+ if ( _pPreviewPage->bVisible )
+ {
+ tools::Rectangle aPreviewPageRect( _pPreviewPage->aPreviewWinPos, _pPreviewPage->aPageSize );
+ return aPreviewPageRect.Contains( mnPreviewPos );
+ }
+ return false;
+ }
+};
+
+}
+
+bool SwPagePreviewLayout::IsPreviewPosInDocPreviewPage( const Point& rPreviewPos,
+ Point& _orDocPos,
+ bool& _obPosInEmptyPage,
+ sal_uInt16& _onPageNum ) const
+{
+ // initialize variable parameter values.
+ _orDocPos.setX( 0 );
+ _orDocPos.setY( 0 );
+ _obPosInEmptyPage = false;
+ _onPageNum = 0;
+
+ auto aFoundPreviewPageIter =
+ std::find_if( maPreviewPages.begin(), maPreviewPages.end(),
+ PreviewPosInsidePagePred( rPreviewPos ) );
+
+ if ( aFoundPreviewPageIter != maPreviewPages.end() )
+ {
+ // given preview position is inside a document page.
+ _onPageNum = (*aFoundPreviewPageIter)->pPage->GetPhyPageNum();
+ _obPosInEmptyPage = (*aFoundPreviewPageIter)->pPage->IsEmptyPage();
+ if ( !_obPosInEmptyPage )
+ {
+ // given preview position inside a normal page
+ _orDocPos = rPreviewPos -
+ (*aFoundPreviewPageIter)->aPreviewWinPos +
+ (*aFoundPreviewPageIter)->aLogicPos;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** determine window page scroll amount */
+SwTwips SwPagePreviewLayout::GetWinPagesScrollAmount(
+ const sal_Int16 _nWinPagesToScroll ) const
+{
+ SwTwips nScrollAmount;
+ if ( mbDoesLayoutRowsFitIntoWindow )
+ {
+ nScrollAmount = (mnPreviewLayoutHeight - gnYFree) * _nWinPagesToScroll;
+ }
+ else
+ nScrollAmount = _nWinPagesToScroll * maPaintedPreviewDocRect.GetHeight();
+
+ // check, if preview layout size values are valid.
+ // If not, the checks for an adjustment of the scroll amount aren't useful.
+ if ( mbLayoutSizesValid )
+ {
+ if ( (maPaintedPreviewDocRect.Top() + nScrollAmount) <= 0 )
+ nScrollAmount = -maPaintedPreviewDocRect.Top();
+
+ // correct scroll amount
+ if ( nScrollAmount > 0 &&
+ maPaintedPreviewDocRect.Bottom() == maPreviewDocRect.Bottom()
+ )
+ {
+ nScrollAmount = 0;
+ }
+ else
+ {
+ while ( (maPaintedPreviewDocRect.Top() + nScrollAmount + gnYFree) >= maPreviewDocRect.GetHeight() )
+ {
+ nScrollAmount -= mnRowHeight;
+ }
+ }
+ }
+
+ return nScrollAmount;
+}
+
+// methods to paint page preview layout
+
+namespace
+{
+/// Similar to RenderContextGuard, but does not touch the draw view.
+class PreviewRenderContextGuard
+{
+ VclPtr<vcl::RenderContext> m_pOriginalValue;
+ SwViewShell& m_rShell;
+
+public:
+ PreviewRenderContextGuard(SwViewShell& rShell, vcl::RenderContext* pValue)
+ : m_pOriginalValue(rShell.GetOut()),
+ m_rShell(rShell)
+ {
+ m_rShell.SetOut(pValue);
+ }
+
+ ~PreviewRenderContextGuard()
+ {
+ m_rShell.SetOut(m_pOriginalValue);
+ }
+};
+}
+
+/** paint prepared preview
+
+*/
+bool SwPagePreviewLayout::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rOutRect) const
+{
+ PreviewRenderContextGuard aGuard(mrParentViewShell, &rRenderContext);
+ // check environment and parameters
+ {
+ if (!mrParentViewShell.GetWin() && !mrParentViewShell.GetOut()->GetConnectMetaFile())
+ {
+ return false;
+ }
+
+ OSL_ENSURE(mbPaintInfoValid, "invalid preview settings - no paint of preview");
+ if (!mbPaintInfoValid)
+ return false;
+ }
+
+ // OD 17.11.2003 #i22014# - no paint, if <superfluous> flag is set at layout
+ if (mrLayoutRootFrame.IsSuperfluous())
+ {
+ return true;
+ }
+
+ // environment and parameter ok
+
+ if (mbInPaint)
+ {
+ return false;
+ }
+ mbInPaint = true;
+
+ OutputDevice* pOutputDev = &rRenderContext;
+
+ // prepare paint
+ if ( !maPreviewPages.empty() )
+ {
+ mrParentViewShell.Imp()->m_bFirstPageInvalid = false;
+ mrParentViewShell.Imp()->m_pFirstVisiblePage =
+ const_cast<SwPageFrame*>(maPreviewPages[0]->pPage);
+ }
+
+ // paint preview background
+ {
+ SwRegionRects aPreviewBackgrdRegion((SwRect(rOutRect)));
+ // calculate preview background rectangles
+ for ( auto & rpPreviewPage : maPreviewPages )
+ {
+ if ( rpPreviewPage->bVisible )
+ {
+ aPreviewBackgrdRegion -=
+ SwRect( rpPreviewPage->aPreviewWinPos, rpPreviewPage->aPageSize );
+ }
+ }
+ // paint preview background rectangles
+ mrParentViewShell.PaintDesktop_(aPreviewBackgrdRegion);
+ }
+
+ // prepare data for paint of pages
+ const tools::Rectangle aPxOutRect( pOutputDev->LogicToPixel(rOutRect) );
+
+ MapMode aMapMode( pOutputDev->GetMapMode() );
+ MapMode aSavedMapMode = aMapMode;
+
+ const vcl::Font& rEmptyPgFont = SwPageFrame::GetEmptyPageFont();
+
+ for ( auto & rpPreviewPage : maPreviewPages )
+ {
+ if ( !rpPreviewPage->bVisible )
+ continue;
+
+ tools::Rectangle aPageRect( rpPreviewPage->aLogicPos, rpPreviewPage->aPageSize );
+ aMapMode.SetOrigin( rpPreviewPage->aMapOffset );
+ pOutputDev->SetMapMode( aMapMode );
+ tools::Rectangle aPxPaintRect = pOutputDev->LogicToPixel( aPageRect );
+ if ( aPxOutRect.Overlaps( aPxPaintRect) )
+ {
+ const SwPageFrame* pPage = rpPreviewPage->pPage;
+
+ if (pPage->IsEmptyPage())
+ {
+ const Color aRetouche( mrParentViewShell.Imp()->GetRetoucheColor() );
+ if( pOutputDev->GetFillColor() != aRetouche )
+ pOutputDev->SetFillColor( aRetouche );
+ pOutputDev->SetLineColor(); // no line color
+ // use aligned page rectangle
+ {
+ SwRect aTmpPageRect( aPageRect );
+ ::SwAlignRect( aTmpPageRect, &mrParentViewShell, &rRenderContext );
+ aPageRect = aTmpPageRect.SVRect();
+ }
+ pOutputDev->DrawRect( aPageRect );
+
+ // paint empty page text
+ vcl::Font aOldFont( pOutputDev->GetFont() );
+ pOutputDev->SetFont( rEmptyPgFont );
+ pOutputDev->DrawText( aPageRect, SwResId( STR_EMPTYPAGE ),
+ DrawTextFlags::VCenter |
+ DrawTextFlags::Center |
+ DrawTextFlags::Clip );
+ pOutputDev->SetFont( aOldFont );
+ // paint shadow and border for empty page
+ // use new method to paint page border and shadow
+ SwPageFrame::PaintBorderAndShadow( SwRect(aPageRect), &mrParentViewShell, true, false, true );
+ }
+ else
+ {
+ const bool bIsLeftShadowed = pPage->IsLeftShadowNeeded();
+ const bool bIsRightShadowed = pPage->IsRightShadowNeeded();
+
+ mrParentViewShell.maVisArea = SwRect(aPageRect);
+ aPxPaintRect.Intersection( aPxOutRect );
+ tools::Rectangle aPaintRect = pOutputDev->PixelToLogic( aPxPaintRect );
+ mrParentViewShell.Paint(rRenderContext, aPaintRect);
+
+ // --> OD 2007-08-15 #i80691#
+ // paint page border and shadow
+ {
+ SwRect aPageBorderRect;
+ SwPageFrame::GetBorderAndShadowBoundRect( SwRect( aPageRect ), &mrParentViewShell, &rRenderContext, aPageBorderRect,
+ bIsLeftShadowed, bIsRightShadowed, true );
+ const vcl::Region aDLRegion(aPageBorderRect.SVRect());
+ mrParentViewShell.DLPrePaint2(aDLRegion);
+ SwPageFrame::PaintBorderAndShadow( SwRect(aPageRect), &mrParentViewShell, true, false, true );
+ mrParentViewShell.DLPostPaint2(true);
+ }
+ // <--
+ }
+ // OD 07.11.2003 #i22014# - stop painting, because new print
+ // preview layout is created during paint.
+ if ( mbNewLayoutDuringPaint )
+ {
+ break;
+ }
+
+ if (pPage->GetPhyPageNum() == mnSelectedPageNum)
+ {
+ PaintSelectMarkAtPage(rRenderContext, rpPreviewPage.get());
+ }
+ }
+ }
+
+ // OD 17.11.2003 #i22014# - no update of accessible preview, if a new
+ // print preview layout is created during paint.
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if ( !mbNewLayoutDuringPaint )
+ {
+ // update at accessibility interface
+ mrParentViewShell.Imp()->UpdateAccessiblePreview(
+ maPreviewPages,
+ aMapMode.GetScaleX(),
+ mrLayoutRootFrame.GetPageByPageNum( mnSelectedPageNum ),
+ maWinSize );
+ }
+#endif
+
+ pOutputDev->SetMapMode( aSavedMapMode );
+ mrParentViewShell.maVisArea.Clear();
+
+ // OD 07.11.2003 #i22014#
+ mbInPaint = false;
+ mbNewLayoutDuringPaint = false;
+
+ return true;
+}
+
+/** repaint pages on page preview
+
+ OD 18.12.2002 #103492#
+*/
+void SwPagePreviewLayout::Repaint( const tools::Rectangle& rInvalidCoreRect ) const
+{
+ // check environment and parameters
+ {
+ if ( !mrParentViewShell.GetWin() &&
+ !mrParentViewShell.GetOut()->GetConnectMetaFile() )
+ return;
+
+ OSL_ENSURE( mbPaintInfoValid,
+ "invalid preview settings - no paint of preview" );
+ if ( !mbPaintInfoValid )
+ return;
+ }
+
+ // environment and parameter ok
+
+ // prepare paint
+ if ( !maPreviewPages.empty() )
+ {
+ mrParentViewShell.Imp()->m_bFirstPageInvalid = false;
+ mrParentViewShell.Imp()->m_pFirstVisiblePage =
+ const_cast<SwPageFrame*>(maPreviewPages[0]->pPage);
+ }
+
+ // invalidate visible pages, which overlap the invalid core rectangle
+ for ( auto & rpPreviewPage : maPreviewPages )
+ {
+ if ( !rpPreviewPage->bVisible )
+ continue;
+
+ tools::Rectangle aPageRect( rpPreviewPage->aLogicPos, rpPreviewPage->aPageSize );
+ if ( rInvalidCoreRect.Overlaps( aPageRect ) )
+ {
+ aPageRect.Intersection(rInvalidCoreRect);
+ tools::Rectangle aInvalidPreviewRect = aPageRect;
+ aInvalidPreviewRect.SetPos( aInvalidPreviewRect.TopLeft() -
+ rpPreviewPage->aLogicPos +
+ rpPreviewPage->aPreviewWinPos );
+ mrParentViewShell.GetWin()->Invalidate( aInvalidPreviewRect );
+ }
+ }
+}
+
+/** paint selection mark at page
+
+ OD 17.12.2002 #103492#
+*/
+void SwPagePreviewLayout::PaintSelectMarkAtPage(vcl::RenderContext& rRenderContext,
+ const PreviewPage* _aSelectedPreviewPage ) const
+{
+ OutputDevice* pOutputDev = &rRenderContext;
+ MapMode aMapMode( pOutputDev->GetMapMode() );
+ // save mapping mode of output device
+ MapMode aSavedMapMode = aMapMode;
+ // save fill and line color of output device
+ Color aFill( pOutputDev->GetFillColor() );
+ Color aLine( pOutputDev->GetLineColor() );
+
+ // determine selection mark color
+ Color aSelPgLineColor(117, 114, 106);
+ const StyleSettings& rSettings =
+ mrParentViewShell.GetWin()->GetSettings().GetStyleSettings();
+ if ( rSettings.GetHighContrastMode() )
+ aSelPgLineColor = rSettings.GetHighlightTextColor();
+
+ // set needed mapping mode at output device
+ aMapMode.SetOrigin( _aSelectedPreviewPage->aMapOffset );
+ pOutputDev->SetMapMode( aMapMode );
+
+ // calculate page rectangle in pixel coordinates
+ SwRect aPageRect( _aSelectedPreviewPage->aLogicPos,
+ _aSelectedPreviewPage->aPageSize );
+ // OD 19.02.2003 #107369# - use aligned page rectangle, as it is used for
+ // page border and shadow paint - see <SwPageFrame::PaintBorderAndShadow(..)>
+ ::SwAlignRect( aPageRect, &mrParentViewShell, pOutputDev );
+ tools::Rectangle aPxPageRect = pOutputDev->LogicToPixel( aPageRect.SVRect() );
+
+ // draw two rectangle
+ // OD 19.02.2003 #107369# - adjust position of select mark rectangle
+ tools::Rectangle aRect( aPxPageRect.Left(), aPxPageRect.Top(),
+ aPxPageRect.Right(), aPxPageRect.Bottom() );
+ aRect = pOutputDev->PixelToLogic( aRect );
+ pOutputDev->SetFillColor(); // OD 20.02.2003 #107369# - no fill color
+ pOutputDev->SetLineColor( aSelPgLineColor );
+ pOutputDev->DrawRect( aRect );
+ // OD 19.02.2003 #107369# - adjust position of select mark rectangle
+ aRect = tools::Rectangle( aPxPageRect.Left()+1, aPxPageRect.Top()+1,
+ aPxPageRect.Right()-1, aPxPageRect.Bottom()-1 );
+ aRect = pOutputDev->PixelToLogic( aRect );
+ pOutputDev->DrawRect( aRect );
+
+ // reset fill and line color of output device
+ pOutputDev->SetFillColor( aFill );
+ pOutputDev->SetLineColor( aLine );
+
+ // reset mapping mode of output device
+ pOutputDev->SetMapMode( aSavedMapMode );
+}
+
+/** paint to mark new selected page
+
+ OD 17.12.2002 #103492#
+ Perform paint for current selected page in order to unmark it.
+ Set new selected page and perform paint to mark this page.
+
+ @note _nSelectedPage, mnSelectedPage are absolute
+*/
+void SwPagePreviewLayout::MarkNewSelectedPage( const sal_uInt16 _nSelectedPage )
+{
+ const sal_uInt16 nOldSelectedPageNum = mnSelectedPageNum;
+ mnSelectedPageNum = _nSelectedPage;
+
+ // re-paint for current selected page in order to unmark it.
+ const PreviewPage* pOldSelectedPreviewPage = GetPreviewPageByPageNum( nOldSelectedPageNum );
+ OutputDevice* pOutputDev = mrParentViewShell.GetOut();
+ if ( pOldSelectedPreviewPage && pOldSelectedPreviewPage->bVisible )
+ {
+ // OD 20.02.2003 #107369# - invalidate only areas of selection mark.
+ SwRect aPageRect( pOldSelectedPreviewPage->aPreviewWinPos,
+ pOldSelectedPreviewPage->aPageSize );
+ ::SwAlignRect( aPageRect, &mrParentViewShell, pOutputDev );
+ tools::Rectangle aPxPageRect = pOutputDev->LogicToPixel( aPageRect.SVRect() );
+ // invalidate top mark line
+ tools::Rectangle aInvalPxRect( aPxPageRect.Left(), aPxPageRect.Top(),
+ aPxPageRect.Right(), aPxPageRect.Top()+1 );
+ mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) );
+ // invalidate right mark line
+ aInvalPxRect = tools::Rectangle( aPxPageRect.Right()-1, aPxPageRect.Top(),
+ aPxPageRect.Right(), aPxPageRect.Bottom() );
+ mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) );
+ // invalidate bottom mark line
+ aInvalPxRect = tools::Rectangle( aPxPageRect.Left(), aPxPageRect.Bottom()-1,
+ aPxPageRect.Right(), aPxPageRect.Bottom() );
+ mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) );
+ // invalidate left mark line
+ aInvalPxRect = tools::Rectangle( aPxPageRect.Left(), aPxPageRect.Top(),
+ aPxPageRect.Left()+1, aPxPageRect.Bottom() );
+ mrParentViewShell.GetWin()->Invalidate( pOutputDev->PixelToLogic( aInvalPxRect ) );
+ }
+
+ // re-paint for new selected page in order to mark it.
+ const PreviewPage* pNewSelectedPreviewPage = GetPreviewPageByPageNum( _nSelectedPage );
+ if ( pNewSelectedPreviewPage && pNewSelectedPreviewPage->bVisible )
+ {
+ const PreviewPage* pSelectedPreviewPage = GetPreviewPageByPageNum(mnSelectedPageNum);
+ SwRect aPageRect(pSelectedPreviewPage->aPreviewWinPos, pSelectedPreviewPage->aPageSize);
+ ::SwAlignRect(aPageRect, &mrParentViewShell, pOutputDev);
+ mrParentViewShell.GetWin()->Invalidate(aPageRect.SVRect());
+ }
+}
+
+// helper methods
+
+namespace {
+
+/** get preview page by physical page number
+
+ OD 17.12.2002 #103492#
+*/
+struct EqualsPageNumPred
+{
+ const sal_uInt16 mnPageNum;
+ explicit EqualsPageNumPred(const sal_uInt16 _nPageNum)
+ : mnPageNum( _nPageNum )
+ {}
+ bool operator() ( const std::unique_ptr<PreviewPage> & _pPreviewPage )
+ {
+ return _pPreviewPage->pPage->GetPhyPageNum() == mnPageNum;
+ }
+};
+
+}
+
+const PreviewPage* SwPagePreviewLayout::GetPreviewPageByPageNum( const sal_uInt16 _nPageNum ) const
+{
+ auto aFoundPreviewPageIter =
+ std::find_if( maPreviewPages.begin(), maPreviewPages.end(),
+ EqualsPageNumPred( _nPageNum ) );
+
+ if ( aFoundPreviewPageIter == maPreviewPages.end() )
+ return nullptr;
+
+ return aFoundPreviewPageIter->get();
+}
+
+/** determine row the page with the given number is in
+
+ OD 17.01.2003 #103492#
+
+ @note _nPageNum is relative
+*/
+sal_uInt16 SwPagePreviewLayout::GetRowOfPage( sal_uInt16 _nPageNum ) const
+{
+ // OD 19.02.2003 #107369# - leaving left-top-corner blank is controlled
+ // by <mbBookPreview>.
+ if ( mbBookPreview )
+ {
+ // Note: increase given physical page number by one, because left-top-corner
+ // in the preview layout is left blank.
+ ++_nPageNum;
+ }
+
+ return _nPageNum / mnCols + ((_nPageNum % mnCols)>0 ? 1 : 0);
+}
+
+/** determine column the page with the given number is in
+
+ OD 17.01.2003 #103492#
+
+ @note _nPageNum is relative
+*/
+sal_uInt16 SwPagePreviewLayout::GetColOfPage( sal_uInt16 _nPageNum ) const
+{
+ // OD 19.02.2003 #107369# - leaving left-top-corner blank is controlled
+ // by <mbBookPreview>.
+ if ( mbBookPreview )
+ {
+ // Note: increase given physical page number by one, because left-top-corner
+ // in the preview layout is left blank.
+ ++_nPageNum;
+ }
+
+ const sal_uInt16 nCol = _nPageNum % mnCols;
+ return nCol ? nCol : mnCols;
+}
+
+Size SwPagePreviewLayout::GetPreviewDocSize() const
+{
+ OSL_ENSURE( PreviewLayoutValid(), "PagePreviewLayout not valid" );
+ return maPreviewDocRect.GetSize();
+}
+
+/** get size of a preview page by its physical page number
+
+ OD 15.01.2003 #103492#
+*/
+Size SwPagePreviewLayout::GetPreviewPageSizeByPageNum( sal_uInt16 _nPageNum ) const
+{
+ const PreviewPage* pPreviewPage = GetPreviewPageByPageNum( _nPageNum );
+ if ( pPreviewPage )
+ {
+ return pPreviewPage->aPageSize;
+ }
+ return Size( 0, 0 );
+}
+
+/** get virtual page number by its physical page number
+
+ OD 21.03.2003 #108282#
+*/
+sal_uInt16 SwPagePreviewLayout::GetVirtPageNumByPageNum( sal_uInt16 _nPageNum ) const
+{
+ const PreviewPage* pPreviewPage = GetPreviewPageByPageNum( _nPageNum );
+ if ( pPreviewPage )
+ {
+ return pPreviewPage->pPage->GetVirtPageNum();
+ }
+ return 0;
+}
+
+/** Convert absolute to relative page numbers (see PrintEmptyPages) */
+sal_uInt16 SwPagePreviewLayout::ConvertAbsoluteToRelativePageNum( sal_uInt16 _nAbsPageNum ) const
+{
+ if ( mbBookPreview || mbPrintEmptyPages || !_nAbsPageNum )
+ {
+ return _nAbsPageNum;
+ }
+
+ const SwPageFrame* pTmpPage = static_cast<const SwPageFrame*>(mrLayoutRootFrame.Lower());
+
+ sal_uInt16 nRet = 1;
+
+ while ( pTmpPage && pTmpPage->GetPhyPageNum() != _nAbsPageNum )
+ {
+ if ( !pTmpPage->IsEmptyPage() )
+ ++nRet;
+
+ pTmpPage = static_cast<const SwPageFrame*>( pTmpPage->GetNext() );
+ }
+
+ return nRet;
+}
+
+/** Convert relative to absolute page numbers (see PrintEmptyPages) */
+sal_uInt16 SwPagePreviewLayout::ConvertRelativeToAbsolutePageNum( sal_uInt16 _nRelPageNum ) const
+{
+ if ( mbBookPreview || mbPrintEmptyPages || !_nRelPageNum )
+ {
+ return _nRelPageNum;
+ }
+
+ const SwPageFrame* pTmpPage = static_cast<const SwPageFrame*>(mrLayoutRootFrame.Lower());
+ const SwPageFrame* pRet = nullptr;
+
+ sal_uInt16 i = 0;
+ while( pTmpPage && i != _nRelPageNum )
+ {
+ if ( !pTmpPage->IsEmptyPage() )
+ ++i;
+
+ pRet = pTmpPage;
+ pTmpPage = static_cast<const SwPageFrame*>( pTmpPage->GetNext() );
+ }
+
+ return pRet->GetPhyPageNum();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/printdata.cxx b/sw/source/core/view/printdata.cxx
new file mode 100644
index 000000000..95504f6b1
--- /dev/null
+++ b/sw/source/core/view/printdata.cxx
@@ -0,0 +1,447 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <printdata.hxx>
+
+#include <strings.hrc>
+#include <doc.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <unotxdoc.hxx>
+#include <wdocsh.hxx>
+#include <viewsh.hxx>
+#include <docfld.hxx>
+
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <vcl/outdev.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+SwRenderData::SwRenderData()
+{
+}
+
+SwRenderData::~SwRenderData()
+{
+ OSL_ENSURE( !m_pPostItShell, "m_pPostItShell should already have been deleted" );
+ OSL_ENSURE( !m_pPostItFields, " should already have been deleted" );
+}
+
+void SwRenderData::CreatePostItData( SwDoc& rDoc, const SwViewOption *pViewOpt, OutputDevice *pOutDev )
+{
+ DeletePostItData();
+ m_pPostItFields.reset(new SetGetExpFields);
+ sw_GetPostIts( rDoc.getIDocumentFieldsAccess(), m_pPostItFields.get() );
+
+ //!! Disable spell and grammar checking in the temporary document.
+ //!! Otherwise the grammar checker might process it and crash if we later on
+ //!! simply delete this document while he is still at it.
+ SwViewOption aViewOpt( *pViewOpt );
+ aViewOpt.SetOnlineSpell( false );
+
+ m_pPostItShell.reset(new SwViewShell(*new SwDoc, nullptr, &aViewOpt, pOutDev));
+}
+
+void SwRenderData::DeletePostItData()
+{
+ if (HasPostItData())
+ {
+ // printer needs to remain at the real document
+ m_pPostItShell->GetDoc()->getIDocumentDeviceAccess().setPrinter( nullptr, false, false );
+ { // avoid destroying layout from SwDoc dtor
+ rtl::Reference<SwDoc> const xKeepAlive(m_pPostItShell->GetDoc());
+ m_pPostItShell.reset();
+ }
+ m_pPostItFields.reset();
+ }
+}
+
+void SwRenderData::SetTempDocShell(SfxObjectShellLock const& xShell)
+{
+ m_xTempDocShell = xShell;
+}
+
+bool SwRenderData::NeedNewViewOptionAdjust( const SwViewShell& rCompare ) const
+{
+ return !(m_pViewOptionAdjust && m_pViewOptionAdjust->checkShell( rCompare ));
+}
+
+void SwRenderData::ViewOptionAdjustStart(
+ SwViewShell &rSh, const SwViewOption &rViewOptions)
+{
+ if (m_pViewOptionAdjust)
+ {
+ OSL_FAIL("error: there should be no ViewOptionAdjust active when calling this function" );
+ }
+ m_pViewOptionAdjust.reset(
+ new SwViewOptionAdjust_Impl( rSh, rViewOptions ));
+}
+
+void SwRenderData::ViewOptionAdjust(SwPrintData const*const pPrtOptions, bool setShowPlaceHoldersInPDF)
+{
+ m_pViewOptionAdjust->AdjustViewOptions( pPrtOptions, setShowPlaceHoldersInPDF );
+}
+
+void SwRenderData::ViewOptionAdjustStop()
+{
+ m_pViewOptionAdjust.reset();
+}
+
+void SwRenderData::ViewOptionAdjustCrashPreventionKludge()
+{
+ m_pViewOptionAdjust->DontTouchThatViewShellItSmellsFunny();
+}
+
+void SwRenderData::MakeSwPrtOptions(
+ SwDocShell const*const pDocShell,
+ SwPrintUIOptions const*const pOpt,
+ bool const bIsPDFExport)
+{
+ if (!pDocShell || !pOpt)
+ return;
+
+ m_pPrtOptions.reset(new SwPrintData);
+ SwPrintData & rOptions(*m_pPrtOptions);
+
+ // get default print options
+ bool bWeb = dynamic_cast<const SwWebDocShell*>( pDocShell) != nullptr;
+ ::sw::InitPrintOptionsFromApplication(rOptions, bWeb);
+
+ // get print options to use from provided properties
+ rOptions.m_bPrintGraphic = pOpt->IsPrintGraphics();
+ rOptions.m_bPrintTable = true; // for now it was decided that tables should always be printed
+ rOptions.m_bPrintDraw = pOpt->IsPrintDrawings();
+ rOptions.m_bPrintControl = pOpt->IsPrintFormControls();
+ rOptions.m_bPrintLeftPages = pOpt->IsPrintLeftPages();
+ rOptions.m_bPrintRightPages = pOpt->IsPrintRightPages();
+ rOptions.m_bPrintPageBackground = pOpt->IsPrintPageBackground();
+ rOptions.m_bPrintEmptyPages = pOpt->IsPrintEmptyPages( bIsPDFExport );
+ // bUpdateFieldsInPrinting <-- not set here; mail merge only
+ rOptions.m_bPaperFromSetup = pOpt->IsPaperFromSetup();
+ rOptions.m_bPrintReverse = false; /*handled by print dialog now*/
+ rOptions.m_bPrintProspect = pOpt->IsPrintProspect();
+ rOptions.m_bPrintProspectRTL = pOpt->IsPrintProspectRTL();
+ // bPrintSingleJobs <-- not set here; mail merge and or configuration
+ // bModified <-- not set here; mail merge only
+ rOptions.m_bPrintBlackFont = pOpt->IsPrintWithBlackTextColor();
+ rOptions.m_bPrintHiddenText = pOpt->IsPrintHiddenText();
+ rOptions.m_bPrintTextPlaceholder = pOpt->IsPrintTextPlaceholders();
+ rOptions.m_nPrintPostIts = pOpt->GetPrintPostItsType();
+
+ //! needs to be set after MakeOptions since the assignment operation in that
+ //! function will destroy the pointers
+ rOptions.SetRenderData( this );
+}
+
+SwPrintUIOptions::SwPrintUIOptions(
+ sal_uInt16 nCurrentPage,
+ bool bWeb,
+ bool bSwSrcView,
+ bool bHasSelection,
+ bool bHasPostIts,
+ const SwPrintData &rDefaultPrintData ) :
+ m_rDefaultPrintData( rDefaultPrintData )
+{
+ // printing HTML sources does not have any valid UI options.
+ // It's just the source code that gets printed...
+ if (bSwSrcView)
+ {
+ m_aUIProperties.clear();
+ return;
+ }
+
+ // check if either CJK or CTL is enabled
+ bool bRTL = SvtCJKOptions::IsCJKFontEnabled() || SvtCTLOptions().IsCTLFontEnabled();
+
+ // create sequence of print UI options
+ // (5 options are not available for Writer-Web)
+ const int nRTLOpts = bRTL ? 1 : 0;
+ const int nNumProps = nRTLOpts + (bWeb ? 15 : 19);
+ m_aUIProperties.resize( nNumProps);
+ int nIdx = 0;
+
+ // load the writer PrinterOptions into the custom tab
+ m_aUIProperties[nIdx].Name = "OptionsUIFile";
+ m_aUIProperties[nIdx++].Value <<= OUString("modules/swriter/ui/printeroptions.ui");
+
+ // create "writer" section (new tab page in dialog)
+ SvtModuleOptions aModOpt;
+ OUString aAppGroupname( SwResId( STR_PRINTOPTUI_PRODUCTNAME) );
+ aAppGroupname = aAppGroupname.replaceFirst( "%s", aModOpt.GetModuleName( SvtModuleOptions::EModule::WRITER ) );
+ m_aUIProperties[ nIdx++ ].Value = setGroupControlOpt("tabcontrol-page2", aAppGroupname, ".HelpID:vcl:PrintDialog:TabPage:AppPage");
+
+ // create sub section for Contents
+ m_aUIProperties[ nIdx++ ].Value = setSubgroupControlOpt("contents", SwResId( STR_PRINTOPTUI_CONTENTS), OUString());
+
+ // create a bool option for background
+ bool bDefaultVal = rDefaultPrintData.IsPrintPageBackground();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("pagebackground", SwResId( STR_PRINTOPTUI_PAGE_BACKGROUND),
+ ".HelpID:vcl:PrintDialog:PrintPageBackground:CheckBox",
+ "PrintPageBackground",
+ bDefaultVal);
+
+ // create a bool option for pictures/graphics AND OLE and drawing objects as well
+ bDefaultVal = rDefaultPrintData.IsPrintGraphic() || rDefaultPrintData.IsPrintDraw();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("pictures", SwResId( STR_PRINTOPTUI_PICTURES),
+ ".HelpID:vcl:PrintDialog:PrintPicturesAndObjects:CheckBox",
+ "PrintPicturesAndObjects",
+ bDefaultVal);
+ if (!bWeb)
+ {
+ // create a bool option for hidden text
+ bDefaultVal = rDefaultPrintData.IsPrintHiddenText();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("hiddentext", SwResId( STR_PRINTOPTUI_HIDDEN),
+ ".HelpID:vcl:PrintDialog:PrintHiddenText:CheckBox",
+ "PrintHiddenText",
+ bDefaultVal);
+
+ // create a bool option for place holder
+ bDefaultVal = rDefaultPrintData.IsPrintTextPlaceholder();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("placeholders", SwResId( STR_PRINTOPTUI_TEXT_PLACEHOLDERS),
+ ".HelpID:vcl:PrintDialog:PrintTextPlaceholder:CheckBox",
+ "PrintTextPlaceholder",
+ bDefaultVal);
+ }
+
+ // create a bool option for controls
+ bDefaultVal = rDefaultPrintData.IsPrintControl();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("formcontrols", SwResId( STR_PRINTOPTUI_FORM_CONTROLS),
+ ".HelpID:vcl:PrintDialog:PrintControls:CheckBox",
+ "PrintControls",
+ bDefaultVal);
+
+ // create sub section for Color
+ m_aUIProperties[ nIdx++ ].Value = setSubgroupControlOpt("color", SwResId( STR_PRINTOPTUI_COLOR), OUString());
+
+ // create a bool option for printing text with black font color
+ bDefaultVal = rDefaultPrintData.IsPrintBlackFont();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("textinblack", SwResId( STR_PRINTOPTUI_PRINT_BLACK),
+ ".HelpID:vcl:PrintDialog:PrintBlackFonts:CheckBox",
+ "PrintBlackFonts",
+ bDefaultVal);
+
+ if (!bWeb)
+ {
+ // create subgroup for misc options
+ m_aUIProperties[ nIdx++ ].Value = setSubgroupControlOpt("pages", SwResId( STR_PRINTOPTUI_PAGES_TEXT), OUString());
+
+ // create a bool option for printing automatically inserted blank pages
+ bDefaultVal = rDefaultPrintData.IsPrintEmptyPages();
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("autoblankpages", SwResId( STR_PRINTOPTUI_PRINT_BLANK),
+ ".HelpID:vcl:PrintDialog:PrintEmptyPages:CheckBox",
+ "PrintEmptyPages",
+ bDefaultVal);
+ }
+
+ // create a bool option for paper tray
+ bDefaultVal = rDefaultPrintData.IsPaperFromSetup();
+ vcl::PrinterOptionsHelper::UIControlOptions aPaperTrayOpt;
+ aPaperTrayOpt.maGroupHint = "OptionsPageOptGroup";
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("printpaperfromsetup", SwResId( STR_PRINTOPTUI_ONLY_PAPER),
+ ".HelpID:vcl:PrintDialog:PrintPaperFromSetup:CheckBox",
+ "PrintPaperFromSetup",
+ bDefaultVal,
+ aPaperTrayOpt);
+
+ // print range selection
+ vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt;
+ aPrintRangeOpt.maGroupHint = "PrintRange";
+ aPrintRangeOpt.mbInternalOnly = true;
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt( "printrange",
+ SwResId( STR_PRINTOPTUI_PAGES_TEXT ),
+ OUString(),
+ aPrintRangeOpt );
+
+ // create a choice for the content to create
+ static const OUStringLiteral aPrintRangeName( u"PrintContent" );
+ uno::Sequence< OUString > aChoices{ SwResId( STR_PRINTOPTUI_PRINTALLPAGES ),
+ SwResId( STR_PRINTOPTUI_PRINTPAGES ),
+ SwResId( STR_PRINTOPTUI_PRINTSELECTION ) };
+ uno::Sequence< sal_Bool > aChoicesDisabled{ false, false, !bHasSelection };
+ uno::Sequence< OUString > aHelpIds{ ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:0",
+ ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:1",
+ ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:2" };
+ uno::Sequence< OUString > aWidgetIds{ "rbAllPages", "rbRangePages", "rbRangeSelection" };
+ m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(),
+ aHelpIds, aPrintRangeName,
+ aChoices,
+ bHasSelection ? 2 : 0,
+ aChoicesDisabled);
+
+ // show an Edit dependent on "Pages" selected
+ vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt( aPrintRangeName, 1, true );
+ m_aUIProperties[nIdx++].Value = setEditControlOpt("pagerange", OUString(),
+ ".HelpID:vcl:PrintDialog:PageRange:Edit",
+ "PageRange",
+ OUString::number( nCurrentPage ) /* set text box to current page number */,
+ aPageRangeOpt);
+
+ vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintRangeName, -1, true);
+ m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("evenoddbox",
+ OUString(),
+ uno::Sequence<OUString>(),
+ "EvenOdd",
+ uno::Sequence<OUString>(),
+ 0,
+ uno::Sequence< sal_Bool >(),
+ aEvenOddOpt);
+
+ // create a list box for notes content
+ const SwPostItMode nPrintPostIts = rDefaultPrintData.GetPrintPostIts();
+ aChoices = { SwResId( STR_PRINTOPTUI_NONE),
+ SwResId( STR_PRINTOPTUI_COMMENTS_ONLY),
+ SwResId( STR_PRINTOPTUI_PLACE_END),
+ SwResId( STR_PRINTOPTUI_PLACE_PAGE),
+ SwResId( STR_PRINTOPTUI_PLACE_MARGINS) };
+ aHelpIds = { ".HelpID:vcl:PrintDialog:PrintAnnotationMode:FixedText",
+ ".HelpID:vcl:PrintDialog:PrintAnnotationMode:ListBox" };
+ vcl::PrinterOptionsHelper::UIControlOptions aAnnotOpt( "PrintProspect", 0, false );
+ aAnnotOpt.mbEnabled = bHasPostIts;
+ m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("writercomments",
+ SwResId( STR_PRINTOPTUI_COMMENTS),
+ aHelpIds,
+ "PrintAnnotationMode",
+ aChoices,
+ bHasPostIts ? static_cast<sal_uInt16>(nPrintPostIts) : 0,
+ uno::Sequence< sal_Bool >(),
+ aAnnotOpt);
+
+ // create subsection for Page settings
+ vcl::PrinterOptionsHelper::UIControlOptions aPageSetOpt;
+ aPageSetOpt.maGroupHint = "LayoutPage";
+
+ // create a bool option for brochure
+ bDefaultVal = rDefaultPrintData.IsPrintProspect();
+ static const OUStringLiteral aBrochurePropertyName( u"PrintProspect" );
+ m_aUIProperties[ nIdx++ ].Value = setBoolControlOpt("brochure", SwResId( STR_PRINTOPTUI_BROCHURE),
+ ".HelpID:vcl:PrintDialog:PrintProspect:CheckBox",
+ aBrochurePropertyName,
+ bDefaultVal,
+ aPageSetOpt);
+
+ if (bRTL)
+ {
+ // create a bool option for brochure RTL dependent on brochure
+ uno::Sequence< OUString > aBRTLChoices{ SwResId( STR_PRINTOPTUI_LEFT_SCRIPT),
+ SwResId( STR_PRINTOPTUI_RIGHT_SCRIPT) };
+ vcl::PrinterOptionsHelper::UIControlOptions aBrochureRTLOpt( aBrochurePropertyName, -1, true );
+ uno::Sequence<OUString> aBRTLHelpIds { ".HelpID:vcl:PrintDialog:PrintProspectRTL:ListBox" };
+ aBrochureRTLOpt.maGroupHint = "LayoutPage";
+ // RTL brochure choices
+ // 0 : left-to-right
+ // 1 : right-to-left
+ const sal_Int16 nBRTLChoice = rDefaultPrintData.IsPrintProspectRTL() ? 1 : 0;
+ m_aUIProperties[ nIdx++ ].Value = setChoiceListControlOpt("scriptdirection",
+ OUString(),
+ aBRTLHelpIds,
+ "PrintProspectRTL",
+ aBRTLChoices,
+ nBRTLChoice,
+ uno::Sequence< sal_Bool >(),
+ aBrochureRTLOpt);
+ }
+
+ assert(nIdx == nNumProps);
+}
+
+SwPrintUIOptions::~SwPrintUIOptions()
+{
+}
+
+bool SwPrintUIOptions::IsPrintLeftPages() const
+{
+ // take care of different property names for the option.
+ // for compatibility the old name should win (may still be used for PDF export or via Uno API)
+
+ // 0: left and right pages
+ // 1: left pages only
+ // 2: right pages only
+ sal_Int64 nEOPages = getIntValue( "EvenOdd", 0 /* default: all */ );
+ bool bRes = nEOPages != 1;
+ bRes = getBoolValue( "EvenOdd", bRes /* <- default value if property is not found */ );
+ return bRes;
+}
+
+bool SwPrintUIOptions::IsPrintRightPages() const
+{
+ // take care of different property names for the option.
+ // for compatibility the old name should win (may still be used for PDF export or via Uno API)
+
+ sal_Int64 nEOPages = getIntValue( "EvenOdd", 0 /* default: all */ );
+ bool bRes = nEOPages != 2;
+ bRes = getBoolValue( "EvenOdd", bRes /* <- default value if property is not found */ );
+ return bRes;
+}
+
+bool SwPrintUIOptions::IsPrintEmptyPages( bool bIsPDFExport ) const
+{
+ // take care of different property names for the option.
+
+ bool bRes = bIsPDFExport ?
+ !getBoolValue( "IsSkipEmptyPages", true ) :
+ getBoolValue( "PrintEmptyPages", true );
+ return bRes;
+}
+
+bool SwPrintUIOptions::IsPrintGraphics() const
+{
+ // take care of different property names for the option.
+ // for compatibility the old name should win (may still be used for PDF export or via Uno API)
+
+ bool bRes = getBoolValue( "PrintPicturesAndObjects", true );
+ bRes = getBoolValue( "PrintGraphics", bRes );
+ return bRes;
+}
+
+bool SwPrintUIOptions::IsPrintDrawings() const
+{
+ // take care of different property names for the option.
+ // for compatibility the old name should win (may still be used for PDF export or via Uno API)
+
+ bool bRes = getBoolValue( "PrintPicturesAndObjects", true );
+ bRes = getBoolValue( "PrintDrawings", bRes );
+ return bRes;
+}
+
+bool SwPrintUIOptions::processPropertiesAndCheckFormat( const uno::Sequence< beans::PropertyValue >& i_rNewProp )
+{
+ bool bChanged = processProperties( i_rNewProp );
+
+ uno::Reference< awt::XDevice > xRenderDevice;
+ uno::Any aVal( getValue( "RenderDevice" ) );
+ aVal >>= xRenderDevice;
+
+ VclPtr< OutputDevice > pOut;
+ if (xRenderDevice.is())
+ {
+ VCLXDevice* pDevice = comphelper::getFromUnoTunnel<VCLXDevice>( xRenderDevice );
+ if (pDevice)
+ pOut = pDevice->GetOutputDevice();
+ }
+ bChanged = bChanged || (pOut.get() != m_pLast.get());
+ if( pOut )
+ m_pLast = pOut;
+
+ return bChanged;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/vdraw.cxx b/sw/source/core/view/vdraw.cxx
new file mode 100644
index 000000000..11029bb87
--- /dev/null
+++ b/sw/source/core/view/vdraw.cxx
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <fmtanchr.hxx>
+#include <frmfmt.hxx>
+
+#include <svx/svdoutl.hxx>
+
+#include <drawdoc.hxx>
+#include <fesh.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <viewimp.hxx>
+#include <dflyobj.hxx>
+#include <printdata.hxx>
+#include <dcontact.hxx>
+#include <dview.hxx>
+#include <flyfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/canvastools.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <basegfx/range/b2irectangle.hxx>
+
+#include <IDocumentDrawModelAccess.hxx>
+
+void SwViewShellImp::StartAction()
+{
+ if ( HasDrawView() )
+ {
+ CurrShell aCurr( GetShell() );
+ if ( auto pFEShell = dynamic_cast<SwFEShell*>( m_pShell) )
+ pFEShell->HideChainMarker(); // might have changed
+ }
+}
+
+void SwViewShellImp::EndAction()
+{
+ if ( HasDrawView() )
+ {
+ CurrShell aCurr( GetShell() );
+ if ( auto pFEShell = dynamic_cast<SwFEShell*>(m_pShell) )
+ pFEShell->SetChainMarker(); // might have changed
+ }
+}
+
+void SwViewShellImp::LockPaint()
+{
+ if ( HasDrawView() )
+ {
+ m_bResetHdlHiddenPaint = !GetDrawView()->areMarkHandlesHidden();
+ GetDrawView()->hideMarkHandles();
+ }
+ else
+ {
+ m_bResetHdlHiddenPaint = false;
+ }
+}
+
+void SwViewShellImp::UnlockPaint()
+{
+ if ( m_bResetHdlHiddenPaint )
+ GetDrawView()->showMarkHandles();
+}
+
+void SwViewShellImp::PaintLayer( const SdrLayerID _nLayerID,
+ SwPrintData const*const pPrintData,
+ SwPageFrame const& rPageFrame,
+ const SwRect& aPaintRect,
+ const Color* _pPageBackgrdColor,
+ const bool _bIsPageRightToLeft,
+ sdr::contact::ViewObjectContactRedirector* pRedirector )
+{
+ if ( !HasDrawView() )
+ return;
+
+ //change the draw mode in high contrast mode
+ OutputDevice* pOutDev = GetShell()->GetOut();
+ DrawModeFlags nOldDrawMode = pOutDev->GetDrawMode();
+ if( GetShell()->GetWin() &&
+ Application::GetSettings().GetStyleSettings().GetHighContrastMode() &&
+ (!GetShell()->IsPreview() || officecfg::Office::Common::Accessibility::IsForPagePreviews::get()))
+ {
+ pOutDev->SetDrawMode( nOldDrawMode | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill |
+ DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+ }
+
+ // For correct handling of accessibility, high contrast, the
+ // page background color is set as the background color at the
+ // outliner of the draw view. Only necessary for the layers
+ // hell and heaven
+ Color aOldOutlinerBackgroundColor;
+ // set default horizontal text direction on painting <hell> or
+ // <heaven>.
+ EEHorizontalTextDirection aOldEEHoriTextDir = EEHorizontalTextDirection::L2R;
+ const IDocumentDrawModelAccess& rIDDMA = GetShell()->getIDocumentDrawModelAccess();
+ if ( (_nLayerID == rIDDMA.GetHellId()) ||
+ (_nLayerID == rIDDMA.GetHeavenId()) )
+ {
+ OSL_ENSURE( _pPageBackgrdColor,
+ "incorrect usage of SwViewShellImp::PaintLayer: pPageBackgrdColor have to be set for painting layer <hell> or <heaven>");
+ if ( _pPageBackgrdColor )
+ {
+ aOldOutlinerBackgroundColor =
+ GetDrawView()->GetModel()->GetDrawOutliner().GetBackgroundColor();
+ GetDrawView()->GetModel()->GetDrawOutliner().SetBackgroundColor( *_pPageBackgrdColor );
+ }
+
+ aOldEEHoriTextDir =
+ GetDrawView()->GetModel()->GetDrawOutliner().GetDefaultHorizontalTextDirection();
+ EEHorizontalTextDirection aEEHoriTextDirOfPage =
+ _bIsPageRightToLeft ? EEHorizontalTextDirection::R2L : EEHorizontalTextDirection::L2R;
+ GetDrawView()->GetModel()->GetDrawOutliner().SetDefaultHorizontalTextDirection( aEEHoriTextDirOfPage );
+ }
+
+ pOutDev->Push( vcl::PushFlags::LINECOLOR );
+ if (pPrintData)
+ {
+ // hide drawings but not form controls (form controls are handled elsewhere)
+ SdrView &rSdrView = GetPageView()->GetView();
+ rSdrView.setHideDraw( !pPrintData->IsPrintDraw() );
+ }
+ basegfx::B2IRectangle const pageFrame = vcl::unotools::b2IRectangleFromRectangle(rPageFrame.getFrameArea().SVRect());
+ GetPageView()->DrawLayer(_nLayerID, pOutDev, pRedirector, aPaintRect.SVRect(), &pageFrame);
+ pOutDev->Pop();
+
+ // reset background color of the outliner & default horiz. text dir.
+ if ( (_nLayerID == rIDDMA.GetHellId()) ||
+ (_nLayerID == rIDDMA.GetHeavenId()) )
+ {
+ GetDrawView()->GetModel()->GetDrawOutliner().SetBackgroundColor( aOldOutlinerBackgroundColor );
+ GetDrawView()->GetModel()->GetDrawOutliner().SetDefaultHorizontalTextDirection( aOldEEHoriTextDir );
+ }
+
+ pOutDev->SetDrawMode( nOldDrawMode );
+
+}
+
+#define FUZZY_EDGE 400
+
+bool SwViewShellImp::IsDragPossible( const Point &rPoint )
+{
+ if ( !HasDrawView() )
+ return false;
+
+ const SdrMarkList &rMrkList = GetDrawView()->GetMarkedObjectList();
+
+ if( !rMrkList.GetMarkCount() )
+ return false;
+
+ SdrObject *pO = rMrkList.GetMark(rMrkList.GetMarkCount()-1)->GetMarkedSdrObj();
+
+ SwRect aRect;
+ if( pO && ::CalcClipRect( pO, aRect, false ) )
+ {
+ SwRect aTmp;
+ ::CalcClipRect( pO, aTmp );
+ aRect.Union( aTmp );
+ }
+ else
+ aRect = GetShell()->GetLayout()->getFrameArea();
+
+ aRect.AddTop (- FUZZY_EDGE );
+ aRect.AddBottom( FUZZY_EDGE );
+ aRect.AddLeft (- FUZZY_EDGE );
+ aRect.AddRight ( FUZZY_EDGE );
+ return aRect.Contains( rPoint );
+}
+
+void SwViewShellImp::NotifySizeChg( const Size &rNewSz )
+{
+ if ( !HasDrawView() )
+ return;
+
+ if ( GetPageView() )
+ GetPageView()->GetPage()->SetSize( rNewSz );
+
+ // Limitation of the work area
+ const tools::Rectangle aDocRect( Point( DOCUMENTBORDER, DOCUMENTBORDER ), rNewSz );
+ const tools::Rectangle &rOldWork = GetDrawView()->GetWorkArea();
+ bool bCheckDrawObjs = false;
+ if ( aDocRect != rOldWork )
+ {
+ if ( rOldWork.Bottom() > aDocRect.Bottom() || rOldWork.Right() > aDocRect.Right())
+ bCheckDrawObjs = true;
+ GetDrawView()->SetWorkArea( aDocRect );
+ }
+ if ( !bCheckDrawObjs )
+ return;
+
+ OSL_ENSURE( m_pShell->getIDocumentDrawModelAccess().GetDrawModel(), "NotifySizeChg without DrawModel" );
+ SdrPage* pPage = m_pShell->getIDocumentDrawModelAccess().GetDrawModel()->GetPage( 0 );
+ const size_t nObjs = pPage->GetObjCount();
+ for( size_t nObj = 0; nObj < nObjs; ++nObj )
+ {
+ SdrObject *pObj = pPage->GetObj( nObj );
+ if( dynamic_cast<const SwVirtFlyDrawObj*>( pObj) == nullptr )
+ {
+ // Objects not anchored to the frame, do not need to be adjusted
+ const SwContact *pCont = GetUserCall(pObj);
+ // this function might be called by the InsertDocument, when
+ // a PageDesc-Attribute is set on a node. Then the SdrObject
+ // must not have an UserCall.
+ if( !pCont )
+ continue;
+ auto pDrawContact = dynamic_cast<const SwDrawContact*>( pCont);
+ if( !pDrawContact )
+ continue;
+
+ const SwFrame *pAnchor = pDrawContact->GetAnchorFrame();
+ if ( !pAnchor || pAnchor->IsInFly() || !pAnchor->isFrameAreaDefinitionValid() ||
+ !pAnchor->GetUpper() || !pAnchor->FindPageFrame() ||
+ (RndStdIds::FLY_AS_CHAR == pCont->GetFormat()->GetAnchor().GetAnchorId()) )
+ {
+ continue;
+ }
+ else
+ {
+ // Actually this should never happen but currently layouting
+ // is broken. So don't move anchors, if the page is invalid.
+ // This should be turned into a DBG_ASSERT, once layouting is fixed!
+ const SwPageFrame *pPageFrame = pAnchor->FindPageFrame();
+ if (!pPageFrame || pPageFrame->IsInvalid() ) {
+ SAL_WARN( "sw.core", "Trying to move anchor from invalid page - fix layouting!" );
+ continue;
+ }
+ }
+
+ // no move for drawing objects in header/footer
+ if ( pAnchor->FindFooterOrHeader() )
+ {
+ continue;
+ }
+
+ const tools::Rectangle aObjBound( pObj->GetCurrentBoundRect() );
+ if ( !aDocRect.Contains( aObjBound ) )
+ {
+ Size aSz;
+ if ( aObjBound.Left() > aDocRect.Right() )
+ aSz.setWidth( (aDocRect.Right() - aObjBound.Left()) - MINFLY );
+ if ( aObjBound.Top() > aDocRect.Bottom() )
+ aSz.setHeight( (aDocRect.Bottom() - aObjBound.Top()) - MINFLY );
+ if ( aSz.Width() || aSz.Height() )
+ pObj->Move( aSz );
+
+ // Don't let large objects disappear to the top
+ aSz.setWidth(0);
+ aSz.setHeight(0);
+ if ( aObjBound.Right() < aDocRect.Left() )
+ aSz.setWidth( (aDocRect.Left() - aObjBound.Right()) + MINFLY );
+ if ( aObjBound.Bottom() < aDocRect.Top() )
+ aSz.setHeight( (aDocRect.Top() - aObjBound.Bottom()) + MINFLY );
+ if ( aSz.Width() || aSz.Height() )
+ pObj->Move( aSz );
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/viewimp.cxx b/sw/source/core/view/viewimp.cxx
new file mode 100644
index 000000000..f44850d70
--- /dev/null
+++ b/sw/source/core/view/viewimp.cxx
@@ -0,0 +1,501 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <flyfrm.hxx>
+#include <layact.hxx>
+#include <dview.hxx>
+#include <svx/svdpage.hxx>
+#include <accmap.hxx>
+
+#include <officecfg/Office/Common.hxx>
+#include <pagepreviewlayout.hxx>
+#include <comphelper/lok.hxx>
+#include <tools/diagnose_ex.h>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <drawdoc.hxx>
+#include <prevwpage.hxx>
+
+void SwViewShellImp::Init( const SwViewOption *pNewOpt )
+{
+ OSL_ENSURE( m_pDrawView, "SwViewShellImp::Init without DrawView" );
+ //Create PageView if it doesn't exist
+ SwRootFrame *pRoot = m_pShell->GetLayout();
+ if ( !m_pSdrPageView )
+ {
+ IDocumentDrawModelAccess& rIDDMA = m_pShell->getIDocumentDrawModelAccess();
+ if ( !pRoot->GetDrawPage() )
+ pRoot->SetDrawPage( rIDDMA.GetDrawModel()->GetPage( 0 ) );
+
+ if ( pRoot->GetDrawPage()->GetSize() != pRoot->getFrameArea().SSize() )
+ pRoot->GetDrawPage()->SetSize( pRoot->getFrameArea().SSize() );
+
+ m_pSdrPageView = m_pDrawView->ShowSdrPage( pRoot->GetDrawPage());
+ // Notify drawing page view about invisible layers
+ rIDDMA.NotifyInvisibleLayers( *m_pSdrPageView );
+ }
+ m_pDrawView->SetDragStripes( pNewOpt->IsCrossHair() );
+ m_pDrawView->SetGridSnap( pNewOpt->IsSnap() );
+ m_pDrawView->SetGridVisible( pNewOpt->IsGridVisible() );
+ const Size &rSz = pNewOpt->GetSnapSize();
+ m_pDrawView->SetGridCoarse( rSz );
+ const Size aFSize
+ ( rSz.Width() ? rSz.Width() /std::max(short(1),pNewOpt->GetDivisionX()):0,
+ rSz.Height()? rSz.Height()/std::max(short(1),pNewOpt->GetDivisionY()):0);
+ m_pDrawView->SetGridFine( aFSize );
+ Fraction aSnGrWdtX(rSz.Width(), pNewOpt->GetDivisionX() + 1);
+ Fraction aSnGrWdtY(rSz.Height(), pNewOpt->GetDivisionY() + 1);
+ m_pDrawView->SetSnapGridWidth( aSnGrWdtX, aSnGrWdtY );
+
+ if ( pRoot->getFrameArea().HasArea() )
+ m_pDrawView->SetWorkArea( pRoot->getFrameArea().SVRect() );
+
+ if ( GetShell()->IsPreview() )
+ m_pDrawView->SetAnimationEnabled( false );
+
+ m_pDrawView->SetUseIncompatiblePathCreateInterface( false );
+
+ // set handle size to 9 pixels, always
+ m_pDrawView->SetMarkHdlSizePixel(9);
+}
+
+/// CTor for the core internals
+SwViewShellImp::SwViewShellImp( SwViewShell *pParent ) :
+ m_pShell( pParent ),
+ m_pSdrPageView( nullptr ),
+ m_pFirstVisiblePage( nullptr ),
+ m_pLayAction( nullptr ),
+ m_pIdleAct( nullptr ),
+ m_bFirstPageInvalid( true ),
+ m_bResetHdlHiddenPaint( false ),
+ m_bSmoothUpdate( false ),
+ m_bStopSmooth( false ),
+ m_nRestoreActions( 0 )
+{
+}
+
+SwViewShellImp::~SwViewShellImp()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ m_pAccessibleMap.reset();
+#endif
+
+ m_pPagePreviewLayout.reset();
+
+ // Make sure HideSdrPage is also executed after ShowSdrPage.
+ if( m_pDrawView )
+ m_pDrawView->HideSdrPage();
+
+ m_pDrawView.reset();
+
+ DeletePaintRegion();
+
+ OSL_ENSURE( !m_pLayAction, "Have action for the rest of your life." );
+ OSL_ENSURE( !m_pIdleAct,"Be idle for the rest of your life." );
+}
+
+bool SwViewShellImp::AddPaintRect( const SwRect &rRect )
+{
+ // In case of tiled rendering the visual area is the last painted tile -> not interesting.
+ if ( rRect.Overlaps( m_pShell->VisArea() ) || comphelper::LibreOfficeKit::isActive() )
+ {
+ if ( !m_pPaintRegion )
+ {
+ // In case of normal rendering, this makes sure only visible rectangles are painted.
+ // Otherwise get the rectangle of the full document, so all paint rectangles are invalidated.
+ const SwRect& rArea = comphelper::LibreOfficeKit::isActive() ? m_pShell->GetLayout()->getFrameArea() : m_pShell->VisArea();
+ m_pPaintRegion.reset(new SwRegionRects);
+ m_pPaintRegion->ChangeOrigin(rArea);
+ }
+ if(!m_pPaintRegion->empty())
+ {
+ // This function often gets called with rectangles that line up vertically.
+ // Try to extend the last one downwards to include the new one (use Union()
+ // in case the new one is actually already contained in the last one).
+ SwRect& last = m_pPaintRegion->back();
+ if(last.Left() == rRect.Left() && last.Width() == rRect.Width()
+ && last.Bottom() + 1 >= rRect.Top() && last.Bottom() <= rRect.Bottom())
+ {
+ last.Union(rRect);
+ // And these rectangles lined up vertically often come up in groups
+ // that line up horizontally. Try to extend the previous rectangle
+ // to the right to include the last one.
+ if(m_pPaintRegion->size() > 1)
+ {
+ SwRect& last2 = (*m_pPaintRegion)[m_pPaintRegion->size() - 2];
+ if(last2.Top() == last.Top() && last2.Height() == last.Height()
+ && last2.Right() + 1 >= last.Left() && last2.Right() <= last2.Right())
+ {
+ last2.Union(last);
+ m_pPaintRegion->pop_back();
+ return true;
+ }
+ }
+ return true;
+ }
+ }
+ (*m_pPaintRegion) += rRect;
+ return true;
+ }
+ return false;
+}
+
+void SwViewShellImp::AddPendingLOKInvalidation( const SwRect& rRect )
+{
+ // These are often repeated, so check first for duplicates.
+ std::vector<SwRect>& l = m_pendingLOKInvalidations;
+ if( std::find( l.begin(), l.end(), rRect ) == l.end())
+ l.push_back( rRect );
+}
+
+std::vector<SwRect> SwViewShellImp::TakePendingLOKInvalidations()
+{
+ std::vector<SwRect> ret;
+ std::swap(ret, m_pendingLOKInvalidations);
+ return ret;
+}
+
+void SwViewShellImp::CheckWaitCursor()
+{
+ if ( m_pLayAction )
+ m_pLayAction->CheckWaitCursor();
+}
+
+bool SwViewShellImp::IsCalcLayoutProgress() const
+{
+ return m_pLayAction && m_pLayAction->IsCalcLayout();
+}
+
+bool SwViewShellImp::IsUpdateExpFields()
+{
+ if ( m_pLayAction && m_pLayAction->IsCalcLayout() )
+ {
+ m_pLayAction->SetUpdateExpFields();
+ return true;
+ }
+ return false;
+}
+
+void SwViewShellImp::SetFirstVisPage(OutputDevice const * pRenderContext)
+{
+ if ( m_pShell->mbDocSizeChgd && m_pShell->VisArea().Top() > m_pShell->GetLayout()->getFrameArea().Height() )
+ {
+ //We are in an action and because of erase actions the VisArea is
+ //after the first visible page.
+ //To avoid excessive formatting, hand back the last page.
+ m_pFirstVisiblePage = static_cast<SwPageFrame*>(m_pShell->GetLayout()->Lower());
+ while ( m_pFirstVisiblePage && m_pFirstVisiblePage->GetNext() )
+ m_pFirstVisiblePage = static_cast<SwPageFrame*>(m_pFirstVisiblePage->GetNext());
+ }
+ else
+ {
+ const SwViewOption* pSwViewOption = GetShell()->GetViewOptions();
+ const bool bBookMode = pSwViewOption->IsViewLayoutBookMode();
+
+ SwPageFrame *pPage = static_cast<SwPageFrame*>(m_pShell->GetLayout()->Lower());
+ SwRect aPageRect = pPage->GetBoundRect(pRenderContext);
+ while ( pPage && !aPageRect.Overlaps( m_pShell->VisArea() ) )
+ {
+ pPage = static_cast<SwPageFrame*>(pPage->GetNext());
+ if ( pPage )
+ {
+ aPageRect = pPage->GetBoundRect(pRenderContext);
+ if ( bBookMode && pPage->IsEmptyPage() )
+ {
+ const SwPageFrame& rFormatPage = pPage->GetFormatPage();
+ aPageRect.SSize( rFormatPage.GetBoundRect(pRenderContext).SSize() );
+ }
+ }
+ }
+ m_pFirstVisiblePage = pPage ? pPage : static_cast<SwPageFrame*>(m_pShell->GetLayout()->Lower());
+ }
+ m_bFirstPageInvalid = false;
+}
+
+void SwViewShellImp::MakeDrawView()
+{
+ IDocumentDrawModelAccess& rIDDMA = GetShell()->getIDocumentDrawModelAccess();
+
+ // the else here is not an error, MakeDrawModel_() calls this method again
+ // after the DrawModel is created to create DrawViews for all shells...
+ if( !rIDDMA.GetDrawModel() )
+ {
+ rIDDMA.MakeDrawModel_();
+ }
+ else
+ {
+ if ( !m_pDrawView )
+ {
+ // #i72809#
+ // Discussed with FME, he also thinks that the getPrinter is old and not correct. When i got
+ // him right, it anyways returns GetOut() when it's a printer, but NULL when not. He suggested
+ // to use GetOut() and check the existing cases.
+ // Check worked well. Took a look at viewing, printing, PDF export and print preview with a test
+ // document which has an empty 2nd page (right page, see bug)
+ auto pWin = GetShell()->GetWin();
+ OutputDevice* pOutDevForDrawView = pWin ? pWin->GetOutDev() : nullptr;
+
+ if(!pOutDevForDrawView)
+ {
+ pOutDevForDrawView = GetShell()->GetOut();
+ }
+
+ m_pDrawView.reset( new SwDrawView(
+ *this,
+ *rIDDMA.GetOrCreateDrawModel(),
+ pOutDevForDrawView) );
+ }
+
+ GetDrawView()->SetActiveLayer("Heaven");
+ const SwViewOption* pSwViewOption = GetShell()->GetViewOptions();
+ Init(pSwViewOption);
+
+ // #i68597# If document is read-only, we will not profit from overlay,
+ // so switch it off.
+ if (m_pDrawView->IsBufferedOverlayAllowed())
+ {
+ if(pSwViewOption->IsReadonly())
+ {
+ m_pDrawView->SetBufferedOverlayAllowed(false);
+ }
+ }
+ }
+}
+
+Color SwViewShellImp::GetRetoucheColor() const
+{
+ Color aRet( COL_TRANSPARENT );
+ const SwViewShell &rSh = *GetShell();
+ if ( rSh.GetWin() )
+ {
+ if ( rSh.GetViewOptions()->getBrowseMode() &&
+ COL_TRANSPARENT != rSh.GetViewOptions()->GetRetoucheColor() )
+ aRet = rSh.GetViewOptions()->GetRetoucheColor();
+ else if(rSh.GetViewOptions()->IsPagePreview() &&
+ !officecfg::Office::Common::Accessibility::IsForPagePreviews::get())
+ aRet = COL_WHITE;
+ else
+ aRet = SwViewOption::GetDocColor();
+ }
+ return aRet;
+}
+
+SwPageFrame *SwViewShellImp::GetFirstVisPage(OutputDevice const * pRenderContext)
+{
+ if ( m_bFirstPageInvalid )
+ SetFirstVisPage(pRenderContext);
+ return m_pFirstVisiblePage;
+}
+
+const SwPageFrame *SwViewShellImp::GetFirstVisPage(OutputDevice const * pRenderContext) const
+{
+ if ( m_bFirstPageInvalid )
+ const_cast<SwViewShellImp*>(this)->SetFirstVisPage(pRenderContext);
+ return m_pFirstVisiblePage;
+}
+
+// create page preview layout
+void SwViewShellImp::InitPagePreviewLayout()
+{
+ OSL_ENSURE( m_pShell->GetLayout(), "no layout - page preview layout can not be created.");
+ if ( m_pShell->GetLayout() )
+ m_pPagePreviewLayout.reset( new SwPagePreviewLayout( *m_pShell, *(m_pShell->GetLayout()) ) );
+}
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+void SwViewShellImp::UpdateAccessible()
+{
+ // We require a layout and an XModel to be accessible.
+ IDocumentLayoutAccess& rIDLA = GetShell()->getIDocumentLayoutAccess();
+ vcl::Window *pWin = GetShell()->GetWin();
+ OSL_ENSURE( GetShell()->GetLayout(), "no layout, no access" );
+ OSL_ENSURE( pWin, "no window, no access" );
+
+ if( IsAccessible() && rIDLA.GetCurrentViewShell() && pWin )
+ {
+ try
+ {
+ GetAccessibleMap().GetDocumentView();
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("sw.a11y", "");
+ assert(!"SwViewShellImp::UpdateAccessible: unhandled exception");
+ }
+ }
+}
+
+void SwViewShellImp::DisposeAccessible(const SwFrame *pFrame,
+ const SdrObject *pObj,
+ bool bRecursive,
+ bool bCanSkipInvisible)
+{
+ OSL_ENSURE( !pFrame || pFrame->IsAccessibleFrame(), "frame is not accessible" );
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if( rTmp.Imp()->IsAccessible() )
+ rTmp.Imp()->GetAccessibleMap().A11yDispose( pFrame, pObj, nullptr, bRecursive, bCanSkipInvisible );
+ }
+}
+
+void SwViewShellImp::MoveAccessible( const SwFrame *pFrame, const SdrObject *pObj,
+ const SwRect& rOldFrame )
+{
+ OSL_ENSURE( !pFrame || pFrame->IsAccessibleFrame(), "frame is not accessible" );
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if( rTmp.Imp()->IsAccessible() )
+ rTmp.Imp()->GetAccessibleMap().InvalidatePosOrSize( pFrame, pObj, nullptr,
+ rOldFrame );
+ }
+}
+
+void SwViewShellImp::InvalidateAccessibleFrameContent( const SwFrame *pFrame )
+{
+ OSL_ENSURE( pFrame->IsAccessibleFrame(), "frame is not accessible" );
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if( rTmp.Imp()->IsAccessible() )
+ rTmp.Imp()->GetAccessibleMap().InvalidateContent( pFrame );
+ }
+}
+
+void SwViewShellImp::InvalidateAccessibleCursorPosition( const SwFrame *pFrame )
+{
+ if( IsAccessible() )
+ GetAccessibleMap().InvalidateCursorPosition( pFrame );
+}
+
+void SwViewShellImp::InvalidateAccessibleEditableState( bool bAllShells,
+ const SwFrame *pFrame )
+{
+ if( bAllShells )
+ {
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if( rTmp.Imp()->IsAccessible() )
+ rTmp.Imp()->GetAccessibleMap().InvalidateEditableStates( pFrame );
+ }
+ }
+ else if( IsAccessible() )
+ {
+ GetAccessibleMap().InvalidateEditableStates( pFrame );
+ }
+}
+
+void SwViewShellImp::InvalidateAccessibleRelationSet( const SwFlyFrame *pMaster,
+ const SwFlyFrame *pFollow )
+{
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if( rTmp.Imp()->IsAccessible() )
+ rTmp.Imp()->GetAccessibleMap().InvalidateRelationSet( pMaster,
+ pFollow );
+ }
+}
+
+/// invalidate CONTENT_FLOWS_FROM/_TO relation for paragraphs
+void SwViewShellImp::InvalidateAccessibleParaFlowRelation_( const SwTextFrame* _pFromTextFrame,
+ const SwTextFrame* _pToTextFrame )
+{
+ if ( !_pFromTextFrame && !_pToTextFrame )
+ {
+ // No text frame provided. Thus, nothing to do.
+ return;
+ }
+
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if ( rTmp.Imp()->IsAccessible() )
+ {
+ if ( _pFromTextFrame )
+ {
+ rTmp.Imp()->GetAccessibleMap().
+ InvalidateParaFlowRelation( *_pFromTextFrame, true );
+ }
+ if ( _pToTextFrame )
+ {
+ rTmp.Imp()->GetAccessibleMap().
+ InvalidateParaFlowRelation( *_pToTextFrame, false );
+ }
+ }
+ }
+}
+
+/// invalidate text selection for paragraphs
+void SwViewShellImp::InvalidateAccessibleParaTextSelection_()
+{
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if ( rTmp.Imp()->IsAccessible() )
+ {
+ rTmp.Imp()->GetAccessibleMap().InvalidateTextSelectionOfAllParas();
+ }
+ }
+}
+
+/// invalidate attributes for paragraphs
+void SwViewShellImp::InvalidateAccessibleParaAttrs_( const SwTextFrame& rTextFrame )
+{
+ for(SwViewShell& rTmp : GetShell()->GetRingContainer())
+ {
+ if ( rTmp.Imp()->IsAccessible() )
+ {
+ rTmp.Imp()->GetAccessibleMap().InvalidateAttr( rTextFrame );
+ }
+ }
+}
+
+void SwViewShellImp::UpdateAccessiblePreview( const std::vector<std::unique_ptr<PreviewPage>>& _rPreviewPages,
+ const Fraction& _rScale,
+ const SwPageFrame* _pSelectedPageFrame,
+ const Size& _rPreviewWinSize )
+{
+ if( IsAccessible() )
+ GetAccessibleMap().UpdatePreview( _rPreviewPages, _rScale,
+ _pSelectedPageFrame, _rPreviewWinSize );
+}
+
+void SwViewShellImp::InvalidateAccessiblePreviewSelection( sal_uInt16 nSelPage )
+{
+ if( IsAccessible() )
+ GetAccessibleMap().InvalidatePreviewSelection( nSelPage );
+}
+
+SwAccessibleMap *SwViewShellImp::CreateAccessibleMap()
+{
+ assert(!m_pAccessibleMap);
+ m_pAccessibleMap = std::make_shared<SwAccessibleMap>(GetShell());
+ return m_pAccessibleMap.get();
+}
+
+void SwViewShellImp::FireAccessibleEvents()
+{
+ if( IsAccessible() )
+ GetAccessibleMap().FireEvents();
+}
+#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/viewpg.cxx b/sw/source/core/view/viewpg.cxx
new file mode 100644
index 000000000..4d2385483
--- /dev/null
+++ b/sw/source/core/view/viewpg.cxx
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <tools/fract.hxx>
+#include <osl/diagnose.h>
+#include <viewsh.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <printdata.hxx>
+#include <ptqueue.hxx>
+#include <fntcache.hxx>
+
+#include "vprint.hxx"
+
+using namespace ::com::sun::star;
+
+SwPagePreviewLayout* SwViewShell::PagePreviewLayout()
+{
+ return Imp()->PagePreviewLayout();
+}
+
+void SwViewShell::ShowPreviewSelection( sal_uInt16 nSelPage )
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ Imp()->InvalidateAccessiblePreviewSelection( nSelPage );
+#else
+ (void)nSelPage;
+#endif
+}
+
+// adjust view options for page preview
+void SwViewShell::AdjustOptionsForPagePreview(SwPrintData const& rPrintOptions)
+{
+ if ( !IsPreview() )
+ {
+ OSL_FAIL( "view shell doesn't belongs to a page preview - no adjustment of its view options");
+ return;
+ }
+
+ PrepareForPrint( rPrintOptions );
+}
+
+/// print brochure
+// consider empty pages on calculation of the scaling
+// for a page to be printed.
+void SwViewShell::PrintProspect(
+ OutputDevice *pOutDev,
+ const SwPrintData &rPrintData,
+ sal_Int32 nRenderer // the index in the vector of prospect pages to be printed
+ )
+{
+ const sal_Int32 nMaxRenderer = rPrintData.GetRenderData().GetPagePairsForProspectPrinting().size() - 1;
+ OSL_ENSURE( 0 <= nRenderer && nRenderer <= nMaxRenderer, "nRenderer out of bounds");
+ Printer *pPrinter = dynamic_cast< Printer * >(pOutDev);
+ if (!pPrinter || nMaxRenderer < 0 || nRenderer < 0 || nRenderer > nMaxRenderer)
+ return;
+
+ // save settings of OutputDevice (should be done always since the
+ // output device is now provided by a call from outside the Writer)
+ pPrinter->Push();
+
+ std::pair< sal_Int32, sal_Int32 > rPagesToPrint =
+ rPrintData.GetRenderData().GetPagePairsForProspectPrinting()[ nRenderer ];
+ OSL_ENSURE( rPagesToPrint.first == -1 || rPrintData.GetRenderData().GetValidPagesSet().count( rPagesToPrint.first ) == 1, "first Page not valid" );
+ OSL_ENSURE( rPagesToPrint.second == -1 || rPrintData.GetRenderData().GetValidPagesSet().count( rPagesToPrint.second ) == 1, "second Page not valid" );
+
+ // create a new shell for the printer
+ SwViewShell aShell( *this, nullptr, pPrinter );
+
+ CurrShell aCurr( &aShell );
+
+ aShell.PrepareForPrint( rPrintData );
+
+ //!! applying view options and formatting the document should now only be done in getRendererCount!
+
+ MapMode aMapMode( MapUnit::MapTwip );
+ Size aPrtSize( pPrinter->PixelToLogic( pPrinter->GetPaperSizePixel(), aMapMode ) );
+
+ SwTwips nMaxRowSz, nMaxColSz;
+
+ const SwPageFrame *pStPage = nullptr;
+ const SwPageFrame *pNxtPage = nullptr;
+ if (rPagesToPrint.first > 0)
+ {
+ pStPage = sw_getPage(*aShell.GetLayout(), rPagesToPrint.first);
+ }
+ if (rPagesToPrint.second > 0)
+ {
+ pNxtPage = sw_getPage(*aShell.GetLayout(), rPagesToPrint.second);
+ }
+
+ // i#14016 consider empty pages on calculation
+ // of page size, used for calculation of scaling.
+ Size aSttPageSize;
+ if ( pStPage )
+ {
+ if ( pStPage->IsEmptyPage() )
+ {
+ if ( pStPage->GetPhyPageNum() % 2 == 0 )
+ aSttPageSize = pStPage->GetPrev()->getFrameArea().SSize();
+ else
+ aSttPageSize = pStPage->GetNext()->getFrameArea().SSize();
+ }
+ else
+ {
+ aSttPageSize = pStPage->getFrameArea().SSize();
+ }
+ }
+ Size aNxtPageSize;
+ if ( pNxtPage )
+ {
+ if ( pNxtPage->IsEmptyPage() )
+ {
+ if ( pNxtPage->GetPhyPageNum() % 2 == 0 )
+ aNxtPageSize = pNxtPage->GetPrev()->getFrameArea().SSize();
+ else
+ aNxtPageSize = pNxtPage->GetNext()->getFrameArea().SSize();
+ }
+ else
+ {
+ aNxtPageSize = pNxtPage->getFrameArea().SSize();
+ }
+ }
+
+ if( !pStPage )
+ {
+ nMaxColSz = 2 * aNxtPageSize.Width();
+ nMaxRowSz = aNxtPageSize.Height();
+ }
+ else if( !pNxtPage )
+ {
+ nMaxColSz = 2 * aSttPageSize.Width();
+ nMaxRowSz = aSttPageSize.Height();
+ }
+ else
+ {
+ nMaxColSz = aNxtPageSize.Width() + aSttPageSize.Width();
+ nMaxRowSz = std::max( aNxtPageSize.Height(), aSttPageSize.Height() );
+ }
+
+ // set the MapMode
+ aMapMode.SetOrigin( Point() );
+ {
+ Fraction aScX( aPrtSize.Width(), nMaxColSz );
+ Fraction aScY( aPrtSize.Height(), nMaxRowSz );
+ if( aScX < aScY )
+ aScY = aScX;
+
+ {
+ // Round percentages for Drawings so that these can paint their objects properly
+ aScY *= Fraction( 1000, 1 );
+ tools::Long nTmp = static_cast<tools::Long>(aScY);
+ if( 1 < nTmp )
+ --nTmp;
+ else
+ nTmp = 1;
+ aScY = Fraction( nTmp, 1000 );
+ }
+
+ aMapMode.SetScaleY( aScY );
+ aMapMode.SetScaleX( aScY );
+ }
+
+ Size aTmpPrtSize( pPrinter->PixelToLogic( pPrinter->GetPaperSizePixel(), aMapMode ) );
+
+ // calculate start point for equal border on all sides
+ Point aSttPt( (aTmpPrtSize.Width() - nMaxColSz) / 2,
+ (aTmpPrtSize.Height() - nMaxRowSz) / 2 );
+ for( int nC = 0; nC < 2; ++nC )
+ {
+ if( pStPage )
+ {
+ aShell.Imp()->SetFirstVisPageInvalid();
+ aShell.maVisArea = pStPage->getFrameArea();
+
+ Point aPos( aSttPt );
+ aPos -= aShell.maVisArea.Pos();
+ aMapMode.SetOrigin( aPos );
+ pPrinter->SetMapMode( aMapMode );
+ pStPage->GetUpper()->PaintSwFrame( *pOutDev, pStPage->getFrameArea() );
+ }
+
+ pStPage = pNxtPage;
+ aSttPt.AdjustX(aTmpPrtSize.Width() / 2 );
+ }
+
+ SwPaintQueue::Repaint();
+
+ //!! applying/modifying view options and formatting the document should now only be done in getRendererCount!
+
+ pFntCache->Flush();
+
+ // restore settings of OutputDevice (should be done always now since the
+ // output device is now provided by a call from outside the Writer)
+ pPrinter->Pop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/viewsh.cxx b/sw/source/core/view/viewsh.cxx
new file mode 100644
index 000000000..057b69878
--- /dev/null
+++ b/sw/source/core/view/viewsh.cxx
@@ -0,0 +1,2809 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <officecfg/Office/Common.hxx>
+#include <config_wasm_strip.h>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/progress.hxx>
+#include <svx/srchdlg.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sal/log.hxx>
+#include <drawdoc.hxx>
+#include <swwait.hxx>
+#include <crsrsh.hxx>
+#include <doc.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentOutlineNodes.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentState.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <viewimp.hxx>
+#include <frmtool.hxx>
+#include <viewopt.hxx>
+#include <dview.hxx>
+#include <swregion.hxx>
+#include <hints.hxx>
+#include <docufld.hxx>
+#include <txtfrm.hxx>
+#include <layact.hxx>
+#include <mdiexp.hxx>
+#include <fntcache.hxx>
+#include <ptqueue.hxx>
+#include <docsh.hxx>
+#include <bookmark.hxx>
+#include <ndole.hxx>
+#include <ndindex.hxx>
+#include <accmap.hxx>
+#include <vcl/bitmapex.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <accessibilityoptions.hxx>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <pagepreviewlayout.hxx>
+#include <sortedobjs.hxx>
+#include <anchoredobject.hxx>
+#include <DocumentSettingManager.hxx>
+#include <DocumentRedlineManager.hxx>
+
+#include <unotxdoc.hxx>
+#include <view.hxx>
+#include <PostItMgr.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <tools/UnitConversion.hxx>
+
+#if !HAVE_FEATURE_DESKTOP
+#include <vcl/sysdata.hxx>
+#endif
+
+#include <frameformats.hxx>
+#include <fmtcntnt.hxx>
+
+bool SwViewShell::sbLstAct = false;
+ShellResource *SwViewShell::spShellRes = nullptr;
+vcl::DeleteOnDeinit<std::shared_ptr<weld::Window>> SwViewShell::spCareDialog {};
+
+static bool bInSizeNotify = false;
+
+
+using namespace ::com::sun::star;
+
+void SwViewShell::SetShowHeaderFooterSeparator( FrameControlType eControl, bool bShow ) {
+
+ //tdf#118621 - Optionally disable floating header/footer menu
+ if ( bShow )
+ bShow = GetViewOptions()->IsUseHeaderFooterMenu();
+
+ if ( eControl == FrameControlType::Header )
+ mbShowHeaderSeparator = bShow;
+ else
+ mbShowFooterSeparator = bShow;
+}
+
+void SwViewShell::ToggleHeaderFooterEdit()
+{
+ mbHeaderFooterEdit = !mbHeaderFooterEdit;
+ if ( !mbHeaderFooterEdit )
+ {
+ SetShowHeaderFooterSeparator( FrameControlType::Header, false );
+ SetShowHeaderFooterSeparator( FrameControlType::Footer, false );
+ }
+
+ // Avoid corner case
+ if ( ( GetViewOptions()->IsUseHeaderFooterMenu() ) &&
+ ( !IsShowHeaderFooterSeparator( FrameControlType::Header ) &&
+ !IsShowHeaderFooterSeparator( FrameControlType::Footer ) ) )
+ {
+ mbHeaderFooterEdit = false;
+ }
+
+ // Repaint everything
+ GetWin()->Invalidate();
+}
+
+void SwViewShell::setOutputToWindow(bool bOutputToWindow)
+{
+ mbOutputToWindow = bOutputToWindow;
+}
+
+bool SwViewShell::isOutputToWindow() const
+{
+ return mbOutputToWindow;
+}
+
+void SwViewShell::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwViewShell"));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+static void
+lcl_PaintTransparentFormControls(SwViewShell const & rShell, SwRect const& rRect)
+{
+ // Direct paint has been performed: the background of transparent child
+ // windows has been painted, so need to paint the child windows now.
+ if (rShell.GetWin())
+ {
+ vcl::Window& rWindow = *(rShell.GetWin());
+ const tools::Rectangle aRectanglePixel(rShell.GetOut()->LogicToPixel(rRect.SVRect()));
+ PaintTransparentChildren(rWindow, aRectanglePixel);
+ }
+}
+
+// #i72754# 2nd set of Pre/PostPaints
+// This time it uses the lock counter (mPrePostPaintRegions empty/non-empty) to allow only one activation
+// and deactivation and mpPrePostOutDev to remember the OutDev from the BeginDrawLayers
+// call. That way, all places where paint take place can be handled the same way, even
+// when calling other paint methods. This is the case at the places where SW paints
+// buffered into VDevs to avoid flicker. It is in general problematic and should be
+// solved once using the BufferedOutput functionality of the DrawView.
+
+void SwViewShell::PrePaint()
+{
+ // forward PrePaint event from VCL Window to DrawingLayer
+ if(HasDrawView())
+ {
+ Imp()->GetDrawView()->PrePaint();
+ }
+}
+
+void SwViewShell::DLPrePaint2(const vcl::Region& rRegion)
+{
+ if(mPrePostPaintRegions.empty())
+ {
+ mPrePostPaintRegions.push( rRegion );
+ // #i75172# ensure DrawView to use DrawingLayer bufferings
+ if ( !HasDrawView() )
+ MakeDrawView();
+
+ // Prefer window; if not available, get mpOut (e.g. printer)
+ const bool bWindow = GetWin() && !comphelper::LibreOfficeKit::isActive() && !isOutputToWindow();
+ mpPrePostOutDev = bWindow ? GetWin()->GetOutDev() : GetOut();
+
+ // #i74769# use SdrPaintWindow now direct
+ mpTargetPaintWindow = Imp()->GetDrawView()->BeginDrawLayers(mpPrePostOutDev, rRegion);
+ OSL_ENSURE(mpTargetPaintWindow, "BeginDrawLayers: Got no SdrPaintWindow (!)");
+
+ // #i74769# if prerender, save OutDev and redirect to PreRenderDevice
+ if(mpTargetPaintWindow->GetPreRenderDevice())
+ {
+ mpBufferedOut = mpOut;
+ mpOut = &(mpTargetPaintWindow->GetTargetOutputDevice());
+ }
+ else if (isOutputToWindow())
+ // In case mpOut is used without buffering and we're not printing, need to set clipping.
+ mpOut->SetClipRegion(rRegion);
+
+ // remember original paint MapMode for wrapped FlyFrame paints
+ maPrePostMapMode = mpOut->GetMapMode();
+ }
+ else
+ {
+ // region needs to be updated to the given one
+ if( mPrePostPaintRegions.top() != rRegion )
+ Imp()->GetDrawView()->UpdateDrawLayersRegion(mpPrePostOutDev, rRegion);
+ mPrePostPaintRegions.push( rRegion );
+ }
+}
+
+void SwViewShell::DLPostPaint2(bool bPaintFormLayer)
+{
+ OSL_ENSURE(!mPrePostPaintRegions.empty(), "SwViewShell::DLPostPaint2: Pre/PostPaint encapsulation broken (!)");
+
+ if( mPrePostPaintRegions.size() > 1 )
+ {
+ vcl::Region current = std::move(mPrePostPaintRegions.top());
+ mPrePostPaintRegions.pop();
+ if( current != mPrePostPaintRegions.top())
+ Imp()->GetDrawView()->UpdateDrawLayersRegion(mpPrePostOutDev, mPrePostPaintRegions.top());
+ return;
+ }
+ mPrePostPaintRegions.pop(); // clear
+ if(nullptr != mpTargetPaintWindow)
+ {
+ // #i74769# restore buffered OutDev
+ if(mpTargetPaintWindow->GetPreRenderDevice())
+ {
+ mpOut = mpBufferedOut;
+ }
+
+ // #i74769# use SdrPaintWindow now direct
+ Imp()->GetDrawView()->EndDrawLayers(*mpTargetPaintWindow, bPaintFormLayer);
+ mpTargetPaintWindow = nullptr;
+ }
+}
+// end of Pre/PostPaints
+
+void SwViewShell::ImplEndAction( const bool bIdleEnd )
+{
+ // Nothing to do for the printer?
+ if ( !GetWin() || IsPreview() )
+ {
+ mbPaintWorks = true;
+ UISizeNotify();
+ // tdf#101464 print preview may generate events if another view shell
+ // performs layout...
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (IsPreview() && Imp()->IsAccessible())
+ {
+ Imp()->FireAccessibleEvents();
+ }
+#endif
+ return;
+ }
+
+ mbInEndAction = true;
+ //will this put the EndAction of the last shell in the sequence?
+
+ SwViewShell::sbLstAct = true;
+ for(SwViewShell& rShell : GetRingContainer())
+ {
+ if(&rShell != this && rShell.ActionPend())
+ {
+ SwViewShell::sbLstAct = false;
+ break;
+ }
+ }
+
+ const bool bIsShellForCheckViewLayout = ( this == GetLayout()->GetCurrShell() );
+
+ CurrShell aCurr( this );
+ if ( Imp()->HasDrawView() && !Imp()->GetDrawView()->areMarkHandlesHidden() )
+ Imp()->StartAction();
+
+ if ( Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea() )
+ Imp()->DeletePaintRegion();
+
+ const bool bExtraData = ::IsExtraData( GetDoc() );
+
+ if ( !bIdleEnd )
+ {
+ SwLayAction aAction( GetLayout(), Imp() );
+ aAction.SetComplete( false );
+ if ( mnLockPaint )
+ aAction.SetPaint( false );
+ aAction.SetInputType( VclInputFlags::KEYBOARD );
+ aAction.Action(GetWin()->GetOutDev());
+ }
+
+ if ( bIsShellForCheckViewLayout )
+ GetLayout()->CheckViewLayout( GetViewOptions(), &maVisArea );
+
+ //If we don't call Paints, we wait for the Paint of the system.
+ //Then the clipping is set correctly; e.g. shifting of a Draw object
+ if ( Imp()->HasPaintRegion() ||
+ maInvalidRect.HasArea() ||
+ bExtraData )
+ {
+ if ( !mnLockPaint )
+ {
+ SolarMutexGuard aGuard;
+
+ bool bPaintsFromSystem = maInvalidRect.HasArea();
+ GetWin()->PaintImmediately();
+ if ( maInvalidRect.HasArea() )
+ {
+ if ( bPaintsFromSystem )
+ Imp()->AddPaintRect( maInvalidRect );
+
+ ResetInvalidRect();
+ bPaintsFromSystem = true;
+ }
+ mbPaintWorks = true;
+
+ std::unique_ptr<SwRegionRects> pRegion = Imp()->TakePaintRegion();
+
+ //JP 27.11.97: what hid the selection, must also Show it,
+ // else we get Paint errors!
+ // e.g. additional mode, page half visible vertically, in the
+ // middle a selection and with another cursor jump to left
+ // right border. Without ShowCursor the selection disappears.
+ bool bShowCursor = pRegion && dynamic_cast<const SwCursorShell*>(this) != nullptr;
+ if( bShowCursor )
+ static_cast<SwCursorShell*>(this)->HideCursors();
+
+ if ( pRegion )
+ {
+ SwRootFrame* pCurrentLayout = GetLayout();
+
+ pRegion->LimitToOrigin();
+ pRegion->Compress( SwRegionRects::CompressFuzzy );
+
+ ScopedVclPtr<VirtualDevice> pVout;
+ while ( !pRegion->empty() )
+ {
+ SwRect aRect( pRegion->back() );
+ pRegion->pop_back();
+
+ bool bPaint = true;
+ if ( IsEndActionByVirDev() )
+ {
+ //create virtual device and set.
+ if ( !pVout )
+ pVout = VclPtr<VirtualDevice>::Create( *GetOut() );
+ MapMode aMapMode( GetOut()->GetMapMode() );
+ pVout->SetMapMode( aMapMode );
+
+ bool bSizeOK = true;
+
+ tools::Rectangle aTmp1( aRect.SVRect() );
+ aTmp1 = GetOut()->LogicToPixel( aTmp1 );
+ tools::Rectangle aTmp2( GetOut()->PixelToLogic( aTmp1 ) );
+ if ( aTmp2.Left() > aRect.Left() )
+ aTmp1.SetLeft( std::max( tools::Long(0), aTmp1.Left() - 1 ) );
+ if ( aTmp2.Top() > aRect.Top() )
+ aTmp1.SetTop( std::max<tools::Long>( 0, aTmp1.Top() - 1 ) );
+ aTmp1.AdjustRight(1 );
+ aTmp1.AdjustBottom(1 );
+ aTmp1 = GetOut()->PixelToLogic( aTmp1 );
+ aRect = SwRect( aTmp1 );
+
+ const Size aTmp( pVout->GetOutputSize() );
+ if ( aTmp.Height() < aRect.Height() ||
+ aTmp.Width() < aRect.Width() )
+ {
+ bSizeOK = pVout->SetOutputSize( aRect.SSize() );
+ }
+ if ( bSizeOK )
+ {
+ bPaint = false;
+
+ // --> OD 2007-07-26 #i79947#
+ // #i72754# start Pre/PostPaint encapsulation before mpOut is changed to the buffering VDev
+ const vcl::Region aRepaintRegion(aRect.SVRect());
+ DLPrePaint2(aRepaintRegion);
+ // <--
+
+ OutputDevice *pOld = GetOut();
+ pVout->SetLineColor( pOld->GetLineColor() );
+ pVout->SetFillColor( pOld->GetFillColor() );
+ Point aOrigin( aRect.Pos() );
+ aOrigin.setX( -aOrigin.X() ); aOrigin.setY( -aOrigin.Y() );
+ aMapMode.SetOrigin( aOrigin );
+ pVout->SetMapMode( aMapMode );
+
+ mpOut = pVout.get();
+ if ( bPaintsFromSystem )
+ PaintDesktop(*mpOut, aRect);
+ pCurrentLayout->PaintSwFrame( *mpOut, aRect );
+ pOld->DrawOutDev( aRect.Pos(), aRect.SSize(),
+ aRect.Pos(), aRect.SSize(), *pVout );
+ mpOut = pOld;
+
+ // #i72754# end Pre/PostPaint encapsulation when mpOut is back and content is painted
+ DLPostPaint2(true);
+ }
+ }
+ if ( bPaint )
+ {
+ if (GetWin()->SupportsDoubleBuffering())
+ InvalidateWindows(aRect);
+ else
+ {
+ // #i75172# begin DrawingLayer paint
+ // need to do begin/end DrawingLayer preparation for each single rectangle of the
+ // repaint region. I already tried to prepare only once for the whole Region. This
+ // seems to work (and does technically) but fails with transparent objects. Since the
+ // region given to BeginDrawLayers() defines the clip region for DrawingLayer paint,
+ // transparent objects in the single rectangles will indeed be painted multiple times.
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ DLPrePaint2(vcl::Region(aRect.SVRect()));
+ }
+
+ if ( bPaintsFromSystem )
+ PaintDesktop(*GetOut(), aRect);
+ if (!comphelper::LibreOfficeKit::isActive())
+ pCurrentLayout->PaintSwFrame( *mpOut, aRect );
+ else
+ pCurrentLayout->GetCurrShell()->InvalidateWindows(aRect);
+
+ // #i75172# end DrawingLayer paint
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ DLPostPaint2(true);
+ }
+ }
+ }
+
+ lcl_PaintTransparentFormControls(*this, aRect); // i#107365
+ }
+ }
+ if( bShowCursor )
+ static_cast<SwCursorShell*>(this)->ShowCursors( true );
+ }
+ else
+ {
+ Imp()->DeletePaintRegion();
+ mbPaintWorks = true;
+ }
+ }
+ else
+ mbPaintWorks = true;
+
+ mbInEndAction = false;
+ SwViewShell::sbLstAct = false;
+ Imp()->EndAction();
+
+ //We artificially end the action here to enable the automatic scrollbars
+ //to adjust themselves correctly
+ //EndAction sends a Notify, and that must call Start-/EndAction to
+ //adjust the scrollbars correctly
+ --mnStartAction;
+ UISizeNotify();
+ ++mnStartAction;
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( Imp()->IsAccessible() )
+ Imp()->FireAccessibleEvents();
+#endif
+}
+
+void SwViewShell::ImplStartAction()
+{
+ mbPaintWorks = false;
+ Imp()->StartAction();
+}
+
+void SwViewShell::ImplLockPaint()
+{
+ if ( GetWin() && GetWin()->IsVisible() )
+ GetWin()->EnablePaint( false ); //Also cut off the controls.
+ Imp()->LockPaint();
+}
+
+void SwViewShell::ImplUnlockPaint( bool bVirDev )
+{
+ CurrShell aCurr( this );
+ if ( GetWin() && GetWin()->IsVisible() )
+ {
+ if ( (bInSizeNotify || bVirDev ) && VisArea().HasArea() )
+ {
+ //Refresh with virtual device to avoid flickering.
+ VclPtrInstance<VirtualDevice> pVout( *mpOut );
+ pVout->SetMapMode( mpOut->GetMapMode() );
+ Size aSize( VisArea().SSize() );
+ aSize.AdjustWidth(20 );
+ aSize.AdjustHeight(20 );
+ if( pVout->SetOutputSize( aSize ) )
+ {
+ GetWin()->EnablePaint( true );
+ GetWin()->Validate();
+
+ Imp()->UnlockPaint();
+ pVout->SetLineColor( mpOut->GetLineColor() );
+ pVout->SetFillColor( mpOut->GetFillColor() );
+
+ // #i72754# start Pre/PostPaint encapsulation before mpOut is changed to the buffering VDev
+ const vcl::Region aRepaintRegion(VisArea().SVRect());
+ DLPrePaint2(aRepaintRegion);
+
+ OutputDevice *pOld = mpOut;
+ mpOut = pVout.get();
+ Paint(*mpOut, VisArea().SVRect());
+ mpOut = pOld;
+ mpOut->DrawOutDev( VisArea().Pos(), aSize,
+ VisArea().Pos(), aSize, *pVout );
+
+ // #i72754# end Pre/PostPaint encapsulation when mpOut is back and content is painted
+ DLPostPaint2(true);
+
+ lcl_PaintTransparentFormControls(*this, VisArea()); // fdo#63949
+ }
+ else
+ {
+ Imp()->UnlockPaint();
+ GetWin()->EnablePaint( true );
+ GetWin()->Invalidate( InvalidateFlags::Children );
+ }
+ pVout.disposeAndClear();
+ }
+ else
+ {
+ Imp()->UnlockPaint();
+ GetWin()->EnablePaint( true );
+ GetWin()->Invalidate( InvalidateFlags::Children );
+ }
+ }
+ else
+ Imp()->UnlockPaint();
+}
+
+bool SwViewShell::AddPaintRect( const SwRect & rRect )
+{
+ bool bRet = false;
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if( rSh.Imp() )
+ {
+ if ( rSh.IsPreview() && rSh.GetWin() )
+ ::RepaintPagePreview( &rSh, rRect );
+ else
+ bRet |= rSh.Imp()->AddPaintRect( rRect );
+ }
+ }
+ return bRet;
+}
+
+void SwViewShell::InvalidateWindows( const SwRect &rRect )
+{
+ if ( Imp()->IsCalcLayoutProgress() )
+ return;
+
+ if(comphelper::LibreOfficeKit::isActive())
+ {
+ // If we are inside tiled painting, invalidations are ignored.
+ // Ignore them right now to save work, but also to avoid the problem
+ // that this state could be reset before FlushPendingLOKInvalidateTiles()
+ // gets called.
+ if(comphelper::LibreOfficeKit::isTiledPainting())
+ return;
+ // First collect all invalidations and perform them only later,
+ // otherwise the number of Invalidate() calls would be at least
+ // O(n^2) if not worse. The problem is that if any change in a document
+ // is made, SwEditShell::EndAllAction() is called, which calls EndAction()
+ // for every view. And every view does it own handling of paint rectangles,
+ // and then calls InvalidateWindows() based on that. On then this code
+ // would call Invalidate() for all views for each rectangle.
+ // So collect the rectangles, avoid duplicates (which there usually will
+ // be many because of the repetitions), FlushPendingLOKInvalidateTiles()
+ // will collect all rectangles from all related views, compress them
+ // and only with those relatively few rectangle it'd call Invalidate()
+ // for all views.
+ Imp()->AddPendingLOKInvalidation(rRect);
+ return;
+ }
+
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if ( rSh.GetWin() )
+ {
+ if ( rSh.IsPreview() )
+ ::RepaintPagePreview( &rSh, rRect );
+ // In case of tiled rendering, invalidation is wanted even if
+ // the rectangle is outside the visual area.
+ else if ( rSh.VisArea().Overlaps( rRect ) || comphelper::LibreOfficeKit::isActive() )
+ rSh.GetWin()->Invalidate( rRect.SVRect() );
+ }
+ }
+}
+
+void SwViewShell::FlushPendingLOKInvalidateTiles()
+{
+ assert(comphelper::LibreOfficeKit::isActive());
+ SwRegionRects rects;
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ std::vector<SwRect> tmpRects = rSh.Imp()->TakePendingLOKInvalidations();
+ rects.insert( rects.end(), tmpRects.begin(), tmpRects.end());
+ }
+ rects.Compress( SwRegionRects::CompressFuzzy );
+ if(rects.empty())
+ return;
+ // This is basically the loop from SwViewShell::InvalidateWindows().
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if ( rSh.GetWin() )
+ {
+ if ( rSh.IsPreview() )
+ {
+ for( const SwRect& rect : rects )
+ ::RepaintPagePreview( &rSh, rect );
+ }
+ else
+ {
+ for( const SwRect& rect : rects )
+ rSh.GetWin()->Invalidate( rect.SVRect() );
+ }
+ }
+ }
+}
+
+const SwRect& SwViewShell::VisArea() const
+{
+ // when using the tiled rendering, consider the entire document as our
+ // visible area
+ return comphelper::LibreOfficeKit::isActive()? GetLayout()->getFrameArea(): maVisArea;
+}
+
+void SwViewShell::MakeVisible( const SwRect &rRect )
+{
+ if ( !(!VisArea().Contains( rRect ) || IsScrollMDI( this, rRect ) || GetCareDialog(*this)) )
+ return;
+
+ if ( IsViewLocked() )
+ return;
+
+ if( mpWin )
+ {
+ const SwFrame* pRoot = GetLayout();
+ int nLoopCnt = 3;
+ tools::Long nOldH;
+ do{
+ nOldH = pRoot->getFrameArea().Height();
+ StartAction();
+ ScrollMDI( this, rRect, USHRT_MAX, USHRT_MAX );
+ EndAction();
+ } while( nOldH != pRoot->getFrameArea().Height() && nLoopCnt-- );
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ //MA: 04. Nov. 94, no one needs this, does one?
+ OSL_ENSURE( false, "Is MakeVisible still needed for printers?" );
+ }
+
+#endif
+}
+
+weld::Window* SwViewShell::CareChildWin(SwViewShell const & rVSh)
+{
+ if (!rVSh.mpSfxViewShell)
+ return nullptr;
+#if HAVE_FEATURE_DESKTOP
+ const sal_uInt16 nId = SvxSearchDialogWrapper::GetChildWindowId();
+ SfxViewFrame* pVFrame = rVSh.mpSfxViewShell->GetViewFrame();
+ SfxChildWindow* pChWin = pVFrame->GetChildWindow( nId );
+ if (!pChWin)
+ return nullptr;
+ weld::DialogController* pController = pChWin->GetController().get();
+ if (!pController)
+ return nullptr;
+ weld::Window* pWin = pController->getDialog();
+ if (pWin && pWin->get_visible())
+ return pWin;
+#endif
+ return nullptr;
+}
+
+Point SwViewShell::GetPagePos( sal_uInt16 nPageNum ) const
+{
+ return GetLayout()->GetPagePos( nPageNum );
+}
+
+sal_uInt16 SwViewShell::GetNumPages() const
+{
+ //It is possible that no layout exists when the method from
+ //root-Ctor is called.
+ return GetLayout() ? GetLayout()->GetPageNum() : 0;
+}
+
+bool SwViewShell::IsDummyPage( sal_uInt16 nPageNum ) const
+{
+ return GetLayout() && GetLayout()->IsDummyPage( nPageNum );
+}
+
+/**
+ * Forces update of each field.
+ * It notifies all fields with pNewHt. If that is 0 (default), the field
+ * type is sent (???).
+ * @param[in] bCloseDB Passed in to GetDoc()->UpdateFields. [TODO] Purpose???
+ */
+void SwViewShell::UpdateFields(bool bCloseDB)
+{
+ CurrShell aCurr( this );
+
+ auto pCursorShell = dynamic_cast<SwCursorShell*>( this );
+ if ( pCursorShell )
+ pCursorShell->StartAction();
+ else
+ StartAction();
+
+ GetDoc()->getIDocumentFieldsAccess().UpdateFields(bCloseDB);
+
+ if ( pCursorShell )
+ pCursorShell->EndAction();
+ else
+ EndAction();
+}
+
+void SwViewShell::UpdateOleObjectPreviews()
+{
+ SwDoc* pDoc = GetDoc();
+ const SwFrameFormats* const pFormats = pDoc->GetSpzFrameFormats();
+ if (pFormats->empty())
+ {
+ return;
+ }
+
+ for (size_t i = 0; i < pFormats->size(); ++i)
+ {
+ SwFrameFormat* pFormat = (*pFormats)[i];
+ if (pFormat->Which() != RES_FLYFRMFMT)
+ {
+ continue;
+ }
+
+ const SwNodeIndex* pNodeIndex = pFormat->GetContent().GetContentIdx();
+ if (!pNodeIndex || !pNodeIndex->GetNodes().IsDocNodes())
+ {
+ continue;
+ }
+
+ SwNode* pNode = pDoc->GetNodes()[pNodeIndex->GetIndex() + 1];
+ SwOLENode* pOleNode = pNode->GetOLENode();
+ if (!pOleNode)
+ {
+ continue;
+ }
+
+ SwOLEObj& rOleObj = pOleNode->GetOLEObj();
+ svt::EmbeddedObjectRef& rObject = rOleObj.GetObject();
+ rObject.UpdateReplacement( true );
+ // Trigger the repaint.
+ pOleNode->SetChanged();
+ }
+}
+
+/** update all charts for which any table exists */
+void SwViewShell::UpdateAllCharts()
+{
+ CurrShell aCurr( this );
+ // Start-/EndAction handled in the SwDoc-Method!
+ GetDoc()->UpdateAllCharts();
+}
+
+bool SwViewShell::HasCharts() const
+{
+ bool bRet = false;
+ SwNodeIndex aIdx( *GetDoc()->GetNodes().GetEndOfAutotext().
+ StartOfSectionNode(), 1 );
+ while (aIdx.GetNode().GetStartNode())
+ {
+ ++aIdx;
+ const SwOLENode *pNd = aIdx.GetNode().GetOLENode();
+ if( pNd && !pNd->GetChartTableName().isEmpty() )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ return bRet;
+}
+
+void SwViewShell::LayoutIdle()
+{
+ if( !mpOpt->IsIdle() || !GetWin() || HasDrawViewDrag() )
+ return;
+
+ //No idle when printing is going on.
+ for(const SwViewShell& rSh : GetRingContainer())
+ {
+ if ( !rSh.GetWin() )
+ return;
+ }
+
+ CurrShell aCurr( this );
+
+#ifdef DBG_UTIL
+ // If Test5 has been set, the IdleFormatter is disabled.
+ if( mpOpt->IsTest5() )
+ return;
+#endif
+
+ {
+ // Preserve top of the text frame cache.
+ SwSaveSetLRUOfst aSaveLRU;
+ // #125243# there are lots of stacktraces indicating that Imp() returns NULL
+ // this SwViewShell seems to be invalid - but it's not clear why
+ // this return is only a workaround!
+ OSL_ENSURE(Imp(), "SwViewShell already deleted?");
+ if(!Imp())
+ return;
+ SwLayIdle aIdle( GetLayout(), Imp() );
+ }
+}
+
+static void lcl_InvalidateAllContent( SwViewShell& rSh, SwInvalidateFlags nInv )
+{
+ auto pCursorShell = dynamic_cast<SwCursorShell*>( &rSh);
+ if ( pCursorShell )
+ pCursorShell->StartAction();
+ else
+ rSh.StartAction();
+ rSh.GetLayout()->InvalidateAllContent( nInv );
+ if ( pCursorShell )
+ pCursorShell->EndAction();
+ else
+ rSh.EndAction();
+
+ rSh.GetDoc()->getIDocumentState().SetModified();
+}
+
+/** local method to invalidate/re-calculate positions of floating screen
+ * objects (Writer fly frame and drawing objects), which are anchored
+ * to paragraph or to character. #i11860#
+ */
+static void lcl_InvalidateAllObjPos( SwViewShell &_rSh )
+{
+ auto pCursorShell = dynamic_cast<SwCursorShell*>( &_rSh);
+ if ( pCursorShell )
+ pCursorShell->StartAction();
+ else
+ _rSh.StartAction();
+
+ _rSh.GetLayout()->InvalidateAllObjPos();
+
+ if ( pCursorShell )
+ pCursorShell->EndAction();
+ else
+ _rSh.EndAction();
+
+ _rSh.GetDoc()->getIDocumentState().SetModified();
+}
+
+void SwViewShell::SetParaSpaceMax( bool bNew )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX) != bNew )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::PARA_SPACE_MAX, bNew );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+void SwViewShell::SetParaSpaceMaxAtPages( bool bNew )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if( rIDSA.get(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES) != bNew )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::PARA_SPACE_MAX_AT_PAGES, bNew );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+void SwViewShell::SetTabCompat( bool bNew )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if( rIDSA.get(DocumentSettingId::TAB_COMPAT) != bNew )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::TAB_COMPAT, bNew );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+void SwViewShell::SetAddExtLeading( bool bNew )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::ADD_EXT_LEADING) != bNew )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::ADD_EXT_LEADING, bNew );
+ SwDrawModel* pTmpDrawModel = getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pTmpDrawModel )
+ pTmpDrawModel->SetAddExtLeading( bNew );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+void SwViewShell::SetUseVirDev( bool bNewVirtual )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::USE_VIRTUAL_DEVICE) != bNewVirtual )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ // this sets the flag at the document and calls PrtDataChanged
+ IDocumentDeviceAccess& rIDDA = getIDocumentDeviceAccess();
+ rIDDA.setReferenceDeviceType( bNewVirtual, true );
+ }
+}
+
+/** Sets if paragraph and table spacing is added at bottom of table cells.
+ * #106629#
+ * @param[in] (bool) setting of the new value
+ */
+void SwViewShell::SetAddParaSpacingToTableCells( bool _bAddParaSpacingToTableCells )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS) != _bAddParaSpacingToTableCells
+ || rIDSA.get(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS) != _bAddParaSpacingToTableCells)
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::ADD_PARA_SPACING_TO_TABLE_CELLS, _bAddParaSpacingToTableCells );
+ // note: the dialog can't change the value to indeterminate, so only false/false and true/true
+ rIDSA.set(DocumentSettingId::ADD_PARA_LINE_SPACING_TO_TABLE_CELLS, _bAddParaSpacingToTableCells );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+/**
+ * Sets if former formatting of text lines with proportional line spacing should used.
+ * #i11859#
+ * @param[in] (bool) setting of the new value
+ */
+void SwViewShell::SetUseFormerLineSpacing( bool _bUseFormerLineSpacing )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::OLD_LINE_SPACING) != _bUseFormerLineSpacing )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::OLD_LINE_SPACING, _bUseFormerLineSpacing );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+/**
+ * Sets IDocumentSettingAccess if former object positioning should be used.
+ * #i11860#
+ * @param[in] (bool) setting the new value
+ */
+void SwViewShell::SetUseFormerObjectPositioning( bool _bUseFormerObjPos )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::USE_FORMER_OBJECT_POS) != _bUseFormerObjPos )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::USE_FORMER_OBJECT_POS, _bUseFormerObjPos );
+ lcl_InvalidateAllObjPos( *this );
+ }
+}
+
+// #i28701#
+void SwViewShell::SetConsiderWrapOnObjPos( bool _bConsiderWrapOnObjPos )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION) != _bConsiderWrapOnObjPos )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::CONSIDER_WRAP_ON_OBJECT_POSITION, _bConsiderWrapOnObjPos );
+ lcl_InvalidateAllObjPos( *this );
+ }
+}
+
+void SwViewShell::SetUseFormerTextWrapping( bool _bUseFormerTextWrapping )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::USE_FORMER_TEXT_WRAPPING) != _bUseFormerTextWrapping )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::USE_FORMER_TEXT_WRAPPING, _bUseFormerTextWrapping );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+// #i45491#
+void SwViewShell::SetDoNotJustifyLinesWithManualBreak( bool _bDoNotJustifyLinesWithManualBreak )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if ( rIDSA.get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK) != _bDoNotJustifyLinesWithManualBreak )
+ {
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+ rIDSA.set(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK, _bDoNotJustifyLinesWithManualBreak );
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent( *this, nInv );
+ }
+}
+
+void SwViewShell::SetProtectForm( bool _bProtectForm )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ rIDSA.set(DocumentSettingId::PROTECT_FORM, _bProtectForm );
+}
+
+void SwViewShell::SetMsWordCompTrailingBlanks( bool _bMsWordCompTrailingBlanks )
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS) != _bMsWordCompTrailingBlanks)
+ {
+ SwWait aWait(*GetDoc()->GetDocShell(), true);
+ rIDSA.set(DocumentSettingId::MS_WORD_COMP_TRAILING_BLANKS, _bMsWordCompTrailingBlanks);
+ const SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Size | SwInvalidateFlags::Table | SwInvalidateFlags::Section;
+ lcl_InvalidateAllContent(*this, nInv);
+ }
+}
+
+void SwViewShell::SetSubtractFlysAnchoredAtFlys(bool bSubtractFlysAnchoredAtFlys)
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ rIDSA.set(DocumentSettingId::SUBTRACT_FLYS, bSubtractFlysAnchoredAtFlys);
+}
+
+void SwViewShell::SetEmptyDbFieldHidesPara(bool bEmptyDbFieldHidesPara)
+{
+ IDocumentSettingAccess& rIDSA = getIDocumentSettingAccess();
+ if (rIDSA.get(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA) == bEmptyDbFieldHidesPara)
+ return;
+
+ SwWait aWait(*GetDoc()->GetDocShell(), true);
+ rIDSA.set(DocumentSettingId::EMPTY_DB_FIELD_HIDES_PARA, bEmptyDbFieldHidesPara);
+ StartAction();
+ GetDoc()->getIDocumentState().SetModified();
+ for (auto const & pFieldType : *GetDoc()->getIDocumentFieldsAccess().GetFieldTypes())
+ {
+ if(pFieldType->Which() == SwFieldIds::Database)
+ pFieldType->UpdateFields();
+ }
+ EndAction();
+}
+
+void SwViewShell::Reformat()
+{
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+
+ // we go for safe: get rid of the old font information,
+ // when the printer resolution or zoom factor changes.
+ // Init() and Reformat() are the safest locations.
+ pFntCache->Flush( );
+
+ if( GetLayout()->IsCallbackActionEnabled() )
+ {
+ StartAction();
+ GetLayout()->InvalidateAllContent( SwInvalidateFlags::Size | SwInvalidateFlags::Pos | SwInvalidateFlags::PrtArea );
+ EndAction();
+ }
+}
+
+void SwViewShell::ChgNumberDigits()
+{
+ SdrModel* pTmpDrawModel = getIDocumentDrawModelAccess().GetDrawModel();
+ if ( pTmpDrawModel )
+ pTmpDrawModel->ReformatAllTextObjects();
+ Reformat();
+}
+
+void SwViewShell::CalcLayout()
+{
+ // extremely likely to be a Bad Idea to call this without StartAction
+ // (except the Page Preview apparently only has a non-subclassed ViewShell)
+ assert((typeid(*this) == typeid(SwViewShell)) || mnStartAction);
+
+ CurrShell aCurr( this );
+ SwWait aWait( *GetDoc()->GetDocShell(), true );
+
+ // Preserve top of the text frame cache.
+ SwSaveSetLRUOfst aSaveLRU;
+
+ //switch on Progress when none is running yet.
+ const bool bEndProgress = SfxProgress::GetActiveProgress( GetDoc()->GetDocShell() ) == nullptr;
+ if ( bEndProgress )
+ {
+ tools::Long nEndPage = GetLayout()->GetPageNum();
+ nEndPage += nEndPage * 10 / 100;
+ ::StartProgress( STR_STATSTR_REFORMAT, 0, nEndPage, GetDoc()->GetDocShell() );
+ }
+
+ SwLayAction aAction( GetLayout(), Imp() );
+ aAction.SetPaint( false );
+ aAction.SetStatBar( true );
+ aAction.SetCalcLayout( true );
+ aAction.SetReschedule( true );
+ GetDoc()->getIDocumentFieldsAccess().LockExpFields();
+ aAction.Action(GetOut());
+ GetDoc()->getIDocumentFieldsAccess().UnlockExpFields();
+
+ //the SetNewFieldLst() on the Doc was cut off and must be fetched again
+ //(see flowfrm.cxx, txtfld.cxx)
+ if ( aAction.IsExpFields() )
+ {
+ aAction.Reset();
+ aAction.SetPaint( false );
+ aAction.SetStatBar( true );
+ aAction.SetReschedule( true );
+
+ SwDocPosUpdate aMsgHint( 0 );
+ GetDoc()->getIDocumentFieldsAccess().UpdatePageFields( &aMsgHint );
+ GetDoc()->getIDocumentFieldsAccess().UpdateExpFields(nullptr, true);
+
+ aAction.Action(GetOut());
+ }
+
+ if ( VisArea().HasArea() )
+ InvalidateWindows( VisArea() );
+ if ( bEndProgress )
+ ::EndProgress( GetDoc()->GetDocShell() );
+}
+
+void SwViewShell::SetFirstVisPageInvalid()
+{
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if ( rSh.Imp() )
+ rSh.Imp()->SetFirstVisPageInvalid();
+ }
+}
+
+void SwViewShell::SizeChgNotify()
+{
+ if ( !mpWin )
+ mbDocSizeChgd = true;
+ else if( ActionPend() || Imp()->IsCalcLayoutProgress() || mbPaintInProgress )
+ {
+ mbDocSizeChgd = true;
+
+ if ( !Imp()->IsCalcLayoutProgress() && dynamic_cast<const SwCursorShell*>( this ) != nullptr )
+ {
+ PageNumNotify(this);
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ Size aDocSize = GetDocSize();
+ std::stringstream ss;
+ ss << aDocSize.Width() + 2 * DOCUMENTBORDER << ", " << aDocSize.Height() + 2 * DOCUMENTBORDER;
+ OString sSize = ss.str().c_str();
+
+ SwXTextDocument* pModel = comphelper::getFromUnoTunnel<SwXTextDocument>(GetSfxViewShell()->GetCurrentDocument());
+ SfxLokHelper::notifyDocumentSizeChanged(GetSfxViewShell(), sSize, pModel);
+ }
+ }
+ }
+ else
+ {
+ mbDocSizeChgd = false;
+ ::SizeNotify( this, GetDocSize() );
+ }
+}
+
+void SwViewShell::VisPortChgd( const SwRect &rRect)
+{
+ OSL_ENSURE( GetWin(), "VisPortChgd without Window." );
+
+ if ( rRect == VisArea() )
+ return;
+
+ // Is someone spuriously rescheduling again?
+ SAL_WARN_IF(mbInEndAction, "sw.core", "Scrolling during EndAction");
+
+ //First get the old visible page, so we don't have to look
+ //for it afterwards.
+ const SwFrame *pOldPage = Imp()->GetFirstVisPage(GetWin()->GetOutDev());
+
+ const SwRect aPrevArea( VisArea() );
+ const bool bFull = aPrevArea.IsEmpty();
+ maVisArea = rRect;
+ SetFirstVisPageInvalid();
+
+ //When there a PaintRegion still exists and the VisArea has changed,
+ //the PaintRegion is at least by now obsolete. The PaintRegion can
+ //have been created by RootFrame::PaintSwFrame.
+ if ( !mbInEndAction &&
+ Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea() )
+ Imp()->DeletePaintRegion();
+
+ CurrShell aCurr( this );
+
+ bool bScrolled = false;
+
+ SwPostItMgr* pPostItMgr = GetPostItMgr();
+
+ if ( bFull )
+ GetWin()->Invalidate();
+ else
+ {
+ //Calculate amount to be scrolled.
+ const tools::Long nXDiff = aPrevArea.Left() - VisArea().Left();
+ const tools::Long nYDiff = aPrevArea.Top() - VisArea().Top();
+
+ if( !nXDiff && !GetViewOptions()->getBrowseMode() &&
+ (!Imp()->HasDrawView() || !Imp()->GetDrawView()->IsGridVisible() ) )
+ {
+ // If possible, don't scroll the application background
+ // (PaintDesktop). Also limit the left and right side of
+ // the scroll range to the pages.
+ const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower());
+ if ( pPage->getFrameArea().Top() > pOldPage->getFrameArea().Top() )
+ pPage = static_cast<const SwPageFrame*>(pOldPage);
+ SwRect aBoth( VisArea() );
+ aBoth.Union( aPrevArea );
+ const SwTwips nBottom = aBoth.Bottom();
+ SwTwips nMinLeft = SAL_MAX_INT32;
+ SwTwips nMaxRight= 0;
+
+ const bool bBookMode = GetViewOptions()->IsViewLayoutBookMode();
+
+ while ( pPage && pPage->getFrameArea().Top() <= nBottom )
+ {
+ SwRect aPageRect( pPage->GetBoundRect(GetWin()->GetOutDev()) );
+ if ( bBookMode )
+ {
+ const SwPageFrame& rFormatPage = pPage->GetFormatPage();
+ aPageRect.SSize( rFormatPage.GetBoundRect(GetWin()->GetOutDev()).SSize() );
+ }
+
+ // #i9719# - consider new border and shadow width
+ if ( aPageRect.Overlaps( aBoth ) )
+ {
+ SwTwips nPageLeft = 0;
+ SwTwips nPageRight = 0;
+ const sw::sidebarwindows::SidebarPosition aSidebarPos = pPage->SidebarPosition();
+
+ if( aSidebarPos != sw::sidebarwindows::SidebarPosition::NONE )
+ {
+ nPageLeft = aPageRect.Left();
+ nPageRight = aPageRect.Right();
+ }
+
+ if( nPageLeft < nMinLeft )
+ nMinLeft = nPageLeft;
+ if( nPageRight > nMaxRight )
+ nMaxRight = nPageRight;
+ //match with the draw objects
+ //take nOfst into account as the objects have been
+ //selected and have handles attached.
+ if ( pPage->GetSortedObjs() )
+ {
+ const tools::Long nOfst = GetOut()->PixelToLogic(
+ Size(Imp()->GetDrawView()->GetMarkHdlSizePixel()/2,0)).Width();
+ for (SwAnchoredObject* pObj : *pPage->GetSortedObjs())
+ {
+ // ignore objects that are not actually placed on the page
+ if (pObj->IsFormatPossible())
+ {
+ const tools::Rectangle &rBound = pObj->GetObjRect().SVRect();
+ if (rBound.Left() != FAR_AWAY) {
+ // OD 03.03.2003 #107927# - use correct datatype
+ const SwTwips nL = std::max( SwTwips(0), SwTwips(rBound.Left() - nOfst) );
+ if ( nL < nMinLeft )
+ nMinLeft = nL;
+ if( rBound.Right() + nOfst > nMaxRight )
+ nMaxRight = rBound.Right() + nOfst;
+ }
+ }
+ }
+ }
+ }
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+ tools::Rectangle aRect( aPrevArea.SVRect() );
+ aRect.SetLeft( nMinLeft );
+ aRect.SetRight( nMaxRight );
+ if( VisArea().Overlaps( aPrevArea ) && !mnLockPaint )
+ {
+ bScrolled = true;
+ maVisArea.Pos() = aPrevArea.Pos();
+ if ( SmoothScroll( nXDiff, nYDiff, &aRect ) )
+ return;
+ maVisArea.Pos() = rRect.Pos();
+ }
+ else
+ GetWin()->Invalidate( aRect );
+ }
+ else if ( !mnLockPaint ) //will be released in Unlock
+ {
+ if( VisArea().Overlaps( aPrevArea ) )
+ {
+ bScrolled = true;
+ maVisArea.Pos() = aPrevArea.Pos();
+ if ( SmoothScroll( nXDiff, nYDiff, nullptr ) )
+ return;
+ maVisArea.Pos() = rRect.Pos();
+ }
+ else
+ GetWin()->Invalidate();
+ }
+ }
+
+ // When tiled rendering, the map mode of the window is disabled, avoid
+ // enabling it here.
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ Point aPt( VisArea().Pos() );
+ aPt.setX( -aPt.X() ); aPt.setY( -aPt.Y() );
+ MapMode aMapMode( GetWin()->GetMapMode() );
+ aMapMode.SetOrigin( aPt );
+ GetWin()->SetMapMode( aMapMode );
+ }
+
+ if ( HasDrawView() )
+ {
+ Imp()->GetDrawView()->VisAreaChanged( GetWin()->GetOutDev() );
+ Imp()->GetDrawView()->SetActualWin( GetWin()->GetOutDev() );
+ }
+ GetWin()->PaintImmediately();
+
+ if ( pPostItMgr ) // #i88070#
+ {
+ pPostItMgr->Rescale();
+ pPostItMgr->CalcRects();
+ pPostItMgr->LayoutPostIts();
+ }
+
+ if ( !bScrolled && pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ pPostItMgr->CorrectPositions();
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( Imp()->IsAccessible() )
+ Imp()->UpdateAccessible();
+#endif
+}
+
+bool SwViewShell::SmoothScroll( tools::Long lXDiff, tools::Long lYDiff, const tools::Rectangle *pRect )
+{
+#if !defined(MACOSX) && !defined(ANDROID) && !defined(IOS)
+ // #i98766# - disable smooth scrolling for Mac
+
+ const sal_uLong nBitCnt = mpOut->GetBitCount();
+ tools::Long lMult = 1, lMax = LONG_MAX;
+ if ( nBitCnt == 16 )
+ {
+ lMax = 7000;
+ lMult = 2;
+ }
+ if ( nBitCnt == 24 )
+ {
+ lMax = 5000;
+ lMult = 6;
+ }
+ else if ( nBitCnt == 1 )
+ {
+ lMax = 3000;
+ lMult = 12;
+ }
+
+ // #i75172# isolated static conditions
+ const bool bOnlyYScroll(!lXDiff && std::abs(lYDiff) != 0 && std::abs(lYDiff) < lMax);
+ const bool bAllowedWithChildWindows(GetWin()->GetWindowClipRegionPixel().IsNull());
+ const bool bSmoothScrollAllowed(bOnlyYScroll && mbEnableSmooth && GetViewOptions()->IsSmoothScroll() && bAllowedWithChildWindows);
+
+ if(bSmoothScrollAllowed)
+ {
+ Imp()->m_bStopSmooth = false;
+
+ const SwRect aOldVis( VisArea() );
+
+ //create virtual device and set.
+ const Size aPixSz = GetWin()->PixelToLogic(Size(1,1));
+ VclPtrInstance<VirtualDevice> pVout( *GetWin()->GetOutDev() );
+ pVout->SetLineColor( GetWin()->GetOutDev()->GetLineColor() );
+ pVout->SetFillColor( GetWin()->GetOutDev()->GetFillColor() );
+ MapMode aMapMode( GetWin()->GetMapMode() );
+ pVout->SetMapMode( aMapMode );
+ Size aSize( maVisArea.Width()+2*aPixSz.Width(), std::abs(lYDiff)+(2*aPixSz.Height()) );
+ if ( pRect )
+ aSize.setWidth( std::min(aSize.Width(), pRect->GetWidth()+2*aPixSz.Width()) );
+ if ( pVout->SetOutputSize( aSize ) )
+ {
+ mnLockPaint++;
+
+ //First Paint everything in the virtual device.
+ SwRect aRect( VisArea() );
+ aRect.Height( aSize.Height() );
+ if ( pRect )
+ {
+ aRect.Pos().setX( std::max(aRect.Left(),pRect->Left()-aPixSz.Width()) );
+ aRect.Right( std::min(aRect.Right()+2*aPixSz.Width(), pRect->Right()+aPixSz.Width()));
+ }
+ else
+ aRect.AddWidth(2*aPixSz.Width() );
+ aRect.Pos().setY( lYDiff < 0 ? aOldVis.Bottom() - aPixSz.Height()
+ : aRect.Top() - aSize.Height() + aPixSz.Height() );
+ aRect.Pos().setX( std::max( tools::Long(0), aRect.Left()-aPixSz.Width() ) );
+ aRect.Pos() = GetWin()->PixelToLogic( GetWin()->LogicToPixel( aRect.Pos()));
+ aRect.SSize( GetWin()->PixelToLogic( GetWin()->LogicToPixel( aRect.SSize())) );
+ maVisArea = aRect;
+ const Point aPt( -aRect.Left(), -aRect.Top() );
+ aMapMode.SetOrigin( aPt );
+ pVout->SetMapMode( aMapMode );
+ OutputDevice *pOld = mpOut;
+ mpOut = pVout.get();
+
+ {
+ // #i75172# To get a clean repaint, a new ObjectContact is needed here. Without, the
+ // repaint would not be correct since it would use the wrong DrawPage visible region.
+ // This repaint IS about painting something currently outside the visible part (!).
+ // For that purpose, AddWindowToPaintView is used which creates a new SdrPageViewWindow
+ // and all the necessary stuff. It's not cheap, but necessary here. Alone because repaint
+ // target really is NOT the current window.
+ // Also will automatically NOT use PreRendering and overlay (since target is VirtualDevice)
+ if(!HasDrawView())
+ MakeDrawView();
+ SdrView* pDrawView = GetDrawView();
+ pDrawView->AddWindowToPaintView(pVout, nullptr);
+
+ // clear mpWin during DLPrePaint2 to get paint preparation for mpOut, but set it again
+ // immediately afterwards. There are many decisions in SW which imply that Printing
+ // is used when mpWin == 0 (wrong but widely used).
+ vcl::Window* pOldWin = mpWin;
+ mpWin = nullptr;
+ DLPrePaint2(vcl::Region(aRect.SVRect()));
+ mpWin = pOldWin;
+
+ // SW paint stuff
+ PaintDesktop(*GetOut(), aRect);
+ SwViewShell::sbLstAct = true;
+ GetLayout()->PaintSwFrame( *GetOut(), aRect );
+ SwViewShell::sbLstAct = false;
+
+ // end paint and destroy ObjectContact again
+ DLPostPaint2(true);
+ pDrawView->DeleteWindowFromPaintView(pVout);
+ }
+
+ mpOut = pOld;
+ maVisArea = aOldVis;
+
+ //Now shift in parts and copy the new Pixel from the virtual device.
+
+ // ??????????????????????
+ // or is it better to get the scrollfactor from the User
+ // as option?
+ // ??????????????????????
+ tools::Long lMaDelta = aPixSz.Height();
+ if ( std::abs(lYDiff) > ( maVisArea.Height() / 3 ) )
+ lMaDelta *= 6;
+ else
+ lMaDelta *= 2;
+
+ lMaDelta *= lMult;
+
+ if ( lYDiff < 0 )
+ lMaDelta = -lMaDelta;
+
+ tools::Long lDiff = lYDiff;
+ while ( lDiff )
+ {
+ tools::Long lScroll;
+ if ( Imp()->m_bStopSmooth || std::abs(lDiff) <= std::abs(lMaDelta) )
+ {
+ lScroll = lDiff;
+ lDiff = 0;
+ }
+ else
+ {
+ lScroll = lMaDelta;
+ lDiff -= lMaDelta;
+ }
+
+ const SwRect aTmpOldVis = VisArea();
+ maVisArea.Pos().AdjustY( -lScroll );
+ maVisArea.Pos() = GetWin()->PixelToLogic( GetWin()->LogicToPixel( VisArea().Pos()));
+ lScroll = aTmpOldVis.Top() - VisArea().Top();
+ if ( pRect )
+ {
+ tools::Rectangle aTmp( aTmpOldVis.SVRect() );
+ aTmp.SetLeft( pRect->Left() );
+ aTmp.SetRight( pRect->Right() );
+ GetWin()->Scroll( 0, lScroll, aTmp, ScrollFlags::Children);
+ }
+ else
+ GetWin()->Scroll( 0, lScroll, ScrollFlags::Children );
+
+ const Point aTmpPt( -VisArea().Left(), -VisArea().Top() );
+ MapMode aTmpMapMode( GetWin()->GetMapMode() );
+ aTmpMapMode.SetOrigin( aTmpPt );
+ GetWin()->SetMapMode( aTmpMapMode );
+
+ if ( Imp()->HasDrawView() )
+ Imp()->GetDrawView()->VisAreaChanged( GetWin()->GetOutDev() );
+
+ SetFirstVisPageInvalid();
+ if ( !Imp()->m_bStopSmooth )
+ {
+ const bool bScrollDirectionIsUp(lScroll > 0);
+ Imp()->m_aSmoothRect = VisArea();
+
+ if(bScrollDirectionIsUp)
+ {
+ Imp()->m_aSmoothRect.Bottom( VisArea().Top() + lScroll + aPixSz.Height());
+ }
+ else
+ {
+ Imp()->m_aSmoothRect.Top( VisArea().Bottom() + lScroll - aPixSz.Height());
+ }
+
+ Imp()->m_bSmoothUpdate = true;
+ GetWin()->PaintImmediately();
+ Imp()->m_bSmoothUpdate = false;
+
+ if(!Imp()->m_bStopSmooth)
+ {
+ // start paint on logic base
+ const tools::Rectangle aTargetLogic(Imp()->m_aSmoothRect.SVRect());
+ DLPrePaint2(vcl::Region(aTargetLogic));
+
+ // get target rectangle in discrete pixels
+ OutputDevice& rTargetDevice = mpTargetPaintWindow->GetTargetOutputDevice();
+ const tools::Rectangle aTargetPixel(rTargetDevice.LogicToPixel(aTargetLogic));
+
+ // get source top-left in discrete pixels
+ const Point aSourceTopLeft(pVout->LogicToPixel(aTargetLogic.TopLeft()));
+
+ // switch off MapModes
+ const bool bMapModeWasEnabledDest(rTargetDevice.IsMapModeEnabled());
+ const bool bMapModeWasEnabledSource(pVout->IsMapModeEnabled());
+ rTargetDevice.EnableMapMode(false);
+ pVout->EnableMapMode(false);
+
+ rTargetDevice.DrawOutDev(
+ aTargetPixel.TopLeft(), aTargetPixel.GetSize(), // dest
+ aSourceTopLeft, aTargetPixel.GetSize(), // source
+ *pVout);
+
+ // restore MapModes
+ rTargetDevice.EnableMapMode(bMapModeWasEnabledDest);
+ pVout->EnableMapMode(bMapModeWasEnabledSource);
+
+ // end paint on logoc base
+ DLPostPaint2(true);
+ }
+ else
+ --mnLockPaint;
+ }
+ }
+ pVout.disposeAndClear();
+ GetWin()->PaintImmediately();
+ if ( !Imp()->m_bStopSmooth )
+ --mnLockPaint;
+ SetFirstVisPageInvalid();
+ return true;
+ }
+ pVout.disposeAndClear();
+ }
+#endif
+
+ maVisArea.Pos().AdjustX( -lXDiff );
+ maVisArea.Pos().AdjustY( -lYDiff );
+ if ( pRect )
+ GetWin()->Scroll( lXDiff, lYDiff, *pRect, ScrollFlags::Children);
+ else
+ GetWin()->Scroll( lXDiff, lYDiff, ScrollFlags::Children);
+ return false;
+}
+
+void SwViewShell::PaintDesktop(const vcl::RenderContext& rRenderContext, const SwRect &rRect)
+{
+ if ( !GetWin() && !GetOut()->GetConnectMetaFile() )
+ return; //for the printer we don't do anything here.
+
+ if(comphelper::LibreOfficeKit::isActive())
+ return;
+
+ //Catch exceptions, so that it doesn't look so surprising.
+ //Can e.g. happen during Idle.
+ //Unfortunately we must at any rate Paint the rectangles next to the pages,
+ //as these are not painted at VisPortChgd.
+ bool bBorderOnly = false;
+ const SwRootFrame *pRoot = GetLayout();
+ if ( rRect.Top() > pRoot->getFrameArea().Bottom() )
+ {
+ const SwFrame *pPg = pRoot->Lower();
+ while ( pPg && pPg->GetNext() )
+ pPg = pPg->GetNext();
+ if ( !pPg || !pPg->getFrameArea().Overlaps( VisArea() ) )
+ bBorderOnly = true;
+ }
+
+ const bool bBookMode = GetViewOptions()->IsViewLayoutBookMode();
+
+ SwRegionRects aRegion( rRect );
+
+ //mod #i6193: remove sidebar area to avoid flickering
+ const SwPostItMgr* pPostItMgr = GetPostItMgr();
+ const SwTwips nSidebarWidth = pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() ?
+ pPostItMgr->GetSidebarWidth() + pPostItMgr->GetSidebarBorderWidth() :
+ 0;
+
+ if ( bBorderOnly )
+ {
+ const SwFrame *pPage =pRoot->Lower();
+ SwRect aLeft( rRect ), aRight( rRect );
+ while ( pPage )
+ {
+ tools::Long nTmp = pPage->getFrameArea().Left();
+ if ( nTmp < aLeft.Right() )
+ aLeft.Right( nTmp );
+ nTmp = pPage->getFrameArea().Right();
+ if ( nTmp > aRight.Left() )
+ {
+ aRight.Left( nTmp + nSidebarWidth );
+ }
+ pPage = pPage->GetNext();
+ }
+ aRegion.clear();
+ if ( aLeft.HasArea() )
+ aRegion.push_back( aLeft );
+ if ( aRight.HasArea() )
+ aRegion.push_back( aRight );
+ }
+ else
+ {
+ const SwFrame *pPage = Imp()->GetFirstVisPage(&rRenderContext);
+ const SwTwips nBottom = rRect.Bottom();
+ while ( pPage && !aRegion.empty() &&
+ (pPage->getFrameArea().Top() <= nBottom) )
+ {
+ SwRect aPageRect( pPage->getFrameArea() );
+ if ( bBookMode )
+ {
+ const SwPageFrame& rFormatPage = static_cast<const SwPageFrame*>(pPage)->GetFormatPage();
+ aPageRect.SSize( rFormatPage.getFrameArea().SSize() );
+ }
+
+ const bool bSidebarRight =
+ static_cast<const SwPageFrame*>(pPage)->SidebarPosition() == sw::sidebarwindows::SidebarPosition::RIGHT;
+ aPageRect.Pos().AdjustX( -(bSidebarRight ? 0 : nSidebarWidth) );
+ aPageRect.AddWidth(nSidebarWidth );
+
+ if ( aPageRect.Overlaps( rRect ) )
+ aRegion -= aPageRect;
+
+ pPage = pPage->GetNext();
+ }
+ }
+ if ( !aRegion.empty() )
+ PaintDesktop_(aRegion);
+}
+
+// PaintDesktop is split in two, this part is also used by PreviewPage
+void SwViewShell::PaintDesktop_(const SwRegionRects &rRegion)
+{
+ // OD 2004-04-23 #116347#
+ GetOut()->Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ GetOut()->SetLineColor();
+
+ for ( auto &rRgn : rRegion )
+ {
+ const tools::Rectangle aRectangle(rRgn.SVRect());
+
+ // #i93170#
+ // Here we have a real Problem. On the one hand we have the buffering for paint
+ // and overlay which needs an embracing pair of DLPrePaint2/DLPostPaint2 calls,
+ // on the other hand the MapMode is not set correctly when this code is executed.
+ // This is done in the users of this method, for each SWpage before painting it.
+ // Since the MapMode is not correct here, the call to DLPostPaint2 will paint
+ // existing FormControls due to the current MapMode.
+
+ // There are basically three solutions for this:
+
+ // (1) Set the MapMode correct, move the background painting to the users of
+ // this code
+
+ // (2) Do no DLPrePaint2/DLPostPaint2 here; no SdrObjects are allowed to lie in
+ // the desktop region. Disadvantage: the desktop will not be part of the
+ // buffers, e.g. overlay. Thus, as soon as overlay will be used over the
+ // desktop, it will not work.
+
+ // (3) expand DLPostPaint2 with a flag to signal if FormControl paints shall
+ // be done or not
+
+ // Currently, (3) will be the best possible solution. It will keep overlay and
+ // buffering intact and work without MapMode for single pages. In the medium
+ // to long run, (1) will need to be used and the bool bPaintFormLayer needs
+ // to be removed again
+
+ // #i68597# inform Drawinglayer about display change
+ DLPrePaint2(vcl::Region(aRectangle));
+
+ // #i75172# needed to move line/Fill color setters into loop since DLPrePaint2
+ // may exchange GetOut(), that's it's purpose. This happens e.g. at print preview.
+ GetOut()->SetFillColor( SwViewOption::GetAppBackgroundColor());
+ GetOut()->SetLineColor();
+ GetOut()->DrawRect(aRectangle);
+
+ DLPostPaint2(false);
+ }
+
+ GetOut()->Pop();
+}
+
+bool SwViewShell::CheckInvalidForPaint( const SwRect &rRect )
+{
+ if ( !GetWin() )
+ return false;
+
+ const SwPageFrame *pPage = Imp()->GetFirstVisPage(GetOut());
+ const SwTwips nBottom = VisArea().Bottom();
+ const SwTwips nRight = VisArea().Right();
+ bool bRet = false;
+ while ( !bRet && pPage && ((pPage->getFrameArea().Top() <= nBottom) &&
+ (pPage->getFrameArea().Left() <= nRight)))
+ {
+ if ( pPage->IsInvalid() || pPage->IsInvalidFly() )
+ bRet = true;
+ pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
+ }
+
+ if ( bRet )
+ {
+ //Unfortunately Start/EndAction won't help here, as the Paint originated
+ //from GUI and so Clipping has been set against getting through.
+ //Ergo: do it all yourself (see ImplEndAction())
+ if ( Imp()->HasPaintRegion() && Imp()->GetPaintRegion()->GetOrigin() != VisArea())
+ Imp()->DeletePaintRegion();
+
+ SwLayAction aAction( GetLayout(), Imp() );
+ aAction.SetComplete( false );
+ // We increment the action counter to avoid a recursive call of actions
+ // e.g. from a SwFEShell::RequestObjectResize(..) in bug 95829.
+ // A recursive call of actions is no good idea because the inner action
+ // can't format frames which are locked by the outer action. This may
+ // cause and endless loop.
+ ++mnStartAction;
+ aAction.Action(GetWin()->GetOutDev());
+ --mnStartAction;
+
+ std::unique_ptr<SwRegionRects> pRegion = Imp()->TakePaintRegion();
+ if ( pRegion && aAction.IsBrowseActionStop() )
+ {
+ //only of interest when something has changed in the visible range
+ bool bStop = true;
+ for ( size_t i = 0; i < pRegion->size(); ++i )
+ {
+ const SwRect &rTmp = (*pRegion)[i];
+ bStop = rTmp.Overlaps( VisArea() );
+ if ( !bStop )
+ break;
+ }
+ if ( bStop )
+ pRegion.reset();
+ }
+
+ if ( pRegion )
+ {
+ pRegion->LimitToOrigin();
+ pRegion->Compress( SwRegionRects::CompressFuzzy );
+ bRet = false;
+ if ( !pRegion->empty() )
+ {
+ SwRegionRects aRegion( rRect );
+ for ( size_t i = 0; i < pRegion->size(); ++i )
+ { const SwRect &rTmp = (*pRegion)[i];
+ if ( !rRect.Contains( rTmp ) )
+ {
+ InvalidateWindows( rTmp );
+ if ( rTmp.Overlaps( VisArea() ) )
+ { aRegion -= rTmp;
+ bRet = true;
+ }
+ }
+ }
+ if ( bRet )
+ {
+ for ( size_t i = 0; i < aRegion.size(); ++i )
+ GetWin()->Invalidate( aRegion[i].SVRect() );
+
+ if ( rRect != VisArea() )
+ {
+ //rRect == VisArea is the special case for new or
+ //Shift-Ctrl-R, when it shouldn't be necessary to
+ //hold the rRect again in Document coordinates.
+ if ( maInvalidRect.IsEmpty() )
+ maInvalidRect = rRect;
+ else
+ maInvalidRect.Union( rRect );
+ }
+ }
+ }
+ else
+ bRet = false;
+ }
+ else
+ bRet = false;
+ }
+ return bRet;
+}
+
+namespace
+{
+/// Similar to comphelper::FlagRestorationGuard, but for vcl::RenderContext.
+class RenderContextGuard
+{
+ std::unique_ptr<SdrPaintWindow> m_TemporaryPaintWindow;
+ SdrPageWindow* m_pPatchedPageWindow;
+ SdrPaintWindow* m_pPreviousPaintWindow = nullptr;
+
+public:
+ RenderContextGuard(VclPtr<vcl::RenderContext>& pRef, vcl::RenderContext* pValue, SwViewShell* pShell)
+ : m_pPatchedPageWindow(nullptr)
+ {
+ pRef = pValue;
+
+ if (pValue == pShell->GetWin()->GetOutDev())
+ return;
+
+ SdrView* pDrawView(pShell->Imp()->GetDrawView());
+
+ if (nullptr == pDrawView)
+ return;
+
+ SdrPageView* pSdrPageView(pDrawView->GetSdrPageView());
+
+ if (nullptr != pSdrPageView)
+ {
+ m_pPatchedPageWindow = pSdrPageView->FindPageWindow(*pShell->GetWin()->GetOutDev());
+
+ if (nullptr != m_pPatchedPageWindow)
+ {
+ m_TemporaryPaintWindow.reset(new SdrPaintWindow(*pDrawView, *pValue));
+ m_pPreviousPaintWindow = m_pPatchedPageWindow->patchPaintWindow(*m_TemporaryPaintWindow);
+ }
+ }
+ }
+
+ ~RenderContextGuard()
+ {
+ if(nullptr != m_pPatchedPageWindow)
+ {
+ m_pPatchedPageWindow->unpatchPaintWindow(m_pPreviousPaintWindow);
+ }
+ }
+};
+}
+
+void SwViewShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect)
+{
+ RenderContextGuard aGuard(mpOut, &rRenderContext, this);
+ if ( mnLockPaint )
+ {
+ if ( Imp()->m_bSmoothUpdate )
+ {
+ SwRect aTmp( rRect );
+ if ( !Imp()->m_aSmoothRect.Contains( aTmp ) )
+ Imp()->m_bStopSmooth = true;
+ else
+ {
+ Imp()->m_aSmoothRect = aTmp;
+ return;
+ }
+ }
+ else
+ return;
+ }
+
+ if ( SwRootFrame::IsInPaint() )
+ {
+ //During the publication of a page at printing the Paint is buffered.
+ SwPaintQueue::Add( this, SwRect( rRect ) );
+ return;
+ }
+
+ //With !nStartAction I try to protect me against erroneous code at other places.
+ //Hopefully it will not lead to problems!?
+ if ( mbPaintWorks && !mnStartAction )
+ {
+ if( GetWin() && GetWin()->IsVisible() )
+ {
+ SwRect aRect( rRect );
+ if ( mbPaintInProgress ) //Guard against double Paints!
+ {
+ GetWin()->Invalidate( rRect );
+ return;
+ }
+
+ mbPaintInProgress = true;
+ CurrShell aCurr( this );
+ SwRootFrame::SetNoVirDev( true );
+
+ //We don't want to Clip to and from, we trust that all are limited
+ //to the rectangle and only need to calculate the clipping once.
+ //The ClipRect is removed here once and not recovered, as externally
+ //no one needs it anymore anyway.
+ //Not when we paint a Metafile.
+ if( !GetOut()->GetConnectMetaFile() && GetOut()->IsClipRegion())
+ GetOut()->SetClipRegion();
+
+ if ( IsPreview() )
+ {
+ //When useful, process or destroy the old InvalidRect.
+ if ( aRect.Contains( maInvalidRect ) )
+ ResetInvalidRect();
+ SwViewShell::sbLstAct = true;
+ GetLayout()->PaintSwFrame( rRenderContext, aRect );
+ SwViewShell::sbLstAct = false;
+ }
+ else
+ {
+ //When one of the visible pages still has anything entered for
+ //Repaint, Repaint must be triggered.
+ if ( !CheckInvalidForPaint( aRect ) )
+ {
+ // --> OD 2009-08-12 #i101192#
+ // start Pre/PostPaint encapsulation to avoid screen blinking
+ const vcl::Region aRepaintRegion(aRect.SVRect());
+ DLPrePaint2(aRepaintRegion);
+
+ // <--
+ PaintDesktop(rRenderContext, aRect);
+
+ //When useful, process or destroy the old InvalidRect.
+ if ( aRect.Contains( maInvalidRect ) )
+ ResetInvalidRect();
+ SwViewShell::sbLstAct = true;
+ GetLayout()->PaintSwFrame( rRenderContext, aRect );
+ SwViewShell::sbLstAct = false;
+ // --> OD 2009-08-12 #i101192#
+ // end Pre/PostPaint encapsulation
+ DLPostPaint2(true);
+ // <--
+ }
+ }
+ SwRootFrame::SetNoVirDev( false );
+ mbPaintInProgress = false;
+ UISizeNotify();
+ }
+ }
+ else
+ {
+ if ( maInvalidRect.IsEmpty() )
+ maInvalidRect = SwRect( rRect );
+ else
+ maInvalidRect.Union( SwRect( rRect ) );
+
+ if ( mbInEndAction && GetWin() )
+ {
+ const vcl::Region aRegion(GetWin()->GetPaintRegion());
+ RectangleVector aRectangles;
+ aRegion.GetRegionRectangles(aRectangles);
+
+ for(const auto& rRectangle : aRectangles)
+ {
+ Imp()->AddPaintRect(SwRect(rRectangle));
+ }
+
+ //RegionHandle hHdl( aRegion.BeginEnumRects() );
+ //Rectangle aRect;
+ //while ( aRegion.GetEnumRects( hHdl, aRect ) )
+ // Imp()->AddPaintRect( aRect );
+ //aRegion.EndEnumRects( hHdl );
+ }
+ else if ( SfxProgress::GetActiveProgress( GetDoc()->GetDocShell() ) &&
+ GetOut() == GetWin()->GetOutDev() )
+ {
+ // #i68597#
+ const vcl::Region aDLRegion(rRect);
+ DLPrePaint2(aDLRegion);
+
+ rRenderContext.Push( vcl::PushFlags::FILLCOLOR|vcl::PushFlags::LINECOLOR );
+ rRenderContext.SetFillColor( Imp()->GetRetoucheColor() );
+ rRenderContext.SetLineColor();
+ rRenderContext.DrawRect( rRect );
+ rRenderContext.Pop();
+ // #i68597#
+ DLPostPaint2(true);
+ }
+ }
+}
+
+void SwViewShell::PaintTile(VirtualDevice &rDevice, int contextWidth, int contextHeight, int tilePosX, int tilePosY, tools::Long tileWidth, tools::Long tileHeight)
+{
+ // SwViewShell's output device setup
+ // TODO clean up SwViewShell's approach to output devices (the many of
+ // them - mpBufferedOut, mpOut, mpWin, ...)
+ OutputDevice *pSaveOut = mpOut;
+ comphelper::LibreOfficeKit::setTiledPainting(true);
+ mpOut = &rDevice;
+
+ // resizes the virtual device so to contain the entries context
+ rDevice.SetOutputSizePixel(Size(contextWidth, contextHeight));
+
+ // setup the output device to draw the tile
+ MapMode aMapMode(rDevice.GetMapMode());
+ aMapMode.SetMapUnit(MapUnit::MapTwip);
+ aMapMode.SetOrigin(Point(-tilePosX, -tilePosY));
+
+ // Scaling. Must convert from pixels to twips. We know
+ // that VirtualDevices use a DPI of 96.
+ const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip);
+ Fraction scaleX = Fraction(contextWidth, tileWidth) * scale;
+ Fraction scaleY = Fraction(contextHeight, tileHeight) * scale;
+ aMapMode.SetScaleX(scaleX);
+ aMapMode.SetScaleY(scaleY);
+ rDevice.SetMapMode(aMapMode);
+
+ // Update scaling of SwEditWin and its sub-widgets, needed for comments.
+ sal_uInt16 nOldZoomValue = 0;
+ if (GetWin() && GetWin()->GetMapMode().GetScaleX() != scaleX)
+ {
+ double fScale = double(scaleX);
+ SwViewOption aOption(*GetViewOptions());
+ nOldZoomValue = aOption.GetZoom();
+ aOption.SetZoom(fScale * 100);
+ ApplyViewOptions(aOption);
+ // Make sure the map mode (disabled in SwXTextDocument::initializeForTiledRendering()) is still disabled.
+ GetWin()->EnableMapMode(false);
+ }
+
+ tools::Rectangle aOutRect(Point(tilePosX, tilePosY),
+ rDevice.PixelToLogic(Size(contextWidth, contextHeight)));
+
+ // Make the requested area visible -- we can't use MakeVisible as that will
+ // only scroll the contents, but won't zoom/resize if needed.
+ // Without this, items/text that are outside the visible area (in the SwView)
+ // won't be painted when rendering tiles (at least when using either the
+ // tiledrendering app, or the gtktiledviewer) -- although ultimately we
+ // probably want to fix things so that the SwView's area doesn't affect
+ // tiled rendering?
+ VisPortChgd(SwRect(aOutRect));
+
+ // Invoke SwLayAction if layout is not yet ready.
+ CheckInvalidForPaint(SwRect(aOutRect));
+
+ // draw - works in logic coordinates
+ Paint(rDevice, aOutRect);
+
+ SwPostItMgr* pPostItMgr = GetPostItMgr();
+ if (GetViewOptions()->IsPostIts() && pPostItMgr)
+ pPostItMgr->PaintTile(rDevice);
+
+ // SwViewShell's output device tear down
+
+ // A view shell can get a PaintTile call for a tile at a zoom level
+ // different from the one, the related client really is.
+ // In such a case it is better to reset the current scale value to
+ // the original one, since such a value should be in synchronous with
+ // the zoom level in the client (see setClientZoom).
+ // At present the zoom value returned by GetViewOptions()->GetZoom() is
+ // used in SwXTextDocument methods (postMouseEvent and setGraphicSelection)
+ // for passing the correct mouse position to an edited chart (if any).
+ if (nOldZoomValue !=0)
+ {
+ SwViewOption aOption(*GetViewOptions());
+ aOption.SetZoom(nOldZoomValue);
+ ApplyViewOptions(aOption);
+
+ // Changing the zoom value doesn't always trigger the updating of
+ // the client ole object area, so we call it directly.
+ SfxInPlaceClient* pIPClient = GetSfxViewShell()->GetIPClient();
+ if (pIPClient)
+ {
+ pIPClient->VisAreaChanged();
+ }
+ // Make sure the map mode (disabled in SwXTextDocument::initializeForTiledRendering()) is still disabled.
+ GetWin()->EnableMapMode(false);
+ }
+
+ mpOut = pSaveOut;
+ comphelper::LibreOfficeKit::setTiledPainting(false);
+}
+
+void SwViewShell::SetBrowseBorder( const Size& rNew )
+{
+ if( rNew != maBrowseBorder )
+ {
+ maBrowseBorder = rNew;
+ if ( maVisArea.HasArea() )
+ InvalidateLayout( false );
+ }
+}
+
+const Size& SwViewShell::GetBrowseBorder() const
+{
+ return maBrowseBorder;
+}
+
+sal_Int32 SwViewShell::GetBrowseWidth() const
+{
+ const SwPostItMgr* pPostItMgr = GetPostItMgr();
+ if ( pPostItMgr && pPostItMgr->HasNotes() && pPostItMgr->ShowNotes() )
+ {
+ Size aBorder( maBrowseBorder );
+ aBorder.AdjustWidth(maBrowseBorder.Width() );
+ aBorder.AdjustWidth(pPostItMgr->GetSidebarWidth(true) + pPostItMgr->GetSidebarBorderWidth(true) );
+ return maVisArea.Width() - GetOut()->PixelToLogic(aBorder).Width();
+ }
+ else
+ return maVisArea.Width() - 2 * GetOut()->PixelToLogic(maBrowseBorder).Width();
+}
+
+void SwViewShell::InvalidateLayout( bool bSizeChanged )
+{
+ if ( !bSizeChanged && !GetViewOptions()->getBrowseMode() &&
+ !GetViewOptions()->IsWhitespaceHidden() )
+ return;
+
+ CurrShell aCurr( this );
+
+ OSL_ENSURE( GetLayout(), "Layout not ready" );
+
+ // When the Layout doesn't have a height yet, nothing is formatted.
+ // That leads to problems with Invalidate, e.g. when setting up a new View
+ // the content is inserted and formatted (regardless of empty VisArea).
+ // Therefore the pages must be roused for formatting.
+ if( !GetLayout()->getFrameArea().Height() )
+ {
+ SwFrame* pPage = GetLayout()->Lower();
+ while( pPage )
+ {
+ pPage->InvalidateSize_();
+ pPage = pPage->GetNext();
+ }
+ return;
+ }
+
+ LockPaint();
+ StartAction();
+
+ SwPageFrame *pPg = static_cast<SwPageFrame*>(GetLayout()->Lower());
+ do
+ { pPg->InvalidateSize();
+ pPg->InvalidatePrt_();
+ pPg->InvaPercentLowers();
+ if ( bSizeChanged )
+ {
+ pPg->PrepareHeader();
+ pPg->PrepareFooter();
+ }
+ pPg = static_cast<SwPageFrame*>(pPg->GetNext());
+ } while ( pPg );
+
+ // When the size ratios in browse mode change,
+ // the Position and PrtArea of the Content and Tab frames must be Invalidated.
+ SwInvalidateFlags nInv = SwInvalidateFlags::PrtArea | SwInvalidateFlags::Table | SwInvalidateFlags::Pos;
+ // In case of layout or mode change, the ContentFrames need a size-Invalidate
+ // because of printer/screen formatting.
+ if ( bSizeChanged )
+ nInv |= SwInvalidateFlags::Size | SwInvalidateFlags::Direction;
+
+ GetLayout()->InvalidateAllContent( nInv );
+
+ SwFrame::CheckPageDescs( static_cast<SwPageFrame*>(GetLayout()->Lower()) );
+
+ EndAction();
+ UnlockPaint();
+}
+
+SwRootFrame *SwViewShell::GetLayout() const
+{
+ return mpLayout.get();
+}
+
+vcl::RenderContext& SwViewShell::GetRefDev() const
+{
+ OutputDevice* pTmpOut = nullptr;
+ if ( GetWin() &&
+ GetViewOptions()->getBrowseMode() &&
+ !GetViewOptions()->IsPrtFormat() )
+ pTmpOut = GetWin()->GetOutDev();
+ else
+ pTmpOut = GetDoc()->getIDocumentDeviceAccess().getReferenceDevice( true );
+
+ return *pTmpOut;
+}
+
+const SwNodes& SwViewShell::GetNodes() const
+{
+ return mxDoc->GetNodes();
+}
+
+void SwViewShell::DrawSelChanged()
+{
+}
+
+Size SwViewShell::GetDocSize() const
+{
+ Size aSz;
+ const SwRootFrame* pRoot = GetLayout();
+ if( pRoot )
+ aSz = pRoot->getFrameArea().SSize();
+
+ return aSz;
+}
+
+SfxItemPool& SwViewShell::GetAttrPool()
+{
+ return GetDoc()->GetAttrPool();
+}
+
+void SwViewShell::ApplyViewOptions( const SwViewOption &rOpt )
+{
+ for(SwViewShell& rSh : GetRingContainer())
+ rSh.StartAction();
+
+ ImplApplyViewOptions( rOpt );
+
+ // With one layout per view it is no longer necessary
+ // to sync these "layout related" view options
+ // But as long as we have to disable "multiple layout"
+
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if(&rSh == this)
+ continue;
+ SwViewOption aOpt( *rSh.GetViewOptions() );
+ aOpt.SetFieldName( rOpt.IsFieldName() );
+ aOpt.SetShowHiddenField( rOpt.IsShowHiddenField() );
+ aOpt.SetShowHiddenPara( rOpt.IsShowHiddenPara() );
+ aOpt.SetShowHiddenChar( rOpt.IsShowHiddenChar() );
+ aOpt.SetViewLayoutBookMode( rOpt.IsViewLayoutBookMode() );
+ aOpt.SetHideWhitespaceMode(rOpt.IsHideWhitespaceMode());
+ aOpt.SetViewLayoutColumns(rOpt.GetViewLayoutColumns());
+ aOpt.SetPostIts(rOpt.IsPostIts());
+ if ( !(aOpt == *rSh.GetViewOptions()) )
+ rSh.ImplApplyViewOptions( aOpt );
+ }
+ // End of disabled multiple window
+
+ for(SwViewShell& rSh : GetRingContainer())
+ rSh.EndAction();
+}
+
+static bool
+IsCursorInFieldmarkHidden(SwPaM const& rCursor, sw::FieldmarkMode const eMode)
+{
+ if (eMode == sw::FieldmarkMode::ShowBoth)
+ {
+ return false;
+ }
+ IDocumentMarkAccess const& rIDMA(*rCursor.GetDoc().getIDocumentMarkAccess());
+ // iterate, for nested fieldmarks
+ for (auto iter = rIDMA.getFieldmarksBegin(); iter != rIDMA.getFieldmarksEnd(); ++iter)
+ {
+ if (*rCursor.GetPoint() <= (**iter).GetMarkStart())
+ {
+ return false;
+ }
+ if (*rCursor.GetPoint() < (**iter).GetMarkEnd())
+ {
+ SwPosition const sepPos(sw::mark::FindFieldSep(
+ dynamic_cast<sw::mark::IFieldmark&>(**iter)));
+ if (eMode == sw::FieldmarkMode::ShowResult)
+ {
+ if (*rCursor.GetPoint() <= sepPos
+ && *rCursor.GetPoint() != (**iter).GetMarkStart())
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (sepPos < *rCursor.GetPoint())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void SwViewShell::ImplApplyViewOptions( const SwViewOption &rOpt )
+{
+ if (*mpOpt == rOpt)
+ return;
+
+ vcl::Window *pMyWin = GetWin();
+ if( !pMyWin )
+ {
+ OSL_ENSURE( pMyWin, "SwViewShell::ApplyViewOptions: no window" );
+ return;
+ }
+
+ CurrShell aCurr( this );
+
+ bool bReformat = false;
+
+ if( mpOpt->IsShowHiddenField() != rOpt.IsShowHiddenField() )
+ {
+ static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))->
+ SetHiddenFlag( !rOpt.IsShowHiddenField() );
+ bReformat = true;
+ }
+ if ( mpOpt->IsShowHiddenPara() != rOpt.IsShowHiddenPara() )
+ {
+ SwHiddenParaFieldType* pFieldType = static_cast<SwHiddenParaFieldType*>(GetDoc()->
+ getIDocumentFieldsAccess().GetSysFieldType(SwFieldIds::HiddenPara));
+ if( pFieldType && pFieldType->HasWriterListeners() )
+ pFieldType->PrintHiddenPara();
+ bReformat = true;
+ }
+ if ( !bReformat && mpOpt->IsShowHiddenChar() != rOpt.IsShowHiddenChar() )
+ {
+ bReformat = GetDoc()->ContainsHiddenChars();
+ }
+ if ( mpOpt->IsShowChangesInMargin() != rOpt.IsShowChangesInMargin() ||
+ mpOpt->IsShowChangesInMargin2() != rOpt.IsShowChangesInMargin2() )
+ {
+ if (rOpt.IsShowChangesInMargin())
+ GetDoc()->GetDocumentRedlineManager().HideAll(
+ /*bDeletion=*/!rOpt.IsShowChangesInMargin2() );
+ else
+ GetDoc()->GetDocumentRedlineManager().ShowAll();
+ GetLayout()->SetHideRedlines( false );
+ }
+
+ // bReformat becomes true, if ...
+ // - fieldnames apply or not ...
+ // ( - SwEndPortion must _no_ longer be generated. )
+ // - Of course, the screen is something completely different than the printer ...
+ bool const isToggleFieldNames(mpOpt->IsFieldName() != rOpt.IsFieldName());
+
+ if (mpOpt->IsFieldName() != rOpt.IsFieldName())
+ {
+ GetLayout()->SetFieldmarkMode( rOpt.IsFieldName()
+ ? sw::FieldmarkMode::ShowCommand
+ : sw::FieldmarkMode::ShowResult );
+ bReformat = true;
+ }
+
+ // The map mode is changed, minima/maxima will be attended by UI
+ if( mpOpt->GetZoom() != rOpt.GetZoom() && !IsPreview() )
+ {
+ MapMode aMode( pMyWin->GetMapMode() );
+ Fraction aNewFactor( rOpt.GetZoom(), 100 );
+ aMode.SetScaleX( aNewFactor );
+ aMode.SetScaleY( aNewFactor );
+ pMyWin->SetMapMode( aMode );
+ // if not a reference device (printer) is used for formatting,
+ // but the screen, new formatting is needed for zoomfactor changes.
+ if (mpOpt->getBrowseMode() || mpOpt->IsWhitespaceHidden())
+ bReformat = true;
+ }
+
+ bool bBrowseModeChanged = false;
+ if( mpOpt->getBrowseMode() != rOpt.getBrowseMode() )
+ {
+ bBrowseModeChanged = true;
+ bReformat = true;
+ }
+ else if( mpOpt->getBrowseMode() && mpOpt->IsPrtFormat() != rOpt.IsPrtFormat() )
+ bReformat = true;
+
+ bool bHideWhitespaceModeChanged = false;
+ if (mpOpt->IsWhitespaceHidden() != rOpt.IsWhitespaceHidden())
+ {
+ // When whitespace is hidden, view change needs reformatting.
+ bHideWhitespaceModeChanged = true;
+ bReformat = true;
+ }
+
+ if ( HasDrawView() || rOpt.IsGridVisible() )
+ {
+ if ( !HasDrawView() )
+ MakeDrawView();
+
+ SwDrawView *pDView = Imp()->GetDrawView();
+ if ( pDView->IsDragStripes() != rOpt.IsCrossHair() )
+ pDView->SetDragStripes( rOpt.IsCrossHair() );
+
+ if ( pDView->IsGridSnap() != rOpt.IsSnap() )
+ pDView->SetGridSnap( rOpt.IsSnap() );
+
+ if ( pDView->IsGridVisible() != rOpt.IsGridVisible() )
+ pDView->SetGridVisible( rOpt.IsGridVisible() );
+
+ const Size &rSz = rOpt.GetSnapSize();
+ pDView->SetGridCoarse( rSz );
+
+ const Size aFSize
+ ( rSz.Width() ? rSz.Width() / (rOpt.GetDivisionX()+1) : 0,
+ rSz.Height()? rSz.Height()/ (rOpt.GetDivisionY()+1) : 0);
+ pDView->SetGridFine( aFSize );
+ Fraction aSnGrWdtX(rSz.Width(), rOpt.GetDivisionX() + 1);
+ Fraction aSnGrWdtY(rSz.Height(), rOpt.GetDivisionY() + 1);
+ pDView->SetSnapGridWidth( aSnGrWdtX, aSnGrWdtY );
+
+ // set handle size to 9 pixels, always
+ pDView->SetMarkHdlSizePixel(9);
+ }
+
+ bool bOnlineSpellChgd = mpOpt->IsOnlineSpell() != rOpt.IsOnlineSpell();
+
+ *mpOpt = rOpt; // First the options are taken.
+ mpOpt->SetUIOptions(rOpt);
+
+ mxDoc->GetDocumentSettingManager().set(DocumentSettingId::HTML_MODE, 0 != ::GetHtmlMode(mxDoc->GetDocShell()));
+
+ if( bBrowseModeChanged || bHideWhitespaceModeChanged )
+ {
+ // #i44963# Good occasion to check if page sizes in
+ // page descriptions are still set to (LONG_MAX, LONG_MAX) (html import)
+ mxDoc->CheckDefaultPageFormat();
+ InvalidateLayout( true );
+ }
+
+ pMyWin->Invalidate();
+ if ( bReformat )
+ {
+ // Nothing helps, we need to send all ContentFrames a
+ // Prepare, we format anew:
+ StartAction();
+ Reformat();
+ EndAction();
+ }
+
+ if (isToggleFieldNames)
+ {
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if (SwCursorShell *const pSh = dynamic_cast<SwCursorShell *>(&rSh))
+ {
+ if ((mpOpt->IsFieldName() && pSh->CursorInsideInputField())
+ || IsCursorInFieldmarkHidden(*pSh->GetCursor(),
+ pSh->GetLayout()->GetFieldmarkMode()))
+ { // move cursor out of field
+ pSh->Left(1, CRSR_SKIP_CHARS);
+ }
+ }
+ }
+ }
+
+ if( !bOnlineSpellChgd )
+ return;
+
+ bool bOnlineSpl = rOpt.IsOnlineSpell();
+ for(SwViewShell& rSh : GetRingContainer())
+ {
+ if(&rSh == this)
+ continue;
+ rSh.mpOpt->SetOnlineSpell( bOnlineSpl );
+ vcl::Window *pTmpWin = rSh.GetWin();
+ if( pTmpWin )
+ pTmpWin->Invalidate();
+ }
+
+}
+
+void SwViewShell::SetUIOptions( const SwViewOption &rOpt )
+{
+ mpOpt->SetUIOptions(rOpt);
+ //the API-Flag of the view options is set but never reset
+ //it is required to set scroll bars in readonly documents
+ if(rOpt.IsStarOneSetting())
+ mpOpt->SetStarOneSetting(true);
+
+ mpOpt->SetSymbolFont(rOpt.GetSymbolFont());
+}
+
+void SwViewShell::SetReadonlyOption(bool bSet)
+{
+ //JP 01.02.99: at readonly flag query properly
+ // and if need be format; Bug 61335
+
+ // Are we switching from readonly to edit?
+ if( bSet == mpOpt->IsReadonly() )
+ return;
+
+ // so that the flags can be queried properly.
+ mpOpt->SetReadonly( false );
+
+ bool bReformat = mpOpt->IsFieldName();
+
+ mpOpt->SetReadonly( bSet );
+
+ if( bReformat )
+ {
+ StartAction();
+ Reformat();
+ if ( GetWin() )
+ GetWin()->Invalidate();
+ EndAction();
+ }
+ else if ( GetWin() )
+ GetWin()->Invalidate();
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( Imp()->IsAccessible() )
+ Imp()->InvalidateAccessibleEditableState( false );
+#endif
+}
+
+void SwViewShell::SetPDFExportOption(bool bSet)
+{
+ if( bSet != mpOpt->IsPDFExport() )
+ {
+ if( bSet && mpOpt->getBrowseMode() )
+ mpOpt->SetPrtFormat( true );
+ mpOpt->SetPDFExport(bSet);
+ }
+}
+
+void SwViewShell::SetReadonlySelectionOption(bool bSet)
+{
+ if( bSet != mpOpt->IsSelectionInReadonly() )
+ {
+ mpOpt->SetSelectionInReadonly(bSet);
+ }
+}
+
+void SwViewShell::SetPrtFormatOption( bool bSet )
+{
+ mpOpt->SetPrtFormat( bSet );
+}
+
+void SwViewShell::UISizeNotify()
+{
+ if ( mbDocSizeChgd )
+ {
+ mbDocSizeChgd = false;
+ bool bOld = bInSizeNotify;
+ bInSizeNotify = true;
+ ::SizeNotify( this, GetDocSize() );
+ bInSizeNotify = bOld;
+ }
+}
+
+void SwViewShell::SetRestoreActions(sal_uInt16 nSet)
+{
+ OSL_ENSURE(!GetRestoreActions()||!nSet, "multiple restore of the Actions ?");
+ Imp()->SetRestoreActions(nSet);
+}
+sal_uInt16 SwViewShell::GetRestoreActions() const
+{
+ return Imp()->GetRestoreActions();
+}
+
+bool SwViewShell::IsNewLayout() const
+{
+ return GetLayout()->IsNewLayout();
+}
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+uno::Reference< css::accessibility::XAccessible > SwViewShell::CreateAccessible()
+{
+ uno::Reference< css::accessibility::XAccessible > xAcc;
+
+ // We require a layout and an XModel to be accessible.
+ OSL_ENSURE( mpLayout, "no layout, no access" );
+ OSL_ENSURE( GetWin(), "no window, no access" );
+
+ if( mxDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && GetWin() )
+ xAcc = Imp()->GetAccessibleMap().GetDocumentView();
+
+ return xAcc;
+}
+
+uno::Reference< css::accessibility::XAccessible > SwViewShell::CreateAccessiblePreview()
+{
+ OSL_ENSURE( IsPreview(),
+ "Can't create accessible preview for non-preview SwViewShell" );
+
+ // We require a layout and an XModel to be accessible.
+ OSL_ENSURE( mpLayout, "no layout, no access" );
+ OSL_ENSURE( GetWin(), "no window, no access" );
+
+ if ( IsPreview() && GetLayout()&& GetWin() )
+ {
+ return Imp()->GetAccessibleMap().GetDocumentPreview(
+ PagePreviewLayout()->maPreviewPages,
+ GetWin()->GetMapMode().GetScaleX(),
+ GetLayout()->GetPageByPageNum( PagePreviewLayout()->mnSelectedPageNum ),
+ PagePreviewLayout()->maWinSize );
+ }
+ return nullptr;
+}
+
+void SwViewShell::InvalidateAccessibleFocus()
+{
+ if( Imp() && Imp()->IsAccessible() )
+ Imp()->GetAccessibleMap().InvalidateFocus();
+}
+
+/**
+ * invalidate CONTENT_FLOWS_FROM/_TO relation for paragraphs #i27138#
+ */
+void SwViewShell::InvalidateAccessibleParaFlowRelation( const SwTextFrame* _pFromTextFrame,
+ const SwTextFrame* _pToTextFrame )
+{
+ if ( GetLayout() && GetLayout()->IsAnyShellAccessible() )
+ {
+ Imp()->InvalidateAccessibleParaFlowRelation_( _pFromTextFrame, _pToTextFrame );
+ }
+}
+
+/**
+ * invalidate text selection for paragraphs #i27301#
+ */
+void SwViewShell::InvalidateAccessibleParaTextSelection()
+{
+ if ( GetLayout() && GetLayout()->IsAnyShellAccessible() )
+ {
+ Imp()->InvalidateAccessibleParaTextSelection_();
+ }
+}
+
+/**
+ * invalidate attributes for paragraphs #i88069#
+ */
+void SwViewShell::InvalidateAccessibleParaAttrs( const SwTextFrame& rTextFrame )
+{
+ if ( GetLayout() && GetLayout()->IsAnyShellAccessible() )
+ {
+ Imp()->InvalidateAccessibleParaAttrs_( rTextFrame );
+ }
+}
+
+SwAccessibleMap* SwViewShell::GetAccessibleMap()
+{
+ if ( Imp()->IsAccessible() )
+ {
+ return &(Imp()->GetAccessibleMap());
+ }
+
+ return nullptr;
+}
+
+void SwViewShell::ApplyAccessibilityOptions(SvtAccessibilityOptions const & rAccessibilityOptions)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ if (mpOpt->IsPagePreview() && !officecfg::Office::Common::Accessibility::IsForPagePreviews::get())
+ {
+ mpAccOptions->SetAlwaysAutoColor(false);
+ mpAccOptions->SetStopAnimatedGraphics(false);
+ }
+ else
+ {
+ mpAccOptions->SetAlwaysAutoColor(rAccessibilityOptions.GetIsAutomaticFontColor());
+ mpAccOptions->SetStopAnimatedGraphics(! rAccessibilityOptions.GetIsAllowAnimatedGraphics());
+
+ // Form view
+ // Always set this option, not only if document is read-only:
+ mpOpt->SetSelectionInReadonly(rAccessibilityOptions.IsSelectionInReadonly());
+ }
+}
+#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
+
+ShellResource* SwViewShell::GetShellRes()
+{
+ return spShellRes;
+}
+
+void SwViewShell::SetCareDialog(const std::shared_ptr<weld::Window>& rNew)
+{
+ (*spCareDialog.get()) = rNew;
+}
+
+sal_uInt16 SwViewShell::GetPageCount() const
+{
+ return GetLayout() ? GetLayout()->GetPageNum() : 1;
+}
+
+Size SwViewShell::GetPageSize( sal_uInt16 nPageNum, bool bSkipEmptyPages ) const
+{
+ Size aSize;
+ const SwRootFrame* pTmpRoot = GetLayout();
+ if( pTmpRoot && nPageNum )
+ {
+ const SwPageFrame* pPage = static_cast<const SwPageFrame*>
+ (pTmpRoot->Lower());
+
+ while( --nPageNum && pPage->GetNext() )
+ pPage = static_cast<const SwPageFrame*>( pPage->GetNext() );
+
+ if( !bSkipEmptyPages && pPage->IsEmptyPage() && pPage->GetNext() )
+ pPage = static_cast<const SwPageFrame*>( pPage->GetNext() );
+
+ aSize = pPage->getFrameArea().SSize();
+ }
+ return aSize;
+}
+
+void SwViewShell::OnGraphicArrived(const SwRect& rRect)
+{
+ for(SwViewShell& rShell : GetRingContainer())
+ {
+ CurrShell aCurr(&rShell);
+ if(rShell.IsPreview())
+ {
+ if(rShell.GetWin())
+ ::RepaintPagePreview(&rShell, rRect);
+ }
+ else if(rShell.VisArea().Overlaps(rRect) && OUTDEV_WINDOW == rShell.GetOut()->GetOutDevType())
+ {
+ // invalidate instead of painting
+ rShell.GetWin()->Invalidate(rRect.SVRect());
+ }
+ }
+}
+// #i12836# enhanced pdf export
+sal_Int32 SwViewShell::GetPageNumAndSetOffsetForPDF( OutputDevice& rOut, const SwRect& rRect ) const
+{
+ OSL_ENSURE( GetLayout(), "GetPageNumAndSetOffsetForPDF assumes presence of layout" );
+
+ sal_Int32 nRet = -1;
+
+ // #i40059# Position out of bounds:
+ SwRect aRect( rRect );
+ aRect.Pos().setX( std::max( aRect.Left(), GetLayout()->getFrameArea().Left() ) );
+
+ const SwPageFrame* pPage = GetLayout()->GetPageAtPos( aRect.Center() );
+ if ( pPage )
+ {
+ OSL_ENSURE( pPage, "GetPageNumAndSetOffsetForPDF: No page found" );
+
+ Point aOffset( pPage->getFrameArea().Pos() );
+ aOffset.setX( -aOffset.X() );
+ aOffset.setY( -aOffset.Y() );
+
+ MapMode aMapMode( rOut.GetMapMode() );
+ aMapMode.SetOrigin( aOffset );
+ rOut.SetMapMode( aMapMode );
+
+ nRet = pPage->GetPhyPageNum() - 1;
+ }
+
+ return nRet;
+}
+
+// --> PB 2007-05-30 #146850#
+const BitmapEx& SwViewShell::GetReplacementBitmap( bool bIsErrorState )
+{
+ if (bIsErrorState)
+ {
+ if (!m_xErrorBmp)
+ m_xErrorBmp.reset(new BitmapEx(RID_GRAPHIC_ERRORBMP));
+ return *m_xErrorBmp;
+ }
+
+ if (!m_xReplaceBmp)
+ m_xReplaceBmp.reset(new BitmapEx(RID_GRAPHIC_REPLACEBMP));
+ return *m_xReplaceBmp;
+}
+
+void SwViewShell::DeleteReplacementBitmaps()
+{
+ m_xErrorBmp.reset();
+ m_xReplaceBmp.reset();
+}
+
+SwPostItMgr* SwViewShell::GetPostItMgr()
+{
+ SwView* pView = GetDoc()->GetDocShell() ? GetDoc()->GetDocShell()->GetView() : nullptr;
+ if ( pView )
+ return pView->GetPostItMgr();
+
+ return nullptr;
+}
+
+/*
+ * Document Interface Access
+ */
+const IDocumentSettingAccess& SwViewShell::getIDocumentSettingAccess() const { return mxDoc->GetDocumentSettingManager(); }
+IDocumentSettingAccess& SwViewShell::getIDocumentSettingAccess() { return mxDoc->GetDocumentSettingManager(); }
+const IDocumentDeviceAccess& SwViewShell::getIDocumentDeviceAccess() const { return mxDoc->getIDocumentDeviceAccess(); }
+IDocumentDeviceAccess& SwViewShell::getIDocumentDeviceAccess() { return mxDoc->getIDocumentDeviceAccess(); }
+const IDocumentMarkAccess* SwViewShell::getIDocumentMarkAccess() const { return mxDoc->getIDocumentMarkAccess(); }
+IDocumentMarkAccess* SwViewShell::getIDocumentMarkAccess() { return mxDoc->getIDocumentMarkAccess(); }
+const IDocumentDrawModelAccess& SwViewShell::getIDocumentDrawModelAccess() const { return mxDoc->getIDocumentDrawModelAccess(); }
+IDocumentDrawModelAccess& SwViewShell::getIDocumentDrawModelAccess() { return mxDoc->getIDocumentDrawModelAccess(); }
+const IDocumentRedlineAccess& SwViewShell::getIDocumentRedlineAccess() const { return mxDoc->getIDocumentRedlineAccess(); }
+IDocumentRedlineAccess& SwViewShell::getIDocumentRedlineAccess() { return mxDoc->getIDocumentRedlineAccess(); }
+const IDocumentLayoutAccess& SwViewShell::getIDocumentLayoutAccess() const { return mxDoc->getIDocumentLayoutAccess(); }
+IDocumentLayoutAccess& SwViewShell::getIDocumentLayoutAccess() { return mxDoc->getIDocumentLayoutAccess(); }
+IDocumentContentOperations& SwViewShell::getIDocumentContentOperations() { return mxDoc->getIDocumentContentOperations(); }
+IDocumentStylePoolAccess& SwViewShell::getIDocumentStylePoolAccess() { return mxDoc->getIDocumentStylePoolAccess(); }
+const IDocumentStatistics& SwViewShell::getIDocumentStatistics() const { return mxDoc->getIDocumentStatistics(); }
+
+IDocumentUndoRedo & SwViewShell::GetIDocumentUndoRedo()
+{ return mxDoc->GetIDocumentUndoRedo(); }
+IDocumentUndoRedo const& SwViewShell::GetIDocumentUndoRedo() const
+{ return mxDoc->GetIDocumentUndoRedo(); }
+
+// --> OD 2007-11-14 #i83479#
+const IDocumentListItems* SwViewShell::getIDocumentListItemsAccess() const
+{
+ return &mxDoc->getIDocumentListItems();
+}
+
+const IDocumentOutlineNodes* SwViewShell::getIDocumentOutlineNodesAccess() const
+{
+ return &mxDoc->getIDocumentOutlineNodes();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/vnew.cxx b/sw/source/core/view/vnew.cxx
new file mode 100644
index 000000000..b841134cc
--- /dev/null
+++ b/sw/source/core/view/vnew.cxx
@@ -0,0 +1,385 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+#include <sfx2/printer.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <doc.hxx>
+#include <IDocumentDrawModelAccess.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentSettingManager.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentState.hxx>
+#include <docsh.hxx>
+#include <viewsh.hxx>
+#include <rootfrm.hxx>
+#include <viewimp.hxx>
+#include <viewopt.hxx>
+#include <txtfrm.hxx>
+#include <notxtfrm.hxx>
+#include <fntcache.hxx>
+#include <docufld.hxx>
+#include <ptqueue.hxx>
+#include <dview.hxx>
+#include <ndgrf.hxx>
+#include <ndindex.hxx>
+#include <accessibilityoptions.hxx>
+
+void SwViewShell::Init( const SwViewOption *pNewOpt )
+{
+ mbDocSizeChgd = false;
+
+ // We play it safe: Remove old font information whenever the printer
+ // resolution or the zoom factor changes. For that, Init() and Reformat()
+ // are the most secure places.
+ pFntCache->Flush( );
+
+ // ViewOptions are created dynamically
+
+ if( !mpOpt )
+ {
+ mpOpt.reset(new SwViewOption);
+
+ // ApplyViewOptions() does not need to be called
+ if( pNewOpt )
+ {
+ *mpOpt = *pNewOpt;
+ // Zoom factor needs to be set because there is no call to ApplyViewOptions() during
+ // CTOR for performance reasons.
+ if( GetWin() && 100 != mpOpt->GetZoom() )
+ {
+ MapMode aMode( mpWin->GetMapMode() );
+ const Fraction aNewFactor( mpOpt->GetZoom(), 100 );
+ aMode.SetScaleX( aNewFactor );
+ aMode.SetScaleY( aNewFactor );
+ mpWin->SetMapMode( aMode );
+ }
+ }
+ }
+
+ SwDocShell* pDShell = mxDoc->GetDocShell();
+ mxDoc->GetDocumentSettingManager().set(DocumentSettingId::HTML_MODE, 0 != ::GetHtmlMode( pDShell ) );
+ // set readonly flag at ViewOptions before creating layout. Otherwise,
+ // one would have to reformat again.
+
+ if( pDShell && pDShell->IsReadOnly() )
+ mpOpt->SetReadonly( true );
+
+ SAL_INFO( "sw.core", "View::Init - before InitPrt" );
+ OutputDevice* pPDFOut = nullptr;
+
+ if (mpOut && (OUTDEV_PDF == mpOut->GetOutDevType()))
+ pPDFOut = mpOut;
+
+ // Only setup the printer if we need one:
+ const bool bBrowseMode = mpOpt->getBrowseMode();
+ if( pPDFOut )
+ InitPrt( pPDFOut );
+
+ // i#44963 Good occasion to check if page sizes in
+ // page descriptions are still set to (LONG_MAX, LONG_MAX) (html import)
+ if ( !bBrowseMode )
+ {
+ mxDoc->CheckDefaultPageFormat();
+ }
+
+ SAL_INFO( "sw.core", "View::Init - after InitPrt" );
+ if( GetWin() )
+ {
+ SwViewOption::Init( GetWin()->GetOutDev() );
+ GetWin()->GetOutDev()->SetFillColor();
+ GetWin()->SetBackground();
+ GetWin()->GetOutDev()->SetLineColor();
+ }
+
+ // Create a new layout, if there is no one available
+ if( !mpLayout )
+ {
+ // Here's the code which disables the usage of "multiple" layouts at the moment
+ // If the problems with controls and groups objects are solved,
+ // this code can be removed...
+ SwViewShell *pCurrShell = GetDoc()->getIDocumentLayoutAccess().GetCurrentViewShell();
+ if( pCurrShell )
+ mpLayout = pCurrShell->mpLayout;
+ // end of "disable multiple layouts"
+ if( !mpLayout )
+ {
+ // switched to two step construction because creating the layout in SwRootFrame needs a valid pLayout set
+ mpLayout = SwRootFramePtr(new SwRootFrame(mxDoc->GetDfltFrameFormat(), this),
+ &SwFrame::DestroyFrame);
+ mpLayout->Init( mxDoc->GetDfltFrameFormat() );
+ }
+ }
+ SizeChgNotify();
+
+ // XForms mode: initialize XForms mode, based on design mode (draw view)
+ // MakeDrawView() requires layout
+ if( GetDoc()->isXForms() )
+ {
+ if( ! HasDrawView() )
+ MakeDrawView();
+ mpOpt->SetFormView( ! GetDrawView()->IsDesignMode() );
+ }
+}
+
+/// CTor for the first Shell.
+SwViewShell::SwViewShell( SwDoc& rDocument, vcl::Window *pWindow,
+ const SwViewOption *pNewOpt, OutputDevice *pOutput,
+ tools::Long nFlags )
+ :
+ mpSfxViewShell( nullptr ),
+ mpImp( new SwViewShellImp( this ) ),
+ mpWin( pWindow ),
+ mpOut( pOutput ? pOutput
+ : pWindow ? pWindow->GetOutDev()
+ : static_cast<OutputDevice*>(rDocument.getIDocumentDeviceAccess().getPrinter( true ))),
+ mpAccOptions( new SwAccessibilityOptions ),
+ mbShowHeaderSeparator( false ),
+ mbShowFooterSeparator( false ),
+ mbHeaderFooterEdit( false ),
+ mpTargetPaintWindow(nullptr),
+ mpBufferedOut(nullptr),
+ mxDoc( &rDocument ),
+ mnStartAction( 0 ),
+ mnLockPaint( 0 ),
+ mbSelectAll(false),
+ mbOutputToWindow(false),
+ mpPrePostOutDev(nullptr)
+{
+ // in order to suppress event handling in
+ // <SwDrawContact::Changed> during construction of <SwViewShell> instance
+ mbInConstructor = true;
+
+ mbPaintInProgress = mbViewLocked = mbInEndAction = mbFrameView =
+ mbEndActionByVirDev = false;
+ mbPaintWorks = mbEnableSmooth = true;
+ mbPreview = 0 !=( VSHELLFLAG_ISPREVIEW & nFlags );
+
+ // i#38810 Do not reset modified state of document,
+ // if it's already been modified.
+ const bool bIsDocModified( mxDoc->getIDocumentState().IsModified() );
+ OutputDevice* pOrigOut = mpOut;
+ Init( pNewOpt ); // may change the Outdev (InitPrt())
+ mpOut = pOrigOut;
+
+ // initialize print preview layout after layout
+ // is created in <SwViewShell::Init(..)> - called above.
+ if ( mbPreview )
+ {
+ // init page preview layout
+ mpImp->InitPagePreviewLayout();
+ }
+
+ CurrShell aCurr( this );
+
+ static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))->
+ SetHiddenFlag( !mpOpt->IsShowHiddenField() );
+
+ // In Init a standard FrameFormat is created.
+ if ( !mxDoc->GetIDocumentUndoRedo().IsUndoNoResetModified()
+ && !bIsDocModified )
+ {
+ mxDoc->getIDocumentState().ResetModified();
+ }
+
+ // extend format cache.
+ if ( SwTextFrame::GetTextCache()->GetCurMax() < 2550 )
+ SwTextFrame::GetTextCache()->IncreaseMax( 100 );
+ if( mpOpt->IsGridVisible() || getIDocumentDrawModelAccess().GetDrawModel() )
+ Imp()->MakeDrawView();
+
+ mbInConstructor = false;
+}
+
+/// CTor for further Shells on a document.
+SwViewShell::SwViewShell( SwViewShell& rShell, vcl::Window *pWindow,
+ OutputDevice * pOutput, tools::Long const nFlags)
+ : Ring( &rShell ) ,
+ maBrowseBorder( rShell.maBrowseBorder ),
+ mpSfxViewShell( nullptr ),
+ mpImp( new SwViewShellImp( this ) ),
+ mpWin( pWindow ),
+ mpOut( pOutput ? pOutput
+ : pWindow ? pWindow->GetOutDev()
+ : static_cast<OutputDevice*>(rShell.GetDoc()->getIDocumentDeviceAccess().getPrinter( true ))),
+ mpAccOptions( new SwAccessibilityOptions ),
+ mbShowHeaderSeparator( false ),
+ mbShowFooterSeparator( false ),
+ mbHeaderFooterEdit( false ),
+ mpTargetPaintWindow(nullptr),
+ mpBufferedOut(nullptr),
+ mxDoc( rShell.GetDoc() ),
+ mnStartAction( 0 ),
+ mnLockPaint( 0 ),
+ mbSelectAll(false),
+ mbOutputToWindow(false),
+ mpPrePostOutDev(nullptr)
+{
+ // in order to suppress event handling in
+ // <SwDrawContact::Changed> during construction of <SwViewShell> instance
+ mbInConstructor = true;
+
+ mbPaintWorks = mbEnableSmooth = true;
+ mbPaintInProgress = mbViewLocked = mbInEndAction = mbFrameView =
+ mbEndActionByVirDev = false;
+ mbPreview = 0 !=( VSHELLFLAG_ISPREVIEW & nFlags );
+
+ if( nFlags & VSHELLFLAG_SHARELAYOUT )
+ mpLayout = rShell.mpLayout;
+
+ CurrShell aCurr( this );
+
+ bool bModified = mxDoc->getIDocumentState().IsModified();
+
+ OutputDevice* pOrigOut = mpOut;
+ Init( rShell.GetViewOptions() ); // might change Outdev (InitPrt())
+ mpOut = pOrigOut;
+
+ if ( mbPreview )
+ mpImp->InitPagePreviewLayout();
+
+ static_cast<SwHiddenTextFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::HiddenText ))->
+ SetHiddenFlag( !mpOpt->IsShowHiddenField() );
+
+ // In Init a standard FrameFormat is created.
+ if( !bModified && !mxDoc->GetIDocumentUndoRedo().IsUndoNoResetModified() )
+ {
+ mxDoc->getIDocumentState().ResetModified();
+ }
+
+ // extend format cache.
+ if ( SwTextFrame::GetTextCache()->GetCurMax() < 2550 )
+ SwTextFrame::GetTextCache()->IncreaseMax( 100 );
+ if( mpOpt->IsGridVisible() || getIDocumentDrawModelAccess().GetDrawModel() )
+ Imp()->MakeDrawView();
+
+ mbInConstructor = false;
+
+}
+
+SwViewShell::~SwViewShell()
+{
+ IDocumentLayoutAccess* const pLayoutAccess
+ = mxDoc ? &mxDoc->getIDocumentLayoutAccess() : nullptr;
+
+ {
+ CurrShell aCurr( this );
+ mbPaintWorks = false;
+
+ // i#9684 Stopping the animated graphics is not
+ // necessary during printing or pdf export, because the animation
+ // has not been started in this case.
+ if( mxDoc && GetWin() )
+ {
+ SwNodes& rNds = mxDoc->GetNodes();
+
+ SwStartNode *pStNd;
+ SwNodeIndex aIdx( *rNds.GetEndOfAutotext().StartOfSectionNode(), 1 );
+ while ( nullptr != (pStNd = aIdx.GetNode().GetStartNode()) )
+ {
+ ++aIdx;
+ SwGrfNode *pGNd = aIdx.GetNode().GetGrfNode();
+ if ( nullptr != pGNd )
+ {
+ if( pGNd->IsAnimated() )
+ {
+ SwIterator<SwFrame,SwGrfNode> aIter( *pGNd );
+ for( SwFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next() )
+ {
+ OSL_ENSURE( pFrame->IsNoTextFrame(), "GraphicNode with Text?" );
+ static_cast<SwNoTextFrame*>(pFrame)->StopAnimation( mpOut );
+ }
+ }
+ }
+ aIdx.Assign( *pStNd->EndOfSectionNode(), +1 );
+ }
+
+ GetDoc()->StopNumRuleAnimations( mpOut );
+ }
+
+ mpImp.reset();
+
+ if (mxDoc)
+ {
+ if( mxDoc->getReferenceCount() > 1 )
+ GetLayout()->ResetNewLayout();
+ }
+
+ mpOpt.reset();
+
+ // resize format cache.
+ if ( SwTextFrame::GetTextCache()->GetCurMax() > 250 )
+ SwTextFrame::GetTextCache()->DecreaseMax( 100 );
+
+ // Remove from PaintQueue if necessary
+ SwPaintQueue::Remove( this );
+
+ OSL_ENSURE( !mnStartAction, "EndAction() pending." );
+ }
+
+ if ( pLayoutAccess )
+ {
+ GetLayout()->DeRegisterShell( this );
+ if(pLayoutAccess->GetCurrentViewShell()==this)
+ {
+ pLayoutAccess->SetCurrentViewShell(nullptr);
+ for(SwViewShell& rShell : GetRingContainer())
+ {
+ if(&rShell != this)
+ {
+ pLayoutAccess->SetCurrentViewShell(&rShell);
+ break;
+ }
+ }
+ }
+ }
+
+ mpAccOptions.reset();
+}
+
+bool SwViewShell::HasDrawView() const
+{
+ return Imp() && Imp()->HasDrawView();
+}
+
+void SwViewShell::MakeDrawView()
+{
+ Imp()->MakeDrawView( );
+}
+
+bool SwViewShell::HasDrawViewDrag() const
+{
+ return Imp()->HasDrawView() && Imp()->GetDrawView()->IsDragObj();
+}
+
+SdrView* SwViewShell::GetDrawView()
+{
+ return Imp()->GetDrawView();
+}
+
+SdrView* SwViewShell::GetDrawViewWithValidMarkList()
+{
+ SwDrawView* pDView = Imp()->GetDrawView();
+ pDView->ValidateMarkList();
+ return pDView;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/vprint.cxx b/sw/source/core/view/vprint.cxx
new file mode 100644
index 000000000..a1d6415cd
--- /dev/null
+++ b/sw/source/core/view/vprint.cxx
@@ -0,0 +1,690 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+
+
+#include <hintids.hxx>
+#include <sfx2/printer.hxx>
+#include <svx/svdview.hxx>
+#include <osl/diagnose.h>
+#include <tools/UnitConversion.hxx>
+
+#include <txtfld.hxx>
+#include <fmtfld.hxx>
+#include <fmtfsize.hxx>
+#include <rootfrm.hxx>
+#include <pagefrm.hxx>
+#include <cntfrm.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <IDocumentDeviceAccess.hxx>
+#include <IDocumentFieldsAccess.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <fesh.hxx>
+#include <pam.hxx>
+#include <viewimp.hxx>
+#include <layact.hxx>
+#include <ndtxt.hxx>
+#include <viewopt.hxx>
+#include <printdata.hxx>
+#include <pagedesc.hxx>
+#include <ptqueue.hxx>
+#include <viscrs.hxx>
+#include <fmtpdsc.hxx>
+#include <PostItMgr.hxx>
+#include "vprint.hxx"
+
+using namespace ::com::sun::star;
+
+/// Painting buffer
+class SwQueuedPaint
+{
+public:
+ SwQueuedPaint *pNext;
+ SwViewShell *pSh;
+ SwRect aRect;
+
+ SwQueuedPaint( SwViewShell *pNew, const SwRect &rRect ) :
+ pNext( nullptr ),
+ pSh( pNew ),
+ aRect( rRect )
+ {}
+};
+
+SwQueuedPaint *SwPaintQueue::s_pPaintQueue = nullptr;
+
+namespace {
+
+// saves some settings from the draw view
+class SwDrawViewSave
+{
+ SdrView* pDV;
+ bool bPrintControls;
+public:
+ explicit SwDrawViewSave( SdrView* pSdrView );
+ ~SwDrawViewSave();
+};
+
+}
+
+void SwPaintQueue::Add( SwViewShell *pNew, const SwRect &rNew )
+{
+ SwQueuedPaint *pPt = s_pPaintQueue;
+ if (nullptr != pPt)
+ {
+ while ( pPt->pSh != pNew && pPt->pNext )
+ pPt = pPt->pNext;
+ if ( pPt->pSh == pNew )
+ {
+ pPt->aRect.Union( rNew );
+ return;
+ }
+ }
+ SwQueuedPaint *pNQ = new SwQueuedPaint( pNew, rNew );
+ if ( pPt )
+ pPt->pNext = pNQ;
+ else
+ s_pPaintQueue = pNQ;
+}
+
+void SwPaintQueue::Repaint()
+{
+ if (SwRootFrame::IsInPaint() || !s_pPaintQueue)
+ return;
+
+ SwQueuedPaint *pPt = s_pPaintQueue;
+ do
+ { SwViewShell *pSh = pPt->pSh;
+ CurrShell aCurr( pSh );
+ if ( pSh->IsPreview() )
+ {
+ if ( pSh->GetWin() )
+ {
+ // for previewing, since rows/columns are known in PaintHdl (UI)
+ pSh->GetWin()->Invalidate();
+ }
+ }
+ else
+ pSh->Paint(*pSh->GetOut(), pPt->aRect.SVRect());
+ pPt = pPt->pNext;
+ } while ( pPt );
+
+ do
+ {
+ pPt = s_pPaintQueue;
+ s_pPaintQueue = s_pPaintQueue->pNext;
+ delete pPt;
+ } while (s_pPaintQueue);
+}
+
+void SwPaintQueue::Remove( SwViewShell const *pSh )
+{
+ SwQueuedPaint *pPt = s_pPaintQueue;
+ if (nullptr == pPt)
+ return;
+
+ SwQueuedPaint *pPrev = nullptr;
+ while ( pPt && pPt->pSh != pSh )
+ {
+ pPrev = pPt;
+ pPt = pPt->pNext;
+ }
+ if ( pPt )
+ {
+ if ( pPrev )
+ pPrev->pNext = pPt->pNext;
+ else if (pPt == s_pPaintQueue)
+ s_pPaintQueue = nullptr;
+ delete pPt;
+ }
+}
+
+void SetSwVisArea( SwViewShell *pSh, const SwRect &rRect )
+{
+ OSL_ENSURE( !pSh->GetWin(), "Print with window?" );
+ pSh->maVisArea = rRect;
+ pSh->Imp()->SetFirstVisPageInvalid();
+ Point aPt( rRect.Pos() );
+
+ // calculate an offset for the rectangle of the n-th page to
+ // move the start point of the output operation to a position
+ // such that in the output device all pages will be painted
+ // at the same position
+ aPt.setX( -aPt.X() ); aPt.setY( -aPt.Y() );
+
+ vcl::RenderContext *pOut = pSh->GetOut();
+
+ MapMode aMapMode( pOut->GetMapMode() );
+ aMapMode.SetOrigin( aPt );
+ pOut->SetMapMode( aMapMode );
+}
+
+void SwViewShell::InitPrt( OutputDevice *pOutDev )
+{
+ // For printing we use a negative offset (exactly the offset of OutputSize).
+ // This is necessary because the origin is in the upper left corner of the
+ // physical page while the output uses OutputOffset as origin.
+ if ( pOutDev )
+ {
+ maPrtOffset = Point();
+
+ maPrtOffset += pOutDev->GetMapMode().GetOrigin();
+ MapMode aMapMode( pOutDev->GetMapMode() );
+ aMapMode.SetMapUnit( MapUnit::MapTwip );
+ pOutDev->SetMapMode( aMapMode );
+ pOutDev->SetLineColor();
+ pOutDev->SetFillColor();
+ }
+ else
+ {
+ maPrtOffset.setX(0);
+ maPrtOffset.setY(0);
+ }
+
+ if ( !mpWin )
+ mpOut = pOutDev;
+}
+
+void SwViewShell::ChgAllPageOrientation( Orientation eOri )
+{
+ OSL_ENSURE( mnStartAction, "missing an Action" );
+ CurrShell aCurr( this );
+
+ const size_t nAll = GetDoc()->GetPageDescCnt();
+ bool bNewOri = eOri != Orientation::Portrait;
+
+ for( size_t i = 0; i < nAll; ++ i )
+ {
+ const SwPageDesc& rOld = GetDoc()->GetPageDesc( i );
+
+ if( rOld.GetLandscape() != bNewOri )
+ {
+ SwPageDesc aNew( rOld );
+ {
+ ::sw::UndoGuard const ug(GetDoc()->GetIDocumentUndoRedo());
+ GetDoc()->CopyPageDesc(rOld, aNew);
+ }
+ aNew.SetLandscape( bNewOri );
+ SwFrameFormat& rFormat = aNew.GetMaster();
+ SwFormatFrameSize aSz( rFormat.GetFrameSize() );
+ // adjust size
+ // PORTRAIT -> higher than wide
+ // LANDSCAPE -> wider than high
+ // Height is the VarSize, width the FixSize (per Def.)
+ if( bNewOri ? aSz.GetHeight() > aSz.GetWidth()
+ : aSz.GetHeight() < aSz.GetWidth() )
+ {
+ SwTwips aTmp = aSz.GetHeight();
+ aSz.SetHeight( aSz.GetWidth() );
+ aSz.SetWidth( aTmp );
+ rFormat.SetFormatAttr( aSz );
+ }
+ GetDoc()->ChgPageDesc( i, aNew );
+ }
+ }
+}
+
+void SwViewShell::ChgAllPageSize( Size const &rSz )
+{
+ OSL_ENSURE( mnStartAction, "missing an Action" );
+ CurrShell aCurr( this );
+
+ SwDoc* pMyDoc = GetDoc();
+ const size_t nAll = pMyDoc->GetPageDescCnt();
+
+ for( size_t i = 0; i < nAll; ++i )
+ {
+ const SwPageDesc &rOld = pMyDoc->GetPageDesc( i );
+ SwPageDesc aNew( rOld );
+ {
+ ::sw::UndoGuard const ug(GetDoc()->GetIDocumentUndoRedo());
+ GetDoc()->CopyPageDesc( rOld, aNew );
+ }
+ SwFrameFormat& rPgFormat = aNew.GetMaster();
+ Size aSz( rSz );
+ const bool bOri = aNew.GetLandscape();
+ if( bOri ? aSz.Height() > aSz.Width()
+ : aSz.Height() < aSz.Width() )
+ {
+ SwTwips aTmp = aSz.Height();
+ aSz.setHeight( aSz.Width() );
+ aSz.setWidth( aTmp );
+ }
+
+ SwFormatFrameSize aFrameSz( rPgFormat.GetFrameSize() );
+ aFrameSz.SetSize( aSz );
+ rPgFormat.SetFormatAttr( aFrameSz );
+ pMyDoc->ChgPageDesc( i, aNew );
+ }
+}
+
+void SwViewShell::CalcPagesForPrint( sal_uInt16 nMax )
+{
+ CurrShell aCurr( this );
+
+ SwRootFrame* pMyLayout = GetLayout();
+
+ const SwFrame *pPage = pMyLayout->Lower();
+ SwLayAction aAction( pMyLayout, Imp() );
+
+ pMyLayout->StartAllAction();
+ for ( sal_uInt16 i = 1; pPage && i <= nMax; pPage = pPage->GetNext(), ++i )
+ {
+ pPage->Calc(GetOut());
+ SwRect aOldVis( VisArea() );
+ maVisArea = pPage->getFrameArea();
+ Imp()->SetFirstVisPageInvalid();
+ aAction.Reset();
+ aAction.SetPaint( false );
+ aAction.SetWaitAllowed( false );
+ aAction.SetReschedule( true );
+
+ aAction.Action(GetOut());
+
+ maVisArea = aOldVis; //reset due to the paints
+ Imp()->SetFirstVisPageInvalid();
+ }
+
+ pMyLayout->EndAllAction();
+}
+
+void SwViewShell::FillPrtDoc( SwDoc& rPrtDoc, const SfxPrinter* pPrt)
+{
+ assert( dynamic_cast<const SwFEShell*>( this) && "SwViewShell::Prt for FEShell only");
+ SwFEShell* pFESh = static_cast<SwFEShell*>(this);
+ rPrtDoc.getIDocumentFieldsAccess().LockExpFields();
+
+ // use given printer
+ //! Make a copy of it since it gets destroyed with the temporary document
+ //! used for PDF export
+ if (pPrt)
+ rPrtDoc.getIDocumentDeviceAccess().setPrinter( VclPtr<SfxPrinter>::Create(*pPrt), true, true );
+
+ const SfxItemPool& rPool = GetAttrPool();
+ for( sal_uInt16 nWh = POOLATTR_BEGIN; nWh < POOLATTR_END; ++nWh )
+ {
+ const SfxPoolItem* pCpyItem = rPool.GetPoolDefaultItem( nWh );
+ if( nullptr != pCpyItem )
+ rPrtDoc.GetAttrPool().SetPoolDefaultItem( *pCpyItem );
+ }
+
+ // JP 29.07.99 - Bug 67951 - set all Styles from the SourceDoc into
+ // the PrintDoc - will be replaced!
+ rPrtDoc.ReplaceStyles( *GetDoc() );
+
+ SwShellCursor *pActCursor = pFESh->GetCursor_();
+ SwShellCursor *pFirstCursor = pActCursor->GetNext();
+ if( !pActCursor->HasMark() ) // with a multi-selection the current cursor might be empty
+ {
+ pActCursor = pActCursor->GetPrev();
+ }
+
+ // Y-position of the first selection
+ Point aSelPoint;
+ if( pFESh->IsTableMode() )
+ {
+ SwShellTableCursor* pShellTableCursor = pFESh->GetTableCursor();
+
+ const SwContentNode *const pContentNode =
+ pShellTableCursor->Start()->nNode.GetNode().GetContentNode();
+ const SwContentFrame *const pContentFrame = pContentNode ? pContentNode->getLayoutFrame(GetLayout(), pShellTableCursor->Start()) : nullptr;
+ if( pContentFrame )
+ {
+ SwRect aCharRect;
+ SwCursorMoveState aTmpState( CursorMoveState::NONE );
+ pContentFrame->GetCharRect( aCharRect, *pShellTableCursor->Start(), &aTmpState );
+ aSelPoint = Point( aCharRect.Left(), aCharRect.Top() );
+ }
+ }
+ else if (pFirstCursor)
+ {
+ aSelPoint = pFirstCursor->GetSttPos();
+ }
+
+ const SwPageFrame* pPage = GetLayout()->GetPageAtPos( aSelPoint );
+ OSL_ENSURE( pPage, "no page found!" );
+
+ // get page descriptor - fall back to the first one if pPage could not be found
+ const SwPageDesc* pPageDesc = pPage ? rPrtDoc.FindPageDesc(
+ pPage->GetPageDesc()->GetName() ) : &rPrtDoc.GetPageDesc( 0 );
+
+ if( !pFESh->IsTableMode() && pActCursor && pActCursor->HasMark() )
+ { // Tweak paragraph attributes of last paragraph
+ SwNodeIndex aNodeIdx( *rPrtDoc.GetNodes().GetEndOfContent().StartOfSectionNode() );
+ SwTextNode* pTextNd = rPrtDoc.GetNodes().GoNext( &aNodeIdx )->GetTextNode();
+ SwContentNode *pLastNd =
+ pActCursor->GetContentNode( (*pActCursor->GetMark()) <= (*pActCursor->GetPoint()) );
+ // copy the paragraph attributes of the first paragraph
+ if( pLastNd && pLastNd->IsTextNode() )
+ static_cast<SwTextNode*>(pLastNd)->CopyCollFormat( *pTextNd );
+ }
+
+ // fill it with the selected content
+ pFESh->Copy(rPrtDoc);
+
+ // set the page style at the first paragraph
+ {
+ SwNodeIndex aNodeIdx( *rPrtDoc.GetNodes().GetEndOfContent().StartOfSectionNode() );
+ SwContentNode* pCNd = rPrtDoc.GetNodes().GoNext( &aNodeIdx ); // go to 1st ContentNode
+ if( pFESh->IsTableMode() )
+ {
+ SwTableNode* pTNd = pCNd->FindTableNode();
+ if( pTNd )
+ pTNd->GetTable().GetFrameFormat()->SetFormatAttr( SwFormatPageDesc( pPageDesc ) );
+ }
+ else
+ {
+ pCNd->SetAttr( SwFormatPageDesc( pPageDesc ) );
+ if( pFirstCursor && pFirstCursor->HasMark() )
+ {
+ SwTextNode *pTextNd = pCNd->GetTextNode();
+ if( pTextNd )
+ {
+ SwContentNode *pFirstNd =
+ pFirstCursor->GetContentNode( (*pFirstCursor->GetMark()) > (*pFirstCursor->GetPoint()) );
+ // copy paragraph attributes of the first paragraph
+ if( pFirstNd && pFirstNd->IsTextNode() )
+ static_cast<SwTextNode*>(pFirstNd)->CopyCollFormat( *pTextNd );
+ }
+ }
+ }
+ }
+}
+
+// TODO: there is already a GetPageByPageNum, but it checks some physical page
+// number; unsure if we want that here, should find out what that is...
+SwPageFrame const*
+sw_getPage(SwRootFrame const& rLayout, sal_Int32 const nPage)
+{
+ // yes this is O(n^2) but at least it does not crash...
+ SwPageFrame const* pPage = dynamic_cast<const SwPageFrame*>(rLayout.Lower());
+ for (sal_Int32 i = nPage; pPage && (i > 0); --i)
+ {
+ if (1 == i) { // note: nPage is 1-based, i.e. 0 is invalid!
+ return pPage;
+ }
+ pPage = dynamic_cast<SwPageFrame const*>(pPage->GetNext());
+ }
+ OSL_ENSURE(pPage, "ERROR: SwPageFrame expected");
+ OSL_FAIL("non-existent page requested");
+ return nullptr;
+}
+
+bool SwViewShell::PrintOrPDFExport(
+ OutputDevice *pOutDev,
+ SwPrintData const& rPrintData,
+ sal_Int32 nRenderer, /* the index in the vector of pages to be printed */
+ bool bIsPDFExport )
+{
+ // CAUTION: Do also always update the printing routines in viewpg.cxx (PrintProspect)!
+
+ const sal_Int32 nMaxRenderer = rPrintData.GetRenderData().GetPagesToPrint().size() - 1;
+ OSL_ENSURE( 0 <= nRenderer && nRenderer <= nMaxRenderer, "nRenderer out of bounds");
+ if (!pOutDev || nMaxRenderer < 0 || nRenderer < 0 || nRenderer > nMaxRenderer)
+ return false;
+
+ // save settings of OutputDevice (should be done always since the
+ // output device is now provided by a call from outside the Writer)
+ pOutDev->Push();
+
+ // fdo#36815 for comments in margins print to a metafile
+ // and then scale that metafile down so that the comments
+ // will fit on the real page, and replay that scaled
+ // output to the real outputdevice
+ GDIMetaFile *pOrigRecorder(nullptr);
+ std::unique_ptr<GDIMetaFile> pMetaFile;
+ SwPostItMode nPostItMode = rPrintData.GetPrintPostIts();
+
+ // tdf#91680 Reserve space in margin for comments only if there are comments
+ const bool bHasPostItsToPrintInMargins = ( nPostItMode == SwPostItMode::InMargins ) &&
+ sw_GetPostIts( GetDoc()->getIDocumentFieldsAccess(), nullptr );
+
+ if ( bHasPostItsToPrintInMargins )
+ {
+ //get and disable the existing recorder
+ pOrigRecorder = pOutDev->GetConnectMetaFile();
+ pOutDev->SetConnectMetaFile(nullptr);
+ // turn off output to the device
+ pOutDev->EnableOutput(false);
+ // just record the rendering commands to the metafile
+ // instead
+ pMetaFile.reset(new GDIMetaFile);
+ pMetaFile->SetPrefSize(pOutDev->GetOutputSize());
+ pMetaFile->SetPrefMapMode(pOutDev->GetMapMode());
+ pMetaFile->Record(pOutDev);
+ }
+
+ // Print/PDF export for (multi-)selection has already generated a
+ // temporary document with the selected text.
+ // (see XRenderable implementation in unotxdoc.cxx)
+ // It is implemented this way because PDF export calls this Prt function
+ // once per page and we do not like to always have the temporary document
+ // to be created that often here.
+ std::unique_ptr<SwViewShell> pShell(new SwViewShell(*this, nullptr, pOutDev));
+
+ SdrView *pDrawView = pShell->GetDrawView();
+ if (pDrawView)
+ {
+ pDrawView->SetBufferedOutputAllowed( false );
+ pDrawView->SetBufferedOverlayAllowed( false );
+ }
+
+ { // additional scope so that the CurrShell is reset before destroying the shell
+
+ CurrShell aCurr( pShell.get() );
+
+ //JP 01.02.99: Bug 61335 - the ReadOnly flag is never copied
+ if( mpOpt->IsReadonly() )
+ pShell->mpOpt->SetReadonly( true );
+
+ // save options at draw view:
+ SwDrawViewSave aDrawViewSave( pShell->GetDrawView() );
+ pShell->PrepareForPrint( rPrintData, bIsPDFExport );
+
+ const sal_Int32 nPage = rPrintData.GetRenderData().GetPagesToPrint()[ nRenderer ];
+ OSL_ENSURE( nPage < 0 ||
+ rPrintData.GetRenderData().GetValidPagesSet().count( nPage ) == 1,
+ "SwViewShell::PrintOrPDFExport: nPage not valid" );
+ SwViewShell *const pViewSh2 = (nPage < 0)
+ ? rPrintData.GetRenderData().m_pPostItShell.get()// post-it page
+ : pShell.get(); // a 'regular' page, not one from the post-it doc
+
+ SwPageFrame const*const pStPage =
+ sw_getPage(*pViewSh2->GetLayout(), abs(nPage));
+ OSL_ENSURE( pStPage, "failed to get start page" );
+ if (!pStPage)
+ {
+ return false;
+ }
+
+ //!! applying view options and formatting the document should now only be done in getRendererCount!
+
+ ::SetSwVisArea( pViewSh2, pStPage->getFrameArea() );
+
+ pShell->InitPrt(pOutDev);
+
+ ::SetSwVisArea( pViewSh2, pStPage->getFrameArea() );
+
+ pStPage->GetUpper()->PaintSwFrame( *pOutDev, pStPage->getFrameArea(), &rPrintData );
+
+ SwPaintQueue::Repaint();
+
+ SwPostItMgr *pPostItManager = bHasPostItsToPrintInMargins ? pShell->GetPostItMgr() : nullptr;
+
+ if (pPostItManager)
+ {
+ pPostItManager->CalcRects();
+ pPostItManager->LayoutPostIts();
+ pPostItManager->DrawNotesForPage(pOutDev, nPage-1);
+
+ //Stop recording now
+ pMetaFile->Stop();
+ pMetaFile->WindStart();
+ //Enable output to the device again
+ pOutDev->EnableOutput();
+ //Restore the original recorder
+ pOutDev->SetConnectMetaFile(pOrigRecorder);
+
+ //Now scale the recorded page down so the notes
+ //will fit in the final page
+ double fScale = 0.75;
+ tools::Long nOrigHeight = pStPage->getFrameArea().Height();
+ tools::Long nNewHeight = nOrigHeight*fScale;
+ tools::Long nShiftY = (nOrigHeight-nNewHeight)/2;
+ pMetaFile->Scale( fScale, fScale );
+ pMetaFile->WindStart();
+ //Move the scaled page down to center it
+ //the other variant of Move does not map pixels
+ //back to the logical units correctly
+ pMetaFile->Move(0, convertTwipToMm100(nShiftY), pOutDev->GetDPIX(), pOutDev->GetDPIY());
+ pMetaFile->WindStart();
+
+ //play back the scaled page
+ pMetaFile->Play(*pOutDev);
+ pMetaFile.reset();
+ }
+ }
+
+ pShell.reset();
+
+ // restore settings of OutputDevice (should be done always now since the
+ // output device is now provided by a call from outside the Writer)
+ pOutDev->Pop();
+
+ return true;
+}
+
+void SwViewShell::PrtOle2( SwDoc *pDoc, const SwViewOption *pOpt, const SwPrintData& rOptions,
+ vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
+{
+ // For printing a shell is needed. Either the Doc already has one, then we
+ // create a new view, or it has none, then we create the first view.
+ std::unique_ptr<SwViewShell> pSh;
+ if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() )
+ pSh.reset(new SwViewShell( *pDoc->getIDocumentLayoutAccess().GetCurrentViewShell(), nullptr, &rRenderContext,VSHELLFLAG_SHARELAYOUT ));
+ else
+ pSh.reset(new SwViewShell( *pDoc, nullptr, pOpt, &rRenderContext));
+
+ {
+ CurrShell aCurr( pSh.get() );
+ pSh->PrepareForPrint( rOptions );
+ pSh->SetPrtFormatOption( true );
+
+ SwRect aSwRect( rRect );
+ pSh->maVisArea = aSwRect;
+
+ if ( pSh->GetViewOptions()->getBrowseMode() &&
+ pSh->GetRingContainer().size() == 1 )
+ {
+ pSh->InvalidateLayout( false );
+ pSh->GetLayout()->Lower()->InvalidateSize();
+ }
+
+ // CalcPagesForPrint() should not be necessary here. The pages in the
+ // visible area will be formatted in SwRootFrame::PaintSwFrame().
+ // Removing this gives us a performance gain during saving the
+ // document because the thumbnail creation will not trigger a complete
+ // formatting of the document.
+
+ rRenderContext.Push( vcl::PushFlags::CLIPREGION );
+ rRenderContext.IntersectClipRegion( aSwRect.SVRect() );
+ pSh->GetLayout()->PaintSwFrame( rRenderContext, aSwRect );
+
+ rRenderContext.Pop();
+ // first the CurrShell object needs to be destroyed!
+ }
+}
+
+/// Check if the DocNodesArray contains fields.
+bool SwViewShell::IsAnyFieldInDoc() const
+{
+ for (const SfxPoolItem* pItem : mxDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_FIELD))
+ {
+ auto pFormatField = dynamic_cast<const SwFormatField*>(pItem);
+ if(pFormatField)
+ {
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ if( pTextField && pTextField->GetTextNode().GetNodes().IsDocNodes() )
+ {
+ return true;
+ }
+ }
+ }
+
+ for (const SfxPoolItem* pItem : mxDoc->GetAttrPool().GetItemSurrogates(RES_TXTATR_INPUTFIELD))
+ {
+ const SwFormatField* pFormatField = dynamic_cast<const SwFormatField*>(pItem);
+ if(pFormatField)
+ {
+ const SwTextField* pTextField = pFormatField->GetTextField();
+ if( pTextField && pTextField->GetTextNode().GetNodes().IsDocNodes() )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/// Saves some settings at the draw view
+SwDrawViewSave::SwDrawViewSave( SdrView* pSdrView )
+ : pDV( pSdrView )
+ , bPrintControls(true)
+{
+ if ( pDV )
+ {
+ bPrintControls = pDV->IsLayerPrintable( "Controls" );
+ }
+}
+
+SwDrawViewSave::~SwDrawViewSave()
+{
+ if ( pDV )
+ {
+ pDV->SetLayerPrintable( "Controls", bPrintControls );
+ }
+}
+
+// OD 09.01.2003 #i6467# - method also called for page preview
+void SwViewShell::PrepareForPrint( const SwPrintData &rOptions, bool bIsPDFExport )
+ {
+ mpOpt->SetGraphic ( rOptions.m_bPrintGraphic );
+ mpOpt->SetTable ( rOptions.m_bPrintTable );
+ mpOpt->SetDraw ( rOptions.m_bPrintDraw );
+ mpOpt->SetControl ( rOptions.m_bPrintControl );
+ mpOpt->SetPageBack ( rOptions.m_bPrintPageBackground );
+ // Font should not be black if it's a PDF Export
+ mpOpt->SetBlackFont( rOptions.m_bPrintBlackFont && !bIsPDFExport );
+
+ if ( !HasDrawView() )
+ return;
+
+ SdrView *pDrawView = GetDrawView();
+ // OD 09.01.2003 #i6467# - consider, if view shell belongs to page preview
+ if ( !IsPreview() )
+ {
+ pDrawView->SetLayerPrintable( "Controls", rOptions.m_bPrintControl );
+ }
+ else
+ {
+ pDrawView->SetLayerVisible( "Controls", rOptions.m_bPrintControl );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/view/vprint.hxx b/sw/source/core/view/vprint.hxx
new file mode 100644
index 000000000..60f2750ca
--- /dev/null
+++ b/sw/source/core/view/vprint.hxx
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 .
+ */
+#pragma once
+
+#include <sal/types.h>
+
+class SwRootFrame;
+class SwPageFrame;
+SwPageFrame const* sw_getPage(SwRootFrame const& rLayout, sal_Int32 const nPage);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */